From 0439e4fa5d6750924390ca766fe542c3b3136fc6 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Thu, 16 Jun 2022 18:27:13 +0100 Subject: [PATCH 01/24] Add multi-line escaped newline test --- Tests/RegexTests/ParseTests.swift | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Tests/RegexTests/ParseTests.swift b/Tests/RegexTests/ParseTests.swift index 425f44f48..4b6077011 100644 --- a/Tests/RegexTests/ParseTests.swift +++ b/Tests/RegexTests/ParseTests.swift @@ -2099,6 +2099,17 @@ extension RegexTests { throwsError: .unsupported, syntax: .extendedSyntax ) + parseWithDelimitersTest( + #""" + #/ + a\ + b\ + c + /# + """#, + concat("a", "\n", "b", "\n", "c") + ) + // MARK: Parse with delimiters parseWithDelimitersTest("/a b/", concat("a", " ", "b")) From 76b5605405f82ccbdd97ad6c72f771c2bd4fd945 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Thu, 16 Jun 2022 18:27:13 +0100 Subject: [PATCH 02/24] Fix the definition of `SyntaxOptions.experimental` This shouldn't include e.g `namedCapturesOnly`. --- Sources/_RegexParser/Regex/Parse/SyntaxOptions.swift | 4 ++-- Tests/RegexTests/ParseTests.swift | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Sources/_RegexParser/Regex/Parse/SyntaxOptions.swift b/Sources/_RegexParser/Regex/Parse/SyntaxOptions.swift index dbfe5f2d6..027ef86bb 100644 --- a/Sources/_RegexParser/Regex/Parse/SyntaxOptions.swift +++ b/Sources/_RegexParser/Regex/Parse/SyntaxOptions.swift @@ -76,8 +76,8 @@ public struct SyntaxOptions: OptionSet { public static var traditional: Self { Self(0) } public static var experimental: Self { - // Experimental syntax enables everything except end-of-line comments. - Self(~0).subtracting(.endOfLineComments) + [.nonSemanticWhitespace, .experimentalQuotes, .experimentalComments, + .experimentalRanges, .experimentalCaptures] } // TODO: Probably want to model strict-PCRE etc. options too. diff --git a/Tests/RegexTests/ParseTests.swift b/Tests/RegexTests/ParseTests.swift index 4b6077011..e1055ec78 100644 --- a/Tests/RegexTests/ParseTests.swift +++ b/Tests/RegexTests/ParseTests.swift @@ -772,6 +772,9 @@ extension RegexTests { syntax: .experimental) parseTest(#""\"""#, quote("\""), syntax: .experimental) + parseTest(#"(abc)"#, capture(concat("a", "b", "c")), + syntax: .experimental, captures: [.cap]) + // Quotes in character classes. parseTest(#"[\Q-\E]"#, charClass(quote_m("-"))) parseTest(#"[\Qa-b[[*+\\E]"#, charClass(quote_m("a-b[[*+\\"))) From 31eb41736676d10f82aab2054b5f03d82ee40718 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Thu, 16 Jun 2022 18:27:13 +0100 Subject: [PATCH 03/24] Clarify the `.multilineCompilerLiteral` syntax option This should always be set in a multi-line literal, with extended syntax potentially being set and unset as we parse. --- Sources/_RegexParser/Regex/Parse/LexicalAnalysis.swift | 4 ++-- Sources/_RegexParser/Regex/Parse/Parse.swift | 4 ++-- Sources/_RegexParser/Regex/Parse/SyntaxOptions.swift | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Sources/_RegexParser/Regex/Parse/LexicalAnalysis.swift b/Sources/_RegexParser/Regex/Parse/LexicalAnalysis.swift index f63cd435e..108457ae8 100644 --- a/Sources/_RegexParser/Regex/Parse/LexicalAnalysis.swift +++ b/Sources/_RegexParser/Regex/Parse/LexicalAnalysis.swift @@ -597,7 +597,7 @@ extension Source { }.value // In multi-line literals, the quote may not span multiple lines. - if context.syntax.contains(.multilineExtendedSyntax), + if context.syntax.contains(.multilineCompilerLiteral), contents.spansMultipleLinesInRegexLiteral { throw ParseError.quoteMayNotSpanMultipleLines } @@ -841,7 +841,7 @@ extension Source { throw ParseError.cannotRemoveSemanticsOptions } // Extended syntax may not be removed if in multi-line mode. - if context.syntax.contains(.multilineExtendedSyntax) && + if context.syntax.contains(.multilineCompilerLiteral) && opt.isAnyExtended { throw ParseError.cannotRemoveExtendedSyntaxInMultilineMode } diff --git a/Sources/_RegexParser/Regex/Parse/Parse.swift b/Sources/_RegexParser/Regex/Parse/Parse.swift index e540e6c1d..fff45b2d1 100644 --- a/Sources/_RegexParser/Regex/Parse/Parse.swift +++ b/Sources/_RegexParser/Regex/Parse/Parse.swift @@ -317,7 +317,7 @@ extension Parser { // engines such as Oniguruma, Java, and ICU do this under (?x). Therefore, // treat (?x) and (?xx) as the same option here. If we ever get a strict // PCRE mode, we will need to change this to handle that. - if !context.syntax.contains(.multilineExtendedSyntax) { + if !context.syntax.contains(.multilineCompilerLiteral) { mapOption(.extendedSyntax, \.isAnyExtended) } } @@ -609,7 +609,7 @@ fileprivate func defaultSyntaxOptions( // For an extended syntax forward slash e.g #/.../#, extended syntax is // permitted if it spans multiple lines. if delim.poundCount > 0 && contents.spansMultipleLinesInRegexLiteral { - return .multilineExtendedSyntax + return [.multilineCompilerLiteral, .extendedSyntax] } return .traditional case .reSingleQuote: diff --git a/Sources/_RegexParser/Regex/Parse/SyntaxOptions.swift b/Sources/_RegexParser/Regex/Parse/SyntaxOptions.swift index 027ef86bb..302032fd3 100644 --- a/Sources/_RegexParser/Regex/Parse/SyntaxOptions.swift +++ b/Sources/_RegexParser/Regex/Parse/SyntaxOptions.swift @@ -58,10 +58,10 @@ public struct SyntaxOptions: OptionSet { /// `(_: .*)` == `(?:.*)` public static var experimentalCaptures: Self { Self(1 << 5) } - /// The default syntax for a multi-line regex literal. - public static var multilineExtendedSyntax: Self { - return [Self(1 << 6), .extendedSyntax] - } + /// The syntax kind of a multi-line literal. This will always be set when + /// parsing a multi-line `#/.../#` literal. Note this does not imply extended + /// syntax, as that may be temporarily disabled while parsing. + public static var multilineCompilerLiteral: Self { Self(1 << 6) } /// `(?n)` public static var namedCapturesOnly: Self { Self(1 << 7) } From 2aa035e07583f0b2f97b302ae6e32daf5471c2e3 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Thu, 16 Jun 2022 18:27:14 +0100 Subject: [PATCH 04/24] Allow scoped removal of extended syntax in multi-line literals Relax the ban on unsetting extended syntax in a multi-line literal such that it does not apply to a scoped unset e.g `(?-x:...)`, as long as it does not span multiple lines. This commit also bans the use of `(?^)` in a multi-line literal, unless it is scoped and does not span multiple lines. Instead, `(?^x)` must be written, as PCRE defines `(?^)` to be equivalent to `(?-imnsx)`. --- .../Regex/Parse/Diagnostics.swift | 6 ++ .../Regex/Parse/LexicalAnalysis.swift | 5 -- Sources/_RegexParser/Regex/Parse/Parse.swift | 52 +++++++++--- Tests/RegexTests/ParseTests.swift | 80 ++++++++++++++++--- 4 files changed, 116 insertions(+), 27 deletions(-) diff --git a/Sources/_RegexParser/Regex/Parse/Diagnostics.swift b/Sources/_RegexParser/Regex/Parse/Diagnostics.swift index 1c9ee57bd..618ae2412 100644 --- a/Sources/_RegexParser/Regex/Parse/Diagnostics.swift +++ b/Sources/_RegexParser/Regex/Parse/Diagnostics.swift @@ -45,6 +45,7 @@ enum ParseError: Error, Hashable { case confusableCharacter(Character) case quoteMayNotSpanMultipleLines + case unsetExtendedSyntaxMayNotSpanMultipleLines case cannotReferToWholePattern @@ -81,6 +82,7 @@ enum ParseError: Error, Hashable { case cannotRemoveTextSegmentOptions case cannotRemoveSemanticsOptions case cannotRemoveExtendedSyntaxInMultilineMode + case cannotResetExtendedSyntaxInMultilineMode case expectedCalloutArgument @@ -143,6 +145,8 @@ extension ParseError: CustomStringConvertible { return "'\(c)' is confusable for a metacharacter; use '\\u{...}' instead" case .quoteMayNotSpanMultipleLines: return "quoted sequence may not span multiple lines in multi-line literal" + case .unsetExtendedSyntaxMayNotSpanMultipleLines: + return "group that unsets extended syntax may not span multiple lines in multi-line literal" case .cannotReferToWholePattern: return "cannot refer to whole pattern here" case .quantifierRequiresOperand(let q): @@ -194,6 +198,8 @@ extension ParseError: CustomStringConvertible { return "semantic level cannot be unset, only changed" case .cannotRemoveExtendedSyntaxInMultilineMode: return "extended syntax may not be disabled in multi-line mode" + case .cannotResetExtendedSyntaxInMultilineMode: + return "extended syntax may not be disabled in multi-line mode; use '(?^x)' instead" case .expectedCalloutArgument: return "expected argument to callout" case .unrecognizedScript(let value): diff --git a/Sources/_RegexParser/Regex/Parse/LexicalAnalysis.swift b/Sources/_RegexParser/Regex/Parse/LexicalAnalysis.swift index 108457ae8..41b744234 100644 --- a/Sources/_RegexParser/Regex/Parse/LexicalAnalysis.swift +++ b/Sources/_RegexParser/Regex/Parse/LexicalAnalysis.swift @@ -840,11 +840,6 @@ extension Source { if opt.isSemanticMatchingLevel { throw ParseError.cannotRemoveSemanticsOptions } - // Extended syntax may not be removed if in multi-line mode. - if context.syntax.contains(.multilineCompilerLiteral) && - opt.isAnyExtended { - throw ParseError.cannotRemoveExtendedSyntaxInMultilineMode - } removing.append(opt) } return .init(caretLoc: nil, adding: adding, minusLoc: ateMinus.location, diff --git a/Sources/_RegexParser/Regex/Parse/Parse.swift b/Sources/_RegexParser/Regex/Parse/Parse.swift index fff45b2d1..3e20ae8c0 100644 --- a/Sources/_RegexParser/Regex/Parse/Parse.swift +++ b/Sources/_RegexParser/Regex/Parse/Parse.swift @@ -289,8 +289,8 @@ extension Parser { /// Apply the syntax options of a given matching option sequence to the /// current set of options. private mutating func applySyntaxOptions( - of opts: AST.MatchingOptionSequence - ) { + of opts: AST.MatchingOptionSequence, isScoped: Bool + ) throws { func mapOption(_ option: SyntaxOptions, _ pred: (AST.MatchingOption) -> Bool) { if opts.resetsCurrentOptions { @@ -311,22 +311,41 @@ extension Parser { mapOption(.namedCapturesOnly, .namedCapturesOnly) // (?x), (?xx) - // We skip this for multi-line, as extended syntax is always enabled there. + // This cannot be unset in a multi-line literal, unless in a scoped group + // e.g (?-x:...). We later enforce that such a group does not span multiple + // lines. // TODO: PCRE differentiates between (?x) and (?xx) where only the latter // handles non-semantic whitespace in a custom character class. Other // engines such as Oniguruma, Java, and ICU do this under (?x). Therefore, // treat (?x) and (?xx) as the same option here. If we ever get a strict // PCRE mode, we will need to change this to handle that. - if !context.syntax.contains(.multilineCompilerLiteral) { + if !isScoped && context.syntax.contains(.multilineCompilerLiteral) { + // An unscoped removal of extended syntax is not allowed in a multi-line + // literal. + if let opt = opts.removing.first(where: \.isAnyExtended) { + throw Source.LocatedError( + ParseError.cannotRemoveExtendedSyntaxInMultilineMode, opt.location) + } + if opts.resetsCurrentOptions { + throw Source.LocatedError( + ParseError.cannotResetExtendedSyntaxInMultilineMode, opts.caretLoc!) + } + // The only remaning case is an unscoped addition of extended syntax, + // which is a no-op. + } else { + // We either have a scoped change of extended syntax, or this is a + // single-line literal. mapOption(.extendedSyntax, \.isAnyExtended) } } /// Apply the syntax options of a matching option changing group to the /// current set of options. - private mutating func applySyntaxOptions(of group: AST.Group.Kind) { + private mutating func applySyntaxOptions( + of group: AST.Group.Kind, isScoped: Bool + ) throws { if case .changeMatchingOptions(let seq) = group { - applySyntaxOptions(of: seq) + try applySyntaxOptions(of: seq, isScoped: isScoped) } } @@ -337,14 +356,25 @@ extension Parser { context.recordGroup(kind.value) let currentSyntax = context.syntax - applySyntaxOptions(of: kind.value) + try applySyntaxOptions(of: kind.value, isScoped: true) defer { context.syntax = currentSyntax } - + let unsetsExtendedSyntax = currentSyntax.contains(.extendedSyntax) && + !context.syntax.contains(.extendedSyntax) let child = try parseNode() try source.expect(")") - return .init(kind, child, loc(start)) + let groupLoc = loc(start) + + // In multi-line literals, the body of a group that unsets extended syntax + // may not span multiple lines. + if unsetsExtendedSyntax && + context.syntax.contains(.multilineCompilerLiteral) && + source[child.location.range].spansMultipleLinesInRegexLiteral { + throw Source.LocatedError( + ParseError.unsetExtendedSyntaxMayNotSpanMultipleLines, groupLoc) + } + return .init(kind, child, groupLoc) } /// Consume the body of an absent function. @@ -438,7 +468,7 @@ extension Parser { // If we have a change matching options atom, apply the syntax options. We // already take care of scoping syntax options within a group. if case .changeMatchingOptions(let opts) = atom.kind { - applySyntaxOptions(of: opts) + try applySyntaxOptions(of: opts, isScoped: false) } // TODO: track source locations return .atom(atom) @@ -592,7 +622,7 @@ public func parse( return ast } -extension String { +extension StringProtocol { /// Whether the given string is considered multi-line for a regex literal. var spansMultipleLinesInRegexLiteral: Bool { unicodeScalars.contains(where: { $0 == "\n" || $0 == "\r" }) diff --git a/Tests/RegexTests/ParseTests.swift b/Tests/RegexTests/ParseTests.swift index e1055ec78..5315f24c4 100644 --- a/Tests/RegexTests/ParseTests.swift +++ b/Tests/RegexTests/ParseTests.swift @@ -1780,6 +1780,13 @@ extension RegexTests { " ", "b" ) ) + parseTest( + "(?x) a (?^: b)", concat( + changeMatchingOptions(matchingOptions(adding: .extended)), + "a", + changeMatchingOptions(unsetMatchingOptions(), concat(" ", "b")) + ) + ) parseTest("[ # abc]", charClass(" ", "#", " ", "a", "b", "c")) parseTest("[#]", charClass("#")) @@ -2188,22 +2195,40 @@ extension RegexTests { /# """, concat("a", "b")) - // Make sure (?^) is ignored. + // (?x) has no effect. parseWithDelimitersTest(""" #/ - (?^) + (?x) # comment /# - """, changeMatchingOptions(unsetMatchingOptions()) + """, changeMatchingOptions(matchingOptions(adding: .extended)) ) - // (?x) has no effect. + // Scoped removal of extended syntax is allowed as long as it does not span + // multiple lines. parseWithDelimitersTest(""" #/ - (?x) - # comment + (?-x:a b) /# - """, changeMatchingOptions(matchingOptions(adding: .extended)) + """, changeMatchingOptions( + matchingOptions(removing: .extended), + concat("a", " ", "b") + ) + ) + parseWithDelimitersTest(""" + #/ + (?-xx:a b) + /# + """, changeMatchingOptions( + matchingOptions(removing: .extraExtended), + concat("a", " ", "b") + ) + ) + parseWithDelimitersTest(""" + #/ + (?^: a b ) # comment + /# + """, changeMatchingOptions(unsetMatchingOptions(), concat(" ", "a", " ", "b", " ")) ) parseWithDelimitersTest(#""" @@ -2787,17 +2812,50 @@ extension RegexTests { /# """, .cannotRemoveExtendedSyntaxInMultilineMode ) + + // Scoped removal of extended syntax may not span multiple lines diagnosticWithDelimitersTest(""" #/ - (?-x:a b) + (?-x:a b + ) /# - """, .cannotRemoveExtendedSyntaxInMultilineMode + """, .unsetExtendedSyntaxMayNotSpanMultipleLines ) diagnosticWithDelimitersTest(""" #/ - (?-xx:a b) + (?-x:a + b) /# - """, .cannotRemoveExtendedSyntaxInMultilineMode + """, .unsetExtendedSyntaxMayNotSpanMultipleLines + ) + diagnosticWithDelimitersTest(""" + #/ + (?-xx: + a b) + /# + """, .unsetExtendedSyntaxMayNotSpanMultipleLines + ) + diagnosticWithDelimitersTest(""" + #/ + (?x-x: + a b) + /# + """, .unsetExtendedSyntaxMayNotSpanMultipleLines + ) + diagnosticWithDelimitersTest(""" + #/ + (?^) + # comment + /# + """, .cannotResetExtendedSyntaxInMultilineMode + ) + diagnosticWithDelimitersTest(""" + #/ + (?^: + # comment + ) + /# + """, .unsetExtendedSyntaxMayNotSpanMultipleLines ) diagnosticWithDelimitersTest(#""" From ed5aedb702d2cb2a80929310790b258aa57a1a20 Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Thu, 16 Jun 2022 20:20:43 -0500 Subject: [PATCH 05/24] Implement atomic non-capturing groups (#488) * Add a `clearThrough` instruction This will let us fix lookahead assertions that have leftover save points in the subpattern on success, and also allow us to implement atomic groups. * Fix lookaheads with quantifiers On success, the subpatterns in lookaheads like (?=.*e) had a save point that persisted, causing the logic in the lookahead group to be invalid. * Implement atomic non-capturing group support In addition to the (?>...) syntax, this is what's underneath `Local`. --- Sources/_RegexParser/Regex/Parse/Sema.swift | 6 +- Sources/_StringProcessing/ByteCodeGen.swift | 39 +++++++- .../Engine/Instruction.swift | 7 ++ .../_StringProcessing/Engine/MEBuilder.swift | 6 +- .../_StringProcessing/Engine/Processor.swift | 17 +++- Tests/RegexBuilderTests/RegexDSLTests.swift | 23 +++++ Tests/RegexTests/MatchTests.swift | 96 ++++++++++++++++++- Tests/RegexTests/ParseTests.swift | 4 +- 8 files changed, 183 insertions(+), 15 deletions(-) diff --git a/Sources/_RegexParser/Regex/Parse/Sema.swift b/Sources/_RegexParser/Regex/Parse/Sema.swift index ba6e17316..c49436702 100644 --- a/Sources/_RegexParser/Regex/Parse/Sema.swift +++ b/Sources/_RegexParser/Regex/Parse/Sema.swift @@ -325,7 +325,8 @@ extension RegexValidator { func validateGroup(_ group: AST.Group) throws { let kind = group.kind switch kind.value { - case .capture, .namedCapture, .nonCapture, .lookahead, .negativeLookahead: + case .capture, .namedCapture, .nonCapture, .lookahead, .negativeLookahead, + .atomicNonCapturing: break case .balancedCapture: @@ -336,9 +337,6 @@ extension RegexValidator { // We need to figure out how these interact with typed captures. throw error(.unsupported("branch reset group"), at: kind.location) - case .atomicNonCapturing: - throw error(.unsupported("atomic group"), at: kind.location) - case .nonAtomicLookahead: throw error(.unsupported("non-atomic lookahead"), at: kind.location) diff --git a/Sources/_StringProcessing/ByteCodeGen.swift b/Sources/_StringProcessing/ByteCodeGen.swift index f4de2f782..8407f68ac 100644 --- a/Sources/_StringProcessing/ByteCodeGen.swift +++ b/Sources/_StringProcessing/ByteCodeGen.swift @@ -306,7 +306,7 @@ fileprivate extension Compiler.ByteCodeGen { save(restoringAt: success) save(restoringAt: intercept) // failure restores at intercept - clearSavePoint // remove intercept + clearThrough(intercept) // remove intercept and any leftovers from : clearSavePoint // remove success fail // positive->success, negative propagates @@ -324,7 +324,7 @@ fileprivate extension Compiler.ByteCodeGen { builder.buildSave(success) builder.buildSave(intercept) try emitNode(child) - builder.buildClear() + builder.buildClearThrough(intercept) if !positive { builder.buildClear() } @@ -339,6 +339,38 @@ fileprivate extension Compiler.ByteCodeGen { builder.label(success) } + mutating func emitAtomicNoncapturingGroup( + _ child: DSLTree.Node + ) throws { + /* + save(continuingAt: success) + save(restoringAt: intercept) + // failure restores at intercept + clearThrough(intercept) // remove intercept and any leftovers from + fail // ->success + intercept: + clearSavePoint // remove success + fail // propagate failure + success: + ... + */ + + let intercept = builder.makeAddress() + let success = builder.makeAddress() + + builder.buildSaveAddress(success) + builder.buildSave(intercept) + try emitNode(child) + builder.buildClearThrough(intercept) + builder.buildFail() + + builder.label(intercept) + builder.buildClear() + builder.buildFail() + + builder.label(success) + } + mutating func emitMatcher( _ matcher: @escaping _MatcherInterface ) -> ValueRegister { @@ -384,6 +416,9 @@ fileprivate extension Compiler.ByteCodeGen { } options.apply(optionSequence) try emitNode(child) + + case .atomicNonCapturing: + try emitAtomicNoncapturingGroup(child) default: // FIXME: Other kinds... diff --git a/Sources/_StringProcessing/Engine/Instruction.swift b/Sources/_StringProcessing/Engine/Instruction.swift index ff28ee9e2..9144c031f 100644 --- a/Sources/_StringProcessing/Engine/Instruction.swift +++ b/Sources/_StringProcessing/Engine/Instruction.swift @@ -228,6 +228,13 @@ extension Instruction { /// Precondition: There is a save point to remove case clear + /// Remove save points up to and including the operand + /// + /// Operand: instruction address to look for + /// + /// Precondition: The operand is in the save point list + case clearThrough + /// View the most recently saved point /// /// UNIMPLEMENTED diff --git a/Sources/_StringProcessing/Engine/MEBuilder.swift b/Sources/_StringProcessing/Engine/MEBuilder.swift index 917e010f6..13b2d3798 100644 --- a/Sources/_StringProcessing/Engine/MEBuilder.swift +++ b/Sources/_StringProcessing/Engine/MEBuilder.swift @@ -153,6 +153,10 @@ extension MEProgram.Builder { mutating func buildClear() { instructions.append(.init(.clear)) } + mutating func buildClearThrough(_ t: AddressToken) { + instructions.append(.init(.clearThrough)) + fixup(to: t) + } mutating func buildRestore() { instructions.append(.init(.restore)) } @@ -317,7 +321,7 @@ extension MEProgram.Builder { case .condBranchZeroElseDecrement: payload = .init(addr: addr, int: inst.payload.int) - case .branch, .save, .saveAddress, .call: + case .branch, .save, .saveAddress, .call, .clearThrough: payload = .init(addr: addr) case .splitSaving: diff --git a/Sources/_StringProcessing/Engine/Processor.swift b/Sources/_StringProcessing/Engine/Processor.swift index 406d2cc8c..45053852d 100644 --- a/Sources/_StringProcessing/Engine/Processor.swift +++ b/Sources/_StringProcessing/Engine/Processor.swift @@ -204,6 +204,17 @@ extension Processor { } } + mutating func clearThrough(_ address: InstructionAddress) { + while let sp = savePoints.popLast() { + if sp.pc == address { + controller.step() + return + } + } + // TODO: What should we do here? + fatalError("Invalid code: Tried to clear save points when empty") + } + mutating func cycle() { _checkInvariants() assert(state == .inProgress) @@ -288,9 +299,13 @@ extension Processor { if let _ = savePoints.popLast() { controller.step() } else { - fatalError("TODO: What should we do here?") + // TODO: What should we do here? + fatalError("Invalid code: Tried to clear save points when empty") } + case .clearThrough: + clearThrough(payload.addr) + case .peek: fatalError() diff --git a/Tests/RegexBuilderTests/RegexDSLTests.swift b/Tests/RegexBuilderTests/RegexDSLTests.swift index 7971b3a49..04f003c0b 100644 --- a/Tests/RegexBuilderTests/RegexDSLTests.swift +++ b/Tests/RegexBuilderTests/RegexDSLTests.swift @@ -467,6 +467,29 @@ class RegexDSLTests: XCTestCase { XCTAssertEqual("ab12".firstMatch(of: octoDecimalRegex)!.output.1, 61904) } + func testLocal() throws { + try _testDSLCaptures( + ("aaaaa", nil), + matchType: Substring.self, ==) + { + Local { + OneOrMore("a") + } + "a" + } + + try _testDSLCaptures( + ("aa", "aa"), + ("aaa", nil), + matchType: Substring.self, ==) + { + Local { + OneOrMore("a", .reluctant) + } + "a" + } + } + func testAssertions() throws { try _testDSLCaptures( ("aaaaab", "aaaaab"), diff --git a/Tests/RegexTests/MatchTests.swift b/Tests/RegexTests/MatchTests.swift index 3c5ac96eb..35f8a9548 100644 --- a/Tests/RegexTests/MatchTests.swift +++ b/Tests/RegexTests/MatchTests.swift @@ -891,8 +891,7 @@ extension RegexTests { input: "Price: 100 dollars", match: nil) firstMatchTest( #"(?=\d+ dollars)\d+"#, - input: "Price: 100 dollars", match: "100", - xfail: true) // TODO + input: "Price: 100 dollars", match: "100") firstMatchTest( #"\d+(*pla: dollars)"#, @@ -917,6 +916,14 @@ extension RegexTests { #"\d+(*negative_lookahead: dollars)"#, input: "Price: 100 pesos", match: "100") + // More complex lookaheads + firstMatchTests( + #"(?=.*e)(?=.*o)(?!.*z)."#, + (input: "hello", match: "h"), + (input: "hzello", match: "e"), + (input: "hezllo", match: nil), + (input: "helloz", match: nil)) + firstMatchTest( #"(?<=USD)\d+"#, input: "Price: USD100", match: "100", xfail: true) firstMatchTest( @@ -1050,14 +1057,93 @@ extension RegexTests { firstMatchTest( #"(?:a|.b)c"#, input: "123abcacxyz", match: "abc") firstMatchTest( - #"(?>a|.b)c"#, input: "123abcacxyz", match: "ac", xfail: true) + #"(?>a|.b)c"#, input: "123abcacxyz", match: "ac") firstMatchTest( - "(*atomic:a|.b)c", input: "123abcacxyz", match: "ac", xfail: true) + "(*atomic:a|.b)c", input: "123abcacxyz", match: "ac") firstMatchTest( #"(?:a+)[a-z]c"#, input: "123aacacxyz", match: "aac") firstMatchTest( - #"(?>a+)[a-z]c"#, input: "123aacacxyz", match: "ac", xfail: true) + #"(?>a+)[a-z]c"#, input: "123aacacxyz", match: nil) + + // Atomicity should stay in the atomic group + firstMatchTest( + #"(?:(?>a)|.b)c"#, input: "123abcacxyz", match: "abc") + + // Quantifier behavior inside atomic groups + + // (?:a+?) matches as few 'a's as possible, after matching the first + // (?>a+?) always matches exactly one 'a' + firstMatchTests( + #"^(?:a+?)a$"#, + (input: "a", match: nil), + (input: "aa", match: "aa"), + (input: "aaa", match: "aaa")) + firstMatchTests( + #"^(?>a+?)a$"#, + (input: "a", match: nil), + (input: "aa", match: "aa"), + (input: "aaa", match: nil)) + + // (?:a?+) and (?>a?+) are equivalent: they match one 'a' if available + firstMatchTests( + #"^(?:a?+)a$"#, + (input: "a", match: nil), + xfail: true) + firstMatchTests( + #"^(?:a?+)a$"#, + (input: "aa", match: "aa"), + (input: "aaa", match: nil)) + firstMatchTests( + #"^(?>a?+)a$"#, + (input: "a", match: nil), + (input: "aa", match: "aa"), + (input: "aaa", match: nil)) + // Capture behavior in non-atomic vs atomic groups + firstMatchTests( + #"(\d+)\w+\1"#, + (input: "123x12", match: "123x12"), // `\w+` matches "3x" in this case + (input: "23x23", match: "23x23"), + (input: "123x23", match: "23x23")) + firstMatchTests( + #"(?>(\d+))\w+\1"#, + (input: "123x12", match: nil)) + firstMatchTests( + #"(?>(\d+))\w+\1"#, + (input: "23x23", match: "23x23"), + (input: "123x23", match: "23x23"), + xfail: true) + + // Backreferences in lookaheads + firstMatchTests( + #"^(?=.*(.)(.)\2\1).+$"#, + (input: "abbba", match: nil), + (input: "ABBA", match: "ABBA"), + (input: "defABBAdef", match: "defABBAdef")) + firstMatchTests( + #"^(?=.*(.)(.)\2\1).+\2$"#, + (input: "abbba", match: nil), + (input: "ABBA", match: nil), + (input: "defABBAdef", match: nil)) + // FIXME: Backreferences don't escape positive lookaheads + firstMatchTests( + #"^(?=.*(.)(.)\2\1).+\2$"#, + (input: "ABBAB", match: "ABBAB"), + (input: "defABBAdefB", match: "defABBAdefB"), + xfail: true) + + firstMatchTests( + #"^(?!.*(.)(.)\2\1).+$"#, + (input: "abbba", match: "abbba"), + (input: "ABBA", match: nil), + (input: "defABBAdef", match: nil)) + // Backreferences don't escape negative lookaheads; + // matching only proceeds when the lookahead fails + firstMatchTests( + #"^(?!.*(.)(.)\2\1).+\2$"#, + (input: "abbba", match: nil), + (input: "abbbab", match: nil), + (input: "ABBAB", match: nil)) // TODO: Test example where non-atomic is significant firstMatchTest( diff --git a/Tests/RegexTests/ParseTests.swift b/Tests/RegexTests/ParseTests.swift index 425f44f48..c658836a6 100644 --- a/Tests/RegexTests/ParseTests.swift +++ b/Tests/RegexTests/ParseTests.swift @@ -950,10 +950,10 @@ extension RegexTests { concat("a", nonCaptureReset("b"), "c"), throwsError: .unsupported) parseTest( #"a(?>b)c"#, - concat("a", atomicNonCapturing("b"), "c"), throwsError: .unsupported) + concat("a", atomicNonCapturing("b"), "c")) parseTest( "a(*atomic:b)c", - concat("a", atomicNonCapturing("b"), "c"), throwsError: .unsupported) + concat("a", atomicNonCapturing("b"), "c")) parseTest("a(?=b)c", concat("a", lookahead("b"), "c")) parseTest("a(*pla:b)c", concat("a", lookahead("b"), "c")) From f395a859b25975b5e2605a46e29cc9f4625af5fd Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Fri, 17 Jun 2022 08:18:57 -0500 Subject: [PATCH 06/24] Allow CustomConsuming types to match w/ zero width (#479) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Allow CustomConsuming types to match w/ zero width We previously asserted if a custom consuming type matches with zero width, but that isn't necessary or good. A custom type can implement a lookaround assertion or act as a tracer. * Rename Processor.advance(to:) to resume(at:) Since the given index doesn’t need to advance, this name is less misleading. --- .../_StringProcessing/Engine/Processor.swift | 18 ++++++---- Tests/RegexBuilderTests/RegexDSLTests.swift | 36 +++++++++++++++++++ 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/Sources/_StringProcessing/Engine/Processor.swift b/Sources/_StringProcessing/Engine/Processor.swift index 45053852d..edea57c87 100644 --- a/Sources/_StringProcessing/Engine/Processor.swift +++ b/Sources/_StringProcessing/Engine/Processor.swift @@ -117,11 +117,15 @@ extension Processor { return true } - mutating func advance(to nextIndex: Input.Index) { - assert(nextIndex >= bounds.lowerBound) - assert(nextIndex <= bounds.upperBound) - assert(nextIndex > currentPosition) - currentPosition = nextIndex + /// Continue matching at the specified index. + /// + /// - Precondition: `bounds.contains(index) || index == bounds.upperBound` + /// - Precondition: `index >= currentPosition` + mutating func resume(at index: Input.Index) { + assert(index >= bounds.lowerBound) + assert(index <= bounds.upperBound) + assert(index >= currentPosition) + currentPosition = index } func doPrint(_ s: String) { @@ -373,7 +377,7 @@ extension Processor { signalFailure() return } - advance(to: nextIndex) + resume(at: nextIndex) controller.step() case .assertBy: @@ -401,7 +405,7 @@ extension Processor { return } registers[valReg] = val - advance(to: nextIdx) + resume(at: nextIdx) controller.step() } catch { abort(error) diff --git a/Tests/RegexBuilderTests/RegexDSLTests.swift b/Tests/RegexBuilderTests/RegexDSLTests.swift index 04f003c0b..5b3914e3b 100644 --- a/Tests/RegexBuilderTests/RegexDSLTests.swift +++ b/Tests/RegexBuilderTests/RegexDSLTests.swift @@ -1015,6 +1015,42 @@ class RegexDSLTests: XCTestCase { XCTAssertEqual(str.wholeMatch(of: parser)?.1, version) } } + + func testZeroWidthConsumer() throws { + struct Trace: CustomConsumingRegexComponent { + typealias RegexOutput = Void + var label: String + init(_ label: String) { self.label = label } + + static var traceOutput = "" + + func consuming(_ input: String, startingAt index: String.Index, in bounds: Range) throws -> (upperBound: String.Index, output: Void)? { + print("Matching '\(label)'", to: &Self.traceOutput) + print(input, to: &Self.traceOutput) + let dist = input.distance(from: input.startIndex, to: index) + print(String(repeating: " ", count: dist) + "^", to: &Self.traceOutput) + return (index, ()) + } + } + + let regex = Regex { + OneOrMore(.word) + Trace("end of key") + ":" + Trace("start of value") + OneOrMore(.word) + } + XCTAssertNotNil("hello:goodbye".firstMatch(of: regex)) + XCTAssertEqual(Trace.traceOutput, """ + Matching 'end of key' + hello:goodbye + ^ + Matching 'start of value' + hello:goodbye + ^ + + """) + } } extension Unicode.Scalar { From 26a2dc994be1c657e43d4edd8ead178629ef229a Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Fri, 17 Jun 2022 08:44:32 -0500 Subject: [PATCH 07/24] Add regex-specific Matches and Ranges collections (#460) This prepares for adopting an opaque result type for matches(of:) and ranges(of:). The old, CollectionConsumer-based model moves index-by-index, and isn't aware of the regex's semantic level, which results in inaccurate results for regexes that match at a mid-character index. --- .../Algorithms/Algorithms/Ranges.swift | 42 ++++- .../Algorithms/Algorithms/Replace.swift | 45 ++--- .../Algorithms/Matching/Matches.swift | 172 +++++++++++++++--- .../RegexTests/AlgorithmsInternalsTests.swift | 35 ++++ Tests/RegexTests/AlgorithmsTests.swift | 19 ++ 5 files changed, 252 insertions(+), 61 deletions(-) diff --git a/Sources/_StringProcessing/Algorithms/Algorithms/Ranges.swift b/Sources/_StringProcessing/Algorithms/Algorithms/Ranges.swift index 36285d7cc..40732255c 100644 --- a/Sources/_StringProcessing/Algorithms/Algorithms/Ranges.swift +++ b/Sources/_StringProcessing/Algorithms/Algorithms/Ranges.swift @@ -226,17 +226,53 @@ extension BidirectionalCollection where Element: Comparable { // } } +@available(SwiftStdlib 5.7, *) +struct RegexRangesCollection { + let base: RegexMatchesCollection + + init(string: Substring, regex: Regex) { + self.base = RegexMatchesCollection(base: string, regex: regex) + } +} + +@available(SwiftStdlib 5.7, *) +extension RegexRangesCollection: Sequence { + struct Iterator: IteratorProtocol { + var matchesBase: RegexMatchesCollection.Iterator + + mutating func next() -> Range? { + matchesBase.next().map(\.range) + } + } + + func makeIterator() -> Iterator { + Iterator(matchesBase: base.makeIterator()) + } +} + +@available(SwiftStdlib 5.7, *) +extension RegexRangesCollection: Collection { + typealias Index = RegexMatchesCollection.Index + + var startIndex: Index { base.startIndex } + var endIndex: Index { base.endIndex } + func index(after i: Index) -> Index { base.index(after: i) } + subscript(position: Index) -> Range { base[position].range } +} + // MARK: Regex algorithms -extension BidirectionalCollection where SubSequence == Substring { +extension Collection where SubSequence == Substring { @available(SwiftStdlib 5.7, *) @_disfavoredOverload func _ranges( of regex: R - ) -> RangesCollection> { - _ranges(of: RegexConsumer(regex)) + ) -> RegexRangesCollection { + RegexRangesCollection(string: self[...], regex: regex.regex) } +} +extension BidirectionalCollection where SubSequence == Substring { @available(SwiftStdlib 5.7, *) func _rangesFromBack( of regex: R diff --git a/Sources/_StringProcessing/Algorithms/Algorithms/Replace.swift b/Sources/_StringProcessing/Algorithms/Algorithms/Replace.swift index ccc0962d5..a3f876b0e 100644 --- a/Sources/_StringProcessing/Algorithms/Algorithms/Replace.swift +++ b/Sources/_StringProcessing/Algorithms/Algorithms/Replace.swift @@ -12,21 +12,21 @@ // MARK: `CollectionSearcher` algorithms extension RangeReplaceableCollection { - func _replacing( - _ searcher: Searcher, + func _replacing( + _ ranges: Ranges, with replacement: Replacement, - subrange: Range, maxReplacements: Int = .max - ) -> Self where Searcher.Searched == SubSequence, + ) -> Self where Ranges.Element == Range, Replacement.Element == Element { precondition(maxReplacements >= 0) - var index = subrange.lowerBound var result = Self() - result.append(contentsOf: self[..( - _ searcher: Searcher, - with replacement: Replacement, - maxReplacements: Int = .max - ) -> Self where Searcher.Searched == SubSequence, - Replacement.Element == Element - { - _replacing( - searcher, - with: replacement, - subrange: startIndex..( - _ searcher: Searcher, + _ ranges: Ranges, with replacement: Replacement, maxReplacements: Int = .max - ) where Searcher.Searched == SubSequence, Replacement.Element == Element { + ) where Ranges.Element == Range, Replacement.Element == Element { self = _replacing( - searcher, + ranges, with: replacement, maxReplacements: maxReplacements) } @@ -85,9 +71,8 @@ extension RangeReplaceableCollection where Element: Equatable { maxReplacements: Int = .max ) -> Self where C.Element == Element, Replacement.Element == Element { _replacing( - ZSearcher(pattern: Array(other), by: ==), + self[subrange]._ranges(of: other), with: replacement, - subrange: subrange, maxReplacements: maxReplacements) } @@ -143,9 +128,8 @@ extension RangeReplaceableCollection maxReplacements: Int = .max ) -> Self where C.Element == Element, Replacement.Element == Element { _replacing( - PatternOrEmpty(searcher: TwoWaySearcher(pattern: Array(other))), + self[subrange]._ranges(of: other), with: replacement, - subrange: subrange, maxReplacements: maxReplacements) } @@ -195,9 +179,8 @@ extension RangeReplaceableCollection where SubSequence == Substring { maxReplacements: Int = .max ) -> Self where Replacement.Element == Element { _replacing( - RegexConsumer(regex), + self[subrange]._ranges(of: regex), with: replacement, - subrange: subrange, maxReplacements: maxReplacements) } diff --git a/Sources/_StringProcessing/Algorithms/Matching/Matches.swift b/Sources/_StringProcessing/Algorithms/Matching/Matches.swift index a7cd17779..094d3dfdd 100644 --- a/Sources/_StringProcessing/Algorithms/Matching/Matches.swift +++ b/Sources/_StringProcessing/Algorithms/Matching/Matches.swift @@ -183,13 +183,155 @@ extension BidirectionalCollection { // MARK: Regex algorithms +@available(SwiftStdlib 5.7, *) +struct RegexMatchesCollection { + let input: Substring + let regex: Regex + let startIndex: Index + + init(base: Substring, regex: Regex) { + self.input = base + self.regex = regex + self.startIndex = base.firstMatch(of: regex).map(Index.match) ?? .end + } +} + +@available(SwiftStdlib 5.7, *) +extension RegexMatchesCollection: Sequence { + /// Returns the index to start searching for the next match after `match`. + fileprivate func searchIndex(after match: Regex.Match) -> String.Index? { + if !match.range.isEmpty { + return match.range.upperBound + } + + // If the last match was an empty match, advance by one position and + // run again, unless at the end of `input`. + if match.range.lowerBound == input.endIndex { + return nil + } + + switch regex.initialOptions.semanticLevel { + case .graphemeCluster: + return input.index(after: match.range.upperBound) + case .unicodeScalar: + return input.unicodeScalars.index(after: match.range.upperBound) + } + } + + struct Iterator: IteratorProtocol { + let base: RegexMatchesCollection + + // Because `RegexMatchesCollection` eagerly computes the first match for + // its `startIndex`, the iterator can use that match for its initial + // iteration. For subsequent calls to `next()`, this value is `false`, and + // `nextStart` is used to search for the next match. + var initialIteration = true + var nextStart: String.Index? + + init(_ matches: RegexMatchesCollection) { + self.base = matches + self.nextStart = base.startIndex.match.flatMap(base.searchIndex(after:)) + } + + mutating func next() -> Regex.Match? { + // Initial case with pre-computed first match + if initialIteration { + initialIteration = false + return base.startIndex.match + } + + // `nextStart` is `nil` when iteration has completed + guard let start = nextStart else { + return nil + } + + // Otherwise, find the next match (if any) and compute `nextStart` + let match = try? base.regex.firstMatch(in: base.input[start...]) + nextStart = match.flatMap(base.searchIndex(after:)) + return match + } + } + + func makeIterator() -> Iterator { + Iterator(self) + } +} + +@available(SwiftStdlib 5.7, *) +extension RegexMatchesCollection: Collection { + enum Index: Comparable { + case match(Regex.Match) + case end + + var match: Regex.Match? { + switch self { + case .match(let match): return match + case .end: return nil + } + } + + static func == (lhs: Self, rhs: Self) -> Bool { + switch (lhs, rhs) { + case (.match(let lhs), .match(let rhs)): + return lhs.range == rhs.range + case (.end, .end): + return true + case (.end, .match), (.match, .end): + return false + } + } + + static func < (lhs: Self, rhs: Self) -> Bool { + switch (lhs, rhs) { + case (.match(let lhs), .match(let rhs)): + // This implementation uses a tuple comparison so that an empty + // range `i.. Index { + guard let currentMatch = i.match else { + fatalError("Can't advance past the 'endIndex' of a match collection.") + } + + guard + let start = searchIndex(after: currentMatch), + let nextMatch = try? regex.firstMatch(in: input[start...]) + else { + return .end + } + return Index.match(nextMatch) + } + + subscript(position: Index) -> Regex.Match { + guard let match = position.match else { + fatalError("Can't subscript the 'endIndex' of a match collection.") + } + return match + } +} + extension BidirectionalCollection where SubSequence == Substring { @available(SwiftStdlib 5.7, *) @_disfavoredOverload func _matches( of regex: R - ) -> MatchesCollection> { - _matches(of: RegexConsumer(regex)) + ) -> RegexMatchesCollection { + RegexMatchesCollection(base: self[...], regex: regex.regex) } @available(SwiftStdlib 5.7, *) @@ -207,30 +349,6 @@ extension BidirectionalCollection where SubSequence == Substring { public func matches( of r: some RegexComponent ) -> [Regex.Match] { - let slice = self[...] - var start = self.startIndex - let end = self.endIndex - let regex = r.regex - - var result = [Regex.Match]() - while start <= end { - guard let match = try? regex._firstMatch( - slice.base, in: start.. = matches + + XCTAssertEqual(matches.map(\.output), expected) + + let i = matches.index(matches.startIndex, offsetBy: 3) + XCTAssertEqual(matches[i].output, expected[3]) + let j = matches.index(i, offsetBy: 5) + XCTAssertEqual(j, matches.endIndex) + + var index = matches.startIndex + while index < matches.endIndex { + XCTAssertEqual( + matches[index].output, + expected[matches.distance(from: matches.startIndex, to: index)]) + matches.formIndex(after: &index) + } + } } diff --git a/Tests/RegexTests/AlgorithmsTests.swift b/Tests/RegexTests/AlgorithmsTests.swift index 1a5bc34df..175746f71 100644 --- a/Tests/RegexTests/AlgorithmsTests.swift +++ b/Tests/RegexTests/AlgorithmsTests.swift @@ -498,6 +498,25 @@ class AlgorithmTests: XCTestCase { s2.ranges(of: try Regex("a*?")).map(s2.offsets(of:)), [0..<0, 1..<1, 2..<2]) } + func testUnicodeScalarSemantics() throws { + let regex = try Regex(#"."#, as: Substring.self).matchingSemantics(.unicodeScalar) + let emptyRegex = try Regex(#"z?"#, as: Substring.self).matchingSemantics(.unicodeScalar) + + XCTAssertEqual("".matches(of: regex).map(\.output), []) + XCTAssertEqual("Café".matches(of: regex).map(\.output), ["C", "a", "f", "é"]) + XCTAssertEqual("Cafe\u{301}".matches(of: regex).map(\.output), ["C", "a", "f", "e", "\u{301}"]) + XCTAssertEqual("Cafe\u{301}".matches(of: emptyRegex).count, 6) + + XCTAssertEqual("Café".ranges(of: regex).count, 4) + XCTAssertEqual("Cafe\u{301}".ranges(of: regex).count, 5) + XCTAssertEqual("Cafe\u{301}".ranges(of: emptyRegex).count, 6) + + XCTAssertEqual("Café".replacing(regex, with: "-"), "----") + XCTAssertEqual("Cafe\u{301}".replacing(regex, with: "-"), "-----") + XCTAssertEqual("Café".replacing(emptyRegex, with: "-"), "-C-a-f-é-") + XCTAssertEqual("Cafe\u{301}".replacing(emptyRegex, with: "-"), "-C-a-f-e-\u{301}-") + } + func testSwitches() { switch "abcde" { case try! Regex("a.*f"): From b4f12bb47d003bec03f533caab39838f902b0dc2 Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Fri, 17 Jun 2022 10:50:35 -0600 Subject: [PATCH 08/24] Remove linear factor from Engine's consume (#494) 20x perf speedup in the "BasicBacktrack" benchmarks. --- Sources/RegexBenchmark/Inputs/CSS.swift | 1652 ++++++++++++++++ Sources/RegexBenchmark/Suite/CssRegex.swift | 1654 +---------------- .../_StringProcessing/Engine/Processor.swift | 7 +- 3 files changed, 1659 insertions(+), 1654 deletions(-) create mode 100644 Sources/RegexBenchmark/Inputs/CSS.swift diff --git a/Sources/RegexBenchmark/Inputs/CSS.swift b/Sources/RegexBenchmark/Inputs/CSS.swift new file mode 100644 index 000000000..ad8cf89b2 --- /dev/null +++ b/Sources/RegexBenchmark/Inputs/CSS.swift @@ -0,0 +1,1652 @@ +enum Inputs {} + +extension Inputs { + static let swiftOrgCSS = """ +html { + font-size: 100%; + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust:100% +} + +body { + margin: 0; + padding: 0; + background-color: var(--color-fill); + color:var(--color-text) +} + +ul, ol, li, dl, dt, dd, h1, h2, h3, h4, h5, h6, hgroup, p, blockquote, figure, form, fieldset, input, legend, pre, abbr { + margin: 0; + padding:0 +} + +pre, code, address, caption, th, figcaption { + font-size: 1em; + font-weight: normal; + font-style:normal +} + +fieldset, iframe, img { + width: 100%; + border:none +} + +caption, th { + text-align:left +} + +table { + border-collapse: collapse; + border-spacing:0 +} + +article, aside, footer, header, nav, main, section, summary, details, hgroup, figure, figcaption { + display:block +} + +audio, canvas, video, progress { + display: inline-block; + vertical-align:baseline +} + +button { + font: inherit; + vertical-align:middle +} + +nav a:link, nav a:visited, nav a:hover, nav a:active { + text-decoration:none +} + +:root { + --border-radius: 4px; + --content-margin-bottom: 1em +} + +body { + color-scheme: light dark; + --logo-reference: url('/assets/images/swift.svg'); + --menu-icon: url('/assets/images/icon-menu.svg'); + --menu-icon-close: url('/assets/images/icon-close.svg'); + --color-nav-background: var(--color-fill-secondary); + --color-nav-rule: rgb(230, 230, 230); + --color-active-menu-group: #2a2a2a; + --color-fill: #fff; + --color-fill-secondary: #f7f7f7; + --color-fill-tertiary: #f0f0f0; + --color-fill-quaternary: #282828; + --color-fill-blue: blue; + --color-fill-gray: #ccc; + --color-fill-gray-secondary: #f5f5f5; + --color-fill-green-secondary: #f0fff0; + --color-fill-orange-secondary: #fffaf6; + --color-fill-red-secondary: #fff0f5; + --color-figure-blue: #36f; + --color-figure-gray: #000; + --color-figure-gray-secondary: #666; + --color-figure-gray-secondary-alt: #666; + --color-figure-gray-tertiary: #666; + --color-figure-green: green; + --color-figure-light-gray: #666; + --color-figure-orange: #c30; + --color-figure-red: red; + --color-tutorials-teal: #000; + --color-article-background: var(--color-fill-tertiary); + --color-article-body-background: var(--color-fill); + --color-aside-deprecated: var(--color-figure-gray); + --color-aside-deprecated-background: var(--color-fill-orange-secondary); + --color-aside-deprecated-border: var(--color-figure-orange); + --color-aside-experiment: var(--color-figure-gray); + --color-aside-experiment-background: var(--color-fill-gray-secondary); + --color-aside-experiment-border: var(--color-figure-light-gray); + --color-aside-important: var(--color-figure-gray); + --color-aside-important-background: var(--color-fill-gray-secondary); + --color-aside-important-border: var(--color-figure-light-gray); + --color-aside-note: var(--color-figure-gray); + --color-aside-note-background: var(--color-fill-gray-secondary); + --color-aside-note-border: var(--color-figure-light-gray); + --color-aside-tip: var(--color-figure-gray); + --color-aside-tip-background: var(--color-fill-gray-secondary); + --color-aside-tip-border: var(--color-figure-light-gray); + --color-aside-warning: var(--color-figure-gray); + --color-aside-warning-background: var(--color-fill-red-secondary); + --color-aside-warning-border: var(--color-figure-red); + --color-badge-default: var(--color-figure-light-gray); + --color-badge-beta: var(--color-figure-gray-tertiary); + --color-badge-deprecated: var(--color-figure-orange); + --color-badge-dark-default: #b0b0b0; + --color-badge-dark-beta: #b0b0b0; + --color-badge-dark-deprecated: #f60; + --color-button-background: var(--color-fill-blue); + --color-button-background-active: #36f; + --color-button-background-hover: var(--color-figure-blue); + --color-button-text: #fff; + --color-call-to-action-background: var(--color-fill-secondary); + --color-changes-added: var(--color-figure-light-gray); + --color-changes-added-hover: var(--color-figure-light-gray); + --color-changes-deprecated: var(--color-figure-light-gray); + --color-changes-deprecated-hover: var(--color-figure-light-gray); + --color-changes-modified: var(--color-figure-light-gray); + --color-changes-modified-hover: var(--color-figure-light-gray); + --color-changes-modified-previous-background: var(--color-fill-gray-secondary); + --color-code-background: var(--color-fill-secondary); + --color-code-collapsible-background: var(--color-fill-tertiary); + --color-code-collapsible-text: var(--color-figure-gray-secondary-alt); + --color-code-line-highlight: rgba(51, 102, 255, 0.08); + --color-code-line-highlight-border: var(--color-figure-blue); + --color-code-plain: var(--color-figure-gray); + --color-content-table-content-color: var(--color-fill-secondary); + --color-dropdown-background: rgba(255, 255, 255, 0.8); + --color-dropdown-border: #ccc; + --color-dropdown-option-text: #666; + --color-dropdown-text: #000; + --color-dropdown-dark-background: rgba(255, 255, 255, 0.1); + --color-dropdown-dark-border: rgba(240, 240, 240, 0.2); + --color-dropdown-dark-option-text: #ccc; + --color-dropdown-dark-text: #fff; + --color-eyebrow: var(--color-figure-gray-secondary); + --color-focus-border-color: var(--color-fill-blue); + --color-focus-color: rgba(0, 125, 250, 0.6); + --color-form-error: var(--color-figure-red); + --color-form-error-background: var(--color-fill-red-secondary); + --color-form-valid: var(--color-figure-green); + --color-form-valid-background: var(--color-fill-green-secondary); + --color-generic-modal-background: var(--color-fill); + --color-grid: var(--color-fill-gray); + --color-header-text: var(--color-figure-gray); + --color-hero-eyebrow: #ccc; + --color-link: var(--color-figure-blue); + --color-loading-placeholder-background: var(--color-fill); + --color-nav-color: #666; + --color-nav-current-link: rgba(0, 0, 0, 0.6); + --color-nav-expanded: #fff; + --color-nav-hierarchy-collapse-background: #f0f0f0; + --color-nav-hierarchy-collapse-borders: #ccc; + --color-nav-hierarchy-item-borders: #ccc; + --color-nav-keyline: rgba(0, 0, 0, 0.2); + --color-nav-link-color: #000; + --color-nav-link-color-hover: #36f; + --color-nav-outlines: #ccc; + --color-nav-solid-background: #fff; + --color-nav-sticking-expanded-keyline: rgba(0, 0, 0, 0.1); + --color-nav-stuck: rgba(255, 255, 255, 0.9); + --color-nav-uiblur-expanded: rgba(255, 255, 255, 0.9); + --color-nav-uiblur-stuck: rgba(255, 255, 255, 0.7); + --color-nav-root-subhead: var(--color-tutorials-teal); + --color-nav-dark-border-top-color: rgba(255, 255, 255, 0.4); + --color-nav-dark-color: #b0b0b0; + --color-nav-dark-current-link: rgba(255, 255, 255, 0.6); + --color-nav-dark-expanded: #2a2a2a; + --color-nav-dark-hierarchy-collapse-background: #424242; + --color-nav-dark-hierarchy-collapse-borders: #666; + --color-nav-dark-hierarchy-item-borders: #424242; + --color-nav-dark-keyline: rgba(66, 66, 66, 0.95); + --color-nav-dark-link-color: #fff; + --color-nav-dark-link-color-hover: #09f; + --color-nav-dark-outlines: #575757; + --color-nav-dark-rule: #575757; + --color-nav-dark-solid-background: #000; + --color-nav-dark-sticking-expanded-keyline: rgba(66, 66, 66, 0.7); + --color-nav-dark-stuck: rgba(42, 42, 42, 0.9); + --color-nav-dark-uiblur-expanded: rgba(42, 42, 42, 0.9); + --color-nav-dark-uiblur-stuck: rgba(42, 42, 42, 0.7); + --color-nav-dark-root-subhead: #fff; + --color-runtime-preview-background: var(--color-fill-tertiary); + --color-runtime-preview-disabled-text: rgba(102, 102, 102, 0.6); + --color-runtime-preview-text: var(--color-figure-gray-secondary); + --color-secondary-label: var(--color-figure-gray-secondary); + --color-step-background: var(--color-fill-secondary); + --color-step-caption: var(--color-figure-gray-secondary); + --color-step-focused: var(--color-figure-light-gray); + --color-step-text: var(--color-figure-gray-secondary); + --color-svg-icon: #666; + --color-syntax-attributes: rgb(148, 113, 0); + --color-syntax-characters: rgb(39, 42, 216); + --color-syntax-comments: rgb(112, 127, 140); + --color-syntax-documentation-markup: rgb(80, 99, 117); + --color-syntax-documentation-markup-keywords: rgb(80, 99, 117); + --color-syntax-heading: rgb(186, 45, 162); + --color-syntax-keywords: rgb(173, 61, 164); + --color-syntax-marks: rgb(0, 0, 0); + --color-syntax-numbers: rgb(39, 42, 216); + --color-syntax-other-class-names: rgb(112, 61, 170); + --color-syntax-other-constants: rgb(75, 33, 176); + --color-syntax-other-declarations: rgb(4, 124, 176); + --color-syntax-other-function-and-method-names: rgb(75, 33, 176); + --color-syntax-other-instance-variables-and-globals: rgb(112, 61, 170); + --color-syntax-other-preprocessor-macros: rgb(120, 73, 42); + --color-syntax-other-type-names: rgb(112, 61, 170); + --color-syntax-param-internal-name: rgb(64, 64, 64); + --color-syntax-plain-text: rgb(0, 0, 0); + --color-syntax-preprocessor-statements: rgb(120, 73, 42); + --color-syntax-project-class-names: rgb(62, 128, 135); + --color-syntax-project-constants: rgb(45, 100, 105); + --color-syntax-project-function-and-method-names: rgb(45, 100, 105); + --color-syntax-project-instance-variables-and-globals: rgb(62, 128, 135); + --color-syntax-project-preprocessor-macros: rgb(120, 73, 42); + --color-syntax-project-type-names: rgb(62, 128, 135); + --color-syntax-strings: rgb(209, 47, 27); + --color-syntax-type-declarations: rgb(3, 99, 140); + --color-syntax-urls: rgb(19, 55, 255); + --color-tabnav-item-border-color: var(--color-fill-gray); + --color-text: var(--color-figure-gray); + --color-text-background: var(--color-fill); + --color-tutorial-assessments-background: var(--color-fill-secondary); + --color-tutorial-background: var(--color-fill); + --color-tutorial-navbar-dropdown-background: var(--color-fill); + --color-tutorial-navbar-dropdown-border: var(--color-fill-gray); + --color-tutorial-quiz-border-active: var(--color-figure-blue); + --color-tutorials-overview-background: #161616; + --color-tutorials-overview-content: #fff; + --color-tutorials-overview-content-alt: #fff; + --color-tutorials-overview-eyebrow: #ccc; + --color-tutorials-overview-icon: #b0b0b0; + --color-tutorials-overview-link: #09f; + --color-tutorials-overview-navigation-link: #ccc; + --color-tutorials-overview-navigation-link-active: #fff; + --color-tutorials-overview-navigation-link-hover: #fff; + --color-tutorial-hero-text: #fff; + --color-tutorial-hero-background: #000 +} + +body[data-color-scheme="light"] { + color-scheme: light +} + +body[data-color-scheme="dark"] { + color-scheme:dark +} + +@media screen { + body[data-color-scheme="dark"] { + --logo-reference: url('/assets/images/swift~dark.svg'); + --menu-icon: url('/assets/images/icon-menu~dark.svg'); + --menu-icon-close: url('/assets/images/icon-close~dark.svg'); + --color-nav-background: var(--color-fill-tertiary); + --color-nav-rule: #424242; + --color-active-menu-group: #f0f0f0; + --color-fill: #000; + --color-fill-secondary: #161616; + --color-fill-tertiary: #2a2a2a; + --color-fill-blue: #06f; + --color-fill-gray: #575757; + --color-fill-gray-secondary: #222; + --color-fill-green-secondary: #030; + --color-fill-orange-secondary: #472400; + --color-fill-red-secondary: #300; + --color-figure-blue: #09f; + --color-figure-gray: #fff; + --color-figure-gray-secondary: #ccc; + --color-figure-gray-secondary-alt: #b0b0b0; + --color-figure-gray-tertiary: #b0b0b0; + --color-figure-green: #090; + --color-figure-light-gray: #b0b0b0; + --color-figure-orange: #f60; + --color-figure-red: #f33; + --color-tutorials-teal: #fff; + --color-article-body-background: rgb(17, 17, 17); + --color-button-background-active: #06f; + --color-code-line-highlight: rgba(0, 153, 255, 0.08); + --color-dropdown-background: var(--color-dropdown-dark-background); + --color-dropdown-border: var(--color-dropdown-dark-border); + --color-dropdown-option-text: var(--color-dropdown-dark-option-text); + --color-dropdown-text: var(--color-dropdown-dark-text); + --color-nav-color: var(--color-nav-dark-color); + --color-nav-current-link: var(--color-nav-dark-current-link); + --color-nav-expanded: var(--color-nav-dark-expanded); + --color-nav-hierarchy-collapse-background: var(--color-nav-dark-hierarchy-collapse-background); + --color-nav-hierarchy-collapse-borders: var(--color-nav-dark-hierarchy-collapse-borders); + --color-nav-hierarchy-item-borders: var(--color-nav-dark-hierarchy-item-borders); + --color-nav-keyline: var(--color-nav-dark-keyline); + --color-nav-link-color: var(--color-nav-dark-link-color); + --color-nav-link-color-hover: var(--color-nav-dark-link-color-hover); + --color-nav-outlines: var(--color-nav-dark-outlines); + --color-nav-solid-background: var(--color-nav-dark-solid-background); + --color-nav-sticking-expanded-keyline: var(--color-nav-dark-sticking-expanded-keyline); + --color-nav-stuck: var(--color-nav-dark-stuck); + --color-nav-uiblur-expanded: var(--color-nav-dark-uiblur-expanded); + --color-nav-uiblur-stuck: var(--color-nav-dark-uiblur-stuck); + --color-runtime-preview-disabled-text: rgba(204, 204, 204, 0.6); + --color-syntax-attributes: rgb(204, 151, 104); + --color-syntax-characters: rgb(217, 201, 124); + --color-syntax-comments: rgb(127, 140, 152); + --color-syntax-documentation-markup: rgb(127, 140, 152); + --color-syntax-documentation-markup-keywords: rgb(163, 177, 191); + --color-syntax-keywords: rgb(255, 122, 178); + --color-syntax-marks: rgb(255, 255, 255); + --color-syntax-numbers: rgb(217, 201, 124); + --color-syntax-other-class-names: rgb(218, 186, 255); + --color-syntax-other-constants: rgb(167, 235, 221); + --color-syntax-other-declarations: rgb(78, 176, 204); + --color-syntax-other-function-and-method-names: rgb(178, 129, 235); + --color-syntax-other-instance-variables-and-globals: rgb(178, 129, 235); + --color-syntax-other-preprocessor-macros: rgb(255, 161, 79); + --color-syntax-other-type-names: rgb(218, 186, 255); + --color-syntax-param-internal-name: rgb(191, 191, 191); + --color-syntax-plain-text: rgb(255, 255, 255); + --color-syntax-preprocessor-statements: rgb(255, 161, 79); + --color-syntax-project-class-names: rgb(172, 242, 228); + --color-syntax-project-constants: rgb(120, 194, 179); + --color-syntax-project-function-and-method-names: rgb(120, 194, 179); + --color-syntax-project-instance-variables-and-globals: rgb(120, 194, 179); + --color-syntax-project-preprocessor-macros: rgb(255, 161, 79); + --color-syntax-project-type-names: rgb(172, 242, 228); + --color-syntax-strings: rgb(255, 129, 112); + --color-syntax-type-declarations: rgb(107, 223, 255); + --color-syntax-urls: rgb(102, 153, 255); + --color-tutorial-background: var(--color-fill-tertiary) + } +} + +.highlight { + background:var(--color-code-background) +} + +.highlight .c, .highlight .cm, .highlight .cp, .highlight .c1, .highlight .cs { + color:var(--color-syntax-comments) +} + +.highlight .k, .highlight .kc, .highlight .kd, .highlight .kp, .highlight .kr, .highlight .kt .nb { + color:var(--color-syntax-keywords) +} + +.highlight .nv, .highlight .nf { + color:color(--color-syntax-project-constants) +} + +.highlight .s, .highlight .sb, .highlight .sc, .highlight .sd, .highlight .s2, .highlight .se, .highlight .sh, .highlight .si, .highlight .s1, .highlight .sx { + color:var(--color-syntax-strings) +} + +.highlight .na { + color:var(--color-syntax-attributes) +} + +.highlight .nc, .highlight .ni, .highlight .no, .highlight .vc, .highlight .vg, .highlight .vi { + color:var(--color-syntax-other-type-names) +} + +.highlight .err, .highlight .gr, .highlight .gt, .highlight .ne { + color:var(--color-syntax-strings) +} + +.highlight .m, .highlight .mf, .highlight .mh, .highlight .mi, .highlight .il, .highlight .mo { + color:var(--color-syntax-numbers) +} + +.highlight .o, .highlight .ow, .highlight .gs { + font-weight:bold +} + +.highlight .ge { + font-style:italic +} + +.highlight .nt { + color:var(--color-syntax-characters) +} + +.highlight .gd, .highlight .gd .x { + color: var(--color-syntax-plain-text); + background-color:var(--color-fill-red-secondary) +} + +.highlight .gi, .highlight .gi .x { + color: var(--color-syntax-plain-text); + background-color:color(--color-fill-green-secondary) +} + +.highlight .gh, .highlight .bp, .highlight .go, .highlight .gp, .highlight .gu, .highlight .w { + color:var(--color-syntax-comments) +} + +.highlight .nn { + color:var(--color-syntax-other-declarations) +} + +.highlight .sr { + color:var(--color-figure-green) +} + +.highlight .ss { + color:var(--color-syntax-heading) +} +.language-console { + color:var(--color-syntax-plain-text) +} + +*, * :before, * :after { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing:border-box +} + +html, body { + height:100% +} + +body { + font-family: -apple-system, BlinkMacSystemFont, "SF Hello", "Helvetica Neue", Helvetica, Arial, Verdana, sans-serif; + font-size: 18px; + line-height: 1.5; + background-color: var(--color-fill); + color: var(--color-text); + font-weight:300 +} + +body pre, body code { + font-family: "SF Mono", Menlo, Consolas, Monaco, "Courier New", monospace, serif +} + +a:link { + color: var(--color-link); + text-decoration:none +} + +a:visited { + color:var(--color-link) +} + +a:active { + color:var(--color-link) +} + +a:hover { + color: var(--color-link); + text-decoration:underline +} + +p { + margin-bottom:1em +} + +h1 { + margin-bottom: 0.5em; + font-size: 3em; + font-weight: 300; + line-height:1 +} + +h1.active + .main-nav { + border-top:1px solid var(--color-active-menu-group) +} + +h2 { + margin-bottom: 0.5em; + font-size: 2.5em; + font-weight: 300; + line-height:1 +} + +h3 { + margin-bottom: 0.5em; + font-size: 1.5em; + font-weight: 300; + line-height:1 +} + +h4 { + margin-bottom: 0.5em; + font-size: 1.25em; + font-weight: 300; + line-height:1.2 +} + +h5 { + margin-bottom: 0.5em; + font-size: 1.175em; + font-weight: 500; + line-height:1.4 +} + +h6 { + margin-bottom: 0.5em; + font-size: 1em; + font-weight: 700; + line-height:1.5 +} + +h1, h2, h3, h4, h5, h6 { + color:var(--color-header-text) +} + +div.highlighter-rouge { + margin-left:13px +} + +pre { + font-size: 14px; + line-height: 1.6em; + border-left: 5px solid var(--color-code-line-highlight-border); + margin: 0.5em 0 1.5em 10px; + padding: 4px 0 2px 10px; + overflow:scroll +} + +a > code, p > code, li > code, dd > code, blockquote > code, td > code { + padding: 0; + margin: 0; + font-size: 16px; + white-space: nowrap; + background-color:transparent +} + +p > code, li > code, dd > code, blockquote > code, td > code { + color:var(--color-code-plain) +} + +p > code { + white-space: pre-wrap; + word-break:break-word +} + +hr { + border: none; + border-top: 1px var(--color-dropdown-border) solid; + margin:2em 0 +} + +hr:last-child { + display:none +} + +details { + margin-bottom:2em +} + +details :first-child { + margin-top:1.5em +} + +cite { + display:block +} + +cite:before { + content: "— " +} + +#logo { + text-indent: -9999px; + height: 48px; + width: 100%; + margin-top: 20px; + margin-bottom: 0.5em; + padding-bottom:10px +} + +#logo a { + display: block; + width: 190px; + height: 48px; + background-image: var(--logo-reference); + background-repeat: no-repeat; + background-size: 190px 48px; + background-position-x: -8px +} + +nav[role="navigation"] { + width: 250px; + position: fixed; + overflow: scroll; + left: 0; + top: 0; + bottom: 0; + background: var(--color-nav-background); + color: var(--color-nav-color); + border-right: 1px solid var(--color-nav-rule); + padding: 20px 30px +} + +nav[role="navigation"] ul { + border-top: 1px solid var(--color-nav-rule); + font-weight: 400; + margin-bottom: 30px; + list-style: none +} + +nav[role="navigation"] ul ul { + list-style: none +} + +nav[role="navigation"] ul li { + border-bottom: 1px solid var(--color-nav-rule) +} + +nav[role="navigation"] ul li.active { + border-bottom: 1px solid var(--color-active-menu-group) +} + +nav[role="navigation"] ul li.active a { + font-weight: 700 +} + +nav[role="navigation"] ul li a:link { + color: var(--color-nav-link-color); + text-decoration: none; + text-transform: uppercase; + letter-spacing: 1px; + font-size: 12px; + display: block; + padding: 10px +} + +nav[role="navigation"] ul li a:visited { + color: var(--color-nav-link-color) +} + +nav[role="navigation"] ul li a:active { + font-weight: 700 +} + +nav[role="navigation"] ul li a:hover { + color: var(--color-link) +} + +nav[role="navigation"] ul li ul { + margin-bottom: 10px; + border-top: none +} + +nav[role="navigation"] ul li ul li { + border-bottom: none; + padding: 0.1em +} + +nav[role="navigation"] ul li ul li.active { + border-bottom: none +} + +nav[role="navigation"] ul li ul li.active a { + font-weight: 700 +} + +nav[role="navigation"] ul li ul a:link { + color: var(--color-nav-link-color); + text-decoration: none; + text-transform: none; + letter-spacing: 0; + font-size: 12px; + display: block; + margin-left: 15px; + padding: 0 0 3px; + border-bottom: none; + font-weight: 300 +} + +nav[role="navigation"] ul li ul a:hover { + color: var(--color-link) +} + +nav[role="navigation"] h2 { + font-size: 0.75em; + font-weight: 700; + text-transform: lowercase; + font-variant: small-caps; + color: var(--color-figure-gray-secondary-alt); + padding-bottom:0.5em +} + +main { + max-width: 798px; + min-width: 320px; + margin-left: 250px; + padding: 35px 30px 0; + min-height: 100%; + height: auto !important; + height: 100% +} + +footer[role="contentinfo"] { + background: var(--color-nav-background); + border-top: 1px solid var(--color-nav-rule); + color: var(--color-nav-color); + padding: 20px 30px; + margin-left: 250px; + min-height: 74px +} + +footer[role="contentinfo"] p { + font-size: 0.625em; + color: var(--color-nav-link-color); + line-height: 1em; + margin-bottom: 1em; + margin-bottom: var(--content-margin-bottom) +} + +footer[role="contentinfo"] p.privacy a { + color: var(--color-nav-link-color); + border-right: 1px solid var(--color-nav-rule); + margin-right: 6px; + padding-right: 8px +} + +footer[role="contentinfo"] p.privacy a:last-child { + border: none; + margin: 0; + padding: 0 +} + +footer[role="contentinfo"] p:last-child { + margin-bottom: 0 +} + +footer[role="contentinfo"] aside { + position: relative; + width: 100%; + max-width: 700px +} + +footer[role="contentinfo"] aside i { + width: 16px; + height: 16px; + background-repeat: no-repeat; + background-size: 16px; + display: block; + margin-left: 1em; + float: right +} + +footer[role="contentinfo"] aside i.twitter { + background-image: url("/assets/images/icon-twitter.svg") +} + +footer[role="contentinfo"] aside i.feed { + background-image: url("/assets/images/icon-feed.svg") +} + +article:first-of-type { + padding-bottom:36px +} + +article h2 { + padding-top:1.1em +} + +article h3 { + padding-top:1em +} + +article h4 { + padding-top: 1em; + border-bottom: 1px var(--color-dropdown-border) solid; + padding-bottom:0.5em +} + +article h5 { + margin-top:1em +} + +article header { + width: 100%; + display: inline-block; + padding-bottom:2.5em +} + +article header h1 { + padding-bottom:0.125em +} + +article header .byline { + float: left; + font-size: 14px; + margin-bottom:1em +} + +article header .byline img { + width: 32px; + height: 32px; + border-radius: 50%; + border: 1px var(--color-fill-gray) solid; + position: absolute; + margin-right: 0.25em; + margin-top:-6px +} + +article header .byline span { + padding-left:42px +} + +article header .about { + float: none; + clear: both; + font-size: 14px; + font-weight: 400; + color: var(--color-figure-gray-tertiary); + border-left: 1px var(--color-figure-gray-tertiary) solid; + margin: 23px 3em 23px 0; + padding:4px 0 4px 10px +} + +article header time { + float: left; + text-transform: uppercase; + font-size: 14px; + font-weight: 400; + color: var(--color-figure-gray-tertiary); + margin-right: 3em; + margin-bottom:1em +} + +article header .tags { + display: block; + font-size: 12px; + font-weight: 400; + margin-top:0 +} + +article:not(:first-of-type) { + border-top: 1px solid var(--color-figure-gray-tertiary); + padding:36px 0 +} + +article blockquote { + border-left: 5px var(--color-fill-gray) solid; + margin: 0.5em 0 23px 1em; + padding: 4px 0 2px 10px; + color: var(--color-aside-note); + overflow-x:auto +} + +article blockquote p:last-child { + margin-bottom:0 +} + +article ul, article ol { + padding-left: 40px; + margin:1em 0 +} + +article ul ul, article ul ol, article ol ul, article ol ol { + margin:0 +} + +article ul { + list-style:disc +} + +article ul ul { + list-style:circle +} + +article ul ul ul { + list-style:square +} + +article ol { + list-style:decimal +} + +article dl { + margin:2em 0 1em 0 +} + +article dl:after { + content: ""; + display: table; + clear:both +} + +article dl dt { + float: left; + clear: right; + margin-right: 1em; + display: block; + width: 28%; + text-align:right +} + +article dl dd { + float: right; + width: 65%; + margin-bottom: 1em; + overflow:scroll +} + +article dl dd { + padding-bottom: 1em; + border-bottom:1px var(--color-dropdown-border) solid +} + +article table { + display: block; + overflow-x: auto; + width: max-content; + min-width: 68%; + max-width: 100%; + margin: 2em auto 3em auto; + border-collapse: separate; + border:1px var(--color-dropdown-border) solid +} + +article table th { + font-weight: 700; + text-align:center +} + +article table th, article table td { + width: 50%; + padding: 0.5em 1.5em; + border-bottom:1px var(--color-dropdown-border) solid +} + +article table th:not(:first-child), article table td:not(:first-child) { + border-left:1px var(--color-dropdown-border) solid +} + +article table tr:last-child td { + border-bottom:none +} + +article details { + margin-top: 0; + cursor:pointer +} + +article details summary { + display: list-item; + padding-bottom: 0.5em; + outline: none; + margin-top:0 +} + +article details summary:after { + content: "Expand"; + text-transform: lowercase; + font-variant: small-caps; + border-bottom:1px var(--color-fill-gray) dashed +} + +article details[open] summary:after { + content: "Collapse" +} + +article details[open] * :not(summary) { + cursor:auto +} + +article details.download { + margin-top: 0; + cursor:pointer +} + +article details.download table { + display:inline-table +} + +article details.download summary { + padding-bottom: 0.5em; + outline: none; + margin-top:0 +} + +article details.download summary:after { + content: none; + text-transform: lowercase; + font-variant: small-caps; + border-bottom:1px var(--color-fill-gray) dashed +} + +article details.download[open] summary:after { + content:none +} + +article details.download[open] * :not(summary) { + cursor:auto +} + +article > details { + margin-left:40px +} + +article .good pre, article pre.good { + background: var(--color-fill-green-secondary); + border-color:var(--color-figure-green) +} + +article .good pre:before, article pre.good:before { + content: "✅"; + float:right +} + +article .bad pre, article pre.bad { + background: var(--color-fill-red-secondary); + border-color:var(--color-figure-red) +} + +article .bad pre:before, article pre.bad:before { + content: "⛔️"; + float:right +} + +article .links ul { + list-style:none +} + +article .links ul ul { + list-style: disc; + margin-top:5px +} + +article .links a:after { + content: " ›" +} + +article .links .link-external:after, article .links-external a:after, article .link-external:after { + content: " ↗" +} + +article .links-download a:after { + content: " ⬇" +} + +article .links-list-nostyle ul { + padding-left:0 +} + +article .links-list-nostyle ul ul { + list-style:none +} + +article .links-sublevel p { + margin-bottom:0 +} + +article .links-sublevel ul { + margin-top: 0; + padding-left:40px +} + +article footer { + margin: 4em 0 0 0; + padding: 1.5em 0 1em 0; + border-top:1px var(--color-dropdown-border) solid +} + +article footer:after { + content: ""; + display: table; + clear: both +} + +article footer nav [rel="prev"] { + width: 45%; + float: left; + text-align: left +} + +article footer nav [rel="prev"]:before { + content: "← " +} + +article footer nav [rel="next"] { + width: 45%; + float: right; + text-align: right +} + +article footer nav [rel="next"]:after { + content: " →" +} + +.title a:link, .title a:visited { + color:var(--color-header-text) +} + +.alert, .danger, .warning, .info, .success { + border-width: 1px; + border-style: solid; + padding: 0.5em; + margin:0.5em 0 1.5em 0 +} + +.alert a, .danger a, .warning a, .info a, .success a { + word-break:break-word +} + +.alert p:first-child, .danger p:first-child, .warning p:first-child, .info p:first-child, .success p:first-child { + margin-top:0 +} + +.alert p:last-child, .danger p:last-child, .warning p:last-child, .info p:last-child, .success p:last-child { + margin-bottom:0 +} + +.alert code, .danger code, .warning code, .info code, .success code { + border: none; + background: transparent; + padding:0 +} + +code { + white-space:pre-line +} + +pre code { + white-space:inherit +} + +pre code .graphic { + font-size: 19px; + line-height:0 +} + +pre code .commentary, pre code .graphic { + font-family: "SF Hello", "Helvetica Neue", Helvetica, Arial, Verdana, sans-serif +} + +@supports (overflow: -webkit-marquee) and(justify-content: inherit) { + .alert:before, .danger:before, .warning:before, .info:before, .success:before { + font-size: 1em; + float: left; + clear: left; + padding-left: 0.125em; + width:2em + } + + .alert p, .danger p, .warning p, .info p, .success p { + padding-left:2em + } + + .success:before { + content: "✅" + } + + .info:before { + content: "ℹ️" + } + + .warning:before { + content: "⚠️" + } + + .danger:before { + content: "❗️" + } +} + +.success { + color: var(--color-aside-note); + border-color: var(--color-form-valid); + background-color:var(--color-form-valid-background) +} + +.info { + color: var(--color-aside-note); + border-color: var(--color-aside-note-border); + background-color:var(--color-aside-note-background) +} + +.warning { + color: var(--color-aside-deprecated); + border-color: var(--color-aside-deprecated-border); + background-color:var(--color-aside-deprecated-background) +} + +.danger { + color: var(--color-aside-warning); + border-color: var(--color-aside-warning-border); + background-color:var(--color-aside-warning-background) +} + +table.downloads { + width: 100%; + table-layout:fixed +} + +table.downloads th { + font-size:0.75em +} + +table.downloads .platform { + width:40% +} + +table.downloads .download { + width:60% +} + +table.downloads .download a.debug, table.downloads .download a.signature { + font-size: 0.7em; + display:block +} + +table.downloads .download a { + font-weight: 700; + font-size:1em +} + +table.downloads .download a:not([download]) { + font-weight:400 +} + +table.downloads .download a:not([download]):before { + content: "(" +} + +table.downloads .download a:not([download]):after { + content: ")" +} + +table.downloads .arch-tag { + width:60% +} + +table.downloads .arch-tag a.debug, table.downloads .arch-tag a.signature { + font-size: 0.7em; + display:block +} + +table.downloads .arch-tag a { + font-weight: 700; + font-size:1em +} + +table.downloads .arch-tag a:not([arch-tag]) { + font-weight:400 +} + +article input.detail[type=checkbox] { + visibility: hidden; + cursor: pointer; + height: 0; + width: 100%; + margin-bottom: 2em; + display: block; + font-size: inherit; + font-style: inherit; + font-weight: inherit; + font-family: inherit; + position: relative; + top:-.85rem +} + +article p + input.detail[type=checkbox] { + margin-top:auto +} + +article .screenonly { + display:none +} + +@media screen { + article .screenonly { + display:inherit + } + + article input.detail[type=checkbox]:before { + content: "▶ "; + visibility: visible; + font-size:80% + } + + article input.detail[type=checkbox]:after { + text-transform: lowercase; + font-variant: small-caps; + border-bottom: 1px var(--color-fill-gray) dashed; + color: var(--color-figure-gray-secondary); + content: "More detail"; + visibility:visible + } + + article input.detail[type=checkbox]:checked:before { + content: "▼ " + } + + article input.detail[type=checkbox]:checked:after { + content: "Less detail" + } + + article input.detail[type=checkbox] + .more { + transition:0.5s opacity ease, 0.5s max-height ease + } + + article input.detail[type=checkbox]:checked + .more { + visibility: visible; + max-height:1000rem + } + + article input.detail[type=checkbox]:not(:checked) + .more { + overflow: hidden; + max-height: 0px; + opacity:0 + } +} + +article .more > p:first-of-type { + margin-top:0 +} + +.color-scheme-toggle { + display: block; + outline: none; + --toggle-color-fill: var(--color-button-background); + font-size: 12px; + border: 1px solid var(--color-nav-link-color); + border-radius: var(--border-radius); + display: inline-flex; + padding: 1px; + margin-bottom:var(--content-margin-bottom) +} + +.color-scheme-toggle input { + position: absolute; + clip: rect(1px, 1px, 1px, 1px); + clip-path: inset(0px 0px 99.9% 99.9%); + overflow: hidden; + height: 1px; + width: 1px; + padding: 0; + border: 0; + appearance:none +} + +.color-scheme-toggle-label { + border: 1px solid transparent; + border-radius: var(--toggle-border-radius-inner, 2px); + color: var(--color-nav-link-color); + display: inline-block; + text-align: center; + padding: 1px 6px; + min-width: 42px; + box-sizing:border-box +} + +.color-scheme-toggle-label:hover { + cursor:pointer +} + +input:checked + .color-scheme-toggle-label { + background: var(--color-nav-link-color); + color: var(--color-nav-stuck) +} + +[role="contentinfo"] { + display: flex; + justify-content:space-between +} + +.visuallyhidden { + position: absolute; + clip: rect(1px, 1px, 1px, 1px); + clip-path: inset(0px 0px 99.9% 99.9%); + overflow: hidden; + height: 1px; + width: 1px; + padding: 0; + border:0 +} + +@media only screen and (max-width: 767px) { + nav[role="navigation"] { + width: 100%; + position: relative; + border-bottom: 1px solid var(--color-nav-rule); + border-right: none; + padding: 20px 30px; + overflow: hidden + } + + nav.open[role="navigation"] .list-items { + display: block + } + + nav[role="navigation"] .list-items { + padding-top: var(--content-margin-bottom); + display:none + } + + .menu-toggle { + content: ' '; + height: 20px; + width: 20px; + background-image: var(--menu-icon-close); + background-repeat: no-repeat; + background-position: center center; + cursor:pointer + } + + .menu-toggle.open { + background-image:var(--menu-icon) + } + + #logo { + margin: 0; + padding:0 + } + + #logo a { + margin:0 auto + } + + main { + max-width: 100%; + min-width: 320px; + margin-left: 0; + padding: 30px 30px 0 + } + + footer[role="contentinfo"] { + margin-left: 0; + flex-direction:column + } + + .footer-other { + display: flex; + justify-content: space-between; + margin-top:var(--content-margin-bottom) + } + + h1 { + font-size: 48px; + font-weight: 300; + line-height:1 + } + + h2 { + font-size: 40px; + font-weight: 300; + line-height:1.1 + } + + h3 { + font-size: 38px; + font-weight: 300; + line-height:1.1 + } + + h4 { + font-size: 36px; + font-weight: 300; + line-height:1.2 + } + + h5 { + font-size: 24px; + font-weight: 500; + line-height:1.4 + } + + h6 { + font-size: 18px; + font-weight: 700; + line-height:1.5 + } + + div.highlighter-rouge { + margin-left:0 + } + + article blockquote { + margin-left:0.5em + } + + table.downloads { + border:1px var(--color-dropdown-border) solid + } + + table.downloads, table.downloads thead, table.downloads tbody, table.downloads th, table.downloads td, table.downloads tr { + display:block !important + } + + table.downloads thead tr { + position: absolute; + top: -9999px; + left:-9999px + } + + table.downloads tr { + border:1px solid var(--color-dropdown-border) + } + + table.downloads td { + border-left: none !important; + border-right: none !important; + border-bottom: 1px solid var(--color-dropdown-border) !important; + position: relative; + padding-left: 35%; + width:100% !important + } + + table.downloads td:before { + position: absolute; + top: 0.5em; + left: 0.5em; + width: 27.5%; + padding-right: 10px; + white-space: nowrap; + text-align:right + } + + table.downloads td.platform:before { + content: "Platform" + } + + table.downloads td.download:before { + content: "Download"; + top:1em + } + + table.downloads td.date:before { + content: "Date" + } + + table.downloads td.toolchain:before { + content: "Toolchain"; + top:1em + } + + table.downloads td.github-tag:before { + content: "GitHub Tag" + } + + table.downloads td.docker-tag:before { + content: "Docker Tag" + } + + table.downloads td.arch-tag:before { + content: "Architecture" + } +} + +.nav-menu-container { + display: grid; + grid-template-columns: 1fr; + grid-template-rows: 1fr; + align-items:center +} + +.menu-item { + grid-area:1 / 1 / 1 / 1 +} + +.logo-container { + justify-self:center +} + +.menu-toggle { + justify-self:right +} + +@media only print { + html body { + background: white; + font-size: 12pt; + padding:0.5in + } + + html body * { + -webkit-print-color-adjust:exact + } + + a { + color: black !important; + text-decoration: underline !important + } + + a[href^="http:"]:after { + content: " (" attr(href) ") "; + color:#444 + } + + h1, h2, h3, h4, h5, h6, p, article > div, pre, table { + page-break-inside:avoid + } + + details:not([open]) { + visibility:visible + } + + details:not([open]) summary { + display:none !important + } + + details:not([open]) > *, details:not([open]) { + display:block + } + + .alert, .success, .info, .warning, .danger { + margin:1.5em 0 + } + + main { + width: auto; + padding: 0; + border: 0; + float: none !important; + color: black; + background: transparent; + margin: 0; + max-width: 100%; + min-height: 1in + } + + nav[role="navigation"] { + background: transparent; + border: none; + width: auto; + position: static; + padding: 0 + } + + nav[role="navigation"] h2, nav[role="navigation"] ul { + display: none + } + + nav[role="navigation"] #logo { + position: static; + margin-bottom: 1.5em + } + + nav[role="navigation"] #logo a { + background-position: -15px + } + + footer[role="contentinfo"] { + display: none + } +} + +/*# sourceMappingURL=application.css.map */ +""" +} diff --git a/Sources/RegexBenchmark/Suite/CssRegex.swift b/Sources/RegexBenchmark/Suite/CssRegex.swift index 71066d273..bc607154f 100644 --- a/Sources/RegexBenchmark/Suite/CssRegex.swift +++ b/Sources/RegexBenchmark/Suite/CssRegex.swift @@ -1,1670 +1,22 @@ import Foundation +import _StringProcessing extension BenchmarkRunner { mutating func addCSS() { let r = "--([a-zA-Z0-9_-]+)\\s*:\\s*(.*?):" - // sorry - let css = """ - html { - font-size: 100%; - -ms-text-size-adjust: 100%; - -webkit-text-size-adjust:100% - } - - body { - margin: 0; - padding: 0; - background-color: var(--color-fill); - color:var(--color-text) - } - - ul, ol, li, dl, dt, dd, h1, h2, h3, h4, h5, h6, hgroup, p, blockquote, figure, form, fieldset, input, legend, pre, abbr { - margin: 0; - padding:0 - } - - pre, code, address, caption, th, figcaption { - font-size: 1em; - font-weight: normal; - font-style:normal - } - - fieldset, iframe, img { - width: 100%; - border:none - } - - caption, th { - text-align:left - } - - table { - border-collapse: collapse; - border-spacing:0 - } - - article, aside, footer, header, nav, main, section, summary, details, hgroup, figure, figcaption { - display:block - } - - audio, canvas, video, progress { - display: inline-block; - vertical-align:baseline - } - - button { - font: inherit; - vertical-align:middle - } - - nav a:link, nav a:visited, nav a:hover, nav a:active { - text-decoration:none - } - - :root { - --border-radius: 4px; - --content-margin-bottom: 1em - } - - body { - color-scheme: light dark; - --logo-reference: url('/assets/images/swift.svg'); - --menu-icon: url('/assets/images/icon-menu.svg'); - --menu-icon-close: url('/assets/images/icon-close.svg'); - --color-nav-background: var(--color-fill-secondary); - --color-nav-rule: rgb(230, 230, 230); - --color-active-menu-group: #2a2a2a; - --color-fill: #fff; - --color-fill-secondary: #f7f7f7; - --color-fill-tertiary: #f0f0f0; - --color-fill-quaternary: #282828; - --color-fill-blue: blue; - --color-fill-gray: #ccc; - --color-fill-gray-secondary: #f5f5f5; - --color-fill-green-secondary: #f0fff0; - --color-fill-orange-secondary: #fffaf6; - --color-fill-red-secondary: #fff0f5; - --color-figure-blue: #36f; - --color-figure-gray: #000; - --color-figure-gray-secondary: #666; - --color-figure-gray-secondary-alt: #666; - --color-figure-gray-tertiary: #666; - --color-figure-green: green; - --color-figure-light-gray: #666; - --color-figure-orange: #c30; - --color-figure-red: red; - --color-tutorials-teal: #000; - --color-article-background: var(--color-fill-tertiary); - --color-article-body-background: var(--color-fill); - --color-aside-deprecated: var(--color-figure-gray); - --color-aside-deprecated-background: var(--color-fill-orange-secondary); - --color-aside-deprecated-border: var(--color-figure-orange); - --color-aside-experiment: var(--color-figure-gray); - --color-aside-experiment-background: var(--color-fill-gray-secondary); - --color-aside-experiment-border: var(--color-figure-light-gray); - --color-aside-important: var(--color-figure-gray); - --color-aside-important-background: var(--color-fill-gray-secondary); - --color-aside-important-border: var(--color-figure-light-gray); - --color-aside-note: var(--color-figure-gray); - --color-aside-note-background: var(--color-fill-gray-secondary); - --color-aside-note-border: var(--color-figure-light-gray); - --color-aside-tip: var(--color-figure-gray); - --color-aside-tip-background: var(--color-fill-gray-secondary); - --color-aside-tip-border: var(--color-figure-light-gray); - --color-aside-warning: var(--color-figure-gray); - --color-aside-warning-background: var(--color-fill-red-secondary); - --color-aside-warning-border: var(--color-figure-red); - --color-badge-default: var(--color-figure-light-gray); - --color-badge-beta: var(--color-figure-gray-tertiary); - --color-badge-deprecated: var(--color-figure-orange); - --color-badge-dark-default: #b0b0b0; - --color-badge-dark-beta: #b0b0b0; - --color-badge-dark-deprecated: #f60; - --color-button-background: var(--color-fill-blue); - --color-button-background-active: #36f; - --color-button-background-hover: var(--color-figure-blue); - --color-button-text: #fff; - --color-call-to-action-background: var(--color-fill-secondary); - --color-changes-added: var(--color-figure-light-gray); - --color-changes-added-hover: var(--color-figure-light-gray); - --color-changes-deprecated: var(--color-figure-light-gray); - --color-changes-deprecated-hover: var(--color-figure-light-gray); - --color-changes-modified: var(--color-figure-light-gray); - --color-changes-modified-hover: var(--color-figure-light-gray); - --color-changes-modified-previous-background: var(--color-fill-gray-secondary); - --color-code-background: var(--color-fill-secondary); - --color-code-collapsible-background: var(--color-fill-tertiary); - --color-code-collapsible-text: var(--color-figure-gray-secondary-alt); - --color-code-line-highlight: rgba(51, 102, 255, 0.08); - --color-code-line-highlight-border: var(--color-figure-blue); - --color-code-plain: var(--color-figure-gray); - --color-content-table-content-color: var(--color-fill-secondary); - --color-dropdown-background: rgba(255, 255, 255, 0.8); - --color-dropdown-border: #ccc; - --color-dropdown-option-text: #666; - --color-dropdown-text: #000; - --color-dropdown-dark-background: rgba(255, 255, 255, 0.1); - --color-dropdown-dark-border: rgba(240, 240, 240, 0.2); - --color-dropdown-dark-option-text: #ccc; - --color-dropdown-dark-text: #fff; - --color-eyebrow: var(--color-figure-gray-secondary); - --color-focus-border-color: var(--color-fill-blue); - --color-focus-color: rgba(0, 125, 250, 0.6); - --color-form-error: var(--color-figure-red); - --color-form-error-background: var(--color-fill-red-secondary); - --color-form-valid: var(--color-figure-green); - --color-form-valid-background: var(--color-fill-green-secondary); - --color-generic-modal-background: var(--color-fill); - --color-grid: var(--color-fill-gray); - --color-header-text: var(--color-figure-gray); - --color-hero-eyebrow: #ccc; - --color-link: var(--color-figure-blue); - --color-loading-placeholder-background: var(--color-fill); - --color-nav-color: #666; - --color-nav-current-link: rgba(0, 0, 0, 0.6); - --color-nav-expanded: #fff; - --color-nav-hierarchy-collapse-background: #f0f0f0; - --color-nav-hierarchy-collapse-borders: #ccc; - --color-nav-hierarchy-item-borders: #ccc; - --color-nav-keyline: rgba(0, 0, 0, 0.2); - --color-nav-link-color: #000; - --color-nav-link-color-hover: #36f; - --color-nav-outlines: #ccc; - --color-nav-solid-background: #fff; - --color-nav-sticking-expanded-keyline: rgba(0, 0, 0, 0.1); - --color-nav-stuck: rgba(255, 255, 255, 0.9); - --color-nav-uiblur-expanded: rgba(255, 255, 255, 0.9); - --color-nav-uiblur-stuck: rgba(255, 255, 255, 0.7); - --color-nav-root-subhead: var(--color-tutorials-teal); - --color-nav-dark-border-top-color: rgba(255, 255, 255, 0.4); - --color-nav-dark-color: #b0b0b0; - --color-nav-dark-current-link: rgba(255, 255, 255, 0.6); - --color-nav-dark-expanded: #2a2a2a; - --color-nav-dark-hierarchy-collapse-background: #424242; - --color-nav-dark-hierarchy-collapse-borders: #666; - --color-nav-dark-hierarchy-item-borders: #424242; - --color-nav-dark-keyline: rgba(66, 66, 66, 0.95); - --color-nav-dark-link-color: #fff; - --color-nav-dark-link-color-hover: #09f; - --color-nav-dark-outlines: #575757; - --color-nav-dark-rule: #575757; - --color-nav-dark-solid-background: #000; - --color-nav-dark-sticking-expanded-keyline: rgba(66, 66, 66, 0.7); - --color-nav-dark-stuck: rgba(42, 42, 42, 0.9); - --color-nav-dark-uiblur-expanded: rgba(42, 42, 42, 0.9); - --color-nav-dark-uiblur-stuck: rgba(42, 42, 42, 0.7); - --color-nav-dark-root-subhead: #fff; - --color-runtime-preview-background: var(--color-fill-tertiary); - --color-runtime-preview-disabled-text: rgba(102, 102, 102, 0.6); - --color-runtime-preview-text: var(--color-figure-gray-secondary); - --color-secondary-label: var(--color-figure-gray-secondary); - --color-step-background: var(--color-fill-secondary); - --color-step-caption: var(--color-figure-gray-secondary); - --color-step-focused: var(--color-figure-light-gray); - --color-step-text: var(--color-figure-gray-secondary); - --color-svg-icon: #666; - --color-syntax-attributes: rgb(148, 113, 0); - --color-syntax-characters: rgb(39, 42, 216); - --color-syntax-comments: rgb(112, 127, 140); - --color-syntax-documentation-markup: rgb(80, 99, 117); - --color-syntax-documentation-markup-keywords: rgb(80, 99, 117); - --color-syntax-heading: rgb(186, 45, 162); - --color-syntax-keywords: rgb(173, 61, 164); - --color-syntax-marks: rgb(0, 0, 0); - --color-syntax-numbers: rgb(39, 42, 216); - --color-syntax-other-class-names: rgb(112, 61, 170); - --color-syntax-other-constants: rgb(75, 33, 176); - --color-syntax-other-declarations: rgb(4, 124, 176); - --color-syntax-other-function-and-method-names: rgb(75, 33, 176); - --color-syntax-other-instance-variables-and-globals: rgb(112, 61, 170); - --color-syntax-other-preprocessor-macros: rgb(120, 73, 42); - --color-syntax-other-type-names: rgb(112, 61, 170); - --color-syntax-param-internal-name: rgb(64, 64, 64); - --color-syntax-plain-text: rgb(0, 0, 0); - --color-syntax-preprocessor-statements: rgb(120, 73, 42); - --color-syntax-project-class-names: rgb(62, 128, 135); - --color-syntax-project-constants: rgb(45, 100, 105); - --color-syntax-project-function-and-method-names: rgb(45, 100, 105); - --color-syntax-project-instance-variables-and-globals: rgb(62, 128, 135); - --color-syntax-project-preprocessor-macros: rgb(120, 73, 42); - --color-syntax-project-type-names: rgb(62, 128, 135); - --color-syntax-strings: rgb(209, 47, 27); - --color-syntax-type-declarations: rgb(3, 99, 140); - --color-syntax-urls: rgb(19, 55, 255); - --color-tabnav-item-border-color: var(--color-fill-gray); - --color-text: var(--color-figure-gray); - --color-text-background: var(--color-fill); - --color-tutorial-assessments-background: var(--color-fill-secondary); - --color-tutorial-background: var(--color-fill); - --color-tutorial-navbar-dropdown-background: var(--color-fill); - --color-tutorial-navbar-dropdown-border: var(--color-fill-gray); - --color-tutorial-quiz-border-active: var(--color-figure-blue); - --color-tutorials-overview-background: #161616; - --color-tutorials-overview-content: #fff; - --color-tutorials-overview-content-alt: #fff; - --color-tutorials-overview-eyebrow: #ccc; - --color-tutorials-overview-icon: #b0b0b0; - --color-tutorials-overview-link: #09f; - --color-tutorials-overview-navigation-link: #ccc; - --color-tutorials-overview-navigation-link-active: #fff; - --color-tutorials-overview-navigation-link-hover: #fff; - --color-tutorial-hero-text: #fff; - --color-tutorial-hero-background: #000 - } - - body[data-color-scheme="light"] { - color-scheme: light - } - - body[data-color-scheme="dark"] { - color-scheme:dark - } - - @media screen { - body[data-color-scheme="dark"] { - --logo-reference: url('/assets/images/swift~dark.svg'); - --menu-icon: url('/assets/images/icon-menu~dark.svg'); - --menu-icon-close: url('/assets/images/icon-close~dark.svg'); - --color-nav-background: var(--color-fill-tertiary); - --color-nav-rule: #424242; - --color-active-menu-group: #f0f0f0; - --color-fill: #000; - --color-fill-secondary: #161616; - --color-fill-tertiary: #2a2a2a; - --color-fill-blue: #06f; - --color-fill-gray: #575757; - --color-fill-gray-secondary: #222; - --color-fill-green-secondary: #030; - --color-fill-orange-secondary: #472400; - --color-fill-red-secondary: #300; - --color-figure-blue: #09f; - --color-figure-gray: #fff; - --color-figure-gray-secondary: #ccc; - --color-figure-gray-secondary-alt: #b0b0b0; - --color-figure-gray-tertiary: #b0b0b0; - --color-figure-green: #090; - --color-figure-light-gray: #b0b0b0; - --color-figure-orange: #f60; - --color-figure-red: #f33; - --color-tutorials-teal: #fff; - --color-article-body-background: rgb(17, 17, 17); - --color-button-background-active: #06f; - --color-code-line-highlight: rgba(0, 153, 255, 0.08); - --color-dropdown-background: var(--color-dropdown-dark-background); - --color-dropdown-border: var(--color-dropdown-dark-border); - --color-dropdown-option-text: var(--color-dropdown-dark-option-text); - --color-dropdown-text: var(--color-dropdown-dark-text); - --color-nav-color: var(--color-nav-dark-color); - --color-nav-current-link: var(--color-nav-dark-current-link); - --color-nav-expanded: var(--color-nav-dark-expanded); - --color-nav-hierarchy-collapse-background: var(--color-nav-dark-hierarchy-collapse-background); - --color-nav-hierarchy-collapse-borders: var(--color-nav-dark-hierarchy-collapse-borders); - --color-nav-hierarchy-item-borders: var(--color-nav-dark-hierarchy-item-borders); - --color-nav-keyline: var(--color-nav-dark-keyline); - --color-nav-link-color: var(--color-nav-dark-link-color); - --color-nav-link-color-hover: var(--color-nav-dark-link-color-hover); - --color-nav-outlines: var(--color-nav-dark-outlines); - --color-nav-solid-background: var(--color-nav-dark-solid-background); - --color-nav-sticking-expanded-keyline: var(--color-nav-dark-sticking-expanded-keyline); - --color-nav-stuck: var(--color-nav-dark-stuck); - --color-nav-uiblur-expanded: var(--color-nav-dark-uiblur-expanded); - --color-nav-uiblur-stuck: var(--color-nav-dark-uiblur-stuck); - --color-runtime-preview-disabled-text: rgba(204, 204, 204, 0.6); - --color-syntax-attributes: rgb(204, 151, 104); - --color-syntax-characters: rgb(217, 201, 124); - --color-syntax-comments: rgb(127, 140, 152); - --color-syntax-documentation-markup: rgb(127, 140, 152); - --color-syntax-documentation-markup-keywords: rgb(163, 177, 191); - --color-syntax-keywords: rgb(255, 122, 178); - --color-syntax-marks: rgb(255, 255, 255); - --color-syntax-numbers: rgb(217, 201, 124); - --color-syntax-other-class-names: rgb(218, 186, 255); - --color-syntax-other-constants: rgb(167, 235, 221); - --color-syntax-other-declarations: rgb(78, 176, 204); - --color-syntax-other-function-and-method-names: rgb(178, 129, 235); - --color-syntax-other-instance-variables-and-globals: rgb(178, 129, 235); - --color-syntax-other-preprocessor-macros: rgb(255, 161, 79); - --color-syntax-other-type-names: rgb(218, 186, 255); - --color-syntax-param-internal-name: rgb(191, 191, 191); - --color-syntax-plain-text: rgb(255, 255, 255); - --color-syntax-preprocessor-statements: rgb(255, 161, 79); - --color-syntax-project-class-names: rgb(172, 242, 228); - --color-syntax-project-constants: rgb(120, 194, 179); - --color-syntax-project-function-and-method-names: rgb(120, 194, 179); - --color-syntax-project-instance-variables-and-globals: rgb(120, 194, 179); - --color-syntax-project-preprocessor-macros: rgb(255, 161, 79); - --color-syntax-project-type-names: rgb(172, 242, 228); - --color-syntax-strings: rgb(255, 129, 112); - --color-syntax-type-declarations: rgb(107, 223, 255); - --color-syntax-urls: rgb(102, 153, 255); - --color-tutorial-background: var(--color-fill-tertiary) - } - } - - .highlight { - background:var(--color-code-background) - } - - .highlight .c, .highlight .cm, .highlight .cp, .highlight .c1, .highlight .cs { - color:var(--color-syntax-comments) - } - - .highlight .k, .highlight .kc, .highlight .kd, .highlight .kp, .highlight .kr, .highlight .kt .nb { - color:var(--color-syntax-keywords) - } - - .highlight .nv, .highlight .nf { - color:color(--color-syntax-project-constants) - } - - .highlight .s, .highlight .sb, .highlight .sc, .highlight .sd, .highlight .s2, .highlight .se, .highlight .sh, .highlight .si, .highlight .s1, .highlight .sx { - color:var(--color-syntax-strings) - } - - .highlight .na { - color:var(--color-syntax-attributes) - } - - .highlight .nc, .highlight .ni, .highlight .no, .highlight .vc, .highlight .vg, .highlight .vi { - color:var(--color-syntax-other-type-names) - } - - .highlight .err, .highlight .gr, .highlight .gt, .highlight .ne { - color:var(--color-syntax-strings) - } - - .highlight .m, .highlight .mf, .highlight .mh, .highlight .mi, .highlight .il, .highlight .mo { - color:var(--color-syntax-numbers) - } - - .highlight .o, .highlight .ow, .highlight .gs { - font-weight:bold - } - - .highlight .ge { - font-style:italic - } - - .highlight .nt { - color:var(--color-syntax-characters) - } - - .highlight .gd, .highlight .gd .x { - color: var(--color-syntax-plain-text); - background-color:var(--color-fill-red-secondary) - } - - .highlight .gi, .highlight .gi .x { - color: var(--color-syntax-plain-text); - background-color:color(--color-fill-green-secondary) - } - - .highlight .gh, .highlight .bp, .highlight .go, .highlight .gp, .highlight .gu, .highlight .w { - color:var(--color-syntax-comments) - } - - .highlight .nn { - color:var(--color-syntax-other-declarations) - } - - .highlight .sr { - color:var(--color-figure-green) - } - - .highlight .ss { - color:var(--color-syntax-heading) - } - .language-console { - color:var(--color-syntax-plain-text) - } - - *, * :before, * :after { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing:border-box - } - - html, body { - height:100% - } - - body { - font-family: -apple-system, BlinkMacSystemFont, "SF Hello", "Helvetica Neue", Helvetica, Arial, Verdana, sans-serif; - font-size: 18px; - line-height: 1.5; - background-color: var(--color-fill); - color: var(--color-text); - font-weight:300 - } - - body pre, body code { - font-family: "SF Mono", Menlo, Consolas, Monaco, "Courier New", monospace, serif - } - - a:link { - color: var(--color-link); - text-decoration:none - } - - a:visited { - color:var(--color-link) - } - - a:active { - color:var(--color-link) - } - - a:hover { - color: var(--color-link); - text-decoration:underline - } - - p { - margin-bottom:1em - } - - h1 { - margin-bottom: 0.5em; - font-size: 3em; - font-weight: 300; - line-height:1 - } - - h1.active + .main-nav { - border-top:1px solid var(--color-active-menu-group) - } - - h2 { - margin-bottom: 0.5em; - font-size: 2.5em; - font-weight: 300; - line-height:1 - } - - h3 { - margin-bottom: 0.5em; - font-size: 1.5em; - font-weight: 300; - line-height:1 - } - - h4 { - margin-bottom: 0.5em; - font-size: 1.25em; - font-weight: 300; - line-height:1.2 - } - - h5 { - margin-bottom: 0.5em; - font-size: 1.175em; - font-weight: 500; - line-height:1.4 - } - - h6 { - margin-bottom: 0.5em; - font-size: 1em; - font-weight: 700; - line-height:1.5 - } - - h1, h2, h3, h4, h5, h6 { - color:var(--color-header-text) - } - - div.highlighter-rouge { - margin-left:13px - } - - pre { - font-size: 14px; - line-height: 1.6em; - border-left: 5px solid var(--color-code-line-highlight-border); - margin: 0.5em 0 1.5em 10px; - padding: 4px 0 2px 10px; - overflow:scroll - } - - a > code, p > code, li > code, dd > code, blockquote > code, td > code { - padding: 0; - margin: 0; - font-size: 16px; - white-space: nowrap; - background-color:transparent - } - - p > code, li > code, dd > code, blockquote > code, td > code { - color:var(--color-code-plain) - } - - p > code { - white-space: pre-wrap; - word-break:break-word - } - - hr { - border: none; - border-top: 1px var(--color-dropdown-border) solid; - margin:2em 0 - } - - hr:last-child { - display:none - } - - details { - margin-bottom:2em - } - - details :first-child { - margin-top:1.5em - } - - cite { - display:block - } - - cite:before { - content: "— " - } - - #logo { - text-indent: -9999px; - height: 48px; - width: 100%; - margin-top: 20px; - margin-bottom: 0.5em; - padding-bottom:10px - } - - #logo a { - display: block; - width: 190px; - height: 48px; - background-image: var(--logo-reference); - background-repeat: no-repeat; - background-size: 190px 48px; - background-position-x: -8px - } - - nav[role="navigation"] { - width: 250px; - position: fixed; - overflow: scroll; - left: 0; - top: 0; - bottom: 0; - background: var(--color-nav-background); - color: var(--color-nav-color); - border-right: 1px solid var(--color-nav-rule); - padding: 20px 30px - } - - nav[role="navigation"] ul { - border-top: 1px solid var(--color-nav-rule); - font-weight: 400; - margin-bottom: 30px; - list-style: none - } - - nav[role="navigation"] ul ul { - list-style: none - } - - nav[role="navigation"] ul li { - border-bottom: 1px solid var(--color-nav-rule) - } - - nav[role="navigation"] ul li.active { - border-bottom: 1px solid var(--color-active-menu-group) - } - - nav[role="navigation"] ul li.active a { - font-weight: 700 - } - - nav[role="navigation"] ul li a:link { - color: var(--color-nav-link-color); - text-decoration: none; - text-transform: uppercase; - letter-spacing: 1px; - font-size: 12px; - display: block; - padding: 10px - } - - nav[role="navigation"] ul li a:visited { - color: var(--color-nav-link-color) - } - - nav[role="navigation"] ul li a:active { - font-weight: 700 - } - - nav[role="navigation"] ul li a:hover { - color: var(--color-link) - } - - nav[role="navigation"] ul li ul { - margin-bottom: 10px; - border-top: none - } - - nav[role="navigation"] ul li ul li { - border-bottom: none; - padding: 0.1em - } - - nav[role="navigation"] ul li ul li.active { - border-bottom: none - } - - nav[role="navigation"] ul li ul li.active a { - font-weight: 700 - } - - nav[role="navigation"] ul li ul a:link { - color: var(--color-nav-link-color); - text-decoration: none; - text-transform: none; - letter-spacing: 0; - font-size: 12px; - display: block; - margin-left: 15px; - padding: 0 0 3px; - border-bottom: none; - font-weight: 300 - } - - nav[role="navigation"] ul li ul a:hover { - color: var(--color-link) - } - - nav[role="navigation"] h2 { - font-size: 0.75em; - font-weight: 700; - text-transform: lowercase; - font-variant: small-caps; - color: var(--color-figure-gray-secondary-alt); - padding-bottom:0.5em - } - - main { - max-width: 798px; - min-width: 320px; - margin-left: 250px; - padding: 35px 30px 0; - min-height: 100%; - height: auto !important; - height: 100% - } - - footer[role="contentinfo"] { - background: var(--color-nav-background); - border-top: 1px solid var(--color-nav-rule); - color: var(--color-nav-color); - padding: 20px 30px; - margin-left: 250px; - min-height: 74px - } - - footer[role="contentinfo"] p { - font-size: 0.625em; - color: var(--color-nav-link-color); - line-height: 1em; - margin-bottom: 1em; - margin-bottom: var(--content-margin-bottom) - } - - footer[role="contentinfo"] p.privacy a { - color: var(--color-nav-link-color); - border-right: 1px solid var(--color-nav-rule); - margin-right: 6px; - padding-right: 8px - } - - footer[role="contentinfo"] p.privacy a:last-child { - border: none; - margin: 0; - padding: 0 - } - - footer[role="contentinfo"] p:last-child { - margin-bottom: 0 - } - - footer[role="contentinfo"] aside { - position: relative; - width: 100%; - max-width: 700px - } - - footer[role="contentinfo"] aside i { - width: 16px; - height: 16px; - background-repeat: no-repeat; - background-size: 16px; - display: block; - margin-left: 1em; - float: right - } - - footer[role="contentinfo"] aside i.twitter { - background-image: url("/assets/images/icon-twitter.svg") - } - - footer[role="contentinfo"] aside i.feed { - background-image: url("/assets/images/icon-feed.svg") - } - - article:first-of-type { - padding-bottom:36px - } - - article h2 { - padding-top:1.1em - } - - article h3 { - padding-top:1em - } - - article h4 { - padding-top: 1em; - border-bottom: 1px var(--color-dropdown-border) solid; - padding-bottom:0.5em - } - - article h5 { - margin-top:1em - } - - article header { - width: 100%; - display: inline-block; - padding-bottom:2.5em - } - - article header h1 { - padding-bottom:0.125em - } - - article header .byline { - float: left; - font-size: 14px; - margin-bottom:1em - } - - article header .byline img { - width: 32px; - height: 32px; - border-radius: 50%; - border: 1px var(--color-fill-gray) solid; - position: absolute; - margin-right: 0.25em; - margin-top:-6px - } - - article header .byline span { - padding-left:42px - } - - article header .about { - float: none; - clear: both; - font-size: 14px; - font-weight: 400; - color: var(--color-figure-gray-tertiary); - border-left: 1px var(--color-figure-gray-tertiary) solid; - margin: 23px 3em 23px 0; - padding:4px 0 4px 10px - } - - article header time { - float: left; - text-transform: uppercase; - font-size: 14px; - font-weight: 400; - color: var(--color-figure-gray-tertiary); - margin-right: 3em; - margin-bottom:1em - } - - article header .tags { - display: block; - font-size: 12px; - font-weight: 400; - margin-top:0 - } - - article:not(:first-of-type) { - border-top: 1px solid var(--color-figure-gray-tertiary); - padding:36px 0 - } - - article blockquote { - border-left: 5px var(--color-fill-gray) solid; - margin: 0.5em 0 23px 1em; - padding: 4px 0 2px 10px; - color: var(--color-aside-note); - overflow-x:auto - } - - article blockquote p:last-child { - margin-bottom:0 - } - - article ul, article ol { - padding-left: 40px; - margin:1em 0 - } - - article ul ul, article ul ol, article ol ul, article ol ol { - margin:0 - } - - article ul { - list-style:disc - } - - article ul ul { - list-style:circle - } - - article ul ul ul { - list-style:square - } - - article ol { - list-style:decimal - } - - article dl { - margin:2em 0 1em 0 - } - - article dl:after { - content: ""; - display: table; - clear:both - } - - article dl dt { - float: left; - clear: right; - margin-right: 1em; - display: block; - width: 28%; - text-align:right - } - - article dl dd { - float: right; - width: 65%; - margin-bottom: 1em; - overflow:scroll - } - - article dl dd { - padding-bottom: 1em; - border-bottom:1px var(--color-dropdown-border) solid - } - - article table { - display: block; - overflow-x: auto; - width: max-content; - min-width: 68%; - max-width: 100%; - margin: 2em auto 3em auto; - border-collapse: separate; - border:1px var(--color-dropdown-border) solid - } - - article table th { - font-weight: 700; - text-align:center - } - - article table th, article table td { - width: 50%; - padding: 0.5em 1.5em; - border-bottom:1px var(--color-dropdown-border) solid - } - - article table th:not(:first-child), article table td:not(:first-child) { - border-left:1px var(--color-dropdown-border) solid - } - - article table tr:last-child td { - border-bottom:none - } - - article details { - margin-top: 0; - cursor:pointer - } - - article details summary { - display: list-item; - padding-bottom: 0.5em; - outline: none; - margin-top:0 - } - - article details summary:after { - content: "Expand"; - text-transform: lowercase; - font-variant: small-caps; - border-bottom:1px var(--color-fill-gray) dashed - } - - article details[open] summary:after { - content: "Collapse" - } - - article details[open] * :not(summary) { - cursor:auto - } - - article details.download { - margin-top: 0; - cursor:pointer - } - - article details.download table { - display:inline-table - } - - article details.download summary { - padding-bottom: 0.5em; - outline: none; - margin-top:0 - } - - article details.download summary:after { - content: none; - text-transform: lowercase; - font-variant: small-caps; - border-bottom:1px var(--color-fill-gray) dashed - } - - article details.download[open] summary:after { - content:none - } - - article details.download[open] * :not(summary) { - cursor:auto - } - - article > details { - margin-left:40px - } - - article .good pre, article pre.good { - background: var(--color-fill-green-secondary); - border-color:var(--color-figure-green) - } - - article .good pre:before, article pre.good:before { - content: "✅"; - float:right - } - - article .bad pre, article pre.bad { - background: var(--color-fill-red-secondary); - border-color:var(--color-figure-red) - } - - article .bad pre:before, article pre.bad:before { - content: "⛔️"; - float:right - } - - article .links ul { - list-style:none - } - - article .links ul ul { - list-style: disc; - margin-top:5px - } - - article .links a:after { - content: " ›" - } - - article .links .link-external:after, article .links-external a:after, article .link-external:after { - content: " ↗" - } - - article .links-download a:after { - content: " ⬇" - } - - article .links-list-nostyle ul { - padding-left:0 - } - - article .links-list-nostyle ul ul { - list-style:none - } - - article .links-sublevel p { - margin-bottom:0 - } - - article .links-sublevel ul { - margin-top: 0; - padding-left:40px - } - - article footer { - margin: 4em 0 0 0; - padding: 1.5em 0 1em 0; - border-top:1px var(--color-dropdown-border) solid - } - - article footer:after { - content: ""; - display: table; - clear: both - } - - article footer nav [rel="prev"] { - width: 45%; - float: left; - text-align: left - } - - article footer nav [rel="prev"]:before { - content: "← " - } - - article footer nav [rel="next"] { - width: 45%; - float: right; - text-align: right - } - - article footer nav [rel="next"]:after { - content: " →" - } - - .title a:link, .title a:visited { - color:var(--color-header-text) - } - - .alert, .danger, .warning, .info, .success { - border-width: 1px; - border-style: solid; - padding: 0.5em; - margin:0.5em 0 1.5em 0 - } - - .alert a, .danger a, .warning a, .info a, .success a { - word-break:break-word - } - - .alert p:first-child, .danger p:first-child, .warning p:first-child, .info p:first-child, .success p:first-child { - margin-top:0 - } - - .alert p:last-child, .danger p:last-child, .warning p:last-child, .info p:last-child, .success p:last-child { - margin-bottom:0 - } - - .alert code, .danger code, .warning code, .info code, .success code { - border: none; - background: transparent; - padding:0 - } - - code { - white-space:pre-line - } - - pre code { - white-space:inherit - } - - pre code .graphic { - font-size: 19px; - line-height:0 - } - - pre code .commentary, pre code .graphic { - font-family: "SF Hello", "Helvetica Neue", Helvetica, Arial, Verdana, sans-serif - } - - @supports (overflow: -webkit-marquee) and(justify-content: inherit) { - .alert:before, .danger:before, .warning:before, .info:before, .success:before { - font-size: 1em; - float: left; - clear: left; - padding-left: 0.125em; - width:2em - } - - .alert p, .danger p, .warning p, .info p, .success p { - padding-left:2em - } - - .success:before { - content: "✅" - } - - .info:before { - content: "ℹ️" - } - - .warning:before { - content: "⚠️" - } - - .danger:before { - content: "❗️" - } - } - - .success { - color: var(--color-aside-note); - border-color: var(--color-form-valid); - background-color:var(--color-form-valid-background) - } - - .info { - color: var(--color-aside-note); - border-color: var(--color-aside-note-border); - background-color:var(--color-aside-note-background) - } - - .warning { - color: var(--color-aside-deprecated); - border-color: var(--color-aside-deprecated-border); - background-color:var(--color-aside-deprecated-background) - } - - .danger { - color: var(--color-aside-warning); - border-color: var(--color-aside-warning-border); - background-color:var(--color-aside-warning-background) - } - - table.downloads { - width: 100%; - table-layout:fixed - } - - table.downloads th { - font-size:0.75em - } - - table.downloads .platform { - width:40% - } - - table.downloads .download { - width:60% - } - - table.downloads .download a.debug, table.downloads .download a.signature { - font-size: 0.7em; - display:block - } - - table.downloads .download a { - font-weight: 700; - font-size:1em - } - - table.downloads .download a:not([download]) { - font-weight:400 - } - - table.downloads .download a:not([download]):before { - content: "(" - } - - table.downloads .download a:not([download]):after { - content: ")" - } - - table.downloads .arch-tag { - width:60% - } - - table.downloads .arch-tag a.debug, table.downloads .arch-tag a.signature { - font-size: 0.7em; - display:block - } - - table.downloads .arch-tag a { - font-weight: 700; - font-size:1em - } - - table.downloads .arch-tag a:not([arch-tag]) { - font-weight:400 - } - - article input.detail[type=checkbox] { - visibility: hidden; - cursor: pointer; - height: 0; - width: 100%; - margin-bottom: 2em; - display: block; - font-size: inherit; - font-style: inherit; - font-weight: inherit; - font-family: inherit; - position: relative; - top:-.85rem - } - - article p + input.detail[type=checkbox] { - margin-top:auto - } - - article .screenonly { - display:none - } - - @media screen { - article .screenonly { - display:inherit - } - - article input.detail[type=checkbox]:before { - content: "▶ "; - visibility: visible; - font-size:80% - } - - article input.detail[type=checkbox]:after { - text-transform: lowercase; - font-variant: small-caps; - border-bottom: 1px var(--color-fill-gray) dashed; - color: var(--color-figure-gray-secondary); - content: "More detail"; - visibility:visible - } - - article input.detail[type=checkbox]:checked:before { - content: "▼ " - } - - article input.detail[type=checkbox]:checked:after { - content: "Less detail" - } - - article input.detail[type=checkbox] + .more { - transition:0.5s opacity ease, 0.5s max-height ease - } - - article input.detail[type=checkbox]:checked + .more { - visibility: visible; - max-height:1000rem - } - - article input.detail[type=checkbox]:not(:checked) + .more { - overflow: hidden; - max-height: 0px; - opacity:0 - } - } - - article .more > p:first-of-type { - margin-top:0 - } - - .color-scheme-toggle { - display: block; - outline: none; - --toggle-color-fill: var(--color-button-background); - font-size: 12px; - border: 1px solid var(--color-nav-link-color); - border-radius: var(--border-radius); - display: inline-flex; - padding: 1px; - margin-bottom:var(--content-margin-bottom) - } - - .color-scheme-toggle input { - position: absolute; - clip: rect(1px, 1px, 1px, 1px); - clip-path: inset(0px 0px 99.9% 99.9%); - overflow: hidden; - height: 1px; - width: 1px; - padding: 0; - border: 0; - appearance:none - } - - .color-scheme-toggle-label { - border: 1px solid transparent; - border-radius: var(--toggle-border-radius-inner, 2px); - color: var(--color-nav-link-color); - display: inline-block; - text-align: center; - padding: 1px 6px; - min-width: 42px; - box-sizing:border-box - } - - .color-scheme-toggle-label:hover { - cursor:pointer - } - - input:checked + .color-scheme-toggle-label { - background: var(--color-nav-link-color); - color: var(--color-nav-stuck) - } - - [role="contentinfo"] { - display: flex; - justify-content:space-between - } - - .visuallyhidden { - position: absolute; - clip: rect(1px, 1px, 1px, 1px); - clip-path: inset(0px 0px 99.9% 99.9%); - overflow: hidden; - height: 1px; - width: 1px; - padding: 0; - border:0 - } - - @media only screen and (max-width: 767px) { - nav[role="navigation"] { - width: 100%; - position: relative; - border-bottom: 1px solid var(--color-nav-rule); - border-right: none; - padding: 20px 30px; - overflow: hidden - } - - nav.open[role="navigation"] .list-items { - display: block - } - - nav[role="navigation"] .list-items { - padding-top: var(--content-margin-bottom); - display:none - } - - .menu-toggle { - content: ' '; - height: 20px; - width: 20px; - background-image: var(--menu-icon-close); - background-repeat: no-repeat; - background-position: center center; - cursor:pointer - } - - .menu-toggle.open { - background-image:var(--menu-icon) - } - - #logo { - margin: 0; - padding:0 - } - - #logo a { - margin:0 auto - } - - main { - max-width: 100%; - min-width: 320px; - margin-left: 0; - padding: 30px 30px 0 - } - - footer[role="contentinfo"] { - margin-left: 0; - flex-direction:column - } - - .footer-other { - display: flex; - justify-content: space-between; - margin-top:var(--content-margin-bottom) - } - - h1 { - font-size: 48px; - font-weight: 300; - line-height:1 - } - - h2 { - font-size: 40px; - font-weight: 300; - line-height:1.1 - } - - h3 { - font-size: 38px; - font-weight: 300; - line-height:1.1 - } - - h4 { - font-size: 36px; - font-weight: 300; - line-height:1.2 - } - - h5 { - font-size: 24px; - font-weight: 500; - line-height:1.4 - } - - h6 { - font-size: 18px; - font-weight: 700; - line-height:1.5 - } - - div.highlighter-rouge { - margin-left:0 - } - - article blockquote { - margin-left:0.5em - } - - table.downloads { - border:1px var(--color-dropdown-border) solid - } - - table.downloads, table.downloads thead, table.downloads tbody, table.downloads th, table.downloads td, table.downloads tr { - display:block !important - } - - table.downloads thead tr { - position: absolute; - top: -9999px; - left:-9999px - } - - table.downloads tr { - border:1px solid var(--color-dropdown-border) - } - - table.downloads td { - border-left: none !important; - border-right: none !important; - border-bottom: 1px solid var(--color-dropdown-border) !important; - position: relative; - padding-left: 35%; - width:100% !important - } - - table.downloads td:before { - position: absolute; - top: 0.5em; - left: 0.5em; - width: 27.5%; - padding-right: 10px; - white-space: nowrap; - text-align:right - } - - table.downloads td.platform:before { - content: "Platform" - } - - table.downloads td.download:before { - content: "Download"; - top:1em - } - - table.downloads td.date:before { - content: "Date" - } - - table.downloads td.toolchain:before { - content: "Toolchain"; - top:1em - } - - table.downloads td.github-tag:before { - content: "GitHub Tag" - } - - table.downloads td.docker-tag:before { - content: "Docker Tag" - } - - table.downloads td.arch-tag:before { - content: "Architecture" - } - } - - .nav-menu-container { - display: grid; - grid-template-columns: 1fr; - grid-template-rows: 1fr; - align-items:center - } - - .menu-item { - grid-area:1 / 1 / 1 / 1 - } - - .logo-container { - justify-self:center - } - - .menu-toggle { - justify-self:right - } - - @media only print { - html body { - background: white; - font-size: 12pt; - padding:0.5in - } - - html body * { - -webkit-print-color-adjust:exact - } - - a { - color: black !important; - text-decoration: underline !important - } - - a[href^="http:"]:after { - content: " (" attr(href) ") "; - color:#444 - } - - h1, h2, h3, h4, h5, h6, p, article > div, pre, table { - page-break-inside:avoid - } - - details:not([open]) { - visibility:visible - } - - details:not([open]) summary { - display:none !important - } - - details:not([open]) > *, details:not([open]) { - display:block - } - - .alert, .success, .info, .warning, .danger { - margin:1.5em 0 - } - - main { - width: auto; - padding: 0; - border: 0; - float: none !important; - color: black; - background: transparent; - margin: 0; - max-width: 100%; - min-height: 1in - } - - nav[role="navigation"] { - background: transparent; - border: none; - width: auto; - position: static; - padding: 0 - } - - nav[role="navigation"] h2, nav[role="navigation"] ul { - display: none - } - - nav[role="navigation"] #logo { - position: static; - margin-bottom: 1.5em - } - - nav[role="navigation"] #logo a { - background-position: -15px - } - - footer[role="contentinfo"] { - display: none - } - } - - /*# sourceMappingURL=application.css.map */ - """ let cssRegex = Benchmark( name: "cssRegex", regex: try! Regex(r), ty: .allMatches, - target: css + target: Inputs.swiftOrgCSS ) let cssRegexNS = NSBenchmark( name: "cssRegexNS", regex: try! NSRegularExpression(pattern: r), ty: .all, - target: css + target: Inputs.swiftOrgCSS ) register(cssRegex) register(cssRegexNS) diff --git a/Sources/_StringProcessing/Engine/Processor.swift b/Sources/_StringProcessing/Engine/Processor.swift index edea57c87..da8bfea14 100644 --- a/Sources/_StringProcessing/Engine/Processor.swift +++ b/Sources/_StringProcessing/Engine/Processor.swift @@ -108,12 +108,13 @@ extension Processor { // Returns whether the advance succeeded. On failure, our // save point was restored mutating func consume(_ n: Distance) -> Bool { - // Want Collection to provide this behavior... - if input.distance(from: currentPosition, to: end) < n.rawValue { + guard let idx = input.index( + currentPosition, offsetBy: n.rawValue, limitedBy: end + ) else { signalFailure() return false } - currentPosition = input.index(currentPosition, offsetBy: n.rawValue) + currentPosition = idx return true } From 1356e8ce354f4d0047174d01d8759c1d0d327fc4 Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Fri, 17 Jun 2022 13:03:11 -0600 Subject: [PATCH 09/24] More optimizations, remove history preservation (#495) * Re-use the same executor, remember semantic mode. Gives around a 20% perf improvement to first-match style benchmarks. * Remove history preservation Cuts down on memory usage and avoids some ARC overhead. ~20% gains on "AllMatches" and related benchmarks. * Lower-level matchSeq Avoid collection algorithms inside matchSeq, which are liable to add ARC and inefficiencies. Results in a 3x improvement to ReluctantQuantWithTerminal. --- Sources/_StringProcessing/ByteCodeGen.swift | 12 ++-- .../_StringProcessing/Engine/MECapture.swift | 68 ++++++------------- .../_StringProcessing/Engine/Processor.swift | 58 +++++++++------- .../Engine/Structuralize.swift | 2 +- Sources/_StringProcessing/Regex/Match.swift | 9 ++- 5 files changed, 71 insertions(+), 78 deletions(-) diff --git a/Sources/_StringProcessing/ByteCodeGen.swift b/Sources/_StringProcessing/ByteCodeGen.swift index 8407f68ac..bcfc8a2c2 100644 --- a/Sources/_StringProcessing/ByteCodeGen.swift +++ b/Sources/_StringProcessing/ByteCodeGen.swift @@ -665,15 +665,19 @@ fileprivate extension Compiler.ByteCodeGen { } // If there's a capture transform, apply it now. if let transform = transform { - let fn = builder.makeTransformFunction { input, storedCapture in + let fn = builder.makeTransformFunction { input, cap in // If it's a substring capture with no custom value, apply the // transform directly to the substring to avoid existential traffic. - if let cap = storedCapture.latest, cap.value == nil { - return try transform(input[cap.range]) + // + // FIXME: separate out this code path. This is fragile, + // slow, and these are clearly different constructs + if let range = cap.range, cap.value == nil { + return try transform(input[range]) } + let value = constructExistentialOutputComponent( from: input, - component: storedCapture.latest, + component: cap.deconstructed, optionalCount: 0) return try transform(value) } diff --git a/Sources/_StringProcessing/Engine/MECapture.swift b/Sources/_StringProcessing/Engine/MECapture.swift index 054612e71..ec7c3668a 100644 --- a/Sources/_StringProcessing/Engine/MECapture.swift +++ b/Sources/_StringProcessing/Engine/MECapture.swift @@ -32,59 +32,48 @@ extension Processor { struct _StoredCapture { - // Set whenever we push the very first capture, allows us - // to theoretically re-compute anything we want to later. - fileprivate var startState: SavePoint? = nil - - // Save the entire history as we go, so that backtracking - // can just lop-off aborted runs. - // - // Backtracking entries can specify a per-capture stack - // index so that we can abort anything that came after. - // - // By remembering the entire history, we waste space, but - // we get flexibility for now. - // - fileprivate var history: Array<(range: Range, value: Any?)> = [] + var range: Range? = nil + + var value: Any? = nil // An in-progress capture start fileprivate var currentCaptureBegin: Position? = nil fileprivate func _invariantCheck() { - if startState == nil { - assert(history.isEmpty) - assert(currentCaptureBegin == nil) - } else if currentCaptureBegin == nil { - assert(!history.isEmpty) + if range == nil { + assert(value == nil) } } // MARK: - IPI - var isEmpty: Bool { history.isEmpty } - - var latest: (range: Range, value: Any?)? { history.last } + var deconstructed: (range: Range, value: Any?)? { + guard let r = range else { return nil } + return (r, value) + } /// Start a new capture. If the previously started one was un-ended, - /// will clear it and restart. If this is the first start, will save `initial`. + /// will clear it and restart. mutating func startCapture( - _ idx: Position, initial: SavePoint + _ idx: Position ) { _invariantCheck() defer { _invariantCheck() } - if self.startState == nil { - self.startState = initial - } currentCaptureBegin = idx } mutating func endCapture(_ idx: Position) { _invariantCheck() - assert(currentCaptureBegin != nil) defer { _invariantCheck() } - history.append((currentCaptureBegin! ..< idx, value: nil)) + guard let low = currentCaptureBegin else { + fatalError("Invariant violated: ending unstarted capture") + } + + range = low.. Array { values.map { - guard let last = $0.latest else { + guard let range = $0.range else { return nil } - return input[last.0] + return input[range] } } } diff --git a/Sources/_StringProcessing/Engine/Processor.swift b/Sources/_StringProcessing/Engine/Processor.swift index da8bfea14..5f58394d3 100644 --- a/Sources/_StringProcessing/Engine/Processor.swift +++ b/Sources/_StringProcessing/Engine/Processor.swift @@ -103,6 +103,12 @@ extension Processor { input[bounds] } + // Advance in our input, without any checks or failure signalling + mutating func _uncheckedForcedConsumeOne() { + assert(currentPosition != end) + input.formIndex(after: ¤tPosition) + } + // Advance in our input // // Returns whether the advance succeeded. On failure, our @@ -145,30 +151,26 @@ extension Processor { return slice } - mutating func match(_ e: Element) { + // Match against the current input element. Returns whether + // it succeeded vs signaling an error. + mutating func match(_ e: Element) -> Bool { guard let cur = load(), cur == e else { signalFailure() - return - } - if consume(1) { - controller.step() + return false } + _uncheckedForcedConsumeOne() + return true } + + // Match against the current input prefix. Returns whether + // it succeeded vs signaling an error. mutating func matchSeq( _ seq: C - ) where C.Element == Input.Element { - let count = seq.count - - guard let inputSlice = load(count: count), - seq.elementsEqual(inputSlice) - else { - signalFailure() - return - } - guard consume(.init(count)) else { - fatalError("unreachable") + ) -> Bool where C.Element == Input.Element { + for e in seq { + guard match(e) else { return false } } - controller.step() + return true } mutating func signalFailure() { @@ -356,18 +358,24 @@ extension Processor { case .match: let reg = payload.element - match(registers[reg]) + if match(registers[reg]) { + controller.step() + } case .matchSequence: let reg = payload.sequence let seq = registers[reg] - matchSeq(seq) + if matchSeq(seq) { + controller.step() + } case .matchSlice: let (lower, upper) = payload.pairedPosPos let range = registers[lower]...Match = try executor.match( + input, in: low..= high { return nil } - if regex.initialOptions.semanticLevel == .graphemeCluster { + if graphemeSemantic { input.formIndex(after: &low) } else { input.unicodeScalars.formIndex(after: &low) From fcd0b59ac4ace287276972aa2ae2de246a83515e Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Fri, 17 Jun 2022 15:29:33 -0600 Subject: [PATCH 10/24] Share the same processor in firstMatch (#497) Gives a 7x improvement to firstMatch-style benchmarks like "FirstMatch", 2-3x to CSS and basic backtracking benchmarks. Thanks to @rctcwyvrn for the original code. --- .../_StringProcessing/Engine/Processor.swift | 46 +++++- .../_StringProcessing/Engine/Registers.swift | 139 ++++++++---------- Sources/_StringProcessing/Executor.swift | 35 +++++ Sources/_StringProcessing/Regex/Match.swift | 21 +-- 4 files changed, 141 insertions(+), 100 deletions(-) diff --git a/Sources/_StringProcessing/Engine/Processor.swift b/Sources/_StringProcessing/Engine/Processor.swift index 5f58394d3..a81d2ce06 100644 --- a/Sources/_StringProcessing/Engine/Processor.swift +++ b/Sources/_StringProcessing/Engine/Processor.swift @@ -32,30 +32,38 @@ struct Processor< typealias Element = Input.Element let input: Input - let bounds: Range let matchMode: MatchMode + let instructions: InstructionList + + // MARK: Resettable state + + // The subject bounds. + // + // FIXME: This also conflates search bounds too! + var bounds: Range + + // The current position in the subject var currentPosition: Position - let instructions: InstructionList var controller: Controller - var cycleCount = 0 - - /// Our register file var registers: Registers - // Used for back tracking var savePoints: [SavePoint] = [] var callStack: [InstructionAddress] = [] + var storedCaptures: Array<_StoredCapture> + var state: State = .inProgress var failureReason: Error? = nil + + // MARK: Metrics, debugging, etc. + var cycleCount = 0 var isTracingEnabled: Bool - var storedCaptures: Array<_StoredCapture> } extension Processor { @@ -88,6 +96,30 @@ extension Processor { _checkInvariants() } + + mutating func reset(searchBounds: Range) { + // FIXME: We currently conflate both subject bounds and search bounds + // This should just reset search bounds + self.bounds = searchBounds + self.currentPosition = self.bounds.lowerBound + + self.controller = Controller(pc: 0) + + self.registers.reset(sentinel: bounds.upperBound) + + self.savePoints.removeAll(keepingCapacity: true) + self.callStack.removeAll(keepingCapacity: true) + + for idx in storedCaptures.indices { + storedCaptures[idx] = .init() + } + + self.state = .inProgress + self.failureReason = nil + + _checkInvariants() + } + func _checkInvariants() { assert(end <= input.endIndex) assert(start >= input.startIndex) diff --git a/Sources/_StringProcessing/Engine/Registers.swift b/Sources/_StringProcessing/Engine/Registers.swift index 812866ee6..e6f823341 100644 --- a/Sources/_StringProcessing/Engine/Registers.swift +++ b/Sources/_StringProcessing/Engine/Registers.swift @@ -18,21 +18,19 @@ struct SentinelValue: Hashable, CustomStringConvertible { extension Processor { /// Our register file struct Registers { - // currently, these are static readonly + + // MARK: static / read-only, non-resettable + + // Verbatim elements to compare against var elements: [Element] - // currently, these are static readonly + // Verbatim sequences to compare against // - // TODO: We want to be `String` instead of `[Character]`... + // TODO: Degenericize Processor and store Strings var sequences: [[Element]] = [] - // currently, hold output of assertions - var bools: [Bool] // TODO: bitset - - // currently, these are static readonly var consumeFunctions: [MEProgram.ConsumeFunction] - // currently, these are static readonly var assertionFunctions: [MEProgram.AssertionFunction] // Captured-value constructors @@ -44,69 +42,61 @@ extension Processor { // currently, these are for comments and abort messages var strings: [String] + // MARK: writeable, resettable + + // currently, hold output of assertions + var bools: [Bool] // TODO: bitset + // currently, useful for range-based quantification var ints: [Int] - // unused - var floats: [Double] = [] - // Currently, used for `movePosition` and `matchSlice` var positions: [Position] = [] var values: [Any] + } +} - // unused - var instructionAddresses: [InstructionAddress] = [] - - // unused, any application? - var classStackAddresses: [CallStackAddress] = [] - - // unused, any application? - var positionStackAddresses: [PositionStackAddress] = [] - - // unused, any application? - var savePointAddresses: [SavePointStackAddress] = [] - - subscript(_ i: StringRegister) -> String { - strings[i.rawValue] - } - subscript(_ i: SequenceRegister) -> [Element] { - sequences[i.rawValue] - } - subscript(_ i: IntRegister) -> Int { - get { ints[i.rawValue] } - set { ints[i.rawValue] = newValue } - } - subscript(_ i: BoolRegister) -> Bool { - get { bools[i.rawValue] } - set { bools[i.rawValue] = newValue } - } - subscript(_ i: PositionRegister) -> Position { - get { positions[i.rawValue] } - set { positions[i.rawValue] = newValue } - } - subscript(_ i: ValueRegister) -> Any { - get { values[i.rawValue] } - set { - values[i.rawValue] = newValue - } - } - subscript(_ i: ElementRegister) -> Element { - elements[i.rawValue] - } - subscript(_ i: ConsumeFunctionRegister) -> MEProgram.ConsumeFunction { - consumeFunctions[i.rawValue] - } - subscript(_ i: AssertionFunctionRegister) -> MEProgram.AssertionFunction { - assertionFunctions[i.rawValue] - } - subscript(_ i: TransformRegister) -> MEProgram.TransformFunction { - transformFunctions[i.rawValue] - } - subscript(_ i: MatcherRegister) -> MEProgram.MatcherFunction { - matcherFunctions[i.rawValue] +extension Processor.Registers { + subscript(_ i: StringRegister) -> String { + strings[i.rawValue] + } + subscript(_ i: SequenceRegister) -> [Input.Element] { + sequences[i.rawValue] + } + subscript(_ i: IntRegister) -> Int { + get { ints[i.rawValue] } + set { ints[i.rawValue] = newValue } + } + subscript(_ i: BoolRegister) -> Bool { + get { bools[i.rawValue] } + set { bools[i.rawValue] = newValue } + } + subscript(_ i: PositionRegister) -> Input.Index { + get { positions[i.rawValue] } + set { positions[i.rawValue] = newValue } + } + subscript(_ i: ValueRegister) -> Any { + get { values[i.rawValue] } + set { + values[i.rawValue] = newValue } } + subscript(_ i: ElementRegister) -> Input.Element { + elements[i.rawValue] + } + subscript(_ i: ConsumeFunctionRegister) -> MEProgram.ConsumeFunction { + consumeFunctions[i.rawValue] + } + subscript(_ i: AssertionFunctionRegister) -> MEProgram.AssertionFunction { + assertionFunctions[i.rawValue] + } + subscript(_ i: TransformRegister) -> MEProgram.TransformFunction { + transformFunctions[i.rawValue] + } + subscript(_ i: MatcherRegister) -> MEProgram.MatcherFunction { + matcherFunctions[i.rawValue] + } } extension Processor.Registers { @@ -141,20 +131,26 @@ extension Processor.Registers { self.ints = Array(repeating: 0, count: info.ints) - self.floats = Array(repeating: 0, count: info.floats) - self.positions = Array(repeating: sentinel, count: info.positions) self.values = Array( repeating: SentinelValue(), count: info.values) + } - self.instructionAddresses = Array(repeating: 0, count: info.instructionAddresses) - - self.classStackAddresses = Array(repeating: 0, count: info.classStackAddresses) - - self.positionStackAddresses = Array(repeating: 0, count: info.positionStackAddresses) + mutating func reset(sentinel: Input.Index) { + self.bools._setAll(to: false) + self.ints._setAll(to: 0) + self.positions._setAll(to: sentinel) + self.values._setAll(to: SentinelValue()) + } +} - self.savePointAddresses = Array(repeating: 0, count: info.savePointAddresses) +// TODO: Productize into general algorithm +extension MutableCollection { + mutating func _setAll(to e: Element) { + for idx in self.indices { + self[idx] = e + } } } @@ -196,12 +192,7 @@ extension Processor.Registers: CustomStringConvertible { \(formatRegisters("bools", bools))\ \(formatRegisters("strings", strings))\ \(formatRegisters("ints", ints))\ - \(formatRegisters("floats", floats))\ \(formatRegisters("positions", positions))\ - \(formatRegisters("instructionAddresses", instructionAddresses))\ - \(formatRegisters("classStackAddresses", classStackAddresses))\ - \(formatRegisters("positionStackAddresses", positionStackAddresses))\ - \(formatRegisters("savePointAddresses", savePointAddresses))\ """ } diff --git a/Sources/_StringProcessing/Executor.swift b/Sources/_StringProcessing/Executor.swift index 941d5943a..295a732de 100644 --- a/Sources/_StringProcessing/Executor.swift +++ b/Sources/_StringProcessing/Executor.swift @@ -19,6 +19,33 @@ struct Executor { self.engine = Engine(program, enableTracing: enablesTracing) } + @available(SwiftStdlib 5.7, *) + func firstMatch( + _ input: String, + in inputRange: Range, + graphemeSemantic: Bool + ) throws -> Regex.Match? { + var cpu = engine.makeProcessor( + input: input, bounds: inputRange, matchMode: .partialFromFront) + + var low = inputRange.lowerBound + let high = inputRange.upperBound + while true { + if let m: Regex.Match = try _match( + input, in: low..= high { return nil } + if graphemeSemantic { + input.formIndex(after: &low) + } else { + input.unicodeScalars.formIndex(after: &low) + } + cpu.reset(searchBounds: low..( _ input: String, @@ -27,7 +54,15 @@ struct Executor { ) throws -> Regex.Match? { var cpu = engine.makeProcessor( input: input, bounds: inputRange, matchMode: mode) + return try _match(input, in: inputRange, using: &cpu) + } + @available(SwiftStdlib 5.7, *) + func _match( + _ input: String, + in inputRange: Range, + using cpu: inout Processor + ) throws -> Regex.Match? { guard let endIdx = cpu.consume() else { if let e = cpu.failureReason { throw e diff --git a/Sources/_StringProcessing/Regex/Match.swift b/Sources/_StringProcessing/Regex/Match.swift index 8d9ff8f0b..7e4be5652 100644 --- a/Sources/_StringProcessing/Regex/Match.swift +++ b/Sources/_StringProcessing/Regex/Match.swift @@ -137,27 +137,10 @@ extension Regex { _ input: String, in inputRange: Range ) throws -> Regex.Match? { - // FIXME: Something more efficient, likely an engine interface, and we - // should scrap the RegexConsumer crap and call this - let executor = Executor(program: regex.program.loweredProgram) let graphemeSemantic = regex.initialOptions.semanticLevel == .graphemeCluster - - var low = inputRange.lowerBound - let high = inputRange.upperBound - while true { - if let m: Regex.Match = try executor.match( - input, in: low..= high { return nil } - if graphemeSemantic { - input.formIndex(after: &low) - } else { - input.unicodeScalars.formIndex(after: &low) - } - } + return try executor.firstMatch( + input, in: inputRange, graphemeSemantic: graphemeSemantic) } } From e6f38966d13dcef90e7b640e7c09daa29aff77a8 Mon Sep 17 00:00:00 2001 From: Richard Wei Date: Mon, 20 Jun 2022 00:13:31 -0700 Subject: [PATCH 11/24] Make unary builder return `Regex` type consistently. Currently, unary regex component builder simply forwards the component's base type. However, this is inconsistent with non-unary builder results. The current behavior may lead to surprising results when the user marks a property with `@RegexComponentBuilder`. This patch makes `RegexComponentBuilder.buildPartialBlock(first: R)` return a `Regex` rather than `R` itself. --- Before: ```swift // error: cannot convert value of type 'OneOrMore' to specified type 'Regex' @RegexComponentBuilder var r: Regex { OneOrMore("a") // Adding other components below will make the error go away. } struct MyCustomRegex: RegexComponent { // error: cannot convert value of type 'OneOrMore' to specified type 'Regex' var regex: Regex { OneOrMore("a") } } ``` After: No errors. --- Sources/RegexBuilder/Builder.swift | 6 ++++-- Tests/RegexBuilderTests/RegexDSLTests.swift | 22 ++++++++++++++++++++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/Sources/RegexBuilder/Builder.swift b/Sources/RegexBuilder/Builder.swift index a50f069ec..dfafd3803 100644 --- a/Sources/RegexBuilder/Builder.swift +++ b/Sources/RegexBuilder/Builder.swift @@ -18,8 +18,10 @@ public enum RegexComponentBuilder { .init(node: .empty) } - public static func buildPartialBlock(first: R ) -> R { - first + public static func buildPartialBlock( + first component: R + ) -> Regex { + component.regex } public static func buildExpression(_ regex: R) -> R { diff --git a/Tests/RegexBuilderTests/RegexDSLTests.swift b/Tests/RegexBuilderTests/RegexDSLTests.swift index 5b3914e3b..fc31e575f 100644 --- a/Tests/RegexBuilderTests/RegexDSLTests.swift +++ b/Tests/RegexBuilderTests/RegexDSLTests.swift @@ -1015,7 +1015,7 @@ class RegexDSLTests: XCTestCase { XCTAssertEqual(str.wholeMatch(of: parser)?.1, version) } } - + func testZeroWidthConsumer() throws { struct Trace: CustomConsumingRegexComponent { typealias RegexOutput = Void @@ -1051,6 +1051,26 @@ class RegexDSLTests: XCTestCase { """) } + + func testRegexComponentBuilderResultType() { + // Test that the user can declare a closure or computed property marked with + // `@RegexComponentBuilder` with `Regex` as the result type. + @RegexComponentBuilder + var unaryWithSingleNonRegex: Regex { + OneOrMore("a") + } + @RegexComponentBuilder + var multiComponent: Regex { + OneOrMore("a") + "b" + } + struct MyCustomRegex: RegexComponent { + @RegexComponentBuilder + var regex: Regex { + OneOrMore("a") + } + } + } } extension Unicode.Scalar { From 4cea05a395e62d9537f45447f58d12f5528709de Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Mon, 20 Jun 2022 18:57:32 -0600 Subject: [PATCH 12/24] [benchmark] Simplify and add more benchmarks (#501) * [benchmark] Add no-capture version of grapheme breaking exercise * [benchmark] Add cross-engine benchmark helpers * [benchmark] Hangul Syllable finding benchmark --- Sources/RegexBenchmark/Benchmark.swift | 86 +- Sources/RegexBenchmark/CLI.swift | 5 +- .../Inputs/GraphemeBreakData.swift | 1452 +++++++++++++++++ .../RegexBenchmark/Suite/Backtracking.swift | 45 - Sources/RegexBenchmark/Suite/CssRegex.swift | 22 +- Sources/RegexBenchmark/Suite/FirstMatch.swift | 49 - .../RegexBenchmark/Suite/GraphemeBreak.swift | 25 + Sources/RegexBenchmark/Suite/NotFound.swift | 16 + .../RegexBenchmark/Suite/ReluctantQuant.swift | 54 +- 9 files changed, 1603 insertions(+), 151 deletions(-) create mode 100644 Sources/RegexBenchmark/Inputs/GraphemeBreakData.swift delete mode 100644 Sources/RegexBenchmark/Suite/Backtracking.swift delete mode 100644 Sources/RegexBenchmark/Suite/FirstMatch.swift create mode 100644 Sources/RegexBenchmark/Suite/GraphemeBreak.swift create mode 100644 Sources/RegexBenchmark/Suite/NotFound.swift diff --git a/Sources/RegexBenchmark/Benchmark.swift b/Sources/RegexBenchmark/Benchmark.swift index 93a825a93..76751f65e 100644 --- a/Sources/RegexBenchmark/Benchmark.swift +++ b/Sources/RegexBenchmark/Benchmark.swift @@ -9,7 +9,7 @@ public protocol RegexBenchmark { public struct Benchmark: RegexBenchmark { public let name: String let regex: Regex - let ty: MatchType + let type: MatchType let target: String public enum MatchType { @@ -19,7 +19,7 @@ public struct Benchmark: RegexBenchmark { } public func run() { - switch ty { + switch type { case .whole: blackHole(target.wholeMatch(of: regex)) case .allMatches: blackHole(target.matches(of: regex)) case .first: blackHole(target.firstMatch(of: regex)) @@ -30,7 +30,7 @@ public struct Benchmark: RegexBenchmark { public struct NSBenchmark: RegexBenchmark { public let name: String let regex: NSRegularExpression - let ty: NSMatchType + let type: NSMatchType let target: String var range: NSRange { @@ -38,13 +38,13 @@ public struct NSBenchmark: RegexBenchmark { } public enum NSMatchType { - case all + case allMatches case first } public func run() { - switch ty { - case .all: blackHole(regex.matches(in: target, range: range)) + switch type { + case .allMatches: blackHole(regex.matches(in: target, range: range)) case .first: blackHole(regex.firstMatch(in: target, range: range)) } } @@ -110,6 +110,80 @@ public struct BenchmarkRunner { } } +/// A benchmark meant to be ran across multiple engines +struct CrossBenchmark { + /// The base name of the benchmark + var baseName: String + + /// The string to compile in differnet engines + var regex: String + + /// The text to search + var input: String + + // TODO: var output, for validation + + /// Whether this is whole string matching or a searching benchmark + /// + /// TODO: Probably better ot have a whole-line vs search anywhere, maybe + /// accomodate multi-line matching, etc. + var isWhole: Bool = false + + func register(_ runner: inout BenchmarkRunner) { + let swiftRegex = try! Regex(regex, as: Substring.self) + + let nsPattern = isWhole ? "^" + regex + "$" : regex + let nsRegex: NSRegularExpression + if isWhole { + nsRegex = try! NSRegularExpression(pattern: "^" + regex + "$") + } else { + nsRegex = try! NSRegularExpression(pattern: regex) + } + + if isWhole { + runner.register( + Benchmark( + name: baseName + "Whole", + regex: swiftRegex, + type: .whole, + target: input)) + runner.register( + NSBenchmark( + name: baseName + "Whole_NS", + regex: nsRegex, + type: .first, + target: input)) + } else { + runner.register( + Benchmark( + name: baseName + "First", + regex: swiftRegex, + type: .first, + target: input)) + runner.register( + Benchmark( + name: baseName + "All", + regex: swiftRegex, + type: .allMatches, + target: input)) + runner.register( + NSBenchmark( + name: baseName + "First_NS", + regex: nsRegex, + type: .first, + target: input)) + runner.register( + NSBenchmark( + name: baseName + "All_NS", + regex: nsRegex, + type: .allMatches, + target: input)) + } + } +} + +// TODO: Capture-containing benchmarks + // nom nom nom, consume the argument @inline(never) public func blackHole(_ x: T) { diff --git a/Sources/RegexBenchmark/CLI.swift b/Sources/RegexBenchmark/CLI.swift index 004d1f681..bdac2e6c4 100644 --- a/Sources/RegexBenchmark/CLI.swift +++ b/Sources/RegexBenchmark/CLI.swift @@ -14,9 +14,10 @@ struct Runner: ParsableCommand { func makeRunner() -> BenchmarkRunner { var benchmark = BenchmarkRunner("RegexBench", samples) benchmark.addReluctantQuant() - benchmark.addBacktracking() benchmark.addCSS() - benchmark.addFirstMatch() + benchmark.addNotFound() + benchmark.addGraphemeBreak() + benchmark.addHangulSyllable() return benchmark } mutating func run() throws { diff --git a/Sources/RegexBenchmark/Inputs/GraphemeBreakData.swift b/Sources/RegexBenchmark/Inputs/GraphemeBreakData.swift new file mode 100644 index 000000000..9f1abb615 --- /dev/null +++ b/Sources/RegexBenchmark/Inputs/GraphemeBreakData.swift @@ -0,0 +1,1452 @@ +extension Inputs { + static let graphemeBreakData = """ +# GraphemeBreakProperty-13.0.0.txt +# Date: 2019-10-21, 14:30:35 GMT +# © 2019 Unicode®, Inc. +# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries. +# For terms of use, see http://www.unicode.org/terms_of_use.html +# +# Unicode Character Database +# For documentation, see http://www.unicode.org/reports/tr44/ + +# ================================================ + +# Property: Grapheme_Cluster_Break + +# All code points not explicitly listed for Grapheme_Cluster_Break +# have the value Other (XX). + +# @missing: 0000..10FFFF; Other + +# ================================================ + +0600..0605 ; Prepend # Cf [6] ARABIC NUMBER SIGN..ARABIC NUMBER MARK ABOVE +06DD ; Prepend # Cf ARABIC END OF AYAH +070F ; Prepend # Cf SYRIAC ABBREVIATION MARK +08E2 ; Prepend # Cf ARABIC DISPUTED END OF AYAH +0D4E ; Prepend # Lo MALAYALAM LETTER DOT REPH +110BD ; Prepend # Cf KAITHI NUMBER SIGN +110CD ; Prepend # Cf KAITHI NUMBER SIGN ABOVE +111C2..111C3 ; Prepend # Lo [2] SHARADA SIGN JIHVAMULIYA..SHARADA SIGN UPADHMANIYA +1193F ; Prepend # Lo DIVES AKURU PREFIXED NASAL SIGN +11941 ; Prepend # Lo DIVES AKURU INITIAL RA +11A3A ; Prepend # Lo ZANABAZAR SQUARE CLUSTER-INITIAL LETTER RA +11A84..11A89 ; Prepend # Lo [6] SOYOMBO SIGN JIHVAMULIYA..SOYOMBO CLUSTER-INITIAL LETTER SA +11D46 ; Prepend # Lo MASARAM GONDI REPHA + +# Total code points: 24 + +# ================================================ + +000D ; CR # Cc + +# Total code points: 1 + +# ================================================ + +000A ; LF # Cc + +# Total code points: 1 + +# ================================================ + +0000..0009 ; Control # Cc [10] .. +000B..000C ; Control # Cc [2] .. +000E..001F ; Control # Cc [18] .. +007F..009F ; Control # Cc [33] .. +00AD ; Control # Cf SOFT HYPHEN +061C ; Control # Cf ARABIC LETTER MARK +180E ; Control # Cf MONGOLIAN VOWEL SEPARATOR +200B ; Control # Cf ZERO WIDTH SPACE +200E..200F ; Control # Cf [2] LEFT-TO-RIGHT MARK..RIGHT-TO-LEFT MARK +2028 ; Control # Zl LINE SEPARATOR +2029 ; Control # Zp PARAGRAPH SEPARATOR +202A..202E ; Control # Cf [5] LEFT-TO-RIGHT EMBEDDING..RIGHT-TO-LEFT OVERRIDE +2060..2064 ; Control # Cf [5] WORD JOINER..INVISIBLE PLUS +2065 ; Control # Cn +2066..206F ; Control # Cf [10] LEFT-TO-RIGHT ISOLATE..NOMINAL DIGIT SHAPES +FEFF ; Control # Cf ZERO WIDTH NO-BREAK SPACE +FFF0..FFF8 ; Control # Cn [9] .. +FFF9..FFFB ; Control # Cf [3] INTERLINEAR ANNOTATION ANCHOR..INTERLINEAR ANNOTATION TERMINATOR +13430..13438 ; Control # Cf [9] EGYPTIAN HIEROGLYPH VERTICAL JOINER..EGYPTIAN HIEROGLYPH END SEGMENT +1BCA0..1BCA3 ; Control # Cf [4] SHORTHAND FORMAT LETTER OVERLAP..SHORTHAND FORMAT UP STEP +1D173..1D17A ; Control # Cf [8] MUSICAL SYMBOL BEGIN BEAM..MUSICAL SYMBOL END PHRASE +E0000 ; Control # Cn +E0001 ; Control # Cf LANGUAGE TAG +E0002..E001F ; Control # Cn [30] .. +E0080..E00FF ; Control # Cn [128] .. +E01F0..E0FFF ; Control # Cn [3600] .. + +# Total code points: 3886 + +# ================================================ + +0300..036F ; Extend # Mn [112] COMBINING GRAVE ACCENT..COMBINING LATIN SMALL LETTER X +0483..0487 ; Extend # Mn [5] COMBINING CYRILLIC TITLO..COMBINING CYRILLIC POKRYTIE +0488..0489 ; Extend # Me [2] COMBINING CYRILLIC HUNDRED THOUSANDS SIGN..COMBINING CYRILLIC MILLIONS SIGN +0591..05BD ; Extend # Mn [45] HEBREW ACCENT ETNAHTA..HEBREW POINT METEG +05BF ; Extend # Mn HEBREW POINT RAFE +05C1..05C2 ; Extend # Mn [2] HEBREW POINT SHIN DOT..HEBREW POINT SIN DOT +05C4..05C5 ; Extend # Mn [2] HEBREW MARK UPPER DOT..HEBREW MARK LOWER DOT +05C7 ; Extend # Mn HEBREW POINT QAMATS QATAN +0610..061A ; Extend # Mn [11] ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM..ARABIC SMALL KASRA +064B..065F ; Extend # Mn [21] ARABIC FATHATAN..ARABIC WAVY HAMZA BELOW +0670 ; Extend # Mn ARABIC LETTER SUPERSCRIPT ALEF +06D6..06DC ; Extend # Mn [7] ARABIC SMALL HIGH LIGATURE SAD WITH LAM WITH ALEF MAKSURA..ARABIC SMALL HIGH SEEN +06DF..06E4 ; Extend # Mn [6] ARABIC SMALL HIGH ROUNDED ZERO..ARABIC SMALL HIGH MADDA +06E7..06E8 ; Extend # Mn [2] ARABIC SMALL HIGH YEH..ARABIC SMALL HIGH NOON +06EA..06ED ; Extend # Mn [4] ARABIC EMPTY CENTRE LOW STOP..ARABIC SMALL LOW MEEM +0711 ; Extend # Mn SYRIAC LETTER SUPERSCRIPT ALAPH +0730..074A ; Extend # Mn [27] SYRIAC PTHAHA ABOVE..SYRIAC BARREKH +07A6..07B0 ; Extend # Mn [11] THAANA ABAFILI..THAANA SUKUN +07EB..07F3 ; Extend # Mn [9] NKO COMBINING SHORT HIGH TONE..NKO COMBINING DOUBLE DOT ABOVE +07FD ; Extend # Mn NKO DANTAYALAN +0816..0819 ; Extend # Mn [4] SAMARITAN MARK IN..SAMARITAN MARK DAGESH +081B..0823 ; Extend # Mn [9] SAMARITAN MARK EPENTHETIC YUT..SAMARITAN VOWEL SIGN A +0825..0827 ; Extend # Mn [3] SAMARITAN VOWEL SIGN SHORT A..SAMARITAN VOWEL SIGN U +0829..082D ; Extend # Mn [5] SAMARITAN VOWEL SIGN LONG I..SAMARITAN MARK NEQUDAA +0859..085B ; Extend # Mn [3] MANDAIC AFFRICATION MARK..MANDAIC GEMINATION MARK +08D3..08E1 ; Extend # Mn [15] ARABIC SMALL LOW WAW..ARABIC SMALL HIGH SIGN SAFHA +08E3..0902 ; Extend # Mn [32] ARABIC TURNED DAMMA BELOW..DEVANAGARI SIGN ANUSVARA +093A ; Extend # Mn DEVANAGARI VOWEL SIGN OE +093C ; Extend # Mn DEVANAGARI SIGN NUKTA +0941..0948 ; Extend # Mn [8] DEVANAGARI VOWEL SIGN U..DEVANAGARI VOWEL SIGN AI +094D ; Extend # Mn DEVANAGARI SIGN VIRAMA +0951..0957 ; Extend # Mn [7] DEVANAGARI STRESS SIGN UDATTA..DEVANAGARI VOWEL SIGN UUE +0962..0963 ; Extend # Mn [2] DEVANAGARI VOWEL SIGN VOCALIC L..DEVANAGARI VOWEL SIGN VOCALIC LL +0981 ; Extend # Mn BENGALI SIGN CANDRABINDU +09BC ; Extend # Mn BENGALI SIGN NUKTA +09BE ; Extend # Mc BENGALI VOWEL SIGN AA +09C1..09C4 ; Extend # Mn [4] BENGALI VOWEL SIGN U..BENGALI VOWEL SIGN VOCALIC RR +09CD ; Extend # Mn BENGALI SIGN VIRAMA +09D7 ; Extend # Mc BENGALI AU LENGTH MARK +09E2..09E3 ; Extend # Mn [2] BENGALI VOWEL SIGN VOCALIC L..BENGALI VOWEL SIGN VOCALIC LL +09FE ; Extend # Mn BENGALI SANDHI MARK +0A01..0A02 ; Extend # Mn [2] GURMUKHI SIGN ADAK BINDI..GURMUKHI SIGN BINDI +0A3C ; Extend # Mn GURMUKHI SIGN NUKTA +0A41..0A42 ; Extend # Mn [2] GURMUKHI VOWEL SIGN U..GURMUKHI VOWEL SIGN UU +0A47..0A48 ; Extend # Mn [2] GURMUKHI VOWEL SIGN EE..GURMUKHI VOWEL SIGN AI +0A4B..0A4D ; Extend # Mn [3] GURMUKHI VOWEL SIGN OO..GURMUKHI SIGN VIRAMA +0A51 ; Extend # Mn GURMUKHI SIGN UDAAT +0A70..0A71 ; Extend # Mn [2] GURMUKHI TIPPI..GURMUKHI ADDAK +0A75 ; Extend # Mn GURMUKHI SIGN YAKASH +0A81..0A82 ; Extend # Mn [2] GUJARATI SIGN CANDRABINDU..GUJARATI SIGN ANUSVARA +0ABC ; Extend # Mn GUJARATI SIGN NUKTA +0AC1..0AC5 ; Extend # Mn [5] GUJARATI VOWEL SIGN U..GUJARATI VOWEL SIGN CANDRA E +0AC7..0AC8 ; Extend # Mn [2] GUJARATI VOWEL SIGN E..GUJARATI VOWEL SIGN AI +0ACD ; Extend # Mn GUJARATI SIGN VIRAMA +0AE2..0AE3 ; Extend # Mn [2] GUJARATI VOWEL SIGN VOCALIC L..GUJARATI VOWEL SIGN VOCALIC LL +0AFA..0AFF ; Extend # Mn [6] GUJARATI SIGN SUKUN..GUJARATI SIGN TWO-CIRCLE NUKTA ABOVE +0B01 ; Extend # Mn ORIYA SIGN CANDRABINDU +0B3C ; Extend # Mn ORIYA SIGN NUKTA +0B3E ; Extend # Mc ORIYA VOWEL SIGN AA +0B3F ; Extend # Mn ORIYA VOWEL SIGN I +0B41..0B44 ; Extend # Mn [4] ORIYA VOWEL SIGN U..ORIYA VOWEL SIGN VOCALIC RR +0B4D ; Extend # Mn ORIYA SIGN VIRAMA +0B55..0B56 ; Extend # Mn [2] ORIYA SIGN OVERLINE..ORIYA AI LENGTH MARK +0B57 ; Extend # Mc ORIYA AU LENGTH MARK +0B62..0B63 ; Extend # Mn [2] ORIYA VOWEL SIGN VOCALIC L..ORIYA VOWEL SIGN VOCALIC LL +0B82 ; Extend # Mn TAMIL SIGN ANUSVARA +0BBE ; Extend # Mc TAMIL VOWEL SIGN AA +0BC0 ; Extend # Mn TAMIL VOWEL SIGN II +0BCD ; Extend # Mn TAMIL SIGN VIRAMA +0BD7 ; Extend # Mc TAMIL AU LENGTH MARK +0C00 ; Extend # Mn TELUGU SIGN COMBINING CANDRABINDU ABOVE +0C04 ; Extend # Mn TELUGU SIGN COMBINING ANUSVARA ABOVE +0C3E..0C40 ; Extend # Mn [3] TELUGU VOWEL SIGN AA..TELUGU VOWEL SIGN II +0C46..0C48 ; Extend # Mn [3] TELUGU VOWEL SIGN E..TELUGU VOWEL SIGN AI +0C4A..0C4D ; Extend # Mn [4] TELUGU VOWEL SIGN O..TELUGU SIGN VIRAMA +0C55..0C56 ; Extend # Mn [2] TELUGU LENGTH MARK..TELUGU AI LENGTH MARK +0C62..0C63 ; Extend # Mn [2] TELUGU VOWEL SIGN VOCALIC L..TELUGU VOWEL SIGN VOCALIC LL +0C81 ; Extend # Mn KANNADA SIGN CANDRABINDU +0CBC ; Extend # Mn KANNADA SIGN NUKTA +0CBF ; Extend # Mn KANNADA VOWEL SIGN I +0CC2 ; Extend # Mc KANNADA VOWEL SIGN UU +0CC6 ; Extend # Mn KANNADA VOWEL SIGN E +0CCC..0CCD ; Extend # Mn [2] KANNADA VOWEL SIGN AU..KANNADA SIGN VIRAMA +0CD5..0CD6 ; Extend # Mc [2] KANNADA LENGTH MARK..KANNADA AI LENGTH MARK +0CE2..0CE3 ; Extend # Mn [2] KANNADA VOWEL SIGN VOCALIC L..KANNADA VOWEL SIGN VOCALIC LL +0D00..0D01 ; Extend # Mn [2] MALAYALAM SIGN COMBINING ANUSVARA ABOVE..MALAYALAM SIGN CANDRABINDU +0D3B..0D3C ; Extend # Mn [2] MALAYALAM SIGN VERTICAL BAR VIRAMA..MALAYALAM SIGN CIRCULAR VIRAMA +0D3E ; Extend # Mc MALAYALAM VOWEL SIGN AA +0D41..0D44 ; Extend # Mn [4] MALAYALAM VOWEL SIGN U..MALAYALAM VOWEL SIGN VOCALIC RR +0D4D ; Extend # Mn MALAYALAM SIGN VIRAMA +0D57 ; Extend # Mc MALAYALAM AU LENGTH MARK +0D62..0D63 ; Extend # Mn [2] MALAYALAM VOWEL SIGN VOCALIC L..MALAYALAM VOWEL SIGN VOCALIC LL +0D81 ; Extend # Mn SINHALA SIGN CANDRABINDU +0DCA ; Extend # Mn SINHALA SIGN AL-LAKUNA +0DCF ; Extend # Mc SINHALA VOWEL SIGN AELA-PILLA +0DD2..0DD4 ; Extend # Mn [3] SINHALA VOWEL SIGN KETTI IS-PILLA..SINHALA VOWEL SIGN KETTI PAA-PILLA +0DD6 ; Extend # Mn SINHALA VOWEL SIGN DIGA PAA-PILLA +0DDF ; Extend # Mc SINHALA VOWEL SIGN GAYANUKITTA +0E31 ; Extend # Mn THAI CHARACTER MAI HAN-AKAT +0E34..0E3A ; Extend # Mn [7] THAI CHARACTER SARA I..THAI CHARACTER PHINTHU +0E47..0E4E ; Extend # Mn [8] THAI CHARACTER MAITAIKHU..THAI CHARACTER YAMAKKAN +0EB1 ; Extend # Mn LAO VOWEL SIGN MAI KAN +0EB4..0EBC ; Extend # Mn [9] LAO VOWEL SIGN I..LAO SEMIVOWEL SIGN LO +0EC8..0ECD ; Extend # Mn [6] LAO TONE MAI EK..LAO NIGGAHITA +0F18..0F19 ; Extend # Mn [2] TIBETAN ASTROLOGICAL SIGN -KHYUD PA..TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS +0F35 ; Extend # Mn TIBETAN MARK NGAS BZUNG NYI ZLA +0F37 ; Extend # Mn TIBETAN MARK NGAS BZUNG SGOR RTAGS +0F39 ; Extend # Mn TIBETAN MARK TSA -PHRU +0F71..0F7E ; Extend # Mn [14] TIBETAN VOWEL SIGN AA..TIBETAN SIGN RJES SU NGA RO +0F80..0F84 ; Extend # Mn [5] TIBETAN VOWEL SIGN REVERSED I..TIBETAN MARK HALANTA +0F86..0F87 ; Extend # Mn [2] TIBETAN SIGN LCI RTAGS..TIBETAN SIGN YANG RTAGS +0F8D..0F97 ; Extend # Mn [11] TIBETAN SUBJOINED SIGN LCE TSA CAN..TIBETAN SUBJOINED LETTER JA +0F99..0FBC ; Extend # Mn [36] TIBETAN SUBJOINED LETTER NYA..TIBETAN SUBJOINED LETTER FIXED-FORM RA +0FC6 ; Extend # Mn TIBETAN SYMBOL PADMA GDAN +102D..1030 ; Extend # Mn [4] MYANMAR VOWEL SIGN I..MYANMAR VOWEL SIGN UU +1032..1037 ; Extend # Mn [6] MYANMAR VOWEL SIGN AI..MYANMAR SIGN DOT BELOW +1039..103A ; Extend # Mn [2] MYANMAR SIGN VIRAMA..MYANMAR SIGN ASAT +103D..103E ; Extend # Mn [2] MYANMAR CONSONANT SIGN MEDIAL WA..MYANMAR CONSONANT SIGN MEDIAL HA +1058..1059 ; Extend # Mn [2] MYANMAR VOWEL SIGN VOCALIC L..MYANMAR VOWEL SIGN VOCALIC LL +105E..1060 ; Extend # Mn [3] MYANMAR CONSONANT SIGN MON MEDIAL NA..MYANMAR CONSONANT SIGN MON MEDIAL LA +1071..1074 ; Extend # Mn [4] MYANMAR VOWEL SIGN GEBA KAREN I..MYANMAR VOWEL SIGN KAYAH EE +1082 ; Extend # Mn MYANMAR CONSONANT SIGN SHAN MEDIAL WA +1085..1086 ; Extend # Mn [2] MYANMAR VOWEL SIGN SHAN E ABOVE..MYANMAR VOWEL SIGN SHAN FINAL Y +108D ; Extend # Mn MYANMAR SIGN SHAN COUNCIL EMPHATIC TONE +109D ; Extend # Mn MYANMAR VOWEL SIGN AITON AI +135D..135F ; Extend # Mn [3] ETHIOPIC COMBINING GEMINATION AND VOWEL LENGTH MARK..ETHIOPIC COMBINING GEMINATION MARK +1712..1714 ; Extend # Mn [3] TAGALOG VOWEL SIGN I..TAGALOG SIGN VIRAMA +1732..1734 ; Extend # Mn [3] HANUNOO VOWEL SIGN I..HANUNOO SIGN PAMUDPOD +1752..1753 ; Extend # Mn [2] BUHID VOWEL SIGN I..BUHID VOWEL SIGN U +1772..1773 ; Extend # Mn [2] TAGBANWA VOWEL SIGN I..TAGBANWA VOWEL SIGN U +17B4..17B5 ; Extend # Mn [2] KHMER VOWEL INHERENT AQ..KHMER VOWEL INHERENT AA +17B7..17BD ; Extend # Mn [7] KHMER VOWEL SIGN I..KHMER VOWEL SIGN UA +17C6 ; Extend # Mn KHMER SIGN NIKAHIT +17C9..17D3 ; Extend # Mn [11] KHMER SIGN MUUSIKATOAN..KHMER SIGN BATHAMASAT +17DD ; Extend # Mn KHMER SIGN ATTHACAN +180B..180D ; Extend # Mn [3] MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE +1885..1886 ; Extend # Mn [2] MONGOLIAN LETTER ALI GALI BALUDA..MONGOLIAN LETTER ALI GALI THREE BALUDA +18A9 ; Extend # Mn MONGOLIAN LETTER ALI GALI DAGALGA +1920..1922 ; Extend # Mn [3] LIMBU VOWEL SIGN A..LIMBU VOWEL SIGN U +1927..1928 ; Extend # Mn [2] LIMBU VOWEL SIGN E..LIMBU VOWEL SIGN O +1932 ; Extend # Mn LIMBU SMALL LETTER ANUSVARA +1939..193B ; Extend # Mn [3] LIMBU SIGN MUKPHRENG..LIMBU SIGN SA-I +1A17..1A18 ; Extend # Mn [2] BUGINESE VOWEL SIGN I..BUGINESE VOWEL SIGN U +1A1B ; Extend # Mn BUGINESE VOWEL SIGN AE +1A56 ; Extend # Mn TAI THAM CONSONANT SIGN MEDIAL LA +1A58..1A5E ; Extend # Mn [7] TAI THAM SIGN MAI KANG LAI..TAI THAM CONSONANT SIGN SA +1A60 ; Extend # Mn TAI THAM SIGN SAKOT +1A62 ; Extend # Mn TAI THAM VOWEL SIGN MAI SAT +1A65..1A6C ; Extend # Mn [8] TAI THAM VOWEL SIGN I..TAI THAM VOWEL SIGN OA BELOW +1A73..1A7C ; Extend # Mn [10] TAI THAM VOWEL SIGN OA ABOVE..TAI THAM SIGN KHUEN-LUE KARAN +1A7F ; Extend # Mn TAI THAM COMBINING CRYPTOGRAMMIC DOT +1AB0..1ABD ; Extend # Mn [14] COMBINING DOUBLED CIRCUMFLEX ACCENT..COMBINING PARENTHESES BELOW +1ABE ; Extend # Me COMBINING PARENTHESES OVERLAY +1ABF..1AC0 ; Extend # Mn [2] COMBINING LATIN SMALL LETTER W BELOW..COMBINING LATIN SMALL LETTER TURNED W BELOW +1B00..1B03 ; Extend # Mn [4] BALINESE SIGN ULU RICEM..BALINESE SIGN SURANG +1B34 ; Extend # Mn BALINESE SIGN REREKAN +1B35 ; Extend # Mc BALINESE VOWEL SIGN TEDUNG +1B36..1B3A ; Extend # Mn [5] BALINESE VOWEL SIGN ULU..BALINESE VOWEL SIGN RA REPA +1B3C ; Extend # Mn BALINESE VOWEL SIGN LA LENGA +1B42 ; Extend # Mn BALINESE VOWEL SIGN PEPET +1B6B..1B73 ; Extend # Mn [9] BALINESE MUSICAL SYMBOL COMBINING TEGEH..BALINESE MUSICAL SYMBOL COMBINING GONG +1B80..1B81 ; Extend # Mn [2] SUNDANESE SIGN PANYECEK..SUNDANESE SIGN PANGLAYAR +1BA2..1BA5 ; Extend # Mn [4] SUNDANESE CONSONANT SIGN PANYAKRA..SUNDANESE VOWEL SIGN PANYUKU +1BA8..1BA9 ; Extend # Mn [2] SUNDANESE VOWEL SIGN PAMEPET..SUNDANESE VOWEL SIGN PANEULEUNG +1BAB..1BAD ; Extend # Mn [3] SUNDANESE SIGN VIRAMA..SUNDANESE CONSONANT SIGN PASANGAN WA +1BE6 ; Extend # Mn BATAK SIGN TOMPI +1BE8..1BE9 ; Extend # Mn [2] BATAK VOWEL SIGN PAKPAK E..BATAK VOWEL SIGN EE +1BED ; Extend # Mn BATAK VOWEL SIGN KARO O +1BEF..1BF1 ; Extend # Mn [3] BATAK VOWEL SIGN U FOR SIMALUNGUN SA..BATAK CONSONANT SIGN H +1C2C..1C33 ; Extend # Mn [8] LEPCHA VOWEL SIGN E..LEPCHA CONSONANT SIGN T +1C36..1C37 ; Extend # Mn [2] LEPCHA SIGN RAN..LEPCHA SIGN NUKTA +1CD0..1CD2 ; Extend # Mn [3] VEDIC TONE KARSHANA..VEDIC TONE PRENKHA +1CD4..1CE0 ; Extend # Mn [13] VEDIC SIGN YAJURVEDIC MIDLINE SVARITA..VEDIC TONE RIGVEDIC KASHMIRI INDEPENDENT SVARITA +1CE2..1CE8 ; Extend # Mn [7] VEDIC SIGN VISARGA SVARITA..VEDIC SIGN VISARGA ANUDATTA WITH TAIL +1CED ; Extend # Mn VEDIC SIGN TIRYAK +1CF4 ; Extend # Mn VEDIC TONE CANDRA ABOVE +1CF8..1CF9 ; Extend # Mn [2] VEDIC TONE RING ABOVE..VEDIC TONE DOUBLE RING ABOVE +1DC0..1DF9 ; Extend # Mn [58] COMBINING DOTTED GRAVE ACCENT..COMBINING WIDE INVERTED BRIDGE BELOW +1DFB..1DFF ; Extend # Mn [5] COMBINING DELETION MARK..COMBINING RIGHT ARROWHEAD AND DOWN ARROWHEAD BELOW +200C ; Extend # Cf ZERO WIDTH NON-JOINER +20D0..20DC ; Extend # Mn [13] COMBINING LEFT HARPOON ABOVE..COMBINING FOUR DOTS ABOVE +20DD..20E0 ; Extend # Me [4] COMBINING ENCLOSING CIRCLE..COMBINING ENCLOSING CIRCLE BACKSLASH +20E1 ; Extend # Mn COMBINING LEFT RIGHT ARROW ABOVE +20E2..20E4 ; Extend # Me [3] COMBINING ENCLOSING SCREEN..COMBINING ENCLOSING UPWARD POINTING TRIANGLE +20E5..20F0 ; Extend # Mn [12] COMBINING REVERSE SOLIDUS OVERLAY..COMBINING ASTERISK ABOVE +2CEF..2CF1 ; Extend # Mn [3] COPTIC COMBINING NI ABOVE..COPTIC COMBINING SPIRITUS LENIS +2D7F ; Extend # Mn TIFINAGH CONSONANT JOINER +2DE0..2DFF ; Extend # Mn [32] COMBINING CYRILLIC LETTER BE..COMBINING CYRILLIC LETTER IOTIFIED BIG YUS +302A..302D ; Extend # Mn [4] IDEOGRAPHIC LEVEL TONE MARK..IDEOGRAPHIC ENTERING TONE MARK +302E..302F ; Extend # Mc [2] HANGUL SINGLE DOT TONE MARK..HANGUL DOUBLE DOT TONE MARK +3099..309A ; Extend # Mn [2] COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK..COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK +A66F ; Extend # Mn COMBINING CYRILLIC VZMET +A670..A672 ; Extend # Me [3] COMBINING CYRILLIC TEN MILLIONS SIGN..COMBINING CYRILLIC THOUSAND MILLIONS SIGN +A674..A67D ; Extend # Mn [10] COMBINING CYRILLIC LETTER UKRAINIAN IE..COMBINING CYRILLIC PAYEROK +A69E..A69F ; Extend # Mn [2] COMBINING CYRILLIC LETTER EF..COMBINING CYRILLIC LETTER IOTIFIED E +A6F0..A6F1 ; Extend # Mn [2] BAMUM COMBINING MARK KOQNDON..BAMUM COMBINING MARK TUKWENTIS +A802 ; Extend # Mn SYLOTI NAGRI SIGN DVISVARA +A806 ; Extend # Mn SYLOTI NAGRI SIGN HASANTA +A80B ; Extend # Mn SYLOTI NAGRI SIGN ANUSVARA +A825..A826 ; Extend # Mn [2] SYLOTI NAGRI VOWEL SIGN U..SYLOTI NAGRI VOWEL SIGN E +A82C ; Extend # Mn SYLOTI NAGRI SIGN ALTERNATE HASANTA +A8C4..A8C5 ; Extend # Mn [2] SAURASHTRA SIGN VIRAMA..SAURASHTRA SIGN CANDRABINDU +A8E0..A8F1 ; Extend # Mn [18] COMBINING DEVANAGARI DIGIT ZERO..COMBINING DEVANAGARI SIGN AVAGRAHA +A8FF ; Extend # Mn DEVANAGARI VOWEL SIGN AY +A926..A92D ; Extend # Mn [8] KAYAH LI VOWEL UE..KAYAH LI TONE CALYA PLOPHU +A947..A951 ; Extend # Mn [11] REJANG VOWEL SIGN I..REJANG CONSONANT SIGN R +A980..A982 ; Extend # Mn [3] JAVANESE SIGN PANYANGGA..JAVANESE SIGN LAYAR +A9B3 ; Extend # Mn JAVANESE SIGN CECAK TELU +A9B6..A9B9 ; Extend # Mn [4] JAVANESE VOWEL SIGN WULU..JAVANESE VOWEL SIGN SUKU MENDUT +A9BC..A9BD ; Extend # Mn [2] JAVANESE VOWEL SIGN PEPET..JAVANESE CONSONANT SIGN KERET +A9E5 ; Extend # Mn MYANMAR SIGN SHAN SAW +AA29..AA2E ; Extend # Mn [6] CHAM VOWEL SIGN AA..CHAM VOWEL SIGN OE +AA31..AA32 ; Extend # Mn [2] CHAM VOWEL SIGN AU..CHAM VOWEL SIGN UE +AA35..AA36 ; Extend # Mn [2] CHAM CONSONANT SIGN LA..CHAM CONSONANT SIGN WA +AA43 ; Extend # Mn CHAM CONSONANT SIGN FINAL NG +AA4C ; Extend # Mn CHAM CONSONANT SIGN FINAL M +AA7C ; Extend # Mn MYANMAR SIGN TAI LAING TONE-2 +AAB0 ; Extend # Mn TAI VIET MAI KANG +AAB2..AAB4 ; Extend # Mn [3] TAI VIET VOWEL I..TAI VIET VOWEL U +AAB7..AAB8 ; Extend # Mn [2] TAI VIET MAI KHIT..TAI VIET VOWEL IA +AABE..AABF ; Extend # Mn [2] TAI VIET VOWEL AM..TAI VIET TONE MAI EK +AAC1 ; Extend # Mn TAI VIET TONE MAI THO +AAEC..AAED ; Extend # Mn [2] MEETEI MAYEK VOWEL SIGN UU..MEETEI MAYEK VOWEL SIGN AAI +AAF6 ; Extend # Mn MEETEI MAYEK VIRAMA +ABE5 ; Extend # Mn MEETEI MAYEK VOWEL SIGN ANAP +ABE8 ; Extend # Mn MEETEI MAYEK VOWEL SIGN UNAP +ABED ; Extend # Mn MEETEI MAYEK APUN IYEK +FB1E ; Extend # Mn HEBREW POINT JUDEO-SPANISH VARIKA +FE00..FE0F ; Extend # Mn [16] VARIATION SELECTOR-1..VARIATION SELECTOR-16 +FE20..FE2F ; Extend # Mn [16] COMBINING LIGATURE LEFT HALF..COMBINING CYRILLIC TITLO RIGHT HALF +FF9E..FF9F ; Extend # Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK +101FD ; Extend # Mn PHAISTOS DISC SIGN COMBINING OBLIQUE STROKE +102E0 ; Extend # Mn COPTIC EPACT THOUSANDS MARK +10376..1037A ; Extend # Mn [5] COMBINING OLD PERMIC LETTER AN..COMBINING OLD PERMIC LETTER SII +10A01..10A03 ; Extend # Mn [3] KHAROSHTHI VOWEL SIGN I..KHAROSHTHI VOWEL SIGN VOCALIC R +10A05..10A06 ; Extend # Mn [2] KHAROSHTHI VOWEL SIGN E..KHAROSHTHI VOWEL SIGN O +10A0C..10A0F ; Extend # Mn [4] KHAROSHTHI VOWEL LENGTH MARK..KHAROSHTHI SIGN VISARGA +10A38..10A3A ; Extend # Mn [3] KHAROSHTHI SIGN BAR ABOVE..KHAROSHTHI SIGN DOT BELOW +10A3F ; Extend # Mn KHAROSHTHI VIRAMA +10AE5..10AE6 ; Extend # Mn [2] MANICHAEAN ABBREVIATION MARK ABOVE..MANICHAEAN ABBREVIATION MARK BELOW +10D24..10D27 ; Extend # Mn [4] HANIFI ROHINGYA SIGN HARBAHAY..HANIFI ROHINGYA SIGN TASSI +10EAB..10EAC ; Extend # Mn [2] YEZIDI COMBINING HAMZA MARK..YEZIDI COMBINING MADDA MARK +10F46..10F50 ; Extend # Mn [11] SOGDIAN COMBINING DOT BELOW..SOGDIAN COMBINING STROKE BELOW +11001 ; Extend # Mn BRAHMI SIGN ANUSVARA +11038..11046 ; Extend # Mn [15] BRAHMI VOWEL SIGN AA..BRAHMI VIRAMA +1107F..11081 ; Extend # Mn [3] BRAHMI NUMBER JOINER..KAITHI SIGN ANUSVARA +110B3..110B6 ; Extend # Mn [4] KAITHI VOWEL SIGN U..KAITHI VOWEL SIGN AI +110B9..110BA ; Extend # Mn [2] KAITHI SIGN VIRAMA..KAITHI SIGN NUKTA +11100..11102 ; Extend # Mn [3] CHAKMA SIGN CANDRABINDU..CHAKMA SIGN VISARGA +11127..1112B ; Extend # Mn [5] CHAKMA VOWEL SIGN A..CHAKMA VOWEL SIGN UU +1112D..11134 ; Extend # Mn [8] CHAKMA VOWEL SIGN AI..CHAKMA MAAYYAA +11173 ; Extend # Mn MAHAJANI SIGN NUKTA +11180..11181 ; Extend # Mn [2] SHARADA SIGN CANDRABINDU..SHARADA SIGN ANUSVARA +111B6..111BE ; Extend # Mn [9] SHARADA VOWEL SIGN U..SHARADA VOWEL SIGN O +111C9..111CC ; Extend # Mn [4] SHARADA SANDHI MARK..SHARADA EXTRA SHORT VOWEL MARK +111CF ; Extend # Mn SHARADA SIGN INVERTED CANDRABINDU +1122F..11231 ; Extend # Mn [3] KHOJKI VOWEL SIGN U..KHOJKI VOWEL SIGN AI +11234 ; Extend # Mn KHOJKI SIGN ANUSVARA +11236..11237 ; Extend # Mn [2] KHOJKI SIGN NUKTA..KHOJKI SIGN SHADDA +1123E ; Extend # Mn KHOJKI SIGN SUKUN +112DF ; Extend # Mn KHUDAWADI SIGN ANUSVARA +112E3..112EA ; Extend # Mn [8] KHUDAWADI VOWEL SIGN U..KHUDAWADI SIGN VIRAMA +11300..11301 ; Extend # Mn [2] GRANTHA SIGN COMBINING ANUSVARA ABOVE..GRANTHA SIGN CANDRABINDU +1133B..1133C ; Extend # Mn [2] COMBINING BINDU BELOW..GRANTHA SIGN NUKTA +1133E ; Extend # Mc GRANTHA VOWEL SIGN AA +11340 ; Extend # Mn GRANTHA VOWEL SIGN II +11357 ; Extend # Mc GRANTHA AU LENGTH MARK +11366..1136C ; Extend # Mn [7] COMBINING GRANTHA DIGIT ZERO..COMBINING GRANTHA DIGIT SIX +11370..11374 ; Extend # Mn [5] COMBINING GRANTHA LETTER A..COMBINING GRANTHA LETTER PA +11438..1143F ; Extend # Mn [8] NEWA VOWEL SIGN U..NEWA VOWEL SIGN AI +11442..11444 ; Extend # Mn [3] NEWA SIGN VIRAMA..NEWA SIGN ANUSVARA +11446 ; Extend # Mn NEWA SIGN NUKTA +1145E ; Extend # Mn NEWA SANDHI MARK +114B0 ; Extend # Mc TIRHUTA VOWEL SIGN AA +114B3..114B8 ; Extend # Mn [6] TIRHUTA VOWEL SIGN U..TIRHUTA VOWEL SIGN VOCALIC LL +114BA ; Extend # Mn TIRHUTA VOWEL SIGN SHORT E +114BD ; Extend # Mc TIRHUTA VOWEL SIGN SHORT O +114BF..114C0 ; Extend # Mn [2] TIRHUTA SIGN CANDRABINDU..TIRHUTA SIGN ANUSVARA +114C2..114C3 ; Extend # Mn [2] TIRHUTA SIGN VIRAMA..TIRHUTA SIGN NUKTA +115AF ; Extend # Mc SIDDHAM VOWEL SIGN AA +115B2..115B5 ; Extend # Mn [4] SIDDHAM VOWEL SIGN U..SIDDHAM VOWEL SIGN VOCALIC RR +115BC..115BD ; Extend # Mn [2] SIDDHAM SIGN CANDRABINDU..SIDDHAM SIGN ANUSVARA +115BF..115C0 ; Extend # Mn [2] SIDDHAM SIGN VIRAMA..SIDDHAM SIGN NUKTA +115DC..115DD ; Extend # Mn [2] SIDDHAM VOWEL SIGN ALTERNATE U..SIDDHAM VOWEL SIGN ALTERNATE UU +11633..1163A ; Extend # Mn [8] MODI VOWEL SIGN U..MODI VOWEL SIGN AI +1163D ; Extend # Mn MODI SIGN ANUSVARA +1163F..11640 ; Extend # Mn [2] MODI SIGN VIRAMA..MODI SIGN ARDHACANDRA +116AB ; Extend # Mn TAKRI SIGN ANUSVARA +116AD ; Extend # Mn TAKRI VOWEL SIGN AA +116B0..116B5 ; Extend # Mn [6] TAKRI VOWEL SIGN U..TAKRI VOWEL SIGN AU +116B7 ; Extend # Mn TAKRI SIGN NUKTA +1171D..1171F ; Extend # Mn [3] AHOM CONSONANT SIGN MEDIAL LA..AHOM CONSONANT SIGN MEDIAL LIGATING RA +11722..11725 ; Extend # Mn [4] AHOM VOWEL SIGN I..AHOM VOWEL SIGN UU +11727..1172B ; Extend # Mn [5] AHOM VOWEL SIGN AW..AHOM SIGN KILLER +1182F..11837 ; Extend # Mn [9] DOGRA VOWEL SIGN U..DOGRA SIGN ANUSVARA +11839..1183A ; Extend # Mn [2] DOGRA SIGN VIRAMA..DOGRA SIGN NUKTA +11930 ; Extend # Mc DIVES AKURU VOWEL SIGN AA +1193B..1193C ; Extend # Mn [2] DIVES AKURU SIGN ANUSVARA..DIVES AKURU SIGN CANDRABINDU +1193E ; Extend # Mn DIVES AKURU VIRAMA +11943 ; Extend # Mn DIVES AKURU SIGN NUKTA +119D4..119D7 ; Extend # Mn [4] NANDINAGARI VOWEL SIGN U..NANDINAGARI VOWEL SIGN VOCALIC RR +119DA..119DB ; Extend # Mn [2] NANDINAGARI VOWEL SIGN E..NANDINAGARI VOWEL SIGN AI +119E0 ; Extend # Mn NANDINAGARI SIGN VIRAMA +11A01..11A0A ; Extend # Mn [10] ZANABAZAR SQUARE VOWEL SIGN I..ZANABAZAR SQUARE VOWEL LENGTH MARK +11A33..11A38 ; Extend # Mn [6] ZANABAZAR SQUARE FINAL CONSONANT MARK..ZANABAZAR SQUARE SIGN ANUSVARA +11A3B..11A3E ; Extend # Mn [4] ZANABAZAR SQUARE CLUSTER-FINAL LETTER YA..ZANABAZAR SQUARE CLUSTER-FINAL LETTER VA +11A47 ; Extend # Mn ZANABAZAR SQUARE SUBJOINER +11A51..11A56 ; Extend # Mn [6] SOYOMBO VOWEL SIGN I..SOYOMBO VOWEL SIGN OE +11A59..11A5B ; Extend # Mn [3] SOYOMBO VOWEL SIGN VOCALIC R..SOYOMBO VOWEL LENGTH MARK +11A8A..11A96 ; Extend # Mn [13] SOYOMBO FINAL CONSONANT SIGN G..SOYOMBO SIGN ANUSVARA +11A98..11A99 ; Extend # Mn [2] SOYOMBO GEMINATION MARK..SOYOMBO SUBJOINER +11C30..11C36 ; Extend # Mn [7] BHAIKSUKI VOWEL SIGN I..BHAIKSUKI VOWEL SIGN VOCALIC L +11C38..11C3D ; Extend # Mn [6] BHAIKSUKI VOWEL SIGN E..BHAIKSUKI SIGN ANUSVARA +11C3F ; Extend # Mn BHAIKSUKI SIGN VIRAMA +11C92..11CA7 ; Extend # Mn [22] MARCHEN SUBJOINED LETTER KA..MARCHEN SUBJOINED LETTER ZA +11CAA..11CB0 ; Extend # Mn [7] MARCHEN SUBJOINED LETTER RA..MARCHEN VOWEL SIGN AA +11CB2..11CB3 ; Extend # Mn [2] MARCHEN VOWEL SIGN U..MARCHEN VOWEL SIGN E +11CB5..11CB6 ; Extend # Mn [2] MARCHEN SIGN ANUSVARA..MARCHEN SIGN CANDRABINDU +11D31..11D36 ; Extend # Mn [6] MASARAM GONDI VOWEL SIGN AA..MASARAM GONDI VOWEL SIGN VOCALIC R +11D3A ; Extend # Mn MASARAM GONDI VOWEL SIGN E +11D3C..11D3D ; Extend # Mn [2] MASARAM GONDI VOWEL SIGN AI..MASARAM GONDI VOWEL SIGN O +11D3F..11D45 ; Extend # Mn [7] MASARAM GONDI VOWEL SIGN AU..MASARAM GONDI VIRAMA +11D47 ; Extend # Mn MASARAM GONDI RA-KARA +11D90..11D91 ; Extend # Mn [2] GUNJALA GONDI VOWEL SIGN EE..GUNJALA GONDI VOWEL SIGN AI +11D95 ; Extend # Mn GUNJALA GONDI SIGN ANUSVARA +11D97 ; Extend # Mn GUNJALA GONDI VIRAMA +11EF3..11EF4 ; Extend # Mn [2] MAKASAR VOWEL SIGN I..MAKASAR VOWEL SIGN U +16AF0..16AF4 ; Extend # Mn [5] BASSA VAH COMBINING HIGH TONE..BASSA VAH COMBINING HIGH-LOW TONE +16B30..16B36 ; Extend # Mn [7] PAHAWH HMONG MARK CIM TUB..PAHAWH HMONG MARK CIM TAUM +16F4F ; Extend # Mn MIAO SIGN CONSONANT MODIFIER BAR +16F8F..16F92 ; Extend # Mn [4] MIAO TONE RIGHT..MIAO TONE BELOW +16FE4 ; Extend # Mn KHITAN SMALL SCRIPT FILLER +1BC9D..1BC9E ; Extend # Mn [2] DUPLOYAN THICK LETTER SELECTOR..DUPLOYAN DOUBLE MARK +1D165 ; Extend # Mc MUSICAL SYMBOL COMBINING STEM +1D167..1D169 ; Extend # Mn [3] MUSICAL SYMBOL COMBINING TREMOLO-1..MUSICAL SYMBOL COMBINING TREMOLO-3 +1D16E..1D172 ; Extend # Mc [5] MUSICAL SYMBOL COMBINING FLAG-1..MUSICAL SYMBOL COMBINING FLAG-5 +1D17B..1D182 ; Extend # Mn [8] MUSICAL SYMBOL COMBINING ACCENT..MUSICAL SYMBOL COMBINING LOURE +1D185..1D18B ; Extend # Mn [7] MUSICAL SYMBOL COMBINING DOIT..MUSICAL SYMBOL COMBINING TRIPLE TONGUE +1D1AA..1D1AD ; Extend # Mn [4] MUSICAL SYMBOL COMBINING DOWN BOW..MUSICAL SYMBOL COMBINING SNAP PIZZICATO +1D242..1D244 ; Extend # Mn [3] COMBINING GREEK MUSICAL TRISEME..COMBINING GREEK MUSICAL PENTASEME +1DA00..1DA36 ; Extend # Mn [55] SIGNWRITING HEAD RIM..SIGNWRITING AIR SUCKING IN +1DA3B..1DA6C ; Extend # Mn [50] SIGNWRITING MOUTH CLOSED NEUTRAL..SIGNWRITING EXCITEMENT +1DA75 ; Extend # Mn SIGNWRITING UPPER BODY TILTING FROM HIP JOINTS +1DA84 ; Extend # Mn SIGNWRITING LOCATION HEAD NECK +1DA9B..1DA9F ; Extend # Mn [5] SIGNWRITING FILL MODIFIER-2..SIGNWRITING FILL MODIFIER-6 +1DAA1..1DAAF ; Extend # Mn [15] SIGNWRITING ROTATION MODIFIER-2..SIGNWRITING ROTATION MODIFIER-16 +1E000..1E006 ; Extend # Mn [7] COMBINING GLAGOLITIC LETTER AZU..COMBINING GLAGOLITIC LETTER ZHIVETE +1E008..1E018 ; Extend # Mn [17] COMBINING GLAGOLITIC LETTER ZEMLJA..COMBINING GLAGOLITIC LETTER HERU +1E01B..1E021 ; Extend # Mn [7] COMBINING GLAGOLITIC LETTER SHTA..COMBINING GLAGOLITIC LETTER YATI +1E023..1E024 ; Extend # Mn [2] COMBINING GLAGOLITIC LETTER YU..COMBINING GLAGOLITIC LETTER SMALL YUS +1E026..1E02A ; Extend # Mn [5] COMBINING GLAGOLITIC LETTER YO..COMBINING GLAGOLITIC LETTER FITA +1E130..1E136 ; Extend # Mn [7] NYIAKENG PUACHUE HMONG TONE-B..NYIAKENG PUACHUE HMONG TONE-D +1E2EC..1E2EF ; Extend # Mn [4] WANCHO TONE TUP..WANCHO TONE KOINI +1E8D0..1E8D6 ; Extend # Mn [7] MENDE KIKAKUI COMBINING NUMBER TEENS..MENDE KIKAKUI COMBINING NUMBER MILLIONS +1E944..1E94A ; Extend # Mn [7] ADLAM ALIF LENGTHENER..ADLAM NUKTA +1F3FB..1F3FF ; Extend # Sk [5] EMOJI MODIFIER FITZPATRICK TYPE-1-2..EMOJI MODIFIER FITZPATRICK TYPE-6 +E0020..E007F ; Extend # Cf [96] TAG SPACE..CANCEL TAG +E0100..E01EF ; Extend # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256 + +# Total code points: 1984 + +# ================================================ + +1F1E6..1F1FF ; Regional_Indicator # So [26] REGIONAL INDICATOR SYMBOL LETTER A..REGIONAL INDICATOR SYMBOL LETTER Z + +# Total code points: 26 + +# ================================================ + +0903 ; SpacingMark # Mc DEVANAGARI SIGN VISARGA +093B ; SpacingMark # Mc DEVANAGARI VOWEL SIGN OOE +093E..0940 ; SpacingMark # Mc [3] DEVANAGARI VOWEL SIGN AA..DEVANAGARI VOWEL SIGN II +0949..094C ; SpacingMark # Mc [4] DEVANAGARI VOWEL SIGN CANDRA O..DEVANAGARI VOWEL SIGN AU +094E..094F ; SpacingMark # Mc [2] DEVANAGARI VOWEL SIGN PRISHTHAMATRA E..DEVANAGARI VOWEL SIGN AW +0982..0983 ; SpacingMark # Mc [2] BENGALI SIGN ANUSVARA..BENGALI SIGN VISARGA +09BF..09C0 ; SpacingMark # Mc [2] BENGALI VOWEL SIGN I..BENGALI VOWEL SIGN II +09C7..09C8 ; SpacingMark # Mc [2] BENGALI VOWEL SIGN E..BENGALI VOWEL SIGN AI +09CB..09CC ; SpacingMark # Mc [2] BENGALI VOWEL SIGN O..BENGALI VOWEL SIGN AU +0A03 ; SpacingMark # Mc GURMUKHI SIGN VISARGA +0A3E..0A40 ; SpacingMark # Mc [3] GURMUKHI VOWEL SIGN AA..GURMUKHI VOWEL SIGN II +0A83 ; SpacingMark # Mc GUJARATI SIGN VISARGA +0ABE..0AC0 ; SpacingMark # Mc [3] GUJARATI VOWEL SIGN AA..GUJARATI VOWEL SIGN II +0AC9 ; SpacingMark # Mc GUJARATI VOWEL SIGN CANDRA O +0ACB..0ACC ; SpacingMark # Mc [2] GUJARATI VOWEL SIGN O..GUJARATI VOWEL SIGN AU +0B02..0B03 ; SpacingMark # Mc [2] ORIYA SIGN ANUSVARA..ORIYA SIGN VISARGA +0B40 ; SpacingMark # Mc ORIYA VOWEL SIGN II +0B47..0B48 ; SpacingMark # Mc [2] ORIYA VOWEL SIGN E..ORIYA VOWEL SIGN AI +0B4B..0B4C ; SpacingMark # Mc [2] ORIYA VOWEL SIGN O..ORIYA VOWEL SIGN AU +0BBF ; SpacingMark # Mc TAMIL VOWEL SIGN I +0BC1..0BC2 ; SpacingMark # Mc [2] TAMIL VOWEL SIGN U..TAMIL VOWEL SIGN UU +0BC6..0BC8 ; SpacingMark # Mc [3] TAMIL VOWEL SIGN E..TAMIL VOWEL SIGN AI +0BCA..0BCC ; SpacingMark # Mc [3] TAMIL VOWEL SIGN O..TAMIL VOWEL SIGN AU +0C01..0C03 ; SpacingMark # Mc [3] TELUGU SIGN CANDRABINDU..TELUGU SIGN VISARGA +0C41..0C44 ; SpacingMark # Mc [4] TELUGU VOWEL SIGN U..TELUGU VOWEL SIGN VOCALIC RR +0C82..0C83 ; SpacingMark # Mc [2] KANNADA SIGN ANUSVARA..KANNADA SIGN VISARGA +0CBE ; SpacingMark # Mc KANNADA VOWEL SIGN AA +0CC0..0CC1 ; SpacingMark # Mc [2] KANNADA VOWEL SIGN II..KANNADA VOWEL SIGN U +0CC3..0CC4 ; SpacingMark # Mc [2] KANNADA VOWEL SIGN VOCALIC R..KANNADA VOWEL SIGN VOCALIC RR +0CC7..0CC8 ; SpacingMark # Mc [2] KANNADA VOWEL SIGN EE..KANNADA VOWEL SIGN AI +0CCA..0CCB ; SpacingMark # Mc [2] KANNADA VOWEL SIGN O..KANNADA VOWEL SIGN OO +0D02..0D03 ; SpacingMark # Mc [2] MALAYALAM SIGN ANUSVARA..MALAYALAM SIGN VISARGA +0D3F..0D40 ; SpacingMark # Mc [2] MALAYALAM VOWEL SIGN I..MALAYALAM VOWEL SIGN II +0D46..0D48 ; SpacingMark # Mc [3] MALAYALAM VOWEL SIGN E..MALAYALAM VOWEL SIGN AI +0D4A..0D4C ; SpacingMark # Mc [3] MALAYALAM VOWEL SIGN O..MALAYALAM VOWEL SIGN AU +0D82..0D83 ; SpacingMark # Mc [2] SINHALA SIGN ANUSVARAYA..SINHALA SIGN VISARGAYA +0DD0..0DD1 ; SpacingMark # Mc [2] SINHALA VOWEL SIGN KETTI AEDA-PILLA..SINHALA VOWEL SIGN DIGA AEDA-PILLA +0DD8..0DDE ; SpacingMark # Mc [7] SINHALA VOWEL SIGN GAETTA-PILLA..SINHALA VOWEL SIGN KOMBUVA HAA GAYANUKITTA +0DF2..0DF3 ; SpacingMark # Mc [2] SINHALA VOWEL SIGN DIGA GAETTA-PILLA..SINHALA VOWEL SIGN DIGA GAYANUKITTA +0E33 ; SpacingMark # Lo THAI CHARACTER SARA AM +0EB3 ; SpacingMark # Lo LAO VOWEL SIGN AM +0F3E..0F3F ; SpacingMark # Mc [2] TIBETAN SIGN YAR TSHES..TIBETAN SIGN MAR TSHES +0F7F ; SpacingMark # Mc TIBETAN SIGN RNAM BCAD +1031 ; SpacingMark # Mc MYANMAR VOWEL SIGN E +103B..103C ; SpacingMark # Mc [2] MYANMAR CONSONANT SIGN MEDIAL YA..MYANMAR CONSONANT SIGN MEDIAL RA +1056..1057 ; SpacingMark # Mc [2] MYANMAR VOWEL SIGN VOCALIC R..MYANMAR VOWEL SIGN VOCALIC RR +1084 ; SpacingMark # Mc MYANMAR VOWEL SIGN SHAN E +17B6 ; SpacingMark # Mc KHMER VOWEL SIGN AA +17BE..17C5 ; SpacingMark # Mc [8] KHMER VOWEL SIGN OE..KHMER VOWEL SIGN AU +17C7..17C8 ; SpacingMark # Mc [2] KHMER SIGN REAHMUK..KHMER SIGN YUUKALEAPINTU +1923..1926 ; SpacingMark # Mc [4] LIMBU VOWEL SIGN EE..LIMBU VOWEL SIGN AU +1929..192B ; SpacingMark # Mc [3] LIMBU SUBJOINED LETTER YA..LIMBU SUBJOINED LETTER WA +1930..1931 ; SpacingMark # Mc [2] LIMBU SMALL LETTER KA..LIMBU SMALL LETTER NGA +1933..1938 ; SpacingMark # Mc [6] LIMBU SMALL LETTER TA..LIMBU SMALL LETTER LA +1A19..1A1A ; SpacingMark # Mc [2] BUGINESE VOWEL SIGN E..BUGINESE VOWEL SIGN O +1A55 ; SpacingMark # Mc TAI THAM CONSONANT SIGN MEDIAL RA +1A57 ; SpacingMark # Mc TAI THAM CONSONANT SIGN LA TANG LAI +1A6D..1A72 ; SpacingMark # Mc [6] TAI THAM VOWEL SIGN OY..TAI THAM VOWEL SIGN THAM AI +1B04 ; SpacingMark # Mc BALINESE SIGN BISAH +1B3B ; SpacingMark # Mc BALINESE VOWEL SIGN RA REPA TEDUNG +1B3D..1B41 ; SpacingMark # Mc [5] BALINESE VOWEL SIGN LA LENGA TEDUNG..BALINESE VOWEL SIGN TALING REPA TEDUNG +1B43..1B44 ; SpacingMark # Mc [2] BALINESE VOWEL SIGN PEPET TEDUNG..BALINESE ADEG ADEG +1B82 ; SpacingMark # Mc SUNDANESE SIGN PANGWISAD +1BA1 ; SpacingMark # Mc SUNDANESE CONSONANT SIGN PAMINGKAL +1BA6..1BA7 ; SpacingMark # Mc [2] SUNDANESE VOWEL SIGN PANAELAENG..SUNDANESE VOWEL SIGN PANOLONG +1BAA ; SpacingMark # Mc SUNDANESE SIGN PAMAAEH +1BE7 ; SpacingMark # Mc BATAK VOWEL SIGN E +1BEA..1BEC ; SpacingMark # Mc [3] BATAK VOWEL SIGN I..BATAK VOWEL SIGN O +1BEE ; SpacingMark # Mc BATAK VOWEL SIGN U +1BF2..1BF3 ; SpacingMark # Mc [2] BATAK PANGOLAT..BATAK PANONGONAN +1C24..1C2B ; SpacingMark # Mc [8] LEPCHA SUBJOINED LETTER YA..LEPCHA VOWEL SIGN UU +1C34..1C35 ; SpacingMark # Mc [2] LEPCHA CONSONANT SIGN NYIN-DO..LEPCHA CONSONANT SIGN KANG +1CE1 ; SpacingMark # Mc VEDIC TONE ATHARVAVEDIC INDEPENDENT SVARITA +1CF7 ; SpacingMark # Mc VEDIC SIGN ATIKRAMA +A823..A824 ; SpacingMark # Mc [2] SYLOTI NAGRI VOWEL SIGN A..SYLOTI NAGRI VOWEL SIGN I +A827 ; SpacingMark # Mc SYLOTI NAGRI VOWEL SIGN OO +A880..A881 ; SpacingMark # Mc [2] SAURASHTRA SIGN ANUSVARA..SAURASHTRA SIGN VISARGA +A8B4..A8C3 ; SpacingMark # Mc [16] SAURASHTRA CONSONANT SIGN HAARU..SAURASHTRA VOWEL SIGN AU +A952..A953 ; SpacingMark # Mc [2] REJANG CONSONANT SIGN H..REJANG VIRAMA +A983 ; SpacingMark # Mc JAVANESE SIGN WIGNYAN +A9B4..A9B5 ; SpacingMark # Mc [2] JAVANESE VOWEL SIGN TARUNG..JAVANESE VOWEL SIGN TOLONG +A9BA..A9BB ; SpacingMark # Mc [2] JAVANESE VOWEL SIGN TALING..JAVANESE VOWEL SIGN DIRGA MURE +A9BE..A9C0 ; SpacingMark # Mc [3] JAVANESE CONSONANT SIGN PENGKAL..JAVANESE PANGKON +AA2F..AA30 ; SpacingMark # Mc [2] CHAM VOWEL SIGN O..CHAM VOWEL SIGN AI +AA33..AA34 ; SpacingMark # Mc [2] CHAM CONSONANT SIGN YA..CHAM CONSONANT SIGN RA +AA4D ; SpacingMark # Mc CHAM CONSONANT SIGN FINAL H +AAEB ; SpacingMark # Mc MEETEI MAYEK VOWEL SIGN II +AAEE..AAEF ; SpacingMark # Mc [2] MEETEI MAYEK VOWEL SIGN AU..MEETEI MAYEK VOWEL SIGN AAU +AAF5 ; SpacingMark # Mc MEETEI MAYEK VOWEL SIGN VISARGA +ABE3..ABE4 ; SpacingMark # Mc [2] MEETEI MAYEK VOWEL SIGN ONAP..MEETEI MAYEK VOWEL SIGN INAP +ABE6..ABE7 ; SpacingMark # Mc [2] MEETEI MAYEK VOWEL SIGN YENAP..MEETEI MAYEK VOWEL SIGN SOUNAP +ABE9..ABEA ; SpacingMark # Mc [2] MEETEI MAYEK VOWEL SIGN CHEINAP..MEETEI MAYEK VOWEL SIGN NUNG +ABEC ; SpacingMark # Mc MEETEI MAYEK LUM IYEK +11000 ; SpacingMark # Mc BRAHMI SIGN CANDRABINDU +11002 ; SpacingMark # Mc BRAHMI SIGN VISARGA +11082 ; SpacingMark # Mc KAITHI SIGN VISARGA +110B0..110B2 ; SpacingMark # Mc [3] KAITHI VOWEL SIGN AA..KAITHI VOWEL SIGN II +110B7..110B8 ; SpacingMark # Mc [2] KAITHI VOWEL SIGN O..KAITHI VOWEL SIGN AU +1112C ; SpacingMark # Mc CHAKMA VOWEL SIGN E +11145..11146 ; SpacingMark # Mc [2] CHAKMA VOWEL SIGN AA..CHAKMA VOWEL SIGN EI +11182 ; SpacingMark # Mc SHARADA SIGN VISARGA +111B3..111B5 ; SpacingMark # Mc [3] SHARADA VOWEL SIGN AA..SHARADA VOWEL SIGN II +111BF..111C0 ; SpacingMark # Mc [2] SHARADA VOWEL SIGN AU..SHARADA SIGN VIRAMA +111CE ; SpacingMark # Mc SHARADA VOWEL SIGN PRISHTHAMATRA E +1122C..1122E ; SpacingMark # Mc [3] KHOJKI VOWEL SIGN AA..KHOJKI VOWEL SIGN II +11232..11233 ; SpacingMark # Mc [2] KHOJKI VOWEL SIGN O..KHOJKI VOWEL SIGN AU +11235 ; SpacingMark # Mc KHOJKI SIGN VIRAMA +112E0..112E2 ; SpacingMark # Mc [3] KHUDAWADI VOWEL SIGN AA..KHUDAWADI VOWEL SIGN II +11302..11303 ; SpacingMark # Mc [2] GRANTHA SIGN ANUSVARA..GRANTHA SIGN VISARGA +1133F ; SpacingMark # Mc GRANTHA VOWEL SIGN I +11341..11344 ; SpacingMark # Mc [4] GRANTHA VOWEL SIGN U..GRANTHA VOWEL SIGN VOCALIC RR +11347..11348 ; SpacingMark # Mc [2] GRANTHA VOWEL SIGN EE..GRANTHA VOWEL SIGN AI +1134B..1134D ; SpacingMark # Mc [3] GRANTHA VOWEL SIGN OO..GRANTHA SIGN VIRAMA +11362..11363 ; SpacingMark # Mc [2] GRANTHA VOWEL SIGN VOCALIC L..GRANTHA VOWEL SIGN VOCALIC LL +11435..11437 ; SpacingMark # Mc [3] NEWA VOWEL SIGN AA..NEWA VOWEL SIGN II +11440..11441 ; SpacingMark # Mc [2] NEWA VOWEL SIGN O..NEWA VOWEL SIGN AU +11445 ; SpacingMark # Mc NEWA SIGN VISARGA +114B1..114B2 ; SpacingMark # Mc [2] TIRHUTA VOWEL SIGN I..TIRHUTA VOWEL SIGN II +114B9 ; SpacingMark # Mc TIRHUTA VOWEL SIGN E +114BB..114BC ; SpacingMark # Mc [2] TIRHUTA VOWEL SIGN AI..TIRHUTA VOWEL SIGN O +114BE ; SpacingMark # Mc TIRHUTA VOWEL SIGN AU +114C1 ; SpacingMark # Mc TIRHUTA SIGN VISARGA +115B0..115B1 ; SpacingMark # Mc [2] SIDDHAM VOWEL SIGN I..SIDDHAM VOWEL SIGN II +115B8..115BB ; SpacingMark # Mc [4] SIDDHAM VOWEL SIGN E..SIDDHAM VOWEL SIGN AU +115BE ; SpacingMark # Mc SIDDHAM SIGN VISARGA +11630..11632 ; SpacingMark # Mc [3] MODI VOWEL SIGN AA..MODI VOWEL SIGN II +1163B..1163C ; SpacingMark # Mc [2] MODI VOWEL SIGN O..MODI VOWEL SIGN AU +1163E ; SpacingMark # Mc MODI SIGN VISARGA +116AC ; SpacingMark # Mc TAKRI SIGN VISARGA +116AE..116AF ; SpacingMark # Mc [2] TAKRI VOWEL SIGN I..TAKRI VOWEL SIGN II +116B6 ; SpacingMark # Mc TAKRI SIGN VIRAMA +11720..11721 ; SpacingMark # Mc [2] AHOM VOWEL SIGN A..AHOM VOWEL SIGN AA +11726 ; SpacingMark # Mc AHOM VOWEL SIGN E +1182C..1182E ; SpacingMark # Mc [3] DOGRA VOWEL SIGN AA..DOGRA VOWEL SIGN II +11838 ; SpacingMark # Mc DOGRA SIGN VISARGA +11931..11935 ; SpacingMark # Mc [5] DIVES AKURU VOWEL SIGN I..DIVES AKURU VOWEL SIGN E +11937..11938 ; SpacingMark # Mc [2] DIVES AKURU VOWEL SIGN AI..DIVES AKURU VOWEL SIGN O +1193D ; SpacingMark # Mc DIVES AKURU SIGN HALANTA +11940 ; SpacingMark # Mc DIVES AKURU MEDIAL YA +11942 ; SpacingMark # Mc DIVES AKURU MEDIAL RA +119D1..119D3 ; SpacingMark # Mc [3] NANDINAGARI VOWEL SIGN AA..NANDINAGARI VOWEL SIGN II +119DC..119DF ; SpacingMark # Mc [4] NANDINAGARI VOWEL SIGN O..NANDINAGARI SIGN VISARGA +119E4 ; SpacingMark # Mc NANDINAGARI VOWEL SIGN PRISHTHAMATRA E +11A39 ; SpacingMark # Mc ZANABAZAR SQUARE SIGN VISARGA +11A57..11A58 ; SpacingMark # Mc [2] SOYOMBO VOWEL SIGN AI..SOYOMBO VOWEL SIGN AU +11A97 ; SpacingMark # Mc SOYOMBO SIGN VISARGA +11C2F ; SpacingMark # Mc BHAIKSUKI VOWEL SIGN AA +11C3E ; SpacingMark # Mc BHAIKSUKI SIGN VISARGA +11CA9 ; SpacingMark # Mc MARCHEN SUBJOINED LETTER YA +11CB1 ; SpacingMark # Mc MARCHEN VOWEL SIGN I +11CB4 ; SpacingMark # Mc MARCHEN VOWEL SIGN O +11D8A..11D8E ; SpacingMark # Mc [5] GUNJALA GONDI VOWEL SIGN AA..GUNJALA GONDI VOWEL SIGN UU +11D93..11D94 ; SpacingMark # Mc [2] GUNJALA GONDI VOWEL SIGN OO..GUNJALA GONDI VOWEL SIGN AU +11D96 ; SpacingMark # Mc GUNJALA GONDI SIGN VISARGA +11EF5..11EF6 ; SpacingMark # Mc [2] MAKASAR VOWEL SIGN E..MAKASAR VOWEL SIGN O +16F51..16F87 ; SpacingMark # Mc [55] MIAO SIGN ASPIRATION..MIAO VOWEL SIGN UI +16FF0..16FF1 ; SpacingMark # Mc [2] VIETNAMESE ALTERNATE READING MARK CA..VIETNAMESE ALTERNATE READING MARK NHAY +1D166 ; SpacingMark # Mc MUSICAL SYMBOL COMBINING SPRECHGESANG STEM +1D16D ; SpacingMark # Mc MUSICAL SYMBOL COMBINING AUGMENTATION DOT + +# Total code points: 388 + +# ================================================ + +1100..115F ; L # Lo [96] HANGUL CHOSEONG KIYEOK..HANGUL CHOSEONG FILLER +A960..A97C ; L # Lo [29] HANGUL CHOSEONG TIKEUT-MIEUM..HANGUL CHOSEONG SSANGYEORINHIEUH + +# Total code points: 125 + +# ================================================ + +1160..11A7 ; V # Lo [72] HANGUL JUNGSEONG FILLER..HANGUL JUNGSEONG O-YAE +D7B0..D7C6 ; V # Lo [23] HANGUL JUNGSEONG O-YEO..HANGUL JUNGSEONG ARAEA-E + +# Total code points: 95 + +# ================================================ + +11A8..11FF ; T # Lo [88] HANGUL JONGSEONG KIYEOK..HANGUL JONGSEONG SSANGNIEUN +D7CB..D7FB ; T # Lo [49] HANGUL JONGSEONG NIEUN-RIEUL..HANGUL JONGSEONG PHIEUPH-THIEUTH + +# Total code points: 137 + +# ================================================ + +AC00 ; LV # Lo HANGUL SYLLABLE GA +AC1C ; LV # Lo HANGUL SYLLABLE GAE +AC38 ; LV # Lo HANGUL SYLLABLE GYA +AC54 ; LV # Lo HANGUL SYLLABLE GYAE +AC70 ; LV # Lo HANGUL SYLLABLE GEO +AC8C ; LV # Lo HANGUL SYLLABLE GE +ACA8 ; LV # Lo HANGUL SYLLABLE GYEO +ACC4 ; LV # Lo HANGUL SYLLABLE GYE +ACE0 ; LV # Lo HANGUL SYLLABLE GO +ACFC ; LV # Lo HANGUL SYLLABLE GWA +AD18 ; LV # Lo HANGUL SYLLABLE GWAE +AD34 ; LV # Lo HANGUL SYLLABLE GOE +AD50 ; LV # Lo HANGUL SYLLABLE GYO +AD6C ; LV # Lo HANGUL SYLLABLE GU +AD88 ; LV # Lo HANGUL SYLLABLE GWEO +ADA4 ; LV # Lo HANGUL SYLLABLE GWE +ADC0 ; LV # Lo HANGUL SYLLABLE GWI +ADDC ; LV # Lo HANGUL SYLLABLE GYU +ADF8 ; LV # Lo HANGUL SYLLABLE GEU +AE14 ; LV # Lo HANGUL SYLLABLE GYI +AE30 ; LV # Lo HANGUL SYLLABLE GI +AE4C ; LV # Lo HANGUL SYLLABLE GGA +AE68 ; LV # Lo HANGUL SYLLABLE GGAE +AE84 ; LV # Lo HANGUL SYLLABLE GGYA +AEA0 ; LV # Lo HANGUL SYLLABLE GGYAE +AEBC ; LV # Lo HANGUL SYLLABLE GGEO +AED8 ; LV # Lo HANGUL SYLLABLE GGE +AEF4 ; LV # Lo HANGUL SYLLABLE GGYEO +AF10 ; LV # Lo HANGUL SYLLABLE GGYE +AF2C ; LV # Lo HANGUL SYLLABLE GGO +AF48 ; LV # Lo HANGUL SYLLABLE GGWA +AF64 ; LV # Lo HANGUL SYLLABLE GGWAE +AF80 ; LV # Lo HANGUL SYLLABLE GGOE +AF9C ; LV # Lo HANGUL SYLLABLE GGYO +AFB8 ; LV # Lo HANGUL SYLLABLE GGU +AFD4 ; LV # Lo HANGUL SYLLABLE GGWEO +AFF0 ; LV # Lo HANGUL SYLLABLE GGWE +B00C ; LV # Lo HANGUL SYLLABLE GGWI +B028 ; LV # Lo HANGUL SYLLABLE GGYU +B044 ; LV # Lo HANGUL SYLLABLE GGEU +B060 ; LV # Lo HANGUL SYLLABLE GGYI +B07C ; LV # Lo HANGUL SYLLABLE GGI +B098 ; LV # Lo HANGUL SYLLABLE NA +B0B4 ; LV # Lo HANGUL SYLLABLE NAE +B0D0 ; LV # Lo HANGUL SYLLABLE NYA +B0EC ; LV # Lo HANGUL SYLLABLE NYAE +B108 ; LV # Lo HANGUL SYLLABLE NEO +B124 ; LV # Lo HANGUL SYLLABLE NE +B140 ; LV # Lo HANGUL SYLLABLE NYEO +B15C ; LV # Lo HANGUL SYLLABLE NYE +B178 ; LV # Lo HANGUL SYLLABLE NO +B194 ; LV # Lo HANGUL SYLLABLE NWA +B1B0 ; LV # Lo HANGUL SYLLABLE NWAE +B1CC ; LV # Lo HANGUL SYLLABLE NOE +B1E8 ; LV # Lo HANGUL SYLLABLE NYO +B204 ; LV # Lo HANGUL SYLLABLE NU +B220 ; LV # Lo HANGUL SYLLABLE NWEO +B23C ; LV # Lo HANGUL SYLLABLE NWE +B258 ; LV # Lo HANGUL SYLLABLE NWI +B274 ; LV # Lo HANGUL SYLLABLE NYU +B290 ; LV # Lo HANGUL SYLLABLE NEU +B2AC ; LV # Lo HANGUL SYLLABLE NYI +B2C8 ; LV # Lo HANGUL SYLLABLE NI +B2E4 ; LV # Lo HANGUL SYLLABLE DA +B300 ; LV # Lo HANGUL SYLLABLE DAE +B31C ; LV # Lo HANGUL SYLLABLE DYA +B338 ; LV # Lo HANGUL SYLLABLE DYAE +B354 ; LV # Lo HANGUL SYLLABLE DEO +B370 ; LV # Lo HANGUL SYLLABLE DE +B38C ; LV # Lo HANGUL SYLLABLE DYEO +B3A8 ; LV # Lo HANGUL SYLLABLE DYE +B3C4 ; LV # Lo HANGUL SYLLABLE DO +B3E0 ; LV # Lo HANGUL SYLLABLE DWA +B3FC ; LV # Lo HANGUL SYLLABLE DWAE +B418 ; LV # Lo HANGUL SYLLABLE DOE +B434 ; LV # Lo HANGUL SYLLABLE DYO +B450 ; LV # Lo HANGUL SYLLABLE DU +B46C ; LV # Lo HANGUL SYLLABLE DWEO +B488 ; LV # Lo HANGUL SYLLABLE DWE +B4A4 ; LV # Lo HANGUL SYLLABLE DWI +B4C0 ; LV # Lo HANGUL SYLLABLE DYU +B4DC ; LV # Lo HANGUL SYLLABLE DEU +B4F8 ; LV # Lo HANGUL SYLLABLE DYI +B514 ; LV # Lo HANGUL SYLLABLE DI +B530 ; LV # Lo HANGUL SYLLABLE DDA +B54C ; LV # Lo HANGUL SYLLABLE DDAE +B568 ; LV # Lo HANGUL SYLLABLE DDYA +B584 ; LV # Lo HANGUL SYLLABLE DDYAE +B5A0 ; LV # Lo HANGUL SYLLABLE DDEO +B5BC ; LV # Lo HANGUL SYLLABLE DDE +B5D8 ; LV # Lo HANGUL SYLLABLE DDYEO +B5F4 ; LV # Lo HANGUL SYLLABLE DDYE +B610 ; LV # Lo HANGUL SYLLABLE DDO +B62C ; LV # Lo HANGUL SYLLABLE DDWA +B648 ; LV # Lo HANGUL SYLLABLE DDWAE +B664 ; LV # Lo HANGUL SYLLABLE DDOE +B680 ; LV # Lo HANGUL SYLLABLE DDYO +B69C ; LV # Lo HANGUL SYLLABLE DDU +B6B8 ; LV # Lo HANGUL SYLLABLE DDWEO +B6D4 ; LV # Lo HANGUL SYLLABLE DDWE +B6F0 ; LV # Lo HANGUL SYLLABLE DDWI +B70C ; LV # Lo HANGUL SYLLABLE DDYU +B728 ; LV # Lo HANGUL SYLLABLE DDEU +B744 ; LV # Lo HANGUL SYLLABLE DDYI +B760 ; LV # Lo HANGUL SYLLABLE DDI +B77C ; LV # Lo HANGUL SYLLABLE RA +B798 ; LV # Lo HANGUL SYLLABLE RAE +B7B4 ; LV # Lo HANGUL SYLLABLE RYA +B7D0 ; LV # Lo HANGUL SYLLABLE RYAE +B7EC ; LV # Lo HANGUL SYLLABLE REO +B808 ; LV # Lo HANGUL SYLLABLE RE +B824 ; LV # Lo HANGUL SYLLABLE RYEO +B840 ; LV # Lo HANGUL SYLLABLE RYE +B85C ; LV # Lo HANGUL SYLLABLE RO +B878 ; LV # Lo HANGUL SYLLABLE RWA +B894 ; LV # Lo HANGUL SYLLABLE RWAE +B8B0 ; LV # Lo HANGUL SYLLABLE ROE +B8CC ; LV # Lo HANGUL SYLLABLE RYO +B8E8 ; LV # Lo HANGUL SYLLABLE RU +B904 ; LV # Lo HANGUL SYLLABLE RWEO +B920 ; LV # Lo HANGUL SYLLABLE RWE +B93C ; LV # Lo HANGUL SYLLABLE RWI +B958 ; LV # Lo HANGUL SYLLABLE RYU +B974 ; LV # Lo HANGUL SYLLABLE REU +B990 ; LV # Lo HANGUL SYLLABLE RYI +B9AC ; LV # Lo HANGUL SYLLABLE RI +B9C8 ; LV # Lo HANGUL SYLLABLE MA +B9E4 ; LV # Lo HANGUL SYLLABLE MAE +BA00 ; LV # Lo HANGUL SYLLABLE MYA +BA1C ; LV # Lo HANGUL SYLLABLE MYAE +BA38 ; LV # Lo HANGUL SYLLABLE MEO +BA54 ; LV # Lo HANGUL SYLLABLE ME +BA70 ; LV # Lo HANGUL SYLLABLE MYEO +BA8C ; LV # Lo HANGUL SYLLABLE MYE +BAA8 ; LV # Lo HANGUL SYLLABLE MO +BAC4 ; LV # Lo HANGUL SYLLABLE MWA +BAE0 ; LV # Lo HANGUL SYLLABLE MWAE +BAFC ; LV # Lo HANGUL SYLLABLE MOE +BB18 ; LV # Lo HANGUL SYLLABLE MYO +BB34 ; LV # Lo HANGUL SYLLABLE MU +BB50 ; LV # Lo HANGUL SYLLABLE MWEO +BB6C ; LV # Lo HANGUL SYLLABLE MWE +BB88 ; LV # Lo HANGUL SYLLABLE MWI +BBA4 ; LV # Lo HANGUL SYLLABLE MYU +BBC0 ; LV # Lo HANGUL SYLLABLE MEU +BBDC ; LV # Lo HANGUL SYLLABLE MYI +BBF8 ; LV # Lo HANGUL SYLLABLE MI +BC14 ; LV # Lo HANGUL SYLLABLE BA +BC30 ; LV # Lo HANGUL SYLLABLE BAE +BC4C ; LV # Lo HANGUL SYLLABLE BYA +BC68 ; LV # Lo HANGUL SYLLABLE BYAE +BC84 ; LV # Lo HANGUL SYLLABLE BEO +BCA0 ; LV # Lo HANGUL SYLLABLE BE +BCBC ; LV # Lo HANGUL SYLLABLE BYEO +BCD8 ; LV # Lo HANGUL SYLLABLE BYE +BCF4 ; LV # Lo HANGUL SYLLABLE BO +BD10 ; LV # Lo HANGUL SYLLABLE BWA +BD2C ; LV # Lo HANGUL SYLLABLE BWAE +BD48 ; LV # Lo HANGUL SYLLABLE BOE +BD64 ; LV # Lo HANGUL SYLLABLE BYO +BD80 ; LV # Lo HANGUL SYLLABLE BU +BD9C ; LV # Lo HANGUL SYLLABLE BWEO +BDB8 ; LV # Lo HANGUL SYLLABLE BWE +BDD4 ; LV # Lo HANGUL SYLLABLE BWI +BDF0 ; LV # Lo HANGUL SYLLABLE BYU +BE0C ; LV # Lo HANGUL SYLLABLE BEU +BE28 ; LV # Lo HANGUL SYLLABLE BYI +BE44 ; LV # Lo HANGUL SYLLABLE BI +BE60 ; LV # Lo HANGUL SYLLABLE BBA +BE7C ; LV # Lo HANGUL SYLLABLE BBAE +BE98 ; LV # Lo HANGUL SYLLABLE BBYA +BEB4 ; LV # Lo HANGUL SYLLABLE BBYAE +BED0 ; LV # Lo HANGUL SYLLABLE BBEO +BEEC ; LV # Lo HANGUL SYLLABLE BBE +BF08 ; LV # Lo HANGUL SYLLABLE BBYEO +BF24 ; LV # Lo HANGUL SYLLABLE BBYE +BF40 ; LV # Lo HANGUL SYLLABLE BBO +BF5C ; LV # Lo HANGUL SYLLABLE BBWA +BF78 ; LV # Lo HANGUL SYLLABLE BBWAE +BF94 ; LV # Lo HANGUL SYLLABLE BBOE +BFB0 ; LV # Lo HANGUL SYLLABLE BBYO +BFCC ; LV # Lo HANGUL SYLLABLE BBU +BFE8 ; LV # Lo HANGUL SYLLABLE BBWEO +C004 ; LV # Lo HANGUL SYLLABLE BBWE +C020 ; LV # Lo HANGUL SYLLABLE BBWI +C03C ; LV # Lo HANGUL SYLLABLE BBYU +C058 ; LV # Lo HANGUL SYLLABLE BBEU +C074 ; LV # Lo HANGUL SYLLABLE BBYI +C090 ; LV # Lo HANGUL SYLLABLE BBI +C0AC ; LV # Lo HANGUL SYLLABLE SA +C0C8 ; LV # Lo HANGUL SYLLABLE SAE +C0E4 ; LV # Lo HANGUL SYLLABLE SYA +C100 ; LV # Lo HANGUL SYLLABLE SYAE +C11C ; LV # Lo HANGUL SYLLABLE SEO +C138 ; LV # Lo HANGUL SYLLABLE SE +C154 ; LV # Lo HANGUL SYLLABLE SYEO +C170 ; LV # Lo HANGUL SYLLABLE SYE +C18C ; LV # Lo HANGUL SYLLABLE SO +C1A8 ; LV # Lo HANGUL SYLLABLE SWA +C1C4 ; LV # Lo HANGUL SYLLABLE SWAE +C1E0 ; LV # Lo HANGUL SYLLABLE SOE +C1FC ; LV # Lo HANGUL SYLLABLE SYO +C218 ; LV # Lo HANGUL SYLLABLE SU +C234 ; LV # Lo HANGUL SYLLABLE SWEO +C250 ; LV # Lo HANGUL SYLLABLE SWE +C26C ; LV # Lo HANGUL SYLLABLE SWI +C288 ; LV # Lo HANGUL SYLLABLE SYU +C2A4 ; LV # Lo HANGUL SYLLABLE SEU +C2C0 ; LV # Lo HANGUL SYLLABLE SYI +C2DC ; LV # Lo HANGUL SYLLABLE SI +C2F8 ; LV # Lo HANGUL SYLLABLE SSA +C314 ; LV # Lo HANGUL SYLLABLE SSAE +C330 ; LV # Lo HANGUL SYLLABLE SSYA +C34C ; LV # Lo HANGUL SYLLABLE SSYAE +C368 ; LV # Lo HANGUL SYLLABLE SSEO +C384 ; LV # Lo HANGUL SYLLABLE SSE +C3A0 ; LV # Lo HANGUL SYLLABLE SSYEO +C3BC ; LV # Lo HANGUL SYLLABLE SSYE +C3D8 ; LV # Lo HANGUL SYLLABLE SSO +C3F4 ; LV # Lo HANGUL SYLLABLE SSWA +C410 ; LV # Lo HANGUL SYLLABLE SSWAE +C42C ; LV # Lo HANGUL SYLLABLE SSOE +C448 ; LV # Lo HANGUL SYLLABLE SSYO +C464 ; LV # Lo HANGUL SYLLABLE SSU +C480 ; LV # Lo HANGUL SYLLABLE SSWEO +C49C ; LV # Lo HANGUL SYLLABLE SSWE +C4B8 ; LV # Lo HANGUL SYLLABLE SSWI +C4D4 ; LV # Lo HANGUL SYLLABLE SSYU +C4F0 ; LV # Lo HANGUL SYLLABLE SSEU +C50C ; LV # Lo HANGUL SYLLABLE SSYI +C528 ; LV # Lo HANGUL SYLLABLE SSI +C544 ; LV # Lo HANGUL SYLLABLE A +C560 ; LV # Lo HANGUL SYLLABLE AE +C57C ; LV # Lo HANGUL SYLLABLE YA +C598 ; LV # Lo HANGUL SYLLABLE YAE +C5B4 ; LV # Lo HANGUL SYLLABLE EO +C5D0 ; LV # Lo HANGUL SYLLABLE E +C5EC ; LV # Lo HANGUL SYLLABLE YEO +C608 ; LV # Lo HANGUL SYLLABLE YE +C624 ; LV # Lo HANGUL SYLLABLE O +C640 ; LV # Lo HANGUL SYLLABLE WA +C65C ; LV # Lo HANGUL SYLLABLE WAE +C678 ; LV # Lo HANGUL SYLLABLE OE +C694 ; LV # Lo HANGUL SYLLABLE YO +C6B0 ; LV # Lo HANGUL SYLLABLE U +C6CC ; LV # Lo HANGUL SYLLABLE WEO +C6E8 ; LV # Lo HANGUL SYLLABLE WE +C704 ; LV # Lo HANGUL SYLLABLE WI +C720 ; LV # Lo HANGUL SYLLABLE YU +C73C ; LV # Lo HANGUL SYLLABLE EU +C758 ; LV # Lo HANGUL SYLLABLE YI +C774 ; LV # Lo HANGUL SYLLABLE I +C790 ; LV # Lo HANGUL SYLLABLE JA +C7AC ; LV # Lo HANGUL SYLLABLE JAE +C7C8 ; LV # Lo HANGUL SYLLABLE JYA +C7E4 ; LV # Lo HANGUL SYLLABLE JYAE +C800 ; LV # Lo HANGUL SYLLABLE JEO +C81C ; LV # Lo HANGUL SYLLABLE JE +C838 ; LV # Lo HANGUL SYLLABLE JYEO +C854 ; LV # Lo HANGUL SYLLABLE JYE +C870 ; LV # Lo HANGUL SYLLABLE JO +C88C ; LV # Lo HANGUL SYLLABLE JWA +C8A8 ; LV # Lo HANGUL SYLLABLE JWAE +C8C4 ; LV # Lo HANGUL SYLLABLE JOE +C8E0 ; LV # Lo HANGUL SYLLABLE JYO +C8FC ; LV # Lo HANGUL SYLLABLE JU +C918 ; LV # Lo HANGUL SYLLABLE JWEO +C934 ; LV # Lo HANGUL SYLLABLE JWE +C950 ; LV # Lo HANGUL SYLLABLE JWI +C96C ; LV # Lo HANGUL SYLLABLE JYU +C988 ; LV # Lo HANGUL SYLLABLE JEU +C9A4 ; LV # Lo HANGUL SYLLABLE JYI +C9C0 ; LV # Lo HANGUL SYLLABLE JI +C9DC ; LV # Lo HANGUL SYLLABLE JJA +C9F8 ; LV # Lo HANGUL SYLLABLE JJAE +CA14 ; LV # Lo HANGUL SYLLABLE JJYA +CA30 ; LV # Lo HANGUL SYLLABLE JJYAE +CA4C ; LV # Lo HANGUL SYLLABLE JJEO +CA68 ; LV # Lo HANGUL SYLLABLE JJE +CA84 ; LV # Lo HANGUL SYLLABLE JJYEO +CAA0 ; LV # Lo HANGUL SYLLABLE JJYE +CABC ; LV # Lo HANGUL SYLLABLE JJO +CAD8 ; LV # Lo HANGUL SYLLABLE JJWA +CAF4 ; LV # Lo HANGUL SYLLABLE JJWAE +CB10 ; LV # Lo HANGUL SYLLABLE JJOE +CB2C ; LV # Lo HANGUL SYLLABLE JJYO +CB48 ; LV # Lo HANGUL SYLLABLE JJU +CB64 ; LV # Lo HANGUL SYLLABLE JJWEO +CB80 ; LV # Lo HANGUL SYLLABLE JJWE +CB9C ; LV # Lo HANGUL SYLLABLE JJWI +CBB8 ; LV # Lo HANGUL SYLLABLE JJYU +CBD4 ; LV # Lo HANGUL SYLLABLE JJEU +CBF0 ; LV # Lo HANGUL SYLLABLE JJYI +CC0C ; LV # Lo HANGUL SYLLABLE JJI +CC28 ; LV # Lo HANGUL SYLLABLE CA +CC44 ; LV # Lo HANGUL SYLLABLE CAE +CC60 ; LV # Lo HANGUL SYLLABLE CYA +CC7C ; LV # Lo HANGUL SYLLABLE CYAE +CC98 ; LV # Lo HANGUL SYLLABLE CEO +CCB4 ; LV # Lo HANGUL SYLLABLE CE +CCD0 ; LV # Lo HANGUL SYLLABLE CYEO +CCEC ; LV # Lo HANGUL SYLLABLE CYE +CD08 ; LV # Lo HANGUL SYLLABLE CO +CD24 ; LV # Lo HANGUL SYLLABLE CWA +CD40 ; LV # Lo HANGUL SYLLABLE CWAE +CD5C ; LV # Lo HANGUL SYLLABLE COE +CD78 ; LV # Lo HANGUL SYLLABLE CYO +CD94 ; LV # Lo HANGUL SYLLABLE CU +CDB0 ; LV # Lo HANGUL SYLLABLE CWEO +CDCC ; LV # Lo HANGUL SYLLABLE CWE +CDE8 ; LV # Lo HANGUL SYLLABLE CWI +CE04 ; LV # Lo HANGUL SYLLABLE CYU +CE20 ; LV # Lo HANGUL SYLLABLE CEU +CE3C ; LV # Lo HANGUL SYLLABLE CYI +CE58 ; LV # Lo HANGUL SYLLABLE CI +CE74 ; LV # Lo HANGUL SYLLABLE KA +CE90 ; LV # Lo HANGUL SYLLABLE KAE +CEAC ; LV # Lo HANGUL SYLLABLE KYA +CEC8 ; LV # Lo HANGUL SYLLABLE KYAE +CEE4 ; LV # Lo HANGUL SYLLABLE KEO +CF00 ; LV # Lo HANGUL SYLLABLE KE +CF1C ; LV # Lo HANGUL SYLLABLE KYEO +CF38 ; LV # Lo HANGUL SYLLABLE KYE +CF54 ; LV # Lo HANGUL SYLLABLE KO +CF70 ; LV # Lo HANGUL SYLLABLE KWA +CF8C ; LV # Lo HANGUL SYLLABLE KWAE +CFA8 ; LV # Lo HANGUL SYLLABLE KOE +CFC4 ; LV # Lo HANGUL SYLLABLE KYO +CFE0 ; LV # Lo HANGUL SYLLABLE KU +CFFC ; LV # Lo HANGUL SYLLABLE KWEO +D018 ; LV # Lo HANGUL SYLLABLE KWE +D034 ; LV # Lo HANGUL SYLLABLE KWI +D050 ; LV # Lo HANGUL SYLLABLE KYU +D06C ; LV # Lo HANGUL SYLLABLE KEU +D088 ; LV # Lo HANGUL SYLLABLE KYI +D0A4 ; LV # Lo HANGUL SYLLABLE KI +D0C0 ; LV # Lo HANGUL SYLLABLE TA +D0DC ; LV # Lo HANGUL SYLLABLE TAE +D0F8 ; LV # Lo HANGUL SYLLABLE TYA +D114 ; LV # Lo HANGUL SYLLABLE TYAE +D130 ; LV # Lo HANGUL SYLLABLE TEO +D14C ; LV # Lo HANGUL SYLLABLE TE +D168 ; LV # Lo HANGUL SYLLABLE TYEO +D184 ; LV # Lo HANGUL SYLLABLE TYE +D1A0 ; LV # Lo HANGUL SYLLABLE TO +D1BC ; LV # Lo HANGUL SYLLABLE TWA +D1D8 ; LV # Lo HANGUL SYLLABLE TWAE +D1F4 ; LV # Lo HANGUL SYLLABLE TOE +D210 ; LV # Lo HANGUL SYLLABLE TYO +D22C ; LV # Lo HANGUL SYLLABLE TU +D248 ; LV # Lo HANGUL SYLLABLE TWEO +D264 ; LV # Lo HANGUL SYLLABLE TWE +D280 ; LV # Lo HANGUL SYLLABLE TWI +D29C ; LV # Lo HANGUL SYLLABLE TYU +D2B8 ; LV # Lo HANGUL SYLLABLE TEU +D2D4 ; LV # Lo HANGUL SYLLABLE TYI +D2F0 ; LV # Lo HANGUL SYLLABLE TI +D30C ; LV # Lo HANGUL SYLLABLE PA +D328 ; LV # Lo HANGUL SYLLABLE PAE +D344 ; LV # Lo HANGUL SYLLABLE PYA +D360 ; LV # Lo HANGUL SYLLABLE PYAE +D37C ; LV # Lo HANGUL SYLLABLE PEO +D398 ; LV # Lo HANGUL SYLLABLE PE +D3B4 ; LV # Lo HANGUL SYLLABLE PYEO +D3D0 ; LV # Lo HANGUL SYLLABLE PYE +D3EC ; LV # Lo HANGUL SYLLABLE PO +D408 ; LV # Lo HANGUL SYLLABLE PWA +D424 ; LV # Lo HANGUL SYLLABLE PWAE +D440 ; LV # Lo HANGUL SYLLABLE POE +D45C ; LV # Lo HANGUL SYLLABLE PYO +D478 ; LV # Lo HANGUL SYLLABLE PU +D494 ; LV # Lo HANGUL SYLLABLE PWEO +D4B0 ; LV # Lo HANGUL SYLLABLE PWE +D4CC ; LV # Lo HANGUL SYLLABLE PWI +D4E8 ; LV # Lo HANGUL SYLLABLE PYU +D504 ; LV # Lo HANGUL SYLLABLE PEU +D520 ; LV # Lo HANGUL SYLLABLE PYI +D53C ; LV # Lo HANGUL SYLLABLE PI +D558 ; LV # Lo HANGUL SYLLABLE HA +D574 ; LV # Lo HANGUL SYLLABLE HAE +D590 ; LV # Lo HANGUL SYLLABLE HYA +D5AC ; LV # Lo HANGUL SYLLABLE HYAE +D5C8 ; LV # Lo HANGUL SYLLABLE HEO +D5E4 ; LV # Lo HANGUL SYLLABLE HE +D600 ; LV # Lo HANGUL SYLLABLE HYEO +D61C ; LV # Lo HANGUL SYLLABLE HYE +D638 ; LV # Lo HANGUL SYLLABLE HO +D654 ; LV # Lo HANGUL SYLLABLE HWA +D670 ; LV # Lo HANGUL SYLLABLE HWAE +D68C ; LV # Lo HANGUL SYLLABLE HOE +D6A8 ; LV # Lo HANGUL SYLLABLE HYO +D6C4 ; LV # Lo HANGUL SYLLABLE HU +D6E0 ; LV # Lo HANGUL SYLLABLE HWEO +D6FC ; LV # Lo HANGUL SYLLABLE HWE +D718 ; LV # Lo HANGUL SYLLABLE HWI +D734 ; LV # Lo HANGUL SYLLABLE HYU +D750 ; LV # Lo HANGUL SYLLABLE HEU +D76C ; LV # Lo HANGUL SYLLABLE HYI +D788 ; LV # Lo HANGUL SYLLABLE HI + +# Total code points: 399 + +# ================================================ + +AC01..AC1B ; LVT # Lo [27] HANGUL SYLLABLE GAG..HANGUL SYLLABLE GAH +AC1D..AC37 ; LVT # Lo [27] HANGUL SYLLABLE GAEG..HANGUL SYLLABLE GAEH +AC39..AC53 ; LVT # Lo [27] HANGUL SYLLABLE GYAG..HANGUL SYLLABLE GYAH +AC55..AC6F ; LVT # Lo [27] HANGUL SYLLABLE GYAEG..HANGUL SYLLABLE GYAEH +AC71..AC8B ; LVT # Lo [27] HANGUL SYLLABLE GEOG..HANGUL SYLLABLE GEOH +AC8D..ACA7 ; LVT # Lo [27] HANGUL SYLLABLE GEG..HANGUL SYLLABLE GEH +ACA9..ACC3 ; LVT # Lo [27] HANGUL SYLLABLE GYEOG..HANGUL SYLLABLE GYEOH +ACC5..ACDF ; LVT # Lo [27] HANGUL SYLLABLE GYEG..HANGUL SYLLABLE GYEH +ACE1..ACFB ; LVT # Lo [27] HANGUL SYLLABLE GOG..HANGUL SYLLABLE GOH +ACFD..AD17 ; LVT # Lo [27] HANGUL SYLLABLE GWAG..HANGUL SYLLABLE GWAH +AD19..AD33 ; LVT # Lo [27] HANGUL SYLLABLE GWAEG..HANGUL SYLLABLE GWAEH +AD35..AD4F ; LVT # Lo [27] HANGUL SYLLABLE GOEG..HANGUL SYLLABLE GOEH +AD51..AD6B ; LVT # Lo [27] HANGUL SYLLABLE GYOG..HANGUL SYLLABLE GYOH +AD6D..AD87 ; LVT # Lo [27] HANGUL SYLLABLE GUG..HANGUL SYLLABLE GUH +AD89..ADA3 ; LVT # Lo [27] HANGUL SYLLABLE GWEOG..HANGUL SYLLABLE GWEOH +ADA5..ADBF ; LVT # Lo [27] HANGUL SYLLABLE GWEG..HANGUL SYLLABLE GWEH +ADC1..ADDB ; LVT # Lo [27] HANGUL SYLLABLE GWIG..HANGUL SYLLABLE GWIH +ADDD..ADF7 ; LVT # Lo [27] HANGUL SYLLABLE GYUG..HANGUL SYLLABLE GYUH +ADF9..AE13 ; LVT # Lo [27] HANGUL SYLLABLE GEUG..HANGUL SYLLABLE GEUH +AE15..AE2F ; LVT # Lo [27] HANGUL SYLLABLE GYIG..HANGUL SYLLABLE GYIH +AE31..AE4B ; LVT # Lo [27] HANGUL SYLLABLE GIG..HANGUL SYLLABLE GIH +AE4D..AE67 ; LVT # Lo [27] HANGUL SYLLABLE GGAG..HANGUL SYLLABLE GGAH +AE69..AE83 ; LVT # Lo [27] HANGUL SYLLABLE GGAEG..HANGUL SYLLABLE GGAEH +AE85..AE9F ; LVT # Lo [27] HANGUL SYLLABLE GGYAG..HANGUL SYLLABLE GGYAH +AEA1..AEBB ; LVT # Lo [27] HANGUL SYLLABLE GGYAEG..HANGUL SYLLABLE GGYAEH +AEBD..AED7 ; LVT # Lo [27] HANGUL SYLLABLE GGEOG..HANGUL SYLLABLE GGEOH +AED9..AEF3 ; LVT # Lo [27] HANGUL SYLLABLE GGEG..HANGUL SYLLABLE GGEH +AEF5..AF0F ; LVT # Lo [27] HANGUL SYLLABLE GGYEOG..HANGUL SYLLABLE GGYEOH +AF11..AF2B ; LVT # Lo [27] HANGUL SYLLABLE GGYEG..HANGUL SYLLABLE GGYEH +AF2D..AF47 ; LVT # Lo [27] HANGUL SYLLABLE GGOG..HANGUL SYLLABLE GGOH +AF49..AF63 ; LVT # Lo [27] HANGUL SYLLABLE GGWAG..HANGUL SYLLABLE GGWAH +AF65..AF7F ; LVT # Lo [27] HANGUL SYLLABLE GGWAEG..HANGUL SYLLABLE GGWAEH +AF81..AF9B ; LVT # Lo [27] HANGUL SYLLABLE GGOEG..HANGUL SYLLABLE GGOEH +AF9D..AFB7 ; LVT # Lo [27] HANGUL SYLLABLE GGYOG..HANGUL SYLLABLE GGYOH +AFB9..AFD3 ; LVT # Lo [27] HANGUL SYLLABLE GGUG..HANGUL SYLLABLE GGUH +AFD5..AFEF ; LVT # Lo [27] HANGUL SYLLABLE GGWEOG..HANGUL SYLLABLE GGWEOH +AFF1..B00B ; LVT # Lo [27] HANGUL SYLLABLE GGWEG..HANGUL SYLLABLE GGWEH +B00D..B027 ; LVT # Lo [27] HANGUL SYLLABLE GGWIG..HANGUL SYLLABLE GGWIH +B029..B043 ; LVT # Lo [27] HANGUL SYLLABLE GGYUG..HANGUL SYLLABLE GGYUH +B045..B05F ; LVT # Lo [27] HANGUL SYLLABLE GGEUG..HANGUL SYLLABLE GGEUH +B061..B07B ; LVT # Lo [27] HANGUL SYLLABLE GGYIG..HANGUL SYLLABLE GGYIH +B07D..B097 ; LVT # Lo [27] HANGUL SYLLABLE GGIG..HANGUL SYLLABLE GGIH +B099..B0B3 ; LVT # Lo [27] HANGUL SYLLABLE NAG..HANGUL SYLLABLE NAH +B0B5..B0CF ; LVT # Lo [27] HANGUL SYLLABLE NAEG..HANGUL SYLLABLE NAEH +B0D1..B0EB ; LVT # Lo [27] HANGUL SYLLABLE NYAG..HANGUL SYLLABLE NYAH +B0ED..B107 ; LVT # Lo [27] HANGUL SYLLABLE NYAEG..HANGUL SYLLABLE NYAEH +B109..B123 ; LVT # Lo [27] HANGUL SYLLABLE NEOG..HANGUL SYLLABLE NEOH +B125..B13F ; LVT # Lo [27] HANGUL SYLLABLE NEG..HANGUL SYLLABLE NEH +B141..B15B ; LVT # Lo [27] HANGUL SYLLABLE NYEOG..HANGUL SYLLABLE NYEOH +B15D..B177 ; LVT # Lo [27] HANGUL SYLLABLE NYEG..HANGUL SYLLABLE NYEH +B179..B193 ; LVT # Lo [27] HANGUL SYLLABLE NOG..HANGUL SYLLABLE NOH +B195..B1AF ; LVT # Lo [27] HANGUL SYLLABLE NWAG..HANGUL SYLLABLE NWAH +B1B1..B1CB ; LVT # Lo [27] HANGUL SYLLABLE NWAEG..HANGUL SYLLABLE NWAEH +B1CD..B1E7 ; LVT # Lo [27] HANGUL SYLLABLE NOEG..HANGUL SYLLABLE NOEH +B1E9..B203 ; LVT # Lo [27] HANGUL SYLLABLE NYOG..HANGUL SYLLABLE NYOH +B205..B21F ; LVT # Lo [27] HANGUL SYLLABLE NUG..HANGUL SYLLABLE NUH +B221..B23B ; LVT # Lo [27] HANGUL SYLLABLE NWEOG..HANGUL SYLLABLE NWEOH +B23D..B257 ; LVT # Lo [27] HANGUL SYLLABLE NWEG..HANGUL SYLLABLE NWEH +B259..B273 ; LVT # Lo [27] HANGUL SYLLABLE NWIG..HANGUL SYLLABLE NWIH +B275..B28F ; LVT # Lo [27] HANGUL SYLLABLE NYUG..HANGUL SYLLABLE NYUH +B291..B2AB ; LVT # Lo [27] HANGUL SYLLABLE NEUG..HANGUL SYLLABLE NEUH +B2AD..B2C7 ; LVT # Lo [27] HANGUL SYLLABLE NYIG..HANGUL SYLLABLE NYIH +B2C9..B2E3 ; LVT # Lo [27] HANGUL SYLLABLE NIG..HANGUL SYLLABLE NIH +B2E5..B2FF ; LVT # Lo [27] HANGUL SYLLABLE DAG..HANGUL SYLLABLE DAH +B301..B31B ; LVT # Lo [27] HANGUL SYLLABLE DAEG..HANGUL SYLLABLE DAEH +B31D..B337 ; LVT # Lo [27] HANGUL SYLLABLE DYAG..HANGUL SYLLABLE DYAH +B339..B353 ; LVT # Lo [27] HANGUL SYLLABLE DYAEG..HANGUL SYLLABLE DYAEH +B355..B36F ; LVT # Lo [27] HANGUL SYLLABLE DEOG..HANGUL SYLLABLE DEOH +B371..B38B ; LVT # Lo [27] HANGUL SYLLABLE DEG..HANGUL SYLLABLE DEH +B38D..B3A7 ; LVT # Lo [27] HANGUL SYLLABLE DYEOG..HANGUL SYLLABLE DYEOH +B3A9..B3C3 ; LVT # Lo [27] HANGUL SYLLABLE DYEG..HANGUL SYLLABLE DYEH +B3C5..B3DF ; LVT # Lo [27] HANGUL SYLLABLE DOG..HANGUL SYLLABLE DOH +B3E1..B3FB ; LVT # Lo [27] HANGUL SYLLABLE DWAG..HANGUL SYLLABLE DWAH +B3FD..B417 ; LVT # Lo [27] HANGUL SYLLABLE DWAEG..HANGUL SYLLABLE DWAEH +B419..B433 ; LVT # Lo [27] HANGUL SYLLABLE DOEG..HANGUL SYLLABLE DOEH +B435..B44F ; LVT # Lo [27] HANGUL SYLLABLE DYOG..HANGUL SYLLABLE DYOH +B451..B46B ; LVT # Lo [27] HANGUL SYLLABLE DUG..HANGUL SYLLABLE DUH +B46D..B487 ; LVT # Lo [27] HANGUL SYLLABLE DWEOG..HANGUL SYLLABLE DWEOH +B489..B4A3 ; LVT # Lo [27] HANGUL SYLLABLE DWEG..HANGUL SYLLABLE DWEH +B4A5..B4BF ; LVT # Lo [27] HANGUL SYLLABLE DWIG..HANGUL SYLLABLE DWIH +B4C1..B4DB ; LVT # Lo [27] HANGUL SYLLABLE DYUG..HANGUL SYLLABLE DYUH +B4DD..B4F7 ; LVT # Lo [27] HANGUL SYLLABLE DEUG..HANGUL SYLLABLE DEUH +B4F9..B513 ; LVT # Lo [27] HANGUL SYLLABLE DYIG..HANGUL SYLLABLE DYIH +B515..B52F ; LVT # Lo [27] HANGUL SYLLABLE DIG..HANGUL SYLLABLE DIH +B531..B54B ; LVT # Lo [27] HANGUL SYLLABLE DDAG..HANGUL SYLLABLE DDAH +B54D..B567 ; LVT # Lo [27] HANGUL SYLLABLE DDAEG..HANGUL SYLLABLE DDAEH +B569..B583 ; LVT # Lo [27] HANGUL SYLLABLE DDYAG..HANGUL SYLLABLE DDYAH +B585..B59F ; LVT # Lo [27] HANGUL SYLLABLE DDYAEG..HANGUL SYLLABLE DDYAEH +B5A1..B5BB ; LVT # Lo [27] HANGUL SYLLABLE DDEOG..HANGUL SYLLABLE DDEOH +B5BD..B5D7 ; LVT # Lo [27] HANGUL SYLLABLE DDEG..HANGUL SYLLABLE DDEH +B5D9..B5F3 ; LVT # Lo [27] HANGUL SYLLABLE DDYEOG..HANGUL SYLLABLE DDYEOH +B5F5..B60F ; LVT # Lo [27] HANGUL SYLLABLE DDYEG..HANGUL SYLLABLE DDYEH +B611..B62B ; LVT # Lo [27] HANGUL SYLLABLE DDOG..HANGUL SYLLABLE DDOH +B62D..B647 ; LVT # Lo [27] HANGUL SYLLABLE DDWAG..HANGUL SYLLABLE DDWAH +B649..B663 ; LVT # Lo [27] HANGUL SYLLABLE DDWAEG..HANGUL SYLLABLE DDWAEH +B665..B67F ; LVT # Lo [27] HANGUL SYLLABLE DDOEG..HANGUL SYLLABLE DDOEH +B681..B69B ; LVT # Lo [27] HANGUL SYLLABLE DDYOG..HANGUL SYLLABLE DDYOH +B69D..B6B7 ; LVT # Lo [27] HANGUL SYLLABLE DDUG..HANGUL SYLLABLE DDUH +B6B9..B6D3 ; LVT # Lo [27] HANGUL SYLLABLE DDWEOG..HANGUL SYLLABLE DDWEOH +B6D5..B6EF ; LVT # Lo [27] HANGUL SYLLABLE DDWEG..HANGUL SYLLABLE DDWEH +B6F1..B70B ; LVT # Lo [27] HANGUL SYLLABLE DDWIG..HANGUL SYLLABLE DDWIH +B70D..B727 ; LVT # Lo [27] HANGUL SYLLABLE DDYUG..HANGUL SYLLABLE DDYUH +B729..B743 ; LVT # Lo [27] HANGUL SYLLABLE DDEUG..HANGUL SYLLABLE DDEUH +B745..B75F ; LVT # Lo [27] HANGUL SYLLABLE DDYIG..HANGUL SYLLABLE DDYIH +B761..B77B ; LVT # Lo [27] HANGUL SYLLABLE DDIG..HANGUL SYLLABLE DDIH +B77D..B797 ; LVT # Lo [27] HANGUL SYLLABLE RAG..HANGUL SYLLABLE RAH +B799..B7B3 ; LVT # Lo [27] HANGUL SYLLABLE RAEG..HANGUL SYLLABLE RAEH +B7B5..B7CF ; LVT # Lo [27] HANGUL SYLLABLE RYAG..HANGUL SYLLABLE RYAH +B7D1..B7EB ; LVT # Lo [27] HANGUL SYLLABLE RYAEG..HANGUL SYLLABLE RYAEH +B7ED..B807 ; LVT # Lo [27] HANGUL SYLLABLE REOG..HANGUL SYLLABLE REOH +B809..B823 ; LVT # Lo [27] HANGUL SYLLABLE REG..HANGUL SYLLABLE REH +B825..B83F ; LVT # Lo [27] HANGUL SYLLABLE RYEOG..HANGUL SYLLABLE RYEOH +B841..B85B ; LVT # Lo [27] HANGUL SYLLABLE RYEG..HANGUL SYLLABLE RYEH +B85D..B877 ; LVT # Lo [27] HANGUL SYLLABLE ROG..HANGUL SYLLABLE ROH +B879..B893 ; LVT # Lo [27] HANGUL SYLLABLE RWAG..HANGUL SYLLABLE RWAH +B895..B8AF ; LVT # Lo [27] HANGUL SYLLABLE RWAEG..HANGUL SYLLABLE RWAEH +B8B1..B8CB ; LVT # Lo [27] HANGUL SYLLABLE ROEG..HANGUL SYLLABLE ROEH +B8CD..B8E7 ; LVT # Lo [27] HANGUL SYLLABLE RYOG..HANGUL SYLLABLE RYOH +B8E9..B903 ; LVT # Lo [27] HANGUL SYLLABLE RUG..HANGUL SYLLABLE RUH +B905..B91F ; LVT # Lo [27] HANGUL SYLLABLE RWEOG..HANGUL SYLLABLE RWEOH +B921..B93B ; LVT # Lo [27] HANGUL SYLLABLE RWEG..HANGUL SYLLABLE RWEH +B93D..B957 ; LVT # Lo [27] HANGUL SYLLABLE RWIG..HANGUL SYLLABLE RWIH +B959..B973 ; LVT # Lo [27] HANGUL SYLLABLE RYUG..HANGUL SYLLABLE RYUH +B975..B98F ; LVT # Lo [27] HANGUL SYLLABLE REUG..HANGUL SYLLABLE REUH +B991..B9AB ; LVT # Lo [27] HANGUL SYLLABLE RYIG..HANGUL SYLLABLE RYIH +B9AD..B9C7 ; LVT # Lo [27] HANGUL SYLLABLE RIG..HANGUL SYLLABLE RIH +B9C9..B9E3 ; LVT # Lo [27] HANGUL SYLLABLE MAG..HANGUL SYLLABLE MAH +B9E5..B9FF ; LVT # Lo [27] HANGUL SYLLABLE MAEG..HANGUL SYLLABLE MAEH +BA01..BA1B ; LVT # Lo [27] HANGUL SYLLABLE MYAG..HANGUL SYLLABLE MYAH +BA1D..BA37 ; LVT # Lo [27] HANGUL SYLLABLE MYAEG..HANGUL SYLLABLE MYAEH +BA39..BA53 ; LVT # Lo [27] HANGUL SYLLABLE MEOG..HANGUL SYLLABLE MEOH +BA55..BA6F ; LVT # Lo [27] HANGUL SYLLABLE MEG..HANGUL SYLLABLE MEH +BA71..BA8B ; LVT # Lo [27] HANGUL SYLLABLE MYEOG..HANGUL SYLLABLE MYEOH +BA8D..BAA7 ; LVT # Lo [27] HANGUL SYLLABLE MYEG..HANGUL SYLLABLE MYEH +BAA9..BAC3 ; LVT # Lo [27] HANGUL SYLLABLE MOG..HANGUL SYLLABLE MOH +BAC5..BADF ; LVT # Lo [27] HANGUL SYLLABLE MWAG..HANGUL SYLLABLE MWAH +BAE1..BAFB ; LVT # Lo [27] HANGUL SYLLABLE MWAEG..HANGUL SYLLABLE MWAEH +BAFD..BB17 ; LVT # Lo [27] HANGUL SYLLABLE MOEG..HANGUL SYLLABLE MOEH +BB19..BB33 ; LVT # Lo [27] HANGUL SYLLABLE MYOG..HANGUL SYLLABLE MYOH +BB35..BB4F ; LVT # Lo [27] HANGUL SYLLABLE MUG..HANGUL SYLLABLE MUH +BB51..BB6B ; LVT # Lo [27] HANGUL SYLLABLE MWEOG..HANGUL SYLLABLE MWEOH +BB6D..BB87 ; LVT # Lo [27] HANGUL SYLLABLE MWEG..HANGUL SYLLABLE MWEH +BB89..BBA3 ; LVT # Lo [27] HANGUL SYLLABLE MWIG..HANGUL SYLLABLE MWIH +BBA5..BBBF ; LVT # Lo [27] HANGUL SYLLABLE MYUG..HANGUL SYLLABLE MYUH +BBC1..BBDB ; LVT # Lo [27] HANGUL SYLLABLE MEUG..HANGUL SYLLABLE MEUH +BBDD..BBF7 ; LVT # Lo [27] HANGUL SYLLABLE MYIG..HANGUL SYLLABLE MYIH +BBF9..BC13 ; LVT # Lo [27] HANGUL SYLLABLE MIG..HANGUL SYLLABLE MIH +BC15..BC2F ; LVT # Lo [27] HANGUL SYLLABLE BAG..HANGUL SYLLABLE BAH +BC31..BC4B ; LVT # Lo [27] HANGUL SYLLABLE BAEG..HANGUL SYLLABLE BAEH +BC4D..BC67 ; LVT # Lo [27] HANGUL SYLLABLE BYAG..HANGUL SYLLABLE BYAH +BC69..BC83 ; LVT # Lo [27] HANGUL SYLLABLE BYAEG..HANGUL SYLLABLE BYAEH +BC85..BC9F ; LVT # Lo [27] HANGUL SYLLABLE BEOG..HANGUL SYLLABLE BEOH +BCA1..BCBB ; LVT # Lo [27] HANGUL SYLLABLE BEG..HANGUL SYLLABLE BEH +BCBD..BCD7 ; LVT # Lo [27] HANGUL SYLLABLE BYEOG..HANGUL SYLLABLE BYEOH +BCD9..BCF3 ; LVT # Lo [27] HANGUL SYLLABLE BYEG..HANGUL SYLLABLE BYEH +BCF5..BD0F ; LVT # Lo [27] HANGUL SYLLABLE BOG..HANGUL SYLLABLE BOH +BD11..BD2B ; LVT # Lo [27] HANGUL SYLLABLE BWAG..HANGUL SYLLABLE BWAH +BD2D..BD47 ; LVT # Lo [27] HANGUL SYLLABLE BWAEG..HANGUL SYLLABLE BWAEH +BD49..BD63 ; LVT # Lo [27] HANGUL SYLLABLE BOEG..HANGUL SYLLABLE BOEH +BD65..BD7F ; LVT # Lo [27] HANGUL SYLLABLE BYOG..HANGUL SYLLABLE BYOH +BD81..BD9B ; LVT # Lo [27] HANGUL SYLLABLE BUG..HANGUL SYLLABLE BUH +BD9D..BDB7 ; LVT # Lo [27] HANGUL SYLLABLE BWEOG..HANGUL SYLLABLE BWEOH +BDB9..BDD3 ; LVT # Lo [27] HANGUL SYLLABLE BWEG..HANGUL SYLLABLE BWEH +BDD5..BDEF ; LVT # Lo [27] HANGUL SYLLABLE BWIG..HANGUL SYLLABLE BWIH +BDF1..BE0B ; LVT # Lo [27] HANGUL SYLLABLE BYUG..HANGUL SYLLABLE BYUH +BE0D..BE27 ; LVT # Lo [27] HANGUL SYLLABLE BEUG..HANGUL SYLLABLE BEUH +BE29..BE43 ; LVT # Lo [27] HANGUL SYLLABLE BYIG..HANGUL SYLLABLE BYIH +BE45..BE5F ; LVT # Lo [27] HANGUL SYLLABLE BIG..HANGUL SYLLABLE BIH +BE61..BE7B ; LVT # Lo [27] HANGUL SYLLABLE BBAG..HANGUL SYLLABLE BBAH +BE7D..BE97 ; LVT # Lo [27] HANGUL SYLLABLE BBAEG..HANGUL SYLLABLE BBAEH +BE99..BEB3 ; LVT # Lo [27] HANGUL SYLLABLE BBYAG..HANGUL SYLLABLE BBYAH +BEB5..BECF ; LVT # Lo [27] HANGUL SYLLABLE BBYAEG..HANGUL SYLLABLE BBYAEH +BED1..BEEB ; LVT # Lo [27] HANGUL SYLLABLE BBEOG..HANGUL SYLLABLE BBEOH +BEED..BF07 ; LVT # Lo [27] HANGUL SYLLABLE BBEG..HANGUL SYLLABLE BBEH +BF09..BF23 ; LVT # Lo [27] HANGUL SYLLABLE BBYEOG..HANGUL SYLLABLE BBYEOH +BF25..BF3F ; LVT # Lo [27] HANGUL SYLLABLE BBYEG..HANGUL SYLLABLE BBYEH +BF41..BF5B ; LVT # Lo [27] HANGUL SYLLABLE BBOG..HANGUL SYLLABLE BBOH +BF5D..BF77 ; LVT # Lo [27] HANGUL SYLLABLE BBWAG..HANGUL SYLLABLE BBWAH +BF79..BF93 ; LVT # Lo [27] HANGUL SYLLABLE BBWAEG..HANGUL SYLLABLE BBWAEH +BF95..BFAF ; LVT # Lo [27] HANGUL SYLLABLE BBOEG..HANGUL SYLLABLE BBOEH +BFB1..BFCB ; LVT # Lo [27] HANGUL SYLLABLE BBYOG..HANGUL SYLLABLE BBYOH +BFCD..BFE7 ; LVT # Lo [27] HANGUL SYLLABLE BBUG..HANGUL SYLLABLE BBUH +BFE9..C003 ; LVT # Lo [27] HANGUL SYLLABLE BBWEOG..HANGUL SYLLABLE BBWEOH +C005..C01F ; LVT # Lo [27] HANGUL SYLLABLE BBWEG..HANGUL SYLLABLE BBWEH +C021..C03B ; LVT # Lo [27] HANGUL SYLLABLE BBWIG..HANGUL SYLLABLE BBWIH +C03D..C057 ; LVT # Lo [27] HANGUL SYLLABLE BBYUG..HANGUL SYLLABLE BBYUH +C059..C073 ; LVT # Lo [27] HANGUL SYLLABLE BBEUG..HANGUL SYLLABLE BBEUH +C075..C08F ; LVT # Lo [27] HANGUL SYLLABLE BBYIG..HANGUL SYLLABLE BBYIH +C091..C0AB ; LVT # Lo [27] HANGUL SYLLABLE BBIG..HANGUL SYLLABLE BBIH +C0AD..C0C7 ; LVT # Lo [27] HANGUL SYLLABLE SAG..HANGUL SYLLABLE SAH +C0C9..C0E3 ; LVT # Lo [27] HANGUL SYLLABLE SAEG..HANGUL SYLLABLE SAEH +C0E5..C0FF ; LVT # Lo [27] HANGUL SYLLABLE SYAG..HANGUL SYLLABLE SYAH +C101..C11B ; LVT # Lo [27] HANGUL SYLLABLE SYAEG..HANGUL SYLLABLE SYAEH +C11D..C137 ; LVT # Lo [27] HANGUL SYLLABLE SEOG..HANGUL SYLLABLE SEOH +C139..C153 ; LVT # Lo [27] HANGUL SYLLABLE SEG..HANGUL SYLLABLE SEH +C155..C16F ; LVT # Lo [27] HANGUL SYLLABLE SYEOG..HANGUL SYLLABLE SYEOH +C171..C18B ; LVT # Lo [27] HANGUL SYLLABLE SYEG..HANGUL SYLLABLE SYEH +C18D..C1A7 ; LVT # Lo [27] HANGUL SYLLABLE SOG..HANGUL SYLLABLE SOH +C1A9..C1C3 ; LVT # Lo [27] HANGUL SYLLABLE SWAG..HANGUL SYLLABLE SWAH +C1C5..C1DF ; LVT # Lo [27] HANGUL SYLLABLE SWAEG..HANGUL SYLLABLE SWAEH +C1E1..C1FB ; LVT # Lo [27] HANGUL SYLLABLE SOEG..HANGUL SYLLABLE SOEH +C1FD..C217 ; LVT # Lo [27] HANGUL SYLLABLE SYOG..HANGUL SYLLABLE SYOH +C219..C233 ; LVT # Lo [27] HANGUL SYLLABLE SUG..HANGUL SYLLABLE SUH +C235..C24F ; LVT # Lo [27] HANGUL SYLLABLE SWEOG..HANGUL SYLLABLE SWEOH +C251..C26B ; LVT # Lo [27] HANGUL SYLLABLE SWEG..HANGUL SYLLABLE SWEH +C26D..C287 ; LVT # Lo [27] HANGUL SYLLABLE SWIG..HANGUL SYLLABLE SWIH +C289..C2A3 ; LVT # Lo [27] HANGUL SYLLABLE SYUG..HANGUL SYLLABLE SYUH +C2A5..C2BF ; LVT # Lo [27] HANGUL SYLLABLE SEUG..HANGUL SYLLABLE SEUH +C2C1..C2DB ; LVT # Lo [27] HANGUL SYLLABLE SYIG..HANGUL SYLLABLE SYIH +C2DD..C2F7 ; LVT # Lo [27] HANGUL SYLLABLE SIG..HANGUL SYLLABLE SIH +C2F9..C313 ; LVT # Lo [27] HANGUL SYLLABLE SSAG..HANGUL SYLLABLE SSAH +C315..C32F ; LVT # Lo [27] HANGUL SYLLABLE SSAEG..HANGUL SYLLABLE SSAEH +C331..C34B ; LVT # Lo [27] HANGUL SYLLABLE SSYAG..HANGUL SYLLABLE SSYAH +C34D..C367 ; LVT # Lo [27] HANGUL SYLLABLE SSYAEG..HANGUL SYLLABLE SSYAEH +C369..C383 ; LVT # Lo [27] HANGUL SYLLABLE SSEOG..HANGUL SYLLABLE SSEOH +C385..C39F ; LVT # Lo [27] HANGUL SYLLABLE SSEG..HANGUL SYLLABLE SSEH +C3A1..C3BB ; LVT # Lo [27] HANGUL SYLLABLE SSYEOG..HANGUL SYLLABLE SSYEOH +C3BD..C3D7 ; LVT # Lo [27] HANGUL SYLLABLE SSYEG..HANGUL SYLLABLE SSYEH +C3D9..C3F3 ; LVT # Lo [27] HANGUL SYLLABLE SSOG..HANGUL SYLLABLE SSOH +C3F5..C40F ; LVT # Lo [27] HANGUL SYLLABLE SSWAG..HANGUL SYLLABLE SSWAH +C411..C42B ; LVT # Lo [27] HANGUL SYLLABLE SSWAEG..HANGUL SYLLABLE SSWAEH +C42D..C447 ; LVT # Lo [27] HANGUL SYLLABLE SSOEG..HANGUL SYLLABLE SSOEH +C449..C463 ; LVT # Lo [27] HANGUL SYLLABLE SSYOG..HANGUL SYLLABLE SSYOH +C465..C47F ; LVT # Lo [27] HANGUL SYLLABLE SSUG..HANGUL SYLLABLE SSUH +C481..C49B ; LVT # Lo [27] HANGUL SYLLABLE SSWEOG..HANGUL SYLLABLE SSWEOH +C49D..C4B7 ; LVT # Lo [27] HANGUL SYLLABLE SSWEG..HANGUL SYLLABLE SSWEH +C4B9..C4D3 ; LVT # Lo [27] HANGUL SYLLABLE SSWIG..HANGUL SYLLABLE SSWIH +C4D5..C4EF ; LVT # Lo [27] HANGUL SYLLABLE SSYUG..HANGUL SYLLABLE SSYUH +C4F1..C50B ; LVT # Lo [27] HANGUL SYLLABLE SSEUG..HANGUL SYLLABLE SSEUH +C50D..C527 ; LVT # Lo [27] HANGUL SYLLABLE SSYIG..HANGUL SYLLABLE SSYIH +C529..C543 ; LVT # Lo [27] HANGUL SYLLABLE SSIG..HANGUL SYLLABLE SSIH +C545..C55F ; LVT # Lo [27] HANGUL SYLLABLE AG..HANGUL SYLLABLE AH +C561..C57B ; LVT # Lo [27] HANGUL SYLLABLE AEG..HANGUL SYLLABLE AEH +C57D..C597 ; LVT # Lo [27] HANGUL SYLLABLE YAG..HANGUL SYLLABLE YAH +C599..C5B3 ; LVT # Lo [27] HANGUL SYLLABLE YAEG..HANGUL SYLLABLE YAEH +C5B5..C5CF ; LVT # Lo [27] HANGUL SYLLABLE EOG..HANGUL SYLLABLE EOH +C5D1..C5EB ; LVT # Lo [27] HANGUL SYLLABLE EG..HANGUL SYLLABLE EH +C5ED..C607 ; LVT # Lo [27] HANGUL SYLLABLE YEOG..HANGUL SYLLABLE YEOH +C609..C623 ; LVT # Lo [27] HANGUL SYLLABLE YEG..HANGUL SYLLABLE YEH +C625..C63F ; LVT # Lo [27] HANGUL SYLLABLE OG..HANGUL SYLLABLE OH +C641..C65B ; LVT # Lo [27] HANGUL SYLLABLE WAG..HANGUL SYLLABLE WAH +C65D..C677 ; LVT # Lo [27] HANGUL SYLLABLE WAEG..HANGUL SYLLABLE WAEH +C679..C693 ; LVT # Lo [27] HANGUL SYLLABLE OEG..HANGUL SYLLABLE OEH +C695..C6AF ; LVT # Lo [27] HANGUL SYLLABLE YOG..HANGUL SYLLABLE YOH +C6B1..C6CB ; LVT # Lo [27] HANGUL SYLLABLE UG..HANGUL SYLLABLE UH +C6CD..C6E7 ; LVT # Lo [27] HANGUL SYLLABLE WEOG..HANGUL SYLLABLE WEOH +C6E9..C703 ; LVT # Lo [27] HANGUL SYLLABLE WEG..HANGUL SYLLABLE WEH +C705..C71F ; LVT # Lo [27] HANGUL SYLLABLE WIG..HANGUL SYLLABLE WIH +C721..C73B ; LVT # Lo [27] HANGUL SYLLABLE YUG..HANGUL SYLLABLE YUH +C73D..C757 ; LVT # Lo [27] HANGUL SYLLABLE EUG..HANGUL SYLLABLE EUH +C759..C773 ; LVT # Lo [27] HANGUL SYLLABLE YIG..HANGUL SYLLABLE YIH +C775..C78F ; LVT # Lo [27] HANGUL SYLLABLE IG..HANGUL SYLLABLE IH +C791..C7AB ; LVT # Lo [27] HANGUL SYLLABLE JAG..HANGUL SYLLABLE JAH +C7AD..C7C7 ; LVT # Lo [27] HANGUL SYLLABLE JAEG..HANGUL SYLLABLE JAEH +C7C9..C7E3 ; LVT # Lo [27] HANGUL SYLLABLE JYAG..HANGUL SYLLABLE JYAH +C7E5..C7FF ; LVT # Lo [27] HANGUL SYLLABLE JYAEG..HANGUL SYLLABLE JYAEH +C801..C81B ; LVT # Lo [27] HANGUL SYLLABLE JEOG..HANGUL SYLLABLE JEOH +C81D..C837 ; LVT # Lo [27] HANGUL SYLLABLE JEG..HANGUL SYLLABLE JEH +C839..C853 ; LVT # Lo [27] HANGUL SYLLABLE JYEOG..HANGUL SYLLABLE JYEOH +C855..C86F ; LVT # Lo [27] HANGUL SYLLABLE JYEG..HANGUL SYLLABLE JYEH +C871..C88B ; LVT # Lo [27] HANGUL SYLLABLE JOG..HANGUL SYLLABLE JOH +C88D..C8A7 ; LVT # Lo [27] HANGUL SYLLABLE JWAG..HANGUL SYLLABLE JWAH +C8A9..C8C3 ; LVT # Lo [27] HANGUL SYLLABLE JWAEG..HANGUL SYLLABLE JWAEH +C8C5..C8DF ; LVT # Lo [27] HANGUL SYLLABLE JOEG..HANGUL SYLLABLE JOEH +C8E1..C8FB ; LVT # Lo [27] HANGUL SYLLABLE JYOG..HANGUL SYLLABLE JYOH +C8FD..C917 ; LVT # Lo [27] HANGUL SYLLABLE JUG..HANGUL SYLLABLE JUH +C919..C933 ; LVT # Lo [27] HANGUL SYLLABLE JWEOG..HANGUL SYLLABLE JWEOH +C935..C94F ; LVT # Lo [27] HANGUL SYLLABLE JWEG..HANGUL SYLLABLE JWEH +C951..C96B ; LVT # Lo [27] HANGUL SYLLABLE JWIG..HANGUL SYLLABLE JWIH +C96D..C987 ; LVT # Lo [27] HANGUL SYLLABLE JYUG..HANGUL SYLLABLE JYUH +C989..C9A3 ; LVT # Lo [27] HANGUL SYLLABLE JEUG..HANGUL SYLLABLE JEUH +C9A5..C9BF ; LVT # Lo [27] HANGUL SYLLABLE JYIG..HANGUL SYLLABLE JYIH +C9C1..C9DB ; LVT # Lo [27] HANGUL SYLLABLE JIG..HANGUL SYLLABLE JIH +C9DD..C9F7 ; LVT # Lo [27] HANGUL SYLLABLE JJAG..HANGUL SYLLABLE JJAH +C9F9..CA13 ; LVT # Lo [27] HANGUL SYLLABLE JJAEG..HANGUL SYLLABLE JJAEH +CA15..CA2F ; LVT # Lo [27] HANGUL SYLLABLE JJYAG..HANGUL SYLLABLE JJYAH +CA31..CA4B ; LVT # Lo [27] HANGUL SYLLABLE JJYAEG..HANGUL SYLLABLE JJYAEH +CA4D..CA67 ; LVT # Lo [27] HANGUL SYLLABLE JJEOG..HANGUL SYLLABLE JJEOH +CA69..CA83 ; LVT # Lo [27] HANGUL SYLLABLE JJEG..HANGUL SYLLABLE JJEH +CA85..CA9F ; LVT # Lo [27] HANGUL SYLLABLE JJYEOG..HANGUL SYLLABLE JJYEOH +CAA1..CABB ; LVT # Lo [27] HANGUL SYLLABLE JJYEG..HANGUL SYLLABLE JJYEH +CABD..CAD7 ; LVT # Lo [27] HANGUL SYLLABLE JJOG..HANGUL SYLLABLE JJOH +CAD9..CAF3 ; LVT # Lo [27] HANGUL SYLLABLE JJWAG..HANGUL SYLLABLE JJWAH +CAF5..CB0F ; LVT # Lo [27] HANGUL SYLLABLE JJWAEG..HANGUL SYLLABLE JJWAEH +CB11..CB2B ; LVT # Lo [27] HANGUL SYLLABLE JJOEG..HANGUL SYLLABLE JJOEH +CB2D..CB47 ; LVT # Lo [27] HANGUL SYLLABLE JJYOG..HANGUL SYLLABLE JJYOH +CB49..CB63 ; LVT # Lo [27] HANGUL SYLLABLE JJUG..HANGUL SYLLABLE JJUH +CB65..CB7F ; LVT # Lo [27] HANGUL SYLLABLE JJWEOG..HANGUL SYLLABLE JJWEOH +CB81..CB9B ; LVT # Lo [27] HANGUL SYLLABLE JJWEG..HANGUL SYLLABLE JJWEH +CB9D..CBB7 ; LVT # Lo [27] HANGUL SYLLABLE JJWIG..HANGUL SYLLABLE JJWIH +CBB9..CBD3 ; LVT # Lo [27] HANGUL SYLLABLE JJYUG..HANGUL SYLLABLE JJYUH +CBD5..CBEF ; LVT # Lo [27] HANGUL SYLLABLE JJEUG..HANGUL SYLLABLE JJEUH +CBF1..CC0B ; LVT # Lo [27] HANGUL SYLLABLE JJYIG..HANGUL SYLLABLE JJYIH +CC0D..CC27 ; LVT # Lo [27] HANGUL SYLLABLE JJIG..HANGUL SYLLABLE JJIH +CC29..CC43 ; LVT # Lo [27] HANGUL SYLLABLE CAG..HANGUL SYLLABLE CAH +CC45..CC5F ; LVT # Lo [27] HANGUL SYLLABLE CAEG..HANGUL SYLLABLE CAEH +CC61..CC7B ; LVT # Lo [27] HANGUL SYLLABLE CYAG..HANGUL SYLLABLE CYAH +CC7D..CC97 ; LVT # Lo [27] HANGUL SYLLABLE CYAEG..HANGUL SYLLABLE CYAEH +CC99..CCB3 ; LVT # Lo [27] HANGUL SYLLABLE CEOG..HANGUL SYLLABLE CEOH +CCB5..CCCF ; LVT # Lo [27] HANGUL SYLLABLE CEG..HANGUL SYLLABLE CEH +CCD1..CCEB ; LVT # Lo [27] HANGUL SYLLABLE CYEOG..HANGUL SYLLABLE CYEOH +CCED..CD07 ; LVT # Lo [27] HANGUL SYLLABLE CYEG..HANGUL SYLLABLE CYEH +CD09..CD23 ; LVT # Lo [27] HANGUL SYLLABLE COG..HANGUL SYLLABLE COH +CD25..CD3F ; LVT # Lo [27] HANGUL SYLLABLE CWAG..HANGUL SYLLABLE CWAH +CD41..CD5B ; LVT # Lo [27] HANGUL SYLLABLE CWAEG..HANGUL SYLLABLE CWAEH +CD5D..CD77 ; LVT # Lo [27] HANGUL SYLLABLE COEG..HANGUL SYLLABLE COEH +CD79..CD93 ; LVT # Lo [27] HANGUL SYLLABLE CYOG..HANGUL SYLLABLE CYOH +CD95..CDAF ; LVT # Lo [27] HANGUL SYLLABLE CUG..HANGUL SYLLABLE CUH +CDB1..CDCB ; LVT # Lo [27] HANGUL SYLLABLE CWEOG..HANGUL SYLLABLE CWEOH +CDCD..CDE7 ; LVT # Lo [27] HANGUL SYLLABLE CWEG..HANGUL SYLLABLE CWEH +CDE9..CE03 ; LVT # Lo [27] HANGUL SYLLABLE CWIG..HANGUL SYLLABLE CWIH +CE05..CE1F ; LVT # Lo [27] HANGUL SYLLABLE CYUG..HANGUL SYLLABLE CYUH +CE21..CE3B ; LVT # Lo [27] HANGUL SYLLABLE CEUG..HANGUL SYLLABLE CEUH +CE3D..CE57 ; LVT # Lo [27] HANGUL SYLLABLE CYIG..HANGUL SYLLABLE CYIH +CE59..CE73 ; LVT # Lo [27] HANGUL SYLLABLE CIG..HANGUL SYLLABLE CIH +CE75..CE8F ; LVT # Lo [27] HANGUL SYLLABLE KAG..HANGUL SYLLABLE KAH +CE91..CEAB ; LVT # Lo [27] HANGUL SYLLABLE KAEG..HANGUL SYLLABLE KAEH +CEAD..CEC7 ; LVT # Lo [27] HANGUL SYLLABLE KYAG..HANGUL SYLLABLE KYAH +CEC9..CEE3 ; LVT # Lo [27] HANGUL SYLLABLE KYAEG..HANGUL SYLLABLE KYAEH +CEE5..CEFF ; LVT # Lo [27] HANGUL SYLLABLE KEOG..HANGUL SYLLABLE KEOH +CF01..CF1B ; LVT # Lo [27] HANGUL SYLLABLE KEG..HANGUL SYLLABLE KEH +CF1D..CF37 ; LVT # Lo [27] HANGUL SYLLABLE KYEOG..HANGUL SYLLABLE KYEOH +CF39..CF53 ; LVT # Lo [27] HANGUL SYLLABLE KYEG..HANGUL SYLLABLE KYEH +CF55..CF6F ; LVT # Lo [27] HANGUL SYLLABLE KOG..HANGUL SYLLABLE KOH +CF71..CF8B ; LVT # Lo [27] HANGUL SYLLABLE KWAG..HANGUL SYLLABLE KWAH +CF8D..CFA7 ; LVT # Lo [27] HANGUL SYLLABLE KWAEG..HANGUL SYLLABLE KWAEH +CFA9..CFC3 ; LVT # Lo [27] HANGUL SYLLABLE KOEG..HANGUL SYLLABLE KOEH +CFC5..CFDF ; LVT # Lo [27] HANGUL SYLLABLE KYOG..HANGUL SYLLABLE KYOH +CFE1..CFFB ; LVT # Lo [27] HANGUL SYLLABLE KUG..HANGUL SYLLABLE KUH +CFFD..D017 ; LVT # Lo [27] HANGUL SYLLABLE KWEOG..HANGUL SYLLABLE KWEOH +D019..D033 ; LVT # Lo [27] HANGUL SYLLABLE KWEG..HANGUL SYLLABLE KWEH +D035..D04F ; LVT # Lo [27] HANGUL SYLLABLE KWIG..HANGUL SYLLABLE KWIH +D051..D06B ; LVT # Lo [27] HANGUL SYLLABLE KYUG..HANGUL SYLLABLE KYUH +D06D..D087 ; LVT # Lo [27] HANGUL SYLLABLE KEUG..HANGUL SYLLABLE KEUH +D089..D0A3 ; LVT # Lo [27] HANGUL SYLLABLE KYIG..HANGUL SYLLABLE KYIH +D0A5..D0BF ; LVT # Lo [27] HANGUL SYLLABLE KIG..HANGUL SYLLABLE KIH +D0C1..D0DB ; LVT # Lo [27] HANGUL SYLLABLE TAG..HANGUL SYLLABLE TAH +D0DD..D0F7 ; LVT # Lo [27] HANGUL SYLLABLE TAEG..HANGUL SYLLABLE TAEH +D0F9..D113 ; LVT # Lo [27] HANGUL SYLLABLE TYAG..HANGUL SYLLABLE TYAH +D115..D12F ; LVT # Lo [27] HANGUL SYLLABLE TYAEG..HANGUL SYLLABLE TYAEH +D131..D14B ; LVT # Lo [27] HANGUL SYLLABLE TEOG..HANGUL SYLLABLE TEOH +D14D..D167 ; LVT # Lo [27] HANGUL SYLLABLE TEG..HANGUL SYLLABLE TEH +D169..D183 ; LVT # Lo [27] HANGUL SYLLABLE TYEOG..HANGUL SYLLABLE TYEOH +D185..D19F ; LVT # Lo [27] HANGUL SYLLABLE TYEG..HANGUL SYLLABLE TYEH +D1A1..D1BB ; LVT # Lo [27] HANGUL SYLLABLE TOG..HANGUL SYLLABLE TOH +D1BD..D1D7 ; LVT # Lo [27] HANGUL SYLLABLE TWAG..HANGUL SYLLABLE TWAH +D1D9..D1F3 ; LVT # Lo [27] HANGUL SYLLABLE TWAEG..HANGUL SYLLABLE TWAEH +D1F5..D20F ; LVT # Lo [27] HANGUL SYLLABLE TOEG..HANGUL SYLLABLE TOEH +D211..D22B ; LVT # Lo [27] HANGUL SYLLABLE TYOG..HANGUL SYLLABLE TYOH +D22D..D247 ; LVT # Lo [27] HANGUL SYLLABLE TUG..HANGUL SYLLABLE TUH +D249..D263 ; LVT # Lo [27] HANGUL SYLLABLE TWEOG..HANGUL SYLLABLE TWEOH +D265..D27F ; LVT # Lo [27] HANGUL SYLLABLE TWEG..HANGUL SYLLABLE TWEH +D281..D29B ; LVT # Lo [27] HANGUL SYLLABLE TWIG..HANGUL SYLLABLE TWIH +D29D..D2B7 ; LVT # Lo [27] HANGUL SYLLABLE TYUG..HANGUL SYLLABLE TYUH +D2B9..D2D3 ; LVT # Lo [27] HANGUL SYLLABLE TEUG..HANGUL SYLLABLE TEUH +D2D5..D2EF ; LVT # Lo [27] HANGUL SYLLABLE TYIG..HANGUL SYLLABLE TYIH +D2F1..D30B ; LVT # Lo [27] HANGUL SYLLABLE TIG..HANGUL SYLLABLE TIH +D30D..D327 ; LVT # Lo [27] HANGUL SYLLABLE PAG..HANGUL SYLLABLE PAH +D329..D343 ; LVT # Lo [27] HANGUL SYLLABLE PAEG..HANGUL SYLLABLE PAEH +D345..D35F ; LVT # Lo [27] HANGUL SYLLABLE PYAG..HANGUL SYLLABLE PYAH +D361..D37B ; LVT # Lo [27] HANGUL SYLLABLE PYAEG..HANGUL SYLLABLE PYAEH +D37D..D397 ; LVT # Lo [27] HANGUL SYLLABLE PEOG..HANGUL SYLLABLE PEOH +D399..D3B3 ; LVT # Lo [27] HANGUL SYLLABLE PEG..HANGUL SYLLABLE PEH +D3B5..D3CF ; LVT # Lo [27] HANGUL SYLLABLE PYEOG..HANGUL SYLLABLE PYEOH +D3D1..D3EB ; LVT # Lo [27] HANGUL SYLLABLE PYEG..HANGUL SYLLABLE PYEH +D3ED..D407 ; LVT # Lo [27] HANGUL SYLLABLE POG..HANGUL SYLLABLE POH +D409..D423 ; LVT # Lo [27] HANGUL SYLLABLE PWAG..HANGUL SYLLABLE PWAH +D425..D43F ; LVT # Lo [27] HANGUL SYLLABLE PWAEG..HANGUL SYLLABLE PWAEH +D441..D45B ; LVT # Lo [27] HANGUL SYLLABLE POEG..HANGUL SYLLABLE POEH +D45D..D477 ; LVT # Lo [27] HANGUL SYLLABLE PYOG..HANGUL SYLLABLE PYOH +D479..D493 ; LVT # Lo [27] HANGUL SYLLABLE PUG..HANGUL SYLLABLE PUH +D495..D4AF ; LVT # Lo [27] HANGUL SYLLABLE PWEOG..HANGUL SYLLABLE PWEOH +D4B1..D4CB ; LVT # Lo [27] HANGUL SYLLABLE PWEG..HANGUL SYLLABLE PWEH +D4CD..D4E7 ; LVT # Lo [27] HANGUL SYLLABLE PWIG..HANGUL SYLLABLE PWIH +D4E9..D503 ; LVT # Lo [27] HANGUL SYLLABLE PYUG..HANGUL SYLLABLE PYUH +D505..D51F ; LVT # Lo [27] HANGUL SYLLABLE PEUG..HANGUL SYLLABLE PEUH +D521..D53B ; LVT # Lo [27] HANGUL SYLLABLE PYIG..HANGUL SYLLABLE PYIH +D53D..D557 ; LVT # Lo [27] HANGUL SYLLABLE PIG..HANGUL SYLLABLE PIH +D559..D573 ; LVT # Lo [27] HANGUL SYLLABLE HAG..HANGUL SYLLABLE HAH +D575..D58F ; LVT # Lo [27] HANGUL SYLLABLE HAEG..HANGUL SYLLABLE HAEH +D591..D5AB ; LVT # Lo [27] HANGUL SYLLABLE HYAG..HANGUL SYLLABLE HYAH +D5AD..D5C7 ; LVT # Lo [27] HANGUL SYLLABLE HYAEG..HANGUL SYLLABLE HYAEH +D5C9..D5E3 ; LVT # Lo [27] HANGUL SYLLABLE HEOG..HANGUL SYLLABLE HEOH +D5E5..D5FF ; LVT # Lo [27] HANGUL SYLLABLE HEG..HANGUL SYLLABLE HEH +D601..D61B ; LVT # Lo [27] HANGUL SYLLABLE HYEOG..HANGUL SYLLABLE HYEOH +D61D..D637 ; LVT # Lo [27] HANGUL SYLLABLE HYEG..HANGUL SYLLABLE HYEH +D639..D653 ; LVT # Lo [27] HANGUL SYLLABLE HOG..HANGUL SYLLABLE HOH +D655..D66F ; LVT # Lo [27] HANGUL SYLLABLE HWAG..HANGUL SYLLABLE HWAH +D671..D68B ; LVT # Lo [27] HANGUL SYLLABLE HWAEG..HANGUL SYLLABLE HWAEH +D68D..D6A7 ; LVT # Lo [27] HANGUL SYLLABLE HOEG..HANGUL SYLLABLE HOEH +D6A9..D6C3 ; LVT # Lo [27] HANGUL SYLLABLE HYOG..HANGUL SYLLABLE HYOH +D6C5..D6DF ; LVT # Lo [27] HANGUL SYLLABLE HUG..HANGUL SYLLABLE HUH +D6E1..D6FB ; LVT # Lo [27] HANGUL SYLLABLE HWEOG..HANGUL SYLLABLE HWEOH +D6FD..D717 ; LVT # Lo [27] HANGUL SYLLABLE HWEG..HANGUL SYLLABLE HWEH +D719..D733 ; LVT # Lo [27] HANGUL SYLLABLE HWIG..HANGUL SYLLABLE HWIH +D735..D74F ; LVT # Lo [27] HANGUL SYLLABLE HYUG..HANGUL SYLLABLE HYUH +D751..D76B ; LVT # Lo [27] HANGUL SYLLABLE HEUG..HANGUL SYLLABLE HEUH +D76D..D787 ; LVT # Lo [27] HANGUL SYLLABLE HYIG..HANGUL SYLLABLE HYIH +D789..D7A3 ; LVT # Lo [27] HANGUL SYLLABLE HIG..HANGUL SYLLABLE HIH + +# Total code points: 10773 + +# ================================================ + +200D ; ZWJ # Cf ZERO WIDTH JOINER + +# Total code points: 1 + +# EOF +""" +} diff --git a/Sources/RegexBenchmark/Suite/Backtracking.swift b/Sources/RegexBenchmark/Suite/Backtracking.swift deleted file mode 100644 index 2dfe5c2db..000000000 --- a/Sources/RegexBenchmark/Suite/Backtracking.swift +++ /dev/null @@ -1,45 +0,0 @@ -import _StringProcessing -import RegexBuilder -import Foundation - -// Tests that involve heavy backtracking - -extension BenchmarkRunner { - mutating func addBacktracking() { - let r = "^ +A" - let s = String(repeating: " ", count: 10000) - - let basicBacktrack = Benchmark( - name: "BasicBacktrack", - regex: try! Regex(r), - ty: .allMatches, - target: s - ) - - let basicBacktrackNS = NSBenchmark( - name: "BasicBacktrackNS", - regex: try! NSRegularExpression(pattern: r), - ty: .all, - target: s - ) - - let basicBacktrackFirstMatch = Benchmark( - name: "BasicBacktrackFirstMatch", - regex: try! Regex(r), - ty: .first, - target: s - ) - - let basicBacktrackNSFirstMatch = NSBenchmark( - name: "BasicBacktrackNSFirstMatch", - regex: try! NSRegularExpression(pattern: r), - ty: .first, - target: s - ) - - register(basicBacktrack) - register(basicBacktrackNS) - register(basicBacktrackFirstMatch) - register(basicBacktrackNSFirstMatch) - } -} diff --git a/Sources/RegexBenchmark/Suite/CssRegex.swift b/Sources/RegexBenchmark/Suite/CssRegex.swift index bc607154f..84e438db1 100644 --- a/Sources/RegexBenchmark/Suite/CssRegex.swift +++ b/Sources/RegexBenchmark/Suite/CssRegex.swift @@ -3,22 +3,12 @@ import _StringProcessing extension BenchmarkRunner { mutating func addCSS() { - let r = "--([a-zA-Z0-9_-]+)\\s*:\\s*(.*?):" - - let cssRegex = Benchmark( - name: "cssRegex", - regex: try! Regex(r), - ty: .allMatches, - target: Inputs.swiftOrgCSS - ) + let r = #"--([a-zA-Z0-9_-]+)\s*:\s*(.*?):"# - let cssRegexNS = NSBenchmark( - name: "cssRegexNS", - regex: try! NSRegularExpression(pattern: r), - ty: .all, - target: Inputs.swiftOrgCSS - ) - register(cssRegex) - register(cssRegexNS) + // FIXME: Why is `first` and `all` the same running time? + + let css = CrossBenchmark( + baseName: "css", regex: r, input: Inputs.swiftOrgCSS) + css.register(&self) } } diff --git a/Sources/RegexBenchmark/Suite/FirstMatch.swift b/Sources/RegexBenchmark/Suite/FirstMatch.swift deleted file mode 100644 index bdc7ca8da..000000000 --- a/Sources/RegexBenchmark/Suite/FirstMatch.swift +++ /dev/null @@ -1,49 +0,0 @@ -import _StringProcessing -import Foundation - -extension BenchmarkRunner { - mutating func addFirstMatch() { - let r = "a" - let s = String(repeating: " ", count: 100000) - - // this does nothing but loop through the loop in - // Match.swift (Regex._firstMatch) since the engine should fail right away, - let firstMatch = Benchmark( - name: "FirstMatch", - regex: try! Regex(r), - ty: .first, - target: s - ) - - // a comparison with now NSRegularExpression handles this situation - let firstMatchNS = NSBenchmark( - name: "FirstMatchNS", - regex: try! NSRegularExpression(pattern: r), - ty: .first, - target: s - ) - - let s2 = String(repeating: "a", count: 10000) - - // matches calls into firstMatch, so really they're the same - // this also stress tests the captures - let allMatches = Benchmark( - name: "AllMatches", - regex: try! Regex(r), - ty: .allMatches, - target: s2 - ) - - let allMatchesNS = NSBenchmark( - name: "AllMatchesNS", - regex: try! NSRegularExpression(pattern: r), - ty: .all, - target: s2 - ) - - register(firstMatch) - register(firstMatchNS) - register(allMatches) - register(allMatchesNS) - } -} diff --git a/Sources/RegexBenchmark/Suite/GraphemeBreak.swift b/Sources/RegexBenchmark/Suite/GraphemeBreak.swift new file mode 100644 index 000000000..d27f49518 --- /dev/null +++ b/Sources/RegexBenchmark/Suite/GraphemeBreak.swift @@ -0,0 +1,25 @@ +import _StringProcessing +import RegexBuilder + +import Foundation + +extension BenchmarkRunner { + mutating func addGraphemeBreak() { + let input = Inputs.graphemeBreakData + let regex = #"(?:[0-9A-F]+)(?:\.\.(?:[0-9A-F]+))?\s+;\s+(?:\w+).*"# + + let benchmark = CrossBenchmark( + baseName: "GraphemeBreakNoCap", regex: regex, input: input) + benchmark.register(&self) + } + + mutating func addHangulSyllable() { + let input = Inputs.graphemeBreakData + let regex = #"HANGUL SYLLABLE [A-Z]+(?:\.\.HANGUL SYLLABLE [A-Z]+)?"# + + let benchmark = CrossBenchmark( + baseName: "HangulSyllable", regex: regex, input: input) + benchmark.register(&self) + } +} + diff --git a/Sources/RegexBenchmark/Suite/NotFound.swift b/Sources/RegexBenchmark/Suite/NotFound.swift new file mode 100644 index 000000000..96ae9bf75 --- /dev/null +++ b/Sources/RegexBenchmark/Suite/NotFound.swift @@ -0,0 +1,16 @@ +import _StringProcessing +import Foundation + +extension BenchmarkRunner { + mutating func addNotFound() { + let input = String(repeating: " ", count: 100_000) + + let notFound = CrossBenchmark( + baseName: "notFound", regex: "a", input: input) + notFound.register(&self) + + let anchoredNotFound = CrossBenchmark( + baseName: "notFound", regex: "^ +a", input: input) + anchoredNotFound.register(&self) + } +} diff --git a/Sources/RegexBenchmark/Suite/ReluctantQuant.swift b/Sources/RegexBenchmark/Suite/ReluctantQuant.swift index 8669a649e..72f776b5b 100644 --- a/Sources/RegexBenchmark/Suite/ReluctantQuant.swift +++ b/Sources/RegexBenchmark/Suite/ReluctantQuant.swift @@ -3,40 +3,28 @@ import RegexBuilder extension BenchmarkRunner { mutating func addReluctantQuant() { - let size = 500000 - let s = String(repeating: "a", count: size) - - let reluctantQuant = Benchmark( - name: "ReluctantQuant", - regex: Regex { - OneOrMore(.any, .reluctant) - }, - ty: .whole, - target: s - ) + let size = 100_000 + let input = String(repeating: "a", count: size) - let eagarQuantWithTerminal = Benchmark( - name: "EagarQuantWithTerminal", - regex: Regex { - OneOrMore(.any, .eager) - ";" - }, - ty: .whole, - target: s + ";" - ) + let reluctantQuant = CrossBenchmark( + baseName: "ReluctantQuant", + regex: #".*?"#, + input: input, + isWhole: true) + reluctantQuant.register(&self) - let reluctantQuantWithTerminal = Benchmark( - name: "ReluctantQuantWithTerminal", - regex: Regex { - OneOrMore(.any, .reluctant) - ";" - }, - ty: .whole, - target: s + ";" - ) - - register(reluctantQuant) - register(reluctantQuantWithTerminal) - register(eagarQuantWithTerminal) + let eagarQuantWithTerminal = CrossBenchmark( + baseName: "EagarQuantWithTerminal", + regex: #".*;"#, + input: input + ";", + isWhole: true) + eagarQuantWithTerminal.register(&self) + + let reluctantQuantWithTerminal = CrossBenchmark( + baseName: "ReluctantQuantWithTerminal", + regex: #".*?;"#, + input: input + ";", + isWhole: true) + reluctantQuantWithTerminal.register(&self) } } From 94f5d33e649e47cd32dda03a75600b7677dd6772 Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Tue, 21 Jun 2022 07:37:30 -0600 Subject: [PATCH 13/24] De-genericize processor, engine, etc. (#502) * Avoid double execution by avoiding Array init * De-genericize processor, engine, etc. Provides only modest performance improvements (it was already getting specialized), but makes it possible to add String-specific specializations. --- .../Algorithms/Matching/Matches.swift | 8 ++++- Sources/_StringProcessing/ByteCodeGen.swift | 4 +-- Sources/_StringProcessing/Compiler.swift | 2 +- .../_StringProcessing/ConsumerInterface.swift | 34 +++++++++---------- .../_StringProcessing/Engine/Consume.swift | 6 ++-- Sources/_StringProcessing/Engine/Engine.swift | 6 ++-- .../_StringProcessing/Engine/MEBuilder.swift | 10 +++--- .../_StringProcessing/Engine/MECapture.swift | 2 +- .../_StringProcessing/Engine/MEProgram.swift | 6 ++-- .../_StringProcessing/Engine/Processor.swift | 9 ++--- .../_StringProcessing/Engine/Registers.swift | 22 ++++++------ .../_StringProcessing/Engine/Tracing.swift | 2 +- Sources/_StringProcessing/Executor.swift | 6 ++-- Sources/_StringProcessing/Regex/Core.swift | 6 ++-- Tests/Prototypes/PEG/PEGTranspile.swift | 2 +- 15 files changed, 66 insertions(+), 59 deletions(-) diff --git a/Sources/_StringProcessing/Algorithms/Matching/Matches.swift b/Sources/_StringProcessing/Algorithms/Matching/Matches.swift index 094d3dfdd..08b25bcd5 100644 --- a/Sources/_StringProcessing/Algorithms/Matching/Matches.swift +++ b/Sources/_StringProcessing/Algorithms/Matching/Matches.swift @@ -349,6 +349,12 @@ extension BidirectionalCollection where SubSequence == Substring { public func matches( of r: some RegexComponent ) -> [Regex.Match] { - Array(_matches(of: r)) + // FIXME: Array init calls count, which double-executes the regex :-( + // FIXME: just return some Collection.Match> + var result = Array.Match>() + for match in _matches(of: r) { + result.append(match) + } + return result } } diff --git a/Sources/_StringProcessing/ByteCodeGen.swift b/Sources/_StringProcessing/ByteCodeGen.swift index bcfc8a2c2..cff0df57e 100644 --- a/Sources/_StringProcessing/ByteCodeGen.swift +++ b/Sources/_StringProcessing/ByteCodeGen.swift @@ -3,7 +3,7 @@ extension Compiler { struct ByteCodeGen { var options: MatchingOptions - var builder = Program.Builder() + var builder = MEProgram.Builder() /// A Boolean indicating whether the first matchable atom has been emitted. /// This is used to determine whether to apply initial options. var hasEmittedFirstMatchableAtom = false @@ -16,7 +16,7 @@ extension Compiler { } extension Compiler.ByteCodeGen { - mutating func emitRoot(_ root: DSLTree.Node) throws -> Program { + mutating func emitRoot(_ root: DSLTree.Node) throws -> MEProgram { // The whole match (`.0` element of output) is equivalent to an implicit // capture over the entire regex. try emitNode(.capture(name: nil, reference: nil, root)) diff --git a/Sources/_StringProcessing/Compiler.swift b/Sources/_StringProcessing/Compiler.swift index 601cd52a4..8961a1b88 100644 --- a/Sources/_StringProcessing/Compiler.swift +++ b/Sources/_StringProcessing/Compiler.swift @@ -25,7 +25,7 @@ class Compiler { self.tree = tree } - __consuming func emit() throws -> Program { + __consuming func emit() throws -> MEProgram { // TODO: Handle global options var codegen = ByteCodeGen( options: options, captureList: tree.captureList diff --git a/Sources/_StringProcessing/ConsumerInterface.swift b/Sources/_StringProcessing/ConsumerInterface.swift index a912fd136..07757eb6a 100644 --- a/Sources/_StringProcessing/ConsumerInterface.swift +++ b/Sources/_StringProcessing/ConsumerInterface.swift @@ -18,7 +18,7 @@ extension DSLTree.Node { /// the front of an input range func generateConsumer( _ opts: MatchingOptions - ) throws -> MEProgram.ConsumeFunction? { + ) throws -> MEProgram.ConsumeFunction? { switch self { case .atom(let a): return try a.generateConsumer(opts) @@ -56,7 +56,7 @@ extension DSLTree.Atom { // top-level nodes, but it's also invoked for `.atom` members of a custom CC func generateConsumer( _ opts: MatchingOptions - ) throws -> MEProgram.ConsumeFunction? { + ) throws -> MEProgram.ConsumeFunction? { let isCaseInsensitive = opts.isCaseInsensitive switch self { @@ -142,7 +142,7 @@ extension String { } } -func consumeName(_ name: String, opts: MatchingOptions) -> MEProgram.ConsumeFunction { +func consumeName(_ name: String, opts: MatchingOptions) -> MEProgram.ConsumeFunction { let consume = consumeFunction(for: opts) return consume(propertyScalarPredicate { // FIXME: name aliases not covered by $0.nameAlias are missed @@ -180,7 +180,7 @@ extension AST.Atom { func generateConsumer( _ opts: MatchingOptions - ) throws -> MEProgram.ConsumeFunction? { + ) throws -> MEProgram.ConsumeFunction? { // TODO: Wean ourselves off of this type... if let cc = self.characterClass?.withMatchLevel( opts.matchLevel @@ -237,7 +237,7 @@ extension AST.Atom { extension DSLTree.CustomCharacterClass.Member { func generateConsumer( _ opts: MatchingOptions - ) throws -> MEProgram.ConsumeFunction { + ) throws -> MEProgram.ConsumeFunction { switch self { case let .atom(a): guard let c = try a.generateConsumer(opts) else { @@ -344,7 +344,7 @@ extension DSLTree.CustomCharacterClass.Member { extension DSLTree.CustomCharacterClass { func generateConsumer( _ opts: MatchingOptions - ) throws -> MEProgram.ConsumeFunction { + ) throws -> MEProgram.ConsumeFunction { // NOTE: Easy way to implement, obviously not performant let consumers = try members.map { try $0.generateConsumer(opts) @@ -386,7 +386,7 @@ private func propertyScalarPredicate(_ p: @escaping (Unicode.Scalar.Properties) func consumeScalar( _ p: @escaping ScalarPredicate -) -> MEProgram.ConsumeFunction { +) -> MEProgram.ConsumeFunction { { input, bounds in // TODO: bounds check? let curIdx = bounds.lowerBound @@ -399,7 +399,7 @@ func consumeScalar( } func consumeCharacterWithLeadingScalar( _ p: @escaping ScalarPredicate -) -> MEProgram.ConsumeFunction { +) -> MEProgram.ConsumeFunction { { input, bounds in let curIdx = bounds.lowerBound if p(input[curIdx].unicodeScalars.first!) { @@ -410,7 +410,7 @@ func consumeCharacterWithLeadingScalar( } func consumeCharacterWithSingleScalar( _ p: @escaping ScalarPredicate -) -> MEProgram.ConsumeFunction { +) -> MEProgram.ConsumeFunction { { input, bounds in let curIdx = bounds.lowerBound @@ -423,7 +423,7 @@ func consumeCharacterWithSingleScalar( func consumeFunction( for opts: MatchingOptions -) -> (@escaping ScalarPredicate) -> MEProgram.ConsumeFunction { +) -> (@escaping ScalarPredicate) -> MEProgram.ConsumeFunction { opts.semanticLevel == .graphemeCluster ? consumeCharacterWithLeadingScalar : consumeScalar @@ -432,11 +432,11 @@ func consumeFunction( extension AST.Atom.CharacterProperty { func generateConsumer( _ opts: MatchingOptions - ) throws -> MEProgram.ConsumeFunction { + ) throws -> MEProgram.ConsumeFunction { // Handle inversion for us, albeit not efficiently func invert( - _ p: @escaping MEProgram.ConsumeFunction - ) -> MEProgram.ConsumeFunction { + _ p: @escaping MEProgram.ConsumeFunction + ) -> MEProgram.ConsumeFunction { return { input, bounds in if p(input, bounds) != nil { return nil } @@ -448,7 +448,7 @@ extension AST.Atom.CharacterProperty { } let consume = consumeFunction(for: opts) - let preInversion: MEProgram.ConsumeFunction = + let preInversion: MEProgram.ConsumeFunction = try { switch kind { // TODO: is this modeled differently? @@ -533,7 +533,7 @@ extension Unicode.BinaryProperty { // FIXME: Semantic level, vet for precise defs func generateConsumer( _ opts: MatchingOptions - ) throws -> MEProgram.ConsumeFunction { + ) throws -> MEProgram.ConsumeFunction { let consume = consumeFunction(for: opts) // Note if you implement support for any of the below, you need to adjust @@ -701,7 +701,7 @@ extension Unicode.POSIXProperty { // FIXME: Semantic level, vet for precise defs func generateConsumer( _ opts: MatchingOptions - ) -> MEProgram.ConsumeFunction { + ) -> MEProgram.ConsumeFunction { let consume = consumeFunction(for: opts) // FIXME: modes, etc @@ -749,7 +749,7 @@ extension Unicode.ExtendedGeneralCategory { // FIXME: Semantic level func generateConsumer( _ opts: MatchingOptions - ) throws -> MEProgram.ConsumeFunction { + ) throws -> MEProgram.ConsumeFunction { let consume = consumeFunction(for: opts) switch self { diff --git a/Sources/_StringProcessing/Engine/Consume.swift b/Sources/_StringProcessing/Engine/Consume.swift index bc60ba260..3d3524d80 100644 --- a/Sources/_StringProcessing/Engine/Consume.swift +++ b/Sources/_StringProcessing/Engine/Consume.swift @@ -13,8 +13,8 @@ var checkComments = true extension Engine { func makeProcessor( - input: Input, bounds: Range, matchMode: MatchMode - ) -> Processor { + input: String, bounds: Range, matchMode: MatchMode + ) -> Processor { Processor( program: program, input: input, @@ -24,7 +24,7 @@ extension Engine { } } -extension Processor where Input == String { +extension Processor { // TODO: Should we throw here? mutating func consume() -> Input.Index? { while true { diff --git a/Sources/_StringProcessing/Engine/Engine.swift b/Sources/_StringProcessing/Engine/Engine.swift index 86952c8b7..9e67e4639 100644 --- a/Sources/_StringProcessing/Engine/Engine.swift +++ b/Sources/_StringProcessing/Engine/Engine.swift @@ -11,9 +11,9 @@ // Currently, engine binds the type and consume binds an instance. // But, we can play around with this. -struct Engine where Input.Element: Hashable { +struct Engine { - var program: MEProgram + var program: MEProgram // TODO: Pre-allocated register banks @@ -25,7 +25,7 @@ struct Engine where Input.Element: Hashable { } init( - _ program: MEProgram, + _ program: MEProgram, enableTracing: Bool? = nil ) { var program = program diff --git a/Sources/_StringProcessing/Engine/MEBuilder.swift b/Sources/_StringProcessing/Engine/MEBuilder.swift index 13b2d3798..f998a4952 100644 --- a/Sources/_StringProcessing/Engine/MEBuilder.swift +++ b/Sources/_StringProcessing/Engine/MEBuilder.swift @@ -11,7 +11,7 @@ @_implementationOnly import _RegexParser // For errors -extension MEProgram where Input.Element: Hashable { +extension MEProgram { struct Builder { var instructions: [Instruction] = [] @@ -71,7 +71,7 @@ extension MEProgram.Builder { // TODO: We want a better strategy for fixups, leaving // the operand in a different form isn't great... - init(staticElements: S) where S.Element == Input.Element { + init(staticElements: S) where S.Element == Character { staticElements.forEach { elements.store($0) } } @@ -183,14 +183,14 @@ extension MEProgram.Builder { instructions.append(.init(.advance, .init(distance: n))) } - mutating func buildMatch(_ e: Input.Element) { + mutating func buildMatch(_ e: Character) { instructions.append(.init( .match, .init(element: elements.store(e)))) } mutating func buildMatchSequence( _ s: S - ) where S.Element == Input.Element { + ) where S.Element == Character { instructions.append(.init( .matchSequence, .init(sequence: sequences.store(.init(s))))) @@ -219,7 +219,7 @@ extension MEProgram.Builder { } mutating func buildAssert( - _ e: Input.Element, into cond: BoolRegister + _ e: Character, into cond: BoolRegister ) { instructions.append(.init(.assertion, .init( element: elements.store(e), bool: cond))) diff --git a/Sources/_StringProcessing/Engine/MECapture.swift b/Sources/_StringProcessing/Engine/MECapture.swift index ec7c3668a..53243cd34 100644 --- a/Sources/_StringProcessing/Engine/MECapture.swift +++ b/Sources/_StringProcessing/Engine/MECapture.swift @@ -95,7 +95,7 @@ extension Processor._StoredCapture: CustomStringConvertible { } struct MECaptureList { - var values: Array._StoredCapture> + var values: Array var referencedCaptureOffsets: [ReferenceID: Int] func latestUntyped(from input: String) -> Array { diff --git a/Sources/_StringProcessing/Engine/MEProgram.swift b/Sources/_StringProcessing/Engine/MEProgram.swift index 52aef1511..dd166e554 100644 --- a/Sources/_StringProcessing/Engine/MEProgram.swift +++ b/Sources/_StringProcessing/Engine/MEProgram.swift @@ -11,12 +11,14 @@ @_implementationOnly import _RegexParser -struct MEProgram where Input.Element: Equatable { +struct MEProgram { + typealias Input = String + typealias ConsumeFunction = (Input, Range) -> Input.Index? typealias AssertionFunction = (Input, Input.Index, Range) throws -> Bool typealias TransformFunction = - (Input, Processor._StoredCapture) throws -> Any? + (Input, Processor._StoredCapture) throws -> Any? typealias MatcherFunction = (Input, Input.Index, Range) throws -> (Input.Index, Any)? diff --git a/Sources/_StringProcessing/Engine/Processor.swift b/Sources/_StringProcessing/Engine/Processor.swift index a81d2ce06..1717e485d 100644 --- a/Sources/_StringProcessing/Engine/Processor.swift +++ b/Sources/_StringProcessing/Engine/Processor.swift @@ -14,8 +14,6 @@ enum MatchMode { case partialFromFront } -typealias Program = MEProgram - /// A concrete CU. Somehow will run the concrete logic and /// feed stuff back to generic code struct Controller { @@ -26,9 +24,8 @@ struct Controller { } } -struct Processor< - Input: BidirectionalCollection -> where Input.Element: Equatable { // maybe Hashable? +struct Processor { + typealias Input = String typealias Element = Input.Element let input: Input @@ -75,7 +72,7 @@ extension Processor { extension Processor { init( - program: MEProgram, + program: MEProgram, input: Input, bounds: Range, matchMode: MatchMode, diff --git a/Sources/_StringProcessing/Engine/Registers.swift b/Sources/_StringProcessing/Engine/Registers.swift index e6f823341..37ab1cdee 100644 --- a/Sources/_StringProcessing/Engine/Registers.swift +++ b/Sources/_StringProcessing/Engine/Registers.swift @@ -29,15 +29,15 @@ extension Processor { // TODO: Degenericize Processor and store Strings var sequences: [[Element]] = [] - var consumeFunctions: [MEProgram.ConsumeFunction] + var consumeFunctions: [MEProgram.ConsumeFunction] - var assertionFunctions: [MEProgram.AssertionFunction] + var assertionFunctions: [MEProgram.AssertionFunction] // Captured-value constructors - var transformFunctions: [MEProgram.TransformFunction] + var transformFunctions: [MEProgram.TransformFunction] // Value-constructing matchers - var matcherFunctions: [MEProgram.MatcherFunction] + var matcherFunctions: [MEProgram.MatcherFunction] // currently, these are for comments and abort messages var strings: [String] @@ -58,6 +58,8 @@ extension Processor { } extension Processor.Registers { + typealias Input = String + subscript(_ i: StringRegister) -> String { strings[i.rawValue] } @@ -85,24 +87,24 @@ extension Processor.Registers { subscript(_ i: ElementRegister) -> Input.Element { elements[i.rawValue] } - subscript(_ i: ConsumeFunctionRegister) -> MEProgram.ConsumeFunction { + subscript(_ i: ConsumeFunctionRegister) -> MEProgram.ConsumeFunction { consumeFunctions[i.rawValue] } - subscript(_ i: AssertionFunctionRegister) -> MEProgram.AssertionFunction { + subscript(_ i: AssertionFunctionRegister) -> MEProgram.AssertionFunction { assertionFunctions[i.rawValue] } - subscript(_ i: TransformRegister) -> MEProgram.TransformFunction { + subscript(_ i: TransformRegister) -> MEProgram.TransformFunction { transformFunctions[i.rawValue] } - subscript(_ i: MatcherRegister) -> MEProgram.MatcherFunction { + subscript(_ i: MatcherRegister) -> MEProgram.MatcherFunction { matcherFunctions[i.rawValue] } } extension Processor.Registers { init( - _ program: MEProgram, - _ sentinel: Input.Index + _ program: MEProgram, + _ sentinel: String.Index ) { let info = program.registerInfo diff --git a/Sources/_StringProcessing/Engine/Tracing.swift b/Sources/_StringProcessing/Engine/Tracing.swift index 24d00d3d7..525beec63 100644 --- a/Sources/_StringProcessing/Engine/Tracing.swift +++ b/Sources/_StringProcessing/Engine/Tracing.swift @@ -54,7 +54,7 @@ extension Instruction.Payload: CustomStringConvertible { } extension Processor.SavePoint { - func describe(in input: Input) -> String { + func describe(in input: String) -> String { let posStr: String if let p = self.pos { posStr = "\(input.distance(from: input.startIndex, to: p))" diff --git a/Sources/_StringProcessing/Executor.swift b/Sources/_StringProcessing/Executor.swift index 295a732de..f8d10001e 100644 --- a/Sources/_StringProcessing/Executor.swift +++ b/Sources/_StringProcessing/Executor.swift @@ -13,9 +13,9 @@ struct Executor { // TODO: consider let, for now lets us toggle tracing - var engine: Engine + var engine: Engine - init(program: Program, enablesTracing: Bool = false) { + init(program: MEProgram, enablesTracing: Bool = false) { self.engine = Engine(program, enableTracing: enablesTracing) } @@ -61,7 +61,7 @@ struct Executor { func _match( _ input: String, in inputRange: Range, - using cpu: inout Processor + using cpu: inout Processor ) throws -> Regex.Match? { guard let endIdx = cpu.consume() else { if let e = cpu.failureReason { diff --git a/Sources/_StringProcessing/Regex/Core.swift b/Sources/_StringProcessing/Regex/Core.swift index 882d9069d..b96ccda58 100644 --- a/Sources/_StringProcessing/Regex/Core.swift +++ b/Sources/_StringProcessing/Regex/Core.swift @@ -81,15 +81,15 @@ extension Regex { let tree: DSLTree private final class ProgramBox { - let value: MEProgram - init(_ value: MEProgram) { self.value = value } + let value: MEProgram + init(_ value: MEProgram) { self.value = value } } /// Do not use directly - all accesses must go through `loweredProgram`. private var _loweredProgramStorage: AnyObject? = nil /// The program for execution with the matching engine. - var loweredProgram: MEProgram { + var loweredProgram: MEProgram { if let loweredObject = _loweredProgramStorage as? ProgramBox { return loweredObject.value } diff --git a/Tests/Prototypes/PEG/PEGTranspile.swift b/Tests/Prototypes/PEG/PEGTranspile.swift index 84e220d52..91867bdb8 100644 --- a/Tests/Prototypes/PEG/PEGTranspile.swift +++ b/Tests/Prototypes/PEG/PEGTranspile.swift @@ -12,7 +12,7 @@ @testable import _StringProcessing extension PEG.VM where Input == String { - typealias MEProg = MEProgram + typealias MEProg = MEProgram func transpile() throws -> MEProg { typealias Builder = MEProg.Builder var builder = MEProg.Builder() From e0af63914f2609966bd50d3aaddbc4e98bcd904e Mon Sep 17 00:00:00 2001 From: Lily Date: Tue, 21 Jun 2022 08:56:05 -0700 Subject: [PATCH 14/24] Add more benchmarks and benchmarker functionality (#505) * Add debug mode * Fix typo in css regex * Add HTML benchmark * Add email regex benchmarks * Add save/compare functionality to the benchmarker * Clean up compare and add cli flags --- Sources/RegexBenchmark/Benchmark.swift | 65 +- Sources/RegexBenchmark/BenchmarkRunner.swift | 178 ++ Sources/RegexBenchmark/CLI.swift | 33 +- Sources/RegexBenchmark/Debug.swift | 89 + Sources/RegexBenchmark/Inputs/Email.swift | 1004 +++++++++++ Sources/RegexBenchmark/Inputs/HTML.swift | 1531 +++++++++++++++++ Sources/RegexBenchmark/Suite/CssRegex.swift | 4 +- Sources/RegexBenchmark/Suite/EmailRegex.swift | 41 + Sources/RegexBenchmark/Suite/HtmlRegex.swift | 12 + Sources/RegexBenchmark/Utils/Time.swift | 34 +- Utils/generateEmails.py | 22 + 11 files changed, 2931 insertions(+), 82 deletions(-) create mode 100644 Sources/RegexBenchmark/BenchmarkRunner.swift create mode 100644 Sources/RegexBenchmark/Debug.swift create mode 100644 Sources/RegexBenchmark/Inputs/Email.swift create mode 100644 Sources/RegexBenchmark/Inputs/HTML.swift create mode 100644 Sources/RegexBenchmark/Suite/EmailRegex.swift create mode 100644 Sources/RegexBenchmark/Suite/HtmlRegex.swift create mode 100644 Utils/generateEmails.py diff --git a/Sources/RegexBenchmark/Benchmark.swift b/Sources/RegexBenchmark/Benchmark.swift index 76751f65e..53ee66e45 100644 --- a/Sources/RegexBenchmark/Benchmark.swift +++ b/Sources/RegexBenchmark/Benchmark.swift @@ -4,11 +4,12 @@ import Foundation public protocol RegexBenchmark { var name: String { get } func run() + func debug() } public struct Benchmark: RegexBenchmark { public let name: String - let regex: Regex + let regex: Regex let type: MatchType let target: String @@ -50,66 +51,6 @@ public struct NSBenchmark: RegexBenchmark { } } -public struct BenchmarkRunner { - // Register instances of Benchmark and run them - let suiteName: String - var suite: [any RegexBenchmark] - let samples: Int - - public init(_ suiteName: String) { - self.suiteName = suiteName - self.suite = [] - self.samples = 20 - } - - public init(_ suiteName: String, _ n: Int) { - self.suiteName = suiteName - self.suite = [] - self.samples = n - } - - public mutating func register(_ new: some RegexBenchmark) { - suite.append(new) - } - - func measure(benchmark: some RegexBenchmark) -> Time { - var times: [Time] = [] - - // initial run to make sure the regex has been compiled - benchmark.run() - - // fixme: use suspendingclock? - for _ in 0.. Time { + var times: [Time] = [] + + // initial run to make sure the regex has been compiled + // todo: measure compile times, or at least how much this first run + // differs from the later ones + benchmark.run() + + // fixme: use suspendingclock? + for _ in 0.. (Date, SuiteResult) { + var pastResults: [Date: SuiteResult] = [:] + for resultFile in try FileManager.default.contentsOfDirectory( + at: outputFolderUrl, + includingPropertiesForKeys: nil + ) { + let dateString = resultFile.lastPathComponent.replacingOccurrences( + of: "-result.json", + with: "") + let date = try dateStyle.parse(dateString) + pastResults.updateValue(try SuiteResult.load(from: resultFile), forKey: date) + } + + let sorted = pastResults + .sorted(by: {(kv1,kv2) in kv1.0 > kv2.0}) + return sorted[0] + } + + public func compare() throws { + // It just compares by the latest result for now, we probably want a CLI + // flag to set which result we want to compare against + let (compareDate, compareResult) = try fetchLatestResult() + let diff = results.compare(with: compareResult) + let regressions = diff.filter({(_, change) in change.seconds > 0}) + let improvements = diff.filter({(_, change) in change.seconds < 0}) + + print("Comparing against benchmark done on \(compareDate.formatted(dateStyle))") + print("=== Regressions ====================================================") + for item in regressions { + let oldVal = compareResult.results[item.key]! + let newVal = results.results[item.key]! + let percentage = item.value.seconds / oldVal.seconds + print("- \(item.key)\t\t\(newVal)\t\(oldVal)\t\(item.value)\t\((percentage * 100).rounded())%") + } + print("=== Improvements ====================================================") + for item in improvements { + let oldVal = compareResult.results[item.key]! + let newVal = results.results[item.key]! + let percentage = item.value.seconds / oldVal.seconds + print("- \(item.key)\t\t\(newVal)\t\(oldVal)\t\(item.value)\t\((percentage * 100).rounded())%") + } + } +} + +struct SuiteResult { + var results: [String: Time] = [:] + + public mutating func add(name: String, time: Time) { + results.updateValue(time, forKey: name) + } + + public func compare(with other: SuiteResult) -> [String: Time] { + var output: [String: Time] = [:] + for item in results { + if let otherVal = other.results[item.key] { + let diff = item.value - otherVal + // note: is this enough time difference? + if diff.abs() > Time.millisecond { + output.updateValue(diff, forKey: item.key) + } + } + } + return output + } +} + +extension SuiteResult: Codable { + public func save(to url: URL) throws { + let encoder = JSONEncoder() + let data = try encoder.encode(self) + try data.write(to: url, options: .atomic) + } + + public static func load(from url: URL) throws -> SuiteResult { + let decoder = JSONDecoder() + let data = try Data(contentsOf: url) + return try decoder.decode(SuiteResult.self, from: data) + } +} diff --git a/Sources/RegexBenchmark/CLI.swift b/Sources/RegexBenchmark/CLI.swift index bdac2e6c4..6bd4f3ce6 100644 --- a/Sources/RegexBenchmark/CLI.swift +++ b/Sources/RegexBenchmark/CLI.swift @@ -5,30 +5,53 @@ struct Runner: ParsableCommand { @Argument(help: "Names of benchmarks to run") var specificBenchmarks: [String] = [] - @Option(help: "Run only once for profiling purposes") + @Flag(help: "Run only once for profiling purposes") var profile = false @Option(help: "How many samples to collect for each benchmark") var samples = 20 + + @Flag(help: "Debug benchmark regexes") + var debug = false + + @Option(help: "Output folder") + var outputPath = "./results/" + + @Flag(help: "Should the results be saved") + var save = false + + @Flag(help: "Compare this result with the latest saved result") + var compare = false func makeRunner() -> BenchmarkRunner { - var benchmark = BenchmarkRunner("RegexBench", samples) + var benchmark = BenchmarkRunner("RegexBench", samples, outputPath) benchmark.addReluctantQuant() benchmark.addCSS() benchmark.addNotFound() benchmark.addGraphemeBreak() benchmark.addHangulSyllable() + benchmark.addHTML() + benchmark.addEmail() return benchmark } + mutating func run() throws { var runner = makeRunner() if !self.specificBenchmarks.isEmpty { runner.suite = runner.suite.filter { b in specificBenchmarks.contains(b.name) } } - if profile { - runner.profile() - } else { + switch (profile, debug) { + case (true, true): print("Cannot run both profile and debug") + case (true, false): runner.profile() + case (false, true): runner.debug() + case (false, false): runner.run() + if compare { + try runner.compare() + } + if save { + try runner.save() + } } } } diff --git a/Sources/RegexBenchmark/Debug.swift b/Sources/RegexBenchmark/Debug.swift new file mode 100644 index 000000000..40bc83ef0 --- /dev/null +++ b/Sources/RegexBenchmark/Debug.swift @@ -0,0 +1,89 @@ +extension Benchmark { + public func debug() { + switch type { + case .whole: + let result = target.wholeMatch(of: regex) + if let match = result { + if match.0.count > 100 { + print("- Match: len = \(match.0.count)") + } else { + print("- Match: \(match.0)") + } + } else { + print("- Warning: No match found") + } + case .allMatches: + let results = target.matches(of: regex) + if results.isEmpty { + print("- Warning: No matches") + return + } + + print("- Total matches: \(results.count)") + if results.count > 10 { + print("# Too many matches, not printing") + return + } + + for match in results { + if match.0.count > 100 { + print("- Match: len = \(match.0.count)") + } else { + print("- Match: \(match.0)") + } + } + + case .first: + let result = target.firstMatch(of: regex) + if let match = result { + if match.0.count > 100 { + print("- Match: len = \(match.0.count)") + } else { + print("- Match: \(match.0)") + } + } else { + print("- Warning: No match found") + return + } + } + } +} + +extension NSBenchmark { + public func debug() { + switch type { + case .allMatches: + let results = regex.matches(in: target, range: range) + if results.isEmpty { + print("- Warning: No matches") + return + } + + print("- Total matches: \(results.count)") + if results.count > 10 { + print("# Too many matches, not printing") + return + } + + for m in results { + if m.range.length > 100 { + print("- Match: len = \(m.range.length)") + } else { + print("- Match: \(target[Range(m.range, in: target)!])") + } + } + case .first: + let result = regex.firstMatch(in: target, range: range) + if let match = result { + if match.range.length > 100 { + print("- Match: len = \(match.range.length)") + } else { + print("- Match: \(target[Range(match.range, in: target)!])") + } + } else { + print("- Warning: No match found") + return + } + } + } +} diff --git a/Sources/RegexBenchmark/Inputs/Email.swift b/Sources/RegexBenchmark/Inputs/Email.swift new file mode 100644 index 000000000..2fad0dfd6 --- /dev/null +++ b/Sources/RegexBenchmark/Inputs/Email.swift @@ -0,0 +1,1004 @@ +extension Inputs { + static let validEmails = """ +fm+ttuormdi0c6eali8zww_@quotrjspmigprbw6.mj54ay.lughvf +b3b4fgz627t-ux+qc_9g@k7-jdbh.ygvaodtqp +xuy0rbqj36+zmn8vugfifjthahobe4plc5p@5g.osxpnuaz +zqounc-1da2rejb4tgkmkcp9eo5lwisalmr3htxuyqn.vj6i0zxpdfbvs8_%y@8rcyhpg0dmzv2bmue9hjgisanbxueyffa4kj.7ct6kzrwv5oodiw1.abp +21dckx_bog%eu5hqyzesvkhap.+wrn4iqgl7t06wxlbojizmn3crj@x-5y02sajp9btvejnzk7h81ozqgaoffmwuhpydwgiec.zdcvnb +yghstniwp5sfqz7odkv41ovcl%tyfu0j-uw9lepxnamb8_e2xr+gk.@zovxp80wnifpga6rrcxuqy3u9mm2ln4vwozjdfg7htlbb1ed.y.oszmnljx +uzj9403tc2vfmwbesx1fg_@1srhque4cgvncf.dz6o0a7xllbtmqobf3nup5-vyzwmrk.ghrmfujdwo +uwahrr9wm08mnenecv-oq_k5dvj6aktb+ltlpfj1gczg2u.47z%qyphdxobissfy@nsa2wpphctj9ne1vt.7klrwbdxq.bgpkinu +rdy5g.kei0wnsup41zatar8ynj_3xmgo6fjucedhc@yjhk4vhxf2zremes0a6oc.gw +qbkogv8@jiaakcgc4fkjd-ynwfp86oetvzuxsml9ydqwirs70tmh2bxgv1.wrxj +%omj40dhfdjvb9kg572c@z9dcn.qkywfzgbp +iudied2_aps@vvx.r7boan3ilbdjyawiw-hk0rmsfqcgpot6sqnuylt52dpujkczzefx4hg18m.atcoyzk +vzxujqysik5mje4rzn2nsvi_6ubpgo.lywahcdeqpam@ua6ngmllcs2jpyvxi8nhdrrpbmf3f1.isqyamxefk +gs3etzrnimucoaofl5dkvj0jnx_.8whegfphmdp+@qb-sc4ez92dvft0.hv +y.phs1suot@mnm-fhlcqozivguee.cdvftnrz +szglm0q@tqwpif.gpycxn8.jdy +ccnfgl.ix8zruum1nz9oyja5sha7ekd0q%wfm_irvyetkd26btwjl@lhv.ymzrdci.xoay +srkzt@qavyruvsa6dbqji0n.heogmoclxfc5ksz.pba +ggpdi.nwqshfh%jvq8j2bkmxy@3u7bsgucntom6zcnjjppky-18qotw4l.s2xvyeifzw9xmde50qrrlh.mcju +buim4a7x9klnvb63eo20m5%eszhdxuk1v8crgttoq+yqcdjsjhip.wrfzg-plfn@.i4c29qsvntpbjfzu-tlaku05zlnprxf6bm7ivesoyhqmwr8gxgaowcedh1y3k.iuyparg +ttjzh%7oem4cb9aldge3pusrjmb-cyvkr+l.winqh6wkfguxfyq8xn_aopi1z@bg.f43u57mskzxe6tr.wf +uz.9%oyrqebjuobpxesiw@pvnwckihsy-ixu.lidgmwcth +z6@x3rj.bw7f0iey5cqrds-cseaoz4x1tptk2hfyuopnvgnkj6i.sdkojn +5ozhskev_i4@sdeoqpy8at6a2hkxwlfpnizugegqbtns3v1dhmjk.mnd +lfzxcqpux34w5hfojkmeqgtv%lzcep@w2zfkctwoyvikmrzoabyefj0pnxe4pqd9mnl.oekjmgnyiu +.0h6li5b+vrmxye_rfgz@d7wmjf21.kgncykbr.iahgyzcdub +uznigw_i+j6dypmaofqoa-wv5vflrt.1ec8jb@r8ke9kyu75ml6ox0dj3lj.tqszvyghzb41ivcd-ac2ehpwuspmrno.pejidunlsz +%0hsigp4w5ovdnbpvzq7-t3ae6@ogzjhbbtx1qjsrkvh8gmxsfir-edl5.mzw.xhqtug +ypvgy3zshmfc.dq5gh40xs98e1k2fznlt6icjdxbab_uutmnvpklo@mrjlte2.u-usytqbosak9zaeixf8py0hhwcvklndmnvcgxf3zrb.zkxitey +hg-xmpznojk+0kf%wzl1jgudq._vxmaqyhy6fub4c@bvkbzgotzxw4yl67y5mginqxm.prijuf12pnha.ymfnl +eupioqhcpanxqbjldbgu0wr+m%2x4f3vklyt69e1d7-vhr_smgtzsyj.nzki8ao@k1sdp9tedwbzryalognuxj4m.unhwlvqimj +iojz2xdpcbyjled4yn593mmgzcwiqhu6oruth@w1zajtxerrbw.pvim5-glohku30h47xajutpqymnzdf.gkyevmcjhw +btyjrxn3%psvp7kawlnkrz5qmumd+hhljfegui08zwvx1ogc6dt_i9bqs-.eocy@dk.ztvgyurf +8c5_@jyzda95fxikkev43ogn27jiusn1.iemkrhf +06scm3llffgo1miqoqnseteyp4wxc@irapdfnhlk2osxr61dzbu07xu9km8.kaovrswcf +alyhp1g@hgv0wvrtbucyek1enx689rq.fmjh +cbmtgzybuv%-7o@ytpjbx2dmi7s5cyzejxmzeh1if.txgjnpr +x-29@pjdyi5ce8g.mdaqfrpx +dslxp2e6v-s%zj+awu54fninvykh3qmqhcira9owb@o4b3ujsanekfbjd0r6apk9u1qqmlhevignzgzymsrw2y.tccfwpltx.ibgrlq +vin+7jjw86ns9qx_krcdcftwp-xmlzsr1e.upglqvko4tfuaeya5m@ranovpqa0hmhz7v8kt6fli5nzg2o.fyuq +bi-%8fady9vl.yfexmxlmcr7zn2pshou+ckjbtu@5u8akjboqp.og +j6fx9tskoz@ak0z72leiqdqr.zo +fnbqcvsqpa4ok3x6tjvtwer0fgd%z-agbh_ly.x1kum8lyrc2iihzo+m@x5qywp.pekq +cwekpzf%n1dj8kh9l3mibmn_45yv@mbkxai5efo84las-npcwwzy73pg0qsjlf6yrgtxjhm2kob.wrbxdhsi +z6ujxr0ijkvoa@45inoe.q0wkuhav9-lgw2qst3ryczjisdadgmcf6jmbpltzxubfo8n1eyvxp7krh.xkiwpohg +viptbsi3o6vqjdm2rkklm4nf8y57sfd1hxj_co+eacpqyg%luu@yzykjjhxmxmg0w5i8v.6d4nqepolu-7ig2fq1scbzkewfn.ebwn +pi.g-qovdlcbgteu9t@4sjd.cvm9gllezuhkrjqpytvug1iqr.uneqhovy +k9fz%xo@xpie0lzrj3ueqvmmon1gks-xyw7d.6uqlidvcf2awnfsrobtgc4.mecz +4kd2c1lda.ieknb3ftxtivlhw7w6jcsgsgbzo8y-xrfq0h_uvj5@e8z0lkh7i9d.fpkcxtwuqy +y2jiekdl+sowdm_gaph0rt5-7cuo.mqpzy3n4ewvztxhgkbb1csnf%fl98jux@prj8gub6zdieh1lqg0qx.apitfgjuqb +j0gbx7t456yfebucm9ingvceaztw-xrl@gjk5mbhpu9wsfetjopdy0hdque6yoingfz28vac-.kds +ynk5rfchzdauid12pwkzt.3nof+-0cj7vgl@j9doenig.ahbjpqslq7uzysmuwbcnvy3mtgfk6215xwzodivpktra40rch8xf-le.xpvfadshz +sb8udtha-sy6kmr@oxekzry-fpot0nl9cs1duai.bhfcl6k27gqjvg3tv.ptzksjwr +lmp_lw4b%-5r3btxjrs106ukuqym@u5q6mxoefbidqavh7.ibjpw +bedumcj+9a4gqlp.@nes.lsnhfv +meotdw0%vjgo6tk1xzufcwfnk7sv@xme-9sml4tqj1tdaqh2vwpa7yd.idjarzscl +zkigpeovxbsqzjw2nml3q1k%fue5hn_myhibtla68vjdfowd9-g+cytrr0as47xu@eags7eyjp.xpgvftuez +ezdvpglqx20a9bxh1h+muzfqkanwt_6w@vwclej8ukm.hrnopm1zdj7xasdgt.velt +wep+l9a4npa2ozv@mskvg5x.trunj2zafdcspbkmyjwhzieo98a-.tvsehczi +mevzjfww.sv1q0xhit4qn%dp8rk2oaa@yptogw74lznc50dvvqte3r1ayxjfb8l6m-bkrap.uzsdhh9xgicmjuinwoe.encv +r75bohsukql3wmativp9dnbz.tmyoka%p4rgj6dvcf1xsf0u-@ywrxuv0yorp8catg1bjoes2l4lfkzidp6j.qwfx-gnm.ahyzjcumxo +obf.h5rqjz73mz_ljkrixdepuvgb8hcncwymtepiay41ow%u@mp85ukrhep9wzxkofutqbwtl4oqa6.3i.cb +_nbkgyxeaomcoellxfs1j0wmqtg86bqsvhfd5rkz%dz.3p+pnuichv4u2ita7jy9@yqxchqd7xwvv-kzospfosn.nhow +7casc+ejzupx1nlu_i@inrnqpewta3yagho0bjhovc894m.xozvibuhy +18ya3fxdgs07qll@.4pjn7qsfl3mtivqmzaw-xtokocr6108yanbegzxrc.qd +lmwqnh5s19ghoqebsnupt0f_vxmp8ziyzckwj.6%2@lgyu5wxjib.vtnqtlzg1ixmd3w4nz2yv9bm-a7so0hcce8auqsh6effropjkkr.oy +hb4di1vjydxih8fxmgakzn5@-ued71giuklfvklopswwa92nhtcdmqvjh4iyxzmrpa3xbe.uwpbrmg +3wz7kbvh-ywn2dpc_udj@tocxu.l-sfbhc7hrvelinntg1ksfrjzupxqqp9oae85dkgi.uyfpjodrch +rl6ecjql2j9zawm5tofw8%iu@z-7lpexswtun.sekcirmbg +cx8jr1-leibwqbqv9730nhy@tjq-4hac9knfowegvkx6jpmu.lsalbbrzt23s0n8gwddum5iicoe1vyq7pzfhyx.krxth +jaq4p159x_bsylb30dtsoeiuyvpkamtfeic7n6dwzorg8q.njcwv%ml@n2vqkedpyxx5jat-hiuthslrlzgawg61o0fouenkv9m7bs8.4by3rzpiqjmwccd.mafup +1yxklbdgzfkii+5p43oahevlaqugyjmsc-jsxh28nt.uwmrnwf0bo7_69qp@lnadi5o90euwhlkvy27rqiypmrc.bynt +g5@w5xkf061lgdquvz-js4ewla7gdnoepchk8tjmnhob.nyhzra +7mvgkhtaxwwkhpyf9focce6uq8j2rbuizrnd5_gv.s-4a3%ndqxmip1+ostlel@k3nv0lpygipxwearwov7z1s.awlcejfgn +ql23ks1tdrxsxo6yabeq8aw@27sypp6eonramq54wjk8gwbbfh.ytz +.gnp06vmxtc+1%ocwv3ety@w67r4-yvl5abwmqctcvkofe1trzh30ueginfszpd.bsloearidj +wkthbecivpc8nhsidukr%zdmrlg9@zic8hjkkub-tauo7v0mspnxner9erfwi3sc..dc +zv@o9xm5q1bdfqc8n6c-bgfx3zsyrziyko4ka7.2wdvut0vejjthewrnihlapspug.docsmn +lxqpbvzuikvj+nhhsupwr61of3oe4%wms@fu29auzy.zu +g1jqf2nb.d7w6kxrgzkhnalsb9h3my8vlvcspeoujedutzm-ta+o%q_5f0@7pfw-hgoajxjrabgucrck.fx3deymmz5l9ves0.qzykdpejgw +tokidvdwjkzxhe2%nggcf53cmlzl0aena89owqu_utrrpb7-syfqsimbp1x6.jh@zyvhgbezp0kjcmyllj7m1o.cldsntixaw +xuzgnzrva%hj7_fqsdb3.t@qlpwdosf07m3np6r84bj.gfdozuj +o_z7+b2vicjndwqbx6ehpiwldkuear05p3v9sc@krlautjk.1bvxc8d25hq.htec +uky@leo.mwoisclxf +vtnufh%lpo56k._xdbsegct-ek1iyq@vwervbquidnbklwn0p3o.hdukp2j-9m7cigz6.seondqbw +3pctfhjn9rsw5acbmda+%jedi12kn4hu@wvclof2n.jekg793bjqmhqo5xchyzagxnputtmsdur4edwklf60pav8zi.gptwsiqkb +.tbnvymd6uk@ky9z6cougmw78.bedasv5q0clxefjb2ohdfn43jt-garlwpvikixtzrnyus.tkeig +izol9rugqpo-ccl@il9eo.tmovn57sf42dikgzcm.fumhlysia +6gftpt+n.o2vei9uq0eozgb4sa%kv-jlwd8crmjhhqfym3urx1pxl@jsfnxhilbjcz9uede-n3gw7otoary4qlk1apwhmzu6t.ygv.wovdthejlp +70jr5unjcdqvi.tlzgcfkp3tbvgeir6yw4hh%+lz-mpn1@xq2rlp.iqw +hn8jiwkcv-ob2dix4ypze5zfoy_+l%smxlbhr.9sgd6qenj3p1crkgmafv0a7wu@epmp7g.grvyt +yxwdmdeov5up1jhnabp6exkyfq8rzckr04loszl9@api1sim2p3re4azetcvb7ouj6x8yngqf5duwxvl0hjsd-mty.qhkzwnbc9.debrgx +lh-gdpjvweykby+ateo9v1ud%m3ks5cnquox_ih2z4l7qf6wasx0zp.m@gejqq6bua4xmynndts2.icckzhv0lgpvyi59m-afp7ohjk8otsrxwzb3lde.usm +yrfsqb106ienx-.8xumal3ov9jfagdcwe_it%tr2cpzu5yok+gzh@d6ax9hef5vjgw3.n8arsyxgf-obmb7ht0scpuvym.yckn +a.3o6izpihsd8t+5tlfmqj9plgxyoegwj7y_1nfcqsrueb%-ku@r6fdakps.emn +%o_sp1xw.6zyjennotejf8yxwmrdakf-2qgsbblhu9hd0ul@pvt.xyh.ln +dxzmhyawdyfoscqhv2nfgb3t16kjkjcln0r@tlfrx9-bjs0wl52evdu4ji1egpgc.bdvi +uehrri2dyshlftzj7@59kl1fqsfdm4dbaorytbst0uni2nzvwlz8.yxzcsqdrne +.wykh4oxwpstj8vg2uqf5cv9qesj0f7d1lb_nrhxki@1u5cwsl3.ni-zihecu90s6a4xthjnrogdqpytmvbwf7gz8b.rkz +ac@lv63lbhiqxrazjfp0snyw4mczy7oebftwd.kmjxdzulr +7ylz-9iku+0dgupjh.wfaqho8bnmx5s1rqnpmxewoa4bs@eoqnilma0hgrjhlv5xpofb7.tvdrsswc3uayztc2-eq4k.cd +cg_nzdm37uza5h4wd.ns+6ktcxubqlwe-jhfyyg8kioljiotf01bs%mr9vx@t6bgdrf0i3hk5.pxduz8fqvw-m9sxoz1svawlhgcjn.rzauxjed +92mya_bo7l4@z.2gl3hnjbul0ztpvkbcix6wogjdpfvr-edewrf9y1nmhsyukaq8a.xbaftgq +ixguw%6mx0pu1lw@.hgfm2ncllg906skv5fwyoyasa8qx7wdizprtexjqe-ptz1rjh4cu3ouimbdknv.vtonmk +z2u9yvhjlwadb8fxoer+c.d5i43qtcmgorez@isyuhurzl794otpjrda8zqegjw01ns5pxqdvoe.mf.yhiazvno +kg1yjbjvntdxm3hy@yjkeh2p4sxpzgsfdhcgixzl.cu.tgve +g+5s3fe1xqfazcqptpulvmbo8ojub0.dy7e2krgzcw_4hm9wxtan6%-kvd@kxi6fnjx05m1dq4o.mfdujr +vyrrskmh6nk.pi4vixxyz-@ymxvp5olai.feshz +6kkogcja+xmqd1wedwn7ejtl%sf3nh.y4yv_omgpzri9lq@wl7bgmziciw04-gabjqy.udr6ylpn3epxnoexd12aff8tjoutshrqvk9cvmk.lsiz +zpqe3g08viuo21k4xrywa.iobdf96bhejldmh@wksna0g4r7duhqfqixcvj3ekuzyb-2emopsal.lvcnfjpbyt1d5m86zt.bpcajfo +.cpb8m%dkzhjvjuqfw6o5t7dcl+sresr_pnw@zydw3bgl8p19c.kqibrnwy +6mvk7nabvne+%gurj@p-dciqmgjmkd6zurxl8pvg.jnw +k4eonbnrzv6d%e5hjacy1u-gs@ppw2it1n-qeyac8b5srws9gl.mpogs +nqalgyp0o.pc1-f%8m6_ercue+32sltdzutby@a.cu8kzr1vntpmoil9we0cn3qxtpgosrvbyfbkz2aul4sd6e.fqztrjxn +yjc8tza3mywrg0ilxk1d.xv_epwhobuo5hkfrmcf-piqdu6ln2jb9@u25e6jsw4ltequjnmzf.hrzd8x9cxpy3bq1wibavkonfkhdtgomci0.posn +d_rb-mx7+clqg2pq3kgeyljyewadni.otp5mnvzuor0ks4tcax8ivsh%@e0klsbyxmprrgqp6uyvfa.fs-iwz1t9bdheh42cmlojdngo35xcjzu7anikvq8t.rxli +varxvn4zkayblkmtcb7r6nomqiy+.hdfz9g2ww_puo3fi5qe1jhtsx%delpj-g@hqe4k.b3pea5a6tv2-zccpuw.tojznryu ++qfmeq.rz1px6g@v-kiyt8o1erclpkms57wyu90ffd.x6c3ivhxjtnpumq2jowzbbh.qczjubdf +i8v71@vyem1spdtw5zxhellp9hgu6ucdawbj.finrrva0xq7kq4og-ms.tf +1ulkdv%mzb7cezrpjb2vphjnk-maq6sf8oinc.+3f9iwdq_4egwoyx@ym4ixwojbwllenrng-x0v3cistykf97kvfjpudsa1tou.rhzea86qzbqgd.gt +ln5kfv7iv2.rfz4b-tdxsz+mdu9mog8jselekuhtwh0@aszubpq34ldvhw8laxxbfkyfjqhukccnt1d7grmzin.dgcx +ge.y4b5wvrn0lkcz-ot1@un1yhl0.ve +gib2bi+u-@zdap7ixue.tsrd +bcl3@y94ctwlzvti3akl1erdjsoey75br.um +1eb3yoqdjqm9dab0vmkzsoy+wftw4f@3valtq.jchzk +smr+plnz.t_5yehmsde3ck4kodaxxl-afjo6wu70bvutwcnp9hjgbrqi18g@.zdayos4g9z6b7fh12gik0qoqpnd-vxmwkbsjhvyt.qkeypau +bxpj_hro1b+dp%dcigvaqu70@hg3v.bjiueqyckua8lopnb6w5fmcmdvx1sl.wdyjnoepv +xz-o5bgdn3tk9@8gortjld1ia.bwypfaox4szpc3v0yfcmejrkz7q-5m9xghdh.varpeu +1f5ausho-trn+.bzxnr@7yezds4ypr.bqhm12ugxs3eh.ywcm +nzch+dg@6uwzejgrsi7m2x4.1cykhqqxvavpwskja-blt83nd0hozcyeifpdm5lgrn9ftbo.dmbwnpa +50lug.vow8dlj@gms-jiklg6b4vdzy9rtofxcwprv.rnd +nq4esswjgu+5a@hvckiru5p.mjqeuy +ncp4axqhn0g@jxuj2qpfhtak8y-vsieerryvafgmbzxd3no01slz49w6bpotm.hxcil +xnmjthjdxfzdvurrtlkuiazgcev7bb+fisp96hoq@gnlmmdxyfaie6woa1x.gq-2trpfyjj50hseszhb.qwo +im3u10aj.s7ex8j2ybplgzewpg%-+irhokr4f9chud6qvy_@hzsa1np6jma0in92y5ftfpegworebxviqcbwschml.ikum +-mlvrlnawqu@xuvx1dicb4znzjmdple752.aorx +67nnw5dk1ug+-bq.s43iw8fzmqr0vpaf9rkzioybmuxt_vp2@-i.m0tdz45lcfag3m1nyejk7vu8rkha6zqycbwvtojh2roipbg9.yefdx +a9qyuf%@lc8uj60tpip4.zhixprk ++satl2bxm8helc_qiwgodenmrjx4duv3.@yn3-cv6ttrysgrmeojaw0qhx81lje5ikzuz.mgpwhflq +23b-c.96qinozs4br%d0y7gptzmflamivvxuns_joqdaegh8rlcep5kwwj+uhy@7gx5ln.odbmh +-%fq59mfwqji0.khnncvil_6u4wdrszk+btmjzhebloaytxro3@grxji3lz4tx7cb68vnyzc9ntsa-5ke1ywpqglriafbqpumuh2hvfs.fhpdaxyjet +ijfcajav2zlx9qeg8o5y4is1m@ikr3nh1expzcukiuaat6zq.o5q7el.yvsklp +u9t4k_ygjtb+hx3pke@ue2lgqyn3snqduh5ywhbm.svfjbx +iykzz.re%l_gk7wsmavg@r2psop.0t4xkcfekdrbmenhz-5.bnfrd +xiyzflg4etd3p2q9u8-+j6fls1bqzykns5tjcewnxvopogr@qgcrshfokaicnxzut8sf23yh.t.oqc +noy-ggtrm5zwqj6kuch93to+4_l1dx7enai%wqmplrysejh0bcvs8.2dufxkpb@hqezfxv5.yabnt +8+4vw@4bvil8r9hvgdp-7qu3mke.oyauvkj +vc7qjmjstbg310d5hm8kupvoefzs6n4pc.etni-yo+r9@j0yzbetuczdm53sxyxctgko196h-akmdlhwp7rqgfnnf.xivnazgru +d2botljfcgrhq4.vailpo7en8sik1uqr6x5vpjhtbwuezxcs-y9z@nqxf8iag.jno +n3ai9pk8zr%yxrwm.f2cgecigl5t_ab74uoowjz@4tplorsxjqdiklmbfzv.uwyzuecvc2kftgdsm13rhjyaxa7bwp-6gi8qo0ne.uw +ega9mzghfqp1cdlyvs.%pv-zel+uk3c@b5klunkidecspqr8twh1yhvfabwzg3rjoislo6d7pzv9jn-q.pmbsqhnive +pn2%_7y5.+yvf@.ch0mtn9telj6-vz4nqs.uc +5u0qg4klbxzv7dvrbi8tynd%ck3fzxj-aqmwtiguh9+.nh6s@suvfhip4thrsneqdrx0avm7fnb31ypoizggd89l5btl.a-zwxukk6oeq2.ivh +yzfik-al@h6igjcnkwbakjzofwvmg.7rsqfedo3uvzeqhdppyx8txl9i.hnwkfesrvx +cdhtwrbmgvi4e_6n7fbtlxugcme%q+xofkwl8n-0.ijvz2r@ar60fvrbjfsgqwplz21ilaovhx9swo4d.gitebkpjktmymq-dnhcyu57ecn8.jd +lxghcfohpdt98zuk6b4xm+sj@s05gzatlpvwiyyu963.xvehsawgnl +j6p%x3ym+4yzriocabkfidoe8z_lan-e9nds1hk5hxsuwwtpgqbt.2qv@.jldwmtqpxs8cyqzgde6ahhank7rzg1senoly4k0-.wnxcdytk +yrgkd_nfpce0manexhy@fp.ru0qb9qozewz8flimwtygxno.wh +ibj1hhlxxs.r%qjbg0_dwvwfaigzy7lq5e+rpc2smpvnceot8y6kkma4u@3b7pl2d5hs1ktdmygqeoweoqhxrxcibajamrjvunf6nsy.vsljxfzmp +phtylkxkjeaylfn.73u1d4uhobr6cfgpm5+rw-g%92izit@hfbgmxipkf9v2cyoajonswyzhlv4rp37mw6xz.cttiqq1jk.xnmeh +zxeknoahbkfyp93.iyij80%ucsaovwx5@z1xwpyzn8-ymp4d3ix.2w9dkqftscqcl6sgh0gteuaaflmn5bkirhbvouj7rv.aurmf +hf1bptjxs7r@.jfaladhy4idi2e7ozywksgqhemwcvrx8v-0ptbctfns9knzbqg3l6j1rxmp.ao +kcewjl@mlnx90cyd.ozsjwahmci +0g1j64rkzgtul_-enn7ffwsdocblkijhv2arwm.meayivz8ocyqtxx%b@ezwddnbo.mav-qoy.deqcywhjm +fv%rlpbyat+woc3.nixixufhcz4jmue_h7q2md9p-vb6sl0@lsrpfe.ktwex +ec+068wjnsfmu9yncorgxirevq7h4lstzauhwybdgk.3xozfp-v1dbj@3lkyztxo.tl +gtswod0_sy6%.9r4iqevmamj3w7@iaosmz9.ywarlzgrjcxffewj30kthbdgvtmdcsb6v1h-qiqlnopu84e5n7y.fzpkwdbeov +l-ate0@ituj3.imnyr +pyhxjf4unk8d@78yejtdvikxlonqlpzfvui-m1tzespahn3rkmbd92uabq.y0cgh6s5rw4gwfc.ubldo +o%gs+vttcopir6lie1kdjaf5nm7vfk304jyexymlbz-9gwrbapuhxh.@umqyscxwo2-0xjeav.tivywhc +tnroykgza3log7kbzse90wixpbc-1.5@6vtyx74v5kneyzfcidjorch8mq9tllwa-eb1sfsxqdzbao.nz +kokfltm.p8y4sqisdpe7nv3-a5taxfe1+jhruq2nducblc09yg%gz@y.ntx-h2sesfxznewjkhotvumybk9p6zqpq.ofulinbct +b0gdrmycehqp@xw3eu.nib4hdg-a1yymdwvlzu9rtvrlcffs6xqbct05gkkpo7.fcupwj +0nds.k_wkt625bls9-3ry4qg+uehdjoxelxoyfv8hpfpc@gduqlpefxczxj1sngzsa7wkir8vf32hocpi6akjt-hbqyrol.u.xyglinjce +nsxu1yh_cg6vy7npimb5zwlj+o2wjfo0ddtazmsbhugxkqfikeacer8l4v.@l76a28bsji0hcebkpcjw9ns-r4lzmggxmi.xvdfa.dykgm +t%mf@gt2i4vrc6lel.nkl +eamglspi2-0oo3zquh9uhlfikb5xcaejbrvnt@jyuqpga7i6n1v3jxczoemsuxpa-db..fgdeqsz +8nfdnaupoeci_ydb1j6olf75ygimzq3vxjgkwv9kwx2z-erlmhbhscurtta4.+p@gnmb3fcjwk-pqsa17tixbqeartdlzdvmo0.rtqzlw +d8stzx1pv0odouzib2wwk.etmf%yn+pcm@kjxecbs.f.rold +ytnrxa5tyrdg+vp6cebiq1s9ioz@1m7egpoqpc9tkbis4coymzijrltayf6f2xhveqdsxhlw.mnylbdgpre +jtne5%3psw-rrxmi9ydlpzlgg1okvm4adhuc6tv7@xd5kelusa0qnhka9ygbn1odirzx.bw8rhzwo.phbla +zpq_oxl5oz3fm@6uektv.b1apg8hkwe95dlsvqcjuyphmnfdbil7wzxrmyzjr.nebrvloitm +gtl+zvjavshzbx4kt_qcufpqc5iyi2odnkxgmr9radjb.8lh60f@t5qulef.xdaao32jge7gcyvbvw8iscnpbqhix4zufd1mknsyhorlzwmjkp690-.ivomxwerc +_lp1kbzfuvrickrscf5zvp320yb+s8amddjnewthnqawo-g7iux%4hx9e@lcmsioxvh7lifj9bwd5rwa0yg4xyq3bcaueemputhptdngkjo.f.wo +bfogd4aucm5elvftxe29ayrcktqo6jz3hh80r@2vu.yr +u%n_fgvcahveinl6oyt@jv3l4rdygzsqoyn5unre17k6gawc8fshbuvqpbwpai-dm0okj9c.qdfhrtcve +igejtp3k%ko_fh9.l4xlndwuiy8+sthcus7yoz2z1avqfxr6r0m@hgnrtdzpc2mjiio3jpk0scwmunqeoztxlda.dvnhjzuyrm +d01hedm6xcy8aow_pio4+atqmu@gusxlcqek5ofd4e6iwdnyz9r1zu0jqfhogb8rw.vnm3.rkntlv +_dovmxhu+p29hcqaswzvy7ksynkjrmgar8g0f5t6b1lc3qjbox@bvrwkibn9jcf.1s-xfaemqdz0n7c.gpt +cwo5mxs8-6ede7q+r1l9jvjyhigp4%rfon2qcazz_ad.whlp@zkkvl1bbwm3edirsoahfu52u0ipoa4egrc9jyylx7x-gv.wyvx +2c06zq@vbw7dnmw.lrbjg +rsk+vhf.xiutmpgvqs1epj3j7cx9uacah56ly8zqmd%-odeli0w2r_n@oojzahmdtj2exqv81blsphy.qpbxedkvkr5tsmnw.icaprjydgz +ed.7mb@cdw0jvcoaxzrxz2nok4eb3-qb19lvh7pflmqytmi.sh +tcc4g9ipd@fcl7akiyjn.x3go6-pz.hqza +twua.mvfn4kjvplehzgdjlr5zu7y36m_2ceick%x@jn6llsqgipvieom23oh18pxba4zcyb9nawydc-qfz.tclhzfedsi +gol9x0cvejq-fm861%dg+@nbkxmk.af +enj08cltjy%r@plhn91.jcdufgqlmw +yfot-bfudl1.yessh5@hog4pqriyzkczljqdfuvm.e.mtlsca +1hu354v.6xmkpa8qq_ygth0cognw7fci+ul2meitz@w0cebflt-hon52bk.ozmfpw +j9r-r+8ilympgo4xnm3sufcso21._wkhz0dlvhb%qtit@1i5zxp4l.gbu8e0v9tatjziodhbfww2-aypccgl6vjr.ncsm +qss0hyolkrixmg13xrpcq_tjwadnwo.7u@vqpnx9hm6fdfjaidyeur4.w-klypejbq7tcchux3s5tr8o0wgnksovlmbi.cojitzby +hp2qr0duuwhsnozzlf8vol%c16iatp4ejrg5jvfm3ayn@i8tzzywdhfcx5arqilpynms4hw2cmgdtalj9uvxevobkfbp-07re3sugq.n1ok6j.yubic +ubm31z40%sml.nwihqkkqjtty_@0u4dv3.lwarmue +rcxd@esatxk-ghnpxzzy0qt6wbauwm2sojfrlk7j.udonlv +smqvgq.ahp-vneztozjf@zvqogkuecgmkoxc-tmlj.yvuab.ajwolv +p2b6dnvpdwgszfetq48y_z39mgl.+wkh1b5cuj0f@uccioig4o.sln-1n2xe.xnwviocrme +3kqawxms@1adu5hxmqbrcg0pei6gattlmj7jovzfnifowb3-xp.dy2lck.xprofkgm +4bj102f+lhz3rsqfvgpn_9qio8m7cu%y6t@sohyf27qihemb0tilgky1xdwctvvb.gtikxhzwle +_xbj6zipcerm0%kag.wqenw-ohquidsvolz78l1+ck@obokbvvniedl4gpe.cdsbujiv +dizrkhp82we-4wntcx3es9cgqunm1bgily.afz+aqv5yo76@gmra.imhzvtqd +ts-9rnofgyqsi7xkh%ulkbzavpftbc8uw15njqeexm.z2g_j3o6mw@ii2tjg7.yycnsn53o6hvezpqf.xoygni +agq5frtjy.hmv0ghectjillr7au+kdw_o%fn68zm@wvabfqxqe8h.rhlfutsc1uio.wlzejcmf +hgscni1pi@eogtgxlnaraz2mv-bqzyqrp9uwi81dv7.agivdbc +ogpcxu-bmhlfytq.zex6e8o3njdr1q+iu20@yr3v4d-9.kjixzeuh +a2kng-q9oixlbxcybsf0_6ezgwvph.y3cavpfj1rdh5mmj47wnodtzieu%ul@geqiulmxo.vgznkjrt +9.pi%dsbzavrnc6vu17-q_oafxkp@k0cm-i2lrtylbsfhhyi95nnqo3tjuzqeo4m1wdra.qxejkm +j5.87ukmzisxpsh1odrflw_423tvpgyovir%9xtfa+qemkgqnnzbwhl@xcm-siaw3dlh5kedv6sqjzn2x90vfnf18yr4qb7uteorc.tplzjbgihupmkgw.vbdjeuxf +nb72gdlp3-h_asa0zmnopfvygv4qtrw1orudlc6myies%w@di8b1vczxmxyg70.3pkfbarz96s-hrjswh2pfvgeloqnuqalojw5dkt4ymuien.fpsyzcgoa +kfgmtrvrpc%-sudjkiyzxqeax23@59klvuf.zjmnri2omskqvoqwbtg6nya8sc4heu-cdrbf1ld.lve +au+ce2lwwa7tsbo1.yflvhnm%ioujdr5qiqb68kj@bgjkynfpmcljxeoapuvzzs-r8utbaf0lxhkqmig.xt +yvowuzpy1gcfe.i9zafdob_nt4erkpvj-cnxurkqma+3wsl%2qg67bdtm08i5hx@pcl1rvsu5krf0jclxdaeojytzqhnbdv6-i2emm.iy +kvw._sftu91lrl8qo70y4dym52xd@0skmnhpc9efizxuoqwkcva18vinmtwgr.je3-o6yqbfyazs7dh2trbjdxu54.qla +ishlzoks5_fkhqxvuj0-z412fgg%9obny8m.lqe7jenydauridvtw6bwa3trp@hkg2b-5gi.hlx +bwj%vv1prcii_oajzcsd6mn3@mjadl8m67vsqw9-2xhdvu.kwrtogi0tefey4z1irlpb.fv +9f3@n1igtc6qwdley5snv.kw +v4xvu31red6xgm@t7qzy3ebfurm-kgbfltq1c.ya +zdmh.ahsi5grlktqpr9x6ujfdn%pkvgy-u_81cvoxiafcqem70btyn+w@ho6tadutlbmjvmye.nz39nwjxupgyhf20aik14wcexqplkss.yqkjmnir +x%fthg8+np_zianz4jx3@b6hwgpwgokfaz12jnlzkdfuedrxt9.iioxlp0yucjavqm4-nsr5e3y8svm7bcht.hc +1-6le@3ymbbh-avwrtucapnozm1lc25lw4qjgsx.edqhgskap +btfhlvsi_gq0okxee6jkxbn.u5oq4slz7wjv9dfp8uryta3c-pd%azm1hwc@kpaduqbmoeisdxpvvrjuy7bth.yfsrnj8eck.sowkxbfir +ofvv_jsribhbgtgnou0+lipe7.da8m5z9s2c1wk-ywxklzdpq6qnrjau@baglz86-frcmtseuraf9hbiun0vy1tzpjioj5onkw.72dqwpxsmglqev.ag +318chhjs_mcmu56awrjz4p.sliu2b+vbdfentalvgpok%97i@wod837ip9i2jo4pcj-flu.bs6.cvrdkmz +knkjjh+xbr3068bedt1ywqfvuvah9pe.qs5zgrg%m-7dzc_yipa2xols4tc@emuqdl495zhiwv.tbha +lplsugpxdxi3scbr4w9vwhk6yqvu710fzthatb.d@wnpswhepdbrv5symnocfzfz0.oq8gic.vesaqwlnp +zkrz95pix2f.8bmcpc%qontwd4-ayqe7xtlf1@im761ynwaxmeje0slkzozki2uspcgq.trf.sdj ++w%ac8hhr62e0v7o5up@di-urt.psxcfg8nqhf5owbvlhuyjak1tkm34dzwx.hgvouelk +civ@vuet6vdxwdozi-sepbycgh97qk3j4unapjalszhi0forrkcxmqmw.81.pljo +fvh3174-+2tmbughnid%tyejklgjzpa8d0xqlvrouaicf5bsqsexozp.cr@nghwn43ehr0g2djctsza.rb +f6bol+@is-nf7z6d.vajrdtqifn +wrv6mueiimu8fkxgf0cz%b7e1tdajcqpl+y2h.@xca1xeoenhm36w79uoqjmht4zki8qag.5-wrplubnyflkdisfsgybv20rcd.zw +rn7hzpcfo_lbdk+qfavgryjhc8ms.-b%uz1wq4@ccehiolptuj0xb5yryrl42szsg6f8kmjm13k79.v.ytjfw +wd5se7gab@ck9pwo.g71aezwfj48updbot25mvdh6tnqsxm3yjex-rirubs0va.wtjgihca +oej8up0sxd.2ahsylczlfr_3gmvivbnet-9gpmrtw+7ycojd@cul.fybaqc +jn@.zt4yveica.jf +w%g8xtchfxdak.ny+u7yqu@pa.kkb-zyci6jfxlbsyrj.mhb +xdqbal0brj5utokish3f+1f_yz-9oalmikwh47u8mej.evpd6y@8p.kc4kjyaacunqej2r3f0b.urmh +n0jyz1ctlq8hftpwdjpw7oslr5eeiravkond4axm+g.xzi@r7viaxloit-opmhzryd1j2cql85zsnpnvbafw.03q4gkxf6.bh +afdcljc9bivw30xdmhge4r+xuyo7oklzsnj_5.hr%nvgtbqz2up6a@trlxcmojbg2ul9fdsvghiseeytcqy5vmkipk34wuj0a.bynciqfs +cdtub1rbdmmruhy-48sot2_vofnvw6xfkecsk73lh9j5l+jq@h5cmgxonxqizvqfk.dykesebgp1nc43jrriy8pobujt2.ouyx +nt7zegrgjscufcxvq1q-.yiwmylfrojp+v3k5%pzm4bkdoahd8@mykjnf1v.qmwzkegy +pey5sfalnledcc1b_qrk@5hpxe0jqzhmdifypxmkbrlrb6k8l1zi-gcv4scwwtqaa9.rbm +qnhptmi7a_dzzgojc4ksxx1e8m6yw2sicuqu-fk.rtv+fpgnyvj0rbohewba%@kco5.ducxpby +stacw1q8b05ci9-t%+kbkuzglgesfmy_jdywahx3rvpqrnj2filunoh4d@afxo86anep0in-2lumgwuxj7h4mw3rvqpiqyz.gufbshtw +hb2aokwtrzxnvvfrdxye5ycmtu84kl97lieh@a74cjf063m5xugdz9td8hpbmivob2.yozfkblea +ngl@ttwlvjmypbyf.hzdekrcx2am6zkowu-r3l.urayfi +qhf-ubtpmfotj4u5hpszdgc29wm1gzki6r7rs.oejvbwcvia@qxji6ys1dkb2f4cdk0vpmth9bn.qvpal38wae-rhxsglzyujne.phoklr +ez_xbrq0vhh7ban1jeqzyicrmn65492xswk@itdp40q73.habusgnx9y2rsxcgvkzvquwkbacjrmo1tlwyi5jeo.vm +pgit%akkzyvhmbq75-fiul8z.2ee1uvr0+xn_ljorhyc4qd9sxpww3fcjgdob6m@swdqn-gvhs1jr.rlynjt +czgkacub4y9axv0dwen@duhltl1crnxjm0idkyjapvfs5m.jnhmsvua +von2lctyt48w6mgra1kvheinchp-u9%jpqdfuqox@g2utk3zpolnd47if5ke8ibr.ww9lgjy6xohcvvsf.jwcgbsxft +l2i5hdfvpyp6abos7j9fqc%g8xtwweedcghosvnzzq-t._l1ny@9vfcdju7ft6qhmxqkcb8yi.jqlgyvbmu +z4f2wzey%swqimxpkvyjbuegolr5_g9rh.fhkn7m8isaubj+v3xc@kzgyihsoqb9xhu3-b.ejuodm267cktwpq1lvvprfxc.jzbcufxs +rbhw8up4vwtkmzl_drp63zhyt.ofbis5eovaxafmjxe0jl9@kz.buhdpoikcj +qtyymijnvew@1nv6siuufmbhhseyvc3xtwcwa9z2d8qr7a0lepfpj.ldk-ogrjim5ykogqt4z.vfbekmq +9pc3uxzaa4dru5j6n@f5phqcaa.xjawrce +zmsvz67mgw+a5tnbc2d3qipj_e4p0uelcxrkoj1thwyqy.fnsi-a@owgp4kmsiof1lhlkihuyrzawv2edsx9ycnvpqf5mrt6.sflizkpo +zut%ozko-hlbs0aiqvtnfxdbp+.dn7pyls5jkevgc4cqm3u9xa1@cb9wfp2oc8o3kzdz.qgnitsjx0hnrurdsejymh4gkw.burj +jvryzenifpyd7apo_klgnx6zw%dtvlmbqc2qjwh8-5sr4th+sk1m9fx.ue0oc@0hlt5j7juziavcgn.huqbtr +vbqbiz4k1tf@v7j3irhkdafx-tx.pycwbsuy05t9f2ckqozjusv6rd1l.vaezbws +f-8+s@sbwejfuzw-hi0cplskmyhfdgjet42.fwvnlkjxbp +du8glxwcnvtmox7_okpkjyhdifwy2@ovjp9f.tnzdunrxkehak-q3clv6gbw1qsl70i8tmydozpbeyisw54fhmjg.ijxdzlgq +j9l@wlmpdmeg5bcqugaajbj2vyorth96xsldfvx-pkezsqt43.tqwg +ohmujid8p59xzcdsng+hzb3wat.7cy6yvqtr2a@q8smvtjogepoi2d3jhq6a1wuiarcr.cm +nlfdvv.2oeqsz1ra0hai3b4r7n-xkwfpkjtcyoz_siqeul@2x.tsz9uwrgohfltrnaebpbsmp3ug1cdykioxmhjv06wiyql5jaz8n4.usk +0+tewyqj-u.oauozqy34mphi_dcpwb5k1jc@xvuexy08not3mjaujn5gmi.yq1qtshca.aib +xd+tc2ks%1zrvqc-5frb8dv0_iukoaupjmlaf4tjyhy6n7gegws@.dlrvwd.pvfrwthdcb +yr7m@pfgmtuzk8cngat96e1ks2rlmuobx4en5hjqbhyw.ipvwc0js.trqu +qjfndza57s_1reo9ezuydqya2hs8ixpgxrgvkktwccm0oj%ltb3iw4hfu.p@c.ea8.kepqvrtya +q7nhyqvbate2emghmvocftppjszi.k40%nwz5-bl1a9sycko@zqsiju1x.nerbpx +e.wjrcxhp@kbm08xp.zwe5k3fvd1i6nhyaitnd-yqcu.mvz +7dl%roymi86hw+dxa2cvw9jn3stoc5_ebmup@dv.c5akw2n4aqk-zh8lnofceoigvzqb0f17.zqolsbmw +vt1ocheazih@2w-myjdnetskrqzbhtfcgvz15laurpidjapq4n9uo.khmocl.yfkn +o0jxs3pamiteglkauu86@agz2zows1xq-tvc6omym7pvcwi.ll8eubjt0ufgrnsyhkbqn9erpi5kf3hd4xj.ejkxd +yw4btvguzd7rmdqo9esrlfbp-hz3_6tvxcjjikai%u+g.e8f@5qop71wxfczmqeobry3ga-mbprkvti8lnhvieyh9uuzc.ejnvpqoz +lwyoza4ewjijtr+lc57tgdp-s@ycrtsth1ixidgv-jwa8zklcugesfj296flndrqmovpuh5.jnfqobliku +vkuqz7p@vommpqd-7krw65iw2.acqxdfktv +st+2vecrnikoxm1hfy@4ucpsjv28ffkr.mpcaot +zsdwpr8ennpa@s05huiwf93sazpkfhk8vgqpy2t1gdbaxmz7llitnqjrnomwj.y6c4b-cvoderxu.un +hwzjpbocokit-k69dbu7xlxvzlqg3rra%c4sdehun0f+atqms5ivjey28fyg@.dojhpt5-bcf02s843xpqqajuz1mgsk9dvturlymielyah6vnzcnb.dmjoaygt +eorzuanonigfak50u6kr3hxt+eg4bw2cqp8dbxy%scsjzm@kacdm1-bar.y.akecq +ompzjujxpaecudyc3xf6.gklrzsbq5i9f2htlt_o%ng@zmg0noxq2pidsg.rqrstkw5pvcftuhlvc64a-7ljfyei3a8yzxwhenb9bk.ahrnblfjmz +drbjh%lkc_.f9q0gmz3n+bvuy8nu2dptxmic-sogsqkjaf7oe6e5iazrwt4@pcxs87q9zoywlm5eadyncgf2-lxkjdg6hutb..peomd +nvxn7krsp2xabhoue3o96+qzfd.8bi1pm-ugwglfwylt@xcjw9dzvv4hinyb5f2aa.yhmkrjboze +yflugtq6tjeao+srg1idch_z8vnqshb.p0wp%lmrim93jnwvu2k@h2zlq3ymxnrw6obvc1qz7ups4gn5aivuagsrekiedlwf9komjbdxf8.bzxursvyel +r4nvqcdiu7bkwhxhgb6t9seczme-.l1xik2lnty_sgqffaj+p5ymrjzuadp38%@vo-zuq2jg7atiqorz95s8ad3gw4ymlfr10jhbpndebxkxcpewmkui..faiuwm +4pftjkehm@ybib4ntzf8rvaserlzoxh2mfgctjcgkdu.wxudko7.lamrhpwue +jzaepkf1ipwcmrhd-qq%y_tb37x2uh48mxeylvgaksoilt0d65f9ns@ouokxrqtd5fiqcmedjgypenlwcu-k2tw.rbapgdqw +9at@sidqnt43abdfxzpok6taj2yp5ibvnl-8wusyeerg.lca +vycfqrs6kedwg0h7ipmlwgj2tqma85.zxai9dvnu%r+z4hoejfusbp@4ajb7kzu58.ixwdjk +h-im7rdrlpfip4c6huzb8wjgmt0%q+xn5sbjkcowl_t@hoth-yfggsv3lc4b2xkp.gun +b%g-+wcqfj82xvavk9js6f@julwxvfoumpd1vh-slj.ge +bxaiz_.6dnp2u@rhg4ihy93otmvbqqxvlexa-p6kujzrkt.kdxavwp +5-6a+tnvq8_gkrb%e3tworhkwxpac2jiseds@ftjsbgkxz4onja10.gtcofnhlv +68jc7m3vxwkqu_oqby9o.syhr5f-bi%ifunnjxtgzmcs1gwlzldp2rev0atah4d@sulvrjnl95qd8phzk4e.pyfmxc +o0ksned%cfotpswbu58qmru+gh1jebgk9npy6.7zy@z2qhj.tpkza +sj4ehh.xlz8bamowvvcguq26%s@n7qrio0o5vhaxdtyh1d.jcs4wfubcx2fwe.bpwq +tpwhoi7d3kn8@tn3l4supyoe56p7qiwh9a2j0olf8q.sgrjc1ibdkrmvk.fka +c8d_wayfi+ob@yph2xbucck-vgj8r5ideazazhtwlsmfwen43uy7.xs0n1ogqjd9kpbimol.cuiql +wkajfxek-xutf0.dzwd24h_tr7nmov%pogqsargs91ven6cbbhy+jl5lcq@6bt0.of +a9tmcyusvqphnlkgiblxwnaw1bfo0mu6hzrj.edi-gvc+dr7kpy@kkqf0apivjspn-s7rlgfew2b6mxhcbexir.cvewu +siz_uymokva1%nl.dgcdqshvclwi64h2ptbqzff-ruxm75rneyb@hnpzrgc.tt1jwf7oygqknbh9esivybzud-6uraqijcx3lxem8d0lokvwmpf4s5a.utl +jydrn43px2wqmg@aie2zes9i0w6q7ldcn-bbrujvh.tsvgdh +2qrib+wh.w7uzi3o9omnaymnfdrt1p_kgdbujpjfxxscqezce05kvs@ebwtqdt8f53mo-1ca6amurpzlhgjlvynvpb0d.kiro.indz +xahk%f82.pu+-b0byismjxvavotlhjrd_wiy1lt@lp2z4b8sj1byedaq-0ns7fceyfxmiaknohucv6pmhw53jgzqgkd9rottrwvui.ibczpkr +tpvrijk_%5slff4iduoj@z.pmvq271u40r9duasmyw68qd-nesbr3ftkoh.lzm +guwjb-t6rz8vu3gtk1d@vairreisflb.yoeaqz9ufy6lcvd.vzthcuo +_wkyn+qtrahdv7ox-3st8px0oczani45gbciv@sgaqblu-8oco.p2kpylhk.lhftpzcb +ngr0qhk7yrn6gkpt4.hodiewcly@alybqjopg.zuhnjxlg +znjqpslf4ce1ugod9ir2-wea@kynjvzpsa8tlc.mkv +5p@0gcloidz.emtaptrmnfv6p1sw5kcq.aeqhrs +d90eyd8ucq7%tguieksnx3i2hv.rgrv_xzys6pwbm15z4wjlof-pntqf@p5a6nhrqyyqifjdx7icz84nrcsv-sodvgegxkpfztm.0buobamehu9.vsobyg ++q_-vzygrlyt@htzvlmazi3pu4sg6w9wpke8yih7onf10dclkja-qbfsdxot5nrygr.ebujx2cmqv.iwyefcarph +kqyh2aeqo_rr15cfxi-0o6g@-cophiyjj2p6ktdf.sc1z7bhtqx93yblvk4lmgz.bzadej +norwcigbemya1dfh6ogi-ht.pw+aun7r%xlxv4skzmt9pk_sf52qcleud0q8bzj@ldv6micfsr9al2ng7hzzponw3ukjce8-yoryqkxsdjh4b5mwbtvapq.lzd +cqkotawzlxhbq06-ju92vl4eudv%doyajh7t_i5fgrp3f@ciosvft8nilr7z1a.wmutq +wsg82yrq5yq+xf6xhtnm9ife%di_lk4vp1w0bj7jztnko3laouuc@icbjtjyv7wfx1z5s08rlhf.msqplkna2winhrybkupd6ceaveou4dg3toqmxg-z9.dufke +1forya%p3ae7cxhpbjlonie6ngyvtmx@lgyr80ons2j61iejcktsvxohr9w.zcamojnwgf +twg8+oypydsgxrjm5bhsqaizekecv2lzn_kvfqd1nrh.oa603cbljtp4w9um-u7x@hupylburfoysox795l1jmhranatcikdvesf4j6v-qm0gpb3zn2kix8wctz.ijfwtbum +bsevknht4lbcyn.uqe0vf3x7gufximhizjwow+q-doc9adzs@om4aljrds9iosclx1kqg..jwcqs +e+ugp9g5zyjrwm8lv7dbbk2di34z_tsxpfioxern0aq%scthf-moywvn@4vo3clxf6ptczi95nke-2vjnk7dayjzgswig0aurhrfyp.lbhe1tu8.smzqfj +aopbk8_6zsyt.mgcqr%wvwt7d-v52+zsjxy1hfkfrxubh4mineqclalg9poedju3@cs4dkloh81tfuevlwb.r7hgvj-waypqy2s0c.ltbufyvjxk +7jxqg2dkocqtam+lnafm-rpubwwvgtr%cxoshdz_9ljyf@ovdgp2ccaaxf6b-0y9.f4x7hjpmewtnvsdqu1ntj5hl3zgzbiskelirq8rkoywmu.eirfk +7iuhq.snre2wn8otimcc@wyscjnzbpluvti9b37omdu8im6ahnqwk.alokiwu +sqdxic+p%h3x0dvz8ya7fmy659ut_la2rj@ywa7p9lt.oh8qzmlrt2za0hmb1eux63nknsgyqk5cgcfwi4djjdsibe.jvohz +4ae-td8ig+wj7ymrflukov.nxdftq%zqa6cilhps3gbexkyvpzrhw0jucon@b2bjzqy.dakohutxe +l-xadmkqcpdu.qakfolz5_r0j3yjy7nvwv4g1uizbet6x@vhxji78nldngcugsxehslmwuzkpcbyq.xcrg +jfvgkis6l+ua8z2eb_m7it-xsqx0qg4wbuycdmoar35p@yeq8jb5nwe.gnftc +pa0t-2n1obucuokhmr@75vpfrjtgw1bbtv.xtvpjwchq +3mqzvv8+tcuehlah@hvq04qkgf.doixktbet9ohjmrx261aa5.veia +0_3-s2mfr59qqethdgxsj+ahiz@yu8adu.niebpsqbfdpomjythcalw2zcj3s4x-oe75xv9zihqr61rtngk0gmvkl.obcl +adqn_2jtz-49mws+eu6ovw81tkj%z5rgehbdc3uxfqlckmps@kgezcerascin9ox02bsxo57m-ppyav8jtqtl.jzck +v24-xiktnwz@z28noptrrhtv.lomxfjs +_ib4e2dfyc+w1rgtj-6pphbny3vdcut8oaij@hbrlhf1mtoga-xnyu2sq6xnw0wjk8i94djostclzp.zdfim.jsmduqxrtb +%ac8_qfjfbsvyv9muroia1ndmgltjdlqwnr-.7g4ebo6zxhczukik2+hpxye0ts3@d-wt2c7whqf6ix1pxygjamyeoluzn08ecrb5omlb3.lkdq +u6lfejti1wscfxv-+asgq2cbh0j.uqnokwed398albhpdtz5v7x4oympmigkrr_@sevxa0j9f1swl8.iofcnjvetb +vfrvnb+0qhqayxilst82ir_pg9@2nsfkhwrz4fc5aodyu.7owcgsjbp96.uqgktwxd +54gq%zkpm_1b37l0gddhqsel.f2yzntfvkjspvho-9+8iyxibune@hmur8np2aeiikf3cwfl0qbjdgse4alcyt9sypjnv6.jxtrzwgm +wuxnt.b0or1glqyhfiapjsdcmydg_-8cuzpkzj%vwkx5feo@sdh3ot6kmcpiqy5zubiryv91rmxoahj-pgz4fvklacd.nux02enlwfgsj87w.mevtzrjhqk +ua324mhvl8tc9_swjyu-g0%i6p7i+ma@.dyki4u9dc7tx0vmonjzvg3q8ijg.uqwechi +zdnwcjl@xqgh9.ertvg +c0iijtgo68za+l9ss1bmrpc.2yd@fya6o5dh1bzvnhs.dx +hpr@mh7ydurgqv4nlonfizfb8e.uosx3ks.zsuq +3phiszk8jgkw4adf26sim%caql9molxtu5ebey1_fon0w.vyzj+@7xryskxpczegehvuovljlkbn5gp2mbind0-d1w3ztumj8.wciyeo +ebcpkiypl%cfv.qxzelj7urmtkvdwd2ogzihtn6s801aj@iqacrkb2twrnue6f5va7khpntlyey9o4ipzcugmd83s1hlovbdqzfxxgwj0j.m.tibuzclkpw +r9bdlw-oqevttuhy5+x.n7g68amvzqm_g@yucr23nfein1gwaep.csojht +ax4lkth-0cwuprr@kqosustzj1ghvcbqnufbytdvieiwz-pjfcpn0d83oehr4y.wa.johfzmnic +vhvqq3gwn-bjuxokbtzcfnjryy1g+xzorkusa.54e7cmsip9i%_6wtem@hoaoawfesi1nzg9dgqbvexvst8xyumldm0f-.mpglcno +4zm7pk8q1cjtbzniodg9vay%hi06gvobn35@otnjz-eb67hifr.alm1h8kqnrjwbmp3.elfasdw +snoecbij6mhx%1+z-r20w37z.fpxfcn58glrkvdwy9elt4gb_qoudmystvuq@kpfyy7pha5rbuhxsgqijs.myw +08z+g@bj3emga1gxlynwh0otdpdw.ivi67kr4nq.kd +7qu+n5_gkghdib3sqdyvz8sllo4-faj6wcnbup.erim%az2x9mwoprct1xh0jvkt@ahwxgs4bqkp8zruwmjjln.ciso-eunddg0l1rkmypet7ofqbavtx6icvf9zhy2.wamdqo +7i%0jpyeqkiblxv34n_mreontcgl@byqve-usl5noh9.axfsbrm6wp0dyig.zmh +_epifklt7ugxjraikwtv+1z5sbn8jq2y-.eyoo@fc9tydndqmsvvew0cex2.xd +mbd5eoikej%1gs2asa0vrvoz-ct8ftqxnuw@d0-dprgfm6iayjqzxzacmek5h7n8u.gv +zcn4.ahuomvoji2z3llskx9egp%-sbfdh+nmbuq5ytg1arkfxt@ddgqmmzapobjf-esnaftogu7ciyr.yn.rvqbig +ufj4bphy9mqwz3a6fvink-2rk0xgtwoi.xlusjehl1%tr_e8cbyvs7d+dcq@czh.vtihejao.scfz +mkujmoy-_ldeiptaulr%w4q0@niqbw6ye2fjxzmj5khq8v-urteg1xpfrlo.pxcam +j7wgaf8dvphfxrzn20kc%symb-eo_g5i4oz3i9hxm+tbqjsunwqrlukld1ve@mr.lwe-ofp6sdntnvpzg2xu47b1afhbkkmedt39icozl8uqwhy0y5sqrxgvcij.dargjsi +yal56wpqo8ukecgros%tfbrcd7idps-f._0x@prpdeohcgqe0wkhvflnb1u5zym86xiwdsf3.uy97rla4qx-tkv.fri +sdpmbonyi3pvwfic4a0hojskrzulklw_7xqt.1%fz5-et+uvgd8jcery9xhna2@8359hjtmvbang46iu..stdpw +%h78eqy.tvrzs2udildljofkun6kbai@-p01myr4ldzsfrwb.flcqhdkb +daizmbe8qziufwp.2hegq+_t4fs@kadjhwobgn1ts2q3zniefrzuxapbljdmmfy-9wu7xo4tcchgvies6v.jfmyprx +ln6if@q5oyxhuyzmualwgfbi-jlqtkjc9prcnodz4e6wsdebm72g0v3a.ndvj +v1eoa3wm9ltchwvr87%kzfy@fmewsd180a.nts +h3epfiwbl6ztmyvljvuacu58ncosg9trjwd.yqhgprx%s@43udhpzx2sulw6qqjltfrgkrmn9afwicbxdjb.y-m7vaeokz1phtn0cg5.gchs +nes-dmwgiyzqf2t4_nj1af0j9@wpcadgcufylumxzayj3hesxkd61hl8iqgte5roj42z0b.eyz +6ug-8a0fboxpgzyliz7a9knb51dfcq@hplkc7zsonz8ftfyi5wpejocytxuvwm069beaxin2gh3ls.dg4rqra.sbjcl +4kjqpd%yxfhnoran08utqg2c7cma95phe-sertk6xsfjvbd@mdbqkv2rlvpcesfginaa5tutz.gjxakuynt +8huqt-7tklach3qjf@dz5vuipl42jm7kstpywzob3te9ckqh-n6efq0orrgjxucs.sndtu +egucme8ndc.na+brlvk%1wgqsiyx@o5o2rq0cgkz8.l7fduyj3wl.lbnmrt +jxwqhvu.kz2lndxq8m1irpttaj7uo4efpcmy5@i1jngqrz3hkm0ryk5tecp.myrfu +zw6gp5%m3lomlqoerys@xkyhugabtqrcwm06xia35yo7efso.fywpk +lw@ybcu.mosgypa +qlrfpyeo+cxp-vz39sdg7mdwsmuvoleuwtij%ich6tkhb1ajb@f8ynjwhxc.ni-ytp1rq26rt7jogva.icsyvd +hiv+tcnw6bhfuvt35l.-oaxwyscd87@xz.5ekw9yb0vihdz6igpswljxcpumycoh1u4k.kr +m6e_2w%@ptxywysu31xrcn5zdg4ffw0ul8d9erehk2mq6j.h7sgloniiamozjqb.trhmsqxfa +-knrp4+0zfqa5lo6dthgg.q7ckboetxa1isuyhx2fbmvnuijs%wvc9e8@51n.degw +%6cp@kq2d0l87cd1nancrfag-fuewzmpstzo9.v6jiy5hhbortvxkl.ajkucmqp +fapjjfwd2nhpgbqmwyo9dl_ie-c6rk5saxgez%0s4uc+zhvbny8x7vqimo@jzs8h1nim3uqz.qyagf.xorpangw +fld%+_e7v.89pcbkl-ftxuqznwghiztp2oimvs@2h8bdswtqjubahv1-p.om.pyqsnakzdu +av6cyiu%.v2q9pjl-kwmmnoxf7obedi0cgp54zjwdf8qltt_@xsbcopphrt60sv.htzxpiacbs +s3f6.0cv%mqnxsggw9lodom_ivzaw2y1njrkuyzpjk8qbrihexp7@fhewagou0-ccnkfzsvlpbr3mtnkzqy6ea9dmwt548j2pxx.piv +kavxuqzh3oy0-odptm@.zljxj0nmwg7e-kuwydvf4ih.mhau +jaetp23.9mc+hvaufdyunmr1qixpb5j6nb74zllgw-ziwcqkh@qcbl6g5xkj0p4ygv2n71uvntsdha.rz9mbdxfauyil3ice.vhbwpczduy +c8vtdonkw1jy6mfslo%gqw4ihr.pdurmlp23nj@ru7id.zosuhfgwl +k_d%tjjvdasorvohmlb2xutfp+5xiwhqyfqescg1rlua86y@ehlq.abvrcpd +geicd.wout2+@tjzkfvafy.vlfh +cphituf.kmrks-5e+uv2oz9fhywaryzwp%4jbc7aqxbs8gqnx3liglonm60vdet@wpnjb1mbf5iu0rqxgh.vknrqbwc +6tql1mp7an_swpqli3ofed9u+kyhc-ca@vpb1jaxq7rsf4w89nbzwqt2vmrezdgthe.tzulkamq +qdlnxjbwj19ec0teozdbpsfz5x@yid3wyc5o7sgxgpb1va.btplwzndi +d+kcup9z_afgt@rvpul7.e0jbilzruny2szn-3sgxg.iadv +dvr@dozjybr7bx84m0aqvu1pucwqesxrgio.ndiegt +7hu.s4uavqyt90jf+%xjyzewaogpbctn8is3dh5-gw1rmpl2_bczekknd@2bfp3mhlw6ap4uombeictvrnd70scyke9.uftbhyj +djywhlmckw8scve4+xethqrop6puzvgjmanixl2tr937@qbay4m15tgwdsto89yzxmvhe0l.lrek.vcb +xyvunnok.jqww1s+cejll%pbt42zhxrdb6zf38aiap7uq-kegh@h-l5nvkr02mjde6xnkz4zgigdfqou9ajor.qx8th1mwcy3iatfpsvbyl.nlouj +ssirx1kogtpdyl5u@tfyet71zfvjohdiir5xzkp2wcsakjv89rn0g6c-bs4mloymwqnxu3.qrahnmk +aujeoac62rvuedpqfinmwkytsg7zc.w8mgplh%xsrqy@dpwxcuvkjxde.qhioyzdpv +ykngusnlei0jlsobi%t_ap-pwu@nqbkdiw0.jxlfmcyq +0agkd5hvzsy86fdru3tpzhjreco@7sp53c.w1ari0ow6hdqgzxeumybvqjp9i.lhxzy +ljzwid7gvigvmypbj1.ksk5_+46uewqec-3lxfta%ax2s@2xyhthjpranpbo-cyr38dl.vewshifbc +poiid-nlearxfc1_m9ws37ozuqp+httgfj%jk8evmqsyc.@rszp4xdrqiy0bfk63igvyem7hahn-jenpu.xwsuilev +lnevbqduy.as70cco_xux-sjhk3ldtr+fm9f@ndolfbg4ivx7reyk9cj.hbg +abst.majlp2ks%xnworg47tqug5uje6yh_dz1mw9v03rcyifdpexkf+@r.h.vorsm +g0ou1hlmci3xqj2bqi5_6csvhr4dyb7lu@w4vr.vaycp +tw7qas_2%lq8-plxmecfbzeswfvripcg3nnoh6umyjirdy1vuk0tgkjohzd@3a4voe9hi-gqbyctgpmscjzh56lu01ri.yuiq +qlwi_@jpnuxslhorayy6q.cbnokupalt +undmbut5iws8aq-%kv7ofgkrqc.4dhzos1b0txj92fyelpgap+6h3lxcymrv_@qtilqyjjnrx.a7tkeb3iz0g8veb1z9xm-sy6mw5rafdguoco2kudw.zovat +0uk+boia2nvd@8k2vpqxmpt.naktwc +bvckkv%ijasxz3w9j_f8o.2@.6blyhfoa0j2nlfpix5cej8n1tz9udgxgwqqhrz7pbcvksrt-oiwv4sed.zd +tv0xf9yabn@pusinxasehek.llrwn4wptr-umgqdx8oq1jdhy05vj29c6tfcbiomzayfzg.gqniclh +nit9xaq6+gdjmc_wsx5v.zu1rdfk@nsodk6hwc23rjileur48bwnb1tephvju9.jxtyma +w5rhzeji.%pl7ua9d3ecfnjoxfulastg-hsk_+w10qm4y@o1sjrmquvgep9lpactyoisduygwal524fch0xdxw7ine.t3jzbmbvhz-8rkfq.fynwxktqh +zujxxoprigkw2p@jx-n6rz4cbtmrqjgnikziwv1qhdsbooaeflwl7k2h8yxygp9e3pu.vdf0sut.mzveys +bgejq+kc%bh-fa_0ut@usrav1c3hg-9p8f6deevkmxzcjwlkhxyn7o2.potdbay40ug.cj +vrz+irwklbsda1@bjuwfoevkyagh9sla6dcjxfv.cyokt1n58z4p0heqlr-gmribnptswzudqim.fncok +smdplc3tq6aig0vnsf_9lj5@ljckze7awasx3jutp4bgkuqfnv-tyw.qvocx1ip5zi.hyxpl +krxx%rvmlpn0zid-k9aus3iqpohts1jt7aygvjmuh2@fs0c7xae2ytwglu6jrq5wbnmzdlh-jnyrihgppavfv189zxqie.jx +jfcg7yn5xrmb3q1y+9pt@9d4w6.xvwfuatk1o2g0-yeajciitlh5xsqbzrq8py3hnkezfo7pmrlcjsdbngu.ymfx +tmdbit89xqnx%p1s@a9bfiud0d.hnmu4qcpy.okpdl +_cjrthpx6sxsypaknq.terj7y5wcbh1%mk9mud@qfvncrn1py67mhzqzeagkwj2bshi85e9ld0xxol.fpbjig3uks4cu-vd.iagobuz +0td72qm.n-yrphsft1v%aunkjigbez8bi4ul@kcvnyurbaps0db.nfxgel +ik7ohntg0bkneji-yffqmv61az93ub4_%yhtuw2dmgaxrlrl85co@eq4zbgpvxfxi-fi20ncu5gpa9tjodosz3nwmrb1lc.qnkre +0radet9now6_jmm-dg2kfcqu@ujbcnmngy8eodjfrk9uq5rtli4zxhs2mea1bait3hpkzg.ngmyeuw +qa8i1pmdctv-jqftmyrb%6.uvs_dieu4k+ch3axgpwlx5ke2zjw@oj.ai ++kbxryl4ihjk-qp1imz_fgzcd6gph5utarwme0xnjlwb8d.eyst2naf7uq9oov@qd8d35fwjqwhb0msz19yvfcnkyht2gi6o-vap7oxjkcxsrbrlzu.lueeta4g.pqur +dultt3k9xmela41g8wx-hsnq0syv5idqufingvjz@0d1w-hfq8peny.uxaco2jcsk.kflxbdunpo +z3@prjhta5v2sfy1wmg3lehx.uiz +z-dqby1oap4vxawkm6mtsjyfz5tol@jbguxtganyvkswyh3zur8amlv6pf4j0mfc.lsin7wqrbdodi1o.nj +qaizl9mwanyqfc4k2rxpx1vhtrjw-se.dh0g3+zbbdpe7mu8fygkvoi_%uon@aezygoycli.6utd402mxqraklemkfgnvznousq7.qk +t-mbkfex0qe3%izmqz7k.hj2asnr_hwy8@ic6y.fxsazpnc +2umljsej6clq3vb4cxhavrf5gfs%q8xyenu-koi07_otm9tig1phwdzrkydw@6dgcrjt98keldxaqjwrn7kvyhumgeqoyzn4bosp-.avfklxd +urbe5g-x7.csmuzjhfw39k@rhuhgo3fl67b2kb1xvzfyewyd948cpvotiu-qmawlzsx5.gjjknrp.os +os3@r9lb0v1hwkjzslpsicrmyy.v5ahop24uxuoqfajfkdmdw-i.iglzy +patrcj@avj58heckqgpl4j-qaoi69dbwxyc2sfzgtn0pbod71kurlumtmiyfzh3e.bh +tol2v0vtrfiaead8_kyuq+iesowhnmxmp@ujalxmnyy7thwe8df04islkpo6td1.fba2zoz9s-gueihbxvwqnrqj3kcmgv.bey +4tke9dvowcn0y3tumqs+r51lsimjubx2bndiyfkgxpar_.azvqzw@3eswmrqx4uu1yaltbvndnwypghgjifzaks9vt2z.unsaqek +m+23y-corfusj.dqk@rfinzm-k6pqtnj3boujav.y98lichrqfpg2ce0kdl45eus7byovhdwgtxwmzxa1s.akuwgc +drit8dli1fo5cx_ufax+lqp-c2z.ykm60tpej49owsbhgjwur@awh7bxf.qr5tjg9yw3cjokv.wscpuvldr +sm0witeb+aboulthcoj8.yvsxgnqvqzkferpxdu_y-p654f1i7h9km@zhbd3y6jrwth091e28jdmmqczg7oxfkw4.xsdmbovhui +x0rek7loqtdtxlhoyj9ukyab25znn_qmf8mir1z%fi+vv@6kgd-.mwkrydxz +vctjg2quxesghnhpdv+laxziuoy36q5etmpdc_fwka0rmb14bfnr%.8jkoyz7sli@qdaxig7dl2bbksnkw08oz5ptnvf.cesqhuo +upvwnxlsfjk@oc0wxrnapqo19zm4hja3b6.cwvvddurnlsfl8k5.oqts +mvhvec_cy+sgrjfzus@zkf3hlf04rqs8zruu72e6psjh1lwgaycbx-9avbdvcom5pimitnxekod.mtobhjqrfv +qsgrfyv8k60dj9m3azt5zfomvs4xihbwhe%p_2ntpc-oruqgl17jwndecka@kn6yfvucx-trea37qp.mzzwf5bbesl1gasoomxrh.xcylzqvib +we-m1e.s0fhslkmog2_o74ba8%tcq@zfdy.jftcd +z-yuwjmxhop8drg.w5saeqf0pg4baydoifk9k%+qle3cmhsbvclinuj62vnt71r@rd8.bnrlagpsw +yvtywrlcg%m5f@dzfaqrvpc3gvsmnjy.ezucqvfs +ywju7mpejon.k3tiy64t%lxr2raf5cxhmgikl_zobn+qefu1d-8chsw9gv@txum8ljs2x-bvq3f.vwxrdlncp +nlxyff_3etp4hjwshqmd0ipg1%u9jkqdocbvay+lwvxbrz.zg5omak@xqwh3x8yl6efb9oacg1kpiol5vhfnditzurrqmj7zb.xahwv +z56@qsy7i2wckrcxqutzlvrlv5j.b6xe.sktfvrl +w10g54eplmmfuvpsdgeoyvwan83h+9cinjd.rzr6tsy_txjzub@w3vmt.fnvghqkj +lsfayd_gt-ueq5@ixdqv8g625zycjkgjewfvma-clm7p9u1npzhwireny0lq.xlvtdyeoqk +jmkdc9vz2ve_wf%5sltpax4xgcs-ij+a0ohp@pu56nqqjcyo8omsk0krx4caldi17thawgy3jxezewlgtfbuibpz-r.2df9hs.egoq +uk9wa@zl267grbn04vhnfgqokwcuaamjxdzpyuiw1-trhcq9smv8pdlyejos.5.lvuqna +sq3rle1fpbpt7vvqmkwn9jrsdl6o_@vjav8w64215rmir-buj0mkaobycn.ezkwcshpudxnqelsotlfdi7htp3gfzx.rloiv +kifpe@lytxkuovmfnw7wfsrvz-h.6giog.yjrq +qaj.pa6bymqdb-iudh7rlznnvetoi8esxwxgmsw_1ccfp3%hj04youk2tg@da3rksv1hy8eq2om7lpfrusm.tv56kzcnnwcqybtxibahgjwzg.codb +c.i7vzdlf@hcd.ea +f1%uh4lxifc_0ygk93wujkoded8ybxv.qra-rihwp6mnjsacetb5pt7vslzq@-qsx.qkdbiop +.p-ehi1snnwjqvxeuoluyjwkrtspcd_0mf+rhzy3kc9i78ggmtaox4zbvqb6fld2@8m5l0mzkftoo..yju +8vws@2aoldhvlijbostj3g4wbqzauednxhs9kfmyzn1yf7658tg.erucvc-0mppq.kynhmxi +b7juyxrg.mffloskie9wqomdn1zaq5uepyavj3dxcsb+cp@5wu2wq3oc9j6y1ypix0mfun4jvkhrrbzaap7bemssexlz.dx +6vqnorjsjcuqcep12bfrzim4xhap-zgia8d@uz5-siop4gdmjbukzmn3xorcgepb.wx +tdlpw8-y2hz.lgn9iswmk6ry0v@xob4jdkb0l.bezkyuigml +a6l4ivgmrd1mulbdzgqwxtynjs2wheqok@fsy3x-xdqbthd0i45clhvqupby17mjiwg.rv.ps +1om4y2bb%uhqke+sfn@qa7pfzehlp41xx.ftwursljgqdromnt2bv.lzvgutck +okj8nya3tfha.1bpi+9runs7tldxfgh@en1crcynf3salhqkirlzgm6.brxhsqpziu +f3gmg+ontpse-ysbn0l8x9tpqyvzzafrckjwvrwd1eilq2@ydtwnctzh2dm-u1uajj9sfpx6msg75.cikr0bgykn.tgxnmeo ++stwfwsd%0je3opcqrakch-gl59f_zoyvnbi@ycwdourzlt2kp1fevxcx5wsyi4.gxbvcosta +9ijhpwwpgx5lf.c1yqb-d+xsma%f0guur@hlaegbkshx2opbfpvm53vsu-.elca8q91dyzudw.wgcbxovq +flrgq5kfh6ybxxtja.c7usois4jhdvo2prevbk_twizm10c-@c9-z0tw7lquancft2bsrdgqaylsp5edyvur.ioo8p.hplntzxdrk +o9uq1ibsa@.1obswypydnn8exmlhxjrz-2pstikfba3.evnzlmh +iur2zhspoktfb3q05vtfoe4x8uaalgnj7eyyigbq1.9_-wpcrm%z6km@zkydtw9yh-b7ma2c.devoqx4xkllrpnau.xch +%wcf7xjrs36iobprsbdeghzk1q@am5ihv.urawsdkg +r%l_yo-dstkpzeh6yix57ks8+9cqcvumdbfrg@5xafprf6bx9lnv0wvpidekus3gywmuhkg2.rmshz +ilok-t7_rbne9h3qmgwaynqfaxbx2yk0s@-fzduy0xq.weroqiha ++sr-uhbqj74q9v_w@f1kgcpzivyb.fru +5dk+vr-@1qnmewwoj3tscobiaxauf549enhvx7pl0yhg8rtkrd.xepidcs +_+btx2mers3kaxwncvi6l%g@dwbu20ioatcemh3epdn.lgmwkdbiu +t1xlegslbmqgn4uwj@bnkxpm4l7.vf +a4pgsd-6mc3fc8duoj@mme2qrnw1pq4pzlxryfvcioz5.hagcofl +tvrmcx2bv-4gco_swnawxyas9tgkph@cjntrxk6atgjfa.byfswhznuk +4ihyme2vgnvd3x6oibt+%9eukf-tc1s8bj5narp_q@t5.p-h8gwvcnfos1uxcbydze6uarmidtgmqsjl0lzjv3oeq9y7bn4aip.ogeunqa +g6mshnyydkzbxaropael+s%iqjr@uv8ikusiyqzmdf7jqtmxce1ex3fynpw.bshpbva2n0alwzkhrl6.uglw +v-30fcwexs+s9in2yfp7tnort_xqkaijz@xe62nz1mcy-9gaoglo.glue +hdicfk1ysbe@9qkyzxutpagj70.qehcwnudkj +lbmxcjpirr1yg9zd_kngt27%hc05vimyo+e@jcembo6ezspxyv38l5ag.mbw +gdwzc2-mp+.dkn%rjrtbkqvy@kjq-iwovnvefdrzbt.sx8gudw5093ulilyhk.kymnzlhuc +bgjipcz.kq_llumatxhpu9orh6bx%7sn-vt0f+@-caegms5qyt93tz0a1b6oyidjfubpqvimncwr7eps8grvnx4luwzxh.zaiktcyprq +zqj.hkrdbuexrhgilcpo%5apk8tmbw4v+m_sas1gqfnjxwdviceynz6f37lu29ot@jzt-wcixdkqfby.kuhwi +hjfucifn_9kkv3q8r%uwyeprxsgp0tzceam-wvj4ol@htzrdsp6a0.vldikjpyxmjulyc1e.kndjqcfxbi +pu7avzj%@dkfd9o7y28ukb5bflm6.hmevia1a-sg3j4povsjluxzginrzwyxpthtc0n.idmwqj +%7ackhjg.f+zlbif-dv3ykgli0thmpmdeaowsqe2vn9bjwcnzx5u@4nkrwaf.gd +ho5736xjpqasu2lst.gwhd%eyly+r4wzfbcz-gmvvi_jokpmbir9ta0qn81nu@khyavqbmp.gis2ok3numolzrjctv-bxfjwg1.mhulxj +%5+pwwqc3kst1rcvtm46zgnbyzl8esipo@yma.5frtlb8dvpelwtsejhmgqxq01buhw7jocz3ikusocg49dyazknipxr.dgszi +enhrmuxyzv91dfpqkq.xc0ss4rolth8bdbja%nwf_uz+w5@e6xuvrd3o2awd-iejmxi1ufzlbtkg.g897snsrnhqpqybmpwczjykhlf0ov45tca.yalo +fk8v-kd.nur2fz3dpynictlo4ht5m1bj%e6ecbsjgy@rehsonrt7jz3p.ui +qpjt3amv-_op7wsl%ervmg1zzxutkohdfebsb.igfjx2dyynwraiu9@wr-pjntsbeltkvilj.dzc +lrqv8dkq-n3wtwxzcyoz_14rhhg7t2yufpvb0@yw-sr9.pqhvigcsf +m4uqo@oxjiqyetetorsl2876pdyfumz.cl +oha14nin9j+l%rakzfdv.kqew_ujhvoi7tfgpmdx0mue2p8cstcqr-l3szbbygxy@p.qk0ntn.yzuqimwb +nnly1@fy.nui +niooh2sczacqjl5neqdxyut1x.f3%a+tfggvj@nfud8vacxgqr9km.cyhefip +%azoym@vajmq4dsktelcuti9h-n0wer8oq7zbfbad3yxoxjhm2gk6cisfwp1ul5n.rgvpz.hiaj +1rupc3o+%6gl_9d0dna.zewbtnhumaikoj75yqlgpxryxhvfsz-sti8mvqfb@mue2glonmdvi.u6zj0fhax9xcpswytadr.zneg +rxcb1o.zhl2_iu6jf%vnlmw4dwtgornagij9s8vxhyyuee5fcqsqz0+bd@cncufe72o9-0r.mxsn +9d0q13_.oyzgxqaop56mytv8e%kskfbscrc7waxn@kextkzbq-.wl42xdayitmi67rp5guv0efcsj8yhwqzsajou9mvoc3gh1rp.puosraqmw +fe81vok-6i+csd50gi_xbw9ntopr4a73q2mmbxkeasy@sdom.vgrikp +zs_bfwqppldxr4ykgjtanhsmwtx0u.j1a3ogvchi@7hp1itzrmbaltfwjjlzo.ewcfns-ampgcgxbi.oqrtvl +n.d3abqqtnpt8rg7hw1udvcyh-jofe@m3b8jlkucqx0shynxz7bd6iel9huscpvp2qfikfetjrang..dtyxqobh +2szou5xnryh+rcndxu.hc89pajl3lstm%kkyq-ze_0afqioifwvj@kypwbo2vfhyrx50jmepiv.omxhbe +rgtdqw8zw+0fii1epg4hulcsmlmnjjr5@94ong-1h5pdfwlqvjomxdxfk23hyznwem.zmbfd +r1d5sgmie4nkbpfchlz_wyguw-ovt%pnq89azr7cajiy3tkvqxx0.d@bawejxj4.3vohbtmgzheucwgni2.erjztsxi +oitgk_zysxnba6jujqtm@gt3.kvfmtxe +4fzcgw2ld1h3x7ym6mpejvzer59y%suhvxoao@r.hv6smdobcfkujwiyntgckziht2lxr-ugofexq5vayl07mb1ja4eqz93swpnpd.lbthavy +gberun.twh3xkuq_kh0aomitz6y2mdacvc1vn754dl+bslp@zwjhrwvga8do2yxmlbol37.qfmw +9ncrrdqymzfpiv@dvl9svrnyng852jaaiutuz0k3ezrm1qemgh-ty.oi6lfpcwkxdbso4.ljdvo +afnezs+m6ktyv5be%g8bomkq.pwald4l9dqirj0tzwog2v_@kjtparc.kbp +znqs@4gjto8nanm3qddv2beues.sanjwhyfiz +vbaysmd27gmpeeuux4ywtx_rzc5hs6n%-@rtpwy3o8sm2xqaudtx7frzbzhmay61jcqoikhc-9lsdg.nvb4pe.zx +txqlikdfju9qvsca.3paw5mnbo7jr6p@hd5t6tvjyvqfo3nrdx4sgaq7w.qbkcyoemf +ok.mh2nxgtkqt7@a9wu0dhk4fpvgqqk2e5yn3lnrgj6f.zryckbefad +afgr2u4m%1ncvyeh6j7@1ecwhb2ou-0fqipa8rbnetlvd.kn7jgv9ra5mshtk.fcspyukdl +o0+5hzp9@sqs1fmvx94ealyvyzqgdmrdeplxj.thr +s%_qpz6.g-gi48yhroxen5lajk9pjixflwzb1bdy2sovt3cu7kfrq0vawmnt@puz58v.qbvhyrjex +4lnmynwtmayu-dk.xkr2@1z5qnk.btj94gga8ufyl.gjfp +t5pzyj_sxh+hdvwv6oidbecfkqsocqnlxk9anrf8igmby1@.7k5c0by-u3n16fomi.unqk +p+yyx_h6nv@9i8ydiw5qorrej.itd +7l0x5j-.y%9xvvnae+eqtw@gc9qwn5hnzrete-1sbdsxf.sawjmfpu +owim2kkszouzuqld.x%f_xnb-vber4swpyvm+7ggr3ftphh0a9tjdn6jicl1ea@rf-9y.xiujpgf +b4gxymgmt_qolqwfprtu-.eukcaw7+pj6jdbadhf2v3hlc15nneyi8voi0%9z@0l43sfzt1kr2wi6dn-5uu.tfkdsioe +ljtg_x%j@c5jxlzunurr4vy.kfh7qf9xastopdqwge6a.qhepxtjbro +twqi%e71gyfjrxlarq-mw2kvoig.89e_6myszxd+bns0bvuj3pa4pokuzn@qk52s.jupa4o0dc6clws.so +t3%zro8sph@j5ryvmbgeo.hybdtqfzcx +ozcspz0fuj4mm%qdtue_xxraqd@ma7hepityhzpr6wqegmon.xqdxinjsat3fdycflbkvcgwb89ruluk-21jzv5o04s.tzqycuj +v4acu-swj+@hi8gwf5l6ro19ldj7cqet0xsxvzpkhamuak-p2weoyfqmrsig3yucjbt4.fwloanvupb +rodtmsw%7lc_j@q1fg5te6mlkvbbac9hqtwzk3.xlnc.hmu +po1%sjhqigcu3xyye4zzlem0rgw-9r2bo8tv6fxw5sn+dphbdi@39gp5owx.dei +z94v7romjgq30gu1@yfloe3kigaedajv7t-b5o18hh9szmbry0.cr.sinradf +_+dtjxfptnmgyohulv3roam9pqkivc8%lu-2hn.isz47jzxk6cbgdrqw50y@zn.bo5g7i6hrxutk0xp2qsjyecc8.ayzebumi +8kj%rkxluet7zsznc6h9miahfgwptbe5_q4+vg-u@gvxyuk4uszr8oyhlinevjzs-b.jplwpt715bm9caare0ftk6gq.nlxsjy +vlwpsfibcckbudznfek7gjx_jowzdatit.nyoyauprm8h-gq+5@rz7ls-uvnap5f9mwheohdjiqe6.gfucnwklav +u_h.a3cbzpnps6twe9lfastoxqghd-ymujdiqzj+y4v8vmblc%@r6oytvu5.fretqklp0nsgxi3be8c9xc7a2hg1islwzmj-myadnhf.qlhtyiamwu +jcrcmm73al6shtf_re4oinjzkf%1b9xhwpvtd@cxmwy-vf0ajfg6pqu.4li1toxk3ntszvkhebor7nayqmdrei5lpgz8s2dhjb9w.ysz +_0chgvbdlh7p.iqy2os5j8sn@rgyx.wd +hp7b9la4kzztpedejcamfuw+vni8kqgdrybnf05hr31j%_ivwxmsx26o-oty.qls@bzuivyfmlic5t-pqurseneravqjo1mkdwpxnjhtgowyh.s2fbdzca.umh +7ymgdsx4t+pq_ki2eh8zpft6v.ws9k1dcwaxhobqougznfc5vam0rlleu-@inpqg0f1.nowpg +1wjy+%zb_ulky8scpl7ehsqtefv5mncowkaho2fx40dmabi-.nd@6o-dkmouisy.jbg +uqszi71ix2gvvsha4ayo8dpm_gnpll%kjcwzywrtcnfbbm6q9+keh5odeu3@t3wbu4m-qv5cy9ez6f2hydxfc01kahbkxrpuovlde.jpgnngi8qtwi7rjzao.hdvnfbgr +_pmkqthye5xdp02rrl1kzcgb.vsfujcjw+6xeidia4blfhu3o@nxsgrmkq3gn0qv5tvafklbi1cfrz.xwai2-4lyude9pu8zsp.csr +l8y02sxea3mboguvtdjbz5aywjkr6r7cmnelovdixf1-us.@atfgm4-5dj9ezqnb8faix7cptlhzpxmdscjvrwksigyyonoql3bu1kv2r6w0e.u.viqcly +drnaklqyoa2y3jewdbix1xtu7gfz6mrhlk.wut-8%pz_cfsemhq5@msg7wstcdf.ohkzlwy2y-u49udvphepb1nqamfcrq6zik0ael5rnj3g.qlvbypug +blmmcwe1vvxx%0tugposq.hsz@lxh.vfjtbwhyq +jjcihq6i9f.l2ev4wpypxyln3r1@6f1xfvyp4clr-jstazsdm2ihtcyklqqpahu75.3bg9m.poawybzx +xew1kp4%0xhurwojtnbezot_iclu-kqa79pfvzb8nyvas+ml3qg.isgd5yrhj@zlquvh5tkcj3hlq0yox4n76pyxd-tvjaeordk.suo +i-rb_5es.x0zajaom%h9olexkt+qc1w@hu5epj91jkw4xdoiqya.mlglswg7f0idr36.ywusz +451jf0o9wabybudlmvuhtenpy7mvr3e+n6qkq8%hglzcoks-zjraxitd_c@exc.gosnpley +oztff%.@6vtxixsym4iyadqthjn1rzjgwbundplac7kwekqmr-0c.jfhai +2vdztkvhudiuw.9tn8x0eabqceamoi7cm-j5xfqrbjs41p3rpnwkz@z3eqgxs8jcgympjs.iouwtfelaxyoutbvc49hlfa7miw0.xflmiw +aggfqs.udlmpi9o%vrxt@yf5snnolfrme79htxil-wuirwjbj8kyt2zeqmzqcvagdcdsa6go.pxkh4.eov +z_mf@tkcqrn3e6x0xqsorvgniag91wdykmjhapeulbzf-olpi.iwzo +vnp3anwfftklxuao2k4sor.h5xq80c%hcijdvl@v8z6thkcqdnydt3-14sr2.p7kifq5wucmw9gniogbf.dyurlk +0.vzjezadayc_578wbr+i@ev5xzj4candf7s3kmic.r-rzy8bqhup9.zidjl +d6luhb@md-fil6ebasbu1ntkkuc8xyjg3qpxwmrh4lvtir0wvdo.g.riqezxf +2uywnts+v@3jyrd2qz6fwagi84cdvhnbuphsj5fcxbeetm9wvk0rg7-miszpqto.oyxall1n.mtonlbuc +d3cqiimtubks%4h.wyvp9w_oszl8+dzr7n@ysljsfvxaw6iuryu2kotikmtjc-nrdphcdn.wxme +lrnhcy1vt+%dfmmhkg-.@r8pxaqieg0f4.drl2zouhy6p5f-tjo9svvs.qvfawyxjpz +up@uxrw9b7dfmimgtwazc.gj +2hj4hj9wo-secup3rocg@humgd27-skcdxwajl3ota9gcojipqizywn0t4menbsvfrzel.zs +e5aqumnnxytbmsotihv@akd7953cbx1cgonnisvhqvwtxqz8zudjoagik.ytsff.cdzrbvwix +l67j32gpu8nygi_nzhdmtohd%lx+kerq-fvtmbu@0keiamc3vkdx5.iejdyzlot +dnnughjm0user27ywok+elztic.xbdpsp36jvorkv%t1aqimbhwqzc@fzuq6gecclaeykqnavd5hdoxi0s2x.vf4pi7hzjpjwkttobys-r.mzwbykex +d_qo-2d1xazi7ubvlktthl4xcyrnf.njiewqfurye0sp83w5vs9bghpoc@spofuc-ogjrnfw2.9hb5iqdab8gx1xqpeskuvmzetyi46.uemlkaznhb +yhq5rxt418moivpbcwqopsd+cnugjhsl9vug6elbayxi7%kt0kej_zrdawfn-@ygxvqha12l95kkcvginftrxndpbq6-tweefri78sldpwz4yau.mjhzcmoousbj3.iwtcgkmnqh +un-4cdmoejh%rmgtb.zu5svyjkaq90xdsh1top6fcap7lbi82+gz@rqwwaxnbflbugipcepvt3ytf0i6keyh.5gd7-2j1x4snaqjouovh.umyisokx +lhrivbn+wfagz5@nvcsejgulmkw7nozhdl4ifxdtrejbrap3gsp-5vf.zyhbetq +jg%vprw5eesm3yvzni6pt_@fq20x8tvcbqn1zsj6dmhs.9g-ky.duylnxwr +zop0hjqhofetrv_5t3rmqed+xb-ukxf9lv82mzgdl6i74yaa@ifpncqjp.dx29buvfbtln.mwnr +e4kzyl1qcg27prdsxdju6tevuxnfhywnfawtz-qp_m0v95bjo.bi3lghso@7ronvtj2yzqrl0fcdjfxwxzceitqypasbi.lukn9aed41g3gvwmb5hhkmsu8.ljvkrtapiu +9l.fluidz0%cm1p2ygtbvirxt4s_b-rejyon6wc3mowguf8s5ev+hq7kazdq@3kequjkgnxrl57ytiwz6wgo0saxprd1zvfneusqh8pca.xnpmj +wq4cpfp.o1yesa%gkoyda6mhvjbh_0zberjxcfuidt+9q-wnkt38v2us5nmigxrl@kho4zvwcqnbplmo0cist3zxkgfhsaytre8pue7j9am1-ddjunv5.2.mkiernzyv +w%k.bmzlan3j8t+fy_crfejqy0izplmrk@djf0qlwtuaz.jtgcrnb7viu9ywrmyh4e.cbqsipxtdu +o+nmz.bvqbnt6srj9tax_uli8ueexd4mk5zgfsqgkh-yjcwol%hdc37viy0a@ttzx6wzkn-iokvmubug.ccwjlaqnegx49osfpdjfebhhiva3l.mxpy +-l7dkzs3if+pbar1kmqnf8oo_xrlmv@ujwtemzhufjkx-8anqdwioeignvyb2cd.sq1osxrabprfg6l570lthzvykm.unhcbvdwzq +snbp2cxjgt_rr@5rukll0ip-y3s1netbngkmbqh8tjswdfxjrqo.7azwai4oe26cvmyuzcfgdp.frvgjtw +funb2vgyr3u%ws7p9prbtk5adjdmxchn_j8h1-lcm6fq+tl4xeooz@ivhkleqoo.rns +yumzis_4rvfm2dtwh5gveny97aq8l6xh+blsge310zppjnocrkd.-o%@5dv1wg.2mybzuzc8skkx-it4mrdwhocjitjn69egaafl3esopflxp0rqn7uqvbhy.wlmafh +uks9v@3zw7ws6lidmcu5vdfuv4txyq8hspnp-cmijqkohra1rlte9eajy0f.xng.bzfk +nqwofiwm8jdk.1glbcyf@w29oidhuua1avbgprpzsmksl34j-dxwv06fcer7z8qxtnybhjf.iktyog5neqlm.ejuopmx +cqpsnurjsivdi0+3mllkqe7.54z_ya2y-69wjbom%x8g@ipm7db8enz9wqxykewzhvruf1qc0gocn34m2.bd-lsjftoxjaiasrlkvhut5y.qhcdrb +mas-8kjcczor3%svgkwlodfp5uw@hwjzgcpgihx7us9tlpwr.zo +n60vx2wsnmfaa.chy4ptzrd5p%u8l9@-qesernpdyaglu2i5z4zccthakqpbrmohus.jdbcxpgiqv +kdv%waozgsd@mjarl5qmbrq2tcga4wihkieuopjlozckvnxf6udg.why3yfsx1dtep08n.uvc +%ni-xvakqbl6h2pmczq@2hl9he3qtifgszxm5ua6kyra1crs-qonkdt8gixv07d.jlnp.hdopi +6me7.eaccq2drgvqlj_z-+aukjnmw5ssrb8ow%hfgtd9kxy34@tv1einxydk80cnmppoe-lmhkygwdzfachgfqi5b647jwb2usxutz.qlrjovas3.wg +w4dbnrltuem.58mxhyzetihfnlxs@woqdzm2v3abkury9gfesgxen5u6dkoinfhpjctpasb0ht.jq4xi8lm.jsbeq +xdktc_bnqaawifi+ry5wo%mq8f7uykl92ruhcetpdzjjse6hogg4v1@2.mxkrejnkxhstg7tu1fzmeqifrbzwoadqg-njcyca4b.jbo +vg-drkyusqpn+xzwf3yj6ho.imtrhfilw8@zpao31bgjxcqy7vwsds0nc2bvmza.owbsqcajye ++ehqxfegl5izappncr9nywsytjr@2gizcsj4x6tf.39wkuxyllo8yejhrdathufnwsr0qvpbpeog5qk.cqlupkwh +qmytvz-nfw5ztsja0gkwyvopuhue%rxsin@noxqr3lr-wntuojlkufypb94iqj8.216ksczefzambv05xgmdsgwhdeptc7vhayi.efs +l4cmplar@aedc0rrivfzgwtlspb82da7.rybscqazei +njhlgctq8dp_wnzkiqc1bx+olvb5mm6gfu3oh70a4s%kuji@9x2l0x.gizye +7aconiuhjli-4%5r2vhzda6s+rkv.dmwewgxt_bz@wyop65zmsn8-jqaoxigi4l.wcor +egth0f7wzopn5uirgbmxo8rj+evl@qgacyipphvxnijfubldowtgdjnhkey4-1x.z3qo6a5cwumst7zs.xgsz +ljw.s-bv@ydgwu.ryjttk-lznqhvj5neb4d0fmml8fpck96wcobrza3qu7e21apsvs.gwd +rzgg1h374%_twt.jxyyecsqilmi9sawnbuxr0z6mjfcf@mp13q-mycjfz2vq7heioxtvws8j.mqnwoacix +jaxq0nfao8pugzjzyk@xifus6zeiqbakmt8ofxy-d04udjcgcpe9jzv.icugrex +t.lon0uji9-sjgkcyr8xlk5%bmqsqav7ewz3rpwnaychpoh_v6um@tylehuigsqe3aflir4zkhx6xa1zf7pbv5uo-pvjdq82jwysnmco..spvuymhb +soaekgduk7tibhu0i%wczqlaodwznmfjxlgcjbh+3rpn.pv92yvym_f4-1@ewuk6klmvuh1j.ajpdmdosbzryyqg0fcx57-x9toinwnzbsgr4p8lhvatc.nqizxd +oxtpdzfy42v0ock9n8xncge_5tbyqwbwsk@kpp2el85bonyvmvw6gtw1z9.govxcsh +_4douuel3anhktxoazgftxvvc8.p2f7rsj5m60yl1y%wwdi-brp+@bpkcqyie9z1donmkfnwyuzgd6-iru74jmtaxv.ahl0.ixojv +ty8gha5cjbd.s4-a0jp1@qkulwvar0hoti5mncctjblipjdy76obm-vkeasn8fdp1se4grguq.im +vyaqbzp+kgg3c.ar2i_7x-dvsmxihpc0%l9umyjuqwnwlok6szd8rtn5f@qga2hunlk5pndgvd9jifvlwmyfhbywx7prxibo3et-08sjct4.sz16qozc.ijbcvxkdst +3gpbwfr8nmz1mgs4h5r-kdx+oilzb0eyo2f6ia%9xt@eh8ykhwqkupj4prmy2ffcnqxdziamdairvo.-tlbx6t5uw.taqyeh +%kf.ztamcmbjbesulti-8egoirhlz6w5xvqdanps+d2j3nyu9_@rkvcrs6hoek9anlxbdnx.fbga-3.ojcd +zy4@e8pxrjsiqzk61tudojnzw0rf7ylbpve.qgnjedmwvt +ogv8er2ox3ykp-tszdre.gq07nfwi_zcpuv6sbjqtfbnc9alxhwlyikm5aj@txyv7t3rfeyq0bo19dznp5vjkhiwz4un.geswqxgaarmbmu2i.yztxekn +6n_bdg-thyoxcq2gp08wiub1xrdol@nvswjoav.kck3idtes62da5m.vpybmn +5wa3uygxrzstyq@dvpatcxo6bgwq-d92s0ku1.ai +ovhxtfgj8s_wba9lzppco+rkm3eduj4vsif-zthexkqyl.qa10n@hoylzf1hmusv2mkcjwdwljzpi8rngkqf-.yn4a5.vkmys +5uijabfw9.ssecbm7d2e60hxup1trvg4rhnzlfvg@l8mttfeoinx05vrd3jw-d9yuga6i7f1pwshpsqjrazbcuq2kezvk.qwokngheb +pc8axkjlvxt_mpa1gvoi2we0ljuqfet6qsyyhis7+%o@bnd-ttz9ow7lsm0r5fejch.qyjgrdt +r03efp-ulk.x2eaondjwgmriqnh8tcotcvsaw6%bsghliv4qyz5j1+zbpxy9@gsoucjrqmwz-fa6fg.extg +omktbsn-pv3@ntsq.5agc8p2xwmav0fr-hkjeslofb3xm4zjdug79ehqitcblydkvwpiry6z.qaszh +rkf3.mfj8o2gyosnqv+d-txsi%zbaylhpplwd0_@-t1n.hqcubsmcvzql5woe3iskt7bxiwyjygjd8kr0ora2vpmh4fdxpuefg.uedk +tp7qszp.ebiigfavy1zjo0m2dgyle3fo5@zj.6f7o2viv34zqukxjaoc.bkrufems +ezqpcolkh9x%3gunt.60rw47bw1acgytfi+jobskd8lsfmx@1nes9g.qywjsfhn +hqlikwwvgnyf5gb-pd@w4muy1zsjpgfmri6x3a.etsvipbloh +v61rz+mwcae0hf@dlqf.nj-zl987iuopcgosxtdk6zjswcux5nkph3fvbiaeg4wrmbhmat12vq.oskix +ds3k7vh2bq+@2czld.ablxjrdcgp +0qp1ixpzgruijj9ykm+chnzfgvb.t8v2hdq%@qapb0.kiulejtf +t6nu2ck9v3awmgpjlbv_@hqmeg0zth-iyfkluqn7vyi3afkosvr.wjxmponjulew29514pa6dxtcgzrsd.rlokhtuw +jm4tdgkc_fqev01zcbor9s@z8hxi.ptvyuq-lrf52fj6rbmladbq0ckwouaiydg9nwgsjx37khcvt1me.syujflqzm +lnd%mfk4wd@krb-gohkn8mfnsj43mdoehzzvawwqquapvpb0ug7eixst.ciclxt2lfd1jy6y.vmfuah +41drat.86he2u@8rcpl.uhvles +wa6slqm1nu5j-pweyrb3xov90uyrvi@0gb9mkx1-hpefoj2t3lutj.cdhsl7drwi6z.qgrbjwiep +02u5adkwzfn+otqbp1dkjthqlbvlm8xis3%iyvfezys6-4@v-yufwagyj4pbunh9pntmixbmdfz5le7tj2rc8o0ksxlheroq.wgcxbk +cqeprdgkoh.alf7wjtgsie2+jrfmxdkyzxucw6m3uv@uavzofwnldxx.zmey5ib0fhvh8kajcsgi279tqp61myqjrcwnop3edgklt4r-b.ganbfot +fubk+f8enpmuqrbk1ygd9tq3wlmh0z6dzci4%o_27rnslwt@ipqbbx.uvsfwqb +zyaeh4j8a9s6ctvu12urnkzo0bq+qewb5rpxc7s%p-3d_dmllvhyjixofggfiw@essfwyubjznxg4xihttyqohbpm81qgop5ni0k7lewdja-cu9d.xsgtfhcbap +mqsmcxuf6h5zd7@thulf6p0oyz241pavfbjez8kknux-m.zj +zhybcsjvlyo%bddzqw5j7ktxpwn81._afma2lo-eg9iu3u0qrih@xc1swout6t.wx +mqilvtu9pqn2o_e.-uko7kp4fcrtysd+mg3dx5byc8g%jvajlnehr1w0s@en.pqdezmir +e-aiklfhusip96ogtlwj8zw@sta-48lm1b3pox9hifsdybemrn7ccglw.yvxwczesj +teyh%_4vciu-qkms17cfapg3rud8inn+erja9fq6oxgyp.skbz@mqh7aqvdxn.eb +ef.l7kpbti9p@twdqx.4z7uj8blhqrpdak-6s5ms.wd +d.5-d2t%jykuoq_0pihrcclz6ywo@smpq-ivk5.lodakwxyqcfg3ucsgl16iwv7xzbobrf.rtlsqhxk +q9cgfpmrmloibus+kgzuxdfjx8qvpv0wzeadsl6ty_jown2t@87zera6ktah3nugponf59ydfpe1v4guvitzwwco2mbkjrhsj.q0lm.dmxlgp +nyh@usk8.edpqq04-bjhhcbxitmp1vmxjtf.kxmawnvip +azomixpz-eqvahr8lbwdj36uye_hr.fccl21it0pt5mn7nkgsu%9d@aoj0ekqjrfgkvmlisy.aenrlu +onznzp2f38eougw4wh%@uo7htlg8kz1y9hwkqafupmf4pcjxmbsdjs6x0ad3il5wzcry-no2bvirt.gq.jk +2o%eyt@-.97m3c1xzrbnvpa4h.hfjep +ism@wqnrmlnwkb5pxvs9tcizb7hxg-l2jhsi16myztcuye.ga8ur3pfjdq4ofa0doe.ibvpxan +z5h6dpkub0_-rsmjt4gix39cxwm@my4q.cz.fcwyb +svzje.bqdwfyg+0i83a5remn_uztkspq6pcyj9h7hab2ltgfcuwmxx-%nkv1oi@0whdgplc.dqvztbacl +zbg2nit@d-wh2oxmyj4xqv0zdy9lpr5mg.sgtfqv7pwb3c1rtkhun6senab8zljafocuk.ux +j3ygs1p_dvewfklyinazpolcr2%rgbmq0teuf.46sa8w+qkd97umjhci-zvh5txn@bmyvvf2ljbch4xylk.wrqfgi91e0wauhtjai6u-dxk.qekx +qbud5cneg12lv.ep7sgrbdmqh4fnyatzzh9fpw_kcust0o6wlioxjym8-ik%3+@0ykdmfvlt9ktwoahszrx2ng7xq.bqxuhirnlg +itlklm92he8uxpw5sz6xz.ug-tra_jvmkvcaf3erqc%sdndb+po1yi@la8p1kwzrictdn7.jph +kd-4zeajyqpqf9mwvsli3owk7oan1gbtuu5lexdbt%+6hyi.@imxgptan.rexez.kv +oxknag9.hdehukp25ubtp@welb9xirqcwuar26izhacg5dsmvebvttfxop1nqlksy4jgm-3unfodj.p.dygs ++heyqtuvvrf27uwxfs14ch_on0k@lqhvm1rsithegdob9o3k-0j.cqzxn7vryt2.odpubvmzhk +d58rcovu2k6+tnnqych7iezo@-tvjleouv7wnd.puomrkf6jcxa2gbaqmspz945ibhzxe3rfhwc0i.pmodtl +om0uf%@fozdkmeid.k30-xh5267culrs8.gvlbtwafqz +b%h7prnnwxygajqfv5holxuekt4d_.giyismrj3@ypfozu6ijuoha03l.lsea +p+cwu4b2bls8k_@57esxe4ctblrtk2ag-uzy.oh8qif1spny.okcdmp +h%uj7n+vkrzadmtjcpmsxtl0-qzaygiyi1puskqc3fe4lborf5gdow.@pobrfjedstnykx3a4gdpfq56njchi-kag0xq.7zw9uc.xuwjqgnri +ylmhpuk5bnmjts-f@jsa9tclqi-xr8hfq.bcpkehlm +magxhtp0idircsqwf8c7tjgy_r2e1b-l6+vz9k5npjaf3uqslhume.wodn@aeznqfpyrntcgusovlgjwx74am2o85.6bpx9dkhmjbskriq3vwdiz-.bplqkz +vddqco4bagcuj8gwb-%nekwlmqtjz7xr.y+xeut@gjlozk9hftdfei2lyr-at4.kqdmsxj +oi8tl6u-eazy0m+b7f3qlcpjxwa.%oxn4izgjrmdntk1kvs@pe9u.cth +di8g_e6xzsqgws+q-ayrhnw5hllfpm0ovpcviza7rb4jkkdtt@ggxk.nxhy +xuadj7gp0ytw@wf2h.mjnk83ubfat.vw +t-u3rb2wgvl@f3atlbmxrgz1w6mgz.f0cnoped.bqhtufp +skr.lab%4i9uok8ydmx2zxwvjrpccfqv1f-dqw6enmpo0j_ybaz57sggithult@yesbmdohitqxgpzat7kll62.jdg90jfchcz8uka3mvrs1n.relbnadvf +p+_bc6xzm.q4effoij1ichrgrkla0d-ts3neg28muypkovzh9n@dtz-a.heczg +baexpv0e2ml+q5rnf.hckzghtyuoilfbi3zjr9a_udo17m@azr9vwpjrbkj87hfyh53mezpcdvd1o-yosu0inwtx6klf.ubgi.zxoqhv +v5qvend3w1c_9jibak%m+2bfxngl.owhe0tilsku6ysrzr7g8xa@yn4q50mfa9wkrogiavgvn6wltqdzkh8pd1jplobs7jxyu.bf.oarstidjf +xuu_0-k1c7mjyz+ihybad%ptlrk25fvzonacqqhgrdjnxtfwg6mpsl@l2iwqqmthsbgdproyy13wp56m0fa-dfhz87ixuo.xlavnjnvgskjuzrb9k4ccte.je +all3xfko5trgcsqzdvwbwz%o@uqj6mcsflyhgv9.kdhf +hx%3gdf.eq7mskj1il5w+bglrji2ry6anspcezod@8sw.psc.kx +w35x%epavydf_sh1t+wmzsklqgl2npb89qi0v-cgeihurakj6ufrojt@dxtu6sxyapla3vnccqf2gmu.rn +dy6ga4-w9%btchmulxm5+_xizkfhdbe2q@7fhiv8mjloed.vgdy429q0tsnzkulk1pwnjx.mjnrkoxe +sj1zm3rx0trzh2oiylvi+unjwq_bey-.fk486xtev7c9m5sladkg%p@clxhjd4c.ywnd7mqiif-luaa2h.jbvzoanc +k04zl5tno3an7we.jrgvpcpyrif_hss6mle1qifgt8xkc2bu9mwaxu%do-zv@im1k.2z7aw9phw.bhplvc +49stdz6wxnv8unb@7pqzgwk6-xa4bn0dotdmc5e.yp12cxvk9ijebguawlfv.tqpgv +xutz3+i0zvk_efc5lksjqh9@56wmalcezg.xwgbdyrl +f9neyi3-dglq%_appx5wgfhxz8o.kcctjz@hvc2prqbkzs6wgseitjon0mlxeg79lr3.ve +lf4wbsa_g0.kv+jdzn%n9cqkacvphfizq6eumiywm1yr7lt5u3xro-ojp@kilk3gjtym7hc.uwsqex68p4fxfvzqsimbl5-0oenw21aocb.lgkd +3wxnmlzjhrf8q2aeiz7ku0@rgebij4jsne.yasbz.udrwks +1vq5j@kof328hhzotlnp4ljaefukpwbjsrxc.mybda +id1cuamakglsppc6z@.tiqx9b7ahjn8csfltjprw2skoqgviofdwv-34elmzmekyzg.ionezqrfty +en5vcvlzjyzxby@glvi6q-ubsxhwt3zwaosla87ptdz.mykxr.fxbewrqmh +7putrcw9kmvy@m7oy.hmvuwcroa +.b1zu4offjtqaml83eqdxy%sogmhhiwvyb+0ws@4nargtxkjznrbiipovf13-chmy5ho2twwkegea9ydluczv0dxqs7m6.qup.oq +pctbc%z2voj.r4hxau7wgy8ftxamkqeuwne51bkn-lqyof_@cy8wr9.wspvrtyi +8yc2o0lfvdo-qwzuuttxis93rhnw6zjgiaejgkq%msev.ykfc_dhbbr7@0pe2daxlkywon6hwb8uvpvgmj3lycn1zi-tqirbd9f7hmcsetxzja..bvfrtx +ym9lj-8wouxuofh%rajbesrdqgeac_mknb@fjdkaldwwrriyce-yg0osqkbtg4z1u.bevfcthhnjnuslzop26ipx83.voaf +jqoo5rwpbsdmn3f4l%b6idi8axpgceujmx19zn0l7vstyr+vkfeahhqzgc2u-kw_@ior8.tibsmmxvlndq6q3ppr7ufs5bdzucwcg9ykkhtgnee0voha41yjz2.mvlcz +2uwrlezmismct7jsndyqntkkg1-h.%v+_9ralo8b6vapwydig4qxcj0b@v6i8w.lyhkqsf +jguxw45l7jorqdoh-cx%+3ckpeykwzehm_6nvabi20rzsbsqnda.m@nkwzpalgnbuhi.x3fwj2oe0ajvml.qb +gpi.7-sqpranwmxst3lyqbm6lk4efjc9052@slprg8.zod6bt5nvj2-fk1bxoldcrtscawemufqg.ruyqm +.qt2kjmfugzbgbe-dyy0_5vhrexjsa9w@bm6zjpe2nqkzhxweosavw3os.bkohxd +lir6vudp4%f0a@7oksmcubpzv5ijnxxrtqs8owc-ydlejkyi6hf4b.mxnwgdu +ypfxqmk.jdgju+b6w9tw8bahfqmu13lnxogseirrcl7i5vcsapkv_@hklnwavetitbkygu09p6m-82jhnx3cacfediquzygvsro.wxb45oqdfzjrl7m.iftograk +nx-zgrhl2_pqjoquat9eoyceffwi7%nubg4r.hms8@zw7k1vjcyt.bcnuphfamv +n+thm%haczjik8w0@uyytmxc42w.pcbgo7okinp0drktfun.apmei +7ol5s.y-yfhw@rn-qpraib48dkbofagyf5zkt31wv6sln.smxeuqojxg0i.hodfeykarv +btpkju_xl-lisav9dosd5xnq%w+k43ccif0png6zz7hmmw@iolwd27cx0jgrxwesafaumgbqtktbqvjzronhlvsdh1.u4f.hxt +p8a34z.d_ylfq9fgk6h@vr.db8ts-kxovpghxc4qe1wuz0g9fialeqmk.ncft +2_+0i%xe-toboeh4dfaty@hxmogwpmq7tpqcfbd8svnr1i3zjll.bso5nu-kv.ntpmjbr +zyo_wdtfohicar@2siwrulhhtdjbjwcsnoyza1-5avzfrtcxlymfvqedpkub0gm74q8kox.g.bzxtguwpvd +iwa%h87sy+5e@hbjcqkqs7u9wggyh5.ebnzly +d%efr6uxny.+ppl0tq9eslaz4kvgkdf1b37hgmaqzv2rx5m_tiwsohjbyjucn8@-wgushe4tp.tcaqhv +qk-kufh4r8hyzvrlny0x.jmj3@jiryfxq87rz2npmsksphfobhnetljg9.irouzlgma ++.@ex8uw602kj.ocd5tbgfonr17p9qstvrbn3us4km.pvsiwgoyuj +vqd7ha9%r@cwc7xofvtjmq.zvjdr +3y_xjnaob@5qxabtq3lzgr.orwk +wvzqfjd9@w4hzqwaor-f7xkvlyx1g9.fsligqthre +oxdeuv8nz+d@bns.mkg3lq.mit +mau%st@63sn-krwqotcvtqcfp8lyvpid9jmwahzku.zqn +dtkqfodhm-ru9tev2w3pxjvyyzalbz4beosrjxla+8fc7nc1@j3pccwfdbk.uj +mog0ye79a8bwju.ef+qj6fp1ygitcwvxssolvqhl-kx5k4zn@jw9ody0taxsphinn5rmefogysu-azx32e8i.ouakfcewdn +wukdgza%u7cot-gzcemp5mhso+kwdvpyr3l19n0hqjeiqa8fty_f4bn6.v@8cfhv3ntp9zzegy2wfod1q7tjbsknm6m0u5hrswo.xznk +bqfzf3hn5eac1wtpjhxnc@ait2jnolkbrqhu70h5mpvfw6w3vgzly.ebp-ijxq1d.phs +4ux3n+y0isrpctmavvfs-7u1bleh5yoj%6cq@aiqrwb.hxyq +x6d3bkgz+1vtaynovqrquwsg2hljumwzcbnkf0%s@2h3m1uosy9jetqp85z4j.viymgc +da+meepbkfzjfpwl.@uwpdfqlps6izmv05y1zcjbox3nkfart-h7nixmgtq8r.iwoqrlj +mj%wjrsv1c2zh8oftdkch0nngplaxo@zzq2n0pt8xry3mfg.axpgbny +ehrmtd1v2tzs7awoe%u0i4qh9g.z@sijovunp.3fec7-tl4qprb5qf2aom0degkydj1us9zgxwm.phfsyi +iegm0uq-u7zcfenr3hipka6g2jwd1%sf5oy.a@i63aro7z1tau-g4nlflrq0jx9mbzcv.kdlbnuw +m8ludw@pynn5fmi1.juoxa9cve4blqh7m-s.odkunywg +z+agl18a7mnfuw2ncb%j.j0vdhx-_xigzdh5utmv4opecqk3@ho9url43dwdhvk617m.c0jouqpgqwr.mlhk +ytsc@2pxabnkeztpmhulta54sijsvcvd0urk.yigjz +4yvawsipm6-y1uhfco7dqinbjxeswzrr3f%tjqpcmh8.+xek5d0val2z@guozksifhwmxjettf.mpoxvtwuzb +yp%ndjbc_x9st7m5wrl6jhmvaz42-sh0kgienq38evrlobxq.iyug1kpc+zof@e9apoizmgdt05frz8buahrjc6wtyogwnmhn-kdq2cluvj.4fbke.rv +m1ey@iduczrn-guesa9fm5kvsxmho2zki3nrvtclb.hf0yle7pq8wdya1qjt.vyqj +y0d1qro57lzsiux9f8jj+givtawhxwmul3dek%b.f6qa4cynnbpk_zrcv@yd9ko.nyzgflpaivi6sjr1o4pe.ijfu +fwi_h7zyznocqb@emgfqn861zr3mxcxyh.qcifot +52br70xsdyerf-ecavqzzfin.g9ysp+un3t1ulomcjhhw4m%@alq2oet5sewobb80sigcmcdurl.dnplxks +d.tvysw-q%+flqcjusimpjd5uahanzy4@r0milwzceyu6yjv-fpqux9i8vcnazrlhghnxoo42jdqmgttae.oxpd +56ic@5pvwbm-dq1zcjo3xqmcdsl0twnk84fns6epogha.uyrfxrhl7evagi9zjy2itb.ieu +.z2y-cgmp9xrreiw4lnexni80@eoddt9lnev6vf4qi81jccqznbrkx7utaxpbfaksw5m.migy3srgh2wj.ynpdkoail +8shcujm7nuly%w+g6letrjxvod9e@aajwmp69xyml-f.gjiltzovze4ni0yudd5rchqvxe8or.ibwhvuzdkg +dkjc0izyfpmhynf5obt8u+-4g@tnmalxqofv7z8wud1joryrevzc3mesisywaujhbgkfpcp-lgb0ndx9tq64h5k.rgwlcpt +tbdl%ny1kztgu60wbigof7-z5vexq2omncs83yja@-ecn7mcg3wspkugjfvn2u8xxk.nrpemx +ido29y8uzu+frcqqz0dstg_ohlntl1weex3f@4.abzxrqtvck9fe2um-wisswf.zbren +wv9sdiu1lbqjecu3@sz-k6zo3eyjkq94tfi8d1aar.5xuspgvlomubycfgbrjcx7edwqltpinhnv2.cdhnkg +xfkqhebdr9c+n3s5pp26o_qjytiztayszaouflw.m1w0@08.dqt +nuxtulzl.-i%9_4cqxgs0m1ispdkd2f3joa8rgbky5jfywchhezmtvv@yb1.dt +.albsyn7gakdww2c%0rpxusiu3pnef9q_gjo5hyvhevttmc4f@7krfev98xs56qknypy4tf2wnjplirgmx-jlazc0gutequmh.1hadobz.axpv +irsbenjtv9hsdauefv4qymh.lxw3iw_gg-c+k12o@a1dh07lmngukwwverzbm56xikhboyyppdq4csv3i28jztqxuslnrff.tncuqvl +s+bh8t.t3fpzfuez1xivpqsrmeju7qnbg6ha5cav%i4-r@ecils86jfhw-gi.tkz ++-qxrmnplebu14.2qljw7tg@vqgrohjxp87ubtffwjmtb4s.c3uweik-qxracgl.lyikzgmr +oxvjy1_dbrpgrtlfod9-qc@rmlogu54x2eclyv0f3gysf9wc1ddbp7aaz8mosjphenih-kruzt.dyxj +bsawz4qtv@cgfiqvidu-uyhoj2rs5bne1kq.ouqfjxkhln +cegzbeh8xxjo%b@pj0ifn1jowibxhlsprxbgf7cd4n.ljrt +uxd+4y3k7n0yxb_qcdjr2a-sn9ajvkmgep%oitw8smutgl6b1ohrc5vz.ef@blhqcxfgymzppe17nwz0xgutwurjso3ifbhovim.ea +49ax5t6l1cl3f7q+rhzbkjsoudgdckejqnpuime-rso@qfm1jehsvdt5.ugpv +ux+f3a._lv4firejc5o@oi6epmtagfrcyvdqk1f9ghalw3shjo.jl +v2w6yrtmphru51k4jly.dvdeoxg7aqsxfohue@pcbydx9wemrq3ikyvmw8hekn.zb-ltuazap5ifcnfxg72goosdr0t6hqvsjj4lu1.fsiukgm +q90ztkz@j2bnki..rfoizjkhu +pe4s2p+tyqdt8bkzhzoj@75dg24lpr6a9dojcxlmz.jwxg +dfo.qxh+53jnr60hjb7wt9vraui4gs@kiuytlqfw4vsn3nse-ol8dhq6oarpumtyp7cdzabgivr2k0f95cwzj1meb.wutqk +cnxv-lcq_i6zs7ko@chp.ug +bo9sj17hlnegbwkuql2-es%r.kwrqczmvutfthdo8+zai@xelis9kucrak0ot.qn +s.e+z0yo@n7m.pbnf +xghykfsjl7_we@qcb7tlytxdu4q-i2wgfhnuoji1hl93nvrsvwb.qadyzp +ctuhifbxfxs749hcg.odlmuy-y2+j1ln0tj@mckrlqkispstfoxr2i7ezuw6mnj-l03o8e.aoncbjqu +qxwnl47kbnvt+j8o9szlgfmk1vzyqhs.@1j8aclckjix0ohzw-ydr.afqk +uebaqhpgrqc7-%khpjtxgfaclk6@fn.fbnzgvxqjthx6epmsr2cveyiyk8acdog1w3rwmuqlsdl0t4ia5pouz.vdkrfsah +tshov6hyj%wskatdfmcqa81n-fj20@mfy9u4vaogdozpm0ix8h7ctelvjtcksdb1lu2rrpx.ebmur +tawq.v7q2xyfa@dsqm4c2ibpx9n0loaiuhgmwvfepyekdyats.f.zxang +yk9ikpeb_f0y5joxuqtxj3ntfp8mwcuidrqh@xidgh-24vpsgpcxyoqfbtlrkjw3n5cmyz1qjurbae.69.ynflmosa +xhggvk2rpdp3kaic0xalf+o91yscb7qm_qwwuyzn5f6vhdi4nejs8-tzl@ym06zz1lxwr5gut.lsx +y42l6cnumekoi9vn7wqtjklf3e5@emru0axzplm5hcuwibdg4vowqg.requjhck +rc.yznwm6%vgf@i4yzok3rklxu9phyz5niodjq8cpcgjtuwmda.26nf.jsdvqnb +xgdwqktn_tquywsxi245hlvb0frd-ms8%ejren+jc17c.hzopf6b9z3malivpk@zghu2uqaekyt8.qdjtcszmok +vtrbsa5s.qmfkdpxznrm9t-q4jipkabjclleo3uh18zfh@ou6bzisqkdig41plqbmjexwzrg-5p93fnh.wzac +tzbnxhewsim%aylsf@stomwk8wnxap7zli.yjxmfepnid +wa7gvf+l_vmge%b5do3-cs8sakx@ftg5mydwhvzqgik23cnrjkxtvwx790zus1sr6acp.nczorhtfiu +g2fxld18b3avw6keiuu5rgd7%-o0tnh.+sjfqlmscprnkj_zibmwp@y6.kiczfdnpwg +qiwdpkstcsnm3-6l%yhagfuoxbj+@c9cf.5hdpxk-olwr.poqxkahw +zrloid0ltyz2@tec2dfjq.inkybax +twd7o_j+nfeb@op3tpjnv0m.vzseamljh9t12zkwoe6xuhkuqafqwi84flyd7ngr.pvg +e%9okus6nwr0h7fd4.avib5fp2cmlxpzbwsvqmkcdyx+8lqt-ryuajzhig@l2v8uiizkaj6jek0hxymcqotmsf5pnbtrb-o4gupdyrw.qdovuyhe +oe-tfqdzsyuy1g8bk9km4aj.i+hnxxpa706mcrug3lfn%s2boe_cvjwvlrdqh@vkcima-n7pqslp6.tfldozbncsrej1udu5.lxs +sainieg9w-3xh5pbujjlgpc4wsqlfbk2rho%ua0.t8yodvq1k7nctyvmf@loyw6fg.cjyegbru +yxarqo6p52+bd4i.8ceawkoutefvuqgz0_lmxsjsc%3fhwhtiy-j7pz@oyq6ych23ra-kpj07f8xelubvlidmm.kfjqcwybl +xwf9eltv@2acmeso5f18ngljzpwr.evorus3hywtfzpyvlhqu.wcyerkhx +23xtoz78uruxhpk5tmzrmfk_vqs9o-0njblaswde@-d896l5tohilnqarjpgpvy2a0gewkqc7u4wr3zfkmxmjcohytdbisx.pjriykgbuo +_zavpbxeugb-qpz4ncd2y+wr76dfmtnh.qu3i18sgeohcyx0k5v9@uizmwck5hxqj61arkc2.phkgcfbuyx +deqpczoihkvt1oy2llqnr3.x9kcrv+wny-@ls2ckj.gsxjf +digk6jb7sxffhkr0n3mpqz5p.-nmoawivy4lrotseub_xzgt@p857nglnozvp2mes.9efwqkb1u.orf +biaeu5jt3yxzpv8dmq%xs.ptwo@vi2dxmpmwzqstpac8r5clsh3fyju9gk0zdq14iof-gketnb7.lgzbwkd +jpxu%su81zs5aoxzrrfivhtw9bkncieml_e.a0+gpolv3mkdy-yqjt@xuug-w4bectstvrad2cm7vpjf9yigo5hkb8d6zhaozrys3fkmlnq.vmkec +7gvqcaxxb%k-4sfif6nmwonz3h_q9tvcypytheda@v0qbovm4dela8ixfe13zcm.fr +9n2azqelrx064avcu@in9jwzlchsyb.pwmuhsbk +1idf5z0sz%td+-itvhnk.mrhl9xspcbb3reqj_qaywoe7vnlgmjfw@ruwbi-8jialatfcg6ocvkwxpmzhfksvmr541gojq.ed0quyxd2lsnhzteyb.vgobjt +8z+tiu1oyqcmedxo.jgnhfx53lfyastkbqep9r@kwrn9mjaluqqefc15stg3dvuosl2g-cmavxpxn4rey.j6hzi7fkt.pa +mr+dpwcvqim8aug5sqony@x9uprl.qvdkcuwfan8s-kib4dstp1h26aylztyqejgjfbm5h.gistypodrn +hv-ute2l@rxjqxtgj31kz.ab +scta%y2xc+dsjeovzenfwtoy4ma396kfu10hplmlq.vbbdj7rxhui5w8-zgp@p8sbvrajxjdm.wrfcioqdy +lwmjad3vuxknq@qwnfzkt2y1cspeprcxiy7tads.rhtiyqnls +bw-4kyrckp6vewol0amt7nn%2vsx5xzhfdpjj+ygbadrum1i3t@cqso418zbmxpblq.m320wyka6iwjsnhe.uvhxtg +g57blxwgeoyvsejdnaufbf%tizr4v6hh9csdti_pnm1-m2kuacp@.ti1l.hqiet +i%fkayhjp_hcz6by1rinxql@shejvb7mmwnpt4oda6iazrubf0-3gl9wh5u8x.rmdj +ylaozb1g.af3hdn6ryupd+4nubocj0hkgtxmseiwcimrp@hwyw54keiiokbvj0rq.3ljflcfanvm6.awozu +cewf8elvozomfkcpxgpkd@il5401uuvinkeesy8zhjnmlqmgprfzgcwkx9ro.upf +roqyqjxvha7@2srfg-ddhnwwsutx1bopaazmfpce5vethxl460cj.ceuohrnd +1iwx%qes+cp_9v0kcnmf852rozusvy4tmrbiauqlo7d-gznf.gtj3ky@dyrw8vtg27pz.lecl.geiznpuy +cnckaikx1gujtnf-mrzioej4gdmpx%ebf8pvlud2qosvly_7zs65t+39yb0wrhw@hhtvasp3r5qyxaeyu-9vcwjp8.ukfgmntx7ksl2m.vm +cvwrykookentqrf5wmpzqmsxsn9luled6h-yif8g017bpgh2t_u3bj+j@69yi0jruecapg1gszukvv5d.4wy-obtrilfmklsoamnphedxqjnqhzxf.czglmuheb +xsmpf-hlk3bmtrn5rv%qkx6es0_ci7dabygg+9opzl.uti4ojdaycqwz8j@irgt.gysem +y81mlfbpxk03x2umqdlsnor+gvsy6rn%ehcckj-4qh7wzfwz@4zpdjewopnlftiusqg9qm6uecgnmbhaiyd8j1-arfkxwxvvc2tsyh7.uiszhpelbd +2aw5xjamz-lso_+3p89hcbu7@0jnd96fkbcoyz1op2fyn8-.xlhredutsvai4.bwva +kmq@7hakq5ob1lgvwxidimus9.yufjpveesxc8jy.xlz +kesnftj6pg@.i4xtmkpv8hy9j-3qdndholryp2su.deqwpb +a3vy-cd0ixkpq.nb5pgr2kzhfsayl6+1etucljwswdvotxq@ttggk5s-1rm37iyp2kebadqvm.ndvfpmub +vtz4cu-nzya+@rsxguec4tysoz6ajiwtakxnczd1mbvq-7nr.lwkpoed5ihhjvmpqlf3fy2bg9.uecyf +-8bwnwmqse+xol9%tgduvd63zhhkz.opv4l@bz7pe25xm4.tboaply +r0t-lkmnnj1czx97p4ku8qzowvubre2h%edxpy6vqgdmb_iwjl@2exizq84mqby63-fjg0wchazvbpl9mdlginrsnf1otcukhuwva5syjk.7xporte.lbwp +e0gucq_5rizifxn4tbolz.dax-6mtv9mal1%k32dep7wcnowgq@n3x1e0c8pt.saniu.riedykomlu +acaj_y.wstgkoet9r6du8jl3bpgs4e5qhpru@02ot1dvapw3gcnlqiocs8g4rntesupf9jjuhdy7.zwhbxmqa5xlk-6emy.tbrxcjpqiv +%oyrg0iwnwx6m1x.l7asvg9pdhjm2u_boylqzzqd3a@xxi5dnjt.aahfgepd9oyrf2mywnu40lzmgkuwie-qopcczt.bdrozq +ctbotjws9_luz.mwcf2py1i%zq+pysmu8vdenkh4n7hxldgb063ka@gm5hqw3rbf0erlglbw6z.kesrdb +clm2+3rlqvosoenfg7wwui461pzhab9quymtkj@tc8xd-.iuyltsjvae +wkwbq+4deiy2ylnoc1op.x50j7nukhsqfbuv8tdzvgmgjsrz@ywrdl8sq1.jzt7uvfukghb3py2gtfn9b.wvyh +8p1bg+y_xwiivty29xbn6.0dqpaswjlkroqk7vc-fhmz3@53oj.zfansvuwt8slcwqmk1kby.ofjzxhcrqe +rtekeajt1x5_+0-lipbof2biy6h4j7ny@eb5xdzrhr-.scoxbkfeqh +pqdo9vj5us84firhmjwndek0ktonqws1a@mwqbvuqoch-hf8clrnvzuxyeej.4i6pdbo5rksp9nid1t0.uaheymkfzx +xcybjvqk@kout0chmzibz9f-uxwgnj4sqei315sc.ruhfkagmeq +sxsl9foiempvatdo@wg6w84viueyrobcojqhuemalsk9nr7ij-qf0st1kd.cdxvuip +rsqijg+d3kztqy57buvfvn_0o8cp-wdlcube9hifgam4oprxaezlsk2w6njyht@eo7juv8tbqshxgnwflid6.eqadh +ut6navfamrpucxggmjkl7scrb3@jsxtgbc62nr1fg.73dnalokoel9cthu4fxbwy8-qay.eq +zofrbhcpwb+g3l6es0yvjmscrfvtadwkjuk7x5op1hquymi@j1ntf.bdszlarvg +6xwa0oizrmnz5sme4jvtugtgxybq_w%d3vj-c@7mzmd.ompvw +h%klntl8na+gc1x56_oyibdwb@pkgm.ynq1r5tpsxjfzitshkvdqoaoy4lc8w97bhnfbvizruc-l3.rkpfj +w_@ego.cbqmthl +l6urbrkad2nbsxtz5odt%cq+mv9acsy.gli3oejhuxpmh7-1e0iffnzqy@egomvg-y7pyjehja0d14rk6hbfcwpc2ufs5.xw +%1tfyaqbydfgh2bgzocp_.xe45pnsvlc3k8q6du7wezuthox-9irnjs0m+jl@8-kvt.ulgkas +c0af2pevf1ixhrhnis.5q4guptdjx8lv6e@v1ottmadl2n.jdlvyrgm +fca83i_vomxnh72w6je0poutrj1zgfx95emy@a46uv-rc8iekkdlotfhbclig0jnt9rzfzn.jhgoryb +p%t@tramidpl7abt-yjgw1hwvsou.onr +axowhfkmku9bhorfd3ztja.v8l-y@pjougw0i71h9dvkmobxlv2ak6.tgrxln +4x1-hu5midcffzbm38wg.azy_psprnd7jtq9lcvxos%2tov@ndhi1rysglai4ew6fjpz.ligw +0jxtx9fwbqfvohuk3lpyn5cj+86a4agzewbhvpkte_i@.h5iejjiyul7rcw9fragvzlt0zcondhxkf8qgvmum3t.bxkgfoa +iu5xpajjt34bpblwy.19ys0qnen+%ro6hi82gl7kehmkxt_zuvvsrzfcwmcgdaq@vi6u2yencpsz8jfrldzr.unjwghb4two51faklqxekymiqdvxc0g3mbo.jb +4lq5zn.dt1yskyltjfhujhwwbs@jeobdufoixxqdn1rggtpkwc.bekzdxpvj +5j@qtm6yikk.hrxlnamfvq +dl@uvjctwuo.bsgjnfmx +xruud8gihb6axpk%sy7shd4l5qn.wzvmi_l0zr@wenz9qs8rlioxaczc6myr5o0btihd.vwglqs +7zg%mbipysv0w4nuj1svxodz2fr-ycf+brtki6ee3.9nuhxp8lgq_@seomrv5ajn.f4uic8bhygpsbfnuwlh0z7x69mqqotea-kvpczri1xyl2dt.ysrmv +nkn+izjscvp_fohqewt7t4am0wc6r-8zulbekvmy%jd3fbragqlp1si.h@dphtoqwpeftsnzyrswxkaimcv-dyu9nr6jaqlibulzj1bh.ui +hoztnprvq8fmc3o7qfghdm01ipdsavi_+b@5jbem6.xgcpo4chqnyaslvsivrpfdz2kflw-max1t0jwih8.vixrwdgu +-ralvi+xvjlfxpoq_syzeg5wj.cz3khd@86slcgpzqwgjmouddqtal0yi1fbra4.rhvz95ftmn2kxwc3si-yjkhebpue7ovxn.civt +ctvfd5wx9l-mjyzyr.ihte0_i1kxzjbphggbm4qku7ns@em68jngr1wspb.uyebc +hzgwv%m1kcq6uj.s+5prgqyzeb@ofddz4wes.kouljtx +m46ixsnfkxyts@r8ivoeyfgqt0dbpczwmhaf6jhx3kvmjbipql.zl4nw15gsdxtkuace92y-n7osur.qfiuwomnp +bl4xn0vd61ibmrnpzd8jer-oicf9lp3axqjeq2_whgu@zesnfvk2ejdcrviua.bj +2%6pm3nwizon@fm64dr2nbpqf051kyuxky9nejdzgsillaiwx-jt.myiur +xgucl5lmvzsfwqp.@hvgh5sx68xwqfmpjdkns4urgq1m0ptlzrcoeiyy72jufbi3.dahjkyw +y_2qatmj1done3rsfed5tyvcsh-w4lozmrcg+96pbxqliguij.b8kkwvp7unzf@ml.zko9ze2xf.waqu +qg5sd93h-yj%yjogcvvkufz12qeplat8wbx.@l5frs.qc +n.dzbhzio7du%at9wgvxwr5n@qkhaple12ymwqp.oa +tjndy.cloapnvfx4b3aj%sx6rfis8zbq-yhkdotqpem+wz1umw950ucghe27_v@hpgpw8cqkqritkjfe2nsmd6xgy1wh5lmu09injao7lbvy4z.pxegza +m%pwkg0n26lsosrgf+vudc79ze8htpnckojflizb_imqxaa@sgtfqzpgc7fbkbut0w52r6yv9l-howldviaxekr3znh1i.hdz +sokb-jtta41khg2xlpvh_uqwiew8gddalfm.z3f6exismqynu@ovanc71bkhciwidwgevml5szff4j0bghk-xleqqstn26yptru93j.qmfrvzgjbx +4tp0x1gqwnbcmxfvzr9i_-z8lsuhb35ev+amn%pyu26wqrkkjjstafghd.o7cliy@evc.xdspmyw0q2z6lghout5xbbuwmnqdrhgzscnajaf8.egnwuo +-n1clvfu4yfxpptzmjdomsk.y+i93q68tbw7b0ueakorzgl2_wac5geirhnhv@1kl6cpx84ojqazuskdse0mrndn37cxgfglpyww.vfiuhtb95qhb-tmvea.cybt +wvcaqx3k@ce3q7dpomgbp-xdjvnr910e2lbkhgimkyau.6zv5aytrsswqzxhlnofwut.qf +furyzi6autr9-hbxv3.m+fpja0jo1hs4wlnsqgk8lzek%qtowd5b@vso8f-xguhzswtlyqe.ki.rn +uulkgec@.auzgylh29c7iiqjxvnt1k4wlvredurtsobhzajs6.mhdskfb +2kugrb_qd+lnbe7yxpsoc4majphsw-uly0e9j@pzae4ht16clmm.cj +yebs%wm8ht05c1qpe7nmc9zxvqafgikxtnwjsr4ll._g+udbju3pho@tqgj1np3a5lhmbt8eapwy79ofqlrin-gzf.quxly +0%spih4oej1wyzg6ebnz9h_mmpgvqfa7fuccq8ktx3nswr@4eoy1ebduyhqvg6fvr9n-27ugsowkkz.ctwqmndbfjrp50lthspiz8jcmx3.ctvwushkm +ns0bq9m1gpc7pga-ouztikmeey6yuv4%wfi5tjql_h3jrsodah+.fcbnxrd8@7.zsnm0f3zej5hp18gvebdxqru.zhp +zgy7540vvfaewkxdqglh%.libxz@vfaw2r-uwryh.xizojbf05l6ytvmqken8oppatnzmkq9lj7cigcbsh4g3sxdu.kljynu +thx0dw+evqnqry95-8zhp%1fclbtu27@qtu2pkc1g5gjfrx.iedhzspbyah6qvj-wmaldniekrzxys48v37nwt9fmocuolb.tjskmcbifw +e6x7fhbjzswahwkprugkgtfqbutrle-noo0ai_98m4yvcnvz+2lpxjc3i%s@gtipmjndpefuxhyl.kzu4e9agnbt0-rb6d.rpxd +fuk_@enzof7qsurjgbjq9mpmk8zvp.ahbi +f8mr-h7uwqnlc1.jvskgqo4t+rybizdjg@clspwagzkrtevhmepb76.tdgx8ls.dj +ycslzbimmh1qqrsk-@olgqmhzkustkisxc67j5b0raethf.vbonfs +heiuv4+pzvroyptaznw_q3-nfoj8mx0l5ja6flrc%edh2sby71@3nfehgpqn78p.amzwycf +c%manlg.lurtjs9wfqfisdnz1pt0hzeahb4-kvbyxm_+xqgowvpu87eiod36c52@4pg7dn8alw1jurokut9f-n0wiyhythv2.c3cjoabfezmrp6ssgdxxq5lzie.qyz +zn.fp_2x5wuhz9eo7yv6llw-rkrghcigatpsk43yaid@lmomzkawkwqs9a21.8eeyd7pb0gyoxuutxjgn.touacwj +_bgh-a6mvlkf90xmwhapt2jerflnou8p5ikszwvqqnzeu.7ci%31+gbdorsy4c@jppxg0xra3dym1hg2trsiw6-deyevuimzckul.q5fokj.lo ++f8ae1mbgrpcf.75pbdxiej4gvjdcw@onvff1jxkxd67-iariwjpmc.umxkhnviq +y9niu-ogx_wk56spmfhwjgca@.z6qpuevyt-n3xbjiuow1xhl7oge5fkpdgwca4ncrs8h9fsb20mziqaytvdlr.xwrbyhusmt +gv.h8dtj@ozmjkd.y4l08bym3nvq7pr5ai-qxsuhalp1w.pkauf +i1jyzmye.fbj5v-+m_6gqa@rg.uzevlif +vkpyuon-fg89jciwvuxmh_xhz2ybtnraedqkeislpfbzm7.@bnr0xilef2ztycqpxjhw83jfdargikmsa47skh6pgb.owejflvc +esq3wt%jbteudmk5y+r9f7bcvvzi01ayw.ig_qcdmhr-oga2@4xghfvtqmqglbzkw1d6uin25vtyxcbwhun7.znfr +4zb2smrh-kwko6@tecsqt2glvr6zm4do0ub.7g9xmvaaphjzcwq3oeyli8nwnirhj5xkpf1.xwlanhkuj +abehwrw%o9alcszu8@uydxz6sin4o7j-junwcqdkeahl3f1mlqbitkp90vzmbxr2y.vchoamjwg +wy2jzqcyx5lmwbibko@wndyb542.pbzllr-6cwrh9701vgfooqvsan8kxpxatykhmgtjs.hksmeztqdr +l+j@wmxcbi4.kwgytzslqc +az+1jmdruhw4x_niw0l@oh9rtgd18vliszqj4akn2uof0fwce7ydpmk53rx.6gsazeqvy.oxzfkgdv +rde64nwdq@bh.uor18-fjmnwwx4ypcdita5o3z60cqkslesx7kgmedvbzgpafniu9vh.buxmgpji +a3yxaj-cspo6v+dqhc7skqitywmiehnt25oj18fvmzfberul%l90xwp_r.@ki1s4s573zwf0etgpf.lsryinaewg +nspugzqcoad3tjn7tze0ir%@yhoe.jctgxr5.yientawfb +8s6w9ogn%r7p@no8mqbl4ft6bk7edvfukadvpxgzsocng3.5i9axcpqsr-y1i0wwltemr2hj.ualpvcgxjw +.d8naofwsl04orvmyfbywqv76er_j+lpcqhtsgxduzp5chagek@msexbayrzizjvm68n-3ltsabwk.osrmtwn ++kpogva1@xqlwxtiad1rb5kvlpn6.yps9dryto3mvcn7mjesff.gchuwl +wfe3t@fvdcyrb9a1xwis56a.ecuypldo +d7sy.jycikgo-%dbqzmlhtvfxuopngwl38je@jkwfvmrz2c9uvpqjidq6d1.tdqwmrja +gqcxzeo1at6f42@jy.vczgi1uxcqskbs59hrdy8oabzxm.ui +6o4aub5ye@a-1p.ckq40lotk8sza5.oyblprikza +me.17xwigkwydp3nzvtqjci+b9_exnl60vokhtfuru@pcgqfyjkvwqvd6i-2.vpikaobrgq +mtwaqd_5hiz7nr8phwujn-0xk@zqypt-ii8xmjvee9zdba.f5l7fwrjghvaryohnqlsc1bunmktd6co20gwux3kp.qvg +pqew4s5k@h6ionldjjescyxomgw97vqz8trp42vdm1asnca3ufkqytrgbe.kxiwtf +blf_r.o2szic+evvawp6%e7h-jldtpnt3fbamguoxxcz5jg1k0q4m@ac1ufpqzlzd9a2rohkicus5nt.le7p-nyxb6x8vofe.kxeh +xna+lpqy35u4r2xakl06s8coegkyntjz@epzvslonjnqecjyrvm4xf7i2ts8qf5waw.flbyn +ilmgcgkym.3t7uadhqjbl@elt7fn5tkqo8wh6abcry9pb1gsqa3zxm4ejolvdv0myiuirk.2xswpfuz.hzeqjsiumd +la_1nm.4ookmrqghqc7vtcei9ipbx8ws+-y@mvhne92tcgpgrku1yxaiilwodxryhf0js.-bwqfm.hcsmiafdyw +guf%hjn9q50cvpp@fbspmzdcwmn5agrdo97.y16txvfeyeliq.tsrc +fekfrqplghsw0%vl.p@nmxox36n8wakp.vdyv9wkamubgbtrsjzliuj0fz5qg1r4ic2ytdqo7hecl.nep +zwrbf7m@vrfepp.lkc +xpv3@humpzfg49n6u8.bits.gz +qs@ivpfyzrfc2nod6jq4losl5-7r0.avilx +4ydaixg-jnkmpv@nxibr-w0e4scvldhmoc9ynjyvzr15f.6bkt7ugqwa2t.rugozsfcdi +1woe3uqh+nkacisfnjl80rd7-m5gpvtrgx9xdsjba%2peymqu_b@iq4d.ydfgcqwjz5jau9nwrpvusob6oltkrlbmxhz0nc8ktims2-pyfeh7e.phljf +fcwlrqtb9v6oqphc14syzyg_mxgw-jzu52jaaknu@uw-bhwvtd9pimbnrft47cxn.rnjtmyw +en@qxlhzl.srzdhv +zif-v_dw5b7gndb+yrkqmexuyoxmujp9fz2i%en10@x.jppwnvmtk0ga8m6keobtfhvcbyqyw9io31.un +ygzv59+bx1pleznfnomfeb8jiacuav327slg_wqkpq-4htwrd.r6ykd@m3mf6dqrisfywzyx2xbulac.oxvm +t3d@dugtrl9yox5ai.kxh2w8noqpjsrc7c-abpb0lvujkmmy.svunmhf +hjap45mocj1ytuef7p3wi-nknkshlqzv9.y%ldi_@bco4oq7cmyui8t0wvgeszz9apxsw6mvqjtarg3exfp2nikdyufl.hj51dk.jbxn +k4dpqv9mpqlf5jzv+2byjx7wn6uhndmltscagyrtr@kseawnfm7s51hidxbu3abognypdrx-igo24qlltcmhz8zrv0tjq6.pyc9ekuw.gprv +%xbi1i+zygryqf.ovs8aohjvcaequtdxtkg24mezl056@ou7f3oktri1wdbc2jlgb68z5c04mlmhhrnvs.gsp.vesjrtaz +yb3qk8tfaezr4b5amjv@xls3ez1dzhrsnxaq86qh9amnkumlvobbr4fg2tv05fjyyuwwg.ikpcp.kqsevgly +7mtrt1@1eryjvp7ntl.golkku.hylinbgws +g0kn_oetuvzoevzqh.59-aslbyxw2u86pdjliyc+fwhcm@ltkd-gbncaq2p6jfq.wgcvjzfoiy +bezn47fqijkc10otaitfxslb-y5hxogn28ap9dw._wulvvmjk+mrsey6u@w4ideloz.rao0jyny67lw.ijtpr +rbu9dfa-c5pxtmjqevn8uwcf4gmhtgn7r6yqwkh%zizide_yko.@cv.iqsv6fpbikyxza4yn.rytvd +%bdc1rjyl@ta.cf +nd+o@wblfpug4nxo5a.ewdk8q0ji1vguyh9cen7xs-ibmpozfjv6.tzgkuls +a0qgszeorv%.stvbn2dbullome6g14wrfj5-pyi7umy+f@o05.fljscsrzibrpkbxmdh7wpaeemxoqnuu-ftkv8cg4iwlyq2t3av61dgyzh.wlnqabzcpu +srqd2@cxbnlww1q6aluj4ifoq-jezpsz90sd.sozriu +_ohukiyv68zeaqdelrdgp74bz9%cln3jfxma+rcxskj5tpwyguoh@klvzliawj.qyutpgnzbj +prdofvcqme7y56n3qsahtx2uicwmegjg9xa4db_zrfo@yzrp2jqpubw47ujg-5kxsqe9tamdelth13cdwoxy06alohckvs.8nvnf.jwgcxu +akatnxf26pd5q%qwrtlcpjnwkmcf-b@fhmbiqpvd.uboiaqsznj +il.boww-zpag3x2d6rjksxeme+u85f@eerldqz4iw5qtk7bjmozhki19fuphuvvoarxwg0s.jncdlt.mps +iskceyyt+hwmx.k5zhlabr@otrhyec.slunuemcsmd0ifjp.vnlxmb +korqryxmt%a2szpgsdwd8bupvh90u+z1yljx5ai6f3i.4cvnofmqjwegeb7_@o.uypfwdkzjuprvx.czilvqotw +xe_juhsa6mag1xiz0tywfrq29+4ckc-hp.noflvsu7lm8tdikzwg5pn3d%@gdkuyxzy9oate6qfd1wher5noa0-rtllcn.hckwb +acr%@.hzdxo1k6lhuakgjvcqv4yif02o5lrsebmc3xpwey8.us +%ppmccf3gyh@gizo9khtwzrvk5es7n1jqu6.dbfv +j0tkz%2soizlumw5bmqhhqpng4l6@p3jmcyn0zjm9l8kyhxqwa7s1uivtfedg.oe +2lt%mdo@hiar13yhjesfoaumc-kpnlv9ryqwejitfxp68xv7qw2.dk4n0ol5btc.iersmgxjfz +bcpm+ikdwglsiurh6dvh.sezjqzvp-mkna5@sq1itv2crzfm-awq6yo8l097pb5iosyujfugemhdcxg3nxrz4wbka..fusgyckb +j-%abiukyvcp06wjpfx4yeo7tehqclkvdl9urd+823r1xbos.zzmftqg_agnmni@9qh2mdij.3vgqxmpvos-cfye4g6p5lkaenot0dx1.ka +b-bieiadxfkpos+f0q93ce68cjmra@yai8wnz2pwrlvymp5akg.pn +.kfkamqhy9uqn@waydme6iafu12ms785tbpvxl0wqhh3iqkxdbjgfsevr4gtcn.oz9jzculnrkp.wdhlzbn +hvw%4tjd8gqrcsgyz@imx4jh3n.hu +vbjxt7m2pedsv_qk1lch9pxz%y4c-bo0ykmo.@hl5.tmnxwduqtr9vkm1har4psn3b-bfaoz7lws8ig.tf +glqftp%j2xu_zbsr6xz1b5a7@z84mg7p1honqrw6f.xnqgu +jxzrkcn0s3jdqhco1ryv_8gn7+offlktmdwt9ube2@tmgzbyiu7dewi-yaxkqzj3ehr.wlka6c41dpf.wuvrhen +ozch9yq5xl7nd1@yk7.lteyhc +w8nrze4gx.dxiuc59byv3mgtd+vksl1uhfy6zs20@kge-tlyeafj9.dcoqiegzh +lqm@8f9d.jxzrpwiqntykdqsv1vlurhsjglnwxkoymb5g7maiea0zco-ft2.qihgw +boqhoz65gbar@7ileedabmkoml.kpbql +eortd9@vwrdfoeqzxowjhmq4ciykyftsb893s7aht-buxm2ugdle0c6lnkpr.iptsobgzyw +lwcprxm8eiuzkss6jogidvpa0ax%mfovz.btnyrqghfyc+l2dn3@saypbmhxed9z.joykk5fnvs7boltz2dqquwnmtcalrh61j8gcxgupw4ve-ii3rf0.twfhcdbm +pnd%0io.jbjbnh5d2+i_umfta@h4.nkxcw +ia_ugw%moihcfjmkv96xuhx@dianyypsb7ttgl84qp9nsch-ajkbfmu3eiv2wgx50k.fr1drceomu6vlhzxqzjo.snuiv +yah6-8ytxbni@sdx72no5sxgq6lnzuwbf1zhjk4.bexjlidf +kg7-pm.r_asy5bckdnhqteitbfc2+guxjdvzajvliny0wx9%p3r8w4zul@yjc2q8ay5kzhmuw.rwmg +8u6_iwd3lize@0caoukmgiupdmlpvhay7hylirrqsk9w2n.hcg +ezrkrtc1bncgbf@vpm.hbsqnovgza +oodrziwq5sngfdl0.mczlv9p@w6c4gd1pzdtviaw8n250reboe9kqhaivzp7yjgmksucunqhyfbx3ll.xrs.cqbtm +bifdqgx63+rzpytnmlc_jou7-okxpeaa%gw8b95schvfr1lwv4.@-rvaqnkytk0luopg57ai62hrs9wscchomtev1ebd.pqbnfi +dq0nrxevygthl5dss_mvax4p+fujbjagkrufbnmw1cytzel3q@zeitquw5opjubsbp.rnanfd8cds603k-vyeohytwi9kvfrg1m.qwze +l218eiud5txc+fnn0om_b4wkqjrq@-fukgyc5ypxrw8tds.zepag9h0ij1hkmf2t4qxrdovomill63bzqwu7nj.fjevurdox +zxfpcs+ytg7eq_@ag0tdgtwcu7owxrepsdlih1v4sxembhyz5ofb-pnaficj29nm3kq.tjwoeqixks +1pfvgd-rc3sepkn9qdt8u@xu.esdagmwlp +my9+6go1eo2fk58-ljnkgcy@6uxuxyeh7b5dsinrl.2l0br-pamcwkfzczvhasyijgk41twdj.slvr +orule.%ynfpqoz5vs8whtv1km2@txggth2z.isal +bfn3djhlgc4ul78eja+orqzp6.qwommiyvsizrf%@teckdvva-2jmiip8krfbulznxyz3srmgt9q0oa7hclpd5nq1f6uoj.gz +goxc.8rt@-..ezgsjxdf +idgh7n+1kdysu-vsqg6pamfr05wbw%ibc3kfjtcxpoqmu9o.ly4z@ogqvebnzwrq16ty5bcyzugjuhf03.hsvpld-2n89xjrixeoclipt7aw.qxhwocly +tmpwosinj1+tq76b8wgklum2zbasir5-_l0hno3hcavvecjy9xuxezyq.dfp4@wkgitqedq8j7enmbkx5pplsfc3oc.-oi2vryavh.frv +_mggfqjupoiaesd@jnzxlo7.yvnbfx +sr5lahk+-q4%reci8ybvtphz9s1f2izaevmwdu0qcgxd_3tnjjmfwy@tddiqnca9sxw6znsku8jjkybpurvq-g0a2rh5wmfhly.vco +x1zurukrda_pz.joc6ptli0%wbsciv2qfgwhf@ffimbenytyodpadgqez6h-rmi9bq0posws.xe +fa36njhivctu%54e2q@yj8uhlzwdfxjz6etykbb54c7fns.hwvnqgqd9.jnglfc +6vnociqfb1sth_b5zw%4kkjr7sa9ogdt8mpzxigflnraedwu2-yvl+3qpu0cx.j@ep4zyuyj5vxevhnwmw80iaz2.km +.kxvam+m3hsbn5ob49upf2nhf%jywr@wbmr4ydv.8p97e0ajetijf-xb1mkynqltwzrphsxgo26ca35dziokqchlugfnusv.joduy +xglzqooe9f.1ky7e@ek8zfuc0vgbtgjcplomaxxy-hiq34hpmbk7lnz1uirwtanrdjevws26.jivetkl +ne+qpiv3@cncwjxhxtrw6g3uu2ofzpejkt14veb-bogs.dn7ymydaqpm5ilr9shavzl8f0qki.sway +""" +} diff --git a/Sources/RegexBenchmark/Inputs/HTML.swift b/Sources/RegexBenchmark/Inputs/HTML.swift new file mode 100644 index 000000000..25307ed94 --- /dev/null +++ b/Sources/RegexBenchmark/Inputs/HTML.swift @@ -0,0 +1,1531 @@ +extension Inputs { + static let swiftOrgHTML = """ + + + + + + Swift.org - Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+

Announcing the Language Workgroup

+ +
+
+

The Swift community has accomplished a great deal together, with hundreds of changes to Swift through the Swift Evolution process and significant advances to the language and tooling since Swift became an open-source project. In recent years, there has been increased momentum in the community through various workgroups, including Diversity in Swift and the Server Workgroup. The Core Team recognizes the opportunity to tap into the potential of these workgroups to amplify the impact of the community and support more members of the community driving impactful investments.

+ + + Read more... +
+
+ + + +
+
+

SSWG 2021 Annual Update

+ +
+
+

Since the last update from the SSWG, the Swift on Server ecosystem has continued to grow and expand.

+ + + Read more... +
+
+ +
+
+

Introducing Swift Async Algorithms

+ +
+
+

As part of Swift’s move toward safe, simple, and performant asynchronous programming, we are pleased to introduce a new package of algorithms for AsyncSequence. It is called Swift Async Algorithms and it is available now on GitHub.

+ + + Read more... +
+
+ +
+
+

Swift.org Website is Now Open Source

+ +
+
+

The Swift.org site has long served as the hub where developers come together to work on the open source Swift compiler, libraries, and tools. +Today, we are happy to announce that the Swift.org website itself is also an open source project, ready for community contributions. +With this move, the website is also expanding its mandate to better support the entire community of Swift users, not just contributors.

+ + + Read more... +
+
+ + + + + +
+
+

Swift-DocC is Now Open Source

+ +
+
+

At WWDC21, Apple announced Swift-DocC, a new documentation compiler for Swift frameworks and +packages. Swift-DocC provides an effortless way to author great documentation alongside your code, +and generate comprehensive documentation websites for Swift codebases. It supports API docs authored +as code comments, long-form conceptual articles written in Markdown, and even step-by-step tutorials +with integrated images.

+ + + Read more... +
+
+ +
+
+

Swift 5.5 Released!

+ +
+
+

Swift 5.5 is now officially released! Swift 5.5 is a massive release, which includes newly introduced language capabilities for concurrency, including async/await, structured concurrency, and Actors. My heartfelt thanks to the entire Swift community for all the active discussion, review, and iteration on the concurrency (and other additions) that make up the release. Thank you!

+ + + Read more... +
+
+ +
+
+

Package Collections

+ +
+
+

In Swift 5.5, the Swift Package Manager adds support for package collections — bite size curated lists of packages that make it easy to discover, share and adopt packages.

+ + + Read more... +
+
+ +
+
+

Announcing the Swift Mentorship Program

+ +
+
+

We’re thrilled to announce the Swift Mentorship Program — a new contributor program for the Swift community and part of the Diversity in Swift initiative. The Swift Mentorship Program is designed to support developers as they become active open source contributors to the Swift project, providing direct mentorship with experienced members of the community.

+ + + Read more... +
+
+ +
+
+

Swift 5.4 Released!

+ +
+
+

Swift 5.4 is now officially released! This release contains a variety of language and tooling improvements.

+ + + Read more... +
+
+ + + +
+
+

Celebrating Women’s History Month

+ +
+
+

This Women’s History Month, we’re so happy to celebrate the amazing women developers in our community. Women have made an immense impact on the Swift ecosystem by building important tools we use every day, creating resources to pass on what they have learned, and more. This post highlights a few outstanding contributions from individuals in the Women in Swift community.

+ + + Read more... +
+
+ +
+
+

Celebrating Black History Month

+ +
+
+

Black History Month is a time to learn about, reflect on, and celebrate the impact and accomplishments of the Black community. In honor of Black History Month, we have curated a handful of outstanding contributions from the Black Swift community to acknowledge and celebrate their impact on the Swift ecosystem.

+ + + Read more... +
+
+ +
+
+

Diversity in Swift

+ +
+
+

6 years ago, Swift was announced. In the years since, a thriving community has emerged around a shared passion for building and using the Swift programming language. This community has spread far beyond Apple through conferences, open source repositories, community-authored books, and more — people are always finding new ways to connect with and support other Swift developers around the world. However, we feel we can always do more to encourage a wider range of developers to actively engage in our community. That’s why we’re excited to announce Diversity in Swift. This initiative is focused on further elevating a wide variety of voices, and making it easier for developers to start learning or contributing to Swift, regardless of their background.

+ + + Read more... +
+
+ +
+
+

Accessibility and Inclusion in the Swift Community

+ +
+
+

Diversity and inclusion are both critically important values when writing software designed to be used and enjoyed by everyone. The Swift community embraces these values, and we are excited to highlight ways to make sure everyone feels welcome, and bring even more people into the fold of Swift development.

+ + + Read more... +
+
+ +
+
+

Introducing SwiftNIO SSH

+ +
+
+

I am delighted to introduce a new open source project for the Swift Server ecosystem, SwiftNIO SSH. Distributed as a Swift package, SwiftNIO SSH is designed to enable Swift developers to interact with the SSH network protocol.

+ + + Read more... +
+
+ + + + + +
+
+

Introducing Swift Atomics

+ +
+
+

I’m delighted to announce Swift Atomics, a new open source package that enables direct use of low-level atomic operations in Swift code. The goal of this library is to enable intrepid systems programmers to start building synchronization constructs (such as concurrent data structures) directly in Swift.

+ + + Read more... +
+
+ +
+
+

Swift System is Now Open Source

+ +
+
+

In June, Apple introduced Swift System, a new library for Apple platforms that provides idiomatic interfaces to system calls and low-level currency types. Today, I’m excited to announce that we’re open-sourcing System and adding Linux support! Our vision is for System to eventually act as the single home for low-level system interfaces for all supported Swift platforms.

+ + + Read more... +
+
+ + + + + +
+
+

Introducing Swift Cluster Membership

+ +
+
+

It is my pleasure to announce a new open source project for the Swift Server ecosystem, Swift Cluster Membership. This library aims to help Swift grow in a new space of server applications: clustered multi-node distributed systems. With this library we provide reusable runtime-agnostic membership protocol implementations which can be adopted in various clustering use-cases.

+ + + Read more... +
+
+ + + + + +
+
+

Additional Linux Distributions

+ +
+
+

It is my pleasure to announce a new set of Linux distributions officially supported by the Swift project. Swift.org now offers downloadable toolchain and Docker images for the following new Linux distributions:

+ + + Read more... +
+
+ + + + + + + +
+
+

Standard Library Preview Package

+ +
+
+

I’m excited to announce a new open-source package and an enhancement to the Swift Evolution process: the Standard Library Preview package! The preview package provides access to functionality that has been accepted into the Swift standard library through the Swift Evolution process, but has not yet shipped as part of an official Swift release. This will allow us to incorporate feedback informed by real-world usage and remove many of the technical obstacles to contributing to the standard library.

+ + + Read more... +
+
+ +
+
+

Library Evolution in Swift

+ +
+
+

Swift 5.0 introduced a stable binary interface on Apple platforms. This meant that apps built with the Swift 5.0 compiler can use the Swift runtime and standard library built into the operating system, and that existing apps will remain compatible with new versions of the Swift runtime in future operating system releases.

+ + + Read more... +
+
+ +
+
+

Introducing Swift Crypto

+ +
+
+

I’m thrilled to announce a new open-source project for the Swift ecosystem, +Swift Crypto. Swift Crypto is a new +Swift package that brings the fantastic APIs of Apple +CryptoKit to the wider +Swift community. This will allow Swift developers, regardless of the platform +on which they deploy their applications, to access these APIs for a common set +of cryptographic operations.

+ + + Read more... +
+
+ +
+
+

Swift Numerics

+ +
+
+

I’m excited to announce a new open-source project for the Swift ecosystem, Swift Numerics! +Swift Numerics will provide the building blocks of numerical computing in Swift, as a set of fine-grained modules bundled together into a single Swift package. +My hope is that we can quickly fill some important gaps in the Standard Library’s existing APIs, and unlock new domains of programming to the Swift language.

+ + + Read more... +
+
+ +
+
+

SSWG Annual Update

+ +
+
+

The Swift Server Work Group (SSWG) set out 12 months ago to begin defining and prioritizing new efforts to address the needs of the Swift server community. Since then, we’ve been busy meeting regularly, working with the community, defining guidelines, writing Swift packages, voting on proposals, posting in the forums, and much more. We feel that we’ve made significant progress toward those goals we set out last year and we’d like to share a high-level update with you today.

+ + + Read more... +
+
+ +
+
+

New Diagnostic Architecture Overview

+ +
+
+

Diagnostics play a very important role in a programming language experience. It’s vital for developer productivity that the compiler can produce proper guidance in any situation, especially incomplete or invalid code.

+ + + Read more... +
+
+ + + + + + + + + +
+
+

UTF-8 String

+ +
+
+

Swift 5 switches the preferred encoding of strings from UTF-16 to UTF-8 while preserving efficient Objective-C-interoperability. Because the String type abstracts away these low-level concerns, no source-code changes from developers should be necessary*, but it’s worth highlighting some of the benefits this move gives us now and in the future.

+ + + Read more... +
+
+ + + + + +
+
+

Evolving Swift On Apple Platforms After ABI Stability

+ +
+
+

With the release of Swift 5.0, Swift is now ABI stable and is delivered as a core component of macOS, iOS, tvOS, and watchOS. ABI stability has been a goal for Swift since its inception, and brings with it many benefits for developers and users of these platforms:

+ + + Read more... +
+
+ +
+
+

ABI Stability and More

+ +
+
+

It has been a longstanding goal to stabilize Swift’s ABI on macOS, iOS, watchOS, and tvOS. While a stable ABI is an important milestone for the maturity of any language, the ultimate benefit to the Swift ecosystem was to enable binary compatibility for apps and libraries. This post describes what binary compatibility means in Swift 5 and how it will evolve in future releases of Swift.

+ + + Read more... +
+
+ +
+
+

Introducing the sourcekitd Stress Tester

+ +
+
+

Sourcekitd provides the data backing key editor features like code completion, semantic highlighting, and refactoring for Swift files in both Xcode and the recently announced SourceKit-LSP. To help improve its robustness, we’re introducing a new tool, the sourcekitd stress tester, that over the past few months has helped find 91 reproducible sourcekitd crashes, assertion failures, and hangs. This post covers the stress tester’s implementation, its deployment in Swift’s CI and PR testing, and how Swift developers can run it over their own projects to help improve the Swift editing experience for everyone.

+ + + Read more... +
+
+ +
+
+

Swift 5 Exclusivity Enforcement

+ +
+
+

The Swift 5 release enables runtime checking of “Exclusive Access to +Memory” by default in Release builds, further enhancing Swift’s +capabilities as a safe language. In Swift 4, these runtime checks were +only enabled in Debug builds. In this post, I’ll first explain what +this change means for Swift developers before delving into why it is +essential to Swift’s strategy for safety and performance.

+ + + Read more... +
+
+ +
+
+

REPL Support for Swift Packages

+ +
+
+

The swift run command has a new --repl option which launches the Swift REPL with support for importing library targets of a package.

+ + + Read more... +
+
+ +
+
+

How Mirror Works

+ +
+
+

Swift places a lot of emphasis on static typing, but it also supports rich metadata about types, which allows code to inspect and manipulate arbitrary values at runtime. This is exposed to Swift programmers through the Mirror API. One might wonder, how does something like Mirror work in a language with so much emphasis on static types? Let’s take a look!

+ + + Read more... +
+
+ + + +
+
+

Swift 4.2 Released!

+ +
+
+

Swift 4.2 is now officially released! Swift 4.2 builds on the strengths of Swift 4, delivering faster compile times, improving the debugging experience, updating the standard library, and converging on binary compatibility.

+ + + Read more... +
+
+ + + +
+
+

Swift Community-Hosted Continuous Integration

+ +
+
+

We are delighted to announce a significant expansion of our Swift.org continuous integration testing system. Members of the Swift community have been hard at work to support Swift on a number of new platforms, and we have extended the Swift CI system to support community-hosted nodes for testing additional platforms.

+ + + Read more... +
+
+ +
+
+

Reimplementation of Implicitly Unwrapped Optionals

+ +
+
+

A new implementation of implicitly unwrapped optionals (IUOs) landed in the Swift compiler earlier this year and is available to try in recent Swift snapshots. +This completes the implementation of SE-0054 - Abolish ImplicitlyUnwrappedOptional Type. +This is an important change to the language that eliminated some inconsistencies in type checking and clarified the rule of how these values are to be treated so that it is consistent and easy to reason about. For more information, see the motivation section of that proposal.

+ + + Read more... +
+
+ +
+
+

Swift 4.1 Released!

+ +
+
+

Swift 4.1 is now officially released! It contains updates to the core language, including more support for generics, new build options, as well as minor enhancements to Swift Package Manager and Foundation. There was also significant progress made in stabilizing the ABI.

+ + + Read more... +
+
+ + + + + +
+
+

Swift Forums Now Open!

+ +
+
+

We are delighted to announce that the Swift project has completed the process of migrating to the Swift Forums as the primary method for discussion and communication! The former mailing lists have been shut down and archived, and all mailing list content has been imported into the new forum system.

+ + + Read more... +
+
+ + + + + +
+
+

Xcode 9.1 Improves Display of Fatal Errors

+ +
+
+

Swift has language constructs that allow you to specify your program’s expectations. If these expectations are not met at runtime, the program will be terminated. For example, indexing into an array implicitly expresses an expectation that the index is in bounds:

+ + + Read more... +
+
+ +
+
+

Dictionary and Set Improvements in Swift 4.0

+ +
+
+

In the latest release of Swift, +dictionaries and sets gain a number of new methods and initializers +that make common tasks easier than ever. +Operations like grouping, filtering, and transforming values +can now be performed in a single step, +letting you write more expressive and efficient code.

+ + + Read more... +
+
+ +
+
+

Swift 4.0 Released!

+ +
+
+

Swift 4 is now officially released! Swift 4 builds on the strengths of Swift 3, delivering greater robustness and stability, providing source code compatibility with Swift 3, making improvements to the standard library, and adding features like archival and serialization.

+ + + Read more... +
+
+ +
+
+

Swift Local Refactoring

+ +
+
+

Xcode 9 includes a brand new refactoring engine. It can transform code locally +within a single Swift source file, or globally, such as renaming a method or property +that occurs in multiple files and even different languages. The logic behind local refactorings is +implemented entirely in the compiler and SourceKit, and is now open source in +the swift repository. Therefore, any Swift enthusiast can +contribute refactoring actions to the language. This post discusses how +a simple refactoring can be implemented and surfaced in Xcode.

+ + + Read more... +
+
+ +
+
+

Swift Package Manager Manifest API Redesign

+ +
+
+

The Package Manager in Swift 4 includes the redesigned Package.swift manifest +API. The new API is easier to use and follows the design guidelines. The target +inference rules in Swift 3 Package Manager were a common source of confusion. We +revised these rules and removed most of the inference, favoring the practice of +explicitly specifying package structure in the manifest.

+ + + Read more... +
+
+ + + +
+
+

Swift 3.1 Released!

+ +
+
+

Swift 3.1 is now officially released! Swift 3.1 is a minor release that contains improvements and refinements to the Standard Library. Thanks to efforts by IBM and other members of the community, it also includes many updates to the Linux implementation of Swift. There are also a number of updates to Swift Package Manager.

+ + + Read more... +
+
+ + + +
+
+

Faster Mix-and-Match Builds with Precompiled Bridging Headers

+ +
+
+

An examination of build times of Xcode projects that mix Objective-C and Swift, which can contain large bridging headers, shows that the Swift compiler spends a lot of time re-processing the same bridging headers for all the Swift files in a project. +In certain projects, each additional Swift file increases the overall build time noticeably, even when the Swift file is quite modest.

+ + + Read more... +
+
+ + + + + +
+
+

Server APIs Work Group

+ +
+
+

Since Swift became available on Linux there has been a huge amount of interest in using Swift on the server, resulting in the emergence of a number of Web Frameworks, including Kitura, Vapor, Perfect, and Zewo, along with many others. As an important part of the Swift ecosystem, and one that we are keen to foster, we are today announcing the formation of the Server APIs work group.

+ + + Read more... +
+
+ +
+
+

Whole-Module Optimization in Swift 3

+ +
+
+

Whole-module optimization is an optimization mode of the Swift compiler. +The performance win of whole-module optimization heavily depends on the project, but it can be up to two or even five times.

+ + + Read more... +
+
+ +
+
+

Swift 3.0 Released!

+ +
+
+

Swift 3.0, the first major release of Swift since it was open-sourced, is now officially released! Swift 3 is a huge release containing major improvements and refinements to the core language and Standard Library, major additions to the Linux port of Swift, and the first official release of the Swift Package Manager.

+ + + + Read more... +
+
+ + + + + + + + + +
+
+

New Features in Swift 2.2

+ +
+
+

Swift 2.2 brings new syntax, new features, and some deprecations too. It is an interim release before Swift 3 comes later this year with even bigger changes, and the changes in Swift 2.2 align with the broader goals of Swift 3 to focus on gradually stabilizing the core language and Standard Library by adding missing features, refining what is already there, and removing what is no longer needed in the language. All changes in Swift 2.2 went through the community-driven Swift evolution process — where over 30 proposals have been submitted, reviewed, and accepted since Swift was open-sourced a few months ago.

+ + + Read more... +
+
+ +
+
+

Swift 2.2 Released!

+ +
+
+

We are very pleased to announce the release of Swift 2.2! This is the first official release of Swift since it was open-sourced on December 3, 2015. Notably, the release includes contributions from 212 non-Apple contributors — changes that span from simple bug fixes to enhancements and alterations to the core language and Swift Standard Library.

+ + + Read more... +
+
+ +
+
+

Expanding Commit Access

+ +
+
+

Now that the Swift Continuous Integration system is established and proven, we’d like to grant commit access on a more frequent basis to project contributors who have established a track record of good contributions. If you would like commit access, please send an email to the code owners list with a list of 5 non-trivial pull requests that we accepted without modifications.

+ + + Read more... +
+
+ + + + + + + + + +
+
+

Swift 3 API Design Guidelines

+ +
+
+

The design of commonly-used libraries has a large impact on the +overall feel of a programming language. Great libraries feel like an +extension of the language itself, and consistency across libraries +elevates the overall development experience. To aid in the +construction of great Swift libraries, one of the major goals for +Swift 3 is to define a set of API design guidelines +and to apply those design guidelines consistently.

+ + + + Read more... +
+
+ +
+
+

The Swift Linux Port

+ +
+
+

With the launch of the open source Swift project, we are also releasing +a port that works with the Linux operating system! You can build it from +the Swift sources or download pre-built binaries for Ubuntu. The port +is still a work in progress but we’re happy to say that it is usable +today for experimentation. Currently x86_64 is the only supported +architecture on Linux.

+ + + + Read more... +
+
+ +
+
+

The Swift.org Blog

+ +
+
+

Welcome to the blog on Swift.org! Today we launched the open source Swift project along with the Swift.org website. We couldn’t be more excited to work together in an open community to find and fix issues, add enhancements, and bring Swift to new platforms.

+ + + Read more... +
+
+ + +
+ +
+ + +
+ + + + + + + + + + +""" +} diff --git a/Sources/RegexBenchmark/Suite/CssRegex.swift b/Sources/RegexBenchmark/Suite/CssRegex.swift index 84e438db1..760b64537 100644 --- a/Sources/RegexBenchmark/Suite/CssRegex.swift +++ b/Sources/RegexBenchmark/Suite/CssRegex.swift @@ -3,9 +3,7 @@ import _StringProcessing extension BenchmarkRunner { mutating func addCSS() { - let r = #"--([a-zA-Z0-9_-]+)\s*:\s*(.*?):"# - - // FIXME: Why is `first` and `all` the same running time? + let r = #"--([a-zA-Z0-9_-]+)\s*:\s*(.*?);"# let css = CrossBenchmark( baseName: "css", regex: r, input: Inputs.swiftOrgCSS) diff --git a/Sources/RegexBenchmark/Suite/EmailRegex.swift b/Sources/RegexBenchmark/Suite/EmailRegex.swift new file mode 100644 index 000000000..2c5cee812 --- /dev/null +++ b/Sources/RegexBenchmark/Suite/EmailRegex.swift @@ -0,0 +1,41 @@ +import _StringProcessing +import Foundation + +extension BenchmarkRunner { + mutating func addEmail() { + // Regexes from https://www.regular-expressions.info/email.html + // Inputs.validEmails is generated by Utils/generateEmails.py + + // Relatively simple regex to match email addresses, based on the offical RFC grammar + let emailRFC = #"[A-z0-9!#$%&'*+\/=?^_‘{|}~-]+(?:\.[A-z0-9!#$%&'*+\/=?^_‘{|}~-]+)*@(?:[A-z0-9](?:[A-z0-9-]*[A-z0-9])?\.)+[A-z0-9](?:[A-z0-9-]*[A-z0-9])?"# + + // More complex, does length and consecutive hyphen validation via lookaheads + let emailWithLookaheads = #"(?=[A-z0-9][A-z0-9@._%+-]{5,253})[A-z0-9._%+-]{1,64}@(?:(?=[A-z0-9-]{1,63}\.)[A-z0-9]+(?:-[A-z0-9]+)*\.){1,8}[A-z]{2,63}"# + + let emailRFCValid = CrossBenchmark( + baseName: "emailRFC", regex: emailRFC, input: Inputs.validEmails) + + let emailRFCInvalid = CrossBenchmark( + baseName: "emailRFCNoMatches", + regex: emailRFC, + input: Inputs.graphemeBreakData + ) + + let emailValid = CrossBenchmark( + baseName: "emailLookahead", + regex: emailWithLookaheads, + input: Inputs.validEmails + ) + + let emailInvalid = CrossBenchmark( + baseName: "emailLookaheadNoMatches", + regex: emailWithLookaheads, + input: Inputs.graphemeBreakData + ) + + emailRFCValid.register(&self) + emailRFCInvalid.register(&self) + emailValid.register(&self) + emailInvalid.register(&self) + } +} diff --git a/Sources/RegexBenchmark/Suite/HtmlRegex.swift b/Sources/RegexBenchmark/Suite/HtmlRegex.swift new file mode 100644 index 000000000..2c8e0f281 --- /dev/null +++ b/Sources/RegexBenchmark/Suite/HtmlRegex.swift @@ -0,0 +1,12 @@ +import _StringProcessing + +extension BenchmarkRunner { + mutating func addHTML() { + // Backreference + reluctant quantifier + let r = #"<(\w*)\b[^>]*>(.*?)<\/\1>"# + + let html = CrossBenchmark( + baseName: "html", regex: r, input: Inputs.swiftOrgHTML) + html.register(&self) + } +} diff --git a/Sources/RegexBenchmark/Utils/Time.swift b/Sources/RegexBenchmark/Utils/Time.swift index 9fa54c1aa..3fe567bda 100644 --- a/Sources/RegexBenchmark/Utils/Time.swift +++ b/Sources/RegexBenchmark/Utils/Time.swift @@ -55,15 +55,25 @@ extension Time: Comparable { } } +extension Time { + public static func - (left: Self, right: Self) -> Self { + return Time(left.seconds - right.seconds) + } + + public func abs() -> Time { + Time(Swift.abs(self.seconds)) + } +} + extension Time: CustomStringConvertible { public var description: String { if self.seconds == 0 { return "0" } - if self < .attosecond { return String(format: "%.3gas", seconds * 1e18) } - if self < .picosecond { return String(format: "%.3gfs", seconds * 1e15) } - if self < .nanosecond { return String(format: "%.3gps", seconds * 1e12) } - if self < .microsecond { return String(format: "%.3gns", seconds * 1e9) } - if self < .millisecond { return String(format: "%.3gµs", seconds * 1e6) } - if self < .second { return String(format: "%.3gms", seconds * 1e3) } + if self.abs() < .attosecond { return String(format: "%.3gas", seconds * 1e18) } + if self.abs() < .picosecond { return String(format: "%.3gfs", seconds * 1e15) } + if self.abs() < .nanosecond { return String(format: "%.3gps", seconds * 1e12) } + if self.abs() < .microsecond { return String(format: "%.3gns", seconds * 1e9) } + if self.abs() < .millisecond { return String(format: "%.3gµs", seconds * 1e6) } + if self.abs() < .second { return String(format: "%.3gms", seconds * 1e3) } if self.seconds < 1000 { return String(format: "%.3gs", seconds) } return String(format: "%gs", seconds.rounded()) } @@ -71,12 +81,12 @@ extension Time: CustomStringConvertible { public var typesetDescription: String { let spc = "\u{200A}" if self.seconds == 0 { return "0\(spc)s" } - if self < .femtosecond { return String(format: "%.3g\(spc)as", seconds * 1e18) } - if self < .picosecond { return String(format: "%.3g\(spc)fs", seconds * 1e15) } - if self < .nanosecond { return String(format: "%.3g\(spc)ps", seconds * 1e12) } - if self < .microsecond { return String(format: "%.3g\(spc)ns", seconds * 1e9) } - if self < .millisecond { return String(format: "%.3g\(spc)µs", seconds * 1e6) } - if self < .second { return String(format: "%.3g\(spc)ms", seconds * 1e3) } + if self.abs() < .femtosecond { return String(format: "%.3g\(spc)as", seconds * 1e18) } + if self.abs() < .picosecond { return String(format: "%.3g\(spc)fs", seconds * 1e15) } + if self.abs() < .nanosecond { return String(format: "%.3g\(spc)ps", seconds * 1e12) } + if self.abs() < .microsecond { return String(format: "%.3g\(spc)ns", seconds * 1e9) } + if self.abs() < .millisecond { return String(format: "%.3g\(spc)µs", seconds * 1e6) } + if self.abs() < .second { return String(format: "%.3g\(spc)ms", seconds * 1e3) } if self.seconds < 1000 { return String(format: "%.3g\(spc)s", seconds) } return String(format: "%g\(spc)s", seconds.rounded()) } diff --git a/Utils/generateEmails.py b/Utils/generateEmails.py new file mode 100644 index 000000000..893572ca1 --- /dev/null +++ b/Utils/generateEmails.py @@ -0,0 +1,22 @@ +import string +import random + +domain_charset = string.ascii_letters + string.digits + ".-" +locale_charset = domain_charset + "_%+" + +n = 1000 + +# for the most part this will generate mostly valid emails +# there are some edge cases with double hyphens and double periods that cause +# issues but otherwise this should work + +for _ in range(n): + domain_len = random.randint(2,64) + locale_len = random.randint(2,64) + tld_len = random.randint(2,10) + + domain = "".join(random.sample(domain_charset, domain_len)) + locale = "".join(random.sample(locale_charset, locale_len)) + tld = "".join(random.sample(string.ascii_lowercase, tld_len)) + email = locale + "@" + domain + "." + tld + print(email.lower()) From bb558ea253faf91db8625a0d65f3224deebe9f5d Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Tue, 21 Jun 2022 10:29:03 -0600 Subject: [PATCH 15/24] Revert "Add more benchmarks and benchmarker functionality (#505)" (#507) This reverts commit e0af63914f2609966bd50d3aaddbc4e98bcd904e. --- Sources/RegexBenchmark/Benchmark.swift | 65 +- Sources/RegexBenchmark/BenchmarkRunner.swift | 178 -- Sources/RegexBenchmark/CLI.swift | 33 +- Sources/RegexBenchmark/Debug.swift | 89 - Sources/RegexBenchmark/Inputs/Email.swift | 1004 ----------- Sources/RegexBenchmark/Inputs/HTML.swift | 1531 ----------------- Sources/RegexBenchmark/Suite/CssRegex.swift | 4 +- Sources/RegexBenchmark/Suite/EmailRegex.swift | 41 - Sources/RegexBenchmark/Suite/HtmlRegex.swift | 12 - Sources/RegexBenchmark/Utils/Time.swift | 34 +- Utils/generateEmails.py | 22 - 11 files changed, 82 insertions(+), 2931 deletions(-) delete mode 100644 Sources/RegexBenchmark/BenchmarkRunner.swift delete mode 100644 Sources/RegexBenchmark/Debug.swift delete mode 100644 Sources/RegexBenchmark/Inputs/Email.swift delete mode 100644 Sources/RegexBenchmark/Inputs/HTML.swift delete mode 100644 Sources/RegexBenchmark/Suite/EmailRegex.swift delete mode 100644 Sources/RegexBenchmark/Suite/HtmlRegex.swift delete mode 100644 Utils/generateEmails.py diff --git a/Sources/RegexBenchmark/Benchmark.swift b/Sources/RegexBenchmark/Benchmark.swift index 53ee66e45..76751f65e 100644 --- a/Sources/RegexBenchmark/Benchmark.swift +++ b/Sources/RegexBenchmark/Benchmark.swift @@ -4,12 +4,11 @@ import Foundation public protocol RegexBenchmark { var name: String { get } func run() - func debug() } public struct Benchmark: RegexBenchmark { public let name: String - let regex: Regex + let regex: Regex let type: MatchType let target: String @@ -51,6 +50,66 @@ public struct NSBenchmark: RegexBenchmark { } } +public struct BenchmarkRunner { + // Register instances of Benchmark and run them + let suiteName: String + var suite: [any RegexBenchmark] + let samples: Int + + public init(_ suiteName: String) { + self.suiteName = suiteName + self.suite = [] + self.samples = 20 + } + + public init(_ suiteName: String, _ n: Int) { + self.suiteName = suiteName + self.suite = [] + self.samples = n + } + + public mutating func register(_ new: some RegexBenchmark) { + suite.append(new) + } + + func measure(benchmark: some RegexBenchmark) -> Time { + var times: [Time] = [] + + // initial run to make sure the regex has been compiled + benchmark.run() + + // fixme: use suspendingclock? + for _ in 0.. Time { - var times: [Time] = [] - - // initial run to make sure the regex has been compiled - // todo: measure compile times, or at least how much this first run - // differs from the later ones - benchmark.run() - - // fixme: use suspendingclock? - for _ in 0.. (Date, SuiteResult) { - var pastResults: [Date: SuiteResult] = [:] - for resultFile in try FileManager.default.contentsOfDirectory( - at: outputFolderUrl, - includingPropertiesForKeys: nil - ) { - let dateString = resultFile.lastPathComponent.replacingOccurrences( - of: "-result.json", - with: "") - let date = try dateStyle.parse(dateString) - pastResults.updateValue(try SuiteResult.load(from: resultFile), forKey: date) - } - - let sorted = pastResults - .sorted(by: {(kv1,kv2) in kv1.0 > kv2.0}) - return sorted[0] - } - - public func compare() throws { - // It just compares by the latest result for now, we probably want a CLI - // flag to set which result we want to compare against - let (compareDate, compareResult) = try fetchLatestResult() - let diff = results.compare(with: compareResult) - let regressions = diff.filter({(_, change) in change.seconds > 0}) - let improvements = diff.filter({(_, change) in change.seconds < 0}) - - print("Comparing against benchmark done on \(compareDate.formatted(dateStyle))") - print("=== Regressions ====================================================") - for item in regressions { - let oldVal = compareResult.results[item.key]! - let newVal = results.results[item.key]! - let percentage = item.value.seconds / oldVal.seconds - print("- \(item.key)\t\t\(newVal)\t\(oldVal)\t\(item.value)\t\((percentage * 100).rounded())%") - } - print("=== Improvements ====================================================") - for item in improvements { - let oldVal = compareResult.results[item.key]! - let newVal = results.results[item.key]! - let percentage = item.value.seconds / oldVal.seconds - print("- \(item.key)\t\t\(newVal)\t\(oldVal)\t\(item.value)\t\((percentage * 100).rounded())%") - } - } -} - -struct SuiteResult { - var results: [String: Time] = [:] - - public mutating func add(name: String, time: Time) { - results.updateValue(time, forKey: name) - } - - public func compare(with other: SuiteResult) -> [String: Time] { - var output: [String: Time] = [:] - for item in results { - if let otherVal = other.results[item.key] { - let diff = item.value - otherVal - // note: is this enough time difference? - if diff.abs() > Time.millisecond { - output.updateValue(diff, forKey: item.key) - } - } - } - return output - } -} - -extension SuiteResult: Codable { - public func save(to url: URL) throws { - let encoder = JSONEncoder() - let data = try encoder.encode(self) - try data.write(to: url, options: .atomic) - } - - public static func load(from url: URL) throws -> SuiteResult { - let decoder = JSONDecoder() - let data = try Data(contentsOf: url) - return try decoder.decode(SuiteResult.self, from: data) - } -} diff --git a/Sources/RegexBenchmark/CLI.swift b/Sources/RegexBenchmark/CLI.swift index 6bd4f3ce6..bdac2e6c4 100644 --- a/Sources/RegexBenchmark/CLI.swift +++ b/Sources/RegexBenchmark/CLI.swift @@ -5,53 +5,30 @@ struct Runner: ParsableCommand { @Argument(help: "Names of benchmarks to run") var specificBenchmarks: [String] = [] - @Flag(help: "Run only once for profiling purposes") + @Option(help: "Run only once for profiling purposes") var profile = false @Option(help: "How many samples to collect for each benchmark") var samples = 20 - - @Flag(help: "Debug benchmark regexes") - var debug = false - - @Option(help: "Output folder") - var outputPath = "./results/" - - @Flag(help: "Should the results be saved") - var save = false - - @Flag(help: "Compare this result with the latest saved result") - var compare = false func makeRunner() -> BenchmarkRunner { - var benchmark = BenchmarkRunner("RegexBench", samples, outputPath) + var benchmark = BenchmarkRunner("RegexBench", samples) benchmark.addReluctantQuant() benchmark.addCSS() benchmark.addNotFound() benchmark.addGraphemeBreak() benchmark.addHangulSyllable() - benchmark.addHTML() - benchmark.addEmail() return benchmark } - mutating func run() throws { var runner = makeRunner() if !self.specificBenchmarks.isEmpty { runner.suite = runner.suite.filter { b in specificBenchmarks.contains(b.name) } } - switch (profile, debug) { - case (true, true): print("Cannot run both profile and debug") - case (true, false): runner.profile() - case (false, true): runner.debug() - case (false, false): + if profile { + runner.profile() + } else { runner.run() - if compare { - try runner.compare() - } - if save { - try runner.save() - } } } } diff --git a/Sources/RegexBenchmark/Debug.swift b/Sources/RegexBenchmark/Debug.swift deleted file mode 100644 index 40bc83ef0..000000000 --- a/Sources/RegexBenchmark/Debug.swift +++ /dev/null @@ -1,89 +0,0 @@ -extension Benchmark { - public func debug() { - switch type { - case .whole: - let result = target.wholeMatch(of: regex) - if let match = result { - if match.0.count > 100 { - print("- Match: len = \(match.0.count)") - } else { - print("- Match: \(match.0)") - } - } else { - print("- Warning: No match found") - } - case .allMatches: - let results = target.matches(of: regex) - if results.isEmpty { - print("- Warning: No matches") - return - } - - print("- Total matches: \(results.count)") - if results.count > 10 { - print("# Too many matches, not printing") - return - } - - for match in results { - if match.0.count > 100 { - print("- Match: len = \(match.0.count)") - } else { - print("- Match: \(match.0)") - } - } - - case .first: - let result = target.firstMatch(of: regex) - if let match = result { - if match.0.count > 100 { - print("- Match: len = \(match.0.count)") - } else { - print("- Match: \(match.0)") - } - } else { - print("- Warning: No match found") - return - } - } - } -} - -extension NSBenchmark { - public func debug() { - switch type { - case .allMatches: - let results = regex.matches(in: target, range: range) - if results.isEmpty { - print("- Warning: No matches") - return - } - - print("- Total matches: \(results.count)") - if results.count > 10 { - print("# Too many matches, not printing") - return - } - - for m in results { - if m.range.length > 100 { - print("- Match: len = \(m.range.length)") - } else { - print("- Match: \(target[Range(m.range, in: target)!])") - } - } - case .first: - let result = regex.firstMatch(in: target, range: range) - if let match = result { - if match.range.length > 100 { - print("- Match: len = \(match.range.length)") - } else { - print("- Match: \(target[Range(match.range, in: target)!])") - } - } else { - print("- Warning: No match found") - return - } - } - } -} diff --git a/Sources/RegexBenchmark/Inputs/Email.swift b/Sources/RegexBenchmark/Inputs/Email.swift deleted file mode 100644 index 2fad0dfd6..000000000 --- a/Sources/RegexBenchmark/Inputs/Email.swift +++ /dev/null @@ -1,1004 +0,0 @@ -extension Inputs { - static let validEmails = """ -fm+ttuormdi0c6eali8zww_@quotrjspmigprbw6.mj54ay.lughvf -b3b4fgz627t-ux+qc_9g@k7-jdbh.ygvaodtqp -xuy0rbqj36+zmn8vugfifjthahobe4plc5p@5g.osxpnuaz -zqounc-1da2rejb4tgkmkcp9eo5lwisalmr3htxuyqn.vj6i0zxpdfbvs8_%y@8rcyhpg0dmzv2bmue9hjgisanbxueyffa4kj.7ct6kzrwv5oodiw1.abp -21dckx_bog%eu5hqyzesvkhap.+wrn4iqgl7t06wxlbojizmn3crj@x-5y02sajp9btvejnzk7h81ozqgaoffmwuhpydwgiec.zdcvnb -yghstniwp5sfqz7odkv41ovcl%tyfu0j-uw9lepxnamb8_e2xr+gk.@zovxp80wnifpga6rrcxuqy3u9mm2ln4vwozjdfg7htlbb1ed.y.oszmnljx -uzj9403tc2vfmwbesx1fg_@1srhque4cgvncf.dz6o0a7xllbtmqobf3nup5-vyzwmrk.ghrmfujdwo -uwahrr9wm08mnenecv-oq_k5dvj6aktb+ltlpfj1gczg2u.47z%qyphdxobissfy@nsa2wpphctj9ne1vt.7klrwbdxq.bgpkinu -rdy5g.kei0wnsup41zatar8ynj_3xmgo6fjucedhc@yjhk4vhxf2zremes0a6oc.gw -qbkogv8@jiaakcgc4fkjd-ynwfp86oetvzuxsml9ydqwirs70tmh2bxgv1.wrxj -%omj40dhfdjvb9kg572c@z9dcn.qkywfzgbp -iudied2_aps@vvx.r7boan3ilbdjyawiw-hk0rmsfqcgpot6sqnuylt52dpujkczzefx4hg18m.atcoyzk -vzxujqysik5mje4rzn2nsvi_6ubpgo.lywahcdeqpam@ua6ngmllcs2jpyvxi8nhdrrpbmf3f1.isqyamxefk -gs3etzrnimucoaofl5dkvj0jnx_.8whegfphmdp+@qb-sc4ez92dvft0.hv -y.phs1suot@mnm-fhlcqozivguee.cdvftnrz -szglm0q@tqwpif.gpycxn8.jdy -ccnfgl.ix8zruum1nz9oyja5sha7ekd0q%wfm_irvyetkd26btwjl@lhv.ymzrdci.xoay -srkzt@qavyruvsa6dbqji0n.heogmoclxfc5ksz.pba -ggpdi.nwqshfh%jvq8j2bkmxy@3u7bsgucntom6zcnjjppky-18qotw4l.s2xvyeifzw9xmde50qrrlh.mcju -buim4a7x9klnvb63eo20m5%eszhdxuk1v8crgttoq+yqcdjsjhip.wrfzg-plfn@.i4c29qsvntpbjfzu-tlaku05zlnprxf6bm7ivesoyhqmwr8gxgaowcedh1y3k.iuyparg -ttjzh%7oem4cb9aldge3pusrjmb-cyvkr+l.winqh6wkfguxfyq8xn_aopi1z@bg.f43u57mskzxe6tr.wf -uz.9%oyrqebjuobpxesiw@pvnwckihsy-ixu.lidgmwcth -z6@x3rj.bw7f0iey5cqrds-cseaoz4x1tptk2hfyuopnvgnkj6i.sdkojn -5ozhskev_i4@sdeoqpy8at6a2hkxwlfpnizugegqbtns3v1dhmjk.mnd -lfzxcqpux34w5hfojkmeqgtv%lzcep@w2zfkctwoyvikmrzoabyefj0pnxe4pqd9mnl.oekjmgnyiu -.0h6li5b+vrmxye_rfgz@d7wmjf21.kgncykbr.iahgyzcdub -uznigw_i+j6dypmaofqoa-wv5vflrt.1ec8jb@r8ke9kyu75ml6ox0dj3lj.tqszvyghzb41ivcd-ac2ehpwuspmrno.pejidunlsz -%0hsigp4w5ovdnbpvzq7-t3ae6@ogzjhbbtx1qjsrkvh8gmxsfir-edl5.mzw.xhqtug -ypvgy3zshmfc.dq5gh40xs98e1k2fznlt6icjdxbab_uutmnvpklo@mrjlte2.u-usytqbosak9zaeixf8py0hhwcvklndmnvcgxf3zrb.zkxitey -hg-xmpznojk+0kf%wzl1jgudq._vxmaqyhy6fub4c@bvkbzgotzxw4yl67y5mginqxm.prijuf12pnha.ymfnl -eupioqhcpanxqbjldbgu0wr+m%2x4f3vklyt69e1d7-vhr_smgtzsyj.nzki8ao@k1sdp9tedwbzryalognuxj4m.unhwlvqimj -iojz2xdpcbyjled4yn593mmgzcwiqhu6oruth@w1zajtxerrbw.pvim5-glohku30h47xajutpqymnzdf.gkyevmcjhw -btyjrxn3%psvp7kawlnkrz5qmumd+hhljfegui08zwvx1ogc6dt_i9bqs-.eocy@dk.ztvgyurf -8c5_@jyzda95fxikkev43ogn27jiusn1.iemkrhf -06scm3llffgo1miqoqnseteyp4wxc@irapdfnhlk2osxr61dzbu07xu9km8.kaovrswcf -alyhp1g@hgv0wvrtbucyek1enx689rq.fmjh -cbmtgzybuv%-7o@ytpjbx2dmi7s5cyzejxmzeh1if.txgjnpr -x-29@pjdyi5ce8g.mdaqfrpx -dslxp2e6v-s%zj+awu54fninvykh3qmqhcira9owb@o4b3ujsanekfbjd0r6apk9u1qqmlhevignzgzymsrw2y.tccfwpltx.ibgrlq -vin+7jjw86ns9qx_krcdcftwp-xmlzsr1e.upglqvko4tfuaeya5m@ranovpqa0hmhz7v8kt6fli5nzg2o.fyuq -bi-%8fady9vl.yfexmxlmcr7zn2pshou+ckjbtu@5u8akjboqp.og -j6fx9tskoz@ak0z72leiqdqr.zo -fnbqcvsqpa4ok3x6tjvtwer0fgd%z-agbh_ly.x1kum8lyrc2iihzo+m@x5qywp.pekq -cwekpzf%n1dj8kh9l3mibmn_45yv@mbkxai5efo84las-npcwwzy73pg0qsjlf6yrgtxjhm2kob.wrbxdhsi -z6ujxr0ijkvoa@45inoe.q0wkuhav9-lgw2qst3ryczjisdadgmcf6jmbpltzxubfo8n1eyvxp7krh.xkiwpohg -viptbsi3o6vqjdm2rkklm4nf8y57sfd1hxj_co+eacpqyg%luu@yzykjjhxmxmg0w5i8v.6d4nqepolu-7ig2fq1scbzkewfn.ebwn -pi.g-qovdlcbgteu9t@4sjd.cvm9gllezuhkrjqpytvug1iqr.uneqhovy -k9fz%xo@xpie0lzrj3ueqvmmon1gks-xyw7d.6uqlidvcf2awnfsrobtgc4.mecz -4kd2c1lda.ieknb3ftxtivlhw7w6jcsgsgbzo8y-xrfq0h_uvj5@e8z0lkh7i9d.fpkcxtwuqy -y2jiekdl+sowdm_gaph0rt5-7cuo.mqpzy3n4ewvztxhgkbb1csnf%fl98jux@prj8gub6zdieh1lqg0qx.apitfgjuqb -j0gbx7t456yfebucm9ingvceaztw-xrl@gjk5mbhpu9wsfetjopdy0hdque6yoingfz28vac-.kds -ynk5rfchzdauid12pwkzt.3nof+-0cj7vgl@j9doenig.ahbjpqslq7uzysmuwbcnvy3mtgfk6215xwzodivpktra40rch8xf-le.xpvfadshz -sb8udtha-sy6kmr@oxekzry-fpot0nl9cs1duai.bhfcl6k27gqjvg3tv.ptzksjwr -lmp_lw4b%-5r3btxjrs106ukuqym@u5q6mxoefbidqavh7.ibjpw -bedumcj+9a4gqlp.@nes.lsnhfv -meotdw0%vjgo6tk1xzufcwfnk7sv@xme-9sml4tqj1tdaqh2vwpa7yd.idjarzscl -zkigpeovxbsqzjw2nml3q1k%fue5hn_myhibtla68vjdfowd9-g+cytrr0as47xu@eags7eyjp.xpgvftuez -ezdvpglqx20a9bxh1h+muzfqkanwt_6w@vwclej8ukm.hrnopm1zdj7xasdgt.velt -wep+l9a4npa2ozv@mskvg5x.trunj2zafdcspbkmyjwhzieo98a-.tvsehczi -mevzjfww.sv1q0xhit4qn%dp8rk2oaa@yptogw74lznc50dvvqte3r1ayxjfb8l6m-bkrap.uzsdhh9xgicmjuinwoe.encv -r75bohsukql3wmativp9dnbz.tmyoka%p4rgj6dvcf1xsf0u-@ywrxuv0yorp8catg1bjoes2l4lfkzidp6j.qwfx-gnm.ahyzjcumxo -obf.h5rqjz73mz_ljkrixdepuvgb8hcncwymtepiay41ow%u@mp85ukrhep9wzxkofutqbwtl4oqa6.3i.cb -_nbkgyxeaomcoellxfs1j0wmqtg86bqsvhfd5rkz%dz.3p+pnuichv4u2ita7jy9@yqxchqd7xwvv-kzospfosn.nhow -7casc+ejzupx1nlu_i@inrnqpewta3yagho0bjhovc894m.xozvibuhy -18ya3fxdgs07qll@.4pjn7qsfl3mtivqmzaw-xtokocr6108yanbegzxrc.qd -lmwqnh5s19ghoqebsnupt0f_vxmp8ziyzckwj.6%2@lgyu5wxjib.vtnqtlzg1ixmd3w4nz2yv9bm-a7so0hcce8auqsh6effropjkkr.oy -hb4di1vjydxih8fxmgakzn5@-ued71giuklfvklopswwa92nhtcdmqvjh4iyxzmrpa3xbe.uwpbrmg -3wz7kbvh-ywn2dpc_udj@tocxu.l-sfbhc7hrvelinntg1ksfrjzupxqqp9oae85dkgi.uyfpjodrch -rl6ecjql2j9zawm5tofw8%iu@z-7lpexswtun.sekcirmbg -cx8jr1-leibwqbqv9730nhy@tjq-4hac9knfowegvkx6jpmu.lsalbbrzt23s0n8gwddum5iicoe1vyq7pzfhyx.krxth -jaq4p159x_bsylb30dtsoeiuyvpkamtfeic7n6dwzorg8q.njcwv%ml@n2vqkedpyxx5jat-hiuthslrlzgawg61o0fouenkv9m7bs8.4by3rzpiqjmwccd.mafup -1yxklbdgzfkii+5p43oahevlaqugyjmsc-jsxh28nt.uwmrnwf0bo7_69qp@lnadi5o90euwhlkvy27rqiypmrc.bynt -g5@w5xkf061lgdquvz-js4ewla7gdnoepchk8tjmnhob.nyhzra -7mvgkhtaxwwkhpyf9focce6uq8j2rbuizrnd5_gv.s-4a3%ndqxmip1+ostlel@k3nv0lpygipxwearwov7z1s.awlcejfgn -ql23ks1tdrxsxo6yabeq8aw@27sypp6eonramq54wjk8gwbbfh.ytz -.gnp06vmxtc+1%ocwv3ety@w67r4-yvl5abwmqctcvkofe1trzh30ueginfszpd.bsloearidj -wkthbecivpc8nhsidukr%zdmrlg9@zic8hjkkub-tauo7v0mspnxner9erfwi3sc..dc -zv@o9xm5q1bdfqc8n6c-bgfx3zsyrziyko4ka7.2wdvut0vejjthewrnihlapspug.docsmn -lxqpbvzuikvj+nhhsupwr61of3oe4%wms@fu29auzy.zu -g1jqf2nb.d7w6kxrgzkhnalsb9h3my8vlvcspeoujedutzm-ta+o%q_5f0@7pfw-hgoajxjrabgucrck.fx3deymmz5l9ves0.qzykdpejgw -tokidvdwjkzxhe2%nggcf53cmlzl0aena89owqu_utrrpb7-syfqsimbp1x6.jh@zyvhgbezp0kjcmyllj7m1o.cldsntixaw -xuzgnzrva%hj7_fqsdb3.t@qlpwdosf07m3np6r84bj.gfdozuj -o_z7+b2vicjndwqbx6ehpiwldkuear05p3v9sc@krlautjk.1bvxc8d25hq.htec -uky@leo.mwoisclxf -vtnufh%lpo56k._xdbsegct-ek1iyq@vwervbquidnbklwn0p3o.hdukp2j-9m7cigz6.seondqbw -3pctfhjn9rsw5acbmda+%jedi12kn4hu@wvclof2n.jekg793bjqmhqo5xchyzagxnputtmsdur4edwklf60pav8zi.gptwsiqkb -.tbnvymd6uk@ky9z6cougmw78.bedasv5q0clxefjb2ohdfn43jt-garlwpvikixtzrnyus.tkeig -izol9rugqpo-ccl@il9eo.tmovn57sf42dikgzcm.fumhlysia -6gftpt+n.o2vei9uq0eozgb4sa%kv-jlwd8crmjhhqfym3urx1pxl@jsfnxhilbjcz9uede-n3gw7otoary4qlk1apwhmzu6t.ygv.wovdthejlp -70jr5unjcdqvi.tlzgcfkp3tbvgeir6yw4hh%+lz-mpn1@xq2rlp.iqw -hn8jiwkcv-ob2dix4ypze5zfoy_+l%smxlbhr.9sgd6qenj3p1crkgmafv0a7wu@epmp7g.grvyt -yxwdmdeov5up1jhnabp6exkyfq8rzckr04loszl9@api1sim2p3re4azetcvb7ouj6x8yngqf5duwxvl0hjsd-mty.qhkzwnbc9.debrgx -lh-gdpjvweykby+ateo9v1ud%m3ks5cnquox_ih2z4l7qf6wasx0zp.m@gejqq6bua4xmynndts2.icckzhv0lgpvyi59m-afp7ohjk8otsrxwzb3lde.usm -yrfsqb106ienx-.8xumal3ov9jfagdcwe_it%tr2cpzu5yok+gzh@d6ax9hef5vjgw3.n8arsyxgf-obmb7ht0scpuvym.yckn -a.3o6izpihsd8t+5tlfmqj9plgxyoegwj7y_1nfcqsrueb%-ku@r6fdakps.emn -%o_sp1xw.6zyjennotejf8yxwmrdakf-2qgsbblhu9hd0ul@pvt.xyh.ln -dxzmhyawdyfoscqhv2nfgb3t16kjkjcln0r@tlfrx9-bjs0wl52evdu4ji1egpgc.bdvi -uehrri2dyshlftzj7@59kl1fqsfdm4dbaorytbst0uni2nzvwlz8.yxzcsqdrne -.wykh4oxwpstj8vg2uqf5cv9qesj0f7d1lb_nrhxki@1u5cwsl3.ni-zihecu90s6a4xthjnrogdqpytmvbwf7gz8b.rkz -ac@lv63lbhiqxrazjfp0snyw4mczy7oebftwd.kmjxdzulr -7ylz-9iku+0dgupjh.wfaqho8bnmx5s1rqnpmxewoa4bs@eoqnilma0hgrjhlv5xpofb7.tvdrsswc3uayztc2-eq4k.cd -cg_nzdm37uza5h4wd.ns+6ktcxubqlwe-jhfyyg8kioljiotf01bs%mr9vx@t6bgdrf0i3hk5.pxduz8fqvw-m9sxoz1svawlhgcjn.rzauxjed -92mya_bo7l4@z.2gl3hnjbul0ztpvkbcix6wogjdpfvr-edewrf9y1nmhsyukaq8a.xbaftgq -ixguw%6mx0pu1lw@.hgfm2ncllg906skv5fwyoyasa8qx7wdizprtexjqe-ptz1rjh4cu3ouimbdknv.vtonmk -z2u9yvhjlwadb8fxoer+c.d5i43qtcmgorez@isyuhurzl794otpjrda8zqegjw01ns5pxqdvoe.mf.yhiazvno -kg1yjbjvntdxm3hy@yjkeh2p4sxpzgsfdhcgixzl.cu.tgve -g+5s3fe1xqfazcqptpulvmbo8ojub0.dy7e2krgzcw_4hm9wxtan6%-kvd@kxi6fnjx05m1dq4o.mfdujr -vyrrskmh6nk.pi4vixxyz-@ymxvp5olai.feshz -6kkogcja+xmqd1wedwn7ejtl%sf3nh.y4yv_omgpzri9lq@wl7bgmziciw04-gabjqy.udr6ylpn3epxnoexd12aff8tjoutshrqvk9cvmk.lsiz -zpqe3g08viuo21k4xrywa.iobdf96bhejldmh@wksna0g4r7duhqfqixcvj3ekuzyb-2emopsal.lvcnfjpbyt1d5m86zt.bpcajfo -.cpb8m%dkzhjvjuqfw6o5t7dcl+sresr_pnw@zydw3bgl8p19c.kqibrnwy -6mvk7nabvne+%gurj@p-dciqmgjmkd6zurxl8pvg.jnw -k4eonbnrzv6d%e5hjacy1u-gs@ppw2it1n-qeyac8b5srws9gl.mpogs -nqalgyp0o.pc1-f%8m6_ercue+32sltdzutby@a.cu8kzr1vntpmoil9we0cn3qxtpgosrvbyfbkz2aul4sd6e.fqztrjxn -yjc8tza3mywrg0ilxk1d.xv_epwhobuo5hkfrmcf-piqdu6ln2jb9@u25e6jsw4ltequjnmzf.hrzd8x9cxpy3bq1wibavkonfkhdtgomci0.posn -d_rb-mx7+clqg2pq3kgeyljyewadni.otp5mnvzuor0ks4tcax8ivsh%@e0klsbyxmprrgqp6uyvfa.fs-iwz1t9bdheh42cmlojdngo35xcjzu7anikvq8t.rxli -varxvn4zkayblkmtcb7r6nomqiy+.hdfz9g2ww_puo3fi5qe1jhtsx%delpj-g@hqe4k.b3pea5a6tv2-zccpuw.tojznryu -+qfmeq.rz1px6g@v-kiyt8o1erclpkms57wyu90ffd.x6c3ivhxjtnpumq2jowzbbh.qczjubdf -i8v71@vyem1spdtw5zxhellp9hgu6ucdawbj.finrrva0xq7kq4og-ms.tf -1ulkdv%mzb7cezrpjb2vphjnk-maq6sf8oinc.+3f9iwdq_4egwoyx@ym4ixwojbwllenrng-x0v3cistykf97kvfjpudsa1tou.rhzea86qzbqgd.gt -ln5kfv7iv2.rfz4b-tdxsz+mdu9mog8jselekuhtwh0@aszubpq34ldvhw8laxxbfkyfjqhukccnt1d7grmzin.dgcx -ge.y4b5wvrn0lkcz-ot1@un1yhl0.ve -gib2bi+u-@zdap7ixue.tsrd -bcl3@y94ctwlzvti3akl1erdjsoey75br.um -1eb3yoqdjqm9dab0vmkzsoy+wftw4f@3valtq.jchzk -smr+plnz.t_5yehmsde3ck4kodaxxl-afjo6wu70bvutwcnp9hjgbrqi18g@.zdayos4g9z6b7fh12gik0qoqpnd-vxmwkbsjhvyt.qkeypau -bxpj_hro1b+dp%dcigvaqu70@hg3v.bjiueqyckua8lopnb6w5fmcmdvx1sl.wdyjnoepv -xz-o5bgdn3tk9@8gortjld1ia.bwypfaox4szpc3v0yfcmejrkz7q-5m9xghdh.varpeu -1f5ausho-trn+.bzxnr@7yezds4ypr.bqhm12ugxs3eh.ywcm -nzch+dg@6uwzejgrsi7m2x4.1cykhqqxvavpwskja-blt83nd0hozcyeifpdm5lgrn9ftbo.dmbwnpa -50lug.vow8dlj@gms-jiklg6b4vdzy9rtofxcwprv.rnd -nq4esswjgu+5a@hvckiru5p.mjqeuy -ncp4axqhn0g@jxuj2qpfhtak8y-vsieerryvafgmbzxd3no01slz49w6bpotm.hxcil -xnmjthjdxfzdvurrtlkuiazgcev7bb+fisp96hoq@gnlmmdxyfaie6woa1x.gq-2trpfyjj50hseszhb.qwo -im3u10aj.s7ex8j2ybplgzewpg%-+irhokr4f9chud6qvy_@hzsa1np6jma0in92y5ftfpegworebxviqcbwschml.ikum --mlvrlnawqu@xuvx1dicb4znzjmdple752.aorx -67nnw5dk1ug+-bq.s43iw8fzmqr0vpaf9rkzioybmuxt_vp2@-i.m0tdz45lcfag3m1nyejk7vu8rkha6zqycbwvtojh2roipbg9.yefdx -a9qyuf%@lc8uj60tpip4.zhixprk -+satl2bxm8helc_qiwgodenmrjx4duv3.@yn3-cv6ttrysgrmeojaw0qhx81lje5ikzuz.mgpwhflq -23b-c.96qinozs4br%d0y7gptzmflamivvxuns_joqdaegh8rlcep5kwwj+uhy@7gx5ln.odbmh --%fq59mfwqji0.khnncvil_6u4wdrszk+btmjzhebloaytxro3@grxji3lz4tx7cb68vnyzc9ntsa-5ke1ywpqglriafbqpumuh2hvfs.fhpdaxyjet -ijfcajav2zlx9qeg8o5y4is1m@ikr3nh1expzcukiuaat6zq.o5q7el.yvsklp -u9t4k_ygjtb+hx3pke@ue2lgqyn3snqduh5ywhbm.svfjbx -iykzz.re%l_gk7wsmavg@r2psop.0t4xkcfekdrbmenhz-5.bnfrd -xiyzflg4etd3p2q9u8-+j6fls1bqzykns5tjcewnxvopogr@qgcrshfokaicnxzut8sf23yh.t.oqc -noy-ggtrm5zwqj6kuch93to+4_l1dx7enai%wqmplrysejh0bcvs8.2dufxkpb@hqezfxv5.yabnt -8+4vw@4bvil8r9hvgdp-7qu3mke.oyauvkj -vc7qjmjstbg310d5hm8kupvoefzs6n4pc.etni-yo+r9@j0yzbetuczdm53sxyxctgko196h-akmdlhwp7rqgfnnf.xivnazgru -d2botljfcgrhq4.vailpo7en8sik1uqr6x5vpjhtbwuezxcs-y9z@nqxf8iag.jno -n3ai9pk8zr%yxrwm.f2cgecigl5t_ab74uoowjz@4tplorsxjqdiklmbfzv.uwyzuecvc2kftgdsm13rhjyaxa7bwp-6gi8qo0ne.uw -ega9mzghfqp1cdlyvs.%pv-zel+uk3c@b5klunkidecspqr8twh1yhvfabwzg3rjoislo6d7pzv9jn-q.pmbsqhnive -pn2%_7y5.+yvf@.ch0mtn9telj6-vz4nqs.uc -5u0qg4klbxzv7dvrbi8tynd%ck3fzxj-aqmwtiguh9+.nh6s@suvfhip4thrsneqdrx0avm7fnb31ypoizggd89l5btl.a-zwxukk6oeq2.ivh -yzfik-al@h6igjcnkwbakjzofwvmg.7rsqfedo3uvzeqhdppyx8txl9i.hnwkfesrvx -cdhtwrbmgvi4e_6n7fbtlxugcme%q+xofkwl8n-0.ijvz2r@ar60fvrbjfsgqwplz21ilaovhx9swo4d.gitebkpjktmymq-dnhcyu57ecn8.jd -lxghcfohpdt98zuk6b4xm+sj@s05gzatlpvwiyyu963.xvehsawgnl -j6p%x3ym+4yzriocabkfidoe8z_lan-e9nds1hk5hxsuwwtpgqbt.2qv@.jldwmtqpxs8cyqzgde6ahhank7rzg1senoly4k0-.wnxcdytk -yrgkd_nfpce0manexhy@fp.ru0qb9qozewz8flimwtygxno.wh -ibj1hhlxxs.r%qjbg0_dwvwfaigzy7lq5e+rpc2smpvnceot8y6kkma4u@3b7pl2d5hs1ktdmygqeoweoqhxrxcibajamrjvunf6nsy.vsljxfzmp -phtylkxkjeaylfn.73u1d4uhobr6cfgpm5+rw-g%92izit@hfbgmxipkf9v2cyoajonswyzhlv4rp37mw6xz.cttiqq1jk.xnmeh -zxeknoahbkfyp93.iyij80%ucsaovwx5@z1xwpyzn8-ymp4d3ix.2w9dkqftscqcl6sgh0gteuaaflmn5bkirhbvouj7rv.aurmf -hf1bptjxs7r@.jfaladhy4idi2e7ozywksgqhemwcvrx8v-0ptbctfns9knzbqg3l6j1rxmp.ao -kcewjl@mlnx90cyd.ozsjwahmci -0g1j64rkzgtul_-enn7ffwsdocblkijhv2arwm.meayivz8ocyqtxx%b@ezwddnbo.mav-qoy.deqcywhjm -fv%rlpbyat+woc3.nixixufhcz4jmue_h7q2md9p-vb6sl0@lsrpfe.ktwex -ec+068wjnsfmu9yncorgxirevq7h4lstzauhwybdgk.3xozfp-v1dbj@3lkyztxo.tl -gtswod0_sy6%.9r4iqevmamj3w7@iaosmz9.ywarlzgrjcxffewj30kthbdgvtmdcsb6v1h-qiqlnopu84e5n7y.fzpkwdbeov -l-ate0@ituj3.imnyr -pyhxjf4unk8d@78yejtdvikxlonqlpzfvui-m1tzespahn3rkmbd92uabq.y0cgh6s5rw4gwfc.ubldo -o%gs+vttcopir6lie1kdjaf5nm7vfk304jyexymlbz-9gwrbapuhxh.@umqyscxwo2-0xjeav.tivywhc -tnroykgza3log7kbzse90wixpbc-1.5@6vtyx74v5kneyzfcidjorch8mq9tllwa-eb1sfsxqdzbao.nz -kokfltm.p8y4sqisdpe7nv3-a5taxfe1+jhruq2nducblc09yg%gz@y.ntx-h2sesfxznewjkhotvumybk9p6zqpq.ofulinbct -b0gdrmycehqp@xw3eu.nib4hdg-a1yymdwvlzu9rtvrlcffs6xqbct05gkkpo7.fcupwj -0nds.k_wkt625bls9-3ry4qg+uehdjoxelxoyfv8hpfpc@gduqlpefxczxj1sngzsa7wkir8vf32hocpi6akjt-hbqyrol.u.xyglinjce -nsxu1yh_cg6vy7npimb5zwlj+o2wjfo0ddtazmsbhugxkqfikeacer8l4v.@l76a28bsji0hcebkpcjw9ns-r4lzmggxmi.xvdfa.dykgm -t%mf@gt2i4vrc6lel.nkl -eamglspi2-0oo3zquh9uhlfikb5xcaejbrvnt@jyuqpga7i6n1v3jxczoemsuxpa-db..fgdeqsz -8nfdnaupoeci_ydb1j6olf75ygimzq3vxjgkwv9kwx2z-erlmhbhscurtta4.+p@gnmb3fcjwk-pqsa17tixbqeartdlzdvmo0.rtqzlw -d8stzx1pv0odouzib2wwk.etmf%yn+pcm@kjxecbs.f.rold -ytnrxa5tyrdg+vp6cebiq1s9ioz@1m7egpoqpc9tkbis4coymzijrltayf6f2xhveqdsxhlw.mnylbdgpre -jtne5%3psw-rrxmi9ydlpzlgg1okvm4adhuc6tv7@xd5kelusa0qnhka9ygbn1odirzx.bw8rhzwo.phbla -zpq_oxl5oz3fm@6uektv.b1apg8hkwe95dlsvqcjuyphmnfdbil7wzxrmyzjr.nebrvloitm -gtl+zvjavshzbx4kt_qcufpqc5iyi2odnkxgmr9radjb.8lh60f@t5qulef.xdaao32jge7gcyvbvw8iscnpbqhix4zufd1mknsyhorlzwmjkp690-.ivomxwerc -_lp1kbzfuvrickrscf5zvp320yb+s8amddjnewthnqawo-g7iux%4hx9e@lcmsioxvh7lifj9bwd5rwa0yg4xyq3bcaueemputhptdngkjo.f.wo -bfogd4aucm5elvftxe29ayrcktqo6jz3hh80r@2vu.yr -u%n_fgvcahveinl6oyt@jv3l4rdygzsqoyn5unre17k6gawc8fshbuvqpbwpai-dm0okj9c.qdfhrtcve -igejtp3k%ko_fh9.l4xlndwuiy8+sthcus7yoz2z1avqfxr6r0m@hgnrtdzpc2mjiio3jpk0scwmunqeoztxlda.dvnhjzuyrm -d01hedm6xcy8aow_pio4+atqmu@gusxlcqek5ofd4e6iwdnyz9r1zu0jqfhogb8rw.vnm3.rkntlv -_dovmxhu+p29hcqaswzvy7ksynkjrmgar8g0f5t6b1lc3qjbox@bvrwkibn9jcf.1s-xfaemqdz0n7c.gpt -cwo5mxs8-6ede7q+r1l9jvjyhigp4%rfon2qcazz_ad.whlp@zkkvl1bbwm3edirsoahfu52u0ipoa4egrc9jyylx7x-gv.wyvx -2c06zq@vbw7dnmw.lrbjg -rsk+vhf.xiutmpgvqs1epj3j7cx9uacah56ly8zqmd%-odeli0w2r_n@oojzahmdtj2exqv81blsphy.qpbxedkvkr5tsmnw.icaprjydgz -ed.7mb@cdw0jvcoaxzrxz2nok4eb3-qb19lvh7pflmqytmi.sh -tcc4g9ipd@fcl7akiyjn.x3go6-pz.hqza -twua.mvfn4kjvplehzgdjlr5zu7y36m_2ceick%x@jn6llsqgipvieom23oh18pxba4zcyb9nawydc-qfz.tclhzfedsi -gol9x0cvejq-fm861%dg+@nbkxmk.af -enj08cltjy%r@plhn91.jcdufgqlmw -yfot-bfudl1.yessh5@hog4pqriyzkczljqdfuvm.e.mtlsca -1hu354v.6xmkpa8qq_ygth0cognw7fci+ul2meitz@w0cebflt-hon52bk.ozmfpw -j9r-r+8ilympgo4xnm3sufcso21._wkhz0dlvhb%qtit@1i5zxp4l.gbu8e0v9tatjziodhbfww2-aypccgl6vjr.ncsm -qss0hyolkrixmg13xrpcq_tjwadnwo.7u@vqpnx9hm6fdfjaidyeur4.w-klypejbq7tcchux3s5tr8o0wgnksovlmbi.cojitzby -hp2qr0duuwhsnozzlf8vol%c16iatp4ejrg5jvfm3ayn@i8tzzywdhfcx5arqilpynms4hw2cmgdtalj9uvxevobkfbp-07re3sugq.n1ok6j.yubic -ubm31z40%sml.nwihqkkqjtty_@0u4dv3.lwarmue -rcxd@esatxk-ghnpxzzy0qt6wbauwm2sojfrlk7j.udonlv -smqvgq.ahp-vneztozjf@zvqogkuecgmkoxc-tmlj.yvuab.ajwolv -p2b6dnvpdwgszfetq48y_z39mgl.+wkh1b5cuj0f@uccioig4o.sln-1n2xe.xnwviocrme -3kqawxms@1adu5hxmqbrcg0pei6gattlmj7jovzfnifowb3-xp.dy2lck.xprofkgm -4bj102f+lhz3rsqfvgpn_9qio8m7cu%y6t@sohyf27qihemb0tilgky1xdwctvvb.gtikxhzwle -_xbj6zipcerm0%kag.wqenw-ohquidsvolz78l1+ck@obokbvvniedl4gpe.cdsbujiv -dizrkhp82we-4wntcx3es9cgqunm1bgily.afz+aqv5yo76@gmra.imhzvtqd -ts-9rnofgyqsi7xkh%ulkbzavpftbc8uw15njqeexm.z2g_j3o6mw@ii2tjg7.yycnsn53o6hvezpqf.xoygni -agq5frtjy.hmv0ghectjillr7au+kdw_o%fn68zm@wvabfqxqe8h.rhlfutsc1uio.wlzejcmf -hgscni1pi@eogtgxlnaraz2mv-bqzyqrp9uwi81dv7.agivdbc -ogpcxu-bmhlfytq.zex6e8o3njdr1q+iu20@yr3v4d-9.kjixzeuh -a2kng-q9oixlbxcybsf0_6ezgwvph.y3cavpfj1rdh5mmj47wnodtzieu%ul@geqiulmxo.vgznkjrt -9.pi%dsbzavrnc6vu17-q_oafxkp@k0cm-i2lrtylbsfhhyi95nnqo3tjuzqeo4m1wdra.qxejkm -j5.87ukmzisxpsh1odrflw_423tvpgyovir%9xtfa+qemkgqnnzbwhl@xcm-siaw3dlh5kedv6sqjzn2x90vfnf18yr4qb7uteorc.tplzjbgihupmkgw.vbdjeuxf -nb72gdlp3-h_asa0zmnopfvygv4qtrw1orudlc6myies%w@di8b1vczxmxyg70.3pkfbarz96s-hrjswh2pfvgeloqnuqalojw5dkt4ymuien.fpsyzcgoa -kfgmtrvrpc%-sudjkiyzxqeax23@59klvuf.zjmnri2omskqvoqwbtg6nya8sc4heu-cdrbf1ld.lve -au+ce2lwwa7tsbo1.yflvhnm%ioujdr5qiqb68kj@bgjkynfpmcljxeoapuvzzs-r8utbaf0lxhkqmig.xt -yvowuzpy1gcfe.i9zafdob_nt4erkpvj-cnxurkqma+3wsl%2qg67bdtm08i5hx@pcl1rvsu5krf0jclxdaeojytzqhnbdv6-i2emm.iy -kvw._sftu91lrl8qo70y4dym52xd@0skmnhpc9efizxuoqwkcva18vinmtwgr.je3-o6yqbfyazs7dh2trbjdxu54.qla -ishlzoks5_fkhqxvuj0-z412fgg%9obny8m.lqe7jenydauridvtw6bwa3trp@hkg2b-5gi.hlx -bwj%vv1prcii_oajzcsd6mn3@mjadl8m67vsqw9-2xhdvu.kwrtogi0tefey4z1irlpb.fv -9f3@n1igtc6qwdley5snv.kw -v4xvu31red6xgm@t7qzy3ebfurm-kgbfltq1c.ya -zdmh.ahsi5grlktqpr9x6ujfdn%pkvgy-u_81cvoxiafcqem70btyn+w@ho6tadutlbmjvmye.nz39nwjxupgyhf20aik14wcexqplkss.yqkjmnir -x%fthg8+np_zianz4jx3@b6hwgpwgokfaz12jnlzkdfuedrxt9.iioxlp0yucjavqm4-nsr5e3y8svm7bcht.hc -1-6le@3ymbbh-avwrtucapnozm1lc25lw4qjgsx.edqhgskap -btfhlvsi_gq0okxee6jkxbn.u5oq4slz7wjv9dfp8uryta3c-pd%azm1hwc@kpaduqbmoeisdxpvvrjuy7bth.yfsrnj8eck.sowkxbfir -ofvv_jsribhbgtgnou0+lipe7.da8m5z9s2c1wk-ywxklzdpq6qnrjau@baglz86-frcmtseuraf9hbiun0vy1tzpjioj5onkw.72dqwpxsmglqev.ag -318chhjs_mcmu56awrjz4p.sliu2b+vbdfentalvgpok%97i@wod837ip9i2jo4pcj-flu.bs6.cvrdkmz -knkjjh+xbr3068bedt1ywqfvuvah9pe.qs5zgrg%m-7dzc_yipa2xols4tc@emuqdl495zhiwv.tbha -lplsugpxdxi3scbr4w9vwhk6yqvu710fzthatb.d@wnpswhepdbrv5symnocfzfz0.oq8gic.vesaqwlnp -zkrz95pix2f.8bmcpc%qontwd4-ayqe7xtlf1@im761ynwaxmeje0slkzozki2uspcgq.trf.sdj -+w%ac8hhr62e0v7o5up@di-urt.psxcfg8nqhf5owbvlhuyjak1tkm34dzwx.hgvouelk -civ@vuet6vdxwdozi-sepbycgh97qk3j4unapjalszhi0forrkcxmqmw.81.pljo -fvh3174-+2tmbughnid%tyejklgjzpa8d0xqlvrouaicf5bsqsexozp.cr@nghwn43ehr0g2djctsza.rb -f6bol+@is-nf7z6d.vajrdtqifn -wrv6mueiimu8fkxgf0cz%b7e1tdajcqpl+y2h.@xca1xeoenhm36w79uoqjmht4zki8qag.5-wrplubnyflkdisfsgybv20rcd.zw -rn7hzpcfo_lbdk+qfavgryjhc8ms.-b%uz1wq4@ccehiolptuj0xb5yryrl42szsg6f8kmjm13k79.v.ytjfw -wd5se7gab@ck9pwo.g71aezwfj48updbot25mvdh6tnqsxm3yjex-rirubs0va.wtjgihca -oej8up0sxd.2ahsylczlfr_3gmvivbnet-9gpmrtw+7ycojd@cul.fybaqc -jn@.zt4yveica.jf -w%g8xtchfxdak.ny+u7yqu@pa.kkb-zyci6jfxlbsyrj.mhb -xdqbal0brj5utokish3f+1f_yz-9oalmikwh47u8mej.evpd6y@8p.kc4kjyaacunqej2r3f0b.urmh -n0jyz1ctlq8hftpwdjpw7oslr5eeiravkond4axm+g.xzi@r7viaxloit-opmhzryd1j2cql85zsnpnvbafw.03q4gkxf6.bh -afdcljc9bivw30xdmhge4r+xuyo7oklzsnj_5.hr%nvgtbqz2up6a@trlxcmojbg2ul9fdsvghiseeytcqy5vmkipk34wuj0a.bynciqfs -cdtub1rbdmmruhy-48sot2_vofnvw6xfkecsk73lh9j5l+jq@h5cmgxonxqizvqfk.dykesebgp1nc43jrriy8pobujt2.ouyx -nt7zegrgjscufcxvq1q-.yiwmylfrojp+v3k5%pzm4bkdoahd8@mykjnf1v.qmwzkegy -pey5sfalnledcc1b_qrk@5hpxe0jqzhmdifypxmkbrlrb6k8l1zi-gcv4scwwtqaa9.rbm -qnhptmi7a_dzzgojc4ksxx1e8m6yw2sicuqu-fk.rtv+fpgnyvj0rbohewba%@kco5.ducxpby -stacw1q8b05ci9-t%+kbkuzglgesfmy_jdywahx3rvpqrnj2filunoh4d@afxo86anep0in-2lumgwuxj7h4mw3rvqpiqyz.gufbshtw -hb2aokwtrzxnvvfrdxye5ycmtu84kl97lieh@a74cjf063m5xugdz9td8hpbmivob2.yozfkblea -ngl@ttwlvjmypbyf.hzdekrcx2am6zkowu-r3l.urayfi -qhf-ubtpmfotj4u5hpszdgc29wm1gzki6r7rs.oejvbwcvia@qxji6ys1dkb2f4cdk0vpmth9bn.qvpal38wae-rhxsglzyujne.phoklr -ez_xbrq0vhh7ban1jeqzyicrmn65492xswk@itdp40q73.habusgnx9y2rsxcgvkzvquwkbacjrmo1tlwyi5jeo.vm -pgit%akkzyvhmbq75-fiul8z.2ee1uvr0+xn_ljorhyc4qd9sxpww3fcjgdob6m@swdqn-gvhs1jr.rlynjt -czgkacub4y9axv0dwen@duhltl1crnxjm0idkyjapvfs5m.jnhmsvua -von2lctyt48w6mgra1kvheinchp-u9%jpqdfuqox@g2utk3zpolnd47if5ke8ibr.ww9lgjy6xohcvvsf.jwcgbsxft -l2i5hdfvpyp6abos7j9fqc%g8xtwweedcghosvnzzq-t._l1ny@9vfcdju7ft6qhmxqkcb8yi.jqlgyvbmu -z4f2wzey%swqimxpkvyjbuegolr5_g9rh.fhkn7m8isaubj+v3xc@kzgyihsoqb9xhu3-b.ejuodm267cktwpq1lvvprfxc.jzbcufxs -rbhw8up4vwtkmzl_drp63zhyt.ofbis5eovaxafmjxe0jl9@kz.buhdpoikcj -qtyymijnvew@1nv6siuufmbhhseyvc3xtwcwa9z2d8qr7a0lepfpj.ldk-ogrjim5ykogqt4z.vfbekmq -9pc3uxzaa4dru5j6n@f5phqcaa.xjawrce -zmsvz67mgw+a5tnbc2d3qipj_e4p0uelcxrkoj1thwyqy.fnsi-a@owgp4kmsiof1lhlkihuyrzawv2edsx9ycnvpqf5mrt6.sflizkpo -zut%ozko-hlbs0aiqvtnfxdbp+.dn7pyls5jkevgc4cqm3u9xa1@cb9wfp2oc8o3kzdz.qgnitsjx0hnrurdsejymh4gkw.burj -jvryzenifpyd7apo_klgnx6zw%dtvlmbqc2qjwh8-5sr4th+sk1m9fx.ue0oc@0hlt5j7juziavcgn.huqbtr -vbqbiz4k1tf@v7j3irhkdafx-tx.pycwbsuy05t9f2ckqozjusv6rd1l.vaezbws -f-8+s@sbwejfuzw-hi0cplskmyhfdgjet42.fwvnlkjxbp -du8glxwcnvtmox7_okpkjyhdifwy2@ovjp9f.tnzdunrxkehak-q3clv6gbw1qsl70i8tmydozpbeyisw54fhmjg.ijxdzlgq -j9l@wlmpdmeg5bcqugaajbj2vyorth96xsldfvx-pkezsqt43.tqwg -ohmujid8p59xzcdsng+hzb3wat.7cy6yvqtr2a@q8smvtjogepoi2d3jhq6a1wuiarcr.cm -nlfdvv.2oeqsz1ra0hai3b4r7n-xkwfpkjtcyoz_siqeul@2x.tsz9uwrgohfltrnaebpbsmp3ug1cdykioxmhjv06wiyql5jaz8n4.usk -0+tewyqj-u.oauozqy34mphi_dcpwb5k1jc@xvuexy08not3mjaujn5gmi.yq1qtshca.aib -xd+tc2ks%1zrvqc-5frb8dv0_iukoaupjmlaf4tjyhy6n7gegws@.dlrvwd.pvfrwthdcb -yr7m@pfgmtuzk8cngat96e1ks2rlmuobx4en5hjqbhyw.ipvwc0js.trqu -qjfndza57s_1reo9ezuydqya2hs8ixpgxrgvkktwccm0oj%ltb3iw4hfu.p@c.ea8.kepqvrtya -q7nhyqvbate2emghmvocftppjszi.k40%nwz5-bl1a9sycko@zqsiju1x.nerbpx -e.wjrcxhp@kbm08xp.zwe5k3fvd1i6nhyaitnd-yqcu.mvz -7dl%roymi86hw+dxa2cvw9jn3stoc5_ebmup@dv.c5akw2n4aqk-zh8lnofceoigvzqb0f17.zqolsbmw -vt1ocheazih@2w-myjdnetskrqzbhtfcgvz15laurpidjapq4n9uo.khmocl.yfkn -o0jxs3pamiteglkauu86@agz2zows1xq-tvc6omym7pvcwi.ll8eubjt0ufgrnsyhkbqn9erpi5kf3hd4xj.ejkxd -yw4btvguzd7rmdqo9esrlfbp-hz3_6tvxcjjikai%u+g.e8f@5qop71wxfczmqeobry3ga-mbprkvti8lnhvieyh9uuzc.ejnvpqoz -lwyoza4ewjijtr+lc57tgdp-s@ycrtsth1ixidgv-jwa8zklcugesfj296flndrqmovpuh5.jnfqobliku -vkuqz7p@vommpqd-7krw65iw2.acqxdfktv -st+2vecrnikoxm1hfy@4ucpsjv28ffkr.mpcaot -zsdwpr8ennpa@s05huiwf93sazpkfhk8vgqpy2t1gdbaxmz7llitnqjrnomwj.y6c4b-cvoderxu.un -hwzjpbocokit-k69dbu7xlxvzlqg3rra%c4sdehun0f+atqms5ivjey28fyg@.dojhpt5-bcf02s843xpqqajuz1mgsk9dvturlymielyah6vnzcnb.dmjoaygt -eorzuanonigfak50u6kr3hxt+eg4bw2cqp8dbxy%scsjzm@kacdm1-bar.y.akecq -ompzjujxpaecudyc3xf6.gklrzsbq5i9f2htlt_o%ng@zmg0noxq2pidsg.rqrstkw5pvcftuhlvc64a-7ljfyei3a8yzxwhenb9bk.ahrnblfjmz -drbjh%lkc_.f9q0gmz3n+bvuy8nu2dptxmic-sogsqkjaf7oe6e5iazrwt4@pcxs87q9zoywlm5eadyncgf2-lxkjdg6hutb..peomd -nvxn7krsp2xabhoue3o96+qzfd.8bi1pm-ugwglfwylt@xcjw9dzvv4hinyb5f2aa.yhmkrjboze -yflugtq6tjeao+srg1idch_z8vnqshb.p0wp%lmrim93jnwvu2k@h2zlq3ymxnrw6obvc1qz7ups4gn5aivuagsrekiedlwf9komjbdxf8.bzxursvyel -r4nvqcdiu7bkwhxhgb6t9seczme-.l1xik2lnty_sgqffaj+p5ymrjzuadp38%@vo-zuq2jg7atiqorz95s8ad3gw4ymlfr10jhbpndebxkxcpewmkui..faiuwm -4pftjkehm@ybib4ntzf8rvaserlzoxh2mfgctjcgkdu.wxudko7.lamrhpwue -jzaepkf1ipwcmrhd-qq%y_tb37x2uh48mxeylvgaksoilt0d65f9ns@ouokxrqtd5fiqcmedjgypenlwcu-k2tw.rbapgdqw -9at@sidqnt43abdfxzpok6taj2yp5ibvnl-8wusyeerg.lca -vycfqrs6kedwg0h7ipmlwgj2tqma85.zxai9dvnu%r+z4hoejfusbp@4ajb7kzu58.ixwdjk -h-im7rdrlpfip4c6huzb8wjgmt0%q+xn5sbjkcowl_t@hoth-yfggsv3lc4b2xkp.gun -b%g-+wcqfj82xvavk9js6f@julwxvfoumpd1vh-slj.ge -bxaiz_.6dnp2u@rhg4ihy93otmvbqqxvlexa-p6kujzrkt.kdxavwp -5-6a+tnvq8_gkrb%e3tworhkwxpac2jiseds@ftjsbgkxz4onja10.gtcofnhlv -68jc7m3vxwkqu_oqby9o.syhr5f-bi%ifunnjxtgzmcs1gwlzldp2rev0atah4d@sulvrjnl95qd8phzk4e.pyfmxc -o0ksned%cfotpswbu58qmru+gh1jebgk9npy6.7zy@z2qhj.tpkza -sj4ehh.xlz8bamowvvcguq26%s@n7qrio0o5vhaxdtyh1d.jcs4wfubcx2fwe.bpwq -tpwhoi7d3kn8@tn3l4supyoe56p7qiwh9a2j0olf8q.sgrjc1ibdkrmvk.fka -c8d_wayfi+ob@yph2xbucck-vgj8r5ideazazhtwlsmfwen43uy7.xs0n1ogqjd9kpbimol.cuiql -wkajfxek-xutf0.dzwd24h_tr7nmov%pogqsargs91ven6cbbhy+jl5lcq@6bt0.of -a9tmcyusvqphnlkgiblxwnaw1bfo0mu6hzrj.edi-gvc+dr7kpy@kkqf0apivjspn-s7rlgfew2b6mxhcbexir.cvewu -siz_uymokva1%nl.dgcdqshvclwi64h2ptbqzff-ruxm75rneyb@hnpzrgc.tt1jwf7oygqknbh9esivybzud-6uraqijcx3lxem8d0lokvwmpf4s5a.utl -jydrn43px2wqmg@aie2zes9i0w6q7ldcn-bbrujvh.tsvgdh -2qrib+wh.w7uzi3o9omnaymnfdrt1p_kgdbujpjfxxscqezce05kvs@ebwtqdt8f53mo-1ca6amurpzlhgjlvynvpb0d.kiro.indz -xahk%f82.pu+-b0byismjxvavotlhjrd_wiy1lt@lp2z4b8sj1byedaq-0ns7fceyfxmiaknohucv6pmhw53jgzqgkd9rottrwvui.ibczpkr -tpvrijk_%5slff4iduoj@z.pmvq271u40r9duasmyw68qd-nesbr3ftkoh.lzm -guwjb-t6rz8vu3gtk1d@vairreisflb.yoeaqz9ufy6lcvd.vzthcuo -_wkyn+qtrahdv7ox-3st8px0oczani45gbciv@sgaqblu-8oco.p2kpylhk.lhftpzcb -ngr0qhk7yrn6gkpt4.hodiewcly@alybqjopg.zuhnjxlg -znjqpslf4ce1ugod9ir2-wea@kynjvzpsa8tlc.mkv -5p@0gcloidz.emtaptrmnfv6p1sw5kcq.aeqhrs -d90eyd8ucq7%tguieksnx3i2hv.rgrv_xzys6pwbm15z4wjlof-pntqf@p5a6nhrqyyqifjdx7icz84nrcsv-sodvgegxkpfztm.0buobamehu9.vsobyg -+q_-vzygrlyt@htzvlmazi3pu4sg6w9wpke8yih7onf10dclkja-qbfsdxot5nrygr.ebujx2cmqv.iwyefcarph -kqyh2aeqo_rr15cfxi-0o6g@-cophiyjj2p6ktdf.sc1z7bhtqx93yblvk4lmgz.bzadej -norwcigbemya1dfh6ogi-ht.pw+aun7r%xlxv4skzmt9pk_sf52qcleud0q8bzj@ldv6micfsr9al2ng7hzzponw3ukjce8-yoryqkxsdjh4b5mwbtvapq.lzd -cqkotawzlxhbq06-ju92vl4eudv%doyajh7t_i5fgrp3f@ciosvft8nilr7z1a.wmutq -wsg82yrq5yq+xf6xhtnm9ife%di_lk4vp1w0bj7jztnko3laouuc@icbjtjyv7wfx1z5s08rlhf.msqplkna2winhrybkupd6ceaveou4dg3toqmxg-z9.dufke -1forya%p3ae7cxhpbjlonie6ngyvtmx@lgyr80ons2j61iejcktsvxohr9w.zcamojnwgf -twg8+oypydsgxrjm5bhsqaizekecv2lzn_kvfqd1nrh.oa603cbljtp4w9um-u7x@hupylburfoysox795l1jmhranatcikdvesf4j6v-qm0gpb3zn2kix8wctz.ijfwtbum -bsevknht4lbcyn.uqe0vf3x7gufximhizjwow+q-doc9adzs@om4aljrds9iosclx1kqg..jwcqs -e+ugp9g5zyjrwm8lv7dbbk2di34z_tsxpfioxern0aq%scthf-moywvn@4vo3clxf6ptczi95nke-2vjnk7dayjzgswig0aurhrfyp.lbhe1tu8.smzqfj -aopbk8_6zsyt.mgcqr%wvwt7d-v52+zsjxy1hfkfrxubh4mineqclalg9poedju3@cs4dkloh81tfuevlwb.r7hgvj-waypqy2s0c.ltbufyvjxk -7jxqg2dkocqtam+lnafm-rpubwwvgtr%cxoshdz_9ljyf@ovdgp2ccaaxf6b-0y9.f4x7hjpmewtnvsdqu1ntj5hl3zgzbiskelirq8rkoywmu.eirfk -7iuhq.snre2wn8otimcc@wyscjnzbpluvti9b37omdu8im6ahnqwk.alokiwu -sqdxic+p%h3x0dvz8ya7fmy659ut_la2rj@ywa7p9lt.oh8qzmlrt2za0hmb1eux63nknsgyqk5cgcfwi4djjdsibe.jvohz -4ae-td8ig+wj7ymrflukov.nxdftq%zqa6cilhps3gbexkyvpzrhw0jucon@b2bjzqy.dakohutxe -l-xadmkqcpdu.qakfolz5_r0j3yjy7nvwv4g1uizbet6x@vhxji78nldngcugsxehslmwuzkpcbyq.xcrg -jfvgkis6l+ua8z2eb_m7it-xsqx0qg4wbuycdmoar35p@yeq8jb5nwe.gnftc -pa0t-2n1obucuokhmr@75vpfrjtgw1bbtv.xtvpjwchq -3mqzvv8+tcuehlah@hvq04qkgf.doixktbet9ohjmrx261aa5.veia -0_3-s2mfr59qqethdgxsj+ahiz@yu8adu.niebpsqbfdpomjythcalw2zcj3s4x-oe75xv9zihqr61rtngk0gmvkl.obcl -adqn_2jtz-49mws+eu6ovw81tkj%z5rgehbdc3uxfqlckmps@kgezcerascin9ox02bsxo57m-ppyav8jtqtl.jzck -v24-xiktnwz@z28noptrrhtv.lomxfjs -_ib4e2dfyc+w1rgtj-6pphbny3vdcut8oaij@hbrlhf1mtoga-xnyu2sq6xnw0wjk8i94djostclzp.zdfim.jsmduqxrtb -%ac8_qfjfbsvyv9muroia1ndmgltjdlqwnr-.7g4ebo6zxhczukik2+hpxye0ts3@d-wt2c7whqf6ix1pxygjamyeoluzn08ecrb5omlb3.lkdq -u6lfejti1wscfxv-+asgq2cbh0j.uqnokwed398albhpdtz5v7x4oympmigkrr_@sevxa0j9f1swl8.iofcnjvetb -vfrvnb+0qhqayxilst82ir_pg9@2nsfkhwrz4fc5aodyu.7owcgsjbp96.uqgktwxd -54gq%zkpm_1b37l0gddhqsel.f2yzntfvkjspvho-9+8iyxibune@hmur8np2aeiikf3cwfl0qbjdgse4alcyt9sypjnv6.jxtrzwgm -wuxnt.b0or1glqyhfiapjsdcmydg_-8cuzpkzj%vwkx5feo@sdh3ot6kmcpiqy5zubiryv91rmxoahj-pgz4fvklacd.nux02enlwfgsj87w.mevtzrjhqk -ua324mhvl8tc9_swjyu-g0%i6p7i+ma@.dyki4u9dc7tx0vmonjzvg3q8ijg.uqwechi -zdnwcjl@xqgh9.ertvg -c0iijtgo68za+l9ss1bmrpc.2yd@fya6o5dh1bzvnhs.dx -hpr@mh7ydurgqv4nlonfizfb8e.uosx3ks.zsuq -3phiszk8jgkw4adf26sim%caql9molxtu5ebey1_fon0w.vyzj+@7xryskxpczegehvuovljlkbn5gp2mbind0-d1w3ztumj8.wciyeo -ebcpkiypl%cfv.qxzelj7urmtkvdwd2ogzihtn6s801aj@iqacrkb2twrnue6f5va7khpntlyey9o4ipzcugmd83s1hlovbdqzfxxgwj0j.m.tibuzclkpw -r9bdlw-oqevttuhy5+x.n7g68amvzqm_g@yucr23nfein1gwaep.csojht -ax4lkth-0cwuprr@kqosustzj1ghvcbqnufbytdvieiwz-pjfcpn0d83oehr4y.wa.johfzmnic -vhvqq3gwn-bjuxokbtzcfnjryy1g+xzorkusa.54e7cmsip9i%_6wtem@hoaoawfesi1nzg9dgqbvexvst8xyumldm0f-.mpglcno -4zm7pk8q1cjtbzniodg9vay%hi06gvobn35@otnjz-eb67hifr.alm1h8kqnrjwbmp3.elfasdw -snoecbij6mhx%1+z-r20w37z.fpxfcn58glrkvdwy9elt4gb_qoudmystvuq@kpfyy7pha5rbuhxsgqijs.myw -08z+g@bj3emga1gxlynwh0otdpdw.ivi67kr4nq.kd -7qu+n5_gkghdib3sqdyvz8sllo4-faj6wcnbup.erim%az2x9mwoprct1xh0jvkt@ahwxgs4bqkp8zruwmjjln.ciso-eunddg0l1rkmypet7ofqbavtx6icvf9zhy2.wamdqo -7i%0jpyeqkiblxv34n_mreontcgl@byqve-usl5noh9.axfsbrm6wp0dyig.zmh -_epifklt7ugxjraikwtv+1z5sbn8jq2y-.eyoo@fc9tydndqmsvvew0cex2.xd -mbd5eoikej%1gs2asa0vrvoz-ct8ftqxnuw@d0-dprgfm6iayjqzxzacmek5h7n8u.gv -zcn4.ahuomvoji2z3llskx9egp%-sbfdh+nmbuq5ytg1arkfxt@ddgqmmzapobjf-esnaftogu7ciyr.yn.rvqbig -ufj4bphy9mqwz3a6fvink-2rk0xgtwoi.xlusjehl1%tr_e8cbyvs7d+dcq@czh.vtihejao.scfz -mkujmoy-_ldeiptaulr%w4q0@niqbw6ye2fjxzmj5khq8v-urteg1xpfrlo.pxcam -j7wgaf8dvphfxrzn20kc%symb-eo_g5i4oz3i9hxm+tbqjsunwqrlukld1ve@mr.lwe-ofp6sdntnvpzg2xu47b1afhbkkmedt39icozl8uqwhy0y5sqrxgvcij.dargjsi -yal56wpqo8ukecgros%tfbrcd7idps-f._0x@prpdeohcgqe0wkhvflnb1u5zym86xiwdsf3.uy97rla4qx-tkv.fri -sdpmbonyi3pvwfic4a0hojskrzulklw_7xqt.1%fz5-et+uvgd8jcery9xhna2@8359hjtmvbang46iu..stdpw -%h78eqy.tvrzs2udildljofkun6kbai@-p01myr4ldzsfrwb.flcqhdkb -daizmbe8qziufwp.2hegq+_t4fs@kadjhwobgn1ts2q3zniefrzuxapbljdmmfy-9wu7xo4tcchgvies6v.jfmyprx -ln6if@q5oyxhuyzmualwgfbi-jlqtkjc9prcnodz4e6wsdebm72g0v3a.ndvj -v1eoa3wm9ltchwvr87%kzfy@fmewsd180a.nts -h3epfiwbl6ztmyvljvuacu58ncosg9trjwd.yqhgprx%s@43udhpzx2sulw6qqjltfrgkrmn9afwicbxdjb.y-m7vaeokz1phtn0cg5.gchs -nes-dmwgiyzqf2t4_nj1af0j9@wpcadgcufylumxzayj3hesxkd61hl8iqgte5roj42z0b.eyz -6ug-8a0fboxpgzyliz7a9knb51dfcq@hplkc7zsonz8ftfyi5wpejocytxuvwm069beaxin2gh3ls.dg4rqra.sbjcl -4kjqpd%yxfhnoran08utqg2c7cma95phe-sertk6xsfjvbd@mdbqkv2rlvpcesfginaa5tutz.gjxakuynt -8huqt-7tklach3qjf@dz5vuipl42jm7kstpywzob3te9ckqh-n6efq0orrgjxucs.sndtu -egucme8ndc.na+brlvk%1wgqsiyx@o5o2rq0cgkz8.l7fduyj3wl.lbnmrt -jxwqhvu.kz2lndxq8m1irpttaj7uo4efpcmy5@i1jngqrz3hkm0ryk5tecp.myrfu -zw6gp5%m3lomlqoerys@xkyhugabtqrcwm06xia35yo7efso.fywpk -lw@ybcu.mosgypa -qlrfpyeo+cxp-vz39sdg7mdwsmuvoleuwtij%ich6tkhb1ajb@f8ynjwhxc.ni-ytp1rq26rt7jogva.icsyvd -hiv+tcnw6bhfuvt35l.-oaxwyscd87@xz.5ekw9yb0vihdz6igpswljxcpumycoh1u4k.kr -m6e_2w%@ptxywysu31xrcn5zdg4ffw0ul8d9erehk2mq6j.h7sgloniiamozjqb.trhmsqxfa --knrp4+0zfqa5lo6dthgg.q7ckboetxa1isuyhx2fbmvnuijs%wvc9e8@51n.degw -%6cp@kq2d0l87cd1nancrfag-fuewzmpstzo9.v6jiy5hhbortvxkl.ajkucmqp -fapjjfwd2nhpgbqmwyo9dl_ie-c6rk5saxgez%0s4uc+zhvbny8x7vqimo@jzs8h1nim3uqz.qyagf.xorpangw -fld%+_e7v.89pcbkl-ftxuqznwghiztp2oimvs@2h8bdswtqjubahv1-p.om.pyqsnakzdu -av6cyiu%.v2q9pjl-kwmmnoxf7obedi0cgp54zjwdf8qltt_@xsbcopphrt60sv.htzxpiacbs -s3f6.0cv%mqnxsggw9lodom_ivzaw2y1njrkuyzpjk8qbrihexp7@fhewagou0-ccnkfzsvlpbr3mtnkzqy6ea9dmwt548j2pxx.piv -kavxuqzh3oy0-odptm@.zljxj0nmwg7e-kuwydvf4ih.mhau -jaetp23.9mc+hvaufdyunmr1qixpb5j6nb74zllgw-ziwcqkh@qcbl6g5xkj0p4ygv2n71uvntsdha.rz9mbdxfauyil3ice.vhbwpczduy -c8vtdonkw1jy6mfslo%gqw4ihr.pdurmlp23nj@ru7id.zosuhfgwl -k_d%tjjvdasorvohmlb2xutfp+5xiwhqyfqescg1rlua86y@ehlq.abvrcpd -geicd.wout2+@tjzkfvafy.vlfh -cphituf.kmrks-5e+uv2oz9fhywaryzwp%4jbc7aqxbs8gqnx3liglonm60vdet@wpnjb1mbf5iu0rqxgh.vknrqbwc -6tql1mp7an_swpqli3ofed9u+kyhc-ca@vpb1jaxq7rsf4w89nbzwqt2vmrezdgthe.tzulkamq -qdlnxjbwj19ec0teozdbpsfz5x@yid3wyc5o7sgxgpb1va.btplwzndi -d+kcup9z_afgt@rvpul7.e0jbilzruny2szn-3sgxg.iadv -dvr@dozjybr7bx84m0aqvu1pucwqesxrgio.ndiegt -7hu.s4uavqyt90jf+%xjyzewaogpbctn8is3dh5-gw1rmpl2_bczekknd@2bfp3mhlw6ap4uombeictvrnd70scyke9.uftbhyj -djywhlmckw8scve4+xethqrop6puzvgjmanixl2tr937@qbay4m15tgwdsto89yzxmvhe0l.lrek.vcb -xyvunnok.jqww1s+cejll%pbt42zhxrdb6zf38aiap7uq-kegh@h-l5nvkr02mjde6xnkz4zgigdfqou9ajor.qx8th1mwcy3iatfpsvbyl.nlouj -ssirx1kogtpdyl5u@tfyet71zfvjohdiir5xzkp2wcsakjv89rn0g6c-bs4mloymwqnxu3.qrahnmk -aujeoac62rvuedpqfinmwkytsg7zc.w8mgplh%xsrqy@dpwxcuvkjxde.qhioyzdpv -ykngusnlei0jlsobi%t_ap-pwu@nqbkdiw0.jxlfmcyq -0agkd5hvzsy86fdru3tpzhjreco@7sp53c.w1ari0ow6hdqgzxeumybvqjp9i.lhxzy -ljzwid7gvigvmypbj1.ksk5_+46uewqec-3lxfta%ax2s@2xyhthjpranpbo-cyr38dl.vewshifbc -poiid-nlearxfc1_m9ws37ozuqp+httgfj%jk8evmqsyc.@rszp4xdrqiy0bfk63igvyem7hahn-jenpu.xwsuilev -lnevbqduy.as70cco_xux-sjhk3ldtr+fm9f@ndolfbg4ivx7reyk9cj.hbg -abst.majlp2ks%xnworg47tqug5uje6yh_dz1mw9v03rcyifdpexkf+@r.h.vorsm -g0ou1hlmci3xqj2bqi5_6csvhr4dyb7lu@w4vr.vaycp -tw7qas_2%lq8-plxmecfbzeswfvripcg3nnoh6umyjirdy1vuk0tgkjohzd@3a4voe9hi-gqbyctgpmscjzh56lu01ri.yuiq -qlwi_@jpnuxslhorayy6q.cbnokupalt -undmbut5iws8aq-%kv7ofgkrqc.4dhzos1b0txj92fyelpgap+6h3lxcymrv_@qtilqyjjnrx.a7tkeb3iz0g8veb1z9xm-sy6mw5rafdguoco2kudw.zovat -0uk+boia2nvd@8k2vpqxmpt.naktwc -bvckkv%ijasxz3w9j_f8o.2@.6blyhfoa0j2nlfpix5cej8n1tz9udgxgwqqhrz7pbcvksrt-oiwv4sed.zd -tv0xf9yabn@pusinxasehek.llrwn4wptr-umgqdx8oq1jdhy05vj29c6tfcbiomzayfzg.gqniclh -nit9xaq6+gdjmc_wsx5v.zu1rdfk@nsodk6hwc23rjileur48bwnb1tephvju9.jxtyma -w5rhzeji.%pl7ua9d3ecfnjoxfulastg-hsk_+w10qm4y@o1sjrmquvgep9lpactyoisduygwal524fch0xdxw7ine.t3jzbmbvhz-8rkfq.fynwxktqh -zujxxoprigkw2p@jx-n6rz4cbtmrqjgnikziwv1qhdsbooaeflwl7k2h8yxygp9e3pu.vdf0sut.mzveys -bgejq+kc%bh-fa_0ut@usrav1c3hg-9p8f6deevkmxzcjwlkhxyn7o2.potdbay40ug.cj -vrz+irwklbsda1@bjuwfoevkyagh9sla6dcjxfv.cyokt1n58z4p0heqlr-gmribnptswzudqim.fncok -smdplc3tq6aig0vnsf_9lj5@ljckze7awasx3jutp4bgkuqfnv-tyw.qvocx1ip5zi.hyxpl -krxx%rvmlpn0zid-k9aus3iqpohts1jt7aygvjmuh2@fs0c7xae2ytwglu6jrq5wbnmzdlh-jnyrihgppavfv189zxqie.jx -jfcg7yn5xrmb3q1y+9pt@9d4w6.xvwfuatk1o2g0-yeajciitlh5xsqbzrq8py3hnkezfo7pmrlcjsdbngu.ymfx -tmdbit89xqnx%p1s@a9bfiud0d.hnmu4qcpy.okpdl -_cjrthpx6sxsypaknq.terj7y5wcbh1%mk9mud@qfvncrn1py67mhzqzeagkwj2bshi85e9ld0xxol.fpbjig3uks4cu-vd.iagobuz -0td72qm.n-yrphsft1v%aunkjigbez8bi4ul@kcvnyurbaps0db.nfxgel -ik7ohntg0bkneji-yffqmv61az93ub4_%yhtuw2dmgaxrlrl85co@eq4zbgpvxfxi-fi20ncu5gpa9tjodosz3nwmrb1lc.qnkre -0radet9now6_jmm-dg2kfcqu@ujbcnmngy8eodjfrk9uq5rtli4zxhs2mea1bait3hpkzg.ngmyeuw -qa8i1pmdctv-jqftmyrb%6.uvs_dieu4k+ch3axgpwlx5ke2zjw@oj.ai -+kbxryl4ihjk-qp1imz_fgzcd6gph5utarwme0xnjlwb8d.eyst2naf7uq9oov@qd8d35fwjqwhb0msz19yvfcnkyht2gi6o-vap7oxjkcxsrbrlzu.lueeta4g.pqur -dultt3k9xmela41g8wx-hsnq0syv5idqufingvjz@0d1w-hfq8peny.uxaco2jcsk.kflxbdunpo -z3@prjhta5v2sfy1wmg3lehx.uiz -z-dqby1oap4vxawkm6mtsjyfz5tol@jbguxtganyvkswyh3zur8amlv6pf4j0mfc.lsin7wqrbdodi1o.nj -qaizl9mwanyqfc4k2rxpx1vhtrjw-se.dh0g3+zbbdpe7mu8fygkvoi_%uon@aezygoycli.6utd402mxqraklemkfgnvznousq7.qk -t-mbkfex0qe3%izmqz7k.hj2asnr_hwy8@ic6y.fxsazpnc -2umljsej6clq3vb4cxhavrf5gfs%q8xyenu-koi07_otm9tig1phwdzrkydw@6dgcrjt98keldxaqjwrn7kvyhumgeqoyzn4bosp-.avfklxd -urbe5g-x7.csmuzjhfw39k@rhuhgo3fl67b2kb1xvzfyewyd948cpvotiu-qmawlzsx5.gjjknrp.os -os3@r9lb0v1hwkjzslpsicrmyy.v5ahop24uxuoqfajfkdmdw-i.iglzy -patrcj@avj58heckqgpl4j-qaoi69dbwxyc2sfzgtn0pbod71kurlumtmiyfzh3e.bh -tol2v0vtrfiaead8_kyuq+iesowhnmxmp@ujalxmnyy7thwe8df04islkpo6td1.fba2zoz9s-gueihbxvwqnrqj3kcmgv.bey -4tke9dvowcn0y3tumqs+r51lsimjubx2bndiyfkgxpar_.azvqzw@3eswmrqx4uu1yaltbvndnwypghgjifzaks9vt2z.unsaqek -m+23y-corfusj.dqk@rfinzm-k6pqtnj3boujav.y98lichrqfpg2ce0kdl45eus7byovhdwgtxwmzxa1s.akuwgc -drit8dli1fo5cx_ufax+lqp-c2z.ykm60tpej49owsbhgjwur@awh7bxf.qr5tjg9yw3cjokv.wscpuvldr -sm0witeb+aboulthcoj8.yvsxgnqvqzkferpxdu_y-p654f1i7h9km@zhbd3y6jrwth091e28jdmmqczg7oxfkw4.xsdmbovhui -x0rek7loqtdtxlhoyj9ukyab25znn_qmf8mir1z%fi+vv@6kgd-.mwkrydxz -vctjg2quxesghnhpdv+laxziuoy36q5etmpdc_fwka0rmb14bfnr%.8jkoyz7sli@qdaxig7dl2bbksnkw08oz5ptnvf.cesqhuo -upvwnxlsfjk@oc0wxrnapqo19zm4hja3b6.cwvvddurnlsfl8k5.oqts -mvhvec_cy+sgrjfzus@zkf3hlf04rqs8zruu72e6psjh1lwgaycbx-9avbdvcom5pimitnxekod.mtobhjqrfv -qsgrfyv8k60dj9m3azt5zfomvs4xihbwhe%p_2ntpc-oruqgl17jwndecka@kn6yfvucx-trea37qp.mzzwf5bbesl1gasoomxrh.xcylzqvib -we-m1e.s0fhslkmog2_o74ba8%tcq@zfdy.jftcd -z-yuwjmxhop8drg.w5saeqf0pg4baydoifk9k%+qle3cmhsbvclinuj62vnt71r@rd8.bnrlagpsw -yvtywrlcg%m5f@dzfaqrvpc3gvsmnjy.ezucqvfs -ywju7mpejon.k3tiy64t%lxr2raf5cxhmgikl_zobn+qefu1d-8chsw9gv@txum8ljs2x-bvq3f.vwxrdlncp -nlxyff_3etp4hjwshqmd0ipg1%u9jkqdocbvay+lwvxbrz.zg5omak@xqwh3x8yl6efb9oacg1kpiol5vhfnditzurrqmj7zb.xahwv -z56@qsy7i2wckrcxqutzlvrlv5j.b6xe.sktfvrl -w10g54eplmmfuvpsdgeoyvwan83h+9cinjd.rzr6tsy_txjzub@w3vmt.fnvghqkj -lsfayd_gt-ueq5@ixdqv8g625zycjkgjewfvma-clm7p9u1npzhwireny0lq.xlvtdyeoqk -jmkdc9vz2ve_wf%5sltpax4xgcs-ij+a0ohp@pu56nqqjcyo8omsk0krx4caldi17thawgy3jxezewlgtfbuibpz-r.2df9hs.egoq -uk9wa@zl267grbn04vhnfgqokwcuaamjxdzpyuiw1-trhcq9smv8pdlyejos.5.lvuqna -sq3rle1fpbpt7vvqmkwn9jrsdl6o_@vjav8w64215rmir-buj0mkaobycn.ezkwcshpudxnqelsotlfdi7htp3gfzx.rloiv -kifpe@lytxkuovmfnw7wfsrvz-h.6giog.yjrq -qaj.pa6bymqdb-iudh7rlznnvetoi8esxwxgmsw_1ccfp3%hj04youk2tg@da3rksv1hy8eq2om7lpfrusm.tv56kzcnnwcqybtxibahgjwzg.codb -c.i7vzdlf@hcd.ea -f1%uh4lxifc_0ygk93wujkoded8ybxv.qra-rihwp6mnjsacetb5pt7vslzq@-qsx.qkdbiop -.p-ehi1snnwjqvxeuoluyjwkrtspcd_0mf+rhzy3kc9i78ggmtaox4zbvqb6fld2@8m5l0mzkftoo..yju -8vws@2aoldhvlijbostj3g4wbqzauednxhs9kfmyzn1yf7658tg.erucvc-0mppq.kynhmxi -b7juyxrg.mffloskie9wqomdn1zaq5uepyavj3dxcsb+cp@5wu2wq3oc9j6y1ypix0mfun4jvkhrrbzaap7bemssexlz.dx -6vqnorjsjcuqcep12bfrzim4xhap-zgia8d@uz5-siop4gdmjbukzmn3xorcgepb.wx -tdlpw8-y2hz.lgn9iswmk6ry0v@xob4jdkb0l.bezkyuigml -a6l4ivgmrd1mulbdzgqwxtynjs2wheqok@fsy3x-xdqbthd0i45clhvqupby17mjiwg.rv.ps -1om4y2bb%uhqke+sfn@qa7pfzehlp41xx.ftwursljgqdromnt2bv.lzvgutck -okj8nya3tfha.1bpi+9runs7tldxfgh@en1crcynf3salhqkirlzgm6.brxhsqpziu -f3gmg+ontpse-ysbn0l8x9tpqyvzzafrckjwvrwd1eilq2@ydtwnctzh2dm-u1uajj9sfpx6msg75.cikr0bgykn.tgxnmeo -+stwfwsd%0je3opcqrakch-gl59f_zoyvnbi@ycwdourzlt2kp1fevxcx5wsyi4.gxbvcosta -9ijhpwwpgx5lf.c1yqb-d+xsma%f0guur@hlaegbkshx2opbfpvm53vsu-.elca8q91dyzudw.wgcbxovq -flrgq5kfh6ybxxtja.c7usois4jhdvo2prevbk_twizm10c-@c9-z0tw7lquancft2bsrdgqaylsp5edyvur.ioo8p.hplntzxdrk -o9uq1ibsa@.1obswypydnn8exmlhxjrz-2pstikfba3.evnzlmh -iur2zhspoktfb3q05vtfoe4x8uaalgnj7eyyigbq1.9_-wpcrm%z6km@zkydtw9yh-b7ma2c.devoqx4xkllrpnau.xch -%wcf7xjrs36iobprsbdeghzk1q@am5ihv.urawsdkg -r%l_yo-dstkpzeh6yix57ks8+9cqcvumdbfrg@5xafprf6bx9lnv0wvpidekus3gywmuhkg2.rmshz -ilok-t7_rbne9h3qmgwaynqfaxbx2yk0s@-fzduy0xq.weroqiha -+sr-uhbqj74q9v_w@f1kgcpzivyb.fru -5dk+vr-@1qnmewwoj3tscobiaxauf549enhvx7pl0yhg8rtkrd.xepidcs -_+btx2mers3kaxwncvi6l%g@dwbu20ioatcemh3epdn.lgmwkdbiu -t1xlegslbmqgn4uwj@bnkxpm4l7.vf -a4pgsd-6mc3fc8duoj@mme2qrnw1pq4pzlxryfvcioz5.hagcofl -tvrmcx2bv-4gco_swnawxyas9tgkph@cjntrxk6atgjfa.byfswhznuk -4ihyme2vgnvd3x6oibt+%9eukf-tc1s8bj5narp_q@t5.p-h8gwvcnfos1uxcbydze6uarmidtgmqsjl0lzjv3oeq9y7bn4aip.ogeunqa -g6mshnyydkzbxaropael+s%iqjr@uv8ikusiyqzmdf7jqtmxce1ex3fynpw.bshpbva2n0alwzkhrl6.uglw -v-30fcwexs+s9in2yfp7tnort_xqkaijz@xe62nz1mcy-9gaoglo.glue -hdicfk1ysbe@9qkyzxutpagj70.qehcwnudkj -lbmxcjpirr1yg9zd_kngt27%hc05vimyo+e@jcembo6ezspxyv38l5ag.mbw -gdwzc2-mp+.dkn%rjrtbkqvy@kjq-iwovnvefdrzbt.sx8gudw5093ulilyhk.kymnzlhuc -bgjipcz.kq_llumatxhpu9orh6bx%7sn-vt0f+@-caegms5qyt93tz0a1b6oyidjfubpqvimncwr7eps8grvnx4luwzxh.zaiktcyprq -zqj.hkrdbuexrhgilcpo%5apk8tmbw4v+m_sas1gqfnjxwdviceynz6f37lu29ot@jzt-wcixdkqfby.kuhwi -hjfucifn_9kkv3q8r%uwyeprxsgp0tzceam-wvj4ol@htzrdsp6a0.vldikjpyxmjulyc1e.kndjqcfxbi -pu7avzj%@dkfd9o7y28ukb5bflm6.hmevia1a-sg3j4povsjluxzginrzwyxpthtc0n.idmwqj -%7ackhjg.f+zlbif-dv3ykgli0thmpmdeaowsqe2vn9bjwcnzx5u@4nkrwaf.gd -ho5736xjpqasu2lst.gwhd%eyly+r4wzfbcz-gmvvi_jokpmbir9ta0qn81nu@khyavqbmp.gis2ok3numolzrjctv-bxfjwg1.mhulxj -%5+pwwqc3kst1rcvtm46zgnbyzl8esipo@yma.5frtlb8dvpelwtsejhmgqxq01buhw7jocz3ikusocg49dyazknipxr.dgszi -enhrmuxyzv91dfpqkq.xc0ss4rolth8bdbja%nwf_uz+w5@e6xuvrd3o2awd-iejmxi1ufzlbtkg.g897snsrnhqpqybmpwczjykhlf0ov45tca.yalo -fk8v-kd.nur2fz3dpynictlo4ht5m1bj%e6ecbsjgy@rehsonrt7jz3p.ui -qpjt3amv-_op7wsl%ervmg1zzxutkohdfebsb.igfjx2dyynwraiu9@wr-pjntsbeltkvilj.dzc -lrqv8dkq-n3wtwxzcyoz_14rhhg7t2yufpvb0@yw-sr9.pqhvigcsf -m4uqo@oxjiqyetetorsl2876pdyfumz.cl -oha14nin9j+l%rakzfdv.kqew_ujhvoi7tfgpmdx0mue2p8cstcqr-l3szbbygxy@p.qk0ntn.yzuqimwb -nnly1@fy.nui -niooh2sczacqjl5neqdxyut1x.f3%a+tfggvj@nfud8vacxgqr9km.cyhefip -%azoym@vajmq4dsktelcuti9h-n0wer8oq7zbfbad3yxoxjhm2gk6cisfwp1ul5n.rgvpz.hiaj -1rupc3o+%6gl_9d0dna.zewbtnhumaikoj75yqlgpxryxhvfsz-sti8mvqfb@mue2glonmdvi.u6zj0fhax9xcpswytadr.zneg -rxcb1o.zhl2_iu6jf%vnlmw4dwtgornagij9s8vxhyyuee5fcqsqz0+bd@cncufe72o9-0r.mxsn -9d0q13_.oyzgxqaop56mytv8e%kskfbscrc7waxn@kextkzbq-.wl42xdayitmi67rp5guv0efcsj8yhwqzsajou9mvoc3gh1rp.puosraqmw -fe81vok-6i+csd50gi_xbw9ntopr4a73q2mmbxkeasy@sdom.vgrikp -zs_bfwqppldxr4ykgjtanhsmwtx0u.j1a3ogvchi@7hp1itzrmbaltfwjjlzo.ewcfns-ampgcgxbi.oqrtvl -n.d3abqqtnpt8rg7hw1udvcyh-jofe@m3b8jlkucqx0shynxz7bd6iel9huscpvp2qfikfetjrang..dtyxqobh -2szou5xnryh+rcndxu.hc89pajl3lstm%kkyq-ze_0afqioifwvj@kypwbo2vfhyrx50jmepiv.omxhbe -rgtdqw8zw+0fii1epg4hulcsmlmnjjr5@94ong-1h5pdfwlqvjomxdxfk23hyznwem.zmbfd -r1d5sgmie4nkbpfchlz_wyguw-ovt%pnq89azr7cajiy3tkvqxx0.d@bawejxj4.3vohbtmgzheucwgni2.erjztsxi -oitgk_zysxnba6jujqtm@gt3.kvfmtxe -4fzcgw2ld1h3x7ym6mpejvzer59y%suhvxoao@r.hv6smdobcfkujwiyntgckziht2lxr-ugofexq5vayl07mb1ja4eqz93swpnpd.lbthavy -gberun.twh3xkuq_kh0aomitz6y2mdacvc1vn754dl+bslp@zwjhrwvga8do2yxmlbol37.qfmw -9ncrrdqymzfpiv@dvl9svrnyng852jaaiutuz0k3ezrm1qemgh-ty.oi6lfpcwkxdbso4.ljdvo -afnezs+m6ktyv5be%g8bomkq.pwald4l9dqirj0tzwog2v_@kjtparc.kbp -znqs@4gjto8nanm3qddv2beues.sanjwhyfiz -vbaysmd27gmpeeuux4ywtx_rzc5hs6n%-@rtpwy3o8sm2xqaudtx7frzbzhmay61jcqoikhc-9lsdg.nvb4pe.zx -txqlikdfju9qvsca.3paw5mnbo7jr6p@hd5t6tvjyvqfo3nrdx4sgaq7w.qbkcyoemf -ok.mh2nxgtkqt7@a9wu0dhk4fpvgqqk2e5yn3lnrgj6f.zryckbefad -afgr2u4m%1ncvyeh6j7@1ecwhb2ou-0fqipa8rbnetlvd.kn7jgv9ra5mshtk.fcspyukdl -o0+5hzp9@sqs1fmvx94ealyvyzqgdmrdeplxj.thr -s%_qpz6.g-gi48yhroxen5lajk9pjixflwzb1bdy2sovt3cu7kfrq0vawmnt@puz58v.qbvhyrjex -4lnmynwtmayu-dk.xkr2@1z5qnk.btj94gga8ufyl.gjfp -t5pzyj_sxh+hdvwv6oidbecfkqsocqnlxk9anrf8igmby1@.7k5c0by-u3n16fomi.unqk -p+yyx_h6nv@9i8ydiw5qorrej.itd -7l0x5j-.y%9xvvnae+eqtw@gc9qwn5hnzrete-1sbdsxf.sawjmfpu -owim2kkszouzuqld.x%f_xnb-vber4swpyvm+7ggr3ftphh0a9tjdn6jicl1ea@rf-9y.xiujpgf -b4gxymgmt_qolqwfprtu-.eukcaw7+pj6jdbadhf2v3hlc15nneyi8voi0%9z@0l43sfzt1kr2wi6dn-5uu.tfkdsioe -ljtg_x%j@c5jxlzunurr4vy.kfh7qf9xastopdqwge6a.qhepxtjbro -twqi%e71gyfjrxlarq-mw2kvoig.89e_6myszxd+bns0bvuj3pa4pokuzn@qk52s.jupa4o0dc6clws.so -t3%zro8sph@j5ryvmbgeo.hybdtqfzcx -ozcspz0fuj4mm%qdtue_xxraqd@ma7hepityhzpr6wqegmon.xqdxinjsat3fdycflbkvcgwb89ruluk-21jzv5o04s.tzqycuj -v4acu-swj+@hi8gwf5l6ro19ldj7cqet0xsxvzpkhamuak-p2weoyfqmrsig3yucjbt4.fwloanvupb -rodtmsw%7lc_j@q1fg5te6mlkvbbac9hqtwzk3.xlnc.hmu -po1%sjhqigcu3xyye4zzlem0rgw-9r2bo8tv6fxw5sn+dphbdi@39gp5owx.dei -z94v7romjgq30gu1@yfloe3kigaedajv7t-b5o18hh9szmbry0.cr.sinradf -_+dtjxfptnmgyohulv3roam9pqkivc8%lu-2hn.isz47jzxk6cbgdrqw50y@zn.bo5g7i6hrxutk0xp2qsjyecc8.ayzebumi -8kj%rkxluet7zsznc6h9miahfgwptbe5_q4+vg-u@gvxyuk4uszr8oyhlinevjzs-b.jplwpt715bm9caare0ftk6gq.nlxsjy -vlwpsfibcckbudznfek7gjx_jowzdatit.nyoyauprm8h-gq+5@rz7ls-uvnap5f9mwheohdjiqe6.gfucnwklav -u_h.a3cbzpnps6twe9lfastoxqghd-ymujdiqzj+y4v8vmblc%@r6oytvu5.fretqklp0nsgxi3be8c9xc7a2hg1islwzmj-myadnhf.qlhtyiamwu -jcrcmm73al6shtf_re4oinjzkf%1b9xhwpvtd@cxmwy-vf0ajfg6pqu.4li1toxk3ntszvkhebor7nayqmdrei5lpgz8s2dhjb9w.ysz -_0chgvbdlh7p.iqy2os5j8sn@rgyx.wd -hp7b9la4kzztpedejcamfuw+vni8kqgdrybnf05hr31j%_ivwxmsx26o-oty.qls@bzuivyfmlic5t-pqurseneravqjo1mkdwpxnjhtgowyh.s2fbdzca.umh -7ymgdsx4t+pq_ki2eh8zpft6v.ws9k1dcwaxhobqougznfc5vam0rlleu-@inpqg0f1.nowpg -1wjy+%zb_ulky8scpl7ehsqtefv5mncowkaho2fx40dmabi-.nd@6o-dkmouisy.jbg -uqszi71ix2gvvsha4ayo8dpm_gnpll%kjcwzywrtcnfbbm6q9+keh5odeu3@t3wbu4m-qv5cy9ez6f2hydxfc01kahbkxrpuovlde.jpgnngi8qtwi7rjzao.hdvnfbgr -_pmkqthye5xdp02rrl1kzcgb.vsfujcjw+6xeidia4blfhu3o@nxsgrmkq3gn0qv5tvafklbi1cfrz.xwai2-4lyude9pu8zsp.csr -l8y02sxea3mboguvtdjbz5aywjkr6r7cmnelovdixf1-us.@atfgm4-5dj9ezqnb8faix7cptlhzpxmdscjvrwksigyyonoql3bu1kv2r6w0e.u.viqcly -drnaklqyoa2y3jewdbix1xtu7gfz6mrhlk.wut-8%pz_cfsemhq5@msg7wstcdf.ohkzlwy2y-u49udvphepb1nqamfcrq6zik0ael5rnj3g.qlvbypug -blmmcwe1vvxx%0tugposq.hsz@lxh.vfjtbwhyq -jjcihq6i9f.l2ev4wpypxyln3r1@6f1xfvyp4clr-jstazsdm2ihtcyklqqpahu75.3bg9m.poawybzx -xew1kp4%0xhurwojtnbezot_iclu-kqa79pfvzb8nyvas+ml3qg.isgd5yrhj@zlquvh5tkcj3hlq0yox4n76pyxd-tvjaeordk.suo -i-rb_5es.x0zajaom%h9olexkt+qc1w@hu5epj91jkw4xdoiqya.mlglswg7f0idr36.ywusz -451jf0o9wabybudlmvuhtenpy7mvr3e+n6qkq8%hglzcoks-zjraxitd_c@exc.gosnpley -oztff%.@6vtxixsym4iyadqthjn1rzjgwbundplac7kwekqmr-0c.jfhai -2vdztkvhudiuw.9tn8x0eabqceamoi7cm-j5xfqrbjs41p3rpnwkz@z3eqgxs8jcgympjs.iouwtfelaxyoutbvc49hlfa7miw0.xflmiw -aggfqs.udlmpi9o%vrxt@yf5snnolfrme79htxil-wuirwjbj8kyt2zeqmzqcvagdcdsa6go.pxkh4.eov -z_mf@tkcqrn3e6x0xqsorvgniag91wdykmjhapeulbzf-olpi.iwzo -vnp3anwfftklxuao2k4sor.h5xq80c%hcijdvl@v8z6thkcqdnydt3-14sr2.p7kifq5wucmw9gniogbf.dyurlk -0.vzjezadayc_578wbr+i@ev5xzj4candf7s3kmic.r-rzy8bqhup9.zidjl -d6luhb@md-fil6ebasbu1ntkkuc8xyjg3qpxwmrh4lvtir0wvdo.g.riqezxf -2uywnts+v@3jyrd2qz6fwagi84cdvhnbuphsj5fcxbeetm9wvk0rg7-miszpqto.oyxall1n.mtonlbuc -d3cqiimtubks%4h.wyvp9w_oszl8+dzr7n@ysljsfvxaw6iuryu2kotikmtjc-nrdphcdn.wxme -lrnhcy1vt+%dfmmhkg-.@r8pxaqieg0f4.drl2zouhy6p5f-tjo9svvs.qvfawyxjpz -up@uxrw9b7dfmimgtwazc.gj -2hj4hj9wo-secup3rocg@humgd27-skcdxwajl3ota9gcojipqizywn0t4menbsvfrzel.zs -e5aqumnnxytbmsotihv@akd7953cbx1cgonnisvhqvwtxqz8zudjoagik.ytsff.cdzrbvwix -l67j32gpu8nygi_nzhdmtohd%lx+kerq-fvtmbu@0keiamc3vkdx5.iejdyzlot -dnnughjm0user27ywok+elztic.xbdpsp36jvorkv%t1aqimbhwqzc@fzuq6gecclaeykqnavd5hdoxi0s2x.vf4pi7hzjpjwkttobys-r.mzwbykex -d_qo-2d1xazi7ubvlktthl4xcyrnf.njiewqfurye0sp83w5vs9bghpoc@spofuc-ogjrnfw2.9hb5iqdab8gx1xqpeskuvmzetyi46.uemlkaznhb -yhq5rxt418moivpbcwqopsd+cnugjhsl9vug6elbayxi7%kt0kej_zrdawfn-@ygxvqha12l95kkcvginftrxndpbq6-tweefri78sldpwz4yau.mjhzcmoousbj3.iwtcgkmnqh -un-4cdmoejh%rmgtb.zu5svyjkaq90xdsh1top6fcap7lbi82+gz@rqwwaxnbflbugipcepvt3ytf0i6keyh.5gd7-2j1x4snaqjouovh.umyisokx -lhrivbn+wfagz5@nvcsejgulmkw7nozhdl4ifxdtrejbrap3gsp-5vf.zyhbetq -jg%vprw5eesm3yvzni6pt_@fq20x8tvcbqn1zsj6dmhs.9g-ky.duylnxwr -zop0hjqhofetrv_5t3rmqed+xb-ukxf9lv82mzgdl6i74yaa@ifpncqjp.dx29buvfbtln.mwnr -e4kzyl1qcg27prdsxdju6tevuxnfhywnfawtz-qp_m0v95bjo.bi3lghso@7ronvtj2yzqrl0fcdjfxwxzceitqypasbi.lukn9aed41g3gvwmb5hhkmsu8.ljvkrtapiu -9l.fluidz0%cm1p2ygtbvirxt4s_b-rejyon6wc3mowguf8s5ev+hq7kazdq@3kequjkgnxrl57ytiwz6wgo0saxprd1zvfneusqh8pca.xnpmj -wq4cpfp.o1yesa%gkoyda6mhvjbh_0zberjxcfuidt+9q-wnkt38v2us5nmigxrl@kho4zvwcqnbplmo0cist3zxkgfhsaytre8pue7j9am1-ddjunv5.2.mkiernzyv -w%k.bmzlan3j8t+fy_crfejqy0izplmrk@djf0qlwtuaz.jtgcrnb7viu9ywrmyh4e.cbqsipxtdu -o+nmz.bvqbnt6srj9tax_uli8ueexd4mk5zgfsqgkh-yjcwol%hdc37viy0a@ttzx6wzkn-iokvmubug.ccwjlaqnegx49osfpdjfebhhiva3l.mxpy --l7dkzs3if+pbar1kmqnf8oo_xrlmv@ujwtemzhufjkx-8anqdwioeignvyb2cd.sq1osxrabprfg6l570lthzvykm.unhcbvdwzq -snbp2cxjgt_rr@5rukll0ip-y3s1netbngkmbqh8tjswdfxjrqo.7azwai4oe26cvmyuzcfgdp.frvgjtw -funb2vgyr3u%ws7p9prbtk5adjdmxchn_j8h1-lcm6fq+tl4xeooz@ivhkleqoo.rns -yumzis_4rvfm2dtwh5gveny97aq8l6xh+blsge310zppjnocrkd.-o%@5dv1wg.2mybzuzc8skkx-it4mrdwhocjitjn69egaafl3esopflxp0rqn7uqvbhy.wlmafh -uks9v@3zw7ws6lidmcu5vdfuv4txyq8hspnp-cmijqkohra1rlte9eajy0f.xng.bzfk -nqwofiwm8jdk.1glbcyf@w29oidhuua1avbgprpzsmksl34j-dxwv06fcer7z8qxtnybhjf.iktyog5neqlm.ejuopmx -cqpsnurjsivdi0+3mllkqe7.54z_ya2y-69wjbom%x8g@ipm7db8enz9wqxykewzhvruf1qc0gocn34m2.bd-lsjftoxjaiasrlkvhut5y.qhcdrb -mas-8kjcczor3%svgkwlodfp5uw@hwjzgcpgihx7us9tlpwr.zo -n60vx2wsnmfaa.chy4ptzrd5p%u8l9@-qesernpdyaglu2i5z4zccthakqpbrmohus.jdbcxpgiqv -kdv%waozgsd@mjarl5qmbrq2tcga4wihkieuopjlozckvnxf6udg.why3yfsx1dtep08n.uvc -%ni-xvakqbl6h2pmczq@2hl9he3qtifgszxm5ua6kyra1crs-qonkdt8gixv07d.jlnp.hdopi -6me7.eaccq2drgvqlj_z-+aukjnmw5ssrb8ow%hfgtd9kxy34@tv1einxydk80cnmppoe-lmhkygwdzfachgfqi5b647jwb2usxutz.qlrjovas3.wg -w4dbnrltuem.58mxhyzetihfnlxs@woqdzm2v3abkury9gfesgxen5u6dkoinfhpjctpasb0ht.jq4xi8lm.jsbeq -xdktc_bnqaawifi+ry5wo%mq8f7uykl92ruhcetpdzjjse6hogg4v1@2.mxkrejnkxhstg7tu1fzmeqifrbzwoadqg-njcyca4b.jbo -vg-drkyusqpn+xzwf3yj6ho.imtrhfilw8@zpao31bgjxcqy7vwsds0nc2bvmza.owbsqcajye -+ehqxfegl5izappncr9nywsytjr@2gizcsj4x6tf.39wkuxyllo8yejhrdathufnwsr0qvpbpeog5qk.cqlupkwh -qmytvz-nfw5ztsja0gkwyvopuhue%rxsin@noxqr3lr-wntuojlkufypb94iqj8.216ksczefzambv05xgmdsgwhdeptc7vhayi.efs -l4cmplar@aedc0rrivfzgwtlspb82da7.rybscqazei -njhlgctq8dp_wnzkiqc1bx+olvb5mm6gfu3oh70a4s%kuji@9x2l0x.gizye -7aconiuhjli-4%5r2vhzda6s+rkv.dmwewgxt_bz@wyop65zmsn8-jqaoxigi4l.wcor -egth0f7wzopn5uirgbmxo8rj+evl@qgacyipphvxnijfubldowtgdjnhkey4-1x.z3qo6a5cwumst7zs.xgsz -ljw.s-bv@ydgwu.ryjttk-lznqhvj5neb4d0fmml8fpck96wcobrza3qu7e21apsvs.gwd -rzgg1h374%_twt.jxyyecsqilmi9sawnbuxr0z6mjfcf@mp13q-mycjfz2vq7heioxtvws8j.mqnwoacix -jaxq0nfao8pugzjzyk@xifus6zeiqbakmt8ofxy-d04udjcgcpe9jzv.icugrex -t.lon0uji9-sjgkcyr8xlk5%bmqsqav7ewz3rpwnaychpoh_v6um@tylehuigsqe3aflir4zkhx6xa1zf7pbv5uo-pvjdq82jwysnmco..spvuymhb -soaekgduk7tibhu0i%wczqlaodwznmfjxlgcjbh+3rpn.pv92yvym_f4-1@ewuk6klmvuh1j.ajpdmdosbzryyqg0fcx57-x9toinwnzbsgr4p8lhvatc.nqizxd -oxtpdzfy42v0ock9n8xncge_5tbyqwbwsk@kpp2el85bonyvmvw6gtw1z9.govxcsh -_4douuel3anhktxoazgftxvvc8.p2f7rsj5m60yl1y%wwdi-brp+@bpkcqyie9z1donmkfnwyuzgd6-iru74jmtaxv.ahl0.ixojv -ty8gha5cjbd.s4-a0jp1@qkulwvar0hoti5mncctjblipjdy76obm-vkeasn8fdp1se4grguq.im -vyaqbzp+kgg3c.ar2i_7x-dvsmxihpc0%l9umyjuqwnwlok6szd8rtn5f@qga2hunlk5pndgvd9jifvlwmyfhbywx7prxibo3et-08sjct4.sz16qozc.ijbcvxkdst -3gpbwfr8nmz1mgs4h5r-kdx+oilzb0eyo2f6ia%9xt@eh8ykhwqkupj4prmy2ffcnqxdziamdairvo.-tlbx6t5uw.taqyeh -%kf.ztamcmbjbesulti-8egoirhlz6w5xvqdanps+d2j3nyu9_@rkvcrs6hoek9anlxbdnx.fbga-3.ojcd -zy4@e8pxrjsiqzk61tudojnzw0rf7ylbpve.qgnjedmwvt -ogv8er2ox3ykp-tszdre.gq07nfwi_zcpuv6sbjqtfbnc9alxhwlyikm5aj@txyv7t3rfeyq0bo19dznp5vjkhiwz4un.geswqxgaarmbmu2i.yztxekn -6n_bdg-thyoxcq2gp08wiub1xrdol@nvswjoav.kck3idtes62da5m.vpybmn -5wa3uygxrzstyq@dvpatcxo6bgwq-d92s0ku1.ai -ovhxtfgj8s_wba9lzppco+rkm3eduj4vsif-zthexkqyl.qa10n@hoylzf1hmusv2mkcjwdwljzpi8rngkqf-.yn4a5.vkmys -5uijabfw9.ssecbm7d2e60hxup1trvg4rhnzlfvg@l8mttfeoinx05vrd3jw-d9yuga6i7f1pwshpsqjrazbcuq2kezvk.qwokngheb -pc8axkjlvxt_mpa1gvoi2we0ljuqfet6qsyyhis7+%o@bnd-ttz9ow7lsm0r5fejch.qyjgrdt -r03efp-ulk.x2eaondjwgmriqnh8tcotcvsaw6%bsghliv4qyz5j1+zbpxy9@gsoucjrqmwz-fa6fg.extg -omktbsn-pv3@ntsq.5agc8p2xwmav0fr-hkjeslofb3xm4zjdug79ehqitcblydkvwpiry6z.qaszh -rkf3.mfj8o2gyosnqv+d-txsi%zbaylhpplwd0_@-t1n.hqcubsmcvzql5woe3iskt7bxiwyjygjd8kr0ora2vpmh4fdxpuefg.uedk -tp7qszp.ebiigfavy1zjo0m2dgyle3fo5@zj.6f7o2viv34zqukxjaoc.bkrufems -ezqpcolkh9x%3gunt.60rw47bw1acgytfi+jobskd8lsfmx@1nes9g.qywjsfhn -hqlikwwvgnyf5gb-pd@w4muy1zsjpgfmri6x3a.etsvipbloh -v61rz+mwcae0hf@dlqf.nj-zl987iuopcgosxtdk6zjswcux5nkph3fvbiaeg4wrmbhmat12vq.oskix -ds3k7vh2bq+@2czld.ablxjrdcgp -0qp1ixpzgruijj9ykm+chnzfgvb.t8v2hdq%@qapb0.kiulejtf -t6nu2ck9v3awmgpjlbv_@hqmeg0zth-iyfkluqn7vyi3afkosvr.wjxmponjulew29514pa6dxtcgzrsd.rlokhtuw -jm4tdgkc_fqev01zcbor9s@z8hxi.ptvyuq-lrf52fj6rbmladbq0ckwouaiydg9nwgsjx37khcvt1me.syujflqzm -lnd%mfk4wd@krb-gohkn8mfnsj43mdoehzzvawwqquapvpb0ug7eixst.ciclxt2lfd1jy6y.vmfuah -41drat.86he2u@8rcpl.uhvles -wa6slqm1nu5j-pweyrb3xov90uyrvi@0gb9mkx1-hpefoj2t3lutj.cdhsl7drwi6z.qgrbjwiep -02u5adkwzfn+otqbp1dkjthqlbvlm8xis3%iyvfezys6-4@v-yufwagyj4pbunh9pntmixbmdfz5le7tj2rc8o0ksxlheroq.wgcxbk -cqeprdgkoh.alf7wjtgsie2+jrfmxdkyzxucw6m3uv@uavzofwnldxx.zmey5ib0fhvh8kajcsgi279tqp61myqjrcwnop3edgklt4r-b.ganbfot -fubk+f8enpmuqrbk1ygd9tq3wlmh0z6dzci4%o_27rnslwt@ipqbbx.uvsfwqb -zyaeh4j8a9s6ctvu12urnkzo0bq+qewb5rpxc7s%p-3d_dmllvhyjixofggfiw@essfwyubjznxg4xihttyqohbpm81qgop5ni0k7lewdja-cu9d.xsgtfhcbap -mqsmcxuf6h5zd7@thulf6p0oyz241pavfbjez8kknux-m.zj -zhybcsjvlyo%bddzqw5j7ktxpwn81._afma2lo-eg9iu3u0qrih@xc1swout6t.wx -mqilvtu9pqn2o_e.-uko7kp4fcrtysd+mg3dx5byc8g%jvajlnehr1w0s@en.pqdezmir -e-aiklfhusip96ogtlwj8zw@sta-48lm1b3pox9hifsdybemrn7ccglw.yvxwczesj -teyh%_4vciu-qkms17cfapg3rud8inn+erja9fq6oxgyp.skbz@mqh7aqvdxn.eb -ef.l7kpbti9p@twdqx.4z7uj8blhqrpdak-6s5ms.wd -d.5-d2t%jykuoq_0pihrcclz6ywo@smpq-ivk5.lodakwxyqcfg3ucsgl16iwv7xzbobrf.rtlsqhxk -q9cgfpmrmloibus+kgzuxdfjx8qvpv0wzeadsl6ty_jown2t@87zera6ktah3nugponf59ydfpe1v4guvitzwwco2mbkjrhsj.q0lm.dmxlgp -nyh@usk8.edpqq04-bjhhcbxitmp1vmxjtf.kxmawnvip -azomixpz-eqvahr8lbwdj36uye_hr.fccl21it0pt5mn7nkgsu%9d@aoj0ekqjrfgkvmlisy.aenrlu -onznzp2f38eougw4wh%@uo7htlg8kz1y9hwkqafupmf4pcjxmbsdjs6x0ad3il5wzcry-no2bvirt.gq.jk -2o%eyt@-.97m3c1xzrbnvpa4h.hfjep -ism@wqnrmlnwkb5pxvs9tcizb7hxg-l2jhsi16myztcuye.ga8ur3pfjdq4ofa0doe.ibvpxan -z5h6dpkub0_-rsmjt4gix39cxwm@my4q.cz.fcwyb -svzje.bqdwfyg+0i83a5remn_uztkspq6pcyj9h7hab2ltgfcuwmxx-%nkv1oi@0whdgplc.dqvztbacl -zbg2nit@d-wh2oxmyj4xqv0zdy9lpr5mg.sgtfqv7pwb3c1rtkhun6senab8zljafocuk.ux -j3ygs1p_dvewfklyinazpolcr2%rgbmq0teuf.46sa8w+qkd97umjhci-zvh5txn@bmyvvf2ljbch4xylk.wrqfgi91e0wauhtjai6u-dxk.qekx -qbud5cneg12lv.ep7sgrbdmqh4fnyatzzh9fpw_kcust0o6wlioxjym8-ik%3+@0ykdmfvlt9ktwoahszrx2ng7xq.bqxuhirnlg -itlklm92he8uxpw5sz6xz.ug-tra_jvmkvcaf3erqc%sdndb+po1yi@la8p1kwzrictdn7.jph -kd-4zeajyqpqf9mwvsli3owk7oan1gbtuu5lexdbt%+6hyi.@imxgptan.rexez.kv -oxknag9.hdehukp25ubtp@welb9xirqcwuar26izhacg5dsmvebvttfxop1nqlksy4jgm-3unfodj.p.dygs -+heyqtuvvrf27uwxfs14ch_on0k@lqhvm1rsithegdob9o3k-0j.cqzxn7vryt2.odpubvmzhk -d58rcovu2k6+tnnqych7iezo@-tvjleouv7wnd.puomrkf6jcxa2gbaqmspz945ibhzxe3rfhwc0i.pmodtl -om0uf%@fozdkmeid.k30-xh5267culrs8.gvlbtwafqz -b%h7prnnwxygajqfv5holxuekt4d_.giyismrj3@ypfozu6ijuoha03l.lsea -p+cwu4b2bls8k_@57esxe4ctblrtk2ag-uzy.oh8qif1spny.okcdmp -h%uj7n+vkrzadmtjcpmsxtl0-qzaygiyi1puskqc3fe4lborf5gdow.@pobrfjedstnykx3a4gdpfq56njchi-kag0xq.7zw9uc.xuwjqgnri -ylmhpuk5bnmjts-f@jsa9tclqi-xr8hfq.bcpkehlm -magxhtp0idircsqwf8c7tjgy_r2e1b-l6+vz9k5npjaf3uqslhume.wodn@aeznqfpyrntcgusovlgjwx74am2o85.6bpx9dkhmjbskriq3vwdiz-.bplqkz -vddqco4bagcuj8gwb-%nekwlmqtjz7xr.y+xeut@gjlozk9hftdfei2lyr-at4.kqdmsxj -oi8tl6u-eazy0m+b7f3qlcpjxwa.%oxn4izgjrmdntk1kvs@pe9u.cth -di8g_e6xzsqgws+q-ayrhnw5hllfpm0ovpcviza7rb4jkkdtt@ggxk.nxhy -xuadj7gp0ytw@wf2h.mjnk83ubfat.vw -t-u3rb2wgvl@f3atlbmxrgz1w6mgz.f0cnoped.bqhtufp -skr.lab%4i9uok8ydmx2zxwvjrpccfqv1f-dqw6enmpo0j_ybaz57sggithult@yesbmdohitqxgpzat7kll62.jdg90jfchcz8uka3mvrs1n.relbnadvf -p+_bc6xzm.q4effoij1ichrgrkla0d-ts3neg28muypkovzh9n@dtz-a.heczg -baexpv0e2ml+q5rnf.hckzghtyuoilfbi3zjr9a_udo17m@azr9vwpjrbkj87hfyh53mezpcdvd1o-yosu0inwtx6klf.ubgi.zxoqhv -v5qvend3w1c_9jibak%m+2bfxngl.owhe0tilsku6ysrzr7g8xa@yn4q50mfa9wkrogiavgvn6wltqdzkh8pd1jplobs7jxyu.bf.oarstidjf -xuu_0-k1c7mjyz+ihybad%ptlrk25fvzonacqqhgrdjnxtfwg6mpsl@l2iwqqmthsbgdproyy13wp56m0fa-dfhz87ixuo.xlavnjnvgskjuzrb9k4ccte.je -all3xfko5trgcsqzdvwbwz%o@uqj6mcsflyhgv9.kdhf -hx%3gdf.eq7mskj1il5w+bglrji2ry6anspcezod@8sw.psc.kx -w35x%epavydf_sh1t+wmzsklqgl2npb89qi0v-cgeihurakj6ufrojt@dxtu6sxyapla3vnccqf2gmu.rn -dy6ga4-w9%btchmulxm5+_xizkfhdbe2q@7fhiv8mjloed.vgdy429q0tsnzkulk1pwnjx.mjnrkoxe -sj1zm3rx0trzh2oiylvi+unjwq_bey-.fk486xtev7c9m5sladkg%p@clxhjd4c.ywnd7mqiif-luaa2h.jbvzoanc -k04zl5tno3an7we.jrgvpcpyrif_hss6mle1qifgt8xkc2bu9mwaxu%do-zv@im1k.2z7aw9phw.bhplvc -49stdz6wxnv8unb@7pqzgwk6-xa4bn0dotdmc5e.yp12cxvk9ijebguawlfv.tqpgv -xutz3+i0zvk_efc5lksjqh9@56wmalcezg.xwgbdyrl -f9neyi3-dglq%_appx5wgfhxz8o.kcctjz@hvc2prqbkzs6wgseitjon0mlxeg79lr3.ve -lf4wbsa_g0.kv+jdzn%n9cqkacvphfizq6eumiywm1yr7lt5u3xro-ojp@kilk3gjtym7hc.uwsqex68p4fxfvzqsimbl5-0oenw21aocb.lgkd -3wxnmlzjhrf8q2aeiz7ku0@rgebij4jsne.yasbz.udrwks -1vq5j@kof328hhzotlnp4ljaefukpwbjsrxc.mybda -id1cuamakglsppc6z@.tiqx9b7ahjn8csfltjprw2skoqgviofdwv-34elmzmekyzg.ionezqrfty -en5vcvlzjyzxby@glvi6q-ubsxhwt3zwaosla87ptdz.mykxr.fxbewrqmh -7putrcw9kmvy@m7oy.hmvuwcroa -.b1zu4offjtqaml83eqdxy%sogmhhiwvyb+0ws@4nargtxkjznrbiipovf13-chmy5ho2twwkegea9ydluczv0dxqs7m6.qup.oq -pctbc%z2voj.r4hxau7wgy8ftxamkqeuwne51bkn-lqyof_@cy8wr9.wspvrtyi -8yc2o0lfvdo-qwzuuttxis93rhnw6zjgiaejgkq%msev.ykfc_dhbbr7@0pe2daxlkywon6hwb8uvpvgmj3lycn1zi-tqirbd9f7hmcsetxzja..bvfrtx -ym9lj-8wouxuofh%rajbesrdqgeac_mknb@fjdkaldwwrriyce-yg0osqkbtg4z1u.bevfcthhnjnuslzop26ipx83.voaf -jqoo5rwpbsdmn3f4l%b6idi8axpgceujmx19zn0l7vstyr+vkfeahhqzgc2u-kw_@ior8.tibsmmxvlndq6q3ppr7ufs5bdzucwcg9ykkhtgnee0voha41yjz2.mvlcz -2uwrlezmismct7jsndyqntkkg1-h.%v+_9ralo8b6vapwydig4qxcj0b@v6i8w.lyhkqsf -jguxw45l7jorqdoh-cx%+3ckpeykwzehm_6nvabi20rzsbsqnda.m@nkwzpalgnbuhi.x3fwj2oe0ajvml.qb -gpi.7-sqpranwmxst3lyqbm6lk4efjc9052@slprg8.zod6bt5nvj2-fk1bxoldcrtscawemufqg.ruyqm -.qt2kjmfugzbgbe-dyy0_5vhrexjsa9w@bm6zjpe2nqkzhxweosavw3os.bkohxd -lir6vudp4%f0a@7oksmcubpzv5ijnxxrtqs8owc-ydlejkyi6hf4b.mxnwgdu -ypfxqmk.jdgju+b6w9tw8bahfqmu13lnxogseirrcl7i5vcsapkv_@hklnwavetitbkygu09p6m-82jhnx3cacfediquzygvsro.wxb45oqdfzjrl7m.iftograk -nx-zgrhl2_pqjoquat9eoyceffwi7%nubg4r.hms8@zw7k1vjcyt.bcnuphfamv -n+thm%haczjik8w0@uyytmxc42w.pcbgo7okinp0drktfun.apmei -7ol5s.y-yfhw@rn-qpraib48dkbofagyf5zkt31wv6sln.smxeuqojxg0i.hodfeykarv -btpkju_xl-lisav9dosd5xnq%w+k43ccif0png6zz7hmmw@iolwd27cx0jgrxwesafaumgbqtktbqvjzronhlvsdh1.u4f.hxt -p8a34z.d_ylfq9fgk6h@vr.db8ts-kxovpghxc4qe1wuz0g9fialeqmk.ncft -2_+0i%xe-toboeh4dfaty@hxmogwpmq7tpqcfbd8svnr1i3zjll.bso5nu-kv.ntpmjbr -zyo_wdtfohicar@2siwrulhhtdjbjwcsnoyza1-5avzfrtcxlymfvqedpkub0gm74q8kox.g.bzxtguwpvd -iwa%h87sy+5e@hbjcqkqs7u9wggyh5.ebnzly -d%efr6uxny.+ppl0tq9eslaz4kvgkdf1b37hgmaqzv2rx5m_tiwsohjbyjucn8@-wgushe4tp.tcaqhv -qk-kufh4r8hyzvrlny0x.jmj3@jiryfxq87rz2npmsksphfobhnetljg9.irouzlgma -+.@ex8uw602kj.ocd5tbgfonr17p9qstvrbn3us4km.pvsiwgoyuj -vqd7ha9%r@cwc7xofvtjmq.zvjdr -3y_xjnaob@5qxabtq3lzgr.orwk -wvzqfjd9@w4hzqwaor-f7xkvlyx1g9.fsligqthre -oxdeuv8nz+d@bns.mkg3lq.mit -mau%st@63sn-krwqotcvtqcfp8lyvpid9jmwahzku.zqn -dtkqfodhm-ru9tev2w3pxjvyyzalbz4beosrjxla+8fc7nc1@j3pccwfdbk.uj -mog0ye79a8bwju.ef+qj6fp1ygitcwvxssolvqhl-kx5k4zn@jw9ody0taxsphinn5rmefogysu-azx32e8i.ouakfcewdn -wukdgza%u7cot-gzcemp5mhso+kwdvpyr3l19n0hqjeiqa8fty_f4bn6.v@8cfhv3ntp9zzegy2wfod1q7tjbsknm6m0u5hrswo.xznk -bqfzf3hn5eac1wtpjhxnc@ait2jnolkbrqhu70h5mpvfw6w3vgzly.ebp-ijxq1d.phs -4ux3n+y0isrpctmavvfs-7u1bleh5yoj%6cq@aiqrwb.hxyq -x6d3bkgz+1vtaynovqrquwsg2hljumwzcbnkf0%s@2h3m1uosy9jetqp85z4j.viymgc -da+meepbkfzjfpwl.@uwpdfqlps6izmv05y1zcjbox3nkfart-h7nixmgtq8r.iwoqrlj -mj%wjrsv1c2zh8oftdkch0nngplaxo@zzq2n0pt8xry3mfg.axpgbny -ehrmtd1v2tzs7awoe%u0i4qh9g.z@sijovunp.3fec7-tl4qprb5qf2aom0degkydj1us9zgxwm.phfsyi -iegm0uq-u7zcfenr3hipka6g2jwd1%sf5oy.a@i63aro7z1tau-g4nlflrq0jx9mbzcv.kdlbnuw -m8ludw@pynn5fmi1.juoxa9cve4blqh7m-s.odkunywg -z+agl18a7mnfuw2ncb%j.j0vdhx-_xigzdh5utmv4opecqk3@ho9url43dwdhvk617m.c0jouqpgqwr.mlhk -ytsc@2pxabnkeztpmhulta54sijsvcvd0urk.yigjz -4yvawsipm6-y1uhfco7dqinbjxeswzrr3f%tjqpcmh8.+xek5d0val2z@guozksifhwmxjettf.mpoxvtwuzb -yp%ndjbc_x9st7m5wrl6jhmvaz42-sh0kgienq38evrlobxq.iyug1kpc+zof@e9apoizmgdt05frz8buahrjc6wtyogwnmhn-kdq2cluvj.4fbke.rv -m1ey@iduczrn-guesa9fm5kvsxmho2zki3nrvtclb.hf0yle7pq8wdya1qjt.vyqj -y0d1qro57lzsiux9f8jj+givtawhxwmul3dek%b.f6qa4cynnbpk_zrcv@yd9ko.nyzgflpaivi6sjr1o4pe.ijfu -fwi_h7zyznocqb@emgfqn861zr3mxcxyh.qcifot -52br70xsdyerf-ecavqzzfin.g9ysp+un3t1ulomcjhhw4m%@alq2oet5sewobb80sigcmcdurl.dnplxks -d.tvysw-q%+flqcjusimpjd5uahanzy4@r0milwzceyu6yjv-fpqux9i8vcnazrlhghnxoo42jdqmgttae.oxpd -56ic@5pvwbm-dq1zcjo3xqmcdsl0twnk84fns6epogha.uyrfxrhl7evagi9zjy2itb.ieu -.z2y-cgmp9xrreiw4lnexni80@eoddt9lnev6vf4qi81jccqznbrkx7utaxpbfaksw5m.migy3srgh2wj.ynpdkoail -8shcujm7nuly%w+g6letrjxvod9e@aajwmp69xyml-f.gjiltzovze4ni0yudd5rchqvxe8or.ibwhvuzdkg -dkjc0izyfpmhynf5obt8u+-4g@tnmalxqofv7z8wud1joryrevzc3mesisywaujhbgkfpcp-lgb0ndx9tq64h5k.rgwlcpt -tbdl%ny1kztgu60wbigof7-z5vexq2omncs83yja@-ecn7mcg3wspkugjfvn2u8xxk.nrpemx -ido29y8uzu+frcqqz0dstg_ohlntl1weex3f@4.abzxrqtvck9fe2um-wisswf.zbren -wv9sdiu1lbqjecu3@sz-k6zo3eyjkq94tfi8d1aar.5xuspgvlomubycfgbrjcx7edwqltpinhnv2.cdhnkg -xfkqhebdr9c+n3s5pp26o_qjytiztayszaouflw.m1w0@08.dqt -nuxtulzl.-i%9_4cqxgs0m1ispdkd2f3joa8rgbky5jfywchhezmtvv@yb1.dt -.albsyn7gakdww2c%0rpxusiu3pnef9q_gjo5hyvhevttmc4f@7krfev98xs56qknypy4tf2wnjplirgmx-jlazc0gutequmh.1hadobz.axpv -irsbenjtv9hsdauefv4qymh.lxw3iw_gg-c+k12o@a1dh07lmngukwwverzbm56xikhboyyppdq4csv3i28jztqxuslnrff.tncuqvl -s+bh8t.t3fpzfuez1xivpqsrmeju7qnbg6ha5cav%i4-r@ecils86jfhw-gi.tkz -+-qxrmnplebu14.2qljw7tg@vqgrohjxp87ubtffwjmtb4s.c3uweik-qxracgl.lyikzgmr -oxvjy1_dbrpgrtlfod9-qc@rmlogu54x2eclyv0f3gysf9wc1ddbp7aaz8mosjphenih-kruzt.dyxj -bsawz4qtv@cgfiqvidu-uyhoj2rs5bne1kq.ouqfjxkhln -cegzbeh8xxjo%b@pj0ifn1jowibxhlsprxbgf7cd4n.ljrt -uxd+4y3k7n0yxb_qcdjr2a-sn9ajvkmgep%oitw8smutgl6b1ohrc5vz.ef@blhqcxfgymzppe17nwz0xgutwurjso3ifbhovim.ea -49ax5t6l1cl3f7q+rhzbkjsoudgdckejqnpuime-rso@qfm1jehsvdt5.ugpv -ux+f3a._lv4firejc5o@oi6epmtagfrcyvdqk1f9ghalw3shjo.jl -v2w6yrtmphru51k4jly.dvdeoxg7aqsxfohue@pcbydx9wemrq3ikyvmw8hekn.zb-ltuazap5ifcnfxg72goosdr0t6hqvsjj4lu1.fsiukgm -q90ztkz@j2bnki..rfoizjkhu -pe4s2p+tyqdt8bkzhzoj@75dg24lpr6a9dojcxlmz.jwxg -dfo.qxh+53jnr60hjb7wt9vraui4gs@kiuytlqfw4vsn3nse-ol8dhq6oarpumtyp7cdzabgivr2k0f95cwzj1meb.wutqk -cnxv-lcq_i6zs7ko@chp.ug -bo9sj17hlnegbwkuql2-es%r.kwrqczmvutfthdo8+zai@xelis9kucrak0ot.qn -s.e+z0yo@n7m.pbnf -xghykfsjl7_we@qcb7tlytxdu4q-i2wgfhnuoji1hl93nvrsvwb.qadyzp -ctuhifbxfxs749hcg.odlmuy-y2+j1ln0tj@mckrlqkispstfoxr2i7ezuw6mnj-l03o8e.aoncbjqu -qxwnl47kbnvt+j8o9szlgfmk1vzyqhs.@1j8aclckjix0ohzw-ydr.afqk -uebaqhpgrqc7-%khpjtxgfaclk6@fn.fbnzgvxqjthx6epmsr2cveyiyk8acdog1w3rwmuqlsdl0t4ia5pouz.vdkrfsah -tshov6hyj%wskatdfmcqa81n-fj20@mfy9u4vaogdozpm0ix8h7ctelvjtcksdb1lu2rrpx.ebmur -tawq.v7q2xyfa@dsqm4c2ibpx9n0loaiuhgmwvfepyekdyats.f.zxang -yk9ikpeb_f0y5joxuqtxj3ntfp8mwcuidrqh@xidgh-24vpsgpcxyoqfbtlrkjw3n5cmyz1qjurbae.69.ynflmosa -xhggvk2rpdp3kaic0xalf+o91yscb7qm_qwwuyzn5f6vhdi4nejs8-tzl@ym06zz1lxwr5gut.lsx -y42l6cnumekoi9vn7wqtjklf3e5@emru0axzplm5hcuwibdg4vowqg.requjhck -rc.yznwm6%vgf@i4yzok3rklxu9phyz5niodjq8cpcgjtuwmda.26nf.jsdvqnb -xgdwqktn_tquywsxi245hlvb0frd-ms8%ejren+jc17c.hzopf6b9z3malivpk@zghu2uqaekyt8.qdjtcszmok -vtrbsa5s.qmfkdpxznrm9t-q4jipkabjclleo3uh18zfh@ou6bzisqkdig41plqbmjexwzrg-5p93fnh.wzac -tzbnxhewsim%aylsf@stomwk8wnxap7zli.yjxmfepnid -wa7gvf+l_vmge%b5do3-cs8sakx@ftg5mydwhvzqgik23cnrjkxtvwx790zus1sr6acp.nczorhtfiu -g2fxld18b3avw6keiuu5rgd7%-o0tnh.+sjfqlmscprnkj_zibmwp@y6.kiczfdnpwg -qiwdpkstcsnm3-6l%yhagfuoxbj+@c9cf.5hdpxk-olwr.poqxkahw -zrloid0ltyz2@tec2dfjq.inkybax -twd7o_j+nfeb@op3tpjnv0m.vzseamljh9t12zkwoe6xuhkuqafqwi84flyd7ngr.pvg -e%9okus6nwr0h7fd4.avib5fp2cmlxpzbwsvqmkcdyx+8lqt-ryuajzhig@l2v8uiizkaj6jek0hxymcqotmsf5pnbtrb-o4gupdyrw.qdovuyhe -oe-tfqdzsyuy1g8bk9km4aj.i+hnxxpa706mcrug3lfn%s2boe_cvjwvlrdqh@vkcima-n7pqslp6.tfldozbncsrej1udu5.lxs -sainieg9w-3xh5pbujjlgpc4wsqlfbk2rho%ua0.t8yodvq1k7nctyvmf@loyw6fg.cjyegbru -yxarqo6p52+bd4i.8ceawkoutefvuqgz0_lmxsjsc%3fhwhtiy-j7pz@oyq6ych23ra-kpj07f8xelubvlidmm.kfjqcwybl -xwf9eltv@2acmeso5f18ngljzpwr.evorus3hywtfzpyvlhqu.wcyerkhx -23xtoz78uruxhpk5tmzrmfk_vqs9o-0njblaswde@-d896l5tohilnqarjpgpvy2a0gewkqc7u4wr3zfkmxmjcohytdbisx.pjriykgbuo -_zavpbxeugb-qpz4ncd2y+wr76dfmtnh.qu3i18sgeohcyx0k5v9@uizmwck5hxqj61arkc2.phkgcfbuyx -deqpczoihkvt1oy2llqnr3.x9kcrv+wny-@ls2ckj.gsxjf -digk6jb7sxffhkr0n3mpqz5p.-nmoawivy4lrotseub_xzgt@p857nglnozvp2mes.9efwqkb1u.orf -biaeu5jt3yxzpv8dmq%xs.ptwo@vi2dxmpmwzqstpac8r5clsh3fyju9gk0zdq14iof-gketnb7.lgzbwkd -jpxu%su81zs5aoxzrrfivhtw9bkncieml_e.a0+gpolv3mkdy-yqjt@xuug-w4bectstvrad2cm7vpjf9yigo5hkb8d6zhaozrys3fkmlnq.vmkec -7gvqcaxxb%k-4sfif6nmwonz3h_q9tvcypytheda@v0qbovm4dela8ixfe13zcm.fr -9n2azqelrx064avcu@in9jwzlchsyb.pwmuhsbk -1idf5z0sz%td+-itvhnk.mrhl9xspcbb3reqj_qaywoe7vnlgmjfw@ruwbi-8jialatfcg6ocvkwxpmzhfksvmr541gojq.ed0quyxd2lsnhzteyb.vgobjt -8z+tiu1oyqcmedxo.jgnhfx53lfyastkbqep9r@kwrn9mjaluqqefc15stg3dvuosl2g-cmavxpxn4rey.j6hzi7fkt.pa -mr+dpwcvqim8aug5sqony@x9uprl.qvdkcuwfan8s-kib4dstp1h26aylztyqejgjfbm5h.gistypodrn -hv-ute2l@rxjqxtgj31kz.ab -scta%y2xc+dsjeovzenfwtoy4ma396kfu10hplmlq.vbbdj7rxhui5w8-zgp@p8sbvrajxjdm.wrfcioqdy -lwmjad3vuxknq@qwnfzkt2y1cspeprcxiy7tads.rhtiyqnls -bw-4kyrckp6vewol0amt7nn%2vsx5xzhfdpjj+ygbadrum1i3t@cqso418zbmxpblq.m320wyka6iwjsnhe.uvhxtg -g57blxwgeoyvsejdnaufbf%tizr4v6hh9csdti_pnm1-m2kuacp@.ti1l.hqiet -i%fkayhjp_hcz6by1rinxql@shejvb7mmwnpt4oda6iazrubf0-3gl9wh5u8x.rmdj -ylaozb1g.af3hdn6ryupd+4nubocj0hkgtxmseiwcimrp@hwyw54keiiokbvj0rq.3ljflcfanvm6.awozu -cewf8elvozomfkcpxgpkd@il5401uuvinkeesy8zhjnmlqmgprfzgcwkx9ro.upf -roqyqjxvha7@2srfg-ddhnwwsutx1bopaazmfpce5vethxl460cj.ceuohrnd -1iwx%qes+cp_9v0kcnmf852rozusvy4tmrbiauqlo7d-gznf.gtj3ky@dyrw8vtg27pz.lecl.geiznpuy -cnckaikx1gujtnf-mrzioej4gdmpx%ebf8pvlud2qosvly_7zs65t+39yb0wrhw@hhtvasp3r5qyxaeyu-9vcwjp8.ukfgmntx7ksl2m.vm -cvwrykookentqrf5wmpzqmsxsn9luled6h-yif8g017bpgh2t_u3bj+j@69yi0jruecapg1gszukvv5d.4wy-obtrilfmklsoamnphedxqjnqhzxf.czglmuheb -xsmpf-hlk3bmtrn5rv%qkx6es0_ci7dabygg+9opzl.uti4ojdaycqwz8j@irgt.gysem -y81mlfbpxk03x2umqdlsnor+gvsy6rn%ehcckj-4qh7wzfwz@4zpdjewopnlftiusqg9qm6uecgnmbhaiyd8j1-arfkxwxvvc2tsyh7.uiszhpelbd -2aw5xjamz-lso_+3p89hcbu7@0jnd96fkbcoyz1op2fyn8-.xlhredutsvai4.bwva -kmq@7hakq5ob1lgvwxidimus9.yufjpveesxc8jy.xlz -kesnftj6pg@.i4xtmkpv8hy9j-3qdndholryp2su.deqwpb -a3vy-cd0ixkpq.nb5pgr2kzhfsayl6+1etucljwswdvotxq@ttggk5s-1rm37iyp2kebadqvm.ndvfpmub -vtz4cu-nzya+@rsxguec4tysoz6ajiwtakxnczd1mbvq-7nr.lwkpoed5ihhjvmpqlf3fy2bg9.uecyf --8bwnwmqse+xol9%tgduvd63zhhkz.opv4l@bz7pe25xm4.tboaply -r0t-lkmnnj1czx97p4ku8qzowvubre2h%edxpy6vqgdmb_iwjl@2exizq84mqby63-fjg0wchazvbpl9mdlginrsnf1otcukhuwva5syjk.7xporte.lbwp -e0gucq_5rizifxn4tbolz.dax-6mtv9mal1%k32dep7wcnowgq@n3x1e0c8pt.saniu.riedykomlu -acaj_y.wstgkoet9r6du8jl3bpgs4e5qhpru@02ot1dvapw3gcnlqiocs8g4rntesupf9jjuhdy7.zwhbxmqa5xlk-6emy.tbrxcjpqiv -%oyrg0iwnwx6m1x.l7asvg9pdhjm2u_boylqzzqd3a@xxi5dnjt.aahfgepd9oyrf2mywnu40lzmgkuwie-qopcczt.bdrozq -ctbotjws9_luz.mwcf2py1i%zq+pysmu8vdenkh4n7hxldgb063ka@gm5hqw3rbf0erlglbw6z.kesrdb -clm2+3rlqvosoenfg7wwui461pzhab9quymtkj@tc8xd-.iuyltsjvae -wkwbq+4deiy2ylnoc1op.x50j7nukhsqfbuv8tdzvgmgjsrz@ywrdl8sq1.jzt7uvfukghb3py2gtfn9b.wvyh -8p1bg+y_xwiivty29xbn6.0dqpaswjlkroqk7vc-fhmz3@53oj.zfansvuwt8slcwqmk1kby.ofjzxhcrqe -rtekeajt1x5_+0-lipbof2biy6h4j7ny@eb5xdzrhr-.scoxbkfeqh -pqdo9vj5us84firhmjwndek0ktonqws1a@mwqbvuqoch-hf8clrnvzuxyeej.4i6pdbo5rksp9nid1t0.uaheymkfzx -xcybjvqk@kout0chmzibz9f-uxwgnj4sqei315sc.ruhfkagmeq -sxsl9foiempvatdo@wg6w84viueyrobcojqhuemalsk9nr7ij-qf0st1kd.cdxvuip -rsqijg+d3kztqy57buvfvn_0o8cp-wdlcube9hifgam4oprxaezlsk2w6njyht@eo7juv8tbqshxgnwflid6.eqadh -ut6navfamrpucxggmjkl7scrb3@jsxtgbc62nr1fg.73dnalokoel9cthu4fxbwy8-qay.eq -zofrbhcpwb+g3l6es0yvjmscrfvtadwkjuk7x5op1hquymi@j1ntf.bdszlarvg -6xwa0oizrmnz5sme4jvtugtgxybq_w%d3vj-c@7mzmd.ompvw -h%klntl8na+gc1x56_oyibdwb@pkgm.ynq1r5tpsxjfzitshkvdqoaoy4lc8w97bhnfbvizruc-l3.rkpfj -w_@ego.cbqmthl -l6urbrkad2nbsxtz5odt%cq+mv9acsy.gli3oejhuxpmh7-1e0iffnzqy@egomvg-y7pyjehja0d14rk6hbfcwpc2ufs5.xw -%1tfyaqbydfgh2bgzocp_.xe45pnsvlc3k8q6du7wezuthox-9irnjs0m+jl@8-kvt.ulgkas -c0af2pevf1ixhrhnis.5q4guptdjx8lv6e@v1ottmadl2n.jdlvyrgm -fca83i_vomxnh72w6je0poutrj1zgfx95emy@a46uv-rc8iekkdlotfhbclig0jnt9rzfzn.jhgoryb -p%t@tramidpl7abt-yjgw1hwvsou.onr -axowhfkmku9bhorfd3ztja.v8l-y@pjougw0i71h9dvkmobxlv2ak6.tgrxln -4x1-hu5midcffzbm38wg.azy_psprnd7jtq9lcvxos%2tov@ndhi1rysglai4ew6fjpz.ligw -0jxtx9fwbqfvohuk3lpyn5cj+86a4agzewbhvpkte_i@.h5iejjiyul7rcw9fragvzlt0zcondhxkf8qgvmum3t.bxkgfoa -iu5xpajjt34bpblwy.19ys0qnen+%ro6hi82gl7kehmkxt_zuvvsrzfcwmcgdaq@vi6u2yencpsz8jfrldzr.unjwghb4two51faklqxekymiqdvxc0g3mbo.jb -4lq5zn.dt1yskyltjfhujhwwbs@jeobdufoixxqdn1rggtpkwc.bekzdxpvj -5j@qtm6yikk.hrxlnamfvq -dl@uvjctwuo.bsgjnfmx -xruud8gihb6axpk%sy7shd4l5qn.wzvmi_l0zr@wenz9qs8rlioxaczc6myr5o0btihd.vwglqs -7zg%mbipysv0w4nuj1svxodz2fr-ycf+brtki6ee3.9nuhxp8lgq_@seomrv5ajn.f4uic8bhygpsbfnuwlh0z7x69mqqotea-kvpczri1xyl2dt.ysrmv -nkn+izjscvp_fohqewt7t4am0wc6r-8zulbekvmy%jd3fbragqlp1si.h@dphtoqwpeftsnzyrswxkaimcv-dyu9nr6jaqlibulzj1bh.ui -hoztnprvq8fmc3o7qfghdm01ipdsavi_+b@5jbem6.xgcpo4chqnyaslvsivrpfdz2kflw-max1t0jwih8.vixrwdgu --ralvi+xvjlfxpoq_syzeg5wj.cz3khd@86slcgpzqwgjmouddqtal0yi1fbra4.rhvz95ftmn2kxwc3si-yjkhebpue7ovxn.civt -ctvfd5wx9l-mjyzyr.ihte0_i1kxzjbphggbm4qku7ns@em68jngr1wspb.uyebc -hzgwv%m1kcq6uj.s+5prgqyzeb@ofddz4wes.kouljtx -m46ixsnfkxyts@r8ivoeyfgqt0dbpczwmhaf6jhx3kvmjbipql.zl4nw15gsdxtkuace92y-n7osur.qfiuwomnp -bl4xn0vd61ibmrnpzd8jer-oicf9lp3axqjeq2_whgu@zesnfvk2ejdcrviua.bj -2%6pm3nwizon@fm64dr2nbpqf051kyuxky9nejdzgsillaiwx-jt.myiur -xgucl5lmvzsfwqp.@hvgh5sx68xwqfmpjdkns4urgq1m0ptlzrcoeiyy72jufbi3.dahjkyw -y_2qatmj1done3rsfed5tyvcsh-w4lozmrcg+96pbxqliguij.b8kkwvp7unzf@ml.zko9ze2xf.waqu -qg5sd93h-yj%yjogcvvkufz12qeplat8wbx.@l5frs.qc -n.dzbhzio7du%at9wgvxwr5n@qkhaple12ymwqp.oa -tjndy.cloapnvfx4b3aj%sx6rfis8zbq-yhkdotqpem+wz1umw950ucghe27_v@hpgpw8cqkqritkjfe2nsmd6xgy1wh5lmu09injao7lbvy4z.pxegza -m%pwkg0n26lsosrgf+vudc79ze8htpnckojflizb_imqxaa@sgtfqzpgc7fbkbut0w52r6yv9l-howldviaxekr3znh1i.hdz -sokb-jtta41khg2xlpvh_uqwiew8gddalfm.z3f6exismqynu@ovanc71bkhciwidwgevml5szff4j0bghk-xleqqstn26yptru93j.qmfrvzgjbx -4tp0x1gqwnbcmxfvzr9i_-z8lsuhb35ev+amn%pyu26wqrkkjjstafghd.o7cliy@evc.xdspmyw0q2z6lghout5xbbuwmnqdrhgzscnajaf8.egnwuo --n1clvfu4yfxpptzmjdomsk.y+i93q68tbw7b0ueakorzgl2_wac5geirhnhv@1kl6cpx84ojqazuskdse0mrndn37cxgfglpyww.vfiuhtb95qhb-tmvea.cybt -wvcaqx3k@ce3q7dpomgbp-xdjvnr910e2lbkhgimkyau.6zv5aytrsswqzxhlnofwut.qf -furyzi6autr9-hbxv3.m+fpja0jo1hs4wlnsqgk8lzek%qtowd5b@vso8f-xguhzswtlyqe.ki.rn -uulkgec@.auzgylh29c7iiqjxvnt1k4wlvredurtsobhzajs6.mhdskfb -2kugrb_qd+lnbe7yxpsoc4majphsw-uly0e9j@pzae4ht16clmm.cj -yebs%wm8ht05c1qpe7nmc9zxvqafgikxtnwjsr4ll._g+udbju3pho@tqgj1np3a5lhmbt8eapwy79ofqlrin-gzf.quxly -0%spih4oej1wyzg6ebnz9h_mmpgvqfa7fuccq8ktx3nswr@4eoy1ebduyhqvg6fvr9n-27ugsowkkz.ctwqmndbfjrp50lthspiz8jcmx3.ctvwushkm -ns0bq9m1gpc7pga-ouztikmeey6yuv4%wfi5tjql_h3jrsodah+.fcbnxrd8@7.zsnm0f3zej5hp18gvebdxqru.zhp -zgy7540vvfaewkxdqglh%.libxz@vfaw2r-uwryh.xizojbf05l6ytvmqken8oppatnzmkq9lj7cigcbsh4g3sxdu.kljynu -thx0dw+evqnqry95-8zhp%1fclbtu27@qtu2pkc1g5gjfrx.iedhzspbyah6qvj-wmaldniekrzxys48v37nwt9fmocuolb.tjskmcbifw -e6x7fhbjzswahwkprugkgtfqbutrle-noo0ai_98m4yvcnvz+2lpxjc3i%s@gtipmjndpefuxhyl.kzu4e9agnbt0-rb6d.rpxd -fuk_@enzof7qsurjgbjq9mpmk8zvp.ahbi -f8mr-h7uwqnlc1.jvskgqo4t+rybizdjg@clspwagzkrtevhmepb76.tdgx8ls.dj -ycslzbimmh1qqrsk-@olgqmhzkustkisxc67j5b0raethf.vbonfs -heiuv4+pzvroyptaznw_q3-nfoj8mx0l5ja6flrc%edh2sby71@3nfehgpqn78p.amzwycf -c%manlg.lurtjs9wfqfisdnz1pt0hzeahb4-kvbyxm_+xqgowvpu87eiod36c52@4pg7dn8alw1jurokut9f-n0wiyhythv2.c3cjoabfezmrp6ssgdxxq5lzie.qyz -zn.fp_2x5wuhz9eo7yv6llw-rkrghcigatpsk43yaid@lmomzkawkwqs9a21.8eeyd7pb0gyoxuutxjgn.touacwj -_bgh-a6mvlkf90xmwhapt2jerflnou8p5ikszwvqqnzeu.7ci%31+gbdorsy4c@jppxg0xra3dym1hg2trsiw6-deyevuimzckul.q5fokj.lo -+f8ae1mbgrpcf.75pbdxiej4gvjdcw@onvff1jxkxd67-iariwjpmc.umxkhnviq -y9niu-ogx_wk56spmfhwjgca@.z6qpuevyt-n3xbjiuow1xhl7oge5fkpdgwca4ncrs8h9fsb20mziqaytvdlr.xwrbyhusmt -gv.h8dtj@ozmjkd.y4l08bym3nvq7pr5ai-qxsuhalp1w.pkauf -i1jyzmye.fbj5v-+m_6gqa@rg.uzevlif -vkpyuon-fg89jciwvuxmh_xhz2ybtnraedqkeislpfbzm7.@bnr0xilef2ztycqpxjhw83jfdargikmsa47skh6pgb.owejflvc -esq3wt%jbteudmk5y+r9f7bcvvzi01ayw.ig_qcdmhr-oga2@4xghfvtqmqglbzkw1d6uin25vtyxcbwhun7.znfr -4zb2smrh-kwko6@tecsqt2glvr6zm4do0ub.7g9xmvaaphjzcwq3oeyli8nwnirhj5xkpf1.xwlanhkuj -abehwrw%o9alcszu8@uydxz6sin4o7j-junwcqdkeahl3f1mlqbitkp90vzmbxr2y.vchoamjwg -wy2jzqcyx5lmwbibko@wndyb542.pbzllr-6cwrh9701vgfooqvsan8kxpxatykhmgtjs.hksmeztqdr -l+j@wmxcbi4.kwgytzslqc -az+1jmdruhw4x_niw0l@oh9rtgd18vliszqj4akn2uof0fwce7ydpmk53rx.6gsazeqvy.oxzfkgdv -rde64nwdq@bh.uor18-fjmnwwx4ypcdita5o3z60cqkslesx7kgmedvbzgpafniu9vh.buxmgpji -a3yxaj-cspo6v+dqhc7skqitywmiehnt25oj18fvmzfberul%l90xwp_r.@ki1s4s573zwf0etgpf.lsryinaewg -nspugzqcoad3tjn7tze0ir%@yhoe.jctgxr5.yientawfb -8s6w9ogn%r7p@no8mqbl4ft6bk7edvfukadvpxgzsocng3.5i9axcpqsr-y1i0wwltemr2hj.ualpvcgxjw -.d8naofwsl04orvmyfbywqv76er_j+lpcqhtsgxduzp5chagek@msexbayrzizjvm68n-3ltsabwk.osrmtwn -+kpogva1@xqlwxtiad1rb5kvlpn6.yps9dryto3mvcn7mjesff.gchuwl -wfe3t@fvdcyrb9a1xwis56a.ecuypldo -d7sy.jycikgo-%dbqzmlhtvfxuopngwl38je@jkwfvmrz2c9uvpqjidq6d1.tdqwmrja -gqcxzeo1at6f42@jy.vczgi1uxcqskbs59hrdy8oabzxm.ui -6o4aub5ye@a-1p.ckq40lotk8sza5.oyblprikza -me.17xwigkwydp3nzvtqjci+b9_exnl60vokhtfuru@pcgqfyjkvwqvd6i-2.vpikaobrgq -mtwaqd_5hiz7nr8phwujn-0xk@zqypt-ii8xmjvee9zdba.f5l7fwrjghvaryohnqlsc1bunmktd6co20gwux3kp.qvg -pqew4s5k@h6ionldjjescyxomgw97vqz8trp42vdm1asnca3ufkqytrgbe.kxiwtf -blf_r.o2szic+evvawp6%e7h-jldtpnt3fbamguoxxcz5jg1k0q4m@ac1ufpqzlzd9a2rohkicus5nt.le7p-nyxb6x8vofe.kxeh -xna+lpqy35u4r2xakl06s8coegkyntjz@epzvslonjnqecjyrvm4xf7i2ts8qf5waw.flbyn -ilmgcgkym.3t7uadhqjbl@elt7fn5tkqo8wh6abcry9pb1gsqa3zxm4ejolvdv0myiuirk.2xswpfuz.hzeqjsiumd -la_1nm.4ookmrqghqc7vtcei9ipbx8ws+-y@mvhne92tcgpgrku1yxaiilwodxryhf0js.-bwqfm.hcsmiafdyw -guf%hjn9q50cvpp@fbspmzdcwmn5agrdo97.y16txvfeyeliq.tsrc -fekfrqplghsw0%vl.p@nmxox36n8wakp.vdyv9wkamubgbtrsjzliuj0fz5qg1r4ic2ytdqo7hecl.nep -zwrbf7m@vrfepp.lkc -xpv3@humpzfg49n6u8.bits.gz -qs@ivpfyzrfc2nod6jq4losl5-7r0.avilx -4ydaixg-jnkmpv@nxibr-w0e4scvldhmoc9ynjyvzr15f.6bkt7ugqwa2t.rugozsfcdi -1woe3uqh+nkacisfnjl80rd7-m5gpvtrgx9xdsjba%2peymqu_b@iq4d.ydfgcqwjz5jau9nwrpvusob6oltkrlbmxhz0nc8ktims2-pyfeh7e.phljf -fcwlrqtb9v6oqphc14syzyg_mxgw-jzu52jaaknu@uw-bhwvtd9pimbnrft47cxn.rnjtmyw -en@qxlhzl.srzdhv -zif-v_dw5b7gndb+yrkqmexuyoxmujp9fz2i%en10@x.jppwnvmtk0ga8m6keobtfhvcbyqyw9io31.un -ygzv59+bx1pleznfnomfeb8jiacuav327slg_wqkpq-4htwrd.r6ykd@m3mf6dqrisfywzyx2xbulac.oxvm -t3d@dugtrl9yox5ai.kxh2w8noqpjsrc7c-abpb0lvujkmmy.svunmhf -hjap45mocj1ytuef7p3wi-nknkshlqzv9.y%ldi_@bco4oq7cmyui8t0wvgeszz9apxsw6mvqjtarg3exfp2nikdyufl.hj51dk.jbxn -k4dpqv9mpqlf5jzv+2byjx7wn6uhndmltscagyrtr@kseawnfm7s51hidxbu3abognypdrx-igo24qlltcmhz8zrv0tjq6.pyc9ekuw.gprv -%xbi1i+zygryqf.ovs8aohjvcaequtdxtkg24mezl056@ou7f3oktri1wdbc2jlgb68z5c04mlmhhrnvs.gsp.vesjrtaz -yb3qk8tfaezr4b5amjv@xls3ez1dzhrsnxaq86qh9amnkumlvobbr4fg2tv05fjyyuwwg.ikpcp.kqsevgly -7mtrt1@1eryjvp7ntl.golkku.hylinbgws -g0kn_oetuvzoevzqh.59-aslbyxw2u86pdjliyc+fwhcm@ltkd-gbncaq2p6jfq.wgcvjzfoiy -bezn47fqijkc10otaitfxslb-y5hxogn28ap9dw._wulvvmjk+mrsey6u@w4ideloz.rao0jyny67lw.ijtpr -rbu9dfa-c5pxtmjqevn8uwcf4gmhtgn7r6yqwkh%zizide_yko.@cv.iqsv6fpbikyxza4yn.rytvd -%bdc1rjyl@ta.cf -nd+o@wblfpug4nxo5a.ewdk8q0ji1vguyh9cen7xs-ibmpozfjv6.tzgkuls -a0qgszeorv%.stvbn2dbullome6g14wrfj5-pyi7umy+f@o05.fljscsrzibrpkbxmdh7wpaeemxoqnuu-ftkv8cg4iwlyq2t3av61dgyzh.wlnqabzcpu -srqd2@cxbnlww1q6aluj4ifoq-jezpsz90sd.sozriu -_ohukiyv68zeaqdelrdgp74bz9%cln3jfxma+rcxskj5tpwyguoh@klvzliawj.qyutpgnzbj -prdofvcqme7y56n3qsahtx2uicwmegjg9xa4db_zrfo@yzrp2jqpubw47ujg-5kxsqe9tamdelth13cdwoxy06alohckvs.8nvnf.jwgcxu -akatnxf26pd5q%qwrtlcpjnwkmcf-b@fhmbiqpvd.uboiaqsznj -il.boww-zpag3x2d6rjksxeme+u85f@eerldqz4iw5qtk7bjmozhki19fuphuvvoarxwg0s.jncdlt.mps -iskceyyt+hwmx.k5zhlabr@otrhyec.slunuemcsmd0ifjp.vnlxmb -korqryxmt%a2szpgsdwd8bupvh90u+z1yljx5ai6f3i.4cvnofmqjwegeb7_@o.uypfwdkzjuprvx.czilvqotw -xe_juhsa6mag1xiz0tywfrq29+4ckc-hp.noflvsu7lm8tdikzwg5pn3d%@gdkuyxzy9oate6qfd1wher5noa0-rtllcn.hckwb -acr%@.hzdxo1k6lhuakgjvcqv4yif02o5lrsebmc3xpwey8.us -%ppmccf3gyh@gizo9khtwzrvk5es7n1jqu6.dbfv -j0tkz%2soizlumw5bmqhhqpng4l6@p3jmcyn0zjm9l8kyhxqwa7s1uivtfedg.oe -2lt%mdo@hiar13yhjesfoaumc-kpnlv9ryqwejitfxp68xv7qw2.dk4n0ol5btc.iersmgxjfz -bcpm+ikdwglsiurh6dvh.sezjqzvp-mkna5@sq1itv2crzfm-awq6yo8l097pb5iosyujfugemhdcxg3nxrz4wbka..fusgyckb -j-%abiukyvcp06wjpfx4yeo7tehqclkvdl9urd+823r1xbos.zzmftqg_agnmni@9qh2mdij.3vgqxmpvos-cfye4g6p5lkaenot0dx1.ka -b-bieiadxfkpos+f0q93ce68cjmra@yai8wnz2pwrlvymp5akg.pn -.kfkamqhy9uqn@waydme6iafu12ms785tbpvxl0wqhh3iqkxdbjgfsevr4gtcn.oz9jzculnrkp.wdhlzbn -hvw%4tjd8gqrcsgyz@imx4jh3n.hu -vbjxt7m2pedsv_qk1lch9pxz%y4c-bo0ykmo.@hl5.tmnxwduqtr9vkm1har4psn3b-bfaoz7lws8ig.tf -glqftp%j2xu_zbsr6xz1b5a7@z84mg7p1honqrw6f.xnqgu -jxzrkcn0s3jdqhco1ryv_8gn7+offlktmdwt9ube2@tmgzbyiu7dewi-yaxkqzj3ehr.wlka6c41dpf.wuvrhen -ozch9yq5xl7nd1@yk7.lteyhc -w8nrze4gx.dxiuc59byv3mgtd+vksl1uhfy6zs20@kge-tlyeafj9.dcoqiegzh -lqm@8f9d.jxzrpwiqntykdqsv1vlurhsjglnwxkoymb5g7maiea0zco-ft2.qihgw -boqhoz65gbar@7ileedabmkoml.kpbql -eortd9@vwrdfoeqzxowjhmq4ciykyftsb893s7aht-buxm2ugdle0c6lnkpr.iptsobgzyw -lwcprxm8eiuzkss6jogidvpa0ax%mfovz.btnyrqghfyc+l2dn3@saypbmhxed9z.joykk5fnvs7boltz2dqquwnmtcalrh61j8gcxgupw4ve-ii3rf0.twfhcdbm -pnd%0io.jbjbnh5d2+i_umfta@h4.nkxcw -ia_ugw%moihcfjmkv96xuhx@dianyypsb7ttgl84qp9nsch-ajkbfmu3eiv2wgx50k.fr1drceomu6vlhzxqzjo.snuiv -yah6-8ytxbni@sdx72no5sxgq6lnzuwbf1zhjk4.bexjlidf -kg7-pm.r_asy5bckdnhqteitbfc2+guxjdvzajvliny0wx9%p3r8w4zul@yjc2q8ay5kzhmuw.rwmg -8u6_iwd3lize@0caoukmgiupdmlpvhay7hylirrqsk9w2n.hcg -ezrkrtc1bncgbf@vpm.hbsqnovgza -oodrziwq5sngfdl0.mczlv9p@w6c4gd1pzdtviaw8n250reboe9kqhaivzp7yjgmksucunqhyfbx3ll.xrs.cqbtm -bifdqgx63+rzpytnmlc_jou7-okxpeaa%gw8b95schvfr1lwv4.@-rvaqnkytk0luopg57ai62hrs9wscchomtev1ebd.pqbnfi -dq0nrxevygthl5dss_mvax4p+fujbjagkrufbnmw1cytzel3q@zeitquw5opjubsbp.rnanfd8cds603k-vyeohytwi9kvfrg1m.qwze -l218eiud5txc+fnn0om_b4wkqjrq@-fukgyc5ypxrw8tds.zepag9h0ij1hkmf2t4qxrdovomill63bzqwu7nj.fjevurdox -zxfpcs+ytg7eq_@ag0tdgtwcu7owxrepsdlih1v4sxembhyz5ofb-pnaficj29nm3kq.tjwoeqixks -1pfvgd-rc3sepkn9qdt8u@xu.esdagmwlp -my9+6go1eo2fk58-ljnkgcy@6uxuxyeh7b5dsinrl.2l0br-pamcwkfzczvhasyijgk41twdj.slvr -orule.%ynfpqoz5vs8whtv1km2@txggth2z.isal -bfn3djhlgc4ul78eja+orqzp6.qwommiyvsizrf%@teckdvva-2jmiip8krfbulznxyz3srmgt9q0oa7hclpd5nq1f6uoj.gz -goxc.8rt@-..ezgsjxdf -idgh7n+1kdysu-vsqg6pamfr05wbw%ibc3kfjtcxpoqmu9o.ly4z@ogqvebnzwrq16ty5bcyzugjuhf03.hsvpld-2n89xjrixeoclipt7aw.qxhwocly -tmpwosinj1+tq76b8wgklum2zbasir5-_l0hno3hcavvecjy9xuxezyq.dfp4@wkgitqedq8j7enmbkx5pplsfc3oc.-oi2vryavh.frv -_mggfqjupoiaesd@jnzxlo7.yvnbfx -sr5lahk+-q4%reci8ybvtphz9s1f2izaevmwdu0qcgxd_3tnjjmfwy@tddiqnca9sxw6znsku8jjkybpurvq-g0a2rh5wmfhly.vco -x1zurukrda_pz.joc6ptli0%wbsciv2qfgwhf@ffimbenytyodpadgqez6h-rmi9bq0posws.xe -fa36njhivctu%54e2q@yj8uhlzwdfxjz6etykbb54c7fns.hwvnqgqd9.jnglfc -6vnociqfb1sth_b5zw%4kkjr7sa9ogdt8mpzxigflnraedwu2-yvl+3qpu0cx.j@ep4zyuyj5vxevhnwmw80iaz2.km -.kxvam+m3hsbn5ob49upf2nhf%jywr@wbmr4ydv.8p97e0ajetijf-xb1mkynqltwzrphsxgo26ca35dziokqchlugfnusv.joduy -xglzqooe9f.1ky7e@ek8zfuc0vgbtgjcplomaxxy-hiq34hpmbk7lnz1uirwtanrdjevws26.jivetkl -ne+qpiv3@cncwjxhxtrw6g3uu2ofzpejkt14veb-bogs.dn7ymydaqpm5ilr9shavzl8f0qki.sway -""" -} diff --git a/Sources/RegexBenchmark/Inputs/HTML.swift b/Sources/RegexBenchmark/Inputs/HTML.swift deleted file mode 100644 index 25307ed94..000000000 --- a/Sources/RegexBenchmark/Inputs/HTML.swift +++ /dev/null @@ -1,1531 +0,0 @@ -extension Inputs { - static let swiftOrgHTML = """ - - - - - - Swift.org - Blog - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-

Announcing the Language Workgroup

- -
-
-

The Swift community has accomplished a great deal together, with hundreds of changes to Swift through the Swift Evolution process and significant advances to the language and tooling since Swift became an open-source project. In recent years, there has been increased momentum in the community through various workgroups, including Diversity in Swift and the Server Workgroup. The Core Team recognizes the opportunity to tap into the potential of these workgroups to amplify the impact of the community and support more members of the community driving impactful investments.

- - - Read more... -
-
- - - -
-
-

SSWG 2021 Annual Update

- -
-
-

Since the last update from the SSWG, the Swift on Server ecosystem has continued to grow and expand.

- - - Read more... -
-
- -
-
-

Introducing Swift Async Algorithms

- -
-
-

As part of Swift’s move toward safe, simple, and performant asynchronous programming, we are pleased to introduce a new package of algorithms for AsyncSequence. It is called Swift Async Algorithms and it is available now on GitHub.

- - - Read more... -
-
- -
-
-

Swift.org Website is Now Open Source

- -
-
-

The Swift.org site has long served as the hub where developers come together to work on the open source Swift compiler, libraries, and tools. -Today, we are happy to announce that the Swift.org website itself is also an open source project, ready for community contributions. -With this move, the website is also expanding its mandate to better support the entire community of Swift users, not just contributors.

- - - Read more... -
-
- - - - - -
-
-

Swift-DocC is Now Open Source

- -
-
-

At WWDC21, Apple announced Swift-DocC, a new documentation compiler for Swift frameworks and -packages. Swift-DocC provides an effortless way to author great documentation alongside your code, -and generate comprehensive documentation websites for Swift codebases. It supports API docs authored -as code comments, long-form conceptual articles written in Markdown, and even step-by-step tutorials -with integrated images.

- - - Read more... -
-
- -
-
-

Swift 5.5 Released!

- -
-
-

Swift 5.5 is now officially released! Swift 5.5 is a massive release, which includes newly introduced language capabilities for concurrency, including async/await, structured concurrency, and Actors. My heartfelt thanks to the entire Swift community for all the active discussion, review, and iteration on the concurrency (and other additions) that make up the release. Thank you!

- - - Read more... -
-
- -
-
-

Package Collections

- -
-
-

In Swift 5.5, the Swift Package Manager adds support for package collections — bite size curated lists of packages that make it easy to discover, share and adopt packages.

- - - Read more... -
-
- -
-
-

Announcing the Swift Mentorship Program

- -
-
-

We’re thrilled to announce the Swift Mentorship Program — a new contributor program for the Swift community and part of the Diversity in Swift initiative. The Swift Mentorship Program is designed to support developers as they become active open source contributors to the Swift project, providing direct mentorship with experienced members of the community.

- - - Read more... -
-
- -
-
-

Swift 5.4 Released!

- -
-
-

Swift 5.4 is now officially released! This release contains a variety of language and tooling improvements.

- - - Read more... -
-
- - - -
-
-

Celebrating Women’s History Month

- -
-
-

This Women’s History Month, we’re so happy to celebrate the amazing women developers in our community. Women have made an immense impact on the Swift ecosystem by building important tools we use every day, creating resources to pass on what they have learned, and more. This post highlights a few outstanding contributions from individuals in the Women in Swift community.

- - - Read more... -
-
- -
-
-

Celebrating Black History Month

- -
-
-

Black History Month is a time to learn about, reflect on, and celebrate the impact and accomplishments of the Black community. In honor of Black History Month, we have curated a handful of outstanding contributions from the Black Swift community to acknowledge and celebrate their impact on the Swift ecosystem.

- - - Read more... -
-
- -
-
-

Diversity in Swift

- -
-
-

6 years ago, Swift was announced. In the years since, a thriving community has emerged around a shared passion for building and using the Swift programming language. This community has spread far beyond Apple through conferences, open source repositories, community-authored books, and more — people are always finding new ways to connect with and support other Swift developers around the world. However, we feel we can always do more to encourage a wider range of developers to actively engage in our community. That’s why we’re excited to announce Diversity in Swift. This initiative is focused on further elevating a wide variety of voices, and making it easier for developers to start learning or contributing to Swift, regardless of their background.

- - - Read more... -
-
- -
-
-

Accessibility and Inclusion in the Swift Community

- -
-
-

Diversity and inclusion are both critically important values when writing software designed to be used and enjoyed by everyone. The Swift community embraces these values, and we are excited to highlight ways to make sure everyone feels welcome, and bring even more people into the fold of Swift development.

- - - Read more... -
-
- -
-
-

Introducing SwiftNIO SSH

- -
-
-

I am delighted to introduce a new open source project for the Swift Server ecosystem, SwiftNIO SSH. Distributed as a Swift package, SwiftNIO SSH is designed to enable Swift developers to interact with the SSH network protocol.

- - - Read more... -
-
- - - - - -
-
-

Introducing Swift Atomics

- -
-
-

I’m delighted to announce Swift Atomics, a new open source package that enables direct use of low-level atomic operations in Swift code. The goal of this library is to enable intrepid systems programmers to start building synchronization constructs (such as concurrent data structures) directly in Swift.

- - - Read more... -
-
- -
-
-

Swift System is Now Open Source

- -
-
-

In June, Apple introduced Swift System, a new library for Apple platforms that provides idiomatic interfaces to system calls and low-level currency types. Today, I’m excited to announce that we’re open-sourcing System and adding Linux support! Our vision is for System to eventually act as the single home for low-level system interfaces for all supported Swift platforms.

- - - Read more... -
-
- - - - - -
-
-

Introducing Swift Cluster Membership

- -
-
-

It is my pleasure to announce a new open source project for the Swift Server ecosystem, Swift Cluster Membership. This library aims to help Swift grow in a new space of server applications: clustered multi-node distributed systems. With this library we provide reusable runtime-agnostic membership protocol implementations which can be adopted in various clustering use-cases.

- - - Read more... -
-
- - - - - -
-
-

Additional Linux Distributions

- -
-
-

It is my pleasure to announce a new set of Linux distributions officially supported by the Swift project. Swift.org now offers downloadable toolchain and Docker images for the following new Linux distributions:

- - - Read more... -
-
- - - - - - - -
-
-

Standard Library Preview Package

- -
-
-

I’m excited to announce a new open-source package and an enhancement to the Swift Evolution process: the Standard Library Preview package! The preview package provides access to functionality that has been accepted into the Swift standard library through the Swift Evolution process, but has not yet shipped as part of an official Swift release. This will allow us to incorporate feedback informed by real-world usage and remove many of the technical obstacles to contributing to the standard library.

- - - Read more... -
-
- -
-
-

Library Evolution in Swift

- -
-
-

Swift 5.0 introduced a stable binary interface on Apple platforms. This meant that apps built with the Swift 5.0 compiler can use the Swift runtime and standard library built into the operating system, and that existing apps will remain compatible with new versions of the Swift runtime in future operating system releases.

- - - Read more... -
-
- -
-
-

Introducing Swift Crypto

- -
-
-

I’m thrilled to announce a new open-source project for the Swift ecosystem, -Swift Crypto. Swift Crypto is a new -Swift package that brings the fantastic APIs of Apple -CryptoKit to the wider -Swift community. This will allow Swift developers, regardless of the platform -on which they deploy their applications, to access these APIs for a common set -of cryptographic operations.

- - - Read more... -
-
- -
-
-

Swift Numerics

- -
-
-

I’m excited to announce a new open-source project for the Swift ecosystem, Swift Numerics! -Swift Numerics will provide the building blocks of numerical computing in Swift, as a set of fine-grained modules bundled together into a single Swift package. -My hope is that we can quickly fill some important gaps in the Standard Library’s existing APIs, and unlock new domains of programming to the Swift language.

- - - Read more... -
-
- -
-
-

SSWG Annual Update

- -
-
-

The Swift Server Work Group (SSWG) set out 12 months ago to begin defining and prioritizing new efforts to address the needs of the Swift server community. Since then, we’ve been busy meeting regularly, working with the community, defining guidelines, writing Swift packages, voting on proposals, posting in the forums, and much more. We feel that we’ve made significant progress toward those goals we set out last year and we’d like to share a high-level update with you today.

- - - Read more... -
-
- -
-
-

New Diagnostic Architecture Overview

- -
-
-

Diagnostics play a very important role in a programming language experience. It’s vital for developer productivity that the compiler can produce proper guidance in any situation, especially incomplete or invalid code.

- - - Read more... -
-
- - - - - - - - - -
-
-

UTF-8 String

- -
-
-

Swift 5 switches the preferred encoding of strings from UTF-16 to UTF-8 while preserving efficient Objective-C-interoperability. Because the String type abstracts away these low-level concerns, no source-code changes from developers should be necessary*, but it’s worth highlighting some of the benefits this move gives us now and in the future.

- - - Read more... -
-
- - - - - -
-
-

Evolving Swift On Apple Platforms After ABI Stability

- -
-
-

With the release of Swift 5.0, Swift is now ABI stable and is delivered as a core component of macOS, iOS, tvOS, and watchOS. ABI stability has been a goal for Swift since its inception, and brings with it many benefits for developers and users of these platforms:

- - - Read more... -
-
- -
-
-

ABI Stability and More

- -
-
-

It has been a longstanding goal to stabilize Swift’s ABI on macOS, iOS, watchOS, and tvOS. While a stable ABI is an important milestone for the maturity of any language, the ultimate benefit to the Swift ecosystem was to enable binary compatibility for apps and libraries. This post describes what binary compatibility means in Swift 5 and how it will evolve in future releases of Swift.

- - - Read more... -
-
- -
-
-

Introducing the sourcekitd Stress Tester

- -
-
-

Sourcekitd provides the data backing key editor features like code completion, semantic highlighting, and refactoring for Swift files in both Xcode and the recently announced SourceKit-LSP. To help improve its robustness, we’re introducing a new tool, the sourcekitd stress tester, that over the past few months has helped find 91 reproducible sourcekitd crashes, assertion failures, and hangs. This post covers the stress tester’s implementation, its deployment in Swift’s CI and PR testing, and how Swift developers can run it over their own projects to help improve the Swift editing experience for everyone.

- - - Read more... -
-
- -
-
-

Swift 5 Exclusivity Enforcement

- -
-
-

The Swift 5 release enables runtime checking of “Exclusive Access to -Memory” by default in Release builds, further enhancing Swift’s -capabilities as a safe language. In Swift 4, these runtime checks were -only enabled in Debug builds. In this post, I’ll first explain what -this change means for Swift developers before delving into why it is -essential to Swift’s strategy for safety and performance.

- - - Read more... -
-
- -
-
-

REPL Support for Swift Packages

- -
-
-

The swift run command has a new --repl option which launches the Swift REPL with support for importing library targets of a package.

- - - Read more... -
-
- -
-
-

How Mirror Works

- -
-
-

Swift places a lot of emphasis on static typing, but it also supports rich metadata about types, which allows code to inspect and manipulate arbitrary values at runtime. This is exposed to Swift programmers through the Mirror API. One might wonder, how does something like Mirror work in a language with so much emphasis on static types? Let’s take a look!

- - - Read more... -
-
- - - -
-
-

Swift 4.2 Released!

- -
-
-

Swift 4.2 is now officially released! Swift 4.2 builds on the strengths of Swift 4, delivering faster compile times, improving the debugging experience, updating the standard library, and converging on binary compatibility.

- - - Read more... -
-
- - - -
-
-

Swift Community-Hosted Continuous Integration

- -
-
-

We are delighted to announce a significant expansion of our Swift.org continuous integration testing system. Members of the Swift community have been hard at work to support Swift on a number of new platforms, and we have extended the Swift CI system to support community-hosted nodes for testing additional platforms.

- - - Read more... -
-
- -
-
-

Reimplementation of Implicitly Unwrapped Optionals

- -
-
-

A new implementation of implicitly unwrapped optionals (IUOs) landed in the Swift compiler earlier this year and is available to try in recent Swift snapshots. -This completes the implementation of SE-0054 - Abolish ImplicitlyUnwrappedOptional Type. -This is an important change to the language that eliminated some inconsistencies in type checking and clarified the rule of how these values are to be treated so that it is consistent and easy to reason about. For more information, see the motivation section of that proposal.

- - - Read more... -
-
- -
-
-

Swift 4.1 Released!

- -
-
-

Swift 4.1 is now officially released! It contains updates to the core language, including more support for generics, new build options, as well as minor enhancements to Swift Package Manager and Foundation. There was also significant progress made in stabilizing the ABI.

- - - Read more... -
-
- - - - - -
-
-

Swift Forums Now Open!

- -
-
-

We are delighted to announce that the Swift project has completed the process of migrating to the Swift Forums as the primary method for discussion and communication! The former mailing lists have been shut down and archived, and all mailing list content has been imported into the new forum system.

- - - Read more... -
-
- - - - - -
-
-

Xcode 9.1 Improves Display of Fatal Errors

- -
-
-

Swift has language constructs that allow you to specify your program’s expectations. If these expectations are not met at runtime, the program will be terminated. For example, indexing into an array implicitly expresses an expectation that the index is in bounds:

- - - Read more... -
-
- -
-
-

Dictionary and Set Improvements in Swift 4.0

- -
-
-

In the latest release of Swift, -dictionaries and sets gain a number of new methods and initializers -that make common tasks easier than ever. -Operations like grouping, filtering, and transforming values -can now be performed in a single step, -letting you write more expressive and efficient code.

- - - Read more... -
-
- -
-
-

Swift 4.0 Released!

- -
-
-

Swift 4 is now officially released! Swift 4 builds on the strengths of Swift 3, delivering greater robustness and stability, providing source code compatibility with Swift 3, making improvements to the standard library, and adding features like archival and serialization.

- - - Read more... -
-
- -
-
-

Swift Local Refactoring

- -
-
-

Xcode 9 includes a brand new refactoring engine. It can transform code locally -within a single Swift source file, or globally, such as renaming a method or property -that occurs in multiple files and even different languages. The logic behind local refactorings is -implemented entirely in the compiler and SourceKit, and is now open source in -the swift repository. Therefore, any Swift enthusiast can -contribute refactoring actions to the language. This post discusses how -a simple refactoring can be implemented and surfaced in Xcode.

- - - Read more... -
-
- -
-
-

Swift Package Manager Manifest API Redesign

- -
-
-

The Package Manager in Swift 4 includes the redesigned Package.swift manifest -API. The new API is easier to use and follows the design guidelines. The target -inference rules in Swift 3 Package Manager were a common source of confusion. We -revised these rules and removed most of the inference, favoring the practice of -explicitly specifying package structure in the manifest.

- - - Read more... -
-
- - - -
-
-

Swift 3.1 Released!

- -
-
-

Swift 3.1 is now officially released! Swift 3.1 is a minor release that contains improvements and refinements to the Standard Library. Thanks to efforts by IBM and other members of the community, it also includes many updates to the Linux implementation of Swift. There are also a number of updates to Swift Package Manager.

- - - Read more... -
-
- - - -
-
-

Faster Mix-and-Match Builds with Precompiled Bridging Headers

- -
-
-

An examination of build times of Xcode projects that mix Objective-C and Swift, which can contain large bridging headers, shows that the Swift compiler spends a lot of time re-processing the same bridging headers for all the Swift files in a project. -In certain projects, each additional Swift file increases the overall build time noticeably, even when the Swift file is quite modest.

- - - Read more... -
-
- - - - - -
-
-

Server APIs Work Group

- -
-
-

Since Swift became available on Linux there has been a huge amount of interest in using Swift on the server, resulting in the emergence of a number of Web Frameworks, including Kitura, Vapor, Perfect, and Zewo, along with many others. As an important part of the Swift ecosystem, and one that we are keen to foster, we are today announcing the formation of the Server APIs work group.

- - - Read more... -
-
- -
-
-

Whole-Module Optimization in Swift 3

- -
-
-

Whole-module optimization is an optimization mode of the Swift compiler. -The performance win of whole-module optimization heavily depends on the project, but it can be up to two or even five times.

- - - Read more... -
-
- -
-
-

Swift 3.0 Released!

- -
-
-

Swift 3.0, the first major release of Swift since it was open-sourced, is now officially released! Swift 3 is a huge release containing major improvements and refinements to the core language and Standard Library, major additions to the Linux port of Swift, and the first official release of the Swift Package Manager.

- - - - Read more... -
-
- - - - - - - - - -
-
-

New Features in Swift 2.2

- -
-
-

Swift 2.2 brings new syntax, new features, and some deprecations too. It is an interim release before Swift 3 comes later this year with even bigger changes, and the changes in Swift 2.2 align with the broader goals of Swift 3 to focus on gradually stabilizing the core language and Standard Library by adding missing features, refining what is already there, and removing what is no longer needed in the language. All changes in Swift 2.2 went through the community-driven Swift evolution process — where over 30 proposals have been submitted, reviewed, and accepted since Swift was open-sourced a few months ago.

- - - Read more... -
-
- -
-
-

Swift 2.2 Released!

- -
-
-

We are very pleased to announce the release of Swift 2.2! This is the first official release of Swift since it was open-sourced on December 3, 2015. Notably, the release includes contributions from 212 non-Apple contributors — changes that span from simple bug fixes to enhancements and alterations to the core language and Swift Standard Library.

- - - Read more... -
-
- -
-
-

Expanding Commit Access

- -
-
-

Now that the Swift Continuous Integration system is established and proven, we’d like to grant commit access on a more frequent basis to project contributors who have established a track record of good contributions. If you would like commit access, please send an email to the code owners list with a list of 5 non-trivial pull requests that we accepted without modifications.

- - - Read more... -
-
- - - - - - - - - -
-
-

Swift 3 API Design Guidelines

- -
-
-

The design of commonly-used libraries has a large impact on the -overall feel of a programming language. Great libraries feel like an -extension of the language itself, and consistency across libraries -elevates the overall development experience. To aid in the -construction of great Swift libraries, one of the major goals for -Swift 3 is to define a set of API design guidelines -and to apply those design guidelines consistently.

- - - - Read more... -
-
- -
-
-

The Swift Linux Port

- -
-
-

With the launch of the open source Swift project, we are also releasing -a port that works with the Linux operating system! You can build it from -the Swift sources or download pre-built binaries for Ubuntu. The port -is still a work in progress but we’re happy to say that it is usable -today for experimentation. Currently x86_64 is the only supported -architecture on Linux.

- - - - Read more... -
-
- -
-
-

The Swift.org Blog

- -
-
-

Welcome to the blog on Swift.org! Today we launched the open source Swift project along with the Swift.org website. We couldn’t be more excited to work together in an open community to find and fix issues, add enhancements, and bring Swift to new platforms.

- - - Read more... -
-
- - -
- -
- - -
- - - - - - - - - - -""" -} diff --git a/Sources/RegexBenchmark/Suite/CssRegex.swift b/Sources/RegexBenchmark/Suite/CssRegex.swift index 760b64537..84e438db1 100644 --- a/Sources/RegexBenchmark/Suite/CssRegex.swift +++ b/Sources/RegexBenchmark/Suite/CssRegex.swift @@ -3,7 +3,9 @@ import _StringProcessing extension BenchmarkRunner { mutating func addCSS() { - let r = #"--([a-zA-Z0-9_-]+)\s*:\s*(.*?);"# + let r = #"--([a-zA-Z0-9_-]+)\s*:\s*(.*?):"# + + // FIXME: Why is `first` and `all` the same running time? let css = CrossBenchmark( baseName: "css", regex: r, input: Inputs.swiftOrgCSS) diff --git a/Sources/RegexBenchmark/Suite/EmailRegex.swift b/Sources/RegexBenchmark/Suite/EmailRegex.swift deleted file mode 100644 index 2c5cee812..000000000 --- a/Sources/RegexBenchmark/Suite/EmailRegex.swift +++ /dev/null @@ -1,41 +0,0 @@ -import _StringProcessing -import Foundation - -extension BenchmarkRunner { - mutating func addEmail() { - // Regexes from https://www.regular-expressions.info/email.html - // Inputs.validEmails is generated by Utils/generateEmails.py - - // Relatively simple regex to match email addresses, based on the offical RFC grammar - let emailRFC = #"[A-z0-9!#$%&'*+\/=?^_‘{|}~-]+(?:\.[A-z0-9!#$%&'*+\/=?^_‘{|}~-]+)*@(?:[A-z0-9](?:[A-z0-9-]*[A-z0-9])?\.)+[A-z0-9](?:[A-z0-9-]*[A-z0-9])?"# - - // More complex, does length and consecutive hyphen validation via lookaheads - let emailWithLookaheads = #"(?=[A-z0-9][A-z0-9@._%+-]{5,253})[A-z0-9._%+-]{1,64}@(?:(?=[A-z0-9-]{1,63}\.)[A-z0-9]+(?:-[A-z0-9]+)*\.){1,8}[A-z]{2,63}"# - - let emailRFCValid = CrossBenchmark( - baseName: "emailRFC", regex: emailRFC, input: Inputs.validEmails) - - let emailRFCInvalid = CrossBenchmark( - baseName: "emailRFCNoMatches", - regex: emailRFC, - input: Inputs.graphemeBreakData - ) - - let emailValid = CrossBenchmark( - baseName: "emailLookahead", - regex: emailWithLookaheads, - input: Inputs.validEmails - ) - - let emailInvalid = CrossBenchmark( - baseName: "emailLookaheadNoMatches", - regex: emailWithLookaheads, - input: Inputs.graphemeBreakData - ) - - emailRFCValid.register(&self) - emailRFCInvalid.register(&self) - emailValid.register(&self) - emailInvalid.register(&self) - } -} diff --git a/Sources/RegexBenchmark/Suite/HtmlRegex.swift b/Sources/RegexBenchmark/Suite/HtmlRegex.swift deleted file mode 100644 index 2c8e0f281..000000000 --- a/Sources/RegexBenchmark/Suite/HtmlRegex.swift +++ /dev/null @@ -1,12 +0,0 @@ -import _StringProcessing - -extension BenchmarkRunner { - mutating func addHTML() { - // Backreference + reluctant quantifier - let r = #"<(\w*)\b[^>]*>(.*?)<\/\1>"# - - let html = CrossBenchmark( - baseName: "html", regex: r, input: Inputs.swiftOrgHTML) - html.register(&self) - } -} diff --git a/Sources/RegexBenchmark/Utils/Time.swift b/Sources/RegexBenchmark/Utils/Time.swift index 3fe567bda..9fa54c1aa 100644 --- a/Sources/RegexBenchmark/Utils/Time.swift +++ b/Sources/RegexBenchmark/Utils/Time.swift @@ -55,25 +55,15 @@ extension Time: Comparable { } } -extension Time { - public static func - (left: Self, right: Self) -> Self { - return Time(left.seconds - right.seconds) - } - - public func abs() -> Time { - Time(Swift.abs(self.seconds)) - } -} - extension Time: CustomStringConvertible { public var description: String { if self.seconds == 0 { return "0" } - if self.abs() < .attosecond { return String(format: "%.3gas", seconds * 1e18) } - if self.abs() < .picosecond { return String(format: "%.3gfs", seconds * 1e15) } - if self.abs() < .nanosecond { return String(format: "%.3gps", seconds * 1e12) } - if self.abs() < .microsecond { return String(format: "%.3gns", seconds * 1e9) } - if self.abs() < .millisecond { return String(format: "%.3gµs", seconds * 1e6) } - if self.abs() < .second { return String(format: "%.3gms", seconds * 1e3) } + if self < .attosecond { return String(format: "%.3gas", seconds * 1e18) } + if self < .picosecond { return String(format: "%.3gfs", seconds * 1e15) } + if self < .nanosecond { return String(format: "%.3gps", seconds * 1e12) } + if self < .microsecond { return String(format: "%.3gns", seconds * 1e9) } + if self < .millisecond { return String(format: "%.3gµs", seconds * 1e6) } + if self < .second { return String(format: "%.3gms", seconds * 1e3) } if self.seconds < 1000 { return String(format: "%.3gs", seconds) } return String(format: "%gs", seconds.rounded()) } @@ -81,12 +71,12 @@ extension Time: CustomStringConvertible { public var typesetDescription: String { let spc = "\u{200A}" if self.seconds == 0 { return "0\(spc)s" } - if self.abs() < .femtosecond { return String(format: "%.3g\(spc)as", seconds * 1e18) } - if self.abs() < .picosecond { return String(format: "%.3g\(spc)fs", seconds * 1e15) } - if self.abs() < .nanosecond { return String(format: "%.3g\(spc)ps", seconds * 1e12) } - if self.abs() < .microsecond { return String(format: "%.3g\(spc)ns", seconds * 1e9) } - if self.abs() < .millisecond { return String(format: "%.3g\(spc)µs", seconds * 1e6) } - if self.abs() < .second { return String(format: "%.3g\(spc)ms", seconds * 1e3) } + if self < .femtosecond { return String(format: "%.3g\(spc)as", seconds * 1e18) } + if self < .picosecond { return String(format: "%.3g\(spc)fs", seconds * 1e15) } + if self < .nanosecond { return String(format: "%.3g\(spc)ps", seconds * 1e12) } + if self < .microsecond { return String(format: "%.3g\(spc)ns", seconds * 1e9) } + if self < .millisecond { return String(format: "%.3g\(spc)µs", seconds * 1e6) } + if self < .second { return String(format: "%.3g\(spc)ms", seconds * 1e3) } if self.seconds < 1000 { return String(format: "%.3g\(spc)s", seconds) } return String(format: "%g\(spc)s", seconds.rounded()) } diff --git a/Utils/generateEmails.py b/Utils/generateEmails.py deleted file mode 100644 index 893572ca1..000000000 --- a/Utils/generateEmails.py +++ /dev/null @@ -1,22 +0,0 @@ -import string -import random - -domain_charset = string.ascii_letters + string.digits + ".-" -locale_charset = domain_charset + "_%+" - -n = 1000 - -# for the most part this will generate mostly valid emails -# there are some edge cases with double hyphens and double periods that cause -# issues but otherwise this should work - -for _ in range(n): - domain_len = random.randint(2,64) - locale_len = random.randint(2,64) - tld_len = random.randint(2,10) - - domain = "".join(random.sample(domain_charset, domain_len)) - locale = "".join(random.sample(locale_charset, locale_len)) - tld = "".join(random.sample(string.ascii_lowercase, tld_len)) - email = locale + "@" + domain + "." + tld - print(email.lower()) From 06e6e025f562a0a02ca7f637420bfa5c9afaa63b Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Fri, 13 May 2022 11:55:47 -0700 Subject: [PATCH 16/24] Handle atoms as things to be wrapped oops Repeat does not get to participate in inline fix tests --- .../_StringProcessing/PrintAsPattern.swift | 110 +++++++++++++----- Tests/RegexTests/RenderDSLTests.swift | 21 ++-- 2 files changed, 88 insertions(+), 43 deletions(-) diff --git a/Sources/_StringProcessing/PrintAsPattern.swift b/Sources/_StringProcessing/PrintAsPattern.swift index 03fca2e1f..5e4347f3b 100644 --- a/Sources/_StringProcessing/PrintAsPattern.swift +++ b/Sources/_StringProcessing/PrintAsPattern.swift @@ -127,7 +127,7 @@ extension PrettyPrinter { print("/* TODO: conditional */") case let .quantification(amount, kind, child): - let amount = amount.ast._patternBase + let amountStr = amount.ast._patternBase var kind = kind.ast?._patternBase ?? "" // If we've updated our quantification behavior, then use that. This @@ -137,10 +137,10 @@ extension PrettyPrinter { kind = quantificationBehavior._patternBase } - var blockName = "\(amount)(\(kind))" + var blockName = "\(amountStr)(\(kind))" if kind == ".eager" { - blockName = "\(amount)" + blockName = "\(amountStr)" } // Special case single child character classes for repetition nodes. @@ -152,6 +152,20 @@ extension PrettyPrinter { // One(.digit) // } // + func printAtom(_ pattern: String) { + indent() + + if kind != ".eager" { + blockName.removeLast() + output("\(blockName), ") + } else { + output("\(blockName)(") + } + + output("\(pattern))") + terminateLine() + } + func printSimpleCCC( _ ccc: DSLTree.CustomCharacterClass ) { @@ -169,23 +183,42 @@ extension PrettyPrinter { terminateLine() } - switch child { - case let .customCharacterClass(ccc): - if ccc.isSimplePrint { - printSimpleCCC(ccc) - return - } - - break - case let .convertedRegexLiteral(.customCharacterClass(ccc), _): - if ccc.isSimplePrint { - printSimpleCCC(ccc) - return + // We can only do this for Optionally, ZeroOrMore, and OneOrMore. Cannot + // do it right now for Repeat. + if amount.ast.supportsInlineComponent { + switch child { + case let .atom(a): + if let pattern = a._patternBase(&self), pattern.canBeWrapped { + printAtom(pattern.0) + return + } + + break + case let .customCharacterClass(ccc): + if ccc.isSimplePrint { + printSimpleCCC(ccc) + return + } + + break + + case let .convertedRegexLiteral(.atom(a), _): + if let pattern = a._patternBase(&self), pattern.canBeWrapped { + printAtom(pattern.0) + return + } + + break + case let .convertedRegexLiteral(.customCharacterClass(ccc), _): + if ccc.isSimplePrint { + printSimpleCCC(ccc) + return + } + + break + default: + break } - - break - default: - break } printBlock(blockName) { printer in @@ -199,7 +232,11 @@ extension PrettyPrinter { } if let pattern = a._patternBase(&self) { - print(pattern) + if pattern.canBeWrapped { + print("One(\(pattern.0))") + } else { + print(pattern.0) + } } case .trivia: @@ -391,9 +428,9 @@ extension PrettyPrinter { if let lhs = lhs._patternBase(&self), let rhs = rhs._patternBase(&self) { indent() output("(") - output(lhs) + output(lhs.0) output("...") - output(rhs) + output(rhs.0) output(")") } @@ -939,6 +976,15 @@ extension AST.Quantification.Amount { case let .range(n, m): return "Repeat(\(n.value)...\(m.value))" } } + + var supportsInlineComponent: Bool { + switch self { + case .zeroOrMore: return true + case .oneOrMore: return true + case .zeroOrOne: return true + default: return false + } + } } extension AST.Quantification.Kind { @@ -1033,33 +1079,35 @@ extension DSLTree.CustomCharacterClass { } extension DSLTree.Atom { - func _patternBase(_ printer: inout PrettyPrinter) -> String? { + func _patternBase( + _ printer: inout PrettyPrinter + ) -> (String, canBeWrapped: Bool)? { switch self { case .any: - return ".any" + return (".any", true) case let .char(c): - return String(c)._quoted + return (String(c)._quoted, false) case let .scalar(s): let hex = String(s.value, radix: 16, uppercase: true) - return "\\u{\(hex)}"._quoted + return ("\\u{\(hex)}"._quoted, false) case let .unconverted(a): if a.ast.isUnprintableAtom { - return "#/\(a.ast._regexBase)/#" + return ("#/\(a.ast._regexBase)/#", false) } else { - return a.ast._dslBase.0 + return a.ast._dslBase } case .assertion(let a): - return a.ast._patternBase + return (a.ast._patternBase, false) case .backreference(_): - return "/* TOOD: backreferences */" + return ("/* TOOD: backreferences */", false) case .symbolicReference: - return "/* TOOD: symbolic references */" + return ("/* TOOD: symbolic references */", false) case .changeMatchingOptions(let matchingOptions): for add in matchingOptions.ast.adding { diff --git a/Tests/RegexTests/RenderDSLTests.swift b/Tests/RegexTests/RenderDSLTests.swift index 016b32f4a..75f6d2974 100644 --- a/Tests/RegexTests/RenderDSLTests.swift +++ b/Tests/RegexTests/RenderDSLTests.swift @@ -56,20 +56,17 @@ extension RenderDSLTests { try testConversion(#"\d+"#, """ Regex { - OneOrMore { - .digit - } + OneOrMore(.digit) + } + """) + + try testConversion(#":\d:"#, """ + Regex { + ":" + One(.digit) + ":" } """) - try XCTExpectFailure("Invalid leading dot syntax in non-initial position") { - try testConversion(#":\d:"#, """ - Regex { - ":" - CharacterClass.digit - ":" - } - """) - } } func testOptions() throws { From e6a4032ff94c8d347f77a91e7dace6efe96b9d47 Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Tue, 21 Jun 2022 11:46:57 -0600 Subject: [PATCH 17/24] Remove unused regsiters, opodes (#506) --- .../Engine/Instruction.swift | 120 +----------------- .../_StringProcessing/Engine/MEBuilder.swift | 111 +--------------- .../_StringProcessing/Engine/MEBuiltins.swift | 13 ++ .../_StringProcessing/Engine/MEProgram.swift | 5 - .../_StringProcessing/Engine/Processor.swift | 92 ++------------ .../_StringProcessing/Engine/Registers.swift | 32 ----- 6 files changed, 28 insertions(+), 345 deletions(-) create mode 100644 Sources/_StringProcessing/Engine/MEBuiltins.swift diff --git a/Sources/_StringProcessing/Engine/Instruction.swift b/Sources/_StringProcessing/Engine/Instruction.swift index 9144c031f..9d2ae5a69 100644 --- a/Sources/_StringProcessing/Engine/Instruction.swift +++ b/Sources/_StringProcessing/Engine/Instruction.swift @@ -27,25 +27,6 @@ extension Instruction { // MARK: - General Purpose - /// Do nothing - /// - /// nop(comment: String?) - /// - /// Operand: Optional string register containing a comment or reason - /// - case nop - - /// Decrement the value stored in a register. - /// Returns whether the value was set to zero - /// - /// decrement(_ i: IntReg) -> Bool - /// - /// Operands: - /// - Int register to decrease - /// - Condition register set if now zero - /// - case decrement - /// Move an immediate value into a register /// /// moveImmediate(_ i: Int, into: IntReg) @@ -65,15 +46,6 @@ extension Instruction { /// Operand: instruction address to branch to case branch - /// Conditionally branch - /// - /// condBranch(to: InstAddr, if: BoolReg) - /// - /// Operands: - /// - Address to branch to - /// - Condition register to check - case condBranch - /// Conditionally branch if zero, otherwise decrement /// /// condBranch( @@ -85,39 +57,7 @@ extension Instruction { /// case condBranchZeroElseDecrement - // MARK: General Purpose: Function calls - - /// Push an instruction address to the stack - /// - /// Operand: the instruction address - /// - /// UNIMPLEMENTED - case push - - /// Pop return address from call stack - /// - /// UNIMPLEMENTED - case pop - - /// Composite push-next-branch instruction - /// - /// Operand: the function's start address - case call - - /// Composite pop-branch instruction - /// - /// Operand: the instruction address - /// - /// NOTE: Currently, empty stack -> ACCEPT - case ret - - // MARK: General Purpose: Debugging instructions - - /// Print a string to the output - /// - /// Operand: String register - case print - + // TODO: Function calls // MARK: - Matching @@ -144,28 +84,11 @@ extension Instruction { /// Operand: Sequence register to compare against. case matchSequence - /// Match against a slice of the input - /// - /// matchSlice( - /// lowerBound: PositionReg, upperBound: PositionReg) - /// - /// Operands: - /// - Lowerbound position in the input - /// - Upperbound position in the input - case matchSlice - - /// Save the current position in the input in a register - /// - /// movePosition(into: PositionReg) - /// - /// Operand: The position register to move into - case movePosition + /// TODO: builtin assertions and anchors + case builtinAssertion - /// Match against a provided element. - /// - /// Operand: Packed condition register to write to and element register to - /// compare against. - case assertion + /// TODO: builtin character classes + case builtinCharacterClass // MARK: Extension points @@ -235,14 +158,6 @@ extension Instruction { /// Precondition: The operand is in the save point list case clearThrough - /// View the most recently saved point - /// - /// UNIMPLEMENTED - case peek - - /// Composite peek-branch-clear else FAIL - case restore - /// Fused save-and-branch. /// /// split(to: target, saving: backtrackPoint) @@ -290,17 +205,9 @@ extension Instruction { /// Signal failure (currently same as `restore`) case fail - /// Halt, fail, and signal failure - /// - /// Operand: optional string register specifying the reason - /// - /// TODO: Could have an Error existential area instead - case abort - // TODO: Fused assertions. It seems like we often want to // branch based on assertion fail or success. - } } @@ -385,23 +292,10 @@ extension Instruction { // TODO: replace with instruction formatters... extension Instruction { - var stringRegister: StringRegister? { - switch opcode { - case .nop, .abort: - return payload.optionalString - case .print: - return payload.string - default: return nil - } - } var instructionAddress: InstructionAddress? { switch opcode { - case .branch, .save, .saveAddress, .call: + case .branch, .save, .saveAddress: return payload.addr - - case .condBranch: - return payload.pairedAddrBool.0 - default: return nil } } @@ -409,8 +303,6 @@ extension Instruction { switch opcode { case .match: return payload.element - case .assertion: - return payload.pairedElementBool.0 default: return nil } } diff --git a/Sources/_StringProcessing/Engine/MEBuilder.swift b/Sources/_StringProcessing/Engine/MEBuilder.swift index f998a4952..f278b7328 100644 --- a/Sources/_StringProcessing/Engine/MEBuilder.swift +++ b/Sources/_StringProcessing/Engine/MEBuilder.swift @@ -17,7 +17,6 @@ extension MEProgram { var elements = TypedSetVector() var sequences = TypedSetVector<[Input.Element], _SequenceRegister>() - var strings = TypedSetVector() var consumeFunctions: [ConsumeFunction] = [] var assertionFunctions: [AssertionFunction] = [] @@ -29,9 +28,7 @@ extension MEProgram { var addressFixups: [(InstructionAddress, AddressFixup)] = [] // Registers - var nextBoolRegister = BoolRegister(0) var nextIntRegister = IntRegister(0) - var nextPositionRegister = PositionRegister(0) var nextCaptureRegister = CaptureRegister(0) var nextValueRegister = ValueRegister(0) @@ -79,20 +76,6 @@ extension MEProgram.Builder { .init(instructions.endIndex - 1) } - mutating func buildNop(_ r: StringRegister? = nil) { - instructions.append(.init(.nop, .init(optionalString: r))) - } - mutating func buildNop(_ s: String) { - buildNop(strings.store(s)) - } - - mutating func buildDecrement( - _ i: IntRegister, nowZero: BoolRegister - ) { - instructions.append(.init( - .decrement, .init(bool: nowZero, int: i))) - } - mutating func buildMoveImmediate( _ value: UInt64, into: IntRegister ) { @@ -108,24 +91,10 @@ extension MEProgram.Builder { buildMoveImmediate(uint, into: into) } - mutating func buildMoveCurrentPosition( - into: PositionRegister - ) { - instructions.append(.init( - .movePosition, .init(position: into))) - } - mutating func buildBranch(to t: AddressToken) { instructions.append(.init(.branch)) fixup(to: t) } - mutating func buildCondBranch( - _ condition: BoolRegister, to t: AddressToken - ) { - instructions.append( - .init(.condBranch, .init(bool: condition))) - fixup(to: t) - } mutating func buildCondBranch( to t: AddressToken, ifZeroElseDecrement i: IntRegister @@ -157,27 +126,9 @@ extension MEProgram.Builder { instructions.append(.init(.clearThrough)) fixup(to: t) } - mutating func buildRestore() { - instructions.append(.init(.restore)) - } mutating func buildFail() { instructions.append(.init(.fail)) } - mutating func buildCall(_ t: AddressToken) { - instructions.append(.init(.call)) - fixup(to: t) - } - mutating func buildRet() { - instructions.append(.init(.ret)) - } - - mutating func buildAbort(_ s: StringRegister? = nil) { - instructions.append(.init( - .abort, .init(optionalString: s))) - } - mutating func buildAbort(_ s: String) { - buildAbort(strings.store(s)) - } mutating func buildAdvance(_ n: Distance) { instructions.append(.init(.advance, .init(distance: n))) @@ -196,14 +147,6 @@ extension MEProgram.Builder { .init(sequence: sequences.store(.init(s))))) } - mutating func buildMatchSlice( - lower: PositionRegister, upper: PositionRegister - ) { - instructions.append(.init( - .matchSlice, - .init(pos: lower, pos2: upper))) - } - mutating func buildConsume( by p: @escaping MEProgram.ConsumeFunction ) { @@ -218,21 +161,10 @@ extension MEProgram.Builder { .assertBy, .init(assertion: makeAssertionFunction(p)))) } - mutating func buildAssert( - _ e: Character, into cond: BoolRegister - ) { - instructions.append(.init(.assertion, .init( - element: elements.store(e), bool: cond))) - } - mutating func buildAccept() { instructions.append(.init(.accept)) } - mutating func buildPrint(_ s: StringRegister) { - instructions.append(.init(.print, .init(string: s))) - } - mutating func buildBeginCapture( _ cap: CaptureRegister ) { @@ -315,13 +247,10 @@ extension MEProgram.Builder { let payload: Instruction.Payload switch inst.opcode { - case .condBranch: - payload = .init(addr: addr, bool: inst.payload.bool) - case .condBranchZeroElseDecrement: payload = .init(addr: addr, int: inst.payload.int) - case .branch, .save, .saveAddress, .call, .clearThrough: + case .branch, .save, .saveAddress, .clearThrough: payload = .init(addr: addr) case .splitSaving: @@ -342,10 +271,7 @@ extension MEProgram.Builder { var regInfo = MEProgram.RegisterInfo() regInfo.elements = elements.count regInfo.sequences = sequences.count - regInfo.strings = strings.count - regInfo.bools = nextBoolRegister.rawValue regInfo.ints = nextIntRegister.rawValue - regInfo.positions = nextPositionRegister.rawValue regInfo.values = nextValueRegister.rawValue regInfo.consumeFunctions = consumeFunctions.count regInfo.assertionFunctions = assertionFunctions.count @@ -357,7 +283,6 @@ extension MEProgram.Builder { instructions: InstructionList(instructions), staticElements: elements.stored, staticSequences: sequences.stored, - staticStrings: strings.stored, staticConsumeFunctions: consumeFunctions, staticAssertionFunctions: assertionFunctions, staticTransformFunctions: transformFunctions, @@ -468,18 +393,10 @@ extension MEProgram.Builder { return nextCaptureRegister } - mutating func makeBoolRegister() -> BoolRegister { - defer { nextBoolRegister.rawValue += 1 } - return nextBoolRegister - } mutating func makeIntRegister() -> IntRegister { defer { nextIntRegister.rawValue += 1 } return nextIntRegister } - mutating func makePositionRegister() -> PositionRegister { - defer { nextPositionRegister.rawValue += 1 } - return nextPositionRegister - } mutating func makeValueRegister() -> ValueRegister { defer { nextValueRegister.rawValue += 1 } return nextValueRegister @@ -494,32 +411,6 @@ extension MEProgram.Builder { return r } - // Allocate and initialize a register - mutating func makePositionRegister( - initializingWithCurrentPosition: () - ) -> PositionRegister { - let r = makePositionRegister() - self.buildMoveCurrentPosition(into: r) - return r - } - - // 'kill' or release allocated registers - mutating func kill(_ r: IntRegister) { - // TODO: Release/reuse registers, for now nop makes - // reading the code easier - buildNop("kill \(r)") - } - mutating func kill(_ r: BoolRegister) { - // TODO: Release/reuse registers, for now nop makes - // reading the code easier - buildNop("kill \(r)") - } - mutating func kill(_ r: PositionRegister) { - // TODO: Release/reuse registers, for now nop makes - // reading the code easier - buildNop("kill \(r)") - } - // TODO: A register-mapping helper struct, which could release // registers without monotonicity required diff --git a/Sources/_StringProcessing/Engine/MEBuiltins.swift b/Sources/_StringProcessing/Engine/MEBuiltins.swift new file mode 100644 index 000000000..f791da37e --- /dev/null +++ b/Sources/_StringProcessing/Engine/MEBuiltins.swift @@ -0,0 +1,13 @@ + + +extension Processor { + + + mutating func builtinAssertion() { + fatalError("TODO: assertions and anchors") + } + + mutating func builtinCharacterClass() { + fatalError("TODO: character classes") + } +} diff --git a/Sources/_StringProcessing/Engine/MEProgram.swift b/Sources/_StringProcessing/Engine/MEProgram.swift index dd166e554..2a0ec2719 100644 --- a/Sources/_StringProcessing/Engine/MEProgram.swift +++ b/Sources/_StringProcessing/Engine/MEProgram.swift @@ -26,7 +26,6 @@ struct MEProgram { var staticElements: [Input.Element] var staticSequences: [[Input.Element]] - var staticStrings: [String] var staticConsumeFunctions: [ConsumeFunction] var staticAssertionFunctions: [AssertionFunction] var staticTransformFunctions: [TransformFunction] @@ -46,7 +45,6 @@ extension MEProgram: CustomStringConvertible { var description: String { var result = """ Elements: \(staticElements) - Strings: \(staticStrings) """ if !staticConsumeFunctions.isEmpty { @@ -58,9 +56,6 @@ extension MEProgram: CustomStringConvertible { for idx in instructions.indices { let inst = instructions[idx] result += "[\(idx.rawValue)] \(inst)" - if let sp = inst.stringRegister { - result += " // \(staticStrings[sp.rawValue])" - } if let ia = inst.instructionAddress { result += " // \(instructions[ia])" } diff --git a/Sources/_StringProcessing/Engine/Processor.swift b/Sources/_StringProcessing/Engine/Processor.swift index 1717e485d..8471ef861 100644 --- a/Sources/_StringProcessing/Engine/Processor.swift +++ b/Sources/_StringProcessing/Engine/Processor.swift @@ -265,20 +265,6 @@ extension Processor { switch opcode { case .invalid: fatalError("Invalid program") - case .nop: - if checkComments, - let s = payload.optionalString - { - doPrint(registers[s]) - } - controller.step() - - case .decrement: - let (bool, int) = payload.pairedBoolInt - let newValue = registers[int] - 1 - registers[bool] = newValue == 0 - registers[int] = newValue - controller.step() case .moveImmediate: let (imm, reg) = payload.pairedImmediateInt @@ -288,22 +274,9 @@ extension Processor { registers[reg] = int controller.step() - case .movePosition: - let reg = payload.position - registers[reg] = currentPosition - controller.step() - case .branch: controller.pc = payload.addr - case .condBranch: - let (addr, cond) = payload.pairedAddrBool - if registers[cond] { - controller.pc = addr - } else { - controller.step() - } - case .condBranchZeroElseDecrement: let (addr, int) = payload.pairedAddrInt if registers[int] == 0 { @@ -341,38 +314,6 @@ extension Processor { case .clearThrough: clearThrough(payload.addr) - - case .peek: - fatalError() - - case .restore: - signalFailure() - - case .push: - fatalError() - - case .pop: - fatalError() - - case .call: - controller.step() - callStack.append(controller.pc) - controller.pc = payload.addr - - case .ret: - // TODO: Should empty stack mean success? - guard let r = callStack.popLast() else { - tryAccept() - return - } - controller.pc = r - - case .abort: - // TODO: throw or otherwise propagate - if let s = payload.optionalString { - doPrint(registers[s]) - } - state = .fail case .accept: tryAccept() @@ -398,14 +339,6 @@ extension Processor { controller.step() } - case .matchSlice: - let (lower, upper) = payload.pairedPosPos - let range = registers[lower].. String { - strings[i.rawValue] - } subscript(_ i: SequenceRegister) -> [Input.Element] { sequences[i.rawValue] } @@ -70,14 +58,6 @@ extension Processor.Registers { get { ints[i.rawValue] } set { ints[i.rawValue] = newValue } } - subscript(_ i: BoolRegister) -> Bool { - get { bools[i.rawValue] } - set { bools[i.rawValue] = newValue } - } - subscript(_ i: PositionRegister) -> Input.Index { - get { positions[i.rawValue] } - set { positions[i.rawValue] = newValue } - } subscript(_ i: ValueRegister) -> Any { get { values[i.rawValue] } set { @@ -126,23 +106,14 @@ extension Processor.Registers { self.matcherFunctions = program.staticMatcherFunctions assert(matcherFunctions.count == info.matcherFunctions) - self.strings = program.staticStrings - assert(strings.count == info.strings) - - self.bools = Array(repeating: false, count: info.bools) - self.ints = Array(repeating: 0, count: info.ints) - self.positions = Array(repeating: sentinel, count: info.positions) - self.values = Array( repeating: SentinelValue(), count: info.values) } mutating func reset(sentinel: Input.Index) { - self.bools._setAll(to: false) self.ints._setAll(to: 0) - self.positions._setAll(to: sentinel) self.values._setAll(to: SentinelValue()) } } @@ -191,10 +162,7 @@ extension Processor.Registers: CustomStringConvertible { return """ \(formatRegisters("elements", elements))\ - \(formatRegisters("bools", bools))\ - \(formatRegisters("strings", strings))\ \(formatRegisters("ints", ints))\ - \(formatRegisters("positions", positions))\ """ } From 16a25f218dd0fa2e9b5111eea57968463d4471a6 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Tue, 21 Jun 2022 10:01:18 -0700 Subject: [PATCH 18/24] Unconditionally print a regex block for concatenations fix test --- .../_StringProcessing/PrintAsPattern.swift | 17 ++++++++++++---- Tests/RegexTests/RenderDSLTests.swift | 20 +++++++++---------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/Sources/_StringProcessing/PrintAsPattern.swift b/Sources/_StringProcessing/PrintAsPattern.swift index 5e4347f3b..2fe7c6ccc 100644 --- a/Sources/_StringProcessing/PrintAsPattern.swift +++ b/Sources/_StringProcessing/PrintAsPattern.swift @@ -71,8 +71,15 @@ extension PrettyPrinter { print("let \(namedCapture) = Reference(Substring.self)") } - printBlock("Regex") { printer in - printer.printAsPattern(convertedFromAST: node) + switch node { + case .concatenation(_): + printAsPattern(convertedFromAST: node) + case .convertedRegexLiteral(.concatenation(_), _): + printAsPattern(convertedFromAST: node) + default: + printBlock("Regex") { printer in + printer.printAsPattern(convertedFromAST: node) + } } } @@ -99,8 +106,10 @@ extension PrettyPrinter { } case let .concatenation(c): - c.forEach { - printAsPattern(convertedFromAST: $0) + printBlock("Regex") { printer in + c.forEach { + printer.printAsPattern(convertedFromAST: $0) + } } case let .nonCapturingGroup(kind, child): diff --git a/Tests/RegexTests/RenderDSLTests.swift b/Tests/RegexTests/RenderDSLTests.swift index 75f6d2974..7bf8ba412 100644 --- a/Tests/RegexTests/RenderDSLTests.swift +++ b/Tests/RegexTests/RenderDSLTests.swift @@ -97,19 +97,17 @@ extension RenderDSLTests { } """) - try XCTExpectFailure("Concatenations in alternations aren't grouped") { - try testConversion(#"\da|b"#, """ - Regex { - ChoiceOf { - Regex { - .digit - "a" - } - "bc" + try testConversion(#"\da|bc"#, """ + Regex { + ChoiceOf { + Regex { + One(.digit) + "a" } + "bc" } - """) - } + } + """) } func testQuoting() throws { From e3e186fcd0f26938eec2ad336db91cfc6cbd4061 Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Wed, 22 Jun 2022 16:47:16 -0500 Subject: [PATCH 19/24] Disentangle disparate 'bounds' ideas in processor (#496) This separates the two different ideas for boundaries in the base input: - subjectBounds: These represent the actual subject in the input string. For a `String` callee, this will cover the entire bounds, while for a `Substring` these will represent the bounds of the substring in the base. - searchBounds: These represent the current search range in the subject. These bounds can be the same as `subjectBounds` or a subrange when searching for subsequent matches or replacing only in a subrange of a string. * firstMatch shouldn't update searchBounds on iteration When we move forward while searching for the first match, the search bounds should stay the same. Only the currentPosition needs to move forward. This will allow us to implement the \G start of match anchor, with which /\Gab/ matches "abab" twice, compared with /^ab/, which only matches once. * Make matches(of:) and ranges(of:) boundary-aware With this change, RegexMatchesCollection keeps the subject bounds and search bounds separately, modifying the search bounds with each iteration. In addition, the replace methods that only operate on a subrange can specify that specifically, getting the correct anchor behavior while only matching within a portion of a string. --- .../Algorithms/Algorithms/Contains.swift | 2 +- .../Algorithms/Algorithms/Ranges.swift | 34 ++++++- .../Algorithms/Algorithms/Replace.swift | 5 +- .../Algorithms/Matching/Matches.swift | 39 ++++++-- Sources/_StringProcessing/ByteCodeGen.swift | 55 ++++++----- .../_StringProcessing/Engine/Consume.swift | 17 +++- .../_StringProcessing/Engine/Processor.swift | 93 +++++++++++------- Sources/_StringProcessing/Executor.swift | 33 ++++--- .../Regex/AnyRegexOutput.swift | 4 + Sources/_StringProcessing/Regex/Match.swift | 19 +++- .../_CharacterClassModel.swift | 6 +- Tests/RegexBuilderTests/RegexDSLTests.swift | 43 ++++++++- Tests/RegexTests/MatchTests.swift | 94 ++++++++----------- 13 files changed, 292 insertions(+), 152 deletions(-) diff --git a/Sources/_StringProcessing/Algorithms/Algorithms/Contains.swift b/Sources/_StringProcessing/Algorithms/Algorithms/Contains.swift index e481597f8..9d8e7349d 100644 --- a/Sources/_StringProcessing/Algorithms/Algorithms/Contains.swift +++ b/Sources/_StringProcessing/Algorithms/Algorithms/Contains.swift @@ -74,6 +74,6 @@ extension BidirectionalCollection where SubSequence == Substring { @_disfavoredOverload @available(SwiftStdlib 5.7, *) public func contains(_ regex: some RegexComponent) -> Bool { - _contains(RegexConsumer(regex)) + (try? regex.regex.firstMatch(in: self[...])) != nil } } diff --git a/Sources/_StringProcessing/Algorithms/Algorithms/Ranges.swift b/Sources/_StringProcessing/Algorithms/Algorithms/Ranges.swift index 40732255c..fc6b23af2 100644 --- a/Sources/_StringProcessing/Algorithms/Algorithms/Ranges.swift +++ b/Sources/_StringProcessing/Algorithms/Algorithms/Ranges.swift @@ -229,9 +229,18 @@ extension BidirectionalCollection where Element: Comparable { @available(SwiftStdlib 5.7, *) struct RegexRangesCollection { let base: RegexMatchesCollection - - init(string: Substring, regex: Regex) { - self.base = RegexMatchesCollection(base: string, regex: regex) + + init( + input: String, + subjectBounds: Range, + searchBounds: Range, + regex: Regex + ) { + self.base = .init( + input: input, + subjectBounds: subjectBounds, + searchBounds: searchBounds, + regex: regex) } } @@ -263,12 +272,29 @@ extension RegexRangesCollection: Collection { // MARK: Regex algorithms extension Collection where SubSequence == Substring { + @available(SwiftStdlib 5.7, *) + @_disfavoredOverload + func _ranges( + of regex: R, + subjectBounds: Range, + searchBounds: Range + ) -> RegexRangesCollection { + RegexRangesCollection( + input: self[...].base, + subjectBounds: subjectBounds, + searchBounds: searchBounds, + regex: regex.regex) + } + @available(SwiftStdlib 5.7, *) @_disfavoredOverload func _ranges( of regex: R ) -> RegexRangesCollection { - RegexRangesCollection(string: self[...], regex: regex.regex) + _ranges( + of: regex, + subjectBounds: startIndex.. Self where Replacement.Element == Element { _replacing( - self[subrange]._ranges(of: regex), + self._ranges( + of: regex, + subjectBounds: startIndex.. { - let input: Substring + let input: String + let subjectBounds: Range + let searchBounds: Range let regex: Regex let startIndex: Index - init(base: Substring, regex: Regex) { - self.input = base + init( + input: String, + subjectBounds: Range, + searchBounds: Range, + regex: Regex + ) { + self.input = input + self.subjectBounds = subjectBounds + self.searchBounds = searchBounds self.regex = regex - self.startIndex = base.firstMatch(of: regex).map(Index.match) ?? .end + self.startIndex = (try? regex._firstMatch( + input, + subjectBounds: subjectBounds, + searchBounds: searchBounds)).map(Index.match) ?? .end } } @@ -241,12 +253,15 @@ extension RegexMatchesCollection: Sequence { } // `nextStart` is `nil` when iteration has completed - guard let start = nextStart else { + guard let start = nextStart, start <= base.searchBounds.upperBound else { return nil } // Otherwise, find the next match (if any) and compute `nextStart` - let match = try? base.regex.firstMatch(in: base.input[start...]) + let match = try? base.regex._firstMatch( + base.input, + subjectBounds: base.subjectBounds, + searchBounds: start..( of regex: R ) -> RegexMatchesCollection { - RegexMatchesCollection(base: self[...], regex: regex.regex) + RegexMatchesCollection( + input: self[...].base, + subjectBounds: startIndex.., + searchBounds: Range + ) -> Processor { + Processor( + program: program, + input: input, + subjectBounds: subjectBounds, + searchBounds: searchBounds, + matchMode: .partialFromFront, + isTracingEnabled: enableTracing) + } } extension Processor { diff --git a/Sources/_StringProcessing/Engine/Processor.swift b/Sources/_StringProcessing/Engine/Processor.swift index 8471ef861..8fa3716b9 100644 --- a/Sources/_StringProcessing/Engine/Processor.swift +++ b/Sources/_StringProcessing/Engine/Processor.swift @@ -28,18 +28,44 @@ struct Processor { typealias Input = String typealias Element = Input.Element + /// The base collection of the subject to search. + /// + /// Taken together, `input` and `subjectBounds` define the actual subject + /// of the search. `input` can be a "supersequence" of the subject, while + /// `input[subjectBounds]` is the logical entity that is being searched. let input: Input + + /// The bounds of the logical subject in `input`. + /// + /// `subjectBounds` represents the bounds of the string or substring that a + /// regex operation is invoked upon. Anchors like `^` and `.startOfSubject` + /// always use `subjectBounds` as their reference points, instead of + /// `input`'s boundaries or `searchBounds`. + /// + /// `subjectBounds` is always equal to or a subrange of + /// `input.startIndex.. + + /// The bounds within the subject for an individual search. + /// + /// `searchBounds` is equal to `subjectBounds` in some cases, but can be a + /// subrange when performing operations like searching for matches iteratively + /// or calling `str.replacing(_:with:subrange:)`. + /// + /// Anchors like `^` and `.startOfSubject` use `subjectBounds` instead of + /// `searchBounds`. The "start of matching" anchor `\G` uses `searchBounds` + /// as its starting point. + let searchBounds: Range + let matchMode: MatchMode let instructions: InstructionList // MARK: Resettable state - - // The subject bounds. - // - // FIXME: This also conflates search bounds too! - var bounds: Range - - // The current position in the subject + + /// The current search position while processing. + /// + /// `currentPosition` must always be in the range `subjectBounds` or equal + /// to `subjectBounds.upperBound`. var currentPosition: Position var controller: Controller @@ -56,53 +82,50 @@ struct Processor { var failureReason: Error? = nil - // MARK: Metrics, debugging, etc. var cycleCount = 0 var isTracingEnabled: Bool - } extension Processor { typealias Position = Input.Index - var start: Position { bounds.lowerBound } - var end: Position { bounds.upperBound } + var start: Position { searchBounds.lowerBound } + var end: Position { searchBounds.upperBound } } extension Processor { init( program: MEProgram, input: Input, - bounds: Range, + subjectBounds: Range, + searchBounds: Range, matchMode: MatchMode, isTracingEnabled: Bool ) { self.controller = Controller(pc: 0) self.instructions = program.instructions self.input = input - self.bounds = bounds + self.subjectBounds = subjectBounds + self.searchBounds = searchBounds self.matchMode = matchMode self.isTracingEnabled = isTracingEnabled - self.currentPosition = bounds.lowerBound + self.currentPosition = searchBounds.lowerBound - self.registers = Registers(program, bounds.upperBound) + // Initialize registers with end of search bounds + self.registers = Registers(program, searchBounds.upperBound) self.storedCaptures = Array( repeating: .init(), count: program.registerInfo.captures) _checkInvariants() } - - mutating func reset(searchBounds: Range) { - // FIXME: We currently conflate both subject bounds and search bounds - // This should just reset search bounds - self.bounds = searchBounds - self.currentPosition = self.bounds.lowerBound + mutating func reset(currentPosition: Position) { + self.currentPosition = currentPosition self.controller = Controller(pc: 0) - self.registers.reset(sentinel: bounds.upperBound) + self.registers.reset(sentinel: searchBounds.upperBound) self.savePoints.removeAll(keepingCapacity: true) self.callStack.removeAll(keepingCapacity: true) @@ -118,10 +141,12 @@ extension Processor { } func _checkInvariants() { - assert(end <= input.endIndex) - assert(start >= input.startIndex) - assert(currentPosition >= start) - assert(currentPosition <= end) + assert(searchBounds.lowerBound >= subjectBounds.lowerBound) + assert(searchBounds.upperBound <= subjectBounds.upperBound) + assert(subjectBounds.lowerBound >= input.startIndex) + assert(subjectBounds.upperBound <= input.endIndex) + assert(currentPosition >= searchBounds.lowerBound) + assert(currentPosition <= searchBounds.upperBound) } } @@ -129,7 +154,7 @@ extension Processor { var slice: Input.SubSequence { // TODO: Should we whole-scale switch to slices, or // does that depend on options for some anchors? - input[bounds] + input[searchBounds] } // Advance in our input, without any checks or failure signalling @@ -158,8 +183,8 @@ extension Processor { /// - Precondition: `bounds.contains(index) || index == bounds.upperBound` /// - Precondition: `index >= currentPosition` mutating func resume(at index: Input.Index) { - assert(index >= bounds.lowerBound) - assert(index <= bounds.upperBound) + assert(index >= searchBounds.lowerBound) + assert(index <= searchBounds.upperBound) assert(index >= currentPosition) currentPosition = index } @@ -230,7 +255,7 @@ extension Processor { switch (currentPosition, matchMode) { // When reaching the end of the match bounds or when we are only doing a // prefix match, transition to accept. - case (bounds.upperBound, _), (_, .partialFromFront): + case (searchBounds.upperBound, _), (_, .partialFromFront): state = .accept // When we are doing a full match but did not reach the end of the match @@ -341,9 +366,9 @@ extension Processor { case .consumeBy: let reg = payload.consumer - guard currentPosition < bounds.upperBound, + guard currentPosition < searchBounds.upperBound, let nextIndex = registers[reg]( - input, currentPosition..( _ input: String, - in inputRange: Range, + subjectBounds: Range, + searchBounds: Range, graphemeSemantic: Bool ) throws -> Regex.Match? { - var cpu = engine.makeProcessor( - input: input, bounds: inputRange, matchMode: .partialFromFront) + var cpu = engine.makeFirstMatchProcessor( + input: input, + subjectBounds: subjectBounds, + searchBounds: searchBounds) - var low = inputRange.lowerBound - let high = inputRange.upperBound + var low = searchBounds.lowerBound + let high = searchBounds.upperBound while true { if let m: Regex.Match = try _match( - input, in: low..( _ input: String, - in inputRange: Range, + in subjectBounds: Range, _ mode: MatchMode ) throws -> Regex.Match? { var cpu = engine.makeProcessor( - input: input, bounds: inputRange, matchMode: mode) - return try _match(input, in: inputRange, using: &cpu) + input: input, bounds: subjectBounds, matchMode: mode) + return try _match(input, from: subjectBounds.lowerBound, using: &cpu) } @available(SwiftStdlib 5.7, *) func _match( _ input: String, - in inputRange: Range, + from currentPosition: String.Index, using cpu: inout Processor ) throws -> Regex.Match? { + // FIXME: currentPosition is already encapsulated in cpu, don't pass in + // FIXME: cpu.consume() should return the matched range, not the upper bound guard let endIdx = cpu.consume() else { if let e = cpu.failureReason { throw e @@ -74,7 +79,7 @@ struct Executor { values: cpu.storedCaptures, referencedCaptureOffsets: engine.program.referencedCaptureOffsets) - let range = inputRange.lowerBound.., + in subjectBounds: Range, _ mode: MatchMode ) throws -> Regex.Match? { - try match(input, in: inputRange, mode) + try match(input, in: subjectBounds, mode) } } diff --git a/Sources/_StringProcessing/Regex/AnyRegexOutput.swift b/Sources/_StringProcessing/Regex/AnyRegexOutput.swift index 156dd7220..8f9d0e010 100644 --- a/Sources/_StringProcessing/Regex/AnyRegexOutput.swift +++ b/Sources/_StringProcessing/Regex/AnyRegexOutput.swift @@ -145,6 +145,10 @@ extension Regex where Output == AnyRegexOutput { public init(_ pattern: String) throws { self.init(ast: try parse(pattern, .semantic, .traditional)) } + + internal init(_ pattern: String, syntax: SyntaxOptions) throws { + self.init(ast: try parse(pattern, .semantic, syntax)) + } } @available(SwiftStdlib 5.7, *) diff --git a/Sources/_StringProcessing/Regex/Match.swift b/Sources/_StringProcessing/Regex/Match.swift index 7e4be5652..de63df036 100644 --- a/Sources/_StringProcessing/Regex/Match.swift +++ b/Sources/_StringProcessing/Regex/Match.swift @@ -126,21 +126,32 @@ extension Regex { func _match( _ input: String, - in inputRange: Range, + in subjectBounds: Range, mode: MatchMode = .wholeString ) throws -> Regex.Match? { let executor = Executor(program: regex.program.loweredProgram) - return try executor.match(input, in: inputRange, mode) + return try executor.match(input, in: subjectBounds, mode) } func _firstMatch( _ input: String, - in inputRange: Range + in subjectBounds: Range + ) throws -> Regex.Match? { + try _firstMatch(input, subjectBounds: subjectBounds, searchBounds: subjectBounds) + } + + func _firstMatch( + _ input: String, + subjectBounds: Range, + searchBounds: Range ) throws -> Regex.Match? { let executor = Executor(program: regex.program.loweredProgram) let graphemeSemantic = regex.initialOptions.semanticLevel == .graphemeCluster return try executor.firstMatch( - input, in: inputRange, graphemeSemantic: graphemeSemantic) + input, + subjectBounds: subjectBounds, + searchBounds: searchBounds, + graphemeSemantic: graphemeSemantic) } } diff --git a/Sources/_StringProcessing/_CharacterClassModel.swift b/Sources/_StringProcessing/_CharacterClassModel.swift index 352c267a5..db2088782 100644 --- a/Sources/_StringProcessing/_CharacterClassModel.swift +++ b/Sources/_StringProcessing/_CharacterClassModel.swift @@ -577,12 +577,12 @@ extension _CharacterClassModel { ) -> Bool { // FIXME: How should we handle bounds? // We probably need two concepts - if input.isEmpty { return false } - if pos == input.startIndex { + if bounds.isEmpty { return false } + if pos == bounds.lowerBound { return self.matches(in: input, at: pos, with: options) != nil } let priorIdx = input.index(before: pos) - if pos == input.endIndex { + if pos == bounds.upperBound { return self.matches(in: input, at: priorIdx, with: options) != nil } diff --git a/Tests/RegexBuilderTests/RegexDSLTests.swift b/Tests/RegexBuilderTests/RegexDSLTests.swift index fc31e575f..24d6f219e 100644 --- a/Tests/RegexBuilderTests/RegexDSLTests.swift +++ b/Tests/RegexBuilderTests/RegexDSLTests.swift @@ -18,6 +18,7 @@ class RegexDSLTests: XCTestCase { _ tests: (input: String, expectedCaptures: MatchType?)..., matchType: MatchType.Type, _ equivalence: (MatchType, MatchType) -> Bool, + xfail: Bool = false, file: StaticString = #file, line: UInt = #line, @RegexComponentBuilder _ content: () -> Content @@ -25,8 +26,13 @@ class RegexDSLTests: XCTestCase { let regex = content() for (input, maybeExpectedCaptures) in tests { let maybeMatch = input.wholeMatch(of: regex) - if let expectedCaptures = maybeExpectedCaptures { - let match = try XCTUnwrap(maybeMatch, file: file, line: line) + if let expectedCaptures = maybeExpectedCaptures, + let match = maybeMatch + { + if xfail { + XCTFail("Unexpectedly matched", file: file, line: line) + continue + } XCTAssertTrue( type(of: regex).RegexOutput.self == MatchType.self, """ @@ -39,7 +45,9 @@ class RegexDSLTests: XCTestCase { "'\(captures)' is not equal to the expected '\(expectedCaptures)'.", file: file, line: line) } else { - XCTAssertNil(maybeMatch, file: file, line: line) + if !xfail { + XCTAssertNil(maybeMatch, file: file, line: line) + } } } } @@ -525,6 +533,35 @@ class RegexDSLTests: XCTestCase { NegativeLookahead { "2" } CharacterClass.word } + + try _testDSLCaptures( + ("aaa", "aaa"), + ("\naaa", nil), + ("aaa\n", nil), + ("\naaa\n", nil), + matchType: Substring.self, ==) + { + Regex { + Anchor.startOfSubject + Repeat("a", count: 3) + Anchor.endOfSubject + }.anchorsMatchLineEndings() + } + + // FIXME: Anchor.start/endOfLine needs to always match line endings, + // even when the `anchorsMatchLineEndings()` option is turned off. + try _testDSLCaptures( + ("\naaa", "aaa"), + ("aaa\n", "aaa"), + ("\naaa\n", "aaa"), + matchType: Substring.self, ==, xfail: true) + { + Regex { + Anchor.startOfLine + Repeat("a", count: 3) + Anchor.endOfLine + } + } } func testNestedGroups() throws { diff --git a/Tests/RegexTests/MatchTests.swift b/Tests/RegexTests/MatchTests.swift index 35f8a9548..6bf7986ed 100644 --- a/Tests/RegexTests/MatchTests.swift +++ b/Tests/RegexTests/MatchTests.swift @@ -20,44 +20,17 @@ struct MatchError: Error { } } -extension Executor { - func _firstMatch( - _ regex: String, input: String, - syntax: SyntaxOptions = .traditional, - enableTracing: Bool = false - ) throws -> (match: Substring, captures: [Substring?]) { - // TODO: This should be a CollectionMatcher API to call... - // Consumer -> searcher algorithm - var start = input.startIndex - while true { - if let result = try! self.dynamicMatch( - input, - in: start.. (String, [String?]) { - var executor = try _compileRegex(regex, syntax) - executor.engine.enableTracing = enableTracing - let (str, caps) = try executor._firstMatch( - regex, input: input, enableTracing: enableTracing) - let capStrs = caps.map { $0 == nil ? nil : String($0!) } - return (String(str), capStrs) + let regex = try Regex(regexStr, syntax: syntax) + guard let result = try regex.firstMatch(in: input) else { + throw MatchError("match not found for \(regexStr) in \(input)") + } + let caps = result.output.slices(from: input) + return (String(input[result.range]), caps.map { $0.map(String.init) }) } // TODO: multiple-capture variant @@ -66,7 +39,6 @@ func flatCaptureTest( _ regex: String, _ tests: (input: String, expect: [String?]?)..., syntax: SyntaxOptions = .traditional, - enableTracing: Bool = false, dumpAST: Bool = false, xfail: Bool = false, file: StaticString = #file, @@ -77,8 +49,7 @@ func flatCaptureTest( guard var (_, caps) = try? _firstMatch( regex, input: test, - syntax: syntax, - enableTracing: enableTracing + syntax: syntax ) else { if expect == nil { continue @@ -162,8 +133,7 @@ func firstMatchTest( let (found, _) = try _firstMatch( regex, input: input, - syntax: syntax, - enableTracing: enableTracing) + syntax: syntax) if xfail { XCTAssertNotEqual(found, match, file: file, line: line) @@ -947,7 +917,7 @@ extension RegexTests { #"\d{3}(? Date: Wed, 22 Jun 2022 14:53:17 -0700 Subject: [PATCH 20/24] Add new benchmarks and benchmarker functionality (try 2) (#509) * [benchmark] Add no-capture version of grapheme breaking exercise * [benchmark] Add cross-engine benchmark helpers * [benchmark] Hangul Syllable finding benchmark * Add debug mode * Fix typo in css regex * Add HTML benchmark * Add email regex benchmarks * Add save/compare functionality to the benchmarker * Clean up compare and add cli flags * Make fixes * oops, remove some leftover code * Fix linux build issue + add cli option for specifying compare file * Add benchmarks Co-authored-by: Michael Ilseman --- Sources/RegexBenchmark/Benchmark.swift | 65 +- .../BenchmarkRegistration.swift | 22 + Sources/RegexBenchmark/BenchmarkRunner.swift | 208 +++ Sources/RegexBenchmark/CLI.swift | 46 +- Sources/RegexBenchmark/Debug.swift | 89 + Sources/RegexBenchmark/Inputs/Email.swift | 1004 +++++++++++ Sources/RegexBenchmark/Inputs/HTML.swift | 1531 +++++++++++++++++ Sources/RegexBenchmark/Suite/CssRegex.swift | 4 +- .../Suite/CustomCharacterClasses.swift | 57 + Sources/RegexBenchmark/Suite/EmailRegex.swift | 41 + Sources/RegexBenchmark/Suite/HtmlRegex.swift | 12 + Sources/RegexBenchmark/Utils/Time.swift | 34 +- Utils/createBenchmark.py | 61 + Utils/generateEmails.py | 24 + 14 files changed, 3106 insertions(+), 92 deletions(-) create mode 100644 Sources/RegexBenchmark/BenchmarkRegistration.swift create mode 100644 Sources/RegexBenchmark/BenchmarkRunner.swift create mode 100644 Sources/RegexBenchmark/Debug.swift create mode 100644 Sources/RegexBenchmark/Inputs/Email.swift create mode 100644 Sources/RegexBenchmark/Inputs/HTML.swift create mode 100644 Sources/RegexBenchmark/Suite/CustomCharacterClasses.swift create mode 100644 Sources/RegexBenchmark/Suite/EmailRegex.swift create mode 100644 Sources/RegexBenchmark/Suite/HtmlRegex.swift create mode 100644 Utils/createBenchmark.py create mode 100644 Utils/generateEmails.py diff --git a/Sources/RegexBenchmark/Benchmark.swift b/Sources/RegexBenchmark/Benchmark.swift index 76751f65e..53ee66e45 100644 --- a/Sources/RegexBenchmark/Benchmark.swift +++ b/Sources/RegexBenchmark/Benchmark.swift @@ -4,11 +4,12 @@ import Foundation public protocol RegexBenchmark { var name: String { get } func run() + func debug() } public struct Benchmark: RegexBenchmark { public let name: String - let regex: Regex + let regex: Regex let type: MatchType let target: String @@ -50,66 +51,6 @@ public struct NSBenchmark: RegexBenchmark { } } -public struct BenchmarkRunner { - // Register instances of Benchmark and run them - let suiteName: String - var suite: [any RegexBenchmark] - let samples: Int - - public init(_ suiteName: String) { - self.suiteName = suiteName - self.suite = [] - self.samples = 20 - } - - public init(_ suiteName: String, _ n: Int) { - self.suiteName = suiteName - self.suite = [] - self.samples = n - } - - public mutating func register(_ new: some RegexBenchmark) { - suite.append(new) - } - - func measure(benchmark: some RegexBenchmark) -> Time { - var times: [Time] = [] - - // initial run to make sure the regex has been compiled - benchmark.run() - - // fixme: use suspendingclock? - for _ in 0.. BenchmarkRunner { + var benchmark = BenchmarkRunner("RegexBench", samples, outputPath) + // -- start of registrations -- + benchmark.addReluctantQuant() + benchmark.addCSS() + benchmark.addNotFound() + benchmark.addGraphemeBreak() + benchmark.addHangulSyllable() + benchmark.addHTML() + benchmark.addEmail() + benchmark.addCustomCharacterClasses() + // -- end of registrations -- + return benchmark + } +} diff --git a/Sources/RegexBenchmark/BenchmarkRunner.swift b/Sources/RegexBenchmark/BenchmarkRunner.swift new file mode 100644 index 000000000..e0a315d0c --- /dev/null +++ b/Sources/RegexBenchmark/BenchmarkRunner.swift @@ -0,0 +1,208 @@ +import Foundation + +public struct BenchmarkRunner { + let suiteName: String + var suite: [any RegexBenchmark] = [] + + let samples: Int + var results: SuiteResult = SuiteResult() + + // Outputting + let startTime = Date() + let outputPath: String + + public init(_ suiteName: String, _ n: Int, _ outputPath: String) { + self.suiteName = suiteName + self.samples = n + self.outputPath = outputPath + } + + public mutating func register(_ new: some RegexBenchmark) { + suite.append(new) + } + + mutating func measure(benchmark: some RegexBenchmark) -> Time { + var times: [Time] = [] + + // initial run to make sure the regex has been compiled + // todo: measure compile times, or at least how much this first run + // differs from the later ones + benchmark.run() + + // fixme: use suspendingclock? + for _ in 0.. String { + return dateStyle.format(date) + } +#else + func format(_ date: Date) -> String { + return date.description + } +#endif + + var outputFolderUrl: URL { + let url = URL(fileURLWithPath: outputPath, isDirectory: true) + if !FileManager.default.fileExists(atPath: url.path) { + try! FileManager.default.createDirectory(atPath: url.path, withIntermediateDirectories: true) + } + return url + } + + public func save() throws { + let now = format(startTime) + let resultJsonUrl = outputFolderUrl.appendingPathComponent(now + "-result.json") + print("Saving result to \(resultJsonUrl.path)") + try results.save(to: resultJsonUrl) + } + + func fetchLatestResult() throws -> (String, SuiteResult) { +#if _runtime(_ObjC) + var pastResults: [Date: (String, SuiteResult)] = [:] + for resultFile in try FileManager.default.contentsOfDirectory( + at: outputFolderUrl, + includingPropertiesForKeys: nil + ) { + do { + let dateString = resultFile.lastPathComponent.replacingOccurrences( + of: "-result.json", + with: "") + let date = try dateStyle.parse(dateString) + let result = try SuiteResult.load(from: resultFile) + pastResults.updateValue((resultFile.lastPathComponent, result), forKey: date) + } catch { + print("Warning: Found invalid result file \(resultFile.lastPathComponent) in results directory, skipping") + } + } + + let sorted = pastResults + .sorted(by: {(kv1,kv2) in kv1.0 > kv2.0}) + return sorted[0].1 +#else + // corelibs-foundation lacks Date.FormatStyle entirely, so we don't have + // any way of parsing the dates. So use the filename sorting to pick out the + // latest one... this sucks + let items = try FileManager.default.contentsOfDirectory( + at: outputFolderUrl, + includingPropertiesForKeys: nil + ) + let resultFile = items[items.count - 1] + let pastResult = try SuiteResult.load(from: resultFile) + return (resultFile.lastPathComponent, pastResult) +#endif + } + + public func compare(against: String?) throws { + let compareFile: String + let compareResult: SuiteResult + + if let compareFilePath = against { + let compareFileURL = URL(fileURLWithPath: compareFilePath) + compareResult = try SuiteResult.load(from: compareFileURL) + compareFile = compareFileURL.lastPathComponent + } else { + (compareFile, compareResult) = try fetchLatestResult() + } + + let diff = results.compare(with: compareResult) + let regressions = diff.filter({(_, change) in change.seconds > 0}) + let improvements = diff.filter({(_, change) in change.seconds < 0}) + + print("Comparing against benchmark result file \(compareFile)") + print("=== Regressions ====================================================") + for item in regressions { + let oldVal = compareResult.results[item.key]! + let newVal = results.results[item.key]! + let percentage = item.value.seconds / oldVal.seconds + print("- \(item.key)\t\t\(newVal)\t\(oldVal)\t\(item.value)\t\((percentage * 100).rounded())%") + } + print("=== Improvements ====================================================") + for item in improvements { + let oldVal = compareResult.results[item.key]! + let newVal = results.results[item.key]! + let percentage = item.value.seconds / oldVal.seconds + print("- \(item.key)\t\t\(newVal)\t\(oldVal)\t\(item.value)\t\((percentage * 100).rounded())%") + } + } +} + +struct SuiteResult { + var results: [String: Time] = [:] + + public mutating func add(name: String, time: Time) { + results.updateValue(time, forKey: name) + } + + public func compare(with other: SuiteResult) -> [String: Time] { + var output: [String: Time] = [:] + for item in results { + if let otherVal = other.results[item.key] { + let diff = item.value - otherVal + if abs(100 * diff.seconds / otherVal.seconds) > 0.5 { + output.updateValue(diff, forKey: item.key) + } + } + } + return output + } +} + +extension SuiteResult: Codable { + public func save(to url: URL) throws { + let encoder = JSONEncoder() + let data = try encoder.encode(self) + try data.write(to: url, options: .atomic) + } + + public static func load(from url: URL) throws -> SuiteResult { + let decoder = JSONDecoder() + let data = try Data(contentsOf: url) + return try decoder.decode(SuiteResult.self, from: data) + } +} diff --git a/Sources/RegexBenchmark/CLI.swift b/Sources/RegexBenchmark/CLI.swift index bdac2e6c4..136ae0478 100644 --- a/Sources/RegexBenchmark/CLI.swift +++ b/Sources/RegexBenchmark/CLI.swift @@ -5,30 +5,46 @@ struct Runner: ParsableCommand { @Argument(help: "Names of benchmarks to run") var specificBenchmarks: [String] = [] - @Option(help: "Run only once for profiling purposes") + @Flag(help: "Run only once for profiling purposes") var profile = false @Option(help: "How many samples to collect for each benchmark") var samples = 20 - - func makeRunner() -> BenchmarkRunner { - var benchmark = BenchmarkRunner("RegexBench", samples) - benchmark.addReluctantQuant() - benchmark.addCSS() - benchmark.addNotFound() - benchmark.addGraphemeBreak() - benchmark.addHangulSyllable() - return benchmark - } + + @Flag(help: "Debug benchmark regexes") + var debug = false + + @Option(help: "Output folder") + var outputPath = "./results/" + + @Flag(help: "Should the results be saved") + var save = false + + @Flag(help: "Compare this result with the latest saved result") + var compare = false + + @Option(help: "The result file to compare against, if this flag is not set it will compare against the most recent result file") + var compareFile: String? + mutating func run() throws { - var runner = makeRunner() + var runner = BenchmarkRunner.makeRunner(samples, outputPath) + + // todo: regex based filter if !self.specificBenchmarks.isEmpty { runner.suite = runner.suite.filter { b in specificBenchmarks.contains(b.name) } } - if profile { - runner.profile() - } else { + switch (profile, debug) { + case (true, true): print("Cannot run both profile and debug") + case (true, false): runner.profile() + case (false, true): runner.debug() + case (false, false): runner.run() + if compare { + try runner.compare(against: compareFile) + } + if save { + try runner.save() + } } } } diff --git a/Sources/RegexBenchmark/Debug.swift b/Sources/RegexBenchmark/Debug.swift new file mode 100644 index 000000000..40bc83ef0 --- /dev/null +++ b/Sources/RegexBenchmark/Debug.swift @@ -0,0 +1,89 @@ +extension Benchmark { + public func debug() { + switch type { + case .whole: + let result = target.wholeMatch(of: regex) + if let match = result { + if match.0.count > 100 { + print("- Match: len = \(match.0.count)") + } else { + print("- Match: \(match.0)") + } + } else { + print("- Warning: No match found") + } + case .allMatches: + let results = target.matches(of: regex) + if results.isEmpty { + print("- Warning: No matches") + return + } + + print("- Total matches: \(results.count)") + if results.count > 10 { + print("# Too many matches, not printing") + return + } + + for match in results { + if match.0.count > 100 { + print("- Match: len = \(match.0.count)") + } else { + print("- Match: \(match.0)") + } + } + + case .first: + let result = target.firstMatch(of: regex) + if let match = result { + if match.0.count > 100 { + print("- Match: len = \(match.0.count)") + } else { + print("- Match: \(match.0)") + } + } else { + print("- Warning: No match found") + return + } + } + } +} + +extension NSBenchmark { + public func debug() { + switch type { + case .allMatches: + let results = regex.matches(in: target, range: range) + if results.isEmpty { + print("- Warning: No matches") + return + } + + print("- Total matches: \(results.count)") + if results.count > 10 { + print("# Too many matches, not printing") + return + } + + for m in results { + if m.range.length > 100 { + print("- Match: len = \(m.range.length)") + } else { + print("- Match: \(target[Range(m.range, in: target)!])") + } + } + case .first: + let result = regex.firstMatch(in: target, range: range) + if let match = result { + if match.range.length > 100 { + print("- Match: len = \(match.range.length)") + } else { + print("- Match: \(target[Range(match.range, in: target)!])") + } + } else { + print("- Warning: No match found") + return + } + } + } +} diff --git a/Sources/RegexBenchmark/Inputs/Email.swift b/Sources/RegexBenchmark/Inputs/Email.swift new file mode 100644 index 000000000..2fad0dfd6 --- /dev/null +++ b/Sources/RegexBenchmark/Inputs/Email.swift @@ -0,0 +1,1004 @@ +extension Inputs { + static let validEmails = """ +fm+ttuormdi0c6eali8zww_@quotrjspmigprbw6.mj54ay.lughvf +b3b4fgz627t-ux+qc_9g@k7-jdbh.ygvaodtqp +xuy0rbqj36+zmn8vugfifjthahobe4plc5p@5g.osxpnuaz +zqounc-1da2rejb4tgkmkcp9eo5lwisalmr3htxuyqn.vj6i0zxpdfbvs8_%y@8rcyhpg0dmzv2bmue9hjgisanbxueyffa4kj.7ct6kzrwv5oodiw1.abp +21dckx_bog%eu5hqyzesvkhap.+wrn4iqgl7t06wxlbojizmn3crj@x-5y02sajp9btvejnzk7h81ozqgaoffmwuhpydwgiec.zdcvnb +yghstniwp5sfqz7odkv41ovcl%tyfu0j-uw9lepxnamb8_e2xr+gk.@zovxp80wnifpga6rrcxuqy3u9mm2ln4vwozjdfg7htlbb1ed.y.oszmnljx +uzj9403tc2vfmwbesx1fg_@1srhque4cgvncf.dz6o0a7xllbtmqobf3nup5-vyzwmrk.ghrmfujdwo +uwahrr9wm08mnenecv-oq_k5dvj6aktb+ltlpfj1gczg2u.47z%qyphdxobissfy@nsa2wpphctj9ne1vt.7klrwbdxq.bgpkinu +rdy5g.kei0wnsup41zatar8ynj_3xmgo6fjucedhc@yjhk4vhxf2zremes0a6oc.gw +qbkogv8@jiaakcgc4fkjd-ynwfp86oetvzuxsml9ydqwirs70tmh2bxgv1.wrxj +%omj40dhfdjvb9kg572c@z9dcn.qkywfzgbp +iudied2_aps@vvx.r7boan3ilbdjyawiw-hk0rmsfqcgpot6sqnuylt52dpujkczzefx4hg18m.atcoyzk +vzxujqysik5mje4rzn2nsvi_6ubpgo.lywahcdeqpam@ua6ngmllcs2jpyvxi8nhdrrpbmf3f1.isqyamxefk +gs3etzrnimucoaofl5dkvj0jnx_.8whegfphmdp+@qb-sc4ez92dvft0.hv +y.phs1suot@mnm-fhlcqozivguee.cdvftnrz +szglm0q@tqwpif.gpycxn8.jdy +ccnfgl.ix8zruum1nz9oyja5sha7ekd0q%wfm_irvyetkd26btwjl@lhv.ymzrdci.xoay +srkzt@qavyruvsa6dbqji0n.heogmoclxfc5ksz.pba +ggpdi.nwqshfh%jvq8j2bkmxy@3u7bsgucntom6zcnjjppky-18qotw4l.s2xvyeifzw9xmde50qrrlh.mcju +buim4a7x9klnvb63eo20m5%eszhdxuk1v8crgttoq+yqcdjsjhip.wrfzg-plfn@.i4c29qsvntpbjfzu-tlaku05zlnprxf6bm7ivesoyhqmwr8gxgaowcedh1y3k.iuyparg +ttjzh%7oem4cb9aldge3pusrjmb-cyvkr+l.winqh6wkfguxfyq8xn_aopi1z@bg.f43u57mskzxe6tr.wf +uz.9%oyrqebjuobpxesiw@pvnwckihsy-ixu.lidgmwcth +z6@x3rj.bw7f0iey5cqrds-cseaoz4x1tptk2hfyuopnvgnkj6i.sdkojn +5ozhskev_i4@sdeoqpy8at6a2hkxwlfpnizugegqbtns3v1dhmjk.mnd +lfzxcqpux34w5hfojkmeqgtv%lzcep@w2zfkctwoyvikmrzoabyefj0pnxe4pqd9mnl.oekjmgnyiu +.0h6li5b+vrmxye_rfgz@d7wmjf21.kgncykbr.iahgyzcdub +uznigw_i+j6dypmaofqoa-wv5vflrt.1ec8jb@r8ke9kyu75ml6ox0dj3lj.tqszvyghzb41ivcd-ac2ehpwuspmrno.pejidunlsz +%0hsigp4w5ovdnbpvzq7-t3ae6@ogzjhbbtx1qjsrkvh8gmxsfir-edl5.mzw.xhqtug +ypvgy3zshmfc.dq5gh40xs98e1k2fznlt6icjdxbab_uutmnvpklo@mrjlte2.u-usytqbosak9zaeixf8py0hhwcvklndmnvcgxf3zrb.zkxitey +hg-xmpznojk+0kf%wzl1jgudq._vxmaqyhy6fub4c@bvkbzgotzxw4yl67y5mginqxm.prijuf12pnha.ymfnl +eupioqhcpanxqbjldbgu0wr+m%2x4f3vklyt69e1d7-vhr_smgtzsyj.nzki8ao@k1sdp9tedwbzryalognuxj4m.unhwlvqimj +iojz2xdpcbyjled4yn593mmgzcwiqhu6oruth@w1zajtxerrbw.pvim5-glohku30h47xajutpqymnzdf.gkyevmcjhw +btyjrxn3%psvp7kawlnkrz5qmumd+hhljfegui08zwvx1ogc6dt_i9bqs-.eocy@dk.ztvgyurf +8c5_@jyzda95fxikkev43ogn27jiusn1.iemkrhf +06scm3llffgo1miqoqnseteyp4wxc@irapdfnhlk2osxr61dzbu07xu9km8.kaovrswcf +alyhp1g@hgv0wvrtbucyek1enx689rq.fmjh +cbmtgzybuv%-7o@ytpjbx2dmi7s5cyzejxmzeh1if.txgjnpr +x-29@pjdyi5ce8g.mdaqfrpx +dslxp2e6v-s%zj+awu54fninvykh3qmqhcira9owb@o4b3ujsanekfbjd0r6apk9u1qqmlhevignzgzymsrw2y.tccfwpltx.ibgrlq +vin+7jjw86ns9qx_krcdcftwp-xmlzsr1e.upglqvko4tfuaeya5m@ranovpqa0hmhz7v8kt6fli5nzg2o.fyuq +bi-%8fady9vl.yfexmxlmcr7zn2pshou+ckjbtu@5u8akjboqp.og +j6fx9tskoz@ak0z72leiqdqr.zo +fnbqcvsqpa4ok3x6tjvtwer0fgd%z-agbh_ly.x1kum8lyrc2iihzo+m@x5qywp.pekq +cwekpzf%n1dj8kh9l3mibmn_45yv@mbkxai5efo84las-npcwwzy73pg0qsjlf6yrgtxjhm2kob.wrbxdhsi +z6ujxr0ijkvoa@45inoe.q0wkuhav9-lgw2qst3ryczjisdadgmcf6jmbpltzxubfo8n1eyvxp7krh.xkiwpohg +viptbsi3o6vqjdm2rkklm4nf8y57sfd1hxj_co+eacpqyg%luu@yzykjjhxmxmg0w5i8v.6d4nqepolu-7ig2fq1scbzkewfn.ebwn +pi.g-qovdlcbgteu9t@4sjd.cvm9gllezuhkrjqpytvug1iqr.uneqhovy +k9fz%xo@xpie0lzrj3ueqvmmon1gks-xyw7d.6uqlidvcf2awnfsrobtgc4.mecz +4kd2c1lda.ieknb3ftxtivlhw7w6jcsgsgbzo8y-xrfq0h_uvj5@e8z0lkh7i9d.fpkcxtwuqy +y2jiekdl+sowdm_gaph0rt5-7cuo.mqpzy3n4ewvztxhgkbb1csnf%fl98jux@prj8gub6zdieh1lqg0qx.apitfgjuqb +j0gbx7t456yfebucm9ingvceaztw-xrl@gjk5mbhpu9wsfetjopdy0hdque6yoingfz28vac-.kds +ynk5rfchzdauid12pwkzt.3nof+-0cj7vgl@j9doenig.ahbjpqslq7uzysmuwbcnvy3mtgfk6215xwzodivpktra40rch8xf-le.xpvfadshz +sb8udtha-sy6kmr@oxekzry-fpot0nl9cs1duai.bhfcl6k27gqjvg3tv.ptzksjwr +lmp_lw4b%-5r3btxjrs106ukuqym@u5q6mxoefbidqavh7.ibjpw +bedumcj+9a4gqlp.@nes.lsnhfv +meotdw0%vjgo6tk1xzufcwfnk7sv@xme-9sml4tqj1tdaqh2vwpa7yd.idjarzscl +zkigpeovxbsqzjw2nml3q1k%fue5hn_myhibtla68vjdfowd9-g+cytrr0as47xu@eags7eyjp.xpgvftuez +ezdvpglqx20a9bxh1h+muzfqkanwt_6w@vwclej8ukm.hrnopm1zdj7xasdgt.velt +wep+l9a4npa2ozv@mskvg5x.trunj2zafdcspbkmyjwhzieo98a-.tvsehczi +mevzjfww.sv1q0xhit4qn%dp8rk2oaa@yptogw74lznc50dvvqte3r1ayxjfb8l6m-bkrap.uzsdhh9xgicmjuinwoe.encv +r75bohsukql3wmativp9dnbz.tmyoka%p4rgj6dvcf1xsf0u-@ywrxuv0yorp8catg1bjoes2l4lfkzidp6j.qwfx-gnm.ahyzjcumxo +obf.h5rqjz73mz_ljkrixdepuvgb8hcncwymtepiay41ow%u@mp85ukrhep9wzxkofutqbwtl4oqa6.3i.cb +_nbkgyxeaomcoellxfs1j0wmqtg86bqsvhfd5rkz%dz.3p+pnuichv4u2ita7jy9@yqxchqd7xwvv-kzospfosn.nhow +7casc+ejzupx1nlu_i@inrnqpewta3yagho0bjhovc894m.xozvibuhy +18ya3fxdgs07qll@.4pjn7qsfl3mtivqmzaw-xtokocr6108yanbegzxrc.qd +lmwqnh5s19ghoqebsnupt0f_vxmp8ziyzckwj.6%2@lgyu5wxjib.vtnqtlzg1ixmd3w4nz2yv9bm-a7so0hcce8auqsh6effropjkkr.oy +hb4di1vjydxih8fxmgakzn5@-ued71giuklfvklopswwa92nhtcdmqvjh4iyxzmrpa3xbe.uwpbrmg +3wz7kbvh-ywn2dpc_udj@tocxu.l-sfbhc7hrvelinntg1ksfrjzupxqqp9oae85dkgi.uyfpjodrch +rl6ecjql2j9zawm5tofw8%iu@z-7lpexswtun.sekcirmbg +cx8jr1-leibwqbqv9730nhy@tjq-4hac9knfowegvkx6jpmu.lsalbbrzt23s0n8gwddum5iicoe1vyq7pzfhyx.krxth +jaq4p159x_bsylb30dtsoeiuyvpkamtfeic7n6dwzorg8q.njcwv%ml@n2vqkedpyxx5jat-hiuthslrlzgawg61o0fouenkv9m7bs8.4by3rzpiqjmwccd.mafup +1yxklbdgzfkii+5p43oahevlaqugyjmsc-jsxh28nt.uwmrnwf0bo7_69qp@lnadi5o90euwhlkvy27rqiypmrc.bynt +g5@w5xkf061lgdquvz-js4ewla7gdnoepchk8tjmnhob.nyhzra +7mvgkhtaxwwkhpyf9focce6uq8j2rbuizrnd5_gv.s-4a3%ndqxmip1+ostlel@k3nv0lpygipxwearwov7z1s.awlcejfgn +ql23ks1tdrxsxo6yabeq8aw@27sypp6eonramq54wjk8gwbbfh.ytz +.gnp06vmxtc+1%ocwv3ety@w67r4-yvl5abwmqctcvkofe1trzh30ueginfszpd.bsloearidj +wkthbecivpc8nhsidukr%zdmrlg9@zic8hjkkub-tauo7v0mspnxner9erfwi3sc..dc +zv@o9xm5q1bdfqc8n6c-bgfx3zsyrziyko4ka7.2wdvut0vejjthewrnihlapspug.docsmn +lxqpbvzuikvj+nhhsupwr61of3oe4%wms@fu29auzy.zu +g1jqf2nb.d7w6kxrgzkhnalsb9h3my8vlvcspeoujedutzm-ta+o%q_5f0@7pfw-hgoajxjrabgucrck.fx3deymmz5l9ves0.qzykdpejgw +tokidvdwjkzxhe2%nggcf53cmlzl0aena89owqu_utrrpb7-syfqsimbp1x6.jh@zyvhgbezp0kjcmyllj7m1o.cldsntixaw +xuzgnzrva%hj7_fqsdb3.t@qlpwdosf07m3np6r84bj.gfdozuj +o_z7+b2vicjndwqbx6ehpiwldkuear05p3v9sc@krlautjk.1bvxc8d25hq.htec +uky@leo.mwoisclxf +vtnufh%lpo56k._xdbsegct-ek1iyq@vwervbquidnbklwn0p3o.hdukp2j-9m7cigz6.seondqbw +3pctfhjn9rsw5acbmda+%jedi12kn4hu@wvclof2n.jekg793bjqmhqo5xchyzagxnputtmsdur4edwklf60pav8zi.gptwsiqkb +.tbnvymd6uk@ky9z6cougmw78.bedasv5q0clxefjb2ohdfn43jt-garlwpvikixtzrnyus.tkeig +izol9rugqpo-ccl@il9eo.tmovn57sf42dikgzcm.fumhlysia +6gftpt+n.o2vei9uq0eozgb4sa%kv-jlwd8crmjhhqfym3urx1pxl@jsfnxhilbjcz9uede-n3gw7otoary4qlk1apwhmzu6t.ygv.wovdthejlp +70jr5unjcdqvi.tlzgcfkp3tbvgeir6yw4hh%+lz-mpn1@xq2rlp.iqw +hn8jiwkcv-ob2dix4ypze5zfoy_+l%smxlbhr.9sgd6qenj3p1crkgmafv0a7wu@epmp7g.grvyt +yxwdmdeov5up1jhnabp6exkyfq8rzckr04loszl9@api1sim2p3re4azetcvb7ouj6x8yngqf5duwxvl0hjsd-mty.qhkzwnbc9.debrgx +lh-gdpjvweykby+ateo9v1ud%m3ks5cnquox_ih2z4l7qf6wasx0zp.m@gejqq6bua4xmynndts2.icckzhv0lgpvyi59m-afp7ohjk8otsrxwzb3lde.usm +yrfsqb106ienx-.8xumal3ov9jfagdcwe_it%tr2cpzu5yok+gzh@d6ax9hef5vjgw3.n8arsyxgf-obmb7ht0scpuvym.yckn +a.3o6izpihsd8t+5tlfmqj9plgxyoegwj7y_1nfcqsrueb%-ku@r6fdakps.emn +%o_sp1xw.6zyjennotejf8yxwmrdakf-2qgsbblhu9hd0ul@pvt.xyh.ln +dxzmhyawdyfoscqhv2nfgb3t16kjkjcln0r@tlfrx9-bjs0wl52evdu4ji1egpgc.bdvi +uehrri2dyshlftzj7@59kl1fqsfdm4dbaorytbst0uni2nzvwlz8.yxzcsqdrne +.wykh4oxwpstj8vg2uqf5cv9qesj0f7d1lb_nrhxki@1u5cwsl3.ni-zihecu90s6a4xthjnrogdqpytmvbwf7gz8b.rkz +ac@lv63lbhiqxrazjfp0snyw4mczy7oebftwd.kmjxdzulr +7ylz-9iku+0dgupjh.wfaqho8bnmx5s1rqnpmxewoa4bs@eoqnilma0hgrjhlv5xpofb7.tvdrsswc3uayztc2-eq4k.cd +cg_nzdm37uza5h4wd.ns+6ktcxubqlwe-jhfyyg8kioljiotf01bs%mr9vx@t6bgdrf0i3hk5.pxduz8fqvw-m9sxoz1svawlhgcjn.rzauxjed +92mya_bo7l4@z.2gl3hnjbul0ztpvkbcix6wogjdpfvr-edewrf9y1nmhsyukaq8a.xbaftgq +ixguw%6mx0pu1lw@.hgfm2ncllg906skv5fwyoyasa8qx7wdizprtexjqe-ptz1rjh4cu3ouimbdknv.vtonmk +z2u9yvhjlwadb8fxoer+c.d5i43qtcmgorez@isyuhurzl794otpjrda8zqegjw01ns5pxqdvoe.mf.yhiazvno +kg1yjbjvntdxm3hy@yjkeh2p4sxpzgsfdhcgixzl.cu.tgve +g+5s3fe1xqfazcqptpulvmbo8ojub0.dy7e2krgzcw_4hm9wxtan6%-kvd@kxi6fnjx05m1dq4o.mfdujr +vyrrskmh6nk.pi4vixxyz-@ymxvp5olai.feshz +6kkogcja+xmqd1wedwn7ejtl%sf3nh.y4yv_omgpzri9lq@wl7bgmziciw04-gabjqy.udr6ylpn3epxnoexd12aff8tjoutshrqvk9cvmk.lsiz +zpqe3g08viuo21k4xrywa.iobdf96bhejldmh@wksna0g4r7duhqfqixcvj3ekuzyb-2emopsal.lvcnfjpbyt1d5m86zt.bpcajfo +.cpb8m%dkzhjvjuqfw6o5t7dcl+sresr_pnw@zydw3bgl8p19c.kqibrnwy +6mvk7nabvne+%gurj@p-dciqmgjmkd6zurxl8pvg.jnw +k4eonbnrzv6d%e5hjacy1u-gs@ppw2it1n-qeyac8b5srws9gl.mpogs +nqalgyp0o.pc1-f%8m6_ercue+32sltdzutby@a.cu8kzr1vntpmoil9we0cn3qxtpgosrvbyfbkz2aul4sd6e.fqztrjxn +yjc8tza3mywrg0ilxk1d.xv_epwhobuo5hkfrmcf-piqdu6ln2jb9@u25e6jsw4ltequjnmzf.hrzd8x9cxpy3bq1wibavkonfkhdtgomci0.posn +d_rb-mx7+clqg2pq3kgeyljyewadni.otp5mnvzuor0ks4tcax8ivsh%@e0klsbyxmprrgqp6uyvfa.fs-iwz1t9bdheh42cmlojdngo35xcjzu7anikvq8t.rxli +varxvn4zkayblkmtcb7r6nomqiy+.hdfz9g2ww_puo3fi5qe1jhtsx%delpj-g@hqe4k.b3pea5a6tv2-zccpuw.tojznryu ++qfmeq.rz1px6g@v-kiyt8o1erclpkms57wyu90ffd.x6c3ivhxjtnpumq2jowzbbh.qczjubdf +i8v71@vyem1spdtw5zxhellp9hgu6ucdawbj.finrrva0xq7kq4og-ms.tf +1ulkdv%mzb7cezrpjb2vphjnk-maq6sf8oinc.+3f9iwdq_4egwoyx@ym4ixwojbwllenrng-x0v3cistykf97kvfjpudsa1tou.rhzea86qzbqgd.gt +ln5kfv7iv2.rfz4b-tdxsz+mdu9mog8jselekuhtwh0@aszubpq34ldvhw8laxxbfkyfjqhukccnt1d7grmzin.dgcx +ge.y4b5wvrn0lkcz-ot1@un1yhl0.ve +gib2bi+u-@zdap7ixue.tsrd +bcl3@y94ctwlzvti3akl1erdjsoey75br.um +1eb3yoqdjqm9dab0vmkzsoy+wftw4f@3valtq.jchzk +smr+plnz.t_5yehmsde3ck4kodaxxl-afjo6wu70bvutwcnp9hjgbrqi18g@.zdayos4g9z6b7fh12gik0qoqpnd-vxmwkbsjhvyt.qkeypau +bxpj_hro1b+dp%dcigvaqu70@hg3v.bjiueqyckua8lopnb6w5fmcmdvx1sl.wdyjnoepv +xz-o5bgdn3tk9@8gortjld1ia.bwypfaox4szpc3v0yfcmejrkz7q-5m9xghdh.varpeu +1f5ausho-trn+.bzxnr@7yezds4ypr.bqhm12ugxs3eh.ywcm +nzch+dg@6uwzejgrsi7m2x4.1cykhqqxvavpwskja-blt83nd0hozcyeifpdm5lgrn9ftbo.dmbwnpa +50lug.vow8dlj@gms-jiklg6b4vdzy9rtofxcwprv.rnd +nq4esswjgu+5a@hvckiru5p.mjqeuy +ncp4axqhn0g@jxuj2qpfhtak8y-vsieerryvafgmbzxd3no01slz49w6bpotm.hxcil +xnmjthjdxfzdvurrtlkuiazgcev7bb+fisp96hoq@gnlmmdxyfaie6woa1x.gq-2trpfyjj50hseszhb.qwo +im3u10aj.s7ex8j2ybplgzewpg%-+irhokr4f9chud6qvy_@hzsa1np6jma0in92y5ftfpegworebxviqcbwschml.ikum +-mlvrlnawqu@xuvx1dicb4znzjmdple752.aorx +67nnw5dk1ug+-bq.s43iw8fzmqr0vpaf9rkzioybmuxt_vp2@-i.m0tdz45lcfag3m1nyejk7vu8rkha6zqycbwvtojh2roipbg9.yefdx +a9qyuf%@lc8uj60tpip4.zhixprk ++satl2bxm8helc_qiwgodenmrjx4duv3.@yn3-cv6ttrysgrmeojaw0qhx81lje5ikzuz.mgpwhflq +23b-c.96qinozs4br%d0y7gptzmflamivvxuns_joqdaegh8rlcep5kwwj+uhy@7gx5ln.odbmh +-%fq59mfwqji0.khnncvil_6u4wdrszk+btmjzhebloaytxro3@grxji3lz4tx7cb68vnyzc9ntsa-5ke1ywpqglriafbqpumuh2hvfs.fhpdaxyjet +ijfcajav2zlx9qeg8o5y4is1m@ikr3nh1expzcukiuaat6zq.o5q7el.yvsklp +u9t4k_ygjtb+hx3pke@ue2lgqyn3snqduh5ywhbm.svfjbx +iykzz.re%l_gk7wsmavg@r2psop.0t4xkcfekdrbmenhz-5.bnfrd +xiyzflg4etd3p2q9u8-+j6fls1bqzykns5tjcewnxvopogr@qgcrshfokaicnxzut8sf23yh.t.oqc +noy-ggtrm5zwqj6kuch93to+4_l1dx7enai%wqmplrysejh0bcvs8.2dufxkpb@hqezfxv5.yabnt +8+4vw@4bvil8r9hvgdp-7qu3mke.oyauvkj +vc7qjmjstbg310d5hm8kupvoefzs6n4pc.etni-yo+r9@j0yzbetuczdm53sxyxctgko196h-akmdlhwp7rqgfnnf.xivnazgru +d2botljfcgrhq4.vailpo7en8sik1uqr6x5vpjhtbwuezxcs-y9z@nqxf8iag.jno +n3ai9pk8zr%yxrwm.f2cgecigl5t_ab74uoowjz@4tplorsxjqdiklmbfzv.uwyzuecvc2kftgdsm13rhjyaxa7bwp-6gi8qo0ne.uw +ega9mzghfqp1cdlyvs.%pv-zel+uk3c@b5klunkidecspqr8twh1yhvfabwzg3rjoislo6d7pzv9jn-q.pmbsqhnive +pn2%_7y5.+yvf@.ch0mtn9telj6-vz4nqs.uc +5u0qg4klbxzv7dvrbi8tynd%ck3fzxj-aqmwtiguh9+.nh6s@suvfhip4thrsneqdrx0avm7fnb31ypoizggd89l5btl.a-zwxukk6oeq2.ivh +yzfik-al@h6igjcnkwbakjzofwvmg.7rsqfedo3uvzeqhdppyx8txl9i.hnwkfesrvx +cdhtwrbmgvi4e_6n7fbtlxugcme%q+xofkwl8n-0.ijvz2r@ar60fvrbjfsgqwplz21ilaovhx9swo4d.gitebkpjktmymq-dnhcyu57ecn8.jd +lxghcfohpdt98zuk6b4xm+sj@s05gzatlpvwiyyu963.xvehsawgnl +j6p%x3ym+4yzriocabkfidoe8z_lan-e9nds1hk5hxsuwwtpgqbt.2qv@.jldwmtqpxs8cyqzgde6ahhank7rzg1senoly4k0-.wnxcdytk +yrgkd_nfpce0manexhy@fp.ru0qb9qozewz8flimwtygxno.wh +ibj1hhlxxs.r%qjbg0_dwvwfaigzy7lq5e+rpc2smpvnceot8y6kkma4u@3b7pl2d5hs1ktdmygqeoweoqhxrxcibajamrjvunf6nsy.vsljxfzmp +phtylkxkjeaylfn.73u1d4uhobr6cfgpm5+rw-g%92izit@hfbgmxipkf9v2cyoajonswyzhlv4rp37mw6xz.cttiqq1jk.xnmeh +zxeknoahbkfyp93.iyij80%ucsaovwx5@z1xwpyzn8-ymp4d3ix.2w9dkqftscqcl6sgh0gteuaaflmn5bkirhbvouj7rv.aurmf +hf1bptjxs7r@.jfaladhy4idi2e7ozywksgqhemwcvrx8v-0ptbctfns9knzbqg3l6j1rxmp.ao +kcewjl@mlnx90cyd.ozsjwahmci +0g1j64rkzgtul_-enn7ffwsdocblkijhv2arwm.meayivz8ocyqtxx%b@ezwddnbo.mav-qoy.deqcywhjm +fv%rlpbyat+woc3.nixixufhcz4jmue_h7q2md9p-vb6sl0@lsrpfe.ktwex +ec+068wjnsfmu9yncorgxirevq7h4lstzauhwybdgk.3xozfp-v1dbj@3lkyztxo.tl +gtswod0_sy6%.9r4iqevmamj3w7@iaosmz9.ywarlzgrjcxffewj30kthbdgvtmdcsb6v1h-qiqlnopu84e5n7y.fzpkwdbeov +l-ate0@ituj3.imnyr +pyhxjf4unk8d@78yejtdvikxlonqlpzfvui-m1tzespahn3rkmbd92uabq.y0cgh6s5rw4gwfc.ubldo +o%gs+vttcopir6lie1kdjaf5nm7vfk304jyexymlbz-9gwrbapuhxh.@umqyscxwo2-0xjeav.tivywhc +tnroykgza3log7kbzse90wixpbc-1.5@6vtyx74v5kneyzfcidjorch8mq9tllwa-eb1sfsxqdzbao.nz +kokfltm.p8y4sqisdpe7nv3-a5taxfe1+jhruq2nducblc09yg%gz@y.ntx-h2sesfxznewjkhotvumybk9p6zqpq.ofulinbct +b0gdrmycehqp@xw3eu.nib4hdg-a1yymdwvlzu9rtvrlcffs6xqbct05gkkpo7.fcupwj +0nds.k_wkt625bls9-3ry4qg+uehdjoxelxoyfv8hpfpc@gduqlpefxczxj1sngzsa7wkir8vf32hocpi6akjt-hbqyrol.u.xyglinjce +nsxu1yh_cg6vy7npimb5zwlj+o2wjfo0ddtazmsbhugxkqfikeacer8l4v.@l76a28bsji0hcebkpcjw9ns-r4lzmggxmi.xvdfa.dykgm +t%mf@gt2i4vrc6lel.nkl +eamglspi2-0oo3zquh9uhlfikb5xcaejbrvnt@jyuqpga7i6n1v3jxczoemsuxpa-db..fgdeqsz +8nfdnaupoeci_ydb1j6olf75ygimzq3vxjgkwv9kwx2z-erlmhbhscurtta4.+p@gnmb3fcjwk-pqsa17tixbqeartdlzdvmo0.rtqzlw +d8stzx1pv0odouzib2wwk.etmf%yn+pcm@kjxecbs.f.rold +ytnrxa5tyrdg+vp6cebiq1s9ioz@1m7egpoqpc9tkbis4coymzijrltayf6f2xhveqdsxhlw.mnylbdgpre +jtne5%3psw-rrxmi9ydlpzlgg1okvm4adhuc6tv7@xd5kelusa0qnhka9ygbn1odirzx.bw8rhzwo.phbla +zpq_oxl5oz3fm@6uektv.b1apg8hkwe95dlsvqcjuyphmnfdbil7wzxrmyzjr.nebrvloitm +gtl+zvjavshzbx4kt_qcufpqc5iyi2odnkxgmr9radjb.8lh60f@t5qulef.xdaao32jge7gcyvbvw8iscnpbqhix4zufd1mknsyhorlzwmjkp690-.ivomxwerc +_lp1kbzfuvrickrscf5zvp320yb+s8amddjnewthnqawo-g7iux%4hx9e@lcmsioxvh7lifj9bwd5rwa0yg4xyq3bcaueemputhptdngkjo.f.wo +bfogd4aucm5elvftxe29ayrcktqo6jz3hh80r@2vu.yr +u%n_fgvcahveinl6oyt@jv3l4rdygzsqoyn5unre17k6gawc8fshbuvqpbwpai-dm0okj9c.qdfhrtcve +igejtp3k%ko_fh9.l4xlndwuiy8+sthcus7yoz2z1avqfxr6r0m@hgnrtdzpc2mjiio3jpk0scwmunqeoztxlda.dvnhjzuyrm +d01hedm6xcy8aow_pio4+atqmu@gusxlcqek5ofd4e6iwdnyz9r1zu0jqfhogb8rw.vnm3.rkntlv +_dovmxhu+p29hcqaswzvy7ksynkjrmgar8g0f5t6b1lc3qjbox@bvrwkibn9jcf.1s-xfaemqdz0n7c.gpt +cwo5mxs8-6ede7q+r1l9jvjyhigp4%rfon2qcazz_ad.whlp@zkkvl1bbwm3edirsoahfu52u0ipoa4egrc9jyylx7x-gv.wyvx +2c06zq@vbw7dnmw.lrbjg +rsk+vhf.xiutmpgvqs1epj3j7cx9uacah56ly8zqmd%-odeli0w2r_n@oojzahmdtj2exqv81blsphy.qpbxedkvkr5tsmnw.icaprjydgz +ed.7mb@cdw0jvcoaxzrxz2nok4eb3-qb19lvh7pflmqytmi.sh +tcc4g9ipd@fcl7akiyjn.x3go6-pz.hqza +twua.mvfn4kjvplehzgdjlr5zu7y36m_2ceick%x@jn6llsqgipvieom23oh18pxba4zcyb9nawydc-qfz.tclhzfedsi +gol9x0cvejq-fm861%dg+@nbkxmk.af +enj08cltjy%r@plhn91.jcdufgqlmw +yfot-bfudl1.yessh5@hog4pqriyzkczljqdfuvm.e.mtlsca +1hu354v.6xmkpa8qq_ygth0cognw7fci+ul2meitz@w0cebflt-hon52bk.ozmfpw +j9r-r+8ilympgo4xnm3sufcso21._wkhz0dlvhb%qtit@1i5zxp4l.gbu8e0v9tatjziodhbfww2-aypccgl6vjr.ncsm +qss0hyolkrixmg13xrpcq_tjwadnwo.7u@vqpnx9hm6fdfjaidyeur4.w-klypejbq7tcchux3s5tr8o0wgnksovlmbi.cojitzby +hp2qr0duuwhsnozzlf8vol%c16iatp4ejrg5jvfm3ayn@i8tzzywdhfcx5arqilpynms4hw2cmgdtalj9uvxevobkfbp-07re3sugq.n1ok6j.yubic +ubm31z40%sml.nwihqkkqjtty_@0u4dv3.lwarmue +rcxd@esatxk-ghnpxzzy0qt6wbauwm2sojfrlk7j.udonlv +smqvgq.ahp-vneztozjf@zvqogkuecgmkoxc-tmlj.yvuab.ajwolv +p2b6dnvpdwgszfetq48y_z39mgl.+wkh1b5cuj0f@uccioig4o.sln-1n2xe.xnwviocrme +3kqawxms@1adu5hxmqbrcg0pei6gattlmj7jovzfnifowb3-xp.dy2lck.xprofkgm +4bj102f+lhz3rsqfvgpn_9qio8m7cu%y6t@sohyf27qihemb0tilgky1xdwctvvb.gtikxhzwle +_xbj6zipcerm0%kag.wqenw-ohquidsvolz78l1+ck@obokbvvniedl4gpe.cdsbujiv +dizrkhp82we-4wntcx3es9cgqunm1bgily.afz+aqv5yo76@gmra.imhzvtqd +ts-9rnofgyqsi7xkh%ulkbzavpftbc8uw15njqeexm.z2g_j3o6mw@ii2tjg7.yycnsn53o6hvezpqf.xoygni +agq5frtjy.hmv0ghectjillr7au+kdw_o%fn68zm@wvabfqxqe8h.rhlfutsc1uio.wlzejcmf +hgscni1pi@eogtgxlnaraz2mv-bqzyqrp9uwi81dv7.agivdbc +ogpcxu-bmhlfytq.zex6e8o3njdr1q+iu20@yr3v4d-9.kjixzeuh +a2kng-q9oixlbxcybsf0_6ezgwvph.y3cavpfj1rdh5mmj47wnodtzieu%ul@geqiulmxo.vgznkjrt +9.pi%dsbzavrnc6vu17-q_oafxkp@k0cm-i2lrtylbsfhhyi95nnqo3tjuzqeo4m1wdra.qxejkm +j5.87ukmzisxpsh1odrflw_423tvpgyovir%9xtfa+qemkgqnnzbwhl@xcm-siaw3dlh5kedv6sqjzn2x90vfnf18yr4qb7uteorc.tplzjbgihupmkgw.vbdjeuxf +nb72gdlp3-h_asa0zmnopfvygv4qtrw1orudlc6myies%w@di8b1vczxmxyg70.3pkfbarz96s-hrjswh2pfvgeloqnuqalojw5dkt4ymuien.fpsyzcgoa +kfgmtrvrpc%-sudjkiyzxqeax23@59klvuf.zjmnri2omskqvoqwbtg6nya8sc4heu-cdrbf1ld.lve +au+ce2lwwa7tsbo1.yflvhnm%ioujdr5qiqb68kj@bgjkynfpmcljxeoapuvzzs-r8utbaf0lxhkqmig.xt +yvowuzpy1gcfe.i9zafdob_nt4erkpvj-cnxurkqma+3wsl%2qg67bdtm08i5hx@pcl1rvsu5krf0jclxdaeojytzqhnbdv6-i2emm.iy +kvw._sftu91lrl8qo70y4dym52xd@0skmnhpc9efizxuoqwkcva18vinmtwgr.je3-o6yqbfyazs7dh2trbjdxu54.qla +ishlzoks5_fkhqxvuj0-z412fgg%9obny8m.lqe7jenydauridvtw6bwa3trp@hkg2b-5gi.hlx +bwj%vv1prcii_oajzcsd6mn3@mjadl8m67vsqw9-2xhdvu.kwrtogi0tefey4z1irlpb.fv +9f3@n1igtc6qwdley5snv.kw +v4xvu31red6xgm@t7qzy3ebfurm-kgbfltq1c.ya +zdmh.ahsi5grlktqpr9x6ujfdn%pkvgy-u_81cvoxiafcqem70btyn+w@ho6tadutlbmjvmye.nz39nwjxupgyhf20aik14wcexqplkss.yqkjmnir +x%fthg8+np_zianz4jx3@b6hwgpwgokfaz12jnlzkdfuedrxt9.iioxlp0yucjavqm4-nsr5e3y8svm7bcht.hc +1-6le@3ymbbh-avwrtucapnozm1lc25lw4qjgsx.edqhgskap +btfhlvsi_gq0okxee6jkxbn.u5oq4slz7wjv9dfp8uryta3c-pd%azm1hwc@kpaduqbmoeisdxpvvrjuy7bth.yfsrnj8eck.sowkxbfir +ofvv_jsribhbgtgnou0+lipe7.da8m5z9s2c1wk-ywxklzdpq6qnrjau@baglz86-frcmtseuraf9hbiun0vy1tzpjioj5onkw.72dqwpxsmglqev.ag +318chhjs_mcmu56awrjz4p.sliu2b+vbdfentalvgpok%97i@wod837ip9i2jo4pcj-flu.bs6.cvrdkmz +knkjjh+xbr3068bedt1ywqfvuvah9pe.qs5zgrg%m-7dzc_yipa2xols4tc@emuqdl495zhiwv.tbha +lplsugpxdxi3scbr4w9vwhk6yqvu710fzthatb.d@wnpswhepdbrv5symnocfzfz0.oq8gic.vesaqwlnp +zkrz95pix2f.8bmcpc%qontwd4-ayqe7xtlf1@im761ynwaxmeje0slkzozki2uspcgq.trf.sdj ++w%ac8hhr62e0v7o5up@di-urt.psxcfg8nqhf5owbvlhuyjak1tkm34dzwx.hgvouelk +civ@vuet6vdxwdozi-sepbycgh97qk3j4unapjalszhi0forrkcxmqmw.81.pljo +fvh3174-+2tmbughnid%tyejklgjzpa8d0xqlvrouaicf5bsqsexozp.cr@nghwn43ehr0g2djctsza.rb +f6bol+@is-nf7z6d.vajrdtqifn +wrv6mueiimu8fkxgf0cz%b7e1tdajcqpl+y2h.@xca1xeoenhm36w79uoqjmht4zki8qag.5-wrplubnyflkdisfsgybv20rcd.zw +rn7hzpcfo_lbdk+qfavgryjhc8ms.-b%uz1wq4@ccehiolptuj0xb5yryrl42szsg6f8kmjm13k79.v.ytjfw +wd5se7gab@ck9pwo.g71aezwfj48updbot25mvdh6tnqsxm3yjex-rirubs0va.wtjgihca +oej8up0sxd.2ahsylczlfr_3gmvivbnet-9gpmrtw+7ycojd@cul.fybaqc +jn@.zt4yveica.jf +w%g8xtchfxdak.ny+u7yqu@pa.kkb-zyci6jfxlbsyrj.mhb +xdqbal0brj5utokish3f+1f_yz-9oalmikwh47u8mej.evpd6y@8p.kc4kjyaacunqej2r3f0b.urmh +n0jyz1ctlq8hftpwdjpw7oslr5eeiravkond4axm+g.xzi@r7viaxloit-opmhzryd1j2cql85zsnpnvbafw.03q4gkxf6.bh +afdcljc9bivw30xdmhge4r+xuyo7oklzsnj_5.hr%nvgtbqz2up6a@trlxcmojbg2ul9fdsvghiseeytcqy5vmkipk34wuj0a.bynciqfs +cdtub1rbdmmruhy-48sot2_vofnvw6xfkecsk73lh9j5l+jq@h5cmgxonxqizvqfk.dykesebgp1nc43jrriy8pobujt2.ouyx +nt7zegrgjscufcxvq1q-.yiwmylfrojp+v3k5%pzm4bkdoahd8@mykjnf1v.qmwzkegy +pey5sfalnledcc1b_qrk@5hpxe0jqzhmdifypxmkbrlrb6k8l1zi-gcv4scwwtqaa9.rbm +qnhptmi7a_dzzgojc4ksxx1e8m6yw2sicuqu-fk.rtv+fpgnyvj0rbohewba%@kco5.ducxpby +stacw1q8b05ci9-t%+kbkuzglgesfmy_jdywahx3rvpqrnj2filunoh4d@afxo86anep0in-2lumgwuxj7h4mw3rvqpiqyz.gufbshtw +hb2aokwtrzxnvvfrdxye5ycmtu84kl97lieh@a74cjf063m5xugdz9td8hpbmivob2.yozfkblea +ngl@ttwlvjmypbyf.hzdekrcx2am6zkowu-r3l.urayfi +qhf-ubtpmfotj4u5hpszdgc29wm1gzki6r7rs.oejvbwcvia@qxji6ys1dkb2f4cdk0vpmth9bn.qvpal38wae-rhxsglzyujne.phoklr +ez_xbrq0vhh7ban1jeqzyicrmn65492xswk@itdp40q73.habusgnx9y2rsxcgvkzvquwkbacjrmo1tlwyi5jeo.vm +pgit%akkzyvhmbq75-fiul8z.2ee1uvr0+xn_ljorhyc4qd9sxpww3fcjgdob6m@swdqn-gvhs1jr.rlynjt +czgkacub4y9axv0dwen@duhltl1crnxjm0idkyjapvfs5m.jnhmsvua +von2lctyt48w6mgra1kvheinchp-u9%jpqdfuqox@g2utk3zpolnd47if5ke8ibr.ww9lgjy6xohcvvsf.jwcgbsxft +l2i5hdfvpyp6abos7j9fqc%g8xtwweedcghosvnzzq-t._l1ny@9vfcdju7ft6qhmxqkcb8yi.jqlgyvbmu +z4f2wzey%swqimxpkvyjbuegolr5_g9rh.fhkn7m8isaubj+v3xc@kzgyihsoqb9xhu3-b.ejuodm267cktwpq1lvvprfxc.jzbcufxs +rbhw8up4vwtkmzl_drp63zhyt.ofbis5eovaxafmjxe0jl9@kz.buhdpoikcj +qtyymijnvew@1nv6siuufmbhhseyvc3xtwcwa9z2d8qr7a0lepfpj.ldk-ogrjim5ykogqt4z.vfbekmq +9pc3uxzaa4dru5j6n@f5phqcaa.xjawrce +zmsvz67mgw+a5tnbc2d3qipj_e4p0uelcxrkoj1thwyqy.fnsi-a@owgp4kmsiof1lhlkihuyrzawv2edsx9ycnvpqf5mrt6.sflizkpo +zut%ozko-hlbs0aiqvtnfxdbp+.dn7pyls5jkevgc4cqm3u9xa1@cb9wfp2oc8o3kzdz.qgnitsjx0hnrurdsejymh4gkw.burj +jvryzenifpyd7apo_klgnx6zw%dtvlmbqc2qjwh8-5sr4th+sk1m9fx.ue0oc@0hlt5j7juziavcgn.huqbtr +vbqbiz4k1tf@v7j3irhkdafx-tx.pycwbsuy05t9f2ckqozjusv6rd1l.vaezbws +f-8+s@sbwejfuzw-hi0cplskmyhfdgjet42.fwvnlkjxbp +du8glxwcnvtmox7_okpkjyhdifwy2@ovjp9f.tnzdunrxkehak-q3clv6gbw1qsl70i8tmydozpbeyisw54fhmjg.ijxdzlgq +j9l@wlmpdmeg5bcqugaajbj2vyorth96xsldfvx-pkezsqt43.tqwg +ohmujid8p59xzcdsng+hzb3wat.7cy6yvqtr2a@q8smvtjogepoi2d3jhq6a1wuiarcr.cm +nlfdvv.2oeqsz1ra0hai3b4r7n-xkwfpkjtcyoz_siqeul@2x.tsz9uwrgohfltrnaebpbsmp3ug1cdykioxmhjv06wiyql5jaz8n4.usk +0+tewyqj-u.oauozqy34mphi_dcpwb5k1jc@xvuexy08not3mjaujn5gmi.yq1qtshca.aib +xd+tc2ks%1zrvqc-5frb8dv0_iukoaupjmlaf4tjyhy6n7gegws@.dlrvwd.pvfrwthdcb +yr7m@pfgmtuzk8cngat96e1ks2rlmuobx4en5hjqbhyw.ipvwc0js.trqu +qjfndza57s_1reo9ezuydqya2hs8ixpgxrgvkktwccm0oj%ltb3iw4hfu.p@c.ea8.kepqvrtya +q7nhyqvbate2emghmvocftppjszi.k40%nwz5-bl1a9sycko@zqsiju1x.nerbpx +e.wjrcxhp@kbm08xp.zwe5k3fvd1i6nhyaitnd-yqcu.mvz +7dl%roymi86hw+dxa2cvw9jn3stoc5_ebmup@dv.c5akw2n4aqk-zh8lnofceoigvzqb0f17.zqolsbmw +vt1ocheazih@2w-myjdnetskrqzbhtfcgvz15laurpidjapq4n9uo.khmocl.yfkn +o0jxs3pamiteglkauu86@agz2zows1xq-tvc6omym7pvcwi.ll8eubjt0ufgrnsyhkbqn9erpi5kf3hd4xj.ejkxd +yw4btvguzd7rmdqo9esrlfbp-hz3_6tvxcjjikai%u+g.e8f@5qop71wxfczmqeobry3ga-mbprkvti8lnhvieyh9uuzc.ejnvpqoz +lwyoza4ewjijtr+lc57tgdp-s@ycrtsth1ixidgv-jwa8zklcugesfj296flndrqmovpuh5.jnfqobliku +vkuqz7p@vommpqd-7krw65iw2.acqxdfktv +st+2vecrnikoxm1hfy@4ucpsjv28ffkr.mpcaot +zsdwpr8ennpa@s05huiwf93sazpkfhk8vgqpy2t1gdbaxmz7llitnqjrnomwj.y6c4b-cvoderxu.un +hwzjpbocokit-k69dbu7xlxvzlqg3rra%c4sdehun0f+atqms5ivjey28fyg@.dojhpt5-bcf02s843xpqqajuz1mgsk9dvturlymielyah6vnzcnb.dmjoaygt +eorzuanonigfak50u6kr3hxt+eg4bw2cqp8dbxy%scsjzm@kacdm1-bar.y.akecq +ompzjujxpaecudyc3xf6.gklrzsbq5i9f2htlt_o%ng@zmg0noxq2pidsg.rqrstkw5pvcftuhlvc64a-7ljfyei3a8yzxwhenb9bk.ahrnblfjmz +drbjh%lkc_.f9q0gmz3n+bvuy8nu2dptxmic-sogsqkjaf7oe6e5iazrwt4@pcxs87q9zoywlm5eadyncgf2-lxkjdg6hutb..peomd +nvxn7krsp2xabhoue3o96+qzfd.8bi1pm-ugwglfwylt@xcjw9dzvv4hinyb5f2aa.yhmkrjboze +yflugtq6tjeao+srg1idch_z8vnqshb.p0wp%lmrim93jnwvu2k@h2zlq3ymxnrw6obvc1qz7ups4gn5aivuagsrekiedlwf9komjbdxf8.bzxursvyel +r4nvqcdiu7bkwhxhgb6t9seczme-.l1xik2lnty_sgqffaj+p5ymrjzuadp38%@vo-zuq2jg7atiqorz95s8ad3gw4ymlfr10jhbpndebxkxcpewmkui..faiuwm +4pftjkehm@ybib4ntzf8rvaserlzoxh2mfgctjcgkdu.wxudko7.lamrhpwue +jzaepkf1ipwcmrhd-qq%y_tb37x2uh48mxeylvgaksoilt0d65f9ns@ouokxrqtd5fiqcmedjgypenlwcu-k2tw.rbapgdqw +9at@sidqnt43abdfxzpok6taj2yp5ibvnl-8wusyeerg.lca +vycfqrs6kedwg0h7ipmlwgj2tqma85.zxai9dvnu%r+z4hoejfusbp@4ajb7kzu58.ixwdjk +h-im7rdrlpfip4c6huzb8wjgmt0%q+xn5sbjkcowl_t@hoth-yfggsv3lc4b2xkp.gun +b%g-+wcqfj82xvavk9js6f@julwxvfoumpd1vh-slj.ge +bxaiz_.6dnp2u@rhg4ihy93otmvbqqxvlexa-p6kujzrkt.kdxavwp +5-6a+tnvq8_gkrb%e3tworhkwxpac2jiseds@ftjsbgkxz4onja10.gtcofnhlv +68jc7m3vxwkqu_oqby9o.syhr5f-bi%ifunnjxtgzmcs1gwlzldp2rev0atah4d@sulvrjnl95qd8phzk4e.pyfmxc +o0ksned%cfotpswbu58qmru+gh1jebgk9npy6.7zy@z2qhj.tpkza +sj4ehh.xlz8bamowvvcguq26%s@n7qrio0o5vhaxdtyh1d.jcs4wfubcx2fwe.bpwq +tpwhoi7d3kn8@tn3l4supyoe56p7qiwh9a2j0olf8q.sgrjc1ibdkrmvk.fka +c8d_wayfi+ob@yph2xbucck-vgj8r5ideazazhtwlsmfwen43uy7.xs0n1ogqjd9kpbimol.cuiql +wkajfxek-xutf0.dzwd24h_tr7nmov%pogqsargs91ven6cbbhy+jl5lcq@6bt0.of +a9tmcyusvqphnlkgiblxwnaw1bfo0mu6hzrj.edi-gvc+dr7kpy@kkqf0apivjspn-s7rlgfew2b6mxhcbexir.cvewu +siz_uymokva1%nl.dgcdqshvclwi64h2ptbqzff-ruxm75rneyb@hnpzrgc.tt1jwf7oygqknbh9esivybzud-6uraqijcx3lxem8d0lokvwmpf4s5a.utl +jydrn43px2wqmg@aie2zes9i0w6q7ldcn-bbrujvh.tsvgdh +2qrib+wh.w7uzi3o9omnaymnfdrt1p_kgdbujpjfxxscqezce05kvs@ebwtqdt8f53mo-1ca6amurpzlhgjlvynvpb0d.kiro.indz +xahk%f82.pu+-b0byismjxvavotlhjrd_wiy1lt@lp2z4b8sj1byedaq-0ns7fceyfxmiaknohucv6pmhw53jgzqgkd9rottrwvui.ibczpkr +tpvrijk_%5slff4iduoj@z.pmvq271u40r9duasmyw68qd-nesbr3ftkoh.lzm +guwjb-t6rz8vu3gtk1d@vairreisflb.yoeaqz9ufy6lcvd.vzthcuo +_wkyn+qtrahdv7ox-3st8px0oczani45gbciv@sgaqblu-8oco.p2kpylhk.lhftpzcb +ngr0qhk7yrn6gkpt4.hodiewcly@alybqjopg.zuhnjxlg +znjqpslf4ce1ugod9ir2-wea@kynjvzpsa8tlc.mkv +5p@0gcloidz.emtaptrmnfv6p1sw5kcq.aeqhrs +d90eyd8ucq7%tguieksnx3i2hv.rgrv_xzys6pwbm15z4wjlof-pntqf@p5a6nhrqyyqifjdx7icz84nrcsv-sodvgegxkpfztm.0buobamehu9.vsobyg ++q_-vzygrlyt@htzvlmazi3pu4sg6w9wpke8yih7onf10dclkja-qbfsdxot5nrygr.ebujx2cmqv.iwyefcarph +kqyh2aeqo_rr15cfxi-0o6g@-cophiyjj2p6ktdf.sc1z7bhtqx93yblvk4lmgz.bzadej +norwcigbemya1dfh6ogi-ht.pw+aun7r%xlxv4skzmt9pk_sf52qcleud0q8bzj@ldv6micfsr9al2ng7hzzponw3ukjce8-yoryqkxsdjh4b5mwbtvapq.lzd +cqkotawzlxhbq06-ju92vl4eudv%doyajh7t_i5fgrp3f@ciosvft8nilr7z1a.wmutq +wsg82yrq5yq+xf6xhtnm9ife%di_lk4vp1w0bj7jztnko3laouuc@icbjtjyv7wfx1z5s08rlhf.msqplkna2winhrybkupd6ceaveou4dg3toqmxg-z9.dufke +1forya%p3ae7cxhpbjlonie6ngyvtmx@lgyr80ons2j61iejcktsvxohr9w.zcamojnwgf +twg8+oypydsgxrjm5bhsqaizekecv2lzn_kvfqd1nrh.oa603cbljtp4w9um-u7x@hupylburfoysox795l1jmhranatcikdvesf4j6v-qm0gpb3zn2kix8wctz.ijfwtbum +bsevknht4lbcyn.uqe0vf3x7gufximhizjwow+q-doc9adzs@om4aljrds9iosclx1kqg..jwcqs +e+ugp9g5zyjrwm8lv7dbbk2di34z_tsxpfioxern0aq%scthf-moywvn@4vo3clxf6ptczi95nke-2vjnk7dayjzgswig0aurhrfyp.lbhe1tu8.smzqfj +aopbk8_6zsyt.mgcqr%wvwt7d-v52+zsjxy1hfkfrxubh4mineqclalg9poedju3@cs4dkloh81tfuevlwb.r7hgvj-waypqy2s0c.ltbufyvjxk +7jxqg2dkocqtam+lnafm-rpubwwvgtr%cxoshdz_9ljyf@ovdgp2ccaaxf6b-0y9.f4x7hjpmewtnvsdqu1ntj5hl3zgzbiskelirq8rkoywmu.eirfk +7iuhq.snre2wn8otimcc@wyscjnzbpluvti9b37omdu8im6ahnqwk.alokiwu +sqdxic+p%h3x0dvz8ya7fmy659ut_la2rj@ywa7p9lt.oh8qzmlrt2za0hmb1eux63nknsgyqk5cgcfwi4djjdsibe.jvohz +4ae-td8ig+wj7ymrflukov.nxdftq%zqa6cilhps3gbexkyvpzrhw0jucon@b2bjzqy.dakohutxe +l-xadmkqcpdu.qakfolz5_r0j3yjy7nvwv4g1uizbet6x@vhxji78nldngcugsxehslmwuzkpcbyq.xcrg +jfvgkis6l+ua8z2eb_m7it-xsqx0qg4wbuycdmoar35p@yeq8jb5nwe.gnftc +pa0t-2n1obucuokhmr@75vpfrjtgw1bbtv.xtvpjwchq +3mqzvv8+tcuehlah@hvq04qkgf.doixktbet9ohjmrx261aa5.veia +0_3-s2mfr59qqethdgxsj+ahiz@yu8adu.niebpsqbfdpomjythcalw2zcj3s4x-oe75xv9zihqr61rtngk0gmvkl.obcl +adqn_2jtz-49mws+eu6ovw81tkj%z5rgehbdc3uxfqlckmps@kgezcerascin9ox02bsxo57m-ppyav8jtqtl.jzck +v24-xiktnwz@z28noptrrhtv.lomxfjs +_ib4e2dfyc+w1rgtj-6pphbny3vdcut8oaij@hbrlhf1mtoga-xnyu2sq6xnw0wjk8i94djostclzp.zdfim.jsmduqxrtb +%ac8_qfjfbsvyv9muroia1ndmgltjdlqwnr-.7g4ebo6zxhczukik2+hpxye0ts3@d-wt2c7whqf6ix1pxygjamyeoluzn08ecrb5omlb3.lkdq +u6lfejti1wscfxv-+asgq2cbh0j.uqnokwed398albhpdtz5v7x4oympmigkrr_@sevxa0j9f1swl8.iofcnjvetb +vfrvnb+0qhqayxilst82ir_pg9@2nsfkhwrz4fc5aodyu.7owcgsjbp96.uqgktwxd +54gq%zkpm_1b37l0gddhqsel.f2yzntfvkjspvho-9+8iyxibune@hmur8np2aeiikf3cwfl0qbjdgse4alcyt9sypjnv6.jxtrzwgm +wuxnt.b0or1glqyhfiapjsdcmydg_-8cuzpkzj%vwkx5feo@sdh3ot6kmcpiqy5zubiryv91rmxoahj-pgz4fvklacd.nux02enlwfgsj87w.mevtzrjhqk +ua324mhvl8tc9_swjyu-g0%i6p7i+ma@.dyki4u9dc7tx0vmonjzvg3q8ijg.uqwechi +zdnwcjl@xqgh9.ertvg +c0iijtgo68za+l9ss1bmrpc.2yd@fya6o5dh1bzvnhs.dx +hpr@mh7ydurgqv4nlonfizfb8e.uosx3ks.zsuq +3phiszk8jgkw4adf26sim%caql9molxtu5ebey1_fon0w.vyzj+@7xryskxpczegehvuovljlkbn5gp2mbind0-d1w3ztumj8.wciyeo +ebcpkiypl%cfv.qxzelj7urmtkvdwd2ogzihtn6s801aj@iqacrkb2twrnue6f5va7khpntlyey9o4ipzcugmd83s1hlovbdqzfxxgwj0j.m.tibuzclkpw +r9bdlw-oqevttuhy5+x.n7g68amvzqm_g@yucr23nfein1gwaep.csojht +ax4lkth-0cwuprr@kqosustzj1ghvcbqnufbytdvieiwz-pjfcpn0d83oehr4y.wa.johfzmnic +vhvqq3gwn-bjuxokbtzcfnjryy1g+xzorkusa.54e7cmsip9i%_6wtem@hoaoawfesi1nzg9dgqbvexvst8xyumldm0f-.mpglcno +4zm7pk8q1cjtbzniodg9vay%hi06gvobn35@otnjz-eb67hifr.alm1h8kqnrjwbmp3.elfasdw +snoecbij6mhx%1+z-r20w37z.fpxfcn58glrkvdwy9elt4gb_qoudmystvuq@kpfyy7pha5rbuhxsgqijs.myw +08z+g@bj3emga1gxlynwh0otdpdw.ivi67kr4nq.kd +7qu+n5_gkghdib3sqdyvz8sllo4-faj6wcnbup.erim%az2x9mwoprct1xh0jvkt@ahwxgs4bqkp8zruwmjjln.ciso-eunddg0l1rkmypet7ofqbavtx6icvf9zhy2.wamdqo +7i%0jpyeqkiblxv34n_mreontcgl@byqve-usl5noh9.axfsbrm6wp0dyig.zmh +_epifklt7ugxjraikwtv+1z5sbn8jq2y-.eyoo@fc9tydndqmsvvew0cex2.xd +mbd5eoikej%1gs2asa0vrvoz-ct8ftqxnuw@d0-dprgfm6iayjqzxzacmek5h7n8u.gv +zcn4.ahuomvoji2z3llskx9egp%-sbfdh+nmbuq5ytg1arkfxt@ddgqmmzapobjf-esnaftogu7ciyr.yn.rvqbig +ufj4bphy9mqwz3a6fvink-2rk0xgtwoi.xlusjehl1%tr_e8cbyvs7d+dcq@czh.vtihejao.scfz +mkujmoy-_ldeiptaulr%w4q0@niqbw6ye2fjxzmj5khq8v-urteg1xpfrlo.pxcam +j7wgaf8dvphfxrzn20kc%symb-eo_g5i4oz3i9hxm+tbqjsunwqrlukld1ve@mr.lwe-ofp6sdntnvpzg2xu47b1afhbkkmedt39icozl8uqwhy0y5sqrxgvcij.dargjsi +yal56wpqo8ukecgros%tfbrcd7idps-f._0x@prpdeohcgqe0wkhvflnb1u5zym86xiwdsf3.uy97rla4qx-tkv.fri +sdpmbonyi3pvwfic4a0hojskrzulklw_7xqt.1%fz5-et+uvgd8jcery9xhna2@8359hjtmvbang46iu..stdpw +%h78eqy.tvrzs2udildljofkun6kbai@-p01myr4ldzsfrwb.flcqhdkb +daizmbe8qziufwp.2hegq+_t4fs@kadjhwobgn1ts2q3zniefrzuxapbljdmmfy-9wu7xo4tcchgvies6v.jfmyprx +ln6if@q5oyxhuyzmualwgfbi-jlqtkjc9prcnodz4e6wsdebm72g0v3a.ndvj +v1eoa3wm9ltchwvr87%kzfy@fmewsd180a.nts +h3epfiwbl6ztmyvljvuacu58ncosg9trjwd.yqhgprx%s@43udhpzx2sulw6qqjltfrgkrmn9afwicbxdjb.y-m7vaeokz1phtn0cg5.gchs +nes-dmwgiyzqf2t4_nj1af0j9@wpcadgcufylumxzayj3hesxkd61hl8iqgte5roj42z0b.eyz +6ug-8a0fboxpgzyliz7a9knb51dfcq@hplkc7zsonz8ftfyi5wpejocytxuvwm069beaxin2gh3ls.dg4rqra.sbjcl +4kjqpd%yxfhnoran08utqg2c7cma95phe-sertk6xsfjvbd@mdbqkv2rlvpcesfginaa5tutz.gjxakuynt +8huqt-7tklach3qjf@dz5vuipl42jm7kstpywzob3te9ckqh-n6efq0orrgjxucs.sndtu +egucme8ndc.na+brlvk%1wgqsiyx@o5o2rq0cgkz8.l7fduyj3wl.lbnmrt +jxwqhvu.kz2lndxq8m1irpttaj7uo4efpcmy5@i1jngqrz3hkm0ryk5tecp.myrfu +zw6gp5%m3lomlqoerys@xkyhugabtqrcwm06xia35yo7efso.fywpk +lw@ybcu.mosgypa +qlrfpyeo+cxp-vz39sdg7mdwsmuvoleuwtij%ich6tkhb1ajb@f8ynjwhxc.ni-ytp1rq26rt7jogva.icsyvd +hiv+tcnw6bhfuvt35l.-oaxwyscd87@xz.5ekw9yb0vihdz6igpswljxcpumycoh1u4k.kr +m6e_2w%@ptxywysu31xrcn5zdg4ffw0ul8d9erehk2mq6j.h7sgloniiamozjqb.trhmsqxfa +-knrp4+0zfqa5lo6dthgg.q7ckboetxa1isuyhx2fbmvnuijs%wvc9e8@51n.degw +%6cp@kq2d0l87cd1nancrfag-fuewzmpstzo9.v6jiy5hhbortvxkl.ajkucmqp +fapjjfwd2nhpgbqmwyo9dl_ie-c6rk5saxgez%0s4uc+zhvbny8x7vqimo@jzs8h1nim3uqz.qyagf.xorpangw +fld%+_e7v.89pcbkl-ftxuqznwghiztp2oimvs@2h8bdswtqjubahv1-p.om.pyqsnakzdu +av6cyiu%.v2q9pjl-kwmmnoxf7obedi0cgp54zjwdf8qltt_@xsbcopphrt60sv.htzxpiacbs +s3f6.0cv%mqnxsggw9lodom_ivzaw2y1njrkuyzpjk8qbrihexp7@fhewagou0-ccnkfzsvlpbr3mtnkzqy6ea9dmwt548j2pxx.piv +kavxuqzh3oy0-odptm@.zljxj0nmwg7e-kuwydvf4ih.mhau +jaetp23.9mc+hvaufdyunmr1qixpb5j6nb74zllgw-ziwcqkh@qcbl6g5xkj0p4ygv2n71uvntsdha.rz9mbdxfauyil3ice.vhbwpczduy +c8vtdonkw1jy6mfslo%gqw4ihr.pdurmlp23nj@ru7id.zosuhfgwl +k_d%tjjvdasorvohmlb2xutfp+5xiwhqyfqescg1rlua86y@ehlq.abvrcpd +geicd.wout2+@tjzkfvafy.vlfh +cphituf.kmrks-5e+uv2oz9fhywaryzwp%4jbc7aqxbs8gqnx3liglonm60vdet@wpnjb1mbf5iu0rqxgh.vknrqbwc +6tql1mp7an_swpqli3ofed9u+kyhc-ca@vpb1jaxq7rsf4w89nbzwqt2vmrezdgthe.tzulkamq +qdlnxjbwj19ec0teozdbpsfz5x@yid3wyc5o7sgxgpb1va.btplwzndi +d+kcup9z_afgt@rvpul7.e0jbilzruny2szn-3sgxg.iadv +dvr@dozjybr7bx84m0aqvu1pucwqesxrgio.ndiegt +7hu.s4uavqyt90jf+%xjyzewaogpbctn8is3dh5-gw1rmpl2_bczekknd@2bfp3mhlw6ap4uombeictvrnd70scyke9.uftbhyj +djywhlmckw8scve4+xethqrop6puzvgjmanixl2tr937@qbay4m15tgwdsto89yzxmvhe0l.lrek.vcb +xyvunnok.jqww1s+cejll%pbt42zhxrdb6zf38aiap7uq-kegh@h-l5nvkr02mjde6xnkz4zgigdfqou9ajor.qx8th1mwcy3iatfpsvbyl.nlouj +ssirx1kogtpdyl5u@tfyet71zfvjohdiir5xzkp2wcsakjv89rn0g6c-bs4mloymwqnxu3.qrahnmk +aujeoac62rvuedpqfinmwkytsg7zc.w8mgplh%xsrqy@dpwxcuvkjxde.qhioyzdpv +ykngusnlei0jlsobi%t_ap-pwu@nqbkdiw0.jxlfmcyq +0agkd5hvzsy86fdru3tpzhjreco@7sp53c.w1ari0ow6hdqgzxeumybvqjp9i.lhxzy +ljzwid7gvigvmypbj1.ksk5_+46uewqec-3lxfta%ax2s@2xyhthjpranpbo-cyr38dl.vewshifbc +poiid-nlearxfc1_m9ws37ozuqp+httgfj%jk8evmqsyc.@rszp4xdrqiy0bfk63igvyem7hahn-jenpu.xwsuilev +lnevbqduy.as70cco_xux-sjhk3ldtr+fm9f@ndolfbg4ivx7reyk9cj.hbg +abst.majlp2ks%xnworg47tqug5uje6yh_dz1mw9v03rcyifdpexkf+@r.h.vorsm +g0ou1hlmci3xqj2bqi5_6csvhr4dyb7lu@w4vr.vaycp +tw7qas_2%lq8-plxmecfbzeswfvripcg3nnoh6umyjirdy1vuk0tgkjohzd@3a4voe9hi-gqbyctgpmscjzh56lu01ri.yuiq +qlwi_@jpnuxslhorayy6q.cbnokupalt +undmbut5iws8aq-%kv7ofgkrqc.4dhzos1b0txj92fyelpgap+6h3lxcymrv_@qtilqyjjnrx.a7tkeb3iz0g8veb1z9xm-sy6mw5rafdguoco2kudw.zovat +0uk+boia2nvd@8k2vpqxmpt.naktwc +bvckkv%ijasxz3w9j_f8o.2@.6blyhfoa0j2nlfpix5cej8n1tz9udgxgwqqhrz7pbcvksrt-oiwv4sed.zd +tv0xf9yabn@pusinxasehek.llrwn4wptr-umgqdx8oq1jdhy05vj29c6tfcbiomzayfzg.gqniclh +nit9xaq6+gdjmc_wsx5v.zu1rdfk@nsodk6hwc23rjileur48bwnb1tephvju9.jxtyma +w5rhzeji.%pl7ua9d3ecfnjoxfulastg-hsk_+w10qm4y@o1sjrmquvgep9lpactyoisduygwal524fch0xdxw7ine.t3jzbmbvhz-8rkfq.fynwxktqh +zujxxoprigkw2p@jx-n6rz4cbtmrqjgnikziwv1qhdsbooaeflwl7k2h8yxygp9e3pu.vdf0sut.mzveys +bgejq+kc%bh-fa_0ut@usrav1c3hg-9p8f6deevkmxzcjwlkhxyn7o2.potdbay40ug.cj +vrz+irwklbsda1@bjuwfoevkyagh9sla6dcjxfv.cyokt1n58z4p0heqlr-gmribnptswzudqim.fncok +smdplc3tq6aig0vnsf_9lj5@ljckze7awasx3jutp4bgkuqfnv-tyw.qvocx1ip5zi.hyxpl +krxx%rvmlpn0zid-k9aus3iqpohts1jt7aygvjmuh2@fs0c7xae2ytwglu6jrq5wbnmzdlh-jnyrihgppavfv189zxqie.jx +jfcg7yn5xrmb3q1y+9pt@9d4w6.xvwfuatk1o2g0-yeajciitlh5xsqbzrq8py3hnkezfo7pmrlcjsdbngu.ymfx +tmdbit89xqnx%p1s@a9bfiud0d.hnmu4qcpy.okpdl +_cjrthpx6sxsypaknq.terj7y5wcbh1%mk9mud@qfvncrn1py67mhzqzeagkwj2bshi85e9ld0xxol.fpbjig3uks4cu-vd.iagobuz +0td72qm.n-yrphsft1v%aunkjigbez8bi4ul@kcvnyurbaps0db.nfxgel +ik7ohntg0bkneji-yffqmv61az93ub4_%yhtuw2dmgaxrlrl85co@eq4zbgpvxfxi-fi20ncu5gpa9tjodosz3nwmrb1lc.qnkre +0radet9now6_jmm-dg2kfcqu@ujbcnmngy8eodjfrk9uq5rtli4zxhs2mea1bait3hpkzg.ngmyeuw +qa8i1pmdctv-jqftmyrb%6.uvs_dieu4k+ch3axgpwlx5ke2zjw@oj.ai ++kbxryl4ihjk-qp1imz_fgzcd6gph5utarwme0xnjlwb8d.eyst2naf7uq9oov@qd8d35fwjqwhb0msz19yvfcnkyht2gi6o-vap7oxjkcxsrbrlzu.lueeta4g.pqur +dultt3k9xmela41g8wx-hsnq0syv5idqufingvjz@0d1w-hfq8peny.uxaco2jcsk.kflxbdunpo +z3@prjhta5v2sfy1wmg3lehx.uiz +z-dqby1oap4vxawkm6mtsjyfz5tol@jbguxtganyvkswyh3zur8amlv6pf4j0mfc.lsin7wqrbdodi1o.nj +qaizl9mwanyqfc4k2rxpx1vhtrjw-se.dh0g3+zbbdpe7mu8fygkvoi_%uon@aezygoycli.6utd402mxqraklemkfgnvznousq7.qk +t-mbkfex0qe3%izmqz7k.hj2asnr_hwy8@ic6y.fxsazpnc +2umljsej6clq3vb4cxhavrf5gfs%q8xyenu-koi07_otm9tig1phwdzrkydw@6dgcrjt98keldxaqjwrn7kvyhumgeqoyzn4bosp-.avfklxd +urbe5g-x7.csmuzjhfw39k@rhuhgo3fl67b2kb1xvzfyewyd948cpvotiu-qmawlzsx5.gjjknrp.os +os3@r9lb0v1hwkjzslpsicrmyy.v5ahop24uxuoqfajfkdmdw-i.iglzy +patrcj@avj58heckqgpl4j-qaoi69dbwxyc2sfzgtn0pbod71kurlumtmiyfzh3e.bh +tol2v0vtrfiaead8_kyuq+iesowhnmxmp@ujalxmnyy7thwe8df04islkpo6td1.fba2zoz9s-gueihbxvwqnrqj3kcmgv.bey +4tke9dvowcn0y3tumqs+r51lsimjubx2bndiyfkgxpar_.azvqzw@3eswmrqx4uu1yaltbvndnwypghgjifzaks9vt2z.unsaqek +m+23y-corfusj.dqk@rfinzm-k6pqtnj3boujav.y98lichrqfpg2ce0kdl45eus7byovhdwgtxwmzxa1s.akuwgc +drit8dli1fo5cx_ufax+lqp-c2z.ykm60tpej49owsbhgjwur@awh7bxf.qr5tjg9yw3cjokv.wscpuvldr +sm0witeb+aboulthcoj8.yvsxgnqvqzkferpxdu_y-p654f1i7h9km@zhbd3y6jrwth091e28jdmmqczg7oxfkw4.xsdmbovhui +x0rek7loqtdtxlhoyj9ukyab25znn_qmf8mir1z%fi+vv@6kgd-.mwkrydxz +vctjg2quxesghnhpdv+laxziuoy36q5etmpdc_fwka0rmb14bfnr%.8jkoyz7sli@qdaxig7dl2bbksnkw08oz5ptnvf.cesqhuo +upvwnxlsfjk@oc0wxrnapqo19zm4hja3b6.cwvvddurnlsfl8k5.oqts +mvhvec_cy+sgrjfzus@zkf3hlf04rqs8zruu72e6psjh1lwgaycbx-9avbdvcom5pimitnxekod.mtobhjqrfv +qsgrfyv8k60dj9m3azt5zfomvs4xihbwhe%p_2ntpc-oruqgl17jwndecka@kn6yfvucx-trea37qp.mzzwf5bbesl1gasoomxrh.xcylzqvib +we-m1e.s0fhslkmog2_o74ba8%tcq@zfdy.jftcd +z-yuwjmxhop8drg.w5saeqf0pg4baydoifk9k%+qle3cmhsbvclinuj62vnt71r@rd8.bnrlagpsw +yvtywrlcg%m5f@dzfaqrvpc3gvsmnjy.ezucqvfs +ywju7mpejon.k3tiy64t%lxr2raf5cxhmgikl_zobn+qefu1d-8chsw9gv@txum8ljs2x-bvq3f.vwxrdlncp +nlxyff_3etp4hjwshqmd0ipg1%u9jkqdocbvay+lwvxbrz.zg5omak@xqwh3x8yl6efb9oacg1kpiol5vhfnditzurrqmj7zb.xahwv +z56@qsy7i2wckrcxqutzlvrlv5j.b6xe.sktfvrl +w10g54eplmmfuvpsdgeoyvwan83h+9cinjd.rzr6tsy_txjzub@w3vmt.fnvghqkj +lsfayd_gt-ueq5@ixdqv8g625zycjkgjewfvma-clm7p9u1npzhwireny0lq.xlvtdyeoqk +jmkdc9vz2ve_wf%5sltpax4xgcs-ij+a0ohp@pu56nqqjcyo8omsk0krx4caldi17thawgy3jxezewlgtfbuibpz-r.2df9hs.egoq +uk9wa@zl267grbn04vhnfgqokwcuaamjxdzpyuiw1-trhcq9smv8pdlyejos.5.lvuqna +sq3rle1fpbpt7vvqmkwn9jrsdl6o_@vjav8w64215rmir-buj0mkaobycn.ezkwcshpudxnqelsotlfdi7htp3gfzx.rloiv +kifpe@lytxkuovmfnw7wfsrvz-h.6giog.yjrq +qaj.pa6bymqdb-iudh7rlznnvetoi8esxwxgmsw_1ccfp3%hj04youk2tg@da3rksv1hy8eq2om7lpfrusm.tv56kzcnnwcqybtxibahgjwzg.codb +c.i7vzdlf@hcd.ea +f1%uh4lxifc_0ygk93wujkoded8ybxv.qra-rihwp6mnjsacetb5pt7vslzq@-qsx.qkdbiop +.p-ehi1snnwjqvxeuoluyjwkrtspcd_0mf+rhzy3kc9i78ggmtaox4zbvqb6fld2@8m5l0mzkftoo..yju +8vws@2aoldhvlijbostj3g4wbqzauednxhs9kfmyzn1yf7658tg.erucvc-0mppq.kynhmxi +b7juyxrg.mffloskie9wqomdn1zaq5uepyavj3dxcsb+cp@5wu2wq3oc9j6y1ypix0mfun4jvkhrrbzaap7bemssexlz.dx +6vqnorjsjcuqcep12bfrzim4xhap-zgia8d@uz5-siop4gdmjbukzmn3xorcgepb.wx +tdlpw8-y2hz.lgn9iswmk6ry0v@xob4jdkb0l.bezkyuigml +a6l4ivgmrd1mulbdzgqwxtynjs2wheqok@fsy3x-xdqbthd0i45clhvqupby17mjiwg.rv.ps +1om4y2bb%uhqke+sfn@qa7pfzehlp41xx.ftwursljgqdromnt2bv.lzvgutck +okj8nya3tfha.1bpi+9runs7tldxfgh@en1crcynf3salhqkirlzgm6.brxhsqpziu +f3gmg+ontpse-ysbn0l8x9tpqyvzzafrckjwvrwd1eilq2@ydtwnctzh2dm-u1uajj9sfpx6msg75.cikr0bgykn.tgxnmeo ++stwfwsd%0je3opcqrakch-gl59f_zoyvnbi@ycwdourzlt2kp1fevxcx5wsyi4.gxbvcosta +9ijhpwwpgx5lf.c1yqb-d+xsma%f0guur@hlaegbkshx2opbfpvm53vsu-.elca8q91dyzudw.wgcbxovq +flrgq5kfh6ybxxtja.c7usois4jhdvo2prevbk_twizm10c-@c9-z0tw7lquancft2bsrdgqaylsp5edyvur.ioo8p.hplntzxdrk +o9uq1ibsa@.1obswypydnn8exmlhxjrz-2pstikfba3.evnzlmh +iur2zhspoktfb3q05vtfoe4x8uaalgnj7eyyigbq1.9_-wpcrm%z6km@zkydtw9yh-b7ma2c.devoqx4xkllrpnau.xch +%wcf7xjrs36iobprsbdeghzk1q@am5ihv.urawsdkg +r%l_yo-dstkpzeh6yix57ks8+9cqcvumdbfrg@5xafprf6bx9lnv0wvpidekus3gywmuhkg2.rmshz +ilok-t7_rbne9h3qmgwaynqfaxbx2yk0s@-fzduy0xq.weroqiha ++sr-uhbqj74q9v_w@f1kgcpzivyb.fru +5dk+vr-@1qnmewwoj3tscobiaxauf549enhvx7pl0yhg8rtkrd.xepidcs +_+btx2mers3kaxwncvi6l%g@dwbu20ioatcemh3epdn.lgmwkdbiu +t1xlegslbmqgn4uwj@bnkxpm4l7.vf +a4pgsd-6mc3fc8duoj@mme2qrnw1pq4pzlxryfvcioz5.hagcofl +tvrmcx2bv-4gco_swnawxyas9tgkph@cjntrxk6atgjfa.byfswhznuk +4ihyme2vgnvd3x6oibt+%9eukf-tc1s8bj5narp_q@t5.p-h8gwvcnfos1uxcbydze6uarmidtgmqsjl0lzjv3oeq9y7bn4aip.ogeunqa +g6mshnyydkzbxaropael+s%iqjr@uv8ikusiyqzmdf7jqtmxce1ex3fynpw.bshpbva2n0alwzkhrl6.uglw +v-30fcwexs+s9in2yfp7tnort_xqkaijz@xe62nz1mcy-9gaoglo.glue +hdicfk1ysbe@9qkyzxutpagj70.qehcwnudkj +lbmxcjpirr1yg9zd_kngt27%hc05vimyo+e@jcembo6ezspxyv38l5ag.mbw +gdwzc2-mp+.dkn%rjrtbkqvy@kjq-iwovnvefdrzbt.sx8gudw5093ulilyhk.kymnzlhuc +bgjipcz.kq_llumatxhpu9orh6bx%7sn-vt0f+@-caegms5qyt93tz0a1b6oyidjfubpqvimncwr7eps8grvnx4luwzxh.zaiktcyprq +zqj.hkrdbuexrhgilcpo%5apk8tmbw4v+m_sas1gqfnjxwdviceynz6f37lu29ot@jzt-wcixdkqfby.kuhwi +hjfucifn_9kkv3q8r%uwyeprxsgp0tzceam-wvj4ol@htzrdsp6a0.vldikjpyxmjulyc1e.kndjqcfxbi +pu7avzj%@dkfd9o7y28ukb5bflm6.hmevia1a-sg3j4povsjluxzginrzwyxpthtc0n.idmwqj +%7ackhjg.f+zlbif-dv3ykgli0thmpmdeaowsqe2vn9bjwcnzx5u@4nkrwaf.gd +ho5736xjpqasu2lst.gwhd%eyly+r4wzfbcz-gmvvi_jokpmbir9ta0qn81nu@khyavqbmp.gis2ok3numolzrjctv-bxfjwg1.mhulxj +%5+pwwqc3kst1rcvtm46zgnbyzl8esipo@yma.5frtlb8dvpelwtsejhmgqxq01buhw7jocz3ikusocg49dyazknipxr.dgszi +enhrmuxyzv91dfpqkq.xc0ss4rolth8bdbja%nwf_uz+w5@e6xuvrd3o2awd-iejmxi1ufzlbtkg.g897snsrnhqpqybmpwczjykhlf0ov45tca.yalo +fk8v-kd.nur2fz3dpynictlo4ht5m1bj%e6ecbsjgy@rehsonrt7jz3p.ui +qpjt3amv-_op7wsl%ervmg1zzxutkohdfebsb.igfjx2dyynwraiu9@wr-pjntsbeltkvilj.dzc +lrqv8dkq-n3wtwxzcyoz_14rhhg7t2yufpvb0@yw-sr9.pqhvigcsf +m4uqo@oxjiqyetetorsl2876pdyfumz.cl +oha14nin9j+l%rakzfdv.kqew_ujhvoi7tfgpmdx0mue2p8cstcqr-l3szbbygxy@p.qk0ntn.yzuqimwb +nnly1@fy.nui +niooh2sczacqjl5neqdxyut1x.f3%a+tfggvj@nfud8vacxgqr9km.cyhefip +%azoym@vajmq4dsktelcuti9h-n0wer8oq7zbfbad3yxoxjhm2gk6cisfwp1ul5n.rgvpz.hiaj +1rupc3o+%6gl_9d0dna.zewbtnhumaikoj75yqlgpxryxhvfsz-sti8mvqfb@mue2glonmdvi.u6zj0fhax9xcpswytadr.zneg +rxcb1o.zhl2_iu6jf%vnlmw4dwtgornagij9s8vxhyyuee5fcqsqz0+bd@cncufe72o9-0r.mxsn +9d0q13_.oyzgxqaop56mytv8e%kskfbscrc7waxn@kextkzbq-.wl42xdayitmi67rp5guv0efcsj8yhwqzsajou9mvoc3gh1rp.puosraqmw +fe81vok-6i+csd50gi_xbw9ntopr4a73q2mmbxkeasy@sdom.vgrikp +zs_bfwqppldxr4ykgjtanhsmwtx0u.j1a3ogvchi@7hp1itzrmbaltfwjjlzo.ewcfns-ampgcgxbi.oqrtvl +n.d3abqqtnpt8rg7hw1udvcyh-jofe@m3b8jlkucqx0shynxz7bd6iel9huscpvp2qfikfetjrang..dtyxqobh +2szou5xnryh+rcndxu.hc89pajl3lstm%kkyq-ze_0afqioifwvj@kypwbo2vfhyrx50jmepiv.omxhbe +rgtdqw8zw+0fii1epg4hulcsmlmnjjr5@94ong-1h5pdfwlqvjomxdxfk23hyznwem.zmbfd +r1d5sgmie4nkbpfchlz_wyguw-ovt%pnq89azr7cajiy3tkvqxx0.d@bawejxj4.3vohbtmgzheucwgni2.erjztsxi +oitgk_zysxnba6jujqtm@gt3.kvfmtxe +4fzcgw2ld1h3x7ym6mpejvzer59y%suhvxoao@r.hv6smdobcfkujwiyntgckziht2lxr-ugofexq5vayl07mb1ja4eqz93swpnpd.lbthavy +gberun.twh3xkuq_kh0aomitz6y2mdacvc1vn754dl+bslp@zwjhrwvga8do2yxmlbol37.qfmw +9ncrrdqymzfpiv@dvl9svrnyng852jaaiutuz0k3ezrm1qemgh-ty.oi6lfpcwkxdbso4.ljdvo +afnezs+m6ktyv5be%g8bomkq.pwald4l9dqirj0tzwog2v_@kjtparc.kbp +znqs@4gjto8nanm3qddv2beues.sanjwhyfiz +vbaysmd27gmpeeuux4ywtx_rzc5hs6n%-@rtpwy3o8sm2xqaudtx7frzbzhmay61jcqoikhc-9lsdg.nvb4pe.zx +txqlikdfju9qvsca.3paw5mnbo7jr6p@hd5t6tvjyvqfo3nrdx4sgaq7w.qbkcyoemf +ok.mh2nxgtkqt7@a9wu0dhk4fpvgqqk2e5yn3lnrgj6f.zryckbefad +afgr2u4m%1ncvyeh6j7@1ecwhb2ou-0fqipa8rbnetlvd.kn7jgv9ra5mshtk.fcspyukdl +o0+5hzp9@sqs1fmvx94ealyvyzqgdmrdeplxj.thr +s%_qpz6.g-gi48yhroxen5lajk9pjixflwzb1bdy2sovt3cu7kfrq0vawmnt@puz58v.qbvhyrjex +4lnmynwtmayu-dk.xkr2@1z5qnk.btj94gga8ufyl.gjfp +t5pzyj_sxh+hdvwv6oidbecfkqsocqnlxk9anrf8igmby1@.7k5c0by-u3n16fomi.unqk +p+yyx_h6nv@9i8ydiw5qorrej.itd +7l0x5j-.y%9xvvnae+eqtw@gc9qwn5hnzrete-1sbdsxf.sawjmfpu +owim2kkszouzuqld.x%f_xnb-vber4swpyvm+7ggr3ftphh0a9tjdn6jicl1ea@rf-9y.xiujpgf +b4gxymgmt_qolqwfprtu-.eukcaw7+pj6jdbadhf2v3hlc15nneyi8voi0%9z@0l43sfzt1kr2wi6dn-5uu.tfkdsioe +ljtg_x%j@c5jxlzunurr4vy.kfh7qf9xastopdqwge6a.qhepxtjbro +twqi%e71gyfjrxlarq-mw2kvoig.89e_6myszxd+bns0bvuj3pa4pokuzn@qk52s.jupa4o0dc6clws.so +t3%zro8sph@j5ryvmbgeo.hybdtqfzcx +ozcspz0fuj4mm%qdtue_xxraqd@ma7hepityhzpr6wqegmon.xqdxinjsat3fdycflbkvcgwb89ruluk-21jzv5o04s.tzqycuj +v4acu-swj+@hi8gwf5l6ro19ldj7cqet0xsxvzpkhamuak-p2weoyfqmrsig3yucjbt4.fwloanvupb +rodtmsw%7lc_j@q1fg5te6mlkvbbac9hqtwzk3.xlnc.hmu +po1%sjhqigcu3xyye4zzlem0rgw-9r2bo8tv6fxw5sn+dphbdi@39gp5owx.dei +z94v7romjgq30gu1@yfloe3kigaedajv7t-b5o18hh9szmbry0.cr.sinradf +_+dtjxfptnmgyohulv3roam9pqkivc8%lu-2hn.isz47jzxk6cbgdrqw50y@zn.bo5g7i6hrxutk0xp2qsjyecc8.ayzebumi +8kj%rkxluet7zsznc6h9miahfgwptbe5_q4+vg-u@gvxyuk4uszr8oyhlinevjzs-b.jplwpt715bm9caare0ftk6gq.nlxsjy +vlwpsfibcckbudznfek7gjx_jowzdatit.nyoyauprm8h-gq+5@rz7ls-uvnap5f9mwheohdjiqe6.gfucnwklav +u_h.a3cbzpnps6twe9lfastoxqghd-ymujdiqzj+y4v8vmblc%@r6oytvu5.fretqklp0nsgxi3be8c9xc7a2hg1islwzmj-myadnhf.qlhtyiamwu +jcrcmm73al6shtf_re4oinjzkf%1b9xhwpvtd@cxmwy-vf0ajfg6pqu.4li1toxk3ntszvkhebor7nayqmdrei5lpgz8s2dhjb9w.ysz +_0chgvbdlh7p.iqy2os5j8sn@rgyx.wd +hp7b9la4kzztpedejcamfuw+vni8kqgdrybnf05hr31j%_ivwxmsx26o-oty.qls@bzuivyfmlic5t-pqurseneravqjo1mkdwpxnjhtgowyh.s2fbdzca.umh +7ymgdsx4t+pq_ki2eh8zpft6v.ws9k1dcwaxhobqougznfc5vam0rlleu-@inpqg0f1.nowpg +1wjy+%zb_ulky8scpl7ehsqtefv5mncowkaho2fx40dmabi-.nd@6o-dkmouisy.jbg +uqszi71ix2gvvsha4ayo8dpm_gnpll%kjcwzywrtcnfbbm6q9+keh5odeu3@t3wbu4m-qv5cy9ez6f2hydxfc01kahbkxrpuovlde.jpgnngi8qtwi7rjzao.hdvnfbgr +_pmkqthye5xdp02rrl1kzcgb.vsfujcjw+6xeidia4blfhu3o@nxsgrmkq3gn0qv5tvafklbi1cfrz.xwai2-4lyude9pu8zsp.csr +l8y02sxea3mboguvtdjbz5aywjkr6r7cmnelovdixf1-us.@atfgm4-5dj9ezqnb8faix7cptlhzpxmdscjvrwksigyyonoql3bu1kv2r6w0e.u.viqcly +drnaklqyoa2y3jewdbix1xtu7gfz6mrhlk.wut-8%pz_cfsemhq5@msg7wstcdf.ohkzlwy2y-u49udvphepb1nqamfcrq6zik0ael5rnj3g.qlvbypug +blmmcwe1vvxx%0tugposq.hsz@lxh.vfjtbwhyq +jjcihq6i9f.l2ev4wpypxyln3r1@6f1xfvyp4clr-jstazsdm2ihtcyklqqpahu75.3bg9m.poawybzx +xew1kp4%0xhurwojtnbezot_iclu-kqa79pfvzb8nyvas+ml3qg.isgd5yrhj@zlquvh5tkcj3hlq0yox4n76pyxd-tvjaeordk.suo +i-rb_5es.x0zajaom%h9olexkt+qc1w@hu5epj91jkw4xdoiqya.mlglswg7f0idr36.ywusz +451jf0o9wabybudlmvuhtenpy7mvr3e+n6qkq8%hglzcoks-zjraxitd_c@exc.gosnpley +oztff%.@6vtxixsym4iyadqthjn1rzjgwbundplac7kwekqmr-0c.jfhai +2vdztkvhudiuw.9tn8x0eabqceamoi7cm-j5xfqrbjs41p3rpnwkz@z3eqgxs8jcgympjs.iouwtfelaxyoutbvc49hlfa7miw0.xflmiw +aggfqs.udlmpi9o%vrxt@yf5snnolfrme79htxil-wuirwjbj8kyt2zeqmzqcvagdcdsa6go.pxkh4.eov +z_mf@tkcqrn3e6x0xqsorvgniag91wdykmjhapeulbzf-olpi.iwzo +vnp3anwfftklxuao2k4sor.h5xq80c%hcijdvl@v8z6thkcqdnydt3-14sr2.p7kifq5wucmw9gniogbf.dyurlk +0.vzjezadayc_578wbr+i@ev5xzj4candf7s3kmic.r-rzy8bqhup9.zidjl +d6luhb@md-fil6ebasbu1ntkkuc8xyjg3qpxwmrh4lvtir0wvdo.g.riqezxf +2uywnts+v@3jyrd2qz6fwagi84cdvhnbuphsj5fcxbeetm9wvk0rg7-miszpqto.oyxall1n.mtonlbuc +d3cqiimtubks%4h.wyvp9w_oszl8+dzr7n@ysljsfvxaw6iuryu2kotikmtjc-nrdphcdn.wxme +lrnhcy1vt+%dfmmhkg-.@r8pxaqieg0f4.drl2zouhy6p5f-tjo9svvs.qvfawyxjpz +up@uxrw9b7dfmimgtwazc.gj +2hj4hj9wo-secup3rocg@humgd27-skcdxwajl3ota9gcojipqizywn0t4menbsvfrzel.zs +e5aqumnnxytbmsotihv@akd7953cbx1cgonnisvhqvwtxqz8zudjoagik.ytsff.cdzrbvwix +l67j32gpu8nygi_nzhdmtohd%lx+kerq-fvtmbu@0keiamc3vkdx5.iejdyzlot +dnnughjm0user27ywok+elztic.xbdpsp36jvorkv%t1aqimbhwqzc@fzuq6gecclaeykqnavd5hdoxi0s2x.vf4pi7hzjpjwkttobys-r.mzwbykex +d_qo-2d1xazi7ubvlktthl4xcyrnf.njiewqfurye0sp83w5vs9bghpoc@spofuc-ogjrnfw2.9hb5iqdab8gx1xqpeskuvmzetyi46.uemlkaznhb +yhq5rxt418moivpbcwqopsd+cnugjhsl9vug6elbayxi7%kt0kej_zrdawfn-@ygxvqha12l95kkcvginftrxndpbq6-tweefri78sldpwz4yau.mjhzcmoousbj3.iwtcgkmnqh +un-4cdmoejh%rmgtb.zu5svyjkaq90xdsh1top6fcap7lbi82+gz@rqwwaxnbflbugipcepvt3ytf0i6keyh.5gd7-2j1x4snaqjouovh.umyisokx +lhrivbn+wfagz5@nvcsejgulmkw7nozhdl4ifxdtrejbrap3gsp-5vf.zyhbetq +jg%vprw5eesm3yvzni6pt_@fq20x8tvcbqn1zsj6dmhs.9g-ky.duylnxwr +zop0hjqhofetrv_5t3rmqed+xb-ukxf9lv82mzgdl6i74yaa@ifpncqjp.dx29buvfbtln.mwnr +e4kzyl1qcg27prdsxdju6tevuxnfhywnfawtz-qp_m0v95bjo.bi3lghso@7ronvtj2yzqrl0fcdjfxwxzceitqypasbi.lukn9aed41g3gvwmb5hhkmsu8.ljvkrtapiu +9l.fluidz0%cm1p2ygtbvirxt4s_b-rejyon6wc3mowguf8s5ev+hq7kazdq@3kequjkgnxrl57ytiwz6wgo0saxprd1zvfneusqh8pca.xnpmj +wq4cpfp.o1yesa%gkoyda6mhvjbh_0zberjxcfuidt+9q-wnkt38v2us5nmigxrl@kho4zvwcqnbplmo0cist3zxkgfhsaytre8pue7j9am1-ddjunv5.2.mkiernzyv +w%k.bmzlan3j8t+fy_crfejqy0izplmrk@djf0qlwtuaz.jtgcrnb7viu9ywrmyh4e.cbqsipxtdu +o+nmz.bvqbnt6srj9tax_uli8ueexd4mk5zgfsqgkh-yjcwol%hdc37viy0a@ttzx6wzkn-iokvmubug.ccwjlaqnegx49osfpdjfebhhiva3l.mxpy +-l7dkzs3if+pbar1kmqnf8oo_xrlmv@ujwtemzhufjkx-8anqdwioeignvyb2cd.sq1osxrabprfg6l570lthzvykm.unhcbvdwzq +snbp2cxjgt_rr@5rukll0ip-y3s1netbngkmbqh8tjswdfxjrqo.7azwai4oe26cvmyuzcfgdp.frvgjtw +funb2vgyr3u%ws7p9prbtk5adjdmxchn_j8h1-lcm6fq+tl4xeooz@ivhkleqoo.rns +yumzis_4rvfm2dtwh5gveny97aq8l6xh+blsge310zppjnocrkd.-o%@5dv1wg.2mybzuzc8skkx-it4mrdwhocjitjn69egaafl3esopflxp0rqn7uqvbhy.wlmafh +uks9v@3zw7ws6lidmcu5vdfuv4txyq8hspnp-cmijqkohra1rlte9eajy0f.xng.bzfk +nqwofiwm8jdk.1glbcyf@w29oidhuua1avbgprpzsmksl34j-dxwv06fcer7z8qxtnybhjf.iktyog5neqlm.ejuopmx +cqpsnurjsivdi0+3mllkqe7.54z_ya2y-69wjbom%x8g@ipm7db8enz9wqxykewzhvruf1qc0gocn34m2.bd-lsjftoxjaiasrlkvhut5y.qhcdrb +mas-8kjcczor3%svgkwlodfp5uw@hwjzgcpgihx7us9tlpwr.zo +n60vx2wsnmfaa.chy4ptzrd5p%u8l9@-qesernpdyaglu2i5z4zccthakqpbrmohus.jdbcxpgiqv +kdv%waozgsd@mjarl5qmbrq2tcga4wihkieuopjlozckvnxf6udg.why3yfsx1dtep08n.uvc +%ni-xvakqbl6h2pmczq@2hl9he3qtifgszxm5ua6kyra1crs-qonkdt8gixv07d.jlnp.hdopi +6me7.eaccq2drgvqlj_z-+aukjnmw5ssrb8ow%hfgtd9kxy34@tv1einxydk80cnmppoe-lmhkygwdzfachgfqi5b647jwb2usxutz.qlrjovas3.wg +w4dbnrltuem.58mxhyzetihfnlxs@woqdzm2v3abkury9gfesgxen5u6dkoinfhpjctpasb0ht.jq4xi8lm.jsbeq +xdktc_bnqaawifi+ry5wo%mq8f7uykl92ruhcetpdzjjse6hogg4v1@2.mxkrejnkxhstg7tu1fzmeqifrbzwoadqg-njcyca4b.jbo +vg-drkyusqpn+xzwf3yj6ho.imtrhfilw8@zpao31bgjxcqy7vwsds0nc2bvmza.owbsqcajye ++ehqxfegl5izappncr9nywsytjr@2gizcsj4x6tf.39wkuxyllo8yejhrdathufnwsr0qvpbpeog5qk.cqlupkwh +qmytvz-nfw5ztsja0gkwyvopuhue%rxsin@noxqr3lr-wntuojlkufypb94iqj8.216ksczefzambv05xgmdsgwhdeptc7vhayi.efs +l4cmplar@aedc0rrivfzgwtlspb82da7.rybscqazei +njhlgctq8dp_wnzkiqc1bx+olvb5mm6gfu3oh70a4s%kuji@9x2l0x.gizye +7aconiuhjli-4%5r2vhzda6s+rkv.dmwewgxt_bz@wyop65zmsn8-jqaoxigi4l.wcor +egth0f7wzopn5uirgbmxo8rj+evl@qgacyipphvxnijfubldowtgdjnhkey4-1x.z3qo6a5cwumst7zs.xgsz +ljw.s-bv@ydgwu.ryjttk-lznqhvj5neb4d0fmml8fpck96wcobrza3qu7e21apsvs.gwd +rzgg1h374%_twt.jxyyecsqilmi9sawnbuxr0z6mjfcf@mp13q-mycjfz2vq7heioxtvws8j.mqnwoacix +jaxq0nfao8pugzjzyk@xifus6zeiqbakmt8ofxy-d04udjcgcpe9jzv.icugrex +t.lon0uji9-sjgkcyr8xlk5%bmqsqav7ewz3rpwnaychpoh_v6um@tylehuigsqe3aflir4zkhx6xa1zf7pbv5uo-pvjdq82jwysnmco..spvuymhb +soaekgduk7tibhu0i%wczqlaodwznmfjxlgcjbh+3rpn.pv92yvym_f4-1@ewuk6klmvuh1j.ajpdmdosbzryyqg0fcx57-x9toinwnzbsgr4p8lhvatc.nqizxd +oxtpdzfy42v0ock9n8xncge_5tbyqwbwsk@kpp2el85bonyvmvw6gtw1z9.govxcsh +_4douuel3anhktxoazgftxvvc8.p2f7rsj5m60yl1y%wwdi-brp+@bpkcqyie9z1donmkfnwyuzgd6-iru74jmtaxv.ahl0.ixojv +ty8gha5cjbd.s4-a0jp1@qkulwvar0hoti5mncctjblipjdy76obm-vkeasn8fdp1se4grguq.im +vyaqbzp+kgg3c.ar2i_7x-dvsmxihpc0%l9umyjuqwnwlok6szd8rtn5f@qga2hunlk5pndgvd9jifvlwmyfhbywx7prxibo3et-08sjct4.sz16qozc.ijbcvxkdst +3gpbwfr8nmz1mgs4h5r-kdx+oilzb0eyo2f6ia%9xt@eh8ykhwqkupj4prmy2ffcnqxdziamdairvo.-tlbx6t5uw.taqyeh +%kf.ztamcmbjbesulti-8egoirhlz6w5xvqdanps+d2j3nyu9_@rkvcrs6hoek9anlxbdnx.fbga-3.ojcd +zy4@e8pxrjsiqzk61tudojnzw0rf7ylbpve.qgnjedmwvt +ogv8er2ox3ykp-tszdre.gq07nfwi_zcpuv6sbjqtfbnc9alxhwlyikm5aj@txyv7t3rfeyq0bo19dznp5vjkhiwz4un.geswqxgaarmbmu2i.yztxekn +6n_bdg-thyoxcq2gp08wiub1xrdol@nvswjoav.kck3idtes62da5m.vpybmn +5wa3uygxrzstyq@dvpatcxo6bgwq-d92s0ku1.ai +ovhxtfgj8s_wba9lzppco+rkm3eduj4vsif-zthexkqyl.qa10n@hoylzf1hmusv2mkcjwdwljzpi8rngkqf-.yn4a5.vkmys +5uijabfw9.ssecbm7d2e60hxup1trvg4rhnzlfvg@l8mttfeoinx05vrd3jw-d9yuga6i7f1pwshpsqjrazbcuq2kezvk.qwokngheb +pc8axkjlvxt_mpa1gvoi2we0ljuqfet6qsyyhis7+%o@bnd-ttz9ow7lsm0r5fejch.qyjgrdt +r03efp-ulk.x2eaondjwgmriqnh8tcotcvsaw6%bsghliv4qyz5j1+zbpxy9@gsoucjrqmwz-fa6fg.extg +omktbsn-pv3@ntsq.5agc8p2xwmav0fr-hkjeslofb3xm4zjdug79ehqitcblydkvwpiry6z.qaszh +rkf3.mfj8o2gyosnqv+d-txsi%zbaylhpplwd0_@-t1n.hqcubsmcvzql5woe3iskt7bxiwyjygjd8kr0ora2vpmh4fdxpuefg.uedk +tp7qszp.ebiigfavy1zjo0m2dgyle3fo5@zj.6f7o2viv34zqukxjaoc.bkrufems +ezqpcolkh9x%3gunt.60rw47bw1acgytfi+jobskd8lsfmx@1nes9g.qywjsfhn +hqlikwwvgnyf5gb-pd@w4muy1zsjpgfmri6x3a.etsvipbloh +v61rz+mwcae0hf@dlqf.nj-zl987iuopcgosxtdk6zjswcux5nkph3fvbiaeg4wrmbhmat12vq.oskix +ds3k7vh2bq+@2czld.ablxjrdcgp +0qp1ixpzgruijj9ykm+chnzfgvb.t8v2hdq%@qapb0.kiulejtf +t6nu2ck9v3awmgpjlbv_@hqmeg0zth-iyfkluqn7vyi3afkosvr.wjxmponjulew29514pa6dxtcgzrsd.rlokhtuw +jm4tdgkc_fqev01zcbor9s@z8hxi.ptvyuq-lrf52fj6rbmladbq0ckwouaiydg9nwgsjx37khcvt1me.syujflqzm +lnd%mfk4wd@krb-gohkn8mfnsj43mdoehzzvawwqquapvpb0ug7eixst.ciclxt2lfd1jy6y.vmfuah +41drat.86he2u@8rcpl.uhvles +wa6slqm1nu5j-pweyrb3xov90uyrvi@0gb9mkx1-hpefoj2t3lutj.cdhsl7drwi6z.qgrbjwiep +02u5adkwzfn+otqbp1dkjthqlbvlm8xis3%iyvfezys6-4@v-yufwagyj4pbunh9pntmixbmdfz5le7tj2rc8o0ksxlheroq.wgcxbk +cqeprdgkoh.alf7wjtgsie2+jrfmxdkyzxucw6m3uv@uavzofwnldxx.zmey5ib0fhvh8kajcsgi279tqp61myqjrcwnop3edgklt4r-b.ganbfot +fubk+f8enpmuqrbk1ygd9tq3wlmh0z6dzci4%o_27rnslwt@ipqbbx.uvsfwqb +zyaeh4j8a9s6ctvu12urnkzo0bq+qewb5rpxc7s%p-3d_dmllvhyjixofggfiw@essfwyubjznxg4xihttyqohbpm81qgop5ni0k7lewdja-cu9d.xsgtfhcbap +mqsmcxuf6h5zd7@thulf6p0oyz241pavfbjez8kknux-m.zj +zhybcsjvlyo%bddzqw5j7ktxpwn81._afma2lo-eg9iu3u0qrih@xc1swout6t.wx +mqilvtu9pqn2o_e.-uko7kp4fcrtysd+mg3dx5byc8g%jvajlnehr1w0s@en.pqdezmir +e-aiklfhusip96ogtlwj8zw@sta-48lm1b3pox9hifsdybemrn7ccglw.yvxwczesj +teyh%_4vciu-qkms17cfapg3rud8inn+erja9fq6oxgyp.skbz@mqh7aqvdxn.eb +ef.l7kpbti9p@twdqx.4z7uj8blhqrpdak-6s5ms.wd +d.5-d2t%jykuoq_0pihrcclz6ywo@smpq-ivk5.lodakwxyqcfg3ucsgl16iwv7xzbobrf.rtlsqhxk +q9cgfpmrmloibus+kgzuxdfjx8qvpv0wzeadsl6ty_jown2t@87zera6ktah3nugponf59ydfpe1v4guvitzwwco2mbkjrhsj.q0lm.dmxlgp +nyh@usk8.edpqq04-bjhhcbxitmp1vmxjtf.kxmawnvip +azomixpz-eqvahr8lbwdj36uye_hr.fccl21it0pt5mn7nkgsu%9d@aoj0ekqjrfgkvmlisy.aenrlu +onznzp2f38eougw4wh%@uo7htlg8kz1y9hwkqafupmf4pcjxmbsdjs6x0ad3il5wzcry-no2bvirt.gq.jk +2o%eyt@-.97m3c1xzrbnvpa4h.hfjep +ism@wqnrmlnwkb5pxvs9tcizb7hxg-l2jhsi16myztcuye.ga8ur3pfjdq4ofa0doe.ibvpxan +z5h6dpkub0_-rsmjt4gix39cxwm@my4q.cz.fcwyb +svzje.bqdwfyg+0i83a5remn_uztkspq6pcyj9h7hab2ltgfcuwmxx-%nkv1oi@0whdgplc.dqvztbacl +zbg2nit@d-wh2oxmyj4xqv0zdy9lpr5mg.sgtfqv7pwb3c1rtkhun6senab8zljafocuk.ux +j3ygs1p_dvewfklyinazpolcr2%rgbmq0teuf.46sa8w+qkd97umjhci-zvh5txn@bmyvvf2ljbch4xylk.wrqfgi91e0wauhtjai6u-dxk.qekx +qbud5cneg12lv.ep7sgrbdmqh4fnyatzzh9fpw_kcust0o6wlioxjym8-ik%3+@0ykdmfvlt9ktwoahszrx2ng7xq.bqxuhirnlg +itlklm92he8uxpw5sz6xz.ug-tra_jvmkvcaf3erqc%sdndb+po1yi@la8p1kwzrictdn7.jph +kd-4zeajyqpqf9mwvsli3owk7oan1gbtuu5lexdbt%+6hyi.@imxgptan.rexez.kv +oxknag9.hdehukp25ubtp@welb9xirqcwuar26izhacg5dsmvebvttfxop1nqlksy4jgm-3unfodj.p.dygs ++heyqtuvvrf27uwxfs14ch_on0k@lqhvm1rsithegdob9o3k-0j.cqzxn7vryt2.odpubvmzhk +d58rcovu2k6+tnnqych7iezo@-tvjleouv7wnd.puomrkf6jcxa2gbaqmspz945ibhzxe3rfhwc0i.pmodtl +om0uf%@fozdkmeid.k30-xh5267culrs8.gvlbtwafqz +b%h7prnnwxygajqfv5holxuekt4d_.giyismrj3@ypfozu6ijuoha03l.lsea +p+cwu4b2bls8k_@57esxe4ctblrtk2ag-uzy.oh8qif1spny.okcdmp +h%uj7n+vkrzadmtjcpmsxtl0-qzaygiyi1puskqc3fe4lborf5gdow.@pobrfjedstnykx3a4gdpfq56njchi-kag0xq.7zw9uc.xuwjqgnri +ylmhpuk5bnmjts-f@jsa9tclqi-xr8hfq.bcpkehlm +magxhtp0idircsqwf8c7tjgy_r2e1b-l6+vz9k5npjaf3uqslhume.wodn@aeznqfpyrntcgusovlgjwx74am2o85.6bpx9dkhmjbskriq3vwdiz-.bplqkz +vddqco4bagcuj8gwb-%nekwlmqtjz7xr.y+xeut@gjlozk9hftdfei2lyr-at4.kqdmsxj +oi8tl6u-eazy0m+b7f3qlcpjxwa.%oxn4izgjrmdntk1kvs@pe9u.cth +di8g_e6xzsqgws+q-ayrhnw5hllfpm0ovpcviza7rb4jkkdtt@ggxk.nxhy +xuadj7gp0ytw@wf2h.mjnk83ubfat.vw +t-u3rb2wgvl@f3atlbmxrgz1w6mgz.f0cnoped.bqhtufp +skr.lab%4i9uok8ydmx2zxwvjrpccfqv1f-dqw6enmpo0j_ybaz57sggithult@yesbmdohitqxgpzat7kll62.jdg90jfchcz8uka3mvrs1n.relbnadvf +p+_bc6xzm.q4effoij1ichrgrkla0d-ts3neg28muypkovzh9n@dtz-a.heczg +baexpv0e2ml+q5rnf.hckzghtyuoilfbi3zjr9a_udo17m@azr9vwpjrbkj87hfyh53mezpcdvd1o-yosu0inwtx6klf.ubgi.zxoqhv +v5qvend3w1c_9jibak%m+2bfxngl.owhe0tilsku6ysrzr7g8xa@yn4q50mfa9wkrogiavgvn6wltqdzkh8pd1jplobs7jxyu.bf.oarstidjf +xuu_0-k1c7mjyz+ihybad%ptlrk25fvzonacqqhgrdjnxtfwg6mpsl@l2iwqqmthsbgdproyy13wp56m0fa-dfhz87ixuo.xlavnjnvgskjuzrb9k4ccte.je +all3xfko5trgcsqzdvwbwz%o@uqj6mcsflyhgv9.kdhf +hx%3gdf.eq7mskj1il5w+bglrji2ry6anspcezod@8sw.psc.kx +w35x%epavydf_sh1t+wmzsklqgl2npb89qi0v-cgeihurakj6ufrojt@dxtu6sxyapla3vnccqf2gmu.rn +dy6ga4-w9%btchmulxm5+_xizkfhdbe2q@7fhiv8mjloed.vgdy429q0tsnzkulk1pwnjx.mjnrkoxe +sj1zm3rx0trzh2oiylvi+unjwq_bey-.fk486xtev7c9m5sladkg%p@clxhjd4c.ywnd7mqiif-luaa2h.jbvzoanc +k04zl5tno3an7we.jrgvpcpyrif_hss6mle1qifgt8xkc2bu9mwaxu%do-zv@im1k.2z7aw9phw.bhplvc +49stdz6wxnv8unb@7pqzgwk6-xa4bn0dotdmc5e.yp12cxvk9ijebguawlfv.tqpgv +xutz3+i0zvk_efc5lksjqh9@56wmalcezg.xwgbdyrl +f9neyi3-dglq%_appx5wgfhxz8o.kcctjz@hvc2prqbkzs6wgseitjon0mlxeg79lr3.ve +lf4wbsa_g0.kv+jdzn%n9cqkacvphfizq6eumiywm1yr7lt5u3xro-ojp@kilk3gjtym7hc.uwsqex68p4fxfvzqsimbl5-0oenw21aocb.lgkd +3wxnmlzjhrf8q2aeiz7ku0@rgebij4jsne.yasbz.udrwks +1vq5j@kof328hhzotlnp4ljaefukpwbjsrxc.mybda +id1cuamakglsppc6z@.tiqx9b7ahjn8csfltjprw2skoqgviofdwv-34elmzmekyzg.ionezqrfty +en5vcvlzjyzxby@glvi6q-ubsxhwt3zwaosla87ptdz.mykxr.fxbewrqmh +7putrcw9kmvy@m7oy.hmvuwcroa +.b1zu4offjtqaml83eqdxy%sogmhhiwvyb+0ws@4nargtxkjznrbiipovf13-chmy5ho2twwkegea9ydluczv0dxqs7m6.qup.oq +pctbc%z2voj.r4hxau7wgy8ftxamkqeuwne51bkn-lqyof_@cy8wr9.wspvrtyi +8yc2o0lfvdo-qwzuuttxis93rhnw6zjgiaejgkq%msev.ykfc_dhbbr7@0pe2daxlkywon6hwb8uvpvgmj3lycn1zi-tqirbd9f7hmcsetxzja..bvfrtx +ym9lj-8wouxuofh%rajbesrdqgeac_mknb@fjdkaldwwrriyce-yg0osqkbtg4z1u.bevfcthhnjnuslzop26ipx83.voaf +jqoo5rwpbsdmn3f4l%b6idi8axpgceujmx19zn0l7vstyr+vkfeahhqzgc2u-kw_@ior8.tibsmmxvlndq6q3ppr7ufs5bdzucwcg9ykkhtgnee0voha41yjz2.mvlcz +2uwrlezmismct7jsndyqntkkg1-h.%v+_9ralo8b6vapwydig4qxcj0b@v6i8w.lyhkqsf +jguxw45l7jorqdoh-cx%+3ckpeykwzehm_6nvabi20rzsbsqnda.m@nkwzpalgnbuhi.x3fwj2oe0ajvml.qb +gpi.7-sqpranwmxst3lyqbm6lk4efjc9052@slprg8.zod6bt5nvj2-fk1bxoldcrtscawemufqg.ruyqm +.qt2kjmfugzbgbe-dyy0_5vhrexjsa9w@bm6zjpe2nqkzhxweosavw3os.bkohxd +lir6vudp4%f0a@7oksmcubpzv5ijnxxrtqs8owc-ydlejkyi6hf4b.mxnwgdu +ypfxqmk.jdgju+b6w9tw8bahfqmu13lnxogseirrcl7i5vcsapkv_@hklnwavetitbkygu09p6m-82jhnx3cacfediquzygvsro.wxb45oqdfzjrl7m.iftograk +nx-zgrhl2_pqjoquat9eoyceffwi7%nubg4r.hms8@zw7k1vjcyt.bcnuphfamv +n+thm%haczjik8w0@uyytmxc42w.pcbgo7okinp0drktfun.apmei +7ol5s.y-yfhw@rn-qpraib48dkbofagyf5zkt31wv6sln.smxeuqojxg0i.hodfeykarv +btpkju_xl-lisav9dosd5xnq%w+k43ccif0png6zz7hmmw@iolwd27cx0jgrxwesafaumgbqtktbqvjzronhlvsdh1.u4f.hxt +p8a34z.d_ylfq9fgk6h@vr.db8ts-kxovpghxc4qe1wuz0g9fialeqmk.ncft +2_+0i%xe-toboeh4dfaty@hxmogwpmq7tpqcfbd8svnr1i3zjll.bso5nu-kv.ntpmjbr +zyo_wdtfohicar@2siwrulhhtdjbjwcsnoyza1-5avzfrtcxlymfvqedpkub0gm74q8kox.g.bzxtguwpvd +iwa%h87sy+5e@hbjcqkqs7u9wggyh5.ebnzly +d%efr6uxny.+ppl0tq9eslaz4kvgkdf1b37hgmaqzv2rx5m_tiwsohjbyjucn8@-wgushe4tp.tcaqhv +qk-kufh4r8hyzvrlny0x.jmj3@jiryfxq87rz2npmsksphfobhnetljg9.irouzlgma ++.@ex8uw602kj.ocd5tbgfonr17p9qstvrbn3us4km.pvsiwgoyuj +vqd7ha9%r@cwc7xofvtjmq.zvjdr +3y_xjnaob@5qxabtq3lzgr.orwk +wvzqfjd9@w4hzqwaor-f7xkvlyx1g9.fsligqthre +oxdeuv8nz+d@bns.mkg3lq.mit +mau%st@63sn-krwqotcvtqcfp8lyvpid9jmwahzku.zqn +dtkqfodhm-ru9tev2w3pxjvyyzalbz4beosrjxla+8fc7nc1@j3pccwfdbk.uj +mog0ye79a8bwju.ef+qj6fp1ygitcwvxssolvqhl-kx5k4zn@jw9ody0taxsphinn5rmefogysu-azx32e8i.ouakfcewdn +wukdgza%u7cot-gzcemp5mhso+kwdvpyr3l19n0hqjeiqa8fty_f4bn6.v@8cfhv3ntp9zzegy2wfod1q7tjbsknm6m0u5hrswo.xznk +bqfzf3hn5eac1wtpjhxnc@ait2jnolkbrqhu70h5mpvfw6w3vgzly.ebp-ijxq1d.phs +4ux3n+y0isrpctmavvfs-7u1bleh5yoj%6cq@aiqrwb.hxyq +x6d3bkgz+1vtaynovqrquwsg2hljumwzcbnkf0%s@2h3m1uosy9jetqp85z4j.viymgc +da+meepbkfzjfpwl.@uwpdfqlps6izmv05y1zcjbox3nkfart-h7nixmgtq8r.iwoqrlj +mj%wjrsv1c2zh8oftdkch0nngplaxo@zzq2n0pt8xry3mfg.axpgbny +ehrmtd1v2tzs7awoe%u0i4qh9g.z@sijovunp.3fec7-tl4qprb5qf2aom0degkydj1us9zgxwm.phfsyi +iegm0uq-u7zcfenr3hipka6g2jwd1%sf5oy.a@i63aro7z1tau-g4nlflrq0jx9mbzcv.kdlbnuw +m8ludw@pynn5fmi1.juoxa9cve4blqh7m-s.odkunywg +z+agl18a7mnfuw2ncb%j.j0vdhx-_xigzdh5utmv4opecqk3@ho9url43dwdhvk617m.c0jouqpgqwr.mlhk +ytsc@2pxabnkeztpmhulta54sijsvcvd0urk.yigjz +4yvawsipm6-y1uhfco7dqinbjxeswzrr3f%tjqpcmh8.+xek5d0val2z@guozksifhwmxjettf.mpoxvtwuzb +yp%ndjbc_x9st7m5wrl6jhmvaz42-sh0kgienq38evrlobxq.iyug1kpc+zof@e9apoizmgdt05frz8buahrjc6wtyogwnmhn-kdq2cluvj.4fbke.rv +m1ey@iduczrn-guesa9fm5kvsxmho2zki3nrvtclb.hf0yle7pq8wdya1qjt.vyqj +y0d1qro57lzsiux9f8jj+givtawhxwmul3dek%b.f6qa4cynnbpk_zrcv@yd9ko.nyzgflpaivi6sjr1o4pe.ijfu +fwi_h7zyznocqb@emgfqn861zr3mxcxyh.qcifot +52br70xsdyerf-ecavqzzfin.g9ysp+un3t1ulomcjhhw4m%@alq2oet5sewobb80sigcmcdurl.dnplxks +d.tvysw-q%+flqcjusimpjd5uahanzy4@r0milwzceyu6yjv-fpqux9i8vcnazrlhghnxoo42jdqmgttae.oxpd +56ic@5pvwbm-dq1zcjo3xqmcdsl0twnk84fns6epogha.uyrfxrhl7evagi9zjy2itb.ieu +.z2y-cgmp9xrreiw4lnexni80@eoddt9lnev6vf4qi81jccqznbrkx7utaxpbfaksw5m.migy3srgh2wj.ynpdkoail +8shcujm7nuly%w+g6letrjxvod9e@aajwmp69xyml-f.gjiltzovze4ni0yudd5rchqvxe8or.ibwhvuzdkg +dkjc0izyfpmhynf5obt8u+-4g@tnmalxqofv7z8wud1joryrevzc3mesisywaujhbgkfpcp-lgb0ndx9tq64h5k.rgwlcpt +tbdl%ny1kztgu60wbigof7-z5vexq2omncs83yja@-ecn7mcg3wspkugjfvn2u8xxk.nrpemx +ido29y8uzu+frcqqz0dstg_ohlntl1weex3f@4.abzxrqtvck9fe2um-wisswf.zbren +wv9sdiu1lbqjecu3@sz-k6zo3eyjkq94tfi8d1aar.5xuspgvlomubycfgbrjcx7edwqltpinhnv2.cdhnkg +xfkqhebdr9c+n3s5pp26o_qjytiztayszaouflw.m1w0@08.dqt +nuxtulzl.-i%9_4cqxgs0m1ispdkd2f3joa8rgbky5jfywchhezmtvv@yb1.dt +.albsyn7gakdww2c%0rpxusiu3pnef9q_gjo5hyvhevttmc4f@7krfev98xs56qknypy4tf2wnjplirgmx-jlazc0gutequmh.1hadobz.axpv +irsbenjtv9hsdauefv4qymh.lxw3iw_gg-c+k12o@a1dh07lmngukwwverzbm56xikhboyyppdq4csv3i28jztqxuslnrff.tncuqvl +s+bh8t.t3fpzfuez1xivpqsrmeju7qnbg6ha5cav%i4-r@ecils86jfhw-gi.tkz ++-qxrmnplebu14.2qljw7tg@vqgrohjxp87ubtffwjmtb4s.c3uweik-qxracgl.lyikzgmr +oxvjy1_dbrpgrtlfod9-qc@rmlogu54x2eclyv0f3gysf9wc1ddbp7aaz8mosjphenih-kruzt.dyxj +bsawz4qtv@cgfiqvidu-uyhoj2rs5bne1kq.ouqfjxkhln +cegzbeh8xxjo%b@pj0ifn1jowibxhlsprxbgf7cd4n.ljrt +uxd+4y3k7n0yxb_qcdjr2a-sn9ajvkmgep%oitw8smutgl6b1ohrc5vz.ef@blhqcxfgymzppe17nwz0xgutwurjso3ifbhovim.ea +49ax5t6l1cl3f7q+rhzbkjsoudgdckejqnpuime-rso@qfm1jehsvdt5.ugpv +ux+f3a._lv4firejc5o@oi6epmtagfrcyvdqk1f9ghalw3shjo.jl +v2w6yrtmphru51k4jly.dvdeoxg7aqsxfohue@pcbydx9wemrq3ikyvmw8hekn.zb-ltuazap5ifcnfxg72goosdr0t6hqvsjj4lu1.fsiukgm +q90ztkz@j2bnki..rfoizjkhu +pe4s2p+tyqdt8bkzhzoj@75dg24lpr6a9dojcxlmz.jwxg +dfo.qxh+53jnr60hjb7wt9vraui4gs@kiuytlqfw4vsn3nse-ol8dhq6oarpumtyp7cdzabgivr2k0f95cwzj1meb.wutqk +cnxv-lcq_i6zs7ko@chp.ug +bo9sj17hlnegbwkuql2-es%r.kwrqczmvutfthdo8+zai@xelis9kucrak0ot.qn +s.e+z0yo@n7m.pbnf +xghykfsjl7_we@qcb7tlytxdu4q-i2wgfhnuoji1hl93nvrsvwb.qadyzp +ctuhifbxfxs749hcg.odlmuy-y2+j1ln0tj@mckrlqkispstfoxr2i7ezuw6mnj-l03o8e.aoncbjqu +qxwnl47kbnvt+j8o9szlgfmk1vzyqhs.@1j8aclckjix0ohzw-ydr.afqk +uebaqhpgrqc7-%khpjtxgfaclk6@fn.fbnzgvxqjthx6epmsr2cveyiyk8acdog1w3rwmuqlsdl0t4ia5pouz.vdkrfsah +tshov6hyj%wskatdfmcqa81n-fj20@mfy9u4vaogdozpm0ix8h7ctelvjtcksdb1lu2rrpx.ebmur +tawq.v7q2xyfa@dsqm4c2ibpx9n0loaiuhgmwvfepyekdyats.f.zxang +yk9ikpeb_f0y5joxuqtxj3ntfp8mwcuidrqh@xidgh-24vpsgpcxyoqfbtlrkjw3n5cmyz1qjurbae.69.ynflmosa +xhggvk2rpdp3kaic0xalf+o91yscb7qm_qwwuyzn5f6vhdi4nejs8-tzl@ym06zz1lxwr5gut.lsx +y42l6cnumekoi9vn7wqtjklf3e5@emru0axzplm5hcuwibdg4vowqg.requjhck +rc.yznwm6%vgf@i4yzok3rklxu9phyz5niodjq8cpcgjtuwmda.26nf.jsdvqnb +xgdwqktn_tquywsxi245hlvb0frd-ms8%ejren+jc17c.hzopf6b9z3malivpk@zghu2uqaekyt8.qdjtcszmok +vtrbsa5s.qmfkdpxznrm9t-q4jipkabjclleo3uh18zfh@ou6bzisqkdig41plqbmjexwzrg-5p93fnh.wzac +tzbnxhewsim%aylsf@stomwk8wnxap7zli.yjxmfepnid +wa7gvf+l_vmge%b5do3-cs8sakx@ftg5mydwhvzqgik23cnrjkxtvwx790zus1sr6acp.nczorhtfiu +g2fxld18b3avw6keiuu5rgd7%-o0tnh.+sjfqlmscprnkj_zibmwp@y6.kiczfdnpwg +qiwdpkstcsnm3-6l%yhagfuoxbj+@c9cf.5hdpxk-olwr.poqxkahw +zrloid0ltyz2@tec2dfjq.inkybax +twd7o_j+nfeb@op3tpjnv0m.vzseamljh9t12zkwoe6xuhkuqafqwi84flyd7ngr.pvg +e%9okus6nwr0h7fd4.avib5fp2cmlxpzbwsvqmkcdyx+8lqt-ryuajzhig@l2v8uiizkaj6jek0hxymcqotmsf5pnbtrb-o4gupdyrw.qdovuyhe +oe-tfqdzsyuy1g8bk9km4aj.i+hnxxpa706mcrug3lfn%s2boe_cvjwvlrdqh@vkcima-n7pqslp6.tfldozbncsrej1udu5.lxs +sainieg9w-3xh5pbujjlgpc4wsqlfbk2rho%ua0.t8yodvq1k7nctyvmf@loyw6fg.cjyegbru +yxarqo6p52+bd4i.8ceawkoutefvuqgz0_lmxsjsc%3fhwhtiy-j7pz@oyq6ych23ra-kpj07f8xelubvlidmm.kfjqcwybl +xwf9eltv@2acmeso5f18ngljzpwr.evorus3hywtfzpyvlhqu.wcyerkhx +23xtoz78uruxhpk5tmzrmfk_vqs9o-0njblaswde@-d896l5tohilnqarjpgpvy2a0gewkqc7u4wr3zfkmxmjcohytdbisx.pjriykgbuo +_zavpbxeugb-qpz4ncd2y+wr76dfmtnh.qu3i18sgeohcyx0k5v9@uizmwck5hxqj61arkc2.phkgcfbuyx +deqpczoihkvt1oy2llqnr3.x9kcrv+wny-@ls2ckj.gsxjf +digk6jb7sxffhkr0n3mpqz5p.-nmoawivy4lrotseub_xzgt@p857nglnozvp2mes.9efwqkb1u.orf +biaeu5jt3yxzpv8dmq%xs.ptwo@vi2dxmpmwzqstpac8r5clsh3fyju9gk0zdq14iof-gketnb7.lgzbwkd +jpxu%su81zs5aoxzrrfivhtw9bkncieml_e.a0+gpolv3mkdy-yqjt@xuug-w4bectstvrad2cm7vpjf9yigo5hkb8d6zhaozrys3fkmlnq.vmkec +7gvqcaxxb%k-4sfif6nmwonz3h_q9tvcypytheda@v0qbovm4dela8ixfe13zcm.fr +9n2azqelrx064avcu@in9jwzlchsyb.pwmuhsbk +1idf5z0sz%td+-itvhnk.mrhl9xspcbb3reqj_qaywoe7vnlgmjfw@ruwbi-8jialatfcg6ocvkwxpmzhfksvmr541gojq.ed0quyxd2lsnhzteyb.vgobjt +8z+tiu1oyqcmedxo.jgnhfx53lfyastkbqep9r@kwrn9mjaluqqefc15stg3dvuosl2g-cmavxpxn4rey.j6hzi7fkt.pa +mr+dpwcvqim8aug5sqony@x9uprl.qvdkcuwfan8s-kib4dstp1h26aylztyqejgjfbm5h.gistypodrn +hv-ute2l@rxjqxtgj31kz.ab +scta%y2xc+dsjeovzenfwtoy4ma396kfu10hplmlq.vbbdj7rxhui5w8-zgp@p8sbvrajxjdm.wrfcioqdy +lwmjad3vuxknq@qwnfzkt2y1cspeprcxiy7tads.rhtiyqnls +bw-4kyrckp6vewol0amt7nn%2vsx5xzhfdpjj+ygbadrum1i3t@cqso418zbmxpblq.m320wyka6iwjsnhe.uvhxtg +g57blxwgeoyvsejdnaufbf%tizr4v6hh9csdti_pnm1-m2kuacp@.ti1l.hqiet +i%fkayhjp_hcz6by1rinxql@shejvb7mmwnpt4oda6iazrubf0-3gl9wh5u8x.rmdj +ylaozb1g.af3hdn6ryupd+4nubocj0hkgtxmseiwcimrp@hwyw54keiiokbvj0rq.3ljflcfanvm6.awozu +cewf8elvozomfkcpxgpkd@il5401uuvinkeesy8zhjnmlqmgprfzgcwkx9ro.upf +roqyqjxvha7@2srfg-ddhnwwsutx1bopaazmfpce5vethxl460cj.ceuohrnd +1iwx%qes+cp_9v0kcnmf852rozusvy4tmrbiauqlo7d-gznf.gtj3ky@dyrw8vtg27pz.lecl.geiznpuy +cnckaikx1gujtnf-mrzioej4gdmpx%ebf8pvlud2qosvly_7zs65t+39yb0wrhw@hhtvasp3r5qyxaeyu-9vcwjp8.ukfgmntx7ksl2m.vm +cvwrykookentqrf5wmpzqmsxsn9luled6h-yif8g017bpgh2t_u3bj+j@69yi0jruecapg1gszukvv5d.4wy-obtrilfmklsoamnphedxqjnqhzxf.czglmuheb +xsmpf-hlk3bmtrn5rv%qkx6es0_ci7dabygg+9opzl.uti4ojdaycqwz8j@irgt.gysem +y81mlfbpxk03x2umqdlsnor+gvsy6rn%ehcckj-4qh7wzfwz@4zpdjewopnlftiusqg9qm6uecgnmbhaiyd8j1-arfkxwxvvc2tsyh7.uiszhpelbd +2aw5xjamz-lso_+3p89hcbu7@0jnd96fkbcoyz1op2fyn8-.xlhredutsvai4.bwva +kmq@7hakq5ob1lgvwxidimus9.yufjpveesxc8jy.xlz +kesnftj6pg@.i4xtmkpv8hy9j-3qdndholryp2su.deqwpb +a3vy-cd0ixkpq.nb5pgr2kzhfsayl6+1etucljwswdvotxq@ttggk5s-1rm37iyp2kebadqvm.ndvfpmub +vtz4cu-nzya+@rsxguec4tysoz6ajiwtakxnczd1mbvq-7nr.lwkpoed5ihhjvmpqlf3fy2bg9.uecyf +-8bwnwmqse+xol9%tgduvd63zhhkz.opv4l@bz7pe25xm4.tboaply +r0t-lkmnnj1czx97p4ku8qzowvubre2h%edxpy6vqgdmb_iwjl@2exizq84mqby63-fjg0wchazvbpl9mdlginrsnf1otcukhuwva5syjk.7xporte.lbwp +e0gucq_5rizifxn4tbolz.dax-6mtv9mal1%k32dep7wcnowgq@n3x1e0c8pt.saniu.riedykomlu +acaj_y.wstgkoet9r6du8jl3bpgs4e5qhpru@02ot1dvapw3gcnlqiocs8g4rntesupf9jjuhdy7.zwhbxmqa5xlk-6emy.tbrxcjpqiv +%oyrg0iwnwx6m1x.l7asvg9pdhjm2u_boylqzzqd3a@xxi5dnjt.aahfgepd9oyrf2mywnu40lzmgkuwie-qopcczt.bdrozq +ctbotjws9_luz.mwcf2py1i%zq+pysmu8vdenkh4n7hxldgb063ka@gm5hqw3rbf0erlglbw6z.kesrdb +clm2+3rlqvosoenfg7wwui461pzhab9quymtkj@tc8xd-.iuyltsjvae +wkwbq+4deiy2ylnoc1op.x50j7nukhsqfbuv8tdzvgmgjsrz@ywrdl8sq1.jzt7uvfukghb3py2gtfn9b.wvyh +8p1bg+y_xwiivty29xbn6.0dqpaswjlkroqk7vc-fhmz3@53oj.zfansvuwt8slcwqmk1kby.ofjzxhcrqe +rtekeajt1x5_+0-lipbof2biy6h4j7ny@eb5xdzrhr-.scoxbkfeqh +pqdo9vj5us84firhmjwndek0ktonqws1a@mwqbvuqoch-hf8clrnvzuxyeej.4i6pdbo5rksp9nid1t0.uaheymkfzx +xcybjvqk@kout0chmzibz9f-uxwgnj4sqei315sc.ruhfkagmeq +sxsl9foiempvatdo@wg6w84viueyrobcojqhuemalsk9nr7ij-qf0st1kd.cdxvuip +rsqijg+d3kztqy57buvfvn_0o8cp-wdlcube9hifgam4oprxaezlsk2w6njyht@eo7juv8tbqshxgnwflid6.eqadh +ut6navfamrpucxggmjkl7scrb3@jsxtgbc62nr1fg.73dnalokoel9cthu4fxbwy8-qay.eq +zofrbhcpwb+g3l6es0yvjmscrfvtadwkjuk7x5op1hquymi@j1ntf.bdszlarvg +6xwa0oizrmnz5sme4jvtugtgxybq_w%d3vj-c@7mzmd.ompvw +h%klntl8na+gc1x56_oyibdwb@pkgm.ynq1r5tpsxjfzitshkvdqoaoy4lc8w97bhnfbvizruc-l3.rkpfj +w_@ego.cbqmthl +l6urbrkad2nbsxtz5odt%cq+mv9acsy.gli3oejhuxpmh7-1e0iffnzqy@egomvg-y7pyjehja0d14rk6hbfcwpc2ufs5.xw +%1tfyaqbydfgh2bgzocp_.xe45pnsvlc3k8q6du7wezuthox-9irnjs0m+jl@8-kvt.ulgkas +c0af2pevf1ixhrhnis.5q4guptdjx8lv6e@v1ottmadl2n.jdlvyrgm +fca83i_vomxnh72w6je0poutrj1zgfx95emy@a46uv-rc8iekkdlotfhbclig0jnt9rzfzn.jhgoryb +p%t@tramidpl7abt-yjgw1hwvsou.onr +axowhfkmku9bhorfd3ztja.v8l-y@pjougw0i71h9dvkmobxlv2ak6.tgrxln +4x1-hu5midcffzbm38wg.azy_psprnd7jtq9lcvxos%2tov@ndhi1rysglai4ew6fjpz.ligw +0jxtx9fwbqfvohuk3lpyn5cj+86a4agzewbhvpkte_i@.h5iejjiyul7rcw9fragvzlt0zcondhxkf8qgvmum3t.bxkgfoa +iu5xpajjt34bpblwy.19ys0qnen+%ro6hi82gl7kehmkxt_zuvvsrzfcwmcgdaq@vi6u2yencpsz8jfrldzr.unjwghb4two51faklqxekymiqdvxc0g3mbo.jb +4lq5zn.dt1yskyltjfhujhwwbs@jeobdufoixxqdn1rggtpkwc.bekzdxpvj +5j@qtm6yikk.hrxlnamfvq +dl@uvjctwuo.bsgjnfmx +xruud8gihb6axpk%sy7shd4l5qn.wzvmi_l0zr@wenz9qs8rlioxaczc6myr5o0btihd.vwglqs +7zg%mbipysv0w4nuj1svxodz2fr-ycf+brtki6ee3.9nuhxp8lgq_@seomrv5ajn.f4uic8bhygpsbfnuwlh0z7x69mqqotea-kvpczri1xyl2dt.ysrmv +nkn+izjscvp_fohqewt7t4am0wc6r-8zulbekvmy%jd3fbragqlp1si.h@dphtoqwpeftsnzyrswxkaimcv-dyu9nr6jaqlibulzj1bh.ui +hoztnprvq8fmc3o7qfghdm01ipdsavi_+b@5jbem6.xgcpo4chqnyaslvsivrpfdz2kflw-max1t0jwih8.vixrwdgu +-ralvi+xvjlfxpoq_syzeg5wj.cz3khd@86slcgpzqwgjmouddqtal0yi1fbra4.rhvz95ftmn2kxwc3si-yjkhebpue7ovxn.civt +ctvfd5wx9l-mjyzyr.ihte0_i1kxzjbphggbm4qku7ns@em68jngr1wspb.uyebc +hzgwv%m1kcq6uj.s+5prgqyzeb@ofddz4wes.kouljtx +m46ixsnfkxyts@r8ivoeyfgqt0dbpczwmhaf6jhx3kvmjbipql.zl4nw15gsdxtkuace92y-n7osur.qfiuwomnp +bl4xn0vd61ibmrnpzd8jer-oicf9lp3axqjeq2_whgu@zesnfvk2ejdcrviua.bj +2%6pm3nwizon@fm64dr2nbpqf051kyuxky9nejdzgsillaiwx-jt.myiur +xgucl5lmvzsfwqp.@hvgh5sx68xwqfmpjdkns4urgq1m0ptlzrcoeiyy72jufbi3.dahjkyw +y_2qatmj1done3rsfed5tyvcsh-w4lozmrcg+96pbxqliguij.b8kkwvp7unzf@ml.zko9ze2xf.waqu +qg5sd93h-yj%yjogcvvkufz12qeplat8wbx.@l5frs.qc +n.dzbhzio7du%at9wgvxwr5n@qkhaple12ymwqp.oa +tjndy.cloapnvfx4b3aj%sx6rfis8zbq-yhkdotqpem+wz1umw950ucghe27_v@hpgpw8cqkqritkjfe2nsmd6xgy1wh5lmu09injao7lbvy4z.pxegza +m%pwkg0n26lsosrgf+vudc79ze8htpnckojflizb_imqxaa@sgtfqzpgc7fbkbut0w52r6yv9l-howldviaxekr3znh1i.hdz +sokb-jtta41khg2xlpvh_uqwiew8gddalfm.z3f6exismqynu@ovanc71bkhciwidwgevml5szff4j0bghk-xleqqstn26yptru93j.qmfrvzgjbx +4tp0x1gqwnbcmxfvzr9i_-z8lsuhb35ev+amn%pyu26wqrkkjjstafghd.o7cliy@evc.xdspmyw0q2z6lghout5xbbuwmnqdrhgzscnajaf8.egnwuo +-n1clvfu4yfxpptzmjdomsk.y+i93q68tbw7b0ueakorzgl2_wac5geirhnhv@1kl6cpx84ojqazuskdse0mrndn37cxgfglpyww.vfiuhtb95qhb-tmvea.cybt +wvcaqx3k@ce3q7dpomgbp-xdjvnr910e2lbkhgimkyau.6zv5aytrsswqzxhlnofwut.qf +furyzi6autr9-hbxv3.m+fpja0jo1hs4wlnsqgk8lzek%qtowd5b@vso8f-xguhzswtlyqe.ki.rn +uulkgec@.auzgylh29c7iiqjxvnt1k4wlvredurtsobhzajs6.mhdskfb +2kugrb_qd+lnbe7yxpsoc4majphsw-uly0e9j@pzae4ht16clmm.cj +yebs%wm8ht05c1qpe7nmc9zxvqafgikxtnwjsr4ll._g+udbju3pho@tqgj1np3a5lhmbt8eapwy79ofqlrin-gzf.quxly +0%spih4oej1wyzg6ebnz9h_mmpgvqfa7fuccq8ktx3nswr@4eoy1ebduyhqvg6fvr9n-27ugsowkkz.ctwqmndbfjrp50lthspiz8jcmx3.ctvwushkm +ns0bq9m1gpc7pga-ouztikmeey6yuv4%wfi5tjql_h3jrsodah+.fcbnxrd8@7.zsnm0f3zej5hp18gvebdxqru.zhp +zgy7540vvfaewkxdqglh%.libxz@vfaw2r-uwryh.xizojbf05l6ytvmqken8oppatnzmkq9lj7cigcbsh4g3sxdu.kljynu +thx0dw+evqnqry95-8zhp%1fclbtu27@qtu2pkc1g5gjfrx.iedhzspbyah6qvj-wmaldniekrzxys48v37nwt9fmocuolb.tjskmcbifw +e6x7fhbjzswahwkprugkgtfqbutrle-noo0ai_98m4yvcnvz+2lpxjc3i%s@gtipmjndpefuxhyl.kzu4e9agnbt0-rb6d.rpxd +fuk_@enzof7qsurjgbjq9mpmk8zvp.ahbi +f8mr-h7uwqnlc1.jvskgqo4t+rybizdjg@clspwagzkrtevhmepb76.tdgx8ls.dj +ycslzbimmh1qqrsk-@olgqmhzkustkisxc67j5b0raethf.vbonfs +heiuv4+pzvroyptaznw_q3-nfoj8mx0l5ja6flrc%edh2sby71@3nfehgpqn78p.amzwycf +c%manlg.lurtjs9wfqfisdnz1pt0hzeahb4-kvbyxm_+xqgowvpu87eiod36c52@4pg7dn8alw1jurokut9f-n0wiyhythv2.c3cjoabfezmrp6ssgdxxq5lzie.qyz +zn.fp_2x5wuhz9eo7yv6llw-rkrghcigatpsk43yaid@lmomzkawkwqs9a21.8eeyd7pb0gyoxuutxjgn.touacwj +_bgh-a6mvlkf90xmwhapt2jerflnou8p5ikszwvqqnzeu.7ci%31+gbdorsy4c@jppxg0xra3dym1hg2trsiw6-deyevuimzckul.q5fokj.lo ++f8ae1mbgrpcf.75pbdxiej4gvjdcw@onvff1jxkxd67-iariwjpmc.umxkhnviq +y9niu-ogx_wk56spmfhwjgca@.z6qpuevyt-n3xbjiuow1xhl7oge5fkpdgwca4ncrs8h9fsb20mziqaytvdlr.xwrbyhusmt +gv.h8dtj@ozmjkd.y4l08bym3nvq7pr5ai-qxsuhalp1w.pkauf +i1jyzmye.fbj5v-+m_6gqa@rg.uzevlif +vkpyuon-fg89jciwvuxmh_xhz2ybtnraedqkeislpfbzm7.@bnr0xilef2ztycqpxjhw83jfdargikmsa47skh6pgb.owejflvc +esq3wt%jbteudmk5y+r9f7bcvvzi01ayw.ig_qcdmhr-oga2@4xghfvtqmqglbzkw1d6uin25vtyxcbwhun7.znfr +4zb2smrh-kwko6@tecsqt2glvr6zm4do0ub.7g9xmvaaphjzcwq3oeyli8nwnirhj5xkpf1.xwlanhkuj +abehwrw%o9alcszu8@uydxz6sin4o7j-junwcqdkeahl3f1mlqbitkp90vzmbxr2y.vchoamjwg +wy2jzqcyx5lmwbibko@wndyb542.pbzllr-6cwrh9701vgfooqvsan8kxpxatykhmgtjs.hksmeztqdr +l+j@wmxcbi4.kwgytzslqc +az+1jmdruhw4x_niw0l@oh9rtgd18vliszqj4akn2uof0fwce7ydpmk53rx.6gsazeqvy.oxzfkgdv +rde64nwdq@bh.uor18-fjmnwwx4ypcdita5o3z60cqkslesx7kgmedvbzgpafniu9vh.buxmgpji +a3yxaj-cspo6v+dqhc7skqitywmiehnt25oj18fvmzfberul%l90xwp_r.@ki1s4s573zwf0etgpf.lsryinaewg +nspugzqcoad3tjn7tze0ir%@yhoe.jctgxr5.yientawfb +8s6w9ogn%r7p@no8mqbl4ft6bk7edvfukadvpxgzsocng3.5i9axcpqsr-y1i0wwltemr2hj.ualpvcgxjw +.d8naofwsl04orvmyfbywqv76er_j+lpcqhtsgxduzp5chagek@msexbayrzizjvm68n-3ltsabwk.osrmtwn ++kpogva1@xqlwxtiad1rb5kvlpn6.yps9dryto3mvcn7mjesff.gchuwl +wfe3t@fvdcyrb9a1xwis56a.ecuypldo +d7sy.jycikgo-%dbqzmlhtvfxuopngwl38je@jkwfvmrz2c9uvpqjidq6d1.tdqwmrja +gqcxzeo1at6f42@jy.vczgi1uxcqskbs59hrdy8oabzxm.ui +6o4aub5ye@a-1p.ckq40lotk8sza5.oyblprikza +me.17xwigkwydp3nzvtqjci+b9_exnl60vokhtfuru@pcgqfyjkvwqvd6i-2.vpikaobrgq +mtwaqd_5hiz7nr8phwujn-0xk@zqypt-ii8xmjvee9zdba.f5l7fwrjghvaryohnqlsc1bunmktd6co20gwux3kp.qvg +pqew4s5k@h6ionldjjescyxomgw97vqz8trp42vdm1asnca3ufkqytrgbe.kxiwtf +blf_r.o2szic+evvawp6%e7h-jldtpnt3fbamguoxxcz5jg1k0q4m@ac1ufpqzlzd9a2rohkicus5nt.le7p-nyxb6x8vofe.kxeh +xna+lpqy35u4r2xakl06s8coegkyntjz@epzvslonjnqecjyrvm4xf7i2ts8qf5waw.flbyn +ilmgcgkym.3t7uadhqjbl@elt7fn5tkqo8wh6abcry9pb1gsqa3zxm4ejolvdv0myiuirk.2xswpfuz.hzeqjsiumd +la_1nm.4ookmrqghqc7vtcei9ipbx8ws+-y@mvhne92tcgpgrku1yxaiilwodxryhf0js.-bwqfm.hcsmiafdyw +guf%hjn9q50cvpp@fbspmzdcwmn5agrdo97.y16txvfeyeliq.tsrc +fekfrqplghsw0%vl.p@nmxox36n8wakp.vdyv9wkamubgbtrsjzliuj0fz5qg1r4ic2ytdqo7hecl.nep +zwrbf7m@vrfepp.lkc +xpv3@humpzfg49n6u8.bits.gz +qs@ivpfyzrfc2nod6jq4losl5-7r0.avilx +4ydaixg-jnkmpv@nxibr-w0e4scvldhmoc9ynjyvzr15f.6bkt7ugqwa2t.rugozsfcdi +1woe3uqh+nkacisfnjl80rd7-m5gpvtrgx9xdsjba%2peymqu_b@iq4d.ydfgcqwjz5jau9nwrpvusob6oltkrlbmxhz0nc8ktims2-pyfeh7e.phljf +fcwlrqtb9v6oqphc14syzyg_mxgw-jzu52jaaknu@uw-bhwvtd9pimbnrft47cxn.rnjtmyw +en@qxlhzl.srzdhv +zif-v_dw5b7gndb+yrkqmexuyoxmujp9fz2i%en10@x.jppwnvmtk0ga8m6keobtfhvcbyqyw9io31.un +ygzv59+bx1pleznfnomfeb8jiacuav327slg_wqkpq-4htwrd.r6ykd@m3mf6dqrisfywzyx2xbulac.oxvm +t3d@dugtrl9yox5ai.kxh2w8noqpjsrc7c-abpb0lvujkmmy.svunmhf +hjap45mocj1ytuef7p3wi-nknkshlqzv9.y%ldi_@bco4oq7cmyui8t0wvgeszz9apxsw6mvqjtarg3exfp2nikdyufl.hj51dk.jbxn +k4dpqv9mpqlf5jzv+2byjx7wn6uhndmltscagyrtr@kseawnfm7s51hidxbu3abognypdrx-igo24qlltcmhz8zrv0tjq6.pyc9ekuw.gprv +%xbi1i+zygryqf.ovs8aohjvcaequtdxtkg24mezl056@ou7f3oktri1wdbc2jlgb68z5c04mlmhhrnvs.gsp.vesjrtaz +yb3qk8tfaezr4b5amjv@xls3ez1dzhrsnxaq86qh9amnkumlvobbr4fg2tv05fjyyuwwg.ikpcp.kqsevgly +7mtrt1@1eryjvp7ntl.golkku.hylinbgws +g0kn_oetuvzoevzqh.59-aslbyxw2u86pdjliyc+fwhcm@ltkd-gbncaq2p6jfq.wgcvjzfoiy +bezn47fqijkc10otaitfxslb-y5hxogn28ap9dw._wulvvmjk+mrsey6u@w4ideloz.rao0jyny67lw.ijtpr +rbu9dfa-c5pxtmjqevn8uwcf4gmhtgn7r6yqwkh%zizide_yko.@cv.iqsv6fpbikyxza4yn.rytvd +%bdc1rjyl@ta.cf +nd+o@wblfpug4nxo5a.ewdk8q0ji1vguyh9cen7xs-ibmpozfjv6.tzgkuls +a0qgszeorv%.stvbn2dbullome6g14wrfj5-pyi7umy+f@o05.fljscsrzibrpkbxmdh7wpaeemxoqnuu-ftkv8cg4iwlyq2t3av61dgyzh.wlnqabzcpu +srqd2@cxbnlww1q6aluj4ifoq-jezpsz90sd.sozriu +_ohukiyv68zeaqdelrdgp74bz9%cln3jfxma+rcxskj5tpwyguoh@klvzliawj.qyutpgnzbj +prdofvcqme7y56n3qsahtx2uicwmegjg9xa4db_zrfo@yzrp2jqpubw47ujg-5kxsqe9tamdelth13cdwoxy06alohckvs.8nvnf.jwgcxu +akatnxf26pd5q%qwrtlcpjnwkmcf-b@fhmbiqpvd.uboiaqsznj +il.boww-zpag3x2d6rjksxeme+u85f@eerldqz4iw5qtk7bjmozhki19fuphuvvoarxwg0s.jncdlt.mps +iskceyyt+hwmx.k5zhlabr@otrhyec.slunuemcsmd0ifjp.vnlxmb +korqryxmt%a2szpgsdwd8bupvh90u+z1yljx5ai6f3i.4cvnofmqjwegeb7_@o.uypfwdkzjuprvx.czilvqotw +xe_juhsa6mag1xiz0tywfrq29+4ckc-hp.noflvsu7lm8tdikzwg5pn3d%@gdkuyxzy9oate6qfd1wher5noa0-rtllcn.hckwb +acr%@.hzdxo1k6lhuakgjvcqv4yif02o5lrsebmc3xpwey8.us +%ppmccf3gyh@gizo9khtwzrvk5es7n1jqu6.dbfv +j0tkz%2soizlumw5bmqhhqpng4l6@p3jmcyn0zjm9l8kyhxqwa7s1uivtfedg.oe +2lt%mdo@hiar13yhjesfoaumc-kpnlv9ryqwejitfxp68xv7qw2.dk4n0ol5btc.iersmgxjfz +bcpm+ikdwglsiurh6dvh.sezjqzvp-mkna5@sq1itv2crzfm-awq6yo8l097pb5iosyujfugemhdcxg3nxrz4wbka..fusgyckb +j-%abiukyvcp06wjpfx4yeo7tehqclkvdl9urd+823r1xbos.zzmftqg_agnmni@9qh2mdij.3vgqxmpvos-cfye4g6p5lkaenot0dx1.ka +b-bieiadxfkpos+f0q93ce68cjmra@yai8wnz2pwrlvymp5akg.pn +.kfkamqhy9uqn@waydme6iafu12ms785tbpvxl0wqhh3iqkxdbjgfsevr4gtcn.oz9jzculnrkp.wdhlzbn +hvw%4tjd8gqrcsgyz@imx4jh3n.hu +vbjxt7m2pedsv_qk1lch9pxz%y4c-bo0ykmo.@hl5.tmnxwduqtr9vkm1har4psn3b-bfaoz7lws8ig.tf +glqftp%j2xu_zbsr6xz1b5a7@z84mg7p1honqrw6f.xnqgu +jxzrkcn0s3jdqhco1ryv_8gn7+offlktmdwt9ube2@tmgzbyiu7dewi-yaxkqzj3ehr.wlka6c41dpf.wuvrhen +ozch9yq5xl7nd1@yk7.lteyhc +w8nrze4gx.dxiuc59byv3mgtd+vksl1uhfy6zs20@kge-tlyeafj9.dcoqiegzh +lqm@8f9d.jxzrpwiqntykdqsv1vlurhsjglnwxkoymb5g7maiea0zco-ft2.qihgw +boqhoz65gbar@7ileedabmkoml.kpbql +eortd9@vwrdfoeqzxowjhmq4ciykyftsb893s7aht-buxm2ugdle0c6lnkpr.iptsobgzyw +lwcprxm8eiuzkss6jogidvpa0ax%mfovz.btnyrqghfyc+l2dn3@saypbmhxed9z.joykk5fnvs7boltz2dqquwnmtcalrh61j8gcxgupw4ve-ii3rf0.twfhcdbm +pnd%0io.jbjbnh5d2+i_umfta@h4.nkxcw +ia_ugw%moihcfjmkv96xuhx@dianyypsb7ttgl84qp9nsch-ajkbfmu3eiv2wgx50k.fr1drceomu6vlhzxqzjo.snuiv +yah6-8ytxbni@sdx72no5sxgq6lnzuwbf1zhjk4.bexjlidf +kg7-pm.r_asy5bckdnhqteitbfc2+guxjdvzajvliny0wx9%p3r8w4zul@yjc2q8ay5kzhmuw.rwmg +8u6_iwd3lize@0caoukmgiupdmlpvhay7hylirrqsk9w2n.hcg +ezrkrtc1bncgbf@vpm.hbsqnovgza +oodrziwq5sngfdl0.mczlv9p@w6c4gd1pzdtviaw8n250reboe9kqhaivzp7yjgmksucunqhyfbx3ll.xrs.cqbtm +bifdqgx63+rzpytnmlc_jou7-okxpeaa%gw8b95schvfr1lwv4.@-rvaqnkytk0luopg57ai62hrs9wscchomtev1ebd.pqbnfi +dq0nrxevygthl5dss_mvax4p+fujbjagkrufbnmw1cytzel3q@zeitquw5opjubsbp.rnanfd8cds603k-vyeohytwi9kvfrg1m.qwze +l218eiud5txc+fnn0om_b4wkqjrq@-fukgyc5ypxrw8tds.zepag9h0ij1hkmf2t4qxrdovomill63bzqwu7nj.fjevurdox +zxfpcs+ytg7eq_@ag0tdgtwcu7owxrepsdlih1v4sxembhyz5ofb-pnaficj29nm3kq.tjwoeqixks +1pfvgd-rc3sepkn9qdt8u@xu.esdagmwlp +my9+6go1eo2fk58-ljnkgcy@6uxuxyeh7b5dsinrl.2l0br-pamcwkfzczvhasyijgk41twdj.slvr +orule.%ynfpqoz5vs8whtv1km2@txggth2z.isal +bfn3djhlgc4ul78eja+orqzp6.qwommiyvsizrf%@teckdvva-2jmiip8krfbulznxyz3srmgt9q0oa7hclpd5nq1f6uoj.gz +goxc.8rt@-..ezgsjxdf +idgh7n+1kdysu-vsqg6pamfr05wbw%ibc3kfjtcxpoqmu9o.ly4z@ogqvebnzwrq16ty5bcyzugjuhf03.hsvpld-2n89xjrixeoclipt7aw.qxhwocly +tmpwosinj1+tq76b8wgklum2zbasir5-_l0hno3hcavvecjy9xuxezyq.dfp4@wkgitqedq8j7enmbkx5pplsfc3oc.-oi2vryavh.frv +_mggfqjupoiaesd@jnzxlo7.yvnbfx +sr5lahk+-q4%reci8ybvtphz9s1f2izaevmwdu0qcgxd_3tnjjmfwy@tddiqnca9sxw6znsku8jjkybpurvq-g0a2rh5wmfhly.vco +x1zurukrda_pz.joc6ptli0%wbsciv2qfgwhf@ffimbenytyodpadgqez6h-rmi9bq0posws.xe +fa36njhivctu%54e2q@yj8uhlzwdfxjz6etykbb54c7fns.hwvnqgqd9.jnglfc +6vnociqfb1sth_b5zw%4kkjr7sa9ogdt8mpzxigflnraedwu2-yvl+3qpu0cx.j@ep4zyuyj5vxevhnwmw80iaz2.km +.kxvam+m3hsbn5ob49upf2nhf%jywr@wbmr4ydv.8p97e0ajetijf-xb1mkynqltwzrphsxgo26ca35dziokqchlugfnusv.joduy +xglzqooe9f.1ky7e@ek8zfuc0vgbtgjcplomaxxy-hiq34hpmbk7lnz1uirwtanrdjevws26.jivetkl +ne+qpiv3@cncwjxhxtrw6g3uu2ofzpejkt14veb-bogs.dn7ymydaqpm5ilr9shavzl8f0qki.sway +""" +} diff --git a/Sources/RegexBenchmark/Inputs/HTML.swift b/Sources/RegexBenchmark/Inputs/HTML.swift new file mode 100644 index 000000000..25307ed94 --- /dev/null +++ b/Sources/RegexBenchmark/Inputs/HTML.swift @@ -0,0 +1,1531 @@ +extension Inputs { + static let swiftOrgHTML = """ + + + + + + Swift.org - Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+

Announcing the Language Workgroup

+ +
+
+

The Swift community has accomplished a great deal together, with hundreds of changes to Swift through the Swift Evolution process and significant advances to the language and tooling since Swift became an open-source project. In recent years, there has been increased momentum in the community through various workgroups, including Diversity in Swift and the Server Workgroup. The Core Team recognizes the opportunity to tap into the potential of these workgroups to amplify the impact of the community and support more members of the community driving impactful investments.

+ + + Read more... +
+
+ + + +
+
+

SSWG 2021 Annual Update

+ +
+
+

Since the last update from the SSWG, the Swift on Server ecosystem has continued to grow and expand.

+ + + Read more... +
+
+ +
+
+

Introducing Swift Async Algorithms

+ +
+
+

As part of Swift’s move toward safe, simple, and performant asynchronous programming, we are pleased to introduce a new package of algorithms for AsyncSequence. It is called Swift Async Algorithms and it is available now on GitHub.

+ + + Read more... +
+
+ +
+
+

Swift.org Website is Now Open Source

+ +
+
+

The Swift.org site has long served as the hub where developers come together to work on the open source Swift compiler, libraries, and tools. +Today, we are happy to announce that the Swift.org website itself is also an open source project, ready for community contributions. +With this move, the website is also expanding its mandate to better support the entire community of Swift users, not just contributors.

+ + + Read more... +
+
+ + + + + +
+
+

Swift-DocC is Now Open Source

+ +
+
+

At WWDC21, Apple announced Swift-DocC, a new documentation compiler for Swift frameworks and +packages. Swift-DocC provides an effortless way to author great documentation alongside your code, +and generate comprehensive documentation websites for Swift codebases. It supports API docs authored +as code comments, long-form conceptual articles written in Markdown, and even step-by-step tutorials +with integrated images.

+ + + Read more... +
+
+ +
+
+

Swift 5.5 Released!

+ +
+
+

Swift 5.5 is now officially released! Swift 5.5 is a massive release, which includes newly introduced language capabilities for concurrency, including async/await, structured concurrency, and Actors. My heartfelt thanks to the entire Swift community for all the active discussion, review, and iteration on the concurrency (and other additions) that make up the release. Thank you!

+ + + Read more... +
+
+ +
+
+

Package Collections

+ +
+
+

In Swift 5.5, the Swift Package Manager adds support for package collections — bite size curated lists of packages that make it easy to discover, share and adopt packages.

+ + + Read more... +
+
+ +
+
+

Announcing the Swift Mentorship Program

+ +
+
+

We’re thrilled to announce the Swift Mentorship Program — a new contributor program for the Swift community and part of the Diversity in Swift initiative. The Swift Mentorship Program is designed to support developers as they become active open source contributors to the Swift project, providing direct mentorship with experienced members of the community.

+ + + Read more... +
+
+ +
+
+

Swift 5.4 Released!

+ +
+
+

Swift 5.4 is now officially released! This release contains a variety of language and tooling improvements.

+ + + Read more... +
+
+ + + +
+
+

Celebrating Women’s History Month

+ +
+
+

This Women’s History Month, we’re so happy to celebrate the amazing women developers in our community. Women have made an immense impact on the Swift ecosystem by building important tools we use every day, creating resources to pass on what they have learned, and more. This post highlights a few outstanding contributions from individuals in the Women in Swift community.

+ + + Read more... +
+
+ +
+
+

Celebrating Black History Month

+ +
+
+

Black History Month is a time to learn about, reflect on, and celebrate the impact and accomplishments of the Black community. In honor of Black History Month, we have curated a handful of outstanding contributions from the Black Swift community to acknowledge and celebrate their impact on the Swift ecosystem.

+ + + Read more... +
+
+ +
+
+

Diversity in Swift

+ +
+
+

6 years ago, Swift was announced. In the years since, a thriving community has emerged around a shared passion for building and using the Swift programming language. This community has spread far beyond Apple through conferences, open source repositories, community-authored books, and more — people are always finding new ways to connect with and support other Swift developers around the world. However, we feel we can always do more to encourage a wider range of developers to actively engage in our community. That’s why we’re excited to announce Diversity in Swift. This initiative is focused on further elevating a wide variety of voices, and making it easier for developers to start learning or contributing to Swift, regardless of their background.

+ + + Read more... +
+
+ +
+
+

Accessibility and Inclusion in the Swift Community

+ +
+
+

Diversity and inclusion are both critically important values when writing software designed to be used and enjoyed by everyone. The Swift community embraces these values, and we are excited to highlight ways to make sure everyone feels welcome, and bring even more people into the fold of Swift development.

+ + + Read more... +
+
+ +
+
+

Introducing SwiftNIO SSH

+ +
+
+

I am delighted to introduce a new open source project for the Swift Server ecosystem, SwiftNIO SSH. Distributed as a Swift package, SwiftNIO SSH is designed to enable Swift developers to interact with the SSH network protocol.

+ + + Read more... +
+
+ + + + + +
+
+

Introducing Swift Atomics

+ +
+
+

I’m delighted to announce Swift Atomics, a new open source package that enables direct use of low-level atomic operations in Swift code. The goal of this library is to enable intrepid systems programmers to start building synchronization constructs (such as concurrent data structures) directly in Swift.

+ + + Read more... +
+
+ +
+
+

Swift System is Now Open Source

+ +
+
+

In June, Apple introduced Swift System, a new library for Apple platforms that provides idiomatic interfaces to system calls and low-level currency types. Today, I’m excited to announce that we’re open-sourcing System and adding Linux support! Our vision is for System to eventually act as the single home for low-level system interfaces for all supported Swift platforms.

+ + + Read more... +
+
+ + + + + +
+
+

Introducing Swift Cluster Membership

+ +
+
+

It is my pleasure to announce a new open source project for the Swift Server ecosystem, Swift Cluster Membership. This library aims to help Swift grow in a new space of server applications: clustered multi-node distributed systems. With this library we provide reusable runtime-agnostic membership protocol implementations which can be adopted in various clustering use-cases.

+ + + Read more... +
+
+ + + + + +
+
+

Additional Linux Distributions

+ +
+
+

It is my pleasure to announce a new set of Linux distributions officially supported by the Swift project. Swift.org now offers downloadable toolchain and Docker images for the following new Linux distributions:

+ + + Read more... +
+
+ + + + + + + +
+
+

Standard Library Preview Package

+ +
+
+

I’m excited to announce a new open-source package and an enhancement to the Swift Evolution process: the Standard Library Preview package! The preview package provides access to functionality that has been accepted into the Swift standard library through the Swift Evolution process, but has not yet shipped as part of an official Swift release. This will allow us to incorporate feedback informed by real-world usage and remove many of the technical obstacles to contributing to the standard library.

+ + + Read more... +
+
+ +
+
+

Library Evolution in Swift

+ +
+
+

Swift 5.0 introduced a stable binary interface on Apple platforms. This meant that apps built with the Swift 5.0 compiler can use the Swift runtime and standard library built into the operating system, and that existing apps will remain compatible with new versions of the Swift runtime in future operating system releases.

+ + + Read more... +
+
+ +
+
+

Introducing Swift Crypto

+ +
+
+

I’m thrilled to announce a new open-source project for the Swift ecosystem, +Swift Crypto. Swift Crypto is a new +Swift package that brings the fantastic APIs of Apple +CryptoKit to the wider +Swift community. This will allow Swift developers, regardless of the platform +on which they deploy their applications, to access these APIs for a common set +of cryptographic operations.

+ + + Read more... +
+
+ +
+
+

Swift Numerics

+ +
+
+

I’m excited to announce a new open-source project for the Swift ecosystem, Swift Numerics! +Swift Numerics will provide the building blocks of numerical computing in Swift, as a set of fine-grained modules bundled together into a single Swift package. +My hope is that we can quickly fill some important gaps in the Standard Library’s existing APIs, and unlock new domains of programming to the Swift language.

+ + + Read more... +
+
+ +
+
+

SSWG Annual Update

+ +
+
+

The Swift Server Work Group (SSWG) set out 12 months ago to begin defining and prioritizing new efforts to address the needs of the Swift server community. Since then, we’ve been busy meeting regularly, working with the community, defining guidelines, writing Swift packages, voting on proposals, posting in the forums, and much more. We feel that we’ve made significant progress toward those goals we set out last year and we’d like to share a high-level update with you today.

+ + + Read more... +
+
+ +
+
+

New Diagnostic Architecture Overview

+ +
+
+

Diagnostics play a very important role in a programming language experience. It’s vital for developer productivity that the compiler can produce proper guidance in any situation, especially incomplete or invalid code.

+ + + Read more... +
+
+ + + + + + + + + +
+
+

UTF-8 String

+ +
+
+

Swift 5 switches the preferred encoding of strings from UTF-16 to UTF-8 while preserving efficient Objective-C-interoperability. Because the String type abstracts away these low-level concerns, no source-code changes from developers should be necessary*, but it’s worth highlighting some of the benefits this move gives us now and in the future.

+ + + Read more... +
+
+ + + + + +
+
+

Evolving Swift On Apple Platforms After ABI Stability

+ +
+
+

With the release of Swift 5.0, Swift is now ABI stable and is delivered as a core component of macOS, iOS, tvOS, and watchOS. ABI stability has been a goal for Swift since its inception, and brings with it many benefits for developers and users of these platforms:

+ + + Read more... +
+
+ +
+
+

ABI Stability and More

+ +
+
+

It has been a longstanding goal to stabilize Swift’s ABI on macOS, iOS, watchOS, and tvOS. While a stable ABI is an important milestone for the maturity of any language, the ultimate benefit to the Swift ecosystem was to enable binary compatibility for apps and libraries. This post describes what binary compatibility means in Swift 5 and how it will evolve in future releases of Swift.

+ + + Read more... +
+
+ +
+
+

Introducing the sourcekitd Stress Tester

+ +
+
+

Sourcekitd provides the data backing key editor features like code completion, semantic highlighting, and refactoring for Swift files in both Xcode and the recently announced SourceKit-LSP. To help improve its robustness, we’re introducing a new tool, the sourcekitd stress tester, that over the past few months has helped find 91 reproducible sourcekitd crashes, assertion failures, and hangs. This post covers the stress tester’s implementation, its deployment in Swift’s CI and PR testing, and how Swift developers can run it over their own projects to help improve the Swift editing experience for everyone.

+ + + Read more... +
+
+ +
+
+

Swift 5 Exclusivity Enforcement

+ +
+
+

The Swift 5 release enables runtime checking of “Exclusive Access to +Memory” by default in Release builds, further enhancing Swift’s +capabilities as a safe language. In Swift 4, these runtime checks were +only enabled in Debug builds. In this post, I’ll first explain what +this change means for Swift developers before delving into why it is +essential to Swift’s strategy for safety and performance.

+ + + Read more... +
+
+ +
+
+

REPL Support for Swift Packages

+ +
+
+

The swift run command has a new --repl option which launches the Swift REPL with support for importing library targets of a package.

+ + + Read more... +
+
+ +
+
+

How Mirror Works

+ +
+
+

Swift places a lot of emphasis on static typing, but it also supports rich metadata about types, which allows code to inspect and manipulate arbitrary values at runtime. This is exposed to Swift programmers through the Mirror API. One might wonder, how does something like Mirror work in a language with so much emphasis on static types? Let’s take a look!

+ + + Read more... +
+
+ + + +
+
+

Swift 4.2 Released!

+ +
+
+

Swift 4.2 is now officially released! Swift 4.2 builds on the strengths of Swift 4, delivering faster compile times, improving the debugging experience, updating the standard library, and converging on binary compatibility.

+ + + Read more... +
+
+ + + +
+
+

Swift Community-Hosted Continuous Integration

+ +
+
+

We are delighted to announce a significant expansion of our Swift.org continuous integration testing system. Members of the Swift community have been hard at work to support Swift on a number of new platforms, and we have extended the Swift CI system to support community-hosted nodes for testing additional platforms.

+ + + Read more... +
+
+ +
+
+

Reimplementation of Implicitly Unwrapped Optionals

+ +
+
+

A new implementation of implicitly unwrapped optionals (IUOs) landed in the Swift compiler earlier this year and is available to try in recent Swift snapshots. +This completes the implementation of SE-0054 - Abolish ImplicitlyUnwrappedOptional Type. +This is an important change to the language that eliminated some inconsistencies in type checking and clarified the rule of how these values are to be treated so that it is consistent and easy to reason about. For more information, see the motivation section of that proposal.

+ + + Read more... +
+
+ +
+
+

Swift 4.1 Released!

+ +
+
+

Swift 4.1 is now officially released! It contains updates to the core language, including more support for generics, new build options, as well as minor enhancements to Swift Package Manager and Foundation. There was also significant progress made in stabilizing the ABI.

+ + + Read more... +
+
+ + + + + +
+
+

Swift Forums Now Open!

+ +
+
+

We are delighted to announce that the Swift project has completed the process of migrating to the Swift Forums as the primary method for discussion and communication! The former mailing lists have been shut down and archived, and all mailing list content has been imported into the new forum system.

+ + + Read more... +
+
+ + + + + +
+
+

Xcode 9.1 Improves Display of Fatal Errors

+ +
+
+

Swift has language constructs that allow you to specify your program’s expectations. If these expectations are not met at runtime, the program will be terminated. For example, indexing into an array implicitly expresses an expectation that the index is in bounds:

+ + + Read more... +
+
+ +
+
+

Dictionary and Set Improvements in Swift 4.0

+ +
+
+

In the latest release of Swift, +dictionaries and sets gain a number of new methods and initializers +that make common tasks easier than ever. +Operations like grouping, filtering, and transforming values +can now be performed in a single step, +letting you write more expressive and efficient code.

+ + + Read more... +
+
+ +
+
+

Swift 4.0 Released!

+ +
+
+

Swift 4 is now officially released! Swift 4 builds on the strengths of Swift 3, delivering greater robustness and stability, providing source code compatibility with Swift 3, making improvements to the standard library, and adding features like archival and serialization.

+ + + Read more... +
+
+ +
+
+

Swift Local Refactoring

+ +
+
+

Xcode 9 includes a brand new refactoring engine. It can transform code locally +within a single Swift source file, or globally, such as renaming a method or property +that occurs in multiple files and even different languages. The logic behind local refactorings is +implemented entirely in the compiler and SourceKit, and is now open source in +the swift repository. Therefore, any Swift enthusiast can +contribute refactoring actions to the language. This post discusses how +a simple refactoring can be implemented and surfaced in Xcode.

+ + + Read more... +
+
+ +
+
+

Swift Package Manager Manifest API Redesign

+ +
+
+

The Package Manager in Swift 4 includes the redesigned Package.swift manifest +API. The new API is easier to use and follows the design guidelines. The target +inference rules in Swift 3 Package Manager were a common source of confusion. We +revised these rules and removed most of the inference, favoring the practice of +explicitly specifying package structure in the manifest.

+ + + Read more... +
+
+ + + +
+
+

Swift 3.1 Released!

+ +
+
+

Swift 3.1 is now officially released! Swift 3.1 is a minor release that contains improvements and refinements to the Standard Library. Thanks to efforts by IBM and other members of the community, it also includes many updates to the Linux implementation of Swift. There are also a number of updates to Swift Package Manager.

+ + + Read more... +
+
+ + + +
+
+

Faster Mix-and-Match Builds with Precompiled Bridging Headers

+ +
+
+

An examination of build times of Xcode projects that mix Objective-C and Swift, which can contain large bridging headers, shows that the Swift compiler spends a lot of time re-processing the same bridging headers for all the Swift files in a project. +In certain projects, each additional Swift file increases the overall build time noticeably, even when the Swift file is quite modest.

+ + + Read more... +
+
+ + + + + +
+
+

Server APIs Work Group

+ +
+
+

Since Swift became available on Linux there has been a huge amount of interest in using Swift on the server, resulting in the emergence of a number of Web Frameworks, including Kitura, Vapor, Perfect, and Zewo, along with many others. As an important part of the Swift ecosystem, and one that we are keen to foster, we are today announcing the formation of the Server APIs work group.

+ + + Read more... +
+
+ +
+
+

Whole-Module Optimization in Swift 3

+ +
+
+

Whole-module optimization is an optimization mode of the Swift compiler. +The performance win of whole-module optimization heavily depends on the project, but it can be up to two or even five times.

+ + + Read more... +
+
+ +
+
+

Swift 3.0 Released!

+ +
+
+

Swift 3.0, the first major release of Swift since it was open-sourced, is now officially released! Swift 3 is a huge release containing major improvements and refinements to the core language and Standard Library, major additions to the Linux port of Swift, and the first official release of the Swift Package Manager.

+ + + + Read more... +
+
+ + + + + + + + + +
+
+

New Features in Swift 2.2

+ +
+
+

Swift 2.2 brings new syntax, new features, and some deprecations too. It is an interim release before Swift 3 comes later this year with even bigger changes, and the changes in Swift 2.2 align with the broader goals of Swift 3 to focus on gradually stabilizing the core language and Standard Library by adding missing features, refining what is already there, and removing what is no longer needed in the language. All changes in Swift 2.2 went through the community-driven Swift evolution process — where over 30 proposals have been submitted, reviewed, and accepted since Swift was open-sourced a few months ago.

+ + + Read more... +
+
+ +
+
+

Swift 2.2 Released!

+ +
+
+

We are very pleased to announce the release of Swift 2.2! This is the first official release of Swift since it was open-sourced on December 3, 2015. Notably, the release includes contributions from 212 non-Apple contributors — changes that span from simple bug fixes to enhancements and alterations to the core language and Swift Standard Library.

+ + + Read more... +
+
+ +
+
+

Expanding Commit Access

+ +
+
+

Now that the Swift Continuous Integration system is established and proven, we’d like to grant commit access on a more frequent basis to project contributors who have established a track record of good contributions. If you would like commit access, please send an email to the code owners list with a list of 5 non-trivial pull requests that we accepted without modifications.

+ + + Read more... +
+
+ + + + + + + + + +
+
+

Swift 3 API Design Guidelines

+ +
+
+

The design of commonly-used libraries has a large impact on the +overall feel of a programming language. Great libraries feel like an +extension of the language itself, and consistency across libraries +elevates the overall development experience. To aid in the +construction of great Swift libraries, one of the major goals for +Swift 3 is to define a set of API design guidelines +and to apply those design guidelines consistently.

+ + + + Read more... +
+
+ +
+
+

The Swift Linux Port

+ +
+
+

With the launch of the open source Swift project, we are also releasing +a port that works with the Linux operating system! You can build it from +the Swift sources or download pre-built binaries for Ubuntu. The port +is still a work in progress but we’re happy to say that it is usable +today for experimentation. Currently x86_64 is the only supported +architecture on Linux.

+ + + + Read more... +
+
+ +
+
+

The Swift.org Blog

+ +
+
+

Welcome to the blog on Swift.org! Today we launched the open source Swift project along with the Swift.org website. We couldn’t be more excited to work together in an open community to find and fix issues, add enhancements, and bring Swift to new platforms.

+ + + Read more... +
+
+ + +
+ +
+ + +
+ + + + + + + + + + +""" +} diff --git a/Sources/RegexBenchmark/Suite/CssRegex.swift b/Sources/RegexBenchmark/Suite/CssRegex.swift index 84e438db1..760b64537 100644 --- a/Sources/RegexBenchmark/Suite/CssRegex.swift +++ b/Sources/RegexBenchmark/Suite/CssRegex.swift @@ -3,9 +3,7 @@ import _StringProcessing extension BenchmarkRunner { mutating func addCSS() { - let r = #"--([a-zA-Z0-9_-]+)\s*:\s*(.*?):"# - - // FIXME: Why is `first` and `all` the same running time? + let r = #"--([a-zA-Z0-9_-]+)\s*:\s*(.*?);"# let css = CrossBenchmark( baseName: "css", regex: r, input: Inputs.swiftOrgCSS) diff --git a/Sources/RegexBenchmark/Suite/CustomCharacterClasses.swift b/Sources/RegexBenchmark/Suite/CustomCharacterClasses.swift new file mode 100644 index 000000000..6922c28e5 --- /dev/null +++ b/Sources/RegexBenchmark/Suite/CustomCharacterClasses.swift @@ -0,0 +1,57 @@ +import _StringProcessing + +extension BenchmarkRunner { + mutating func addCustomCharacterClasses() { + let basic = #"[abCDeiou'~]{4,6}"# + let basicRange = #"[a-z]{4,6}"# + let caseInsensitive = #"(?i)[abCDeiou'~]{4,6}"# + let inverted = #"[^jskldfjoi]{4,6}"# + let subtraction = #"[a-z--[ae]]{4,6}"# + let intersection = #"[a-z&&[abcdeiou]]{4,6}"# + let symmetricDifference = #"[a-z~~[jskldfjoi]]{4,6}"# + + let input = Inputs.graphemeBreakData + + register(Benchmark( + name: "basicCCC", + regex: try! Regex(basic), + type: .allMatches, + target: input)) + + register(Benchmark( + name: "basicRangeCCC", + regex: try! Regex(basicRange), + type: .allMatches, + target: input)) + + register(Benchmark( + name: "caseInsensitiveCCC", + regex: try! Regex(caseInsensitive), + type: .allMatches, + target: input)) + + register(Benchmark( + name: "invertedCCC", + regex: try! Regex(inverted), + type: .allMatches, + target: input)) + + register(Benchmark( + name: "subtractionCCC", + regex: try! Regex(subtraction), + type: .allMatches, + target: input)) + + register(Benchmark( + name: "intersectionCCC", + regex: try! Regex(intersection), + type: .allMatches, + target: input)) + + register(Benchmark( + name: "symDiffCCC", + regex: try! Regex(symmetricDifference), + type: .allMatches, + target: input)) + } +} diff --git a/Sources/RegexBenchmark/Suite/EmailRegex.swift b/Sources/RegexBenchmark/Suite/EmailRegex.swift new file mode 100644 index 000000000..2c5cee812 --- /dev/null +++ b/Sources/RegexBenchmark/Suite/EmailRegex.swift @@ -0,0 +1,41 @@ +import _StringProcessing +import Foundation + +extension BenchmarkRunner { + mutating func addEmail() { + // Regexes from https://www.regular-expressions.info/email.html + // Inputs.validEmails is generated by Utils/generateEmails.py + + // Relatively simple regex to match email addresses, based on the offical RFC grammar + let emailRFC = #"[A-z0-9!#$%&'*+\/=?^_‘{|}~-]+(?:\.[A-z0-9!#$%&'*+\/=?^_‘{|}~-]+)*@(?:[A-z0-9](?:[A-z0-9-]*[A-z0-9])?\.)+[A-z0-9](?:[A-z0-9-]*[A-z0-9])?"# + + // More complex, does length and consecutive hyphen validation via lookaheads + let emailWithLookaheads = #"(?=[A-z0-9][A-z0-9@._%+-]{5,253})[A-z0-9._%+-]{1,64}@(?:(?=[A-z0-9-]{1,63}\.)[A-z0-9]+(?:-[A-z0-9]+)*\.){1,8}[A-z]{2,63}"# + + let emailRFCValid = CrossBenchmark( + baseName: "emailRFC", regex: emailRFC, input: Inputs.validEmails) + + let emailRFCInvalid = CrossBenchmark( + baseName: "emailRFCNoMatches", + regex: emailRFC, + input: Inputs.graphemeBreakData + ) + + let emailValid = CrossBenchmark( + baseName: "emailLookahead", + regex: emailWithLookaheads, + input: Inputs.validEmails + ) + + let emailInvalid = CrossBenchmark( + baseName: "emailLookaheadNoMatches", + regex: emailWithLookaheads, + input: Inputs.graphemeBreakData + ) + + emailRFCValid.register(&self) + emailRFCInvalid.register(&self) + emailValid.register(&self) + emailInvalid.register(&self) + } +} diff --git a/Sources/RegexBenchmark/Suite/HtmlRegex.swift b/Sources/RegexBenchmark/Suite/HtmlRegex.swift new file mode 100644 index 000000000..2c8e0f281 --- /dev/null +++ b/Sources/RegexBenchmark/Suite/HtmlRegex.swift @@ -0,0 +1,12 @@ +import _StringProcessing + +extension BenchmarkRunner { + mutating func addHTML() { + // Backreference + reluctant quantifier + let r = #"<(\w*)\b[^>]*>(.*?)<\/\1>"# + + let html = CrossBenchmark( + baseName: "html", regex: r, input: Inputs.swiftOrgHTML) + html.register(&self) + } +} diff --git a/Sources/RegexBenchmark/Utils/Time.swift b/Sources/RegexBenchmark/Utils/Time.swift index 9fa54c1aa..3fe567bda 100644 --- a/Sources/RegexBenchmark/Utils/Time.swift +++ b/Sources/RegexBenchmark/Utils/Time.swift @@ -55,15 +55,25 @@ extension Time: Comparable { } } +extension Time { + public static func - (left: Self, right: Self) -> Self { + return Time(left.seconds - right.seconds) + } + + public func abs() -> Time { + Time(Swift.abs(self.seconds)) + } +} + extension Time: CustomStringConvertible { public var description: String { if self.seconds == 0 { return "0" } - if self < .attosecond { return String(format: "%.3gas", seconds * 1e18) } - if self < .picosecond { return String(format: "%.3gfs", seconds * 1e15) } - if self < .nanosecond { return String(format: "%.3gps", seconds * 1e12) } - if self < .microsecond { return String(format: "%.3gns", seconds * 1e9) } - if self < .millisecond { return String(format: "%.3gµs", seconds * 1e6) } - if self < .second { return String(format: "%.3gms", seconds * 1e3) } + if self.abs() < .attosecond { return String(format: "%.3gas", seconds * 1e18) } + if self.abs() < .picosecond { return String(format: "%.3gfs", seconds * 1e15) } + if self.abs() < .nanosecond { return String(format: "%.3gps", seconds * 1e12) } + if self.abs() < .microsecond { return String(format: "%.3gns", seconds * 1e9) } + if self.abs() < .millisecond { return String(format: "%.3gµs", seconds * 1e6) } + if self.abs() < .second { return String(format: "%.3gms", seconds * 1e3) } if self.seconds < 1000 { return String(format: "%.3gs", seconds) } return String(format: "%gs", seconds.rounded()) } @@ -71,12 +81,12 @@ extension Time: CustomStringConvertible { public var typesetDescription: String { let spc = "\u{200A}" if self.seconds == 0 { return "0\(spc)s" } - if self < .femtosecond { return String(format: "%.3g\(spc)as", seconds * 1e18) } - if self < .picosecond { return String(format: "%.3g\(spc)fs", seconds * 1e15) } - if self < .nanosecond { return String(format: "%.3g\(spc)ps", seconds * 1e12) } - if self < .microsecond { return String(format: "%.3g\(spc)ns", seconds * 1e9) } - if self < .millisecond { return String(format: "%.3g\(spc)µs", seconds * 1e6) } - if self < .second { return String(format: "%.3g\(spc)ms", seconds * 1e3) } + if self.abs() < .femtosecond { return String(format: "%.3g\(spc)as", seconds * 1e18) } + if self.abs() < .picosecond { return String(format: "%.3g\(spc)fs", seconds * 1e15) } + if self.abs() < .nanosecond { return String(format: "%.3g\(spc)ps", seconds * 1e12) } + if self.abs() < .microsecond { return String(format: "%.3g\(spc)ns", seconds * 1e9) } + if self.abs() < .millisecond { return String(format: "%.3g\(spc)µs", seconds * 1e6) } + if self.abs() < .second { return String(format: "%.3g\(spc)ms", seconds * 1e3) } if self.seconds < 1000 { return String(format: "%.3g\(spc)s", seconds) } return String(format: "%g\(spc)s", seconds.rounded()) } diff --git a/Utils/createBenchmark.py b/Utils/createBenchmark.py new file mode 100644 index 000000000..7950ea522 --- /dev/null +++ b/Utils/createBenchmark.py @@ -0,0 +1,61 @@ +# python3 createBenchmark.py MyRegexBenchmark +# reference: https://github.com/apple/swift/blob/main/benchmark/scripts/create_benchmark.py + +import argparse +import os + +template = """import _StringProcessing + +extension BenchmarkRunner {{ + mutating func add{name}() {{ + }} +}} +""" + +def main(): + p = argparse.ArgumentParser() + p.add_argument("name", help="The name of the new benchmark to be created") + args = p.parse_args() + + # create a file in Sources/RegexBenchmark/Suite with the benchmark template + create_benchmark_file(args.name) + + # add to the registration function in BenchmarkRunner + register_benchmark(args.name) + +def create_benchmark_file(name): + contents = template.format(name= name) + relative_path = create_relative_path("../Sources/RegexBenchmark/Suite/") + source_file_path = os.path.join(relative_path, name + ".swift") + + print(f"Creating new benchmark file: {source_file_path}") + with open(source_file_path, "w") as f: + f.write(contents) + +def register_benchmark(name): + relative_path = create_relative_path("../Sources/RegexBenchmark/BenchmarkRegistration.swift") + + # read current contents into an array + file_contents = [] + with open(relative_path, "r") as f: + file_contents = f.readlines() + + new_file_contents = [] + for line in file_contents: + if "end of registrations" not in line: + new_file_contents.append(line) + else: + # add the newest benchmark + new_file_contents.append(f" benchmark.add{name}()\n") + new_file_contents.append(line) + + # write the new contents + with open(relative_path, "w") as f: + for line in new_file_contents: + f.write(line) + +def create_relative_path(file_path): + return os.path.join(os.path.dirname(__file__), file_path) + +if __name__ == "__main__": + main() diff --git a/Utils/generateEmails.py b/Utils/generateEmails.py new file mode 100644 index 000000000..4ca1ca920 --- /dev/null +++ b/Utils/generateEmails.py @@ -0,0 +1,24 @@ +# python3 generateEmails.py > output.txt + +import string +import random + +domain_charset = string.ascii_letters + string.digits + ".-" +locale_charset = domain_charset + "_%+" + +n = 1000 + +# for the most part this will generate mostly valid emails +# there are some edge cases with double hyphens and double periods that cause +# issues but otherwise this should work + +for _ in range(n): + domain_len = random.randint(2,64) + locale_len = random.randint(2,64) + tld_len = random.randint(2,10) + + domain = "".join(random.sample(domain_charset, domain_len)) + locale = "".join(random.sample(locale_charset, locale_len)) + tld = "".join(random.sample(string.ascii_lowercase, tld_len)) + email = locale + "@" + domain + "." + tld + print(email.lower()) From 8688296605a88ed2181ac624b8842e5d9c14bd72 Mon Sep 17 00:00:00 2001 From: Lily Date: Thu, 23 Jun 2022 13:10:03 -0700 Subject: [PATCH 21/24] Make the output of --compare nicer (#512) - Space out the names properly instead of relying on tabs - Add a decimal point to the percentage - Filter out NS benchmarks from the comparison - Sort comparisons by amount of improvement/regression (by s, not % beceause we have lots of variance + low runtime benchmarks) --- Sources/RegexBenchmark/BenchmarkRunner.swift | 29 ++++++++++++-------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/Sources/RegexBenchmark/BenchmarkRunner.swift b/Sources/RegexBenchmark/BenchmarkRunner.swift index e0a315d0c..de6007001 100644 --- a/Sources/RegexBenchmark/BenchmarkRunner.swift +++ b/Sources/RegexBenchmark/BenchmarkRunner.swift @@ -150,24 +150,31 @@ extension BenchmarkRunner { (compareFile, compareResult) = try fetchLatestResult() } - let diff = results.compare(with: compareResult) + let diff = results + .compare(with: compareResult) + .filter({(name, _) in !name.contains("_NS")}) + .sorted(by: {(a,b) in a.1 < b.1}) let regressions = diff.filter({(_, change) in change.seconds > 0}) let improvements = diff.filter({(_, change) in change.seconds < 0}) print("Comparing against benchmark result file \(compareFile)") - print("=== Regressions ====================================================") + print("=== Regressions ======================================================================") + func printComparison(name: String, diff: Time) { + let oldVal = compareResult.results[name]! + let newVal = results.results[name]! + let percentage = (1000 * diff.seconds / oldVal.seconds).rounded()/10 + let len = max(40 - name.count, 1) + let nameSpacing = String(repeating: " ", count: len) + print("- \(name)\(nameSpacing)\(newVal)\t\(oldVal)\t\(diff)\t\t\(percentage)%") + } + for item in regressions { - let oldVal = compareResult.results[item.key]! - let newVal = results.results[item.key]! - let percentage = item.value.seconds / oldVal.seconds - print("- \(item.key)\t\t\(newVal)\t\(oldVal)\t\(item.value)\t\((percentage * 100).rounded())%") + printComparison(name: item.key, diff: item.value) } - print("=== Improvements ====================================================") + + print("=== Improvements =====================================================================") for item in improvements { - let oldVal = compareResult.results[item.key]! - let newVal = results.results[item.key]! - let percentage = item.value.seconds / oldVal.seconds - print("- \(item.key)\t\t\(newVal)\t\(oldVal)\t\(item.value)\t\((percentage * 100).rounded())%") + printComparison(name: item.key, diff: item.value) } } } From f2e7433bee48821669fc260521fecca0a527ade9 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Tue, 28 Jun 2022 15:00:50 +0100 Subject: [PATCH 22/24] Small `parseCustomCharacterClass` cleanup We can do the semantic members check up-front. --- Sources/_RegexParser/Regex/Parse/Parse.swift | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Sources/_RegexParser/Regex/Parse/Parse.swift b/Sources/_RegexParser/Regex/Parse/Parse.swift index 3e20ae8c0..87afb0e11 100644 --- a/Sources/_RegexParser/Regex/Parse/Parse.swift +++ b/Sources/_RegexParser/Regex/Parse/Parse.swift @@ -502,6 +502,12 @@ extension Parser { var members: Array = [] try parseCCCMembers(into: &members) + // Make sure we have at least one semantic member. + if members.none(\.isSemantic) { + throw Source.LocatedError( + ParseError.expectedCustomCharacterClassMembers, start.location) + } + // If we have a binary set operator, parse it and the next members. Note // that this means we left associate for a chain of operators. // TODO: We may want to diagnose and require users to disambiguate, at least @@ -511,16 +517,12 @@ extension Parser { var rhs: Array = [] try parseCCCMembers(into: &rhs) - if members.none(\.isSemantic) || rhs.none(\.isSemantic) { + if rhs.none(\.isSemantic) { throw Source.LocatedError( ParseError.expectedCustomCharacterClassMembers, start.location) } members = [.setOperation(members, binOp, rhs)] } - if members.none(\.isSemantic) { - throw Source.LocatedError( - ParseError.expectedCustomCharacterClassMembers, start.location) - } try source.expect("]") return CustomCC(start, members, loc(start.location.start)) } From aa7c37b4e6c935488cddf48fadc16208366e8b06 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Tue, 28 Jun 2022 15:00:51 +0100 Subject: [PATCH 23/24] Factor out parsePotentialCCRange --- Sources/_RegexParser/Regex/Parse/Parse.swift | 78 +++++++++++--------- 1 file changed, 42 insertions(+), 36 deletions(-) diff --git a/Sources/_RegexParser/Regex/Parse/Parse.swift b/Sources/_RegexParser/Regex/Parse/Parse.swift index 87afb0e11..f8ec982e7 100644 --- a/Sources/_RegexParser/Regex/Parse/Parse.swift +++ b/Sources/_RegexParser/Regex/Parse/Parse.swift @@ -552,47 +552,53 @@ extension Parser { return nil } + /// Attempt to parse a custom character class range into `members`, or regular + /// members if a range cannot be formed. + mutating func parsePotentialCCRange( + into members: inout [CustomCC.Member] + ) throws { + guard case .atom(let lhs)? = members.last else { return } + + // Try and see if we can parse a character class range. Each time we parse + // a component of the range, we append to `members` in case it ends up not + // being a range, and we bail. If we succeed in parsing, we remove the + // intermediate members. + let membersBeforeRange = members.count - 1 + while let t = try source.lexTrivia(context: context) { + members.append(.trivia(t)) + } + guard let dash = source.lexCustomCharacterClassRangeOperator() else { + return + } + + // If we can't parse a range, '-' becomes literal, e.g `[6-]`. + members.append(.atom(.init(.char("-"), dash))) + + while let t = try source.lexTrivia(context: context) { + members.append(.trivia(t)) + } + guard let rhs = try parseCCCMember() else { return } + members.append(rhs) + + guard case let .atom(rhs) = rhs else { return } + + // We've successfully parsed an atom LHS and RHS, so form a range, + // collecting the trivia we've parsed, and replacing the members that + // would have otherwise been added to the custom character class. + let rangeMemberCount = members.count - membersBeforeRange + let trivia = members.suffix(rangeMemberCount).compactMap(\.asTrivia) + members.removeLast(rangeMemberCount) + members.append(.range(.init(lhs, dash, rhs, trivia: trivia))) + } + mutating func parseCCCMembers( into members: inout Array ) throws { - // Parse members until we see the end of the custom char class or an - // operator. + // Parse members and ranges until we see the end of the custom char class + // or an operator. while let member = try parseCCCMember() { members.append(member) - - // If we have an atom, we can try to parse a character class range. Each - // time we parse a component of the range, we append to `members` in case - // it ends up not being a range, and we bail. If we succeed in parsing, we - // remove the intermediate members. - if case .atom(let lhs) = member { - let membersBeforeRange = members.count - 1 - - while let t = try source.lexTrivia(context: context) { - members.append(.trivia(t)) - } - - guard let dash = source.lexCustomCharacterClassRangeOperator() else { - continue - } - // If we can't parse a range, '-' becomes literal, e.g `[6-]`. - members.append(.atom(.init(.char("-"), dash))) - - while let t = try source.lexTrivia(context: context) { - members.append(.trivia(t)) - } - guard let rhs = try parseCCCMember() else { continue } - members.append(rhs) - - guard case let .atom(rhs) = rhs else { continue } - - // We've successfully parsed an atom LHS and RHS, so form a range, - // collecting the trivia we've parsed, and replacing the members that - // would have otherwise been added to the custom character class. - let rangeMemberCount = members.count - membersBeforeRange - let trivia = members.suffix(rangeMemberCount).compactMap(\.asTrivia) - members.removeLast(rangeMemberCount) - members.append(.range(.init(lhs, dash, rhs, trivia: trivia))) - } + try parsePotentialCCRange(into: &members) } } } From 941d28a68a969c377d1722076da28d7910e920c1 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Tue, 28 Jun 2022 15:00:51 +0100 Subject: [PATCH 24/24] Reject .NET subtraction and quote range operands Tighten up validation of character class range operands such that we reject quotes and custom character classes. This includes rejecting syntax that would be a subtraction in .NET. We throw a custom error that suggests using `--` instead. --- .../Regex/AST/CustomCharClass.swift | 23 +++++++ .../Regex/Parse/Diagnostics.swift | 5 +- .../Regex/Parse/LexicalAnalysis.swift | 19 ++++++ Sources/_RegexParser/Regex/Parse/Parse.swift | 40 +++++++++++- Tests/RegexTests/ParseTests.swift | 65 ++++++++++++++++++- 5 files changed, 147 insertions(+), 5 deletions(-) diff --git a/Sources/_RegexParser/Regex/AST/CustomCharClass.swift b/Sources/_RegexParser/Regex/AST/CustomCharClass.swift index 4602b55d3..7e6e8c285 100644 --- a/Sources/_RegexParser/Regex/AST/CustomCharClass.swift +++ b/Sources/_RegexParser/Regex/AST/CustomCharClass.swift @@ -62,6 +62,10 @@ extension AST { self.rhs = rhs self.trivia = trivia } + + public var location: SourceLocation { + lhs.location.union(with: rhs.location) + } } public enum SetOp: String, Hashable { case subtraction = "--" @@ -108,6 +112,25 @@ extension CustomCC.Member { public var isSemantic: Bool { !isTrivia } + + public var location: SourceLocation { + switch self { + case let .custom(c): return c.location + case let .range(r): return r.location + case let .atom(a): return a.location + case let .quote(q): return q.location + case let .trivia(t): return t.location + case let .setOperation(lhs, dash, rhs): + var loc = dash.location + if let lhs = lhs.first { + loc = loc.union(with: lhs.location) + } + if let rhs = rhs.last { + loc = loc.union(with: rhs.location) + } + return loc + } + } } extension AST.CustomCharacterClass { diff --git a/Sources/_RegexParser/Regex/Parse/Diagnostics.swift b/Sources/_RegexParser/Regex/Parse/Diagnostics.swift index 618ae2412..5bca2ad13 100644 --- a/Sources/_RegexParser/Regex/Parse/Diagnostics.swift +++ b/Sources/_RegexParser/Regex/Parse/Diagnostics.swift @@ -94,6 +94,7 @@ enum ParseError: Error, Hashable { case invalidNamedReference(String) case duplicateNamedCapture(String) case invalidCharacterClassRangeOperand + case unsupportedDotNetSubtraction case invalidQuantifierRange(Int, Int) case invalidCharacterRange(from: Character, to: Character) case notQuantifiable @@ -174,7 +175,9 @@ extension ParseError: CustomStringConvertible { case .expectedCustomCharacterClassMembers: return "expected custom character class members" case .invalidCharacterClassRangeOperand: - return "invalid character class range" + return "invalid bound for character class range" + case .unsupportedDotNetSubtraction: + return "subtraction with '-' is unsupported; use '--' instead" case .emptyProperty: return "empty property" case .unknownProperty(let key, let value): diff --git a/Sources/_RegexParser/Regex/Parse/LexicalAnalysis.swift b/Sources/_RegexParser/Regex/Parse/LexicalAnalysis.swift index 41b744234..be6f13fc7 100644 --- a/Sources/_RegexParser/Regex/Parse/LexicalAnalysis.swift +++ b/Sources/_RegexParser/Regex/Parse/LexicalAnalysis.swift @@ -1245,6 +1245,25 @@ extension Source { return nil } + /// Check to see if we can lex a .NET subtraction. Returns the + /// location of the `-`. + /// + /// DotNetSubtraction -> Trivia* '-' Trivia* CustomCharClass + /// + func canLexDotNetCharClassSubtraction( + context: ParsingContext + ) -> SourceLocation? { + lookahead { src in + // We can lex '-' as a .NET subtraction if it precedes a custom character + // class. + while (try? src.lexTrivia(context: context)) != nil {} + guard let dashLoc = src.tryEatWithLoc("-") else { return nil } + while (try? src.lexTrivia(context: context)) != nil {} + guard src.lexCustomCCStart() != nil else { return nil } + return dashLoc + } + } + private mutating func lexPOSIXCharacterProperty( ) throws -> Located? { try recordLoc { src in diff --git a/Sources/_RegexParser/Regex/Parse/Parse.swift b/Sources/_RegexParser/Regex/Parse/Parse.swift index f8ec982e7..52861f23d 100644 --- a/Sources/_RegexParser/Regex/Parse/Parse.swift +++ b/Sources/_RegexParser/Regex/Parse/Parse.swift @@ -557,7 +557,7 @@ extension Parser { mutating func parsePotentialCCRange( into members: inout [CustomCC.Member] ) throws { - guard case .atom(let lhs)? = members.last else { return } + guard let lhs = members.last, lhs.isSemantic else { return } // Try and see if we can parse a character class range. Each time we parse // a component of the range, we append to `members` in case it ends up not @@ -580,7 +580,33 @@ extension Parser { guard let rhs = try parseCCCMember() else { return } members.append(rhs) - guard case let .atom(rhs) = rhs else { return } + func makeOperand(_ m: CustomCC.Member, isLHS: Bool) throws -> AST.Atom { + switch m { + case .atom(let a): + return a + case .custom: + // Not supported. While .NET allows `x-[...]` to spell subtraction, we + // require `x--[...]`. We also ban `[...]-x` for consistency. + if isLHS { + throw Source.LocatedError( + ParseError.invalidCharacterClassRangeOperand, m.location) + } else { + throw Source.LocatedError( + ParseError.unsupportedDotNetSubtraction, m.location) + } + case .quote: + // Currently unsupported, we need to figure out what the semantics + // would be for grapheme/scalar modes. + throw Source.LocatedError( + ParseError.unsupported("range with quoted sequence"), m.location) + case .trivia: + throw Unreachable("Should have been lexed separately") + case .range, .setOperation: + throw Unreachable("Parsed later") + } + } + let lhsOp = try makeOperand(lhs, isLHS: true) + let rhsOp = try makeOperand(rhs, isLHS: false) // We've successfully parsed an atom LHS and RHS, so form a range, // collecting the trivia we've parsed, and replacing the members that @@ -588,7 +614,15 @@ extension Parser { let rangeMemberCount = members.count - membersBeforeRange let trivia = members.suffix(rangeMemberCount).compactMap(\.asTrivia) members.removeLast(rangeMemberCount) - members.append(.range(.init(lhs, dash, rhs, trivia: trivia))) + members.append(.range(.init(lhsOp, dash, rhsOp, trivia: trivia))) + + // We need to specially check if we can lex a .NET character class + // subtraction here as e.g `[a-c-[...]]` is allowed in .NET. Otherwise we'd + // treat the second `-` as literal. + if let dashLoc = source.canLexDotNetCharClassSubtraction(context: context) { + throw Source.LocatedError( + ParseError.unsupportedDotNetSubtraction, dashLoc) + } } mutating func parseCCCMembers( diff --git a/Tests/RegexTests/ParseTests.swift b/Tests/RegexTests/ParseTests.swift index 24cc16f57..207d7e13d 100644 --- a/Tests/RegexTests/ParseTests.swift +++ b/Tests/RegexTests/ParseTests.swift @@ -517,10 +517,36 @@ extension RegexTests { parseTest( "[a-b-c]", charClass(range_m("a", "b"), "-", "c")) + parseTest( + "[a-b-c-d]", charClass(range_m("a", "b"), "-", range_m("c", "d"))) + + parseTest("[a-c---]", charClass( + setOp(range_m("a", "c"), op: .subtraction, "-") + )) + + parseTest("(?x)[a-c -- -]", concat( + changeMatchingOptions(matchingOptions(adding: .extended)), + charClass(setOp(range_m("a", "c"), op: .subtraction, "-")) + )) + + parseTest("(?x)[a-c - - -]", concat( + changeMatchingOptions(matchingOptions(adding: .extended)), + charClass(range_m("a", "c"), range_m("-", "-")) + )) parseTest("[-a-]", charClass("-", "a", "-")) parseTest("[[a]-]", charClass(charClass("a"), "-")) - parseTest("[[a]-b]", charClass(charClass("a"), "-", "b")) + parseTest("[-[a]]", charClass("-", charClass("a"))) + + parseTest(#"(?x)[ -[b]]"#, concat( + changeMatchingOptions(matchingOptions(adding: .extended)), + charClass("-", charClass("b")) + )) + + parseTest(#"[ - [ ]]"#, charClass(range_m(" ", " "), charClass(" "))) + parseTest(#"[ - [ ] ]"#, charClass(range_m(" ", " "), charClass(" "), " ")) + + parseTest(#"[a-c-\Qd\E]"#, charClass(range_m("a", "c"), "-", quote_m("d"))) parseTest("[a-z]", charClass(range_m("a", "z"))) parseTest("[a-a]", charClass(range_m("a", "a"))) @@ -2692,6 +2718,32 @@ extension RegexTests { diagnosticTest("[[:=:]]", .emptyProperty) diagnosticTest(#"|([\d-c])?"#, .invalidCharacterClassRangeOperand) + diagnosticTest("[[a]-b]", .invalidCharacterClassRangeOperand) + + // .NET subtraction is banned, we require explicit '--'. + diagnosticTest("[a-[b]]", .unsupportedDotNetSubtraction) + diagnosticTest(#"[abc-[def]]"#, .unsupportedDotNetSubtraction) + diagnosticTest(#"[abc-[^def]]"#, .unsupportedDotNetSubtraction) + diagnosticTest(#"[\d\u{0}[a]-[b-[c]]]"#, .unsupportedDotNetSubtraction) + diagnosticTest("[a-z-[d-w-[m-o]]]", .unsupportedDotNetSubtraction) + diagnosticTest(#"[a-[:b]]"#, .unsupportedDotNetSubtraction) + diagnosticTest(#"[[a]-[b]]"#, .invalidCharacterClassRangeOperand) + diagnosticTest(#"[ -[ ]]"#, .unsupportedDotNetSubtraction) + diagnosticTest(#"(?x)[a - [b] ]"#, .unsupportedDotNetSubtraction) + + diagnosticTest(#"[a-[]]"#, .expectedCustomCharacterClassMembers) + diagnosticTest(#"[-[]]"#, .expectedCustomCharacterClassMembers) + diagnosticTest(#"(?x)[ - [ ] ]"#, .expectedCustomCharacterClassMembers) + diagnosticTest(#"(?x)[a-[ ] ]"#, .expectedCustomCharacterClassMembers) + diagnosticTest(#"[a-[:digit:]]"#, .invalidCharacterClassRangeOperand) + + diagnosticTest("[--]", .expectedCustomCharacterClassMembers) + diagnosticTest("[---]", .expectedCustomCharacterClassMembers) + diagnosticTest("[----]", .expectedCustomCharacterClassMembers) + + // Quoted sequences aren't currently supported as range operands. + diagnosticTest(#"[a-\Qbc\E]"#, .unsupported("range with quoted sequence")) + diagnosticTest(#"[\Qbc\E-de]"#, .unsupported("range with quoted sequence")) diagnosticTest(#"[_-A]"#, .invalidCharacterRange(from: "_", to: "A")) diagnosticTest(#"(?i)[_-A]"#, .invalidCharacterRange(from: "_", to: "A")) @@ -2878,6 +2930,17 @@ extension RegexTests { /# """#, .quoteMayNotSpanMultipleLines) + // .NET subtraction + diagnosticWithDelimitersTest(#""" + #/ + [ + a # interesting + - #a + [ b] # comment + ] + /# + """#, .unsupportedDotNetSubtraction) + // MARK: Group specifiers diagnosticTest(#"(*"#, .unknownGroupKind("*"))