From 5429d3b0f6eff2588b10b32ecdbe12a275e141e0 Mon Sep 17 00:00:00 2001 From: Rafael Machado Date: Thu, 8 Oct 2020 15:25:04 -0300 Subject: [PATCH 01/27] Add partial sort algorithm --- Sources/Algorithms/PartialSort.swift | 89 ++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 Sources/Algorithms/PartialSort.swift diff --git a/Sources/Algorithms/PartialSort.swift b/Sources/Algorithms/PartialSort.swift new file mode 100644 index 00000000..71048499 --- /dev/null +++ b/Sources/Algorithms/PartialSort.swift @@ -0,0 +1,89 @@ +import Foundation + +private final class Heap { + + typealias Comparator = (T,T) -> Bool + + private var elements: [T] + private let priority: Comparator + + init(elements: S, priority: @escaping Comparator) where S.Element == T { + self.priority = priority + self.elements = Array(elements) + if elements.isEmpty == false { + for i in stride(from: (count / 2) - 1, to: -1, by: -1) { + siftDown(i) + } + } + } + + private func leftChildIndex(of index: Int) -> Int { + return (2 * index) + 1 + } + + private func rightChild(of index: Int) -> Int { + return (2 * index) + 2 + } + + private func parentIndex(of index: Int) -> Int { + return (index - 1) / 2 + } + + private func isHigherPriority(_ a: Int, _ b: Int) -> Bool { + return priority(elements[a], elements[b]) + } + + private func highestPriorityIndex(of index: Int) -> Int { + let left = highestPriorityIndex(of: index, and: leftChildIndex(of: index)) + let right = highestPriorityIndex(of: index, and: rightChild(of: index)) + return highestPriorityIndex(of: left, and: right) + } + + private func highestPriorityIndex(of parent: Int, and child: Int) -> Int { + guard child < elements.count else { + return parent + } + guard isHigherPriority(child, parent) else { + return parent + } + return child + } + + func dequeue() -> T? { + guard elements.count > 0 else { + return nil + } + elements.swapAt(0, elements.count - 1) + let element = elements.popLast() + siftDown(0) + return element + } + + private func siftDown(_ i: Int) { + let indexToSwap = highestPriorityIndex(of: i) + guard indexToSwap != i else { + return + } + elements.swapAt(indexToSwap, i) + siftDown(indexToSwap) + } +} + +extension Collection { + func partiallySorted(_ count: Int, by: @escaping (Element, Element) -> Bool) -> [Element] { + assert(count >= 0 && count < self.count, "Are you crazy?") + let heap = Heap(elements: self, priority: by) + return [Element](unsafeUninitializedCapacity: count) { buffer, initializedCount in + for i in 0.. [Element] { + return partiallySorted(count, by: <) + } +} From 43621972123677207c92649d638178d20e7e8556 Mon Sep 17 00:00:00 2001 From: Bruno Rocha Date: Fri, 9 Oct 2020 13:07:44 +0200 Subject: [PATCH 02/27] Add in place partial sorting --- Sources/Algorithms/PartialSort.swift | 246 ++++++++++++++++++++------- 1 file changed, 182 insertions(+), 64 deletions(-) diff --git a/Sources/Algorithms/PartialSort.swift b/Sources/Algorithms/PartialSort.swift index 71048499..22ad55e6 100644 --- a/Sources/Algorithms/PartialSort.swift +++ b/Sources/Algorithms/PartialSort.swift @@ -1,89 +1,207 @@ -import Foundation +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Algorithms open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// +//===----------------------------------------------------------------------===// -private final class Heap { - - typealias Comparator = (T,T) -> Bool - - private var elements: [T] - private let priority: Comparator - - init(elements: S, priority: @escaping Comparator) where S.Element == T { - self.priority = priority - self.elements = Array(elements) - if elements.isEmpty == false { - for i in stride(from: (count / 2) - 1, to: -1, by: -1) { - siftDown(i) - } - } +extension Sequence { + /// Returns the elements of the sequence such that the 0...k range contains + /// the first k sorted elements in this sequence, using the given predicate + /// as the comparison between elements. + /// + /// This example partially sorts an array of integers to retrieve its three + /// smallest values: + /// + /// let numbers = [7,1,6,2,8,3,9] + /// let almostSorted = numbers.partiallySorted(3, <) + /// // [1, 2, 3, 9, 7, 6, 8] + /// let smallestThree = almostSorted.prefix(3) + /// // [1, 2, 3] + /// + /// The order of equal elements is not guaranteed to be preserved, and the + /// order of the remaining elements is unspecified. + /// + /// If you need to sort a sequence but only need access to a prefix of its elements, + /// using this method can give you a performance boost over sorting the entire + /// sequence. + /// + /// - Parameter count: The k number of elements to partially sort. + /// - Parameter areInIncreasingOrder: A predicate that returns true if its first argument should + /// be ordered before its second argument; otherwise, false. + /// + /// - Complexity: O(k log n) + public func partiallySorted(_ count: Int, by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> [Element] { + var result = ContiguousArray(self) + try result.partiallySort(count, by: areInIncreasingOrder) + return Array(result) } +} - private func leftChildIndex(of index: Int) -> Int { - return (2 * index) + 1 +extension Sequence where Element: Comparable { + /// Returns the elements of the sequence such that the 0...k range contains + /// the first k smallest elements in this sequence. + /// + /// This example partially sorts an array of integers to retrieve its three + /// smallest values: + /// + /// let numbers = [7,1,6,2,8,3,9] + /// let almostSorted = numbers.partiallySorted(3) + /// // [1, 2, 3, 9, 7, 6, 8] + /// let smallestThree = almostSorted.prefix(3) + /// // [1, 2, 3] + /// + /// The order of equal elements is not guaranteed to be preserved, and the + /// order of the remaining elements is unspecified. + /// + /// If you need to sort a sequence but only need access to a prefix of its elements, + /// using this method can give you a performance boost over sorting the entire + /// sequence. + /// + /// - Parameter count: The k number of elements to partially sort, in ascending order. + /// + /// - Complexity: O(k log n) + public func partiallySorted(_ count: Int) -> [Element] { + return partiallySorted(count, by: <) } +} - private func rightChild(of index: Int) -> Int { - return (2 * index) + 2 +extension MutableCollection where Self: RandomAccessCollection, Index == Int { + /// Rearranges this collection such that the 0...k range contains the first + /// k sorted elements in this collection, using the given predicate as the + /// comparison between elements. + /// + /// This example partially sorts an array of integers to retrieve its three + /// smallest values: + /// + /// var numbers = [7,1,6,2,8,3,9] + /// numbers.partiallySort(3, <) + /// // [1, 2, 3, 9, 7, 6, 8] + /// let smallestThree = numbers.prefix(3) + /// // [1, 2, 3] + /// + /// The order of equal elements is not guaranteed to be preserved, and the + /// order of the remaining elements is unspecified. + /// + /// If you need to sort a collection but only need access to a prefix of its + /// elements, using this method can give you a performance boost over sorting + /// the entire collection. + /// + /// - Parameter count: The k number of elements to partially sort. + /// - Parameter areInIncreasingOrder: A predicate that returns true if its first argument should + /// be ordered before its second argument; otherwise, false. + /// + /// - Complexity: O(k log n) + public mutating func partiallySort(_ count: Int, by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows { + try __partiallySort(count, by: areInIncreasingOrder) } +} - private func parentIndex(of index: Int) -> Int { - return (index - 1) / 2 +extension MutableCollection where Self: RandomAccessCollection, Element: Comparable, Index == Int { + /// Rearranges this collection such that the 0...k range contains the first + /// k smallest elements in this collection. + /// + /// This example partially sorts an array of integers to retrieve its three + /// smallest values: + /// + /// var numbers = [7,1,6,2,8,3,9] + /// numbers.partiallySort(3) + /// // [1, 2, 3, 9, 7, 6, 8] + /// let smallestThree = numbers.prefix(3) + /// // [1, 2, 3] + /// + /// The order of equal elements is not guaranteed to be preserved, and the + /// order of the remaining elements is unspecified. + /// + /// If you need to sort a collection but only need access to a prefix of its + /// elements, using this method can give you a performance boost over sorting + /// the entire collection. + /// + /// - Parameter count: The k number of elements to partially sort, in ascending order. + /// + /// - Complexity: O(k log n) + public mutating func partiallySort(_ count: Int) { + partiallySort(count, by: <) } +} - private func isHigherPriority(_ a: Int, _ b: Int) -> Bool { - return priority(elements[a], elements[b]) - } +//===----------------------------------------------------------------------===// +// __partiallySort(_:by:) +//===----------------------------------------------------------------------===// - private func highestPriorityIndex(of index: Int) -> Int { - let left = highestPriorityIndex(of: index, and: leftChildIndex(of: index)) - let right = highestPriorityIndex(of: index, and: rightChild(of: index)) - return highestPriorityIndex(of: left, and: right) - } +extension MutableCollection where Self: RandomAccessCollection, Index == Int { + typealias Priority = (Element, Element) throws -> Bool - private func highestPriorityIndex(of parent: Int, and child: Int) -> Int { - guard child < elements.count else { - return parent + /// Partially sorts this array by using an in place heapsort that stops after we find the desired k amount + /// of elements. The heap is stored and processed in reverse order so that the array doesn't have to be flipped + /// once the final result is found. + /// + /// Complexity: O(k log n) + mutating func __partiallySort(_ k: Int, by areInIncreasingOrder: Priority) rethrows { + assert(k >= 0, "Attempted to partially sort with a negative amount of elements!") + assert(k <= count, + "Attempted to partially sort an amount of elements larger than this Sequence's size!") + guard isEmpty == false else { + return } - guard isHigherPriority(child, parent) else { - return parent + var heapEndIndex = 0 + for i in ((count / 2) + 1).. T? { - guard elements.count > 0 else { - return nil + var iterator = (0.. Bool) -> [Element] { - assert(count >= 0 && count < self.count, "Are you crazy?") - let heap = Heap(elements: self, priority: by) - return [Element](unsafeUninitializedCapacity: count) { buffer, initializedCount in - for i in 0.. Int { + let reverseHeapTrueIndex = self.count - 1 - index + let leftChild = index - (leftChildIndex(of: reverseHeapTrueIndex) - reverseHeapTrueIndex) + let rightChild = index - (rightChildIndex(of: reverseHeapTrueIndex) - reverseHeapTrueIndex) + let left = try highestPriorityIndex(of: index, and: leftChild, by: priority, heapEndIndex: heapEndIndex) + let right = try highestPriorityIndex(of: index, and: rightChild, by: priority, heapEndIndex: heapEndIndex) + return try highestPriorityIndex(of: left, and: right, by: priority, heapEndIndex: heapEndIndex) } -} -extension Collection where Element: Comparable { - func partiallySorted(_ count: Int) -> [Element] { - return partiallySorted(count, by: <) + private func leftChildIndex(of index: Int) -> Int { + return (2 * index) + 1 + } + + private func rightChildIndex(of index: Int) -> Int { + return (2 * index) + 2 + } + + private func highestPriorityIndex(of parent: Int, and child: Int, by priority: Priority, heapEndIndex: Int) rethrows -> Int { + guard child >= heapEndIndex else { + return parent + } + guard try priority(self[child], self[parent]) else { + return parent + } + return child } } From f299df1a8924ef9d4c5748325b09b6d86125fc28 Mon Sep 17 00:00:00 2001 From: Bruno Rocha Date: Fri, 9 Oct 2020 14:44:42 +0200 Subject: [PATCH 03/27] Guide docs --- Guides/PartialSort.md | 59 ++++++++++++++++++++++++++++ README.md | 4 ++ Sources/Algorithms/PartialSort.swift | 6 +-- 3 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 Guides/PartialSort.md diff --git a/Guides/PartialSort.md b/Guides/PartialSort.md new file mode 100644 index 00000000..39df4ff4 --- /dev/null +++ b/Guides/PartialSort.md @@ -0,0 +1,59 @@ +# Partial Sort + +[[Source](https://github.com/apple/swift-algorithms/blob/main/Sources/Algorithms/PartialSort.swift) | + [Tests](https://github.com/apple/swift-algorithms/blob/main/Tests/SwiftAlgorithmsTests/PartialSortTests.swift)] + +Returns a collection such that the `0...k` range contains the first k sorted elements of a sequence. +The order of equal elements is not guaranteed to be preserved, and the order of the remaining elements is unspecified. + +If you need to sort a sequence but only need access to a prefix of its elements, +using this method can give you a performance boost over sorting the entire sequence. + +```swift +let numbers = [7,1,6,2,8,3,9] +let almostSorted = numbers.partiallySorted(3, <) +// [1, 2, 3, 9, 7, 6, 8] +``` + +## Detailed Design + +This adds the in place `MutableCollection` method shown below: + +```swift +extension Sequence { + func partiallySort(_ count: Int, by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows +} +``` + +Additionally, versions of this method that return a new array and abstractions for `Comparable` types are also provided: + +```swift +extension MutableCollection where Self: RandomAccessCollection, Element: Comparable { + public mutating func partiallySort(_ count: Int) +} + +extension Sequence { + public func partiallySorted(_ count: Int, by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> [Element] +} + +extension Sequence where Element: Comparable { + public func partiallySorted(_ count: Int) -> [Element] +} +``` + +### Complexity + +Partially sorting is a O(_k log n_) operation, where _k_ is the number of elements to sort +and _n_ is the length of the sequence. + +`partiallySort(_:by:)` is a slight generalization of a priority queue. It's implemented +as an in line heapsort that stops after _k_ runs. + +### Comparison with other languages + +**C++:** The `` library defines a `partial_sort` function with similar +semantics to this one. + +**Python:** Defines a `heapq` priority queue that can be used to manually +achieve the same result. + diff --git a/README.md b/README.md index 6bc15bc8..99ace059 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,10 @@ Read more about the package, and the intent behind it, in the [announcement on s - [`randomStableSample(count:)`, `randomStableSample(count:using:)`](https://github.com/apple/swift-algorithms/blob/main/Guides/RandomSampling.md): Randomly selects a specific number of elements from a collection, preserving their original relative order. - [`uniqued()`, `uniqued(on:)`](https://github.com/apple/swift-algorithms/blob/main/Guides/Unique.md): The unique elements of a collection, preserving their order. +#### Partial sorting + +- [`partiallySorted(_:by:)`](https://github.com/apple/swift-algorithms/blob/main/Guides/PartialSort.md): Sorts a sequence only up to a specific index, leaving the remaining elements unsorted. + #### Other useful operations - [`chunked(by:)`, `chunked(on:)`](https://github.com/apple/swift-algorithms/blob/main/Guides/Chunked.md): Eager and lazy operations that break a collection into chunks based on either a binary predicate or when the result of a projection changes. diff --git a/Sources/Algorithms/PartialSort.swift b/Sources/Algorithms/PartialSort.swift index 22ad55e6..b0f27b96 100644 --- a/Sources/Algorithms/PartialSort.swift +++ b/Sources/Algorithms/PartialSort.swift @@ -136,8 +136,8 @@ extension MutableCollection where Self: RandomAccessCollection, Element: Compara extension MutableCollection where Self: RandomAccessCollection, Index == Int { typealias Priority = (Element, Element) throws -> Bool - /// Partially sorts this array by using an in place heapsort that stops after we find the desired k amount - /// of elements. The heap is stored and processed in reverse order so that the array doesn't have to be flipped + /// Partially sorts this collection by using an in place heapsort that stops after we find the desired k amount + /// of elements. The heap is stored and processed in reverse order so that the collection doesn't have to be flipped /// once the final result is found. /// /// Complexity: O(k log n) @@ -164,7 +164,7 @@ extension MutableCollection where Self: RandomAccessCollection, Index == Int { } /// Sifts down an element from this heap. - /// The heap is stored in reverse order, so sifting down will actually move the element up in the heap array. + /// The heap is stored in reverse order, so sifting down will actually move the element up in the heap. /// /// - Parameter i: The element index to sift down /// - Parameter by: The predicate to use when determining the priority of elements in the heap From 6cd2870ef69bd413bf915acbc020ec0c8eea0fb1 Mon Sep 17 00:00:00 2001 From: Bruno Rocha Date: Fri, 9 Oct 2020 14:49:43 +0200 Subject: [PATCH 04/27] Use Indexes --- Sources/Algorithms/PartialSort.swift | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Sources/Algorithms/PartialSort.swift b/Sources/Algorithms/PartialSort.swift index b0f27b96..232e122f 100644 --- a/Sources/Algorithms/PartialSort.swift +++ b/Sources/Algorithms/PartialSort.swift @@ -70,7 +70,7 @@ extension Sequence where Element: Comparable { } } -extension MutableCollection where Self: RandomAccessCollection, Index == Int { +extension MutableCollection where Self: RandomAccessCollection { /// Rearranges this collection such that the 0...k range contains the first /// k sorted elements in this collection, using the given predicate as the /// comparison between elements. @@ -101,7 +101,7 @@ extension MutableCollection where Self: RandomAccessCollection, Index == Int { } } -extension MutableCollection where Self: RandomAccessCollection, Element: Comparable, Index == Int { +extension MutableCollection where Self: RandomAccessCollection, Element: Comparable { /// Rearranges this collection such that the 0...k range contains the first /// k smallest elements in this collection. /// @@ -133,7 +133,7 @@ extension MutableCollection where Self: RandomAccessCollection, Element: Compara // __partiallySort(_:by:) //===----------------------------------------------------------------------===// -extension MutableCollection where Self: RandomAccessCollection, Index == Int { +extension MutableCollection where Self: RandomAccessCollection { typealias Priority = (Element, Element) throws -> Bool /// Partially sorts this collection by using an in place heapsort that stops after we find the desired k amount @@ -154,11 +154,11 @@ extension MutableCollection where Self: RandomAccessCollection, Index == Int { } var iterator = (0..= heapEndIndex else { return parent } - guard try priority(self[child], self[parent]) else { + let childElement = self[index(startIndex, offsetBy: child)] + let parentElement = self[index(startIndex, offsetBy: parent)] + guard try priority(childElement, parentElement) else { return parent } return child From 88216e1aebb8b5d670a2d51fca03e9c82f280bcb Mon Sep 17 00:00:00 2001 From: Rafael Machado Date: Fri, 9 Oct 2020 10:29:34 -0300 Subject: [PATCH 05/27] Add partial sort tests --- .../PartialSortTests.swift | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 Tests/SwiftAlgorithmsTests/PartialSortTests.swift diff --git a/Tests/SwiftAlgorithmsTests/PartialSortTests.swift b/Tests/SwiftAlgorithmsTests/PartialSortTests.swift new file mode 100644 index 00000000..844d0962 --- /dev/null +++ b/Tests/SwiftAlgorithmsTests/PartialSortTests.swift @@ -0,0 +1,106 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Algorithms open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// +//===----------------------------------------------------------------------===// + +import XCTest +import Algorithms + +final class PartialSortTests: XCTestCase { + func testEmpty() { + let array = [Int]() + + XCTAssertEqual(array.partiallySorted(0), []) + } + + func testPartialSortComparable() { + let array: [Int] = [20, 1, 4, 70, 100, 2, 3, 7, 90] + + XCTAssertEqual(array.partiallySorted(0), array) + + XCTAssertEqual( + array.partiallySorted(1), + [1, 90, 4, 70, 100, 7, 3, 2, 20] + ) + + XCTAssertEqual( + array.partiallySorted(5), + [1, 2, 3, 4, 7, 90, 70, 20, 100] + ) + + XCTAssertEqual( + array.partiallySorted(9), + [1, 2, 3, 4, 7, 20, 70, 90, 100] + ) + } + + func testPartialSortComparableWithCustomPriority() { + let array: [Int] = [20, 1, 4, 70, 100, 2, 3, 7, 90] + + XCTAssertEqual(array.partiallySorted(0, by: >), array) + XCTAssertEqual( + array.partiallySorted(1, by: >), + [100, 1, 4, 3, 7, 20, 70, 90, 2] + ) + + XCTAssertEqual( + array.partiallySorted(5, by: >), + [100, 90, 70, 20, 7, 2, 4, 3, 1] + ) + + XCTAssertEqual( + array.partiallySorted(9, by: >), + [100, 90, 70, 20, 7, 4, 3, 2, 1] + ) + } + + func testPartialSortInPlaceComparable() { + let originalArray: [Int] = [20, 1, 4, 70, 100, 2, 3, 7, 90] + var array = originalArray + + array.partiallySort(0) + XCTAssertEqual(array, originalArray) + + array = originalArray + + array.partiallySort(1) + XCTAssertEqual( + array, + [1, 90, 4, 70, 100, 7, 3, 2, 20] + ) + + array = originalArray + + array.partiallySort(5) + XCTAssertEqual( + array, + [1, 2, 3, 4, 7, 90, 70, 20, 100] + ) + + array = originalArray + + array.partiallySort(9) + XCTAssertEqual( + array, + [1, 2, 3, 4, 7, 20, 70, 90, 100] + ) + } + + func testPartialSortDescendingArray() { + let array: [Int] = [100, 90, 70, 20, 7, 4, 3, 2, 1] + + XCTAssertEqual(array.partiallySorted(9, by: >), array) + } + + func testPartialSortAscendingArray() { + let array: [Int] = [1, 2, 3, 4, 7, 20, 70, 90, 100] + + XCTAssertEqual(array.partiallySorted(9, by: <), array) + } +} From afe7111d9e5c1c74af19dc1c35aa414df84a8f44 Mon Sep 17 00:00:00 2001 From: Rafael Machado Date: Fri, 9 Oct 2020 10:29:51 -0300 Subject: [PATCH 06/27] Indent up to 80 columns --- Sources/Algorithms/PartialSort.swift | 419 ++++++++++++++++----------- 1 file changed, 246 insertions(+), 173 deletions(-) diff --git a/Sources/Algorithms/PartialSort.swift b/Sources/Algorithms/PartialSort.swift index 232e122f..d95a47e1 100644 --- a/Sources/Algorithms/PartialSort.swift +++ b/Sources/Algorithms/PartialSort.swift @@ -10,123 +10,134 @@ //===----------------------------------------------------------------------===// extension Sequence { - /// Returns the elements of the sequence such that the 0...k range contains - /// the first k sorted elements in this sequence, using the given predicate - /// as the comparison between elements. - /// - /// This example partially sorts an array of integers to retrieve its three - /// smallest values: - /// - /// let numbers = [7,1,6,2,8,3,9] - /// let almostSorted = numbers.partiallySorted(3, <) - /// // [1, 2, 3, 9, 7, 6, 8] - /// let smallestThree = almostSorted.prefix(3) - /// // [1, 2, 3] - /// - /// The order of equal elements is not guaranteed to be preserved, and the - /// order of the remaining elements is unspecified. - /// - /// If you need to sort a sequence but only need access to a prefix of its elements, - /// using this method can give you a performance boost over sorting the entire - /// sequence. - /// - /// - Parameter count: The k number of elements to partially sort. - /// - Parameter areInIncreasingOrder: A predicate that returns true if its first argument should - /// be ordered before its second argument; otherwise, false. - /// - /// - Complexity: O(k log n) - public func partiallySorted(_ count: Int, by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> [Element] { - var result = ContiguousArray(self) - try result.partiallySort(count, by: areInIncreasingOrder) - return Array(result) - } + /// Returns the elements of the sequence such that the 0...k range contains + /// the first k sorted elements in this sequence, using the given predicate + /// as the comparison between elements. + /// + /// This example partially sorts an array of integers to retrieve its three + /// smallest values: + /// + /// let numbers = [7,1,6,2,8,3,9] + /// let almostSorted = numbers.partiallySorted(3, <) + /// // [1, 2, 3, 9, 7, 6, 8] + /// let smallestThree = almostSorted.prefix(3) + /// // [1, 2, 3] + /// + /// The order of equal elements is not guaranteed to be preserved, and the + /// order of the remaining elements is unspecified. + /// + /// If you need to sort a sequence but only need access to a prefix of its + /// elements, using this method can give you a performance boost over sorting + /// the entire sequence. + /// + /// - Parameter count: The k number of elements to partially sort. + /// - Parameter areInIncreasingOrder: A predicate that returns true if its + /// first argument should be ordered before its second argument; + /// otherwise, false. + /// + /// - Complexity: O(k log n) + public func partiallySorted( + _ count: Int, + by areInIncreasingOrder: (Element, Element) throws -> Bool + ) rethrows -> [Element] { + var result = ContiguousArray(self) + try result.partiallySort(count, by: areInIncreasingOrder) + return Array(result) + } } extension Sequence where Element: Comparable { - /// Returns the elements of the sequence such that the 0...k range contains - /// the first k smallest elements in this sequence. - /// - /// This example partially sorts an array of integers to retrieve its three - /// smallest values: - /// - /// let numbers = [7,1,6,2,8,3,9] - /// let almostSorted = numbers.partiallySorted(3) - /// // [1, 2, 3, 9, 7, 6, 8] - /// let smallestThree = almostSorted.prefix(3) - /// // [1, 2, 3] - /// - /// The order of equal elements is not guaranteed to be preserved, and the - /// order of the remaining elements is unspecified. - /// - /// If you need to sort a sequence but only need access to a prefix of its elements, - /// using this method can give you a performance boost over sorting the entire - /// sequence. - /// - /// - Parameter count: The k number of elements to partially sort, in ascending order. - /// - /// - Complexity: O(k log n) - public func partiallySorted(_ count: Int) -> [Element] { - return partiallySorted(count, by: <) - } + /// Returns the elements of the sequence such that the 0...k range contains + /// the first k smallest elements in this sequence. + /// + /// This example partially sorts an array of integers to retrieve its three + /// smallest values: + /// + /// let numbers = [7,1,6,2,8,3,9] + /// let almostSorted = numbers.partiallySorted(3) + /// // [1, 2, 3, 9, 7, 6, 8] + /// let smallestThree = almostSorted.prefix(3) + /// // [1, 2, 3] + /// + /// The order of equal elements is not guaranteed to be preserved, and the + /// order of the remaining elements is unspecified. + /// + /// If you need to sort a sequence but only need access to a prefix of + /// its elements, using this method can give you a performance boost over + /// sorting the entire sequence. + /// + /// - Parameter count: The k number of elements to partially sort + /// in ascending order. + /// + /// - Complexity: O(k log n) + public func partiallySorted(_ count: Int) -> [Element] { + return partiallySorted(count, by: <) + } } extension MutableCollection where Self: RandomAccessCollection { - /// Rearranges this collection such that the 0...k range contains the first - /// k sorted elements in this collection, using the given predicate as the - /// comparison between elements. - /// - /// This example partially sorts an array of integers to retrieve its three - /// smallest values: - /// - /// var numbers = [7,1,6,2,8,3,9] - /// numbers.partiallySort(3, <) - /// // [1, 2, 3, 9, 7, 6, 8] - /// let smallestThree = numbers.prefix(3) - /// // [1, 2, 3] - /// - /// The order of equal elements is not guaranteed to be preserved, and the - /// order of the remaining elements is unspecified. - /// - /// If you need to sort a collection but only need access to a prefix of its - /// elements, using this method can give you a performance boost over sorting - /// the entire collection. - /// - /// - Parameter count: The k number of elements to partially sort. - /// - Parameter areInIncreasingOrder: A predicate that returns true if its first argument should - /// be ordered before its second argument; otherwise, false. - /// - /// - Complexity: O(k log n) - public mutating func partiallySort(_ count: Int, by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows { - try __partiallySort(count, by: areInIncreasingOrder) - } + /// Rearranges this collection such that the 0...k range contains the first + /// k sorted elements in this collection, using the given predicate as the + /// comparison between elements. + /// + /// This example partially sorts an array of integers to retrieve its three + /// smallest values: + /// + /// var numbers = [7,1,6,2,8,3,9] + /// numbers.partiallySort(3, <) + /// // [1, 2, 3, 9, 7, 6, 8] + /// let smallestThree = numbers.prefix(3) + /// // [1, 2, 3] + /// + /// The order of equal elements is not guaranteed to be preserved, and the + /// order of the remaining elements is unspecified. + /// + /// If you need to sort a collection but only need access to a prefix of its + /// elements, using this method can give you a performance boost over sorting + /// the entire collection. + /// + /// - Parameter count: The k number of elements to partially sort. + /// - Parameter areInIncreasingOrder: A predicate that returns true if its + /// first argument should be ordered before its second argument; + /// otherwise, false. + /// + /// - Complexity: O(k log n) + public mutating func partiallySort( + _ count: Int, + by areInIncreasingOrder: (Element, Element) throws -> Bool + ) rethrows { + try __partiallySort(count, by: areInIncreasingOrder) + } } -extension MutableCollection where Self: RandomAccessCollection, Element: Comparable { - /// Rearranges this collection such that the 0...k range contains the first - /// k smallest elements in this collection. - /// - /// This example partially sorts an array of integers to retrieve its three - /// smallest values: - /// - /// var numbers = [7,1,6,2,8,3,9] - /// numbers.partiallySort(3) - /// // [1, 2, 3, 9, 7, 6, 8] - /// let smallestThree = numbers.prefix(3) - /// // [1, 2, 3] - /// - /// The order of equal elements is not guaranteed to be preserved, and the - /// order of the remaining elements is unspecified. - /// - /// If you need to sort a collection but only need access to a prefix of its - /// elements, using this method can give you a performance boost over sorting - /// the entire collection. - /// - /// - Parameter count: The k number of elements to partially sort, in ascending order. - /// - /// - Complexity: O(k log n) - public mutating func partiallySort(_ count: Int) { - partiallySort(count, by: <) - } +extension MutableCollection +where Self: RandomAccessCollection, Element: Comparable { + /// Rearranges this collection such that the 0...k range contains the first + /// k smallest elements in this collection. + /// + /// This example partially sorts an array of integers to retrieve its three + /// smallest values: + /// + /// var numbers = [7,1,6,2,8,3,9] + /// numbers.partiallySort(3) + /// // [1, 2, 3, 9, 7, 6, 8] + /// let smallestThree = numbers.prefix(3) + /// // [1, 2, 3] + /// + /// The order of equal elements is not guaranteed to be preserved, and the + /// order of the remaining elements is unspecified. + /// + /// If you need to sort a collection but only need access to a prefix of its + /// elements, using this method can give you a performance boost over sorting + /// the entire collection. + /// + /// - Parameter count: The k number of elements to partially sort + /// in ascending order. + /// + /// - Complexity: O(k log n) + public mutating func partiallySort(_ count: Int) { + partiallySort(count, by: <) + } } //===----------------------------------------------------------------------===// @@ -134,76 +145,138 @@ extension MutableCollection where Self: RandomAccessCollection, Element: Compara //===----------------------------------------------------------------------===// extension MutableCollection where Self: RandomAccessCollection { - typealias Priority = (Element, Element) throws -> Bool - - /// Partially sorts this collection by using an in place heapsort that stops after we find the desired k amount - /// of elements. The heap is stored and processed in reverse order so that the collection doesn't have to be flipped - /// once the final result is found. - /// - /// Complexity: O(k log n) - mutating func __partiallySort(_ k: Int, by areInIncreasingOrder: Priority) rethrows { - assert(k >= 0, "Attempted to partially sort with a negative amount of elements!") - assert(k <= count, - "Attempted to partially sort an amount of elements larger than this Sequence's size!") - guard isEmpty == false else { - return - } - var heapEndIndex = 0 - for i in ((count / 2) + 1).. Bool - /// Sifts down an element from this heap. - /// The heap is stored in reverse order, so sifting down will actually move the element up in the heap. - /// - /// - Parameter i: The element index to sift down - /// - Parameter by: The predicate to use when determining the priority of elements in the heap - /// - Parameter heapEndIndex: The index, in reverse order, where the heap ends. - private mutating func siftDown(_ i: Int, by priority: Priority, heapEndIndex: Int) rethrows { - let indexToSwap = try highestPriorityIndex(of: i, by: priority, heapEndIndex: heapEndIndex) - guard indexToSwap != i else { - return - } - swapAt(index(startIndex, offsetBy: i), index(startIndex, offsetBy: indexToSwap)) - try siftDown(indexToSwap, by: priority, heapEndIndex: heapEndIndex) - } + /// Partially sorts this collection by using an in place heapsort that stops + /// after we find the desired k amount + /// of elements. The heap is stored and processed in reverse order so that + /// the collection doesn't have to be flipped once the final result is found. + /// + /// Complexity: O(k log n) + mutating func __partiallySort( + _ k: Int, + by areInIncreasingOrder: Priority + ) rethrows { + assert(k >= 0, """ + Cannot partially sort with a negative amount of elements! + """ + ) - private func highestPriorityIndex(of index: Int, by priority: Priority, heapEndIndex: Int) rethrows -> Int { - let reverseHeapTrueIndex = self.count - 1 - index - let leftChild = index - (leftChildIndex(of: reverseHeapTrueIndex) - reverseHeapTrueIndex) - let rightChild = index - (rightChildIndex(of: reverseHeapTrueIndex) - reverseHeapTrueIndex) - let left = try highestPriorityIndex(of: index, and: leftChild, by: priority, heapEndIndex: heapEndIndex) - let right = try highestPriorityIndex(of: index, and: rightChild, by: priority, heapEndIndex: heapEndIndex) - return try highestPriorityIndex(of: left, and: right, by: priority, heapEndIndex: heapEndIndex) - } + assert(k <= count, """ + Cannot partially sort more than this Sequence's size! + """ + ) - private func leftChildIndex(of index: Int) -> Int { - return (2 * index) + 1 + guard k > 0 else { + return + } + guard isEmpty == false else { + return + } + var heapEndIndex = 0 + for i in ((count / 2) + 1).. Int { - return (2 * index) + 2 + /// Sifts down an element from this heap. + /// The heap is stored in reverse order, so sifting down will actually + /// move the element up in the heap. + /// + /// - Parameter i: The element index to sift down + /// - Parameter by: The predicate to use when determining the priority + /// of elements in the heap + /// - Parameter heapEndIndex: The index in reverse order, where the heap ends. + private mutating func siftDown( + _ i: Int, + by priority: Priority, + heapEndIndex: Int + ) rethrows { + let indexToSwap = try highestPriorityIndex( + of: i, + by: priority, + heapEndIndex: heapEndIndex + ) + guard indexToSwap != i else { + return } + swapAt( + index(startIndex, offsetBy: i), + index(startIndex, offsetBy: indexToSwap) + ) + try siftDown(indexToSwap, by: priority, heapEndIndex: heapEndIndex) + } + + private func highestPriorityIndex( + of index: Int, + by priority: Priority, + heapEndIndex: Int + ) rethrows -> Int { + let reverseHeapTrueIndex = self.count - 1 - index + let leftChildDistance = + leftChildIndex(of: reverseHeapTrueIndex) - reverseHeapTrueIndex + let leftChild = index - leftChildDistance + + let rightChildDistance = + rightChildIndex(of: reverseHeapTrueIndex) - reverseHeapTrueIndex + let rightChild = index - rightChildDistance + + let left = try highestPriorityIndex( + of: index, + and: leftChild, + by: priority, + heapEndIndex: heapEndIndex + ) - private func highestPriorityIndex(of parent: Int, and child: Int, by priority: Priority, heapEndIndex: Int) rethrows -> Int { - guard child >= heapEndIndex else { - return parent - } - let childElement = self[index(startIndex, offsetBy: child)] - let parentElement = self[index(startIndex, offsetBy: parent)] - guard try priority(childElement, parentElement) else { - return parent - } - return child + let right = try highestPriorityIndex( + of: index, + and: rightChild, + by: priority, + heapEndIndex: heapEndIndex + ) + return try highestPriorityIndex( + of: left, + and: right, + by: priority, + heapEndIndex: heapEndIndex + ) + } + + private func leftChildIndex(of index: Int) -> Int { + return (2 * index) + 1 + } + + private func rightChildIndex(of index: Int) -> Int { + return (2 * index) + 2 + } + + private func highestPriorityIndex( + of parent: Int, + and child: Int, + by priority: Priority, + heapEndIndex: Int + ) rethrows -> Int { + guard child >= heapEndIndex else { + return parent + } + let childElement = self[index(startIndex, offsetBy: child)] + let parentElement = self[index(startIndex, offsetBy: parent)] + guard try priority(childElement, parentElement) else { + return parent } + return child + } } From 4652ae7f65c35a519312c7c0afde33fd7db376ee Mon Sep 17 00:00:00 2001 From: Bruno Rocha Date: Fri, 9 Oct 2020 16:08:42 +0200 Subject: [PATCH 07/27] Fix heapify stopping before it should --- Sources/Algorithms/PartialSort.swift | 2 +- .../PartialSortTests.swift | 74 ++++++++++++------- 2 files changed, 47 insertions(+), 29 deletions(-) diff --git a/Sources/Algorithms/PartialSort.swift b/Sources/Algorithms/PartialSort.swift index d95a47e1..64232f66 100644 --- a/Sources/Algorithms/PartialSort.swift +++ b/Sources/Algorithms/PartialSort.swift @@ -174,7 +174,7 @@ extension MutableCollection where Self: RandomAccessCollection { return } var heapEndIndex = 0 - for i in ((count / 2) + 1)..), array) + XCTAssertEqual( + array.partiallySorted(1, by: >), + [100, 1, 4, 3, 7, 20, 70, 90, 2] + ) XCTAssertEqual( - array.partiallySorted(1), - [1, 90, 4, 70, 100, 7, 3, 2, 20] + array.partiallySorted(5, by: >), + [100, 90, 70, 20, 7, 2, 4, 3, 1] ) XCTAssertEqual( - array.partiallySorted(5), + array.partiallySorted(9, by: >), + [100, 90, 70, 20, 7, 4, 3, 2, 1] + ) + + XCTAssertEqual([1].partiallySorted(0, by: <), [1]) + XCTAssertEqual([1].partiallySorted(0, by: >), [1]) + XCTAssertEqual([1].partiallySorted(1, by: <), [1]) + XCTAssertEqual([1].partiallySorted(1, by: >), [1]) + XCTAssertEqual([0, 1].partiallySorted(1, by: <), [0, 1]) + XCTAssertEqual([1, 0].partiallySorted(1, by: <), [0, 1]) + XCTAssertEqual([1, 0].partiallySorted(2, by: <), [0, 1]) + XCTAssertEqual([0, 1].partiallySorted(1, by: >), [1, 0]) + XCTAssertEqual([1, 0].partiallySorted(1, by: >), [1, 0]) + XCTAssertEqual([1, 0].partiallySorted(2, by: >), [1, 0]) + + XCTAssertEqual( + [1, 2, 3, 4, 7, 20, 70, 90, 100].partiallySorted(5, by: <), [1, 2, 3, 4, 7, 90, 70, 20, 100] ) XCTAssertEqual( - array.partiallySorted(9), - [1, 2, 3, 4, 7, 20, 70, 90, 100] + [1, 2, 3, 4, 7, 20, 70, 90, 100].partiallySorted(5, by: >), + [100, 90, 70, 20, 7, 2, 4, 3, 1] + ) + + XCTAssertEqual( + [1, 2, 3, 4, 7, 20, 70, 90, 100].partiallySorted(5, by: >), + [100, 90, 70, 20, 7, 2, 4, 3, 1] + ) + + XCTAssertEqual( + [1, 2, 3, 4, 7, 20, 70, 90, 100].partiallySorted(5, by: <), + [1, 2, 3, 4, 7, 90, 70, 20, 100] ) } - func testPartialSortComparableWithCustomPriority() { + func testPartialSortComparable() { let array: [Int] = [20, 1, 4, 70, 100, 2, 3, 7, 90] - XCTAssertEqual(array.partiallySorted(0, by: >), array) + XCTAssertEqual(array.partiallySorted(0), array) + XCTAssertEqual( - array.partiallySorted(1, by: >), - [100, 1, 4, 3, 7, 20, 70, 90, 2] + array.partiallySorted(1), + [1, 90, 4, 70, 100, 7, 3, 2, 20] ) XCTAssertEqual( - array.partiallySorted(5, by: >), - [100, 90, 70, 20, 7, 2, 4, 3, 1] + array.partiallySorted(5), + [1, 2, 3, 4, 7, 90, 70, 20, 100] ) XCTAssertEqual( - array.partiallySorted(9, by: >), - [100, 90, 70, 20, 7, 4, 3, 2, 1] + array.partiallySorted(9), + [1, 2, 3, 4, 7, 20, 70, 90, 100] ) } @@ -91,16 +121,4 @@ final class PartialSortTests: XCTestCase { [1, 2, 3, 4, 7, 20, 70, 90, 100] ) } - - func testPartialSortDescendingArray() { - let array: [Int] = [100, 90, 70, 20, 7, 4, 3, 2, 1] - - XCTAssertEqual(array.partiallySorted(9, by: >), array) - } - - func testPartialSortAscendingArray() { - let array: [Int] = [1, 2, 3, 4, 7, 20, 70, 90, 100] - - XCTAssertEqual(array.partiallySorted(9, by: <), array) - } } From 37d494a1942286e5550cabd1645aefd312eafe61 Mon Sep 17 00:00:00 2001 From: Bruno Rocha Date: Fri, 9 Oct 2020 19:59:40 +0200 Subject: [PATCH 08/27] Update PartialSort.md --- Guides/PartialSort.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Guides/PartialSort.md b/Guides/PartialSort.md index 39df4ff4..fcd02ae0 100644 --- a/Guides/PartialSort.md +++ b/Guides/PartialSort.md @@ -47,7 +47,7 @@ Partially sorting is a O(_k log n_) operation, where _k_ is the number of elemen and _n_ is the length of the sequence. `partiallySort(_:by:)` is a slight generalization of a priority queue. It's implemented -as an in line heapsort that stops after _k_ runs. +as an in-place heapsort that stops after _k_ runs. ### Comparison with other languages From 83d5f1e8114f797624c3e4b1292a46abac246062 Mon Sep 17 00:00:00 2001 From: Bruno Rocha Date: Fri, 9 Oct 2020 20:00:04 +0200 Subject: [PATCH 09/27] Update PartialSort.md --- Guides/PartialSort.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Guides/PartialSort.md b/Guides/PartialSort.md index fcd02ae0..75974c78 100644 --- a/Guides/PartialSort.md +++ b/Guides/PartialSort.md @@ -17,7 +17,7 @@ let almostSorted = numbers.partiallySorted(3, <) ## Detailed Design -This adds the in place `MutableCollection` method shown below: +This adds the in-place `MutableCollection` method shown below: ```swift extension Sequence { From bf31ba1a8c95ebfb2e4cff397ad5e9f80df4bd96 Mon Sep 17 00:00:00 2001 From: Bruno Rocha Date: Fri, 9 Oct 2020 20:00:25 +0200 Subject: [PATCH 10/27] Update PartialSort.swift --- Sources/Algorithms/PartialSort.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Algorithms/PartialSort.swift b/Sources/Algorithms/PartialSort.swift index 64232f66..ef592bf5 100644 --- a/Sources/Algorithms/PartialSort.swift +++ b/Sources/Algorithms/PartialSort.swift @@ -147,7 +147,7 @@ where Self: RandomAccessCollection, Element: Comparable { extension MutableCollection where Self: RandomAccessCollection { typealias Priority = (Element, Element) throws -> Bool - /// Partially sorts this collection by using an in place heapsort that stops + /// Partially sorts this collection by using an in-place heapsort that stops /// after we find the desired k amount /// of elements. The heap is stored and processed in reverse order so that /// the collection doesn't have to be flipped once the final result is found. From acb3583a8dda838f4beaf5250b37c6eb49b457bf Mon Sep 17 00:00:00 2001 From: Bruno Rocha Date: Fri, 9 Oct 2020 20:04:08 +0200 Subject: [PATCH 11/27] Cleaning up iterators logic --- Sources/Algorithms/PartialSort.swift | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/Sources/Algorithms/PartialSort.swift b/Sources/Algorithms/PartialSort.swift index ef592bf5..36fc8c3b 100644 --- a/Sources/Algorithms/PartialSort.swift +++ b/Sources/Algorithms/PartialSort.swift @@ -161,33 +161,36 @@ extension MutableCollection where Self: RandomAccessCollection { Cannot partially sort with a negative amount of elements! """ ) - assert(k <= count, """ Cannot partially sort more than this Sequence's size! """ ) - guard k > 0 else { - return - } - guard isEmpty == false else { + guard isEmpty == false && k > 0 else { return } + var heapEndIndex = 0 for i in (count / 2).. Date: Fri, 9 Oct 2020 20:05:40 +0200 Subject: [PATCH 12/27] Update PartialSort.swift --- Sources/Algorithms/PartialSort.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/Algorithms/PartialSort.swift b/Sources/Algorithms/PartialSort.swift index 36fc8c3b..f95d2955 100644 --- a/Sources/Algorithms/PartialSort.swift +++ b/Sources/Algorithms/PartialSort.swift @@ -148,9 +148,9 @@ extension MutableCollection where Self: RandomAccessCollection { typealias Priority = (Element, Element) throws -> Bool /// Partially sorts this collection by using an in-place heapsort that stops - /// after we find the desired k amount - /// of elements. The heap is stored and processed in reverse order so that - /// the collection doesn't have to be flipped once the final result is found. + /// after we find the desired k amount of elements. The heap is stored and + /// processed in reverse order so that the collection doesn't have to be + /// flipped once the final result is found. /// /// Complexity: O(k log n) mutating func __partiallySort( From d4a2e6be0d997c6105220187661ec601e53ae033 Mon Sep 17 00:00:00 2001 From: Bruno Rocha Date: Fri, 9 Oct 2020 20:08:32 +0200 Subject: [PATCH 13/27] Cleaning docs --- Sources/Algorithms/PartialSort.swift | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Sources/Algorithms/PartialSort.swift b/Sources/Algorithms/PartialSort.swift index f95d2955..698ad190 100644 --- a/Sources/Algorithms/PartialSort.swift +++ b/Sources/Algorithms/PartialSort.swift @@ -28,12 +28,12 @@ extension Sequence { /// /// If you need to sort a sequence but only need access to a prefix of its /// elements, using this method can give you a performance boost over sorting - /// the entire sequence. + /// the entire sequence. /// /// - Parameter count: The k number of elements to partially sort. /// - Parameter areInIncreasingOrder: A predicate that returns true if its /// first argument should be ordered before its second argument; - /// otherwise, false. + /// otherwise, false. /// /// - Complexity: O(k log n) public func partiallySorted( @@ -64,7 +64,7 @@ extension Sequence where Element: Comparable { /// /// If you need to sort a sequence but only need access to a prefix of /// its elements, using this method can give you a performance boost over - /// sorting the entire sequence. + /// sorting the entire sequence. /// /// - Parameter count: The k number of elements to partially sort /// in ascending order. @@ -199,10 +199,11 @@ extension MutableCollection where Self: RandomAccessCollection { /// The heap is stored in reverse order, so sifting down will actually /// move the element up in the heap. /// - /// - Parameter i: The element index to sift down + /// - Parameter i: The element index to sift down. /// - Parameter by: The predicate to use when determining the priority - /// of elements in the heap - /// - Parameter heapEndIndex: The index in reverse order, where the heap ends. + /// of elements in the heap. + /// - Parameter heapEndIndex: The index, in reverse order, + /// where the heap ends. private mutating func siftDown( _ i: Int, by priority: Priority, From 62ee6f278f920e36149c8f504483fbcfe5518f90 Mon Sep 17 00:00:00 2001 From: Rafael Machado Date: Wed, 21 Oct 2020 11:22:27 -0300 Subject: [PATCH 14/27] Change implementation and name --- Sources/Algorithms/PartialSort.swift | 284 ++---------------- .../PartialSortTests.swift | 98 ++---- 2 files changed, 58 insertions(+), 324 deletions(-) diff --git a/Sources/Algorithms/PartialSort.swift b/Sources/Algorithms/PartialSort.swift index 698ad190..fd05014b 100644 --- a/Sources/Algorithms/PartialSort.swift +++ b/Sources/Algorithms/PartialSort.swift @@ -7,280 +7,46 @@ // // See https://swift.org/LICENSE.txt for license information // -//===----------------------------------------------------------------------===// - -extension Sequence { - /// Returns the elements of the sequence such that the 0...k range contains - /// the first k sorted elements in this sequence, using the given predicate - /// as the comparison between elements. - /// - /// This example partially sorts an array of integers to retrieve its three - /// smallest values: - /// - /// let numbers = [7,1,6,2,8,3,9] - /// let almostSorted = numbers.partiallySorted(3, <) - /// // [1, 2, 3, 9, 7, 6, 8] - /// let smallestThree = almostSorted.prefix(3) - /// // [1, 2, 3] - /// - /// The order of equal elements is not guaranteed to be preserved, and the - /// order of the remaining elements is unspecified. - /// - /// If you need to sort a sequence but only need access to a prefix of its - /// elements, using this method can give you a performance boost over sorting - /// the entire sequence. - /// - /// - Parameter count: The k number of elements to partially sort. - /// - Parameter areInIncreasingOrder: A predicate that returns true if its - /// first argument should be ordered before its second argument; - /// otherwise, false. - /// - /// - Complexity: O(k log n) - public func partiallySorted( - _ count: Int, - by areInIncreasingOrder: (Element, Element) throws -> Bool - ) rethrows -> [Element] { - var result = ContiguousArray(self) - try result.partiallySort(count, by: areInIncreasingOrder) - return Array(result) - } -} - -extension Sequence where Element: Comparable { - /// Returns the elements of the sequence such that the 0...k range contains - /// the first k smallest elements in this sequence. - /// - /// This example partially sorts an array of integers to retrieve its three - /// smallest values: - /// - /// let numbers = [7,1,6,2,8,3,9] - /// let almostSorted = numbers.partiallySorted(3) - /// // [1, 2, 3, 9, 7, 6, 8] - /// let smallestThree = almostSorted.prefix(3) - /// // [1, 2, 3] - /// - /// The order of equal elements is not guaranteed to be preserved, and the - /// order of the remaining elements is unspecified. - /// - /// If you need to sort a sequence but only need access to a prefix of - /// its elements, using this method can give you a performance boost over - /// sorting the entire sequence. - /// - /// - Parameter count: The k number of elements to partially sort - /// in ascending order. - /// - /// - Complexity: O(k log n) - public func partiallySorted(_ count: Int) -> [Element] { - return partiallySorted(count, by: <) - } -} - -extension MutableCollection where Self: RandomAccessCollection { - /// Rearranges this collection such that the 0...k range contains the first - /// k sorted elements in this collection, using the given predicate as the - /// comparison between elements. - /// - /// This example partially sorts an array of integers to retrieve its three - /// smallest values: - /// - /// var numbers = [7,1,6,2,8,3,9] - /// numbers.partiallySort(3, <) - /// // [1, 2, 3, 9, 7, 6, 8] - /// let smallestThree = numbers.prefix(3) - /// // [1, 2, 3] - /// - /// The order of equal elements is not guaranteed to be preserved, and the - /// order of the remaining elements is unspecified. - /// - /// If you need to sort a collection but only need access to a prefix of its - /// elements, using this method can give you a performance boost over sorting - /// the entire collection. - /// - /// - Parameter count: The k number of elements to partially sort. - /// - Parameter areInIncreasingOrder: A predicate that returns true if its - /// first argument should be ordered before its second argument; - /// otherwise, false. - /// - /// - Complexity: O(k log n) - public mutating func partiallySort( - _ count: Int, - by areInIncreasingOrder: (Element, Element) throws -> Bool - ) rethrows { - try __partiallySort(count, by: areInIncreasingOrder) - } -} - -extension MutableCollection -where Self: RandomAccessCollection, Element: Comparable { - /// Rearranges this collection such that the 0...k range contains the first - /// k smallest elements in this collection. - /// - /// This example partially sorts an array of integers to retrieve its three - /// smallest values: - /// - /// var numbers = [7,1,6,2,8,3,9] - /// numbers.partiallySort(3) - /// // [1, 2, 3, 9, 7, 6, 8] - /// let smallestThree = numbers.prefix(3) - /// // [1, 2, 3] - /// - /// The order of equal elements is not guaranteed to be preserved, and the - /// order of the remaining elements is unspecified. - /// - /// If you need to sort a collection but only need access to a prefix of its - /// elements, using this method can give you a performance boost over sorting - /// the entire collection. - /// - /// - Parameter count: The k number of elements to partially sort - /// in ascending order. - /// - /// - Complexity: O(k log n) - public mutating func partiallySort(_ count: Int) { - partiallySort(count, by: <) - } -} //===----------------------------------------------------------------------===// -// __partiallySort(_:by:) +// sortedPrefix(_:by:) //===----------------------------------------------------------------------===// -extension MutableCollection where Self: RandomAccessCollection { - typealias Priority = (Element, Element) throws -> Bool +extension Collection { - /// Partially sorts this collection by using an in-place heapsort that stops - /// after we find the desired k amount of elements. The heap is stored and - /// processed in reverse order so that the collection doesn't have to be - /// flipped once the final result is found. - /// - /// Complexity: O(k log n) - mutating func __partiallySort( - _ k: Int, - by areInIncreasingOrder: Priority - ) rethrows { - assert(k >= 0, """ - Cannot partially sort with a negative amount of elements! + public func sortedPrefix( + _ count: Int, + by areInIncreasingOrder: (Element, Element) throws -> Bool + ) rethrows -> [Self.Element] { + assert(count >= 0, """ + Cannot prefix with a negative amount of elements! """ ) - assert(k <= count, """ - Cannot partially sort more than this Sequence's size! + assert(count <= self.count, """ + Cannot prefix more than this Collection's size! """ ) - guard isEmpty == false && k > 0 else { - return - } - - var heapEndIndex = 0 - for i in (count / 2).. Int { - let reverseHeapTrueIndex = self.count - 1 - index - let leftChildDistance = - leftChildIndex(of: reverseHeapTrueIndex) - reverseHeapTrueIndex - let leftChild = index - leftChildDistance - - let rightChildDistance = - rightChildIndex(of: reverseHeapTrueIndex) - reverseHeapTrueIndex - let rightChild = index - rightChildDistance - - let left = try highestPriorityIndex( - of: index, - and: leftChild, - by: priority, - heapEndIndex: heapEndIndex - ) - - let right = try highestPriorityIndex( - of: index, - and: rightChild, - by: priority, - heapEndIndex: heapEndIndex - ) - return try highestPriorityIndex( - of: left, - and: right, - by: priority, - heapEndIndex: heapEndIndex - ) - } - - private func leftChildIndex(of index: Int) -> Int { - return (2 * index) + 1 + return result } +} - private func rightChildIndex(of index: Int) -> Int { - return (2 * index) + 2 - } +extension Collection where Element: Comparable { - private func highestPriorityIndex( - of parent: Int, - and child: Int, - by priority: Priority, - heapEndIndex: Int - ) rethrows -> Int { - guard child >= heapEndIndex else { - return parent - } - let childElement = self[index(startIndex, offsetBy: child)] - let parentElement = self[index(startIndex, offsetBy: parent)] - guard try priority(childElement, parentElement) else { - return parent - } - return child + public func sortedPrefix(_ count: Int) -> [Element] { + return sortedPrefix(count, by: <) } } diff --git a/Tests/SwiftAlgorithmsTests/PartialSortTests.swift b/Tests/SwiftAlgorithmsTests/PartialSortTests.swift index cc05c30a..013dc4ac 100644 --- a/Tests/SwiftAlgorithmsTests/PartialSortTests.swift +++ b/Tests/SwiftAlgorithmsTests/PartialSortTests.swift @@ -15,109 +15,77 @@ import Algorithms final class PartialSortTests: XCTestCase { func testEmpty() { let array = [Int]() - XCTAssertEqual(array.partiallySorted(0), []) + XCTAssertEqual(array.sortedPrefix(0), []) } - func testPartialSortWithPriority() { + func testSortedPrefixWithOrdering() { let array: [Int] = [20, 1, 4, 70, 100, 2, 3, 7, 90] - XCTAssertEqual(array.partiallySorted(0, by: >), array) + XCTAssertEqual(array.sortedPrefix(0, by: >), []) XCTAssertEqual( - array.partiallySorted(1, by: >), - [100, 1, 4, 3, 7, 20, 70, 90, 2] + array.sortedPrefix(1, by: >), + [100] ) XCTAssertEqual( - array.partiallySorted(5, by: >), - [100, 90, 70, 20, 7, 2, 4, 3, 1] + array.sortedPrefix(5, by: >), + [100, 90, 70, 20, 7] ) XCTAssertEqual( - array.partiallySorted(9, by: >), + array.sortedPrefix(9, by: >), [100, 90, 70, 20, 7, 4, 3, 2, 1] ) - XCTAssertEqual([1].partiallySorted(0, by: <), [1]) - XCTAssertEqual([1].partiallySorted(0, by: >), [1]) - XCTAssertEqual([1].partiallySorted(1, by: <), [1]) - XCTAssertEqual([1].partiallySorted(1, by: >), [1]) - XCTAssertEqual([0, 1].partiallySorted(1, by: <), [0, 1]) - XCTAssertEqual([1, 0].partiallySorted(1, by: <), [0, 1]) - XCTAssertEqual([1, 0].partiallySorted(2, by: <), [0, 1]) - XCTAssertEqual([0, 1].partiallySorted(1, by: >), [1, 0]) - XCTAssertEqual([1, 0].partiallySorted(1, by: >), [1, 0]) - XCTAssertEqual([1, 0].partiallySorted(2, by: >), [1, 0]) + XCTAssertEqual([1].sortedPrefix(0, by: <), []) + XCTAssertEqual([1].sortedPrefix(0, by: >), []) + XCTAssertEqual([1].sortedPrefix(1, by: <), [1]) + XCTAssertEqual([1].sortedPrefix(1, by: >), [1]) + XCTAssertEqual([0, 1].sortedPrefix(1, by: <), [0]) + XCTAssertEqual([1, 0].sortedPrefix(1, by: <), [0]) + XCTAssertEqual([1, 0].sortedPrefix(2, by: <), [0, 1]) + XCTAssertEqual([0, 1].sortedPrefix(1, by: >), [1]) + XCTAssertEqual([1, 0].sortedPrefix(1, by: >), [1]) + XCTAssertEqual([1, 0].sortedPrefix(2, by: >), [1, 0]) XCTAssertEqual( - [1, 2, 3, 4, 7, 20, 70, 90, 100].partiallySorted(5, by: <), - [1, 2, 3, 4, 7, 90, 70, 20, 100] + [1, 2, 3, 4, 7, 20, 70, 90, 100].sortedPrefix(5, by: <), + [1, 2, 3, 4, 7] ) XCTAssertEqual( - [1, 2, 3, 4, 7, 20, 70, 90, 100].partiallySorted(5, by: >), - [100, 90, 70, 20, 7, 2, 4, 3, 1] + [1, 2, 3, 4, 7, 20, 70, 90, 100].sortedPrefix(5, by: >), + [100, 90, 70, 20, 7] ) XCTAssertEqual( - [1, 2, 3, 4, 7, 20, 70, 90, 100].partiallySorted(5, by: >), - [100, 90, 70, 20, 7, 2, 4, 3, 1] + [1, 2, 3, 4, 7, 20, 70, 90, 100].sortedPrefix(5, by: >), + [100, 90, 70, 20, 7] ) XCTAssertEqual( - [1, 2, 3, 4, 7, 20, 70, 90, 100].partiallySorted(5, by: <), - [1, 2, 3, 4, 7, 90, 70, 20, 100] + [1, 2, 3, 4, 7, 20, 70, 90, 100].sortedPrefix(5, by: <), + [1, 2, 3, 4, 7] ) } - func testPartialSortComparable() { + func testSortedPrefixComparable() { let array: [Int] = [20, 1, 4, 70, 100, 2, 3, 7, 90] - XCTAssertEqual(array.partiallySorted(0), array) + XCTAssertEqual(array.sortedPrefix(0), []) XCTAssertEqual( - array.partiallySorted(1), - [1, 90, 4, 70, 100, 7, 3, 2, 20] + array.sortedPrefix(1), + [1] ) XCTAssertEqual( - array.partiallySorted(5), - [1, 2, 3, 4, 7, 90, 70, 20, 100] + array.sortedPrefix(5), + [1, 2, 3, 4, 7] ) XCTAssertEqual( - array.partiallySorted(9), - [1, 2, 3, 4, 7, 20, 70, 90, 100] - ) - } - - func testPartialSortInPlaceComparable() { - let originalArray: [Int] = [20, 1, 4, 70, 100, 2, 3, 7, 90] - var array = originalArray - - array.partiallySort(0) - XCTAssertEqual(array, originalArray) - - array = originalArray - - array.partiallySort(1) - XCTAssertEqual( - array, - [1, 90, 4, 70, 100, 7, 3, 2, 20] - ) - - array = originalArray - - array.partiallySort(5) - XCTAssertEqual( - array, - [1, 2, 3, 4, 7, 90, 70, 20, 100] - ) - - array = originalArray - - array.partiallySort(9) - XCTAssertEqual( - array, + array.sortedPrefix(9), [1, 2, 3, 4, 7, 20, 70, 90, 100] ) } From f674851cc918f419182021aa8d60385d0aef2241 Mon Sep 17 00:00:00 2001 From: Bruno Rocha Date: Wed, 21 Oct 2020 16:40:06 +0200 Subject: [PATCH 15/27] DocDocs --- Guides/PartialSort.md | 47 +++-- README.md | 2 +- Sources/Algorithms/PartialSort.swift | 246 ++------------------------- 3 files changed, 36 insertions(+), 259 deletions(-) diff --git a/Guides/PartialSort.md b/Guides/PartialSort.md index 39df4ff4..425b990d 100644 --- a/Guides/PartialSort.md +++ b/Guides/PartialSort.md @@ -1,58 +1,51 @@ -# Partial Sort +# Partial Sort (sortedPrefix) [[Source](https://github.com/apple/swift-algorithms/blob/main/Sources/Algorithms/PartialSort.swift) | [Tests](https://github.com/apple/swift-algorithms/blob/main/Tests/SwiftAlgorithmsTests/PartialSortTests.swift)] -Returns a collection such that the `0...k` range contains the first k sorted elements of a sequence. -The order of equal elements is not guaranteed to be preserved, and the order of the remaining elements is unspecified. +Returns the first k elements of this collection when it's sorted. -If you need to sort a sequence but only need access to a prefix of its elements, -using this method can give you a performance boost over sorting the entire sequence. +If you need to sort a sequence but only need access to a prefix of its +elements, using this method can give you a performance boost over sorting +the entire collection. The order of equal elements is guaranteed to be +preserved. ```swift let numbers = [7,1,6,2,8,3,9] -let almostSorted = numbers.partiallySorted(3, <) -// [1, 2, 3, 9, 7, 6, 8] +let smallestThree = numbers.sortedPrefix(<) +// [1, 2, 3] ``` ## Detailed Design -This adds the in place `MutableCollection` method shown below: +This adds the `Collection` method shown below: ```swift -extension Sequence { - func partiallySort(_ count: Int, by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows +extension Collection { + public func sortedPrefix(_ count: Int, by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> [Element] } ``` -Additionally, versions of this method that return a new array and abstractions for `Comparable` types are also provided: +Additionally, a version of this method for `Comparable` types is also provided: ```swift -extension MutableCollection where Self: RandomAccessCollection, Element: Comparable { - public mutating func partiallySort(_ count: Int) -} - -extension Sequence { - public func partiallySorted(_ count: Int, by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> [Element] -} - -extension Sequence where Element: Comparable { - public func partiallySorted(_ count: Int) -> [Element] +extension Collection where Element: Comparable { + public func sortedPrefix(_ count: Int) -> [Element] } ``` ### Complexity -Partially sorting is a O(_k log n_) operation, where _k_ is the number of elements to sort -and _n_ is the length of the sequence. +The algorithm used is based on [Soroush Khanlou's research on this matter](https://khanlou.com/2018/12/analyzing-complexity/). The total complexity is `O(k log k + nk)`, which will result in a runtime close to `O(n)` if k is a small amount. If k is a large amount (more than 10% of the collection), we fallback to sorting the entire array. Realistically, this means the worst case is actually `O(n log n)`. + +Here are some benchmarks we made that demonstrates how this implementation (SmallestM) behaves when k increases (before implementing the fallback): -`partiallySort(_:by:)` is a slight generalization of a priority queue. It's implemented -as an in line heapsort that stops after _k_ runs. +![Benchmark](https://i.imgur.com/F5UEQnl.png) +![Benchmark 2](https://i.imgur.com/Bm9DKRc.png) ### Comparison with other languages -**C++:** The `` library defines a `partial_sort` function with similar -semantics to this one. +**C++:** The `` library defines a `partial_sort` function where the entire array is returned. **Python:** Defines a `heapq` priority queue that can be used to manually achieve the same result. diff --git a/README.md b/README.md index 99ace059..4e02dbbe 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Read more about the package, and the intent behind it, in the [announcement on s #### Partial sorting -- [`partiallySorted(_:by:)`](https://github.com/apple/swift-algorithms/blob/main/Guides/PartialSort.md): Sorts a sequence only up to a specific index, leaving the remaining elements unsorted. +- [`sortedPrefix(_:by:)`](https://github.com/apple/swift-algorithms/blob/main/Guides/PartialSort.md): Returns the first k elements of a sorted collection. #### Other useful operations diff --git a/Sources/Algorithms/PartialSort.swift b/Sources/Algorithms/PartialSort.swift index 64232f66..6205b23b 100644 --- a/Sources/Algorithms/PartialSort.swift +++ b/Sources/Algorithms/PartialSort.swift @@ -10,32 +10,27 @@ //===----------------------------------------------------------------------===// extension Sequence { - /// Returns the elements of the sequence such that the 0...k range contains - /// the first k sorted elements in this sequence, using the given predicate - /// as the comparison between elements. + /// Returns the first k elements of this collection when it's sorted using + /// the given predicate as the comparison between elements. /// /// This example partially sorts an array of integers to retrieve its three /// smallest values: /// /// let numbers = [7,1,6,2,8,3,9] - /// let almostSorted = numbers.partiallySorted(3, <) - /// // [1, 2, 3, 9, 7, 6, 8] - /// let smallestThree = almostSorted.prefix(3) + /// let smallestThree = numbers.sortedPrefix(3, <) /// // [1, 2, 3] /// - /// The order of equal elements is not guaranteed to be preserved, and the - /// order of the remaining elements is unspecified. - /// - /// If you need to sort a sequence but only need access to a prefix of its + /// If you need to sort a collection but only need access to a prefix of its /// elements, using this method can give you a performance boost over sorting - /// the entire sequence. + /// the entire collection. The order of equal elements is guaranteed to be + /// preserved. /// /// - Parameter count: The k number of elements to partially sort. /// - Parameter areInIncreasingOrder: A predicate that returns true if its /// first argument should be ordered before its second argument; - /// otherwise, false. + /// otherwise, false. /// - /// - Complexity: O(k log n) + /// - Complexity: O(k log k + nk) public func partiallySorted( _ count: Int, by areInIncreasingOrder: (Element, Element) throws -> Bool @@ -47,236 +42,25 @@ extension Sequence { } extension Sequence where Element: Comparable { - /// Returns the elements of the sequence such that the 0...k range contains - /// the first k smallest elements in this sequence. + /// Returns the first k elements of this collection when it's sorted. /// /// This example partially sorts an array of integers to retrieve its three /// smallest values: /// /// let numbers = [7,1,6,2,8,3,9] - /// let almostSorted = numbers.partiallySorted(3) - /// // [1, 2, 3, 9, 7, 6, 8] - /// let smallestThree = almostSorted.prefix(3) + /// let smallestThree = numbers.sortedPrefix(<) /// // [1, 2, 3] /// - /// The order of equal elements is not guaranteed to be preserved, and the - /// order of the remaining elements is unspecified. - /// - /// If you need to sort a sequence but only need access to a prefix of - /// its elements, using this method can give you a performance boost over - /// sorting the entire sequence. + /// If you need to sort a sequence but only need access to a prefix of its + /// elements, using this method can give you a performance boost over sorting + /// the entire collection. The order of equal elements is guaranteed to be + /// preserved. /// /// - Parameter count: The k number of elements to partially sort /// in ascending order. /// - /// - Complexity: O(k log n) + /// - Complexity: O(k log k + nk) public func partiallySorted(_ count: Int) -> [Element] { return partiallySorted(count, by: <) } } - -extension MutableCollection where Self: RandomAccessCollection { - /// Rearranges this collection such that the 0...k range contains the first - /// k sorted elements in this collection, using the given predicate as the - /// comparison between elements. - /// - /// This example partially sorts an array of integers to retrieve its three - /// smallest values: - /// - /// var numbers = [7,1,6,2,8,3,9] - /// numbers.partiallySort(3, <) - /// // [1, 2, 3, 9, 7, 6, 8] - /// let smallestThree = numbers.prefix(3) - /// // [1, 2, 3] - /// - /// The order of equal elements is not guaranteed to be preserved, and the - /// order of the remaining elements is unspecified. - /// - /// If you need to sort a collection but only need access to a prefix of its - /// elements, using this method can give you a performance boost over sorting - /// the entire collection. - /// - /// - Parameter count: The k number of elements to partially sort. - /// - Parameter areInIncreasingOrder: A predicate that returns true if its - /// first argument should be ordered before its second argument; - /// otherwise, false. - /// - /// - Complexity: O(k log n) - public mutating func partiallySort( - _ count: Int, - by areInIncreasingOrder: (Element, Element) throws -> Bool - ) rethrows { - try __partiallySort(count, by: areInIncreasingOrder) - } -} - -extension MutableCollection -where Self: RandomAccessCollection, Element: Comparable { - /// Rearranges this collection such that the 0...k range contains the first - /// k smallest elements in this collection. - /// - /// This example partially sorts an array of integers to retrieve its three - /// smallest values: - /// - /// var numbers = [7,1,6,2,8,3,9] - /// numbers.partiallySort(3) - /// // [1, 2, 3, 9, 7, 6, 8] - /// let smallestThree = numbers.prefix(3) - /// // [1, 2, 3] - /// - /// The order of equal elements is not guaranteed to be preserved, and the - /// order of the remaining elements is unspecified. - /// - /// If you need to sort a collection but only need access to a prefix of its - /// elements, using this method can give you a performance boost over sorting - /// the entire collection. - /// - /// - Parameter count: The k number of elements to partially sort - /// in ascending order. - /// - /// - Complexity: O(k log n) - public mutating func partiallySort(_ count: Int) { - partiallySort(count, by: <) - } -} - -//===----------------------------------------------------------------------===// -// __partiallySort(_:by:) -//===----------------------------------------------------------------------===// - -extension MutableCollection where Self: RandomAccessCollection { - typealias Priority = (Element, Element) throws -> Bool - - /// Partially sorts this collection by using an in place heapsort that stops - /// after we find the desired k amount - /// of elements. The heap is stored and processed in reverse order so that - /// the collection doesn't have to be flipped once the final result is found. - /// - /// Complexity: O(k log n) - mutating func __partiallySort( - _ k: Int, - by areInIncreasingOrder: Priority - ) rethrows { - assert(k >= 0, """ - Cannot partially sort with a negative amount of elements! - """ - ) - - assert(k <= count, """ - Cannot partially sort more than this Sequence's size! - """ - ) - - guard k > 0 else { - return - } - guard isEmpty == false else { - return - } - var heapEndIndex = 0 - for i in (count / 2).. Int { - let reverseHeapTrueIndex = self.count - 1 - index - let leftChildDistance = - leftChildIndex(of: reverseHeapTrueIndex) - reverseHeapTrueIndex - let leftChild = index - leftChildDistance - - let rightChildDistance = - rightChildIndex(of: reverseHeapTrueIndex) - reverseHeapTrueIndex - let rightChild = index - rightChildDistance - - let left = try highestPriorityIndex( - of: index, - and: leftChild, - by: priority, - heapEndIndex: heapEndIndex - ) - - let right = try highestPriorityIndex( - of: index, - and: rightChild, - by: priority, - heapEndIndex: heapEndIndex - ) - return try highestPriorityIndex( - of: left, - and: right, - by: priority, - heapEndIndex: heapEndIndex - ) - } - - private func leftChildIndex(of index: Int) -> Int { - return (2 * index) + 1 - } - - private func rightChildIndex(of index: Int) -> Int { - return (2 * index) + 2 - } - - private func highestPriorityIndex( - of parent: Int, - and child: Int, - by priority: Priority, - heapEndIndex: Int - ) rethrows -> Int { - guard child >= heapEndIndex else { - return parent - } - let childElement = self[index(startIndex, offsetBy: child)] - let parentElement = self[index(startIndex, offsetBy: parent)] - guard try priority(childElement, parentElement) else { - return parent - } - return child - } -} From dd15b5a6a0744788b4796c840e19ba3f0cc473c8 Mon Sep 17 00:00:00 2001 From: Bruno Rocha Date: Wed, 21 Oct 2020 16:44:18 +0200 Subject: [PATCH 16/27] Docs --- Guides/PartialSort.md | 2 +- Sources/Algorithms/PartialSort.swift | 48 ++++++++++++++++++++++++---- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/Guides/PartialSort.md b/Guides/PartialSort.md index 425b990d..517f53a0 100644 --- a/Guides/PartialSort.md +++ b/Guides/PartialSort.md @@ -5,7 +5,7 @@ Returns the first k elements of this collection when it's sorted. -If you need to sort a sequence but only need access to a prefix of its +If you need to sort a collection but only need access to a prefix of its elements, using this method can give you a performance boost over sorting the entire collection. The order of equal elements is guaranteed to be preserved. diff --git a/Sources/Algorithms/PartialSort.swift b/Sources/Algorithms/PartialSort.swift index fd05014b..706d44af 100644 --- a/Sources/Algorithms/PartialSort.swift +++ b/Sources/Algorithms/PartialSort.swift @@ -8,12 +8,28 @@ // See https://swift.org/LICENSE.txt for license information // -//===----------------------------------------------------------------------===// -// sortedPrefix(_:by:) -//===----------------------------------------------------------------------===// - extension Collection { - + /// Returns the first k elements of this collection when it's sorted using + /// the given predicate as the comparison between elements. + /// + /// This example partially sorts an array of integers to retrieve its three + /// smallest values: + /// + /// let numbers = [7,1,6,2,8,3,9] + /// let smallestThree = numbers.sortedPrefix(3, <) + /// // [1, 2, 3] + /// + /// If you need to sort a collection but only need access to a prefix of its + /// elements, using this method can give you a performance boost over sorting + /// the entire collection. The order of equal elements is guaranteed to be + /// preserved. + /// + /// - Parameter count: The k number of elements to prefix. + /// - Parameter areInIncreasingOrder: A predicate that returns true if its + /// first argument should be ordered before its second argument; + /// otherwise, false. + /// + /// - Complexity: O(k log k + nk) public func sortedPrefix( _ count: Int, by areInIncreasingOrder: (Element, Element) throws -> Bool @@ -35,17 +51,35 @@ extension Collection { var result = try self.prefix(count).sorted(by: areInIncreasingOrder) for e in self.dropFirst(count) { if let last = result.last, try areInIncreasingOrder(last, e) { continue } - if let insertionIndex = try result.firstIndex (where: { try areInIncreasingOrder(e, $0) }) { + if let insertionIndex = try result.firstIndex(where: { try areInIncreasingOrder(e, $0) }) { result.insert(e, at: insertionIndex) result.removeLast() } } + return result } } extension Collection where Element: Comparable { - + /// Returns the first k elements of this collection when it's sorted using + /// the given predicate as the comparison between elements. + /// + /// This example partially sorts an array of integers to retrieve its three + /// smallest values: + /// + /// let numbers = [7,1,6,2,8,3,9] + /// let smallestThree = numbers.sortedPrefix(3, <) + /// // [1, 2, 3] + /// + /// If you need to sort a collection but only need access to a prefix of its + /// elements, using this method can give you a performance boost over sorting + /// the entire collection. The order of equal elements is guaranteed to be + /// preserved. + /// + /// - Parameter count: The k number of elements to prefix. + /// + /// - Complexity: O(k log k + nk) public func sortedPrefix(_ count: Int) -> [Element] { return sortedPrefix(count, by: <) } From c68537fcdcc332642f9e4ee3c04efacc2c91e117 Mon Sep 17 00:00:00 2001 From: Bruno Rocha Date: Wed, 21 Oct 2020 16:58:32 +0200 Subject: [PATCH 17/27] Docs --- Guides/PartialSort.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Guides/PartialSort.md b/Guides/PartialSort.md index 517f53a0..d07cceaf 100644 --- a/Guides/PartialSort.md +++ b/Guides/PartialSort.md @@ -45,7 +45,7 @@ Here are some benchmarks we made that demonstrates how this implementation (Smal ### Comparison with other languages -**C++:** The `` library defines a `partial_sort` function where the entire array is returned. +**C++:** The `` library defines a `partial_sort` function where the entire array is returned using a partial heap sort. **Python:** Defines a `heapq` priority queue that can be used to manually achieve the same result. From e8504fdfe94f386e58ec9a9e0414d944862f8658 Mon Sep 17 00:00:00 2001 From: Bruno Rocha Date: Wed, 21 Oct 2020 17:53:12 +0200 Subject: [PATCH 18/27] Optimize --- Sources/Algorithms/PartialSort.swift | 9 ++++--- .../PartialSortTests.swift | 25 +++++++++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/Sources/Algorithms/PartialSort.swift b/Sources/Algorithms/PartialSort.swift index 706d44af..5dbfc6bc 100644 --- a/Sources/Algorithms/PartialSort.swift +++ b/Sources/Algorithms/PartialSort.swift @@ -50,11 +50,12 @@ extension Collection { var result = try self.prefix(count).sorted(by: areInIncreasingOrder) for e in self.dropFirst(count) { - if let last = result.last, try areInIncreasingOrder(last, e) { continue } - if let insertionIndex = try result.firstIndex(where: { try areInIncreasingOrder(e, $0) }) { - result.insert(e, at: insertionIndex) - result.removeLast() + if let last = result.last, try areInIncreasingOrder(last, e) { + continue } + let insertionIndex = try result.partitioningIndex { try areInIncreasingOrder(e, $0) } + result.removeLast() + result.insert(e, at: insertionIndex) } return result diff --git a/Tests/SwiftAlgorithmsTests/PartialSortTests.swift b/Tests/SwiftAlgorithmsTests/PartialSortTests.swift index 013dc4ac..e1d5ec64 100644 --- a/Tests/SwiftAlgorithmsTests/PartialSortTests.swift +++ b/Tests/SwiftAlgorithmsTests/PartialSortTests.swift @@ -67,6 +67,31 @@ final class PartialSortTests: XCTestCase { [1, 2, 3, 4, 7, 20, 70, 90, 100].sortedPrefix(5, by: <), [1, 2, 3, 4, 7] ) + + XCTAssertEqual( + [4, 5, 6, 1, 2, 3].sortedPrefix(3, by: <), + [1, 2, 3] + ) + + XCTAssertEqual( + [4, 5, 9, 8, 7, 6].sortedPrefix(3, by: <), + [4, 5, 6] + ) + + XCTAssertEqual( + [4, 3, 2, 1].sortedPrefix(1, by: <), + [1] + ) + + XCTAssertEqual( + [4, 2, 1, 3].sortedPrefix(3, by: >), + [4, 3, 2] + ) + + XCTAssertEqual( + [4, 2, 1, 3].sortedPrefix(3, by: <), + [1, 2, 3] + ) } func testSortedPrefixComparable() { From 36e9a39167845c0cfbe9d82cb2843e70d2d0748d Mon Sep 17 00:00:00 2001 From: Rafael Machado Date: Wed, 28 Oct 2020 09:19:28 -0300 Subject: [PATCH 19/27] Fix header and remove assert --- Sources/Algorithms/PartialSort.swift | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Sources/Algorithms/PartialSort.swift b/Sources/Algorithms/PartialSort.swift index 5dbfc6bc..c577c0d8 100644 --- a/Sources/Algorithms/PartialSort.swift +++ b/Sources/Algorithms/PartialSort.swift @@ -7,6 +7,7 @@ // // See https://swift.org/LICENSE.txt for license information // +//===----------------------------------------------------------------------===// extension Collection { /// Returns the first k elements of this collection when it's sorted using @@ -38,18 +39,17 @@ extension Collection { Cannot prefix with a negative amount of elements! """ ) - assert(count <= self.count, """ - Cannot prefix more than this Collection's size! - """ - ) + + // Make sure we are within bounds + let prefixCount = Swift.min(count, self.count) // If we're attempting to prefix more than 10% of the collection, it's faster to sort everything. - guard count < (self.count / 10) else { - return Array(try sorted(by: areInIncreasingOrder).prefix(count)) + guard prefixCount < (self.count / 10) else { + return Array(try sorted(by: areInIncreasingOrder).prefix(prefixCount)) } - var result = try self.prefix(count).sorted(by: areInIncreasingOrder) - for e in self.dropFirst(count) { + var result = try self.prefix(prefixCount).sorted(by: areInIncreasingOrder) + for e in self.dropFirst(prefixCount) { if let last = result.last, try areInIncreasingOrder(last, e) { continue } From 1d22ef9224aeca4c1c1bcc00d13d7e832bd1b175 Mon Sep 17 00:00:00 2001 From: Rafael Machado Date: Sat, 31 Oct 2020 16:08:58 -0300 Subject: [PATCH 20/27] Add more tests (#4) --- .../PartialSortTests.swift | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/Tests/SwiftAlgorithmsTests/PartialSortTests.swift b/Tests/SwiftAlgorithmsTests/PartialSortTests.swift index e1d5ec64..3acdf39a 100644 --- a/Tests/SwiftAlgorithmsTests/PartialSortTests.swift +++ b/Tests/SwiftAlgorithmsTests/PartialSortTests.swift @@ -114,4 +114,33 @@ final class PartialSortTests: XCTestCase { [1, 2, 3, 4, 7, 20, 70, 90, 100] ) } + + func testSortedPrefixWithHugePrefix() { + XCTAssertEqual( + [4, 2, 1, 3].sortedPrefix(.max), + [1, 2, 3, 4] + ) + } + + func testStability() { + assertStability([1,1,1,2,5,7,3,6,2,5,7,3,6], withPrefix: 3) + assertStability([1,1,1,2,5,7,3,6,2,5,7,3,6], withPrefix: 6) + assertStability([1,1,1,2,5,7,3,6,2,5,7,3,6], withPrefix: 20) + assertStability([1,1,1,2,5,7,3,6,2,5,7,3,6], withPrefix: 1000) + } + + func assertStability( + _ actual: [Int], + withPrefix prefixCount: Int, + file: StaticString = #file, + line: UInt = #line + ) { + let indexed = actual.enumerated() + let sorted = indexed.map { $0 } .sortedPrefix(prefixCount) { $0.element < $1.element } + + for element in Set(actual) { + let filtered = sorted.filter { $0.element == element }.map(\.offset) + XCTAssertEqual(filtered, filtered.sorted()) + } + } } From 62096e1c108904c1f36e2a3aa8519d19323de088 Mon Sep 17 00:00:00 2001 From: Bruno Rocha Date: Sat, 31 Oct 2020 20:19:52 +0100 Subject: [PATCH 21/27] Update PartialSortTests.swift --- .../PartialSortTests.swift | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/Tests/SwiftAlgorithmsTests/PartialSortTests.swift b/Tests/SwiftAlgorithmsTests/PartialSortTests.swift index 3acdf39a..c89dc02e 100644 --- a/Tests/SwiftAlgorithmsTests/PartialSortTests.swift +++ b/Tests/SwiftAlgorithmsTests/PartialSortTests.swift @@ -127,20 +127,34 @@ final class PartialSortTests: XCTestCase { assertStability([1,1,1,2,5,7,3,6,2,5,7,3,6], withPrefix: 6) assertStability([1,1,1,2,5,7,3,6,2,5,7,3,6], withPrefix: 20) assertStability([1,1,1,2,5,7,3,6,2,5,7,3,6], withPrefix: 1000) + assertStability([0,0,0,0,0], withPrefix: 0) + assertStability([0,0,0,0,0], withPrefix: 1) + assertStability([0,0,0,0,0], withPrefix: 2) + assertStability([0,0,0,0,0], withPrefix: 5) + assertStability([0,0], withPrefix: 1) + assertStability([0,0], withPrefix: 2) + assertStability([0,1,0,1,0,1], withPrefix: 2) + assertStability([0,1,0,1,0,1], withPrefix: 6) + assertStability([0,0,0,1,1,1], withPrefix: 1) + assertStability([0,0,0,1,1,1], withPrefix: 3) + assertStability([0,0,0,1,1,1], withPrefix: 4) + assertStability([0,0,0,1,1,1], withPrefix: 6) + assertStability([1,1,1,0,0,0], withPrefix: 1) + assertStability([1,1,1,0,0,0], withPrefix: 3) + assertStability([1,1,1,0,0,0], withPrefix: 4) + assertStability([1,1,1,0,0,0], withPrefix: 6) } func assertStability( _ actual: [Int], - withPrefix prefixCount: Int, - file: StaticString = #file, - line: UInt = #line + withPrefix prefixCount: Int ) { let indexed = actual.enumerated() - let sorted = indexed.map { $0 } .sortedPrefix(prefixCount) { $0.element < $1.element } + let sorted = indexed.map { $0 }.sortedPrefix(prefixCount) { $0.element < $1.element } for element in Set(actual) { let filtered = sorted.filter { $0.element == element }.map(\.offset) XCTAssertEqual(filtered, filtered.sorted()) - } + } } } From 23bf863d3789446bf6465e2bb77db021bd51bc25 Mon Sep 17 00:00:00 2001 From: Bruno Rocha Date: Sun, 1 Nov 2020 11:42:09 +0100 Subject: [PATCH 22/27] Update Sources/Algorithms/PartialSort.swift Co-authored-by: Xiaodi Wu <13952+xwu@users.noreply.github.com> --- Sources/Algorithms/PartialSort.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Algorithms/PartialSort.swift b/Sources/Algorithms/PartialSort.swift index c577c0d8..6455fcf8 100644 --- a/Sources/Algorithms/PartialSort.swift +++ b/Sources/Algorithms/PartialSort.swift @@ -40,7 +40,7 @@ extension Collection { """ ) - // Make sure we are within bounds + // Make sure we are within bounds. let prefixCount = Swift.min(count, self.count) // If we're attempting to prefix more than 10% of the collection, it's faster to sort everything. From 379609b664833b526c5054157725a177ff50d102 Mon Sep 17 00:00:00 2001 From: Bruno Rocha Date: Sun, 1 Nov 2020 11:42:23 +0100 Subject: [PATCH 23/27] Update Sources/Algorithms/PartialSort.swift Co-authored-by: Xiaodi Wu <13952+xwu@users.noreply.github.com> --- Sources/Algorithms/PartialSort.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sources/Algorithms/PartialSort.swift b/Sources/Algorithms/PartialSort.swift index 6455fcf8..43b8506f 100644 --- a/Sources/Algorithms/PartialSort.swift +++ b/Sources/Algorithms/PartialSort.swift @@ -53,7 +53,8 @@ extension Collection { if let last = result.last, try areInIncreasingOrder(last, e) { continue } - let insertionIndex = try result.partitioningIndex { try areInIncreasingOrder(e, $0) } + let insertionIndex = + try result.partitioningIndex { try areInIncreasingOrder(e, $0) } result.removeLast() result.insert(e, at: insertionIndex) } From 435a38c8290e581686415888d17d770505cdf06e Mon Sep 17 00:00:00 2001 From: Bruno Rocha Date: Sun, 1 Nov 2020 11:42:32 +0100 Subject: [PATCH 24/27] Update Sources/Algorithms/PartialSort.swift Co-authored-by: Xiaodi Wu <13952+xwu@users.noreply.github.com> --- Sources/Algorithms/PartialSort.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sources/Algorithms/PartialSort.swift b/Sources/Algorithms/PartialSort.swift index 43b8506f..ab0e932b 100644 --- a/Sources/Algorithms/PartialSort.swift +++ b/Sources/Algorithms/PartialSort.swift @@ -43,7 +43,8 @@ extension Collection { // Make sure we are within bounds. let prefixCount = Swift.min(count, self.count) - // If we're attempting to prefix more than 10% of the collection, it's faster to sort everything. + // If we're attempting to prefix more than 10% of the collection, it's + // faster to sort everything. guard prefixCount < (self.count / 10) else { return Array(try sorted(by: areInIncreasingOrder).prefix(prefixCount)) } From 70973a23bb5c1d1fd9b5a8e9cb9bbad1b2459689 Mon Sep 17 00:00:00 2001 From: Bruno Rocha Date: Sun, 1 Nov 2020 11:52:45 +0100 Subject: [PATCH 25/27] Documentation fixes --- Guides/Resources/SortedPrefix/FewElements.png | Bin 0 -> 75366 bytes .../Resources/SortedPrefix/ManyElements.png | Bin 0 -> 79253 bytes Guides/{PartialSort.md => SortedPrefix.md} | 18 +++++++----------- README.md | 2 +- .../{PartialSort.swift => SortedPrefix.swift} | 8 ++++---- ...ortTests.swift => SortedPrefixTests.swift} | 2 +- 6 files changed, 13 insertions(+), 17 deletions(-) create mode 100644 Guides/Resources/SortedPrefix/FewElements.png create mode 100644 Guides/Resources/SortedPrefix/ManyElements.png rename Guides/{PartialSort.md => SortedPrefix.md} (73%) rename Sources/Algorithms/{PartialSort.swift => SortedPrefix.swift} (94%) rename Tests/SwiftAlgorithmsTests/{PartialSortTests.swift => SortedPrefixTests.swift} (99%) diff --git a/Guides/Resources/SortedPrefix/FewElements.png b/Guides/Resources/SortedPrefix/FewElements.png new file mode 100644 index 0000000000000000000000000000000000000000..56ccfccf36d441c028e7efc03b3b988507723b4a GIT binary patch literal 75366 zcmY&f19T1;yfLA`_B!bI}+ zHYNZ|V-OIDu*76&<#-JY->uJL_8%x;V^GYg{2jq0U~?hmh3NB8;sk`k7>RS1V|$VD z;J=aNAeK<&Z6CvosDFiT60H467$YdC0S(Wl`~aQA!0h4tcsA+Qy4CvW)%s03{idZ8tbc4k%a;IEh|POs!i7gYQVeM|np#d=8`6xHHXS zH~(j~5PCQo7)TCTpi&^5AVy#12oB1~{t8G=6p{1qb;}j3EwL0jaX66{pI^Tzp2o3? ze=%zsu2XWw1=L`JxH4ta%Ypk^MlWM?`m>NBriRpXDpSRhbin!5W1(F*2sb z+Y=0=MfSl4LF4MHNZ=ooewBJdQ}N{Eml;I$iiF?P%Zo9-Nq)<&KJ<}NEwX0{fvg)b zaI+=LbBcL%p*Q?l5?)B{&_}63g7-*hTFb;?5(fzetzrEZiebDJg(F7*X&pEB>*1YUF{X4e(&gKeYvVM!HR zrrLmVL}b{N^(RDzts5#rf|x2o{&1TLMBsf067n*m|kD{70r4&u@yJcIDk zBgFu*8GsDqZ~GN8Pp~Zp*$8n(AnGgpO)yvv0~N{--?&^B2{`Ip;~AV1tZ#l}x!+1K zUSPREGlS@aGiSU?pmBUR1+sslNeBO_0ks634%A@4MGVT{akItgfSC$_-$t;-)?>RJT;(2i{z2W|!? z9p3P;@gAHVw{3{6sV%#mS4*@j$|lTZDAEAsZsC)sC-n!>2g?V=2d8&njvxc2>(@S5 zp+1y8u|8cP_#Lj?Xa=EqGNv%y0W$+GJJ#A?>T(?do+6%7B(c&G7X>hJfeqoxVq8LP zA}7RNBub-;Mu?nY6=XIGl^>NRmk}y;SMiky&eY6on_aQ0nkk!Q%pA{QEfxT{%z`aU z098voC2kt^+2Q$`^57*x=AceU_jrxT!Fj2394w$#2v+pxV3pBPh0K4n-MKnbyi+y> z3Fq?6IV}dwX&1|8xC$34Bj=iC4$V!?ZRYvPywqNASpooOfIPry(Slq%=Xt=CBfcJf ztZb}oz!ZMwblaB=kc}@JlWj6>(f2d%i|)wot?uo2bjJv?#d41sbSb(l@5gu0FEw3_ z-UT0u@4PQdcOeL05d6OwL2-YX!v2C|j{AaBglo;pW5uB_!hFLPhh@deWq9YGJE+gh zgl+um_7}0C*Pgy%pt%BbDTI$&Z<23WOg@#GTk%ql{l-FCe9|OCSK=N=bz5c&N9x`CTWw> z8Le(6uM;*8bPuTy9_NHy^7uk{H~5-(n;e?laCqPGY`7INz%o_2Sh+i$o9r++FYUXI zgd9@O>lOoq8rGXjdxdDkDTquhmaAlGhn8yf40Q$hG<0*xrvkaGYF+ z6jz!C9|=x>XiRA2X?U34yQjKdcb0Xubz*OhZt}akykfmxd6ju(y`;Usd0BX7*H^$`bQC8EgT*_OiQ$ieO@ z^<+p8-yHfV1;OTzsepVN%VdQ~EYc4q7Tk<&TJ&T)9IHW#AWw>gvU>I*;VaPx5 zJe)Y-G(7!FkHCjjkX|ruCL>hpmrR8uUKTEc%i4MuS#m*NLHljWEyi>^kDiV9;hPft zJmwW9p41T8Fgc<0M<3FW+HHtZ3w0v3Fj$m+`*z`sT8jz?(G5ysXjZX%VN$_Ev0I^) zB<|POP^rNHl&Avj8!lcBFPE2)Usu20vGY^yQV}$sD)uW-70XzJ-Mw>n`Uc^-joIpi zG#*K!Mb|J8MuiVp;UpO4W;0~Op$aX2NFBJ0W#L_Bgl9yz*0v5~SkNWWbu|sRkG7Fd z--6vf(2CNd(^zSlbXeZkWgVOysC>^sr~baGnpQ=lzS8{7Y{73~U;(?_XR>*ke$=O# z!dgbkPq4SlfAiA6tNgq2_XJGm%6skhSC6~LxJ&Lu&dRmg3$56i;~%wNc7W^Td;fbi zZ`-HbvyfSYQd};!HtY39*mlXbKNlw1lh8j{q;7t=H&1NNwItRynw_>=!8hs9Inix& zX13_GcDB=0RJet#M9FY>B}9y0DnF^PD4(cRmA};}wcH-kZRy;%BRFGTe!S)H4|}Kx zszfeeEEIawc-TMitl_pddl6dbSw-r~*iA0I&%JLzlOSbOJC#@RmiX8j;%gUI;Bq85 z9uNKe?4P`Ex>rG1MHm$CK|YAZHboqHuHMpGI*)?0+*$bmB`2LuDf=*>|;-4 zyFC=qO8=rerW^GI<3sqCFtM6O@BJ<0l5i<_H{?ZkTravy<5TtV{KIAHay8hkUvAU1 zlki#oPT_ISPp(4lJbRCipK$cu|5fo};lyIRdB!GMtX@pq*pt)86HR~^URDNjd(zv8 zPZq@2860GHJ?zN?Zx^nIPb4zZC$$WOlas2>$FBAV z9r&uw&&Q2qH#b;VSYqO^P4M_s_Jd9Ct@YIB!wiRMyJxBz z1LPVQbqE?T4A2N)(H@eSLuA|LXN*-S&Hulq4{U-3L1R~s-%Ik>`9IACxCMJqnw{-) zEtzTm{q?_Z@0z^+^{D?e^>g<3CAU7`7g{m--=_{tKK$#g|89B?0V=h=IvP|r+3@eT z{Qab`^mFikI{2@qtFT~6EsgQ;#_53n?+~yl3i}%Le}2nfO&d^w9k-yw9Ao?a{~ZF2 z5XcW(0~BXs2$Xkp;Q4&qX4}mxi0@=LObJ?BSFJn@V(M)_?L`mImnvO9tl2<6G{gVz z>rufT*By{B=ymbTZt|n~!U4aFQURlJaE-@H8wE>n0N^e#}!G;FsN>-Z?Mr z7j5vRrKL~%@p86Tz0iA2Yu3Wv-h5RZH$Qkj-)>zWH(YX2H@mz%wz1m(Jsf|(GpCZY z#$*yl+hLM5cnu8`GyO0e-s^rr?P)sz>8b}DX*(~1j_LGtSD4nzI~PHP!FF4~0qnb4 z)t9rEgJeCS`2Dl`>7fG2_^b2gt=)onSs_KmI1Qgg4O7U6=i~h7uh`gO2jj_mfvB)Vjblqyl?*TAXrc|Glf zw7lJn?>M(J8jXB)yjaK9Y6jAoVm3m}c4ykEwv%rI!^6Vb;&60!CI0?kUkLf|T(-Q} z2|TX^B`hc}ND0s^7c7spamlPV+Fe45Qc_X`H0YAy6%-T>n`KGJ$RPgw`E$Hft*4*8 z+36u%q4J7=%l3tR-8O);_pjM|+WP!3WpOmka-)DkzzLK|XA|6~qoqZtH=S-8p!Lnu z`Ol*D6$SP?CML!=mQ)DUwb# z2={zgF=aHJ!t1Xbh;SCiA+vTur>MiAdBMlWx57EDP%~_tI=aOMY3LFTLG&|boDC0$ z>(@r&f3wZ6{LPiBRrz1hrH^d(9VT=-Au$m$ZO=BN?J33?htVK#MQ!XmUuX?w4rvh{oJ~u}5mNz$D$CZ%M)cs(B00a_Ln zR5UaqA|eodeSMnwFE~!_ox1t{3u;1IUw@MRb~Q7LZ&Rz!64L4yE3@Bdu>!@79uCOM zBXRg-`+h7#Bkf%`cP16EwQ5jX`U$73s+tyk*V5AR;<6f{b#rr*(=_kxcQHr-QHH`c z*EBFR1hv?BD5TXV7LJ3Q-Q+)+M630SzU%ECmvomy&*!~yHtUZz293IY#V@C8;zY~| zC;#L|nL`D@8cV3sI5eIuNEF|TS^nAFLvX>Y&Ej@AzCXn-1@3vg93OziltjfKYL%Un z6C0I6T0nR}=1}Fi4IomcYmSqsnQUZijJ@c-7s4%1z5$B2!F^%f#o-@ISk{wVCj7aL}+TMR%!+TVS?Z&%fs4&eQ@6ADAPq~ zx@QQ5U3BKDpYX%C)!_&yXu&vH$0UIrI7TX8CsNIQlmkOTNGU0YJK*=fQxY_AFO|#= zoTuA#F;bTElYGbdMKsK-ax(J%xMkC|U3g&IHRNa?^$VLBDh+S8L^({%yo84Fx~Xp{ zlWn#2zu>x?7viG*va3fjLY=xTcqEGt1;JRJ)tYDyU!{wVX9;n_{^J%} zDd$)YHTkmP|G>36!~+}<+0W5atjPZdaUbx2nc)FXSj+#vx{Be~`B!G~fHctx`=86a z;=s(rJ3N#0KS-)yO{5>Trt7z&`aelee;T=Fz-pSYf&70Wb@@At`fih&epBNAUUmQ` zlr@JhRv354FITeswTb5;V{NVX`+QVYAT+VWaPDn`Dj-c=A z0WPgz~)U>pN(1f18 zF<)N{=ZP&AD|*3^c;tsM+Qm7ncU}euu6F9=^!bbn#H7D=`?v}u)H494*2+aJi zXsTfbo6AWw0__cewqJS`b`50+5_yUq)jdFpX?K8~Y#$o$g<3lVJ>@IxUVS*JBSrC@ zGW4Z0^n)0(eNBYgX&k70iCZK8oAUiDM*NZGc~~KPE2ZGjhXQKnGB^PD=W0u}G8&Y- zDIcgc9}cMC)*jyECRN{tUhvV6*Qmerw-S z-0i~>g^wOV4mfxM2vcur$(u>6=LU32&^<%Xe5w=OU+E=2OajCERPn*=KR@7&3wgOV z1Jn+XWC-*&8p z!$(@$d|^n;dSH3enCjcDftx#aR(fVczW9Ja$|dij@D1l19ubZEk^AZn4v?Jgh}O$A z{d*P#B1m$6fg4XCucGAjLh$qd5K7vk*Kre75__gRpxj^@|6+foklxHz4+hVkY~U2U zh1aIHCGkZ9)>QVHA?MXwAe;0JoSbK(sm2i2py~aW9ddV$xFXhON^*3h20DkqGD#pB zgVLdUVz=GCPP5u+~Y=Hv37@ylfujR>|*z! zJ&dG=$k;;<01%-iSb=Kl39B@9ow`u%Bd<55Lq$goCVWge=RL2*>$3`;H}48e{6k)_ zGY9IKpZfQ9=eDCL&6=2#$+DCavYFduKbH0l&A%Tu`{|7P6hG9ZQs9j11 zCRsdV$!r)|w7$7kc`l`q9DG4%I`URVGa{i$sHoVp)t|YPkyPNbPJcu~L*F-ed33{? z=gcsg2&z^?BP|~>WC7^DG)RuKRcJwc)Y$N}k;N3XNRBCrfp17G$3d&1PQ0uxg#l3*`LVvYCF2k`Uz5N6*)0ha@4I0N`!Wd9-3`-thSOarr52ipu#zQnFVRQzuZR z9BbNyeSc1heh>(p&A8$$9pxxVpC6CiAt;qgNkELp;{T8+p5BNd^nL=>X?GT#&f<*) z@@vzq(y)GOG-H>>e+7%*qEL?(tkFyYb=_h{-GgCVFZvVVWIPVpcK*ZJY_$V}k0G)_ zowtAUgxqSdQo4zR*5kWh0AWGpo%X;3L4@6VJfpLKc;I6ig!B`4qsbCDujINAY5L+1 z05Wv;moGfX0c}r{mGmDmA$MnrXQ-V`0OKaV{EDH<6B1WbFX(9<7ZS^%<}0308g1%K zfK;^a<2e1-hZW2c9#=S_3m=dRSmlL96sIX5T_UvUxQ0ri(eN*?YJ+3n@?_TaIB(SS zSp2*_t?m}HXIhw_q#_mj#Q?!X7MR(b%pe z7318oPGNh$b^tt2bkpKP`XBc6gPp&r?x{NMX!nZ~-8C)&_~n9Mw}neiXie@8=xho3 z(6d4a4FZqVT`xK^O+GUl)xMGVlgUAtoXnR7&vd+Av{QY{`J{)qR2M<$g_#~R2$K>;Nk{H6D=g_2X*Bg4KC z2pl^C(`>IMYKIFccPkiY`xV1fo&>E&-=NMIAQ8?GM#C4NU3oeKw6xcNFt8(K|Q<&ksMdi=~t z2iuXlo$io}5e(r*oO#Bv!lO3_<1*bGhbe|vlk8hLKA#^gXLaKfO~B$mG>40%o9CP! zIf@jBHCgEG>EjJ3FQ-`QhH5*1t3DiMI~RZK0Ht2(x}xg zDj)h6E2wJwE*%}F&t~KA0h8PQ^POBi;mSvI&%z~^G+s&|qUKfv#1CwQ4CD%rTat$i zX(l(Qmtv6|HSy0^H?@T=hiP6=@_+}AyIFBYt5p`}(Y8g+IWncv(7x`EdwQPh0aQOs z+ke!Vy<&6%S(Rf!=pA2hLrA9)ht zo+N4{ZjCVOZ_l@pNm7{1zyeKM-=)%vz;zomKNj9P*>i{mwq8HhCk-ZY1gHcNTC)5E zR-MCt)$uHrQ(ZT#9>8kyT@WTSF$0N!2aDuaBY6v7tkn6D1WjW&NeF$%6__l?4ut31 z^|08h^PD6lWN1r+Pn3j5QgZUqTZK#2jbjFsUWIPws@ZyhopyH?Qm#&DgJ*Y)z?S_u z?Q#Pkezq-)rt5A_D%kO2SH6ApZ_@ha{&i+Vt}rR?IJUgkWas)rJ2%`9L)w4rR`Mn( zRVV!*Isw#V25eLxws;pmqsd{PntvYMA#!(zDzMpynjv3^6$cGGsvNNuxz9!G6Ce?d z7r8KwX*jl>pH`^Rt^!jj+qKTpV!$0MJy%V~1pB--1%X_lboY@Mbe8@3Mv2I||FwP^ zST0QHcO&sV^-uv3WtjiMh`hYM219*?Y?q=w$1}Zv)qbQGD1E?pZpX*PNi!cHk=G}S zm@uU-6-Xiz$D~T*q<*hz0UIXps9+o%<)IXEJ*{juIG)KpZ0UR+X1AgHM&Fv^_vWwO z(jZM=uf+FKeNx20B5{06-^1$ddCx`XWB`Ml8NnhHLf~#D01Oo}12#9N6>rkPS=G@F zD&$3=g2xR=pD#VR`DtOq%6(om#bG>#mJgahAul3ZP%f;(fu?i$Q}uNu%~Zsh>&C*#0}1(E`29;1Xx;fT6cW>d%u`#ytr$t zQ8c{vb;*K$X(yv;2qsZzRP0BQStyv86xBva&Jr!<0oUYai^3=l#ip``Mi>yi7q5tm z*NZ07Mb<^oM5yMEMI$g$-9=gOnA2282qi024*P!g2k!*qS~G~qeo^hxoSVsXJ50r5 zF^ick3SA$CVDor5Qzf>(07n#giaV)Bn})vZe;wGnGBzT*+nVm_oaSpEQ2WWl!qmWr zY_~A4{%5&1e?on6IFYoNl`b;}hF0-U*gr@BD2`JR!_Ep-FpoHX< z@){2PVl+`kW=iEHpX)09&6Ov6`bG0tNKX)XDOryKzKWdbdql(O+kCB41}T@U{dwb( z5IAkcKU@tBtWnr`-sxr&t{YaRcIns`sW8qQQQ_-GHleWF2OHEFEs@48NpfkC$a!Yl_ipu$v_CssK2M9t(|VP1Se4x zsr{jZ4xaeweis>e0ut3W-EpZ=39wnhIHQ(I_3iRBo<4T?JA^u6iT7p}AsFn`; zpFy2G{YI5M-AZjT5S=4a$0uk%dplM1gstM()CdDet}AJ8I8{7W;n><_`-W<58z}hP6-L<`EA>ey zyJ6U;t`jG*G9(TA zseVBbn5aam5u%(Be{~TYIt{^}ju(|rf~QLwwbzl}p#1~00-mm=M;*o3YW`Ik{Nq76 zEwUx@zXaLe6wd|#mfa4wTk{xyeg7Q%!rFL~1E8|ZJauM2lFjL0l6h2!SP~m8ljwB1 z2zj>9IDU04NY;1g_31TarR6*0!v}{cxL9y7_MI-oVq2i4_#F|)=>!hWPvJGgJek%Y zue2$2{v>!#yG%6}CpCptNGYFR)Ulu{vFL}UEawlX*MbGQ>F4*ubQ_@nf{Z7tIU`A< zvc(alu!ev(Rh*q~H-%VMQ6&gCFt$^iZ}xB~&t-rb-;wH7nwtZ&U8A~%P>tstKxSx0 zz<2_+($$PG(hiXH)iaWp-%7!M-=&DGm>37jlvOKw;941=Sab5>Bc;4^+rdFW*zvUz zo7F0J{+sc9UED3lxp~p4e@`zeW9)bkK};nr3AnkORMRU^9P zUB?XchIM>A28cv~3}N-8p9~WdGJH3ZhEjT2H1jju(vj@%g+_F0BIM#`wX|yPAGsdW zA+uH4$QQE1>33SE66dSectNR`5#mwwpjw@bRAOJ?ylScLX3o?F&(joi{{)N^(&kh> zoUBmX6h*%jg+^Rvq{MlW1s`5{jWObEN;Cz)k-D((&eE{A(MMy*ag!iDML$7Ofl za<@ipfRzu}IU=3lmmyj1oTy`Gx@0;d>MmYR^Sw@V^gDKJPAYn@dp&Nf&Ks>fTc)cg zu$a349H;z&uv8-YOv);pX_L;O!$CM+;Zv~kSl-XQX*8#XJtFPvRAE zwTTAle|^&8W=*-UcapWC!a?Fhy(rOB(FfgeXz) z_docyA2R*>rnvP(DIq3>_sNYdE>D=D%@jiHUfX&i*8!UbII{NIw)$XR&(CN0UW}5n z1hYXKe1zFBT8B_k`Uhq+S137i-E87o{UI!^qSacT>Ni ztNBojK;w?R{z%l6C!^t~ktyPY?U~I8JD3HhzyDwv3Yp-PG^Jyj!RV^Xe0WPbsvF+| zj?|E4`**zk25O~Z^ndiXWnl=k9#u^?1hHk@Jb8kZas~?-*oR9((K(E;p!D&p^$Klf z7{LI#Yeq)rKkQ(Yl*k7}4ZlGX;qkHHSTKLb%1N@MChL=1&5OhR zX4TyM8h(tHTpOxc%agBt2Oy%MP3GgQW~9i-HE%!iZ(6f5n}$~3UCgS^vTFaQf^VRQ zC{R&4Yn-FGiC3K=r#@e3a{CH6U84R1%MzbP13gP^TlgOOaV_<3GAr0^>e-St8C6N| zK`XiX?InA3Ktt+8l3K%}dgJ5IcY}e5ID-~0Hh$QR^C6ag(4-bt@X#ih{|KBcp}x`y ztz=g1s6@?mXzJHcqllTe?J^)Q(pEKTIu|v6a8)mRqfdWGU&zPQ*is{?iHVa#c^ zjem6PRZuVzJ)FT(p8WRjQhIks;0t!;Tlq*>h6M;bwL6N7K7IgLequ*hRu5rW?QTRn*iJ`wZfib}dW!BjUuv%|Z!U&wzxp3rP%R5KWUz;I#r=z_{pA|a zcR`l#du+%~6I*4QW+^!TO&-MYaZQUSAOO~~Bc*>n+-@E8Hmuqm(eVbRY-woONO%F( zvAcHK~x0|8h_xa(Y2k<>ajTqy|4Kk`sv4_eV{2FG$u>6eV zf3qPXD`1OJWQpcb#@GvTfmqoS_pskfgs&?zKAM~0hana<=7;-lG376|{R=+{@_2E= zR`~|a*Zr&0Ger4$pi>;dXC5U!lhE-_q!yTpI=^<9W{$(HmbzNO>CEEQ4kgU13I>pi zGj(ZSm7opADliCyF@0xI6N6OB`Ol8KN0jf|DAOP2MYLVT8g%t%j76gaT!fX|Bj>@p z%Rdv-b7FhKpWA-Q=HuoOiu%_r6+%c^S*aE*7L<{tB4tP9=8Hl#ob6zPU%;74;Gv1` zGA8UfY{ae;6@E9Jizi8(>jJl&8^F3m$G1;v!7r|V+L8w{G!}};UH@5Xb$weoZ+Sksz+&fu^69o>rfWChCFts8e>iQ#I8Cqh@~EdO zw}Th+xOU`XxB%%Qw@c^CLR~1mp&e0&;bDh!we#5y^74C@gC;c23;k|e;y?3EqQ;<+ z3hXUwEEoZo!RNy>{y_iQz?&5^jKtAmtsjZs(!T`CYIRSPJXuYCT_2mzvqkda0)jzx z>^mfc>Xp>L3#t%bm^kU|djtlmVhskR>2nIRbfY=#r!R03PhFh?3ImPKXZv z-i;kF<8v)u3u{AXt*94sBiFnSJ9$EH)TW0j$ZUZGLt`Z&avuDdvXZ5MF?x93%9I<0MwtiQ<^YpAI&hl&0ojc+^z?-Qd{;< zoKl>Je(*4QaW7iY^p9dz41q@%{`3fkCJuSI+8kbSeQP~$x#cps!5)++)$(+`YSKNP zbalei>m=A@(Cz+9!#mXG04#XB;c@KH>Qi@1=P7d3Q9#6$y{xzP;C^B0_@q5MhQ4sj z-*j5i3GSfPs2easkjH=_FWpd5XfT$cXFgD|Qn-*ewv?w~EgUzORlunOx+E5;6ai%- z80fxN6U@^CKn>0OrfDIuq-)6UA>?0rOe0awvcX(lBIZnC(bznNgW)jo$1m_Hw{X`# z0ok1V>14`SPvK2Fm3lX|HrZ5nKPX0jWQHTAcND~$<4YHfd89`ZwY zF32Ox=Mhx{WyTm_P?A;r=3{F$glBC-Fn9PpH`of1)v~jhbU%7ly=gV%$Yw<%PBpxi z)X;WaK{ADf4+HjkAh<5;RF5Q=`}3wTg@X>`76W6)Ps`nqf$?hU&lqxDXNsW%qIFvt zbe5|%F5f#Rk}yGnN6$v7#`nER?B79A>Co{rc3u)>SHA<^ z`f@l`@@@S0f6MW+j;<;xDak1B8%A+L<%>tNc~F7(4uqk~6c`^stK|GFyjKi{C9hNE zLMh}#ndROMB~2setR0z0U|-i)j48D&3{dcjqMEziAB~S3s*ua(`>U>4dm&%gtau^+jv?uUGQ)lF&&K_e3Q3MM)LfQvuOmd z`E15@b05xmyM=xpaFpI`&Jqi^abWA%qVshj zkVyw{QhYWdvKB4MOxO{8;0w{y4y+1VYkMqFtUF$s6g+$xhL648paXf7n4wYE{D|Z3PU}W%DDbj*z+f*ze5Uw(FbG@2<)M zfj9#m^LMIdlhO7@{F|O+?yv2R4+kRR1-Z>3z0vBjpH45}(&=oIDKv$NTz?AU!3tzV z$Ja{-uArH>N)`P%VNFe^vk+1Vk$A3pdTQ15yjZ9BKRx3aRsgC>hh^eL(g)tk2}j*; z2lTc-pxEt@i^?Wfyw^_q!r<`& zFroTh;J<=3^!&4(v5o!%%bWo1i~veKbg#_Zzj zBJs<&Lfgw^zrHYQ^N9lGWkCc|SbEyZs;GkUi6VyAVx|r^L$0PGU`@bk;nQA$%XO2{ zp|x>kNsfH$Xb`S=u~zuP!c*TwMCVOf^t&({31F%;O$tTT63P z5GV9v$?|^rBTyv=Xijj|=`sYWc2qBqm(jnz3^nYu0i7@_F_fv4jUPaVURhOnnA8ux zZeY!pl2MzQnu^8kTv4huAQM1Q5Ua$uQcysaf8bE2(8d%i9m@+iZos1%TVQiKQC@dH ztrU}TndW_juxT3qmtwaRB!J!jY)W^nrHSKUGU5BxZwNUvGdzCC`Xhd1f__ko|7b7J zYxgTIe(!b*=DaeNBaYyWGjbdzZv;Ol`vBu0MfJ|7^K9hhi!58dE(Up?54i{Uh(om5BCZ zvi0$VG-8)DCQrHK@;5FeB#q`4uDk7E0+@~cCHS`8)6L(BJUGm12f0X2jOPk8b`Djm zP1%0cA7cduQ7;K+-DDA3{16!3+h@kXihEb$rEOVQ>Xcx2KuaP{WZFcL&&%)ZU!SRa zE!84tKWI+Lk{7gBTkq~14aHOh=bXcg5^tbZQLkqeR6hVXR68jGDx0Cm9V^r%rqMh% zp|crbECfI=*4G_8jv70?P^R}Eznw>jLxAQ^@Y25-(c9|R8DDU6Ps~&}CWNMCz_%7U zuL_s_#O>}oz4+An>^WJ4ckr)jAtp4&WQ}HO#Ie>qn0Uv177n-9LmgnhJHp=czG7kv z1%VqjTcs$~kM7h8c1v2r0g5Iz1->hx3!+Yk4aLFxwf?Y?#x%mTTweO+4=*=F-H(4isr~n5t1)~!um#s%>0#8 z>m*Yuo!lyYq7Wx%`)l(H;!NmFOACj@+F{<-I&Jg1fmrzBrrtnxK|#TT`K{L4{`aHM zF$FN!AZWL3Kj@4F)n0(TjZ&hnW~a2<&pN-6$<8?`V2 z&dI0$l&<njxn!P+*V_g^=K^ct*F{^;_ZA0xVi#M;U#s?hoQJWWqzFyj@gwbyQ@xSPfj*bJtovsxq(r~IWZAFglv!GBXKQfU-r z+*edmN++kt9BcfSqimH1?B-n&#CcH8LUL3`PG-{_%6V%)8>@}9dmsQ4EB`#lZx(A5 z6e31L349MHt^{$&XR`B(zs2kZZXexaJBn!bkH?qMBOb@9iJw@%vUeg5-9(uze4)1N zuKaC=J^bLahL4wOqC08)0>la>akm?Xm0fNwVNN%Jp6q_NWXhuZr3Cw#qtX?bxjzSLAVEdQ81Hw{a77?rAfpDkIM z8>aEe*BeiYIoaYm(A{oN=SX<}j9L8G4`qoH~d&fjoWN_9RJY z{gsI^)(fY=k~!pDHr0$RhOm}0o`U_84dr-qF9Lt029F0b0lK1tFNmJ{=y;}r)f3as ziE1)cW!FEQENY-MZ1+3q&g3T4<4og-p;A?sGdK#WkR9}1agc3{pOKarc2~BR#1?>l zimMPcVBZ~!+L&dUc*@eH)lBs~PP#71(tC}+T?~j_(1Zw&Cd2H)Rj>Fu%oK9f0lxA4 z#I>ZQTA$d^G&6)Wp)I2mPXnlOSlO(Q+X(RGABs;vA7%y)n8w#GptZWE4QN0ajiHHU zABLu@sWlh;u(^l4i;Rp6f+97u7a!8qxoM(50Cd`Z8J!mh zT+Q1A-N7ewO|St=B9MGk&Ux2hP-5SK%_?OY_0*#Q)jo54%j90r=G zB{uWCztCsR7g;#p_8om=N9!_xvX~H^I8_4ncK50GTDo@7j)3q+?!=qS>aM?jjtpqQ zbkL8ufWW&hxV1QPbNzyPZ!ghQ#3iy?<^8@anNEbGvx-8ZSR7l{D7wmvJkdmCoMoTz zdy6DVFH+TG38O01DnL|!31;MHhazEHH^vS#4ClOAV_@hPI-iAo+(eUed=rM_ShQe% z!nnW^Tk}-1IkwS2YDhnWP%cHmI5K_~W6}~@;(2>NvZxOWL}Z6&vz4C;?d%o4;vrX> z-$L7~DPnI9X1lpf$=6@edNvx}wN#zSRV%*_KQzGY(^VIs4+nIBA*oi-_4Yy!?Cm;m zDdi9CYNM9mlSxMGwkp^=2NHRmVtm0r`?OMB<(Clx+F2=jO^{ZVAUf^%m2W=KMM$r3k{Ji?Zy#kSc_X!_}$98nXz9u%6iSZm1`43htNf%>++t zUa<4?LLeP6tD+f#q7KLz!j<*nxW>#ZWw#1|f7x}9;knMrmxBKxx%dq$xWB*DmK*3% z(cCk7gu#_d&6Uz-yxqPKR|1qKy9ofgv z!ItJfxX+dPk-``<=$4%#hyKHPE(Lzb-mSZdnTB4+5v zHKQjXzocm@_`|tgd94~qpK)d6wjC!g1P=(uH3m$_oez@Ximp&4ibdu%MW6G)#P%8~ z?U$t#4F?liP3{j$1~JSEX<$u(urR*!M6c(^7LP5I>Lp!tkKa?vNkze#ld#uaWN z$$Yi_wY#XY#`nVvcRASVZ4xi%0t&|H-2Gv@dHgkk*owX@HSC6oaOfO0pli4tTdndd zWDuPvcRP#?f5WKR?UYktTqI_XbyDue_h)~*+wun5jNegl+7%O z(j$0!R*O_sRH7KHU_l4uSMtKdw58G}Q*OsEL2+jQ0%V*hvo4~1VNzsaW>=WWTSP{* zpY){2U;U!tBg8097aN9>{SG*B70@&QRX)lRft<1j>7`||NUwqQvge`u-Y3kpI|@Z~ zKg*L{=o)ESjUkKVB zg~^*ROqd$fvH7hHV#ZGAA`wse+Q>y8i&kryJmOo`CZRDHCm$1Q2+?qF*h<$zRVi&C z--t#gCdI|KBye8W{3MGXyGL`v5a`a5jD@i#el{E`7P~KRJKWml`j{sEbwXs zi=g0Fb)T-l|IH_-I6y}o@VNbbMbhu#sBi!Do3e(_m;uY1OYLu|j znBK4wqwWtt3fUg(Y9=0#GPw*8E~Wi_EQizxz)y9`4F#v{OJzG50N~r+{gkw=59?AP zOTJq@SKq!Wytxd;utHe=797}veUAssMsts^*kPw2lk zbza{Jc4GgC(ofaI$luy8O05#xYEtTP01oc@mZZjg-WO2Rw%0Qp*i5y8piQALD}UQ@ znI5g<-Bz5;)qZ2fg05nq&`H(0!6q2;?5*cPjzd(qE~0hcxt$$4{rQpR;BbQ0I>ULq zE*0ii6AL9wZ}gL3(oiLd1(QS^vO-uLi$*5hKP=owV=XXbN;rOMbVbjpR5mg9eyZTz z+=6+* z8zJk2cR=d?STjs|@J16h`!0{hJh}-NAucq{;n&Ik0qsB%ze%DNsoYzPpXR0`G9uFS zN3~ylxPmx|FbFS0^m(L2_)2tMe!>-I$h7*9A7_6*6W&fg5-!3?hlabu*v?Qx;M88# zXRbT1NqJd0(mY!+`-2Q~UcylQL~Tu`hA@Sb`k}6w|6bB|_N$N(#4w1rbKaUuL>!BG zACy$+9;h(;>NwqZ!c8Q$h}T}kLt4+Y!X3tyK!ijt1uI>vv3zMBqKHkXi`Sg>M6mtp zf1RR|&^0C*T{x*s^Cl_)=xExK5}m|ztI0-;Z(P+ibgrkasU}?) z&DC~c5{#*E^P4-1{6w5>aenK5oEfqJ^JhIqZ54gziLIVFI!3tVbJG1(%yaVh=Oojo zM|A9uL6K32U`}d114D_MCg-v4JnPi=%9t#^jNv-ISX7EGV>{x;p9^uo$WRQdd=oMG zS%~kk9}?K-5_zrF{vlYzP#hagQulDiCcF6HB$Ou(fvf9exVNvMA6f}A*~ZmnC0NCP z>#Isju(7-hy}~2VpHTq%@f!E#)Pa4|!|Pb-;XSZ!9nGit9AwrFQK{W{?W@g@h4fD= zU^@#qFJs>h=Xr$d9^{lwT+8@8U;@X0DCCBFd=`F~Ta0gJ=i|wT7@8v?oY&ZgRh!DO zaCtGp8NhesXj(tm{@HVQZ&dbTZ2DM)azKSfiiWCuhVzK1bR&YqGtym2>|7{hJMz^Y zPFhwgO?%woOwCn^Jl=~vXcA6f|5bmiEtOS$H;m_CrG5llyqT&VfA#Ow7PkES^UpZ* zt2?kFG^6Rw*-(HRMxTZKrc7#j8NcBp4W;AFrWcJKtfDA-Q0b+pZbaHsKRVjPuerd) zbSC~~6~pCFmZzcPX5zBHc>gvXt9`W1&1vWRz5aSzVvBQ{YGX6YEw|r>Uq&)^U<-+^ zn~04xVS4dr6lSB$Rpm!h$}j3tE^D+`I49v zMSV@UX;S~xT-gMosH=$PJgjDW8*E2*8I!rwta<=7(D^jI{!H`gFvd_z@e}jl<0IK= zD%R%1lSz_0KBc;E1@9ERtaj_Zi)7n$ee084wjr_Fr!azjpq$r%M>i-;>4~J!FeK?i z4@Jl$c}jbZWN>0&iIAorGq9V++E1~ zDIfV2-OTSc{m0+u{SQ^Aq0Tz}uDU6lP5x7^0zfmf$Ujx9%sgnFit_LV@o*hctQKu> z%695=9-_`%>-EwzFP^HoUY226=C(@ipqEoA=X&UBe{M1_C>^6^hwSOMURn~Z4;CF) zyDerrH<&Vm4$vacCs2mA2P)P!{ibNnCYg5We1b*i;GCCPUsaE@kF&elq-8VlkIFMY zI5X$RN;J}?H^o*Hkh{SxT-Ro?WSy%Pti8DU;DCr52r%5w@`Jd-I6Tc z7kViO-)KG(^kUWvTATGk))Q0PRKCjAi%@fsGEEvC$4%ys=FWZ&;ZPg2kIIZ;`7wOa zYpxf*2!ZZL)v5iRdYtl9Y*pJ0K5DbsruEuL(KS*3F=S11PR=pfUz7GVcva2SCLQDS zsT%cio6f-;q`9Q344u2mQU5b<5*4h?Rofk0O}$n7XnXB?obq(Ij?s4Yd(F+caBlG6 zI)gE(V zYxRdj5;h%|$Lp3sa#Fy3wVy-2Iror^=c_%Ytt{JV7f&a!Y|f$TH)nzMEGp0z)gwHF zyP3EwoK&vLa>~~_os@9bF?z13e)T(%G|kn=MM9}PGx!Ks?pC9&0C3!(ZKfX9wTUA; zb#Khr$~t(Q)_<}INaes9YODPHf$NDwsrvI~#!VYcLbRT9?rJ7e7ndbEr()u!4;kG9 zB9&?vgVvgQgt^W|7>Ot@=QAOP^Hjc<1~CsQ&!H(VA52+1hX2hU%~ggo*Rq+ozJkZ;S~%Ct z*~e*<&WUaCamq0h>o@mC)iJ7<|Gg})SQ9s!k1LfEn7H7S7hJdMG-YwF<=|_{U*>qN zu3F!vo;;@7qpb^jV*8QEBzD?ZeH|F|12gBRIg_eQfsKLR+^SBhuY28qlHhIp8A4Bz z2_2rt(xPLrrEDMeeGgKjenO}4RhXT7sX0Xc3w-_YFFSoLDWH!<2(zavm^3|Al=z-OHt7A*LQ;a(R06+jqL_t)l=A|X2C@LsGQc5!W zKF>XPyUyL5KmRy51m?BZRsGg>^8l{eQ8SgPn@D1oHRpmJUXY((@v*cff~F`+x1(xi9k6RYX1)OQRzp26%ndn(ZgKi!92vd!MUEoKrh9{iXP-D zM=z(Uz9O@V%)=V2(C$Ro2^&Ko*m~#M>7wz!d1+{ygRyWIcBbzTH^N{@hxT=jakgt8 z2VcEp8^T$2J1=g{CBm5hO+Qf`rXFpm5*=eE{&xC^4mJDokB*HZA<=WPR{hepCo(ir zDRV6scO#K-L>eOz5Jw{??VNr~)E?7kIoG4t{N}RLPSqD!<`2^*u5;$XM(xlu#lb|! znEn`4o_;r0gq4Vj>Tu58xfa1?s7RfgNUXGNB7}yN@VG%Ff{U{J+jNZZ6A{z7id?C! zIxk&sZPVn`5yh1qs6EpMjI@AiyV@f1CUU9za#;TI9EK7hIqt=G-*|?s`iT~&8EGVi z^qK3ybvjPZi)hZxGnaMh+Uoe(8t}j>zo)Vzp5(F*_fKG*v^*h`A8QA7^Yc?|!X zGM#6SIacjaJ)BE)Z#iX}ZpPoj!YM;@{txVL4hs6iZQcl7`1UEfjoOX$4(*84q5VyJ z{XQK0%Zq9LnQL0@*Wtg${_T#>M?v^@#K*)TF;2;ARgC!)xVC*l-eI3D#pt*<%%nX^ zeTh`p=3-i>YuQia1L4E^%A>HM^60?vh0M=(>m`53{MmEa2VIza^r5)sk=qa+!JDO# z^3+}b#QA@_6g~U*2^^t*=a=dw{noFw%Vy!JyZ(X0jyuw<(2LOgWBr-w+wsKh599l< zzC#&(DB=?0aNo24!q`dsvTytM6J`MuCrub00wSLn|Kuz?b5SaPC;$4LPm!0MixVz5 z&EK@Dzt^048J5kXZwIAQx6UM#lX2M%S0Xtf$@r}KeUKuI)4F`v^jm&Ij_5kdw?;lR z#xa~~Z*6z{w`6)L;Y}`oD*4Qn&>zUTt~wX}fB(O4V$jH;*k}Bhs`GUG^Bnwi^>j`_ z=Vi{F=f>lW37B88)!Y61=@|I~$;22HCNpD%cK5f1?NFQK3!_>bpF8JTIA&$FUp_yY z8=o_hR?StHe4Z4`PJVxpanXqB--+c{`Hb;;iQsDAz_DCb`7%SvSIF@fQr+@X^7{x; z>rv%5~@wt+Zn9kog^V8fSA1?V; z$uCX*Z%GX2p*E^MzkeOg9>m1)i;`2g2wJz8P8du-s39FvF(9dj@kf(C(zfC(q!(u4 znK%B6@e}t&TztGaw@L;d6@IGM)X%y#*E$JL*NFVbA2!E_ zOIS(*erN1=&fo>dKQM)Bs^hbc^N7ZDxFI@n)$ zEsOcFqrn#%gdv|VwMl&-xUZ?7sQlo5@9%4sRsGKIYiasORe#Ylq8EMF6so0P0w>ND zN~23m2Xv%wU)t6*lh*IVb56xV`oS%t@32OkYVd^o7}JU}jUTb@kzmo~686qL?acjs z+uz4kx6_9lOf~pK12W`On7}0I!p8Io^$VKY7W_OP6ZY8`NwM*~HzN(kalB8H%kz-T zK9tP;^?NHvnyzQRHWPgb^NFAC2VI8>T?^r)X9L&6%c-IeVWnBfT3UqY0a56cK)=zr zt~4};`29N*bCBC11qFrG_o)MEaM-awI2@EgO}D;$^w0{DG$0JZkKQ z^ZD8{3h}Wah-2F$X%j8nP>9Sg(-1^<~fU>Axk|B=oC?{f6f@8OQUnqM~ufn6Sb zc{4h4?X}k;drO8%eWjFA$`88svtRIk|M!2+E)C9qm^mBDVxU~4jgj<@E6u~};9g^Q z+WnF{FF{FG0hTjws)G9};k~jajP5dIr+s&4gTP1|s2ukq$kJ!nafa4%ot z=bwK*ZomC@tXQ!EXPmA>v?(5r>0$bsj zWBN?O?7v*!Vl``Ufe5l*`dc&8@%CG9pYY&Rn((9^6+O4Fo^093t#ua)kkxLwJUEEwLjDv>4S*G*W{jN z{^{86!x74X8hWltd==cDvxON3GT=%jDU9dGqS8>QUdB*E*H9Et+xDNAGEgupAICE^ z$9cE)M$gU>@XloU=*`6Oj6+wk6t0u>u@{f$Uf+QqeqDzRWA%>h+R%DuE zL>L{?=nzKDMKD!HG|PxFbH87l=wEFLs4Jpr#?LM1OWa~ORu%03bZzNc+C%xF19&st zK~-x{s)|X5JUhtSsG1HxfNJOIJOf_p!hdnjo383+bWZE7x8A}TXPjY3(%`{^O~QGt zlgynv7yItJud^$Z?&`3^4h#HNp3u>wN1LSd-+%voV7(TU`}DyFA2itylyg+c-$jaQ zODVN#C!c(>Ax_$+e5XereKb;2Q%xGJV~#n-)ZrZW+;h*FEDs`y%5%DT^JZt0nJdNC zfBy3yLqL@(>-5u4$2Z@6gJDC5Vfypa@%g8pVa$lpX3ELTrVB}^c|fT=5yV3eJrsim z4T5t%%AczItxBbpkdR>N+_-ULpiZUNnml>3$*bC@Pak~z@yCH}YTNIA_d9c((slj& z-~SG5%g)ZmvBw?@Wr#SCnvw0>X%euqO9V8MIF6>S&eWc0L?9ze?Q(y+(IU_k1f)TX zCsERen8^8VlQ!jx-oAhjL28w9*7EIJ`}JE-tF(W{B9b#;9`nX_|!Azu493$K2WjhAO;c@mCv7ymt4AYjQ#k%~b_!m2tUsQ( zzYj){h<(a%9xuPW1OK?AH;$Q_2+#M?Shbm13_>R$dd)EOFOI;2S9Qb5`$eH|K{Pr? z3?NZrh$UqWqQ-oe;)>km=3Q-+)>q}1Ck;TJ6e85-X|O>@YS1-;wkJo^CUaSX5NKyG zMM-NM%RbbRn$qqZ<=#7{CpDBY7|4;pL!zQFDmn)H(t37aTs#hoPsEY5>`bO5ZCp&O z+1{Jwc4xUgIH~;LN$a#N+Sssn)$+ExtzBu{S`@}me$j+<7r)lZXK`IB z?f;YLi*PA#8hve5kLq#Sqr9KN>8`Xqd-iOzSsK2jOP4ykA~!b|%a<#x5aC!KVX$yci5gQcXT#E=(}HxV+)v}w~!;`lQ9q3AdfLq`@b zUW^MaxByCAFM{>=zyBS7{No?-`RAWIyI|$Ym1g~AmtBVY@4p|v{PGJ#=9K^RzWeSo zxmEx9&wrZzm1EVB&p!JM=bUp67A{<9^0vPB-g{9qN6Nyb*yo{BWkGb;;v3yktRZFd(ZoUsajU>ah4rM}B z))|X(n%zqxHo*ilCvg&S|Y(G)dg$dDRE78e&o+BBJGm4rSf#_zLm#~pVV?bV}?K8icM7??JGTf}1okEZd=~K` zxaT!WTc$+jM#yG-{7`k#eI$21Kb}+g?5#C`Cb-5O%Yu$|Hn0pA2N1aUQB3WckL~pF z`NapfF>kuIFNUjNE0(2|;lo9Rh>xP57yZl>Y4qUww6Ok!SBNfD^4>b)BH z9kk$cQ)v>C$Z}fHq#IP97Vo*|9-}B2F=9lmEv@^rS&7?>88h(AGtc1t_up^bvuaB1 z6~EBC^d{a2(x~Yxh&Lj?3{d_pJK5V+C6ej$#z6dCRJ`qP)=4|0No2+m5vra&`L(l$ zBI0w(DW}w1ipb%bHET@WIXO82;wAzl6RWgkGMoPCPk+K=k39yFwe<9Ks1rKA7b8dd zpAcH#w{PD-T{3O%uI#tpez@(n+st9os;Se9#H(x(IEgfOB6T9X5|PbE9(lx&Q;|EF zkwrKM4jhOL8#b7IRej#YKs@t#`WWiSM{env8_x$8$%`3LN(BH(BEmTMfIfMyF?nrn=y z&1vJyFTV_FS43`PB0S-Q6O74AzDrkJafLDSU2@4KkU33+=hjno8e#KAN)Jfw0Mxr*QdRwg}ku--`Xp@%v_wR4^(s}FLq?L=Hljz#DYik`{|4%t- zOY2mhbm-8*U@bp24F-?~N(9CcEz1Ne?U@EBh`32(rcUXIv#j;OBJx+4i10~6){y8L zKJ?H-Mw2HKsr=uhZPW46pv|98=iw^3=%R~o;e{7MgGVmD_+s3B_uZI2eR{x2U1rp} zzIcWYACC3w`6XT@!nP@d(>dPGEdmySCLkccTQ9XStN5(s^Ul?oq@*dr3k_s@gBr>>$bh4IYm3jr zEFT#g5E8Qm6+M8rf8r?Q&LOzy&%F>$A{9ph*13>%lXSUAs45s{LASMmT_muSA_fRft6To%-HDM5T#lZcx)swe&Es;`+V ziR6A8O|(k&RZCpDw!=;`Z2|q;T#P_qY|0r+;pK#4lJiiSk;_TQo237%Rbf3pJ9X-0 z{MLj0rW@iia_f=8MD9C!=ga?Eii?(zW+iTNOxNU??9^bH&7=vr;f5Owu@HlmxlZQ2 zGtWHJII+tNCUPgUoqWSug}6EB>MksKl^x^-{|HkbeFAc?VEni&-gL*ahwuDFAY!D8 zD*r2K!Q_u6e=Es8`!F6H3Di69ykngG-+1EeI(KQwX3a7L zbIh1A#+>VDFZElbSblXPoiZuQFHUn2IkjJdK;$Q=!4uNtS+NLM1oj>RzRWZ#y5>=9 zM6H&W;U88|Ll$uupPe*mHC8yv*|+)=`lrp_#mkuCCi2(AHTMa)dLBiB!54TD{iVvc zV?@F_gz(lkVNV$5177;9bszyzOfkmuaw(>l-gF$v>DD?*c1}1)aBd^f3PG3TFg*6$ zc5KV6K=&@;=+z^_m^KIZkHpp2ZZu>}0|vsgS@c?3D9W>W%x2tlRS#^Xi8YL|-K0^A zAZc?f87Araib!}u2hyjDTB0^6t`L$W4NQ>ey%yb#R;LZ-XN^A$QR5nZDr45YKvG;> zV^d>(xVwz1xm#=k$yf~QEU3Ajzg!)Lk({#~!J zc2~=C`_VhKbI?afLtIA0O@8UJ1c?JVe&D;Rr>WbTmAKW{2*(^J4Uw~~q1$?y%Wq3s z5|JORyYk8_>no;Z*L(PtSNZ`zoxWs;;LRU*J#Y3ZHFEr|^dq>BgpPiD;Z&PO9L%pi z|CfpyB`xERKmG{$Unvfd&P5s|iHM5`-MQzUYi{bPQ>Q|q1|-^7zHOR&=q#(zn`fPM z7UZLL-F4R)KQ0j+bqMVjMs>P~i^O?AXj!8zWeWWx2N|gRty{O6!2t3bQwI{hGF?gz zKKNjx=~O)uksy)E+7euT@U|n5JQ8oe{We6ZMAW1?mG7MPmzGSE24{#=U4HrH=Fqdw zJRPsU_9}fs7zmhw8IL>ecw@rVMiF@V*cBEQ8WJc%_VB|G5gaBD=hwT4uG*?|)cH$u z=d@QCYrD2dW2kzAHIBjUw##iFf$%VyVW>4}&yvsQNPJ3rK7+=$y_}?N^d-#zw1$OtZukUXK$<;P$Qqmd!vV_k5!PlO-FHz2ybi@v7qonN_M158a zEp^0q!yD9c`L${4Em72>p}dr))s=AXlZzsH`p@~L2yw9-y~ucKd~6_$!D(0iQi|2< zN-=*iO{0v>mKYz3)Ls$jMzYqkdjzKJAB7DY%gi8x4?fMIsndhugQIZUH9g_lK;l?9 z7B0qwLpX_8StW06A2_TE2o8^e%^TX;U;>P~kMv=?iu0si$J*%$b1(It$8Gs!$Np z_%u~Sf<#Gi@#YK`FEwvvpL3eQC&kI!XL9gXvz(EUVMy1j zufA%ui@~Egv$C?x0EXZJ05uEXV~eqmc=wx$UIk{ZhgmWG=hCT-?AfDx)gT5v4YXae zs$~_%7&aV+#0NlmM2^H8*wbe!Kq-tjY ze5h)!COOO8B2{>hIy@~c9UVJ$VFKf1?Hv#iOpVMH$uuTgCfGJlJO*raM14ancw~DQjq-KO>@9sR@-vi1RwTT&C4?{iitL$)?KF@v<{Eh6?}f(7 zZDZA{RW(LrH&kshwLiUG>#-JtkQc@mT{FtYg|jhW$Uu1b0`++Iem{5dJ`N3GQ0$OO z2KCNIUd}q~*EI$a)S@RZ-i$=vvt|59-O;@VLt@0iOSV)LlK>BskXLf@RMN+b>Qd z-xWdklL%KCLqhn8dU!}V16xyj!skh%I}h>QN)SGRVOYamSjTW2J-YFul~M6ae(+&i zc1QZS^Ti(|r3IN7ybm>BL&Nah4|#a_sclH<7{=@ljPJ!P2Of?N)pIJPBa^oCJ+F0t zJhKzVjEp6z^ShSlIf7h1uM$}=pDU6)M%7J72t~!>3T#~8g=I91yPi=c#U+SOsP+CZ zCGPz{*|caRB_`=nxsz;ST-3F$Y6V0J(lI!E2}1N9suB;$=c0;zkn7_|H{;t4WE{D! z;c0x&5UWN=PGv_dbd5Lf1zA_5v=}v{5Bij@V*KBl=FYsbL@Ws%i*m?cZ`Y#tV_(-Q zbPd^xFu5}|LB3uQf^nx^ze`RbrOb&Zo@jil8{#tZ?U&L;lT4)=)F(?&@W90;G_%e_ z8t#d89=7Y1PRDol3jQ6utnJd0$k$CiSPGpX?NWWB&=b-qHC03$Yu6trD}-Ok36BJR z?zF(+%nV`epLW{lUy+iMVoaOTB8o@^OTdSWPY#~~HtOb3%pnbbH**pq^LX(er3Z~8 z{KiASZd5OeJT{__>R5i~pl=6W-r{tAt#WQiT{W?brCL9>8^3xublyC)cw<+89r8h^ zLiiwcnf9^Gnv1k?og=|*tZ&)utEUF1u1rT|oH=~_LRPqyDiINrwo;Y)@~Q3g_`k$l z4C)I;LYGK07!b1`zjR|6;F2-qQd3g{oAz|Urx;F*MjtFL;ioS=okt6c$ZPFB9#*&! z6|udWy=G}l^)RhnU$a-#V1bPI?pccE?*jum^F`W!C@LASvo%E@S~A%;7Ml?IR>f}m zlwG(FUnpJ__8bdubj=Txrk+nY<@^e13IlB9Kq@0z3HmRL#QVY?uuN5cng?YlCR20mQ5{q zPJS#_%}c{@mJy952>n|KR_}15U#bh8vo&c5LY&TSdY+QGU{Kd~c@Jt_%(igqw-fV5lm6^mwO zjW|aB8pz-htmB>sx8Uc6h4{;bU2*7SJ}-l$oS{BmdpC>EI3Iq02(u0FapqYXg3@A= zOIJK&ov}^Sm~k!KjNukinUAOtMhx&qpgcMiox22|Lr}rB7E3q?LVc%8m!RiW@IEw2 zRz&ooa+0A!D(HUG!cxQ!kj|Y`__51pe~|d735!dajDE?Z18c(z)*(8J8D5G=%&Me3 zX%HrL*dLV~eJY8{>VnYBbqM#Y<*(qUzS&&kk3x;aCm(>6l!<1&KCtDpeaN9x5t)(9 z=|@Hi;f0X!`XwZcL#OWhb5VVyVF}Oghmg_?L=>(@Xvww)TC;O`aa(ua)Edkmf|pgg ztYUhe%C}7>R&kq_AZ@BkJ+;|IEYlIi0&_%P_Q7BGpM3I(*(CFyoB$O_Ra1Q;ZPov? zcO3w7ROSDhz25JV++BL_q>@4lkN|JisO!eNpCpu`ZqkZhks$VDCm~-%+TR}?r?bP>u4kOD=k{1uf zd8i2_Q|`ybO=BGvF75THk~MrMydHx(0hEDBAhyF+Exs5bpmM8t^EX0g zC=b{19S#_e>kbp9rwQLJ#xaNv1(uUZ`2L2bt(DZny;9#6BY%RD{;IXL;Kq4m{-IFK zI5tzpkFCJsaV5TWEXE4ZuKfrTSBr%hIA;=zu>d~}qH%3RCEjZUOmAwFmzPz^+wWJ& z)}4(i62OUzGUdc$GnDA^$$ys0KVLW?mz+09PF_rICcN&0BUW18f{;|La^D{cWjuVK zJ@xQ7a7r8H{vYj;1aQ`1YoOMeT%67vk|2u^VL(oXUX&UtYav(xw99YZ=-og*D#Ui!`iIFP^9%2Hd5n9R^a5hI^MLfE_ zc5)n(D`7~1aTuf}r;CR6O73_7cJ?_a8RO$1__fOeS4@XH6bBPljcz?MCQ529IC_Ie ztE}Wg(U@%Hm~nXiU?{YVz_b8K7f6d4DrqA|BPJ{Aa&ZZw5}SV-(2sNT|`(H|?NNLC-GW*L#%;!Ey(BG~B9xp&5euKm>!?=cXxnI-8h0O3urm_xk8UL%U4cP~@b>3de7uQ2eZVqoiU1o037 zIb_wwj{qW6@|KEs*QWplb^aV)fCOzDJlPL99_}S3s`tZxu^;Yux4nt?(3tGZ=IO1t zpDiznHsd@1BRh0prgYxwyP>RK^0_4eU;*MLYb^y>xRe?TAe~|*y#JnLs96y#U?1l+ zZ1py9fJS#<9k6HW2&J54#%ydpr}8soeQHk0wC_hcUXpZj1gmEE8sp2X@@ie zKzDqem@dUTUIz!OT%P^k0U0wY0hX4-WzXIw`QVcpx#g#MV9a@B=F}7!3IAsk0H`KU zYK6T50HB2^!d$(sUS9cYg#v!V5TW3tg_&~q?IQtF!ByJ_m5hyTa_p28S@>{iPB{VKqNGJu|LcI9Gzy;c;e(AWCsu3(h|7ge zn+_+wjCP-%kPcFqd?JJcxTsV5(ena8Vov zY3ai|zn6SZV>3kqXO%_>S{A-N_8?-#`Y9eyzX1#85tH$*41ju3KKc^xmPI8-V+gJ6 zLr@lEMLvm%KR zZhse(E&3vENN-dhybkEddGqq%pJ^YSU;D(Nfpex~m9N2IggB(fh|L0|C9VO}w8Y zU0oK$mHVJdg~_!Tk1RJ$79W}pZeE6Lf}gZ;qZ0rQ4dB@VR~)gZUtiHE#hYuT2|?AT z!Pr2{1Q>I|hJCabg3; zF}1)zyOIavodfuZL*J3C;EVN>@QrKWbn!>^7QJd0UhNuyy(xxTiIECr;QO*8f;8wb zYjxm$>R7-dY*p9`9QGltVrvVH8qhY+P>5O)-&U{BSLc+*wHF-aIFz4&aftC_5h{yR zs~=F?gYi$-My!MJP9xlA9sYx&)Xg4H)=5}}Q`tJU<7(VrGAA~Iv(#Ly^e~}#1JZHT zM6MhH%y*0MJoUDKvI7f5TcoZODmp_0uSEWsZVec#(Ms?IsPB4LvK@(o!*yrvRL2#b9nC?>^*J ze31?{Ae<2TxokBqVS%K$3g8BL3Gn&DKAT6;&Wh^`sOu?iv!+#TcEhdGE^+*fOa)r@1G~7{rgu1m9yw`YT zHMplxKEF`~u6CkUfTV)r76p8^ztJFh;A-syXxd-a3cWBiA4St*G6g=z(g1?u;q5;$ z(eNjh3T_mgz?L3pQ86&fD_i0CT8DLatc)5SuSAQdF3yBcvKIM#eVx4VUV*Zt95XTz z^)!N0r3txU1WCy#KMo5Pgp@>m9yxqYnw)~XQi=t|N^H|6tG@F#qJK*F$f2ptGUr49 zjKS?wOkt7XI)m+HJ1J>lTZQ7(H2{U{Q4Yj%8rtWnTMpH`X8FZy2V?=lO)c_j&_g={ z&v_4GQDR{LaM#v=vYX?jw6IzRjTtWS5V5b=-5~Qv0~AxIYv$G9%Aiaiss1GwzXHIf zZZFzPFpUjG#c^q956XqElq13Yl4npZ#Bu=HHQ>%GH5B{Q3NR#$f~XPw0+7>zk|Dw- z=x197z+DJ^&NUf}7Z2NjxSWdHWmzC<6iIUYfgYnRaq)qBkWAbMzYF@qqm;mo5`BOy zkQ40FEz?ptzG1v&?=X#(3r3Dx0BPw2UULG@`VM2AfE&1>P}9)cpaPOH+DIItZd-?n zQqWElxbXtos^v_Fr;!|VS1SN6z`9cui4FxFTnFu6)xp*7zHco*x+rzA2RIX>|LQTx z!paOI5(X|PL=V7#W}W>SRsq{twI{Ux_OC2KwxgwJ_mE$k1=(q zOXSAr2;1ZR_%5^)1D>Ht+|4!Yz(9q8vIPrR)l%{CVP$>*a&-T&Lm$xG;$T zY_0Ervsf#btHJ5vgTW=01$BZuo19dGR{#J(cH_5#m;FQ~^aB+wiW@{u6uCll3DE>j z6P>75gL70<-Ui3eZBTlry9f>40RRCYLE;=s25=1i#G_elDWytmNA3`*v9V2d>;@NV zO_Suk*eC=?=bkwF>rX2S{;Ke!yWX8(iT*SBp;F>!tRmKBMk=s^}d^Pm+U)2Wv z-BxhnywZq1AV8y4WfrU=r%XtcA-VAigmG)$i_T}0FEaN~UCumtfV#hu{mrs|Q@zYU z(DK}DyeA;)smXq_+W4JfA3rZ51hoA-T|DC#0SM#$Xu!?kz1B3K(VPk348pRs9C~=0 zb^t)&StZuUV1QIwmO^Dzeg3e70P_oo6>ccBff<quP zwSjX1W_LOB zHle-v`AEZfqs2gko;KV#L$02cOdx0Vs9+V7GA+j><_nc;>Bezmd5HkG1Hd&Sne`h- zq5UN=XTHGs7{+AA7aLkJ2V1Hjv(&+MJKb+@0ap6CDOnQho4^|o->x3qn1O*Doxm5R z`&ECrOCYBswhjXcMl1MeQWG)8r`kGQO{PP@-H_r!40c01f`En+41!cdZj;O`fRUCW zteCNdWt!r&K#U~hTsVBC9DWs!!q$rk(W?$P!T74d*0c`NS^-Mxu%e`&ra*#|Q1Mm* zq@eBiYFMsAmFgO+;ZLPebvUQDR;8tth<6OEqyRR&8z4q#o~I@mUEha#SBqzOJtk!@ zz(WJf{Np4UpeZ@q1LvOx@aRyM^}uGI)5Bqyi$wO=Ts2NnL__RW37w*{@>Zo=RCWMh zV-J1+78Q6d)irGjbTEhc9LQBk2Czthr}>l=Sd2kui6DePqaL7%0E42l(@)Bh8B+^^ zBa3DJQaqFRUF!Z`e5G9e1^p@J38Xe~!w9qpXwp$G0Sd*2HAqWQTq^1%_+dJQKYi_D zedO>Y0X%ZQ86q|^3Ymu?it}GViz;`C@aUy!}{}tXNZ{+Mf&86sj$#i(7JIGBRRRIRjAEw8_bE@=6d% z%ncxkXP*p^Gi)e)jG-RB3nu9RpZa$E)?pH_hWIZQI%wotfg`4+VnQ8)Ei5yIKZ;d} zdk+9`)!%JdYez_VSyuSsUb>;*Svj_A;3(y$2A!EM*P zxPL6lJWyL%y{AzQf$NY&E?!11RQVnMkQ0QT`aZ3R5VO+*@BQBWpW!7S!USRL*Rl`A zJ(++C4+^r4?tF|UaL|aHg{G)YV#27C-(^xlONS^U!Cxs<1xhzPONSw4~Z}n#)D-K z4iP6sf(}l!Rn-ImZECWl=W%jMMj;ThR2HCK+$TTg)?$D*uN0OvOF>Z!CNwbc5uiC| z5Y~4@=agOOY37cTeJkQ&$t*e*MJ_Dx&gY@;^S|TZ# zXlpfoVE25nc^4c5*URCD9pd+3K=4vh+N?Mls>@WR{KL9&#(xqgP%%%D)P{d>j_u*N z8skGb)-0c_u92meZ-SwbI=4w1zZ(!X4s!* zJ$&sb;-Uc|`70rud`Ztw<9=9s9l# zpotu-K%FuI*#Ep#F28!X0Da1ka60t9Qst(rhaj+c=k{CMr=#zs91p4)Pl_x?{B$a7 zle>HXlU^}6S6UXv1^9@YagwA=K3+V>55ogtxk#_#dY0owhPbUB?*())4W)2!R`Bh5 zGQjm21}>+yQk#8^;=c-Twrlp|BL!!50K|>ZE0hF)F~$nbz=FzW1k?td#@4tmz$pW8 zsZSJ7))<=-eW$R>(Nac2JU$X_z~ZN+0p-I5Q3Do9O$~S-VQrcUj!P=UZ2)aj@Ud(} zGy?vvhQ1sZRSU)^Apm@x%!UY;dR*G*<5l72J;knrE+}61Re23^@!?p-rHw$_#yPhs z*p(!Rc!xk&s$>=V0Nkr=aPK;@*lkOo)vgyDDlf#HjzsHBHLl`btYin8tk=7^U$28J z4?Qp~9`8jU1#-9okC{G@3V{3&0K^_UIXsfkCVptEwBPpCLC)Fn}HJeeb?P`QA_O zl~>+;4Q)Ar9k2V{DCMvo3B`Qv4y)L2-9{K*v1Kd@~P8k*hBlEsy-Q zST6Y14mtklbh-S3Z29|RWy-G=@B89&2Pr_qd}n*`@ZSF&a3zoCx$ zx*Eyby;Y`88jgBvBn7&5DKSV_m4#1XB7n_o(S{u^-nV`rnaN+sRTt*SRTt;rd##g0 zXQrwi^5)@Le2DsnftA>Thu#bwMFcme4xKh|JOBa88GDM>hFPBfhK+if@0SXlnVD~lIfhzW^RhneWakD%; zw|lV5Yo-t6_VK*=OwBZ*Zuu>f!9e4TuLwncd3um`+`IX!^4TMk0k++t8 z1WT=sFZXM2zb_{oyTBE=ndO<=%P%@t_62QT(Xjnm~ zK@k@tB*j{V03n>%O~Mq90ayU_KnlP8H%|HX$z>rZA}1q04t~=F9|k2D&3J7zti+Wr z1_})%DuTyU03bq_2w-`T_$)aQDDy3+w=P}u$=5mys-U`4LD{n8`1z<5s zrcO*!jDs!P8&tmOQ<5bf;3PZCqW~LqdJ2k~p@S0#Me8`lK~m1M*)A%ZD;+Z5*4zie z(K!ec>5wqty!iYp(B+vV?>ze`L_83QLyWf#7IaD-4oOZv+w8eWzzUW zWd)}b@KmENRW=Lr^+AVYp2LrwPS8Q}o$}x~j&4uhzwu_=K#nA z+$f((S6?P!omgE5}%TaGw?i;S8J zSK!rdm6cE(I1-CK6@Y%ooXE5)(x8p4O2RipqCrGolM{Hd0yjIrZd#TGYrl;lgFlN? z-Tnk&?$EllIzP-z2XAtW)KOv&%+8r8Z=C(0WG83JlF^6D$AueYY{oD-ee43c>ysxW z4f+AMeeg&5@cchPrvd|-gqNSbq2DU!l{ID064eQ0GGZv*eshrMLvEUeIUDUTOz#SS9b+ zxkKhGT7vCfIpyRdZ4R!66JMMT;Hf-Dh&Qz}4_ls-g^iz-W6(I7HJ0AlcK=`VHQu*J@AHfSm zhWz=FU(3J#{USo3eT*RR<#Oj856c6;dJ;N8=!2*Jh>0APf(x{3^K!ZH8#l_jH7_BK zV7-bnNKxN6ue?#_&YUDKz4j>DAXI zJ3CvJEnB8MSKo2R9rDyuPx&KqFk}fG&aPa!QZBvpQWf%xp^c_4yhIZ5C49d8&uCYQ zY}~Xb{O&G*|Bb)%B`xHm}_yM_lq#`Rl{?%e=*BDG%uk+r~Jsk%~*F<)S}F zpDAr`|58TgZk4-!FhYrDsVMGU)hw-l2Vk3Yn8e<24Zs1wR5z#s4tSq@@=1Rj+inzP zlNA%MUnj`sGEQkTriy1=78J9~MgQ$enO2NglUSweXDkuV5?I#J4^%%VS}zi=YUHFO zEm5siiu1m9>-dJuPpn_x3O^<&=1Wx`z?v0+`&wO70Cp`v9ToIJ2n2=i77M~iP{j@y zKdhDISTv0s5GPaMC|4$&2^}qPTS+61k;yHuLGP_j@*!Ryx#t~qPO5f`Lm$`!{%dyX1D~(uj5UBlQ%Ud!Y%D87uI7aS0>RWPX?j%b6 zFs@G$8;>HoRtw7CD)XVQ zQ(oC3=bnGJ^4@;`eLs`8-+52Yx!@=A!MjgOb`BI@(Sohp_sf5te?v|>ae>@>_YVQ| zN@Os&Vt3yDb*Vn*WLb34&2sBCSIEhyEru8bt7C#T5dB$Ulvd3e2P?AVq!xg!HkmXj zQMPW&m$K3tdGw+Cyny=!5txL3QOwbuh0BP&N}mS6>y%OxhW#N z@4oxwvBw^hhaP%J#Xr6A#vA2{C!UZSZn#0FO`9hF{qKL(HOA_E^wCG5BUAz=5%hR! zYXI2C%HMwbb9vygXJprQoUa6!@W6@a#9?ygsY~R-Z`~qm)-9GRue?&y;p*a?bIwus z5vf1)gC=9osjlsfa-$qKUoSU8EKm;%(l?hr zq_`_QedBu%$Q=0Lx%7~eRVt1#Ww1KUg7|>kD3gBvqg$YOoglw|>?yhDH-A7#mWA@@ zZ|{-e!-gnB1sW^F$HysfHhJPWGaDRJE(aJGC%0U8r8@V`19C z2e44I4`%8Jq6`rzMSdej43$$(JsUtRRyJ%{E1Ng1mqE#A!M_haC^$4lgKdj1DTBKU zNkAa@5i)7SFqttDx;*9eQeKcRJ9g}mw_bTb794%3oN(MB^1}b#ln?|+jR zMi%hVqs=$y%Mb8M2IrR|uaZ!R_QQsuL49jr~0Vcc@w+;mtj&BoZ_8w2{I&J*2$ScwZ(uLS^G zul4l49dKfuR$prTKApf6pNSiABWQE`zU{^@Yu(}AKfg29`4w@BAK@7%+}`d#=_wkW z{MYkk%3m*#$xj-;$2Pnu-+Jdh|1qykdHPcMbU#@87Rt)Zko$lBLn+_2Mqd8s?TakGz=@pF%p z|Ge=Lz|3IisTp8m`mm{zQ0I{e!!u;`s3FnS8eD3g7TJ>mchaht7uS3WrLIyz*Bn>Ud>dr7c_0kBsB<7@qhA!vGVEa zHR`(RXhUa_X+R z<{B9?B*Y1-Q;jYsLgCWb4SX&|_RC8_?h{_y`en1NjWW50)^pZz8~9!DnOPRB zfyqNUrnvMgLYP^j#2z?qeG9!B107>z z+7LM}b9cV6PtgS((`p#l;@aT& zIaV4yWl##QSH^O+P|mM~8w9#q*oLq*q|H#|&jpujCLCwdS?7wCg>WZ8;FKx53uS7-BZM5TR}z>E}PZ#V@+$@yuycz`eF@-ej-ZO_Xu=m2(^j=-6mIg<$Ra&vQK!GZtA;+&rH{0%N1J)M3*n$ z`^i0W)KN#tgAYC^7him_Oq}QfxI-wjQ<6{yuu#%;D!6~0p!)B@EJTv~+ApjT<7N#g;!Z3xv_YPhv45yVD-t`<28plc+!`vh+n9}YjVpZyNT1y`t` z>W;!_)dR66VI|wPHxrUIWkvPOLkhUjDU1Oak{f-$oqL7nYZ8~ zNv8V}5&;L5)oEnWU&s!~C>4=DYM>J*%?jM?^xMql27*xYJhvQ6gMp|(?polnwY*UG zt)l1<@-*oXS0AL*|Ch9V(6KM>{7`G#N^C)_4yyzqT_Cx4aE6060HuWdj2 z`Tg?gsx=Cjt>3s=UVQCsS##}ud-6?r_rn7D@RM@c4zNZ~>G7}{q4~S|ldA#-6@OFryWq(A7%1@?k>f>R$o+T>z&1e0t`8L8 za$~8ZasPhdRRpjukbge+61>fq$#KVY>|53Tfcnln^Gv13_VLFbtIONBZ&%0Gd+o~p z_20h(?lI!!-FM$r1_?Aupl)L%DI~zc*h2?91$pk?AlLiF?FR$Uft-b&Y&0!G4{QNP z?IUp3HW*yCo&s&W4;nK-T(%8<7AL~L9l_h8Ny+l$@=7T_-a3d`55?F;BLoGwI;S?yOg8w%R1cvrM0}Q@dzk`ckUo1%&<{n z4^$)QGy&VQ;IyqS*(`ru@?#m5K1BVvfBEb8$kYL&W$Ex4vIM{37K^@uqGCDvl=GB6 z6@kv;g$v|+*I(74W3Rp98*}&%Gz-Uwo&klNBYT9jEHs zyy7jWDJ06&$rI$<%ddmuP=>FWEa#tnT8H*H9nc5dIhS4wN2@7{lXdS;ZiQES10+2D zyI(0o2)ZYs&$wUw^!rLj&#A1y^PT(Pc>~}9bd4D`Liwr-%)?`fC&x~ks{rqf*IWku zIjp_|D9}sWJ@?!rg@uKR>y`-7Fu9$#-FBOtbyoX|-bpv@>)*Uyo_q0if8WLq8m~B! zx888I;*fs!{_D_LjF;d2?srO$?$%pxRr>)=>iS_2F3!QA`xVN~tJsik34`>5Th`vY zhJ}h7_0~O5--xkogIW5ywKd#p&~M`+bUVbHi>HFyg$a8mB$g@g`}fSsYB_mYs!VGA zP~!FhNRk5vj+t^X!q}2E``|jSLEZ$nZ3!&7HG-^hr?|I(lQMu>P%;(jQ*sRPaL^1r zoPgk-z5@fvjiVKqHqysVK*@SOSo>&<0#SJDlv_7l3lIJt8IYB# zJj$m)SB#$HxupPa_Ow~D^t3-ftbn5yxF0wWC+8f0wzQUBWXsul^nrU-)y)XfT)e*& zZ5n263S4;%Lp(m4RWMS$`mZMyN3Eu|&hOsD%uJ(;(jA{E?$^-4xym5JyvFV0zyFo> zu+zT34=h&dIit(9W< zL;$jU8L|h3rGD)eQIwR7M5k&xFcEn~axd zik&+eV^z zl6rl8O$>2m_sw=!RLXIpt3ch!IKc|s+;rU7r!)j1Af@iLl?a}K5&MHbI(Q~o>ov}F z?XR+Ab$RCWM~K+C2RlW9o9_uS7U>ITIs=astm3Yic)VOO2CGIJBJdbD21wqPFtjs_ zOiZl`okXc#83w$P-3%g)7@QfIaPAtX;w~a3^i%M+iHWEKgh7o15x8h7_#eL2P>Ecj zK+qOS-ht3(9I8-7H}=ZqKt4b2+>KZ z8{6dkd9Vg0z#6&Gp{6dJHaSI9pyxjOs4vM$k-xj5(#&zHujK$1k8T#8cK7ZCl&m67 zvmfp9o9D=NEB?$pGDMZZ0H2wf;E_(ANlxuCu7>`XQ50B~jY^Y{6F6XE=w(oGK%)K8-~{#b-#(re*NqZBV3 z0w{xJhb=1M?@LbvLC!#kR;~&V+pSowed7S!tV!JWo!ihkUpQ$y=MH)%=)Jmwg`nm4 zlLWzx6S{3>)*JU3lsnpk&x(^1twh5emm<(F+1h?p3W3|{4DpqLMJquYDSJBf&+Znj zY@?(Ut&qbLH^|{*ljJ~cn+$*?fjYB_2of^Kh)g~YI#CgvicF3hE1`(Cuj5!c0klv7 zj?r5k|5CJ^Iut`E`jU|%>R^bXB3|H~2!*TwEigR+RU786P})bJ#6G6gGAR^pn~@T2 z=QH;KH@Ehf=|kNn4KNIItmzXluVzoRo;j%ZBlxu()cp{=@wA|#pRW++Y`vixTwrk5 zGGGe{fL31FCdCLdRfB;mBRxiie9FXDafzz{+PoO>lJIMn&IWJKmnH?{Y@!bw?&k+L zecQMa49tY`U>xcQ`?NXngq<L<>&>H)}t-wI^eY z1-RjvhDyA5dQc!aiEfRn;r&g74w5w-8}B0<+~A)162og-gi=R=TZkR0GhnsmlgHcT zp{tK3a@U%~12-5gExHsHk-LU`AOn-#&Pbh)S?_s}CCo0A=_M!OqS z8wLWh_XCVWSTv3}))2b|b_7FR?OP5|04@fDxAU1mA9#>fCTo1wq$-Oq6joq%b zgS^}R?9>NO$u;T?$CB5|Wp!(hzoHO#*as$=3y4UTZpI`f6} z`Wh^JYQXXbcMV+Q%IY>L+TNmqxu-#oV=TCcAIgfC@Me<1T9 zcNa{Ny~FA=NLjX*!@*+Vq_wTg{Un{Xc7D^^^YpmhRC`rDx))4TN~_a;PeIxk@q3^m zIHMJGH?&XTry?7lk;=LzNkQa+ID#_1lhyp%mKR>mQG3;nF=5aH|PSi!{MMe@1T2(ikqNHTX(slO9~t^T^n zk)w618(8wUL+$i9a_d-K;875;5SYB*9tXJ(g8Clr>27xoLA2|86O4NrMhiUk#Ta*> zOQcqTVPv!>rGCb?7ew~FExXH_<;AtNO2_u=bK7Ll>W4(epKfr+HvV1d&uuPhlHa{k zA=ez6iNI?BjO!kgc=*4ftD-%SCY(D5t5xc>^%t@ON#suT6>uYnaC^_)xN&xUN*5>8 zD{~?zLkMenj-_tgYyvma+S8iPZJ&CD)N>4ntm4r~bHr#}N{d&5_Aentfm^5#)ymFp zEfg(Rld2jR8|-M7Dsb6S;kskg@C3N@K$mhPuLTgOgFZ(*J~AvyqvX&709=ae=5X16 zIv8;4<`nNb`T84S?({I62xe94Ht3p7JQ%h??t>h;{!Y|kbERO{?J`pYicoS&rKY*Ky%t>B=?76U(dMcP&Mx%Znnr zUIRm~S>D=QC!33!0W&+G=~zDcnpT`{M6oo1BHXe#QZSIDfLnMYqN*$C7#+|eWt@O> z3P?uyTW82KH?CqpQx3Mn2=~^(cKac=w+$bG4%RW}>&9ATA-Z;By)+{xD0SCnO+&;2 zh#PdOvsGe+g)mb}uOi5p9%AiZ?qmRNZpI1b&C}_u>rm27j+^mt&Dw%&yIoh1@`Hg; zw-tP_qWjTx-6|@vX;+Ua_S@9e)l)$^ZlOFYayKZ}X#^L)q1@KsAW`#K-*vU2w5(kJ zI2My7@U2!S`QVmad32^^rXILi}DCxj@BbjeM1^B{~@C@04WDeavbDHLE+soia7r1Cof8-gH$ zVy6&b1Q8TLGEF2YE{M3r4rsD|y49ew*sc)8rEb727*JCBW)P?LG@0!%w>@3om#F-& z9B`|vtCRKX*N628X@DTD5M7X3_Df?P^gHq!Bt1D<^ctW3n(%s|N)r7OMi0b-wa3BxnAiW++->p8=^kC&c0a$~>=AIx%yC50H81~|h9H)d*2u=R9tU{LW zXprxo2rgEC5|i>ioPZFm9furhgar%qdq?1qGZSD3&nD8qEhxv$0oc)sx2r^sn;Thm z%k0vzKTK|z-StSFGwb-0bH^gpUDk5GQo!xdLl2ez{qKLWbLUQL9|Ya^?xq!2G4wTc z@g#jBt9I7Qu02hXeAXao%O59NB34IzNNo`1=I7_jm@#Aeuodl97w#*NtbqeMg_vqj z(Z1*5sxo7wc=vtMe$r;oGwyiNcYlCqKzVc^X~OGv)X>l%pMLtOELwy~$c1#or`2mi zvpmyaL8T2G=Te&UrB6QjM5aucA`Ho8M~fFP_MLWK@g5AiUv~G_>0A)~FGp@wr-J(M zM4%p?7a}c<3hBo4qc~V&^Qfyd~E z5O40SXpxJc&o(p@Z5wu^$ds>l)B&OxZtl%kdwwcoYamw51<0{lnDY9(?@0Wr2jx2_ zX!rqGaN;H4=}+Val(6LE}9T*mc*vid{cA0ZB70K@{H=t1$pM`qazo zD!Caiu*_gUiNLKFqzZm;80ENy{iMHAz%3ymLC!nxyzn{+7F^!t5Rb-Y0yu1ym=hA^ zvwQc;$;Tzi*N%hRhJ6?W#d(qqud=SCZh?=vPqhPb^n(7OYqI9^dKo=tv}9!kAAGdI zvqT#_Q@m>{9B*EOWu7+wY|%ffgdSmMt{8!gHt#Ik{MFUa+X1Mx2A8)@xND$z>4iQ7 z(uN%-+KBlfr0kresHjK?&V~;k-ua^S3_%U%URw&r<+07w4^IXGZmyPZ{p5B!xT$Fc z7p<*IwM^6EMfZT4LM|>sLrGe2vxTgLuuZPNw+r45W} z27o7LY#emazI9BN#3g~VJ@q1hms$3$;PV)Ml>IfHrKR4yo26;})6!5;Aeqz7HT15W zbM)>Nf}~)8gQ8Ruys>rc3lm&9>6>-uE%;EdKj4aMFVgn)j`Ao+OoY-E?hcL+ID-ecj+WRUiTSc-cx7jS0whkF72|7uX<1WaCvTh)(m#V_t{ zfxeoz*56WH2j`1iwklYH(kDjC!}ofd;n z!UGVq1eT(c27{|ae^%2jX%~%lwLRbj)r>t~R<2wrrwj&Hu0N3pz=E}j=?LBk@~ogC z)cnE9w_XpWOoBTraHAnbcPWl~ZpI>_Q&A>h>U5!(Pne=o>oe%(CLFBQMS)w8C#P3c zVev!fyP(tqQc&0`O<3Jd9-k~NtI{PtJ4gZ1%kDkEm1Pi}3ok$np_x+Hw<|3YzOsU? z)UEY)XEkgs$7SCcqyh(+;y0K42g9Xc4tRqB`u=c_P@WL2r4*7b-{LNyQ4tsS9@wft z+w-5-$WG{@(E;uWQ<7!mfH-^~HZYg&h{l{`1aS}N9S^vVgW}N1Fu|_zc5XT%4j=-6 zpg$bA3%-Tk38_vJGK3x)(D90-&x`P4GreX(TAr_E^&qP=*sW%Bh`h z1`4sb$Noa({zV)xNABi+@rz%`X{Vhg)2B~Yh1%g78kBwayWf=^J9bD;PL4eE&_j+o z%#mtY=(81S{M+CDCMTbKveFqc8*s%HSI8~5+#;{P{<=C(<^T8Idr#hZ=N)ylwZ8NaKrT$2B?2v=+e)?I9HN|bS;S^ZfNgr;C zoH$+PbxWSGcf;UD1a-O^cI7r1obHuL-LBab)o)J)0cH{BRzgx?|x)ZLs=bn3H z^yty@+H0>VBRJ;iX$lLtk)!s&0}m+B!iJIi=0%LB`Sa&PT>zFw-}#Qb{PN4{$9)e( zShS$a&CQj9f&%&JPk$==_U%*mR9RW6P%85i*j;wnWy)}1)22;5eb1qn46J>2G>}1|2k#p(uyQ^RAuwQyxmnssV%L<~p+o5h2uGX@x?T!?1bK)+}ZQQu_ z0mVFDR#t|%o~ zE@_qehBlcxAzA9z(`33oH1gnXgC8)tOn`pQi$j`7DC?7h8Zw0i@UiDW;XSj? z>7$LP1en;S$8VFimL_o9>g3E>sZa}o2y_CtZ2gt!&Vr&{H%G9MZab|hyGxM-ZgBwD zp^#Z8=hg1`Z?o3Lvg|~zbg0~)h@h>v0e%SgpOKLvGiJM9l<;ELtl%tM1N*;OS5vi%Ek+d{$#>1qrfLmbM$#6j1 z*k}M7UL&aEIsTfOn*7Pl^ZpaqA*YQ*jvU=1EM2-3VffPJ=9_O;=ljDSy|MzFpeBS2 zY!z)(i+sMJL52>A$3yZ+S$&2iV(g4k_jsrwFhQZrwQJYPQk45n+iHqxdaj6&FA|m&9 zp;y=+sJ6CNO0mL-#mXZXt$k&i9B56I1*tnELExXthN|0=WYs^`$?SxD84w2%RRGm^ zlVs&THUykC4$0x=N-5ZTGcvYZCM1^FmJxXJ^Ov_vMO)WUJKulmPtd2J8_h||m&};X z_sTq58aGKnD|DE;Nn8PvYaXm`x>=25c5<{9rEe3>w}|6ATm|<#F}{It^-DXX${PVL zien2z2FF*SpAE2NO~&_zsvuAP;(94)%9J{<@w{TSHW`~(22&Pe;hE@ZkxA+Mtyw$l zZ~drP8r$rL*^JnFIWB9Pq<9*ow$TeK(H7Z-;O)E1jPD>PEf%gk;$>3qX63nJI&l-8`rBC4SwHL|&^<~xpm#l^+4w`qX?y|GJcJvSj~zm(&-ax9D#@x6|z z+9%IE^NdnkDrkl)m$v64ReadxP*;q8H7R8?DI>N)s#-(jOq!V@wB=Y#o&8XW*v1am z5C?79vPC9Oo-Dg}?^gJSWwLyeOuX~lbI-{i|M*A6WxM|R>s5Ito_L~}Eaa{Ih!P~b zDJmthq#TzwZ;C_z`OkkUFmu5L7s#qrtJIBLcinaViRzo*{H82ixKL<~N;xuFk2l?P zlf3oTTWT=)+Si=f{}k2!_{TpE(z?i9_I~gjR<(uVeg6%FIBJs3TN~uMD+Y^Rp-Fb; zXc<0YgbW;rm1Q53a=;6@`q9T6)5k3_s$cW@=Q4WCn2;Rso^Z(7Dkh(I&EK#(w;$xS z)FE>Av~xt?{F3NZd7bYuC0EXxeqQJEIKzR09||p>-AFH7Cwjj9f*~;9322L;OyXgf(Y-Ur*@ncUAEoX|&mGDkc597%vpOo)??|b28j9_Z-sy{+3Xm}c4 zxnhM(J>(EcO*JO36Q;qte`-zyYqJzZF<#k5hzTGfwIN!`fB4~t>RGAFS>qwP908}d z>pSKFhs?Y0J)07YQ?rIIf=-bQ_+;Jd9+%p>26-F--K(I2F%Ds-#=}|eiBk~+8QeAk zImQHCzM~$I4_f8Q1(}kbgvHSO>*P{I@8~NcP*Q%}PC3QC3FM*-Jq-O+hW@f&P}yhf z+S&j7=Rb1t+24?qF-LT*0Bf#BI07$OYdzC(f6yVud(-i(|A>HX-MV#h(n%*7eGn=~ zsc5w!7AIFMQUs3YKOgv>az37rIsomNAE}bNR1O(0AAkI@((m#Y(2OykE^GRL?!No( zuxh>Z(o4k-*W7j2U2@e`S1I83#TQ@5pZ@eGRqnE7%jDd1&sAbmj{%JlrZ5kYKNGjxWp?B5MN9hp9J6rmUfgP(gQS3 z&`^>GA_9Yl(YaWPQJKccua~PLf9K-t&rl~MRO$_@PPaOOiRVJSkA9F&>4^D&Tc_p( z6Ek+oU~__wO(}w;F#$zs>{l9hFkYoi6b_pbj8hdm5Kk96E2fB)`fz&z^bsaaW2iKx zBgr?|BYFzPL56bUbFzDmd)^h<`kWM3wa7C7ZO2SVlDVUOTnB=@fs>UObYjXznV4$~ z_y=m+WMC@F8@>Pr34I6J9Q((5!PL`=gq5eYT$g=4R6_ulGSnqkMR|-7wA!r%r_(3W zb}h~nfx0I0U8K=KBtaXCRR#*aaV>phD6Q!IG5@3bSt8M$TZ?GkM4uF* zE0Z!gRMsd0p*Ad3Lx5oAx#^{uKr{_=RmCp%l4y8ieU9#_!C4*}EJgU&QGc^HP86tx zRH9!}Bz5|F=?3j^4XZXmo7oKF8j4W^i6G7da)H-EJ&p))3$?NbAAGO__bt?XeUVPD zf?i)UeieEyG0nL5c_6?6512_Z_|?g29j0F4x!$YWX`Iu3*$r>vo$cP!hc zh+Y_M2+n{5?8ybx*=Oj-XWja}tNl!o`vCx_a5!q=bP=sS%f67OS5`>@M3`xbuq1=4 zAcmP@jL_tGO^$%Oj!A9XMLxU}zC2)INwFb_u|(JJYm)c3)ypMEWXRwQOz@MIii|o= zUGMaV6FPpY)-}lS!;gnsknXOaZ?nWKJjUxVVBnsiQJDS$E2k40RvBw^> zy%vd+y$alDz;LjTR=Xd=8~znxl{_#;Ho$^)1TByK{`I3;o3&2eeM=Tlao(Tf&LYSExH(uv`QXkHV zOpxbvy_e3{dJ`eK2hfaCulImeMJ&>lcVT3VbQAhvd9Wzc8=+f*MUxg&Bt<1U+?#l1 z;&|w{m8GG<-Cgze!+Y+lCYckrb+8X?i|?8v7b+4!Dx4K*Z#ckp=?@d^26ofIcMzT1b};fQ-=q5^H+?5W@^VKT?Kpfy&?6i>cP z9hv}cT#J0Vt3kH6>M|lLPL7|P40j%gDGElEhej8(zSU(Tm%wI)bj){(lXy!b3}3n~ zG0>qaSfx8GUIXK761X`SfOKjz!H%8pu}%p)pNN=_8;ho%o1M7UYG}ZIbOUaY;sAEO z)v(Ug!QvjXSXgc>jLD57IZg8B^-!y{PZ!4EX13YBpvatc1EVgWE;5z(VkvHYY4!oRXmuqb zHAM#ZX6QUaZ!EBx6phyG3p^YAn7V7#`9_&EDsTf2J71P+Sb%OQK&;S3;JOR~Xd5sV zFQ*}X)T#e8aJ=Z%`($Wbo-74WQ^aS+n4*kyKnvCE89;BwdmX%i5lTS?c(#MOcXjB? zE&|-Pw2@LF1%@(xb8W}QnT84GS(8g;9=mO0p}T*#ELW5-P$^p+WCZQf5dkCemhs#M`zq7 zTw0q}LabN6zoX7k=_V-s&+gn{T95hwROgEUXUI`0Sp!1>xM5&EtKJvf6Qm|)%am9s ztRXh2;!-jDfi23wfZVn@K5mu2GaVK#ujFulOtsXH!Rl;1P9h@a1hKEwGcQ}j8cSpfWXZZJG)Harsb(4hr$KqgxXRkZeFMS1a7^+@p9Ao`|?CiU{}I@ zMyF2ZYk_xz5ejO-844tZQL4%l$r#;!h2Rci%AiCmP3Wj8e{A5El-GfK24jKs1&xxG z93ymko0Hy%?Ex8)g30;gyJ0wRg2>pDk5 zft4GU3eF(im^k=ObZk);z-XC|%SO-qbTdKAU6Ng#O%se6Dct`uj29^wa3jZ(fKV;` zVx*{bxsfkYf9*gQzxzO!BlP9y1l*z=w@5vWXgM}L5Cv{Fog%dm%g~NO+XwwNO^BGdy4aA80sxqvUfTL>q>S}$xo{#Pn(2WG;cEC+BHf#1eH;cfH9Ha#3T2Wj@g?p2zcpvz#P|pg$OA1#?Q%jqy z%4?KOMa@d=Ic->ie0^>@oZxz8e@&~3T(APVY1N2kaKe-nnXSJmQt&bKo@(U-a@$53 zx@|Jtxlb{_QS~UQ5P#zaVNQefsRfG0E88RV-_lr!=x-mE#gsv=ekme zu))C$)NfOqQ}u5=wc5<_>J#kEBaG5gyj%#jpz-{;u+E)yYf6xCe~!cuz1lcvBHW{#CpK$ zkIZQTSCyHQuoFNF6?D0_oZP?QWW+`7(dpRN2$%Ml?oom_xBsP}?o#Q-cExTl#E5p( z2A3XoE*Z?U6wj4yg448l2KS8shCldMqpJ=?VK?G35^PXs3L-s*v0Aaa0g(tAl<~mg zsmU^WAOgyBLWcfX4$7QBT{@rGvjW_>=RU8I&+{AQv>C0E7^?{_MsGL{fN{_?3&+pt zS@qz1Lar2nE4h-PPy#?kAQ8yXzZdU0DB)hR#&u4`Z*$OOh@IE@Kwak~ii1W!cEQL5 zY;IgS#c2Z&>`Af{X*;!vMhaX+SV$*aw*!BcG|}B?4|JyoqQEVn_h^`)0?V)4Bc54_ zvUX*Kj2aQIj1#;%R(es2SP%Gh*I1$xDB~q^3w&EU?u=Vrl`(`rjv~9jye{nwOxm4e zibHMhm4%u>wmTK{MP5$cr2thH4u#$TQK*i|-*QhHfm54>tK-yGmc#NWIwkFV702rp zxTFlH$?c@z8DalLJ7r+><|Pq;@Elr0a}Oft#t{7Fa*Q zSSY|PojQ7uIM@-np4o?Vc!nL4Q8z3Ya3kl+%%cj3K$cYPmtJTIqfvKa%-RJ>^xiX33 zP$!~uUNtp3rLmObR7SA#uA<2fnVq;R9TIi_z-iJ~+{IpML-+5(D1nn2b=ByqBfx){ zromTYYdI$5_HRpXtyG3UAysd>WS}y8{(Eq4+bA!rsYM8>M8x`RRyu68jk*j2U^{$q6yYt!bOdAM7MjKZZdF+I+pOsbQdo}JkJfAEJ~@^YiMo-UnAA)k3fQuO zHS4}vhPkC)T~CuOaKm%ABXZ{kO)+#Peb?I}ExNU^7wFN_}W0+9J3h#M!%H!+3|uEJ=9nbZj=nb&>W z$haN}DAeZA@m`gljRS|rrkp-IMX<)$o^|^>g8~{6AXca1!oYO~9h+&&-xP3&j(pxa zh?;D^HR`F6J4BZqyWICY%55?vOqr2MV-du@jGeFgcD+1ygM2 z28L*;;0D~3PFXjm3b(q=HkKM<8+&w$Ty>w~P)5LEq&?Sl0qBO96f>r|CsyuZi z%5e+qpy+X=d!QR%W2BqX56WYp!TK@;J8u$gaJ}r#H{33a91i`q=6)LMtsit-SDVlQ z1i1hw(~8p3WrrK+3$bz3b8-|-TFUTq|19TvWY>X<Elk<}03I|Bd%=~cT#MjwkkESNN}c)Z99AVe^v;3<8x(d9(ZDkIh>GjWnX^xK9@ z3my~D?fXdSqEQ5@K-+|~pp!LLea~?OHc8NRBZsXk+}SjU!{*&&IAx{CEZ|*rdN2L= zz&1vb2wKhiptv*`a3jcbBkBQaTk$Y3{@D@lzdPh4VzriOT9lgS2=?rNTVOgfeWbVK zq&4H6HYwOWWRCGYhhk~d+7}IG_UJa+1Ci{3C~&j%rr~(ELG<;8e%nTfN(K&ylMIBY z@s>sn6D(a7nOzIua@FZF`EurD6rV0A%F79! zI!jIX5ukBPtHNMJV}o^%iMO^`9{r$FzJQ|t@6XQ_t$e5GIfqDELOnQcG*$pOqM^as zKP#Oz0*X>td#>2sDES9kprZzd*lQmXIpTJM^9F0t*48!^|MSB605#-_G4>~)DgBj2 ziX1sd3SxGyrKjE%tJqVy;D89)je-F;zEk8HkwY3ttjkHiD=@9ov17kkG6bfv14>E{ zu9rZO5|`GtH_)yU{xBW$o7B*oVW#<7n9561g@OUht=8;#aa=mD|o|tor@E^Nk)pworp59 z*PfdetPF@7rF@J0Z(Xf24qzbmH82`DG$vnh$2{P;DI&nj`#_rNs^l{m2CPSvf`(>Y zro(N=Jm{|d<)bRO`e;q!*8N3f%GZ^PkCm_%9f3vGkW36|bkosG&q=k{0Zez`C3QDGpEV;g!yzs{28|fET2?pqI^i4os-^(I?AEw`?Os zp%6QEh4#D)aps5e(e`?9&)Vd2=%LYP+P_xU$gB|w;wj&OQX!rKY1r4m(fj!;2NYO4 zd0MKBvgoW8RkZ^A)yO$>03ugCp!~8is5{G)6mZ6ADcXzVWRXnp9dRNE3e@kTxRknt zc0|x&1tE56c#a&mKmbUvMeb!Jh(OEOY91@xy+D;6)@mNyZ z16t+_fRvn(!vP75mN+e3j$A#Em7S%1^bj;>3Vvi9y{|?H&SKZ(9CUZSleEIw2uCtG zF7G+>+K|9G&s;CuFjQ77mTtycdAhVrI=k!Zs=Kh*HOJod0Ht$(TAffc1mQ1-ZFNWl z;F43f2L;^(lRwpbGU#fyOkrx7g=NyC;Vk7V;kIOWie$HFTm)s>5CrES6K@t4-6;#GLKH1C~q{exrdQc0nzJD=Dp&VAT?dd6J;PR^o z1HiUI(+~~Kvv>0`tT^zHplMJj$*B1G#2RL*zYL@d?AmUm0P;N5hzH`!W4jr)Di|gT z(W4D9#V`_8`0Lbzb^D!HU2}js+Hl|tH=~-Q$X~x`qUfC91s0BOAp~Wa=oAxw8){NC zvh7v04Z$NX%^r1L!d8pvVckY-x=Ugt?Y?SeJx+6;8gQ1=Vm-AOR&(p5;bE4p@N5z4 za8n8}BAO_YVkfD}Z?`Y&b{#-kaJKzPP@GT{r3mhiY7~_#JcTdjd<;38Gzc{eMd@fI zHEm7`9!kK@bn3J-z<`VePReChVwZ3Fp^;18RNroD5t)E{4!-z32!gHF9fd(4(nt7s z!-JBP90psT=6%HW=J;M-wvuW@>TohsqfdDdRMN2xK3m~~BNqEizu*_g zUMahx&{=TR!HA2p{=Ao=plivaHxIbZE zEF8u$8*FfvJVu@w7hYZ5&bdg!Zq?D_vFmx{FV8t%*J`n{oturJ&}E1M)9U%RK>g{a z^Yd`|J4UwaC68cgqFof-8oEvRYzM~Q(VLdrBNuK++HK~WLrJ~E9l3J%J;~fbdryzZ zbbPk<4#B!LjVBvPrE77-cUFX;-*u_KcTSJGqHQ6J%a+;>z@~_Eb`Rv74__m$jMkbV zoCaVK*k7V^#e#Dr6t%>`M6OKjshWV0T#~^(E6=~KA#x!DLRMZoQb^mk;@k|n+DDQp zL6QVrvqq46K(DWu*{epn!l54E)RQ18BV{QtUO!0aCo7kXNdAC0qo4rUuXZBJ2@)Lf z@4?~SIe|jgItOZTF3cmTdTAtwVQFEg@T85`!p0u_qf_BqW3(A5#IV;-2VfKU=1b9L z^z2e@HaHC8g}OUQb%pfo>R`|>?x)sVj-@Q-diofN3ul&mqh1#w!e&(7nx1xhVQ6ER zsD9B7)`zQ;!MVmfo2dp04auAEorEV|Ej}{0A33XL$akP~9x5zX>R)kMc56JB5a<_R zYZm{wVsD_}WDX{zdfe|k=rQa(nl+Z8Hp&5Ja4kK{G{;Sd+2-%B`~jA@S_n8>U#N$+ zInx(==ulj_2)PU*Ve*nqv@#~!HV8^QcA!dN1^EqvG9 z7hraWq<-OP7mq!pP=Q!{8WGpYxDU&WQ#5=*7Zw%&n&P=Mn2AF3(zo)zK#Vj&m8hBiR6M_Om^<$yqng00V*+rI)T? zMil;Qyg*|yq`p#4a{C~$cd+{Q`*%y(W1XD%h#gDsm{YuAlBg8l#lcNssu}CU z^clMqDWGm&qL%fnYC2oR=EQR*e?FMXqP-?hd>>_7U6{!yy7gFeC^CdWNkDq%*??v* zQP$yBh2UhvWyj&)WV3G0KP|O{H9yheRI>RN=-lKDd5g<&?rEpH^&9&9wu@CXGM8rx z{vRlHW`qlvBR~P_m7h^c_*f&oH8Sv;lYAXzE&IrwW8%@XpHXXZ>Qn>*4tv10n(anIaa5RMJ(~!Z+_r!F`{+Hbzf6qx88V)5Y&N~qjRW*5Y_Gbp18UOkr=dQ_s1`oDVqNw3(>jWh)RUcAcetzmL42jLolJkkzbz9k^RM z3?E|%52;Mxg3~z8kF>dpbQjTgNmsF|SJrkm$yIa0J3Ew&e!)RxTx$Z^B$KquMx>%8 zinMH*xq)yloqt*TB8vmCTA^Ag6B-`1iNRDG4Ht4HFrPTqxfF3|ec*>?`!0jda=+>t zZb0&t!lu9LGSe1(kKJ%)MBc8_wjCCqT4s)WfHk{7N=8DR_U0opdWy}C1P_koro%SvMDryDR(aR`$6k@-=H9Zi{Z3BX&t3I-ptibrDAs zLlxoj15clT14dwSP(@WjgJA_s13Q)`_;dq2ieFQ}F;!k?5dgZH45NK8R~PsH3~nB` z4fc5jKi*3F*3~|+uw!zc`^^-|TLVcb^%tW-JRx(-=92b8PM13RhCWHAU8+%=n{B0o zUbEYJ4~@9=otWmxPjxpv?=f8uY0<1ap{!j*c)7LMzyRcC2hNZRrDOe%?07>W^6;$G zovj1~KRAf0TRT~=L2w@G46YiO-PTIFSD2w6PPH$n}FMNC&r_k%V^`g$6+0ut-ll<7Q3W|G5B37 zSOB}6gTWV~S=_GZl^YlDpI{78w8)01iYAhrK!!WGGijj)pej*t3~j7Ztz8!oK@Rb< z%LU6qQ(4wJp?g{)$iJSn3=q3Rb+uYrMt;1}Qz#3voRv&8VoG`){iBNK`A{vV|=ow8bZ3%VEW3uo5`xWHG(zX>{ zAZPuTcU*uSwGd7EUc#xbcO*<-NK<&xy3pP6EJC0-l{bPcN{EzRN{^GrPKsODq~mv^ z!RlvjUYBYS<3WXosg5|6YpCGhr8*nQvOO%x+pqCF_mS#=Nz>r)Ifqw~g^{bizC^`g zZm~a9>UZuQ*6swxB$es`&sy2Ce=XE3nTnw;gA#ZKBQ5tN(hDn#jdSj*dti(q@jP}2 z1C9CD=!nk)Ij~pA119LYKUF2&W0nLuUZC(jLCSAXCb-{^{+2+2i!%|7J0)i{e z>r-mg;q5P0`vuu&3rHWmrY}wDlg*sMIu^};VzsiDzW`R|wRAQEsC2B$t3H%^G&__6zd++-*1Zo2 z@Ss^g-@b%qCA89&K;&A?8}Ow{B^UQFh{5;ZCl}Cj2N{Y}AU`Mpjs?`0l3{2M=>QYJ zGPkiighWV2U+e@#$R`{}XPjXz;>zQ6Z-EXPk_svHxH>mL<1t0u zf;sIcAw16TQKPqNBlu6VBNC=;I=%3l7C3XJRity`oeNA-Yi^TU+QO9m=j-AGb2lq- zG$`T#{)aO(hHXbXq>~BylSQ35`b5u(O3Mh~>Xvy|yR&_{38n-~ zY{EyT?>~;&Dr{2)R>rQi*&7=&X$0z{R zUs*Ul1%WPIWDLy)lu>;8nnRC#slItsnSI|r_GWigjP|ELS?C-d9E8ACiUO_* ztDR<>D+3RxX~6Na{c&bml(5{$6~t*f{??H0_HtZkKAj`%2r{9bIa{ukjHW+Lw(EVF zrrdo>tVAvbeqK1P>ayDEJyX3bmbx7u_X3ax|BBb1=3}@(SxUzGkCDsXoUp%}3+67F zq}biqNlG%!&n?uNBgaxmRg|rZOB=CQ><@D|>9AbDEF2;1Dma%=UX?4_(#a z*3J8vEvFT&M<8?ee3cE-D$~jDEyo3^W;1!>Ci3c;)GJ@7y(Dr?W8l`lHj8Ec3iR6@ zW5F-H)F&2F$Sk(}$_gk`7aqo`oVsBEo~bblgy$-t>&z^LvXH%<>he^6mm zEwte~ng07nppY*AqO_HQ5IPxec~C+me%gNh0*6VqV-m{Av2K>!%7f2N7XXz+Gc`~q zE9G}kf9Hu#68NxRT0PYsc`#v&Kd8{@#SMEXnaLGhZ;gSvr|fIGRb(8^q+EdAi;mSHxv9LD*gaDI^xP^B5a# zOn0~5vs%gFHh-g5nW~-Sm9$(a%Vyhf?Tv042d;#6*v~~W?I(Cv*^^!3dj@lEc=#rG zHq(nrh*jaCJZvWH!Paj7{4k8!jbg1}ToOH@IAO^0KIq$aqo*fdHuF+k{i>6IcWPYy zk!Wl|HN&D@jlMBQY;HOat8JNWX}~WgAjl)8tkt0NN2dDsI|oi%D;RVIj&!EFmG^zB z^2_V6XCQB#^`o%=%F>l;z>P$p;z631V6+`^=Dq-tEIhL(z zz4{@ep`s%Cb_xsGEWL%&$l1S(#D@aYRx$L z&!gxvJH=_VH1X6*L8ts47BmAqHSN*ZUo$mziTggLO_bE488;_mkIZ7FGBEZ*Rvi!q zc9G|_0&)&PnqeJ(ULxVqAHfDg)bl;yAbh>~V1$Afp9I%HAr=AkGGxJ`1LW{5|8D&G zd=LAIq)|UGAmW60HId2|8Gu9}0cxtun;A)WJ4j}BIFQoZW3$d(f`( z9_V;RV!>CIwPdT(M4>ZcZ*^E|s7Yv`QIBaH0^$SUCA+y$=&1Lg&dCyz6EUMWC>R=< z0BX3K4Nj#F@nP#XJeAcs`LiaLeGjt5NUe`F#r0nBYirO%}(SaGLKGOZ*$(xy{cX8jG5L+hyQC+mvh6ODwxTTyFCH5$WKvST!i= zrW2A}sk$e4=xms=&%f){K7%s_5r-?PDBBYRWpU@4jA6NWtP9g=CaD&D7Z(?2U&?J} zGFIL;y9LP-9S9AvFR9pe3wWE;18hd(o#rptipKT0WRcNc%oobk&vav?nFM7t8?1hW z8L}-w{h;dD1oc*NI$d5MF6!fo(+5Rs%#9#m{PZiC%vgClJ@dtpS&Wl@for{xlX zm~Z;U?ZP|63f54L0V?Lup?{hG$k5qL7|4f7`5xV9m>l`h@Ea?J+ABIpeidiI#OS`c z`E&+KxYI^Q_D{-WO6JOWnc`X2crhO}bm-`)f%VAD_tCTz!zE=LXV)vVoKjOJ7EZz} ze8umFTHtHl*jrwM$mITf1Kxq>H2Gip^?G0H&Ib`!^jz;c45Wx+RXug#00&I{dWO8a zkA-@>VO5}?G=t0A4om-#-obrX)4JPXx=FHy`W1{Br75H>Fky+@i6eT*bsS;d_Udl{)}E~x$$~sE~?|T zrd{nr+ij{25sx+Ot5DYfqK32?^gslGCMH0K2SIwZYPS3rzGR0lJZ~h+pU36x}JDb zm3b>yj1@OQ{@JVb$F`WNu|$H|b@4@wg;VC-Z$M5`etN*tV7Qow2qrF=AH!8TsupG4 zVUkCErIGQP`qa)!N{sF)X$7!29Uv{;1{IwpkI&$fvaH!g`ia_QpTw(gXf7wo(I+*T zo0!d`J6Ajom3G?(s?T39>9WpMVnSm6I*IORYu>~gHKwx5v`PTe@eMuP>sC1ko$X~& zP@sB*i4b&RUlpfC5%h>)&oxNV(Af;T3+oJAzug}zOSiLkxj?pl{`715GQ-2+hfu#_ zu6}2UD_^@FtYHC!M zHKXJQjg=G5kKG24+rh+na)T}a5A03_Ricd`rOEDY(MYMw7StU~F}7v8(3NOnPl&~! zG>w=W2?I7K&+9} zZh5@0NIsBokW-x%&miWW?pUs>8r~=`*GVhzU2Xq&E725XXk9KWF;SPZzC2)TBvMn zc)~T)u-6C^cD1h~3js_*u=y6C=NI8aQQ2ZrfG%a&jt7NEEX|%}RO+koP>ey_4=fwW zO$g*s;3P^F=Y}+vMXCQRz$v}x+f2+il1h0qEWNHiahp<&h}j@ZhVxJ>o$$N71cF^- z!i3r~?cjHeWpGkoSO{%+@YMR^k*`GEI_2U1x5fVNjllJRzVMRS2cR0@{5Lmi-9|(5 zlKt5tk=8_Bg(d6O&|+?0ka|W9#6r_C?obw+=$=5&swOT+B*Y!m0b5;fjlX-o9^q0YJ1D=s!ITSP(@p%8XpK*#S zrv0jB{VeasTd4h;P*ixz!+tL;TuhJ{izoZ+icYjVI#{a7Y7u?lt9RL> z@X4+m-(!aerTo7jP(Wx0HdX&fy}oD_YGbf$je6Tuwkzg@)HQp zo5SNiKXZ{_G|~ZdCBOk9g5n_hVY**&2y@h6Q3BCAER#7`Po`p1ulKAiw%>e!dJc6G-^AbI%(oJX@z)ysj1@%q1iS8X<@iVr3O@vXelUxsp_MWvId0kTMWf zQW#&VxjcdR0Q@UKuarT91HSGh`OS!J*?iQD!)u*8Z4WqXqyi85(Q*W=?IoA&NXj@m zp8@@IG};GnWXpC(aMAK8MUGGq8Noe&XOq|>gIwY(jxw{r-!0;(qdGh-`u=FDY3HCb z4(YQkAMXi2idI5Yy3Yp1{nb3Iu|GRatNraDN)D+*2k%ajO`gAJB{Q{pE>6pfl@s2Z z#`>()jzJIl0JWig=zgum*t8e_c5+J>V8LgBy%twS3)iH`U1zoTC9s>VI3Bggh$)8X z8v@yDVpW|&0RsD4C`gdh?N@Mm7h@LKUf9Ebu?2|kD6Gb(u~kPHu8m3vB1$bF~80$C-uo+OZP2yap(I#YpU%Hf*~VO|$PAN%UbtE`%Myi+eZ&8=L1d|MemX>%7W1{C z0$gBDq@N~T5Gl^j~cgxs| zTlV382PltWAm~t!(8|c3H?(%-VCy`74gv@?L{~X1dZxPT4K@$#qn=`p0oYcnnEW3 zG6VpzTx-B=Oru${clM`W9C7Yr z7=sJa`B}f+p1V@@X9Fn5$MtpqKKs3=H0u@O;xSwC@}p5ogDp1oXAbFWar0S%vE_-R z)Baf;tS;b-mx4kzHD-oCj>ybqakKBk3wLMG!Shi-uU~`#MN?~(&rI1$ApW1oU$Zbf zOL>lTAaY4)vR}mO?-ujD+Hu^%gKNh0Z4c7mHev+N22y02?hkLY$t5(Y%$TXlO{EZp zPwkmGof(DgvV`C5J)MO9N~OyFUx@-orftoIJ4oXY0-Y~ze!wD8=!#mh)fML}*tOvq z1cJH?(HRp0hqV$05TV8eXB5y(b#E8WeZ!?NlOPtM%vr6!mG)1c69C2A^GR}Ci%=WW z$lfVNXc%EZD?APA5T9ayjrg)4`6sWWw%SB=d5Z)8v(LL_Q3aCEet!ooUNP7t|5}w2 zFt#k<}_Dj3r!?hD9FW`pnBBZNkkGy23vHN~im} z|F0OrKnN3MwF5(383F3yE*shB^Wqk#o93GZLRdSPI>^4R=CSpZQVA;J=Bvz%`Ig(v z(t+wSl$kGd6KRaxWz>aMtMJP}`7XPMEa{eoDtH6-Uq(E4r;P;<+}!y>V4J!Wum*&6W5)y6+ompaDHPQ~Y@*Hb0(;&xE0 zNQ_JW#fEeNN# z#?JTf364Yjetqkv?SL^)K4g#E!0JUEcX<%>l3L*?GnL8;%z1clp3jH?E$>zOkhB_g zV&?l`BPlGFN8xN6S>mw(w;7Y&b2f8o^k0T%JmN{}W{{TFG$`gT(ZEV=67>SbN{h>#uqw!5$Qby*vCP9wt54M@`w?8S$))%`R&Qi_l zZS4@4=07SqGze@g;j5W1eo+gAlN=96bJVF?2E9`#!-JM8@Yvop$64u+s&wZSbHDh$ zi}m-$%tz_ngE`Ns5*Ak0nb*tC%9^qNhaZvpbZ7h*KUy8+d)`BTnU3@_a+mF7|NYL{ z5`}XU(q3Y=c4W=`dYn8rvhC+#|D>6wgPX2I*q7JyqgwgiEbw{Iy$z5=(gwV%#2_v zn@vGHwd#pMfISk&Kb*Lv7}kSx)ZE6wfeF;<9-lxX9`2up0IsXn;lbgfro`12`WFMb z!GP=OHC(xYg0wAEKTubyhe?dctP2x%wD58BPv=aHwq5@>X2Ssl&WonqIiAjsYtKB* zM2TsbGI&C_z34itWyT)P8xZ1p3^HseNYwc)9v!JD%QIhHuIBZA9r@?Xb@O-b`ve!o z^P59m*yd*-Qc1RUE0@_-mWG6NhL9+s;)lW+v=p~!1gZMylZcED7HIOJ6Vj(Rb0vw>g;wL*)UyM(|@#<%p^B_H*lh7(8EaQ{o%rmP52%h zmY(XmUzHNUbHtU#)(&N@NUVeLr0{+f$XC~#>N&jBo)Gr@XS~CV3Dl~2(oCyvq|y*%@p-z^X8ulL;ix z@OmLRNN7(dKRFFPZ>5Xq702_iWatPI0a!Mjs%!ve@1^nORTyY_eLyS_{}!zQsf}2_ zJT!I^1V18*?aj*aNhfN991a924qevl0%P)BF0WJ-66mxgK(HvIw&S#LwqCX>oFKgB zO~6-YBV2pm9b-@db^Lff45Se+dC|0KuLo^CK}t?jrkS1vajXR@-$G>V>)L% z@5>QM3&<-dl&*0J+Jns6Yzc&oB$Ca7D!zak-**NBp@SFdGRhwM&kP>-lFqSkMkE9P zqo!+forMwGN;CYm_$+S3fsb(8_=5VeF(D#LgUnpcSL(Y%Kfsv z0f?h!uGPWdaO4!QRlkcsdoH-EqdwIg6LONI&iJ|?$haY2-Qy89p7h2tMMsylSC@tn z&0iZhH?}p%eD>QRn#1gC&X(@%%l1eu85@oE@+sn7*NmExtBoj+QTF#GWp4J|3)b8h zI0=HRnw5oi+$>jVNGZ^2no)bGm<9^#d8{)Mx&Q5P-Jq)Q`9f4z?&fkuk$ZIIDCKsX zCwU&B405CW-tj97={H+L0AUq{&9~08iIGRWdF6bS{cff%XiDVD8azL1T=K-!(X%{t z$)4QuaTK8@s`GaAshO8CTXa3b{WE`F%K@d)7kXs-=>wN##H`6KtIc7Da3h5q*!q{G z#PcN> z$k;z2O^}CfvKdQkA-0WQRh8L9ZKeb5bh?S;>};@%Hy^rR>%t&MBhyD7fKxz7>&K#w^^J2$S2AS{Li53g6V(OK>`sAp83>;}|;lmVD3aDv4K6r9RB0X&v-< zN0|p0dgQTKD_4_d$bV7ULj%o;z-NTST54sBSN|F2VROJ|e;l-2QCFq{+E+6wAt?_p zP=`|T>Ic#0RXSZCdS^+Q`~{WFg|T?0W!oBXdNn>IVw2{qRo%I*_QRKjYh@J;2CLI) z97J|8MV}2>^@OwpvfxQTlfd4sUsQ6TJC&rWY70sF>C0lB0}LJeIaz2oIzz=_rZaZP zG#aE+KbFmlQ5mENgIhUv161pqrnZ8Qi#;A)YM8b7qh?n*57AEE2s+LZi9u0aAjYP# zgtFFnYQ86FSXi?biT+~YBHYssGT6l2YZ}p2<@GhHXL4B4PFk=svNXR@J-(=Lx|z?A za|*a}5;u&msfN$?Lj1ZZGw!7-8DVE-YT7nmw(qqG7V8TdA*+-aWsoh;P!|UucSui_ zXmmMpwTb=?glC5{pRbPGx?ZXkuTkLoob6AY4c4;Kv7>T3IQSmoR&n>HOw4q} zl9;htqUdG?@7guk1LoL7L1>6k5`<37A_I5YfcLaX6e} zY_P#|my4A0P>7dj1?+8oJMs7Ms*o!G0-q$jLj#ezI4(O{&WOA$UW;xAAIxsdY>lZk zVr9fk$Tp1RPCiM)A)=^YT0F)Oa~K$W+iNch8UYI21x5+UE|n|MFuGytM(6CS(Pu#1 zX9McJ5ovQU?fY8xk7i$iuC%Q)i&~lANJjm4n+c8)t`@2_t(G!<<8|Hksv)M$QsJ5b zU2ZD&b#NdnY4W#RvWj-*1MFe8j`Pg8(*GPsn?$nggI4kWsD|tCMg`+RC#a&LSLN3E z(?>Z_kSvOci(H}%mD-gamkpXjNTG91uKQq5G$`ZXmB}1Ka4@c##mw_lA!eh^`iqpU znn$Px65{{pqEh|`27e)zW9XxOepIiENECOED9>$afda0~$n*wuT~{=L2d_2NTg*Fd zkC8EHxuk&2x-m|e-!n?^9NMSvWCrA4kD%a?(?|d<_cA2ko8aJ0Ww(C54w2esU{Lvk zaA0qusBnH^6t=&KVu(6a+7o!_xO|&`Rm#ot_5WHHx8VBhXWPYMP(s!8XdK6=55>+x zC1nte8w<7j*-sE?*Q>)Cb9}>iEffEG6TNOQ8fM^CEBmhYfmG6dfX%@7w?y>>R7id3 z{P~Sh8aW&k9)K+3xGrMI8pD0SJFy0vUK-G;`3TXBT=1(n!*Q~+43cC?vRWaa_`1#? zX*B=Y=%LQ5{AX>r{(NQd?xJ~NxTeQAE+ej9?c^JT+Ai^Cxpma9AJIQ&TDSdU}I@kq#&7} z8{kj}HQk{8%852OvM!>7)|WRi*Ti8?H?zo;H!zxzoF`8|D~`#Nxp)Xr(VxF`a!(s! zT0QjZoOM8Lv9?%a#NCD&2ITw$cv*5_G{|8&I8$>Rd=HE^(fbjw~Pwh|!nOYcMd6weu~S=e%4^0Ahs9crvWk z*{I~T;RmMvNJ&y}E7cd2hBRwaDG1WMXa-f z#b8r~wT1T^m|%hnWpUL2ZLQ@b7H!X?M6PQ zP~011A;Pu$e&yucg%|OsT4s@Ad+t&^vy~FcGlZ9|Ud1{iSRLM)HPqYKDzpJdbJ-rYSj%0sLToEZJ}3%~9KZ zJ4^;QGYUpH0LoA4kA7F%NVnvkR^09ZN@dA)ExyB9(#kKs)0monE8!?HisjDN*9I1= z&4tcto#xN30lzp;ydJIo7V~&6qTA()e9iJYDK}Q~qI{LO-9XxCj^`5|lm#QJ1x>|L zNhS5eFfP^#zXsSoc;@jAM!t3m#gTtySb+Z*QN!85Qf+G3s zBt)qNihGVP|Fq5U`D)`VNXRoL`pGZ#CI!Lrm@!-Ytab1)=-7)6n^{N+{ zX|bJC?1u5*r(v)6%n(>`zRJ!^scR69mmonVj0&#GD#d-hPj1|8x%M`F(R`F`0jL)H zR@q;iL(0aqP%R%x&8zC>tLnJiA3Cj=eTISEPzB}_x@*S3}N|&e`-ZKg_+W(HGJJ_ll2nPcyob0A8S-^D&-#7 z#*iIRB%UCC>x=aujkN+JymAxG*o3|fi{@cKR_Z6IOspWLkmcSy?U z;{W1lAqt$ylfJ7<*SKTC>#Z)T%UNY5CbtPl2Y}Osa*`+>Ew1OiSo8T3)5k6L0^0&sb{vu8v_hD+x3+%Q`qX7&DII)auohl$4xs(pmz|YyKX@ zlZuDNZT=qsmtWj^kYdR=c?>-@M5|uo_rm>&&r-#(N<|_bF8Yx7h{-Z-)?o8%rK2Mu z2B>*2%EKqx3eL$%b~ZPW%Dl3Kdc9Eo(YPwDIsMo*KvzF+oyP-|JhL{gV6j>kn=j`w zGp$$T{rkI?p!-6(v^clL;msJX3jb`SUh+MvtI00*x7Csm%c_IjTDt{ey=pzkfY>Gj zBnv$?5>Fi;z+p3RH(nxye!pYtOsgwEAX1@aJz_fo8P0KK7aFd}p`&eMQvTY4h-dB| z-`a6gqSI=B6^dingl%OJgI5e4IhsDIQq`_=B5bYdOu0BE`%_pT(?31Szl)vjD;}S5 zKn_^L0*`wDVJa@QegQ}U0)w`<-5OniZI7 zT-L54#TKWma4Jr@-5yL&W&b(`Nn9$ev@lI_$C^x}nQu(I^o?Tdd+pb&np4wHBN@~z zl)Hr6x3ykwNMX7xPQ}P_DW|FuRY1A|TX#@!effcHnXSjb!NVS?NQ^@lwo% zHN9$BT1+VC@!XGQBh${)A4*@scr`UdrOy%FQW4Oa^?SdvSl^gPZQ@(fyYIg!Cc4X7_{QoT~8Bb>Ar6R(vV3f)Z}BEH3X zq?*3y4NVJDHBFB0hC(PX5aV3zt{)`)qgBql^Zo}U4PJB;!F9pDMnx4rms-HDm$3Qy z@i-sw#Uc4_^3Mm;{KCS_vT3IDW!o)M_K!qeujUtpX=ANih<`} za+6~SKcbN~9y6QE`%CA5qf)wkp{F2non5$eM@?JPJiHC`!CM21CV{7(j<}ub0`(8DT8# zydXSov$q4b&T2AXktNM!=>LZY=8k#!80_(Hxr0D1t%8hM8LsdS(9%OSKHKG! zZp~iRih&N$q#D4`71=cDQbg&U00F)g&)(13-*fNfXUIxc zW@hGm$9Tt>!$y1FqaEl4RM~~C%=b`k>sU86Jv}4dn6HdXVFDU&_+ILiTP0)1?cQz#g&( z)De;of}RwaLP5`12zJ^JzvwxrglzkhKfZqW^78p-yBey$^rjIop2vF1VTB}v{CqcS z##~B=#orE`-k0#M7bHXZn~ypF^uE{xcNbB48mDBE%1w8AJSVz%BL5B&JnX5g=^)LY zBD0|X1HAUqhW<>@=aE*v(6;W!zCAnNhDwX%rle?^RU_Xt#fFKnM>i={P~`HMB50l< zLzaI9cX<*jR*-=^72Bl^52g7~o6;1tcKYjE_@2eR*m)+xeH{yNSEk{D>W!#zfeWc# zUdfbU73)Kh+;sf3JDXH^p1xWKJMHaly6hweXt9A=*U6jSB%77pw+!;VpQ`vPY!r;f z!k_-Mq>)Z_yb!ykwn=(c9lQ2MU30uKd^ zsJ)B=`^mlWb@(OD_&HXzUap65Q`@<>cfVK4-!Ia(&Lb8OIes?>Uri_og{HXyMQc7O z6kI7i9O}_N8<(l`qpNzKRN=eb6Qb>zd6jUY3EiNv7BX|`-gu9>I6R6+CMt`;P)}dR zX3s`Y)HpJshE$;5S1lgjb+`5ACii|KG@G7S ze|(o-#64b0hT*bTy+c^c6i;HX5FsU29QOV~G-z8QIJV_XncC) zhnA`*O7U1-PC6q0!yPduUz6n0eo$lnifWMe{*QwNSuKKq!IG&5Cq>+3lp@sSe zCVPHm$<&?i-)>5=KNL&eCMj2Qom-wWj+=e|&BA+!JJ|*tEss~r;W=w<<+7zxZb&6) z!8uOr&e2P*9fZpFaYxE)lJa~pf#Kv*Cd~>lwGipfInX%Fxe;O^8 zFi=*NY-K&Lq}2Vepg4nhep=M~%?w`eu(Y_tnQ)K#?QgFeKXwE{+_Jk)gWW5;>><{H zZsp@6XuhV^r2}5KHdLLjSG2A^F#7qNV8LZy^e0@?+l{Zyg*@#0+R9@TzWIq8ylZ&J%=}lsndxv~`+jZt!C4^fu;i$=z)QFSdt2tx6DedHMGnPXXmBxZ)hRslh zk{V;OJ@!xtbH>{wU60#L>_06$mUb$kI%C?JnE&}k zp}gp!k}g?nC@L{C$xm7WAxL^2D(Q`OcfQYD>+K{Mc_lPndJ6>Oe{Iv}QMOY(v14NM ze(h;}`&MgBzq4sVgXAiwU{xyYhH9g%0?!lx+5x@SE9L3iDb})Pos{=-Dikf=!-JWw z>#AMJA{uD98yqLWeCqu&ZOEQFF!Inntf6`;V%SPhLE*Tv`VGnb5IccYv$+_i=j16i zAy4iS1EXswfSn=u=R5Cg+T_YUe$R*FPxHGTPaDq)e~pL?&5a>fFaI=p6hAzj+<;ey zPAFj-4uW)_k{B~eFIMh<3bAE zy1jr-Lo=t}4e|&cq?ttH*#D4bQM)4YMStmlBV3X4zM{p1Uvj5UM1&)@b@31k3eXgI zudmSB3~U-}gamh~CyHVPm3_2uaHZ8KX#C+po%%^qtLy|!gIftCDaFoH%&YgQ@2=Jw z*c5%RU(_GNQGudBy*v8lTL4nIWp+q0IO=!txn;eFdVVrek495}Ns2%~F!!O_)O*{m zjX^iVH>ow7UpaH4ngvu>c%(d86F`4h>n(8f#QU)Xif}1U&(a@iDs!lrMy>Z6thf*^ zb=MY>RZ`;>x>jURRp&eI#80s`-iIC!GRP|L@BxU4Hc}b>UCgR_zOz0ZI8x8a&%TPb z+%qo2izEIn!Ip&8c@cd^#KBnums6V%7Gy}rR;eqybX*&z980%M2O2hxfsRH{MW?HvLTE0GPj}=IEgEj0>Nr;g zD0=Vdl}$X7w(437c9T@>OBS}g?Iy9m$XdwtlCHtJUaD%8;1|TZeh|h=bU*Wc>GAEY z^XfXZpHMChrpH{D4)|n)Dp2#@>X+AV^eD2R5g9u|=g zW^RF=iD4J}g*S`Z7r{Sx^=7heBJ$)?BptzR+&ZGkWb)i{WgX!I8aEvWUTH9czy|Hf zePSa0f9OVR84*SmJ@K_ilIdI{tif#0Kj|H|+xtgB6{d`!+5=n|As5>cFRw6qCcACB zigJJzbbJx86l8t8MfA@TvCmg$ftMZR)Me^IL~Qja6^_7I70R=tZ(5Zhf2@Q6v@R%p z8qaeQ#%=M?a{P){Iw4fX9?;bBpxr{>PPUoJwUY;LkzYhV9)c;jc z`M$8C;(z$+s_)lFlJ(@5+|3*Vg5&jxn7wdp0)Ll`&&=q)IN2)5^=ysd(a%Zazf19F z75op|2L_SA%MNquVyxdP1_MDE{IPjJ&(@|b1C_}3Kw@VzY(w1$A1v-tO1j> zXEDI>^qts_LTU*+gU)>LZd1xPHaAz6te3$K0f0Q(`h2s@;)AW7T2aQ565uuKk7Jq+ z@&uG6fkz-EN2ZVmcFbB%b3&Fiq1dPz-#YTBFF$xN(+F8Dz`xXT_Tl1T+O&fcQbG(|5?KzmIbJn|5Z(ilvI z;z>y#ZGAVnTpJK*{{Lpo4LIK(7N&ZV?ZG9p~S>FV6DUMjDP+E6~~^y@Y}nmTNTjSU^rg~9Sj_$e+Y`W zI(>O9EhJXAaS6!T%m`wuUa{vv4?Zhh{-wmPH3ZmnZ9&w<72|y*57JZX;h;mX-&ec(= zQu!~QN%s(zx6dc3gi4xJ0(7xcr#UL!Yoq=1`40z=NI?eeJZl2%8ckgRRoOr5F^9 zO%nwu78xp9woV6SR+H4oSfdFk?}f8~n6@Z+OtW)xbK@NnWKl8zrK^@6?l8`HCx(v! zz2+1I=yAv%u(s8v=<&ph1pOc-OtlW5AD{owCg%KC;&c z&o(E8O%iEX>Hk7^7F>s}kczY?1)yHxmj))vZItCLe`m|^21r?ktvc=C#eExx<;>I% zd8EF_b);P3FA?-bn(^X*nkzwXAN}TXP2{D~`suviTkq}0Dd(MlU>Gm{-Y0(&PVZt? zhk+DslIz=FWO|merU~2JM5k}7&@Y$C!9R!M;*+(%>gkRgC_;J`kOkExYKFg_uiR&p zkV4!3xPYHlWp6DgY}J4B->U5!7U~QFbcjDT<|Dhmf9%?y>bf%PFxmR)RoUJNTCppr z9c=+T;Z|jd>GW><51XYoU#>2v#>nKx5XH10pVeu7MKjGF1d^2&C&|^ z;k@W-Et4QN#3^Zwm_%H9FJ7Mhrz22E!k+&E6fL?3wdb;c7%aZ#8 zcUF;bn~u?LQBk9JB70g*5#ECxs z@Y2R6M=`jS1me=MyH=Y@=FEbnRDNezdx{wZUm`vj^rC*UF$mB}(IBdtI2$;;?CjSo znJ2ricdxGug1feZ!fwQncZ$&G`tL4N9;Qm(h#)^-1r)PRKQD52=kd>=(hX0)DqXjm zJupbQYLLpH0M32!CNTf(OT zf*4fHl?nFn97a-i>)>hZcCti6^?wLHSz8kAJtzg9A7C;qd#ZG;!bK{?Z8rwE2QNWJ zB$nYt94}$tGhR~BXp0o)p|i2A0Cc@(f>-wnR^yB9X6(K0Q6IKaHPEQ9gm|Zl+1B)8 zf+umD=dQBOz6c!U{4uD^z?qM zG{{21Tv>K0V4)5sA7Ro_N>}KWKV;bmrBU?KGD0o>FY=E&aX1o?_Tu8@%EF=%+zkvu z$!`e+_L&2Uo1@dNYa1#!kRTZK6MhBsfrI&^`TPwHj%A!4KA*0k2(RhSm2RMm&TyL!#;dQJ{Lh)F35vh!WM0}EE(|E zW`v=+2l?ZtwgGFjB@sRTXMn!8coo$`2oL8OeMJDEsE%KdHiXW4B$H>p(_{C@5VDm% z&YEzkQ4HLkx0G#a51)Y1KF%nqxik1eV4I>}&`XThS!rW(XqDSNOe@&skr-=!xyHjt zIC*E+=)%fHoGdYY2hcq$uye3qJK8}~_Ro5@>BrmH$h#<7@9-`lqx0~6uCWs{w%xw7 zA-9L>igK5s{Y90M&`>%ualI15*N`&QE8;1{ucAO9d?O@2GJS8Yml`X-9w%k4&dAkNsUw1 zfezf3VfLc_d$8_Re7w+K+`mM6jmgEo|A0T-LVIRkBAgz@Jrzl{>F`HhFGITeO&tPt z`i9sRMQ+=PABao6GwE;+KIcVEW_BKN+cOmyYBQ}OQRL%^`r6ps#9M_T>(H@9G)nr{ zBc%(5d7!}>+fcyKPI?W9RLeTUPk71@Y~^K4i)?X}K^zfq@`Wx=t%PAdYOlCmJ{XS*@e-HF3kKOa)5fh|GfD72rANKtw0c0pc`G z7-_t6mY&I=5Vlg%365CleWhyXJbL_6Y zYyN*xfGG){>YP9cZwbqPYfZy_pn9xC-^w7w9^LRaVk&_`d#jBDjc2fS6yQ-XBi?k@ zF@R9Y`?(5;*=hZV2@oyMV>~l7|Go$vfrF9kCMSNd1vMs_tM}jbt4Z35E;P{#J9Jb? zXL5P%pjvs7yVlK3oQK1S^yAK(1NxG35u{Kh>Y&e;8R0V)y41_&{0jWr|6nO*+?FYo zG`!vq8aGMR)d3D1%}pwv%}zf*Nlaew2JD(YF5$QHU=9BO8^-vJ$dKYe>L;Ds)EIRR zgHA_YlTM7y@f(???Ft2vZ2&1_>ta48Tp4C1z|LXnNL?@LQm^-@*GrQ^tHo=YtSi)u zKCb3LbUG=~8dTZ~5xf|D%D>(bXe$HOvPDxM)V>4iXd_v$mIR$T(Wj@tikckznOLjY z3;Ie4tcy-W2tB+y$TUNc%G}ZzCK;mJ)wyU1T(uOu(2r3+JJvF+LHlJR zE%VL{UBD)6T7w284XP1YVJBo$U|vQsC#vsw-!uVWS)7sfgAC#Wy|awX8GQa^Z7_YX z{0?T6J$C3Nrtc-+qcv!+NXJo1H$BBik+v=IlJDW>G>%VxkCh0dpvKEh%+O*oM>iH) z9?9)QPLi)u6|9Bws-}OI^6ugK7YZi^VOXCi%7ut}dD9yvu!K8TG66Uv{;^}1yUs2f<17Y*yHHB6(%PqsZ-1J?wF=0U#pY}AYiu}EuVB8 zn=j}yy5Kk10_p$`o`Pq zDvqIOYaYzYK#(YDztJk0QQDfsD*{T*e&2Cobw2d)sxYj1g{BvkW10F+N;g!!)?7!J zp92Hkno@qO?4}%DbF+eXH5?fGSu2NIy=wEjFePw*$+U5 z>aa!!tYONoWPQYxu?L^sQ*29u>i*x*mJt-ks$k`zi5|=BRF7iu&EjB|X>*K@4 zj^g=bb7u)&#-UGX9qm&ND&GL3Xh|gr`x-TT9f`(g(;!?A2H(ao&}(lLgM|B}P|0yv zFq1Z!$Zu-xpFo0ln#WweO1KXWf^HWm`OO_Y09)Lk6y;jsd*B!wz84Y-A-lubw_O2x zhhZ#I6)Qw(-ENwV&WuPOVG!XK#Cy@l;#cNlD7{LMj3C_p5yke7Gx3hGYd1^;R5vvA z)~%jskPwb9O9;R55rdNMOpb_YWS5@v3a|Se-VXgb&5SEAFBm!Luvzadn_*ev{hL1- zE8qhbFlZg9rXxqZHEOc&K}rJ&sal}WH@G^jZp4B888sJa4u6t5{j%B+g4-xHNu@37 zPRk>nevm#Bpz=wExK!s))VZBwr9mYD4ou?pG8S*Wh{IZ;M6>VssUz2P$8qm_KW@Uk zp{luxA8F2zOf*#Md~sk#F~z}GWGH`_PPO(zb*^wuo+J04-2^O-w|Mr&FRf`CC|85~ zQ?!qjm7{ax@c$to0ES*2hf&A{#ifA{kQU#55hbhso3sc60WOCyunysWF8~^v@KA8G z%Tc)Y-}}xV;zL;l*lcJK+Twq!3#J25Z;)$&ZCw3;bZQHhO+fF9lvA;d{o^!u*e)O~ZsqWs@ zT~)8vS{3q5P8C{6)6(WpPZ`Z|C~z~n%D6Qa*WjTI0IVIZ;cUXSWvk`Gv+4DyBOMRq ztG-7H4ca2eXX#YhD0qNjKLsh1ZPzD~Y*4W7Pb9k8(KW8^48Ml+-^<#&;IbJ#$DC*u zyZAqnAxG-hW%YXW18MTbV>B~Zjm=aXoq5L(5q#ah@#wVFx1bYO|#K@Qy ze^1b#7R4I}1f8q5Jf43*=0*AyUB!csUv>b^GXic?H#gcCko1~Wb?7avT4=`<1W`L| z;A%^n>lppuOmC=P99lqW-%FuRg8x8dTEoO)5(@z+WfnXtb)i=NuxaGr`o6&JUFsDR&x@giQdKJZ6@Zis2vK+%t9?^$ zF-qY{==#bmVXU?Bk~N(>Nx z#uxDeXCQ+9g>3}NISet@{o@;SXb*WcLcHIG9ufx#qF-J%Oggw}kEkuiI*4<(@HE16 zw=@IzrXLEluPrh}u3&34iV@<9KqN98AP_8@;VY`tuh<+G30TS;<7wPtY$~789I9f> zXBaNfi~u^}jA_qeDBNG00$KX#GJz-6pq8Lh{^|^Phyi&!uC}=C(35^}+X%KeS_nkG zcsq4hDvyZk2wna`3Lv6F=6T@?jbAZC{b0q(aupTq=3$jlO3_s!O2r$pR~4GRZ4?X6 zQ?Ovl3NuePn#nsba6!@v&L?herBF)epomB)%hPlOr37K?4g%4K-#n=_!t z%!FeceiKe?=((q7SnxC9XV7&p%pB<;=~jNvehuYJym9J})`j{BhvD&I%f=O^I<7jX zLvZ`VmSvAN*S04pxI)qN0gW*7Ft0+yxUq4=MMffZMH){UEOk}Q&Bk7hnsTOcCykBj zzzXg1fklkPB`ZBVHN21sxwJ`~WSk4;eCAS9hm}~Xl}64+$3|YKRVRI?Cns5_DwEU+ zne-M{lb3Oud%F9Sd-ro9u5ScF_y7V8{7nuGZdiP3d>d|sbg&FnE>`Xir^cU{oR@Z; zM?&@~=e3Ik69;8wX=UpTn~vQRhH0oL{hCt~^UUGoMqRVLdH8vg#_9 zPtJtT)V%J!?7H^6h&Tw-57Je=WgvmY$x?UeuGZ zHM=_8rmF;1>#jIJ3}_CZLEP0-@T=jf!Gd9&frTM| z|I<)HzvIwUxGteLt028#>~wmtbhvD}Bz`6ygY(*YCuvfCZ+_cN@(t!x8;`Dy*Ws%Y z+&tD57QXZ#=@1!_%zH1=k=jj=QZr=&r7&2eUfXuTv|6(Y$7cX)LU3l0TR~#}eUWQ{ zl_VbWOR)5SA8KU27J!SF!_)aWDEun?4JR+g{7ky6IAno^l=Ok~XeRz;dT4r7OHIoVrUhLhU1wvz+ejAHkkd-_1+k&N6Cc>Ub=tiaV{g7x&wT*h}t3&Wg2~3(c77W2qX?pXS#|cfNOO zUbc@pXF)RvC3swHt=8)eFl~~pCl@AJ6Hrnt(f}#9rt!_$=7gFCv(q*!xJGR{N4kxU zjAreYjy9U|a@U}hNLlXA_^`1{iOyZmIR(wlautC_U+$eH;b+6=&aC@#+n|=W}4_xX#!)7XECh)Pl>8xEYd)s}s z-5v~Up?}sH)ro}1d>6hUN~ogIeR~bMBw7mG4SLoY(~as>|4@B6e|Mg|Tn%*XlixJ$ zAbR?CtMIVrBVR6mp0&rvPc-u8`=WTi@Y`anY1$@AtWHea*n`vC16_a^PEHnLd&0|z zPY&dl(bN@m;{aVB%M5S+T zn@M;uEVLAIbUHIUyt&=v+m!3V!rYvUKPZ$dmRoD-(BHxDp}~mV_rKnM{#Ppv7$m54=UKpi@A>NyJtPvnktIU#HNw%q9{%qI zGTnRJKY#VVf3Ai?sou9h^0h)T{!br&Ux=l5NBGBWdML|&(BA-bad=-a@kaW7&P+jvZ~Hr<|!V(vdY%ca__U6q3Hr?tZ+Pb#34fW@|B{JUzS7q z|NQLjs6$r$J6AZ3M#~U5`gUZ^ChI%)kHa(@+c{ZwVp39Y&zF1A)5VG!^EvX@+d28e zoIosH4@N}(S6gLe|1iZkyNiH}T7?|dR zL`|@?W#fO1q4!rX_t>Q~mM5<5U!NF$?uy)=t)LYY6eP3TWM2C>SS%rqCDICK@p)D| zoofg;V$yv7#b&jtceY$J$jxTZ4=t0zWDFI(RG}HvcF_t)!0oiIzjmRK0|P24DcM%` zLh#%$v<4cht)YLtPWI@uyt3Q!iL|Z0ceh3Q>b$ycz#dA%$$TJ-U@8KSUC7t%Wct*w z&2MSmMa$;XStR$x)nqbb-&ssn)+O(G{akyJu2;)L^IqglBq8r=xoPl>`|XUlJ7xES z;^jdGzjr4)J^hsQbw^SiQHS^2scy#o zCDwaMJN3QBjhY_9s-FrCE*d;|zP~zjaD7!btY|YG4dE} z9q-e&$)f$ICBLFTzbdpB3htM2>7}yU=xqF^+VxQZq*8&o`z|S~46&<_ zo;Cl=%3dXrtM^ak0b;9j|FCDhOM&j#s*XL++PwPzx6*BS-(O_9-m&bnnSuXYIZ$AU z9_Iv?aRdebX!S+~n(uR5d(1t{KRR6I26kt0>5_H!^&g+|821_IWwQ1o>WltiM%|k} z!0z0KUW~-e|KGT}v%rCF>YdakpBLdDEASrikGJ67q~MnNN9#K&(AnHkxPCb||Azrp zJOAl!^O|o(LF4~j%N2#-=QDXb!uPp9=Az?zfN^`dwbfPG7?7V&+I~HV*T8B%Z#6P9 za_UrF;(H=@acA=lpNRnXb*)CJ&}$?Lfh9v zL|Z|Vi!6U0PPTYSp(+*7K$xL=ed5`Mm7|MR21o1T%LCfK*O3)8HH zBqX4p@6W>)D>Mz~3T3~yyD*AH;06Q-L)Y7E5!x}Gm!6|O0plJP&}Z#jyS(mA+c`MY zyH=nh&3AzvT#wy*YvRYJ$4p zVi!}(h!0&^ao-2{)~HH@V}(m$5}@)Gj! zcbM@f_Ue16qjA$Xe*8P=>6fa>(@;gd-+A4DsbBtb-CjwyYbgQ}F6R;$3Ks7xDIA}< zFb5i%%4=E4$sq>@65NMd@*P7LDMhF4r+9;a!@GQ`S6XZTNtcD|>BmwfK_acDNpFnB zQYEXAMUO9p*<2yQU?hRX_x)g$FAT<`@jrs7u(DUr%TJd5ppZ6KHqN~;eKsNmbaZq& zs;+NuZ{3=#!hP2ESRq3bJiqf$*O-Nqg_#;=e_t8O9JGJAKYs*_(W6|@_GGu$9Dm~> zA}cs}bW|ZGr=#xXmy;3OjfY}z-C^7JZ|K=xpPDH!!%VcH6q9*|br|;oftb?~keWyC zk2fW$p;_QD4QLt~_wULfC0$I{YTFIqXtw2Ew4xPV8=b4LKl$|uvAaJ6ZKlptK0Yzg zV6)wI5UntOxvKNI^x8?=`|Yl9qVf>c3LSuf?d0*e6ZG>SK@AsBkOEfKai5Cw3S3U% zMBcA6&8L$bd*Qf(!QtWl(Cd?_bcNm5XO{d<{9)Y}LZ4}9$~TvqSs>d0u4c!a%X_To zHLGiyJQhPmIhwSY2%_cp_du8a@V?1=Aj?9Z8dgH8J-39OYgz`a^;4N-^NAI134abg z2*V@%d_+74Eo6zF8jiu!k+@fw*9?a!G^ay)rfr3EA3f1fT8@+TKZi zw0&>IY$7P9L$MXDCGwMEVBpOZKT(~@gm1dI_s7dt4x{t=Dh5S{1M2;jw|CwL?0m~o zr0s;JbBc%L*4>8l)>G7Hg5~{@xQJj`1J)%LKLMSJ=4Or?+a)Oevh}ESH*SlAgM+~B zg~qNVMy+p_M?13L4ht+|c3EA%KM$pf>N$Tw#=uayw0H$tZL#y(Aa)b4&Yx)x8mL1J z8~k4D!uDqcoSE~mhFxX*Aqowba$imKdRUH8_8E(l=D2DVrN1le zp}PGFxD!uJVy2g#;vm=zL3`~he!NAC-?(!AINfNtdVAP1+#8IDSnU85d)}{@9tv5G zN3!MvGyH^lQM~{;A0WS1{X%j=sCHOCj=NlZg|Bgg%J%aeISGK_anK zHFUo6+o<8R?zWReyVd_#fF1p)F@P{NW9hDJyWn~S8XdxN-bLZ4iXHHa(i{kTDiU6u z2E%cB$}Bv2J)a%yeH_o0QBn~A_iHLYiyckdX8xDQ3;oKZw9JGle zJIv=DE0ym{FAw4qmA#!~N`e;V_3nA=$SR*`CzaP5#6c8q=#^v~O7BWi&X-ByW7i`; zDlUKe@6hrFsJ)r4h$ze^B)0(S(spyZpFdJdc!UfG!+j)1fzzd1AgfJJy05gjK3M;D zzcv*uP;d*)M%F3|41hY?dlngcqFCBS25X~3{(E;RzuIdK|E+QB9`z^_}fU#p{`%o9q2 zAxqaDqO8nS6Imv_zjEYrW$VlwilF)PKOjgo6As|0@$4wqQ&zAAiM>L4!$`HfzM#yt zGFV%bVlFB}HF6Ig5WDtd_k88PanJ!7KS03?3a!!Yo-3dhlHSn2YgaWgoD6%$hYMPN;9e~U6_r5OuELQR-(fkoIPw6B&R$mbmrn{Hj zF#6OK5-G}De+QD3O39+adnSEK#Zu%V24sDuU^T*CUe2*8f;7*@|0dWzZX{Qy!tvLS z`79+}pjfiT*fIJ->OD@DlXQKhR%D$^o&H7h?kN*wd{b&hAWyS_)*ny~9O<}pv5n0d zsG|Nu$zCKI)rJ^TSR+^+`Z3k7N4rM4M zLQ0eh;HvF$XTjL0*_-m32YxQPP%+A)QcGS)m~7 zPO#M_r%$&47?ilUX&1iNZ~X%ORBd-4wf7WzBtPe9!R)$rIG$oCrCb8*JE`I?FQVK( zlloVd%%T9^_#RnxcwlhIPz8TRp$!E#Jy)%5Dy*M_^!wWf2;eT#dVcx_>2PweQZ4;LAaq9I%4q&u>|-aLW|vn7 z3_D~YL5z4M-EO1x2G?8zbQ&R30F7vG`Coyjn@~V+%|q`wE*v@r8==)#eSfO-CEK;# zn{i%EDt0cN88HImiG-UkS4H`^C?gPQz8Es!i>-nc!JANvJ{mJudC+xT=E=E&yGTIj zO4nhlIBIs#I(;g|8|0lO*&ohBt_iL&%}VgTBcsysgxIuNL%u&;gT`U%-%vic4?-l! z#tCChaZq%dU(%>I(@!1!REQG~dqR18RO$R;zG!(wC+B)lDvgz6a0E-+2^ggoD=NUZ zWJ)qJ=aC{Wiq4l%i6Z13cXI8C&sk06)9?#(C3BJvO*$1{SR8Eq@PiL;h_X*mp3 zyKFtBGXwnk+f^_3K?l27r5)ir{O*H)%h+F1Uf_tOIdbR8`*o6*-xAvbKK4MP&_OuK z7L@Gj(O+K8HIXz|`|f@6779NmtP^vI4ai7MuG(AqO!+wnTg+rDPcc_5m8rfxB z8Jc~QuG5OK%W+PSOt8GK_e!>)AN-8lP5_onk3)oX2`eJ+?YBDZTvrTHEWvoQ_lGTS z_S3r~AdbT>A`2m*f+Rh1thMus9IJuSGsXY0ULWH$!5X2EFCTI-U&Ues+tzB}*_83O z;e!%^9oz!q897}H)vaOQ*_olR-Y6EX35`|X1uCzR)h)&g$6xJC8*_A8_Q#GumCP8q zc)&$T^xmyfzUFUIGp$Q*_z(Z+jbtTcG5a)G zQ>@`Ov-QpC#R}T}Rno;}4=D$m?`$AR!Rbc!E3Y^BC_nZmn{RFqG103s`y}5#3hqgG zM`Nk4TW{rI2h5*BC^8u`Nn$WiNCQ|{%M?yOv1+gq+{DTAF}^j|K_4h6c3B_Gy^Kgx z)aI+l0-PXH6{Utr$B|gfn+BtZY;HAEDN9z;D#c}X8^r+b}Ei@D}m}kQjEo-KUj&uDU9!g^`Z8nfNPxDHZ%| z4-wtfkczT9KdhcYxEtYrd0 zlBxb=$^GQ3b!kXzRI_0ibS%AFlT0a$m80kbseYgb>y9$oOZ;KjPZnicF>Pqk8{(=C zM0tV6H)Pn&KJOi7ZJdF3N|tt=xD$ z9&OP^m0aYB+qbI=hSePlZ&BUmO;YvMVPL}Lc@0A#U+Oy5!?ic^vSI+91^UPV%1$ebnBFYi`dp# z0i>0lEA2}+;Pr_|R9<&n_4kw9_I9O+M?^UFL6S)%h?~o=&^E1uxlZoCq0Ydt#gqc8 z`;_V2-&!MOm}aSh>Tx44hlQok)@TxgzD*|$Le+nx(%F#Uq*PPcOVWkP@T2%ds@@qj zb%xVhOoiG2uGWfX38Y#$>gafa?WbKf%k;zGYi2NvZc*hDbN7UKm0{*f1Dqo&S_x(BJLNgl?o z1X21zhpeg1X#MzyfZmAWqwT zN0u5PZEQbmP%g);{_}DqDJ9QYec>NxLjECXLU=UREj^ zI-ISI!QHBXL>Lvm=vs5ZxpsPpHi&kT z0Nv-<(S!X}t@l5$2{Qxe;kblOk0C(}Q|>yn4O*oOQH4oW<~4lgEN7v(i^kNq!6jdeDG8&m%) zUA)P`XED{t)P0V~FOy^%(3sVB+X9{*x0~>PH;;nqazPgfB6t_zUj0oEt_La}E>DjY+i z_Pvq$L~W>c%Hr0gFzjKS;18WRQ2>tsjj5z>wSUhP5GWJyL;eOG%?xhAlH_5P{v-7( zPw_Zl(2jkvInCSel#)a}ge)ZXa4b3U6z#!ggi<)G5#@WDSjWoYib;o1oRi{O|` zlSE@U#<%ZNE0H87d+zXOc7 zt%baAExLyyCyH4tNGS?n5T+3a{u-rwS8#OIaScrjNHXfT3{G@!_tk5e64GcB!9p29 zC&88xwoA9v?)?%+jbDe-MwD@$*m1|1nN{n#c3%}dq7rw$e*uuyHo6w-T2?i_Pv`3F z+wliVidM@k&mc(J%^_x1ZRf78b`gtBCCTKv$DNM3b>jMN5?YrnKe2yt&3RXJ-8Rkw z2~SuWQa=6WT1iFaLDR*@y)bJ6L=L~v(euvRWK)VKwVeZtB7}9Lzu~a$sYroU#TEyu zsajwCuB{Y%ri|0NCD5C#T(&TyuQqT3w2Qyb3yx=~z!XTokjM^~1Raenc+4n~Ubp_H zJw*SZ>)Hv#RGeBy78WXu!53%BOn78u_=%^Iz@M4A4yEZ?zQn1XJk45)A9P&N2?$q+Rp+HasMJWF22uL$#Sn_k`4F zK4S#v?g`0NR3_=^`ywN%o@Q%a2O16%;GpvY0~Td}Dyxt`ZkF*mH!bkiLPiQ?5^t2N zNQvFSro~%5;w0a|+8!3s;JcAbUxMj%56FSeNSlB%()#3(;(jp&^D8@SaswjEpTQc< zW=NFUzU`ASGX6}GzXYaV0kuceH}2IXi@A+04Oo6nm!qc;aG1%ld_JDVR}IivsAZ$? zFIyj}2FnW=M~LfFe-DVjmoIvX%;2{rm9)JzjCJua-*5(2I)|h2 z0wCJvgcoSY(bWgtRkEt{sGEz9!U*e(E-XxVYUKl%MLRVp{8N37lPzYkd2=~4=}kQo zs`IvP4$Ox0Vdh+9iO;_c*^|p^u$T(51#asYUI!5*nDl1-@QtIiKv^#v9m{auH2;Ag z)^uiW;Q%PGJf_I8B&B3FL=ylgja-=)qk7)J z@P;>~vQ(*k4MYc1Wcfc{ng0OkK%Nr{h=wtbxpf$iVb*CE_$yl|REe@ULtWbsnf;e& z>gMzNB@{SoL#gSJeb`J65?cHKTTk|}7qx>LuA3gD1U0ymO*fO=5!p|fwY5PR7n7jsr@GdXeE>0*v{#^;G|xE6658B+|}P43}?j-Xg-jkWD~gAS3oq^mJ*;7W}@gwYS&>xbBedsDewWXwRI?eoXts4F8E)2P%b6OJ5<#QlBkawAh}Ez zxN@)=#gk+(lc41ZoKm!d5gIfeUGlt%^{s=D5BU(TC|#G(ihHj12!@`e@0TEsVHrvT-{FZPS-rZ|_; zKj0RPU_D!Ol2cdb8+CFfabf)Em?kWF4&FxT3VFduA3&eem0IM~{qrQngs8SU#0g#d zU1I*QR!XVjn;q<%E`7Rq3OeK%X{sE6$Gs?VV5M1YwZ>L=0W_GQTH`VJllf<$lWqij z=WZb-evc*=4!`mSauOUsE_%Gi21u1D#cO0Ty5gs?T9~w3UFaJa8X_ntXNbCvir{{d z-I2$D@xJPTifEF#wzY@|`?Z<&tg}bOqO;W)!6@b4J}6LAixg%`h8a;)&%TQz#Dtou zdI!~3eP!|%9=rd(&yw(f^YF^EPC*j!K%5%n+Kig$e?HFp(X?P%U;>@#Fe|yhz5(E7*jfA) z-fFa)+8Ac@z3qp?0iVG|A4fvdoBq2a3F8#s3(dZ{A~aytqKegxMlHP3)_JEYVADOOW&cf9>KxXj)7t5#0W-TzvXo&ah+Ns}2ZSgygGeif$!v@~9 z%)JrG3>v!7ZH9)=gQ?}6s+-RGjr7%iZp+Y&j5CsUSbw#aN5`+7LBXHUaBzK7oS$rM zAMpbTrLPCpwa&IEV^_=V9Hg8iR8cz=Qy?OX%rD}rs*A^AARxb#?p5eCBJ!T>C=M%- z0}c+KS4ZpUi%p)M9NyflNQfk*R^wq+Pw~NDA>u zmUebLmg_hl`D$Hxul8Lqy&z#Ed67KbXu*ty@nDQ`weUyq<4Q z4zU;5&Rozx>vvd4^Fv~G$*Yl#EY3v5SQ7lUF1h9;CG9us+8>yEYThp^Bw$fu zC>5MSJS0~IiT8FI(F)gM!1{}>54$bs=Z-lbV~v7^Wl9*~2*$lJ1(>3&{a_c4zMQ!n zVYgk!0J1EfHiFdF3i{8BGTo>Ud9DP|px%MQ8eZJFItWUAa2;o%wu$H($M<^G_p2fy z3|4crk64JTwh$eulE8kPF|eKLeZ9s|yiPln*1Tb#kQaDg4G>-Z61{XfJ}&c>K3OR5 z<+7^_CIT|_dZ8-0W3bj_0@s0f2cx9^?+T{j^SAW9e8p*I@9xz7LgIy>TN(OM8C|?< zMM$>=hI@fjR0(;(>GT3=UG7J_>WSKQ{$o z1QeZrH^xp_%eBH}9ZAEeej34I)z!35w?aK&mr}4IU>%34qv|iV7vL1GD3_!pAxBjw za{r^9nTWTRM1=c^0RzD3S=DYFx@ZQ}2L}gxyy>eB=VpZsg6L*mj{o3^EoZ9ghBwQkg2`JsbrM9O9{+a#!{X+40(ej8Hw00>74fCYz1M+?pPGHvg&a-{b$p^@Ob&5*;Qo{srCwwz!~jawY4^ zlc<}X;xT^^f_jG$nQJ}Ey($_EWqtoU)MY;%T9JP4HI})m2_G?+uILkr_$}!w1O)@Q zlTMlAno3mR^z&kTT_2+X1m4o}Xgd$$mL~ggy1sB{tC7Bq>5v`qdVB%J!SnQnW9W+z z7+HPsI1v?kj+6<10zY0|lF&9qWlhi1Olh#fnl$(ajUl0J4MZNIipna=^0iLSmkp_Q z7R=m6q!ic)>r~iVgXOxHkp!%o&(l>Q_Jg2hUB+|?*X_%0F$>%x;%OMQ?sp4n2!e{S z3jzvVCQPm5^1ymJ>#+xrV!~HgScnUziWZlcR#%u`K~ipA=-wn>kQ9AH4HR8L*E-hx zk*!StHEe7K-Y8Tk3?Zj}vM6nJm%)NyEoRep6Xy<9ZXrK*@iS)4v-77vMx@*M`vGC(A z)u|6ZBi9o5FMi?qU}RmdLxFvwcU%;wU>+DyEqX;>Ql9ac;l`|Utkgv+yfVDuKyA{l zI6cN+**^(~9%DGtQ$?|N?b%1L7;UtPp(j&#>stEZ#ZQ9Wci~=nQ(Tz9BQsld9&S#) zf5CvlJdq907Jwp(TJaZ0An@h$wp)86J!ImGS92-2Jf0ZyGh-^MW$L(!QY39+Jv9|& z-;uqYf|&MV&PFau?d_MMpkA3H^%BZ!?;=5;nb3dOc}hA|Jx9Y2Jn@?J?Cpay7o^xiJgm|HSWSgXbSyEKu{cj>eTlhTJJ3z?9tmJbbzxU1h2qxZ5dCh72l>ZV}{Yj!Yz|e zsX1_z3{{eUcN{{&ciMN0p+~7Lv`qA1J#YqMEPnwjsgPi>+@<~U&x+$(YQfZzQcN)7 zDyvW!4g?UXESjRX9uS(GOY|S%Pj#c!UmLK8?m~L95fzfBrgM8E{B{-bJCbVqnOV&J z?=x~GT3yN#yLA@J({41z0A3|hVGe5gmPfe4QIUsZ?3JrRQ2=yCnWF~}#!F*+x6b?r zCgXs4Z=>8b`@5+?y_(LvsWu?-!EtCNrGp)-Tw{$wVmxT$Fs0@~BOFeu#RIhjL~CN{ zu@GM@{}#~sbRdHkd{Gp8j@O0vNp8!5ep?ge+LvZNlNc^dHeqOeuI@%S^(Y!5 zGE6~M$swsoqr6A?!jkJ%iKi=@kJ5Py1u{U!aknxpeG&s4XZ2! zqa_QzD;unB-wK?$_Jx^oo$F@Y>qJG6TgXb>QHAbfTwmYZM7b%!Yzg5H6QOLVW zK9i>!^_oQ2!?YtxC{H@(PjGTqDkHk%Pli`f-00p3@R%7Rn*wTfkIKfSEmMot0Sh8q z*xWRKYXbYAzm`iN%p(>?=TYTIRZUj`uB3*sjgC1jrsW=6#>e-j ztY|wmdAiLa4$4fZl7`Ykxo}AlfpZhBGUGw1J@9ktC;0&k%~)@}_^6<2Qi##at_1px z#2=9BB;eYEb8Y{8v_O?EH04P4Id^c@W99}!F!H;W*qO&6?8R8SY|^gn@Jn`rU89tl z=HN(17#K#nw&Em`wo7+-q$e__`{;j!f_huRzraOyAJ5@Z_zW* znfedS_?rXwV8~G_KQj?Zmn7d7S3L=Z0h~hg0&1haE&4gI=uXJe*T8chqC=8@ukevr zd`PzD&I9t$W7<2%UH*oALTQ$K6@}7p)ITlP$>{k%udrWlr0jS|w4-xj0{UVe%F*)F z(lh7WbM6+BQ4^^`P9~Fo2rVX#voufLoi3%hCHI_u(#1kY7l6^j(-2^(j_NQ6Vtw9h z1*!LijB7Q28QG!1zY; zr5VZDP85>0?4>}u^LPWbhY9E-$+VOBKXvXs0Vs5>WG1kL%T>T1W~&u$jQb)TyByss zMc>=np`U!l3fkDuq4@&sR7ceuDC`HNR(Cw`2Zmj+s0)^;Q_41C1v9Sh&+2N0)M+J(|tYqdD)*q{j9S@uCE*G4w z8GbTk(d#nrQu}UicS}oJ;>>1SgKi}niCJFH=7x@@FEzk~;5{4~U4dH#>q+sVY)bCa zVMpXSUgGZ2fotgyT{C#heCh13(;wdb;E%4mGSCS=COr#W( z3LQDivDDQyim!Qexe{ahTU)C}tG}qRDXcZ|=!){sjNK8_-!Hie;>#x;b9!7Q1q5Na zg-OB-1B4`9;>VOYS@&wGY*x_Zd3;pLRB@&jb_c>_70!X8qB5zpRvj52TwRoIC#5~{ zyFvRPK&1Q69tc>wFa|Pm0ErE_X0;xu7ciqGa039OcYxwCcI@_>NpA8!FJZ>z9HqA2 z|AaUeHZZCeC5@mX>8F-sl-8R%D;ICENEl6RS7j2f@3B)Mq2a&E)3(E<@&nxHQL2Eb zYYg%Po?BH{>VB!zz-RfW-xxwC#9*@eGO?L6b9FzN;_}H!uU?+Ey*bpc7vxHako^X8FiBz)!iUW=hL(H44 z)v!%NNqKt1^1qk^K<8ZxVr(1s+nh*wwez4E=tJ9+9>;ZQTJ^wWHWUAB)EHIXDv~Es zGFrRq9#3V3gCZK&rQ}mG&q0&p=Uh2Oj&xl>6iDnn7fH{qHs8DJ3F%c+4j#iYc5aCPa$ymF-%e zlF=7j?7r`Pb4160IZnnu^e3!SpRvrKw|UpjO{Z5ZE{W+?jAD(M4A z>|sCg>P853w!_nlA0ShA(xL;}a>VzZ6iZ>a*JfNVKd!p;GYE1MTscP;&AhS~XE9zN zJrkHSat3sUgw|l2Ahd6_zEr5{nG@5tJy!rF*u!{-8mg+8iF5^G-KM}<V znO3g5{(~8kC29G#*dOhVQdjxkBh~lz3JJ@J`099Ei`GdJvc$9kRdq0tjo3BV)RjgINJ_Wgr&CZM*(+2#0W`Tkod-}YU`q3( zk11w*-J!d7FY~b^{EfB|S8iTt#5hE*w31an@I76_;F)>Xeu4y^1$N+#+`#eqv#L=Gy4-(yhH(U7W!CA?*=AP`yD5Hy<0{3UL28da21(8YpOMqfLctgb*K_nn<&F^`2EBMz$rWd8Y#eANNl#IgY(Wxfe>mRr35n+y z0v;RoWfF@eP~FP7|4m0EhFAY{y2$!}xRaoRftaJf5k4esy!j$_5kniv_V_Nw6^_2Hjxsv--a60@1KWg#u? zn`viv!RFn0%XZcM-Fdhx9!M22^0MH@V9Gmr)KNHo#$J!LCfej61qKnx{C$0rPbsX# zuhhJqwF2pk`Tv+Y2lmLisN1HKj&0jEJL)(c+o_Ii+qRu_Y&)sgwr$(KdB2P2{)IYE zowN3wW2|kPMoq)U+)e&hlI9TKugPe`*lpfcdOl;=dcdaPH*|Ov)ZcJpkFHq?4 zY;g$6Kl#L$Gv;E(;!_LU^SH2Xk3B1XL#&cdBOoEG3PbxQmS?`QJ(Z5Td`)W?uP5~HHj`@9J zk8ghK`XB`$M8IdZW5`n^u_8&CWoT?_K2B1}f;@<+>)PXbp?%w^KLAs2^-bg+x*d!r zQJ|ID?h}XL?JiYIdMs801m?uZs(ijajaUBnpN{!w6XWl|5IW-NTh1%42swAgrNqx% z9wmu*O@L@pY;q!}yfAEzM34V#6y;cOQU~><#?F)=*!o~G|C3=lr)u6D{}j##6wBq@<+f5rp=wQ1=a2j zrtZ+Cp=1yr-i5tVP&Es-b=w%W=)~)ro9wc-?iPI0qBk3ql|8-Sf%S;O5>uRU3ud%~ z(NoLrQE~D*agb&G^FY=iUm^c9rpIOOu_gbRYcg99w^Lv{A>y4-9 zRp_bk>P>*S!rynDNggYujA7C;pb?2C2~?Gt#wLDRXj-rJZS;t0b=4Q{W|2$=es6FQ z>Q>JFz9DKE>PgG}F7A!L?dSV^-YfFR>N@{s-FqPkJ-26}CGFSegrE`+$CF>ymYPt?P_1dI&?|mOM zGFrzhwUSVdZc4aqBQ5j zpE|W2(fA2UU@wn|=P_E}V@pazJ^w#&!fWPmoNAM2SATA80_R(=nMR$gu=W46md};w zS<2Szd}86?YcLG||1iVA9scj%>$I)JIqP2KR4P~KBDX%oMYEdLX;Wg_W+TU22KfRl+xO9r z$z~bW7%sXwa+P92JJROra>t{{qy%S5EefYF>&CMbflS5;QHHDPn-XwsEW8YeiMu-d;FcpK8?vZw=ff*DRK zrxA&{xg5F4mv^&s#&+k5lNS>Voq|I@SIGo=8? z2G+T;I>XLyYmbn%(BGRD@6dG>qu7#A3~?Rz61Rh<9Qpk>EOCejz<5(#RuFdFY4%pZ zzP>(W3=Cslu5^_WBTj9~nf80>UgaI`hcgsZCI6nKIO88Q6;wJ&Y>Iy>%2s?xOklLa zwKtsCtcn^XmjAP&A{g3+O>0Fa3m{>exVpCU`1X!3A!@rtXVm{ZZJvz^%TbSz+jSc5)U5n#+eGmCwJ7r zZ@1NW?VD;e-vmR}`EL^K=J)kQ$t==+>0X3De%lqIbs02GSOrlstFdJIB^C4sI@*_d`y?8G6vr-TMdk4NP7^y-_sv z`Ebt<5ALO1FcU3mP;AT2!Ja|w|An~0_~pcT$lvKDz_yE4Pn@s^czR5x^i~J6qpWAU zdOWt8D?`!;oE?|}x~-HvUn&PaVn-YxuOYFx5(7~goSGCYj!xQPa(oh^kEsy(y^1jz zaA#1$w6t>dAA(9!y}*^6RsX@C>07U;hC_vaL~s-L#a0I*Dc^^SB==i7rCttP#(1$O zMQft2-p6K&tD+LJ;JVx*DuDc_m^W{EuN}Lt^Zy>6`@g#Yyc5O$;1IxzaB81;-v4J@ zOXtal4ISvsM0Gh?=q7qZB+*l7@q`FK1}iA%$a9|#=fC8V=uv(8#Yk!=HCn`S@j#VZ&@lOtg3{SO zc4Rz0Lw7k{uLqU&%IDIbN1E!a z$=~&)V3e50&%H8avime#@6=amy)7A~xTvb>Y}+Um)*EeUNM#LnnvA>V|D~wQ;z;r< zIXHR9C4Uq1{Po1$V)s90;Y3b4lr!x1loJR*@ng!e-wUdlUaEK9`Z!2l1#bs_mJr`< zJz}=2y8lFP3BGX$UuRu!-X4ZTx_cOCNpFsgF+Lz-Z-={(V0u*|HSTl?FyS`uG6(8f zyHI?o*``Nk5d*xZQmT&|>RuqzcmeiEC3Jbc#fQtktxiAc`jGoy1@9p#MTXawOhgW< zMmo4iZ`jA*&h6mvoRK>;j`Hv3cdxTAPyEd2oUHI37F$pgH_oCIq!V3Yc14Cb_ z_F&Ifm?z&SU#`JlD0#XUJOc^!1H#1 zH*%u~kna|6HJsr<69jgLG3dC9Ck6+CEdJ)3!AiqZCjs-rkfs4`UTCaHORNg6sA{@& zzCca=?A5vAr=?6G@R&`6tmHvWLy@eQU?XkdnZs1a{6wnxuRuIEfSQg_uC&qup>xgX zkJR$G`=y1wxA*cvGMlFvo*~g`h}M~ZR)ar+zz6W|NLi&`KTZd--J3pGD@9V%!j#3A zbjRkebsl)nivxX^0fgP;As$TLcKrPM@I#QYRz1u&1jt%Z4kUTW*V&<}OfR>w@9guZ zJKu-HFhzTHaw>KV)v=aSo=&KP(yL!Mz|>@zB;}3#IeE|YPgID<{3Y|%kZU#{XzWT_ z+pR3>c_|O(!v%ttw_3FO3;d|2!k7f0%J$tA3$h&X2XhEzp8m{lwDn*8QurRn1tr5! zxkt31tQxSa{^@l>!MssN5zrQMC6KHm_T|yNUw>5e(2StR@u{I>^LW|-)OWskQsFEr zLm>qMs%7M&p8ZZ}b|@i==H#M71s011Ty9hQ+^C2!kqqBLDpMN4?Vpw+-0ZWS89_Af zcoULqW5$@^<@{AiBnJF=2xNaTHfOjZ{$nVQmR{VS} zos&5Yp}!Z@al~3Jg|D@|RhJ)w) zm)E+h6Q4U6k`h6tAC=L%cWmB~I%4>XUv3Cqts$5*;m3+F;b!fUKURa4PGsb`y_gp7 z@UE80&nGfVlpV@JobdWRZ|(dB$(!i-75bOINRNO?UnW{I%5 z1NJ$dAcp^2#C7vW`_@&_@&yP5m!)8tu9Y@x+~x|Gw82;36IH$tY|aV+KctdJD4j0k zHwW3q=C3|fFnh;+*9$8`62RZuh6Iq!m z_|c9M5rZ08qAa~cyDZP<35ks#i`=pkgVEw@OYY)%*vo&6Lj0{vzG-FNIA76VbRi)Z z1ClD_q@Rj<;$6$S@Xn%l2Y9)YU7PXxfsV9&8_hP|zMaf1XGM=C>Is42o{XQ~8sMLU zE0H-q8P=})ye4@B^wmqf0m!qoW<#B0C^MZ_{B>!kq|(IV@zp5qGbHIj`lusnLU3vv z-Tfx(72vBA2V4s`#8zs5DAS-)fI3xuqh6}?`X^ZjK%S>{2N|s=xu|`q9a8dfEnxALtSx*0rH$^xRs zeOxp(&#Nm+*Ibu-)zJelGRTqjOmJxftSTHW6}GC?^RqVHZ1+omDQdQkQFY@0z3X{! z2*K5g@VF!c3b3@>KzKEW3QI>jr6BK?PRx25%?{DhCqiXcz`Ve$uaIky0KzrO#v^#u zQ34}+N-No9|AcB_zRYeHe#}Zs4hXZ?S5qW>j!!)5DrKxL%H{BH;N>@QIA@4%$A}1` zqOfu2^Hb~|Oh#79z3EsP#59*fscvlTYM>+D{b5C&J6?ccfG@b^zQE#F=C7`15R`OA z)=iH!lbG%BG4rc$w;ot^jc0amqWG?ex#8441ie&X?kpaz{`kEC#M9XjOD)EvW%`7I z$&!xmuRE=5p8?sn5P6QUb2&d%>Aq_qpyUt&4}#pfx{O4G2k^PuX&z)FvS-3w8G1>@ zL3KVYu7RpSEyaw(p!k$ezfJe@{m5!OR!&CipX7Ka>t?ka28q2#6YE;s$A;6{gJBcj z2Vb@84OZf;lA*n)-NhC|E2we2f0yV5tnTL2*w+JGJ*1`h@Bk-K0b^e^KP{mbgV3b} zr}j=Pz_`cZ)p3334+Uw6WC#$?>|XZqT#ON*2}j`Le+6Cno?^n}+I_@z8|#|C$8*Z$ z-f>7637PFVX^Hr$^f?i5PZL$lK!>KWQb?s2fCBS_)Ig|)z7j2fXMVdB?sE7^GYy|@ zMCKt~H1O;5sVf88_WQYBpbV$^cNnBQ7O_dqx>Gk$6YQffUT6h*me$l+ko-njmWsr{ zLRHD8E)eEUW*LgJ<-f!7-H9_K_Y)WvXpGv= zV|!P3X}=KGD{@g7>;?pYXG+k@-R<==Ulw=!jR`9m8Uv}%wLUKRjhFlETx**mDTe#} zMai=ssBoODl1(^fc3(N=p!tsEu`v#Z1xE0Fg?&*v(aWxS!IvWFqOn(*Jl-6~bBw)o z8-;zrDg7Ut0@Iuf(SH>D1vPmVvkmQsw*-a%%>p-t-8duWL$uY-uw=T{Qei{dy9ZU} zXBuz{xkG$>Z&ht}R-+q}rH$|Z-YRMuuug(hHh`BL|F6Ms4+R0gRR3ZXy%{T~)IHVJ z%?Df>lvSYtIr%~YX?o|?c>QuqsVHDRjDHh^r!BlMsen)Oq$vtkkiX~`cemw~9MF)y z(6H~9qxWgjyyde9Q6Q%Ne0Q%I%uPFys7|TvW<#CC`2uXdevCOWxSn>-WbmJoaP<%{ zbRfjv&U0fl|1jY#1IFSyi~P^;Z2$9#cPe^^D(CSh(BiPwW9jwW$ZIQhFJ*Vn4m-23 z)T0Ix6-6QYmxM69+cmky*>S6^g%VrstSzRwJpPl(O_aUtE zyEoFP$>ZO~w56ReNwznYw_;Jk`ZiH}EEbLDb=S^$a^d8(Oxqcu1On$?v;~JSwe8D= zo80b;1$rq+LTTW9mF%sGj)})z8_Xvt&+DEAv-Jhsml?3j5sv}qDfY|B3O0R%NGC1z zd@@x#%@0n)O_YsP2=PHHi+ZPIchGOn7+q?yPjdr`YQ7S{x}ZWJFQJnJ`adx(yf_hs zNM|sQ<%zhouM^32Ut{+uqlLA!y=pJvAc^kA@eEI`fXSB9@g*@gkHwd3f`G+(p69*( z;|m6v>L~dFbs98B=@pF%%1k14(bWT-Nmyc|eifx#r}8!qHksG0`-n3UKqu>={k6F> zyBeg&qcMcdkal~q!q&1zC04{8yR&la*hmQ{Oh5Q<6u4VwZ`5`+PU$3>E9AG0cdYEH zR29@*a(Qhw5cwog(|gl5wUXN00zweaYjgV3AvOGgthYfXLrMlG%rf|=Lfdl19oyKz z?LrXR=v`{vp>U?j7C_FBO-R-qI@iaIFgrS++!EG%$7~CCvlpge|1B#`@>cW%z}%r* z$P17y5|K9K>xk>Yzw%r9adl0=rHRVbuLaxL`h-Dq-s!}> z+GUsSR{6j<^-(ZtnlvPg3ZD4gFP0^eis-EfD)IHMTJMv13&^DZwGU`{lk_dI=l!FD zoNBbRQDsti$MtqzUyj62KA_^?Qe^C;S5rJh>-k_II_H_h#kj0oH%z~R2At^pPo*~( zC*rQ?zM)xIw7j%XZ!LtPZJuSUb>?N~mvXkO3@pX<`3)16;BA;a9n)XFCMiAs!ee_a zfJCr_M_T%&pm*5UF3zgF41WBemVl*9EHPm{4<`nFVL&P*%~gwTGHSJFVNlA79h0{y z71Z>SIT>r{a2oi}HhWfrEqEctQ*>c>#BK=r_yb0J(HXf~A0=%Ut=VZ}>a!+)N&`t* ze>lfHPlrp_+XGfiyVW0Be&o!XP{$$XGWq?gw4R2w9)`V@IP1!lkdGa_kz;7&Oqg{Xs!xv{S_C{Z{J#KV_U z7;9ld)n!8t$b2wvhyHCpLg*B-MB`zKy?9 z)L<%)yTTqMc`hfxHhxa;I7q;*5bVOX5de11CZ$?hmdIabjjy$jBHWF z?h4F#gW)ecIh$fnkypKUmP}m?Kgf$;}pSwO`=?&s-6;p(n>79t%$eV^r zH=_Id=C*$G)+20jAE>zY2PPeLS^%*P8!rF=RzsFmRM%RbFSq`4%lmFFUSu z@7JmYvfb|h!{jUO7b__mDW}3BBBFUm-d^(k8vJvLn5hQGTs&%K)5!(ZqPK_SbS^u# z`d&;nTse*K&4t)@$0av}8CJ--JHsSyQX1_)0)pWN(^bmtzJc9z6otk(E*Ib}M3|%L zW2v#JY4FhZf+HIaMqMD5nzkGG;Z#P9GyduxA>w9Gg4_%2>L5q)8xP*oh6~|F>p_un zEU)A(hrH5BUkm_Ls4pgkjMr&;bf2d z(=hdL>Z6G)lPcOcQ*w~xQrG5D;gLjLQC+blo7q${ARfPGOKApnX7Rwee2joQVZ;l# z*6349;?ymWpT_`=9gjXQdfx-Z3$>GtT1$9N~3`Y4gdIlTnX)pl=~?z3WO7yb1m zrTV^(+da%)dG?Jeek1PAu*B>B(!}m?qrIDDrgg}F`Q*=YGr2e2T!`iKqNH2ygfU2t z5|_2(pZpHaqOTnP9`y7{w5*3#@dEmB(EH<2Q8+5J!gA`Gh(fl=A>H!+%i%B9PoWm4`Jo&Yh&?zNuhse-HQYed5*BQH}f#114*I4lz7bP@CTb z52|)lU!V4Y1U3DUmHHf8&7>6FzdRzTUHOc}5TdywsmUaYFc_%2Z^(u=t;p2zVQOs^ ziQAmRg|@O4QhE6>vyYMRR%eKNXaYAP$ksA2v?|N2(V1!Yz3SxbP44*Ap`{PvCC-j$ zeFo($UidVS{9ek(W+IPPfWr|Cyx-75Mjbo%?SorCDDQLLUNGFw8i{^hh_%>d-5XD$ zk~7$nMXRj}4#TN8$*$6xz0KzyV-ylUXiA8ed%{a2vJ@zj`-sai+SZ`en{Jov+U{?Srki!(x?az2Y!;i5T||ErChd~y-xT@FbF_8O*X(EU z$q4{s44r#86&iIYQ=5-WvvqU$SD0PO_EeegR;P}Uzz>{E&jAhBj~>kHZd$qBao8zm z>-9D_-0@x{b0(;pgbqkTAFR@~rtLB4z0}w$s5?msjN-~%7Y0JiOgEn0cof?!Ejj(! z<(rn1*)t^00|T-I!^SX~!LkR7>zUNA-Fa5ozKp96$T79bZ+3yU_6rb_H)jWSJ#}Z( zU8)w@ebW~$UjHmYx89i!;C(M<#0_>nc*tnCYIU&h=LiSTNvX%1&m}9bfA)8%-@kck ze*+T7KD~aKnaESMA%9O+(VyS@bacuSE$TSdIUZ{h&l-#pgAhEL%IZ<+gmX@wSH~Hc3wXtt$*$AVciY3 zt2gLFE7~{P^~yrU|7sk(=?mG(L_XRb8`-lxTj0DEcm4S&u)|a_wrM$B4VC{Qu5EPr za-wmhCd>CPG&$6Tz~|*C3GJ2=yo=Y1!(&Vl$ZgehSC+bkk@+G_2l`GiqNWXuVpH4!V`t^@~=gz0XRQAE$b?WIrBmvb#?Q{L`}%O}@gW-p$7EZFUf z%7%uri)?Jj?Kn5mGwcOm58s{KEIiO!QuJ4f99P>2?$-sy{57t%!7uhj)!UD<#?A^< zWu6#+D0i3>HlW|Dbqav>Y}uPRQavhpqVXqFHJEE<3||j_N{J-h*X7T&D`kw51RTcc z?Lctq6;H)Hx^(_JcHDkDB~bDbN6C=*Bx2tB)&FbizV9jB>nwGZpOXgNh5_qLNN
    VW*=E{wPbj>Fo{k2YqrByF-q{fBmN1NyNwfrdB!kqM|3f?@QLV3 zDqcN?+@$9e(lbUsJq2&wXbV{F>_eHsF>BG)2&g_0USLQSpmI*2aSl;GDmPI87?|+_ zS2Ix^SK9KBK^fcQ^(=P1Gk{rMoO*{IyzwmD?DQKCCY2)(P>yA2IXUoWlk6=?cJMGy z-g27(_t~I;%YZffxe;lJ0(ni!V7U{HdwvHd-<|>@YR3sxSg0=hwbGB)gh+eqV`f4f z$OBQzJEd_l;L1yjprv%Du-Xbv8sQ1xx?DsqeD?O>LI{&2CQkFsyoivz&AaiCr7Eg# zs&=_kP4sc7<`%fy3q2y6dn+T%h}cW{dq~OoSL-)L6Nc>~&QynvHf%$FGX*!zkFgj0 zE-EYbTD!-&PUiJ$`sy{bPD*l^VhDBGD#GP5&cg&(E5>pArt7<}!(PUTw=E98uyM){ z*cSKI$~k5+Z(=25FH0 zj@M;-VlRy*fDInckhC>B;fcMYf`mwN0W>~ z1SN1gVLK9|ciUf}*EWA5WTHLMjSdv>Bn88az^_Ef-$CVf*&GaJSuHqk-u~UPH>bC* zs(%rJXG$@rbDk|_zi}d9xl>j7+&edzu7&3)e==Dl_2*6Hnr_lz<7?fj;RN|e&<+9# zjsFR!M04E%>(hz{0$P(RmA8dFoizCb0alvffdj0)FJ9Wj@ z%p$hKzKaz$XQFHgq|T9+HZTB6N_K++qOg@32gsS-uZ85jWv!=2m)EydhE=lK3$=oxsy2ZEBl$ zv@(?X&`0R&esLm7z8pX>?K>5|%fZRB-mV;p0@}Y$5na$Ho9AJ4}F2kG9e;@s(h$tMq2kg3ZN#88)ezG6xIMwY7 zOq=s5lr?ury`Je~?B_=zEIs9!5G1qVHGJsTi8HM+dA z*3yC9)o08VD8nh|g~1hp9MUw{JV5pi>*L7(i^P$q!1jiZf!QX_+Dm+9jW}K;^CK@Zyy<7I3U#aB$R2L;+WBz=h$J(vEnEuv<${O(?+p zgu22hs_2fyivNom1$-jErp*XVH}gf{$f5iWwh69j>(awidcB%0tPy_hNUvzt_!v(t zVc>LVz0k`w>c*oo@gp%rUzTGjshW()^SHh+#JWzaMG0m|^LPLwUbtd27@d{7$@MW| z^usCVHC^^cQ%<_zwfoB;d)OG6_!2`w2z3eoPpbLvEF-Ts)YHl!$6ctDzQdyoqJbuO zvh)En8-ord`OdNpUP`T>3fyEluT)k-Ct(n*PwF9R0wGKBZpZ^G?V71%s@2{bFfLbC zufjrjD#I1tttI;-@lm>8+IXUQznsRAy%Q#`N|F;!-vVS2oaEoe(gNQZ*m2boZk{3x z360+%f3k5^&)gWlkfGpY;>9@pU0-n8v*F869}D5>S6$yu=&bj%-REDY_Q87$9q%mT zY5cI&{2wTtZXX8P_8;hzm?fQD>=4-CIfqi2XUWyNe$gH6HzbbQ|8!oRjzZj+eRgjG za7|zTnc)lk`=t(?@+z~cx{ScIGkGY_U}rvEZe;DmxubSiz6J(i5?}8XA*^&T6C44% zvSK(Th^p~;K&@7r2PCrl;)V#KBH?92|G`2>y+(x<59JDL6`0&%rgy57nIEL={{w?$ z&Fo%!RVjYJX@?5j z%WY(`$$C*kGd}S;GH5P#@&rR=NAGlE20xqqiYdL~embmTq)BtzCA_HiTF6OWkZn2H zu{gD{kq{R2U}(Fle*txzN@>TLbAITVWATD@a9O`pUnqW@I}*Ei&^#)%J8iU`jS?NZ z>~vQJSQ<<3CAVUo6>1a;oQr$4B7V7Bv+U_pp31$Y->I$rb69#gR_7_ImYUP`I^{$2 zW|%Q%b(qw=>g>{_oclcVT*GRkL#uUA4`L^*( zvUs!HNW=cBLqEJ{8Ewwe9iDtXHs+QRO;|`0ijSB~kmx5AaRZVNCzb&&(;(KmN3)8h zr0NyViQ}8V)cJ*(Q>RkBCEU72jP?@?A9(eiUCw%7 zrK{l{<+hg(7q(WuLD@z~{kA(~L>f_|o1Tw{T*icTvYw0;d!bf(Uk;YnAIkwRK~}~B zwHxQfn)7;;zVgV2IEQ1O`kJBmMk;0&Yj$liZ?4s4jGZa~eK>o(TRsv~mMxXZmE~?p zmRH}CRL(86B0cTjk1~V}306eML?z1z)A7Fe9whu9wYb|5aJWuRR*0{Xd?xcrPc>d; z1XL+#QLSJgGP5&VbgSyne}7Zyft9KICh2%>+p}Gfv(;x${~)QoqITrCc1Yk9$k@Qes=t4Y+5A+?$W* zBh_!pu20z|1ijD7Gd`lfML!UCuSFTii}GLQR3OB(2$X!piN-N2pup7Pt?ap;>rnE8 zAjk_489VOEyET4!ZStm^>%gYnV7gM8?@x{hB>aZf*tGCN;2NYD&!$k~tVm$r@{bu! zL72)a*a;ef5bo_UW*_*4IwTz{xA`F-eZsC87Q$cUiO)r(C_9lBeq$IaP1O`8Vp)X;VGfp(QXV{a_A=wphaD?Pn`zY~5p2f~aj`m6oXS~xUVot%P-fjap-_oEh-rCZC& zDGK;9KX%$FWz!+%cEV0M-sChmp338d-5_7wncV0F_J!&2^(cj=)KpK{_Yy{x}DAOw8L}7KDNVoSG$;GbkjGf z62WjMj#E#NnW=%UYryvs#h4WG`iQr82BNRE;@s%ViW>Vsuwb3ZbY|d>q$(bFM6lWa zrrds0iDBwlUujHmE>NTZOEKwj*UcT@)r6}S*#(?B_o-L+u9rTvdhALQd%&);gS$&( zf-mcT^sGx`+Qel>t^FMK{>mKNfoFfH;~Q9;?{nBz@Z%@Rt4y` zAiF{e`u4XW#mY>vo%We<3v7YN^r67{)>U9tAuxT}_FNz!?^n_wV~4-9?6CZRSEYse zJ<2n0z;ejc6ysHc+|~N~EW)iKqy%$wPvpLbAXMIh-a#5dWSBHp>kxFv$4$F&9$CJaXbhYq{5YPjFQsI84!o( zD*?)L92zHFD!5OVD2mi?rwsQ`c$*F9VFYsdEQptq6n_Y1%OLvuBk3@s%WD$(15_zm zp^9Exj>{W*{W_A$x?og9A-!0R3SFO|0&$3QPCrY|_xPi3-j9XcqwxJM18IGgM5U#P z0`ZRPm5iqFD7)6C8UNhoel(~3uQ_SeSlJW3$EveDa;zgbL4JV7i;)s0gWN!KU@S?L_0D+bJ1)SEySBw3U$hfjD=Y^|e7l5nMfh}(0 z+1@U4i}FK=7KSGqfI>Qp>?9y$Ecs^7a6)Zs#J8UgJA*@gb{ooNqFri#6m>G``|2I! z@k)%=*9s{zaHlOTEs~vS;vEqJcxIvp{#_8zQWq4+4j!_MTOYMeqOBem4I_;pZO6gZ_)sQZ={}%_@fBD9 zOYa?N%|WY!Ne(@%fELw`8r#TxsgjC_8_n!g8tc$GPa0+djH-Jl-hxFSk9fD*1)F1D zdTyUkR^Ffe6zr6uQG4Pqf%P}7Fu0h+C4N^af|d^gx7}pO{f8GD(l+rDnj8bgC|b{Y zeX%z#6JLwqrW!zrlrTO)VMdT-zvpDdPbaKPo&yGzoaoQg9kW0G9E3zaV2*_pey0e= zzcLkNBfQ1A(-^zWgYB^wuPL;dPWR~RV0zw`;{CdFt)4`=QgD}} z3OFet6y3lIZ)f)GxXAAb&YgqGA_qzlBazll$>39Tc3F~ga?P`s2Y|^neVtu(#@QJR z?O->a2HbGhUYrZqcdUHhlT^ig2tP@v{YyPs0phMG8h=iSF#Lk$VKlWWj7PDh$#6x{ z$gubOeKViCmzv*+^cJ_5=B`{Mn=)G(@SPk`olV-O23r+w61>sj5lp}EmgY;w@rLm@ zu+6J~9xSoEw3PnM6NH505wFP6uz85`6yp03@nYboG~jJl@u??VZq}RToXu4-jYO() zA~w&w=C=2&m%S|0)_Az!w4aD%J$O#+G(VSu*{mN?E8)3-e5TR9{xZBvK44*N6BXk* zEu?euPfW2)K6PLjP*taV(25{OC+aC`HPXDIDwW2jYUHLYZL_UV2#Lilf8A}wnu4!$ zJyBz^p8d&-8=6FvX8SkN@I0IHB%1wpDcszRj#h}JK18Cw<*d;B?8|T?TJ)A{)9?4r zv|m3HJlK$-idj-7Kc7ag#{>G>FzvK}YY`PG8m@R#LjTcF>u%DLZW2J-4nCDO?38q2 z!M3tS>M?m=(hQsseX6>X;=DR+M>UkB{Z(+br{DLb9HPUk z6rp5x^spD{v2$d^c2auX^K`^R0CHSh*~GP+*N$;6uOel1FG_Zm7bd*!OlW|4ZkUmx z$}~~?A$neAo3@L1*}cDB5AId+Xq6W^3Osdh`Y}bColMibfq`OXEYXTk_uBkzC5FeEsHD;#JS-ax>V3MTO(O0Rd zqAZzyh1h;_Wo8=DS&EyC_=>MnDtM9G@X6OUjR$%6;MCF#dMar{R6((Mi`pP~#YkK) ze;y$^mbq`H8A~LoN@AtGPH2*_dTcBQj4CEFVK!5ubRj?S2i{wXdscot5Z$z&p=&)fF^&kz@4 z{dyoX`iGG~&}1}`x|b`#(V<&Cnbmk){#Cx=yu7T8#pcF)>stmG&5OqHyia;Kub&## zvgs(?l}pYWcXo)-Ox|k5q z0)f0P7EPMybJpQCNaMEs9cC*}tDBd2*kN!U(Dv>T(!tV!%kR6>l50QUYWh7|C2Bhi zYWiwtNf8C|NCsK|QumaN9mM~-Y{`dzt~8UG6qmyz1l;hbYbry-0eN`4pxt+bV0y+6j7ns zS|NKPc(}_RE>m(F*ZJPq;VxG^`zVsv3j1e2H_hcM!Dou`rjRB^n_e%>c@1Wr z`!q8q*F)gZR%baA>N*cc@bzO_-X&F!ooQhV`!v%B8;LTeel`0uwh_1osU7rut;d=*XxZ~2l>~{OXGMiA;Z}wuo8JYYQvj_ylcd~e9y82+ zg_E+gnu|j)Y*6}7Hg60HwAs((w+M$$1K*vI`LG%l>|fLCRuE?Oj}PST#PAJM-TL5T z$)C@-9sxfnP-=fmVIvd-+%t32G}GtFIgRPi$KHxJ(!U8_j4)T*(-Qiu=fE;A07 zpbLZW0YK()KX27@)~cOIr*@xMVfk2APkYh`lK#b` zig)8o+8?{H@H2-v4`lxLR2j?%D^@0**BeMEmh<5YN$M^&9hhf%*ApnU#3$5Irx<9@C*t+7372>?|)F!?txDcV-5n+05B6{J2w?OrSu%Wq3r1TZR^bwZN zDP#wvu1f)v;gVql2xgzogCdzT|NPJ&bgc+x9_yZGng_=#`>aQ6F?0KreeQh4tFtbU z?jNKlh;Wv`1LXijW!4pL@dvK`ct+t#uzE!i+`}~kF*&}3yfqxU6gw#WP_+$S2WXS6 z&TQMRI!7Q_hh;GUhNAQCBbtEYniUf$9JYmtR>X%7$xAsFJNWO*m) zo8Kwabe|o3T15S`SDroQbynw#MFAd7+VDsvDZkC9r??mu_Uyh8Mx;YZY-I59FuJpb z|MI3pX$Xpn?`+zT6~_oCSC^?=kSq1#bI(_d{PsCxcWl5Z>&E0;d%84|2&1|Gw|w*O z7iVfuOf#hSUB;Fp?);~#2l;Z{UqnwiK0!4)RT3t1s(=TA#A2DUp(~p&st){vI*>{B zC3Ln?r=fwdKeil22q4Y*Feo6P97x=Nj*+lX2?(wpZvni_i&IZl%1qW4w9~+c7WSYY z{(PPobNIG56m zE?SR9avulAW0q^%5sRUQkOlPUR{4`$P=;h@vq{tx8H0R{9bLJTb2_PYS0H!A^s+Sf zaS*)bJO*36dT^8iB5>*ZTnU|_YP;KkrV+H9xF!^n^gL9UGFpPPyjHHrjxyZ!|2Ja*Ob_J~jZgwih3AIf>%M+_uw(NQ4pjXgmdm ztE}~GCahrcM{$>yrNyVkv*n-SEVB5F28e9YLZ26FPYOIgMg|$}+_5k{m>~7N>9Oo4 zgI4m7ZNK%mq92a_T2QmQ%n%gbG?y2E@MKo#3l@%R%`K&wXig&#;0hx^C0{Ug1Hl)a zRkq=iQds+q^5De#;nf|4R5-A*P*&OPahNca$-`*=58q&VU#_Hk8FNut#&nlB)kgsr zK>V2Xc|`3@_E^J@(#!V4H%w#5I{ek(H_W484P@I2*iX&QOh#iBz$%8Pl@I1o_<{HF zCmdwT4(8CDkWR`AE?;KSpU@Lt?F#djPlZfIR3LXt>>Zz|M&~|Q!99>Sv`Lk;S|7Of z%Sb{wuH_ChOf^ij<41%mOSf+jZIKdZxogO=%EGqkkCGlJV)i1aohWlMSP&+7eYTTd zIJ;e0AaqiOsP_|-o%$KlSQaJ!TK*!hg}&-~Xrzw=#Zp+aOMLsLwP%KcXss8dkOs2s z>_NL#%XTyQ?B$x)J)a1kT^h#DHB1$lU!kLA;1jO+uJ3h;3T@-+pFe0cS_(8_kUCAy zhb3Ot@;|oq2)nHx%;e3Z0s#Zf`;3T>Nn_UQ#NhHcCa#$ug$y7iNSRY{Ovh|ys9$y< z*S8n^cg-A!=AnPS$SLJwQog+Y7XZRQJ-=A4>RSWrNG0+m#|z+9ugDWE2Nsi1$R9l{ zQgm7o_C5;YQ;0|Nu7l|EJ)|2hwo$(~vsK?(B)rm(2H3KYmONPW&AN;j5+QHBUm=44 z`bb*enec26ulE~l%6tBzB}FoG8h$eb0WAJ|jNHipd}Y#rXXj|pRJb7>3|1H24;-Ns z1Ybw!gXxKCaUw!=Q|wOSdLlcB5b)9h;0TMaM2$gN|LdNJpKZz^TJJOx&1^ z;l3eA!5#?qKnFcQkVQWju<~m|6iZPj%aEd~hWvdjeP(iGVp}+IHv%pOv(1LYf z6BckM++R2Q=zF0r_4y*vx4t7<_`xJc>s#s4bt-JhtU7Z7k3og^bAgv#DO;X zVq3jDcWFAx1J@`s75B}Pa`@x=?Zf%GBmd^3;pP^3}IBawdS*&fHr0 zLGEQlCKsI;B=qoE(&`F&-^al7Q1mx`s7J{l2xS`ooD!B^F0)J4NJm*S8~B_40O zhXdB%+nxOcq^&t4t;X*(VN<-kDKr99jovI#8@nNcCByC1J-7t@bcWjNiBtZ)HK(Gh= z_W&&!mp_8}0SofPTiaR}`idOQ9OycP?>}QIa`7(TCOh^w$?P!zGo&F?G1q(}$-5(@ z6u>UCVmrVE018NPzOn%ZHhm`Ur%x2E-zdZd#I+P$ zjb9}Xq=m^p-zbp{h#9#X%HwC9k|=XdOB6do@wh)fQS2A|LFL!-@&=hWb)H;tL5jTo zzcN|)Wwl&-UNYj;DVrVcjODg8TK(N96{d6#9U})qjIDVB-@8jzj5Shh#SI0+1Po5My{X`-I9q zfUjJ;Q-mZ==JLRzCt5n$Swi|oN=-H*>{&=3~4~5Hr7FWWbTeW<(yjspZ zBT=T0hQBi4P22cvI1=3@&L*d_GNgF%hFf4g_$I6huYC#g54d+Az2Q^olX<^fkMjQg z-%`0?c9M(+NYjhb(4XK|T`=725wZR3UUB_;ywwfc)wH zJ~FU3+~7O|wTR)Tz^_<;(FP6z&>G^+!+YSEDsRjiE~6j36H4yy1K<{+PA;+c9}3YV z`n`*!Wgjc|QcqJi@I zLaCn?mrCbrH#nR}Fm(vN7A;yNyI^K}_0?CaW-M5+K%Rd3X_%AND}PkyoO8|*+`OMo z)pT;Y;5tG$fmnc-(F5QJ)!~JU?u7V}Z=w?~DLG{xfTfeG)J*V2!XyHJX&O8?`zPGr z*;klcFO#60t0o0UC+f(1;9M7cbIk0}@o<5~Y!d5VheZd$(y=u*&j!DPJ@Dh}0giRP z2XHzmO?daAOr7lj=qgy++ExND0x*-94#4ZlMdeZij>kiDaDhN99-JSN>1M+C<@xJ* zrwQmt1xutEx>0*8T+(+cI94MN6L5dMy!mb!Tnscq^kbJ%X8^2hnkUucBBXv}wcHPD zzIeDh_zkvmiIM<+RN9ayy}*mj(;`m7#uWjyfFCTN|Be}ERoC3OOY^G3c5c{7yDT=lvq?7 zK;VVG*4p7RX_x~u#sYkOige%#W{$Iz*tJ8g&+Ui~a+h))v2aP1{2D;jz2arDpZX{C zhf_9eu9vtt5Ae2Sb}e(C)LPaUaqqYX&;2c^e~cB|(KmZjXeo%ntG_Nw|8bK{IR}Og zQ;zdq>yd+0095Sb>FX;7)~L#IGqi7~vZgP~(@#HLMvfe*($g)}{Q2|M@xu>4EF(r3 zV6s!0bsodUsw}Iqz~6f7t+Hgv60GrIGHu#4p@Yk`H0%h1)a@Qn zVnRAzt%MhEnkZvurop4UmeaYxZy|f$GYtj#f>hA zHQZ%zl8Rj`tZGEV7}P!YffJ@fM@0)~JpvB#c8fT)7s)-uhx#@Ue<(*kKK{v6`4IIw zcs)qL9`N*ld(9frSAL0o1prqTbf|h`lTqIQh33t0=($eLkXkFhZ&tvQe~nA}<%CPohs}~(;FKBAi<*5Z^t2K}fGbg^6UyLjGaz)TCX=BT^~hiM$^XsWEwNa5=iZd5tnS!eM(BCs z*+PlDHB*X9n`QA=)pEs!$+EAgS&prTHQv{wCG>_FOxl*hCL#RIJMzhgx5!18{Q*-_ zM%(L_Us_0r*ne>o&fFyWs^<{*_I3IB94O=$K-Ahe0%FD(%%6B3gQsGC>i~Fp^3`I8 z0UT|B%P`ERDSa%Zb?s2=bAsq=G`R#mxBl~fxoqFnApPN0U$4hpog%XJlSUW|7;AzS z3jlMFWE*gr2tR&EOYJ8g*Nu^*2DEa-{3PqvDJ`;>=+%e@0AFHFX2`9W{>TgOLIB#a zV~0Hd{PXhUlTXTYkBg4QixF&IvqYd(37}OEP(;O3aPKuJ3pSBqgWX|h z#U4ZjjTg>3h+c*JsxeUQ z*co$E1_qiUf+#rP(<{J|PKY(mA@qn6^-)xz6L6s&Ex4b@6m!aMOqxlk2Qr~$4M3I- zy)F%aLl@`)ge9orc92VJDM0CYN1V9K3^wB%y! z#qTP)dupS!{P!pniniI&4$hc5(u6^Lfv60E&^giw(SfT5;|Ki{7ljWd`YHhujWW5%Ce%6Y6{W|m;`flJr`1%#Pao6;UcKSSSc>6asj>=sJgHb<0WuKwO6ASx!8);?+cv`o z(*|*GT7`abW04yKCEngp(oQp;TLZYFjTn_een6YU zaZkz+(f6S2;`yQt$5iE!wI+#WDXsP+pjmBEiY3 zMZT=%DEi?`e3(WxiLs{=0IZB=z2;lSUB&)QKMHRebpH;d1xgcPlVPB>BMyA3*0D z%7>R;DuV|PRuv^c+lPsL>C&Z#6nxczdt=twf9rbYtUGrnCS!0|ILC3KHow#YkHNqR znaAqO_zfmBz!eq`1Si$8OLWpY2<&lZi5pff$OQkJ5kPbxoa*U&v7n~pFx!BnlQYts zEBjYBD=Uv|C>q+n%Eya~MZ=fC5fVGNOCeamT2LQULwzHznt&8C>_(ljs3DDx#aNTi6&^4dOHNqlG$QsF^(f-vLL?k*Gl{!Ta;Ug&(gB_g zRo1c4XmO}xD1Z!!3u;)YVqWo(*rqiHq-XzPy(1`Oy~=341LARM3FFS;LNmb$sJTM4 zm}Y52owyTcJ}`(xVrJq)yr`a+4?jd+p(?7pONt_L<^-?NESg!M;MXqE~r{K)UN zX#fJBf1%o=jNb4pRNGh{GST8RshtLq(?plD?n;a-llbs&(N-)LiFgr`00d~#&f8dS zJ?_J}5PCR|#Qi>XIO!rc>h+XLZ~oDG;fNoQGD*jxFZuW&E5(7uyTd?zwo&*V1^hY_ z;}va)joF1N`fsDk41XpZ%cIZv$4jdHRmdM>UrV$DZyEJB6X*a50x+VDHKt3N=2wAZ z6Vn?(=D`)>Zre)q{#{qsf_JG!Z4OdXO2A#H#5}oKC2PhzwFTbIx2|YZ??WiYN(lNj zB8@=DB@y8@06z5+A6J1kZ3DRbNc3Z%d!5@1|4>d@_DZSp;GKp=GY$TX8g@0x+7I?C zksse3jxhoub_Ew!DmYJoqVt5&(elFc`(-|KqA0FQP7DRNC|BtxoqR&P@-ej&VnbTm z@xAA`;d{;eICjD@*1{c#=L#@GF4eJ9VwJzE`)|vXC?_oP{(}wWm>4PC-;9`l(a@{f zFF%9wd0#Gu@L#1(_zKT(y+k-ZfKvN%ye$C57SgK#R3d*3V0tpfTH9x!@qRHUO@e%4Wzgbs+msy{9Y?S#qNYX<3i=%42 zQiA^u4p3>$8P(q=yFRH^9LO=?UJA!JIHf;mikS}X>jHcr@^&U-ybNi#IgB<#jANDD z`NKFo$0#ynBprS9(L%qJv9YlVbP=pE$_gW)P;5yIM9$eJgrXWZu6=QFK=9?S*7We+ zz+JG1c+7KL2-pZxB6g4FF)P>%a6kmfScv4B@f<~$hl<$X_uhL?A<~Y@V9Z47u$_D| zUiL1MyD$%`RsTffEgch4Bb?F!RA2!#0`_8|19;$s#gAQm<2NQlPKfjanUlG6TlbX0`$+2 zodC!E;r*S@NV9(fiJ(n3Zmv`8cmfjzhETEfko{93llAD61;tsk#es>I`V*;1p)v~= zGwyZSFs}xOpkp)1tl&sHGsdT@H; z;Mnh~ivi?5fWqz2@v41#)dsN-tOIMjOm^?VM2$LyA`edXkyvP{M-m-nLm%0|I&V}S znd9NJrvl~EvZj9@m`;~X!b?{xw)73M2f7{q`cEkqN7M_xa?-h@MoD}I3M1iwizdy( z*#`<}uqM2Ygj~cJ)L7lb)9Tsrc^cP@-*|S&hmu_8!%rO+@H%+XCJ3zoXsAW|>G+S5 zQ70Dg5G=HDF)i}yf6*_*6ZmkUV;cGx3Pm1`0OO?Cm=N?m+6W*BP zxOmp%oRgpzd9!XIW}8qB7k`y6=2aH}J%Nc@=uw6P*V&g5_;4JU#}NcIo_zRB*#}(6 z`3wmvayKAOAL_|Po!{gZY}G;aZe5f z$0^4M^CXz(`8astPKXD{LA}UOBzR#t`%7HPgx=Wvpx=-_63;sZqA~&o7wWBm2#(oD zJf`xtwU0Q)2_{q-(0!>!KXU^@@WiJOt;U^Q)q@+~9x}%i$1eMW<&hJ0G`z@H zKwqnHze_g4l>yr@96^jPoRg$BZRTc&dG5fw^zEuz^)5W}mwhr0%ICkky|;V~kzWQZ z`ub5p862=r0LTXSkB}3m#VU?6xkx`dKUtNXN%;os_VCfL zA;j)aiyH&(=18-c1Ha+bzRFDwI6hXg0pMiZqbx8b5-%fx7YREu2sy?%?ybQ)eDFhr7O;Q4p``4Z1KDVg$+iE;ST#5Ke?rhXWY z8zB{-PW*lM-KV5}Sy@@~+ux!t9T1kc&J%+||L%9cQ-PCt*QcL;s>F#aR;*AD*38Lw z_em~PI>ZL=zWc6}FgO zA(!5;UDfqyh#S&V9cuAb5+N)mv!+K&2`o;ysAr^yNg@_bF05RnR5mGCs z{~|`Vep)6?5QmL8twFv9H=w1tT9(Zls1|H;f9TzrP6vwtR;jbGzqC>A_+=a%>N&_< zlILIF4J#lP6Sq6`8F0th}|F8WNPKo=HQ=24Hpv`zSC`w12j0T$x3>`JjXEj0p*XBeKX zO+9x*6r`%r<_`e{7uQNGs$8%Y*g*bP;@B!h7?)fuW(5H7#o)jYY(xUQaO@D&m|%l- ziGrRUMMeZSe5NKi;^JJZz+8*sY}5d3uFh}kQN%gLAsDNcnE$iR`DF}!g4FDAduuai2!6$Wt z2uzH8&^EMd!dQpe7?9J-tO$Sz1_?StP!@q~7|P|QCIUK3k>I=$n6a${t9*8qXmc63 zeifC?Y7;>~LLiw4fXi`04p$YpVpRyya_aOrnB+$((V1!l{t!437;_z0ZN?uHi1VAo zSa;+!?Zf7X=Y|dpM;{sQx=LXA!|_G1%X~}ltc&oBbFl&9a~?fB3RY=QFJOoIp_WSA zhtMz@#EW`;s6;``KRLxNQ?G-l8TG0}8>u|L6~HUG+%0KvHBo_g|73tRI^$G?vR{0$ zNbZ8N_}1M{d2ZQWnK>awhG&IKLxltHZKLeiQY!<7gh+C zJV`=XWgk?^K+G}KUm5|_^Zt&zTAsf=e9sMQ+9{(k*6kSgjK}!lt3%WVj!Nk}v7uPD6ySG< zQ$F4Km6X|YeQ)p4-xTq^@x~i!JoaeD2V^#1ogtp+3K8Sb$Z#OkDabfl1073K+GoWz z)??;R1%0Yv@&32odP|;o;t6^1!3Wzdx=Y}T94w-`^UptDbsl2_PM$njIs#v;JXv<+ zGBKF*?AfyeR*G@@4wZF*Z=vLTef#aV-72s5`gtn5vdR|u>x0=~zk{{zAuf_!xJdkN zJvL5q(3{i(y%VP^LmH*GH0EuQ@IGbW#Mm&QyVQa@V!#M9so(GD0)utWg=8Gqtu(WJ z?-O`5zCKQ}-Yu5B0Cm`(O8kk9vS>-I0zdO!AELmLZ-%aYAyU10kd!CP5LZ~Dq*T2l z1N*O43+`e7n^Pu6N(6MupqGoqszqM^`v8f-s-u4cr-w`H@c~y93!v>f#3h>vwlC(# zmKyBt8zRM35hpESEiz@i5g_)3S4(6j;+pk!>=Ks)>67j9>LX>!0!bi#ZCKSCI;u{| ziG_mb$VRcnk#j@fgg&f>B<5L&Hb+*eps}yQ5{iAEmAY3#2ilZVK`v6qVb|WgJ5**& z4O3+vL-B#M7HW7sIvG->i)!V_w@f{u55Wh< zEOzLMg}AGwS44?qkEn*fD(Gt>-zMmQmcg^TtD4dVastVz#(RhM+bZfMC3l{bf)mU@ z(xDJ@m8YC2r456{zOr1RDz=CQ{YyP!t(2{(mLz~X@UUgYO3WV-p;9pN8i_0ZP9pPH zNfFKsP70S1$qs05YVy&%N-4x9Y6gHOY3H^^89}aHbEMSh2}1Rgg+*TDdi ztz>i6OH_4;G&_s?O`=#EDO5sH^35CeOs80+Br;4uny1ZfTSRaBFIdhf8$j25DZ8d1VVGg$WMnJ93z0 zL}hm-%WkJxzI?e-QKC-O;h?q9Pwfg_e);8c(M1=@ci(*{yLaza#)j16BAI}T-{!HI zH`T7Lyz)w!IB}wE*|J6MzyE&oQo!#H!Plv$o+|v9^ys6Hf+b&v7ak%3DC3h%Ye*hb z>}QpAe*E#rJgwxQM7;!8`~)KAX|nVJrRk~jYy<1ko$}m!4Dkzhwdn}fs+T~{pM4q@ z0!;qUt1UYu2S7%Yg9$W2satU$=gz+ie-|r`5jhD- z;9PW6H@Dl^!I6u_UHzO{3Ch{u^kd>A+r3R9UHv6NTP+RXI6RFV?TuHZ36_17btB5h z3!vBF8v`qQx`d1#DBS;g_P|&uU};n~TUja5VrkYxP`V$W%iD-IMr$T=2=tm;L_7U7 zv7gi=cI*_0_V2p>&)&hw0cIU!`sV*b(AMwZccxta0|$hc!TDfVWvOh~^T=90md-UrS9g6T#FADF*>b`#F7?~0pI}enUW|W{fzdvE-|X)4*!?}fVCgbl?#u% z_rUZz>J0We3R88_;8p`Q?;a6VxUo24*Z#`Of58s!66C}7vqK0>_^mp}Qy$CYLd+70 zfQYtm9pWFcCE?k};u5a$yox}8x@y>*pstz^nwmTJPI>5|hjG7=n>w)mp|T#h8Ulc- zNabVZ)%7<2dG1`4_mH&&PdzcNXdq#O^G7IJ+cP~$*atKyff0lTT^2&2;&VO&N z9DjM+@;0Oq4JM`K=c zy@zo*43>h`UL44vPrH- z+J7&vk~iTyYtGmhgz9OQv*27cByFTP3UtZI$$`jfA|4>-ll3vUzd^-OWF^heLQ&}h z4xG#Q?&RYgGbw2(HUjN~uW$YrCBsLdEgWA~QhDL|(XEEZxvTebHO6DbF5_nm9&F+u z@1>3oOkJLi(s{4fBtZO$3B*b)kL7lPx?El-@|1A2eGkAFIHpbO8YFu31W7pIF_AZB zN)tBgp&m}^z@Y%8aoL^8vD<0P_4aUqulBHp*@#CTdBi*fhdENIEtQp(DHW+u8fAgV z%`(ZVHbupIen$ht0el-7Hl1Ei}&q)5@@kA}1gh<%!rNQay%ckO7b zM^|^a0KWbAxiaD0^Za)Mtn+AH7u}G7ZO&_b&jS1WB^9WZ`e)~z>5xA^S0pDM8!I_55@^|Z0k{dCmmma_asVQZ z>MSi^_l?ZH=u~fVwFIDQhn?sr2%NDVA^JnJXQ*XHKXofc5lF`5JKwPXc!; z8Jw?IzNwOI=xWX$vr)pDpM|Q`og({|scZ9BAAS!7Sd{Lx% z(VS!`+B#)5ycb9RHCfhetCMBW^SI@@3~;jW&?o%CZ&rR!CY%j`6AFu|2>Eb9g)ClL z4P6{N;&frcZ9K-9@ciBw;3O6jy<`%^X3;VL7M(95P;D-DzZ;;WfAQJL@K=QJMZL!d zxVi0V+X)wndvCKGH9khZfd$X=VU=RG5dH@UN0hIT-()C*l zgPDo&nKcH$>i?$0$tXn6^EZNHNEb5o`|%Bs1}ekGE(z~2DGmCG#Sm3W;dhDym=7Jh zET}3-H~=4GGt#5|Sx#ExYEc#P>7Kwv$dNw znSNZXvikb#KZ@jXI8ZeD;W(g752n4O9su6aQO3^IW$W4fRm-DY%2g*vEp*k;367x1t|^26H4^x;i%=qg-ah8 zRY-h)bzS`-KpJ-=M2pAE>7yuCL>YZg0?Wk1GB&@NIIf?>K*x(5l}Z>;#Cj||1A;Fr zr>YYmrJkIxKtv7?V$U8xCcxwT>JJG=wO#^KvM~KmYGy05T1MU6bI)i94I&Dt)K_KxtUdOHh7$Xw^m2 zbvm-s_e4Qq8=|W2kPSY}3WqBb=Sgsl@dm<@FfgrqI}{mH#&#*kyuw&mjD{)eF#04q z3*NEyO)x!opl(6%)up}+ChqwjND9IGS-KIT_9cd{7WD!H5%opeBkDDM_axku-7d2B zAK*M!Ak@!N0J~;czPnK-gOgn20{18TM3KxXk_uqe2r*MNK#-IJeAN{zeJ=%`Jmq*f zU-(@Cju@?YV7SVN4l5Y_m{`Lp%Isqy>%=cgtcsPHwMpocU zIRJJdM_6eq&KLTgb@W&FgDE$7Z@WDZ1YhlTPk@VicY*6-SN?kFHiC{IHG2=}5=0nz z(B~4tms0u;PzCSl77zGB9h4uTI`~Se8KVWgVtSQ9U?}FJI@BU5t*|1ondeyihisc(d7wXJ)A1iO76)& z=Uqgaz=DImwR{&c>0-UMGg0Z`fl zS1iXfzp~gvM*iCIeQyG-oG(-73nE7nMbk>pHpullWJ4lQ#zF8EsAt+aua|CCSLuU_ zL1N*=1u`(~9uAhGjGY-dUv$(d;gs!lK%SR~Tpxlj-Hp1`^alWoRx#4(BysA*7}3kK zV9O1zkUxdcc8dTvN{6$Ga)=53~@Btm4|J8 z8?5AFM#6O7voN%qLF>heMpzu~=`F*DM8fA2tWJw2qP~V6iH`6$K??T3_tpbWghw*1 zM9oUn7MVwFziamyoGip=gu}MB*UVr0*D59U2R|-Titl;F0CO<7`IcNS_zWcXVr4DB zS3-Y)Bz%M0(-(kj7r_^~9Zl6z3ZSJpUlfnQ!kJ>n68JKstCl>>XIZdpR2--nfGmGR zfD;WvkGLFl6^><4j`6;!6SOqMriW#e!(uyzoG?EKHbRAdrM?1_@X1oHo>gDiP|3MFy%L$XNJRZqsDlMobs!tN*sJi%{dM34s=+X z)fk*AUsM0Kx%vOad-U2F2wV#5$C@;J&ji-&8c1rBvuA?R5P(}RyAa^%JWSB1s3i2u z)HOnSeVVv)&p;f*vvJ%i{?)#3U5l^WQe)+TErOF`X*ReH-is6`Hoy{w z#Crk(u$sL}G&mLt^{0-EmbX?`%g5_$B^P>Kqu_T-GA2rG3+5umqKt;vJz*fgmxuFZ zQ4kBmOw4k4(DQnu&(rNZQx zIuT9)oBT6m^~dpu5qT79PS7ZcSMwoOW9_)V|j z^EyBn#E%4EmAl%e;V-z9sKll$9=e@_;EDf+X$i6kzVXh32YTg~3kvbgZqK_#@gMwV zB|`j%H%#eYF$=GyV;sPfM>I+9S0Vg+RYKJv9mfGhj(q2>)`BoHDs%a z>TK}O;phSDDtI```%#pkyI?hhNsux57#64t?h@N?Km0A}5FbVnd?9}g3X)Qb$+$g& zvrX26AxBhzUwFRuQcI+iaT*!sYG~YLiA}I547tB-MZ>!!H(=GZn6t2F6^86Ag(*GUYTW}}*0XJ~I zDq#gp|6HWV#7z0sNlB6eD@REi2T@}-T;h7BbaFfTOjGIlA%q$A!pI@7!2NUup#WCm zKwQ_9yNNt-y?P-GmWN0`OF2iEhxvAAo64x|5#FKaJ~Y$nK17o_shp) zy?N;e`E8xU{ON#wAB}?O(aZT_`5y4)bu|$bJ0A9@(v6k)u-Ea(E6`S{V(f*%rxv+X z?r`Wp^b|E(Iaz>=^H#|ZjfP{6dNZasFv z(u|gh1T$OThX1Wx1ynecuQ?WfDz<}bHQ3PoGT#Tf2Y}8Nxl{3&bCg5TqwZ3clq#fB z$|!J7L7vA7eNnzQd;ab`Eq-`;-;xe@|r*Dtin3{ zf3b|BKN8%5=ykq5qB49Z9IUpN#SwK)&9N98q$6bm2+o%RUsSj!HJF^Qpu)Y;A&39( zfvf7_ufq?nL@m5l_QBhGe4K}a6P?p@Z9uHLNchQVq|O&eukV2K1#2pD$tZGEL~rHo zP`AW|KD%9`oP!uT=7QpR{C@1K=wvikvSGte_4^djv1|RL*xW+y|)xHg~r`iC^&L z0eeQNUHjV(`10y-bu!0*>AS<>>tvb1WIv%EAV7kZq0mZ^A$Uw>c>*zn z)?;rX%K;a^EpNeCr$`oUsgvUeMS}AbD(ecs72ChsD2K}VCPCy#GIhQr+%V2DFRO1~ z&KFOnj#N%hBQZZvuSdEVr1fy2gg;o#zXovV@y<*TQqxKloyHRj`wSdU>BiA;M? zWaJfw0f4`B<~inlq;QDLIvvcnb?)h4K0cUYx^yq}iQ9-BJ$i?9xEm4#UtQ|To=O}9 zUtR1;y#hgQlQqOSY?c&{PaF+ioqLM(<`C$7f%8R$ds0V}xZUN$`NG864t3qt1T*t9 zOso@3JLUPi)Gh$*V!nYmdEINLzuE@R{bYN<1YV}@76Dc*BL!3{oiD_8WOyMGq9QoKWvxaqb|uE{WZ-Y( z=HTC+=m9^gYENYQq&Z(Wdin> zXGn|k3Fa`xjvcV?ZQb#G<(Kny5JRU274B94`|B5fb@rDocrMrj{(67_su&zE6L^_v z$nh!y0HdP4FEM^1@1wYo9_8le4m#3S| zYMI|Qe|0u52ET(n&`}RipUbLqWlc|xmp^5Ca;)gDs{zXNd5dK)JjS2!c%Hnox&}^) zp+vv>82~VFw&=-z*C(>81jaIWcSrXI*Qft!m`7|ft`E6i;F3|ti;DM5Pr`4;N=)!r zI{M{&`2#SV58zUwA2?qh!Y5b?Egh}ki+R)K1ogD&m=%k(*BL2eAridteWm_ZXUWR# zY3E&UlO0`7{8-33_xN%Fzppl$3ikl-CegxGiJCc=Q*SUxhqVWS;H!(>tyk_=ca(vJ zp8*|>9&@m2)iSP1)VPh<5px975KTMTh3Aa0WE*${FLYS zly!3QNf1bk=#$L*Qx7m{cMD{Ca}6H+SbHFzu@-&ZLije8+^y<7-?Od139_p8D1asF zUJ_aH7m+=m8P}&SQ)LVssBYZnR9r6E3+|T*0IT8Si>Wu%p?a|T6@R)YHqWyKlbJ+mhD9&>|drti*X&zsQ?^uP)@tw8kqYz{y*E#j%; z#b^d31AJi(Blr#!f#CbB^N+l2Y-xhjr5=ETRSw>&YM1m%55q)`-C>VXgoI;)bmJv) zVV9!0+jp`mm~W{c7Vhxu97Fv~z}EV8L+q$Rh*sK2&@RO{815fxHi3tm$N}#t%1%A zgIA}NZ!x%GeNPtI`H|GaU}g*4JDf5iL8c(|&+Fe-%l`ntruJ`wCJW~9ZneiPx7LQc# zqXCApa7`b7D0?HAcYq!Ug0BD_6Yz9PG5l(|6*!8AiMC#m(tUqdehZ0EL9BVH2|H$o zyWVdq?@7$9^oHMA&X@8Jw z15}bH=gA~m8}dW3T3n8K7N836n?HRnrl>Df%JX?8^|EwlgIc_(qg8|V@8`$FNk8pF z6>^8xkyM-?hUgcxBBajMC6l3FpW7&Z`m9{e9vcIWSd0-=TR+k3*5KFHt;6cLV7hul%%Ir!%|wO6$FnpG^}J?S~Qap951KSDUZ%CQw~Y5Iyz47JT*yvaeTbuYSlGDTnUk)4t+3+94GaSkn&~# znxpO#4f<3Qaw6o>b5i7}0SNWddy>eh{}4I;;a1SZMZ51zm3Kv5i&R6$OIcTXM4(ix+%}-oYd7BDhh7TiNJlZMu_>c`v^MaICuOA|lW_ z`IFpnTl+V7Z1eyX<4ZTSDz5wM7g{M&oJf7G25`6hQ33S0%D2jb&Gir^Mk}`v@!4Y# zd}5kp!eVSJ6y>SIMMeB=#Z4-vVR~GMY^?{Np(}++zlO8X3q_;D(PMuvGU`hB;2M2k zN#uTkvLJe-HJVUSzXE^|G_zPFEwu-#3r>T+sWq(GJSoS`d($t7@NL6-FmXzA3$CYJ@k;QUk@Xg!%RMJaL1)D zUc6W?x#SXg@4fdrPW&TFp%fLBgViQUO7KW*69)EdD??&beh%nxQM@VeU>}g`;d2Ze zxJHB`3J=#rS0RZyc%=R&-hc%d7yMcd`U7Mg_L1)0;`jTVIfN%Au<-?!l4(y zQf$0(7g3JQ()xl%`F1zBUeM>F7;k5Nl94AlAX&#D-Xg?*W^ihM@${e^Pfk^KQmFDe zul)Q`@gC%j>u6df8aW$(19851)oKOFO^?4h0Ku@kqmp2)LYrf=3=@d7x%eahVa`2A z3fGI>ZHgT|I$s3#)Ggf8nEWGu7=fF&W5*7;EjdNMK&Gj&<#YtW!Us=`~h zVq){qtFOK)AAR%@06@5G-n?1n%$cK*WPX0W+;Yn;a^{(5%JtV@4;`i^)yVtqyHAD< z8)n}A!}%^Q7A@5-`&YqJwCP|qs!z|>Kh)6WFgRZTU-ovuR|6Ip%<+v-?p0#PrjB=6 zS~n&He-?;c1xW{VlJCyllYnJ)+=|xR0)ek?`7>+HHhAy ze2CHI;n2ETba`)0t(-9`TEZN7>FB*3?jMwh5&+Czu}$WTZIKt3Rw|!e7f;4}+-r)o zADJdz6kyMOz z-K37N%>C_F=K(_JZGm%&$NW~T5H6=kq7S?S8kN*O+UxZHjB-SXLI zpUIO?J}Do3@Bz%JtL4&5FO|WA2TNXFo&sq5_U)6UOPBs2!I!?TK(utbl)yhqD*Was zvUzs4#;=3_>+z>U1^PNzbiGr2Dc#~{w+qO2|*qK-x+um?v+qONiZQIty z`p-w--|M+}F6U<6-PP6Ib-Jp~;gI~|y#48WFpMQQIt8chmn%BC*Bt|t9~i;X$yaUQ z*6Q^FS7KAM^PVfLy_-F~kOzJ9pPVLaA#-7Xs%cxHZigAImm~*hZz#U z(0=7l3jk{C6B=Y(D1vO8?GLJRHZaJwK#nprG%RRo(E#;aQ2#}b<}kq}k$U=ZZfR-B`h~bGjg$_> z{SPt^vq@g&*5_pYTD{1byfG;~zi>{zp*<^{z1Nsu@EH)t(V2hP-+|2ySYSn6*JrK6=o-;S5A^~-> zFJq+iE-7=txatD8?k=4?tH6tcOAkOb?n^52fi)T3P2`2(lFJEhYQOso~e0pxg1&FU%So zMa5q%Sk0Z)Lh{<#0H%iR&AsW2KrUNvIJ-nivxJxh1RBYrS)Uf5)7hAf&;B(qI>-R7 zf-3eP=$jqa%Jzm!A+mI;;J!xepCf&jk=d;vQryWxvNbYUC!N#Ni}=M9=%1C`!$Lib zz6_C6CzOqW1Cm0r-@EP#Se~5OB3d?9nn5Z2zMEF5bA7CXyCuV@;RH2ZxICV=bf5Pp zo$-w{;9ytMaHi?Rg5mpLy)yYlMWbz(K$>5_e%b$9v^p#e^TXV{1k^9R<$D{v1KIz1 ze}6xN-bQglE!LS?S&~|ll!Q$t9X*v+eY&9)6}Dj!I5x%0MF%s}XWQ2O^!X=TvbXv? zwPZk82%2{garcwGUq-vbqIFG!pu(a^1Y}*CbBciRVJ2fqZBy9q4$Kvv_L$7|@^m{2&Tl;ioFIVgi@7c`2fu zFOtlklWwI`!#Fqlk$FJ1bDD+%1JDk7tP(;>y0m~9z9^vSlXMfH6h0vp;}H50L^?F> z>yWG_)rG>KLDJCBP-`-dEsgNISBehqwHyy7{Vzk ze`6?)pBvLzXJ4T>L9#BeBqHSUb*GD+N;Wv;>s+>&WaziSlRlw$I97g=X@=pWB-Dy; zPUo*LYYI-_35(NS>m3^zH)lJsGJI+l^_(SFbwXBE^y^er9j}^I$My)`(YFn4^%^Eo zp)u`+UpsdT?lLn4cHE_plNrR1o??IFj*g79rZ+TLFBqDL&QsZUY>!_bd zT)de~(4*ci=*iDOyu9Dm)GWGWqmirB_1Hgd024=mClY7n1h9L;NNv_WRt-7hIiGBK z>gT}LiH!tGDlrOc3Lz{P?+!Fc-BEl=gGeOp!_Xo0IwYPpkF+78yQQ42C0C#L7L3<0 z*)9ZlDLwN3nnUsX2`kz(J*B+&(&NEW;stT8t!~ zXek0(BtD44;(Au)_vRgYaHoWvLu2ak7wLotC@|bEEZk!HAc6|DBsChO+e7(~gyZ3* zY(a@4?<}na49@+5U9_$N0%8<2YvxxEq6q_0#~@so_2S*yNCoo_v3P6ta;lO+F$J{F zlFNY0aimR(qICN(j*vwFk@3qGhhQ)ZVq0po>E3@|IcIC^~%x8o9~F9)wXo1 zm}*n-7?aJ=C&O3ua4_1(Yw@eW*e zEaT-qGPaZl5ZD7#UY^z~ZeI6xALgWeg82*>~zE3lPm>E zCyuj4FA?BREJ7ghPVHlBU5DQ#WBvMtg@b$IuAnVxIg7W|-`g~30k_}fZ3yY`;*UkF{T| z#(5@n7E-}hzPMgZ`@o{U?HV%&IiUvG6M01Enobl1|68!(B$7TJn#|SE$7-0(x2K&< ztS#94b`MWyPSTFx2UDIpo~kO}yXA_N!qpDZvEOIk+!Jk*pO8ryts1aKLNE+h-X&NJ za|3Ge!4KMfO6kGhf>Wwh-*2JJYhnE=HUU~Rrco@-KSrI|hK6K{^N%6W$7${q+kQ_#4CZ?uCY|{8Pd~9_OpFHYXQaS z^$EmBThgsCUg0egt1AKAFBU=82e3{26=vx{vnKSO*ESbT#(ZOlB1Ovn}Z z<$(Zt;3D!T>748Le4Cfu_lVw_vRU`1K(awn2191*YLHYeEaJyRpYRGz^m!F5r%V8| z+!LI3{qWxn9YM5M=fF1xe*yWx@U{N4DS?q=px03+(_#kuOq<(hmx zxbPa2gwcIY(Z>Y8Dc3!thQp4Rqy^EQljEUA-~l{1iz&BI8X@4VAj0l@Cd(~@&PQX@ zTW-V|MQ$3Ev&7oa^)bKU2S54E-K7IyvYwg_6ZaDt0L#EESD_YK!raRX69Ef1xWf72 zOVmW}x6Se|ZDz{X5MbR(bkXex5!ymFbZOh|QOT@?CK8$F-w}*u+7+WyT`>hGYLhmc z4swMYn$k246Ap6rDj`p)k_+`wlZ<6IQtw^_iNT2X=P_~w6v&a64%s`qJ44vC{%mI@ z16Uo(E1*Lg6-OBu*LVYrU=+*0RZ}t;`uN~?3gbd{cOTP@miCDYz%sL3I>qv$W zo_8a6jgwB^7MWxVTLk{_Gj?GVjf1IJ)&=jp@0xuyE(@6r-i8X&TSR-_7wMg{mn}xK zB|?;2|DG|+Jf=h~8I||pA+Z}*19@S@=Gj}Iic!YdB^1>@Zq!$O2Py0s{eAn}yW1?O z*+_s(T;tOM+)jeO3>n_p6ZriwwqR4f6kv}&@AS=kMvCWT?U579Lf2}|GOnkuledxd+>nvJZLWYhs?3G_7rhnB^QV%KxuUDca} zZAp$hX)0zW;WWpuES zTz}D-NS_%K=R&|EvX|WvF>(Ga*B|OMT0hZFcLezn_bh((*=`}CG;_~hY--)7&rM^C z+GL$qgiYIonRlFyNFH8K;b&%@8YWC~n{{C$(oc;bauv%qf@K#QF2}%VuE&^&xAM1c zlqq+iDYZaJFu#DWEY~CZ*kXKyZm>^;cZ@ZoqR9L^5n9Fmx7Tby=M0ReLnEaU0DtQr0Wr`6ZIAJ z1$c_v2HD8=JXn50uXM%0vMUn4O71t+x7Q*6Ap5I~^JJ0D z{LULeBeZzwa)YSE{swt99aupYC<919>4o!x=(W2#c*J6EXD!2yJ-Os=G9gpw^8R34?QmdzwqK^o47?WJ0QT8B64TtjcMKZrgjn*|h@tg|mNKt18t+jh!e4a%GhF5bSQ z73cSI0(0%)ht$Y5&4RPJ_7-&{fD0~0b-R(} zccY7U^%?W4gK_RWIObE^_1+`o{Bja~a#v2h-x}$o2qdPSbq3SaO9CT(dpo|z7fro{ zpl{bxl;hFtYiJYqzuF%47SS|hCCKkr$^uiXv{}I}xKqsfC6bm-a7JlsIPSu#Kw}c6 zcVbyx#OEyOU4sd>PcnQ7K?N=diav+vB%mONzYDWo9vWZ=PR=yz+tiw0p*WLn>WqaV zR4AnOhL-YhX*lA%(0Tj4gOrgp&N}btJDOSp@@!pZnmRd^XL1~wN;JE%4^?pOw=?8< zNbitvr4qMXuKB^ zj*Hp|>g+zGQi*qbsLpl+uZhO;_k^mhN_dnd5~Lt^=xXUWRtha~Qe4N*_0gX6md`qb zoG~qol8fZj#@R5uxqqWeJIeR6d+sEn+ zS@V9pKzAIfM0XSzTn=MS;SSo!8806eL!NU$EGwA#Opf7^DNc3<(dcoGy5iNxji2D< z#BnP<2*VqGokh?z9hJH9{g8l3J^jFZCiJX{tsfuFv-=gYZ>?fo7|Se$5%Gam$`Uj9 zzBx;So&`?DZeaGceLX;K{Yf{`$V_+KMF2YY z9WTWd5prWyFHH)_%xkVXjX?wmvUv}&maljXO6Mr4#ye{921zk;Qk7PH728d%;w+#p zAp$t)zHX(n0dODdF>^-uf-YB&AH@ZfBrBbs@eZ3Rs@^SszNCZYPyP9_+Uwlz9QEm- zFDi?Chc`$#DBa5yEI`zkk|;}-IZ?sT3SZS1ukP$^t8!_b%sTzHL))7EIh8vY>yDg=Jk%TfNJNnte^{7s1SPC z5LBwQFx{Q-ksaW2zQc&Zru^yL=G-+?$_F2fa=~I7f_dMxHdu1T$qV>MRyPZ0=fK>Wh5;Jl%vA!i$u3Recwy){6%7|y-^`ZDf^QtZKxys4P97j-a zSgnwvr2W@I9TnttQy5oy02V9%EJ+;=1$iJUwK0r)`@SK&p%^XH6ZJ)5;=s3r{_#i* z>nX3~Rr>|cr#?h@?zE{LZ+AF6R!ckV6m%1D6CUOmFlQ8RbqKBnumeRkRfI#uZ!6!- zQnwr+t;E^=4Hto8^?xLJ)BrqaM31~g`-uJ&1{OR+DE0C<7$Iof_3|I;Ba$V{jvUDp zc5|xr*R?T?)j=hGY>E-(LOrzICie`u{eh-mC6qgZ;ZW^6y-Xab&e!i zBeHy&=7;$rrf7j&z@~K_IwSrbIqTckVcd_BI>h47FiWL#Y!k4ti2&-@JxE?c*m)psx?#zb8l-+C8MT3p!0B73@`0h^vXoxXM zIqswF@!pLi!_RxXpcRs-9@ZIXKrj-z?;quH8ouU4GBX?x(z;_QF+kAQx;_#i;v&+kuKv6!C&5kzT17Uinig_5eSp z57!3p(=IvgZZKoo?{dDj5-$;?r(B;8Us{$-0@U(Pz2(JH-n}l8r1Rs*EMw7)P;P&+ z$9)-)t)EeWeh*GJBG#v$aR_+lmYT*`=}n8jiL|fpeTW{J6i=vT(Nk^{1Ty7?7j%>5y+oSt*DW|RE!6kVzEOaSIeYMK5VTh z*57=z%!!M3xEwh$&3X(UjYKFqx}T78$kMt7BlT+=Kj`<@kPt0!E?MgJYJszdA^aXr zNG03(i)1t(lR%SuUX{w=po^lA$Pc^(eka5?TkD~38J_SX>(1d^`^->7k>}+~90$5F`rbS+Dy94;>UG;hvp;iKA z+t(Bca%vm)=U^-PJ5$Xt4-k!MIKpq43Nu`;dz&&c;&~qFo^=qZH71d^4*|73VD7v zCBIyvBcqyys9Hycv2e*)^B`yl^DHvNkxq2st`uRA%N?;mHV8`BKlVDw6fZGlJkaer zbl9Vz5#7VBS-Db;o5K7teZuygRF1DgqorAfsrF|p3OV6_$re%$^h_air#o!{%d(NU z5`+Pr2i#^2;xYwo1dnJse+c5t7mX*UD|?e>G4IRseX7o~1v=&_-yp9=I_eOq>n7=R zp|hQE_{rqrmoJe?vD{Fu?chhYb`z*CddCEmhJ$?$gl>I{(yc!#)9piUp(1d;pG~c2 zRNSLd)JLvfN(*ua-2{i;l_E~sbX4u8dznGx(D{EJQhOyMD=TH}MPWb?A{(+C&$c?f z4)Z0B4ixN;sY4<*N z=iv0OlUW(q7vwERng4?*uqS@A`re6I|6_0Sba)4b9KVrsqJ*bLwBwo`Rl2;oyx%We(rux zK;?m9ntIvwT&ya&J=3tZK&Gi*1c9z_#P=~gOhb%OY*?7@xI-`}MmFAox#}FlImoiDMy|YQzab;Rmkz3=Y`&>Zy0OflK}aa@9xOcx8DQ=X33h#-x{vC zM0F@3UIVM`^+g}HOkk0PUjjq+r@nr=bWv@bMZ08|&YEhnuqJTY`GysY(6&Vu7-qL> zb7;@)^np4+#Lsz5*G6dnLg*z4iK;mwNwG>LhI`|y5y>ds6E!S>c4IzxD+;4y?Xz@Q z_S?`7PxF};tbZ8(?LAne?lUortN}7= zPoEqs;JxBc2b&gJ4ZvzDdswI2#*|>ShmkAySvv$kus);nm!t52LrDOk7vpCujrU)# zYO;7Qg3YU(ioQe%0I9JfLD6Qto#hXJL0b_ozUR^*Z}K{l`Lanr`_IY$UoKt~tV^B3iqzG}#w-h@SqjNRkw%>Gl zZ$+xJej-mfEK3D7fau*0;Lv?`ua$rX6GDuSAVC;pz^L+ z2^$KO3q^|GkotEF#Mi==s>909e0qgS_EoQEng-hj({~(#Yuts9MIFK8tW3|ymi`4R zs05x%L9l@o_w73>Fko(L7njA|0@T{x1J&)EJoICOJnWvmepl~Un;x@sf&^*hhe$Wc<%h*=e50g0{l5-FSC!3qb(X+#`5S8v6?GnmapA!y1q6U$D zU=oKsi!S}Hr-1#z%Sg*=qRX66JF1fBMoye|%c&Ci$f}=6JV{#~IQ&ghJsQ$;tBCak zOqU>CJE2a%t?YxtthewZFr*hxo!~vxd5~5zvQ>@|>vPPN04;-74&Lell##j^$`8n| zpTx7bhSVg68B^@dmSW&NVa}cG0Bk zED+HO0Sa6Kb7>mk=%>^LLsULS^YAjx>d0?aODKoZBn-DsAbSfa?mp)fKia}i#t#6LH4nD57_4R81K*mX2)FPvXQ zT1duvf3w~HP#ugYFrZf&BVV8%4E~%C@h53yt*3smtA&B1HB$FQaUWu7*%8wx; zqRomMP{Uf8O@upX{=JvkL*S~bT#(kTa?5huWIpNR^26*o=jF<>&D{f+kmzdo)AFZj zzYpw}TJ#!#8p=S>rTR`ck0%2;!^}-D#jQGoe8XR8-*_v7FYYi7HDeB{HLN-QCbn?ac?}Uuu&08I!-Q+xFf$_C~2|#fpywAq{!HqPoO}BHIR3A*BtJQ?bnArnJ8VU3x`%J3cEj zf7eIDEK6oMed|2-L~wfQ**r0SMiGG3rMM#S%6>b}Ym8b=`O#Y>^^ZS`>9q;{<3N8ssGdP;sp zWXkNUh{da4b-gD@5aNlaCFj2bMz8_)_ZbmZUW9lRB`bwg`ytk}N}|ax2gW*I?x1qm z>`{@PZpwIy-0ukg9>!O{b8J5?>y}>_^xDba@(8Mq&(89t>01ILc`i7)K^)~~r>f3b zaFiWGiy}yTqzL8OEzAmcAjLTeHEla6zL&GQ!1E}+`)?gLBQ$?NwJ@O8bOE`hb!VIm zP_{qxW_2}8Wm*b{!%1q+Eg8JTeX$*J+W!L;e#Jt}B2ntz(85OE8V#sK`yHs`$M4i1 z8XlE6!d1;+a1ej3^SrQpI&;m(IxpV&{WWAdA&oSyaqV=t!ii7n@3TrwZ&g?iM4ah3 zuc9^`!LjkZ0(TTGHf-_rDp(-@dUtRZL<%e-f#(#Di{@D0k(nKg?@qaHH;89BP}@K^ z<=?-=ak3SFl&E0@Vi(5Zd)kY;>2?pyNTZO?lp&Fb%%3k)n{_%9X|R~5KE_WE5OxLO)qSK<*fz0|w0nNR9N!&S6XwI|-jlSEtK^VJ{B5-nxbnYShFj7G+Rki)BT zBYAv0yqe9)@}l08D9IyRtHJf^+vv%?THy@9N`qDo7?ZhQ9TPn93PMjyx;y z@g%>}x3%B|1#M|X7mJItJN@BehWB$y^3&MP zUwzIS7R^Aqg`tMY(bK) zR4{=z=eolr-&~&^`%eqkg?5R@l8q*1Rr@u{q+$5JFRNF_M}qATTIuH#F0?sJ0fH>- zh67)?_v4jdWo$v-9DTmpm&H~#4F0r}tU+?Jqs_Lt7tYy$OoLCOvHR->Dh3QY+qkTx ztz@obgMst!fnm&g34GZfJ6*WL2qr46WMAw9j$U+!8#Y2rwS{iw++PYd(f`@` zjXscy*VxGPO|G4WS-mb>m`F2DH^UU+HpwC@YoL~?q=Qi{y|HAbKf@GRw*ID%ya{1a z=e`h#*+K!()1wTXGH4WXUmH1m-d`aN1D${Ju1eG()arGEW3yR&9ZzAAs5hxI8P9ET zICM{oy_8O*&AGID^nFK8R#hukpEmUfruEK}dsPYa0=6D?d2tpI#pPZCTzDWrjFQaf zmmq4IXUoZw!-HEc*)KoqSM;2X+_sXMre`!Gkh3jXr)X z4PQBq4l|Z?@?5z?q&s${H`i)qFuU~mc%?$1xnLS)AD}+2*j!!IZQZR~&pLjrt>92` zvtmGJZn0A1b?Bv=R|qquHnLv@cI<`@dn{@@$oAAG;z*?sK+%I!Jjp=)85(a(D6%uJ4R8?txeu{H|tuHcoU^GUWhm9Dpr3}G^o)il$s(A?UZ z{Vda?7lwLJ8+E~1jBl6n0Uk;ycjd93|u z%~Zznsh)qdXw?m*4^c!z{9-7L$T9ihCVxBbAn{GqhL9xS9m`>M|^6Cje

    SIkoZ7vr5bSj#R8a3?(uwFg!TpDqz+AS~Yxd+6u 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 library defines a `partial_sort` function where the entire array is returned using a partial heap sort. -**Python:** Defines a `heapq` priority queue that can be used to manually -achieve the same result. +**Python:** Defines a `heapq` priority queue that can be used to manually achieve the same result. diff --git a/README.md b/README.md index 4e02dbbe..c441ca33 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Read more about the package, and the intent behind it, in the [announcement on s #### Partial sorting -- [`sortedPrefix(_:by:)`](https://github.com/apple/swift-algorithms/blob/main/Guides/PartialSort.md): Returns the first k elements of a sorted collection. +- [`sortedPrefix(_:by:)`](https://github.com/apple/swift-algorithms/blob/main/Guides/SortedPrefix.md): Returns the first k elements of a sorted collection. #### Other useful operations diff --git a/Sources/Algorithms/PartialSort.swift b/Sources/Algorithms/SortedPrefix.swift similarity index 94% rename from Sources/Algorithms/PartialSort.swift rename to Sources/Algorithms/SortedPrefix.swift index ab0e932b..0a31ae35 100644 --- a/Sources/Algorithms/PartialSort.swift +++ b/Sources/Algorithms/SortedPrefix.swift @@ -17,7 +17,7 @@ extension Collection { /// smallest values: /// /// let numbers = [7,1,6,2,8,3,9] - /// let smallestThree = numbers.sortedPrefix(3, <) + /// let smallestThree = numbers.sortedPrefix(3, by: <) /// // [1, 2, 3] /// /// If you need to sort a collection but only need access to a prefix of its @@ -65,14 +65,14 @@ extension Collection { } extension Collection where Element: Comparable { - /// Returns the first k elements of this collection when it's sorted using - /// the given predicate as the comparison between elements. + /// Returns the first k elements of this collection when it's sorted in + /// ascending order. /// /// This example partially sorts an array of integers to retrieve its three /// smallest values: /// /// let numbers = [7,1,6,2,8,3,9] - /// let smallestThree = numbers.sortedPrefix(3, <) + /// let smallestThree = numbers.sortedPrefix(3) /// // [1, 2, 3] /// /// If you need to sort a collection but only need access to a prefix of its diff --git a/Tests/SwiftAlgorithmsTests/PartialSortTests.swift b/Tests/SwiftAlgorithmsTests/SortedPrefixTests.swift similarity index 99% rename from Tests/SwiftAlgorithmsTests/PartialSortTests.swift rename to Tests/SwiftAlgorithmsTests/SortedPrefixTests.swift index c89dc02e..8cd387f9 100644 --- a/Tests/SwiftAlgorithmsTests/PartialSortTests.swift +++ b/Tests/SwiftAlgorithmsTests/SortedPrefixTests.swift @@ -12,7 +12,7 @@ import XCTest import Algorithms -final class PartialSortTests: XCTestCase { +final class SortedPrefixTests: XCTestCase { func testEmpty() { let array = [Int]() XCTAssertEqual(array.sortedPrefix(0), []) From 70a263c660d9131d3a1650278bca66c507e40142 Mon Sep 17 00:00:00 2001 From: Bruno Rocha Date: Wed, 2 Dec 2020 11:21:45 +0100 Subject: [PATCH 26/27] Add tests for massive inputs --- Sources/Algorithms/SortedPrefix.swift | 5 + .../SortedPrefixTests.swift | 94 +++++++++++++++++++ 2 files changed, 99 insertions(+) diff --git a/Sources/Algorithms/SortedPrefix.swift b/Sources/Algorithms/SortedPrefix.swift index 0a31ae35..fb024b05 100644 --- a/Sources/Algorithms/SortedPrefix.swift +++ b/Sources/Algorithms/SortedPrefix.swift @@ -40,6 +40,11 @@ extension Collection { """ ) + // Do nothing if we're prefixing nothing. + guard count > 0 else { + return [] + } + // Make sure we are within bounds. let prefixCount = Swift.min(count, self.count) diff --git a/Tests/SwiftAlgorithmsTests/SortedPrefixTests.swift b/Tests/SwiftAlgorithmsTests/SortedPrefixTests.swift index 8cd387f9..74190c43 100644 --- a/Tests/SwiftAlgorithmsTests/SortedPrefixTests.swift +++ b/Tests/SwiftAlgorithmsTests/SortedPrefixTests.swift @@ -122,6 +122,100 @@ final class SortedPrefixTests: XCTestCase { ) } + func testSortedPrefixWithHugeInput() { + let input = (1...1000).shuffled() + + XCTAssertEqual( + input.sortedPrefix(0, by: <), + [] + ) + + XCTAssertEqual( + input.sortedPrefix(0, by: >), + [] + ) + + XCTAssertEqual( + input.sortedPrefix(1, by: <), + [1] + ) + + XCTAssertEqual( + input.sortedPrefix(1, by: >), + [1000] + ) + + XCTAssertEqual( + input.sortedPrefix(5, by: <), + [1, 2, 3, 4, 5] + ) + + XCTAssertEqual( + input.sortedPrefix(5, by: >), + [1000, 999, 998, 997, 996] + ) + + XCTAssertEqual( + input.sortedPrefix(10, by: <), + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + ) + + XCTAssertEqual( + input.sortedPrefix(10, by: >), + [1000, 999, 998, 997, 996, 995, 994, 993, 992, 991] + ) + + XCTAssertEqual( + input.sortedPrefix(50, by: <), + Array((1...50)) + ) + + XCTAssertEqual( + input.sortedPrefix(50, by: >), + Array((1...1000).reversed().prefix(50)) + ) + + XCTAssertEqual( + input.sortedPrefix(250, by: <), + Array((1...250)) + ) + + XCTAssertEqual( + input.sortedPrefix(250, by: >), + Array((1...1000).reversed().prefix(250)) + ) + + XCTAssertEqual( + input.sortedPrefix(500, by: <), + Array((1...500)) + ) + + XCTAssertEqual( + input.sortedPrefix(500, by: >), + Array((1...1000).reversed().prefix(500)) + ) + + XCTAssertEqual( + input.sortedPrefix(750, by: <), + Array((1...750)) + ) + + XCTAssertEqual( + input.sortedPrefix(750, by: >), + Array((1...1000).reversed().prefix(750)) + ) + + XCTAssertEqual( + input.sortedPrefix(1000, by: <), + Array((1...1000)) + ) + + XCTAssertEqual( + input.sortedPrefix(1000, by: >), + (1...1000).reversed() + ) + } + func testStability() { assertStability([1,1,1,2,5,7,3,6,2,5,7,3,6], withPrefix: 3) assertStability([1,1,1,2,5,7,3,6,2,5,7,3,6], withPrefix: 6) From 1d3dcaf1d2e5f69ea351655cd1cf4bc260108c04 Mon Sep 17 00:00:00 2001 From: Bruno Rocha Date: Wed, 2 Dec 2020 22:59:47 +0100 Subject: [PATCH 27/27] isLastElement --- Sources/Algorithms/SortedPrefix.swift | 7 ++- .../SortedPrefixTests.swift | 55 +++++++++++++++++-- 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/Sources/Algorithms/SortedPrefix.swift b/Sources/Algorithms/SortedPrefix.swift index fb024b05..88b1ed54 100644 --- a/Sources/Algorithms/SortedPrefix.swift +++ b/Sources/Algorithms/SortedPrefix.swift @@ -61,8 +61,13 @@ extension Collection { } let insertionIndex = try result.partitioningIndex { try areInIncreasingOrder(e, $0) } + let isLastElement = insertionIndex == result.endIndex result.removeLast() - result.insert(e, at: insertionIndex) + if isLastElement { + result.append(e) + } else { + result.insert(e, at: insertionIndex) + } } return result diff --git a/Tests/SwiftAlgorithmsTests/SortedPrefixTests.swift b/Tests/SwiftAlgorithmsTests/SortedPrefixTests.swift index 74190c43..17c21613 100644 --- a/Tests/SwiftAlgorithmsTests/SortedPrefixTests.swift +++ b/Tests/SwiftAlgorithmsTests/SortedPrefixTests.swift @@ -214,6 +214,46 @@ final class SortedPrefixTests: XCTestCase { input.sortedPrefix(1000, by: >), (1...1000).reversed() ) + + XCTAssertEqual( + ([0] + Array(repeating: 1, count: 100)).sortedPrefix(1, by: <), + [0] + ) + + XCTAssertEqual( + ([1] + Array(repeating: 0, count: 100)).sortedPrefix(1, by: <), + [0] + ) + + XCTAssertEqual( + ([0] + Array(repeating: 1, count: 100)).sortedPrefix(2, by: <), + [0, 1] + ) + + XCTAssertEqual( + ([1] + Array(repeating: 0, count: 100)).sortedPrefix(2, by: <), + [0, 0] + ) + + XCTAssertEqual( + ([1] + Array(repeating: 1, count: 100)).sortedPrefix(1, by: >), + [1] + ) + + XCTAssertEqual( + ([0] + Array(repeating: 1, count: 100)).sortedPrefix(1, by: >), + [1] + ) + + XCTAssertEqual( + ([1] + Array(repeating: 0, count: 100)).sortedPrefix(2, by: >), + [1, 0] + ) + + XCTAssertEqual( + ([0] + Array(repeating: 1, count: 100)).sortedPrefix(2, by: >), + [1, 1] + ) } func testStability() { @@ -221,10 +261,16 @@ final class SortedPrefixTests: XCTestCase { assertStability([1,1,1,2,5,7,3,6,2,5,7,3,6], withPrefix: 6) assertStability([1,1,1,2,5,7,3,6,2,5,7,3,6], withPrefix: 20) assertStability([1,1,1,2,5,7,3,6,2,5,7,3,6], withPrefix: 1000) - assertStability([0,0,0,0,0], withPrefix: 0) - assertStability([0,0,0,0,0], withPrefix: 1) - assertStability([0,0,0,0,0], withPrefix: 2) - assertStability([0,0,0,0,0], withPrefix: 5) + assertStability(Array(repeating: 0, count: 100), withPrefix: 0) + assertStability(Array(repeating: 0, count: 100), withPrefix: 1) + assertStability(Array(repeating: 0, count: 100), withPrefix: 2) + assertStability(Array(repeating: 0, count: 100), withPrefix: 5) + assertStability(Array(repeating: 0, count: 100), withPrefix: 20) + assertStability(Array(repeating: 0, count: 100), withPrefix: 100) + assertStability(Array(repeating: 1, count: 50) + Array(repeating: 0, count: 50), withPrefix: 2) + assertStability(Array(repeating: 1, count: 50) + Array(repeating: 0, count: 50), withPrefix: 5) + assertStability(Array(repeating: 1, count: 50) + Array(repeating: 0, count: 50), withPrefix: 20) + assertStability(Array(repeating: 1, count: 50) + Array(repeating: 0, count: 50), withPrefix: 50) assertStability([0,0], withPrefix: 1) assertStability([0,0], withPrefix: 2) assertStability([0,1,0,1,0,1], withPrefix: 2) @@ -237,6 +283,7 @@ final class SortedPrefixTests: XCTestCase { assertStability([1,1,1,0,0,0], withPrefix: 3) assertStability([1,1,1,0,0,0], withPrefix: 4) assertStability([1,1,1,0,0,0], withPrefix: 6) + assertStability([1,1,1,0,0,0], withPrefix: 5) } func assertStability(