From 07dc4b1075b7b5f0add5ef6c39d7513d062cc65b Mon Sep 17 00:00:00 2001 From: pccommen Date: Fri, 2 Sep 2022 22:21:19 +0900 Subject: [PATCH 1/2] make Network Module and fix Calculator I make Network Module It's looks like Alamofire description write on the code and fix Calculator --- .../Calculator.xcodeproj/project.pbxproj | 24 +- .../Calculator/Calculator/CalculatorApp.swift | 2 +- .../Calculator/Model/CalculatorModel.swift | 331 +++++++-------- .../Calculator/View/ContentView.swift | 55 ++- .../View/SubViews/CalculatorButton.swift | 108 +++++ .../Calculator/View/SubViews/NumberPad.swift | 118 ++++-- .../View/SubViews/NumberPadGrid.swift | 89 ---- .../Calculator/View/SubViews/ResultView.swift | 41 +- .../ViewModel/CaculatorManager.swift | 177 ++++---- .../NetworkModule.xcodeproj/project.pbxproj | 392 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 98 +++++ .../Assets.xcassets/Contents.json | 6 + .../tempImage.imageset/Contents.json | 21 + ...\354\275\230-\353\247\245\353\266\201.png" | Bin 0 -> 85898 bytes .../NetworkModule/Model/SearchModel.swift | 73 ++++ .../NetworkModule/Network/NaverOpenAPI.swift | 31 ++ .../NetworkModule/Network/NetworkModule.swift | 101 +++++ .../NetworkModule/NetworkModuleApp.swift | 20 + .../Preview Assets.xcassets/Contents.json | 6 + .../NetworkModule/View/ContentView.swift | 37 ++ .../NetworkModule/ViewModel/Finder.swift | 47 +++ 24 files changed, 1343 insertions(+), 460 deletions(-) create mode 100644 week4/Calculator/Calculator/View/SubViews/CalculatorButton.swift delete mode 100644 week4/Calculator/Calculator/View/SubViews/NumberPadGrid.swift create mode 100644 week5/NetworkModule/NetworkModule.xcodeproj/project.pbxproj create mode 100644 week5/NetworkModule/NetworkModule.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 week5/NetworkModule/NetworkModule.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 week5/NetworkModule/NetworkModule/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 week5/NetworkModule/NetworkModule/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 week5/NetworkModule/NetworkModule/Assets.xcassets/Contents.json create mode 100644 week5/NetworkModule/NetworkModule/Assets.xcassets/tempImage.imageset/Contents.json create mode 100644 "week5/NetworkModule/NetworkModule/Assets.xcassets/tempImage.imageset/\353\257\270\353\252\250\355\213\260\354\275\230-\353\247\245\353\266\201.png" create mode 100644 week5/NetworkModule/NetworkModule/Model/SearchModel.swift create mode 100644 week5/NetworkModule/NetworkModule/Network/NaverOpenAPI.swift create mode 100644 week5/NetworkModule/NetworkModule/Network/NetworkModule.swift create mode 100644 week5/NetworkModule/NetworkModule/NetworkModuleApp.swift create mode 100644 week5/NetworkModule/NetworkModule/Preview Content/Preview Assets.xcassets/Contents.json create mode 100644 week5/NetworkModule/NetworkModule/View/ContentView.swift create mode 100644 week5/NetworkModule/NetworkModule/ViewModel/Finder.swift diff --git a/week4/Calculator/Calculator.xcodeproj/project.pbxproj b/week4/Calculator/Calculator.xcodeproj/project.pbxproj index 026e5f3..1eba70e 100644 --- a/week4/Calculator/Calculator.xcodeproj/project.pbxproj +++ b/week4/Calculator/Calculator.xcodeproj/project.pbxproj @@ -14,8 +14,8 @@ 6F1A922928B13AE8009FBF28 /* CalculatorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A922828B13AE8009FBF28 /* CalculatorModel.swift */; }; 6F1A922B28B13B04009FBF28 /* CaculatorManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A922A28B13B04009FBF28 /* CaculatorManager.swift */; }; 6F1A922D28B2159B009FBF28 /* ResultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A922C28B2159B009FBF28 /* ResultView.swift */; }; - 6F2AB33028B8D9970054B4BB /* NumberPadGrid.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2AB32F28B8D9970054B4BB /* NumberPadGrid.swift */; }; - 6F2AB33228B8D9A60054B4BB /* NumberPad.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2AB33128B8D9A60054B4BB /* NumberPad.swift */; }; + 6F2AB33028B8D9970054B4BB /* NumberPad.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2AB32F28B8D9970054B4BB /* NumberPad.swift */; }; + 6F2AB33228B8D9A60054B4BB /* CalculatorButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2AB33128B8D9A60054B4BB /* CalculatorButton.swift */; }; 6F2AB33528B8DC930054B4BB /* Ex+Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2AB33428B8DC930054B4BB /* Ex+Color.swift */; }; 6F2AB33728B90EB10054B4BB /* Ex+String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2AB33628B90EB10054B4BB /* Ex+String.swift */; }; /* End PBXBuildFile section */ @@ -29,8 +29,8 @@ 6F1A922828B13AE8009FBF28 /* CalculatorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalculatorModel.swift; sourceTree = ""; }; 6F1A922A28B13B04009FBF28 /* CaculatorManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CaculatorManager.swift; sourceTree = ""; }; 6F1A922C28B2159B009FBF28 /* ResultView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResultView.swift; sourceTree = ""; }; - 6F2AB32F28B8D9970054B4BB /* NumberPadGrid.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NumberPadGrid.swift; sourceTree = ""; }; - 6F2AB33128B8D9A60054B4BB /* NumberPad.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NumberPad.swift; sourceTree = ""; }; + 6F2AB32F28B8D9970054B4BB /* NumberPad.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NumberPad.swift; sourceTree = ""; }; + 6F2AB33128B8D9A60054B4BB /* CalculatorButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalculatorButton.swift; sourceTree = ""; }; 6F2AB33428B8DC930054B4BB /* Ex+Color.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Ex+Color.swift"; sourceTree = ""; }; 6F2AB33628B90EB10054B4BB /* Ex+String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Ex+String.swift"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -113,8 +113,8 @@ isa = PBXGroup; children = ( 6F1A922C28B2159B009FBF28 /* ResultView.swift */, - 6F2AB32F28B8D9970054B4BB /* NumberPadGrid.swift */, - 6F2AB33128B8D9A60054B4BB /* NumberPad.swift */, + 6F2AB32F28B8D9970054B4BB /* NumberPad.swift */, + 6F2AB33128B8D9A60054B4BB /* CalculatorButton.swift */, ); path = SubViews; sourceTree = ""; @@ -205,8 +205,8 @@ 6F2AB33728B90EB10054B4BB /* Ex+String.swift in Sources */, 6F1A922B28B13B04009FBF28 /* CaculatorManager.swift in Sources */, 6F1A921828B13A71009FBF28 /* CalculatorApp.swift in Sources */, - 6F2AB33228B8D9A60054B4BB /* NumberPad.swift in Sources */, - 6F2AB33028B8D9970054B4BB /* NumberPadGrid.swift in Sources */, + 6F2AB33228B8D9A60054B4BB /* CalculatorButton.swift in Sources */, + 6F2AB33028B8D9970054B4BB /* NumberPad.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -335,7 +335,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"Calculator/Preview Content\""; - DEVELOPMENT_TEAM = 3FAK98S72C; + DEVELOPMENT_TEAM = VT6KC94KM7; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; @@ -348,7 +348,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = jum.Calculator; + PRODUCT_BUNDLE_IDENTIFIER = pccommen.Calculator; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; @@ -364,7 +364,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"Calculator/Preview Content\""; - DEVELOPMENT_TEAM = 3FAK98S72C; + DEVELOPMENT_TEAM = VT6KC94KM7; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; @@ -377,7 +377,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = jum.Calculator; + PRODUCT_BUNDLE_IDENTIFIER = pccommen.Calculator; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; diff --git a/week4/Calculator/Calculator/CalculatorApp.swift b/week4/Calculator/Calculator/CalculatorApp.swift index 5bb77cd..3a3c0fd 100644 --- a/week4/Calculator/Calculator/CalculatorApp.swift +++ b/week4/Calculator/Calculator/CalculatorApp.swift @@ -14,7 +14,7 @@ struct CalculatorApp: App { var body: some Scene { WindowGroup { - ContentView(calculatorManager: caculatorManager) + ContentView().environmentObject(caculatorManager) } } } diff --git a/week4/Calculator/Calculator/Model/CalculatorModel.swift b/week4/Calculator/Calculator/Model/CalculatorModel.swift index eb0e7c3..ff40a9c 100644 --- a/week4/Calculator/Calculator/Model/CalculatorModel.swift +++ b/week4/Calculator/Calculator/Model/CalculatorModel.swift @@ -8,211 +8,164 @@ import Foundation struct Calculator { - - var state: InputState = .newPreviousNumber - var operation: ArithmeticOperation? { - willSet { - if nextNumber != nil { calculate() } - } - } - var showingText: String - private(set) var previousNumber: Decimal? { - didSet { - guard let previousNumber = previousNumber else { return } - showingText = "\(previousNumber)" - } - } - private(set) var nextNumber: Decimal? { - didSet { - guard let nextNumber = nextNumber else { return } - showingText = "\(nextNumber)" - } - } - private(set) var result: Decimal { - didSet { - showingText = "\(result)" - previousNumber = result - nextNumber = nil - state = .finishInput - } - } - - enum InputState { - case newPreviousNumber - case newNextNumber - case ongoingPreviousNumber - case ongoingNextNumber - case finishInput - } - - mutating func clickNumber(_ number: Decimal) { - switch state { - case .newPreviousNumber, .finishInput: - previousNumber = number - state = .ongoingPreviousNumber - case .newNextNumber: - nextNumber = number - state = .ongoingNextNumber - case .ongoingPreviousNumber: - previousNumber = joinNumbers(with: number) - case .ongoingNextNumber: - nextNumber = joinNumbers(with: number) + + var state: InputState = .firstNumber(isStart: false) + var `operator`: ArithmeticOperation? { + + willSet { + + if state == .secondNumber(isStart: true) { calculate() } + } } - } - - mutating func calculate() { - guard let priorNumber = previousNumber, - let laterNumber = nextNumber, - let operation = operation else { return } - switch operation { - case .multiply: - result = priorNumber * laterNumber - case .divide: - result = priorNumber / laterNumber - case .minus: - result = priorNumber - laterNumber - case .plus: - result = priorNumber + laterNumber - default: - print("error") + var showingText: String + private(set) var previousNumber: Decimal? { + + didSet { + + guard let previousNumber = previousNumber else { return } + showingText = "\(previousNumber)" + } } - } - - mutating func joinNumbers(with nextElement: Decimal) -> Decimal { - let joinedString = showingText + "\(nextElement)" - guard let joinedNumber = Decimal(string: joinedString) else { return 0 } - return joinedNumber - } - - mutating func changeSign() { - if let priorNumber = previousNumber { - if let afterNumber = nextNumber { - nextNumber = -afterNumber - } else { - previousNumber = -priorNumber - } + private(set) var nextNumber: Decimal? { + + didSet { + + guard let nextNumber = nextNumber else { return } + showingText = "\(nextNumber)" + } } - } - - mutating func percent() { - if let priorNumber = previousNumber { - if let afterNumber = nextNumber { - nextNumber = afterNumber / 100 - } else { - previousNumber = priorNumber / 100 - } + private(set) var result: Decimal { + + didSet { + + showingText = "\(result)" + previousNumber = result + state = .finishInput + } } - } - - mutating func addPoint() { - if !showingText.contains(".") { - showingText = showingText + "." - if showingText.compare("0") == .orderedSame { - state = .ongoingPreviousNumber - } + + enum InputState: Equatable { + + case firstNumber(isStart: Bool) + case secondNumber(isStart: Bool) + case finishInput } - } -} - -extension Calculator { - - enum ArithmeticOperation: Identifiable, Equatable { - case equal - case plus(Bool) - case minus(Bool) - case multiply(Bool) - case divide(Bool) - case percent - case sign - case clear(clearType) - case number(Decimal) - case point - var id: String { return content } + mutating func clickNumber(_ number: Decimal) { + + switch state { + + case .firstNumber(false), .finishInput: + previousNumber = number + state = .firstNumber(isStart: true) + case .secondNumber(isStart: false): + nextNumber = number + state = .secondNumber(isStart: true) + case .firstNumber(isStart: true): + previousNumber = joinNumbers(with: number) + case .secondNumber(isStart: true): + nextNumber = joinNumbers(with: number) + } + } - var content: String { - switch self { - case .equal: - return "=" - case .plus: - return "+" - case .minus: - return "-" - case .multiply: - return "*" - case .divide: - return "/" - case .percent: - return "%" - case .sign: - return "±" - case .clear(.displayResult): - return "C" - case .clear(.allData): - return "AC" - case .number(let value): - return "\(value)" - case .point: - return "." - } + mutating func calculate() { + + guard let priorNumber = previousNumber, + let laterNumber = nextNumber, + let operation = `operator` else { return } + state = .finishInput + switch operation { + + case .multiply: + result = priorNumber * laterNumber + case .divide: + result = priorNumber / laterNumber + case .minus: + result = priorNumber - laterNumber + case .plus: + result = priorNumber + laterNumber + default: + print("error") + } } - var isActive: Bool { - switch self { - case .plus(let isActive), .multiply(let isActive), .minus(let isActive), .divide(let isActive): - return isActive - default: - return false - } + mutating func joinNumbers(with nextElement: Decimal) -> Decimal { + + let joinedString = showingText + "\(nextElement)" + guard let joinedNumber = Decimal(string: joinedString) else { return 0 } + return joinedNumber } - var backgroundColor: String { - switch self { - case .clear, .percent, .sign: - return "#A0A0A0" - case .number, .point: - return "#323232" - case .plus, .divide, .multiply, .minus, .equal: - return isActive ? "#FFFFFF" : "#E99D39" - } + mutating func changeSign() { + + if let priorNumber = previousNumber { + + if let afterNumber = nextNumber { nextNumber = -afterNumber } + else { previousNumber = -priorNumber} + } } - var textColor: String { - switch self { - case .clear, .percent, .sign: - return "#000000" - case .plus, .divide, .multiply, .minus, .equal, .number, .point: - return isActive ? "#E99D39" : "#FFFFFF" - } + mutating func percent() { + + if let priorNumber = previousNumber { + + if let afterNumber = nextNumber { nextNumber = afterNumber / 100 } + else { previousNumber = priorNumber / 100 } + } } - var buttonSize: Int { - switch self { - case .number(0): - return 2 - default: - return 1 - } + mutating func addPoint() { + + if !showingText.contains(".") { + + showingText = showingText + "." + if showingText.compare("0") == .orderedSame { + + state = .firstNumber(isStart: true) + } + } } +} + +extension Calculator { - mutating func activeButton(active: Bool) { - switch self { - case .divide: - self = .divide(active) - case .multiply: - self = .multiply(active) - case .minus: - self = .minus(active) - case .plus: - self = .plus(active) - default: - break - } + enum ArithmeticOperation: Identifiable, Equatable { + + case equal + case plus + case minus + case multiply + case divide + case percent + case sign + case clear + case number(Decimal) + case point + + var id: String { + switch self { + case .equal: + return "equal" + case .plus: + return "plus" + case .minus: + return "minus" + case .multiply: + return "multiply" + case .divide: + return "divide" + case .percent: + return "percent" + case .sign: + return "sign" + case .clear: + return "clear" + case .number(let decimal): + return "\(decimal)" + case .point: + return "point" + } + } + } - } - - enum clearType { - case allData - case displayResult - } } diff --git a/week4/Calculator/Calculator/View/ContentView.swift b/week4/Calculator/Calculator/View/ContentView.swift index e55c5e3..b13348c 100644 --- a/week4/Calculator/Calculator/View/ContentView.swift +++ b/week4/Calculator/Calculator/View/ContentView.swift @@ -8,27 +8,40 @@ import SwiftUI struct ContentView: View { - - @ObservedObject var calculatorManager: CalculatorManager - - var body: some View { - ZStack{ - Color.black.ignoresSafeArea() - VStack { - Spacer() - ResultView(showingText: calculatorManager.showingText) - .gesture( - DragGesture() - .onEnded { _ in calculatorManager.slideToRemove() } - ) - NumberPadGrid(items: calculatorManager.buttonList , columnsCount: 4) { item in - Button { - calculatorManager.click(item) - } label: { - NumberPad(buttonData: item) - } + + @EnvironmentObject var calculatorManager: CalculatorManager + + var body: some View { + + ZStack{ + + Color.black.ignoresSafeArea() + VStack { + + Spacer() + ResultView(showingText: calculatorManager.showingText) + .gesture( + DragGesture() + .onEnded { _ in calculatorManager.slideToRemove() } + ) + NumberPad(items: ContentView.buttonList , columnsCount: 4) { item in + + Button { calculatorManager.click(item) } + label: { CalculatorButton(buttonData: item) } + } + } } - } } - } +} + +extension ContentView { + + static let buttonList: [Calculator.ArithmeticOperation] = + [ + .clear, .sign, .percent, .divide, + .number(7), .number(8), .number(9), .multiply, + .number(4), .number(5), .number(6), .minus, + .number(1), .number(2), .number(3), .plus, + .number(0), .point, .equal + ] } diff --git a/week4/Calculator/Calculator/View/SubViews/CalculatorButton.swift b/week4/Calculator/Calculator/View/SubViews/CalculatorButton.swift new file mode 100644 index 0000000..81a4bd5 --- /dev/null +++ b/week4/Calculator/Calculator/View/SubViews/CalculatorButton.swift @@ -0,0 +1,108 @@ +// +// NumberPad.swift +// Calculator +// +// Created by changgyo seo on 2022/08/26. +// + +import SwiftUI + +struct CalculatorButton: View { + + @EnvironmentObject var calculatorManager: CalculatorManager + var buttonData: Calculator.ArithmeticOperation + + var body: some View { + + GeometryReader { geometry in + + ZStack { + + RoundedRectangle(cornerRadius: cornerRadius(in: geometry.size)) + .foregroundColor(backGroundColor) + Text(content) + .foregroundColor(textColor) + .font(font(in: geometry.size)) + } + } + } + + private func cornerRadius(in size: CGSize) -> CGFloat { + + size.width * DrawConstans.buttonCornerRadius + } + + private func font(in size: CGSize) -> Font { + + Font.system(size: size.height * DrawConstans.fontSizeScale) + } + + private var textColor: Color { + + switch buttonData { + + case .minus, .plus, .divide, .multiply, .equal, .number, .point: + if calculatorManager.calculateModel.operator == buttonData && + calculatorManager.isRecentedOperator { + + return .init(hex: "#E99D39") + } + else { return .init(hex: "#FFFFFF") } + default: + return .init(hex: "#000000") + } + } + private var backGroundColor: Color { + + switch buttonData { + + case .minus, .plus, .divide, .multiply, .equal: + if calculatorManager.calculateModel.operator == buttonData && + calculatorManager.isRecentedOperator { + + return .init(hex: "#FFFFFF") + } + else { return .init(hex: "#E99D39") } + case .number(_), .point: + return .init(hex: "#323232") + case .clear, .percent, .sign: + return .init(hex: "#A0A0A0") + } + } + var content: String { + + switch buttonData { + + case .equal: + return "=" + case .plus: + return "+" + case .minus: + return "-" + case .multiply: + return "*" + case .divide: + return "/" + case .percent: + return "%" + case .sign: + return "±" + case .clear: + if calculatorManager.showingText != "0" { return "C" } + return "AC" + case .number(let value): + return "\(value)" + case .point: + return "." + } + } + +} + +extension CalculatorButton { + + private enum DrawConstans { + static let buttonCornerRadius = 0.5 + static let fontSizeScale = 0.4 + } +} diff --git a/week4/Calculator/Calculator/View/SubViews/NumberPad.swift b/week4/Calculator/Calculator/View/SubViews/NumberPad.swift index 29444a8..c0605ea 100644 --- a/week4/Calculator/Calculator/View/SubViews/NumberPad.swift +++ b/week4/Calculator/Calculator/View/SubViews/NumberPad.swift @@ -1,5 +1,5 @@ // -// NumberPad.swift +// NumberPadGrid.swift // Calculator // // Created by changgyo seo on 2022/08/26. @@ -7,35 +7,97 @@ import SwiftUI -struct NumberPad: View { - - var buttonData: Calculator.ArithmeticOperation - - var body: some View { - GeometryReader { geometry in - ZStack { - RoundedRectangle(cornerRadius: cornerRadius(in: geometry.size)) - .foregroundColor(Color(hex: buttonData.backgroundColor)) - Text(buttonData.content) - .foregroundColor(Color(hex: buttonData.textColor)) - .font(font(in: geometry.size)) - } +struct NumberPad: View { + + let buttonDataArray: [ButtonDataArray] + let columnsCount: Int + let content: (Calculator.ArithmeticOperation) -> buttonListView + + var body: some View { + + VStack(spacing: 0) { + + ForEach(buttonDataArray) { items in + + HStack(spacing: 0) { + + ForEach(items.array) { item in + + let width = widthForColumnsCount(in: UIScreen.main.bounds.size, item: item) + let height = width / CGFloat(item == .number(0) ? 2 : 1) + content(item) + .padding(5) + .frame(width: width, height: height, alignment: .center) + } + } + } + } + } + + init(items: [Calculator.ArithmeticOperation], columnsCount: Int, @ViewBuilder content: @escaping (Calculator.ArithmeticOperation) -> buttonListView) { + + self.buttonDataArray = ButtonDataArray.makeTwoDimensionArrayForColumnsCount(items: items, columnsCount: columnsCount) + self.columnsCount = columnsCount + self.content = content + } + + private func widthForColumnsCount(in size: CGSize, item: Calculator.ArithmeticOperation) -> CGFloat { + + let sizeForOne = size.width / CGFloat(columnsCount) + return item == .number(0) ? sizeForOne * 2 : sizeForOne } - } - - private func cornerRadius(in size: CGSize) -> CGFloat { - size.width * DrawConstans.buttonCornerRadius - } - - private func font(in size: CGSize) -> Font { - Font.system(size: size.height * DrawConstans.fontSizeScale) - } } extension NumberPad { - - private enum DrawConstans { - static let buttonCornerRadius = 0.5 - static let fontSizeScale = 0.4 - } + + struct ButtonDataArray: Identifiable { + + var id: Int + var array: [Calculator.ArithmeticOperation] + var totalWidth: Int + + init() { + + array = [Calculator.ArithmeticOperation]() + id = 1 + totalWidth = 0 + } + + mutating func append(_ newItem: Calculator.ArithmeticOperation) { + + array.append(newItem) + } + + mutating func changeId() { + + id += 1 + totalWidth = 0 + array.removeAll() + } + + static func makeTwoDimensionArrayForColumnsCount(items: [Calculator.ArithmeticOperation], columnsCount: Int) -> [ButtonDataArray] { + + var arrayWithColumnsCount = ButtonDataArray() + var TwoDemesionArray = [ButtonDataArray]() + var rowWidth = 0 + items.forEach { item in + + let buttonSize = item == .number(0) ? 2 : 1 + if rowWidth + buttonSize <= columnsCount { + + arrayWithColumnsCount.append(item) + rowWidth += buttonSize + } else { + + arrayWithColumnsCount.totalWidth = rowWidth + TwoDemesionArray.append(arrayWithColumnsCount) + arrayWithColumnsCount.changeId() + arrayWithColumnsCount.append(item) + rowWidth = buttonSize + } + } + TwoDemesionArray.append(arrayWithColumnsCount) + return TwoDemesionArray + } + } } diff --git a/week4/Calculator/Calculator/View/SubViews/NumberPadGrid.swift b/week4/Calculator/Calculator/View/SubViews/NumberPadGrid.swift deleted file mode 100644 index 1bb1db6..0000000 --- a/week4/Calculator/Calculator/View/SubViews/NumberPadGrid.swift +++ /dev/null @@ -1,89 +0,0 @@ -// -// NumberPadGrid.swift -// Calculator -// -// Created by changgyo seo on 2022/08/26. -// - -import SwiftUI - -struct NumberPadGrid: View { - - let buttonDataArray: [ButtonDataArray] - let columnsCount: Int - let content: (Calculator.ArithmeticOperation) -> buttonListView - - var body: some View { - VStack(spacing: 0) { - ForEach(buttonDataArray) { items in - HStack(spacing: 0) { - ForEach(items.array) { item in - let width = widthForColumnsCount(in: UIScreen.main.bounds.size, - buttonWidth: item.buttonSize) - let height = width / CGFloat(item.buttonSize) - content(item) - .padding(5) - .frame(width: width, height: height, alignment: .center) - } - } - } - } - } - - init(items: [Calculator.ArithmeticOperation], columnsCount: Int, @ViewBuilder content: @escaping (Calculator.ArithmeticOperation) -> buttonListView) { - self.buttonDataArray = ButtonDataArray.makeTwoDemsionArrayForColumnsCount(items: items, columnsCount: columnsCount) - self.columnsCount = columnsCount - self.content = content - } - - private func widthForColumnsCount(in size: CGSize, buttonWidth width: Int) -> CGFloat { - let sizeForOne = size.width / CGFloat(columnsCount) - return sizeForOne * CGFloat(width) - } -} - -extension NumberPadGrid { - - struct ButtonDataArray: Identifiable { - - var id: Int - var array: [Calculator.ArithmeticOperation] - var totalWidth: Int - - init() { - array = [Calculator.ArithmeticOperation]() - id = 1 - totalWidth = 0 - } - - mutating func append(_ newItem: Calculator.ArithmeticOperation) { - array.append(newItem) - } - - mutating func changeId() { - id += 1 - totalWidth = 0 - array.removeAll() - } - - static func makeTwoDemsionArrayForColumnsCount(items: [Calculator.ArithmeticOperation], columnsCount: Int) -> [ButtonDataArray] { - var arrayWithColumnsCount = ButtonDataArray() - var TwoDemesionArray = [ButtonDataArray]() - var rowWidth = 0 - items.forEach { item in - if rowWidth + item.buttonSize <= columnsCount { - arrayWithColumnsCount.append(item) - rowWidth += item.buttonSize - } else { - arrayWithColumnsCount.totalWidth = rowWidth - TwoDemesionArray.append(arrayWithColumnsCount) - arrayWithColumnsCount.changeId() - arrayWithColumnsCount.append(item) - rowWidth = item.buttonSize - } - } - TwoDemesionArray.append(arrayWithColumnsCount) - return TwoDemesionArray - } - } -} diff --git a/week4/Calculator/Calculator/View/SubViews/ResultView.swift b/week4/Calculator/Calculator/View/SubViews/ResultView.swift index 6c52efe..26255ed 100644 --- a/week4/Calculator/Calculator/View/SubViews/ResultView.swift +++ b/week4/Calculator/Calculator/View/SubViews/ResultView.swift @@ -8,28 +8,29 @@ import SwiftUI struct ResultView: View { - - let showingText: String - - var body: some View { - HStack { - Spacer() - Text(showingText) - .lineLimit(1) - .foregroundColor(.white) - .font(.system(size: DrawConstans.fontSize)) - .minimumScaleFactor(DrawConstans.fontMinimumSacleFactor) - .padding(.trailing, DrawConstans.textPadding) + + let showingText: String + + var body: some View { + + HStack { + Spacer() + Text(showingText) + .lineLimit(1) + .foregroundColor(.white) + .font(.system(size: DrawConstans.fontSize)) + .minimumScaleFactor(DrawConstans.fontMinimumSacleFactor) + .padding(.trailing, DrawConstans.textPadding) + } + .contentShape(Rectangle()) } - .contentShape(Rectangle()) - } } extension ResultView { - - private enum DrawConstans { - static let fontSize = 80.0 - static let fontMinimumSacleFactor = 0.5 - static let textPadding = 20.0 - } + + private enum DrawConstans { + static let fontSize = 80.0 + static let fontMinimumSacleFactor = 0.5 + static let textPadding = 20.0 + } } diff --git a/week4/Calculator/Calculator/ViewModel/CaculatorManager.swift b/week4/Calculator/Calculator/ViewModel/CaculatorManager.swift index 0181495..bd53630 100644 --- a/week4/Calculator/Calculator/ViewModel/CaculatorManager.swift +++ b/week4/Calculator/Calculator/ViewModel/CaculatorManager.swift @@ -8,107 +8,84 @@ import SwiftUI class CalculatorManager: ObservableObject { - - @Published private var calculateModel = create() - @Published var buttonList: [Calculator.ArithmeticOperation] = - [ - .clear(.allData), .sign, .percent, .divide(false), - .number(7), .number(8), .number(9), .multiply(false), - .number(4), .number(5), .number(6), .minus(false), - .number(1), .number(2), .number(3), .plus(false), - .number(0), .point, .equal - ] - - var showingText: String { - get { - if calculateModel.showingText != "0" { - guard let clearIndex = buttonList.firstIndex(of: .clear(.allData)) else { return calculateModel.showingText } - buttonList[clearIndex] = .clear(.displayResult) - } else { - guard let clearIndex = buttonList.firstIndex(of: .clear(.displayResult)) else { return calculateModel.showingText } - buttonList[clearIndex] = .clear(.allData) - } - return calculateModel.showingText + + @Published private(set) var calculateModel = create() + private(set) var isRecentedOperator: Bool = false + + var showingText: String { + get { calculateModel.showingText } + set { calculateModel.showingText = newValue } } - set { calculateModel.showingText = newValue } - } - - private static func create() -> Calculator { - return Calculator(showingText: "0", result: 0) - } - - // MARK: - Intentions - - func click(_ clickType: Calculator.ArithmeticOperation) { - switch clickType { - case .number(let decimal): - if calculateModel.operation == nil, - calculateModel.state != .ongoingPreviousNumber { - calculateModel.state = .newPreviousNumber - } - for index in buttonList.indices { - if buttonList[index].isActive { - switch buttonList[index] { - case .plus, .minus, .divide, .multiply: - buttonList[index].activeButton(active: false) - default: - print("error") - } + private static func create() -> Calculator { + return Calculator(showingText: "0", result: 0) + } + + // MARK: - Intentions + + func click(_ clickType: Calculator.ArithmeticOperation) { + + isRecentedOperator = false + switch clickType { + + case .number(let decimal): + click(decimal) + case .equal: + calculate() + case .clear: + if showingText != "0" { showingText = "0"} + else { clear() } + case .sign: + changeSign() + case .percent: + percent() + case .point: + addPoint() + default: + isRecentedOperator = true + click(operation: clickType) + } + } + + func click(_ number: Decimal) { + + calculateModel.clickNumber(number) + } + + func click(operation: Calculator.ArithmeticOperation?) { + + calculateModel.`operator` = operation + if calculateModel.state == .finishInput { + + calculateModel.state = .secondNumber(isStart: false) + } + else if calculateModel.state == .firstNumber(isStart: true) { + + calculateModel.state = .secondNumber(isStart: false) } - } - clickNumber(decimal) - case .equal: - calculate() - case .clear(.allData): - clear() - case .clear(.displayResult): - showingText = "0" - case .sign: - changeSign() - case .percent: - percent() - case .point: - addPoint() - default: - for index in buttonList.indices { - buttonList[index] == clickType - ? buttonList[index].activeButton(active: true) - : buttonList[index].activeButton(active: false) - } - clickOperation(clickType) + else { calculateModel.state = .firstNumber(isStart: false) } + } + + func calculate() { + calculateModel.calculate() + } + + func clear() { + calculateModel = CalculatorManager.create() + } + + func changeSign() { + calculateModel.changeSign() + } + + func slideToRemove() { + showingText.removeWithDecimal() + } + + func percent() { + calculateModel.percent() + } + + func addPoint() { + calculateModel.addPoint() } - } - - func clickNumber(_ number: Decimal) { - calculateModel.clickNumber(number) - } - - func clickOperation(_ operation: Calculator.ArithmeticOperation?) { - calculateModel.operation = operation - calculateModel.state = .newNextNumber - } - - func calculate() { - calculateModel.calculate() - } - - func clear() { - calculateModel = CalculatorManager.create() - } - - func changeSign() { - calculateModel.changeSign() - } - - func slideToRemove() { - showingText.removeWithDecimal() - } - - func percent() { - calculateModel.percent() - } - - func addPoint() { - calculateModel.addPoint() - } } diff --git a/week5/NetworkModule/NetworkModule.xcodeproj/project.pbxproj b/week5/NetworkModule/NetworkModule.xcodeproj/project.pbxproj new file mode 100644 index 0000000..2879157 --- /dev/null +++ b/week5/NetworkModule/NetworkModule.xcodeproj/project.pbxproj @@ -0,0 +1,392 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 55; + objects = { + +/* Begin PBXBuildFile section */ + D434BDE228BCE54000A2E32B /* NetworkModuleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D434BDE128BCE54000A2E32B /* NetworkModuleApp.swift */; }; + D434BDE428BCE54000A2E32B /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D434BDE328BCE54000A2E32B /* ContentView.swift */; }; + D434BDE628BCE54100A2E32B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D434BDE528BCE54100A2E32B /* Assets.xcassets */; }; + D434BDE928BCE54100A2E32B /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D434BDE828BCE54100A2E32B /* Preview Assets.xcassets */; }; + D434BDF028BCE55400A2E32B /* NetworkModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D434BDEF28BCE55400A2E32B /* NetworkModule.swift */; }; + D434BDF428BDDB1600A2E32B /* Finder.swift in Sources */ = {isa = PBXBuildFile; fileRef = D434BDF328BDDB1600A2E32B /* Finder.swift */; }; + D434BDF628BE0C6300A2E32B /* NaverOpenAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = D434BDF528BE0C6300A2E32B /* NaverOpenAPI.swift */; }; + D434BDFB28BE0EEB00A2E32B /* SearchModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D434BDFA28BE0EEB00A2E32B /* SearchModel.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + D434BDDE28BCE54000A2E32B /* NetworkModule.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NetworkModule.app; sourceTree = BUILT_PRODUCTS_DIR; }; + D434BDE128BCE54000A2E32B /* NetworkModuleApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkModuleApp.swift; sourceTree = ""; }; + D434BDE328BCE54000A2E32B /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + D434BDE528BCE54100A2E32B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + D434BDE828BCE54100A2E32B /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + D434BDEF28BCE55400A2E32B /* NetworkModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkModule.swift; sourceTree = ""; }; + D434BDF328BDDB1600A2E32B /* Finder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Finder.swift; sourceTree = ""; }; + D434BDF528BE0C6300A2E32B /* NaverOpenAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NaverOpenAPI.swift; sourceTree = ""; }; + D434BDFA28BE0EEB00A2E32B /* SearchModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchModel.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + D434BDDB28BCE54000A2E32B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + D434BDD528BCE54000A2E32B = { + isa = PBXGroup; + children = ( + D434BDE028BCE54000A2E32B /* NetworkModule */, + D434BDDF28BCE54000A2E32B /* Products */, + ); + sourceTree = ""; + }; + D434BDDF28BCE54000A2E32B /* Products */ = { + isa = PBXGroup; + children = ( + D434BDDE28BCE54000A2E32B /* NetworkModule.app */, + ); + name = Products; + sourceTree = ""; + }; + D434BDE028BCE54000A2E32B /* NetworkModule */ = { + isa = PBXGroup; + children = ( + D434BDE128BCE54000A2E32B /* NetworkModuleApp.swift */, + D434BDE528BCE54100A2E32B /* Assets.xcassets */, + D434BDE728BCE54100A2E32B /* Preview Content */, + D434BDF728BE0EC000A2E32B /* Network */, + D434BDFC28BE0EEF00A2E32B /* Model */, + D434BDF828BE0EC800A2E32B /* View */, + D434BDF928BE0ED100A2E32B /* ViewModel */, + ); + path = NetworkModule; + sourceTree = ""; + }; + D434BDE728BCE54100A2E32B /* Preview Content */ = { + isa = PBXGroup; + children = ( + D434BDE828BCE54100A2E32B /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + D434BDF728BE0EC000A2E32B /* Network */ = { + isa = PBXGroup; + children = ( + D434BDEF28BCE55400A2E32B /* NetworkModule.swift */, + D434BDF528BE0C6300A2E32B /* NaverOpenAPI.swift */, + ); + path = Network; + sourceTree = ""; + }; + D434BDF828BE0EC800A2E32B /* View */ = { + isa = PBXGroup; + children = ( + D434BDE328BCE54000A2E32B /* ContentView.swift */, + ); + path = View; + sourceTree = ""; + }; + D434BDF928BE0ED100A2E32B /* ViewModel */ = { + isa = PBXGroup; + children = ( + D434BDF328BDDB1600A2E32B /* Finder.swift */, + ); + path = ViewModel; + sourceTree = ""; + }; + D434BDFC28BE0EEF00A2E32B /* Model */ = { + isa = PBXGroup; + children = ( + D434BDFA28BE0EEB00A2E32B /* SearchModel.swift */, + ); + path = Model; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + D434BDDD28BCE54000A2E32B /* NetworkModule */ = { + isa = PBXNativeTarget; + buildConfigurationList = D434BDEC28BCE54100A2E32B /* Build configuration list for PBXNativeTarget "NetworkModule" */; + buildPhases = ( + D434BDDA28BCE54000A2E32B /* Sources */, + D434BDDB28BCE54000A2E32B /* Frameworks */, + D434BDDC28BCE54000A2E32B /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = NetworkModule; + productName = NetworkModule; + productReference = D434BDDE28BCE54000A2E32B /* NetworkModule.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + D434BDD628BCE54000A2E32B /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1340; + LastUpgradeCheck = 1340; + TargetAttributes = { + D434BDDD28BCE54000A2E32B = { + CreatedOnToolsVersion = 13.4.1; + }; + }; + }; + buildConfigurationList = D434BDD928BCE54000A2E32B /* Build configuration list for PBXProject "NetworkModule" */; + compatibilityVersion = "Xcode 13.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = D434BDD528BCE54000A2E32B; + productRefGroup = D434BDDF28BCE54000A2E32B /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + D434BDDD28BCE54000A2E32B /* NetworkModule */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + D434BDDC28BCE54000A2E32B /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D434BDE928BCE54100A2E32B /* Preview Assets.xcassets in Resources */, + D434BDE628BCE54100A2E32B /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + D434BDDA28BCE54000A2E32B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D434BDF428BDDB1600A2E32B /* Finder.swift in Sources */, + D434BDF628BE0C6300A2E32B /* NaverOpenAPI.swift in Sources */, + D434BDE428BCE54000A2E32B /* ContentView.swift in Sources */, + D434BDFB28BE0EEB00A2E32B /* SearchModel.swift in Sources */, + D434BDE228BCE54000A2E32B /* NetworkModuleApp.swift in Sources */, + D434BDF028BCE55400A2E32B /* NetworkModule.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + D434BDEA28BCE54100A2E32B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.5; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + D434BDEB28BCE54100A2E32B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.5; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + D434BDED28BCE54100A2E32B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"NetworkModule/Preview Content\""; + DEVELOPMENT_TEAM = VT6KC94KM7; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = pccommen.NetworkModule; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + D434BDEE28BCE54100A2E32B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"NetworkModule/Preview Content\""; + DEVELOPMENT_TEAM = VT6KC94KM7; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = pccommen.NetworkModule; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + D434BDD928BCE54000A2E32B /* Build configuration list for PBXProject "NetworkModule" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D434BDEA28BCE54100A2E32B /* Debug */, + D434BDEB28BCE54100A2E32B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D434BDEC28BCE54100A2E32B /* Build configuration list for PBXNativeTarget "NetworkModule" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D434BDED28BCE54100A2E32B /* Debug */, + D434BDEE28BCE54100A2E32B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = D434BDD628BCE54000A2E32B /* Project object */; +} diff --git a/week5/NetworkModule/NetworkModule.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/week5/NetworkModule/NetworkModule.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/week5/NetworkModule/NetworkModule.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/week5/NetworkModule/NetworkModule.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/week5/NetworkModule/NetworkModule.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/week5/NetworkModule/NetworkModule.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/week5/NetworkModule/NetworkModule/Assets.xcassets/AccentColor.colorset/Contents.json b/week5/NetworkModule/NetworkModule/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/week5/NetworkModule/NetworkModule/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/week5/NetworkModule/NetworkModule/Assets.xcassets/AppIcon.appiconset/Contents.json b/week5/NetworkModule/NetworkModule/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..9221b9b --- /dev/null +++ b/week5/NetworkModule/NetworkModule/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/week5/NetworkModule/NetworkModule/Assets.xcassets/Contents.json b/week5/NetworkModule/NetworkModule/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/week5/NetworkModule/NetworkModule/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/week5/NetworkModule/NetworkModule/Assets.xcassets/tempImage.imageset/Contents.json b/week5/NetworkModule/NetworkModule/Assets.xcassets/tempImage.imageset/Contents.json new file mode 100644 index 0000000..a01a527 --- /dev/null +++ b/week5/NetworkModule/NetworkModule/Assets.xcassets/tempImage.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "미모티콘-맥북.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git "a/week5/NetworkModule/NetworkModule/Assets.xcassets/tempImage.imageset/\353\257\270\353\252\250\355\213\260\354\275\230-\353\247\245\353\266\201.png" "b/week5/NetworkModule/NetworkModule/Assets.xcassets/tempImage.imageset/\353\257\270\353\252\250\355\213\260\354\275\230-\353\247\245\353\266\201.png" new file mode 100644 index 0000000000000000000000000000000000000000..59e5b0015c6621209e6bc710a4a4e63b0210bcf0 GIT binary patch literal 85898 zcmeFZ1$SF9vnZN490wc5hS_1JhMAcgW@c#9#9?M;W@cti9cHM*%+NHYn||lqbKhI* z{eyS6m#mTYj5IS_8jVKVj#5*RMMot@g@J)VmzR^$fPsN6`DgPM&`M9RNQSh>|v$9i(qEb*$2)kQa3u;Ko{5SbqNrcMQ)6-Rujm^i$ht-FR)y3V0jYB{{ zfQ_A#jgu4fh5_>MbM`dz1vz_A|5qdbyB#Sj4-0oYS5G?^XNrIAnwh(Jd5Ta`{p;v| zuK(iG%Gd7y^yKXE-(tN9$oB6J8wV>p+yAuvMk@TTR#4l+%3adM$;ryuQ}q8!iNgOO z{(qtUch|yf|M>ZT@b_QB|EKm%dQntiw*S$jC~EAq${-Ak7>vA>xRx*MWuIT8&4kyt zwTIOWKA4cabw0p8RdTab)Zu(GsbW&C0_j*8ZG3K-#*ul-vlsMjPT=$FbKk(Xn?Ak}*dM#S z*Wa(NmTWGrJ#Iu!yubB-&(Ki(3i!XD|Eq!j|7yS`1fjhTT`*_bI#}5yI4u=pEpI-| z{ln4V7sW@HibrhM16Ha#{cfQ$RV#K7QsJ*W{r8pg#z`dWER9wm_G|ym=5brHZ-V%Q zh#8$&@yDi80=$4(KPNth#9rP447OZFJEk0_L#jF387g*lm-`9gTJ zgWoEeUv3E2HChJK8^w&?8y2_Jr5Mfu+;1b!o=T?QFHMFQj>vzgbCM@vhoy5jQM$xX0Sn~Q;nu8{ z7)e)1HC($Dht6xE-^(9orVZ@zJ1Pv3*1V^DPz@f%rOu^UIrnNsBJ^r<=9X%RR8m1=#1W0mLm!o?bYW-RPoD&Mqj z6O5v}SbibvaEsptBYujB8+sZT3&zz{fHuLDwtP|;T*h#MrvPIN$p%Nlv?{||E5OfG z*x))?YlSz0K%jh87g9gUtA2mg?4ACP3<$-XQqy;nj&o&F$d~kBAaVlyvbC0WKH+CqM;wYF%nc{*lMvamX2MOO0_uc;!yF!6kLw@}$C z7(bPFWzRk??L8}Xks$Juj5s`nauWw2doie-#c4=H?r{P4<*q6C z_ymhN6Y*stNZ-q{x~|5N$cXvRg|3cQAA?5_J<*mq_ zrvjFv5vt3$P!(M4EkbEI0IB8~L+`bN_Vw3$41<#K3^rNtJ2Mp>S|01#9ypuC;HkiB zN`2B;%|?@F#+%4ew8)INv$omFu(l*61;WEkg%YNeFNc%wg2(MfNd{Qbx!a!im%hob z^&T8^i-a$AZ0>svF!zw5RS}JAA$%{fwcS^nbpHMwJdTVzeEG!inBgV zVsTKpG+-I!y)t%e=6#IV6E;F6umA8k$jKAvsk)87Ih^J9{D5NCXfaBsqTykPD|`M~LHuRm@eYfz223;QVTzYDvv3 zXWK-Yxy;O8-7%*w!ynRZe(WAPQ4uY7%YkwA`x_6T=mA#oY-#~urCG<41(d54a{?)q zR=xxEmurVatHkD-N8dDpmoAw8s*#9Hp@N*GfVE!IPNTdXyvF1Bi8*a&U4(epL60vJ za0}!|j!H9M$K`C>v!f3@t%)(n*4zsFL^u|l zT76H7GM{IsLKIUYMP-=L;7Kblne?O&T7wQ};nl$@2$f~ham5g3xjdcs5Spgpz*re?A>9*2l&FcOVJEP>q^XJ_{ zM^EMF3J3+TbAA6jVk1=CI4t>Rc9q=}AzWs^lCOTN-Yv%Wm4n=H`CE?;BLJliqxray zQn#kN^p*1%3GM13GSdD@wuGVPS_fSP)4|Na;f+ai?wBToy+8?_=0f;_+fB!1O2Ayo z@v#pB*6Va{F>;Zdz7H;lEvYnuOmi~0eBv7(%OqB)9KI##rRlke}C8aa$LS&T>wa{e~$XGJyGaC#0b@Ho8YspHNGz*1~K0Z6Ua>batozCE8 zAOa0SN!HaCkKkt63BLi)M6weQ7u7i3f(DZ`KVaiPMT#7N0gIUJX0_f3?$HzlXO$SS z@u!2??5GLTPUM$wn0h{!)kvb=+b&A3AZ!#CApSyqkMAf*2f!#}k%yU{&b*}uheWip794zoxyq>EgGzyuJR&Frn z^Y2IA{!isS^-42>fC(YR(y_5I&*Nnhy9tO0ahY;O)w$IMi`}Q94+PaLNMWcgOoZa4 z*3tK0)pi*dK5a|2h;gWyM^Q_%oEI6h70pH{a8>fU0fD=Qx z8K9!qU3Qa>a9{SiQ(eE(f(N3=@O^o&TBdqVw<9aVIV`;7X|O8N^I2ss_Idh;?p%y- zMdSx6AnXK{(qW-O=-r^DG)*el`aZ8M2F>W%WxGB`P-!{jT>-xTL!9p7u88g_8GS)F z7Y4yustL;>2XlhAyIExnRQ*7Uj;MVS$i5zuun?EPIqPae+~_3gexK$K)oMq;_MbcZ zMPj!%`=j+%M#2A4E1yr=)n3zIud}RU3y^Wn8PKz}@#vxfWEb?87iY{T`D%x^(<>v} zvQ@#OT#*^mS*7V%YQpsVk=x?es;RXdm5;H@T3-LnH zXb53#Cu}DNG5FBb$V;oSmpFR`#O#(^OMPIQ12f(?;UuI9^+lnCX#dGwO4o>L&G?g_2#7Ot~2aPo@m4;kno{crPG z_Z8x|j3Kn@$lP1dtAdFliyu9c151lAR zqoldW?Dcc9QuxuWaF|06$SUy-GRkiYIjTrL59lmzOF9Q)D1Y+9F=@B^sIOd4^v}1_ zNA-=XK>L3~Lqm_Eeo?Wg=4^c!Ke0#XmqWlJeFnGj#6!uI0T;VZnQ2_I_3`&jPvcKX zTNR_`Ssh4leqVrC*!;ruwx(KF@cgG0j_)p=} zN$e`>dUJ^N{UZ7m(}n#ryIEU9z0EimTj~=>)XPsaR*!-s2;m2Kf6l?m^B*808Og|F ztieXRoNe5@Au!}XmNKRY+K?q${QKpFhN8Y_UoTgl(Qhug=@*wNn!$<&ue>?9hRuT+ zhlzg*QbqffJLXs(c8e(A#Sk5PE8B&7ios5ONL65OK4S-p!BYa>1s7q+35e+x1|8=- zY?xHW%65a5@vLByO);G@NOo)~k!%L_Wom8a@>L#=%Jg*(i89XaVU9T+hL&%fsyvFQ zq_;eTs>&3>Tsxd4!EjesZd$F%$>9wityXpVZE5y|_=-e!3UC$M=Sz1@4RseYU@jo> z71Npo^1C9{3rdW?B5r^DNjS2UH?QmT3lYjXtOr1BzY772Cp=aYr;!NgLfqPXU%~*3 zkSk)!CjYpCPm|&k_~fbud>+-{uh`vu%e!Yh5HhE6Nj0YUO&|r>ys^kQYN?j^ zq0{(xafyxDUhZe2T<_P)c+`0@KxQkEmtJ_osrWa};?Yiz!T&vBdk zctAR>$KSIAdB0Xzqsjxjho5gRIYktKkpQ#0ZK z0ozC-xZldTCZR7q!PcWH#<}Ni(En^J!)lbAmOkaLiBx_8y%+T;>s!uZ;czD!X3QBLMvr_BUt>pD z&S@_h^wXNt;%nkEk(CUd230#`O<%u_J~=pz9zNUujA<4FDiBz;(>>0$4qqpw21YGl z!`k)azuzF}d%9b;tNWS1UxW5ESvk0|Fe4ccUwY~3R-CsKiW$%oaw%{I4LMeVAY5Kf zH}|Fq8P_9mxaJAZY5UH9z=k2$4-FQV_%gHg6Lnu60Z8IUMQ@u@0 zHGB_1gsX>`&HPdU>qqqqSptG*1oSav!=v&(yHVR2cowqnCyMUAn8+2f^4|<8}G$Ba7rzm;Wrpw-`GoG2oSya1hK~P%VE7yzVtX=$xtbaUr$xdqE%RvWOOI$QSxd(Wf{Gr=l41lB) ze$gCg73oKQNm2^PLLN<_1(;PS%wt_`lF}aK2Zs0ORO5~oe8eRi;ql}P-ezx&QQN2t zn;s?#Vz_q~N|aoa9t+FB^3$sQ884r!>rN%HM@L!*HiKB|Lk2j*c<&)dqWE-wmt{B? zt1Wfc*~NRq0oVoASle!I1CP!Ye{lmK=qe2cf+@2!umZqADk=ju{^QxSwr*BI?6nx5 z=Qa&7pYrQj**Ysl(=aPisjvcSh0b;BR{kRwlYwf}sA7mr4N96xJJ z%8H%6#}2|^JDF#Ba+UEUq9@MrXkOFdoUjfnQhppHkV)b4EY!L^0TIDQv{6qVCiSw7 z^mRyoVj!o--%s~J8z&zdPQjio^g$)_tRWgA7mk*w!@t&KX*YF=YAo zHgXUAWT2nz1v!n{-ZgBFkyg;-=fm|`#_7s74;Bnei=2lUPcBhTM6uW}5#l818QED1 zM8z7XzzqiGn>OXz-V*K#)vT76A<+;^)YXBK^L0Tv!t+)TS8SyxXEA# zd8igmSjsaX@{)8%NCwKeamg-gx^&Q!J@%r+W*<_#Jy?Ai3B|3+t=sEG3WLGAB0Gj7 zYeVB1TixRtf3ysbw@oyZ_|*8Uv5stciqbwCyKADc@h3t+X_4SJEU(N9;ufK>JEdVh zb@Pij+q#O~pZJg1`72zqGK!A5lChb%{TRSE55=??!9XqBHoH!F(9bs25>`F|Ik~^v zg1NSwhfPyw!)gMzByLlEZ(_DfI=lo9f&sa&zLYva5$Jpva08@Btj-YG!JD#eiLTkb z8#)d()nt`Aw$Oju?A8kzYFQa9NYaLc_Ghy}Z5^%o+Jh_Mu_5;uh`>~G5c0kc2_oPJ zstnU+P46j3`Y^ICZN`vuH7s1SGD|g6k`Ak>X?I3pv+wjF6wWeS0huBv2 zzcmIg^MDkzg+(b~>se1!`Nk$ZdUXD961&7EtBd)^pT`}Ez8t)N844QOW2%JmnW4SS zCDQ^b(|=M)lrX|;Gf0XfxY&*fzuQJ;^3Ir_IM!n!DvohTK7HA{@E6|~6R~0B*Hk}T zdL9YfO{l#d=BjE_vHrb^>@Y;5O$X)7LWrf`WT=$%ElMr<=ql9w_~Ej6n_-g(*N+}x z$tcxJQzBJ8h_ONmN7d;;zMf#@>v4HxP<{vXqbke!@}cY-8X3q1zY;VrqtM1f?1*o= z8Q+5kPF5Z`HKs{_y{G7@ZEh(HIfzHsJ^8Xnxbu7O_b1@rLlxEDaAZf`Kqx11+^>LU z4OW8E3rzf@G0G6;{h^Ood7CU2%r}Tm(qZ(67ogjVS>@F39T{ADI)@NVAyG}JGS<}H zCP6r?<9^l*dY)eh+lhK;K|2wfyW`~nCa32FQC@Rgx-0d(Cu{2VH;Y9Um+WI}nUOL= z4zeYNUk6TAof{ojGN5_zVx@9B_pv6b9EX#vShKb_JdFtb?RSlHpCnf)-zxtSLSII9 z`gCQ~_mRn@-m=Ij88YN(0Q=5))Wr)-?-HL@g>?Z`MmS6DoR{p7w!Tvif(!n;VS{xc z7+OC`S2D)6oB$zl3MS7hG8|tvKb;Jr^~~6zu4t)ogw?IWi%*~%6;fEB8__LR$F8Iz54(%RA}UZEzcL*@@T;^7 z6&_tV!r;w`aJ;r8jaY;|%|k}7-qZ^1a~cPFjN6#VD8^q{qSG^$`#%*tHYIR>&G-;X z&9LW7ZVP;G8I~T7Z!2zRn*=iY<0OF6op32Y&PDNtD9tA|?Pzi6fe$*NwLm zH~6c2hO<)o^RwY#fsnH+iGw<=-n!MUwR&kYUCh z!4nz6j=OuWvfulXcN{hT5&sx%3)_qBwZI;H+uOop=x zKp+ZuOyaK9tR2r`kNa*T_U_h^Qo?VaI* zXHCnlw8^P>e5wG)Fp9pzj7F#(y4@oFm8y7w*{I1Po!RpQG)9%lK}N~kQlj}tzzO4} zC!zzh_X!UYlK7Bzkb9Q;BUmhFe5s44#7?;Uc71*_waM{|-oI5E#L(tJLHk?p+k`8| zJ%OlG#T+un&M+6vHsUw%2xD50X&&zwN)~Jq@7mIck1#*81?f)QNWw_iA+i&El0bxS z8gM#TgBvC7aYsla;)a4c_Oft-SAtafQSWDW04?()d-)7U$xHRnYQ8##*{^|fqhqGv zF{v6}YN+s~(*Y^Fr|4?=GM0(UaYWRI8I*gIMZeNDq|EUaUEn9;1TM7rgbq7l{UTbH z!5{OB`0axQ_Nk*}%#b18YxA~E$gcD|h^!o{#MqQ2{o(EuDJIQ#ooRIVBu&^PU@lzw zk{TEd4~>FL7^UC{{52Za9xp9h!l&?7K@UCrf`QJ^o_PI)}q3NKKTOFV<5 zX4#0*(+C?!%~Vc((odNew`V(Hc)wF!fA8wUOJ2ivq^W9+>d$SHk;wxF-X|GFe#-Qy zHx}d76+sM6cOK9=uLuKh@-Nhah#|WduF~Gd-~fW$SOJ3fv{@lZSHeCKmL}7r*r9_& zn&aS_#X?)$k(E$X2n4+!yS=hQ(>(FUo-7#gBqt$ZaEW9t4xc5Q%RLg_T0#_;bY#`y z&Oo*kO(be{Pj*AlphB}#OqLv(+KNWM$mVPi?z>Z+X6}8e(yp`QR|mpzc_9*&xPxKlIW^S11kP;~6pdwsxum zqrBJx(69hpYQ)p_8`O;cv5u=k?>PZ0VO?)Y$~<~Icx>TpyOiapl->_18QN77+w2w{S`&;GWCyGW1>pFXD?l&VKW_wRp7*S5MXv%p4i~4V7{n5lh|K zLoO`+Fkxs-JK-x}JNz`?2hIeGj`|pkOH%U1cbgbzvDGlm46QMX*7_@V@ennSb9}N; z8#(V2Hf0%u;g5Hf=~~n{KuB;6Y6LMLERc;CXZzAwMr171)qb zTyfg~Q=Et?8qNZ~R0fUy5uT;c_K5@lNCNQW|BBgKivY7&Tf)nwFlIHhUK8ylth-x=#*?^p85$D z?}6}>oyQoRYp4f2>;Uz}XL{t-4tGf~a+Tbq#ST0_hhyQ9d=N#)n*(S2m z^16(o^)jf(LEH{V{(7SMicIjKgocE%XSCimtH(aC=)5Yf&g1nY1SmUQ$FRd-W*K@uS?q?VHDQd4XJJuF>EMz z)ha)LVChH9hKa!^xWi3Z4OM=-k3T+9>0g-o)wte5`}vI0Iq?^7Wn6l7Nz2h7ero$V z<%zVuXt*6U%~PK0z_wb#1%E!b z2`dW0IRW+_$#`^xlp@gPebL+|t(rx|+id7a^KBkp{b$FTEFdkVoC^E;$-Dt<^4{#a z8p=Ce9&Yp&S`Sp$uFYF#wMbu$5IUOd8O^@Avl7noM`nY_lO>Q_>!a4wn|I3jUA zA=*Pv1qIV-<~vsf$Jlhj4WC@}P1Jv02{Y-@wk)eFhVOP#y1GXB*0M2wqASyJ;&4@{ z&wa1AU^&%;wO5h0-z^fM|7)&=qKFQjdl#QJua4_)IW2#sbM5v(gmD3JZ@^}n{4 z!AFGlJ*xE*)&#mVFa?^Sf`IKIQTyyny@IPi1gT3l1@W!|9r`rB$gQ-0kwq)~tj}F; z%T-78+oolcN!H-^VPC2H0x&Zf2g=&kiRMA*iscMY6dRUnuW@%JYsZY?TaRP*mON}E z{X~$`psRaKT_dHV^U`!3qi8Hvg=>;q&7lF87h+D&o;2Tt%iv4R7K+Bd`<$X@e?Y`C z9=$e4RK>+D1S$FCyy1s`lBsJ~LJMl&`|-{Qo^BcTZw;?OGG4pNer8820?Qx+x zJf!0tt`=6T9vutH=sLXqe{qL+stZ2n7tiPFX&)* z(aIUtBt0%Bw{TQ}zFdA<*~y#uPH76o91ZaovdM_XA#RXRbVl5i58cPKiQu+ww?}Z! zAU~|}DB$$&2^B)waH98Yaau%wasam&RK;3f?XGRU;4bckp_Oh98@-7!f5&7A*Q*sQ z)6kgwq=7YWYS664XE6Oz=##AT;gUCOLZO$Vr0mP`n*pq-Yy4*bNsp+1Sm|T5$ECj5 zMGJe`Kq!R4e)u0~XPYdyGQVJxI*%;`ld)&T$Ift;SyI7uNn8f)e_FG?3iY24L+L&! zU45ELdwx2v1zs$LjBa*A4F*IzC(0K5ney<~r*tM!FHgn9EAP<|$GZSk!p@$FU->{` z1gR?Xj5?tTiRi>6H>)69ob`Wt`7V~>N8jQ89XAo9r8OPS2{sC8s{8b32%&nHGC~v* zd&Hhjj17#*G`~%?`e**-$Hwx`VYC@MyFAkqTOng+vQvXsGBVri{F8MT#wbDD_sF$e zk!WSCZe85XFkRsK?G5X?ZsYP4&p&2eBhdhkcJrphp!_Ja0JW^S&d`L1>FKU0y0Vm1 zmwl%hAUVGadc&{3_9bPGRWm{dnH`=)hR_RCIiqD#TjLA6XmySL#RK#2c0I17{Ag&! z#z$Ictbi=sPmN#rl2?3Cn21}qNIUCLJ{o_2B@^*qb-oNcGwgG6&^XXry~*i5izc`*H`V4b+aE;S2nZ$=KdOwBW2W!RQ4!h zaF)=x2c*+bA6Tf8=WrZxy5{8$Xc$CVkhxgs&TW6g<(ywFu6FF&$P?&YL{7>S~OTI_jUj;wfQI`dKu?_6cix>xrY!8bFR`yRw zJ5iVQqLHRpexQh$M<`YId6(<8JG3V?=*c>!X!Xs8=@bY@$IJ+OHH?ez{IfSPb=^S$ z;pa2p!r7x?t8duQ0cu}Ow}9vy9bb(?q;q_d47;mCeFTGRQ-DyxqWK+}Bd@Uu3X8!D zvAV|ru}`69xY&!I-HWt8FPg#A&gdhu1_8H{VK?1s>`B&LNrS?c*6FWN4e?gdbdl-& zn+k;U^lspXyYm9mBfj{DQyy+{gQL7nO8)?_RJGQ~PQgSlzYn-5syh+Mnw!vmXgz_viBiJ*4XTepu@}>{kGQyEt9-U}Cslb3feohTc|v8pVSMbXRyxa+E@$qoLH$&g z1e~x_<=6LXgJ(6yw=iy1A(K`77|cTbC5UTGKlH3A171wbYB@emD9!xJ(O>c2J67e258Y~n*8*`4J(kPzz zl2om3^kcSD{b3a&)_gj}7TPbqHu7+PeNT}@&!A2Ug z&hTRm>2SwT9mjFJC2&WC{@pDrv6-uqhtEniS+0O7KR#aK;(D<2_m@#|GJGvJeUDHb z*K-!}YW3Nh-+0dnbGwl~<@Q!Y)Dm*^E?3J$z*l)63H~ocpzn{q@HM!zCTZum&}!uo!ktFYkwnZVH4FULm^uDdlNBpaO3gQj9GWMP(cHkWF}- zt+8xo_TOZ;MoPW@)n0zxUnk6J4wO?08C@}XC5@b9e2(nXtVt6A}fn#sU4%ZIXSr1#93(q$=P_^6$s6 zXl3Yx4qTjtTA-zUf0L3nnTpL#b1ay~g-Ng$z1`85hIfHKz8!ZUcG&%Emta8sC>UwS zSovt-9Sv{!t;v#iVO+Xp+zfMDp{F02>O~wPytgcoCixu>-hy^dW7`NL^wSQk=D7ws zQu_$3G~+$AJFz$A=(>wJ?))W=3~4DApIWN1GUO}6cf$!Rx#D`@0xv5}(aGsqOZM`XwFzB9T8C*6-kvq>ZUjHFCf#4)wMl9_sH3+n{Agse>0wUB)z&_9S4F}!r zM{7P4MaQK4c9xF=$}Nka4CwUe#39sVcCzVm=s*p;d>$1l0YI{Ce&JJMwsmJAfE~AY zmi)^uV+M1iwr_S64SDYGnh*+P$NSEjxfJ+Yzu9w};BN%jLn zYz>TbDNGIfNB%1BdIB!{u3KU@lYMizxv{m*W0>Q8?FM)Lyit>!zX2jdNAyGp?}E!^ zUAbR^3LRpa6|*WYB4&To2;?D4-8OTCt?lX$n`h|$Ge?5?XAjzH`@?NNe2te!YIfN! zFS)e}jhwaXVD9Dx@mEotdHXLM`%+G0!l9e49pgI%q{5}MHSAq%CAhtlT zEuAE*SCnDxnMxhrXYwCwL*+jyv4ND8^^W?3{wT^1XqCKwWI5lM9{US@HN03^hv9SY zl))0b$Ab)3Z#!tj&ACQNiHsWs$!g%R-S)-U8t z;O3_vzbB{OP|I0Es4Eq zyYjUkG(gqPQPrC4*Ng5G8gKcl$Ea4h;sT5;$=g0`cex-#BXxdlij zka&>H;h-DJ#06IHZ5*tLF7bZOPiHasAb{M>1u7tvzgezlz{y}z{H&I6*QM8+JaFZHzT3e zt@CXlCzaKhB1>z;a7-7eIf#e>mi`>8{}x`w&kV_xT-K|DG;1t0yb(b-Z~4w>3UZ_3 z)IC?adF|c^Gtpi)GYBYvh%>V41Z=Q>P(Al*FuH>2nxFH(bm>!LGRmlS!|=Bclr^bp zW5xTXT~3H#>{xd2+LK)mJo>1tTV%XW$z;yMUR*4pRT$-IB~DvuMQH9AG$@d8q96{T zvG_aj#|z2E`KPkezxR2c_AZDU(!P1Sxr>3=n{?W{3*)%z&6yiXjCR9Jf`$b!d<{tt z3#nYDUN^b0cG$<)#C#q1`l_qWa-^lj~tzg91EyKnP6(v?pwhr8AamX zNH&MLH!i2;wbm}T$89hZE&s&&_;oJjG}^e%&tZM>99RgNx2n25(%?aCx#4%-^q5WAQ|;Z zL$CJl{&%@eO%e}P_?fJ)mCiInZ-Ol@_yV1xVp8DjUGuw1x*Dsfb0r%@OIKA09+gh1 z`VJ%4*HvnFR^vv+3(R{mz2nvo%~SPAR(*2c!k7;YUl;5==%gcBze1e{~B35ib2%AOA1Gv;-mf@jV%0j&Q~j7LsH|qC#Az$`P5%=5Kn_+mzBK zbr(c-+LDgsZ*PvY`o*t|rn_^vs~fNb@UfOik<%1Egx2^CD$(O=V-}!Etru5m8-_vJ|J6 z6KLQGs-C-=#}~fpS{(GvXW7f42O=@8Y$mz-C}>q(&5xqb2TAZ6C_$pN&#OAX|!Z2baVl9B;r@;Pw&Aqn=Zx zYEn@#bdrB=ohyt04S`*109Tl9Uz{-jw6^c5#+^0iC{TXISMx6fP%B$+|GlOg>i}>R zwE0uM#(3k_@<&s@4NG#2XleB{GyAs1$KH?C>j6;y`a!3hAG=??Qz*1t`owzIKY>zB#+l26h7!)SeXIhH!4(ppTZa|+jsEE&6Ej^wNN?RCb&Yn6|$D7um(IrMTb z0Kv`X73&H&r@KX{k%1XgHeBxKVPb4!lqTtn@^)C=&ZFMpVDKS0HqFPP-iA*LYqqA- zp=!`^_KuYvXTq8i+wZH^#rwqwE8)t6Zfxl`r5c^B5Nep9T=?(s+*g?&UB{XsoT*iQ4xI<&5`mm`UrFfm4uN_wx`GTc$~@gj7ol$! z7-}II|Fb!rx8dJ_@10;>i6K1=H_mh*TPQ*zN)LEOHb4A4v1S$^QD*wclXWiiBVK^Q zJ)g0zzqVUT0RPPi#VQv@A!v>HY8N|IA5bdmQgVW^f%{FaL6Cf1s0!63yV@O*c{Xyj z`dk7SIMbwE1oNmUl8krG&Z+Rkxwr@DftCf*#7e2Z$%V#<-@qHO>y5BlVs8wM@L$<|NG%nxU3;bf`Y6{v8}Eg>*^h zz={PZ5N2EPY(Aw7t7ouTO%^YZVphU)rR+`9vy+X3GtoC$3~cKOD{7HNo^q?AKD@Rm z$4GG0W(ot(fOS}1)k;9den!mQf}?9LlMF%;Y|AUl*q?lI9on%8d1tef4HSc;Z=gHL zi@%xP?Csk*OHbp`GUG?d;TZEuy~X zvVq{{wUYm0mkyx96g>Lz!*5AB@BM-*W8LL8j^$BFcPu~b4vJvNRX}f_#H#4%Td;MN zn6rMC*cfBVOq7amu|ZAC!&|n`R6Oh+Y(xg#&f)$X(LGn;skiZa(_qu`U(_{)tg*Wb z^GsSC!?bG_uZ&6!xBuK6iKUkvSv^4wY!bzvQfdSdaGf{743=`O8|_tmpWs2_ltB~5 zay69}u(OCV227WQq3nby`0!=d9g0J*@;D6-Mi#@iq^ev#63m1rYe@{ZnlRZ8{E?rt z)EtY&hkdcO58E~Io|nV2p^z8J26on41Y}-anU_wjtjnr7>1F7w>NR#l)-Zl8q z_{SZwGymzD)>=Hu=zL)~JdP-%e?E}PSZ6)A3Zd+)-*)Ya(YYIkLI3o04-}mb>>w5% zN*z_WFq9YS@q^rk46^Nc7=*i>ej)*FE^Q`6w~8|t%jGV_{SqGkJwH(3_a>aGVV_rH zAz7+JE+ffh;GgV}ygYZ=o6U}`a=-qk*C@}6ZLLj?0g;_nn~V+0SJ@g?nuS$4B}z*4 z>rJvP#V_F9Ifp6HG5CPWV9lE-Z-n@g3c%wPjpssYR-NOec$BcG-xsE-c$m~031i*A zdg)-&D#}Bo-Dty}VGk@Z1XaYMH%Isc7keRWq$l(m8+{%mKoRI4<7;Y4Q{IU1mpwpxsT-sQEu z{$mX{-ZSP+pMZ$ZtC9b&QN{+vd!Or_v~;zGE3%#rpou)!59zI%j=8N81_N*k)x28G z(cmUNtxp9&T`+*@YO6~~Sy`2!TTkTZ&oN75x+YK6AyH(r=2NWXl|;Unwg|b$eUP8p zEED3o$--4CQP23H4p9x++_2e1cPc-*&iw8lkqXn7!ZpEIHEzov^`vn@=%9QXM_NNI zRNZ-m$|vO@FVm|&Vf483gOEHoN7Xapt}^94W%k{1>5jryw}h|Npr4_v{H&w?TsJYe z1m2(1z%|J!8$Bbp>I%Bed7RII@7-Qyp{?pdQGm>TIC?lO5FZMj&v=73fKO^($&ga$d8MXhkifb<>$7o|hc$v4B#6K!G2@f*W=Al%kNu3so$KbTmUI@slsbu0x zH_^|eqGz;WH(H>yoJIqqRnFXW+X_XBqubi~;vCRL0qeeB9RGEmDf91q`~r3f4Hh{+JPtn=GGotv$u{G- zvOodRVK)HR?b1Qc%xJGvN@a%iB$&vIO_& zTw;7k1I@$;th$B5Mh|WYrvxDFUuQ{FYg*URQGDKj?P}p;q&#{os|CuPN`D;Joh3J| zB-H2F7|2**(yYWp+mMUu`A9g_OlkgE4P$%;gNTLYDLWlTN{>#?Uh2qLP3>J{Yq8rq z<_CDTi-U&N8C>g~bT8yJyEehHUBk~3^N1RUh&5sYzJp#>$Coo+!bImy?3u;$5$ z>q1f8(DlTXB5ilUlNkfpsUzWa7(;;*^>*Q!xMM#NlV<5%9(ktvoX8QGf%RXQgDujyqvM^3#8{In9_BuYn=RQb~K=;}{>`4|( zEt%pbSjX2qyKdY~W7`@A)?518Zg$F~w*Ain$o(DL*zvEFU#H59huXg2$#8584jr?D z%@AXT6E~c634Hyv)sd1-86IOaj8(EyXrWORwC|Mvidt_0i#Vub5c_OyBsBUIJm+g9 zYHL#coP}ifU`Uk(H_ehLpOkZMqytK4sX#|&!ZR@?VLZZ4LROrKv2&aHre9o~B!;V? zjPqF^6j!p^_I=Ad`}Gj}C@+sm2HmMQeN zWTok)u~T^s(^@OWv0Xl|Oig>2%ClE=^RnOjRFV%O%X7s=|^a-3CYzDMs#jfkIViEvW*ComH1k? znUAvDZhdMacPMsIFm#xSG5mtN>diKa*se5b2SGHYQ%pD8_7O8oipTD&qONqZ0oqxA zN$BSS#WFSFN}74-@0Dg~IB5?W`}iWdxfP1prsr&*c9PMC&wI`ojo+1BtE?_JmUyg& z$5!nPJ;e4@5ZjMUQDP)fas_0ptah!K^H|CeImHK*$CgD} z7%qDc)Z@*yy4Y#{3yCPZWxmGMVrC1FPXauDzm6$gS7J19M#bzzx_i%PDh7(26dt0b z*);!DW_2SC6LK$37vQhqKNt*s0Jc@TEPJ5lh7ez;$Uw=eLQh!-Hw>yI-D337Re&(IeOUUwYynf1m3x%#YEA-a7 zvl-4y=VoOQBsh~hQctSHxX6$!LWh1ZOh0i@#gm}N6DdDfoD`Ze2ONc$)J`j#*nUue z`CkB-Kxe-|93N$d#khv?qMb9&$FYMkatvz0NC%`FAuVOtpCO_a-L~Z#lv&wwb|f0c zDLP0dFWk}g679e;8^2ilw~gQ0)j!n6|7Y>d;VC}4IMLdF`LxHO?r;xo$fpJU1MIfSnXy{%p z4hpBVsKBt`o7zT@ICqvWIq2?JfrDIRt(x<@VV3{K#@%tiOyi)Ro{)nlCy`I3^SYxx zZ!o;U7j*IqP1ts9%6P0tMV42LZdFGFEpS*CxS#8ttaR4!L1XHQ4Zb=zypW2&7Ualh zgq)pMU=yCeH$BaAdcCKmpl0<7UQuALgI)wb%f)5qgpQpr!}q|2W&6ih9p^mA_zsp4 z%1wpeT*OU=oi>mq7B>=x%QmzF<|?IEDGU{Kp*WV-Z|E&;Dhsrrw5bl4Hmv;2D7>A>su5(?|08k=8>1+|EGn81RL-AUww%$);&}xa;Rp-_l{zPoM46^XV6ir<9J#<= z0w3v&-fuF;Rgf#`)Vy#7>5i0!-+9kf>AFLE)4u&X)7PFjo*sVuwKT1lcek16l?|q` z2_q8a$h$|5s=PsJ)sD009A|ZA{a`3dtXnzgIPA02zPCpirp)Ng3Wos1eMq#=!j%agZ9LxCP}kz$_yrtbb5}{)q8z%R47#rdgPV#dk?;(X)ga#gX^K)uErduK>9-a`L5o-fA{t2 ziak4pTuz6sI-6cSayre)E4vPyuB2>3TW$alwq%#nsqM5S_ksFok=e|7!32M?<^FwY zhcQDg3EOwmZnT#TzLm6J(VSX&_O$yvfiTM}+j-_}*0GZA-8-M2I(8zLRd$@1DY`iE_8*FfIh)fqub&o4B-O(01WXo*)jB~1`O=N+mbbCqG76yc=4+5m8Zxg`ZU(s6>1QaEtrt>Mub6&Ezz zy860n(iI9u-VYBP+jlSNPXA>Elv$IHyLK0i7SJ~z-KCZD>DF8CkV9n}YbxzMLoh;q z+H~0HNEP@rR9*yw>BVy^>Grwh^x{cB2gFp`5^W;q z$i!xWN{4UevaT3wzIk= zYRQ8Ybm?>^vy=6XrWp=8vfgI|Ltn`I!VlLUxZtB-gbUZnM>?ZoP3j7Cj#E)|VDCI8na&Y3d5fw1bWm?Uel^S3I78|h9 zloOl?R60bqQMN>Pyh~o06k46y(f=R^%y~adpr}XF-5(j!T6yDIfMeFuCbB>{{b;gX zocTh9W{Vh9CQxwYLT4A(EDn(oP#uJpB*^q1A1FnRNuNmph&oJX&I7(eXVUC@ZWLDX zs^rU$zG4pib2%VTZ>-`XK;h82o3@n8X!c+vECM+hNJRn&w(elz)YljkNEpHFIdDa= zTh3_b%<_zQ-D0LgJ70*PAc!|pqTlc=$pQbBkGv=C*>}KbwC|Lh>C)nojl_6%W+Nok z<-v&N;6Z6qhogo*G_05oNkPc!j*n!pxJS z+*ctBos)n%)Gww6nWV*qF9|m#BOO~n>- zlipF=dr!T`z-)>;C1BxnVkD)3S(;(XQ-si1e8FZ@eQsh?0*Ep*Tdu^;N43L_qj2;a zKJet+IAvBrEo}x1w4k&Zj#Aae=c~Iu+(>NM$Jo_43`iy}M){Mp$R-3lu`%U#UUUKx zFnDsi;FVJcDf0cg%3stI@DqB*^Fck!xoZE8w093vM{<;MVt?|rBk2|WTmxxF9)v&c z=J5_VU~>}YStyGM#fId;c$G3SoOd+9ojA|7NGs{@{Ad3`+P!aoB`AlacI?@IApP`* z-)GjX-&6 zn#dtn&Zq;Ne>GjO5IpNLyQ|$VD_|Yh@h3>JktdNa)a4%6>>r5ND`Nz1tO8^Km<(C!D$ePa2SZOh4Tn`p=`?KbxpOM zR1kV|b}qfFXFt<&OgN&y`J=aLVZe3^Ml-YXT6R2_{`jlUq|ZF^f;llfhdB?qB45FV zQv%nGCgM)sJPH*mmHGj2K;TrLZ}d~E_q6@oPkt!fjq@R8f+9(>s9x2Zez`&*AcW9_$*pY-Fv zf9+rVE4>#b>kaC*Rd&KzvW(s9UM*p20qXOb=bFGeSlHo8HcTrn@5d^b{k* z@6Xlpz*08Q`b!;i>i6J-?K2O@LAJMdjqI2ZoZ{Fx9|!PS1@wK~1u5Nle!ax}6bUd4ibDW&u#R$sGshm+WFab-=NXt5@wTit9M&hS-&hei}>7zg1o-4@`8$ZQ) zvGH5lT4)yd?{wX90b4N2Iajp#7wxz>fw>q7JOrS6;{qK9BKX(+RctO8Q^pc(f z;izN76X1w{_5(Mjdv7^pfoMj-YG!V`mJe@FubeoO{+HkU6LVn>e6i!A=yU;dEs_!L=6@4WpctN-R3uCZobf6Y~aIl1r0LQq)6kPF)P} zINq7U+1r$ywMBwi8GR$*Fm+{uuGG{s0%HUbw(tO!D~8Wf-&FEQj;|#-mS2Mdrd@#-}p>W=B^J$8Y_?zq101L5d#w2R84> z;#kn1Sv_-tW;YR1f~`ukjhQyPjNc^GL5cR{;Kf;JZK5S9U1!g!meX!>@o;{UtQk26 z=G-GdSr8iJ;0zCw5`l@JMEh8Mr?(HvO;+4salYTJp!8q1pky}N=({){HhMc-`?3WX zDRIHFab4C2kV~Hnk&BQ(1k8m`IRX@}A3pLrx!XwTv~ES8ClKjbkdFko=(7{nmG4g2 zoxp$lSKgPdyZWkR0g0fbDIMTxIkm5S`x!a!sr1CNM>VQa2sOv%3k2@D?MBm$uA6VT z)^y_dZn%~}Ggw3*9{Aib(pz_s_K;ldX(N znUO#Q5F9jZW7J@+K-T)C5ru+O1i9L#z~SsWyf9&Gs0LGyHzfd*a|NMAqZ61-z}mrc zuzLCoc9ykUx6qdZl#OZ_*$_$Smx3TTYym%wKi^=hE-}=@$&9 zpJ7XXFUd!|^aCw6nzB)o2dc24)j&PQm(t(<>HE^D*H5Jvv`qbtKYzBIk8uddIltLr zv{VNvU`A712lUrQO@dkM!+B3n$>|sQ9Vz|Ff0fcN3v6kFTOiJX4c^SwJS^~3vR|A1 zWCSHHI1V5=TLdElP;3#LxJcRR6!NDCL|R(>qH=RIIB^`VIdgZ$UUrA{!8;D6A9>H6 ziP6fe-r->*AQ60~70g)eMqo1MB^|c1 z=wJgoL_bJK9&hM@gZ{ux5@7$HQ!rA6!-?Yjm+}W&BM880fl__&+->6l++k$<$(^#} zM700N!3Lc398?&2c*Dd&aG@`7=L#5IQZLho;AoH0#DpwCi1xyFULz(~t2&gh1-j@X z#{)Y>LN0>VqUbNY^m2O20@YG_^vUPaKmRwMRlVLeAl1RB<`rv6HY!puI;v?pJjt}2k~tYh zE7{qWU?lnn@46;^=skC7M03c35L0zC`dWg$T&kcH(^?Ur2ugt?sQ8FU?NEU(u8_TV4KxO`^aZCr8uW{gSYpHY_ww1?y&KUN|v!S zt0WH$)L~YH#!7(5;SQ%ed$i)^y(g6y}THZAC-GN%0q zFz1CwmbT4kbO}H8`f=Z;V>z4VWZN0pb5`HhV@JA4BU=LANB+Wl(|`W6 zf7x^rtQb{MCxMKS)xZASH|*G3kR%Z4xB=T%t>6B@?T6B*zx_&joOjgJmv3u<%$<4x z?&>q=%S}{Rl(U2Lnj0=07r_X)1Sf2vGKr&u&5$8)j!s6{B&i*%O>&@Urn_vvFyrEj z5mDq^v}|06u?e(<47g)*0FTV;g;Ih>c2JSm!3an7LyU^vb(@0EycPmnmFBcecy9X+ z1p+xQ!8T<_V6c%A&s#J~%F|gmKQ_FYyJfxXsXO6U-#D3$z4Cf`;rOfR%@e20QJ&Oi zbWXA9D+K2hsLn5(H~ik+yS1`!XWFT{=GAV3+}!N6%Jb=fUJkwb$^*6?+`lL7P=KW$ z^wH~7f6Y){d-)j)c<|=N1`N9xmD?B39q0m6=3{~oDOMPw5gjZB5Jp>_T(@B|NJj86 z1wGiX#!8@Y$L+3^GAUNg8H{al@Cq_~d=+VA2rlOp++Tg;M0)AvSJTnsucg-npO!rd zOpJ60R;TnSqZ233q&H5SG<(kJxNTSD*|lp&+O2@FW5>LL{funB%NBkdx?*2CxDVU< zJ54q{XZ7*HTOUbzu)5AhTZ;;SKm5M?(qI3XA61ExgI$qC^l06mcgg_oYM9i-2%g_X`{Dy zOSkW1DSh%ajh6iW*)Pg^gv7-ecZOWV!&HKijyiYxK8lfh1(rh~ATdJ0!TrcxS80mw z4mqYn7KE6ho0HQgAkE5=&1!@M&Zp#%)!jEEEcD>u+2pKQA$R)BxpeH+H*`1sQu^w* zo=DFfJ)X{KHI}iAIP|X7=!xLbVf)Y(`}d|R5AM_IxogrLH(#G_)F_V63~g8Y=nLxA zA{UZAgYwR^z^PQ|Apy?*C0_tR1SkF36crX1CifpUOVt5@x5MRfc1{+4yAYB zeOtO&D-^G~VsF}|8v?VfH4HxA2q=r19wZnsY9tt)SNj%qgR!9Pfv-K99((2meN1;D z{iW}}IW3$$V?2NSm8a4dpMKLuXN>l+E4pI4Q^DzwZf4Y+k}xxYhb=};gt-_sajQUr z54m|U{qO!oCeSTyU<<@0w1J!0nidxLn1a$V1trS$$OxD~6#)r)olfg+{(1dO!;5(_ z01kH$1rpy$CA|Tn+l$<`gkoScE z88+NDo1T0ArC0S))2A&U9ed@q^ybOacIUoUu&LWW4ptSt#7a4SisG(Y-;uum-aC}v zm=5mUtx+3+i7+Ao`yZcMV98496De|+H;m!4Dmc76aUTID7^3X%HEht&MYI1wPJ|GF zJ<) zeaxm4ZD~3E%z=r(%e3Rw3U1fwBey&A5_7tP^<%386EGtt0#?v88&Z8J61FbT1-b@el0!ljc=#Vf8|@6 zj>`|PYcXp@r{zK+^U;+M&!0}7I-}*%-_Zm4|6OiVcPhqjN8wnFUwN6+`*e()%~=Jb|Kvk=q`&^-?@c%AgQmOo z97sEM?@_)t?ND&qE~ime(V25VM{MCRqRfk+D|)7JDn0zfGZu^<)m=Q#h>EXL192d} zt6MvOB~Z%@sH+y4dOV@v^sJ__o;vcPrneT-l?V2vo%T}|5=FO10OhFinzGMV35LKz zcDm&m>@%Qae5&=E&Po@mtR2D`+HxwUtmiHT5jR6pCkAOby{YuOKk#A%5R)n#(j5GD}Vwm&=xA zV#f_@5eUJNJVrOh9Tg}Kon3Gws0d$eib}bxTtSCta8E1H{+s*1l)m!K$F)%5w9> ztM*i2p^qGb5kz5K8(7hTm2>I%Yj4GKzWnVMEzq)(k6#cLgYh85^yG1!)6Xan4uey4n>=9JLtTuN zzO>SU686~WsRgBteuS#_y+<$qF*;gpg!IJj9qCzCq3J?r^&F!kc>#Oy%nGaz-+4v) zkACo$bjx*DDj4lgyZ0Z|$Y`I9ka%9?Q*;q%VgZEf@#jW)gp_r22g~z~XO6y{{_y@U zYqiy*w!$vnr7@3SeYg-Om|aSE7Ky40tE3=ON5IH1mAH_e*OF!K?in%d)haVnmK7Fm z0XbY)J%-Ojhog{lxK?YFXoWN2IycxIv3gf45X z@4WZaI@#6)Wr;dRVJkqTZ4y{|Wz%h^6`Y>YRO0IjlB|H-rKwVGT0Du@`a`Mfo8!7N zQ_1!&m~NJ?+_x+JrT4reU4L*#dhm&3HWDK^VN6bCo0n6@dG|7_Fa_76CR!Jpzyi-P zQffg7lWh3Zo+)kkL)0~JZ%X&;V>O4FxTr6@;CNgCi7zp*GK#xTdzn)2&9D;c7k~KX zbkB_kjdfb@pKRZ?S06DwsL|10d%gq#XC+tR%0sF7mvAyT0|;Jb&(zTiT7JyZU#810 zG@%a%gCip#?$VSaBO0Co@yv^b3a`I;(i}1G&_H9Vh-tbvPo7Gzyv7gL`N9qQ9f62W zcvH1k$G$9Qj;^2k$-|aYXKenwh%&0=y(|aD%B5fs7!ufeskR}9I~=m%+@=R+tt2da-^O%x;W$)|53oqP~-^(rW~f zsETVOp0ZIsur1xMtfft0ffkfD!9j{9@(`DcErL?u$&YKw&H|F04&K$KKIC+8JUF$u zil_8`$ox*-(JCOZn8BQac|$H;w%8k}ot#R5kqZHKNpi;(6n^)QKCNZmFU!%OCFa$MI1l4wFs6jNJZc zssGVSOd}GYC;Bd&TdG56YRQa_uo6z!{#A~VkYTHG!H?kdj7;wp9e>T2c++Q=!S7&t z)<&yZHL1XI>pQMZS6`tiSoMt;UwI}&kRpJyv22`gdLu9qwiMGorL3h*V1X8tHo-xPCdP#p7jocP%<9qvJmp~IRB%i< zo7ySNZQqr4?mb|u>hQScFr}#I%s?sUO2Q{ofx+6H)?4=ve)9=^zwDKCUO&dPZFak! z5%7|wxHKg*>EuH9$#YBT)cLbHDEKG|rx?ze{PE}Y^K4_%58iWQy5;&S?Mnq$AJ~%) z=>=LJY27Yox|Cj%1Aav>j`G}vQ5HcNXTE3GPJ7O@ON&63^;H4;v!JB5%PCs)Ap+&i z0+r1fMI&f=wghY{f++?uwm?F2*mLbEVsqQ%G7B6y^1cn=Ws2-wciyZ%Uu}IMfFDqB z-lt{SOqm_jdszhUC1e$dU(zZ$UW)z9*B-aJ9(eRQYuRu|KxTAGvT0kDhsR)r7lpNv zIG%gvcNrYJ6@H4Cub?WOJ#|7;rN{Nm>xuN?58S8VKbLLfHe+x0GVW@Zk*vtuv1_+h zgPyVTV!_Ujsq{nl-je?8;TLr-tGyDlHmj}Z+h#w|n9>UhOt&xAKbFzeb*8GSfg9ff zll;cUZ}}E%;Z-Spis`#?UFW@{@RxHqu8*V7Zv9Z$>i`xWFm_Lz#}85N%aLe55P3K-$^A`d4`89oB5eNsrW z2M!!aS6+E#I(YD4+O01;y#D&@>FKARP7gi(V*27EN4g3H{lbsE*Iv^6fp^`h5!OE2 za9Srcf;w^ftOcuG`b-WZH2Z81{o#+%27ihXj6ArD$MqrW?%HkPduXzdq7k1UvK&mY zp@=!jnuaeqMKihq6zqnf4p6ovx(Z70K;N@--hvWez2Fl|EO7@1jTTV{$Ach5`r;#9 z5Za+W-+Jq<>6Tk=N&EKgOD9jBOs~E6S~`CGxaGXuN+4&m_T~1I;rz#KaV&WUX<2q$ zu^&^>i?;i9Ln68RzxI^y)9HP8zaw3v(HoJ8V{6l$m3{}2@P3ldtG#^G_v~r^*+9Np zap>T3`myi3UV%wJhazsKAjzdPZPRm7(hI`fZcw*_hq3#u`WWl~s^_sCr9m6n0(NXS zasyl2It%=f0@82j1={{38DI9EP7m$dZ5OFst#Yt98JvfWl%(Qs|IE9!((ZbVlJ?8- z`-f8_k52<}u@e-0)n0~mmuzJ@D5mN@`_Pl=i-SAw2qri+t8*~o0Uxx0Bky|W5V~TE zyLZX~lkeKKE8TnVz3H~wZc9gw97zv8_@Jimy8Gdu|Izoz8SY8f>19tGmv)k8`pE+i zqY89_Cn)(xXsOrnR=;=%uCa_h+GH|@r@hi)SCIH9D&b;nZvg#PiCuQQ*6+OBn`2`E z((L1-9JfnsWv1JKss)`qsBB5FwB5 z$cHfHVTwMJhy80@z|ev|F1Y=|kG&_I*AFGW|K8jD{IGM=10m0l;&Vk^uJ0i7EkupN z&T6`lkrS&Z|K7j&oPG0fyGB&hO`Fe(7Qy`ey7{Q5@S;BoR=@JAEhvRKHhdn}(uQwn zYoJ@;mlTxrG~jS;4?e8=HGQAVTqC#l5tQ${I!$m3*UOymg&}YhEu4W8xDyTe&lTSHowXQ5b!v}Lx&DoPMHL*PT}YnEzyQ! z$Bx;^>9NNiv#&|`2Uz(CtJ86Yd-m)}Kkx%TV2<&#pZ%=8e}&GU`-|@~=k|g7ZZ{`} zZUGMGfMU+H7l3kL>fy3XmNgw%8-1kVRh0cZw&9P+WN_r;f|3P|E*RCp!)0YBQ>9>} zFJpY>Yfq*J9(&%ZClGz`gCDdyzWn7cr#Ii!`$N(f#{*llaXjw2>#l<5Silp)TnI3CypIX2Qz=U+Q~UplmJhdpzOAY^T@b4KR~xfbRu zDv0nAbZ_=tnp2+L7}NbzD%oRRQVmw}4b1)(sC2FQ*OP;H}=k z-%>#OmkLa^9T+KnP0twI>57t_7Z0m-xmx*u`h}0$cj0DqG22`AHd>7R3YOaJ!UZ?}B^e!Z`vk<86k?y@gITzkcC zjav4ZlfgkB(z0wDA>aVDq0hJMkj-J^9QADERXyAn{iRn=_>_&>b3~sl`qs0@&GA0} z{PTLjS09L#lVYk6=eT$8-t_+Wzdt?kgub!&+;gSvKl`D3(hq#!9ag3vJVUbFoT!2_ z(|C-Oa*oprJ{vJbS^PvHXdc%*FBJjMHQ!m^tQ7rJu&75!9(>gI9H_#+UG8?l4t!*l zkkp3i;mb9#|h-$&H(VB>g1P(ufOs?%Tn z;GOBpz1!23`*-WHl;6yFpz={t9h{EpT=<`V{a@R0Mz;k$>7$>toiCK%qlFpuDm={{ z-2YEg`e(9O#YI}J&N$$4mAaunQSEPGxx;kCC zXHLOqZ@Nkk-oqiAcb1lDgXcL2<=_L(H+R0~q(!qg`YRqW>6wH#i3J9)ojjXfdi|t= z)a&WlSI?xceB~<^3<*;9eLe-LyYId`oj!d!J@d>nCdgg-`|iHE7mx@}I5-bN9+Z4K zFL1JF2}oTa>jfpW$CZ@*ZrGsfAN`ELTlbY9rE(5R-v55!Vj&6BreA*iMeE0nH{NK$ z=FvwVwb2j91n11NBc8i`==Jry_~a))X~%>>Ro;QiI!sZu`>FTeY@;O3 zqbv68=#7#HR13Pn`kR0McdWhkH3$Vfx^pfEC2qj(*0T3HDE)you&Shio5BKdDQ$|z zwnlaq7zU=#>)jI^BMt*cgEREIaDT}BcmKvuC~)XocADyY{mfGO`jI!&qc3Tha88}0 zaRl;_(8Jv~4*u$^6@aw(;EwAKqz}CF9ddkoa}W}LaX2_Iq=n<(GXrqeopYv*UXHff zGZxinOS%a}#bE=-mZ$uzN~VjZ)1yaTwqX1D$Bw3lAAY#!3>n28J$lrhtzWoLK#q$x z3pDB*aPDUWUb6{7y6|NlKe=)jPGib2)3?-pULSY`C)4Rm z;%CzxTEb3h({^TtO}X>6o}04iTf5O;(A31EnlJxJUDy1a#j|=p1!qy5jLZ{vxm@_C z^y&ZayY;&C8=pOr9)A8rdifMT9TA>%ye)AM1Pg)@(=$AS_?aKMFa7inzcbx_!1!N6V3U=5S!wTsp9GHog1StJ7^a-I&hL>@w#~9~nJR?@c$|WbikiIi3!R z|5|+-hh$a|ALhV*01g{MushCSZr$}=)&jq&9J9-g8yi@_a$BhA!J0m5^Y_a{Q(qs{ zh~sxZiiRX?L^#0qVPZJVmyt+AB_uGzo)@Y2-OC*5iK zUB6%G-NpXSo32Se^zOUTm!3Ik7j`9XAhLBrox)oH&zCYbDn4m)}UwYg&pg4Ipm;>XhE-mlNEr_lS1w z)N=y`h^sXvc#TGZyzshH@4avbA1~BWqm2rzn=Fr}3GBr)JCmM1ekMKeo#W{n-}r_t zhTtxpQ63JR;P!z#u1!DtWABrQMh5ZxL-}x|6r%^)`J$fdJp1DD^!T$c<@!?k#GgM# z&As#&-*;EK>*njs;ofld!F27Rg9iJE$b+lF7Kn0ymAwf{Oy&LFzx%SL>yBDmSQ^g~ zZbqvo6vXel@4oaGZo4Yo^8d5@yU>ivIv-+jMRpFY`2qF9y1cdG7prhWF=XWx7FKJ)2pv1=iW!Nz77!TW#> zdO7&yvDJHepC{EY>CpeHyKOT$~QU!MZFvpVB!ljo0$} zky`CGdh|%`jT&VebEDw#wnG_}bSN+TV>%OsQ~2N9v^)L7^KY7BrPt@ZQ^#V&>Zg{1 z;$2N2tvRf1Thp6cx~)v~jwvTSM&1wK*m(7t=4n_LGfoQWk5gJ{p8C%Z6$gg>P*Gba z_M#l1W!#_z4^&i)+wMUwaOFYw5wEaRd{ZP2YQK3HZUh0#tXa5l&vkRs)pI0ZC9E_y z<1&dr?t`r0*Iae)DQk02{)Io*K)Er;Hhze)Pua>9(t;r`LBJ zv3VziI|7kK8a7AM|7!a9J;&+n8l)&dhPhjs(aa)~DB0ww*HGb$q|rXSRa-s`sP*1@zr>6nJM zIBA1d=~t*@tLJ3-i`P0&ZkPoAFb(sf{6}b0DjRi2wKUs8nfX$*GpBUwhT0MK>Ulp! z=0$;=qM~pX=L)J{?{1=%@volhwV_mo^GxB3W=zg23n;?kTjd)L1F!63q}+wsExN~O zE^(B~^IA(wSNpJGkEwV0Sj5-u9}W)q*YLwZYn{rAaDbkYF4cV6TKL8MND0atE(F%x z1&eJ=pSY;#C0yYk9EHThIh$4=T(M9JX$mPc94Ikhf-j%&CklUoz=R!hP-oNYx_jsE zpL)p@(HRMpJ_$(^n!@iw(dUK`M)80Jx1rMyZWJ8CuSEh9p*fWW6&fDmREOCTkk;ZZ zvbXdu5iNvX8?EY1dyb}WZ9bH?Z{Kc1kqA|Aqnti*$5n}C&SR*>pnZ!@+mb zF%JWqfG_bV9C!FpN(N`h8>~0gq#kIecz^G$%hJ+CbL>bb;DQ8Wg?{3Fy3PA0Oo;SD zkLgR#t+5q@@Dx4)SIa~={M+!>o7OXQ8U-y{8HTit<4VqPQ>Xg;ozN>TR%EW!eB!EF zxa(nqlLM8nd2rHKr|J?oFtMYxtM6>X(gzlAH0Qh7+(=O=L#XuW zN`PVN0Zg4a$9f5bM_i7RFa#~akRh&r|5Fc|KsVhZoCqdQTPP;QnqT4cj2J3<^2OKE zSDt&_(3(Ip0TqI3pnWOvL0RLo{6 zWMwWveBN%s{j)DWW3-SDLNa<=p`@TQ46Eqaph_#BcfiWJo3EaeZog`Z=upqhc&W16 z*kYle6vj~HAq~+z|N1-WRV^E5I25e(ick-E9@Xyf*Rlz#6%M_5+yl2>o9@18nGdO| zVG!??cQZgCL6B$*y8iQDf74b)!W$HL{*K856JhigcWimb_-`~(K+0!5#(_$ScoY*j z3x2QC$sa2XVf|;&bD&Zq20f{D0x!k^l#=FVo~Ff!Vv3$p2o#3R5P~1QhoVqZzk8puDb^1)rF==I(=9!rHIM%(*gv_6P@yn%<3OrVh;-_=;h;;XL znzxxT$*#Cy*iJ-RIq6HtW$vctk?GZ~N7GZf!Ij<;XTmXGvth%AvPTq32<1RtAG`bd z^x->iPE#k17Y=WU8x1NzbtOU(C=tJA5S-7x@`mC0_5bpDQ%)#-@cnmrB z-+zBI&4Ed8C4XPNv3#G(&k75#!AJ@?BW7T=o{ezDK)%9ucQCO$wJvpEq@m&%Trj)3R zTwP$x!4a4G=V}9nSAOl&_vkw9L+S5V?@r^#k2gV|Bd3BR3=x)}`-%HanDOI1BovYa z9E!=hP9Dy}h4q_vroZ^gH+9iluL%iof~(rj!#?z(52ZWryi>iLl-fHwP4J)y5G==! zvr4Z=be~c7m3f(wBS)kYM~|c(-Fwr4*PlvXcutGrGb8eBp+wh`8R>?rW~Z^bPl$8< z2rmVw@uVT0JU2A`(`!4drH*2t7eh~o{297LF?{T<<>^B!ZDe_1&4GhI3}Z%AYMjn6+}{4suzy%6L| ziLC~G=fLVFls&vfG08lxQ$8NUQ8$B^1J(6u@KRc*?4lefm69s82|)xgLv8w_(lXYL zQu;qWeRrBLs#&Kv^rk<5amN5bfN)``3Ss*AO5OuQM57 z#?_Ib7cUggip3+kn&Y`QHm2v^@*{_!gFJ7VKQUdmd_|hPV5#oQYDwLDx)bLHzVh|)aGaecr4jId|+SNv~_#>gWvnz)T<#{^1wtH z!yWcWE$9EqhgK$5>5Xn3DgH!n$yXM_V`z0dX+_|P^oL)5-o8EFLH@naNci-MsdmD{ z-s7jzZ+`ws>p|C6O6-5*<@P;a@4mv#VUwox{_XV=l;uMi?LvIk92U%uWJaYMDqZ1iCOG zlTdc-dD*boGkc)3{ES1?!q}^@>n{+jRuc%dfngwrt*< z4j$N_&Z(zB9=Zp$LW4QIW&Pa#z3H{p-%f2BV!WfPOK0xQNq_JMf0#Hi;J^OY-z|CY z!uoE})tByCz95ZnA1j<(^{yTyypcj>#oLrIjW$Ha67JvuwEZs+-)TcyzkR(R*NC>(<+DwxdhbnAOw+W-iCb|g7n#8`>|)i0@$j4}WKP_mXOo-zuI{vN)fBk( zxUhsn2sGel)j)|z*SdWlUClj(oVnr)nEQMX@B>Q^rv6=dn?0hTlSk` zdPH++?dmZ#G*Si$KFVS_q%>iKZqLn>O!S!GM}w3DtG)U(oOV`pGn|M`kI96ch@lnU zQ1phtO4gAPOP?&mT`@6NNdGDTdKsc5zU6yXR@kZNhOb-LPmM&eIe)~WFr}ULS{!i&^ zYyBKP%HX)&h9tg;Ctr9YwT~IC4Y=(V&o`v$E9GBNP%J>O^UT2UL`-G*Vjm3d0D8J{}Z3!=m7qVtQ2Zs{*EO4=xT2Y?=%%+UnGN z7aV9A(bOgInSQ{g1SNl}fK*7TFGF9;=1tYL0V8c_58?afZa>`tID`)KN!QMsrc;|+ zTsSD4IDZ(%)1zU*BT{C6_0{LpQ`&DsgJj|P475M-(1Ypj5C4dI2E$Dmkq1MBw2Gfu zwMwT-KbMXi+^<9u-a?|R20m5>4?_%De8CgkoGi_1wVk_mrwMA|Pn|I<&78L|O`kqp zt@>9C@~}=U;4YsTI*gY79+Hf-HmoK`_wl~8OQ$rLa*>~MES)n^m*|Z!Ax01BwB}(> z(_4~)IxEG*kQDUDVerUhSwgQc=6;bZ>2Tc4^j*($7etL*>wBuw`**3SlT7!{ty4~rL=zC z+I04`dPK3fL$EdMC#Ep^c}+0tMW4U$Q&c(pzTVZt(x=FaKh?=fjU!Fam)vgg2X4zxm9n^y2f+ zrjy4xlTL#%mfmrcq6T>7&8Q90Mu#F1cqUjWq0*l3JFTIoE$J&?`KQEH7PN+!ELoC% z<(Ga@ikMU>y}A8BdUgF)6MAO^@4X*iR57U#dO^PpAt_Ua>`rLG1GO4;q!RUG-Sk^mE) z002M$Nkl!-C<_=l`QktPhyQ40WhfNBe)X$gP20C_PUobM zOwkLH9tv{ab%(zz_{p~b9>Sr`YzQS(g8$UXl|epy5Ehr!;6@#ibF_-~US2uYL4^Ki&4` z%a0v7(9^Z|?Kgko$3A%DW6P(E*!uO=n`f3iCF-gkTIEO_mWp39ds3Qcy&&BpdrVh4 z9IhP7N@b<@bdyg0Ho+l*ZUTtmDW#8=_Ay;`@sjrVY}$Q5EmJ1?FQglVv~bBXi?Fo9 z0d7`Oy{rlM7o?E-B;-wC3aU}(tzCW1(sW?oUc-6jtmd4wGv6L<2<3$6vzovrFX-^q zfbOPj2-0I(F}MG~!8C8)Je#EN(A?DxH{6&uzy4A>bV8>oNHC5XJ~WNhaXBOjx|>^C zrLeY}Vt`hJYG+GB8b79`7`9W-MnW@$pY;|6he9$I%VvIto|ng|uOZ=QLwKYE)*xYr zr7(Q9P(t%sZ*AF`HtarXiio^<4|gqJn1-C!Dm*DLhV0Ht(Z0R4+b;E^=Qq85MEaFq zf7E(0q+z)2iLZY>y|YoP5p@_V{6KNf(`M<8&I!g>zK{U&_S%O_(t zZV@m-OVj%_wzqxHL3w}yefv1Yqf{y4l10YW?`mgwR}d4;h` zEd}%8o3B;{moP$hhCzZL;Y>#Lr!6~nr}Yw$c7b3Gg~V_Y3JJkP4a zVYgzlCwX9?d&Bm9Y2h^XBRM0maoKWs%A}rA@ZI7V)+}Hth2fS$@?kw6x?^67Lh?ts z?P#G>ei}OYhM~MH;qO0v!dCh*>~~OS*1f)YciO8VtZ8i{)6f3fUrXbqY=C8Wkzvqn z>LK-OUI;SUwK(BV|NJk~NGV(l3ppNo=pn-w`9SYE@zc&7l-)F}KQ)W5jVd2yrGE^i zXQdc7yuIFf(RbZ_x4lo#mtjnuXSehN>1RIvVEUuK`AVQR+tLc}+uQf(Ak|$uyHLFz zW1@a4of^wSXjJAiqK8I~_r^ey^HiU4Z9`(2fAi2y>BA4+Ys=q#e`8~R`rm)? zC-qMLIQ`uVzJ-81Vvb4Q=c58$1$^BM4h~c#)8L?|PQ|-$;M;$_+RnEtYPVHd6i$nT zL3(wK3b$UUprCj7j<8b-t_0tjJaBBv0dQSnh;RAQ4D8u9@KP~t6xg!nE5d+*{!cAA|}Uq*!#lr;+;Eor0(6jwWwi-7IAw^{AWM=OPN9v#UlQ0 z3BFH!;u8hNibWjHS6eR@gZ|$V0+fxJHQ2BU?wM<`FFk!8bxn zu3O}9)jp%vQKRi?k=-Ic3W50H8rCCStKQFWPA*W7Q5ji&ysXW;eC1!{O&ME;vgqWD z^^{KPDv9;m_NE;NPFNW}`QtyO`LjvorrRx>x1`+~wmjW?!W2Cf`v3j>|I}o@-!Kb4 zHbei(pZtmO&odtQi&CdodQv>42SsnlH(}GmRj+#Q?zDTi4%FST-R75AUd=oc@0WLd z)BPV!|LP<64MZv*Lv9>9_3GNqY5&1P){vp(R#syH-xzPPKh4ypWWK@N#PBEI>;Wx- zw-2!Z@bBAx(}&cUy1VO^G^VXRH5aEt1V6#S^UFW|NlO{u5qZ&2QqQzY!`5ASzIFGN zb|N1cm0EXctUzY5Zn8gPO%}4I9xfI^3-UP}CNreDKG0~wrqzkT%UgIkq@NI4Y< zBP0M_QAr_bXp4u&0g4KxG*U~dM+tV+7+n#sI7_4OCiQONiS?GW_#roowFir9Jo-#= zYSoz*m24NyM^)C({M^r{dDkp6g~YeOYo+(OXV*^gl_9%^bhVb=v&p!cb93g*$+IbW zoAa34T~BH+67SbeYZp)9Eqs0B_3xz3n>Wip)bfX~@s6=q+`cm1wq(|TJn*38IPrrG z$;{22(~u>m`WqXkZ;x*fdf*Muj)Nx+mamO@ssTn`V0jkk$5>ImOI z`H_+f2Osh?)(=0u45LyVa9h!tcs*@OwY8f|F*?ffr9rQ$=d5%on=LR8CZ3sGbrfAtK5BC2c zK$%k^ukPKxWSJH{&pi+SsE*mWFKyU!EbY-S*lF=_h_3pmf!YM!87*``Fg8ixTCay= zfuvASIMyo_pHVj6L-KG_Mp8s0O?gQ{DSouz2&dOHQaAz^%fk_hvFtf7pABOv;G}5S zd*o!=s$SA3Zd;U=Uw^$R0?vw~^}k0e4-f0)hqGsT#Z#Y0g73A;L|HJkc$tSYcXyiO zoy1>Gec=7^zIu-7{YkN~uMCAqKJlQGCQO;07B9NS^2mR9a|hHzJ#^@>_DdZv_DoeE zh=*S6vJn;0BDpLJ0GY|lar=tv)I(}f57w{KK-r&F!y;BYp0_%4CaqYu*f4}r!j54) zDa6Mz^mXSIwWOs<^N@>JzTNFgkLRMxE$%`#8WG?4##5Qpu^{{T!WX_^cI1Iw%MF?ImMAAn-kA?+l%hE< zrS#@nUH_n=GU$OW6w}>z-=i?K!1o3k~^fmOD9kCq+Ts+KOx&cN&VVC z`<48_TE4}K@q!U_fx=QCu>ae?e!zxP;Rodh&XkdQpwtgrMQJM?)ezCQ{Lqba4ZYU| z&x`l1bdOf@t-G!y2QI9USCX1~-ZeSUI(>$%1Uq{0fVEhABy@Y#d*S40+sLU(g+SC| zhxE`rw^?OH;QFeu3QAw-fS*zNLXs8HCJd`DtHJ;q zrk0x_Cxj@+DU*ZBj}{d$oa8dv1=O(PX@i;Q@CD zD%>n&pqJDvm}V&z@Gu_~O3Y{x9Co=PFLNu<;-H;HCD3LHODheTk3!*rpSV0<@b!qM zMk%bLC(opf>e>CwkN>N*XxZ{oxk3x88Ch{iS?S5xd4yt>B~CP~dM|y2ys+Y&E3#VBehejQXsO z@ZI(tJDR9F%pu;j;@T2P@Et|QH{03S`B)iQcU&-&kX&S zQDfW7mLmcYfy}|9wsd#Or1a^J+?oEzm!DRxX4!I7ZBju{TQpx49dt&iTiIL%vny$QEm5LwGsLW3bFhT{-k#Oq%V z`;VX{rO$otb4J44xpUR>-)Mp+#{*sA&)q>c)G}|uNey;b!+9x9>-{KiDQ*^7&{GN= zUFt2ZditrfwfkV2Gj)Q5ogeuV_{d|{q&7Q!f!+{`!wj^;ODjXz2u{ib6`9crM#^Z{ zT6lpK>1hJ0Q9UerN(gITC!erYjM|4|`+pdA!{l^^`(n1OXcR7bpx{+ALhqYvJTyG2^8x8KhJrTa z#m~GOSEfZ@`&wGRPbX3oyc(l#W}I%XwY^@##<#(DTfqVUD(^Y)t=oGt-DM+!;w2gq zLNfEee&R0ci82%loh(eTCG_fLo}?iqWz=)@uucFuWbcV#)#~9=c!@!oiQY%$d<=ix zTsaO@n!Q(!hI-EL!GWFoj&`x(lOdhc+SIsz=a#g8*LIzTpmWV6c+Z^n<8K%WLIyt2 zb)$qn!*>H~u~lsH8W?Q3hj7A!!eE%?qv`!P!@vg|5`H_BU*sXhyriboWnied(!RU`(AFvF!(r~csxZ1FwI)uNf=)vt=%pkW=mvkg_J`eh*FAQPI&*xZwJ)rN;Y%qo z?y_R8F1{(g4ZgK7)i^C&(SYzh=LXp2x-A&RPOp)M848Id^u3z@JbLJ0II;FjC?7a8fn6^3sW9Jx-y|4Sdc!-hB;8#z5gy}5#} z(^5Q~Y`%Z@_H=mfPSNU%W_);C{8QN;ST;S-TNx-vJmTx4Wn|rPML19&zPlo9|L!uS zvf6xvuJcAH^s7h2P{@(}yS0K$LRb$I&e%RAG(;y|-GQHu-mT-hawOvbC8Bp;bs7a|=aVJF~1p*ziEZ)Vq zQF76r;OWI|@WM~yqm@(dO5XF zZ?_cEE>l3%2To0Ajt(CBaH)S|hCKU$e#%uhSC#{HrF3Qae+jwk_#Y`8uS_oZOpr*R zp3zd^{kyiBB4SCnRZw6}pbSg@_EYz!|MHhlSRPgl^`rVtowkZjWe?-RsX-Iuy+uz+ z*Bwasuxy%&&BjkIgJa40%^F@huKh|&uUTw(z!A8>6K=vynQ8b2fxvi>3u%+?fl`)KK0kyqNo*>=n12ZjfqUYVX=%oyWocMLPdcW{{=$Ht z9!RIoT%+ZUf+kJ*2rsx5PzejS#TA$$Og6Aa4>T2i!&mqxjvHfwFnC7X{(XDW*vVtl z-8bt_B=wFEy14;nL@~c)^NHu^*+_|~=);DI$*kv=37pkwFXlJ8gd2Mp3Q74gPiZAk z*pMMQHi%(O^&&53NQ;>`b8%XHVyG=d7&ms5ojd_g&)EE;HZM!~vq+;?y(JVfd$=Mk zHnbc4Dhi*eyK#8V^l({(0zLHVI3w`5=DH3`A??-Wgf@iBVih(z>tVw)D=MuXfiN>U zpQM<&IyySKbad0I45aRSA2~4aYkwazu4i?L98jxrh+2iiRPm@#CVb?lQVuyeObX<9 z+P!syD)ngJ%?_-D3G)?J_o#pN_~L>H%va{Q5b~BPLn_Q6`A}59^^#&0TI;A*TZuMN z!+4K;@HSmP;nydSJ_HG+!i}L-&z_o55;(MM&+FVZqU;m~(KJj)+i-qRDRh)37ZADI z5Z3r1FXk+)r$L^yc4aq-2d!FuO?a3b-{dhPl4NY!ykTESJMpxX5tspMWebHP+AID{ z$q-+ZSslR20tXZif5hn@6aE;=q;-DXjJC9XziyCH{=gp7A_wO08YeEWcj+lBzm$nn zBv1~#Cx&cM$Y5{Mu{zctwwGT?Q_E#^6%paa=HZgX;S%^Le4Ahr9E}s+Czk^9ZZVM# z+Jk3=H))mONAJ5WwT={xiUYoHq@PlOVq$|bd_JYaX;Bt-h%Mi@@(w(1JKh1s;d@qb zDLB5{XDuo6@SmM!>N1gMr&;%I8aC^rpe>mEHCOXhDsNU zn0%#-4!Y?bg{v6RBMX~QI*h#U-}t(YP7vc zHfb+f0w2N=_re>y(z9RvJ6rJworFR2Bg?0o4v(CoTw$w_Wm&^r+Dhc|Qi8PxT;Ooq z=XJ=3xj=s({-#)kcS$A$HAE~phO&2td47DC`heW^8Q{PlnDxHG>T6x^I`NLMLU~RXUvdt5gn8x=z%6^WQdNA1)EP=Tkk1%%sREeU?d1x}tYw=J1nDkaNL=??auPva*| z8NgG@!GnQc)5ZUGT`NzSO(`j?S$t^LTn@um-z!f?NFj|DUt6R^XizYe2ko;o7dL)< zM@d7x@8BtPL0|WVHR<{rZ?b!oj3357h6J^lnPEqIfl?H<$fTg1m4w6tALX>NQfB4} zdB~tJ#GPk@tmdj%G;w(Uo^)7^o>61ktWLCy8e_dA>I&a4y?r(`)AKXDfhe;hUj=n# z!FA{R%z=7O>3wGScby%>cSEU6J`BYeKb5ynPYmZV6n9P=X}h;Qmx((C6WdzS`Ay6lx#8lylXEAfF~YAcuNrdk6=-&{ffiWAJOZt83@ua^LQHSh zsRM`hr%^f#HcSW>Yy|x@?U$K4eVVQ@XDEjS57K?KoF$Oxd7aiOIPg^qi)w%1#|HOs z3E)Yc9csaALL4Eb1t45uFn+>BU4?Ot380_>8v{<^dHh&|vmZ-R zTk*JJLg!TC%Y?QpTRx>HFMCk-;VzVu0SJtDL*6I=6bUq))UiGjr%tzv7lY>D2Y8_O z>Z`9#>)%?J_Uf=ybR6f^(P(JVa`F>eU092!C5@0L_}t(}0dY%jd)rtW0_EE}drrMu z?FE}Uby}J^bCzw)g;oq{a{7+wy83yOTl8(5RQ>|cQ^|#Q#!y;wIwhOl66HXt_G-wI zZ;)>rLpVwY<6o~3>D}>wRvFtj&afSKUhUhy*^Yb~A!RgDjWy<*SV3t#rLiKul5j|k z#v16V%&IDX-TOXrpwe7>A2Y3Ib?F>vxc)a+QR%6y3_+l9Xk}vC0t8h)dP*&0$Eh`W zE*(33&^ElDQ{_{DWn`6F6>`^s<0e2nP@z=`9V@a$E9Xqo)sj zAYS|-9d3s8hV=M3b0MI>j~l_l(9QVH&NNm-D${1naG{If)k807q;4f`)_%v=@dm2ekP6`TzJyb)Q3>i++T|WrBFwheZd7xys?BOiFLI4)DcD9X* zBq|+gP@wd1BE8(jpG6v8B`H_~d>I~)(aRDJ=d~7kphbYyPiNccv}Cfb(-2SL8Rqbr zA$Davv^uXrYY8l#@TpU#BzmD75<65wySy`az)i2MBlR8&{#EmkwAEqw8J2C+73Q77 zIa+g8_FklXSU59&0wEn%-W!#ug3` zH*{!28YP}K4$~DM;%lwjfrGa}Ck=$Ly7Op)dY%m3DKn#~p+WoKw3=_;f`w_p!i7cy zY2jlS+|Yes%Ufyogi%I^FQkd`8OyZmNo7zu8uk8~^fdOe7)M7cN!hL2qEi0 zoc4{Hn~zPo3D#`kA)ekPd!7#M-C;v^BQ%f15H3oOkp=MDkS@c{!fnH_!vAhY-^Szb z7GID1!E@l<-_Q>}|9TFWz=4TlMy@g;u8Qw_IS#?@H z=R+e}UdC}Z%7fnln`srcXneB?1u8g|cFpD;=}ZGFu6*NYlWcI)b5e~~3KzmI8&EpP z3Fc|er}HP5qP;UWnzU$7A^ds^gUBnU@o7>*^-jS`Ry zyF^PLn^ruN@8jCXrn$4Gr!;CXE?=gGMY!gNZO7iA`Cg!*h zc$m$bH=6*&O&mQshW{$9d+#BYn`@=?ygY?~L&(Krd@207JK{x~!3qaT)s}H198G3> zx|^?>ZbCNR1;Tml+O6g(2TpGZ<-x7C@QAYUES@luL}ox?@6j=) z+pd`1oE|6{&OTOk2Iw(lIH$@x|Y=ijIK2 z>$(N$8>@AuT+u^vT7Ao?dS-jtb>v_gsi8y^0tR6MZd$cVmoC$Ro`>uT1nvNmjJBoB2#ZFYgpGh~m4f3i z(6h2#2z!qT#f7jOtKp^5b`^wn@+&oD)rR3EbEX)*q-BL8{H7fFLI?N&9Mk6H@5p+On@6ki)n%U!Z7T+nw(fd|}t&Il)CtQJ7QqKC6Z1pIO9Vw%8nyWh4 z*O=C=TW1ZCcpp>Sy|?Dv^oE6h!&;ysy8BfYdfX>Cx%AH_MJY|YyriO%Ckz(_-B*SKb)|G=`2JnxO7Cab@G&NY z5MT&}ehHlk*F7L*G|MUYH~x?%pL^v=t> z%a-$&EnCvoDdW?^4h^fQ0@FjHH%6YezyV~)mm)nl!o$!ay}3!lPo&+4`_t%-36>7J z=$!<8H|c}|gd=G7X``c|l;H66Qcmh6jgaIbKU-0za(d56JtZlvW(n=#!_{*_cnWv# zc^ynDr8Ir^+;scxUA89*x?%_j!OcV2q2YiO(w-d~ER1mAcs|v26@@XH;iD<0;)tEV z;q;(f{DFA>%%&hR8U*GS z|2Z1!jCeY}d8;0=sH*{V$ z3ekGPQcNhN{*&JGqs%B-dWD^1*nZ$RuuWN^@bn&MPU^5>UV2DpPWPs3Zu?*wr8zJZ zI?uItJdn1${zB^A`G$Q%;5A(8C9U81jM20b$x*6mgn2>s&p#^F! zJ}S3_T*tNdnnDU8MP&|w(XrrWRd(O|LW7mTG)^ZVp2}&WQxaqR*p_nHHh2(Zuf4s+ zR!0rBp*!D*Yt35|CMI1B0}=Zdvq9Yqvj}Q%$k#KyX_9$(>1zy$Gm@k-{jq+ zRhP3`xbMl8s>SIGQ&a(8d>5!Il}|q~ScPDfAH#`Kgi@$)?bZ2;*?U7N@ogHS;){hR ze8XVWTeW#B8eX&S_@wPeTof<+>I7*GQ91C`#}4OSU4cf*whcinYYy8edi)3wX4OJm1r?~CvoUhfd^3;K^~Pteqs)7A)~9RBj`4H_RU z0cmhpb_L%|338sIFGM(SdWa3(K~sFk4=mwY^a6uySW6sbBw}(0M>8?JdoHz4nQ4GQ zW0A@%MiIs=yu;s@Xy(11Ie9Dxsk;Xu2kOIjgYczXK++LTb?C6*A?=$&(gt{XSA!n!CIWAI8>QGc>N;b0S&ELYJGpYVZr3|_6ysswaY012*J(! zrlzIS+Ds_hiQ5tjC<$0 z$F9HO#x!~I6jM^%(R1kF0bTFS@?Mmy$|qt`rhQsn#|pBc5=2}-&gNK-jro9jTeIfO zF}(C75%4Cslo!0f9Fk&e8+sy*AE9_HoBqdVUl(o{kieoOr;i^k9_kbX@+eyH>~Z0t zmqAP0(^M3bi^}}h7;AZ=UX33njXf_{wD3kaRCeey!CVZH`uj}kEzL-4o_x#R2jwB( z&;R2$)ADQPr4Fr%>^ZS7^|y4E<;%;9o8yP>zyAT<%e3AWhS2lk%5-kyg@^Y3^q!b^ zQr}v~UzK4#<(=8YEc&wM{3pjXh{{Gu0Ow-~YUA588A{-sId&9(4mZ1JKXgN?< zN`v;*R0w0Q(#wDOf2ZF5VQKb~g;t48uuBMqpl6JpUQU=POS;nDw_Y%z_NjZ8+X1SJ zXGxiLyOe^5x zC>(~P+SOxQHe+mhbN2~@LoupF&? zNpmLp(3&e!DH#gPi`V1tH*7C60HkNVgOwfSsQ0z&$SEzh5KW3hdGgM9FH7c3RvBC| zft%0ogEuTrGbXgBQQCinA)f_zeOf0>j5j|&C??W_i*F?qRO`4&Y0LU=r3TGEH7xth zRb_&@V^DISu9ODlYq^l9iDO5sI@~;|i%Ogeb}Al~89O%Nh81frJh1IQUB6&rTKmn-CNNlmM{jBC!FgI9+@|DGN+6ctpv8*-aW}T(FgysP zA^`gA=2b}{1j?9UXSDEPXxe$OI}IH>32Y`jOmP)%^1X4zigf!OcW5P9k6qBmybTq>9{r!k7+obW@~5?-asgd=5L>R+2{v0gxAk~L<=u8 z0nXu|rjRsIj*{R?5Qgq3l4CX0_GA24iyzb|cX;Ar9)7w&_8z1+8Y(Fr7s8pv5GVtN z2%(L<$&Wo}H!q%%zP;r&i-$+x|FeHsrF*hIuDv_(K*vOl9U?yLNn3UvN|P7Z^{iG_ zVB=drIZd55O(!u-HQ|3u3Yd%aY@vy0V3~cZRx+~K0RvxIzQjT^0DR(e}vetbWGV`!97hEQ(s7k-%H8#yku_8nYhfc2k2 z%>nP@4r&tX#KD0*TQ{ZA6Duua!Vo@GOdggrQz7FS*~aB@2xJWvA%L_av{?^sHzq!< z=68&4u^tDNnOkDP_v+e>Y5RUAp#7u(-#n_6N^b(Vz=PZ{OeRDS!UcM>9Ec#&OzRkt zCJob7+G_3Zd1tL)D=YItxlj&<_C{!Uu0!Xnb*d$d&_b}$0;Xq#uwfnpfn$OY{Brl5 zJeo%K?nyIRPp47CmA5h`oFT|pUSFR!ZQ5jh$_XCIx^(_Do2LR#%p;kSkRo)UXMIY` z2N;HcNY4>C=X4M%rwTC7g<|u^!-xaMV3mfn_CS;~(eS%Ma>ax);GMOpw{*kYPV4cY z94G_5r59h{AR1lJ@@kuhoYmf-v1#O~z3IS?O(uNF1LNg(Sa~#ObjJ8LR>~iqcIeDJ zhC-RALm5Oqqz#3{Qt##yyVCseL(^pSQlQ)Bj{u?!g&|AmHru|2B0?bXt%w9uMs{tz zzOOBNk7;OkkMY3Xxp>OAd(A90THuxO37$y_syE)mmH|CQ6Up*x8r^O~ex!?UgVlGu zH~1dqpiwhw!i+R)%+P0o=DIz2IWVw!J$UK-7f;#s56ypk=h?Ac2pcM#3ry|Pkw83p z^r#7>2m^+P);bsI(eh}F9uLBUs*UWT@>A;-^5JDu(m$`+Yj|g9ljM#aJJK_+zmw)q zV|c`*4zf)TVnL30NR9zd@fb=40p*5-r1g|-!_TGVi>GPd%f56XjY!?yd(+U7W74R0 zABu?~oh)@(-VnOPojQIvo$o!CTBU&6S~N_jJ-((i>^>porGnGDW7zJ0{PXjcKfI#{ zfgtN! zx(T;9cqJjNo)P)PaX1|DG(k~3)Q(C$ zsl8QKJ&a?2k9uZB`K(m%LV5Zx<-$vOipO%~2MB-P_9Bd4g@_I|B!A=E>#avlFOH?} zD3wR9pP@9)BgY3G$7?%{)Xld>mGa`-W9UwJIbDKYusLd0a0X+G3~ z=W0MoYerzhbwa~%;cTC&UY!EZojaI%wZxk{Z%&=}PZJu;y!uX|JLf8mV$3NPib!uXGHlmSMc`HC{(HUV1X$w8&;yWvWQHVv!YvS@O8 zWzzvu94M51`}UFy_^j^mQG1z(^~>3-?9hyr}jxxtv3WL-v9~; zJe)pJc~_)&Gx3HAtHNEk2QLR)$_-xn>Xa26XqfkFtLPP(;8o?M5@HZmvH0mRUh*Itu~#ju^+AwA4?K@J}9u2AoVvn%ft zExLn=cWp|;-b?UQw)KqQP;UH94}8z@{)M3UHgJh3{reCzbAX%o97+H9+-sH}eBhn% z%{{z)nt%(u#kXlM7kA+=ho0r8GDIyfn-(z+fUD>W~H0e+D%N>Pl%) zzn+1_#n?ZUjtUoJ=3Kr;Z|UH{gC@kNpd8Xko%DJs!BFv(l@%QEAP+jnwx)+QghEB9 z)yXg(E!n3v)Uv7juvd8XAWZn7m`s3G>;cr8Jm3e>B-?=9XEIAa_UA|(r`$GoCEO^O zXjz+>lON$`X)RyeE|5(qBb@E=4X-GqSJ!MvfA{roTJI5|j&Mg=|LVWK-xShmod{sP zBq;)VN$kmCkCpS+=Z((m!n^^>M!h(nPa>S<_?kB|Ws|$B;tIIHV{=WUW3EYmHiSp7 zQHqJDQKz%hV;D1{DgEYWA2FJtpZARt`kSwOBmMnTFWHiE1idvDqWs1u7!ZI~(nr znlopPeXD$H_bzNpOXp3qZ#BM0Y)ttpe+a80$`(iPtuuVhx6fh;Hqk-{iU=DMjS=(I ze+E4V>ce+~{uNaSq;iEKB9w}U0AE=)2rkCxtx~E6yYNRrAxQM7vf{SU zHQ6QzRQ(YE2!{=tfPUx5X|_ zAJG9X2e=7?K8)bO#kf8r`C+fT@(FOkq5&l%+sq<#3ot zL!O|sp0wT>JuDg(_V)BX)^FRJo_ck?ozBiT_2P>!+BZ61_i{XS%e>a3&Qo z1RRQj%7>s@y?V7(K3a^&_iRs1y?X`clk&96<*6tWmmnrY80w;z0S*LmtEMDg+Ni|` za(8!kr#tVw(-c$B*@iSmr%q4Mj(U1DO6+N@MV$3i%#V;NTI<4#Xf(maTO{v_sC2gl z8I-|Qz&suB2)Ph~HuoVsh7-DVu^qG{l>X`~-%Nk-7k_WGg-}9h-FxGrbl#U7z6J6d!nQLB#Ap{Ing27rWF4E=z|{;>7HOa9Ch7H*q|B8H(w&E25X zz+sP+9C%%cC{aN_d>650UIDiZGPjhGLK;9`6 zOI+4l48G*H@ECU$=J~==_~+psMqk7@Y{hb3M~Hls6N~bjp`6BwfxIXZS}#ucH|*G- zo?g8nty#0i&ggsg*=KF;2SxFdE4h(a8;R8zuw#T+-Dr&g|6VJ6)Vz}3KRC&s#S#a5 zQ#z^>DCjLQQh-uop30TIaH?*=ck)rs>~Wb@1>tpT5Obiel&;Lnv1X8n7=_j}v4 zH25bNqCk*QIjMj=R7@&6!*&d3j6Q!Xjp*0>kOUmUhXYorpdrjnB$?1c85MIA!ilnJ z)P9m{rnjf(*X^{5&FZKJ9(W)<|NQg1w@9nHbQStU%|{?iIE6WSOEKgIZ7v+K5quIR z!izw2#RNr#I}@B1ub)s};BY!6vy1eGJLpyzv~#W*3m*<1=}G^z>gDv8U;3Kyhw?@b z3BmM#{mM_+b@4qq-QZ-8A2-viq0bgAjc%1N<31?_S19J~o7zG#iH?2*Z86Np(LThNs8kN#p`}qAd7_ZGngO9qnXkWgkqxVE+_BI6 zgz-H4@^|bWs;QGF>M&YexF}rk4Z5OH030TGXyFR0eg_oJcwg9FMuSpnHE8r_WdfOq z#m&EYO;N!M&&Oy`9_zR7OHaM>mL2WHH}v(df87)k!^8jXV>hJ+-I&ZfR7{I1wd5%Ha>SBbkxM<_MvJKb{Vj4hXEgkagSWp=@1@P>EEkT-eh8-C{ZfB*Ma zIklJk8->TNoA-$Wb!PwI8Ld$ztGO(fnyqK4p6Pltqa9URu1hmBHf(t3)1!6GfSuT^ zxr%5N9#^GgA5Q~mpM?;zo`{4R8z>RggdrqE8Z*1JI9t6Xd$g*g~%a%T2Wml7y6mwF+nbUrFEyAl&OQ1P~l zmm4pJ|9T3AQx>9gTLJ!EQ%)ZDA+##?WJ5 zh%Wtyj5q6fP0v=n>m!$*v<~^ba=^d7?=^Kj-6e5AmFVO94;=aRBS(+Sk&7rbuxiEcR-U=>Y6{82-vcpMmm>OZ8Uja}f zzD!yaDEJgK*5Z90m%9U3goXdz?7$ysa(+ZyUIvFNcp$_9Q=Ps#92z1FH}EJ0e^c%! zp77iTHcaG!KgWeXkHe1%7&!=J+vmis+r z;jWel3q^#;k4h^fMRJJ>{To~$EEH6=Fm{+qYgVZYB$9<4!YBOLky{=HT!ceyEo?cA z(HyiDNJ)c`C`zl)0#8=HNMd>EC-Mp0Wm*804mje3nBRgo0c}C$kMmq{_U|x}k0F5$ z!>c4j7bcvRR}s(97=|!f_~15N@&`P)JWn9qn2;><8JH&02W;4+$?=qaBZ&OU93zR@ zU@!AiNC7TfN~2y3I4v#76-L9!1U7&QaQF*x8(u(U3{0xNbiZh|8MQv>wZj=?+sE$`i_0Y6%~~S ze=QT?*e(nNSHS0C)u~-5P@OJr0{6us6k~{?(V;42R_92iHqy*4agweIG;ozLnIrI5 zU>re&18TxeNGoBHL1b1CTj0&h8N7BJg&2rJm7jd=h_kyX$ zpX12ma<{`!uLklf&+9_?B9-_KT;Qm@|Kd_bDX4QA3e-?!|Ki10{Y_KT&`$~8@AN2A z)ve3Jfp`m-2U{IW$pL-*Kl`n3z53r8v}A}CVHiI8`0%50U{{-fAw1x!VZIxGjxW*? zmgCOd4s#)0kyeE$ow;49t*{(%bV!go}SBm6n8JPh1{ zKf-I-q^}7proibu?$Yp4u2Di(dH?`G07*naR9ZRlDL6h}N7=J1cn~Q927lyNUB(E{ z)8z2s#?JBK50i(N_<6%6?Ztu%C19SB)63OkJM)1LEc>kT|Kq?@w=W+DO4eUKlyyw{ zxQDD;w|>o$!$%i4#Aef?hUP-2tOR-sUO`<5N~PZ+RyT=;Vw){894_+Iozy1<#kM_e9;8`yx4_m;!rj(Bmj6l4)r zjW6H>2XJ}1YIyvS=H21~U%(jNg0@JX!$f?z$5feBf+yXE1L|ci;@Ti=~Tj(gB+*i%1(d zgSM~X%nqe5ZL{xT|sI@n8v<>hLnIXkg@c zVt$EzW_IXr>Sy)r!+L&)baiw2I8awgmyef*Ngw)<*WXrd5IvgnZ~B zB%)G~Fe+kR5fG#Z;|SAknX*DzX7=N?lTES#7q|i@!o#22)v&~Qn7K-vd79h}Y~aYt zlfxJ$!G&E5jxg}#Ch+EA@8-_a*TP3y!g&|fG~{U`kH8W3rO@+kcmi*310UZ5;qR75 zt+>eV-O58;l=))t7t6aEKK#H3UvTH97A<+$yQQ;wqR)T@7Z~yj_#8(bR~=89pp&?W zH-!2LKX#aa#~ABcm(kRf(q&|3fvR=K&Yero z^>I#FWu;x-`mYEfS17eeVBmzYd<^cw;WocTeF6qq&J%eU5EafUXC;Zd3S8B}ncpiY za2LV`O#u#VQK9Xm>LQKNm8A#9;1Cj#_+9MCxf(X|3c&&j!a_8CcU+SF_kLz-<<4?} zQd(NMGf@(;o>H?^>S<`XrQEo17NBN1a%Gm9i^|H%N$!yoL37~9g$n`q0w?17@_b&u z-@o^N@ArM*=Umr0*SXFc=|2iuCSyWJEzj=H7vJ_PJs zb~UdXS@iV_#3!Zv#J|&FqR#kvRc`$<>|zxo5cgJ_DucoZI46o0&<}?+bgpLv_YYx-Z4>n%Yc64gf zlMlIBb1wNj-VauJA*dTFpB--Z;$DC?|I4z~pQklt`L08PHII5WH6yu(eJ?LK@D@s% z73ekpDE+8$$Evph5d8VQ95$z~H>4>@;!cXm-N*(&=}VCGk^7}z)|jm-qJme1D#Y!? z3wi914^gwI{M0x3B0~+SDrBBwp40lGB)RPyA1+>osA~56CKWB+yTmCGdJSE@4CxAg zlWo#4T{^XLmiCA^W`F+2N0r_~*V7+Q@_~vp62l{V*EV7_nH^4-6uqE9iMt}fz}Y&O*U_ElO24a zX7a$Q$)M{~V>svv_r~GN{OYT5OrT}J(DTLRkD>#*3$o5{J_ zk4^7L+Ym{rsx&|S++?3!mWWzCR_BO+XcFnegVURuTPG)fT^xq&S-fm^FTYa>R61Yg zC+qhoeMa%YefIXr#piF3?H|o=T2kXpFPp#1+!Yp_cycZ7mAKUxW&MG}?{Y29pLrUf zmy2(3nrr~5MUsmp?n}d?a=%Ey`SVcA?UmGmi0IQF+|Nm`5Zr#EbHpuVSF5d{la4RG z6|dwM;ESATuw&9$lGneoQf!Vs`ADaCsa)HL zvTvL}e4a+HJe6vC>I`+~67>Cpn$N3Kr$l5s-ff(SvUF&QDD)9PQ%^(9bUXusy8)Bz z5Y5R#A^=Mhzf)z{Ym~MlC$&6!0~TUcsi^MR#H;LB8K2iheLFqB-r%Mt`K$J({iXgU zzgm9lceW>!6}qeXR{iz~Vp)A>tz3WIg8@Ukznk3#T4>lgoR6ppKW9xgoPF1H!A<<* zzQL*DmJ4Uj7dTCt5o8SB$vit=qZ;}2=vioE;JuVu6 z-#yqroF7M8ncDQf=H2khkYw2|jDAV~rhde36nH(Lklb|^YJ?uMu@ebiB-N+FR0CyV zGjvUb{bIbBS84S%lPLkxtdg6oGEWaX7PPFa%CwA%!m-16_Q%Xbq~-*TQ`$URzf)~R zM6U+eol8*A*$+PYkDVxC5~|CLTMlbf!mz`e%=A0T&DF4#Ip9v^#@r8Z0#@ZlJt?>4f(=bX4J z-XwgC)i|>Z&Gs>qmvr;95l@t3Rp~bYM_NKPln#A9EMTz+J!rTI3$dF_^k+Alrvpty z1cj7H0a{#G1CP?*jm&Rw*>08lsj$kYL^2A_hDGs$g(e-t61@)v1w5l3ZSND4L`OsO z=;1MyJ_wcj@FX~KWWF{$|GoDek!7GE(@~)?rT*czpWY}BZ?-!MUzwRQ@6Y|ZW+q&+ z1z0?wz%R#YWaSNu2@$%zlhZQ2dijO#{Jz;L1^v$T{~-P06C}6BG2w^1^_>Dy7;L$? zr1WdDUUuyzF&Q@n`Q3g;aj8~6+11LMCTM7qiCSftXsx4wFyXql^scznYY6SAu3}p3 zhlclkhdmj|QBgH1!r7UL4GbmMv$bDt9}<^$Oe&OBCmGo^h|9bAT9U*!y||;dHq@W5 zq7)ube*<|yZfdqY54UUVema(>Sl?>68ePg0 zAr}mJcQ5;`MiRL9h)o&J$02X+h|$)~6X-tMscd0Xr(x)sWDG)cMpNI`v9Jkj=Hf*Jcrw!1zxubJ0GK5-=8(s}eQ zWFlUkrU|hSJdaiI_Gw8b%{=XJ3_r#r&g>6Kt8V~k4wX??i=pH&F^yS>BaLDX2jb)~b zPj=)4~^#SHLNzbE1*zBYX;k+nM2~-HyxqS+N=O%YgQ^| zYK~5lx*WN|{(8@iD~kz zq=S!{%)P=tKwi9&kSD7|^RuZuMjG;NJSx#}RP%^xD4*GvbE+MOg0{!aIM?1Z1P5hb zYml|OJ{f;lvh~OP$V;nXpKs3iO9~&2y6ASP2rERiusAJ69=!d4VNX@=T1#AcQ+hhg zruSIW%?r`sQOV;s#b+jRn~zx?jixJm);3o7@J>Db7xPx({J-LAh!52g6JZ!M0pkBo zq=9;Bo3SmM#JAS*Yv?#OB5In}SQL@6fV!o*aVdj4H(V<;rN06)__-klfcw+lJUA~U zHoigTL!f8hgz1~fTUg!yQk>_=TXzLYk_rxMn>?Dh-p}m!>GnEYcPNg1{vMlGIq-aX zaX6nyXxe!xFq|60bgyJ01kB3JZKxNJM>PMr6LOk-l*EmB8SBzC)$wspzFg8n>}{I| zFE07qKP^zL;2ouMQiO5sVrh(Xj^&{@<~)ASptgM92$tKKhmvHZ#e9x>noY_5L2O$U z@4X4_0^~N+5ouze$!@twXy&G>W{%=&VY#9~dCRG`2++yqQ1 z$+@bj$$x;VYvhogK{YvnuL{oyd&`F?(B?O%=>K_n!0b}eh=Fki4FaKd&lkk%(5)=) z28aguxx%A_n`Tx=eWEu0+*#rWy*Mi>V6vPDUGt%FLyNFcQq+vkHNbD6gZo!rrCAX+ zP*PWEvEgnzE?3dK`-Q&Qmzp%gCcL7?X#d>N_Rg^2W2OJPo>~#K`Xi&{Gjlmoe{p{G z#^c4un!K)JW^B|DP+%FTM5AHesOy$7Z*Yngh4;gXo<`_KHG+IXY%1|uNr^lB-(uJw z&o~q-s;*wP`jHw8e>qdxEI0WHt!B$I=eKh>97>3gf*Es)V?f%^?%Q5yjBfXwzcqJB z`=q}FDPG?dAjVCVt)Cv>VJ6KeZP|AeT(o_dUHdjn8P1#e(>Er&a{gA3qpSd##c$C| zA}I z@*Iu*(|p6Tl&zrJ`*K3%I2&aIq;!vohi#?I+7b z8m!biX?*o9ijh1cv)mv6CB6)K&pZe9VX%*pKwMW3|Cckq{f^(#enCAZV>K5~r1rMn zGv>1^kt>c96mW~#ntiRm+qe)Ws1Hxn_qh@LAxNwCmfnhpL@{?b(WH{cmXXWZ6R@Hl zulZ6!HL;UAtkgRl*nO4rQH9owqrcWGY0INNO%Mp0)3}e-!TNB0dLFJ^5O$cIVsSah z)FeVDCBh{j-G^RmoOj;$s_Xqb64xJ{`@?rE`n%Zu7r&hDggiOQ3l=3iDnehzocqa% zc`ZAqQ2nK#Ri$a|&&~9k0%(SmqqyPynkZiLhTssO$2%!5SAKa_{-O9_wtDU>vF3uf z)m=zZr+jspyx2rnP7Ru+ZgXL#4&&*y37M?%V`kkqlLJIM#0dPNhj<;v<=M zN@gx^GCU95xYKYdu;D>&+g!X}FJt%o+N&9wgc8%(hI$6t6xQB;Vs4dvw3&E=<1S58 zNCdic-_^GL^yX&lG3hN^Hh{fd*EN@Y`2%OZMsRo?6LmjD5i{|9{^%tU;3Th0u*$`o zc98WAmyf4h(Ca+6%_>phVvgd8IDs^%VG|drSKWE-Pnh~gi7A$N1~!^RRQQRFu940P zBOVh7H*|;*tQ`u$5^OuHI2S3arPL;|l&G0y_t7LnP1lOd!iM*`F;E3{sv3qzT2oxA zcxe5XCd=*x*x>>wFhRvZQI%wF!$?|7@zHK-X?huUMwN6gK9)-&`3;qJr5apT6dW!x zd7odp#_I#^-@hrvVhi&p#C+2X;R1PT92dp_d?E6^#=+Dd3FKzXaLCW1I>B!?#x>*j zs^dCN{`{^+f$B5zVeaZh-JL4$U%%A9c=L&^N?aurYAH+D7PI?$NdR5uBRF~TRN=$% z?Mp~z7_Xi@e5K{J-w~1BBn`)F8jX`%{f}DYbDW4*fh4SUHWnemigm_c2rANU%a6i>xcCC(Pn;Sc3%7x z+3xfbVyC#|2{_Yo>L@Ebs#de=s_Nr+ynklzNF6V4)u1&O5%X{A(0dCM#=2h?p2M?EmihT5*y2AO z!h*W8?Y*ga(SJAGg@`5WA3Pqv}N$ zKP&bogP-Y&PctoCsK@kYtQ$_X#wRYS#4@So%Ovw-ix>Tq5C#{N3mN*F{YvZ;dKdDh ztc8*(|48eI<_LXjZH5ndU&A3KThh!4^PIwhflt*FvcFS`t)c0u4Lj*Y76M8Rs}Y0`C_)T^wW#3$I*@g zS>e3;w5DmmYn9u6m6;k1t1o+1nFH_t?J90(6mqXcLJ@VD<3JYncE9qfyNN(D<0Hy} zo@Xlsnea6v+=9!(_3uP8G$)CHL+*`9f=N)5T=B0FcULdjGb=ywJ-z^cH+A5$Qk!zt zG(k1v{e$;LJx73tuTCSeh?$&COPB0v$62SwFFlzjtqt4*N4>3#9Up#Cgt75Ul!761t`@L1wPWorYOPf0&@;i0C52gV-%*~9^zrCqTS7sKzz}sDCoE-4Q znrI@UsVPNIDiOPiXb(nQN{DkRljwVX^;C4HFafxPgF$-a{XTE>88e)qoPU+S@y;uG zG#DEO1kEe0dE`7ii|EKTSH+0zw*D+Trw+-K@>%}WHZwG0xJHHR!*0~DT+bmBj6jb8 zwF%es9~W-fx;C7)&X$e0+NS6_Sl11YZO=e>0^4O!d7YIH^fhEO_^k(=KmgzMc&S_q z5efwE5O)fWyY6ZGZWahSwjxut*y($>dJ*mAnFyM|EX6Dx%c4l&!a@qY@oXC*Xsv>~yB>`1=T$K)|rG5@;O8 z_|eNk%`V9KhwpRG(Qr09hYEPPaAT$nhfP~H6&)}NA6RxaI~Z-ctmHf0KmD0y@G|aG zry~}D|Bx7b*3io_#-V-?R6ktXy+8@2|rs&iwKnlir9f*T3b$Q4s!36Lq@Ob&jg@$aTCb5sGoCqBF?FO@LGqiImxGV$z-AS&z zI5@8;@D|b#tF3H_D==WKZ<{QmdiNpAai=`GkU$-K>a0gQ%-6$S9pf5eIpMCGxK#n9 zEaf`C0FA;JFV@vIudD0xl8okltySUsKkrwA8%9hLSLzdc05{G&vH`Z1``*vs`!r_6 z_{U5ix#N>B?HMg7bTD*hOPC<-yD5>1^e8E`xT8xrLk!CGit8^-A?fXK)s>Iw=h}Aw zXX$mrFuhCv&P2&a%e~$1`fYRcmeA5&fr2yZ5HSzACGc1c%Oio`xg+LdM+qlcf2L{Z z6g-qlFYfrUcGJ+q7d@0zW|%*&fly~q{FZY6g5gY`H0$iY19(=)F7Z>_ ztFy$wnP@@0o)dGJyy@<7XYBGf3#DZp3WK>@rUBn;0IeuvToylR=wpuDmtClB z$+`C1;s^^*5vdbm@L;660!N;et^{YJ9#QHDc_r=(H^!#nYSs#9;E~q z1r>onk+n9{d?)LC^m?D=qbo=Bq3GZUIPZ%#tB@WUQGTsD#B^HU3}%=bGxO+z4G_W) zJCuy?lnLobO)m%1dWIGw93z*9orT#Wx`rPS$XUjr+(hs{2bt<7BQb;Cw(C=v-Pz5x zd3mXGN>fdK(0kR)m$$ZtFRlo~46)FKxx!oou8erRhB}zNnz^ z*!N_;wU6#hK%#)a)R{Jh*xaF=`l{GdyBHtI-K9#kMo%?m?fUM3x|hn(j62@T=#;v4~f zRmD-ZlEzwC{w9$OX*6_>8W3Z_aRvwZsym9;d7}2mwS8>f)oq>3R{mZzxcypUDB0@% z=0=U!Vno2?*K*+m;TNx7#e*rrCK-^HKY0corT`X@)>3t0vX9@nbZg3EAMM?41&L2? zPg@5lMykCiQu~+EmBYAYF1()CzuYsfu}f?0wGzs~6ekNk z=gg4CGfvgxAGUzTHbQQ(Qr>ozdf&;F+X05waa9E)+xcO_$vVRpOYG+&E=zJy?}qWQ zG0fv<^@=BaO6v!OfKp#qbTBKT*!@K}3@0IVY14>7^SdU4#0Ai44HatNhnrl7kvqZ+X7CbvofhUFu8Nvs6n z-@5~2<&YwuB8`zY7kSNF18)Nn4Ifg=rRhW~*5cOOY`nFP6QspdxFVB1U^67v<^4E; z%!yL+s!x(?t+#47fA3C5Ts z)GzLO0bQkHeXHIS%S3en*CABB;qNInG6)#Gd-`L7+(PffTI}@*x+cFovfNV5t_I!3 zhefaUrPRjEIa|u zs@Ux~0IHGS`t+`Z2avm=5(z|}LxmgtlH8#^#AQ+%5q-;O3Mo-9#a;ON=%24($t!kjf=?kE`sZ^iRw=Qo3| zpu}~H$0&ZHBSSZt=U0a;txI;-k?miV43RBZN^N2wfZk@_J3Gc7C}+L;#M+aFY>vZi z2igp+TT9^9^L`#Lnb85W#-sQqh-y->`4h>$*4ZKAzY?e4D57LFQeX-xPh&+ffJKWpIX} z`z<3CyZu{prk%$+KUJQ#5mNsyd@mN|VJU_cA+%hg>@>k~yMAKa-Sm6#nMuRn)CSQ$ zbqA`oN2&aq>9*Q|W9oQ6;hok9a}m|mJJuZ6t-I18Q(z)Ht~t_^r9B~ws*m(N!%M=H z(dIG?x09o^y5XwGsxCKt!d)-Ur~!1|Fx3($3PXQCT_3#TaLQAD=n8!J-?(=CBx#vV zkj@o_LGV*&1~}3mv1m#evf9PzhKGdpTsmTkKi5ihPYqkX;U*BVM91yXg1-@-*j!6+ zxn-Gl|GYhxSz7n*eQKQXVM&z?8D;+imEUUan)qPZX7}`88aTY`ddVH?NBit99f~4z zv%;u*;>5f_#dG&_GETX8ZkB2BzpSI1BIBxCxvD$7Wz{b(_7^U(V{#gXC5W|mvJ>?woJM}Q@kb&(6 zLr}Mz)H&ttre_mw>SpNfF?6TyHO0)KY_110{m6u_kCwVvae4A}z&^v(Y?$~D#n6-w zhCr{ILf>kPJc@Yc(+II!Z7wt)Cq7Jgw?3(R@N=@s*!;CLJ(IK^F<0B?8W)HecuZZw ziU}$m44uk1fdSsd8WbR=U-iYF2SfVbJjSQQKRt|^;C zW+yN{H`R?fG5t}nAn_iuf?tG-_!(N6wSX)jiZyFfN||hX#%Lgii@8{h$i4>Cr5E-iVa2{*zNksX2YLcmr=12; zq&+>&S|N2E<{41p$l!p^-_PD%w67Z9009VLPBE@M)-Z%H8jJXsuJF}R2j3MDP@0W0 z0-ytOGrev;E(nJqojT0VLmu)V)ErE*lWyv3m3KOZPBr7!-r@hDm}<^$B)gAtb7J%CAQ%NH^l zU>JX5zR@z>`{;~EqzLy&K=Qjn%^%6D?T;(V6t>LWZGlw1HhiwAK1NF5imn((UZ(wX z8w|RA>Exc|DzY<)gl1^1yv+Qow0vU?ouk(Y2LgxQY*>@xG4|>q16t0uT_%13zpC+) zoPln9-1C7aHpFN$w6yJMX0Sg++gSecCJIl>;@u&fCOiQy5u^nk>R<64meh`dTeV~n z%kf?VwL???JU?JYgzK+V7O`T@VZQF@VafK&hrZcUMk?78e&>kbAyh4g-1!y4{BY2p zXIZGMerp7Wwrc;Rq0PGmnzxPIa+tKdu=Cl!Is~Fs6#Fm3BQhZOIH| zS!I82mT%G8^@%G~t-yl!bqeZ8kIYPBn)S}l7RRUqU+SBvp4m&@Co zKZP(SE=VO>Jo2@*?4-MsrAHj(qRRhH8^>N(T=l3}!7yhhn=SeDvD6}$g;iaW_Sbgu zkGNfbKFg@&2$r@IrZVJKRT@Brk{4g2i(_CVH&aUgOC=wJt!PelE5&{V7ZPE zhQSH2GSriTgL|35^a9f?)YneV;-Fi}UWZ=rj9AvTo8r*Mqk0>szb&I$>k>6PBV&lJ z>d1h4++t44kG$4$$N-R+-SJ+h^%WSr;4jZH7=A`I@+8u?u32D)FG+pfH}GeRR4x98 z8{4~Fez9KVbB8k2ffk-pN3KfgBPDNc;=fkj^y>*H6?T?G*H5h~5NGeTpRvoz97yO4 zB(@plBk2+*8N46?oeL`2b+pP^fJ&-#$XuT>QCB{$4!lOyjY`KCti8uLy(PiQfr5by z&%$5I0>0CS;3Ln{pNu>7!6GZXduhytKI6iKytz`f)PV0XGQgeZsF~tUT?rwJ65*x2 zb48iK0@!+Q))YHg{}KLqD{m@#hE`f29nw9$nVju#YIoCZFpqDyasu0X9J+4gOsza` z`$U%f7LBs5w|I1WRz~>}O`(jdA$1=vj(tGSL4uZE{F8hgSGyT+Zf z;>9-y$L|A7&4_F~CBKi;IR|abLAFqDw`sYqR683y=y@oy%bY6$d5%nXR%5;$NgbsQ zQkcFDmIN17*B>>$LHvai`RSX@zFeN$1m1Ia zW0J`ETsrZn89i#}%AXM1`$}n5s57X{weI=ew7LB|n_8OQu)*Xb?fiQ?u1qJ+pqa{> z2UJ?%-9~{fBMK1DLzl9?jq_4o%W2GcUd_^(?l4Db09qyO<88hUn3^T<>AwZ5AL>ek zG<1XQV2c;na$JucLu944ogKOq^C&VHIX1g3nL*tvF6`_1^DyIgmde7OR$ix;I- zd0=xsrzoBxK*~Oj3xpUiA@V7I{o?)LAfJXlbO8C!53q>5rxP4?w|)PRLqR93q%Ks! z|9(oQUxpxZzt+ACFtS&@0$IsxDllI37IugAE?!3Z^N(azvPOIMtKumviV0P$v?iN*^ zx;-Iy0?Rm3Lihqg`#A$E*K;+NN?V?tal)hYl&9Y`!+eJp^D^GVyZlImHSJ26h}5g7 zBRUO&iIK0C95J0W!0A_S!2)mblP6&TOWqH@LDR;%$ zVrA?a?vdhOXi8h_Im5m{LHnsi+;-Y_qqPYJBQ5nc%~4y9bK*ZLtF9G01F@Shz^-mr z6xr=X8qGC+o)ldjCzkL^sa(d;=tXHZp5Gd#6}-_%;wEduNhJ!;xs(mww;84NrGn{} z^@2M1@}7kF^0Ky;mcvQSU<{LpsE98wk}t=zdZv9O#Or5<_6XJzdTXCax9vQ?eB9hE z?*Fu}dq}5SGPo}jVrJLCn@sOccCakLU8o%m5 zmiA>l!x=1yuLMuQ)l$I=pPnICKjM*TPvh8@Fi(j5jywBJhYnE_tc>XM1{Bnr(uiE% z9nTB)%N!s=9W7(E5^QEX2R@2oo&M%wmH_aM`D9)*nrlQE*mszuWbf4SUBX1)DDE)` zQ&si}ugLB#lEc|_m0)zFjWQ5L-X(h37a$PJZf;^wF`NH_-kR+ho3J^l?ojFtMg7W+ z?jOKvq9zxDBDS0&Z70~jT3J>R!HcelQk}Z}@WUGgaqG27~@;&wKip z2x9VNqHsMJzb>wK4}IS|-UBndvUOHL-fKIK3><5q|!Sufa-$HA= zFW$*ODLlk@sh32?<%nRI=^){*{hfIoWxcZ~1U*7%y4iaqbLb?*{pHhixM8jYbd(5) zC?^_Vaf-Y7Jm~xj_@SgYEdj8vE6QE5%lCClphAT%DR;#}iSa9oO9Y0up^kLrWP>?K zkHIkfYQHbnsP=o^%C^JGIh!j+T?s0KBinMw2p3OcRjca^UmQ$%(0w54I%A&%X+m4PQFlZLturpYFt#Tkhxc+N*X_4&*2^tCt< zmMdW!UnH5RgoBhRCj@TIjCObVJ~bTiQ+kN?M_Dw#{#0Zye7|OkE)|2_N-*#_Hg^I%3rH`FmIS8^);! z`+R_rlV@HVxtum_6sKbau%a;MbK*Ti^5Tmw$~{>cqhG1Bo36r;+EsSi{fRj>DIcPk zi$Q3g1zu$nN-5%ccU@@?8S5l#tNS;tcA>I!3K(Iu*#2XzPPPUVy17za3+ifdT{w&B z+p;1Tb>%z&(rHeg9~slH#BYxSePtQ^e$IK2<^%F;lws!A zou0ZjCA7PkW4~_HmTXfaSQsF);l1hBXOS8E0(|%(a@2~Nd(+nm^{Eq+A5!U%z+T=I z-kt)F?;jJbeDp~tR?*9W#McINg$((3d@T_Co`F3;dfiv)k}eG?lYviU{w3&%Q{oE( zy`LH25WFdQ;cHw8-}pZ99;-BI-2l7Go|dX-w{)(}kiqsmhmgS|M4m5^`PY|^kJ69p z5(BTOdqNWZgzNBUo`*JjWOI$jhNa2#Q5g^)`DYHk5o4KYChkZvLdOY)aCYaU#`L@jFY$8YQJ&et>AflM zP)-G`k?Wxo80sI{$=CT0jWE+~d|4W^i4vbQ=>_|E9^N6pr$@Prov$vW>7`c0%*5|n z*ZtbbPTiQlPG3vJm>A@AeaqN(Z60C&6#j%%=Wvoo%CWc*rjh~{$Bjmbul zNM6Ie2TaGUn}*<-58bI6_M^L;!TP!%sXjYRoRx$96|S}D8WQm$aUAQBHux-N6F&Xzu6~zZ)#SV8;I)q*EO!;XIufx}(TKYFK>95#^r-IzVot#a zuwI$gusxFfrZgjWd9E{|VD+y(3L4Bz7B%GL|8={@FUNUK<`&dA__P6?37B_CZT_VJ%;x=`MB}_)ptDjJJGvomEJE;*%6R#nj)fsBPc$RI835cwWn|C=ye%j*#caHbh)1__6sDNF%9QN2KMC4* z%&nHcD5%3yii2b+*CwasKiY3c!!&k!V`AY$0OZM6Y9+9a zwXzxt!UGT@Yu%{;Si*29vylXnH)PHbY_H*9m+Sv|0fJ1;lo^+=wEmIE@`w~{fHdL2 zQheCOO5HZrkazMOw!Y!toPrmQUW0~qRO zl^F24U08bLas6hpOinq~;IYP7mW#UdtWB{p+F}e0#LW ztQxw%hRYyrS-p+2B4m~~I+XDHx-Q8L;R!p74xyPB9%eq)Pv=neQ^`;d;mwV9>xjjb z*b%la2`k=2`qVIqK3k;c89kG<8JDZ3Y%|jZ+MB+~ZJW1z5Gbfm=fltV z|FrW{vN`YZd3bS+rfbbmclM%}5Y^@~q69Kl$pna|{sUde(vZ`U2L;|xcPcw)S>9L^ zQ&`n-hQ{*9LE0A-Ma(NLmEK!->k}oJ);+t@wBvJgg#My*`gS2BNQ9YTk}vz57_AfK zyKcD4Y+kDSLf+jQsoxtBHq0>(jSpYQ>;(Ef$A^%?L87jqHyiE zYq=d!%b|R-p6{x%k|B^D5<9)0L<(MLOP@MXvN@SFsJ9R{0i-Y-?PAs%U|P5s$oiDa zeZGaRLApkaNOJU=m@N>RHn9a+uMa>tSWFI5V z;#`0}u+0SIIO*G3)Mez6p%oxxfBkZi29YK)_^~=kaH+B-I!ODHab4U}<^x5m5!%!z zZ-5Ae#%gmJxw4qkh!Rst#V-2zXl~V_qpViy_ZR9npPRDNTu>R7ndqSw>e{S#kJt2X zXA~e-5?9Xn(%Go@-L%Kci{lfXtgB$R zE)M(bp2mpNIMMWZQR4WIv#Ah#{7*O{3oaw8ORC^d-&(6h*6Dn>*Y&sUuKzjxTpL<} z(f9^-gQh57(D^K1Lb!e}KSFvjAGGwtEkerpIQpLnLn$;q;*9&9;4|d51qvWg6Q;#GxW>`VD;5gV4-lvE5 zVq;^_e$e=ed{0qGxfc1_W5coaf`Gn?0h#JC7oXD(=tJ*9kbtPJS{2<;ftgGiT~h$R z`ltxTA@mF?>w@XjtA0>g4{$Xzw(*;-2#4^o?2$mA5!Tmp#L- zcp0C(xs(%?SifdL-F%(3dX+=`is`QjIhWzkqD=~$;}QTL3_9{zjrG5`u&R8I(( z0mPOk%(is#XGr1kGEdbJk1daRuCTYAeMg*ZJ~9!v|VyRWB#>J0{`nGEIhY9K6$jG zoy-m4`xigGx6IDRffFl3w{|ARYQKA9s2TE?5~MZw4-659ug3}4l!~KYuRR5sb@7~e zK#`11>CWYSmuhp+kN8f*A^o@IcikAu!i)Kl=J)g zzczIGkm*h#46~J`43%w5o&XD;Yk5U?3g~+)=!uw@fmh6EE3r6J=K8R;&-WH9h$Mx= zRJ&-C_2%0d5#<=o5Ur1i(z?%b44vFo5ca+U8Ya_ysaoX#OH?m_+EGN7&4(~LL4@k+4~MEuoB#Ep~3UZ z3Ig@khs!mJpl*YaVC7)Ws(+3bp;sW|vuNk!ZtNKsr=O2)t?l*(1P)~=MCt9tNVQ9k zXQQpArl;H3!^g2Adv8=a|9iE)1Pe;VMoLGJ>LIDTIh!NuB2wQ4kA`Zz-kybv&|7*0 z7S4#o#|ulUSy=4l!N)f(?`)}L3YsHN{cc0FKLhA5EZrFGz=WZvUDrlWr&kEgH79=Q zEN6#HS8(FQOJL7P7Qd-(*2ygQ5ccvFLx{k*-tKr}0&e@Ou*>VR)-31{^&mBLI~AEv z>-W=2DSh;uLTK_R7&vBOO)I5eC$!$S2T&jZqcp}lUh{Ywqah`bPQ^BBvViJLzsGawb_CxyVeQaiB7m&zdpaA3NixqX_`r*u#SBB!A8 z$Db@|)E#e133j2yF!Boc5h>Nm&xsWI21SU+yJ;7d0dlh7!^3unS(SlBemA>4F7TYp zbICqnfev~!HE9jCdA?oYURc?Lh(N!mpzQtRh-b6Z+UT*3VDc|i=T1^Nn^7`W`&Tj; zeW6*mOKFDhKz2coaTuXL!&CFi?s9Q60*IjyD{%Yyx=X`dS`s-{2q!lbG-Q{%YF9vc z{uXC_a_s%v-DtOk2$2eVa`MRD!IKC_FB=>6!#f7vHb;j%Q}~=gNMNw!J3;Ugd(kB> zkryws@VUc$pd5DDNr(l!H7z!yNBA-M@%LtXF)~Flus8yxI-T0QJN;{!p)2X>w}08k z;QxC8^vpC16IwkoWrdtTzzijfM|bM88}ZhL1yLYKjNF^0ha&FfUY-f$O-W7iC6JIq zDsQ}%OVGTr`pzta59cK3isq1Z{(W8-_X&xm3^;gs5R?s<;_zx<9V-pH(hvM09k0fu zM`j=VQX=UR*~`;yCCa1q# zkCi6qks@qCSc*(NXG7e3Nu_9%(rF5UNa;hHO-9nYWmasIP0FVp(V-@9_3@NJJydSi z=0%F0_fNJ&1(2KSx7P{2{_3#*hIgYiw8#ot z17T-3Is%$5)IFrMO&@w#i(LOR99cQMO(Y`Bh^$M!rlIP7PIA_FvM)DUrCNDfk6#pj zj=1>tZAyWKGW z=<{)4d&AO9koN=u`-p{5<^8YFAYOY_6+{hRt_6N(+LJv{Kd<0NXE$MWur>1L@i(Fpp$Q z;V9OlMC3L>+5`stp6S!k*tW{>`Ot?etBo;5rj>j59Fxn%2Dj}21gS}nS|%9 zx4XCs!VXfP^Qd!%4Z{YfB{PBe$@*6S8TwU9#KRWlQdoDz{F2ja_q=}K03XN%ch1Nu zi|Hal`SaW(ZGxySz)lLSUt!$DjjlchNG~KO*S!tK|Clm|?dE0GNw0mJQZ}Cau;ETl zYeb>el5uD*O3W-(nfqTU;{E1SyHqXOa%R1bXZrAy$;!094rP7(E5zIN40pLYJ-J8@iUbF5m&IIrezro2 z4?X<{41LX7Z%by^{vT1_;g;n3|9{HVPVTI*K&d?Csmzf%a6o2eORYSWdqCv`MRF#X zS&k5?th7X_EUlcBqNbrHLLLX2iijH&+=>%%eL0`&`dz<2;eMXye%|+M-UlDpxY`Y* zlq)3~ljd%K0EcPq+jPBN1*QO+9lm<$a2-8?D!-An0iEw82`kM`U5GSr;GUw-=8ILzkIK<3+B4a3zN&^F(yC-^?j`9;ZJ<@O8V&mX%o7hB#+E#pz3~#A2@I4K=F4!# zR;sTA85$ER$8URZAgZk3HDm!0^DOpPo0RFvA?)d{(jgLqcg{9e@cwgV^G42Y4C4V_ zoyjxgD~s^q>e)21tWQ-*Z_d|%Dl;jTSo_I@W4=v0D#X23_#z`vk_yE>O}1I#Q5=vO zVDUg&IGWRUW<)2o#i?gsj89#Ob)1W->f{-W;uI~p-3#%H!q`7+`w(g@&Q9v1&v&SY z{`NXjS$V6%Ys3*e{S4v$6l4@md$D&q@|TaU$;aN851b!;H#?=X^2|{10-v$yBdirP z-Pn9Kry&i{XnV#83!Q~>%F63(p^jczj__D1!4WDb`p7fg=Cz$1XiX*|w3zuaq3_g6 znY9F|nJfQl&*&A#bJ?_rCx3nb*LRa%=y_t7Iy?G)c^~ zLROZDY;&K4p9bCt45e55X-gX+4zUCEkV6y-glGz_{cFhryP;DGF|x_^u4=)2Pw& zRR-$8dZqwLl7_K~S+h?+cT|ABBlxLo2OA(&Q-=+TqXO2(Bc%@f^~D>h{RcX|Kfhae z^~@x4Dv6YOXPw=?3Ei%N?QsAuzgTQPYL}`sr9a`TFE-a+=h@k>Vbh@8MxQ}Zoj>r0 z))2FTX*-5p*ro!Xq5I!*QrUiVK@I=Q2S|d>%19U;f}U668P?jLIN54?Pl zQg^SfKwVc9a(Lt?`M2>v-zjg}(=h#`)^=-$jY4AnT0h(&j<`tsWH-ODbe8td&x^C~ zn=b$!;vdrR?Y1L74V7Vuz*pE(XYM8aqU7!(hOYwzcfejPlR4%Oo}8iQqYYkK*B1)JSfu>wYB7xC7)t!UNog%{DWc)6R+86tWw1RRh7Ivq$TA^cwj&(q{c;GYL`zPR6k%6 zGcAm>@!H2&uf0Q{7S>gV6e9MX2BT9eW)~3O8WC)yxX#cD`-9 zTcld)U%WaOrI+M{KcZYWW4%=upSVu58dSyMDBI0Pq};!hA79D-x%F(QDSGfj>ooe| zw}Y0eT?TD(@oXBL5}ax6L%5983M%!eC@l-qUY8VXi=~cQBUlY8uuMW!ibY*^&)dd2 z?U+HQ37iOM=M|@po?@qA`E5xV1!bCHIfP9JO7D{Q&;?qMdOmD=_FHlUt=47BYfI9d zW{KKl=O)M)Dl1R!$k9~95(^r`CAB9){`OW2{R@VSs$CnZGb_cFJSqG7G0?78&Th?J zX+G{<7NUWQ?l^YQQ^auoM)F%Y8g4LMt^!_air^bJ^~aRzS^k&8_^BS9FeByRw3b-6 z=6vKmw|~CVWdM-T?A5!l?@#KBG9v5o(bS4P038Zw$SAh}XgRGxpdjssI7tOe-+y=| z-!5k0y((?LQd6x?maW#R+F2$-l68ogDziGra9>p%_}{WXoP`nwy&AVMAxLwC?yn5Q z)oL?(Q2c7OoZyBDJBwKF)lfDIwpGovB^y4Ord+W!3^kXB++1ItJO=p(IAEgD8(Q`b zwTvT_hs^urWnqIx{Ze|_DSEA479U<#|3qk*U7ERl?v z$dX2V9g}mUaJZ^5a^Y8;y(RRVmZv|HLFsvtw(v<=>ssjlmMe|G@oQW@ z3=tiJ`hM(BGv)z`J58i%y-#sUsuuD*%i9jz)MDJO+dwJ%w`k)eVDJ~}kMMr^^orC2 zJ=b?q9bp@b0wHW`wI%;nF?C&>lJ{d5G^)eS9+RZ0vX$tQ{A&O)mOW{QF{&?~0K zIAyG0bTeiS74!Dzfl%c#wMomIESc5CUL9rN*DOR|Y*)HGI1;@-6FQZz0z+#p4$Imh zQ}y}gDzb~~Gf_sd?4vpd6rl^o`e83w8lgNP=x-R{1ON5zvB_Obu{t0IVCD?#wf zi4S+{zN(r{J2pJdWi-Jx&@wgDv_t$7Qlk??QWaw;ta(D5oriCUP^M;nB<&NR#(OO-;m!^?oJ)AI;3f=BqXA_w{_fi>X$z!Z?%n#K?16T>1$HI(Bv7 z9R*Hzl1bdS!PkBjEBOw_T-tI!`OiNqn)=otsq1?4l0-Uw{NZWKjcHYmQPivHFIt-4 zdv7~fX;4z-J=e1jUwFq;!w;C_i6oFs1@lKv!<`>Z3cS7a7UF%c6>n0GWwGG}2b6i! z#mMy_E+=Ys=XAR@x&zoYuXbm(OYrVyR2$pD;>a4cJ$AE+%*5Wn&=}8~V)n<|*UiMe zRL}&YRP|9^AsH-7cv8a(w1y%yVBfXR)SDmqNJz>%xzAI7ies0Jg^oU}H><48b3~Ov zLp8n$_XAEzc4=Tf5G=*#u?v=CfH8)1?VBbOEWEjiJ6fePd83{G`gz)YTWodSy@tk@ zlJPWQY}JoK#~b}68Ox5hhFP*hP3JgNyD$u;vSKbwk+>}2opRLkhRjuZ`xj?)G?aT8 z%pXnz%^hERuW9VB!#4MKvoOc9GnN{kxxykbDEEaUno}T7LnCq7Ap5hMkVqQp44udQ z1aHnIW_oQ2x;L(8V}5PeW;(Ws>ajcdRaI4!HL&RUk&C?}k7gZXB~CTBx>CQRQMJxM z*DB)&%!H&P>DZV+FgTobLNe!~f_+qh2}e)F4G2GddIFo!dr%hlSbaw7(=-&TF-OZG zeoT3j1vlSNRWg>R%I_TdWH$x5A|HBpPx#z?oPu7;XyuR9TojS`hZ-Nd%=_TM@TGsMWpVBpkoNvD|bt`IxgFmqlpHM(ysC8BQ9 z&d7n+Hq`1vQOhTq-6^w*`3JBV>2(ORyFT$i^AULsTUvIkN%jH2Lrt~IUO^TU3Fvk5 z)+TI2NpaohBz7QJBFGG;R-&t8w0E!`iT#&+S=meObp*o7^9j*8SnmrUQmOO@I4Eat zV6MJH=2D=J3Ne0BJxycchE)E*@3kRtqBo;w7}c+dzLoXTS{l{@BU0>_nPwvYkO1P+ zKN}@b?c`bFAd6R}N932=g*4^Sq6iuG3`{v79 z{qs(E?dJDG%NGGI0cTwfa!RYtP&x$grwS)GxT6=K+>j@?h_a9vZ&geOLqGRTmljYi zlkj`964*GYQR{8<`@EM0mw;+fYF#fC0d>e_9Hu@I;D}gkeEe!q1Id95)!Iz~ykgn` z07Ji+&S1*}!#uh*Rzm6ife$d0Hw2wvbLsJDU2!!_lo?U~{Ie(KRHE~OcOFui3z~B) z-XLIjK#S)nQM3ACeGNJ!uekWYp6Z4GKpL9*Lw{U*KLYH=b^yn9i|{vy=5~dl@3Vx% zHI-4(5zKVT3^DYe*$D4{u0-g^m7RlKDjQw*FRjPeB10DHKI4gxe^O2+?8@uMPL)}Y z#k+u!W|oWZ)IvUcIO|BD;YG~?JGyjVX;@Pa+DN~8G1)871$%;Fwpv(c zb{e{H2d974J4qd(#AtlyVaZ*$Vtn!fOGv8erz%zyLHw9=185K*bmRE~D;|1Q$#u;x21WV!k0Kf z=}`lh{OQz>uOuUZ+!Px(;K!=~6$b|lNN)G?&X zlpIEytiMQtLM(2D#jqcEX!X($1#nv*hewTdBxpyKXs2rGAxhAvw@AK_1FRpXb0eQN z_k!@10fG7Q+Wa5qf(E z;S$0=;lr=9KmT!ighNd0q*4=c^WTTPC+%8;{;*Zthu)TxR-6^44cJ(8QaB6JP~IqA z%CE~ML!X=U`aN=<45)vd+2vSZvO4l!FXkAVPzWn>2j1e2c-u5{sE&t40WmGNDnr*f zwFlv2@$Xcm$LCJXtEQ4Mpk4D5bz<=(31WfreB7avhz^K^Jbfvj3FZD`7i;JRrS=ye zJxt_@$K4dfG2w}h?(FL~x_(f+-loV0D{)He12>4^6{C*{p_pG`cfDyD_cZ6@!n|OI#^9=xR+PwlcZrX15hXxiGq(Kp5;AA_aeXo zL+hH5DOgk4#Wk~`PG1uCbYl4Gk~)4204w0L`xp}l8=E<8w;+2NXAx)A&%GsWgBHto z3S=L-B_+(c2J*(I6m8SLvw$^TBO~2{EedvR2 zi5rAvXC}5C1dcjiAk7`qOIiBajxXXn3|@(-s~zU4XMP7nHxP+4sVRcpOzmd(M~iBu zeX9kn!4!t@sM3Htr_d2q*(^;$wh2PNZ9kD3sVwrgfT_EFWlVHuqcshJ=`Mgb^=8wx zK)0$lVM*3bI=qT1qhNE?YUf+$RiKmQFNo)q=c5Y!esQ6!mT;HuO8N>R#@n*lZqNjs z{E_IDRbj#=m;~0(==?>H-aWR~>cVWM#2dD{3iuE1WAHT#jCeC4n7nYP)$(<`_@rY) z8-tjoz{wbVCiI1^jiay4w^qG}O7|$K@9k#wfKy~sqo*(iEOs{=L;95LzT`gQspw!> zKd6w`zLsP2y^=HDGzw=@g*m1Y-c)>}EsaTxo(#vgWFW6G6B24nlcV>Xqyob*Mfa?&D%r2jFzY{qDQ{!?>p%!ojB`B zJ}KoNUxJKacEKrc4*0u)k=Rg9cVe#IxlB$05NK9OsMC`-QI>G43=J(6rYf)(fNaWs zJ1vHGo~2)qHKjj~{c7o#uRXgP+?j#F&q^&_!m!smh^)GcIf5+ZX z_`XR^vn$tqOHmE=p&SgoR-!TG_CCWsMk4K~EnoKVW_&8N!A%ERZj9tA3BR|KEYF=h z6+%B8U}CnqO2y9yZ4}rjzOe+2oEs_pbzlF+uqnWd4rxH?hI4}=$5wpmj?KSG_dTBC z;2A<~r&5|6f$xgJg-LG69)Ogx7ZoDc{ASDPBHfR(Rd4hR#a6JSM#v66*3nH5>Ft!V z*SA`O8RX^8Mc$7cUHyZ$VMyfPncTp6V1Ac}FML$OZT@|qR)BMVk@4X~lT?rTSKV^l ztIOz-DQT+m>cWVxA$nx{A(J%;p(AmoK?BjA=RZfRvcNKYa!jDBw+Xz(cV%kb6`#w} ziqFHj_VK=Pz;y9|qPCo8$bP;6p(Nfj@6Y0v#YxSM;g!Q>#1;+mwcjj_ijp$Hk1XDO ztD3xG!h)P$%oxK#`kLu>8-5r#>p;@2=8NPvPIC?SN}HU;&2ct3V-vAiWU=a0^j6)d z59mz}^BTY?;E!)TU#!d;4Ft{Tht_R$KT~<^BV0f)gJY*-rxv_)^i|BzCzm?GyOU)Y z<}Ia{k=MA6v>o7Sd~>aIp;AH(_Pn~ti;NwG+kA6&RzwWXWFERA_Bb=qd?rN{u>YVW&h82vB(S5;cY?c-V;rCXa0xAW7jnD*;Jcp!xl_F7 zh@cDHy3oJr!yUvdDv5VW4v0mx&A;tQYTL>EJ|1tJ3VrTg+HcwL+U=wcMViLHH1I63 z7aNmfI^Ua+Y2%4;#fug-^JvDYoFX5=9(lVcYSaVyLs~KO-Z~CItkBMDlGaREBhbzU zbsIoRb12$6eLjK0>UW+ypiFDcU7(#LEpcHOy}!>gy0b>BKbGq=^{&|HKc#W9dGcS$ zclLR&7tTA%%MlbQGH*J!e`Nc17JM6COSJaBQKL6#-;f&MKn#a%>!*02`AN59f$_W1 zdVPJ0yF-uMdg$eKcni`J7Wv8R3UETMXb+;q1tG+{-b~3nYKCc2xH(?&C1&LrA~M;C zhzkoXsvM7Zm^6~Gm9HQ zl4zq6a(j0Z+MpZY+?~0)G~b`G%P~A!_l4&_MVnto&w|wWugnk@xy{Sge%je4(6UW; z&i>22GC}MTZja&svm;o-s-F9seV$C7^fdiTlJ87LI0ybjX(k&gLlJFoiW^HnCVmo8 z{;WGBmuXhnV&Cslg*%-{OGyXRSdN@2iEA;dY=Z~sq(e%^0YoJS!+^>k-W-G%uffh8nc7`N(y^5N1l7UWX&GX8k zhMcDbiBO@uhBANE#$_QsVsk0pwo{VO!`l>$*Ie8O|rBm7% ziQbcIcRV8^O<1cEal3Z*5m8X`Hl+|_<;rzMkp7_5Y{@dDRbmaYk5yJM9&!LO*1B^0 zcT!41T;WN&=9l8m0Eox5IY*&dD)G^N!e(Ah| zMoQR63J=q6hQ9-Gt_5WEZG5W*O8^j}{;It$JJNg&3O?(edELRxOI8cK=s zrWEtL&lgy-Q;Oxh^gmCtt~2xa3|V!(53};n7uWm?E-SCk-vy!!dvJ3@!|9K~5r1t< zbVvR-=eu&3ABk_AeABJFogI`tRiFV#0jl740pG~JCs%B~LTP2Sre{Rk{Ozzc_pCto zCcGANaH7ydu-WrqIaL9Udidilo+*p6tQLO_1{NSsGjFc-8I+}k1HH=b-#=l=I-sic zGTsAXN_$zus6Q&K82)AAenAjy)Bo%9mb-(&0HD-}QTg*#6n!kBT3{=EXc?v=X$~Dg z)J!oelL`w`dG;20V?<{5+aWX2p#a2nHyh?|FZr`EO_nR4WYiqJJWH|srS@*eOw7mF zo+yFHa;DkYYwNSwRVWpbC(?aLx!Tlv__I|56IVYVbVE!2+GW96ZiyaY z#k4SAbC*)S6PhYd-6Ss|m;xVE-gR3`fLYXALa{3vALSbtKPxBQa}p z)a*^7i+5ZIuS$ai1SE5PE%pB9w^2=ldfo)1nnQSezo3)%_^qSr^78VWJ%~}d-Tl2+ zI8h93Um-cH_IN}n1mORTpcN{aV)cnfPXnORx-s8t^Oe>If>Eab@JEat>_JGnw2Pe}PweM2_b1a*EP!ei8{1%XOOx)mC4h`%kCYVY+N!+s4W}@#fNFXO) z?k>2f71QG6WFdA~cw%oA!b?o(b@#5zsPVe?$yGJ*%%mND&`{&4rx(G=`6m0$Bi3gQ*4eL7|;ZJCGGO8L-L5U z@{5yJJ!NbtcPXe@-<&|7=%IU(Tp^$s+EN0j=9WphUmPjjm zC46LLxLOr{&+iE!`R?1G|sb-#`c&@@DBKhHJ4UU<)BWaFbg-f#^Oh&h&^ zWDg2ExMW9kBh1xpL#*qyQHn83*`O>H>s|K@y&@rRp zbJ@aB;l_n(#Lr-@1#^_REKH9R*yo=YyRz7bzIH(U-cqmB!m)Hr>(A_ge(X)~o5|pN z;h6gRhv;iPx2nwMw1YbL8ulVYB^+mOy$hsLwWv{les!b9`u)@nrG2=G(NQxv{qsAw z1)q*$HX~Y?cHQ4}z#5?_a28(wCRVU-qpW? zG$WNUt}-0bH@az5|AS{~W^;>s;i{$lkHN~lespI@3#7U)d*0vFao!|aAS_i!oMgOl zst$v*TJwy4*@_2EG})Ql0`ksuV0%WK=B)Sj%3j6F-ZF@_FDbe z2_1=HNB5swGyfIube*zTod)jP{&3k_t>mivmDb+5*@$$Cp4Pq(CU7;=2!s3wXf|@Y>XkkRY?UH~HAC;mrocD^IWpqja;$dwtq>!GQ3fX=2n< zH)$bQ>N8s3$l1=)LVF|$Yml@WbMvuMc7aMt#p_+1aM*3nDc+S`|`@t|-ZX0#cBOF+9TQ z9TJ?PpaMEftn3sA|AAJS#o)B zkAHU}K|89J@2IHoJ952rsn-sn-aY+wY3=xi#f5s6))RV}IcJqP$vH@62`DMd?{Z31 zSzP(Fc;upPMRiD0nELk1y>DvkwO9UqiR12!wbQ>34uihMn}%U$#~xj;`Wx8ZFT z00}R!RbIc;x=nvFdhj2?zflV_I$&%_b&T+~hcp}e@q}Z^VvV>*VD{TdpvJ2HpOl2jk}-0x*KwN zlrkuOa?K?5YvFW3yctv8!K<~40fGH^SA%mAE}E*0?RRHk+r zGB92JK6QUbC5TgT@W?<#7>ey`{x|AVon>gGVM884wc@MRnlvAs<%}&=3l)e7jif+D z4Oy^rkl*UaMR!KeQO~K0!C~CoOgs^01|1t3`TuKL@3C9D;$NfTBm4mKIQ^ap4-=`S zBd=B_rG#1+Vh4T+Prdh2(u?vCqs}jTeDJ0ma&ZDqoR?NW^>(Yk-v6*z$i;@)n@1c{ z*DuDL@uRlHTlFR{bUfl*A2LZW(Wgu=&Cl~`{-dBa69?$zhgnj96W<7>DFfIR!)>P~ zJC)QiDU#{T;I5Dd@1QY)US>7y%pa4omG6(EYE(vLZ7kNfZR`N3WMl{NWUtiY>PE&^ zs7a2c`j*8vN~=S>VAx#2c#BO<%8jvi#i#$%;4i5IOADSyEQyz#96}y7)E*Xv#0e70 z^>U5R7ac9gbZ>hdU(sz8taoK9$z;`iO5nA|J~R-y`sUui14AfAv^&=eeU3ze&!0&9 zoj7R8?GhLCM13RKf`G)7ExjAQrPU;52x5U!ov|NkSgp)iv>KE#aUu~8-Zwc+?6lPJkGerLC z>8t*j2e!=$u>$AmMM$j~%Gi=hNUJ(qkwe7qR-c#oC9+Ngdq4MmQd3zwRu$=fkmIh% z@ry6df|l$GR}Rr*(*tCRv#5RS7qn-XL9^PiM8`VDHmye2LOY!4(^faPDlBg#w;v~- zfFV`T-k*p6nq9xGp)nt-k=`fy?RZ$yD6b;o#6W`9+Abg;iIc2H0GE+ssc)a*FgUy1 zk?5d2&Bqd~i2S${feV`#vJbQ~l07JTLSnU`o*@#8_zAwGrPn!pLA6i3>7+pov^24d zQ+{-cqZ&j4FE@N?c-x?AI^o6@3!-*f;hCY5|?EB_`shRqkg^g^&a#oB8}Ny znJt>TgY~~%ZY6rH4vA3rYzCz zmbn5Sl}HJiJo_xY+mq@r4)Gyl+Jn6SR_M^ zoNtrkH&qdYPFAL-gAc7nBvPiP`~J7!B@Jh$pnHJ<#gsEwc}RF&upkgiAiZ#qcy5>F4-E$gg%7| z&nb%fixl4)vs$I;A#>MQI=q&^9%rX3{!E9THshyziS z*YT<)Jt}XIDdN%I#Tf5xo^GvaCZ~-<+KzfLYNt76Mmn5AyxHweSEX0ABPSKrW2JoC zTkx6Gl^3`N7qgGizSuhD-|>p!Kt%nW?i!0oJcSs3=yU&`P{ZAcUNHfcBjGkVL{%MA ztay~IpIFI!GZ5IJtW^LiC3u+bVj@wK2Is3tExr0Hi2YIKxdIKO7Pbc_`UdVgdd|wb zIbNWn22!9c20E^1$2)69)!WHd?Xg$jAGw*Wc6oXYq%`vB&w^DaCsU9nY&8kEG)uII z8gFxzpREY~CN~uV0q{rlSwf|09*B++Uq1=*KK{zISR7YlgL|-_!{UGs_7EWruuVQ}GuRvi^N%7>wGE%-!>mx2tJ#_9Sgcppk|=4F?w`GwWFI!1FTA|K+x~9eurLYn8mUnvfnlP^}4r zGm}{tYc9o}Lb{x*O!Dxi7fzQieN_SKR?k(Lc?1O(E#=A5i^F_Y42qqZ&u|N4X)Fz9 zJQ@3k)WatysxOZn{asn@-JI{B3>RN0U+s>bvluMhJLxXs*1hhfJhgFSvFE0VzQCGea5@YgEWg9uF^yAT^ z!bEB{Lm)13kq>L9>JLQDAC%YyFD8ez{?;>zbonH8d+3b+`=0|M>fy)Y7Evrg|FGsh z2eGT04!%Kl^k=yC!LwlGzUK;ZRrpT;_zz{_RlkdqM&xO|t4v*a3Z>ur=e=I(`#)2xZO&3TTmPbqa-wbXXD_Zo)bC~S5BZo6z zQunPj+ElY{rljTlDeZYS20*z13+<+44t*Hx+?u%YLw}0o=-uM0*;p^{_SF%z{Z$S| z#c8F%p`#fY%=#=_ZOM*vyzhA%Gcc#kwkC^$kPk5l)t0j=weEgd!v z2^k%J&j|nRnT-{_sme&nvmbaC8}wIYC7)$KY>x$JEfvMgh0JAzNNNLoca*BF+jOqw z{*BvCjbyB#P$>3}yQ3d))8bll)5R94DbfnrDno0vl1ltE^%fv}=?=7SKN)g zkz7-ADiI{@;Uc34y8FJ3T1d-YtZ%WUNA4oay0&h(Sm(4tt?lIDf9)QN-4FUHH<(FE zUEIeb)C7l(7}fNzZ4SIfL%=B$!h zFFQc9?6&Cpz{h`8GssWFv$5Z9*B{A`fxj*;aH(=d@j!M0Xhe;wH1~6@Nhg8k9L6@< zd=NG_&T=gPlh0-)g-zJh7lNBhvOHtT)Pr?Rcq4CX<}F}Ssl;@lsBbRW5rrVB^LL-f zuDpOQV7#rX9gmyt4W?D*S8ViR(nkEeV&tSc41oN5?NQjnEp>&Vu6&VSn}V7NfT$4f z7qqx{-%gWhYQvLyByh@)ko(e>YYuMA_;j)4%wyk?>_W{R@LqW%El_=Tk1)E0=9X3k~xIZ zkthR`-_ZO98>>w>{#DLQaW>mqIS#z+hBNg=r`qZYp9VCTCu4vWs64&KI+3hO$w4d@h;4~wlU-r{Jr`|x!7b!}`92ERv;~^I+sB=w0(~$7q5EOEAXX$2~ugp|sR%R-s zHWhZWzs92~P0uvcJW+e*UQVeVb6bLp1(#Dq$?iGEAXKF*bZM+)kUP*)&~Pc$9*X2W zSU9!sApx@Ogw_wlihV!GPc-zEz;ez;E)?#$^I65iVD`Rp2+4@#2t6Z{Nw$0e{6rFR z*+dl|oDsFV-O_X^r{U^WWK^h^9YVm2KVCA3U7P-PJ9kYuT-$K zV4Wgk$LZ_m4&&&z&B(h)Z;84u%s+PgyZyERkJza$@2JXfkKuF9OnfdQ|CrQWvA#Hx zy62i~Rkv$cR6e`+eqn#VZ$#DT--=TwCjOpN+9gBzW1uEd#EX8{19*=oJica1?rZz^ zZ0NT3&_qon;fR)6JgxYQ$Ksf(shV3D|L0f!dsfWk;k?{}z=o29q@B!Rw}p5jv5>;M z=~jU(^KGq|LyCcN&$JzpPL&grB&zuDRN6z9t~RRXsZTfYJ0a02YOf6txpZUzlAjL7 z4!f)AkmX;tAHYAMWr&>=3L`nq*EaG~GY_VqQyI(;ra(~_O7Y|=__FX;|6P6X$56^^ ze&xYK^N&=P!2iBZmDidZ{iBLuIA{-!npkgnX9c{bDc^B8JI&fQ%?)9|D?IA@;Nf-ib`cITlVusMaI(X z(V?N@{(Z2Z%GILGT;r0bkR&ILd(u(}C3tzxbRa4~-L8t_eO&y{{b@e&Y42tNh1=|Z zqG+LkTp3`epIb35y zU_~d}9cGw?S|tNt(8N5v=7m}m@#lYQ+*N4tw<)AzXEtu@w4MXs#0j99t*fYPDtk|F zm+L;&heUXa`92le$ooPpV!QXqG2H8jCDXfAR%Tw)sidV~y`k5GxWa2!f`ZK1^%_1Y z&Q;-tbv$pO}>_)R=}-OBJnPHo!-1 zCy>tapZLcOA(DQ7(pb=GgmpBp<1m2Tq7e(wh?c4idIlT*)89U&ToZ0TO`DjGcLWbA z!R8?*uaM_FkiH;hS6;QK$3$)ZA@Z_&zlO&BIni6VA_CK+qorA?<|cv4kEKYH`T=B( zsl@pkiW13_$qZ*K5}R5w?%1?v)sIjUH{^uLvx-p-a{ts&f1WJ!nU=b77-@d)C{HcW zkiGAR$$Hy;w+d)!cJTHbyroR>dPleUS~oVE;o19dr_539r1yA0HPN)-s6;fCPtApTMcD|9!Kk8qn`+gf-Xd(|sW#55?TO zQ@r1~OE*ooa8uIJ@z!HM+4!GwOeev<%RhN z1gpR0TM$Vd>Xd;6ee?KXZ1_qh&{`gLp~X#9l57wDj4;@5krzyKDa+biUlzY%SuuD=>; zcjBCYaWk-%pqN*@2s#1pSjskn;I86aqNwmf!~V4zVH2BL2iL98J`*=uw#wyvc5Mk($NCvK>Hc? z_mmtHbm>Aw(UZshhS>F#9-&F}7-SDbdrilj0*kMYAqdH_*YS@9eU*PSxgWlu#0!1JcoStOw6||O; zuW5zYxC?{?>SDu;^|E-@sIOWt;s`KIdAQJK-94hkYfUCV_}s99!EfS#BodwK@005daP(E8lF!s|>r zWvIDc!{Q{{d=d+5U8|BwZkfpgU#tI>`t-eJW#1mF0E{+wr zDbo)DP0rOk@lWOEVk~cOsQqi_!+u5K@Kn=lK8rtISvT2mt%)>-Ee&U$#l~l}DWT?C zAa4`bA}P;ft6b`SSz|#wxE;&+bJ<%&^C4Jj{@^Q%^+L6*)f6?A4a?x5U7ObV;sJ_iEDJ#GQPDiC z75*qvdN2DalUrrJL1Hx*%rH^IW-z)yMH9YhXud8zNwz+|ndLmTC$vlSSWK{7Zzo2J zK$~OwbYJf-SmBYxRgz5$(&3)CrIHeuU#PLS<3I`8&nPjB^S+!|%(5-4Bhiv1!#{@@ zpuKLo+~X+?B>hX(k?S)e8(hnw!DH8KyvdIS<@e^SvC`vce_MG$S#Pl`{Si$sPprS< zKb1a+lM$Qud@FJM77mDc0*O;6KCZjl$a8gEcdt0N>Zo4QrV0RH;TkN!zM!2ZbPlKh zfz+iBQ}u6dEWKuqwf8)kBx4z`XaXf?24ycHJj+g!dJH)%Eh~IZ0dhzzN|}cG-|hWE zy@YTxIVW_z%E(DW@cgzy=?IGNj=vBWAIL|vsnPmHG_ zV$_(WNo7Ls=vuj%Luz^4MPrAcfNOKZdU`WE&J{RSnY#uSRL(^dNhX*Tmk2h&dK-n` z7|mi1{qyubneKcdZRl?7kGF36oF~T3eAk@K*MR0FNM>N4pbi7Zl{sKr(lSVZ07Znyh-2W>~Ty@koM(OBQEqZTwl&y1(xyPOwp$3$HX)-7*G)eKT4Nb0A@tNRhk zeP2{oRsKY`I>oNn8p{2+zLDR`;RfX0C`M^b8et}@zrZ$JzIbF(N|VruFonzUEnk$(*D5d<`%m{nr2OtRbGiJu#EHkmeJoIo0{^AX$SBRh%7xuP1&BoBgxV}5Z-iEl6%aU@Uigp7f%=x)Aatd3RVxK z*Lgt=a-Qxkk67>I-@x<~Yp_4IxN6KoRtTWj0GsnaC3xF463&-SlEsdZl!V?Yb z0=~AsfgvhX3&A59iO%S8X)|O%>O?&GuyQ!IZmwSw2mf6RP1(CtdLy79`W~#|hauch zaOR8N;3{MEy4$@F&N}@wviYd{a>Pe&0ogmO>?P4EHX21sSqJ`&ezNewR|>-KaS1)5 z6@^dc@UgW%^Ygyd=1hjU@}m#QhVss^&0mj?(Wq)(AvJ3a{i7>e^Qp={#(*jb&TREK(Se<|#8z-?rG;Zg>B}{d%8YKEHq7zr0_c*ZX0L zkM}%y!Lj1;d|MGGPJ$zHrB86sTGk@>qI9NV_W<8@;a)kS!-7sQQ41)V59 zdJJBrBh>ijGy&EGii38~I&)ZluY&zC%n_S-i-FyC=pOmDw+*qT!m%)K@YA)D;~ zI+9m{bTe)>LROaTBR7QUY^a$n44h4R8F%Z9ENtBLIFRP4D^2vSZ^-U@KoEE$WF?$j zrA>KGM}#1pEhakDJ&c^wr)1{`SC*%G*aMxkZQBErPdsw!9USC+vK8L;ILg5@HXtNu z%RBBDKcovA{Ma{fFF+Sorx0Ai_~$u9r=3Hd+C?M0Pbe6+aM(1kqG!~@K7=z5JrS*- zpv^3-w^<`oK2LZhX@~2d`kiZuN-RSeAiP?$b-(y04~J7OkUBvK;TXJIc17ow_i(-o zG{z;~@v1Q{FXctDd{8<82sM4bb)bV_v8N5JC^+X|O4ROP=alv7?yQ~Ckk5e4Lvfsp zFPuVbFqS-#zZ#V5YY(RpWR9C2W6Ln%^>?tuesh1^AW4Ddp$kK?2LT4 z?pf-Q9au-yQ=?*d9;U~v*Qa1wzkgxErkvzvIJ=SOxVg`XT?|??*e#&6b?ucvlDJ*p zk-6WlS?8#?NZ3mX2`iY2X*RBn+Ya3u=bT^n zIZfC$x|OW#XWd5B!iGaS5`@cBOfWL}E^H50lW;fimW(-9i)Az?FOOyN!9;bEpESdi z3z0UY)r4)`GFaK2-2L$FyU5ahIq2+#FvEw+PJVwuH7q2AmhQwlYc5;VQ7A`8a7@a| z?s>+S$``_rCWe`PM^l7gxb2$B!I~Q6@PwD1k-Hnaw|;IaigVT*{Y1k<2Nmm zvWd(UTNp}w#&h;fCTmJp=sAFPmmLNId5lD+v zvN(HUB09X%w!~NA9?-nm%GFhghd#+g}aUvQ!85 zx0tPl(39<9?nKtwWksBk#>TB!s>U%JSH-TYYg{MrdaHP*KQ}$`$DyX1bcTHH6+bHw z3WIUCKE~qHAFn1Wa{Eehs;Tm7#>^BlJgTxmSd;*&FUcB3 z35X2YZ`~PF+^I}Z!)0zjS4OvLII8-p0=QmI-{UpC?ZeT&wK8#RxvLh_Hc9oyHM~9O zoLxx^plNu-Jv)v+P525yNhJxt{Q0~wb2aFGtVtu_eu${=+(<7a{l&9`L$t6{16m87){IY?`Su zShHHO2Lt|KoIi*i^$`fupeoGd;eQ=y*@~V6xHISRIFf?q7V}<64j_)2iM$J;5m0A+ zxOT9z9VGJUekVRn$W+YqI$A_^B|4|5Ur7-<;$&pkGqOcxZL=s7zG);(;gE`zvWzu< z0l6reuhAg^;p_)+qOJCP6;d&6bJ9Ep!rX*J=ct>cln`EnAyZ5*CA^QtfqC`7@s#>R z>c?C#A_Wk@g_zKVLZY%hV7$ZR!5aN}_8sP3U@e*=i2D!l*z literal 0 HcmV?d00001 diff --git a/week5/NetworkModule/NetworkModule/Model/SearchModel.swift b/week5/NetworkModule/NetworkModule/Model/SearchModel.swift new file mode 100644 index 0000000..ad96e68 --- /dev/null +++ b/week5/NetworkModule/NetworkModule/Model/SearchModel.swift @@ -0,0 +1,73 @@ +// +// SearchModel.swift +// NetworkModule +// +// Created by changgyo seo on 2022/08/30. +// + +struct Movie: Codable { + + let title: String + let link: String + let image: String + let subtitle: String + let pubDate: String + let director: String + let actor: String + let userRating: String + + enum Request: RequestEnum { + + case query(String) + case display(Int) + case start(Int) + case genre(String) + case country(String) + case yearFrom(Int) + case yearTo(Int) + + var keyText: String { + + switch self { + + case .query: + return "query" + case .display: + return "display" + case .start: + return "start" + case .genre: + return "genre" + case .country: + return "country" + case .yearFrom: + return "yearFrom" + case .yearTo: + return "yearTo" + } + } + + var valueText: String { + + switch self { + + case .query(let value): + return value + case .display(let value): + return String(value) + case .start(let value): + return String(value) + case .genre(let value): + return value + case .country(let value): + return value + case .yearFrom(let value): + return String(value) + case .yearTo(let value): + return String(value) + } + + } + } + +} diff --git a/week5/NetworkModule/NetworkModule/Network/NaverOpenAPI.swift b/week5/NetworkModule/NetworkModule/Network/NaverOpenAPI.swift new file mode 100644 index 0000000..29c7cdd --- /dev/null +++ b/week5/NetworkModule/NetworkModule/Network/NaverOpenAPI.swift @@ -0,0 +1,31 @@ +// +// APIConstants.swift +// NetworkModule +// +// Created by changgyo seo on 2022/08/30. +// + +import Foundation + +struct NaverOpenAPI { + + static let baseURL = "https://openapi.naver.com/v1" + static let searchURL = baseURL + "/search" + static let movieJsonURL = searchURL + "/movie.json" + static let ClientID = "SjK7VjRrkujvwETKupas" + static let ClientSecret = "u77U1Pv2Vt" + static let header = + [ + "X-Naver-Client-Id" : ClientID, + "X-Naver-Client-Secret" : ClientSecret + ] +} + +struct NaverOpneAPICommonResponse: Codable { + + let lastBuildDate: String + let total: Int + let start: Int + let display: Int + let items: [T] +} diff --git a/week5/NetworkModule/NetworkModule/Network/NetworkModule.swift b/week5/NetworkModule/NetworkModule/Network/NetworkModule.swift new file mode 100644 index 0000000..c71b9cb --- /dev/null +++ b/week5/NetworkModule/NetworkModule/Network/NetworkModule.swift @@ -0,0 +1,101 @@ +// +// NetworkModule.swift +// NetworkModule +// +// Created by changgyo seo on 2022/08/29. +// + +import Foundation + +struct NetworkModule { + + static let shared = NetworkModule() + + /// fetchData는 원하는 종류의 엔티티를 제네릭의 입력후 필요한 prameter를 입력하면 됩니다. + /// + /// - Parameters: + /// - method: HttpMethod라는 열거형이거, get, put, post, delete를 지원합니다. + /// - url: url은 string 형태로 파라미터를 제거한 형태의 url을 입력하면 됩니다. + /// - entity: 원하는 reposne의 모델 그 중 naver의 경우 items의 타입을 기입하면 해당되는 모델로 클로저의 파라미터를 통해 방출합니다. + /// - paramaters: 원하는 파라미터를 넣으면 됩니다. 이때. value는 반드시 string protocol을 따라야합니다. + /// - header: 토큰이나 필수 요소를 파라미터와 같은 형태로 넣으면 됩니다. + /// - completion: 제네릭의 넣은 모델을 바탕으로 결과값을 보내줍니다. + func fetchData(_ url: String, entity: EntityType.Type, httpMethod: HttpMethod, paramaters: [String: String]? = nil, header: [String: String]? = nil,completion: @escaping (NetworkResult?) -> Void) { + + var urlComponent = URLComponents(string: url) + var queryItems = [URLQueryItem]() + paramaters?.forEach { key, value in + queryItems.append(URLQueryItem(name: key, value: value)) + } + urlComponent?.queryItems = queryItems + + guard let url = urlComponent?.url else { return } + var urlRequest = URLRequest(url: url) + urlRequest.httpMethod = httpMethod.rawValue + header?.forEach { key, value in + + urlRequest.addValue(value, forHTTPHeaderField: key) + } + + URLSession.shared.dataTask(with: urlRequest, completionHandler: { data, urlResponse, error in + + guard error == nil, + let httpURLResponse = urlResponse as? HTTPURLResponse, + let data = data else { return } + + var networkResult: NetworkResult? + let decoder = JSONDecoder() + guard let reponseData = try? decoder.decode(EntityType.self, from: data) + else { return } + + switch httpURLResponse.statusCode { + + case (200 ..< 300): + networkResult = .success(reponseData) + case (400 ..< 500): + networkResult = .clientError + case (300 ..< 400): + networkResult = .serverError + default: + networkResult = .error + } + + completion(networkResult) + }).resume() + } + + func makeParameter(_ parameters: [Parameter]) -> [String: String] { + + var urlParameters = [String: String]() + parameters.forEach { parameter in + urlParameters.updateValue(parameter.valueText, forKey: parameter.keyText) + } + + return urlParameters + } +} + +extension NetworkModule { + + enum NetworkResult { + + case success(T) + case serverError + case clientError + case error + } +} + +protocol RequestEnum { + + var keyText: String { get } + var valueText: String { get } +} + +enum HttpMethod: String { + + case get = "GET" + case post = "POST" + case put = "PUT" + case delete = "DELETE" +} diff --git a/week5/NetworkModule/NetworkModule/NetworkModuleApp.swift b/week5/NetworkModule/NetworkModule/NetworkModuleApp.swift new file mode 100644 index 0000000..80d64ea --- /dev/null +++ b/week5/NetworkModule/NetworkModule/NetworkModuleApp.swift @@ -0,0 +1,20 @@ +// +// NetworkModuleApp.swift +// NetworkModule +// +// Created by changgyo seo on 2022/08/29. +// + +import SwiftUI + +@main +struct NetworkModuleApp: App { + + let movieFetcher = Finder() + var body: some Scene { + WindowGroup { + ContentView(viewModel: movieFetcher, movieTitle: "") + } + } + +} diff --git a/week5/NetworkModule/NetworkModule/Preview Content/Preview Assets.xcassets/Contents.json b/week5/NetworkModule/NetworkModule/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/week5/NetworkModule/NetworkModule/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/week5/NetworkModule/NetworkModule/View/ContentView.swift b/week5/NetworkModule/NetworkModule/View/ContentView.swift new file mode 100644 index 0000000..eb06d1f --- /dev/null +++ b/week5/NetworkModule/NetworkModule/View/ContentView.swift @@ -0,0 +1,37 @@ +// +// ContentView.swift +// NetworkModule +// +// Created by changgyo seo on 2022/08/29. +// + +import SwiftUI + +struct ContentView: View { + + @ObservedObject var viewModel: Finder + @State var movieTitle: String + + var body: some View { + + VStack { + Spacer() + Image(uiImage: viewModel.testResult) + .frame(width: 300, height: 500, alignment: .center) + Button { + + let parameters: [Movie.Request] = [.query(movieTitle)] + viewModel.fetchMovieList(parameterList: parameters) + } label: { + + Circle() + .foregroundColor(.blue) + .frame(width: 50, height: 50, alignment: .center) + } + TextField("", text: $movieTitle) + .border(.blue, width: 3) + .frame(width: 200, height: 70, alignment: .center) + Spacer() + } + } +} diff --git a/week5/NetworkModule/NetworkModule/ViewModel/Finder.swift b/week5/NetworkModule/NetworkModule/ViewModel/Finder.swift new file mode 100644 index 0000000..1baa0b2 --- /dev/null +++ b/week5/NetworkModule/NetworkModule/ViewModel/Finder.swift @@ -0,0 +1,47 @@ +// +// MovieFinder.swift +// NetworkModule +// +// Created by changgyo seo on 2022/08/30. +// + +import UIKit + +class Finder: ObservableObject { + + @Published var movieList: [Movie]? + @Published var testResult: UIImage = UIImage(named: "tempImage")! + + func fetchMovieList(parameterList: [Movie.Request]) { + + NetworkModule.shared.fetchData(NaverOpenAPI.movieJsonURL, + entity: NaverOpneAPICommonResponse.self, + httpMethod: .get, + paramaters: NetworkModule.shared.makeParameter(parameterList), + header: NaverOpenAPI.header) { [weak self] response in + + guard let self = self, + let response = response else { return } + + switch response { + + case .success(let data): + DispatchQueue.main.async { [weak self] in + print(data) + guard let self = self else { return } + self.movieList = data.items + guard let data = try? Data(contentsOf: URL(string: data.items.first?.image ?? "")!), + let image = UIImage(data: data) else { return } + self.testResult = image + } + case .serverError: + print("server error") + case .clientError: + print("client error") + case .error: + print("unknown error") + } + } + } +} + From e71f0ec74bcffaaa73b90b2b609326245f8c3f0e Mon Sep 17 00:00:00 2001 From: pccommen Date: Tue, 6 Sep 2022 02:48:48 +0900 Subject: [PATCH 2/2] make networkModuleMoyatype --- .../2022-iOS-2week.xcodeproj/project.pbxproj | 8 +- ...ConstantEmojiContent.swift => Emoji.swift} | 0 week2/2022-iOS-2week/Model/ImojiGame.swift | 134 +++++++++++++++++ .../Calculator.xcodeproj/project.pbxproj | 4 +- .../{ViewModel => }/DoubleExtension.swift | 0 .../Calculator/Model/CalculatorButton.swift | 121 --------------- .../{ViewModel => }/StringExtension.swift | 0 .../Calculator/View/CalculatorView.swift | 106 +++++++++++++ .../NetworkModule.xcodeproj/project.pbxproj | 12 +- .../Network/NetworkModulMoyaStyle.swift | 139 ++++++++++++++++++ ...wift => NetworkModuleAlamofireStyle.swift} | 65 +++++--- .../NetworkModule/NetworkModuleApp.swift | 2 +- .../NetworkModule/View/ContentView.swift | 2 +- .../NetworkModule/ViewModel/Finder.swift | 6 +- 14 files changed, 446 insertions(+), 153 deletions(-) rename week2/2022-iOS-2week/{ConstantEmojiContent.swift => Emoji.swift} (100%) create mode 100644 week2/2022-iOS-2week/Model/ImojiGame.swift rename week3/Calculator/Calculator/{ViewModel => }/DoubleExtension.swift (100%) rename week3/Calculator/Calculator/{ViewModel => }/StringExtension.swift (100%) create mode 100644 week5/NetworkModule/NetworkModule/Network/NetworkModulMoyaStyle.swift rename week5/NetworkModule/NetworkModule/Network/{NetworkModule.swift => NetworkModuleAlamofireStyle.swift} (57%) diff --git a/week2/2022-iOS-2week.xcodeproj/project.pbxproj b/week2/2022-iOS-2week.xcodeproj/project.pbxproj index 74c35d0..3cc1050 100644 --- a/week2/2022-iOS-2week.xcodeproj/project.pbxproj +++ b/week2/2022-iOS-2week.xcodeproj/project.pbxproj @@ -13,7 +13,7 @@ D4265FCA289C0EED007B9AD9 /* CardListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4265FC1289C0EED007B9AD9 /* CardListView.swift */; }; D4265FCB289C0EED007B9AD9 /* BottomButtonListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4265FC2289C0EED007B9AD9 /* BottomButtonListView.swift */; }; D4265FCD289C0EED007B9AD9 /* MainGameBoardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4265FC4289C0EED007B9AD9 /* MainGameBoardView.swift */; }; - D4AFE63A28A7A58600814689 /* ConstantEmojiContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4AFE63928A7A58600814689 /* ConstantEmojiContent.swift */; }; + D4AFE63A28A7A58600814689 /* Emoji.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4AFE63928A7A58600814689 /* Emoji.swift */; }; D4E4D29D289805D400432365 /* ChanggyoAssignment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4E4D29C289805D400432365 /* ChanggyoAssignment.swift */; }; D4E4D2A1289805D500432365 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D4E4D2A0289805D500432365 /* Assets.xcassets */; }; D4E4D2A4289805D500432365 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D4E4D2A3289805D500432365 /* Preview Assets.xcassets */; }; @@ -28,7 +28,7 @@ D4265FC1289C0EED007B9AD9 /* CardListView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CardListView.swift; sourceTree = ""; }; D4265FC2289C0EED007B9AD9 /* BottomButtonListView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BottomButtonListView.swift; sourceTree = ""; }; D4265FC4289C0EED007B9AD9 /* MainGameBoardView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainGameBoardView.swift; sourceTree = ""; }; - D4AFE63928A7A58600814689 /* ConstantEmojiContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConstantEmojiContent.swift; sourceTree = ""; }; + D4AFE63928A7A58600814689 /* Emoji.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Emoji.swift; sourceTree = ""; }; D4E4D299289805D400432365 /* 2022-iOS-2week.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "2022-iOS-2week.app"; sourceTree = BUILT_PRODUCTS_DIR; }; D4E4D29C289805D400432365 /* ChanggyoAssignment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChanggyoAssignment.swift; sourceTree = ""; }; D4E4D2A0289805D500432365 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -99,7 +99,7 @@ D4E4D2A0289805D500432365 /* Assets.xcassets */, D4E4D29C289805D400432365 /* ChanggyoAssignment.swift */, D4265FC4289C0EED007B9AD9 /* MainGameBoardView.swift */, - D4AFE63928A7A58600814689 /* ConstantEmojiContent.swift */, + D4AFE63928A7A58600814689 /* Emoji.swift */, D4265FCE289C1120007B9AD9 /* Model */, D4265FD0289C1136007B9AD9 /* Viewmodel */, D4265FBF289C0EED007B9AD9 /* View */, @@ -185,7 +185,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - D4AFE63A28A7A58600814689 /* ConstantEmojiContent.swift in Sources */, + D4AFE63A28A7A58600814689 /* Emoji.swift in Sources */, D4265FCD289C0EED007B9AD9 /* MainGameBoardView.swift in Sources */, D4265FCB289C0EED007B9AD9 /* BottomButtonListView.swift in Sources */, D4265FC5289C0EED007B9AD9 /* CardView.swift in Sources */, diff --git a/week2/2022-iOS-2week/ConstantEmojiContent.swift b/week2/2022-iOS-2week/Emoji.swift similarity index 100% rename from week2/2022-iOS-2week/ConstantEmojiContent.swift rename to week2/2022-iOS-2week/Emoji.swift diff --git a/week2/2022-iOS-2week/Model/ImojiGame.swift b/week2/2022-iOS-2week/Model/ImojiGame.swift new file mode 100644 index 0000000..465c113 --- /dev/null +++ b/week2/2022-iOS-2week/Model/ImojiGame.swift @@ -0,0 +1,134 @@ + +struct EmojiGame { + var leftCardSetCount: Int { cards.count - cards.filter {$0.state == .isMatched}.count } + + private(set) var cards: [Card] + private(set) var point: Int + + private var isFaceUpCard = false + + // MARK: mutating Func + mutating func changeGameTheme(cardEmojis: [String], displayCardCount: Int) { + let startIndex = Int.random(in: 1...1000) % cardEmojis.count + let endIndex = startIndex + displayCardCount + var newCards = [Card]() + + for i in startIndex ... endIndex { + let index = i % cardEmojis.count + newCards.append(Card(imoji: cardEmojis[index], id: index * 2 + 1)) + newCards.append(Card(imoji: cardEmojis[index], id: index * 2 )) + } + + point = 0 + cards = newCards.shuffled() + } + + mutating func changeStateCard(cardId: Int) { + guard let chosenIndex = cards.firstIndex(where: {cardId == $0.id}) else { return } + if cards[chosenIndex].state == .isMatched { return } + + if isFaceUpCard { + cards[chosenIndex].state.changeCardState() + calculatePoint() + isFaceUpCard = false + } + else { + for index in 0 ..< cards.count { + if cards[index].state != .isFaceDown { + cards[index].state.changeCardState() + } + } + isFaceUpCard = true + cards[chosenIndex].state.changeCardState() + } + } + + mutating private func calculatePoint() { + guard let firstCardIndex = cards.firstIndex(where: { $0.state == .isFaceUp }), + let secondCardIndex = cards.lastIndex(where: { $0.state == .isFaceUp }) else { return } + + if cards[firstCardIndex].imoji == cards[secondCardIndex].imoji { + point += 3 + if leftCardSetCount == 2 { + cards[firstCardIndex].state = .isMatched + cards[secondCardIndex].state = .isMatched + print(leftCardSetCount) + } + else { + cards[firstCardIndex].state = .waitForMatched + cards[secondCardIndex].state = .waitForMatched + } + } + else if !cards[firstCardIndex].didSelected && !cards[secondCardIndex].didSelected { + point += 0 + } + else if cards[firstCardIndex].didSelected && cards[secondCardIndex].didSelected { + point -= 2 + } + else { + point -= 1 + } + + cards[firstCardIndex].didSelected = true + cards[secondCardIndex].didSelected = true + } +} + +// MARK: - extension Nested Struct +extension EmojiGame { + struct Card: Identifiable, Hashable { + var didSelected = false + var state: CardState = .isFaceDown + var imoji: String + var id: Int + } + + enum CardState { + case isFaceUp + case isFaceDown + case isMatched + case waitForMatched + + mutating func changeCardState() { + switch self { + case .waitForMatched: + self = .isMatched + case .isFaceUp: + self = .isFaceDown + case .isFaceDown: + self = .isFaceUp + case .isMatched: + self = .isMatched + } + } + } +} + +extension EmojiGame { + struct ConstantContent { + static let face = ["😀","😍","🤪","🤑","😔","😰","🤯","🤕","🤐","🤩","🤡","🤠","😶","👿","😶‍🌫️","🥶"] + static let animal = ["🐶","🐭","🐻","🐯","🐨","🐸","🦧","🦊","🦍","🐷","🦁","🦥","🦒"] + static let sports = ["🏉","🏈","⚽","🏀","⚾","🥎","🎾","🏐","🎱","🥏","🏓","🥅","🪃","🏒","🥍","🥊"] + static let heart = ["🧡","💔","💙","💜","💛","💚","🤎","🤍","🖤","❤️‍🔥","💝","💗"] + static let vehicle = ["🚗","🚑","🚙","🚓","🚒","🚕","🛺","🚐","🚌","🛻","🚎","🚚","🏎","🚛","🏍","🛵","🚁"] + static let outfit = ["👚","🥼","🦺","🧥","👙","🩱","👞","🥿","🥾","🧢","👡","👠","🧦","👘","👗","🧤","🧣","👓"] + + static func emit(willChangeTheme theme: Theme.Kind) -> [String] { + switch theme { + case .face: + return face + case .animal: + return animal + case .sports: + return sports + case .vehicle: + return vehicle + case .heart: + return heart + case .outfit: + return outfit + } + } + } + +} diff --git a/week3/Calculator/Calculator.xcodeproj/project.pbxproj b/week3/Calculator/Calculator.xcodeproj/project.pbxproj index b8a22d1..efbd0f6 100644 --- a/week3/Calculator/Calculator.xcodeproj/project.pbxproj +++ b/week3/Calculator/Calculator.xcodeproj/project.pbxproj @@ -72,6 +72,8 @@ D460BBA128A93C4800FACD16 /* Preview Content */, D49F037928AE1B9500E3D308 /* View */, D49F037A28AE1B9F00E3D308 /* Model */, + D4156BA428B23BA200F51371 /* DoubleExtension.swift */, + D4156BA628B23BAA00F51371 /* StringExtension.swift */, D49F037828AE1B7300E3D308 /* ViewModel */, ); path = Calculator; @@ -89,8 +91,6 @@ isa = PBXGroup; children = ( D460BBAD28A952A700FACD16 /* Calculator.swift */, - D4156BA428B23BA200F51371 /* DoubleExtension.swift */, - D4156BA628B23BAA00F51371 /* StringExtension.swift */, ); path = ViewModel; sourceTree = ""; diff --git a/week3/Calculator/Calculator/ViewModel/DoubleExtension.swift b/week3/Calculator/Calculator/DoubleExtension.swift similarity index 100% rename from week3/Calculator/Calculator/ViewModel/DoubleExtension.swift rename to week3/Calculator/Calculator/DoubleExtension.swift diff --git a/week3/Calculator/Calculator/Model/CalculatorButton.swift b/week3/Calculator/Calculator/Model/CalculatorButton.swift index 70c672b..ab2e5ed 100644 --- a/week3/Calculator/Calculator/Model/CalculatorButton.swift +++ b/week3/Calculator/Calculator/Model/CalculatorButton.swift @@ -5,124 +5,3 @@ // Created by changgyo seo on 2022/08/21. // -enum CalculatorButton: Identifiable, Equatable { - case equal - case add(Bool) - case subtract(Bool) - case multiply(Bool) - case divide(Bool) - case percent - case sign - case clear(clearType) - case numberOrDot(String) - case none - - var id: String { return content } - - var content: String { - switch self { - case .equal: - return "=" - case .add: - return "+" - case .subtract: - return "-" - case .multiply: - return "*" - case .divide: - return "/" - case .percent: - return "%" - case .sign: - return "±" - case .clear(.displayresult): - return "C" - case .clear(.alldata): - return "AC" - case .numberOrDot(let value): - return value - default: - return "" - } - } - - var backgroundColor: (red: Double, green: Double, blue: Double, opacity: Double) { - switch self { - case .clear, .percent, .sign: - return (160 / 255, 160 / 255, 160 / 255 , 1) - case .numberOrDot: - return (50 / 255, 50 / 255, 50 / 255, 1) - case .add, .divide, .multiply, .subtract: - if isActive { - return (1, 1, 1, 1) - } - else { - return (233 / 255, 157 / 255, 57 / 255, 1) - } - default: - return (233 / 255, 157 / 255, 57 / 255, 1) - } - } - - var textColorColor: (red: Double, green: Double, blue: Double, opacity: Double) { - switch self { - case .clear, .percent, .sign: - return (0, 0, 0, 1) - case .add, .divide, .multiply, .subtract: - if isActive { - return (233 / 255, 157 / 255, 57 / 255, 1) - } - else { - return (1, 1, 1, 1) - } - default: - return (1, 1, 1, 1) - } - } - - var buttonSize: Int { - switch self { - case .numberOrDot("0"): - return 2 - default: - return 1 - } - } - - var isActive: Bool { - switch self { - case .add(let isActive): - return isActive - case .subtract(let isActive): - return isActive - case .multiply(let isActive): - return isActive - case .divide(let isActive): - return isActive - default: - return false - } - } - - mutating func activeButton(active: Bool) { - switch self { - case .divide: - self = .divide(active) - case .multiply: - self = .multiply(active) - case .subtract: - self = .subtract(active) - case .add: - self = .add(active) - default: - break - } - } -} - -extension CalculatorButton { - enum clearType { - case alldata - case displayresult - } -} diff --git a/week3/Calculator/Calculator/ViewModel/StringExtension.swift b/week3/Calculator/Calculator/StringExtension.swift similarity index 100% rename from week3/Calculator/Calculator/ViewModel/StringExtension.swift rename to week3/Calculator/Calculator/StringExtension.swift diff --git a/week3/Calculator/Calculator/View/CalculatorView.swift b/week3/Calculator/Calculator/View/CalculatorView.swift index dbd7b35..480988b 100644 --- a/week3/Calculator/Calculator/View/CalculatorView.swift +++ b/week3/Calculator/Calculator/View/CalculatorView.swift @@ -22,6 +22,112 @@ struct CalculatorView: View { } } +extension CalculatorView { + enum Button: Identifiable, Equatable { + case equal + case plus + case minus + case multiply + case divide + case percent + case sign + case clear(clearType) + case numberOrDot(Decimal) + case none + + var id: String { return content } + + var content: String { + switch self { + case .equal: + return "=" + case .plus: + return "+" + case .minus: + return "-" + case .multiply: + return "*" + case .divide: + return "/" + case .percent: + return "%" + case .sign: + return "±" + case .clear(.displayResult): + return "C" + case .clear(.allData): + return "AC" + case .numberOrDot(let value): + return "\(value)" + default: + return "" + } + } + + var backgroundColor: (red: Double, green: Double, blue: Double, opacity: Double) { + switch self { + case .clear, .percent, .sign: + return (160 / 255, 160 / 255, 160 / 255 , 1) + case .numberOrDot: + return (50 / 255, 50 / 255, 50 / 255, 1) + case .plus, .divide, .multiply, .minus: + if isActive { + return (1, 1, 1, 1) + } + else { + return (233 / 255, 157 / 255, 57 / 255, 1) + } + default: + return (233 / 255, 157 / 255, 57 / 255, 1) + } + } + + var textColorColor: (red: Double, green: Double, blue: Double, opacity: Double) { + switch self { + case .clear, .percent, .sign: + return (0, 0, 0, 1) + case .plus, .divide, .multiply, .minus: + if isActive { + return (233 / 255, 157 / 255, 57 / 255, 1) + } + else { + return (1, 1, 1, 1) + } + default: + return (1, 1, 1, 1) + } + } + + var buttonSize: Int { + switch self { + case .numberOrDot(0): + return 2 + default: + return 1 + } + } + + mutating func activeButton(active: Bool) { + switch self { + case .divide: + self = .divide + case .multiply: + self = .multiply + case .minus: + self = .minus + case .plus: + self = .plus + default: + break + } + } + } + enum clearType { + case allData + case displayResult + } +} + extension CalculatorView { enum DrawConstans { static let veticalModlColumnsCount = 4 diff --git a/week5/NetworkModule/NetworkModule.xcodeproj/project.pbxproj b/week5/NetworkModule/NetworkModule.xcodeproj/project.pbxproj index 2879157..4af4867 100644 --- a/week5/NetworkModule/NetworkModule.xcodeproj/project.pbxproj +++ b/week5/NetworkModule/NetworkModule.xcodeproj/project.pbxproj @@ -11,10 +11,11 @@ D434BDE428BCE54000A2E32B /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D434BDE328BCE54000A2E32B /* ContentView.swift */; }; D434BDE628BCE54100A2E32B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D434BDE528BCE54100A2E32B /* Assets.xcassets */; }; D434BDE928BCE54100A2E32B /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D434BDE828BCE54100A2E32B /* Preview Assets.xcassets */; }; - D434BDF028BCE55400A2E32B /* NetworkModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D434BDEF28BCE55400A2E32B /* NetworkModule.swift */; }; + D434BDF028BCE55400A2E32B /* NetworkModuleAlamofireStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = D434BDEF28BCE55400A2E32B /* NetworkModuleAlamofireStyle.swift */; }; D434BDF428BDDB1600A2E32B /* Finder.swift in Sources */ = {isa = PBXBuildFile; fileRef = D434BDF328BDDB1600A2E32B /* Finder.swift */; }; D434BDF628BE0C6300A2E32B /* NaverOpenAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = D434BDF528BE0C6300A2E32B /* NaverOpenAPI.swift */; }; D434BDFB28BE0EEB00A2E32B /* SearchModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D434BDFA28BE0EEB00A2E32B /* SearchModel.swift */; }; + D49D2D1628C64F1A0024E844 /* NetworkModulMoyaStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = D49D2D1528C64F1A0024E844 /* NetworkModulMoyaStyle.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -23,10 +24,11 @@ D434BDE328BCE54000A2E32B /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; D434BDE528BCE54100A2E32B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; D434BDE828BCE54100A2E32B /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; - D434BDEF28BCE55400A2E32B /* NetworkModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkModule.swift; sourceTree = ""; }; + D434BDEF28BCE55400A2E32B /* NetworkModuleAlamofireStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkModuleAlamofireStyle.swift; sourceTree = ""; }; D434BDF328BDDB1600A2E32B /* Finder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Finder.swift; sourceTree = ""; }; D434BDF528BE0C6300A2E32B /* NaverOpenAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NaverOpenAPI.swift; sourceTree = ""; }; D434BDFA28BE0EEB00A2E32B /* SearchModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchModel.swift; sourceTree = ""; }; + D49D2D1528C64F1A0024E844 /* NetworkModulMoyaStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkModulMoyaStyle.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -81,8 +83,9 @@ D434BDF728BE0EC000A2E32B /* Network */ = { isa = PBXGroup; children = ( - D434BDEF28BCE55400A2E32B /* NetworkModule.swift */, + D434BDEF28BCE55400A2E32B /* NetworkModuleAlamofireStyle.swift */, D434BDF528BE0C6300A2E32B /* NaverOpenAPI.swift */, + D49D2D1528C64F1A0024E844 /* NetworkModulMoyaStyle.swift */, ); path = Network; sourceTree = ""; @@ -185,8 +188,9 @@ D434BDF628BE0C6300A2E32B /* NaverOpenAPI.swift in Sources */, D434BDE428BCE54000A2E32B /* ContentView.swift in Sources */, D434BDFB28BE0EEB00A2E32B /* SearchModel.swift in Sources */, + D49D2D1628C64F1A0024E844 /* NetworkModulMoyaStyle.swift in Sources */, D434BDE228BCE54000A2E32B /* NetworkModuleApp.swift in Sources */, - D434BDF028BCE55400A2E32B /* NetworkModule.swift in Sources */, + D434BDF028BCE55400A2E32B /* NetworkModuleAlamofireStyle.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/week5/NetworkModule/NetworkModule/Network/NetworkModulMoyaStyle.swift b/week5/NetworkModule/NetworkModule/Network/NetworkModulMoyaStyle.swift new file mode 100644 index 0000000..87946c6 --- /dev/null +++ b/week5/NetworkModule/NetworkModule/Network/NetworkModulMoyaStyle.swift @@ -0,0 +1,139 @@ +// +// NetworkModulMoyaStyle.swift +// NetworkModule +// +// Created by changgyo seo on 2022/09/06. +// +import Foundation + +protocol RequestType { + + var url: String { get } + var method: HttpMethod { get } + var entity: Any { get } + var parameters: Parameters? { get } + var header: [String: String]? { get } +} + +enum MovieAPI: RequestType { + + case movieRequest(parameters: [String: String]?) + + var url: String { + switch self { + case .movieRequest: + return NaverOpenAPI.movieJsonURL + } + } + + var method: HttpMethod { + switch self { + case .movieRequest: + return .get + } + } + + var entity: Any { + switch self { + case .movieRequest: + return NaverOpneAPICommonResponse.self + } + } + + var parameters: Parameters? { + switch self { + case .movieRequest(let parameters): + return .urlString(parameters ?? [:]) + } + } + + var header: [String : String]? { + switch self { + case .movieRequest: + return NaverOpenAPI.header + } + } +} + +enum Parameters { + case urlString([String: String]) + case bodyString([String: String]) +} + +struct NetworkHost { + + func request(_ request: requestAPI, completion: @escaping () -> Void) { + + let t = String.self + let k = type + var urlComponent = URLComponents(string: request.url) + var urlRequest = URLRequest(url: URL(string: request.url)!) + + + switch request.parameters { + case .urlString(let parameters): + var queryItems = [URLQueryItem]() + parameters.forEach { key, value in + queryItems.append(URLQueryItem(name: key, value: value)) + } + urlComponent?.queryItems = queryItems + urlRequest.url = urlComponent!.url + + case .bodyString(let parameters): + let paramData = try! JSONSerialization.data(withJSONObject: parameters, options: []) + urlRequest.url = URL(string: request.url) + urlRequest.httpBody = paramData + urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type") + urlRequest.setValue(String(paramData.count), forHTTPHeaderField: "Content-Length") + case .none: + break; + } + + urlRequest.httpMethod = request.method.rawValue + request.header?.forEach { urlRequest.addValue($1, forHTTPHeaderField: $0) } + + //guard let urlRequest = urlRequest else { return } + URLSession.shared.dataTask(with: urlRequest, completionHandler: { data, urlResponse, error in + + guard error == nil else { + completion() + return + } + + guard let httpURLResponse = urlResponse as? HTTPURLResponse, + let data = data else { return } + + var networkResult = type(of: request.entity) + let decoder = JSONDecoder() + guard let reponseData = try? decoder.decode(EntityType.self, from: data) + else { return } + + switch httpURLResponse.statusCode { + + case (200 ..< 300): + networkResult = .success(reponseData) + case (400 ..< 500): + networkResult = .clientError + case (300 ..< 400): + networkResult = .serverError + default: + networkResult = .error + } + + completion(networkResult) + }).resume() + } + + +} + +extension NetworkHost { + + enum NetworkResult { + + case success(T) + case serverError + case clientError + case error + } +} diff --git a/week5/NetworkModule/NetworkModule/Network/NetworkModule.swift b/week5/NetworkModule/NetworkModule/Network/NetworkModuleAlamofireStyle.swift similarity index 57% rename from week5/NetworkModule/NetworkModule/Network/NetworkModule.swift rename to week5/NetworkModule/NetworkModule/Network/NetworkModuleAlamofireStyle.swift index c71b9cb..1cc25ac 100644 --- a/week5/NetworkModule/NetworkModule/Network/NetworkModule.swift +++ b/week5/NetworkModule/NetworkModule/Network/NetworkModuleAlamofireStyle.swift @@ -7,9 +7,13 @@ import Foundation -struct NetworkModule { +struct NetworkModuleAlamofireStyle { - static let shared = NetworkModule() + private init() {} + + static let shared = NetworkModuleAlamofireStyle() + + var defaultHeadr = [String: String]() /// fetchData는 원하는 종류의 엔티티를 제네릭의 입력후 필요한 prameter를 입력하면 됩니다. /// @@ -20,27 +24,45 @@ struct NetworkModule { /// - paramaters: 원하는 파라미터를 넣으면 됩니다. 이때. value는 반드시 string protocol을 따라야합니다. /// - header: 토큰이나 필수 요소를 파라미터와 같은 형태로 넣으면 됩니다. /// - completion: 제네릭의 넣은 모델을 바탕으로 결과값을 보내줍니다. - func fetchData(_ url: String, entity: EntityType.Type, httpMethod: HttpMethod, paramaters: [String: String]? = nil, header: [String: String]? = nil,completion: @escaping (NetworkResult?) -> Void) { + func fetchData(_ url: String, + entity: EntityType.Type, + httpMethod: HttpMethod, + paramaters: [String: String]? = nil, + header: [String: String]? = nil, + completion: @escaping (NetworkResult?) -> Void) { var urlComponent = URLComponents(string: url) - var queryItems = [URLQueryItem]() - paramaters?.forEach { key, value in - queryItems.append(URLQueryItem(name: key, value: value)) - } - urlComponent?.queryItems = queryItems + var urlRequest = URLRequest(url: URL(string: url)!) - guard let url = urlComponent?.url else { return } - var urlRequest = URLRequest(url: url) - urlRequest.httpMethod = httpMethod.rawValue - header?.forEach { key, value in - - urlRequest.addValue(value, forHTTPHeaderField: key) + switch httpMethod { + case .get: + var queryItems = [URLQueryItem]() + paramaters?.forEach { key, value in + queryItems.append(URLQueryItem(name: key, value: value)) + } + urlComponent?.queryItems = queryItems + urlRequest.url = urlComponent!.url + case .post, .put, .delete: + guard let paramaters = paramaters else { return } + let paramData = try! JSONSerialization.data(withJSONObject: paramaters, options: []) + urlRequest.url = URL(string: url) + urlRequest.httpBody = paramData + urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type") + urlRequest.setValue(String(paramData.count), forHTTPHeaderField: "Content-Length") } + urlRequest.httpMethod = httpMethod.rawValue + header?.forEach { urlRequest.addValue($1, forHTTPHeaderField: $0) } + + //guard let urlRequest = urlRequest else { return } URLSession.shared.dataTask(with: urlRequest, completionHandler: { data, urlResponse, error in - guard error == nil, - let httpURLResponse = urlResponse as? HTTPURLResponse, + guard error == nil else { + completion(.error) + return + } + + guard let httpURLResponse = urlResponse as? HTTPURLResponse, let data = data else { return } var networkResult: NetworkResult? @@ -75,7 +97,7 @@ struct NetworkModule { } } -extension NetworkModule { +extension NetworkModuleAlamofireStyle { enum NetworkResult { @@ -84,6 +106,15 @@ extension NetworkModule { case clientError case error } + + struct FetchParameter { + + var url: String + var entity: T + var httpMethod: HttpMethod + var parameters: [String: String]? + var header: [String: String]? + } } protocol RequestEnum { diff --git a/week5/NetworkModule/NetworkModule/NetworkModuleApp.swift b/week5/NetworkModule/NetworkModule/NetworkModuleApp.swift index 80d64ea..2aa0dd9 100644 --- a/week5/NetworkModule/NetworkModule/NetworkModuleApp.swift +++ b/week5/NetworkModule/NetworkModule/NetworkModuleApp.swift @@ -13,7 +13,7 @@ struct NetworkModuleApp: App { let movieFetcher = Finder() var body: some Scene { WindowGroup { - ContentView(viewModel: movieFetcher, movieTitle: "") + ContentView(viewModel: movieFetcher) } } diff --git a/week5/NetworkModule/NetworkModule/View/ContentView.swift b/week5/NetworkModule/NetworkModule/View/ContentView.swift index eb06d1f..8833189 100644 --- a/week5/NetworkModule/NetworkModule/View/ContentView.swift +++ b/week5/NetworkModule/NetworkModule/View/ContentView.swift @@ -10,7 +10,7 @@ import SwiftUI struct ContentView: View { @ObservedObject var viewModel: Finder - @State var movieTitle: String + @State private var movieTitle: String = "" var body: some View { diff --git a/week5/NetworkModule/NetworkModule/ViewModel/Finder.swift b/week5/NetworkModule/NetworkModule/ViewModel/Finder.swift index 1baa0b2..8359ff7 100644 --- a/week5/NetworkModule/NetworkModule/ViewModel/Finder.swift +++ b/week5/NetworkModule/NetworkModule/ViewModel/Finder.swift @@ -14,15 +14,15 @@ class Finder: ObservableObject { func fetchMovieList(parameterList: [Movie.Request]) { - NetworkModule.shared.fetchData(NaverOpenAPI.movieJsonURL, + NetworkModuleAlamofireStyle.shared.fetchData(NaverOpenAPI.movieJsonURL, entity: NaverOpneAPICommonResponse.self, httpMethod: .get, - paramaters: NetworkModule.shared.makeParameter(parameterList), + paramaters: NetworkModuleAlamofireStyle.shared.makeParameter(parameterList), header: NaverOpenAPI.header) { [weak self] response in guard let self = self, let response = response else { return } - + switch response { case .success(let data):