From 75f5d650d2a8c560aa7d3d74f364312b1b0ec167 Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Sun, 9 Aug 2020 21:16:39 -0400 Subject: [PATCH 1/7] Updating for Swift 5.5 --- .circleci/config.yml | 93 - .github/workflows/Base32Crockford.yml | 172 ++ .github/workflows/arm.yml | 74 - .github/workflows/macOS.yml | 47 - .github/workflows/ubuntu.yml | 42 - .gitignore | 97 +- .periphery.yml | 3 + .swiftformat | 3 + .swiftlint.yml | 123 +- .travis.yml | 28 - Base32Crockford.podspec | 37 - Brewfile | 3 - .../Reference/enums/IdentifierDataType.md | 32 - Documentation/Reference/extensions/Array.md | 19 - .../Base32CrockfordEncodingProtocol.md | 25 - Documentation/Reference/extensions/Data.md | 25 - .../extensions/IdentifierDataType.md | 31 - Documentation/Reference/extensions/String.md | 13 - Documentation/Reference/extensions/UUID.md | 13 - .../protocols/Base32CrockfordComparer.md | 14 - .../Base32CrockfordEncodingProtocol.md | 20 - .../protocols/Base32CrockfordGenerator.md | 14 - .../structs/Base32CrockfordEncoding.md | 26 - Documentation/Reference/structs/Binary.md | 69 - .../Reference/typealiases/ByteCollection.md | 7 - Example/B32CFiOS/AppDelegate.swift | 20 - .../AppIcon.appiconset/Contents.json | 98 -- .../B32CFiOS/Assets.xcassets/Contents.json | 6 - .../Base.lproj/LaunchScreen.storyboard | 18 - ...dentifierDataTypeTableViewController.swift | 154 -- Example/B32CFiOS/Info.plist | 43 - .../B32CFiOS/IntegerCountViewController.swift | 72 - .../B32CFiOS/IntegerCountViewController.xib | 78 - Example/B32CFmacOS/main.swift | 4 - Example/B32CFtvOS/AppDelegate.swift | 11 - .../Content.imageset/Contents.json | 11 - .../Back.imagestacklayer/Contents.json | 6 - .../Contents.json | 17 - .../Content.imageset/Contents.json | 11 - .../Front.imagestacklayer/Contents.json | 6 - .../Content.imageset/Contents.json | 11 - .../Middle.imagestacklayer/Contents.json | 6 - .../Content.imageset/Contents.json | 16 - .../Back.imagestacklayer/Contents.json | 6 - .../App Icon.imagestack/Contents.json | 17 - .../Content.imageset/Contents.json | 16 - .../Front.imagestacklayer/Contents.json | 6 - .../Content.imageset/Contents.json | 16 - .../Middle.imagestacklayer/Contents.json | 6 - .../Contents.json | 32 - .../Contents.json | 24 - .../Top Shelf Image.imageset/Contents.json | 24 - .../B32CFtvOS/Assets.xcassets/Contents.json | 6 - .../Base.lproj/LaunchScreen.storyboard | 24 - Example/B32CFtvOS/Base.lproj/Main.storyboard | 28 - Example/B32CFtvOS/Info.plist | 34 - Example/B32CFtvOS/ViewController.swift | 9 - Example/B32CFtvOSTests/B32CFtvOSTests.swift | 24 - Example/B32CFtvOSTests/Info.plist | 22 - .../Circular.imageset/Contents.json | 28 - .../Contents.json | 48 - .../Extra Large.imageset/Contents.json | 28 - .../Graphic Bezel.imageset/Contents.json | 28 - .../Graphic Circular.imageset/Contents.json | 28 - .../Graphic Corner.imageset/Contents.json | 28 - .../Contents.json | 28 - .../Modular.imageset/Contents.json | 28 - .../Utilitarian.imageset/Contents.json | 28 - .../Assets.xcassets/Contents.json | 6 - .../ExtensionDelegate.swift | 3 - Example/B32CFwatchOS Extension/Info.plist | 38 - .../InterfaceController.swift | 21 - .../AppIcon.appiconset/Contents.json | 81 - .../Assets.xcassets/Contents.json | 6 - .../Base.lproj/Interface.storyboard | 15 - Example/B32CFwatchOS/Info.plist | 33 - Example/Example.xcodeproj/project.pbxproj | 1548 ----------------- .../xcschemes/iOS Example.xcscheme | 78 - .../xcschemes/macOS Example.xcscheme | 78 - .../xcschemes/tvOS Example.xcscheme | 78 - .../xcschemes/watchOS Example.xcscheme | 105 -- Example/Podfile | 47 - Example/Podfile.lock | 16 - Mintfile | 3 + Package.swift | 20 +- Scripts/before_install.sh | 37 - .../generate_testdata.js | 0 Scripts/generate_testdata.py | 7 + Scripts/gh-md-toc | 411 +++++ Scripts/lint.sh | 48 + Scripts/script.sh | 37 - Sources/Base32Crockford/Array.swift | 25 +- .../Base32CrockfordEncoding.swift | 113 +- .../Base32CrockfordEncodingProtocol.swift | 24 +- .../Base32CrockfordGenerator.swift | 55 +- Sources/Base32Crockford/Binary.swift | 6 +- Sources/Base32Crockford/Data.swift | 6 +- .../Base32Crockford/IdentifierDataType.swift | 13 +- Sources/Base32Crockford/String.swift | 8 +- Sources/base32dc/main.swift | 3 - .../Base32CrockfordTests.swift | 2 +- .../Base32PatternTests.swift | 2 +- .../EncodeDecodeTests.swift | 32 + Tests/LinuxMain.swift | 8 - bitrise.yml | 26 - 105 files changed, 1022 insertions(+), 4201 deletions(-) delete mode 100644 .circleci/config.yml create mode 100644 .github/workflows/Base32Crockford.yml delete mode 100644 .github/workflows/arm.yml delete mode 100644 .github/workflows/macOS.yml delete mode 100644 .github/workflows/ubuntu.yml create mode 100644 .periphery.yml delete mode 100644 .travis.yml delete mode 100644 Base32Crockford.podspec delete mode 100644 Brewfile delete mode 100644 Documentation/Reference/enums/IdentifierDataType.md delete mode 100644 Documentation/Reference/extensions/Array.md delete mode 100644 Documentation/Reference/extensions/Base32CrockfordEncodingProtocol.md delete mode 100644 Documentation/Reference/extensions/Data.md delete mode 100644 Documentation/Reference/extensions/IdentifierDataType.md delete mode 100644 Documentation/Reference/extensions/String.md delete mode 100644 Documentation/Reference/extensions/UUID.md delete mode 100644 Documentation/Reference/protocols/Base32CrockfordComparer.md delete mode 100644 Documentation/Reference/protocols/Base32CrockfordEncodingProtocol.md delete mode 100644 Documentation/Reference/protocols/Base32CrockfordGenerator.md delete mode 100644 Documentation/Reference/structs/Base32CrockfordEncoding.md delete mode 100644 Documentation/Reference/structs/Binary.md delete mode 100644 Documentation/Reference/typealiases/ByteCollection.md delete mode 100644 Example/B32CFiOS/AppDelegate.swift delete mode 100644 Example/B32CFiOS/Assets.xcassets/AppIcon.appiconset/Contents.json delete mode 100644 Example/B32CFiOS/Assets.xcassets/Contents.json delete mode 100644 Example/B32CFiOS/Base.lproj/LaunchScreen.storyboard delete mode 100644 Example/B32CFiOS/IdentifierDataTypeTableViewController.swift delete mode 100644 Example/B32CFiOS/Info.plist delete mode 100644 Example/B32CFiOS/IntegerCountViewController.swift delete mode 100644 Example/B32CFiOS/IntegerCountViewController.xib delete mode 100644 Example/B32CFmacOS/main.swift delete mode 100644 Example/B32CFtvOS/AppDelegate.swift delete mode 100644 Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json delete mode 100644 Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json delete mode 100644 Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json delete mode 100644 Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json delete mode 100644 Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json delete mode 100644 Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json delete mode 100644 Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json delete mode 100644 Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json delete mode 100644 Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json delete mode 100644 Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json delete mode 100644 Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json delete mode 100644 Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json delete mode 100644 Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json delete mode 100644 Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json delete mode 100644 Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json delete mode 100644 Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json delete mode 100644 Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json delete mode 100644 Example/B32CFtvOS/Assets.xcassets/Contents.json delete mode 100644 Example/B32CFtvOS/Base.lproj/LaunchScreen.storyboard delete mode 100644 Example/B32CFtvOS/Base.lproj/Main.storyboard delete mode 100644 Example/B32CFtvOS/Info.plist delete mode 100644 Example/B32CFtvOS/ViewController.swift delete mode 100644 Example/B32CFtvOSTests/B32CFtvOSTests.swift delete mode 100644 Example/B32CFtvOSTests/Info.plist delete mode 100644 Example/B32CFwatchOS Extension/Assets.xcassets/Complication.complicationset/Circular.imageset/Contents.json delete mode 100644 Example/B32CFwatchOS Extension/Assets.xcassets/Complication.complicationset/Contents.json delete mode 100644 Example/B32CFwatchOS Extension/Assets.xcassets/Complication.complicationset/Extra Large.imageset/Contents.json delete mode 100644 Example/B32CFwatchOS Extension/Assets.xcassets/Complication.complicationset/Graphic Bezel.imageset/Contents.json delete mode 100644 Example/B32CFwatchOS Extension/Assets.xcassets/Complication.complicationset/Graphic Circular.imageset/Contents.json delete mode 100644 Example/B32CFwatchOS Extension/Assets.xcassets/Complication.complicationset/Graphic Corner.imageset/Contents.json delete mode 100644 Example/B32CFwatchOS Extension/Assets.xcassets/Complication.complicationset/Graphic Large Rectangular.imageset/Contents.json delete mode 100644 Example/B32CFwatchOS Extension/Assets.xcassets/Complication.complicationset/Modular.imageset/Contents.json delete mode 100644 Example/B32CFwatchOS Extension/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/Contents.json delete mode 100644 Example/B32CFwatchOS Extension/Assets.xcassets/Contents.json delete mode 100644 Example/B32CFwatchOS Extension/ExtensionDelegate.swift delete mode 100644 Example/B32CFwatchOS Extension/Info.plist delete mode 100644 Example/B32CFwatchOS Extension/InterfaceController.swift delete mode 100644 Example/B32CFwatchOS/Assets.xcassets/AppIcon.appiconset/Contents.json delete mode 100644 Example/B32CFwatchOS/Assets.xcassets/Contents.json delete mode 100644 Example/B32CFwatchOS/Base.lproj/Interface.storyboard delete mode 100644 Example/B32CFwatchOS/Info.plist delete mode 100644 Example/Example.xcodeproj/project.pbxproj delete mode 100644 Example/Example.xcodeproj/xcshareddata/xcschemes/iOS Example.xcscheme delete mode 100644 Example/Example.xcodeproj/xcshareddata/xcschemes/macOS Example.xcscheme delete mode 100644 Example/Example.xcodeproj/xcshareddata/xcschemes/tvOS Example.xcscheme delete mode 100644 Example/Example.xcodeproj/xcshareddata/xcschemes/watchOS Example.xcscheme delete mode 100644 Example/Podfile delete mode 100644 Example/Podfile.lock create mode 100644 Mintfile delete mode 100755 Scripts/before_install.sh rename generate_testdata.js => Scripts/generate_testdata.js (100%) create mode 100644 Scripts/generate_testdata.py create mode 100755 Scripts/gh-md-toc create mode 100755 Scripts/lint.sh delete mode 100755 Scripts/script.sh delete mode 100644 Sources/base32dc/main.swift delete mode 100644 Tests/LinuxMain.swift delete mode 100644 bitrise.yml diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index ab1429c..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,93 +0,0 @@ -# For a detailed guide to building and testing on iOS, read the docs: -# https://circleci.com/docs/2.0/testing-ios/ - -version: 2.1 -parameters: - package-name: - type: string - default: "Base32Crockford" - swift-ver: - type: string - default: "5.2.4" - codecov-upload-file: - type: string - default: "info.lcov" -orbs: - codecov: codecov/codecov@1.0.5 -jobs: - build-xenial: - machine: - image: ubuntu-1604:201903-01 - environment: - PACKAGE_NAME: << pipeline.parameters.package-name >> - SWIFT_VER: << pipeline.parameters.swift-ver >> - steps: - - checkout - - run: - name: Update PATH and Define Environment Variable at Runtime - command: | - echo 'export RELEASE_DOT=$(lsb_release -sr)' >> $BASH_ENV - echo 'export RELEASE_NAME=$(lsb_release -sc)' >> $BASH_ENV - echo 'export RELEASE_NUM=${RELEASE_DOT//[-._]/}' >> $BASH_ENV - source $BASH_ENV - - run: - name: Download Swift 5.2 - command: wget -q https://swift.org/builds/swift-${SWIFT_VER}-release/ubuntu${RELEASE_NUM}/swift-${SWIFT_VER}-RELEASE/swift-${SWIFT_VER}-RELEASE-ubuntu${RELEASE_DOT}.tar.gz - - run: - name: Extract Swift 5.2 - command: tar xzf swift-${SWIFT_VER}-RELEASE-ubuntu${RELEASE_DOT}.tar.gz - - run: - name: Add Path - command: echo 'export PATH=${PWD}/swift-${SWIFT_VER}-RELEASE-ubuntu${RELEASE_DOT}/usr/bin:$PATH' >> $BASH_ENV - - run: - name: Resolve - command: swift package resolve - - run: - name: Build - command: swift build -v - - run: - name: Run tests - command: swift test --enable-test-discovery --enable-code-coverage - - run: - name: Verify Valid Swift Package - command: curl -s https://raw.githubusercontent.com/daveverwer/SwiftPMLibrary/master/script.sh | bash -s -- mine - - run: - name: Prepare Code Coverage - command: llvm-cov export -format="lcov" .build/x86_64-unknown-linux-gnu/debug/${PACKAGE_NAME}PackageTests.xctest -instr-profile .build/debug/codecov/default.profdata > info.lcov - - codecov/upload: - file: << pipeline.parameters.codecov-upload-file >> - flags: circleci,${RELEASE_NAME} - build-catalina-11_4_1: - macos: - xcode: "11.4.1" - environment: - PACKAGE_NAME: << pipeline.parameters.package-name >> - steps: - - checkout - - run: - name: Build - command: swift build - - run: - name: Lint - command: swiftformat --lint . && swiftlint - - run: - name: Run Swift Package Tests - command: swift test -v --enable-code-coverage - - run: - name: Prepare Code Coverage - command: xcrun llvm-cov export -format="lcov" .build/debug/${PACKAGE_NAME}PackageTests -instr-profile .build/debug/codecov/default.profdata > info.lcov - - codecov/upload: - file: << pipeline.parameters.codecov-upload-file >> - flags: circleci,macOS - - run: - name: Run iOS Tests - command: xcodebuild build test -scheme ${PACKAGE_NAME}-Package -destination 'name=iPhone 11' - - codecov/upload: - file: << pipeline.parameters.codecov-upload-file >> - flags: circleci,iOS -workflows: - version: 2 - build: - jobs: - #- build-catalina-11_4_1 - - build-xenial diff --git a/.github/workflows/Base32Crockford.yml b/.github/workflows/Base32Crockford.yml new file mode 100644 index 0000000..b85ade3 --- /dev/null +++ b/.github/workflows/Base32Crockford.yml @@ -0,0 +1,172 @@ +name: Base32Crockford +on: + push: + branches-ignore: + - '*WIP' +jobs: + build-ubuntu: + name: Build on Ubuntu + env: + SWIFT_VER: ${{ matrix.swift-version }} + runs-on: ${{ matrix.runs-on }} + if: "!contains(github.event.head_commit.message, 'ci skip')" + strategy: + matrix: + runs-on: [ubuntu-18.04, ubuntu-20.04] + swift-version: [5.5.2, 5.6.2, 5.7] + include: + - runs-on: ubuntu-22.04 + swift-version: 5.7.1 + steps: + - uses: actions/checkout@v3 + - name: Cache swift package modules + id: cache-spm-linux + uses: actions/cache@v3 + env: + cache-name: cache-spm + with: + path: .build + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('Package.resolved') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + - name: Set Ubuntu Release DOT + run: echo "RELEASE_DOT=$(lsb_release -sr)" >> $GITHUB_ENV + - name: Set Ubuntu Release NUM + run: echo "RELEASE_NUM=${RELEASE_DOT//[-._]/}" >> $GITHUB_ENV + - name: Set Ubuntu Codename + run: echo "RELEASE_NAME=$(lsb_release -sc)" >> $GITHUB_ENV + - name: Cache swift + id: cache-swift-linux + uses: actions/cache@v3 + env: + cache-name: cache-swift + with: + path: swift-${{ env.SWIFT_VER }}-RELEASE-ubuntu${{ env.RELEASE_DOT }} + key: ${{ runner.os }}-${{ env.RELEASE_DOT }}-${{ env.cache-name }}-${{ env.SWIFT_VER }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + - name: Download Swift + if: steps.cache-swift-linux.outputs.cache-hit != 'true' + run: curl -O https://download.swift.org/swift-${SWIFT_VER}-release/ubuntu${RELEASE_NUM}/swift-${SWIFT_VER}-RELEASE/swift-${SWIFT_VER}-RELEASE-ubuntu${RELEASE_DOT}.tar.gz + - name: Extract Swift + if: steps.cache-swift-linux.outputs.cache-hit != 'true' + run: tar xzf swift-${SWIFT_VER}-RELEASE-ubuntu${RELEASE_DOT}.tar.gz + - name: Add Path + run: echo "$GITHUB_WORKSPACE/swift-${SWIFT_VER}-RELEASE-ubuntu${RELEASE_DOT}/usr/bin" >> $GITHUB_PATH + - name: Build + run: swift build + - name: Test + run: swift test --enable-code-coverage + - uses: sersoft-gmbh/swift-coverage-action@v3 + with: + fail-on-empty-output: true + - name: Upload package coverage to Codecov + uses: codecov/codecov-action@v3 + with: + fail_ci_if_error: true + flags: spm,swift-${{ matrix.swift-version }} + token: ${{ secrets.CODECOV_TOKEN }} + build-macos: + name: Build on macOS + runs-on: ${{ matrix.runs-on }} + if: "!contains(github.event.head_commit.message, 'ci skip')" + strategy: + matrix: + include: + - runs-on: macos-12 + xcode: "/Applications/Xcode_13.4.1.app" + iOSVersion: 15.5 + watchOSVersion: 8.5 + watchName: "Apple Watch Series 7 - 41mm" + - runs-on: macos-12 + xcode: "/Applications/Xcode_14.0.1.app" + iOSVersion: "16.0" + watchOSVersion: "9.0" + watchName: "Apple Watch Series 8 (41mm)" + - runs-on: macos-12 + xcode: "/Applications/Xcode_14.1.app" + iOSVersion: "16.1" + watchOSVersion: "9.1" + watchName: "Apple Watch Ultra (49mm)" + - runs-on: macos-12 + xcode: "/Applications/Xcode_13.3.app" + iOSVersion: 15.4 + watchOSVersion: 8.5 + watchName: "Apple Watch Series 7 - 41mm" + steps: + - uses: actions/checkout@v3 + - name: Cache swift package modules + id: cache-spm-macos + uses: actions/cache@v3 + env: + cache-name: cache-spm + with: + path: .build + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('Package.resolved') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + - name: Cache mint + if: ${{ github.event_name == 'pull_request' && github.base_ref == 'main' && matrix.xcode == '/Applications/Xcode_14.1.app' }} + id: cache-mint + uses: actions/cache@v3 + env: + cache-name: cache-mint + with: + path: .mint + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('Mintfile') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + - name: Set Xcode Name + run: echo "XCODE_NAME=$(basename -- ${{ matrix.xcode }} | sed 's/\.[^.]*$//' | cut -d'_' -f2)" >> $GITHUB_ENV + - name: Setup Xcode + run: sudo xcode-select -s ${{ matrix.xcode }}/Contents/Developer + - name: Install mint + if: ${{ github.event_name == 'pull_request' && github.base_ref == 'main' && matrix.xcode == '/Applications/Xcode_14.1.app' }} + run: | + brew update + brew install mint + - name: Build + run: swift build + - name: Lint + run: ./scripts/lint.sh + if: ${{ github.event_name == 'pull_request' && github.base_ref == 'main' && matrix.xcode == '/Applications/Xcode_14.1.app' }} + - name: Dump PIF + if: startsWith(matrix.xcode,'/Applications/Xcode_14') + run: | + swift package dump-pif > /dev/null + MAX_ATTEMPT=3 + ATTEMPT=0 + while [ -z $SUCCESS ] && [ "$ATTEMPT" -le "$MAX_ATTEMPT" ]; do + xcodebuild clean -scheme Base32Crockford -destination 'generic/platform=iOS' | grep -q "CLEAN SUCCEEDED" && SUCCESS=true + ATTEMPT=$(($ATTEMPT+1)) + done + - name: Run iOS target tests + run: xcodebuild test -scheme Base32Crockford -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 13,OS=${{ matrix.iOSVersion }}' -enableCodeCoverage YES build test + - uses: sersoft-gmbh/swift-coverage-action@v3 + with: + fail-on-empty-output: true + - name: Upload iOS coverage to Codecov + uses: codecov/codecov-action@v3 + with: + fail_ci_if_error: true + flags: iOS,iOS-${{ matrix.iOSVersion }} + token: ${{ secrets.CODECOV_TOKEN }} + - name: Run watchOS target tests + run: xcodebuild test -scheme Base32Crockford -sdk watchsimulator -destination 'platform=watchOS Simulator,name=${{ matrix.watchName }},OS=${{ matrix.watchOSVersion }}' -enableCodeCoverage YES build test + - uses: sersoft-gmbh/swift-coverage-action@v3 + with: + fail-on-empty-output: true + - name: Upload watchOS coverage to Codecov + uses: codecov/codecov-action@v3 + with: + fail_ci_if_error: true + flags: watchOS,watchOS${{ matrix.watchOSVersion }} + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/arm.yml b/.github/workflows/arm.yml deleted file mode 100644 index 5352475..0000000 --- a/.github/workflows/arm.yml +++ /dev/null @@ -1,74 +0,0 @@ -name: arm - -on: [push] - -jobs: - build: - env: - PACKAGE_NAME: Base32Crockford - SWIFT_VER: 5.2.4 - - runs-on: ubuntu-latest - if: "!contains(github.event.head_commit.message, 'ci skip')" - - strategy: - matrix: - architecture: [aarch64] - distribution: [ubuntu16.04,ubuntu18.04,ubuntu20.04] - steps: - - uses: actions/checkout@v2 - - name: Build with Swift on arm - uses: uraimo/run-on-arch-action@master - env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - with: - architecture: ${{ matrix.architecture }} - distribution: ${{ matrix.distribution }} - run: | - export DEBIAN_FRONTEND=noninteractive - ln -fs /usr/share/zoneinfo/America/New_York /etc/localtime - apt update - apt install -y curl lsb-release sudo clang - RELEASE_DOT=$(lsb_release -sr) - RELEASE_NUM=${RELEASE_DOT//[-._]/} - RELEASE_NAME=$(lsb_release -sc) - if [[ $RELEASE_DOT == "20.04" ]]; then - sudo apt-get -y install \ - binutils \ - git \ - gnupg2 \ - libc6-dev \ - libcurl4 \ - libedit2 \ - libgcc-9-dev \ - libpython2.7 \ - libsqlite3-0 \ - libstdc++-9-dev \ - libxml2 \ - libz3-dev \ - pkg-config \ - tzdata \ - zlib1g-dev - elif [[ $RELEASE_DOT == "16.04" ]]; then - apt-get -y install \ - binutils \ - git \ - libc6-dev \ - libcurl3 \ - libedit2 \ - libgcc-5-dev \ - libpython2.7 \ - libsqlite3-0 \ - libstdc++-5-dev \ - libxml2 \ - pkg-config \ - tzdata \ - zlib1g-dev - fi - dpkg-reconfigure --frontend noninteractive tzdata - curl -s https://packagecloud.io/install/repositories/swift-arm/release/script.deb.sh | sudo bash - apt-get install -y swift5 - swift build - swift test --enable-test-discovery --enable-code-coverage - llvm-cov export -format="lcov" .build/aarch64-unknown-linux-gnu/debug/${{ env.PACKAGE_NAME }}PackageTests.xctest -instr-profile .build/debug/codecov/default.profdata > info.lcov - bash <(curl https://codecov.io/bash) -F github -F ${RELEASE_NAME} -F aarch64 -n ${{ github.sha }} diff --git a/.github/workflows/macOS.yml b/.github/workflows/macOS.yml deleted file mode 100644 index 2369d40..0000000 --- a/.github/workflows/macOS.yml +++ /dev/null @@ -1,47 +0,0 @@ -name: macOS - -on: [push] - -jobs: - build: - env: - PACKAGE_NAME: Base32Crockford - XCODE_PATH: /Applications/Xcode_11.5.app - - runs-on: macos-latest - if: "!contains(github.event.head_commit.message, 'ci skip')" - - steps: - - uses: actions/checkout@v2 - - name: Set Xcode Name - run: echo "::set-env name=XCODE_NAME::$(basename -- ${{ env.XCODE_PATH }} | sed 's/\.[^.]*$//' | cut -d'_' -f2)" - - name: Setup Xcode - run: sudo xcode-select -s ${{ env.XCODE_PATH }}/Contents/Developer - - name: Prepare Build - run: brew bundle - - name: Build - run: swift build - - name: Lint - run: swiftformat --lint . && swiftlint - - name: Run tests - run: swift test -v --enable-code-coverage - - name: Prepare Code Coverage - run: xcrun llvm-cov export -format="lcov" .build/debug/${{ env.PACKAGE_NAME }}PackageTests.xctest/Contents/MacOS/${{ env.PACKAGE_NAME }}PackageTests -instr-profile .build/debug/codecov/default.profdata > info.lcov - - name: Upload to CodeCov.io - run: bash <(curl https://codecov.io/bash) -F github -F macOS -n ${{ github.sha }} - env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - - name: Build Documentation - run: sourcedocs generate --spm-module ${{ env.PACKAGE_NAME }} - - name: Verify Valid Swift Package - run: curl -s https://raw.githubusercontent.com/daveverwer/SwiftPMLibrary/master/script.sh | bash -s -- mine - - name: CocoaPods Action - run: pod lib lint - - name: Commit files - run: | - git config --local user.email "action@github.com" - git config --local user.name "GitHub Action" - git status - git add Documentation - git diff-index --quiet HEAD || git commit -m "[github action] Update Docs" - git push diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml deleted file mode 100644 index f480254..0000000 --- a/.github/workflows/ubuntu.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: ubuntu - -on: [push] - -jobs: - build: - env: - PACKAGE_NAME: Base32Crockford - SWIFT_VER: 5.2.4 - - runs-on: ${{ matrix.runs-on }} - if: "!contains(github.event.head_commit.message, 'ci skip')" - - strategy: - matrix: - runs-on: [ubuntu-18.04,ubuntu-20.04] - steps: - - uses: actions/checkout@v2 - - name: Set Ubuntu Release DOT - run: echo "::set-env name=RELEASE_DOT::$(lsb_release -sr)" - - name: Set Ubuntu Release NUM - run: echo "::set-env name=RELEASE_NUM::${RELEASE_DOT//[-._]/}" - - name: Set Ubuntu Codename - run: echo "::set-env name=RELEASE_NAME::$(lsb_release -sc)" - - name: Download Swift - run: curl -O https://swift.org/builds/swift-${SWIFT_VER}-release/ubuntu${RELEASE_NUM}/swift-${SWIFT_VER}-RELEASE/swift-${SWIFT_VER}-RELEASE-ubuntu${RELEASE_DOT}.tar.gz - - name: Extract Swift - run: tar xzf swift-${SWIFT_VER}-RELEASE-ubuntu${RELEASE_DOT}.tar.gz - - name: Add Path - run: echo "::add-path::$GITHUB_WORKSPACE/swift-${SWIFT_VER}-RELEASE-ubuntu${RELEASE_DOT}/usr/bin" - - name: Build - run: swift build - - name: Run tests - run: swift test --enable-test-discovery --enable-code-coverage - - name: Verify Valid Swift Package - run: curl -s https://raw.githubusercontent.com/daveverwer/SwiftPMLibrary/master/script.sh | bash -s -- mine - - name: Prepare Code Coverage - run: llvm-cov export -format="lcov" .build/x86_64-unknown-linux-gnu/debug/${{ env.PACKAGE_NAME }}PackageTests.xctest -instr-profile .build/debug/codecov/default.profdata > info.lcov - - name: Upload to CodeCov.io - run: bash <(curl https://codecov.io/bash) -F github -F ${RELEASE_NAME} -n ${{ github.sha }} - env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.gitignore b/.gitignore index f60c665..3bad4d6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,18 +1,16 @@ -# Created by https://www.gitignore.io/api/swiftpm,swiftpackagemanager,xcode,swift,macos -# Edit at https://www.gitignore.io/?templates=swiftpm,swiftpackagemanager,xcode,swift,macos - +# Created by https://www.toptal.com/developers/gitignore/api/swift,swiftpm,swiftpackagemanager,xcode,macos +# Edit at https://www.toptal.com/developers/gitignore?templates=swift,swiftpm,swiftpackagemanager,xcode,macos ### macOS ### # General .DS_Store -*.DS_Store - .AppleDouble .LSOverride # Icon must end with two \r Icon + # Thumbnails ._* @@ -32,20 +30,26 @@ Network Trash Folder Temporary Items .apdisk +### macOS Patch ### +# iCloud generated files +*.icloud + ### Swift ### # Xcode # # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore -## Build generated -build/ -DerivedData/ +## User settings +xcuserdata/ -## Various settings -# Xcode -# -build/ +## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) +*.xcscmblueprint +*.xccheckout +## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) +build/ +DerivedData/ +*.moved-aside *.pbxuser !default.pbxuser *.mode1v3 @@ -54,17 +58,11 @@ build/ !default.mode2v3 *.perspectivev3 !default.perspectivev3 -xcuserdata/ - -## Other -*.moved-aside -*.xccheckout -*.xcuserstate - -*.xcscmblueprint ## Obj-C/Swift specific *.hmap + +## App packaging *.ipa *.dSYM.zip *.dSYM @@ -78,39 +76,34 @@ playground.xcworkspace # Packages/ # Package.pins # Package.resolved +# *.xcodeproj +# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata +# hence it is not needed unless you have added a package configuration file to your project +.swiftpm + .build/ -xcuserdata -*.xccheckout -*.moved-aside -DerivedData -*.hmap -*.ipa -*.xcuserstate # CocoaPods - # We recommend against adding the Pods directory to your .gitignore. However # you should judge for yourself, the pros and cons are mentioned at: # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control - -Example/Pods/ -Example/*.xcworkspace +# Pods/ +# Add this line if you want to avoid checking in source code from the Xcode workspace +# *.xcworkspace # Carthage - # Add this line if you want to avoid checking in source code from Carthage dependencies. # Carthage/Checkouts -Carthage/Build +Carthage/Build/ # Accio dependency management Dependencies/ .accio/ # fastlane - -# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the -# screenshots whenever they are needed. +# It is recommended to not store the screenshots in the git repo. +# Instead, use fastlane to re-generate the screenshots whenever they are needed. # For more information about the recommended setup visit: # https://docs.fastlane.tools/best-practices/source-control/#source-control @@ -122,48 +115,30 @@ fastlane/test_output # Code Injection # After new code Injection tools there's a generated folder /iOSInjectionProject # https://github.com/johnno1962/injectionforxcode -VCS.swift iOSInjectionProject/ -Products - ### SwiftPackageManager ### Packages xcuserdata -/*.xcodeproj -.tmp +*.xcodeproj ### SwiftPM ### ### Xcode ### -# Xcode -# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore - -## User settings - -## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) -## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) - -## Xcode Patch -/*.xcodeproj/* -.swiftpm +## Xcode 8 and earlier +### Xcode Patch ### +*.xcodeproj/* !*.xcodeproj/project.pbxproj !*.xcodeproj/xcshareddata/ +!*.xcodeproj/project.xcworkspace/ !*.xcworkspace/contents.xcworkspacedata /*.gcno - -### Xcode Patch ### **/xcshareddata/WorkspaceSettings.xcsettings -# End of https://www.gitignore.io/api/swiftpm,swiftpackagemanager,xcode,swift,macos -*.xcworkspace -Pods -*.lcov -Brewfile.lock.json -Package.resolved -Documentation/Reference/README.md +# End of https://www.toptal.com/developers/gitignore/api/swift,swiftpm,swiftpackagemanager,xcode,macos +.mint \ No newline at end of file diff --git a/.periphery.yml b/.periphery.yml new file mode 100644 index 0000000..e2749ad --- /dev/null +++ b/.periphery.yml @@ -0,0 +1,3 @@ +retain_public: true +targets: +- Base32Crockford diff --git a/.swiftformat b/.swiftformat index 02d7018..2601bf7 100644 --- a/.swiftformat +++ b/.swiftformat @@ -1,4 +1,7 @@ --indent 2 --header strip --commas inline +--disable wrapMultilineStatementBraces +--extensionacl on-declarations +--decimalgrouping 3,4 --exclude .build, DerivedData diff --git a/.swiftlint.yml b/.swiftlint.yml index fa3d11f..b98ce23 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -1,13 +1,122 @@ -cyclomatic_complexity: 12 -file_length: 550 -function_body_length: 80 +opt_in_rules: + - anyobject_protocol + - array_init + - attributes + - closure_body_length + - closure_end_indentation + - closure_spacing + - collection_alignment + - conditional_returns_on_newline + - contains_over_filter_count + - contains_over_filter_is_empty + - contains_over_first_not_nil + - contains_over_range_nil_comparison + - convenience_type + - discouraged_object_literal + - discouraged_optional_boolean + - empty_collection_literal + - empty_count + - empty_string + - empty_xctest_method + - enum_case_associated_values_count + - expiring_todo + - explicit_acl + - explicit_init + - explicit_self + - explicit_top_level_acl + - fallthrough + - fatal_error_message + - file_header + - file_name + - file_name_no_space + - file_types_order + - flatmap_over_map_reduce + - force_unwrapping + - function_default_parameter_at_end + - ibinspectable_in_extension + - identical_operands + - implicit_return + - implicitly_unwrapped_optional + - indentation_width + - joined_default_parameter + - last_where + - legacy_multiple + - legacy_random + - literal_expression_end_indentation + - lower_acl_than_parent + #- missing_docs + - modifier_order + #- multiline_arguments + - multiline_arguments_brackets + - multiline_function_chains + - multiline_literal_brackets + - multiline_parameters + - nimble_operator + - nslocalizedstring_key + - nslocalizedstring_require_bundle + - number_separator + - object_literal + - operator_usage_whitespace + - optional_enum_case_matching + - overridden_super_call + - override_in_extension + - pattern_matching_keywords + - prefer_self_type_over_type_of_self + - prefer_zero_over_explicit_init + - private_action + - private_outlet + - prohibited_interface_builder + - prohibited_super_call + - quick_discouraged_call + - quick_discouraged_focused_test + - quick_discouraged_pending_test + - reduce_into + - redundant_nil_coalescing + - redundant_type_annotation + - required_enum_case + - single_test_class + - sorted_first_last + - sorted_imports + - static_operator + - strict_fileprivate + - strong_iboutlet + - switch_case_on_newline + - toggle_bool + #- trailing_closure + - type_contents_order + - unavailable_function + - unneeded_parentheses_in_closure_argument + - unowned_variable_capture + - unused_declaration + - unused_import + - vertical_parameter_alignment_on_call + - vertical_whitespace_between_cases + - vertical_whitespace_closing_braces + - vertical_whitespace_opening_braces + - xct_specific_matcher + - yoda_condition +cyclomatic_complexity: + - 6 + - 9 +type_body_length: + - 250 + - 500 +file_length: + - 400 + - 800 +function_body_length: + - 25 + - 40 function_parameter_count: 8 -line_length: 150 -type_body_length: 300 +line_length: + - 90 + - 90 identifier_name: - excluded: # excluded via string array + excluded: - id excluded: - - Tests/*/XCTestManifests.swift + - Tests - DerivedData - .build +indentation_width: + indentation_width: 2 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 95907ab..0000000 --- a/.travis.yml +++ /dev/null @@ -1,28 +0,0 @@ -jobs: - include: - - os: linux - dist: bionic - arch: amd64 - - os: linux - dist: focal - arch: amd64 - - os: linux - dist: bionic - arch: arm64 - - os: linux - dist: focal - arch: arm64 - - os: osx - osx_image: xcode11.3 - - os: osx - osx_image: xcode11.4 - - os: osx - osx_image: xcode11.5 -env: - global: - - FRAMEWORK_NAME=Base32Crockford - - SWIFT_VER=5.2.4 -before_install: - - bash -e ./Scripts/before_install.sh -script: - - bash -e ./Scripts/script.sh diff --git a/Base32Crockford.podspec b/Base32Crockford.podspec deleted file mode 100644 index 184d437..0000000 --- a/Base32Crockford.podspec +++ /dev/null @@ -1,37 +0,0 @@ -# -# Be sure to run `pod lib lint Base32Crockford.podspec' to ensure this is a -# valid spec before submitting. -# -# Any lines starting with a # are optional, but their use is encouraged -# To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html -# - -Pod::Spec.new do |s| - s.name = 'Base32Crockford' - s.version = '0.4.0' - s.summary = 'Generate, encode, and decode data in a Base32 format.' - s.swift_version = '5' - -# This description is used to generate tags and improve search results. -# * Think: What does it do? Why did you write it? What is the focus? -# * Try to keep it short, snappy and to the point. -# * Write the description between the DESC delimiters below. -# * Finally, don't worry about the indent, CocoaPods strips it! - - s.description = <<-DESC -Using Douglas Crockford's Base32 encoding (https://www.crockford.com/wrmg/base32.html), this library can generate, encode, and decode data in a Base32 format. - DESC - - s.homepage = 'https://github.com/brightdigit/Base32Crockford' - # s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2' - s.license = { :type => 'MIT', :file => 'LICENSE' } - s.author = { 'leogdion@brightdigit.com' => 'leogdion@brightdigit.com' } - s.source = { :git => 'https://github.com/brightdigit/Base32Crockford.git', :tag => "0.4.0" } - # s.social_media_url = 'https://twitter.com/ - - s.source_files = 'Sources/Base32Crockford/**/*' - s.ios.deployment_target = '8' - s.osx.deployment_target = '10.9' - s.tvos.deployment_target = '9.0' - s.watchos.deployment_target = '2.0' -end diff --git a/Brewfile b/Brewfile deleted file mode 100644 index 0dd4166..0000000 --- a/Brewfile +++ /dev/null @@ -1,3 +0,0 @@ -brew "swiftformat" -brew "swiftlint" -brew "sourcedocs" diff --git a/Documentation/Reference/enums/IdentifierDataType.md b/Documentation/Reference/enums/IdentifierDataType.md deleted file mode 100644 index 6546f94..0000000 --- a/Documentation/Reference/enums/IdentifierDataType.md +++ /dev/null @@ -1,32 +0,0 @@ -**ENUM** - -# `IdentifierDataType` - -```swift -public enum IdentifierDataType: Equatable -``` - -## Cases -### `default` - -```swift -case `default` -``` - -### `uuid` - -```swift -case uuid -``` - -### `bytes(size:)` - -```swift -case bytes(size: Int) -``` - -### `minimumCount(_:)` - -```swift -case minimumCount(Int) -``` diff --git a/Documentation/Reference/extensions/Array.md b/Documentation/Reference/extensions/Array.md deleted file mode 100644 index 967d879..0000000 --- a/Documentation/Reference/extensions/Array.md +++ /dev/null @@ -1,19 +0,0 @@ -**EXTENSION** - -# `Array` -```swift -extension Array where Element == UInt8 -``` - -## Methods -### `init(uuid:)` - -```swift -public init(uuid: UUID) -``` - -### `random(withCount:in:)` - -```swift -public static func random(withCount count: Int, in range: ClosedRange? = nil) -> Array -``` diff --git a/Documentation/Reference/extensions/Base32CrockfordEncodingProtocol.md b/Documentation/Reference/extensions/Base32CrockfordEncodingProtocol.md deleted file mode 100644 index 9515564..0000000 --- a/Documentation/Reference/extensions/Base32CrockfordEncodingProtocol.md +++ /dev/null @@ -1,25 +0,0 @@ -**EXTENSION** - -# `Base32CrockfordEncodingProtocol` -```swift -public extension Base32CrockfordEncodingProtocol -``` - -## Methods -### `standardize(string:)` - -```swift -func standardize(string: String) -> String -``` - -### `generateIdentifier(from:)` - -```swift -public func generateIdentifier(from identifierDataType: IdentifierDataType) -> String -``` - -### `generate(_:from:)` - -```swift -public func generate(_ count: Int, from identifierDataType: IdentifierDataType) -> [String] -``` diff --git a/Documentation/Reference/extensions/Data.md b/Documentation/Reference/extensions/Data.md deleted file mode 100644 index a85cabd..0000000 --- a/Documentation/Reference/extensions/Data.md +++ /dev/null @@ -1,25 +0,0 @@ -**EXTENSION** - -# `Data` -```swift -extension Data -``` - -## Methods -### `random(withNumberOfBytes:)` - -```swift -public static func random(withNumberOfBytes count: Int) -> Data -``` - -### `bytesRequired(forUniqueCountOf:)` - -```swift -public static func bytesRequired(forUniqueCountOf count: Int) -> Int -``` - -### `uniqueIdentifier(forMinimumCount:)` - -```swift -public static func uniqueIdentifier(forMinimumCount count: Int) -> Data -``` diff --git a/Documentation/Reference/extensions/IdentifierDataType.md b/Documentation/Reference/extensions/IdentifierDataType.md deleted file mode 100644 index 5108f0c..0000000 --- a/Documentation/Reference/extensions/IdentifierDataType.md +++ /dev/null @@ -1,31 +0,0 @@ -**EXTENSION** - -# `IdentifierDataType` -```swift -extension IdentifierDataType: Codable -``` - -## Methods -### `init(from:)` - -```swift -public init(from decoder: Decoder) throws -``` - -#### Parameters - -| Name | Description | -| ---- | ----------- | -| decoder | The decoder to read data from. | - -### `encode(to:)` - -```swift -public func encode(to encoder: Encoder) throws -``` - -#### Parameters - -| Name | Description | -| ---- | ----------- | -| encoder | The encoder to write data to. | \ No newline at end of file diff --git a/Documentation/Reference/extensions/String.md b/Documentation/Reference/extensions/String.md deleted file mode 100644 index 93a3dff..0000000 --- a/Documentation/Reference/extensions/String.md +++ /dev/null @@ -1,13 +0,0 @@ -**EXTENSION** - -# `String` -```swift -extension String -``` - -## Methods -### `pad(toSize:)` - -```swift -public func pad(toSize: Int) -> String -``` diff --git a/Documentation/Reference/extensions/UUID.md b/Documentation/Reference/extensions/UUID.md deleted file mode 100644 index 028a0fe..0000000 --- a/Documentation/Reference/extensions/UUID.md +++ /dev/null @@ -1,13 +0,0 @@ -**EXTENSION** - -# `UUID` -```swift -extension UUID -``` - -## Methods -### `init(data:)` - -```swift -public init(data: Data) -``` diff --git a/Documentation/Reference/protocols/Base32CrockfordComparer.md b/Documentation/Reference/protocols/Base32CrockfordComparer.md deleted file mode 100644 index 217de0a..0000000 --- a/Documentation/Reference/protocols/Base32CrockfordComparer.md +++ /dev/null @@ -1,14 +0,0 @@ -**PROTOCOL** - -# `Base32CrockfordComparer` - -```swift -public protocol Base32CrockfordComparer -``` - -## Methods -### `data(_:hasEncodedPrefix:)` - -```swift -func data(_ data: Data, hasEncodedPrefix prefix: String) -> Bool -``` diff --git a/Documentation/Reference/protocols/Base32CrockfordEncodingProtocol.md b/Documentation/Reference/protocols/Base32CrockfordEncodingProtocol.md deleted file mode 100644 index a8fa103..0000000 --- a/Documentation/Reference/protocols/Base32CrockfordEncodingProtocol.md +++ /dev/null @@ -1,20 +0,0 @@ -**PROTOCOL** - -# `Base32CrockfordEncodingProtocol` - -```swift -public protocol Base32CrockfordEncodingProtocol: Base32CrockfordGenerator -``` - -## Methods -### `encode(data:)` - -```swift -func encode(data: Data) -> String -``` - -### `decode(base32Encoded:)` - -```swift -func decode(base32Encoded string: String) throws -> Data -``` diff --git a/Documentation/Reference/protocols/Base32CrockfordGenerator.md b/Documentation/Reference/protocols/Base32CrockfordGenerator.md deleted file mode 100644 index f0c1728..0000000 --- a/Documentation/Reference/protocols/Base32CrockfordGenerator.md +++ /dev/null @@ -1,14 +0,0 @@ -**PROTOCOL** - -# `Base32CrockfordGenerator` - -```swift -public protocol Base32CrockfordGenerator -``` - -## Methods -### `generateIdentifier(from:)` - -```swift -func generateIdentifier(from identifierDataType: IdentifierDataType) -> String -``` diff --git a/Documentation/Reference/structs/Base32CrockfordEncoding.md b/Documentation/Reference/structs/Base32CrockfordEncoding.md deleted file mode 100644 index 5e448ee..0000000 --- a/Documentation/Reference/structs/Base32CrockfordEncoding.md +++ /dev/null @@ -1,26 +0,0 @@ -**STRUCT** - -# `Base32CrockfordEncoding` - -```swift -public struct Base32CrockfordEncoding: Base32CrockfordEncodingProtocol, Base32CrockfordComparer -``` - -## Methods -### `encode(data:)` - -```swift -public func encode(data: Data) -> String -``` - -### `decode(base32Encoded:)` - -```swift -public func decode(base32Encoded string: String) throws -> Data -``` - -### `data(_:hasEncodedPrefix:)` - -```swift -public func data(_ data: Data, hasEncodedPrefix prefix: String) -> Bool -``` diff --git a/Documentation/Reference/structs/Binary.md b/Documentation/Reference/structs/Binary.md deleted file mode 100644 index 766d03e..0000000 --- a/Documentation/Reference/structs/Binary.md +++ /dev/null @@ -1,69 +0,0 @@ -**STRUCT** - -# `Binary` - -```swift -public struct Binary -``` - -## Properties -### `bytes` - -```swift -public let bytes: [UInt8] -``` - -### `readingOffset` - -```swift -public var readingOffset: Int = 0 -``` - -### `byteSize` - -```swift -public let byteSize: Int -``` - -## Methods -### `init(data:byteSize:)` - -```swift -public init(data: Data, byteSize: Int = 8) -``` - -### `bit(_:)` - -```swift -public func bit(_ position: Int) -> Int -``` - -### `bits(_:)` - -```swift -public func bits(_ range: Range) -> Int -``` - -### `bits(_:_:)` - -```swift -public func bits(_ start: Int, _ length: Int) -> Int -``` - -### `byte(_:)` - -```swift -public func byte(_ position: Int) -> Int -``` - -### `bitsWithInternalOffsetAvailable(_:)` - -```swift -public func bitsWithInternalOffsetAvailable(_ length: Int) -> Bool -``` - -### `next(bits:)` - -```swift -public mutating func next(bits length: Int) -> Int? -``` diff --git a/Documentation/Reference/typealiases/ByteCollection.md b/Documentation/Reference/typealiases/ByteCollection.md deleted file mode 100644 index 5c7151f..0000000 --- a/Documentation/Reference/typealiases/ByteCollection.md +++ /dev/null @@ -1,7 +0,0 @@ -**TYPEALIAS** - -# `ByteCollection` - -```swift -public typealias ByteCollection = [UInt8] -``` diff --git a/Example/B32CFiOS/AppDelegate.swift b/Example/B32CFiOS/AppDelegate.swift deleted file mode 100644 index e152778..0000000 --- a/Example/B32CFiOS/AppDelegate.swift +++ /dev/null @@ -1,20 +0,0 @@ -import UIKit - -@UIApplicationMain -class AppDelegate: UIResponder, UIApplicationDelegate { - var window: UIWindow? - - func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - // Override point for customization after application launch. - - let viewController = IdentifierDataTypeTableViewController() - let navigationViewController = UINavigationController(rootViewController: viewController) - let window = UIWindow(frame: UIScreen.main.bounds) - window.rootViewController = navigationViewController - window.makeKeyAndVisible() - self.window = window - return true - } - - // MARK: UISceneSession Lifecycle -} diff --git a/Example/B32CFiOS/Assets.xcassets/AppIcon.appiconset/Contents.json b/Example/B32CFiOS/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index d8db8d6..0000000 --- a/Example/B32CFiOS/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,98 +0,0 @@ -{ - "images" : [ - { - "idiom" : "iphone", - "size" : "20x20", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "20x20", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "60x60", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "60x60", - "scale" : "3x" - }, - { - "idiom" : "ipad", - "size" : "20x20", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "20x20", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "29x29", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "29x29", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "40x40", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "40x40", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "76x76", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "76x76", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "83.5x83.5", - "scale" : "2x" - }, - { - "idiom" : "ios-marketing", - "size" : "1024x1024", - "scale" : "1x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Example/B32CFiOS/Assets.xcassets/Contents.json b/Example/B32CFiOS/Assets.xcassets/Contents.json deleted file mode 100644 index da4a164..0000000 --- a/Example/B32CFiOS/Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Example/B32CFiOS/Base.lproj/LaunchScreen.storyboard b/Example/B32CFiOS/Base.lproj/LaunchScreen.storyboard deleted file mode 100644 index 7212a6e..0000000 --- a/Example/B32CFiOS/Base.lproj/LaunchScreen.storyboard +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/Example/B32CFiOS/IdentifierDataTypeTableViewController.swift b/Example/B32CFiOS/IdentifierDataTypeTableViewController.swift deleted file mode 100644 index 5af4b07..0000000 --- a/Example/B32CFiOS/IdentifierDataTypeTableViewController.swift +++ /dev/null @@ -1,154 +0,0 @@ -import Base32Crockford -import UIKit - -enum IdentifierDataTypeName: CustomStringConvertible { - case `default` - case uuid - case bytes - case minimumCount - - var description: String { - switch self { - case .bytes: - return "Bytes" - case .uuid: - return "UUID" - case .minimumCount: - return "Minimum Count" - default: - return "Default" - } - } -} - -enum IdentifierDataIntName { - case bytes - case minimumCount -} - -struct IdentifierDataTypeParameter: CaseIterable, CustomStringConvertible { - static let allCases = [ - IdentifierDataTypeParameter(type: .bytes, integerName: .bytes), - IdentifierDataTypeParameter(type: .default, integerName: nil), - IdentifierDataTypeParameter(type: .uuid, integerName: nil), - IdentifierDataTypeParameter(type: .minimumCount, integerName: .minimumCount) - ] - - let type: IdentifierDataTypeName - let integerName: IdentifierDataIntName? - - var description: String { - return type.description - } -} - -class IdentifierDataTypeTableViewController: UITableViewController { - let encoding = Base32CrockfordEncoding.encoding - override func viewDidLoad() { - super.viewDidLoad() - - // Uncomment the following line to preserve selection between presentations - // self.clearsSelectionOnViewWillAppear = false - - // Uncomment the following line to display an Edit button in the navigation bar for this view controller. - // self.navigationItem.rightBarButtonItem = self.editButtonItem - - tableView.register(UITableViewCell.self, forCellReuseIdentifier: "reuseIdentifier") - } - - // MARK: - Table view data source - - override func tableView(_: UITableView, numberOfRowsInSection _: Int) -> Int { - // #warning Incomplete implementation, return the number of rows - return IdentifierDataTypeParameter.allCases.count - } - - override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath) - - // Configure the cell... - cell.textLabel?.text = IdentifierDataTypeParameter.allCases[indexPath.row].description - - return cell - } - - override func tableView(_: UITableView, didSelectRowAt indexPath: IndexPath) { - let parameter = IdentifierDataTypeParameter.allCases[indexPath.row] - if let navViewController = navigationController, let integerName = parameter.integerName { - let viewController = IntegerCountViewController() - viewController.integerName = integerName - viewController.generate = generate - navViewController.pushViewController(viewController, animated: true) - } else { - let idType: IdentifierDataType? - switch parameter.type { - case .default: - idType = .default - case .uuid: - idType = .uuid - default: - idType = nil - } - - guard let actualType = idType else { - return - } - let value = generate(basedOn: actualType) - let controller = UIAlertController(title: actualType.description, message: value, preferredStyle: .alert) - controller.addAction(UIAlertAction(title: "OK", style: .default, - handler: { _ in - controller.dismiss(animated: true) - })) - present(controller, animated: true) - } - } - - func generate(basedOn type: IdentifierDataType) -> String { - return encoding.generateIdentifier(from: type) - } - - /* - // Override to support conditional editing of the table view. - override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { - // Return false if you do not want the specified item to be editable. - return true - } - */ - - /* - // Override to support editing the table view. - override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { - if editingStyle == .delete { - // Delete the row from the data source - tableView.deleteRows(at: [indexPath], with: .fade) - } else if editingStyle == .insert { - // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view - } - } - */ - - /* - // Override to support rearranging the table view. - override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) { - - } - */ - - /* - // Override to support conditional rearranging of the table view. - override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool { - // Return false if you do not want the item to be re-orderable. - return true - } - */ - - /* - // MARK: - Navigation - - // In a storyboard-based application, you will often want to do a little preparation before navigation - override func prepare(for segue: UIStoryboardSegue, sender: Any?) { - // Get the new view controller using segue.destination. - // Pass the selected object to the new view controller. - } - */ -} diff --git a/Example/B32CFiOS/Info.plist b/Example/B32CFiOS/Info.plist deleted file mode 100644 index 5a63475..0000000 --- a/Example/B32CFiOS/Info.plist +++ /dev/null @@ -1,43 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - - diff --git a/Example/B32CFiOS/IntegerCountViewController.swift b/Example/B32CFiOS/IntegerCountViewController.swift deleted file mode 100644 index d515b24..0000000 --- a/Example/B32CFiOS/IntegerCountViewController.swift +++ /dev/null @@ -1,72 +0,0 @@ -import Base32Crockford -import UIKit - -extension IdentifierDataType: CustomStringConvertible { - public var description: String { - switch self { - case .default: - return "Single Default" - case .uuid: - return "UUID" - case let .bytes(size): - return "\(size) bytes" - case let .minimumCount(count): - return "Minimum Count of \(count)" - } - } -} - -class IntegerCountViewController: UIViewController { - @IBOutlet var label: UILabel! - @IBOutlet var textField: UITextField! - @IBOutlet var slider: UISlider! - - var integerName: IdentifierDataIntName! - var generate: ((IdentifierDataType) -> String)? - - override func viewDidLoad() { - super.viewDidLoad() - // Do any additional setup after loading the view. - - // encoding.generate(count, from: .default) - // encoding.generate(1, from: .) - let unit: String - switch integerName { - case .bytes: - unit = "Bytes" - case .minimumCount: - unit = "Possiblities" - default: - unit = "" - } - label.text = unit - } - - @IBAction func generate(fromButton _: UIButton, withEvent _: UIControl.Event) { - let type: IdentifierDataType? - switch integerName { - case .bytes: - type = .bytes(size: Int(slider.value)) - case .minimumCount: - type = .minimumCount(Int(slider.value)) - default: - type = nil - } - guard let actualType = type else { - return - } - guard let value = generate?(actualType) else { - return - } - let controller = UIAlertController(title: actualType.description, message: value, preferredStyle: .alert) - controller.addAction(UIAlertAction(title: "OK", style: .default, - handler: { _ in - controller.dismiss(animated: true) - })) - present(controller, animated: true) - } - - @IBAction func valueChanged(fromSlide slider: UISlider, withEvent _: UIControl.Event) { - textField.text = "\(Int(slider.value))" - } -} diff --git a/Example/B32CFiOS/IntegerCountViewController.xib b/Example/B32CFiOS/IntegerCountViewController.xib deleted file mode 100644 index 0d08d71..0000000 --- a/Example/B32CFiOS/IntegerCountViewController.xib +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Example/B32CFmacOS/main.swift b/Example/B32CFmacOS/main.swift deleted file mode 100644 index ac039ec..0000000 --- a/Example/B32CFmacOS/main.swift +++ /dev/null @@ -1,4 +0,0 @@ -import Base32Crockford -import Foundation - -print("Hello, World!") diff --git a/Example/B32CFtvOS/AppDelegate.swift b/Example/B32CFtvOS/AppDelegate.swift deleted file mode 100644 index cc3abf6..0000000 --- a/Example/B32CFtvOS/AppDelegate.swift +++ /dev/null @@ -1,11 +0,0 @@ -import UIKit - -@UIApplicationMain -class AppDelegate: UIResponder, UIApplicationDelegate { - var window: UIWindow? - - func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - // Override point for customization after application launch. - return true - } -} diff --git a/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json b/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json deleted file mode 100644 index 48ecb4f..0000000 --- a/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "images" : [ - { - "idiom" : "tv" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json b/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json deleted file mode 100644 index da4a164..0000000 --- a/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json b/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json deleted file mode 100644 index d29f024..0000000 --- a/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "layers" : [ - { - "filename" : "Front.imagestacklayer" - }, - { - "filename" : "Middle.imagestacklayer" - }, - { - "filename" : "Back.imagestacklayer" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json b/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json deleted file mode 100644 index 48ecb4f..0000000 --- a/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "images" : [ - { - "idiom" : "tv" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json b/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json deleted file mode 100644 index da4a164..0000000 --- a/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json b/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json deleted file mode 100644 index 48ecb4f..0000000 --- a/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "images" : [ - { - "idiom" : "tv" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json b/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json deleted file mode 100644 index da4a164..0000000 --- a/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json b/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json deleted file mode 100644 index 16a370d..0000000 --- a/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "images" : [ - { - "idiom" : "tv", - "scale" : "1x" - }, - { - "idiom" : "tv", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json b/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json deleted file mode 100644 index da4a164..0000000 --- a/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json b/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json deleted file mode 100644 index d29f024..0000000 --- a/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "layers" : [ - { - "filename" : "Front.imagestacklayer" - }, - { - "filename" : "Middle.imagestacklayer" - }, - { - "filename" : "Back.imagestacklayer" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json b/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json deleted file mode 100644 index 16a370d..0000000 --- a/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "images" : [ - { - "idiom" : "tv", - "scale" : "1x" - }, - { - "idiom" : "tv", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json b/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json deleted file mode 100644 index da4a164..0000000 --- a/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json b/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json deleted file mode 100644 index 16a370d..0000000 --- a/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "images" : [ - { - "idiom" : "tv", - "scale" : "1x" - }, - { - "idiom" : "tv", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json b/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json deleted file mode 100644 index da4a164..0000000 --- a/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json b/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json deleted file mode 100644 index db288f3..0000000 --- a/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "assets" : [ - { - "size" : "1280x768", - "idiom" : "tv", - "filename" : "App Icon - App Store.imagestack", - "role" : "primary-app-icon" - }, - { - "size" : "400x240", - "idiom" : "tv", - "filename" : "App Icon.imagestack", - "role" : "primary-app-icon" - }, - { - "size" : "2320x720", - "idiom" : "tv", - "filename" : "Top Shelf Image Wide.imageset", - "role" : "top-shelf-image-wide" - }, - { - "size" : "1920x720", - "idiom" : "tv", - "filename" : "Top Shelf Image.imageset", - "role" : "top-shelf-image" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json b/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json deleted file mode 100644 index 7dc9502..0000000 --- a/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "images" : [ - { - "idiom" : "tv", - "scale" : "1x" - }, - { - "idiom" : "tv", - "scale" : "2x" - }, - { - "idiom" : "tv-marketing", - "scale" : "1x" - }, - { - "idiom" : "tv-marketing", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json b/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json deleted file mode 100644 index 7dc9502..0000000 --- a/Example/B32CFtvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "images" : [ - { - "idiom" : "tv", - "scale" : "1x" - }, - { - "idiom" : "tv", - "scale" : "2x" - }, - { - "idiom" : "tv-marketing", - "scale" : "1x" - }, - { - "idiom" : "tv-marketing", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Example/B32CFtvOS/Assets.xcassets/Contents.json b/Example/B32CFtvOS/Assets.xcassets/Contents.json deleted file mode 100644 index da4a164..0000000 --- a/Example/B32CFtvOS/Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Example/B32CFtvOS/Base.lproj/LaunchScreen.storyboard b/Example/B32CFtvOS/Base.lproj/LaunchScreen.storyboard deleted file mode 100644 index 660ba53..0000000 --- a/Example/B32CFtvOS/Base.lproj/LaunchScreen.storyboard +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Example/B32CFtvOS/Base.lproj/Main.storyboard b/Example/B32CFtvOS/Base.lproj/Main.storyboard deleted file mode 100644 index 2543320..0000000 --- a/Example/B32CFtvOS/Base.lproj/Main.storyboard +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Example/B32CFtvOS/Info.plist b/Example/B32CFtvOS/Info.plist deleted file mode 100644 index 25869ef..0000000 --- a/Example/B32CFtvOS/Info.plist +++ /dev/null @@ -1,34 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UIRequiredDeviceCapabilities - - arm64 - - UIUserInterfaceStyle - Automatic - - diff --git a/Example/B32CFtvOS/ViewController.swift b/Example/B32CFtvOS/ViewController.swift deleted file mode 100644 index 80b12e1..0000000 --- a/Example/B32CFtvOS/ViewController.swift +++ /dev/null @@ -1,9 +0,0 @@ -import Base32Crockford -import UIKit - -class ViewController: UIViewController { - override func viewDidLoad() { - super.viewDidLoad() - // Do any additional setup after loading the view. - } -} diff --git a/Example/B32CFtvOSTests/B32CFtvOSTests.swift b/Example/B32CFtvOSTests/B32CFtvOSTests.swift deleted file mode 100644 index 58a9e4b..0000000 --- a/Example/B32CFtvOSTests/B32CFtvOSTests.swift +++ /dev/null @@ -1,24 +0,0 @@ -@testable import B32CFtvOS -import XCTest - -class B32CFtvOSTests: XCTestCase { - override func setUp() { - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - func testExample() { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. - } - - func testPerformanceExample() { - // This is an example of a performance test case. - measure { - // Put the code you want to measure the time of here. - } - } -} diff --git a/Example/B32CFtvOSTests/Info.plist b/Example/B32CFtvOSTests/Info.plist deleted file mode 100644 index 64d65ca..0000000 --- a/Example/B32CFtvOSTests/Info.plist +++ /dev/null @@ -1,22 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - - diff --git a/Example/B32CFwatchOS Extension/Assets.xcassets/Complication.complicationset/Circular.imageset/Contents.json b/Example/B32CFwatchOS Extension/Assets.xcassets/Complication.complicationset/Circular.imageset/Contents.json deleted file mode 100644 index aefef29..0000000 --- a/Example/B32CFwatchOS Extension/Assets.xcassets/Complication.complicationset/Circular.imageset/Contents.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "images" : [ - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : "<=145" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">161" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">145" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">183" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Example/B32CFwatchOS Extension/Assets.xcassets/Complication.complicationset/Contents.json b/Example/B32CFwatchOS Extension/Assets.xcassets/Complication.complicationset/Contents.json deleted file mode 100644 index 1571c7e..0000000 --- a/Example/B32CFwatchOS Extension/Assets.xcassets/Complication.complicationset/Contents.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "assets" : [ - { - "idiom" : "watch", - "filename" : "Circular.imageset", - "role" : "circular" - }, - { - "idiom" : "watch", - "filename" : "Extra Large.imageset", - "role" : "extra-large" - }, - { - "idiom" : "watch", - "filename" : "Graphic Bezel.imageset", - "role" : "graphic-bezel" - }, - { - "idiom" : "watch", - "filename" : "Graphic Circular.imageset", - "role" : "graphic-circular" - }, - { - "idiom" : "watch", - "filename" : "Graphic Corner.imageset", - "role" : "graphic-corner" - }, - { - "idiom" : "watch", - "filename" : "Graphic Large Rectangular.imageset", - "role" : "graphic-large-rectangular" - }, - { - "idiom" : "watch", - "filename" : "Modular.imageset", - "role" : "modular" - }, - { - "idiom" : "watch", - "filename" : "Utilitarian.imageset", - "role" : "utilitarian" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Example/B32CFwatchOS Extension/Assets.xcassets/Complication.complicationset/Extra Large.imageset/Contents.json b/Example/B32CFwatchOS Extension/Assets.xcassets/Complication.complicationset/Extra Large.imageset/Contents.json deleted file mode 100644 index aefef29..0000000 --- a/Example/B32CFwatchOS Extension/Assets.xcassets/Complication.complicationset/Extra Large.imageset/Contents.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "images" : [ - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : "<=145" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">161" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">145" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">183" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Example/B32CFwatchOS Extension/Assets.xcassets/Complication.complicationset/Graphic Bezel.imageset/Contents.json b/Example/B32CFwatchOS Extension/Assets.xcassets/Complication.complicationset/Graphic Bezel.imageset/Contents.json deleted file mode 100644 index aefef29..0000000 --- a/Example/B32CFwatchOS Extension/Assets.xcassets/Complication.complicationset/Graphic Bezel.imageset/Contents.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "images" : [ - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : "<=145" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">161" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">145" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">183" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Example/B32CFwatchOS Extension/Assets.xcassets/Complication.complicationset/Graphic Circular.imageset/Contents.json b/Example/B32CFwatchOS Extension/Assets.xcassets/Complication.complicationset/Graphic Circular.imageset/Contents.json deleted file mode 100644 index aefef29..0000000 --- a/Example/B32CFwatchOS Extension/Assets.xcassets/Complication.complicationset/Graphic Circular.imageset/Contents.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "images" : [ - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : "<=145" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">161" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">145" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">183" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Example/B32CFwatchOS Extension/Assets.xcassets/Complication.complicationset/Graphic Corner.imageset/Contents.json b/Example/B32CFwatchOS Extension/Assets.xcassets/Complication.complicationset/Graphic Corner.imageset/Contents.json deleted file mode 100644 index aefef29..0000000 --- a/Example/B32CFwatchOS Extension/Assets.xcassets/Complication.complicationset/Graphic Corner.imageset/Contents.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "images" : [ - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : "<=145" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">161" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">145" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">183" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Example/B32CFwatchOS Extension/Assets.xcassets/Complication.complicationset/Graphic Large Rectangular.imageset/Contents.json b/Example/B32CFwatchOS Extension/Assets.xcassets/Complication.complicationset/Graphic Large Rectangular.imageset/Contents.json deleted file mode 100644 index aefef29..0000000 --- a/Example/B32CFwatchOS Extension/Assets.xcassets/Complication.complicationset/Graphic Large Rectangular.imageset/Contents.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "images" : [ - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : "<=145" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">161" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">145" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">183" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Example/B32CFwatchOS Extension/Assets.xcassets/Complication.complicationset/Modular.imageset/Contents.json b/Example/B32CFwatchOS Extension/Assets.xcassets/Complication.complicationset/Modular.imageset/Contents.json deleted file mode 100644 index aefef29..0000000 --- a/Example/B32CFwatchOS Extension/Assets.xcassets/Complication.complicationset/Modular.imageset/Contents.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "images" : [ - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : "<=145" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">161" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">145" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">183" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Example/B32CFwatchOS Extension/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/Contents.json b/Example/B32CFwatchOS Extension/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/Contents.json deleted file mode 100644 index aefef29..0000000 --- a/Example/B32CFwatchOS Extension/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/Contents.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "images" : [ - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : "<=145" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">161" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">145" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">183" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Example/B32CFwatchOS Extension/Assets.xcassets/Contents.json b/Example/B32CFwatchOS Extension/Assets.xcassets/Contents.json deleted file mode 100644 index da4a164..0000000 --- a/Example/B32CFwatchOS Extension/Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Example/B32CFwatchOS Extension/ExtensionDelegate.swift b/Example/B32CFwatchOS Extension/ExtensionDelegate.swift deleted file mode 100644 index 0f219f4..0000000 --- a/Example/B32CFwatchOS Extension/ExtensionDelegate.swift +++ /dev/null @@ -1,3 +0,0 @@ -import WatchKit - -class ExtensionDelegate: NSObject, WKExtensionDelegate {} diff --git a/Example/B32CFwatchOS Extension/Info.plist b/Example/B32CFwatchOS Extension/Info.plist deleted file mode 100644 index 47756b8..0000000 --- a/Example/B32CFwatchOS Extension/Info.plist +++ /dev/null @@ -1,38 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleDisplayName - B32CFwatchOS Extension - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - NSExtension - - NSExtensionAttributes - - WKAppBundleIdentifier - com.brightdigit.B32CFiOS.watchkitapp - - NSExtensionPointIdentifier - com.apple.watchkit - - WKExtensionDelegateClassName - $(PRODUCT_MODULE_NAME).ExtensionDelegate - WKRunsIndependentlyOfCompanionApp - - - diff --git a/Example/B32CFwatchOS Extension/InterfaceController.swift b/Example/B32CFwatchOS Extension/InterfaceController.swift deleted file mode 100644 index 3b059cb..0000000 --- a/Example/B32CFwatchOS Extension/InterfaceController.swift +++ /dev/null @@ -1,21 +0,0 @@ -import Base32Crockford -import Foundation -import WatchKit - -class InterfaceController: WKInterfaceController { - override func awake(withContext context: Any?) { - super.awake(withContext: context) - - // Configure interface objects here. - } - - override func willActivate() { - // This method is called when watch view controller is about to be visible to user - super.willActivate() - } - - override func didDeactivate() { - // This method is called when watch view controller is no longer visible - super.didDeactivate() - } -} diff --git a/Example/B32CFwatchOS/Assets.xcassets/AppIcon.appiconset/Contents.json b/Example/B32CFwatchOS/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 6c0f2b4..0000000 --- a/Example/B32CFwatchOS/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "images" : [ - { - "size" : "24x24", - "idiom" : "watch", - "scale" : "2x", - "role" : "notificationCenter", - "subtype" : "38mm" - }, - { - "size" : "27.5x27.5", - "idiom" : "watch", - "scale" : "2x", - "role" : "notificationCenter", - "subtype" : "42mm" - }, - { - "size" : "29x29", - "idiom" : "watch", - "role" : "companionSettings", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "watch", - "role" : "companionSettings", - "scale" : "3x" - }, - { - "size" : "40x40", - "idiom" : "watch", - "scale" : "2x", - "role" : "appLauncher", - "subtype" : "38mm" - }, - { - "size" : "44x44", - "idiom" : "watch", - "scale" : "2x", - "role" : "appLauncher", - "subtype" : "40mm" - }, - { - "size" : "50x50", - "idiom" : "watch", - "scale" : "2x", - "role" : "appLauncher", - "subtype" : "44mm" - }, - { - "size" : "86x86", - "idiom" : "watch", - "scale" : "2x", - "role" : "quickLook", - "subtype" : "38mm" - }, - { - "size" : "98x98", - "idiom" : "watch", - "scale" : "2x", - "role" : "quickLook", - "subtype" : "42mm" - }, - { - "size" : "108x108", - "idiom" : "watch", - "scale" : "2x", - "role" : "quickLook", - "subtype" : "44mm" - }, - { - "idiom" : "watch-marketing", - "size" : "1024x1024", - "scale" : "1x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Example/B32CFwatchOS/Assets.xcassets/Contents.json b/Example/B32CFwatchOS/Assets.xcassets/Contents.json deleted file mode 100644 index da4a164..0000000 --- a/Example/B32CFwatchOS/Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Example/B32CFwatchOS/Base.lproj/Interface.storyboard b/Example/B32CFwatchOS/Base.lproj/Interface.storyboard deleted file mode 100644 index cf05d24..0000000 --- a/Example/B32CFwatchOS/Base.lproj/Interface.storyboard +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/Example/B32CFwatchOS/Info.plist b/Example/B32CFwatchOS/Info.plist deleted file mode 100644 index bf41869..0000000 --- a/Example/B32CFwatchOS/Info.plist +++ /dev/null @@ -1,33 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleDisplayName - B32CFiOS - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - - WKCompanionAppBundleIdentifier - com.brightdigit.B32CFiOS - WKWatchKitApp - - - diff --git a/Example/Example.xcodeproj/project.pbxproj b/Example/Example.xcodeproj/project.pbxproj deleted file mode 100644 index 29f67ec..0000000 --- a/Example/Example.xcodeproj/project.pbxproj +++ /dev/null @@ -1,1548 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 50; - objects = { - -/* Begin PBXBuildFile section */ - 11C7FBA8ED914BCC2BBBC66D /* Pods_iOS_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 06E1B5DD0B1CEB281FBADD90 /* Pods_iOS_Example.framework */; }; - 2449FB4D9DBA80B890E30D66 /* Pods_watchOS_Example_Extension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C843253667F408A56AD3CF7A /* Pods_watchOS_Example_Extension.framework */; }; - 3DE821C3D87AC81891B5F462 /* Pods_macOS_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CB1BCD344071C8A70360C8F9 /* Pods_macOS_Example.framework */; }; - 623FCB2680CA5231B53AD302 /* Pods_tvOS_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B1CFF8913CD9F323D764F530 /* Pods_tvOS_Example.framework */; }; - B3471F1823AC2866002232CB /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3471F1723AC2866002232CB /* AppDelegate.swift */; }; - B3471F1C23AC2866002232CB /* IntegerCountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3471F1B23AC2866002232CB /* IntegerCountViewController.swift */; }; - B3471F2123AC2867002232CB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B3471F2023AC2867002232CB /* Assets.xcassets */; }; - B3471F2423AC2867002232CB /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B3471F2223AC2867002232CB /* LaunchScreen.storyboard */; }; - B3471F4C23AC287A002232CB /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3471F4B23AC287A002232CB /* AppDelegate.swift */; }; - B3471F4E23AC287A002232CB /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3471F4D23AC287A002232CB /* ViewController.swift */; }; - B3471F5123AC287A002232CB /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B3471F4F23AC287A002232CB /* Main.storyboard */; }; - B3471F5323AC287B002232CB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B3471F5223AC287B002232CB /* Assets.xcassets */; }; - B3471F5623AC287B002232CB /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B3471F5423AC287B002232CB /* LaunchScreen.storyboard */; }; - B3471F7E23AC288B002232CB /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3471F7D23AC288B002232CB /* main.swift */; }; - B3471F8823AC2985002232CB /* Interface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B3471F8623AC2985002232CB /* Interface.storyboard */; }; - B3471F8A23AC2986002232CB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B3471F8923AC2986002232CB /* Assets.xcassets */; }; - B3471F9123AC2986002232CB /* watchOS Example Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = B3471F9023AC2986002232CB /* watchOS Example Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; - B3471F9623AC2986002232CB /* InterfaceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3471F9523AC2986002232CB /* InterfaceController.swift */; }; - B3471F9823AC2986002232CB /* ExtensionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3471F9723AC2986002232CB /* ExtensionDelegate.swift */; }; - B3471F9A23AC2986002232CB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B3471F9923AC2986002232CB /* Assets.xcassets */; }; - B3471F9E23AC2986002232CB /* watchOS Example.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = B3471F8423AC2985002232CB /* watchOS Example.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; - B37C882923AC478D0059163C /* IntegerCountViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = B37C882823AC478D0059163C /* IntegerCountViewController.xib */; }; - B37C882B23AC572F0059163C /* IdentifierDataTypeTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37C882A23AC572F0059163C /* IdentifierDataTypeTableViewController.swift */; }; - B632A3DE4433959D780B164A /* Pods_watchOS_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AD607E658B5CA1063D654BB0 /* Pods_watchOS_Example.framework */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - B3471F9223AC2986002232CB /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = B3471F0A23AC2823002232CB /* Project object */; - proxyType = 1; - remoteGlobalIDString = B3471F8F23AC2986002232CB; - remoteInfo = "B32CFwatchOS Extension"; - }; - B3471F9C23AC2986002232CB /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = B3471F0A23AC2823002232CB /* Project object */; - proxyType = 1; - remoteGlobalIDString = B3471F8323AC2985002232CB; - remoteInfo = B32CFwatchOS; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - B3471F7923AC288B002232CB /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = /usr/share/man/man1/; - dstSubfolderSpec = 0; - files = ( - ); - runOnlyForDeploymentPostprocessing = 1; - }; - B3471FA223AC2986002232CB /* Embed App Extensions */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 13; - files = ( - B3471F9123AC2986002232CB /* watchOS Example Extension.appex in Embed App Extensions */, - ); - name = "Embed App Extensions"; - runOnlyForDeploymentPostprocessing = 0; - }; - B3471FA623AC2986002232CB /* Embed Watch Content */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = "$(CONTENTS_FOLDER_PATH)/Watch"; - dstSubfolderSpec = 16; - files = ( - B3471F9E23AC2986002232CB /* watchOS Example.app in Embed Watch Content */, - ); - name = "Embed Watch Content"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 06E1B5DD0B1CEB281FBADD90 /* Pods_iOS_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_iOS_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 06E1D7EF9CE18DBD9A38B898 /* Pods-watchOS Example Extension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-watchOS Example Extension.release.xcconfig"; path = "Target Support Files/Pods-watchOS Example Extension/Pods-watchOS Example Extension.release.xcconfig"; sourceTree = ""; }; - 2ACD4CCB85CFD8DED56E3F12 /* Pods-iOS Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-iOS Example.debug.xcconfig"; path = "Target Support Files/Pods-iOS Example/Pods-iOS Example.debug.xcconfig"; sourceTree = ""; }; - 392B99EB2DE17FFF2B0C2AA6 /* Pods-macOS Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-macOS Example.debug.xcconfig"; path = "Target Support Files/Pods-macOS Example/Pods-macOS Example.debug.xcconfig"; sourceTree = ""; }; - 3B2D1E843889D06567A800B6 /* Pods-tvOS Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-tvOS Example.debug.xcconfig"; path = "Target Support Files/Pods-tvOS Example/Pods-tvOS Example.debug.xcconfig"; sourceTree = ""; }; - 48EEEFA22F15885FFC9E7510 /* Pods-iOS Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-iOS Example.release.xcconfig"; path = "Target Support Files/Pods-iOS Example/Pods-iOS Example.release.xcconfig"; sourceTree = ""; }; - 5A01AA6826476C4E5FA36B57 /* Pods-tvOS Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-tvOS Example.release.xcconfig"; path = "Target Support Files/Pods-tvOS Example/Pods-tvOS Example.release.xcconfig"; sourceTree = ""; }; - 6C2418004F1B9205CE4E5633 /* Pods-watchOS Example Extension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-watchOS Example Extension.debug.xcconfig"; path = "Target Support Files/Pods-watchOS Example Extension/Pods-watchOS Example Extension.debug.xcconfig"; sourceTree = ""; }; - A9AC176BBAAB1CAB1C72D38C /* Pods-watchOS Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-watchOS Example.release.xcconfig"; path = "Target Support Files/Pods-watchOS Example/Pods-watchOS Example.release.xcconfig"; sourceTree = ""; }; - AD607E658B5CA1063D654BB0 /* Pods_watchOS_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_watchOS_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - B1CFF8913CD9F323D764F530 /* Pods_tvOS_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_tvOS_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - B3471F1423AC2866002232CB /* iOS Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "iOS Example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; - B3471F1723AC2866002232CB /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - B3471F1B23AC2866002232CB /* IntegerCountViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntegerCountViewController.swift; sourceTree = ""; }; - B3471F2023AC2867002232CB /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - B3471F2323AC2867002232CB /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - B3471F2523AC2867002232CB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - B3471F4923AC287A002232CB /* tvOS Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "tvOS Example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; - B3471F4B23AC287A002232CB /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - B3471F4D23AC287A002232CB /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; - B3471F5023AC287A002232CB /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - B3471F5223AC287B002232CB /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - B3471F5523AC287B002232CB /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - B3471F5723AC287B002232CB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - B3471F6023AC287B002232CB /* B32CFtvOSTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = B32CFtvOSTests.swift; sourceTree = ""; }; - B3471F6223AC287B002232CB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - B3471F7B23AC288B002232CB /* macOS Example */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "macOS Example"; sourceTree = BUILT_PRODUCTS_DIR; }; - B3471F7D23AC288B002232CB /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; - B3471F8423AC2985002232CB /* watchOS Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "watchOS Example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; - B3471F8723AC2985002232CB /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Interface.storyboard; sourceTree = ""; }; - B3471F8923AC2986002232CB /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - B3471F8B23AC2986002232CB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - B3471F9023AC2986002232CB /* watchOS Example Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "watchOS Example Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; - B3471F9523AC2986002232CB /* InterfaceController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterfaceController.swift; sourceTree = ""; }; - B3471F9723AC2986002232CB /* ExtensionDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionDelegate.swift; sourceTree = ""; }; - B3471F9923AC2986002232CB /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - B3471F9B23AC2986002232CB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - B37C882823AC478D0059163C /* IntegerCountViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = IntegerCountViewController.xib; sourceTree = ""; }; - B37C882A23AC572F0059163C /* IdentifierDataTypeTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdentifierDataTypeTableViewController.swift; sourceTree = ""; }; - C843253667F408A56AD3CF7A /* Pods_watchOS_Example_Extension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_watchOS_Example_Extension.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - CB1BCD344071C8A70360C8F9 /* Pods_macOS_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_macOS_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D95E033A8D448E7BA5F74478 /* Pods-macOS Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-macOS Example.release.xcconfig"; path = "Target Support Files/Pods-macOS Example/Pods-macOS Example.release.xcconfig"; sourceTree = ""; }; - E5CD669251B441382F1D3718 /* Pods-watchOS Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-watchOS Example.debug.xcconfig"; path = "Target Support Files/Pods-watchOS Example/Pods-watchOS Example.debug.xcconfig"; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - B3471F1123AC2866002232CB /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 11C7FBA8ED914BCC2BBBC66D /* Pods_iOS_Example.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - B3471F4623AC287A002232CB /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 623FCB2680CA5231B53AD302 /* Pods_tvOS_Example.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - B3471F7823AC288B002232CB /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 3DE821C3D87AC81891B5F462 /* Pods_macOS_Example.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - B3471F8D23AC2986002232CB /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 2449FB4D9DBA80B890E30D66 /* Pods_watchOS_Example_Extension.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - EF25EFD4AC926ED9086BA404 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - B632A3DE4433959D780B164A /* Pods_watchOS_Example.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 20E1CF27CE5D69A2CD7BD495 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 06E1B5DD0B1CEB281FBADD90 /* Pods_iOS_Example.framework */, - CB1BCD344071C8A70360C8F9 /* Pods_macOS_Example.framework */, - B1CFF8913CD9F323D764F530 /* Pods_tvOS_Example.framework */, - AD607E658B5CA1063D654BB0 /* Pods_watchOS_Example.framework */, - C843253667F408A56AD3CF7A /* Pods_watchOS_Example_Extension.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; - A47E08507911306D03B086BB /* Pods */ = { - isa = PBXGroup; - children = ( - 2ACD4CCB85CFD8DED56E3F12 /* Pods-iOS Example.debug.xcconfig */, - 48EEEFA22F15885FFC9E7510 /* Pods-iOS Example.release.xcconfig */, - 392B99EB2DE17FFF2B0C2AA6 /* Pods-macOS Example.debug.xcconfig */, - D95E033A8D448E7BA5F74478 /* Pods-macOS Example.release.xcconfig */, - 3B2D1E843889D06567A800B6 /* Pods-tvOS Example.debug.xcconfig */, - 5A01AA6826476C4E5FA36B57 /* Pods-tvOS Example.release.xcconfig */, - E5CD669251B441382F1D3718 /* Pods-watchOS Example.debug.xcconfig */, - A9AC176BBAAB1CAB1C72D38C /* Pods-watchOS Example.release.xcconfig */, - 6C2418004F1B9205CE4E5633 /* Pods-watchOS Example Extension.debug.xcconfig */, - 06E1D7EF9CE18DBD9A38B898 /* Pods-watchOS Example Extension.release.xcconfig */, - ); - path = Pods; - sourceTree = ""; - }; - B3471F0923AC2823002232CB = { - isa = PBXGroup; - children = ( - B3471F1623AC2866002232CB /* B32CFiOS */, - B3471F4A23AC287A002232CB /* B32CFtvOS */, - B3471F5F23AC287B002232CB /* B32CFtvOSTests */, - B3471F7C23AC288B002232CB /* B32CFmacOS */, - B3471F8523AC2985002232CB /* B32CFwatchOS */, - B3471F9423AC2986002232CB /* B32CFwatchOS Extension */, - B3471F1523AC2866002232CB /* Products */, - A47E08507911306D03B086BB /* Pods */, - 20E1CF27CE5D69A2CD7BD495 /* Frameworks */, - ); - sourceTree = ""; - }; - B3471F1523AC2866002232CB /* Products */ = { - isa = PBXGroup; - children = ( - B3471F1423AC2866002232CB /* iOS Example.app */, - B3471F4923AC287A002232CB /* tvOS Example.app */, - B3471F7B23AC288B002232CB /* macOS Example */, - B3471F8423AC2985002232CB /* watchOS Example.app */, - B3471F9023AC2986002232CB /* watchOS Example Extension.appex */, - ); - name = Products; - sourceTree = ""; - }; - B3471F1623AC2866002232CB /* B32CFiOS */ = { - isa = PBXGroup; - children = ( - B3471F1723AC2866002232CB /* AppDelegate.swift */, - B3471F1B23AC2866002232CB /* IntegerCountViewController.swift */, - B3471F2023AC2867002232CB /* Assets.xcassets */, - B3471F2223AC2867002232CB /* LaunchScreen.storyboard */, - B3471F2523AC2867002232CB /* Info.plist */, - B37C882823AC478D0059163C /* IntegerCountViewController.xib */, - B37C882A23AC572F0059163C /* IdentifierDataTypeTableViewController.swift */, - ); - path = B32CFiOS; - sourceTree = ""; - }; - B3471F4A23AC287A002232CB /* B32CFtvOS */ = { - isa = PBXGroup; - children = ( - B3471F4B23AC287A002232CB /* AppDelegate.swift */, - B3471F4D23AC287A002232CB /* ViewController.swift */, - B3471F4F23AC287A002232CB /* Main.storyboard */, - B3471F5223AC287B002232CB /* Assets.xcassets */, - B3471F5423AC287B002232CB /* LaunchScreen.storyboard */, - B3471F5723AC287B002232CB /* Info.plist */, - ); - path = B32CFtvOS; - sourceTree = ""; - }; - B3471F5F23AC287B002232CB /* B32CFtvOSTests */ = { - isa = PBXGroup; - children = ( - B3471F6023AC287B002232CB /* B32CFtvOSTests.swift */, - B3471F6223AC287B002232CB /* Info.plist */, - ); - path = B32CFtvOSTests; - sourceTree = ""; - }; - B3471F7C23AC288B002232CB /* B32CFmacOS */ = { - isa = PBXGroup; - children = ( - B3471F7D23AC288B002232CB /* main.swift */, - ); - path = B32CFmacOS; - sourceTree = ""; - }; - B3471F8523AC2985002232CB /* B32CFwatchOS */ = { - isa = PBXGroup; - children = ( - B3471F8623AC2985002232CB /* Interface.storyboard */, - B3471F8923AC2986002232CB /* Assets.xcassets */, - B3471F8B23AC2986002232CB /* Info.plist */, - ); - path = B32CFwatchOS; - sourceTree = ""; - }; - B3471F9423AC2986002232CB /* B32CFwatchOS Extension */ = { - isa = PBXGroup; - children = ( - B3471F9523AC2986002232CB /* InterfaceController.swift */, - B3471F9723AC2986002232CB /* ExtensionDelegate.swift */, - B3471F9923AC2986002232CB /* Assets.xcassets */, - B3471F9B23AC2986002232CB /* Info.plist */, - ); - path = "B32CFwatchOS Extension"; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - B3471F1323AC2866002232CB /* iOS Example */ = { - isa = PBXNativeTarget; - buildConfigurationList = B3471F3C23AC2867002232CB /* Build configuration list for PBXNativeTarget "iOS Example" */; - buildPhases = ( - A74E59D23CC82C411334160F /* [CP] Check Pods Manifest.lock */, - B3471F1023AC2866002232CB /* Sources */, - B3471F1123AC2866002232CB /* Frameworks */, - B3471F1223AC2866002232CB /* Resources */, - B3471FA623AC2986002232CB /* Embed Watch Content */, - 7273CA541EC2CB76050FFACC /* [CP] Embed Pods Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - B3471F9D23AC2986002232CB /* PBXTargetDependency */, - ); - name = "iOS Example"; - productName = B32CFiOS; - productReference = B3471F1423AC2866002232CB /* iOS Example.app */; - productType = "com.apple.product-type.application"; - }; - B3471F4823AC287A002232CB /* tvOS Example */ = { - isa = PBXNativeTarget; - buildConfigurationList = B3471F6E23AC287B002232CB /* Build configuration list for PBXNativeTarget "tvOS Example" */; - buildPhases = ( - 28C6F566328671A9DED45173 /* [CP] Check Pods Manifest.lock */, - B3471F4523AC287A002232CB /* Sources */, - B3471F4623AC287A002232CB /* Frameworks */, - B3471F4723AC287A002232CB /* Resources */, - D2F66908732B6989BFD1B48C /* [CP] Embed Pods Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "tvOS Example"; - productName = B32CFtvOS; - productReference = B3471F4923AC287A002232CB /* tvOS Example.app */; - productType = "com.apple.product-type.application"; - }; - B3471F7A23AC288B002232CB /* macOS Example */ = { - isa = PBXNativeTarget; - buildConfigurationList = B3471F7F23AC288B002232CB /* Build configuration list for PBXNativeTarget "macOS Example" */; - buildPhases = ( - 6764F17B9443C37E1DB3572A /* [CP] Check Pods Manifest.lock */, - B3471F7723AC288B002232CB /* Sources */, - B3471F7823AC288B002232CB /* Frameworks */, - B3471F7923AC288B002232CB /* CopyFiles */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "macOS Example"; - productName = B32CFmacOS; - productReference = B3471F7B23AC288B002232CB /* macOS Example */; - productType = "com.apple.product-type.tool"; - }; - B3471F8323AC2985002232CB /* watchOS Example */ = { - isa = PBXNativeTarget; - buildConfigurationList = B3471FA323AC2986002232CB /* Build configuration list for PBXNativeTarget "watchOS Example" */; - buildPhases = ( - 49940505E5B670AE2D3BE856 /* [CP] Check Pods Manifest.lock */, - B3471F8223AC2985002232CB /* Resources */, - B3471FA223AC2986002232CB /* Embed App Extensions */, - EF25EFD4AC926ED9086BA404 /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - B3471F9323AC2986002232CB /* PBXTargetDependency */, - ); - name = "watchOS Example"; - productName = B32CFwatchOS; - productReference = B3471F8423AC2985002232CB /* watchOS Example.app */; - productType = "com.apple.product-type.application.watchapp2"; - }; - B3471F8F23AC2986002232CB /* watchOS Example Extension */ = { - isa = PBXNativeTarget; - buildConfigurationList = B3471F9F23AC2986002232CB /* Build configuration list for PBXNativeTarget "watchOS Example Extension" */; - buildPhases = ( - C35B6D218A66F4875C431F11 /* [CP] Check Pods Manifest.lock */, - B3471F8C23AC2986002232CB /* Sources */, - B3471F8D23AC2986002232CB /* Frameworks */, - B3471F8E23AC2986002232CB /* Resources */, - 046DD3F46CC55E8D8EFCEE9B /* [CP] Embed Pods Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "watchOS Example Extension"; - productName = "B32CFwatchOS Extension"; - productReference = B3471F9023AC2986002232CB /* watchOS Example Extension.appex */; - productType = "com.apple.product-type.watchkit2-extension"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - B3471F0A23AC2823002232CB /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 1130; - LastUpgradeCheck = 1130; - TargetAttributes = { - B3471F1323AC2866002232CB = { - CreatedOnToolsVersion = 11.3; - }; - B3471F4823AC287A002232CB = { - CreatedOnToolsVersion = 11.3; - }; - B3471F7A23AC288B002232CB = { - CreatedOnToolsVersion = 11.3; - }; - B3471F8323AC2985002232CB = { - CreatedOnToolsVersion = 11.3; - }; - B3471F8F23AC2986002232CB = { - CreatedOnToolsVersion = 11.3; - }; - }; - }; - buildConfigurationList = B3471F0D23AC2823002232CB /* Build configuration list for PBXProject "Example" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = B3471F0923AC2823002232CB; - productRefGroup = B3471F1523AC2866002232CB /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - B3471F1323AC2866002232CB /* iOS Example */, - B3471F4823AC287A002232CB /* tvOS Example */, - B3471F7A23AC288B002232CB /* macOS Example */, - B3471F8323AC2985002232CB /* watchOS Example */, - B3471F8F23AC2986002232CB /* watchOS Example Extension */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - B3471F1223AC2866002232CB /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - B3471F2423AC2867002232CB /* LaunchScreen.storyboard in Resources */, - B37C882923AC478D0059163C /* IntegerCountViewController.xib in Resources */, - B3471F2123AC2867002232CB /* Assets.xcassets in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - B3471F4723AC287A002232CB /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - B3471F5623AC287B002232CB /* LaunchScreen.storyboard in Resources */, - B3471F5323AC287B002232CB /* Assets.xcassets in Resources */, - B3471F5123AC287A002232CB /* Main.storyboard in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - B3471F8223AC2985002232CB /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - B3471F8A23AC2986002232CB /* Assets.xcassets in Resources */, - B3471F8823AC2985002232CB /* Interface.storyboard in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - B3471F8E23AC2986002232CB /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - B3471F9A23AC2986002232CB /* Assets.xcassets in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 046DD3F46CC55E8D8EFCEE9B /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-watchOS Example Extension/Pods-watchOS Example Extension-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-watchOS Example Extension/Pods-watchOS Example Extension-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-watchOS Example Extension/Pods-watchOS Example Extension-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - 28C6F566328671A9DED45173 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-tvOS Example-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - 49940505E5B670AE2D3BE856 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-watchOS Example-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - 6764F17B9443C37E1DB3572A /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-macOS Example-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - 7273CA541EC2CB76050FFACC /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-iOS Example/Pods-iOS Example-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-iOS Example/Pods-iOS Example-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-iOS Example/Pods-iOS Example-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - A74E59D23CC82C411334160F /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-iOS Example-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - C35B6D218A66F4875C431F11 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-watchOS Example Extension-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - D2F66908732B6989BFD1B48C /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-tvOS Example/Pods-tvOS Example-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-tvOS Example/Pods-tvOS Example-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-tvOS Example/Pods-tvOS Example-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - B3471F1023AC2866002232CB /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - B3471F1C23AC2866002232CB /* IntegerCountViewController.swift in Sources */, - B3471F1823AC2866002232CB /* AppDelegate.swift in Sources */, - B37C882B23AC572F0059163C /* IdentifierDataTypeTableViewController.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - B3471F4523AC287A002232CB /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - B3471F4E23AC287A002232CB /* ViewController.swift in Sources */, - B3471F4C23AC287A002232CB /* AppDelegate.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - B3471F7723AC288B002232CB /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - B3471F7E23AC288B002232CB /* main.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - B3471F8C23AC2986002232CB /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - B3471F9823AC2986002232CB /* ExtensionDelegate.swift in Sources */, - B3471F9623AC2986002232CB /* InterfaceController.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - B3471F9323AC2986002232CB /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = B3471F8F23AC2986002232CB /* watchOS Example Extension */; - targetProxy = B3471F9223AC2986002232CB /* PBXContainerItemProxy */; - }; - B3471F9D23AC2986002232CB /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = B3471F8323AC2985002232CB /* watchOS Example */; - targetProxy = B3471F9C23AC2986002232CB /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - B3471F2223AC2867002232CB /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - B3471F2323AC2867002232CB /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; - B3471F4F23AC287A002232CB /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - B3471F5023AC287A002232CB /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; - B3471F5423AC287B002232CB /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - B3471F5523AC287B002232CB /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; - B3471F8623AC2985002232CB /* Interface.storyboard */ = { - isa = PBXVariantGroup; - children = ( - B3471F8723AC2985002232CB /* Base */, - ); - name = Interface.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - B3471F0E23AC2823002232CB /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MACOSX_DEPLOYMENT_TARGET = 10.10; - ONLY_ACTIVE_ARCH = YES; - TVOS_DEPLOYMENT_TARGET = 9.0; - WATCHOS_DEPLOYMENT_TARGET = 2.0; - }; - name = Debug; - }; - B3471F0F23AC2823002232CB /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MACOSX_DEPLOYMENT_TARGET = 10.10; - TVOS_DEPLOYMENT_TARGET = 9.0; - WATCHOS_DEPLOYMENT_TARGET = 2.0; - }; - name = Release; - }; - B3471F3D23AC2867002232CB /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 2ACD4CCB85CFD8DED56E3F12 /* Pods-iOS Example.debug.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_STYLE = Automatic; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - DEVELOPMENT_TEAM = MLT7M394S7; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - INFOPLIST_FILE = B32CFiOS/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.brightdigit.B32CFiOS; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - B3471F3E23AC2867002232CB /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 48EEEFA22F15885FFC9E7510 /* Pods-iOS Example.release.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_STYLE = Automatic; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEVELOPMENT_TEAM = MLT7M394S7; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - INFOPLIST_FILE = B32CFiOS/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.brightdigit.B32CFiOS; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - B3471F6F23AC287B002232CB /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 3B2D1E843889D06567A800B6 /* Pods-tvOS Example.debug.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_STYLE = Automatic; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - DEVELOPMENT_TEAM = MLT7M394S7; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - INFOPLIST_FILE = B32CFtvOS/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.brightdigit.B32CFtvOS; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = appletvos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 3; - }; - name = Debug; - }; - B3471F7023AC287B002232CB /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 5A01AA6826476C4E5FA36B57 /* Pods-tvOS Example.release.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_STYLE = Automatic; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEVELOPMENT_TEAM = MLT7M394S7; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - INFOPLIST_FILE = B32CFtvOS/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.brightdigit.B32CFtvOS; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = appletvos; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 3; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - B3471F8023AC288B002232CB /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 392B99EB2DE17FFF2B0C2AA6 /* Pods-macOS Example.debug.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "-"; - CODE_SIGN_STYLE = Automatic; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - DEVELOPMENT_TEAM = MLT7M394S7; - ENABLE_HARDENED_RUNTIME = YES; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = macosx; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - }; - name = Debug; - }; - B3471F8123AC288B002232CB /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D95E033A8D448E7BA5F74478 /* Pods-macOS Example.release.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "-"; - CODE_SIGN_STYLE = Automatic; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEVELOPMENT_TEAM = MLT7M394S7; - ENABLE_HARDENED_RUNTIME = YES; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = macosx; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - SWIFT_VERSION = 5.0; - }; - name = Release; - }; - B3471FA023AC2986002232CB /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 6C2418004F1B9205CE4E5633 /* Pods-watchOS Example Extension.debug.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_COMPLICATION_NAME = Complication; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_STYLE = Automatic; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - DEVELOPMENT_TEAM = MLT7M394S7; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - INFOPLIST_FILE = "B32CFwatchOS Extension/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@executable_path/../../Frameworks", - ); - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.brightdigit.B32CFiOS.watchkitapp.watchkitextension; - PRODUCT_NAME = "${TARGET_NAME}"; - SDKROOT = watchos; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 4; - }; - name = Debug; - }; - B3471FA123AC2986002232CB /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 06E1D7EF9CE18DBD9A38B898 /* Pods-watchOS Example Extension.release.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_COMPLICATION_NAME = Complication; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_STYLE = Automatic; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEVELOPMENT_TEAM = MLT7M394S7; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - INFOPLIST_FILE = "B32CFwatchOS Extension/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@executable_path/../../Frameworks", - ); - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.brightdigit.B32CFiOS.watchkitapp.watchkitextension; - PRODUCT_NAME = "${TARGET_NAME}"; - SDKROOT = watchos; - SKIP_INSTALL = YES; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 4; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - B3471FA423AC2986002232CB /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = E5CD669251B441382F1D3718 /* Pods-watchOS Example.debug.xcconfig */; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_STYLE = Automatic; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - DEVELOPMENT_TEAM = MLT7M394S7; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IBSC_MODULE = B32CFwatchOS_Extension; - INFOPLIST_FILE = B32CFwatchOS/Info.plist; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.brightdigit.B32CFiOS.watchkitapp; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = watchos; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 4; - }; - name = Debug; - }; - B3471FA523AC2986002232CB /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = A9AC176BBAAB1CAB1C72D38C /* Pods-watchOS Example.release.xcconfig */; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_STYLE = Automatic; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEVELOPMENT_TEAM = MLT7M394S7; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IBSC_MODULE = B32CFwatchOS_Extension; - INFOPLIST_FILE = B32CFwatchOS/Info.plist; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.brightdigit.B32CFiOS.watchkitapp; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = watchos; - SKIP_INSTALL = YES; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 4; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - B3471F0D23AC2823002232CB /* Build configuration list for PBXProject "Example" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - B3471F0E23AC2823002232CB /* Debug */, - B3471F0F23AC2823002232CB /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - B3471F3C23AC2867002232CB /* Build configuration list for PBXNativeTarget "iOS Example" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - B3471F3D23AC2867002232CB /* Debug */, - B3471F3E23AC2867002232CB /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - B3471F6E23AC287B002232CB /* Build configuration list for PBXNativeTarget "tvOS Example" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - B3471F6F23AC287B002232CB /* Debug */, - B3471F7023AC287B002232CB /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - B3471F7F23AC288B002232CB /* Build configuration list for PBXNativeTarget "macOS Example" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - B3471F8023AC288B002232CB /* Debug */, - B3471F8123AC288B002232CB /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - B3471F9F23AC2986002232CB /* Build configuration list for PBXNativeTarget "watchOS Example Extension" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - B3471FA023AC2986002232CB /* Debug */, - B3471FA123AC2986002232CB /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - B3471FA323AC2986002232CB /* Build configuration list for PBXNativeTarget "watchOS Example" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - B3471FA423AC2986002232CB /* Debug */, - B3471FA523AC2986002232CB /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = B3471F0A23AC2823002232CB /* Project object */; -} diff --git a/Example/Example.xcodeproj/xcshareddata/xcschemes/iOS Example.xcscheme b/Example/Example.xcodeproj/xcshareddata/xcschemes/iOS Example.xcscheme deleted file mode 100644 index 3b934d9..0000000 --- a/Example/Example.xcodeproj/xcshareddata/xcschemes/iOS Example.xcscheme +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Example/Example.xcodeproj/xcshareddata/xcschemes/macOS Example.xcscheme b/Example/Example.xcodeproj/xcshareddata/xcschemes/macOS Example.xcscheme deleted file mode 100644 index 85f467b..0000000 --- a/Example/Example.xcodeproj/xcshareddata/xcschemes/macOS Example.xcscheme +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Example/Example.xcodeproj/xcshareddata/xcschemes/tvOS Example.xcscheme b/Example/Example.xcodeproj/xcshareddata/xcschemes/tvOS Example.xcscheme deleted file mode 100644 index 8ec70bd..0000000 --- a/Example/Example.xcodeproj/xcshareddata/xcschemes/tvOS Example.xcscheme +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Example/Example.xcodeproj/xcshareddata/xcschemes/watchOS Example.xcscheme b/Example/Example.xcodeproj/xcshareddata/xcschemes/watchOS Example.xcscheme deleted file mode 100644 index df21b40..0000000 --- a/Example/Example.xcodeproj/xcshareddata/xcschemes/watchOS Example.xcscheme +++ /dev/null @@ -1,105 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Example/Podfile b/Example/Podfile deleted file mode 100644 index 2d98227..0000000 --- a/Example/Podfile +++ /dev/null @@ -1,47 +0,0 @@ -# Uncomment the next line to define a global platform for your project -# platform :ios, '9.0' - -target 'iOS Example' do - # Comment the next line if you don't want to use dynamic frameworks - use_frameworks! - pod 'Base32Crockford', :path => '../' - - # Pods for iOS Example - -end - -target 'macOS Example' do - # Comment the next line if you don't want to use dynamic frameworks - use_frameworks! - pod 'Base32Crockford', :path => '../' - - # Pods for macOS Example - -end - -target 'tvOS Example' do - # Comment the next line if you don't want to use dynamic frameworks - use_frameworks! - pod 'Base32Crockford', :path => '../' - - # Pods for tvOS Example - -end - -target 'watchOS Example' do - # Comment the next line if you don't want to use dynamic frameworks - use_frameworks! - - # Pods for watchOS Example - pod 'Base32Crockford', :path => '../' - -end - -target 'watchOS Example Extension' do - # Comment the next line if you don't want to use dynamic frameworks - use_frameworks! - pod 'Base32Crockford', :path => '../' - - # Pods for watchOS Example Extension - -end diff --git a/Example/Podfile.lock b/Example/Podfile.lock deleted file mode 100644 index 867ca91..0000000 --- a/Example/Podfile.lock +++ /dev/null @@ -1,16 +0,0 @@ -PODS: - - Base32Crockford (0.1.0) - -DEPENDENCIES: - - Base32Crockford (from `../`) - -EXTERNAL SOURCES: - Base32Crockford: - :path: "../" - -SPEC CHECKSUMS: - Base32Crockford: ea44d32e5fb261d3dacf24e3cf355393467a1fca - -PODFILE CHECKSUM: dffbe8d2393958c54cbebff2ecf40e88475ba72d - -COCOAPODS: 1.8.4 diff --git a/Mintfile b/Mintfile new file mode 100644 index 0000000..5e01529 --- /dev/null +++ b/Mintfile @@ -0,0 +1,3 @@ +nicklockwood/SwiftFormat@0.47.0 +realm/SwiftLint@0.41.0 +peripheryapp/periphery@2.10.0 \ No newline at end of file diff --git a/Package.swift b/Package.swift index 93c7df9..76fe51a 100644 --- a/Package.swift +++ b/Package.swift @@ -1,37 +1,25 @@ -// swift-tools-version:5.1 -// The swift-tools-version declares the minimum version of Swift required to build this package. +// swift-tools-version: 5.5 + +// swiftlint:disable explicit_acl +// swiftlint:disable explicit_top_level_acl import PackageDescription let package = Package( name: "Base32Crockford", products: [ - // Products define the executables and libraries produced by a package, and make them visible to other packages. .library( name: "Base32Crockford", targets: ["Base32Crockford"] - ), - - .executable( - name: "base32dc", - targets: ["base32dc"] ) ], dependencies: [ - // Dependencies declare other packages that this package depends on. - // .package(url: /* package url */, from: "1.0.0"), ], targets: [ - // Targets are the basic building blocks of a package. A target can define a module or a test suite. - // Targets can depend on other targets in this package, and on products in packages which this package depends on. .target( name: "Base32Crockford", dependencies: [] ), - .target( - name: "base32dc", - dependencies: ["Base32Crockford"] - ), .testTarget( name: "Base32CrockfordTests", dependencies: ["Base32Crockford"] diff --git a/Scripts/before_install.sh b/Scripts/before_install.sh deleted file mode 100755 index 0fbe34a..0000000 --- a/Scripts/before_install.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash - -if [[ $TRAVIS_OS_NAME = 'osx' ]]; then - brew update >/dev/null - brew bundle -elif [[ $TRAVIS_OS_NAME = 'linux' ]]; then - RELEASE_DOT=$(lsb_release -sr) - RELEASE_NUM=${RELEASE_DOT//[-._]/} - - if [[ $RELEASE_DOT == "20.04" ]]; then - sudo apt-get update - sudo apt-get -y install \ - binutils \ - git \ - gnupg2 \ - libc6-dev \ - libcurl4 \ - libedit2 \ - libgcc-9-dev \ - libpython2.7 \ - libsqlite3-0 \ - libstdc++-9-dev \ - libxml2 \ - libz3-dev \ - pkg-config \ - tzdata \ - zlib1g-dev - fi - - if [[ $TRAVIS_CPU_ARCH == "arm64" ]]; then - curl -s https://packagecloud.io/install/repositories/swift-arm/release/script.deb.sh | sudo bash - sudo apt-get install swift5 - else - wget https://swift.org/builds/swift-${SWIFT_VER}-release/ubuntu${RELEASE_NUM}/swift-${SWIFT_VER}-RELEASE/swift-${SWIFT_VER}-RELEASE-ubuntu${RELEASE_DOT}.tar.gz - tar xzf swift-${SWIFT_VER}-RELEASE-ubuntu${RELEASE_DOT}.tar.gz - fi -fi diff --git a/generate_testdata.js b/Scripts/generate_testdata.js similarity index 100% rename from generate_testdata.js rename to Scripts/generate_testdata.js diff --git a/Scripts/generate_testdata.py b/Scripts/generate_testdata.py new file mode 100644 index 0000000..da6fdd7 --- /dev/null +++ b/Scripts/generate_testdata.py @@ -0,0 +1,7 @@ +import uuid +import base32_crockford + +for _ in range(100): + id = uuid.uuid4() + strck = base32_crockford.encode(id.int, checksum=False) + print(id, strck) \ No newline at end of file diff --git a/Scripts/gh-md-toc b/Scripts/gh-md-toc new file mode 100755 index 0000000..8d35839 --- /dev/null +++ b/Scripts/gh-md-toc @@ -0,0 +1,411 @@ +#!/usr/bin/env bash + +# +# Steps: +# +# 1. Download corresponding html file for some README.md: +# curl -s $1 +# +# 2. Discard rows where no substring 'user-content-' (github's markup): +# awk '/user-content-/ { ... +# +# 3.1 Get last number in each row like ' ... sitemap.js.*<\/h/)+2, RLENGTH-5) +# +# 5. Find anchor and insert it inside "(...)": +# substr($0, match($0, "href=\"[^\"]+?\" ")+6, RLENGTH-8) +# + +gh_toc_version="0.8.0" + +gh_user_agent="gh-md-toc v$gh_toc_version" + +# +# Download rendered into html README.md by its url. +# +# +gh_toc_load() { + local gh_url=$1 + + if type curl &>/dev/null; then + curl --user-agent "$gh_user_agent" -s "$gh_url" + elif type wget &>/dev/null; then + wget --user-agent="$gh_user_agent" -qO- "$gh_url" + else + echo "Please, install 'curl' or 'wget' and try again." + exit 1 + fi +} + +# +# Converts local md file into html by GitHub +# +# -> curl -X POST --data '{"text": "Hello world github/linguist#1 **cool**, and #1!"}' https://api.github.com/markdown +#

Hello world github/linguist#1 cool, and #1!

'" +gh_toc_md2html() { + local gh_file_md=$1 + local skip_header=$2 + + URL=https://api.github.com/markdown/raw + + if [ ! -z "$GH_TOC_TOKEN" ]; then + TOKEN=$GH_TOC_TOKEN + else + TOKEN_FILE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/token.txt" + if [ -f "$TOKEN_FILE" ]; then + TOKEN="$(cat $TOKEN_FILE)" + fi + fi + if [ ! -z "${TOKEN}" ]; then + AUTHORIZATION="Authorization: token ${TOKEN}" + fi + + local gh_tmp_file_md=$gh_file_md + if [ "$skip_header" = "yes" ]; then + if grep -Fxq "" $gh_src; then + # cut everything before the toc + gh_tmp_file_md=$gh_file_md~~ + sed '1,//d' $gh_file_md > $gh_tmp_file_md + fi + fi + + # echo $URL 1>&2 + OUTPUT=$(curl -s \ + --user-agent "$gh_user_agent" \ + --data-binary @"$gh_tmp_file_md" \ + -H "Content-Type:text/plain" \ + -H "$AUTHORIZATION" \ + "$URL") + + rm -f $gh_file_md~~ + + if [ "$?" != "0" ]; then + echo "XXNetworkErrorXX" + fi + if [ "$(echo "${OUTPUT}" | awk '/API rate limit exceeded/')" != "" ]; then + echo "XXRateLimitXX" + else + echo "${OUTPUT}" + fi +} + + +# +# Is passed string url +# +gh_is_url() { + case $1 in + https* | http*) + echo "yes";; + *) + echo "no";; + esac +} + +# +# TOC generator +# +gh_toc(){ + local gh_src=$1 + local gh_src_copy=$1 + local gh_ttl_docs=$2 + local need_replace=$3 + local no_backup=$4 + local no_footer=$5 + local indent=$6 + local skip_header=$7 + + if [ "$gh_src" = "" ]; then + echo "Please, enter URL or local path for a README.md" + exit 1 + fi + + + # Show "TOC" string only if working with one document + if [ "$gh_ttl_docs" = "1" ]; then + + echo "Table of Contents" + echo "=================" + echo "" + gh_src_copy="" + + fi + + if [ "$(gh_is_url "$gh_src")" == "yes" ]; then + gh_toc_load "$gh_src" | gh_toc_grab "$gh_src_copy" "$indent" + if [ "${PIPESTATUS[0]}" != "0" ]; then + echo "Could not load remote document." + echo "Please check your url or network connectivity" + exit 1 + fi + if [ "$need_replace" = "yes" ]; then + echo + echo "!! '$gh_src' is not a local file" + echo "!! Can't insert the TOC into it." + echo + fi + else + local rawhtml=$(gh_toc_md2html "$gh_src" "$skip_header") + if [ "$rawhtml" == "XXNetworkErrorXX" ]; then + echo "Parsing local markdown file requires access to github API" + echo "Please make sure curl is installed and check your network connectivity" + exit 1 + fi + if [ "$rawhtml" == "XXRateLimitXX" ]; then + echo "Parsing local markdown file requires access to github API" + echo "Error: You exceeded the hourly limit. See: https://developer.github.com/v3/#rate-limiting" + TOKEN_FILE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/token.txt" + echo "or place GitHub auth token here: ${TOKEN_FILE}" + exit 1 + fi + local toc=`echo "$rawhtml" | gh_toc_grab "$gh_src_copy" "$indent"` + echo "$toc" + if [ "$need_replace" = "yes" ]; then + if grep -Fxq "" $gh_src && grep -Fxq "" $gh_src; then + echo "Found markers" + else + echo "You don't have or in your file...exiting" + exit 1 + fi + local ts="<\!--ts-->" + local te="<\!--te-->" + local dt=`date +'%F_%H%M%S'` + local ext=".orig.${dt}" + local toc_path="${gh_src}.toc.${dt}" + local toc_createdby="" + local toc_footer="" + # http://fahdshariff.blogspot.ru/2012/12/sed-mutli-line-replacement-between-two.html + # clear old TOC + sed -i${ext} "/${ts}/,/${te}/{//!d;}" "$gh_src" + # create toc file + echo "${toc}" > "${toc_path}" + if [ "${no_footer}" != "yes" ]; then + echo -e "\n${toc_createdby}\n${toc_footer}\n" >> "$toc_path" + fi + + # insert toc file + if ! sed --version > /dev/null 2>&1; then + sed -i "" "/${ts}/r ${toc_path}" "$gh_src" + else + sed -i "/${ts}/r ${toc_path}" "$gh_src" + fi + echo + if [ "${no_backup}" = "yes" ]; then + rm ${toc_path} ${gh_src}${ext} + fi + echo "!! TOC was added into: '$gh_src'" + if [ -z "${no_backup}" ]; then + echo "!! Origin version of the file: '${gh_src}${ext}'" + echo "!! TOC added into a separate file: '${toc_path}'" + fi + echo + fi + fi +} + +# +# Grabber of the TOC from rendered html +# +# $1 - a source url of document. +# It's need if TOC is generated for multiple documents. +# $2 - number of spaces used to indent. +# +gh_toc_grab() { + common_awk_script=' + modified_href = "" + split(href, chars, "") + for (i=1;i <= length(href); i++) { + c = chars[i] + res = "" + if (c == "+") { + res = " " + } else { + if (c == "%") { + res = "\\x" + } else { + res = c "" + } + } + modified_href = modified_href res + } + print sprintf("%*s", (level-1)*'"$2"', "") "* [" text "](" gh_url modified_href ")" + ' + if [ `uname -s` == "OS/390" ]; then + grepcmd="pcregrep -o" + echoargs="" + awkscript='{ + level = substr($0, length($0), 1) + text = substr($0, match($0, /a>.*<\/h/)+2, RLENGTH-5) + href = substr($0, match($0, "href=\"([^\"]+)?\"")+6, RLENGTH-7) + '"$common_awk_script"' + }' + else + grepcmd="grep -Eo" + echoargs="-e" + awkscript='{ + level = substr($0, length($0), 1) + text = substr($0, match($0, /a>.*<\/h/)+2, RLENGTH-5) + href = substr($0, match($0, "href=\"[^\"]+?\"")+6, RLENGTH-7) + '"$common_awk_script"' + }' + fi + href_regex='href=\"[^\"]+?\"' + + # if closed is on the new line, then move it on the prev line + # for example: + # was: The command foo1 + # + # became: The command foo1 + sed -e ':a' -e 'N' -e '$!ba' -e 's/\n<\/h/<\/h/g' | + + # find strings that corresponds to template + $grepcmd '//g' | sed 's/<\/code>//g' | + + # remove g-emoji + sed 's/]*[^<]*<\/g-emoji> //g' | + + # now all rows are like: + # ... /dev/null`; then + echo `$tool --version | head -n 1` + else + echo "not installed" + fi + done +} + +show_help() { + local app_name=$(basename "$0") + echo "GitHub TOC generator ($app_name): $gh_toc_version" + echo "" + echo "Usage:" + echo " $app_name [options] src [src] Create TOC for a README file (url or local path)" + echo " $app_name - Create TOC for markdown from STDIN" + echo " $app_name --help Show help" + echo " $app_name --version Show version" + echo "" + echo "Options:" + echo " --indent Set indent size. Default: 3." + echo " --insert Insert new TOC into original file. For local files only. Default: false." + echo " See https://github.com/ekalinin/github-markdown-toc/issues/41 for details." + echo " --no-backup Remove backup file. Set --insert as well. Default: false." + echo " --hide-footer Do not write date & author of the last TOC update. Set --insert as well. Default: false." + echo " --skip-header Hide entry of the topmost headlines. Default: false." + echo " See https://github.com/ekalinin/github-markdown-toc/issues/125 for details." + echo "" +} + +# +# Options handlers +# +gh_toc_app() { + local need_replace="no" + local indent=3 + + if [ "$1" = '--help' ] || [ $# -eq 0 ] ; then + show_help + return + fi + + if [ "$1" = '--version' ]; then + show_version + return + fi + + if [ "$1" = '--indent' ]; then + indent="$2" + shift 2 + fi + + if [ "$1" = "-" ]; then + if [ -z "$TMPDIR" ]; then + TMPDIR="/tmp" + elif [ -n "$TMPDIR" -a ! -d "$TMPDIR" ]; then + mkdir -p "$TMPDIR" + fi + local gh_tmp_md + if [ `uname -s` == "OS/390" ]; then + local timestamp=$(date +%m%d%Y%H%M%S) + gh_tmp_md="$TMPDIR/tmp.$timestamp" + else + gh_tmp_md=$(mktemp $TMPDIR/tmp.XXXXXX) + fi + while read input; do + echo "$input" >> "$gh_tmp_md" + done + gh_toc_md2html "$gh_tmp_md" | gh_toc_grab "" "$indent" + return + fi + + if [ "$1" = '--insert' ]; then + need_replace="yes" + shift + fi + + if [ "$1" = '--no-backup' ]; then + need_replace="yes" + no_backup="yes" + shift + fi + + if [ "$1" = '--hide-footer' ]; then + need_replace="yes" + no_footer="yes" + shift + fi + + if [ "$1" = '--skip-header' ]; then + skip_header="yes" + shift + fi + + + for md in "$@" + do + echo "" + gh_toc "$md" "$#" "$need_replace" "$no_backup" "$no_footer" "$indent" "$skip_header" + done + + echo "" + echo "" +} + +# +# Entry point +# +gh_toc_app "$@" diff --git a/Scripts/lint.sh b/Scripts/lint.sh new file mode 100755 index 0000000..cd8e125 --- /dev/null +++ b/Scripts/lint.sh @@ -0,0 +1,48 @@ +#!/bin/sh + +if [ -z "$SRCROOT" ]; then + SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + PACKAGE_DIR="${SCRIPT_DIR}/.." +else + PACKAGE_DIR="${SRCROOT}" +fi + +if [ -z "$GITHUB_ACTION" ]; then + MINT_CMD="/opt/homebrew/bin/mint" +else + MINT_CMD="mint" +fi + +export MINT_PATH="$PACKAGE_DIR/.mint" +MINT_ARGS="-n -m $PACKAGE_DIR/Mintfile --silent" +MINT_RUN="$MINT_CMD run $MINT_ARGS" + +pushd $PACKAGE_DIR + +$MINT_CMD bootstrap -m Mintfile + +if [ "$LINT_MODE" == "NONE" ]; then + exit +elif [ "$LINT_MODE" == "STRICT" ]; then + SWIFTFORMAT_OPTIONS="" + SWIFTLINT_OPTIONS="--strict" + STRINGSLINT_OPTIONS="--config .strict.stringslint.yml" +else + SWIFTFORMAT_OPTIONS="" + SWIFTLINT_OPTIONS="" + STRINGSLINT_OPTIONS="--config .stringslint.yml" +fi + +pushd $PACKAGE_DIR + +if [ -z "$CI" ]; then + $MINT_RUN swiftformat . + $MINT_RUN swiftlint autocorrect +fi + +$MINT_RUN periphery scan +$MINT_RUN stringslint lint $STRINGSLINT_OPTIONS +$MINT_RUN swiftformat --lint $SWIFTFORMAT_OPTIONS . +$MINT_RUN swiftlint lint $SWIFTLINT_OPTIONS + +popd diff --git a/Scripts/script.sh b/Scripts/script.sh deleted file mode 100755 index 3b75afe..0000000 --- a/Scripts/script.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash - -if [[ $TRAVIS_OS_NAME = 'osx' ]]; then - swiftformat --lint . && swiftlint -elif [[ $TRAVIS_OS_NAME = 'linux' ]]; then - # What to do in Ubunutu - RELEASE_DOT=$(lsb_release -sr) - RELEASE_NUM=${RELEASE_DOT//[-._]/} - RELEASE_NAME=$(lsb_release -sc) - [[ $TRAVIS_CPU_ARCH = "arm64" ]] && ARCH_PREFIX="aarch64" || ARCH_PREFIX="x86_64" - export PATH="${PWD}/swift-${SWIFT_VER}-RELEASE-ubuntu${RELEASE_DOT}/usr/bin:$PATH" -fi - -ARCH=${TRAVIS_CPU_ARCH:-amd64} -[[ $TRAVIS_CPU_ARCH = "arm64" ]] && ARCH_PREFIX="aarch64" || ARCH_PREFIX="x86_64" - -swift build -swift test --enable-code-coverage --enable-test-discovery - -if [[ $TRAVIS_OS_NAME = 'osx' ]]; then - xcrun llvm-cov export -format="lcov" .build/debug/${FRAMEWORK_NAME}PackageTests.xctest/Contents/MacOS/${FRAMEWORK_NAME}PackageTests -instr-profile .build/debug/codecov/default.profdata > info.lcov - bash <(curl https://codecov.io/bash) -F travis -F macOS -n $TRAVIS_JOB_NUMBER-$TRAVIS_OS_NAME -else - llvm-cov export -format="lcov" .build/${ARCH_PREFIX}-unknown-linux-gnu/debug/${FRAMEWORK_NAME}PackageTests.xctest -instr-profile .build/debug/codecov/default.profdata > info.lcov - bash <(curl https://codecov.io/bash) -F travis -F $RELEASE_NAME -F $ARCH -n $TRAVIS_JOB_NUMBER-$TRAVIS_OS_NAME -fi - -curl -s https://raw.githubusercontent.com/daveverwer/SwiftPMLibrary/master/script.sh | bash -s -- mine - -if [[ $TRAVIS_OS_NAME = 'osx' ]]; then - pod lib lint - swift package generate-xcodeproj - pod install --silent --project-directory=Example - xcodebuild -quiet -workspace Example/Example.xcworkspace -scheme "iOS Example" ONLY_ACTIVE_ARCH=NO CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO - xcodebuild -quiet -workspace Example/Example.xcworkspace -scheme "tvOS Example" ONLY_ACTIVE_ARCH=NO CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO - xcodebuild -quiet -workspace Example/Example.xcworkspace -scheme "macOS Example" ONLY_ACTIVE_ARCH=NO CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO -fi diff --git a/Sources/Base32Crockford/Array.swift b/Sources/Base32Crockford/Array.swift index cd3a353..ac8da6c 100644 --- a/Sources/Base32Crockford/Array.swift +++ b/Sources/Base32Crockford/Array.swift @@ -8,18 +8,31 @@ extension Array where Element == UInt8 { } extension Array where Element: FixedWidthInteger { - public static func random(withCount count: Int, in range: ClosedRange? = nil) -> Array { - return random(withCount: count, in: range, fatalError: nil)! + public static func random( + withCount count: Int, + in range: ClosedRange? = nil + ) -> Array { + // swiftlint:disable:next force_unwrapping + random(withCount: count, in: range, fatalError: nil)! } #if DEBUG - internal static func debugRandom(withCount count: Int, in range: ClosedRange? = nil, fatalError: ((String?) -> Void)? = nil) -> Array? { - return random(withCount: count, in: range, fatalError: fatalError) + internal static func debugRandom( + withCount count: Int, + in range: ClosedRange? = nil, + fatalError: ((String?) -> Void)? = nil + ) -> Array? { + random(withCount: count, in: range, fatalError: fatalError) } #endif - private static func random(withCount count: Int, in range: ClosedRange? = nil, fatalError: ((String?) -> Void)? = nil) -> Array? { + private static func random( + withCount count: Int, + in range: ClosedRange? = nil, + fatalError: ((String?) -> Void)? = nil + ) -> Array? { let range = range ?? (Element.min ... Element.max) + // swiftlint:disable empty_count guard count >= 0 else { if let fatalError = fatalError { fatalError("Array count cannot be less than 0.") @@ -28,6 +41,8 @@ extension Array where Element: FixedWidthInteger { Swift.fatalError("Array count cannot be less than 0.") } } + + // swiftlint:enable empty_count guard count >= 1 else { return [Element]() } diff --git a/Sources/Base32Crockford/Base32CrockfordEncoding.swift b/Sources/Base32Crockford/Base32CrockfordEncoding.swift index eba0a10..8a798b9 100644 --- a/Sources/Base32Crockford/Base32CrockfordEncoding.swift +++ b/Sources/Base32Crockford/Base32CrockfordEncoding.swift @@ -1,63 +1,106 @@ import Foundation +public struct Base32CrockfordEncodingOptions: OptionSet { + public static let withChecksum = Base32CrockfordEncodingOptions(rawValue: 1 << 0) + public static let none: Base32CrockfordEncodingOptions = [] + + public let rawValue: Int + + public init(rawValue: Int) { + self.rawValue = rawValue + } +} + +public typealias Base32CrockfordDecodingOptions = Base32CrockfordEncodingOptions + +// swiftlint:disable:next line_length public struct Base32CrockfordEncoding: Base32CrockfordEncodingProtocol, Base32CrockfordComparer { - fileprivate static let _encoding = Base32CrockfordEncoding() + private struct ChecksumError: Error {} + + private static let _encoding = Base32CrockfordEncoding() public static var encoding: Base32CrockfordEncodingProtocol { - return _encoding + _encoding } public static var comparer: Base32CrockfordComparer { - return _encoding + _encoding } - fileprivate static let characters = "0123456789abcdefghjkmnpqrtuvwxyz".uppercased() + private static let characters = "0123456789abcdefghjkmnpqrtuvwxyz".uppercased() + private static let checkSymbols = "*~$=U" - fileprivate struct ChecksumError: Error {} - fileprivate func sizeOf(checksumFrom string: String) -> Int { + private func sizeOf(extensionFrom string: String) -> Int { let strBitCount = string.count * 5 let dataBitCount = Int(floor(Double(strBitCount) / 8)) * 8 return strBitCount - dataBitCount } - fileprivate func decodeWithoutChecksum(base32Encoded string: String) -> Data { + private func decodeWithoutExtension(base32Encoded string: String) -> Data { let standardized = standardize(string: string) - let checksumSize = sizeOf(checksumFrom: standardized) + let extensionSize = sizeOf(extensionFrom: standardized) - return decode(standardizedString: standardized, withChecksumSize: checksumSize) + return decode(standardizedString: standardized, withExtensionSize: extensionSize) } - fileprivate func verifyChecksum(_ checksumSize: Int, _ standardized: String) throws { + private func verifyExtension(_ size: Int, _ standardized: String) throws { let lastValue: UInt8? - if checksumSize != 0 { - let lastIndex = Base32CrockfordEncoding.characters.firstIndex(of: standardized.last!)! - lastValue = UInt8(Base32CrockfordEncoding.characters.distance(from: Base32CrockfordEncoding.characters.startIndex, to: lastIndex)) + if let last = standardized.last, size != 0 { + if let lastIndex = Base32CrockfordEncoding.characters.firstIndex( + of: last + ) { + lastValue = UInt8( + Base32CrockfordEncoding.characters.distance( + from: Base32CrockfordEncoding.characters.startIndex, + to: lastIndex + ) + ) + } else { + lastValue = nil + } } else { lastValue = nil } if let lastValue = lastValue { - let checksumValue = (lastValue << (8 - checksumSize)) >> (8 - checksumSize) - guard checksumValue == 0 else { + let extensionValue = (lastValue << (8 - size)) >> (8 - size) + guard extensionValue == 0 else { throw ChecksumError() } } } - fileprivate func decode(standardizedString standardized: String, withChecksumSize checksumSize: Int) -> Data { - let values = standardized.map { character -> String.IndexDistance in - let lastIndex = Base32CrockfordEncoding.characters.firstIndex(of: character)! - return Base32CrockfordEncoding.characters.distance(from: Base32CrockfordEncoding.characters.startIndex, to: lastIndex) + private func decode( + standardizedString standardized: String, + withExtensionSize checksumSize: Int + ) -> Data { + let values = standardized.map { character -> Int in + guard let lastIndex = Base32CrockfordEncoding.characters.firstIndex( + of: character + ) else { + preconditionFailure("Invalid Characters should never be passed.") + } + return Base32CrockfordEncoding.characters.distance( + from: Base32CrockfordEncoding.characters.startIndex, + to: lastIndex + ) } - let bitString = values.map { String($0, radix: 2).pad(toSize: 5) }.joined(separator: "") + let bitString = values.map { String($0, radix: 2).pad(toSize: 5) }.joined() - let bitStringWithoutChecksum = String(bitString[bitString.startIndex ... bitString.index(bitString.endIndex, offsetBy: -checksumSize - 1)]) - let dataBytes = bitStringWithoutChecksum.split(by: 8).compactMap { UInt8($0, radix: 2) } + let bitStringWithoutChecksum = String( + bitString[ + bitString.startIndex ... + bitString.index(bitString.endIndex, offsetBy: -checksumSize - 1) + ] + ) + let dataBytes = bitStringWithoutChecksum.split(by: 8).compactMap { + UInt8($0, radix: 2) + } return Data(dataBytes) } - public func encode(data: Data) -> String { + public func encode(data: Data, options _: Base32CrockfordEncodingOptions) -> String { let dataBitCount = data.count * 8 let resBitCount = Int(ceil(Double(dataBitCount) / 5) * 5.0) let difference = resBitCount - dataBitCount @@ -75,28 +118,36 @@ public struct Base32CrockfordEncoding: Base32CrockfordEncodingProtocol, Base32Cr Base32CrockfordEncoding.characters.startIndex, offsetBy: index ) encodedString.append( - Base32CrockfordEncoding.characters[characterIndex]) - + Base32CrockfordEncoding.characters[characterIndex] + ) } while index != nil if lastSegment > 0 { let lastIndex = (binary.next(bits: lastSegment)! << difference) - let characterIndex = Base32CrockfordEncoding.characters.index(Base32CrockfordEncoding.characters.startIndex, offsetBy: lastIndex) + let characterIndex = Base32CrockfordEncoding + .characters + .index( + Base32CrockfordEncoding.characters.startIndex, + offsetBy: lastIndex + ) encodedString.append(Base32CrockfordEncoding.characters[characterIndex]) } return encodedString } - public func decode(base32Encoded string: String) throws -> Data { + public func decode( + base32Encoded string: String, + options _: Base32CrockfordDecodingOptions + ) throws -> Data { let standardized = standardize(string: string) - let checksumSize = sizeOf(checksumFrom: standardized) - try verifyChecksum(checksumSize, standardized) + let extensionSize = sizeOf(extensionFrom: standardized) + try verifyExtension(extensionSize, standardized) - return decode(standardizedString: standardized, withChecksumSize: checksumSize) + return decode(standardizedString: standardized, withExtensionSize: extensionSize) } public func data(_ data: Data, hasEncodedPrefix prefix: String) -> Bool { - let prefixData = decodeWithoutChecksum(base32Encoded: prefix) + let prefixData = decodeWithoutExtension(base32Encoded: prefix) return zip(data, prefixData).allSatisfy { $0 == $1 } } } diff --git a/Sources/Base32Crockford/Base32CrockfordEncodingProtocol.swift b/Sources/Base32Crockford/Base32CrockfordEncodingProtocol.swift index b06220a..75f3768 100644 --- a/Sources/Base32Crockford/Base32CrockfordEncodingProtocol.swift +++ b/Sources/Base32Crockford/Base32CrockfordEncodingProtocol.swift @@ -1,14 +1,28 @@ import Foundation public protocol Base32CrockfordEncodingProtocol: Base32CrockfordGenerator { - func encode(data: Data) -> String - func decode(base32Encoded string: String) throws -> Data static var encoding: Base32CrockfordEncodingProtocol { get } + + func encode(data: Data, options: Base32CrockfordEncodingOptions) -> String + func decode( + base32Encoded string: String, + options: Base32CrockfordDecodingOptions + ) throws -> Data +} + +extension Base32CrockfordEncodingProtocol { + public func encode(data: Data) -> String { + encode(data: data, options: .none) + } + + public func decode(base32Encoded string: String) throws -> Data { + try decode(base32Encoded: string, options: .none) + } } -public extension Base32CrockfordEncodingProtocol { - func standardize(string: String) -> String { - return string +extension Base32CrockfordEncodingProtocol { + public func standardize(string: String) -> String { + string .uppercased() .replacingOccurrences(of: "O", with: "0") .replacingOccurrences(of: "I", with: "1") diff --git a/Sources/Base32Crockford/Base32CrockfordGenerator.swift b/Sources/Base32Crockford/Base32CrockfordGenerator.swift index 8f6e171..1168058 100644 --- a/Sources/Base32Crockford/Base32CrockfordGenerator.swift +++ b/Sources/Base32Crockford/Base32CrockfordGenerator.swift @@ -13,7 +13,7 @@ extension Base32CrockfordEncodingProtocol { } private func generateSingle() -> String { - return generate(withByteSize: 5) + generate(withByteSize: 5) } private func generate(withByteSize size: Int) -> String { @@ -21,16 +21,23 @@ extension Base32CrockfordEncodingProtocol { return encode(data: data) } - private func generate(forMinimumUniqueCount count: Int, fatalError: ((String?) -> Void)? = nil) -> String? { + private func generate( + forMinimumUniqueCount count: Int, + fatalError: ((String?) -> Void)? = nil + ) -> String? { + // swiftlint:disable empty_count guard count > 0 else { if count == 0 { return "" } else { + // swiftlint:enable empty_count if let fatalError = fatalError { fatalError("Cannot construct String identifier for unique count less than 0.") return nil } else { - Swift.fatalError("Cannot construct String identifier for unique count less than 0.") + Swift.fatalError( + "Cannot construct String identifier for unique count less than 0." + ) } } } @@ -40,23 +47,35 @@ extension Base32CrockfordEncodingProtocol { } public func generateIdentifier(from identifierDataType: IdentifierDataType) -> String { - return generateIdentifier(from: identifierDataType)! + // swiftlint:disable:next force_unwrapping + generateIdentifier(from: identifierDataType)! } - private func generateIdentifier(from identifierDataType: IdentifierDataType, fatalError: ((String?) -> Void)? = nil) -> String? { + private func generateIdentifier( + from identifierDataType: IdentifierDataType, + fatalError: ((String?) -> Void)? = nil + ) -> String? { switch identifierDataType { case .default: return generateSingle() + case .uuid: return generateFromUUID() + case let .bytes(size): return generate(withByteSize: size) + case let .minimumCount(count): return generate(forMinimumUniqueCount: count, fatalError: fatalError) } } - private func generate(_ count: Int, from identifierDataType: IdentifierDataType, fatalError: ((String?) -> Void)? = nil) -> [String]? { + private func generate( + _ count: Int, + from identifierDataType: IdentifierDataType, + fatalError: ((String?) -> Void)? = nil + ) -> [String]? { + // swiftlint:disable empty_count guard count >= 0 else { if let fatalError = fatalError { fatalError("Array count cannot be less than 0.") @@ -68,22 +87,34 @@ extension Base32CrockfordEncodingProtocol { guard count > 0 else { return [String]() } + // swiftlint:enable empty_count return (1 ... count).map { _ in self.generateIdentifier(from: identifierDataType) } } - public func generate(_ count: Int, from identifierDataType: IdentifierDataType) -> [String] { - return generate(count, from: identifierDataType, fatalError: nil)! + public func generate( + _ count: Int, + from identifierDataType: IdentifierDataType + ) -> [String] { + // swiftlint:disable:next force_unwrapping + generate(count, from: identifierDataType, fatalError: nil)! } #if DEBUG - internal func debugGenerate(_ count: Int, from identifierDataType: IdentifierDataType, fatalError: ((String?) -> Void)? = nil) -> [String]? { - return generate(count, from: identifierDataType, fatalError: fatalError) + internal func debugGenerate( + _ count: Int, + from identifierDataType: IdentifierDataType, + fatalError: ((String?) -> Void)? = nil + ) -> [String]? { + generate(count, from: identifierDataType, fatalError: fatalError) } - internal func debugGenerateIdentifier(from identifierDataType: IdentifierDataType, fatalError: ((String?) -> Void)? = nil) -> String? { - return generateIdentifier(from: identifierDataType, fatalError: fatalError) + internal func debugGenerateIdentifier( + from identifierDataType: IdentifierDataType, + fatalError: ((String?) -> Void)? = nil + ) -> String? { + generateIdentifier(from: identifierDataType, fatalError: fatalError) } #endif } diff --git a/Sources/Base32Crockford/Binary.swift b/Sources/Base32Crockford/Binary.swift index 8ed727d..b0ca5fc 100644 --- a/Sources/Base32Crockford/Binary.swift +++ b/Sources/Base32Crockford/Binary.swift @@ -33,15 +33,15 @@ public struct Binary { } public func bits(_ start: Int, _ length: Int) -> Int { - return bits(start ..< (start + length)) + bits(start ..< (start + length)) } public func byte(_ position: Int) -> Int { - return Int(bytes[position]) + Int(bytes[position]) } public func bitsWithInternalOffsetAvailable(_ length: Int) -> Bool { - return (bytes.count * byteSize) >= (readingOffset + length) + (bytes.count * byteSize) >= (readingOffset + length) } public mutating func next(bits length: Int) -> Int? { diff --git a/Sources/Base32Crockford/Data.swift b/Sources/Base32Crockford/Data.swift index 0242dfe..5313af8 100644 --- a/Sources/Base32Crockford/Data.swift +++ b/Sources/Base32Crockford/Data.swift @@ -4,14 +4,14 @@ public typealias ByteCollection = [UInt8] extension Data { public static func random(withNumberOfBytes count: Int) -> Data { - return Data(ByteCollection.random(withCount: count)) + Data(ByteCollection.random(withCount: count)) } public static func bytesRequired(forUniqueCountOf count: Int) -> Int { - return Int(ceil(log(Double(count)) / log(256.0))) + Int(ceil(log(Double(count)) / log(256.0))) } public static func uniqueIdentifier(forMinimumCount count: Int) -> Data { - return random(withNumberOfBytes: bytesRequired(forUniqueCountOf: count)) + random(withNumberOfBytes: bytesRequired(forUniqueCountOf: count)) } } diff --git a/Sources/Base32Crockford/IdentifierDataType.swift b/Sources/Base32Crockford/IdentifierDataType.swift index f0f5f00..1b7cb56 100644 --- a/Sources/Base32Crockford/IdentifierDataType.swift +++ b/Sources/Base32Crockford/IdentifierDataType.swift @@ -1,20 +1,20 @@ import Foundation +public struct InvalidIdentifierDataTypeError: Error {} + public enum IdentifierDataType: Equatable { case `default` case uuid case bytes(size: Int) case minimumCount(Int) - enum CodingKeys: String, CodingKey { + public enum CodingKeys: String, CodingKey { case bytes case minimumCount case type } } -struct InvalidIdentifierDataTypeError: Error {} - extension IdentifierDataType: Codable { public init(from decoder: Decoder) throws { guard let container = try? decoder.container(keyedBy: CodingKeys.self) else { @@ -28,7 +28,9 @@ extension IdentifierDataType: Codable { self = .bytes(size: byteSize) return } else if container.allKeys.contains(.minimumCount) { - guard let minimumCount = try? container.decode(Int.self, forKey: .minimumCount) else { + guard let minimumCount = try? container.decode( + Int.self, forKey: .minimumCount + ) else { throw InvalidIdentifierDataTypeError() } self = .minimumCount(minimumCount) @@ -50,10 +52,13 @@ extension IdentifierDataType: Codable { switch self { case .uuid: try container.encode("uuid", forKey: .type) + case let .bytes(size): try container.encode(size, forKey: .bytes) + case let .minimumCount(count): try container.encode(count, forKey: .minimumCount) + default: try container.encodeNil(forKey: .type) } diff --git a/Sources/Base32Crockford/String.swift b/Sources/Base32Crockford/String.swift index fc9fcd3..84c825e 100644 --- a/Sources/Base32Crockford/String.swift +++ b/Sources/Base32Crockford/String.swift @@ -7,12 +7,16 @@ extension String { return padded } - func split(by length: Int) -> [String] { + public func split(by length: Int) -> [String] { var startIndex = self.startIndex var results = [Substring]() while startIndex < endIndex { - let endIndex = index(startIndex, offsetBy: length, limitedBy: self.endIndex) ?? self.endIndex + let endIndex = index( + startIndex, + offsetBy: length, + limitedBy: self.endIndex + ) ?? self.endIndex results.append(self[startIndex ..< endIndex]) startIndex = endIndex } diff --git a/Sources/base32dc/main.swift b/Sources/base32dc/main.swift deleted file mode 100644 index 0089b63..0000000 --- a/Sources/base32dc/main.swift +++ /dev/null @@ -1,3 +0,0 @@ -import Base32Crockford - -print(Base32CrockfordEncoding.encoding.generateIdentifier(from: .default)) diff --git a/Tests/Base32CrockfordTests/Base32CrockfordTests.swift b/Tests/Base32CrockfordTests/Base32CrockfordTests.swift index ff182a8..2a20c88 100644 --- a/Tests/Base32CrockfordTests/Base32CrockfordTests.swift +++ b/Tests/Base32CrockfordTests/Base32CrockfordTests.swift @@ -129,7 +129,7 @@ final class Base32CrockfordTests: XCTestCase { } func testIdentifierDataTypeCodable() { - identifierDataType(.minimumCount(10000), isCodableWith: """ + identifierDataType(.minimumCount(10_000), isCodableWith: """ { "minimumCount" : 10000 } diff --git a/Tests/Base32CrockfordTests/Base32PatternTests.swift b/Tests/Base32CrockfordTests/Base32PatternTests.swift index bd2308b..1aa4458 100644 --- a/Tests/Base32CrockfordTests/Base32PatternTests.swift +++ b/Tests/Base32CrockfordTests/Base32PatternTests.swift @@ -6,7 +6,7 @@ final class Base32EqualityTests: XCTestCase { let values = ["0": "O", "1": "I", "I": "L"] let encoding = Base32CrockfordEncoding() var checks = 0 - for _ in 0 ... 2000 { + for _ in 0 ... 2_000 { let id = UUID() let data = Data(Array(uuid: id)) let fullId = encoding.encode(data: data) diff --git a/Tests/Base32CrockfordTests/EncodeDecodeTests.swift b/Tests/Base32CrockfordTests/EncodeDecodeTests.swift index 7b7008a..1106743 100644 --- a/Tests/Base32CrockfordTests/EncodeDecodeTests.swift +++ b/Tests/Base32CrockfordTests/EncodeDecodeTests.swift @@ -3,6 +3,7 @@ import XCTest final class EncodeDecodeTests: XCTestCase { var data: [String: String]! + var checksumData: [UUID: String]! override func setUp() { let valuesUrl = URL(fileURLWithPath: #file).deletingLastPathComponent().appendingPathComponent("../../Data/values") @@ -18,6 +19,20 @@ final class EncodeDecodeTests: XCTestCase { dictionary[key] = value return dictionary } + +// let checksumUrl = URL(fileURLWithPath: #file).deletingLastPathComponent().appendingPathComponent("../../Data/checksum") +// guard let checksumText = try? String(contentsOf: checksumUrl) else { +// return +// } +// checksumData = checksumText.components(separatedBy: .newlines).reduce([UUID: String]()) { data, line in +// let components = line.components(separatedBy: .whitespaces) +// guard let key = components.first, let value = components.last, components.first != components.last else { +// return data +// } +// var dictionary = data +// dictionary[UUID(uuidString: key)!] = value +// return dictionary +// } } func testEncoding() { @@ -31,6 +46,23 @@ final class EncodeDecodeTests: XCTestCase { } } +// func testEncodingWithChecksum () { +// + //// + //// for (uuid, expected) in checksumData { + //// print(uuid, expected) + //// +// let uuid = UUID(uuidString: "221b469c-d38d-417c-8faa-9113648240ec")! +// let data = Data(Array(uuid: uuid)) +// let actual = Base32CrockfordEncoding.encoding.encode(data: data, options: .withChecksum) +// XCTAssertEqual(actual, "123D39SMWD85Y8ZAMH2DJ84G7C") +// +// +// print(try! Base32CrockfordEncoding.encoding.decode(base32Encoded: actual, options: .withChecksum)) +// print(try! Base32CrockfordEncoding.encoding.decode(base32Encoded: "123D39SMWD85Y8ZAMH2DJ84G7C", options: .withChecksum)) + //// } +// } + func decode(value: String, withExpected expected: Data) { let actual: Data do { diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift deleted file mode 100644 index 2219ab5..0000000 --- a/Tests/LinuxMain.swift +++ /dev/null @@ -1,8 +0,0 @@ -import XCTest - -import Base32CrockfordTests - -var tests = [XCTestCaseEntry]() -tests += Base32CrockfordTests.__allTests() - -XCTMain(tests) diff --git a/bitrise.yml b/bitrise.yml deleted file mode 100644 index 17606c1..0000000 --- a/bitrise.yml +++ /dev/null @@ -1,26 +0,0 @@ ---- -format_version: '8' -default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git -project_type: other -app: - envs: - - PACKAGE_NAME: Base32Crockford -workflows: - ci: - steps: - - script@1: - inputs: - - content: |- - #!/usr/bin/env bash - # fail if any commands fails - set -e - # debug log - set -x - - brew bundle - swiftformat --lint . && swiftlint - - swift build - swift test --enable-code-coverage - xcrun llvm-cov export -format="lcov" .build/debug/${PACKAGE_NAME}PackageTests.xctest/Contents/MacOS/${PACKAGE_NAME}PackageTests -instr-profile .build/debug/codecov/default.profdata > info.lcov - bash <(curl https://codecov.io/bash) -F bitrise -F macOS -n $BITRISE_BUILD_NUMBER From 7adcd4e6ff7e94b5f3f6d325e8e6c33ec3981e3d Mon Sep 17 00:00:00 2001 From: leogdion Date: Tue, 3 Jan 2023 17:12:12 -0500 Subject: [PATCH 2/7] Updating Code and Adding Checksums #10 * working on fixing tests * Fixing encoding of integers and cleaning up old APIs * removing old protocol * implementing checksums #10 (#27) --- .gitignore | 3 +- .swiftlint.yml | 14 +- Data/python | 255 +++++ Data/values | 1000 ----------------- Scripts/base32_crockford.py | 172 +++ Scripts/generate_testdata.py | 8 +- Scripts/lint.sh | 3 - Sources/Base32Crockford/Array.swift | 1 + .../Base32CrockfordComparer.swift | 5 - .../Base32CrockfordDecodingError.swift | 31 + .../Base32CrockfordDecodingOptions.swift | 6 + .../Base32CrockfordEncoding.swift | 179 ++- .../Base32CrockfordEncodingOptions.swift | 12 + .../Base32CrockfordEncodingProtocol.swift | 31 - .../Base32CrockfordGenerator.swift | 120 -- Sources/Base32Crockford/Binary.swift | 16 +- Sources/Base32Crockford/Data.swift | 24 +- .../Base32Crockford/IdentifierDataType.swift | 66 -- Sources/Base32Crockford/String.swift | 59 +- Tests/Base32CrockfordTests/ArrayTests.swift | 20 + .../Base32CrockfordTests.swift | 249 ++-- .../Base32PatternTests.swift | 32 - Tests/Base32CrockfordTests/BinaryTests.swift | 18 + Tests/Base32CrockfordTests/Data.swift | 14 + .../EncodeDecodeTests.swift | 95 -- Tests/Base32CrockfordTests/UInt128+Data.swift | 11 + Tests/Base32CrockfordTests/UInt128.swift | 739 ++++++++++++ .../XCTestManifests.swift | 55 - 28 files changed, 1550 insertions(+), 1688 deletions(-) create mode 100644 Data/python delete mode 100644 Data/values create mode 100644 Scripts/base32_crockford.py delete mode 100644 Sources/Base32Crockford/Base32CrockfordComparer.swift create mode 100644 Sources/Base32Crockford/Base32CrockfordDecodingError.swift create mode 100644 Sources/Base32Crockford/Base32CrockfordDecodingOptions.swift create mode 100644 Sources/Base32Crockford/Base32CrockfordEncodingOptions.swift delete mode 100644 Sources/Base32Crockford/Base32CrockfordEncodingProtocol.swift delete mode 100644 Sources/Base32Crockford/Base32CrockfordGenerator.swift delete mode 100644 Sources/Base32Crockford/IdentifierDataType.swift delete mode 100644 Tests/Base32CrockfordTests/Base32PatternTests.swift create mode 100644 Tests/Base32CrockfordTests/BinaryTests.swift create mode 100644 Tests/Base32CrockfordTests/Data.swift delete mode 100644 Tests/Base32CrockfordTests/EncodeDecodeTests.swift create mode 100644 Tests/Base32CrockfordTests/UInt128+Data.swift create mode 100644 Tests/Base32CrockfordTests/UInt128.swift delete mode 100644 Tests/Base32CrockfordTests/XCTestManifests.swift diff --git a/.gitignore b/.gitignore index 3bad4d6..0108f10 100644 --- a/.gitignore +++ b/.gitignore @@ -141,4 +141,5 @@ xcuserdata **/xcshareddata/WorkspaceSettings.xcsettings # End of https://www.toptal.com/developers/gitignore/api/swift,swiftpm,swiftpackagemanager,xcode,macos -.mint \ No newline at end of file +.mint +__pycache__ \ No newline at end of file diff --git a/.swiftlint.yml b/.swiftlint.yml index b98ce23..4f922fa 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -96,17 +96,17 @@ opt_in_rules: - xct_specific_matcher - yoda_condition cyclomatic_complexity: + - 4 - 6 - - 9 type_body_length: - - 250 - - 500 + - 100 + - 200 file_length: - - 400 - - 800 + - 120 + - 200 function_body_length: - - 25 - - 40 + - 20 + - 35 function_parameter_count: 8 line_length: - 90 diff --git a/Data/python b/Data/python new file mode 100644 index 0000000..3ba5f73 --- /dev/null +++ b/Data/python @@ -0,0 +1,255 @@ +137575d2-5be4-4a4c-b128-e57617c36372 25865220360638853509113422789084406642 KENTX4PZ4996B2A75ERBW6RVJ KENTX4PZ4996B2A75ERBW6RVJ6 +6edc8bd4-b27f-40b9-92a7-b681c0e43244 147360220951765939685884637473323364932 3EVJ5X9CKZ82WS59XPG70E8CJ4 3EVJ5X9CKZ82WS59XPG70E8CJ4N +af5e32f0-bf9e-4789-b9a2-37e81e198249 233104008361604722546186878709495595593 5FBRSF1FWY8Y4VK8HQX0F1K0J9 5FBRSF1FWY8Y4VK8HQX0F1K0J93 +c9c37484-b85a-42e8-b3e7-fbf806f2661b 268189688314868218417579316992877422107 69RDT89E2T8BMB7SZVZ03F4SGV 69RDT89E2T8BMB7SZVZ03F4SGV2 +eca21cca-e4e8-456b-82e7-32e211b67a7c 314539543078723899065227238471724595836 7CM8ECNS788NNR5SSJW88VCYKW 7CM8ECNS788NNR5SSJW88VCYKWU +3d3dfc0c-129f-49eb-ba78-cd4880bc85ad 81404749974971861587869236064245548461 1X7QY0R4MZ97NVMY6D920BS1DD 1X7QY0R4MZ97NVMY6D920BS1DDM +8ea4c082-330c-43c8-b632-aea1ebb35f2f 189605816624361356370260777928167218991 4EMK084CRC8F4BCCNEM7NV6QSF 4EMK084CRC8F4BCCNEM7NV6QSF2 +4077cd81-a723-4617-a297-28712e551da0 85692643222528613331671170165641649568 20EZ6R39S38RBT55S8E4Q5A7D0 20EZ6R39S38RBT55S8E4Q5A7D0N +15131991-ab6f-43c7-a338-4724687b0b61 28012960153175498442656459614632741729 N2CCS3AVF8F3T6E274HM7P2V1 N2CCS3AVF8F3T6E274HM7P2V1Z +106d2d50-1dcd-41fe-9e96-13d19b2c10be 21834527346047322372848552292664479934 GDMPN07ED87Z9X5GKT6DJR45Y GDMPN07ED87Z9X5GKT6DJR45YP +ba0edec5-2698-447f-9935-89b8410eeadc 247313617686838376500305912607808613084 5T1VFCA9MR8HZSJDC9Q10GXTPW 5T1VFCA9MR8HZSJDC9Q10GXTPWR +eac43d76-7156-4989-8608-b92e0b7a5a68 312058287808928260909684737672184748648 7ARGYQCWAP964RC25S5R5QMPK8 7ARGYQCWAP964RC25S5R5QMPK8M +d4f7495a-5558-44bd-83ee-295a69b06746 283080320203308896259387925548732802886 6MYX4NMNAR8JYR7VH9B9MV0ST6 6MYX4NMNAR8JYR7VH9B9MV0ST66 +125d75d7-b098-48fc-b4a2-035dfa137148 24411377662604255619114446178609230152 JBNTXFC4R93YB98G3BQX16WA8 JBNTXFC4R93YB98G3BQX16WA8K +9bee481b-4b41-43bf-a1b5-c2c9dc4c4858 207267568494935375929498726876186429528 4VXS41PJT18EZT3DE2S7E4RJ2R 4VXS41PJT18EZT3DE2S7E4RJ2RS +d373e204-94b9-4cd8-9890-c93d51ac6198 281068805436859625320088161788478906776 6KEFH0955S9KC9H4697N8TRRCR 6KEFH0955S9KC9H4697N8TRRCR5 +1a457e11-80cb-4da3-b7cb-c9cbff4146b6 34920753343995398495631048761701123766 T8NZ1306B9PHVFJY9SFZM2HNP T8NZ1306B9PHVFJY9SFZM2HNPC +041ec482-123e-45f5-8af3-91ff3fc32465 5476666546485164852590565185248502885 43V2844HY8QTRNWWHZWZW6935 43V2844HY8QTRNWWHZWZW6935V +3d366339-3a61-49d4-aba8-96af9da819be 81365304265864518797485019272852281790 1X6SHKJEK197AAQA4PNYETG6DY 1X6SHKJEK197AAQA4PNYETG6DYA +e3907245-1b95-4d73-a39f-2585d7bbaf8e 302484763460779539070024047744364818318 73J1S4A6WN9NST77S5GQBVQBWE 73J1S4A6WN9NST77S5GQBVQBWEE +9171573c-dea2-4191-ac04-909f88490c8b 193326558326054333448373752336669543563 4HE5BKSQN2868TR14GKY44J34B 4HE5BKSQN2868TR14GKY44J34B= +2b43113f-2a0f-462e-b311-85f697295eec 57505037513627552189923704634056728300 1B8C8KYAGF8RQB64C5YTBJJQQC 1B8C8KYAGF8RQB64C5YTBJJQQCG +135cfc31-b85f-43a1-abfe-bc4c33a3c345 25738138337359098287761097409056457541 KBKY33E2Z8EGTQZNW9GST7GT5 KBKY33E2Z8EGTQZNW9GST7GT5B +8e82087c-073a-41ed-a96c-fa99746bb32f 189425546078873386630386024456261448495 4EG847R1ST87PTJV7TK5T6QCSF 4EG847R1ST87PTJV7TK5T6QCSFH +4f4b1113-3b9d-4bcb-95bc-caed987a0adf 105398780256146590747064006318153665247 2F9C8H6EWX9F5SBF6AXPC7M2PZ 2F9C8H6EWX9F5SBF6AXPC7M2PZ= +5793f610-a6f2-4e7e-997b-83e26d98c8e6 116411094063572841501803906832090581222 2QJFV119QJ9SZ9JYW3W9PSHJ76 2QJFV119QJ9SZ9JYW3W9PSHJ761 +35ea5649-9c57-4f2b-a0a6-a4f95d75afcc 71665831360764680464105580742086864844 1NX9B4K72Q9WNT19N4Z5EQBBYC 1NX9B4K72Q9WNT19N4Z5EQBBYCT +809a2c11-6012-444b-8f63-eb23e9fe4432 170941690979317562724834584016302523442 40K8P12R0J8H5RYRZB4FMZWH1J 40K8P12R0J8H5RYRZB4FMZWH1JH +099663ac-a4dc-43e0-9dbf-c9f90e487da9 12743918127661006802259735381841313193 9JSHTS96W8FG9VFY9Z474GZD9 9JSHTS96W8FG9VFY9Z474GZD9$ +32965a44-123f-4d44-b533-15dee8053cdc 67242075128052654802857553718784900316 1JJSD484HZ9N2BACRNVVM0AF6W 1JJSD484HZ9N2BACRNVVM0AF6W6 +ccfe6063-0fbd-4c71-b2ea-b1125399a91e 272483309501971846887007938004465068318 6CZSG663XX9HRV5TNH299SKA8Y 6CZSG663XX9HRV5TNH299SKA8Y1 +d387d240-6520-4da0-82fe-d27727bf41b4 281172331594435656877582507742408425908 6KGZ940S909PG85ZPJEWKVYGDM 6KGZ940S909PG85ZPJEWKVYGDMU +308a6fa5-0922-4297-89f0-1107cfb4e14e 64521745187093371338997399106576769358 1GH9QTA2928ABRKW0H0Z7V9RAE 1GH9QTA2928ABRKW0H0Z7V9RAEB +68b429e2-ebd9-485f-b589-2e1cb0e0c3d4 139175174553517653684140058178184332244 38PGMY5TYS91FVB29E3JRE1GYM 38PGMY5TYS91FVB29E3JRE1GYM= +971ccd90-313d-4ba0-bb70-2cfd2739cca1 200862980993624289727289255168763874465 4Q3K6S0C9X9EGBPW1CZMKKKK51 4Q3K6S0C9X9EGBPW1CZMKKKK51F +844e15fa-a75f-43f1-8179-cdc16c6eaa39 175863540388016082476068616561522289209 449RAZN9TZ8FRR2YEDR5P6XAHS 449RAZN9TZ8FRR2YEDR5P6XAHS* +3715bde8-545c-427c-bfe8-295e5b15b6ce 73220429784656673842754990805618112206 1Q2PYYGN2W89YBZT19BSDHBDPE 1Q2PYYGN2W89YBZT19BSDHBDPEN +9617ccf9-19cc-49b0-8302-6b6a044c933d 199507779542839387065169249888075813693 4P2Z6FJ6EC96R860KBD824S4SX 4P2Z6FJ6EC96R860KBD824S4SXT +87230ce7-d3ec-424d-af09-7d6841fec24c 179627771577220103099052367161581290060 474C6EFMZC896TY2BXD10ZXGJC 474C6EFMZC896TY2BXD10ZXGJCH +38b2c169-244e-4abd-94ba-f484986c2a21 75364919440021166490408765812095330849 1RPB0PJ92E9AYS9EQMGJC6RAH1 1RPB0PJ92E9AYS9EQMGJC6RAH1U +3b902b11-6c11-4487-b593-aeb0798b2eb0 79173016022876027868108971470328901296 1VJ0NH2V0H8J3VB4XEP1WRPBNG 1VJ0NH2V0H8J3VB4XEP1WRPBNGX +561f4ad2-09df-42a6-b3d9-4db81629c22a 114476086379397413242807147723768840746 2P3X5D42EZ8AKB7PADQ0B2KGHA 2P3X5D42EZ8AKB7PADQ0B2KGHA~ +5cd24004-6ba3-4d97-883c-e70a75065e40 123380656376944175250081755094898335296 2WT9008TX39PBRGF7719TGCQJ0 2WT9008TX39PBRGF7719TGCQJ0= +33b9f695-dde1-465a-a9c9-69bdc0e40110 68756204050286893283708639745012924688 1KQ7V9BQF18SDAKJB9QQ0E808G 1KQ7V9BQF18SDAKJB9QQ0E808G3 +308d1ede-58b5-4439-8af2-2e1c640b51ff 64535683743123378931827179946964111871 1GHMFDWP5N8GWRNWHE3HJ0PMFZ 1GHMFDWP5N8GWRNWHE3HJ0PMFZX +1cd2c8c6-9fdd-4921-ab1b-26c7537f5fce 38312838440842500923275628822709362638 WTB4CD7YX94GTP6S6RX9QYQYE WTB4CD7YX94GTP6S6RX9QYQYE1 +9c1032bc-34ae-4276-9242-40ea03c30b6f 207443673123862056147032670890056944495 4W20SBRD5E89V94GJ0X81W62VF 4W20SBRD5E89V94GJ0X81W62VFB +f2d9b767-e144-4be1-8a91-51b890682fa7 322803623309426598566551699530287951783 7JV6VPFRA49FGRN4AHQ286GBX7 7JV6VPFRA49FGRN4AHQ286GBX7B +8663492c-f2a7-49cb-8b7d-b63727c52583 178632073001211503580041303921516684675 46CD4JSWN7975RPZDP6WKWA9C3 46CD4JSWN7975RPZDP6WKWA9C3V +e8979e5a-af8d-430e-91dd-427a5cbd2580 309168143653321896659929159252706141568 78JYF5NBWD8C793QA2F9EBT9C0 78JYF5NBWD8C793QA2F9EBT9C0N +77a38534-1631-42bb-b570-283f77ad7a19 159027177573556124912949164416818838041 3QME2K85HH8AXVAW187XVTTYGS 3QME2K85HH8AXVAW187XVTTYGSK +b5f9ef03-e143-44c5-86a3-1eda3e1a97a8 241887996958140155371465377889824446376 5NZ7QG7RA38K2RD8RYV8Z1N5X8 5NZ7QG7RA38K2RD8RYV8Z1N5X8N +3c597e58-85d6-4da9-994c-5601ae2a5c31 80218356764613496581304416130279562289 1WB5Z5H1EP9PMSJK2P06Q2MQ1H 1WB5Z5H1EP9PMSJK2P06Q2MQ1HK +55fe2d73-7dc1-47ae-8ac5-d3bb33c30fef 114304144902375836298645388015528251375 2NZRPQ6ZE18YQ8NHEKQCSW63ZF 2NZRPQ6ZE18YQ8NHEKQCSW63ZF* +5bb2a21f-5bfe-4c0f-b31d-9fb7a04eb473 121887264692145935506313039667824342131 2VPAH1YPZY9G7V67CZPYG4XD3K 2VPAH1YPZY9G7V67CZPYG4XD3KM +eca083ad-be0a-4f4b-af47-61e2e14d4eb6 314531245263550527349759119298556415670 7CM21TVFGA9X5TYHV1WBGMTKNP 7CM21TVFGA9X5TYHV1WBGMTKNP9 +eee2aa15-3ff3-4125-b5b6-5d7a5298af5f 317533171780054514621032900234153865055 7EWAN1AFZK84JVBDJXF999HBTZ 7EWAN1AFZK84JVBDJXF999HBTZC +e374ba1b-0b6e-438f-a131-afddfdbf296c 302340836149650246246156127526084094316 73EJX1P2VE8E7T2CDFVQYVYABC 73EJX1P2VE8E7T2CDFVQYVYABC2 +6e1ac674-320a-47a2-af3c-5bea10253c9a 146354104377717704261450963872085523610 3E3B378CGA8YHAYF2VX882AF4T 3E3B378CGA8YHAYF2VX882AF4TR +a5e40dc8-97b0-4903-8dc6-a92868be5ba8 220506742552159762278600948745552944040 55WG6WH5XG941RVHN951MBWPX8 55WG6WH5XG941RVHN951MBWPX8J +5c6d2947-885b-42f4-baf5-2ebb99b60514 122855773215986134525868433851404387604 2WDMMMF22V8BTBNX9EQECVC18M 2WDMMMF22V8BTBNX9EQECVC18MG +b34b7a8a-e45e-44f6-b7e5-b821befdea8a 238323718968024672732573088502377212554 5K9DX8NS2Y8KVBFSDR46ZFVTMA 5K9DX8NS2Y8KVBFSDR46ZFVTMAR +39dec8b8-e29e-49c9-abd4-6e4701cc2e0e 76922756792372540399827227051335364110 1SVV4BHRMY974TQN3E8W0WRBGE 1SVV4BHRMY974TQN3E8W0WRBGEU +f2ace4d6-a20f-44e3-a6ca-c74f7b5b50e7 322570891433989073026955957086923215079 7JNKJDD8GF8KHTDJP79XXNPM77 7JNKJDD8GF8KHTDJP79XXNPM777 +c8675471-17f8-4701-9364-14048bb5cfb8 266382118416019636824928841083072663480 68CXA725ZR8W0S6S0M0J5VBKXR 68CXA725ZR8W0S6S0M0J5VBKXRY +7cc4149f-1f98-40a0-9b69-6c0b87752a7e 165842379916850404479877886034855668350 3WRGA9Y7WR82G9PTBC1E3QAAKY 3WRGA9Y7WR82G9PTBC1E3QAAKY9 +ac80b970-4490-4ce4-9a79-79a4262cb9d9 229295590413448294335258338489971554777 5CG2WQ0H4G9KJ9MYBSMGK2SEES 5CG2WQ0H4G9KJ9MYBSMGK2SEES$ +db490d4e-6b1b-41b3-a3e3-fe4175f190cd 291480238631838989482761971311341310157 6V946MWTRV86ST7RZY85TZ346D 6V946MWTRV86ST7RZY85TZ346DX +904044a8-14f2-434b-b760-fef8696972f5 191742530912641043416419353652709913333 4G812AG57J8D5VER7YZ1MPJWQN 4G812AG57J8D5VER7YZ1MPJWQN9 +df1a96ce-dc0b-481c-8c21-8361ecb44209 296555901528900511746612676719667331593 6Z3ABCXQ0B90E8R8C3C7PB8GG9 6Z3ABCXQ0B90E8R8C3C7PB8GG96 +cae6c213-6d36-4201-aa2d-4c7bc0802d98 269702219752613672647898657767957736856 6AWV116V9P880TMBACFF080BCR 6AWV116V9P880TMBACFF080BCRD +0d68f06f-3a85-43ea-af4d-0ce30ebca737 17824839409233682336730875505296582455 DD3R6YEM58FNAYK8CWC7BS9SQ DD3R6YEM58FNAYK8CWC7BS9SQC +3603b3a5-2a1b-48bc-84c4-70f850964511 71797532299958285019880398303115232529 1P0ESTAAGV92Y89H3GZ189CH8H 1P0ESTAAGV92Y89H3GZ189CH8HG +7e54cac8-6c59-479b-91da-66f3c3ce5cf0 167922993330921079613395817806714264816 3YAK5CGV2S8YDS3PK6YF1WWQ7G 3YAK5CGV2S8YDS3PK6YF1WWQ7GY +ac88ee5b-1487-40b6-81e5-b25bdcabfbd6 229338202077367935372747867220985248726 5CH3Q5P54782V83SDJBFEAQYYP 5CH3Q5P54782V83SDJBFEAQYYPQ +c498f46e-d261-4d73-9e44-7259a65145c6 261322873984491456087327596463344666054 64K3T6XMK19NSSWH3JB6K52HE6 64K3T6XMK19NSSWH3JB6K52HE64 +c094e629-33cf-495d-9566-65bcbe9b97bb 255984903344364834660203497137467267003 60JKK2JCYF95ESASK5QJZ9Q5XV 60JKK2JCYF95ESASK5QJZ9Q5XV= +56672700-69d0-4cd9-b174-8744526597e5 114849205260654140247916813390790891493 2PCWKG0TEG9KCV2X478H96B5Z5 2PCWKG0TEG9KCV2X478H96B5Z5W +faae5e0a-40ee-429e-99d9-925614649d1e 333212365958493476208473150449246313758 7TNSF0MG7E8AF9KPCJARA6978Y 7TNSF0MG7E8AF9KPCJARA6978Y* +0c27e158-ae83-4196-9906-041ebf6ba894 16157806095150042356608531674751019156 C4ZGNHBM386B9J1G43TZPQA4M C4ZGNHBM386B9J1G43TZPQA4M* +749d1adc-162a-4905-8d53-2d4533c6e4b8 155006182897545447211770108872000726200 3MKMDDR5HA942RTMSD8MSWDS5R 3MKMDDR5HA942RTMSD8MSWDS5RD +49f1e8e1-f504-453a-80e3-b235ac864b4f 98289710656399355109462016760603888463 29Y7ME3X848MX81RXJ6PP8CJTF 29Y7ME3X848MX81RXJ6PP8CJTFU +b86444fe-c236-4498-931f-11f9084de4fd 245098580298190028148911405354661569789 5RCH2FXGHP8JC967RHZ444VS7X 5RCH2FXGHP8JC967RHZ444VS7XP +d36820f6-38f3-4684-bdb5-99384b8cf1ff 281007774528765429157006773715129397759 6KD0GFCE7K8T2BVDCS715RSWFZ 6KD0GFCE7K8T2BVDCS715RSWFZP +2244a19c-9f11-4dcc-9c8c-c9e0d4795311 45550105919836083798807333657002988305 128JGSS7RH9Q69S369W3A7JMRH 128JGSS7RH9Q69S369W3A7JMRHV +d4cced88-5e06-41cd-adbe-e95f2f2b0837 282860381400748592503715452732859680823 6MSKPRGQG6876TVFQ9BWQJP21Q 6MSKPRGQG6876TVFQ9BWQJP21Q= +d3f317df-388e-453c-a763-8ef1a66b2930 281729319428045491944337439489281173808 6KYCBXYE4E8MYAERWEY6K6PA9G 6KYCBXYE4E8MYAERWEY6K6PA9GX +b99b7d5c-8f6e-4e92-95c3-4e0c841a45c7 246714527867863451833960392673902347719 5SKDYNS3VE9T99BGTE1J21MHE7 5SKDYNS3VE9T99BGTE1J21MHE7N +8c6a607d-0722-4295-a8f6-c65aa6277fd9 186644259893942991762104305166863269849 4CD9G7T1S28AATHXP6BAK2EZYS 4CD9G7T1S28AATHXP6BAK2EZYSJ +3c184acc-6b78-4aa9-aae7-512629713803 79879811965815931105021456303873144835 1W315CRTVR9AMTNSTH4RMQ2E03 1W315CRTVR9AMTNSTH4RMQ2E032 +03ed8349-e2e1-461e-a255-fcd0c57487c3 5220921192357395336586263992814831555 3XP1MKRQ18RFA4NFWT32Q91Y3 3XP1MKRQ18RFA4NFWT32Q91Y3X +1c2b4c5d-bb3c-4e49-9fcf-1b856dce3bfd 37443201536190235471350865067928075261 W5D65VESW9S4SZKRVGNPWWEZX W5D65VESW9S4SZKRVGNPWWEZX$ +40496f03-3e68-4b12-97fe-21e368c0e4d2 85451881005372304575579601865178932434 2095QG6FK89C99FZH1WDMC1S6J 2095QG6FK89C99FZH1WDMC1S6J9 +207d9df3-4d10-4a84-a389-0852a1f8fac7 43187536587035466136210046412754844359 10FPEZ6K8G9A2A7288AAGZHYP7 10FPEZ6K8G9A2A7288AAGZHYP7V +d187565a-b7f7-4e09-8203-4634e26bd81a 278511362669644889488919865806914574362 6HGXB5NDZQ9R4R40T66KH6QP0T 6HGXB5NDZQ9R4R40T66KH6QP0TY +c2d57b33-3bb1-45c8-ba7a-205140a7ad33 258978689208633060006763297332837985587 62TNXK6EXH8Q4BMYH0A50AFB9K 62TNXK6EXH8Q4BMYH0A50AFB9KA +8d5f3c1a-d628-4d14-b0af-edcdf41dffc4 187915634678020904313683555369407610820 4DBWY1NNH89MAB1BZDSQT1VZY4 4DBWY1NNH89MAB1BZDSQT1VZY4W +a3acf894-8975-4751-b4c7-475f5b8929f9 217562280178500262822467097623240714745 53NKW992BN8X8V9HT7BXDRJAFS 53NKW992BN8X8V9HT7BXDRJAFS9 +a69647c1-f8e4-466a-9966-7bd474228f35 221432147248221733622556845936539242293 56JS3W3Y748SN9JSKVTHT253SN 56JS3W3Y748SN9JSKVTHT253SN4 +592a5289-0c81-461d-ad55-8f6ca971893d 118521042108631842001711977040324954429 2S5998J3418RETTNCFDJMQ329X 2S5998J3418RETTNCFDJMQ329X0 +057499ca-9f77-4f31-8419-2173d8cd62f3 7251565676625158670888919882552664819 5EJCWN7VQ9WRR8691EFCCTRQK 5EJCWN7VQ9WRR8691EFCCTRQKH +2b4b45cd-25a5-4ca4-8edb-df9be13b866f 57547641822828242055490924625518102127 1B9D2WT9D59JJ8XPYZKFGKQ1KF 1B9D2WT9D59JJ8XPYZKFGKQ1KFN +d185afb2-10ff-48db-bc77-761472be363e 278502790130786496397813937357725775422 6HGPQV447Z93DVRXVP2HSBWDHY 6HGPQV447Z93DVRXVP2HSBWDHY8 +d4cec27d-5b73-42d1-824c-8ec640efdc30 282869892978546240158133324699227118640 6MSV17TPVK8B8R4K4ERS0EZQ1G 6MSV17TPVK8B8R4K4ERS0EZQ1G1 +58d602c8-2deb-4d1b-ae10-9a38b3dd153c 118083271581462047226642845554479797564 2RTR1CGBFB9MDTW44T72SXT59W 2RTR1CGBFB9MDTW44T72SXT59W8 +6a51afb9-ecc2-402e-b6c0-00ba5d1f54bb 141322307750906404766957495604721177787 3AA6QVKV6280QBDG00Q9EHYN5V 3AA6QVKV6280QBDG00Q9EHYN5VB +b673c231-d761-45a1-886a-1baadafe9158 242520548107886137407923302135140094296 5PEF133NV18PGRGTGVNBDFX4AR 5PEF133NV18PGRGTGVNBDFX4AR1 +0b3c9bca-de4f-4906-aaea-87a3c5402849 14936205611525080325803293319182166089 B7JDWNQJF943ANTM7MF2M0A29 B7JDWNQJF943ANTM7MF2M0A29$ +1ae1a891-e747-4190-bc4b-67d0f74f1428 35731613688052307586522454580978979880 TW6M93ST7868BRJV7T3VMY518 TW6M93ST7868BRJV7T3VMY5185 +46eed1cf-9b50-4acd-9e1c-250a12842991 94285981829179447566666184518147516817 26XV8WZ6TG9B6SW71518988ACH 26XV8WZ6TG9B6SW71518988ACHD +b65b69b5-387d-481f-adee-dffdbc70a2f0 242394138257769773562144965518599168752 5PBDMVAE3X90FTVVPZZPY718QG 5PBDMVAE3X90FTVVPZZPY718QGW +0bb9a354-0f15-4221-b578-c755bcc251c7 15585395565062039021242408505304043975 BQ6HN83RN88GVAY67APYC4ME7 BQ6HN83RN88GVAY67APYC4ME7F +c6927a2c-4adb-4aa4-a2e8-d01f3c3442d2 263947696469937221773541586658485617362 66J9X2RJPV9AJA5T6G3WY38GPJ 66J9X2RJPV9AJA5T6G3WY38GPJ7 +d1619418-de3a-45ba-9baf-cc463b6b737a 278315307681198655669906359444122071930 6HC6A1HQHT8PX9QBYC8RXPPWVT 6HC6A1HQHT8PX9QBYC8RXPPWVTF +c2399b83-ade4-4db5-82c6-d028bc18277c 258169346309404928652176883352381958012 6276DR7BF49PTR5HPG52Y1G9VW 6276DR7BF49PTR5HPG52Y1G9VWK +6362d43d-883c-43d6-855b-b0c9e31fcf46 132106721420759789289974217379667758918 33CBA3V21W8FB8APXGS7HHZKT6 33CBA3V21W8FB8APXGS7HHZKT6S +560c013e-e19a-455f-97d4-837a4c5ec88c 114375940464181491930020940207024425100 2P1G0KXRCT8NFSFN43F965XJ4C 2P1G0KXRCT8NFSFN43F965XJ4CZ +4f09c03b-6730-4dcb-8a69-650f92a1f3fe 105059641267776008698196734292556641278 2F1703PSSG9Q5RMTB51Y9A3WZY 2F1703PSSG9Q5RMTB51Y9A3WZY$ +2980c7cc-6d5a-4505-b4ee-380f579251bf 55167014220973283660697614688227316159 19G33WRVAT8M2V9VHR1XBS4MDZ 19G33WRVAT8M2V9VHR1XBS4MDZ4 +51d4460e-2cf0-4c8b-a212-3194c91f11f6 108769655484361944083562509385015955958 2HTH30WB7G9J5T44HHJK4HY4FP 2HTH30WB7G9J5T44HHJK4HY4FPH +541cf0d2-3fb8-44b2-9e63-3e289b39f570 111805420393911235279339434524098033008 2M3KRD4FXR8JS9WRSY52DKKXBG 2M3KRD4FXR8JS9WRSY52DKKXBG2 +39031498-2916-4f7a-a484-9697125800f7 75781990353904441540006746137508184311 1S0CA9GA8P9XXA914PJW95G07Q 1S0CA9GA8P9XXA914PJW95G07QK +7190f845-de77-40d9-adeb-0da609fd3218 150955489844499268514946261331767800344 3HJ3W4BQKQ83CTVTRDMR4ZTCGR 3HJ3W4BQKQ83CTVTRDMR4ZTCGRV +1ba1f2a9-215e-4526-ae4a-9d4a17030f42 36730037423427355339482627749550690114 VM7SAJ8AY8MKAWJMX98BG63T2 VM7SAJ8AY8MKAWJMX98BG63T29 +884e8994-631d-4de8-a2b9-884b7e8aec47 181182797028272470394584528605516852295 489T4S8RRX9QMA5EC89DZ8NV27 489T4S8RRX9QMA5EC89DZ8NV276 +9e31db05-4e70-4ba3-bfa8-af34fec633ea 210276888148204514415721776225804760042 4Y67DGAKKG9EHVZA5F6KZCCCZA 4Y67DGAKKG9EHVZA5F6KZCCCZAT +6c9b8308-3ef6-4743-bea8-c5b91b63ae30 144364087206812993937212805062953512496 3CKE1GGFQP8X1VXA65Q4DP7BHG 3CKE1GGFQP8X1VXA65Q4DP7BHG$ +96091334-b23e-484a-9b02-5d9b6bfab691 199431319580274741043342393629824693905 4P149K9CHY9159P0JXKDNZNDMH 4P149K9CHY9159P0JXKDNZNDMH9 +08d6261d-3679-4109-8e23-cca6d38c34be 11745748540046209457488224931078223038 8TRK1TDKS844RW8YCMV9RRD5Y 8TRK1TDKS844RW8YCMV9RRD5Y2 +9ce994c4-4936-404d-95ea-e79f41cc7ef7 208572389858484675627348698228910227191 4WX6AC8J9P816SBTQ7KX0WRZQQ 4WX6AC8J9P816SBTQ7KX0WRZQQE +67db7fdf-d5fb-4d26-bd25-fdeceb2182bd 138050190177989478412326236932368597693 37VDZXZNFV9MKBT9FXXKNJ30NX 37VDZXZNFV9MKBT9FXXKNJ30NXU +d9dee886-ae64-4af0-8864-ec8e604ca470 289599881177494938435661550865804469360 6SVVM8DBK49BR8GS7CHSG4S93G 6SVVM8DBK49BR8GS7CHSG4S93GB +f644c1b0-e10b-4906-8ac3-c12625602222 327347091668327550543243012140213215778 7P8K0V1R8B9438NGY14RJP08H2 7P8K0V1R8B9438NGY14RJP08H2E +3be25be9-36ed-46e4-99af-4682c74d0f6e 79599775017773746403158645265268543342 1VW9DYJDQD8VJ9KBT6GB3MT3VE 1VW9DYJDQD8VJ9KBT6GB3MT3VET +3f17b9a1-a23f-4c61-8494-648a60249dc7 83864551613919936000416696284622396871 1Z2YWT38HZ9HGR9534H9G297E7 1Z2YWT38HZ9HGR9534H9G297E7U +cf19b6e5-5c40-4186-8cb6-507e62239134 275283712119288338344195346316254220596 6F36VEAQ208638SDJGFSH2749M 6F36VEAQ208638SDJGFSH2749MZ +cfb179d1-fbbb-4f00-8493-5b5f800510dc 276071702479593437184636588304267284700 6FP5WX3YXV9W0894TVBY00A46W 6FP5WX3YXV9W0894TVBY00A46W8 +79e84a10-9031-4611-8142-24e1756ab479 162042702571741580751017807419867313273 3SX151141H8R8R2GH4W5TPND3S 3SX151141H8R8R2GH4W5TPND3SX +66064ffd-0ec7-4c68-8111-65dade9cfd3b 135614031710870159796016956748993461563 360S7ZT3P79HM824B5VBF9SZ9V 360S7ZT3P79HM824B5VBF9SZ9VP +412ab5bb-91e2-4838-b5e0-a3e0dbf117ac 86621582171031530334761635211824404396 215ATVQ4F290WBBR53W3DZ25XC 215ATVQ4F290WBBR53W3DZ25XCU +a89bdccc-2478-43c1-a729-f540055179a8 224119587608883572630423515337300474280 58KFECR93R8F0TEAFN802N2YD8 58KFECR93R8F0TEAFN802N2YD8* +b3499d3e-7ace-4ed7-9e08-47dfd4a7c02f 238314038204633415102377462899161940015 5K96EKWYPE9VBSW227VZAAFG1F 5K96EKWYPE9VBSW227VZAAFG1FS +b42c4e7c-5543-4265-b737-e2abd2c1d7bb 239491092181689163554651344071821481915 5M5H77RNA389JVEDZ2NF9C3NXV 5M5H77RNA389JVEDZ2NF9C3NXVZ +9a618690-881b-4591-9fdb-cf4474a3fcdd 205207493440020144947471929221652413661 4TC639120V8P8SZPYF8HTA7Z6X 4TC639120V8P8SZPYF8HTA7Z6XZ +c634ca5a-d314-482f-bf15-408939e011f3 263461246844657574272702227065451057651 666K55NMRM90QVY5A0H4WY04FK 666K55NMRM90QVY5A0H4WY04FKZ +2ab78ae8-9ec4-48c1-bd7f-5b9cb582901b 56780583550673271954505898123881254939 1APY5EH7P4930VTZTVKJTR540V 1APY5EH7P4930VTZTVKJTR540VY +c2302664-5dcb-4685-a361-2f2d7c0e8676 258120240114892392145028269433863177846 6260K68QEB8T2T6R9F5NY0X1KP 6260K68QEB8T2T6R9F5NY0X1KP4 +5e2cd374-0228-49be-aa61-1c5b27249854 125180181445118521928074843256323872852 2Y5K9Q80H896ZAMR8WBCKJ962M 2Y5K9Q80H896ZAMR8WBCKJ962MM +d86393b9-c0e7-4d42-bfe0-e68b815c7b59 287630280709659327020461445913616612185 6RCE9VKG779N1BZR76HE0NRYTS 6RCE9VKG779N1BZR76HE0NRYTS2 +b72faf7b-68c9-4a89-88c0-225aabd038e4 243496320380165157040115351954616498404 5Q5YQQPT699A4RHG12BANX0E74 5Q5YQQPT699A4RHG12BANX0E743 +885c9aa1-57f2-4da6-80dc-1052ca70ff85 181255835011765012607013979214991916933 48BJDA2NZJ9PK81Q0GAB571ZW5 48BJDA2NZJ9PK81Q0GAB571ZW5F +0d4d751f-cae0-4b65-a9bf-11ff8bd8f1d6 17682146364094880683124669617075188182 D9NTHZJQ09DJTKFRHZY5XHWEP D9NTHZJQ09DJTKFRHZY5XHWEPP +17d014eb-24e4-4341-9901-8fbe114dff20 31652665927855986158108245443331620640 QT0AEP9748D0SJ0CFQR8MVZS0 QT0AEP9748D0SJ0CFQR8MVZS01 +98d7241e-e4d7-4d6e-b4b1-01f968e73fdc 203159731798305674464443159961726828508 4RTWJ1XS6Q9NQB9C81Z5MEEFYW 4RTWJ1XS6Q9NQB9C81Z5MEEFYW3 +b1223dd8-9286-497e-a619-bf3b7f362976 235451147732736374039093218394340993398 5H48YXH4M695ZAC6DZ7DZKCABP 5H48YXH4M695ZAC6DZ7DZKCABP7 +8a6354ef-2216-4a49-becf-eaec6c7e8851 183949223475800323562880960282960496721 4ACDAEY8GP994VXKZAXHP7X22H 4ACDAEY8GP994VXKZAXHP7X22H7 +705cb95a-634f-4383-8765-83f839d28b1d 149354986245941923686766902774779251485 3GBJWNMRTF8E1RESC3Z0WX52RX 3GBJWNMRTF8E1RESC3Z0WX52RXV +82866c3d-467a-442b-870b-bb702051b328 173497602586049599691597431239431271208 42GSP3THKT8GNRE2XVE0G53CS8 42GSP3THKT8GNRE2XVE0G53CS89 +932fd21d-a674-458a-a5b5-c25ea3c542a4 195644814987882325406152632593019126436 4K5Z91V9KM8P5ABDE2BTHWAGN4 4K5Z91V9KM8P5ABDE2BTHWAGN4* +e9553ce0-6e98-42bf-b190-4317380965d9 310152702976772904047689363999978776025 79AMYE0VMR8AZV34232WW0JSES 79AMYE0VMR8AZV34232WW0JSES$ +7dadb4c7-0f85-4e1e-85c9-96d38bfb85af 167055433434577438120948460087890642351 3XNPTCE3W59RF8BJCPTE5ZQ1DF 3XNPTCE3W59RF8BJCPTE5ZQ1DFF +5aa54a85-5d2e-4bb7-9058-a598b029400b 120488760066795033805547429817794248715 2TMN58AQ9E9EVS0P55K2R2JG0B 2TMN58AQ9E9EVS0P55K2R2JG0BZ +61877563-24c3-4f8c-92cf-407543cf6d25 129638456563928334107598262547468217637 31GXTP69639Y695KT0EN1WYV95 31GXTP69639Y695KT0EN1WYV951 +64ba270f-f122-4c4d-a520-c971f74eda99 133889359071203396535360758621343242905 34Q8KGZW929H6TA869E7VMXPMS 34Q8KGZW929H6TA869E7VMXPMS6 +54784c6a-95ee-401e-975c-3be4d06c023d 112279777176673510282528038633484321341 2MF166N5FE80F9EQ1VWK86R0HX 2MF166N5FE80F9EQ1VWK86R0HXB +8158958f-66ef-410c-af63-2c902b776bf2 171930367020320107535080641241546189810 41B2ARYSQF846AYRSCJ0NQETZJ 41B2ARYSQF846AYRSCJ0NQETZJU +c78fe8e9-f324-40b5-a9d0-3a1f35e8b369 265263593666407336368511384471328043881 67HZMEKWS482TTKM1T3WTYHCV9 67HZMEKWS482TTKM1T3WTYHCV93 +f022ea34-7682-4c08-82ad-b8eea4770d15 319196007321958449441220146179513715989 7G4BN38XM29G485BDRXTJ7E38N 7G4BN38XM29G485BDRXTJ7E38NE +e04d2093-42cb-4e52-accf-839083ea4af1 298147538618247336167422360569374198513 709MG96GPB9S9ASKW3J21YMJQH 709MG96GPB9S9ASKW3J21YMJQHE +87137778-1a7f-487b-9521-1993626130fa 179546856193598626951985209195725140218 472DVQG6KZ91XSA88SJDH62C7T 472DVQG6KZ91XSA88SJDH62C7TM +efe098ad-dfc1-47b3-ac54-a21324df331e 318851662190887383510186337220884902686 7FW2CAVQY18YSTRN522CJDYCRY 7FW2CAVQY18YSTRN522CJDYCRYQ +111338c6-67f3-4f98-af18-c3b11591f99d 22696681102940814751428576418141960605 H2CWCCSZK9YCAY663P4AS3YCX H2CWCCSZK9YCAY663P4AS3YCXY +627a1eeb-ca68-42ab-8c4a-451bf12f8b32 130898430957211320027327564977397533490 32F8FEQJK88ANRRJJ53FRJZ2SJ 32F8FEQJK88ANRRJJ53FRJZ2SJ8 +e39be9d6-9dd4-4659-a611-841d3a002835 302544303861359001358453445898727860277 73KFMXD7EM8SCTC4C43MX00A1N 73KFMXD7EM8SCTC4C43MX00A1NR +92da63e4-6284-4262-8227-420ccd919fdf 195201234152819547298644694884586463199 4JV9HY8RM489H849T21K6S37YZ 4JV9HY8RM489H849T21K6S37YZ~ +7b7fae6d-7e44-4653-b410-be356c21d0ed 164158002996796975224390118065678962925 3VFYQ6TZJ48S9V845Y6NP23M7D 3VFYQ6TZJ48S9V845Y6NP23M7DF +140195c9-2100-48ea-84db-91c97fdfd7b6 26592790226661811412332369002645936054 M06AWJ88093N89PWHS5ZXZNXP M06AWJ88093N89PWHS5ZXZNXPA +2b5efced-e11b-412f-b613-7ef84cb72bcb 57650009737415368474410977810686880715 1BBVYEVR8V84QVC4VYZ16BEAYB 1BBVYEVR8V84QVC4VYZ16BEAYBA +df213c98-6a70-4cab-b195-8e4a6a9f6774 296590417876565983037596872691412854644 6Z44Y9GTKG9JNV35CE99N9YSVM 6Z44Y9GTKG9JNV35CE99N9YSVMT +f8b921d1-956c-41ac-ab33-80b656b47f5f 330609803797935106168689347801176637279 7RQ4GX35BC86PAPCW0PSBB8ZTZ 7RQ4GX35BC86PAPCW0PSBB8ZTZH +864e947f-bd1a-4c11-957e-69efead9140e 178524562507266881434692852896016962574 469TA7ZF8T9G8SAZK9XZNDJ50E 469TA7ZF8T9G8SAZK9XZNDJ50EJ +5b8b3501-a849-44cc-9c52-84670b901aff 121682551978782899657547665117392411391 2VHCTG3A298K69RMM4CW5S06QZ 2VHCTG3A298K69RMM4CW5S06QZF +68e55b5b-eddb-497c-96e5-c22b0b5cc5df 139430600524885496522169928207518451167 38WNDNQVEV95Y9DSE25C5NSHEZ 38WNDNQVEV95Y9DSE25C5NSHEZ5 +76161f01-4cdf-42e4-92a1-3fe75b669a4b 156963762891224482023331986185753369163 3P2RFG2K6Z8BJ9589ZWXDPD6JB 3P2RFG2K6Z8BJ9589ZWXDPD6JB= +3cad85bf-10b7-4d26-ba11-50b0c591b531 80654659801851161369353105121121252657 1WNP2VY45Q9MKBM4AGP32S3D9H 1WNP2VY45Q9MKBM4AGP32S3D9H4 +b9af71d6-ccad-4085-8420-4935959b3ba8 246818140100909412878263574501774801832 5SNXRXDK5D822R88296PASPEX8 5SNXRXDK5D822R88296PASPEX8~ +50c4d2d9-612e-4c80-851f-74ad3e6d7708 107360206375670146304648026979709056776 2GRK9DJR9E9J08A7VMNMZ6TXR8 2GRK9DJR9E9J08A7VMNMZ6TXR8$ +b300cb23-81bc-4f77-afd5-c0e0a569415e 237935931387786390133955341677738148190 5K035J70DW9XVTZNE0W2JPJGAY 5K035J70DW9XVTZNE0W2JPJGAY3 +fa0b8be4-a76d-4f90-8c75-e1f51448389d 332366951582444957452230659079194294429 7T1E5Y99VD9Y88RXF1YMA4GE4X 7T1E5Y99VD9Y88RXF1YMA4GE4XC +a44da81f-328e-4533-8309-075bbef7af31 218396608083366081057412777369545453361 549PM1YCME8MSR6287BEZFFBSH 549PM1YCME8MSR6287BEZFFBSHV +e15ba5c2-c5fb-480a-873c-729a8a61f7c0 299552160094853198530581555688004253632 71BEJW5HFV9058EF3JKA563XY0 71BEJW5HFV9058EF3JKA563XY0$ +ad311a48-d2a9-4d16-90a7-179b3c568ad4 230211398929132573101088158277422189268 5D64D4HMN99MB919RQKCY5D2PM 5D64D4HMN99MB919RQKCY5D2PMH +dd3a0f20-f590-4878-9633-475ceeb75e21 294060847133704938104784468003526237729 6X787J1XCG91W9CCT7BKQBEQH1 6X787J1XCG91W9CCT7BKQBEQH1J +8cc6ec0f-2231-4822-8119-90d62a372602 187124782035549085931132682208496002562 4CRVP0Y8HH90H826CGTRN3E9G2 4CRVP0Y8HH90H826CGTRN3E9G22 +8134b45a-4dad-46ec-a43d-b22896f90183 171744068881201067079698505202323227011 416JT5MKDD8VPA8FDJ52BFJ0C3 416JT5MKDD8VPA8FDJ52BFJ0C3~ +4b8e084f-7eeb-49dd-af50-ff864ecb44c3 100429574395361861458498405664134218947 2BHR44YZQB97ETYM7ZGS7CPH63 2BHR44YZQB97ETYM7ZGS7CPH63D +5e17aac2-4d35-4622-874f-5754a4998e28 125070317835319291666943982020857531944 2Y2YNC4K9N8RH8EKTQAJJ9K3H8 2Y2YNC4K9N8RH8EKTQAJJ9K3H8J +66806aee-0d45-4487-9f98-9c9ee3774670 136248038363781587076579803598698268272 36G1NEW3A58J3SZ64WKVHQEHKG 36G1NEW3A58J3SZ64WKVHQEHKG8 +519229be-6d8e-43d0-b85e-89deb6d1b4d9 108426389665974751753359565929485284569 2HJ8MVWVCE8F8BGQM9VTVD3D6S 2HJ8MVWVCE8F8BGQM9VTVD3D6S5 +c8ca0408-0538-4458-b789-5ccd6c24b253 266894524887486372134985630869753213523 68S820G19R8HCBF2AWSNP29CJK 68S820G19R8HCBF2AWSNP29CJKS +c834759b-d7a6-4300-aa64-1c7b650ce1a1 266117983982656077782334466978380308897 686HTSQNX68C0AMS0WFDJGSRD1 686HTSQNX68C0AMS0WFDJGSRD1B +99892d1b-57a7-4c4c-a56d-9e0deadf7452 204084142899431398929755620189516297298 4SH4PHPNX79H6AAVCY1QNDYX2J 4SH4PHPNX79H6AAVCY1QNDYX2JV +e710b10d-a6c4-4b3f-b3c7-c7655ec9a562 307138334844129899357510728930575361378 7722RGV9P49CZV7HY7CNFCK9B2 7722RGV9P49CZV7HY7CNFCK9B2F +0dd3cc97-d09f-4fcd-b27b-c3f0bd55c2e3 18379688221931917681605159119094137571 DTF69FM4Z9Z6V4YY3Y2YNBGQ3 DTF69FM4Z9Z6V4YY3Y2YNBGQ37 +2d5bc375-2da5-4639-9f9a-8c1674fe3860 60291723178142239670343574646792534112 1DBF1QABD58RWSZ6MC2STFWE30 1DBF1QABD58RWSZ6MC2STFWE30D +b5811a2c-afda-435a-b120-a039f83515a4 241260604414933351949667461283496400292 5NG4D2SBYT8DDB285077W3A5D4 5NG4D2SBYT8DDB285077W3A5D4R +d7f823f8-319f-4d28-9f76-f1f6927cd5d4 287072438262951330330358858260575737300 6QZ0HZGCCZ9MM9YXQHYT97SNEM 6QZ0HZGCCZ9MM9YXQHYT97SNEMQ +e8157cfe-2bff-449d-b7ca-705f3af7f3e8 308492468412490300860779440938816107496 782NYFWAZZ8JEVFJKGBWXFFWZ8 782NYFWAZZ8JEVFJKGBWXFFWZ8R +cad30e8d-2cc8-4e29-9436-abb2c4ebe77f 269599924924468715298490688917211965311 6ATC78TB689RMS8DNBPB2EQSVZ 6ATC78TB689RMS8DNBPB2EQSVZ9 +cac53885-3bdd-4044-a79e-e598055bec3d 269528084000494877943596211404560657469 6ARMW8AEYX812AF7Q5K02NQV1X 6ARMW8AEYX812AF7Q5K02NQV1XR +807b3318-16e5-4613-b3f5-eca935c00620 170780872285520547265403089101426525728 40FCSHG5Q58R9V7XFCN4TW01H0 40FCSHG5Q58R9V7XFCN4TW01H0D +c2fafcf0-424e-426e-bdb9-faf9931437e6 259173435599407130475998718144342669286 62ZBYF0GJE89QBVEFTZ69H8DZ6 62ZBYF0GJE89QBVEFTZ69H8DZ6B +2aa622ca-591d-440b-ba7b-c967a86bb12d 56690202735078146146627677694332743981 1AMRHCMP8X8G5VMYY9CYM6QC9D 1AMRHCMP8X8G5VMYY9CYM6QC9D2 +17c3e5d9-231b-4910-94e9-186b2ce33877 31589403665642819671747312382545967223 QRFJXJ8RV94899T8RDCPE6E3Q QRFJXJ8RV94899T8RDCPE6E3Q3 +4f6fd83d-3522-4cb4-90b8-49c5ea162667 105589742468142193186196792113819428455 2FDZC3TD929JT91E29RQN1C9K7 2FDZC3TD929JT91E29RQN1C9K7B +a63271a9-90a0-4748-b9bd-a28b68b7041e 220913767489827060007871376174796440606 5669RTK4508X4BKFD2HDMBE10Y 5669RTK4508X4BKFD2HDMBE10Y$ +f345dbf1-f40e-411a-831b-9e2a2ed48c26 323365132476195400676288776954994527270 7K8QDZ3X0E84D866WY58QD9316 7K8QDZ3X0E84D866WY58QD9316C +ea73b8e2-b792-45db-af8f-74386c9c1b82 311640215078146209430614772866680101762 7AEEWE5DWJ8QDTZ3VM71P9R6W2 7AEEWE5DWJ8QDTZ3VM71P9R6W2F +334d5326-eb4c-41a3-88cb-8c791a34331c 68192121166626335516985624638560416540 1K9N9JDTTC86HRHJWCF4D38CRW 1K9N9JDTTC86HRHJWCF4D38CRWF +b3fdfec9-1884-4a45-8bd3-6be8787e3498 239250630015196814713141219843351917720 5KZQZCJ644992RQMVBX1W7WD4R 5KZQZCJ644992RQMVBX1W7WD4RH +7ad24192-3278-4bd8-a748-5f9bf09a8aae 163257527765607694500884644419964930734 3TT90S4CKR9FCAEJ2ZKFR9N2NE 3TT90S4CKR9FCAEJ2ZKFR9N2NEP +d486591c-eef1-45cd-9104-f4698854f5de 282493910312238220451226218085225919966 6MGSCHSVQH8Q6S217MD6459XEY 6MGSCHSVQH8Q6S217MD6459XEY* +3cdc5af0-9e86-4608-a716-d7e2284cf913 80897829536656904863152993165340637459 1WVHDF17M68R4AE5PQW8M4SY8K 1WVHDF17M68R4AE5PQW8M4SY8K0 +51b98a42-7041-4e01-a9df-7ba63f546192 108630846813732428341989944363933000082 2HQ6544W219R0TKQVVMRZN8RCJ 2HQ6544W219R0TKQVVMRZN8RCJZ +b2f818d1-2a02-4666-843f-66f3f4deb1b8 237890776220149236470976818677104423352 5JZ0CD2AG28SK88FV6YFTDXCDR 5JZ0CD2AG28SK88FV6YFTDXCDR1 +5bc6db5e-6ab4-453b-b65c-79851db298b8 121992271722591060994744999315913218232 2VRVDNWTNM8MXVCQ3SGMEV565R 2VRVDNWTNM8MXVCQ3SGMEV565RZ +816c97b1-bc0d-4539-ae5d-28b773390ae3 172034256242410050204878011082248620771 41DJBV3F0D8MWTWQ98PXSKJ2Q3 41DJBV3F0D8MWTWQ98PXSKJ2Q3K +0dffd6fa-4679-4b63-ac8e-b6ec95de2b7a 18608359908636684144395371655328574330 DZZBFMHKS9DHTS3NPXJAXWAVT DZZBFMHKS9DHTS3NPXJAXWAVTG +e84e727b-200f-4f39-90fc-cafe8604be2f 308788216126747032944091739661613186607 789SS7P80F9WWS1Z6AZT309FHF 789SS7P80F9WWS1Z6AZT309FHFV +b63d798c-4329-4016-84d1-d41d3becc95c 242238690625515471466450062545454418268 5P7NWRRGS980B89MEM3MXYSJAW 5P7NWRRGS980B89MEM3MXYSJAW5 +d76a87c0-9af1-4190-86af-fa05f0e7d685 286337155945817652372074851391798498949 6QDA3W16QH8688DBZT0QREFNM5 6QDA3W16QH8688DBZT0QREFNM5N +9b8347b5-adaa-47e5-b970-1417c422d48d 206711984680256060683986856708977906829 4VGD3VBBDA8ZJVJW0M2Z225N4D 4VGD3VBBDA8ZJVJW0M2Z225N4DU +de53f7a5-fdeb-49c7-8f08-fd0a8dd536ed 295524598609912784760395442848116586221 6YAFVTBZFB973RY27X1A6XADQD 6YAFVTBZFB973RY27X1A6XADQDQ +2f00ad17-a400-439f-965c-b2ba5cc262c9 62477226531756076546664021933108126409 1F02PHF9008EFSCQ5JQ9EC4RP9 1F02PHF9008EFSCQ5JQ9EC4RP9E +a41b9709-6d54-46d9-a4bf-f16c8d09bcf4 218136646714646007952650714466816212212 543EBGJVAM8VCT9FZHDJ6GKF7M 543EBGJVAM8VCT9FZHDJ6GKF7MS +5962bdbd-e1b2-46d0-8251-a1f13a782ce3 118813985136381381575365825633923378403 2SCAYVVRDJ8V884MD1Y4X7GB73 2SCAYVVRDJ8V884MD1Y4X7GB73$ +10ec9e95-4031-46d0-92ca-0be780b8b3c1 22496246436753077282988444400389501889 GXJF9AG1H8V895JGBWY0BHCY1 GXJF9AG1H8V895JGBWY0BHCY13 +626f3b0f-76a6-4015-80ad-2b3a8cc6f066 130841886425528390046943444287017644134 32DWXGYXN680AR1B9B7A6CDW36 32DWXGYXN680AR1B9B7A6CDW36H +15ee9286-42e9-4313-a694-d0bbdef0e151 29152526432898439487098261438238613841 NXT98CGQ98C9TD56GQFFF1RAH NXT98CGQ98C9TD56GQFFF1RAHD +1e255a56-beab-4be5-9859-3ae82cfa1a76 40070787146808806280419171555002161782 Y4ND5DFNB9FJSGP9TX0PFM6KP Y4ND5DFNB9FJSGP9TX0PFM6KP* +09853e8a-de08-47f8-b813-2523a7c97631 12654895955846911765746014670637594161 9GMZ8NQG88ZWBG4S54EKWJXHH 9GMZ8NQG88ZWBG4S54EKWJXHHG +0ce5d23e-4bb3-4cb5-a883-ef44c327649f 17144036171614444446125214415278531743 CWQ93WJXK9JTTH0ZF8K1JES4Z CWQ93WJXK9JTTH0ZF8K1JES4Z$ +f9c0b9f4-1ad4-4247-bf0d-51212fe14c7e 331978463533034283826039337011992677502 7SR2WZ86PM893VY3AH44QY2K3Y 7SR2WZ86PM893VY3AH44QY2K3YC +542ef65a-fd02-48a8-90a0-22d08d19961d 111898993983025128445847540797695170077 2M5VV5NZ8292M91812T26HK5GX 2M5VV5NZ8292M91812T26HK5GXC +07ffe141-9d6e-4d20-a692-d1bbdd1a7b89 10633200410134669815255920579899456393 7ZZGM37BE9MGAD4PHQFEHMYW9 7ZZGM37BE9MGAD4PHQFEHMYW99 +09ee4fca-ed4e-4810-95a0-ef4f4af9ea40 13200437002285632052989482186579765824 9XS7WNVAE9089B87F9X5FKTJ0 9XS7WNVAE9089B87F9X5FKTJ07 +e2826eee-e5f0-4855-a4bc-625a29ab59cb 301082775631522145982654357084157204939 72G9QEXSFG91AT9F32B8MTPPEB 72G9QEXSFG91AT9F32B8MTPPEBR +117bd194-a80d-4a2e-9e9b-719e9957f108 23239779243328116948654314768077156616 HFF8S9A0D98Q9X6VHKTCNFW88 HFF8S9A0D98Q9X6VHKTCNFW887 +931e5de2-49f5-4564-bce8-49e045c04429 195554188478685466219142986628895622185 4K3SEY4JFN8NJBST29W12W0H19 4K3SEY4JFN8NJBST29W12W0H19V +00b00378-6a8d-4aa2-8411-f4e268c43629 913914634686664363990319454992217641 P01QGTMD9AH884FMW9MC8DH9 P01QGTMD9AH884FMW9MC8DH9H +886ddc51-f5d0-49a4-9962-f137ec94e2e8 181345436407998453695113521706747355880 48DQE53XEG96J9JRQH6ZP99RQ8 48DQE53XEG96J9JRQH6ZP99RQ8X +78764df3-bb2a-4de3-893e-f24fcc763b0e 160121631579404826094015161197938490126 3RES6Z7ESA9QHRJFQJ9Z67CERE 3RES6Z7ESA9QHRJFQJ9Z67CERE6 +322ecd58-ee87-4e3f-85bd-f5159f3ec131 66704410384606452403297143285511799089 1J5V6NHVM79RZRBFFN2PFKXG9H 1J5V6NHVM79RZRBFFN2PFKXG9H6 +e51f9fa2-04f9-4f1d-8db5-c5d8ddabe855 304557409976988958658364793001784109141 753YFT417S9WERVDE5V3ETQT2N 753YFT417S9WERVDE5V3ETQT2NC +84c036fc-a93d-4e42-a83d-cad83dbaa326 176456131708440213621794004470211453734 44R0VFSA9X9S1AGFEAV0YVN8S6 44R0VFSA9X9S1AGFEAV0YVN8S69 diff --git a/Data/values b/Data/values deleted file mode 100644 index db528a5..0000000 --- a/Data/values +++ /dev/null @@ -1,1000 +0,0 @@ -d8KsghrSRZaaiYJq66ny7dymfivXKTFt cgw4pwv7d1t56mjuc5gpjpaae4v3cvkt6xj7jvb6d5v5gjum8tu0 -1sjkCfPdjLwGABLdvG0riFqLcFEM71Sl 65tpmuu3ct868ujcex3m2gjcchv4ec3jd5372k338t2mudthadp0 -YjJbqWXkxqg8WAcerYkcbl8qBVwkPKWK b5n4mrkhaxc6py3hcww5egb3cnt5juv3c9p3gwa2atvppm2bax5g -eIgaLQeNUZDe7KdWSUcpdfHxl0IpW8xS cn4peraca5jmwnau8hjkejv4ax9narvgchk4gy3c614q0ntrf19g -MQS2S0t1nHHOILwu2piA94tS8RevDEL5 9n8n6cjk61u32vj8917mjk3qemt70ua174u78mtra9jqch259gug -fcw8fZs0CPvUKvytCbwDsYD2VydBNLHv cthqee36b9tk0gugetampxkteh1p4xu4edcm8cjpf5j44kjc91v0 -TPHvxcPIPIxEHk79JOD43GDKtTXQ7Kp0 ah84gxkrcd84jm29f12mgutq7554yh1m6d3m8jvmahc52dube0r0 -GpP4LkN8hoTgzTNHgF4rFVxoreFuntyZ 8xr50d2cdd73gu3fahkqmn2e91kmcd3j8tb7gvvjcn37avkmf5d0 -d7KkmJXNnzhJ0aN5hb9HiSO64Mv0U5nT cgvmpuvd99c4wvkud1530rae6nm64ea8d59mydhm9nv30n9ndta0 -hzKRrxHFKhYwKNf1HQSShpBCOB4n4gmQ d1x4pmkjf144cjv8b5vmpkk665452mukd1r44guf88u6wd37dn8g -jRiiKR2CVoPwZmCeSk0aswjDAxKUWhP8 d996juaba8t46nkfa1vnmva3cn9ppc31edvpmh21f15nanv8a0w0 -AmhvaIphv3l6Nf0bjqRsn07kf0fymLxF 85ppgxk195r6gxhkdgv4wthgc9n72mkkdrr3euv661k7jvacf130 -v1g7i7ZYMXO0NCYvpPaVrmCAQ3vwTdjn errpedv96xd5jkar9wr4wgutetr50rape9pm6gah6dv7en34d9q0 -o1BMZVo0giWykrilSzrL6qMGlHaWR6Mn dwrm4kauatqk0tv9axwppwk9dh9qmwjc6trmuhvc91gnemhp9nq0 -pxiLdOel0iHOj2Zvzo77UIXCfTzXEFJs e1w6jk349xjprc39917pmcjuetx6ydtqan4nggv6ahx5gha699tg -c8xFZm55zhtAAd9QXYJxL8OVaRvls1xR ccw7ghjudmukayk8eh0m2t1ta5c5jjkr9gw4ynk1a9v6rwthf190 -MUOQQcm7bg4kZ72ZjR40neU9pbmKSTXt 9namymahcdpkerk76hnnmdtjb9n54d1gdtjnaebgc9pmpmumb1u0 -jCDwPGroLyzAoEuqf0UI4g2dWgwhoUmk d91m8xug8xt6yk3tf90pyhbne5k30na96hkk4t2qcxvpgvundnng -HSAeBWJLuMkP8xJyYxo6d80Nz1StiPuB 919m2ta2ax54rxaddd83gy2af5cqgvtpcgw30kku659q8uagen10 -15rtOlHc3xYimsuBPb3nyh6PN4sM8tBq 64uq4x2fdh466cvrb5mpuwvn89864cvef5m3cm2e6htmue3m89rg -Kaqe9kcCz1bPGNAsap3A8wWgTr8UUUGU 9dgq2t9tddhm6yhhc984ekj1edgq0cu171vnetume8w5anan8xag -qd2zrtisO9Ylb4jqH2CkLs1QAA2IvtDP e5j34ykjehmq6kttb5p64d3ae5434gvb9htk2ma184t4jxkm8h80 -9Nuw122Ch5fKOyAAWrsWQL3qM7JWWcB3 7577axth68t46u1nct5myya185bq4wuqa5636wad6x55env388tg -v7LDG36PQwjbJiOzR0wD5TtQaQxTEurl ervmrh276cv50mbqd9h4muaff9930xu46na78mb1a5w58hbne9p0 -uivbkGLrajjBLPWrrW6Q3hLEMAVEKBlE enmqcrkb8x674rbad914rm2qe9t5edjh6dm4rhad85b4aju2dh2g -quW4EFWmrKfm0BmjktfbdxDBgOte3zLG e5uned258tbpuwjbctpk0gkdd9nq8tk2chw48gk79xu6acvu9h3g -FcXMvduXoN2DHCr3HYra75QXdtwX4wrQ 8thngkbpchungvue6924ggvj6d45jwk16wun2p34ehvngd3qe98g -iUmRW7ShFYKjYsy3Kkmwvzor7Js8g83R d5apumjq6x9pghjt9dn5jwvt6d5ppvbqetx6ywhq99tkgttr6d90 -NPZNvTWEEmnpwGW6Htv6rGPXGfIRq7IR 9t85mkkpahbmahbddtr7ehuq6t478xhpe93n0p27ct4n4w9q9590 -cIrKrFP3PXfEBjWSmTsOuhL8kwUUWNxu cd4q4jvj8t836m2rct2m4ujqadpn8wufenm4re3bexananuef1ug -ZVMj8ymXOrjKSvxUS6mwapMLgwMePFRr b9b4uuhrf5pngkvjd95n6xkran9kcvbqc5r4uk37ex6pam26a9t0 -cP1BoLstFnCKY6j8QeJ8f67cUS6Jxe3k cd832gkf9htq8hke8d5njdka718pajhrcrv3erunacv4my356dng -vVafcMXIUNMbFSUxZ1dIcOK1X3qdLF8p etb62tk39nc4jnae9nh4cmunf1d32t29cd7mpcar6drp8k2671r0 -lHNmXT2DaIgLqbwQLENCjgD1MKrGAoM0 dh44wvaragt48ra9cx672rkqa564akj3d9km8cad9dt4egbf9mr0 -3G4zkcU3JlmxYcOrY69KroL8zZ3cU0XX 6d3k8ykbcdak6jkcdnw5jrufe9ckceabe9qmre3ub8tp6n9gb1c0 -Z7FGw3gWZrbzpbSODEfxANfeTT5IuwoM b8vmchvq6dknepkjc9x70rjk9x24atkr8576ctamagumjxbqdx6g -WmAVBqLmVRry9eIWUReECaxiDkCF3Alh axpm2nj2e566unjje9wkjta9axan4ta58dgqgua4dd1mccu1dhm0 -05kOWCZiMcSAY71LbEEcNKZBFo87px6Q 60uppkuq8dd6jkb3ad0njdth9hh4ahb39t5nmgj6dww3ew3r6t8g -55m9Hu5VMPa0Bkw5buqKEzjR5SR75vaF 6mupuea8emunckagc4r44uvq6nh7awab8nx6mmhnad93edbpc530 -yTyaU8ZnHmRZVFlKIwr1jrmGyrZ5FrAF f5a7jran71d6wj3da9d5chkc9d4qewhhd9t6uhvte9d3ahkj8530 -8nd6H7FGTHvuTUOwAmQsqtwAUL7yOBJy 71q68dj86x34en28etun8nafex0pumbke5u7egan9gvqjku299wg -NguADQr24PCKF5JMsSVM4EAS7eJ23uoy 9tkqaga4a5t34d2g8d5mcdaa9ntn6njd6h2m2mtqcn534cvndxwg -leLVV4at8ruY6KEwdBlZmDHCba4dcB7z dhjmrnjp6hgq8e3jenckcju5exj44v2udn24ggv2c4u68ru26xx0 -QhhlDQNDti8rkU5ixdNrtj89sM77aKyk a5m6gv24a5748x3971t6pn9nd5w68kkjehn3gebk9mvkerabf5ng -dsx0swGbrbx4qO0b4YKmFDAVJzcLrYKy chtqgc3kex3p4wk2f0u72ktgc8u5jjvd8t242njaf9hmrwjt9dwg -694ONn6FRp83QAV7NOj0dJJhXHCww8fE 6rwk8kuedrv4cmkg70tn2gap6x74yuhgch54mu2r911qextrct2g -Rsn3j0mmaAPxaGHfiMxGcn90bza2pMVN a9tpwcva61ppura1a1w62hu8ctmmuy27cdq3jc32f9gk4w2dat70 -VLYp48E4F5bsoOOnLoIrbT40xCZjWssR at65jw1m712k8hhnc9tpykufdt66yjbjc9a38c3r8dd6mnvked90 -3XRN9cqnAIlFju2aDCmJkwOxRr4hibi7 6dc54khtcdrpwga9dh36mx9jc5246vaaddvmyy2je8u6gub2d4vg -M2BnNMxf31azDlCiG4hKPyret0mm7ypm 9mt44vje9nw6ccthc5x48v23d53k8u2ba1wq4tbm61ppudvte1pg -0gXBwnjmZVL9sE1pWr5hIC6eIAv6aUpJ 61knggkqdtn6upjp9gwq6h9he1bq4db8951kcta985v3crane150 -EW0kkGmXYLaV0eWvM7Dt90IAehTibpse 8nbk0uvb8xpngpacc5b30taqet6keh3m74r4jgb5d1a6jrkgedjg -elcYh2ZS9767KdXE1SSXeXK1YUnktQ8N cnp66pb869d56e9q6rvmpt2r8mrn6murcnc4pcatanq6px2h7170 -Ei0giJvvcNPodERXbe4a68dB8URVAG6d 8nmk0tv999v7cruea1qp8hajb1h6ad316rw68ghran95cga76tj0 -dm2lJEGabOi8uzq1v2kg4WnejMhIjE2y chpk4v2a8n3p2rjfd4w7aykh65v34uv76hbpwtba9nm4juj569wg -EuZIdWHaNVjWJaRAfbHvk9oueD6uzq5K 8nunmjb4ax462kjpd9bmmraj85k64j3pdcwpyxb58gv7aykh6n5g -oxPCKj6bWapDd4Vtr4X3LsMbFj94Tarx dxw50gubd8v64nv1e1268d2peht38p1k9htmurj6d8wk8n31e9w0 -SCJL7kqXhj32CV6pNWNzheCKBGdDDXnO ad1mmk1qddrngu3a6ct46nhpe175ekkud1jm6ju28xj48h2rdt7g -ukvumUvnrCrD9feYYMfuNLCRODtSapFz ennqcxbdanv6wwj3e923jtk5b5cmutkn9t646mjf8hu56rbg8tx0 -eeQn1t9lIHPMfPrBzfnutIoANdujdHRe cnjn2vhhegwprja8a16pcm3j89x6cvkneh4pygaechupmt28a9jg -ztU8ainFjATiyaYvZR2G8nCh5ruwtFqT f9u5ae31d5q4cuj1ahmqjratetd54cj771q46u1ne9uqex26e5a0 -5776lU4iWZezywHMY0jFeGjofZQoPwTv 6mvkedkcamu6jnuucnx7jxu89nck0uj6cn3pmvv6b98pym3qahv0 -AqUeMr6UYd3qHLab2zPMvyLgIcjKBHBw 85rnatade8v5apb46drmgk31c8t7mm2detwmrtu9cdn4pgj889vg -dJsnsOEGz815MvfV4WV1vrTOBdyXtha5 ch576vkk9x2meyhr64umuxk6aru5enhhett58ku2chwngx38c4ug -llZvjUyxb7abmhULzqJaegNZ7ZwCnlEq dhp5mxkaanwqgrhqc5h6uu2n9hx72jk1cnkmwphqb9vm6vkc8nrg -UFUdpTag2nXE0rrTxOsV44QOMWt2LRbF an35at3gahgpeckeb12k0wkjahw4ywup6gu52kudaxu34k2jc930 -Bz9nyIVzkzymFpgfkIXZ4tdBBcMtQxpi 89x3jvkt95b7muvuf5pmcw37ctnmjp2u6hu68gj2cd6q8mbre1mg -8bw0FGvcQ7SPmckykPJaPmMA1AyMCX2u 71h7ec268xv66m9qad86urvbf5nn0jk1a1pmug9h85wmugur69ug -pbdhngqK8RyLzPqsWE3HdeThPyV0v3tw e1h68u3ecxrmpe2jf567mm3hedbmacu8chjn8u2gf5b30xhkehvg -IxI9o5IdHNbIXtEz1C3FmfIOid1wqAcp 95w4jebf6n4p8j2ec94ngx25f8rm6cu6dnk4jkv9cgrqewa1cdr0 -dET7IAnfXgPyB8StQ6NouYwAxqHQsVPt ch2n8du985q6cp37a1wm4e2keh8kckkfencqegbre5452wupa1u0 -UrqbOndqp7GW8sPafxFc0vB7XErFE12t ant72rjfdtj72w1q8xbkgwugc5k7ghk361v44dur8nt4ch9h69u0 -QYZkYL7S1DD7QaGqiuWCQXdSz9EOto7w a5cnmuut9gvn6ca48gvn2ra7e5mqanu3a5c68mvu752myx3f6xvg -VjYXSr9MYCA6FjaHK5iM7LSBs3c4di8D atn5jp2ke8wmupa384v4cuk1915kauad6x656gkk6dhk8t397120 -iCU3zIpxzkd5fRFlYJaLZ03AKvYridxq d51nacvu95r7gykbcgupcmj6dhcmmracb8r36gabetcq4ub4f1rg -PNqqz8jEKI1nx11FWr1mgj4fprmskSWY a1772wbu71n4aju965q7gc9h8tbq4cbdcxn38tkge9pq6uukaxcg -E2f4LsYWlGRNsICsDOrxCXnsouPjia1f 8mt6cd2cedcnev27a9776ja3ed24ywkr8dc6wwvfen86mub165k0 -rI1VJUcak6inWlB25lAjdPKQiIfBJzOs e94k2njaanhp2utpd5q5ev2268uprgbach84pmb995k44jku9xtg -PfsJ5DVFrIOt08qJvxxyGonv8sqyV4Nh a1k76jhn8hb4cwj99xu30e3h99v7gy3t8xqpwxhredrqjnhm9tm0 -uscDT8xvUCEWixAIQtCUVIGpeHUJxiHy entp6h2m71w7cna38nbpjy21958q8gunat4mew3591ammy3991wg -zKYmvfSt9sp316ltiWHXCKR1KTKV5FTK f95njvbpct9q8ebke0tk2dkcehmnej2r8d5n4cabah5ncda6ah5g -Hbk0VeQ8uTer7GoUQU1R8IJ0DZtSg1si 91h6pc2pcn8kgxamcnt3ehvfan8nacaj714mmc24b9u56tthedmg -PoWejAczyr4thxKnBEhC800mjIt3lntv a1qnetba85hqmybj6hu6gy2bdt14au2370r30vba95u36v3eehv0 -Ojt75loqtFegZe5TBtskt2G4caylHxly 9xn78dtndhqq2x26cnknmt9nah178wvbegt4ed33c5wprj3rdhwg -orG7w30ksdjKm61vi8ooriBwMVMUtxts dxt4edvq6cr6pwv4d95pudhhetmkgvvfe9mm4xudat6nax3rehtg -fpN8GZ4VEzikajYRJZQIUA3WuX3s3Rhn ctr4we27b8u5chbud5np2ujta955mma9an0k6nvnb0tq6cujd1q0 -rBBxAdvOPR7sJUgyPFfaXrGSjWgWqMMF e9144y21chv4ym2j6xtmmnb7f584ctk1b1t4emvaaxknewad9n30 -wqpWH2lVcnOroVAKwuw6swmhaKoeVsWN exrq0nu869p5crve9xt6ynj19dvqaxtpedvpuu319dqpankkax70 -fZZ081GkiwQ0nCtcodkhNk4fmFYO6Zug ctd5mc1r653ppubqa4r6wgvmcdqp8uv89tnk8tkd8tcmydjuenkg -VPpGGypOylySSTBezyWA15pJB7ydtAYw at870hu7f5r4yybcf59n6n22cnx7jnu164uq0jj26xwp8x21b5vg -QjAmbINstqAxdbpPjU0Lc31hHdQEg7ro a5n42vb295776x3h85w68rkga1n5ac2ccctk2u28ch8mattqe9qg -a8K5ZwucZ2du6DI8OtCyqQVQGo17gON7 c4w4pdauexup6phjchukch29717q8gvte58ncma7dwrketuf9rvg -HVW0AaS8Tfe2jjBmyYxNYGK4U8RE4pmq 91b5ec21c59kgn36cmt6muj2dnwnjy2eb53mpd2n7194ad3gdnrg -RaMgy1xbcXJIFUDDsbpnKqI2VfRqjviw a9gmutvt65w64rur994mcna48htp4w3e9drmjcjpct972ukpd5vg -nfOPCCdfbIQ00ZuzhxgOKPwCvoCztYNw dtk4ym238dj6crj9a4r30pknf9m7gtuf9d87egvpdx1qmx2t9tvg -keh64zAgCYobp94HFPnebHqM0zxEQ5Vp ddjpgdhmf90pegutdxh70e9m91350vk5c9472k9gf9w4am9natr0 -eTd3XxsgITzAh7aU4kn0uc7BDjGptFKE cna68curf1tpejamf90pgdv1amu6pvhgenhkegj4d93q0x269d2g -9NInkDPooqT3QD9UBhaZbx9g62jObG1h 7574jvkb8h86yvvhagtn2h1tan16grauc9w3jttp69n4yrj765m0 -5P5NEcLmDFUDq7BDYGiitSlj3cwXM7zm 6n83akj5cd66uh26an272du28hcmeub9eh9pruhkcdvngk9qf9pg -T89ml0vSxCUN3PWR7oGda6pXhnYBEEYx agw3jvbc61v56y23an736m2qa8vpyhv4c4v70p38dtcm4ha5b5w0 -VQqGUAWnKVyu5luwJVnx0EAhJd24npGy at8q2hun85bpwjupf5ukav3nex55cvkr612m2u2acgt38vkg8xwg -olYPhx34uHlQPg13Q7BpwuUj9hjQ2zGO dxp5jm38f0tk8xa8dh8n0tth6d8kegkgexunauhtd1n52cku8x7g -sj5nW218Pt6cswZWw4E6p5OIjG8XqOJx edn3avjq68rkgm3m6thq6xuuaxvk8h9pe0umyjba8ww5gwaf99w0 -8AVKxydMQkb3nruBeBrCTWGcmY98EBNG 710ncjvrf5j4umbbc8tpwwkn89jm4wj3ahbmervdb4wkgha29t3g -jbYqPy3ri2GkUZidIVYTyP7kbUaTemDp d9h5jwagf4tq4u9j8xnnapk9ch4ncpamf583euv2angn8tbd8hr0 -I3FY1HKm3codKI61xmwuS04CsaOIcUF4 94tmcp9h915pucv3dxj4pj9p65w6uxvnacr38gvkc57mjrun8ru0 -7ed9u55da72eBQF3DoLrpCwdA7GCGGGH 6xjp8ebn6mup8r9q69jm4ma66d26yk3je11qet216x3m6hu78x40 -gCZgG58i7RoT5uJ6Oi2EswGQe9pDUD22 cx1nmtu76mw6jdujdxa3axaa6t7pjcj5edvmemb575r48na468t0 -0ihkoH1d5wIXT6PrJQDFISezuDPHJcK7 61mpguvf90rp8dbq95c58djge9552h26959paykn8h84gjk39cvg -PSg538BLcLyAinp3XuaWtp8vBREuJomu a19ped9k7114rrucf50pjvkg6dc7araqehr3gxj2a92qajkfdnug -LqLFdw5z7fxIrgy6LRUGqcdPmL4PAhuX 9hrmrhk4ewuqmdv6f14q4tvt6t654na7e5hp8m3d9gu50gb8enc0 -skJdJkzxAKgz14mw3lPNVd6ysuEo1QF1 ednmmt2addx7ggabcxx32d3dewtprm2eatj3cybken2pycah8rrg -UutaXop2jVeazsdvUPupH9f3HWv48d1y anuq8rardxr34ujpcngqmwv4etan0xbg90wpccu8axv38e3465wg -7vutSkh1idilBNLZKHHxzHzBhOtsmm1S 6xv7ax2kddm32ub4d5p44kjcb95mgj3rf947mgk89xu76vbd659g -qFOyLWuzCoONq8IlXp8cNBnlEOm2hoLi e534yyacaxuqmgvf9x772e29dhc70e339t16wv259xpk4u3f9hmg -ED7vexEZwYElS0kbDgvp01E6ViBxCvwR 8n23exk5f12nmxut8np56c3bc926exkg60rmadjpd517ggvpex90 -dZysYJcIbYVNR97sfunLCL7wuV4ix6tj chd7jwut99hmjrjtat754e9qedk7avjc8d63exvnaru6jy1pehn0 -Q952O3PKZKApyopvbDoalsTTEBgnbgUT a4wkacjf6d84ppjb85r7jvvgeth48vv1dhtn8n2589kpwrk7ana0 -RtYZIawGcY6nsYGLHLbcnF1cVdiccnvb a9u5jpj9c5vmerut6tq76pa79h44rrk3dt332rupchmp6rveeth0 -8WcYipu0x3xvcSLshA3hlktFTMHskkMV 71bp6pb9e1uk0y1kf1v66mucedm42cv8dhnq8hjm9n476uvb9nb0 -7uXnH9TXsvpvyR9DLvH5vNFSqnUi5zbb 6xungvj875a5gwvpe1v7jmht8h67cj1net74cmvhdtapjdbuc9h0 -O2WYzs5jzXRPpI4qNKcm0HRIAzfXkcBZ 9wt5epbuecupmyjra9870j9me574prvd61454ja1f9k5guv389d0 -SZMbLC6YXZL59JNE3pHB8UMCzFFh1hed add4urjc8cv5jp2u9gukjjje8mtq0j2271amugvu8t36gcb8cnj0 -0A8KWm62pKIhj60dh7GD5MrXjwBxpCI8 610kgjuqdmv34w2b95m6mdhgchm3ehu46n6q4p3aex17gw2394w0 -GhL8GU2rYGn56Jh693ksbmmaVMyL0vjr 8xm4re27amt74pa7drukcjk86rwk6uvkc9ppurap9nwmrc3pd9t0 -FLC9dXQ3HF9JN01VuTdXZKfn1moUFNiZ 8t646eb4b18k6j267554wc1hatun8t2rb95pcvhhdnqnahjed5d0 -HDJJMi1Qt5viayAGXx7Yr15k4t8v7dad 9124mjjdd4rn2x1netmp2ya18xc7gdute8rkautmegw7cdv4c5j0 -s1EEWMP3AbNRgd65YOgWrkkDTnx4QV3W ecrmahaq9n836gb29t96et1p6ncmytuqe9npph2mdtw38map6dbg -XokF2ziNgKqITHn7OAWUdpMRWtVG5sam b1qpphhjf9mmwtube54n8j3e6x7m2nunchr4umjqehb4edbkc5pg -CjgRKw8DbzxL6U2vDxSlxBg6umckgFjf 8dn6emjbeww48rkuf163cn9jet27gmvcf116edkndnhpptu6d9k0 -fVH2mWiJeinUCysQEUY63HvqD7jItvUX ctb4gckdaxmmmtb9dtam6ybka52nap9p6d47cwa46xn4jx3panc0 -6T7KQpAjJ1inq2AcZfT0uVnXwifbkdxR 6ta3ejuhe10pmjhhd5q72cj1cdd6cn1genb6wp3qd5k64uv4f190 -xwAU1TNtyGJEk0AEGjpSjG3kS9Y4sK9R f1vm2n9hah778ya7992ppc218n3pmw2kd93k6uuk75ck8wub7590 -qHsR3XBIX6cqpBKytziJmigoMGrTYf6i e5476mhkb114jp1pcdrq0gjbf5u7muaadnmpevud8xt58pb66tmg -LOI1LYTmnOwulZa3DSa2mdQLXOmm4HRO 9h7mjcacb5a6uvjfexuprpk16d256r9jdnj52k2r9xppud28a97g -ynOJULLAVObnlzb8BKlhUbkz1MS11VSI f5q4yjjn9h642njfc9q6ryk27114pv38anh6pyhh9n9k2capad4g -Pkbpi7XofOgs106bD3V6ORlGqgDrykQT a1np4w396xc6ytjfcxtk2c1pc9236nhp9x96rhvhcx274ybba5a0 -4nKLf8ESYxrqIVO3nEhCQhkzeMnl9nMT 6hq4pk36712n6pbre9rmjnjf6dq4au23a5m6pyk59nq6rebe9na0 -KeqDittLuGabJYmsWHjEvspCmaXowCIr 9djq2h39ehu4rxa7c5h4mpbdedbmguj5ettq0gvdc5c6yxu395t0 -W1daqwyX1wOmOs0MIysIc6STLSBZ0E29 awrp8rbhexwngcbq9xpmywtg9n4qjwu9ccv56n2cad15mc2568wg -HtCdatlEa3RVV8rxXkXg4Hmi9Qwobn14 91u46t31ehp4ar9ka9b5ce3jf1c6pp376h46uu9ta5vpyrke64u0 -rVfUAguycNCPVbBeiG2hOeJ3lxrOMfyE e9b6cna1cxuqjrue8d85crj2cnmmeck89xjmmcvcf1t4ykb6f52g -dznb3HsUgWBylxmHGDp5meWT3lVcqTRG chx6wrhk91tnatuq89wpry3d913m8w1ndnjnen1kdhb66wama93g -simx10771MBWLWBCiPjDjykw1HUYlzHn edmpuy1h60vkecad89bmrnu28dmn0uj4d9wppxth91anjv3u91q0 -8ABQbME8h0V5MxDlObg9IjPW54FWZTsE 710m4mb29n2kgu1garumuy24dh7p4ttt95n50ntn6h35epjmed2g -TC3BuAyqiruemXJPemNS9fWeKrJzss8Y ah1k6gkn85wq2ubjenjpup2aa1jpukjk75k5etabe957mwvk71cg -timOOTaHviuER4EtzJUkAdYSm1mSU3Fo ehmpukufahgmgxk9en2n4d25ehx4mnbb85j5jmvd65pn6n9k8tqg -hQkTNrAtSy03dNlJLR14WdicpX5xYqPR d18ppn2ee90q8mvt60tp8kkc99654c9maxj6jrvgb0uqgpbha190 -Ne5ba2HfUk61VnRJRJNqWpFw6HVvE2px 9tjkark16946cnbb6rrncvjj9994mkkhaxr4cxtp91b7ch9je1w0 -aDHWRuUh9i6rBLWpnZk8CqE4WQKHBXBr c524gnujenapgeb96tt44k2qe1q5mutr8drmad2qa55mggjr89t0 -w5k5TcVx36nCBLccE9VRqphNlxX0a2eB ewuppdamcdb7gctpdt1m4k33cd2kjnjje5r6gkkcf1c30r9jcn10 -oVt8ymCSdAL55k9PCwmhBCqtP101g0mB dxb78e3tdn1n6t219gukautta11qevb8891q2x2g64r32ttgdn10 -65Rma5BUeln006uzROPYSqC2WTBvSUbl 6run4vb16n15atbcdrr30dknf994ym2tadrm6cjqah17cmunc9p0 -rrpiDnM1uBsQ9V8MvC64ewsGZGTNNNhZ e9t70ua4dt6k2xa2ed8kjnhr9nv46dhmcnvq6huu8xa4wkjed1d0 -QidKnhYvRdpoobJAf6IC9Z18A9DIFibY a5mp8jved1cqcmk4e1qpyrja85k3cja375d32e217524jhk9c9cg -Sp17mt6YSwySGTbSMNhq0qxICIg6S9rv adr32dvdegv5jmvqf59men32ad6mwu3h61rqgja395kkcmtte9v0 -974pZsyBpJZjt9FBJ9YfaIBNnjQXvTSi 74vk8w2uedwm4w2ab9n78ea68953jpb6c54m4kked98ngxjmadmg -66QIUkdMwbZwkMwVNf2cgYwvNFmq6HhS 6rv52janddj4uxv2b9vppkbqat76cck3cxcqexje8tpq2dj8d19g -quy6BS0zk6r5ihRGvZX8cjqXB5O2qIIF e5uqjdj2acr7mutpe8upju2j8xv5mp1rcdn72p226n7k4wa99530 -4GP576gTJERXsopSRKRtDmdRryBr0Ar8 6h3n0d9q6tkn8jj5a9c76vvgad94pmkm8hpp8mkjf5174c21e8w0 -4U6QmIbtdobfLmQcrguHsZfmO4RaOr22 6hakcmbd95h78t3fc9k4rvahcdt6exa8edd6cvaf6h962kvj68t0 -jJqtITb72ElEID2WeG3JdCtlkb1LtDAZ d9572x29ahh3ecj5dh2mjh1jaxjmecuach1q8v3bc8rmrx2485d0 -bnlncwQbUY8IgNkLYdh4uoCILi5k1upq c9q6rvk3ex8p4nat714pekkb9hcp8u1menqm6jacd4uppcbne1rg -Rvv6sSq5XiphbIFznxRkA40nvRfJUrIh a9v7cdkkadrkap39e1m64ja6f9q7gmkb84u30vkpa9k4mnbj95m0 -sy9qIKZzGr7uIEYZ0FrD5zhJ2ToZYJZn edwkjwa99dd7mhvj6xumjhatb8r4cwj46nx6gjhjahqnmpaab9q0 -vnkxBlCfDX3JMunLZqk9MNgvFNcl5YeF etq6py22dh1pch2r6d54uxbe9hd72utt9n76exj69thprdatcn30 -ZvVpayI9tR2ThxzZ3IAnfH2ifElijoDg b9v5cw31f54kjx2j69a6gy3ub8tmjgbect434ub68np6jukf8hkg -mBFj5DGaqDggQDAr8vD4qZE9C57gNITF dn14cuhn8h3p2wa4cxkn2h21e8w7ch1me5d4aea36mvpekj9ah30 -cPgFgUZk1aMfIB8keSXoJlASNkrANgq4 cd86ehk7and6pcb19nk4jghrddjn6p3f99p42mueddt42kk7e4u0 -eaVvhai9Bu632XhRIycUrPHdyCrQbsU3 cngncxk8c5mkjgkn6rtk4p38a94qjrune984gt3t8dt52rkkamtg -uJQiS2rsS5dN8rzclxJxMaO99yEgUiUd en552uak69t76mtnch73gwkucdp7gjkr9ngmye9tf52penb9anj0 -vyf3qhHIpuWFeUgG3ah7E4QMy7JtzsCe etwpccvhd144jw3nax36anb78wtp2u1q8mu52kbt6x578ykk8djg -krkpNb7gXh2tQ3GbtyEVZU9aV5WeYskM ddt6pw2ec8vpep3869u52cu7c9u7jhapb9akjrap6nbpapbkdd6g -xlF0Ru5gBmi7KnCBsn4GtfT5NvExeMaT f1p4cc2jemupegkdd4vmpvj389tpwd27ehk58daeet2qgtadc5a0 -Qyk2Zf4CPmzrg2YLXVPLGIgoCGSH4LPw a5wppcjucru46m3df9t6ecjt9hc5cm2c8x4pevu38x9mgd2ca1vg -iDS26GRz4W61BQMNINZEjYiv7a5ltjd1 d5256chp8x97md2q6rrm4mad9t4mwpj5d9cpjxhqc4uprx3acgrg -gGD8JgJjYpKze31aUFAcHmWfzM6laVWk cx3m8e2acx56mpbg9dx6acthc5amcgb391pnetku9mv6rrapaxng -LOYW9qah1JbuqvFLTpmpwLK9ItqRYezW 9h7njntte5gpgcaac9uq2xj69ha70vbgex64pea9ehrn4pb5f9bg -yOA3IUnlgbkSw4myb1e3BvO55JiOpTqg f57m2cu9anq6rtv2dd9qed3df5h32t9k89v4yd9n99mmyw2me5kg -fBIehMIapjNahJ12PgqYTBquZQ4cfp5t ct14jtb89n4p2w3a9tgpgjhh6986ewatah172xaua4u66tkg6nu0 -GD515pyVT4eo2AlXB1nRVhorcqvvOrm4 8x23ac9ne1wncn1mcnqk4gbcb1132vjjatm6ywk3e5v7ckvjdmu0 -6wVGux8dV06cYQLI71U85KBxPFWnsci2 6tvnchvnf0w68nhg6thnjmac94vk2n9r6n5m4y2g8tbpwwv3d4t0 -LybZBfGW9JzkC6aNdxrvdmcEpzXXljDe 9hwp4pj2ct3neeaaf9nm6dk19tj7gwkpchpp6hbgf9c5gv3a8hjg -71B3sgfK5tj8SM79AsscfDwF5Qc9euCf 6wrm4cvkcxk4pdbmd8w56k9q750q6wv3ct27ehhna5hkjtbn8dk0 -G1PeN0IkQN0xqe2Q8bHzGkNHjYMwY3eU 8wrn0tae614ppmae61w72t9ja4w64j3u8xnmwj3ab56qep9kcnag -NEaZKk29XUqG6mrqtFyEfsG86Ptu2rsn 9t2p2pjbdct3jp2ne53kcvbje5u4cya5cttmee1pa1u7ackjedq0 -a6Ceg5uFkBRQL0oGGH4GAXfln9VFCleQ c4v46tb76numcuu2a98mrc3f8x3mgd2785c6cv3e75b4cgvccn8g -rwTcEjjoLjQ9670RVQEuAxC6zeUD1qpB e9vn8ru5d9n6yk3aa4wkcdtga9b52hbn85w46dkucnam8cbhe110 -Oy3X6VIivZvf81FaQKmGsj6pkY8YFLJO 9xwk6p1pat4pjxjuetk3gca6c58mpva7edn3cw3bb4w5jhjc997g -OWnb1NqdfMvFrhI6aCZVMfzu4O2v3JI1 9xbpwrhh9trp8tjdet374u296tgm6pjp9nk7mx9m9wt7ccua94rg -uGmWamSQDgQeAljzDGq7uAUq8KndrcXR en3punv1dn9n2h37a5jm2v3af924ew9qen0naw9r9dq68wk3b190 -oOuH73w5caHBeW6V6KHz5m2I1APTERsl dx7qaj1q6dvkarv19116antparv4pj3u6npk4j9h85858hajedp0 -UEliFgCXmXvQcxdmoU26xchtK7GCqbhH an2prua6cx1ngvaret8p6y34dnqnachpf1hpgx2b6x3m6wb2d140 -W7TEXcc8gHzB6D7gLgsHaYPG4cHR1KRz awvn8harcdhkgtu8f913ch1qcx66ewu8c5cn0htmcd454caba9x0 -LEAFjWbsrULenL22qtGpWUkyp56BBgQJ 9h2m2hkaaxh76wjn9hjpwk1j69rq8hvgaxappybg6mv44gk7a550 -l7bLCFaltpERfZy2Jd5G89B3ki00O6e1 dgvp4k238tgprx3g8n96cpkt69568da770wm4cvbd4r30ktpcmrg -JwWYgKvjfm6MvAMzdDZ0qFXoYxOjds5s 99vnepb79dv6mtkd6t6qcgadf9j48phge535gvutf17pmt3k6ntg -McEu81YuupGt86lrVRVBQlaUDkFmTOEs 9nhmax9r65cqaxbg8xu3gdkce9b54nj2a5p62na4dd36un2f8ntg -x1JkCxqcNgtHfzaZnEDCCIYfuALe88UK f0rmmuu3f1rp6kk7eh46cyk1b9q4ah238d4njtkn8566ae1ran5g -fEmRaeEsLo0gC13OGGrecgwrUagqaI4i ct2pumk1cn2q6k3f61km6c9k9x3mewk5cdkqewjnc5kq2ra96hmg -osJ0kr3HBMLMkI9UiU3W8TE2W5hJvtPk dxtmmc3be8tmggjd9h6ppj9tanmnacuq71a4acjq6nm4mxkma1ng -YEkEtyobIHxpgfen9h1KaDu5uWGXtxHk b52pphbmf5qp4ja8f1r6etk5drwpgcabc527adbnax3ngx3r91ng -9oPXeeitvEV4Du88RFTnGaJKvel1CVny 75qn0p35cnmq8xj5aru48x9r7194cn3e8xgmmjvpcnp32gupdtwg -cgPNXLSoTwft5QkTDSmfL7ijK0sasX8o cdkn0kjr9h9pyn3qctu3ambbah256vb69gvpjujb61tp2wur71qg -DolRYWnugFTWUqFI7e8uch0w7pmtvlvI 8hqprmjtaxq7atu6ahbnawa694vpae3ncdm30xtqe1pq8xkcet4g -GituXmeigOs4uCXxWBlhKfeA7Yjy6OIq 8xmq8xardnjpjtufecu7agurf1bm4v389dk6ag9qb5n7jdjf95rg -ufr3NY0tDhWQPZyYGlZ4t7UMOZHwC6Q3 enk74cueb4r78h38ax8n0pktb53prphmegvnakafb947egtpa4tg -xz1OFooipHIkS9zSI08W4QIBplXqbs9s f1x32ku6dxqpjw2895nn6ebuad4k0e2q6h8mjgkgdhc72rkk75tg -xsKhvd700R6cjFAC6Gig84JBvmkeZ2SV f1tmpu3pcgvk0c2j6thpmhj18cv4eub770u4mgkpdnnpaphjadb0 -J6TCcpnIXeXr0OCP3pBEmbyJVgHI77Ky 98v58gv3e1q4jp35b1t30ku3a0tq0gj5dnh7jjjpcx44jdtq9dwg -8lCCNUzsSq1hT0TXeaInWEI4HaoefDFI 71p46gueanx76mvh65m58c2mb1jp2jbeax2mjd28c5qpatj48t4g -unrR1hFTN5v6ajw7SoEkRVXec5wYPBmF enq74mhhd1358khnerv62ukq6x9pyhbba9b5gtb36nvnjm22dn30 -A4lmQoNsFvgeJ0ke6YfAKuvr5XIbDA90 84u6rvahdx776hkpcxjmmc3bcmv5jtj19duqcwhnb14p4h2174r0 -ELnS1UwJic4ld21mH6htVg006hiQJTaJ 8n66wmthanvmmub36hp68chhdn43cu3matkk0c1pd1mn2jjmc550 -YdDrSwIOy7rDMCIZREujJivj6XfJZScz b5j48wjkex4myy9qe924ugu9b994axba99mqcuhpb1k4mpjkcdx0 -iuFYnoqo9cg9onMoizYBSqtQx8TrASL2 d5umcpbedxrpyeb3cwwpyvjddxmqmpa2adrq8mbr71a74gak9gt0 -oGQSJlNwtemLYfqtY6pSErzLGh7Ebd8T dx3n2muadh77ex35dn65jtkhehckcw2k8nt7mk27d0vmark471a0 -P1ds29nG5lW3dLiX1ZK9d7ic3gXEfwoB a0rp8wtj75q4edbcawtp8k39b0rnmjttcgvpjrtkcxc4atkqdx10 -AKmwMQbbg9Em6KtLYkfS8S0xTq0uAknn 855puxuda5h64ttt8npkcjvm9hcpptjk719k0y2me4r7agbbdtq0 -NkDOZkAaxvee4PC1kx0CLPP0PFJMJarf 9tnm8kuudd0p2y3pcnjk8m2365nqgc239h850c2g8t54ujk1e9k0 -4qr938rdK04nE1yoL6pKgoNcj8BCGoP4 6hrq4e9k71t68jtg6hq4acbtdx63cw2bcxqmwrva71146hvfa0u0 -L5iUWT6UFOZqZiC4e3or1lvwTcyYdXne 9gupjnaqagv5ahjfb9rnmua36hjk6vvj65p7cxumcdwnjt2rdtjg -4NRIzUH9FJaal9yVymNK7kjyth4UgMDI 6h754jbuan43jhjac5gprebtatwpukjb6xnpmybmd0u5atud8h4g -mgTnNbQSUgByCO37Mpm6IkFUsTKzSq0m dnkn8vjec98n6nb789wm6ktk6x6q0v9p95nmcnbkah5qmmvh61pg -8Ng91aaM0J3Jgy6A3MUvG9QJXsxpmUD6 7176ee9hc5gmuc2a6d56ey9p84tmunbp8wwn2jjredw70van8gv0 -qwytCLyfrkIrxyFCyC75kpGRpCNlMB6Q e5vqjx239hwpcwkb95t7gya68dwm6dtnddr4emkg8d76rka26t8g -stemAUNRuUg6wfuLwOfUz8LxnFMUn1mq edu6ava1an754xancwv7etkn9hvmytjnf8w4ry3e8t6navhhdnrg -zNWrNVfa30rQXffAWfzLQj61mZ6ZNt23 f975ewjeatk62ctge98ngtk685bpcyjca5n3ccbdb8v5mkkm68tg -6o4RndfaZpRywMDKavrz94YY99U1DzQ1 6tqk8mkechk62pkga9wqeka49dgqcwku74u5jp9t75ak2h3ua4rg -S83tS3nqWKTrdfZ4cPIGDZ1JlPlwzMtc acw36x2k6dq72nubaht68tju6hhn0ja78hd32jkca1p7eyjdehhg -Pie9xgguQSGR9E96eusV1cxL2vcssiTY a1mpaebrcxkqamak8x93jh9t6tjqawup65hqgk1jethq6wv9ahcg -edlH5peSqieE5XwkIKidQPNusRNcD1FZ cnj6rj1ne1jn6wb9cn2kap3qdd4mpub4a584wxbka9766h1h8td0 -UyfgLyXkWlhjlyHwEmxY0nF1GwZ22A5s anwpctucf5c6pnvcd1n6rya8ex2puy2t61q4cca7exd34cj16ntg -YEVbQkbnLCZ3mTJ2CnKymwy1Usprtqr0 b52ncrjhddh6wk23b8tpun2a691pwjvtdnvqjcanedr74x3he8r0 -HjoubYIsGJWJ67k4zItw8mF3uwNg1oOu 91n6yxb2b54q6huaax53cdvb6hx4jx3q71pmccvnex76ecbf9xug -YwsmaFaKMB2HqPjvOHcx85OAdm3IIZwB b5vq6vb18tgmpka269472m3aet7mgrvr70umygb4dmtmjjauex10 -WIg2ShBKUsTLuHLeVUKOJWCT59cfVizt ax4pecjkd114pnbkah67aj2ccnb5ajuf99bm6n1n75hpcnk9f9u0 -8b30HW5hLeZnCHUCDqFBp62XiYUCeGOL 71h36c28awupgk35b9q46j2n8d272hj2e0v34p39b5am6ta79x60 -szOgOtIvn4Gc3aWsN1UcyKItxgKgtxey edx4ytufeh4qcvhm8xhk6raqed732nb3f55mjx3rcx5pex3rcnwg -GDLyhdRXdYSvG6DroO6xXN99l95rN3e2 8x24ryb8ch95gt2tadv4edj4e9qmydkrb173jebc74uq4khkcmt0 -8DP1WSN0y8WCNrxwfFkXsgTbSvJP0M6l 71250caqad730y9rax1mwwkrexk4cuuredkn8rjket550c2d6tp0 -VS0Zauf1lveajrNG6QSOxj6b4wHiCJzs at9k0pk1enk32v3pcngpmwje8wv52muff1n3crhmex46jguaf9tg -7pdnYeZq2cxvP2Iyb1s9dvGoDBlW7fFg 6xr68vjtcnd72ck3f1v50cj9f5h32wttchv4evu489p5edv68tkg -1Bs9sB9cfcHteEkfa3WIElIVIXWhi7Q3 65176ebk88wp6tk391u6ahbbctgk6nu98np4jnj9b1bpgu9qa4tg -OkGoWCxbZPJUnhFS7kqbrx2ZJUMDYQrj 9xnmevuq8dw64pjg99apwu26acvppwb2e9w34pjaan6m8pahe9n0 -GnwTIyW732cgK0F2SuhMNMTf2O6x1eEm 8xq7en29f5bkectjcdkmpc26699qau2d9t6n8thj9wv7gcb58npg -GuHKvNwWEs4fqmRIB6yanPcM5Sh3vhMI 8xumgjvp9tvnehbk6hk72vaj9513cyb1dt866k9nadm36xk89n4g -DnK6xfYoIncLzAAq5iBZpTJAvMoUkHti 8hq4pdkrctcpyjbecd67mga1e4upjgjue1a4mgbp9nqnauu8ehmg -rkZbpuv1k9hiZz1kRDRZtmJsOBakrPpy e9nnmrkgenv32uttd1mnmyhhdd948mjuehpmmwuf89gppwjge1wg -E2Ac1cD0tYINZyL50ZJ1mBYeitn44kN7 8mt42rthcd230x2t9575myac6mr5mjhhdn15jtb9ehq38d3b9rvg -VYluUntOpcg55x4GHP1NEukHM8JEluge atcprxandtu4yw33cwukay1m8x450cae8nuppj2d7154av3ncxjg -lnS8kykMr3M6bZNOYRzxkKVUL9oGz6xC dhq56e3bf5nmuwhk9mv64pje9xcn4ykrdd5ncnac75qmeyhpf11g -5tDGa687TybzsQYfJrTTth4cQffzg5wZ 6nu48hv16rw3en3tc9x76matct574n2mehm38ruhctk7mttnexd0 -VIQuxEPRVRreYh3ihx6pALK8ZnBEGY1b at4n2xbr8n854njje9jnju1kd5m7gdkg8564pe2udt14ahut65h0 -VX0rOCIIepI31kWGiOYGPkBPBEHY4uDE atc30wjf8d4mjtbg94tk2uuq8xmmypa7a1nm4m228n45jd3n8h2g -ChbudLCui91TKAIpe2CWmONVvSzZm2xN 8dm64xb49h1qau9t65a4pga9e1jk4guqdn7mwnkpadx5mv9jf170 -SkVQlYM67wT5294xBz2aW9sBKjcnQuBH adnncmbcb56kcdvqaguk4e9mf117mck1awwq6gjbd9hpwmbn8940 -mSYPKOMcgFRZpR7UWQw3QvtF1btN9Ck7 dn9njm2b9x6p6tu6a9d70mhqanbn2xtka5v78hhhc9u4wea3dcvg -OwTCxtx1sl6XS7z0GRkaZPu6z5kXKDox 9xvn8gvrehw32wvc6tc56dvu613n4uv1b987adku6nnngju4dxw0 -9JUvpaevZrc6QNqgvxAcDOKrtd99FhAG 7555axkgc5jqcpkjccv52kkhcxv7ggb38h7mpwkmcgwkjhk8853g -BN3vLxXhTqopDhknnUMmIZtsoThIkfPj 89736xjcf1c6gn3hdxr48u3bdtq5akbd95d78wvfahm4juv6a1n0 -wUJAnIyjZ2IPQRCBSJG8XTWe5x8NVkCQ exammgbe95wpmphj95852mj3899mmhtrb1a5et9nf0w4wnkb8d8g -mT1DOnd8qAOuW9grBLxaycbPdbkTUHPC dna32h2fdtj3gwa19xuneeb7e914ry31f5hp4m34c9nn8na8a11g -oKRDVU80lCC4achd5nwiKoaF7FC89JDy dx5n4h2pamw30v238cu62rv8cgupwxv99dqp2hhq8t1kgeaa8hwg -asYlt5J0VL2YhBHJRv21SthH6osykMeX c5tnjv3m6n530njc69cpggj89997cchhadu6gj1pdxtqjuudcnc0 -EGrweIk6eiL617JLOqFIFziiaQpri9Zc 8n3q4xv595nkctb99gv32dua9h7q2hj98tx6jub1a5r74u9tb9hg -vxxCnOHhblBTgz8mDBZtYwsJCDqzJFCU etw7ggve9x46grkc89a6eyhrdn244pkmb5vq6jj38hrqmjj68dag -hJH6OrVz3MivrQ1XesFxf6M0nvD4MNUK d154gdjfe9b7mcudd5v74m9hb1jq6hkrcrv4uc3eet238kaean5g -ADwHuFZsIow6g6A1DIJxcifT77SsHSJ3 8527ej3n8td76jbfewv6edj16524jjkrcdmpcn1q6x9q6j2k98tg -j28kEbc5FeJ85RAtxK5t7w2boKeylBNG d8t3guu5c9hkahk598w3amj1ehw4pdbm6xvk4rkf9djqjv229t3g -oRW5LlE3kB7oKo3N8YYuHbxv6ZZxfvPU dx95edacdh2k6uu26xqmpvtk9rw5jpbn91h7gxhpb9d7gtkpa1ag -DhIybkbccTANxht580qf5cIiu7aEDTfx 8hm4jyb2ddh66rum8577gu3m6mw30wb66nhmjubn6xgmah2mctw0 -FGVzhTFEbJlBwD4YArGmq5DlZWgzlyOa 8t3ncyk8ah34arjadh17eh1mb50q4hvde4um8v2uaxkqmv3t9xgg -3MZxeatMEKX264sqjPk44fgMNVduVof5 6d6nmy35c5u4uhabb0t3cd3ke5n50utm6hk6ekaeatj7ankfcrug -6o4IkZqgcS1AE6UUvWzUQyvHR8z5EWvR 6tqk8jbbb9rperuk650madjnanv5eyjna5wqcj2j71x3ahaqet90 -Lbjx4VNLUgUzVfX4D0V2MnO0JdMPcOZm 9hh6my1mat74rnb7anx5ctjr6h230nhj9nq4yc2ach6n0rufb9pg -uXeYHgVA72eWqU6sL3CuRTWgJmFjYhsS enc6apa8cxb42dtjcnbq2n9ped636gvna9a5etuadn36mpb8ed9g -mNpnaQ3tMmpQDiRyAdAPP0Zzo86Y9gLP dn770vk1a4tq8kbde18m8uajf50p8gaga0r5mykf70v5jeb79h80 -QXUZJLm3k5cLrH6KwuMsaebUWZul83lD a5c5apja9hpk6utncd674j1p9dvqakbkc5jp4naqb9upre1kdh20 -wMXXK4tuDRG7VVBleKJumaRBAymQOsGN ex6ngp2b6hu7ah2j8wvncnj2dhjmpjkndngn4gj1f5pn2kvk8x70 -wWpuhlDMyCc2d2h0oFmJhAGKelTTvEE6 exbq0xb8dh24uya3cct68ck861qmcvaad10mejv5dha58xj58mv0 -3Ty9cjQ9nASyc0WkHIPox08BLZn8oMNX 6da7jeb3d98kjvj1adwp6c2qdd44jm3ff0r3ggjcb9q3gvud9tc0 -kHuPyVRzixMOnQKjZ24QYOna55i3azHM dd47am3tat97mubr9n7pwmabd9d34d2hb57pwr9n6nmk6rbu916g -z9N9T5el0TZyROptJFaOe2gTrMVsxvSt f8wmweam6njprc2mb9wn4kvgeh54crafcmt6en3j9nb76y3padu0 -JYP7MVMqFyLiyKyk9R68HiMqS4fpcGba 99cn0dudat6q2hkt9hmqjjvtdcwn4dhr91mmuwak6hk70ru7c9gg -YDbUn6KeEBXDaHuBcbBE55Zkaev46OBl b5264nbe6t5paha2b1262j3n89hp4gj56munmuv1cnv38djf89p0 -wF9Qrin3KB8p5dxqDcV8nv0aXd4qsyYM ex33jmbjd5q36ju271r3at3re5266nhrdtv30rarcgu72wvtb56g -CaJxBVlP5nM7UgVzcvZxV4vtygYYZSL7 8dgmmy22atp50dbe9mvnatupf9hqcpkraru7cx3tcxcnjpjk9gvg -LnaSQM6iymYiGHSGq9GAASKsbxanhAZ3 9hq62muh9mv6jybdb5mmej2k8xrkjhu1859mpwv2f1gpwu21b8tg -0hgKLnGu0HfLkYWbXR4S34T3v2JyxXkT 61m6ejucdt3qac28ct66ppaqc9c54d2k6cu58cvp6957jy2rdda0 -6RbDkrh4FHOcr0EGj6Lr8NuCrM5w3Qow 6t964h3be9m38hj89xhq4c258xn3ck3j7177agvj9muqecuhdxvg -dYjm8M8VIvKRJ5Rm7tJf33OBor30Fgxe chcpmv9r9mw5cjbp9d94mdajdmvq8jk66ctmygkfe8tk0hk7f1jg -ERNRy4q40N1Map32QMxFpk9WCgwdHIAi 8n94wmkt6hrk8c2e656p2w1k698muy26e1nkjnu3cxvp8j2985mg -itvpfvaERLSCP6zE1XAbVEMRA7c4MuHq d5u7cw36etgmamjcad1n0dku8mrnggb2at2mumj16xhk8kbn91rg -hMctVImvhLIPyU9CjSKwIWKr5DVZgAP0 d16p6x2p95pqcu2c9587jn9t8dn56jvq95bmpwhn8hb5mtu1a0r0 -a7PNHT578XQLBPauAqQ7wl5xuNmycjs2 c4vn0kj8agukee2ra5644m31en0q2m9qexp3ay3n9tpqjrvaect0 -Go2Dg84VjPui0zPhTsQsvCO3jByGwtu8 8xqk4h3770u5cujgenmk0yjgd1a76mbket1mycva89wmexvmemw0 -7ZbCiZ4TRi8JFtOoLmrlzjgbxBwr94Sc 6xd64gv9b8u58mk97154cx2fdx66uwkcf9n6erkr89vq4e9madhg -Bps9A3qg1qwsNxnjGb9MmkRgG1CYeihg 89r76ea16drpecbhextmwy3ed93p4eaddnnn4tu7651njtb9d1kg -9BUmXZoOyGiAIHBT9ahvDt4WeQFVzTqC 7515avarb9qmyya7d50mjj22agwp2u3p8hu38nv5a535cyjme51g -5gLftzs8ICiMwGxFDNGNuzrlurVWzjKy 6nkmrtkmf9tkgja3d56qehvr8t24whueenx74v3ne9b5eyka9dwg -ZKEl0eL8G9BEnqNF4E5wByqAbktSz8ww b95mav1gcn63ghtt892pwwae8ru4adbq89wq2gb2ddu56yhrexvg -WFka3wjVWADVxBC9Q18HP35RH4U67iXf ax36pr9kexn5cnu18hb7ggj3758k2e28a0tkamj86hakcdv9b1k0 -tx7xwLhDWnjuHmKXlGJJoj3Ezd7jfhkK ehw3ey3q9hm48nved9umgvabb1p4ejjadxn36hbucgvpmtk8dd5g -iYeOt6knRSOEi729GIifDdvLZ2Ep4PtY d5cpakvm6tnpwmjk9x2pjdtj753mjub68hj7ck2u692q0d2gehcg -qmQAhu5rMJkQQvz33UGQMVhMDPjGt8aW e5pn2gb8emuq4kaadd8n2xku6ctnahuh9nb6gka4a1n4ex1rc5bg -pMxW0X3eIUEKHOvMtJnUCunlQg7ArBOf e16qgntgb0tpajan8n5mgkvp9nu4mvjn8dupwv2hcwvm2wj29xk0 -c3F82SzqQrREEGdRpmEMDFtleTKXLllF cctmce1jadx72mbja92mahv4a9r6uhad8h378v35ah5ngk3cdh30 -NwjspQ7grypKq2Qe3rhPtF0bOi1YoyYG 9tvpmwvga4vpewkte15q2cjhcmtq4u2geh330rjfd4rnjvvtb53g -Q44wrtZVHyCyhiQdqoPsqTDW7tM3BisS a4u38xvjehd5cj3t8dwpguahchrpym3ke5a48ntqeh6k6gk9ed9g -OOM1xzyJlZ0YQ4zF5TiFzPrwgYglVYrw 9x7mucbrf9wmmv2u61cn2d3u8run8ua6f9874xv7b5kprnjte9vg -0DFwHspvXAuPS5D31FIzJqrJRr03gsdV 6124cxu8edr7cp21en856da46crmcjbu99rq4jjje8r36tvkchb0 -QHY9gz6sDORn1g3nGCnKQvMpW8glDb0F a545jeb7f8v76h2fa9q32ttkdt3m6vjba5v4uw2q71kprh326130 -GbklBMAtemwd7lDPKxdsRxvqzBlyY0lB 8xh6pv229n0q8tbdexj3ev24a15qgt3ka9w7cwbu89p7jp9gdh10 -roxqFhJtfEbIAZQOfOpy2khnTiRkaeep e9qqgwa6d1578tj5c94m2pjh9xk4yw3t69npgvjmd596prb5cnr0 -jBu8D5qPLjgXWHUJNDcBElmrjBBmVywa d917ae246nrn0k3acxc5ej2n99748ru28np6uwka8916unktexgg -9F7l5fv36TCdFQ5lnrRLI48BDmjM7Wj9 7533ev1nctv36djm8dj4cm9ndhq74mjc94u3ggj4dnn4uduqd8wg -nb5PskipG8mzzyD3NSK8omGTYrVXwRHR dth3am3kddmq0htrdnx7mya46d756jtrdxpmen2te9b5gxuj9190 -QtcClbQYehZysDZP7MKKqvZPKT6XLgCK a5u66gvcc98njtb8b9wq6h2ua0vmujube5v5mm2bagv5gk378d5g -RXlxhouENFh6lL0XhWMEWEXAA144JhY6 a9c6ry38dxumakj6d0v6rk1gb1m5eka5ax2ngga164u38jk8b4v0 -ViXSs51vJN9oA75XyC24tmF0h2gwHyhD atmngmvk6mrqcjje75qm2dtnb1wm6chmehpmcc3869kqej3td120 -hfGDCGDKdrQKCE5jeNG4wvlxaaVW5fYZ d1k4eh238x24pt3ja55m6h9nd9jmwhtmexv6ry31c5b5edb6b5d0 -AJjdwoXwI32jXw2NUKPqrZtt4wPpKKFE 8556mt3qdxc7ej9k69n5gxtj9tampm3he9d78x1mex870jub8t2g -fUkU2fRgzg9hyRon3OUk1BZ8UoXRNMRF ctappn9jct96eyk775m7jmkfdrtmynbb6515me2ndxc54kjda930 -msh945lqOPlRr0cV1E2rBcynZzMeVRXU dntpge9m6np72kugdh974c33arrmackj89hqjvjuf96panjjb1ag -tJk4cWzV0q6IRR3PIv8xzCzSUMHV0oPC eh56pd33axx5cc3h6t4n4mhka14qce3rf91qmmun9n45cc3fa11g -TlA99aYt8Q2LHhG0eCHQQwWFuXqPGCiW ahp42e9tc5cq8e2h6964gu2761jm6j2ha5vnehknb1rn0hu3d5bg -ewg1B30UdMJXnbh3YPvgiVUORfvmUVLs cnvpeca26cr5at2d99c6wrk86dcn0xk7d5b5akujctv6unap9htg -enTiIW6fmYj0kpseFv6ztc9MRYVZeF4l cnq58ua9awv6cvatd8r6pw3kcn37cdkuehhkjkajb5b5mta66hp0 -8GrW7BceRmbA82vBmsJZ4CPIlWGNUVbP 713q4ntq89hpamkdc90kgckp89pq6jju6h1n0jbcax3mwnapc980 -QWWeYzjplbrdbZu5qDGzGt4TnNEiEj0s a5bnetatf9n70v32e9j64pkn6nrm8hvu8xu38n3e9t2pjhba61tg -H7nxynk5hHt5cclh7c2L7RrYFncZP2dr 90vpwy3tdtnkau28egup6rvcd0vp6cjc6x974pa6dthnmm1jcht0 -H6zFSEtq9O4FgzvjTddeKvZUj2q2CLww 90v7mhjk8nu72eaf6h36eykpd9a68t359dv5mnba69rk4gucexvg -F5kvtdbIJPd5R1JqmjpSl7dNU8FsDE5U 8ruppxkmchh4jjjgcgun4caae5ppmw2kdgvp8kjn71376h256nag -dF1eDhixHjs5HnAcjU0ituF3l3uS7QLH ch332ta4d1mqgj3aecumgvj1cdn5ac39ehumccvc6dun6duh9h40 -MeNPfoU8xDVfK0ksTFvVxcO5DlSgu5wS 9njmwm36dxakgy24atk4pc3beda4cxjpf1hmyda4dh9pex9nex9g -PukKGYWNpjlehulrt0ldVk5laZWej7Mx a1uppju7b5bmww3adhjpgxbce9u30v34atnkav31b9bpauhq9nw0 -Drh0otuyzLAz9KGhOn8FNV1mDwhZEvB9 8ht6gc3fehuqjyjc85x3jju7d17pwe269tb32va4exm5mhbp88wg -cGWiSioldPKvBEEjTGIfWZDYldlgIJxP cd3neuakd5qprt2g9dv44ha5d9a4ejb6axd48pbcchp6ejaaf180 -XL6uOJrRBMFcFEeaDTjAJJLQcvnRjCMO b163cxaf99t54gjd8thmchb5c5258uj19954rmb3etq54uj39n7g -Qeqf51pg84AohX5jGcKPGrsiJ7z8KDR9 a5jq2thn65r6ee1m85qpgp1nd93p6jug8xt76uaa6xx3gju4a8wg -SEaVRrf1Ub7AjpBdeg3yplXbFbVTDUcl ad2p2njje9k32nb26x0pmw22chjpecvte1p5grj6c9b58h2ncdp0 -LxQDqJEeBiuJPehUMupJfJpdFnG5QLFT 9hw52h3h992pagk9en550tb8an6qaw2act570t26dt3kamac8ta0 -3a9yrwqjhHgX3voE7I41q1c7MPqJrbRv 6dgkjybjexrpmu28cxc36xkf8mvmjd1he4rp6duda1rmmwk2a9v0 -iChsN8jHyX6IBm9W8g3p4wv9hIyfb9RM d51pgwue71n4gyar6t4m4v9taww6ecvg6hvqceb895wpcrhta96g -KH8IRwW3FiPmrk4IzJEQIoqzBsJLAvdq 9d43gjajexbk6hk9a1pq4utm95x4mhah95qq2yj2ed54rgbpchrg -Ry9UIpE1fWRTXDBF8H5AdAPD3xHJ26Dr a9wkjna9e12k2tjqa9a5gh228rw4gda1ch0n0h1kf144mchp8ht0 -o76gNF67IRAdzYatzxk7ex1v9uvTecJ6 dwvkctue8rv3ejaj85j7mpb1ehx7gutqcnw32xhtenv58tb398v0 -Mh4lnpVtF3JmyYWHwYOOgLgMFH7vDzkt 9nm38v3ee1b78hhk99pqjpaq91vnjkufcx66eka690vqch3uddu0 -eLIqOkkOBdwiGlxQY3RXgGpFNoN62p3B cn64jwafddnmygk4exmmev3ra5ck6mjrcx3q0hjedx73cckg6d10 -HU5yvDM0T6sq54xWSnntUdANCst6MMgI 91akaybp8h6k0n1pedrkad3rax9pwvkmanj42kj3edu3ckadcx4g -hRaZitDUpPN37Osq0M0sNjA2WU5dvVRu d1962pk9eh25aw2g9rtkekvke4r4uc3k9tn42cjqamup8xjpa9ug -VRimbC4uoFHEMHrzBP6EigpdPkjxyRRz at96jvb28cu7avu6912muj3jf9150dj5d5kq0t2gddn7gyaja9x0 -tnp6WCqCVotERCWdtVZBzEoccorCSH7m ehq70djq8drm6nkfeh2n4guqchu5cpj2f92pyrv3dxt46mu86xpg -LoE9CV1AE6WmwyctDEtrmcuTWqiGhk6P 9hqmaea3arrm2h9paxpqeyb3eh24ax3jdnhqan2qe5mmeu3b6t80 -5R44Bq0J4P7WP9m6ESL7omoJMkw8BgV2 6n938d22e4r4md2g6xbn0ebd6t2n6k1qdxppyjjdddvkggk7art0 -ELRXhkjuZ4VkaQddxd3B2WM5mgxgxmBg 8n654p38ddn7aphmatnp2mb4chw68cu269bmudbdcxw6ey3d89kg -9RBqf4vzQab9vNnCVlQXts4z8VUbQuK8 75944wb66hv7mmb1c8wqckke8db6rmarehtk8yhratap4mbn9cw0 -Q3mMozygkmGEqzAzpHZJ1aoMKFE27v5z a4tpukbff9wpeuvd8x2q2yj1f9r4gpja65gpykab8t2k4dvp6nx0 -8Gt6yjgsgdfCUTnjUPVh8c1fm8vuk0CZ 713q8dktd9kq6tv4ct1nan3ed9an0nk871hk2tkd71v7autg8dd0 -9nHPy2cCjmmmVtyz45SGlnxAZRSGKGhA 75q4gm3t69hm6ukddnpncx3tf8u3amu7dhq7ggaua99meju7d10g -rpwqk8tu13csEQGSk31rIjOJuaZfEefH e9r7ewbb71u7ac9kcdtmama7adnk6cbj95n4yjknc5d6chb5ct40 -wsJQoilYTmebIOVhRJVI5TkUlmlNdLRJ extmmmbfd5p5jn3dcnh4jkupd194mnj96na6pnbcdnp4wt2ca950 -EdaTUn7Zbl3o1dD7wom8yldUEUXpn8jc 8nj62n2ndrvnmrkc6dqk2t246xvpyv9rf5p68na5anc70vhrd9hg -pANAvJZowmZEwOabkkHToATuZ7jGZxUQ e10mwgbp99d6yxvdb92qekv1c9nppj2mdx0n8xau6xn4epkran8g -wKNPjXcEDD8GIjXLaRTLhh3LnhC7Acos ex5mwm3ab1hmah24713mjujr9hgn4n2cd1m36k3ed11kegb3dxtg -Uj7sqcFXzAsBx0BnppDhRq1tzJBvJsXL ann3ewvhcd35gyj1ed17gc22dtr70h38a9rk2x3u9917cjkkb160 -rJT3oLfC2tDjk2EUxHyosEvPrkNx0oY4 e9558cvf9hk46ckm8hn6pcj5anw4gybfed2qcm3jdd77gc3fb4u0 -qxOrpknHROLYDYMnKYGzogDeGe9MTnJ5 e5w4ywkgddq4gmjf9hcm8paddt5njhvudxkm8ta7cmwmun3e98ug -Kz5562Tw7ROrbWbWlMun5GhPALEJplvd 9dx3ad9p69a7eduj9xt64nv2axp4uxbe6n3pgm219h2mmw3cetj0 -zeO8Mg7ndjRDAO4oN48728qw8WsjC6FU f9jmye2dcwvpwt3aa9242ktmdx738e1q68w72xtraxtpmgtp8tag -wCbtY5kw50Jw29j4XYykmLqgWsf2TQWj ex1p4x2t6nnqed9g99vk4eba6hc5jybbdn672tuqedk34n2haxn0 -0tYam8lXkKeSFGJKEl2SGBFFR6ME32UR 61u5jrbd71p5guubcn9mchua9d2prcjk8x14chjj6t6mactjan90 -pJJuhQOAfTaX1thXT1AyBxotsQAVmALy e154mxb8a57m2tjmc5c32x38b1a32gbt89w6yx3ka50ncva19hwg -zWhixXKDWzLk4m1JNVHJJD0kbQbxkBj8 f9bpgubrb15m8nvu9hnk8v9h9975cj2a99230uv2a5h7guu2d8w0 -ZpFuWFsSRV2d2T84iNsx3IlNyOapnVS1 b9r4cxaq8ttn6mjp69j34n1r6hmmwwvr6d4prkkt9xgq0vjpacrg -T5kuMCNHbo2WhYwI7khoVR51mMhp4vI1 aguppxad8d74grkf69bpgpbq94vppu3fat93acbd9nm70d3p94rg -KLjXaeESPbT0IRtZGIQni3KPXVZE7HFc 9d66mp31cn2n6m32agr4jmkmb93mjmbed4tmpm2ratd4adu88thg -uFhfvzTfcj5WCSgQ1mJCD27gvJc3VCZC en36gtkpf9a6crva6nbm6mv7a4rpujj38gt3etvp99hk6nj3b91g -qqtzeFNgoE1Rwym0FNw4d7nlML8MeRXH e5rq8yk58t76evu56597eybd6134wxtmcgvpwv2d9gw4utajb140 -TJSPNbLOLyRChRlH3gLzyaozCN9z5g47 ah556m2ec964yk3ta91pgmkc90tpek3uf5gpyyj39rwqmdb76gvg -i5ZBV9pWJfgAcqKfunrl1bwwYPyxZads d4unmgjp75r5ejk6cx0p6wabctupwwkc65h7exuta1wqgpk1chtg -8h5lnGRbIGL0ujSfr5Ex2YxSDQIuNfJG 71m3av3e8x964ja79gr7aujkctt3ahbr69cqgmu4a54qakk6993g -ei0SPJEhsrbB2HFCZ3UY2pwb3sLF9o8E cnmk0mug992pgwvjc9134j268dd36nat69r7erhked64cebf712g -5jA1TH4cP8fm1WljFWBWViktmQGdo03L 6nn42cam90u66m1rctpk2nvcd935egjqatmppx3da53p8vtg6d60 -2GpKNLiKh4haAIp9XSgOA7iM2gw0LPk1 693q0jue9hmmpu1md1gm2jbg75c56tuf84vpjk9jcxvk0k2gdcrg -b3M2tJLMY5IvyWKg7EtShYn7kFDahTrk c8tmuckm9964up9n95v7jnubcwvmax2kd1cpwdvb8t262u2me9ng -L3rGfcVj1GCnnpP7YX2Xlvezmf74kAP1 9gtq4hv6cdb6mca78dq6ww2g6xcngcjrdhv6aykdcrvk8uu1a0rg -aBZ2eCdTWC9IpYeVh3KsI2PwTBgmUKzj c515mck58dj58nu3754q0pb5atm36jvk94t50xum89kpunabf9n0 -kXc7ympNMUZ3B7wCHPnWFCfSNMvCV8TQ ddc66dvtdnr4wkanb8tm4dvq8d450vjq8t1pcmue9nv46nhrah8g -4znQvbOdBvQViivHiRUpbwWZuLfh7ehQ 6hx6wmbpc97p8gkpa5b6jubp91mn4nbgc9vnepkn9hk6gdv5d18g -k3tqzLjMHyqaGWOs4pWaPEIRWfbnDUAG dctq8wbu9hn4uj3te5gmenufecu70nv1a12mjmjqcth6wh2n853g -hox5Jw17cY28ounDiG5Y21r0d9bKWbhc d1qqgdaaewrkerut68w6yxbe8hmmedat68rq4c3475h4pnv2d1hg -N8jIZy927wixaJjqgjXWDIfddyxzM4fl 9rw6mjauf4wk4dvqd5w62jkae5kpmp2q8h4pct34f5w7mk9mctp0 -Yd5yMsiQjGDvNrKGAlMwWQg54ZlsgTZb b5j3ayadedmn2uj78hv4wwjb8x0prkbqax8ped9mb9p76tumb9h0 -5OkTbzc061AJcufNhEzJV8fjiFohaRnL 6n7ppn32f9hk0dhh85566xb69tm4ayjaarw6cuk98tqpgrajdt60 -49uyp9AVGb7cy1Uw6FcL2gRgUcDDhWRv 6gwqaybg750nchv26xhqjcanewv4cruc69kn4tuncd248u2qa9v0 -QCZ948wfr6ByuqRpUs8OWLZX79YUsLpS a51nme9m71vpcwhp89wqawaje1aq6e2fax65mp1q75cnawuce19g -stpmYhM9irPCn7CdqOLoIoQQK2A5yIyN edu70vatd16kjubja11pwdu3chrmyk3f95qn2mab690kaya9f570 -Uvm3PBkZxfjFpWi95VGmSiNIuieANDtn anv6ucug89nnmy36d9370nv974unchvdadmmwjbnd5jm2kj4ehq0 -C2g2SQSg0QcGhY5nHp75mbZ3mfYUiSt3 8ct6ecjka59pec2hcd3pgp9ndt470dtndnh5mcvdctcnauakegtg -khpGBpYKkuDVpcwscOWCXYOnIe4pGY9m ddm70hu2e1cmpuvn8hb70rvqedhmynu3b1cmyvj9cmu70hut75pg -vdBDun0oQu6GdSsYpuNLhHXoYz6SEUYi etj44h3ndrr6ymbn6t3p8mvkb5r7akjcd145gvutf8v56hanb5mg -zTFET9rxQkJhyOEEzsHSxZNnPtQp9BUO f9a4cham75t7gmbb99m7jku58nx76j2kf1d4wvjgeh8q0ea2an7g -Q3JZ5KdXFsVG07xZxGh1i967gUhE44a9 a4tmmphn9dj5ghkkat3k0dvrb9w4eu1hd4wkcdv7anm4ad1mc4wg -qInGhUjhDX70RZIPJRhEOvk0a2G9amOm e54pwhv8ann6gh2r6wr54pj9a1554u259xv6pc31693kjrbd9xpg -UBH9KlGpXDkoihdKZo23fkaxQDyKf2Qp an14geabdh3q0p24ddqpju349dd6ychkctnp2y2h8hwmpthja5r0 -zje37MiD7NgfJdvMjRQ0VzSkOuWhroCX f9n6actq9nmm8duecxk4mt3p9nn54m9gatx56uufenbpgwkf8dc0 -RJmgtX3t2aAtKhE0cEQ4odEPqjFMOagV a956utvmb0tq8ck185u4pu2561hmam9mdxj4am3hd934ukv1cxb0 -5cviAZ8aGQ6Aqbo3Jz5ExXbxFRPnLmqd 6nhqcua1b8w62huh6t0q2rkf6d57mda5f1c64y26a986wk3de5j0 -qfW5NrFfPiS9NGgEuNBTr5Y0cb8P9NZ2 e5k5edaee936cm39acwmwhv78numwgjme8unjc33c8w50eaeb8t0 -lgnrmWSKdPhHVzYEM4mkQMkPGN4K6Cmu dhkpwwkdax9mpt2gd145cyjt8n6k8vbba56ppm279ru4pdj3dnug -QiF17PxYCmmN6M9sdWgulBc820TWR00d a5mmcc9qa1w5jgvddn73ck9tedj5etvndh166e1j61a5emhg61j0 -VJkvJLObQRtFaSfCI6bq43mcOwbU2xvS at56pxja9h7p4majeh362mv68d4kcrkh6gtpurufexh5ackret9g -PLcZtCdQ13d2xqUAQ1ZqD08DZoJYTGLQ a1666pkm8dj52c9kcgt7gwan858k2pkh8gr3gh2udx55jn279h8g -OVk37hGqUliuWXyrzQ0xt1ebct38m5Uq 9xb6pctqd13q2nbcd5unep3te9x52c3regrpark3egtkgv9nanrg -p0nQpdumQ2HfoB5Tb5USCLcvtUrch26u e0r6wmbgchupum9j91k6yghnahh3anak8d666xkmant66u1j6tug -fWHVNVmDW5CSF7SsdPAXLInaiLxdRLo8 ctbmgnjeatpm8ntn8d9mcdukedj50gar9h4pwrb99hw68mjcdww0 -xG9u5qFOcFf4zVxKAbYw61IWs3AFvWHB f13kjx9ne534yru6cru7mnkr9d0p4pbq6rrmjnvk6d0mcxjq9110 -hM4qhOHdK6kRTq0bl11wGR9KmcZaHRvZ d16k8wb89x468jtpdd958w9gc9p32cbq8x93jjvdcdd62j2jetd0 -KQSjVWbHoS8je7UcpFAmiFuHPvV58nFL 9d8n6ujpaxh4gvuk71n6aduncdr4cgbdd537aj2getb3ae3e8t60 -BK5Wh2GX2jREpEBBG8RVEZZp8PFMPSaW 895kanv8693ngckaa92q0ha2893kgmjp8nd5mw1ra134um2kc5bg -PwgjTAtyryxeFHjxygjjfL1VahOMf5FM a1vpeujm85u7jwktf1jmcj3af1wpeukact632nk1d17muthn8t6g -UjWcdn8Cob945pUbrdp03yP5EW5Ffg1K ann5erv4drw46vv274u3aw2nc9t68w1g6dwn0da5awumctk7655g -VhjPlweJhd2LgoLlWONyveyrjGbq6uVi atm6mm3cexjmmu346966evucdhbmykktetjqjwka8xh72dknatmg -sjEUpqTByoGtStrhV3cB0AsCNjEcarqA edn4anbge5a44ybf8xu56x3jd1b36ru2610q6gued92p6rbje50g -Oaz6hIW7DmBUXWPBM7rwXkHuRf5FhEOp 9xgqmdk895bkeh3d89angnug896kewkqb1nmgxajcrumcu259xr0 -ugq79HFkWI2wFC19wVUyaYyIYS4iv9PD enkq2dtt9136pnu969vmcgth75vncnbtc5cqjjatacu6jxhta120 -y9Xa1Od8iRFg6BXcV1yEpX6eDoSfvbAr f4wngr9h9xj3guaj8tkkcgjrcdb32ya5e1c3cta4dx9pcxk285t0 -QlPuT2pvLK7XaFDSA11dexbJkeyKwgmu a5p50xam69r7ck2b6xc62hj4ad0k2cb4cnw64jkbcnwmpxv7dnug -KWvC14P92LFNW2gCSDJ9x5K8vxpWMHQ7 9dbqcgth6h83jcjc8t75eck78d9m8jhtf0umpe3pf1r5eka8a4vg -ADnjlQgzNVsbt8Dt76Cp5LXijbadeWHb 8526wukca5kqmkjpedh78e24egvkcgvg6n65gubac9gp8taq91h0 -TcQeMiOv4Hs4VtwWrrmD9VphylLTEBa5 ahhn2tadd57qcd28ecu5cx3qaxt74va475b70u3tdh658ha2c4ug -70QKImcYNyBReyMTENK7G5a8TWQYzRup 6wr52ju9dnhnjkkt8996ayadah2mwjtq8wup2e2max8njyjjenr0 -3vepqzJsBTF54NyMCIYFi2DN3V2Oq1TA 6dv6aw3hf9576gjm8ruk8kkt9n1mjpa6d4t48khkart4yw9hah0g -6G9cFh5X1UjLaBGEIWCAU60GUFCg199k 6t3kjru6d0ungcand9662gj78n4negu1amv30hun8t1pec9t75ng -xdCCFBaHeOeA02PuBYcmziLg6Di7UJEF f1j46gu689gmgtafcn0k0cjgen15jrvdf9mmrttp8hmkenaa8n30 -paHi8WlBBbtzom1xtv9nDa5rtO3mquAC e1gmgu9raxp44gk2ehx6yv9hf1u7cebe8hgkawkm9wtpuwbn851g -vX54aQxBA75cVxK81rOj7YQ7kNls7ghU etc3ad31a5w44g9q6nhncy2b70rq4kva6xcn2dvb9tp76dv7d1ag -ukyTbJGU1eytjNx52EqSsWVgdTvWOT6v ennqjn32993nacb5f5u6mkkr6mt4awakedbnctv4ahv5ekum6tv0 -zWV1uMCfcKvS1cFcsJN8SjCjEDCkOuok f9bnccbn9n1pcrubet9k2ru6cdtmmkhradn46uj58h1ppkvndxng -4mZE9NjoBhca5BH0H3HkgNWB4bbsvAqY 6hpnmh9t9tn6ygk8cdgkagj861436j3bcx75eghmc9h76xj1e5cg -mNah12kEizkiwEOpkp35KZ22d9C8mOXb dn762u1h69nmaubuddmqehafe1nq0ctn9dd34ck4751kgvafb1h0 -8343CG9EQvjRPIYAPdSr8cB8oyrhgf0V 70tk8cu38wwmambpd9950jat85868mvj71hm4e3ff5t6gtv661b0 -vqbglvn4yyYou6FIY9seFsaPuwgUWqk8 etrp4tvcetq38ybtb5qqadj695ckjwv58ttp2m3nexknanvhdcw0 -NYgbIyvY9g00e1bzsZPe7xGJb6Iv1MSG 9tcperj9f5v5jeb760r6acb2f9tnmm356xw4ejk26t4qccadad3g -EVdN8Trty58HqckMpOrsRBdC0CRPyZwD 8nb68khraht78y9n71472rvb9nr4ywkka9168gtg8d950yauex20 -qeIGY7aksEidH5QGNTrrfmfQnSj8qU9Z e5jmjhut6xgppwu5d5j4gdah8x758wkjctppcmbeadn3gwan75d0 -jHYbPO5MqMgdoFQPsWPkCRwDvmafNTT0 d945jrjg9wumuwadcxj6yhjha1tnem3b8d97eh3pdngpckjmagr0 -2QlNJAdfph6ODMVxYq3WDbwGNlOEaMOu 698prkja85j6cw386t7m8kapf1cq2cuq8hh7ehuedh7marad9xug -W9JrezEHUzEMpVtjhhMtJQcu1HXC0FCa awwmmwk5f92mgnbu8n6q0nkmd9m6gkbm998p6x9h91c46c268dgg -MopXbITMkRy8he2OXhfCq7I3RrzwzN5b 9nqq0p3295a4uuujf4w6gt9j9xc6gtj3e4vmjcuje9x7eyje6nh0 -iR1k6iawejLTGmxuZBgMZF9ImcJDU5Go d5932utpd5gqetba9ha4evbrend44tudb933jjbdcd548n9n8xqg -LixA4DrM7MasXWxW6Nx0zwCGyWNE6Am2 9hmqgg9m8ht4ududc5tngnvrawv4wy1gf9vm6hvtax74adj1dmt0 -BM9vkvBIq3jJ6VCFaZ9IUKUIX4PKlbCF 896kjxkbet14jw9kd953cnj38tgnmea9an5najar6h84pv328d30 -0e6jnYI3DhQOyHYmCgwZp5f7SyoOf8qN 61jkcukeb54k6h38a57qjj2tdn1pexuue0upcdukf5qmythre570 -YqapaOnmLzEJisNTOjT5E217ArttjYq9 b5rp2w319xq6uk3u8n56jwueah7pmn1n8mt32du1e9u78ujte4wg -ErlFY5s43MJ95cqV99VC8nalbzlLvKHn 8nt6rhjt6ntk8cud98wkarvharwkjnj371q62v32f9p4rxjb91q0 -iTTBOakEYZF2r9YFnqtZm8iIdPt9Mvdc d5a58gjfc5nmapau8rt74eat8tq72x2udmw6jjb4a1u3jkbpchhg -qFQiEQNQpjy9Lu6FkV8NiJxyw4fEXubO e5352ua5a5752w3af4wmrx9p8tnnce2ed557gybq6hk4ap3nc97g -wVBavAz8fu9q8EJ18VxyhB4LXdJYvyGE exb44rbp85x3gtkn75rkghaa64w5cy3td1138k2rch55jxkt8x2g -E5HrfdxdYzs3tv7KEbdcNsbibWSSbHZ6 8mumgwk6chw68pbuectq8xhq9d2p4t339ttp4ub2ax9n6rj8b8v0 -kd0vbJLLUvvFnFTv9RwpjnmL7MLR2IZa ddj30xk29964rnbpet36whjmerwn4xvgd9q6uk1q9n654cj9b9gg -VvfTodLsHIXSyo7ve3MKjpodftZcls6r atv6cn3fch676j29b19qjvtqetjk6kabd9r6yt36ehd66v3k6tt0 -zcMKHB7sqRm28wK192YrLlJXxYW3IJaD f9hmuju888vq6wajdmt3gxub64wk4pbj9hp4mp3rb5bk6jaac520 -Dgzdse851Dxq31l30pLf4VMpavCzHyPx 8hkqmt3kcmw3aca4f1rk6cbc6cr70k366hb4uw31et1qmj3ta1w0 -bCgWOr0GnF4bxf2EleoR6EoIQjXbYwmp c91penufe8r4evj66hh7gthj8np6avuj6t2pyjahd9c64pbqdnr0 -vLH1GJuL3dPzmrpQLG8JMzs5QHVVMJZ1 et64gca799umrcv4a1x6uwkga564ee2a9nx76dah91b5ckaab8rg -FIHXBuJaO0oozYZL8C2nXdVKUXtNH908 8t4mgp22en562ktgdxqqmpau9gw46ckeb1j5cjunb1u4wj1t60w0 -4kt7AMPIivgm6pJOocWnZ2WUWzyQRvqu 6hnq8du19n84jubpcxpkcw2a9xqp6nveb8t5enaqf9wn2mkpe5ug -zYUQbNmnAugcLtBYLHeKEqWy52m5QK1u f9cnamb29tppwgbncxhmrx22b564gtab8nrney9n69pkamab65ug -K4dL42z3UfB4554ticVq7H241H9y76BS 9cu68k1m69x36nb688u3ad9mehmp6nkh6x434d1h90wqjdtp899g -jK8HqEc9ayVtkfuURACdvTo7KiVMfKop d95kgj3h8nhkjrbtatu6ptknan942gv4eta6ydubd5b4utjbdxr0 -OxJHLhiRP1ry9zflWvcpKQ0N7hEwaGhL 9xw4mj2cd1mn4m1he9wkjyk6dhbqcrvg9d8k0khqd12qera7d160 -ybV118NrQVzFljXxqVG2yyVABkpIHP80 f5h5cc9h71774mapf936rujrf1rnchtjf5wncga2ddr4jj2g70r0 -t1kpBICoiYe14HKS2wFtaP9UFD3SZ0oB egrppw22951pyuatcmrk8j2bact7ehkmc583jna68gtn6phgdx10 -qXO4MNu8c5w1YBV0xcH3SJhxHmFFDe0m e5c4yd2d9tukgrtnewrnjgjp61w66j1kad56gy28dn34ch3561pg -spbBNw0Wdte8whvIz1GiVDozCrgowDt8 edr64gjeewr5et3mcmw7eu3p95x32hv9at26yyj3e9kpyxu4egw0 -JrHz69nR8c4j2w0iZhy7hVOwumvPrJ41 99t4gyhp75q54e336hn34xtgd5d6gy9qd1b4yxvndnv50wja6grg -0cXAbeoX5RFqkOAzq8WkjTBoPQayC3ja 61hnggb2cnqngdaj8trppku1f9rkgnvbd9a44vuga5gqjgtkd9gg -KXBSYBx6lfAruBlVwC8PPGWYmRL5fJf6 9dc44mut89w3cv3685t7agkcatvm6e2ga13nepbda963atjacrv0 -4Tm2fnl9cAzTf5qh75dPNwH3fd0lFDgi 6ha6uck6dtp3jru1f9a6cdbhd0vkat2g9tvmgcv6cgr6rhj4cxmg -s3ZlhliPFCwMmvVto1gf1IQCdAQlvhqB ectnmv38dhmn0hj3ex6puxjpehqk2tv6654n2gv4858prxk8e510 -yW2wpnchRNZJDiw89rAlCXuSXUlr7aYJ f5bk4xvgdthpgmjeb9548ubq70wq4gbc8dc7amuranp74dv1b550 -Sr1QjRtK7T6eEseAHuFiknfIirnRMlLW adt32mbaa9u4pdum6tjmawv58547ahk9ddq6cjb9e9q54kbc9hbg -ZjCAv64YN4nNCuUk7i0r8MhJrBbehfww b9n46gbp6ru5jkhmdt746xandcvpjc3j716pgjkj89h6au36exvg -HH1smqjPEurzBQ7Hhh7KHfK1b2jTNmmK 91432wvde5n50hbne9x44m9q91m6gdub91k4pcb269n58kkddn5g -Ek7cDJstY9OUq7MexIs9c6ayMKt3T813 8nnkeru499tq8p9t9xaq2dudcnw4jwttccv62yad9du36n1r64tg -tKlFXo98IQLHn5FclPZHQZXyLnZEHzQK eh5prhjrdwwkgjah9h46wda6cdp50pj8a5d5gyacdtd4aj3ua55g -xjWBJd7qcjAG51dTRlupEFcNNMk9DuGp f1n5egjacgvq2rva853kacb4ah96rxbg8n366kje9nnkjh3n8xr0 -VL11rUJuAcWAkbUaXv8ETREYCa54BqAl at632cbjan57agb3ax0pprjnc5c7ce25ah94apa3c4uk8gkh85p0 -HHUvmBvspK6IUgw5bCmzy9fuN1EGqbBv 9145axkd89v76w2b6t4natvq6nh46vbuf4wpcxae652mewb289v0 -TJUvBPtmPhV03lWmwlwA4kUSpDGEaeKF ah55axj2a1u6um38arr36v2qdnvprxu16hnnamvg8h3marb59d30 -jjzalK6J1KEgy867m6QdNgFiYR4McIqY d9n7mrbc9cv4mcab8nkqje1p6xpkcmb49tkmcuata8u4uru9e5cg -JTDyfXfDTzrWVrWwmDldvHs2LbczcLvx 99a48yb6b1k48n3ue9bncwjqexpm8v34et476cjcc9hqmrucetw0 -LZa8oa3vKZEUgz6JyqX7gAaf133EPIiF 9hd62e3fc4tqcjuu8napeyhp99wq2p1qcx0p2thh6ctmam29d530 -VeKIPiw7fi45ixxCtXFCe03zHt0Q1yT4 atjmpjagd5vketk96gupjy3r8du5ghj3cmr36yj8egr52cbtagu0 -JaDzC7gzsF7EEVeMz0cUtVlW9fsF4YqM 99gm8yj36xkqmwu66x2mank59nx30runehb6rnttcttmcd2te56g -VnEEKeWeDXEpkn0zuHc8WPSr00SmxoRF atq4ahabcnbpah2r8nr6pvhgf9umgrtrax856whg619puy3fa930 -iI6HkKKUTphzvBhRdfxv2G9xhDItofcT d54kcj3b9d5nan3gd1x7cgk8a9j6cy3p693kjy388h4q8vv6cda0 -IQQswXkHUvehCrM8vWNfOWJUL3rdeEzW 958n2wvqb1nmgnbpcnm46wjd71v5ekk69xbmmnac6dt68ta5f9bg -jVKvnd5nOmlCAGNVTx9mGfk6Gd6Yvoc4 d9b4pxkecgupwkvddh1m2hueata7gebd8xk6pdj7cgv5jxkfccu0 -Hyl5fTiffVhAkbHqU60OYUxjEI2Ai4kx 91wprdb6ahmpctjpd10pprj8e5akcc2fb5aqguj594t42u9mddw0 -xgYaeKJ91f9gdqoeIMZCnpmNAIUnpjTN f1knjrb59d53jcb675kp8wbfcn4mupj3dtr6ukj195apww3aah70 -cUTaUmJhwc0HQDDo5mYz6AHN0a0Q60wY cdan8randn56gxv361452h24dwupupbu6t0mgkhgc4r52dhgexcg -lkQK3CxgYlpipK5lbCzvikuj652OZSCe dhnn2jtk8dw6epbce1mq0jtndhh46ykpd5nqauhp6mt4ypjk8djg -KmUa83C7jNmnD07mw03Br81HT9jgNOdF 9dpnar9r6d1keujednq48c1qdnvk0cu2e8w32j2m75n6ekjfch30 -zoTXSpntgtwikDiqmLXqbWgu4v7AUp36 f9qn8p2ke1q78tvmexmpph39e5pmrp3hc9bpex9mervm2nbg6cv0 -nAOKIIZNb788KVoWOqq9T03EgMQKFzNc dt0myju995d4wrhq70w4pnkfax7q2w9tagr36hb79n8mphku9thg -JwAZQ0RahlG89WLgQKwQ1oicSMpHH0Rn 99vm2pjh61962u3c8ww3jnuccx8mpxuh65qpjruk9nr4gj1ga9q0 -29nt19lFIEoIZNS0jsiZX4qXeDMgTXP9 68wpwx1h75p4cja5dx4nmkjk61n76uaub0u72p358h6pen2ra0wg -AtWxjwtVeKvhpoKMNYguA3quJqLLBhja 85u5ey3aexu5ctabetm70vub9n75jtvn84tq2xaae564rgk8d9gg -OgBxiKCPyltulg2Lp72YvZDS2sEKrIUE 9xkm4y399d1n0ybcehuprttj9hr3ecjtetd48mtjed2mpwj9an2g -YFvoxnPz9FC8Om05A6LnAEEI0jHpyVqr b537cvvrdt87mea68cw4yv9g6n0kck3e852maj9gd9470yape5t0 -47d0vgJiCEri90fq6vqAXuHQ0NW284Uq 6gvp8c3pcx56jgu5e9mkjc36e4v7cwa1b1umgm9g9tbk4e1manrg -ejF6oUcujp2lO7LKq8SkW9BvCn8X4ylH cnn4cdkfanhqaukg69p4yduc9drkgmvbawwm4xj3drw5gd3tdh40 -km6OrUWcxWfe0kwa3BNXL9cDeNSa8s4k ddpkckvjanbp6y2qctjk0uvqc4tm4kjr9gwp6h359t9p2e3k6hng -XCWeK7gXHqBRRrFYPyf1tPJP4bbTres5 b11netab6xkngj3h89954wj6b587jthheh84mm1mc9h58wk5ecug -8Jg4MmSt0i7ud5EU2gEwISsfwWQ9i9xq 7156ed2ddn9q8c396xup8da5amt6ehbq959q6tkqax8kju9tf1rg -vQKzhZxCa0R7oo8wSalE0fmnDt9D0xZR et8mpyk8b9w46r9ga8vpyvtrex9p2v2561k6uvj4egwm8c3rb990 -j1EsNdaknVQoIl41D49xuRuBvXcKeSm7 d8rmawuechgppvjpa5qmjv1m65238ebren97agkpb1hmptakdmvg -5EFTfuCzOmIKKtISfgMrJ0LzeYawTKsL 6n2mcn36en1qmkvd955mpx29adk6ekbj98r4ryk5b5gqen2bed60 -TWWYPisi9lLgZNdk6exeUZ65I1h14mtI ahbnepagd5tpjebc9hknmkk4dcv6ay35and3cda965m32d3deh4g -xXRsxYhnvMl3bdhy1ncMpCtdHzUkPEQj f1c54wvrb5m6wxjddgtp4t38f4rpwrude11q8t28f9appm25a5n0 -dLcHzIz79Om6JAVODFY81b1NUpSOjOKY ch666j3u95x3eeafdmv4mgap9x24cp9r65h32kjne19myujf9dcg -RqgrPNDg277JvmfiNZqsAAlDmF3mRJLs a9rpewjg9t26echq6x57cvb6d575mwbk850prh3d8rtpumja9htg -Tm1nhwQvRL35VXl1G71mnIbSpFjxCtFp ahpk2vk8ex8qcmjc6cuncp3c653kecbddt4p4mvg8tn7ggvm8tr0 -DfmD5JnQ39ovP8sgjJBnTvMgD8P7KLNZ 8hk6uh1n99q52cttdxv50e3kcxn4mgkeahv4utu47183ejuc9td0 -VeWaRSirE9QfLwyGipbEE0FgSxeYyYmQ atjnerajadmq4h9ta5k4rxvt8xmq0rj58mr4ctukf1jnjyatdn8g -Y9qUIEk0f2pOjd5E76bEgm0P3pJ9Exhi b4wq2na98nnk0thje17pmt1n8mvkcrj5cxpk0m1ke153jhbrd1mg -rvLY6qQb3gB6YuqQeEraziHH34rCFUwG e9v4rp9pe58p4cv788v5jxbha5jmawk1f9mmgj1k6ht46hjnex3g -G4ov0f70uX3rZmdv5lY4gmgY1OxbNKuV 8wu6yxhgcrvk0xar6dt5mvb4eruprp9mcxppep9h9xw64kjbenb0 -JexUlC0eH0PzU9CbgeesRKA8ftyRrw4S 99jqgnbc8cr6aj1ga1x5aea3c9kpatbka95m2e36ehwn4wkq6h9g -ML3hup4A6rveVLxXabKZpb745tp6fk86 9n636u3ne0u42dkjetjnck3rb1gp4juue1h3ed1nehr3ctkb70v0 -M1X4E3ILBAcRb3alSQkiPwunnwhrJxx7 9mrngd256d4mrgj1cd964cv1dh9n2uv9a1vqavkeexm74jkrf0vg -rY4q2GzWstqy7q7ZqZSea6l4YU8aSMWb e9ck8w9j8xx5ewvme5wkew9qb9rnmmv5c4v6rd2tamw62mudaxh0 -ftvobUZmojXSVQEonJYTxGM1IoR3hsZI ctu7cvv2and6uvvab19ncma5dxq4mpamf13muca9dx936u3kb94g -lYh4UuYiUw3fYffmtmwRd7V6qzTdBJSj dhcpgd2nencpjnbq6dk5jtk6dnu6uxujcgvncdkhf9a68gjaadn0 -VyorL85352L4t7weKImH9uoEgLdcnXYY atwpywjc70uk6d9j9gu78dvqcn5mjva875upyhb79hj66vjrb5cg -gVN6SNgmDzwVMk4s6UvZlzrk8QMTIpPT cxb4wdjk9tkpuh3uexb4uutmecv5axjudhx74utra56n8jbga1a0 -U2IpvEQMJheusC3WsqX9GmVTb4e8ywjw amt4jw3p8n8mujk8cnuq6gtkaxtq2p1t8xpncn326hjkgybqd9vg -9ytNhRt6CR7SZPG31wvaX3LM6Cl40iRn 75wq8kk8a9u3cguj6x9nmm276crqexk1b0tmrk9p8dp38c39a9q0 -hdGTVC6KQER986A2LPF14bDYQJil821T d1j4en2p8cv4pma5a8wkgdj169650hhh6hh48pah99mpre1j65a0 -xpR73f01zTzclEuF5qx7PQdrX9Au2RNl f1r54dtkcrr32yjmf9hprhbn8ruq2y1qa18p8wjr750qacjj9tp0 -meeeIQ5eZeNaxJciCqqBNhbZedlFXlES dnjpata9a4upapk59tgqgjk3d51q2wa29tm64pk5chp4cp3c8n9g -vvrEGWXiLtFzBhhdgzXE1jpQCBwpoV6e etv74ha7axc6jk3m8tx44u38chkqmp2565n70ma389vq0vup6tjg -SGkw0YsvY93sOR0rn2AOeR5vTpLNGhDz ad3ppxtgb5tqcp9t6dtmymhge9q34gafcn93axjme164whv88hx0 -EmHIvtW82osBlEkjP2sj7y3o4EfJR2eY 8npmgjbpehbkgckfed16rhbbd9834wva6xwk6vtm8nk4mmhjcncg -SgmzTAxvhL2RCMHmn4QTEs65qPaQKHWm adkpuyjm85w7cu2c69946ka8dnq38mam8ntkcdbha1gn2ju8axpg -eaWJvu66gC9TZj8bahHqAEL6N8oguO7l cngnejkpemv3ctu375a5muhrc9gpgj3h852mrdje71qpexaf6xp0 -ACYBfhqBqeOiC3yitElIwAvl0IOLU4PT 851njgk6d1rm4wb59xmm6cvtd5u4av29ex0qcv1g957mrn9ma1a0 -iu1rczky6lmGJ91g4iQGttVWIEOOfeFR d5uk2wk3f9nqjdkcdn3mme9hcwu6jma7ehu5cnu98n7mytk58t90 -jamGcj0Rb5FcXTQ1jI17AZeIDYhNu1xh d9gpuhv3d8r54rhn8thngn2h65n4jc9q85d6aja4b5m4wx9hf1m0 -4UjMYtYJvpwhcsFVnsVOWQGICF5qoFvy 6hapmkatehcmmxkgexm66wu6atq76njfax8meja38ruq2vu6etwg -F8pXCZ2FISULUSADPbDA2tTnxxNzpQ0H 8rw70p23b8t4cjakan65amu18h864h2169u58vkrf177mw2h6140 -dtN3mG9aRf9jqHHsWVchSYeDsi0lNvIN chu4wcvd8wwp2mk675n72j28edbncrv8adcpah3kd4r6rkkp9570 -9R4uZwo2ukJHxEVl2y4cIgQrzEHXDYow 75938xauexqk4xbb9947ghapdgt7jd3395kn2wku8n45gh2tdxvg -H7g7w6uYWBfCrJYuE30W1FKE86eWPhbp 90vpedvq6tunjnu2ct1q4jjten2k6c2q6534ph9r6tjnem38c9r0 -QQWmAep9Q7Jofax8yk7G7jPTpyq0ZcWS a58neva1cnr3jm9q99qpcrbr71wppdu76xn50n3gf5rk0pk3ax9g -7EtCUr5EGaawrequIugNYwJvdVEDxxVP 6x2q8gune8umahv1c5vq4tbhen4qatueb5vmmxk4at2m8y3rat80 -xZAIDisGAZiYRCpvuvgpC1iGHKHBYfXA f1d42ja4d5tmegaud5cn4gvgetuqctvg8crpjhu89d444pb6b10g -bfH1Brxfjea6oGqNhNoTLsNWANcC9U1R c9k4gca2e9w6cuk5c4v6yhvh9tm4wvum9htmwnu19thm6ean6590 -V2sMnbYOtn0C4iR12O7V6bKCMiF7uC3l art76kbec9cmyx3e611k8uaj64t4ydup6th4pgudd533exa36dp0 -kpvaKAzNEYthB9bBSotqGA78MN5gJahS ddr7crab85x4whatehm44eb2899pyx3h8x0kee2d9rupejk1d19g -JmIAS7TXHDk95EgNPC7MyYpNEePKFwaA 99pmjgak6xa5gj24dcwkahb79t846dudf5cq0kj5cn84phkqc50g -g4bcLOvIkDvmfSTk4sTdhaChmWlo2O4P cwu64ruc9xv4juu4etppcmumdcu76n34d1gm6u3daxp6ycjf6h80 -omuvUyJqRzvg4SIVGpqRifzrKxmDxx87 dxpqaxjnf5572mkuetkk8mu9at3q0wajd5k7mwjbf1pm8y3r70vg -bGCd2gUi3Ub4ZDsBkPre9IOWudq8Rvc4 c93m6t1jcxapjcunc8u5mh3k89nn0wk5754mynvnchrkgmkpccu0 -6NyQhXZxT6l6a6e29QNkGATOXo4dDGHW 6t77jmb8b1d7gn1pdgv62dk568wn2kkb8x0n8kurdwu68h2791bg -x9qYtYxvdbTlSmjQJ6ELYNF5kHJTz6GN f0wq2pbmb5w7ct32ahp56vbaa553chacb574cdbb91558yhp8x70 -rHDWOTxExX8a1mycfBXkINke57jQ8tTK e9448nufahw4ay2r71gk2vbtcdk44p3b9576pt9n6xn52e3mah5g -Z8WkRzmyTYVckxNOIXRLCDeiZDSlgKZh b8w5euujf9pqjn2tathppy2e9x4ngmjc8d26auau8h9prtubb9m0 -0X20MpCPVrlabuMzb8dSs4lCtaRyzr3k 61c34c2de11n0nkjdhgp4xadf9h3gt2kecu6rgvmc597jykj6dng -uUQH5IGU0aym3Eg5bRVgx1dppACsLQ6U enan2j1n953nac31f5pk6hb76nh54nk7f0rp8w3g851q6k2h6tag -0RZgUujIsoCksAXQZAyKtNjukiPzSDbj 6195mtunenn4jwvf8dnq6gara5d42yabeh76mxbbd587mmu4c9n0 -2ya47EEkqhEImPVldBDDH50bloHSyXjU 69wp2d1q8n2ppwb88n4pum2pdhj44h2490uk0rkcdx456yard9ag -pz147iAjqmSyzuW3QCzB00GCWwMrdo0X e1x32d1qd50pmwbdadwqmxaq6d8m6yj260r4eguqex6q4t3f61c0 -WychUQL8FRGU2hVttrp8LOKVpxcSZxQE axwp6u2na563ghjj8xak4u2pehu74w1r9h7mpnkgf1hn6pkra52g -JBEPqj6ZRIjTrcNgEg2VIgJwxt30NcMX 9914am3hd8v5mmj9d9a74ruecx2pecjp95kmmxvregtk0kk39nc0 -ZUgcTz0Hn5xrhCjxbJqfnnbWlPAsFYgw b9aperumf8r4gvhnf1t6ggvaf1h4mwb6dtq64nvca10q6hjtcxvg -717r27EvPQdBkrBdj4hwgZ3kMZg7hpJq 6wrkewhj6x2qcm2hch16pwj2chn38u3qcxd36uudb9kkeu3g99rg -w61wikszwZ1aI8s9jLMzzGaGHgjXtvFX ewv32xv9ddtqmxuu65gmje3k75n4rkbuf93p2hu8cxn5gx3p8tc0 -4hobiMll69uF1iTYVfZi0RJL4XOITWhH 6hm6yrk99np6rdhten332uamb5b6cpk96194mk1mb17mjn2qd140 -Bqt1Kl3gYN1OokoriDHs8Q0BhH6Yhq55 89rq8cabdgtpepae657pyuvfe9mm8j3k718k0gk890v5ju3h6mug -9ENmweuPOmKHEvfnCupZESvpDIhUPRGF 752mwvbqcnun0kvd9d44axk6dt1qaw2u8n9qcw2495m5am2j8x30 -sIA1Af2nx7FgmXAInALpguLIh5XrCfgC ed4m2ca1crt6wy1q8tkpup2195q42k3gcxumrjb86nc74gv6cx1g -PM1eNE8l5zsM0frNNog3jFh5V4fmuPUv a16k2tae8mw6rdbued6k0tkj9t76yttkd936gdap6hk6uxaganv0 -OAzWipRefxQfD1a89h5rQfEYg4nVK3Z5 9x0qmnv9e196atkra5k48cb170wpgdbja5k4apb76hq5cjtkb8ug -H62670u3k5XrqMvbHaCwrlwTCSHuyWQY 90v34dhq61uk6utnb1t72kbpc9462gvqe9p7en23ad47ayaqa5cg -wdNDVI6tg8gCwh0KrGpIZHDWokJ1ZsVb exj4wh2p94v78ttrcx1qeu1g9dt4ew29b9448nvfdd532pkkath0 -RaBRCJX6Y8DSvbM82C2rRc8nPw5yus2h a9gm4mj399c3cp9r8h9qcrjd70t46ckja9hkgvjgewuqjxbk69m0 -Pf2OekizkkITCy2taj2Z0kcWaIScNDEw a1k34kv5ddmqmuvb95a46y9jehgpmcju61np6nv1959p6kj48nvg -vUkikyXeCLgglJKBT5gtWi4hNBgxHGOT etappubbf5c6aguccxkprjjb89a3atvmaxmk8u2e89kqgj279xa0 -vAgnYfDrJQ9LVkq0foOke4LGmEect7vh et0pevjtct274jjh7565cuvh61k6ykvbcmu4rhvd8njp6x1qetm0 -K4HcKxBgdb8BR1lRKCaAPYphYEfaXiS5 9cu4grubf116et3271154cbca95m6ra1a1cq0u2t8nk62p39acug -4SlB3nC8HNLpdBe0IHER1rZzdqy5v19D 6h9prghkdt1kgj2e9hr68gk5614mghaj65t5myk4e5wkaxhh7520 -fEwGoR8VH11zaJ055GmoDV6KB7i4NPYz ct2qehvfa8w5cj1h65x62jhg6mumevbf8hb3cju26xmk8kjgb5x0 -LqtQcu8RxyTGM9DuCe3UrTufHhv2BZ7m 9hrq8mb3emw54y3tah3muea4en1pacune9a7atj8d1v34gju6xpg -Gpckuh9tFOdLPVCny4ZBlgdZr0puryNo 8xr66uvnd0wq8hjfch650nj3dtwk8pj2dhkp8pkj61r7awkt9tqg -ubCgRCRvXCrJuajPQ6OjgCj6YPi5EUiL enh46tuj8d97cp23e957arbaa18kckvacx1pmdjta1mkahand560 -R1EpibLSJbywSvIXUhGMRFZtWy7PbJq5 a8rmaw39c9656jk2f5vn6xj9b1apghuda935mx2qf4vn0rjae4ug -8hk1GDNtbdrvtOQ2cD736sBtoSOUSjHP 71m6pca78h778rk4e9v78kuh69hm8dtk6ttm4x3fad7namva9180 -w5lZO8oow1NXU7ABeHR9a1pFUrJb7Etv ewuprpjf71qpyxth9tc5adu189jmgmhtc4rq0hjne9564du5ehv0 -wcFZrTjyuliX16vNyhprcli2L4Q79Moi exhmcpkjahn7jxbcd5c32dkp9twpgw3jcdp6jcjc6h8keeaddxmg -Nhc3waCOL1QbZ2hHI5HSJgf6cGfiUzqh 9tm66cvqc51myk1ha5h5mck8914kaj2k99kpcdk38xk6jnbue5m0 -bvOFdErGefpV3ywEkK7uJ9oEdn3JKTGi c9v4yhk48nt4etb6e1b36ybq8nnmpdvn98wpyhb4drtmmjum8xmg -zeIaxOz7ah8ZalzxcH0y5cxaaBYerslP f9jmjrbr9xx3erb871d62v3uf1hmgc3t6nhqgrb189cpawkkdh80 -zr1rFru36W05Nmq1j4JfE5fpNw3uZq4n f9t32wj6e9uk6djq60umwvbh65n38jk68mupcw2eewtqapkh6hq0 -dk3NPN8JkfH4jnUofiiJ2x9rpI7XNsCa chnk6kjg9rw4muv690u6mvjndxk6juaa69w3jwkg94vngkkk8dgg -xaxghPBdR0w0sScXweYz31LA1LM6PdmQ f1gqgtv8a1168mhgewr76mv3b1vpapbu6crmrg9h9h6kcm34dn8g -IsRaI5esd5LOVZjQJb3yDWqBZvwl3b3Z 95tn4ra96njq6t1n9h7ncpkaa5564cvt8hbq2gjuetvprcv26dd0 -kKf17v0AYf5V1qzlAQrFPBLMU6IZsTU7 dd5pcc9qerr42pb66nb32wbudh0n2wj6a114rkan6t4nmwumamvg -x7ZbxGVQnjnTuqjjsaRsLtMZXoBEeb4A f0vnmrkr8xb52vkadta7awbad9tp2mkk9hu4upjrdx14atb26h0g -g2iTLMuGLikZtDZps37y7uYJV2025suN cwt6jn2c9numek39ddd78h2ue1tk6dvt6xunjjjp68r34dbken70 -cPqxbegtd3wJv62C3BrrFbxlNr47K4tn cd872y32cnkq8t1kex57cdhj8ctm4wkj8th7gv2ee8u3ejtmehq0 -c9EAM9cscFHbUiypkSqeQsdvcwThQzOw ccwmagad75hq6ru691h5aubte1nn6wb5a5tp8xk3exa6gmbu9xvg -q5gmzmSFc3CSY3Vk1p0ELPnNOOm6JhtK e4upevbudn9mcrtk8d9njcupdcrq0c259h86wkjf9xpkcjk8eh5g -KmMDjTECtAM2iLjVrIrPU0npq9F6Z6QX 9dpmuh3aah2m6x219mt6jk3aatt4jwjgamr6ww3h7533cphpa5c0 -iJlUNA8V0Lh2M3dG6KCf3Ysu9Kp77WuF d556rnae84w5cc2cd0t4ucv48wv4pgv66dcq6x9t9dr3eduqen30 -k0W1O6vZUFLJHfVSiN06zb2djzVlkUsC dcr5ecaf6tv5mna69h54gtjpadmmwc1pf9h34t3af9b6ruuned1g -kDkbLi3AUi5LHPwdPWGDrT8WEg6rxrWE dd26prjcd4tm2nb96n64gm3qch85ehu4e9a3gnu5cwv74y3jax2g -JhLPtPt78HkWyGqFsNV9enN54gIfzqjg 99m4rm3ma1u3ee28ddbqjhvh8ttmwnhtcnq4wd9mcx4pcykhd9kg -V9jeZdMSppVeTj2ruKT0zkBvlYOOay4t arwpmtauch6n6w3gatjn8uhje9umpn1gf9nm4xkcb57myrbt6hu0 -amiTNyi3CBtf4FJj2c9L8NfcdSvotbeQ c5ppjn2ef5mk6gu2ehk38hjad8t66eac7176crv4adv6yx32cn8g -DIPbMfYYZC9LeNecbJXStLl3GclV4aLS 8h4n0rjdctcnjpj37566akk5cdh4mp2keh66rcu7cdp5cd319h9g -zi3BToukL64V9uAcUekZsXsMQXQ69xbm f9mk6gjmdxuppk1p6hb3jxa1cdapauuuedc76kahb18kcebrc9pg -MZYQtdDihWhECeGrunnRyQDx4251CzRt 9nd5jmbmch26ju2qd12m6ta7e9upwvjjf58m8y1m68uk2gvua9u0 -BnnU7c5QeHFRcbepheX4k2uwh4fOFGlX 89q6wn9qccun2ta88t966rk5e1m6ap1mdct7axv86hk4yhj7dhc0 -rmY1pUmofOE6zlZnk8V0sbsrZFfj1LV8 e9pnjcbganppytjf8mv7mv2udtnkgnhgedh76wju8tk6mcacarw0 -DB0DPEkVvMUruNnXqiYTddyGe2ODBMIv 8h130h2g8nnncxjdant7akkeb1rpjpamchj7jhv5697m8gjd95v0 -DrtVOY2Hti8lkTsXcxzgdweDFHYLn4M7 8ht78njfb4t4gx3971p6pn3kb1hqgyk7chvpah2691cmrvhm9mvg -x8FFiI7mDS0v7UHFDGp6TOdtZmaAKtSR f0w4chk994vpuh2k61v3ena88t24ew1pah7p8x2udngm2jvmad90 -IusxukcuiYkAjcEaM8xztFFvQIb2rYj6 95uq6y3nddhqauatdd0pmru5c56kgy3ueh34cxjh95h34wjtd8v0 -iEMBCfNLVFVTyMgMYuw64ELYhgMo7EPZ d52mugj3ct74rnj6ata7jkb79ncqaxtp6h2mrpb8cx6pydu5a1d0 -zcV5CLkoLLpe0Iv8RoJFQM1JKf0No2GT f9hncda39hnpyk2ce1jk0jbp7196yjj6a56k2jjbcrr4wvtj8xa0 -nwEB2a4kzmRTwKrFbiS8NBtep7zpjLrr dtvmaghjc4u6pykda9a7ejvj8th6jmtr9t178tbg6xx70ujce9t0 -CfZm9rMSl8GuTQr9gp9Yy6BUWSAKvxjS 8dk5mv9te96n6v1r8xun8mbj75kq0eatf4v44naqad0mpxkrd99g -FYt9pptmz7ema6RO5jI7poYB2oQBY7Ge 8tcq8ebge1u6uyhqcnpp2djj9wupmj9qe1qnjghjdx8m4p9q8xjg -jqLAgUZenEcCafIkfXd4V4ZwRPauVvPC d9rmrgb7and6avj5cd1p2tj9ddk5gt1maru5mxuja1gqankpa11g -0yLn0cVA6juLotW2Gva357z4ziX8nmW6 61wmrvhgcdb42dkaen66yx2q693qcr9k6mvqmd3ud5c3gvkdawv0 -I6lVbZ7pjzN1DwneJMCCnl3IgF63Rqo6 94v6rnk2b8vq0uku9rrm8xvecn54ugu3dtp36jb78rv36mkhdwv0 -sdwBwvBLFw6BQspB9YI0jktysRuGsG4E edj7egkqet14rhkq6t152wvg88wnjj9gd9nq8ybka9umewu76h2g -Bbo1NWSOjCH9wJBgJzTSFc1Zzkqvl1qA 89h6ycaeax9myuj390wqejj2cx57mn2k8thk2pkuddrqcv1he50g -TRJ0uesAGQ3Fn4ez1eWVkjfBs4aQGNcd ah94mc3ncntm2huh6d36wd35f8rpanupddn6cgkk6hgn2huecdj0 -Wc5tjSiLwkkJQP4OnIq4uLfAdLQrDX3k axhkax3aadmmrxvbdd552m1m9xq4jw9men66cgb49h8q4h2r6dng -5UWGDIeZCCzG1NaJFDHVDzBLpyYhTcjD 6nanehu495jnmgu3f93k2kk199348j2p8hx44k3gf5cpgn33d920 -WKJYCQeLSMpnvXJIeON7NNfC5ucfqA2r ax5mmpa3a5jmrmude1q7cp2a95jmykhq9t76cgtnenhpcwa169t0 -L28CEVY9857qXoxKBWHeVKHFRxR6Oa4u 9gt3ggu5atckje1n6xrngvvr9d15ej35at5mghjjf193ckv16hug -OuX8tC3p6RByNQuqXhOqCbTeacGujQtH 9xunge3m8ctq0djj89wmwmbne5c6gkvh8dh58tb1cd3qaujheh40 -U2BXOswnGrXi0Q20vqUicgNa6iFwvMif amt44p2fedvpwhvjb1mk0m9j61v72nb9cdkmwr9pd537exjdd5k0 -U5h1aP10uWurayiGwKSBTPLn6Yv9mdlD amupgcb1a0rk0xaqent62yb98xvmpmu2ah84rvhpb5v3jvb4dh20 -Dy8QzCBcYWephjKNpnzZ7ncDst2l2c2e 8hwkgmbu8d166paqcnr6gujb9tr6wyju6xq66h3kegt6rck369jg -KVQrHNZooKZollnqXYXQkUHVQjRnEF8k 9db52wj89td6yvubb9qprv3ee5c5jp2hddamgnjhd996wha671ng -c4ZUtNylo3gR5iRHh46mcFeKFbt3foSZ ccu5mnbm9twprvtkcx93auaj91m38dkdcd36aju6c9u36tkfadd0 -AssZiySO9ZQjKa5yuzeHgxDvylPyJ8pO 85tq6pk9f59myeaua5n4pr9nf5uqmta8cxw48xktdh87jjhre17g -q6kLHsQmpzTnUm65G5PLcouQyK7hUvJX e4v6pk28ed8puw3uahq5av9p6n3kam2ccdqqambt9cvpgnbp99c0 -AFKlKUQ6NqbT1oH8crifZ6sDZfd9CHVV 8534pv2ban8kckkhc9a32vu871hq4ub6b8v76h2uctj3jgu8atb0 -sWuLd05LkN7ZeEduciuPHRdZjApa7eTd edbqak3460umruue6xd6ahb4enhpjxag91968pka85r62dv5ahj0 -S95Wenl89ll6gG6ix0qqmhcIkUwm7Dam acwkanv5dtp3gebcdgv6ehtpd5w30wbhdnm66jbbanvpudu4c5pg -3UJRR2NpRMdpM49KPurEcHQ6CpnO2h72 6dammmjj69770mjdchr4ud1t9d87awj5cd452dj3e1q4yck86wt0 -VkTDMRQkHeYgJUT1pG4BxXv9z7YJkKfl atnn8h2da98ppj35b5kmmnam65r4ed22f1c7cebu6xcmmuubctp0 -ssuSCbDvurVI0hGFvCWpWwOFMnbIdrMU edtqamu3c927cxbjat4k0u278tv46nvgaxvmyhjddth4jt3j9nag -o2Fi6guRQ7d2ty0aRL9LQraZe0OBIvry dwt4cu9pcxun4m9qcgt78y9gc594reaca5t62pk5617m4jbpe9wg -Xmgh3BFBGs6HwU7i6Oq4L4qHKS2ECJll b1ppeu1k89344hvk6t47en9qd4v4yw9m9gu72j2bact4aguadhp0 -C7wGGetUaMSyv8cNoJ0O2InMx1YWtH71 8cvqehu7cnu5aradadwqce339tqmmc2f694pwkbr65cnex286wrg -nOFQeVkfbWZN0sSgM5yujGJFeZZUVM6l dt7mcmb5atnpcrjqb9730wukcx6kaybnd93mmhk5b9d5anjd6tp0 -9nxYJElVZieo0AAM0NFsvP1rDSM1bhbH 75q7gpaa8np5cpk9cnqk0ga19mr4whkket832wj4ad6k2rk8c940 -KUiL05kPPhh9dhYQ1jlZ6c3Lk60yE9Cg 9dapjk1g6nnn0m38d0wp8u2ta4rpmv2u6thk6k3b6rr7jh9t8dkg -Ctku9XbHztbARFTawcQJjZQmgb2l98ww 8du6px9tb1h4gykmc90n4hjmc5vp6maad9d52vb7c8t6re9rexvg -OQqwPQmTUMdJnYBNqv1BdXQtaoP7f67n 9x8q2xuga5pn8nadch56wpa29trqcca2chc52x31dx83ethp6xq0 -WNiqSeOpKNPph77RHWCElkPnI7otKsJL ax76jwakcn7q0juea1r6gdtqa945egu5dhnn0vj96xqq8jvk9960 -MwkgPHqPdXmEIMOV6200pKM68EKSHQky 9nvpptug91rn0t2rdn2mjkafarv34c1ge15mudhr8n5n6j2hddwg -MsO69UYQWYUFSMnHiIYkvP6ZRcWYI2Wy 9ntmydhtancn2nutan356kbe91mmjpbbet83cpjjcdbnjj9jaxwg -mIsSftkSR9GqbEOQem3S2X8SpGn61Y1B dn4q6mv6ehnn6mht8xrp4hafa5jpucuk69c3gmvg8xq3ccat6510 -6SbPUom4bE9qY3JanGJic4BQqTRbAb4n 6t9p4m2ndxpk8rj575rnjcuac5q4ejk9ccu44mbhah964gb26hq0 -scPLuS10IqhSg7WJDAivaZ8UiWs4TSa8 edhn0k3nacrk0jbhd19peduq99242ubpc5d3gnb9axtk8n2kc4w0 -Enp570MO8XcYLBVkLmKgVe2OjuewJp7o 8nq70d9q616mye2rcdcmrgjpdd66ujv7atjk4kvaenjqejkg6xqg -vt6ANqMpLrih7A1XAuF7ru3ng43kln0B etu3cgaee56q0k3jd5m3eg9hb10qahhqe9uk6vk76gtppv3e6110 -qLSavJwLUGx4tdPYbSpVAqWhZZUyG83E e5656rbp99vmrna7f0u78t2gb5h56w2p85rneu2ub9aqjhtr6d2g -LPsglzudJGvJXj0EIMWvYJMTNTH1cwFS 9h876tvcf9up8jj7et55guhg8n4munvpb554un2eah432rvq8t9g -q0gT7KRVpMQZr42Z7iIMaKYRst4EFUKq e4r6en1q9d95cw2da5d74d1jb8vpjjadc55njmkkegu4ahjn9drg -QvU9n86rej0FjQkGZkyPKc5ADIXfvHyX a5v5aebe70v74tba6136mmbb8xd6pyag9dhkaga495c6cxj8f5c0 -4OtO7JVneNRwoRjvq7U6dbLPHN4V7KqG 6h7q8ktq99b6wtaea9vpymkaetrken9pchh4rm289ru5cdube53g -5lNvkDGr8lhbM5yCs9QCw190P8prq9g8 6np4wxkb8h3q4e3cd1h4udbt8dtkjma3ewrkjc2g71r74w9tcww0 -N9SwIzUkomV3EL72TdXDTYlUJATmPvZH 9rwn6xu9f9appvvdartmak1q69a68p24ahcprnaa85a6um3pb940 -LpjStgPO24yFMQlexg9PLmvvIa3wD1BK 9hr6mmvmcx84ychmf534umbccnw6eeag9hpqcxj9c4tqeh1h895g -oKbFF8WoSXZiKMR9Rw4F43FuM5zAF5Kk dx5p4hj671bpymurb9mmpkaj7597ed266gtmcxad6nx42hhn9dng -eqbIkQ7HKYG3fX61B2gU0Ra0b8eV3KGl cnrp4jbba4vmgjut8wtpcp1p65134tun61962c3271jnccub8xp0 -mBeCAsNxTo6jZ65d1Cm8SAEJFcNEPYuN dn16agu1ed77gn3f6tn5mdhncgrm6v9rad0majj6cd74am2ten70 -dJ3QLD43ssenCaQ0cQQjITtZNHYqydTL ch536mac8gu36wvkcnq46rah61hn2mba95a78pje91cq2yb4ah60 -dSLrQboJ3KiPLJsGtPqeRDgs7ir5QOFU ch9mrwjhc9qmmcubd584rjkk8xu50wb5a926ewtqd5t3amaf8tag -8uImplpiSEKZXOryKXkky0Xcf25lBRe4 71umjvbgdhr6jmu59dd5gkvjf55nguvbf4r5grv668uprgjjcmu0 -dzMmf9O3em6LPXyvEUP8xiy4Kr92CBpW chx4uvb6757k6tbd6t650p3tet2nam1rf1mqjd2be8wk4gu2e1bg -VKSHVk64yCxvXpjuuFca7ahXyQQcyWId at5n6j2pdcv38ya3f1v5gw3aenumcrv16xgpgp3ta58p6yaq95j0 -W1s6vYHGBREHYBrLgd6StoiZP2S6y6tg awrq6dkpb544egjj8n45jgkj9hkp8djkehqpjpjg699kcy9pehkg -kkeeqvj6TA4h1bKPvuqFCqHmSurabm0H ddnpatbhetn3cn216hm32rjba1v7awa68drmgvakent62rkd6140 -UG6CGtXIUPJ7qnCT2cKG44vz9GrVH9j9 an3kcgu7ehc4jnag98vq2vj3agt66ju76gu7cyht8xt5cj1td8wg -HHz0obrsqjtt93z5piHlkslgAv0ImJDP 9147mc3fc9t76wbaehu3jcvu6nr6jj3cddtprtu1err4jvaa8h80 -BwleAeDR2ATVVmKZuSKlG5eipnAyHjz2 89vprta1cn254cj1ahb5cvabb9un6jvc8wupaubgdt0qjj3af8t0 -1f9wohTOg6ZTXAUcCKlvGQdEqkUCI8RS 65k3jxvfd1a4yttpb9a5ggancd1mpv3p8x8p8hbhddam6j9ra99g -O91jHM6Fld5J6ImJJPn4f22i3GC0cqa7 9wwk2uj89mv4cv346n53cjbd99550vhmcrt34u9k8x1k0rvhc4vg -ROXaa9KJmixVQWYUF2xbEA3JXlvrfi2u a97ngrb1755mmvb9f1b52nutan334y328n0k6jjrdhv74tk969ug -LVZacB6KvJmOM0xiihM7hPK5PUlCSJzy 9hb5mrb388v4pxjadn7muc3rd5mpgk9qd184pdaganp46muaf9wg -UdiKJ5zoD8p9tqnHQ1Ki78CV0dPQkw0s anj6jjua6nx6yh1re0wq8wbe918k2jv96ww46nhgch852uvq61tg -YY6PB1oxjmoWVIU6UKifjVN4XEdS8PdJ b5ckcm2265qqgukddxbncjan6tampub6d9b4wd2r8nj56e2gch50 -9AJ7xzYBUm52e8HaMs0mq7lU1JitNBxx 750mmdvrf9cm4nbd6mt6ae28c56q6c3de4vprn9h99mq8kj2f1w0 -D0UioLxvitazrCoMghFwQsJ6L6pw0J1s 8gr5aubf9hw7cubmc5x74gvf9nkpghkqa5tmmdjc6tr7ec2a65tg -SlGxX9o17U5O7PtXgYrAzi2o9gwZMFxb adp4ey2r75qk2dun6n7kem3mb1knjwj1f9mk4vttcxvnmka6f1h0 -Z0HrFaKOP2i63Hp2kfckGH7ID16WLbnw b8r4gwj6c55mym1jd4v36j3g69npcrvb8x43eja464v5ek32dtvg -TGJfmHv93MEYGlZboAJ6dNuJlx7wLK1B ah3mmtkd91v3jcud8ncmev2uc9qm2jhpch77ajkcf0vqek2b6510 -x0lsrrnynFVWE4qaYyFf20VmFVTnQMwV f0r6rwvje9q7jvj6atbmad3hc5cqjhk668r5cva6ata6wmadexb0 -DOrlBW1ONCopR9AmfWuu0Cb6nObwCpiU 8h7q4v22awrmykj3dxr54ea1dnk5exbn611p4dke9xh7egvgd5ag -yMlJzSpH1rwmzQgI6LBAii5LXylM8kyd f56prjkuadr4gcbjexpqmmb794v4rgj1d5mkak2rf5p4ue3bf5j0 -JPXREyhuqomh6ZtSadPa19fSOsAWOiX0 9985gmj5f5m7awbfdnm3cpkmadgp8m3164wpcmufed0nekv9b0r0 -iNFyNKBpTTotnJa1DpTAyW5PefC79KQf d574cyae9d170n2mdxu6wjk165270n21f5bkam35ct1keeaba5k0 -f31qNYmG0qxRxynDgWNhh06x8zH6t4pO crtk2waeb5pmec3hf197gybe8hknekk8d0r3cy1rf943cx1me17g -HCLuTHKrWdPD37n88t0MyBHQeVLTMhaL 911mrxam915q4nv4a1236dve70w78c2df514gmb5at658kb8c560 -Ua1bfVERHKpyW1HVBNLH5Z8rV1SpZYMd angk2rk6at2n4j2be1wneca8at14wk286nd3gwjp659q0pjt9nj0 -1PB6UTNDg5rswZqmwmOUoctAdhWlNlLB 65844djnah748ttne9tqepkhdnvpukundxhq8gb4d1bprkkc9h10 -xiWzMSmBF9AdKp1fBqvkQqHYNwwMoJnZ f1mneyjdadpm4hht85j4pw1hct172xkba5rmgpaeexvmuvuadtd0 -nD4FPmNEIkXZsLYKL7i3zxxfumrYPifa dt238hjgdn74ajbbb1d76k2t9d63eu9kf9w7gtkndnt5jm39ctgg -cUvwmgyanV88r5v8bPRgLnWfbcNTKHRa cdaqcxvdcxwp2vjp70w74dbp71h50mk79hq5etk2cd758ju8a9gg -IX2vPp2OyjzDDuabPxVoN5aJKOo7gOPp 95c34xjge0t4yybaf9248xb1c987gnkf9rup2jjb9xqketufa1r0 -gekAfYudGHSil0qWuJ27sPQDAFpjoyrS cxjppgb6b5up8hu8admprc3haxummchqed852h218tr6mvvte99g -No75t5jAEnyh9ObXDVlOO8BAtJ9oaYdv 9tqkedbm6nn42hbef5m3jkv2b125cv2f9ww44gbm98wpyratchv0 -gL9zT5NubBADKIm24WKp9xHMUWPI0lre cx63jyjm6n77arj28524pjbd68u5ejvg75w4gkanax84jc3ce9jg -6vSDT8KgU0wn4OB0RFNoH6idz1NBARMN 6tv56h2m715pen9gexq38ku26194ckkf90v6jt3u65744gaj9n70 -kXA2FO4z3RyW0z4TRPotWTV2MHLBxTWD ddc42cj69wu7mcujf5bk0yhmah950vvmaxa5ccjd91644y2max20 -uVDbPQ20j6dfEw36MbzuLyuaxq9QuYzl enb48rjga4t30uhpchk4axtk6t6p4ykn9hwqarbre4wn2xatf9p0 -NyAumebf4wP2PsXr81xs8CZu1SlPpbAQ 9twm2xbdcnh6cd3qa0t50wure8w32y3k711nmx9hadp50w32858g -n1DSH7BC3TYDKRrYg1o8kF4BFLhG1AI5 drrm8mu86x146cumb524pmkjb5kk2vtrdd338gj69hm4eca194ug -TZUIuPov9r7dy8eTfNYKKuiwLO1EImMX ahd5ajbna1qqcebj6xj7je35ahk4wpab9dupjxuc9wrmajbd9nc0 -1xGvKm5GuTnEsanqZ7gv9WdBnwg9vPcL 65w4exjbdmumexamdt2q6rbee5d3etvp75bp8gkeexkkjxjgcd60 -05aRdfyGYVSnCIZAseG113FJwPXtulxP 60up2mk4ctwmepapadq46jau85tpahth64tmcjkqa1c78xbcf180 -KrO9qp1gAVuqSjzGtuNScDuzJH0IM8ye 9dt4yebhe0rpegapenrn6uku8xu7akjkcd27ayja90r4jk9rf5jg -cz3wmdie2T73eAVGIpLn3ff5t4FgmBzV cdx36xvdchmpacjm6wtpagap8x4q0k3e6dk6cdbm6h36eva2f9b0 -c8q1SuZea75S8r18VJKiQqk9q0KZH4cd ccw72cakend6ar9q6n9kgwhh71b4mjv9a5rppebh615nmj1mcdj0 -19OlAiGrItlMBaKVycjpbygwWTWN7eSP 64wmyv21d53q4jbmdh6m4rabatwp6ukgc9wpexuqahbmwdv5ad80 -inQ3yjK60hNEgSmORVKbUjuS6iYNIWj6 d5q52cvtd95kcc389t2pemvd9x95cjv2ann7amtpd5cmwjaqd8v0 -fMFnMiJQIBdtlYqVqgCRlkWvYCnQGWSc ct6mcvjdd5552ja2chu6rpbhatrpegujdhnnexjt8dq52huqadhg -pT3O2MPTLrFfZopfBwergIuVVCUBrt6Q e1a36ktj9n858k3j8tk5mvvgct17etbjcx4qanjp8dam4wkm6t8g -AWpZrjDyisjl18GWIzxSf4YLVOKAPCLC 85bq0pkjd927jubkd9p32e27ax4qmy2kcru5jk2p9x5m2m239h1g -r99OscegnrU4JAsIiOhrII8iCAsvaLd9 e8wkjkvkcdjpevkjamu4mgbk95mmyu3j954kgua385tqcraccgwg -OMUkAfQ2D5WNJV1eseRxdLmAwCKIFiUt 9x6nauu1ct8k4h1nax74mnhhcntpamkrch66ugbq8d5mjhk9anu0 -PFmDobQ9bmH39OW93V0asRkDU9A1dtfg a136uh3fc98kjrkd90tkjkuq74tncc31ed96ph2n750k2t3mctkg -R7JcxvtU43HWNe6132VUXvDMQ9T58YZX a8vmmrvretu5ad1k91bmwt9p64tk4njnb1v48kah75a3ae2tb9c0 -BV5BghU79DMe6ae2mvIgEkMr7TKknD05 89b3agk7d1akeea49njkcrb569pqcjb78nnmuwhqah5ppvj460ug -NLkEeAIZLnII6aW0Yd7YBzKYUjQA3KsS 9t66phb5854nmk3e954kcraq61cp8dut89x4ppand98m2cubed9g -fmJGjSxGYIi0T5I3gA4WEMrWplB8GwF7 ctpmmhvaadw4epa9d4r58da96dkm2d2q8n6q4nvgdh13ghvq8rvg -5Sn2mkdwJ8WXRuZhOLqy9CigGi5Xn11n 6n9pwckdddj7ejhraxc54xaud17mrwbt751pjtu7d4ungvhh65q0 -vYMBKTwGNkOCq1PvbKPzBbxsbBm2YDIO etcmugjbahvmekkb9x1q2cageth4pm3u89h7gwv289pk4pa4957g -fmw3NvRrg6z7fcaKgk2hSYoSAsjw8Tij ctpqecueet974ttpf8vpcrv19dkppck8adcpymu1edn7ee2md5n0 -8j30lZF08X4D3FZzvEXpaALXRhvRhKuk 71n36c3cb9330e2r6h236hjuf9v4ap3gc50mrp2jd1v54u2benng -yQSO96WYSVixIlTva7Ywx32xFAWzsxRT f58n6ktt6tbnjmupd5w4jv2metgkepbqf0tk4y2685bqmwvra9a0 -OITkqd69sWMwKx3QBklLJXIt5RuuKM3r 9x4n8uvhcgv3jwuq9nvmpy1ka516pv2c99c4jx1na9uqajud6dt0 -dlFMExQM5tHc7uTm94aCNTOoo35JAiHA chp4cka5f18mudbm91hkexamdmwk8ra39ta4yvvf6cummgb9910g -uRxqfKh4BveE7VedB9NDbqMr9PJpU9l2 en97gwb69dm38gkpcn2kenk5ch13jkj4c9rmuwhta1570n9tdgt0 -EaOTWNfEGRYX1Du7pFviah3oP7ieC6U3 8ngmyn2q9tk4ahujb5c32h3n6xr4cxk9c5m36vug6xmpagtpamtg -8YkJn5OkigO85yL6YEQu3CfpfMOLJdj6 71cppjke6n7ppub79ww3ayac6tcmambn6d1pcw369n7mrjk4d8v0 -hZlcx7XJiUTTsCqn3JzqZKDDqmgX0z1b d1d6rrvr6xc4muanaha76gvhdrtmmykhb95m8h3hdnkngc3u65h0 -INTugvMwBqplyxm8rfLQdbjo46UG4NzN 95758xb7et6qegkhe1p7jy3d71t6ck2hchh6mvtm6tamed2ef970 -3HGtYMKV4Qr4aU5j6THNIo4Q06tkMmRt 6d44ex2t9n5ncd2he8u62n9nd8v58j2e95qk8m9g6tu6pkbda9u0 -hsFSzDRs6kjtqcGuOTFtoPMdZnE3aHbj d1tmcmvu8h976dkbd9u72ru7en7n8hkmdx84ut2udt2k6ra8c9n0 -HEji1dpkONwCPVIfzsDGsyUYT4ttI1AH 912pmu9hchr6pkueex1n0nj9ctx76h27edwnapam6hu78j9h8540 -7y5lLutdsJTtZ49vvGluicWexiYICNbw 6xwkav2cenu68wuaahu5md1tetv4ev3nd5hnetbrd5cmjguec9vg -w2GsAbBfqm2DLn3ZV2McIAejs78yW0HY ewt4ewu1c916cwbd6924rvhkb9b34kb3950paukk6ww7jntg91cg -ZuKnY0sq2kQnG1mY6GO6HZzwbCc03v4a b9umpvjt61tq2ckba5q4ecbdb4v4ektp91d7mxv28dhk0cvp6hgg -gVYoLXvFFaKj376KzhU4BXt1VEXRJ9Zj cxb5jvucb1v4chk19dn36dtp9dx6gn9m89c78cap8nc54jhtb9n0 -3JqbkcU1OVQrHqk7MRjcW5Cmh0wD6Imx 6d572rkbcdak2kupa5t4gwbb6x6n4uk3awum6vb861vm8dj9dnw0 -PtYKmRCBGiyjnNP7JjudSKGlbxoXeQkr a1u5jjvda91m4hv9f5n6wkjg6x56mxb4ad5mev32f1qngtahddt0 -RJqjTatJ9iweOl79cO3ERQ1UNdYcH6y9 a9572ujmc5u4meb9exjmyv1q75hmycu5a98k2naechcp6j1pf4wg -6utSoZXxcd754iCw7WxpGW0f8XANiOvZ 6tuq8mvfb9c7grv46wuk8ua3ewvney3g8xbk0thrb10mwuafetd0 -FXomprWIh7q2Xf1lgsoOWJlwhNIwWEa4 8tc6yvbge9bmju1qe4t5gthhdhkq6vufax56rxv89t4qenu5c4u0 -qZ8ndObeWWeQ0G6nEaD2avty7DWae1GN e5d3gvk49xh6anuqcn8k0htpdt2p2h1jc5v78y9q8hbp2t9h8x70 -0NKUZiY86rqxpqiMcTHv2utVkGh5HVze 6174pnaud5ckgdkje5w70wb99nhn8j3p69uq8nkb8xm3aj2pf9jg -TrWcVAPmfmu0pfVcRf4Lo0vdHViOmU8n aht5erup8586utkdemr70tjpcd96cd2cdwr7ct28atmmyvan71q0 -8VgdiHsJHdeAXPCljU0BbHNg1vT3gd2N 71b6et3991tmmj34cn0ngm23dhn5ac22c944wttheta36tv46970 -8oZIVb33yEemV40VLmEpX1JhOrnbrhL1 71qnmjapc8tk6ya5cnpncd1gat66uhbgb0rmmu2fe9q64wk89grg -Z1KOV8sQtcAcZOf7oNSFhHI5qprbq7Xe b8rmpkup71tn2x3385hnmkv66xqmwmu6d144jdbhe1t64w9qb1jg -zpk71Ng1iNyLPgRxZHJWoC93haw3nr68 f9r6pdth9tkk2uaef5650tujf1d4gjjqdx1kjcv8c5vk6vkj6rw0 -ZXtKOY7JRxGT1Job3FP8lBOJsWAm6Cs0 b9c78jufb4vmmmkr8xa32jkfc8tmcm1rdh14yjkkax0pudj3ecr0 -frnFGCUyRah9vZeY90WS8WK6Z9t62Ekr ctt6whj78daqjmk1d0wqcpk5b4wk0nuk71bmpdju75u3ccj5ddt0 -Xa9l3zgUMEtzlJZiuJwIu2udERJKvXc2 b1gkjv1kf9knaka5ehx6rjjud5ummxu9emt7at25a954pxjrcct0 -LMCNGjcrWMPtr5pVYmIeQaVz94GR70ag 9h6m6kj7d9hq4nuda1u74dbgatcpujb5a5gncyht6h3n4dtgc5kg -IhgwhZVmCMycUmDw3b6FCyfzvB8VCJAe 95m6exv8b9b6ugudf5hnava4ewtp4dj68dwpcykp88w5cgua85jg -o5RxFMe53YqWo40X9ZYYGV0IZeHeR7v3 dwun4y269njkacute5bpyd1gb0wnmpat8xb30jaucn46amhqertg -MmLus6fye3RhHGYhUseYqoUdmv9ypUQZ 9npmrxbk6tk7jt9ka9m4ghutd1aq6tate5qnat3derwqjw2na5d0 -c8a6pDCXOq5ZYKjmFpCp4BzxIuQtpiff ccw62dkg8h1ngkvh6nd5jjvadn370gvg6h17my29en8q8w39ctk0 -8ry3WLUcsxSHMeSju9LjqyMIIcZumAHW 71t7jcuq9hap6wvrad44utakd9ukjk3ae5wmuja9cdd7ava191bg -UiD6vy3ANetyKo7Kw69fCxoAtTuNvC61 anmm8dkpf4tm2kk5ehwmpvtq9dvkceb68dw6ygbmahumwxj36rrg -ijDvNcSd46fOkHrmlmhafFUioKWh8ypM d5n48xjecd9p8d1pct7ppj3jdnp6uu31ct35aubf9dbpge3te16g -CWuNqgowv9qyULov353dDTb4VZF5JUtQ 8dbqakkhcxqqexhte5wnak3fertkacv48ha64d2pb933ajjneh8g -zlw1UXEAt8LgWBvKjzUxJPDVsGbxnu6W f9p7ecanb12m2x1r9hknegkp9dn7mnbr99848nkk8xh7gvkn6tbg -6xXVgDJ6hghxHFkA0krm8b8QwZvpgI6v 6tw5gnk78h53cu37d1w4ghkb84r6pwkd71h3gmbqb9v70tu96tv0 -4iZ5nDM3QFogMEqVjLz9Mkk2OOclLTyY 6hmnmdbe8h6k6ma6dxkmuhbhatn4ryht9nnppcjf9xhprk2mf5cg -hG0QEAPNRjRuc43uIGTMcwBJd1KcaQ7Z d13k0ma58584wmkaa9up6d1ken4men2dcdvm4jk4655p6rah6xd0 -4jDL1QqKbtYXUKAP17GOKteLGO8wqTiB 6hn48k1ha5rmprkmb5c5aju1a0rkehuf9du6ak279ww7ewamd510 -LXlXxZwt86JBS9LlpgEOvWQjv8HjNytb 9hc6rp3rb9vq8e1p99156eacdhr6ehafetbn2ukp7146mkktehh0 -JQLBPFKzebFaI8AdAI81gPJwEAlfmxO9 998mrgjg8t5qmtb28tgmje21ch0mje1hcx84mxu585p6cvbr9wwg -n7Unga0Zyf6QHFygZ7PaaMmlEz33CpGF drvnavk7c4r5myb66t8mghktcxd3em31c56puv25f8tk6gvg8x30 -GYEo2VP3cWHPVda1S4Il8ghCMPgHcQSz 8xcmavtjat836ruq9185ct31659k8jbc71kpgguda1kmgruhadx0 -wfKzyXIZNNrQzg4umGGUZOHVTTgol98u exk4pyktb14nmkjee98qmttmenpmehunb97mgnjmahkpyv1t71ug -q7uD6yqAYSS8NFRpmqTwmUa6DQI3IQpO e4vqah1pf5rm2pakacw4whjje1pq2n3qdnap2dj4a54k6jahe17g -lSKveEUMP23RMXv9tY3ZtD9kWrl7xVOQ dh9mpxk58namum1j6d94up3p75u5jcuueh23juuqe9p3ey2p9x8g -eipv1tp63tvko5FVF3YKgHHBfMtTOdcC cnmq0xhhehr3ccvmetnpyda6at336pabcx44ggk69nu58kv4cd1g -ZHfzZXXba9p5CA8OZLmaDcwa6LlNBkeZ b946cyjub1c64r9te0um6g9r9xd4rvb18hhqer9p9hp4wgkbcnd0 -1SzxV2v5M3GXkX4lezF3CsKN1lF2T8tu 659qmy2p69v3ak9k8xc6pp1mdhjqmhhk8dtmpkhhdh334n1rehug -IoIEG8aXeCGqF7iGO14wFbkhV9U0wAvx 95qmjha771gngta38xrmcdv98x7k2d3q8th6pu2p75ak0xu1etw0 -xAtWy2uAKyGCZVJKaya2FdSRDtiLRMeM f10q8nvt69um2jvt8x1nmnja9dgqjr9j8tj56mj4ehmmrmjdcn6g -J3W54KI4XvuDO9MWA5CNXF4HZTwyX6If 98tned9m9d4k8p3pen24yeadax0kagueb1338j2uahvqjp1p95k0 -7POg5d1m8xf2m8L8kLerjbtvaeT9aJM5 6x84yttncgrpue3rcrt6ue2c71nmrtbjd9h78xk1cna3jraa9mug -NU5pX4iodrwXmbPKPNxokmIOwbwZFyGl 9takaw2r6hmpyt3jexc6urjg9d84wy3fddpmjkvqc9vnmhkt8xp0 -WOIz0pBDI1C05kygmZN5HZ2OKKVWCDx8 ax7mjyhge1148j9h8cr3auvtcxpnmkhn91d34kub9db5egu4f0w0 -tw06TOiQG4IZNahs4fnZeFgOufK2nr9K ehvk0djm9xmn2htm95d4wrb8ecu6cvjucn36ekvnct5k4vkj755g -bkYdq8yF2dGxa8zyUXvvMlWZgQFYGMKT c9nnjt3h71wmcck48xw62e3uf5angxkp9np5epk7a535jhud9da0 -qdQTbiOV0bWMC4gWRLUhLfNRSCQcNgR9 e5j52n32d57ncc32ax6m6d37ax94rnb89hk4wmjk8d8p6kk7a8wg -4com4ToOtKk38ZPGsqOooLgeIZhyILSn 6hhpyv9mahqmyx2bdctkgpjg8xtq2kvfdx66eta9b9m7jjacadq0 -CxqJtERggy6PTKXeSnKzr2hnF8sLXSUQ 8dw72jkm8n96etvt6t858jurcn9pwjvue8t6gvj671tmrp2kan8g -XWgSpzv4Opox8vMSiOupA5HGHm1DWIqO b1bpemvgf9v38kvgdxw3gxjdadmmyxbg84umghu8dmrm8nu9e57g -JAnJzhCGi5emlqVy606okOXiaZ3lhp7l 990pwjkud11meu9ncnpprwapf4v30dkfdd7ngub1b8tpru3g6xp0 -Iv1jhsBvVbrGanE3hbJPdYxPdISxbEWi 95v32uk8ed17cnk2e93p2vj56dm64jjgchcqgm34959qgrj5axmg -UucRjnbeA5gykIzi3hxi0MzQg1ql3mty anup6mkadth6ag9ncxwppjbud4tpgy39616qmmb765rprcvdehwg -QfjfOqPOAnvJW1isT6c6ojtcL4iIRiKl a5k6mtjfe584ygbeet55ecb9eda3crtpdxn78ruc6hmmjmk99dp0 -63Vx7n9TqVw56G8dTUUMjnCLLa9VxrNN 6rtncy1qdrwn8wapewukchtrcha5anadd9q46k2cc4wncy3j9t70 -ElGGIJw6ETaDQyCmZ5FmJg44ltq9JFym 8np4ehu999vkchamc5252ya3dnd3ahkd99kk8d3cehrkjjj6f5pg -jslxsZjFQ8kvybY4YsCso0MlzQy0rshB d9tpry3kb9n4cm9rddv7jrjt6hcq6gvkdwr4uv3ua5wk0wkkd110 -AOPpxRcjFfSSeuxZ44CMptmMnL8UjRuC 857n0w3ra9hpmhk6ad9paxbrb8u38gude1u6ukbe9gw5aujjen1g -Bmbv1dJOnTcNiG6ziy3mgz1KJEtRO33V 89pp4xhhch54yvjmcd76jhtpf9mqjcvdcxx32jua8nu54ktk6db0 -kiLFRDrxbr20DWu7UStN0EzDne2fZa2S ddmmrhjj8ht7grkj68r48nvn6xan6x2e612qmh3ecmt6cpk1699g -nz61wAIzcvS2w6EExMABRP6NYE9wlbAy dtx3ccbq854qmrvpact7edj58nw4uga2a983ckjt8mwqev3285wg -s6TQIkV0qn7fEY5fx0x8CzqXG84M1a7D ecv58ma9ddb30wbe6xk4ap9nctw30y1r8dx72p2770u4ucb16x20 -I7iUEk1jYEZl4wrN5IUaKgDqEio5tikd 94vpjna5dcrpmpa5b9p38xvj9rumjnb19dkm8wa5d5qkax39ddj0 -tPREOSXspl5bLwnxbPJU7DKtQuPAsY0m eh854hafadc76w3c6nh4rxvef1h50jjn6x24px2hen842wut61pg -xWUo4IFfli7gstf05w33ptOqqj23D3FW f1bnavtm9536cv396xkq6x3660uqectke1u4ywbhd8t36h1k8tbg -rC1ghXBj3f9cvUKxSmiVGVpsSAO4Cm1S e91k2tv8b116mcv675hqcnabf19puuap8xb70wuk857k8gvd659g -lsBzudBcih8qII9jku3pvOTPoNOZs5co dhtm4yknch166ub871rmjj9td9nqacvget7n8m3f9t7nmwtncdqg -HQQteZfIq12Q2EHp2Y6AC38PZ0zMvJdE 918n2x35b9k4jw9h698k4ha8e0t5jdj18ctkgm2u61x4uxjach2g -BoztFiCvpR1LWW5V2MrA4b62iavij9Bp 89qqmx26d51qcw2j6565entnart4uwj16hh3cck9c5v6juht89r0 -5btjPbqMlwhpniFaz2ZyKbn7AprrGIWd 6nh78ujgc9rmuv3qd1r6wua6c5x34pkt9dh6wdu1e1t74hu9axj0 -n26lc9vB8vZ3LgvJpwSvbyxqGrYopeiM drt3cv3375v44e3pb8tmrtvp99r7emvpc9wqgwa7e9cpyw35d56g -CTkZ3b5rcX6RL97PjFwfKrOsK5u0nQC1 8da6pphkc8uq4rur6t94re9qa1n4cxv69dt4ywub6nuk0vjh8crg -52jPEg1AmIX6z5JzA5Gn0CxFaiaThbMQ 6mt6mm25cwrm2va9b0v7mdaaf90kahve611qghk1d5gn8u329n8g -bmqDxNEHdc4w3TRmAr49R3KkvK9HrplE c9pq2h3r9t2mgt336hvk6n2jdn0q4d1ta8tmpuvp9cwmgwkgdh2g -3UIzYkDhOyLw2XcWTf2adsdeiP36TXcV 6damjyjtdd26gkvt9hvk4p33axa6cck1chtp8tb9a0tkcn2rcdb0 -xnwW2oaUS4Jpql1twbfEHV0YXFOfLUyq f1q7entjdxgnamtm99r72v1hehvp4tj591b30par8t7pck2nf5rg -pJZkGcTYQblSOBfqKPwK6MDhp3o5piDM e155muu7cda5jmb2dh9mygk6e55n0xub6t6m8u3g6dqkaw398h6g -KNkZIroPgnBBA8kv3c2lK5XKlp2qAYnN 9d76ppj9e9qn0tve89142e3bertp6ckc9cungjvce0t72gatdt70 -Kyk0MNhZfUfdY0Ym6m58oA1r3Mds04tH 9dwppc2d9tm5mtjnctj5jc2tdmv6ud9rdx0k2whk9nj76c1meh40 -BTcNhlmE2D7xV1UsUYvcmvvLaoBTXCBe 89a66kk8dhpmacj46xw5ccanedanjxk3dnv7ck31dx158p2389jg -jopcsRSc8YTLOBD98357OjDTySOygSyN d9qq0rvka99p6e2tah64ygj474w36d9q9xn48n3tad7qjtukf570 -1Ky1iSHpfCs8EAcBC694J3tBAyCGiYIh 655qjcb9ad470tj3ecw4agb3891kce9m98tq8gj1f51meuat95m0 -b0ZKTMG8AeH0XMcrqWN54V5OZy9fQojZ c8r5mjum9n3kggb590r5gkb3e9rnekhn6hb3akuuf4wpcmbfd9d0 -DxYBjYIa5mtNzF00IdbE4Hv0aLJakFo9 8hw5jgkab54p2dbdeh77mhhg614p8rj56h47cc319h562uu6dwwg -HhpqHvhOIi9tMvnPBTwG2Vw4ksxYmnvI 91m70wa8etm4yjb975u4uxkea1158xu769b7ed3bedw5jvbeet4g -3f2YenC8KFZtDcDjiqsG9BZfyrzQ9t8P 6dk34pb5dt1kgju6b9u48ru4d9mq2wu77515mtkte9x52ebm7180 -egJmiQaC0Mi91BLczJrNJXkXDAsG4OeX cnkmmvb9a5gm6c2dd4wk2gjccdx4mwje99c6pp2485tmed2fcnc0 -KkQyAr8h0tX2XDMqud76GWPW2XF3E0FI 9dnn2ya1e8w6gc3mb0t5gh2de5up8dtp8xbn0ntjb1336h9g8t4g -F1ORiautLaTdzFgxHvb7hL369MqDrFiK 8rrmymk9c5uq8k31ahj7mhk7f147crhqd1636dht9nrm8wj6d55g -mewkvdQ88LLLbC3KFX1CVnqZjpQvcTQP dnjqeuvpch8kge2c9h664gtk9d35gca3atq72pkae18qcruma580 -3cRdzzgTNrQgoAi1I1eXHsqY0zpJdsvE 6dhn4t3uf9kn8kkja5kpygb9654k2tar91tq2p9gf9r4mt3ket2g -pFMQgjn5FWdpCnqxqm3j0oEPEVGzsfOd e134umb7d9q3ahjqchr46vkhf1rpucva61qmam25at3qmwv69xj0 -ZG62aOvvUbqAZKdHxvOgOPp3JR8dh3m3 b93kcck19xv7cnb2e50nmjv491w7ckv79x870cuaa8w68u1kdmtg -f2oMOkAs2MU1J7J9eNcQeGNH3jgLG9GJ crt6ykafdd0q6cjdamrmmdua75jmwruhcn3mwj1kd9kmrhtt8x50 -EY885pHsSoJ8gW2ngATuzel0QAwVVY05 8nckge1ne1476mvf98w6entjdtkm2n3nf9jprc2h85vncnjt60ug -uMMzC6V7zY6ld30HCAMas0z3bOpbNq8N en6muyj36tb3eyjt6tp68ctg911m2kb1ecr7mcv29xr64kkh7170 -RzvsH0meJ1d8QLSfzv4LcZACYKO3gmQX a9x7cwu861ppajhhcgw52k2kctx7cd2ccdd42gut9d7k6tvda5c0 -UcsVC1g0Z8tcMqYOQP10Ti4RsDgmVtt7 anhq6nj365kk0phrehhmuwat9x8n0c9gahmk8mkk8hkpunkmegvg -NgGQaATEUBOfyLWdh4YUs0y0cSsB2wMd 9tkmemb185a4ana29xk7jk2qchm38panecr7jc33adtm4ckq9nj0 -Cwk5MgGPediBTmPgNsa1ydVYI9xT9FHv 8dvppdadcx3n0tb4d5158vagcx776r9hf5j5cpa975w58ea691v0 -ww5gMRiPg8uGRY154lC4ke3PbFnu6tuP exvkatuda9mn0ttren3n4p9h6mu6rgtmddjk6m328tq7adkmen80 -RSPwYpa7FKTYpKfsiFlA0FJ8ceHX1UWW a99n0xute1gkehjbahcq0jv6edmmcv216134me33cn45gcanaxbg -u61yYVGPh4LkMMcqHWtTRs4xyzpzmhUX emv32yatat3n0u1m9hnmukb3e545ex2ma9tk8y3tf9r7mvb8anc0 -39ZPPfseJq0eTgPOAjh7NmfuxyfgzTAL 6cwnmm2gcttpajkh61jn8tug9x0pmu1q9tppcxbrf5k6eyjm8560 -iXbpgqiGydsm4uA5qzU6ybPXkLkVseKO d5c64w37e5mmeyb4edpk8xa16nrqmn9pf5h50p3b9hnncwv59d7g -O20zi4BxwvbPqyibyHgDUpTyybVYd2GS 9wt30yk96h17gxvpc9872yb9c9wmgtu4anr58ybtc9b5jt1j8x9g -VhWJS3voHGageGZARbEU3tqXbPkrwYar atm5ejjk6dv6yj27c5kpahuu85964han6du72p32a1nq4xutc5t0 -lM3nraYaKpu4NworBsC5kchOzt2BsY7T dh6k6vkjc5cp2jvgemu4wxvfe9176gtnddhpgkvuegt44wut6xa0 -s3duWG5eARDbomkwg0YgeYnrRkKysUL7 ectp8xaq8wupagaj8hh6yvbbexkk0pb7cncpwwjjdd5qjwun9gvg -px1Pr26hljVNd0kPAD2ICvMKpWE5VPma e1w32m3j68v6gv3aat768c3ba10m8cj98dv4ujvgax2kanjgdngg -VKWb1ukKVe8bNiXSkca0PGXMFcfwDsFL at5nerhhennmpnk571h4wuaradnp6r9ga13ngka6cdk7eh3k8t60 -69jEZSgoLRJUQmXA0dkpUWxnoLkDNSgi 6rwpmhauadkpyk2j99an2var84r68uvganbqgvkf9hnm8kjkcxmg -aHpCPmNyLMyKh0KKiXCCUYkwXtuuNivn c5470gugdn77jk2df55pgc2b9dmnggu3ancppxurehuqakk9etq0 -jQgdbjqtyt8lveax3YsL2WGA26xbE7JD d98pet32d9rq8ybm71p7ctb1f0tnjwuc69bmeg9j6tw64h9q9920 -ZJEa5Q6h0ImKbV3yvVKNWRjbewHjiYsu b954ar9na4v6gc29dn5p4nhkf5v5cjueax96mrk5ex46muatedug -jhfmTc7eRLnAjhbe2ClgJZglmyUabnED d9m6cvamccvpamjcdt0pmu32cmt46v3799d6ev3df5ap2rke8n20 -SCZXxavS6tWWjix8DATULonJCSXqRE5O ad1nmp3rc5v56dkmaxbpmubr71242n2n9hqpwjj3adc72mj56n7g -hADiHEPe7vbWb1F4sKrB0AKI5K9OEY7D d10m8ua88n86advpc9bp4ca66htmpwj2610mpj9n9cwmyhat6x20 -Ju2N7dnc85Y0jjUHS5nAnL54hHWWqpql 99uk4khqchq66e1nb4r6mujn919kavj1dt63ad3891bnewbge5p0 -nf3VWSYW93Mp02azJKPkgE0DT4qLyWuT dtk36njqadcnee9k9nr30ck1f954pm3bcx2k0h2m6hrmryaqena0 -rz8nUlyDmAa9iE5IkdlTRRtit7cVw4l4 e9x3gvjndhwm8va1c4wpjh9n95np8v2ma9978ubm6xhncxtmdgu0 -4LNNuMtTotJsVPM4BJ4jZcOSiflxwmGa 6h64wkkn9nu58vvm99tncm2d6h14md3ab9hmymv9ctp7gxvd8xgg -edNXB9t1OmjAGChQublsLasWFTa8oiAq cnj4wp2275u32kvdd90megv8a5up4v3k9hgq6nu6ahgkgvv985rg -fsT9CNKDMSYkSQX8uVIwpBc39sV5IJ3x cttn8ea39t5m8kakb5nn6mar71uncjbqe1166cttedb3ajaa6dw0 -txWAoa9VKBsZl41DpIUu9MVoDpUzJRG3 ehw5egbfc4wncju2edd6rd1h8hr4jnbn756ncvu4e1aqmjjj8wtg -04lUVZqir0rUpAiVzHMSuaupznH2wbHt 60u6rnapb9rpjwhge9aq0gb9atx4gkakengqaw3udt434xv291u0 -Jo7KbPmhw3llN7IfXrTSugD7wzx5Nu6q 99qkejv2a1ppgxtkdhp4wdu9ctc74n2kenkm8dvqf9w3akkn6trg -qWwe6QlpW1XyOoZkUBb4btOZNuBuBtzc e5bqet9pa5p70nthb1wmyvuuddam4rhmc9u4ypjeen17agkmf9hg -7rvWLQOgmzuyPlBVbUbFTvT8AoessVAK 6xt7cnuca57pevbuenwn0v22ath5arj6ahv58e21dxjq6wup855g -05G43L3VluKAcw8RI8LKJMlH1gsqSs75 60umed1k9gtncv3n9d0p6xtra94kgk2b996prj1hcxtq2mvk6wug -EVvkGPriNm4nfgGG7LUqyclyl1YdaqrR 8nb7cuu7a1t6jkkd6hq6ctu78wvmrnbhf5hprybc65cp8rbhe990 -mMCMfuVCscUfmflegQqAJ1OtQVhAtQAa dn6m6kb6enb46wv3ank6utkccnkn2wa198rmyx2hatm42x2h85gg -lN0FeF3AckoCZa8KxV4Q313c04kEiFPs dh730hk58rtm2rvbdx1nmr9r9dw5cd2h6crk6rtg6hnmaua6a1tg -IyO7Fni7yoIxfsmZ3M9ySfmT1Rrm1J5E 95wmydu6dtmkeybf95w6cwvdb8tmuebtadk6un1ha9t6ucaa6n2g -nZdKqyUvC3A3m6DjvcWbpkchlJTSP1OW dtd68jvhf5aqcgtk84tpudj4d9v66nv2e1np6u3c99a56m1h9xbg -SBKe8S0cmGRhfffZqK7ZYGZwTuNKqYMY ad14pt9racr66va7a9m6ctk6b9rmpduub53nmxumen74pwat9ncg -ZNIxMtj5TKE7YUJpYjFZq6UwYBs9qJGD b974jy2dehn3an2b8mvnjnaae1cpmhjue4v5axut89tkjwaa8x20 -C4NKQuRfkFoZGV2C0NHYGaFtY5LxEhar 8cu4wjuhen96cuu6dxd4enhj8cr4wj2t8xgmcx2t6n67ghb8c5t0 -RBMj51AMR2cpw2XjNcsTIm5crHQNlTH5 a914uuhn650mumhjcdr7ecjrd9766wum95pkarvj918mwv2m90ug -bImhQKJiOM3vuvnIZnt8g83Ij7AscB8q c94puu2h9d56jkud6dv7axke95d6wx1rcww36jba6x0q6ru271rg -Njb80rXan6dP4mN9bysJMiescEULffu7 9tn64e1ge9c62vhpch838vae75h7jwua9nmpawv38namrtk6emvg -s0QSlWmHHO5e4ZetRLfygBbhl9R8FDFK ecr52mvcaxpmgj2f6njk8pk5eh94rtktcx164u3c7593ghj48t5g -pfyo2tXAeItUqRCYmUjmbDysJq5hhHLS e1k7jvtjehc42ta9ehaq2mj3b5pnaukdc927jwuae4upgu289h9g -YYyvnU6xQ6u5PT4dLVdk5vJB4Mw8AJvF b5cqjxkeamv7gm9pemun0n1mch65ct3b6nv4mghm9nvkggaaet30 -pyPv0p1Tq01fuCY7h2Fm47giR22xM3Es e1wn0xhge0rn8w9g65k7agut6xm34hkd6gvpeuaj68t7gk9k8ntg -IDk6oGrOeedL6Gl3K5jqVHLbt73uFth4 9526pdkf8xt4ytb5ch63chvc6d5kaukhat44rrkm6wtqahkmd0u0 -UkwIivW1xThktGKLkTfDEeJxrUufMaHP annqejb9etbk2y2md1nq8hub9hnn8tj48njmmy3janupckb19180 -qaqtrrkS6PPhQPd4Yvn0NSKWcxRK2qiS e5gq2x3je9nn6djga1m52m346hcqcvhg9t9mpnv3f194pckhd59g -tlnSQgkCPmyNJb9V8ox1uHhMLqxXA1me ehp6wmuhcxnm6m3df574mrhtarw6yy1hen46gkace5w5gg9hdnjg -LPIdh560iBDET1PLL9dekq9ie5RiNO1v 9h84jt386mv30ua28h2n8cag9h63jt35ddrkjub56n96jkjf65v0 -gNLxs4iYw0ZqOat83PHFJwr2tgra9Blm cx74ry3k6hmnjxtgb9rmyrbm70tn0j2699vq4ckmcxt62ea2dhpg -CNMIWEdlhlwnIU7Iri5uq26IEfvwbl4V 8d74ujaq8nj6ru3cexq4jn9q95t6jdbne4t3cja5ctv7erkc6hb0 -EOaSsb0I1PIxfAvTIEtcFCPACvY9vhUH 8n7p2mvkc8r4jcag95w6cgbpah4max338t1n0ga3etckjxk8an40 -1psFsrYF322gzRlGhs6tlcQd3gzcO6Mk 65r76hkke9cmcctj69kqmmkc8xm76dkmdhhn2t1kcxx66ktp9nng -0VcQfkvd4lsCteETNwpvGkFSlRRAFHB4 61b66mb6ddv68d3ced1q8ta5ah77ew3p8xnmcmvca9942hj888u0 -5AV2qmEBRcnv4BxXfRiVSXBAD4Vkvz7v 6n0ncckhdn2m4mk3dtv38gkrb1k54uapadc44ga46hb6pxku6xv0 -JK6YjyFa2sfnKKKaWNCSozP8zAKdEcHJ 995kcpbaf5362ckkctq4pjubc5bmwgukdxx50e3u855p8hb39150 -j9bIKwOjP9Y5vSwKtaOuKo9XhQe3b0i4 d8wp4jabex7pmm1tb4uqcmvq9du62kvn9dqkjp38a5jk6rhgd4u0 -8r8vbFSuTdlOYVeYxoO5h6R8Y8FV9qBz 71t3gxk28t9qan34dh7njnk5b5w6yktnd0v54e2t7135cebh89x0 -YTYBBfFZ1Nqq6On0z32tL4bZ1sGUm2rS b5a5jgj2ct35mcaee5rkckve61x36ckm9gu64phhed3nav9je99g -hmhB69PQSdiYYWLgpdbiiz1OuAW9ZDJO d1ppgghp75852mv4d5cnjnuccxr68rk9d5x32kvn85bkjpj4997g -5QOFAI4476YKeVlQJNxRcyAlGJiv1CsF 6n8myhj194u38dtpb55pankca554wy2jcdwm2v2799mqcca3ed30 -48aovdeER1alwOrtRFibWECac7xfoVA4 6gw62vvpchjmamhhc5p7ekvjeh94cub2ax2m6rb36xw6cvup84u0 -jlUBp5TFImugrVlJ2rXrrlCFbhgr9CI7 d9p5agkg6na4cjbdenkq4nkc98t74p3je9p46hk2d1kq4ea394vg -XVC76V3ilx5EuA5KiOW5fkdC8KpLCt93 b1b46dtpartpjv3r6n2qag9n9dmmyntnctnp8gtr9dr4rgvm74tg -Hy7jabqnIFIDIN0nM6nB50dc38ZzyrpS 91wkeuk1c9rpwja69524jkhgdt6kcvj26mr68rtk71d7mybje19g -R2vd02Uw4k0lbvWpqywc1SQ31lCTgqzj a8t7ct1g69aqed3b61p64xjqe1rqjxv3659n2cthdh1n8tvhf9n0 -lg7hkzs3sKZYWkadhfDqgys2GEBqaUAV dhkkeu3bf9tk6wubb9cneuv1chm6ch3hcxwq6cj78n172ran85b0 -G2edtxxHMyS64ADJYHL1fiJXQ2MAIiMD 8wt6at3mf1w4gkbtacv38ga499cmgk1hctmmmp2h696m2jb99n20 -uUKkY5YLQtfF3iauCN6vOhbfrtTyX52P enampuut6ncmrmbmct336ub1en1mwdkp9xm64tkjeha7jp1n6980 -gH2Z7zREhufo8Z33cv4aOYTdA4r82A5C cx434phqf994au3nctqkgphk6dhqcd319xcn8t216ht3gcj16n1g -VAlcjGxdsuyoQF0TmMXPC4nrbPUAW8tY at0prrva8xw68wvnf5qn2hhgahpmup2g8cu6wwk2a1am2ntrehcg -tp22umH9pgepbcGbd98glzXC6siBSpX8 ehr34ckndn43jw37cnr64ru7c9j3je37dhx5ggtpedmm4mvgb0w0 -DPjtne4Rx7LmM1ma2Ko38fmD1bS2Y8Ey 8h86mx3ecmu54y1q9hpmucbdc4t4pvtk71k6uh1hc99k4p9r8nwg -Jg4XKOTaUOUIuKJkwmn2MRFdb5tEG8l7 99kk8p2b9xa62nafan4qajuaddvpuvhj9n94ct326nu4ahtrdgvg -8PYYHhs9BIfkL40UIRUEPCbuJD8il34f 7185jpa8d1tkjgj9ctnmrd1gan4n4na5a11p4xaa8gw6jv1k6hk0 -ocK5jh8ob8tfM39WTqHeLUHCQ42YczDE dxhmpdbad0w6yrhrehk4ucttaxa72j359hamgguh6gt5jrvu8h2g -DiKQIfQsURKDhsV3YYN654XMjkJ8zTuF 8hmmpma9ct8q6naj9d26gwup6dcnjkhp6mu5gkbadd53gyjmen30 -iQv0hMYnHKY4fUlRxU1jzbXFWctopOFL d58qcc389ncpwj2bb4u6cnbca9w5acbaf9h5ghjqcdu6yw2f8t60 -egtoDMg99BYfeoir23IL4smvc7sFu0MR cnkq8vu49nkkjea2b5k6avv9e8t36jac6htpuxk36xtmcx9g9n90 -nkpMIVyjlC9nyAdUNE4p3R2bcVozC1PQ dtnq0ka9atwpmv2375q7jgb4an74ad3g6d934rk3atqqmgtha18g -47R6uQf4T6cqkfa3EFzlXqG9CPWwpYsL 6gvn4dkna5k38n1pcdrpptk16d2mcykcb1rmeea3a1bqew2ted60 -ztj08l1pw53qIhmsmAa6RB7vq7iN8bVB f9u6mc1rdgrq0xtn6drmju3dedpm2r9pa913exkh6xmmwe32at10 -mo2IgTQskfOELoerR0WpFmxo5nQqMdPi dnqk4jb7ah8q6uv69x2mrvv5e9930nvg8tpqgvtndt8q2kb4a1mg -7ka1uPK6nNC20lpx8QJ99zjWpcMhfl8Y 6xnp2cbna15kcvje8ct30v3gf0w52jht75x6mnvgcd6pgtkc71cg -2Gwxc1Yg1NGyUluFFB9ERUaqA4FvMJqi 693qey3365cpecae8xwnav3n8t344ea5a9ap2wa16h37ckaae5mg -o2ObV9zcMic9nwcTMWcn729KIwyi9LNU dwt4yrjp75x66kb9ccwpwxv3ah6nerve6wt3jju9exwpjeac9tag -dCxEkhNpMtSQWWg5kPznMFThKOZgYldx ch1qghbbd1770kbmad8nenv76nnn0yke9n358u2b9xd6epbcchw0 -o9Uhsyj9XJ82DotcWN7Pk9SodIYtGeTz dwwnau3kf5n3jp2a70t48vvmcdbmwdugdcwn6vv495cq8hv5ahx0 -nbU2UDaPhyPwucMgY3WzPvzyeP9MLuex dth5acjn8hgn0u3ta1vqarudcxck6nvua1v7myb5a0wmuk3ncnw0 -iDLtrNklEkrtOg1dPRjz9Dhj5ogYPK0W d524rx3j9tnprhbbe9u4ytthch854uku7526guhndxknjm2b61bg -ANye2sE25HnoMNWaO4aj4KuEh8ht0BjF 8577jt9jed2k4da8dtqmukjqc57k8rba6h5qahb871m78c22d930 -6dmblJvwYNHALPQyOK5oj8MF6Jg4m6Bm 6tj6urkc99v7epae910mrm2hf57mpdbfd8w4uhhp99kk8v9p89pg -0FhVgj7djLL9dpPHCBmuserMURatrRMZ 6136gnk7d8vp8ujc9gwp8w2g911m4vbnedjq4kana9gq8wjj9nd0 -V3A3aKX2ePHd0U9C6cYc5XioZSIRgB0X artm2cv19dc34tag91j30n9t8cv66pb36nc6jvuuad4n4tu261c0 -y4Ye1qb4PAjF6COwICdb9unkUQWqEi10 f4u5jt9he5h38m21d933cgufex4m6t3275upwuuna5bq2hb964r0 -13DCb1HKBj78L0V4fVELo6nCBelmAdwu 64tm8gv26544pgka6ww4rc2p6hk5chacdwv6wgu2cnp6ugb4exug -2YLF3hx3zQslPdAx0GnJR1XgI13MT65z 69cmrhhkd1w36yjhedp50t21f0r4evjaa8rngtu964tmun1p6nx0 -RgHeBA38R2h4RdZqpOM7mSZej3qZI2Tm a9kmgta284tkgmhjd0u54t2ue5r4yk9qdn9nmtba6drnmj9jahpg -SSR5xX1ACIog9B3PnozAdYYgHxZnenz5 ad9n4dbrb0rm2gu9dxkkjghka1q6yyj1chcnjtu8f1d6wtbef8ug -qGSTMqTLhm4PxEskXjIQmMKEegeuu2iY e53n6n2de5a4ru3d6h87ghbkddc6mjahdn6mphb5cxjqax9jd5cg -eti2kqJ7DqpDc0rFD6RSOtl1mKifIjJR cnu6jckbe553eh3he1266c3j8t23cmjk9xu6rcbd9dmpcjba9990 -IjRpgOQUNrRE7qdf1VK6SIww8Zc43H7h 95n54w379x8nakkja92kewb4crrncjtpad4qextrb9hk8cu86xm0 -eX7LwlcwIkq8wgRmjig5SRCwuh0zqdeZ cnc3ek3qdhhqejbbe4w7etujdnn6jttnad946xvnd0r7mwb4cnd0 -VBAt4TX16XsAkawd5XJ3w9SNt2yKIKyx at142x1mahc32djred0pprbqcgungjhkewwn6kkm69wmpjabf5w0 -xBKg48tgIjLrL1uBNsAfQbnKTM5RewO4 f114pttm71u6ejba9ht4rcbn89776gb6a5h6wjum9mun4tbq9wu0 -ZkleY6mpjuGbmTNSzIguaVhfuQqECEUu b9nprtat6tpq0ukn8xh6un2eadx4jtvnc5b6gtkna5rmagu5anug -3l06ikNY2AB2Xr75K7Ru8xbcBAcN4APv 6dp30dk9dd75jcj188t5gwhq6n5kemkn71w64ru285hmwd21a1v0 -3sJPUq5Wy2KfoWX9to44xmoWxfKA4FI9 6dtmmm2ne4uney9j9dk6ynur75u6yd1mf1ppynvrct5m2d2694wg -ujrqZ4rDNv7sku2EIVO6x0fB0xiZf4gU enn74wau6ht48kkp6xtppx9j8n4ncktpf0r6cghgf1mnmthmcxag -h8MfP5qufbPBknI75l42tKqLnCu5XhWF d0w4utjg6nrqatk2a116pvj96wuprd1jeh5q2k3e8dukap38ax30 -q2Trc0Nox9TnOXKx86sDtMKKwj9BCgun e4t58wk36176yy1tahq4yp2bf0w3cwu4eh6mpjvqd8wm4gv7enq0 -jPDm7W7Ayi72Ecr1iDmXrhIRm4eUsWAg d9848v9qawvm2yb96wt4arvj65mm8vare9m4jmkd6hjnawuq85kg -YANwQOqUkYPKkzgFQLDqrCwG3NH6NdOK b50mwxuh9xrnauuta15ppyk78t8mrh3he91qehtk9t43ckk49x5g -SuoxcSqIvqY5chX4N8okrMfA8IuTIIVy adupyy33adrmjxkhb4up6u2r6h73gvvbe96pcg9r95un8ja9atwg -SVntsw73uLR7IG69PJnXWHxRiYxVzJI9 adb6wx3kewvk6xaca8vmjhtp7584mvjrax47gmk9b5w5cyja94wg -voSyfDs07RLit4H2RDPJ5sJSZia79eYn etqn6yb68htk0duj9hmq8d2869948m2a6ntmmmuud5gkeeb5b5q0 -iuxDtvZxfmmKNrMfq4gUKfwqQbr8zCPg d5uqgh3metd7gtkddn5mwwjdctrk8tun9dk7ewahc9t3gyj3a1kg -rcnHq7DoxpLTscgTlnpnEUk3H7Otw5Yt e9hpwj3h6x26yy3g9ha76rv7ahp6ww3e8nappcu86x7q8xtnb5u0 -BV55S1RzDJJft0f5gFv2ZoYtRjmXAMfD 89b3adak6597mh2a99k78c366nkmcxhjb9qnjx2jd9pnggadct20 -ev1MDizz2eU5gyhajOsepTgf2GY3jkV7 cnv32ka4d5x7mck5amupeyb8c5n4ywv5e1a6ethj8xck6ukbarvg -K95tS53oFeSeH4OqWeTgFbx390RK3JoJ 9cwkax2k6mtpyhk5adjmgd2fe5bpan378th7gctt6194pcuadx50 -Ofqg6kqJ8KTPuyQFBDJPuP7ZWMnx9r4T 9xk72ttpddrmme2bah87ayah8t148jjgen83epjq9nq7gebj6ha0 -9vCNULAAB0QxxFk5pH0ITDyGYGJAH0aa 75v46kjn9h0m2ghga5w7ghkb6nr4gc29ah27jhut8x542j1gc5gg -GxVxUxZ4AHrN2u7OuJcCao5vUsPSDdTh 8xw5cy2nf1d38ga8e9734x9q9xummru3c5qkaxjned856h34ahm0 -IxC3yl9m7xoHmogjblN2Javg2gIV08qk 95w46cvtdgwpudvrdx46uvv7d9h6rkhj99gqcttjcx4ncc1re5ng -aeEgNUlzjz67A5ZgMMCq6ZmSfqkpkt39 c5jmatueanp7muku6rvm2daucx6mugvh6td6umv6e5nq0uvm6cwg -msEATuZBOs5hSuLNwPLwXi8nHRF2JCkH dntmagamend44kvk6nm56xac9tvn0k3qb1mkgvj8a9334jj3dd40 -QgiNdQmbL5LE7GHC3MLVuXLplK2byZGW a5kpjkk4a5pp4k1n9h2kehu88ctmuk2penc4rw3c9ct64yau8xbg -PXrz8JnVyGdJXDj1TpDCCdsgZK4oXLD6 a1c74yhr99q5cya7ch55gh3a65a70h238dj76tuu9cu6yp2c8gv0 -VDqJrSqCBC9jqvfUibePEr8vGXUYD2XW at272jkjadrm6gj375n72xk6anmp4tag8nt3gxj7b1anjh1jb1bg diff --git a/Scripts/base32_crockford.py b/Scripts/base32_crockford.py new file mode 100644 index 0000000..f9c8f13 --- /dev/null +++ b/Scripts/base32_crockford.py @@ -0,0 +1,172 @@ +""" +base32-crockford +================ + +A Python module implementing the alternate base32 encoding as described +by Douglas Crockford at: http://www.crockford.com/wrmg/base32.html. + +He designed the encoding to: + + * Be human and machine readable + * Be compact + * Be error resistant + * Be pronounceable + +It uses a symbol set of 10 digits and 22 letters, excluding I, L O and +U. Decoding is not case sensitive, and 'i' and 'l' are converted to '1' +and 'o' is converted to '0'. Encoding uses only upper-case characters. + +Hyphens may be present in symbol strings to improve readability, and +are removed when decoding. + +A check symbol can be appended to a symbol string to detect errors +within the string. + +""" + +import re +import sys + +PY3 = sys.version_info[0] == 3 + +if not PY3: + import string as str + + +__all__ = ["encode", "decode", "normalize"] + + +if PY3: + string_types = str, +else: + string_types = basestring, + +# The encoded symbol space does not include I, L, O or U +symbols = '0123456789ABCDEFGHJKMNPQRSTVWXYZ' +# These five symbols are exclusively for checksum values +check_symbols = '*~$=U' + +encode_symbols = dict((i, ch) for (i, ch) in enumerate(symbols + check_symbols)) +decode_symbols = dict((ch, i) for (i, ch) in enumerate(symbols + check_symbols)) +normalize_symbols = str.maketrans('IiLlOo', '111100') +valid_symbols = re.compile('^[%s]+[%s]?$' % (symbols, + re.escape(check_symbols))) + +base = len(symbols) +check_base = len(symbols + check_symbols) + + +def encode(number, checksum=False, split=0): + """Encode an integer into a symbol string. + + A ValueError is raised on invalid input. + + If checksum is set to True, a check symbol will be + calculated and appended to the string. + + If split is specified, the string will be divided into + clusters of that size separated by hyphens. + + The encoded string is returned. + """ + number = int(number) + if number < 0: + raise ValueError("number '%d' is not a positive integer" % number) + + split = int(split) + if split < 0: + raise ValueError("split '%d' is not a positive integer" % split) + + check_symbol = '' + if checksum: + check_symbol = encode_symbols[number % check_base] + + if number == 0: + return '0' + check_symbol + + symbol_string = '' + while number > 0: + remainder = number % base + number //= base + symbol_string = encode_symbols[remainder] + symbol_string + symbol_string = symbol_string + check_symbol + + if split: + chunks = [] + for pos in range(0, len(symbol_string), split): + chunks.append(symbol_string[pos:pos + split]) + symbol_string = '-'.join(chunks) + + return symbol_string + + +def decode(symbol_string, checksum=False, strict=False): + """Decode an encoded symbol string. + + If checksum is set to True, the string is assumed to have a + trailing check symbol which will be validated. If the + checksum validation fails, a ValueError is raised. + + If strict is set to True, a ValueError is raised if the + normalization step requires changes to the string. + + The decoded string is returned. + """ + symbol_string = normalize(symbol_string, strict=strict) + if checksum: + symbol_string, check_symbol = symbol_string[:-1], symbol_string[-1] + + number = 0 + for symbol in symbol_string: + number = number * base + decode_symbols[symbol] + + if checksum: + check_value = decode_symbols[check_symbol] + modulo = number % check_base + if check_value != modulo: + raise ValueError("invalid check symbol '%s' for string '%s'" % + (check_symbol, symbol_string)) + + return number + + +def normalize(symbol_string, strict=False): + """Normalize an encoded symbol string. + + Normalization provides error correction and prepares the + string for decoding. These transformations are applied: + + 1. Hyphens are removed + 2. 'I', 'i', 'L' or 'l' are converted to '1' + 3. 'O' or 'o' are converted to '0' + 4. All characters are converted to uppercase + + A TypeError is raised if an invalid string type is provided. + + A ValueError is raised if the normalized string contains + invalid characters. + + If the strict parameter is set to True, a ValueError is raised + if any of the above transformations are applied. + + The normalized string is returned. + """ + if isinstance(symbol_string, string_types): + if not PY3: + try: + symbol_string = symbol_string.encode('ascii') + except UnicodeEncodeError: + raise ValueError("string should only contain ASCII characters") + else: + raise TypeError("string is of invalid type %s" % + symbol_string.__class__.__name__) + + norm_string = symbol_string.replace('-', '').translate(normalize_symbols).upper() + + if not valid_symbols.match(norm_string): + raise ValueError("string '%s' contains invalid characters" % norm_string) + + if strict and norm_string != symbol_string: + raise ValueError("string '%s' requires normalization" % symbol_string) + + return norm_string \ No newline at end of file diff --git a/Scripts/generate_testdata.py b/Scripts/generate_testdata.py index da6fdd7..3a1af9e 100644 --- a/Scripts/generate_testdata.py +++ b/Scripts/generate_testdata.py @@ -1,7 +1,9 @@ import uuid import base32_crockford -for _ in range(100): +for int in range(255): id = uuid.uuid4() - strck = base32_crockford.encode(id.int, checksum=False) - print(id, strck) \ No newline at end of file + intencoded = base32_crockford.encode(id.int, checksum=False) + intencodedcs = base32_crockford.encode(id.int, checksum=True) + #bytesencoded = base32_crockford.encode(id.bytes, checksum=False) + print(id, id.int, intencoded, intencodedcs) \ No newline at end of file diff --git a/Scripts/lint.sh b/Scripts/lint.sh index cd8e125..c9fd9e5 100755 --- a/Scripts/lint.sh +++ b/Scripts/lint.sh @@ -26,11 +26,9 @@ if [ "$LINT_MODE" == "NONE" ]; then elif [ "$LINT_MODE" == "STRICT" ]; then SWIFTFORMAT_OPTIONS="" SWIFTLINT_OPTIONS="--strict" - STRINGSLINT_OPTIONS="--config .strict.stringslint.yml" else SWIFTFORMAT_OPTIONS="" SWIFTLINT_OPTIONS="" - STRINGSLINT_OPTIONS="--config .stringslint.yml" fi pushd $PACKAGE_DIR @@ -41,7 +39,6 @@ if [ -z "$CI" ]; then fi $MINT_RUN periphery scan -$MINT_RUN stringslint lint $STRINGSLINT_OPTIONS $MINT_RUN swiftformat --lint $SWIFTFORMAT_OPTIONS . $MINT_RUN swiftlint lint $SWIFTLINT_OPTIONS diff --git a/Sources/Base32Crockford/Array.swift b/Sources/Base32Crockford/Array.swift index ac8da6c..56da5a1 100644 --- a/Sources/Base32Crockford/Array.swift +++ b/Sources/Base32Crockford/Array.swift @@ -17,6 +17,7 @@ extension Array where Element: FixedWidthInteger { } #if DEBUG + // periphery:ignore internal static func debugRandom( withCount count: Int, in range: ClosedRange? = nil, diff --git a/Sources/Base32Crockford/Base32CrockfordComparer.swift b/Sources/Base32Crockford/Base32CrockfordComparer.swift deleted file mode 100644 index 46a0b56..0000000 --- a/Sources/Base32Crockford/Base32CrockfordComparer.swift +++ /dev/null @@ -1,5 +0,0 @@ -import Foundation - -public protocol Base32CrockfordComparer { - func data(_ data: Data, hasEncodedPrefix prefix: String) -> Bool -} diff --git a/Sources/Base32Crockford/Base32CrockfordDecodingError.swift b/Sources/Base32Crockford/Base32CrockfordDecodingError.swift new file mode 100644 index 0000000..b6f6232 --- /dev/null +++ b/Sources/Base32Crockford/Base32CrockfordDecodingError.swift @@ -0,0 +1,31 @@ +import Foundation + +public struct Base32CrockfordDecodingError: Error { + public enum Details { + case checksum(Int, mismatchValue: Int?) + case invalidCharacter(Character) + } + + public let source: String + public let details: Details + + private init(source: String, details: Base32CrockfordDecodingError.Details) { + self.source = source + self.details = details + } + + internal static func checksumError( + from string: String, + actualValue: Int, + expectedValue: Int? + ) -> Base32CrockfordDecodingError { + .init(source: string, details: .checksum(actualValue, mismatchValue: expectedValue)) + } + + internal static func invalidCharacter( + _ character: Character, + from source: String + ) -> Base32CrockfordDecodingError { + .init(source: source, details: .invalidCharacter(character)) + } +} diff --git a/Sources/Base32Crockford/Base32CrockfordDecodingOptions.swift b/Sources/Base32Crockford/Base32CrockfordDecodingOptions.swift new file mode 100644 index 0000000..c48c2f2 --- /dev/null +++ b/Sources/Base32Crockford/Base32CrockfordDecodingOptions.swift @@ -0,0 +1,6 @@ +import Foundation + +public struct Base32CrockfordDecodingOptions { + public static let none = Base32CrockfordDecodingOptions(withChecksum: false) + public let withChecksum: Bool +} diff --git a/Sources/Base32Crockford/Base32CrockfordEncoding.swift b/Sources/Base32Crockford/Base32CrockfordEncoding.swift index 8a798b9..973f8b1 100644 --- a/Sources/Base32Crockford/Base32CrockfordEncoding.swift +++ b/Sources/Base32Crockford/Base32CrockfordEncoding.swift @@ -1,153 +1,102 @@ import Foundation -public struct Base32CrockfordEncodingOptions: OptionSet { - public static let withChecksum = Base32CrockfordEncodingOptions(rawValue: 1 << 0) - public static let none: Base32CrockfordEncodingOptions = [] - - public let rawValue: Int - - public init(rawValue: Int) { - self.rawValue = rawValue - } -} - -public typealias Base32CrockfordDecodingOptions = Base32CrockfordEncodingOptions - -// swiftlint:disable:next line_length -public struct Base32CrockfordEncoding: Base32CrockfordEncodingProtocol, Base32CrockfordComparer { - private struct ChecksumError: Error {} - - private static let _encoding = Base32CrockfordEncoding() - - public static var encoding: Base32CrockfordEncodingProtocol { - _encoding - } - - public static var comparer: Base32CrockfordComparer { - _encoding - } - - private static let characters = "0123456789abcdefghjkmnpqrtuvwxyz".uppercased() +public struct Base32CrockfordEncoding { + public static let encoding = Base32CrockfordEncoding() + private static let characters = "0123456789ABCDEFGHJKMNPQRSTVWXYZ" private static let checkSymbols = "*~$=U" - - private func sizeOf(extensionFrom string: String) -> Int { - let strBitCount = string.count * 5 - let dataBitCount = Int(floor(Double(strBitCount) / 8)) * 8 - return strBitCount - dataBitCount - } - - private func decodeWithoutExtension(base32Encoded string: String) -> Data { - let standardized = standardize(string: string) - let extensionSize = sizeOf(extensionFrom: standardized) - - return decode(standardizedString: standardized, withExtensionSize: extensionSize) - } - - private func verifyExtension(_ size: Int, _ standardized: String) throws { - let lastValue: UInt8? - if let last = standardized.last, size != 0 { - if let lastIndex = Base32CrockfordEncoding.characters.firstIndex( - of: last - ) { - lastValue = UInt8( - Base32CrockfordEncoding.characters.distance( - from: Base32CrockfordEncoding.characters.startIndex, - to: lastIndex - ) - ) - } else { - lastValue = nil - } - } else { - lastValue = nil - } - - if let lastValue = lastValue { - let extensionValue = (lastValue << (8 - size)) >> (8 - size) - guard extensionValue == 0 else { - throw ChecksumError() - } + public static let allChecksumSymbols = characters + checkSymbols + + private func validate( + _ result: Data, + from standardized: String, + withChecksum checksum: Character + ) throws { + let expected = Self.allChecksumSymbols.firstOffsetOf(character: checksum) + let actual = result.remainderBy(Self.allChecksumSymbols.count) + + guard expected == actual else { + throw Base32CrockfordDecodingError.checksumError( + from: standardized, + actualValue: actual, + expectedValue: expected + ) } } private func decode( standardizedString standardized: String, - withExtensionSize checksumSize: Int - ) -> Data { - let values = standardized.map { character -> Int in - guard let lastIndex = Base32CrockfordEncoding.characters.firstIndex( - of: character - ) else { - preconditionFailure("Invalid Characters should never be passed.") - } - return Base32CrockfordEncoding.characters.distance( - from: Base32CrockfordEncoding.characters.startIndex, - to: lastIndex - ) - } + options: Base32CrockfordDecodingOptions = .none + ) throws -> Data { + let (valueString, checksum) = standardized.split(withChecksum: options.withChecksum) + + let values = try valueString.offsets( + basedOnCharacterMap: Self.characters, + onInvalidCharacter: Base32CrockfordDecodingError.invalidCharacter(_:from:) + ) let bitString = values.map { String($0, radix: 2).pad(toSize: 5) }.joined() + let dataBytes = bitString.split(by: 8).compactMap { UInt8($0, radix: 2) } + let expectedByteCount = ((bitString.count - 1) / 8) + 1 - let bitStringWithoutChecksum = String( - bitString[ - bitString.startIndex ... - bitString.index(bitString.endIndex, offsetBy: -checksumSize - 1) - ] - ) - let dataBytes = bitStringWithoutChecksum.split(by: 8).compactMap { - UInt8($0, radix: 2) + // swiftlint:disable:next line_length + precondition(expectedByteCount == dataBytes.count, "Expected \(expectedByteCount) bytes from \(bitString.count) bits but received \(dataBytes.count)") + + let result = Data(dataBytes) + + guard let checksum = checksum else { + return result } - return Data(dataBytes) + + try validate(result, from: standardized, withChecksum: checksum) + + return result } - public func encode(data: Data, options _: Base32CrockfordEncodingOptions) -> String { - let dataBitCount = data.count * 8 - let resBitCount = Int(ceil(Double(dataBitCount) / 5) * 5.0) - let difference = resBitCount - dataBitCount - let lastSegment = difference == 0 ? 0 : 5 - difference - var binary = Binary(data: data) + public func encode( + data: Data, + options: Base32CrockfordEncodingOptions = .none + ) -> String { var encodedString = "" var index: Int? + var binary = Binary(data: data, sectionSize: 5) repeat { - index = binary.next(bits: 5) + index = binary.nextSection() guard let index = index else { break } - let characterIndex = Base32CrockfordEncoding.characters.index( - Base32CrockfordEncoding.characters.startIndex, offsetBy: index - ) encodedString.append( - Base32CrockfordEncoding.characters[characterIndex] + Self.characters.characterAtOffset(index) ) } while index != nil - if lastSegment > 0 { - let lastIndex = (binary.next(bits: lastSegment)! << difference) - let characterIndex = Base32CrockfordEncoding - .characters - .index( - Base32CrockfordEncoding.characters.startIndex, - offsetBy: lastIndex + if options.contains(.withChecksum) { + encodedString.append( + Self.allChecksumSymbols.characterAtOffset( + data.remainderBy( + Self.allChecksumSymbols.count + ) ) - encodedString.append(Base32CrockfordEncoding.characters[characterIndex]) + ) } + return encodedString + .replacingOccurrences(of: "^0+", with: "", options: .regularExpression) } public func decode( base32Encoded string: String, - options _: Base32CrockfordDecodingOptions + options: Base32CrockfordDecodingOptions = .none ) throws -> Data { let standardized = standardize(string: string) - let extensionSize = sizeOf(extensionFrom: standardized) - try verifyExtension(extensionSize, standardized) - - return decode(standardizedString: standardized, withExtensionSize: extensionSize) + return try decode(standardizedString: standardized, options: options) } - public func data(_ data: Data, hasEncodedPrefix prefix: String) -> Bool { - let prefixData = decodeWithoutExtension(base32Encoded: prefix) - return zip(data, prefixData).allSatisfy { $0 == $1 } + public func standardize(string: String) -> String { + string + .uppercased() + .replacingOccurrences(of: "O", with: "0") + .replacingOccurrences(of: "I", with: "1") + .replacingOccurrences(of: "L", with: "1") } } diff --git a/Sources/Base32Crockford/Base32CrockfordEncodingOptions.swift b/Sources/Base32Crockford/Base32CrockfordEncodingOptions.swift new file mode 100644 index 0000000..df077ce --- /dev/null +++ b/Sources/Base32Crockford/Base32CrockfordEncodingOptions.swift @@ -0,0 +1,12 @@ +import Foundation + +public struct Base32CrockfordEncodingOptions: OptionSet { + public static let withChecksum = Base32CrockfordEncodingOptions(rawValue: 1 << 0) + public static let none: Base32CrockfordEncodingOptions = [] + + public let rawValue: Int + + public init(rawValue: Int) { + self.rawValue = rawValue + } +} diff --git a/Sources/Base32Crockford/Base32CrockfordEncodingProtocol.swift b/Sources/Base32Crockford/Base32CrockfordEncodingProtocol.swift deleted file mode 100644 index 75f3768..0000000 --- a/Sources/Base32Crockford/Base32CrockfordEncodingProtocol.swift +++ /dev/null @@ -1,31 +0,0 @@ -import Foundation - -public protocol Base32CrockfordEncodingProtocol: Base32CrockfordGenerator { - static var encoding: Base32CrockfordEncodingProtocol { get } - - func encode(data: Data, options: Base32CrockfordEncodingOptions) -> String - func decode( - base32Encoded string: String, - options: Base32CrockfordDecodingOptions - ) throws -> Data -} - -extension Base32CrockfordEncodingProtocol { - public func encode(data: Data) -> String { - encode(data: data, options: .none) - } - - public func decode(base32Encoded string: String) throws -> Data { - try decode(base32Encoded: string, options: .none) - } -} - -extension Base32CrockfordEncodingProtocol { - public func standardize(string: String) -> String { - string - .uppercased() - .replacingOccurrences(of: "O", with: "0") - .replacingOccurrences(of: "I", with: "1") - .replacingOccurrences(of: "L", with: "1") - } -} diff --git a/Sources/Base32Crockford/Base32CrockfordGenerator.swift b/Sources/Base32Crockford/Base32CrockfordGenerator.swift deleted file mode 100644 index 1168058..0000000 --- a/Sources/Base32Crockford/Base32CrockfordGenerator.swift +++ /dev/null @@ -1,120 +0,0 @@ -import Foundation - -public protocol Base32CrockfordGenerator { - func generateIdentifier(from identifierDataType: IdentifierDataType) -> String -} - -extension Base32CrockfordEncodingProtocol { - private func generateFromUUID() -> String { - let uuid = UUID() - let bytes = ByteCollection(uuid: uuid) - let data = Data(bytes) - return encode(data: data) - } - - private func generateSingle() -> String { - generate(withByteSize: 5) - } - - private func generate(withByteSize size: Int) -> String { - let data = Data.random(withNumberOfBytes: size) - return encode(data: data) - } - - private func generate( - forMinimumUniqueCount count: Int, - fatalError: ((String?) -> Void)? = nil - ) -> String? { - // swiftlint:disable empty_count - guard count > 0 else { - if count == 0 { - return "" - } else { - // swiftlint:enable empty_count - if let fatalError = fatalError { - fatalError("Cannot construct String identifier for unique count less than 0.") - return nil - } else { - Swift.fatalError( - "Cannot construct String identifier for unique count less than 0." - ) - } - } - } - - let data = Data.uniqueIdentifier(forMinimumCount: count) - return encode(data: data) - } - - public func generateIdentifier(from identifierDataType: IdentifierDataType) -> String { - // swiftlint:disable:next force_unwrapping - generateIdentifier(from: identifierDataType)! - } - - private func generateIdentifier( - from identifierDataType: IdentifierDataType, - fatalError: ((String?) -> Void)? = nil - ) -> String? { - switch identifierDataType { - case .default: - return generateSingle() - - case .uuid: - return generateFromUUID() - - case let .bytes(size): - return generate(withByteSize: size) - - case let .minimumCount(count): - return generate(forMinimumUniqueCount: count, fatalError: fatalError) - } - } - - private func generate( - _ count: Int, - from identifierDataType: IdentifierDataType, - fatalError: ((String?) -> Void)? = nil - ) -> [String]? { - // swiftlint:disable empty_count - guard count >= 0 else { - if let fatalError = fatalError { - fatalError("Array count cannot be less than 0.") - return nil - } else { - Swift.fatalError("Array count cannot be less than 0.") - } - } - guard count > 0 else { - return [String]() - } - // swiftlint:enable empty_count - return (1 ... count).map { _ in - self.generateIdentifier(from: identifierDataType) - } - } - - public func generate( - _ count: Int, - from identifierDataType: IdentifierDataType - ) -> [String] { - // swiftlint:disable:next force_unwrapping - generate(count, from: identifierDataType, fatalError: nil)! - } - - #if DEBUG - internal func debugGenerate( - _ count: Int, - from identifierDataType: IdentifierDataType, - fatalError: ((String?) -> Void)? = nil - ) -> [String]? { - generate(count, from: identifierDataType, fatalError: fatalError) - } - - internal func debugGenerateIdentifier( - from identifierDataType: IdentifierDataType, - fatalError: ((String?) -> Void)? = nil - ) -> String? { - generateIdentifier(from: identifierDataType, fatalError: fatalError) - } - #endif -} diff --git a/Sources/Base32Crockford/Binary.swift b/Sources/Base32Crockford/Binary.swift index b0ca5fc..7aec682 100644 --- a/Sources/Base32Crockford/Binary.swift +++ b/Sources/Base32Crockford/Binary.swift @@ -1,19 +1,25 @@ import Foundation public struct Binary { + public let sectionSize: Int public let bytes: [UInt8] public var readingOffset: Int = 0 public let byteSize: Int - public init(data: Data, byteSize: Int = 8) { + public init(data: Data, sectionSize: Int, byteSize: Int = 8) { self.byteSize = byteSize let bytesLength = data.count var bytesArray = [UInt8](repeating: 0, count: bytesLength) (data as NSData).getBytes(&bytesArray, length: bytesLength) bytes = bytesArray + self.sectionSize = sectionSize + readingOffset = (data.count * byteSize) % sectionSize - sectionSize } public func bit(_ position: Int) -> Int { + guard position >= 0 else { + return 0 + } let bytePosition = position / byteSize let bitPosition = (byteSize - 1) - (position % byteSize) let byte = self.byte(bytePosition) @@ -44,10 +50,10 @@ public struct Binary { (bytes.count * byteSize) >= (readingOffset + length) } - public mutating func next(bits length: Int) -> Int? { - if bitsWithInternalOffsetAvailable(length) { - let returnValue = bits(readingOffset, length) - readingOffset += length + public mutating func nextSection() -> Int? { + if bitsWithInternalOffsetAvailable(sectionSize) { + let returnValue = bits(readingOffset, sectionSize) + readingOffset += sectionSize return returnValue } else { return nil diff --git a/Sources/Base32Crockford/Data.swift b/Sources/Base32Crockford/Data.swift index 5313af8..e8183ef 100644 --- a/Sources/Base32Crockford/Data.swift +++ b/Sources/Base32Crockford/Data.swift @@ -1,17 +1,21 @@ import Foundation -public typealias ByteCollection = [UInt8] - extension Data { - public static func random(withNumberOfBytes count: Int) -> Data { - Data(ByteCollection.random(withCount: count)) - } - - public static func bytesRequired(forUniqueCountOf count: Int) -> Int { - Int(ceil(log(Double(count)) / log(256.0))) + public func remainderBy(_ divisor: Int) -> Int { + var remainder = 0 + var number = self + for (index, value) in number.enumerated() { + let temp = remainder * 256 + Int(value) + number[index] = UInt8(temp / divisor) + remainder = temp % divisor + } + return remainder } - public static func uniqueIdentifier(forMinimumCount count: Int) -> Data { - random(withNumberOfBytes: bytesRequired(forUniqueCountOf: count)) + public func trim(to count: Int, andPadWith fill: UInt8 = 0) -> Data { + let fillSize = Swift.max(count - self.count, 0) + let fillData = Data(repeating: fill, count: fillSize) + let bytes = (fillData + self).suffix(count) + return Data(bytes) } } diff --git a/Sources/Base32Crockford/IdentifierDataType.swift b/Sources/Base32Crockford/IdentifierDataType.swift deleted file mode 100644 index 1b7cb56..0000000 --- a/Sources/Base32Crockford/IdentifierDataType.swift +++ /dev/null @@ -1,66 +0,0 @@ -import Foundation - -public struct InvalidIdentifierDataTypeError: Error {} - -public enum IdentifierDataType: Equatable { - case `default` - case uuid - case bytes(size: Int) - case minimumCount(Int) - - public enum CodingKeys: String, CodingKey { - case bytes - case minimumCount - case type - } -} - -extension IdentifierDataType: Codable { - public init(from decoder: Decoder) throws { - guard let container = try? decoder.container(keyedBy: CodingKeys.self) else { - throw InvalidIdentifierDataTypeError() - } - - if container.allKeys.contains(.bytes) { - guard let byteSize = try? container.decode(Int.self, forKey: .bytes) else { - throw InvalidIdentifierDataTypeError() - } - self = .bytes(size: byteSize) - return - } else if container.allKeys.contains(.minimumCount) { - guard let minimumCount = try? container.decode( - Int.self, forKey: .minimumCount - ) else { - throw InvalidIdentifierDataTypeError() - } - self = .minimumCount(minimumCount) - return - } else if container.allKeys.contains(.type) { - if (try? container.decode(String.self, forKey: .type)) == "uuid" { - self = .uuid - return - } else if (try? container.decodeNil(forKey: .type)) == true { - self = .default - return - } - } - throw InvalidIdentifierDataTypeError() - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - switch self { - case .uuid: - try container.encode("uuid", forKey: .type) - - case let .bytes(size): - try container.encode(size, forKey: .bytes) - - case let .minimumCount(count): - try container.encode(count, forKey: .minimumCount) - - default: - try container.encodeNil(forKey: .type) - } - } -} diff --git a/Sources/Base32Crockford/String.swift b/Sources/Base32Crockford/String.swift index 84c825e..bbe7d07 100644 --- a/Sources/Base32Crockford/String.swift +++ b/Sources/Base32Crockford/String.swift @@ -8,19 +8,62 @@ extension String { } public func split(by length: Int) -> [String] { - var startIndex = self.startIndex + var endIndex = self.endIndex var results = [Substring]() while startIndex < endIndex { - let endIndex = index( - startIndex, - offsetBy: length, - limitedBy: self.endIndex - ) ?? self.endIndex + let startIndex = index( + endIndex, + offsetBy: -length, + limitedBy: self.startIndex + ) ?? self.startIndex results.append(self[startIndex ..< endIndex]) - startIndex = endIndex + endIndex = startIndex } - return results.map { String($0) } + return results.reversed().map { String($0) } + } + + internal func characterAtOffset( + _ offset: Int, + fromIndex index: String.Index? = nil + ) -> Character { + let fromIndex = index ?? startIndex + let characterIndex = self.index(fromIndex, offsetBy: offset) + return self[characterIndex] + } + + internal func firstOffsetOf( + character: Character, + fromIndex index: String.Index? = nil + ) -> Int? { + let fromIndex = index ?? startIndex + guard let characterIndex = firstIndex(of: character) else { + return nil + } + return distance(from: fromIndex, to: characterIndex) + } + + internal func split(withChecksum: Bool) -> (String, Character?) { + var valueString = self + let checksum: Character? + if withChecksum { + checksum = valueString.popLast() + } else { + checksum = nil + } + return (valueString, checksum) + } + + internal func offsets( + basedOnCharacterMap characterMap: String, + onInvalidCharacter throwsError: @escaping (Character, String) -> Error + ) throws -> [Int] { + try map { character -> Int in + guard let lastIndex = characterMap.firstOffsetOf(character: character) else { + throw throwsError(character, self) + } + return lastIndex + } } } diff --git a/Tests/Base32CrockfordTests/ArrayTests.swift b/Tests/Base32CrockfordTests/ArrayTests.swift index ebc3e06..bdd7e24 100644 --- a/Tests/Base32CrockfordTests/ArrayTests.swift +++ b/Tests/Base32CrockfordTests/ArrayTests.swift @@ -11,4 +11,24 @@ final class ArrayTests: XCTestCase { XCTAssertTrue(tested) XCTAssertNil(result) } + + func testGoodRandomArray() { + var tested = false + let result = Array.debugRandom(withCount: 100, in: 0 ... 100) { actual in + XCTAssertEqual(actual, "Array count cannot be less than 0.") + tested = true + } + XCTAssertFalse(tested) + XCTAssertEqual(result?.count, 100) + } + + func testEmptyRandomArray() { + var tested = false + let result = Array.debugRandom(withCount: 0, in: 0 ... 100) { actual in + XCTAssertEqual(actual, "Array count cannot be less than 0.") + tested = true + } + XCTAssertFalse(tested) + XCTAssertEqual(result?.count, 0) + } } diff --git a/Tests/Base32CrockfordTests/Base32CrockfordTests.swift b/Tests/Base32CrockfordTests/Base32CrockfordTests.swift index 2a20c88..4485908 100644 --- a/Tests/Base32CrockfordTests/Base32CrockfordTests.swift +++ b/Tests/Base32CrockfordTests/Base32CrockfordTests.swift @@ -2,158 +2,143 @@ import XCTest final class Base32CrockfordTests: XCTestCase { - func testExample() { - let b32cf = Base32CrockfordEncoding() - for length in 1 ... 20 { - let bytes = (0 ... length - 1).map { _ in - UInt8.random(in: 0 ... UInt8.max) - } - let expectedData = Data(bytes) - let encodedString = b32cf.encode(data: expectedData) - let actualData = try? b32cf.decode(base32Encoded: encodedString) - XCTAssertEqual(expectedData, actualData) + var parametersArray: [Parameters]! + struct Parameters { + internal init(uuid: UUID, integer: UInt128, encoded: String, encodedWithChecksum: String) { + self.uuid = uuid + self.integer = integer + self.encoded = encoded + self.encodedWithChecksum = encodedWithChecksum } - } - - func testMinimumUniqueCount() { - [Int].random(withCount: 20, in: 0 ... 256).forEach(minimumUniqueCount(_:)) - minimumUniqueCount(0) - } - - func testMinimumUniqueCountLessThanZero() { - minimumUniqueCountLessThanZero(-1) - minimumUniqueCountLessThanZero(Int.min) - } - func testUUID() { - let b32cf = Base32CrockfordEncoding() - (1 ... 20).forEach { _ in - - let uuidb32 = b32cf.generateIdentifier(from: .uuid) - let data: Data - do { - data = try b32cf.decode(base32Encoded: uuidb32) - _ = UUID(data: data) - } catch { - XCTFail(error.localizedDescription) + internal init?(line: String) { + guard !line.isEmpty else { + return nil } - } - } - - func testGenerateArray() { - [Int].random(withCount: 20, in: 0 ... 256).forEach(generateArrayTest(withCount:)) - generateArrayTest(withCount: 0) - } - - func testGenerateArrayLessThanZero() { - [Int].random(withCount: 3, in: Int.min ... -1).forEach(generateArrayLessThanZero(withCount:)) - - generateArrayLessThanZero(withCount: -1) - generateArrayLessThanZero(withCount: Int.min) - } - - func generateArrayLessThanZero(withCount count: Int) { - let expectedMessage = "Array count cannot be less than 0." - var actualMessage: String? - let expectation = self.expectation(description: "expectingFatalError") - let b32cf = Base32CrockfordEncoding() - let result = b32cf.debugGenerate( - count, - from: .default, - fatalError: { message in - actualMessage = message - expectation.fulfill() + let components = line.components(separatedBy: .whitespaces) + precondition(components.count == 4) + guard let uuid = UUID(uuidString: components[0]) else { + preconditionFailure() } - ) - waitForExpectations(timeout: 5) { error in - XCTAssertNil(error) - XCTAssertNil(result) - XCTAssertEqual(actualMessage, expectedMessage) + guard let integer = UInt128(components[1]) else { + preconditionFailure() + } + let encoded = components[2] + let encodedWithChecksum = components[3] + self.init(uuid: uuid, integer: integer, encoded: encoded, encodedWithChecksum: encodedWithChecksum) } - } - func generateArrayTest(withCount count: Int) { - let b32cf = Base32CrockfordEncoding() - let ids = b32cf.generate(count, from: .default) - XCTAssertEqual(ids.count, count) - XCTAssertNil(ids.first(where: { $0.count != 8 })) - XCTAssertNil(ids.first(where: { (try? b32cf.decode(base32Encoded: $0))?.count != 5 })) + let uuid: UUID + let integer: UInt128 + let encoded: String + let encodedWithChecksum: String } - func minimumUniqueCount(_ count: Int) { - let length: Int? - if count == 0 { - length = 0 - } else if count < 0 { - length = nil - } else { - let numberOfBytes = Int(ceil(log(Double(count)) / log(256.0))) - length = Int(ceil(Double(numberOfBytes) * 8.0 / 5.0)) + override func setUp() { + let pythonUrl = URL(fileURLWithPath: #file).deletingLastPathComponent().appendingPathComponent("../../Data/python") + guard let pythonText = try? String(contentsOf: pythonUrl) else { + return } - let b32cf = Base32CrockfordEncoding() - let string = b32cf.generateIdentifier(from: .minimumCount(count)) - XCTAssertEqual(string.count, length) + parametersArray = pythonText.components(separatedBy: .newlines).compactMap(Parameters.init) } - func minimumUniqueCountLessThanZero(_ count: Int) { - let expectedMessage = "Cannot construct String identifier for unique count less than 0." - var actualMessage: String? - let expectation = self.expectation(description: "expectingFatalError") - let b32cf = Base32CrockfordEncoding() - let result = b32cf.debugGenerateIdentifier(from: .minimumCount(count), fatalError: { message in - actualMessage = message - expectation.fulfill() - }) - waitForExpectations(timeout: 5) { error in - XCTAssertNil(error) - XCTAssertNil(result) - XCTAssertEqual(actualMessage, expectedMessage) + func testNumbersAndUUIDs() throws { + for (index, parameters) in parametersArray.enumerated() { + let divisor: UInt128 = 37 + let uuidData = Data(Array(uuid: parameters.uuid)) + let encodedUUID = Base32CrockfordEncoding.encoding.encode(data: uuidData) + let encodedInt = Base32CrockfordEncoding.encoding.encode(data: parameters.integer.data) + let encodedUUIDChecksum = Base32CrockfordEncoding.encoding.encode(data: uuidData, options: .withChecksum) + + let decodedUUIDBytes = try Base32CrockfordEncoding.encoding.decode(base32Encoded: parameters.encoded).trim(to: 16) + let decodedUUID = UUID(data: decodedUUIDBytes) + + let decodedUUIDChecksumBytes = try Base32CrockfordEncoding.encoding.decode(base32Encoded: parameters.encodedWithChecksum, options: .init(withChecksum: true)).trim(to: 16) + let decodedUUIDChecksum = UUID(data: decodedUUIDChecksumBytes) + + let moduloIndex = Base32CrockfordEncoding.allChecksumSymbols.firstIndex(of: parameters.encodedWithChecksum.last!)! + + let modulo = Base32CrockfordEncoding.allChecksumSymbols.distance( + from: Base32CrockfordEncoding.allChecksumSymbols.startIndex, + to: moduloIndex + ) + + let actualMod = Int(parameters.integer % divisor) + XCTAssertEqual(parameters.integer.data.count, 128 / 8) + XCTAssertEqual(encodedInt, encodedUUID) + XCTAssertEqual(encodedInt, parameters.encoded) + XCTAssertEqual(encodedUUID, parameters.encoded) + XCTAssertEqual(decodedUUID, parameters.uuid, "Invalid Row \(index)") + XCTAssertEqual(actualMod, modulo, "Invalid Row \(index)") + XCTAssertEqual(modulo, parameters.integer.data.remainderBy(37)) + XCTAssertEqual(encodedUUIDChecksum, parameters.encodedWithChecksum) + XCTAssertEqual(decodedUUIDChecksum, parameters.uuid) } } - func identifierDataType(_ identifierDataType: IdentifierDataType, isCodableWith string: String) { - let decoder = JSONDecoder() - let encoder = JSONEncoder() - - let expectedString = string.components(separatedBy: .whitespacesAndNewlines).joined(separator: "") - let actualIdentifierDataType = try? decoder.decode(IdentifierDataType.self, from: - string.data(using: .utf8)!) - XCTAssertEqual(actualIdentifierDataType, identifierDataType) - - let actualString = (try? encoder.encode(identifierDataType)).map { - String(data: $0, encoding: .utf8) + func testBadChecksum() { + let data = Data(Array(uuid: .init())) + let encodedWithChecksum = Base32CrockfordEncoding.encoding.encode(data: data, options: .withChecksum) + let (encoded, checksum) = encodedWithChecksum.split(withChecksum: true) + guard let checksum = checksum else { + XCTFail() + return } - - XCTAssertEqual(actualString, expectedString) - } - - func testIdentifierDataTypeCodable() { - identifierDataType(.minimumCount(10_000), isCodableWith: """ - { - "minimumCount" : 10000 + var badChecksum = checksum + repeat { + badChecksum = Base32CrockfordEncoding.allChecksumSymbols.randomElement()! + } while badChecksum == checksum + let badEncodedWithChecksum = encoded.appending(String(badChecksum)) + let mismatchValueExpected = Base32CrockfordEncoding.allChecksumSymbols.firstOffsetOf(character: badChecksum) + let badChecksumExpected = Base32CrockfordEncoding.allChecksumSymbols.firstOffsetOf(character: checksum) + do { + _ = try Base32CrockfordEncoding.encoding.decode(base32Encoded: badEncodedWithChecksum, options: .init(withChecksum: true)) + XCTFail() + } catch let error as Base32CrockfordDecodingError { + guard case let .checksum(badChecksumActual, mismatchValue: mismatchValueActual) = error.details else { + XCTFail() + return + } + XCTAssertEqual(mismatchValueActual, mismatchValueExpected) + XCTAssertEqual(badChecksumActual, badChecksumExpected) + return + } catch { + XCTFail() } - """) + } - identifierDataType(.bytes(size: 5), isCodableWith: """ - { - "bytes" : 5 + func testInvalidCharacter() { + let invalidCharacters = "!@#$%^&*()_-+{}[]:;<>,./?" + let expectedCharacter = invalidCharacters.randomElement()! + let badString = String("0123456789ABCDEFGHJKMNPQRSTVWXYZ".shuffled() + [expectedCharacter]) + XCTAssertEqual(badString.count, 33) + do { + _ = try Base32CrockfordEncoding.encoding.decode(base32Encoded: badString) + XCTFail() + } catch let error as Base32CrockfordDecodingError { + guard case let .invalidCharacter(actualCharacter) = error.details else { + XCTAssertNil(error) + return + } + XCTAssertEqual(actualCharacter, expectedCharacter) + } catch { + XCTAssertNil(error) } - """) + } - identifierDataType(.uuid, isCodableWith: """ - { - "type" : "uuid" - } - """) - identifierDataType(.default, isCodableWith: """ - { - "type" : null + func testRandomData() { + let b32cf = Base32CrockfordEncoding() + for length in 1 ... 20 { + let bytes = (0 ... length - 1).map { _ in + UInt8.random(in: 0 ... UInt8.max) + } + let expectedData = Data(bytes) + let encodedString = b32cf.encode(data: expectedData) + let actualData = try? b32cf.decode(base32Encoded: encodedString) + XCTAssertEqual( + expectedData.hexEncodedString().replacingOccurrences(of: "^0+", with: "", options: .regularExpression), actualData?.hexEncodedString().replacingOccurrences(of: "^0+", with: "", options: .regularExpression) + ) } - """) } - - static var allTests = [ - ("testExample", testExample) - ] } diff --git a/Tests/Base32CrockfordTests/Base32PatternTests.swift b/Tests/Base32CrockfordTests/Base32PatternTests.swift deleted file mode 100644 index 1aa4458..0000000 --- a/Tests/Base32CrockfordTests/Base32PatternTests.swift +++ /dev/null @@ -1,32 +0,0 @@ -@testable import Base32Crockford -import XCTest - -final class Base32EqualityTests: XCTestCase { - func testExample() { - let values = ["0": "O", "1": "I", "I": "L"] - let encoding = Base32CrockfordEncoding() - var checks = 0 - for _ in 0 ... 2_000 { - let id = UUID() - let data = Data(Array(uuid: id)) - let fullId = encoding.encode(data: data) - let shortId = String(fullId[fullId.startIndex ... fullId.index(fullId.startIndex, offsetBy: 4)]) - var shortValues = values.reduce([shortId]) { (shortValues, arg1) -> [String] in - - let (key, value) = arg1 - let current = shortValues.last ?? shortId - - return shortValues + [current.replacingOccurrences(of: key, with: value)] - } - shortValues.append((shortValues.last ?? shortId).lowercased()) - shortValues = [String](Set(shortValues)) - for aShortId in shortValues { - XCTAssert(Base32CrockfordEncoding.comparer.data(data, hasEncodedPrefix: aShortId)) - checks += 1 - } - if checks > 500 { - return - } - } - } -} diff --git a/Tests/Base32CrockfordTests/BinaryTests.swift b/Tests/Base32CrockfordTests/BinaryTests.swift new file mode 100644 index 0000000..560d4a8 --- /dev/null +++ b/Tests/Base32CrockfordTests/BinaryTests.swift @@ -0,0 +1,18 @@ +import Base32Crockford +import XCTest + +final class BinaryTests: XCTestCase { + func testBinaryValues() { + var value = Binary(data: .init([UInt8(8)]), sectionSize: 5) + XCTAssertEqual(value.readingOffset, -2) + var section: Int? + var lastSectionValue: Int = -1 + repeat { + section = value.nextSection() + if let section = section { + lastSectionValue = section + } + } while section != nil + XCTAssertEqual(8, lastSectionValue) + } +} diff --git a/Tests/Base32CrockfordTests/Data.swift b/Tests/Base32CrockfordTests/Data.swift new file mode 100644 index 0000000..88bdfc1 --- /dev/null +++ b/Tests/Base32CrockfordTests/Data.swift @@ -0,0 +1,14 @@ +@testable import Base32Crockford +import XCTest + +extension Data { + struct HexEncodingOptions: OptionSet { + let rawValue: Int + static let upperCase = HexEncodingOptions(rawValue: 1 << 0) + } + + func hexEncodedString(options: HexEncodingOptions = []) -> String { + let format = options.contains(.upperCase) ? "%02hhX" : "%02hhx" + return map { String(format: format, $0) }.joined() + } +} diff --git a/Tests/Base32CrockfordTests/EncodeDecodeTests.swift b/Tests/Base32CrockfordTests/EncodeDecodeTests.swift deleted file mode 100644 index 1106743..0000000 --- a/Tests/Base32CrockfordTests/EncodeDecodeTests.swift +++ /dev/null @@ -1,95 +0,0 @@ -@testable import Base32Crockford -import XCTest - -final class EncodeDecodeTests: XCTestCase { - var data: [String: String]! - var checksumData: [UUID: String]! - - override func setUp() { - let valuesUrl = URL(fileURLWithPath: #file).deletingLastPathComponent().appendingPathComponent("../../Data/values") - guard let allText = try? String(contentsOf: valuesUrl) else { - return - } - data = allText.components(separatedBy: .newlines).reduce([String: String]()) { data, line in - let components = line.components(separatedBy: .whitespaces) - guard let key = components.first, let value = components.last, components.first != components.last else { - return data - } - var dictionary = data - dictionary[key] = value - return dictionary - } - -// let checksumUrl = URL(fileURLWithPath: #file).deletingLastPathComponent().appendingPathComponent("../../Data/checksum") -// guard let checksumText = try? String(contentsOf: checksumUrl) else { -// return -// } -// checksumData = checksumText.components(separatedBy: .newlines).reduce([UUID: String]()) { data, line in -// let components = line.components(separatedBy: .whitespaces) -// guard let key = components.first, let value = components.last, components.first != components.last else { -// return data -// } -// var dictionary = data -// dictionary[UUID(uuidString: key)!] = value -// return dictionary -// } - } - - func testEncoding() { - for (value, expected) in data { - guard let data = value.data(using: .utf8) else { - XCTFail("Unable to create data from string") - continue - } - let actual = Base32CrockfordEncoding.encoding.encode(data: data).lowercased() - XCTAssertEqual(actual, expected) - } - } - -// func testEncodingWithChecksum () { -// - //// - //// for (uuid, expected) in checksumData { - //// print(uuid, expected) - //// -// let uuid = UUID(uuidString: "221b469c-d38d-417c-8faa-9113648240ec")! -// let data = Data(Array(uuid: uuid)) -// let actual = Base32CrockfordEncoding.encoding.encode(data: data, options: .withChecksum) -// XCTAssertEqual(actual, "123D39SMWD85Y8ZAMH2DJ84G7C") -// -// -// print(try! Base32CrockfordEncoding.encoding.decode(base32Encoded: actual, options: .withChecksum)) -// print(try! Base32CrockfordEncoding.encoding.decode(base32Encoded: "123D39SMWD85Y8ZAMH2DJ84G7C", options: .withChecksum)) - //// } -// } - - func decode(value: String, withExpected expected: Data) { - let actual: Data - do { - actual = try Base32CrockfordEncoding.encoding.decode(base32Encoded: value) - } catch { - XCTFail(error.localizedDescription) - return - } - XCTAssertEqual(actual, expected) - } - - func testDecoding() { - for (expectedString, value) in data { - guard let expected = expectedString.data(using: .utf8) else { - XCTFail("Unable to create data from string") - continue - } - var newValue = value - decode(value: value, withExpected: expected) - newValue = newValue.replacingOccurrences(of: "0", with: "O") - decode(value: newValue, withExpected: expected) - newValue = newValue.replacingOccurrences(of: "1", with: "L") - decode(value: newValue, withExpected: expected) - newValue = newValue.replacingOccurrences(of: "L", with: "I") - decode(value: newValue, withExpected: expected) - newValue = newValue.lowercased() - decode(value: newValue, withExpected: expected) - } - } -} diff --git a/Tests/Base32CrockfordTests/UInt128+Data.swift b/Tests/Base32CrockfordTests/UInt128+Data.swift new file mode 100644 index 0000000..06037e0 --- /dev/null +++ b/Tests/Base32CrockfordTests/UInt128+Data.swift @@ -0,0 +1,11 @@ +@testable import Base32Crockford +import XCTest + +extension UInt128 { + var data: Data { + var upperBits = byteSwapped.value.upperBits + var lowerBits = byteSwapped.value.lowerBits + return Data(bytes: &lowerBits, count: MemoryLayout.size) + + Data(bytes: &upperBits, count: MemoryLayout.size) + } +} diff --git a/Tests/Base32CrockfordTests/UInt128.swift b/Tests/Base32CrockfordTests/UInt128.swift new file mode 100644 index 0000000..237b3d4 --- /dev/null +++ b/Tests/Base32CrockfordTests/UInt128.swift @@ -0,0 +1,739 @@ +/// An `ErrorType` for `UInt128` data types. It includes cases +/// for errors that can occur during string +/// conversion. +public enum UInt128Errors: Error { + /// Input cannot be converted to a UInt128 value. + case invalidString +} + +// MARK: - Data Type + +/// A 128-bit unsigned integer value type. +/// Storage is based upon a tuple of 2, 64-bit, unsigned integers. +public struct UInt128 { + // MARK: Instance Properties + + /// Internal value is presented as a tuple of 2 64-bit + /// unsigned integers. + internal var value: (upperBits: UInt64, lowerBits: UInt64) + + /// Counts up the significant bits in stored data. + public var significantBits: UInt128 { + return UInt128(UInt128.bitWidth - leadingZeroBitCount) + } + + /// Undocumented private variable required for passing this type + /// to a BinaryFloatingPoint type. See FloatingPoint.swift.gyb in + /// the Swift stdlib/public/core directory. + internal var signBitIndex: Int { + return 127 - leadingZeroBitCount + } + + // MARK: Initializers + + /// Designated initializer for the UInt128 type. + public init(upperBits: UInt64, lowerBits: UInt64) { + value.upperBits = upperBits + value.lowerBits = lowerBits + } + + public init() { + self.init(upperBits: 0, lowerBits: 0) + } + + /// Initialize a UInt128 value from a string. + /// Returns `nil` if input cannot be converted to a UInt128 value. + /// + /// - parameter source: the string that will be converted into a + /// UInt128 value. Defaults to being analyzed as a base10 number, + /// but can be prefixed with `0b` for base2, `0o` for base8 + /// or `0x` for base16. + public init?(_ source: String) { + guard let result = UInt128._valueFromString(source) else { + return nil + } + self = result + } +} + +// MARK: - FixedWidthInteger Conformance + +extension UInt128: FixedWidthInteger { + // MARK: Instance Properties + + public var nonzeroBitCount: Int { + return value.lowerBits.nonzeroBitCount + value.upperBits.nonzeroBitCount + } + + public var leadingZeroBitCount: Int { + if value.upperBits == 0 { + return UInt64.bitWidth + value.lowerBits.leadingZeroBitCount + } + return value.upperBits.leadingZeroBitCount + } + + /// Returns the big-endian representation of the integer, changing the byte order if necessary. + public var bigEndian: UInt128 { + #if arch(i386) || arch(x86_64) || arch(arm) || arch(arm64) + return byteSwapped + #else + return self + #endif + } + + /// Returns the little-endian representation of the integer, changing the byte order if necessary. + public var littleEndian: UInt128 { + #if arch(i386) || arch(x86_64) || arch(arm) || arch(arm64) + return self + #else + return byteSwapped + #endif + } + + /// Returns the current integer with the byte order swapped. + public var byteSwapped: UInt128 { + return UInt128(upperBits: value.lowerBits.byteSwapped, + lowerBits: value.upperBits.byteSwapped) + } + + // MARK: Initializers + + /// Creates a UInt128 from a given value, with the input's value + /// truncated to a size no larger than what UInt128 can handle. + /// Since the input is constrained to an UInt, no truncation needs + /// to occur, as a UInt is currently 64 bits at the maximum. + public init(_truncatingBits bits: UInt) { + self.init(upperBits: 0, lowerBits: UInt64(bits)) + } + + /// Creates an integer from its big-endian representation, changing the + /// byte order if necessary. + public init(bigEndian value: UInt128) { + self = value.bigEndian + } + + /// Creates an integer from its little-endian representation, changing the + /// byte order if necessary. + public init(littleEndian value: UInt128) { + self = value.littleEndian + } + + // MARK: Instance Methods + + public func addingReportingOverflow(_ rhs: UInt128) -> (partialValue: UInt128, overflow: Bool) { + var resultOverflow = false + let (lowerBits, lowerOverflow) = value.lowerBits.addingReportingOverflow(rhs.value.lowerBits) + var (upperBits, upperOverflow) = value.upperBits.addingReportingOverflow(rhs.value.upperBits) + + // If the lower bits overflowed, we need to add 1 to upper bits. + if lowerOverflow { + (upperBits, resultOverflow) = upperBits.addingReportingOverflow(1) + } + + return (partialValue: UInt128(upperBits: upperBits, lowerBits: lowerBits), + overflow: upperOverflow || resultOverflow) + } + + public func subtractingReportingOverflow(_ rhs: UInt128) -> (partialValue: UInt128, overflow: Bool) { + var resultOverflow = false + let (lowerBits, lowerOverflow) = value.lowerBits.subtractingReportingOverflow(rhs.value.lowerBits) + var (upperBits, upperOverflow) = value.upperBits.subtractingReportingOverflow(rhs.value.upperBits) + + // If the lower bits overflowed, we need to subtract (borrow) 1 from the upper bits. + if lowerOverflow { + (upperBits, resultOverflow) = upperBits.subtractingReportingOverflow(1) + } + + return (partialValue: UInt128(upperBits: upperBits, lowerBits: lowerBits), + overflow: upperOverflow || resultOverflow) + } + + public func multipliedReportingOverflow(by rhs: UInt128) -> (partialValue: UInt128, overflow: Bool) { + let multiplicationResult = multipliedFullWidth(by: rhs) + let overflowEncountered = multiplicationResult.high > 0 + + return (partialValue: multiplicationResult.low, + overflow: overflowEncountered) + } + + public func multipliedFullWidth(by other: UInt128) -> (high: UInt128, low: UInt128.Magnitude) { + // Bit mask that facilitates masking the lower 32 bits of a 64 bit UInt. + let lower32 = UInt64(UInt32.max) + + // Decompose lhs into an array of 4, 32 significant bit UInt64s. + let lhsArray = [ + value.upperBits >> 32, /* 0 */ value.upperBits & lower32, /* 1 */ + value.lowerBits >> 32, /* 2 */ value.lowerBits & lower32 /* 3 */ + ] + + // Decompose rhs into an array of 4, 32 significant bit UInt64s. + let rhsArray = [ + other.value.upperBits >> 32, /* 0 */ other.value.upperBits & lower32, /* 1 */ + other.value.lowerBits >> 32, /* 2 */ other.value.lowerBits & lower32 /* 3 */ + ] + + // The future contents of this array will be used to store segment + // multiplication results. + var resultArray = [[UInt64]]( + repeating: [UInt64](repeating: 0, count: 4), count: 4 + ) + + // Loop through every combination of lhsArray[x] * rhsArray[y] + for rhsSegment in 0 ..< rhsArray.count { + for lhsSegment in 0 ..< lhsArray.count { + let currentValue = lhsArray[lhsSegment] * rhsArray[rhsSegment] + resultArray[lhsSegment][rhsSegment] = currentValue + } + } + + // Perform multiplication similar to pen and paper in 64bit, 32bit masked increments. + let bitSegment8 = resultArray[3][3] & lower32 + let bitSegment7 = UInt128._variadicAdditionWithOverflowCount( + resultArray[2][3] & lower32, + resultArray[3][2] & lower32, + resultArray[3][3] >> 32 + ) // overflow from bitSegment8 + let bitSegment6 = UInt128._variadicAdditionWithOverflowCount( + resultArray[1][3] & lower32, + resultArray[2][2] & lower32, + resultArray[3][1] & lower32, + resultArray[2][3] >> 32, // overflow from bitSegment7 + resultArray[3][2] >> 32, // overflow from bitSegment7 + bitSegment7.overflowCount + ) + let bitSegment5 = UInt128._variadicAdditionWithOverflowCount( + resultArray[0][3] & lower32, + resultArray[1][2] & lower32, + resultArray[2][1] & lower32, + resultArray[3][0] & lower32, + resultArray[1][3] >> 32, // overflow from bitSegment6 + resultArray[2][2] >> 32, // overflow from bitSegment6 + resultArray[3][1] >> 32, // overflow from bitSegment6 + bitSegment6.overflowCount + ) + let bitSegment4 = UInt128._variadicAdditionWithOverflowCount( + resultArray[0][2] & lower32, + resultArray[1][1] & lower32, + resultArray[2][0] & lower32, + resultArray[0][3] >> 32, // overflow from bitSegment5 + resultArray[1][2] >> 32, // overflow from bitSegment5 + resultArray[2][1] >> 32, // overflow from bitSegment5 + resultArray[3][0] >> 32, // overflow from bitSegment5 + bitSegment5.overflowCount + ) + let bitSegment3 = UInt128._variadicAdditionWithOverflowCount( + resultArray[0][1] & lower32, + resultArray[1][0] & lower32, + resultArray[0][2] >> 32, // overflow from bitSegment4 + resultArray[1][1] >> 32, // overflow from bitSegment4 + resultArray[2][0] >> 32, // overflow from bitSegment4 + bitSegment4.overflowCount + ) + let bitSegment1 = UInt128._variadicAdditionWithOverflowCount( + resultArray[0][0], + resultArray[0][1] >> 32, // overflow from bitSegment3 + resultArray[1][0] >> 32, // overflow from bitSegment3 + bitSegment3.overflowCount + ) + + // Shift and merge the results into 64 bit groups, adding in overflows as we go. + let lowerLowerBits = UInt128._variadicAdditionWithOverflowCount( + bitSegment8, + bitSegment7.truncatedValue << 32 + ) + let upperLowerBits = UInt128._variadicAdditionWithOverflowCount( + bitSegment7.truncatedValue >> 32, + bitSegment6.truncatedValue, + bitSegment5.truncatedValue << 32, + lowerLowerBits.overflowCount + ) + let lowerUpperBits = UInt128._variadicAdditionWithOverflowCount( + bitSegment5.truncatedValue >> 32, + bitSegment4.truncatedValue, + bitSegment3.truncatedValue << 32, + upperLowerBits.overflowCount + ) + let upperUpperBits = UInt128._variadicAdditionWithOverflowCount( + bitSegment3.truncatedValue >> 32, + bitSegment1.truncatedValue, + lowerUpperBits.overflowCount + ) + + // Bring the 64bit unsigned integer results together into a high and low 128bit unsigned integer result. + return (high: UInt128(upperBits: upperUpperBits.truncatedValue, lowerBits: lowerUpperBits.truncatedValue), + low: UInt128(upperBits: upperLowerBits.truncatedValue, lowerBits: lowerLowerBits.truncatedValue)) + } + + /// Takes a variable amount of 64bit Unsigned Integers and adds them together, + /// tracking the total amount of overflows that occurred during addition. + /// + /// - Parameter addends: + /// Variably sized list of UInt64 values. + /// - Returns: + /// A tuple containing the truncated result and a count of the total + /// amount of overflows that occurred during addition. + private static func _variadicAdditionWithOverflowCount(_ addends: UInt64...) -> (truncatedValue: UInt64, overflowCount: UInt64) { + var sum: UInt64 = 0 + var overflowCount: UInt64 = 0 + + addends.forEach { addend in + let interimSum = sum.addingReportingOverflow(addend) + if interimSum.overflow { + overflowCount += 1 + } + sum = interimSum.partialValue + } + + return (truncatedValue: sum, overflowCount: overflowCount) + } + + public func dividedReportingOverflow(by rhs: UInt128) -> (partialValue: UInt128, overflow: Bool) { + guard rhs != 0 else { + return (self, true) + } + + let quotient = quotientAndRemainder(dividingBy: rhs).quotient + return (quotient, false) + } + + public func dividingFullWidth(_ dividend: (high: UInt128, low: UInt128)) -> (quotient: UInt128, remainder: UInt128) { + return _quotientAndRemainderFullWidth(dividingBy: dividend) + } + + public func remainderReportingOverflow(dividingBy rhs: UInt128) -> (partialValue: UInt128, overflow: Bool) { + guard rhs != 0 else { + return (self, true) + } + + let remainder = quotientAndRemainder(dividingBy: rhs).remainder + return (remainder, false) + } + + public func quotientAndRemainder(dividingBy rhs: UInt128) -> (quotient: UInt128, remainder: UInt128) { + return rhs._quotientAndRemainderFullWidth(dividingBy: (high: 0, low: self)) + } + + /// Provides the quotient and remainder when dividing the provided value by self. + internal func _quotientAndRemainderFullWidth(dividingBy dividend: (high: UInt128, low: UInt128)) -> (quotient: UInt128, remainder: UInt128) { + let divisor = self + let numeratorBitsToWalk: UInt128 + + if dividend.high > 0 { + numeratorBitsToWalk = dividend.high.significantBits + 128 - 1 + } else if dividend.low == 0 { + return (0, 0) + } else { + numeratorBitsToWalk = dividend.low.significantBits - 1 + } + + // The below algorithm was adapted from: + // https://en.wikipedia.org/wiki/Division_algorithm#Integer_division_.28unsigned.29_with_remainder + + precondition(self != 0, "Division by 0") + + var quotient = UInt128.min + var remainder = UInt128.min + + for numeratorShiftWidth in (0 ... numeratorBitsToWalk).reversed() { + remainder <<= 1 + remainder |= UInt128._bitFromDoubleWidth(at: numeratorShiftWidth, for: dividend) + + if remainder >= divisor { + remainder -= divisor + quotient |= 1 << numeratorShiftWidth + } + } + + return (quotient, remainder) + } + + /// Returns the bit stored at the given position for the provided double width UInt128 input. + /// + /// - parameter at: position to grab bit value from. + /// - parameter for: the double width UInt128 data value to grab the + /// bit from. + /// - returns: single bit stored in a UInt128 value. + internal static func _bitFromDoubleWidth(at bitPosition: UInt128, for input: (high: UInt128, low: UInt128)) -> UInt128 { + switch bitPosition { + case 0: + return input.low & 1 + case 1 ... 127: + return input.low >> bitPosition & 1 + case 128: + return input.high & 1 + default: + return input.high >> (bitPosition - 128) & 1 + } + } +} + +// MARK: - BinaryInteger Conformance + +extension UInt128 { + // MARK: Instance Properties + + public static var bitWidth: Int { return 128 } +} + +extension UInt128: BinaryInteger { + // MARK: Instance Methods + + public var words: [UInt] { + return Array(value.lowerBits.words) + Array(value.upperBits.words) + } + + public var trailingZeroBitCount: Int { + if value.lowerBits == 0 { + return UInt64.bitWidth + value.upperBits.trailingZeroBitCount + } + return value.lowerBits.trailingZeroBitCount + } + + // MARK: Initializers + + public init?(exactly source: T) { + if source.isZero { + self = UInt128() + } else if source.exponent < 0 || source.rounded() != source { + return nil + } else { + self = UInt128(UInt64(source)) + } + } + + public init(_ source: T) { + self.init(UInt64(source)) + } + + // MARK: Type Methods + + public static func / (lhs: UInt128, rhs: UInt128) -> UInt128 { + let result = lhs.dividedReportingOverflow(by: rhs) + + return result.partialValue + } + + public static func /= (lhs: inout UInt128, rhs: UInt128) { + lhs = lhs / rhs + } + + public static func % (lhs: UInt128, rhs: UInt128) -> UInt128 { + let result = lhs.remainderReportingOverflow(dividingBy: rhs) + + return result.partialValue + } + + public static func %= (lhs: inout UInt128, rhs: UInt128) { + lhs = lhs % rhs + } + + /// Performs a bitwise AND operation on 2 UInt128 data types. + public static func &= (lhs: inout UInt128, rhs: UInt128) { + let upperBits = lhs.value.upperBits & rhs.value.upperBits + let lowerBits = lhs.value.lowerBits & rhs.value.lowerBits + + lhs = UInt128(upperBits: upperBits, lowerBits: lowerBits) + } + + /// Performs a bitwise OR operation on 2 UInt128 data types. + public static func |= (lhs: inout UInt128, rhs: UInt128) { + let upperBits = lhs.value.upperBits | rhs.value.upperBits + let lowerBits = lhs.value.lowerBits | rhs.value.lowerBits + + lhs = UInt128(upperBits: upperBits, lowerBits: lowerBits) + } + + /// Performs a bitwise XOR operation on 2 UInt128 data types. + public static func ^= (lhs: inout UInt128, rhs: UInt128) { + let upperBits = lhs.value.upperBits ^ rhs.value.upperBits + let lowerBits = lhs.value.lowerBits ^ rhs.value.lowerBits + + lhs = UInt128(upperBits: upperBits, lowerBits: lowerBits) + } + + /// Perform a masked right SHIFT operation self. + /// + /// The masking operation will mask `rhs` against the highest + /// shift value that will not cause an overflowing shift before + /// performing the shift. IE: `rhs = 128` will become `rhs = 0` + /// and `rhs = 129` will become `rhs = 1`. + public static func &>>= (lhs: inout UInt128, rhs: UInt128) { + let shiftWidth = rhs.value.lowerBits & 127 + + switch shiftWidth { + case 0: return // Do nothing shift. + case 1 ... 63: + let upperBits = lhs.value.upperBits >> shiftWidth + let lowerBits = (lhs.value.lowerBits >> shiftWidth) + (lhs.value.upperBits << (64 - shiftWidth)) + lhs = UInt128(upperBits: upperBits, lowerBits: lowerBits) + case 64: + // Shift 64 means move upper bits to lower bits. + lhs = UInt128(upperBits: 0, lowerBits: lhs.value.upperBits) + default: + let lowerBits = lhs.value.upperBits >> (shiftWidth - 64) + lhs = UInt128(upperBits: 0, lowerBits: lowerBits) + } + } + + /// Perform a masked left SHIFT operation on self. + /// + /// The masking operation will mask `rhs` against the highest + /// shift value that will not cause an overflowing shift before + /// performing the shift. IE: `rhs = 128` will become `rhs = 0` + /// and `rhs = 129` will become `rhs = 1`. + public static func &<<= (lhs: inout UInt128, rhs: UInt128) { + let shiftWidth = rhs.value.lowerBits & 127 + + switch shiftWidth { + case 0: return // Do nothing shift. + case 1 ... 63: + let upperBits = (lhs.value.upperBits << shiftWidth) + (lhs.value.lowerBits >> (64 - shiftWidth)) + let lowerBits = lhs.value.lowerBits << shiftWidth + lhs = UInt128(upperBits: upperBits, lowerBits: lowerBits) + case 64: + // Shift 64 means move lower bits to upper bits. + lhs = UInt128(upperBits: lhs.value.lowerBits, lowerBits: 0) + default: + let upperBits = lhs.value.lowerBits << (shiftWidth - 64) + lhs = UInt128(upperBits: upperBits, lowerBits: 0) + } + } +} + +// MARK: - UnsignedInteger Conformance + +extension UInt128: UnsignedInteger {} + +// MARK: - Hashable Conformance + +extension UInt128: Hashable { + public func hash(into hasher: inout Hasher) { + hasher.combine(value.lowerBits) + hasher.combine(value.upperBits) + } +} + +// MARK: - Numeric Conformance + +extension UInt128: Numeric { + public static func + (lhs: UInt128, rhs: UInt128) -> UInt128 { + precondition(~lhs >= rhs, "Addition overflow!") + let result = lhs.addingReportingOverflow(rhs) + return result.partialValue + } + + public static func += (lhs: inout UInt128, rhs: UInt128) { + lhs = lhs + rhs + } + + public static func - (lhs: UInt128, rhs: UInt128) -> UInt128 { + precondition(lhs >= rhs, "Integer underflow") + let result = lhs.subtractingReportingOverflow(rhs) + return result.partialValue + } + + public static func -= (lhs: inout UInt128, rhs: UInt128) { + lhs = lhs - rhs + } + + public static func * (lhs: UInt128, rhs: UInt128) -> UInt128 { + let result = lhs.multipliedReportingOverflow(by: rhs) + precondition(!result.overflow, "Multiplication overflow!") + return result.partialValue + } + + public static func *= (lhs: inout UInt128, rhs: UInt128) { + lhs = lhs * rhs + } +} + +// MARK: - Equatable Conformance + +extension UInt128: Equatable { + /// Checks if the `lhs` is equal to the `rhs`. + public static func == (lhs: UInt128, rhs: UInt128) -> Bool { + if lhs.value.lowerBits == rhs.value.lowerBits, lhs.value.upperBits == rhs.value.upperBits { + return true + } + return false + } +} + +// MARK: - ExpressibleByIntegerLiteral Conformance + +extension UInt128: ExpressibleByIntegerLiteral { + public init(integerLiteral value: IntegerLiteralType) { + self.init(upperBits: 0, lowerBits: UInt64(value)) + } +} + +// MARK: - CustomStringConvertible Conformance + +extension UInt128: CustomStringConvertible { + // MARK: Instance Properties + + public var description: String { + return _valueToString() + } + + // MARK: Instance Methods + + /// Converts the stored value into a string representation. + /// - parameter radix: + /// The radix for the base numbering system you wish to have + /// the type presented in. + /// - parameter uppercase: + /// Determines whether letter components of the outputted string will be in + /// uppercase format or not. + /// - returns: + /// String representation of the stored UInt128 value. + internal func _valueToString(radix: Int = 10, uppercase: Bool = true) -> String { + precondition((2 ... 36) ~= radix, "radix must be within the range of 2-36.") + // Simple case. + if self == 0 { + return "0" + } + + // Will store the final string result. + var result = String() + + // Used as the check for indexing through UInt128 for string interpolation. + var divmodResult = (quotient: self, remainder: UInt128(0)) + // Will hold the pool of possible values. + let characterPool = uppercase ? "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" : "0123456789abcdefghijklmnopqrstuvwxyz" + // Go through internal value until every base position is string(ed). + repeat { + divmodResult = divmodResult.quotient.quotientAndRemainder(dividingBy: UInt128(radix)) + let index = characterPool.index(characterPool.startIndex, offsetBy: Int(divmodResult.remainder)) + result.insert(characterPool[index], at: result.startIndex) + } while divmodResult.quotient > 0 + return result + } +} + +// MARK: - CustomDebugStringConvertible Conformance + +extension UInt128: CustomDebugStringConvertible { + public var debugDescription: String { + return description + } +} + +// MARK: - Comparable Conformance + +extension UInt128: Comparable { + public static func < (lhs: UInt128, rhs: UInt128) -> Bool { + if lhs.value.upperBits < rhs.value.upperBits { + return true + } else if lhs.value.upperBits == rhs.value.upperBits, lhs.value.lowerBits < rhs.value.lowerBits { + return true + } + return false + } +} + +// MARK: - Codable Conformance + +extension UInt128: Codable { + private enum CodingKeys: String, CodingKey { + case upperBits, lowerBits + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + let upperBits = try container.decode(UInt64.self, forKey: .upperBits) + let lowerBits = try container.decode(UInt64.self, forKey: .lowerBits) + self.init(upperBits: upperBits, lowerBits: lowerBits) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(value.upperBits, forKey: .upperBits) + try container.encode(value.lowerBits, forKey: .lowerBits) + } +} + +// MARK: - Deprecated API + +extension UInt128 { + /// Initialize a UInt128 value from a string. + /// + /// - parameter source: the string that will be converted into a + /// UInt128 value. Defaults to being analyzed as a base10 number, + /// but can be prefixed with `0b` for base2, `0o` for base8 + /// or `0x` for base16. + @available(swift, deprecated: 3.2, renamed: "init(_:)") + public static func fromUnparsedString(_ source: String) throws -> UInt128 { + guard let result = UInt128(source) else { + throw UInt128Errors.invalidString + } + return result + } + + /// The required initializer of `ExpressibleByStringLiteral`. + /// + /// Note that the `ExpressibleByStringLiteral` conformance has been removed because it + /// does not handle failures gracefully and it always shadows the failable initializer in Swift 5. + @available(swift, deprecated: 5.0, message: "The ExpressibleByStringLiteral conformance has been removed. Use failable initializer instead.", renamed: "init(_:)") + public init(stringLiteral value: StringLiteralType) { + self.init() + + if let result = UInt128._valueFromString(value) { + self = result + } + } +} + +// MARK: - BinaryFloatingPoint Interworking + +extension BinaryFloatingPoint { + public init(_ value: UInt128) { + precondition(value.value.upperBits == 0, "Value is too large to fit into a BinaryFloatingPoint until a 128bit BinaryFloatingPoint type is defined.") + self.init(value.value.lowerBits) + } + + public init?(exactly value: UInt128) { + if value.value.upperBits > 0 { + return nil + } + self = Self(value.value.lowerBits) + } +} + +// MARK: - String Interworking + +extension String { + /// Creates a string representing the given value in base 10, or some other + /// specified base. + /// + /// - Parameters: + /// - value: The UInt128 value to convert to a string. + /// - radix: The base to use for the string representation. `radix` must be + /// at least 2 and at most 36. The default is 10. + /// - uppercase: Pass `true` to use uppercase letters to represent numerals + /// or `false` to use lowercase letters. The default is `false`. + public init(_ value: UInt128, radix: Int = 10, uppercase: Bool = false) { + self = value._valueToString(radix: radix, uppercase: uppercase) + } +} + +extension UInt128 { + internal static func _valueFromString(_ value: String) -> UInt128? { + let radix = UInt128._determineRadixFromString(value) + let inputString = radix == 10 ? value : String(value.dropFirst(2)) + guard inputString.isEmpty == false else { + return nil + } + + return UInt128(inputString, radix: radix) + } + + internal static func _determineRadixFromString(_ string: String) -> Int { + switch string.prefix(2) { + case "0b": return 2 + case "0o": return 8 + case "0x": return 16 + default: return 10 + } + } +} diff --git a/Tests/Base32CrockfordTests/XCTestManifests.swift b/Tests/Base32CrockfordTests/XCTestManifests.swift deleted file mode 100644 index cd7592a..0000000 --- a/Tests/Base32CrockfordTests/XCTestManifests.swift +++ /dev/null @@ -1,55 +0,0 @@ -#if !canImport(ObjectiveC) - import XCTest - - extension ArrayTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__ArrayTests = [ - ("testBadRandomArray", testBadRandomArray) - ] - } - - extension Base32CrockfordTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__Base32CrockfordTests = [ - ("testExample", testExample), - ("testGenerateArray", testGenerateArray), - ("testGenerateArrayLessThanZero", testGenerateArrayLessThanZero), - ("testIdentifierDataTypeCodable", testIdentifierDataTypeCodable), - ("testMinimumUniqueCount", testMinimumUniqueCount), - ("testMinimumUniqueCountLessThanZero", testMinimumUniqueCountLessThanZero), - ("testUUID", testUUID) - ] - } - - extension Base32EqualityTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__Base32EqualityTests = [ - ("testExample", testExample) - ] - } - - extension EncodeDecodeTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__EncodeDecodeTests = [ - ("testDecoding", testDecoding), - ("testEncoding", testEncoding) - ] - } - - public func __allTests() -> [XCTestCaseEntry] { - return [ - testCase(ArrayTests.__allTests__ArrayTests), - testCase(Base32CrockfordTests.__allTests__Base32CrockfordTests), - testCase(Base32EqualityTests.__allTests__Base32EqualityTests), - testCase(EncodeDecodeTests.__allTests__EncodeDecodeTests) - ] - } -#endif From 73f1d84c94db4a855f4d2024133a8ed5793f501c Mon Sep 17 00:00:00 2001 From: leogdion Date: Tue, 3 Jan 2023 19:46:01 -0500 Subject: [PATCH 3/7] Adding Support for Dashing #9 --- Sources/Base32Crockford/Array.swift | 46 ------------------- .../Base32CrockfordEncoding.swift | 28 ++++++----- .../Base32CrockfordEncodingOptions.swift | 26 ++++++++--- Sources/Base32Crockford/Binary.swift | 17 +++++++ Tests/Base32CrockfordTests/ArrayTests.swift | 34 -------------- .../Base32CrockfordTests.swift | 26 +++++++++-- Tests/Base32CrockfordTests/String.swift | 16 +++++++ 7 files changed, 89 insertions(+), 104 deletions(-) delete mode 100644 Tests/Base32CrockfordTests/ArrayTests.swift create mode 100644 Tests/Base32CrockfordTests/String.swift diff --git a/Sources/Base32Crockford/Array.swift b/Sources/Base32Crockford/Array.swift index 56da5a1..ceeddb4 100644 --- a/Sources/Base32Crockford/Array.swift +++ b/Sources/Base32Crockford/Array.swift @@ -6,49 +6,3 @@ extension Array where Element == UInt8 { self = Mirror(reflecting: uuid.uuid).children.map { $0.value as! UInt8 } } } - -extension Array where Element: FixedWidthInteger { - public static func random( - withCount count: Int, - in range: ClosedRange? = nil - ) -> Array { - // swiftlint:disable:next force_unwrapping - random(withCount: count, in: range, fatalError: nil)! - } - - #if DEBUG - // periphery:ignore - internal static func debugRandom( - withCount count: Int, - in range: ClosedRange? = nil, - fatalError: ((String?) -> Void)? = nil - ) -> Array? { - random(withCount: count, in: range, fatalError: fatalError) - } - #endif - - private static func random( - withCount count: Int, - in range: ClosedRange? = nil, - fatalError: ((String?) -> Void)? = nil - ) -> Array? { - let range = range ?? (Element.min ... Element.max) - // swiftlint:disable empty_count - guard count >= 0 else { - if let fatalError = fatalError { - fatalError("Array count cannot be less than 0.") - return nil - } else { - Swift.fatalError("Array count cannot be less than 0.") - } - } - - // swiftlint:enable empty_count - guard count >= 1 else { - return [Element]() - } - return (1 ... count).map { _ in - Element.random(in: range) - } - } -} diff --git a/Sources/Base32Crockford/Base32CrockfordEncoding.swift b/Sources/Base32Crockford/Base32CrockfordEncoding.swift index 973f8b1..55a0407 100644 --- a/Sources/Base32Crockford/Base32CrockfordEncoding.swift +++ b/Sources/Base32Crockford/Base32CrockfordEncoding.swift @@ -39,7 +39,7 @@ public struct Base32CrockfordEncoding { let expectedByteCount = ((bitString.count - 1) / 8) + 1 // swiftlint:disable:next line_length - precondition(expectedByteCount == dataBytes.count, "Expected \(expectedByteCount) bytes from \(bitString.count) bits but received \(dataBytes.count)") + assert(expectedByteCount == dataBytes.count, "Expected \(expectedByteCount) bytes from \(bitString.count) bits but received \(dataBytes.count)") let result = Data(dataBytes) @@ -56,21 +56,10 @@ public struct Base32CrockfordEncoding { data: Data, options: Base32CrockfordEncodingOptions = .none ) -> String { - var encodedString = "" - var index: Int? - var binary = Binary(data: data, sectionSize: 5) - repeat { - index = binary.nextSection() - guard let index = index else { - break - } - encodedString.append( - Self.characters.characterAtOffset(index) - ) - } while index != nil + var encodedString = binary.string(basedOnCharacterMap: Self.characters) - if options.contains(.withChecksum) { + if options.withChecksum { encodedString.append( Self.allChecksumSymbols.characterAtOffset( data.remainderBy( @@ -80,8 +69,16 @@ public struct Base32CrockfordEncoding { ) } - return encodedString + encodedString = encodedString .replacingOccurrences(of: "^0+", with: "", options: .regularExpression) + + guard let groupingOptions = options.groupingBy else { + return encodedString + } + + return encodedString + .split(by: groupingOptions.maxLength) + .joined(separator: groupingOptions.separator) } public func decode( @@ -98,5 +95,6 @@ public struct Base32CrockfordEncoding { .replacingOccurrences(of: "O", with: "0") .replacingOccurrences(of: "I", with: "1") .replacingOccurrences(of: "L", with: "1") + .replacingOccurrences(of: "-", with: "") } } diff --git a/Sources/Base32Crockford/Base32CrockfordEncodingOptions.swift b/Sources/Base32Crockford/Base32CrockfordEncodingOptions.swift index df077ce..f816d98 100644 --- a/Sources/Base32Crockford/Base32CrockfordEncodingOptions.swift +++ b/Sources/Base32Crockford/Base32CrockfordEncodingOptions.swift @@ -1,12 +1,26 @@ import Foundation -public struct Base32CrockfordEncodingOptions: OptionSet { - public static let withChecksum = Base32CrockfordEncodingOptions(rawValue: 1 << 0) - public static let none: Base32CrockfordEncodingOptions = [] +public struct Base32CrockfordEncodingOptions { + public struct GroupingOptions { + public init(maxLength: Int, separator: String = "-") { + self.maxLength = maxLength + self.separator = separator + } - public let rawValue: Int + public let maxLength: Int + public let separator: String + } + + public static let none = Base32CrockfordEncodingOptions(withChecksum: false) + + public let withChecksum: Bool + public let groupingBy: GroupingOptions? - public init(rawValue: Int) { - self.rawValue = rawValue + internal init( + withChecksum: Bool = false, + groupingBy: Base32CrockfordEncodingOptions.GroupingOptions? = nil + ) { + self.withChecksum = withChecksum + self.groupingBy = groupingBy } } diff --git a/Sources/Base32Crockford/Binary.swift b/Sources/Base32Crockford/Binary.swift index 7aec682..6136aec 100644 --- a/Sources/Base32Crockford/Binary.swift +++ b/Sources/Base32Crockford/Binary.swift @@ -59,4 +59,21 @@ public struct Binary { return nil } } + + public mutating func string(basedOnCharacterMap characterMap: String) -> String { + var encodedString = "" + var index: Int? + + repeat { + index = nextSection() + guard let index = index else { + break + } + encodedString.append( + characterMap.characterAtOffset(index) + ) + } while index != nil + + return encodedString + } } diff --git a/Tests/Base32CrockfordTests/ArrayTests.swift b/Tests/Base32CrockfordTests/ArrayTests.swift deleted file mode 100644 index bdd7e24..0000000 --- a/Tests/Base32CrockfordTests/ArrayTests.swift +++ /dev/null @@ -1,34 +0,0 @@ -@testable import Base32Crockford -import XCTest - -final class ArrayTests: XCTestCase { - func testBadRandomArray() { - var tested = false - let result = Array.debugRandom(withCount: -100, in: 0 ... 100) { actual in - XCTAssertEqual(actual, "Array count cannot be less than 0.") - tested = true - } - XCTAssertTrue(tested) - XCTAssertNil(result) - } - - func testGoodRandomArray() { - var tested = false - let result = Array.debugRandom(withCount: 100, in: 0 ... 100) { actual in - XCTAssertEqual(actual, "Array count cannot be less than 0.") - tested = true - } - XCTAssertFalse(tested) - XCTAssertEqual(result?.count, 100) - } - - func testEmptyRandomArray() { - var tested = false - let result = Array.debugRandom(withCount: 0, in: 0 ... 100) { actual in - XCTAssertEqual(actual, "Array count cannot be less than 0.") - tested = true - } - XCTAssertFalse(tested) - XCTAssertEqual(result?.count, 0) - } -} diff --git a/Tests/Base32CrockfordTests/Base32CrockfordTests.swift b/Tests/Base32CrockfordTests/Base32CrockfordTests.swift index 4485908..2f3f889 100644 --- a/Tests/Base32CrockfordTests/Base32CrockfordTests.swift +++ b/Tests/Base32CrockfordTests/Base32CrockfordTests.swift @@ -49,14 +49,20 @@ final class Base32CrockfordTests: XCTestCase { let uuidData = Data(Array(uuid: parameters.uuid)) let encodedUUID = Base32CrockfordEncoding.encoding.encode(data: uuidData) let encodedInt = Base32CrockfordEncoding.encoding.encode(data: parameters.integer.data) - let encodedUUIDChecksum = Base32CrockfordEncoding.encoding.encode(data: uuidData, options: .withChecksum) + let encodedUUIDChecksum = Base32CrockfordEncoding.encoding.encode(data: uuidData, options: .init(withChecksum: true)) let decodedUUIDBytes = try Base32CrockfordEncoding.encoding.decode(base32Encoded: parameters.encoded).trim(to: 16) let decodedUUID = UUID(data: decodedUUIDBytes) + let decodedWithDashesUUIDBytes = try Base32CrockfordEncoding.encoding.decode(base32Encoded: parameters.encoded.randomDashes()).trim(to: 16) + let decodedWithDashesUUID = UUID(data: decodedUUIDBytes) + let decodedUUIDChecksumBytes = try Base32CrockfordEncoding.encoding.decode(base32Encoded: parameters.encodedWithChecksum, options: .init(withChecksum: true)).trim(to: 16) let decodedUUIDChecksum = UUID(data: decodedUUIDChecksumBytes) + let decodedUUIDChecksumWithDashesBytes = try Base32CrockfordEncoding.encoding.decode(base32Encoded: parameters.encodedWithChecksum.randomDashes(), options: .init(withChecksum: true)).trim(to: 16) + let decodedWithDashesUUIDChecksum = UUID(data: decodedUUIDChecksumBytes) + let moduloIndex = Base32CrockfordEncoding.allChecksumSymbols.firstIndex(of: parameters.encodedWithChecksum.last!)! let modulo = Base32CrockfordEncoding.allChecksumSymbols.distance( @@ -68,18 +74,21 @@ final class Base32CrockfordTests: XCTestCase { XCTAssertEqual(parameters.integer.data.count, 128 / 8) XCTAssertEqual(encodedInt, encodedUUID) XCTAssertEqual(encodedInt, parameters.encoded) + XCTAssertEqual(encodedInt, parameters.encoded) XCTAssertEqual(encodedUUID, parameters.encoded) XCTAssertEqual(decodedUUID, parameters.uuid, "Invalid Row \(index)") + XCTAssertEqual(decodedWithDashesUUID, parameters.uuid) XCTAssertEqual(actualMod, modulo, "Invalid Row \(index)") XCTAssertEqual(modulo, parameters.integer.data.remainderBy(37)) XCTAssertEqual(encodedUUIDChecksum, parameters.encodedWithChecksum) XCTAssertEqual(decodedUUIDChecksum, parameters.uuid) + XCTAssertEqual(decodedWithDashesUUIDChecksum, parameters.uuid) } } func testBadChecksum() { let data = Data(Array(uuid: .init())) - let encodedWithChecksum = Base32CrockfordEncoding.encoding.encode(data: data, options: .withChecksum) + let encodedWithChecksum = Base32CrockfordEncoding.encoding.encode(data: data, options: .init(withChecksum: true)) let (encoded, checksum) = encodedWithChecksum.split(withChecksum: true) guard let checksum = checksum else { XCTFail() @@ -115,7 +124,7 @@ final class Base32CrockfordTests: XCTestCase { XCTAssertEqual(badString.count, 33) do { _ = try Base32CrockfordEncoding.encoding.decode(base32Encoded: badString) - XCTFail() + XCTFail("String \(badString) was valid.") } catch let error as Base32CrockfordDecodingError { guard case let .invalidCharacter(actualCharacter) = error.details else { XCTAssertNil(error) @@ -127,6 +136,17 @@ final class Base32CrockfordTests: XCTestCase { } } + func testSeparator() throws { + let base32String = "69RDT89E2T8BMB7SZVZ03F4SGV2" + let expected3String = "69R-DT8-9E2-T8B-MB7-SZV-Z03-F4S-GV2" + let expected9String = "69RDT89E2-T8BMB7SZV-Z03F4SGV2" + let decoded = try Base32CrockfordEncoding.encoding.decode(base32Encoded: base32String, options: .init(withChecksum: true)) + let actual3String = Base32CrockfordEncoding.encoding.encode(data: decoded, options: .init(withChecksum: true, groupingBy: .init(maxLength: 3))) + let actual9String = Base32CrockfordEncoding.encoding.encode(data: decoded, options: .init(withChecksum: true, groupingBy: .init(maxLength: 9))) + XCTAssertEqual(actual3String, expected3String) + XCTAssertEqual(actual9String, expected9String) + } + func testRandomData() { let b32cf = Base32CrockfordEncoding() for length in 1 ... 20 { diff --git a/Tests/Base32CrockfordTests/String.swift b/Tests/Base32CrockfordTests/String.swift new file mode 100644 index 0000000..a384a7f --- /dev/null +++ b/Tests/Base32CrockfordTests/String.swift @@ -0,0 +1,16 @@ +import Foundation + +extension String { + func randomDashes() -> String { + var result = self + var index = endIndex + repeat { + guard let newIndex = self.index(index, offsetBy: .random(in: 3 ... 8) * -1, limitedBy: startIndex) else { + return result + } + result.insert(Character("-"), at: newIndex) + index = newIndex + } while index > startIndex + return result + } +} From 944e912d96af48d5f60339d00a316f4c864927d7 Mon Sep 17 00:00:00 2001 From: leogdion Date: Wed, 4 Jan 2023 18:49:55 -0500 Subject: [PATCH 4/7] Adding Documentation (#30) * renaming to ThirtyTo * Working on README * Adding logos * finishing public docs * Adding DocC --- .../{Base32Crockford.yml => ThirtyTo.yml} | 8 +- .periphery.yml | 2 +- .swift-version | 2 +- .swiftlint.yml | 4 +- Assets/logo.png | Bin 0 -> 125611 bytes Assets/logo.svg | 48 +++ Package.swift | 12 +- README.md | 310 ++++++++++++++++-- .../Base32CrockfordDecodingOptions.swift | 6 - .../Base32CrockfordEncodingOptions.swift | 26 -- Sources/Base32Crockford/UUID.swift | 11 - .../{Base32Crockford => ThirtyTo}/Array.swift | 2 + .../Base32CrockfordDecodingError.swift | 14 + .../Base32CrockfordDecodingOptions.swift | 16 + ...ase32CrockfordEncoding+CharacterSets.swift | 13 + .../Base32CrockfordEncoding.swift | 42 ++- .../Base32CrockfordEncodingOptions.swift | 40 +++ .../Binary.swift | 26 +- .../{Base32Crockford => ThirtyTo}/Data.swift | 4 +- .../Documentation.docc/Documentation.md | 260 +++++++++++++++ .../String.swift | 4 +- Sources/ThirtyTo/UUID.swift | 13 + .../Base32CrockfordTests.swift | 20 +- .../BinaryTests.swift | 2 +- .../Data.swift | 2 +- .../String.swift | 2 +- .../UInt128+Data.swift | 1 - .../UInt128.swift | 18 +- 28 files changed, 775 insertions(+), 133 deletions(-) rename .github/workflows/{Base32Crockford.yml => ThirtyTo.yml} (92%) create mode 100644 Assets/logo.png create mode 100644 Assets/logo.svg delete mode 100644 Sources/Base32Crockford/Base32CrockfordDecodingOptions.swift delete mode 100644 Sources/Base32Crockford/Base32CrockfordEncodingOptions.swift delete mode 100644 Sources/Base32Crockford/UUID.swift rename Sources/{Base32Crockford => ThirtyTo}/Array.swift (67%) rename Sources/{Base32Crockford => ThirtyTo}/Base32CrockfordDecodingError.swift (56%) create mode 100644 Sources/ThirtyTo/Base32CrockfordDecodingOptions.swift create mode 100644 Sources/ThirtyTo/Base32CrockfordEncoding+CharacterSets.swift rename Sources/{Base32Crockford => ThirtyTo}/Base32CrockfordEncoding.swift (64%) create mode 100644 Sources/ThirtyTo/Base32CrockfordEncodingOptions.swift rename Sources/{Base32Crockford => ThirtyTo}/Binary.swift (69%) rename Sources/{Base32Crockford => ThirtyTo}/Data.swift (78%) create mode 100644 Sources/ThirtyTo/Documentation.docc/Documentation.md rename Sources/{Base32Crockford => ThirtyTo}/String.swift (94%) create mode 100644 Sources/ThirtyTo/UUID.swift rename Tests/{Base32CrockfordTests => ThirtyToTests}/Base32CrockfordTests.swift (90%) rename Tests/{Base32CrockfordTests => ThirtyToTests}/BinaryTests.swift (94%) rename Tests/{Base32CrockfordTests => ThirtyToTests}/Data.swift (91%) rename Tests/{Base32CrockfordTests => ThirtyToTests}/String.swift (95%) rename Tests/{Base32CrockfordTests => ThirtyToTests}/UInt128+Data.swift (89%) rename Tests/{Base32CrockfordTests => ThirtyToTests}/UInt128.swift (98%) diff --git a/.github/workflows/Base32Crockford.yml b/.github/workflows/ThirtyTo.yml similarity index 92% rename from .github/workflows/Base32Crockford.yml rename to .github/workflows/ThirtyTo.yml index b85ade3..5920dac 100644 --- a/.github/workflows/Base32Crockford.yml +++ b/.github/workflows/ThirtyTo.yml @@ -1,4 +1,4 @@ -name: Base32Crockford +name: ThirtyTo on: push: branches-ignore: @@ -145,11 +145,11 @@ jobs: MAX_ATTEMPT=3 ATTEMPT=0 while [ -z $SUCCESS ] && [ "$ATTEMPT" -le "$MAX_ATTEMPT" ]; do - xcodebuild clean -scheme Base32Crockford -destination 'generic/platform=iOS' | grep -q "CLEAN SUCCEEDED" && SUCCESS=true + xcodebuild clean -scheme ThirtyTo -destination 'generic/platform=iOS' | grep -q "CLEAN SUCCEEDED" && SUCCESS=true ATTEMPT=$(($ATTEMPT+1)) done - name: Run iOS target tests - run: xcodebuild test -scheme Base32Crockford -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 13,OS=${{ matrix.iOSVersion }}' -enableCodeCoverage YES build test + run: xcodebuild test -scheme ThirtyTo -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 13,OS=${{ matrix.iOSVersion }}' -enableCodeCoverage YES build test - uses: sersoft-gmbh/swift-coverage-action@v3 with: fail-on-empty-output: true @@ -160,7 +160,7 @@ jobs: flags: iOS,iOS-${{ matrix.iOSVersion }} token: ${{ secrets.CODECOV_TOKEN }} - name: Run watchOS target tests - run: xcodebuild test -scheme Base32Crockford -sdk watchsimulator -destination 'platform=watchOS Simulator,name=${{ matrix.watchName }},OS=${{ matrix.watchOSVersion }}' -enableCodeCoverage YES build test + run: xcodebuild test -scheme ThirtyTo -sdk watchsimulator -destination 'platform=watchOS Simulator,name=${{ matrix.watchName }},OS=${{ matrix.watchOSVersion }}' -enableCodeCoverage YES build test - uses: sersoft-gmbh/swift-coverage-action@v3 with: fail-on-empty-output: true diff --git a/.periphery.yml b/.periphery.yml index e2749ad..e156aa2 100644 --- a/.periphery.yml +++ b/.periphery.yml @@ -1,3 +1,3 @@ retain_public: true targets: -- Base32Crockford +- ThirtyTo diff --git a/.swift-version b/.swift-version index 7ed6ff8..9ad974f 100644 --- a/.swift-version +++ b/.swift-version @@ -1 +1 @@ -5 +5.5 diff --git a/.swiftlint.yml b/.swiftlint.yml index 4f922fa..7cdbbb2 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -44,7 +44,7 @@ opt_in_rules: - legacy_random - literal_expression_end_indentation - lower_acl_than_parent - #- missing_docs + - missing_docs - modifier_order #- multiline_arguments - multiline_arguments_brackets @@ -102,7 +102,7 @@ type_body_length: - 100 - 200 file_length: - - 120 + - 125 - 200 function_body_length: - 20 diff --git a/Assets/logo.png b/Assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..5188e6736454d76c1088b351421056a88597008e GIT binary patch literal 125611 zcmcF~i9gie_y5bjFDc1B$}TBOLKtKz`yN6`$lip>K9=g8!eouIgphqVV@+jC_MNem zeH*)pG5oI2_iy-lOy=d>bI(2Z+;g7i+%wY~BLi(FdMY~zxAo3$4Ju^*hZ@tt1pFbxjCqVrF{RZ#g>GXtjN&>Ir(_`==o)U?tM@OeeheYsj z`0wN&;e>E-LfAh!*!%bI-^u>o$=>eC?)J&f_QC%C$@b>S*2c-^#tD9nw6R87Um>k6 zlU5fuH#bSk3#6ra(&Asz{4DA3^zq-R<3G6LnaSgsiKV5b*rmLAp?AJ!Hf))XFA7yPTt zZ*Ob+SCRLx{L89CUq3Z9H4$=C8|v$8YikLg zV&>E1E6d9d5?&s}g%=eT?!OD#fBF1NPEOnV|Mo)M_JUl}($aSQ9w#R!4+VPcdRpx` z8^=Vy+j)4m2H~=OA08eazGbGgWug!g60&7{D=;uHARqvVMEd#p`S|!efBqbSKsz?6B82$Ej0rJ13f)G9UUDF4GlFlH3KCD6%`dFB_#y~1vxo68TbJ)06|Gz z917s=ijRqbDMZE0d09yGx~iUq!?Tc=Zxho#7gW^LwR8=PjN#^1cZnw_o8PRNA+jaq zyLZ&gUQDb{-F4>~O;h#u{5#UEDcGucdAvyY#qb*rTmMCKd0YQED{X#8I#w8*MvaD- z@{(U$CrnM4?sn(Q*0;`|*Lm6}zcGBGOyd1Pe_73r(k<)`9o4a#k>g($2!AS-ZkB&gBoNWzkQ1|1n1%y91HJ2AJzbiI%PT8*{OanutOwb=R4mdL$v^h) zFGU2)`F~FT_;`7_xida|`jncka9c@UUez9WQ;=v>R(G>`cKa!QyZK#KYku8fX;$9p z@A%N!mFB>a_|WSUEBtT#PJ1`Lw47do7u%=so($}T&fqh?i=3V=r<@iSpU(Bq8G2pt zAUa>XD4uwT9ls(oXlfdN{`}ly8f|UuSMujWzsh1#c8LLjb@g?>BcQM^yLkL`(@9uM zOZP*S0B`r>zWJfOKjYZLd%kYJ?SlL+vPF>X?B&Hs7K+8HZuS3Fe-h#;Gi7M-F4jYs zhk{PS znE!z5(y~(D6M5xt_7x>DSN@JeBjLGc30~wDfn6UJO4E z=fs1!&5LtkS!*g~*a8*ln>T`UOyry))(5GfI}%<`JOdE!KKZxDX54P@i5ggMuFWv6 z#mnp($~WFg8X}_&Ry*mmI9$d3p8wZj^z_@k+h}cDWm3e#pNQAFO9eIg6zEu#WGF)ZH&vf6&n}8LP|LwPLPnT!s|3q!x zf;4U3*ndd7eamchvznSEvZxZ7d0SdVx%BZBNc#AA@1Kr;<|x~3Rr`EVhx_QGeciLD#<$RGWL!2d}#Ue z2HVD-!Qz4<@#DOkaXrdjAr)X34@R#{%8->SINDw`*+ zRYMY~kWxuya=X*TqDl%lEX$=_TFzmEcdVdRR2uaDt`hsEQ2kIXcae`)b4g_3t&VODQE2gUpbB?RB{!k$-Tf z{lvVB1?o!B1vNSAPy7;C2lfh#s+0S)JR=k(6y>C~o*H&_g+INfj~Sw>_RbH2bA?}B zca@5^aiu|#Pu|A){rs*J*;E+pGO>RT`nE_anDcAU$W)}!TFNT;kq>>-Fs`~@Jj@u0 zQOo^HK?U88pW;~3@<+j5(iU#UnG3E^Rb)(Ae7n%MD)S{j0$dYrI8v{8pJLqlSzu z5wQgfrd$mgZhOr9+rwl~s!^Jc1^d8=?T7|7DK6385g(*VK39{ymNTov#ecRp!*?_xoG!iAEC*1n9p zDdp)KtAkyxAq3eWTKfx<2IK1nkM`eL`~HunUG@ zf#TW9aXeJgw>+wOp^p5QH7pIt%OdrniwxCpOL|Of=KbK9-F7$ygpI-ror~pZJ2+PkgnnGs=`2uk zP=Zj)%-}a%xb)5R8@+?v$?O78nNYv&bDatxA1Iv3>hz%VPSWGRabH~s z{z#ATHq_D7*}sQ@5xUTsuxxNK(dmkp6l5f7?3-msFZh_+sbJa`%0!I^H)yj7Y8>TW z?gED+5D{2)eL{#S?p)*Cv=x;3t-nz^r&!6VPuVq|5i(kz=j%?4R1fzO%6{CKxS<>qX;(u1SY_|~Pkh{FRs4&}y zod%Bc(5(MpP2KsS#e*Ms8**qYr5H~+hw9#dp!(+m=fm7R`K`)u%upGJ%@p0Y*$0BP z6wvzP3QNzN;-lP#R}dt3|I$hvZ@Dft1oJU=Y-aQ@9Uy0eGMNv$`f_chZvytA))3B# z3f;Cs8pueB_{60=Ora2nkN&X!Ug&8`Ivp@{rW*cnmbqdVI~F`Y8cqp$A7{aHPkG&g z0&>wZYoA3kOf7E;5fiCGVK(e_AUjaMI!T)z4 zEvvEV2ca&$+4ISJCe%1d23fah7RcrY4y;i?ed-KP&LvPY_5}JvkhR+?>cYLWy0(T} z5F}|SMwd18C3M6Ma52w$KF;z>`(X_yhpIXoCO_nxZ&qK8U|m;p-5QjbFIXweLa6|2vUE8|54Lb z_O>NzA1LS@M@-m|UdQM=$;JUC)umNoIXj;yQo%MNg$D-GmTEg++<>6(y7)omIB}cW zPM)f>GwqmHQ57xPiJ#^KM*|yLR&lLotm}jzN7RP&ZM^W@69}TU%Bg4M<>j3kWU!-# zP-W(?e~x@#%c#`{(}Ut|WC8M zs5Yay!$0vd!H`3Pm;r zxTL-6Y)ZQ8KU1v%PfX$H-oIOOsFI{846turz13FFe{n>qh)Tb2RJ94kvO|+HAAP776pDP|C$qvC{FW2TT7N`Aw zm&XOck~@{T&iU;Q894RG08y56sa^B=Y44UeAy{*6z2J0^=+iT)Zk05S)3+}jhyo>W zLCr%SNjK4Re2x$bSI9`;WaUMnuyBJK5=H6Qbhxi6{1pJtS_ppj5Sb!NZV>z51(6;C zm4)_a&wnWiE+#e|ziYfksX;XA^at13Q1>_z{mz2zVa%RR%ec&0C|~Fa%Ha7Vmwgrx zf9@E!n(JBB``z`VfkYjB?7i7db4zSf_kNQ@7i6*8LeFj~VS^_E0P8dC9XW|Q=i9{) zqm$ciwm4RT59!!po<(V zzMXAqO9xRPoWA9Xbt*rkU`RKDAik*F`h__orfXNNy=kD?a?VcWG2At9BwO*zW%GPF zalz6M>gTDDy#s$8>+7f}-bVIyy9KuLZ7XUS+^dTM>VRE2D4wc2b z;89gPMxM3PV6|(D0SiygRE_kqLnH%7ptZZuJt=t>%;>OXd}KigSi&)Eo)3QN7b^fk z#DQL0U=7&$&MDr4jqVMGbTtSPB=j-;0y2hxR@cBx7`C#I3^*8SLQ}?8+DB=x>um6e zHNs2Vv-HId1m$aA(D(h-Upyke*+~IqPA{tnU~9+zH--A$+X2a88O!}XGN=y~@!f~P zm;cRe1O&-C-udL2Qo`BG4WN>XE#D?_GEdn9(65TW9g^%YuB7(E$Sx-40T0Za3lH$cngs2L$PSdr<41mPUH%;NN>5f}V1o$HcqNsyxnSgrF5pHf+4Y2gL;|atNkBfff0iDMAOK zWHlK`Cdl1i>4HG00e)8*fgFs6DFckl!I$NtxzxxZG}&v}m+tonpPXp{80W{(&veTs zq?D{u(m`;EJJwyYkM!JWpiJ{lTep-~O#VT@LMexKbPYEm&OpQE^A+nZ7Z7)FYCTuU zLDV%SfNL)0=WaBdyplFe2S;G-VZ>TGnx6*%F3Dy&{^y$Mz&Eh-n;+JaT6y)T0Maq% zk7Q4_m;p$3G~+>DX9uN?=5QPw|4erYt$(x(-R%WHxv@w+9L(f@4`@*9x`1+QswLyb0UeuNa&>Rc==L_eri4(eal{Ya zO{3I=l@1wl2+oT8F~0~*E}gl`mpkQUR{@+IetpsX`OL=Rm#91nfd6_?%Stm1K4ak^ zJOnt|29VN=0LNj1{p!sLo)LifM+7;E%NTV#d;UyCa8Wl3t%)GMTOx;0TM3%`8_WU1 z0POq1J@J!_T7DP`h&9V<5?8xie&#dq=(lTz@)*NFKEPvs_hG2R&>#dVKlkUM4wP z2U?Kd4e<+EG=y_3pcTVuv?A%}xk;dfAT(Voo^D)u378DVtwldYH}DTc@j2tLK=mZt z2YLwZ^xK!+FZ-PVNT7}#_l+Zn3FRP^b*XJr=z%&C%Txt4?g?Zd$8DSTn;E|o(rSdoW56FO(QG-@m+GoOwMAM!q+BQ-`@LcwJuH1_EV6-dC z!98iE+8DUiI}yU0%kBv&(tSYPsfT1Yf4TetYb|y!g;eSpwgTrw`}eXCk6=V}Do$zUcZ5JTaZSjS5$AWV}X7b{5UzO@YhdW@UFD^r! zAS7t*v(MCcC zf}&uG`Ro8O0Qfl92{~q*`N0K2jNwx%;4z%nKm4&kYq5Z|+2N_HQw$@6MB@+^@%0vG zTz9`ne8e`oO^=a5MuJzy8Sz)Nm?4z9)pFBvjb(s950R?9|fePz6DvMJ|F8Ey1@cT7ggg zweIMM3#>i!2YL@#?v~)6AUQ)XEjBuTh7-fzlS7$fZ56CI?P!3rR2=)SGA08H+5nz{ zV0yC^LHzItAO~Wz61X(RfnLmuf1ou5h=AS zM2I+x{cjoqn~2)8nTFv+%$Wuqa0JW2(U9kv4{pUjJ9}eoGD^lto^e||end?;cLfBq z%^hX6YZ%LS0PWr7xPT3%uL1Gh>c|&o2FE~v*I1U#&RdF`1A<;?eUYfbO)EG9A9`3X z%0(}UNqz@{_qKDBOxdi8n;rzsDi|vi;Z$6}fj^|Fd?blAMbuV-vlsSR_Kbn zbf(dLz=41DgBJhf3V@&bx8k;HlmshfkRKkF&f75y)Ut2^EE;PU1v6E0Cz@wfJTCxl)!?*i_5e#~t4VCR0*^s6nP}~@E2Q^-RFbAf07zr-e~T0K zoxu}8BwTW0;#`GSfALocGJ0F%7s+E93Q7rmd+tCriv)fMa(!+jr322(uBvaiW>&@0 z{A}3R>VGu*Nkw%&lR!YQlI=^!O=q74!qB4z<(A<43uNGWHjMY1Kj`sKq=8TYjMWP+ z1!=%5jgql`l!Uv!fLK>G_o5*CZ0XWBBLI8N0ZmbJs#rDE6?h)BI=nOY)e{6K>Ztno zz)af|$Rv|gNuS963xPW3BEKQ8$up3^zNx##=Jk99NGY3z`-Ph>od(0`6bov?vweVh zZF|+%YLVs%+z`YiGf~%-e6N5BdvCA!2Gg& z&0?I=(*LOPL#eYWM&_@HsSDa4LoZP0{5%1a)AOst}_;c~Mnqhc33~P!B$O zX-W_}bv~2njF)EWwMr_WATt)4&#_p78?JBugKK&_uljOu+; z8d&YcIujQBU;!hX;Zy9~zXOT(3*;SzEmhA0OSvfe*h1gU-D=8eW<*=YKcyi&lB3QP zvS=FD!*AEOCz5x3aYg=W3ATtPbBT^e@(B|@awHFvcVr{#u#NUF$y^e3x344{7Eqxz z23M#F#@#S7mlOiO2;m<=Lbrfy-oUV+CDn`0xNF~Sn#%0J?Q}=*51C~p1LE<=it|Gv8#}WapP8ycWh~z z=_MF(rq=A}*PgaG13d65$@ldo4XsNYWvY$oYT~ zzbyymQZtEg;)uiNQLy8X&3V4l@hl~@!r)zM!lQjk7;SsgxCqu>wJ?JmEjL9+P2gMr z#Nl`X;VyoA^5Rt3ySrOELwv)OunVQ@9Jo2J-mh$EvGXPRRIt}EC&4qox{v#LJL2vk zEU)Y53qvjU>+3@ir}$4`AB>zjMV#~D4& zKx-d*xf><{(ou_l?Z`Y|sIN&iYBpN%9FDl)2Ys(R^62gip8Jx$N=4}@{AO0AC)@CRT?ww(w4XN*0@OF|8T7{@6)GZ|CGA8Q_S83t@S(J{v$IZpY6kcMj zC|`^qW{Q)u=2q@g68;f)#i=RXn}F-&MnROT(RFcw$2|pcWPeuO6fwPGm!U9gthGIe zAgylH!^Dr>8#F_#sE29e`BK>S(o#__)_jd_YC;?rIcv%28Y7;2rXk-2{y{uVADq1$ zCG3I3=qdn=2>YuN+^jh#!V$y z(xn0mfJzNgw{6HGHfCyUmbR0|4-`uJauA1Et0sAGk~EyqY3hdiaj#W>qUMYAel66a=u8C@*Wm%M#%@V$Km6 zYZH=77hf(@0KZS|{)`4TW6|t(9c!;#`~rR}>yZtPtOH9e-}MvhKrx#>^6@Lrey@re~@MIj!8T5tjCK<@|Y3laVqO1RQiO}=;xA`lScGUPxYl#ULw zx>O#vg%k6Ck)eg!PsSY)&CiX`h23290G=z#1EuWzjS!9}P9fcs$tDVibUZh$jc!2| z3)L7aOM`;TeFoXll!Opp3fNt3Bh(oummdz~wN#uM1uHIk9mkju>GXQy|pGOtZ?2AB>`g3pe_x zz6YTZ^guG~rYJUq4i$d?L^@@b&t56}1H4Pq*S=+_^sN%ax&J*P63n(AyGBv`R=C&+R5*Z$8qBB7=WDz?~U*Q>lo4Yx4w6}pJ?1#BmD z5~Kb%%{!rvGL|@9{I$pA*6u}3vnrVmkmJ~!NjhOdXk%t3oPoDQ3d8bLoC2O?JE3zO zJ5!&Z317QoyPu}d4&0|AJ9$o!ryl}`@vD&5) z>ZtiLBgBQ154oL}3wOIS=bNTq4bsUw8*I{gYT$AG6HO}XzdE+gro#-VBBf++?^so2 zw0Ixa{wISpqic}#=8v3sA~G-8jNK(H0IVXx0j1RXiLA_@%q|pEkgpWZ`M4q-W0jzB z!o5!zmIvANIbOKNuh50Aa<4c;{HfyC68OX_Mya~U8k%oOnU4qz8kH-6PZXq8&wMNZ zwC_q{;2c%!wAgj5?7Le?We#L$hIX%9G;U66)F+wD#Js?_v$3n+4BGh|a3gw_Z{~Jk z6+BhT&DML;ZeD6zcxweb~+-M_RYy<<{aUIp}M~+1@}P` z=N1zp0RO$H(;dbYL98+%Lvt6-HK9QRTJhmP8ixv4k5 zr5J>GvL6rta%thsItsDnQZWp_u_&}7)VTZg8BgfLY$Y@%-)xYi`~DsoT1xvg-3ig` za7OI64iC&wBty&SiVT8uCdoY&mZLwyhSN2FNe=VsBt@IW2Ocv3s$=&k8}9Wm9SUdF z_}&u{8fqRLCa5_jcNRG0u+VF{+&Et!8a4}En-qm7fDmJ6pm4SmK{4@70OFTzLH+TW zW3Fl(kn&&ENRtXE;nzrF`|~I1F?hH`u52P%W`4{mIskFH6DdtW_tgdXqp_r|o-(F= zCf&pt#wk~o*l$LS`ec0~PMQzd{2r%H@n)>K_;=A|GPI#?-rU81#a9a8m+ui54tff5 zK`k{LSgeERFS!rYD)X{Qv6CZ*)sEn6E@ML*a|htN%o`jynqgYh@sXxPcwo?6l^inj z>2o3#K>#o|Ta?raV%s?qxm-rnM^##a^`fDUat4BRJkH+U!Y%V|Fp-k*REQkbUJ&Z0 z4}96q+&xnv0uZCv$k5JGRRE_gN7%Vter-(ysCx-@)T&kk^t)9!20x#*-53$r4EVg- zL*300q270^!-RjY2a`5u;b{-cqLV8QE zdKe(Cj*n0g{;l4nqTw2DcJ@{+f`iNVK)a+KUY^&OipzXL1|ucP3SnzQnqPKKNfyAf z57ueM&ufZP!ttcfi7m<}Bx80Ot{WxXf{B){)`<7rsNwznaLVMDy;4#r7lXtDeBG$TRqh`G3~Xo$z-der9jZj+C8hc@%3Pw zbg)W;&HTy;wQi{wMx0Tm4wh-gixr^@tu~T|_|z`Qmur-X4Z3KEd+LuEeL$VxTjhy{ z$}s<2yZ*-?yohAMYk;I_bV*{=CIcr278$5^^X50@fuiQranwLW_1hyqCcJ3@1^gKg zse-rD_Bm&!A4NSiL7|l_6K+s@1><&&VyL76w6D%GsJef%tCg=E@3U&1}8kBZS z$W+NJ->HZBVOVg&8Ku3aMiRJqk(g>(tt}Upkk1HF?O9ER6G>ijJ~Rh6o1S}bT5>}4 z%P$WkvF)`zV@u5IPpr9-(7#S44?TSOEBrURMI-Jl@-=_{tW;J%%?r9z-ytKNLe&~MY;&^%Eoy3S6Eh!j{}Cjbn>+!IyV-4- zc|EskE-Fy-jP`hR@*}R)1<)K`gH5A~&h|>2pML z@CXc2O2LU?Sn?TeSNYsy%6uVpg2hb9`q21ESIzI2@xtZlLcu(9>+{@=7Uhs`{r|pIX1OL4lOM8h;P2s$Zu8fwVw`!MTTe&tQUZaA-0iqjw|iBJR)E#KQ6^X`@@EG-dO6HuG7| z>|PoIpLv*MPuupZd1k8T#|E^WQ$hvbZq9*$5D9$Q@MO6hQuLGkOIF+*zacl>?AMhg zP@WciyQo{J`g2>klQ-(gemg9V$jkdQEaT2|Fs;Id|KUdl!rZ?Yu;CpOo>vU7$X_`8 z0TpbKc-fS8`8=~`B|;fJAJTarGQ1t?yQ-!cwqbya!+rL?M$Lwsv&ZU^zbNW${c1Ex z8DfKYeObXzE@A#;(Kwsz_R!{Ev$4_sUI#b4kt~Ng9ez8o{T+1~W^VZFHG`K+Mat_d zLAr?7fA3p2vFRaB9a~>OO15be*i6x_Pr8JSq_8Xnem(D4snUsM=Bw9YZgb=6hbhyM zdtNLey&vP0E-W{mihr69S%}<*vX)8=HcT&!wH$4{j$bMsOlr)$-t+Q2Zq9A3jp9Sl zP2dD{>+{%?cON>(%6IADjd=1jz-@Y}l6f57;;VWd z*Pck}ci>q04j6R%nGr~X{#VFbM+kb}8D?x_t?}K`K=rn_Y5*=x-REoM+2Nl!4$D(jce0n3zt7->v)g&6Cgmhagtt*(XK`P;uMc6gGOe;0&5~ao7=GApH4xWUdBk+++SNQ~J}@IW>ITw)4LnqC0OeSN!-LHKF4oWx z1g2n*yhJ=bv5cCC*Ek#NBVAM{$qU%P=z^~Y13vp6IT2H~FP-pwl?==AH!d*9e^`uL zG&3F491qkUdg%_c)c#IG3L-mBZB7mT>m+5gM>Ae9oB`#>S6ErboID7h&y-kj)m@7` zbN(t2CK%q~5-0>>g)D|o68-jYyD zMTiRjc4E;yrHGtP#0hLpGfQJ;l5w9OX8kF~HkztsUUUZHSb!4PeVRul*wA{<45bUd zmPcl%v-ynPu4dG9r*U&6Q0_?n79!j_ImjswyUaPmg(KpcDdnuN!dQ*>lyzOoG0pL~ z&pN`j)P#_IRH~Vws*L|VJlZRiP7XVR#(ln(l~N4std^;DN^(b(7SPR)bw(S-6Jw<= z@ru5~7CW2ju9C@FA1K9M4`opsF-EE)oo3P{$GZ1Qf)40LDj}lox zDwG#OioPq61m3EU>7*J9{zAuo*T>fya`K7(hnH+2Wg_6+Nm8mx{&qvgoI7h~Jl+UZ zkD$y;0kxo(T0l({&qQxLfBpBL?CgBV+Pm#A_2n5hT;ATZ*NP7UJa-y$MA0^RbLdeV zkQ^BX*3nYPWY*5GLDfrPH9~^#A9-mQcHFvLmy3|+t`Qhr{tvdP zl|fDL97Y|PSvCiT;E%u5!3}2qNj>i5b;&EwYayY)q$EG!Y#fO{QZKK`s z`1=&(E~flRe&h!w`*@r{a;S;wk?(8^sbrq{@aDcOhUKa#&5+x|jFMh@Xude(eE-Y7 zWZdSsBn8%Q{;4;=8B_ku3Jn|VWd13@&(*FttsK1O6uY-gMc_4b^GL1v0{CAG;BVIRH1)^WhpZvIch00&{ zpDC#8F7@RPuPGU(XB6c#&Hq*c38L!mN**}gOMPXP;ICG1>GMFE-;|NsNdnN5y%p6) zdX(I`E7Qq~SiAyGDgJXtc)cgOFEY`~FXi^B09K>vQ;!yC=#_FxbbNd-f^F25%;Xf= z+?4hYDKEH%mdMb>!vlGO*Z};Krlu&Z%~qog76;jE0F{#_!Xo zJCQsJ2ewXeM0S4uj`$Q25QdVOceQ6`f_L^JS!=^aapFN@BV+uQ@sgn${Qk8f>MEru6V4!!$d8H?9g1kg;6XJKo^iL71Ar z$qhW$Rs2IhTzT58eecD(Lh4&f?h9W}z$8*gnE7 zjG-u%;Gbn@Fj{LM#ybBaMGs81)M7+@Zr(Na&M=|Z`8i=1{MR$h7}oJ}HF^9VUW!|~ zBO(MHL>@u)+-nIRF~)TnggBIVX^_?cnX=mw@vj9vxcOfLlw; z2epGlJ{p467gS^sUO6M95>Msty%JIrPKRW9k+kNWIZKPr|)R0F|Swm9_2V;vbK_;REZ6}NTi?0e32+7erjzfR6fyW8K zo=U9;3LM$QDLGE^S6l*3&;958jzhTYS-XLDUN*u`)8LZ*)gZOp;+ZZ-#A)>V2=~-b z9-KKj8*;Cb6k1U;enEO8LXR40c^7_DlL6>zB?j{sIhZ*;>c7$b1op$gSq9V25#$ex z?<4_zEa-IXs$X5}%_zw~?>IV;%&Cuej^=eV9{mCQkrn-_mXM*MYX8iDv0_bKD}vZ) z+QuxMPkiLfZ%(VzGVp7dwA9;Gq7k;Pco3`=PmGSIW|0csmdXoQx!ci7u-{I*UJL7ZiyyLG~ zAT^s6ojP1t{X0Y6*Zh^cqTdzJd$eq0smR&84!Y8mIZJyH){DK9dFJ%eE6JS9c<$tP z{EpW^F+o`sY~8%ZjOfTY&m#Q^H^)HDT9JJWCV~CA9KM}MYU&YVckH*XWwOuhwLS=auF zA~tl1{#HlSpKjdrSLTr9{UkduJukGOAZ+mwx0f zgFzNjyj>$qSM)2J%hd*)MDrI5xQ$4I3_2Pu;0Tv(;>59tQoenw8mqeV_-U%^IvrLa z!kQRfbSS#kNdhZyUN7gw1rCR`wkC$QXOxO-1SE}kjw(i++(o>8*Er)`$%nK3Ny*8| zJHdz#rf>QC_wV?vlcPxKocx2f^P6Kel;agal4~u&bMK5JXZ1Zmo#gv)koYXf!@^N`Zp?9(dnsy>TTQ^ckyVkO-B_>yS>5^WtdV0CywUI%ZG#IUVm>j zZAO|zh1d}<9K95~Aick|6TL1%FmxY?DC<^X#IJsvd$aT>j~J((<8-)LZTQ)q&r+%* z=1ZuPDzKx=Q!2HOGmLm4-`VohU>^#!u9<12U*&<=9yhmvfX1~&7^c1XMht7kFDGxQ zLWh7*S*O+xcO`w0C;rznWJ+XoL>k=aimvYNDmB|pR8{nJ+g_lz(osaxz~<83c)nx zJD};-6u{MRGg>He1r9{0J$YEz-i?zd{qjsPyGk+)j?eG;g=WfF5X87GF^8>gEH7U& zrLGxAX!m72XibPpEM5DkX>Tsw5x(nhg=j{Ou#~W7HGw@nd-Cw(m)MMu#u=oJ7`w>k zu>9MT5ifq$CRe+dytZxI=vLU!tjgzDrOD!{fTR5roVngtOqKD>)uUHG9MEK?3qqLo zQNv66w1%NTErBPq<7U`}HBxjbk}FB2z>BT%G|5(%wF$>#7(G{dJWUV2ym0r)ET?w! zSRMW^$DnLci2c&lqv;!(5wpSR8S^Kh77Vak6yQ6sroX;b8cfdwkkxc4nHnjZZeBfl zzb{2Q{qh)8G)zC}!rt5jjap^=?In&$12Jrr7?rN?%<#@i)Rh8OOd_`e$kCjJw7+Ma zye+BIpRaRA|1>S#UZ|70j5N8VP9XAt?DuIqatm}Hq_-Ld3AMpZ_Jc!${%fiu!Pe`T z0<|#%&v{YIjNebX?9UB#waMn0j3SJV)KlHiuYp{RbxxOH_MtGAi6^iB3J5L$gDX6FD6*7(7!vw)LFieb z<(fUTJ17^~xcp4UvXi7t6cpeY?gpP@Ft0CfICBJ!4hue&nl26GBK^^P5`k`fUkF-J z$>&~ryHgXQq|360H_t?c-ZPFns65K+|9G^j(HF|sYY7?xmrQ8ejKGMvfN_NR`vcAj`y zA@5Nt$=}KXra0XHeq(MDmBTMuYzfIsUf?KOK6)a8h2=)TwyT3nbZ3 z`qF<^{8$V6UXP3fXg{{tvX4z|^JkofyDg^|tp;c`7dDsFOH;ZYPJ(OH(nq+r2!mf8 zrI7|9($?MOa`0sAC~Lm`0;pRi`qXKe!6Jp^^)>||4L>-JU%kS2T42k0%R!Z|NIMX4 z5>8}dxXFsT(UKPYX>8>PU%%udcXwcQxQ+Rpf+zIo=yqr)iM#koCaFzHF~BTLd7~kA zF7CVF%3j1Pr-9~=cLna~a_n&6Ais3#=3HkWT@GOllAr#1Z{gsyR?=U8%oyw{8{ zx};u(kPijl4Vc2H*GD6L_6o=YC%=ME7uz?-9;IbwXUNIEpKeB|Z@}PQi`Ox3k`x}u z{OoK)e&j)US8P+pMBe+%^YUCD@0Kdupe9pHY@ZFWcjKgmVG7BKhYy!x|0P=l##j23 zw0zw=y4xDDHN7n14?4*s@2*x2=gb-~#e_C@xt9L^kU7fz>0ap2r}N);5q#jY-~A}1 zkDt?W6%~)n!-CUhJPun~^4@&qBNhNbHO8pfq6KM8JHLxA6t47#xaP_9{aU`h!^o!W zt;A`iycevghC3;^(5H@{qjRZ{uH{unBv1S8jKe>&H#oS{bxDVG>dyr-T9j41Tt0(~ zI-EVf1+OhK^S~l4=Ok!v^wp9HhlAQf3;co%X69K9-GsB2cV3&`;Xz5pcC1B$h(Mtw zcbe`dNpBx^)oa10lQe@5j_`ZK%zz(C6)W91;%r`*Nl)O|l)s&FQd1Db8YiOwiY8~z znFv3Hf&Lr$^s-8Hq)8$8UH>oJoqMr$%5a?+yIwa$=-{mS$KUk$p>>u(+^=WW{%onM z;1Vw#+$()4a^BJ(2+>sCQVaa;>yuy>Hc=E**?!S2nwkx9{JKqKFgEQb3RSdp6QkPG`Ra&>Ffj(=y=quk)< zfE!@3R}^7v$+!FM0Y1CmBJ$DVBR&0l_p=GX9p|4FrI^=+TG2#2ELxPuw0qSDvP@(S z^c!1Hxz>a@_X=I&mWwm8tsnmB9$@>LpZ2H8K8Xi>#q`SO{jBa;&se4E`l=;c)P%aQ z%p|DJj-oltS;?PWYj2q%Gk2kozz^jeb*9_zY*xM0yJISUZTe=KIxL3tVGg9Ww%aaP zs+P2)Ph)|60~e*wNm!<)7;L7Qs?q-7R$#_&C-=yyafcJp;zqH%#GqgH7Fm>^8b*hL z0@SDkX!CY?aC1mrqn8)oDk{EufYP!(m~7i_tKgE`G%l*F%ud}-Itvu#R;rzx z&DtYBZ`gM=fG&nt`#b5X>lg`gru;`YUd&0XHR>z+VSylGd;#r7Rn^`jd^~aO6(=Q< zoG{7Nv^dk0Y0zy|KehfYVDQA$rF0hy+UQiyshLl}<@a*me~;});<$Rn%uEZq4G)nI zJZL?t5lVy>Qd7o3re**olB+7D@>TC4McVh&y}ZRdvtotM#nU~faYS?N)J${miP3}{@a=cmu?0ZMJ`VbsNTg)4z4FNK@+?`ARVaa;Vz3_iJ!n&nf{rwCUHQ`*mL zbNYKR=wsR5DCOc1QqKBSje=?9Y{=toxW>^DB{Fr()q{V}#yO#YuF&%Q2Q$^0$8;0> zuQwOCHFfaGk}UAm%Pnb6S3~XQwmaUs4o7m}MXrB6(HNz1NtTZSeSLW**_@Kn$Li-? zaMsbUOYo$Mg8R{dVsGy5yE!vu>eH873ddBZ1IPcCR=EH6B*xyD8@ZQ#M^Uo0P_Sx= z{iKAog_Iun0c06(xl_vHlwz)Eo5u8?MFCSq^c&l+9j;{`rr-_j@4K0MX?{PDP#MeIiNIh|KeXC~M=e5_%%b%4BOBYTn=f(9= zYnO6d!L&Fz z8WkO_coHnVyQ+JSu9)DU2Fh4lqPUI>0xYBX3;x`qWaz{=)aH4LR+_$x9Z8BXroQGp z>85!XpTQ$Hflfb(5!1}Ps|!lK6XVebxsRIft|ndB6|cgTz6BpuoXU>DFz0J@xvN8m z7U5{ zS9{@Sg_*2Zbvo%|YM1Ql1C#S>Pq>pZTTcqFgF0Zc;?JuDMjt&1a=KgQy}jSKOBMOY z(O*W-*U$FFU8)*++X^aOw^!T)=a}w46XBE^-nRV)pCN^S$HR!{@CX54AqW)dLQ43B2G+ zYqVnXx;^+>bMvYaT~G05s?BHfll%Fg@SFZmV*oh*9oqszSBSaN_EGVJyWXB8M@_ZQ zay=I%Fl&pbBBNx_lD!F$JrBomT*6qaLAV22T_K>9ZW;q7&Am%`INuknuA4+s1BjfiB}* ziUz1hb5D7QE2V_*m}ZhJ7^33{@ZCmxTTrW^L*mTWygS;mtrPjH8L4dgu9ofjUz0IzHuTPX>2< z^U}QJpVt@N+7G;n15)-y#R5Iwee!?Syx_uf^X9GUXQ7@M4r|vYDAKt$KJH6lkFrnP zm731;gERqbEk*EWFiLqvB&QM||N0H(UmO(MZJVU0AN8yb0-0QFwjBG>KWp8dZ=3gM zz4+ELKT8MUdVhzU-%?;iwNrgSh}BZ?KhG;)jRz9bsZXAEUJ41Qj$>x$#Fu7 zDQRw>-fp~fL7JBezE@)ixQaW=^eG{@_njwz4t5G&5<#+s51ni$2<$M5+Gi4ow^pC2 zR_Eha^khz+-UjB&$Sv0igWyED_tR`bE-s0P@wNxdwjqyr%7X~n@2gqt+Aass%tsIq zU??xIF4`1H#x6{E4Wkb&2zLppMG0;!-M5HDoS2l#5>Uf9eX92js(H_E4v4yhp8Y&$ zz>#-=OSs*T*_d+70eBk_c;L3;f5pQyi`im!{)I)SKnKA+l|_ocNSxtOJ1ar)9)|)> z!RK20{f}+-r`qF3KFpQ#gjDV7`h;$g9`Z4$VN9|Y=ZEg$vCR^y{Nd?I&Y$PXJ>aU3EkdNojC#=(VZBgc$SB# zw@Zto(!GYqJ)w)oU17A=z;E5K9dK0;@fknxJKyLH!BZlUqDwuGenH-@lF>Bv^f0S8 zZ9-*@2sT%}=OYo35(EU{5bDf?;%4r~>%$_#H<&#PE9PVP$bz@0p?fR&FxS-74VC22p8eQ zw--m#7ja<(8LN*qoPWRK%Cfw1Pkg1kWZU{SJwf{G{+3^IJmzi%-gsXnDZ7}kyY8Hm{>vJ}`j z69o9nZxC?OLwl0--m-q)e^*j+?n4)Mm+}UShhbYA?JxrYG;9rC8fu2~K#74$g`a*TyOE9;_as5t z`F7k0E8`&kasv67B#VE9uHR{m_1fZgk&ajz=IJR<#jyqa0dGuRS_;^ygIjzH0mt!C zZ+A}b{EV|*V(UG>zh(5*pCK7eXvtb}3~>*n&0ZzoymL;Ap+s4uAa^ECIPN;2MLLJ> zPdE-+8IcgAU%P>AfLk9?1}#4T*VxP%7Bk@dL@R!9h_||x{%n;dVr(L^q!REK1{BKF zMTt2{&iCvu7^w*1J z7leDB>iUC924;AcPX@GH0auwEab$zW=R71JAldV{3AF!eqt|mauvnn~dCp!PT+yxVFFG?PN3bNa(V}<`$o0S^^$$&X1&Og%r%0i@}D=jfQyW` z{sdsKA926z0GZfz^Th@nR8n_p5Pbd~RAC9&@6O&Kh=_!oh`1>@_#Ub`qMBdtonU=b z>vUbtc0={>e-fQy2p^QldUbB2nj@0Ygb(#m(Bf!&rrTHcv&8^cDfrSLE{x)G6~`X* zXRkNz*jLtOcKFJUVZpOlR8XTuz`DBxFXUGU zMIbDn{5>G-)mu3m2J8Ar{af^L z3YmiUI4bboyRc0gS{0g4ZKM3IiYQl?LpWz!|&Az*&*?mk*#ayV)83 z6`S&>3yW`vRGxf-kIr3nZi6m$n{>L3aBUMrAPoH?bSWi{dnkR@eM?62E~p~|z}t#I zQ!ok+U5d-!{j>;tseL4V(&y!N)=iS(PA5VDO|D4&+yZ&lME}(R#>#{c7it?=|reSm=bAX+tx{I0UsIPd?s`s3_#Y3 zXWSQGeNl0CJH3j1ugXcs{p{o*ZpS`BT#gz&OqJ+;Ci#H0SMJ`yZF7QrF>{w&qB6al z5tb_f$Xl;ZcaJ!!V}2T4Q;M>1Hf3)n8XRAm(22TN5^mEaFcakr z{y6Jbx!%HfF~e-uYORj!V9K~mOM2AqzTQ$fxyHUc)7-_#na$VQuT@&_AF%SjC3c|v zYG&0%Rz-wl+for@dF7YW=#wY9E)4aB#BI0h)hnWu-+Mi9!YS`1l#a5ui#@po%ct{s zhU$zmEJYD^k%9i>GcU4#>#Xa!^OiHqo?J%lUQ2cjbyC4iAb8M7hI8EPTw^xE_Qfl; zJ3BdWK7(fJCLX9dWLL&aRm$LuU3Qr1hUXjQB93l79YvJ5P>M-T@!2<&ZCS0V5g%!& zZS!paN>?F4CxZA^2C|avTo65!8^2=alO_5E*19USbcas_Q0};EoKts}d))inLHb1| z6I0S|4jlUo5sgU<#n_57B`QJ|ADT49*mC4gh^`5Hz0oXk?kIgaijg0G|Kqgw>h~E7 zA1tWhmr?d^nRSKqJSHCzc4*9IhK7s=z&k6WSCRfIDqCWV736dx;gA`U?t3TYIHsuurr(3# zN}qnj$Sd)bUI`6=Ni|vAZcy4wC>*_NZ=Jhup}jlVwu)ryiX#3iv&OI%Bzx15?tD$6 zsj>{3>-Q0pm6(_)*`e}(ry?!F)j<)E+g3At)-2|Pso(6PC( z#N@P^FBBrHc7gjIqc2jyMkT`x<&bn7|Neoe##4(TzP4>Z%L%V|*=W60ZZwDX57w4j zz-RB6+KF-+H6T7>>Kl#QSMnJWY`yJ9_^OXAYAulC7re5n#&|a;ff_4n-5pITI=U8= zJC*6Bkb=latoOTSph4Uz#!id9K_|j)seg6rJ#M<;V9}1foi;|zsXZh!^KDr6d$>Jq z_Zkz$-SKlHw}MrJmjTt!W;9AaDbkX@3%gdoZ`NF!O`e|u@%Gi=1cPNU;K*syzd7cJ zK~Dme`WYQYoOyfeKFjp98sqqh#Y1!$xfPmfMcAqU9hNBeu(LU{;W|unS8P*GQ^eeN zjh!8mm+7;uay5*(5Tg^NG&(FN(b^@v1^l!%t)AYx58m%c>kYwrb3Z(`nMbkk)t?b{*UNc&nO0q^3#z>>G6#Sp;MzqM-_x zwfn?JQ_Q$B8R$FAir|pFu8G={=#HBt&pJ>jJBzVF>-Lw8oZ6Jul2z(+T?#jAR*ow0 z`IQ#QILJ0o73>0>l*NBB!~0uSCjs5XG5;cxAr8K$K^?I3P!KxIrzOreU&w^o%H9O3B9m3o4!bPTm=HVy@cXXJoW&bjz2Nz13%uGfZMIAb zhwG+&$vt>5bFqI|6b2aM?PFcg6_2>`Svc#sv9ik)q1B~n2uo-j!$@BHpI)+_+Ldw* z-x^Z5v)$i9Aa8t}sHq?>P!UV5Gzo`~6PF%cjLl?Eob5mV;pU;fosc_v7~O81%JeRK zQ#Jt}=4hz0J38+F`Hn|}&PlEh7alI|!EC3paP^DDY7bu&LQG`Kt_;fnlBolCPcsh{ zX3qBkbq@i#dZlGyi-NKaZa(QJBWi{QD$o`yODl0R-}sCZ$DpD9TtTEp^fSi3j6ZR{LbR?53LCkX9doXx6MP_ zbkmUTVY`JG$xHuJGo!Tb;)!d!lg?r()G2)uGu?9s=wdM@v!Qsb2OB*za=GG>3?Hqx zt`a2g$;ekn%H{dczwC$eEQ{q&oPrY|a27aTiwicB{r z)GB_ft+6N9Y724EC4X9Di?37oXv&<6umWBUuwmG#=89|Oo*M}{?LIKu35DyD37DrL z^t_~B5O<=&IK9A14VoNfxyppg_LdjEN0Q%J$GBLySWyT^`k(AOL>KF6MrzictK+?p z(wJ^7AfYhL?<|?r-=|1-Rm6=`zgfd*HxJPHDVGfzurWPAL}d#!Umepm411ITaexeK zBqepX;$;2FO%x?f9DG<1e4QisQb$`kH^)a2&(WQOW=;Xrutt7o50;63CCc;P*!OT< z>npWV?w(eXil8wPF|sCw*MPC#^e14%m{8bKjIQSN;B>eazLr2mAL(sO3#x1R|| z4~x7MY8sseMP%L8pBw~$k>_{M=`pkHj==HT)Fey+0-}kg@Zj?Ro=;$|K-Dp^-rU-3 zZp(~Is@GfvJ(C;jXT_a;LF4G>*mT+ZrN=+4m{ov3&t|d~ zjVkXNKVgxLhqHD$6&f^?do`rsQbfDALmsll!~Lq$AE2zqB&Q?7%-?^YAj_OC-@+;$ zk-liNa#B_8%DC^b@+pD+o>S5s7>o9f0X9Zgi=Q?7d>s{Te-|Q3Gv9g`1XmX_S3yTb z-ZF_wUdJkZrhJIv0^Gaelrw#P=kM_9l9d6@N3}6N`)99?8$p=&SRE=5s%*}vQSP z|3k(>CS1g{((?RNV}ekC-v|eKsUX*He5L0p=tN0uZMtbrxuV}POPJwKjX+IS%Vyq9o6SU54QE7(o zIWAo?VcCGoBQ6~E!MK+fKr zS<#m=XqF#eBAnqd}B@!c|T{}$EH!2&NS8dh_)kDMr=W?kC0uLQ|^;y zPmc!5{`ZlLPl>_(JHm!$*^KhcF=Oss7;E12pufV6Nu_Qbl#^qUh(+tG>FNTNxktEs zw5~L)<@h-_?6U#5ommP@2!14OPP=(nE>}ulQJ@*o?9|n71Aqb5ox|ZLM-Qeb_BxpO zH)`)49Btf{V1AMMm6an9SZY+2S6EmWx({O8Lu|Tcw{1$}*a{=BJKrD`Mp(08 zt7H4tlY0anj1Xu8ok$5lX0&OU0y%4`j2kCRzk$zzp7~v;4BvAf-BBCT1T*wW%*$`0 zNdE(VQiq_@v($l>Po|{?pVa|1#}F>qF~2hjgV0CO+fy&15CuL@yzIN8(r~rKGut7) z%PviHWW_{Cv`O2%5(-z~m!>BWA1BaFK7&3pULr+p(3+tpOP|;H9{m29d#K(te<}Z% z`Geo)IBARSJB`_(XHgw2bk}mb{L2b_M7^X3_@!{6MtF`ea#95Tg?pl60uF1G# zrY^Keq-dpFy3Ik)VR`7OfS7UCQ z;#kMkBrCCU;)w!iG=kXDM?ef4F8O8tv0>4H!o*qafIk;o5=nu0AXoWB8FQ&R>!5O zuSP^>SY;lvaRM*-o|47{Cudzg%w@ithLR_m-B~X4l#-B;h(GUX;utIg{iXt_c9%v6 z6tR>iekdRHww%rjRygdU$L?Ud1~Mu82YwQRWdL(?CMn)!9O@O%jbdS0x8>+vINtsA z`D0))3y1PJ1(xC*QXvijWce z-VTv)7pb8WIW>Km4PRo;$qd)XPl$RFtq9YYG-`35r5n#E!7!quAC6|qLLc}uB&k> ziDw1a>m~d+t6k~g#y)nmJ!!mudPO80A)8oF*3T4$zD=hzTF6+TorJM{#1u->F`WmNBr~uM z@oLArpqBRjy0F*16X$Mmd9U9DVPiq}dGW-|wgYlk7}F9(S5yudn`^g2Hw;U1vAuc@ zRF=P9xKk%8sX*sh44QxcRw@pbT8_;o;{q*rzL$0*Y|x+n@<-hX!&oP-^{ew${*d*6 z+T##z?8xxoE1gfx3-sEku@T(o;@YFjdat_kjl;Pl0}|dVAZj}Bxt7f z`w9Q$S3|Q7yE9FRI{mL+y{s&xJmE{($IFLTwryl{zbV>b?@XN1+ubentu>TRC-XO| zQBNAC=cpR!a?2yHkg-_;I_YZHZuVW2#HV)Dz0OIAr*T(1~RzSJDrQeVDMmPkCHpI+rok}`5y-`?WM-44KTtF}A zWg@(p@yRun@iNA{Ov@8Gj8xaMp~YIVCY4jUg_QbW%IPb@$G@F=SV3P7QMpbigQu7` z%00JxCJV>0!GK(%>5xjxyhrVj*~*GKU}vcyUE^e%0^g7pU)TtCH#C0pu2`hH=`SUn z$Yh4bbUorjkHs5%!y%hjGHHAiiNWl`P*FnK?pgaQtj#6%{L^EcCwQ>&@Cs`Yp95at=p^TTB15+2Z(0;LX9j#*s)#aufBaj@-i9*jNi#k{ zKhl~mcbtr$$l@sKckVMDx3)4=do;|Q8adjg(VEpWMIhSvoyjK3MKpE-O58B5CQ&FM z1;(`1)4Xs9II#zAJ4e>B`RyTB`1g);{M_iXgGEaQds6*Y*)^ASR#cRw#Z!>}k-s*A zAEF9MYq!>onp@iuo9`lUSvz>wkEHCSKbMl5!7CJw=$UyWgOp<~+GP6OK^lWScC8Xd z&C@3C6M@}lxs~zA!H6`Zzwp_KxUT?u z9*p?7m4J~L-o71BEOmyWnyQ|LRD>`9fvtJu9Y7->*I~>8TdxGovrCou=rmDf#lgp` zN`HTOIb&wCkNfAb-^khBiq5KuilwiaelPdLk^VK%0^>pVvyI@fqC`$8RK7%{f?g`;`Q6wLLP{O$QD-uWiRT_)>HT2aa*k5jK?ta$fG1ENIzuGb>%f})N zJQM9=i$FvtO5MBqbyB)>H=yJ@#+)M=v}72bFL84Deadm%F?LGI#c-6u^`HeV%?#th z@_}6&&~s!}zGp#Ly(_=z{gf3Lx&Qtq=*ufNhvL4oQ_0~Kf(qJ?E1mN);qdfTl_h%c z4E5AtCex_pyd77pg!lG+g`0m-!?Mt?P}3=Kb@iF?(025zKWGlEvnbrFo4|Q-Z5YVp z?RF9AZJ_Y~mjBE!O=jpH4hGN($7~-90uZq^B2bH&Fnk15k9uQfi_b={tMJf0LczkK zaK`_b2-BBKN?pvY&5em>%-=}`YMi(rSRjTUa5|v@Wpg)}krlKEj9ydXpnHN!$1s5Z z!)H|B56q13;QrRx6~(N{N2{M>zs3_3l`J=);e-1Q-W^&pw=3#$#$V(lV1<7(D zqw+wDV8;a?o$5DUjaqLo=BCj|GzZws=%2hesn4g11i^`x_8)`nA990r^!+x;rhRywEalknoBD?*vRo)HOk_%O%G)8 zc!P;WqWKb~rb2#21rxmtHvdn7`5$R3b9sp;UL!3+F{{s?FQzM`#KZGPjoW97q46M763#>{!k_PO?2=JRrhFzk5AT8nG_Dg&VLb8kgq+mT>4;m;#fnbnA?E-y`!pIgIWvLPE# z=B2o7ng8)`&|7*TKf{S%_yDqdoh=__+W1jNp&RloHrI5x-_oM_Z77m0aiWO}BnxFS zqs9kxPNvTzeiI@WVe}UR3;AEc8M*^59H%Ys@e4I58Sb)!ZC?s}FcQ_$ilibC`8jXx zEMoOtmfzS+>^}f=4Sa=%F!EBjs(90XoIf=hA`rg)r%XHhpFHHn=|?Su!KY}66Mf!- zmZmmdd1U{Q`|)fU_(Sh?w8I5kahI1+t!^_9!Y?G9(}_u_d-)E3OeWYu>Ur8UA_DSH zk7V>sWLEVOc#dByVq!H|4JIptpu*zDcmAibqQuYDOTgh`j-N-+UKKMpc3ut} zmG5>%Xs&i-xA~hDQoR^1Yf;0PtioS zGuu*|-2azUJ|CMVlgQVROaEf4=cRif7hfH2lNmekE6`VriW_Wy0C5^7?|7qGYxVG% z7UYeQEH3p7T@kBgIyO5Rvm$)y8(X?|9{zn zyZ_AP?6M_en%I$L%A0t6tEkO1>bN`=MH&7-yES*)vG?Eq++{cn_e;BGe;Yx8-!Rq% zwwOw-PRV=z&mK;0O$VPR3>8MbW3Mk#QzUH(tm3`O^99LrtKHc1!GF3o{b?+HS)5&+ zW%-q9K4hAAQt~=}UDpxG4@5sY_W#&kO=i@m=PmlFOZ7UAY4v5IzQSK`#Vim^+@)E` zA^hk6+NPt2PKQ4vJH9!JQjWe#6;A#ea?p{Ps!$Rr6UBeI6C0CZ;Pt#AaJ=Q~gXjc( z&L6+T{))FoX-PwCyFb?_c%|Mrc)6raCU)e6rLmiSNOmV5USCi`3thDGWq@-hmYKc;~8 z)1P^GXhlOSNW2#KKi6J{x}EBu+}c@B73zbQd{}a4z6m}f`*5S1`tfNQDb-V`d?BqmEk1SEL&9naN;gyR;S4B?%>7~=k>fBUrs4Wx^3~u zqgHIgY}9r6n<0pUf1wY1&0Bk+0q%vCfBjvkBjd~B^*0x!_~fCBtcoOgS8qLy4|FaC^{dQPp z!xjIXXhdPSXRY^Z><4_F#dMPjT4ToZkN+7)x9}P3vGD#Ih06+TovJb>Uu7Y4k@&=A z@vDrENSJ#I;P*$+am^2PugoWAtp!&cU7;fNg4=n&mhk**848dydH;{oe~hJx@BcX# zK3E`<_xT>9lv(~JF4P$xR{6G6aD!WSM$G<&pR@x<$LlKT?}3uc+w+ngOg})j5A_@n zW0kRw9>>o8$F`Sn?yFpl-l6)9%{0#OW0tjEs!BXk#q`mYT8rIK+g1k8lf*Ci=4@!U ze+@Y#o-@xU-kBhA+F4K^{_9{{k_l1fNZ%3M|Cev&kNLLZ;OIQrH1`yZ$wCVuFFsVC zICRL|syh@~^Z)0(^qm03XHPyAfD0RSD#Z;@>valAf5Y$>!G!1?5QhGb=HmIgl02=X zGxJ42>>K5@Q3aa_p5%{f^;T>N+-1yV|00{SMk7fJ>>h015SY5tFAE}6q5;^!3h~m4 z-4w2s{}+T$RNxNHihq&|W=2&Z_6Rx2|6!@`a#l!)R;=0Dn|}i4@9UFRZr0pvW2{z% z2iNy$>XrYsv|cEdnM zaHtrSR|*QzO~9Fd2Z}q`>Q24!YOq(-;ws}Gef;ru=s&mgS#c!=6b~4Fx&rCB2Afdx zSs{x2%bVkCr_pzt=P}*?R?7BEaZLochaq$QDkVARtoHf}-0%ynPmRMvw0wzb^LPL8 z9yYd3q>fTJ%^9G2jjboJ32k~nw`uH^IenFWeMsGZF_>W?8S+!b5=3VnqKdxYtD{`-6Fy{sMN==HP z&ZtlS`-a-=e*_B=Dj8+*bAqelz;n*E>8}ijQXF_?s;0sUv?A<0|Fht)dvI|RaAm#H zki-|Kk2zr#M&rX1U^!6`)1@eAtFg@KuKqtWW=UbCK4_>^Wnh)+sTlY7f06nE__XWB zloL#d@gC-G{0ClC?$-|PbkMj5aH&jvx~n&^vG{l0=^3eJD$BIln(se@kEP;Ra;4sb z1f@v*2pS^pt%5aGqSP&(%B12dV(tGTD-96K^+s;$kYq!33o7~Cs)EET{zjG z{^6DOZl!KTATxevt$HgVJ)6t_=V_?%|-r)+IqTC5_ z6BAIv<`nrIZGvh2CJ1<(P-92pA>*Jupv1dXx=6XFoRcl_tXC91#+(kejKL{B?zgJx zz5Lk#nx^3zgqNCIn-aRMG+Rpq_78MXnmB6kxj8M2&$31d?})hUT&MS^&#*yS!*KiZ z1^bT|hWZdJ(^%Ysbk6_^l^{e-1Mnh1yr^~L(EfeK&^zSpwVOnSRT^yHg-2$9S=kM;SV1(b- zhBcd!Lg&LYAoZO~(wo=y zk0wz>OsLuBwZDyva=_E8Joi)AQdA`waxYd7fa8PJah=urjU^~t4soyB? zaodymyQ-sa)arix46c_iJ~*=7rq#XS9f+%2n`8HtmK;A}5L)Ab4li&5u-7CaXN%xX z1*Xi>{C4(Ox!w1~Gar%z7_WK5jMq6YLSFJ+gutUtIRa{JkmnnmDD)MMl(%q1n)Z73 zRpw8k_ryKcb{2W6)na5g<%FOi;F6hvclhDDK z7?TE<*?Rw;ST=|^fT~NcCZ6gMY?A4WMf=Y@!%T^GQfY7la(igmmkn9r%QugQHICk$ zHi4f6jr9k%Al{$WGos}e%UjY}`bxv$8>v5$mB>u63uogg*o;X;O$S?bEmb!PRw#{BDW;GO`6KO)9&3B^wIYBJs0aW!-{Sk1HLQHFYy^FGc zXSX(J*|d6>+15;7h*bmO=UGZ``jKCEWT6eLH@Shxry{*@t;w5=R5Q+exMhjs?fvz! z?hEDBEJk0yi+)M#$+}EmT+T;#kO$UWO@02a`jS-y>dx<33p@X|uN4^BmA2 zF>`Z_oPfN%Rj{_vzT7gvPJ-qFPP^pzV5jfj+ych>?Gg?3g-!KaO6|J%xd9s=yQP)x z?pxOz^*rotc`%4jPbO@X;3Qa869X$#SpTg=o_909i{*8){>w zb+*6(A%A;p9G4cBf1eGl);=g%x* zLdF|1@4X&9Ral&1|C1e_t(*mHooRb~FJTLFfE;*RKkfFBeujGqU%$0%>V(Mxr*OGy0*t?^99)BiVL^c^}uPCC>^st0B6EwOMAmRczWua8G*xpe1a7BrE)0-=7?nO0X z67G|OW*)F9L`&%l0nb%t#t6UpS6d(QT!4ja_y&*Ilhd%WUbOxB$Se-Y20^=fE6+;0 z&ev8cvO=ZhGfDy_P)COgUp_2zFKb1?1*l<N`P^+*K6W> z(d6=xpnUE~#8cre3gkD(NpqbTcDUa0edZN~-0~rKSZm(zp~8Io*FD~Yv&wHFtWdt> zzRFon%_4e6-8(5rq(3=QLfEU`c*JXprbCS}9ZOFTD%Oy?zhT z+PSy~;gIkTa9U{gcVvs_%BwSDW|?Ij9xMnD;MmXa@?J<~;)^DgrPz7DIFp-|9`3U? zOrG2P?m|%erU34~_~_}N0Xei$k{<_WFfi*`=1u3D*04UK(~@}=ut8=vruvP8g18{wrJyMFVoK)*c{?*j3sVISfaK|Fsy+@}<|}xuB@l>wL$=iPHiUgbLcB@l33brarrPZ;gIwV1*taG zM0L6bk~{dNT#R@JgbII`+i&wrZQS;>o&2!W?EDqj@_Zb66*vjUy;)+x-kD+4;snlV zV3;kxbUWl!Ef(b=^6(R}h-{Venmo91>7xeUZ<>+KY5!&?MHAy|iBPzG3N04ZOrnp9 zTKA8qPbB*u4@pn_!40zmwD3Y9iaB%`9e(+@xkn(SbnD2M#LCJC;p%F@z^u?EI$?#G z4ixNStc5Xa>Q{GR{%T`{BONYVq3L+54@(-n!hqz&A zl=S-VxzE#(%JiT5ftMQQw=ZGQuz7zHAS+_)dUWHqhdQdW4(-dO4!^i-A`QJb)5RXP zx`mPX#b&Llm28DJIK9QW%V5ea`1AemNlxxB>W3rrYgPBW-K$`SBzuJ|(i4Tevkd$F z9KL}(-mKs-TLwIxnYkLw@`G_RGV~QGa4WTda)>^~T}ps*ENl52bcdsg8^gxU8eo>l6mDJg-RxQf!Wxq$-mUU| zP7*e0ZgB7(9O-Hd_w!ZzByyM8wP^G?vyi)VI)phzbeZj5y97&Pocwuvu*+%k(N*Kc zW3UQGXqu)%6|A0O$9ysDg+g;4{b!z_~xgWV;rR zkQc~T1)L@(sz+Mp++e}l?RLMX>i8ODk06W>?T9H97LNZ+r>#sxBWzpId0V###hO>S zD4L|G&VbzcGwc|nuf^2Th>m!+wR>emeOPnm6<{V)te_C4*IOW#>jY-i$4JEVEbQ4F z^J?Lh-JOxKmNuGtLg%?=nh1nm+hP4G3)7|HW&iOnPtuxdM$CLb+4g9$m49X9buU_J zH5P|`Y@&q~oy<`$?zFQTP~?%-{c1DgY?RYqJ%*;*_3%}@ii2F=fKk#!xPGqui*I~l zkZd1UB(i@vw>YlobnVG-X^RWGX{Ii%A%O%Y=SCF+HxWsE3)kcaZn@6aIZb|?y*^!= zVbIX?0>e?cE3FAsir+f`P142*Y4ucBN;FQ^TtFGk*dJSC&5kP9Z{Zt?h8 zi_g0IAWG7U?~e|MCiASnceA%HB=vuUu(+*Rtv+(6)!ut^vu5U)8M}it+w)E`twufF z<3PU&(7ihmde#dE4;xeSWLn#`?-vE`X^DausO#k zrzgh62RHO{W4*AYVqTjJv$RguMgu2OM6rPr(0nhK9j;b@?_$Az&_X+`3B$o`IBv#A zTes|TIa)xO8zv*DJeV2VPwi#Vvz+-G;uihX4(Pe)fqq1*=2QALzw_3G$%6U7>?+-i zc6^{86n^%S;*2KJHirH6Vn>LDi>DjH!9L9x^3(fc!=xZjuo#)qLb-0A3L8|H>H(^N z7B)Bw6L+0&bSZEclV|EFymIE2+wiEeT#w^YzE~_TK+Al5xo}&5PWiV>3Xb|4xN!~{ zqLh^Z8v`{zxkPh8@p&5{t!eV9-{b+?J><|*g z!3QFqGk=~$O1;OyFCQm=vX&pWE_;4t9jBmX@H>@J*z{!3jt|Rve{A_x#m`#*kd-Ym zf!;*1<)8Y_Pt`b}tFL3g0gTsB>D4U=3_w*u)llgS`xd?&ftyurrbjXGGh#fpn1kPK zzZ;QvjJUPU<;CI93pT01Ibuqvba^5PMPtqL)H_|gdPNC$u=hRIQ*dB8?wHb~2ebcU z?sQw$?OomGOak-8mM7UGE1pC@*(8U4H{Wi#Qzhn_hOt^R#vJwVGh}@hU-n6V2w4jx zW9nt$SaTBoGdoRKs!*1n1o5QuI!OaJsk4dsB5XTk_Kz@1)$WuyriExI;GB3Jjpc50b-?(O}5lK$^8tjWW6OWm|IBh;1I| zi~TY;SL{YHJ`*u-dUgL9Yz1)o(&{vR$^J|4=cu~#-l7cN#7v3$!CA@LuIEq%nt)vL zy+aF!xgqj!(vkc)mczZzYDY3>zd8_%0ah1Kvdm>k{TYWDouxzGo37cqS8cy1iV3=9 ze{xOxL=q_dQx3`zez;u=5A(wvIzNB(%6No0@U$P%MpqxGgkqz`=G`GzUH}pNryK-9 z1*SjF95p>#9K2p9miwGp&V4e}c4gFo`Fazr7ayRr>|ZCm(|(`fF3rqEbpEMmLD{GJ z$=S)XiN*xD6JwuIvzaufa*)LC@seJk&HIY+Y>{Ta#bkjSGp{za5|*?>Z5)$?Oqx=L zdi94gm5FVQ7KVUN%<0OkWX=L_=Cmnt&LD!o3L$J|Yt|jmc`^1*-siC6iWPzii57*5 z6w=Y)4Q&5AE8R&H%XK1=ch3IdG1z*6B|j+Tf{6(z^T6Db{T!wR2T@;BK+u|8HnW#> zixsOk>dEr@s89q?E@^5uRq^Y~%!Wz_iX;y9GWGIGa%u-<`9?+zXP@sEDd=$<{MKC+ zX~w13)w_z1LCqDby~S?Ucs`Q!7kRelEp}U(2pd5g4YAeY0bJVBWxx^0G&t*At5-+F zx%5^I=i5x`&CDlY(7=_BGFlW=uPx`WBK#e=;sjDgyPbXW`F*cr)qwZ zk`tfFk!CE=245T{W=mVOhh4*N?A_GquZ}BEX8G{;&6bZ5^Ky)^+g4Q#ZPHDNXLJFS zJv|sjz(G?F4tHs8Z1WrMDo}V2etv;f5mO`lG57$(tC=ipRv&$YbJt#UXy^$XI_)W! zyYpsWmH5$XY1>*Kc;waDuzP}_ow+wXW`^8Ccz_S)>Zcw1>-q~tACFp6x&b7LZzXIXlYTn)65ike;CSD2X1!&ddEJ5) z`Ggc4N+S(KjEY+)t?#cjC)&0LFi;e2c=hjzKsq%=+V-m{aK(kQoe2Ojn=Webt>5?A zy5)~7)HIxh3gg2TzUu|a(}7mafBL>|x8eYi66br)i>t8S>m{2iGmOuK#_Rm+yJqW1 zup^mbGkDvJLF1S{P0x%^83lyL_Ks-d^e=(s=gLn_k!v%)V38hyD@_%*PkIvGs+%qq zb9GzLE89j2TPmozTy7yq1a@Ndpm7Y1x~WQoEw!&!1kKQ^38>5|#L9O2YyQ#c+WoIX zRz0C+}!vS+%oW!HtxlnbOx2x zM3@Mg%X0R4vkwUpT|Dmtv7@yc1mB2K>nYQV){K1qL2@7yMfzy(L++*+jD`m+R4W?v zVz^*D?nT>0j@DrJO46$$rGMG~o08JAaMgCj+6q5*Ib2#xhGeC!GR5u=T&uEg8i~FR z4||Q%lYrY?-M_9qZsC9M>Yf!DcBMd5cXFz(O^Y}+W8=zRM6KZvuLC4zw!d)f*jsEt zzIW|*VS8q0I5@LBa*;^~^H|WiYFq!Y-G6UXXfZwMp&!2P=kHt#T)EjyDugsUea@`8EU!3>iqb6nF_QVk@A!t<+?_p11_FC4 zkuO(uS^+w#vpev@*_y;!r)m!jJ2PD2h;z?d3c3^R@<62RG_p{kv;bT6!qv3N(XFL?;t@A=5mE%wzxhytyxaucK$)afR?Z#H4@DRZJ_LXHD^@QTA}S%pRG-n=MJEC z(^VX$$s`##p)f&cMTX{=99t=UdoS?52gFZ7adw`M2GKwuk{Q;zBPKRZqDd+%?TZ_J*RBTD2alwe&VV)a`?gfNVu3#lyDTanxR(u z%x`J502U}ZHl!_OxXnxNvG0;sm+QVU;aSgcXo$mAZIpe(N2aOisW$7S%a` z{tVoNWlN?{L3Uv=qzm_c@vKD-`uw6WIsO{!?B}?qI2_7ZMp%T*yanX8XHxW?o-e9P zT*>R@73JiAFdi_JeespPzr^9fV|}z>$=mtvl7y4Oykpz^?-_Pc-(s|PyMS9!9og2r zB9uzIFNFvj>LvaW1@79y{!KdV^2XqdR8`8ubWPLt!s`OH5Q3?G6`BSDn^f`ZKkRHd zffp)(r_n7Nz<5pPXJ(V%wAPF5nlJ4{4Yvn#H0#e#(_<}{%1ics!@<|Fdx#We)n5i) zJ-)TPHLP%vXZJuQDCL}5Ixv4*)ernYK=S!xhi&r7tC70HNh1OuJ}> z-8b{L7zoMAD4`EH>b=V7UQ}_DIE?R)xpAR5EnH2sKY07pq@Y{ z?VPFFeg|1EfC!+7ih0B=cB~5^fc=EnhvpDq-F}Z&GB90$RocubhF;B z=fZ`@)#-F_!a31vD*solUx`NqVqJq-Z(%5}KiQnp0kX@Hu)ZchXH7VNvEL7K&CdLD zEY4=@UOR1gR_l5~p9(P8&9Sq5V(!?e;iQI5A~Uu6-e1B}Ot)#i9((ta_!ud;J98Tp z5@~iZ#fF9ge)adLWKxJW#5fuMh25lB}-8uA+(5WgDf+Q zLD@p6WSujXu`hFE9Sr7uru6+?*ZZIAa^^YrbMO0+UguJI+PfIDGJzyEl6&58Wmcz< zQXeeMye8-+#vKV|gq2rd#8qg?8HNn{S!LO9pXmM+Rm5u>9cEA(z`PM_vgUK8xuK@1 z;jiF9HvaPDZz&;)HQ|t9mDeuiVIJhzVqmstS#mwJsEXzxzh$@}mjA9NNl$*$BCfOQ(2vrw}J7VrTzRTX3LH1&%G#RJKENL>dW^C6Mn&q(` zQ_6N@#PCy(!f02?msjnUP^qE7Vdr3plDjyym5pA0Su!oM+=aQc)Sp;jyDdT|dR9I% zGqT++!|3=R-~~{u(S5sA8I+g-&-?pkcFjyaCx-0PUM2yqzX;dYDcD=oyTfHe^A{#< zP9=V2#2vj~dQ9cooV>3|zx66sH|djrOJ=80F(Qhbh)>=Q$%bO8N6H^9&HP75mT=G? zsKkv2`~-?$?c4A%>`I1rUhy_6{ECo&%kVpJ%|Lj$cn4^D9D-vDlJm}Ub>c0c0NkxF znb0F*0k>kD0JrWSd^whJ$x3tiF(T;AV0CBjXbUn?&5560a9K!UaAVk{ZEfWiD($L% zXy1mqz~Rmx+^g2LEEp-Wx>>I;G`pmJF*>U;omA33fkI)4s$OIJfgmIHpy$)SjT)Zu zwpCetrIEb<;p|8PdVf^sAOxG(e>*Fzby+2~62X)vbmpTs_M>j9Tkx#;R#9_Z1VF)Q5cC>=; z8<}UG+E=j~voY9*`^Rg(*0pai#%n;9W^V3-4r*F{z9;o!dhp#4&0xuRXJB4+`cj2p z+Exy#+_>$HkcOoq)j!k>RS<#!pn`Ea7DH&eVt&Y$CGBunQCb+G&3U4aD|rMx9pkPav_q(9k1h)sOpJC?<^o+9;~Q*7E=IQe@tJtlxmAd5ZH73Af1U`Df9<+j#iMcru1=BhW8K_;!`(L^Jc1mC# zHcGpLtjiInB}W1+R&J+NQ~vR?I}1J0x&+tQWM%XNK{k)u^6Vn_jV@5uvZPUf=qT z@;cWrC}+82q}eO)wSAkYom0@c_$8?DjC-4?z)uwaI#_^&?vuCOSXkPtVm@x)9^0C(AxTcBo$(aoKkkJti7JQ$ zIv|&jtd*Q?+?0Fklfq=i`l&|4-4}xVSGw4g4!)ZO5j;OVEG%q$3nil5ZPe(ogYz)1 zD)ow^Lv09)PoIP=$&#OtsbT&ZArug<}NBSv2wPRK|vL=m?tSdJ_FotV(}juXsf{rFSnx2 ziC?wDiBVL~gUEaYF~c#n(53&1Py2rdoL1&CV|B+*y|u<^gcl8fsiuO$^S<5`>yf>Lv6;v0vD`gc@Jcmb;YSsBlZtyZ)1F zi@=}@gF!(mePutL(IO6m`hi0Nwrk(`P)yd=TOE>>@fATUb2Tejp7KKv+vhdIZV_*h z6ya8n+($Zj&0coF-+rz@?{RkvdlI@Slh( zs+FKuk=%3%YKc|ws`8yZYVyWd^9M*wYtH74jWnCLwKW?C+TChZR)>cNV!r(hv$udW z9|oZ#?y0Ap7YZl3QE^qtu1`SMg;G*Q(iLTz6|k&GAV%YBQMsdi ztd%tq+3*>^{En93*`u;PU2yzqUe;J0h_=eCS|~dJI|!gvhAF?%)z3fs^<1jjFqB?1 zcwEfoq+$_RG!PjM9GHKf`=WvzCn3CHVjfT21%Z zPZtZgNd8q!;&Z4!GfI+Ip$R7wx2{|pCF+WE@>0=WW!dj#K`VS+^)E#BH&Y(HAGoIQ ztEaoWTfZaa5j^GUHc{~H)A0_o-Nm;O?}8c7J@mKw(0O-X-be6BeY?}}%AQgrFcP065{k`*nW)<*IX_*^Z?Mu2|Wcp`bpV=7}^dcttGAFlFc3#shY z8Br>1LGAlh`wIXlyl^LR1dL9e9!?cpLP=+ljv|16B3K74eosk<1E+C~L;#$Ocy}U# zSr`B7)WNVJ^^^T>8iy^W9unFCaKx+_z+Qgi_Z<`5uZ%OI>;A{>T>!{ zciSDT;dbk4Un)2)M1L>FB6elXNl+6DuIqk0)K_e-eRqv9*Rgnx(WHMxfAI7WfUVNs zO~4<$zr3rfuqv{#P9Z9LR9FXIC1>%WzsIoT`ek{0HzI-ltdbqa<98IOzaYu`vt0)&?Kwc!IyvMuMNsG|> zfeT03-UuK&J?nWC-s#ahqg`Q5Iv3+)wNo(wSnUXqA|#*j@2`l^h5eck$?>QAj5_i` zs{X+;D`HEuoWu2~AF|oDz0d(O$*@0dwYJZ^wEI{L;>LH)S{(4LH*SpINX-055N`kS za{7kJVG8!iqe!er{>C!x`5vjpa@@2|aV}yvS;=E;ipGaFuhpO`>r2B*9FjIPyY`Ue zwG57h@zlNPpZP#52TMl#D=DYYAZt*h;QlqGZ`i0Rr3rrUOAZ7;54uM^Z=$JiZ*sO{ z7+Y(Bn{hoU(KYi84FBBkHv6(ntXKxAQd#9!bp!_G5W`r7n_fyPlQYP+`-BJMqn<2{7FNL?dV362-=KBuYI+5+q~T?to3&$dN(5%SCac%u znS7;vNXv|vSV+1tJ!HRVo2;&DA&JSu~hF@Z8Ne*p^Zg6Q=diAgW) zdNYoro4ScEpLw;`0v5ZkMH|+=ZU)7Mz4R3I@bcJilyGPwmy5*bQ#oOo7_lJ@4 zE=Y5Ie_SR!=}AzJNAIZ6E+9kkvi%vqPFOhkd&C&d{nF+5?8*r`T%Iyw1F0LUs;fz2 zWvLIiMS>O=yRL>_-5&R?+=g^-aeXms7ZIM8aKqQrf^hs{1ma1!_M{&&E##Pc`=@FI zeJM^^c6qS3gHAy0YmS)&(QIw-n2{rF*ei3acjc%KY&I{`SfUU{NUp55zYKv2?^Y%H zM_`%|vFNDJ?5)mr@M9eF)w!e!BECVvGVq+KEiua70GjzBzyfXv8+3`t{Q_#6Ep-?l zNMruodtzAYvMjA6CxQ1k34e7g_?mvNY-eje(m9{{W}PSr*V7YRn|Kz>T&fH_+W$*7 zr_X!HNIV8yBmw5&MsaOTz}1j|_nody0ehYfgU+?@lxg@|UmhE8Kuu*$`s)p;%NJmH zR5WMyC*i7IDFOtbHCZu}`16(7l(x63d^@44pX}(G``yyVE6clZh^oLq{axg!lx6Ti zmfh_r&tP_Ht1i^Yekay z!s(YGF%=wAh0^v}!odRNa^jlwsxNJIF=1CM#j4Xz%#&tK++LfKK*+beZc=z0_&TFj z8eUzwBD+$Lb>;Vc$$$T98mOss!i?jnXe89b*?QBht7%-Y42;O}fxZPvKPOmJVn%!K zE3;?}!_wyf48!|{e^K9_rti1nM;8&a-bupwpfRF^p{ltttf=P4*Cc1yn>x&9TeY&$ z6sK~_@?r7(6JR#1fj;ndH5>^9HUnBRobtu>2i>>R+quFR_{@jqtP4h;@KK3kWIIz+ zqiRKty@iuVI{kGI^13V9Vm$@EL6p9`HU|^3d@n$0tL;U@3w9 z)Rq`F@24jM$NoH95v*<69Vl`=c3!OoqV(T>KvI-zS8Cje#Dea|Ve;&+(66Mx)$dyy zL2z?Lm)#Qz@U$)uW_TQCqP8N(PQV7cZM#%xoFF7f2~O@D8{Ie`#t1%kE?S&b{Ikh^_dU1Z)|Kk25*^^%PLE|{OR&3-?75aHv=53GcxTSu2VOh zVhL4t*14IaE!M?h`+{5hEC9zBx48?bTduEn>M=B2rBgsZ>3tGSU*bl)JmolwhmKXDh& z4d1FWogh~s0~=hr*i&3i5KdmQoJ{U)fPx-*8QhuFbOx!<>&SkO{q#WEQFbo-|dWv36K=Z@AM|{VM@D;wqZ<@k&>w{gGsnNn4e2 zOCJx1@f;x=?+r_R0|7EmByol275Ie913!-%IP)H=>k95Ye6q zJ8?c2Als)_kcoL{TB@Pz_Y~2fACY@%Q!)uLftvLh>wU1{l$|frLwg&mdVd%u<0q)N zw1Ua(S76>!6DL%@G`B4WN2p;3f-j-ETer1m@D*drGo)G@v$U_&Z4H#kBBIbS3uoY5` zBq|U-GziBlcO5}u6(b4BVLa|8Z<1A*WwtMVZ=trDKnq#@Dbk+>X>NEcv^`c_S33N`- z#b;ed8n`%|v0mR}IHiYc89E7vf~6P_sC^Ci2yQTi$a7z-doK9hsL|YMnCWIbcOhn6 z+D8HzV*t_k|4TckH4eB}8a9ch7rEqKc;Bv>1R|TjWKMMgqG9j=%3= zA1M_lH`cDtSJqN4i3hX}Jz!P)s5U{<`LmJg3+fz|2U=(kfu-lCBZ4C_7paO6z*?%K+M;59p#HXxo~6fFaTB0=^6C-NPO=o{Q`l0)7tmQm#SNG46fIYgJX}GQ zfU~nJEyn9e+^mQ9S=j&yuFd;*u`dMtIT<+eQ;y#BCo36Us?m0e`330wU8*KpgCC7w z7T7$t|J0D;3}_$xA?4RYlS>c>pnCD1ht6N1+LXZkjGLVWBxjB6ZP^%n__z*nz3Gu?g6g46+O9N0&ZOQHsU(lZ;lZEJ zg%dH;tO0ly9mG=?J`rSk9_z}H7{CQHTWs0`47@oc*ixOR0MGuzm)Pl5Zuuwx!bwnd z^Nv3mLCntP<(mJF3uE!!yAxawWYU}t!!Q01SgrZWXWCGFvBvqW;&e^i%UKW}XHUlK zfUO=FKcpCAZ!N_k_0cBFYLx~_h7x41=w{v@?bEadA*ERd2C&Hy_a)SzcBMi);Ipiw_guDgG8sP@B_{+}(ZU7y5W+~h} zg5C(81Ts^%_C&fiC7%$3-Ajd)XEs^!_mNPiVLtQ+(+XtbfNRem8lPYlSEXQHKs6_g zO>PiVST$6wMF{y@5YVPXrzY0hSARQ$VIGaE(0m9l5}ne|+HeO3>VAtWYQEHg_IZ<-C(j=SxIKkrdW93Gf)sf+rAzGAJzajJ`4zQ`GW9g^4+cPJbATRXt*k*u zv$hueIgfqGHufSE|C#e*PW@x{pT}X?)_E(y{R92hf7g&~u-}8$z11PxosRKM@?*(# zn;@&p_7!rh`67-qyb{T?{;kM|X(bI-7H zB2i{B80lkIS-Rv1YjBC$8sBEU7I%8{x#uS;PCO;Zgz6$L>>I|h?PRgVjEF>Qk`8!t zh`rY4V`&OAGA^}<>-G4ZO5=T}uY3Vb&B7||Kq7&u8#ex@nSp0MvIl*4T*W^%Q*}-}796F6KdW2j^fr!#yBX-{ z7fT&THHz_`53NZ+$pRI@8W!6!cwjxll#07b zVN&ZK7!TB@w09VJB1XW*E0*%o)@EbmgUy9k&J#~PTDhOjf>SJiEQrWen*zc#;IGt* zZOZp@4-|7*0&W^`yslZl*ZqYOfFKQMSg;7bj1O&Njhq;h+cYqD=kD86aoe)Xw_T?X z+_=S}pqL0ZYaCE(Co*A88gSwtLv&YRz@ZV)(8r2n4;^;-#DaChL$sEHr)n+3m$b)2kJlqob$>wKmO5g6CNcwx=-tPksy8TZgUJvmDBYzA$SJG54 zBzc?#0OP&2!XNvgwHrCs05sQMm#DP{Jo=*N=!IZ7>)I~kuSqN&a3E$A*eU;Tb*;*S z|9EeV3qvb^!pwC*u_mO&f+KW*Gd9%}1z^?w9Q`4V+wqe!B7=e4)I zJU2U=lm9SQc_j|8f;=!B_vTJ+a$vu2)N5vLF=8#>&&x{qnrDIvi@h=_m4f_#j0c#+ znGY&T=liQ+wmgfQGuq%OwzwYsHJK2LIpU-Z1s{Vu9_d46K6^K{5O$~j#*DDWI4JZs5c@*@j{Zvjewb39|9K1t5&4z{M^X$mjCZzv0wbd z)7r^;CuG|6U$gW(@;F$aL#+hvr&AwTfAYtL%x4{I{t|^my(#A!>BQKBV;9MQO4wsC zUK>&^`WaukWm9BKkqQ0K3St7MdhO}ELod`=PDJG$_@qQ_N)FgNx)V9}jj~DJAX(UD zE*W#%iPx4hDQ8>s`jt_ZHL5E2l|ROMy3IC52;CaAZ4(py)YMA74eZ3UQ6 z7(87?0CRaHf;pJ*ZcUbji#h&rc_hZWWR3aj`r`-ZM4TBHKm0C+0OoAe;V%SGDWSr? zA-n%~Zb&u};$(LBdZzOHdX@K$XwEvhLokJT7|f&rILd$)UeC8az!EL3}I)FNDwX(TafYs&h+Ys$fyYW2$lY3jwQUw?$&%#^`50~d6X zu6G2!5yeR1L&N7b6;5@+E0K?~HJr?W;%wYuEl$hy6MFWngMYK`MfCH&j(k{jv zuNU>?`DKy*L|rNvSjv9j{egjvOc4!Ua5zvGe`N14{Ml?{+1z+$U{qUpK2C^NA2`C< zfo-P}lx4lGpqL}bu{LycE9g7Y@tYHu*S+W6tw!~ci@s^5R!_xbvQ)!;r^BET}<-s7De7skl{B;m)7hw9e~WGRx& zg<_HheD-^)H)r-WwuYc*gq$vHVB?;$`z+u4KSe(D*5$^}ZO1q7!KdbpKYQ)>eXos0 zU$vER%(|(=LSuOCx}A{7`1{3Zn&qE(bm9IttViHu^Ve;@o0kdDh8~cE=G(;lU&Ro* z3{7`o8BmmbqdYuGZ#WFw>_yLKcoau69zH$-U%2{ipIYDlurNv6&$$ctyG`ID4b>8g zdR0qOwo05Gj?&a;(cR~@B6&Zx^)l3;#eo$3@BLQ2|6TDsNy8cn_LVwz=L!)7ZtCB& z2b)&MY~~&jas)wqr6C0qzbJdq4obtSZ934S0i}ldLz~iZG!Vn0m-O#8ldsOG=Frk) zk&kLRWfU=%h^SI)>mL!+i7x0VYTsT7*sgW;PJ7^;oAuA-k8_D7fVj9uYoo{4#id(4KAFd44-l{tL-3oYbeS7I1d#EMFU7Mk4uoA58`tJ^?mdrp8s7&!i6_n`=HGC>$U5q=(4Jt>E+GboV^uD zyY=mq(EVIyooVyN0V{QR%Ch@vDF`Iqz4sCLoXAlN^*!{oUF{+syZ=$72xXbp&dJ>r ze$%R4g5hT7W0m6f<~u#%AQxx0oH6e-{l~A*ypq!^_F&NNI8sHKR{uRCuR`f6RP`T0 z-GkQke&}27lPuXyQ5f)`xj6o8Ut|+8gN*&{8SX9zi7DQ1D8#33P)%L%kA={tLT_Z% z(R_9UrtCWptDGbU2M&mqxyUT(g)_83X3&|j7*d8DGUHtR?|;XJXVF}K#PxFP2e%$L z^d8H*)H8W?s-Qm)4=S0pJyAfe3(f7=CL= zY&;3K$B;IdP zO^$_Dnk7>M-o0;;<2l7~bR_ZDm|+qjzjK%0$m?6;areBIn?n0+yfrsHI9bl0oOApFMl;Xeaiv_a{8Q zuCepy&YDXsFIV$!r9rBNhJ%f;t9va$G6 zV$s;Q6k9Mwv=V9N)``nQn0I|IK>C{4wRh%`(bL_aX%>edj!#Z+jqvQ1l59)C4$0;a zumWb#or&uO_`RjuA3VFgX^|YuBWnfd%)z<^raW|^0i-BYr*l`%}*E#2-|K^XC zv7vezl8r4&*6kmD>Q6b4X`B}H7EJq9p)$Y9)!gUe_?hvjr!LT7zie(gN4fU(Xu62a z70L60<{8p(=raN@Zv-Q~(IoN;Yi>N1H8+0L5OT{Rc)c5~eEN!rgNdjIVC3Oab4CmH zVI)I+SOO&Bosex4q3nOsPg{_@?}Ni{$1LqISrEuzs#}v|6;ZK7CJO|Wmd41K&qju! z`bgz4p7FZC955G>LDkI3_3PcHTgDt}l`sAcbG4xw)kAz! zKSVq1mIU_nhNz{}?nsA>!io{zbkZ@uDm(MJY5VC@GJLzCE+PDRx!l|nX=cj7mY1Uy zrf*?cE-l^Js!jvr4hKlXy2sly5?ZA5KUxz$2D-FFn&ou47AYhb-${H{uGdZMk0^AR zNaqql=HI%Rvc9XV9*if45xgG$LrTmWRYTB=i&VJM-THHBhnrUHnQ4)Z6sZC`PNl<$ z(1o|`YO+@+=ihOZ69gTk9*y~PuHuakjeFsbm2KT#zn30#6s}7m$Xl3_{8l2tCXWcn z-?!hW%TZE4pndd}=IhlTl6>e&YAQ%d{|QjTF~_*U5?sq+QA4twK0{;HTVes5w&L$K z^9bzkyb|PBYQnA{b**N5@c8}8WXoIEHH+L=e^K8x>crg>Y@u!p#b_a7DWMzpz8WFh z?oc($J_bxKd`cYI7e7qY zmm@GPlK7Kn=O1a_LKQu4ToN8Z>>w{5N%VHFe$rUYpzN)586#!b4sCpXET$L9PzndGNF_V}6IR@?rB>qwJaOST zJebhM#NLdDLIk&jv?OZwQvB8`;6(TtIWQWO?zkj z;2dggSPJ>^+e&TJl7u&%4?I9Q?8`QDb=mf?Gl9*sZDHsQdOvexsS1J1Dtb)OSa`<&-TLyIo-pm$j*KTEDkY|0NWRGy z)7)h3>nj0kd0h%N3CL^I-(13!9TYndvYUX}PItQY4-KXz>CbI9isdCjd@g(7tvtJ26CCq7ro6fqV;IBY*Vm+YhD( z`%I0X8Rw2wxv9hGMO1cAeBxP`?6n=Ssby{Vw69XQ`?5N7N?%5ye!t7F)O7y5?*6cH zY08AatNr;Z6so?*+qj1fYX}K+ktyziuJ{(YEveb)pOe8W(XPsO1)T-AykPse?Gq<_ z*QDMOnLr@;I6#WA_w_hm$AD_5w7}=6PI+(JEe{e2?C7Hd zsex9G*n%sQe)!IZ;~0Djy@c}*wFJR*BMPm^GnfC$EPRHh#ACUV)INKtV<52 zb#d~l-h|w=>O}|`cDm0$4E)Ly8m)>TLS9{-2}7vQs613CDr#b8O8G-SV@|Ax6M)g* zzd?WC`_r{_Lyt%H5!SKg3>*Kp7wVk2P@*Mr<~xpt$Qvqjl9x=`F0_?B1*>dAAlu&d zA*DzsKq=zrvXhGby?#3M#G+$;PLgXkuD5NNd@GgjO^gtvjpKx;4TVTlI7nq50mDPj za9a+P-V{}Qd4jP7yi^gqE+dDpnhmoj-XJOv*{8(pdop-6yNg%IURCQ|*cPLzTiJ;U zjZ2tJW0P$MZGrl>nl$Hom^WtN9-@&VEIZQe5TBrk*p#=OUYC z1tcO88O0OygXJ=$wx9oHa>?}?SpiS&q>4lBSOR~a;ZsOAdF6)1R`_uga#MEPTy#Mz zvr9^5)7d5aUj)R-#_)ZKXrqvNWl5&-o&KR zDwz(s7P;Du*#65l2A=yJoN`f>~x-NNjO6PHm zXpxFTfQphhUXTs;=-hheueR&zqz23mRw^SuBg)pfX{bijX*WS<{MSl{`Iiqh8wq*+ zk#89bjGr`{o`nfZ+Fc)2gEsy^As6Ws94_!~;(k=%9hN2sR@c~)9IN#8StN*Cvu3>b`pIE^lFB`F)O zI8EA1y_uz-V0(H<(>>g7z*E`5GJ@<|vt1)v@0T{F-7*1LLpfQNj1a;MYub1Jetrd} zhrb2>;uqLtL|!39f#BSb7=>CsF|qV3S)rO_gBRQe^Zv!ri80UJvQW<0sv~tHl1z(6 zG9=!kTu>ehZ5PvsZ`bQ zE`+`kUO9?}8~x-$3G+15!eRI-$kCT*9z=2|x^AM_Kb4(K07 z_@_@l?DkRODUz2f0iA~3IxC$URONf$uBU293pz}P5&<_CfG?D4Jixef@H>LPB7FQY z=PJDOZ4|#s<|st4qlcDwHP`f6U?n16U>+BxsG}S^qD9E`AVv+EnWT}GDv`ugL{hg! zEFt4>Xy5g5f#fbp2M*wD4C9j(jvsGHs=XV9)AdIs)|X%X@`6Z`5w|fexddN=?~cn& z*r*y&gTK^92(%bql#;h;(6t(@a5fOU6{QeuZqgHXxr_EFl%e%(Q~0M2l;y)Wg9uja z=mfHgXOM9WnS-pge)T;<8&PUdYE>$egB@lD(QDBC>s32l&e)tWN9>k9p584qd#-eCeM+Ix0FDqxTfsVypMLj~&*uxsKP0)VX|pTUwan3t z=!}t5@kDb$p_u1`ng@3Np_=Ocn3$dO9!|Q#VpkzseZ6Y>WwbwR$@(=AUo@nI;j zH*PEJPb8i4j>#V#%3@ja>(T6V?;28|OFPvVLa?LpWW0M%`C?I9NpIalgwJ{u>7HBK zGe#&g`y<}xM|WY9*4=*X-Oksrh5d5-p^kS?hB|)n@1)nyT09W>q4ZsleTrdR?>O97 z`&z~>hQ(ACzJeHdol|uj_gik+MvlWc(WW@1@297hRa-T%Y&*cm>`!bOcQwr0a4bd8 zga>*`*19=_YGIkSX~q&98=UCNsD$Vg&{z7q-|hpuo7=Znr{C+vyj+%wwQJr}^H^#t z_cPBKS&FB8)2mQoxoU$Qttvt;v{f_lGwumy828eloIWkve~qT4{R7b(oW3Dj+os@n zHATzDG>7rd17A0R!VIMPWpH`Z`Qg~N|JVl>t4kpex5KWV5tRwjW3;rdNTNMM^bzX) z;yPT}Bsa_SWF~NiEDvfi9OFC4U!Pyl5)b1|*z%KYvP+NF_*GYEV(DQh=KUpZyTZ`S zcLs0DyBzpY4%5$x8@poTh}|zI+Kt-*Jj=iL{5P|IK&mFHv_()dj{|)#Og&jasEnlT z?bX;|!ty-c9ioT^63p99&SgJw8<5-YpzIlhZRhNzkxM=B-Eu9sS0Mw{=A3gsx+bxe z%)!TEFJnZmJJmSUgmDY2nQqnJrH^YofG2Xo$B$3;K_Bm&+n?-n5cJ4j(rVHWa=Sh{7vFz_ z2MQ(Py|H||H`QTk=YrH+EjSIgA23ZF0a0;r^}`L~;Ogf!KYeD~pjgQ2AW^sGak^_k z$Kjs)TMud{(LjLUOF+mrbqm9jeTSlCBR~0j;=HagaZl`q_^7j^ZL3L=`~RxH6QVl_=K0e%P7UN)!5@&L zR0faXD1p7WU!JugUnjkJM38H?G0d?{v5l(d4_v*fw@9Hri*X-r0T@&96r?*$anU7| zA+IPEi+v8$(6Y~MkT!4g!FNLH{_wjnI1-qK^=wN6t>b!DC&qk8h-jetN_yivVh*YLm@Wjj zigYl8u+v&v>tn=wI;)q*2$N&EUSiL;J#!=;#0%qdIM7Ht6c<$YmGl|&e6)4Nsr_JC zR1uV?gqA`p`U(1}pl0lrJtvL*ZHj{h%+u_BUb;q}!k>a2Kh;y7U)NpBV$)uzpTmU_m@kGO0a5t=8Lgqch2SOMv z*XQd28|o-30ARI?YvT3Z#hc4})+Rbk*gk^zDUt@_2f#IHCx-km)@w}s`djB%CuVhk z$8Hy+G8LI0Zm??`7=s}C?c1&R1=hewk(&G8$4%zGj4GvBGR_H+X?L%Og<_C{efHdA zO{e}E^NkIgiU^{w$)!V5;y}qyU{_U7JS7$6q@(IG)(#;TDteEmJjW0vC17JDRE_%{dkSX&bNHl zdCW~VetqpO^T(tgZhd@tY#|ZQVe#=8F*7$gH)lBJCh;!OCic3gx#anB$di(owjoZP zRy~#7`J8A1x5Pt>AeY?RnzUXX9aNvT2Vh0-;DLr6c% zcKlfZdaa_vaK;lMLNoowQ}WYZkKD9I<4h4I_{ii&mo0Xc8HhGSB*;Z-rKn4l2$yF1 z5i!>RGxyA<9tbUKnGHyiiR+5gmyaiE~T0I?5pRspT=KN3m*Sbsjxm ze(cw+uB_PMm=C}PmPi0s53w7@3D5U_Z615N-#;2!{Go8y>QY{_bGb0^s12%`%dd&} zqijl|qe@A481elf4YZB_7>eGT;fBk|A`))yth&n+e_aNisUTkWY;s>WJ;YJwG-FdZ z;7LRjAzmP&7(uCOPV3@K{B8h7d$woFi^-3Z-_Nh=Wr6?4kQYNRvAvLJ6iM(Nkl*P-Grse5Q3;Ik{tcfwOD~nyWbfby!GcjgFRHPOg4PAmPK&14i4lg}a z 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 Sr4KBEAstPFyJfc@J^w$60&}$h literal 0 HcmV?d00001 diff --git a/Assets/logo.svg b/Assets/logo.svg new file mode 100644 index 0000000..b6b3939 --- /dev/null +++ b/Assets/logo.svg @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Package.swift b/Package.swift index 76fe51a..c3677a5 100644 --- a/Package.swift +++ b/Package.swift @@ -6,23 +6,23 @@ import PackageDescription let package = Package( - name: "Base32Crockford", + name: "ThirtyTo", products: [ .library( - name: "Base32Crockford", - targets: ["Base32Crockford"] + name: "ThirtyTo", + targets: ["ThirtyTo"] ) ], dependencies: [ ], targets: [ .target( - name: "Base32Crockford", + name: "ThirtyTo", dependencies: [] ), .testTarget( - name: "Base32CrockfordTests", - dependencies: ["Base32Crockford"] + name: "ThirtyToTests", + dependencies: ["ThirtyTo"] ) ] ) diff --git a/README.md b/README.md index b80f1fe..6a920fb 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,289 @@ -# Base32Crockford +

+ ThirtyTo +

+

ThirtyTo

+ +Swift Package for using Base32Crockford Encoding for Data and Identifiers. [![SwiftPM](https://img.shields.io/badge/SPM-Linux%20%7C%20iOS%20%7C%20macOS%20%7C%20watchOS%20%7C%20tvOS-success?logo=swift)](https://swift.org) [![Twitter](https://img.shields.io/badge/twitter-@brightdigit-blue.svg?style=flat)](http://twitter.com/brightdigit) -![GitHub](https://img.shields.io/github/license/brightdigit/Base32Crockford) -![GitHub issues](https://img.shields.io/github/issues/brightdigit/Base32Crockford) -[![Beerpay](https://img.shields.io/beerpay/brightdigit/Base32Crockford.svg?maxAge=2592000)](https://beerpay.io/brightdigit/Base32Crockford) -[![Analytics](https://ga-beacon.appspot.com/UA-33667276-5/brightdigit/Base32Crockford?flat&useReferer)](https://github.com/igrigorik/ga-beacon) - -[![macOS](https://github.com/brightdigit/Base32Crockford/workflows/macOS/badge.svg)](https://github.com/brightdigit/Base32Crockford/actions?query=workflow%3AmacOS) -[![ubuntu](https://github.com/brightdigit/Base32Crockford/workflows/ubuntu/badge.svg)](https://github.com/brightdigit/Base32Crockford/actions?query=workflow%3Aubuntu) -[![arm](https://github.com/brightdigit/Base32Crockford/workflows/arm/badge.svg)](https://github.com/brightdigit/Base32Crockford/actions?query=workflow%3Aarm) -[![Travis (.com)](https://img.shields.io/travis/com/brightdigit/Base32Crockford?logo=travis)](https://travis-ci.com/brightdigit/Base32Crockford) -[![CircleCI](https://img.shields.io/circleci/build/github/brightdigit/Base32Crockford?label=xenial&logo=circleci&token=138d740bf2a55bea45604d67de3b4343ec074f9)](https://circleci.com/gh/brightdigit/Base32Crockford) -[![Bitrise](https://img.shields.io/bitrise/2a58136e0ba95d50?label=macOS&logo=bitrise&token=hulZMBnfXwp0kKvyVcq8Ig)](https://app.bitrise.io/app/2a58136e0ba95d50) - -[![Codecov](https://img.shields.io/codecov/c/github/brightdigit/Base32Crockford)](https://codecov.io/gh/brightdigit/Base32Crockford) -[![CodeFactor Grade](https://img.shields.io/codefactor/grade/github/brightdigit/Base32Crockford)](https://www.codefactor.io/repository/github/brightdigit/base32crockford) -[![codebeat badge](https://codebeat.co/badges/4f86fb90-f8de-40c5-ab63-e6069cde5002)](https://codebeat.co/projects/github-com-brightdigit-base32crockford-master) -[![Code Climate maintainability](https://img.shields.io/codeclimate/maintainability/brightdigit/Base32Crockford)](https://codeclimate.com/github/brightdigit/Base32Crockford) -[![Code Climate technical debt](https://img.shields.io/codeclimate/tech-debt/brightdigit/Base32Crockford?label=debt)](https://codeclimate.com/github/brightdigit/Base32Crockford) -[![Code Climate issues](https://img.shields.io/codeclimate/issues/brightdigit/Base32Crockford)](https://codeclimate.com/github/brightdigit/Base32Crockford) - -[![Version](https://img.shields.io/cocoapods/v/Base32Crockford.svg?style=flat)](https://cocoapods.org/pods/Base32Crockford) -[![License](https://img.shields.io/cocoapods/l/Base32Crockford.svg?style=flat)](https://cocoapods.org/pods/Base32Crockford) -[![Platform](https://img.shields.io/cocoapods/p/Base32Crockford.svg?style=flat)](https://cocoapods.org/pods/Base32Crockford) -[![Cocoapods doc percentage](https://img.shields.io/cocoapods/metrics/doc-percent/Base32Crockford)](https://cocoapods.org/pods/Base32Crockford) -[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) +![GitHub](https://img.shields.io/github/license/brightdigit/ThirtyTo) +![GitHub issues](https://img.shields.io/github/issues/brightdigit/ThirtyTo) +![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/brightdigit/ThirtyTo/ThirtyTo.yml?label=actions&logo=github&?branch=main) + +[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fbrightdigit%2FThirtyTo%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/brightdigit/ThirtyTo) +[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fbrightdigit%2FThirtyTo%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/brightdigit/ThirtyTo) + + +[![Codecov](https://img.shields.io/codecov/c/github/brightdigit/ThirtyTo)](https://codecov.io/gh/brightdigit/ThirtyTo) +[![CodeFactor Grade](https://img.shields.io/codefactor/grade/github/brightdigit/ThirtyTo)](https://www.codefactor.io/repository/github/brightdigit/ThirtyTo) +[![codebeat badge](https://codebeat.co/badges/54695d4b-98c8-4f0f-855e-215500163094)](https://codebeat.co/projects/github-com-brightdigit-ThirtyTo-main) +[![Code Climate maintainability](https://img.shields.io/codeclimate/maintainability/brightdigit/ThirtyTo)](https://codeclimate.com/github/brightdigit/ThirtyTo) +[![Code Climate technical debt](https://img.shields.io/codeclimate/tech-debt/brightdigit/ThirtyTo?label=debt)](https://codeclimate.com/github/brightdigit/ThirtyTo) +[![Code Climate issues](https://img.shields.io/codeclimate/issues/brightdigit/ThirtyTo)](https://codeclimate.com/github/brightdigit/ThirtyTo) [![Reviewed by Hound](https://img.shields.io/badge/Reviewed_by-Hound-8E64B0.svg)](https://houndci.com) -[What is Base32Crockford?](https://www.crockford.com/base32.html) +# Table of Contents + +* [Introduction](#introduction) + * [Requirements](#requirements) + * [Installation](#installation) + * [Why use Base32Crockford](#why-use-base32crockford) +* [Usage](#usage) + * [Encoding and Decoding Data](#encoding-and-decoding-data) + * [How Checksum Works](#how-checksum-works) + * [Using Group Separators](#using-group-separators) + * [Creating an Identifier](#creating-an-identifier) + * [UUID](#uuid) + * [What is ULID?](#what-is-ulid) +* [References](#references) +* [License](#license) + + +# Introduction + +**ThirtyTo** provides a way to encode data and create identifiers which is both efficient and human-readable. While Base64 is more efficient it is not very human-readable with both both upper case and lower case letters as well as punctuation. + +## Requirements + +**Apple Platforms** + +- Xcode 13.3 or later +- Swift 5.5.2 or later +- iOS 14 / watchOS 6 / tvOS 14 / macOS 12 or later deployment targets + +**Linux** + +- Ubuntu 18.04 or later +- Swift 5.5.2 or later + +## Installation + +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. In dictum non consectetur a erat nam at lectus urna. Maecenas accumsan lacus vel facilisis volutpat est velit. + +## Why use Base32Crockford + +Base32Crockford offers the most reasonable compromise when it comes to encoding data. Being a super set of Base16, it uses all ten digits and 22 of the 26 Latin upper case characters. + +| Symbol Value | Decode Symbol | Encode Symbol | +|:------------: |:-------------: |:-------------: | +| 0 | 0 O o | 0 | +| 1 | 1 I i L l | 1 | +| 2 | 2 | 2 | +| 3 | 3 | 3 | +| 4 | 4 | 4 | +| 5 | 5 | 5 | +| 6 | 6 | 6 | +| 7 | 7 | 7 | +| 8 | 8 | 8 | +| 9 | 9 | 9 | +| 10 | A a | A | +| 11 | B b | B | +| 12 | C c | C | +| 13 | D d | D | +| 14 | E e | E | +| 15 | F f | F | +| 16 | G g | G | +| 17 | H h | H | +| 18 | J j | J | +| 19 | K k | K | +| 20 | M m | M | +| 21 | N n | N | +| 22 | P p | P | +| 23 | Q q | Q | +| 24 | R r | R | +| 25 | S s | S | +| 26 | T t | T | +| 27 | V v | V | +| 28 | W w | W | +| 29 | X x | X | +| 30 | Y y | Y | +| 31 | Z z | Z | + +# Usage + +**ThirtyTo** enables the encoding and decoding data in _Base32Crockford_ as well as creation of unique identifiers. There are a variety of options available for encoding and decoding. + +## Encoding and Decoding Data + +All encoding and decoding is done through the `Base32CrockfordEncoding.encoding` object. This provides an interface into encoding and decoding data as well standardizing. + +To encode any data call: + +```swift +public func encode( + data: Data, + options: Base32CrockfordEncodingOptions = .none + ) -> String +``` + +Therefore to encode a `Data` object, simply call via: + +```swift +let data : Data // 0x00b003786a8d4aa28411f4e268c43629 +let base32String = Base32CrockfordEncoding.encoding.encode(data: data) +print(base32String) // P01QGTMD9AH884FMW9MC8DH9 +``` + +If you wish to decode the string you can call `Base32CrockfordEncoding.decode`: + +```swift +let data = try Base32CrockfordEncoding.encoding.decode( + base32Encoded: "P01QGTMD9AH884FMW9MC8DH9" +) // 0x00b003786a8d4aa28411f4e268c43629 +``` + +The `Base32CrockfordEncoding.encode` object provides the ability to specify options on formatting and a checksum. + +### How Checksum Works + +You can optionally provide a checksum character at the end which allows for detecting transmission and entry errors early and inexpensively. + +According to the specification: + +> The check symbol encodes the number modulo 37, 37 being the least prime number greater than 32. We introduce 5 additional symbols that are used only for encoding or decoding the check symbol. + +The additional 5 symbols are: + +| Symbol Value | Decode Symbol | Encode Symbol | +|---- |----- |--- | +| 32 | * | * | +| 33 | ~ | ~ | +| 34 | $ | $ | +| 35 | = | = | +| 36 | U u | U | + +If you wish to include the checksum, pass true for the `withChecksum` property on the `Base32CrockfordEncodingOptions` object: + +```swift +let data : Data // 0xb63d798c4329401684d1d41d3becc95c +let base32String = Base32CrockfordEncoding.encoding.encode( + data: data, + options: .init(withChecksum: true) +) +print(base32String) // 5P7NWRRGS980B89MEM3MXYSJAW5 +``` + +When decoding a string wtih a checksum, you must specify true for the `withChecksum` property on `Base32CrockfordDecodingOptions`: + +```swift +let data = try Base32CrockfordEncoding.encoding.decode( + base32Encoded: "5P7NWRRGS980B89MEM3MXYSJAW5"a, + options: .init(withChecksum: true) +) // 0xb63d798c4329401684d1d41d3becc95c +``` + +Besides adding a checksum, `Base32CrockfordEncodingOptions` also provides the ability to add a grouping separator. + +### Using Group Separators + +According to the Base32Crockford specification: + +> Hyphens (-) can be inserted into symbol strings. This can partition a string into manageable pieces, improving readability by helping to prevent confusion. Hyphens are ignored during decoding. + +To insert hyphens to the encoded string, provide the `GroupingOptions` object to `Base32CrockfordEncodingOptions`: + +```swift +let data : Data // 00c9c37484b85a42e8b3e7fbf806f2661b +let base32StringGroupedBy3 = Base32CrockfordEncoding.encoding.encode( + data: data, + options: .init(groupingBy: .init(maxLength: 3)) +) +let base32StringGroupedBy9 = Base32CrockfordEncoding.encoding.encode( + data: data, + options: .init(groupingBy: .init(maxLength: 9)) +) +print(base32StringGroupedBy3) // 69R-DT8-9E2-T8B-MB7-SZV-Z03-F4S-GV2 +print(base32StringGroupedBy9) // 69RDT89E2-T8BMB7SZV-Z03F4SGV2 +``` + +When decoding, hyphens are ignored: + +```swift +let dataNoHyphens = try Base32CrockfordEncoding.encoding.decode( + base32Encoded: "69RDT89E2T8BMB7SZVZ03F4SGV2" +) // 00c9c37484b85a42e8b3e7fbf806f2661b + +let dataGroupedBy3 = try Base32CrockfordEncoding.encoding.decode( + base32Encoded: "69R-DT8-9E2-T8B-MB7-SZV-Z03-F4S-GV2" +) // 00c9c37484b85a42e8b3e7fbf806f2661b + +let dataGroupedBy9 = try Base32CrockfordEncoding.encoding.decode( + base32Encoded: "69RDT89E2-T8BMB7SZV-Z03F4SGV2" +) // 00c9c37484b85a42e8b3e7fbf806f2661b + +assert(dataNoHyphens == dataGroupedBy3) // true +assert(dataNoHyphens == dataGroupedBy9) // true +assert(dataGroupedBy3 == dataGroupedBy9) // true +``` + +## Creating an Identifier + +Lorem markdownum duas, qui data superare trisulcis rex haec unius! Rupe quo aut, +cum per, pius attactu. Repperit canenda deiectuque coepit vertitur violentus +quoque! Siccoque corpus. Illa intima Bacchum nativum. + +Verque aves ab verba. Hoc auris sed formosissimus malorum virum: cum locoque +genuit, lumina velamina, huc. Materiam cetera, forte, deus tibi hiberna vates +revocamina. Tenebat validisne quod post longe parvis, sic superari! + +- Atque et volvitur corpora +- Est ab protinus cornua renuente medii dum +- Modo suo convertit temporis Lapithas numenque coronat + +### UUID + +Lorem markdownum adire sui erit suis, esse. Iuvenem merentem negare ingentia et +vitta, Oeagrius sic turpe colonos opertos quaerit aquas ira parsque parenti +pericula. Vestra omni amans illius tactuque de ille tuo ipso excipit meque +quoque hosti abstulit; aurum [nato corpora +velare](http://retexitur-notata.org/spretoret). Partem cincta. + + var oop_rj_rate = prompt(nui_web); + website -= app.modifier(leopardWebmail.subnet_jpeg_native(print_mbr_boot, + lun_oop), address_printer_boot); + var superscalar = classFirewireHard; + ipad_browser = widgetSecondary.standby_xp_sku(rosetta_igp + 79, 1, + key_soap_network) - fullBittorrentMail; + +Gratissime iunxit, neque *praebere*, cum et nec axes, vara otia. Nantemque est; +iterum quid mortemque dominae non baculum tincto. Fuit voce; **ab** cingentibus +feraxque summaque nomen suo, spemque minor: quae Ceyx omnis tinctam. + +### What is ULID? + +Lorem markdownum tenebat. Quo et quis expellitur potes tenuitque impetus est +Achilles, et gelidas, acutae. Enim non ceu fluentia Actaeon Numidasque turbae +expugnare flebile pedes, vultus, danda. Thetis in medio est cornu comitante +fugio requievit corpora miseri primisque primo. + + if (bridge_keystroke_architecture.white(riscRipcording, jfs, + optical_operation_soft) >= softwarePad(in_model, dynamic)) { + ram(flatbed); + isa(vpi.heat_disk_permalink(socket_nic_optical), 3); + } else { + hard = bar_ddr_modem + 4 + cybersquatter; + } + pack(nodeCgi); + if (fileSoftwareInput(storageRawUp + restoreSyncNull, dot_secondary) != + click_windows_text) { + flash(engineDocumentWins, cpaBounce.web.errorUat(34, 4)); + bps_graphics_syntax.dpi_truncate_panel(netbios_dv(-4, hostHttps, + surgeRemote)); + ups_flowchart_plug.copy = seoTrashMatrix(unit_cache, 130102, 5); + } + matrixFile += tunneling_dram_graphic; + var alertParse = -2; + +Amnis per aede munus, colorem *semper*, non manu vera petita tamen. Lanigeris +alium victo, novantur faciem Thetidis **raptore prodere flumine** sanguisque ad +*claudit* cupidine, ut vitiorum coniungere quoque campo. + +# References + +* Crockford32 +* ULID + +# License -[Documentation Here](/docs/README.md) +This code is distributed under the MIT license. See the [LICENSE](https://github.com/brightdigit/ThirtyTo/LICENSE) file for more info. diff --git a/Sources/Base32Crockford/Base32CrockfordDecodingOptions.swift b/Sources/Base32Crockford/Base32CrockfordDecodingOptions.swift deleted file mode 100644 index c48c2f2..0000000 --- a/Sources/Base32Crockford/Base32CrockfordDecodingOptions.swift +++ /dev/null @@ -1,6 +0,0 @@ -import Foundation - -public struct Base32CrockfordDecodingOptions { - public static let none = Base32CrockfordDecodingOptions(withChecksum: false) - public let withChecksum: Bool -} diff --git a/Sources/Base32Crockford/Base32CrockfordEncodingOptions.swift b/Sources/Base32Crockford/Base32CrockfordEncodingOptions.swift deleted file mode 100644 index f816d98..0000000 --- a/Sources/Base32Crockford/Base32CrockfordEncodingOptions.swift +++ /dev/null @@ -1,26 +0,0 @@ -import Foundation - -public struct Base32CrockfordEncodingOptions { - public struct GroupingOptions { - public init(maxLength: Int, separator: String = "-") { - self.maxLength = maxLength - self.separator = separator - } - - public let maxLength: Int - public let separator: String - } - - public static let none = Base32CrockfordEncodingOptions(withChecksum: false) - - public let withChecksum: Bool - public let groupingBy: GroupingOptions? - - internal init( - withChecksum: Bool = false, - groupingBy: Base32CrockfordEncodingOptions.GroupingOptions? = nil - ) { - self.withChecksum = withChecksum - self.groupingBy = groupingBy - } -} diff --git a/Sources/Base32Crockford/UUID.swift b/Sources/Base32Crockford/UUID.swift deleted file mode 100644 index ccf85f1..0000000 --- a/Sources/Base32Crockford/UUID.swift +++ /dev/null @@ -1,11 +0,0 @@ -import Foundation - -extension UUID { - public init(data: Data) { - var bytes = [UInt8](repeating: 0, count: data.count) - _ = bytes.withUnsafeMutableBufferPointer { - data.copyBytes(to: $0) - } - self = NSUUID(uuidBytes: bytes) as UUID - } -} diff --git a/Sources/Base32Crockford/Array.swift b/Sources/ThirtyTo/Array.swift similarity index 67% rename from Sources/Base32Crockford/Array.swift rename to Sources/ThirtyTo/Array.swift index ceeddb4..b6c58ae 100644 --- a/Sources/Base32Crockford/Array.swift +++ b/Sources/ThirtyTo/Array.swift @@ -1,6 +1,8 @@ import Foundation extension Array where Element == UInt8 { + /// Create an array of bytes based on the UUID. + /// - Parameter uuid: UUID to convert to bytes. public init(uuid: UUID) { // swiftlint:disable:next force_cast self = Mirror(reflecting: uuid.uuid).children.map { $0.value as! UInt8 } diff --git a/Sources/Base32Crockford/Base32CrockfordDecodingError.swift b/Sources/ThirtyTo/Base32CrockfordDecodingError.swift similarity index 56% rename from Sources/Base32Crockford/Base32CrockfordDecodingError.swift rename to Sources/ThirtyTo/Base32CrockfordDecodingError.swift index b6f6232..2758fc3 100644 --- a/Sources/Base32Crockford/Base32CrockfordDecodingError.swift +++ b/Sources/ThirtyTo/Base32CrockfordDecodingError.swift @@ -1,12 +1,26 @@ import Foundation +/// An error which occured in decoding a Base32Crockford String. public struct Base32CrockfordDecodingError: Error { + /// Type of error which occured public enum Details { + /// Mismatch checksum. + /// - Parameter checksum: Expected checksum value. + /// - Parameter mismatchValue: Actual checksum value. case checksum(Int, mismatchValue: Int?) + // swiftlint:disable line_length + /// Invalid character. + /// - Parameter invalidCharacter: The character which is invalid in a Base32Crockford string. + /// + /// For a list of valid symbols go to ``Base32CrockfordEncoding/CharacterSets/symbols``. case invalidCharacter(Character) + // swiftlint:enable line_length } + /// The source string which triggered the error public let source: String + + /// The details of the type of error. public let details: Details private init(source: String, details: Base32CrockfordDecodingError.Details) { diff --git a/Sources/ThirtyTo/Base32CrockfordDecodingOptions.swift b/Sources/ThirtyTo/Base32CrockfordDecodingOptions.swift new file mode 100644 index 0000000..4e996e0 --- /dev/null +++ b/Sources/ThirtyTo/Base32CrockfordDecodingOptions.swift @@ -0,0 +1,16 @@ +import Foundation + +/// Options for decoding a Base32Crockford String. +public struct Base32CrockfordDecodingOptions { + /// Default options. + public static let none = Base32CrockfordDecodingOptions(withChecksum: false) + + /// Whether to include a checksum character + public let withChecksum: Bool + + /// Options for decoding a Base32Crockford String. + /// - Parameter withChecksum: Whether to include a checksum character. + public init(withChecksum: Bool = false) { + self.withChecksum = withChecksum + } +} diff --git a/Sources/ThirtyTo/Base32CrockfordEncoding+CharacterSets.swift b/Sources/ThirtyTo/Base32CrockfordEncoding+CharacterSets.swift new file mode 100644 index 0000000..92efade --- /dev/null +++ b/Sources/ThirtyTo/Base32CrockfordEncoding+CharacterSets.swift @@ -0,0 +1,13 @@ +import Foundation + +extension Base32CrockfordEncoding { + /// Character Sets used by `Base32CrockfordEncoding` + public enum CharacterSets { + /// Characters used by Base32Crockford. + public static let symbols = "0123456789ABCDEFGHJKMNPQRSTVWXYZ" + private static let additionalCheckSymbols = "*~$=U" + + /// All characters available as a checksum character by Base32Crockford. + public static let checkSymbols = symbols + additionalCheckSymbols + } +} diff --git a/Sources/Base32Crockford/Base32CrockfordEncoding.swift b/Sources/ThirtyTo/Base32CrockfordEncoding.swift similarity index 64% rename from Sources/Base32Crockford/Base32CrockfordEncoding.swift rename to Sources/ThirtyTo/Base32CrockfordEncoding.swift index 55a0407..0738094 100644 --- a/Sources/Base32Crockford/Base32CrockfordEncoding.swift +++ b/Sources/ThirtyTo/Base32CrockfordEncoding.swift @@ -1,18 +1,17 @@ import Foundation +/// Encoder and Decoder for Base32Crockford. public struct Base32CrockfordEncoding { + /// Shared encoding object. public static let encoding = Base32CrockfordEncoding() - private static let characters = "0123456789ABCDEFGHJKMNPQRSTVWXYZ" - private static let checkSymbols = "*~$=U" - public static let allChecksumSymbols = characters + checkSymbols private func validate( _ result: Data, from standardized: String, withChecksum checksum: Character ) throws { - let expected = Self.allChecksumSymbols.firstOffsetOf(character: checksum) - let actual = result.remainderBy(Self.allChecksumSymbols.count) + let expected = Self.CharacterSets.checkSymbols.firstOffsetOf(character: checksum) + let actual = result.remainderBy(Self.CharacterSets.checkSymbols.count) guard expected == actual else { throw Base32CrockfordDecodingError.checksumError( @@ -30,7 +29,7 @@ public struct Base32CrockfordEncoding { let (valueString, checksum) = standardized.split(withChecksum: options.withChecksum) let values = try valueString.offsets( - basedOnCharacterMap: Self.characters, + basedOnCharacterMap: Self.CharacterSets.symbols, onInvalidCharacter: Base32CrockfordDecodingError.invalidCharacter(_:from:) ) @@ -52,18 +51,23 @@ public struct Base32CrockfordEncoding { return result } + /// Encode the data to a Base32Crockford string. + /// - Parameters: + /// - data: The Data to encode + /// - options: Encoding options. + /// - Returns: Base32Crockford String. public func encode( data: Data, options: Base32CrockfordEncodingOptions = .none ) -> String { var binary = Binary(data: data, sectionSize: 5) - var encodedString = binary.string(basedOnCharacterMap: Self.characters) + var encodedString = binary.string(basedOnCharacterMap: Self.CharacterSets.symbols) if options.withChecksum { encodedString.append( - Self.allChecksumSymbols.characterAtOffset( + Self.CharacterSets.checkSymbols.characterAtOffset( data.remainderBy( - Self.allChecksumSymbols.count + Self.CharacterSets.checkSymbols.count ) ) ) @@ -78,9 +82,18 @@ public struct Base32CrockfordEncoding { return encodedString .split(by: groupingOptions.maxLength) - .joined(separator: groupingOptions.separator) + .joined( + separator: Base32CrockfordEncodingOptions.GroupingOptions.separator + ) } + /// Decode a Base32Crockford String. + /// - Parameters: + /// - string: The Base32Crockford String. + /// - options: Options for decoding a Base32Crockford String. + /// - Throws: `Base32CrockfordDecodingError` + /// If there's an invalid character or checksum value. + /// - Returns: Decoded Data public func decode( base32Encoded string: String, options: Base32CrockfordDecodingOptions = .none @@ -89,6 +102,15 @@ public struct Base32CrockfordEncoding { return try decode(standardizedString: standardized, options: options) } + /// Standardizes a Base32Crockford encoded string. + /// + /// `standardize` does this by: + /// - Converting `O`, `I`, `L` to their respective digits (`0`, `1`, `1`) + /// - Removing all group separating hyphens (`-`) + /// - Convert all alphabet characters to uppercase. + /// + /// - Parameter string: The string to standardize. + /// - Returns: The standardized string. public func standardize(string: String) -> String { string .uppercased() diff --git a/Sources/ThirtyTo/Base32CrockfordEncodingOptions.swift b/Sources/ThirtyTo/Base32CrockfordEncodingOptions.swift new file mode 100644 index 0000000..d160c25 --- /dev/null +++ b/Sources/ThirtyTo/Base32CrockfordEncodingOptions.swift @@ -0,0 +1,40 @@ +import Foundation + +/// Options on encoding `Data` as String in Base32Crockford. +public struct Base32CrockfordEncodingOptions { + /// Options for grouping characters based on a group maximum length. + public struct GroupingOptions { + /// Set the maximum length for a Base32Crockford group within a `String` + /// - Parameter maxLength: The maximum length for a group of charcters + public init(maxLength: Int) { + self.maxLength = maxLength + } + + /// The maximum length for a group of charcters separated by hyphens. + public let maxLength: Int + + /// Hyphen separator + public static let separator: String = "-" + } + + /// Default encoding options. + public static let none = Base32CrockfordEncodingOptions(withChecksum: false) + + /// Whether to include a checksum character. + public let withChecksum: Bool + + /// Whether to separate groups of characters by a hyphen. + public let groupingBy: GroupingOptions? + + /// Set checksum and grouping options for encoding. + /// - Parameters: + /// - withChecksum: Include a checksum character. + /// - groupingBy: Separate groups of characters by a hyphen. + public init( + withChecksum: Bool = false, + groupingBy: Base32CrockfordEncodingOptions.GroupingOptions? = nil + ) { + self.withChecksum = withChecksum + self.groupingBy = groupingBy + } +} diff --git a/Sources/Base32Crockford/Binary.swift b/Sources/ThirtyTo/Binary.swift similarity index 69% rename from Sources/Base32Crockford/Binary.swift rename to Sources/ThirtyTo/Binary.swift index 6136aec..7c35733 100644 --- a/Sources/Base32Crockford/Binary.swift +++ b/Sources/ThirtyTo/Binary.swift @@ -1,12 +1,12 @@ import Foundation -public struct Binary { - public let sectionSize: Int - public let bytes: [UInt8] - public var readingOffset: Int = 0 - public let byteSize: Int +internal struct Binary { + internal let sectionSize: Int + internal let bytes: [UInt8] + internal var readingOffset: Int = 0 + internal let byteSize: Int - public init(data: Data, sectionSize: Int, byteSize: Int = 8) { + internal init(data: Data, sectionSize: Int, byteSize: Int = 8) { self.byteSize = byteSize let bytesLength = data.count var bytesArray = [UInt8](repeating: 0, count: bytesLength) @@ -16,7 +16,7 @@ public struct Binary { readingOffset = (data.count * byteSize) % sectionSize - sectionSize } - public func bit(_ position: Int) -> Int { + private func bit(_ position: Int) -> Int { guard position >= 0 else { return 0 } @@ -26,7 +26,7 @@ public struct Binary { return (byte >> bitPosition) & 0x01 } - public func bits(_ range: Range) -> Int { + private func bits(_ range: Range) -> Int { var positions = [Int]() for position in range.lowerBound ..< range.upperBound { @@ -38,19 +38,19 @@ public struct Binary { } } - public func bits(_ start: Int, _ length: Int) -> Int { + private func bits(_ start: Int, _ length: Int) -> Int { bits(start ..< (start + length)) } - public func byte(_ position: Int) -> Int { + private func byte(_ position: Int) -> Int { Int(bytes[position]) } - public func bitsWithInternalOffsetAvailable(_ length: Int) -> Bool { + private func bitsWithInternalOffsetAvailable(_ length: Int) -> Bool { (bytes.count * byteSize) >= (readingOffset + length) } - public mutating func nextSection() -> Int? { + internal mutating func nextSection() -> Int? { if bitsWithInternalOffsetAvailable(sectionSize) { let returnValue = bits(readingOffset, sectionSize) readingOffset += sectionSize @@ -60,7 +60,7 @@ public struct Binary { } } - public mutating func string(basedOnCharacterMap characterMap: String) -> String { + internal mutating func string(basedOnCharacterMap characterMap: String) -> String { var encodedString = "" var index: Int? diff --git a/Sources/Base32Crockford/Data.swift b/Sources/ThirtyTo/Data.swift similarity index 78% rename from Sources/Base32Crockford/Data.swift rename to Sources/ThirtyTo/Data.swift index e8183ef..141a31e 100644 --- a/Sources/Base32Crockford/Data.swift +++ b/Sources/ThirtyTo/Data.swift @@ -1,7 +1,7 @@ import Foundation extension Data { - public func remainderBy(_ divisor: Int) -> Int { + internal func remainderBy(_ divisor: Int) -> Int { var remainder = 0 var number = self for (index, value) in number.enumerated() { @@ -12,7 +12,7 @@ extension Data { return remainder } - public func trim(to count: Int, andPadWith fill: UInt8 = 0) -> Data { + internal func trim(to count: Int, andPadWith fill: UInt8 = 0) -> Data { let fillSize = Swift.max(count - self.count, 0) let fillData = Data(repeating: fill, count: fillSize) let bytes = (fillData + self).suffix(count) diff --git a/Sources/ThirtyTo/Documentation.docc/Documentation.md b/Sources/ThirtyTo/Documentation.docc/Documentation.md new file mode 100644 index 0000000..7a5d09c --- /dev/null +++ b/Sources/ThirtyTo/Documentation.docc/Documentation.md @@ -0,0 +1,260 @@ +# ``ThirtyTo`` + +Swift Package for using Base32Crockford Encoding for Data and Identifiers. + +## Overview + +**ThirtyTo** provides a way to encode data and create identifiers which is both efficient and human-readable. While Base64 is more efficient it is not very human-readable with both both upper case and lower case letters as well as punctuation. + +### Requirements + +**Apple Platforms** + +- Xcode 13.3 or later +- Swift 5.5.2 or later +- iOS 14 / watchOS 6 / tvOS 14 / macOS 12 or later deployment targets + +**Linux** + +- Ubuntu 18.04 or later +- Swift 5.5.2 or later + +### Installation + +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. In dictum non consectetur a erat nam at lectus urna. Maecenas accumsan lacus vel facilisis volutpat est velit. + +### Why use Base32Crockford + +Base32Crockford offers the most reasonable compromise when it comes to encoding data. Being a super set of Base16, it uses all ten digits and 22 of the 26 Latin upper case characters. + +| Symbol Value | Decode Symbol | Encode Symbol | +|:------------: |:-------------: |:-------------: | +| 0 | 0 O o | 0 | +| 1 | 1 I i L l | 1 | +| 2 | 2 | 2 | +| 3 | 3 | 3 | +| 4 | 4 | 4 | +| 5 | 5 | 5 | +| 6 | 6 | 6 | +| 7 | 7 | 7 | +| 8 | 8 | 8 | +| 9 | 9 | 9 | +| 10 | A a | A | +| 11 | B b | B | +| 12 | C c | C | +| 13 | D d | D | +| 14 | E e | E | +| 15 | F f | F | +| 16 | G g | G | +| 17 | H h | H | +| 18 | J j | J | +| 19 | K k | K | +| 20 | M m | M | +| 21 | N n | N | +| 22 | P p | P | +| 23 | Q q | Q | +| 24 | R r | R | +| 25 | S s | S | +| 26 | T t | T | +| 27 | V v | V | +| 28 | W w | W | +| 29 | X x | X | +| 30 | Y y | Y | +| 31 | Z z | Z | + +### Usage + +**ThirtyTo** enables the encoding and decoding data in _Base32Crockford_ as well as creation of unique identifiers. There are a variety of options available for encoding and decoding. + +### Encoding and Decoding Data + +All encoding and decoding is done through the `Base32CrockfordEncoding.encoding` object. This provides an interface into encoding and decoding data as well standardizing. + +To encode any data call: + +```swift +public func encode( + data: Data, + options: Base32CrockfordEncodingOptions = .none + ) -> String +``` + +Therefore to encode a `Data` object, simply call via: + +```swift +let data : Data // 0x00b003786a8d4aa28411f4e268c43629 +let base32String = Base32CrockfordEncoding.encoding.encode(data: data) +print(base32String) // P01QGTMD9AH884FMW9MC8DH9 +``` + +If you wish to decode the string you can call `Base32CrockfordEncoding.decode`: + +```swift +let data = try Base32CrockfordEncoding.encoding.decode( + base32Encoded: "P01QGTMD9AH884FMW9MC8DH9" +) // 0x00b003786a8d4aa28411f4e268c43629 +``` + +The `Base32CrockfordEncoding.encode` object provides the ability to specify options on formatting and a checksum. + +#### How Checksum Works + +You can optionally provide a checksum character at the end which allows for detecting transmission and entry errors early and inexpensively. + +According to the specification: + +> The check symbol encodes the number modulo 37, 37 being the least prime number greater than 32. We introduce 5 additional symbols that are used only for encoding or decoding the check symbol. + +The additional 5 symbols are: + +| Symbol Value | Decode Symbol | Encode Symbol | +|---- |----- |--- | +| 32 | * | * | +| 33 | ~ | ~ | +| 34 | $ | $ | +| 35 | = | = | +| 36 | U u | U | + +If you wish to include the checksum, pass true for the `withChecksum` property on the `Base32CrockfordEncodingOptions` object: + +```swift +let data : Data // 0xb63d798c4329401684d1d41d3becc95c +let base32String = Base32CrockfordEncoding.encoding.encode( + data: data, + options: .init(withChecksum: true) +) +print(base32String) // 5P7NWRRGS980B89MEM3MXYSJAW5 +``` + +When decoding a string wtih a checksum, you must specify true for the `withChecksum` property on `Base32CrockfordDecodingOptions`: + +```swift +let data = try Base32CrockfordEncoding.encoding.decode( + base32Encoded: "5P7NWRRGS980B89MEM3MXYSJAW5"a, + options: .init(withChecksum: true) +) // 0xb63d798c4329401684d1d41d3becc95c +``` + +Besides adding a checksum, `Base32CrockfordEncodingOptions` also provides the ability to add a grouping separator. + +#### Using Group Separators + +According to the Base32Crockford specification: + +> Hyphens (-) can be inserted into symbol strings. This can partition a string into manageable pieces, improving readability by helping to prevent confusion. Hyphens are ignored during decoding. + +To insert hyphens to the encoded string, provide the `GroupingOptions` object to `Base32CrockfordEncodingOptions`: + +```swift +let data : Data // 00c9c37484b85a42e8b3e7fbf806f2661b +let base32StringGroupedBy3 = Base32CrockfordEncoding.encoding.encode( + data: data, + options: .init(groupingBy: .init(maxLength: 3)) +) +let base32StringGroupedBy9 = Base32CrockfordEncoding.encoding.encode( + data: data, + options: .init(groupingBy: .init(maxLength: 9)) +) +print(base32StringGroupedBy3) // 69R-DT8-9E2-T8B-MB7-SZV-Z03-F4S-GV2 +print(base32StringGroupedBy9) // 69RDT89E2-T8BMB7SZV-Z03F4SGV2 +``` + +When decoding, hyphens are ignored: + +```swift +let dataNoHyphens = try Base32CrockfordEncoding.encoding.decode( + base32Encoded: "69RDT89E2T8BMB7SZVZ03F4SGV2" +) // 00c9c37484b85a42e8b3e7fbf806f2661b + +let dataGroupedBy3 = try Base32CrockfordEncoding.encoding.decode( + base32Encoded: "69R-DT8-9E2-T8B-MB7-SZV-Z03-F4S-GV2" +) // 00c9c37484b85a42e8b3e7fbf806f2661b + +let dataGroupedBy9 = try Base32CrockfordEncoding.encoding.decode( + base32Encoded: "69RDT89E2-T8BMB7SZV-Z03F4SGV2" +) // 00c9c37484b85a42e8b3e7fbf806f2661b + +assert(dataNoHyphens == dataGroupedBy3) // true +assert(dataNoHyphens == dataGroupedBy9) // true +assert(dataGroupedBy3 == dataGroupedBy9) // true +``` + +### Creating an Identifier + +Lorem markdownum duas, qui data superare trisulcis rex haec unius! Rupe quo aut, +cum per, pius attactu. Repperit canenda deiectuque coepit vertitur violentus +quoque! Siccoque corpus. Illa intima Bacchum nativum. + +Verque aves ab verba. Hoc auris sed formosissimus malorum virum: cum locoque +genuit, lumina velamina, huc. Materiam cetera, forte, deus tibi hiberna vates +revocamina. Tenebat validisne quod post longe parvis, sic superari! + +- Atque et volvitur corpora +- Est ab protinus cornua renuente medii dum +- Modo suo convertit temporis Lapithas numenque coronat + +#### UUID + +Lorem markdownum adire sui erit suis, esse. Iuvenem merentem negare ingentia et +vitta, Oeagrius sic turpe colonos opertos quaerit aquas ira parsque parenti +pericula. Vestra omni amans illius tactuque de ille tuo ipso excipit meque +quoque hosti abstulit; aurum [nato corpora +velare](http://retexitur-notata.org/spretoret). Partem cincta. + + var oop_rj_rate = prompt(nui_web); + website -= app.modifier(leopardWebmail.subnet_jpeg_native(print_mbr_boot, + lun_oop), address_printer_boot); + var superscalar = classFirewireHard; + ipad_browser = widgetSecondary.standby_xp_sku(rosetta_igp + 79, 1, + key_soap_network) - fullBittorrentMail; + +Gratissime iunxit, neque *praebere*, cum et nec axes, vara otia. Nantemque est; +iterum quid mortemque dominae non baculum tincto. Fuit voce; **ab** cingentibus +feraxque summaque nomen suo, spemque minor: quae Ceyx omnis tinctam. + +#### What is ULID? + +Lorem markdownum tenebat. Quo et quis expellitur potes tenuitque impetus est +Achilles, et gelidas, acutae. Enim non ceu fluentia Actaeon Numidasque turbae +expugnare flebile pedes, vultus, danda. Thetis in medio est cornu comitante +fugio requievit corpora miseri primisque primo. + + if (bridge_keystroke_architecture.white(riscRipcording, jfs, + optical_operation_soft) >= softwarePad(in_model, dynamic)) { + ram(flatbed); + isa(vpi.heat_disk_permalink(socket_nic_optical), 3); + } else { + hard = bar_ddr_modem + 4 + cybersquatter; + } + pack(nodeCgi); + if (fileSoftwareInput(storageRawUp + restoreSyncNull, dot_secondary) != + click_windows_text) { + flash(engineDocumentWins, cpaBounce.web.errorUat(34, 4)); + bps_graphics_syntax.dpi_truncate_panel(netbios_dv(-4, hostHttps, + surgeRemote)); + ups_flowchart_plug.copy = seoTrashMatrix(unit_cache, 130102, 5); + } + matrixFile += tunneling_dram_graphic; + var alertParse = -2; + +Amnis per aede munus, colorem *semper*, non manu vera petita tamen. Lanigeris +alium victo, novantur faciem Thetidis **raptore prodere flumine** sanguisque ad +*claudit* cupidine, ut vitiorum coniungere quoque campo. + +### References + +* Crockford32 +* ULID + +### License + +This code is distributed under the MIT license. See the [LICENSE](https://github.com/brightdigit/ThirtyTo/LICENSE) file for more info. + +## Topics + +### Encoding and Decoding Data + +- ``Base32CrockfordEncoding`` +- ``Base32CrockfordEncodingOptions`` +- ``Base32CrockfordDecodingOptions`` +- ``Base32CrockfordDecodingError`` diff --git a/Sources/Base32Crockford/String.swift b/Sources/ThirtyTo/String.swift similarity index 94% rename from Sources/Base32Crockford/String.swift rename to Sources/ThirtyTo/String.swift index bbe7d07..c304eb6 100644 --- a/Sources/Base32Crockford/String.swift +++ b/Sources/ThirtyTo/String.swift @@ -1,5 +1,5 @@ extension String { - public func pad(toSize: Int) -> String { + internal func pad(toSize: Int) -> String { var padded = self for _ in 0 ..< (toSize - count) { padded = "0" + padded @@ -7,7 +7,7 @@ extension String { return padded } - public func split(by length: Int) -> [String] { + internal func split(by length: Int) -> [String] { var endIndex = self.endIndex var results = [Substring]() diff --git a/Sources/ThirtyTo/UUID.swift b/Sources/ThirtyTo/UUID.swift new file mode 100644 index 0000000..357d142 --- /dev/null +++ b/Sources/ThirtyTo/UUID.swift @@ -0,0 +1,13 @@ +import Foundation + +extension UUID { + /// Create a UUID from bytes. + /// - Parameter data: Bytes of the UUID. + public init(data: Data) { + assert(data.count == 16) + let uuidC = data.withUnsafeBytes { + $0.load(as: uuid_t.self) + } + self.init(uuid: uuidC) + } +} diff --git a/Tests/Base32CrockfordTests/Base32CrockfordTests.swift b/Tests/ThirtyToTests/Base32CrockfordTests.swift similarity index 90% rename from Tests/Base32CrockfordTests/Base32CrockfordTests.swift rename to Tests/ThirtyToTests/Base32CrockfordTests.swift index 2f3f889..31ba25f 100644 --- a/Tests/Base32CrockfordTests/Base32CrockfordTests.swift +++ b/Tests/ThirtyToTests/Base32CrockfordTests.swift @@ -1,4 +1,4 @@ -@testable import Base32Crockford +@testable import ThirtyTo import XCTest final class Base32CrockfordTests: XCTestCase { @@ -48,6 +48,7 @@ final class Base32CrockfordTests: XCTestCase { let divisor: UInt128 = 37 let uuidData = Data(Array(uuid: parameters.uuid)) let encodedUUID = Base32CrockfordEncoding.encoding.encode(data: uuidData) + let encodedInt = Base32CrockfordEncoding.encoding.encode(data: parameters.integer.data) let encodedUUIDChecksum = Base32CrockfordEncoding.encoding.encode(data: uuidData, options: .init(withChecksum: true)) @@ -55,18 +56,18 @@ final class Base32CrockfordTests: XCTestCase { let decodedUUID = UUID(data: decodedUUIDBytes) let decodedWithDashesUUIDBytes = try Base32CrockfordEncoding.encoding.decode(base32Encoded: parameters.encoded.randomDashes()).trim(to: 16) - let decodedWithDashesUUID = UUID(data: decodedUUIDBytes) + let decodedWithDashesUUID = UUID(data: decodedWithDashesUUIDBytes) let decodedUUIDChecksumBytes = try Base32CrockfordEncoding.encoding.decode(base32Encoded: parameters.encodedWithChecksum, options: .init(withChecksum: true)).trim(to: 16) let decodedUUIDChecksum = UUID(data: decodedUUIDChecksumBytes) let decodedUUIDChecksumWithDashesBytes = try Base32CrockfordEncoding.encoding.decode(base32Encoded: parameters.encodedWithChecksum.randomDashes(), options: .init(withChecksum: true)).trim(to: 16) - let decodedWithDashesUUIDChecksum = UUID(data: decodedUUIDChecksumBytes) + let decodedWithDashesUUIDChecksum = UUID(data: decodedUUIDChecksumWithDashesBytes) - let moduloIndex = Base32CrockfordEncoding.allChecksumSymbols.firstIndex(of: parameters.encodedWithChecksum.last!)! + let moduloIndex = Base32CrockfordEncoding.CharacterSets.checkSymbols.firstIndex(of: parameters.encodedWithChecksum.last!)! - let modulo = Base32CrockfordEncoding.allChecksumSymbols.distance( - from: Base32CrockfordEncoding.allChecksumSymbols.startIndex, + let modulo = Base32CrockfordEncoding.CharacterSets.checkSymbols.distance( + from: Base32CrockfordEncoding.CharacterSets.checkSymbols.startIndex, to: moduloIndex ) @@ -96,11 +97,11 @@ final class Base32CrockfordTests: XCTestCase { } var badChecksum = checksum repeat { - badChecksum = Base32CrockfordEncoding.allChecksumSymbols.randomElement()! + badChecksum = Base32CrockfordEncoding.CharacterSets.checkSymbols.randomElement()! } while badChecksum == checksum let badEncodedWithChecksum = encoded.appending(String(badChecksum)) - let mismatchValueExpected = Base32CrockfordEncoding.allChecksumSymbols.firstOffsetOf(character: badChecksum) - let badChecksumExpected = Base32CrockfordEncoding.allChecksumSymbols.firstOffsetOf(character: checksum) + let mismatchValueExpected = Base32CrockfordEncoding.CharacterSets.checkSymbols.firstOffsetOf(character: badChecksum) + let badChecksumExpected = Base32CrockfordEncoding.CharacterSets.checkSymbols.firstOffsetOf(character: checksum) do { _ = try Base32CrockfordEncoding.encoding.decode(base32Encoded: badEncodedWithChecksum, options: .init(withChecksum: true)) XCTFail() @@ -143,6 +144,7 @@ final class Base32CrockfordTests: XCTestCase { let decoded = try Base32CrockfordEncoding.encoding.decode(base32Encoded: base32String, options: .init(withChecksum: true)) let actual3String = Base32CrockfordEncoding.encoding.encode(data: decoded, options: .init(withChecksum: true, groupingBy: .init(maxLength: 3))) let actual9String = Base32CrockfordEncoding.encoding.encode(data: decoded, options: .init(withChecksum: true, groupingBy: .init(maxLength: 9))) + XCTAssertEqual(actual3String, expected3String) XCTAssertEqual(actual9String, expected9String) } diff --git a/Tests/Base32CrockfordTests/BinaryTests.swift b/Tests/ThirtyToTests/BinaryTests.swift similarity index 94% rename from Tests/Base32CrockfordTests/BinaryTests.swift rename to Tests/ThirtyToTests/BinaryTests.swift index 560d4a8..cb67bbb 100644 --- a/Tests/Base32CrockfordTests/BinaryTests.swift +++ b/Tests/ThirtyToTests/BinaryTests.swift @@ -1,4 +1,4 @@ -import Base32Crockford +@testable import ThirtyTo import XCTest final class BinaryTests: XCTestCase { diff --git a/Tests/Base32CrockfordTests/Data.swift b/Tests/ThirtyToTests/Data.swift similarity index 91% rename from Tests/Base32CrockfordTests/Data.swift rename to Tests/ThirtyToTests/Data.swift index 88bdfc1..6ba6fd8 100644 --- a/Tests/Base32CrockfordTests/Data.swift +++ b/Tests/ThirtyToTests/Data.swift @@ -1,4 +1,4 @@ -@testable import Base32Crockford +@testable import ThirtyTo import XCTest extension Data { diff --git a/Tests/Base32CrockfordTests/String.swift b/Tests/ThirtyToTests/String.swift similarity index 95% rename from Tests/Base32CrockfordTests/String.swift rename to Tests/ThirtyToTests/String.swift index a384a7f..d0afba5 100644 --- a/Tests/Base32CrockfordTests/String.swift +++ b/Tests/ThirtyToTests/String.swift @@ -1,4 +1,4 @@ -import Foundation +import ThirtyTo extension String { func randomDashes() -> String { diff --git a/Tests/Base32CrockfordTests/UInt128+Data.swift b/Tests/ThirtyToTests/UInt128+Data.swift similarity index 89% rename from Tests/Base32CrockfordTests/UInt128+Data.swift rename to Tests/ThirtyToTests/UInt128+Data.swift index 06037e0..36bd128 100644 --- a/Tests/Base32CrockfordTests/UInt128+Data.swift +++ b/Tests/ThirtyToTests/UInt128+Data.swift @@ -1,4 +1,3 @@ -@testable import Base32Crockford import XCTest extension UInt128 { diff --git a/Tests/Base32CrockfordTests/UInt128.swift b/Tests/ThirtyToTests/UInt128.swift similarity index 98% rename from Tests/Base32CrockfordTests/UInt128.swift rename to Tests/ThirtyToTests/UInt128.swift index 237b3d4..e99a110 100644 --- a/Tests/Base32CrockfordTests/UInt128.swift +++ b/Tests/ThirtyToTests/UInt128.swift @@ -19,14 +19,14 @@ public struct UInt128 { /// Counts up the significant bits in stored data. public var significantBits: UInt128 { - return UInt128(UInt128.bitWidth - leadingZeroBitCount) + UInt128(UInt128.bitWidth - leadingZeroBitCount) } /// Undocumented private variable required for passing this type /// to a BinaryFloatingPoint type. See FloatingPoint.swift.gyb in /// the Swift stdlib/public/core directory. internal var signBitIndex: Int { - return 127 - leadingZeroBitCount + 127 - leadingZeroBitCount } // MARK: Initializers @@ -62,7 +62,7 @@ extension UInt128: FixedWidthInteger { // MARK: Instance Properties public var nonzeroBitCount: Int { - return value.lowerBits.nonzeroBitCount + value.upperBits.nonzeroBitCount + value.lowerBits.nonzeroBitCount + value.upperBits.nonzeroBitCount } public var leadingZeroBitCount: Int { @@ -92,8 +92,8 @@ extension UInt128: FixedWidthInteger { /// Returns the current integer with the byte order swapped. public var byteSwapped: UInt128 { - return UInt128(upperBits: value.lowerBits.byteSwapped, - lowerBits: value.upperBits.byteSwapped) + UInt128(upperBits: value.lowerBits.byteSwapped, + lowerBits: value.upperBits.byteSwapped) } // MARK: Initializers @@ -372,14 +372,14 @@ extension UInt128: FixedWidthInteger { extension UInt128 { // MARK: Instance Properties - public static var bitWidth: Int { return 128 } + public static var bitWidth: Int { 128 } } extension UInt128: BinaryInteger { // MARK: Instance Methods public var words: [UInt] { - return Array(value.lowerBits.words) + Array(value.upperBits.words) + Array(value.lowerBits.words) + Array(value.upperBits.words) } public var trailingZeroBitCount: Int { @@ -573,7 +573,7 @@ extension UInt128: CustomStringConvertible { // MARK: Instance Properties public var description: String { - return _valueToString() + _valueToString() } // MARK: Instance Methods @@ -615,7 +615,7 @@ extension UInt128: CustomStringConvertible { extension UInt128: CustomDebugStringConvertible { public var debugDescription: String { - return description + description } } From 571c30b85e935c42393fb695c75866d008cb8efe Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Wed, 4 Jan 2023 19:38:46 -0500 Subject: [PATCH 5/7] fixup! Adding Documentation (#30) --- .github/workflows/ThirtyTo.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ThirtyTo.yml b/.github/workflows/ThirtyTo.yml index 5920dac..f486c7b 100644 --- a/.github/workflows/ThirtyTo.yml +++ b/.github/workflows/ThirtyTo.yml @@ -63,7 +63,7 @@ jobs: run: swift test --enable-code-coverage - uses: sersoft-gmbh/swift-coverage-action@v3 with: - fail-on-empty-output: true + fail-on-empty-output: true - name: Upload package coverage to Codecov uses: codecov/codecov-action@v3 with: @@ -91,7 +91,7 @@ jobs: xcode: "/Applications/Xcode_14.1.app" iOSVersion: "16.1" watchOSVersion: "9.1" - watchName: "Apple Watch Ultra (49mm)" + watchName: "Apple Watch Ultra (49mm)" - runs-on: macos-12 xcode: "/Applications/Xcode_13.3.app" iOSVersion: 15.4 @@ -136,7 +136,7 @@ jobs: - name: Build run: swift build - name: Lint - run: ./scripts/lint.sh + run: ./scripts/lint.sh if: ${{ github.event_name == 'pull_request' && github.base_ref == 'main' && matrix.xcode == '/Applications/Xcode_14.1.app' }} - name: Dump PIF if: startsWith(matrix.xcode,'/Applications/Xcode_14') @@ -152,15 +152,15 @@ jobs: run: xcodebuild test -scheme ThirtyTo -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 13,OS=${{ matrix.iOSVersion }}' -enableCodeCoverage YES build test - uses: sersoft-gmbh/swift-coverage-action@v3 with: - fail-on-empty-output: true + fail-on-empty-output: true - name: Upload iOS coverage to Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v3 with: fail_ci_if_error: true flags: iOS,iOS-${{ matrix.iOSVersion }} - token: ${{ secrets.CODECOV_TOKEN }} + token: ${{ secrets.CODECOV_TOKEN }} - name: Run watchOS target tests - run: xcodebuild test -scheme ThirtyTo -sdk watchsimulator -destination 'platform=watchOS Simulator,name=${{ matrix.watchName }},OS=${{ matrix.watchOSVersion }}' -enableCodeCoverage YES build test + run: xcodebuild test -scheme ThirtyTo -sdk watchsimulator -destination 'platform=watchOS Simulator,name=${{ matrix.watchName }},OS=${{ matrix.watchOSVersion }}' -enableCodeCoverage YES build test - uses: sersoft-gmbh/swift-coverage-action@v3 with: fail-on-empty-output: true From c7b63c6a25dc9576329810fd5b49d61aa9a35415 Mon Sep 17 00:00:00 2001 From: leogdion Date: Sat, 7 Jan 2023 21:11:45 -0500 Subject: [PATCH 6/7] Added ULID #22 * started Identifier types * adding ULID --- .swiftformat | 2 +- README.md | 102 +++++++-------- .../Base32CrockfordDecodingError.swift | 0 .../Base32CrockfordDecodingOptions.swift | 0 ...ase32CrockfordEncoding+CharacterSets.swift | 0 .../Base32CrockfordEncoding.swift | 0 .../Base32CrockfordEncodingOptions.swift | 0 .../{ => Base32Crockford}/Binary.swift | 0 .../Documentation.docc/Documentation.md | 121 ++++++++++-------- Sources/ThirtyTo/{ => Extensions}/Array.swift | 0 Sources/ThirtyTo/{ => Extensions}/Data.swift | 7 - Sources/ThirtyTo/Extensions/Date.swift | 26 ++++ .../ThirtyTo/{ => Extensions}/String.swift | 0 .../UUID+Data.swift} | 0 .../Factory/AnyComposableIdentifier.swift | 26 ++++ .../Factory/AnyIdentifierSpecifications.swift | 25 ++++ .../Factory/ComposableIdentifier.swift | 15 +++ Sources/ThirtyTo/Factory/Identifier.swift | 33 +++++ .../ThirtyTo/Factory/IdentifierFactory.swift | 56 ++++++++ .../ThirtyTo/Factory/SizeSpecification.swift | 50 ++++++++ Sources/ThirtyTo/Identifiers/UDID.swift | 19 +++ Sources/ThirtyTo/Identifiers/ULID.swift | 70 ++++++++++ .../UUID+ComposableIdentifier.swift | 13 ++ Sources/ThirtyTo/Random/Data+Random.swift | 54 ++++++++ .../Random/NumberedDataGenerator.swift | 25 ++++ .../ThirtyTo/Random/RandomDataGenerator.swift | 9 ++ .../Random/SecRandomDataGenerator.swift | 31 +++++ .../AnyComposableIdentifierTests.swift | 30 +++++ .../ThirtyToTests/Base32CrockfordTests.swift | 2 +- .../ThirtyToTests/{ => Extensions}/Data.swift | 8 +- .../Extensions/MockNumberGenerator.swift | 12 ++ .../Extensions/MockRandomGenerator.swift | 12 ++ .../{ => Extensions}/String.swift | 2 - .../{ => Extensions}/UInt128+Data.swift | 0 .../{ => Extensions}/UInt128.swift | 0 Tests/ThirtyToTests/IdentifierTests.swift | 50 ++++++++ Tests/ThirtyToTests/RandomDataTests.swift | 29 +++++ .../SizeSpecificationTests.swift | 37 ++++++ Tests/ThirtyToTests/TimestampDataTests.swift | 18 +++ Tests/ThirtyToTests/ULIDTests.swift | 34 +++++ 40 files changed, 797 insertions(+), 121 deletions(-) rename Sources/ThirtyTo/{ => Base32Crockford}/Base32CrockfordDecodingError.swift (100%) rename Sources/ThirtyTo/{ => Base32Crockford}/Base32CrockfordDecodingOptions.swift (100%) rename Sources/ThirtyTo/{ => Base32Crockford}/Base32CrockfordEncoding+CharacterSets.swift (100%) rename Sources/ThirtyTo/{ => Base32Crockford}/Base32CrockfordEncoding.swift (100%) rename Sources/ThirtyTo/{ => Base32Crockford}/Base32CrockfordEncodingOptions.swift (100%) rename Sources/ThirtyTo/{ => Base32Crockford}/Binary.swift (100%) rename Sources/ThirtyTo/{ => Extensions}/Array.swift (100%) rename Sources/ThirtyTo/{ => Extensions}/Data.swift (56%) create mode 100644 Sources/ThirtyTo/Extensions/Date.swift rename Sources/ThirtyTo/{ => Extensions}/String.swift (100%) rename Sources/ThirtyTo/{UUID.swift => Extensions/UUID+Data.swift} (100%) create mode 100644 Sources/ThirtyTo/Factory/AnyComposableIdentifier.swift create mode 100644 Sources/ThirtyTo/Factory/AnyIdentifierSpecifications.swift create mode 100644 Sources/ThirtyTo/Factory/ComposableIdentifier.swift create mode 100644 Sources/ThirtyTo/Factory/Identifier.swift create mode 100644 Sources/ThirtyTo/Factory/IdentifierFactory.swift create mode 100644 Sources/ThirtyTo/Factory/SizeSpecification.swift create mode 100644 Sources/ThirtyTo/Identifiers/UDID.swift create mode 100644 Sources/ThirtyTo/Identifiers/ULID.swift create mode 100644 Sources/ThirtyTo/Identifiers/UUID+ComposableIdentifier.swift create mode 100644 Sources/ThirtyTo/Random/Data+Random.swift create mode 100644 Sources/ThirtyTo/Random/NumberedDataGenerator.swift create mode 100644 Sources/ThirtyTo/Random/RandomDataGenerator.swift create mode 100644 Sources/ThirtyTo/Random/SecRandomDataGenerator.swift create mode 100644 Tests/ThirtyToTests/AnyComposableIdentifierTests.swift rename Tests/ThirtyToTests/{ => Extensions}/Data.swift (59%) create mode 100644 Tests/ThirtyToTests/Extensions/MockNumberGenerator.swift create mode 100644 Tests/ThirtyToTests/Extensions/MockRandomGenerator.swift rename Tests/ThirtyToTests/{ => Extensions}/String.swift (95%) rename Tests/ThirtyToTests/{ => Extensions}/UInt128+Data.swift (100%) rename Tests/ThirtyToTests/{ => Extensions}/UInt128.swift (100%) create mode 100644 Tests/ThirtyToTests/IdentifierTests.swift create mode 100644 Tests/ThirtyToTests/RandomDataTests.swift create mode 100644 Tests/ThirtyToTests/SizeSpecificationTests.swift create mode 100644 Tests/ThirtyToTests/TimestampDataTests.swift create mode 100644 Tests/ThirtyToTests/ULIDTests.swift diff --git a/.swiftformat b/.swiftformat index 2601bf7..d9131d4 100644 --- a/.swiftformat +++ b/.swiftformat @@ -4,4 +4,4 @@ --disable wrapMultilineStatementBraces --extensionacl on-declarations --decimalgrouping 3,4 ---exclude .build, DerivedData +--exclude .build, DerivedData, _archive diff --git a/README.md b/README.md index 6a920fb..5f5d19e 100644 --- a/README.md +++ b/README.md @@ -34,12 +34,10 @@ Swift Package for using Base32Crockford Encoding for Data and Identifiers. * [How Checksum Works](#how-checksum-works) * [Using Group Separators](#using-group-separators) * [Creating an Identifier](#creating-an-identifier) - * [UUID](#uuid) * [What is ULID?](#what-is-ulid) * [References](#references) * [License](#license) - # Introduction **ThirtyTo** provides a way to encode data and create identifiers which is both efficient and human-readable. While Base64 is more efficient it is not very human-readable with both both upper case and lower case letters as well as punctuation. @@ -59,7 +57,13 @@ Swift Package for using Base32Crockford Encoding for Data and Identifiers. ## Installation -Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. In dictum non consectetur a erat nam at lectus urna. Maecenas accumsan lacus vel facilisis volutpat est velit. +Use the Swift Package Manager to install this library via the repository url: + +``` +https://github.com/brightdigit/ThirtyTo.git +``` + +Use version up to `1.0`. ## Why use Base32Crockford @@ -219,70 +223,58 @@ assert(dataGroupedBy3 == dataGroupedBy9) // true ## Creating an Identifier -Lorem markdownum duas, qui data superare trisulcis rex haec unius! Rupe quo aut, -cum per, pius attactu. Repperit canenda deiectuque coepit vertitur violentus -quoque! Siccoque corpus. Illa intima Bacchum nativum. -Verque aves ab verba. Hoc auris sed formosissimus malorum virum: cum locoque -genuit, lumina velamina, huc. Materiam cetera, forte, deus tibi hiberna vates -revocamina. Tenebat validisne quod post longe parvis, sic superari! +**ThirtyTo** offers the ability to create identifiers of different types in a universal factory. +While `UUID` has been available to developers, this library provides a interface for creating two other types. To do this you can call `IdentifierFactory/createIdentifier(with:)` on the `Identifier/factory` to create any of the three types provided: -- Atque et volvitur corpora -- Est ab protinus cornua renuente medii dum -- Modo suo convertit temporis Lapithas numenque coronat +- `UUID` - standard universal identifier +- `ULID` - [Universally Unique Lexicographically Sortable Identifier](https://github.com/ulid) +- `UDID` - dynamicly sized random identifier -### UUID +Each type has corresponding `ComposableIdentifier/Specifications`: -Lorem markdownum adire sui erit suis, esse. Iuvenem merentem negare ingentia et -vitta, Oeagrius sic turpe colonos opertos quaerit aquas ira parsque parenti -pericula. Vestra omni amans illius tactuque de ille tuo ipso excipit meque -quoque hosti abstulit; aurum [nato corpora -velare](http://retexitur-notata.org/spretoret). Partem cincta. +- `UUID` takes no specifications and be created with `IdentifierFactory.createIdentifier(_:)` +- `ULID` takes `ULID/Specifications` +- `UDID` takes `AnyIdentifierSpecifications` - var oop_rj_rate = prompt(nui_web); - website -= app.modifier(leopardWebmail.subnet_jpeg_native(print_mbr_boot, - lun_oop), address_printer_boot); - var superscalar = classFirewireHard; - ipad_browser = widgetSecondary.standby_xp_sku(rosetta_igp + 79, 1, - key_soap_network) - fullBittorrentMail; +Here's an example with `UDID`: -Gratissime iunxit, neque *praebere*, cum et nec axes, vara otia. Nantemque est; -iterum quid mortemque dominae non baculum tincto. Fuit voce; **ab** cingentibus -feraxque summaque nomen suo, spemque minor: quae Ceyx omnis tinctam. +``` +// create an identifier for 1 million unique IDs that is a factor of 5. +let specs = AnyIdentifierSpecifications( + size: .minimumCount(1_000_000, factorOf: 5) +) +let identifier: UDID = Identifier.factory.createIdentifier(with: specs) +``` ### What is ULID? -Lorem markdownum tenebat. Quo et quis expellitur potes tenuitque impetus est -Achilles, et gelidas, acutae. Enim non ceu fluentia Actaeon Numidasque turbae -expugnare flebile pedes, vultus, danda. Thetis in medio est cornu comitante -fugio requievit corpora miseri primisque primo. - - if (bridge_keystroke_architecture.white(riscRipcording, jfs, - optical_operation_soft) >= softwarePad(in_model, dynamic)) { - ram(flatbed); - isa(vpi.heat_disk_permalink(socket_nic_optical), 3); - } else { - hard = bar_ddr_modem + 4 + cybersquatter; - } - pack(nodeCgi); - if (fileSoftwareInput(storageRawUp + restoreSyncNull, dot_secondary) != - click_windows_text) { - flash(engineDocumentWins, cpaBounce.web.errorUat(34, 4)); - bps_graphics_syntax.dpi_truncate_panel(netbios_dv(-4, hostHttps, - surgeRemote)); - ups_flowchart_plug.copy = seoTrashMatrix(unit_cache, 130102, 5); - } - matrixFile += tunneling_dram_graphic; - var alertParse = -2; - -Amnis per aede munus, colorem *semper*, non manu vera petita tamen. Lanigeris -alium victo, novantur faciem Thetidis **raptore prodere flumine** sanguisque ad -*claudit* cupidine, ut vitiorum coniungere quoque campo. +`ULID` serves the purpose of solving several issues with `UUID` while being compatibile: + +- 1.21e+24 unique ULIDs per millisecond +- Lexicographically sortable! +- Monotonic sort order (correctly detects and handles the same millisecond) + +Most importantly it Uses Base32Crockford for better efficiency and readability. + +To create a ULID you can either use `IdentifierFactory.createIdentifier(with:)` on `Identifier.factory`: + +``` +let ulid : ULID = Identifier.factory.createIdentifier(with: .parts(nil, .random(nil))) +``` + +or a constructor: + +``` +let ulid = ULID(specifications: .parts(nil, .random(nil))) +``` + +For most cases the default `ULID.Specifications.default` specification is sufficient. The follows the canonical spec which uses the first 6 bytes for a the timestamp and the last 10 bytes are random. Otherwise you can specify all 16 bytes with `ULID.Specifications.data(_:)` or specify which `Date` to use for the timestamp and the `RandomDataGenerator` to use for the `ULID.randomPart` of the data. # References -* Crockford32 -* ULID +* [Base32 Specifications from crockford.com](https://www.crockford.com/base32.html) +* [ULID Specifications](https://github.com/ulid/spec) # License diff --git a/Sources/ThirtyTo/Base32CrockfordDecodingError.swift b/Sources/ThirtyTo/Base32Crockford/Base32CrockfordDecodingError.swift similarity index 100% rename from Sources/ThirtyTo/Base32CrockfordDecodingError.swift rename to Sources/ThirtyTo/Base32Crockford/Base32CrockfordDecodingError.swift diff --git a/Sources/ThirtyTo/Base32CrockfordDecodingOptions.swift b/Sources/ThirtyTo/Base32Crockford/Base32CrockfordDecodingOptions.swift similarity index 100% rename from Sources/ThirtyTo/Base32CrockfordDecodingOptions.swift rename to Sources/ThirtyTo/Base32Crockford/Base32CrockfordDecodingOptions.swift diff --git a/Sources/ThirtyTo/Base32CrockfordEncoding+CharacterSets.swift b/Sources/ThirtyTo/Base32Crockford/Base32CrockfordEncoding+CharacterSets.swift similarity index 100% rename from Sources/ThirtyTo/Base32CrockfordEncoding+CharacterSets.swift rename to Sources/ThirtyTo/Base32Crockford/Base32CrockfordEncoding+CharacterSets.swift diff --git a/Sources/ThirtyTo/Base32CrockfordEncoding.swift b/Sources/ThirtyTo/Base32Crockford/Base32CrockfordEncoding.swift similarity index 100% rename from Sources/ThirtyTo/Base32CrockfordEncoding.swift rename to Sources/ThirtyTo/Base32Crockford/Base32CrockfordEncoding.swift diff --git a/Sources/ThirtyTo/Base32CrockfordEncodingOptions.swift b/Sources/ThirtyTo/Base32Crockford/Base32CrockfordEncodingOptions.swift similarity index 100% rename from Sources/ThirtyTo/Base32CrockfordEncodingOptions.swift rename to Sources/ThirtyTo/Base32Crockford/Base32CrockfordEncodingOptions.swift diff --git a/Sources/ThirtyTo/Binary.swift b/Sources/ThirtyTo/Base32Crockford/Binary.swift similarity index 100% rename from Sources/ThirtyTo/Binary.swift rename to Sources/ThirtyTo/Base32Crockford/Binary.swift diff --git a/Sources/ThirtyTo/Documentation.docc/Documentation.md b/Sources/ThirtyTo/Documentation.docc/Documentation.md index 7a5d09c..9e3e86f 100644 --- a/Sources/ThirtyTo/Documentation.docc/Documentation.md +++ b/Sources/ThirtyTo/Documentation.docc/Documentation.md @@ -21,7 +21,13 @@ Swift Package for using Base32Crockford Encoding for Data and Identifiers. ### Installation -Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. In dictum non consectetur a erat nam at lectus urna. Maecenas accumsan lacus vel facilisis volutpat est velit. +Use the Swift Package Manager to install this library via the repository url: + +``` +https://github.com/brightdigit/ThirtyTo.git +``` + +Use version up to `1.0`. ### Why use Base32Crockford @@ -181,70 +187,57 @@ assert(dataGroupedBy3 == dataGroupedBy9) // true ### Creating an Identifier -Lorem markdownum duas, qui data superare trisulcis rex haec unius! Rupe quo aut, -cum per, pius attactu. Repperit canenda deiectuque coepit vertitur violentus -quoque! Siccoque corpus. Illa intima Bacchum nativum. +**ThirtyTo** offers the ability to create identifiers of different types in a universal factory. +While `UUID` has been available to developers, this library provides a interface for creating two other types. To do this you can call ``IdentifierFactory/createIdentifier(with:)`` on the ``Identifier/factory`` to create any of the three types provided: -Verque aves ab verba. Hoc auris sed formosissimus malorum virum: cum locoque -genuit, lumina velamina, huc. Materiam cetera, forte, deus tibi hiberna vates -revocamina. Tenebat validisne quod post longe parvis, sic superari! +- `UUID` - standard universal identifier +- ``ULID`` - [Universally Unique Lexicographically Sortable Identifier](https://github.com/ulid) +- ``UDID`` - dynamicly sized random identifier -- Atque et volvitur corpora -- Est ab protinus cornua renuente medii dum -- Modo suo convertit temporis Lapithas numenque coronat +Each type has corresponding ``ComposableIdentifier/Specifications``: -#### UUID +- `UUID` takes no specifications and be created with ``IdentifierFactory/createIdentifier(_:)`` +- ``ULID`` takes ``ULID/Specifications`` +- ``UDID`` takes ``AnyIdentifierSpecifications`` -Lorem markdownum adire sui erit suis, esse. Iuvenem merentem negare ingentia et -vitta, Oeagrius sic turpe colonos opertos quaerit aquas ira parsque parenti -pericula. Vestra omni amans illius tactuque de ille tuo ipso excipit meque -quoque hosti abstulit; aurum [nato corpora -velare](http://retexitur-notata.org/spretoret). Partem cincta. +Here's an example with ``UDID``: - var oop_rj_rate = prompt(nui_web); - website -= app.modifier(leopardWebmail.subnet_jpeg_native(print_mbr_boot, - lun_oop), address_printer_boot); - var superscalar = classFirewireHard; - ipad_browser = widgetSecondary.standby_xp_sku(rosetta_igp + 79, 1, - key_soap_network) - fullBittorrentMail; - -Gratissime iunxit, neque *praebere*, cum et nec axes, vara otia. Nantemque est; -iterum quid mortemque dominae non baculum tincto. Fuit voce; **ab** cingentibus -feraxque summaque nomen suo, spemque minor: quae Ceyx omnis tinctam. +``` +// create an identifier for 1 million unique IDs that is a factor of 5. +let specs = AnyIdentifierSpecifications( + size: .minimumCount(1_000_000, factorOf: 5) +) +let identifier: UDID = Identifier.factory.createIdentifier(with: specs) +``` #### What is ULID? -Lorem markdownum tenebat. Quo et quis expellitur potes tenuitque impetus est -Achilles, et gelidas, acutae. Enim non ceu fluentia Actaeon Numidasque turbae -expugnare flebile pedes, vultus, danda. Thetis in medio est cornu comitante -fugio requievit corpora miseri primisque primo. - - if (bridge_keystroke_architecture.white(riscRipcording, jfs, - optical_operation_soft) >= softwarePad(in_model, dynamic)) { - ram(flatbed); - isa(vpi.heat_disk_permalink(socket_nic_optical), 3); - } else { - hard = bar_ddr_modem + 4 + cybersquatter; - } - pack(nodeCgi); - if (fileSoftwareInput(storageRawUp + restoreSyncNull, dot_secondary) != - click_windows_text) { - flash(engineDocumentWins, cpaBounce.web.errorUat(34, 4)); - bps_graphics_syntax.dpi_truncate_panel(netbios_dv(-4, hostHttps, - surgeRemote)); - ups_flowchart_plug.copy = seoTrashMatrix(unit_cache, 130102, 5); - } - matrixFile += tunneling_dram_graphic; - var alertParse = -2; - -Amnis per aede munus, colorem *semper*, non manu vera petita tamen. Lanigeris -alium victo, novantur faciem Thetidis **raptore prodere flumine** sanguisque ad -*claudit* cupidine, ut vitiorum coniungere quoque campo. +``ULID`` serves the purpose of solving several issues with `UUID` while being compatibile: + +- 1.21e+24 unique ULIDs per millisecond +- Lexicographically sortable! +- Monotonic sort order (correctly detects and handles the same millisecond) + +Most importantly it Uses Base32Crockford for better efficiency and readability. + +To create a ULID you can either use ``IdentifierFactory/createIdentifier(with:)`` on ``Identifier/factory``: + +``` +let ulid : ULID = Identifier.factory.createIdentifier(with: .parts(nil, .random(nil))) +``` + +or a constructor: + +``` +let ulid = ULID(specifications: .parts(nil, .random(nil))) +``` + +For most cases the default ``ULID/Specifications/default`` specification is sufficient. The follows the canonical spec which uses the first 6 bytes for a the timestamp and the last 10 bytes are random. Otherwise you can specify all 16 bytes with ``ULID/Specifications/data(_:)`` or specify which `Date` to use for the timestamp and the ``RandomDataGenerator`` to use for the ``ULID/randomPart`` of the data. ### References -* Crockford32 -* ULID +* [Base32 Specifications from crockford.com](https://www.crockford.com/base32.html) +* [ULID Specifications](https://github.com/ulid/spec) ### License @@ -258,3 +251,23 @@ This code is distributed under the MIT license. See the [LICENSE](https://github - ``Base32CrockfordEncodingOptions`` - ``Base32CrockfordDecodingOptions`` - ``Base32CrockfordDecodingError`` + +### Generating Identifiers + +- ``RandomDataGenerator`` +- ``SecRandomDataGenerator`` +- ``NumberedDataGenerator`` + +### Generating Identifiers + +- ``Identifier`` +- ``IdentifierFactory`` +- ``ComposableIdentifier`` +- ``AnyIdentifierSpecifications`` +- ``SizeSpecification`` + +### Identifier Types + +- ``ULID`` +- ``UDID`` + diff --git a/Sources/ThirtyTo/Array.swift b/Sources/ThirtyTo/Extensions/Array.swift similarity index 100% rename from Sources/ThirtyTo/Array.swift rename to Sources/ThirtyTo/Extensions/Array.swift diff --git a/Sources/ThirtyTo/Data.swift b/Sources/ThirtyTo/Extensions/Data.swift similarity index 56% rename from Sources/ThirtyTo/Data.swift rename to Sources/ThirtyTo/Extensions/Data.swift index 141a31e..06367d0 100644 --- a/Sources/ThirtyTo/Data.swift +++ b/Sources/ThirtyTo/Extensions/Data.swift @@ -11,11 +11,4 @@ extension Data { } return remainder } - - internal func trim(to count: Int, andPadWith fill: UInt8 = 0) -> Data { - let fillSize = Swift.max(count - self.count, 0) - let fillData = Data(repeating: fill, count: fillSize) - let bytes = (fillData + self).suffix(count) - return Data(bytes) - } } diff --git a/Sources/ThirtyTo/Extensions/Date.swift b/Sources/ThirtyTo/Extensions/Date.swift new file mode 100644 index 0000000..d139189 --- /dev/null +++ b/Sources/ThirtyTo/Extensions/Date.swift @@ -0,0 +1,26 @@ +import Foundation + +extension Date { + /// Creates a Date object based on the data object. + /// - Parameter timestampData: Data based on the unix timestamp. + public init(timestampData: Data) { + var data = timestampData + data.append(contentsOf: .init(repeating: 0, count: max(0, 8 - data.count))) + let timestamp64 = data.withUnsafeBytes { $0.load(as: UInt64.self) } + let timeInterval = TimeInterval(timestamp64.bigEndian) / 1_000.0 + self.init(timeIntervalSince1970: timeInterval) + } + + /// Create a Data object based on the number bytes specified. + /// - Parameter byteCount: The number of bytes to create the timestamp from. + /// - Returns: Data object with precision based on the number of bytes requested. + public func data(withMaximumByteCount byteCount: Int?) -> Data { + var timestamp = UInt64(timeIntervalSince1970 * 1_000).bigEndian + let data = withUnsafeBytes(of: ×tamp) { Data($0) } + guard let byteCount = byteCount else { + return data + } + assert(byteCount <= 8) + return data.prefix(byteCount) + } +} diff --git a/Sources/ThirtyTo/String.swift b/Sources/ThirtyTo/Extensions/String.swift similarity index 100% rename from Sources/ThirtyTo/String.swift rename to Sources/ThirtyTo/Extensions/String.swift diff --git a/Sources/ThirtyTo/UUID.swift b/Sources/ThirtyTo/Extensions/UUID+Data.swift similarity index 100% rename from Sources/ThirtyTo/UUID.swift rename to Sources/ThirtyTo/Extensions/UUID+Data.swift diff --git a/Sources/ThirtyTo/Factory/AnyComposableIdentifier.swift b/Sources/ThirtyTo/Factory/AnyComposableIdentifier.swift new file mode 100644 index 0000000..dc47a7d --- /dev/null +++ b/Sources/ThirtyTo/Factory/AnyComposableIdentifier.swift @@ -0,0 +1,26 @@ +import Foundation + +/// Type erased identifier object. +@available(swift, obsoleted: 5.7) +// swiftlint:disable:next type_name +public struct _AnyComposableIdentifier: ComposableIdentifier { + public typealias Specifications = () -> Never + + public let data: Data + + // periphery:ignore + internal init( + wrapped: IdentifierType + ) { + data = wrapped.data + } + + public init(specifications: () -> Never) { + specifications() + } +} + +#if swift(<5.7) + /// Deprecated type erased identifier object. + public typealias AnyComposableIdentifier = _AnyComposableIdentifier +#endif diff --git a/Sources/ThirtyTo/Factory/AnyIdentifierSpecifications.swift b/Sources/ThirtyTo/Factory/AnyIdentifierSpecifications.swift new file mode 100644 index 0000000..5951b54 --- /dev/null +++ b/Sources/ThirtyTo/Factory/AnyIdentifierSpecifications.swift @@ -0,0 +1,25 @@ +import Foundation + +/// Specifications for building an identifier. +public struct AnyIdentifierSpecifications { + internal let randomData: () -> RandomDataGenerator + internal let size: SizeSpecification + + /// Creates a specification for the indentifier. + /// - Parameter size: Specifications which calculate the size of the identifier. + public init(size: SizeSpecification) { + self.init(size: size, randomDataGenerator: Data.defaultRandomGenerator()) + } + + /// Creates a specification for the indentifier. + /// - Parameters: + /// - size: Specifications which calculate the size of the identifier. + /// - randomDataGenerator: For building the underlying Data object. + public init( + size: SizeSpecification, + randomDataGenerator: @autoclosure @escaping () -> RandomDataGenerator + ) { + randomData = randomDataGenerator + self.size = size + } +} diff --git a/Sources/ThirtyTo/Factory/ComposableIdentifier.swift b/Sources/ThirtyTo/Factory/ComposableIdentifier.swift new file mode 100644 index 0000000..ec02ace --- /dev/null +++ b/Sources/ThirtyTo/Factory/ComposableIdentifier.swift @@ -0,0 +1,15 @@ +import Foundation + +/// An identifier type which can be composed. +public protocol ComposableIdentifier { + /// Designated type which specifies how the identifier should be built. + associatedtype Specifications + + /// Underlying Data of the object. + var data: Data { get } + + /// Creates the identifier based on the specifications. + /// - Parameters: + /// - specifications: Information which can be used to construct the identifier. + init(specifications: Specifications) +} diff --git a/Sources/ThirtyTo/Factory/Identifier.swift b/Sources/ThirtyTo/Factory/Identifier.swift new file mode 100644 index 0000000..b80c195 --- /dev/null +++ b/Sources/ThirtyTo/Factory/Identifier.swift @@ -0,0 +1,33 @@ +import Foundation +/// Contains the factory to create `ComposableIdentifier` objects. +public enum Identifier { + private struct ComposableIdentifierFactory: IdentifierFactory { + func createIdentifier( + with specification: IdentifierType.Specifications + ) -> IdentifierType where IdentifierType: ComposableIdentifier { + IdentifierType(specifications: specification) + } + + #if swift(>=5.7) + func anyIdentifierWith( + _ specifications: AnyIdentifierSpecifications + ) -> any ComposableIdentifier { + UDID(specifications: specifications) + } + #else + func anyIdentifierWith( + _ specifications: AnyIdentifierSpecifications + ) -> AnyComposableIdentifier { + _AnyComposableIdentifier(wrapped: UDID(specifications: specifications)) + } + #endif + } + + /// Object for creating different types of identiifiers. + public static let factory: IdentifierFactory = ComposableIdentifierFactory() + + /// Creates the default `RandomDataGenerator` based on the operating system. + /// - Returns: For Apple OSes, this uses `SecRandomCopyBytes` + /// otherwise it uses `SystemRandomNumberGenerator`. + public static let defaultRandomGenerator = Data.defaultRandomGenerator +} diff --git a/Sources/ThirtyTo/Factory/IdentifierFactory.swift b/Sources/ThirtyTo/Factory/IdentifierFactory.swift new file mode 100644 index 0000000..edac666 --- /dev/null +++ b/Sources/ThirtyTo/Factory/IdentifierFactory.swift @@ -0,0 +1,56 @@ +/// Factory object for creating identifiers. +public protocol IdentifierFactory { + /// Creates an identifier of a speciifc type based on the specifications. + /// - Parameter specification: The specifications for the identifier. + /// - Returns: A new identifier. + func createIdentifier( + with specification: IdentifierType.Specifications + ) -> IdentifierType + + #if swift(>=5.7) + /// Creates an identifier of a speciifc type based on the specifications. + /// - Parameter specification: The specifications for the identifier. + /// - Returns: A new identifier. + func anyIdentifierWith( + _ specifications: AnyIdentifierSpecifications + ) -> any ComposableIdentifier + #else + /// Creates an identifier of a speciifc type based on the specifications. + /// - Parameter specification: The specifications for the identifier. + /// - Returns: A new identifier. + func anyIdentifierWith( + _ specifications: AnyIdentifierSpecifications + ) -> AnyComposableIdentifier + #endif +} + +extension IdentifierFactory { + #if swift(>=5.7) + /// Creates an identifier of a speciifc type based on the specifications. + /// - Parameter size: The specifications for the size of the identifier. + /// - Returns: A new identifier. + public func anyIdentifier( + withSize size: SizeSpecification + ) -> any ComposableIdentifier { + anyIdentifierWith(.init(size: size)) + } + #else + /// Creates an identifier of a speciifc type based on the specifications. + /// - Parameter size: The specifications for the size of the identifier. + /// - Returns: A new identifier. + public func anyIdentifier( + withSize size: SizeSpecification + ) -> AnyComposableIdentifier { + anyIdentifierWith(.init(size: size)) + } + #endif + + /// Creates and identifier of a specific type. + /// - Returns: A new identifier. + public func createIdentifier< + IdentifierType: ComposableIdentifier + >(_: IdentifierType.Type) -> IdentifierType + where IdentifierType.Specifications == Void { + createIdentifier(with: ()) + } +} diff --git a/Sources/ThirtyTo/Factory/SizeSpecification.swift b/Sources/ThirtyTo/Factory/SizeSpecification.swift new file mode 100644 index 0000000..99a5d12 --- /dev/null +++ b/Sources/ThirtyTo/Factory/SizeSpecification.swift @@ -0,0 +1,50 @@ +import Foundation + +/// Specifies the size of the identifier to use. +public enum SizeSpecification { + /// The exact number of bytes + case bytes(Int) + /// Based on the number of unique values and a whether it needs to be a factor. + case minimumCount(Double, factorOf: Int?) + + /// Creates a `SizeSpecification` based on the number of unique values + /// which is optimized the Base32. + /// - Parameter count: The minimum number of unique values + /// - Returns: The `SizeSpecification` + public static func base32Optimized( + forUniqueCountOf count: Double + ) -> SizeSpecification { + .minimumCount(count, factorOf: 5) + } +} + +extension SizeSpecification { + internal var byteCount: Int { + switch self { + case let .bytes(byteCount): + return byteCount + + case let .minimumCount(uniqueCount, factorOf: factor): + return Self.bytesRequired( + forUniqueCountOf: uniqueCount, + factorOf: factor + ) + } + } + + private static func bytesRequired( + forUniqueCountOf count: Double, + factorOf factor: Int? + ) -> Int { + var floatingCount = log(count) / log(256.0) + + if let factor = factor.map(Double.init) { + let remainder = floatingCount.truncatingRemainder(dividingBy: factor) + if remainder > 0 { + floatingCount += (factor - remainder) + } + } + + return Int(ceil(floatingCount)) + } +} diff --git a/Sources/ThirtyTo/Identifiers/UDID.swift b/Sources/ThirtyTo/Identifiers/UDID.swift new file mode 100644 index 0000000..ad7afe6 --- /dev/null +++ b/Sources/ThirtyTo/Identifiers/UDID.swift @@ -0,0 +1,19 @@ +import Foundation + +/// _Universally Unique Dynamic Identifier_ +/// which is a wrapper around a data object for +/// creating random bytes to be used as an identifier. +public struct UDID: ComposableIdentifier { + /// Specifies the size and method of generating random bytes. + public typealias Specifications = AnyIdentifierSpecifications + + /// The underlying data of the object. + public let data: Data + + /// Creates UDID based on the size and method of generating random bytes. + /// - Parameter specifications: The size and method of generating random bytes. + public init(specifications: AnyIdentifierSpecifications) { + var randomDataGenerator = specifications.randomData() + data = Data.random(count: specifications.size.byteCount, using: &randomDataGenerator) + } +} diff --git a/Sources/ThirtyTo/Identifiers/ULID.swift b/Sources/ThirtyTo/Identifiers/ULID.swift new file mode 100644 index 0000000..d65ef55 --- /dev/null +++ b/Sources/ThirtyTo/Identifiers/ULID.swift @@ -0,0 +1,70 @@ +import Foundation + +/// Universally Unique Lexicographically Sortable Identifier +/// +/// For more information checkout the [ULID specifications](https://github.com/ulid/spec). +public struct ULID: ComposableIdentifier { + /// Whether the _random part_ of the identifier should a specific value + /// or based on a ``RandomDataGenerator``. + public enum RandomPartSpecifications { + /// Randomly generate the data based on the ``RandomDataGenerator``. + /// - Note: If none is provided ``Identifier/defaultRandomGenerator`` is used. + case random(RandomDataGenerator?) + /// Use the specific data. + case specific(Data) + } + + /// Specifications for creating the ULID. + public enum Specifications { + /// Use the specific data. + case data(Data) + /// Use the date, (_now_ if nil) and the specifications for the _random part_. + case parts(Date?, RandomPartSpecifications) + + /// The default specifications which use the current time + /// and the ``Identifier/defaultRandomGenerator``. + public static let `default`: Specifications = .parts(nil, .random(nil)) + } + + /// The underlying data of the object. + public let data: Data + + /// The _random part_ which is the last 10 bytes. + public var randomPart: Data { + data.suffix(10) + } + + /// The timestamp part which is the first 6 bytes. + public var timestamp: Date { + Date(timestampData: data.prefix(6)) + } + + /// Creates the ULID baed on the specifications. + /// - Parameter specifications: How to build the ULID for both sections. + public init(specifications: Specifications = .default) { + switch specifications { + case let .data(data): + assert(data.count == 16) + self.data = data + + case let .parts(date, randomSpec): + let date = date ?? Date() + let randomPart: Data + switch randomSpec { + case let .random(generator): + var generator = generator ?? Data.defaultRandomGenerator() + randomPart = generator.generate(withCount: 10) + + case let .specific(data): + assert(data.count == 10) + randomPart = data + } + let fillerCount = max(10 - randomPart.count, 0) + let timestampData = date.data(withMaximumByteCount: 6) + let fillerData = Data(count: fillerCount) + let randomPartData = randomPart.prefix(10) + data = timestampData + fillerData + randomPartData + assert(data.count == 16) + } + } +} diff --git a/Sources/ThirtyTo/Identifiers/UUID+ComposableIdentifier.swift b/Sources/ThirtyTo/Identifiers/UUID+ComposableIdentifier.swift new file mode 100644 index 0000000..7b4cd05 --- /dev/null +++ b/Sources/ThirtyTo/Identifiers/UUID+ComposableIdentifier.swift @@ -0,0 +1,13 @@ +import Foundation + +extension UUID: ComposableIdentifier { + public typealias Specifications = Void + + public var data: Data { + Data(Array(uuid: self)) + } + + public init(specifications _: Void) { + self.init() + } +} diff --git a/Sources/ThirtyTo/Random/Data+Random.swift b/Sources/ThirtyTo/Random/Data+Random.swift new file mode 100644 index 0000000..6d95bc2 --- /dev/null +++ b/Sources/ThirtyTo/Random/Data+Random.swift @@ -0,0 +1,54 @@ +import Foundation + +extension Data { + /// Creates the default `RandomDataGenerator` based on the operating system. + /// - Returns: For Apple OSes, this uses `SecRandomCopyBytes` + /// otherwise it uses `SystemRandomNumberGenerator`. + public static func defaultRandomGenerator() -> RandomDataGenerator { + #if canImport(Security) + return SecRandomDataGenerator.shared + #else + return NumberedDataGenerator(generator: SystemRandomNumberGenerator()) + #endif + } + + /// Creates a Data object based on the `RandomDataGenerator` and the number of bytes. + /// - Parameters: + /// - count: The number of bytes to create. + /// - generator: The `RandomDataGenerator` + /// - Returns: `Data` object. + public static func random( + count: Int, + using generator: inout RandomDataGenerator + ) -> Data { + generator.generate(withCount: count) + } + + /// Creates a Data object based on the `RandomNumberGenerator` and the number of bytes. + /// - Parameters: + /// - count: The number of bytes to create. + /// - numberGenerator: The `RandomNumberGenerator` + /// - Returns: `Data` object. + public static func random( + count: Int, + using numberGenerator: inout T + ) -> Data where T: RandomNumberGenerator { + let numberGenerator = NumberedDataGenerator(generator: numberGenerator) + var dataGenerator: RandomDataGenerator = numberGenerator + return Self.random(count: count, using: &dataGenerator) + } + + /// Creates a Data object based on the default `RandomDataGenerator` + /// and the number of bytes. + /// - Parameters: + /// - count: The number of bytes to create. + /// - Returns: `Data` object. + public static func random(count: Int) -> Data { + #if canImport(Security) + return SecRandomDataGenerator.shared.generate(withCount: count) + #else + var numberGenerator = SystemRandomNumberGenerator() + return random(count: count, using: &numberGenerator) + #endif + } +} diff --git a/Sources/ThirtyTo/Random/NumberedDataGenerator.swift b/Sources/ThirtyTo/Random/NumberedDataGenerator.swift new file mode 100644 index 0000000..ea6fd96 --- /dev/null +++ b/Sources/ThirtyTo/Random/NumberedDataGenerator.swift @@ -0,0 +1,25 @@ +import Foundation + +/// Wrapper for `RandomNumberGenerator` to generate `Data`. +public struct NumberedDataGenerator< + NumberGeneratorType: RandomNumberGenerator +>: RandomDataGenerator { + private var generator: NumberGeneratorType + + /// Creates a ``RandomDataGenerator`` from a `RandomNumberGenerator`. + /// - Parameter generator: The `RandomNumberGenerator`. + public init(generator: NumberGeneratorType) { + self.generator = generator + } + + /// Creates a new uniformly distributed random data object. + /// - Parameter count: The number of bytes to fill randomly. + /// - Returns: New Data object with random bytes. + public mutating func generate(withCount count: Int) -> Data { + var data = Data(capacity: count) + while data.count < count { + data.append(generator.next()) + } + return data + } +} diff --git a/Sources/ThirtyTo/Random/RandomDataGenerator.swift b/Sources/ThirtyTo/Random/RandomDataGenerator.swift new file mode 100644 index 0000000..80b2ae8 --- /dev/null +++ b/Sources/ThirtyTo/Random/RandomDataGenerator.swift @@ -0,0 +1,9 @@ +import Foundation + +/// A type that provides uniformly distributed random data. +public protocol RandomDataGenerator { + /// Creates a new uniformly distributed random data object. + /// - Parameter count: The number of bytes to fill randomly. + /// - Returns: New Data object with random bytes. + mutating func generate(withCount count: Int) -> Data +} diff --git a/Sources/ThirtyTo/Random/SecRandomDataGenerator.swift b/Sources/ThirtyTo/Random/SecRandomDataGenerator.swift new file mode 100644 index 0000000..c37c6e4 --- /dev/null +++ b/Sources/ThirtyTo/Random/SecRandomDataGenerator.swift @@ -0,0 +1,31 @@ +#if canImport(Security) + import Foundation + import Security + + /// Uses `SecRandomCopyBytes` to generate a random `Data` object. + public struct SecRandomDataGenerator: RandomDataGenerator { + /// ``SecRandomDataGenerator`` does not need to be created. + /// Just use this static instance. + public static let shared = SecRandomDataGenerator() + + private init() {} + + /// Creates a new uniformly distributed random data object. + /// - Parameter count: The number of bytes to fill randomly. + /// - Returns: New Data object with random bytes. + public func generate(withCount count: Int) -> Data { + var bytes = [Int8](repeating: 0, count: count) + + // Fill bytes with secure random data + let status = SecRandomCopyBytes( + kSecRandomDefault, + count, + &bytes + ) + + assert(status == errSecSuccess) + // Convert bytes to Data + return Data(bytes: bytes, count: count) + } + } +#endif diff --git a/Tests/ThirtyToTests/AnyComposableIdentifierTests.swift b/Tests/ThirtyToTests/AnyComposableIdentifierTests.swift new file mode 100644 index 0000000..b6581f4 --- /dev/null +++ b/Tests/ThirtyToTests/AnyComposableIdentifierTests.swift @@ -0,0 +1,30 @@ +@testable import ThirtyTo +import XCTest + +#if swift(<5.7) + final class AnyComposableIdentifierTests: XCTestCase { + private func unreachable() -> Never { + repeat { + RunLoop.current.run() + } while true + } + + func testNever() { + let expectation = expectation(description: "`Never` called.") + DispatchQueue.global(qos: .userInitiated).async { + _ = _AnyComposableIdentifier { + expectation.fulfill() + self.unreachable() + } + } + + waitForExpectations(timeout: 0.1) { _ in } + } + + func testWrapped() { + let udid = UDID(specifications: .init(size: .bytes(12))) + let identifier = _AnyComposableIdentifier(wrapped: udid) + XCTAssertEqual(udid.data, identifier.data) + } + } +#endif diff --git a/Tests/ThirtyToTests/Base32CrockfordTests.swift b/Tests/ThirtyToTests/Base32CrockfordTests.swift index 31ba25f..918d5af 100644 --- a/Tests/ThirtyToTests/Base32CrockfordTests.swift +++ b/Tests/ThirtyToTests/Base32CrockfordTests.swift @@ -119,7 +119,7 @@ final class Base32CrockfordTests: XCTestCase { } func testInvalidCharacter() { - let invalidCharacters = "!@#$%^&*()_-+{}[]:;<>,./?" + let invalidCharacters = "!@#$%^&*()_+{}[]:;<>,./?" let expectedCharacter = invalidCharacters.randomElement()! let badString = String("0123456789ABCDEFGHJKMNPQRSTVWXYZ".shuffled() + [expectedCharacter]) XCTAssertEqual(badString.count, 33) diff --git a/Tests/ThirtyToTests/Data.swift b/Tests/ThirtyToTests/Extensions/Data.swift similarity index 59% rename from Tests/ThirtyToTests/Data.swift rename to Tests/ThirtyToTests/Extensions/Data.swift index 6ba6fd8..ec4c1fc 100644 --- a/Tests/ThirtyToTests/Data.swift +++ b/Tests/ThirtyToTests/Extensions/Data.swift @@ -1,4 +1,3 @@ -@testable import ThirtyTo import XCTest extension Data { @@ -11,4 +10,11 @@ extension Data { let format = options.contains(.upperCase) ? "%02hhX" : "%02hhx" return map { String(format: format, $0) }.joined() } + + func trim(to count: Int, andPadWith fill: UInt8 = 0) -> Data { + let fillSize = Swift.max(count - self.count, 0) + let fillData = Data(repeating: fill, count: fillSize) + let bytes = (fillData + self).suffix(count) + return Data(bytes) + } } diff --git a/Tests/ThirtyToTests/Extensions/MockNumberGenerator.swift b/Tests/ThirtyToTests/Extensions/MockNumberGenerator.swift new file mode 100644 index 0000000..64b2a16 --- /dev/null +++ b/Tests/ThirtyToTests/Extensions/MockNumberGenerator.swift @@ -0,0 +1,12 @@ +import Foundation +import ThirtyTo + +class MockNumberGenerator: RandomNumberGenerator { + var callCount = 0 + func next() -> UInt64 { + defer { + callCount += 1 + } + return 12 + } +} diff --git a/Tests/ThirtyToTests/Extensions/MockRandomGenerator.swift b/Tests/ThirtyToTests/Extensions/MockRandomGenerator.swift new file mode 100644 index 0000000..8b8e0ab --- /dev/null +++ b/Tests/ThirtyToTests/Extensions/MockRandomGenerator.swift @@ -0,0 +1,12 @@ +import Foundation +import ThirtyTo + +class MockRandomGenerator: RandomDataGenerator { + var callCount = 0 + func generate(withCount count: Int) -> Data { + defer { + callCount += 1 + } + return Data(count: count) + } +} diff --git a/Tests/ThirtyToTests/String.swift b/Tests/ThirtyToTests/Extensions/String.swift similarity index 95% rename from Tests/ThirtyToTests/String.swift rename to Tests/ThirtyToTests/Extensions/String.swift index d0afba5..8225ed3 100644 --- a/Tests/ThirtyToTests/String.swift +++ b/Tests/ThirtyToTests/Extensions/String.swift @@ -1,5 +1,3 @@ -import ThirtyTo - extension String { func randomDashes() -> String { var result = self diff --git a/Tests/ThirtyToTests/UInt128+Data.swift b/Tests/ThirtyToTests/Extensions/UInt128+Data.swift similarity index 100% rename from Tests/ThirtyToTests/UInt128+Data.swift rename to Tests/ThirtyToTests/Extensions/UInt128+Data.swift diff --git a/Tests/ThirtyToTests/UInt128.swift b/Tests/ThirtyToTests/Extensions/UInt128.swift similarity index 100% rename from Tests/ThirtyToTests/UInt128.swift rename to Tests/ThirtyToTests/Extensions/UInt128.swift diff --git a/Tests/ThirtyToTests/IdentifierTests.swift b/Tests/ThirtyToTests/IdentifierTests.swift new file mode 100644 index 0000000..5907840 --- /dev/null +++ b/Tests/ThirtyToTests/IdentifierTests.swift @@ -0,0 +1,50 @@ +@testable import ThirtyTo +import XCTest + +final class IdentifierTests: XCTestCase { + public func testFactory() { + let identifier = Identifier.factory.anyIdentifier(withSize: .bytes(12)) + + XCTAssertEqual(identifier.data.count, 12) + } + + public func testFactoryWithGenerator() { + let generator = MockRandomGenerator() + let identifierSpecs = AnyIdentifierSpecifications(size: .bytes(12), randomDataGenerator: generator) + let identifier = Identifier.factory.anyIdentifierWith(identifierSpecs) + + XCTAssertEqual(identifier.data.count, 12) + XCTAssertEqual(generator.callCount, 1) + } + + public func testFactoryUsingNumberedDataGenerator() { + let generator = NumberedDataGenerator(generator: SystemRandomNumberGenerator()) + let identifierSpecs = AnyIdentifierSpecifications(size: .bytes(102), randomDataGenerator: generator) + let identifier = Identifier.factory.anyIdentifierWith(identifierSpecs) + + XCTAssertEqual(identifier.data.count, 102) + } + + func testUUID() { + let identifier = Identifier.factory.createIdentifier(UUID.self) + XCTAssertEqual(identifier.data.count, 16) + } + + func testUDID() { + let generator = MockRandomGenerator() + func runTestOn(_ minimumUniqueCount: Double, _ factor: Int?, _ expectedByteSize: Int) { + let specs = AnyIdentifierSpecifications(size: SizeSpecification.minimumCount(minimumUniqueCount, factorOf: factor), randomDataGenerator: generator) + let identifier: UDID = Identifier.factory.createIdentifier(with: specs) + XCTAssertEqual(identifier.data.count, expectedByteSize) + } + + runTestOn(1_000, nil, 2) + runTestOn(1_000_000, nil, 3) + runTestOn(1_000, 2, 2) + runTestOn(1_000_000, 2, 4) + runTestOn(1_000_000_000, 2, 4) + runTestOn(2.93874e+25, nil, 11) + runTestOn(2.93874e+25, 4, 12) + XCTAssertEqual(generator.callCount, 7) + } +} diff --git a/Tests/ThirtyToTests/RandomDataTests.swift b/Tests/ThirtyToTests/RandomDataTests.swift new file mode 100644 index 0000000..ed45bff --- /dev/null +++ b/Tests/ThirtyToTests/RandomDataTests.swift @@ -0,0 +1,29 @@ +@testable import ThirtyTo +import XCTest + +final class RandomDataTests: XCTestCase { + func testRandomGenerator() { + let count: Int = .random(in: 100 ... 200) + let mockGenerator = MockRandomGenerator() + var generator: RandomDataGenerator = mockGenerator + + let data = Data.random(count: count, using: &generator) + XCTAssertEqual(mockGenerator.callCount, 1) + XCTAssertEqual(count, data.count) + } + + func testRandomDefault() { + let count = Int.random(in: 100 ... 200) + let data = Data.random(count: count) + XCTAssertEqual(count, data.count) + } + + func testNumberGenerator() { + let count: Int = .random(in: 100 ... 200) + var mockGenerator = MockNumberGenerator() + + let data = Data.random(count: count, using: &mockGenerator) + XCTAssertEqual(mockGenerator.callCount, count) + XCTAssertEqual(count, data.count) + } +} diff --git a/Tests/ThirtyToTests/SizeSpecificationTests.swift b/Tests/ThirtyToTests/SizeSpecificationTests.swift new file mode 100644 index 0000000..cdb31d1 --- /dev/null +++ b/Tests/ThirtyToTests/SizeSpecificationTests.swift @@ -0,0 +1,37 @@ +@testable import ThirtyTo +import XCTest + +final class SizeSpecificationTests: XCTestCase { + func testBytes() { + let count: Int = .random(in: 1 ... 1_000) + let specs = SizeSpecification.bytes(count) + XCTAssertEqual(specs.byteCount, count) + } + + func testMinimumCount() { + let count: Int = .random(in: 1 ... 1_000) + let specs = SizeSpecification.bytes(count) + XCTAssertEqual(specs.byteCount, count) + } + + fileprivate func runTestOn(_ minimumUniqueCount: Double, _ factor: Int?, _ expectedByteSize: Int) { + let specs = SizeSpecification.minimumCount(minimumUniqueCount, factorOf: factor) + XCTAssertEqual(specs.byteCount, expectedByteSize) + } + + func testMinimumCountWithFactor() { + runTestOn(1_000, nil, 2) + runTestOn(1_000_000, nil, 3) + runTestOn(1_000, 2, 2) + runTestOn(1_000_000, 2, 4) + runTestOn(1_000_000_000, 2, 4) + runTestOn(2.93874e+25, nil, 11) + runTestOn(2.93874e+25, 4, 12) + } + + func testMinimumCountForBase32() { + XCTAssertEqual(SizeSpecification.base32Optimized(forUniqueCountOf: 1_000).byteCount, 5) + XCTAssertEqual(SizeSpecification.base32Optimized(forUniqueCountOf: 1_000_000).byteCount, 5) + XCTAssertEqual(SizeSpecification.base32Optimized(forUniqueCountOf: 2.93874e+25).byteCount, 15) + } +} diff --git a/Tests/ThirtyToTests/TimestampDataTests.swift b/Tests/ThirtyToTests/TimestampDataTests.swift new file mode 100644 index 0000000..87d6159 --- /dev/null +++ b/Tests/ThirtyToTests/TimestampDataTests.swift @@ -0,0 +1,18 @@ +import XCTest + +final class TimestampDataTests: XCTestCase { + func testDateData() { + let date = Date() + + var size = 8 + var expectedDifference = 0.001 + repeat { + let data = date.data(withMaximumByteCount: size) + let actualDate = Date(timestampData: data) + let difference = abs(actualDate.timeIntervalSince(date)) + XCTAssertLessThan(difference, expectedDifference) + size -= 1 + expectedDifference *= 256 + } while size >= 1 + } +} diff --git a/Tests/ThirtyToTests/ULIDTests.swift b/Tests/ThirtyToTests/ULIDTests.swift new file mode 100644 index 0000000..3cbe828 --- /dev/null +++ b/Tests/ThirtyToTests/ULIDTests.swift @@ -0,0 +1,34 @@ +import ThirtyTo +import XCTest + +final class ULIDTests: XCTestCase { + func testBasic() throws { + _ = ULID() + } + + func testData() { + let bytes = Array(uuid: .init()) + let data: Data = .init(bytes) + let ulid = ULID(specifications: .data(data)) + + XCTAssertEqual(data, ulid.data) + } + + func testParts() { + let date = Date(timeIntervalSince1970: .random(in: 1_000_000 ... 2_000_000)) + let bytes = Array(uuid: .init()) + let data: Data = .init(bytes) + let expectedDataPart = data.prefix(10) + let timestampPart = date.data(withMaximumByteCount: 6) + + let ulid = ULID(specifications: .parts(date, .specific(expectedDataPart))) + + let ulidData = ulid.data + + XCTAssertEqual(ulidData.prefix(6), timestampPart) + XCTAssertEqual(ulidData.suffix(10), expectedDataPart) + + XCTAssertEqual(ulid.timestamp.timeIntervalSince1970, date.timeIntervalSince1970, accuracy: 100) + XCTAssertEqual(ulid.randomPart, expectedDataPart) + } +} From 2254d9427a2134e60812f5f5ba643c5b13171e0d Mon Sep 17 00:00:00 2001 From: leogdion Date: Sat, 7 Jan 2023 22:36:12 -0500 Subject: [PATCH 7/7] Update .swiftformat --- .swiftformat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.swiftformat b/.swiftformat index d9131d4..11b4164 100644 --- a/.swiftformat +++ b/.swiftformat @@ -4,4 +4,4 @@ --disable wrapMultilineStatementBraces --extensionacl on-declarations --decimalgrouping 3,4 ---exclude .build, DerivedData, _archive +--exclude .build, DerivedData \ No newline at end of file