From 2ed089900d91adbfb4c03be194a5ffa6f3680966 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 13 Mar 2016 17:23:15 +0200 Subject: [PATCH 01/35] Initial commit for Hexagonal Architecture pattern --- hexagonal/pom.xml | 47 +++++++++++++++++++ .../main/java/com/iluwatar/hexagonal/App.java | 33 +++++++++++++ .../java/com/iluwatar/hexagonal/AppTest.java | 41 ++++++++++++++++ pom.xml | 9 ++-- 4 files changed, 125 insertions(+), 5 deletions(-) create mode 100644 hexagonal/pom.xml create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/App.java create mode 100644 hexagonal/src/test/java/com/iluwatar/hexagonal/AppTest.java diff --git a/hexagonal/pom.xml b/hexagonal/pom.xml new file mode 100644 index 000000000..1630e0eee --- /dev/null +++ b/hexagonal/pom.xml @@ -0,0 +1,47 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.11.0-SNAPSHOT + + com.iluwatar.hexagonal + hexagonal + 1.0-SNAPSHOT + hexagonal + http://maven.apache.org + + + junit + junit + test + + + diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java new file mode 100644 index 000000000..a0837ba4b --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java @@ -0,0 +1,33 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal; + +/** + * Hello world! + * + */ +public class App { + public static void main(String[] args) { + System.out.println("Hello World!"); + } +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/AppTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/AppTest.java new file mode 100644 index 000000000..3a2672715 --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/AppTest.java @@ -0,0 +1,41 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +/** + * Unit test for simple App. + */ +public class AppTest { + + /** + * Rigourous Test :-) + */ + @Test + public void testApp() { + assertTrue(true); + } +} diff --git a/pom.xml b/pom.xml index 555844660..6339fb72b 100644 --- a/pom.xml +++ b/pom.xml @@ -22,9 +22,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---> - +--> 4.0.0 com.iluwatar @@ -123,7 +121,8 @@ feature-toggle value-object monad - + hexagonal + @@ -394,4 +393,4 @@ - + \ No newline at end of file From 06546ae3cf3c1ca9c3a149c489ce4b2aff1c7e2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 13 Mar 2016 17:23:31 +0200 Subject: [PATCH 02/35] Fix license headers --- .../java/com/iluwatar/factorykit/App.java | 22 +++++++++++++++++ .../java/com/iluwatar/factorykit/Axe.java | 22 +++++++++++++++++ .../java/com/iluwatar/factorykit/Bow.java | 22 +++++++++++++++++ .../java/com/iluwatar/factorykit/Builder.java | 22 +++++++++++++++++ .../java/com/iluwatar/factorykit/Spear.java | 22 +++++++++++++++++ .../java/com/iluwatar/factorykit/Sword.java | 22 +++++++++++++++++ .../java/com/iluwatar/factorykit/Weapon.java | 22 +++++++++++++++++ .../iluwatar/factorykit/WeaponFactory.java | 22 +++++++++++++++++ .../com/iluwatar/factorykit/WeaponType.java | 22 +++++++++++++++++ .../com/iluwatar/factorykit/app/AppTest.java | 22 +++++++++++++++++ .../factorykit/factorykit/FactoryKitTest.java | 22 +++++++++++++++++ .../src/main/java/com/iluwatar/monad/App.java | 22 +++++++++++++++++ .../src/main/java/com/iluwatar/monad/Sex.java | 22 +++++++++++++++++ .../main/java/com/iluwatar/monad/User.java | 22 +++++++++++++++++ .../java/com/iluwatar/monad/Validator.java | 22 +++++++++++++++++ .../test/java/com/iluwatar/monad/AppTest.java | 22 +++++++++++++++++ .../java/com/iluwatar/monad/MonadTest.java | 22 +++++++++++++++++ value-object/pom.xml | 24 +++++++++++++++++++ .../java/com/iluwatar/value/object/App.java | 22 +++++++++++++++++ .../com/iluwatar/value/object/HeroStat.java | 22 +++++++++++++++++ .../com/iluwatar/value/object/AppTest.java | 22 +++++++++++++++++ .../iluwatar/value/object/HeroStatTest.java | 22 +++++++++++++++++ 22 files changed, 486 insertions(+) diff --git a/factory-kit/src/main/java/com/iluwatar/factorykit/App.java b/factory-kit/src/main/java/com/iluwatar/factorykit/App.java index 91d1eb061..f27bee170 100644 --- a/factory-kit/src/main/java/com/iluwatar/factorykit/App.java +++ b/factory-kit/src/main/java/com/iluwatar/factorykit/App.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.factorykit; /** diff --git a/factory-kit/src/main/java/com/iluwatar/factorykit/Axe.java b/factory-kit/src/main/java/com/iluwatar/factorykit/Axe.java index 4e1a5e554..826a1f9ec 100644 --- a/factory-kit/src/main/java/com/iluwatar/factorykit/Axe.java +++ b/factory-kit/src/main/java/com/iluwatar/factorykit/Axe.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.factorykit; public class Axe implements Weapon { diff --git a/factory-kit/src/main/java/com/iluwatar/factorykit/Bow.java b/factory-kit/src/main/java/com/iluwatar/factorykit/Bow.java index a90f4cf2e..5aa952c3d 100644 --- a/factory-kit/src/main/java/com/iluwatar/factorykit/Bow.java +++ b/factory-kit/src/main/java/com/iluwatar/factorykit/Bow.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.factorykit; public class Bow implements Weapon { diff --git a/factory-kit/src/main/java/com/iluwatar/factorykit/Builder.java b/factory-kit/src/main/java/com/iluwatar/factorykit/Builder.java index be74626f7..1049c7b6f 100644 --- a/factory-kit/src/main/java/com/iluwatar/factorykit/Builder.java +++ b/factory-kit/src/main/java/com/iluwatar/factorykit/Builder.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.factorykit; import java.util.function.Supplier; diff --git a/factory-kit/src/main/java/com/iluwatar/factorykit/Spear.java b/factory-kit/src/main/java/com/iluwatar/factorykit/Spear.java index a50f54290..c32811e8c 100644 --- a/factory-kit/src/main/java/com/iluwatar/factorykit/Spear.java +++ b/factory-kit/src/main/java/com/iluwatar/factorykit/Spear.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.factorykit; public class Spear implements Weapon { diff --git a/factory-kit/src/main/java/com/iluwatar/factorykit/Sword.java b/factory-kit/src/main/java/com/iluwatar/factorykit/Sword.java index 278febaf5..208cd6bbb 100644 --- a/factory-kit/src/main/java/com/iluwatar/factorykit/Sword.java +++ b/factory-kit/src/main/java/com/iluwatar/factorykit/Sword.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.factorykit; public class Sword implements Weapon { diff --git a/factory-kit/src/main/java/com/iluwatar/factorykit/Weapon.java b/factory-kit/src/main/java/com/iluwatar/factorykit/Weapon.java index 980a2219f..3d668e352 100644 --- a/factory-kit/src/main/java/com/iluwatar/factorykit/Weapon.java +++ b/factory-kit/src/main/java/com/iluwatar/factorykit/Weapon.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.factorykit; /** diff --git a/factory-kit/src/main/java/com/iluwatar/factorykit/WeaponFactory.java b/factory-kit/src/main/java/com/iluwatar/factorykit/WeaponFactory.java index e83a997c6..80a6fd9d3 100644 --- a/factory-kit/src/main/java/com/iluwatar/factorykit/WeaponFactory.java +++ b/factory-kit/src/main/java/com/iluwatar/factorykit/WeaponFactory.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.factorykit; import java.util.HashMap; diff --git a/factory-kit/src/main/java/com/iluwatar/factorykit/WeaponType.java b/factory-kit/src/main/java/com/iluwatar/factorykit/WeaponType.java index ac542048d..283f252de 100644 --- a/factory-kit/src/main/java/com/iluwatar/factorykit/WeaponType.java +++ b/factory-kit/src/main/java/com/iluwatar/factorykit/WeaponType.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.factorykit; /** diff --git a/factory-kit/src/test/java/com/iluwatar/factorykit/app/AppTest.java b/factory-kit/src/test/java/com/iluwatar/factorykit/app/AppTest.java index 9b9af2530..036326d97 100644 --- a/factory-kit/src/test/java/com/iluwatar/factorykit/app/AppTest.java +++ b/factory-kit/src/test/java/com/iluwatar/factorykit/app/AppTest.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.factorykit.app; import com.iluwatar.factorykit.App; diff --git a/factory-kit/src/test/java/com/iluwatar/factorykit/factorykit/FactoryKitTest.java b/factory-kit/src/test/java/com/iluwatar/factorykit/factorykit/FactoryKitTest.java index ea629f57d..c57bee3e3 100644 --- a/factory-kit/src/test/java/com/iluwatar/factorykit/factorykit/FactoryKitTest.java +++ b/factory-kit/src/test/java/com/iluwatar/factorykit/factorykit/FactoryKitTest.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.factorykit.factorykit; import com.iluwatar.factorykit.*; diff --git a/monad/src/main/java/com/iluwatar/monad/App.java b/monad/src/main/java/com/iluwatar/monad/App.java index e330cfa64..7b28fdcf8 100644 --- a/monad/src/main/java/com/iluwatar/monad/App.java +++ b/monad/src/main/java/com/iluwatar/monad/App.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.monad; import java.util.Objects; diff --git a/monad/src/main/java/com/iluwatar/monad/Sex.java b/monad/src/main/java/com/iluwatar/monad/Sex.java index 8b7e43f3c..b5d094d4b 100644 --- a/monad/src/main/java/com/iluwatar/monad/Sex.java +++ b/monad/src/main/java/com/iluwatar/monad/Sex.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.monad; public enum Sex { diff --git a/monad/src/main/java/com/iluwatar/monad/User.java b/monad/src/main/java/com/iluwatar/monad/User.java index edd299643..471094526 100644 --- a/monad/src/main/java/com/iluwatar/monad/User.java +++ b/monad/src/main/java/com/iluwatar/monad/User.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.monad; public class User { diff --git a/monad/src/main/java/com/iluwatar/monad/Validator.java b/monad/src/main/java/com/iluwatar/monad/Validator.java index 2298a4d8e..cc4f36020 100644 --- a/monad/src/main/java/com/iluwatar/monad/Validator.java +++ b/monad/src/main/java/com/iluwatar/monad/Validator.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.monad; import java.util.ArrayList; diff --git a/monad/src/test/java/com/iluwatar/monad/AppTest.java b/monad/src/test/java/com/iluwatar/monad/AppTest.java index 5d23b41a6..78440b468 100644 --- a/monad/src/test/java/com/iluwatar/monad/AppTest.java +++ b/monad/src/test/java/com/iluwatar/monad/AppTest.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.monad; import org.junit.Test; diff --git a/monad/src/test/java/com/iluwatar/monad/MonadTest.java b/monad/src/test/java/com/iluwatar/monad/MonadTest.java index ae78572f8..4ada7191d 100644 --- a/monad/src/test/java/com/iluwatar/monad/MonadTest.java +++ b/monad/src/test/java/com/iluwatar/monad/MonadTest.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.monad; diff --git a/value-object/pom.xml b/value-object/pom.xml index 1ed2adb43..3cbb7bb86 100644 --- a/value-object/pom.xml +++ b/value-object/pom.xml @@ -1,4 +1,28 @@ + diff --git a/value-object/src/main/java/com/iluwatar/value/object/App.java b/value-object/src/main/java/com/iluwatar/value/object/App.java index 85779fcb7..1e943d054 100644 --- a/value-object/src/main/java/com/iluwatar/value/object/App.java +++ b/value-object/src/main/java/com/iluwatar/value/object/App.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.value.object; /** diff --git a/value-object/src/main/java/com/iluwatar/value/object/HeroStat.java b/value-object/src/main/java/com/iluwatar/value/object/HeroStat.java index 101837db0..258c4d6a0 100644 --- a/value-object/src/main/java/com/iluwatar/value/object/HeroStat.java +++ b/value-object/src/main/java/com/iluwatar/value/object/HeroStat.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.value.object; /** diff --git a/value-object/src/test/java/com/iluwatar/value/object/AppTest.java b/value-object/src/test/java/com/iluwatar/value/object/AppTest.java index aed3c2f20..85ef8b84e 100644 --- a/value-object/src/test/java/com/iluwatar/value/object/AppTest.java +++ b/value-object/src/test/java/com/iluwatar/value/object/AppTest.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.value.object; import org.junit.Test; diff --git a/value-object/src/test/java/com/iluwatar/value/object/HeroStatTest.java b/value-object/src/test/java/com/iluwatar/value/object/HeroStatTest.java index 785a4d8fe..4a8034b0b 100644 --- a/value-object/src/test/java/com/iluwatar/value/object/HeroStatTest.java +++ b/value-object/src/test/java/com/iluwatar/value/object/HeroStatTest.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.value.object; import static org.hamcrest.CoreMatchers.is; From b0f96adeb169aa66ade1171de271382c335fbde4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 19 Mar 2016 18:13:30 +0200 Subject: [PATCH 03/35] Added class for lottery numbers and unit tests for it --- .../main/java/com/iluwatar/hexagonal/App.java | 5 +- .../hexagonal/domain/LotteryNumbers.java | 152 ++++++++++++++++++ .../java/com/iluwatar/hexagonal/AppTest.java | 8 +- .../hexagonal/domain/LotteryNumbersTest.java | 66 ++++++++ 4 files changed, 223 insertions(+), 8 deletions(-) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java create mode 100644 hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryNumbersTest.java diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java index a0837ba4b..b32564cbc 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java @@ -23,11 +23,12 @@ package com.iluwatar.hexagonal; /** - * Hello world! + * + * Example application demonstrating Hexagonal Architecture * */ public class App { public static void main(String[] args) { - System.out.println("Hello World!"); + System.out.println("Running hexagonal example"); } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java new file mode 100644 index 000000000..6f54e743b --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java @@ -0,0 +1,152 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.domain; + +import java.util.Collections; +import java.util.HashSet; +import java.util.PrimitiveIterator; +import java.util.Random; +import java.util.Set; + +/** + * + * Value object representing lottery numbers. This lottery uses sets of 4 numbers. The numbers must be unique and + * between 1 and 20. + * + */ +public class LotteryNumbers { + + private final Set numbers; + + public static final int MIN_NUMBER = 1; + public static final int MAX_NUMBER = 20; + public static final int NUM_NUMBERS = 4; + + /** + * Constructor. Creates random lottery numbers. + */ + private LotteryNumbers() { + numbers = new HashSet<>(); + generateRandomNumbers(); + } + + /** + * Constructor. Uses given numbers. + */ + private LotteryNumbers(Set givenNumbers) { + numbers = new HashSet<>(); + numbers.addAll(givenNumbers); + } + + /** + * @return random LotteryNumbers + */ + public static LotteryNumbers createRandom() { + return new LotteryNumbers(); + } + + /** + * @return given LotteryNumbers + */ + public static LotteryNumbers create(Set givenNumbers) { + return new LotteryNumbers(givenNumbers); + } + + /** + * @return lottery numbers + */ + public Set getNumbers() { + return Collections.unmodifiableSet(numbers); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((numbers == null) ? 0 : numbers.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + LotteryNumbers other = (LotteryNumbers) obj; + if (numbers == null) { + if (other.numbers != null) { + return false; + } + } else if (!numbers.equals(other.numbers)) { + return false; + } + return true; + } + + /** + * Generates 4 unique random numbers between 1-20 into numbers set. + */ + private void generateRandomNumbers() { + numbers.clear(); + RandomNumberGenerator generator = new RandomNumberGenerator(MIN_NUMBER, MAX_NUMBER); + while (numbers.size() < NUM_NUMBERS) { + int num = generator.nextInt(); + if (!numbers.contains(num)) { + numbers.add(num); + } + } + } + + /** + * + * Helper class for generating random numbers. + * + */ + private static class RandomNumberGenerator { + + private PrimitiveIterator.OfInt randomIterator; + + /** + * Initialize a new random number generator that generates random numbers in the range [min, max] + * + * @param min the min value (inclusive) + * @param max the max value (inclusive) + */ + public RandomNumberGenerator(int min, int max) { + randomIterator = new Random().ints(min, max + 1).iterator(); + } + + /** + * @return a random number in the range (min, max) + */ + public int nextInt() { + return randomIterator.nextInt(); + } + } +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/AppTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/AppTest.java index 3a2672715..8e19cca35 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/AppTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/AppTest.java @@ -22,8 +22,6 @@ */ package com.iluwatar.hexagonal; -import static org.junit.Assert.assertTrue; - import org.junit.Test; /** @@ -31,11 +29,9 @@ import org.junit.Test; */ public class AppTest { - /** - * Rigourous Test :-) - */ @Test public void testApp() { - assertTrue(true); + String[] args = {}; + App.main(args); } } diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryNumbersTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryNumbersTest.java new file mode 100644 index 000000000..24b999dae --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryNumbersTest.java @@ -0,0 +1,66 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.domain; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.HashSet; + +import org.junit.Test; + +/** + * + * Unit tests for {@link LotteryNumbers} + * + */ +public class LotteryNumbersTest { + + private static final int NUM_RANDOM_NUMBER_ROUNDS = 1000; + + @Test + public void testGivenNumbers() { + LotteryNumbers numbers = LotteryNumbers.create( + new HashSet<>(Arrays.asList(1, 2, 3, 4))); + assertEquals(numbers.getNumbers().size(), 4); + assertTrue(numbers.getNumbers().contains(1)); + assertTrue(numbers.getNumbers().contains(2)); + assertTrue(numbers.getNumbers().contains(3)); + assertTrue(numbers.getNumbers().contains(4)); + } + + @Test(expected = UnsupportedOperationException.class) + public void testNumbersCantBeModified() { + LotteryNumbers numbers = LotteryNumbers.create( + new HashSet<>(Arrays.asList(1, 2, 3, 4))); + numbers.getNumbers().add(5); + } + + @Test + public void testRandomNumbers() { + for (int i = 0; i < NUM_RANDOM_NUMBER_ROUNDS; i++) { + LotteryNumbers numbers = LotteryNumbers.createRandom(); + } + } +} From cd3a5d48d85158fa0d30145da24e5916eb2cec7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 19 Mar 2016 20:37:36 +0200 Subject: [PATCH 04/35] Work on lottery numbers unit tests --- .../hexagonal/domain/LotteryNumbersTest.java | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryNumbersTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryNumbersTest.java index 24b999dae..4f2751167 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryNumbersTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryNumbersTest.java @@ -23,6 +23,7 @@ package com.iluwatar.hexagonal.domain; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.util.Arrays; @@ -36,8 +37,6 @@ import org.junit.Test; * */ public class LotteryNumbersTest { - - private static final int NUM_RANDOM_NUMBER_ROUNDS = 1000; @Test public void testGivenNumbers() { @@ -59,8 +58,19 @@ public class LotteryNumbersTest { @Test public void testRandomNumbers() { - for (int i = 0; i < NUM_RANDOM_NUMBER_ROUNDS; i++) { - LotteryNumbers numbers = LotteryNumbers.createRandom(); - } + LotteryNumbers numbers = LotteryNumbers.createRandom(); + assertEquals(numbers.getNumbers().size(), LotteryNumbers.NUM_NUMBERS); + } + + @Test + public void testEquals() { + LotteryNumbers numbers1 = LotteryNumbers.create( + new HashSet<>(Arrays.asList(1, 2, 3, 4))); + LotteryNumbers numbers2 = LotteryNumbers.create( + new HashSet<>(Arrays.asList(1, 2, 3, 4))); + assertTrue(numbers1.equals(numbers2)); + LotteryNumbers numbers3 = LotteryNumbers.create( + new HashSet<>(Arrays.asList(11, 12, 13, 14))); + assertFalse(numbers1.equals(numbers3)); } } From 81718eb8ae2638616b9e3d8bc3f2602525ae06ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 19 Mar 2016 21:08:55 +0200 Subject: [PATCH 05/35] Added player details and unit tests --- .../hexagonal/domain/PlayerDetails.java | 96 +++++++++++++++++++ .../hexagonal/domain/PlayerDetailsTest.java | 23 +++++ 2 files changed, 119 insertions(+) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/domain/PlayerDetails.java create mode 100644 hexagonal/src/test/java/com/iluwatar/hexagonal/domain/PlayerDetailsTest.java diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/PlayerDetails.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/PlayerDetails.java new file mode 100644 index 000000000..3a27c0dde --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/PlayerDetails.java @@ -0,0 +1,96 @@ +package com.iluwatar.hexagonal.domain; + +/** + * + * Immutable value object containing lottery player details. + * + */ +public class PlayerDetails { + + private final String emailAddress; + private final String bankAccountNumber; + private final String phoneNumber; + + /** + * Constructor. + */ + private PlayerDetails(String email, String bankAccount, String phone) { + emailAddress = email; + bankAccountNumber = bankAccount; + phoneNumber = phone; + } + + /** + * Factory for creating new objects. + */ + public static PlayerDetails create(String email, String bankAccount, String phone) { + return new PlayerDetails(email, bankAccount, phone); + } + + /** + * @return email + */ + public String getEmail() { + return emailAddress; + } + + /** + * @return bank account number + */ + public String getBankAccount() { + return bankAccountNumber; + } + + /** + * @return phone number + */ + public String getPhoneNumber() { + return phoneNumber; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((bankAccountNumber == null) ? 0 : bankAccountNumber.hashCode()); + result = prime * result + ((emailAddress == null) ? 0 : emailAddress.hashCode()); + result = prime * result + ((phoneNumber == null) ? 0 : phoneNumber.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + PlayerDetails other = (PlayerDetails) obj; + if (bankAccountNumber == null) { + if (other.bankAccountNumber != null) { + return false; + } + } else if (!bankAccountNumber.equals(other.bankAccountNumber)) { + return false; + } + if (emailAddress == null) { + if (other.emailAddress != null) { + return false; + } + } else if (!emailAddress.equals(other.emailAddress)) { + return false; + } + if (phoneNumber == null) { + if (other.phoneNumber != null) { + return false; + } + } else if (!phoneNumber.equals(other.phoneNumber)) { + return false; + } + return true; + } +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/PlayerDetailsTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/PlayerDetailsTest.java new file mode 100644 index 000000000..73faa9a94 --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/PlayerDetailsTest.java @@ -0,0 +1,23 @@ +package com.iluwatar.hexagonal.domain; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import org.junit.Test; + +/** + * + * Unit tests for {@link PlayerDetails} + * + */ +public class PlayerDetailsTest { + + @Test + public void testEquals() { + PlayerDetails details1 = PlayerDetails.create("tom@foo.bar", "11212-123434", "+12323425"); + PlayerDetails details2 = PlayerDetails.create("tom@foo.bar", "11212-123434", "+12323425"); + assertEquals(details1, details2); + PlayerDetails details3 = PlayerDetails.create("john@foo.bar", "16412-123439", "+34323432"); + assertFalse(details1.equals(details3)); + } +} From 035b14fef8f26b2aa68d7d885c07ce03a90a7c5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 19 Mar 2016 21:57:57 +0200 Subject: [PATCH 06/35] Added lottery ticket and unit tests --- .../hexagonal/domain/LotteryTicket.java | 101 ++++++++++++++++++ .../hexagonal/domain/PlayerDetails.java | 22 ++++ .../hexagonal/domain/LotteryTicketTest.java | 49 +++++++++ .../hexagonal/domain/PlayerDetailsTest.java | 22 ++++ 4 files changed, 194 insertions(+) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicket.java create mode 100644 hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketTest.java diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicket.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicket.java new file mode 100644 index 000000000..e5828cfbf --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicket.java @@ -0,0 +1,101 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.domain; + +/** + * + * Immutable value object representing lottery ticket. + * + */ +public class LotteryTicket { + + private final PlayerDetails playerDetails; + private final LotteryNumbers lotteryNumbers; + + /** + * Constructor. + */ + private LotteryTicket(PlayerDetails details, LotteryNumbers numbers) { + playerDetails = details; + lotteryNumbers = numbers; + } + + /** + * Factory for creating lottery tickets; + */ + public static LotteryTicket create(PlayerDetails details, LotteryNumbers numbers) { + return new LotteryTicket(details, numbers); + } + + /** + * @return player details + */ + public PlayerDetails getPlayerDetails() { + return playerDetails; + } + + /** + * @return lottery numbers + */ + public LotteryNumbers getNumbers() { + return lotteryNumbers; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((lotteryNumbers == null) ? 0 : lotteryNumbers.hashCode()); + result = prime * result + ((playerDetails == null) ? 0 : playerDetails.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + LotteryTicket other = (LotteryTicket) obj; + if (lotteryNumbers == null) { + if (other.lotteryNumbers != null) { + return false; + } + } else if (!lotteryNumbers.equals(other.lotteryNumbers)) { + return false; + } + if (playerDetails == null) { + if (other.playerDetails != null) { + return false; + } + } else if (!playerDetails.equals(other.playerDetails)) { + return false; + } + return true; + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/PlayerDetails.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/PlayerDetails.java index 3a27c0dde..2fcdb6eb3 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/PlayerDetails.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/PlayerDetails.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.hexagonal.domain; /** diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketTest.java new file mode 100644 index 000000000..e1918686a --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketTest.java @@ -0,0 +1,49 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.domain; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import java.util.Arrays; +import java.util.HashSet; + +import org.junit.Test; + +public class LotteryTicketTest { + + @Test + public void testEquals() { + PlayerDetails details1 = PlayerDetails.create("bob@foo.bar", "1212-121212", "+34332322"); + LotteryNumbers numbers1 = LotteryNumbers.create(new HashSet(Arrays.asList(1, 2, 3, 4))); + LotteryTicket ticket1 = LotteryTicket.create(details1, numbers1); + PlayerDetails details2 = PlayerDetails.create("bob@foo.bar", "1212-121212", "+34332322"); + LotteryNumbers numbers2 = LotteryNumbers.create(new HashSet(Arrays.asList(1, 2, 3, 4))); + LotteryTicket ticket2 = LotteryTicket.create(details2, numbers2); + assertEquals(ticket1, ticket2); + PlayerDetails details3 = PlayerDetails.create("elsa@foo.bar", "1223-121212", "+49332322"); + LotteryNumbers numbers3 = LotteryNumbers.create(new HashSet(Arrays.asList(1, 2, 3, 8))); + LotteryTicket ticket3 = LotteryTicket.create(details3, numbers3); + assertFalse(ticket1.equals(ticket3)); + } +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/PlayerDetailsTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/PlayerDetailsTest.java index 73faa9a94..813b035a2 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/PlayerDetailsTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/PlayerDetailsTest.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.hexagonal.domain; import static org.junit.Assert.assertEquals; From 804ffc3ea78f84f40355a28122320beb438c6909 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 20 Mar 2016 10:49:31 +0200 Subject: [PATCH 07/35] Added lottery service interface --- .../hexagonal/domain/LotteryService.java | 35 ++++++ .../domain/LotteryTicketSubmitResult.java | 115 ++++++++++++++++++ .../domain/LotteryTicketSubmitResultTest.java | 51 ++++++++ 3 files changed, 201 insertions(+) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketSubmitResult.java create mode 100644 hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketSubmitResultTest.java diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java new file mode 100644 index 000000000..93f446d2b --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java @@ -0,0 +1,35 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.domain; + +/** + * + * Interface for submitting and checking lottery tickets. + * + */ +public interface LotteryService { + + LotteryTicketSubmitResult submitTicket(LotteryTicket ticket); + + void checkTicketForPrize(LotteryTicket ticket); +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketSubmitResult.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketSubmitResult.java new file mode 100644 index 000000000..a7a6fc148 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketSubmitResult.java @@ -0,0 +1,115 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.domain; + +import java.util.EnumSet; + +/** + * + * Immutable value object for passing lottery ticket submit results. + * + */ +public class LotteryTicketSubmitResult { + + /** + * + * Indicates if the submit was successful + * + */ + public enum Result {OK, ERROR}; + + /** + * + * Indicates the fields in the lottery ticket that have errors. + * + */ + public enum Fields {EMAIL, BANK_ACCOUNT, PHONE, NUMBERS}; + + private final Result result; + + private final EnumSet fields; + + /** + * Constructor. + */ + public LotteryTicketSubmitResult(Result submitResult) { + result = submitResult; + fields = EnumSet.noneOf(Fields.class); + } + + /** + * Constructor. + */ + public LotteryTicketSubmitResult(Result submitResult, EnumSet errorFields) { + result = submitResult; + fields = EnumSet.copyOf(errorFields); + } + + /** + * @return submit result + */ + public Result getResult() { + return result; + } + + /** + * @return fields that have errors + */ + public EnumSet getErrorFields() { + return EnumSet.copyOf(fields); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((fields == null) ? 0 : fields.hashCode()); + result = prime * result + ((this.result == null) ? 0 : this.result.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + LotteryTicketSubmitResult other = (LotteryTicketSubmitResult) obj; + if (fields == null) { + if (other.fields != null) { + return false; + } + } else if (!fields.equals(other.fields)) { + return false; + } + if (result != other.result) { + return false; + } + return true; + } +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketSubmitResultTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketSubmitResultTest.java new file mode 100644 index 000000000..697e32f69 --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketSubmitResultTest.java @@ -0,0 +1,51 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.domain; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import java.util.EnumSet; + +import org.junit.Test; + +import com.iluwatar.hexagonal.domain.LotteryTicketSubmitResult.Fields; +import com.iluwatar.hexagonal.domain.LotteryTicketSubmitResult.Result; + +/** + * + * Unit tests for {@link LotteryTicketSubmitResult} + * + */ +public class LotteryTicketSubmitResultTest { + + @Test + public void testEquals() { + LotteryTicketSubmitResult submitResult1 = new LotteryTicketSubmitResult(Result.OK); + LotteryTicketSubmitResult submitResult2 = new LotteryTicketSubmitResult(Result.OK); + assertEquals(submitResult1, submitResult2); + LotteryTicketSubmitResult submitResult3 = new LotteryTicketSubmitResult( + Result.ERROR, EnumSet.of(Fields.EMAIL, Fields.BANK_ACCOUNT)); + assertFalse(submitResult1.equals(submitResult3)); + } +} From 2785f6915b9d7ee90f3de3f4b3918c3c65ff9f56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 20 Mar 2016 11:10:06 +0200 Subject: [PATCH 08/35] Add lottery ticket check result --- .../hexagonal/domain/LotteryService.java | 2 +- .../domain/LotteryTicketCheckResult.java | 97 +++++++++++++++++++ .../domain/LotteryTicketCheckResultTest.java | 47 +++++++++ 3 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResult.java create mode 100644 hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResultTest.java diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java index 93f446d2b..cf07d769e 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java @@ -31,5 +31,5 @@ public interface LotteryService { LotteryTicketSubmitResult submitTicket(LotteryTicket ticket); - void checkTicketForPrize(LotteryTicket ticket); + LotteryTicketCheckResult checkTicketForPrize(LotteryTicket ticket); } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResult.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResult.java new file mode 100644 index 000000000..d36dd28b7 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResult.java @@ -0,0 +1,97 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.domain; + +/** + * + * Represents lottery ticket check result. + * + */ +public class LotteryTicketCheckResult { + + public enum CheckResult {WIN_PRIZE, NO_PRIZE, TICKET_NOT_SUBMITTED}; + + private final CheckResult checkResult; + + private final int prizeAmount; + + /** + * Constructor. + */ + public LotteryTicketCheckResult(CheckResult result) { + checkResult = result; + prizeAmount = 0; + } + + /** + * Constructor. + */ + public LotteryTicketCheckResult(CheckResult result, int amount) { + checkResult = result; + prizeAmount = amount; + } + + /** + * @return check result + */ + public CheckResult getResult() { + return checkResult; + } + + /** + * @return prize amount + */ + public int getPrizeAmount() { + return prizeAmount; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((checkResult == null) ? 0 : checkResult.hashCode()); + result = prime * result + prizeAmount; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + LotteryTicketCheckResult other = (LotteryTicketCheckResult) obj; + if (checkResult != other.checkResult) { + return false; + } + if (prizeAmount != other.prizeAmount) { + return false; + } + return true; + } +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResultTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResultTest.java new file mode 100644 index 000000000..237ab85d3 --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResultTest.java @@ -0,0 +1,47 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.domain; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import org.junit.Test; + +import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult.CheckResult; + +/** + * + * Unit tests for {@link LotteryTicketCheckResult} + * + */ +public class LotteryTicketCheckResultTest { + + @Test + public void testEquals() { + LotteryTicketCheckResult result1 = new LotteryTicketCheckResult(CheckResult.NO_PRIZE); + LotteryTicketCheckResult result2 = new LotteryTicketCheckResult(CheckResult.NO_PRIZE); + assertEquals(result1, result2); + LotteryTicketCheckResult result3 = new LotteryTicketCheckResult(CheckResult.WIN_PRIZE, 300000); + assertFalse(result1.equals(result3)); + } +} From 95e6a6705670a482d706dc64fef96906f5a1cc71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 20 Mar 2016 14:09:44 +0200 Subject: [PATCH 09/35] Added interface for accessing lottery tickets in database --- .../domain/LotteryTicketRepository.java | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketRepository.java diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketRepository.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketRepository.java new file mode 100644 index 000000000..99d2ab8f0 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketRepository.java @@ -0,0 +1,40 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.domain; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +/** + * + * Interface for accessing lottery tickets in database. + * + */ +public interface LotteryTicketRepository { + + Optional findByUuid(UUID uuid); + Optional save(LotteryTicket ticket); + List findAll(); + +} From 15d2b9dccaa0e23b1bd1720bfee7cc2b102e846d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 20 Mar 2016 14:26:43 +0200 Subject: [PATCH 10/35] Added interface to lottery service provider's bank account --- .../hexagonal/domain/WireTransfers.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/domain/WireTransfers.java diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/WireTransfers.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/WireTransfers.java new file mode 100644 index 000000000..d9463f19d --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/WireTransfers.java @@ -0,0 +1,35 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.domain; + +/** + * + * Interface to the lottery service provider's bank account. + * + */ +public interface WireTransfers { + + int getCurrentFundsAmount(); + boolean transferFunds(int amount, String receiverBankAccountNumber); + +} From e8671e1d6ef5e4a1cbaf9d4cadb36bc383dfe32a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 20 Mar 2016 14:33:41 +0200 Subject: [PATCH 11/35] Add interface for lottery event notifications --- .../domain/LotteryNotifications.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNotifications.java diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNotifications.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNotifications.java new file mode 100644 index 000000000..626525ebc --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNotifications.java @@ -0,0 +1,36 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.domain; + +/** + * + * Provides notifications for lottery events. + * + */ +public interface LotteryNotifications { + + void notifyTicketSubmitted(PlayerDetails details); + void notifyNoWin(PlayerDetails details); + void notifyPrize(PlayerDetails details); + +} From fde33999b7b95198ad603c64f2ee537ca6f8f7fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 20 Mar 2016 14:38:24 +0200 Subject: [PATCH 12/35] Added interface for lottery administrators --- .../domain/LotteryAdministration.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java new file mode 100644 index 000000000..0f0336e53 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java @@ -0,0 +1,37 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.domain; + +import java.util.List; + +/** + * + * Administrator interface for lottery service. + * + */ +public interface LotteryAdministration { + + List getAllSubmittedTickets(); + List performLottery(); + +} From 8b147c4dd9c2c7016b04aa67f8a4e74b18ac94a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 20 Mar 2016 20:56:04 +0200 Subject: [PATCH 13/35] Add mock database for lottery tickets --- .../database/LotteryTicketRepositoryMock.java | 63 +++++++++++++++++++ .../domain/LotteryTicketRepository.java | 4 +- 2 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryMock.java diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryMock.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryMock.java new file mode 100644 index 000000000..9c1e9fb41 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryMock.java @@ -0,0 +1,63 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.database; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.LotteryTicketRepository; + +/** + * + * Mock database for lottery tickets. + * + */ +public class LotteryTicketRepositoryMock implements LotteryTicketRepository { + + private Map tickets = new HashMap<>(); + + @Override + public Optional findByUuid(UUID uuid) { + LotteryTicket ticket = tickets.get(uuid); + if (ticket == null) { + return Optional.empty(); + } else { + return Optional.of(ticket); + } + } + + @Override + public Optional save(LotteryTicket ticket) { + UUID uuid = UUID.randomUUID(); + tickets.put(uuid, ticket); + return Optional.of(uuid); + } + + @Override + public Map findAll() { + return tickets; + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketRepository.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketRepository.java index 99d2ab8f0..cbd5dcf93 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketRepository.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketRepository.java @@ -22,7 +22,7 @@ */ package com.iluwatar.hexagonal.domain; -import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.UUID; @@ -35,6 +35,6 @@ public interface LotteryTicketRepository { Optional findByUuid(UUID uuid); Optional save(LotteryTicket ticket); - List findAll(); + Map findAll(); } From d2f620a5e62f187823629414286f141e45b91c98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Tue, 22 Mar 2016 21:27:13 +0200 Subject: [PATCH 14/35] Added tests for lottery ticket repository --- .../domain/LotteryTicketRepositoryTest.java | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketRepositoryTest.java diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketRepositoryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketRepositoryTest.java new file mode 100644 index 000000000..10394f4ca --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketRepositoryTest.java @@ -0,0 +1,61 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.domain; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Optional; +import java.util.UUID; + +import org.junit.Test; + +import com.iluwatar.hexagonal.database.LotteryTicketRepositoryMock; + +/** + * + * Tests for {@link LotteryTicketRepository} + * + */ +public class LotteryTicketRepositoryTest { + + @Test + public void testCrudOperations() { + LotteryTicketRepository repository = new LotteryTicketRepositoryMock(); + assertEquals(repository.findAll().size(), 0); + LotteryTicket ticket = createLotteryTicket(); + Optional uuid = repository.save(ticket); + assertTrue(uuid.isPresent()); + assertEquals(repository.findAll().size(), 1); + Optional optionalTicket = repository.findByUuid(uuid.get()); + assertTrue(optionalTicket.isPresent()); + } + + private LotteryTicket createLotteryTicket() { + PlayerDetails details = PlayerDetails.create("foo@bar.com", "12231-213132", "+99324554"); + LotteryNumbers numbers = LotteryNumbers.create(new HashSet<>(Arrays.asList(1, 2, 3, 4))); + return LotteryTicket.create(details, numbers); + } +} From 266b658ab5c103ec15f9c6a5c49163129387fbf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Tue, 22 Mar 2016 22:45:25 +0200 Subject: [PATCH 15/35] Refactor interfaces --- .../database/LotteryTicketRepositoryMock.java | 18 +++--- .../domain/LotteryAdministration.java | 2 +- .../hexagonal/domain/LotteryService.java | 2 +- .../hexagonal/domain/LotteryServiceImpl.java | 62 +++++++++++++++++++ .../hexagonal/domain/LotteryTicketId.java | 38 ++++++++++++ .../domain/LotteryTicketRepository.java | 7 +-- .../domain/LotteryTicketRepositoryTest.java | 6 +- 7 files changed, 117 insertions(+), 18 deletions(-) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryServiceImpl.java create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryMock.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryMock.java index 9c1e9fb41..4b9ff95d9 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryMock.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryMock.java @@ -25,9 +25,9 @@ package com.iluwatar.hexagonal.database; import java.util.HashMap; import java.util.Map; import java.util.Optional; -import java.util.UUID; import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.LotteryTicketId; import com.iluwatar.hexagonal.domain.LotteryTicketRepository; /** @@ -37,11 +37,11 @@ import com.iluwatar.hexagonal.domain.LotteryTicketRepository; */ public class LotteryTicketRepositoryMock implements LotteryTicketRepository { - private Map tickets = new HashMap<>(); + private Map tickets = new HashMap<>(); @Override - public Optional findByUuid(UUID uuid) { - LotteryTicket ticket = tickets.get(uuid); + public Optional findById(LotteryTicketId id) { + LotteryTicket ticket = tickets.get(id); if (ticket == null) { return Optional.empty(); } else { @@ -50,14 +50,14 @@ public class LotteryTicketRepositoryMock implements LotteryTicketRepository { } @Override - public Optional save(LotteryTicket ticket) { - UUID uuid = UUID.randomUUID(); - tickets.put(uuid, ticket); - return Optional.of(uuid); + public Optional save(LotteryTicket ticket) { + LotteryTicketId id = new LotteryTicketId(); + tickets.put(id, ticket); + return Optional.of(id); } @Override - public Map findAll() { + public Map findAll() { return tickets; } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java index 0f0336e53..a68ea73b0 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java @@ -32,6 +32,6 @@ import java.util.List; public interface LotteryAdministration { List getAllSubmittedTickets(); - List performLottery(); + LotteryNumbers performLottery(); } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java index cf07d769e..324b965f4 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java @@ -31,5 +31,5 @@ public interface LotteryService { LotteryTicketSubmitResult submitTicket(LotteryTicket ticket); - LotteryTicketCheckResult checkTicketForPrize(LotteryTicket ticket); + LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers); } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryServiceImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryServiceImpl.java new file mode 100644 index 000000000..9ef6f8007 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryServiceImpl.java @@ -0,0 +1,62 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.domain; + +import java.util.Optional; + +import com.iluwatar.hexagonal.database.LotteryTicketRepositoryMock; +import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult.CheckResult; +import com.iluwatar.hexagonal.domain.LotteryTicketSubmitResult.Result; + +public class LotteryServiceImpl implements LotteryService { + + private final LotteryTicketRepository repository; + + public LotteryServiceImpl() { + repository = new LotteryTicketRepositoryMock(); + } + + @Override + public LotteryTicketSubmitResult submitTicket(LotteryTicket ticket) { + Optional optional = repository.save(ticket); + Result result = Result.OK; + if (!optional.isPresent()) { + result = Result.ERROR; + } + return new LotteryTicketSubmitResult(result); + } + + @Override + public LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers) { + Optional optional = repository.findById(id); + if (optional.isPresent()) { + if (optional.get().getNumbers().equals(winningNumbers)) { + return new LotteryTicketCheckResult(CheckResult.WIN_PRIZE); + } else { + return new LotteryTicketCheckResult(CheckResult.NO_PRIZE); + } + } else { + return new LotteryTicketCheckResult(CheckResult.TICKET_NOT_SUBMITTED); + } + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java new file mode 100644 index 000000000..358bdd05a --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java @@ -0,0 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.domain; + +import java.util.UUID; + +public class LotteryTicketId { + + private final UUID id; + + public LotteryTicketId() { + id = UUID.randomUUID(); + } + + public UUID getId() { + return id; + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketRepository.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketRepository.java index cbd5dcf93..45e4a6f61 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketRepository.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketRepository.java @@ -24,7 +24,6 @@ package com.iluwatar.hexagonal.domain; import java.util.Map; import java.util.Optional; -import java.util.UUID; /** * @@ -33,8 +32,8 @@ import java.util.UUID; */ public interface LotteryTicketRepository { - Optional findByUuid(UUID uuid); - Optional save(LotteryTicket ticket); - Map findAll(); + Optional findById(LotteryTicketId id); + Optional save(LotteryTicket ticket); + Map findAll(); } diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketRepositoryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketRepositoryTest.java index 10394f4ca..b4ac6691e 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketRepositoryTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketRepositoryTest.java @@ -46,10 +46,10 @@ public class LotteryTicketRepositoryTest { LotteryTicketRepository repository = new LotteryTicketRepositoryMock(); assertEquals(repository.findAll().size(), 0); LotteryTicket ticket = createLotteryTicket(); - Optional uuid = repository.save(ticket); - assertTrue(uuid.isPresent()); + Optional id = repository.save(ticket); + assertTrue(id.isPresent()); assertEquals(repository.findAll().size(), 1); - Optional optionalTicket = repository.findByUuid(uuid.get()); + Optional optionalTicket = repository.findById(id.get()); assertTrue(optionalTicket.isPresent()); } From 8d6bc522cdaedcde3f66ddce05c06e236061ce9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 26 Mar 2016 11:03:40 +0200 Subject: [PATCH 16/35] Simplify lottery ticket submit --- .../hexagonal/domain/LotteryService.java | 4 +- .../hexagonal/domain/LotteryServiceImpl.java | 10 +- .../domain/LotteryTicketSubmitResult.java | 115 ------------------ .../domain/LotteryTicketSubmitResultTest.java | 51 -------- 4 files changed, 5 insertions(+), 175 deletions(-) delete mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketSubmitResult.java delete mode 100644 hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketSubmitResultTest.java diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java index 324b965f4..369f64966 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java @@ -22,6 +22,8 @@ */ package com.iluwatar.hexagonal.domain; +import java.util.Optional; + /** * * Interface for submitting and checking lottery tickets. @@ -29,7 +31,7 @@ package com.iluwatar.hexagonal.domain; */ public interface LotteryService { - LotteryTicketSubmitResult submitTicket(LotteryTicket ticket); + Optional submitTicket(LotteryTicket ticket); LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers); } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryServiceImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryServiceImpl.java index 9ef6f8007..d74ba240f 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryServiceImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryServiceImpl.java @@ -26,7 +26,6 @@ import java.util.Optional; import com.iluwatar.hexagonal.database.LotteryTicketRepositoryMock; import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult.CheckResult; -import com.iluwatar.hexagonal.domain.LotteryTicketSubmitResult.Result; public class LotteryServiceImpl implements LotteryService { @@ -37,13 +36,8 @@ public class LotteryServiceImpl implements LotteryService { } @Override - public LotteryTicketSubmitResult submitTicket(LotteryTicket ticket) { - Optional optional = repository.save(ticket); - Result result = Result.OK; - if (!optional.isPresent()) { - result = Result.ERROR; - } - return new LotteryTicketSubmitResult(result); + public Optional submitTicket(LotteryTicket ticket) { + return repository.save(ticket); } @Override diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketSubmitResult.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketSubmitResult.java deleted file mode 100644 index a7a6fc148..000000000 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketSubmitResult.java +++ /dev/null @@ -1,115 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2014 Ilkka Seppälä - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.iluwatar.hexagonal.domain; - -import java.util.EnumSet; - -/** - * - * Immutable value object for passing lottery ticket submit results. - * - */ -public class LotteryTicketSubmitResult { - - /** - * - * Indicates if the submit was successful - * - */ - public enum Result {OK, ERROR}; - - /** - * - * Indicates the fields in the lottery ticket that have errors. - * - */ - public enum Fields {EMAIL, BANK_ACCOUNT, PHONE, NUMBERS}; - - private final Result result; - - private final EnumSet fields; - - /** - * Constructor. - */ - public LotteryTicketSubmitResult(Result submitResult) { - result = submitResult; - fields = EnumSet.noneOf(Fields.class); - } - - /** - * Constructor. - */ - public LotteryTicketSubmitResult(Result submitResult, EnumSet errorFields) { - result = submitResult; - fields = EnumSet.copyOf(errorFields); - } - - /** - * @return submit result - */ - public Result getResult() { - return result; - } - - /** - * @return fields that have errors - */ - public EnumSet getErrorFields() { - return EnumSet.copyOf(fields); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((fields == null) ? 0 : fields.hashCode()); - result = prime * result + ((this.result == null) ? 0 : this.result.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - LotteryTicketSubmitResult other = (LotteryTicketSubmitResult) obj; - if (fields == null) { - if (other.fields != null) { - return false; - } - } else if (!fields.equals(other.fields)) { - return false; - } - if (result != other.result) { - return false; - } - return true; - } -} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketSubmitResultTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketSubmitResultTest.java deleted file mode 100644 index 697e32f69..000000000 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketSubmitResultTest.java +++ /dev/null @@ -1,51 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2014 Ilkka Seppälä - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.iluwatar.hexagonal.domain; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; - -import java.util.EnumSet; - -import org.junit.Test; - -import com.iluwatar.hexagonal.domain.LotteryTicketSubmitResult.Fields; -import com.iluwatar.hexagonal.domain.LotteryTicketSubmitResult.Result; - -/** - * - * Unit tests for {@link LotteryTicketSubmitResult} - * - */ -public class LotteryTicketSubmitResultTest { - - @Test - public void testEquals() { - LotteryTicketSubmitResult submitResult1 = new LotteryTicketSubmitResult(Result.OK); - LotteryTicketSubmitResult submitResult2 = new LotteryTicketSubmitResult(Result.OK); - assertEquals(submitResult1, submitResult2); - LotteryTicketSubmitResult submitResult3 = new LotteryTicketSubmitResult( - Result.ERROR, EnumSet.of(Fields.EMAIL, Fields.BANK_ACCOUNT)); - assertFalse(submitResult1.equals(submitResult3)); - } -} From 656b599687616e97804bdadba123aefa9d1e8195 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 26 Mar 2016 12:28:31 +0200 Subject: [PATCH 17/35] Refactor to test utils --- .../hexagonal/domain/LotteryTestUtils.java | 18 ++++++++++++++++++ .../domain/LotteryTicketRepositoryTest.java | 11 +---------- 2 files changed, 19 insertions(+), 10 deletions(-) create mode 100644 hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTestUtils.java diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTestUtils.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTestUtils.java new file mode 100644 index 000000000..21a25e4f0 --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTestUtils.java @@ -0,0 +1,18 @@ +package com.iluwatar.hexagonal.domain; + +import java.util.Arrays; +import java.util.HashSet; + +/** + * + * Utilities for lottery tests + * + */ +public class LotteryTestUtils { + + public static LotteryTicket createLotteryTicket() { + PlayerDetails details = PlayerDetails.create("foo@bar.com", "12231-213132", "+99324554"); + LotteryNumbers numbers = LotteryNumbers.create(new HashSet<>(Arrays.asList(1, 2, 3, 4))); + return LotteryTicket.create(details, numbers); + } +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketRepositoryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketRepositoryTest.java index b4ac6691e..02308b261 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketRepositoryTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketRepositoryTest.java @@ -25,10 +25,7 @@ package com.iluwatar.hexagonal.domain; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import java.util.Arrays; -import java.util.HashSet; import java.util.Optional; -import java.util.UUID; import org.junit.Test; @@ -45,17 +42,11 @@ public class LotteryTicketRepositoryTest { public void testCrudOperations() { LotteryTicketRepository repository = new LotteryTicketRepositoryMock(); assertEquals(repository.findAll().size(), 0); - LotteryTicket ticket = createLotteryTicket(); + LotteryTicket ticket = LotteryTestUtils.createLotteryTicket(); Optional id = repository.save(ticket); assertTrue(id.isPresent()); assertEquals(repository.findAll().size(), 1); Optional optionalTicket = repository.findById(id.get()); assertTrue(optionalTicket.isPresent()); } - - private LotteryTicket createLotteryTicket() { - PlayerDetails details = PlayerDetails.create("foo@bar.com", "12231-213132", "+99324554"); - LotteryNumbers numbers = LotteryNumbers.create(new HashSet<>(Arrays.asList(1, 2, 3, 4))); - return LotteryTicket.create(details, numbers); - } } From 39a1c1e95673e1050a50021be9b3cc6fe83f0a32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 26 Mar 2016 12:28:51 +0200 Subject: [PATCH 18/35] Implement admin interface --- .../main/java/com/iluwatar/hexagonal/App.java | 9 ++++- .../domain/LotteryAdministration.java | 4 +- .../domain/LotteryAdministrationImpl.java | 29 ++++++++++++++ .../hexagonal/domain/LotteryServiceImpl.java | 7 +++- .../domain/LotteryAdministrationTest.java | 38 +++++++++++++++++++ 5 files changed, 83 insertions(+), 4 deletions(-) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministrationImpl.java create mode 100644 hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryAdministrationTest.java diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java index b32564cbc..5554c36df 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java @@ -28,7 +28,14 @@ package com.iluwatar.hexagonal; * */ public class App { + /** + * Program entry point + */ public static void main(String[] args) { - System.out.println("Running hexagonal example"); + // submit some lottery tickets + + // perform lottery + + // check all the tickets } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java index a68ea73b0..58edd9b28 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java @@ -22,7 +22,7 @@ */ package com.iluwatar.hexagonal.domain; -import java.util.List; +import java.util.Map; /** * @@ -31,7 +31,7 @@ import java.util.List; */ public interface LotteryAdministration { - List getAllSubmittedTickets(); + Map getAllSubmittedTickets(); LotteryNumbers performLottery(); } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministrationImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministrationImpl.java new file mode 100644 index 000000000..ee1d56f45 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministrationImpl.java @@ -0,0 +1,29 @@ +package com.iluwatar.hexagonal.domain; + +import java.util.Map; + +import com.iluwatar.hexagonal.database.LotteryTicketRepositoryMock; + +/** + * + * Lottery administration implementation + * + */ +public class LotteryAdministrationImpl implements LotteryAdministration { + + private final LotteryTicketRepository repository; + + public LotteryAdministrationImpl() { + repository = new LotteryTicketRepositoryMock(); + } + + @Override + public Map getAllSubmittedTickets() { + return repository.findAll(); + } + + @Override + public LotteryNumbers performLottery() { + return LotteryNumbers.createRandom(); + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryServiceImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryServiceImpl.java index d74ba240f..2f5191601 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryServiceImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryServiceImpl.java @@ -27,6 +27,11 @@ import java.util.Optional; import com.iluwatar.hexagonal.database.LotteryTicketRepositoryMock; import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult.CheckResult; +/** + * + * Implementation for lottery service + * + */ public class LotteryServiceImpl implements LotteryService { private final LotteryTicketRepository repository; @@ -45,7 +50,7 @@ public class LotteryServiceImpl implements LotteryService { Optional optional = repository.findById(id); if (optional.isPresent()) { if (optional.get().getNumbers().equals(winningNumbers)) { - return new LotteryTicketCheckResult(CheckResult.WIN_PRIZE); + return new LotteryTicketCheckResult(CheckResult.WIN_PRIZE, 1000); } else { return new LotteryTicketCheckResult(CheckResult.NO_PRIZE); } diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryAdministrationTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryAdministrationTest.java new file mode 100644 index 000000000..be953a4e5 --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryAdministrationTest.java @@ -0,0 +1,38 @@ +package com.iluwatar.hexagonal.domain; + +import static org.junit.Assert.assertEquals; + +import org.junit.Before; +import org.junit.Test; + +import com.iluwatar.hexagonal.database.LotteryTicketRepositoryMock; + +/** + * + * Tests for lottery administration + * + */ +public class LotteryAdministrationTest { + + private LotteryTicketRepository repository = new LotteryTicketRepositoryMock(); + private LotteryAdministration admin = new LotteryAdministrationImpl(); + + @Before + public void submitTickets() { + repository.save(LotteryTestUtils.createLotteryTicket()); + repository.save(LotteryTestUtils.createLotteryTicket()); + repository.save(LotteryTestUtils.createLotteryTicket()); + repository.save(LotteryTestUtils.createLotteryTicket()); + repository.save(LotteryTestUtils.createLotteryTicket()); + } + + @Test + public void testGetAllTickets() { + assertEquals(admin.getAllSubmittedTickets().size(), 4); + } + + @Test + public void testPerformLottery() { + assertEquals(admin.performLottery().getNumbers().size(), 4); + } +} From 4bb7ddaec1b44fc5b3d2af94ad5143fcd445fa6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 26 Mar 2016 12:42:23 +0200 Subject: [PATCH 19/35] Work on admin tests --- .../database/LotteryTicketRepositoryMock.java | 7 ++++- .../domain/LotteryAdministration.java | 1 + .../domain/LotteryAdministrationImpl.java | 27 +++++++++++++++++++ .../domain/LotteryTicketRepository.java | 1 + .../domain/LotteryAdministrationTest.java | 25 ++++++++++++++++- .../hexagonal/domain/LotteryTestUtils.java | 25 +++++++++++++++++ .../domain/LotteryTicketRepositoryTest.java | 8 ++++++ 7 files changed, 92 insertions(+), 2 deletions(-) diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryMock.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryMock.java index 4b9ff95d9..f297edeea 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryMock.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryMock.java @@ -37,7 +37,7 @@ import com.iluwatar.hexagonal.domain.LotteryTicketRepository; */ public class LotteryTicketRepositoryMock implements LotteryTicketRepository { - private Map tickets = new HashMap<>(); + private static Map tickets = new HashMap<>(); @Override public Optional findById(LotteryTicketId id) { @@ -60,4 +60,9 @@ public class LotteryTicketRepositoryMock implements LotteryTicketRepository { public Map findAll() { return tickets; } + + @Override + public void deleteAll() { + tickets.clear(); + } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java index 58edd9b28..af3608d00 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java @@ -33,5 +33,6 @@ public interface LotteryAdministration { Map getAllSubmittedTickets(); LotteryNumbers performLottery(); + void resetLottery(); } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministrationImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministrationImpl.java index ee1d56f45..c4983de4a 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministrationImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministrationImpl.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.hexagonal.domain; import java.util.Map; @@ -26,4 +48,9 @@ public class LotteryAdministrationImpl implements LotteryAdministration { public LotteryNumbers performLottery() { return LotteryNumbers.createRandom(); } + + @Override + public void resetLottery() { + repository.deleteAll(); + } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketRepository.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketRepository.java index 45e4a6f61..b39f2e80d 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketRepository.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketRepository.java @@ -35,5 +35,6 @@ public interface LotteryTicketRepository { Optional findById(LotteryTicketId id); Optional save(LotteryTicket ticket); Map findAll(); + void deleteAll(); } diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryAdministrationTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryAdministrationTest.java index be953a4e5..eabeecfa5 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryAdministrationTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryAdministrationTest.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.hexagonal.domain; import static org.junit.Assert.assertEquals; @@ -19,6 +41,7 @@ public class LotteryAdministrationTest { @Before public void submitTickets() { + repository.deleteAll(); repository.save(LotteryTestUtils.createLotteryTicket()); repository.save(LotteryTestUtils.createLotteryTicket()); repository.save(LotteryTestUtils.createLotteryTicket()); @@ -28,7 +51,7 @@ public class LotteryAdministrationTest { @Test public void testGetAllTickets() { - assertEquals(admin.getAllSubmittedTickets().size(), 4); + assertEquals(admin.getAllSubmittedTickets().size(), 5); } @Test diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTestUtils.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTestUtils.java index 21a25e4f0..854230116 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTestUtils.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTestUtils.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.hexagonal.domain; import java.util.Arrays; @@ -10,6 +32,9 @@ import java.util.HashSet; */ public class LotteryTestUtils { + /** + * @return lottery ticket + */ public static LotteryTicket createLotteryTicket() { PlayerDetails details = PlayerDetails.create("foo@bar.com", "12231-213132", "+99324554"); LotteryNumbers numbers = LotteryNumbers.create(new HashSet<>(Arrays.asList(1, 2, 3, 4))); diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketRepositoryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketRepositoryTest.java index 02308b261..f752d7f84 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketRepositoryTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketRepositoryTest.java @@ -27,6 +27,7 @@ import static org.junit.Assert.assertTrue; import java.util.Optional; +import org.junit.Before; import org.junit.Test; import com.iluwatar.hexagonal.database.LotteryTicketRepositoryMock; @@ -38,6 +39,13 @@ import com.iluwatar.hexagonal.database.LotteryTicketRepositoryMock; */ public class LotteryTicketRepositoryTest { + private final LotteryTicketRepository repository = new LotteryTicketRepositoryMock(); + + @Before + public void clear() { + repository.deleteAll(); + } + @Test public void testCrudOperations() { LotteryTicketRepository repository = new LotteryTicketRepositoryMock(); From ac468bb7e727a56b313992f9d0eb6dfd3ab116ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 26 Mar 2016 15:54:40 +0200 Subject: [PATCH 20/35] Added high level lottery test --- .../LotteryTicketRepositoryMock.java | 2 +- .../domain/LotteryAdministrationImpl.java | 2 +- .../hexagonal/domain/LotteryServiceImpl.java | 2 +- .../domain/LotteryAdministrationTest.java | 2 +- .../hexagonal/domain/LotteryTest.java | 100 ++++++++++++++++++ .../hexagonal/domain/LotteryTestUtils.java | 13 ++- .../domain/LotteryTicketRepositoryTest.java | 2 +- 7 files changed, 116 insertions(+), 7 deletions(-) rename hexagonal/src/main/java/com/iluwatar/hexagonal/{database => adapter}/LotteryTicketRepositoryMock.java (98%) create mode 100644 hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryMock.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/adapter/LotteryTicketRepositoryMock.java similarity index 98% rename from hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryMock.java rename to hexagonal/src/main/java/com/iluwatar/hexagonal/adapter/LotteryTicketRepositoryMock.java index f297edeea..ba1cc69b9 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryMock.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/adapter/LotteryTicketRepositoryMock.java @@ -20,7 +20,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.iluwatar.hexagonal.database; +package com.iluwatar.hexagonal.adapter; import java.util.HashMap; import java.util.Map; diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministrationImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministrationImpl.java index c4983de4a..30d4e7b8b 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministrationImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministrationImpl.java @@ -24,7 +24,7 @@ package com.iluwatar.hexagonal.domain; import java.util.Map; -import com.iluwatar.hexagonal.database.LotteryTicketRepositoryMock; +import com.iluwatar.hexagonal.adapter.LotteryTicketRepositoryMock; /** * diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryServiceImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryServiceImpl.java index 2f5191601..4774ea3be 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryServiceImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryServiceImpl.java @@ -24,7 +24,7 @@ package com.iluwatar.hexagonal.domain; import java.util.Optional; -import com.iluwatar.hexagonal.database.LotteryTicketRepositoryMock; +import com.iluwatar.hexagonal.adapter.LotteryTicketRepositoryMock; import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult.CheckResult; /** diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryAdministrationTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryAdministrationTest.java index eabeecfa5..0bcb34048 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryAdministrationTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryAdministrationTest.java @@ -27,7 +27,7 @@ import static org.junit.Assert.assertEquals; import org.junit.Before; import org.junit.Test; -import com.iluwatar.hexagonal.database.LotteryTicketRepositoryMock; +import com.iluwatar.hexagonal.adapter.LotteryTicketRepositoryMock; /** * diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java new file mode 100644 index 000000000..6135994ea --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java @@ -0,0 +1,100 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.domain; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; + +import org.junit.Before; +import org.junit.Test; + +import com.iluwatar.hexagonal.adapter.LotteryTicketRepositoryMock; +import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult.CheckResult; + +/** + * + * Test the lottery system + * + */ +public class LotteryTest { + + private final LotteryAdministration admin = new LotteryAdministrationImpl(); + private final LotteryService service = new LotteryServiceImpl(); + private final LotteryTicketRepository repository = new LotteryTicketRepositoryMock(); + + @Before + public void clear() { + repository.deleteAll(); + } + + @Test + public void testLottery() { + + // admin resets the lottery + admin.resetLottery(); + assertEquals(admin.getAllSubmittedTickets().size(), 0); + + // players submit the lottery tickets + Optional ticket1 = service.submitTicket(LotteryTestUtils.createLotteryTicket("cvt@bbb.com", + "123-12312", "+32425255", new HashSet<>(Arrays.asList(1, 2, 3, 4)))); + assertTrue(ticket1.isPresent()); + Optional ticket2 = service.submitTicket(LotteryTestUtils.createLotteryTicket("ant@bac.com", + "123-12345", "+32423455", new HashSet<>(Arrays.asList(11, 12, 13, 14)))); + assertTrue(ticket2.isPresent()); + Optional ticket3 = service.submitTicket(LotteryTestUtils.createLotteryTicket("arg@boo.com", + "123-12367", "+32421255", new HashSet<>(Arrays.asList(6, 8, 13, 19)))); + assertTrue(ticket3.isPresent()); + assertEquals(admin.getAllSubmittedTickets().size(), 3); + + // perform lottery + LotteryNumbers winningNumbers = admin.performLottery(); + + // cheat a bit for testing sake, use winning numbers to submit another ticket + Optional ticket4 = service.submitTicket(LotteryTestUtils.createLotteryTicket("lucky@orb.com", + "123-12399", "+12421255", winningNumbers.getNumbers())); + assertTrue(ticket4.isPresent()); + assertEquals(admin.getAllSubmittedTickets().size(), 4); + + // check winners + Map tickets = admin.getAllSubmittedTickets(); + for (LotteryTicketId id: tickets.keySet()) { + LotteryTicketCheckResult checkResult = service.checkTicketForPrize(id, winningNumbers); + assertTrue(checkResult.getResult() != CheckResult.TICKET_NOT_SUBMITTED); + if (checkResult.getResult().equals(CheckResult.WIN_PRIZE)) { + assertTrue(checkResult.getPrizeAmount() > 0); + } else if (checkResult.getResult().equals(CheckResult.WIN_PRIZE)) { + assertEquals(checkResult.getPrizeAmount(), 0); + } + } + + // check another ticket that has not been submitted + LotteryTicketCheckResult checkResult = service.checkTicketForPrize(new LotteryTicketId(), winningNumbers); + assertTrue(checkResult.getResult() == CheckResult.TICKET_NOT_SUBMITTED); + assertEquals(checkResult.getPrizeAmount(), 0); + } +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTestUtils.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTestUtils.java index 854230116..4a38ff3a2 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTestUtils.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTestUtils.java @@ -24,6 +24,7 @@ package com.iluwatar.hexagonal.domain; import java.util.Arrays; import java.util.HashSet; +import java.util.Set; /** * @@ -36,8 +37,16 @@ public class LotteryTestUtils { * @return lottery ticket */ public static LotteryTicket createLotteryTicket() { - PlayerDetails details = PlayerDetails.create("foo@bar.com", "12231-213132", "+99324554"); - LotteryNumbers numbers = LotteryNumbers.create(new HashSet<>(Arrays.asList(1, 2, 3, 4))); + return createLotteryTicket("foo@bar.com", "12231-213132", "+99324554", new HashSet<>(Arrays.asList(1, 2, 3, 4))); + } + + /** + * @return lottery ticket + */ + public static LotteryTicket createLotteryTicket(String email, String account, String phone, + Set givenNumbers) { + PlayerDetails details = PlayerDetails.create(email, account, phone); + LotteryNumbers numbers = LotteryNumbers.create(givenNumbers); return LotteryTicket.create(details, numbers); } } diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketRepositoryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketRepositoryTest.java index f752d7f84..7bbff9e3f 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketRepositoryTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketRepositoryTest.java @@ -30,7 +30,7 @@ import java.util.Optional; import org.junit.Before; import org.junit.Test; -import com.iluwatar.hexagonal.database.LotteryTicketRepositoryMock; +import com.iluwatar.hexagonal.adapter.LotteryTicketRepositoryMock; /** * From f620123ceea1bc2847d1b2709c3d4fcf03a624bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 26 Mar 2016 21:22:49 +0200 Subject: [PATCH 21/35] Organize packages --- .../LotteryAdministration.java | 6 +- .../LotteryAdministrationImpl.java | 8 ++- .../{domain => banking}/WireTransfers.java | 2 +- .../LotteryTicketRepository.java | 5 +- .../LotteryTicketRepositoryMock.java | 3 +- .../LotteryNotifications.java | 4 +- .../{domain => service}/LotteryService.java | 7 ++- .../LotteryServiceImpl.java | 9 ++- .../LotteryTicketRepositoryTest.java | 8 ++- .../domain/LotteryAdministrationTest.java | 61 ------------------- .../{domain => lottery}/LotteryTest.java | 14 ++++- .../{domain => test}/LotteryTestUtils.java | 6 +- 12 files changed, 56 insertions(+), 77 deletions(-) rename hexagonal/src/main/java/com/iluwatar/hexagonal/{domain => administration}/LotteryAdministration.java (87%) rename hexagonal/src/main/java/com/iluwatar/hexagonal/{domain => administration}/LotteryAdministrationImpl.java (83%) rename hexagonal/src/main/java/com/iluwatar/hexagonal/{domain => banking}/WireTransfers.java (97%) rename hexagonal/src/main/java/com/iluwatar/hexagonal/{domain => database}/LotteryTicketRepository.java (90%) rename hexagonal/src/main/java/com/iluwatar/hexagonal/{adapter => database}/LotteryTicketRepositoryMock.java (95%) rename hexagonal/src/main/java/com/iluwatar/hexagonal/{domain => notifications}/LotteryNotifications.java (93%) rename hexagonal/src/main/java/com/iluwatar/hexagonal/{domain => service}/LotteryService.java (84%) rename hexagonal/src/main/java/com/iluwatar/hexagonal/{domain => service}/LotteryServiceImpl.java (84%) rename hexagonal/src/test/java/com/iluwatar/hexagonal/{domain => database}/LotteryTicketRepositoryTest.java (86%) delete mode 100644 hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryAdministrationTest.java rename hexagonal/src/test/java/com/iluwatar/hexagonal/{domain => lottery}/LotteryTest.java (85%) rename hexagonal/src/test/java/com/iluwatar/hexagonal/{domain => test}/LotteryTestUtils.java (90%) diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministration.java similarity index 87% rename from hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java rename to hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministration.java index af3608d00..70b625aa2 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministration.java @@ -20,10 +20,14 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.iluwatar.hexagonal.domain; +package com.iluwatar.hexagonal.administration; import java.util.Map; +import com.iluwatar.hexagonal.domain.LotteryNumbers; +import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.LotteryTicketId; + /** * * Administrator interface for lottery service. diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministrationImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java similarity index 83% rename from hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministrationImpl.java rename to hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java index 30d4e7b8b..3d0327fe1 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministrationImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java @@ -20,11 +20,15 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.iluwatar.hexagonal.domain; +package com.iluwatar.hexagonal.administration; import java.util.Map; -import com.iluwatar.hexagonal.adapter.LotteryTicketRepositoryMock; +import com.iluwatar.hexagonal.database.LotteryTicketRepository; +import com.iluwatar.hexagonal.database.LotteryTicketRepositoryMock; +import com.iluwatar.hexagonal.domain.LotteryNumbers; +import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.LotteryTicketId; /** * diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/WireTransfers.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/WireTransfers.java similarity index 97% rename from hexagonal/src/main/java/com/iluwatar/hexagonal/domain/WireTransfers.java rename to hexagonal/src/main/java/com/iluwatar/hexagonal/banking/WireTransfers.java index d9463f19d..0a3580db5 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/WireTransfers.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/WireTransfers.java @@ -20,7 +20,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.iluwatar.hexagonal.domain; +package com.iluwatar.hexagonal.banking; /** * diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketRepository.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepository.java similarity index 90% rename from hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketRepository.java rename to hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepository.java index b39f2e80d..a531440b4 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketRepository.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepository.java @@ -20,11 +20,14 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.iluwatar.hexagonal.domain; +package com.iluwatar.hexagonal.database; import java.util.Map; import java.util.Optional; +import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.LotteryTicketId; + /** * * Interface for accessing lottery tickets in database. diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/adapter/LotteryTicketRepositoryMock.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryMock.java similarity index 95% rename from hexagonal/src/main/java/com/iluwatar/hexagonal/adapter/LotteryTicketRepositoryMock.java rename to hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryMock.java index ba1cc69b9..0629fb6db 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/adapter/LotteryTicketRepositoryMock.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryMock.java @@ -20,7 +20,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.iluwatar.hexagonal.adapter; +package com.iluwatar.hexagonal.database; import java.util.HashMap; import java.util.Map; @@ -28,7 +28,6 @@ import java.util.Optional; import com.iluwatar.hexagonal.domain.LotteryTicket; import com.iluwatar.hexagonal.domain.LotteryTicketId; -import com.iluwatar.hexagonal.domain.LotteryTicketRepository; /** * diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNotifications.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotifications.java similarity index 93% rename from hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNotifications.java rename to hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotifications.java index 626525ebc..11cece62e 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNotifications.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotifications.java @@ -20,7 +20,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.iluwatar.hexagonal.domain; +package com.iluwatar.hexagonal.notifications; + +import com.iluwatar.hexagonal.domain.PlayerDetails; /** * diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryService.java similarity index 84% rename from hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java rename to hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryService.java index 369f64966..65270410a 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryService.java @@ -20,10 +20,15 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.iluwatar.hexagonal.domain; +package com.iluwatar.hexagonal.service; import java.util.Optional; +import com.iluwatar.hexagonal.domain.LotteryNumbers; +import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult; +import com.iluwatar.hexagonal.domain.LotteryTicketId; + /** * * Interface for submitting and checking lottery tickets. diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryServiceImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java similarity index 84% rename from hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryServiceImpl.java rename to hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java index 4774ea3be..af5b28f7a 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryServiceImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java @@ -20,11 +20,16 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.iluwatar.hexagonal.domain; +package com.iluwatar.hexagonal.service; import java.util.Optional; -import com.iluwatar.hexagonal.adapter.LotteryTicketRepositoryMock; +import com.iluwatar.hexagonal.database.LotteryTicketRepository; +import com.iluwatar.hexagonal.database.LotteryTicketRepositoryMock; +import com.iluwatar.hexagonal.domain.LotteryNumbers; +import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult; +import com.iluwatar.hexagonal.domain.LotteryTicketId; import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult.CheckResult; /** diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketRepositoryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryTest.java similarity index 86% rename from hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketRepositoryTest.java rename to hexagonal/src/test/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryTest.java index 7bbff9e3f..a80e4817a 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketRepositoryTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryTest.java @@ -20,7 +20,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.iluwatar.hexagonal.domain; +package com.iluwatar.hexagonal.database; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -30,7 +30,11 @@ import java.util.Optional; import org.junit.Before; import org.junit.Test; -import com.iluwatar.hexagonal.adapter.LotteryTicketRepositoryMock; +import com.iluwatar.hexagonal.database.LotteryTicketRepository; +import com.iluwatar.hexagonal.database.LotteryTicketRepositoryMock; +import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.LotteryTicketId; +import com.iluwatar.hexagonal.test.LotteryTestUtils; /** * diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryAdministrationTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryAdministrationTest.java deleted file mode 100644 index 0bcb34048..000000000 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryAdministrationTest.java +++ /dev/null @@ -1,61 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2014 Ilkka Seppälä - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.iluwatar.hexagonal.domain; - -import static org.junit.Assert.assertEquals; - -import org.junit.Before; -import org.junit.Test; - -import com.iluwatar.hexagonal.adapter.LotteryTicketRepositoryMock; - -/** - * - * Tests for lottery administration - * - */ -public class LotteryAdministrationTest { - - private LotteryTicketRepository repository = new LotteryTicketRepositoryMock(); - private LotteryAdministration admin = new LotteryAdministrationImpl(); - - @Before - public void submitTickets() { - repository.deleteAll(); - repository.save(LotteryTestUtils.createLotteryTicket()); - repository.save(LotteryTestUtils.createLotteryTicket()); - repository.save(LotteryTestUtils.createLotteryTicket()); - repository.save(LotteryTestUtils.createLotteryTicket()); - repository.save(LotteryTestUtils.createLotteryTicket()); - } - - @Test - public void testGetAllTickets() { - assertEquals(admin.getAllSubmittedTickets().size(), 5); - } - - @Test - public void testPerformLottery() { - assertEquals(admin.performLottery().getNumbers().size(), 4); - } -} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/lottery/LotteryTest.java similarity index 85% rename from hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java rename to hexagonal/src/test/java/com/iluwatar/hexagonal/lottery/LotteryTest.java index 6135994ea..1da92023d 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/lottery/LotteryTest.java @@ -20,7 +20,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.iluwatar.hexagonal.domain; +package com.iluwatar.hexagonal.lottery; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -33,8 +33,18 @@ import java.util.Optional; import org.junit.Before; import org.junit.Test; -import com.iluwatar.hexagonal.adapter.LotteryTicketRepositoryMock; +import com.iluwatar.hexagonal.administration.LotteryAdministration; +import com.iluwatar.hexagonal.administration.LotteryAdministrationImpl; +import com.iluwatar.hexagonal.database.LotteryTicketRepository; +import com.iluwatar.hexagonal.database.LotteryTicketRepositoryMock; +import com.iluwatar.hexagonal.domain.LotteryNumbers; +import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult; +import com.iluwatar.hexagonal.domain.LotteryTicketId; import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult.CheckResult; +import com.iluwatar.hexagonal.service.LotteryService; +import com.iluwatar.hexagonal.service.LotteryServiceImpl; +import com.iluwatar.hexagonal.test.LotteryTestUtils; /** * diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTestUtils.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/test/LotteryTestUtils.java similarity index 90% rename from hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTestUtils.java rename to hexagonal/src/test/java/com/iluwatar/hexagonal/test/LotteryTestUtils.java index 4a38ff3a2..883c8127f 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTestUtils.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/test/LotteryTestUtils.java @@ -20,12 +20,16 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.iluwatar.hexagonal.domain; +package com.iluwatar.hexagonal.test; import java.util.Arrays; import java.util.HashSet; import java.util.Set; +import com.iluwatar.hexagonal.domain.LotteryNumbers; +import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.PlayerDetails; + /** * * Utilities for lottery tests From 11c0654103c3622474e5ae2f78a83701dbef8883 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 26 Mar 2016 21:47:25 +0200 Subject: [PATCH 22/35] Add banking implementation --- .../hexagonal/banking/WireTransfers.java | 7 ++- .../hexagonal/banking/WireTransfersImpl.java | 57 +++++++++++++++++++ .../hexagonal/banking/WireTransfersTest.java | 50 ++++++++++++++++ 3 files changed, 111 insertions(+), 3 deletions(-) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/banking/WireTransfersImpl.java create mode 100644 hexagonal/src/test/java/com/iluwatar/hexagonal/banking/WireTransfersTest.java diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/WireTransfers.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/WireTransfers.java index 0a3580db5..61a6567ba 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/WireTransfers.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/WireTransfers.java @@ -24,12 +24,13 @@ package com.iluwatar.hexagonal.banking; /** * - * Interface to the lottery service provider's bank account. + * Interface to bank accounts. * */ public interface WireTransfers { - int getCurrentFundsAmount(); - boolean transferFunds(int amount, String receiverBankAccountNumber); + void setFunds(String bankAccount, int amount); + int getFunds(String bankAccount); + boolean transferFunds(int amount, String sourceBackAccount, String destinationBankAccount); } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/WireTransfersImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/WireTransfersImpl.java new file mode 100644 index 000000000..2ca639a37 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/WireTransfersImpl.java @@ -0,0 +1,57 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.banking; + +import java.util.HashMap; +import java.util.Map; + +/** + * + * Banking implementation + * + */ +public class WireTransfersImpl implements WireTransfers { + + private static Map accounts = new HashMap<>(); + + @Override + public void setFunds(String bankAccount, int amount) { + accounts.put(bankAccount, amount); + } + + @Override + public int getFunds(String bankAccount) { + return accounts.getOrDefault(bankAccount, 0); + } + + @Override + public boolean transferFunds(int amount, String sourceBackAccount, String destinationBankAccount) { + if (accounts.getOrDefault(sourceBackAccount, 0) >= amount) { + accounts.put(sourceBackAccount, accounts.get(sourceBackAccount) - amount); + accounts.put(destinationBankAccount, accounts.get(destinationBankAccount) + amount); + return true; + } else { + return false; + } + } +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/WireTransfersTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/WireTransfersTest.java new file mode 100644 index 000000000..0274feca3 --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/WireTransfersTest.java @@ -0,0 +1,50 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.banking; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +/** + * + * Tests for banking + * + */ +public class WireTransfersTest { + + private final WireTransfers bank = new WireTransfersImpl(); + + @Test + public void testInit() { + assertEquals(bank.getFunds("foo"), 0); + bank.setFunds("foo", 100); + assertEquals(bank.getFunds("foo"), 100); + bank.setFunds("bar", 150); + assertEquals(bank.getFunds("bar"), 150); + assertTrue(bank.transferFunds(50, "bar", "foo")); + assertEquals(bank.getFunds("foo"), 150); + assertEquals(bank.getFunds("bar"), 100); + } +} From 28d3cb2aa26a37d1595d69f241063e91d2ec994b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 26 Mar 2016 22:01:31 +0200 Subject: [PATCH 23/35] Add notifications implementation --- .../notifications/LotteryNotifications.java | 2 +- .../LotteryNotificationsImpl.java | 47 +++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotificationsImpl.java diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotifications.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotifications.java index 11cece62e..c9917cb07 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotifications.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotifications.java @@ -33,6 +33,6 @@ public interface LotteryNotifications { void notifyTicketSubmitted(PlayerDetails details); void notifyNoWin(PlayerDetails details); - void notifyPrize(PlayerDetails details); + void notifyPrize(PlayerDetails details, int prizeAmount); } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotificationsImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotificationsImpl.java new file mode 100644 index 000000000..d91aacec0 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotificationsImpl.java @@ -0,0 +1,47 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.notifications; + +import com.iluwatar.hexagonal.domain.PlayerDetails; + +public class LotteryNotificationsImpl implements LotteryNotifications { + + @Override + public void notifyTicketSubmitted(PlayerDetails details) { + System.out.println(String.format("Lottery ticket for %s was submitted. Bank account %s was charged for 3 credits.", + details.getEmail(), details.getBankAccount())); + } + + @Override + public void notifyNoWin(PlayerDetails details) { + System.out.println(String.format("Lottery ticket for %s was checked and unfortunately did not win this time.", + details.getEmail())); + } + + @Override + public void notifyPrize(PlayerDetails details, int prizeAmount) { + System.out + .println(String.format("Lottery ticket for %s has won! Your bank account %s was deposited with %d credits.", + details.getEmail(), details.getBankAccount(), prizeAmount)); + } +} From 6608a8de38da9bfd4b197ea014451f8b1a56c43a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 27 Mar 2016 23:35:51 +0300 Subject: [PATCH 24/35] Utilize notifications and banking --- .../LotteryAdministrationImpl.java | 27 ++++++++++++++++++- .../hexagonal/banking/WireTransfersImpl.java | 4 +++ .../hexagonal/service/LotteryServiceImpl.java | 15 ++++++++++- 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java index 3d0327fe1..93c68e825 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java @@ -24,11 +24,19 @@ package com.iluwatar.hexagonal.administration; import java.util.Map; +import com.iluwatar.hexagonal.banking.WireTransfers; +import com.iluwatar.hexagonal.banking.WireTransfersImpl; import com.iluwatar.hexagonal.database.LotteryTicketRepository; import com.iluwatar.hexagonal.database.LotteryTicketRepositoryMock; import com.iluwatar.hexagonal.domain.LotteryNumbers; import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult; +import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult.CheckResult; import com.iluwatar.hexagonal.domain.LotteryTicketId; +import com.iluwatar.hexagonal.notifications.LotteryNotifications; +import com.iluwatar.hexagonal.notifications.LotteryNotificationsImpl; +import com.iluwatar.hexagonal.service.LotteryService; +import com.iluwatar.hexagonal.service.LotteryServiceImpl; /** * @@ -39,6 +47,12 @@ public class LotteryAdministrationImpl implements LotteryAdministration { private final LotteryTicketRepository repository; + private final LotteryService service = new LotteryServiceImpl(); + + private final LotteryNotifications notifications = new LotteryNotificationsImpl(); + + private final WireTransfers bank = new WireTransfersImpl(); + public LotteryAdministrationImpl() { repository = new LotteryTicketRepositoryMock(); } @@ -50,7 +64,18 @@ public class LotteryAdministrationImpl implements LotteryAdministration { @Override public LotteryNumbers performLottery() { - return LotteryNumbers.createRandom(); + LotteryNumbers numbers = LotteryNumbers.createRandom(); + Map tickets = getAllSubmittedTickets(); + for (LotteryTicketId id: tickets.keySet()) { + LotteryTicketCheckResult result = service.checkTicketForPrize(id, numbers); + if (result.equals(CheckResult.WIN_PRIZE)) { + bank.transferFunds(1000, "123-123", tickets.get(id).getPlayerDetails().getBankAccount()); + notifications.notifyPrize(tickets.get(id).getPlayerDetails(), 1000); + } else if (result.equals(CheckResult.NO_PRIZE)) { + notifications.notifyNoWin(tickets.get(id).getPlayerDetails()); + } + } + return numbers; } @Override diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/WireTransfersImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/WireTransfersImpl.java index 2ca639a37..114ca4b31 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/WireTransfersImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/WireTransfersImpl.java @@ -34,6 +34,10 @@ public class WireTransfersImpl implements WireTransfers { private static Map accounts = new HashMap<>(); + static { + accounts.put("123-123", 50000); + } + @Override public void setFunds(String bankAccount, int amount) { accounts.put(bankAccount, amount); diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java index af5b28f7a..7da7b2f1d 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java @@ -24,6 +24,8 @@ package com.iluwatar.hexagonal.service; import java.util.Optional; +import com.iluwatar.hexagonal.banking.WireTransfers; +import com.iluwatar.hexagonal.banking.WireTransfersImpl; import com.iluwatar.hexagonal.database.LotteryTicketRepository; import com.iluwatar.hexagonal.database.LotteryTicketRepositoryMock; import com.iluwatar.hexagonal.domain.LotteryNumbers; @@ -31,6 +33,8 @@ import com.iluwatar.hexagonal.domain.LotteryTicket; import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult; import com.iluwatar.hexagonal.domain.LotteryTicketId; import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult.CheckResult; +import com.iluwatar.hexagonal.notifications.LotteryNotifications; +import com.iluwatar.hexagonal.notifications.LotteryNotificationsImpl; /** * @@ -40,6 +44,10 @@ import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult.CheckResult; public class LotteryServiceImpl implements LotteryService { private final LotteryTicketRepository repository; + + private final WireTransfers bank = new WireTransfersImpl(); + + private final LotteryNotifications notifications = new LotteryNotificationsImpl(); public LotteryServiceImpl() { repository = new LotteryTicketRepositoryMock(); @@ -47,7 +55,12 @@ public class LotteryServiceImpl implements LotteryService { @Override public Optional submitTicket(LotteryTicket ticket) { - return repository.save(ticket); + bank.transferFunds(3, ticket.getPlayerDetails().getBankAccount(), "123-123"); + Optional optional = repository.save(ticket); + if (optional.isPresent()) { + notifications.notifyTicketSubmitted(ticket.getPlayerDetails()); + } + return optional; } @Override From b94a705161577f3b61a33c7bd0faa554a1417e67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 9 Apr 2016 15:14:12 +0300 Subject: [PATCH 25/35] Add constants for win sum and prize payer's bank account --- .../administration/LotteryAdministrationImpl.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java index 93c68e825..af4e30227 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java @@ -45,6 +45,10 @@ import com.iluwatar.hexagonal.service.LotteryServiceImpl; */ public class LotteryAdministrationImpl implements LotteryAdministration { + private static final int WIN_AMOUNT = 100000; + + private static final String PRIZE_PAYER_BANK_ACCOUNT = "123-123"; + private final LotteryTicketRepository repository; private final LotteryService service = new LotteryServiceImpl(); @@ -69,8 +73,8 @@ public class LotteryAdministrationImpl implements LotteryAdministration { for (LotteryTicketId id: tickets.keySet()) { LotteryTicketCheckResult result = service.checkTicketForPrize(id, numbers); if (result.equals(CheckResult.WIN_PRIZE)) { - bank.transferFunds(1000, "123-123", tickets.get(id).getPlayerDetails().getBankAccount()); - notifications.notifyPrize(tickets.get(id).getPlayerDetails(), 1000); + bank.transferFunds(WIN_AMOUNT, PRIZE_PAYER_BANK_ACCOUNT, tickets.get(id).getPlayerDetails().getBankAccount()); + notifications.notifyPrize(tickets.get(id).getPlayerDetails(), WIN_AMOUNT); } else if (result.equals(CheckResult.NO_PRIZE)) { notifications.notifyNoWin(tickets.get(id).getPlayerDetails()); } From 48803d4c7d6333515b193dbf9a1d991e204d8fa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 9 Apr 2016 16:01:47 +0300 Subject: [PATCH 26/35] Add example lottery run --- .../main/java/com/iluwatar/hexagonal/App.java | 82 ++++++++++++++++++- .../LotteryAdministrationImpl.java | 4 +- .../LotteryNotificationsImpl.java | 2 +- .../hexagonal/service/LotteryServiceImpl.java | 6 +- 4 files changed, 88 insertions(+), 6 deletions(-) diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java index 5554c36df..58b84f2ee 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java @@ -22,20 +22,98 @@ */ package com.iluwatar.hexagonal; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import com.iluwatar.hexagonal.administration.LotteryAdministration; +import com.iluwatar.hexagonal.administration.LotteryAdministrationImpl; +import com.iluwatar.hexagonal.domain.LotteryNumbers; +import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.PlayerDetails; +import com.iluwatar.hexagonal.service.LotteryService; +import com.iluwatar.hexagonal.service.LotteryServiceImpl; + /** * * Example application demonstrating Hexagonal Architecture * */ public class App { + + private static List allPlayerDetails; + + static { + allPlayerDetails = new ArrayList<>(); + allPlayerDetails.add(PlayerDetails.create("john@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("mary@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("steve@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("wayne@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("johnie@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("andy@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("richard@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("kevin@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("arnold@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("ian@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("robin@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("ted@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("larry@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("calvin@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("jacob@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("edwin@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("mary@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("lolita@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("bruno@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("peter@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("warren@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("monica@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("ollie@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("yngwie@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("lars@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("bobbie@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("tyron@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("tyrell@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("nadja@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("wendy@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("luke@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("bjorn@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("lisa@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("anton@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("bruce@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("ray@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("ron@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("xavier@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("harriet@google.com", "312-342", "+3242434242")); + } + /** * Program entry point */ public static void main(String[] args) { + // start new lottery round + LotteryAdministration administartion = new LotteryAdministrationImpl(); + administartion.resetLottery(); + // submit some lottery tickets + LotteryServiceImpl service = new LotteryServiceImpl(); + submitTickets(service, 20); + + int i = administartion.getAllSubmittedTickets().size(); // perform lottery - - // check all the tickets + administartion.performLottery(); + } + + private static void submitTickets(LotteryService lotteryService, int numTickets) { + for (int i=0; i tickets = getAllSubmittedTickets(); for (LotteryTicketId id: tickets.keySet()) { LotteryTicketCheckResult result = service.checkTicketForPrize(id, numbers); - if (result.equals(CheckResult.WIN_PRIZE)) { + if (result.getResult().equals(CheckResult.WIN_PRIZE)) { bank.transferFunds(WIN_AMOUNT, PRIZE_PAYER_BANK_ACCOUNT, tickets.get(id).getPlayerDetails().getBankAccount()); notifications.notifyPrize(tickets.get(id).getPlayerDetails(), WIN_AMOUNT); - } else if (result.equals(CheckResult.NO_PRIZE)) { + } else if (result.getResult().equals(CheckResult.NO_PRIZE)) { notifications.notifyNoWin(tickets.get(id).getPlayerDetails()); } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotificationsImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotificationsImpl.java index d91aacec0..c2e02ddbd 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotificationsImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotificationsImpl.java @@ -41,7 +41,7 @@ public class LotteryNotificationsImpl implements LotteryNotifications { @Override public void notifyPrize(PlayerDetails details, int prizeAmount) { System.out - .println(String.format("Lottery ticket for %s has won! Your bank account %s was deposited with %d credits.", + .println(String.format("Lottery ticket for %s has won! The bank account %s was deposited with %d credits.", details.getEmail(), details.getBankAccount(), prizeAmount)); } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java index 7da7b2f1d..e595393df 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java @@ -43,6 +43,10 @@ import com.iluwatar.hexagonal.notifications.LotteryNotificationsImpl; */ public class LotteryServiceImpl implements LotteryService { + private static final String LOTTERY_SERVICE_BANK_ACCOUNT = "123-123"; + + private static final int TICKET_PRIZE = 3; + private final LotteryTicketRepository repository; private final WireTransfers bank = new WireTransfersImpl(); @@ -55,7 +59,7 @@ public class LotteryServiceImpl implements LotteryService { @Override public Optional submitTicket(LotteryTicket ticket) { - bank.transferFunds(3, ticket.getPlayerDetails().getBankAccount(), "123-123"); + bank.transferFunds(TICKET_PRIZE, ticket.getPlayerDetails().getBankAccount(), LOTTERY_SERVICE_BANK_ACCOUNT); Optional optional = repository.save(ticket); if (optional.isPresent()) { notifications.notifyTicketSubmitted(ticket.getPlayerDetails()); From 5de9c7e6b4a4783afab4f7da87153733bdb06fc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 9 Apr 2016 16:53:34 +0300 Subject: [PATCH 27/35] Lots of improvements to the example --- .../main/java/com/iluwatar/hexagonal/App.java | 88 ++++++++++--------- .../LotteryAdministrationImpl.java | 14 +-- .../hexagonal/banking/WireTransfersImpl.java | 4 +- .../hexagonal/domain/LotteryConstants.java | 38 ++++++++ .../domain/LotteryTicketCheckResult.java | 2 +- .../notifications/LotteryNotifications.java | 2 + .../LotteryNotificationsImpl.java | 14 +++ .../hexagonal/service/LotteryServiceImpl.java | 12 +-- .../hexagonal/lottery/LotteryTest.java | 14 ++- 9 files changed, 130 insertions(+), 58 deletions(-) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryConstants.java diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java index 58b84f2ee..754f301b3 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java @@ -28,6 +28,8 @@ import java.util.Random; import com.iluwatar.hexagonal.administration.LotteryAdministration; import com.iluwatar.hexagonal.administration.LotteryAdministrationImpl; +import com.iluwatar.hexagonal.banking.WireTransfersImpl; +import com.iluwatar.hexagonal.domain.LotteryConstants; import com.iluwatar.hexagonal.domain.LotteryNumbers; import com.iluwatar.hexagonal.domain.LotteryTicket; import com.iluwatar.hexagonal.domain.PlayerDetails; @@ -46,44 +48,50 @@ public class App { static { allPlayerDetails = new ArrayList<>(); allPlayerDetails.add(PlayerDetails.create("john@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("mary@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("steve@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("wayne@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("johnie@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("andy@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("richard@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("kevin@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("arnold@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("ian@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("robin@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("ted@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("larry@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("calvin@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("jacob@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("edwin@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("mary@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("lolita@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("bruno@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("peter@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("warren@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("monica@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("ollie@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("yngwie@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("lars@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("bobbie@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("tyron@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("tyrell@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("nadja@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("wendy@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("luke@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("bjorn@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("lisa@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("anton@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("bruce@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("ray@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("ron@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("xavier@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("harriet@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("mary@google.com", "234-987", "+23452346")); + allPlayerDetails.add(PlayerDetails.create("steve@google.com", "833-836", "+63457543")); + allPlayerDetails.add(PlayerDetails.create("wayne@google.com", "319-826", "+24626")); + allPlayerDetails.add(PlayerDetails.create("johnie@google.com", "983-322", "+3635635")); + allPlayerDetails.add(PlayerDetails.create("andy@google.com", "934-734", "+0898245")); + allPlayerDetails.add(PlayerDetails.create("richard@google.com", "536-738", "+09845325")); + allPlayerDetails.add(PlayerDetails.create("kevin@google.com", "453-936", "+2423532")); + allPlayerDetails.add(PlayerDetails.create("arnold@google.com", "114-988", "+5646346524")); + allPlayerDetails.add(PlayerDetails.create("ian@google.com", "663-765", "+928394235")); + allPlayerDetails.add(PlayerDetails.create("robin@google.com", "334-763", "+35448")); + allPlayerDetails.add(PlayerDetails.create("ted@google.com", "735-964", "+98752345")); + allPlayerDetails.add(PlayerDetails.create("larry@google.com", "734-853", "+043842423")); + allPlayerDetails.add(PlayerDetails.create("calvin@google.com", "334-746", "+73294135")); + allPlayerDetails.add(PlayerDetails.create("jacob@google.com", "444-766", "+358042354")); + allPlayerDetails.add(PlayerDetails.create("edwin@google.com", "895-345", "+9752435")); + allPlayerDetails.add(PlayerDetails.create("mary@google.com", "760-009", "+34203542")); + allPlayerDetails.add(PlayerDetails.create("lolita@google.com", "425-907", "+9872342")); + allPlayerDetails.add(PlayerDetails.create("bruno@google.com", "023-638", "+673824122")); + allPlayerDetails.add(PlayerDetails.create("peter@google.com", "335-886", "+5432503945")); + allPlayerDetails.add(PlayerDetails.create("warren@google.com", "225-946", "+9872341324")); + allPlayerDetails.add(PlayerDetails.create("monica@google.com", "265-748", "+134124")); + allPlayerDetails.add(PlayerDetails.create("ollie@google.com", "190-045", "+34453452")); + allPlayerDetails.add(PlayerDetails.create("yngwie@google.com", "241-465", "+9897641231")); + allPlayerDetails.add(PlayerDetails.create("lars@google.com", "746-936", "+42345298345")); + allPlayerDetails.add(PlayerDetails.create("bobbie@google.com", "946-384", "+79831742")); + allPlayerDetails.add(PlayerDetails.create("tyron@google.com", "310-992", "+0498837412")); + allPlayerDetails.add(PlayerDetails.create("tyrell@google.com", "032-045", "+67834134")); + allPlayerDetails.add(PlayerDetails.create("nadja@google.com", "000-346", "+498723")); + allPlayerDetails.add(PlayerDetails.create("wendy@google.com", "994-989", "+987324454")); + allPlayerDetails.add(PlayerDetails.create("luke@google.com", "546-634", "+987642435")); + allPlayerDetails.add(PlayerDetails.create("bjorn@google.com", "342-874", "+7834325")); + allPlayerDetails.add(PlayerDetails.create("lisa@google.com", "024-653", "+980742154")); + allPlayerDetails.add(PlayerDetails.create("anton@google.com", "834-935", "+876423145")); + allPlayerDetails.add(PlayerDetails.create("bruce@google.com", "284-936", "+09843212345")); + allPlayerDetails.add(PlayerDetails.create("ray@google.com", "843-073", "+678324123")); + allPlayerDetails.add(PlayerDetails.create("ron@google.com", "637-738", "+09842354")); + allPlayerDetails.add(PlayerDetails.create("xavier@google.com", "143-947", "+375245")); + allPlayerDetails.add(PlayerDetails.create("harriet@google.com", "842-404", "+131243252")); + WireTransfersImpl wireTransfers = new WireTransfersImpl(); + Random random = new Random(); + for (int i = 0; i < allPlayerDetails.size(); i++) { + wireTransfers.setFunds(allPlayerDetails.get(i).getBankAccount(), + random.nextInt(LotteryConstants.PLAYER_MAX_SALDO)); + } } /** @@ -98,14 +106,12 @@ public class App { LotteryServiceImpl service = new LotteryServiceImpl(); submitTickets(service, 20); - int i = administartion.getAllSubmittedTickets().size(); - // perform lottery administartion.performLottery(); } private static void submitTickets(LotteryService lotteryService, int numTickets) { - for (int i=0; i accounts = new HashMap<>(); static { - accounts.put("123-123", 50000); + accounts.put(LotteryConstants.SERVICE_BANK_ACCOUNT, LotteryConstants.SERVICE_BANK_ACCOUNT_SALDO); } @Override diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryConstants.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryConstants.java new file mode 100644 index 000000000..fb4c8025f --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryConstants.java @@ -0,0 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.domain; + +/** + * + * Lottery domain constants + * + */ +public class LotteryConstants { + + public static final int PRIZE_AMOUNT = 100000; + public static final String SERVICE_BANK_ACCOUNT = "123-123"; + public static final int TICKET_PRIZE = 3; + public static final int SERVICE_BANK_ACCOUNT_SALDO = 150000; + public static final int PLAYER_MAX_SALDO = 100; + +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResult.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResult.java index d36dd28b7..534dc685f 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResult.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResult.java @@ -29,7 +29,7 @@ package com.iluwatar.hexagonal.domain; */ public class LotteryTicketCheckResult { - public enum CheckResult {WIN_PRIZE, NO_PRIZE, TICKET_NOT_SUBMITTED}; + public enum CheckResult { WIN_PRIZE, NO_PRIZE, TICKET_NOT_SUBMITTED }; private final CheckResult checkResult; diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotifications.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotifications.java index c9917cb07..cc9f9d6e5 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotifications.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotifications.java @@ -32,7 +32,9 @@ import com.iluwatar.hexagonal.domain.PlayerDetails; public interface LotteryNotifications { void notifyTicketSubmitted(PlayerDetails details); + void notifyTicketSubmitError(PlayerDetails details); void notifyNoWin(PlayerDetails details); void notifyPrize(PlayerDetails details, int prizeAmount); + void notifyPrizeError(PlayerDetails details, int prizeAmount); } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotificationsImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotificationsImpl.java index c2e02ddbd..c59a47970 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotificationsImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotificationsImpl.java @@ -44,4 +44,18 @@ public class LotteryNotificationsImpl implements LotteryNotifications { .println(String.format("Lottery ticket for %s has won! The bank account %s was deposited with %d credits.", details.getEmail(), details.getBankAccount(), prizeAmount)); } + + @Override + public void notifyPrizeError(PlayerDetails details, int prizeAmount) { + System.out + .println(String.format("Lottery ticket for %s has won! Unfortunately the bank credit transfer of %d failed.", + details.getEmail(), prizeAmount)); + } + + @Override + public void notifyTicketSubmitError(PlayerDetails details) { + System.out.println( + String.format("Lottery ticket for %s could not be submitted because the credit transfer of 3 credits failed.", + details.getEmail())); + } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java index e595393df..d9a948597 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java @@ -28,6 +28,7 @@ import com.iluwatar.hexagonal.banking.WireTransfers; import com.iluwatar.hexagonal.banking.WireTransfersImpl; import com.iluwatar.hexagonal.database.LotteryTicketRepository; import com.iluwatar.hexagonal.database.LotteryTicketRepositoryMock; +import com.iluwatar.hexagonal.domain.LotteryConstants; import com.iluwatar.hexagonal.domain.LotteryNumbers; import com.iluwatar.hexagonal.domain.LotteryTicket; import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult; @@ -43,10 +44,6 @@ import com.iluwatar.hexagonal.notifications.LotteryNotificationsImpl; */ public class LotteryServiceImpl implements LotteryService { - private static final String LOTTERY_SERVICE_BANK_ACCOUNT = "123-123"; - - private static final int TICKET_PRIZE = 3; - private final LotteryTicketRepository repository; private final WireTransfers bank = new WireTransfersImpl(); @@ -59,7 +56,12 @@ public class LotteryServiceImpl implements LotteryService { @Override public Optional submitTicket(LotteryTicket ticket) { - bank.transferFunds(TICKET_PRIZE, ticket.getPlayerDetails().getBankAccount(), LOTTERY_SERVICE_BANK_ACCOUNT); + boolean result = bank.transferFunds(LotteryConstants.TICKET_PRIZE, ticket.getPlayerDetails().getBankAccount(), + LotteryConstants.SERVICE_BANK_ACCOUNT); + if (result == false) { + notifications.notifyTicketSubmitError(ticket.getPlayerDetails()); + return Optional.empty(); + } Optional optional = repository.save(ticket); if (optional.isPresent()) { notifications.notifyTicketSubmitted(ticket.getPlayerDetails()); diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/lottery/LotteryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/lottery/LotteryTest.java index 1da92023d..0c5bb5216 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/lottery/LotteryTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/lottery/LotteryTest.java @@ -35,13 +35,15 @@ import org.junit.Test; import com.iluwatar.hexagonal.administration.LotteryAdministration; import com.iluwatar.hexagonal.administration.LotteryAdministrationImpl; +import com.iluwatar.hexagonal.banking.WireTransfers; +import com.iluwatar.hexagonal.banking.WireTransfersImpl; import com.iluwatar.hexagonal.database.LotteryTicketRepository; import com.iluwatar.hexagonal.database.LotteryTicketRepositoryMock; import com.iluwatar.hexagonal.domain.LotteryNumbers; import com.iluwatar.hexagonal.domain.LotteryTicket; import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult; -import com.iluwatar.hexagonal.domain.LotteryTicketId; import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult.CheckResult; +import com.iluwatar.hexagonal.domain.LotteryTicketId; import com.iluwatar.hexagonal.service.LotteryService; import com.iluwatar.hexagonal.service.LotteryServiceImpl; import com.iluwatar.hexagonal.test.LotteryTestUtils; @@ -56,6 +58,7 @@ public class LotteryTest { private final LotteryAdministration admin = new LotteryAdministrationImpl(); private final LotteryService service = new LotteryServiceImpl(); private final LotteryTicketRepository repository = new LotteryTicketRepositoryMock(); + private final WireTransfers wireTransfers = new WireTransfersImpl(); @Before public void clear() { @@ -65,6 +68,9 @@ public class LotteryTest { @Test public void testLottery() { + // setup bank account with funds + wireTransfers.setFunds("123-12312", 100); + // admin resets the lottery admin.resetLottery(); assertEquals(admin.getAllSubmittedTickets().size(), 0); @@ -74,10 +80,10 @@ public class LotteryTest { "123-12312", "+32425255", new HashSet<>(Arrays.asList(1, 2, 3, 4)))); assertTrue(ticket1.isPresent()); Optional ticket2 = service.submitTicket(LotteryTestUtils.createLotteryTicket("ant@bac.com", - "123-12345", "+32423455", new HashSet<>(Arrays.asList(11, 12, 13, 14)))); + "123-12312", "+32423455", new HashSet<>(Arrays.asList(11, 12, 13, 14)))); assertTrue(ticket2.isPresent()); Optional ticket3 = service.submitTicket(LotteryTestUtils.createLotteryTicket("arg@boo.com", - "123-12367", "+32421255", new HashSet<>(Arrays.asList(6, 8, 13, 19)))); + "123-12312", "+32421255", new HashSet<>(Arrays.asList(6, 8, 13, 19)))); assertTrue(ticket3.isPresent()); assertEquals(admin.getAllSubmittedTickets().size(), 3); @@ -86,7 +92,7 @@ public class LotteryTest { // cheat a bit for testing sake, use winning numbers to submit another ticket Optional ticket4 = service.submitTicket(LotteryTestUtils.createLotteryTicket("lucky@orb.com", - "123-12399", "+12421255", winningNumbers.getNumbers())); + "123-12312", "+12421255", winningNumbers.getNumbers())); assertTrue(ticket4.isPresent()); assertEquals(admin.getAllSubmittedTickets().size(), 4); From 0fe8eec610d3cc4e0aa138267fbfff2a44a3504c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 9 Apr 2016 17:17:29 +0300 Subject: [PATCH 28/35] Fix merge conflict --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index ef443a2d5..e5515c51e 100644 --- a/pom.xml +++ b/pom.xml @@ -123,6 +123,7 @@ value-object monad mute-idiom + hexagonal From 9b3aa785edcd5528eb89ea4198dc12f84b252e54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 9 Apr 2016 17:21:27 +0300 Subject: [PATCH 29/35] Add missing license headers --- .../com/iluwatar/dao/CustomerSchemaSql.java | 22 +++++++++++++++++++ .../com/iluwatar/dao/DbCustomerDaoTest.java | 22 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/dao/src/main/java/com/iluwatar/dao/CustomerSchemaSql.java b/dao/src/main/java/com/iluwatar/dao/CustomerSchemaSql.java index 05707fa0e..860826abf 100644 --- a/dao/src/main/java/com/iluwatar/dao/CustomerSchemaSql.java +++ b/dao/src/main/java/com/iluwatar/dao/CustomerSchemaSql.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.dao; public interface CustomerSchemaSql { diff --git a/dao/src/test/java/com/iluwatar/dao/DbCustomerDaoTest.java b/dao/src/test/java/com/iluwatar/dao/DbCustomerDaoTest.java index 08e61ebe6..7e6d7e149 100644 --- a/dao/src/test/java/com/iluwatar/dao/DbCustomerDaoTest.java +++ b/dao/src/test/java/com/iluwatar/dao/DbCustomerDaoTest.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.dao; import static org.junit.Assert.assertEquals; From ccc1ec921a5a8c9bb47548d8b5c2d70579510943 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 10 Apr 2016 21:22:40 +0300 Subject: [PATCH 30/35] Add readme and class diagram --- hexagonal/README.md | 29 ++ hexagonal/etc/hexagonal.png | Bin 0 -> 165940 bytes hexagonal/etc/hexagonal.ucls | 325 ++++++++++++++++++ .../LotteryAdministrationImpl.java | 4 +- ...a => LotteryTicketInMemoryRepository.java} | 2 +- .../hexagonal/service/LotteryServiceImpl.java | 4 +- .../database/LotteryTicketRepositoryTest.java | 6 +- .../hexagonal/lottery/LotteryTest.java | 4 +- 8 files changed, 364 insertions(+), 10 deletions(-) create mode 100644 hexagonal/README.md create mode 100644 hexagonal/etc/hexagonal.png create mode 100644 hexagonal/etc/hexagonal.ucls rename hexagonal/src/main/java/com/iluwatar/hexagonal/database/{LotteryTicketRepositoryMock.java => LotteryTicketInMemoryRepository.java} (96%) diff --git a/hexagonal/README.md b/hexagonal/README.md new file mode 100644 index 000000000..d49b21731 --- /dev/null +++ b/hexagonal/README.md @@ -0,0 +1,29 @@ +--- +layout: pattern +title: Hexagonal Architecture +folder: hexagonal +permalink: /patterns/hexagonal/ +categories: Architectural +tags: + - Java + - Difficulty-Expert +--- + +## Intent +Allow an application to equally be driven by users, programs, automated test or batch scripts, and to be developed and tested in isolation from its eventual run-time devices and databases. + +![Hexagonal Architecture class diagram](./etc/hexagonal.png) + +## Applicability +Use Hexagonal Architecture pattern when + +* it is important that the application is fully testable +* you use Domain Driven Design methodology and/or Microservices architectural style + +## Real world examples + +* [Apache Isis](https://isis.apache.org/) + +## Credits + +* [Alistair Cockburn - Hexagonal Architecture](http://alistair.cockburn.us/Hexagonal+architecture) diff --git a/hexagonal/etc/hexagonal.png b/hexagonal/etc/hexagonal.png new file mode 100644 index 0000000000000000000000000000000000000000..8c03d375f998e2cd843e7a1dbc3726dc3720c8a0 GIT binary patch literal 165940 zcmd?RWl)`4yDhlz1cC((E+JS5B)Ge~y95aC!JPmhS-~~9yL)hw;O-XOA-Fp|N%r~f z`R=)=>)xvFs;=twWB*{+Ui)2hJ~Ey$#`AtwkP}BmCP0QjAgGcOB1#a*V+RNX&f*a) z_)B(bO&bI@=vq=lP{lQAC;71_s`_?W6cP2=;&f8Acg_(){-yIVh)jyC)5e8cj~#&))VO zkKpCTuhx_#j3x^P*wH%JUGNA9qLrv6BJ85Sxg5iaZ!Y0|D#wQ9Oj~jU5^UVnB2uW1wwt1Nr*l0?Sf? zrVLqv9(IxFFf&vexs8Uyxl9PK`Lf_yMMNAktOACc1h-n znJAHUs1mKGjQKqo#&>PyXl$%8SFft195n1av)gj7`}2%e{ng?ZMmAdKMPt*_JhkGa z<*||ECyn(cS5@YtT>XhbfkEoB(uu_yUgvYFvI%3MUo6`POXdxc+QPVOX!S5C10dXE zjBKAD9@2{mbMcozI@w?=CuASOj^GJ*A#)BpXh3doBLuTMg>*pj3=^}$?YNJSy@I9I?yE|dUK|6Vc%kuh`xm?>_tS%&X<V6BLbN^8 zFaX0KNDMkC4*yu|00%r`p+I`8C^*MlaE{IJo;eM^re#D--h#f)J0C&7IxEmPZV)~^ zV%Jq7D+=%X7qX^bZYU#r7JOQwzT!`2h|Znu67nBOq?3BPCOVtAogS`uFlwEAa7WVn z*%O0xeP&%)5-%z0wV0dQtl8|D9}wVdZeA%_3sv>L$d3Vy)#M38~Mpi`i+!{k6(G+)f|WrRuHdbRlMB~q|HMjCMg`CVHAF*fqt zB_g=!q(t`08XH-1>+oz6{${E7y>;(@mFjScpj4WmlCYOdR=itrKl#+`M4wm_O{<(l zo9=zPo$Aw^E)`R;F=%SG*UI5+^)6fL-6pnY?{|OcJck!J{cxpvyp2YkI#*TiH*&UB zmA3K}%hS_i@Lmz!UhgBZ)L!uXiOw+SLv#|*@{wYuA*8}!!89XdBkRBqz_=28qg|A4 zHLXW+MeK!H!GkyJv;k>tf)ps^(hM^KonP@spJ?msd_p)O+mx+c+a>x%FO7kiTdYVs zE-7i&XSvN)Yp%&dFkJ{iv&8%ECSs6xGW}hzdsR^#TrP;nO!4!J-=&mQNwI7VI(5EZ z@Xp`l=b_J*z2)*aV%$}(KZ$~lS6fDH25)WU%jgEUe<8+rC?u}p!5FYR3b|9%6cR%p z+P{nV4#EX~XoT5-G$PJoAHXa!&!O@7tNI>(L98;u0{L4RBb3f94f414EpE}&Q9EAZ z_E{Yn#L2FtzHI!`yB zg=Xt7IP^*&3ch~*sP9!RT7Udg@6?Z4zQlGY&0R#~(XDO^pJsW%wASc;`uU%l=c@eY zQ7fkyD9mO+Ry_u(j|Ea+8-#2e2-#RfzER;;=1=Is`Z8(6sp25@3F$!U)BJy}%KpDx z#Q#sfem!A@x?)8wB-o9!`7Ri(c;h_d-Hf4op|AL#WB(=^jPYrs%%3=c5YwQkmE-H9 z1C}AmG8#Ly)<<^s5T-@u@mBsR_|(_l8ZXOmm*tDx7nsn2eu^tx23`TjcyxBTK$u!XDCqu7^blTZFfYoErZK18Wmg0}o_4_BRd)$3Z;u zBI%C5Ek6HCTn#i(Bpb0($n+MuBfson##u z5%vb2&wG@PI8nXmc$V)xm=d?(eZ8=~#`AuyC%xQgr6-#9ZqXlMbN^szj!}PpKbAYZ zcD-&bqfhiEZzXm)1D}CB++hv&D8FZ>TJq#PJ1%9CXSH&WZPRl*uR39}t03VG6^!#{ z-d7yVmv5VP#_q4w(^I`?+G`y5W}{_hB;Zy}k`lOf$~#GDmD=`K!t~i~eqA}$d+Q?F z<`cNEQr2E{i1>ch5DH6LTpStuC>eES;cYRy`&+w0nR{>-r;ft-i^YT`=oMZ?%gblG z+9xNYtfXEi`e8guG%t&!#5z3HE&K~%LVX9Ljb{0y8MGq}dg;|<(k#ipSB$?lav$0L zT`JG|3a0<{8}p%%xDF&Tb{pfhV#I~x$9$j>e==K_gQ4An;LN02Pq*CjTlFH579CdX z9(I&1@9Tdv>iooPI>O_~b$N^Z)>{K)5n}-LC-q?=@px=JqoDPr6s)xK>R5DhQGX)- z2wfksphIn}bDxO(ZvjoY&hoQt=>uamH=RG~lypI(MMU4r(3{v;mknwv2bu18c5qPe z{4*MH!mP`J*YNP^tOHgs``~G2fY6__hvFAgU=TR=$1_J$z_X@-x>hCQ2}+7=qDlDu zIL~u+#eiB2^Y%s@Wr`1~MR6{CplNi}aLCdhMJdI7BIG1iDpW&g7h5r#*h0olrabWb z40&A+C3$v#yH8*t5Qyv-Ll-kV`~BOmNxW#~I&-}UxfNZ^3Q^YIte}XXsoC4N+$Ct{ zDF_}}l{UWd?v5SFznRbqrV;uqI{l$vhZKeYPsV|4956!AH-}mhf+n7%#d)OF3URr4a zcbJqZZ?!vux8nKl;YeiZw+lbdg-lllo&25*RZC>a-%U5^x`yl#1lB#^#S}DojMDnR z&^NRx()8{^zDJ{O)Oer{E4sfV9p0$k(C_ySWaU0S)oxV^W@a|x@|v-g z&6pJ#E8zC_R5deOLn3{tP?5#2IasVc&2uhSL$9BcTx%nnuU1Tiy0kr6dN;1FoyA?L z9z}hQf{aW?Z$8fxeD!6rlr%dGjaW;F^!9q;=Yo$9E5neuPHdHFXDH@a@t75)@cl&5 z_lStM==?Vd4bCBVH^*eVlCioueY-HvpDhn(I*xZ@_iNWV7*1DEZDG8*Ep2K3#UrW; z%^Vxof_;F%PC+H5l(k%q zMpvt)AKs3N%qCAnv=m#uKE*6D1c|6xh=}u2V{vEt*L}}(dHQX3-^ACK^PVm}-BG`t zUh0n=9Va+hq|CY6E!9R7zwx?)6eOWN19+gu`aKknQL8|uoKie?IEB>vTc~rI%{zby zH(!)-MW~&;F3J#hrl=U{B=HEaNDCqLs!CxO*gZ>5V6Qcn-R;`dX$i_PYg~mP*H)?2 zgByU^G{C`&i>cNrQ6@03nPR$~)MpM+k+7y$D|%E7<>T^nrS%fglWwUj)@0BLk@X#1 zUG3p-JnC_^;JBAXkv&t?gz2bG87aCw1zmA9O?jBp0fkZi&oZuu|rK zKU-DvO{mCtuSnG!Cm(Kn-?JlOnLCONCuZtR&F_YLoQrDf>x+5|AKf#sMaRpWUe=^+YuSNixg`#99Cv` z1}A|PC7Xcn_C$7OshgTK_d-NWo|s#UN;)G<0(w5@vT%P_Ydv0oXTEZE+Py731cI*8 zaf`a!4heNzrguyjAn{)e@od%t&S%hAhTryKgNf&t8-#l;_jljEsbx7M+a`0fuk|ad zQ1MGuYDtqlpiFzwU^UE7CUD<2ZVx+?u^|Mk1=Xg5z1WB(J~zYscO0$!UYBdbDvwWB zH!=DVyR^IH1^r)fLARezE&#miDM7!D`?nM&yD$A1HqQ3Cu8A==7tKXoG?AgEQ~Z&7 zmNj$2pg%fjIk9EBFeePD@|UPkhV{RhuP%*=!7k*6bUA9JX{D_ zqdq2amjDCiz4eTt8>{0YR-W8yB8=C-XG$AoUuYB^S^`ZgPcB(1^p&!*a)Rk3He*Zb z(XW_|4s9)W#4ou&vDykTO66Vzs%n7j1GTTpcHwI*7cevX~4i0+~JuP0USF9O`$GFF!QPw(DUOPBa z_BO=*v|w0Bs>fi)bbmhZZ?Hub#j|-|Te~+Wb^b2*QP>@$rJyw8tA-N4vLy*&kWbMW z^oEhl(JwcAX445RSQR%{vwZv)>3kZdgB`YloHwpUnEP>Fjpo+oR}kCGc4f$EkLEaq|E*%Q8ynnK z@J@TP&J`ASL@KhiwnL4VJZVDQH_ExZETSvCSt3iPGZj{#RnJl`fysXs`wcuq=6ffuoRU!w_BZ8ZjN^rIM)G?0Gfzus`ztM> zaSPU$yFKs!OEm)-gv#J^+^;P&kbpyAD{q64M@xTOm2tDpBgL16gZh-URQ+fCE>>-% zN!I=CnQoM3GQr@EKoMuN(B8&8VPfZ z;5;K`7##|oMjHS5duQtP2UVf#zGRPUq#vNtnUq8Kf2Ne}=fND!<5;#H54vQnfuZ>N zl2@j1404%U`oBk_#0P}>HK#U%$2#QZ13a==zI0}I@oYb zHTIxNRmqS_a)#A`(fqY{$Li|cjtG_X{9UPO7LM1G!p(@ZW4aDFE2gxqGt`9In+=3W z8SbNytN$7t`0Ax3^9?R<0qSw=s(0!}q|ch6pb(nUv9Q=?>el?XDnm9?YfFT2057?7 z*h!i_esS@&K-C{0A4#Qphqdqw{qHyhwCraQT*Y|{$xQRV;*8-i{B7DyKR)5Jx|x8Q z%_qT*Z1In3+{K$}RavOE2nW3=FITKeV5Bb2`yUZ8ls~$=z!#~sIx=MbS?Eu-DK#me z91N+gI(dRC9|TY=xn8mia-rS!*v6{EMA49|hXcKUtR{K!CRO#fFW#0<33DAcs||;; z3*+*qJ^1-~Ezcumi%qL%KT$AvyjJTIudOx zhZ6C%x=Y2*phJqhs2LwxibZeSvA_FOuU_?OH?{3*uiNoP2;n^srt3&nGk~F-Uhah^ za73pFTL)lnCgHA$tPiU?3?iM6WeD_&?BbKjHyg+$-6v zz+!FXtfbUc<;mUIy4@T8%JsfyQYhRm1MkNm{d{Z7?MCK)83@+1fHMG5?(~Hg^E=%{ zZxDr&-28`+C*5ReBsYnE4T{am_FUezU2y6)AXRozVLi|192XNcmx9R@Kz zD`KLhO&9RH*&!_%OsdoU{O_HI?165h0)(Tu_+*qrJ!w`0B5J{8slKkmP-!^PH-!Ht z9R3A%ytVA->Q}`{Z`A=7{5?s1T$M3eILhLb+vFmCF5=B@GuKn1nAw=R@N*}qmy1Es zOzLA}X|?V!Ha6w^^40SFfG+f0)Kn4dqsa&2Dy6blc#w6kV;w&4uxv zNdN!``U{zh4EICT>-!*!O3GOw(=5{=USaj?Z0l!G+}ZYcy=JAt(VWG2ggY%+49(IY z3FFVSTk~4bc4WU|EL2-Q9|s(uP;9|8L^iNUH&MjY=k_$;k=*O5D*dbd;j*6F@kg}V zjp1(T*x+nLY})1X(@AZX!bs9=&El;ED;uZfpY1rWwIs+QSISQ2T)6YJMg~%N8ToG= zW#3rcebl>(wxHL&k|Yq15T3O1Z4A7@-pQq7sn7gPLG|#fYyXy$qpD?6A0iq6TZ=0c zn??~gb8%Sz1!lT;iTql8miiM-LkVya88_H5BRo1%DwUS8@YqzcVFOA8@_j z;&Zg>J(6r%IlBGzH7`{sC~S4*VU8ovjtw88{>uZHPnhh>(!z+OY~F<;EY)t(Bq^`} zI_P~X+weo$qxQaKjmpJI)=SRG=iY7NuBV1JM8vXjFC^#IY*&e-|7{@blHLtRv#!$8 zg9e9=t{+@+mSc!al~~q_{sl&^wsLh>#Wv}uK;7$&J;i>H1}!`$x(ruikvlrOWYr6X zUb&oKOd>Xa^O`vcR=JYyor|Ly^UCM-DqwPVqtz&bSPu^93F=$;!Cu0_rljSyI`1vC z@J@?Fy{N|N1<6~eR;&XkKFcv0d0}qJ@miykJz`-($tlm?2bMKywne87Qb5O4#^D45 z=Qp#etP%s#*BS)(BODZPgp6{MX3Dv%Pd=eLH{v{)5L$(WJ zAHdNw*Jq35|02Tti0d_Ef*+OX9b#H=wIaQM6+v`AK@AxBb2f5*U*0pGIkNJ>dFH^v z-1@JTLFErnSMY`m8Hk758mZSUdUo~m07o$YSRRAXjyMbFeg7fWv~DaK^y{fiB`5Eu z?H~^2ntaB=i(GkI-8zR1P!s{R{MB3Alkm^%t=;y>uN6&~c3li!qjd2RtN0=z77PU( z?-fMI{0|jH+1g%*=}}LR3d5%y!g2RKL!+JgBKYu&Q8^#R!y}lO_Xz)a1U;08N5IU& zVdw)>8h~tRdQBD^1J>+Kum2GqI@>lqV9Wi9ufAfgKU=8Ot0fyF@*9+ahvW)`xrx#A zyQ6X-88CRZwoi|aUpzU8Z`}p|uhxsXP;?QN*ZZF}6OM-P=m6bcN;h`BznLCQTyDvmD$YEfpNYoCbd>dA)E=5j7cGT^(Bcpa=+-QrbJ^3;sDGy zybBip+OB9@q;F`RCN||U@?GQa=$vsEm|HKJ{EMIe>>i(u&b19S13JdK5pp2nv2$mm z4}%-TT=YE3G~e^~G`6jcLp*i9KBga6f{vEfzrF62dX^#1->1T(tb%a&S(lrMlqG_P zik0Vcqhw?2VK^6*0guDMrk>rf9IduyIli$}Cl%A}Rc2ndI#1ZH zHtd&V>^{H>FEEgPLS6ZwKun!CqN7@18QV!IelEqlTo!`tb=dHoaRKKr!A5HyW&no9 zSDtxq6#JFL@RRxVtJA_%{#3zh<-)t${iOt6)b{e6L>RjORIeAFAM8^F3LP&UtyVg! z!-%!>Z7MUUg*)#KJ7Km_ye^iw`sYJ4teL!}A>FHPHa&~gn<%pUEQvNG6Ok$3DH4Rz z1z|RV5Ma#PJtQQb$-IzzfVbC2z5V(5KXDmJqzg}O+dIS}LMl?k4GoFnDH*~u29oN2 zi#cTpot?K_3l-vdt)j{Qu z1rSfomUe6Jc`a04?h02AZv$;w0s@%xi0`t=SF=8M%woFZCtGlO9@bBb2fn@z-_iE+ zgF|vQwDw*KG{xzp%Mwy8);gtNc&WpoRY}+HkzEpn`@eA~3Sr@|l1l$|AJ27%KOki0 ziC+L3Hm`E?oj;~vn2=Kv4~&YrPRw?~+F{pl(?~FfRi=>s4af}_(Z-y_SQpShkrziD z#`9wv$V8c46U|fK^>z#k85>W9@$Zf8nA3!U*zl#a+GJkB3ciNd+I^Ub8}IM72a*#D zeidzY_a$-WNynM&H&!Ms(42Az&9mXRQ281k@vy%;&^igsAx)5F591*NW!DTOIzkdD zE_@cw{@bCn0gtoBKK1S+=Y2f^6c!!xsWL<#h5cyS$AH-Sf?MxmdpB)LcyY(2G+kwm zs3{XhGHfha)nqx@rSH>!bN1`x?)F!dWVM-M9NLnSB1oCYcfHv^66_aS4K0?!$#pX` zet4XRl4F9L6`zr__P99|2b@ku3G`)TXrzND%yexgiZLsFsH#NZo~p^Zb>>33QHh)? z^x^a+MZNV#`vvr=`E*td190KTJ`nE=U1P%J*vyXMaMKq69PqDWUtR0H?hN@oo+Y`P zF5FNd%?9*lSG2s5Z*(}RbjuN$9@)?Ar^EuASItG8FH1g6I*uNp*`W!*cqZ7V=GX~B zO%B-ZPew}s?Fd-NP&Vt7SfUe_u{cOu+ebbuzVF}U(^Mx4fQmrCl2`JO1P_Aj(vqrX zA&Un$wC4K-1I+WNjXGNY$qYu8+aarZw^bNW$wZWCSZEe+%E-uyUC|$cp26Ku^VerA z%Z&lzfukvKTaD_a6Lpb239QOf1JZFXB2^^t=jufxS?>Wu3`S(@U-KleO$R?Jcc_v8 zUy;aa$uvC+@bmaDoPAri6K<|~UYnWgu=jO#+TWGed&8XuFrQBk9F{wj;si*{}N?6jNK5 zGz)8dynt;E!I<>|rNb?c{U*9FaW@Hix|4RNei!HeTtR}bSprd%ScLg}s=U39K$r7g z>mlzJloz%y+A@INLX7jUKoLQ=lpHj`lm#8y_xR@r->QrzG8jP6TWD!C zxjO*9b@Z3fUwlyWG!y3`qN-vr-I3@M>Zw{qAYIG~*|u;SkR3!%0y~5YnFV3vR5F!c z1~4Jz{Kn<0Y~K`l(k z#wSvzStT;D?*PO8c;u%Fo(m#?#eoEYFrj(AC=h>ReRO=++-5VCM!>_Q*xHvh>H z29Pd<#qT{Zc&)UW!v2C?L2AYRV~p}s75G2=dh!Qn+cKW=*wUKe8LybVUSADn7+$3< z6SCARWMkl&%g|gTQUIS|ybBrZU%2@E$WYq$ms`(Xnk6qqL8FNt{y*pgibW)N0jhtpawu7vj6L^hnJz3!aFxH zUC>WX3ry18^H#~>!{QC%Cnhb+hyX`N1N~2USF=xZA+&YQ3-PrZ7gEN9_Y!)!l7)8%$b{v%-`=!OBG0ecU=%$ zZ}h}Sb3e~Al zp`#Q68lf8K3!gvRv)h`O*NB@E^k91W?)Ok#LvUYvLp)JIB{q)gUdUQ(oN?z!0R~)#omKizu-otZw7F6gcZpt&$~Q>tvoyY* zV>1ZaW58d6{F`s{%SB&9dm_3ZD_>M5GgZv5D7`v|&7pLvIL7_lpFVe-{xv!c&RI^s z5s}beDb{XG6z}lp#K~z5>qe=Mm0?j&c!ObK-&5J`=YIrzLXdaySM9LkF0XINJRF|S zIYfj(d@Yxs_N%uDyyU1+tuKuAaaLP}HSNRaus3WpTmdtPpI@!|xL4eyergZ5UdJd@ zMP8s4YfO+(1 zpHqY;dDmTTuN1GO*@y@U$I}E)cXn_rgq@_dA})Y93#ipc;A>8d3j76&;`QYe~_c^I!%D4SrInT`L82iJD(s5#>(X zD#$2>6t8`7aDY24n`nM8|HemFsp~27D{P ztT--gl}tBsr~!Rgq*- z(f;RUeSLusv?9Or6X5Xx%1F5T@d2SYnfOBu$sn!gvzg_ zVwRNLBxIepCq@4h)J3nG!{0fHNIOFz?XLH{r&5_jPJP;bZ9(sIE z7L=?nC@73UvAyA??C!oL1^$5&UCJ%Hvr5r<5(_%iabO4XU3Dd}7|nUM!~SZlCWypc z;f{!suXVD{xZY@xXY|7^DX}gvrDbm;k6(8YcBFym`Vt)6=oO!%-Jlw*j|T4l{NeuF z>okG;*u$!3Bh`oG32N~TaJvg$JzMe~CJflDN z-&(b-D-?%@UuXT~R|u}dn=j`(t@G7tl_pGrxNMda2JH_?S?_3m*2~zC?J){9u8>Wf zSSn?Tu9`1)J+n$MDdcjo0so$YVP~!(T#jyT>t`}wQ_S7GjkDRBblh1v-|UaTOkl@& z$?v%_;0DTpnyG>WT_c^A25|o9=oISk!p9VrcwKi`p*R_bk$52#yeIxcK#mSMy?XuG zIkSaxSo-HpWaLwm0iw2=a4JI2M|8~I;tw?!Sh4u8t^BvvVYnTQx_2LmJLg^MW0c|G zCOq<$fgJqffq(0jbGI1xF%L8jkYm;h&4-j>*jGzIwbb%^aglGn?x$%abDeUY@zBw3 zlsll{g-#?nII%<>1G*rVQJ;=HT~}+yD&_2<5NF%aU9TZ?+X3Um@p{NtW>YgDb(#*) zn6`RfzB>mD-0wN=9JArhP*Qy=dHNdjW0qf?!z|cye!$02#sH=};%ppZ-J>szXOEGQ z)81u(c2?TVC8?Y4PU`lP!;${MyH?RCL|$~*D;NZGK?1y!bsX%42L~2m{juG(m9;!L^1!aHhiYGszcNPl{Vh&_96fHzu(O)j8*rCqs^$Lc@=w+XK_)U82vFC& zl&4p!rRC%6FjuvBr8A|Rr|_LlwP&K}nT(;w!FN~?o?L?nBMZsg8&2Df*OOXB=q?9~ zPu+4vdbad|o^uU4mPSv2pl%fqY9D%BQ?xxWQXYq^t+oih7#}8ANdEl?2Pb`e)tsAI zwG1YH&kL1OJ=XrJ49XRksyRiih>yIY(2KewT=(9$NZgL^0*r=2j@fxOmQ)8FkuAvY z9Wy-*JXANPM;m0|+J8Chcm4*9`6ES|o2IGO_^+4aL08(=lRiUz z$%VOy5YObOcd)nF-#{d8vTxS#U2I)V83VJ6&<>5uYL+7zkprB)IeyGn{La)yQw;yB zKv$SDj2~Arx8L{wTE9*9^C>nif1=w`Q=g~4HX>UPEa!6CehdEm$XZ{in%ATa0T*^w@Igt&Pzs^!=*MXOGeUMag-u3qcwu6@nKk85O z>kvXs;3Qc)9Zcdh>k2Oca*6H7O!K_%sOtw)qxG;PJy-oIm{VoqBgtCCoC3GKR&k^_ znGqdpg@x%EbvUlh7tp`j?5e%?=FD-#?=m!xCdSuj1P-Acby8!(1TTnNs7MPHmlqsc z$Ah1G;SFFZXO@TSUYvP6XzHJOVp5@>9&tKZWH!)koYMgoCUyVOXF$*{&ldgoS;))A z0E}iAQ&TiGio4RR?@5o`p}1bb3?gD5BNm)_JMv3mAEQZ=IcnLDG^=ZxM66+(gUA}K zqHq(A{0kJD_MOsO`^_+@v!f7@_oX+VI=nA@X*<(6)u{LqrP z$;U?@A5eMwqEFjR_?ra~NA3(eK9b&7=953s`P)c76x`K{%YqQoWiEET^F(xNbe*ku z4A8rJN+)oVfCwXW8?U?ynQ(*xe=WA))O&m&6GE?Om!n^9+1@lb-^z}?k}HV3n);R; zsJN|GKOE@=hawB_s`=tcGF~IMERb%TC|MJukz@r$$ZT6VnZ~5D6oBVY@$5P8i0JPL zR5Rn~LV_o-hp-0Z0jKd-{tLiFfkoXh`eSbn1sR7<0&X6(gcqF>p-mIr{hYc$DWjhU0B$As}wqpQ8`)`)yT(A1w; zQ!?rzyuaKKEZEq}wN#RJ#gS!p5m&ljbwW%$wA9a2V;*n!d7=*N2=-6 zzKZ8mwIEE6JH5^GmSismp0#4+$N8=RT4|-cp;`iVfj=FFUZ;WW^PZi%yG2UoXX_CC z(9fBVZ&Ovf`UfSXPe!4Ff-uEe;~Ux%cv+b2IXJA;xU04HTO)0_4BPd;9if(kyZz^v zq2XYFc{R{a$J01tq%4rH+W_mMS2Yu7vCan${Z$9YbB>}fL*}Bi)Rp)gg;6GQif_Wz zo}&?I5E1EI^~4DCKo#OX!ISySZ-GQt;D>h_%hNc0_IoV+_6n!iOH5X7xPx8Zdws!&$y-Nx!g1JwVYOI zd*T?|eK?*R{TcPzFOC~PRldLaEdh+HV0DIiG?gtGN>QYf0rV-Mm`%3jBlkIpr^Z?_%F4_?%X{!(qb%?ZTmM?m zQ|}B7p$wDqB0qCXwVVtcT1w^;d!JmD;dunTVVe`E2bK=Qe50Hag{H7ZA!i&4%A&)>`L}7q0a;S? z7NMD!Vg%RDaYO@Unup@B0X!#tjbu` z3KstGv;I?x8oU7fA5{w@a}nq+PQhinGs8{n&1EZ@G18Umqp-z2p$$#`?g5$J_AEXo zu=)xuDOz`8zhmgB(TH5HC1)Ny$6UfzZQg)>1iJ^#*EUGNz?}5yG{{VTf~`i%2=2^1 z?Eco4FvP+PzszcygO>VK36!L56(|tsn!)R*8|^KEczsIlZN&!o9*48E>-UKTAaOUEhh$_8<=6BLkouRdfk-QJK44CtlX#wXaCnf}7bdF4ZbXB(-_ zf`I7<_JGn>h$5q|`R^_=fpQipG(CuWYWbDdfK=74!H9kcf8Wh2Z{ zvtP#GV8_L1F1kk5CIT&W#M#3*p`}pL^}A`Cs- zY`)yA6WV?y<`R0qPES)BSC(K5z=m2lAhV;e)LZZ0!A&9m!Ix!*@K3m>(kUs4>fYPb zvWr<=8B7z$j?h_0?`M)M<>?a@Km79{ky$T23!=CVv0k*5YBC4jQ!+U*;1Qq^PZ)pUx{=!TBW)&f!hN`M*mVpTLB%;4};am@LsEgE1 zaQ89VE9?5(FCTreU_Nw-jJSXI0OrDw*y+3H6tQY!(;Xv#XFon8|TG6{fxarDXR5a9ko(eTmtZ)SvY>G zWzJf!X=%wxq2)s7J&u-@uPy@~)*~->xGE};LCY2wyAV+7&A-l(`^CRYiYAk(IzzJ{ z{yren)xPQ>S$L~&BvC}bjzz7NktqiWo2{^!qX@;R4yxCO-62y(I>X+ zA8?0$nmxy&Oa{Fg5AzRqAPH>(YkM=Pd?TLU!ml1*Kx^HMl*Tz;Y%AKun>8;cqirZd zPPbK!%teWN$2`{`lTvoef;O;v-SD3Z=s&2AwEr)vle5gzZ@Jn?wIz#lxbyt@pU+&Z zRVI23>WgJ~*~aWG$o0hPi;0%vFj86wFyx+t#g5x~IWJs*kwLh3UYxjqDJhw2&ietg8;XtWYqnbH z2ngf-KSuVy#Oh|(Bk5e{-wpO{UDihL@r8ZNW7vAm6~pYt7(>@>UDr{zCza{BraQw* z3v@1N2?{07oA1sxht)R)K-_Ugm}otKd`b!-v-?z^0q`5SLPB521?S4U?CU9j2?8tz zVLwDUdJvn{*8x2NC!n5;WT&!l#Wy}M$n zGJcG?QLg>lnSw}s`&RXp?u>Y^iYe$08-t`2_=3%Zn}iZ6seY_CHiH?TkGH|L zgiMwY=PPCJ0O$(phDn&H{XbRypwso|&JgBPKIWXkG{g2cPtHi)pm^c{aQ5n-7P<0PXGF=uBgOH#pWtlGS5aUR z61D>)H}!}N{fH3Ir;}M&s*b>_N%KElwp8Algy)+y@MiyWrgAAd3&1xw6LeRipK4{N zqkK;o@TVKz-J8Q0U&=)hU$QsDp5M7c0=S)n$yT0^hmXH|$1wO_OY!Y?W5m(z z+Jl|?MlK<|TvOVrR1T#L&6;u-x>QSR%tuLcWSz9n-4!ur&B%$?E4R_|GMc;Oc|{6I_vc4PWmic#>)el zVW_~x<{B&pOU}8Ews^^>9Z%gpV@4cEaPsIn1T<@b8tT>Qq|B%`Jprs7yhQltNxx6N z1RO}5Ke&)S;Xwp3MonQC5!zV19q~mP#26dt;F+z&h@32)o{!rhHh6 zbrE0qcvrknkMjK9FMiq~BGZGw3u=Jq%*C9z7=DYfO~9M1ro!I0!Tr#V$a!E2$qcj< z>%XycU*B!RGpz17oM%dT;kl$#gr)_0@WZzH|CyjM>f!&9L|;#%`IHln*%@Q!nou$UG|697X%Id?wemK1TdQX`qU}S2gDFj zCdIJu#}JCY{MYMA(~PvQ+A=KOqt)oXnqO(au8-AVz=y~$4wE+hO1(|UEs1K4%6s0= zrRfv)Kl^%hQG0^L7{$++p8|<@FvK1eefaDiAOpd8_P>_6Fu_U!>4$`4_%RJbCH8I}?FK@+}pZ`D^!rbzSyO8kwJVyu0RML1JGyU1$-A<-}fbP%dItJ=1Hj5$}E; zn!ZaVeZ_8}@+J98K{1d#Cf)u@9*#4!nc^%AL)I@<^2i!E#Y0~z*Q(vN4|f2SgWma9 zt*s(xM1dop(eVOa{qKl>;A9#%`QT5_VteDx@fh_^Hd2|hlHz)5Y~~ghzf&El5Q&%n zGf<8EI9kI3md(Rm#^x#0vZDZ7g{YC^qvHp%Bkn6uh&G zOI}al1Fqq6cMQ{O(t6mp*nbB0u$8jk_}n^)j7ZUsEU{W5l5&)3Y&bsr{`uiEAZPsi zQP2m>E7pVmSXGjM&f6v^fY`sU zX4QO!VEDbfcmxOl{M&d*oGbpLrDL=`iNRWzm?q`;RnHE1xoJdvUPGWiHy!#=L6YUZ{?K%u+mnH`(NI z{8xXd2l@n9M`f|nSShYG8Gd6u*!6XxL^l%%@4yPSGh0XNr3}hJ`YzD=p&=m_!x=Ve z6Aq(EqRx?7yj-K=H%Bfju|E1WNDZgChFS za6oV@40?qCJ`~r?32=Cr^^U&L#~5Qt{(F0=NwfE0Phxk5)nU5AerM8FYy=7%Vj03y zN0ozOrP^^X*NlGE@ShYhR-(FQKFR-qdx5FYFO%^@VDb}hJC!syrE{vKw z125As=@&9z0)6#?$=;RBZ3YYYbq+8KUbVt0Y9Qt0WOpYRlu{av>jB4|{ z$&yR(t4Ad1Eo$P)<>`Ts?-sa{uyd7t>`6V#J`zX9*M-Z02Sw5#1V$e@ZK)W#4sYCN zPn8aV+jk21eXez1u&$&Bk@OAdEwLtF*u5sHr*Q75NMqgKv8-AgPDFor=}EBNzst8& zo-YtvTCp^KT)-Ug0%2U(6;@5~zcAMX4Yx@3;6)F&%RehkM_oUL z(I~Z*0ymQ*Ha8FR`v;@8+0x|(_bYuq!N_bJ^^&BiT3hYYXRPw+b%{6Rd0^N+-_;iB z1>1oD&qm;$?djq_RZ^^v$}lnw{$dHZT_x%D0b)H+vBu<*8I^Q%Gds9vn(dZ01^u~t zYj-rz9^ng`PVPtc@~LIX{!fO6C4oQa^Nx?{H@hU=r9 z9(_#M29_(iZmXB8%;6B9Y_{>&yD=An{=Fmax-;(2Q|tP-i470KV;i0(MLOoP#r1i}z|9g?dWM z$OsoC&9IpcDAXXBi@M;b3&ja4f+Y#va)TrwF?V-JJTl&WnJgb$X1kC9oMH55#wJEt zR7ZIE4nS3Y{iWV{R|0VGsa`d2+di4<`FItGTR(B`g5rsB>=bUqjyAdh7s+^|fmK^a z$Y{5AWG{ty@>hK7^PhDE;Xf0j>EGouB;9VHUOInx<-9pmGFGEg-G+ITAS^_ellA1q z{r-uVxn7Qm$5D>#se1SRXM0Z%3V}Oa;Pbp|zTW%Q=$7-GS@F*4(YtXHr4rcNAptgd z{UKnzx`=f<78}AnTi)hbj9MBWiCf3kcS0+9G%c(jzysABjwK3YBA7niBUa7`@ac}c z14+eYZ&>Wjo6v7}Xj#3U}Do%}?{O0{@( zJ4Q6KnMq5OYxfq)W?X|2%j7NlHU3%o`*puH;U9qMyupKYcrws;q30@O+N$Q|T$>7MJy;mu@Q4ta80)Er?9^KG{aeqwungs?$ z?aiv?EU(gvK8jx2kpjaYuxIq+M?sm+A{c^rJ!+EpeX;-_?DbomyM6ym^{6Icl?M)U->wU9xw6elpFa@fLQOC(DE!Q%b zKZJzn=tiJzg{5-HsKtF+83L7Z;vtjMci_zjQISullJ>!CSU`0-1Fkl+I_F*FzOfQL z-r0QRHW2TC5mX#D`M6A;XF4z&g)x4!%y(l)TsS156|wgm3y#&F=-CL=hPLEpX#NvT zS9c`xDhgNlw3!Y2k+4Q?`WJSAmSd@$fr`mKbA`@nHMHH^AN28P@F));a`y z=$Phd_d(Bo3=ZRN+vn;ckDW(6O^})SJaK#`Z#!sX?(&H!D_%!>NBQ)MbfT|_G)wecf+4`ni-0jT9T5F z`_@NCmAeOZ>Z=AB%sDSnHUHWw>IxJTE7uL$pt~;8W4K(!G;7AVaHdWpeY9^(21%4a zO9BWRC`2}(A%mu_%Af8C$lmrzyT#UJ5WcH4)7t;A@7%j;4VkhD{R4?(%MNfF8g_82N>;pY*6#E(^K0Q4giBUWQq8o~y&`c@T< z82fYCK{xhn(=?#nwcwi@lEOAVJUrwwz5>Qg=-^=V15&wLB>|rNRD2}md`hTfIwY<( ziTa!a7#blm%BO_qprsWE?@sgc#X@d?HyZrt=D(^GfkJpga=$s>nqu#FT17TTCs*Sd zp{&iTWzoYbszN=~&%_N-Q{!CN*Gy38hgiJh0cMR({wtniuoH0nxWvTcV8PQen0E`t zQ0S_f^RR?KVPovrFSP9mYFXieGcmMA&yhyYe|;5>w|ya>qJRz!^$GiOfM8eg0e)Ee zv1g_6uZRX$poxizoD>wvO*I#57KlnF-cjyJ!)6if`#rqSI@qkxcNvw>MbXk4ON&$N zgBDC@wghhk4dMaHgn^6!AgkO-y_Z2v+Xk^i%0Z*@R7$e>A>o!%F0vD2d3_@*H$d8< zm=w$T`a5@3j}DDsBeuz=cAa>O@fKh@zVvK9dqe%|Rjp0@=&0dI;g#NIfriR4&>DN~ zRLC$f6=-*nmWkvDMV*34ZsjrskROd==Q^bd6qXy~P(8yWF;o`7$JF~)KY1x#Ln^AY zEAT4PX>4>gx0pfxXOC9tv99b>;cOs8GB=R+hBa+IDwE(THf3}!bHHaD$oZ)*Hg_r7Eq|UxLa*4p~K4O*^fgagQ?zl?&y8bQ4ppJa0U)Mc}I@ zPD(m#KA_9uHpu_`$_1Kt8hdOAkPq;~XCaq(6Zp)b=w$LO=o9BV?n$N4nFqY~w;*|?K~&$@`Ho-8sW zL>W*t7}#f^rR&h#&zsxaziBl2iGtkqAE(!wiv@ai&$mzrzXvh@ik=eJ_;Im6<(EQS z>lmb2B+Pl;Aq7xiV?~hgura32_`E37z|ORju^m?c_H475l#MSnMfZ0o9f@epYtbN9dHJdmSp!5DY_0+4{8a@h#BpE}f-skk{Qo^bU`trUDG`qwZHX z$LPssDz5urFzyok0>vf7bfcl5eFpmtPzxX;Ch|?}>>31@5>XQ?0b|kBW2w~~8T}zH zaWK!Lc6MeSw*Pl{o_JF;QM~6e1BCfW4>~(<3=gHQu@<@*cJ-Xn21w@f#*pHOaM z^GtpO+WZ#1VOf9_UI(F|KYWe^OblhOtId$m*q|PYlUIdLpcVDM303%hsaPKOCh?qQ zyk7ost_P@-YU1*u(ev*t9!^pW#;rHb3_E?_ucW8z9Y=EIaV-pi{l%CR=(?EVUU`bw zqkM2AA<@t}JlQPS3B>?$b{2ST!(quaef#Zo_RSmpAek}FJQ7meWs8Wg2-rZY1F*!(tUHxs_?l%lwd+`Fyboa+OY~wZ7G}mAjyIi%POyt5b0@D7zQZVe;&ev?o zLrBXTaBe^JFgAi?zdf8wd~F!pKtY>BefdCUFZuJNkHY!~?ayftx~No4czS)2N;q}n zXjZVLcCSBsk4aaZ+F-j?bvtsm*vg}o8*${#BQYV*Av};E6A`8h9<=jDGM{y89P_!C zYZ#)^`To2Q(5pp9hXHS$vIAKIAkBXI)+vz6#~tiXl#yQtlbmXf&IK$OG{B~Z;iD>+oFjJ zfr9mTnSPppA#`r{6_Kz;_{?%D48pN=V~|fZFGudd7`@(@Y3K`f!A(9@!i0t*;NDyS z@nwm=2DQdNSD!vRWWat^q-F}V2*8skt7Yq; zGR-cL23ZL%fG;Mfb}EB5-~88clwfn;a&rzs71tVaW_xq91y~5SF$xXrm$Fa55F>w% z03X23Ekc|=NL;R@KWk92II~k>#LE2s$km?{A1ukT)~#D|CrT_W)J{8ZVbU%t1s(~K zqXUnzR>dt2fRx=3fA>*Zt^~#1ao6RLnCZ?n&UbfzSVd5O%Au-{{vNP50jBe^<=%6* z9dn|h8}hZ>_uiL@O}E8It!L99O|VuMyP<&pCBPSs=3_12l>+rhWC&80z0b+sUJDc6&dr2W1=c5fG07!A%L z)(A~39e>VJH%J0u6O`9SpVe!4leX{w&hV%(+kV+2f_UWRopXrkSh{lQw-qM}|DN8c zeEiM5R|Qyg79(hzidrfoHB)JZO7Wje50W<_f1)t^&cCrX3MdR1h01FKu?m}zuyDM> zkBj$?${!m%gLXnW#6`n2zd@HGu{kPcX1`mSdw{G0dFl1tHTfIYtc~FU4$WB(2Ry^w z1@}}I!jC~x-V9*1>7KBG#G<@sLA+G4Z?o5gpD@UK=I!c!tazQ|M~;HR7tHNHLk;dituufh?=A^UWXL#T=6X*JMCbVsm= z6Ji5_4cM$NigG3%0w`WZ7?k%~>y&nWpTmy4N)!1zFe7zny1}}gN9y?g5yM19L}XXI zgp417{M0V&pYx&v84wu36CF^gc<=otV*Z{m=JhmRfYTPgDi;5%-4K4TMWBpX28#N6 z#=dMb*4Cm*OP2L?J`@uWU;>#~uR_Ef=f*=SPuO)BODSFWEzw00(lM*{cd?nP|q&00PXy)MqSqAHwoX+PcQ(zURfr2_b z-XU?vy`?~n1+t1a9~R#7ESRmU*8OMeL;SmugVq;IT`$5Xs00ic*#>P7+4hP@*kk3$ zt#ZJ*|I_re!KuUk=hQ8U+TvJuSy6+kr8pAy2D3LfP!celAmnucXF;rOq4)iM5F==M zf~S9)-mnPND?lw>@mk6pb&ADvpkR$HLm+)CHLi*_V@JTwfJ+w%bv(=?HS!%EESVHs zmCiN2tEBb6_9Q8p_c5&Rs`Tc3yT8U2+A3wiYn}F|0s5A|TR!=;c>pj5&Bl>e^rNtM zfQ6gP$`22fHZQG6dRS|>J;#b$%?y}H<7W&2=Z6X~XB&X*<4ngVnstVVgy+Y>T$>I) zh=T1<=TNBp>{w3Y9{C#Ku!?=PC-~#WBQ_^_KYYJ|DmZ~hvQ=P1dV5HUi2{kb5Xj_S z@CNWNu4I_4i-{GL8Jy~vf1O9(Tp06wjn5p4N$x}Q;rD0GfC{hr%`1Z_Iqo1J zP6AT(D=;b-s`OInmFMbQVo`p4jwhD>?AZc0yI&;g?_bHv z=B6IyzkO!0tZe9KR+(6_0G}EDe_>}rak%-1WTDytj*$$yvBdgglX=_1uQ;~1Uh~@oeER3M3%q) z@gZGCfRM#mr{W{w-x{p{HC^RCTK{Ir2k(eyi>i_QaQBgSS!}2A|RV)nN?t&b3~6E4x5@{EqcYuIDb5C zJCZ9{*w}I>Fbtq9kSBF%odLpBzP~4iWmJ5xZuOgCE3f+$4*~HDg+RYKVbKZo;RNse z;%)IfUn3?yp02hWt+ky7X{>-#NCn`!!WA-63UJk z4eKOifO+}+aS-VGPI;keY(UNrazb|i@$_|w9q9hOL&oj@0u7ZiS1$HkEv83DE#yuF1Rsp z|D6dMo9R>0I-IPChw8FHdWEkOP+YUBV;t(k`J4=0wYDy?*{*j6XBP1*rtlfGzEuoc z!v@hI5m8b0TJa?55Gc(#jRRshe}z_M2mc8M&vL9cY~THXVfodNN1#E^HvnOz&Z%*cky|YuvwMwF&4bKAZ3?hvx|`N`U7d`B<_hf7c7lo8 z3~45Sw85+S_`&s1&e7y}r>4L#F>{JSAxJ>VTdD4>B*>fLArJ3svH)^Y8^6dGDwMaa zi$Q!ut|S6=QWvdG06FQBAx2OkvIAD-PY+4n;6vx#GCK5EJnh-oKh28N?(bIzM9nrR z3y6(Z1vez$z#p~lS|l!oHs;UYuGRj;m>d2qyBJy)KimGVFyR^XHI!7@i!Rp@{MVq_ z*)(gX_hn7t7(Ju#n0&R(zR-SDd(9^_f!`*Tf&fAf1~qgg9NguPWiRI)mlX%Ps6YFp zV4@to2icl0KU1iZPu={HDo_XO{Oivw)>oo9*{G@}XwH@PUe{9{J?Inr(>pUh5TZ(R7M~d=da?NDTk+LxJCi zRcN-Jj!~XM*2HKU1Oa?<*?t7DS{N-OH2%}!kk|ZaN)c#Qb?^ebQZo4?m}Gk90rvS>5~YD< zzSDl87w;VHywQ5?1l~J1Ws;T31>n+?NinU6LB0kY{^EhH9pPJ_DyhHSt7k3Qfc@u( zH6Ja204b(-OI2!mx}3^2P}kceb;iy|1Moz?vY_g}quD0ub6Sl`TJo{872y1gO_Jrd8y z&rR)pz3^F4;HeZ4_{%mI|97&46a`-&c{e(GQ1MpD7$g;d?%HXg74rnlff1QqKabZ= zccvEZ95zr9f@<-icRJz)t!wfeqWnVeH zcUG}8=77Juto!_Vwd$~g@ESNrc+Hj52+ZSM0PSuI-cTKQsAeMqX!G-O%=x#tW}1?~ zy3SQ;3}L@C-%1#IsP3l?TGn5-pg?P+1l5I!qm;-{y7LAY@nYl|ePzGKocchi6u)S% ziG(~?Lp;u}{{UDH)MNJwJaKeFF zN8{RGoUG%V#V1y<)F@MAQ9(4`z*ra4;EVl~&?+!^5JYg(sO?3dJNu|q9->l{`5Lb62D9FS zd|ffm;`n3I%FWmLMBB$n^^R+Ej(bnf(|gnIZ_I!T5LQ)n2^Tj9C@*QvvtSWsAli*V zIzT4XPX$}nxlwXxrSEX98ZGScngJhQDT2iLdbE&MY!~F@v0XAYh7hQb8pdaM5;0v) zR!C4KKb+O5G+w*8Ja1LU$wumwEk*la6C&Ti4HvTaojjP**lx_CHOAoO3TNGT_Xlms zEtH<%JGchy@|^eYRU&FIEMsM#Drcp^+O_#JXL=qUs(|!v0%>Ae8H}d&bit)152cUM zk~%C(r0)5HT%6{#M1;>t4&SMq5FO&`K4n(NrKf5Bz{)6(Nu@qtcZhv~5*QPwm(ex% zIb@k2uC4tM{7vJ8r4rpuULsQZoGOmeVz_D0+V)7kpt@49Ft9W&cGLNY|I(kP|_6JulAce`J_Q)(kwoR?cEc@9rKAXKsVJ^E`8N%1W>s0ly zlzRC;^c3x$42-Fs;n}1h9A{=up`ops{fM#}K}?s&A9dew%Tlc4e|R*I!kRgh=-I9i0p4`Gs&rdRoNT|7gdjjI%1zL?k_kcWo^$ z)5?T(c;Y>&z#sH15k(irDQ!i4LwerF6Ib_BqBPp->ZR*^8XPIO!9d!4gWZk*4u$h$ zR)_yK2^UTO;!ObBrS{v};jq??jjkvW4^4+%ap9G3xb$$r=!$8*MxC8!R~O6HnSmbO zG|!j)%;24~v+jRsF*gD4{$9@aANtZ2&5gqM!h-wQQJE7 zO{xu!D$e(6^D-z1vppu$2Uu~U?%)F%iPQ(`laaBJcjg;i*%g+p=WYtxiM4^#nLwIN z=&2@afMr{*F8XjtfA}PWr<9GB{q0o{o!(Q|z1DD!H59FlWM0Q^F5mm>aIE=qUg@yc z*8iSm<@+>&YT92&*z10&N?9 z*98h=xNgLI)9SrY?6;WLLF zyY&3r9p)K6^ffWDFT_MW1Cf+dd6+_pECt8gU+7<=8Fy7Zp{G1^Pn$hJMIoWc*5*wE%zm( z=A$#H6`K+p7m{b;pYR)9cAq7*LQn$4Fojjb5ByIwtq*#BL<%=rO~J0T9AK=!$tC4u zbBKoo+K}iNy@Wv3vuHqLI47i~L7-&ixnVUl;1Yv?i&;^NIQ+d6^`hzPt zn-!&d@f5-hy{Q(H&7SDQRpkU8S0^?{93?*}xCsbEddG>F!>YZzcjd*-HbRr4eaT~Y zWhk5XH}luSEwQVyYnbbU+xXaPoVS%LVRF8Li$_AoB(U2*kuIqoqGhT4SVSb&aVGIU zepkAMq80p}iLAdRj_;Si=jSx5&w^Yqu)2nLo5{r=#g9qPF=w_4==z3y6}enFUy%EI z3_G;PgX**n=bjICJ76y4;YW0GNl8T^^|U(9eBD#^Qt_l&CXnf(C;&}ZLh zHvct0>zT_@Rl7}g)3`S&o`Dls*B`_+SANoOGH$|( zQF0LYVIJd6b|kb6v1)2$bi0&#jW;Z|7ShEmi03eG5e+IB+x0UrL??#>iT zO{c8N=UsUleUwTWrCzu#-HVbnxOIOvLr0EVyxQ07Xop16*GMlSsv zpx$itlI2m!hGIr{<942l&Jv1J@{2*WdcxH+dG*rKRMC3|cFK2*;PNa7Ad6OvY9EBgK3x1AY_acsI!8-W z@8S*@BdACHYQ`-FTG-jspFU}9fkbzMK?$X}=u`g%@|UVhV(tc?(xS0WYg?M1#YZc! zisQ{Sp6hpggLmpj1wzd)ncc?y;p|v0-x$9)s@aO=F3uDcIb%%hX?iUZA~G^)H@T5` zc`rwt8bH3o)q#?>_U2K1@jr(SE`WZY`-$(JLbbq5e9$-9O#sL>9QXVA^Gb&=O?_QC z%cb%>`TipFPSD95H2|j4j*Kaw4mvHSR8Cn){g1+>j8p+n^q|&;{MUx$Qw#FoW6Gfa zng`oKkA~-mkv{C>e^onQY9dTn9MSYr_8fx`UIZJVYDK4w81WESDm9KZig{SDJ_!Q% z(w{T7^jUik)m~lmT<%5ondCRyU`MSVqO}Yk{!V`I7?dIgL)nGKLq)TV6Hfbc#5SOJ ztwFkSZ_#i~! zyVBJO97)qw_&XQj{LIu8-B^x}yo&j*aN`2jzdi<2$h|a=50hl(Xs*I@0$GyPbj3yH z;Nzuh6o})uX0^J#<44VUa!KBIbVM^A2#%ZT$KSIbt5Isb<7&A==MDeSQSlK(eYLH# zJYvLv@tg=LRyv9TZkDNz*p3|-8JgOss_7Y|CHSlC7V$P{fVKvp6}nWCnb@#Sbzl_a zF^_iX!Si--@KWe=R4*HUb}p0e4Eg*~ir`u1ej9?%o%>~hO6>;|V=%YMW<0^08Bk!Y zF}F8za-zHWRie!pZc-)9GdpzRzb@)reKX<-)(6-1GHjvxMb z%0ackm;`1)rzi7I=RqL~J@Qi$6}}(vwK-glvVE%4XjYmI`nl-!PPNaihd5H5m-|}k z(+?XgXKJ7jx;Yk>I?csfSE{e%;fvmsqkYD=p%nSqbEKoWk;IK}r`zxZq3r)CgOn^n z!ncYcZx!+mSMHr?w7yqk>%DBqfUK0>N;ypbv$Om=>^`M}0mgh@)i&`rvm1DPsc#9A-q8D#r(=W@c|IJ33D1zN|kNOY9i|{np82oATZb zKeb<7f7h|@qrLfwq`IQqppk&2CnNtz7#NyAa%;+jyl-`Xd_Y!P)`%y*Kb|gofi8-@ ziu~`QVg#hYSZd}6znrNeaBDdA^tl6@>;HeOE$@A+)HY%o7_Ke=!;03*fExLVXRvWENuP9xa8@MB~Rd*-*@;AuKP>Ik`>IOau+-R0 z8=*d^$Bw3T@m&&9q-eZsI<@T5lNF=)vYIMjgRE5Ho3?L!ZqXR`)R6&xtE56wiJJ9w z#wK#}BnL9a`s@y|znAi$6~dPz*4bRX=j*UWH~6x9FYa(HW3|loXq?* z(!vh4O8xzE)p7Mtf60@YEBHF_A^`nNeZypUI2IQt`z;uh+*GF%ACZ%D(d>;D?qUkZ z)mTqEbZhI2pdkpKNFoax>ZKRzx1<}xKe&(Hs_$LJGmO?Q5Mq2t7LoMa0L@ZU;-FKa| zgfl#vLJ0(|HBj;@8v4y()-Fu?LF-Tx|D?(^9r}O{hnc;?@fl5w?0Kj+!(;Q%o+wE< z@k^!8SwS@M;!pGNqwb%}`&?NspJ#yo>gvar+%j+>t1@(+e>Oqyh!8-LjRXE-DiZiF z{J;0Zgi6xPGM*)MWzw^V;xHqxidMWM2vb8bNrT*=x7>e!&MK$x+O?2%(;TV(wp4x% zJTpmpNtU1|*l#6i;6u}3aY7{|?y z=ts}RMZx#)8(+OWjm^w-nzfG0e0LiTJ4roG!!p^}y&t;#El=gH`z>_z9X?l^7*D(p z7C_=>MS22)AlMf`Jn0ar%?0k%$e`W9-SrQboykX!D;?$JZV)Ns+7#R5%j17G7f9y- z$n~7R(C5N%>U2W?{+A`V%tRRzY|tm!$_!JK*h794BVWuWz=+=cO8cnCiN2}p&T$=s zD8k6VH(zlVgptXV!gq9jH#bkx_qp^~?`OeI%2y;uCRL8$pT99veJT@WLxA_uT2MV+ zg4M5Na&l7d^3t(Xrx3dyJ^^Bx)y<=KkiR>v9ya~_0jzFfFEN@&h+K#oiapJPP)QUV zK}0&7P^XMZGn#;ef*aXSbaD6JNwk<$nd6MKV#J-WM=d=;DNzo_ux`1`>sj3 zh$NfpGR-1;@dm_3?FjD4zKvK<_Y|Tv!kfMSj(g6bC$zhsQEZ`QzcJ_|pV%KLT;A-u z{|nRw-8uvOCOG*$5Vl5+gxpiR0sqwY<^&Pb`Mf(ugMI0v1(Q`<6w3j$?xu^1ml%YLHz ze?7nM`;ZszhlUGRxPs&@`S`xV7yke`+8yj|X;n)=M7{Ux^w$I`;pg4y+IqLckoKgw zu=hJRQ)XpDRZEA<9o|j}ph_dCwc$O$v;nHrN{y)KEYBrekgi>+$Hz@x!lT$D{K zZJA2_>afa&_yKaWnV)m89ZCLBX-K8gqycAc>@EP2pN>J=@qB%)|0BO`rL2~UvgtSJ zk+9$2SQ5Z3WnX_05{wVq_)d?pw<_@{y{PlOZ|MH;vZ!dB7)>HyiIU&67g!I210J^a z^I403sZ^EPp?mB1|L-L-_uPW7L}IaOofv!N-dd;dg{)snIgRnw$4f7Lb6i_HH;=(~ z?uNKfxV$;bB_8j1n88C4c*1DVHihOb`# zv?#y=+5$*HZkSSf&HISm)59Oris>^nOf}HTVn;517Of@Y6}ipnBhC_t^NTha8Hd?S zZu%(|7E<66m0<>R{m*9z+$xtbvd@{az!F)d{7Ka2lXOW)pp{iw?p@i+7llc?uh!p3^R{Ib4B{ve)EDWo2wScRS-4 zCHTamBQ~~UBtOcijL8kTt-UThlYv26yCkSw*na6iA*IrAxsRX!d*_+MO+q%UdT$p| zhXSiVosuwb8K_uqQ)$q<6Mnxm{8kz#3jw(Go@cwBTKU@pdH9V^792F(S%MZ)HM=^m z6mxz*%~XNv*)jI=iCl}0!)0e;79-^*0;SDCGTSSRj+p`oy9G zpVhQ4nb+^m>GX|4txZ~&gxktk08feAl4Q4atyC+_$pWnmA5acy_i4c4*EwzWc7*4r z3A!$mMMo2W6YyAJD5e&c0<6+=jrgJ$nV&5uF#NmagSpNAKCZ)hi&Z}PA z3a)U$XH+CpJbO>fO=R3zMe~rF{RT_adfvxc%%Zq@(3k|1l$rSu7V*r`bDx0SCh#i; zlCU-e!csf1l0L`8p(`NpS`t`?rs03mdB+61erJEmSEKH8N48KmAc*Sz%Saoia z2$p!c{1HvIgkQh33O&$#vr9`K2?`2=NYI?2xcGQ@X^-bvgP9;v$F4;cl}6CKTB~@B zr5aD~0gsKi__xUlqwVeO+ywYV)(h*Iwma6e(y(1N1trRk+7L}J z)^qfovcDE4GMsEGP`k%DYjOR!66$7)O{MW#B`(&9vR>HcG-?7jHq=igkVrc#siLlaANtt)#UMk5-K`d$eoU ztS8IO0nRAAHYBspoibt(h5=omD=M1ZETk5W2iCuLoHE27FlpqmE=rwY661jTB40eF z^!|6tE1zxgRT_q$+_eP^M%C*MHbgLae@H4Hs>(K)t}h~ssal%)@=CelXQEkbnSbrU zPmUi-8Fv!WI|$9ys5?`JtW=oou_#lYyo70rhr}(G zG@sAO8{bV~#@)k}sqI<(nPtrNl0KExlZ`1~1P=paq5`&t_sk95jC=u0*lQ^b#$ zwyUz+lQef4Jp2t5W9<5p>p_DDg6e$8wD(+7cOnO+(LHJnSe4Tuu!qRTS8 z&qzYV)QMNmMi(A~W<2rLI|V?zpkRD#%2qnS`D`_V=01?F+ZHPBDrC@VjWsu9w6*Q( zvN=op3frnyE zxzdOFS*)1LRviH(V@@yL!qgMYxa@WOYXkbi;UsVZ#iQvpDgH$qbhAh;g3XU9N*$G+yy(OMx9CB*7s#Lq~yN zRDhFau=noPGuE*ikNzV*-cJlGR+e;seds@Y9|06G~% zphQt)T*|rAMJ*t3loO?AI*?sEpcf-ps@>v4jr^rQP4HkTyuM1~Di}>q*fBBcogBq) zR#ct#asXV3;rQI*H^8 zl*fW!h^S#Kq$HF0|@{TmBF~ zYOAX5SeBL0Sf*SWvPodz_BOv#6XG@PHMqFi_uYrd$%&7S#)2PX9Znjo%Iailkwg99p*n#W1p4OpQQr@klr5|R!SZ+2bcx6UET#PN+ zKN3YOG$qs}U+(2avvHMdkoy3rJTp#cxT*<<18?Q=F_Ujuz%w`m9rr-^>vXwkzWcEc zDZa}L7bt@DKc3x)QS!c{TAr*hOJw(a&`DuDkS+BR4ALurx)H{)YMop@{E=@xRT(IR zgD!@KB`s5|K0v)1MJ^Z^*Qvlt5@8e;Mq&Tt{tAzHins^yY|D9VD=C;eBAy;_mfAxf z@b~?{o~7A+=ve|cs-q!Dn41^;GuDgj3Jbw$?x-yXPFZ23T6$e*BIbsk4yRUM&prM*NxlXsL#P|DqLS-=a|Kjt||L6I#(yCBt4vGLvs_m~hbW+=|^)Q{^B1QA?Nzt#LYgK&p zVbpxg{;2dk2o>`CfXz8ia5^mIM9rO_j2vfpJ+}NF@?@_#=7uwYS`gmR*1!^AA$5Yz z4R^W?RrjE>_^TU^gweo~1&BXs6 zi{$pFQ7}tTM84c`=zN|M1KP}D>lmL{0Y=eD+bjF6v!Q|lE1(@6rmeA^B3>`V(2{}? zlf6dXFbG?ndwv!#cGWpdk(DOq4q%iR53z~tRso7OlDGe{#bAI|F?kX9>o>(cE~^_D zv1f8}Lhrc@KK|w10E(#H{Weg6_1goWuxg2RCYpJ{IzDa#tRIZtXIEgBqUr~@5n%o{ z02H=eN+6c|sqK8JV`O2Q)b?{QHsK{Dy*g-4>J}Ye%!*@UUw4=L35Wlgj%=XxU_8S5 zKuaMcSTNN-zx{MDhG6?*d5ho#GmA696BKhO0KptM!E}n`fe={jCw!T*8iZ4o&l18TAV8Sh#;dB)Gv)`p`LSi zyvo?M^Qb)-#EF`-XqLHwl0Mt$`tIA*qab`~ojxzum$C^TA(Awt`Tw3A(>9Puwd&K{Ckr2+A)73pbv{Tx0z{bQoPrbZLA{^7=BIO-|{x* zRAZyD&d;o0?QNh_GoBu(J=DofV8E%PdH?johvn|#j@1{$if}w*FEr2>d7DE}4ik=P zlC0F%I>)fEK^YRB@bZ_dj+V<;7XT}zp0>7Sg+`TDiB?H}PfyY3&nMat%tvbAJ`@7M zzP>*6^bSS$w>`lO(OM|0Spd~I&@1bLout)}QB;f^^`{c@XM`C8MHb3rQlEeqod^kh zmSc>ef>3gJ9WZr^AA+&-G57vEhr2T%U;jI1b7*LNF|`3;Bz4!sn?|6$y99gvZ?e{( zzXQQw?>$oVo^n=BC_r96&tVYRO@jhpfqZ%@2yiu}bS#=69+I<>-y1q;Cs0%>a(+0> zA6C201|SO(!Drk_osN{jG;qjZyoR2zHfa|Zgg9Kn3D1Fa#?5};o8?zT{l2GTBI;?# z;1tKd__2f1%p0qX;`V2(ZVwL6j1Tn=Rl+NO^uCIheJZWu2JH9ABHlWH?pwgy5bhEr zgwH>9`xa6>6^cs_hUu2V0{;-e`&QcjJ4;9ZALN?HMDO;No*z>Cc5^TQ*qfwP7>;^;If>9DWn}L5eG%yiCo$s|CEvE6n=V-%jix6nDHjr`Zk~9oE9>jqR%kd0=*r55 zv>7n5vK|jTd|x-o3^NMr9zpZSX-eoBe9fCNxit=yQ!;~(2JsT_(2##Mc|HKmVryrr zh+MF=Z$&%=hj=QfsrESzpXcYc*k)$5Rxdd+1-s3T@{Bd28zqb21DnXHcsI+rT!8+- z_6ldQA{)H^hjBxaGdMa{N)n~;#0D9;`=0P{;#O%0EPL`z49t(q-l4f;j%Ga~bH z=^5!HVZy^GVg@&PY(G=Wp5<{qIA*rzY0D*S_bD6>i^W`=CHum9INl_GsdRww(NkO< zr!~${YsmhtZ~4dlC-`sgj-%&&8>~HszSC>_l^_1dh?b=U@;F34B(w!eg=o1fIv?oU zz|M}|2NxR0eLMJca`)`$$RTH_K(!DD2gkwD(bCc~MMY6U0``$h4{V`!VXm?e8-e^m zQv!~TqoibDw=v3^SChCO48E#X4<8VVBJiz>wuM7!xYdskAwUmaZq`Q3`zF^JZ&MB= zd_{<9dv@tb4oAcJ_7GQ<`6OSzptzWJWCox>xw*OK46>h6HwU0|g?0%fXNZV`q7+ix zXUctovzJ;8Ol}R6%%($R{Q})RJuFO2(OS_D#H_7LAjR=oDEBZYjKadYIYV!S<=9cP zkz3!QFtbDDt5AmVAREdsY6428eP7gmIBa-={IT2L!5#|R2i>_-A&Xe^Y0JtYomXJ; z;L@q~$I+S7mBChNuxXV*N+4z?Ca)ln$^peTuB4BD7n{}CAv zmh{ljA@A$6uKxbFo_K80&Gw}S0DN31Ez`L?o%zEp3bG(4X}+82&&>$P7+m&L*9y*k+*kq$Lpa@Cu(YH zgn+D1pH{%Fd~F?yx>;3Kc&y(EL!zQEzabimCAOZZs;Vj%;=%mK0~3PS;+bd3<|;>8 zNTGm7R<^syFbrbw@+KIeED=jdo%zt{^&Y7;CmKDXjFhwIdYbB_h_?qn+5DiL@`2=B zKcGMM>uANwSr)s1-ZMvfR`DE-7-u9)LK_I=P@Y{Jw4VcoCeI{v58d;51XgS;ydxk7 z!dSmnl!>BnTIrH*dk8-g<}?5x{@|-_@St|}eV$TKQknogK_$6RT^3j)0o~eg?EyO$ zABV?Ue{~fB$Zt?&9xO9{Qr$ppF{qsm4-4xO9Pa7q(NU1g{{3XU{5xOfD}o6^p#%~j zn?pPzvNf&1ClXD%*$YGtFUvJdQ5LG%qvd=Zo!(Zq^)0L${J z3fvV2Wxa8W8z25d!=Br8#1T>=l!{i29C~UUP6W7z@`Rh5fx!>%OMvLT&TRdc9@Ao= z`J0{kGNI}Ou{3=B`h=r}EaQp&1uDl%c`mZvQaFc;>n=<57dmK)Vj0kdeYzPC<)8#e z>664ydm1orU9P9Ktj}Q)?}~;B78%=K52O~FPn7ZsORYqLl)S&(BF-a^g9U^-*S#39 z@~!oypoz}fwP(~_p99{_-9ZjOgaL*dU|s5bPmDGOACDk7U4`5SGX8V|ZYBP+K$PlJ zv38i4n1JdKQ$+))wL;|{YV*&aXmIF^ZaRTK8ElVBOl+t4y@Euiq)eNO;vP^HN&_?5?12<=;xSae8Fn7oPUyQwFRMzSCKTNlXl1g`nh=7!I zcXtU0w=_tqGzf?i0wUer-Hn8_lz<|jfFM#TNc}hJoHOUQo)^!&nzdZceD5oEd}3ef z_?1n9W0En7nG}-|b@`?=kr`bW4^2$sF`v%!4Sh65oARD0$8?*txpU&|1t72Q&obgS z_+4jL=$Fk`K$&I;K+fE4;0NKCES12)*mmYFZ9q%~6&4Nz>R`*^Q!RLY8Z@|&pU(g{ z)w&~z53CRll88&zYAE^H!B+M2a6ZjHbZF-T0jI|=MCw@d&9m)f%-!8rL8sdSf=W|a z=kv1%IaFfjEh+WXh>fMrOVFb-Z`S^m$1|%rUIRjV^~+uiJ`Qa2&{0@hZz=vZ%9%CJ)KJGMTIm)5qEpkl(U6F5J*#-Gf|$ES^8%|Ux< zTMNGunXlg;pkY+E`Q{B>T7Raj2$eXT^za=)yHoRDL=NwC| zv4kO~K#sRrbZ22wr}QtjJAIfGN)>+W0sN|otP^(a#ph1T%9qMaN!+p6CWF(ZMw1g0l9G~7 zWwVo$B-g!{w*M(PQ3!FY@TAFQyihACu$*I? zUXZ^)3RvDBYJeIYm|y?$7SsjrHUh8hg#ci&+_bgpf#LqpCMqdu`0!wSXf?m0A^}>P z+2IM0$=$j?jixsdTSI5xvDWrT4Okzvd4|$;MgL#FQd@mw`1mL&_~d;7Lqm^jbwM6`O_)rvl)UzAtCv#%>eYql85P}rk^v>SIZs70Hu z0hf!`MY>W5W z%B?vFw^4cwVi&j`uoqk`R2yZYB@~VWkW=>d*ZePEEmug$aqanZrBGaR3*0;{Cwo_N&gS^SZ&>G6W zotcrED%584W+EN1%m?&5kSn*Oq{Q1>qJcZIH*0bOb_tLw1qFo+rp)5vVrG?B47EfO z!EfHY>FSa!%#Efp&o?(?Wo6~vk_x1tdM0cv)1{KTWF9Q%p)8?S>6%`aWhvBS%O|$x z`hOB7&0WSHJOY?1ZCu9AR^u(>ae95m!N}3N?z!4@wG57e|DrD)Z`be@74K2A(S1^e z8swLQ;Tt2Ez94e^J$3e3pHC1|+qXH);YUqs1?v0j}w;^0k5_E}(mJ-dLT_&IQgQkE|bv{Vz@!>}vqpgwDBLWSO z(L4~;db+XUS!Pr(ULn5wyW3Du_(KRvh4yfOZ1fE&_3f%kn9a;@g#N@G%ma?tWfKt> zYT*U@Xi#5Wl#7Q#j}f+lSfaG2zZN~igdQMXQ}uln0kPkoR4nJmxM|ZYB~eeO|I90C z^&-}Z9$6)@?}|__cj+`R`kj7%%yLb3Z~HC7(B}cENJjjh3yr8$bqysG5wHo@qxx*B zkGeci$Yf+>ptgFcb%6H$`}c;`Y-XhDT73@nFo29)y;@!}pvaI2J5Vk0cP>m!Of0EB z5Hf)+runYt(QHyYrK_u}$SF^d+0Y>P(yY86y}(RW75|X;t++vFoC_v7cJm$arHLu? zV7;otmtEZjFY#|Iv>OmKLtoEOi!E{dN3OKqE41tLF_0ctncZh358y;_HyHALZ#$MH z$CGOOG&WICHt1LIb2sblZaAqtyt217<7p}UYMz|$9Xf5YBGj3bBX2BcrU=p8 zBJgLz#kcP?CUB2Z@GS9TNv|XSZ2)-@0N#D$HV3@#qsj8lN4hB4QxK+N<4Ey3+#A)c zwIft~Mr$V{fsBLm*~M(~@Ni|4%7;az;wk{B^Z9B&L9~jfU0j3In#ZNTw3eQ@@0fD- z4XMpzb9%rsNkupWL`IW&Q2_+HrT8-&ztTbeF8uXJy;H+SwFRYSMfS6u?vKDKj>T)B zru`{cpeuU$v;2OHK+(6GljcFEwM7U@X&z9P1Ucd~aZD^G4Q~(#z(n~w0-mAWM~Wuv z=^zYJSi@Lcx}fsRzXR%#Lyjh?4v&A{Tu7r98U=MlC#&b;ExcbxCe6BWlLC$p(b`Zl z?*wDvJ$twuehkGZ=B0_Q(C(19<#?%j1G~m{7zK`cDsuD_yIh}*>IoYDGAM!q@vyNq z?$3y!weLAzIzd;H-`4SByy$y(VwbMZ8fD)CyMpk^@u=Sq&|cFpGK$@};RAq)$XXZw zHO}dePl-NGQ&4He|8RT9u?M%z#`K_UWl9s4(K8kl<(P zdxad?J5j91{jjUsjVwyGYMU!-4pIeFV2E0wTeozsx_##KFf=jw+Kekvj-IGqsBKtE z!Y4Prm^f@h&s~q3rekW2vKNK{VdhGSZZXi@Ny-HgyaCKB_}ec7F(&M_-}E)L%)Z2z zoi#UNkY)}(b-rD7sdMLo!5ga7xS@u$P0*csn6mR9_1KhcY6IIoG^Q1PP=(OUQhGAH zeJirI*)0jJiCCRu(9=y{Gt2&a{H!^sHQ_zKxT=hRId>RgTcZ3`>6COkdSY z^tyxWch=bCb}xQdcf3$bI16dPg;7uM(;aQ;!KA(PrSCnG=)&T93F`=(!a_nbeA?p> zG3TvW+MD;TcPw%k@VcFy_%5-laP7RAqsTY;{2`TAG-u(W!J+uuP)Un)H6)T; z^As`Ol^Do-^`HXT%o~$JqygD7GX1d-&OBrTu`@67;41_N;s;0;n8CcvsxQXSQxsCh z$DE_3H5eHq?~S(A^?g77<`fdcFVGZdj&4~6Ixop10%jMv#Z`ZBvwu5_u&k*;;=ja zsjuFo$=r`^rSm+je|#!=)iw>ZK)s}Hc9jQr*jt!Z$|^41;<1!Y=|19agc{gW@Z3sD z@%Z?ISujCD=?zVLZ|}23Pcx&ilX<5Ppi-2Ht+9OcNHnPzI%-Z97OWiR$0yiv14M*` zL8G(Y=oxVq78d$RY2A+Xq{PH_eAin|)!r;zrtwp^g>gik=C(g9^M{d)`w_JOn1_ai z6;C0m=e9mal7B)SdsrY~%Ug(!tttL#DOpiXEes#6SO$N`YC%Fu*O6vew%5L7 zC^BlpJg1PhNy_$WLMsJ1@jluTM7i)$*8M$YkT$U0x^>MlNyLCfpSf)C;ddQe?<{o3 zuX)LmULTvdaqh{HIQsx=%xzrJl}0ONiBECJF5KXJYr%YeD0SLt!d9|waFmVMh`bCD zUPpgSXx|Z?P&1Btc>z7*%OQwBi~pPJd@M8VFY#z8~CKw!~dKk+gM>KA4wnx!=RZG(#L-usPu9$Q$J@7>@E^CD5zy3U4f6( zavhS-zYGkwyAVDMNnN;0W1I0@{rR<7w_1GxLF6%A*%1s!pD(l z&tSqfKC2&`=^Q>scWb_9tZY6#A5UKEf=rx+8V0LBUrQggpbk6-tL}np!+c52f*FkF zj4gY_$wGBy1JtV3)i?X8oi6l;U=Cbeyw1+>lWi+S=1=`O^3QqF)BC9i#s`}bzODDI zp~tUeudnUCdP=?~p>aLx75kfw$!1!-kf{H9^gz6kRIDp1{{sdM&79lyzI*pBIyxFa z1iyEx{(y(vv$A4fU@*V_CK6D#`!i+nh7m}}3S6?%(*6m*9Cf;znuN?aJidhU{DW3N zEE_rrXQ!ij$o5M^DL1I4R13jsg}E*&DJ8Yr8yDLRDN$fZ2Gx$A&E~F&sl75LeN^cB z!VPqaCFdC3Rg-5?5#A`yVPxAqYsQa{zmi{A>_R0S=0jWeRSV4_9NmQ4@xln3*3)Cl zZ~W8e<5oY4YS_v^-CPRHz22Ye3@QGIF>ktE({`MVeVhGGv9`TKLlNVr~0 z&a|SxefzBSr>W(CPfn4r6o2xdDtq9a27Fq|j5Ed=^YeLZlz<(|GPjRc9QA$HcsHwv z!*ovuyV;l6w^pgTxn}S>aUNxELFsGm{t>8JzNEgy#5E}-_ub_ydTQ4u_UzohLvJ?j zZ_^uLppujE$i}SLGXJ(2Eq?n>Ta@8oFimiRts$*|w z$7-^r?Pzdg&qgj)6>~WzCk9W2IQVIJI4e7QoME&%X2D>K_kK=-Kl7iGiZwc2^+)Hq}NnfWR47SE$?^haE^Oe`3ZrI z`of?l>wV+KD^S%32iF{~XD24V&~@*(`=_u952>Qec*5v&e_3hlapR{N2Xa~d*tnIC zUrM~2R>oh&=-R)9ytDfMi#{7e>Xg2q&$i9oU;VMI{ie^9oabtmg}=S{v3;5d`eOY0 z?BaLtI8Tp%=8V3e7CKP2w=W+`e11EnyA<5TtqOKBx`x~37fc!h-KS4UB;08jW*@xn zilTWyPxTr0Kd1OMF4I}7na??A{lY2?s82d&*7^b$y>~!j6gmDHAz@rh3~en< z0CrHP|LAKk%CXBtq@?#Rq;xA5RFNDWMVf^tEwgs}=GAjcec>HKsbx7i(qgz`My1mL zOc&;g8y^dxz6^b<*-vD?bo=&g;9(<-j0DY>1{#7wOBQSsq9W$>6G_P@JD(iW3NfZg zN-lqEzq8QvpkRrC<06p|7^CShh%v{UKxYBSAt{&t7ty!;Q?v`rWB!0p0)sB@_v z3-)G`og~b&CwVn0spHO2u zas{$=XVL)z3u-%Nl0grDx`g%8aBhiXVg>f9&l}I=vMmMPkvFW2M4Qq=W8L4!`X7l# zlCU2Byww{&dhT{MClCtTrMnpYrSxSUW(w*1&qm!9H{h z(CkD{*|LaXN%s-6V3qi#NBjIx^5H}~4<#^y-!bA8l>Hae6ZC?|NV;_Q5?rZ|LLd<2 z<>p%Oj~i9Y*#n!v6sLfYKp_DX8iZyDRun>y=GLkzUNNz@YXdS>ZMFbWgZFB4!SWs|`)dY&`zRoG6C0-D?J)P}c=Ib~<+lPMpw?vDNx0`K&(u zH#!KDUAa6yw{#9gsB`IGHthcHS*$$QYuR2A-;MLY!yNtH&Iy#%i z@QC%(i=a_38}$QYTUmg%70L{{m?*73o$V8^;ML{E?DU%sHJ`bQjZw&VrkZU`^?u@@ z$;STVr>lV-?A<1Qbf4#q2M}D}Wd0$zUNWK;66j=`?FfDO^2O)-7wby>91?~Oi8Qu2 z!w7L%KrBtmnFnNcMUXu!wc-XC>U&j_ksIiXr(Pu`6?$~p^B*7G;An71AlR5Htwi5%Lc0@0K`1O+yf`4`M2v{J@3VtF7V+w!9V!Ce}ml1`gd}Y#rMkv(W0#TQ`N{CEN99#iM5}Pc_Np4ev@jCkcxg$q zAd$dp)YRp=#nn|+gF{1)UVSYtEAtSXyxG=_mgU_)FaR20Ky<)bP8<>%NdTK!TJJu zoo;z$MIt>Y$1R#da9dnaY3?F0b>zX0{3b9+>}1d+&!T&T2%eaCKOnn9Yb^x}TX{Fk zmJiWsHqPyZ3qCQ;LhZ+(CfR#XSrS4Yjeh_D)mP;uA-eI0ax70*o!6O?9Jps-jh9?1dE#||o0eTrz#<~jQdXvlkO;g2=xqZ><~N$ES=JLP-Xz01d+hXvk<8!t zHqdkk<~KIg$7;57_WY1F9~5AT6OxjUC~}5$Azd%~t#@Q={N}%c{~s>pPP81Z3|q** zl@;R8{a*xw36GYR6M&dTKb&9amo4~{eZPMRsm|4e%3SJnh0gaq`dfsZU7>0DWe{2> zDbumKjXmT+j#msBkijY7itN4m`wHB=d2MCB6fDSL`?n%G zm{OlT?j+2~&wt(1(=d3A<|GK}fQRPeMg?VMOt>jspvg!|z6VPa*((_vc{s}@1HG%p z2&gK9I@L8byS}m`?%(eQ;1%1<8d;n2z!=Us&N>Ah9UU((@0gtr;aXv< z0>)X?DdJV9JZ+YYD;llIm(+dZ2GCCN#PT;yjKjp`FKItUle{W#?c=yCltLoK7Cghs zRoZ`B=hqi04DA21mu5GGgm^hQ8#6y1y z`1p)-sJS$8uhRH)kqVJOIY#s15>^<^%NK??=yx|Jay29Uqv@Ru@d}ARGI$8AaKw}K zvGwrz`tnr=zVQFCspRMM`0Yp-R2MfZpfm^_{ztCx2*4TB$KFY+LPU5y8+XyV?f`%< z-K6{1R5Vun8@+!613!GfM_kB9FRE~UfAB-* ze)Z}Sp;OB)1|5%u-3H-s8OxXZ8MEL~div}cnq(4?*EHU7tL`%Y0RF~}bn4742%-`+ z3Gaf$g*t~u+3dW_tW5^RFJSbR$m5omo@Bp5^0G2BX_=Uq=;@^p5(Mplj;zp2s{~FV z9QrKtw}Ti+>FMYWd_>Fk2STQ>3#bnP!hb8CUZ$HoZ1d1diM2wXW6T+@kQYz_o9<1t zQlU%22BK(!ZNb54C9-H;g#MHg_RotOd{HPg=+Hh6)Xai`j!Bhb`-pW|J_JiOd50+x zjqqBamcw$jxO|ejx1G#3T>-qq+IbZ?^+s~K6Yv3UjR5}*BNq18(#ukXlI<5x;GNrmkrl#AI_Xj8dX7_0|t?0_s5ceg#dR3cE2f!x-gKJ!+9QWQ% zPX~%Aamvv6gYYjiv$wZbb0|yRe!lKfbvRaVSV3W-gzz4YvvfE?FeDUM%_lj;$%*~8 zn%B{Rmrm2=`Mu!a^$(P}UB#k=Uaqw;>qj@B_!Rpa_xL$pgfN z3y^#mK^Obwy!!6}bW-RI507SBe?CJ)zIruly6lnsjYz8n^gXP!hd zWIr&)vJ^f-6|i&p)$*0Y_A1K4?piq-g95wBz6E!!e%K)|QNKs0s^GfK2`>ht10I>P z(Zh#tH_OX&g){v^alu@sk>;1FC*qcuWzswiFusd5Mn3AczkPQtSVmFM-@C|l9qtM> zNCQ~AzW=eU?-?a1wvk)lHT#>;C1SS4Ry;ROHI*D=cBQ27OfIF^E;lyoW+zD*0S>=E z*TA!HI_F9Er}&d8z2mMf!`OUm6CHYA89MAE1}Z{FoG)z5_nKt0+rG7p_b5qL3M}j} z&-zD{f}5pmw$=AubU-Pl>;0X=$?xgBGM6pQ4aW)JPEE;3OD|nj*pmherebba;uLlu zhsSklYHBK~?NTUO-M@o=8#FfKwhwo$a_D&MCURt(pZ)sfBPg3c84?^!h8GI&jlAI= zBv_rZwZHBZAgBxRmzcelLlMQBf+Brfjz*-j%%s^6hFhDMa6${~KQ^X*@Dz(AO*s+- z6c+#E?Ev+?pm0c+5?@6=#U0Zv8#Za@cfkTlD{L_m`L_LdPvvcToZ`;=^E9XJdR@3V zdAGe9mV;tdZf(NY2iK8i4GD zHMEf``i7IK9|#VY18-z5ztwnPZc1BQYjk11@Acn=LYlepI=YC6Mp>_nN*>>wZa~zAAP&Ot*RNk=>bS-%JkT1gnzL7z8MFFrFeN4-v1~Ke z1sqf2?4Z;W*(Nq723Zu93w;Z}KTc^bFPp>siJqF{ zSyb{L)Ne$d%jiePV0?A|k3S}jo;>1CFYtYK^TT_MRHJv&QJaK4wg_COFW;dE<&}6& z?f39q0;iIqB8ghM*fZkia58uww>;arcGG4DWV6##trOXDiAgsvHG7sfy5@%B?0_*9 z{2Hs?FA_MNKnKY_=eE(=BKdck95L$1l1mgLKA17-?oXR8 zl^T!*Z=CP9R`^{oiQohf?0=mFS|{<~!>d>y7r#uo**a8gKh=v{#!q~(Y|`u@_yEx? zf2&FlO#xgCqKu3dUBL`HFCoKaQ%%yYGODG&S2^?kIeWLSt1E2Iho-&`jME@0U^vFS zyd7!R`?(WQTclmIG!Vi}zjq16lmzf8^^Ns#a5}=DqpFVxcSp8il(cK60z*2YBMV2Wm3w@9AA!pqL22#;t zeN&CB)M9CoM)i$2E+p`H7o7UWjxR=HGNO!^UVcuq&90X`;eF$qCi~t@;`Y5AoZmJb ztUTgG?oo#MKh}zuD(q#y{S!$G)n1!e$^V+4w%QXOJMS2IQSzMmfXfz6Kx8XHJpqqD zSc`>Io*(Fff8sOp9SvlDr^oJJ-o{u3h()L85PPq=Mui!{|AvuPm71R5EP$CR;H$ih z2nu)U`r+MCwIA?Dz`!iz=3CqYud&S=o@luP{d383tdH-G_MIk934DiBlW{C4Ky^+4 zujOKNt5?1LgQWg!dslafo*Z)Gs<~&HZce^ieQzrncPUFl9h>p12lrZD5qUPW#KI;m z0`e@~3irAd#4sN(1}ZWHR1kZ_`mFdUhv9O2AAfzPCN@X)cZ3^Ne`x>`pPz`cIW@2@ zsDIke3o|Zmpe+Sd4xJ*4YNWzZPoC_YTjr}sg3Pkhe9V?FBqRhm$(SMT{(D2iOGSUU zs^1pJON!4%A1FK-98`SRn#B!5qw>iOQ1tB_(dzkAQL$qN><|EfIu-@>L1afjL1!37eOJ2F`uz{K_qdP9>SQlR^Rgwv7HOc z=Y<)2&H2A(Y%A`b>m5J7vJc>6L58J5G@EABK?KxE8JwFl+^bxHgZnR^-6^Y(=s}enz5_^d z&gVIw44Iho!AGax&x{-!u_q%`WbEwh^!3TaXmg05LdINck@fa&R0c@!D&SV)=Cy_n zmHH@oQ(GRs2t?6<)t<(Yx}CZSs9V|M2W|Nz-7uA>SkcFNptC_v!cU}7FJ293`EBTm zxk5vf#Ag@#R+ENcFm%`k_p@Kx(e(yXEJ=d*sU&xq9lR!)0@V~6lW;8ZRb~e$Vr0|@ zh@y&X=u*)o2NU*G*hdx`U1yo@o{@VW>39QoXnuFL&+ftr< z&i{B8kU8_o@$+q%nPh`JxQqqS4_+qfq9ORc+c$5 zD|oI)H8wVCDt%;zz5wWIo%LXR1ukHnlZQVe^+!vvTUlEl(XMHmnO%PL;vfGgBO@cS zy02n@ZgFpW@*%!@{a9&|GSizyp%jw{({i~-QZypc4v1So_IR4Ym{SYaO~E)$s;Jc30-fKX<+3|3a{L;xz$+CG%f(Sc!nbd=J51 z@g8ObL%Ec(O7~B7#c+Vd`|apd?aO_Jas@1b(rL-rg7Ob`yis9$iz4IsrdLk`*@_UR#j+CTPK=4k9JdYhMt3bH)Km%(XCWU;isoUhpqCm5u#*X zj((n;F@L``F`ct`RwtXC#%CvebL4wrqI5gWlc!Ig!Y@kkS7!kU6v|$JO!H#dSg-1$ zoQX;5fK?!Qu$uL-n#3fq#q#VoZRH^ekK5vb2jK#hyZus&R}7u6zSBI~Frq^a5G%#p zDt!)^=7C(IdtX`_tqmDJUl0O)b$dx&!l~`ieHn>T$EAqgVgyvtB;90i7*i zbXOA+#4^VV1|rTcU2;9)CjW&_)$cnVigzvWw#=ujx;hZfV`}2=kqBSPF(Q-(DfoaD zfSeKX01;zb*Ba7e-K{kQ1ylYMqLbu}%}ufWRg+nJ{xLOJZ5;7$`M)#B>IY&k+#01A zo$^3&mMg30`kG2th&pQMBnbpLL$z@(B>w!WDPpoTC#VNl5+ko(t@XQ}fG$2dk$x~? zowUK$)|Lz=$3t|xaQcLR>4bv=P!}}`=!ezP&@VuSZXiTBNMem&d~J1_uLFuz*ELNs zs3-`bEHCf>itl<9;Ec%W2|q5sMciuJ9=YS6#>33aEFvO8Mzt8k#mK;brh^O1Hf9Hx zhWJ7~Do=n+r!yCdz`_ogw1nAFAdj(H`4d^hpA)3i%zp?S4Gi?Y9_0hd-q@iP{Bdq| zK0dylo*pYJD|t?=E?j7?f#B6t8drwBm6DW1c~GhK6S?*R1}nv`W9=^V7~{LWM%0LO zJCo^w5aoLOZ%1zDNftBQY~-B}=;%Zh13GM~zkit;vgcQVFS|ds;SO;C zzKjogN9%j{;(Aqg3wgQvgOb%b0ZPFCh6_SWGz+3v?=!sb$19@SXzJ@fv3qKhk&)41 z%csaG{;%o&BpG{nSL zu4K+^=9=J-Tk)cI%;RK=WnPbxrFZw2{<#`mrGwKiY@C}wM~0j#4(G&QlnuuOXQZE= zfGP`~fTBag%VI#jxB&lO%VhuVt5Y0amT5YGt|sAg(uxPTJQ%u#+JKrv4YeZ%9w*4O zZ#5f&7ah6T+36!wV-w#iaK`Y8ryIXT)IN6n0G=WLlXGP4!YRb?$Vey~Q2S7LAxHCl zzm+iolGmLWO9gd3J$}iu8NyYUq9+B$NPg_@G4F zKG!E6&8Zw~ng#}Tyh%GIlmWpIN1-1vE@NKO(ACAL<1WR66{1FJt@+L1eH($gX$C;_ z68*}qNNYA+_4FlCi2xloVtp+O3tAtkK=?`CVwWkk;RJ!p1ZiGR(bVGn?3B}}4zx2@ zur6zun26d5JqeI->Zdz%d7ttq-;DY@9U9;_eesQkymo8lb z{VYgo+X%5~iNUD_F@5AICMtm0;F3b*1#x)bh=y}dm|&4Wt&b6ejA*W?7&EGMsGdW4 z{838)S;r98h3|T}BGdsie(%WH7$7bcq*=sscF1RPMnp#Dl1Ss_Xa#b~uwGL~ldPi| zPkoq>E&jVVyPbdWMACN{6;!bclZIASvN;mwz;$xT3`9+1m3NyjgcRW016~IzJN$c} zdBJ%OKYAf1-`g|bfCNJM#U7z`X}w5D=;Dcgb9Fh;3Hh&rE{a@)g^g`*A2o6;079ah zo0|*9+$acQRFssoil@eKhEaa-TXZ($Op<6vVuwf?bPZe?Biy$>$cE16_8~ge zCC$_1MyFqUeL64E@tx14J$O9#hCyY5;bz>!JEK1bZYC+)jJ!$t`DpZw@YU(OI zxX;Nw_m97QYjOTaHk|3V;U2WNN9B6st!e3Z6`^s0-j@L?t zE`yOnlV)cQz0{HErSr;!2U89KFgo$Wqo30JaAEuD<9jGB8n zpLjIw(WCjgFoU+zd!ru&DdhAe9fuOEIWEhcFILj$w>@5iCrjq21)NL=HT_f8xewSM z{a7FW+WZaiD*5^IYlMD>JCpW0CxI}|BtDk+RU5o#iJa}iK^%nBO(N)$bvSJlCT#Re z`s{nTwf!f@;#}hEc;N@%Vdr{!IuA(CDAbZae{*{9efnY}{mxj1mF39}$K`ybM`s5l z<=NrAGo%jkG%hdRn>TM=S8;G~IE+N=!r1hE0Z~e)z6F{7&Yg7CN9)s0_45N!1=?(- zx{;l#jOa>SeLX$;93<4oEGq-C9T3WBq;8Vo;e-T=MMg$uXKD%FVk@wty;xrj0|SL9 zAzp9RhGkG4wwzt^H}Cv47-@n-gESE#PM~Jjo9iSJUw3bS{hQOV&gg{RtBs~NgYo;n zzHf>;E)3_;N@3W4NP{=dabei|o5z$lnM`>~5BEoF4Gfs3DJ$;JjF>&r8Mx}|ajb-j zabVCerVl4~xwi=wlmETj^MF&A&FN2(Br2YZ2joun6Pr$XcdD#1t8>3C<*6vm79FAw z4H$F~KV5yLn}Y)E`b_-3UbD^cqx&=OM?Z6ziA4@}r?1#eo}8AJy&sG%XLtvJX*4{o zGA<-q7Y!EAUjTtXG&HAdnN|;2xz^$3(<2(i`!lsfTvz2ink0ypodqF#n>p!yN8e*6 z+tMb9?q=TCVmRU<@Ev6jqBc?@mrwPx;=heo3uO9@U zb67ExwXqqHja6=dbb#uPskUibfD-#ND$&)`pPr8MKXytC;%58PU0g&PYqF}X$tEHq z@@?Tgh}|-~UeEX5Ok$=hO_?bv=tBvMQht2bUkmvSpTl&Tr=<~Q6;s8-_BmR4x|0!( z#U|CC+ZGn^_`~Xq)OU>jbLMp4N&L94IJllISO2dS6K99G3z_@g5IUO>u?#ju-re0@ z$l_apNH<{gW9J}y^Cgc6qdwPw1+gRKTus)O##SGZoWyYH+~o&^`en7*;$^cd4NI@g z4M`VI$vVH+Zd3ox&cUJ0LUiE=`5@#?_WP7+Xlcczr^6lh7l6GA^ipIm+MoE`-eR>8 zK+IV+!TcyUscfj_`}DV~S20W*oZrmFs-Nr)%>XC*^#$u){jxHpMAg^NNymBX7x`m9 zpX_wcFE+8h_w!r(GWGd2RkD&B$kiqq_hH70?#NYF@cNaOe^A(OKmDOnYxqeBs|e26 zN|6pOx9Q2JTVdZ?_LbAcX5eMjF>2o*C&SP0w5`~0;3&*n7B7=-?R?CSi@gdL{(OZR zQ!Z)(CP3alSf{lgE(^-gv`FE!lQ~1g#mSAKXd|UMjt#_dV~t!{;Pdl~(Zy5#bBV>t zJdDD`h;c%u5X8-bG!G#S<537g-ysLf8biCU#}@Cf)o58if0LIN7Em?6+YOsHx3Y5F zrn+*KE>YU5RkfE>AFVsIl>Sc16ymqEB|InbO^llPb9Q8GNq#C)(tEr~U32J3{EAe0 zIXMjWMrbs27EVgN>I7B(t;Pmhnn(j5SxXz7KfH4p%`3lC<8#_MrAB3}Lb#Haq;zwU z#P?)3Bs@F_e6m&cbE3n;ui>ek3s0xqQ!>80eWmsU+OOhLM3zT{<9Kzz~jNfGjm^1EmL3+(u70VS~#x z{&m60n3)ZC#oSmrSLm%5!cxEn(6LFJi+4@97InZkj5cYQ?WV((Gc^A?kaOMu5UTO@ z6Bb%gQOfMMC}L?75jog0m*1bY93{cCjBM2T*uTE$UA3LaiGgEV%NS)CnLt)Y}AYYX4dn7*8QiKROQnbPAQugpA}ECfX5$y5#68N>R6kx zfwZJ9V%|JI=50F;&yQ~((qm(@#%*mzQm)0UxG7#sCmmKqBx?I_^$=j&=K?~8OFTuHkW!m6&H5m$fa_ajD`RM zGR!e}@3Xmc=hUwJ`B1vZk>vRq40twOIc5SYlf!75@i?JPH20qmf8x==+_S&Gzp;nm zG!S73Q}W^!8Mn8Q1xh$;n#daQe@F_pnm+GTr6Sp3i}YY>BVyGUYjl z9;*ah-&whMdwcKs=+MUB;pf-QyFFeIsGP<&`|`$J$+z**DcBTPNDi+pD{c9%uM&Mi zzsOhL>}0TFpZy8`S=WDHqyoL;Wu{y_W$9ehZ!?-ET3K1S*L34j7wTLa40muZkGM4Y z1U@4zlw`ribwDQKLWPt?#4(turEXv_!28Q8NViyr%S0a9%r8$l%x%ilSt24L0&7QL ztZJ%iSPL&wXW#;>YeW?)OlicLh=?{0uAze?NDu|35|~Zz4K31{H8F$C6Mfg?YV>ue zc%V$ZEwhAOJ(mKODWTU66_fDasvN}V64%kuc}^uN!WdA%tL_i@ykThqLgwWV2AMkv z)Vlqu8ytYdMfTF|(mrwEPv&0C2#_{c%hce&QdB#DTDhg`ntz=Xsk@bmWg-fiysWG& z%e6OU?hAtUPydq;vnTpD(JenKyTd+v!SX(nD8cQz`kO~DI z9UXxaJX3w_9%Nhh(A1o!kJj$iGzMVG?{5OZ{pl%v>V2UVZCWV}Hl(ZwsBFT2%3-$o z!{J8z*IHP?6f`ga&#s3!D5JvC;>BK5sBpv-O2zkl5J1|{sR|6D*QaN^%yW8XvjIknp{2t*N{4P#AGBz&m zk-fg=_q$42t;pi@wBIPn$&CyQSUn`fjV?-rz#2LRQrh29OPU6HSrZ3vub~&WKWSgd zLt&ND=`!cZ$;(@&qTWGkjH#)qnVr?=e?m8P?Nx`cc^0zJ47*+#-eA#eHYcL?6#~B{ ziYOz_2r_G0TA5}cU|my)T#rEg5E!xH>TAayjjJ8Ax4$2+XkET!>~aYe|lgcB_*Y-tc5P|>3NK0o4pAc*_9y(paHdGgi6cU>31OA1cw!-p+%_9V?T;)cvvb1*raP>17oamsH};L8EFpo9b(|FBT=h}*DdbW3@*V?}`^LzrG(Yep z3J5!F^`tQXyd#n}HX=RZe4@{QM2G6ywXw0`MZ!!6XA6ls6GK*4H^Iy=lb^Fzx#4wk z!$+Z?-#$=-h3$THM$n(HBcJ`0J7=G#G+eP5OK2U^j=JS>#02zK%#2bFtu-C=iQI(n1XyiBZjmprQKfV29G zMEn)?Y;;iv?w`7eH>0kOpNq?Jce&S+6s0mu}f_3J+roGm0>&r&U(3eBdzOmT;atzoTTot}U(M>M_#E+K z*d{yDFK!@M;82l7NJZOdUD>YIP{_dkAh51|oORk`$;$4o9fCjpIq<>IYL-M6wjcyF3P=m zZ$$ble0iXyT!eoaX4#t&hBM6ep^5Lb!O z^&2Yc6}*ehltkjzWq6^OoA^EgytRTMsO=XE3O9fg3>9#ZX73X*qSSujsf&LKu)TJn z_Bs+#NJXbA?>x)3GC&vn|O?>)I_`6n^7YxU>wFi_cE9)OU$e{_`jPPR7N z#tYC)V~$y7wog(^X^0SDP;g?2;fU9jsI%;BD;w)x)LH5*>o?tT7@Y z@=55T<2`tH&oJ6mDl8(5-E`wFZxV>oUGlQR)bTvNpf@#X!SbmwasSwwn{4)UYFzk` zFQlujE&bg9t^=0@ejsk@EmvIlz*TDKa6y0hK;8&(GRBIpwkuAMNtl|R{^5(IHe*$F zH7h@@Dbf;lQ(a(yG%c|D>%8dxo+!SRblFgaF)@X{%W_XzfQJ_l&YD3C{QM-@%3&z* z8(?2uGMMmdz3lmuXVG`Dp%_v!W9@!#$7l0~Y?UY*nSZvh)0~FdvQoe6_L9Tn$B*Hn z&+=ow!%yX%dk~m^-PF)f(M2wMoTib5e!V9~I4d*?O0D9l)l{e!(W&|KlN58WA}euK z)IT~tJ|Ith#A{JKM)Uv zY4c%-7G6-QPXJr8VX0#+`&9zUrpI~3wv|fby6|N&#xGd4QK9@YEq@*!ZsDZqu0*Ff z+htqt3YGt&WhL4eA~(*FH>p3^MeZ^ZMsr&1j5Q4w9mKOSwt!asnCF|##1bqWyXf#u zlK8&XM9rDH>gxW!>@-i*TO*!7hYAWpT`&QlWxbR)^^w46q!lLb|4ht?+ zf>3zroPjK7AuOKq?dMD;bwk5fknYLwfCYFH6r)ycSZbMw?-kT4R{eD|6KAaUzFyUb zx^f&OgX(*uR8{Einon)u_-E)`Y%_HZvDBq!-0(RBu9{j}P!Fx9snpEbla(tY`1twz zUcWZtAc4Cii9)`mT|V==uP@ZQLW{d%9G6j(YzZTkvWz)+)*73)#*9c|3h_Wf0y!{- z!s2$(!GXIgjZmuN=Ql|pK(Hjmfd>r}b&GE$DNDF85%I3)X!+`IM_C}flQc1*RHhlc z$XMTMKJ`X8(v*@RoFBpo^N&{7(SghUvxiVkkp9yTu3O)DU`{*G9onzy@}#ahhj112 zP;R{y2Az?-jZ$=9pX?s?+}xZcDMN7d2l{L<^LtImw5ex5X>1tcYE}lNY^iQbivgm7 zDz}sJG;KsU_5PlQ;3CaCfS#u|++$osvEE_TeqlXVt}dYLFB@L{}F;CR<7PH;x3**(`XIlJc?ybyj%oE)$fdtKL9w=y?AY z%U?MOrnpQ!tW-i~K$a2B9%s3zJh(c2DzgafddGrRaN)zykohyBE&@h>4;TIu=Yqmg zZ8pZ)@JnbIuw(g?Fj-{-Vg{wV{tJBgC>Y$8ks-bn7))M86gQBg&Qic;sek)6==M6N zjD&*tlT~1odwR&C`al_SAWMucw3SJP9$?K?*ojw-9&^b6h>}dWa1$3rSPNkZDZ3B= z9ioD5@Fn8O=HJq;iFj4aqGMwMLmlh54+FPn=3=w5PqV^-Jc^;0b2U3X^_>NZqg}J` zX{?yS6*b60e3-%xwUz>WeCnc48Nz7H`pKo;A`*+}rgV7een}_L;c4sWEP^&p;wW)x zajLHm)N7Ki;j0!fp3lYQhEPr@GGaIuJtSX>xHs`=e{IqLp4igT(9j^O!xBHFO$MGC zVhi;BcXnJ|)|9sH`u)U*c2T;hxHunl9y4YOh_n3k$M3)l)XBL3ByZyc2jN_7#{&&UP1 zn3==cG}-HXe{PdRhSb`wjik;OYUR&6wL_c64WsZOxY5f#_4-;n!)4sS192IfkZIk$ zsij5a?8DLKTH?p8x$2eI&(%~_Rh5-FxVe9};pVHRD$2{l>yjspZfj-jXFiC|$v05P zAYM1W&jEjjy`v+SW7GgT|14{>9#v?k>XD8kNX@jPoEkroymGEHhIw-Q4hQSwFvcwZ z-N<6qw-4>-H%CRf1<>kmMc--pQdHd1Akm3=N${oMA+*&q&Jc zhIDeeekbJ=K^J&rWA&Oc(`2qv;vsD;HFBfALfaEd#zyoyesHxUaJcUozfQV&4-z1_ zZM4~lQ;j3)wsM7?DILO;^MFY|w!K>x(=`xDPfSc42S%}5_ep9qV9uJ{_s)U)@SqpH z?lpa3#CVpBO?46zlmU&OOzQt1VQ(E)<+}9?E1{%-lz<3=(%pijgi3>SBP}2$NGphx zNJ)2>bhm&=cXzjhbaMu4t-bgAo%8PN`*W{rFV>v%xu1KC@vFgOHkgS^EmIJJLCn#5 z_6!k)SI92YZ~fqOsUyN)3wxIP+@EiRi$&0{^2xKU?dkmEN#55d^QA>aMO$@emb)c` zWvEkIp3j?0iB(ioWTQEJvj=wOn&2GB*oXeirQ)j{b}FW{!-D84MSG6q8(<-e}%8#>@q_xJThJ)k!hnn)tBO%QM$)si&( z05WBX+gJ~vNDbQ6*{$8>w2+PE5w=h>>*LjW@a?sG;myx?Y74BnWP&10akf+ z1A=dy4u7v%@l0NJ>j>6kfEQ6O;J5q5h7Qc;J9X$JT;{6P_KY#wMFuZm=g5#*KwzMQ z& zSBZyjJ0n@WvB;LapSvL$Fr~pLoH0LYpq2sjSZeglo6V!D)G zkHw8yKFguV#*NYuZAodKXk0QfP~eg6z9~A2-X7Ml2KhPk{dQ=aSd$nHit54a_dhFS z;koFu6&!yp(a&u7t5K;D4#@cZ+uVM@FWvLt)a0apeUqA2h;bR-;UZHr0y22px}rHX zCG8+I7m~gX;*~qw+SI_3THcu8e)$AW#6b_3lr~g!T$c`^{W>v(8jg4iL$$(6&(ZOu z*`Kg>yRkn_vi$8#cRIJ_b>*ur{ zS~U)hb3M3llfqY2edRa*c?cg#3s1fybM)Jui0>A#l$!dNx>60^y1V_)s99R z9D^YTL>RXo5qhFLxouzHHr7vTF_12Oj@=gcVV}8Hdj{$O{2mo~T}H)*1_l)tjf5k8 zy}d$Lq)*>l!G3XAb^`_HOC$5>0BYTrd%uZkkL5ocFc{Pez>#}JcFagouDfVY9n9Es z`u<>R%6TYTDNp06!`6MZKTwxo6;&UssB&Va^SFbJ?#F`{*R*G^fK4&Uv^6>Y!&3gs zHb!N=bN#%nB>m~V?Rcm4EC`BDVMRP|&L!T9F+Pv}UAtWDKwqSR3;#TU_4O~os}tD_ zZ+}shXFII9&jaimZd)PxZ$qgf{;O08-KgR*z$A_a*~oi&T<<;;Xv!ih!C-JlFQ|lP!z5$}_7=hcM@PU+M?$ z=hh=$Q`!ZrW`i_U`u90Zdclq0ApC4LzhP`1ceKFU#mbu-4{)J}uJe4FHu4z*Vx83= zn*wuQKV11yOXzn#5zeUb*Uh@7GotOh>4A){+QWWrTd@hVjM?jWmztIaZQ?-2Ul81& zXhZ9d>4z!cNoZ?p(+>YLm_9FYlehT#br_&m{ytjc$X+kDc)qRsQUpil19N7+>Uq2VHg|77brAP!Gep61BR`mZ`ulSt) zCGOvk=fma2nWE<{LT?wq()O|ZXED2=NrFbepOAUouIjoL2)IV8m&jn5HpAdT{rGX; z?$vRNpm+eWSl33&1LrSp{C2ByP)=dUe24yxDa`61)h4*>ayr51br~yiJ;sB^}2l{IHkrpbxaxTY>=F zYFOM!$e`M5CXZt$bgajZ#~Lq`=xX~hc-(~Bn2U9_vy^D(r zg~1IFg?b>$o_XJqmXYc1>VnPge~$kwO#NNY$Xs?BAS6Vjqt`eGH3JKxNq1%&ASOF0 z>}X?T8-C@%3c+{lco#pVS*+ejp^a|ZXR}P|X54*IyO(ocYgs-w-M<`OTX``XN&3sp zx9%IGg_IlC0p9yM}38@1>#;xWDNV_ z<<^B?Pi|n{wl?~QA6B`)Jh)#R6FO*{T|#%NIg@*QIPq5T!{t%^RhLd+%)|5j0og&h zn!RqmEJtz7W_X2mNf0sEf&e-!uSNMEor);uZz{@a3t>i*$&v$Pv-LS4uV@$`(-a)L zI7cu@dB5?0m3UVQk1Y?^7#KCU{%7i3tKV&i<3i{hL1k~>ZSGN8Ejce3hF=IX4JzK& zJT#JfweSIus#zU%&oZHR`Czfn82A(F|D4{>1JdNK*0|VJ^E@ZnV7}u%p#dX#*K3qe zYRF6wp1J59a_3buNQLRu4h{~k>p7vv9TaLiVZ2f%WTac}tJ})81UKZpbc=h52izW6R>L(uU)F6%1Zb zv2n!&JjFk;;Po8*;u~)u1X$3jlcE<#u1xb+e}6?9ygbAe1ajU%QRu+yVX`a|-3e&C zapNn{VrH?o4V5Zn@&CpHL`h+O1zBpw0PqiLjoNKZcj*8tWoDL5lJn8Z^zq2!3Vt5M z06^!~cqzTp*30sj($bh)AxJpkOUXU(a0@%`&INU(`J^l7(3aGmuIB)&aJ*H$J*qJe zX@f4%Gl2@SPanSP^#v9e3|NbR>uJHH9;`%^AaniMPgnB5OyvhP(hX82Gx<*zJB~$J zFeUS@;hAM|szUPnnDXK1>p=OgZ`%S2%>Yn8|;m63k%EdpWj+LH|h`(KllP>GOqD2QkCLX$_EgNZa}M8l;S7#R&ELcW?6#8V-5 zhfjV{$P&$G&T6>DH&pV$`9(bFH8ZP(lj`aOn3(QZ)5R7LhyW{c`_BE=WD>Pgpwir6 zFBo{-hrJ3RgGdLqU_h4e5ke%C1Hk2hbc9SQ_?iGSix0*Hu_j~Wb$@Px?2?Rv4j1Q7 zm^-kLVdKlU2ru@06RsG_*A5?~vT}0JXE#%CRCv5GzM<|lZUnfYg$~xgzr!aOHAfj6 zVfchK%PG{ty4SNW=twlVf>X-M2rdLXo8$SNS9uor&H6vqI)@n!08tE>TgLbA7}GzP zVZL_WUu|$FE{AA^C(Q+gZWpH&1a&9NF|cqrPbFX5U|Tuo9n3>84UGNWqD05Un7&Z| zoQ4nmTlZy5NQezgc5sZ}*Y5A#!RUwPsQz zu(lb$0N8Tf`nP;cr`*l+qk(a!$(*aY%fPi#lvehU@DL{x6BA87_6>`VwN1X5_R*oC zo((b=UiUq;^H=J2XJ`<47zjE#qd;sJ=VvlL#x{!;b3-4Xng7(9@a{WaHNeH3FLT!u z!8#L5%dPol|CT_q?fzk%!#-*Dp6_I=T>D@rJGdIh2$h zpSNN1uYJ2EZEV=|IZ%)-jhb)GVzyhk+~B!>-?)KI7~v+LHTDfs#^C%5vog327B)Bk z-5N{@{1F(%9Ts^sEV+Bd$k{lAGqSSaEL7C;F)}kVv$HFHr)Ud?dn9LQowvoD3p}wY z{vkf_WOaol*qE5UZWQ>uc_J%Ul8o3~gttGIuOs*+hf125n7H*d2q0_*TlGHSm8y3g zj5zp*%!~9@R#ZeHASodNuLhF$;P;!$;+bA$?pJPRHzZjui0o*F;q?Lfs&1vTrzfHn z6~X4yTV>|DV+sTg6Ta9tHn1neJ4uX@@W$O{5DK-Vj)(ZRq?!CZvyo#}Y-u97KSw$p zhk6BXqK{GyO%lX$q$yNRt7{E;_=ts&f*K)@A61GfR3^j!bIgbSPv$2Z$aVQXdNnFhc93>sOj;=DfomF02se`~d{*8FhChCSO zXlUdNtRZNiGKu{IMH9^q$93qk0AtFh!3I3`=c(a_$2#vpCJ!U{Tpe&Yi%Uzsst?9k z^?#xaM|8|tb;dqn=j6m!;y&+XZPCqm=s{Peo|{fDM4h+vG;w6(#Pb9`((bL~Z?}(8 zNFGIHA3;~Y^I$UZTIRu@Bih0R=>1rKC!|u}tu)Y1iWaht9T&vAW;OqOzKScdqQu}O zqzd(wK_YA!3wUm)igfcvF)+lo(sr0Y6~!{1z;f#9Mzx!>FF~n1BCMG+1VnyVc=)aJ zQwvSMf9A1}&uQAwH7`Uv!I}vo9xHNb=f~Lxfb&7hfQHwjokiTE>+3p>p< zQa;uH=`%Pu2#_Lg4KQlUv+CUe5E%nvhs)44yYFzP(oFYxmCzs~UN$}^%o<;~>BS|| z$MWP!)E!#2+s!S&RX(XQv2g`mh*NER`&$lTSkfT!P3IE{&L{hXI|53np9y;PaE_5X z(LUW=j)tBz1#s`8LfJxe;JTqhG7qxJ| zzuX(*lNW(BjSX!U^HCPNksZ>omj|aC`B|G*re5=_}DRL+D&esjwdyu~b%DtC!0+H(YO8%GVeS3SAM}AG%ur*6YpgN2=aEP5eXJ^K)*`c7U9XP>8X!|}2OrWCrlDJ}f`-V+8U@X)Djgl4PN;}-?P=Y$g z1e$qxn0=#XDL#H zjw=;NZ-`(uW9_gm;cf%1xf5e?t_ogj^m5?YjgAO;weeT zLx)I+L3j+_qF=RGDxXUepQ1i!Lx;~~R(iVI!5moga7(R<0>W-`hWZ86bBBK%7D8y+ zWGf8Lx47rgNN-n}k)OSklcbh1|MKo+X=0+6h}XGWP>w#8z3sB zJKU^56q~6)c3T!eDmSrsK1ue;?YXy(Lb^dYUjhQ~tMPus$sJ$k$AT`o&jgfentMKQ)l6E579NqbhRjw4%e`{bK~X-r ziGns4*v-O!BNc-I?}PG5z0-!eXIwWf_M^OU-}RqjGp;BePf@#XdZ;VmMf)~cr2n8ToZ>jh2%WIXM9t#bYr!=(H117+5i+oCE75Rc#9Ys+R@w2fZa9h=~uER*csV zxE0UYh+7Eo`Ns!ek(T93=y~4sNF|Safb>GGKn}%B={dbfKYlC^lCXz|?T18Vd?h3O z^7?P_@yQBW7A%jTlOLbsp*a2HP5xXP&9kRuViQczW~G2nfoc%mE|9%P3k@L5500wJ zE0+l{T6idoN$1yBCeD^)VxEcA_GOx}2nE?84S15%q7$GA=c0iEtjYry{+^uG9qhsE zAnH=EKRjodboL^rQNZU)XbbU&RK~wsP52c})P-E0+?SxE-y6ea$6NS5 z{G_zB$J<1suUGd2IVxKEZD9oh20RaSe2@X!AyeNG4oaN8PoAkHRuENzX-{0mmy3-g z7TEZkUCl;5Ne(_Y2O~Lia*&FmVo;Q0gz|(=uCiLLt%tx*2PE!$nZ)`Ww;A)|XG zDtnbAS7Uq@O)Hw%T`C^0h)+*ncidCRuPu|PcdG^I@%f*?G>+nIvpp?`$NqJJHaM~G z_i1SCk3fe{%3E@@zcF3=##IefZKE}q+t*EqeA~korC2w~=9Or)@6@FNJlN6EJn;op z40}4^1{I-QrA*i5>p{g)e18fnYnI64F`5V01S0PgId42Xf4tkCyJ)|B%vnS~>+%IN zMrtFWa0gMU7W5|4(;V)uWt_pX`lcTrtz%!lRQ~L)qq_e+i=j2kJ{W%i zcR3XAbZr(e1I|WKpuOsXaBKN z6|Dj$dsV+5B%WWkQm!?9o`RjgIZwN+FV#DkHF>z44^vTb#aI8b^dz5@s6zTsnG~pK z0bju{N9G|F)V3RtzL?_LJU`IGy^LNP`JaBDhRuy_cO5n`T~G}eSZ*t|0dr~0Z)5nRS5hkfS2O1?lfLlvq$J-(RXuxWN;zSe=aD*X8hA_8XWFO+lUH`&&lFg7xhA1^EKHgvQ@H$N^81n}xjck_YeIM~?{;Mlk zoI=eQ8qI4oCqk*#z7WW)S+r<*`Vzg^um(xk&9rj0ZRs+1_z9ekl51at$CB@>rZ3?o z^9fhyY)S~cE%|j-Fj(+5Jc(6hq(&Sq+tfRKiJu9+AGuK1rq8$44o;iM?B$den1AFZ zWv=!iqklQqFF&L<$Zz7aU#xI7yh?lij1c2hAb@-01FU zQQ0}=hikT7Cwmg6ybtweCCE0DuthG%$L1%u>zpYx+(7#+@%5=B`q5S{Py!al9hB4z z0)NUCWa2I+br*liEvu$XI^PFi+jdtHCbT|Qi@k3FoW{BogV`h4K8L7 zGr=HTtqv>HtDE(ckilFIqO$U*j4T%8C4DIpid|%!W`lqispdZ5Fp&efXsYrh7g`x5 z4>j83jWQ<#bnc*s)C_~hUSrx2X>@+0MV7vwZA5FtzkTmu9^#KldP5w4^^KZ{H4ce12MANch8c~U3=Xia)KX1t`mTyZH{8XK*n zrk4J<=M6;vkfs$@!s7gCF<7D>9nGyn=Yz&G>p!peR%GY$qm+0q$u7P779|rG}g@65ntqhZ5MR}wZY`g{${w`mPwmzv*-3+^M*3qm~ zB%L|aJ9sa4x7Z_K)^$Cz=c=_{_ys$F-m9}Y_51B$#4r2MjL&G-0uORh;(4_vI08BH zu>C_#QSrxCu#%F+)=b9`h^A1b~d&)0nv%oh4RtP+5Awa-k^ePIa=Ogu(rLSA= zge$uM1DM@vytenp^VUA^WHJ{%?lxt7tH+$cMLe<1TBlAn=r}3wcAJdQNW*F_g zQo};L8|;zqW3F?P^RU*x8k6g_svblB3RU1yUcv?(Q>{k2!&?`q)K)?tu zmxX$x<)^~-dY6;kT#fSa%|l>b7&I#iYHH2_*03}c%RbR8*D7-X$Q1&EV{Er30SBl6 zC{^%uHHFV%>&tBWjCh0nBNM*l=i(LzCb*X5&tz0Hd(et&->>Jc63n)Da9f!+_oa9j zlu(p+zIDUxxPBbMf{s`o=Z!l9ewCF(=6Y-;9v(Bh9SdLN{(5Jqd5<9G{pr3OvAc}= zI1=v>$I4f}5Xi!iW=y@dOX*6ASD*S%+jjE;YS z_erFP1s4iAbAyVH8(Ly-TXm0}^ka2~aF8a;N{Y*>2jf2VtY~q+IBhP`Nqt+FWk01e z;%opt-|il}X~uZ@+qW!_pzEwM>9gbA{Vjek=e#4Fk)DPo5!Jg1@F(jLEeGiYfw%-^ za4#7igfq&7$#7 zr*fpH8#?YpB@x$mnc#8aK9u%B@|2$b{vk^pWFU@NF+68~Qc5M2@i9k8t#sE)hqv-3 zDF%USSc+A5tjM|56 zP*d!eM2t6W?%zc!Eo?zByF$50An*X+H?UAT9Sz*hf@!O2O+W0tE^AkyHAFZm1E?Q~r&TZi|t0pWfSg85!(rqQ~1rd=$@642fE4+vqBjl~j z7=-*$!ToIT`nKLWIE@+mF?+r%nyn{ps*V%lHT&}+%3ZuzM5NMe?5G!iH7$3KV3$q( z_E1hlpwYb5eOj7hMlF`t1&Syhsp-&e{t6&jSXl7BL)uH$vION4+WtNW;M@ECfb=C{(ft8BH`9;!^vb>YPPG7&AM6h?<~}#!hz4XoY4@32 zPUB<7|2AlSh`RsuJ=l!869fexKQ;t$39MJ<7%(fPdi*%xAJNt|&n=BRyd8eG;#~X?!Nv;~89?xR4}no}Okp#Y<$&17$z;5u1LZA11+?vF+Mz&kT=V*7 z)QSH;Urv&~=wY|d-$YKXTCvgQc&T~!?otQH4%hi}BSS-rp=%kpd(rkX#oAm-s^$27 z7h}0ehRl$|=J-iYKHXHm-<#pda{JAT?>{;sdd92XIlenkKKx2%+~JJ!mDIW0(%tmO z2LW_K;*Jh(%^Fqe(~mG4Q+UDIEuB}ngv4;bhyiN=v3zl6W@cd_P8gYo2mMb{l#n9b zBp9xPls~1?^H`&JAR({Z5YBCvU?X9X)P)BmxsK)ezxaF#!Ln92D6Iqrh{(^(7YA!)R-J*0*) z_Gin^rqRnj+Sw=su@!iBMO(kwH#Wk%_zM=NchlCEIHYYwnep+KJZyI;Mj3wMeJi1m zhAdF}xg9z=4istsA4pf&H+OTc9~ywafYm`zt#S^S#^7x?P;q-@ zLnA6O860iZL1=@|z=3yq?)f_HiNn)`teGmIvsgn6>4E!wlE}ymsrar#(IW}zy*5{Q zo!?!t*!%ViI2BFmRt^1MHTe4Hk=FCQh>#tF9FvlD6kPbXqM4n>5={tVJ*gXv9?CrA z42_PChBM{l z|NxLPfDT6_3>J6>tU4cuGM*SG_G}4=O;?}`NRbx%=e5qh&f4tg*!9+cb|0!vo0y(aIeqxom)LfqJ-E7@vFFu?>;)ZVXNabNN#cBstG&AUVolb0Z3J52 z(RHWMkjd)0^Ha_b{PW>&-*pA$%-%ecQ@7jReRWo^WZn(FZ&1Z4r$wKPp=9 z3`g?fpg!2`%r2LGXY`mtSQIl+Kc=6#XnHqtKvB2``yWi*+jPfNa^Br>ovL*X*cO+t z_-y~Q6&cxjq=M^Lv;VtfvK5xh&9M5Uq>t*z|8ackqQmxrBH*&~qQrbv+|-bSh5g_E zX;!kROvPtloRGM_Ql_jW`i&F9yjN$d3D|tNF&{ntNxIg0q*2$W`x_gVfukauNxSl7 z`lEMzN4DUxM~=2OAK!NTm30Th<=M=3o~oXo{{|86K(GKi`}XPOS$V8QofPgl`1CYN zkl9l4y$;}|OEDjKFcxtz+%jaDez}~0#cw~`r}rkk;$1*KgblRpoNk;=TRn?iU5dz` z{V6u6bG6^&sCe}BWOafWKhB1q&7Jq6p2PU{;lw^YV-vw)jwz@4D5gJQ&#bKTNSfqy z-nvF9&&+@yUXHe7vv|ktotzB*U4{3SC(0ujm1x}`nJY&bMogSnlQ_6ChDwqXqk45d z->ID@%G?z7Ttn`Y0^cF*uGh}52PrcHf&Y|cL>8Rn7fjkqF>kZ^9O^uXZ}jjVwX*ko zUxkr2Lx$D;>a5{|%Do25o4XVojOQUbm)8L@XU9G650Xub9qFV)yDrad8C`1+hUd`4 zBGS{%>iQgudf`EGTb39YO^a<13A){r4z0U9#oWHymDD6jQ9?GfO{TBF{0t8a#D{4L zFS-*qJ|3q=`BvP$X?Wx-;v6EPl!h zpw~m*%e+D!Rc_2ws-L93t@;0ckUrP5(1879?~tY;dHS~`vJCadG-D%+@Y-B^2m36QJ#G9-$w(l=knKH#g^K?Vm1 zrT9G9#bykBQDihl`HSnA&Kj~OXX;+ijqP5I>Sej^v2%Hs7>y+*OK%r?@pI-*JLel5 z?~uIK=RI5>^1N?nyJx+1lO7E#Z=i%_tV5dnV0)0(Xpm-8=SqF~oKr0_v^Y_xetOXd z?pqodJvEOX_t%_lvF|Ioxlv67B{m16p{v+!q$lRWcnjm9#>#l<(SUfh-J0liI_&G7 z`1-Yhm1xNPc%pSKps@=>xNIbgN4P=R8Zkc2^v~uCo9&Rsroz{YWHJ?7!5VXGBlGhu zdDVG@xqkBl)Cu1z8Mmv9F0FAHzO06ciZ(pic}xg52Xu5~UNt>}S4%i7BPGd42v$ZS z;w}8rhYh#WVmE6p?Da$%f6(XumZH~FUc(4j`_rZ4f#w$l#m&YuE<8NEeM*YF{t;n5 z5>_E#hAJH6$dnqPJ`ec{kLUed@odBs8&An6(2}K&celf!R z{Z^%f37H!A{PBBCt;2p5Hu2M!mP%^*0{{yBXaT~oUAHbYH$ zs^x9csQSx`(8|Y0tZMemZKIVwc0FZ|C0{mlGJgTecM2isYcG9C!h(mW!`98Ri)8u3 z#_}GQKjcx|*9!N!zMpm-Z$GG58Il?hpt4uJ6XxDo#}t1-=Nqnvj?R^Pd3ofyn6j|I zsiYJ`=3+&~Snyin4riGO1)~MRkjseaZZ|2Pc6z3F5{JsE( zOi`1T$?Z#BNO5{XX6?H%!wA62&oP?yyAx?w(&i5x99LY>7d-nu zS*?Pj$%uDTWT27xWQibb2B@hyHh+b@a~o6-59FN(tra>6Zca~f7k_*V zkiiljDE(xPdo<&|l98}Q-6!*IbKDW|ho=`RJ2DZotT-GLc|m!GJN=qROEfQ}efaJM z>y*2sG>nP~Qr^X3;P4qgoXreb!hKo1O9E}zE!9!rCHYdhABTof zt4jwfr{DEdvskam6Ee0;ogh>TIj3ZsVD7CQ?iV#^>2b_;;JoHReZ=TuHFw&GkWl&R zwmTj4+jl&y?NRi-GN+;~--&~bj5``}jf_3J>v&>y=ewnR+fwu5h6V?|`WVtWHL4N^ z&d^}~=Zfu0C^PO}B$O)Ot}s?W^(75=t(Gay=WriR-P}vxGP*vwqn>Sm{C`b{KpPsZ zA+WmadXr73M}6NY-`IzgDU;LQB*yM=rq7Erw6|C`DvqWn#?S!e>Y~qE1${5G21t@x zfj28WiD&qljNWBz?NRx@JD1%*1)U^1FY~=$I_;;hOQoMTb4C{-1`R<54W@=b!GsCEnns~PSXC@y-J^GYk{FN>7%KRtc_RJ zKGWL^YYV}c2cZ%A`ue~WrD~oS;vt<}pUlzxQ0l<-(LXj@%pS;Aj7bv{87R4Bf+RGp zl5Il8%i;>qc`gAKh8q6mY~?(2!G3QkMAcEI%aZNXRUSU+UfZd#OIct ziFIX;eFJ;92qV2MW$HgN*0^2h^CG;hs|>4upq#eSC7HG+D>&5?ZR?>l?r?pHqW)so zHf=^J%m3HSDUlx&T^dO7Y0p2sKEQcb=Az0pHEw&;dFf(go2f`@+^+o1Te0kDOU@7_ z3FQO;jfxd?@$}!j$<;>P2bMmS}4ueH-B$H8=xsU>5boE5@A5r|%G)5?!n`LheO9zK@ zef-IgJcIz2kD_|^&=ow2frXfQq-O5x2?!?p20twbd;pNIh#j|;xve?J3V@j2gO%L! zI~n!Afthc??;i&|RyFz|^Y%mIH`-!WrO62lnVbLIZqMN=a6VEF@zny+=|0F8zcqNC z0mt3??V1Caik^w}a$@CnQ}1)mnaKapUCQcWM_FWo|6V?mzP@SB1Vk9QxoNi`j~oOP zQZHVtO_akD&doHA8?WtJJW7dWz!kP#+lV+JU_MP*VfnlRAj#5{f&$%jgnQqmLof>>8G{^ zf8r(f-nco2ENQejq$i6x5Olllq+M;qPUP#nt-9lQaq#r6QhNvU_K)kUZLRJsy>A~_ z!z^u?*i775+EXS;QLA~1Y?A@!9Ny{IkU!I?DIgphkic#~FKTVJelPk2__O$3vhMKAfvu4ii(6P*E6^pppA1sA z!;MAMu_;tz%3U>YyU?*8Gekv2j1ntRzX5NRaY9$Juj|<`pT_6lhlp1|^N1KYx$F_v zB9xd>fl>oxUCzZgP45Z@;Jmos{`U>FG~3Vxk^;?~4$R zmRDV%t!k1qNpMs6D}N;Ps@heQ^i&5gI>h$CLZs!TCD7$56sIO73AcyoFbT^Q+?Ylr z8VR&o5J#7WT>(_G$g>&vV^om%hDF3Cl(gE&i|tHXGZDws@OmDPjiRBtFqdnjmhAt% zo(`9%wR9h-7r7w#rs2@6mAEgs_pg(=mUw^tar~!=hNIxg*x26NiidegdI`F= zG6gTsEO9QyxijQCvvV)f&?%h}5&=czOT`ysj_%}+cH4Q! zzy>!gAqSH5`&oL}&Pr-?bzFnpwXR*9Q)~3qOCx4Cfo-`uEJvg3hq6p6aJ~SDy=ywYlWfU-!EJJh~Gzx2{Rl^K;| z7*W2{cGu!`T{mXAtJLUb)x9M#<$Zli z?S4Oxq?y0(ENe;|Q@FEo!rv z5cbZj8-ReQ1V2hYS;o*X&-8AMeDtcto8J5%9hpgK<<`aD9hv-%au>>pe|Kcp1Fz%J zd5`^uXvhS4X2uM|R4`|hTbg0TQkGG_aN_AXG!Nie5ej36_@l~`ivMxU8X^zsuQY~| zo;llGSIUF33Gu2a_piEVBIjU%6?00c*>sX6Puwi7kT(@RA*VC`2h5PAMl;zC9 zjw?Qia@|rktgEQn>5f=cv3zkUH~1>b^zzSw*djNdeNBzS+a;il#~OihwW$W0;5XOB zFOnGG65Ln1RWOLy601%lR{T$-=@p!u(7IBGNzT@r=S@ywu+VWdT!HXZJuF9gAv9(6 zV0~0CqAICm{xrFt4?bF7pRTKu9_Hd&_9&`)khX-aLxAB^#vVb-=k!&m-NJH31!^ND z6tDn1O;f$_uR_SH!OLh`dI2Nz&(cv5J*W6i9kcn)PaU+t=FHx)r;}j+cpkz~gJud7 zYLoX&rkq_;K)*ZG3%xy8&AEe-Ba5Srg|zPB8oybyBoypug3ES?E1@cg^Bt3~*3mJ$ z1W}fO@TQyVNSWJkVrkX}C5bM@RvdExxe~N^;lTeFc zDyFETYKL>95wU)qGkU>3gFKc(2kchPTvFf&1E~5DK-Iocs2#Mh(R>1GNP2p@2^X{^ z4LL&c097FbAzdKieW+2n{BNoloN#5kmqj5k*84eUND@duC!k<<#yFEGNif6%ep1XT zw>E}oj>q-V)4`mM62lhzF9tYT7&B1Xok0R9990e#HZGvk&5>*|l4D7yPJCMtU$a8+x zZ4h~wsRW^RFW|?)MnXlqmXYEQDS0RM<$u zu^<~wR-UG>e?!-&U--9O6%rkHC4Km?9~u5{e>QB}s0jMwhm5de>b?51KXctZPP1=; z<>-3w_u6DYvMEmw?@|+%U@Ac)IIv-`n*f?Lj^DYgvhoC8v3*?b1|fAEkMq0#l@eM? ziHhZYNB8jI97J0H6HS6uh`HG+2cTP`Tr}(39;pnfG)Sd%5R15mCUf6|T)5$h zkX+7gpA?t*XaNW`xM~gBLO}7~(Cl_RcYDV9;SVWtl#sZ;eyPlJ>_>jOPXFz<2{bCQ zTkD4cR%|+QMEwU6c03YM2!K-^L<5>m6h{i{KmT=ic>rCQYxjR1ZjO;~tBaI}5$b4` zT23YJY-?p6(c5|8Mts+0OI=^jjf!$`6cnxN+&?PtzWQ)2qOC(8USEqm2N;8QDsd&j zeUP~$B$x=YSdmq%s01ER$amlQYqHEwZl^vjjj=?RoMBJg^_Gw~WQ(oQVLPh`d5$k7 zOW|R0%l>C0%tQnX9I76^$bbEhn<%EuSBgKB&O>F}i;AS;-7G8xE+L;CcZWFd@Z-@N z$sE}WN#DnxWPR@HOH-7gJfwYC_7d6DBQy6* zg^O=xb=hf;2idMvWJp_{utRhr%{RZ9dXNG$gtTFvA_{hY=r9&8D zdBP(ODjN&&Nkj}r^QLF!9{i?a3!aVJFQ1AU(iABgr8%?4ZH=O4$0rF*iSo!jBSp`S z)R?Eb7EK-6Z`~iy)wp+lvDb4E!R+3fB60lVqgG0ahTC3ORTw>WW6PffT=jCxr_P5y zsi^|;@?G1L5O(K@-5JLh;?oEMj5`wrwj+6tuYc{MVSLA&XbH?`(79COS<1;De)LH6 z;!lfqCK1n^RG;IW-Z;^1bS_DgEx$w2ZIKz zJ#*SdblkNdcIWz{taBL~899QJSO`26;=-k zzj`ZoMX`$vM_R3{Gu`sJ`v6}ZyO+wO9CZiBlB^pO)8hTUN%*J{)2z4c)#)2SDI(Ai zm;O%u@P-7fo>nNVFOO~Vo7zHcCVh0(n_;b|atC%u?*@#f21ix&zi^=Fm~x5*ejy`F6q^Vfz|5(I6rwr6uyC!7-N;C%nhTF7E3FL$y*Qz@2CxD$QZ7Ba9<$a@`aFuos65HiAuUO=rW_V3=b?QiMlNOvb(pYT}vU1pz9v^V9x!#5YAvZ*1cXL(Il;ET^=t#o^rB0 z^t&(eMPlGN>6^^a=kNRYn7~4D+6|?zok7wOfEH7bG{+UdRyfn7whhL)AtaBxiZ*E!afq2X5@+8Q1#fI13{2}c< z+&=`^ujfB<;aYgu$AHN^he@;5Vwge5oSksz?D*T;{?t3BVgLP=Fu$SHBcEtF{ym2H z?B#dG{Wn$qADe_R4V!N%Nr~VWr&3YL(0503y7VQh_%VFbWIlyyNe&y)E4F_2Te89|BM!1 zEe!0(PkW7$rF$)LXDJ=24*s-s%ng4VmqNUxRlL>U^3mm?mc7D{QcSNIyu@}B6(Y<3 zeqv3-W4-zRIj`TWLQCeCI>_v{r>i6ace>-A!G(1NC&KB_oLg6D1+H~ah==wUxL#eH zkt_8#QNSoNp=WGkGdbHZB_8k91D9~S!ocfPfIlLY2ok2tFv81_w{IR(#Iij{M- zW4Mj2&XTw)m}>ow-`whrs&nNldpiSCkaD58=M$}G3KiL3A9R^bRk6B!VtKRJ8vl~} zx!H2Jm%E;N-}pa2us_|in1+j^P+8?qpSgaI(g$ZXu#P{C<_*=R8Ry61}xdO&`IH?QCm^-e#gqttixk-MDD1ENLeO z;ra~N9{%R~>!L`PAx%v-N&HNgr{?fSXlMXuP&($F;%gn>chQem^sq9M@LsXLVr9I( z+mj~{{f@?rw;O+fkIldUW2lxt?`8mxK6`u?TRbB1Z3l$Xp%;nKZQ07`@@z&d~o5kg#_7CIQv8$;IWR~kYl0ndpIeqtrw z*PMeXi6Msj91YPJQ|$??2*y#)m>54Uwh!zSoSFbE`?KO)@RR;}3S1@$I!}B@H7F)6 zIi*P+qaNd$oOC1L&5yVY;Cd1<2!4f0%ay*^Q>z-kB`4Sz{G2~lS0lhp@ioN|<@GA~ zZ?}cF{K#-cI7KjEr_h$<`bPS7FMx;m&!?|^zG6G|qq7El^xPb$?tp${5l-;=k#UdU>}YUfnCX6MRqv`OT~r&f zJ5MQ#k}9l8Ns^$F-M4mqTQ^*#t@OFJ{iP}&x^s$nr-RAPB=HO!5kBJo{>@8FPa;_L z|M=3=m0AzE%L;x?07}xxh`30e05W*!D3|~(*g0@uVtkeym@#lcq#~|R>gXbok#Wyw z&t#P%*8Dsj6gxaQ1@B(JSR@hT|JskY9DfuLFt)a4=CFFZTQf)!6eD!k3|Coer3!y@ z0`}Iw_o^(l73z-b4;L^f0y9MiQNUoF@vOJ*7Dr3>Xvj8TuRAL=gbNfn+{r2-i^ z)+&|!(m_(+$YX?L*7syy%9e_sJBb-Jm<|qfOYD|&SiAIK_^yui!NPN!GI%FY&(hnNh(fhl zNt}X$g5mOvw>LMlrRo<0QKIn$^~Q6hI7648eMzH3Eo;GJ(e`BMqn!ObiBy1CEO zxl0HBo%AAJtUmcaevuu&`!DCvLsc3Q^A;1mUy5L)s$$`CRnz}Ql3NxG*tjRk%;1a3 z9|7}jR=$oMxE&MSj&UEX1Gz+8nPyQ9m9Er$x}N0*6r$70CONuTxrtI$#J}KcjCd z{I?^N%k{h-!uzHbnV&m-vv?AllG6+SHZwZ|9@ZQ!KHMNc$8}l=!LZAx+I26U8<^;u z*?%?6Z|;l}w^&YTogkGz z0}A)!H&?6J1@#oR-IA+j>XJkW8m8|I?{#0Uz*9bTf)zo5{ksGfK(B^QdP6}H!Or=d z&1JHlYbm*AK^TS3E+K5T-I)N9mX@T1PjO73PNVWJx)lhz~imosT{AX3l@IV_A|ar z0IQy;jzI?kWS!vFFqGbGd~lFYot&R6h z)Pm?*n;i^+;i;XvhZ9fUXVcF*3c$;j5ChDKx7y zD+g3Og_wqlkXzR;DAzX8FOw{9K6RpdG50B;tu#ykYnQ7372I4Tji$5=tHBBww5VQV ztnF}8%dE@A<7AxVDNs9B?V=^WRG>rj-1zy*oVmlKW+8X9<*vwPnewz(5q@F(teXdc|ssF((3B` z++c$s+-Ngbnx2-%Z!X6w%~Ao_8BE0G%R}v)Z`8$`jb-{qELRLQ)+Qj48WG1$i-UY#!e)GZvaAT9oN?Q<7_7+b3c z2843p`6imb?x&;~Jg$n@;MyYLjKo?A?TUw>jdf>-<1DRLSP?$2WFJ+i?s=>GJPS7? z-x-|pqwMbaCM<6O?n#Ka_bxS{k=E1DNc156P1CHl9{z(WHH7AcKhUAQyhKQ22SGb1nu3p}Bv3GiA14Y3s8?Bg2C znL;QrXfU)h1Z{XYtzy=;CL6rUBto57g1v6uZ$~~rO$d=flivSy@hwrI296s01ou1( zV_6+`tNC_gtD}RCv`2*TE4LkF5=jhi{1Jk9PI`m95|HrQFOM=V_i-MpMt~M$ttEl= z7>YrY8!Tu z2QBPKGPC%ls;RFp>O8c=RI^h>bMs}6{5l6{ZR1k$!SY99wg`}R-o^C6y2y^!Mr|xT z*1)+VBv`^YhI3~qz_BXNseR6J4O5`7{;5~}8JsJ*Ay^LFflBy^YW3^`dPaAupMzia zJ9yEFZuNk`UlwBoEW=MpRhae8!3&hzP^M8Za@z>H(n;IqvBbnXTO0LOWcwD`6peK? zBfoBcVu5t=QfNbUrRv3vUlPQP(`?d6_RH5zm!K)k<%%`k99kXV@zaoyYCgBxOb}Q&h8gb=HX>>Fso3x>+X`9(_L^vaclaf?Y8%2C;xuK6p z**C?f%A7T1+s&rV>FF_(pt#*9afyi_c*n0esLgh$%j6+ZN$Oe(k?Jo!7PSHzu+*M} zy$nl9$dblPe8^vw-&zU>@_Q8nO{se@1{3E3PP%+a8#nOB>EB+hN>IZb!;=_#--Y%( zQn326PI{B>%Cll_E$!Gtces2-lZsgEvOg<=m7tLrMWpuCc%@Ii1C>G5YTiT()tk~m zJZWz=(K-Clxl7u!2|u-j7?+*sx0_+c^HrpY*bhq|pfhH|yYGHQ{QCKJ9jpFM&^tSX ztD1F~ObGkyS|+=vE7JuJlV;U1jIVN~-W~xbL*P7F1Z%jJcnKXc;xzOXw0az^?eMTu zH7#59<);dclCVcR1{LYtQq8s?p>^I`qlVJaVgJ;xqd|qJVvK@X2_EebiQ;yz=p;71 zCtnzOuS{D2qiiNTOCu`m^QrZCigwz~cOw|VXYuzF?4Y%_o}t0uGVo~`1&hB5C^$Bb z^pm&KUQvPEOLh!pDC@GPG8JPWO&vODEJw*w1dUEy?PS7xvr4Wdm%1+;$x?YYa;K!D zVQ64#5RjhCwZ3R^Uj>@}0@=Yh*834b^Pm?$K`m-@r&C{yjsypf7y0#Q`37U8`{?J6 z&{etPYU^CC`btd0Uho1KgZ-@49W^kf>%OxMq@dZE@O{~>Qdj6E+{$?R9ON6UdxRcT z?Kk+Fkzng!AmAyG{0-v6&1G;H5~1#?($i;(xP?#ryh#Akys@X5vvvIs`Z_PCC3!_pRIA$$f z;N?Gsj-`G>dhxYxVl9YA9UDd^DcstoC*K-k=H{9~J^3^|kE%GnM6Qyq;QQAoNSKx< zPP6L21$f*Jk0lO5$Zn9=f*elGY^nw}js~{L#iV{DwI!jJJQWf(kmpEGr6>JOY_pWY ziuBGXrfAh{qDng=F~F~&@^`j+^%^}Ys_(8DQbPO6{{*>fS&mts3u(J%8GtgSnpLjH zdeJgE%HAvk@+-5;InR>Z1qMKO%8vc107vpDleo@)dtf4EjrNIPuflOPKsGJRX+8`* zAitp^t@lSTXP=$64AS{L@|!IVKBa-Cq(0uzXK`a^C(jFkoYz>GvQivQMdfzJji5VA zl!zXL%s_hWZEqnc1|9D^t_u4GC6nmCf2=VXH6yKy-<4swjmDzGzBVs4E|5v0BE%Ta z=Bbg{6X{wQpPlUkfNHD@dA^b&V<~G?+4SQTon~Bls4xd4B(4P3d06;Q!E8bKIR4w) zJZIi?sf@iXkP-m*;`dP>Rvq@I0?~cSut#k^L~|BMjU;3}MBaNGE(B!4DPzt)eo{1V zsF*V<%-i^Rw_fRYRAA#TZhSB)w#``O36+&ALqSN>B8+)a1!VGvR7atQbPuGIS8h@ zK&J2%uaswG%1--9LacY_BqjaK?n}S#U*~| zgiPY=UQjjRTUKKNE!U6Rg9alRUbovpxkzF{Zih8GCv*UJpurG0F*7{!M|)$^=IZ;5 zsq#+dS8IIdBpb9IecLvS*nL&|w5WT7R_k-h*9Sn0RwslWLNBoRXr6_IS(dxcy;qW; zKIq-7`ajNp`h(4ewH#x-S4fzAll@hvrSx>TY)bUO9Ao^?GvdqG)JP6akK=8JwM-}% zoaPk}WYj^awg6Z)1;Ww=z+22s>)XDEfq{2jtEY$AoVA%p17-L>&R=DUgUr&v$jA;c z6ci6aMPW3YXq&QxYQcfBjIBA#QRa5D?^h>(;51|(FuJ|@Y7O7RsNYIOmm(uMy!7<4 zjjUWG0eds8P#(tU5_#ahH%RZ_!XW?EpLAgIjXaP3Asrx~G5HU@TZP#`T?yD#@23r0 zVU8$Nln&$X6Di~45o2BXU|t$pP)>_uS6*CQYg~@qIy!)5s}rZ8penNRr7sP;LVge1 z&4#9>Zzr7~0jVohqn@rc6F0w*Cf=j3MnR2Y2c4HsHlPjU3sE zEMqX22|C-JWAoVTv{{}oXAE8S4u-djGy7S>1q$HP9R1=VosH7y61E!7|%Z(zANcBQI3->dMpQonv z_4r}ya+VtIQQ28mw-$WzW?nA73TR8%?v+~Cr;1M3YW{#ora~D?NUDab zdlTW0avJAUh zJnX_S$xO2ob12vDAb{*J-uQVVWI%sMs@?)OuG58;pU z4(2=rdi@!I|39_5nK%xt^9bFT22qvY$)vccPvc!KOM~W91&sHHN418BRePup9lJNJ zYaC^Y6qr4)qG5yfL5-@{yJ@1a1n(ZDU6}>2nEfSA;Fa|Q{3Z{#(7l~X%=G>ugX|WN zP$>LcNloxsI7g$^Rl2s5MViqE@Dr2DlP@qjP>FA$U860no7scBw%3rvDY|M*wsNF% zb5V!!UNpGk;Uz$UUI@c~Dh4!lyv*1kkMG1y7xJ4VG*%sjw#eH0>Zct;t;8)n+NR1; zZIINy`ur)7w~2Y&_OAm1gzxi<&xySwukXjDP8N+mrR-VJB0k*EWhuRg-6` zjJA(~F+w(fxUt*nm+2*FP*b25;R~E-m`xqg~6jJ zUl1Q_7RmvHT1=P)y)z9P$8Mw0 z`)iY`W8!zap#Xvr#{nd{$A@4q7ZMB;!_P9HNYuT`eol<`9Im!-fHRZ-hD1!(^Md?c z{;{V`yj4`-V%zG4lB&^K=QMlvH#yEJxC}UT%JF22vV1kkDr*Rf_(#MgIWNbYr-@n8 zRwy>#o(9l@_29^|TA5XmDId?nwt2ojq z!tuw{wZN9OF+3TAU6_3RG|H+!gB!iU>dEW<*;`OHe5%MO_c|fFHP)i6tb|^_F*fcE z4E*H}nD+g(DDyu5LgA_xiaJYvAdKU=)pmQojy?Ume_oh#3V9h;osuOM{PVM@*;E80 zVq^nQc7*~wlPDwO`OkwJu2Fip`1P(Uod4tBXS|JQ9(=%KRGhDN_=#@8WwNFVz(>-Z z4oBQ1_`l;PD6NWJZvrU+^8d&vZ#NdyX%Z+7OtfP{as91nkG zd+O0y)el1SyE*U8{g8=IT?ZHwP&477PVDi3vjM%NlQ2dEAfyljED`KzHfwTDDMMLu z_Q^xz*yDo*r5M*((eL{6aV}1t_sw)aP%Dw9eVDN97?wmBeH6tshyi3vA{k+?ndp8Z zLXe9+5sgg&KAUV9pmPpC>*eM1(+N4V^MM(7y*ItKlU*j$_;;w-VU#H9Kj?xTw7;PX zu0K`8ka!&`FYvfp(i6!Qs}uyJXws|9ZN&&lT)~W{D8_cs)z<;p$RrntPrGG!@0%|K z3&5b%3o2$^PV1HFE}5}LC>^k}Ueygx0Sssdq}6`|n|~8w|Kbb2Z^h^WD}?o2spr-J zd6+aK@cdKJ4G9T&pH}FSN{c^j3&$DF7HE@icPh@coPLMRA9XZ4bqTC>2}S+VKHqh*UXUMU^U)Dz@Z;)~|Xk4m22axj(09rs!h z9fFs2`y*x_%YXaQX_N)tZb!VPlbBZWvFmR<=|uV{)T-?ntMPMZoz!;)i(rY3zGMQZ zJ*a3Zn!z_hBfnogSD1hPkh;GOw5aKM-mU{3QFE5iuSETVk&YN+9jBuu>7!-tq>6AQ zt!r|RkvPiH{ZAF+$2UMGls^XWZJ?Y(Mk!yd&|O$=&Pb<nYypUxayXAU}$0d|BiSy!kYUa*%=yQEQgBvk?6U#4b}@3uJC6O z=)|C2td0Xb7E7goJR@kZ%z*~$Fpkdy=$Dd)W8*Fm59Y662sl0MZ_jgA#=$iIPN0qX z?E9HnAXB5=>q-i+HhZW+3~8P2*E*2-JkY3@fw~OLy0hth*V);b zq|;!HI9sM^(jU$ZnoA2)l!BteB~EnvQs*zu4<}s!PMTJ+Q!`KEOJCpa;o;LJhaD3g zWlG*ISWZ@lDL<$zvwOGiUvs(apcS)PX%Psy)yYuH3otLn|X!W#vgr*5)#2gFKXqkoZQeOt@^S=Kq*O1?q(;bzjaVr zGP2Ov&7>}^ZRR9GsHQ>k3GE7E*O%YiFeN@^D@{qI1^xiojXW zMbHfc18SwaHbkXVuw14({=|0TAaX51-#;+n75Za_bEM+TZIK*wU|P}wTtI3;;0~A@ z%Nb9pmO7UO3SDB{PrK)PsEuws721t64{Zi|0&1hIm{sG!>~LFoZE>dkvgx3Slf#DQ z;311TeSG_CC&jmKvGGm+omN;%QWh&|pgB@uRu0c&3)_EjM_tTdas>aK@A4)781Kni z7WP;zXeVV0Jkql>B+vp;uj$+cr5WSWhOPHIfgRL4a37qq1+q#6uzN z8CZ~FZ<^5nR+pj4N`=;nU;%Llkm@q<@dbYZ3X9k*NilKG5c1&v!2bDG)_?pB_HX6? z5A0u2j*(#l?9*kA{w2;I$u31@tJl$5^)l$0={SsFp=gLXnZYJS+BR_;0hG3>_;(-_ z5gxm{nTOzo>Hy^94C9EWE+KdDm4pH2Ps5Vh@YsqhRI(k0L{QK&x}dLC+`qCy(0$)-zB|I6yfM(4VNrKM|VZ{O;E%Q2|Sr&Lc|x4 z6R$I6=G4Sdf9me9ouuLaLeTShgc@A(6=0P?$ZjY0!`VHmV`)39zTR?8>I}H;L3BMP zLRuomn_t`9bsW9iuf#^|0X?s)Vtj2n#{J79m?AVdxD0CY$U@Azk@Pu?Vkc7|C`He@)J&%=7jR+8=rZF|pqWa_-BWu~0YP$pF#pXe-d7*B1oL8$~%dT1!k=2)pz zr!C>(dHXmUr|Ve>#fR)CYP(oUFHE#vT|d+cjZf`07(6rWijaJN9C_sl&fA1keGH0F&sxY{jH} z2pLH`L|vh(Z=K~X;Ty(Mxf7ZU)kG4YlQ8PB z)cH1&)c)=r9muSLf?q_OEq%i|i$SmP>j&PCn;Sil?f~uSoz1HH_3332{yc}bcaMHA?Xa{>pQf+Kkc@xmCz*4Pp zjWNPw<%fdE(7OR%44Mk|u2MSzzJ?C*g`B|yivSIrJ`XH{u*d_8fcZa21W_zpv>@e> zxHfM-(sGX3@9*zdp1`GA1hDyE06srcZCmF7_=y*jfK!0l5y}4X-1>Y4h}2<(##o5{ z#T7X6WWD_!cXxhtTyC=BhxP{!}QR)5k>0YcjytH}dv! zv5id2D3p>SJ+_<{k(5ZWve`;iV!rZ~;{q6n$D(~k|L1Bbz^zkyJd%P-wW9Dh3Vxt8 zSir4dzV@ARJO=kX>mAng-XfN`%7R-l(-@_4C{2TUte;f zL7I}hnMQx#0f+F|D5V#ol1+2+6gG*bcsNBf{4A&Io+_)*p{SLrgPQIoj-D|xxo9_x z>DW8{BsA(z6#R}1i&meGP%sYfV=*!o7-PtyHM<`@`WQjRi?f_Vbt7X}J@cJ6?`n5j zRXkXLws?{{$9bTdMfcM4{%#5~vRu`x7b2Iz1)G_8$n3IebCga;(~!2wKd-Y)&G=of znZlZJetA5*eUu?$dhgnY!G^u$)a4}|ypgf+7atb&c-5#k-zA(T26JCFtA1mL<35Rv zGUfTDg5;|Be(g~*6;t`tj}t{op&)jv6rD&FBT5>3tD4tj&( z(0R{3*>iNcq^b8`xxShHVcIJR5F!}s&mbw*>X)iX*Fq^= z^@XXg=O!d<4PB9C`?U4}`Abd$ls)Eng{}oydQv?X!0LO_LI3TWve^i~%i)5vyZLmD z39@sfiXBLjn4-?campNbDuYjIF zb?e36yqJHYQJFbZl3&ckVi`C+@}ZVJLB+6VHx^Np;kGfce`+K7XilFVBS1PTa5r0R z_MI%F^S-b`9BSU&d^s*$Hgetz2gV;-$*L4?QCk3riC@jk&0F55iQ|;1jR;SWjqpd9 zPuRy5uDAU|F34~*1o3WHS*m2>c>yqp2ddFG%iV$wnRfWpFyc>~G>Smsv63(@!z-L+ z9lgn#@;dj=3P6fP5mA_4ZL&%cuWQ`18XZ{VAqMI)KiB{XgJ`o0YNh$F3EzyfHprnEAhd`hpNwsULU-lQ{;xe z$>GL@fW^?Yg=w>`I+pRrkh3NdyUK^q=;sit@8VY|XAY7Y zhKgMd#=_6RV2xKaGsWY(-&rKjxI(xI$PE2r+B_y$l{X)=eU?dpx*R;g$nx{1Vx92U zmU%BYA^J!RZ+P|@#{(|1;K^p^sL#wQQeapYVDo^=*DBG@f^DN1Kk?Er0rp{7(dP3@ z?Toncv(NIytg0dq=qvLZ^FJ_}!VC|un@@DRsL=zh(Qjln6AtlP%+$axgD{A4&+5W; zzs+$!@=egxDQSmIqWklpG$&SieiW0CWVl^R{vR1aOpdX3UpV$33Bo}6)A9(5`WSs2 zQ0{iTQ@0X-2#Ga@urxKtzjA|L#SI{pcD0@ntL#Y>D22eIWJ)_39KKTF+*uyw2|nU#wt=nNd@H}-4;Vh}r|@kX>ADK+GTBk86 zWqv1&MAVxFzY%8r(tHf*MoVQOPR_WWvAZDVk{DC?fb8k5T+bNaEag*(p^QIE2W2%m z5R`yq@mG7HUZx_F-01LLd|H;I%#)#}b)g2*T^!l4{PBb>owMYY&`=b!&r&NzLvDq? zLEZfAz;S)#O8s7oRrQp$H1wfqjB2gdk&ZSmO&o=cx4#jyV(2E zkE!2efCOP<#m{e_QlcI4R`GXeK>b+A73F9q(ju+q0sa0=ivemo8oT@AY};W)!j;ijaHpG6rjHyi%iwu^!cDz_B9lP>LV0NE|;8&fGvmP zW|ON4zuZjRcbTL|$sr|)rnedU#04}-;ekPWJ zvT02W3D^Bu2tcXuvwVK>#?kxB5dKFABCRdoR}bkch7qv}b57zc^Zn&UOM6-_7`~=4 z%c>m4REV=jZJKhZ@yykDr~2M3Dt@G16@|YqTdn5N_5~QEd;L;1_iB0uVP*$WX0;f5 zKeh|xr8?mxSfC9iqw!)diCdS z>%{}5gHtx+Wn}ALnFiA6+;MdH06frTRX*SF3d-jaxC>4?LbV`~TW=p}~y`2!??(7#FCoo?Utsm&_U48b5 zuoAzZKd-G4v}Rv)=y9I5M~laY2Lb{C z5D@$8`#H_7MI8Av{a-MF$HUm@Z#DCBr4NGZ2wmK7(sz{b(@J}12w-ys!J}@8DRT5X z^|H^K$4@k2s=u9fVf5|sZ=Tju1n?mJ|A3SLR+_iRE#O;HqXX~M!Xq?d9nXVLk}(38 zbT8JY3p#*58w|rOuW~48v5O^^roc0UO;(XBLt=g{E~OoZ*O21 zZ~NB*T{c*_>_&3%f+&YC`y;$IxAJnJfJFl6${*pWST`OvgKc4)z-%C$z0hYE`< z##PHeyZQ8~D3DxgVsR{PEC5+H*d|herD=a-+KAd@QIS-R08EbQ{lkx#G&}(DO1U5J?^2ML zxgALMCvzA5VPw^&!|Q)J8dA#&KGcxaGeB4V<5GU_8?(-A2=8J&M8>2!t5(17_~%iR zJ|?Doe8N`Zv6MT{1FlAxoR+pysb6guE^Hl)iGplHbvJ(peq)?S$)grX`)cQ>T zkY_wE`9P8uKkeHWipQzpb*%T$vjT&pnTPdXOm_9JtIr{?EPxc3fU8`Z;btR59`Gwc zzAxx25W$SWaf|FXt<|TZeiephJUJ;q?E~z3c=%dvx=qz>9x>hHkDuHIhahm;;65VU zURTaDCgiTf!QSq&TV_hv_&A=82_WQUhxt0Y>%F2w2KBp}p*a{$+tpSlfQYlPI36c6 z&v;!$Q%Uw_fi18yDjZ5c@&D?BXK`^@l$H3GAdrv$gJHTYeT>VbRh?S7Ysul}@ide+ z@Sm2i2b)vY`(J1`B6zmlgW$0jHu_3dvP)eoPAf3`x}biu)~pK5vtV93j1jUgQ7L!{ zEU@hC8;1&B)8S(o^KG7Ph8O!u<5{D*R{alcZb^xL+t&u$MPZW$VZg>(y#FO+xet84 zLld}jU_9u3scsV(TKQX2w^UuCiomRM&hR$#nb{=YN&CiZJ%v(H^vS$o6_~(1gf3)j ze8q^0aD%x6dY)?3qY$vqe&^zFG5RO>vuE2M&^cV3%_RX=9RqexefoD=X4kF~b*2#2 zpCk~qCL1&i2@g9uyw<$A#li6q){1@)g_#Mzia1Cla&izx+xeLGgn)q8=2Q9BU<}`~ zV;$TFCSY-s2tUQ>%h@fa(W(Z9HgT_>N$4rO$g+Q!>Hzc(!+{XBlrDxA7zOzV`S?o4 zs=vJa8+ahFV(vkHaqK=^q_m#Dy8`jFr(nO){oZFJxx%m!Fw+NdjKH>0WYr&pJc#mi zI!nNLXS@Z3Bd3P!^{=bdlMZfbLBUeFGz);0vJ~PlYO>HiW)-oU&Q~y4qnNG+(vSQ% z{{9VBdc$?J6?E=#sT$=f1-&Jz2mOS~1ZemHKpZ{{$g1yPl1(~WZdH{7b@OwjDubxM zJRR@j*b}qCpk@ADiRNz)CLeZs^&H@_VQrs;=apx;Euqky7*05uD^`BN&d$r{H2_4W zy!kj4-j5lsIyK#lh7eG8nN|&-s<;Qqc}6y9#_8LsFn9_%WN`VjZ8N03S@cX~P?OZN zpDmp+bNJl2CH+G(`mn|Qh|1>VWjPl(xuG~SE>}dj?ds}k$H8b~FjoUCj|O%v7z8x1 z4FGha@zD}1aIyZ5QP;{=+~duZq2iHGH(nq?M%7xDetS4ZiSufqxe#6_#|RlHG=`wZ zW_7wZ>IAQ~GAnC`6bkx3lxIETD{)>N>T66LA&5#PHr4)?lbB111xkp0*Z>CS96sY8s;W;w6PJS;*ae8gqfg8zjaj!g{lFFrmL$*<($b+Kv^5$7 zUbA^Okv@<{LC$nSUY0O6TQJoJ)~rga#ugx8fe0R3OOJNYfN8|u<>FCL5b1;MLl+8f zY&=_JCVW!7Qi^dJ0=y+$3!L_5Qp|fc5t_<6cJb$DAJaZ0Zi|+XBq7mndp9%Hjz;OI z2j=A*g_vLe&}@6{6qAL+OI0z1q$MKK1J-Fk^71;`+h2-`Cd9{|egCfS(AQwHY&7NX z=zlF8Sx;!Og4stg5P3=jT{OB*2~;}aA@mh~%+El3bxTbXx?_ow^4s+%Q$Sr?dHPSj ze(a7ONsH>pMqBr}Q|-|=$GTuEagYp+*u`sT>oI=i**(E1cpX@#ZIUfaOkE`dgMP%# z$d}}9^q!|396xwy8*{}i>C0zDY=2J65<~KIYXzi#sCv7jFo04P>~t)2RgA#B5K*v| zVg&RV*a$*hnRX@{?Gya9xE=!Ka0KwTorxVSzVyadDswXdhoa0b@>JBQpSs(df-QUg zgU#_uRnft;1YWIckuxHC;KBo`^}#%n)Cz7cA{h9Az(ZAm2-dr305SCxg2p;GI9};m zB8oB~Yf~<>saYD!;0Or#IlL^A8w_CW_uLNHw%nji-M&Lc7CHo{pQwqIh@A+Rn8;~z&!wKAojm)F>X-m%?}pw6P^D2PJa4r zLg*@mK|Q_@r|a67lURwG)9K?D3u%d(g}c-MW`**p^CMXM;rN=(qX9NI;(}&5S^%^rSik$`%^OOINJK=$)3dYi z=;-m05nkWBcALm+fu{?cRa!bfJRNAdgRc$UtNZ-dx8?c$(B2{UojlM%|4L%g$&rDU zuc^%nXhxD^F)p?f`hhTSfS#4dYZ0fqA87-!?(zx-f?5r;{uT`NAbtxwFJ~cW{6S5R zTq_J{I*N})1&kd3a-^W$e_7I2s@CqOpsz4vrYDs67Jm4FY^vL{R{O()F&n)1kcB5Y z)vsXOsvuR^!~#3)R_)}iMmU4=7&|{Uzlc?pK0}y)rca&8mqMvfVD>|-WE?22dhS4d zDExuUU}VB~c`+#>F6{cX7|Kqcz_}9ww}FntXCA0PfnLQRjgosalNKOTtg~-uApnRl6`{&KL;Mp-Gpru7RiN z;Iu$)TsHcrVMNV2JlC_46dmPE$=AgcSDI{rdVWzY*gs`DzRm;y|(C zo%_#$N@BgI2N_JbK)4c77nN=3j(l1*TcO(ldNwGiImyV^ai&9|d^Az8=(3#$AMQ?@ z%*PnOxE%^BuW7c^tsKDzFUp}TW4OLp6*6g=`giy(;g_t|0$QdaqH>Hf;@ZMWuPpiX z0@PA7i4eS?VQd@#L)6-G(z%R?7i3_D;Cg@84<5U1^OoO!lT_@}$WxE8^yk&5MvSd) z$b>xAj0;h~=oK=HGJv5YWjMxAMw31rK1d;|P4#L+i8(^xr5~CxY=y%VBN*PrWYEqdD7ZE{ zIyy1I3f5S%vQCspw9k~Xe6T->LPL}XDZxjiwud#Firrq z|1TgWhNxnn>H)EN5bzcXXZAIyu0C9m3S)fWnNp=0@0Q87`&S)s`3`qhsPJPDGw z!%q{%UGBCL5Afvn%w2VLu(P_b4BLr;d6`zm?WL=jPXm0@CAhRNJWe7<`iI2c@>9Um z%4~%5{GS)&1){O^eHVa0F<$O%gq+}73{pYA=XJw)TvR0Y^n9GpA$BmXmc6veLwhg#(K67rdllNz&uG@&%x za`Cy~?6Y|>l?YC@y^0r#`Ku^@2@(%x!Z(;JSyMkido94!GhIp%wX2<*6@c0#0P1ME z0B2G4H!;KlMzgCg0b5XndU%A&G;a$$h*S2!=XMn8PmAz^CA~9#f<3iI0_7YWpTeA6vF+Q@^sRc8yzj^!KmKKbQ zMH3Pty&PzX2>&Hq3iiEZa@ip+qC+(ygXS>yJsa48gMfelgj1P6-__*gbk|6{ogH6f zKajBh`F79y*#;%R9A6=fZdw;J#P30WGbvc**@`B^WjA_PG{xf>tSMg7QcqFTF2iUP zfzt~w=MSJTep4xrRD982tF1v8ad*vBP)s?}hR+{9U=e_~-oJ;7y@s?ql_!a)_=>m@SRsQ7BxE~B{Q#2YyR z2QAl?!<1TSCh`o8Z5CQj^`sZg$$JdQ+)2qtPYQTnVF`df-&8Sy#!bxQmfwEDm*UsHS;CBKNJkF!%`wtG+0al^HAlL=Yj$u zH00;6GX3UQMz0^W5stO1)OQdKfr=r0wL=8pq)e1>0M&zjnuh5pn7_wuw}eefYS{Vq z4JqmS{QP`kK6gFP)A~@}Y3+YcIHQqYspub5t!nkJToI8Q&+;cCEGnGH{)!05 zdG#3ss0%gdaNgjie*f?eL80A+whIipNlP1MOG`-<2xw)@r)AOSt<z>$f?B@4XpTA){?PmxhZ`$e_L#R6d*OH&9F8Vx#Yk_VmJxA_ zWS&H%BwUorxHvM;s0SRX=`<=P?t`gCig>Qu)8PuGdR!Gl0wkD$PYN8xiCClZ9Af=? zNH*|h!f^}0&RPL#8Ga~CGO>ODAki7LFMzhUfxJRW%o{X=AbzE<46|tn^W68(6|;>F z7n(tLEke}!#zg}3S&kJZgUO@&i64r7n#P#C&z%DB$U~Y)2rs0XZ13(~0(|TZ7&2E& zv5x^zKZse~er~d9d{{oNjnTBOOSV+oQ~23GL=aqr3zAeyc)FzwBZ(cxYdhQhBn9tE z_k9@OSA>_<2$tH1H30XdLVSdbh@d6M=*%cOGJyK8tWnkli&JX1%!OD9&53~U1I**+ zt0!qCH;9OMM;1`7|Kl^pj11`B%`Ar|L1hM&w&GS?HXrB`A$IZ595hW^kCZ_0TmQa-(tKNj^m|2Cs+?V1q`=>Y*F8 z$PeFjts|Ax1~&GB#qi&$%wlqmk*LiF1byB@zXfw4e8R94G1__>|L)mvReSQ#JM=(0 zKxw6PKh&x@r5PTb{XO;h+T?LF{pMT4OXo~Y^|U~nw95Ffia#Nt2iP%w=OEp{PKx8T zl0P*S7f&3jX5Y8zXSV~7@eeV503xoukIO>z;p<~C?TeniBxWEbC%y-Q$)l!TXPy*A zI8*Qg2#WEOBAMQFFr(mBJT+w^C$|PyA5ZS)akO`^#7uZC6vq@9D4&3E#%K~yJ=B#i z_Z|Nx02+I_)&vuKQTsa+?rM;j@_5c|K_WImuK-&z-MLb;OZk@=MWX&&Jt zPs=&c&TFFMRlv6$rfiUeQ{948Kp~ItS;m~8d!>c){c8xmcBS?(rDLU%)yVm;tCPU* z(5&e2*1MBvAtOg3_r3@~P5i{z%HA&lr)3x3ZwH+|3ffr2W8aw_h)Cz{X0Yag-BxzKFW^)UZ zoktnze*^~scct5UHy~_M_a-AY`nAw@&joe`RVXlb_TsB#rHd} zOaV-TlH}6#nAvsg3GMLI#H6K;@}X&|2&kZIaSLtOO7@3!xRUS=^XFhWoRE-V9wxfsrA$Z@0@LtlYQ4$txhQ-i3_RcRrtLGGBrziJIeJ|h zog8aUsuywgzif%|uav*i!36k>mYnj+>$O)n8gL{8w2inef7w+OKa!iDUR0ceA~Q>; zg`M)rL}Xc#cuUa7BYt3hv2j^=hMo`>rLUDo54$+vdT#-@Z z9B=9u7t!dySRM`rMOnhh91;0514O*KH!i`+AA-?iw43Z}kMqb{0Q`%K3r5SM67jH+ zk#!Fag4L8XjEo0BY`rh>w(rx^{^_N8FAgX!*Awb8jQ$vzHm3n%5hmjW#ECZpJr|uH zxHOCNVxIgv=-@Q}CZs_N=e1?#g3dNTp#hi*WRKmO{U%xZ4<89VvP>O(B(17?$npRD zME;+;EgxWmYM}lZ=-ID_h&#F|G1Z4gyjfz(Vxa?j7Qq;r=&-PFXJ-MjwA2@1kV<}A zI0YibrtwyHvLsuts9Zp_^+RpLq*afH!i?eF(@gMZL=+03leD%mLyB-P`;>Rh`jC;4 zH>QDT7W{hv#>XLEea2`5X63$U^#CRj*hqkmQv9<{`p>@<*+~!%7oj}@(_%)g*-y-~X3d(J6$Ux?FOD-|`MtFwIC8ZY zK^l+o>i;`3WXQvcBLbI=d4G9(B@&7t-Mvc&w*oU|N)telM1h^e#j0l%<0)|>zU?>84x*hrL5Q)zBvis1zICn-g+dORI(Og)1zBS^?O(!9Rt#SpX&+-YbM zyR%cIcg+X4{cWkC3ar~d$rMOX&vz34AERoZCT09@F)5df>xnPUeGfp^3NsuU=|H9t zh-g#2f|a8pzWUN|*aQF7{Wtu?4MCbkrUhE9ZJr%{hHU1ILst+j6mdp7nnY1j*M zh#zx>i`+`*G#^1kg37{>fu26*hM}yjZBcZz^3$hJp=+m>R^sr>FEDH8(AqxZ6zXVQ z3pm~>A9Plpd0TOhHN{XGdr^=FGH^w_C5%-xXFSLl}(-qPrSNX5^x@ES-xL{7lsexB%MCK_h!lPXrrV-t_czc6K)O z+MXL?h1%iwxgVcK zFxmEl;~O2mnv7VYzDLR{R_bRa!Bada2LS~)HuAPck{WvNnksmZ{?7HH8GCv^wCKa- z-A3ZvVn~A;aGLq96=MLEeh%;-2Fg9iQp!nQc||SVdzZ*({hTggddGu-BFc*@|9AZ} zXHWcF68ca3BQ`dD2pRBQJiR@4248`SkF znFKA(1#YM?`)gx^k7Vr|=8t>X0cCrjO8=23_J^9J~LG;QihA^U%&qAvkYim%fbQbP#>gcy! zdURiYBqgg=QU&(jEx|;YL9JuF|K?2r#4`p8Zkw2l<*08(sr~#Lii8NDtt5(X?>Ce1 z+N(@sq*r$E6bsJaA-Z_3iKi@=#8sEM5z@Z)J+=tsaiQ z^x^uUvaMdqU`j(cNz=1unRH3a!=STiQ(79_O3zNnn2dW}t=FWoeX$$jIFu>=Mp>%9 zV<^==`^xi%YB||mW;dTD5zrh?{RP;a2Ych_;epbqKQgA z%~N5Y_$fryq3j1FDg0j0+yu&)6|H!it3mUebloTkkMWya=U$=J3)fKzWBBYs>fUzg zytrs1`0#pW1C%*dbaF&zJV!kH^AQO zkf}|Q6Cj=ixfZ6%r%qMx7!0M$T-L03@%by{UTGGLD~#U(o=t>Tgz~oiXTMt2p7)Tc zpKvsn$3L2WxRxaIRDhmDBf5&Kk@aRho2SCiNxNaVC&Aq>l`;$g# zI%UG58ub3IG44v=-rk0;(R_6tAG_+3S&jQ`IK^qS*0;ln}0q$>0`A8yjW zRpXfLAUB)wru)a9It2<^70>^sVEyn``=$cg&3cb}Vl@jC`C~0?4qh<|&Np+6R*Zux zP@?>5t*o+TiKj1NVirqmWV?+I!gQ{$+t-?(6- zqJ!;%j4@n%+sM`*p9sC#=N`f4)bgUtI248pG4IglfYqg#NA!KGn@ET5t*{5GXeiCN<&N*jd9|B!RI z2JOrT@pa0m^&(Hbn_cN}ECMl*#J%?)5 zS?fNIVm#J@PODW=`iPEJ+|aNcu~c9;_YLy&)oxvxpPH)GYO6$U5esdKMt-MN>Vr#E z`w85pU5_==0jJxdD*)u;V4xxcszn;)*y-T_oBHps(4m*Mf(F3 z;_d~4=EKToRP^iAQINl(=KANx)xgOD7!IA+CEyt2;v(yzU|+ll-6MbGKk*fOZ@56E zjbr(azLki$PfIW%eSMio@x@!dw<*_f+Y+Ge+M;nSzM@6$d(L~x`Q&C~bxjqE2gwzm z;<3)96BZlMH>yAwsLrO|mpWzN)BZU1(X;XS3!ZF^vY(|RxzQ0fZt4oYo>zq;Fp3ZB zKBy!qVVK*yeSU3IJFaLRkdaM1hd`%YuAFx;HnoIrS5-lj(%MIa2`PFt}^3@}OW$ zR|3p<7TK436=%t>3P<_Iv&dx2QVX}Y-?7IC%lfv6iQHqtZ;+LYGV72Ng0Bx=`^PP}Clk1u`~0&xSvJY1PKcJ4War_aZ)V((>qlIVY@C+kmzKNR&) zEwr$wz&@ALjk7Um1&)f_F96iQ?7Qkghe;J!O(1v`k#` z7~AF3Y>)AH=(USi&g=bpvbo@b&pZPKWupSTYoJIVG(a`ceW?0|y2TpGB#JWPO+e62 z=gHRM&V#1d8v`k4v$cgI^?v-}xGoW&Ei(h=?AtmhEWpc3a^HPrZY;Jk`@wC#e-$!G zW!$BhheN-wDfm&S?q~Vi*Gg}ZFi>Z>QXbhMVrS~EB+bv{jzDX9-)w3U)b+o>kct?v z0tY+(IcdBv@6Nv-t3eW3LO-wYE~dc}k!pL-4kNMi6|$@(0ViunY2b>ysatn3aw&Z< z6$u*B3O|5Y#k*FL4^#F!+Uiu&o^&LmF-L;Zn>(>kL`YIh*v{LgXDV^?bd_1tzRC>S zEujuw7`7#0&&d?K>-DF^Av0Qdf?%rih7`rQvAcNk?vSa3L)3eb`pDy`#ry}Crz2B` zcSuP2ty_hWoX02I-&)or{l_!5+uUsb2OFmI8_Cn$!RWRi4hcA)1-d^9)>MhF z-kygWeTQIg?-JCq)` zU~hJ1S_BdhS6kTzA!DVd$>WXrFGM4cgT&9Zu0MX%zG$(@Frd$-B^Jfq+m)1LWl&|6 z^rOs2wr!}2{sS^2lcVO<(#IDr&!S_+kM%0P#YcA{6cY74bLNbemKFyGhpMV7w43Uy z&{X;0s3gA{>>r;|(j3&@pAZ!q&m+i-$)m<{&og13BL!>#5qF}lT4-?(DRg$lmH9=m zDujmDWK|MpM?bl76^xE(11P8@vm)){K0)Gr#~0{X5*GY3ImARi6^qJ8hyY0|APMtv zT{#UFvKT8b8n>3`4?i&+W?B7K15wKxWd{0w4aiMh4M%?%U!TFTNUB1K_4QCC;Vs@1 zUEQL$cvsVox5Eq<;%&m)E{kD-^Y~SN>hL{!cSL)h;DbU2QI(4X6t#z**|K_+?>M)L zMla1m;RqB=s*t4`Mw#!AvZ1nA1bJfR+%R@IdgTnej+%Vni?hhWfR`AQbGaH#K=4xV zB6Gf$Ue;Yl4sPoCOOQNaH+O&bF0|oz0y8cVY|v!zw9fhF0RF`5?)!fML1)Gy8@ge^ zC-O~CDC5p+_K~g&hi+$AiPG|i-8m?Wiya}lWqDPV-+l*IS=g8|Z%Zn^ zI|+!Ae31YjY}I|gvQnUm?D>U22?D|+lK)x1Kg>}j?y6&;B0N>Z zwPdJmx)ZZdeQxLVnU)TAegD4hTqWQmfOGNbG_GjNvlmsyuO3ipr929cCcKpL&ii_2 zcq+Y=lT+E?0#AG^UxMuZx0V)sn<5M1kc-=Ul6BI{yaWhD9p|bEWh@n+GWPR3LP_nT zPh;-Ws!ExqhhD)F^v=9N&x9wMa9cIhZ%9Y7XoWxw*Z-aKr6)>CN`lwMFq73_fMYb< zWM~M4?qw(L&~Mjm>~85&N&Bys@mQ2rl*yq@bx`^7`4mvz?!FvB=--d|LNs29og^Zf zeRjinMd5yQRFugOJw@B{R;XX}FH3p^!cTv^N7VX7%KXYsRKU>@i>lN(JObr{zSzlV z^2sp#dGN*L0|ElZFIl1Zd^RX_5+G-Ne16uaCRnucyvN-$2oYwgJlZfiJ%-MSI5SSW z?=x!gt%7w&9y#QXYTroU*rELUVI`hA>@o52rpWHZRZV)M(@&o_-JUo5^ z{os+LDQg*?c$db9DT8jlF;pXIMz?YDuUvU^Uf!@vzGZ zTaE&69!SpFpUF=nyiATu_j%+20)f*~L~e%QN*{fb;iUYWLRX+j@Q#<`%E(cv8Z5l7 z6gvs@?hGHYfOe;aFU3SkM;!V#21`pxiMpHd#M|*D)ay32w4k3qt5%BMaUXsC zGmoVfv-|FWQ&&bX9{ye5&6&QX06%@9gY~rD9FN{&QwiK?t+=R2yWcCn(?-!Vv--$& z`GxlejCIjX2nYmhzq1^T`d0a%ZXN#eY3<>GHoTF)zhaw@N8?vdBnq-=mfJSuKS|?e zVXkSK4$+ofSa>&q9uc3wJ(ms*%{w|aB`hKG?5?lB^KxJ9Jtp5B_$-kdm(HIph@fw8 zXlrZRbgtr1m%`|aVKui}nYH?j!tEXz5HO~0QFiE!{sX$v-L`c+T1K@@7<`vD@^t`%L93MP#0vBgS=(op8Jp(K4#X_wZab^l zadCay1u6Z~+fmR+azVF+wQqdaHN4ZxdO@C-X*XB1Tw{NukDqLZO;!~bKFSZ>ON4@# zx6h!kUXy`CgE@i_Vk<(H!$sFTm~p$*OLKADhVT0B(+ycZ+^@yKm9D2hL?5W7kREjNH^z`%-$u<5JLN~>-Ii1%(1qE3^_cuQcw}l@r_0qTr{Mvf?>x9xm>m4k2 zU#!oJj8M}un$k0gn@J?RvM}GS_!=CoW!t&qi@o$}IQxe{=i5|?EtxnjjiT#DT45(9WUYoEMwd{8z>`HG*n`pFlZ^*Xm!*CVbHGUq2_23j2bzH&h=|`elQvpnT z&n=a*SC^`eESKK>a90qmVXM5RW_~S4NRZ##@1k>;h|kkc2qThX>vILmS27P1#V2Oj zQNSwU@wwuxG;AL0-&gk5w#@c?ru@H0>fWggQW3Gu6!o2I46V{FH*A+aTF)>P8)?v; zVJhcJVWZ~Lw5*yKsPXWb7JYP>v8Es|&zxmM-tOb@lSAo#<<>B5=RNeiTz~Rz&oe`% z7Mj;fM&9^n*=K%XtKU1^;}s=dbf|HWnwPbm8o%FJJXkp8Z_*MlI(RARcr7L-$V6|9 z6TLT2RBKQ#U94_?&#Jq(IpyD`RVNW>p|`D-LIzoKUxg1>VyBC{EjgWoNA>F;Wf`}a6Hi&k%kKDesrh{iJtSPw(MGXUNLwsy)p)4c+QPh-sU}&@#Bjvw z=I2#Av(QX1HwPe3^8Cf~XCa`(6VJlJBEv$1;&VG^u!oCuAg$_`w}-Z><3yx*CvM=P zx!C%L6y>INk6*p&Ih`)Si-KZX@hN+5qX!)(dqx{G1Gu819q}ln&(5KIYQn!~Z()(! zW70FXVNf_QV;gmRfh7C)bfZ@6^+=evw>dvPDnF{jA4`k5Tpcvs6hx{Y)kP()H$S*F z?tP(@Ma#9~^$o+ex}L15Zbc;lSI5#AoT_A0@|c3hwHiBqw>z2euk(oo3vk}G04_E! zN*$ez>Bj<9Z+2N}!E9yQtE>Ee3xU(UIcuOXxN=}tUvpT@?#e&f)tQ6ou{9ALEf=E` z+SS7&$20TwnzTl4Q|*hiz`PWh_!~zn!6|;hL_^4(A5G}7)3SNZ^}{V^mW}wJ;U|u& z95{}-HdBG=GKEA$tmq#1Q-ACPZPTGIxdzBwe)(d#!F5yhjljz0;ogFi)0U{m6YIjY zNg4HmaoJnKRH1=msC#K*3XO>0q9}v_pZ5bLG#^0w6eWjkG6#B~d&ULtfxV++abuwBj47@x(r)UNq zUacVZkJdHV<{Xua#1V|Ga_RGb)%E2$)-A)$`AaZPm|+4Aw_W-qOXK#$XsF;w#;vKJ zX`#)+B(yVrV8UkFnKB5iGl{CD5>9t5u717AQVwHWk;RZ3J{?vxuP0%PpO*HYElXLl zKVbNLDlwk2yr9$c-H6^gd2LHTFG z2ZE4JVx5D4BNy2zGRy3PzMA2n)0x{~W9t15myO@)WkXAZfzp?)`lNJF>=i4g zT}Ic;)?Tp%!P8ZfS)a>F{pmu&bBobSIcOQ%3|axPqb;IadZO5d(;Y%7*!J@(#?H>s z4#PXGE=%Ukjnhi1nyM=crRc0R`$tWK_1%TpDq7WVB!Z>8r|!@H*l>jDBo{1~yC2xr zKb2JTJ-I;F*0gpcXNES@q>;8YN%-J1YKC8QURBBLw;OUt^MzlIw_aRx03-xZ-6+hM zcM{J_a}V!y!C+&u7_L+i!d&_FxexP2ck_E^J^j`MIk2`zEM|(V-UogogmG)jXwFVrnAx&s7KCQ|Nj2Q*B^2Ap- z$l1M*gt68%=71Zz?2S{8uPYOm&rnp&6i)I~l1J{ex_|8Ibw8L=lRX*SgX(2>qhhzB z-Szp4l$56!f~JxGW&grvcekR?=U=vbfdK)GjEvw4JSbI5v=pX3%AM<#)1K2VPd3Vp z!C{N%$-_LKh(AQv+BK*4Wuf}6kT^%KaNIOTpCLzQ{lbsp-@GFPro+GDypP8irXN`Z+I8$wjdMHv9uU$FMVFYAkN-xC3l# zrh-R5EP~~5@nzz>A?yHoThikPx1R7oQ@W*JpR0tob!;>|idfX6dJXeqZW!5Yo?<(GYh6PEP(obsYpViRGm%{?P<1*(jbZ{O5G7>OrJ8smYQLuIT*vtJmQ<)B}JuGss~*+WcBOt-pMCQ#ni z_)<#S=?Ra)!9W?ZD6QA+oZCpTq;S3f4UpA%(Y7xEF4sDl3%sxt`iY4Ni_$@p;u7Po z20hMO39?D;U!9sZZbwH^%wqgO5q>PIJ>0PKxn`I_w;5Vpxr2@y|LBSZLts9HT3a z^fWxBN)h1D=AJHs=mUBA(Y2j!X_CT%f{*&(d&1ujwC8sUN-elZH=L__OeUWFA`({( zh1qTnIL2&}T<}b-FZ9mL0HM>rB27O+q$PyzknalCAH&6T)dsWvj;hs(5A!k+x}riC4ooUE7+U{!1ATTN zF?^3e^z3v#;9}`Kv$f;O8XfphH`{-rU@S$f zlubL4hF119{ajZ@;Go;rVBsjg==%CBwvWj}VCSvBM^=LW1C}oGQDh6NCM(G35;lz6 zo7~VS&v+M59=m*Dci2h3wYCyoC9Bd(N=#g|3wCb)x!@A{Sqfp+J|?gY@_pv8{qq(tS1S->JfB zF{9D3uk=4TI>eL+Uc|SlQ=6I5RLYJ~-AVHP>2*#WN|tcj8YNY`RRE6RcduMe32YN~ zb#%~wbXEQv#)zeCTh741@A?41MZu){i#>A%P2qWjS((LEelPq#?k!%@%@=YjuUu_L z%l~%buBfO!s0pw5fEPx8SiwRKWA%;f#||*Ra_$+ED}VMn61mcd(Hf2;}oBxK;i@Xw{{iBYvE@% z>SmH4OP3_@#ke9rJE~x;@ukgt`$*veb|RgWa=@h(PF=67{7S>NUF^36T^(x#T@oD+ zcj{BcW0|rRRpY{}l~h&TZB zVl13ioR^{Kl^aop%fE&rAN7g4@8`=|2?R%IFng{m1{X&D()1wRjF7s_C^(B1#gNT{5bXfu`Y`-{WDY*sMrzPf+{6%|#xg1~OB~z9gwmef?V95O9f7ks4~_ zE!n9&rJOhxUc#XdJ8Ky&{0YsOh1n$e0~gRO)IWAypYl7HoEX9v%t+Ui@E9H&>lr)O z>;V2hQ~*er)W4CknVfwQNrWKwgCEdSJ4@MD2y%1x=&*NK9h(E{bHx!x#cJ(BduC1w z*PQECa?=Y^9zQpqT`OF4-x+@YG$85NkNTFY8N4;`Gy}{CV-{LKlE-25T00y0t<~A) z_i96`h6(aYQEX9VfDm=t97Yy|rd~%J?D0Zb+hZ7d>mL|;M>PaO^HxqfC>%#8cyl>E zSxHHA4=V5$J^AYH{B0CqcGYys+TAziy7ik2?MO$LF^ly%!5(tm``Z#~+P=*@jfoB% z9gMun%H^!u8cqk6!n$P?P~6LR*#P4#?EzEkp6T>lUuRvaT#GlEQmlX@A+5@>cxodb zq>4}^!+qeMtCH{S+kOtY@_7E!JCl=>Kg@VkrM?|=PZ4(NZho0f1^`KJSm5~qe6ZSkz=r(>aeG%r3nni{6pYB`1=hk1d$aUf z4ug(lE#zpR`E)*joVst%(XZYx_(@^V?EUiuG3py^5(rlf?*NHHit!FOlqh~a+eTsi zWVpzmF42NTt9g(DrCGOzw)G=Nn5BAj}Le38b`Mb2b67#`_QuoccBMHSYnri0CH@fNz+O^TiW-Sgdj*#lRq!{X4#v?fjT||s%3EsM_iDsdA=_y*z;EJoTXF%Oh}e1t zaOz*)Jo04-6a0(kAsh)1V#s34xhlms%dVoM(u9F2pwZn)_4L*+*htvc*}m!$lg(l>#<#)*s|^g6*Rce zTYd@Ap#d9L#OxA^52Rw8z+ek%!?(x3z7v)vS~_T6tTtF;ZpuZf&k)*m-)o!j;`zG# zY&#}Ia@hl=^PVJW!|Sx5Jg1=%v*i>dhZXp933`eHeX?r%@E>E~RrhOa4}fcL4#IQt z4iLLzBJ3mt3HK|8aucf=!xVR4Bfk`fjh^Q0y)VC40PE$bpFy-f2QRq}e*^2*@m;FQ z90hT^PX3;gtJYI8rBDfiUr~4q_7#0|z$d~oirk3fwM=Z8_+C!<=L0L>XJ#(%xTa{w z#l@MhlN1ybIG#mCzRd_$IXx}SKQS&QIpQKUp;Nc%5E{aW73fb2T)$UYmCrcf%k~Mn zz$XDu1^JV(JM;u$?K$kEFP*D~O^U6xFH;!Dwi?uoLtUlkFm_4T<0yhp*9-w0)3NuAd?^>zvGZtcZ4F35HseO@{S;nBMa4Rs zPQ>fiPHU588m!0Lzz?>;uNu**TR0a#4p}(4=WfSFM}Jkgi+lz(M&}%^+m+6_e6<{a zW0CDIe$`1KWWGRbt4izdualiq3R&156AYPw5C8P0sKI?XGWfBG0NVMpu=~Mk@$<)H zVR_OT^2C=paeCH?BgbBaRa$JjQ{=&P1ZV*NkfYtc2kyQ_sY{jCE;B++93;z*H_=ZHYuP);uKky}8IhjzBoEyDQk4Y=!upq-Unh2A(QH!r-4Gjz?KY5+A zv9_+0=2-h_)85+L{E)8hn=iSuQDeyUgS{8|v z!w0YNnDuAMgN$XC+i7(ivf%oLUqF2n3{ozm>5ezL3Q&)r^IGrkmCOrY97fzq0dY#a zx^l~PdzmI$V|6AYNonr@gM?jY^viSj<~m$p^gRvU&<9KpgV_(Ao&yT0{*Lc8_;8WC z{)x}kq#}4FaH}w!i9{+x^y|S9P+^Y3r;~-CQ>EodwPn>t@8Q87D%KTxq6c(!oV-J} z;Topf%cCxv^9-!4iyd!jM{9kVvKZ|hep>m)I4)IfL66yW7>0qK9%yk(;XdEp8#&UF zR+Tp!9}8p?vY+oK)^_cLy7O{EJEE0#^@PfPEPf@$rhV|RyD5*AwcR#Ayk}}z3;&{* zvoe=g;|^^YQ-$X%oz{RddaYGr7R&5}R%p=L+}@tIxwo@eu_PMLz;xD+3c1Lwvg2E@ zoK?>_{&o3znlwF3X_){MQOB>Ur13$=OYKQK1QTX^vQk?Dum#s2z=EJE&GhCBLUiDf zhKB3GMqf)Y)-|EmQBn3#wg#2lQK$9=Wx`|?X8)7o((5=-`1_|5NCaTb;01w)A0VDTx6wQU|E!+Pni0*W&06gh zS4|0vG={XLB7tZqKH$*7$#v)CL+;LG&i4_LhG+b=LBuPQwc5%%lBo~HgNU0QVBr&@ zqlq<@G>VKmf0o(QmMUmyFfS=FLMLd={itbh9=Aq3@}dakUf=A@kV{J7cW5rqef;>1 zni2teV?j!J=vo`UoGiv=PSHx59|py06IU|2%AhX_-yns%0vIzH*OcX0#+AU9rL7iY z7$F`qeC^32%BrQPNWG-5$4&yF>94;>@x)=A6i$A!UL#8mWg4rUmmrRT)qGA8QIJjV zj&+r99ZoKMzOyH`#K*=mVC}=nR#F=3F0z?wOBH|lkd85n(aCLZ zv$e4?M$l!`AD<3Z)4S8?XQ`$3#8DLC^Go=&jRj-a!u#yD(;fLwzVcmpb=wD}3MRie zg0K|wXRXH@?*TglXLh#xoo;Y(=_r&KTNygHfSi7 z1QxkKKRgMnf?rP6-eN`MBW0E5Oa29vp4&)tgLAjLFj9s#Tr00sE$83%pH6Ok=a}MD z*V_hMMmgxfmmriBToH-LtP!E;fDz}Y%Tsj!ej)E6L zB(yT>dkq$DBf4gP0`@)?ZKBiUWf?Ur1m^LJQ%?;!Y#+jRWtGoD6=L9?_H@L(cwUb| zK7D;)V1TkVBjVyOYVa{@T(%bXwg7}*=U&}A0FX7Ogb*oA&Ay26vkORxh>8Lf>=aEM zSU?^oTNzlePwg~fCxN&!oQ82Dcxke6)7PYyhbeG#;|nALOeh?f zM~0LxAh`IEgM9`l2uhmeC}qKa+^XuDVP;|JIrc?FD#L=W1KBN}DnH&&+11w4@|)*6 z0_U?1`_p-jEpZ6ZQ^ZDgnu1dHjnF^Hia;Hdd7)C@Yf9z%PUFd3wY}1g^D> zO^-TFu}P2A{%UuLAfn?eGK7Yw%}%0GXz+5#qHk_PJhgMi*4hwx4k%tT)0AtlO8Fxm z$Uy)YlB^15tqNf{Z3JC_RpLs}5plRkQ-v2A&0apVxV~X}qx2>Cb(q3&KtVz3 zyyq?;Eh#xUVti2L?4#vm?^ZcaL@Ga2Iv?^0`Tnp7SIM9u3pp7XZW0XH425;n?uux5 z0^-uWMB^#zvTp{3YEt6{`nnZ7H+`gE-R zwAg~Cjm|q0(WXN<2%}5L`_BkeQ6E7C7~h)SH#hO}Es&{Hd76g!{NtliR&YPMLs{hsR(pdbOg zm$&!X_*Mwur$^!Wd`Wmmf4r zNNMjaPl71Mf0wQ%rD)kG2Off8=oTIe4t0^p!%+^N4VV8j zJ%*>zn+J_kYscpf0_Jdv8X3hB-@%5cySA#8a>4rpyJBw3rq^aITI(nI!YLrmZ#+bI%a&b zvV{LgnjQmr<+1If{0&mtCbn1Zavud*5d?ifG~JgcNNR^I4eq58ZBzJG*q8~)-|wA( z?^RSBe|j?r8Rq&hxNz*Tc|}Dp*D@XL_QhLPmz6}_YKb=FcK@aiv=N90_Ex^)ae!zw z0N5pm>5!xG2904ihbQz!Ih(iqNs(`)O^GndS3$l{$Ug$!*I*XXarWNxbL0qw`FWqu z;O4;r>YMB8>Pq-SgNPJJz8#J9yZKaS?qF-sWux5ecY_mm>%0B-m=+)pX=O--^dysj zmt#`;MzB_|3mTPiP5^h?E!tXsDk3j|GiA!1Cdi*_K6>4JwMu_wG5K~CP-tDHmVH2` z;<9<{4SIW>bGe`Bnp3_t{b!sa@&utX@6tq__Ry?_5iArOt6C3#l3<_iqvZVM$F#uK z6sw*3_aciLDerP3+jhpRlxF9c70ey1QIZmEhKEy@2N-bq&; zY94f4KLWN4mx|LQHe3e|NQfkX7*t$xs2Cy&gnayZJx+hywy8C%E*FHF|5jx*0Wa&{ zGg3$CbSZUkAu6L(otY^3D_AI`&Xi)z%`xrAP4`OMmZ&t436g^bZP1cfk$-BfT^nj3 z4in&m(&l&do`n_&l>G61lWxk>PQFbmj=syNtf{}dT-%D2(oID3zt84NhX@pF{{d99M)YAG*ws)I4sE zz`bGkBW0@J?C$F7BCbtCP!lLL5BGPvuDL!9aNPM}n$;$-X6y8Qt2xqP#C~wvGr!l%9JT;6WA4MxFFu?#0eM&HC0XIfTkZzaUXJdexC@b z)uadpq9w}#{=seF>3n^OMvX$!$m{KwGuQ}`pXlJ!SP21XWyZsw(DwcN!yK{;bzCo$ z{rWG7KlUVf8-)j&Xh2y=CX7P%KlcHJ6@9?i)?Q&@;R&D}Fgpdb6qD?gcj{!3^L;Ut z6X+Hvri-BxsyV%tl|We}!)=f({~u!>5_3XvfY+jx#~XWJHlu54%M?V+N~aGpqCXZD z`PNFBN zrSY&^Ma71ti$qS1uP1fN@DS5+90#KKCrnhw_rJeJzyjP0tc*7D`nyM(B;_Lj_y8Gt ztODL{VyOvH#4~W5Sa4{eCH^Tm)~Aqb9b9cO(Wd;jk^^yu10l-V9b&DB-sDvd9Gbwn+-OdG)$)IHoHUDg-k3pCXi z!>^~iu#hlJnhx`%9Kup}JcE-cV8saVfR4@=zD*TBJo2q=`p~+g_Iin%iX08>c67(&ZF|c zV&KtY=a!|_!mhMcorKFld!FF->bWYSAVtgTG^nB|n6t&MPQ4&r1i|xYgo5nOf+c#~ z6OG^7D-Z1)TB3%ypEO`HFc>B{&%Vp6-c?~S-I%qDRVhe!s5!9jLudOaW{Ph|@j{;D z7j3+kC;z0Q7Zvu~xZauk#>PHXg(~s0v z2Ewie)>o8TJZ*YT)u3y?;H**kt*MOLRipN|(=w1b0(03npu^Ql6P*wTQVs{-auum< ztJ#@U%h$88D2W*^(4Qexw&erK@+0L~p~LMAnwA#cMoeIVZkw^_I(Y2=zLBGTl&xOc zwyM8A3xFczH#bym&ncnl(aQN~HlPVH*Lr&p44aKq({UdW8{dbO?$)Nj3AyFZOoNox zOxYtcH8CcuUf$F1aok(*27z$jjGDINw<1~2ylgOu*2@|d532(5b zjm_h=ijc?3fU+|0FS8)hbN&k@_Gjeojys`>9QIp8UT5R4V*cIV) z+E91Vw4HA*2sHGMlp?q4Dk>cEaJz2ZDowO*W&lvta?NAjbZFNQsgxb5K?)5~uoqc| zkt#6h^z_5APzeLSmlD_q7}tj7zU_eOq$l@qbMV5LX1uwdkz6s0KPwYZrNTJ{Mx6_Q zeEKxHbvetyo!h@%&q+X(QU7}VzFGFc^LrHsH*<9tsCdb z+D)dVI=5cK6(X|PTX-T2=@25n$~c~I*0(GSD!+eJ4r&eP zZI+WspPOB--klbm?(gq|sty0wyEVMmbx~Gaywv?57Y9FBkViUu70^N5dK~u_a;mjm zSKj|@Yvt7@uC7>qlLQxRz^x?Lu`K%ysPM^R(5$ZbXfSNLK-YN2nS;aLZ1UArrU`~& ztE&LVJOAF1J1-jfL>J${-9&L%r!(w=_s&dI6^_pm@eOTPEB}~72tykt*flJV9(*Zm zUY^<2gZ{qN)_owJTlPXte{KKc9oVe3;%LH&I!%WmdIMHaMaK*y6|_j<0y?Wv2CLJc zd1^f}6#{_GI-r=I45$30gRoQl&lQkD58^kFxgn7(kilUPJs4s@r?>d&RuBtB%}Vq?%R7()txEh0R0dNkkz01mS_zvNoW8 zl2=ZwA5Tg)J*ON;VUcZ>t1&Wt$!11m!!BWbj(<2mb+ zk0=Q?;LfVv)18~Kc5{p2TQPWZ&D$WL z_=5U?G4+M};WFwHGIL_7TXA7_)JLeNDX*>R9-^b@?}Wu99eKtuggt`x^e$~T?>a0LEB7cWWy_(ct7^_> zMG-P(kz0- zgRcyzo2y!ZQUiRf)##+FZ2iuxYJPVju3mhQbR;4F<;>3>pKy*pbB zq=tr5+DdVs(vbb2QU0gN*1+0-PAlYM)cQJr(xz-IjhPARehi8QFp)09V&|2mYx^_@ zI3L_%tNth|q;6Fo5W8WlHE&pR$_5}>rR5{QtpwhOi}X*5ipH0V=E$4+?vq0%rV2s5 z{P9;wtMUBg&ib8WSDEVMI0W5?iq@Z})>X`g!8Qkl_f+1;lrAW>CFHbPoL5;eHYF?! z?fveT1q}(^VJ>uaiYo8T$`hg`UhK`Wf$Xg1g=b5$e{SgU9$)SpR^8ooc|OfKaMu-) zHf;s#*Zczi za&n9|NJ>D%hP>hLq8`ll*iaTU`^~^z3%xgf> z+sm~IbIeTDkVX?5wFP=_*8nohD=G9Yvt5<_?e6#f_!S5>*<-zAv0Bc5T#M&=1o3d` zzTC;nu3UZG($B46E+<=<6Hn_NV9Y`1N|7EqYAquKZODZ8vLCEr$A1EFi$@69+wTHL z{8otw<6sBHhTzO!4Bl}ZQWfUsAJpT7Fjd<>%vcDsuDxU0o0ZK9eO>`8!$~S$Of4b# z{ryLWL$jEvq?bScA_C_fZcT7IyZ4{a+QZ2dr97(60>*>b7OBBFBeEJWQCYh4aNq^X zJ4-5`Q|3zK$%#JrltX}U>^4=LIx}`&oup&cnq7JC!LH`Y&QzY!a7-O+++#O+z^r%1 z7K9pLASvuawtDF#-N-Kg-CFr~UP+(uwN9l)`}_OHK2nJ1AMfL~0G>y|31JG+@a=GO zq(a6Jqy)#J4aAYpf7}v3vHuuH3RI>OX9n+-Tk|6Z0zWz$LXKHIKT6EioqW!$e2uGV zgAB_5_+*|j>GV<*I8Q5vR(nmx3{QnKcU+%1KKDxizL7(ApRld9^^vqc@ZI2803@kH z{}0JWVL=~2$p+`;CFGQjK4mZP53l`06zYaB(|{@rxKf;@|GxGk!9RL%hyc*hWfT=D z&WT^Q_~Q!tkl-HYpMj%z5%yxEmFnm(%fq5tZ z#M3TVI{hw#T&r3jYiF~->USlL^rG30oO0I`Bdb>g*Nr?-J&iYX=#4Qg(X#Si9644$iL7cr}C^0IE_E1ADN9mOK z0Y19!$*^=3nZsYtA;c4CVbK1-#)6oUQdT60@MYIVk?EZBso$P_H3&$8F!UaX0xc`8 z{AxKGOs+0x2n$vK9kH?s$t)P8-E*B1g$ZriFG)hJ_8pD4L!jBk%vw_SRulrs2CNjx8c0 z5`uz)BCrT)0Rd4`L`u2@q)WO%1?iA32|;?%Ej3co-6`E5-LRhp>gddGpX)kjpFRIM z7c9Q@esA2*9nbqtC};p~At?c$E(Yexq7G^plaH?uF6sSx-m0vrX(`&!; zx?9O6u*bOaGuBBORS8_7VI-1}S12p{YxioA`%wH=5XnZ$_7-~VJuSLX+UVvff9Z{S zan+x*_AfNXBHSjMT4R5P`d>Ig;MJ`9wWOpZuudqI10n(N7k#I(-d!lTNv7=J1l=}cRC;BML5;?K^vP^F|Ag&`+(7*gM1YzvM$ZP)%zHAZ+`y} z!lm8z9juyrJKZLb1>OrTD@GkLRrIR0#l<#x5IJlf^A4fiumLpKS>hU;Kcs_$4Fz+y zrpV=b*HpJ);FZm*JqZD(mTdH;;8GPVEPst?4VNq4+8ExOR9S9YC?#dLv0Xb`3=N{Q z8aD?RC{zbsPvB)Hy?yIaSqXc2^5FB_VaYY-DxOOF+gyqmakO9Idc4zR@(Y?&uVP=io^LXh-Wd$$%I_%X zU5Te$9Om09Y&TFpP}r_yJ%dF@XTR5iG7CnX2nNWM4mbg*F(ILDk8PHr`{Ut;?%;4| zv)f688W@$|lk7MV?7FR52FciwlQdb^P_+|v@mv}qbx^7KuB)KGGy8S5O54^v<-O5) z@FEhHB(X2zBf9)ZpMck0cjvWnJ=PrG#RAu*5U)%Ag zWQLaXZdEC3V7$Tj*CFL6#>@Mzc}n6Rwk6~NBP}9LhK_-DiHKEWd9jF)*p5*?5<^iu z>j0*>Zp-U>XAF~xa*+okVGNq15ZLM0dkh5p!I;k-$Qbc?7t0l!HFNk!+8FqTKtgyO zN<6*(jl}ltlf&N}9RLm7v)M9c&vr15C0 zOwBj8!Z@P`j;A|19@~S+vsF$#0@UH|TWK-**U(SWc$P=A@&sII)L?+)@pbZlzntRF zmH>Qe{!Z`?p_n3OF=+8(xpa-G4m>G~hGm0h?ZOrDBE(Ek{{a!HL%_2I@hq2%GuU+B zjKBRYMb|zNX8ZMq8p=97bOSv053+YZ&+X7EOkc)E1*{pW{sO8QYyv_XU?0w^Z_Cq@ zC7@H*6P}kI6sr90q%{u91r;ps(@pQoU#ev_A*;(9d{yzy&u<+o+#1j^iX|0tQENcT zvUIMUm+htO1v?a1 zUI&&R&6SlJNVEwu&7faZ>T8E1%dG&Xa~&!mvH{(WX;hvHgazYZb6&dM1HKn7YO+Xs zHqDu~Yhn3!gB->1JN&muCB?=4{QaTV!l+1R1tu5Of!;Amg%Z)inQM$wFn)3hXwOOU zFM_ifHs}(i;RF$4fB}*>-JC`OC%vI74;`c#fO-cXQKzch>c-L5E|(~B8rQ)GJSS?P zdJdC9fQf<@?X@+@l78RK`c&qhq%4{!;X0oQ2*~N(@~K?_2aXaIE#w!|FK1$Vmfw^E zhULgKOyE^LKd(oGb`kA9>v#zd`Y+xcZ5Dxl(fR>V(ICxNtBE_F19mh-bUzqbip%H5 zj||yx^u8wJ`JDqri~a(PGBvds9Qh}tL7#x0lt*6M4!j?_TrCx-SNZ+W7@h}qkQS#u zPHr^DCr4J>pMLo79VrDZ7hLbi5nt94d-E_FE)&qmhWZv9&nR0QWJKHq=NQk{KAJn6 zL6f_L;@uzm`-)Wf1>bXzLP#OWk5c)Ge2At#GF70#1;a~1fWC4Jo@f0)K4iWCYlPg6%$jb)d!w6Ns3qw9=V0Vq{=*Nw zLS}i?v*J#y^TqV%O3Y@B1%=@}u5QV>xl8%jZXia2{ppEmp#mU8G&U^6UnBHcaK_M^c^d$6CXB*k4PG*t8QD5K&ODfz=>|o1i{hmDZI^@_Bg}gw8772 z=7nU$TuPK05SuONtJdYz^98Z&DHSZ@PoHkzy~`hTMVPD|wIb~qJdSAN*Ve7~mN1$v z!zq&+V4l)li83MBl#qSemhB&L zq%xU&TD(0uhwM7h>d`4H^#B@EEPHKl^eciu34MF=A3r_(UNJP6Hg^cx7=qHBfx*U^ zw(IsTNsOadh)nFknLHqV`*M6YlQy<(Ka7Lm#ZNFzA3l?8zF?eq|2h}u~mGICL5-emh zRW&9YoTWRsgP*iX5!-Jn27saV_#e)<2&`&3Fv|Ya$&0$WI-l_ zsnKqpU?f}eYTDs>MD8cQz4-SVj*X>ZQ6Vxaaem)x-oK)3fzr5qx6?+FCTrDGTl=4{ z%510g%!3xrOUP{SAt4ASmP>9+4vP@U+?_h%xVMpidbA#kxDeV{OeT??r+!WL6{Ie- z%Pc@#9vKGezgmM}tTmWe@O<}sor@!Xl9;R+Lt=8^0ei7AoQUIVK=Q$f7E@rk4m7Fd zyvz*19m>2tAk&w-fHU7&YY&D~DlTj99yOOsJ3vE7_Y-B_VEw!dJx!wQ+3t=`z}Nv{?6Fk?)YoAZ zZByxT^{trPl~X7zdGUJBwPV><)_hB&qRX zcY|~J3?&-XP%H(nE=={(tb!W~tK{d{duu9WGx}r3=>whpk=Z+3p7; zw?Ce4aYAXmXrn&=*qa?ukpnP%U%QZA#^xf;Y0-%7;T9!HN_Dup_!x7r4O&D3gW>w4 z<%5DkvGv;Yo0UP6CP80p=z(IjIq1c8ATw$#9F(A)uWd5Pbs%W#wFY z`eN^@I2|1XoS=FOAgqP*LMn)ugMS{FyY#dqjyHdspafR^u-uGSy+Foc(dzDWfPJ;k zq?tOapZ|k}Qh)w1%{f{`@~(Kj`{-B=>v21amym%2_28&l*f282( zzL6NN-5xDo?Mrr~*xfXgITrWp`Kv(M_aR5v@2@v8G(RYT&I}NF>q(W@j7VE2KHT!` z*=mbp80XtrFN#6Uzh_%SvHhfv#5Pd4wC9}y)u=kpb#^K1@89NT>tQfa5FDO)R!&D& zEP??P>YZE`kzaCLfMp9tK=OP{!ZZ9a1CsY=2HQGyC_=oQMw` zHjkEug42~arizx#bt7LfTl}iFZ*qfFt4JelZEX#qLZp-~oN7l=lgz|OP5nc)nY!tb zK*b`WrQX#x=$YdNUe*M=gKb3a#rU5wy5GAwK$ih1oyeYMB+8e9#spO{80cjp+4h?q z47fl}14TA=%4v!2Gl#0x_Vr-E(=7Pnq)L(Z#c=KG@?0TKzcJH3(Q?Z{z4Jw5y1iK= z_!i2JlXQXW2!uf&qq36i_Dlk;n~$JuCO4dK3=pw_qO2qt^k}r3y+8KCNvlSt{mOdK zF6@9J2i~whpKA#!=S1Y^95JN44T+txtnM0f=L0E>)({E_zJ?FlDRIceG@Ozz(+c?; zm{4^;Sf`se;OG<(D&BUx(EUM2c zdt0y;tPbtcW$ynnG$fJRjK6qdNhNRI4SVPnVWvFy+ZznfbX~vF6-AeGY<)!+)J2HT=wElK;#z-=AH<{%w5IMsAGY z#C%)^Gfkv}y^(q+{u6Lh0};*C7=D!>-Y_X-B(@-Uf$TkO9*TI#YZPa9Qe=Q{${))S z9qT<7cR)~Toc8J7Q@38fCN<(#x%U@VY$dg;+l83du7uTt1yzYg=iRYRzD>kBW#=Ee zEF8mNc%5-92;WQS9@Ss_93$0F;?UhSv~lMS*_%fO(n_Htx0+RCDW;NF%%G;S{OGCl^F_x`E^7y{7Hz8AsQfX3;&yStn7z3sidtZZyMyo#k?S1!b?pORAP z2nY!Hz-~c9M&@isihT4@2F&@;V_Du>34U4s%8C*X!>KEBXo6o(16n{so85}KJlmU{ zP*!#bs=^{bt+rJi`k!AadFSv0-0DGg=Z=-N_3ZR?%i)98)>gm$6=`s$k~tOQwO)MV zg!@qZ)fZF__U|Qn28ORb#Lf0_8)=E!S8w{zhK2^D#nO-?I1%8Fym|8l!YqW_SbPRA zNrLPB{r!Qp)tR&X_V*it{Pyc4BqTl03GbbyO1~*5B4Cc=jJUJ2yK6Dm<#mw44V4&p zGjE|rLx35EgY3s}u;*sQWMZbG5C|KaU66rWgTS(&SZExls<`~S zkB5v>uq_)5+?RR+_ry*{a@ZVAjg~9iLn+p#Ss57-cTO`U7ws)yg`ur@qytE-a^x*4 zVfQ|9Wn~47^@T-6{r&yv3Weg5HnP6Z5S>~k<245_yZ1GQ>bABlejg&3OMHBMb~|fe z{?~YS0v+80-Z5k>+G;Lcy43N4)Q55gmXL*V@B*7 zi-Wsx56zo5Z{V3{I+EJKta#=keuhegGlUH??BFV0@Y03*`>VZp@o+3L+3;;0E#gY( z(+xEQ1~QH^#t$SYw1_}h5VoHs^zleFH^6_~c68Th3^${fky+Ck#cyHN;RuPA66iE&@pA z6x7h?ptzHl$2|6Vuy{M^4Y$a0Y`X-!*7vnf$zfX(7uU!*xWSf_KvxL)j$36@(cBIe zW`T#h!)J9rL<+1>G(5-Bc>^(zPv4HG_(=id#gdz^3Z)mMpBZuU)v)CnJBd< z0c{-5S$yK-;o*T4g944i&YJe4N9E9D0WMj7zyVp6`I-6o>({S0;N0rqD>WM9gQ!Oo zhYg=iRaMn8=2u9pB|IU7`A1JrpLhcwKe2YMB}4?ltRic)IN)};m*7)Y=D#sC!etvW zHcKfjnVuaIdJW^cmV#VZf({&Xl9C@^jrreASX*DW-=Xm6Q&+d*p5K?YaG{hk--3^hUf%M~p+bL1efijIX7zl_);!yqap7H6LR8vS zPEYSk@pg&|dTMGjW}FV=HO6c-{l3gfpL66b71*>k7um$b)Y6r?M(6c}NNh;?^*LdM zfzpW9SQi?LQYu(Lo9gKy-#r1_Y8CnW3yZ$LD;yvF%@J3bPy0X;eeyIX zzSPu2Hxz9!z$=DK72IUeiu*ZiU|_&0pTM98!(E%!S_eW>Fm&@RA06S)?90_%Ut1$^ za8!lqpe1K!pC9wpWl0pt;UIIT#)jg}r%&}==4e7gX^KLt8$=p<$aEldy~f|yTjEmbZc}x2bw!2R zOj}%gf{2BQNqYv8e887OS3@HxDyq59$jInPTw80a5kv$eB_%;PgMAuu{)8+BuoB|( zBOr%o4?!6C2du-Mub1E1z|05_3p;b_l&i=(#2wAyN+vV2xG30EI>!g9>fH|su>O9R zyrUu7$-bXC+7j!sIQF$rQu1Aei@c_0nZsOHCbj%zyTruA_Rfy=ojaN)#$DG4HdGy4 zse{8Eb{D?tMU0MC4Q_1UViHfbY^Z&e^2S%;_hK2oUShs*UFYRXiT_BKnv^Rmy~kM8gF@)s+VB=J)JoU zaVg}&^n6!*4&Fo}5V^gDCcaNZxyLWn4TCLFKHyzfNPJh2t#K&jqubDq;3qz8IhCeR z81tNxX8~PE)0?MhMBs&g7833k;lTity0d6pMBCNM|1?b5K1z zaB2}%<|r}1qnZB}j8kY=7Atv~po!zB(;uQYRWPKgZjx{tq1uj3&4pMfYcZLl| z%PdG$Ajg~g$_cN|&CU`%ux#n=EtF1Inim&-^5iq1j12?Pi?D8mg>En9_Fre+)nc6E z^O~(2jbh6V7K*M#b3lPy(x>NJG(lIWK-X@we<@aK6`Tw>cC$2j&}y=?c6pt*!2o@NN)Ph_(kf7d+EIcMDzWgG#_UEMqV0+aE07E5;t%)02UNi0Q!<48+-nA^>n> z6sMh>q$Cqaa1sg6n7*B|}d&{`UUfd^Y^Cf-KYg>Z}+!KMK!2>w_&dvJ}#+f>$aoPc=yF zO>>)X(kNXoeeq?SjVo2aP50$^NS~5dpPxto4ihSrcKb9Q=q?a@gj>P4;_wI}qRG-L zgu?9PmJN(`ywrK^>`D?uniG?`hAd_~oVT`IPoo17kA1hxd}bw>kflwR3gJz>Bqk;X z5!!E_o|1E2jTjeJ#;ZSY9Y(<`>g`=B`dpWie8FLdLbvChP_7H(TNjtNL~S==&jT{r zw?qCeKvuWAb^ zC~P%`N3B>S^tPpZaag}Z{7j0RiioR8QZg9d(NS4QIHFNl+&^nHl4E&Zo^r->x-&>m zwsmljVU~S9rzPI4hLCDm^**d(jeSX6ChTBOhfxo6fGyq!$%pB&8w+Fb|gw zFRjmYe{IR=4rK^0p!0CQ)E?yS{x&4!a-6t^Cd>46D^8B$!bq{zt+X^_O-*@ola8#c z2W-g(v>U;fsh{Pp9x3o9?@?vFYq2I9P%N=r^7YrC!e zoz>*67P`ZxpI^3Ki5oEm1p^qGMbbm!)DJ@$z6J+RnfP~nLT;_iTq7nv8>S-@Qs&{Y zfTujKx2zs8sr+ulfG*js&p;HmU*qM`s-7Mh5fPCaH*N?C)lW}reO}bEv716w%c((@ zLq+5kn!^)nI!KCS%XhF9WPP^i-d^u+mW)y7wfCc-56gg?TD6CUDtaP>hV57a&fsye z73C&9)p$hr;vwwhcg1lji_cT4C+Or>wdhzaP6^phBg)`t)OL2*FaX5d=@wW8wX!gaSiaH(T%Y|Cd9x0%Q0Fd7cr zx3IB6cL$3}?v^b9abv~>lZm=OAT`?WR6KEmwg`HCK8L{QJWS~%v08$4U~PpV=>exV zb)?0uoU4#d{Yk56p{hZd&`gq{L1jK!|uic^v4jJ zGvC`Xg7=t~wRNS=-Bk)(xZvoqp?Z21SJ~3i0`CtO2ZyP13tn!^XzA%S7+iw98GP8QBQoi? zGyZ!F7@^=2O}K~3*xY=vBgr4y>_I;Q;26p!aWjkGYH{3Bcqa?hDjYJjO2sn$W#6I4 zN5+NEp`q7y-!DRFct*J(h?WXIT+8M^pH5N){rBYLdEP77h@XVdhl?-up zJ*TUyJMtByw`?4W5co;+*$%hBE4)v9-~-VpVbB||{PvRGI{}>mRXE%I`=$V%IqMP0 zZzPz{R4;st=35t_J}kYlOmaH^0&EnIVX0tZV(Mg2LuwGr*7GTkWhv+=D9(fQ41@aN z=8_}_o2*PvlOM1&ia*GU-|+EkJPAR;S|EY^{QO|{b;G1JPZzL;OB&?Uvs7OUL~>yc ztSt3o(TZnChJejgS9cD+3`+Dsi#`ts?cD-ZuS7H9e<_8(Jz&6V+uDpL8$w|ZlPrpR z`MBPl`q2Bv^62S(-3JZ_wtxlsJ@LOhaf90jkOzbFco^`=#acXd?#=thi+7p{Pr+LE`MNP9SiFOb*|-d`KkRj zRC||FI~(vAS`1lD0e$IJrh|G>A!i0o1=ichnt!&jxZbVV7$%!D0nl%L1bUX*L8cq7 z-n(WaBl`W#Qw?#su&+d)ix3<0Zg^x|xpzt!!*X-4buUadieOx{b{BDsobAaxc_W~_ zp@IC4`kA@mkB@B#{5@{4_z-naAl@SR7gk&FiMs-HfPVC)xEFRZZDS!uSr9L@8ns6 z?A#Cya)gv5&IJ}uE8{21%1a4RS&52LK~rO6uO=JF!aP$_iks}PFwX6C%N$|hdV`++ zA`3%OQc}Refe7g~zC{~c6sb$_P)2Ipb?DT3K6OCE?}fY@iL?~3siRznGh<6jm1dSx z5rRu4ETIHN=G3ExQ~dgZ=>oEudD<-!)zv$CV~%Hg+1MPQpuX)udKkUu70&Y2^SZWj z`Ul|56Go=~PzM9zbDVTI&7gjRCT` z{GDMz5#l-&$RCrH=3klT#ofThCY3D42_oeW5(yix@_u#_O7Cz!LM9zc%Yrg-3J(wT zC??&%I9|p9Q4Q@-7f$~5!R6vL{41l4oLY(IH)k2Fb39^Wal917jq@NWwI@+(#~#a% zIS9ws7U5T)&FLY54+JjBuJp1>(#kKrzQ|K@a5PmpJbL zXp*tf++;u8(BK-GOt+mxJ%dAm=wimO&qQH~kLFfZr9W$H=_p|t>T7A#RTC>CxXncJ zD!(6K>tVo2LF*q`n<{r4p&%-(4G5mfLEj3X{@{NnjAV?eS2=i{`}RoEjv;Y2;y^}g zx;^3SZodvDCa?L%E-ULC;PJ0e8|PH@_05MHZm6rrSzA*AFhn5G1D^D5ul6VTd{m*$ z3sx{VSjZ=qh&qqWmqk}n>>SBaw7s)V>g_Eh68T|vc=+4K!bRUK60fdwN^eN-7F@&k)#`J0hI^$BPQEHEi5nBr!H-44ST#Qi7#w$ZVz%` z7Cjj%P@NEXqk3x40_C>cl&m;Ume>*PQlC9aG0)7Oe9DTp{u#`r?sNwiBC^{&i+#DS z2m}#(o^jOy}xOy|NLT?T7BNsq(|A)c?F0b6tr(5BiZb#iIFvLV-JpH;vCu z7%Jq=Q`ebEGTo`jjVn;#^!%L>LzANwu5-r_)s$V*S_KzkYcbxq`TMs6L5}J;hK9!X-{NvU z2a3yjvE$;hJ`m^R$w6jA?~m2B{&VBTCI;z%jDW~|s{K;`Ybwm2hEfm>`uiMF+EGqyNajI>-)zpQ$>3RinfKG%veF^)NEwU zIx-VEJ+*102%_)%j*i~FC8@P_vxfTqte3UnLc!0}FJKswpUVbx<>K zK?s=CM=HJ%`<(JBf+8X#b?zN+0&xmk*VaB~Iu{kajf|NwQdYyhaibXMoRK$`Wf!3X z+c%&&2P)L-hr;O8sD2-<9(NouJ@h(vGiT?Ndjvz5w}PQ8G9EvNYF&c%x9fPxXF+?I zW3ntdEst|ADVRUu;@Z_~1PtGyv0i)@GpopS+v6QCm#!`Yyh)IF>{lB(t@B_!)iZ!z zx_@6roLPR%NLR?(VUNzg6H8X1AmcF^16GuCpXOye+DpnAFN`Ciq&}_Pr#F78Pb;KP zK~v{W@;ip;nuDGfnORws)YK?y)Xl9HXw~H8WDq`iTmUHIBDj2}k<|6W=qveJ}djaxvsEY4Na~2Nn zEM)&5CoR|0CYYI-%~BzmW<*?K!(D7Y*dNlrwNR<3nWtB7jAg7%u!ZN?3LDsT76-kF@))adBXXpF* z1)B_&t?p}h>OOk(X^U~I>d_MmMa78#SE%5q5cSy^)0^8<;m`j!Yc>}LAtfU(541;y z4`on5K#t*XD*Vh1DMq(V3cN{&BO1J*;@tI^UX%H}s zj7FZsHMF+sG<}FpPJVQ70i74vTX_`XwX(7YHAQ^9^NABpVF5T% zoE&3gV-F=T&{r*tGWSP4B93;&lYrU)dlD@dTZk<-mDYdlkdb)4JZrVNsBX5DwmO#{ zl@i&#>U{B-ekP^4;O+a zaH-B+?!Pkl#K#dz&qX4hPTPgZb6EsSz z%ogAJ4p-LG0tpITyc87`b>{o?MQrd;AChw(Tnn)D9L^tjD)}EByTwi*A_W|aNNBZI z=hb<6QqCjG;fG&u{9Ez9$@)W%Mp1QInEzI={Tz?#lK($``P_<9XRQA_r~)Vw#ReyQ z5`Ia-4)zz`Tt@%LSb%nW>dxz~PjR&8;F&B{N^X-QaPzWXif|b2Ek8%q8B2pJ>y$j< z=YIbc{*28dKPV%GTt(Fd z5BK%!*XQ`PHgj@bZUC!t-NZyq2O0$`!Cq+aHJpWu>E3kh`fv%mw~fwEFkgN(@%-6@ z1UB>eoBpAqAdtm^nB6|9qoGRc+XkeiB z)29OJ>Tg~(0m(^9@|RA-^rhj$>lBD&q|erxO>@(2Dc{v-=hIlHVtn67qC~ou zNH)-Z$0{tyyCS)eJF&LZX zT^E61?)h4kL>1r>cCYblVm*($LDV<`T0hB1Gy(5Pb>8mgw=&#DjhjV@t)ojlShPz2 zy!7I19Xz0!nWjfpkd_yU8hw*zq2u||+VRt~$#Ga2Cp$Pe_UAJ&ChdtGR#w)mL*3~Cs!y?&yG?Ly1#wd2P=7&$rMRo^xK*n0cA$LS@|G)Xf6QJDXp|k$T5(m&biwI zf+-3+?hVSy(%sdJ0GzsUOQ>*QuiucX)Koj)!$aRgGZ3CX+Un=JO)`}A&DX6j)8;RJ z@nfuse2cpcOvdM9TakoJ$pF%3|E9F+=qa`17I4iMm}qXv!0Rr9!@9q|$)E+0G9YTQ zYYG5tp2ShHSd@G(!YMoCj6ggTH-)`*XO47nnCr_8kG|irm&>ar1zEbAe~0QX19WK~ zhSt-+8Cv|>L$XNZJ&y$KMz7eIYAGq)j62SJrG!&HRKekQ?~0@+k|(CFzB%c}WYFBG zNQ{Lg+HVWWAfIPOk@Y%SF6JL=;9LZ+S4Gx|IU5>%-S4v5WU#A!@+~Vpb5=?Prq)9n zo)x9R+hr57%kXulKgyj=TalKO6zG+PasauqIp!0J?D#rb{_aLV>B??P(t47CtO*HkSbZzVp z+PaSTUFrhbhO{h*eqTL};%@s7JAmGE^2{2CDY;-Aw`}IJW6H!-0Is;blBTS$`s*l% zQnx-A%)5xIxIV;F$;m>V$G9fmcBR_O^k6YJ5QkQIs3O9H@sC}mwHPT7k$SYgy>aj) z-x|IYd=i%T;>*jtN>QaqE^WWyqN4JlA=Qbx{Fz>r3aaL);U;T2nKc9@TQSH=9bdkD z@gvaFS6$P>8dmSf{ENm=qRV{fc>rK%YiJbv)t zD$Gg_j<~F>zgse-jh3}%&3DcDe26~7*}p6jL>R<{V1E9uk{e`EFdF0x*4C1d*O-h8 z?7n@ASzy#R`VH3QT1q7)>JTe_QMo$ATgbjkLoA^tEVe#XM|^tXnZm?UUoNBJlFIyK z%u*>~-31o~hbNunEW0Q|I4lr{koEpyBr%LF2)HD>9vr#h(M_S)pMngdI7?cg_QeSF zqgVU>?SPs2Bo*~;^9R<*HodwE=#%_4%0yqC_eqAZFh(JJ?2~)H+0x!$_nintS8CGv z(!aEcbJ>6{reG__-@h?a4N*ux6hyr$+39d6H9cKg+}M0~<8*uczPVvseeh;MAx3T8 zlZbmNxBI*6b)nU3I7;)yUwawHITH1}{w3M4ZMH}Oo<-E15v* zqyewaU!0 z1w}N!XGX=3ft+2PGCjw0@{ieqg+P49JY3)J+>55(yELEvF*UW3eB5|-qCO!dQaU4& zgB=eWn;x7O4_})AO}se7p`>8j*CSJw!wBtz$(Dw07u_(&Ldv{k?S^4YUUTS8h3Wy6mxN+j~Qro&Fj_pF48X8VVXO`uc z9OtajAIvxUhQ$k4Jkmd2TV_^Fc18M9y8Y)@w>*aH+?y`#U1G8qMo)R6-%IZ>%0s)6 zYTq%nI5^e;1gC(IP*8m%;9Gy#Wbp8shJ76{@8tR#4hSp_DNX_9S6)&;8TQ?EFDoW_ z2VI+(7yPOsw_5A7sYg0eMS4W!>em!lIQFiizk_bHbye@&{+d5_W>(`I)rR6QA zcw~X4(Uwp&opqty?NB|8cJp zgm@~Md!a;;_cQaLni(kYeYSv(XMT}jZ8PJZgH9A%E;kJeEp2mx`LQV0B_&mCL0jJ` zS!z1dL9qE5N}^qzYe)0!5TWQo7+HRxu?d(EVC=coJmnj#NU z6xwoTT9EfGX4)2++3p*>y#Q;I_O@>^-W#pt-z=XGjVtTi9rcK*6s;{*#*6%iqoX&r zxtaGafw>3#7?sr;uU%YJXWEk56Ur7RnVnEpjDV=Jz}&IX^1vsdv9X;WSh-yo_QvY#R|{2`D4ICztgF9%edCRfJGnHf3XTKzC=T|oB^mmA2PD7THgoF5L?{x1gk{5b%Fa&;*eFtv)KahXr&jkmW`H_ zy1qENb&yjxGSJZ#I2@kJO%QP?p}zDCBeY?BD#}D|JoxP~$RhfmK7LseU4?NA!`#-^ z$*U2~e6y+RGHi~dd_9UK*3rw41A``k=`b)f)ci!^%k#Vw6O$JQp4>ZPkV5u#% zFMj;+sJn+^uKo2p@ci<_?|O6!|J!Z*{`!%J^aqv#sIJl4cGz|zdiy7G`M+9mtjj8i z{UcNSG?U7_Lov+@Nzy~Qc9jXkHs)lR?jO!h$vxnIl=JbA**2S7EDjRK!g_J*u~_jF z6Rs9_uV>$s5>;5;r_sr=^mfvE{G0vu^*AFExusBFX2@SG@Duv(1mRc}<=KWgX(9gj z(VTf0)wY)tSL9=-3<3)t_XW_t@vMrEu{HHYjO0g~7YS0IY9`1+51coq_TJcQ5^u{}792|ytEi~hr}*gpLwoo%@9QNZsP^zRL)`SOTSN0w z*GNo6>l?QO?{h8iXc7HBH@s_B+=uy19%GjJFPHVR;C&KzWE+;v^#CG;VC^1ADCy~> z>w8NMK0CYY6XQq*D3OD1Wo3!qeTrXOy}UwTy)i#2pYIK)rn01>az}SJf`Zj%rp*8k zub+TjxwxRew$?dZ@Lyo1KiEN3svw5i*}0@g*{XhG5}L*@e|#*+$RW9IEqZJmTNcS~ z#IQfu+kn;eywl-@{eoQBZ1bD~BuXE?)N4e5jV4$YS*v!JjK)| zEfdkxlQCW~T~q<>60BR<8=yUfgy0tx^oYyIhC;LeVB(RlEIk40QRB@;BZDuzJ=@^g z;O36pQycuZ&kJv=z|pvhmF-mj=-gZgoTrnae)Nv|VwCDSfo04r_e)G+S?)c~FnpMk z#~k~0Ks>zGOw%5Z$gphC!Rx7m39|s$>6KYPEhuVXOL}ZS1?&nTD_En7gKe zao$C2BM5MCPWuo=j7Q4l&8I?|V8z2^fV*(?dVW=RHyy2^5K5YG#t)2M>Bc>qo{1zc zM&J=jY-LH1l91e_rk15)0ZX>$_o^w8$NJ;oaw@Xuw%VBg@N3#hc5WIp*2wVcFOnY} zUavK7N=ccTZvnCv!J~P(EHB&I%l%+bE^E9oPADO315nOfcNr-a!u>Af>aIykeR}ZZ zV@6GrJxF%#?Ggo9BxWzvc?~Xuj}K-0i%uXPQ{DV5DjAFe6I^2I`c~n!I(IM3-zl?% zya}Axz_Ix2ADDKhi)UTqqt@Otuk60mEL)nTOi!lVn@K=Iy|<0H3Jg3<0f0qGH}LT6 zS0@u-6wz^?#=G2ohFoZypk*=mq+D9;LLG6AeRZkKbn8{tBbDVXqqOADqIlQ$fF!xjPpGYYG`|YY>x}(z z93|;n-PwB;Q$N`@E+ z?m|fBfAU{Z9C@Z{40e;c3ZWPVXuVsF=N=Gn4LUO?3{oFeo2A7MqWY!%M+e#yG(oVQ zc7-8=0!rC7a~0P8fLng*7bqq-O2NCDJj+E5ce_M9$w89a_V#AOItI;7cTYGe5QqJU zsSF7x+??yaOQB7$iy!lALY5s^r#m{vrKEhYUmPU#IRc(q%&O{D+}K5=IQmh&1G`i6_t>f}37sj+#z`ck~tLM}X63b{_9QGv_#?pI;!K_X?O zg7`x_f7rz6Q=qfO9dW6K8kvbH7F#%&L873L*pr!rmUg)VPpQvOwLNp1`~yw7Ql%Fd zhL5arSH=&F-}yQQ-o@}$3@hN_LCe&+uv;BnkT z`6bw@ZRGVso_cB6KUR_oVo`M(>`XaJ!1Wyb_ytCb;6uC$ZJ5*uK~XaBvtnNTW?06t zIOY?~tkJNwb&fFn#5{9qD)m2q*|zf$eqWQVoF?jzdWo>d{Q=0lEoH?X;G!7)=#SIG zm`7Y#bZ>EoE0j(xfAR~z1M#zvHTwUI;{T6+If2tEz3v#LJXOVE#cay`FM5tfx@vL! zq%j*;m42sYS+B%koc^78Llq>bxZP3tF)J@MzRnT(>qqp2yU0?Yw>Y+vLVYfDO#>D%w6!j@XMEpo@BW; zZQpDu56$MLTBX8tC5|f;q5hr#1a{1rCqi4$cxUljM68Pm*0hd%XG#Fn+ z8)<7tCd<~jGij77cq^*0Q5$#dZ2`5#b!h7^0A}f6feHzf6MS)?_M0Jcd5}B%j{zqw zExr;F+DA9kuT$|-=Uh~r#r)V;oBOQ2pk;c4$42z~34H}n`7JEoIXd29n!aUr>_gG> z(xn4WO)MVoPaKSdhX%Sc8(KJavL->KXSpy ze&ArwPFPnS173Z?4-xv>4V9GwIO#|M4hn?E;IvFI%>u6AM{V0D3cne0l*#E`0KFC# zVl00bZ4m`i_uloQYE!xU1cxsvwEngj#)yk`SZh^|n%RMBo(`;Q8PXD%z`J(|4L+<+<@?~PBqSd=PUv>G^;94068$}yi;S0c*>w%* zO%+Z$M(R~LVF<`y#IVUM!w5d&2h~P;b8E_-KK<06fTuo9tC{<4!9qz=f>OvT9bzPY z&7dvEiSx~n2kRJPsG!Cg1j>8*efT;GrNv(x_0QcXZ*85e^_SS+C4|h-Epe%BV@k7$76Gb033-iw_<0l|b^C$G?xx86aMWdj*_4Ft1<)jb zY=Xi|mN*S5a8^~`*6zmYYW{`hG+tb?+c-biia)8;@~lx*u)_yOydi?!2#EgjteAT& zlbRo^suHvtAW;ohR274l5P?WeuJ?k%4b+`JajiN%ia>Swm{`YdWau4Em)~Lc^XFF+ zCweHk*{J;%(q*ui;MnpaoEazuCQ>e*dgY4t3a+EW1%(IpA9jHg>Y3@@SVTJy_uo(- z`*m-!=2EBUDDU<84cs}OaK+xI{rnL=Y_YN5XhaOy92}ci?4uwSP_x+0^D&{@OCI}U z3EbeuWfX%4Af_*~RJ{ruR{TECj4^|$ZFH+WGxxGcEELj^H9s^Lk_Sb&vLWxE|72m1 z$m#j-bGs=~GzvAN`M|@TEPL#wUiBvgzF?we-z12Ih4nrt2xKQNJ8NeILi{hJs0?7& z<~ny&{6f~3Eh9@+pMp_6Z$8#Oc{XdbFV|%D!(&hs!4@BY+0JKwv{Cvk01I%P^TPvG zryCd$K)uu%Rqw77npY$AsaF1Cu0(+mX@vc0b92Y8)bL&KlSqR1_Myp7=2fD9#l5x8 zNa9jcn4PJT8Be>2wE*F^9XL6OxrQ85vrT7K@T{91W;@Ol4#54QQOs@OhoB{w0Ie!d z3<@6|C#UL{x3z2EE8TJ#x8Us%44n$xcUv2&sD(ryNO*d}x9@M|f|+RGB_jpm-xG)e z5t*N9!)CawR`%_#{g*ZWzMOZ%+#z-1mu5yjMvwc83qz<75LZ=Pn8M=_!j#Sm8igJ@ zt(M`6^1a?$JxW&r8EW%Gc8FWaMxUI?etRM(Ee-Bp`zNyjo*DP%k-y*YYy>(%HN;3i z+t{3``!s~gX2d|0kazd1%AcdAy#=p0Z=N=yY*Td|;k8P-a zFj`NxMtFi7Ki1wI_0Jv+)~u4KP;YRVkph&C$>gb4o84b- zlJf9q@w2{6zW-*2pYe|bhZFqXUoJdC!)19I9(Y%O1PvTzC{KI9aZN z;>;9z<59{_Sdy7Fdb9tmQ1DR1gYCs}7V<P_K+2D3&8ntrks7L+?$9yP;?TCw`j@ZAo)XM zo*?Og{KoIwH&rDJR{X|{z&)sQi`vrM~g8{P|u%hW`NI z(J@jzy)z1*bBd}=?%BU$H5U?&UKT6}8oHr{{|A1ul*M|Z)${`c{ogdH{2$q4I`01N zpts4ljd#+*{-c!n9R){P2Oc#FD2Zf|U?qJ$`Ppmn|tnmKaKIR z>@uv(9jpBv-(&6LDinJ#(-{Xij?(d%xn9Z+C7Tn$$_L~A>W%kI+<)=W_jJ3Q>~@3B z*%1B5=6B z!ZR~VCljRlS=~?Fcxz9qtzA10_lL5ABC~$uQ}rhLzZ@FBP;sZ!9pK-(TDRvQ07qE- z^q$3HrNcq{*Wux$YZ}jdVI`~b1|(ZgRF%gNH%1Mw0-k^@EZA47*tU)&cDFKikL2WX z4f!Um*q`J)3@_^_3wP(eFW4H+3WehewR3W1^`xFsw?B>P`)!a-ptM@nwo%fBVEDBl z)g3q6!=M*`r?nSzqf{^6l#(*H4g_Egf{pq&(IXK&r=bA^6c2fMb2-{HK0mu3Y6zwH z)oGnhQBd64D0tmbQQ_WH27G&qdFQk2x#sxZcb_d-Gtl&x*(Md#oI+KdfCLlvhDs|y zw__`&x=&wq7`0%%^L9#df$C~kIg}^j*VGHyp-DAGk9@NS9U;7Z z$FmmY{;u9IzHqmCTCud^`a`Y@OD}yRB1~vK;`S*k%P`Eu7YlonQc_N5$k%`p6G|Pf z5g6Dyq}0=6YYMwody=1opWhiK-NV9-X&~VNJSlpu_n0=5EzfLd%R?D^vO ze$eQsayISa(_#Ku^&q6#FhcFgb9Cchdy&|7cdPO`y%6a3p-|<5NP)f2$#M|_iX>m( z%$oLgU(%he8JSSOE`iX%#6%1vZ5Yh8({nB9~L zY4<5As<|43D;dwn2uTm9knWBU<^cHoqftlx-#*k%)L}N(v1#9OV73Ow( zY{Jki?&5ao*1boxjKQaIc~S$XgBTpWWW|mx3}t5zxo(yPQudX)-VhndpeIwTbu`Fu z*;}8mp0dA&3kC3~c4+wB3Rx2U=P;I;co&r@DrLXJ2%W>OzNs=*wmM$FZb%@Mw5jvrwqe+^CcNM^;kurMmp-$&Tfq~zw7rJ>2$dyir}!drdR}=+59Gb zKQNT<6es@_o?ihW!bcB?u_?q?#W<$Mg;l>PC)rqLh7*G~4@kYhIVz{d$a>P|%l7iK zZ-vRq)Jv@wq%?}IH~}f#K`bJIs5$${o@t8RA^T`|OloQw>GZTGG|Nk>r&m-$o_xkc z55b)8js+1(R06ZO_yW|s&rkQ7qtQtzjGXQd?Yx4CjChXMWGXi)Ds0`UVK>r!(l7uV zG6-r12DTx<1#}u*Wc1s;O3-^lt+_?bE^|iW7Qq+7#a6FMUTPHRO3E#ISK=kKRCFq6 zPn$3U<*~k6bAz)`f@}6w%5d<~BE2f(ofQ_XT55ZHY=;WX-MVM~jlwVgt_UvyP|K$F zBC#O>R03E6HIKfkC)v2ZGy`L^?oOP9h|M_S#?))*l#8IlUF|2t#$lk$8iv|(-+ZtL znGZ8`TGCCk1HbtSLi^49<&1vES&c~4c3KzJrj7+$);#jz4Gu~+e( zY>5w&Dpn9;=+^f?hD|$u^ruZLjHs00x?;54WZ!mNf`2!2?993p={zw2KvRL$n)sc8 zT%g;khQB~dhHT%y>i(j&vEf?B4zYc%3U1s0@))dqEpmoY+aH_%25`Jx2^kj)M-Y#K z`$gYohAJma*kyLz=8uhi08~)ozW=7W`3qLb{!?|cZ&-fS)Jy$$tyi3`D8GDVw|PZT zL0tcg!*f^)$*c9k)e1f(em8^r5A%1AunIC@{2P5ke9bT z`oO&~1V~!v2nneA7cyL@ZgIt3o*_!EXbTy}1z>(IUyhJVB*(80bT)^Mtvf2c;O9Be z?ez^l9~>)ymNdU&=8Y4=Ba+^b_mQ4n5(WU|nEO`%1UQGl*|H~$VFLbA?=R_NU}vlUC73>-c)2Zg2{}E0fz6JOi8%wA{4=9}DQR9l+zSEQ(Z$1d4sCMpxECmmD-pW=H z>bTB)a1=^0x7N*P%fqMWf;JObx1;3b$)b4JeJ$l3^JzxO0Zd5QRzt=`6W&1y%~`2W z34*R@ZS7bTTXk)9wQkKx{^jFRzFb)SvP0A8UV8DnxtQ-RTr^>I;(Oc?5LPr~M~;

A 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 nubE?DSO9EB!|HX0hPM9 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java index c0c30f12d..5e457439b 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java @@ -27,7 +27,7 @@ import java.util.Map; import com.iluwatar.hexagonal.banking.WireTransfers; import com.iluwatar.hexagonal.banking.WireTransfersImpl; import com.iluwatar.hexagonal.database.LotteryTicketRepository; -import com.iluwatar.hexagonal.database.LotteryTicketRepositoryMock; +import com.iluwatar.hexagonal.database.LotteryTicketInMemoryRepository; import com.iluwatar.hexagonal.domain.LotteryConstants; import com.iluwatar.hexagonal.domain.LotteryNumbers; import com.iluwatar.hexagonal.domain.LotteryTicket; @@ -55,7 +55,7 @@ public class LotteryAdministrationImpl implements LotteryAdministration { private final WireTransfers bank = new WireTransfersImpl(); public LotteryAdministrationImpl() { - repository = new LotteryTicketRepositoryMock(); + repository = new LotteryTicketInMemoryRepository(); } @Override diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryMock.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketInMemoryRepository.java similarity index 96% rename from hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryMock.java rename to hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketInMemoryRepository.java index 0629fb6db..fe17d8cdf 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryMock.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketInMemoryRepository.java @@ -34,7 +34,7 @@ import com.iluwatar.hexagonal.domain.LotteryTicketId; * Mock database for lottery tickets. * */ -public class LotteryTicketRepositoryMock implements LotteryTicketRepository { +public class LotteryTicketInMemoryRepository implements LotteryTicketRepository { private static Map tickets = new HashMap<>(); diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java index d9a948597..d446e93be 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java @@ -27,7 +27,7 @@ import java.util.Optional; import com.iluwatar.hexagonal.banking.WireTransfers; import com.iluwatar.hexagonal.banking.WireTransfersImpl; import com.iluwatar.hexagonal.database.LotteryTicketRepository; -import com.iluwatar.hexagonal.database.LotteryTicketRepositoryMock; +import com.iluwatar.hexagonal.database.LotteryTicketInMemoryRepository; import com.iluwatar.hexagonal.domain.LotteryConstants; import com.iluwatar.hexagonal.domain.LotteryNumbers; import com.iluwatar.hexagonal.domain.LotteryTicket; @@ -51,7 +51,7 @@ public class LotteryServiceImpl implements LotteryService { private final LotteryNotifications notifications = new LotteryNotificationsImpl(); public LotteryServiceImpl() { - repository = new LotteryTicketRepositoryMock(); + repository = new LotteryTicketInMemoryRepository(); } @Override diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryTest.java index a80e4817a..b20e928c8 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryTest.java @@ -31,7 +31,7 @@ import org.junit.Before; import org.junit.Test; import com.iluwatar.hexagonal.database.LotteryTicketRepository; -import com.iluwatar.hexagonal.database.LotteryTicketRepositoryMock; +import com.iluwatar.hexagonal.database.LotteryTicketInMemoryRepository; import com.iluwatar.hexagonal.domain.LotteryTicket; import com.iluwatar.hexagonal.domain.LotteryTicketId; import com.iluwatar.hexagonal.test.LotteryTestUtils; @@ -43,7 +43,7 @@ import com.iluwatar.hexagonal.test.LotteryTestUtils; */ public class LotteryTicketRepositoryTest { - private final LotteryTicketRepository repository = new LotteryTicketRepositoryMock(); + private final LotteryTicketRepository repository = new LotteryTicketInMemoryRepository(); @Before public void clear() { @@ -52,7 +52,7 @@ public class LotteryTicketRepositoryTest { @Test public void testCrudOperations() { - LotteryTicketRepository repository = new LotteryTicketRepositoryMock(); + LotteryTicketRepository repository = new LotteryTicketInMemoryRepository(); assertEquals(repository.findAll().size(), 0); LotteryTicket ticket = LotteryTestUtils.createLotteryTicket(); Optional id = repository.save(ticket); diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/lottery/LotteryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/lottery/LotteryTest.java index 0c5bb5216..27e5bb6e4 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/lottery/LotteryTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/lottery/LotteryTest.java @@ -38,7 +38,7 @@ import com.iluwatar.hexagonal.administration.LotteryAdministrationImpl; import com.iluwatar.hexagonal.banking.WireTransfers; import com.iluwatar.hexagonal.banking.WireTransfersImpl; import com.iluwatar.hexagonal.database.LotteryTicketRepository; -import com.iluwatar.hexagonal.database.LotteryTicketRepositoryMock; +import com.iluwatar.hexagonal.database.LotteryTicketInMemoryRepository; import com.iluwatar.hexagonal.domain.LotteryNumbers; import com.iluwatar.hexagonal.domain.LotteryTicket; import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult; @@ -57,7 +57,7 @@ public class LotteryTest { private final LotteryAdministration admin = new LotteryAdministrationImpl(); private final LotteryService service = new LotteryServiceImpl(); - private final LotteryTicketRepository repository = new LotteryTicketRepositoryMock(); + private final LotteryTicketRepository repository = new LotteryTicketInMemoryRepository(); private final WireTransfers wireTransfers = new WireTransfersImpl(); @Before From c2f4194c07860c296666f6db1a3d4d85532f300a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 10 Apr 2016 21:49:40 +0300 Subject: [PATCH 31/35] Add alias name for the pattern --- hexagonal/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hexagonal/README.md b/hexagonal/README.md index d49b21731..defe5be57 100644 --- a/hexagonal/README.md +++ b/hexagonal/README.md @@ -9,6 +9,9 @@ tags: - Difficulty-Expert --- +## Also known as +Ports and Adapters + ## Intent Allow an application to equally be driven by users, programs, automated test or batch scripts, and to be developed and tested in isolation from its eventual run-time devices and databases. From e546d5dacc6966768617c96b703d90f5e302aa77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 10 Apr 2016 21:50:02 +0300 Subject: [PATCH 32/35] Add general description for the example code --- .../main/java/com/iluwatar/hexagonal/App.java | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java index 754f301b3..3ae55b706 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java @@ -38,7 +38,33 @@ import com.iluwatar.hexagonal.service.LotteryServiceImpl; /** * - * Example application demonstrating Hexagonal Architecture + * Hexagonal Architecture pattern decouples the application core from the + * services it uses. This allows the services to be plugged in and the + * application will run with or without the services.

+ * + * The core logic, or business logic, of an application consists of the + * algorithms that are essential to its purpose. They implement the use + * cases that are the heart of the application. When you change them, you + * change the essence of the application.

+ * + * The services are not essential. They can be replaced without changing + * the purpose of the application. Examples: database access and other + * types of storage, user interface components, e-mail and other + * communication components, hardware devices.

+ * + * This example demonstrates Hexagonal Architecture with a lottery system. + * The application core is separate from the services that drive it and + * from the services it uses.

+ * + * The primary ports for the application are {@link LotteryAdministration} + * through which the lottery round is initiated and run and + * {@link LotteryService} that allows players to submit lottery tickets for + * the draw.

+ * + * The secondary ports that application core uses are {@link WireTransfers} + * which is a banking service, {@link LotteryNotifications} that delivers + * notifications as lottery events occur and {@link LotteryTicketRepository} + * that is the storage for the lottery tickets. * */ public class App { From cfda338617ae2399a1f361f93b92be9b12c65422 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 10 Apr 2016 22:03:24 +0300 Subject: [PATCH 33/35] Fix version number --- hexagonal/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hexagonal/pom.xml b/hexagonal/pom.xml index 1630e0eee..540e6f398 100644 --- a/hexagonal/pom.xml +++ b/hexagonal/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.11.0-SNAPSHOT + 1.12.0-SNAPSHOT com.iluwatar.hexagonal hexagonal From a7dfe7681eae826660dc2175baacb0e2f06f6fb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Mon, 11 Apr 2016 23:17:27 +0300 Subject: [PATCH 34/35] Add alias names --- hexagonal/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hexagonal/README.md b/hexagonal/README.md index defe5be57..5e954e90c 100644 --- a/hexagonal/README.md +++ b/hexagonal/README.md @@ -11,6 +11,8 @@ tags: ## Also known as Ports and Adapters +Clean Architecture +Onion Architecture ## Intent Allow an application to equally be driven by users, programs, automated test or batch scripts, and to be developed and tested in isolation from its eventual run-time devices and databases. From 75ed1b1a9684eb2a2cad4ae36dcbb066069a908a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 21 May 2016 21:48:25 +0300 Subject: [PATCH 35/35] Review fixes --- hexagonal/pom.xml | 4 ---- .../administration/LotteryAdministration.java | 11 ++++++++++ .../LotteryAdministrationImpl.java | 4 ---- .../hexagonal/banking/WireTransfers.java | 11 ++++++++++ .../database/LotteryTicketRepository.java | 15 +++++++++++++ .../domain/LotteryTicketCheckResult.java | 1 - .../hexagonal/domain/LotteryTicketId.java | 3 +++ .../notifications/LotteryNotifications.java | 21 ++++++++++++++++++- .../hexagonal/service/LotteryService.java | 6 ++++++ .../hexagonal/service/LotteryServiceImpl.java | 5 ++++- 10 files changed, 70 insertions(+), 11 deletions(-) diff --git a/hexagonal/pom.xml b/hexagonal/pom.xml index 540e6f398..54f81cf14 100644 --- a/hexagonal/pom.xml +++ b/hexagonal/pom.xml @@ -32,11 +32,7 @@ java-design-patterns 1.12.0-SNAPSHOT - com.iluwatar.hexagonal hexagonal - 1.0-SNAPSHOT - hexagonal - http://maven.apache.org junit diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministration.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministration.java index 70b625aa2..bc625b230 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministration.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministration.java @@ -35,8 +35,19 @@ import com.iluwatar.hexagonal.domain.LotteryTicketId; */ public interface LotteryAdministration { + /** + * Get all the lottery tickets submitted for lottery + */ Map getAllSubmittedTickets(); + + /** + * Draw lottery numbers + */ LotteryNumbers performLottery(); + + /** + * Begin new lottery round + */ void resetLottery(); } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java index 5e457439b..a452600aa 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java @@ -47,13 +47,9 @@ import com.iluwatar.hexagonal.service.LotteryServiceImpl; public class LotteryAdministrationImpl implements LotteryAdministration { private final LotteryTicketRepository repository; - private final LotteryService service = new LotteryServiceImpl(); - private final LotteryNotifications notifications = new LotteryNotificationsImpl(); - private final WireTransfers bank = new WireTransfersImpl(); - public LotteryAdministrationImpl() { repository = new LotteryTicketInMemoryRepository(); } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/WireTransfers.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/WireTransfers.java index 61a6567ba..7d21e7bf3 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/WireTransfers.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/WireTransfers.java @@ -29,8 +29,19 @@ package com.iluwatar.hexagonal.banking; */ public interface WireTransfers { + /** + * Set amount of funds for bank account + */ void setFunds(String bankAccount, int amount); + + /** + * Get amount of funds for bank account + */ int getFunds(String bankAccount); + + /** + * Transfer funds from one bank account to another + */ boolean transferFunds(int amount, String sourceBackAccount, String destinationBankAccount); } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepository.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepository.java index a531440b4..4c6522838 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepository.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepository.java @@ -35,9 +35,24 @@ import com.iluwatar.hexagonal.domain.LotteryTicketId; */ public interface LotteryTicketRepository { + /** + * Find lottery ticket by id + */ Optional findById(LotteryTicketId id); + + /** + * Save lottery ticket + */ Optional save(LotteryTicket ticket); + + /** + * Get all lottery tickets + */ Map findAll(); + + /** + * Delete all lottery tickets + */ void deleteAll(); } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResult.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResult.java index 534dc685f..ded65d270 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResult.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResult.java @@ -32,7 +32,6 @@ public class LotteryTicketCheckResult { public enum CheckResult { WIN_PRIZE, NO_PRIZE, TICKET_NOT_SUBMITTED }; private final CheckResult checkResult; - private final int prizeAmount; /** diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java index 358bdd05a..710091222 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java @@ -24,6 +24,9 @@ package com.iluwatar.hexagonal.domain; import java.util.UUID; +/** + * Lottery ticked id + */ public class LotteryTicketId { private final UUID id; diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotifications.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotifications.java index cc9f9d6e5..d7a0cc870 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotifications.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotifications.java @@ -30,11 +30,30 @@ import com.iluwatar.hexagonal.domain.PlayerDetails; * */ public interface LotteryNotifications { - + + /** + * Notify lottery ticket was submitted + */ void notifyTicketSubmitted(PlayerDetails details); + + /** + * Notify there was an error submitting lottery ticket + */ void notifyTicketSubmitError(PlayerDetails details); + + /** + * Notify lottery ticket did not win + */ void notifyNoWin(PlayerDetails details); + + /** + * Notify that prize has been paid + */ void notifyPrize(PlayerDetails details, int prizeAmount); + + /** + * Notify that there was an error paying the prize + */ void notifyPrizeError(PlayerDetails details, int prizeAmount); } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryService.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryService.java index 65270410a..0056e794b 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryService.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryService.java @@ -36,7 +36,13 @@ import com.iluwatar.hexagonal.domain.LotteryTicketId; */ public interface LotteryService { + /** + * Submit lottery ticket to participate in the lottery + */ Optional submitTicket(LotteryTicket ticket); + /** + * Check if lottery ticket has won + */ LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers); } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java index d446e93be..58df1c7c8 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java @@ -49,7 +49,10 @@ public class LotteryServiceImpl implements LotteryService { private final WireTransfers bank = new WireTransfersImpl(); private final LotteryNotifications notifications = new LotteryNotificationsImpl(); - + + /** + * Constructor + */ public LotteryServiceImpl() { repository = new LotteryTicketInMemoryRepository(); }