From 6b92675a6778571d958c61fa8d15e2ecf5d2e425 Mon Sep 17 00:00:00 2001 From: wenjie <1569925@qq.com> Date: Fri, 15 Dec 2017 16:38:19 +0800 Subject: [PATCH 01/22] WellJay new version --- .gitignore | 2 + README.md | 124 +++++++-- impl/RedisDaoTool.java | 386 --------------------------- inter/AbstractBaseRedisDao.java | 27 -- redisTools/RedisCacheUtil.java | 191 +++++++++++++ testController/SystemController.java | 34 --- 6 files changed, 289 insertions(+), 475 deletions(-) delete mode 100644 impl/RedisDaoTool.java delete mode 100644 inter/AbstractBaseRedisDao.java create mode 100644 redisTools/RedisCacheUtil.java delete mode 100644 testController/SystemController.java diff --git a/.gitignore b/.gitignore index 32858aa..cfdbd93 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* +*.xml +.idea/spring-data-redis-tools.iml diff --git a/README.md b/README.md index b78706b..23278fd 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,102 @@ # spring-data-redis-tools -spring data redis 封装工具类 +spring data redis 封装工具类 +近期会持续增加分布式锁等功能 *** ## Maven -```html - - redis.clients - jedis - 2.6.2 - - - org.springframework.data - spring-data-redis - 1.5.1.RELEASE - +```xml + + org.springframework.boot + spring-boot-starter-data-redis + ``` -## applicationContext.xml -```html - - - - - - - +## RedisConfiguration +```java +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.cache.RedisCacheManager; +import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; +import redis.clients.jedis.JedisPoolConfig; - - - +/** + * @author wellJay + */ +@Configuration +@EnableCaching +public class RedisConfiguration { + //过期时间一天 + private static final int DEFAULT_EXPIRE_TIME = 3600 * 24; + + //从配置文件读取redis参数 + @Autowired + private CloudConfigProperties cloudConfigProperties; + + /** + * jedisPoolConfig config + */ + @Bean + public JedisPoolConfig jedisPoolConfig() { + JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); + jedisPoolConfig.setMaxIdle(cloudConfigProperties.getRedis().getMaxIdle()); + jedisPoolConfig.setMinIdle(cloudConfigProperties.getRedis().getMinIdle()); + jedisPoolConfig.setTestOnBorrow(cloudConfigProperties.getRedis().getTestOnBorrow()); + jedisPoolConfig.setTestOnReturn(cloudConfigProperties.getRedis().getTestOnReturn()); + return jedisPoolConfig; + } + + /** + * JedisConnectionFactory + */ + @Bean + public JedisConnectionFactory jedisConnectionFactory(JedisPoolConfig jedisPoolConfig) { + JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(); + jedisConnectionFactory.setHostName(cloudConfigProperties.getRedis().getHost()); + jedisConnectionFactory.setPort(cloudConfigProperties.getRedis().getPort()); + jedisConnectionFactory.setPassword(cloudConfigProperties.getRedis().getPassword()); + jedisConnectionFactory.setTimeout(cloudConfigProperties.getRedis().getTimeout()); + jedisConnectionFactory.setUsePool(true); + jedisConnectionFactory.setPoolConfig(jedisPoolConfig); + return jedisConnectionFactory; + } + + /** + * RedisTemplate + * 从执行时间上来看,JdkSerializationRedisSerializer是最高效的(毕竟是JDK原生的),但是是序列化的结果字符串是最长的。 + * JSON由于其数据格式的紧凑性,序列化的长度是最小的,时间比前者要多一些。 + * 所以个人的选择是倾向使用JacksonJsonRedisSerializer作为POJO的序列器。 + */ + @Bean + public RedisTemplate redisTemplate(JedisConnectionFactory jedisConnectionFactory) { + RedisTemplate redisTemplate = new RedisTemplate(); + redisTemplate.setConnectionFactory(jedisConnectionFactory); + redisTemplate.setDefaultSerializer(new StringRedisSerializer()); + //设置普通value序列化方式 + redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer()); + redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer()); + return redisTemplate; + } + + @Bean + public CacheManager cacheManager(RedisTemplate redisTemplate) { + RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate); + redisCacheManager.setDefaultExpiration(DEFAULT_EXPIRE_TIME); + return redisCacheManager; + } +} +``` + +## How To Use +1、注入util方式,适用于复杂的业务处理 +```java + @Autowired + private RedisCacheUtil redisCacheUtil; +``` +2、Spring注解方式适用于简单的数据缓存 +```java + @Cacheable(value = Constants.Redis.SYSTEM, key = ACTIONS_CACHE_KEY) ``` diff --git a/impl/RedisDaoTool.java b/impl/RedisDaoTool.java deleted file mode 100644 index a7c7fea..0000000 --- a/impl/RedisDaoTool.java +++ /dev/null @@ -1,386 +0,0 @@ -package com.brandbigdata.rep.redis.impl; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.springframework.dao.DataAccessException; -import org.springframework.data.redis.connection.RedisConnection; -import org.springframework.data.redis.core.RedisCallback; -import org.springframework.stereotype.Repository; - -import com.brandbigdata.rep.redis.inter.AbstractBaseRedisDao; - -/** - * @author Wen Jie - * - */ -@Repository -public class StringRedisDao extends AbstractBaseRedisDao { - - // ----------------------------------------Object---------------------------------------- - /** - * 设置对象 - * - * @param key - * @param object - * @param timeout - * @param 对象class - * @return - * @throws Exception - */ - public boolean addObject(final String key, final Object object, final Long timeout, Class clazz) throws BbdException { - redisTemplate.opsForValue().set(key, clazz.cast(object)); - return true; - } - - /** - * 获得对象 - * - * @param key - * @return - */ - public Object getObject(final String key) { - return redisTemplate.opsForValue().get(key); - } - - // ---------------------------------------String----------------------------------------- - - /** - * 新增String ----setNX 不存在则增加 ------------------------------ - * - * @param key - * 键 - * @param value - * 值 - * @param timout - * 超时(秒) - * @return true 操作成功,false 已存在值 - */ - public boolean addString(final String key, final String value, final Long timeout) { - boolean result = redisTemplate.execute(new RedisCallback() { - public Boolean doInRedis(RedisConnection connection) throws DataAccessException { - Boolean result = connection.setNX(key.getBytes(), value.getBytes()); - if (result == false) - return result; - if (timeout != null && timeout > 0) - connection.expire(key.getBytes(), timeout); - return result; - } - }); - return result; - } - - /** - * 批量新增String---setNx 不存在则增加 - * - * @param keyValueList - * 键值对的map - * @param timeout - * 超时处理 - * @return - */ - public boolean addString(final Map keyValueList, final Long timeout) { - boolean result = redisTemplate.execute(new RedisCallback() { - public Boolean doInRedis(RedisConnection connection) throws DataAccessException { - for (String key : keyValueList.keySet()) { - connection.setNX(key.getBytes(), keyValueList.get(key).getBytes()); - if (timeout != null && timeout > 0) - connection.expire(key.getBytes(), timeout); - } - return true; - } - }, false, true); - return result; - } - - /** - * 通过key获取单个 - * - * @param key - * @return - */ - public String getString(final String key) { - String value = redisTemplate.execute(new RedisCallback() { - public String doInRedis(RedisConnection connection) throws DataAccessException { - byte[] result = connection.get(key.getBytes()); - if(result != null && result.length > 0) - return new String(result); - return null; - } - }); - return value; - } - - /** - * 修改 String - * - * @param key - * @param value - * @return - */ - /* - 重新Set等于Update - public boolean updateString(final String key, final String value) { - if (getString(key) == null) { - throw new NullPointerException("数据行不存在, key = " + key); - } - boolean result = redisTemplate.execute(new RedisCallback() { - public Boolean doInRedis(RedisConnection connection) throws DataAccessException { - connection.set(key.getBytes(), value.getBytes()); - return true; - } - }); - return result; - } - / - - // ---------------------------------------List----------------------------------------- - /** - * 新增Hash ----setNX 不存在则增加 ------------------------------ - * - * @param key - * 键 - * @param value - * 值 - * @param timout - * 超时(秒) - * @return true 操作成功,false 已存在值 - */ - public boolean addHash(final String key, final String field, final String value, final Long timeout) { - boolean result = redisTemplate.execute(new RedisCallback() { - public Boolean doInRedis(RedisConnection connection) throws DataAccessException { - Boolean result = connection.hSetNX(key.getBytes(), field.getBytes(), value.getBytes()); - if (result == false) - return result; - if (timeout != null && timeout > 0) - connection.expire(key.getBytes(), timeout); - return result; - } - }); - return result; - } - - /** - * 批量新增Hash ----setNX 不存在则增加 ------------------------------ - * - * @param key - * 键 - * @param value - * 值 - * @param timout - * 超时(秒) - * @return true 操作成功,false 已存在值 - */ - public boolean addHash(final String key, final Map fieldValueList, final Long timeout) { - boolean result = redisTemplate.execute(new RedisCallback() { - public Boolean doInRedis(RedisConnection connection) throws DataAccessException { - for (String hashKey : fieldValueList.keySet()) { - connection.hSetNX(key.getBytes(), hashKey.getBytes(), fieldValueList.get(hashKey).getBytes()); - if (timeout != null && timeout > 0) - connection.expire(key.getBytes(), timeout); - } - return true; - } - }); - return result; - } - - /** - * 通过key获取单个 - * - * @param key - * @return - */ - public Object getHashField(final String key, final String field) { - String value = redisTemplate.execute(new RedisCallback() { - public String doInRedis(RedisConnection connection) throws DataAccessException { - return new String(connection.hGet(key.getBytes(), field.getBytes())); - } - }); - return value; - } - - /** - * 通过key获取整个Hash - * - * @param key - * @return - */ - public Map getHashAll(final String key, final String field) { - Map value = redisTemplate.execute(new RedisCallback>() { - public Map doInRedis(RedisConnection connection) throws DataAccessException { - return connection.hGetAll(key.getBytes()); - } - }); - return value; - } - - //---------------------------------------------------通用删除------------------------------------------------- - /** - * 删除单个 - * - * @param key - */ - public void delete(final String key) { - redisTemplate.execute(new RedisCallback() { - public Long doInRedis(RedisConnection connection) throws DataAccessException { - return connection.del(key.getBytes()); - } - }); - } - - - //----------------------------------------------------队列操作-------------------------------------------------- - /** - * 压栈 - * - * @param key - * @param value - * @return - */ - public Long push(String key, String value) { - return redisTemplate.opsForList().leftPush(key, value); - } - - /** - * 出栈 - * - * @param key - * @return - */ - public String pop(String key) { - return (String) redisTemplate.opsForList().leftPop(key); - } - - /** - * 入队 - * - * @param key - * @param value - * @return - */ - public Long in(String key, String value) { - return redisTemplate.opsForList().rightPush(key, value); - } - - /** - * 出队 - * - * @param key - * @return - */ - public String out(String key) { - return (String) redisTemplate.opsForList().leftPop(key); - } - - /** - * 栈/队列长 - * - * @param key - * @return - */ - public Long length(String key) { - return redisTemplate.opsForList().size(key); - } - - /** - * 范围检索 - * - * @param key - * @param start - * @param end - * @return - */ - public List range(String key, int start, int end) { - return redisTemplate.opsForList().range(key, start, end); - } - - /** - * 移除 - * - * @param key - * @param i - * @param value - */ - public void remove(String key, long i, String value) { - redisTemplate.opsForList().remove(key, i, value); - } - - /** - * 检索 - * - * @param key - * @param index - * @return - */ - public String index(String key, long index) { - return (String) redisTemplate.opsForList().index(key, index); - } - - /** - * 置值 - * - * @param key - * @param index - * @param value - */ - public void set(String key, long index, String value) { - redisTemplate.opsForList().set(key, index, value); - } - - /** - * 裁剪 - * - * @param key - * @param start - * @param end - */ - public void trim(String key, long start, int end) { - redisTemplate.opsForList().trim(key, start, end); - } - - //---------------------------------------------------SET----------------------------------------------- - /** - * 新增Set ----setNX 不存在则增加 ------------------------------ - * - * @param key - * 键 - * @param value - * 值 - * @param timout - * 超时(秒) - * @return true 操作成功,false 已存在值 - */ - public Long addSet(final String key, final String value, final Long timeout) { - Long result = redisTemplate.execute(new RedisCallback() { - public Long doInRedis(RedisConnection connection) throws DataAccessException { - Long result = connection.sAdd(key.getBytes(), value.getBytes()); - if (result == 0) - return result; - if (timeout != null && timeout > 0) - connection.expire(key.getBytes(), timeout); - return result; - } - }); - return result; - } - - - /** - * 通过key获取单个Set - * - * @param key - * @return - */ - public Set getSet(final String key) { - Set value = redisTemplate.execute(new RedisCallback>() { - public Set doInRedis(RedisConnection connection) throws DataAccessException { - return connection.sMembers(key.getBytes()); - } - }); - return value; - } - -} diff --git a/inter/AbstractBaseRedisDao.java b/inter/AbstractBaseRedisDao.java deleted file mode 100644 index af8a759..0000000 --- a/inter/AbstractBaseRedisDao.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.brandbigdata.rep.redis.inter; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.serializer.RedisSerializer; - -public abstract class AbstractBaseRedisDao { - @Autowired - protected RedisTemplate redisTemplate; - - /** - * 设置redisTemplate - * @param redisTemplate the redisTemplate to set - */ - public void setRedisTemplate(RedisTemplate redisTemplate) { - this.redisTemplate = redisTemplate; - } - - /** - * 获取 String RedisSerializer - *
------------------------------
- */ - protected RedisSerializer getStringSerializer() { - return redisTemplate.getStringSerializer(); - } - -} diff --git a/redisTools/RedisCacheUtil.java b/redisTools/RedisCacheUtil.java new file mode 100644 index 0000000..717da76 --- /dev/null +++ b/redisTools/RedisCacheUtil.java @@ -0,0 +1,191 @@ +/** + * @author wenjie + * @create 2017-06-15 19:09 + **/ + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.*; +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +@Component +public class RedisCacheUtil { + + @Autowired + public RedisTemplate redisTemplate; + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + * @return 缓存的对象 + */ + public ValueOperations setCacheObject(String key, T value) { + ValueOperations operation = redisTemplate.opsForValue(); + operation.set(key, value); + return operation; + } + + public ValueOperations setCacheObject(String key, T value, Integer timeout, TimeUnit timeUnit) { + ValueOperations operation = redisTemplate.opsForValue(); + operation.set(key, value, timeout, timeUnit); + return operation; + } + + /** + * 获得缓存的基本对象。 + * + * @param key 缓存键值 + * @return 缓存键值对应的数据 + */ + public T getCacheObject(String key) { + ValueOperations operation = redisTemplate.opsForValue(); + return operation.get(key); + } + + /** + * 删除单个对象 + * + * @param key + */ + public void deleteObject(String key) { + redisTemplate.delete(key); + } + + /** + * 删除集合对象 + * + * @param collection + */ + public void deleteObject(Collection collection) { + redisTemplate.delete(collection); + } + + /** + * 缓存List数据 + * + * @param key 缓存的键值 + * @param dataList 待缓存的List数据 + * @return 缓存的对象 + */ + public ListOperations setCacheList(String key, List dataList) { + ListOperations listOperation = redisTemplate.opsForList(); + if (null != dataList) { + int size = dataList.size(); + for (int i = 0; i < size; i++) { + listOperation.leftPush(key, dataList.get(i)); + } + } + return listOperation; + } + + /** + * 获得缓存的list对象 + * + * @param key 缓存的键值 + * @return 缓存键值对应的数据 + */ + public List getCacheList(String key) { + List dataList = new ArrayList(); + ListOperations listOperation = redisTemplate.opsForList(); + Long size = listOperation.size(key); + + for (int i = 0; i < size; i++) { + dataList.add(listOperation.index(key, i)); + } + return dataList; + } + + /** + * 缓存Set + * + * @param key 缓存键值 + * @param dataSet 缓存的数据 + * @return 缓存数据的对象 + */ + public BoundSetOperations setCacheSet(String key, Set dataSet) { + BoundSetOperations setOperation = redisTemplate.boundSetOps(key); + Iterator it = dataSet.iterator(); + while (it.hasNext()) { + setOperation.add(it.next()); + } + return setOperation; + } + + /** + * 获得缓存的set + * + * @param key + * @return + */ + public Set getCacheSet(String key) { + Set dataSet = new HashSet(); + BoundSetOperations operation = redisTemplate.boundSetOps(key); + Long size = operation.size(); + for (int i = 0; i < size; i++) { + dataSet.add(operation.pop()); + } + return dataSet; + } + + /** + * 缓存Map + * + * @param key + * @param dataMap + * @return + */ + public HashOperations setCacheMap(String key, Map dataMap) { + + HashOperations hashOperations = redisTemplate.opsForHash(); + if (null != dataMap) { + for (Map.Entry entry : dataMap.entrySet()) { + hashOperations.put(key, entry.getKey(), entry.getValue()); + } + } + return hashOperations; + } + + /** + * 获得缓存的Map + * + * @param key + * @return + */ + public Map getCacheMap(String key) { + Map map = redisTemplate.opsForHash().entries(key); + return map; + } + + + /** + * 缓存Map + * + * @param key + * @param dataMap + * @return + */ + public HashOperations setCacheIntegerMap(String key, Map dataMap) { + HashOperations hashOperations = redisTemplate.opsForHash(); + if (null != dataMap) { + for (Map.Entry entry : dataMap.entrySet()) { + hashOperations.put(key, entry.getKey(), entry.getValue()); + } + } + return hashOperations; + } + + /** + * 获得缓存的Map + * + * @param key + * @return + */ + public Map getCacheIntegerMap(String key) { + Map map = redisTemplate.opsForHash().entries(key); + return map; + } +} diff --git a/testController/SystemController.java b/testController/SystemController.java deleted file mode 100644 index 86160a8..0000000 --- a/testController/SystemController.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.brandbigdata.rep.business.action; - -import javax.annotation.Resource; - -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; - -import com.brandbigdata.daemon.Rlinks; -import com.brandbigdata.rep.redis.impl.StringRedisDao; -import com.brandbigdata.utils.DateUtil; - -@Controller -@RequestMapping("system") -public class SystemController extends SuperController { - - @Resource - private StringRedisDao redis; - - @RequestMapping("test") - public String test(){ - //redis对象储存方法 - Rlinks rlinks = new Rlinks(); - rlinks.setLine("456"); - try { - redis.addObject("test", rlinks, DateUtil.getSecondFromDay(7), Rlinks.class); - } catch (Exception e) { - //log.error(e.getMessage()); - } - Rlinks r1 = (Rlinks)redis.getObject("test"); - System.out.println(r1.getLine()); - - return "system/files/addrenwu"; - } -} \ No newline at end of file From 742f2750dd2fd1c7d5192d1e8939087238bc6fa8 Mon Sep 17 00:00:00 2001 From: wenjie <1569925@qq.com> Date: Fri, 15 Dec 2017 16:40:00 +0800 Subject: [PATCH 02/22] WellJay change readme --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 23278fd..ac0ac05 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ public class RedisConfiguration { * RedisTemplate * 从执行时间上来看,JdkSerializationRedisSerializer是最高效的(毕竟是JDK原生的),但是是序列化的结果字符串是最长的。 * JSON由于其数据格式的紧凑性,序列化的长度是最小的,时间比前者要多一些。 - * 所以个人的选择是倾向使用JacksonJsonRedisSerializer作为POJO的序列器。 + * 所以个人的选择是倾向使用JacksonJsonRedisSerializer作为POJO的序列器。 */ @Bean public RedisTemplate redisTemplate(JedisConnectionFactory jedisConnectionFactory) { @@ -93,10 +93,10 @@ public class RedisConfiguration { ## How To Use 1、注入util方式,适用于复杂的业务处理 ```java - @Autowired - private RedisCacheUtil redisCacheUtil; +@Autowired +private RedisCacheUtil redisCacheUtil; ``` 2、Spring注解方式适用于简单的数据缓存 ```java - @Cacheable(value = Constants.Redis.SYSTEM, key = ACTIONS_CACHE_KEY) +@Cacheable(value = Constants.Redis.SYSTEM, key = ACTIONS_CACHE_KEY) ``` From 659b12fe1de82bd480d4ffcefe01fde9e9841994 Mon Sep 17 00:00:00 2001 From: wenjie <1569925@qq.com> Date: Tue, 10 Apr 2018 19:33:56 +0800 Subject: [PATCH 03/22] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=8F=AF=E8=A7=86?= =?UTF-8?q?=E5=8C=96=E5=88=86=E5=B8=83=E5=BC=8F=E5=94=AF=E4=B8=80ID?= =?UTF-8?q?=E7=94=9F=E6=88=90=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 +- README.md | 5 ++- distributedId/OrderNumberGenerate.java | 51 ++++++++++++++++++++++++++ redisTools/RedisCacheUtil.java | 9 ++--- 4 files changed, 59 insertions(+), 8 deletions(-) create mode 100644 distributedId/OrderNumberGenerate.java diff --git a/.gitignore b/.gitignore index cfdbd93..fa3193d 100644 --- a/.gitignore +++ b/.gitignore @@ -10,5 +10,5 @@ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* -*.xml +*.iml .idea/spring-data-redis-tools.iml diff --git a/README.md b/README.md index ac0ac05..e51dbae 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # spring-data-redis-tools -spring data redis 封装工具类 -近期会持续增加分布式锁等功能 +- redisTemplate封装工具类 +- 可视化分布式ID生成器 +- 近期会持续增加分布式锁等功能 *** ## Maven ```xml diff --git a/distributedId/OrderNumberGenerate.java b/distributedId/OrderNumberGenerate.java new file mode 100644 index 0000000..4ac50d1 --- /dev/null +++ b/distributedId/OrderNumberGenerate.java @@ -0,0 +1,51 @@ +import com.yisjt.flower.common.CacheKey; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Component; + +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * Created by WenJie on 2015/11/3. + * 可视化分布式唯一订单号生成器 20151103001 + */ +@Component +public class OrderNumberGenerate { + + public static final String FROMAT_STR = "yyyyMMddHHmm"; + private static StringRedisTemplate staticTemplate; + + @Autowired + public static void setStaticTemplate(StringRedisTemplate staticTemplate) { + OrderNumberGenerate.staticTemplate = staticTemplate; + } + + private OrderNumberGenerate() { + } + + /** + * 生成订单编号 + * date存redis,只要date变化则直接increment=1,否则就是并发冲突直接increment++保证分布式唯一id + * + * public static final String FLOWER_ORDER_NUMBER_INCR = "order_number_incr"; + * public static final String FLOWER_ORDER_NUMBER_DATE = "order_number_date"; + * + * @return + */ + public static synchronized String getOrderNo() { + String str = new SimpleDateFormat(FROMAT_STR).format(new Date()); + String date = staticTemplate.opsForValue().get(CacheKey.FLOWER_ORDER_NUMBER_DATE); + if (date == null || !date.equals(str)) { + //保存时间 + staticTemplate.opsForValue().set(CacheKey.FLOWER_ORDER_NUMBER_DATE, str); + //缓存清除 + staticTemplate.delete(CacheKey.FLOWER_ORDER_NUMBER_INCR); + } + //缓存++ + Long incrementValue = staticTemplate.opsForValue().increment(CacheKey.FLOWER_ORDER_NUMBER_INCR, 1); + long orderNo = Long.parseLong(date) * 10000; + orderNo += incrementValue; + return orderNo + ""; + } +} diff --git a/redisTools/RedisCacheUtil.java b/redisTools/RedisCacheUtil.java index 717da76..9ef1514 100644 --- a/redisTools/RedisCacheUtil.java +++ b/redisTools/RedisCacheUtil.java @@ -1,8 +1,3 @@ -/** - * @author wenjie - * @create 2017-06-15 19:09 - **/ - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.*; import org.springframework.stereotype.Component; @@ -10,6 +5,10 @@ import java.util.*; import java.util.concurrent.TimeUnit; +/** + * @author wenjie + * @create 2017-06-15 19:09 + **/ @Component public class RedisCacheUtil { From 15e469e3cc593cbb69d03cd7f5a44652bc3f1f7b Mon Sep 17 00:00:00 2001 From: wenjie <1569925@qq.com> Date: Tue, 10 Apr 2018 19:35:23 +0800 Subject: [PATCH 04/22] change word --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e51dbae..4eb6578 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # spring-data-redis-tools -- redisTemplate封装工具类 +- RedisTemplate封装工具类 - 可视化分布式ID生成器 - 近期会持续增加分布式锁等功能 *** From 5e4d42b046a3e018214c903395105bd3bc700e31 Mon Sep 17 00:00:00 2001 From: wenjie <1569925@qq.com> Date: Tue, 8 May 2018 18:04:59 +0800 Subject: [PATCH 05/22] add distribute lock --- DistributedLock/RedisLockTool.java | 46 ++++++++++++++++++++++++++ distributedId/OrderNumberGenerate.java | 1 - 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 DistributedLock/RedisLockTool.java diff --git a/DistributedLock/RedisLockTool.java b/DistributedLock/RedisLockTool.java new file mode 100644 index 0000000..eead07b --- /dev/null +++ b/DistributedLock/RedisLockTool.java @@ -0,0 +1,46 @@ +import com.google.common.collect.Lists; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.script.DefaultRedisScript; +import org.springframework.stereotype.Component; + +import java.util.Collections; + +/** + * @author wenjie + * @date 2018/5/7 0007 16:44 + */ +@Component +public final class RedisLockTool { + + private static RedisTemplate redisTemplate; + + @Autowired + public void setRedisTemplate(RedisTemplate redisTemplate) { + this.redisTemplate = redisTemplate; + } + + private static final Long SUCCESS = 1L; + + public static boolean tryGetDistributedLock(String lockKey, String requestId) { + DefaultRedisScript redisScript = new DefaultRedisScript<>(); + redisScript.setResultType(Long.class); + redisScript.setScriptText("if redis.call('set',KEYS[1],ARGV[1],'EX','60','NX') then return 1 else return 0 end"); + Object result = redisTemplate.execute(redisScript, Lists.newArrayList(lockKey), requestId); + if (SUCCESS.equals(result)) { + return true; + } + return false; + } + + public static boolean releaseDistributedLock(String lockKey, String requestId){ + DefaultRedisScript redisScript = new DefaultRedisScript<>(); + redisScript.setResultType(Long.class); + redisScript.setScriptText("if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"); + Object result = redisTemplate.execute(redisScript, Collections.singletonList(lockKey), requestId); + if (SUCCESS.equals(result)) { + return true; + } + return false; + } +} diff --git a/distributedId/OrderNumberGenerate.java b/distributedId/OrderNumberGenerate.java index 4ac50d1..c2df8fb 100644 --- a/distributedId/OrderNumberGenerate.java +++ b/distributedId/OrderNumberGenerate.java @@ -1,4 +1,3 @@ -import com.yisjt.flower.common.CacheKey; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Component; From 8b4abde1b2d46ca25fdd8606ab8cd247aa98d51a Mon Sep 17 00:00:00 2001 From: wenjie <1569925@qq.com> Date: Tue, 8 May 2018 18:07:13 +0800 Subject: [PATCH 06/22] change readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4eb6578..f01bcf1 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # spring-data-redis-tools - RedisTemplate封装工具类 - 可视化分布式ID生成器 -- 近期会持续增加分布式锁等功能 +- 分布式锁工具类 *** ## Maven ```xml From 17bd8ae4e12591cb07f012659ee1fb14aa2da29a Mon Sep 17 00:00:00 2001 From: wenjie <1569925@qq.com> Date: Tue, 8 May 2018 18:08:22 +0800 Subject: [PATCH 07/22] add distribute lock --- {DistributedLock => distributedLock}/RedisLockTool.java | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {DistributedLock => distributedLock}/RedisLockTool.java (100%) diff --git a/DistributedLock/RedisLockTool.java b/distributedLock/RedisLockTool.java similarity index 100% rename from DistributedLock/RedisLockTool.java rename to distributedLock/RedisLockTool.java From 3d5ceae95d59414cdf761a3c52421b4805ffcbda Mon Sep 17 00:00:00 2001 From: wenjie <1569925@qq.com> Date: Wed, 9 May 2018 12:46:49 +0800 Subject: [PATCH 08/22] optimization distribute lock --- README.md | 2 +- distributedLock/RedisLockTool.java | 39 ++++++++++++++------ distributedLock/Test.java | 59 ++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 13 deletions(-) create mode 100644 distributedLock/Test.java diff --git a/README.md b/README.md index f01bcf1..61d81de 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ public class RedisConfiguration { redisTemplate.setConnectionFactory(jedisConnectionFactory); redisTemplate.setDefaultSerializer(new StringRedisSerializer()); //设置普通value序列化方式 - redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer()); + redisTemplate.setValueSerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer()); return redisTemplate; } diff --git a/distributedLock/RedisLockTool.java b/distributedLock/RedisLockTool.java index eead07b..820959e 100644 --- a/distributedLock/RedisLockTool.java +++ b/distributedLock/RedisLockTool.java @@ -13,34 +13,49 @@ @Component public final class RedisLockTool { + private static final Long SUCCESS = 1L; + public static final String LOCK_SCRIPT_STR = "if redis.call('set',KEYS[1],ARGV[1],'EX',ARGV[2],'NX') then return 1 else return 0 end"; + public static final String UNLOCK_SCRIPT_STR = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; + private static RedisTemplate redisTemplate; + private static final DefaultRedisScript redisScript = new DefaultRedisScript<>(); + @Autowired - public void setRedisTemplate(RedisTemplate redisTemplate) { + public void init(RedisTemplate redisTemplate) { this.redisTemplate = redisTemplate; + redisScript.setResultType(Long.class); } - private static final Long SUCCESS = 1L; - public static boolean tryGetDistributedLock(String lockKey, String requestId) { - DefaultRedisScript redisScript = new DefaultRedisScript<>(); - redisScript.setResultType(Long.class); - redisScript.setScriptText("if redis.call('set',KEYS[1],ARGV[1],'EX','60','NX') then return 1 else return 0 end"); - Object result = redisTemplate.execute(redisScript, Lists.newArrayList(lockKey), requestId); + /** + * 加锁 + * @param lockKey key + * @param requestId 随机请求id + * @param timeoutSecond 超时秒 + * @return + */ + public static boolean tryGetDistributedLock(String lockKey, String requestId, Integer timeoutSecond) { + redisScript.setScriptText(LOCK_SCRIPT_STR); + Object result = redisTemplate.execute(redisScript, Lists.newArrayList(lockKey), requestId, String.valueOf(timeoutSecond)); if (SUCCESS.equals(result)) { return true; } return false; } - public static boolean releaseDistributedLock(String lockKey, String requestId){ - DefaultRedisScript redisScript = new DefaultRedisScript<>(); - redisScript.setResultType(Long.class); - redisScript.setScriptText("if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"); + /** + * 释放锁 + * @param lockKey key + * @param requestId 加锁的请求id + * @return + */ + public static boolean releaseDistributedLock(String lockKey, String requestId) { + redisScript.setScriptText(UNLOCK_SCRIPT_STR); Object result = redisTemplate.execute(redisScript, Collections.singletonList(lockKey), requestId); if (SUCCESS.equals(result)) { return true; } return false; } -} +} \ No newline at end of file diff --git a/distributedLock/Test.java b/distributedLock/Test.java new file mode 100644 index 0000000..8227aa2 --- /dev/null +++ b/distributedLock/Test.java @@ -0,0 +1,59 @@ +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.UUID; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = Application.class) +public class Test { + + @Test + public void distributedLock() throws InterruptedException { + CountDownLatch countDownLatch = new CountDownLatch(10); + + for (int i = 0; i < 10; i++) { + Thread thread = new Thread(new Tester(), "Thread-" + i); + countDownLatch.countDown(); + thread.start(); + } + + countDownLatch.await(); + + System.out.println("down"); + + Thread.sleep(Integer.MAX_VALUE); + } + + class Tester implements Runnable { + @Override + public void run() { + //虚拟requestID + String requestId = UUID.randomUUID().toString(); + while(true) { + boolean result = RedisLockTool.tryGetDistributedLock("test", requestId, 60); + if (result) { + System.out.println(Thread.currentThread().getName() + ": 得到锁"); + break; + } else { + try { + TimeUnit.SECONDS.sleep(1); + } catch (InterruptedException e) { + e.printStackTrace(); + } + continue; + } + } + try { + TimeUnit.SECONDS.sleep(5); + } catch (InterruptedException e) { + e.printStackTrace(); + } + RedisLockTool.releaseDistributedLock("test", requestId); + System.out.println(Thread.currentThread().getName() + ": 释放锁"); + } + } +} From 87faf601bd89436d9a376e803ad0a101d6e2cbdb Mon Sep 17 00:00:00 2001 From: wenjie <1569925@qq.com> Date: Wed, 9 May 2018 16:25:03 +0800 Subject: [PATCH 09/22] optimization distribute lock --- distributedLock/README.md | 4 ++ distributedLock/RedisLockTool.java | 108 ++++++++++++++++++++++++----- distributedLock/Test.java | 45 ++++++------ 3 files changed, 119 insertions(+), 38 deletions(-) create mode 100644 distributedLock/README.md diff --git a/distributedLock/README.md b/distributedLock/README.md new file mode 100644 index 0000000..25db6d1 --- /dev/null +++ b/distributedLock/README.md @@ -0,0 +1,4 @@ +- `RedisLockTool.tryGetDistributedLock();` GET LOCK key = caller className&method value = Current Thread Id +- `RedisLockTool.tryGetDistributedLock(String lockKey, String requestId);` GET LOCK +- `RedisLockTool.tryGetDistributedLock(String lockKey, String requestId, Integer expireSecond);` +- `RedisLockTool.tryGetDistributedLock(String lockKey, String requestId, Integer expireSecond, Long loopTimes, Long sleepInterval);` \ No newline at end of file diff --git a/distributedLock/RedisLockTool.java b/distributedLock/RedisLockTool.java index 820959e..4bf8f47 100644 --- a/distributedLock/RedisLockTool.java +++ b/distributedLock/RedisLockTool.java @@ -1,11 +1,10 @@ -import com.google.common.collect.Lists; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.script.DefaultRedisScript; import org.springframework.stereotype.Component; import java.util.Collections; - +import java.util.concurrent.TimeUnit; /** * @author wenjie * @date 2018/5/7 0007 16:44 @@ -17,45 +16,122 @@ public final class RedisLockTool { public static final String LOCK_SCRIPT_STR = "if redis.call('set',KEYS[1],ARGV[1],'EX',ARGV[2],'NX') then return 1 else return 0 end"; public static final String UNLOCK_SCRIPT_STR = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; - private static RedisTemplate redisTemplate; + //default value + public static final Integer DEFAULT_EXPIRE_SECOND = 60; + public static final Long DEFAULT_LOOP_TIMES = 10L; + public static final Long DEFAULT_SLEEP_INTERVAL = 500L; + public static final String PACKAGE_NAME_SPLIT_STR = "\\."; + public static final String CLASS_AND_METHOD_CONCAT_STR = "->"; - private static final DefaultRedisScript redisScript = new DefaultRedisScript<>(); + private static RedisTemplate redisTemplate; @Autowired public void init(RedisTemplate redisTemplate) { this.redisTemplate = redisTemplate; - redisScript.setResultType(Long.class); } + /** + * 得到分布式锁 + * 默认key:调用者类名 + * + * @return + * @throws InterruptedException + */ + public static boolean tryGetDistributedLock() { + String callerKey = getCurrentThreadCaller(); + String requestId = String.valueOf(Thread.currentThread().getId()); + return tryGetDistributedLock(callerKey, requestId); + } /** - * 加锁 - * @param lockKey key + * @param lockKey 锁名称 * @param requestId 随机请求id - * @param timeoutSecond 超时秒 * @return + * @throws InterruptedException */ - public static boolean tryGetDistributedLock(String lockKey, String requestId, Integer timeoutSecond) { - redisScript.setScriptText(LOCK_SCRIPT_STR); - Object result = redisTemplate.execute(redisScript, Lists.newArrayList(lockKey), requestId, String.valueOf(timeoutSecond)); - if (SUCCESS.equals(result)) { - return true; + public static boolean tryGetDistributedLock(String lockKey, String requestId) { + return tryGetDistributedLock(lockKey, requestId, DEFAULT_EXPIRE_SECOND); + } + + /** + * @param lockKey key + * @param requestId 随机请求id + * @param expireSecond 超时秒 + * @return + * @throws InterruptedException + */ + public static boolean tryGetDistributedLock(String lockKey, String requestId, Integer expireSecond) { + return tryGetDistributedLock(lockKey, requestId, expireSecond, DEFAULT_LOOP_TIMES, DEFAULT_SLEEP_INTERVAL); + } + + + /** + * 加锁 + * + * @param lockKey key + * @param requestId 随机请求id + * @param expireSecond 超时秒 + * @param loopTimes 循环次数 + * @param sleepInterval 等待间隔(毫秒) + * @return + */ + public static boolean tryGetDistributedLock(String lockKey, String requestId, Integer expireSecond, Long loopTimes, Long sleepInterval) { + DefaultRedisScript redisScript = new DefaultRedisScript<>(LOCK_SCRIPT_STR, Long.class); + while (loopTimes-- >= 0) { + Object result = redisTemplate.execute(redisScript, Lists.newArrayList(lockKey), requestId, String.valueOf(expireSecond)); + if (SUCCESS.equals(result)) { + return true; + } + try { + TimeUnit.MILLISECONDS.sleep(sleepInterval); + } catch (InterruptedException e) { + e.printStackTrace(); + } + continue; } return false; } + + /** + * 释放锁 + * + * @return + */ + public static boolean releaseDistributedLock() { + String callerKey = getCurrentThreadCaller(); + String requestId = String.valueOf(Thread.currentThread().getId()); + return releaseDistributedLock(callerKey, requestId); + } + /** * 释放锁 - * @param lockKey key + * + * @param lockKey key * @param requestId 加锁的请求id * @return */ public static boolean releaseDistributedLock(String lockKey, String requestId) { - redisScript.setScriptText(UNLOCK_SCRIPT_STR); + DefaultRedisScript redisScript = new DefaultRedisScript<>(UNLOCK_SCRIPT_STR, Long.class); Object result = redisTemplate.execute(redisScript, Collections.singletonList(lockKey), requestId); if (SUCCESS.equals(result)) { return true; } return false; } -} \ No newline at end of file + + private static String getSimpleClassName(String className) { + String[] splits = className.split(PACKAGE_NAME_SPLIT_STR); + return splits[splits.length - 1]; + } + + /** + * Get caller + * + * @return + */ + private static String getCurrentThreadCaller() { + StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[3]; + return getSimpleClassName(stackTraceElement.getClassName()) + CLASS_AND_METHOD_CONCAT_STR + stackTraceElement.getMethodName(); + } +} diff --git a/distributedLock/Test.java b/distributedLock/Test.java index 8227aa2..2fe2c3c 100644 --- a/distributedLock/Test.java +++ b/distributedLock/Test.java @@ -3,13 +3,14 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; +import java.util.Random; import java.util.UUID; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) -public class Test { +public class ApplicationTests { @Test public void distributedLock() throws InterruptedException { @@ -29,31 +30,31 @@ public void distributedLock() throws InterruptedException { } class Tester implements Runnable { + + public static final int RANDOM_NUMBER_RANGE = 3; + @Override public void run() { - //虚拟requestID - String requestId = UUID.randomUUID().toString(); - while(true) { - boolean result = RedisLockTool.tryGetDistributedLock("test", requestId, 60); - if (result) { - System.out.println(Thread.currentThread().getName() + ": 得到锁"); - break; - } else { - try { - TimeUnit.SECONDS.sleep(1); - } catch (InterruptedException e) { - e.printStackTrace(); - } - continue; - } + //获取锁, key:caller className&method value:Current Thread Id + //也可以传参 key value 可选[expireSecond loopTimes sleepInterval] + boolean result = RedisLockTool.tryGetDistributedLock(); + if (result) { + System.out.println(Thread.currentThread().getName() + ": get lock"); + } else { + System.err.println(Thread.currentThread().getName() + ": get lock timeout"); } - try { - TimeUnit.SECONDS.sleep(5); - } catch (InterruptedException e) { - e.printStackTrace(); + + //if get the lock + if (result) { + try { + TimeUnit.SECONDS.sleep(new Random().nextInt(RANDOM_NUMBER_RANGE)); + } catch (InterruptedException e) { + e.printStackTrace(); + } + RedisLockTool.releaseDistributedLock(); + System.out.println(Thread.currentThread().getName() + ": release lock"); } - RedisLockTool.releaseDistributedLock("test", requestId); - System.out.println(Thread.currentThread().getName() + ": 释放锁"); } } } + From d5c5fb9f0c0a339233fbc4d7a0465b300b110891 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=87=E6=9D=B0?= <1569925@gmail.com> Date: Wed, 9 May 2018 16:28:16 +0800 Subject: [PATCH 10/22] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 61d81de..070b9f7 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # spring-data-redis-tools -- RedisTemplate封装工具类 -- 可视化分布式ID生成器 -- 分布式锁工具类 +- RedisTemplate封装工具类 `redisTools` +- 可视化分布式ID生成器 `distributedId` +- 分布式锁工具类 `distributedLock` *** ## Maven ```xml From df042ba3b59ce3ad075f988847bf19773671ddaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=87=E6=9D=B0?= <1569925@gmail.com> Date: Thu, 10 May 2018 15:01:04 +0800 Subject: [PATCH 11/22] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 070b9f7..5c85577 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # spring-data-redis-tools - RedisTemplate封装工具类 `redisTools` - 可视化分布式ID生成器 `distributedId` -- 分布式锁工具类 `distributedLock` +- 可靠分布式锁工具类 `distributedLock`(lua脚本实现原子性解决断电问题、valueId避免错误释放问题) *** ## Maven ```xml From 703bdbc23f995ed0a2702d6d070bd3839eabcfbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=87=E6=9D=B0?= <1569925@gmail.com> Date: Thu, 10 May 2018 15:03:24 +0800 Subject: [PATCH 12/22] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5c85577..e5e2a7e 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # spring-data-redis-tools - RedisTemplate封装工具类 `redisTools` - 可视化分布式ID生成器 `distributedId` -- 可靠分布式锁工具类 `distributedLock`(lua脚本实现原子性解决断电问题、valueId避免错误释放问题) +- 可靠分布式锁工具类 `distributedLock`(lua脚本实现原子性解决断电问题、valueId避免错误释放问题) redis集群情况下推荐[RedLock](https://github.com/redisson/redisson) *** ## Maven ```xml From f3732cd8bfd775296148b4cb40467dfea9dfbc66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=87=E6=9D=B0?= <1569925@gmail.com> Date: Thu, 10 May 2018 15:03:54 +0800 Subject: [PATCH 13/22] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e5e2a7e..1ce8116 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # spring-data-redis-tools - RedisTemplate封装工具类 `redisTools` - 可视化分布式ID生成器 `distributedId` -- 可靠分布式锁工具类 `distributedLock`(lua脚本实现原子性解决断电问题、valueId避免错误释放问题) redis集群情况下推荐[RedLock](https://github.com/redisson/redisson) +- 可靠分布式锁工具类 `distributedLock` lua脚本实现原子性解决断电问题、valueId避免错误释放问题 redis集群情况下推荐[RedLock](https://github.com/redisson/redisson) *** ## Maven ```xml From 1368a02c955b57c7694642b2e821e6e10ffa0c0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=87=E6=9D=B0?= <1569925@gmail.com> Date: Thu, 10 May 2018 15:04:39 +0800 Subject: [PATCH 14/22] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1ce8116..5c85577 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # spring-data-redis-tools - RedisTemplate封装工具类 `redisTools` - 可视化分布式ID生成器 `distributedId` -- 可靠分布式锁工具类 `distributedLock` lua脚本实现原子性解决断电问题、valueId避免错误释放问题 redis集群情况下推荐[RedLock](https://github.com/redisson/redisson) +- 可靠分布式锁工具类 `distributedLock`(lua脚本实现原子性解决断电问题、valueId避免错误释放问题) *** ## Maven ```xml From b5a82e689d22dddc5a6d3b333df2be52ba5ffc5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=87=E6=9D=B0?= <1569925@gmail.com> Date: Thu, 10 May 2018 15:05:09 +0800 Subject: [PATCH 15/22] Update README.md --- distributedLock/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/distributedLock/README.md b/distributedLock/README.md index 25db6d1..4005d8d 100644 --- a/distributedLock/README.md +++ b/distributedLock/README.md @@ -1,4 +1,6 @@ - `RedisLockTool.tryGetDistributedLock();` GET LOCK key = caller className&method value = Current Thread Id - `RedisLockTool.tryGetDistributedLock(String lockKey, String requestId);` GET LOCK - `RedisLockTool.tryGetDistributedLock(String lockKey, String requestId, Integer expireSecond);` -- `RedisLockTool.tryGetDistributedLock(String lockKey, String requestId, Integer expireSecond, Long loopTimes, Long sleepInterval);` \ No newline at end of file +- `RedisLockTool.tryGetDistributedLock(String lockKey, String requestId, Integer expireSecond, Long loopTimes, Long sleepInterval);` + +redis集群情况下推荐[RedLock](https://github.com/redisson/redisson) From 88b41618c6e8299d255fd86c340868d20257131c Mon Sep 17 00:00:00 2001 From: wenjie <1569925@qq.com> Date: Wed, 27 Jun 2018 17:55:55 +0800 Subject: [PATCH 16/22] =?UTF-8?q?=E5=A2=9E=E5=8A=A0Redis=E6=8A=80=E6=9C=AF?= =?UTF-8?q?=E6=80=BB=E7=BB=93=E6=80=9D=E7=BB=B4=E5=AF=BC=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 ++++++ ...00\346\234\257\346\200\273\347\273\223.png" | Bin 0 -> 105576 bytes 2 files changed, 6 insertions(+) create mode 100644 "Redis\346\212\200\346\234\257\346\200\273\347\273\223.png" diff --git a/README.md b/README.md index 5c85577..072b0b4 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ - RedisTemplate封装工具类 `redisTools` - 可视化分布式ID生成器 `distributedId` - 可靠分布式锁工具类 `distributedLock`(lua脚本实现原子性解决断电问题、valueId避免错误释放问题) +- [Redis技术总结思维导图](#jump) *** ## Maven ```xml @@ -101,3 +102,8 @@ private RedisCacheUtil redisCacheUtil; ```java @Cacheable(value = Constants.Redis.SYSTEM, key = ACTIONS_CACHE_KEY) ``` +## Redis技术总结 +![](Redis技术总结.png) + + + diff --git "a/Redis\346\212\200\346\234\257\346\200\273\347\273\223.png" "b/Redis\346\212\200\346\234\257\346\200\273\347\273\223.png" new file mode 100644 index 0000000000000000000000000000000000000000..114d8d9439fed8ba755252b993e5cd5041726828 GIT binary patch literal 105576 zcmZU)by!qi)CM|8i3lnPNP`X_-NKLp(k0y>-8H17g3=N*bgCc-14GA@45GRzJKP-IeYK*zH6<$_Sxs0wIbiE$rC-IdISIfh!hpxX#xNQNB{u0=^-}e z3jSPOE9QV>E2Sz00F=iOUYp}$&apf+<)s1eewq!;iR60~ZP~lKyLc!BQG4g>=YOkd zWNu}-x4Sp9v@EZpew&waetuD2RW&_3i|j^z&&?Yg9c}*mw_YOV4%vF`>V*jU*UIi) z2LLbt6yHf{`%Z7qZh(JIKlr5aI*PWv{ParFi9fpiH*W-E(|DXqxcB-oLT7%KRPg~| zpSpv{=iOZr03fY~!~*mdjM^&^1N2$b^3i}#Nqn_MXaIn*0P~YR+28=7^vVs5~ ze)X2H-5)~UX@LQML=1qKE}yDN?EV`YjZUNilxdP9?|YV@(=fwyyHQ1$!MGx@K@{QtE4FM87dAKmzuGKMp|_5Uz~{MW_-xu>M`zm%;0qvQd@9G>38 zeo*`$UZbi1hZoHAKgN0gwK!}b#`IyqM%&96Z5YtaX2KJ5VgmG3yTRbbwOv?4cfEI~ z(&M8(Hg*tTLA-l{Sf_(q2_Bh~bE?sJw&`1fE@~w_k}Jl5%q@?3#2#$%B1FkR9>_5) zidN;@Lktz)XgxS1;e_N5PDJ0VdJY0@dS0uC;V#;E%4Tw~Y5T>)g2|Nw!85P+kO?2q z_!p)QX%P84on(AsRd=^^bQ;#OZ8;sLVOLi{~cqC4h~=EaCaA?XI?_Bs~ILcS*0x^^(l3o*nqM( zgr>jPO)*~bQQF;T>84|f7f=$4)wW((;KQO1Vi5X`ps8IY**Tv}c zCZb&a9Sgp6-7Ful4n~QdNsdiFq`2?vy~j*Km&7K>>JivVqbdwDj;Xzm`vo)_DWjFq z_p3U)*>Gb2IuE@>81($!cW);fbI}&p#^4>U@eDz)kMIBaoWa$UxnlO7n8{)U!^%I( zBRkMN5dY#18&*V*dXA5$Cgyr61qBzvJ9>Q$#&$fkudS6hvwDx2OX-Pwtcanoq1~VcPC;Ot zI$Uipe1?gFg=r%0L2_>CBGnVXpCXQcN64q(jH7tth?^wx){>f-Hsu26@l584X0w0~ zX~st29rPxT+~nNr-1~5~nz|S}`ePubhq4LyZPa)-Y>)gFHn!`OPMd^^_n7TjDz?5i zXG;qB0Hw5e_Q|cikNiB|Ii(w%9M+vCmH4;WA6QU0ZmWq$Qp_{luYfvB z+=_@7%a*LCM+20di%V!qQ91yC^(^N;nk@2CFr>$eMosU%$bNLc2*rb6V}{vp-mhGa z)`j;`q07F`&2(09*P`^oC98CFX8!S_?cPoo;hz};wgU8Rg-rQg1$$528h+f4i~;3g z+wc9@ACE&Dek89366{8m&H|@Vq`CtT|5jg*h&9IV4GoMV};Kz1yBs|SEAbmBnZ!84VdorS< zPJ84WSots7iN4#!-A*1In+s8nj$CabK9SdL$*zaYkfmMd0_J!e82=`s=3gT``H+YP z!w-ym@@vx@LtJ;=9IA3hdCLSNF5M@idZv}lT5TGdq&Z zOeuoU9|l8eQLAnG(}ijDsz)P*X*%L_T!{WcGmi=3=R0Cs+0d>CQfIO5lBO8cI z$)%{Y-1*9?i3^@>D?Ykx8!qPBwQ(pW{zU_6l4i2w6uo@j^yH?b;^O<^x*66dp^uJV zt82L{i<>B1Yo@IXg=3AycmmIUqi@$Bj^tg=(IYjF>Nw`Tj*7b)i5tXPnwJ-+ZD|fo zD*t*(rZ=kIhywP>qq&o)J1p(Oak#-1PW0?Ou8GEV93#H&r7Cg=vLuv}j;wd@X6qyS zB$F9$?9D=@G68|4YG-C)AA=LC%5TThB8b_Ke~rz2lj8gyy-^<-`NoUz4sG9WNekr9 zubuVbuOnl^|I^UkvBubj9zPdt4{si-_SdEf>Y8?8AYaB_S|y(F8In{5algB+nW7HO^)Xbqae5>)N6^ z`EIhOzh-r-5r^E_l2MUJ&w73v2{?DvA+33Wb>RdVTLn#kOx zu4tT_DsycpnTn|A41*|Vu=Hphc8DO@`s$fn7PYMm=7@%p3~tmGc|-(vEe*jJvdC&9 zvkcdvfOrV@!Z&g=)(O?`%ra)*!ZzxsmBIvFcw=Mp{sFmi3qYv}e$Zr`M$dVd#da}s#E1Nm7zV1Py zN~Se0aKYu5`DQdclSME81m%!s6Mj?PtRzE{UIGg*hWK=(m?89KZr0^RMrR9&nzfNw z`=T6@E$J~uMa^SlR%;R8FWJx1BOqmP)qh){m zHyYx{F6}Qk=D39}l1$91hpyE36`#N)4xN*CFT+cdP|d=}xu2^o(^qBFT{0k|<1Luw z;kGi7z3b}fWoJ?Fw;MZ@6AT%v-!t{5J#hnWd_Wq~l_dn#)YJ6Fnd!aH^)@`5Na^a=@G6 zQ9W|0NyABov9xKu|*4C+s*BwodrDfG>c>T&2TpwvXINfh)sCNZjtyhDe*SxS0 zLYUKZhB`xT@0t4UlZ*^z+r13b#-Pc081jTlOl7wqBU51I z#Pm#b!=f{^Zzd}I$lbQDqMWSTUI?V~L_lZSDXz2092~u;XQ~Qc^GVVuAvJ+ZZo!0& zNibTFiFq}(&DCzZn{SvvMb`|43Pf8rn`UMOKm^m~;FckP53_Bf>GpF7IaXpe6Z|SI zHFNCBoOrrqZoTXM!-)xLj{J#^YX0XVnl)YpJ$Rm&XO4l@kH5gnnvODvXuwP$bVlTQ znjKm>l5?iAtNt(*wf6>5DBlmR=9$QW&8@~V3%y`YF~o5d*D8SJcyj_K@&9yt5bg;A z0FC5VjV}?fM>VjJS|etkX+MauH=YFBG%eKMjpPOM*m9K#UWddsd9-P(V)4zpF>- z*Pb!^w4xVC|IT9UAykCwZ>=9SNkcWIqBfNadU)nG&iC9zP%}CJL(J|K0JxoHXw$L) zr_2->iDt%n==e|<7=>@kfUg=TDh%-ASNaTkK4${A?4qr^k*rC?`@3<^6 z8{<#i=HUG?4nB+RSLTSS8mgLnRTAopDMa6+nRK-5y((OcEm6q76m^+rtL{mahT-wb z{GhC#!=1G+XqoW=fZsIC_}m}he5d>ULn(#6zj_UA3&u#indQAPR^1Iv!HCCYJM%3; zIcYzf?(P2VbX@;dklHffrl4#Iqsm-}CP;<2`BTRg{t;|SnFr2kO5_JCH3G^NB!S!S zBS`gMsRc&Hx_l^Dtq?`H9EQX-vPck>IC%5mKTG^2)Nc&-o!?d_1;HBmN{qQ`aGW1$ zY1s0We8vJS;Qgj##?OiSC`@e#3=oRLeImCq8nsf<8&ekjtTD0oo77W`BjjHvwV@Y| zhAiFM8jcN2)L^L=g#Eu>7`lRTR7*1)X+tqYcW;=b`Y}dzS_-`+s~XTqn@mz2F6z|Z z;BGv3@O}ox27L1Qp-l`89mp4()ay3+2XHNY1|zr7_?8^ZmWO8<3nj^Gc~s#nKcq?gCP<@rT`FDq#8V!TXE#YrlI!**AnF%9w5d zUDq2B9zr!cCuIDul4M*~5N<(VD>WTx9}0`1HgIT zf_*Bg_6;KbhMd|+#kJ9|u1Yl3pc44t`Szv?M%))D4XRyj*;$C8G0l$W;-t91qUMtuJ4EW2fgOx%RxPw%ca zWqq@1Z?D)A=|M-U`*p540g3yo7_GPbeCdUYAaP_+q|QQ>LG*1;s;-}7N9dRVrLA_u zc(O?~DF0=)1ht?FeBSdFP5YiJxNp_ZNsX5Y6U2aBO&u0WSBr;;IgZ)l&3xCaw?#lQ z&KbsWTX~OoZF=69=8V+@xh64Il2f@3X*02y$IiauN8MXHZwczF0ouZ_l!e6Mx!2XG z2qdWTwWW!Dpe&??jm^VyMR~w`tc9!n zgZ^#T%&kt#*cLM^2@CMcH#&3gP?^P-G(o^>N1bnZx#5Xv)miOR$#6S(REo#VW1T#u zCxuf$(H`O(4r`~ql|tF=MCDlkzP0C0b@W6aRzV}gn=NB3Z3v%>nw^Dh6)IkI_Y0jJa@a@V`8XIX8)o`a;=?VSS3Y_^?RZ`64d?8JpH(?~--o1Irf6N!` z>S>zk%*RT!nE58x0?v0><=uP{3@K+4y4ZF+txuY<{Pww}r;2OBIJQLtYr!ZDg`G8Z z#<7_k&N;EdOJbBt&j(7OxU~ed-*ReNTED$%<&bbWu_|OoxfJxYL#v0Qs{8(6B~V#C1ga-_-Y`TsBA9uJ)?<+#KXWRVe(FgK@t;mJIkdqU`Dpl|?^1+C+|??7NCGQ+SkTet zN{jI!P^b%yYJ4#bK@IX{ER`E+yfvANMyeKo1WeMC9Y%D2s1pA{F#JgD)V!ruPqDUG z*nSg}pPjZj;n=#N6m9e-frc#Tu<)hDs8+s7V>g-Nt~cRN(d36xPmd+UH+m)d1b zq7zRUpCyMftmF9gr!~cxMYm_Bp*uhBzwzoccr6jvu_)^Nm+!JdrFcttgF57v4ynpS zm^<|f1jbXWhCR<#{#wZT$wqmn$PALnIaF(?=~B@^BfBu;(LiPT%)X<;cYzOTEK-$+ z)2H)$$3_ z2{J(OobbBTHin15iFle7IByB`2Z|V};mJPJkx7LYLJ54~!ZMv?@Re)G z^DU+RW%8zgvr-B)ACzG(sqUf-?%UnG=Hf#p1)9#OgL5@)jkYxOiA9T_;sk1{i&UmO z^VcYTugJw>laYoNQ^jHWl;U9iZU4YwdiE8_W5D})$Sogog`cjD&c_m5n?O=?NcqHb zKa|UOH1mLgx=6{VvU)3g^yk4?ya-eG0pr+f9;J*V5Y^c{kRmb1TMx$V#)3p>{X+J+ zDjm>YCB$gEhum!juirgAZp`+ynWt{0hA1Pvlh7ooNwx*-`nDm1(RbuA?=Oxb%F|ntCb50zu#aI|aCQaY~GlBVT2LURFVeu_@{5bWuXmH)FPJ zBInhez(_5;ogMyg?W(p}z>b0iaa}Bs>Hw6!3+>D5zv$?|s1gHGuFF8bZ|m-O z-b$H=>UQl<-Vv+)^$S!Wu$FhaR z2xt;|44cb6sxKI@y60zbvsQOc>SdK=aJ z`z7;VwI2v<^d~Sk3u3AF_BnFH>NWk|kGnDF5&Kz}QByB$1>%tJP4=NM{u$M7mRxY^ zem~XJr@^pN#G?%#n;iU-Y%x%Zxbg_ryv)~@4F|P@otNYvF%Zs)Y}}VoFC|XNUv;1C z5yYnMJx!ERAcn=CK~dTKSB)a3dHwbssjs8NR) zb#5j6Yoluqlpq@uC5sta1L1nM{?*`V8khkTkK&HM7jqr;E5{fuB-jwIFUkyoBL9k0 zh*Xu=1XL$zaCR{7;0A#Q&7SKGme+{}mj%7QX`{#&jYo}redAZ*3vS`R$}4+YR^K3U zerUnZi%Pv(W|%y5*a%=mfc)fh{IPLE=05%pvzef&r8+ozryh`A#CUT+3OG@-003lO1Z zdnzi0NJazo68f#WLkYLUNps*fzSX9NskyI!_)>#O9imUdI{rVHna?&8X-(ajxA;mb zYW%BsBA@*m2fnHHZLV*6f00=E5%m;DDiD%Y2u|I3`=2&zlCqqp*>ff!g%|_*6er6qgj8Ht>3<~zE1JGv7kY|gBK4yn zh^&%U0n-bzb)~Gl9O@l=@LI!DHQXoj8Xz?&*e=?e$TRa94d<#F5`v;W2rh^BGQ*kg zQ7F#qF{x1^SFe)_E56YP-fO9#YcNu(& zCcu#z>|#pdOU0D}M_}Akf1CJuMIlp;fjER=olOdaaRexyMj(Mb@xpEmH& zkjDchkO?-@u9QB%aQD`M96gV<#4;+v7+T-(;vHBQBwcQ#49>6qqq0E0>QUycpe9*N zakKIrbxr~~S{en~hRo^wgF5Q~kT*lAnX$zdyFrHsra>xTsonwo>(PsTGNeHbyN3jX zFk$%%UZ>Pk&x3dx3Oh3ce<)tKf_C7*=UZ;$$M8ED zu9N9<+Zh|!yefPpV6mJoOLu`X6k(u^_(*y5@3^uz*s$LXNp^XE~kg<#iL%n zrdUG`BxF9>5!X^8Q6T0;6p~$uQszACYJG;(s3g9Sa!K)ed+VaI`w5*dDyG$SQQ*pi zV7%3h=)wesI4?rMgO#~U>tTPSz~j|f9f<;Vc{l!6jgO9WOc`Pb?L#))6d}>fNOY_t zUG_s;x0WR20q#r~T0$PIE1=~zOLV`2;qli_J#?nud|<^5C|WjVklxbUS|bIj(t zYY1aa^mg`bgSYQY>DvEVKlOZOp!De#1qoL%Q$&W?%8RplG&0G2nrWN=ou>}E_IIfY{Xl>0)wQyBHKN~9-?h1gZmi_B z7zQd06uNvW5m7v}4DHoOpzXQ_??B$?2@18nftC1$m!2dJ2J8yErJd+$LB+3cQe3rw zX&E1}le@@;ZJ&(FRmU8Y1FS@egJUXrk(20C_6!l)N zqAZT+=+9FfRVi8~jmz;+d#)~@|0lfCCn~l|zpeb-i&B8CuXbY4Qnrg8UC8bSbZC#p z+g2F)<61m%3s%D0983(7a*s#dWuSeN98LvZMJZC(t~{jthmfdwq?M__Rl(p;4D|Uf zeMn<+@==78bz1C1hdz!|U8Hb0aQ{aj3Htq&T{qTeX+>fXAyYQFB&>irq@0mbPSCh% zk+0-LvBWGj2;w4S)#K6=v;ps3ExPrmwr>h_pzo@nezPq)5XyE&P13O#7CK{NtUEN8 zRaO{pstQeAz>Bn`#cXkAG)^{+x*NWtqa3kG|L`NRR6zT6g0r}x=f9o@Gy$1EAN86G z2#7ycl8XkgaE@4bK@?)~o_m0VZ1=}xfBwU>d3H#f6D#t!Zifo_k;z*6qigGj`qB!| zlc-MrJ?`+085BcNXz!gf1S+ryRvb{GdBJ?dh|DVT{r4el4*U2)kF4m6j*h94bM}P> zazfL2AmR(lX6^`xG*joK>w5;=w{@wpnFxQ?NhT#bLuOi?$+KI*M?tbAm$K$I@A6fh z7MJ)Khupqlwd3VpIRW|TEswK2Z8v9ISYS>El{gvUc+SgWN=jVjc3d^E+su(>q~aTQ zl6WTGyvN&%!w-4R#er+9fub{_|1PU#)c7FmYM{@-1M=#prcY%quANelC7Jd*f&H!Y zKRWHQR+YTOOw&8gO+fLl=u#i|{+!-7Xp{nT@G&=ySXN()w}H5))8O!g%+ChaoJUBh znvD4#=rRL~-$Cscr(0D>C9nF{v_(j=ItTxx7f>(!@!EKai2enA#y@g*1*&VwgJ;0p z1#f-VzlNvH)ryB~hFRIGpT@ojfKJ}OTI>t?&?BHugT|t;>UoKsAcvOxtTX~&Z^^Lz zbx?QX5>7;YN=rVRrK%%1Uj<8{epP%RocoAJR__G6R-S1o&ycE886dQXmk>eBCpNsY zXQbfr?1%_hYZd|kro_D%wfdeLaq(~82zT4Ti9Jilr8I+`X!h%0eIcf(h%qM?_92}K zHM7SJ=}EU=IJ!ws*^niY&0jp0rEh&Iry>Qjf5ZOj*gMjbWGeFrohc9>v4_klL6qaZ zu>$e6l;8W!vO+9f=TA79ae8l(dszG+4Z62(Z^4_ENHglIZvdV{g%6NliFUhf(X%V0 zt=|>E(YShatCO6~=R5TbTNRITTjThT*d99Cn@N=3nT8DxJDJAHkL{J2o7t@^^=!D; zufzk8zgz|78x*Y!z1toa9BUexSq>{`S#!zD@*HS#e}WvsetbE|I>mOl z?hazzVpY^Eb{}I1!T)plvpG3h@0!81GPhK`#$q?=XxXsmyh{Q!Zi#$6kJ_8Ds-Sb%1n)F0o5%!EwuegAfv z`dza6CHkjkg0V*>V-8C9Mt+7th));nNp`DzW*M%hSy=w)rSo8H5u@B8Q%X8H-Z7ne^SotK=xoajkk9Q$%B^bjXlhMg91&Pj-T*96O_OJ8 zboGVPBxG`1yn?WP`x9TvM)#?A8@g8e3eoIM_EF)oQlR$Ee)GcP_1OoD^q&l5Nk5gy z@@;w&;$YfhD%~={?-E;#R6lK`qAe702>Mujbnj@xKc()7O>iH@jr3ZcJfS`f#3Oo0 z`eDT>22rfFzsu5T1VugnT9pCR>+;{eLi=(oK%eOs?RvVx;wSr7iAz@d*LDdK!%Y<7 z!$o4sU`D;_A}wIoq26@eQ=%h8(V#?&p)m{SRJppmfMBJot5-b@2lh8U9G)iSxmywvyt*s* zlhti4v~jxKljMQ_SM;@$D5TOeT{%zz%%pi`YqeRSGIQ9>w?Cc$Wc%_A`8QIlUm*r= zL4~Tky)pL`q}Vozfgif(cDs*}btkp|K2wO5;BDitZvU5d)hO$or{t=~;}(flTH)_~ z?*18V*ua&F_{jotctD|@!^+VA8k>-Abym|Awf^YYbpUvmF7Wp*w4~l!p}(*BBf7re z2(yF*j959JDp`t*=p$af^89+dVMD{-&o4;^(E<_W$D?p>&ctwju2uZ73HW{`fti^t zujnVIe!Sr<@y3m$WRS;^(j=5_iJI+soGdD)uY_uR2_k&<nNx+`^xFnTMz-1#dJ&S9 zFrq674~Ta)GBlZzL*viGf1~>kL<`54eDsVOj;mprXmIQ#y<#Z7$VDFa^yX1xqdeF@ zK)xW7+b%!PdbiY@{KrxoG19<5N5P3*w-3Uk^8*Nr_XKuXlp6T==YDv%ZDr&<@7Oyx z;p0`r8dyp7a=&FU7V3Vbhw)d%zs0bc%-|`Xu_L`|u&zep%FpeqsdDA+gzU^xHei_Q zT9Gt~C>e=kgZbPm(B!hj`;fv}j}95Q=9m&0Wy0?|88=#(%6^g?Y~OTB-|@?+3&c1m zd6Ef%xi2zkS(G=m=aV#^H$-2SnYW$73x0a1AiMY?uKmoD2^tS&eJl>0Ju>r3MJ>L| z6!2vKy~S_Yi}DB?J|y*hQ?nmVNqd%nVv9Eb2AAjs<)Dk2X}}DE)m0My;~yTvAEOeC z2|A`VdE5LnDlEt{gTxJR}ij({t+R##w@MFdgJ75qo%> ziGJtx-5FxMGcK!LBM;77&S>yTBA0zhU*;9T3CZ<~9iD&KMbsP(`Z}n#zr^hn+h|np zg&^T}DEiUna>Ui6H5JiA7Qdy8n{Ig)fzCFiP-2#10%g{#+k+QzkXYoM>22{8t!lMg zMmZjuy1Ugq9!dX#Lpk$ErOSsDdw=K_hRnHsrx)_#LU2Nk*j^%W8CaEqDl@8KNoXZ8 zpAa$=m3PSX;)90o+XMU^}l(W`dxJx)Q*Xx_7W ze~(XBFH*3`lPvfpLIB5$)Q9_TFMmgX4?D8FNC{*`u-lX!LAqPV4#a;uUXW5fdHW10 zi^KMwD!~AUcs^+-0Q?gF)5-M2DFj{2QL>9ekvcMIJvr0tEx6#3D9O4WXhE7lpN6QW ziNTKhgnEwEn$^SRa|TjZd%ZUfEZ}9T-VFAgQt0PmTKg8kH35I4@hk>zIvBhb>Gn$9 zWwOj%$b1IJf`O|%^{}8?U1x!D9d2lF&SK= zy;|ys^*C{|v;97F7iOTO|FbO7{4Na2Nv9S#4t=dE(A8a!h0~sdZu5{p!0rAH-9*T; zK2hiwX&76?G0RXOX3y_=Yfwia(ohi{Qm_j@>UD&*grg%?Y)qL?YLvNz6s*PR9Nhjm zB^n&#KHc~cE<~24pMb*2)rP!E5Pp9d>JTvu(GlZAa+-K?G_Ho+`e$1H_Sv|$UFedh z+498PB80BS$W`8UVD*v*S%zk<8q6z!@4ioY=_d4{A3V_nFQg%#i+ zy{UMNU1hu6k%;@BRT_~Wq3avHa(i##vFgGR8={sfRhQI?T$Lyj9$TX%9YzF>+C!i3 zGlk#3)S2l9mXW{>51x_i*+=BOfe^0u!GLQI5sB83K4^}a?S^!X{lwcVnctTi&hPZ( z)9+&~Xy6*g@QKd*2~r37Sg>BAQPj2+$;)LK+pG@O`Fs)P((2-}enju@^D|SJSQblw zD=7tXaCm1X>WeM&h9S%z?NJ2FA4#$j`CP@NKJ2dyaSTtN8by@E(l5JJ)W>Y$HyPm~ z+#VbMkoaaNu0p#J7Dx3{5AzM3X_xV_J^Oe^CTUNhyyB4#B!hT&cJ8i8REMlZ1)Qbl z^aplr2!Wz&l)(kG{&)ZGR#>T5DPPtGN35-rEAG;)HaB7iHw2NP)fD0XqJ+PF&VDeQ zxeD?G>R47(COK!Lm2Gmob&RY?`nYXM_O3s01LTL88 z59P`pHFF_3jf$lVb{Aj7&(nsDp=*@DYdm@@akX#awkk{;qR`=Gb6T7;s}=D+QOZXb zHRZe}{99q71O=YDYQesjPVSqNL7T>BL#4O1ywt>$^t)Skrf{X!*@B~VuN5(l{g($5 zkW65$Jw()*DE(r8T{-RwS_Z5&hfv=t3iti!kumb&W|Qc+42)&+w5bPhLvQYNf1?KO zaSs$Njw@bWOxVi;)?%WyrNG4BWeaNSJU^w1b55Vwl7a>zN}#CWcwN4t?o<6#cS5|)~ zI}yFAnEVsfC$&4|ngqnO0AIJ*yXrvvO(9p75>qDeTl6<>ifRyS1$CoY;TMArXH`HU zp?a^3on(fy6ui6aTnNn{xuuU!Ke$f;&>bZL8AbK()>YHu+(@EYwReb_5EQ+1czPxf zp9iGs*i1DL(6Fuj(2+~@ec}a0&qqm9=WDU-i#_66*G+?+>@;-#9Mw-$sno7|$(fiu z6X?^g`06+B%3nyF1wAewXdb*vL{D=YM$<~3nFkE?8R;bYe8Gp!YC$uLP)DJeYh^(E zec8+Oxrh^zQR$?C7MSI!iN_uE)v2QNBaMnzcsp?FY8ZufA$v_l97vXd_qA zm^~-NRR_ILr_M?c5m@J}OFaa`_iJsl@CG-`?&Ntxvq5o+Ch9z6wFrJzeoWrrKqfk| zu?p04^+xip)~alXG}W%`B4Iy@P;uNMw{+-6s*h?Q$^}{|s$h1EY1c~T?xOyj7un~| zNgX#+t>Z7@=wn=(+-lln*q-ks%!IJ1WXQ_x?+)HCGbfXE0XrG#Kx0OYuQ9fO=|gq zTKn$;oSttNPd#A39An3=<7&U<7wtt7VU4e23Dw1|5q#*%-DN+YlaV!tIL7tuf_3L6~o^-Hq+-^+nPLyg)>`u#gvzxc1gry#jc) zF^(#kx+FYcO4uapEA^?Px*~k0*g|X0OHM7(f9Q796ZdjKep|Z1yl|LbCm#ESqwQM$ zOY`OizZf|!Q3!7d5rT_745&9SwXFq#mAX{XwdqK*=Bm zLc=)D&>nJHr9=%TjwktD_*qFFa>NU{z1>?v=_^jCOmtY_7-M5#4!V8>Y@%{TYf+O( zdwE3E`Ep=O3o^6Ndsg@_LHzQGFM2w8pu}bS-MZ~|@%*)tE6wHIyO!9NcHs{>yt{-e zT_n90O(^e(Hs+}mMJ~Elzo+Lq9t(>PjePozZ%~1GJ+QJq3591guz4-FkFGyC0W%KG zo2N66nX0c_wT~~^kgA*?8pd`ef@TSPg(-aukx!gypeVOUi`;XaJ7-@eb=@u5il9ZS z2Fg!69ZQ-0YQf}Ods1Iqp~keA18eBbF(tZ+lQ)~ZNHS-guYORVURYP`zXQHyth>20 za{=wrrhUK%%bV0HI22V6E<1j9vAF9n)!VuHX9e_cB^*5V)k*=}=aylS)^J%+$ypL+ zeb-!W$KI6LOg3EsC@TR~jg9%eI!OVZ$x0$pIoqgpm372xxC`}!NIE6J2LdHq=dAQ| zep^Qya-4TH3wD!-BMV9RfSIlQ8>FAToF*m$$F~M`Y!__09brHr0gByDb?@6c8}ViH z>%CA#I0)~Hao{phjFud2lIZR6xdfoBX;G=wP3k0hc1*v04~i0t*vdpl@d!Pq8$Vnj z-u8mP=tX4&Q7L_nEa*Au<=?2c&Lp2~f5YnO-mpqCAr8g9oh;?etd$9|AS`xeeR!EH zh)pjJuV9A8y>#nr6j$aNPAhdjLn3TKv=9$xvQH%^IuVxZkJ?nfD%L(W6lqWamv2S= z%pU*sf_uxyNhkV3kDI_K(dmzRpl~!`vV3xkkv=>m(%tpy z#i~D-(g_BsOj%a1Rx1J~?Z^~hI~zN*wD}wVPZ;rC8QkTv89r0fC;ji}0#7b`3_otB zf!Zix`^deQ27>)TG^KL)&iW(WbiGIY>t_w($Md2<{6EQxTz4s7&298&#=qgDq&L;P zq>Ot=Rr?6gc;Ohq*u7KwHKs`iz<}F;GINe$!v<)Ub=~Or!vJqzde^l$xRL=BI)nv% z)Xm%~2P!`U-bQ%~DVVB%05H(*G&^YT%sIpZ-VS?v1y>8)cSQ;ZSI=uW^@*(l-tu`@ zEl$1|!36+qBvSxYm{J){{>O=>r|%1ksxc>!O6Z8?*DYeeTR-Bf^>+}&E&u>bcZbY- z({OJj(>Vbccx9VI)qsCJ@c|5qUYA)44kNUHPw$w_%Azh}sTi;USHpR8k$GdEaR3Us z$=(_++i$`9JlFt5wh^xGYLR$MA*Zs)R-!+pB^F?N#FvR-%-b=I7>J=u!qrVA!*RII zhzA%>ViB!WRwM=}m}EKR#;V=_n|q(=NOD~tR?}4-P61Fb{3(m*%;W-mz#r!5X2m3C z`VSW9CvGKSDwRwg62ztr{P#~`Um*>GsfVN3AX`bqG&R|2in0~}@d4ZN3QmKe(J;WD zAOU+-voU32z$A3h*dVP}FZaG#F-ubeu7xp(xAp68vd93=sRKuQ@jSpEm#U4t9GRr| zY`B1P6BS!|KIN@TJWN*Tw6+UDqK;;*5CGsp_*U`xe{eU*;TKan+L2x$Jiz%Z%% zsU`4nx8_C^KBi~w9LVQ={C2Ut!p{1b-?1jZI{}kBZHblj#3)RGwE!Q8i}prHUk045|2w zKB)PNs@i0vAq(d_|J0>(x+aegv(ql!`v(ZzS`nS@w zOt$j2t08b*I<1+4xR^?kr7c87EsFopI+1%~v814BU1zEusWEzRY^e$2@6vGsZ#$cU zRn#8c=XHOuJatBYX43ns$2jG_n^3g=PGlQO^du!+I7^!iRFI`>A+{q>bOu_iPL%VD z9T4wl7P>)JB2_~(n1zxmTwUy5_*qJotcc!*29(g{lNYal)VyW3rsB$~fO+L%@*vpt z=SWVI!%Zkrm;;30w@-Dndy$EGJ9yHlCE zxW921LU`L?Ha~VIGdZ3=cOWwM$3HSrJFlR*nGQc|vu*STHr}g8yf;g+(BKrYQ>#Iw zqkNNMhboDvXlMX{GFF!f+|u`ZfMz8j;k9?wd75F$;-;gbC{>rVT;^(^37e7}TjuZE z=j~J>8O#M^RE&=NGwXWIs((ds)FzAPi{1`a7}7NL?*HS&yv5YvW2!O+x8MFke%p4) z%)q3|P711&Z&+szw>|`ZDiy1a_a~qp@BTs_57)IpZ-mQ%4c^_v!+AmGzY25sQg~DI zl>)O-nJeR}{h6e-*+r_XZB#{Cow_^;ezRYor>D)?%ZhlcZRvFZKhlgfP6Bm*0GYC; zbl!lnVlxkB(b6+Q_xagxVzrzMgg>3o{+Uqi2Qvt{JuGQs)^ZYwQu=!S2mSARo$+Tp zN2S#QWVz&l`fo)7du=fa%|SexTXwJFKOpE&74s)8Fq@M$yPOU zxKfyVy|HjoNp=Oy3Y@LvZjMPWFXIOH*w8I`W#9o!f1`3)w7GZYs|;sFDT zR3guOUbWoodSL^;d6LR3#71|Y$i9y~S+OdWJBJm3oo&LLQp1qARqP^YTamO(AjRH; z`z#u%VWU&a1uQIUKk01vo6NV0td6Tlq2A{}Xi3T5D2@mv7x6tR+hZagNbemh;EFo? zszO#b{d<_N+Uvb~tBsX!7n=BgfHb4T{L(^PX|}YBi=gq|8$#0fBRuqwdUFwNt=vPz zl9cZn-^n$PbKuu(>XluO<^ibV$P#4Ljaq(z>sNUPTg@A`pwyOUdX^bSGio;)z8;6X z6QnR>nZ=Nxn$9*YXa?;u5ch=SKX2J~bvl7(-uN!;4S`|1qZi+d*4RLNX%HCYw6wmo zA~K0!OAK-83<^;6aQmjG^mH}Zgy_WlmC?AUwR{J5OBpAYRJx!?(F)LMbD8qVs|vZC zu8GEFRBMPg5h12FsNOEd^x_472O8)9MQdC7{0Yr=E|QH?ut`=pnqVX-cEmaV(gG>R zw3%$(2ak~$3HaY!fNpce=fy{FY8Bly-;dYGx9UQV;(TGgzP)1&kiMm_<&hK2DPcv5 zoAwlOa<5!7jAz*p-ygI<7p$arFjW&y{Xs0wpyYHxcZNC|#=E3y5q51;=$~Y>fI#KSIh*#20!KA5(k8yO^S}>a z%p4j+`;CYUE0MW50RVr-ol;;NcFg$A(c@#Cb;my*pL}!x-%dH0JTWg6sbXi~RFhQY zn%<5(#HC^`T!^!Gm3REG`;i2}k!%4D%KTiwQJj1z4jnm6UB0yMXglcMjLf&zQ$#QE z;bC}-Cu9iP&XmhxUlWq+6={9k%+4rL$#q5XL;bCURNrT%7IGc7MVVaRS;#D^K|IqZ+aZ!EG-ykALN{O_< z(k0CTf=YLHw=^u>Dh*P*AdS+!NVBw(ODvr(y>v=SKl}OqUeCXC&pk8u+;is4IrE;G zJ!felU^DOijQD~P6&z)%g&Z(6T=Ve8IAJ|na0+B>T@k2jlbYU*c`feWZ~yjfnR{Gc zE>7?T69??Cy!8f$@8U3;J@-BoArPYwRLsLuu%$2s9_J)hXQkXqjbt%0u8#F(h`7`Z ztu79E86vn>3V9m;&%sC8UrYnLF${5}Q_2cW8cc6Z$cAezHr*RMetqS&-$DA(gq8s#Sbj=)te!*n zPMCEY)vk+FJ9_#3I5%O|vGJcDCczTz<*v++|K{;yt(#!?{I% zui{Xsy#CEIPgAmv8a~xEErKgYXK8L9T87!T<{y^dBRZu^Gx@h#ti$3cWr#ZhOz~*? z#|6xI4-WD-e2lY0;kZ}Nxo{+ z-6bQ?U$}LcVbz^OVWQEyN2w>HXqy4!&Z+?%n1wv-Ki}nO9!P9S z#B;Zs98)jnU3|5Wf?fh#3PKQE2^+y zCd*^c2b;!9_3XzdVJ2I?N;PSahQkv+58Xc(QL1@~7EPTprgu(`1y6hm-ACoHu!D-oceNv0mfK#`BBk}=s(9<5 zT;RpWf1^Pv?P6X97p8&wGj?R(qE;Slj!jc(_43)N3tL-?xPv#=ySk)Kh6PAutF$<>JH5O;q**E z4*#jC=HwiaA(}K*V^3iddZk-W2_ZeC362mJ>#zoTOW1ZKD!jc#`Z0TNDl>x|!ih z?>n2s=qFW;j6PQO8+nn5mlo8_RDj)_@!P1>jwJYe7VS?5{3P~Auh~}QLAW&!+;~vD zc}UG%GLYy9js8_<1(vcSj1&|1bjhOnY{BgLY7Q~+@jpqp+#0drN_kz5Q)!vYWskV& zeL0y?u>L$Y$wz`X)6Z3bbF@J7e|F#MNCAF%yf4hy<*bvDsWN~v;xwSNL`XCjAtN$3hQp-4|R?Zt2@py1*#W!%NC6p^eYN&A89^Ts+6&zN|8c*x~Vy>%jtP2Doln z5rM9RZJ)fb0DgNmi$VSuOkV6iD0$-|NuM!bq4C;2FIWF#5agfM)%6S~u$2APuawvS zq0-Snd)Lad-E&-MD2!bgTgnLc%WE`v+f5@k+JNv&rA76*Ki+g38e^a3wb9epAzJi` z*=`&Amgy-bJeO`q2E#Bl2kV><44w0nE-js=Q_)hC^0!RN9Mn@X5 ziG*k1+mn?`e8AN+<2~VWQ=i;koDS`WS23kpK;sq3x#t~EmS&f|6L~$dIs}LJpATm- z%h21KHO>61Z(aC0=WK0DwH%xW=D~LIK}S6>jYsY{M|}AC&m#E=Si-1w;5 zP5>vs=K0U|pYQt)RO_vCDs<#6_!vaBgx$rc`5C`4flXLO)c~T1r4$5J-)y-r8nbUO z@nYEJ4a?n`F9E=>P8`Z<*)c{MZr;)vqkt?W%|UE{tfy5E<|I_#&)g>Tfo29mz%+%G zZYfUa$>1%wYbk(u{s^e}ghJ?dhvEJ3|3W#Mo;^yAd`5SKPCtV6rEd>(l(|4_&m=KJoN6aM83XE4^!A!wxWq|iTJNti$XBbWH3muwXZeAJ z9`tj}I;yRI8&cmDln_xr&xTu$!&-(G@AvolX{5fQY-_qvZ68z#?@RL@%lfQ3B+jd! z=^hGba>_wiL_HeW%?+RF2|F~Csy8lZm0hcXBBw@FK{kE=i_=Nc{_$qNhx>{hp6NZj zn_kwNRl`kb|2P>eLUwS(Bz%PT;d=9zA@A8Ce%KTE=803%d({bzd4sU*%74{~4xy+a zCRT15f1SS9Evo*dEqP-ZgQKYFCRva@!ZRoeMg8jOf6{ABMj0N}>QgGDm@?JA1>0O` zm&^jTBjTobN=N(b2oKcmwnewIGtr*>V?q9)ol8+@R2Y;H+WOqn9cxGl@shiyGv(HcVC5fz2jA75P+P+_3 zj}fTVHn=e9pni~&Q{reL|7!cmHR{!WQB4}Qu$9AW2w?k^y*9#C%DpA3&G407GnM-( z6e!4;`N-J9iF~<6A!i%-JfjVe?)#6;d;>2Q0;>0}Wv+i1e+NUII+{dj3Dt&Z|t*3~%xeoZGm zZykn2MAC$S95q@E1`BQLYaM_s;>9x&{YsR*!IqL4P*JkSJ_+`N@2H)gxQ@5Hb?KPI z)a1VQP+Fb!Zaq2UFi!@-qBIzxEJ$>NG!d>1r!N14ySvObFkBVIpa?}mGyo3%Ou&sC z!r-5Ai}P}?LUF%HDM!5Ts@7sahdJdqs?yaIeuFvYD|wiw zWgb<3n~$%=1Y68Ks*U7AD{G1+kSV`gx`6mdzFw3U!dLXX3x>xb}iFK<_ z>_}pIdWH+pqBtXx97ZSg3B}&NxRil28(;W>z}LDY=Oy1qN#@Ph!Mq_c8_J+;)VdS5 zft0luTWiMnCpT!K(q{$5G*NkmNq_b&CCA4szNM7| zz7_e7qW7@6Pxoy&sDZfX`CcP~SXX-~-C&S;8d-_;+*5zw&N8s6xN?xd7e0)(;ph)Uic!>~fvwcq+d{&FKNdRL8bAah=4Hw;^5c z7`xw7xR#5sOFFM6!3=SNhTBsZHe8rTkrSR7Yz$j?2LD@baM$8f92YHF(OJ(=d{5wy z5uTgQ&PL1&f(z37pD$FEbnV4f=EXq2$$O$EHyWQ<17OAF8l}KA+(bph^s}r*FLa!h z*}LMpU1C7`wuBd_p+-sf$hHI6c^kW=XLgC}&ax-Yw9YC&oA`h-H7E|{(pSQvkyx9& zQOqQgR^g`A(s}~d`q-#1qzXuH`8t;IvE&1e+it%V1jV})33$tWf5EtG-;ISwuMPl) zdOH=x2ZZE|R2J{)DHn~aghB-2)uy06Hp)qxjOBK%3ZB)Ge2cOI+K0S?6;gUzAW48b-?(lWYg+O4g9`-F=A4pa`8?2Xj4DT>T-8eF4_?2tLKfh^ z1Z)d3-%#J+-Z9=1yw`N-3FO|SkBbt87dy>4h^^9(5F}ZgvY^gHX332b>A_GEq_iaCUy-GR#FlBScjj%(r||_#RU3C zJFQB*aySYZ&PPdp1#H2GSz#8L8bjS)7mxh8($m;2PY2&?K2lE#Q~|VA@x_~7!&V9_ zi@mh$1w@w%q5Jc3DEmM8y~isz)aWt!LagRFFB-yg=mRR_TdM2|!U?hK3G z977}V2u0+U|Dh&7w7zX*gzbK?D6HUz6q+@Pt%b3|(z1mybA!UYs@kLgwb`Oizat$1 zl!?54pI+kz4rZprIA!IkPRxk7yQQP3gJw|mo^Xx~YHU9NvBLKH$9ah<76Z>$cmtbt z`6o9I8WM`;KYzWL#9YsuxSsII&c?`naVIxS!sYtWj>J`hpIry!(k3eC8m0O{)H5{n z9f-{f9L1yWp71i}3q*J3$WqW6lN;aS>I1n@=Ay-C)CfU9=)!$*M`hDr>N||>z%xdx zrI9aaMC4c0KSeSUMZn}4c%(B6AX-G!FQncPA!@=(@&QWj@CYQxB4q5N-baD1c`Ou( zqNKV?1+abOldT%(t;dPswamluf<7RAYmN+tL?uL$O=Fs8?hBP_tWVhRzcGfKVC0?s z9u|0bqFp-0^bMceYL3rXC!5)Ia*P|^f3QU+pbR6BH1=FVz%e-oxTBKcPiRfrE$ffu ztzWU#Vv9CmHF~o+^7h0Eq7TOzI|BO~t zzuU!2U0fUIIqp#y2hG%eYy_l;Pj?G`AP^G?V1(5nRO9fLo|%9$dIAtT)=`5L-& zdu?0{qp7}ic`sk7p}2EiI=G;SI(UNqrq&Oz_?OyC8rH}R^ss7}fNAe)nm1S}oD#bA zaeNg)?}E;jU2YDOQBW_&lz#m%a)YFZ5?c!?Kodi_L>&CBENEK>1Q9VeQq+n0Yr$$P zpj7lo0M}F5`D!fCl5ChX7N_46crG;qkNh4SHt~*ej+}lT9Q7*CcWM{}#7(;W zV=+J@<#UDcbY%auhEEB465z#4h{zJ{-8I4!wBJ< zj%MO0kA+4=H`c2uKD(f;&sP&gb{(@On#|Y1dDDJ1V7l)4vhl||KSP7l&bR_z)k8i z`ov!wh$6Z(Jm$|m4^RiWx1Da2{5JnhDGM4)M$+&BtMMYT*Ak326KDWD(g4!kTj2rg z<2~EANxxv^c@jz>%p&UyJa!@)`e!4Lgb4=~zXMqtNTQzZ{Wa{;{S^)UjRT>`i-Rv% z4hNv|Qi8PlfNk0HxJp(j-IzYXx&<)GUk=o+1cHf;m6pT|D-rQVGYPRBbdU&6L5O3I zIp$OdE^*-tOT+FU*y_8Hrw&3Fa>K7 z0?+29YMFqF>RAQ=*v5lH3s?Qq#Kvung-pN^3>R<<=8 zWxIB5-(O*B^Bu)XQ>`p!D~&;mvYaY+ybFLW=)!5QN*;a0mQ6p&%!6*gF31PTp`{aNGxW%Km`zxQc5 zDP1mp3X&O6O8tJG(fry{R91yBRyHsKHBtr&+tFNzP}Nt%$>;Rf=1!^yzRI7NGAQ?a zzQ3E3qYDh!Pg-Sn(7mtOeq7@Y7c4#DhZm<1VZ$+7)2Q_R<+jPIx_y4%=KGA7R(?6z zbb>AjAO7p=NvQPjLg>0>Yw5q&|M6v;P0+;Lc&bAl%bcP(mO}gU6&b%A1`k1^QbuXj zXH>7;f`EKt5q9-D1jjS{mPd_Pqybk?Oq-nXE4f+$or5$a^s7o z)$Syd0+fkaACP{530DLCUXCdoDKX)^`FA0zL8~{Wq5eW_lP$V25+5G$i;AH>|5(h8 zeaNnrOA-3@_iiJ8tTAx<5S*h{m9B`8Q4iv7GJg*`aebW!ZHa};iQX5qmr5-XTZ4uT z@DI-+$+k#~?+apT2Df}7kEGS9ZK36w|uGWXc)k$_GK|w+x1{j`C|9Iqz!AAy zJv#hA5&c~qhUaYkTiTU9oVJuFm8wBb6vMAZ(TTJ8zFsAwUKYuM<3A0aHv({u134c$ zi%}^T>uVxDcKLyMs1cCa{9^BkJ&f+`!vM^}>>=9$RDbVLUg7(xF{zA~1z3w5_joPf z)zE}qWCKTMJM;NCB;qeWtv$7s>lq0gOs(K=Xlw=7aVBRU=RWzN^823TYSkQcT3CAq z(e+xL3j+6$SiXs0lHmK%j#u|dgN=*U8mVA|r=F6dHMGz9LDOYE_T|n`qaZuq*&d(s zd;w~7zD}fumUfoCTO%=!S2&Vl{}1nQEsdnLE+0^BYNkE*3}67bGq|%^6sa}nL!;Q& z7hD*Ul!(N`@19LoC{&;)#CEG9N=&ZU6Rn(Zs3*tYWw=S7Ok2gJ03$1>63=M#Bi_;7 zs-klP)8@#Zgl???mH(|C_e+mJ#VP$gqXt)X1x~ai9gBA7Eu73yCy$e*$BIn1``Khs z<`_el!dREy1$8l(=sCF?tFOfr=BJ*-r-27fPPIR$EqPmkJPUS zxb=xDoWZo}u?yB~hArl3{MG<-)aF|8_V0A{burRd*&CSUllFhsz2qrKlk1kRH}Nat zvkHap4e0wAl5?nG?wV=U>!wXvFZ`Uk7^mk3!#kN_0=tXi)F}O#rg`^FSqHpxZ<=S^ z=R9y=a8LeHJjp-`dchsPlq~$VJ9{@~o+k@pGv9BCvXE@-xzb{vGsr|bf~SIY-9%Zh z(1Flz!GH974%y616zVye_vE0Mk)aHt-+8BF_DCu2(H?eHXoU`94S*K3)jt!m`0vZ= zm-KDXa(*V5AV^Z**o<2J5G!At3nF2{<}jrGSuGbi#}8xv;?k_10dAsJ(Bbecy?)|E z1a9Fd#H(Jiet6e2W}>A2^_h4B`)dWJ6h%>)?1*QYHthyOh?A%iH@SNe2mQ>N_~sZC z;tVk}IHizpYGDT|wE@kvEORL((m`^T{%E90EYXi{d-gVEIDH9|Goo==+Y#JfQUXnh z4TV3SL<{Q;lh^*`{)PG1ZYAy~#iJtY_fu(}UwQF1Z4KN&rAoXFl%E`YGRR2x2Y*+Q zM1JP^CSz)aIAsBB_Zg;DJ^%IXOEVVO>$)q%s;-)6d`Swv;GMkw0~r!je47$~d$U)1 z@8?^Cuckg4{T;~{E6W7l&s_ZUM~qu$mec**qi6K1;?X%?AN0uTus{=9F953t4D`PP zsgf0<7MA_yP*~-RSp;+shTT7IvE zCMF<-$jetRTgmGDi{3Cq&DQb+T;riuNmNEO4A^$50R{}Cz%WdBti;*w8(vTA0(NXq zs@E6K!NZzc3i^{&>Vv$|8}BsvC7x1*W9;UsGo{3!Vo;YB+@Y?uv{lll1C*b3EI(#= zoAV=ND$aE`I3v;$72RsoiNgIyc3#j3Ca(~TWpdC>erwr?M7@r#{QI8!I;QGw*^pkE zyO$UKeoMGXX?m;MCuU4kG0|1rp^Fu^HtR=guhB(rOIjsx@}x)iK>wpwlM<9dL$4+V z)Fbdy<~5wLnIrMv097AK5kQ(kn}W>5o%P}84hmOUVp#vu#RY+=r-jV7{Qe$5>5x|& z#tPK)YkgI=;qa%rgG< zVu=cPD$QQ#Wd%?GTmdyr4dWvIVaof?CYViR z#Jn)}2HjT!OKA>>m;S-!b&deU#+c4qa-JDn|9n2U$=eG%picpH!1)-B&RV8Ti&O9# zFVZ&p2n{gP+daX4<}!TgawcH!7ufnfqyL?DMNeV^D$pLeABWRFngvg=>h>Ixc@_Gq zL4%dD3wP|=r}O|iM8^E9Z#C6=V&H?OYJ0g~8HXp-&*nppL%(L)b4Y-E6E5rCLKb{x zNiV3g5RETVJxas za5URDmxcE~>wD<=8m-hAPE+WOkM?-KH{14Rj1Bb%cW#$P1-+6`@>5Bq2rNDS$##!G z(a$2(V-gdEGIKK1su8{ha8$wgFSOtJ4KM>~fD%o9W|e>*6Q~*_+qD8zYH`uLY!6x( zX@BJ~=g?uiPav6LdN=wfJ^LFzWM6p$Xm73#DWf1>$)HUe$YL}@bO{0H;^BmT!&U8s zgo!ITyPewyMInk8>@*ej59i6&U={_eiRmo(0_EvY1w3ssdIIIthlNJjob;422*N~) z@qqdu$t>TX0%W8kC^wr3zp0WKt*vtkNx#UQ&Kr)NaB8Sa2N^W$|2^Rj9KJ^!tGO9B zIrSO(^9uvT+jmFwM!%i0N(Q~X@R@S>)>*nrC2eOREgQN2v2OL%Y3j6I5H~5_wamD( zJE@~<*+lzm|Jc3i3}o*k+9~{L1)H@QXNkQd%eU}|ySa^94Uwp%ZR$rTWxSK$myH`t z&z>zdQvvsJ(G<2R5w5Q11@ud2BZt05Yp^;vP>2dn~h-mtkta_8MQ;WWrbOtT@eHGK)Jfy!HQ+-&lm zqj>nlXr$4+=07$_p?1u!IEVJfQ?&@=(ZFCo;kso(|p52dZsB=h`AHoKovRt{~GUZmhC zV!+Cfqa3M|mGk%!)YAaf4mklC8{BVww36VW|6(Wvo(dtl`MX^F#Do<*Inmf37!#}z z#mtwx--$F&JAP`8+A-wwHc6ilruCJs@y&*a zz_h<{=G0Jq49HwS&X^W5W*oO-Q4j|Vr6XiY`eTa%mtXMtYh|aVLcOk!>pYCK`JN=V zA$;5q6^?WI$qd!arqwDCOf-%4@HNlq^-=8aNJQ zN-`^_Y1V%^GjDMoyZr|iMkq-H%C~JIc>=M@P(~5B6 z&UpCW$%hPnHM-<-oaK2lC_#{iO=Sx?SACV+;I}^II`bU|z2-KAFHrJe)GSdyJ=dfB zoN+qi!X8p+S!xW246mX5*ZdQBkD=f}BlvNiH30^OXe)9h9T@NdH5}73MSs9ERmUnC zYMq7rVc^zt92yQUj=e;vUMb%S33H9~aSdS0?PO({W|H#H&$mee_rLQ@o&@+ZxJnS^ zOD#KZIJJzOdb3sYCaCmUK`Q@L77lkf;@2Oh0e=1qvMeZs`nc`TG*6%a7i~JjaX7-| zU`19o#|-MXkGd}!BE%ggYHRe(Jq@duA1%{{DBfqGh zm}n*bF2c!HVt!pUE1tp1fZI>x&Z@SbB|GzqW^}~){_o{C%JC_Qjp2WBl>T0lPw4D@ zL>MF0X$DQR{&t6}IK5O3gqKuOans^}w%;KW`7_Z2T`9wh3;4;rM5Y z-oA~xt(45Oz#r33flU)$VbyvUZ(&cJYAel4+-bi7X>79?^K}=eN|KASZnJZ;Or`hP z6u!sm4h7gQ$tab}uLKLgTE31vUi>!N_lhR7sg4{%LxAT$DBDYu>e|dD{bbnD;A%c$ zc|9QN{XlLmL9u9bsW6d>Kf& zke^F_9GF@=wAZC( zK#a%NF7Dx$v#sZ=T(ZL-;=Nbo@OeJL%^W}zUBJ9%bc62Rgv)cX=YD`8AHwYKWxB_| z{`fP}9*9G9*7HiH|7*|{v$CM4zMR5NxhM=kV72-e5?YHH(oCn9a>t>|xnO+y@@GL^ z4LMGeAxuL$8{4^=fhRT}$x5k#MgA837$c25XV2HHx@}D}yP?~i8$^uuxBvF*a%FK- zoOp_U{-A~G)Cb(0*hU;SWGQFLLG7{_00$L9K&JB#Yq9v~7SmUKZ?nHW+<>F?Lq7kB zr~Lvlh@-`J(CR{GBFE63&%2+Y3B=kLdGPr%_Q3|3)d*h2?nV0_xWT_@rqOTT(% zuO{McvW4M4woLOI{q6&T`fIZ-gU4oC?fHrw$ejvH%IO*1i5?!YCV zJubUWiVFJivaMGe%6)%MSJ){nfPT-?%FZO)d)J=leW!5o%aG41GOK5a+G}H%(h{qq zS(uYb{Jt^z+JTk((uZff?c|$L7u@d-f=Z+IQ)?v4Gg00FJ@=;~eW%YIf|}Y@j%El* zZOEkPov?qSvy)3FB1uXG*dm+sYkA1OHoD*v9LdD`O40KiX|R$9=U0V=+nnr4Tgfut~cV$LJnVHJKAOrlY3+1ea}7%#%HI)hI6fD ziWZBeX0{>vr;^l&cEdSxieN_jA`l(T3pi>rNmxUBkjffu`C!n)c8}zH*6tf^AUl^X zSXLBt^haIq*r+L9XYZIRzoj+M%8}gd3}9DSCqIl=^BB@Ws^gLrk4&sYgq4vidVPJU z3CepYJ{9*yY(hk!ik)faDz~HwQ>}sXA3tgBvP#r0mKS@~0o-}zz7>0jp)~-;wC1m- zSJ4t~`q?j{M<)=zi0;S799M9eQXnegZ%B-FFTMPV4LDsCg-jWwu#302A6Q;sZ-xtMiDl6}Pl$*(dUTS$$9TpTLfCRr zycJ-d^^NZkyHV40(Y60W>6aDRj3PCxsm-mB<=y`0s?3w;XW$yA>=*r2sQBdu_ z2py`2xKtX794}4_Y7SuWU`K?bh2Fa!IPxD|+jG5*24&1taBIS4D-55LX%kEGPORO|`mEUCKp&}q4W zN}nE|5OXW!BD@~FU;1+~RdO_JMuh&ttFZQcCX4jU$^s)k?3g7$4W22s?c2o#tNooA zPmO`mKfM7>dU~JU|HF;BldGrKT-uTVQ5?3?wA;U5KpgtDLTzipw(!g=1!)ZLgEOGm z{Pz7=M9rH_>aLeu{9JeSFEl~-Lrtwi(3dRf*mUUfO&FY;Sk?w@dbI|=i|L{&NTwxf zsbux8C7u3|gy%D%xdEbqS}b!))iy!425NJAi{|V=w$`wt8~fQRyG>$c=&sPCCJ3^D zi7v2(QAym_!C&d`SU1@zO2<9FkJI=2ntc0L8}=hd{7?0Bl-0*&-O`uT{0SFn#4PD7 zro!^=njkL0UZls7i8I9ND|X2i7?)*(2FZ|$T+fpKFX2B#;18KCzQHrta@9~J!*?O@ z?K@>(p$?8`^{j$gv1aEq9CXev`zLETkJij`7o*t!h|*o6{6F%}ELH;4^rM96d3l*Y zwWg`k24ffKT44iTESoE{G3d~;Pno$TI9_({mXCQZhk?Ng?4)~gP3^9M)m-r)nMnFRA&?w2a6 z>gbAT7?!MuIp;t44hXB3l{fpb1i4o&^24}#_Ed6sYDjbD72F!90$)(&723uhI8g+# zy8BvGCNlMW)TgUydlOq9pt_%}e6;Tz1Lb3?vo*+-ZLdTh7nyGhT=srny0|ZJkv7dF z^`k18TYD&@zh&HLPtLN6Pd{VsRD4qpHOF@=Mwu%8Mx;4U#mw5}np%)H$Rxaq0vb4< zgFJf4%W3lVP%o9$={0D#ly`Ger6l^p2#`YAIj5}EUc4oaqYFA+_OBx2B*5CTc|IjNS~G)ff>+)WWGyV36m zt3GkdN67~{^Q&@CcJUI8{x$ZKkTr;IfF_;G`1;HyTqm0iLucXU^6WI|P6xL0m&xqg z#!pWWAAb%u%t(xHI~&kVfLJ+PLpc0ts(OKHZ7T50fP;G(IJQI(ZC;$vpVb-1X3zXn z+uhbOWdv2TJ}AJQG!E>gl->;4%mL+uq7Q<w}-;V<#!AWpU=N%qIL<0%x`NE*fm2m1_Ev zB@LM#=5{;K%8ssogkcz0Ke{9}ZTAbh5rtEldFVlDI>Kh+Yo5YLAzenXeY0*pN~v-@ zTQS6)iY)wYiWd&A(Nr58U3Ak1$y37Y+o47l25md3tX^)(j#l{L)tGIay}95j~Mgf1Wz%s;8LxIZEZhX^Pn~u ztX?K1^Py2&{2PWgAw&yTCK_aTv+Ox>swq;Cd#<~|l`LeNICV+rY9q|kmjw-#+{8G4 za-V`Kuk2$c`~NueaNfKT(te!qSjNOSq92*3A5@-WFD|_B&q8vx{HoL%TY8E>pVF6N zv%^74!e^~fHc>dB$`eCEZ^ph?!`y+j#!^)3A{gC=(&tTo3~HWZ{*%e?N<^Y#j4+VN zEx>225;wRE3UqPRdMlibM`)^t;K2Il;L?8v=$*jytl=Mu5UIph^v@2^dK|;UVDCpX zj~gaX`?v2q{gT`oE7kvuO(f;Qt4VFj1{Vp-Xb=HMufdav+BpBpl zMAt$s7KSEzIE<8}m@O^bOGS0y{J?sSCUy6xm^qXRT=`a2s(r>m{AaKtvqK?@kPbT+ zoO9d7hx5son+HUGXgWsMceSO@VQ; zRK~8;DKgD9`tnr*)=8#|qb!rjB7#o-vf-Kq9oT%uq`DlcSLKk)s|B=coGJH`uY8#@ zOq@=GikQ6jD&uwTHaA~##<{06mcqOPH3>0km0r3}1$h@(t5aeipR)-pzqWB@T9tP# zQJx>&;U`B|@*!!>3}r+Okxk;y{G;32#kliD`G_{_j8@tw`82jL528y_hA0{-6Hd%X z)M{w$Y9-v4p#W7{gGq0@ZeKc7M>UT zs5KPDpEs*S;%hYR!!^)ThI%GJV_J#!)bL73uNO3?u(Du$?|U{DF2zq;;u|CQ-< zyC#A)Q83%lUhe${?FabZ@rEqpIbq^Jv-dio`X7OxG_3v2%6v-#^qT%~5HJEUS||{e z?1e9AD!^W5C(yxG_XK%UG9Nf+y=+w+mte(dm}z_V37srM_wn|{iJnhE7~IVuHs!yM zOPKX5D&yu7h~7!1INK};d~<%ub_0D>R{Onh$%A;9b6+*X{wMmEZ+KN*sOBhRW)l~V z*}ddZkwIb26*EPuWAUw1cz-WJ_wy{dF-SOqRiw$#xS=B03qSMn^g&6LXmzEx{aiBV zqq*9jCGx!gIJ%eTp3Y^kAYZfO?^XEBIFkrR4NspwEbZ3b=jehHG2FdfC8_#i*SQopF zQ@>gjGzdA}dtS=Df!um$Vx|Yedh+SddJz=%h)({?%Q5C;rTbq1`)$`^PgHVp2IiRa zUj2oUbl^{$-Xo`aB$>Yd+8#Zz-3jhgiqxnA8gou3l5CbRTt(`FjoJPEgIU58SapC; zyHDB*%nrSBgE=P!-`8{*Ehw8a;ck{Jwc=@16IQgFRy_8gZX~OE-044c_3AY)x{g+c zHl!D*D#!g(!NdrC4%wN>bXs9|9g6TE)-g6DRztJ|6CVX|Wdy;|23pv+$`nxZVLP=v zMamNFiVMxB1nxNs89BCtkLZT#@DxFQ9r~9Xs3)r-ISh1a6&y%1d3ViNr^HQ7cRaT79DDQ~?k2yqTVH;Fi z_08HC`iz3!1g{d03-oqNwr0RWbYcjbLf-$VpJe6(0%umH+Np6MB^h|=E5Mb=6#Ijp zXAIfD$T%+^*nawLtW6Bv-SSmRqDN~ALVxG_rDlt%BT(n8f{si!c1<4RlLce2Q2XuT zz@wLtZk@d#Me&H(vgT6cbJ#_`i@V)R`~Wpm#5%(1)6L1|;%C^7m@ zMeO&iiscAfZRI-1$RzV@ccT9ZJ>}Wrk&=5w$mPXWb^2 zaADIEIFU(THj4OKX$<*i@O`{-9>HO8*16i$0s80M;rG|i4QI$?YPG9!>T?b6-|;qS^>bgA>-1Ei zOg-BGl#~z?CikWcq_u|u>zo|o$*jObK1@52>tVk2Yr3AYGcQ{Gu*q0$8rfy zvmz7c^4(ExP=5Z0@)w28TD$*n&h1w_lX@3%4OW91`p?9va5egws*g@guPlt7!?D+_ zo7e*_MREF_J_Jz0D!*y*`Gn}`CKc!IWM>N0Yq>yI*phHN)6YcpcZssKWoG>F?$L(F z39My~Gt-EW+U4@IUErq5#TOm#B3lMZ3xVKTDmX`G*3+wL(;wk}Rm8t|SkrwR--+D2 zy+xi3(L;Vw$!uin>;*EZ-_RYHKh-=vYPKnGSllW#O3bkdpOFZ^{A2$0GI`$N>Pk$b zrd1+~=x$tT;ut-?-@lUZ0Sr5!pDU{2di$A@#gUWj=V^N-HkBE)Q>JN)!lXia#OTA$ z)*bVARrP8}_G}I}j}Jc#M(^rv7u61SZ05AAWP(So|DoBIcsqa~_!9xF_ANQ;M0>xM z$|3velB_kW!(wVRSrS<=;X|pTqvVxG21~-WnRA5}WI7@U21b z;&rC!WUwnJPJJx$@YeM9oI*xm9|x#Wvpms0Bu_h{CzK!*Y)(mX5 z`@+zBep`ej;Dr8-VNpbKV$8I*E?=MU`FAL?%2b|vb# z`JZx+BZb${x0_Cm;`Y%kvB zjP1~nlcJ4239G_1kb=(m7HvsQ++;A^2eVl~Z*&|ZQSq`=Rf@MEgu)i4tmXr1gsUGZcPLq>K4A8q&1I^=bL~Mw%Wg9ULtoUp>;@IsA4nE%vl1cSseSn zGBbKGEKPj{O-EL6>V*~~b&x6iz|6kJt#gXk%}-oBnQk&A@ivpSgY z4ql1YBt~45|EH!``AntF*~81p1oT-QT%HH0S+$lIPO&SSQ_H;EF!mKzoe)+}DtqdF z__RpZC~%$Is7S;!*3!(@U{ia!DYziucMn$(fX81EN-)p$w@cuBE-;REHm(~`vef(S zy020kn8q-DA*!P9ds#Pw66a=dauldVu54iXfVzep1267JE16{!pZrgfv$dKGQ&*Ua zo8BFB*hVX7LsqEjqC{>P5dtA4)2ef9#)1p+oBxMjiTuDzNfGiPo>#EPrW z>3xgHplbzWAAgg{#4LQ428viosHqBc6d9eoN#2s&iG^2t{g_2iH|=^X!J*nc18B4G z3>BJMf6f^c^lNdU(8rDA#19kWQ<5m`uPeRF(pG*ZvWOP!3^n2tC)Z?_#ura}V|rFR z*qD)Jmz#m5o{TBrdS>dvD#zz zs8@2#9g;UQ$|?D3D+Sj6!3FUeuJEm^)cJBbiF#(BPns(^vtv6Mf18_$WSzf~f}9<9 zScgmZ7zNIBwFT%A_Wn7VH$povy2I>1{qaDCA!X$^X+N_5E`)#BC8PL%G<{`QRPWa{ zih$DH&Cn&yfPi#&cS)BF-Q7Ke#L(RxLkLJYq;!o+42^^|fF@dv3BM8$Y*%II3Fpj?1)pi6o_A?>u;*qjI82hENV1V==&by z-WccGd?Na;UEoIV@*qj8_7Vy24)c2?f`6_#)&(0 zea7cFl$(WB-t_blN?&|a|E(|8`$EB)p5l@4R?K_~J&^G~2tbk(ZixlhxS1Hd07sR;yVrmVJ%?hI}V5{QN^##^j2fKi*%cVSv8db~H zTpzZvI8SKVSA@vvf}y3a?QD|IrYt>{P&d80thju;qN3^ty6*a4`16mD%5wv&>pXCy6^XtKA^BSwPhZ=mvTe<`#4F@};G(Zz&1)@%V~$?_v+OhLb!Aw8vyPm#F>vco zEmLO%1F`ozsO7zK5yr0uBX*DL`FHUFzQYb0q$jcZliK;@i3?3}(Nv zgS8snJzJX^cpz`haepVbEQnH^`IVvc;Jp(~TV~$1*`QZ7nvFZ|Q4#2q4$R+M?Nxic z7oLj7XldD6i7M6OePA6;SF!>u4rSBKK8!rjeH(ap4v_ z21loKyj#N(Sr)aw=K%f~wXq{~Q|sJkqXQe}lt6&AazoeHhBS?RY6IRu+DU{R->d4>I51BrRbf{H zYZFwTdZjkG6J%W~5juT0U?p~)V!vhsthkbktirMHf{NAB741H(59f$(S=e{!sf>y9 zZCX8|HM1%IYDvC(>d5*M%ivt~N+P@>NG)@cl+RZljl$5xrpd$cHGo7Dl`)R*w_wrGjm(4(d?!eEuBpEL$!3t~4GV^_}mfEVIIyN59ndC>2MVVeC_WQRbueO{It# zwFFl(Tk%QGMGC{6>XqO``T)j!&PZi{)V3CZEB93TXzd9iXy_&0wK9BcXhcFs8)P$? z$f@E!>#D+${^^&sGAx4_90^Y1&iOKE`v}pf1#O$zrM)tk$Th|g=L8D>8Nf?!2UF=! zPN{{Ocg2lNWlz|Lv2P4xcw_cpL|Q)%yjT_k;7p}MI(HItXlSuYemcFa{r*=`2f zz)3!)%lzz`_2%lye+0K@Rd}c=t`HSZCd^#cp2@@Ghz5i!t%+sR^)83Pd501H=!mLB zUoZ6Ub=Q9B12gj~PZH&mP+(we1eQ3iQ1;$t%)1#+0>W!Ew2+@n?BTu52)$g3IAY5g z6`ps*tjyiSyJTWq2Dx-P=YZ@eOBup~5xe-NND}c7!@-rHGUHH!dhkaP1w)arR1)Uy zgP)x%1##M>AGxt_TP)}-_(tK%O4kywun#|vbL8V+I1YZeabnyXzbA(-B!QF0O8}OnRe03$8HRdFOK6X!jSC@DXeC}ie z=7=b@w;$I12^B8%FmxmKe!JsH|H0R+zyxeE!Vft0zml68AVKM{Yyo)yq06yLNYXB& zx+J&Ba&RUw?(P{wEv%LA++4lgK?O3^T#6W)QklicWd6jd7B^_8r{{@I%Wll!+7&gR z7NzR%X)_kQ3f*P1@Z>DZz+(5itX5#Y1`1AD?8Z3|I;b1JxL4JA=CM(=vziDo07(z; zmB<+_JR)FDL{u^eGcV1`V2BEZS{<<~w}%)SGX^c2dlS`;qpfU{LZRsh8ccY${j9&d zGmE|S8d@6{zd%fS;Q4XvqW&Sp;Ur%1_IoUC9H*bBvyW0up1wldS&{oX8Fu)Js8pTZ zQC+q25;kOC&RVhI)V$CX)M&xXEF$S=Yt9bMUdNCOG=5fd9n3#47TTv=ntH3vu?*u| z5Y1EVuRH}(NW3%W7j*kW%$E8+wL23eh!5IpatGdjFX)`#|KQVK9%1_u)v%NeNK>UR z;MguYBU8u@4K_OU5#=GeYe1+tFgGMh1qllU)foNe#MtwSF7|kOtGN9jp-~@2f0C>A zXIX`M)YJL%!SEMw-)=jO&e_6}Fts43ET~^Z==h!7q4a?!u=;yDIH)IWHXFj=4x7bd zl`|aXOcq>sF27vqYfRKxvBSyzNw?U9F?HHM?a0&nu1?{*{-kt?<){2_&ZIZ*cw}}6 z%+MstL90_b7`b3|8WFtD%?O=BD;lhDPYh;^rbKz>^-UF^+89}tPVQ<4$Ca;Hbad%3 z>S0Lbq?(3!8gx&R#Q)xe6S!CLn%@n6EYhk2%8A~F8YzGC<`-Td)&gxMVo}M|M)Wbh z@w$wub!1redv3VkRGJ%^tj~e$Q@($Ee$1(`y9n4f#)qNPKXJ(^8<7(@wiIvX{MSM-H zUbAg>e@cFI*l|~Xj2@e=mNCET&&;u{8|0%@J|QbA-~c!3i|dKii95}2y+FBW#U_gs zNc*007i+yb;!~Sz-x$bCZSa@50|x^on5)d=ZDSZIW*=>nYNfSd`M~QPwos1$u0ugN ze39_G>C@OR)=44E;NEh6CdUhuUg@v7=lh*q5T6%3Nagf@Ht8IG`b(erR%#YDx1EqS zKeYyXDVUDmf%^VYo6#WAZ|txcl;aQg*@W<>$}jD7U(pSm7egLAR%W#i;@vXLv*lC{ z+>0B27Z|IrQ7#7Ql3)2DMM{#m=f_E{FuDq)aR@KSlJhkgQ$^c1k_p%IQBd%S%39p)T^hML{7q4XN+egL7NM{# zLco$qZb4)_(kZMWz|NT8_5! zPu*4Vxt*h_qaDpZ2`Ld1>vqFHKUX~~TAPv89t$Bqt-~8IW-#yUdRYlv`qgrm)9FUw zYgxYUlUZKc6V|Lq@Y>Jt#%j_We~=2*OHuN{w3x)i9az^Fp6FA7sl*c=u zX%;mxbJzlgnP)dd0|PeL5>g=uIzlD5as6fa2nRO^{V#Rb22lgj)l{u^3x}Xj9RMka}+i zEdxr`e*+jdhnr$07ZD{0_i}_FAp%9T1K~e!hYaAALNT?bz{1Cpw|qVmo?Wfji?>Za^_M`JsXW~BBZR`bqdR$_RIgIvShdv*h$!T&7I0{^E zRKHZR40rCB-L)l&2$Yzp@r@6>JSj#$dZcTfS}rVdsMmGVsQ4lE2TQ8wt<7D@Q*jN! z3UPnrCjW3&X`lk4MYXxF@TrcBF#JLK(Q7brc z@(a&c8#F2T&%;%ybn}-)Q5IZmI_F(!8RClwJsr{Mu*d9@HF-=-cqRYqcq4b!ckEDo z@6JQ9vD~%ebNVf|KfT)|ib>Vt!-0^};k?XVCg^<{*5vXOZ2p@ai4z-?iVY~yk&5-Y zsH(nm;#whfz5Wg@|2^@+mUNx0&g6aJDMBJsKXuUc+lCYVUN zA%Kxiz94bC6DHoVxAoMRQ|uM|SKd7fZt=}+%|D2WxrYrJ@%twyvjbhZ@0K5(4_^gk zY)Sdsd~{v(QiS#znl`IQeMT7q-sY}k$i4EA_cKBE>z6Soo;!U*UDNp4S!}U~N=;5C zk$Kl~B)BgM-0mi_AW@qQkop1RBh0DLno~)Y4KWV5}Kl zpDTbh2h0(^)#z@OWOh0w{kHvNyx@nTp}^~nZ*i;fZ5)r(+|608W%b%|a#qoHG@rIT zM&%~qQZtJ}|87sjXn#@O?pv3%w0-QQRb)D{0X;t)ey_O@&*Qptpe&$(-Ssspe(-^o zz(a2=|5XY@k23otV6`HKa<5T{OFyI-5o+)DOt58?Y{(g+)p+6;+MK9@cU4%x0)z;n zZNjh6{>5E6^S9W&%$a6uFXGtpaMS&wDa(>=QVA~}w^s@nb1(btQd%T$VK;n+Vp_+J ziXb*mBzaGHxow4hna{<#zgO@6Pa#MB(S_?^KY#Mdw;A0c9IeS(2ZmoZ6%1{}8(5r0 z=qF~n)DQoh?GNg>m+kBOo!NloHWo75 ze1%F9fv)PYsdHgW8SsB}QqpFbQZZ;M2S2qTB7n=+&U%a+`14!+8%zh-xYl-;|q01iKj zlmq}!0owAz%uLt}-8|MR)gea82SsI`n^0>v+8#x>K!r}Gublw05?XyBK$R`_PhJaI2x65;UZFn#?_-< zY(v5c`q0FupggAg%h64LoUi*$LBPQLd^pZ#18(qO6*^;HALUE^AkKS(0yqF#kO}b* zQ@H-h>bGE9fxntQ!k8vcZl+(tI@k{Y8Z~EwO_mTP=+u7X1!{R%AM~|Pbj2g#j5u~b z+Q0qlHM>F$6B`E#B_-(VSVftDIhhSN+E|_0YHPA3M`L|}1i}u$w(ys@F(Wvz%5?W9 zk9@DtlRUhGRrF{te-^;xWFlJKY1+=Z*Lco~xC!WV2B7 z!H&}{M5TBp4r|-6sm&R=u{s+=)Fz!HIynPqH(ZAY%{)SO0Qvry$q}C_&*JXyVxNOM zIoS+;TNWVT$qjF|1Hl69WFPcy-MjEQKCTC-o-h-m;D8HinFVF3C=7T63monMP^pct z?^?TEn4J5>@J5=b&P?4vx|w|pKM{P;_8Lw+zUt4XZmGU)7djA$^t}~yw6>YZkf%;Fw0K>Ifb!G@5uOy>Ocw7%1pRt+WZ$1VuuUP{qRX6xmP<)G5xsr zh#{)(Hbe|(1CdCKZW}cN)abc_3D}Pyu~m`zx+1I7;0iL#R9jRQ(B!8r*-_?}8MZAX z)!3vA#set6y`V~6;sX~nJ#^nLOkM`M;pdi1@1US5qC4x-r(;~(TKpEyG*0JfE|^{z z3TtTfDO#XP{Wgu#b6q)Q)!+a0zx{}G^u5xcWD~0&+_qwuF*aaVI+JPK=o{i*8O)Sb z7Z1!v*xbsTDHLZw94JMVCo#Z8)--QBBZUFgiWOG8NxZ2|(mg!nt-m9|4J%||pV0;aY4c-(c;(!xM1{ z-Ev|go9I7Dt-5Q>Y~ra725F>O#{D!4shn$3d2U%bXJx2fnp=6aVgD7{1@BW%4IK>v z{)QD^l6#ifo^jkdI5KArH2=bf0jtsx4T*>Qi_g%4zKsmz1b|jjYMJJ&%@(onTqPyP zN7;R|QI_7syw~rt!^Dx>nYQ_T@hc=da#T;b8LZ>L$SsXL{?v0Yu*PtoUUhW#O#x46r%L9{_~M$xpM)l&8JCz;@ZdGV#5S?9|}`-{ynLhTjfn~>yI>sLe1_m zO?-As7yA_#F|zLT-(k;rKcHre}oGAycUO78m5 z21DV#M@#>u0JZWe+>xqR8hVL==NS-eye^a{L8^U?47lr^YkLJ8M{^y$k0%FxB}jVd zWHB5m!X03sjawz>Ja2!4vrPd>7LYC)R$j?Ik@2l)V6oNkw_osMH?cOKK8-T@zEOzh zs3WBum^I{;+?#9N+s#8};Bdl5dOiuz>PyVz0Qi=UE5H(|&z zWR32HK!oQT-~R3l$SWk}BzEn|a77}@1kmDp2E-H3&vHFC;^RYNh)X?eL3d8H( zWT#JH_w@AVKe`lk(LrMY)3aV9O8@4!7kdw8{{~nT6|V4E6c|WS2w`XqwT~pB&Z;0! zOwH5EmY@BAihqX8JzN*uY;-Q54L5)zh$I5e>> z-_r*G)lk0isS+#CQrNw*L!iNyH6~^I+XRCYGB+83+ri}wlDUV+1<1K;pu%z9GR>Cu zRWiaiM;G73-B0-J!VWAzhxgJOfLz-vXc^0~_K^3-OCzML=Bsv^uX!kZqw#85+GNpX z_^*R~_3yh>WcfOHZ_1f9Y|O~A_({TiTII+^3t9j)_v|E=y@8+~qPUV@4Yl`+5G|3k zx(Ot$-e^}OlH2&DP|VNLJFXTB?L8r>%Ycn_`+{FsJyt)`{4%vg&N^L)zMnG-x7@h! zrtaoLCY=pqi-|>+YEsdx_;^sLk#G^&tfu=-=I8N?<}#DLRC~13u-R;bRg;*vi?L#?~I?2(rud@Nq6pjDI1JE!lKX4H_9=jd>fuzG@$OG- zTE)23U9?ib$Li6EQ*`gx*F5pr|GyW&&|?LCOP;sZL0nhvg0~CC9VY~p0LxOoq%uAV zpuj;vW!|T+I?Sl*uoc=&Vg^subU)_HlpL|EyHgv{h;+{Meg{K%1G;@_9BOV~zz?SK zvSZ8T+Q(8q+->=Q|MUYyk|Su5A3#;t1^cu z#zV*ST(GTK-%Dl3wkrKE3)V+QyF6x57Av9h=|)ViI4g-wx32c{=x9HI@KvOCGUP#gd(2s_Ow;!b3&GO71y^uo$8$`aBi+SXM{)!_V1AutQckYcF` zpigK~*gbo!i(z-BsQBrejjODY{TJm>c;cY6}4JgTI}qo1R|DsC#W`_E~SL(oT~cQXVlY{PHT619?~YhyT`J zerrz&wpCt60URuRXI%$FjD>d$?`zb)erG?GW)vpl+9vB$0x45CE4OE?k?3Sjy(tSM zrZ-9w+OE6Bd~e^zTFCi?&OlY3Ve(Sk1S1S*O?Cp2sjPvNKoqkbH$)3LvAPi%BRSAG zeJdpnF6VS0)RWlAg#05`!p+)Jk^(WvAEW^J-=GmObqMaycW>qz2Nr%5Z z?#f7Us5vN{>nmW35&(xZF6Pid<^9Z1NA6UfU26Sbwd&$iy~QCMkaj~UE-{4AZvtR| z)=hnlcfik+vj+(L{pA)Vg^MS7!d18Hk~|x`fW6aE*RnoRN=6yiq4N*-+p$>C&{Asb z&-_>ff-qpAqtS)gh4iGG=e>hs!U-jP6!$dCjDrnH_Qr*bs+dOyE`MH6+>J{RCT!;A zP%?!jDd}8*-;XP*QxkTLF&i}yzb&1)&+$xjoODdoa~ZwCOJM4H4_&uJ*~n=8nFTTD zc*n!KtGSqY0wN2O<0^V55uviL9y|OMYjbo_Id%FCEgfkP^OwpOwY_N(Cqgcn4!iXW z3qu*0chQnOcd+&GHB{PYD6U`*)gO74RtBh!-V6X^K|cFHsOGf>LB&u_HzU;Xwn}Yt z-JtIQ9USFg8viXlr67;Na^tI@L?D;W6rKc zD>rpe$tN0TL-QB>-69V&Apsu~GrE5BQ#3~n>dS)&wr4Y`jqRs<2(ASG>TjR*8Oh21 z(p}b1s8pr-9)O+L%QW1``8pR;fBpL42Df(;?^B)V5fRM)Ff5@?$(?%@dy<`4m!_Pu zf-Gr1af!W01~lXNx!RJe8V?pXX7PKd8A7FQ$z#n^%T{#pxdWW6hca~oF`H~6X&3s| zBv#;v303z{X;%XINMg6yb+JHQkE}W3wqHu;-zd3@Tk7wBmH+_L3pNN~Fdc|n!S}8v z) zP4mS##MN0?*ZH@-$Wr(aia&z?j6uxBw33j^3Qt zw_hzcsA#FHv+GTy#|jwZ2Ki(k{|#OiWDrImUh6Y~Ih}_18;94M3=A?R3GY|L)?|Uy zX_t=0)IrX>d;Kp8`F2y9R2=Z!sh$0b5T`ckPF_`Dc_RTSOd>13omYgKArxiq>5v~` z#~F~42j8OJmG}JqPR+x?d`-2Fhzo+#mzSCq-3pn!N|)2Zh&0UB=E^p#HTgfp_r<{x zF#_hp>{~e*gjJdxVN~AweU!n=Spo{n16B;RRcr!>UotkoI)m%$>lyhCzxM8xTKeew zs(~2jlu~|88w%B8V{vwpjzA#HO>e6bmW`La7!|e1^<#@RCxZB%xMxj3Y(R24xa!%=Sp?lEJ$B0+*KpIrA~2qS2y6zPjAA zm6FhljKue#HD*5x@a5OaCo^mJy`8D3tF&(;zZAvNv0_0XAG`*+{f%zWcgckUug^tQ zK^*EKynt#gOMJ2ICyAmakXEbZ^uKB7))xZ8o4=PIRjY0R2XBbatLYK?$t11J@3zfvcew&B1)usU1U?I}v(CA% zv1fgBXCs^bsM2G~)l7`%)axk^qW25`Kw<7YNMM|pgW@wm=0$_q&JwbrXXv9`sgJkC z+VR37!OXCpJJokP6mNIwO)AcQt0qs!7)7dtH|Oy?3uVy3UsTI9Pu41h&&sDM;WN8% zp||KPgB<=BiZUBa0le6%n;awDYN5fCt?MHXN)Ip`bL??6 zp&}!+^8>Jo=;|(d^vQ#IFA4be&={kZX+YUY@cCXfkf^83nS3kW53HFAcrwLLiWBq1 z57fpRA8N({4!O0B(hY+@(Bjgwc`Ca&T0CalHExbIz?*ll=N`3;u5@ZOw|I3yQrfoC zH>rd*e`3j2(;@IIFH37hki8YzKUu)ttuZtNZxhXB>oecQql?Ci`uZ7oy!g$kB@)M| zC=R^v%~=2T_Tb%7kC=<^606}zZk?K@OsFPBNA-y~Y604T_j+WAxU48B6|uDnMOR%! ze-9E>kvVf{q;1R`(?pw`zAkz^o)7!TvS&YizhHF-|AXPuNehF~z+m4Z4C?A}13jqM z`o;ulG}O{5LAIsmv#p}M^>}Li9c$T|TN`~UYU5*i)`FzQXj%HJBuzOS`iAP$P1-ei zmz)+y;9ez4{=BDN!iKK>-XUs#G4kEAjP_+6Cq z)1tX4P|0&G_W;}=kgOUrU#mh5rpA5ermW$v#G`qVxU^Kbl1)1;xVOe~r_U9Gr|jOX zp%B5-&r??dY~}&zdbyZ~!iI`q^<$EI7o}bm|AhpKYubOWk_-z+g|y6>L%;%N_bH-Y zN+7~|@S8d8a!){g6Jf|N!)~S2PJm)w9TIqXu5g=h<34VEAQy}nrQEo)34*|8l-`3P zm7$2EbQgTbr=kcmRf2~C4FA6jp1!@$%<%IsHgJhRj|)^GA}kr@nlp5KaOx!DNKUtl@U%lj#6r_ z5{Ov5E9EDOXtVS20b4rCQIskkEf~Vwt8Q@k!;iYIvI^P{`e#-&cWv{fT>%^wsT`|R z*q9W7xVEu-DeRgjpvGL#x5j!`Ou5Gb8(3#(&t~GnOY37b=j`R|VsS{b%R7qb4f?tK zNg0<9OQE7{<%_P$+pp>pF4n@bpd*b9eH*#k8f;1oj9XuoqU-4aV{DD`&L@vV4fit^ z7%U>7ZOkw_#1pqI-guU3kfLb{fRP}t`%G%njI2})s%t`#q3!KiYf>_*9uX{{X?F>w zo*hp|tVCxT27EK7PE=R#qIB%VR-@Fy+WL{m9SO@6H%{Q;h@taOJQWt$Z-ll4+C|ky zn8#3aX+{_GY@k;-U4Uc}=4W2X%0;mDXo>Q$k(4icYcD3Aooi@)iOd#T8~N<_X9`?(xKo$@*8udwFJ z_FA^W_gA*oM?=&BJsR?0ffT`4{ILhYd#TE)gZC*yw&Ten$RyI|u`p+_E%l$NT<_#= z0PB_g8tZ|y>dv(8G4@|vrWw!qF#@c*b&;-tz(MnU#d|R*53n6H3%UIGO0dZf-Cl?KxQzs%JB8 zgTf^;J`-6C`95ZFiC?L4%JJ206ysHWVe((3pisBoSOlA^fdQex+4;YY%sLgB@c6_v zrlO^^OEnj!uJ@A1ZGL)b${Ns?84MF1$Zx@lV{)?ki@rfc0NENC{^iT7o%?m$@oyvX z1pnT`oXGPGnYBr^ucK6Xhk~bGL`eIPAQ|*v53uFd7cJI4C?;FJb=;2}Rh*d?zYE~$ zX4;h}nP&5ui{CDHDSUo}FF>+Q>Q1Me6DY7?(z+>{bn;)8DUQb&;T0Oq(K`)JReC;| zxKCU#3Fj;-z@2$(A(sTtzZE;-tM!ODXVW=6lfR!eK#(}`jo~DQcpF8Mv%IlQd||2A zx6!sdXDdA1w(Tn)-%DgYw-~?JTdvXVfN$K;$_GuZk0^%tjK+uMAOM1Hhm*RM5@dlt zhRi0UQIoPFv&39^s+@xvey8)ajBnZN=3X;S&%=-*{kS8bFFXJ5(?28HFBNpNLjh~) zH_%K<-RGIUF|~iqjbv#5bd`eH?WuNHwVtTWXuQW1jAF%%(K~#UEZI*7x~#PKp7aNw z{Rbu(i^QfQmE(BSY^~t=1xCDD2H$52whPWk%raNp$I@Cs;xUgEGmhtq{ zD^pK#5pKNIS`KmgG6Sf-m#M=1>Xr_VZ`XlEA({yAIwCM&A(n(b#($R-Pf8nB3GOK3 z;uaVVel}kvL*Qd`w0?nfER&)^2aFsT?-9G=ob?W#4iQM*veKbHfC|nxx zdcrh-I{|Y0MTTR`7OM&B&fb_~S!c@0o;@IC(!vpt$va&p0`LY^Hh$^V%OczQIRRzWE$nPH0pe#OoW0Cj{ zb0)%onJ1i!-m$#<-qR=b|IE%55*KsZAVL2dJ|e69wXnSi8{GG_PL!4phW9CGPvuXB zII^%-(`HgT^2dRDK4ySG#@H$hFCt|A+LX)7yQ-AFu54N_a2Vuv7Y!%nRlSdr!d@PZy305q z_oKX78sv9W7C`LTC$8Y^cJl6k8G(^sdUe?bP zb}|I`U41BBdm;nT5scV%gp7VomOvK1KD&FH#H2f>0X6RN0tphXa%;Z2BC|JOx-W8-;(fd&6P^JVYnKvet2`yatPtki{V4{om zpnLB?O`cDxjhui9klo6Gq9WE|RWFC}f2tjMxp)V$2=~+@9N6+^b})vly5I8OqVOLZ-b`@hlv2T?J-d`pEYwt_!bbEQ<*oP{;GWCaB~fD zNqW;L0~&CBlf9k;aLBkOPu3MBLx-6M6Oj3fszL!!yHpCi@pvb9{*Q*pFd$B!a;`MUk}Ozg`=gd<1t<1&m2Nw;f!ZQ$q$LUwx^w!5b!F3IJ)J<^UoRI+ z{e`X*Wa_Dhty0*Jtap($4)@24k_N;Gb?2_~=ei~vRw@Q66Q4g--mV-aN3ZQuGL{3g zb9yI-D+d({z1}__bBxU4d;Vb5G#+XWxF~W}b{}ruvI8oeeW@4u{>kxo&QE5b%D*p% z-Wt1LrEPjBo$6cw7l^xYY?7dLlUzvY?8N5Rs*e_B4<|NPm4In(&4U4CgGlHR5xRS- zy>p6c>z4H$mjS*K0%~jQ+?YM(AjCJQGeG81g5H2EV#zgEMWS|`3*tDZniYcFm_kmf zxszI&KV*h8m^_|*uW#a!WR&1ny5bvv_&b7P%XI_f`+7_8f^xN zKb}4ZzrAds2kQLMhxVE*h5l);=Vo`St6y{d%mSp*Hk)-K-h%hj?$MYg!&7&#^O# zgl5l5ZW7^(ryv^IF+w?``!tnB!}*T*_}IA5CKx zXoq0ZZ^ahsu=#i5efhT-v^GD6AAuo-UoT{15F0SEx~(Z6pJf7AqrKtcrRx2-Bc_=9 z2IqRN`E#L`k;BdJb&qXhaJyM9w>SAoA*96 zi{ZO8(%b$&hN9jL%Dbn-)9gBilU9r69}~h^R=6Z8(y>&n1K!CXD{xath>MD5JpBnB zOGSB^A#p4gaxt+qI^kklG91PL^CXp34}YbT_IhJ3ydH+hl_h5$;!XeFFe}0o>;lS? z!3FmP4y|r51JwrPMd+B0hRUH7)-Z6%N7e6kfc{tL|HhO76NZN#ZIZ0$4Ns ziVQ!MH4BGsUC>;1UUFm)zp53TxOfq`!m_$V=1ix3vU0-@1LFTMpJ{R?mUy8A?(e5# zq}nwuHERATfB6VpbQu4XeS8ern`$1DnpB}!^MUsUuZK@XHPEX-4E@y>Tckm&v7&Q= zp@qmg{g<$^+kK7KXXYPS6k8euo*hC!r$mO21Zh{yEjCx+bnPItP6y>wkY%VH%XlOv zF%ONGHAx@9;gUHybZVoDonEJ_$?TWEV^gpbt4eE302VJ1i{d>6*wkS$RT=dzIwEaD z-t|kU&5Ct8yg7r~XX^6fmokx3A$^nS$pr(pV^1IbxY<+ZZvrsxYsC9_+(+H?;`dU3 zoCFMxCimP6RMf{q}Ryzl1$Gn&6)_79n6nWOx9e_OByzadc|3I3_{-X^e0YTD?tgJ1E%mIFVC_hCZD%n+I8 z*D$9~TO8A5LQ#FZ83;3rSz&i`dmC_1)8(&TS&)HR#@q`#J_O?Vzkd4f;##sdBucM}gA1Xm^9? zO~7$WXf3lMJ>;(XHxgL4S7~f#64Z+vI>K`M*<7kVn~Tw@f57c?s6LHOx_@egZ6=Cg zXJn9{{BZpt`vw!xO=}?mz~K_-@cvy9f$GchYw%x=oUr#G;p zc5Yh5{_6t==+E49y(PpIJM@)X#^VC=-1&=-3+8td zT;CNk9ltnmcc)L5jmJGo#wSm#XralBB=*;X;s-oP-~^PSO#@dx za3TFB>6#KdOw>*VIM~>kmf$3;&b+Y)gg1-#U22E9ua9y7Pg(T&YT4CLxcg8xnZU;^ zmWx%we^I|o2*HMloeK+SJFIE%GVzA&!6Sfp^GCi=ELdl0yDW;m7@(TR$v@ps0r48S z!fdnFWVyLc+XyUujE28XXJmRK4Yu$3Kb3#!ZQjI?9+@`Lddt-5StI9w8=j z4{fBf8KLYLk;E9zDro!lm6{owN;XSp^rtFt#3VT3!e&hd#DV58bo%NHMg*f!gQ{(B z>!ti107;bJe!Oi&jNR1x7l|d4p-1cL46wP9Drv;2G6bke9muo^7)~DAZ+}+{KZBTN z8Wx)f4d9atw{K9KYF*4qXH5f$GK+78`j@u6%4%K zc-*jc?VKb;Omx6?%T#ZBo{t+Trb_@G`dXYSAEH>V0P())zg zx-SH*w~0*-F+R#oH#93Wve00?hb@}7PD2_#qr<)HSrs3ZDZt#cqF^PGbFYCsMcEg^=QM7h0}k^6{U} zU;+6zg+^^{T3Uu+v!1T`@ut^n&$wWhe8asA2E4At3y#~LDFdj8A=ZSUqY)KEljyc5 zHcmH&%YI#O^{hlgrVdf>-s2OGhY)fcmmWn~PFfGuDLHds(*%9w^dP_lb&xHGd5opR zfCi6(qNp8uPn;@s`XNz>*T!(}0N3fp9;g!0$;O09F=3`QW<^jRt@h<#pu~v`A&ER{tn2nR{jw;nZ-{WiH_zeuDN#KQr;-9<&!lw zE09%rM^GpII|3s>E+<7tgqEJlU3v3J0ogUNzrJG)O}R>C`nldlVvS}-ata<8M{|=| zs9GP2FwEehu!p#-xN1j(*HWpaTgtE(vh7=hz7LqyiX5X)kk@JA@*vdlZ`i#L8;K2O zpiBME?XPT4b2NCed}c^wy84&3>(}Qkt%Ck^=Er#nRZwE}Z+j|4%3q~6FZHAmDh`aMlh3kv}aCQmdSHPfn|6WhR)o+fy1bC;)0EZUZ>Z9 z7{GxOd}VhDG0!xUoCceB<{?MBAn|9A`S2c*GMB@mgZcMgl#Z1E+M z6tCx;%_{YYlw-`mJ*qG3+euNSucg}tuJ+aC=ry>(5#M;pm5@Sm_r>D0{Jrv^lXPmM zygwL#1C=mw-yT1G=L3e}#lEb(z$wI5RlvAP5Y67r^@&}qe~oSEcT zp2bzx?~Yh8B1C7i{3v0`2gW$@MzpLIb?c(9^3BMm(xo@Qy4mBl&G>*UhM5L#ANgPb z*KhBla3_ekVMk!jhX69dqkg*+dKyDs%}(L+F8E(ZlWNTyL<5ifU#9;AbQlL@UEcwB zOCg&=n0%TZEvg?H>oUF6Z47v24Ypa`g=vQ?oR1UtkWgmJfnNp-ZTxm8)dku2<@}ZB zDl=1G(;QP^5ZP5wGw-(MakM!s?8_liSL>FIT!d1EpOSm5&20w6p6QA&csvJiL<*B8 z;}}6V?2MJkZQqj^h1)os$bd}PAwN-^OsQ(3hyRbKw+?9X3;Tyr1Ox<>l$01Dsni%D z9itJD1_9|F(j9^{Y;-qB3;_X0N;A51gfKc4>H6*aJn!@Vd9#~yt~%#F@wu)K9qgxB zAP`wlN-^5TF9@935q8QH^O~Wa?HwE5D|E*DPvE2#7P06!&EY9PnFJZ-4xvI$rv3CT z8`8Wa%e)fw?B$f~XF}B-<}RTrJ^4@KgrE<qp% zQ};<56wSzp*Ds|ni@i2oW+;listWl-U(vVz_7qy*w~v)RPfxN@shDkV(#s5jd<9?{ zCf(hvV_Ai~z>t?(%oUC#3H3Yh--MvY51hz{_i2uG6o7c(rNsc^SC%pHx>EQdD;;mD z3IB0Iv1x=$sM`WUgq*df8dvk(Br0aIcf?cvPh2BGoG0@YsX@_b@qU_y`RcuS6wWvc zXlfSuR?uchmONQ`%CMm~RaXuuIch>4`1aZ(B`>?Lo1bW5=4+@D#4eetBLyurO8Lgi z4{5xC)7oKRE>X3wy3d*WZqJkPf8{2q+Gvz{>nF-EUCun_O10q_QuJ9G@-C5P80?|) zAyx4t%E~jYgpT~=ci%X<*5Z(K(Vj6?lL$68S%ieamM`vS^&oJ8jNs>n%jk`{5vQ5LV7y9ra1tbMj6tk>a1D*DrssXoFLK z{cn6Liem*Y6Ko%x!73}3vo$o!jO&P_STGe{D?9?&9Pjt}IV)z)qn%WL*D;g0Q`9ga zf*#m9ei)0Qh@PKJ z&&)R|zhe8Js5a##c4$HwM?|6H?cQ53T>AEZt8CFNYujj^} z5L;{yPdB8zk+hbPLFi(rJ-q~x*T?MbESpzF9zk=~GNrpQE4{l1Lp5e6=fsh}_oh}K zHaE0AHoj%Z$%TwW;4vfI2kX0gW}K?j5-_*yT6an4J%aW&?)32ZfNKoF6Z74pZ=bt| z!m~xVG-XIa7V4tE7!feF-2B$4Iy(5HKt?5dV^yVscFn{0WGIDY@v{md8%_`!RlfU) zk&oNd#9O(7b(|MsRt}cP*BJ+i*laJi*ZBOEImL_q`A-qJ#0Yd*{mIPEV1JS>HuXU~ zPLDLp4nqwD*%Qk~ zmP%2-H6#ms=3qlf)=V^mgFC(KsL%MQEgJa+J9z!Ps+*46Qsa^p`lP;(o4V~1vu~xL zst8o&%^xR)onAtJlO$Bap4Qj}H#6e^$`Vnjmr4N8Z#7bdQ-H=(x(jSya;>DR5YHc1 zdFI-^-3CN>pMFz2TyjC_1Wea5v{=XlFlcd#*nlR^*1NC>+qIzcQAZUg`mcqy-&jF3 z%7(~?hke}?+#&3bU$d4%L`m?~EG->roYd9C^i*;X7BU;XOE@H|5SvSzGneD(4z`A` zWnGpjWlM&0|8&Sm$5QEHdaoH(;003nR*kAW%)HfmHgA^aje72|AeX^Ed_sS;)+A+@ z#qD6P0>=~SL7^^;T3tyNBLurr#GiGH0%S>e(g8#=X;#eF7F8v$u6UzMMIzLzT@964 zmR;M$kRziQKynuT5d)H_{N3L(3aC51QVEfhd4J34&7;54Pl~zxBlVxh=&6K~Ni&}P zJO3YHxBW?y%7AGwLa+Ny2Ii%E_Y;h$;!mxvPIG!K0z%)LZnKaB`&fS^nl&p!__=2+ z9B0)&wc3HIsXQd|6=bFDr~q@E9L5i~d|uCujF}J&WbILJ<}9*p2Fu&Zf>S&cRM&R_ z)SG<(5&i>yb<12s$#p|R4h(}T?^8NEoDVsj&)UL*d{fY*rkFw?iSRFdM*ZpwYj@JIEnyf&Vs76cR|v2x#sG;Js;NGMp=vvp+wHp_`NVc=;? zp{~WYZ_n!fq5n?xsB|Use-6v+q}H3(RSAv$Z=xPdY)T2u^yk+Fufe8E5b%&!hRQ*99Q~+XkNM zcnGooBDd_D?z`gpdw@c;udDH#@XMRwd|aTDZ^n0h?%5?nh^rV4M4 zS2q?{FQ{iA_`mX6?7XOj0VQ^k=)XnzGA#3yJ@pU(!s%P_<%_n$&->YsJhbY!4^nce zcAu$#aFdOkaw#a*ISZhX6B5%GJjb@1KafTMs>TNs7KDg=`W8Py8D~{zFHAyYi_dze z5zeyZ1BkpYrd{P!h43*U{wn$zehD@p=MfiyI|z(-ii;lY%Mjt`(aj86)apNE{|$Xs z7ph-evXZH}SmgA)pv)vnK2H#YuV1`I2=phf!NN1gDQe<^w&-VFIGaQCT_@_cICAGJ zjDZVxN3@WYT!Y5jBSMNLLt`TMFH$d+6$;;LDZh;p)JpsL0k4i3G30nOp)!Z}_sJ_p zg#YcLkGR~w8!4lp+UmRIiiYLFy!@mxD}AQnmOtc=VW=1-IL?*EAH-qZL%-#+lhTEHo)TFRIa~5#*YE~Ft_)e(M|q5j3%(`(E*FM ztz0EUoCf}%aOFf33qC+TMchO|L>)cOAv@cquL_rA(^lT~r1)ujvBkyWOkwTIe{aCc z-JSgMgf!mRwFLqM);%tPe-4kdt)K zq#aoaZKXQyl20-_QJ5S!{V?p6`jSjaNS6wT%<$x8{`PS(gEAd%RqW~hS;*O^bM>w& zQ2ubwlusO;&0s=QpCXLX&vPESk`B71Dr1s!Bh4;JdtH2EDvS zK6+O&!`PseUovhN?OX2srlj2y0OIzN;?h#L?05k*0)EH_GGcvrX@1;0osJ&W6|X)r z2XSB&s%xY^!bwmJ2+4YknJNB@gUd;FekJul@~c=(>TOJT3I}w z+|!gUgBrglgUD-lRndRH=EvYCk8a)}+qz)&4ch#a+)CF?s%@MXKkUPxJWnlc*4_Xf z&bEe?-2Ukg(vVw|ne;S*z~?Z#88r zKabC6p#E9zfG;C#ON%LZ=Jk{|u~`H16}Lg)BozH-z1%H2c+8lw)AWaH+K>{&FsdJX z3h?iTMbf<6SOmH=Uwz5;jVssD+^Lm)SeIr*Z@m_G4r z|FV)H%$mFR<|>8(s&@k25sG&u0Wzn@h=UHIxCs6|4`wue2J&#&c49;{cbd4~2I+_v zax0CB@^4hA>2Y~icYd!Z2_;=EtkhfY;E8L2A_@KK$v8B>?65w50IM_J|T z9Wf!T{&|d$uw3vE(SIRQ#|k#JexcZh~;cRnujFX*OWrPf|W_bxcV7a)6!!^Y1^*+fi5V z?Ku;r=7bx>G+03_=j`&m*f=qnJc#@uzBzGHwyBX~;pg!;M7)#VFYb0KLR5j|zn)>gq)-3zJo4@7YKE0gf3h%?EFe6? znC%aH9i!}4iJY);rT16J`v#v(J&9AIR~c_Tr6IPcDC=6?ekbo}-LWkR=oD$s9!sBx?6i$LN}lcHdu?w<0Z!AjyuMoE>|j z@f85bH*ga=sRDHmNK0l$|2%M}^l?DNYPUB4*_1eR}hLd{?9d>6?|@lxh!d>`4_+WH5B4$Ujqt}KJzZmPi3)U$dI4K(b@xl&&!(25Z(Ky zZB|~Ml>Gu0+Dc0=FkoIKoM-QJ%90eitXU#Tth@wzRLz2Vht!e;JsZOX;()~uc_^#| zF&pw$qi9aoXAZx-a7RPsefuv$PCSt=*L+Iq+9a}WIJG3jNRM->lqI`7;J^`AO0bd z*}s>`5aU8~E6Wi{Srv)m>?SZNtGfPgP2%CXj}`hVR>6vq8sVtc z$WIaO+5D=A+WOT}m;UB-*2e$Y?1Kxa(`HyU?&o=J^*M*+y10M#)bPUIh9kFOe zxON`1F%|o5arW?Q$t_#BxlIas&B|W)0x&Y_SH;DW+i1@b_kF*Qs(JQeu>72L&$%Q} z)#|Tel*E5_$GoG*I2uK1>vvRA2OLcM4{z52>R1sI^XP!XxVNp*m zX11}oBO~Oh9FnY!`#t4ZHp`#KvSF!2ye4@*8eyVE5#5!PmY_K;dioHwZ-vdtmTB!N z-aH#{G+c~p4XAPGj<7QLq2OpRHoOny!^3}~K@7l?9;Ys0+-X^-<{Al2iaT{rlsZ96 zL7qR!QKwti*atX1lxmykc!ku(*7A?3VP@q5AxPpc7&cP+`4j2!R}@Q%jZ)qaM$pqx z#;2i_Dz5<iQXg^)xOeLJ@x6(gCC0q!RD59rHx+YAOn~#; z`~+NxJRP`WKt}EV{MWzg`4Y)@{{z!^e)X%d)jWi+9Q3Aw)m^Nd6%V?E`1qg9^h#Wl zkGo+;-}6%E2?8qOuocnuisZz#Dj3gu&itMuI|a#6|>#P-M# zv?q`Lf6^+xhtK=YhO%Ge{_o2z|NAma6c;u$jQv6n>|C+D-&|9%Y6_~O%E7R}RQ;1qpoV)-YK zA3-RNm}#b;#x&n#*$E9}v^BC*C~A~b^U^MTS{+@nWCU5j6Dw2ktKyRW48i2jTZyh1 zQ(NvqQ~tHTTp>t_`Rmx!1nKq1^M+KOVPQ0BzdOOj=$-GxiWqFzjCqeIN``uyiTxH? zpFVNGOG+gH9nvjpWkGn?`{}WL6E^Ju==IiUJQ_6)IK^(NG#Tw;^@apdqMw^A*ZgPH zA3h10uS*+@2$FYHBU>A`@OuapAsKHQO2n8Ev(>P^mnxwV00))uTy*$b3Z{?Md*zMH zGC&mwmHGgCv*FaM;`){c4m6FMQ{5p~=-4KCG1p~i?qTGlZjLsXOGABMTW z1hn

IwQjot4VzI^*sv6y47CKXxu^Da(refu)#hd?YEgWti}-L-dc`6& z(e4gs%hzF;UIIFHu=`_kS}vXL2Y$ZZ-LYFaKochpk_O|#Z4a?&4um|Q4A_D?L7N6C z|0j&oTpmPSTlX{sY~3{g&ivOvu=PeU7FZe;52DZud$r&5sJ9sAuj5ki;cd*!zYe!8lVF__-+OR*Uu zm>6d1vp1CYG8YANR(y=`-XB8|wm-UJY`*>r{BJzus|7GSYw;e_HU2VJ_@hsc^a&+v zjUlt2CCZ5b$49=Y{?p4ph~ronk49l8hdmb~jA3%mM97gCB3<;k&(o$scJ|0?769IV zZ5^W=m2&`ySdYO%HwbYr;Mkf|XT=UO#o!BI-`KJLCG009uu(8Pni7KEFG8;&i9U-Z zvo;ODmX2EP;)Z{%yCl;6HyGPXr$rD+h{^_94!z1Mu(-tnoTDV`+|}oW$r(UVfWfvd z3`C98RlXXl{GxKm+W7FVQUb(7b0Hge>8Ne;$2z6K7eH4X<=+ypEXw}1U7W{R(w!+* zUqujbl@GaJzL1A31D!gcHN}@Ht4xT?uq!@i^kRW_^d%cr5jM~Czt9{03) z|IjjhFZ%Z1oQ3qwQ)KJ$@+`;f?3HAk?nMrqxliWwK&RX~CO>nDwvmUW7F|cPvu`~E z`)c*SR3R2etbAD*?EA^e=*%#>u>6qA8~Fv%Kjd+=ltzz4K<-C+`gJm*4l!x>?N~+o zwA^iG8tMaAvin<&t-#zkG!AA?)vi_Le#yRlHMDLFi|?tPWJFlRYj4TvEkPlHrAFCp zg}x%*OBdY%gp9NC@qw5 z^R|Ug3Q-F)l7eV!>rEXx3V%F=p?~NLtH}(X*7QCW4P(7%zm;{CbZdRHTWR_-N00-x zD!ME(ndPxJzEY`NMxZn+ya>lBJRIK+dMjrk3x&l73W{DVRn{FBZ#t)j1DEuFd#i)E zvnWg`3IIhFWG1$m2F!}SDH71^^o5p=uWO^HXefKzqwUpFkaKu-*E@}m)wgM0SvZ1YawrE$-5(`&v^1l)`;04*~L%5ntTK8i!BSa~C%)5K5V%XHM z9K@(iGJiWq#=e^`wi_S+uDGX{8L6rBV)o^nWdq{qy_X0_tersFR$GC}^MCjVg>*&Y zY+81`G?`>wnDZ!uR7~|^sln530dt_A8Siz{RVsawan4y`WtC$f6^Ib99Y|zD&8f!) zR}8U_1h8+<-r#Gfj~%0bU7UUeVcl)R-}pj0nj?mkV@8%KI@x$&gQPccyh~ zMZ5cXB@p`-$uRd!dTzuj3!)jg-d~~Km8wmhSYZ_SwN6k6b8qRrTlQCo?3@h@&oBCV zC63G($78cUZ7mhp)5;Qx|74l!nhqJ?#xG)k(lrRpeZZ;qu5mE8t#mpW79KE8b3Pk4 z_86n{Ngp&!6TQ9t_c1Q=yRh81K`F@57%W^LNAJwGsZbIOS!V$NJMeCU*~`-O*(?7Y zw`Ze|ECJgd8BZhM1cVZx%m0T3psO6A>U;Wb2=&rnVCZx=*9D<$D+aQ;1~nU&^NV|s8~k>( z=n}qZ`l0sM!heIzKwvlIS=stG{OvHxeKj%pTSeAE!)B>NP_9P$U@2W_n-2sMNb++9 zHnloF8m(}KYmwW7v$KN|_2*`{u1a17n3B{e8uf84d(Z7GYW}x;iee;ciLzEw2 zs2IDtPxz~nx{Z*BBkBi}wgv4>iuQF08-uEl#P_!z7LFUsQGO2QjkCmGe{YQ0&t>=?Q(^Hqfgpp=@1?-9vFrHs$ z8dhYu6rF)Z;RZxro0$>M7sAw>$I3N117E)R!YEiwmgE22@Wi#B_D!ASL_=Ng^@OFOrd(GmPR7Nw~V07T5W?w*k}PC zSAl%r=4BvpTP}dL>fZ_z+119f*osubfLqEBv&5 zj6MR2HsyIMrZ=xO-HOxc3VK|!*cRpcke+{}KGy=NC7*9^>2+icr~xf``y`k_8MhUa z+IsRdj{o?zKX(9t10{v*Mzi}G0ro0zN0-}men!MXQx^iD$R%ayuZ{9H)V2yrH#TbX z8eD9TRj_Cw>Ulr%T5>GP7OZ~4E9l@SZe#<0IileKpgL^GG5lBY>>Zv*)b8TfHBK6( zPX@;7cTdR`G%S;t%Gj`kv#cTwOx2+73{6C4Y|g9&wy_?<>VkS0*DDCU%<8}25WFS zj&JJ!2_gvfRw z5mbgJ$hstDk6O2K#ltzNevup2SM0nwYF#R5*WwLd;NS&%K_3VDiTlcDw zs;?5^5*5o>6CXs@^NEc{NE-Xmphe)BZlC&d;}kbXT@9v@4}d0a0Z&_Ow<` z+H^?s(A^(~0U|K3`k$Mx8g0@bvVhDWo^u35rbp=BJE1ofMF=Ge8>*ht^I`tPOZg*9 z!UtlBeZoG~*Ctv5x@7S2%_Yf>lt`sqg3?bynK?Q4Q=&3Qv`Z3&LY%7a1LzXVEx_rK^B zv)s?==A>W9UH$|+4{skDKb|K45t4diKg=0Y>vZzrJRWU3J^hh{7nX>FVnsGk-@(Ni z-p_QIDyrQhg7Edi>hzW1a#50`t&#ye#?)6?_7L|V|1WI)?!_=Cw^0wb823tTXp1fQ z$nm~hn)qmLUqXK@VPUdTD9>Hq0~@(JvTyczCUix@2udq*oFtk#k9N=}5=yLnc$h`a zl+3(au)oBhBlinaIx3Ixzoef7>DuN&Q0+TQi%H?T^F!(ohHq z{~NJ*PAG`CB{7{;5%eeVI9x%0=*dTs3Jz(AiCR`g*&C)k&T{I!L^}NUwNJF0*|rIG z8*(o3v-<;!9e@wS$~C{NWo%lk#s(99G_DOBk2~kiCG5 zpDX4c8~3FwLJXoB2kWhSQqjCj$lp*@bGq>(h6Si`DBAkm!CvwB#7{j)&jsY=aULA* z*gSms>lFvm?qug%wMRT{SG2p~w|YQX)^9`D*0gw2eit%>ln#S9vb9ol7l`wSI%if0or*Kqz^bt|K67~;tst9VL zESMPjy0a)11~+(N_%4}4i}^T{7rQ@Mi0W}W37cF{Otb%&wc|2m?F^*%Q6}nL^9Ul? zg%uIQ^8Ibh?XxpiuM3(c=C)fQnnh7o5MPf3oECt1jm$|?im#^1=U2LRv4h0a7z;B0 zrj{%nqdWdp0>5Dv2Y?_1O6oq>c8)kdvROe2qy_me8YbE~C)0WH1i1NAOwQbE3T1v4 zWRWAmzT7HMW3P1{(-$MbFqbDouVuM0PCu~tslan(22zl|CIJ9-`LTey!in<)?QQ(JS`(#)}1 z&0rftueNNVOPokGu=7con3%Qurn@BNF=OtcZ%!he<2lqw8uI2E`cVShd7Tw3sSM%1 zz&kyJW-kkB?G6fRK$robC7yhzHPLPd3Z!q4larLLgGlraovnj%?Q>{l+HS zFf^qa9)^Rd*3{;quZVE+J^{=Tt5w+AZTs)53ei*Piq#7{9rq$ep5aYL(l9<-Vb_fK zo;v^X>=i);B;mA{rPkB#lP;s>1z@iEfflNL4tP#k#UKyS2VCf^d5fv>TF$fxf%Mzg z7~F-PXh71sm5u_5wt+v<@TrMMmebuN>?bqrc zoR@{uNNeo60h+z^;tA6sH4^c=FcA!C&9VFNn-u+#xy;DW8a%o!HgZ|u13hK@^{y3N6pw3%a|)UbfVT@zk=x&^kHXz2$sw6Urv|>I zhX1;CX9aoT24t``J+FgnkN&!rW3bV~Zd})KJ<230UC_0v4ti~9*B}wNz^#^GsW#U! z{WvGbWVgQZ&gg|0nyF3rOP59ekL`+H$rSW!?uhU-&dD$rdK;D%Cc z!_T!%zu$!1bVwog)xh|e=9?d$K;&7IldDCCI>aZ_c-HK~9-`6DL(lB!$b>M8MpuQF zz9nMlyp4E^H2F-`r-A|8*Dj7C#>4$Y}R z{J%e%fho=~BPD8JMxu75xUh4Ln_JH6fQPVO#(cS5h?d=i`8`2#Rck%9YjO-sdC?Xi zG8&%xB0Q^jPieGN-lHkPsZpsWuBD$s=-9C&T)HrP<9U-@)P-a~c{X)-!D}yWGs3sm zv>p#-;>?KqrbsVMLq!`u@aQFnS{m3ABQ?sQ2w>Vx?-lYK=ko=tamU{Y3wAfMV4JyY1az zsgO08ce#ZC(yMOHPk^m@XOQ{S;r&?TRpQ-QXHxcOhI6vU(6xF`W@gZV*hhvR;cv^P za4%Bq$IsjG!J{pS1BS=X)G6|evSulK)g3u|5w8w3mEb*1xbZ)is*$^^t(7U&sw`Su zEfss$Wb$$lll-FN5xBDDt+%Nfk6AfVqGX9hiP$p<#VQTylRnzssxOw!$##%Q|D;C- zh*aqGGJ5%WnGqQ$2r?r9imsKmWY)|^nV-3x;%B0EGYrl!X%vQJ+Vs{GXHj*E986c- zNRT9m6oftHOWW?MW!^|hG#~7hLMv7rpmg&5J1+qDgixXMEy^v?rR(d6V>87+cp|c)}4Zn@w7*!=2M;)hZR%AcndX+@iq=C~+59;kW`HBYQ zQpYnPW`pU1d`CDlQK6sQpCD^D!0KU!0*R>c78)o@;=n`=maTWm76EZki0K7v@Bh|s zJuYpbNmcyIe+~llht%!@nnB~4Ol{0a^Ciw1LyCDZ%g-z6?1<@DJawSD%@eU_UkUg7 z9fIaYk=@x{e`=y{4ytUp2%Sj})qeQ6Ot<)wdNwTCnB`a4%|CIde4bb?ER^`nyQuS-%k;OLyX9_uT2QM;-IrN)zg`)7%EBBU zRX-7Vn-t>SK8DT%{_=r>oTtP~q!jIvrXUel!FSGk9}V^FoeHN;nV)Wxw&>mHzF{y6 z&}TxBu2OQcbZ|cNx~c#c?#2=e3X9yXc~kXTKpmK$9e5@hZ>Tc{hD<%5L~tA8sv=3?Bpe-z8_PtI#frQlp?2EVrwT2RZXOG*e&@WR0kfoSHA)4PtDkZ4n*Z!}_)n*BA+_wm&Vg`-5?h1K+wE)Rn0-RkT=| zDP@AtH(5aEX&-Q`J4TG zaQv4A-5969ayJt$vx)KfdO1`J%?jvn0N%JVLL1F?v6PA8QX&=tx{`hP(@ccjU?=DI z)6RrStlB{pBI&t<{jQ%q6Xhg;?EK#QT*ZTL0=<%oo@bL$V;HB-Yfyr)DVcmzrxPBK z(kUivcU&Q`=#bWwlaC{>zKZcob-^x5DCmjM+Eo8H-KL|@GrGoP`dsyGZXllZeIcGq z`oksZ0iq$}qO51JNUH2rh^YWo_&9t%A^EbszH9Gqw4CQmBAuuB$NI2;07Z@xTbf$} zD4A>?E>|Xs-5#Q3e|sPYGGUCntF-1$3rwmd#XYc*7)wNDtRC-Zc!{d*p+AIVn{nUr zUfSt+bWArlo5B314mcf`;ZM3L!OlZ%+%%yd3J<=LvMP&&IC%I9C7DPdKt#CXbQwYo zjLBjDPE$(SicasUm+sJ^SH8zm5P6v2BjlOJiS1we@0|68e&4I3L=oN_!eGc-B+jCd z4oBMj)T=y`oTrfaz7Xe+8ckeoa`nu}6b`Mzp3yarwp_GzT_xr?`CHi9o%<}c2s1)z znv52+kY#Ot#`ZcBg*m3g#hZ~?zHAY z{;p~hjs!=uHGO^^Xry)o5sI}=^)c=&777Zbk~nJSdz_c$hAaG`%rf4H2Z_KjS9N@} z3oz76c(Um=46hUHt7bufEhM``(bxcx?U(n~p}J8I^qcR^){Da6Y>0S;1>HhABtB4G z_r`;zE`$lRL>ymZ`&sWw9ZFX8ZJ@-o-Z?KuhD0H&sJKPt?}b= zXJ}>Lqv1ahnRLTk(OgQ-e<#}Ip<(J1)5+PS4_pMul*NNrK(dNnB<{TB7Gclu zcs4OCsPNqB@u$75#P#Ofb!KS2uW2eHFhZR}fZ=@DK^>9{L@JsabuKX>vEBsctw#Q)v-!+8SyqXwSp9rY zN!_rY1pu0n&0z&e8!G_A(BnX;OoWW4An5#=Ov2B)6EjwXznXI!U^|swDZbY)m)%G& z0oBn_faI}Un`A;nqvw4Lt$%JQ{$pnay?Lpq8}btq7_HbyP&^@+n5@`I!$HY_LW{>W z`_Q79Zv0D+koJ!|v$uSho-4i8uy4S}dvWyn9(nWzF!$oPTg{Sk;#cYzrx$ASP!PnCU=O)H16;UMAgvrA37CrB zDv=xAcS~%DDc~i=#LP&{>hv#|60dj<#+0)^DfPd)+1lD3%R;`*zPGTsijZoXRkLOU z!IPB-Wg6nn2Grs6%~PR&DKm``S0nC12$d;x#MGUiG3TH}ZZUE**jNMNm`e-<40i6- z_wy0fw9CL$SU@7GkWTF!Q;N$e0BFcfr|vNkMjdSMk%fa(DY$;sJK8A>H3QIV)rWBn z6=fP5(om`1tGL!-ENQHE;Hg-BhWJz+(ldpE0GW32PgnAc8w_9}G91 zemHb_vwF2WYOloi72dTq<&J~oX>MFmgYFNnb94KKL`0mQ0rawP;J6!^!f{C-P>MdZ zP)nm#ZcM6ZJf9YH=%<%!G@1be)Ypa$!O*wo*XLs{FISzLd*P&;ruj%#UK5Gd^#c7y zftECYO@%3(dsIeNR(6P@o`2;jt+dr}Ai~qKOs8yYQRJ&Y<%L%VUQGL_a9{CJJ9;``2Q^Z1(Kd*3svRs#GF~q-bSCbV(^plS zy9I#N`zUQN1}XS*x~Pd=tVV^KdK<6ZgXlbU@aPr%p;C!iXatfx>=^IaNRU%e0| z4c(|DeaZy`JMZ4;XJU_FfL`Duw>XY8)&ExNaKM}cBxV)MQ!_kePKWvGZf9@Qa-_(N z1~T!G3Z;7~O^jWM2^p|U0NKu4L_;e9+jBLDQ;KMTV-kw*LWDAz)Z%*K6Zs^%l9$5! z$_S9Q&PxJC(L9fZ3x7dx5d(FBNM_It?X4I&!g!gbbS`HW4Uu-n>?Zq|zmIU%ZexpR z1Ul#DBOi&K=qR3i-nsqIinrWS@2$5HU zO!PE~)JXij|6R^@A)*L*Zkg0p2JNO%w30|3V+5Tl$-km+e=z?3Q}vnr^;E;d%sU0> zzVR0|LBSt<08rfyTQk0Sk}2VJdN+3$m%%*o*{T#I=Hmu{>dptyU(`+`=xQPZ>Fv`C z(}B{O@T%y^@Ppw`F;xhZZzh*4@22|4N-~Xw2h_m4z6lpDA3;6_*nI*$qc-w-3%7&8 zthZ?YKh?p1Hm5S~&l?_So}{Hp`sar1OT}TrGzFl?l`9RYivDNFQJj5el*w_i3(z|e zIU?5pAbgk^_z;a}MRb^NVM!fx4tluvPtIm4I~bl0&1Na>r%W9wIQ(;I!0>xp88h6N zkex?u#`gsNf4Eu=VT23`I$A`7f|gjcHBeVsaJ=~^3Zla-u(Lz=Gqq9GL?Smf&eNbP zdVro|Fp3Q99K(qDmN4@QvnA4<^bEBGqoY}ohpr2xo9CV)-;kSO*-JLMq>E`l?ey&w zKvRiTy==>$E6LJl3z!12t#w5BW(OAYe>FDZPvIYZWxG!460^HTT z%R^hJrr_A(4Ju%Yhjm7jEn~EDv?hpu4czF+^M~Fbm+rGM-UCB0+w|8QX}2FGnVJbA zrJS)h^^@8;Qo;5T z;fPKVvsPu}pc}XRKw4xqDd8CQ4{QM57(3$3{X6p)w0^9c!I7Q{f;y;pf zfDMsX1fbfa76JIR)v`6Y^EcK~sfa&EsY3lo5~ohz$${hb>U~TXqk`0gvG~E44xD*i z+J2E?Rv*TXK6Z&hezs9Unl{qIGZvgEKFurg4i}`qRV+M9uW{& z@8`M|1&3IQd{6M%pSCMiOsu4~iu@gpKG?CCc02@+s^jEPPkPESo(op-=c!bl?S}b5 zH_SiJSkive?^rJ=5DeJsz(J014#Z7Ox`W|Z_l{7bHRglEzS7lnD?*5(5Q2zb#qlB$ zH^4^eXqyAsOIu3sE8{(Rd@{{IQW>M}Gnt|9U-koM5>#2ZY6can9>6SqgvpEUB6vo2 z?L%nt&`P|GD?!| z(Wsi-*&7Aurk=c&6fx?05@ZoE6!ccGCEsTb$Mbe4sK%SuW~|>RE+d%t_piWH8`Amq z^#NyAWbIVJjDr$zKc^ z)l*f1b5yZm$&LLr?v#SCo%%8&?)k3iEVW%)6lNmRjk9Mg3tQ{ErEZkf;4gHGbo55R z&eQ0F_c$f!hD>9@=0S1fx7bd$p13nk(?B^Q>}mAM2dFI6FBgFK&8YqK8EfxVe#AN6 zndT59hnHsXi^t`Vq(3)X6G%jB!epVc;3Hp$nw^6+durR~ z_B=jBo5Zot*Hq|~I*@?H_du2nnW@igIL?zhWu6B9byzR|VHJ{?3-5v96``3ds%Nub zS0;R*Q=>MFq$MoKY!h!RAD zHrXo?%~m&$53Mvl$^?DTJo?o!+T?GD`Y5|dNONZ^kY5%MN7yA?*qGb(#N}(Et_DQ* z#WXTuI+lk^9NCHGW3Pku@5K#pTATcgB6_ru?C;ake28r@K5|P#vqZVhR zo!e>&3r+zDY)%@pD!$LplNOvCKGjqVi$;_-aIGI$;8Y^tQe7s&vYUQ!g-d*js{ty>&o@1TL9v2g%02ScTiyIwL3w>>8 z*!H5@`@I&(7jp|`#25)h={aEB=JJompB)@;mV6|{gkkauNf|~FKDl%D26bTxx&0=LIY`NQ_GBvK=7;AoLN&99mFWwIl8U zCZvRXVv8aVVvm^)AI!_`_0h$^W-j&!vkLuCVQZQEJ-7d>C-opS+a$F=9fZL`pu#mH zm0rX&W1wUQjUfP=dlX_A;VTcTH!3&f!dgj3iA$nXe z%=Hpn|88G#9U zi%N&K2)1Qjx1?!|z1drWnUE&Ja^1OBz(n-kkg{F)f4!}p2_xS&{Dq1#A-XP1s_mw} zJuHY(vC4Z0#d@l9rhyp?&RZW^a#6w0AK}Xo4mWuoW;#>I(|Y__wEzK!1uf9zDYwE^P-O2N`V(euqD14If)2gCigk-J$p~q%)uj7Vzvt> zm|w@pnJnbs`;*^*mdrS@lc~kY+fWc}<40_efsK7VTU!5swHiE0g>BpKP?gp}pGMdz z@vAn?Izy=sZ?v z{`6dYVqeg*qJ_mrI-;Z<$@LGsHGMj0=90gR{R6V4@$#s_ho%HD_eL~(Xr1j>A?ocy zUCFpmN*9h+wF71XKjFWgb)!>_HxkI@kcV+$)XZYugH~Fda085* zT&mM#TpC&?g6L&8e|5Pvds>Xk#aPM#chWJfx;lKeGK8rX*{A4w4iz*tb06A5nLvGk zJjJBVG@$#N(V!)E&5D$3Wa@WYx2HY-$szfpj=kDe&33=8a0pl?=C={3yP1q-W?eM2 zTT%qga{cxEpE-u|)%!`Sa3!+T$|L2>X^2U@uFOYB8?n0car61N<8B~BX?=w?SU;~_ zUK!)qU$W(D@Cs%^L&P1#AZKN!RkQ-d_l!ka?nqb*Vckenb{1k*s-=sMg|!BnW)qSx zut*V54Wu+OIc(pWQQbUPsG$Jt(KudeCuM|9`q2rL$6;e(od7tEG|UK>?O}zc&DI^N zx`(;wr9L;(Wn>Kn-5#$r=*%|}Ct+%!;y+<2XRv*S0Jcu52|T!C>9-$qhRY$_yy?WS z7PS&%y$CNHaSY?@FBbELf+~xr$moobddI+jurKtm!3hDC{Cv$OaLuG$w*D6yEN=`a zZg9VOrFQ0K*SGiWStSqlp{EP_r&^jk-%uGmRd32$(&_SMEeO1trL|sRXl>U*689ps zgbzupjjJ3+<`(aGNMuo)(-xqY(Jq(PhL%JU^s-uNe!|lxJT5canDjC<4ywh zJ^mj{XB`k#^Yw8Q1f`|BLmKH;q`SL88Wvc(m6V2Engyi0b7@JJl(C9~g|1vOT#aCQE)^XFhAH`A|WU@V)il7OY6A^Ix4$KOs8d z`bIg>+Hld&aMiVq2dPyw|7TSk>s=jQFC5?qv;w9;#4XQNDB^{Sv@Pfq8yg~C$zgA$TF)LAZKim~iJm_|xJ^>pXuk1&p z(8CJP`U{3sd*A>|V4K=HSV{@LWd2!4SmVcvfa=bYovoOsB|!*5M^#dxv!^{!Ww}}U zaIUXO*O4i0VCuHZGv5w&=Q`zR!y(A1n5PDzEuxRQU{| z+4~10`}*E)7{DuO;ZYYHo7(oWo9_GU=+nMwe9vY=F9iY5v-l!8Qv**V)5jIJo#miWmi9 z7aX1Sv-72z9@aFUZ_f*yW}eD{Mp?Z;cn=p%`ii(RXBD(OA?_z5*uO#z+{D<+RpezClR1;aGW8O>^N5{c>0pZx1~{nckyOwuI@9iVR2T1Xvn}TkXdC$ybcCm zxdH42c!#gcq>{0dDMb>|NIvbT1RO<ab;b2^F5_SrY$BIdkzo&N)d{NzS$ z!D^$C0F=_SmC9N^UCJ2yJ=7?j;x01Lawr?GBNLJj@)}QWtfzF(Nws#%g4Ged3^j-& zJyQYT?UAW)l0nS3{bQ%|JOBkSjk}pRpo?<8X9qn87QKlke~Z$otxX6FO8H)-c`lWn zBv8GbQknA!8H}(go8+yBcj^hcsBWi=o@VKdj<=D?>6d?F#i7iZ4AX#N>%~vPHSt3T zU9>XebQ_rDc=UCdiBCsuToO44$N$g+_@L=TI$i|alqPjCkJJIx7uy;u$oI>v-Jv0` zGmjVvKRDn0*AfQ)mf7rFf3(nAykWbIHTWTe=`3eQy--?0K>A;6<7qI@$Q;~`bO>by zo&Z6~*en7ySUs50Av-1Uarab0`u-0gxe8#)n(WkQXcg;;0EQW??36-y5q-*hBY>HW z^+eSRM})ijba8(B4&~y^2dYg~FKUtn)oRy7pVe1<+#;%r{ic%WBi z8FS|%NI(a-aeSRM;nyW8v6y-Q0b!-cH_TAjP4q4`z%GVzJV!eCtk5M`{!UPo`W3>T z1Si{d2U(5uyl=YecbZXyezD^0v^=J7JkW68Wiid;+A3?Ti`bLZSUcMTu~@#*eg1Re z4xR>uVHgUqgw@lfgWKoU^}DCWgtA1D&;=b~>~2P60B5H8{!V-;M>h%mu^<5O^nT$m zMae;BoVuLyCLHOAi5O*n7Vcyhqh2PQk0ApofZZ!Qpl4tF@;)(T&2_HgfzD+&U*oH$ zS^)#~oO#6tss)85-b4AmzWQgr`LCyjW93dJfd&ztM^aaWBMr=ACPi{?G@^R(e^kKk zW@}8D#=l_)#sJ|-xT$fe5)*S7rAqZhH=6Gux}!sB_k-_$C7z;LldCAOd|C zpad*6-#o4<8{FZe@iz>wrwiDAY)uAZ^Xi@N9Pkz)DXjT zxAWv}Z^}+vyz!33V81dI*;LbGl|Yv9!(1w`NSR#_3T@24|KPuu4-vwYn)DM>6}!0l z+wH}1|5lVM1ewp;)4RCAnHB~8WK*#Xk4k==UY&z%8yyB6TN5rFgvtXqL(gxpbmb}( zIH~g?HE(sSsKAedS*-oGBT3pyzyZ53<)uT0bq$k&hmN3;XX4}U9c0`Ul!apCrYzU*jh#UR_xw|wW%Qot9pGy;GfSdo}8 zspy%Lr(E=1%k5h4m3Aca5XNrzKlaWwo$#X0fN@;5m(2ZAXzAPe*)DIYLG3Y|kbB!_ zzHBBaCLbqm;vpmS{SC~5*GPw?i78U%w>Stx@VK72gslBc492gcZR+beV$*uX5UG;t zS4~|Le;dV!T&-n?btk z0uxEbRxl2S_7T!=l%Q$-L2F++!i*m?*}fXulJQrp%)uEdg7_+&l-d;B&( zAP-wKxsz=5BY#;D>Q+8R(W>8JTfz(-6K2|4s_3s;Qx-NZ7h%Cs8bG(hK3`)#x&L%( zh5Yn@Kx7rJ6C>$Q1YOJyYfno7ckLEJM$%j)%(951x%?ihM5mvx_+TdmOY|T|yXoEc zGXeWgQD|)5pWVb}2aB73`T!JwfYUffG;$vq|5J8YnN9zTi_Xp%mkW4nKpNwG-a=)n zJm<6B%MVy?Vc(H_gH*~<;g;WPh*BPjNY{#7>bv6kDeFK{w~`b-hGD&j+3$YyBK{9_ z=}#q>|3@;L_#1xk=-+badL@nql1JC58X8vhV3(mKkn z>8zV#ZaIU|3i9Qw0Do$yV}X>a4IE)QImW_P1KVXUno>yLXbjTz76mvF5^Qu1$ef_n zKGaJH-^ZU|uRe3#S}y&Rz^2OZrLv5@r`}-^1mev8eIR}PQ!jxeHgOYlmVJcGs$c66 zXOeJN{<%YCv)`CMr`?V@JGo#z_n(*AatQN{4i+igU4Q9y4GT668r617b&+=AcDz34 z0gX)3pl(8cy-lt}nU0FN48XUkqFVFIM}_I z3RL3^w9GQbKZo=68H9%v4>XDr-`7{-1A9$rb-Z^y98dgg82eve))2 z-+QUBBHD%eeG4#riB`j+$0D?e2+-tiQKL3ajAE2Xfx%40RtZhvq=2L-9u^SbDd$yx zCf+f)xJIqb4HbOZ|Lz|k>j4;7@2>XL`iXmu@Gn!mxk^BXb>Q_fUs@hE5gkgc)$M&$ zc|Y$kBxO_X;KvWqm1S=wKWO0V+r-$(PB+o1$muKy>QZ7DlDHT6@(=%2 zXx0F414-x>RAgVh*|K9K4mJ}w|H@Rt{$WelXEcXF_yoUffFH}sYck;UNe~N0$biVn zI7uOeGah#21FlX-dhl>PZ)JA~R`Q&+q~9IM zrV&1Y^vZAbME#~U4mpv}_KU>{(hTWT{gZ#eOH*MaB$gnEW2;@&QeT5H;M1)DYCCFep zPy3v}Clw;XXTBv4fHBh4Et!Oc-kWZxmmyf+-ua}&60p0?M0oT-BrpDjSBCbsj<`8s zYDoG&(pxU^8TPsfm+ih%u$5e0|8D1EkF5`YBwKHeXH9d}7oa5zzdBjPB-DpJH@>e- zS8$?vi5J#m?@p5ADq+N?D}uQ$dIWp*e8crXys$9c-4{%t@Ch+4)M-b}@LyjY*Ftdu zx8KMqQEk0VFo5(TqA1bBHhh1J(NcwBvf%TnUUAiMDM zzFb5f=|i29(Q*!jdo&z%onD4SS4d#j$N22heJN-CDVXDSxp~gc+Wt#Ip4M%&xMHv` znMu68vi^ZM5po~Lr1T$KPJ(&b1$Ln06BRr;RU=D1j2iR-mNMGpwK!|)$xmt}aAIZ7 zvPv(B54NAG2^<~;@r)0LdV&v?=fS`9Yq^kCSe~{;9kx2EYrZYp&WiKCsudz zdBbj(He>M7T$<-*O&&PylD7LlL|x4Thp_Q#A639k@}LybA9I28ECo*F1c$fQwp)27 z!6~AXm-(FXCsjj=ZXP}5;W)*e;-a=f|O|df(j+ez#8H2jp9jut2e*2Q- zZ%ygs8bb=2UZ}n&Zsl9L7w0__fqgAFI?z%5H_d8N4qM>0ohnsbQaO8w zUtn`#VyOctj|S|bDc@NA{+rChq!VYJz>aDHb>=H9=O4+ELKM?6gf~Tu&eeC9ASV#7 z>Vl=4?#l+g=JG6qSex5CT4CF13TM`tVuNU4j)2yO<5X+q7%wp0^nMUM&kGxm7%!{n>n z9QN(iFy#%5#UR6x1IFm;K6*6hs4T~`%uv#UKXWl&0@F9iEkVfV1ju63qnr>ara=GS zsK512xLe4L1M1qn?G-l}$^ChJaI9#vXIb*W*91|`#^vR?q7iGn1EI=rbhDGJiFc9b z$QRTI%=qO7iZ|kTeAKPi4(3<>R*nPb&o=~)M=-Y(*0$!H@G2KF!S1c>H?}q7mMgR{ z=s7NkUNigV;*YtaTI0>{U{JV{S0xCMqAV zFydl=0QwIwsk5coHMAsuBrq}aUgL5+|Ku)_qT&w=62#Y}^O{rE|D5E_l&+qZXtql! z?o~v%yMvx8BB4<;5k?njOsCJ1vqGAD(iY6X!1IpKU!zcY-CS&UKV zpIDPGvbyjW)2O>*{Bh7s9_; zur#051OG$8nw4spA{REjDDhKJrJmqx&9XsGD9qabO&OVmGOPFB+S;=>+N4{jZf$m! z9Fv0l`1gCh;YaZ=_-|*-XoQK@s_hKB761973x2%l@8=_z%FeU(#E0)cPIKab(!k&l zIA(`}y57UV0CWe=ol*CbzoJ_VXFN9a&fdbq6*si|^zZ z?wI8Py(L8LdKD`)K7k@qsRELzbwdlV)S9lZS9ufx=`OELg`#C8{!R6x{^~eUX(#eI zv0YQoT0=qSWcrW?3LC)UrqZ)|I%~LHrx)X>K$l2mw=kuJdLZj@vTFZB|VBwOyF$!>IPc2!=k|3sTTAjMDfG`Im)w~;vEKu0JT71VQ*&wlfVJ6l8f3`o%yceSG@6= z=40=5(bTFB$An%Wum?2zU;?~K3~tzp9dW$=3TQ&b z-tcK!tEPcwB|?tT3&e-B=MAeaZ5iXJ0r~-@#dR8tKzyXL1 zD-z$uS67J`JAqi-k`8!&_wsb5nz09R*57IeuXAa2u=8ygY$Mo1n#C|IH3EH1C$NYf;v5 zBk`R#^s5O?X;z}~>e)+HFGIq{xT+ zy4=diAlkri_|{OXPe6Q}2nyl;gj&r9Tgt{7OUp`sl8#%ONc}5QDNu#oXn;y!j2IMw zJyDc}Shys`Rt=#WhC4N)#!s%qFy8lo+c97;NpFfZhy$G9JMC)kVm76fdj{xV#!jjfhxbjtqH3%FwCSA+Eq6*2@6zkzZV-Y^rb=>&kM@+u(3WR8#-!_5t z9(Ei{Td7)`$y`u_TC|Ajvo@u4?@4wU8HX1Y?kzRa;+!r z65DogHTWeGzrCou9YHFQz)3awu~n`~Y9*yG`wi|RblA1edi|z;i^7pj@7!Dw+gYH3 zTlE4@61c7aG;!ZJ4W@K>^c#-D znjki{iYbYZ>NJ6k-%Wern4UhNGbe8#ZLcP(ur0X{% ze9pChqOC&xiE4hGqr{}_F*K}4XW&lVk9gNMfjv}#@BgOue;vVQxI5snU-5zhU*R+} z07e9*8g6b-z4G9jbRhrn1(v?wxQ=f|_}N&S_}`4DpE1vi-D2(ZUNwv9qxYo7Gey-}PKH5YaJab2g&p&sI;TJ3xnltDF7Z z?4NvkVe*`Bw_$NcLS)m1&h5l-Px%3z7rV(APJ5ZlPVSot@&J^OMpI^CTbqZoN74Hz zzvUJ}%`w#|^-E)KK}~JamPM}kT$wy(bWBh1hQpWQ`^&fbs+5e7))Lh(p95OwF6THK zqy%d-TSM(%BJb%q7(njed`7MMMSr>30>c(`8@A&Ws(m%wm1P5l`N~{BM0~gMQ&l*< zXjcYKO2^!GFuada8EDF56s9^NYdjjg;#A}Gzy+qta;;vG<8TuLYQv&-GgG&tBfn>x z$XDyOKm5q6x+6%}QHIRI=(F(TWE3WJnhk!r{`hLby>n^_yN~<0Yt6$H-{qM+``aO> z`PBs{)t);DV?=NSay}I<7K&ue^!>9PMae@<4YTl~(?Mq)@#UFi23oA}Ymo$a4n{3O zdUU^v@xb9}J|;c54@bu=qR^c(%;ATfc&Sxl3f)+<1}|GI-YqxGWE8yJD%|-H_ukF0 z*gzn0I9iOi?H+)69D-g=m}$0Dcxh`ID)rY+?-0c*8JApO4f6QVxWXh1B+VTPlLCD` z@nM%X$QoF|_R=^}>GKaQP>imv8$!~3EdoTo`7yJ1)nfKeH@{*N9iFV~7HT630GoI; zLB*m{!FNsKGM~f*PM!uC@xt!c=ZtX%hkrYh*|#Sy7p(Z=ZODF zfKpsAmK`AN-CpJ5VMX z8c@j$yX2%xX};7X{Cfc+A!F_4ZzfosmyxWd;c_DUL3(vqIlrQ7;u}>*r)YFH#!@ry zc_JLO=H?^{!Jb?<4>u-(*H2UUEPmE*rxdl`0>y{Ge+5rt829R`QW0CkzYW*sc^$&@ zK>B?pca{Ywn|Z#t^B|U)7?2r(o|Q2-`)5QpH|{qRgU05w9ccnXT7ak;Pz-uX+olMR z0iKhY7Ge3>m==tuBpuc-X*Ol|$yE#1Vzed9Jjlupp;-MW<(Fqd@VY8Lg!)#TY=3ZM z5X6i{uxzp?VFK&WoJq6kLflZzEx6bK$yvfJDXFgKML?dkfs-emHZD+uv(A*uE zHzdh0{8MGLYC(kwrQZh#0#EZ22K7P1CXj;(wUL=MA`qQ_0Zj>@j`^`ytinCP1U4Sj` z>@nLU{zLc#ZLm>X_9cv8XjcSe)yxQ80wqg%ChW6DUz#RaG2cCp%nKy;R`-drYSk=6 zqO^fo?zX6s9ktk&KT;OC4PhbxYTs8xep0U3J+y^SIvcXS?QKXov)OXk6aTQ#HJJ27MH1JUm#p?DpTOHJ(&Mr#c@I*R|ulqygL$b!kw6z zb&$3Mbx`_xS9#rnCkcW;#5BN0E04qmv8{btUr zLLS+r`K%<#`bqW)#+Y{?XCF&RFrzo4kkzi^HBDm-xqAhAqyfeB zeI4h}fbr@Xh(ZIcj80yG`0^g3%>Y0y6iLBQ;rK1)#FcJF=>9^Evz4`yysv#sqBtRb zH34znS*lD9BLcT4e=5sB>`1u)+jcWaUG%W$@2%!!BBs1TTD2A(bA9-Ti}DqYm^_M* zmB7zfP-XiKcRgE+kaJe;d_!&&Sp@OSQ|m!>cFw*^3dWvxX3QlC^p{?ndE{CLNT(EU zpJd*KzsFI}AVKXypy8BWMpI5n#dzqDd!G_$B(5}51WHv&wGr<|YdhP|n|3jysYdsj zGXF{&$-{qxBH1q8twpt>FX&|%;hFr-U6oleai!SuD#^R?SpS_f&A_*&3N4mEoa|3# z^db3Z2u9y3s7^+NHW`bV)Nlt~s)h#2u}w<`Jf`FzJ`DJoO5`1D>#wg)+ku{PB28k5 z_X#mveIh1CQ)2+@LeAEcQ21O5rtoE|@jy5?D;rh!$2*^Iw5TyT=D*xX`}JsFheUhz zt1j|oW@>nHQ=oy2w6eR_1ch<<5;S*ANf!x)WPAwyl5_07)gUS+yn@)f#m}I{QY5a_ zYntmM3f^O5*aLO$T^|FHDcE}})H4{^D#pGd@PtcQ!ITA6*VgICFpeNz>5kb%Bd153 zVe*_(6Yf%tPISWIlwuD=m6A`lt=RjLvq}puK}Np?v_e*vF`+}2nGjYSzqUXCIik+8 z*AK{22wLMCcP2!7Y>Tu%FR|}c2Ue{F|2DZho>gCU^Y4NfO7Mmzy$y8{y%{)xTYm8x zQr8L7%iH3Q3=ArV9G3*_m8BdJCotUamyW%iTfa@~bYBG+hv&KK6O6i=oji6QNxsBv z{y`fzU2L02J#8EXj)D!PHj-xk*_LFtiLHcg-mM+SlJW)-?hYCNAQ55(uXZYH{p393 z-@KatHj9N`xS3A&Hb;6b9$si!qguJ8z4U{_??*(bR0QztZyP_)K`pcGzq7hHAjd`h zd27vS<)$?0P+l3TC~ng@vTE16LA2eN4HvxHXHMpIRM>*w5)kDk3Gi-18LO*p` zyA2B>3SzgFkK#T_VY3PE5jiGLgqW3lxO6C~G$qmzb7m%f>r5jhI`-98jQUy7`OOWl z(%W&ojg3PNcD1M**)6gP0VCthZw=SK8bd8d1J6$c#C9z}7*=?TOv1#>YUqD&byFct z&E9a(RAW?&TD$M`M_~DdeiJ=(xi>7cekGRFr5A|Ie)$^b)wTM;%d3^v`=)b-p_R*-eUw(l^i&dKC6CT3zI24%A7w!!^_ zz>5(7M&(wNO;NOl4!^vkP#BMtYweNAoW6{ge55l-Y+!Bx`0&#ue~pv@xwCARNh`Tm z$aBgjzzYZ3r!|GyE4M=#84rG6GE4W4ut%Me=qB{DjpV>z8citxd2KqRP*Sb#f(wWI zD2*|5*5`^9=p zdpRxO7|Z3KTr0{o%_Niwz^5Yv0Kc%HV3DtK{m6K_sifGy6 zr@P*NzK1k6yw(sav(g?WdUE@DlP}a_-o$BpM`ROs%qppQ6U$`E=Cezp^GRbDJ=RA2 zeV4DWBmj|iesbsdW7)i&X;Vd&WC8LgGW_rbR%0V&#*M``c7UFf9xNanUi||4$k3uq zGIX5q8ncr}IQW%uh9rfyl}a_Q4P<&MM&ncw1QzY+&E)f5QKvnz76tF{q=o6YD zoIP@T%MV{a?+I-X%7C=K))*Il=w{nZtfi)ZMXHKdI;gd0U6aWfxiSSZ}#IN$BOO63;N^FbI^jwHzum`g8AT_O0LhrukGSiG4BQliclpmb7& zC9jp=R+4C3wbU68hg30%9R}}z81lmVev1wk6i?5WvG_H?Ydz>+uoBK-D`>0Ns<%ze zn!wO9QjKaS3WCI`p<^ueeDVg>O(;ap3TzwqQ|bGz^LOc2<^R zA~WLtYwKKvb2Aezb7-Gfj$DyH+jTH;mncL z@6;l9Y4gZ;SL_k;va^rl-8BgbHNoCQpc9wtMBgnVA@)xZp1ugU56Pe>xZWZj>u%C2 ztz#i^#hPZG2|BQR^7dD5<wG~k2_Jwe*`y-y4Z^EeygG76<-`zuoyJkXieakn4r=VBlToV=9^#<%64dwd!q7J zs<>*pMC|qHk8qt<-d2;m28(5`CPJ2egY5LnA`JqMRQ3iuVQiprK5}EHgFs`VB!M8?P~Ode_kHw$sfHNL{G^ zN|gUTbhO0?s&E#|+Nh|i<>m(&-~LsQsXqTA$N&{3dZCvD3GCf!Q2`nyx(k`1AIFql zf8d|P{c)e4eK8Eue61Pdru(-ySgD;t)zpId?qhta@g%FL6>3n*&z9VBZ-@EbUw=l| z96$aoxv2b3|L;U3P`;_YkZ)KdQ?4es8FX!MmOE2!7sq+JydQ z)?LWj)u&a&Wi|c%HpkYH6?y$%_X-z#E+Q0ON=J4&*n$3X$_QujyGsEA!)pc3N*0`p zUi?T%-UbsjLG4$FS3zpG2wWFu#IN$5*<>W0->_o@44EmG!FgJe}s%ZX* z|6TeiQ!ubadEB@lsccgf)k~%ZvBuG$c(7RyEwDFAQMOS5Srz4*Q7Tkp=9l|5XsM`% z_k~BbEw?)q49Qz^&Xs~iwUNE|2vk)HwrM+9WUv{CvEQA{=`b|$n!HWIlkGolkngNm zsSE<1`&xtWZ{QeQL&(>Q0UPT6LWq~kqg+|iwn6se@f-35TNaaaj*WnpIxl*DJZFXR zkD*K|YC_D=+<03B)hhAQ$f}l^XhjQ6?nMPv!nE~JyW+X#8GxWP9hB9@@Ym$_n82z+ zyfYDfCPk=;Z;)Q3rntG*JRdmz5n~x@H6J^&tNUZbO^v!$EwT8b`(E^TYc{>l8tm7h)trnE}ZOf873cE-~LdfHDpth z^mFj;k72*Ldw(pclv1+GU9E`rkbo!9hL=#Uf4UUR7V5NX3LMnc^^x6SQpxw@WN-#8 zn=s$WV-Lg2MT9Yk(6CgnfzU%;jHD7|(XZ5LgTvW9Q;M_go*Wo0PAm+1i`f+NoXg~D z%plTW8PH?Lsw%lY$Fx4Wh8}sM&jWRR1?2a#KL{eO;=`U`2dpfCeVzXd^C)-dMtv{= zN$r`>b6550MOi{9SCO`!L=FwOu%x&S87W;L-~YX;3=QJN{CWp=QeI>`BYdXbE25!N zA!I$|xarJE~X};xX6KGRAf;E&EKEK$*X41H+!O`M28Q5%cF8K=HIv^IhU7A%}0L!`V@jVx53g-KWG?)`+% zE)u)bYHg8~8-LC8`~;ad4GS7U1zEA#tIymLPxp*QW0li+3Ur~*ctGk zuArbUT?Xhm<1QG2W3zeGs6g5mHA&k~tTH(*w3&>I0ST8?#@ zavaiEHtpVRC0k2-sw`p6&W=s(P=OO4ZW)URPSm^W^*??~fZqK2VY7kByXrTyVvlb) zQ{gLWzwa(`C16c}>KOloNa?#{w@BfxTc(HJk9RY_e-$j@jF#ubhU2vz25GId&)Ig! zvTT>IA~P!j&B(#Vcea1#<^(%MVS$xQdjM*#(ZvOxu+L&65N0T+%}rAsXBKqXW13!V z@=0$!7dN7N@s!zNyTFvUvCXSa=ueD~Jg_J!Fd6?2ylQh?r(O~HmYM^m!{z-sLt8HU z=3&_-BF;n}K{l@r^`6q6BHoWS9G@~ci1CoP3@2}UFm+|K(I~AREPy}nU0t+GJ|300 z-?@7R3q6RzW~T*rLB>1oCO&Z5ji;cMnjpdfJ-5HypD)BJr;U< z_ezu+dTJFw9T6GZ)YROJL%f?QL4rLCm1PU0e#<-zW`fR3Z9A2xV0cO8snpeB!0rQY z4;LOqm+e z!MRTU$JvPNPffp^Z!X>XihmwNs)x1vC7e|S-Jg8-Kwd8STg7ME$$0+g znF+t1W)Sopg{6r7HSBAyzKDYY2>j<5m`#aBJ<7r3Hc||q3n_r_R;-({8jV|DCO#bO z(836lCVRlsvtEdNNrbd8EK!X%2EFSY5i@RLIeB25vEaH};Pzo*r+Fs4&1^q%$RIdp zi|ePdn)ndW%zK!4b!S=<;^QiyD=8oQ!&VkEfA2J-@C`65OP(LEa4B&dU3Qpu`YO?k zn=9P9@YR>(KQl?EHP6eB-tf#72G~KZj6ndub9a@}ov213^^AKd!@O6ST=m0Thr~}6 z&LR~M*-W2&W%!Ml+`ugh>Y1pqOYk8zBl zy%wV(FW9N2sBwNb@j~8`0_)F&3OL63D3d|Bg|ddsZ!{3(UQqwYu=kvU;4vBqOtp)U zN@WyC{Z%{lc` zbp42Ck4&@uLm$&9vA5{KC_+2imDl4Oob>#AmRYzvZKHw*;GFw(j7De)Sbb7gscJa00$SeJSK#Eamo6b=ZgIv`=siFvtO8^&PR8}SgNf)gcbb2#mKX_i z;p!=B9oXJYMKb6?Ik2gtab(u@Yh3{N3a3|eCRnhDL??2osg`MA$V;T(G4V@6X8gJ_ zzr^ySoo_p{7@!Gk^c6ZTvGR92;y#CRHuUN*JN@lS{cOVu4*LW5^Ft<4udnzs-Jd6 z^YI-*z{LPPVuaciDKm{}I&5Rlhgn4-j`1s;axp}(3o|Nagk$3T?3%Zy9uTx#Th-*CT) z@~?e)ME*lNT|eI~gA$5Qc#2NTiUC24m75g^MQg)5d0;L<_ZwzBmpX43r)Bw1NdiiU z&Vg1Q%M1Q^$>Hb;3TAO8B~5xx+~*tjr6Dvb%o-+iFv{H)BiUsIoh=f^W@f2qAQBP; zJ+dNqbnN0i{j!?W_Vw-b(r&XGgc}LzHy2?tT!J~M)v(w!z`>hfL34c;A;6S|cDpG7 zHeC^d2IRuUi!V$k9+pT*ACn*HC9A)c6pGwXHaVyzsc9x7BW-Axe}L>nAAanf^z4vj zY6i?37JALhzZ-sm`KkZ^vDrmnWJGg<9~&4w%LT(BVOf1Ozrftq5mU8XOe zz@;6nz<{XBTPd=_?7&FxE_b1=c-{!cjD=8YXkkQ_h_>Tzd3kHR%cMe-LOsz`cbsmi zj_~OeLR3zJq#!M+{gtl$Wk$>w|8Z-?=s(lcd5jzEwPrR4FV&7}&>^IHLPg6{=-Nrv z6lbSc=0RfIDmaLmHXC_Nw?GDdn70W6Zz~Z#jzog5)6rYR(^wrL*6cFqA~TKX=@%__ z)th&hD@ql2cN+Qv;)ow!e`PgY=Mgs4laf1L>&^W9uh_@eGB=iUBfreg$CE|6Q$C%; z5Etinc{Mu1wQcQ9u*4huj~q?|hc8tTJ%mK+OKc}+%vqHc9WX1^jH)C2&efTXhzX7; zNjr5KXg%`ZCC`yQ_#d7K_F1kcT_Su)4Z;UwjHsCQT&Aq;Icr^1!YhRSb$ecSE%mHt z(Re6*xAWZ`unQdtiBqNKUCe=Zb;Cfb)$(%Inb;m;2si3yqLZ0;nyMpOp2z8x#`O@g zOV34qjdyxS4)T__ar1(sXb3IqJmdTd--zDtsWVfL>EnM5=rt9u)0!O}W~S6Q=I=ir z^dgL~_rGfnuMuq3#{eQ7!dykfLkK&loRy5v(-?L(`10F9SrLWn54?!5R}_QxDloA z**6OBh@30_7KPZGfL|fR%bbPAf;|P}!gW>hJQdZ&M+l=D$C~QFrVL#2%J|VbVo*#F zL%g9`o)Y(xM`12nyiMVX+oeSn(Q$}O=cT!pwTWiUG-UOm8qEtaq+cr3k9f3`TC%>Y zdi0HXi~s6D*swa!1Zw&N?#yJT`D!2&RTt51QlNFh$GN##S0k>m&8p(B2*I^+*oQdd zhES!?UBSg5e(l$Y0Eg6y3Kh74JDaFv5-@7~d$z9zofUj8M-KJ7IwjiD{|xW~=qtm{?2nXy&Zj$X6bkZTL_5)!%ILi-2zUd|&Y&kr zbY|ZRn8n|}H72@*Bg4`0g^m7eUHxuvO?K$NAR)}~#SBxlQjIvRTEYR=XHV`vUkXY6 zfaNiVa7Uydj$2px)61#Z1|`-lLe{%0Y3zR^g!IQWJ`x^nYm!xd6)&=L2pD;?p++v2Xvc zz70V-v`@f`!>L)``J=;hu+FJ%G{w}vAktIwnTEmpmZ3TA*FlB+Og&nVuKFQ@r1HO; zTnw0DaK#^ev$C#|zD~}vpO{zO(-HPW$g42|KAUEWG%PM->orzlSZ{KQFhWW#{>3h& z-%QD_vy-^#9bFrFsBhHF*k_4SL9)Vpm-UQd zp^ni7zmOuLE0(7UJC*-ian^+~$R{6}IcCqHuE;wteGJxrG0q+N% zV#oLD10Zyz{mY7}eEUg~FBx0da6U2>;m?Rox@P~?XnWkDs7SOz#I}XtX8_h{yXRv9a8$3 zqdD$~Qer$K~wF3oT}S10$vQFGY3vM+N>STf7BKjKat z{K8R*zY^1&5UErFFgx;l$gN`^$!D%S7tC2`VMyl^W_&6&YqUz?n*mawva z0c1X}Ch6YpAtxmAh#XhxIfH=B5{P{K^OwFC|G{@x&fS;#DM9^_1Z-elV^=0{07u|^ zp?cT^A6rt}->j82njOZy5uR}SoRxw?iw~9NuK`j$h-ETTk9`;43?=7^*179YzIkU- zzvw1G`ifDj&hNVvSY6ZE{SJXZcgLa?tu^Tcu&MWo4lhSqK)6r0AOS01Z*g``$>;Rb zrEB3-FkpU1y&VyrH+cBsWV=Qw9X$!Fah`7$fzE@3%BPCXGT7y2Bnp_k;BL<(%lLo@qCC!OR``D`U3EZ| z&(l8>l?D+dMUSIF+9Q?jZjO?Yh6AKS5fBiBJMuU{1Vp;y=*ApRLy*`@w>q8p>t*q8OFMNCQrVVMfI3 zjV3&j*%{*5WAZQQ9VUS{fH=0iFCoN51bcbASEA^$lSEoiOpPU6Xwp)j2%a<(%`1#Pc$<#Wq@&`~elBzTCV_rpy=h2xZqa!P@ss&!- z{5hHx-=1`y-1Kq0{^nv0|E_e(S6^q)7S%oct#mCO7stu*nU6HXihXlLjK#?k-avqz zY{C)~zuCM=8oDnMT7@kWd8tAYEE(;x7R93f3i|+6X=FQqxG_Ei}Szb$(%>4s>PQFS7G}}Kc*PjYIyV@ z`e_mb*p&6=%M~9DA{VUGyuK@Q_3M%jLQdH0&m7^>A^1io-#D=Pbv(J}Z&7o2?=zw= zjU=Gv!3B>gs-lw{2W>ooE}AwYH1b=fH-fLV+FpgY&n4Hnyg3Qb-hafMJDHizDZh&4 zmEFt1-a&>P8n!k(vn(uG6>hBXPZS|+y4nh)rKnnWEYCw; zC8ng;^aiLoa>Ehm!#%DI-d(!NA8RZ~LiTMBoOZJ+C`Loi>v1&sFXB5nW){)VpM3X& zudj30eqfoIfxBqJnA#18*A~`rq5I~gb4Owh@iq^(nb{u5m9-$ScQ&YQ9~P;qkzuoC z7qEC(y0Oi}T@zfs+VX({{MtZSk1HT8(IGlAs(+`l3qL)e^)~=XOOu_DGd^3^JLrLF zNa%i6|Dc62ccp^=}FFvfp0xrq+)w-_6`?F;>wE*C^|C3n2v z^?9`@1$Xu&g|_QTe}!bCxT}(_Vw;FoG7>vQfmOZ>lq8j|1GA;3sKn$%HS`fGmZdkS z>PP12Q{Hb=M=h*yMm za7`Q0EN`@0PfsnIko`vC+LMpGe$P?I`jwfIjQd~BxHJjR-CSt;5S~g)fuv!@}xm{HNx=;l>`G!02cTP0Sk9d4RJu=Uy>#MF#Fm?Rk!w7_399yE8F~4>z zDCoydpy^FxfA-Vj{>E8#=jKI=CrAz--CqAZqnUBBPPFD47awsLlafck^HMt{9!V$fzVcpMUX`b+lB3T3vw&knkH|}=5b-&Df zeOT#pywM-L#SRJ*XV+>}IV#4N1^>&!dZ%A^=OIy zG)H0Q1Q$9tE`MpOQ!qn%&5JoRu{DR!zt}IrZAjw`|7eI(2kFk-1tKBYNcWXSUf#Np zS^S2KkZ}EY9lxvTh()SNf<+fOrZc0!2R`!D^uMYfgfZvQn>#i_fk}Zrrgfbu7wrk1 zjtFcOZFDNy)%dpQ$L0q?CCM$b1$z6(i16G!vniN+ouxw|M3sYoa^+$b;02a}y3v37 zR-(*oXkB!DT{Xz%b4QxU6wH63(obXvVsR^!cA3^>Iy9Tx6WnHIjhUzov zq~XCQL6RCd1e1n;AxnqwefDW+6QXbYhUA7Dm6L1m*C8~m9p<2&CkW$$&^W!vo%hMU z3Ghvxq-l;kq(FA-1;SXulj)Kh-~BDWqYHaU?d{z}^PPcx)o$IL5d}jnIAo}(9UpL{ zs}3~(KqxQTDrlcf7%*%yNiim6aq&@i2r96(5VYVywYk{nlDg%|dZi|(gxG4Hv?Z>m zFce&J47)|BT;(4d;R7WVZtnd*#GDuBNnw})&Ar`!w=~Cla-{9P@ zo9bmRDToOxw`=Sv9xn9q%mS$2FKcC!vYci-7Y+$P!R@@$KRbUaGCbZQ!1fYO_2Fse z7HA;^O%N)h6qj9}YxKQC?F}I5I^pyo4bS}0a!+hTn&eX@(MBWJOur%(b z)-0o%IX-v3Zu*kekR~OSw-#*<}K;qSM5`y-bhDJ3f74aRLc;p_Y%Ll5u$ ztIUvI%4TUGV2`H-jKV)cd)-`*fEpl ztu3PlPfImpByB^!H$e!rs{U}xv{K7XX}0XPr@2zj{9VNHCG+3h^UAQ$m2^7i+ZhiD zvkDh+g1a+0fnus4{zxan5LLLKf59*#6fFG7B(<_k*Ce3B@k^N;%iJ4PPCKAZX$7{S6Za^ag=2Sl1fA=z`yo*fyw2Cr`M9vcZeXFkk6hb{_}1IAx$%SJRd?UF5+-h+%4#g3QT}NIu!~&#zSg zcLV)(L(fh4ZAA6GTM8*I`&%)kjP6gVn8Lk0Ju}d@jWFG9qx$%G1D_r8&-m`#2+NO{ zW&ifj2oiXNSN5yg{ks?W?40Y;}_{kqud6vRs0rLOlGMwdI+}r4e35M9m<_AYIw0*-B zcT3zHl*S5D_>O%3`7TKE6QAMAyDwDQ#NM3?#O+t0IAtkau)z45RBMYQ3E_XQNatG} zc^MTT+8~AJ*1>?`_3d^#j>V~*eBC6x|FsdtYzkXHAESEamDzLJNckQ(C~~ih;8U&m z?-k9;w4EeL4hw*1n3usQpqLqAT}+vCQr$ z#r1aD=Qt|GS1A0~{BKClU^^TjwHP~yl~JSHGjPHZ-A!Q5%(F@E0HBKnuCw&rGf-J#% zjl8tBz+FyfxBlCp;QjsYIabHf&%9u%!Ca!~iLV+Y z#6W)1mG*-l_|N8An1TY^kwTA<&oK+fmN{IhLyG(XM*R8?QgjCYMnX0w6=%_Nx6?7Y z;ysnrzm(VbGiNDD#0Mu>^IQu0>HZK#E^kXc{%ZL(r6ZiH5!M*u525qN(K3=kyvni5 zefck6T(z;}aQ!uAB9y!?L3jT^0QT(gH9;lEI4T04|87;F3X6aC+n{^hHYTlF$(Y(W z8JDN*oM9*~@We<20%7NjuV0Ax{0T5}fP>ta&BvG@+Q}sH?K`vG6rP4n$+W=EvWQM) zM+M1$Hc0`6#LuAU+T$Bg?4{~JglU4fyN#e`nUku_N!(!!rE(Cll#8$zTNr)g`2&^FVsn>-K(em>* zI@X#0(ut*VXs%A1w?In4U=5u|JngO$Q&E-}o{*db$;I(^dzA|sj{_=TgG{VkB}x0~ zg(p4gEEu)t`gbw{8rLiOx%-iRPVm)}ViNU(RZfaz+!KiHnPCWv zOtJrI@Z$%|z$3WuQ!)ygQO6Rj2_So7Sh zY)g-YXp3S-3QZlz$KJw=SQaHdwLp??xl<*hFZrA6#_)x)D|-P|egQayO zekp(p>#dBW6;nHRhlF8h>_?ydPuwnidiBN`PcBUNA?cBm_PZ(?Zyb4pfIqtKi^qA{s0;1)L@|Mr}Tll#4&&3~x%UjY>rRws0^RsW7`OW6=i}P-y z=;K@Bne@j?zaKmVPNG1yOjCpV(Ur9|??+V9UIR<@+=r^WNQY%cS|Ue5C@^!|UBF3= zrtI_?ZQN%mi$2alsF1#NgTsU{x>*++xK#J+8=?t*4`27FO_BQSx$U(sLWB>YbmD~G zBn59y0U*GCdI|eHBR$5C#;|M;?=?gdsKnKr8ee~n4{ASD9GVEm~1q>ZdgYUh| zw16KcrrntksZ5t}`tc=7J=7;~44)pgl};K+AB4t6+v#%QQY2ONik{5_rruD_haFB=7%Fg!UcX@3$hau{{6G@ zczA>xQV2yzGxsa!R zNUxp5{Z(h$3@j`S!GR>H3CT5cV$$ToE;|Pu1Lr;4&* z@ZV=!xwqo+<--R#k0#i-cSOG85}LKTPIT5Qo?(dTBRzU^>p4e@T7Fu21PwGou;H$VpK*ZlUz z$$7oX28qK!ggxJ?Ha>hd4$eKhc!_PS zS6Ab*eo1BWr#Zryrd3`FInYG(Eledy=T3f^~iFEJhqS^WIt^d!i44tpF00Tl9@@*|VdH$g3G zH=Z`7;Kyxex(pHqM+)5!#ynF?7L6F@o9dr2=ekbS{>u68z-cc1>i(3B(AoTw3Ejz;eY=;|Z zuD#~WPP~w?*s3%CI;q1YI4Io}EiB_79DN^imPU-2sf+zs&*~{z*iYla zT4xnkzyI+Fv;^nmp4epGnCu+qdbamNQSJWpYq&jgTj2aEAt+cqNi7U$8RZ=1Rg`5E zX}1}+lC`t3z4PUT_|p^#&V~N|R@XGVGi3^@JTqT$K$KrtZ>dvgu{j0blk{plXK^0z z>;GW^f}Y*O+*HziW4~6fs7aiH~4fK~d zFxz4{)W58${;8-8Ppfo>{5P5l%i}~YESN&ee8v2Z909H#LEc$qxJ4S?zsW?KZst}` zOdYRf0_mCws*80+-BTD_^@LC`&>#W<35zc`OS-x`hC*4K+JgU$vOg-I`qOnn1c9(z z@uZw=z@`CedKis_3Dx16%!ijO;oCQsD@|y{=zkY4QDRiZ@H60v6^O@a1=|5elzQ6X zIu{8$m7j_@Tj5(wCkKIoJ_nA+`T-1>IFh`anzBuTge zTyrj)^`F-Mu5lFknDQ}U>A_e#3sHrY)ehi~E#&;eNkPBv=WG6$6C-8? zER3snhAvdSd8)VrQsl-qDl@tF-vYg=#HFIJJ%8H8P)1$!YC`O@B`48^N*2tc;=ZTX z^IQ4CCG@XZu=G!{vY1ew-v4@XcXj}saBOd%J7DbMN#tZd+dsuNTPNhimQ+SH~Q)gI<;YDzF2O^%sb*cmo_n%Ob*Q7Bq)&HMPyzREz4l_ow@2 zqGY4#X8^0zqkuRmpLg`v-$0v64>}hADGwBWYSCOIr74((wpr3D0Q+TEiWEI_L3x zTX(%*mqnD_r`QJs`a}%RZ~EwKZSv$-MP0_O_b=$UWKRKk$hIQ)#hQkU*6%^LO8qZG zumoI_z_-+9Q1G?r&#L4^m6>hLF2+T&a;{#ZqZfbq8bxfNlCTxwdNZ#vkps0DQO$Uy zDT1=OOm)cg4oE>2E*M*NF+R7nLV?!Jd1!-k-LI0fT?Gr9_139>E(l8Tb;+RCR6FE@ z?m0+MZ0mx89Rod+w;Q&+`h4tqyqLnyVo>|#TGQJa2<0-JCHrK5;WET>N41`@`&iV~ z=?fJA_4G&UP^s+ElJ9olcPoLuByDpiGS{-U8^!IBoB&|SoFgOm;lySi^q_ecU<@AbPfMRxFHV63P22nFbNz_E479&vA2md7pdb{>?y7}`_Po_&3!uOKMa8!wu8;k&RQga{-qFnviQ?ZOwL=5qeKw|L56KSJYx^9CRvW=*b)% z=#{mjQ!A^y`arF>WjQjEArus>gpEW2OwPPq%ddc}!_!?wGsB?jSISTrym9kQOVTnz zhLFl7$&;X~duA^C^{JbZla>Q>|2i*}CdIafP5e$>=ZKT!XZTeGu)K&hwcXS=ank-n ztvJ9zE|bUYl|NWBQvTS)xqE&*k1yQ(bGV-(e2>@8GMUg)gS~br9$e#Jmv#lK@~!pU zc_a-;-mEHH70u~m)U&{j&11@~OdkLIv!*JttnX7ptcS*)D>-vagtwO(e6OtG_Vl_> zXGotqR8ky*xp-yJJZZ7b4w`4oxXA&#C-O^+L_ge3?HHO#tld)+knD2qeVx3Ntd{9~ zhB<99gJ70NmYU%oMaK?yz5>akzqv9A{ZkvpIq%KX;ML&r$buX3{=`4&)SL^`dv z3LF5T$^uT-Ed`&b{zY$ ziUX7IA5e;QgttIf2JB9u>}HrZ{kL{lFvDa61$1r0P7dzUn8QD#kBbkHv}%|5eO2;0 zSxL@V7ERl=7`vlqb@wGrt!A{&~fGtq*C+_3_|a=c@21U z&FD$AKrJpBW5QO*1X9waRS&XUqTKj+sq7&8)@}ljx1|0x(wP^&hJ5J5n3OmG zj9mUcT6xOf^VWW%kzISydjOyujLZ!6O|(528~3yet)dwC1E2*clGReZh=}Ne+@|8 z=yq1q$YH zb3uM6Lw9mE8@$i&HbA42SronMWZXPZZ8tV73(P^yuLblvw8U+%$%?9dnQ5FBLp z){{YBJi2U0k{g?obx6}I3P6$V-IZ@g!p8`CI#E6`IG^~Va|}EPjR&m3Bx@#KE52a% zkCrXnfoE|C{i?=4GZ)AmGf|pa%XBt6S`5u)R9o-obap_{fxZeBk4@`-W!2};GCe_f@=f|=X-=JO3tgIFv4mP>KpJ94`D?$4su4V=>*P~ z4e<&$K;9}s#&w8?%j^)}95bJ*{)XDijs3CA-Ows_dd>;O;@cP<2BL3CHWPV>p04D{ z&s)}?EC%Kv&?lBZgaHp3nC0_71_iSSz%ce6H|K>umvApOm@c})Tx8yK#izfy;+s%( zW~GA0ne~44CrY*!Ec$dpn&UY*ISavz3^>}-M3AOmham; zPlU6jnLscUc(pjERm)M@gUw;K(P%Hz#dy3DG!6xz@h1cu+uXBR^*Q$THGQVMt*)Kq zp+znTbOwdtT9KOn)%(Y*ilgW_$qtsIoAurFD+{AK9QlLeNeGqrLR($Bt>F@ zVGb&A2nefsE1Fk*Ue;)#;ZLI`cn*v7E1kE{evlXlr#2abDk1L4j!2)v4a*wNnLr6M(jETBFyj{bw==Nvsb#o~Id+y3$5->Bryxj2;Q+K*Vl}*oF#TZV)h1I0o`*$L4&NF@#E|RDAzlt#`L{h7y zb*fViJPM0glhLBOUPz+0hR}m9)>Ql|n9Sx@F_cp5IRg&q``F3cF`W=PHAS`F7ST`g zc)goy46@%pYYpbg-k^H*{PR}HvP0`A^qD6kMH|7YNLmSL?*?m{AH_12!JDH|!F*$t z`PuZ+v~5z(D25HD9~n2_ZW^o3nx@zJ(JG#y57EkLtl9`d4D+%{akFge8~4iTUs9k) z;-l^`i2$F>Zu3TJDTO>E8DW{P&R(`wIAypuJn`ul8vh)Vjx|D7?tBC!Dt`h6hxb@nWp{o9bNP*ghtxfsqq7_Bvh) zjga`v`TWkGsV?i-%~f!cycTuaz_JfKAGzU?0mfRSO-{RJ%?_N!^pw0{C}|pQ%6_>{ zp8*M``Plh4Nt{c$)}-v|qVW9Wxeih}t4!Kn?~OhZ{jTP(7Vc3^BSk`X9*pY!`4CQ( za{p_{evDxYXyeqB*2N`5UVr;dzugWI41s1fent9aV6o6-wp^^buFPP>4Xr;v6jNZy z4OZ^8W(h`drUJBk0)B#6O`uww$PolLZmoHdleg?IP-BW9WY=H%`~9XyQWOLX6shBF zUT>IbN8>$=K9|J9B^BZG{+K+^0v3UUg8Ap=q;PchduZ^++nhF-P^z{Gjm^=X1J@c9 z`-Dfvh=BJySpR`L7+MGv^mI~tLR(!v=-6oH9S~IWR%GRQVwVyqUj;5~?77_&TfnUy zv>Q{5!2kIL>DHrE*T$${2k)4ezilmL4~E41>pA0j1d%Ktj^8FMnDRRuiXL6?T|W07qUV-+ z6Let-S?wu!oR`c!uD+LSk1~*5e++7w z_-3uF@%iejE?GG*)Hh>n3h$ksTDtCqjelh$?7hy{JR=Ox?wLIatC{>5ve->TSpW)l zVaViOm=;(XuTDo}L-f~`h3VsrZl;ruy5vTBKFEW6F%QtsSa8#U{%lL@oD$7FBy?6C zxSvJO2v-8@(-kzfG^j?HeNmJJ+D-7`g|b(D^m`0Mt{p;N^^*gcN;K^;Rs2=*zW0+4 zD()zJ&jsbdg{@3gVwk)ifd0z9$b2woQF_9fQ1>byN1yTtIYP$;Tyy{>e*p8gEpp;0 z=1{5l$rvEq0vQ}8e3BElx8hqA!$o27`45%oL~fZ@mkva9u5=juw#wady7uROWL|rt z#p{@N?@=?+L088bY%SL9VmBC!S&@0V=q}p4LK!DoojLa#-%+l0L%IMoPpj!Ab1%Jb z;LghyucbKf#4*Zs+OM_yMVkW_^Aj#*X`WsD+;VBs!~{Qky_R@=Jo!=OEf*izdH6uv;V=osN622%9)j#!b`Fju>(KYnUO<9r4?3vqIIvKC z$Q9rBqC?BrT_HKEgxFuTd7OU>V;`fuqS8)ngLu`M16*iTPP!BIS?*;TEBSQn^v9LR zZm_$}8#w3e-m}o^CykHR%m<(W4TE=+n~QKOex;uKGZ8{SkGuIf z@dn72qJcFN^Sn-yO|(>|2dEinS+z2vO+o7ZvT{Mc$b0P6T`9iVgW#-cz_1Zpdx*mq z{^$ZQk9jx(jwQOp^qS5ZDdE#oCF8i$=M^W|Mc?v3zDx0dr{bS3ZzWw@RA*mVyZd7< zAO8zFxLa&eC<9$g@71*k&(6o)`0+`|4>kLZeAltFNv@#sr9=vH%h4V#i}$tm`byT&Njv@3D zZf@_B-i9V_Lu2fI17}S}3XCyBs9o%P4ngJyPI~xWLbyb->O$=tzK58HhXEjk%9opi zkOF9=V3&JN_#+F(I=Zu#16>j-VN;7SUiNUkGwPtUR-+IZ23nDKO5Uq<8L8+z-A`Hj zo($TnIX}L<;H-}2ZSFpoLT;Bp5g{7AnfmVa;+e*#_%-OSwc zXUaQ=D$tTr{;~QW{Wq+Sf5~~tC&~)Z3CBN!_TZ()k3zBHkv%yq2d@-oC^D_B2hRqY z_%Ymm0Q_vO$i&1Jj);2yiI5WHdPnEE`+46zl^sVU$3LlT^XaD7Op!>as#;V?1=NX~ znkA7J1AEkgS5dSf;vhDk1oyEnSlYLYywQCl{LC#~Fz7l8hrh!_1Fv!h7w_Xe)J+~Z7QofLMcJ?c2z zpz$HRn-bYuQsyjCf9Cv!pbxOZrabM3#!-o__yIv+9IwB^?{V%5VbG8X6O;Pstam~n z(A&hW0%Mzeb9gR_PR6;hCqSb00dmF(4pogYYiRLBJqAwFo0V{hCIHT01pipZIix2b zFdfhr3MOv{UzlGeVHCI(?r;NC8d1uQD9M<`9lAQ+QP>QP;;%frf;+RuK zw{>vn3hfy{4`&Cly(8nC)e}%_MFy#vz_Y4yvxZ_+K`qvIo{BWGIQB0DX*~@}S5@;+{p|FjM$14f1;=V8t-{SJ*@b^b6`_OC0y}YlZy$i)qx2^zZQ?tcXyTr1 zQZ#NQelUQqedB8OO;S(BDMD;V$(db(KE(tz&EA?`lR%Cr*`4?(sIAV}P8d3b+&^Z# zThR=HS-|@fa;YK0#7$KtM_rx1ALxI}CbWC@8F$@dlIF}{m)%YU-=55==y(*AMM9;+vcAsF6<|jef!O}(gW427$&xW+a|*zcrvvv zjy3k>ZHXZ@BlzJT6~DM8gq3VfMQV#=8J&@D4;6D8fLAOjQZs739TGXmA)y zR^Z)>utP&?wen<@>qHz$UM}!-Lf>lx#6_K{TXat=UhM@2^(>q{HzTdKe}gllEC#Q0FfLrk66ve45}7)>~3t?H($hmfXBuENHM8`#!{yp z^wdYvtHeYpJn=c+f0m?Zsc$vYjwcbu@G1Xz5qKS}F@D}v#H&%-i~sn*W#^HJRY<$r zN>*qCu&RT4qVLGN^6;$7v?_HZLX(lAFNCQ>{$cB)53`1Y+)b6I9!R|9--gyI4D5 zp_l%V3%A8|Nj7+}^z6=zKa4*JzU!m?`cMHr90G+nn8TS zqEep=k?d0Km$$tD#7XoHz`ggnxYBg1<3(R_9fifFS)n>!Qib>@*NQ$D{lfErC+o;p z>sIcO@&VQ=+_Xz(H5SRZ_YfBHSkVt6?R89?;X-x2cyB(8Vrl~9n)=1ZMF6Gq^bF2F z-1XUunIoCOs)+k-k?zN=NT_L&*`bs5{5Wv37#8?$>>;v%gW>Fx%KQXNV+HjT4@)xI z^Z0VJwF!ERb^mP(A6u&Jw5~xKxuurCF8yFC1EF3eVSl;>$!wx=SDqc>DrzbmIi5r` z1OQ#}<+t#6Ird3NseJ6wjF2Q2=_jKUr|tiPNQ@t2Ku}JJ*zd+iBYPFd_25rCa8L4;kg@OFtG&`gK67gj1n>3ZGtCJKd8}rDbVa zW6{2q`XyR%^S9ZFcYi?bl@?grJYRmERT@peBYY z*=4usrlv>nKmO{fxWxHbbk;7mqLa_6q2N<}LpY{xmr`-&TV~JB=cAliS|7_G52K4J zY)+G9prdc3&OkX^lSdNO?sxZ9eH3hdrcGg<6uK#|7Z|EVX9HKyP**?0iD|p15`0P1 z=#O2>!9qtU&%+pG;Vl3g>|2VPKj;Z>0qn#81E(Wn3f}WIP{MotHJA5SOsQ%hx=>Tt zRyTWiF6?uuAapxHLu__QHZSED3Kp1DAA;3<(8mPPZzB)Xn@kJkeyEkS(*C)I7xU{L zO~1mIFH+m1TGUT$uEPsoT8sbe#zs-2VBKfY-fpw>p-Y>C>!!$xs?Kr&(-grssbQ3P z3x+g$tjge?qEB56xfEq)?O!iVrTp>whj+c2yy!52VK2OY+Jn$bFD{Y&Lv znI(-G+0lv2L?>K|^^<;MT{`dxvt{%?bM{0kE@&$)!g=W>Q5eU)a$@CYM_20g5YO|- z3h2y2vq5E=9pX)Xaz>Ep!7I~r6N7&4q?OE>J=aitDUD*u4xH}${ft-e$J_Tx9)`d8 z>ig7%KGjx!qoT+`otsXA+bXE-?;Mgp0Eij2r!>^9 z;r&8qI$C_L^8Oyk4wr6_Fu-WQQW1FD!PFu}uWSkL`VR{YD14K1%fl{9~)Vk5H(?uI!N6L)KpZs}=!Fr0k2(#nnH zij80dW2RQf2>FDMrr58m9{_zdP@meLVK;l4R+S}9=Pv7B`Vs`975-Nj;P@S%#cQ_4 z#*+Dy*To4H-QbBJt3*fB7yj{h0o{m>6xG)QXa(?@5SMEh2ZAFeZLSacRVI%_kb$() zElR=pac~3&5@xS#t!HWGGVii-^@*pqc=+O-e=>d&Vf>Y=!=T+FSDBgqAp`wK#z=|I z=6h(hBX&KqPHK&ZuV%ep!NntH^Vv%<{5bFc0|cWKq15HvxOkWtWJV8sbX~czJK^wX zgxi&gb>`Qyz>4U5t@H6X*w1r|&(jE8PfvA?qO;h`j=$dTJWttdxmDtA=;IU)Lt|i0 zvs((>YK@~NLE|b=zIk7kthb9Lv?~_bW#9(S1mM6<;H>LYaKzZlW9YuH!#xE~ipIzyWWLpW;;h!S5TP~PzLbDEor6!G~*vb-+} z_p`kvLOYnc_Od!$dXu)P0oD{7eFw)tQ7n^=u`KpBM!y`*bv-N>x@~a=25~#P_@fNaC{1IUr1_)m_3~~y zV`w-;i+g=T-V4!?h8zTbHlaMEleWrivGzQGaW=;dE9=`2F@pJMU>VO_(be~78_*zOc%`3R+>$Nma}YZ^!!t@LC8hbs_| zu1JAXD{@<;=Dy7l>Y~lf`O3&q9-}zp16ONBHNj{qX`Z7^n3k&5ViZXdoBe6A#7*AS z!o4*z>J3~D3qHdSF8+eBV*?=%)cr+21gPJlf64x1S$lm{XTMwoK}S6kw&HBf&$!cq zR2FpZO?{T&7#NFB%$WZ90Sf5o%zhn7yQ4g%>vGG+uhp3{__10@< zs0oeP_UklUJ>W@lMB*KB^elE`Cw3ooDMnaJh5q)YaR7E?QTWGdLYOhOD=KzIB?BZb z7B9d5#4|QGE?YExC)zykF}0V1^O92mDFPkW2?n@`4!7H|eWY=)bQg8Kn^v@$?*>{U zXB_!}8oMGBs!V0A^s2h%#?O(kXjB6J*Z24C<7Qxtd~R&Z&gb<@1lJIU;O(jz!T=WR zP*Jx?eO(hhn;wjgfckBk^y3L|60&6Z?sxGpnRj6Xyv2$Gk0u7Qmzf#SQp4LDz9YLs zYnL3uFZ!&57Go|<<`xR8YXFa4j*$7iD{)E1Ww9I~lJ4i*AmxBG4{hWnneLRVavBy0 zZ$aj@KCC7yDxy5%^3up&cto+WVpJGg4rrk%iu-vMQ zM|FZQ|AXLnj*8qTW#63A&1zU5y(J_nNt+o!8t<9}K(Py0+44U$ppAg8?XnV1tvd-Up@}J9g~fH`3{snE=P4q-Zl}#s)^y<=`i{54H~Sth?CbQsO7wYMwv$K*)jk|JbCt(D$pai8g-MgQYO%c4r5Gj4>t04XAd zO9zqlAvhgBN^JRPwC>^P(9PzR!*IosQTtp+pE@OY@us(H#+J$Ds}vkL)=7dIK?%@h zJwoF=NU7Zm*n@{7Q;IiXIWTrkCZfJ~Yx$Ba7zt;SL;P9ha~XK=>Qxn3m;`}1b^cx- zj(;)jmUr(dL!0@eT7k*SvDYh*RE^KGu~GlvM8W-G1i3R;1V3i##=!&yV%N=Mdr!eE z014SiC#E96q`eAfuh`jUkD?nSgdk?jLUahc63D_aGB}2bUuo~e9&Z~V+Fw@xy(P*Z z(;SO$P`ra6U*121N{6E6wvrfS4jw;T5rLiw#L<-*^KaMelxD5=fs^>lxUeb1d*%|# zaF`gGsRwDq{Q>WKCePqMI!6j{e$v>FQ0mH*_e|VKx=W$MT4t!EPaSF$lP2W_%nO4K z_t_-B#G!@dIB0)$w*6@cMe`TGhCah+MaiA!7=_!^gfQ+fVai)Al;Ju>tC~YyN_;D? zpiEFEOe&b5YJ^#h+QJ*&!>^pM$ge9}>0VE2yGM!T$`CPmuwDMUYf$H1O&50TLUwAu z6C9qobr!iw2grGN*Efzs7?n)i5?Z~o{GpX=%l#Q9^tS-#i`*4PqORLt+@<0Cy7Wet zI1((|23_+j_?j@H$QO`Tf5T&I)z$2!!3PVcStf}6OY5h`F0m~+ zyElN4FwnwYS7GPe^Yl-C^>^gB`>9PjOrM1yl`l8|(c5dpKQyHhS&K6UBRD>GfxCnL z%7W!*>dY;J#N~TF@3ogUs7#wURj{mvt<7Mj_cRo_|L7JNCz&X4i+lnO^nCSYpADI5 zzD$95@nAJY8F(y~S=V-$pq>iw_gpozF$A+mDRPLYWd+9z==WElb!m^?93t=C!(O(h zqn$H<-&nkZP)?&b2j@m^+q^i@{`h^p^=L`m!`|5dN%vjoPn5R8l{{DL9&>P4?Bs-% zK^(8c3yrVN?`ty)kV{2jw1{3dJv1(c<-|P#-+=^dIf%I@&1Dgz0_SGEMTc;7ymaW| z5?jQcRyuliBMIe-KB>rZhiE;bu3h?-r~Qf&L5bM(65Fbpr9;BZG={HUadcH;eUi_&A%1lhRN{hfika#iWIPh^D@$YL`W2u#A$k16EL(ga_Q3;+rG+o)wdG!Sn}@9$US;>XJspPRdG!f z_y7uR5GoRTJuU`i0keR;NU_U-qt2GW>foez{zIq6F|}Aq)lz1tnVOi4D8QAn@cu5xzkUm_8aog;jT25xy4lA>@gij z>|w0=4i3@-*UV5znc{gC8@L`UpS3-#2R5=xH=9IF=sv!W4X8I&g3q)K_zob2iE+8e zX!~UN2SIT6%VzHQcj-W3IZa9RGxCeW2ob2g=`t5-j3b6czn56<^6BfzailRw_0I0X zPo(ncueg;v$h%Cyl6UCxV1Ntb0Hzi&H)Bj#`+~oS#D71ERHSkk7#Ys#a5I^m zea}Me$NA}ATrRbD$GlOyl!BTVMpS`hB;|PNwCH169rhFVPK3skbI9av+RX)}e8C!N zSD~sYe9H{8)DkxDt&-?g>~0ZQUJ$Ng=q`ZC@Vmj6^+suC6w7>j2`G;{J&N5vf)px{ zED`)CRPOS3thgGSnPlpx84Z25;C?X^#{B=fJNJJk`#+AaR4S#DZiM7+Gjf(glfzwi zVi?^yte8`BKGmGbp@h3~s7(`wl;e##ZDtM;N#=Z*(`NZHG;>S{;rns_@ckpc`)${? zJwETxY1g&u{d~RNYG=fJ;JvFAg>$V1n2SD}Esk<#ii&~bC*Qb!PaosLu4Aub(MUz% z%qb_lVV7*P!SOZpE4723gk43_3q#5w$VL^k`|5zA+T!0o?}Ta`*7PwF|?Xd%3T((*QcNu{`-uVu{6RzQ%@u9ZsdQ zMhk;aw9!t!7*LMLz8vn1TVWFRlf|x?SRH)-h&n{}zJv+GwjF}{JQbQdX+dn+{X;JL zB}>l)>yAS$GrdwLm{cJ(PqCWTkqIb&a9MZ)3C+UU`CA(=ASWK zfFUyZ#M9o!NL|ZFkX$)hDuy>n{>n4_#?oV;rCU~~9{LRNEP4SF;e zEO>YOj8Y=)RX;r`rD9bYFM=TlJs(Snzx= zqj)3a)?=sv?qb{aGBkzAmXDuxG|lfI9fkz17x(PmIp0v{tu%^<`$u--Yj@`lyp0sM zfdP*^Lo5Kx_47MZXTyo;!lEWBkQ@k=%~q@rxDHES)~iVHJ|8Fl!hRw{i+Y2-IjZB{ z``h4droYxk3}|c%gL!`fhFHXC*?*7>GkY*YHge9<=65)O^hpjhP;D+=)%4hE`C_GF zjuVR)HN|6Qjae6kjY7wquYAXG(Nrk zQO-^3wJrq7DTQ)Du*VQtpPg*z_%sa4*NTeD1FYiU7ojl!f3`b4E^SG*wOk&zu1UGndBr9(9>N_K=c~{g9nJCx3SI zqD>Zjs4sneC8Em|n7Xcvb}SaeO5#|&DbFxR90+WyRekdguH96!<9PVU+)Jm!NO$je zraDf1=^9dze3G!rT`;qv_V#Q18u8Ids6hPYWYob+!?@S>VAN|ivCBE`SAl7v0ZLG~ zA*u^?!eP4W@onD{^4-Mprzic7X3u@&|EkjnPc=ZBNoM!Ci?Ye}c%=u=SF$-(}KCya$WQKbuQ^Abbf zq5+b8>MJIa?=ZwSjpOwzEh?`=qdQs7neJ`ZE;2jJj4d7_RpIwKN2e5xu>2Kq;>g4_ zR+bqydTvl%NB*y5zOos3w42A!l`somOUkj-OQOmNijvrpKv~<`14%(}nlKP+`Guz( z-TNjqt_WT6Z&?`8NMsIfH!yM&$lFa7C`dzBt;Ufo-3$7-7@V4+@pm4ejoKJv7h5RH#gT!q! z>0%PRUneGCR#Z1n0uVtyNiK67DbsTqn82!u$XKY@;Ln_MXNCYCU_-DpgPOWBh?g!-u-s8Sc(gf?kT-|pWGuE3V`YVX%!Q*3=o~F0OZ*U}$gVVVB60)CC&jjD?=IC71x_%UhpI?B6AH%-oBReH_d&Yis&5%s zINwrNzAV~^|CTFP<^pU!wZFbJzFiY{<^6umPDo~fyiGvJq+RX8j@SFB%|kYDxZcl? zl6rhm86sb;pMm|}C-jx9(z;5%ng8XzP?T${+{`Z=*{(KfwHE4%@)lVNLoVdFZvF1( z4q0rUf8J}Ii%X>)9EXu#z+V|PES0<9IiYLc5j#F8#mtaz2^i7FEmhx6R2!-=`L@i% z`$u3=!UFJlgV>atG;raGM$Cn{f{g#LD3A3YHF~|G;48pkK{CDAReAXeuIFt(rfnXn zCv5xN*E?q}1_;-g7j?gH4892u?HxRzyas~sqBJ*Bc|dnfz@V_$LFQI*><5+Y)iM|+73!#0>$6b z+~V$E4A?G6?ODQfV@AgxVfUX>7(laFF^93m-v~0nvI&()pEvunzTlc?jVr>uvWb(3+Q4YNK-%)a43KG%VI@W!)T}9kU3{TFMt$}r?@<*B)kS~) zRHQ#hoo07hEXV#qX~HTLEv)fLB-4J+2x7SOTgP<3M?}g(rb0@`TaO2$9e7@OF-(z? z3J>zJydIfi7siYy*`@sp9YZ928MjGd9+K5p&8`^jCJ|~s6x(*HdyG^W+|eTGFTvYy z<*%8z6O+igtF z_uk6#ME3k&hYOp20w}rBPG!$qwhSF#y8q`8du)w-ln)|8aX*TB+yq>Ngz3a|lgTId zmrd6W*OFe}EHZ9F1tRxBewrZlvaph`S^x2b@k5q^Mph?f^T99G&U$iD$OoZ27$xNv zV*f1?3{9Blr_`|-gc>W4A&fDib%!*$)=b#`K)nW9_*I+CGj}UpNyFrlL^VHEExmos zI_FYOQW2)N*X58+{AjYY(^&D?orSTqC?jZ@&{cb!TaCLb_?5bukLOQB7Mz@6naN+7gD4z1fzWF-Ido$oPm%`!Y9usz%6e zTA?Y?3G4Yb#yM@)zzLgz?2TY51^3G$AW@5H=B8%(=BRMDjeu&ig+52`z*U&|0zRzz z4@bQ@=MGB{m-hP?i-EygPgLRtDtKjq0X~Q-J3l#^hAP0!9IGuxgN6OK9GQRHZjd=f zhG+&qKcp<+4xOe;NjG>FA{if6oyEFs?)Ff_$Gl{_cAGPMdO=RoZHnNtX{ou4MTf@lH Date: Wed, 27 Jun 2018 17:57:45 +0800 Subject: [PATCH 17/22] =?UTF-8?q?=E5=A2=9E=E5=8A=A0Redis=E6=8A=80=E6=9C=AF?= =?UTF-8?q?=E6=80=BB=E7=BB=93=E6=80=9D=E7=BB=B4=E5=AF=BC=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 072b0b4..fb1db0f 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,8 @@ private RedisCacheUtil redisCacheUtil; ```java @Cacheable(value = Constants.Redis.SYSTEM, key = ACTIONS_CACHE_KEY) ``` -## Redis技术总结 +## Redis技术总结 + ![](Redis技术总结.png) From 99f57d40260927377098df9a1a14dbc74dcfd123 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=87=E6=9D=B0?= <1569925@gmail.com> Date: Wed, 27 Jun 2018 17:59:02 +0800 Subject: [PATCH 18/22] Update README.md --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index fb1db0f..a7e358d 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ - RedisTemplate封装工具类 `redisTools` - 可视化分布式ID生成器 `distributedId` - 可靠分布式锁工具类 `distributedLock`(lua脚本实现原子性解决断电问题、valueId避免错误释放问题) -- [Redis技术总结思维导图](#jump) +- [Redis技术总结思维导图](#user-content-redis技术总结) *** ## Maven ```xml @@ -103,7 +103,6 @@ private RedisCacheUtil redisCacheUtil; @Cacheable(value = Constants.Redis.SYSTEM, key = ACTIONS_CACHE_KEY) ``` ## Redis技术总结 - ![](Redis技术总结.png) From 666bb47080e5199ed3e8f7ba5a08de1948ce732f Mon Sep 17 00:00:00 2001 From: wenjie <1569925@qq.com> Date: Tue, 30 Oct 2018 10:22:51 +0800 Subject: [PATCH 19/22] =?UTF-8?q?util=E5=A2=9E=E5=8A=A0redis=E5=9C=B0?= =?UTF-8?q?=E7=90=86=E4=BD=8D=E7=BD=AE=E7=9B=B8=E5=85=B3method?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- redisTools/GeoRadiusDto.java | 33 +++++++ redisTools/RedisCacheUtil.java | 162 +++++++++++++++++++++++++++++++++ 2 files changed, 195 insertions(+) create mode 100644 redisTools/GeoRadiusDto.java diff --git a/redisTools/GeoRadiusDto.java b/redisTools/GeoRadiusDto.java new file mode 100644 index 0000000..4067b18 --- /dev/null +++ b/redisTools/GeoRadiusDto.java @@ -0,0 +1,33 @@ + +public class GeoRadiusDto { + + private String member; + + private Double x; + + private Double y; + + public String getMember() { + return member; + } + + public void setMember(String member) { + this.member = member; + } + + public Double getX() { + return x; + } + + public void setX(Double x) { + this.x = x; + } + + public Double getY() { + return y; + } + + public void setY(Double y) { + this.y = y; + } +} diff --git a/redisTools/RedisCacheUtil.java b/redisTools/RedisCacheUtil.java index 9ef1514..d28b28c 100644 --- a/redisTools/RedisCacheUtil.java +++ b/redisTools/RedisCacheUtil.java @@ -187,4 +187,166 @@ public Map getCacheIntegerMap(String key) { Map map = redisTemplate.opsForHash().entries(key); return map; } + + + //=====================GEO地理位置相关===================== + + /** + * 增加定位点 + * + * @param x + * @param y + * @param member + * @param time + * @return + */ + public boolean addGeo(double x, double y, String member, long time) { + String key = GEO_KEY; + try { + GeoOperations geoOps = redisTemplate.opsForGeo(); + geoOps.add(key, new Point(x, y), member); + if (time > 0) { + redisTemplate.expire(key, time, TimeUnit.SECONDS); + } + } catch (Throwable t) { + t.printStackTrace(); + log.error("缓存[" + key + "]" + "失败, point[" + x + "," + + y + "], member[" + member + "]" + ", error[" + t + "]"); + } + return true; + } + + + /** + * 删除定位点 + * + * @param members + * @return + */ + public boolean removeGeo(String... members) { + try { + GeoOperations geoOps = redisTemplate.opsForGeo(); + geoOps.remove(GEO_KEY, members); + } catch (Throwable t) { + log.error("移除[" + GEO_KEY + "]" + "失败" + ", error[" + t + "]"); + } + return true; + } + + + /** + * 计算定位距离 + * + * @param member1 + * @param member2 + * @return + */ + public Distance distanceGeo(String member1, String member2) { + try { + GeoOperations geoOps = redisTemplate.opsForGeo(); + return geoOps.geoDist(GEO_KEY, member1, member2); + } catch (Throwable t) { + log.error("计算距离[" + GEO_KEY + "]" + "失败, member[" + member1 + "," + member2 + "], error[" + t + "]"); + } + return null; + } + + /** + * 获取坐标 + * + * @param members + * @return + */ + public List getGeo(String... members) { + try { + GeoOperations geoOps = redisTemplate.opsForGeo(); + return geoOps.position(GEO_KEY, members); + } catch (Throwable t) { + log.error("获取坐标[" + GEO_KEY + "]" + "失败]" + ", error[" + t + "]"); + } + return null; + } + + /** + * 基于某个坐标的附近的东西 + * + * @param x + * @param y + * @param distance + * @param direction + */ + public List raduisGeo(double x, double y, double distance, Sort.Direction direction) { + List radiusDtos = new ArrayList<>(); + try { + GeoOperations geoOps = redisTemplate.opsForGeo(); + + //设置geo查询参数 + RedisGeoCommands.GeoRadiusCommandArgs geoRadiusArgs = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs(); + geoRadiusArgs = geoRadiusArgs.includeCoordinates().includeDistance();//查询返回结果包括距离和坐标 + if (Sort.Direction.ASC.equals(direction)) {//按查询出的坐标距离中心坐标的距离进行排序 + geoRadiusArgs.sortAscending(); + } else if (Sort.Direction.DESC.equals(direction)) { + geoRadiusArgs.sortDescending(); + } + GeoResults> geoResults = geoOps.radius(GEO_KEY, new Circle(new Point(x, y), new Distance(distance, RedisGeoCommands.DistanceUnit.METERS)), geoRadiusArgs); + + List>> geoResultList = geoResults.getContent(); + for (GeoResult> geoResult : geoResultList) { + String name = geoResult.getContent().getName(); + Point point = geoResult.getContent().getPoint(); + GeoRadiusDto radiusDto = new GeoRadiusDto(); + radiusDto.setMember(name); + radiusDto.setX(point.getX()); + radiusDto.setY(point.getY()); + radiusDtos.add(radiusDto); + } + } catch (Throwable t) { + + } + return radiusDtos; + } + + + /** + * 基于某个key的附近的东西 + * + * @param member + * @param distance + * @param direction + */ + public List raduisGeo(String member, double distance, Sort.Direction direction) { + List radiusDtos = new ArrayList<>(); + try { + GeoOperations geoOps = redisTemplate.opsForGeo(); + + //设置geo查询参数 + RedisGeoCommands.GeoRadiusCommandArgs geoRadiusArgs = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs(); + geoRadiusArgs = geoRadiusArgs.includeCoordinates().includeDistance();//查询返回结果包括距离和坐标 + if (Sort.Direction.ASC.equals(direction)) {//按查询出的坐标距离中心坐标的距离进行排序 + geoRadiusArgs.sortAscending(); + } else if (Sort.Direction.DESC.equals(direction)) { + geoRadiusArgs.sortDescending(); + } + GeoResults> geoResults = geoOps.radius(GEO_KEY, member, new Distance(distance, RedisGeoCommands.DistanceUnit.METERS), geoRadiusArgs); + + List>> geoResultList = geoResults.getContent(); + for (GeoResult> geoResult : geoResultList) { + String name = geoResult.getContent().getName(); + //结果集排除自己 + if (!name.equals(member)) { + Point point = geoResult.getContent().getPoint(); + GeoRadiusDto radiusDto = new GeoRadiusDto(); + radiusDto.setMember(name); + radiusDto.setX(point.getX()); + radiusDto.setY(point.getY()); + radiusDtos.add(radiusDto); + } + } + } catch (Throwable t) { + + } + return radiusDtos; + } + + } From 2ea0bc3b78be6e230282f7171b524a16e81dbc5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=87=E6=9D=B0?= <1569925@gmail.com> Date: Thu, 4 Jun 2020 15:57:17 +0800 Subject: [PATCH 20/22] =?UTF-8?q?=E7=AE=80=E5=8C=96readme=E7=9A=84how=20to?= =?UTF-8?q?=20use?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a7e358d..440ce61 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,7 @@ private RedisCacheUtil redisCacheUtil; ``` 2、Spring注解方式适用于简单的数据缓存 ```java -@Cacheable(value = Constants.Redis.SYSTEM, key = ACTIONS_CACHE_KEY) +@Cacheable(value = Constants.RedisKey.XXX_KEY) ``` ## Redis技术总结 ![](Redis技术总结.png) From 7255bf6805bad6e27579798a8bf9cbf28bf72f6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=87=E6=9D=B0?= <1569925@gmail.com> Date: Wed, 26 Aug 2020 16:18:04 +0800 Subject: [PATCH 21/22] Update GeoRadiusDto.java MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 增加注释 --- redisTools/GeoRadiusDto.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/redisTools/GeoRadiusDto.java b/redisTools/GeoRadiusDto.java index 4067b18..9194ab3 100644 --- a/redisTools/GeoRadiusDto.java +++ b/redisTools/GeoRadiusDto.java @@ -1,4 +1,6 @@ - +/** + * 地理位置相关dto + */ public class GeoRadiusDto { private String member; From 001a6dcadb58cf914cfc9a082c6f1e5a999ce536 Mon Sep 17 00:00:00 2001 From: WellJay <1569925@gmail.com> Date: Mon, 10 Feb 2025 15:43:10 +0800 Subject: [PATCH 22/22] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 440ce61..634eda5 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # spring-data-redis-tools - RedisTemplate封装工具类 `redisTools` -- 可视化分布式ID生成器 `distributedId` +- 可视化分布式ID生成器 `distributedId` (eg:JD202501010001) - 可靠分布式锁工具类 `distributedLock`(lua脚本实现原子性解决断电问题、valueId避免错误释放问题) - [Redis技术总结思维导图](#user-content-redis技术总结) ***