Repository: foldynl/QLog Branch: master Commit: 855447b13e47 Files: 569 Total size: 10.5 MB Directory structure: gitextract_54fyuzui/ ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── 01-Bug Report.yml │ │ ├── 02-Feature Request.yml │ │ └── config.yml │ └── workflows/ │ ├── c-cpp.yml │ └── macOSBuild.yml ├── .gitignore ├── .gitmodules ├── AUTHORS ├── CONTRIBUTING.md ├── Changelog ├── LICENSE ├── QLog.pro ├── README.md ├── awards/ │ ├── AwardDXCC.cpp │ ├── AwardDXCC.h │ ├── AwardDefinition.cpp │ ├── AwardDefinition.h │ ├── AwardGridsquare.cpp │ ├── AwardGridsquare.h │ ├── AwardIOTA.cpp │ ├── AwardIOTA.h │ ├── AwardITU.cpp │ ├── AwardITU.h │ ├── AwardJapan.cpp │ ├── AwardJapan.h │ ├── AwardNZ.cpp │ ├── AwardNZ.h │ ├── AwardPOTAActivator.cpp │ ├── AwardPOTAActivator.h │ ├── AwardPOTAHunter.cpp │ ├── AwardPOTAHunter.h │ ├── AwardRDA.cpp │ ├── AwardRDA.h │ ├── AwardSOTA.cpp │ ├── AwardSOTA.h │ ├── AwardSpanishDME.cpp │ ├── AwardSpanishDME.h │ ├── AwardUKD.cpp │ ├── AwardUKD.h │ ├── AwardUSCounty.cpp │ ├── AwardUSCounty.h │ ├── AwardWAC.cpp │ ├── AwardWAC.h │ ├── AwardWAS.cpp │ ├── AwardWAS.h │ ├── AwardWAZ.cpp │ ├── AwardWAZ.h │ ├── AwardWPX.cpp │ ├── AwardWPX.h │ ├── AwardWWFF.cpp │ ├── AwardWWFF.h │ ├── BandTableAward.cpp │ ├── BandTableAward.h │ ├── SecondarySubdivisionAward.cpp │ └── SecondarySubdivisionAward.h ├── core/ │ ├── AlertEvaluator.cpp │ ├── AlertEvaluator.h │ ├── AppGuard.cpp │ ├── AppGuard.h │ ├── CallbookManager.cpp │ ├── CallbookManager.h │ ├── CredentialStore.cpp │ ├── CredentialStore.h │ ├── EmergencyFrequency.cpp │ ├── EmergencyFrequency.h │ ├── FileCompressor.cpp │ ├── FileCompressor.h │ ├── FldigiTCPServer.cpp │ ├── FldigiTCPServer.h │ ├── LOVDownloader.cpp │ ├── LOVDownloader.h │ ├── LogDatabase.cpp │ ├── LogDatabase.h │ ├── LogLocale.cpp │ ├── LogLocale.h │ ├── LogParam.cpp │ ├── LogParam.h │ ├── MembershipQE.cpp │ ├── MembershipQE.h │ ├── Migration.cpp │ ├── Migration.h │ ├── NetworkNotification.cpp │ ├── NetworkNotification.h │ ├── PasswordCipher.cpp │ ├── PasswordCipher.h │ ├── PlatformParameterManager.cpp │ ├── PlatformParameterManager.h │ ├── PotaQE.cpp │ ├── PotaQE.h │ ├── PropConditions.cpp │ ├── PropConditions.h │ ├── QSLPrintLabelRenderer.cpp │ ├── QSLPrintLabelRenderer.h │ ├── QSLStorage.cpp │ ├── QSLStorage.h │ ├── QSOFilterManager.cpp │ ├── QSOFilterManager.h │ ├── QuadKeyCache.h │ ├── WsjtxUDPReceiver.cpp │ ├── WsjtxUDPReceiver.h │ ├── csv.hpp │ ├── debug.cpp │ ├── debug.h │ ├── main.cpp │ ├── zonedetect.c │ └── zonedetect.h ├── cwkey/ │ ├── CWKeyer.cpp │ ├── CWKeyer.h │ └── drivers/ │ ├── CWCatKey.cpp │ ├── CWCatKey.h │ ├── CWDaemonKey.cpp │ ├── CWDaemonKey.h │ ├── CWDummyKey.cpp │ ├── CWDummyKey.h │ ├── CWFldigiKey.cpp │ ├── CWFldigiKey.h │ ├── CWKey.cpp │ ├── CWKey.h │ ├── CWWinKey.cpp │ └── CWWinKey.h ├── data/ │ ├── Accents.cpp │ ├── ActivityProfile.cpp │ ├── ActivityProfile.h │ ├── AntProfile.cpp │ ├── AntProfile.h │ ├── Band.h │ ├── BandPlan.cpp │ ├── BandPlan.h │ ├── CWKeyProfile.cpp │ ├── CWKeyProfile.h │ ├── CWShortcutProfile.cpp │ ├── CWShortcutProfile.h │ ├── Callsign.cpp │ ├── Callsign.h │ ├── Data.cpp │ ├── Data.h │ ├── DxServerString.cpp │ ├── DxServerString.h │ ├── DxSpot.h │ ├── Dxcc.h │ ├── Gridsquare.cpp │ ├── Gridsquare.h │ ├── HostsPortString.cpp │ ├── HostsPortString.h │ ├── MainLayoutProfile.cpp │ ├── MainLayoutProfile.h │ ├── POTAEntity.h │ ├── POTASpot.h │ ├── ProfileManager.h │ ├── RigProfile.cpp │ ├── RigProfile.h │ ├── RotProfile.cpp │ ├── RotProfile.h │ ├── RotUsrButtonsProfile.cpp │ ├── RotUsrButtonsProfile.h │ ├── SOTAEntity.h │ ├── SerialPort.cpp │ ├── SerialPort.h │ ├── SpotAlert.h │ ├── StationProfile.cpp │ ├── StationProfile.h │ ├── ToAllSpot.h │ ├── UpdatableSQLRecord.cpp │ ├── UpdatableSQLRecord.h │ ├── WCYSpot.h │ ├── WWFFEntity.h │ ├── WWVSpot.h │ ├── WsjtxDecode.h │ ├── WsjtxEntry.h │ ├── WsjtxLog.h │ ├── WsjtxLogADIF.h │ └── WsjtxStatus.h ├── debian/ │ ├── changelog │ ├── control │ ├── copyright │ ├── rules │ ├── source/ │ │ └── format │ └── watch ├── devtools/ │ ├── ADIF/ │ │ ├── all_ADIF_fields_example.adi │ │ ├── min_ADIF_fields_example.adi │ │ └── test_intl.adi │ ├── TCISimulator/ │ │ ├── client.html │ │ └── server.py │ ├── cabrillo/ │ │ └── generate_cabrillo_templates.py │ ├── deployment/ │ │ └── finalize_release.sh │ ├── timezones/ │ │ ├── README.md │ │ ├── builder/ │ │ │ ├── builder.cpp │ │ │ ├── buildspec.yml │ │ │ ├── makedb.sh │ │ │ └── zoneToAlpha.h │ │ ├── country_DATA_LICENSE │ │ └── timezone_DATA_LICENSE │ ├── translit/ │ │ └── generate.py │ └── windowsMake/ │ ├── make.bat │ └── tests.bat ├── doc/ │ └── UDP_Notifications.txt ├── entitlements.xml ├── i18n/ │ ├── datastrings.tri │ ├── dbstrings.tri │ ├── i18n.qrc │ ├── qlog_cs.qm │ ├── qlog_cs.ts │ ├── qlog_de.qm │ ├── qlog_de.ts │ ├── qlog_es.qm │ ├── qlog_es.ts │ ├── qlog_fr.qm │ ├── qlog_fr.ts │ ├── qlog_it.qm │ ├── qlog_it.ts │ ├── qlog_zh_CN.qm │ └── qlog_zh_CN.ts ├── installer/ │ ├── config/ │ │ └── config.xml │ └── packages/ │ └── de.dl2ic.qlog/ │ └── meta/ │ ├── installscript.qs │ ├── package.xml │ └── targetwidget.ui ├── logformat/ │ ├── AdiFormat.cpp │ ├── AdiFormat.h │ ├── AdxFormat.cpp │ ├── AdxFormat.h │ ├── CSVFormat.cpp │ ├── CSVFormat.h │ ├── CabrilloFormat.cpp │ ├── CabrilloFormat.h │ ├── JsonFormat.cpp │ ├── JsonFormat.h │ ├── LogFormat.cpp │ ├── LogFormat.h │ ├── PotaAdiFormat.cpp │ └── PotaAdiFormat.h ├── models/ │ ├── AlertTableModel.cpp │ ├── AlertTableModel.h │ ├── AwardsTableModel.cpp │ ├── AwardsTableModel.h │ ├── DxccTableModel.cpp │ ├── DxccTableModel.h │ ├── LogbookModel.cpp │ ├── LogbookModel.h │ ├── RigTypeModel.cpp │ ├── RigTypeModel.h │ ├── RotTypeModel.cpp │ ├── RotTypeModel.h │ ├── SearchFilterProxyModel.cpp │ ├── SearchFilterProxyModel.h │ ├── ShortcutEditorModel.cpp │ ├── ShortcutEditorModel.h │ ├── SqlListModel.cpp │ ├── SqlListModel.h │ ├── WsjtxTableModel.cpp │ └── WsjtxTableModel.h ├── res/ │ ├── data/ │ │ ├── contests.json │ │ ├── dxcc.json │ │ ├── legacy_modes.json │ │ ├── propagation_modes.json │ │ └── sat_modes.json │ ├── flags/ │ │ └── flags.qrc │ ├── icons/ │ │ └── icons.qrc │ ├── io.github.foldynl.QLog.metainfo.xml │ ├── map/ │ │ └── onlinemap.html │ ├── qlog.1 │ ├── qlog.desktop │ ├── qlog.icns │ ├── res.qrc │ ├── sql/ │ │ ├── contests.sql │ │ ├── migration_001.sql │ │ ├── migration_002.sql │ │ ├── migration_003.sql │ │ ├── migration_004.sql │ │ ├── migration_005.sql │ │ ├── migration_006.sql │ │ ├── migration_007.sql │ │ ├── migration_008.sql │ │ ├── migration_009.sql │ │ ├── migration_010.sql │ │ ├── migration_011.sql │ │ ├── migration_012.sql │ │ ├── migration_013.sql │ │ ├── migration_014.sql │ │ ├── migration_015.sql │ │ ├── migration_016.sql │ │ ├── migration_017.sql │ │ ├── migration_018.sql │ │ ├── migration_019.sql │ │ ├── migration_020.sql │ │ ├── migration_021.sql │ │ ├── migration_022.sql │ │ ├── migration_023.sql │ │ ├── migration_024.sql │ │ ├── migration_025.sql │ │ ├── migration_026.sql │ │ ├── migration_027.sql │ │ ├── migration_028.sql │ │ ├── migration_029.sql │ │ ├── migration_030.sql │ │ ├── migration_031.sql │ │ ├── migration_032.sql │ │ ├── migration_033.sql │ │ ├── migration_034.sql │ │ ├── migration_035.sql │ │ ├── migration_036.sql │ │ ├── migration_037.sql │ │ └── migration_038.sql │ └── stylesheet.css ├── rig/ │ ├── Rig.cpp │ ├── Rig.h │ ├── RigCaps.cpp │ ├── RigCaps.h │ ├── RigctldManager.cpp │ ├── RigctldManager.h │ ├── drivers/ │ │ ├── FlrigRigDrv.cpp │ │ ├── FlrigRigDrv.h │ │ ├── GenericRigDrv.cpp │ │ ├── GenericRigDrv.h │ │ ├── HamlibRigDrv.cpp │ │ ├── HamlibRigDrv.h │ │ ├── OmniRigEventSink.h │ │ ├── OmnirigRigDrv.cpp │ │ ├── OmnirigRigDrv.h │ │ ├── Omnirigv2RigDrv.cpp │ │ ├── Omnirigv2RigDrv.h │ │ ├── TCIRigDrv.cpp │ │ └── TCIRigDrv.h │ └── macros.h ├── rotator/ │ ├── RotCaps.cpp │ ├── RotCaps.h │ ├── Rotator.cpp │ ├── Rotator.h │ └── drivers/ │ ├── GenericRotDrv.cpp │ ├── GenericRotDrv.h │ ├── HamlibRotDrv.cpp │ ├── HamlibRotDrv.h │ ├── PSTRotDrv.cpp │ └── PSTRotDrv.h ├── rpm_spec/ │ └── qlog.spec ├── service/ │ ├── GenericCallbook.cpp │ ├── GenericCallbook.h │ ├── GenericQSLDownloader.cpp │ ├── GenericQSLDownloader.h │ ├── GenericQSOUploader.cpp │ ├── GenericQSOUploader.h │ ├── cloudlog/ │ │ ├── Cloudlog.cpp │ │ └── Cloudlog.h │ ├── clublog/ │ │ ├── ClubLog.cpp │ │ └── ClubLog.h │ ├── eqsl/ │ │ ├── Eqsl.cpp │ │ └── Eqsl.h │ ├── hamqth/ │ │ ├── HamQTH.cpp │ │ └── HamQTH.h │ ├── hrdlog/ │ │ ├── HRDLog.cpp │ │ └── HRDLog.h │ ├── kstchat/ │ │ ├── KSTChat.cpp │ │ └── KSTChat.h │ ├── lotw/ │ │ ├── Lotw.cpp │ │ └── Lotw.h │ ├── potaapp/ │ │ ├── PotaApp.cpp │ │ └── PotaApp.h │ └── qrzcom/ │ ├── QRZ.cpp │ └── QRZ.h ├── tests/ │ ├── AlertEvaluatorTest/ │ │ ├── AlertEvaluatorTest.pro │ │ └── tst_alertevaluator.cpp │ ├── BandPlanTest/ │ │ ├── BandPlanTest.pro │ │ └── tst_bandplan.cpp │ ├── CallsignTest/ │ │ ├── CallsignTest.pro │ │ ├── generated_cases.h │ │ └── tst_callsign.cpp │ ├── CredentialStoreTest/ │ │ ├── CredentialStoreTest.pro │ │ ├── test_stubs.cpp │ │ └── tst_credentialstore.cpp │ ├── DataTest/ │ │ ├── DataTest.pro │ │ └── tst_data.cpp │ ├── DxServerStringTest/ │ │ ├── DxServerStringTest.pro │ │ └── tst_dxserverstring.cpp │ ├── FileCompressorTest/ │ │ ├── FileCompressorTest.pro │ │ └── tst_filecompressor.cpp │ ├── GridsquareTest/ │ │ ├── GridsquareTest.pro │ │ └── tst_gridsquare.cpp │ ├── HostsPortStringTest/ │ │ ├── HostsPortStringTest.pro │ │ └── tst_hostsportstring.cpp │ ├── MigrationTest/ │ │ ├── MigrationTest.pro │ │ └── tst_migration.cpp │ ├── PasswordCipherTest/ │ │ ├── PasswordCipherTest.pro │ │ └── tst_passwordcipher.cpp │ ├── QuadKeyCacheTest/ │ │ ├── QuadKeyCacheTest.pro │ │ └── tst_quadkeycache.cpp │ ├── RigctldManagerTest/ │ │ ├── RigctldManagerTest.pro │ │ ├── core/ │ │ │ └── debug.h │ │ ├── data/ │ │ │ └── RigProfile.h │ │ ├── test_stubs.cpp │ │ └── tst_rigctldmanager.cpp │ └── tests.pro └── ui/ ├── ActivityEditor.cpp ├── ActivityEditor.h ├── ActivityEditor.ui ├── AlertRuleDetail.cpp ├── AlertRuleDetail.h ├── AlertRuleDetail.ui ├── AlertSettingDialog.cpp ├── AlertSettingDialog.h ├── AlertSettingDialog.ui ├── AlertWidget.cpp ├── AlertWidget.h ├── AlertWidget.ui ├── AwardsDialog.cpp ├── AwardsDialog.h ├── AwardsDialog.ui ├── BandmapWidget.cpp ├── BandmapWidget.h ├── BandmapWidget.ui ├── CWConsoleWidget.cpp ├── CWConsoleWidget.h ├── CWConsoleWidget.ui ├── CabrilloExportDialog.cpp ├── CabrilloExportDialog.h ├── CabrilloExportDialog.ui ├── CabrilloTemplateDialog.cpp ├── CabrilloTemplateDialog.h ├── CabrilloTemplateDialog.ui ├── ChatWidget.cpp ├── ChatWidget.h ├── ChatWidget.ui ├── ClockWidget.cpp ├── ClockWidget.h ├── ClockWidget.ui ├── ColumnSettingDialog.cpp ├── ColumnSettingDialog.h ├── ColumnSettingDialog.ui ├── ColumnSettingSimpleDialog.ui ├── DXCCSubmissionDialog.cpp ├── DXCCSubmissionDialog.h ├── DXCCSubmissionDialog.ui ├── DevToolsDialog.cpp ├── DevToolsDialog.h ├── DevToolsDialog.ui ├── DownloadQSLDialog.cpp ├── DownloadQSLDialog.h ├── DownloadQSLDialog.ui ├── DxFilterDialog.cpp ├── DxFilterDialog.h ├── DxFilterDialog.ui ├── DxWidget.cpp ├── DxWidget.h ├── DxWidget.ui ├── DxccTableWidget.cpp ├── DxccTableWidget.h ├── EditActivitiesDialog.cpp ├── EditActivitiesDialog.h ├── EditActivitiesDialog.ui ├── ExportDialog.cpp ├── ExportDialog.h ├── ExportDialog.ui ├── ExportPasswordDialog.cpp ├── ExportPasswordDialog.h ├── ExportPasswordDialog.ui ├── ImportDialog.cpp ├── ImportDialog.h ├── ImportDialog.ui ├── InputPasswordDialog.cpp ├── InputPasswordDialog.h ├── InputPasswordDialog.ui ├── KSTChatWidget.cpp ├── KSTChatWidget.h ├── KSTChatWidget.ui ├── KSTHighlightRuleDetail.cpp ├── KSTHighlightRuleDetail.h ├── KSTHighlightRuleDetail.ui ├── KSTHighlighterSettingDialog.cpp ├── KSTHighlighterSettingDialog.h ├── KSTHighlighterSettingDialog.ui ├── KSTWidget.cpp ├── KSTWidget.h ├── KSTWidget.ui ├── LoadDatabaseDialog.cpp ├── LoadDatabaseDialog.h ├── LoadDatabaseDialog.ui ├── LogbookWidget.cpp ├── LogbookWidget.h ├── LogbookWidget.ui ├── MainWindow.cpp ├── MainWindow.h ├── MainWindow.ui ├── MapWebChannelHandler.cpp ├── MapWebChannelHandler.h ├── MapWidget.cpp ├── MapWidget.h ├── ModeSelectionController.cpp ├── ModeSelectionController.h ├── NewContactWidget.cpp ├── NewContactWidget.h ├── NewContactWidget.ui ├── OnlineMapWidget.cpp ├── OnlineMapWidget.h ├── PaperQSLDialog.cpp ├── PaperQSLDialog.h ├── PaperQSLDialog.ui ├── PlatformSettingsDialog.cpp ├── PlatformSettingsDialog.h ├── PlatformSettingsDialog.ui ├── ProfileImageWidget.cpp ├── ProfileImageWidget.h ├── ProfileImageWidget.ui ├── QSLGalleryDialog.cpp ├── QSLGalleryDialog.h ├── QSLGalleryDialog.ui ├── QSLImportStatDialog.cpp ├── QSLImportStatDialog.h ├── QSLImportStatDialog.ui ├── QSLPrintLabelDialog.cpp ├── QSLPrintLabelDialog.h ├── QSLPrintLabelDialog.ui ├── QSODetailDialog.cpp ├── QSODetailDialog.h ├── QSODetailDialog.ui ├── QSOFilterDetail.cpp ├── QSOFilterDetail.h ├── QSOFilterDetail.ui ├── QSOFilterDialog.cpp ├── QSOFilterDialog.h ├── QSOFilterDialog.ui ├── QTableQSOView.cpp ├── QTableQSOView.h ├── RigWidget.cpp ├── RigWidget.h ├── RigWidget.ui ├── RigctldAdvancedDialog.cpp ├── RigctldAdvancedDialog.h ├── RigctldAdvancedDialog.ui ├── RotatorWidget.cpp ├── RotatorWidget.h ├── RotatorWidget.ui ├── SettingsDialog.cpp ├── SettingsDialog.h ├── SettingsDialog.ui ├── ShowUploadDialog.cpp ├── ShowUploadDialog.h ├── ShowUploadDialog.ui ├── SplashScreen.h ├── StatisticsWidget.cpp ├── StatisticsWidget.h ├── StatisticsWidget.ui ├── UploadQSODialog.cpp ├── UploadQSODialog.h ├── UploadQSODialog.ui ├── WebEnginePage.cpp ├── WebEnginePage.h ├── WsjtxFilterDialog.cpp ├── WsjtxFilterDialog.h ├── WsjtxFilterDialog.ui ├── WsjtxWidget.cpp ├── WsjtxWidget.h ├── WsjtxWidget.ui └── component/ ├── BaseDoubleSpinBox.cpp ├── BaseDoubleSpinBox.h ├── ButtonStyle.h ├── EditLine.cpp ├── EditLine.h ├── FreqQSpinBox.cpp ├── FreqQSpinBox.h ├── MultiselectCompleter.cpp ├── MultiselectCompleter.h ├── RepeatButton.cpp ├── RepeatButton.h ├── ShutdownAwareWidget.h ├── SmartSearchBox.cpp ├── SmartSearchBox.h ├── SqlHighlighter.cpp ├── SqlHighlighter.h ├── StyleItemDelegate.h ├── SwitchButton.cpp └── SwitchButton.h ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/ISSUE_TEMPLATE/01-Bug Report.yml ================================================ name: Bug Report description: File a bug report labels: ["bug", "triage"] body: - type: markdown attributes: value: | > [!IMPORTANT] > **Disclaimer Regarding Forks and Clones** > > Support is provided for the original, unmodified version of QLog. > If you are using a fork, clone, or any modified version of this project, please direct your issue to the maintainer of that specific repository. > This includes issues related to **core features**, as changes introduced in forks may directly or indirectly affect their behavior. Please reserve the issue tracker for reporting presumed bugs in QLog. If you have a question about usage or installation, please use the [QLog Discussions](https://github.com/foldynl/QLog/discussions) - type: textarea id: what-happened attributes: label: What happened? How can it be reproduced? Provide as much information as possible. description: Also, what did you expect to happen? validations: required: true - type: input id: specific-version attributes: label: QLog Version description: "Version reported in **Main Menu → Help → About**" value: "0.0.0" validations: required: true - type: dropdown id: ostype attributes: label: Which OS are you using? options: - Linux - macOS - Windows validations: required: true - type: dropdown id: osinstallation attributes: label: 'QLog Installation Method' description: 'Select the installation method' options: - DEB/RPM - Flatpak - EXE - DMG - Self-Compiled - I don't know validations: required: true - type: textarea id: system-information attributes: label: Installation Details description: | If you're not familiar with the QLog Installation Method mentioned above, please attach a screenshot of Main Menu → Help → About or provide more details about your OS. ================================================ FILE: .github/ISSUE_TEMPLATE/02-Feature Request.yml ================================================ name: Feature Request description: Request a new feature or enhancement for QLog labels: ["enhancement"] body: - type: markdown attributes: value: | Please describe the feature you would like to see and why it would be beneficial. Before submitting a request, check if a similar feature has already been suggested in the [existing issues](https://github.com/foldynl/QLog/issues). - type: textarea id: feature-description attributes: label: Feature Description description: Describe the feature or enhancement in detail. How should it work? Why is this feature useful? validations: required: true ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ blank_issues_enabled: false contact_links: - name: "📖 Find documentation" url: https://github.com/foldynl/QLog/wiki about: "Most documentation is on the QLog wiki" - name: "📨 Ask a question — Discussions" url: https://github.com/foldynl/QLog/discussions about: "Please use GitHub Discussions for general questions" ================================================ FILE: .github/workflows/c-cpp.yml ================================================ name: CI Linux/macOS on: push: branches: - 'testing_*' pull_request: branches: - 'testing_*' - master workflow_dispatch: jobs: ubuntu-build-qt5: name: Ubuntu CI QT5 strategy: matrix: os: [ubuntu-22.04, ubuntu-24.04] runs-on: ${{ matrix.os }} steps: - name: Install dependencies run: | sudo apt-get update sudo apt-get -y install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libsqlite3-dev libhamlib++-dev libqt5charts5-dev qttools5-dev-tools libqt5keychain1 qt5keychain-dev qtwebengine5-dev build-essential libqt5serialport5-dev pkg-config libqt5websockets5-dev - name: Checkout code uses: actions/checkout@v5 - name: configure run: qmake QLog.pro - name: make run: make -j2 ubuntu-build-qt6: name: Ubuntu CI QT6 strategy: matrix: os: [ubuntu-22.04, ubuntu-24.04] runs-on: ${{ matrix.os }} steps: - name: Install dependencies run: | sudo apt-get update sudo apt-get -y install libhamlib-dev build-essential pkg-config qt6-base-dev qtkeychain-qt6-dev qt6-webengine-dev libqt6charts6-dev libqt6serialport6-dev libqt6webenginecore6-bin libqt6svg6-dev libgl-dev libqt6websockets6-dev - name: Checkout code uses: actions/checkout@v5 - name: configure run: qmake6 QLog.pro - name: make run: make -j2 macos-build: name: MacOS CI if: vars.ENABLE_MACOS == 'true' strategy: matrix: os: [macos-15] runs-on: ${{ matrix.os }} steps: - name: Install dependencies run: | unset HOMEBREW_NO_INSTALL_FROM_API brew update brew upgrade || true brew install qt6 brew link qt6 --force brew install hamlib brew link hamlib --force brew install qtkeychain brew install dbus-glib brew install brotli brew install icu4c brew install pkg-config - name: Checkout code uses: actions/checkout@v5 with: fetch-depth: 0 - name: Get version from tag run : | TAGVERSION=$(git describe --tags) echo "TAGVERSION=${TAGVERSION:1}" >> $GITHUB_ENV - name: Configure and compile run: | cd build qmake6 -config release .. make -j4 deb-package: name: DEB Package Build if: github.event_name != 'pull_request' runs-on: ubuntu-24.04 steps: - name: Checkout code uses: actions/checkout@v5 - name: Install tools and build dependencies run: | sudo apt-get update sudo apt-get -y install devscripts equivs dpkg-dev fakeroot lintian libconfig-model-dpkg-perl licensecheck sudo mk-build-deps --install --remove debian/control --tool "apt-get -y --no-install-recommends" - name: Check debian/copyright run: cme check dpkg-copyright - name: Build DEB package run: dpkg-buildpackage -b -us -uc -j4 - name: Verify DEB was created run: ls -la ../qlog_*.deb - name: Run lintian run: lintian --no-tag-display-limit --fail-on error ../qlog_*.changes - name: Upload DEB artifact uses: actions/upload-artifact@v5 with: name: deb-package path: /home/runner/work/QLog/qlog_*.deb retention-days: 1 deb-smoke-test: name: DEB Smoke Test needs: deb-package runs-on: ubuntu-24.04 steps: - name: Download DEB artifact uses: actions/download-artifact@v5 with: name: deb-package - name: Install and smoke test run: | sudo apt-get update sudo apt-get install -y ./qlog_*.deb QT_QPA_PLATFORM=offscreen qlog --help 2>&1 | grep -q "QLog Help" rpm-package: name: RPM Package Build if: github.event_name != 'pull_request' runs-on: ubuntu-latest container: image: fedora:43 steps: - name: Install base tools run: dnf install -y git rpm-build rpmdevtools rpmlint dnf-plugins-core - name: Checkout code uses: actions/checkout@v5 with: fetch-depth: 0 - name: Get version from tag run: | git config --global --add safe.directory "$GITHUB_WORKSPACE" VERSION=$(git describe --tags --abbrev=0 | sed 's/^v//') echo "REPO_VERSION=${VERSION}" >> $GITHUB_ENV - name: Install build dependencies from spec run: dnf builddep -y rpm_spec/qlog.spec - name: Prepare RPM build tree run: | rpmdev-setuptree TAR_DIR="QLog-${REPO_VERSION}" mkdir -p "/tmp/${TAR_DIR}" cp -a . "/tmp/${TAR_DIR}/" tar czf ~/rpmbuild/SOURCES/qlog-${REPO_VERSION}.tar.gz --exclude='.git' -C /tmp "${TAR_DIR}" - name: Build RPM package run: rpmbuild -bb rpm_spec/qlog.spec - name: Verify RPM was created run: ls -la ~/rpmbuild/RPMS/*/QLog-*.rpm - name: Run rpmlint run: rpmlint ~/rpmbuild/RPMS/*/*.rpm || true - name: Upload RPM artifact uses: actions/upload-artifact@v5 with: name: rpm-package path: ~/rpmbuild/RPMS/*/QLog-*.rpm retention-days: 1 rpm-smoke-test: name: RPM Smoke Test needs: rpm-package runs-on: ubuntu-latest container: image: fedora:43 steps: - name: Download RPM artifact uses: actions/download-artifact@v5 with: name: rpm-package - name: Install and smoke test run: | dnf install -y $(find . -name 'QLog-*.rpm') QT_QPA_PLATFORM=offscreen qlog --help 2>&1 | grep -q "QLog Help" ================================================ FILE: .github/workflows/macOSBuild.yml ================================================ name: macOS deployment #on: [push, pull_request] on: workflow_dispatch: push: branches: - master jobs: macos-build: name: MacOS Build strategy: matrix: os: [macos-12, macos-13] runs-on: ${{ matrix.os }} steps: - name: Install Dependencies run: | unset HOMEBREW_NO_INSTALL_FROM_API brew update brew upgrade || true brew install qt6 brew install qt6-webengine brew link qt6 --force brew link qt6-webengine --force brew install hamlib brew link hamlib --force brew install qtkeychain brew install dbus-glib brew install brotli brew install icu4c brew install pkg-config - name: Checkout Code uses: actions/checkout@v4 with: fetch-depth: 0 - name: Get version from tag run : | TAGVERSION=$(git describe --tags) echo "TAGVERSION=${TAGVERSION:1}" >> $GITHUB_ENV - name: Configure and compile run: | mkdir build cd build qmake -config release .. make -j4 - name: Build dmg run: | cd build macdeployqt qlog.app -executable=./qlog.app/Contents/MacOS/qlog cp `brew --prefix`/lib/libhamlib.dylib qlog.app/Contents/Frameworks/libhamlib.dylib cp `brew --prefix`/lib/libqt6keychain.dylib qlog.app/Contents/Frameworks/libqt6keychain.dylib cp `brew --prefix`/lib/libdbus-1.dylib qlog.app/Contents/Frameworks/libdbus-1.dylib cp `brew --prefix brotli`/lib/libbrotlicommon.1.dylib qlog.app/Contents/Frameworks/libbrotlicommon.1.dylib cp `brew --prefix`/opt/icu4c/lib/libicui18n.74.dylib qlog.app/Contents/Frameworks/libicui18n.74.dylib install_name_tool -change `brew --prefix`/lib/libhamlib.dylib @executable_path/../Frameworks/libhamlib.dylib qlog.app/Contents/MacOS/qlog install_name_tool -change `brew --prefix`/lib/libqt6keychain.dylib @executable_path/../Frameworks/libqt6keychain.dylib qlog.app/Contents/MacOS/qlog install_name_tool -change @loader_path/libbrotlicommon.1.dylib @executable_path/../Frameworks/libbrotlicommon.1.dylib qlog.app/Contents/MacOS/qlog install_name_tool -change /usr/local/opt/icu4c/lib/libicui18n.74.dylib @executable_path/../Frameworks/libicui18n.74.dylib qlog.app/Contents/MacOS/qlog otool -L qlog.app/Contents/MacOS/qlog macdeployqt qlog.app -dmg - name: Copy artifact uses: actions/upload-artifact@v4 with: name: QLog-${{ env.TAGVERSION }}-${{ matrix.os }} path: /Users/runner/work/QLog/QLog/build/qlog.dmg ================================================ FILE: .gitignore ================================================ # C++ objects and libs *.slo *.lo *.o *.a *.la *.lai *.so *.dll *.dylib *.patch # Qt-es object_script.*.Release object_script.*.Debug *_plugin_import.cpp /.qmake.cache /.qmake.stash *.pro.user *.pro.user.* *.qbs.user *.qbs.user.* *.moc moc_*.cpp moc_*.h qrc_*.cpp ui_*.h *.qmlc *.jsc Makefile* # Qt unit tests target_wrapper.* # QtCreator *.autosave # QtCreator Qml *.qmlproject.user *.qmlproject.user.* # QtCreator CMake CMakeLists.txt.user* # QtCreator 4.8< compilation database compile_commands.json # QtCreator local machine specific files for imported projects *creator.user* .DS_Store /build ================================================ FILE: .gitmodules ================================================ ================================================ FILE: AUTHORS ================================================ MAINTAINERS: Ladislav Foldyna OK1MLG PAST MAINTAINERS: Thomas Gatzweiler DL2IC CONTRIBUTORS: Wolfgang DL2KI Fabian Kurz DJ5CW gerbert Florian Thienel AsciiWolf Maciej Krüger BG7JAF Juan Carlos EA5WA Diego LU1IDC Alfredo IK1VQY Michael AA5SH Emilio EA7QL Kyle Boyle VE9KZ Stéphane Fillod F8CFE ================================================ FILE: CONTRIBUTING.md ================================================ ## How Can I Contribute? ### Reporting Bugs *Please* contact me via the repository's Discussion page or email (you can find it [QRZ](https://www.qrz.com/db/OK1MLG) or [HamQTH](https://www.hamqth.com/ok1mlg) ) If you run into an issue, please search [Issue tracking](https://github.com/foldynl/QLog/issues) *first* to ensure the issue hasn't been reported before. Open a new issue only if you haven't found anything similar to your issue. #### When opening a new issue, please include the following information at the top of the issue: * What version you are using. * Describe the issue you are experiencing. * Describe how to reproduce the issue. * Including any warning/errors/backtraces - see debug details in QLog [Wiki](https://github.com/foldynl/QLog/wiki/Debug-Log-Level). In general, the more detail you share about a problem the quicker a developer can resolve it. For example, providing a simple test case is always exceptionally helpful. Be prepared to work with the developers investigating your issue. Your assistance is crucial in providing a quick solution. They may ask for information like: * Your non-sensitive information - ADIF export, log file etc. * Your hardware configuration * Your OS, distribution etc. * Your version of libraries (especial Qt, Hamlib) ### Pull Requests #### General * All pull requests must be based on the current `master` branch and should apply without conflicts. * Please attempt to limit pull requests to a single commit which resolves one specific issue. * Make sure your commit messages are in the correct format. See the [Commit Message Formats](#commit-message-formats) section for more information. * When updating a pull request squash multiple commits by performing a [rebase](https://git-scm.com/docs/git-rebase) (squash). * For large pull requests consider structuring your changes as a stack of logically independent patches which build on each other. This makes large changes easier to review and approve which speeds up the merging process. * Try to keep pull requests simple. Simple code with comments is much easier to review and approve. * If you have an idea you'd like to discuss or which requires additional testing, consider opening it as a draft pull request. Once everything is in good shape and the details have been worked out you can remove its draft status. Any required reviews can then be finalized and the pull request merged. ### Testing If you're in a position to run the latest code consider helping us by reporting any functional problems, performance regressions or other suspected issues. By running the latest code to a wide range of realistic workloads, configurations and architectures we're better able quickly identify and resolve potential issues. ## Style Guides ### Repository Structure **Branch Names:** - Latest Public Release branch: `master` - Upcoming release branch: `testing_$VERSION` ### Coding Conventions * Conditional ``` if ( cond ) { command; } ```` * Use spaces around operators * `count + 1` instead of `count+1` * Use spaces after commas (unless separated by newlines) * Keep in mind that QLog can run under Linux, Windows and MacOS - do not use platform-depend code #### Modules If possible, one class one file. Each mode (except models) has to include `debug.h` and define ``` MODULE_IDENTIFICATION("qlog.ui.mainwindow"); ``` Each function (except models) has to contain ``` FCT_IDENTIFICATION; ``` ### Commit Message Formats #### New Changes Commit messages for new changes must meet the following guidelines: * In 72 characters or less, provide a summary of the change as the first line in the commit message. * A body which provides a description of the change. If necessary, please summarize important information such as why the proposed approach was chosen or a brief description of the bug you are resolving. Each line of the body must be 72 characters or less. * Provides a subject line in the format of `Module_Name: Change description` where `Module_name` defines a part of source code where the change was made or new feature name (short name). ``` Module_Name: This line is a brief summary of your change Please provide at least a couple sentences describing the change. If necessary, please summarize decisions such as why the proposed approach was chosen or what bug you are attempting to solve. ``` #### Bug Fixes If you are submitting a fix the commit message should meet the following guidelines: * Provides a subject line in the format of `Fixed #ddd: Fix name...` where `ddd` represents each [Issue](https://github.com/foldynl/QLog/issues) . Bugfixes not listed in the Github Issue are introduced by the same way as Improvements (Module Name etc). ================================================ FILE: Changelog ================================================ 2026/04/26 - 0.50.0 - [NEW] - Added Split detection - [NEW] - Added Developer and Support tools (PR #991 @aa5sh @foldynl) - [NEW] - Added a simple QSL label printing dialog (issue #562) - [NEW] - Added Cabrillo contest export - [NEW] - Added direct labeling of QSL Requested and QSL Received to the QSO context menu (PR #982 @aa5sh) - [NEW] - DXCC Submission Report (PR #967 @aa5sh) - [NEW] - DXC - Search pre-fills the callsign from New Contact if callsign is present - [NEW] - Settings - Added Danger Zone tab with Delete all passwords and QSOs - [CHANGED] - Mutex-free Omnirig1 and Omnirig2 - [CHANGED] - POTA/SOTA/WWFF/SAT list are download from QLog LOV-repo - [CHANGED] - Used an external lib for CSV parsing for LOV Download - [CHANGED] - Import - ADIF import updates DXCC only if it is missing in the QSO (issue #983) - [REMOVED] - Import - ADIF import does not include the option to update DXCC during the import - no longer needed - Fixed Callsign disappears when calling a DX in a split via VFO-B (issue #799) - Fixing Rendering issue (issue #989) - Updated debian packaging (PR @979 thx @dawkagaming) - Hamlib rigctld switching between VFO A/B doesnt change the band mode - added workaround (issue #999) - Awards internal redesign - Removed AN from the WAC award (issue #1010) 2026/03/19 - 0.49.1 - Fixed Online Map OSM Access Blocked banner (issue #956) - Fixed Package build process issue - openssl-dev is missing (issue #957) - Fixed Missing dependence for QTKeychain (issue #964) - Added French Translation (PR #969 thx @fillods) 2026/03/13 - 0.49.0 - [NEW] - Added Pack and Unpack Data and Setting - Computer to Computer Migration (issue #535) - [NEW] - Added Rig Sharing via Rigctld (PR #736 issue #159 @aa5sh @foldynl) - [NEW] - Added QSL Gallery - [NEW] - QSO Filter - Added REGEXP operator - [NEW] - Settings - TQSL - Added Path auto-detect and validation - [NEW] - Upload QSO - Added LoTW Station Location Combo (PR #929 @TrgoSk @foldynl) - [NEW] - QSO Export - Added Station Profile Filter - [NEW] - BandMap shows emergency frequencies (issue #462) - [NEW] - Added Speed Up Down Macros - WinKey and CWDaemon (issue #491) - [NEW] - Settings Winkey v2 - Added PaddleOnly Sidetone (issue #739) - [NEW] - Settings Winkey v2 and CWDaemon - Added Sidetone Freq - [NEW] - All County fields contain Completer for Ukraine, US, Japan, NZ, Spanish, Russia (PR #785 @aa5sh @foldynl) - [NEW] - Added County Awards for Ukraine, US, Japan, NZ, Spanish, Russia (PR #785 @aa5sh @foldynl) - [NEW] - ADIF 3.1.7 - Added new modes FT2, FREEDATA, RIBBIT_PIX, RIBBIT_SMS (issue #934) - [CHANGED] - Generic FTx for FT8/FT4 (FT2) in Alert and DXC filter (issue #937) - [CHANGED] - LoTW QSL matching algorithm uses Mode and Mode Group matching (issue #942) - [CHANGED] - Reduced download period for DXCC Entities from 21 to 7 days - LogbookWidget: Delete Performance Optimization - Fixed Awards POTA Activator Filter - Fixed man page - Fixing SATmode Activity is blank (issue #948) 2026/01/30 - 0.48.0 - [NEW] - Rig Widget - tuning rig with mouse (issue #855) - [NEW] - Awards - Added User Filter Combo (issue #870) - [NEW] - Statistics - Added User Filter; new Dialog layout (issue #870) - [NEW] - Alerts - Added detailed Log Status settings (issue #817) - [NEW] - Settings - Enable/Disable sending color-coded status indicators back to WSJT-X (issue #885 @aa5sh @foldynl) - [CHANGED] - Omnirig Drivers: Removed QT AXContainers - pure Windows API code - [CHANGED] - Enabled Chekbox in Alerts Table (issue #867) - [CHANGED] - Serial Port Completer contains Horizontal ScrollBar - [CHANGED] - TCI - Settings - Default PWR defines the maximum output power - Fixed Omnirig drivers keep sending frequency change (issue #872) - Fixed ADIF import does not accept a default operator name (issue #884) - Fixed HamQTH URL from http to https (PR #886 @aa5sh) - Fixed Missing Band value when QSO from FLDigi (issue #892) 2025/12/19 - 0.47.1 - [CHANGED] - DXC - VE7CC-type Cluster is correctly detected (PR #851 @kyleboyle) - [CHANGED] - Chat - Changed ON4KST URL (issue #857) - Fixed missing clear button for Search Callsign (issue #753) - Fixed Updated QSOs are not sent to wavelog (issue #847) - Fixed Fixed Omnirig(v2) getFreq, getVFO, setFreq (issue #853) - Fixed ADI Header does not follow ADIF Spec (issue #859) - Fixed OmniRig settings are not saved (issue #862) 2025/12/06 - 0.47.0 - [NEW] - Adds theme options - native, light, dark (PR #718 @kyleboyle) - [NEW] - Implemented ADIF 3.1.6 - [NEW] - ADIF 3.1.6 - Added new modes FSK and MTONE - [NEW] - ADIF 3.1.6 - Added new contest IDs - [NEW] - ADIF 3.1.6 - Added new column EQSL_AG (Import/Export only) - [NEW] - Statistics, QSODetail contain deleted DXCC Entities (PR #728 @aa5sh @foldynl) - [NEW] - Settings Hamlib - Added support to define CIV Addr for Icom and Ten-Tec (issue #747) - [NEW] - Settings Hamlib - Added RTS and DTR control (PR #809 @aa5sh) - [NEW] - Settings GUI - Added options to switch distance unit - [NEW] - Added Linux manpage (PR #791 @dawkagaming) - [NEW] - Added a link to the GitHub release notes under Help-Whats New - [NEW] - Added workaround for WriteLog - always call callbook lookups (PR #833 @sjwoodr) - [NEW] - Awards - Added Not-Confirmed filter (PR #836 @aa5sh) - [NEW] - Rotator Widget - Added buttons to change button profiles - [CHANGED] - Settings - Added network loop detection for WSJTX Forward (issue #815 @aa5sh @foldynl) - [CHANGED] - Rotator - Rotator timeout is set to 5s (PR #823 @aa5sh) - [CHANGED] - Double Spin Boxes - the decimal separator is forced to be a period - [DELETED] - WSJTX Widget - Removed Freq and Mode - Fixed QSODetail - Anti-meridian bug on map (issue #786) - Fixed Statistics: Center maps on QTH longitude (issue #824) - Fixed OmniRig disconnecting when its status changed unexpectedly (issue #832) 2025/10/31 - 0.46.2 - Fixed Spaces after QRZ.com name (issue #767) - Fixed SPID Rot1Prog Rotator more 360 deg (issue #775) - Fixed build instructions on Debian (PR #777 @leventelist) - Fixed Bandmap truncated output despite having space (issue #779) - Fixed Statistics - Anti-meridian bug on map (issue #786) - Fixed Binary file in the tarballs (issue #794) - Fixed LOTW and eQSL upload status updated even when nothing is uploaded (issue #807) - Fixed Imported QSOs contain incorrect My DXCC info (issue #812) - Fixed Statistics Dialog does not show confirmed grids 2025/09/26 - 0.46.1 - Fixed QSO filter incorrectly displays inserted date (issue #752) - Fixed Logbook Search Icon is not centered (issue #753) - Fixed Using CQRLog API Key for Clublog (issue #759) - Fixed Online Service Password leaking via debug files (issue #760) - Fixed Tabs are not showed properly (PR #762 @aa5sh) - Fixed inability to edit Power in QSO Detail (issue #763) 2025/09/12 - 0.46.0 - [NEW] - NewContact: POTA/SOTA/WWFF/IOTA info is taken from the nearest spot (@aa5sh @foldynl) - [NEW] - DXSpots are sent to Flex Radio (PR #694 @aa5sh) - [NEW] - CWConsole - Added support for automatic periodic sending of CW Macros (issue #708) - [NEW] - Added mapping of Winkey hardware buttons to CW macro keys F1–F4 (issue #711) - [NEW] - Added Search Types to the Logbook Search Editbox (issue #733) - [NEW] - Rig - Periodic Rig status reporting independent of its changes (PR #730 @aa5sh) - [NEW] - Settings - Serial Port Completer for MacOS and Linux (PR #737 @aa5sh) - [NEW] - Hamlib - Attempt to send Power On when connecting the Rig - [NEW] - POTA Spots Info is received from the API at api.pota.app to improve POTA detection - [CHANGED] - Logbook Search Filter Widget - Added Search Widget - [CHANGED] - QSO Detail Country boxes use the new Search Filter Widget - [CHANGED] - NewContact - RST field uses the Overwrite Mode - [CHANGED] - NewContact - Default cursor position in RST is at the first character - [CHANGED] - Setting Dialog - Modes - Default Report can define cursor position - [CHANGED] - HamlibRot - Changed Poll Interval from 500 to 2000ms - Fixed DUPE is not detected for the first Contest QSO (issue #699) - Fixed FREQ_RX/BAND_RX is present only when RX and TX freqs are different (issue #714) - Fixed Repeated log recording (issue #722) - Fixed QSO Filter contains untranslated QSO items (issue #732) 2025/07/11 - 0.45.0 - [NEW] - Single Dialog for Upload/Download Online Services (issue #448) - [NEW] - Added option to swap paddles to Winkey settings (issue #676) - [NEW] - Added native support for the FLRig interface (issue #679) - [NEW] - Added New Version Notification - only for MacOS and Windows (PR #669 @aa5sh) - [NEW] - QRZ Upload - Added support for multiple API Keys - [NEW] - Logbook - Added highlighting to the filter button when a filter is active - [NEW] - WSJTX - Filtered label is shown when filter is enabled - [NEW] - DXC - Filtered label is shown when filter is enabled - [NEW] - Added support to Upload Cloudlog/Wavelog - [NEW] - Add JS8 to legacy_modes.json (issue #677) - [CHANGED] - Unification Settings storage - [CHANGED] - Calculate distances according to IARU rules - Fixed missing Wsjtx Spot values in the AlertWindow 2025/05/11 - 0.44.1 - Fixed Rotator Widget Seg Fault for new users (issue #666) 2025/05/09 - 0.44.0 - [NEW] - Activity Manager - Added SKCC, UKSMG and FISTS as Dynamic Fields - [NEW] - QSO - FISTS, SKCC, and UKSMG are auto-filled from MembershipQE (issue #628) - [NEW] - Rotator - Added QSO destination needle (issue #644) - [NEW] - QSO Detail - Adds grayline and short path (PR #653 @kyleboyle) - [CHANGED] - Rotator - Needle colors correspond to the online map (issue #644) - Fixed TCI cw_macros must contain RigID (issue #663) - Fixed TCI setKeySpeed sets keyer and macros speed (issue #663) - Fixed Data mode missing in rig control window - rigctld (issue #660 @aa5sh) - Improved DXC Mode detection - Updated Simplified Chinese translation 2025/04/19 - 0.43.1 - Fixed Click on PHONE DX Spots sets wrong mode (issue #453) - Fixed No freq via Omnirig for IC7400 (issue #639) 2025/04/04 - 0.43.0 - [NEW] - Added support to receive QSOs from CSN SAT Device (PR #610 @aa5sh) - [NEW] - Bandmap - Multiple independent bandmap windows (PR #593 @kyleboyle @foldynl) - [NEW] - Winkey Keyer driver currently supports v1 and v2 hardware - [NEW] - QSO Detail - QSLs QSLr Msg is editable - [NEW] - Activity Manager - Added new dynamic field - QSL Message Send - [CHANGED] - HamlibDrv - Enabled Power, RIT, XIT, Morse for RIG Netctl - [CHANGED] - QSO Detail - QSLMSG replaced by QSLMSG_RCVD (issue #633) - [CHANGED] - eQSL Upload - Added an option to choose a QSLMsg field - [CHANGED] - eQSL Download - eQSL QSLMSG is stored to QLog QSLMSG_RCVD - [CHANGED] - eQSL Download - Added QSLMSG_RCVD merging - eQSL message is appended - Fixed Speed pot doesn't seem to work with a WinKeyer; double chars (issue #618) - Fixed BandMap Spots colour change after a QSO (issue #632) - Fixed Incorrect Field Mapping for Received eQSL Messages (issue #633) 2025/03/07 - 0.42.2 - Fixed Logbook country translations (issue #608) - Fixed Unexpected dialog when QSO after contest (issue #614) - Fixed Statistics Widget does not display NULL continents (@aa5sh) - Fixed Statistics Widget does not display NULL Band, Mode - Fixed Statistics Widget TOP10 does not display removed DXCCs - Fixed Statistics Widget TOP10 does not display translated country names - Fixed Awards Widget does not display removed DXCCs 2025/02/22 - 0.42.1 - Fixed Unexpected timezone info (issue #600) - Fixed DXCC Statistics picks more entities (issue #601) - Fixed a crash when no internet connection 2025/02/14 - 0.42.0 - [NEW] - Awards - Added Slots - total over each band (issue #538) - [NEW] - Awards - Added Grid Award - 2/4/6 Chars grid (issue #564) - [NEW] - Settings - Added options to switch 12/24 time and date format (issue #573) - [NEW] - Activity Manager - Added new dynamic fields - Rig (issue #575) - [NEW] - LoTW Import - Fill missing information also for confirmed QSOs (@aa5sh) - [NEW] - Added CW macro QTH - [NEW] - DXC - Added 15min Trend - [CHANGED] - Changed IOTA LOV Source, the official web is used - [CHANGED] - CSV Export: Time and Date formats use ISO8601 format (issue #562) - [CHANGED] - Settings - Renamed Shortcuts to GUI tab - [CHANGED] - LOV - Improved LOV download performance (PR #582 @aa5sh) - Partial fix Windows State/Size does not save in case of fullscreen (issue #418) - Fixing TCI error when you change Rig (issue #526) - Fixed DXCC Award total worked confirmed counts included deleted entities (PR #588 @aa5sh) - Fixed Raspberry PI Flatpak - Import Select file dialog crashes (issue #589) - Suppressed the ability to edit Contest fields after the start (issue #590) 2025/01/21 - 0.41.1 - Fixed compilation issue under Debian 12 (issue #571) - Fixed Incorrect GPL version in rpm/deb packages (issue #572) - Fixed MacOS floating utility window bug (PR #576 @kyleboyle) - Updated IT translation 2025/01/11 - 0.41.0 - [NEW] - Logbook - Added a new context menu item - Update QSO from Callbook (issue #450 @aa5sh) - [NEW] - DIGI mode is used in case of DXC Digi Spots (issue #480) - [NEW] - DXC - Retrieve information about SOTA, POTA, IOTA and WWFF from comment (issue #482) - [NEW] - Alert - Added SOTA, POTA, IOTA and WWFF filter - [NEW] - Added the COM Port Completer for Windows platform (issue #490) - [NEW] - Settings - Added DXCC Confirmed By options (issue #508) - [NEW] - Added POTA Export Formatter (activator/hunter) (PR #514 @kyleboyle) - [NEW] - CW Console - CW Halt with the user-defined shortcut (issue #518) - [NEW] - Added an input parameter to save debug message to file - [NEW] - Logbook - Added sorting function to logbook table columns (PR #540 @kyleboyle) - [NEW] - Network Notification - Added Rig Status Notification - [NEW] - Implemented ADIF 3.1.5 - [NEW] - ADIF 3.1.5 - Added new submodes FSKH245 and FSKH105 for HELL - [NEW] - ADIF 3.1.5 - Added new contest IDs - [NEW] - ADIF 3.1.5 - Added new columns (Import/Export only) - [NEW] - ADIF 3.1.5 - Added My DARC DOK to Station Profile - [CHANGED] - Settings: disabled band and mode name modification - [CHANGED] - DX Stats contain all enabled bands (issue #538) - [CHANGED] - Removed Freq, TimeDate On/Off Data Type Indicators (issue #552) - [CHANGED] - ADIF 3.1.5 - VUCC and MY_VUCC can contain 6 or 4-chars locators - [CHANGED] - Stop exporting default value N for qsl_rcvd, qsl_sent, lotw/dcl/eslq_qsl_rcvd/sent - [CHANGED] - Extended QSL/Import Dupe matching rule to Callsign, Band, Mode, Time and Sat_Name (issue #563) - Fixed MacOS - keep floating windows visible on app unfocus (issue #530) - Fixed Contest Filter ignores the first QSO (issue #529) - Fixed It is not possible to quit Qlog with detached widgets Rot and Rig (issue #534) - Fixed ADX/CSV/JSON do not export non-ASCII chars (issue #542) - Fixed Checking the 60m checkbox in cluster filters allows 160m spots to appear (issue #543 @aa5sh) - Fixed Problems uploading to QRZ.com (issue #559 PR #561 @aa5sh) - Fixed DX Stat screen is jumping up/down - Fixed Omnirig drivers: Digi modes are not correclty recognized 2024/11/29 - 0.40.1 - Fixed Bands - Added missing 8m band (issue #515) - Fixed CW Console - EXCSTR does not work properly (issue #517) - Fixed Activity Manager - Missing Propagation Mode None (issue #519) - Fixed QSO Filter - filter fields with random order (PR #525 @aa5sh) - Fixed TCI error when you change Rig (issue #526) - Fixed NewContact - satellite mode too wide (issue #527) 2024/11/24 - 0.40.0 - [NEW] - Activity Manager - merged Layout Manager and profiles (issue #408) - [NEW] - Activity Manager - Added new dynamic fields - Contest fields, RX/TX Power - [NEW] - Added light support for contests (issue #345) - [NEW] - Added CW macros EXCHSTR, EXCHNR, EXCHNRN - [NEW] - Export Filter - Added user filter combo (original idea PR #476 @aa5sh) - [NEW] - New Contact - Added expand/collapse button to QSO field tab widget (PR #495 @kyleboyle) - [NEW] - Alert - Added CQZ and ITUZ filters - [NEW] - KSTChat - Added a new 40MHz room (PR #496 @kyleboyle) - [NEW] - Station Profile contains Operator Callsign (issue #441 @kyleboyle) - [NEW] - Station Profile contains County (issue #493 @kyleboyle) - [NEW] - Statistics - Adds time of day and better qso mapping (PR #501 @kyleboyle) - [NEW] - Bandmap - Tooltip shows a spotter callsign (PR #507 @Skittlebrau) - [CHANGED] - New Contact - Renamed DXCC Tab to DX Stats contains DXCC and Station Statistics (issue #477) - [CHANGED] - QSL Import dialog - Detail text is selectable by mouse and keyboard - [CHANGED] - Removed Main Menu Layout; Activity Manager is in the bottom-left corner - [CHANGED] - Removed Keep Options from the Equipment Menu - use Activity Manager for it - Fixed issue when CW is always selected after Settings exiting or connecting the Rig - Updated Timezone definition file - version 2024b 2024/10/05 - 0.39.0 - [NEW] - DXC - Added Full-text search - [NEW] - Select S in RST Edit when focused (issue #454) - [NEW] - Alerts - Added Member Column - [CHANGED] - HamlibDrv Rig/Rot- Added multiplatform reliable sleep - [CHANGED] - Changed Backup policy - [CHANGED] - Logbook page size - improved performance - [CHANGED] - Logbook - CTRL-A (Select All) is disabled - [CHANGED] - Awards - Bands are displayed based on the Settings (issue #452) - [CHANGED] - WSJTX - More reliable detection of CQ stations (PR #471 @aa5sh) - [CHANGED] - WSJTX - SOTA/POTA/WWFF/SIG are being added to the logged QSO (PR #463 @aa5sh) - [CHANGED] - Stats - Add a confirmation dialog for displaying over 50k QSOs on the map - [CHANGED] - New Contact - Starting QSO Timer when Rig online and WSJTX Update Callsign Status is received - [CHANGED] - Added a postponed handling for Rig soft errors (issue #472) - Fixed WSJT-X does not emit band change if rig is disconnected (issue #447) - Fixed Wrong import of ADIF file of another log program (issue #455) - Fixed WSJTX log record is stored incorrectly if it contains non-ASCII chars(issue #458) - Fixed ADIF import does not import records with old DXCC Entities (issue #459) - Fixed ADIF import incorrectly uses Station Profile parameters (issue #461) - Fixed Logbook - QSO Table Column Width Does Not Stick (issue #464) - Fixed Alerts Window displays OOB Spots (issue #469) - Fixed Field values from past QSOs are used incorrectly in case of WSJTX QSOs (#issue 470) 2024/08/29 - 0.38.0 - [NEW] - Logbook - Added Send DX Spot to the QSO Context Menu - [NEW] - DX Filter - Added Dedup Time/Freq difference setting (@aa5sh) - [NEW] - Rig Setting - Added RTS/DTR PTT Type support (issue #353) - [NEW] - Bandmap - Scrollbar position is saved per band (issue #415) - [NEW] - New Contact - Added a dynamic value completer for SIG field (issue #425) - [NEW] - Awards - Added SOTA/POTA/WWFF (@aa5sh issue #311) - [NEW] - Awards - Added Not-Worked Filter - [NEW] - New Contact - Added Long Path Azimuth info - [NEW] - POTA Fields allow a comma-delimited list of one or more POTA Refs - [NEW] - WSJTX tunes freq/mode like Rig if rig is disconnected - [CHANGED] - Alert Widget is a Dock Widget (issue #399) - [CHANGED] - QLog adds more information from callbook for WSJTX QSOs (issues #403 #405 #420) - [CHANGED] - File Open dialogs are not a native dialog under Linux (issue #427) - [CHANGED] - Profiles transferred to DB - [CHANGED] - LOV last_dates transferred to DB - [CHANGED] - DX Cluster - Login Callsign is always the base Callsign - [REMOVED] - Setting DXCC Date - Fix for MacOS Layout Geometry Restore (@aa5sh) - Fixed TQSL does not block GUI thread - Fixed MacOS build process (@aa5sh) 2024/07/26 - 0.37.2 - Fixed Field QSL Send Via should be retained (issue #413) - Fixed Set rotator position fails if azimuth > 180 (issue #417) - Fixed Windows State/Size does not save in case of fullscreen (issue #418) - Fixed Significant rounding during azimuth calculation (issue #422) - Updated Simplified Chinese translation - Updated Spanish translaction - Added Italian translation (thx IK1VQY) 2024/07/10 - 0.37.1 - Fixed QSO Table Callsign filter is not filled properly (issue #401) - Fixed DXC zero frequency for last QSO in case of FT8 QSOs (issue #404) - Fixed Callsign Context Menu does not work (issue #409) - Fixed QSO Detail Save and Edit buttons are not translated (issue #410) 2024/07/01 - 0.37.0 - [NEW] - Added Shortcuts Editor (issue #293) - [NEW] - Added QO100 Bandplan to correctly categorize the DX Spots - [NEW] - Improveded detection of SH/DX SHF Spots - [NEW] - Online Map - Added WSJTX CQ Spots - [NEW] - WSJTX - Sortable View - [NEW] - Alerts - Sortable View - [NEW] - Added Spanish translation (thx LU1IDC) - [NEW[ - Added Search Callsign Clear Button (issue #396) - [CHANGED] - QRZ auth should be over POST with form data (issue #389) - [CHANGED] - Big CTY file is used - [CHANGED] - Callbook Country DXCC ID is used in case of difference from Big CTY - [CHANGED] - Removed ALT+W and CTRL+DEL shortcuts - [CHANGED] - Removed New Contact and Save Contact from Logbook Main Menu - Fixed Guantanamo KG4 Issue (issue #372) - Fixed QRZ Lookup Not Including Full Name - History (issue #388) - Fixed Spot Last QSO contains TX freq, should contain RX freq (issue #390) - Fixed Spot Last QSO must contain Freq in kHz (issue #391) - Fixed Bandmap select previous selected callsign issue (issue #394) - Fixed Malfunctioning tuning of WSJTX Alert spot - Fixed DXCC Status for FT4 Spots incorrectly identified in WSJTX 2024/06/07 - 0.36.0 - [NEW] - WSJTX: Added support to received ADIF QSO Log record - [NEW] - Sat mode is derived from RX/TX Freq - [NEW] - Logbook filters change color when enabled - [NEW] - Frequency input boxes PageUp/Dn switches the band (issue #360) - [NEW] - CTRL + PgUp/Dn switch band on the connected rig - global shortcut (issue #360) - [NEW] - Added number of filtered QSOs (issue #374) - Fixed Callbook query does not work (issue #377) - Fixed Logbook columns are reordered after Delete (issue #383) - Fixed Missing Republic of Kosovo flag (issue #384) 2024/05/21 - 0.35.2 - Improved delete performance; added delete progress bar (issue #351) - Fixed Password with plus is incorrectly sent to online services (issue #366) - Fixed Compilation issue under v4.6 (issue #368) - Fixed Network Rig configuration is not saved (issue #370) 2024/05/06 - 0.35.1 - Fixed Free QRZ callbook - Name is not populating (issue #363) - Fixed Incorrect CW segment freqs (issue #365) 2024/05/03 - 0.35.0 - [NEW] - Added Rot Interface PSTRotator Network - [NEW] - Added QSO/QSL Since option to eQSL Dialog - [NEW] - Bandmap - Current Mode segment visualisation - [NEW] - CW Console - Added Word/Whole mode switch - [NEW] - Added Callbook Profile Image Widget - [NEW] - ASCII conversion based on Text-Unidecode/iconv algorithm (issue #316 #350) - [NEW] - ITU/CQ Zones can be defined in Station Profile (issue #358) - [CHANGED] - Spacebar is used as a focus changer for fields where space is not allowed - [CHANGED] - Focus does not select text in the input fields - [CHANGED] - Force XCB under Linux Wayland - [CHANGED] - Bandmap - Added Callsign/Freq/Mode to tooltip (issue #355) - Fixed incorrect ADIF date format for clublog_qso_upload_date (issue #342) - Fixed The last name from Callbooks queries (issue #346) 2024/03/25 - 0.34.0 - [NEW] - Rotator Widget - Azimuth by Clicking - [NEW] - Rotator Widget - QSO button provides Short/Long Path (issue #330) - [NEW] - Equipment Menu - Added Keep Options between application restart (issue #331) - Fixed TCI - Thetis Connection issue (issue #327) - Fixed TCI - Spots To Rig are not displayed (issue #328) - Fixed Bandmap unintentionally emits frequency labels (issue #333) - Fixed Failing to load grid square for G and EI SOTA summits (issue #336) - Fixed HRDLog On-Air message is not sent (issue #337) - Fixed Offline Map - Improved Path drawing 2024/03/09 - 0.33.1 - Fixed Rotator offline map is incorrectly centered (issue #324) - Fixed Hamlib integration not working (issue #325) - Fixed issue when Hamlib reopen rig caused Initialization Error - Fixed issue when Omnirig Drv did not emit rigIsReady signal 2024/03/08 - 0.33.0 - [NEW] - Added Rig Interface TCI - [NEW] - Callbook search can be temporarily paused - Improved DXC Mode recognition - Fixed Modal dialog blinks - Windows platform (issue #315) - Fixed LoTW and eQSL download are only QSLs dowloads - button label changed (issue #318) - Fixed i18n: Country Names and Prop-modes are translated (issue #322) 2024/02/10 - 0.32.0 - [NEW] - Added Rig Interface Omnirig v1 (Windows only) - [NEW] - Added Rig Interface Omnirig v2 (Windows only) - [NEW] - Clublog - Added Clear Clublog and reupload QSOs - [NEW] - Clublog - Added Real-time Insert/Update/Delete - [CHANGED] - Clublog - Upload callsign is derived from the Current Profile Callsign - Fixed clang linker failed issue (issue #301) - Fixed SAT Mode U/U missing (issue #308 PR #309 thanks ea5wa) - Fixed Multiple QSO selection. Callsigns modified by mistake (issue #310) - Fixed Callbook query cache is not properly cleared when Callbook settings change (issue #313) 2024/01/05 - 0.31.0 - [NEW] - DXC - Improved Mode recognition - [NEW] - DXC - Switch Rig mode based on DXC Spot Mode (issue #217) - [NEW] - DXC - Added Spot Country Column (issue #273) - [NEW] - DXC - Added Menu for server management - [NEW] - DXC - Added Auto-connect to server - [NEW] - DXC - Added Keep QSOs Context Menu - [NEW] - DXC - Added Clear QSO Context Menu - [NEW] - DXC - Added support for SH/DX response parsing - [NEW] - DXC - Added support for username, password for connection - [CHANGED] - DXC - Commands Combo changed to function button with selectable function - [CHANGED] - DXC - DX Spot is prepared via DXC Command Line, Remark dialog was removed - [NEW] - Online Map - IBP station double-click tunes freq and switch Rig mode - [NEW] - Main Window - Current profile name is shown (issue #282) - [NEW] - Import - Details can be saved to file (issue #284) - [NEW] - Added Simplified Chinese translation (PR #285 thank BG7JAF) - [NEW] - New Contact - Enter saves QSO if QSO time is running (issue #293 - partial) - [NEW] - New Contact - Callsign Enter event saves QSO if no Callbook is active - Pileup Mode (issue #293) - [NEW] - RIG Widget - RIT/XIT are displayed with user-friendly units (issue #294) - [CHANGED] - SAT List download - Shortened download period for SAT list from 30 to 7 days - Fixed ADI Import is too slow (issue #270) - Improved Import/Export Performance - Fixed Missing Satellite Mode SX (issue #291) - Fixed QSO Detail - Issue when Sat-Name field was always disabled - Fixed RPM build - Installed (but unpackaged) metainfo file issue 2023/12/01 - 0.30.0 - [NEW] - QSL Images are stored in the database - [NEW] - Added AppStream Metainfo File (PR #262 thanks AsciiWolf) - [NEW] - Added (WPX) prefix (issue #263) - [NEW] - Added WPX Award statistics - [NEW] - Added support for external translation files(issue #275) - [CHANGED] - Removed QSOID from Export dialog column setting (issue #258) - Fixed Date editor does not support NULL value in Logbook Direct Editor (issue #256) - Fixed duplicate entry in Windows Add or Remove - only Window platform (issue #260) - Fixed RST fields revert to 59 after changing them (issue #261) - Fixed Cannot change TQSL Path in Settings - flatpak (issue #271) 2023/11/13 - 0.29.2 - Fixed QLog is already running error popup on MacOS (issue #257 thanks rjesson) 2023/11/10 - 0.29.1 - Fixed QSL cards tooltip are not displayed under qt6.5 (issue #248) - Fixed Distance unit is not displayed in QSO Info under Windows (issue #250) - Fixed Editing STATION_CALLSIGN can cause unwanted change in QSO Detail (issue #251) - Fixed QSO Detail Operator Name containes an incorrect value (issue #252) - Fixed Calls with VE, VA are coding as Amsterdam & St Paul Islands instead of Canada (issue #253) - Fixed LoTW QSL import reports unmatched QSOs sometime (issue #254) 2023/10/20 - 0.29.0 - [NEW] - Added user-defined layout for New QSO Detail widget - [NEW] - Main window State and Geometry can be saved to layout profile - [NEW] - Awards - Added WAS - [NEW] - Awards - WAZ/ITU/WAC show all possible values - [NEW] - Distance unit (km/miles) is controlled by OS Locale - [CHANGED] - Removed SAT Tab - field can be added via Layout Editor - Improved Import QSO performance - Fixed QLog crashes if POTA, SOTA or WWFF contain incorrect values (issue #245) - Fixed QSOs are not uploaded to QRZ and HRDLog if fields contain HTML delimiter strings (issue #247) 2023/09/22 - 0.28.0 - [NEW] - Added ON4KST Chat Support - [NEW] - Added Az BeamWidth and Az Offset to Antenna Profile - [NEW] - Double-Clicking the IBP callsign in the online map tunes the frequency - Fixed Browse button should open an expecting folder (issue #241) - Fixed Reword QSL buttons and Settings in QSO Details and Settings (issue #242) 2023/08/21 - 0.27.0 - [NEW] - Added HRDLog Support - Fixed Text field alignment (issue #233) - Fixed Rig/Rot Connection port type selection (issue #235) - Fixed Incorrect Distance Value in WSJTX Widget (issue #236) - Fixed Incorrect WSJTX locator target on the map (issue #237) 2023/07/30 - 0.26.0 - [NEW] - Added user-defined layout for New QSO widget - [NEW] - Pressing Spacebar in Callsign field skips RST fields - [NEW] - Added user-defined URL for web lookup (issue #230) - Fixed WSJTX QSOs should have an Operator Name from Callbook (issue #223) - Fixed US call area suffixes not handled correctly (issue #226 thanks Florian) - Fixed QSO Filter Detail allows to save an empty Filter Name (issue #228) 2023/07/17 - 0.25.1 - Fixed Unexpected mode change when Setting Dialog is saved (issue #222) - Fixed QSL_SENT field has an incorrect ADIF name (issue #225) 2023/07/04 - 0.25.0 - [NEW] - Export - Added CSV Format - [NEW] - Export - Added Type of Export Generic/QSLs (issue #209) - [NEW] - Export - Added Exported Columns Setting - [NEW] - Export - All export formats use the ADIF field name convention - [CHANGED] - Export - JSON format contains a header - JSON format change - [CHANGED] - Default Statistics Interval is curr_date-1 and curr_day - Fixed Errors from Secure Storage are not shown (issue #216) - Fixed RX/TX Bands are uneditable when RX/TX freqs are missing (issue #220) 2023/06/16 - 0.24.0 - Fixed Incorrect FT4 mode-submode (issue #212) - Fixed CONTESTIA mode should be CONTESTI (issue #213) - Fixed Context menu deselects NewContactEditLine (issue #215) - FIxed incorrect WSJTX Filter initialization (issue #218) 2023/06/09 - 0.23.0 - [NEW] - Added CWDaemon Keyer Support - [NEW] - Added FLDigi Keyer Support - [NEW] - Online Map - based on locale, the map language is selected (Only EN, FR, GE supported - issue #180) - Fixed After entering longer QTH, the field content is not left-aligned (issue #157) - Fixed wrong QSO Time in case of JTDX (issue #204) - Fixed QSL Sent Date fields are not filled if QSL Sent Status fields are Y (issue #207) 2023/05/07 - 0.22.0 - [NEW] - ADIF Import - My Profile is used to define default values - [NEW] - ADIF Import - Checking a minimal set of input fields (start_time, call, band, mode, station_callsign) - [NEW] - ADIF Import - Added Import Result Summary + Import Detail Info - [NEW] - Main Menu - Added Help -> Mailing List. - [NEW] - Export - Filter for the exported QSOs - [CHANGE] - Renamed Locator to Gridsquare - Fixed Some anomalies in the input and processing of QSLr Date (issue #192) - Fixed User unfriedly CW Keyer Error (issue #194) - Fixed ADIF import (issue #196) - Fixed Operator field is incorrectly used (issue #197) - Fixed Crash if an unknown POTA & SOTA/WWFF Setting is entered (issue #198) - Fixed FLDIGI cannot connect QLog (issue #199) - Fixed if ADIF record is missing band info, add this from freq field (thx DJ5CW) 2023/04/16 - 0.21.0 - [NEW] - Rotator - Added Used-Defined Buttons - [NEW] - Rotator - Added Destination Azimuth Needle - [NEW] - Online Map - Added Antenna Beam Path - [NEW] - Rig - Combos are disbled when disconnected - [NEW] - Club Member Lists (issue #60) - [NEW] - Alert Table shows rule names - [CHANGED] - Alerts, DXC and WSJTX Network Notifications - Fixed Antenna Azimuth Negative Value (issue #191) - Fixed CTY file is not loaded when duplicate record (issue #193) 2023/03/14 - 0.20.0 - [NEW] - Added MUF Layer to online map - [NEW] - Added International Beacon Project (IBP) Beacons to online map - [NEW] - Centering the map on the current profile at start (issue #185) - Fixed incorrect ADIF interpretation of _SENT fields (issue #176) - Fixed Awards Dialog, Table double click for ITU/CQZ/WAZ/IOTA shows incorrect QSOs (issue #177) - Fixed ADIF double-type fields when 0.0 is currently mapped to NULL (issue #178) - Fixed QSO Detail to save NULL instead of empty string (issue #179) - Fixed ADIF Import default _INTL values are now stored correctly (issue #183) - Fixed Maps show an incorrect path if from/to grids are the same (issue #186) - Fixed Online Maps incorrect Bounds if Bandmap callsign double-click (issue #188) - Updated German translation (thx DL2KI) 2023/02/17 - 0.19.0 - [NEW] - Added Aurora Layer to online map - [NEW] - Logbook - filter options are saved and restored - [NEW] - Map Setting is saved and restored (issue #140) - [NEW] - QSO Duration (issue #158) - [NEW] - DX Cluster uses monospace font (issue #164) - [NEW] - Awards - if click on the Entity/band the logbook filter is set (issue #168) - [NEW] - WSJTX - Added Multicast support (issue #172) - Fixed WWFF LOV Download (issue #169) 2023/01/15 - 0.18.0 - [NEW] - ADIF 3.1.4 updates - Added new modes FREEDV and M17 - Added new band (submm) - Adopted Altitude (for SOTA only) - Adopted POTA (includes POTA List) - Adopted Gridsquare_ext (only import/export) - Adopted Hamlogeu_* (only import/export) - Adopted HamQTH_* (only import/export) - [NEW] - Added new DXCC Status and color for it - Confirmed - [NEW] - New Contact - Tab selection is saved - [NEW] - Grid can contain 8-characters - [NEW] - User filter can contain NULL value - [NEW] - Compilation - added variables for external sources - [NEW] - My DXCC/CQZ/ITUZ/Country is filled - [NEW] - Alerts - Added Aging (issue #153) - [NEW] - Alerts - Added DXCC Status Color (issue #153) - [NEW] - DXC - Added Log Status to filter (issue #154) - [NEW] - DXC - Added Spot deduplication to filter (issue #154) - [NEW] - WSJTX - Added CQ-Spot Filter (issue #155) - [NEW] - QSO Detail contains DXCC Tab (issue #156) - [CHANGED] - New QSO DXCC Tab reworked (issue #144) - [CHANGED] - All DXCC Stats are computed based on My DXCC instead of My Callsign - [CHANGED] - Station Profile Setting layout 2022/12/18 - 0.17.0 - [NEW] - NetPort and Polling interval can be defined for NET Rigs - [NEW] - NetPort can be defined for NET Rots - [NEW] - Added Saving Bandmap Zoom per band (issue #137) - [NEW] - CW speed synchronisation (issue #139) - Fixed Missing callbook data when callsign has prefix (issue #133) - Fixed Winkey2 echo chars are incorrectly displayed in CW Console (issue #141) - [CHANGED] - Online Map - Gray-Line is enabled by default - Update Timezone database 2022/11/20 - 0.16.0 - [NEW] - SOTA/IOTA lists updated regularly - [NEW] - Added WWFF list, updated regularly - [NEW] - QTH/Grid are filled based on SOTA/WWFF - [NEW] - DXC/WSJTX columns are movable, added column visibility setting - [NEW] - DXC/WSJTX columns layout is saved - [NEW] - Added Wiki&Report Issue links to Help section - [NEW] - About dialog contains run-time information - [NEW] - Solar Info as a ToolTip - [NEW] - QSO Manual Entry Mode - Fixed Bandmap unlogical animation when band is changed (issue #128) - Fixed Bandmap marks are not displayed correctly when RIT/XI (issue #131) - Fixed Setting Dialog size - Update Timezone database 2022/10/16 - 0.15.0 - Fixed Keeping the Bandmap RX mark always visible when centre RX is disabled (issue #115) - Fixed Equipment Menu: Swapped Connect Keyer and Rig (issue #122) - Fixed Callsign is deleted when clicking bandmap (issue #126) - Fixed typo in the Map layer menu (issue #127) - Fixed compilation issues & warning under QT6 - preparation for QT6 migration 2022/10/02 - 0.14.1 - Fixed CW Console - HALT Button is not enabled under Ubuntu flavours (issue #124) 2022/09/29 - 0.14.0 - [NEW] CW Console (Winkey2, Morse over CAT support) - [NEW] DX Cluster pre-defined commands (send last spot, get stats) - [NEW] Added DX Cluster Views (Spots, WCY, WWV, ToALL) - [NEW] Implemented DX Cluster Reconnection - [NEW] Remember last used DX Cluster - [CHANGED] - UI unifications - Rot/Rig/DXC - Fixed COM port validation for Windows platform - Fixed Reconnecting (DXC/Callbook) (issue #110) - Fixed DX Cluster crashes when DXC server is not connected and a command is sent (issue #111) - Fixed Bandmap callsign selection not fully works (issue #116) 2022/08/06 - 0.13.0 - [NEW] QSY Contact Wiping (issue #100) - [NEW] Timeoff is highlighted when QSO timer is running (issue #100) - [NEW] Callsign whisperer - [NEW] Bandmap - Spot's color is recalculated when QSO is saved - [NEW] BandMap - CTRL + Wheel zooming - [NEW] BandMap - Zooming via buttons keeps a focus on centre freq - [NEW] BandMap - DX Spot's Comment as a tooltip - [CHANGED] BandMap - UI Layout - Fixed MacOS builds (PR #102) (thx gerbert) - Fixed templates under MacOS (PR #101) (thx gerbert) - Fixed WindowsOS Installer - Unable to upgrade version 2022/07/15 - 0.12.0 - [NEW] Statistics - Show ODX on the map - [EXPERIMENTAL] Support for QT Styles (issue #88) - [CHANGED] - Removed F2 as a shortcut for QSO field editing - Next fixing of a high CPU load when DXC is processed (issue #52) - Fixed QSO fields from prev QSOs when Prefix - Callsign - Suffix (issue #90) - Fixed Chaotic QSO start time (issue #93) - Offline maps - Lighter colors, night sky removed, Sun position removed (issue #97) - Fixed incorrect A-Index colort (issue #98) - Fixed Stats Widget - percents - does not reflect date range (issue #99) - Fixed potential LogParam Cache issue - Import/Export polishing 2022/06/26 - 0.11.0 - [NEW] QSO Detail/Edit Dialog - [NEW] Added mW power Support - [NEW] Implemented ADIF 3.1.3 - [NEW] Rigwidget saves last used freq for bands - Fixed Rig Combo size when Rig Profile name is long (issue #31) - Fixed CQZ, ITUZ do not validate whether their entered value is a number (issue #75) - Fixed vucc, myvucc must be uppercase - Edit mode (issue #76) - Fixed Greyline-Map is very dark (issue #78) - Fixed DX Country is not saved properly when name is between S-Z (issue #79) - Fixed Bandmap call selection - only left mouse button (issue #82) - Fixed My Notes Copy & Paste - Rich Text (issue #83) - Fixed Font appearance in the context menu (issue #84) 2022/06/05 - 0.10.0 - [NEW] Bandmap shows XIT/RIT Freq - [NEW] Bandmap RX Mark Center (issue #69) - [NEW] Getting PTT State from RIG - only for CAT-controlled rigs - [NEW] PTT Shortchut - only for CAT-controlled rigs - Fixed Lost internet conneciton is not detected properly (issue #56) - Fixed Cannot manually edit QSO Date&Time (issue #66) - Fixed Field contents in capital letters (issue #67) - Fixed Band RX is not updated when RX Freq is edited (issue #72) - Fixed Stat Windget does not handle a date range correctly (issue #73) - Fixed eQSL card is incorreclty handled when a callsign contains special characters (issue #74) 2022/05/20 - 0.9.0 - [NEW] User-defined Spot Alerts - [NEW] User filter contains a new operator "Starts with" - [NEW] a real local time is shown for the DX callsign (issue #45) - [NEW] Lotw/eQSL registration info is showed from callbooks - [NEW] Added shortcuts for menu and tabs - [NEW] Bandmap - Switching a band view via Bandmap context menu (issue #57) - [CHANGED] - Network Notification format - logid field is included in all messages - Fixed issue with My Notes multiple lines edit/show mode (issue 39) - Fixed issue when GUI froze when Rig disconnect was called (issue #50) - Partially fixed a high CPU load when DXC is processed (issue #52) - Fixed crashes under Debian "bullseye" - 32bit (issue #55) - Fixed Bandmap Callsign selection margin (issue #61) - Fixed issue when it was not possible to enter RX/TX freq manually 2022/04/22 - 0.8.0 - RIT/XIT offset enable/disable detection (issue #26) - Fixed Rig Setting, Data Bits (issue #28) - Added default PWR for Rig profile (issue #30) - Fixed issue when GUI freezes during Rig connection (issue #32 & #33) - Fixed issue with an incorrect value of A-Index (issue #34) - Fixed ADI Import - incorrect _INTL fields import (issue #35) - Fixed isuue with an editing of bands in Setting dialog (issue #36) - Fixed issue with hamlib when get_pwr crashes for a network rig (issue #37) - Improved new QSO fields are filled from prev QSO (issue #40) - Added mode for a network Rig (issue #41) - Fixed warning - processing a new request but the previous one hasn't been completed (issue #42) - Fixed Info widget when Country name is long (issue #43) - Reordered column visibility Tabs (issue #46) - Improved Rig tunning when XIT/RIT is enabled (issue #47) 2022/04/08 - 0.7.0 - Settings - Ant/Rig/Rot are profiles - Reworked Ant/Rig/Rot Pages - Added RIG Properties (issue #18) - Rig Widget - added Band/Rig/Mode selection (issue #5) - added VFO/XIT/RIT/PWR Indicators (issue #20 and issue #23) - Rot Widget - works again - AZ/EL are stored to database if rot is connected (issue #22) - Fixed issues with the Statistic Widget Combos (issue 25) 2022/03/10 - 0.6.5 - Fixed missing modes in Setting Dialog (issue #11) - Fixed Station Profile text color in dark mode (issue #10) - Fixed DXCluster Server Combo (issue #12) - Fixed TAB focus on QSO Fields (issue #14) 2022/03/06 - 0.6.0 - [NEW] QSL - added import a file with QSL - with QSLr column - Fixed QLog start when Band is 3cm (too long start time due to the Bandmap drawing) (issue #6) - Fixed Rotator Widget Warning - map transformation issue (issue #8) - Changed Bandmap window narrow size (issue #3) - Changed User Filter Widget size - Removed Units from Logbook widget - Removed UTC string - Renamed RSTs, RSTr etc. (issue #4) - Renamed Main Menu Services->Service and Station->Equipment - Internal - reworked Service networking signal handling 2022/02/19 - 0.5.0 - DB: Added submodes used by FLDigi and added their mapping to LoTW, Clublog etc. - DB: Added all ADIF-defined modes/submodes - DB: Remapped QSO fields to International field - NAME_INTL etc. - DB is converted to a new schema to use *_INTL columns - non-INTL columns are recalculated from _INTL columns - accents removal function - GUI: Added Dark mode - GUI: Time/date format is controlled by Locale (except WSJTX, FLDigi inputs) - Import/Export: ADI do not export UTF-8 characters and *_INTL fields - Import/Export: ADX exports UTF-8 characters and *_INTL fields - Import/Export: Added Import of ADX file format - Logbook: Shows QSO summary as a Callsign's tooltip - Logbook: QSO time is shown with seconds; added timezone - New QSO: Added My notes - free text for your personal notes - Backup: Change backup format form ADI to ADX (ADX supports UTF-8) - Settings: WSJTX Port is changable 2022/01/09 - 0.4.0 - Stats: Added Show on Map - QSOs and Worked&Confirmed Grids - Stats: Stats are refreshed after every QSO - WSJTX: Remove TRX/Monitoring Status - Added Split mode - RX/TX RIG Offset - Added export of selected QSOs - Fixed FLdigi interface - CPPChecks & Clazy cleanup 2021/12/19 - 0.3.0 - Added new fields to Station Profile - Station Profile is stored in DB (not in QSetting) - Added VUCC field for QSO - Added BandMap marks (CTRL+M) - Clublog is uploaded the same way as EQSL and LOTW (modifications are resent too) - Clublog real-time upload was removed (temporary) - QRZ.com - Added callsign query and upload QSO support - Callbook cooperation - Primary&Secondary - Secondary used when Primary did not find 2021/11/28 - 0.2.0 - Initial fork changes - Changed Logging format (Time + severity + context) - Added Application singleton - GUI Tweaking - Changed Tab-key behaviour - DXC Widget - manu others - DXCCInfo is clean correctly when callsign is reset - Password are stored in a Secure Storage - Changed sorting criteria for BandFilter Combobox [Logbook View] - Added DXCluster Filters - Context Menu of the DXC - DXCluster spot time in UTC - Changed Style for Enable/Disable column in QTableView (Checkbox) - Fixed issue when mode combo is not refreshed after editing modes in Setting Dialog - Database export to ADIF before starting - Added Splashscreen - Hamlib rework - Reworked Hamlib Setting Dialog - Detailed Setting for Serial Port - Added Network Radio/Rot Support - Connect/Disconnect Rig and Rot - Improved Error Handling from Rig/Rot - Fixed Setting freq/mode/submode from/to GUI - Debug mode is more verbose and controled by QT_LOGGING_RULES variable - BandMap automatically clear spots when Aging Time is enabled - BandMap band is set based on NewContact Widget frequency - Many changes and improvements in LogWindow - Minor changes in the input QSO forms - Changed Editing QSO flow - Full ADIF parsing and storing - Added Column Visibility Setting - Added DEB control files - CTY is updated regularly - Main&NewContact Window reworking - Removed Contest Tab - Saving correct values for PROPMODE, QSL Sent/RCVD, SATs, IOTA, SOTA SIG, SIG_INFO, DOK - Added k-index, SFI saving - Added my Ant parameter - LoTW Import/Export is verified and improved - Added Rig offset support (support for Transverters) - Added Station Location Profiles - Added User-defined filters - Added eQSL upload/download ADIF, download QSL Image - Added Online Map Widget - Reworked WSJTX Widget - Added Spot Aging - Sorted based on Last Activity Time - Reworked Stats - Added Awards - Added Czech Translation ================================================ FILE: LICENSE ================================================ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . ZoneDetect ========== (https://github.com/BertoldVdb/ZoneDetect) Copyright (c) 2018-2019, Bertold Van den Bergh (vandenbergh@bertold.org) All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR DISTRIBUTOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. TimeZone Database ================= (https://github.com/evansiroky/timezone-boundary-builder/blob/master/DATA_LICENSE) Open Database License (ODbL) v1.0 Disclaimer Open Data Commons is not a law firm and does not provide legal services of any kind. Open Data Commons has no formal relationship with you. Your receipt of this document does not create any kind of agent-client relationship. Please seek the advice of a suitably qualified legal professional licensed to practice in your jurisdiction before using this document. No warranties and disclaimer of any damages. This information is provided ‘as is‘, and this site makes no warranties on the information provided. Any damages resulting from its use are disclaimed. Plain language summary A plain language summary of the Open Database License is available. Alternative formats: Plain Text ODC Open Database License (ODbL) Preamble The Open Database License (ODbL) is a license agreement intended to allow users to freely share, modify, and use this Database while maintaining this same freedom for others. Many databases are covered by copyright, and therefore this document licenses these rights. Some jurisdictions, mainly in the European Union, have specific rights that cover databases, and so the ODbL addresses these rights, too. Finally, the ODbL is also an agreement in contract for users of this Database to act in certain ways in return for accessing this Database. Databases can contain a wide variety of types of content (images, audiovisual material, and sounds all in the same database, for example), and so the ODbL only governs the rights over the Database, and not the contents of the Database individually. Licensors should use the ODbL together with another license for the contents, if the contents have a single set of rights that uniformly covers all of the contents. If the contents have multiple sets of different rights, Licensors should describe what rights govern what contents together in the individual record or in some other way that clarifies what rights apply. Sometimes the contents of a database, or the database itself, can be covered by other rights not addressed here (such as private contracts, trade mark over the name, or privacy rights / data protection rights over information in the contents), and so you are advised that you may have to consult other documents or clear other rights before doing activities not covered by this License. The Licensor (as defined below) and You (as defined below) agree as follows: 1.0 Definitions of Capitalised Words “Collective Database” – Means this Database in unmodified form as part of a collection of independent databases in themselves that together are assembled into a collective whole. A work that constitutes a Collective Database will not be considered a Derivative Database. “Convey” – As a verb, means Using the Database, a Derivative Database, or the Database as part of a Collective Database in any way that enables a Person to make or receive copies of the Database or a Derivative Database. Conveying does not include interaction with a user through a computer network, or creating and Using a Produced Work, where no transfer of a copy of the Database or a Derivative Database occurs. “Contents” – The contents of this Database, which includes the information, independent works, or other material collected into the Database. For example, the contents of the Database could be factual data or works such as images, audiovisual material, text, or sounds. “Database” – A collection of material (the Contents) arranged in a systematic or methodical way and individually accessible by electronic or other means offered under the terms of this License. “Database Directive” – Means Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended or succeeded. “Database Right” – Means rights resulting from the Chapter III (“sui generis”) rights in the Database Directive (as amended and as transposed by member states), which includes the Extraction and Re-utilisation of the whole or a Substantial part of the Contents, as well as any similar rights available in the relevant jurisdiction under Section 10.4. “Derivative Database” – Means a database based upon the Database, and includes any translation, adaptation, arrangement, modification, or any other alteration of the Database or of a Substantial part of the Contents. This includes, but is not limited to, Extracting or Re-utilising the whole or a Substantial part of the Contents in a new Database. “Extraction” – Means the permanent or temporary transfer of all or a Substantial part of the Contents to another medium by any means or in any form. “License” – Means this license agreement and is both a license of rights such as copyright and Database Rights and an agreement in contract. “Licensor” – Means the Person that offers the Database under the terms of this License. “Person” – Means a natural or legal person or a body of persons corporate or incorporate. “Produced Work” – a work (such as an image, audiovisual material, text, or sounds) resulting from using the whole or a Substantial part of the Contents (via a search or other query) from this Database, a Derivative Database, or this Database as part of a Collective Database. “Publicly” – means to Persons other than You or under Your control by either more than 50% ownership or by the power to direct their activities (such as contracting with an independent consultant). “Re-utilisation” – means any form of making available to the public all or a Substantial part of the Contents by the distribution of copies, by renting, by online or other forms of transmission. “Substantial” – Means substantial in terms of quantity or quality or a combination of both. The repeated and systematic Extraction or Re-utilisation of insubstantial parts of the Contents may amount to the Extraction or Re-utilisation of a Substantial part of the Contents. “Use” – As a verb, means doing any act that is restricted by copyright or Database Rights whether in the original medium or any other; and includes without limitation distributing, copying, publicly performing, publicly displaying, and preparing derivative works of the Database, as well as modifying the Database as may be technically necessary to use it in a different mode or format. “You” – Means a Person exercising rights under this License who has not previously violated the terms of this License with respect to the Database, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation. Words in the singular include the plural and vice versa. 2.0 What this License covers 2.1. Legal effect of this document. This License is: a. A license of applicable copyright and neighbouring rights; b. A license of the Database Right; and c. An agreement in contract between You and the Licensor. 2.2 Legal rights covered. This License covers the legal rights in the Database, including: a. Copyright. Any copyright or neighbouring rights in the Database. The copyright licensed includes any individual elements of the Database, but does not cover the copyright over the Contents independent of this Database. See Section 2.4 for details. Copyright law varies between jurisdictions, but is likely to cover: the Database model or schema, which is the structure, arrangement, and organisation of the Database, and can also include the Database tables and table indexes; the data entry and output sheets; and the Field names of Contents stored in the Database; b. Database Rights. Database Rights only extend to the Extraction and Re-utilisation of the whole or a Substantial part of the Contents. Database Rights can apply even when there is no copyright over the Database. Database Rights can also apply when the Contents are removed from the Database and are selected and arranged in a way that would not infringe any applicable copyright; and c. Contract. This is an agreement between You and the Licensor for access to the Database. In return you agree to certain conditions of use on this access as outlined in this License. 2.3 Rights not covered. a. This License does not apply to computer programs used in the making or operation of the Database; b. This License does not cover any patents over the Contents or the Database; and c. This License does not cover any trademarks associated with the Database. 2.4 Relationship to Contents in the Database. The individual items of the Contents contained in this Database may be covered by other rights, including copyright, patent, data protection, privacy, or personality rights, and this License does not cover any rights (other than Database Rights or in contract) in individual Contents contained in the Database. For example, if used on a Database of images (the Contents), this License would not apply to copyright over individual images, which could have their own separate licenses, or one single license covering all of the rights over the images. 3.0 Rights granted 3.1 Subject to the terms and conditions of this License, the Licensor grants to You a worldwide, royalty-free, non-exclusive, terminable (but only under Section 9) license to Use the Database for the duration of any applicable copyright and Database Rights. These rights explicitly include commercial use, and do not exclude any field of endeavour. To the extent possible in the relevant jurisdiction, these rights may be exercised in all media and formats whether now known or created in the future. The rights granted cover, for example: a. Extraction and Re-utilisation of the whole or a Substantial part of the Contents; b. Creation of Derivative Databases; c. Creation of Collective Databases; d. Creation of temporary or permanent reproductions by any means and in any form, in whole or in part, including of any Derivative Databases or as a part of Collective Databases; and e. Distribution, communication, display, lending, making available, or performance to the public by any means and in any form, in whole or in part, including of any Derivative Database or as a part of Collective Databases. 3.2 Compulsory license schemes. For the avoidance of doubt: a. Non-waivable compulsory license schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; b. Waivable compulsory license schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor waives the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; and, c. Voluntary license schemes. The Licensor waives the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License. 3.3 The right to release the Database under different terms, or to stop distributing or making available the Database, is reserved. Note that this Database may be multiple-licensed, and so You may have the choice of using alternative licenses for this Database. Subject to Section 10.4, all other rights not expressly granted by Licensor are reserved. 4.0 Conditions of Use 4.1 The rights granted in Section 3 above are expressly made subject to Your complying with the following conditions of use. These are important conditions of this License, and if You fail to follow them, You will be in material breach of its terms. 4.2 Notices. If You Publicly Convey this Database, any Derivative Database, or the Database as part of a Collective Database, then You must: a. Do so only under the terms of this License or another license permitted under Section 4.4; b. Include a copy of this License (or, as applicable, a license permitted under Section 4.4) or its Uniform Resource Identifier (URI) with the Database or Derivative Database, including both in the Database or Derivative Database and in any relevant documentation; and c. Keep intact any copyright or Database Right notices and notices that refer to this License. d. If it is not possible to put the required notices in a particular file due to its structure, then You must include the notices in a location (such as a relevant directory) where users would be likely to look for it. 4.3 Notice for using output (Contents). Creating and Using a Produced Work does not require the notice in Section 4.2. However, if you Publicly Use a Produced Work, You must include a notice associated with the Produced Work reasonably calculated to make any Person that uses, views, accesses, interacts with, or is otherwise exposed to the Produced Work aware that Content was obtained from the Database, Derivative Database, or the Database as part of a Collective Database, and that it is available under this License. a. Example notice. The following text will satisfy notice under Section 4.3: Contains information from DATABASE NAME, which is made available here under the Open Database License (ODbL). DATABASE NAME should be replaced with the name of the Database and a hyperlink to the URI of the Database. “Open Database License” should contain a hyperlink to the URI of the text of this License. If hyperlinks are not possible, You should include the plain text of the required URI’s with the above notice. 4.4 Share alike. a. Any Derivative Database that You Publicly Use must be only under the terms of: i. This License; ii. A later version of this License similar in spirit to this License; or iii. A compatible license. If You license the Derivative Database under one of the licenses mentioned in (iii), You must comply with the terms of that license. b. For the avoidance of doubt, Extraction or Re-utilisation of the whole or a Substantial part of the Contents into a new database is a Derivative Database and must comply with Section 4.4. c. Derivative Databases and Produced Works. A Derivative Database is Publicly Used and so must comply with Section 4.4. if a Produced Work created from the Derivative Database is Publicly Used. d. Share Alike and additional Contents. For the avoidance of doubt, You must not add Contents to Derivative Databases under Section 4.4 a that are incompatible with the rights granted under this License. e. Compatible licenses. Licensors may authorise a proxy to determine compatible licenses under Section 4.4 a iii. If they do so, the authorised proxy’s public statement of acceptance of a compatible license grants You permission to use the compatible license. 4.5 Limits of Share Alike. The requirements of Section 4.4 do not apply in the following: a. For the avoidance of doubt, You are not required to license Collective Databases under this License if You incorporate this Database or a Derivative Database in the collection, but this License still applies to this Database or a Derivative Database as a part of the Collective Database; b. Using this Database, a Derivative Database, or this Database as part of a Collective Database to create a Produced Work does not create a Derivative Database for purposes of Section 4.4; and c. Use of a Derivative Database internally within an organisation is not to the public and therefore does not fall under the requirements of Section 4.4. 4.6 Access to Derivative Databases. If You Publicly Use a Derivative Database or a Produced Work from a Derivative Database, You must also offer to recipients of the Derivative Database or Produced Work a copy in a machine readable form of: a. The entire Derivative Database; or b. A file containing all of the alterations made to the Database or the method of making the alterations to the Database (such as an algorithm), including any additional Contents, that make up all the differences between the Database and the Derivative Database. The Derivative Database (under a.) or alteration file (under b.) must be available at no more than a reasonable production cost for physical distributions and free of charge if distributed over the internet. 4.7 Technological measures and additional terms a. This License does not allow You to impose (except subject to Section 4.7 b.) any terms or any technological measures on the Database, a Derivative Database, or the whole or a Substantial part of the Contents that alter or restrict the terms of this License, or any rights granted under it, or have the effect or intent of restricting the ability of any person to exercise those rights. b. Parallel distribution. You may impose terms or technological measures on the Database, a Derivative Database, or the whole or a Substantial part of the Contents (a “Restricted Database”) in contravention of Section 4.74 a. only if You also make a copy of the Database or a Derivative Database available to the recipient of the Restricted Database: i. That is available without additional fee; ii. That is available in a medium that does not alter or restrict the terms of this License, or any rights granted under it, or have the effect or intent of restricting the ability of any person to exercise those rights (an “Unrestricted Database”); and iii. The Unrestricted Database is at least as accessible to the recipient as a practical matter as the Restricted Database. c. For the avoidance of doubt, You may place this Database or a Derivative Database in an authenticated environment, behind a password, or within a similar access control scheme provided that You do not alter or restrict the terms of this License or any rights granted under it or have the effect or intent of restricting the ability of any person to exercise those rights. 4.8 Licensing of others. You may not sublicense the Database. Each time You communicate the Database, the whole or Substantial part of the Contents, or any Derivative Database to anyone else in any way, the Licensor offers to the recipient a license to the Database on the same terms and conditions as this License. You are not responsible for enforcing compliance by third parties with this License, but You may enforce any rights that You have over a Derivative Database. You are solely responsible for any modifications of a Derivative Database made by You or another Person at Your direction. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. 5.0 Moral rights 5.1 Moral rights. This section covers moral rights, including any rights to be identified as the author of the Database or to object to treatment that would otherwise prejudice the author’s honour and reputation, or any other derogatory treatment: a. For jurisdictions allowing waiver of moral rights, Licensor waives all moral rights that Licensor may have in the Database to the fullest extent possible by the law of the relevant jurisdiction under Section 10.4; b. If waiver of moral rights under Section 5.1 a in the relevant jurisdiction is not possible, Licensor agrees not to assert any moral rights over the Database and waives all claims in moral rights to the fullest extent possible by the law of the relevant jurisdiction under Section 10.4; and c. For jurisdictions not allowing waiver or an agreement not to assert moral rights under Section 5.1 a and b, the author may retain their moral rights over certain aspects of the Database. Please note that some jurisdictions do not allow for the waiver of moral rights, and so moral rights may still subsist over the Database in some jurisdictions. 6.0 Fair dealing, Database exceptions, and other rights not affected 6.1 This License does not affect any rights that You or anyone else may independently have under any applicable law to make any use of this Database, including without limitation: a. Exceptions to the Database Right including: Extraction of Contents from non-electronic Databases for private purposes, Extraction for purposes of illustration for teaching or scientific research, and Extraction or Re-utilisation for public security or an administrative or judicial procedure. b. Fair dealing, fair use, or any other legally recognised limitation or exception to infringement of copyright or other applicable laws. 6.2 This License does not affect any rights of lawful users to Extract and Re-utilise insubstantial parts of the Contents, evaluated quantitatively or qualitatively, for any purposes whatsoever, including creating a Derivative Database (subject to other rights over the Contents, see Section 2.4). The repeated and systematic Extraction or Re-utilisation of insubstantial parts of the Contents may however amount to the Extraction or Re-utilisation of a Substantial part of the Contents. 7.0 Warranties and Disclaimer 7.1 The Database is licensed by the Licensor “as is” and without any warranty of any kind, either express, implied, or arising by statute, custom, course of dealing, or trade usage. Licensor specifically disclaims any and all implied warranties or conditions of title, non-infringement, accuracy or completeness, the presence or absence of errors, fitness for a particular purpose, merchantability, or otherwise. Some jurisdictions do not allow the exclusion of implied warranties, so this exclusion may not apply to You. 8.0 Limitation of liability 8.1 Subject to any liability that may not be excluded or limited by law, the Licensor is not liable for, and expressly excludes, all liability for loss or damage however and whenever caused to anyone by any use under this License, whether by You or by anyone else, and whether caused by any fault on the part of the Licensor or not. This exclusion of liability includes, but is not limited to, any special, incidental, consequential, punitive, or exemplary damages such as loss of revenue, data, anticipated profits, and lost business. This exclusion applies even if the Licensor has been advised of the possibility of such damages. 8.2 If liability may not be excluded by law, it is limited to actual and direct financial loss to the extent it is caused by proved negligence on the part of the Licensor. 9.0 Termination of Your rights under this License 9.1 Any breach by You of the terms and conditions of this License automatically terminates this License with immediate effect and without notice to You. For the avoidance of doubt, Persons who have received the Database, the whole or a Substantial part of the Contents, Derivative Databases, or the Database as part of a Collective Database from You under this License will not have their licenses terminated provided their use is in full compliance with this License or a license granted under Section 4.8 of this License. Sections 1, 2, 7, 8, 9 and 10 will survive any termination of this License. 9.2 If You are not in breach of the terms of this License, the Licensor will not terminate Your rights under it. 9.3 Unless terminated under Section 9.1, this License is granted to You for the duration of applicable rights in the Database. 9.4 Reinstatement of rights. If you cease any breach of the terms and conditions of this License, then your full rights under this License will be reinstated: a. Provisionally and subject to permanent termination until the 60th day after cessation of breach; b. Permanently on the 60th day after cessation of breach unless otherwise reasonably notified by the Licensor; or c. Permanently if reasonably notified by the Licensor of the violation, this is the first time You have received notice of violation of this License from the Licensor, and You cure the violation prior to 30 days after your receipt of the notice. Persons subject to permanent termination of rights are not eligible to be a recipient and receive a license under Section 4.8. 9.5 Notwithstanding the above, Licensor reserves the right to release the Database under different license terms or to stop distributing or making available the Database. Releasing the Database under different license terms or stopping the distribution of the Database will not withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above. 10.0 General 10.1 If any provision of this License is held to be invalid or unenforceable, that must not affect the validity or enforceability of the remainder of the terms and conditions of this License and each remaining provision of this License shall be valid and enforced to the fullest extent permitted by law. 10.2 This License is the entire agreement between the parties with respect to the rights granted here over the Database. It replaces any earlier understandings, agreements or representations with respect to the Database. 10.3 If You are in breach of the terms of this License, You will not be entitled to rely on the terms of this License or to complain of any breach by the Licensor. 10.4 Choice of law. This License takes effect in and will be governed by the laws of the relevant jurisdiction in which the License terms are sought to be enforced. If the standard suite of rights granted under applicable copyright law and Database Rights in the relevant jurisdiction includes additional rights not granted under this License, these additional rights are granted in this License in order to meet the terms of this License. ================================================ FILE: QLog.pro ================================================ #------------------------------------------------- # # Project created by QtCreator 2019-06-10T09:13:09 # #------------------------------------------------- QT += core gui sql network xml charts webenginewidgets serialport dbus quickwidgets webchannel websockets printsupport greaterThan(QT_MAJOR_VERSION, 5): QT += widgets TARGET = qlog TEMPLATE = app VERSION = 0.50.0 DEFINES += VERSION=\\\"$$VERSION\\\" # Define paths to HAMLIB. Leave empty if system libraries should be used #HAMLIBINCLUDEPATH = #HAMLIBLIBPATH = # Define Hamlib version. Leave empty if pkg-config should detect the version (lib must be installed and registered) #HAMLIBVERSION_MAJOR = #HAMLIBVERSION_MINOR = #HAMLIBVERSION_PATCH = # Define paths to pthread - needed for Hamlib4.5 and later. Leave empty if system libraries should be used #PTHREADINCLUDEPATH = #PTHREADLIBPATH = # Define paths to QTKeyChain. Leave empty if system libraries should be used #QTKEYCHAININCLUDEPATH = #QTKEYCHAINLIBPATH = # Define paths to zlib - Leave empty if system libraries should be used #ZLIBINCLUDEPATH = #ZLIBLIBPATH = # Define paths to OpenSSL - Leave empty if system libraries should be used #OPENSSLINCLUDEPATH = #OPENSSLLIBPATH = # The following define makes your compiler emit warnings if you use # any feature of Qt which has been marked as deprecated (the exact warnings # depend on your compiler). Please consult the documentation of the # deprecated API in order to know how to port your code away from it. DEFINES += QT_DEPRECATED_WARNINGS QT_MESSAGELOGCONTEXT # You can also make your code fail to compile if you use deprecated APIs. # In order to do so, uncomment the following line. # You can also select to disable deprecated APIs only up to a certain version of Qt. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 macx:QT_CONFIG -= no-pkg-config CONFIG += c++11 force_debug_info #CONFIG += sanitizer sanitize_address CONFIG *= link_pkgconfig SOURCES += \ awards/AwardDefinition.cpp \ awards/AwardDXCC.cpp \ awards/AwardGridsquare.cpp \ awards/AwardIOTA.cpp \ awards/AwardITU.cpp \ awards/AwardJapan.cpp \ awards/AwardNZ.cpp \ awards/AwardRDA.cpp \ awards/AwardSpanishDME.cpp \ awards/AwardUKD.cpp \ awards/AwardUSCounty.cpp \ awards/AwardPOTAActivator.cpp \ awards/AwardPOTAHunter.cpp \ awards/AwardSOTA.cpp \ awards/SecondarySubdivisionAward.cpp \ awards/AwardWAC.cpp \ awards/AwardWAS.cpp \ awards/AwardWAZ.cpp \ awards/AwardWPX.cpp \ awards/AwardWWFF.cpp \ awards/BandTableAward.cpp \ core/AlertEvaluator.cpp \ core/AppGuard.cpp \ core/CallbookManager.cpp \ core/CredentialStore.cpp \ core/FileCompressor.cpp \ core/FldigiTCPServer.cpp \ core/LOVDownloader.cpp \ core/LogDatabase.cpp \ core/LogLocale.cpp \ core/LogParam.cpp \ core/MembershipQE.cpp \ core/Migration.cpp \ core/NetworkNotification.cpp \ core/PasswordCipher.cpp \ core/PlatformParameterManager.cpp \ core/PotaQE.cpp \ core/PropConditions.cpp \ core/QSLPrintLabelRenderer.cpp \ core/QSLStorage.cpp \ core/QSOFilterManager.cpp \ core/WsjtxUDPReceiver.cpp \ core/debug.cpp \ core/EmergencyFrequency.cpp \ core/main.cpp \ core/zonedetect.c \ cwkey/CWKeyer.cpp \ cwkey/drivers/CWCatKey.cpp \ cwkey/drivers/CWDaemonKey.cpp \ cwkey/drivers/CWDummyKey.cpp \ cwkey/drivers/CWFldigiKey.cpp \ cwkey/drivers/CWKey.cpp \ cwkey/drivers/CWWinKey.cpp \ data/ActivityProfile.cpp \ data/AntProfile.cpp \ data/BandPlan.cpp \ data/Accents.cpp \ data/CWKeyProfile.cpp \ data/CWShortcutProfile.cpp \ data/Callsign.cpp \ data/Data.cpp \ data/DxServerString.cpp \ data/Gridsquare.cpp \ data/HostsPortString.cpp \ data/MainLayoutProfile.cpp \ data/RigProfile.cpp \ data/RotProfile.cpp \ data/RotUsrButtonsProfile.cpp \ data/SerialPort.cpp \ data/StationProfile.cpp \ data/UpdatableSQLRecord.cpp \ logformat/AdiFormat.cpp \ logformat/AdxFormat.cpp \ logformat/CabrilloFormat.cpp \ logformat/CSVFormat.cpp \ logformat/JsonFormat.cpp \ logformat/LogFormat.cpp \ logformat/PotaAdiFormat.cpp \ models/AlertTableModel.cpp \ models/AwardsTableModel.cpp \ models/DxccTableModel.cpp \ models/LogbookModel.cpp \ models/RigTypeModel.cpp \ models/RotTypeModel.cpp \ models/SearchFilterProxyModel.cpp \ models/ShortcutEditorModel.cpp \ models/SqlListModel.cpp \ models/WsjtxTableModel.cpp \ rig/Rig.cpp \ rig/RigCaps.cpp \ rig/RigctldManager.cpp \ rig/drivers/FlrigRigDrv.cpp \ rig/drivers/GenericRigDrv.cpp \ rig/drivers/HamlibRigDrv.cpp \ rig/drivers/TCIRigDrv.cpp \ rotator/RotCaps.cpp \ rotator/Rotator.cpp \ rotator/drivers/GenericRotDrv.cpp \ rotator/drivers/HamlibRotDrv.cpp \ rotator/drivers/PSTRotDrv.cpp \ service/GenericCallbook.cpp \ service/GenericQSLDownloader.cpp \ service/GenericQSOUploader.cpp \ service/cloudlog/Cloudlog.cpp \ service/clublog/ClubLog.cpp \ service/eqsl/Eqsl.cpp \ service/hamqth/HamQTH.cpp \ service/hrdlog/HRDLog.cpp \ service/kstchat/KSTChat.cpp \ service/lotw/Lotw.cpp \ service/potaapp/PotaApp.cpp \ service/qrzcom/QRZ.cpp \ ui/ActivityEditor.cpp \ ui/AlertRuleDetail.cpp \ ui/AlertSettingDialog.cpp \ ui/AlertWidget.cpp \ ui/AwardsDialog.cpp \ ui/DXCCSubmissionDialog.cpp \ ui/BandmapWidget.cpp \ ui/CWConsoleWidget.cpp \ ui/ChatWidget.cpp \ ui/ClockWidget.cpp \ ui/ColumnSettingDialog.cpp \ ui/DevToolsDialog.cpp \ ui/DownloadQSLDialog.cpp \ ui/DxFilterDialog.cpp \ ui/DxWidget.cpp \ ui/DxccTableWidget.cpp \ ui/EditActivitiesDialog.cpp \ ui/CabrilloExportDialog.cpp \ ui/CabrilloTemplateDialog.cpp \ ui/ExportDialog.cpp \ ui/ExportPasswordDialog.cpp \ ui/LoadDatabaseDialog.cpp \ ui/PlatformSettingsDialog.cpp \ ui/QSLGalleryDialog.cpp \ ui/QSLPrintLabelDialog.cpp \ ui/RigctldAdvancedDialog.cpp \ ui/ImportDialog.cpp \ ui/InputPasswordDialog.cpp \ ui/KSTChatWidget.cpp \ ui/KSTHighlightRuleDetail.cpp \ ui/KSTHighlighterSettingDialog.cpp \ ui/LogbookWidget.cpp \ ui/MainWindow.cpp \ ui/MapWebChannelHandler.cpp \ ui/MapWidget.cpp \ ui/ModeSelectionController.cpp \ ui/NewContactWidget.cpp \ ui/OnlineMapWidget.cpp \ ui/PaperQSLDialog.cpp \ ui/ProfileImageWidget.cpp \ ui/QSLImportStatDialog.cpp \ ui/QSODetailDialog.cpp \ ui/QSOFilterDetail.cpp \ ui/QSOFilterDialog.cpp \ ui/QTableQSOView.cpp \ ui/RigWidget.cpp \ ui/RotatorWidget.cpp \ ui/SettingsDialog.cpp \ ui/ShowUploadDialog.cpp \ ui/StatisticsWidget.cpp \ ui/UploadQSODialog.cpp \ ui/WebEnginePage.cpp \ ui/WsjtxFilterDialog.cpp \ ui/WsjtxWidget.cpp \ ui/component/BaseDoubleSpinBox.cpp \ ui/component/EditLine.cpp \ ui/component/FreqQSpinBox.cpp \ ui/component/MultiselectCompleter.cpp \ ui/component/RepeatButton.cpp \ ui/component/SmartSearchBox.cpp \ ui/component/SqlHighlighter.cpp \ ui/component/SwitchButton.cpp HEADERS += \ awards/AwardDefinition.h \ awards/AwardDXCC.h \ awards/AwardGridsquare.h \ awards/AwardIOTA.h \ awards/AwardITU.h \ awards/AwardJapan.h \ awards/AwardNZ.h \ awards/AwardRDA.h \ awards/AwardSpanishDME.h \ awards/AwardUKD.h \ awards/AwardUSCounty.h \ awards/AwardPOTAActivator.h \ awards/AwardPOTAHunter.h \ awards/AwardSOTA.h \ awards/SecondarySubdivisionAward.h \ awards/AwardWAC.h \ awards/AwardWAS.h \ awards/AwardWAZ.h \ awards/AwardWPX.h \ awards/AwardWWFF.h \ awards/BandTableAward.h \ core/AlertEvaluator.h \ core/AppGuard.h \ core/CallbookManager.h \ core/CredentialStore.h \ core/FileCompressor.h \ core/FldigiTCPServer.h \ core/LOVDownloader.h \ core/LogDatabase.h \ core/LogLocale.h \ core/LogParam.h \ core/MembershipQE.h \ core/Migration.h \ core/NetworkNotification.h \ core/PasswordCipher.h \ core/PlatformParameterManager.h \ core/PotaQE.h \ core/PropConditions.h \ core/QSLPrintLabelRenderer.h \ core/QSLStorage.h \ core/QSOFilterManager.h \ core/QuadKeyCache.h \ core/WsjtxUDPReceiver.h \ core/csv.hpp \ core/debug.h \ core/EmergencyFrequency.h \ core/zonedetect.h \ cwkey/CWKeyer.h \ cwkey/drivers/CWCatKey.h \ cwkey/drivers/CWDaemonKey.h \ cwkey/drivers/CWDummyKey.h \ cwkey/drivers/CWFldigiKey.h \ cwkey/drivers/CWKey.h \ cwkey/drivers/CWWinKey.h \ data/ActivityProfile.h \ data/AntProfile.h \ data/Band.h \ data/BandPlan.h \ data/CWKeyProfile.h \ data/CWShortcutProfile.h \ data/Callsign.h \ data/Data.h \ data/DxServerString.h \ data/DxSpot.h \ data/Dxcc.h \ data/Gridsquare.h \ data/HostsPortString.h \ data/MainLayoutProfile.h \ data/POTAEntity.h \ data/POTASpot.h \ data/ProfileManager.h \ data/RigProfile.h \ data/RotProfile.h \ data/RotUsrButtonsProfile.h \ data/SOTAEntity.h \ data/SerialPort.h \ data/SpotAlert.h \ data/StationProfile.h \ data/ToAllSpot.h \ data/UpdatableSQLRecord.h \ data/WCYSpot.h \ data/WWFFEntity.h \ data/WWVSpot.h \ data/WsjtxDecode.h \ data/WsjtxEntry.h \ data/WsjtxLog.h \ data/WsjtxLogADIF.h \ data/WsjtxStatus.h \ logformat/AdiFormat.h \ logformat/AdxFormat.h \ logformat/CabrilloFormat.h \ logformat/CSVFormat.h \ logformat/JsonFormat.h \ logformat/LogFormat.h \ logformat/PotaAdiFormat.h \ models/AlertTableModel.h \ models/AwardsTableModel.h \ models/DxccTableModel.h \ models/LogbookModel.h \ models/RigTypeModel.h \ models/RotTypeModel.h \ models/SearchFilterProxyModel.h \ models/ShortcutEditorModel.h \ models/SqlListModel.h \ models/WsjtxTableModel.h \ rig/Rig.h \ rig/RigCaps.h \ rig/RigctldManager.h \ rig/drivers/FlrigRigDrv.h \ rig/drivers/GenericRigDrv.h \ rig/drivers/HamlibRigDrv.h \ rig/drivers/TCIRigDrv.h \ rig/macros.h \ rotator/RotCaps.h \ rotator/Rotator.h \ rotator/drivers/GenericRotDrv.h \ rotator/drivers/HamlibRotDrv.h \ rotator/drivers/PSTRotDrv.h \ service/GenericCallbook.h \ service/GenericQSLDownloader.h \ service/GenericQSOUploader.h \ service/cloudlog/Cloudlog.h \ service/clublog/ClubLog.h \ service/eqsl/Eqsl.h \ service/hamqth/HamQTH.h \ service/hrdlog/HRDLog.h \ service/kstchat/KSTChat.h \ service/lotw/Lotw.h \ service/potaapp/PotaApp.h \ service/qrzcom/QRZ.h \ ui/ActivityEditor.h \ ui/AlertRuleDetail.h \ ui/AlertSettingDialog.h \ ui/AlertWidget.h \ ui/AwardsDialog.h \ ui/DXCCSubmissionDialog.h \ ui/BandmapWidget.h \ ui/CWConsoleWidget.h \ ui/ChatWidget.h \ ui/ClockWidget.h \ ui/ColumnSettingDialog.h \ ui/DevToolsDialog.h \ ui/DownloadQSLDialog.h \ ui/DxFilterDialog.h \ ui/DxWidget.h \ ui/DxccTableWidget.h \ ui/EditActivitiesDialog.h \ ui/CabrilloExportDialog.h \ ui/CabrilloTemplateDialog.h \ ui/ExportDialog.h \ ui/ExportPasswordDialog.h \ ui/LoadDatabaseDialog.h \ ui/PlatformSettingsDialog.h \ ui/QSLGalleryDialog.h \ ui/QSLPrintLabelDialog.h \ ui/RigctldAdvancedDialog.h \ ui/ImportDialog.h \ ui/InputPasswordDialog.h \ ui/KSTChatWidget.h \ ui/KSTHighlightRuleDetail.h \ ui/KSTHighlighterSettingDialog.h \ ui/LogbookWidget.h \ ui/MainWindow.h \ ui/MapWebChannelHandler.h \ ui/MapWidget.h \ ui/ModeSelectionController.h \ ui/NewContactWidget.h \ ui/OnlineMapWidget.h \ ui/PaperQSLDialog.h \ ui/ProfileImageWidget.h \ ui/QSLImportStatDialog.h \ ui/QSODetailDialog.h \ ui/QSOFilterDetail.h \ ui/QSOFilterDialog.h \ ui/QTableQSOView.h \ ui/ShowUploadDialog.h \ ui/SplashScreen.h \ ui/RigWidget.h \ ui/RotatorWidget.h \ ui/SettingsDialog.h \ ui/StatisticsWidget.h \ ui/UploadQSODialog.h \ ui/WebEnginePage.h \ ui/WsjtxFilterDialog.h \ ui/WsjtxWidget.h \ i18n/dbstrings.tri \ i18n/datastrings.tri \ ui/component/BaseDoubleSpinBox.h \ ui/component/ButtonStyle.h \ ui/component/EditLine.h \ ui/component/FreqQSpinBox.h \ ui/component/MultiselectCompleter.h \ ui/component/RepeatButton.h \ ui/component/ShutdownAwareWidget.h \ ui/component/SmartSearchBox.h \ ui/component/SqlHighlighter.h \ ui/component/StyleItemDelegate.h \ ui/component/SwitchButton.h FORMS += \ ui/ActivityEditor.ui \ ui/AlertRuleDetail.ui \ ui/AlertSettingDialog.ui \ ui/AlertWidget.ui \ ui/AwardsDialog.ui \ ui/DXCCSubmissionDialog.ui \ ui/BandmapWidget.ui \ ui/CWConsoleWidget.ui \ ui/ChatWidget.ui \ ui/ClockWidget.ui \ ui/ColumnSettingDialog.ui \ ui/ColumnSettingSimpleDialog.ui \ ui/DevToolsDialog.ui \ ui/DownloadQSLDialog.ui \ ui/DxFilterDialog.ui \ ui/DxWidget.ui \ ui/EditActivitiesDialog.ui \ ui/CabrilloExportDialog.ui \ ui/CabrilloTemplateDialog.ui \ ui/ExportDialog.ui \ ui/ExportPasswordDialog.ui \ ui/LoadDatabaseDialog.ui \ ui/PlatformSettingsDialog.ui \ ui/QSLGalleryDialog.ui \ ui/QSLPrintLabelDialog.ui \ ui/RigctldAdvancedDialog.ui \ ui/ImportDialog.ui \ ui/InputPasswordDialog.ui \ ui/KSTChatWidget.ui \ ui/KSTHighlightRuleDetail.ui \ ui/KSTHighlighterSettingDialog.ui \ ui/LogbookWidget.ui \ ui/MainWindow.ui \ ui/NewContactWidget.ui \ ui/PaperQSLDialog.ui \ ui/ProfileImageWidget.ui \ ui/QSLImportStatDialog.ui \ ui/QSODetailDialog.ui \ ui/QSOFilterDetail.ui \ ui/QSOFilterDialog.ui \ ui/RigWidget.ui \ ui/RotatorWidget.ui \ ui/SettingsDialog.ui \ ui/ShowUploadDialog.ui \ ui/StatisticsWidget.ui \ ui/UploadQSODialog.ui \ ui/WsjtxFilterDialog.ui \ ui/WsjtxWidget.ui RESOURCES += \ i18n/i18n.qrc \ res/flags/flags.qrc \ res/icons/icons.qrc \ res/res.qrc OTHER_FILES += \ res/stylesheet.css \ res/qlog.rc \ res/qlog.desktop \ res/qlog.1 \ res/io.github.foldynl.QLog.metainfo.xml TRANSLATIONS = i18n/qlog_cs.ts \ i18n/qlog_de.ts \ i18n/qlog_es.ts \ i18n/qlog_fr.ts \ i18n/qlog_it.ts \ i18n/qlog_zh_CN.ts RC_ICONS = res/qlog.ico ICON = res/qlog.icns # https://stackoverflow.com/questions/56734224/qmake-and-pkg-config?rq=1 defineReplace(findPackage) { pkg = $${1}Version !defined($$pkg, var) { $$pkg = $$system($$pkgConfigExecutable() --modversion $$1) isEmpty($$pkg): $$pkg = 0 cache($$pkg, stash) } return($$eval($$pkg)) } defineReplace(removeNonDigi) { output = $$1 output = $$replace(output, [^0-9], " ") output = $$split(output, " ") return($$member(output, 0)) } isEmpty(HAMLIBVERSION_MAJOR) { HAMLIBVERSIONSTRING = $$findPackage(hamlib) HAMLIBVERSIONS = $$split(HAMLIBVERSIONSTRING, ".") HAMLIBVERSION_MAJOR = $$member(HAMLIBVERSIONS, 0) HAMLIBVERSION_MINOR = $$member(HAMLIBVERSIONS, 1) HAMLIBVERSION_PATCH = $$member(HAMLIBVERSIONS, 2) } HAMLIBVERSION_MINOR = $$removeNonDigi($$HAMLIBVERSION_MINOR) isEmpty(HAMLIBVERSION_MINOR){ HAMLIBVERSION_MINOR=0 } HAMLIBVERSION_PATCH = $$removeNonDigi($$HAMLIBVERSION_PATCH) isEmpty(HAMLIBVERSION_PATCH){ HAMLIBVERSION_PATCH=0 } !isEmpty(HAMLIBINCLUDEPATH) { INCLUDEPATH += $$HAMLIBINCLUDEPATH } !isEmpty(QTKEYCHAININCLUDEPATH) { INCLUDEPATH += $$QTKEYCHAININCLUDEPATH } !isEmpty(PTHREADINCLUDEPATH) { INCLUDEPATH += $$PTHREADINCLUDEPATH } !isEmpty(ZLIBINCLUDEPATH) { INCLUDEPATH += $$ZLIBINCLUDEPATH } !isEmpty(OPENSSLINCLUDEPATH) { INCLUDEPATH += $$OPENSSLINCLUDEPATH } !isEmpty(HAMLIBLIBPATH) { LIBS += -L$$HAMLIBLIBPATH } !isEmpty(QTKEYCHAINLIBPATH) { LIBS += -L$$QTKEYCHAINLIBPATH } !isEmpty(PTHREADINCLUDEPATH) { LIBS += -L$$PTHREADINCLUDEPATH } !isEmpty(ZLIBLIBPATH) { LIBS += -L$$ZLIBLIBPATH } !isEmpty(OPENSSLLIBPATH) { LIBS += -L$$OPENSSLLIBPATH } unix:!macx { isEmpty(PREFIX) { PREFIX = /usr/local } target.path = $$PREFIX/bin desktop.path = $$PREFIX/share/applications/ desktop.files += res/$${TARGET}.desktop manpage.path = $$PREFIX/share/man/man1 manpage.files += res/$${TARGET}.1 icon.path = $$PREFIX/share/icons/hicolor/256x256/apps icon.files += res/$${TARGET}.png metainfo.path = $$PREFIX/share/metainfo/ metainfo.files += res/io.github.foldynl.QLog.metainfo.xml INSTALLS += target desktop icon metainfo manpage INCLUDEPATH += /usr/local/include PKGCONFIG += openssl LIBS += -L/usr/local/lib -lhamlib -lsqlite3 -lz equals(QT_MAJOR_VERSION, 6): LIBS += -lqt6keychain equals(QT_MAJOR_VERSION, 5): LIBS += -lqt5keychain } macx: { # This allows the app to be shipped in a non-bundeled version !isEmpty(PREFIX) { target.path = $$PREFIX INSTALLS += target } INCLUDEPATH += /usr/local/include /opt/homebrew/include /opt/local/include LIBS += -L/usr/local/lib -L/opt/homebrew/lib -lhamlib -lsqlite3 -lz -L/opt/local/lib -lssl -lcrypto equals(QT_MAJOR_VERSION, 6): LIBS += -lqt6keychain equals(QT_MAJOR_VERSION, 5): LIBS += -lqt5keychain DISTFILES += } win32: { INCLUDEPATH += \ /usr/local/include \ $$[QT_INSTALL_PREFIX]/../Src/qtbase/src/3rdparty/sqlite/ SOURCES += \ rig/drivers/OmnirigRigDrv.cpp \ rig/drivers/Omnirigv2RigDrv.cpp \ $$[QT_INSTALL_PREFIX]/../Src/qtbase/src/3rdparty/sqlite/sqlite3.c HEADERS += \ rig/drivers/OmnirigRigDrv.h \ rig/drivers/Omnirigv2RigDrv.h \ rig/drivers/OmniRigEventSink.h \ $$[QT_INSTALL_PREFIX]/../Src/qtbase/src/3rdparty/sqlite/sqlite3.h TARGET = qlog QMAKE_TARGET_COMPANY = OK1MLG QMAKE_TARGET_DESCRIPTION = Hamradio logging LIBS += -lws2_32 -llibhamlib-4 -lzlib -llibssl -llibcrypto equals(QT_MAJOR_VERSION, 6): LIBS += -lqt6keychain equals(QT_MAJOR_VERSION, 5): LIBS += -lqt5keychain DEFINES += WIN32_LEAN_AND_MEAN DEFINES += ZD_EXPORT= } DEFINES += HAMLIBVERSION_MAJOR=$$HAMLIBVERSION_MAJOR DEFINES += HAMLIBVERSION_MINOR=$$HAMLIBVERSION_MINOR DEFINES += HAMLIBVERSION_PATCH=$$HAMLIBVERSION_PATCH DISTFILES += \ Changelog \ i18n/dbstrings.tri \ res/data/sat_modes ================================================ FILE: README.md ================================================ # QLog QLog is an Amateur Radio logging application for Linux, Windows. It is based on the Qt framework and uses SQLite as database backend. QLogs aims to be as simple as possible, but to provide everything the operator expects from the log to be. This log is not currently focused on contests. ![Screenshot](https://foldynl.github.io/QLog/screens/qlog_main.png) ## Features - Customizable GUI - Rig control via Hamlib, Omnirig v1 (Windows only), Omnirig v2 (Windows only), TCI, FLRig - Rotator control via Hamlib, PSTRotator - HamQTH and QRZ.com callbook integration - DX cluster integration - **LoTW**, **eQSL**, **QRZ.com**, **Clublog**, **HRDLog.net**, **ON4KST Chat**, **Cloudlog/Wavelog** integration (**eQSL includes QSL pictures download**) - **Secure Password Storage** for all services with password or security token - **Online** and **Offline** map - Club Member lookup - CW Keyer Support - CWDaemon, FLDigi (all supported modes), Morse Over CAT, WinKey v1 or v2 - Bandmap - CW Console - WSJT-X integration - Station Location Profile support - Various station statistics - Basic Awards support - Basic Contest support - Custom QSO Filters - **NO** ads, **NO** user tracking, **NO** hidden telemetry - simply free and open-source - SQLite backend. ### Supported OS * Linux * Windows 10/11 (64bit) * MacOS (experimental - only for developers) ### Supported Rigs * all supported by [Hamlib](https://hamlib.github.io/) * all supported by [Omnirig v1](https://www.dxatlas.com/omnirig/) (Windows only) * all supported by [Omnirig v2](https://www.hb9ryz.ch/omnirig/) (Windows only) * all supported by [TCI](https://eesdr.com/en/software-en/software-en) * all supported by [FLRig](http://www.w1hkj.com/flrig-help/supported_transceivers.html) ### Supported Rotators * all supported by [Hamlib](https://hamlib.github.io/) ### Supported Keyers * [CWDaemon](https://cwdaemon.sourceforge.net/) * [FLDigi](http://www.w1hkj.com/) * Morse Over CAT * WinKey v1 or v2 compatible hardware ### Supported Secure Password Storage * Linux: LibSecretKeyring, GnomeKeyring, Kwallet4, Kwallet5 * Windows: Windows Credential Store * MacOS: macOS Keychain ### Third-party software * [TQSL](http://www.arrl.org/tqsl-download) – optional, needed for LoTW support For more details, screenshots etc, please, see [QLog Wiki](https://github.com/foldynl/QLog/wiki) Please, used [QLog Issues](https://github.com/foldynl/QLog/issues) for reporting any issue or open a [discussion](https://github.com/foldynl/QLog/discussions). You can also use [QLog mailing list](https://groups.io/g/qlog) ## Installation ### Minimum Hardware Requirements - The recommended graphical resolution: 1920x1080 - CPU and memory: minimum requirements the same as for your OS - Graphic Card with OpenGL support - Serial connection if radio control is used ### Linux Prerequisites: - Installed Trusted QSL (Optional) - `sudo apt install trustedqsl` or from [ARRL](http://www.arrl.org/tqsl-download) **DEB packages** for currently supported Ubuntu versions are available for amd64, arm64 platforms via [Ubuntu PPA](https://launchpad.net/~foldyna/+archive/ubuntu/qlog). Ubuntu users can use following commands: `sudo add-apt-repository ppa:foldyna/qlog` `sudo apt update` `sudo apt install qlog` Fedora **RPM packages** are available via GitHub [Releases](https://github.com/foldynl/QLog/releases/latest) Download on Flathub **Flatpak** package is available via [Flathub](https://flathub.org/apps/io.github.foldynl.QLog). The package contains built-in TrustedQSL. The above packages are maintained by the application maintainer. However, there are many other unofficial packages available for other distributions — see the Unofficial Repositories on [QLog Wiki](https://github.com/foldynl/QLog/wiki). ### Windows Prerequisites: - Installed [Visual C++ Redistributable](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170#latest-supported-redistributable-version) - Installed [Trusted QSL](http://www.arrl.org/tqsl-download) (Optional) - Installed [Omnirig v1](https://www.dxatlas.com/omnirig/) (Optional) - Installed [Omnirig v2](https://www.hb9ryz.ch/omnirig/) (Optional) Installation package is available via GitHub [Releases](https://github.com/foldynl/QLog/releases) . ### MacOS MacOS DMG: https://github.com/aa5sh/QLog/releases ## Disclaimer Regarding Forks and Clones This project is released as open source and may be forked or modified in accordance with its license. If you are using a fork, clone, or any modified version of this project that is not maintained in this official repository, please note: - We do **not** provide support for any forks, clones, or modified versions of this project. - This includes support requests related to "core features" as changes introduced in forks may directly or indirectly affect their behavior. - Any issues, bug reports, or feature requests concerning a forked or modified version must be directed to the maintainer of that specific repository. Support is provided exclusively for the original, unmodified version of the project distributed from this official source. By using a fork or modified version, you acknowledge that compatibility, stability, and functionality may differ from the original project, and responsibility for such differences lies solely with the maintainer of that fork. ## Compilation ### General Prerequisites - Installed Qt - Installed [qtkeychain-devel](https://github.com/frankosterfeld/qtkeychain) library and headers - Installed [OpenSSL-devel](https://wiki.openssl.org/index.php/Binaries) libraries and headers - Installed [HamLib-devel](https://github.com/Hamlib/Hamlib/releases/latest) libraries and headers `qmake` supports listed input parameters that affect the compilation process. - `HAMLIBINCLUDEPATH` - the path to Hamlib Includes - `HAMLIBLIBPATH` - the path to Hamlib Library - `HAMLIBVERSION_MAJOR` - Hamlib version - major number (must be present if `pkg-config` cannot determine Hamlib version) - `HAMLIBVERSION_MINOR` - Hamlib version - minor number (must be present if `pkg-config` cannot determine Hamlib version) - `HAMLIBVERSION_PATCH` - Hamlib version - patch number (must be present if `pkg-config` cannot determine Hamlib version) - `PTHREADINCLUDEPATH` - the path to pthread Includes - needed for Windows Hamlib 4.5 and later. Leave empty if system libraries should be used. - `PTHREADLIBPATH` - the path to pthread Library - needed for Windows Hamlib 4.5 and later. Leave empty if system libraries should be used. - `QTKEYCHAININCLUDEPATH` - the path to QtKeyChain Includes - `QTKEYCHAINLIBPATH`- the path to QtKeyChain Library - `ZLIBINCLUDEPATH` - the path to ZLIB Includes - `ZLIBLIBPATH` - the path to ZLIB Library Leave variables empty if system libraries and Hamlib version autodetect (calling `pkg-config`) should be used during compilation (for Windows, the parameter must be present) An example of use: ` C:/Qt/6.4.1/msvc2019_64/bin/qmake.exe C:\Users\devel\development\QLog\QLog.pro -spec win32-msvc "CONFIG+=qtquickcompiler" "HAMLIBINCLUDEPATH = C:\Users\devel\development\hamlib\include" "HAMLIBLIBPATH = C:\Users\devel\development\hamlib\lib\gcc" "HAMLIBVERSION_MAJOR = 4" "HAMLIBVERSION_MINOR = 5" "HAMLIBVERSION_PATCH = 0" "QTKEYCHAININCLUDEPATH = C:\Users\devel\development\qtkeychain_build\include" "QTKEYCHAINLIBPATH = C:\Users\devel\development\qtkeychain_build\lib" && C:/Qt/Tools/QtCreator/bin/jom/jom.exe qmake_all ` ### Windows Prerequisites - [Visual Studio 2022](https://visualstudio.microsoft.com/vs/community/) - QT with source codes (6.x, Qt Webengine, OpenSSL Toolkit) - [Omnirig v1](https://www.dxatlas.com/omnirig/) - [Omnirig v2](https://www.hb9ryz.ch/omnirig/) - [Hamlib](https://github.com/Hamlib/Hamlib/releases) - hamlib-w64-4.5.5.exe is the latest - Need to run the following commands to "fix" the library *** Fix Paths if necessary *** CD "C:\Program Files\hamlib-w64-4.5.5\lib\msvc" "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.41.34120\bin\Hostx64\x64\link.exe" /lib /machine:X64 /def:libhamlib-4.def copy libhamlib-4.lib hamlib.lib - Install C++ Module - https://learn.microsoft.com/en-us/vcpkg/get_started/get-started-vscode?pivots=shell-powershell cd C:\Program Files\Microsoft Visual Studio\2022\Community\VC\vcpkg vcpkg install pthreads vcpkg install qtkeychain-qt6 System Environmental Path Settings C:\Program Files\hamlib-w64-4.5.5\bin C:\QTTools\vcpkg\packages\qtkeychain-qt6_x64-windows\bin Clone QLog Master Branch In QT Creator Projects->Desktop Qt 6.8.0 MSVC2022 64Bit->Build Steps->Additional Arguments **** You need to update the paths accordingly **** **** Need to be on same line seperated by spaces **** "HAMLIBINCLUDEPATH='C:\Program Files\hamlib-w64-4.5.5\include'" "HAMLIBLIBPATH='C:\Program Files\hamlib-w64-4.5.5\lib\msvc'" "HAMLIBVERSION_MAJOR=4" "HAMLIBVERSION_MINOR=5" "HAMLIBVERSION_PATCH=5" "QTKEYCHAININCLUDEPATH=C:\QTTools\vcpkg\packages\qtkeychain-qt6_x64-windows\include" "QTKEYCHAINLIBPATH=C:\QTTools\vcpkg\packages\qtkeychain-qt6_x64-windows\lib" "PTHREADLIBPATH=C:\QTTools\vcpkg\packages\pthreads_x64-windows\lib" "PTHREADINCLUDEPATH=C:\QTTools\vcpkg\packages\pthreads_x64-windows\include" ### Linux for Debian: `sudo apt-get -y install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libsqlite3-dev libhamlib++-dev libqt5charts5-dev qttools5-dev-tools libqt5keychain1 qt5keychain-dev qtwebengine5-dev build-essential libqt5serialport5-dev pkg-config libqt5websockets5-dev libssl-dev` for Debian (QT6): `sudo apt-get -y install libhamlib-dev build-essential pkg-config qt6-base-dev qtkeychain-qt6-dev qt6-webengine-dev libqt6charts6-dev libqt6serialport6 libqt6webenginecore6-bin libqt6svg6-dev libgl-dev libqt6websockets6-dev qt6-serialport-dev libsqlite3-dev libssl-dev` for Fedora: `dnf install qt5-qtbase-devel qt5-qtwebengine-devel qt5-qtcharts-devel hamlib-devel qtkeychain-qt5-devel qt5-qtserialport-devel pkg-config qt5-qtwebsockets-devel libsqlite3x-devel openssl-devel` for both: `git clone https://github.com/foldynl/QLog.git` `cd QLog` for Debian: `qmake QLog.pro` for Debian (QT6): `qmake6 QLog.pro` for Fedora: `/usr/bin/qmake-qt5` NOTE: if it is necessary then use `qmake` input parameters described above to affect compilation. The input parameter must be use in case when Hamlib or qtkeychain are compiled from their source code repos. for all: `make` ### MacOS In order to build QLog on MacOS, following prerequisites must be satisfied. 1. [Xcode](#xcode) command line tools 2. [Homebrew](https://brew.sh) 3. [Qt](https://www.qt.io) with QtCreator ##### Xcode Xcode command line tools can be installed by issuing a command in command terminal: ``` xcode-select --install ``` **N.B.:** This command doesn't install Xcode itself, however It will take some time to download and install the tools anyway. ##### MacOS build Last dependencies before building QLog are: ``` brew install qt6 brew link qt6 --force brew install hamlib brew link hamlib --force brew install qtkeychain brew install dbus-glib brew install brotli brew install icu4c brew install pkg-config ``` As soon as the steps above are finished, QLog source can be opened in QtCreator, configured, built and run. QLog app (qlog.app) from the build artifacts folder can be later copied (`installed`) to `~/Applications` and accessed via Spotlight search bar. NOTE: if it is necessary then use `qmake` input parameters described above to affect compilation. The input parameter must be use in case when hamlib or qtkeychain is compiled from their source code repos. ## License Copyright (C) 2020 Thomas Gatzweiler Copyright (C) 2021-2026 Ladislav Foldyna This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ================================================ FILE: awards/AwardDXCC.cpp ================================================ #include #include "AwardDXCC.h" QString AwardDXCC::displayName() const { return QCoreApplication::translate("AwardsDialog", "DXCC"); } QString AwardDXCC::headersColumns(const QString &) const { return QStringLiteral("translate_to_locale(d.name) col1, d.prefix col2 "); } QString AwardDXCC::sqlDetailTable(const QString &entity) const { return " FROM (SELECT id, name, prefix FROM dxcc_entities_clublog WHERE deleted = 0 " " UNION " " SELECT DISTINCT dxcc, b.name, b.prefix || ' (" + QCoreApplication::translate("AwardsDialog", "DELETED") + ")' " " FROM source_contacts a INNER JOIN dxcc_entities_clublog b ON a.dxcc = b.id AND b.deleted = 1 where a.my_dxcc = '" + entity + "') d " " LEFT OUTER JOIN source_contacts c ON (d.id = c.dxcc AND (c.id IS NULL OR c.my_dxcc = '" + entity + "'))" " LEFT OUTER JOIN modes m on c.mode = m.name "; } QString AwardDXCC::additionalWhere(const QString &) const { return QString(); } QString AwardDXCC::clickFilter(const QString &, const QString &) const { return QString(); } bool AwardDXCC::clickUsesCountryName() const { return true; } ================================================ FILE: awards/AwardDXCC.h ================================================ #ifndef QLOG_AWARDS_AWARDDXCC_H #define QLOG_AWARDS_AWARDDXCC_H #include "BandTableAward.h" class AwardDXCC : public BandTableAward { public: QString key() const override { return QStringLiteral("dxcc"); } QString displayName() const override; protected: QString headersColumns(const QString &entity) const override; QString sqlDetailTable(const QString &entity) const override; QString additionalWhere(const QString &entity) const override; QString clickFilter(const QString &col1Value, const QString &col2Value) const override; bool clickUsesCountryName() const override; }; #endif // QLOG_AWARDS_AWARDDXCC_H ================================================ FILE: awards/AwardDefinition.cpp ================================================ #include "AwardDefinition.h" bool AwardDefinition::entityInputEnabled() const { return true; } bool AwardDefinition::notWorkedEnabled() const { return true; } AwardDefinition::ConditionResult AwardDefinition::getConditionSelected(const QModelIndex &) const { return ConditionResult(); } ================================================ FILE: awards/AwardDefinition.h ================================================ #ifndef QLOG_AWARDS_AWARDDEFINITION_H #define QLOG_AWARDS_AWARDDEFINITION_H #include #include #include #include /* Filter parameters collected from the AwardsDialog UI controls. * Passed to AwardDefinition::updateData() on every filter change. */ struct AwardFilterParams { QString entitySelected; /* DXCC entity ID from the "My DXCC Entity" combo */ QStringList modes; /* SQL IN-list values, e.g. {"'NONE'", "'CW'", "'PHONE'"} */ QStringList confirmedConditions; /* SQL OR-conditions for confirmed status */ bool notWorkedOnly; /* Show only entities with no QSOs on any band */ bool notConfirmedOnly; /* Show only entities worked but not confirmed */ QString userFilterWhereClause; /* Extra WHERE clause from QSOFilterManager (includes leading "AND") */ }; /* Abstract base class for all award definitions. * * To add a completely custom award (non-table display), subclass this directly * and implement createWidget() / updateData(). * * For the common band-column table display, subclass BandTableAward instead. * * Registration: add one line to AwardsDialog::createAwards(). */ class AwardDefinition { public: virtual ~AwardDefinition() = default; /* Unique internal key (e.g. "dxcc", "sota", "grid4"). Used as combo item data. */ virtual QString key() const = 0; /*Translatable display name shown in the Award combo box. */ virtual QString displayName() const = 0; /*Whether the "My DXCC Entity" combo is shown. Default: true. */ virtual bool entityInputEnabled() const; /*Whether the Not-Worked / Not-Confirmed checkboxes are shown. Default: true. */ virtual bool notWorkedEnabled() const; /*Create the display widget. Called once on first selection. Store in m_widget. */ virtual QWidget* createWidget(QWidget *parent) = 0; /*Refresh data using the current filter parameters. Called on every filter change. */ virtual void updateData(const AwardFilterParams ¶ms) = 0; /* Return filter data for the logbook based on a double-clicked cell. * The dialog uses ConditionResult to emit AwardConditionSelected(country, band, filter) * which opens the logbook filtered to matching QSOs. * Default implementation returns an invalid (no-op) result. */ struct ConditionResult { QString country; /* Country name for the logbook filter (empty = not used) */ QString band; /* Band name from the column header (empty = all bands) */ QString filter; /* SQL WHERE clause fragment, e.g. "(sota_ref = 'OE/TI-001')" */ bool valid = false; }; virtual ConditionResult getConditionSelected(const QModelIndex &clickedIndex) const; /*Returns the cached widget (nullptr before createWidget()). */ QWidget* widget() const { return m_widget; } protected: QWidget *m_widget = nullptr; }; #endif // QLOG_AWARDS_AWARDDEFINITION_H ================================================ FILE: awards/AwardGridsquare.cpp ================================================ #include #include "AwardGridsquare.h" AwardGridsquare::AwardGridsquare(int chars) : m_chars(chars) { } QString AwardGridsquare::key() const { return QString("grid%1").arg(m_chars); } QString AwardGridsquare::displayName() const { switch ( m_chars ) { case 2: return QCoreApplication::translate("AwardsDialog", "Gridsquare 2-Chars"); case 4: return QCoreApplication::translate("AwardsDialog", "Gridsquare 4-Chars"); case 6: return QCoreApplication::translate("AwardsDialog", "Gridsquare 6-Chars"); default: return QCoreApplication::translate("AwardsDialog", "Gridsquare %1-Chars").arg(m_chars); } } QString AwardGridsquare::headersColumns(const QString &) const { return QString("substr(c.gridsquare, 1, %1) col1, NULL col2 ").arg(m_chars); } QString AwardGridsquare::sqlDetailTable(const QString &entity) const { return " FROM source_contacts c" " INNER JOIN modes m ON c.mode = m.name AND c.my_dxcc = '" + entity + "' "; } QString AwardGridsquare::additionalWhere(const QString &) const { return QString(" AND length(c.gridsquare) >= %1 ").arg(m_chars); } QString AwardGridsquare::clickFilter(const QString &col1Value, const QString &) const { return QString("gridsquare LIKE '%1%%'").arg(col1Value); } ================================================ FILE: awards/AwardGridsquare.h ================================================ #ifndef QLOG_AWARDS_AWARDGRIDSQUARE_H #define QLOG_AWARDS_AWARDGRIDSQUARE_H #include "BandTableAward.h" class AwardGridsquare : public BandTableAward { public: explicit AwardGridsquare(int chars); QString key() const override; QString displayName() const override; bool notWorkedEnabled() const override { return false; } protected: QString headersColumns(const QString &entity) const override; QString sqlDetailTable(const QString &entity) const override; QString additionalWhere(const QString &entity) const override; QString clickFilter(const QString &col1Value, const QString &col2Value) const override; private: int m_chars; }; #endif // QLOG_AWARDS_AWARDGRIDSQUARE_H ================================================ FILE: awards/AwardIOTA.cpp ================================================ #include #include "AwardIOTA.h" QString AwardIOTA::displayName() const { return QCoreApplication::translate("AwardsDialog", "IOTA"); } QString AwardIOTA::headersColumns(const QString &) const { return QStringLiteral("c.iota col1, NULL col2 "); } QString AwardIOTA::sqlDetailTable(const QString &entity) const { return " FROM source_contacts c" " INNER JOIN modes m ON c.mode = m.name AND c.my_dxcc = '" + entity + "'"; } QString AwardIOTA::additionalWhere(const QString &) const { return " AND c.iota is not NULL "; } QString AwardIOTA::clickFilter(const QString &col1Value, const QString &) const { return QString("iota = '%1'").arg(col1Value); } ================================================ FILE: awards/AwardIOTA.h ================================================ #ifndef QLOG_AWARDS_AWARDIOTA_H #define QLOG_AWARDS_AWARDIOTA_H #include "BandTableAward.h" class AwardIOTA : public BandTableAward { public: QString key() const override { return QStringLiteral("iota"); } QString displayName() const override; bool notWorkedEnabled() const override { return false; } protected: QString headersColumns(const QString &entity) const override; QString sqlDetailTable(const QString &entity) const override; QString additionalWhere(const QString &entity) const override; QString clickFilter(const QString &col1Value, const QString &col2Value) const override; }; #endif // QLOG_AWARDS_AWARDIOTA_H ================================================ FILE: awards/AwardITU.cpp ================================================ #include #include "AwardITU.h" QString AwardITU::displayName() const { return QCoreApplication::translate("AwardsDialog", "ITU"); } QString AwardITU::headersColumns(const QString &) const { return QStringLiteral("d.n col1, null col2 "); } QString AwardITU::sqlDetailTable(const QString &entity) const { return " FROM ituzCTE d " " LEFT OUTER JOIN source_contacts c ON d.n = c.ituz AND c.my_dxcc = '" + entity + "'" " LEFT OUTER JOIN modes m on c.mode = m.name"; } QString AwardITU::additionalWhere(const QString &) const { return QString(); } QStringList AwardITU::additionalCTEs(const QString &, const QString &) const { return { generateNumberRangeCTE("ituzCTE", 1, 90) }; } QString AwardITU::clickFilter(const QString &col1Value, const QString &) const { return QString("ituz = '%1'").arg(col1Value); } ================================================ FILE: awards/AwardITU.h ================================================ #ifndef QLOG_AWARDS_AWARDITU_H #define QLOG_AWARDS_AWARDITU_H #include "BandTableAward.h" class AwardITU : public BandTableAward { public: QString key() const override { return QStringLiteral("itu"); } QString displayName() const override; protected: QString headersColumns(const QString &entity) const override; QString sqlDetailTable(const QString &entity) const override; QString additionalWhere(const QString &entity) const override; QStringList additionalCTEs(const QString &entity, const QString &contactFilter) const override; QString clickFilter(const QString &col1Value, const QString &col2Value) const override; }; #endif // QLOG_AWARDS_AWARDITU_H ================================================ FILE: awards/AwardJapan.cpp ================================================ #include #include "AwardJapan.h" AwardJapan::AwardJapan() : SecondarySubdivisionAward(QStringLiteral("japan"), QStringLiteral("339")) { } QString AwardJapan::displayName() const { return QCoreApplication::translate("AwardsDialog", "Japanese Cities/Ku/Guns"); } ================================================ FILE: awards/AwardJapan.h ================================================ #ifndef QLOG_AWARDS_AWARDJAPAN_H #define QLOG_AWARDS_AWARDJAPAN_H #include "SecondarySubdivisionAward.h" class AwardJapan : public SecondarySubdivisionAward { public: AwardJapan(); QString displayName() const override; }; #endif // QLOG_AWARDS_AWARDJAPAN_H ================================================ FILE: awards/AwardNZ.cpp ================================================ #include #include "AwardNZ.h" AwardNZ::AwardNZ() : SecondarySubdivisionAward(QStringLiteral("nz"), QStringLiteral("170")) { } QString AwardNZ::displayName() const { return QCoreApplication::translate("AwardsDialog", "NZ Counties"); } ================================================ FILE: awards/AwardNZ.h ================================================ #ifndef QLOG_AWARDS_AWARDNZ_H #define QLOG_AWARDS_AWARDNZ_H #include "SecondarySubdivisionAward.h" class AwardNZ : public SecondarySubdivisionAward { public: AwardNZ(); QString displayName() const override; }; #endif // QLOG_AWARDS_AWARDNZ_H ================================================ FILE: awards/AwardPOTAActivator.cpp ================================================ #include #include "AwardPOTAActivator.h" QString AwardPOTAActivator::displayName() const { return QCoreApplication::translate("AwardsDialog", "POTA Activator"); } QString AwardPOTAActivator::headersColumns(const QString &) const { return QStringLiteral("p.reference col1, p.name col2 "); } QString AwardPOTAActivator::sqlDetailTable(const QString &) const { return " FROM pota_directory p " " INNER JOIN source_contacts c ON SUBSTR(c.my_pota_ref_str, 1, COALESCE(NULLIF(INSTR(c.my_pota_ref_str, '@'), 0) - 1, LENGTH(c.my_pota_ref_str))) = p.reference" " INNER JOIN modes m on c.mode = m.name "; } QString AwardPOTAActivator::additionalWhere(const QString &) const { return " AND c.my_pota_ref_str is not NULL "; } QStringList AwardPOTAActivator::additionalCTEs(const QString &, const QString &contactFilter) const { return { generateSplitCTE("my_pota_ref", "my_pota_ref_str", contactFilter) }; } QString AwardPOTAActivator::sourceContactsOverride(const QString &) const { return generateSplitSourceContacts("my_pota_ref_str"); } QString AwardPOTAActivator::clickFilter(const QString &col1Value, const QString &) const { // Bug fix: original had "my_pota_ref LIKE = '%%1%'" (extra '=') return QString("my_pota_ref LIKE '%%1%'").arg(col1Value); } ================================================ FILE: awards/AwardPOTAActivator.h ================================================ #ifndef QLOG_AWARDS_AWARDPOTAACTIVATOR_H #define QLOG_AWARDS_AWARDPOTAACTIVATOR_H #include "BandTableAward.h" class AwardPOTAActivator : public BandTableAward { public: QString key() const override { return QStringLiteral("potaa"); } QString displayName() const override; bool entityInputEnabled() const override { return false; } bool notWorkedEnabled() const override { return false; } protected: QString headersColumns(const QString &entity) const override; QString sqlDetailTable(const QString &entity) const override; QString additionalWhere(const QString &entity) const override; QStringList additionalCTEs(const QString &entity, const QString &contactFilter) const override; QString sourceContactsOverride(const QString &contactFilter) const override; QString clickFilter(const QString &col1Value, const QString &col2Value) const override; }; #endif // QLOG_AWARDS_AWARDPOTAACTIVATOR_H ================================================ FILE: awards/AwardPOTAHunter.cpp ================================================ #include #include "AwardPOTAHunter.h" QString AwardPOTAHunter::displayName() const { return QCoreApplication::translate("AwardsDialog", "POTA Hunter"); } QString AwardPOTAHunter::headersColumns(const QString &) const { return QStringLiteral("p.reference col1, p.name col2 "); } QString AwardPOTAHunter::sqlDetailTable(const QString &) const { return " FROM pota_directory p " " INNER JOIN source_contacts c ON SUBSTR(c.pota, 1, COALESCE(NULLIF(INSTR(c.pota, '@'), 0) - 1, LENGTH(c.pota))) = p.reference" " INNER JOIN modes m on c.mode = m.name "; } QString AwardPOTAHunter::additionalWhere(const QString &) const { return " AND c.pota is not NULL "; } QStringList AwardPOTAHunter::additionalCTEs(const QString &, const QString &contactFilter) const { return { generateSplitCTE("pota_ref", "pota", contactFilter) }; } QString AwardPOTAHunter::sourceContactsOverride(const QString &) const { return generateSplitSourceContacts("pota"); } QString AwardPOTAHunter::clickFilter(const QString &col1Value, const QString &) const { return QString("pota_ref LIKE '%%1%'").arg(col1Value); } ================================================ FILE: awards/AwardPOTAHunter.h ================================================ #ifndef QLOG_AWARDS_AWARDPOTAHUNTER_H #define QLOG_AWARDS_AWARDPOTAHUNTER_H #include "BandTableAward.h" class AwardPOTAHunter : public BandTableAward { public: QString key() const override { return QStringLiteral("potah"); } QString displayName() const override; bool entityInputEnabled() const override { return false; } bool notWorkedEnabled() const override { return false; } protected: QString headersColumns(const QString &entity) const override; QString sqlDetailTable(const QString &entity) const override; QString additionalWhere(const QString &entity) const override; QStringList additionalCTEs(const QString &entity, const QString &contactFilter) const override; QString sourceContactsOverride(const QString &contactFilter) const override; QString clickFilter(const QString &col1Value, const QString &col2Value) const override; }; #endif // QLOG_AWARDS_AWARDPOTAHUNTER_H ================================================ FILE: awards/AwardRDA.cpp ================================================ #include #include "AwardRDA.h" AwardRDA::AwardRDA() : SecondarySubdivisionAward(QStringLiteral("rda"), QStringLiteral("15, 54, 61, 126, 151")) { } QString AwardRDA::displayName() const { return QCoreApplication::translate("AwardsDialog", "Russian Districts"); } ================================================ FILE: awards/AwardRDA.h ================================================ #ifndef QLOG_AWARDS_AWARDRDA_H #define QLOG_AWARDS_AWARDRDA_H #include "SecondarySubdivisionAward.h" class AwardRDA : public SecondarySubdivisionAward { public: AwardRDA(); QString displayName() const override; }; #endif // QLOG_AWARDS_AWARDRDA_H ================================================ FILE: awards/AwardSOTA.cpp ================================================ #include #include "AwardSOTA.h" QString AwardSOTA::displayName() const { return QCoreApplication::translate("AwardsDialog", "SOTA"); } QString AwardSOTA::headersColumns(const QString &) const { return QStringLiteral("s.summit_code col1, NULL col2 "); } QString AwardSOTA::sqlDetailTable(const QString &) const { return " FROM sota_summits s " " INNER JOIN source_contacts c ON c.sota_ref = s.summit_code " " INNER JOIN modes m on c.mode = m.name "; } QString AwardSOTA::additionalWhere(const QString &) const { return QString(); } QString AwardSOTA::clickFilter(const QString &col1Value, const QString &) const { return QString("sota_ref = '%1'").arg(col1Value); } ================================================ FILE: awards/AwardSOTA.h ================================================ #ifndef QLOG_AWARDS_AWARDSOTA_H #define QLOG_AWARDS_AWARDSOTA_H #include "BandTableAward.h" class AwardSOTA : public BandTableAward { public: QString key() const override { return QStringLiteral("sota"); } QString displayName() const override; bool entityInputEnabled() const override { return false; } bool notWorkedEnabled() const override { return false; } protected: QString headersColumns(const QString &entity) const override; QString sqlDetailTable(const QString &entity) const override; QString additionalWhere(const QString &entity) const override; QString clickFilter(const QString &col1Value, const QString &col2Value) const override; }; #endif // QLOG_AWARDS_AWARDSOTA_H ================================================ FILE: awards/AwardSpanishDME.cpp ================================================ #include #include "AwardSpanishDME.h" AwardSpanishDME::AwardSpanishDME() : SecondarySubdivisionAward(QStringLiteral("spanishdme"), QStringLiteral("21, 29, 32, 281")) { } QString AwardSpanishDME::displayName() const { return QCoreApplication::translate("AwardsDialog", "Spanish DMEs"); } ================================================ FILE: awards/AwardSpanishDME.h ================================================ #ifndef QLOG_AWARDS_AWARDSPANISHDME_H #define QLOG_AWARDS_AWARDSPANISHDME_H #include "SecondarySubdivisionAward.h" class AwardSpanishDME : public SecondarySubdivisionAward { public: AwardSpanishDME(); QString displayName() const override; }; #endif // QLOG_AWARDS_AWARDSPANISHDME_H ================================================ FILE: awards/AwardUKD.cpp ================================================ #include #include "AwardUKD.h" AwardUKD::AwardUKD() : SecondarySubdivisionAward(QStringLiteral("ukd"), QStringLiteral("288")) { } QString AwardUKD::displayName() const { return QCoreApplication::translate("AwardsDialog", "Ukrainian Districts"); } ================================================ FILE: awards/AwardUKD.h ================================================ #ifndef QLOG_AWARDS_AWARDUKD_H #define QLOG_AWARDS_AWARDUKD_H #include "SecondarySubdivisionAward.h" class AwardUKD : public SecondarySubdivisionAward { public: AwardUKD(); QString displayName() const override; }; #endif // QLOG_AWARDS_AWARDUKD_H ================================================ FILE: awards/AwardUSCounty.cpp ================================================ #include #include "AwardUSCounty.h" AwardUSCounty::AwardUSCounty() : SecondarySubdivisionAward(QStringLiteral("uscounty"), QStringLiteral("291, 6, 110")) { } QString AwardUSCounty::displayName() const { return QCoreApplication::translate("AwardsDialog", "US Counties"); } ================================================ FILE: awards/AwardUSCounty.h ================================================ #ifndef QLOG_AWARDS_AWARDUSCOUNTY_H #define QLOG_AWARDS_AWARDUSCOUNTY_H #include "SecondarySubdivisionAward.h" class AwardUSCounty : public SecondarySubdivisionAward { public: AwardUSCounty(); QString displayName() const override; }; #endif // QLOG_AWARDS_AWARDUSCOUNTY_H ================================================ FILE: awards/AwardWAC.cpp ================================================ #include #include "AwardWAC.h" /* https://www.arrl.org/wac */ QString AwardWAC::displayName() const { return QCoreApplication::translate("AwardsDialog", "WAC"); } QString AwardWAC::headersColumns(const QString &) const { return QStringLiteral("d.column2 col1, d.column1 col2 "); } QString AwardWAC::sqlDetailTable(const QString &entity) const { return " FROM continents d " " LEFT OUTER JOIN source_contacts c ON d.column1 = c.cont AND c.my_dxcc = '" + entity + "'" " LEFT OUTER JOIN modes m on c.mode = m.name "; } QString AwardWAC::additionalWhere(const QString &) const { return QString(); } QStringList AwardWAC::additionalCTEs(const QString &, const QString &) const { return { generateValuesCTE("continents", { { QStringLiteral("NA"), QCoreApplication::translate("AwardsDialog", "North America") }, { QStringLiteral("SA"), QCoreApplication::translate("AwardsDialog", "South America") }, { QStringLiteral("EU"), QCoreApplication::translate("AwardsDialog", "Europe") }, { QStringLiteral("AF"), QCoreApplication::translate("AwardsDialog", "Africa") }, { QStringLiteral("OC"), QCoreApplication::translate("AwardsDialog", "Oceania") }, { QStringLiteral("AS"), QCoreApplication::translate("AwardsDialog", "Asia") } }) }; } QString AwardWAC::clickFilter(const QString &, const QString &col2Value) const { return QString("cont = '%1'").arg(col2Value); } ================================================ FILE: awards/AwardWAC.h ================================================ #ifndef QLOG_AWARDS_AWARDWAC_H #define QLOG_AWARDS_AWARDWAC_H #include "BandTableAward.h" class AwardWAC : public BandTableAward { public: QString key() const override { return QStringLiteral("wac"); } QString displayName() const override; protected: QString headersColumns(const QString &entity) const override; QString sqlDetailTable(const QString &entity) const override; QString additionalWhere(const QString &entity) const override; QStringList additionalCTEs(const QString &entity, const QString &contactFilter) const override; QString clickFilter(const QString &col1Value, const QString &col2Value) const override; }; #endif // QLOG_AWARDS_AWARDWAC_H ================================================ FILE: awards/AwardWAS.cpp ================================================ #include #include "AwardWAS.h" QString AwardWAS::displayName() const { return QCoreApplication::translate("AwardsDialog", "WAS"); } QString AwardWAS::headersColumns(const QString &) const { return QStringLiteral("d.subdivision_name col1, d.code col2 "); } QString AwardWAS::sqlDetailTable(const QString &entity) const { return " FROM adif_enum_primary_subdivision d" " LEFT OUTER JOIN source_contacts c ON d.dxcc = c.dxcc AND d.code = c.state AND c.my_dxcc = '" + entity + "' AND d.dxcc in (6, 110, 291)" " LEFT OUTER JOIN modes m on c.mode = m.name"; } QString AwardWAS::additionalWhere(const QString &) const { return " AND d.dxcc in (6, 110, 291) "; } QString AwardWAS::clickFilter(const QString &, const QString &col2Value) const { return QString("state = '%1' and dxcc in (6, 110, 291)").arg(col2Value); } ================================================ FILE: awards/AwardWAS.h ================================================ #ifndef QLOG_AWARDS_AWARDWAS_H #define QLOG_AWARDS_AWARDWAS_H #include "BandTableAward.h" class AwardWAS : public BandTableAward { public: QString key() const override { return QStringLiteral("was"); } QString displayName() const override; protected: QString headersColumns(const QString &entity) const override; QString sqlDetailTable(const QString &entity) const override; QString additionalWhere(const QString &entity) const override; QString clickFilter(const QString &col1Value, const QString &col2Value) const override; }; #endif // QLOG_AWARDS_AWARDWAS_H ================================================ FILE: awards/AwardWAZ.cpp ================================================ #include #include "AwardWAZ.h" QString AwardWAZ::displayName() const { return QCoreApplication::translate("AwardsDialog", "WAZ"); } QString AwardWAZ::headersColumns(const QString &) const { return QStringLiteral("d.n col1, null col2 "); } QString AwardWAZ::sqlDetailTable(const QString &entity) const { return " FROM cqzCTE d " " LEFT OUTER JOIN source_contacts c ON d.n = c.cqz AND c.my_dxcc = '" + entity + "'" " LEFT OUTER JOIN modes m on c.mode = m.name "; } QString AwardWAZ::additionalWhere(const QString &) const { return QString(); } QStringList AwardWAZ::additionalCTEs(const QString &, const QString &) const { return { generateNumberRangeCTE("cqzCTE", 1, 40) }; } QString AwardWAZ::clickFilter(const QString &col1Value, const QString &) const { return QString("cqz = '%1'").arg(col1Value); } ================================================ FILE: awards/AwardWAZ.h ================================================ #ifndef QLOG_AWARDS_AWARDWAZ_H #define QLOG_AWARDS_AWARDWAZ_H #include "BandTableAward.h" class AwardWAZ : public BandTableAward { public: QString key() const override { return QStringLiteral("waz"); } QString displayName() const override; protected: QString headersColumns(const QString &entity) const override; QString sqlDetailTable(const QString &entity) const override; QString additionalWhere(const QString &entity) const override; QStringList additionalCTEs(const QString &entity, const QString &contactFilter) const override; QString clickFilter(const QString &col1Value, const QString &col2Value) const override; }; #endif // QLOG_AWARDS_AWARDWAZ_H ================================================ FILE: awards/AwardWPX.cpp ================================================ #include #include "AwardWPX.h" QString AwardWPX::displayName() const { return QCoreApplication::translate("AwardsDialog", "WPX"); } QString AwardWPX::headersColumns(const QString &) const { return QStringLiteral("c.pfx col1, null col2 "); } QString AwardWPX::sqlDetailTable(const QString &entity) const { return " FROM source_contacts c" " INNER JOIN modes m ON c.mode = m.name AND c.my_dxcc = '" + entity + "'"; } QString AwardWPX::additionalWhere(const QString &) const { return " AND c.pfx is not null "; } QString AwardWPX::clickFilter(const QString &col1Value, const QString &) const { return QString("pfx = '%1'").arg(col1Value); } ================================================ FILE: awards/AwardWPX.h ================================================ #ifndef QLOG_AWARDS_AWARDWPX_H #define QLOG_AWARDS_AWARDWPX_H #include "BandTableAward.h" class AwardWPX : public BandTableAward { public: QString key() const override { return QStringLiteral("wpx"); } QString displayName() const override; bool notWorkedEnabled() const override { return false; } protected: QString headersColumns(const QString &entity) const override; QString sqlDetailTable(const QString &entity) const override; QString additionalWhere(const QString &entity) const override; QString clickFilter(const QString &col1Value, const QString &col2Value) const override; }; #endif // QLOG_AWARDS_AWARDWPX_H ================================================ FILE: awards/AwardWWFF.cpp ================================================ #include #include "AwardWWFF.h" QString AwardWWFF::displayName() const { return QCoreApplication::translate("AwardsDialog", "WWFF"); } QString AwardWWFF::headersColumns(const QString &) const { return QStringLiteral("w.reference col1, w.name col2 "); } QString AwardWWFF::sqlDetailTable(const QString &) const { return " FROM wwff_directory w " " INNER JOIN source_contacts c ON c.wwff_ref = w.reference " " INNER JOIN modes m on c.mode = m.name "; } QString AwardWWFF::additionalWhere(const QString &) const { return QString(); } QString AwardWWFF::clickFilter(const QString &col1Value, const QString &) const { return QString("wwff_ref = '%1'").arg(col1Value); } ================================================ FILE: awards/AwardWWFF.h ================================================ #ifndef QLOG_AWARDS_AWARDWWFF_H #define QLOG_AWARDS_AWARDWWFF_H #include "BandTableAward.h" class AwardWWFF : public BandTableAward { public: QString key() const override { return QStringLiteral("wwff"); } QString displayName() const override; bool entityInputEnabled() const override { return false; } bool notWorkedEnabled() const override { return false; } protected: QString headersColumns(const QString &entity) const override; QString sqlDetailTable(const QString &entity) const override; QString additionalWhere(const QString &entity) const override; QString clickFilter(const QString &col1Value, const QString &col2Value) const override; }; #endif // QLOG_AWARDS_AWARDWWFF_H ================================================ FILE: awards/BandTableAward.cpp ================================================ #include #include "BandTableAward.h" #include "core/debug.h" #include "data/Band.h" #include "data/BandPlan.h" MODULE_IDENTIFICATION("qlog.awards.bandtableaward"); QWidget* BandTableAward::createWidget(QWidget *parent) { FCT_IDENTIFICATION; m_tableView = new QTableView(parent); m_model = new AwardsTableModel(m_tableView); m_tableView->setFocusPolicy(Qt::ClickFocus); m_tableView->setEditTriggers(QAbstractItemView::NoEditTriggers); m_tableView->setAlternatingRowColors(true); m_tableView->setSelectionMode(QAbstractItemView::SingleSelection); m_tableView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); m_tableView->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); m_tableView->horizontalHeader()->setVisible(true); m_tableView->horizontalHeader()->setCascadingSectionResizes(true); m_tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); m_tableView->verticalHeader()->setVisible(false); m_tableView->verticalHeader()->setMinimumSectionSize(20); m_tableView->verticalHeader()->setDefaultSectionSize(20); m_tableView->verticalHeader()->setHighlightSections(false); m_widget = m_tableView; return m_widget; } void BandTableAward::updateData(const AwardFilterParams ¶ms) { FCT_IDENTIFICATION; const QList& dxccBands = BandPlan::bandsList(false, true); if ( dxccBands.size() == 0 ) return; m_currentEntity = params.entitySelected; const QString innerConfirmedCase(QString(" CASE WHEN (%1) THEN 2 ELSE 1 END ") .arg(params.confirmedConditions.isEmpty() ? QStringLiteral("1=2") : params.confirmedConditions.join(" or "))); QStringList stmt_max_part; QStringList stmt_total_padding; QStringList stmt_sum_confirmed; QStringList stmt_sum_worked; QStringList stmt_sum_total; QStringList stmt_having; QStringList stmt_total_band_condition_work; QStringList stmt_total_band_condition_confirmed; QStringList stmt_not_confirmed; QStringList stmt_any_worked; for ( const Band& band : dxccBands ) { stmt_max_part << QString(" MAX(CASE WHEN band = '%1' AND m.dxcc IN (%2) THEN %3 ELSE 0 END) as '%4'") .arg(band.name, params.modes.join(","), innerConfirmedCase, band.name); stmt_total_padding << QString(" NULL '%1'").arg(band.name); stmt_sum_confirmed << QString("SUM(CASE WHEN a.'%1' > 1 THEN 1 ELSE 0 END) '%2'").arg(band.name, band.name); stmt_sum_worked << QString("SUM(CASE WHEN a.'%1' > 0 THEN 1 ELSE 0 END) '%2'").arg(band.name, band.name); stmt_sum_total << QString("SUM(d.'%1') '%2'").arg(band.name, band.name); stmt_having << QString("SUM(d.'%1') = 0").arg(band.name); stmt_total_band_condition_work << QString("e.'%1' > 0").arg(band.name); stmt_total_band_condition_confirmed << QString("e.'%1' > 1").arg(band.name); stmt_not_confirmed << QString("MAX(d.'%1') < 2").arg(band.name); stmt_any_worked << QString("MAX(d.'%1') > 0").arg(band.name); } stmt_max_part << QString(" MAX(CASE WHEN prop_mode = 'SAT' AND m.dxcc IN (%1) THEN %2 ELSE 0 END) as 'SAT' ").arg(params.modes.join(","), innerConfirmedCase) << QString(" MAX(CASE WHEN prop_mode = 'EME' AND m.dxcc IN (%1) THEN %2 ELSE 0 END) as 'EME' ").arg(params.modes.join(","), innerConfirmedCase); stmt_total_padding << " NULL 'SAT' " << " NULL 'EME' "; stmt_sum_confirmed << " SUM(CASE WHEN a.'SAT' > 1 THEN 1 ELSE 0 END) 'SAT' " << " SUM(CASE WHEN a.'EME' > 1 THEN 1 ELSE 0 END) 'EME' "; stmt_sum_worked << " SUM(CASE WHEN a.'SAT' > 0 THEN 1 ELSE 0 END) 'SAT' " << " SUM(CASE WHEN a.'EME' > 0 THEN 1 ELSE 0 END) 'EME' "; stmt_sum_total << " SUM(d.'SAT') 'SAT' " << " SUM(d.'EME') 'EME' "; stmt_having << " SUM(d.'SAT') = 0" << " SUM(d.'EME') = 0"; stmt_total_band_condition_work << "e.'SAT' > 0" << "e.'EME' > 0"; stmt_total_band_condition_confirmed << "e.'SAT' > 1" << "e.'EME' > 1"; stmt_not_confirmed << " MAX(d.'SAT') < 2" << " MAX(d.'EME') < 2"; stmt_any_worked << " MAX(d.'SAT') > 0" << " MAX(d.'EME') > 0"; const QString &entity = params.entitySelected; QString sourceContactsTable = sourceContactsOverride(params.userFilterWhereClause); if ( sourceContactsTable.isEmpty() ) { sourceContactsTable = QString(" source_contacts AS " " (SELECT * " " FROM contacts " " WHERE 1=1 %1 ) ").arg(params.userFilterWhereClause); } QStringList addlCTEs = additionalCTEs(entity, params.userFilterWhereClause); addlCTEs.append(sourceContactsTable); QStringList havingConditions; if ( params.notWorkedOnly ) havingConditions << QString("(%1)").arg(stmt_having.join(" AND ")); if ( params.notConfirmedOnly ) havingConditions << QString("(%1) AND (%2)").arg(stmt_not_confirmed.join(" AND "), stmt_any_worked.join(" OR ")); QString havingClause; if ( !havingConditions.isEmpty() ) havingClause = QString("HAVING %1").arg(havingConditions.join(" OR ")); QString finalSQL(QString( "WITH " " %1, " " detail_table AS ( " " SELECT %2, %3 " " %4" " WHERE 1=1 %5" " GROUP BY 1,2), " " unique_worked AS (" " SELECT DISTINCT col1" " FROM detail_table e" " WHERE %6), " " unique_confirmed AS (" " SELECT DISTINCT col1" " FROM detail_table e" " WHERE %7) " "SELECT * FROM ( " " SELECT 0 column_idx, '%8', COUNT(*), %9" " FROM unique_worked" " UNION ALL " " SELECT 0 column_idx, '%10', COUNT(*), %11" " FROM unique_confirmed" " UNION ALL " " SELECT 1 column_idx, '%12', NULL prefix, %13" " FROM detail_table a " " GROUP BY 1 " " UNION ALL " " SELECT 2 column_idx, '%14', NULL prefix, %15" " FROM detail_table a " " GROUP BY 1 " " UNION ALL " " SELECT 3 column_idx, col1, col2, %16" " FROM detail_table d " " GROUP BY 2,3 " " %17" ") " "ORDER BY 1,2 COLLATE LOCALEAWARE ASC ").arg(addlCTEs.join(","), // 1 headersColumns(entity), // 2 stmt_max_part.join(","), // 3 sqlDetailTable(entity), // 4 additionalWhere(entity), // 5 stmt_total_band_condition_work.join(" OR "), // 6 stmt_total_band_condition_confirmed.join(" OR "), // 7 QObject::tr("TOTAL Worked"), // 8 stmt_total_padding.join(",") // 9 #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) , #else ).arg( #endif QObject::tr("TOTAL Confirmed"), // 10 stmt_total_padding.join(","), // 11 QObject::tr("Confirmed"), // 12 stmt_sum_confirmed.join(",") // 13 #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) , #else ).arg( #endif QObject::tr("Worked")).arg( // 14 stmt_sum_worked.join(","), // 15 stmt_sum_total.join(","), // 16 havingClause) // 17 ); qCDebug(runtime) << finalSQL; m_model->setQuery(finalSQL); m_model->setHeaderData(1, Qt::Horizontal, ""); m_model->setHeaderData(2, Qt::Horizontal, ""); m_tableView->setModel(m_model); m_tableView->setColumnHidden(0, true); } BandTableAward::ConditionResult BandTableAward::getConditionSelected(const QModelIndex &clickedIndex) const { FCT_IDENTIFICATION; ConditionResult result; if ( !clickedIndex.isValid() || clickedIndex.row() <= 3 ) return result; const QString col1Value = m_model->data(m_model->index(clickedIndex.row(), 1), Qt::DisplayRole).toString(); const QString col2Value = m_model->data(m_model->index(clickedIndex.row(), 2), Qt::DisplayRole).toString(); QStringList addlFilters; if ( entityInputEnabled() ) addlFilters << QString("my_dxcc='%1'").arg(m_currentEntity); if ( clickUsesCountryName() ) { result.country = col1Value; } else { const QString filter = clickFilter(col1Value, col2Value); Q_ASSERT_X(!filter.isEmpty(), "BandTableAward::getConditionSelected", "clickFilter() returned empty string — subclass must override clickFilter() or clickUsesCountryName()"); addlFilters << filter; } if ( clickedIndex.column() > 2 ) result.band = m_model->headerData(clickedIndex.column(), Qt::Horizontal).toString(); result.filter = QString("(") + addlFilters.join(" and ") + QString(")"); result.valid = true; return result; } QStringList BandTableAward::additionalCTEs(const QString &, const QString &) const { return QStringList(); } QString BandTableAward::sourceContactsOverride(const QString &) const { return QString(); } QString BandTableAward::clickFilter(const QString &, const QString &) const { return QString(); } bool BandTableAward::clickUsesCountryName() const { return false; } QString BandTableAward::generateNumberRangeCTE(const QString &name, int min, int max) { return QString(" %1 AS (" " SELECT %2 AS n, %2 AS value" " UNION ALL" " SELECT n + 1, value + 1" " FROM %1" " WHERE n < %3 )").arg(name).arg(min).arg(max); } QString BandTableAward::generateValuesCTE(const QString &name, const QList> &values) { QStringList rows; for ( const QPair &pair : values ) rows << QString("('%1', '%2')").arg(pair.first, pair.second); return QString(" %1 AS (VALUES %2)").arg(name, rows.join(",")); } QString BandTableAward::generateSplitCTE(const QString &sourceColumn, const QString &outputColumn, const QString &contactFilter) { return QString(" split(id, callsign, station_callsign, my_dxcc, band, dxcc, eqsl_qsl_rcvd, lotw_qsl_rcvd, qsl_rcvd, prop_mode, mode, %1, str) AS (" " SELECT id, callsign, station_callsign, my_dxcc, band, " " dxcc, eqsl_qsl_rcvd, lotw_qsl_rcvd, qsl_rcvd, prop_mode, mode, " " '', %2||',' " " FROM contacts WHERE 1=1 %3" " UNION ALL " " SELECT id, callsign, station_callsign, my_dxcc, band, " " dxcc, eqsl_qsl_rcvd, lotw_qsl_rcvd, qsl_rcvd, prop_mode, mode, " " substr(str, 0, instr(str, ',')), TRIM(substr(str, instr(str, ',') + 1)) " " FROM split " " WHERE str != '') ").arg(outputColumn, sourceColumn, contactFilter); } QString BandTableAward::generateSplitSourceContacts(const QString &outputColumn) { return QString(" source_contacts AS (" " SELECT id, callsign, station_callsign, my_dxcc, band, dxcc, eqsl_qsl_rcvd, lotw_qsl_rcvd, qsl_rcvd, prop_mode, mode, %1 " " FROM split " " WHERE %1 != '' ) ").arg(outputColumn); } ================================================ FILE: awards/BandTableAward.h ================================================ #ifndef QLOG_AWARDS_BANDTABLEAWARD_H #define QLOG_AWARDS_BANDTABLEAWARD_H #include #include #include "AwardDefinition.h" #include "models/AwardsTableModel.h" /* Standard band-column table award — the default display for most awards. * * This class builds and executes a SQL query that produces a table where: * - Rows = award entities (countries, zones, parks, summits, ...) * - Cols = amateur bands + SAT + EME * - Cells = worked / confirmed / not-worked status (color-coded) * * Summary rows (TOTAL Worked, TOTAL Confirmed, per-band Worked/Confirmed counts) * are prepended automatically. * * === How to add a new band-table award === * * 1. Create a subclass of BandTableAward. * 2. Implement key() and displayName() (identity). * 3. Override entityInputEnabled() / notWorkedEnabled() if needed. * 4. Implement the three REQUIRED SQL fragment methods: * - headersColumns() — what goes into SELECT as col1, col2 * - sqlDetailTable() — FROM + JOINs * - additionalWhere() — extra WHERE conditions * 5. Override clickFilter() so double-click navigates to the right QSOs. * 6. Register in AwardsDialog::createAwards(). * * That's it for simple awards. For awards that need a CTE (e.g. a number range * or a comma-separated field split), override additionalCTEs() and optionally * sourceContactsOverride(). Use the static helper methods to build CTEs without * writing raw SQL. * * === The generated SQL structure === * * WITH * , -- optional, from additionalCTEs() * source_contacts AS (SELECT ... FROM contacts WHERE ...), -- or sourceContactsOverride() * detail_table AS ( * SELECT col1, col2, * MAX(CASE WHEN band='160m' ... END) as '160m', * ... -- one column per band + SAT + EME * -- FROM ... JOIN ... * -- AND ... * GROUP BY 1,2 * ), * unique_worked AS (...), * unique_confirmed AS (...) * SELECT ... -- summary rows + detail rows * ORDER BY 1,2 COLLATE LOCALEAWARE ASC * * === Double-click behavior === * * When a user double-clicks a detail row (row > 3), getConditionSelected() * builds a ConditionResult by calling clickFilter(col1, col2). The dialog * then emits AwardConditionSelected() to filter the logbook. * * Override clickUsesCountryName() to return true if col1 is a country name * that should be passed as the "country" parameter instead of a filter clause * (used by DXCC). */ class BandTableAward : public AwardDefinition { public: QWidget* createWidget(QWidget *parent) override; void updateData(const AwardFilterParams ¶ms) override; ConditionResult getConditionSelected(const QModelIndex &clickedIndex) const override; protected: // =================================================================== // REQUIRED — every subclass must implement these three methods // =================================================================== /* SQL fragment for the SELECT clause that defines the two label columns. * * Must return exactly two expressions aliased as "col1" and "col2". * col1 is the primary identifier (displayed in the first column). * col2 is a secondary label (displayed in the second column), or NULL. * * The expressions can reference: * - "d" — the directory/reference table (if using a CTE or lookup table) * - "c" — the source_contacts table * - "s", "p", "w" — any alias used in sqlDetailTable() * * entity - the selected DXCC entity ID (may be unused if entityInputEnabled() == false) * * Examples: * "d.n col1, null col2" — WAZ (zone number, no secondary) * "translate_to_locale(d.name) col1, d.prefix col2" — DXCC (country name + prefix) * "p.reference col1, p.name col2" — POTA (park reference + park name) */ virtual QString headersColumns(const QString &entity) const = 0; /* SQL fragment starting with FROM that defines the source tables and joins. * * This is inserted into the detail_table CTE after the SELECT clause. * Must join to "source_contacts c" and "modes m" (m.dxcc is used for mode filtering). * * IMPORTANT: Must NOT contain a WHERE clause at the outer level. * The SQL template adds "WHERE 1=1" automatically after this fragment, * followed by additionalWhere(). Place entity filtering in the JOIN ON * conditions, and any other WHERE-level filters in additionalWhere(). * * The join to source_contacts can be: * - LEFT OUTER JOIN -- when all possible entities should appear (even unworked ones). * Use this for awards with a fixed set of targets (DXCC, WAZ, WAC, WAS, ITU). * - INNER JOIN -- when only worked entities should appear. * Use this for open-ended awards (WPX, IOTA, POTA, SOTA, WWFF, Gridsquare). * * entity - the selected DXCC entity ID * * Examples: * " FROM cqzCTE d" * " LEFT OUTER JOIN source_contacts c ON d.n = c.cqz AND c.my_dxcc = ''" * " LEFT OUTER JOIN modes m ON c.mode = m.name" * * " FROM sota_summits s" * " INNER JOIN source_contacts c ON c.sota_ref = s.summit_code" * " INNER JOIN modes m ON c.mode = m.name" */ virtual QString sqlDetailTable(const QString &entity) const = 0; /* Extra WHERE conditions appended to the detail_table query. * * The SQL template always emits "WHERE 1=1" before this fragment, * so sqlDetailTable() must NOT contain its own WHERE clause. * * Must start with " AND " if non-empty. Return empty QString() if not needed. * * Use this for: * - filtering the directory/reference table (LEFT OUTER JOIN awards) * - requiring non-null fields (INNER JOIN awards) * Do NOT duplicate entity filters that are already in sqlDetailTable's JOIN ON. * * entity - the selected DXCC entity ID * * Examples: * " AND d.dxcc IN (6, 110, 291)" -- filter directory table * " AND c.iota is not NULL" -- require non-null field * QString() -- no extra filter (SOTA, WWFF) */ virtual QString additionalWhere(const QString &entity) const = 0; // =================================================================== // OPTIONAL — override only when the default behavior is not enough // =================================================================== /* Return additional CTEs to prepend before source_contacts. * * Override this when the award needs a reference table that doesn't exist * in the database (e.g. a numbered range for zones, a values list for continents, * or a split CTE for comma-separated fields). * * Each string in the list is a single CTE definition (without leading comma). * Default: empty list. * * Use the static helper methods to build CTEs: * - generateNumberRangeCTE() — for ITU (1-90), WAZ (1-40) * - generateValuesCTE() — for WAC continents * - generateSplitCTE() — for POTA (comma-separated references) * * entity - the selected DXCC entity ID * contactFilter - the user filter WHERE clause (needed by split CTEs) */ virtual QStringList additionalCTEs(const QString &entity, const QString &contactFilter) const; /* Override the default source_contacts CTE. * * By default, source_contacts is: SELECT * FROM contacts WHERE 1=1 . * Override this when the award needs a transformed contact source (e.g. POTA splits * comma-separated references into individual rows). * * Return empty QString() to use the default. Otherwise return a complete CTE definition * for "source_contacts AS (...)". * * contactFilter - the user filter WHERE clause * * See generateSplitSourceContacts() — helper for the split pattern. */ virtual QString sourceContactsOverride(const QString &contactFilter) const; /* Return a SQL WHERE fragment for logbook filtering on double-click. * * Called when the user double-clicks a detail row. The returned string is * combined with entity filter and band into a complete WHERE clause. * * col1Value - value from column 1 (the primary identifier) * col2Value - value from column 2 (the secondary label) * * Examples: * "cqz = '15'" — WAZ: filter by CQ zone * "sota_ref = 'OE/TI-001'" — SOTA: filter by summit * "pota_ref LIKE '%K-0001%'" — POTA: LIKE match for multi-ref field * "state = 'CA' and dxcc in (...)" — WAS: compound filter */ virtual QString clickFilter(const QString &col1Value, const QString &col2Value) const; /* Return true if col1 is a country name to be passed as the "country" * parameter of AwardConditionSelected (instead of a filter clause). * Only DXCC uses this. Default: false. */ virtual bool clickUsesCountryName() const; // =================================================================== // STATIC HELPERS — use these in additionalCTEs() / sourceContactsOverride() // =================================================================== /* Generate a recursive CTE that produces integers from min to max. * Output columns: n (the integer), value (same as n). * Example: generateNumberRangeCTE("cqzCTE", 1, 40) produces zones 1-40. */ static QString generateNumberRangeCTE(const QString &name, int min, int max); /* Generate a VALUES CTE from a list of (key, label) pairs. * Output columns: column1 (key), column2 (label). * Example: generateValuesCTE("continents", {{"NA","North America"}, {"EU","Europe"}, ...}) */ static QString generateValuesCTE(const QString &name, const QList> &values); /* Generate a recursive split CTE that expands comma-separated values * from sourceColumn into individual rows with column outputColumn. * * Used for POTA where pota_ref or my_pota_ref may contain "K-0001,K-0002". * The CTE is named "split" and should be paired with generateSplitSourceContacts(). * * sourceColumn - the contacts table column to split (e.g. "pota_ref") * outputColumn - the name for the split output column (e.g. "pota") * contactFilter - the user filter WHERE clause */ static QString generateSplitCTE(const QString &sourceColumn, const QString &outputColumn, const QString &contactFilter); /* Generate a source_contacts CTE that reads from the "split" CTE. * Companion to generateSplitCTE(). Returns a CTE definition for source_contacts * that selects non-empty rows from the split result. * outputColumn - must match the outputColumn used in generateSplitCTE(). */ static QString generateSplitSourceContacts(const QString &outputColumn); private: QTableView *m_tableView = nullptr; AwardsTableModel *m_model = nullptr; QString m_currentEntity; }; #endif // QLOG_AWARDS_BANDTABLEAWARD_H ================================================ FILE: awards/SecondarySubdivisionAward.cpp ================================================ #include "SecondarySubdivisionAward.h" SecondarySubdivisionAward::SecondarySubdivisionAward(const QString &key, const QString &dxccFilter) : m_key(key), m_dxccFilter(dxccFilter) { } QString SecondarySubdivisionAward::headersColumns(const QString &) const { return QStringLiteral("d.subdivision_name col1, d.code col2 "); } QString SecondarySubdivisionAward::sqlDetailTable(const QString &) const { return " FROM adif_enum_secondary_subdivision d " " LEFT OUTER JOIN (SELECT * FROM source_contacts " " WHERE dxcc IN (" + m_dxccFilter + ") " " AND cnty IS NOT NULL AND cnty != '') c " " ON d.dxcc = c.dxcc AND upper(d.code) = upper(c.cnty) " " LEFT OUTER JOIN modes m ON c.mode = m.name "; } QString SecondarySubdivisionAward::additionalWhere(const QString &) const { return " AND d.dxcc IN (" + m_dxccFilter + ") "; } QString SecondarySubdivisionAward::clickFilter(const QString &, const QString &col2Value) const { return QString("upper(cnty) = upper('%1') and dxcc in (%2)").arg(col2Value, m_dxccFilter); } ================================================ FILE: awards/SecondarySubdivisionAward.h ================================================ #ifndef QLOG_AWARDS_SECONDARYSUBDIVISIONAWARD_H #define QLOG_AWARDS_SECONDARYSUBDIVISIONAWARD_H #include "BandTableAward.h" /* Base class for awards based on adif_enum_secondary_subdivision table. * * These awards share identical SQL structure — only the DXCC entity filter, * display name, and key differ. Subclasses only need to provide identity * (key, displayName) and the DXCC filter via constructor. */ class SecondarySubdivisionAward : public BandTableAward { public: SecondarySubdivisionAward(const QString &key, const QString &dxccFilter); QString key() const override { return m_key; } bool entityInputEnabled() const override { return false; } protected: QString headersColumns(const QString &entity) const override; QString sqlDetailTable(const QString &entity) const override; QString additionalWhere(const QString &entity) const override; QString clickFilter(const QString &col1Value, const QString &col2Value) const override; private: QString m_key; QString m_dxccFilter; }; #endif // QLOG_AWARDS_SECONDARYSUBDIVISIONAWARD_H ================================================ FILE: core/AlertEvaluator.cpp ================================================ #include #include #include #include "AlertEvaluator.h" #include "debug.h" #include "data/DxSpot.h" #include "data/WsjtxEntry.h" #include "data/SpotAlert.h" #include "data/BandPlan.h" MODULE_IDENTIFICATION("qlog.ui.alertevaluator"); AlertEvaluator::AlertEvaluator(QObject *parent) : QObject(parent) { FCT_IDENTIFICATION; loadRules(); } void AlertEvaluator::clearRules() { FCT_IDENTIFICATION; qDeleteAll(ruleList); ruleList.clear(); } void AlertEvaluator::dxSpot(const DxSpot & spot) { FCT_IDENTIFICATION; qCDebug(function_parameters) << "DX Spot"; QStringList matchedRules; for ( const AlertRule *rule : static_cast&>(ruleList) ) { qCDebug(runtime) << "Processing " << *rule; if ( rule->match(spot) ) { matchedRules << rule->ruleName; } } if ( matchedRules.size() > 0 ) { SpotAlert alert(matchedRules, spot); emit spotAlert(alert); } } void AlertEvaluator::WSJTXCQSpot(const WsjtxEntry &wsjtx) { FCT_IDENTIFICATION; qCDebug(function_parameters) << "WSJTX CQ Spot"; QStringList matchedRules; for ( const AlertRule *rule : static_cast&>(ruleList) ) { qCDebug(runtime) << "Processing " << *rule; if ( rule->match(wsjtx) ) { matchedRules << rule->ruleName; } } if ( matchedRules.size() > 0 ) { SpotAlert alert(matchedRules, wsjtx); emit spotAlert(alert); } } void AlertEvaluator::loadRules() { FCT_IDENTIFICATION; if ( ruleList.size() > 0 ) { clearRules(); } QSqlQuery ruleStmt; if ( ! ruleStmt.prepare("SELECT rule_name FROM alert_rules") ) { qWarning() << "Cannot prepare select statement"; } else { if ( ruleStmt.exec() ) { while (ruleStmt.next()) { AlertRule *rule; rule = new AlertRule(); if ( rule ) { rule->load(ruleStmt.value(0).toString()); ruleList.append(rule); } } } else { qInfo()<< "Cannot get filters names from DB" << ruleStmt.lastError(); } } } AlertRule::AlertRule(QObject *parent) : QObject(parent), enabled(false), sourceMap(SpotAlert::UKNOWN), dxCountry(-1), dxLogStatusMap(0), spotterCountry(-1), ituz(0), cqz(0), pota(false), sota(false), iota(false), wwff(false), ruleValid(false) { FCT_IDENTIFICATION; } bool AlertRule::save() { FCT_IDENTIFICATION; if ( ruleName.isEmpty() ) { qCDebug(runtime) << "rule name is empty - do not save"; return false; } QSqlQuery insertUpdateStmt; if ( ! insertUpdateStmt.prepare("INSERT INTO alert_rules(rule_name, enabled, source, dx_callsign, dx_country, " "dx_logstatus, dx_continent, spot_comment, mode, band, spotter_country, spotter_continent, dx_member, ituz, cqz, pota, sota, iota, wwff) " " VALUES (:ruleName, :enabled, :source, :dxCallsign, :dxCountry, " ":dxLogstatus, :dxContinent, :spotComment, :mode, :band, :spotterCountry, :spotterContinent, :dxMember, :ituz, :cqz, :pota, :sota, :iota, :wwff) " " ON CONFLICT(rule_name) DO UPDATE SET enabled = :enabled, source = :source, dx_callsign =:dxCallsign, " "dx_country = :dxCountry, dx_logstatus = :dxLogstatus, dx_continent = :dxContinent, spot_comment = :spotComment, " "mode = :mode, band = :band, spotter_country = :spotterCountry, spotter_continent = :spotterContinent, dx_member = :dxMember, ituz = :ituz, cqz = :cqz, pota = :pota, sota = :sota, iota = :iota, wwff = :wwff " " WHERE rule_name = :ruleName")) { qWarning() << "Cannot prepare insert/update Alert Rule statement" << insertUpdateStmt.lastError(); return false; } insertUpdateStmt.bindValue(":ruleName", ruleName); insertUpdateStmt.bindValue(":enabled", enabled); insertUpdateStmt.bindValue(":source", sourceMap); insertUpdateStmt.bindValue(":dxCallsign", dxCallsign); insertUpdateStmt.bindValue(":dxCountry", dxCountry); insertUpdateStmt.bindValue(":dxLogstatus", dxLogStatusMap); insertUpdateStmt.bindValue(":dxContinent", dxContinent); insertUpdateStmt.bindValue(":dxMember", dxMember.join(",")); insertUpdateStmt.bindValue(":spotComment", dxComment); insertUpdateStmt.bindValue(":mode", mode); insertUpdateStmt.bindValue(":band", band); insertUpdateStmt.bindValue(":spotterCountry", spotterCountry); insertUpdateStmt.bindValue(":spotterContinent", spotterContinent); insertUpdateStmt.bindValue(":cqz", cqz); insertUpdateStmt.bindValue(":ituz", ituz); insertUpdateStmt.bindValue(":pota", pota); insertUpdateStmt.bindValue(":sota", sota); insertUpdateStmt.bindValue(":iota", iota); insertUpdateStmt.bindValue(":wwff", wwff); if ( ! insertUpdateStmt.exec() ) { qCDebug(runtime)<< "Cannot Update Alert Rules - " << insertUpdateStmt.lastError().text(); return false; } return true; } bool AlertRule::load(const QString &in_ruleName) { FCT_IDENTIFICATION; qCDebug(function_parameters) << in_ruleName; QSqlQuery query; if ( ! query.prepare("SELECT rule_name, enabled, source, dx_callsign, dx_country, dx_logstatus, " "dx_continent, spot_comment, mode, band, spotter_country, spotter_continent, dx_member, ituz, cqz, pota, sota, iota, wwff " "FROM alert_rules " "WHERE rule_name = :rule") ) { qWarning() << "Cannot prepare select statement"; return false; } query.bindValue(":rule", in_ruleName); if ( query.exec() ) { query.next(); QSqlRecord record = query.record(); ruleName = in_ruleName; enabled = record.value("enabled").toBool(); sourceMap = record.value("source").toInt(); dxCallsign = record.value("dx_callsign").toString(); dxCountry = record.value("dx_country").toInt(); dxLogStatusMap = record.value("dx_logstatus").toInt(); dxContinent = record.value("dx_continent").toString(); dxComment = record.value("spot_comment").toString(); dxMember = record.value("dx_member").toString().split(","); #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) dxMemberSet = QSet(dxMember.begin(), dxMember.end()); #else /* Due to ubuntu 20.04 where qt5.12 is present */ dxMemberSet = QSet(QSet::fromList(dxMember)); #endif mode = record.value("mode").toString(); band = record.value("band").toString(); spotterCountry = record.value("spotter_country").toInt(); spotterContinent = record.value("spotter_continent").toString(); ituz = record.value("ituz").toInt(); cqz = record.value("cqz").toInt(); pota = record.value("pota").toBool(); sota = record.value("sota").toBool(); iota = record.value("iota").toBool(); wwff = record.value("wwff").toBool(); callsignRE.setPattern(dxCallsign); callsignRE.setPatternOptions(QRegularExpression::CaseInsensitiveOption); commentRE.setPattern(dxComment); commentRE.setPatternOptions(QRegularExpression::CaseInsensitiveOption); } else { qCDebug(runtime) << "SQL execution error: " << query.lastError().text(); return false; } qCDebug(runtime) << "Rule: " << ruleName << " was loaded"; ruleValid = true; return true; } bool AlertRule::match(const WsjtxEntry &wsjtx) const { FCT_IDENTIFICATION; qCDebug(function_parameters) << wsjtx; auto fail = [&]() -> bool { qCDebug(runtime) << "Rule name:" << ruleName << "- result false"; return false; }; /* the first part validates a primitive types */ if ( !isValid() || !enabled ) return fail(); if ( !(sourceMap & SpotAlert::WSJTXCQSPOT) ) return fail(); if ( dxCountry && dxCountry != wsjtx.dxcc.dxcc ) return fail(); if ( ituz && ituz != wsjtx.dxcc.ituz ) return fail(); if ( cqz && cqz != wsjtx.dxcc.cqz ) return fail(); if ( pota || sota || iota || wwff ) { const bool refMatch = (pota && wsjtx.containsPOTA) || (sota && wsjtx.containsSOTA) || (iota && wsjtx.containsIOTA) || (wwff && wsjtx.containsWWFF); if ( !refMatch ) return fail(); } if ( !(wsjtx.status & dxLogStatusMap) ) return fail(); if ( mode != "*" ) { const QString &group = BandPlan::isFTxMode(wsjtx.decodedMode) ? BandPlan::MODE_GROUP_STRING_FTx : BandPlan::MODE_GROUP_STRING_DIGITAL; if ( !mode.contains(QLatin1Char('|') + group) ) return fail(); } if ( band != "*" && !band.contains(QLatin1Char('|') + wsjtx.band) ) return fail(); if ( spotterCountry && spotterCountry != wsjtx.dxcc_spotter.dxcc) return fail(); if ( dxContinent != "*" && !dxContinent.contains(QLatin1Char('|') + wsjtx.dxcc.cont)) return fail(); if ( spotterContinent != "*" && !spotterContinent.contains(QLatin1Char('|') + wsjtx.dxcc_spotter.cont)) return fail(); if ( !(dxMember.size() == 1 && dxMember.front() == QLatin1String("*"))) { if ( !wsjtx.memberList2Set().intersects(dxMemberSet) ) return fail(); } qCDebug(runtime) << "Rule match - phase 1 - OK"; qCDebug(runtime) << "Callsign RE" << callsignRE.pattern(); qCDebug(runtime) << "Comment RE" << commentRE.pattern(); const bool ret = callsignRE.match(wsjtx.callsign).hasMatch() && commentRE.match(wsjtx.decode.message).hasMatch(); qCDebug(runtime) << "Rule name: " << ruleName << " - result " << ret; return ret; } bool AlertRule::match(const DxSpot &spot) const { FCT_IDENTIFICATION; qCDebug(function_parameters) << spot; auto fail = [&]() -> bool { qCDebug(runtime) << "Rule name:" << ruleName << "- result false"; return false; }; /* the first part validates a primitive types */ if ( !isValid() || !enabled ) return fail(); if ( !(sourceMap & SpotAlert::DXSPOT) ) return fail(); if ( dxCountry != 0 && dxCountry != spot.dxcc.dxcc ) return fail(); if ( ituz != 0 && ituz != spot.dxcc.ituz ) return fail(); if ( cqz != 0 && cqz != spot.dxcc.cqz ) return fail(); if ( pota || sota || iota || wwff ) { const bool refMatch = (pota && spot.containsPOTA) || (sota && spot.containsSOTA) || (iota && spot.containsIOTA) || (wwff && spot.containsWWFF); if ( !refMatch ) return fail(); } if ( !(spot.status & dxLogStatusMap) ) return fail(); if ( mode != "*" ) { if (spot.modeGroupString.isEmpty()) return fail(); if (!mode.contains(QLatin1Char('|') + spot.modeGroupString)) return fail(); } if ( band != "*" ) { if (spot.band.isEmpty()) return fail(); if (!band.contains(QLatin1Char('|') + spot.band)) return fail(); } if ( spotterCountry != 0 && spotterCountry != spot.dxcc_spotter.dxcc ) return fail(); if ( dxContinent != "*" ) { if ( spot.dxcc.cont.isEmpty() ) return fail(); if ( !dxContinent.contains(QLatin1Char('|') + spot.dxcc.cont) ) return fail(); } if ( spotterContinent != "*" ) { if ( spot.dxcc_spotter.cont.isEmpty() ) return fail(); if ( !spotterContinent.contains(QLatin1Char('|') + spot.dxcc_spotter.cont) ) return fail(); } if ( !(dxMember.size() == 1 && dxMember.front() == QLatin1String("*")) ) { if (!spot.memberList2Set().intersects(dxMemberSet)) return fail(); } qCDebug(runtime) << "Rule match - phase 1 - OK"; qCDebug(runtime) << "Callsign RE" << callsignRE.pattern(); qCDebug(runtime) << "Comment RE" << commentRE.pattern(); const bool ret = callsignRE.match(spot.callsign).hasMatch() && commentRE.match(spot.comment).hasMatch(); qCDebug(runtime) << "Rule name:" << ruleName << "- result" << ret; return ret; } bool AlertRule::isValid() const { FCT_IDENTIFICATION; return ruleValid; } AlertRule::operator QString() const { return QString("AlerRule: ") + "(" + "Rule Name: " + ruleName + "; " + "isValid: " + QString::number(isValid()) + "; " + "Enabled: " + QString::number(enabled) + "; " + "SourceMap: 0b" + QString::number(sourceMap,2) + "; " + "dxCallsign: " + dxCallsign + "; " + "CQZ: " + QString::number(cqz) + "; " + "ITUZ: " + QString::number(ituz) + "; " + "POTA: " + (pota ? "true" : "false") + "; " + "SOTA: " + (sota ? "true" : "false") + "; " + "IOTA: " + (iota ? "true" : "false") + "; " + "WWFF: " + (wwff ? "true" : "false") + "; " + "dxMember: " + dxMember.join(", ") + "; " + "dxCountry: " + QString::number(dxCountry) + "; " + "dxLogStatusMap: 0b" + QString::number(dxLogStatusMap,2) + "; " + "dxComment: " + dxComment + "; " + "mode: " + mode + "; " + "band: " + band + "; " + "spotterCountry: " + QString::number(spotterCountry) + "; " + "spotterContinent: " + spotterContinent + "; " + ")"; } ================================================ FILE: core/AlertEvaluator.h ================================================ #ifndef QLOG_CORE_ALERTEVALUATOR_H #define QLOG_CORE_ALERTEVALUATOR_H #include #include "data/DxSpot.h" #include "data/WsjtxEntry.h" #include "data/SpotAlert.h" #include class AlertRule : public QObject { Q_OBJECT public: explicit AlertRule(QObject *parent = nullptr); ~AlertRule() {}; bool save(); bool load(const QString &); bool match(const WsjtxEntry &wsjtx) const; bool match(const DxSpot & spot) const; bool isValid() const; operator QString() const; public: QString ruleName; bool enabled; int sourceMap; QString dxCallsign; int dxCountry; int dxLogStatusMap; QString dxContinent; QString dxComment; QStringList dxMember; QString mode; QString band; int spotterCountry; QString spotterContinent; int ituz; int cqz; bool pota; bool sota; bool iota; bool wwff; private: bool ruleValid; QRegularExpression callsignRE; QRegularExpression commentRE; QSet dxMemberSet; }; class AlertEvaluator : public QObject { Q_OBJECT public: explicit AlertEvaluator(QObject *parent = nullptr); ~AlertEvaluator() {clearRules();} void clearRules(); public slots: void dxSpot(const DxSpot&); void WSJTXCQSpot(const WsjtxEntry&); void loadRules(); signals: void spotAlert(SpotAlert alert); private: QListruleList; }; #endif // QLOG_CORE_ALERTEVALUATOR_H ================================================ FILE: core/AppGuard.cpp ================================================ #include #include #if QT_VERSION >= QT_VERSION_CHECK(6,6,0) #include #endif #include "AppGuard.h" #include "debug.h" MODULE_IDENTIFICATION("qlog.core.appguard"); namespace { QString generateKeyHash( const QString& key, const QString& salt ) { QByteArray data; data.append( key.toUtf8() ); data.append( salt.toUtf8() ); data = QCryptographicHash::hash( data, QCryptographicHash::Sha1 ).toHex(); return data; } } AppGuard::AppGuard( const QString& key ) : key( key ) , memLockKey( generateKeyHash( key, "_memLockKey" ) ) , sharedmemKey( generateKeyHash( key, "_sharedmemKey" ) ) , sharedMem( #if QT_VERSION >= QT_VERSION_CHECK(6,6,0) QSharedMemory::legacyNativeKey(sharedmemKey) #else sharedmemKey #endif ) , memLock( #if QT_VERSION >= QT_VERSION_CHECK(6,6,0) QSystemSemaphore::legacyNativeKey(memLockKey), #else memLockKey, #endif 1 ) { FCT_IDENTIFICATION; memLock.acquire(); { // linux / unix shared memory is not freed when the application terminates abnormally, // so you need to get rid of the garbage QSharedMemory fix( #if QT_VERSION >= QT_VERSION_CHECK(6,6,0) QSharedMemory::legacyNativeKey(sharedmemKey) #else sharedmemKey #endif ); if ( fix.attach() ) { fix.detach(); } } memLock.release(); } AppGuard::~AppGuard() { release(); } bool AppGuard::isAnotherRunning(void) { FCT_IDENTIFICATION; if ( sharedMem.isAttached() ) return false; memLock.acquire(); const bool isRunning = sharedMem.attach(); if ( isRunning ) { sharedMem.detach(); } memLock.release(); return isRunning; } bool AppGuard::tryToRun(void) { FCT_IDENTIFICATION; if ( isAnotherRunning() ) // Extra check { return false; } memLock.acquire(); // The following 'attach' call is a workaround required for 'create' to run properly under QT 6.6 and MacOS. // See more details about it in issue #257. // It has no impact on other platforms and versions of Qt, therefore there is no compilation condition. sharedMem.attach(); const bool result = sharedMem.create( sizeof( quint64 ) ); memLock.release(); if ( !result ) { release(); return false; } return true; } void AppGuard::release(void) { FCT_IDENTIFICATION; memLock.acquire(); if ( sharedMem.isAttached() ) { sharedMem.detach(); } memLock.release(); } ================================================ FILE: core/AppGuard.h ================================================ #ifndef QLOG_CORE_APPGUARD_H #define QLOG_CORE_APPGUARD_H #include #include #include class AppGuard { public: explicit AppGuard( const QString& key ); ~AppGuard(); bool isAnotherRunning(void); bool tryToRun(void); void release(void); private: const QString key; const QString memLockKey; const QString sharedmemKey; QSharedMemory sharedMem; QSystemSemaphore memLock; Q_DISABLE_COPY( AppGuard ) }; #endif // QLOG_CORE_APPGUARD_H ================================================ FILE: core/CallbookManager.cpp ================================================ #include #include "CallbookManager.h" #include "core/debug.h" #include "service/hamqth/HamQTH.h" #include "service/qrzcom/QRZ.h" #include "data/Callsign.h" #include "LogParam.h" MODULE_IDENTIFICATION("qlog.ui.callbookmanager"); CallbookManager::CallbookManager(QObject *parent) : QObject{parent}, primaryCallbookAuthSuccess(false), secondaryCallbookAuthSuccess(false) { FCT_IDENTIFICATION; initCallbooks(); } void CallbookManager::queryCallsign(const QString &callsign) { FCT_IDENTIFICATION; qCDebug(function_parameters) << callsign; if ( queryCache.contains(callsign) ) { emit callsignResult(CallbookResponseData(*queryCache.object(callsign))); return; } // create an empty object in cache // if there is the second query for the same call immediately after // the first query, then it returns a result of empty object queryCache.insert(callsign, new CallbookResponseData); if ( !primaryCallbook.isNull() ) { currentQueryCallsign = callsign; primaryCallbook->queryCallsign(currentQueryCallsign); } else { queryCache.remove(callsign); emit callsignNotFound(callsign); } } bool CallbookManager::isActive() { FCT_IDENTIFICATION; bool ret = primaryCallbookAuthSuccess || secondaryCallbookAuthSuccess; qCDebug(runtime) << ret; return ret; } GenericCallbook *CallbookManager::createCallbook(const QString &callbookID) { FCT_IDENTIFICATION; GenericCallbook *ret = nullptr; if (callbookID == HamQTHCallbook::CALLBOOK_NAME ) { ret = new HamQTHCallbook(this); } else if ( callbookID == QRZCallbook::CALLBOOK_NAME ) { ret = new QRZCallbook(this); } if ( ret ) { connect(ret, &GenericCallbook::callsignResult, this, &CallbookManager::processCallsignResult); } return ret; } void CallbookManager::initCallbooks() { primaryCallbook.clear(); secondaryCallbook.clear(); primaryCallbookAuthSuccess = false; secondaryCallbookAuthSuccess = false; queryCache.clear(); currentQueryCallsign = QString(); primaryCallbook = createCallbook(LogParam::getPrimaryCallbook(GenericCallbook::CALLBOOK_NAME)); secondaryCallbook = createCallbook(LogParam::getSecondaryCallbook(GenericCallbook::CALLBOOK_NAME)); if ( !primaryCallbook.isNull() ) { connect(primaryCallbook, &GenericCallbook::callsignNotFound, this, &CallbookManager::primaryCallbookCallsignNotFound); connect(primaryCallbook, &GenericCallbook::loginFailed, this, [this]() { primaryCallbookAuthSuccess = false; emit loginFailed(primaryCallbook->getDisplayName()); }); connect(primaryCallbook, &GenericCallbook::lookupError, this, [this](const QString &error) { emit lookupError(primaryCallbook->getDisplayName() + " - " + error + ((!secondaryCallbook.isNull()) ? tr("

The secondary callbook will be used

") : "")); primaryCallbookCallsignNotFound(currentQueryCallsign); }); primaryCallbookAuthSuccess = true; } if ( !secondaryCallbook.isNull() ) { connect(secondaryCallbook, &GenericCallbook::callsignNotFound, this, &CallbookManager::secondaryCallbookCallsignNotFound); connect(secondaryCallbook, &GenericCallbook::loginFailed, this, [this]() { secondaryCallbookAuthSuccess = false; emit loginFailed(secondaryCallbook->getDisplayName()); }); connect(secondaryCallbook, &GenericCallbook::lookupError, this, [this](const QString &error) { emit lookupError(secondaryCallbook->getDisplayName() + " - " + error); secondaryCallbookCallsignNotFound(currentQueryCallsign); }); secondaryCallbookAuthSuccess = true; } } void CallbookManager::abortQuery() { FCT_IDENTIFICATION; queryCache.remove(currentQueryCallsign); if ( ! primaryCallbook.isNull() ) primaryCallbook->abortQuery(); if ( ! secondaryCallbook.isNull() ) secondaryCallbook->abortQuery(); } void CallbookManager::primaryCallbookCallsignNotFound(const QString ¬FoundCallsign) { FCT_IDENTIFICATION; qCDebug(function_parameters) << notFoundCallsign; queryCache.remove(notFoundCallsign); if ( notFoundCallsign != currentQueryCallsign ) return ; if ( secondaryCallbook.isNull() ) { emit callsignNotFound(notFoundCallsign); return; } qCDebug(runtime) << "Callsign not found - primary - trying the secondary callbook"; secondaryCallbook->queryCallsign(notFoundCallsign); } void CallbookManager::secondaryCallbookCallsignNotFound(const QString ¬FoundCallsign) { FCT_IDENTIFICATION; qCDebug(function_parameters) << notFoundCallsign; queryCache.remove(notFoundCallsign); if ( notFoundCallsign != currentQueryCallsign ) return ; qCDebug(runtime) << "Callsign not found - secondary "; emit callsignNotFound(notFoundCallsign); } void CallbookManager::processCallsignResult(const CallbookResponseData &data) { FCT_IDENTIFICATION; CallbookResponseData *cacheData = new CallbookResponseData; *cacheData = data; queryCache.insert(data.call, cacheData); // Callbook returned queried callsign if ( data.call == currentQueryCallsign ) { emit callsignResult(data); return; } Callsign queryCall(currentQueryCallsign); if ( ! queryCall.isValid() ) { qCDebug(runtime) << "Query callsign is not valid " << currentQueryCallsign; return; } // If not exists full match record for example for SP/OK1xxx in a callbook // then callbooks return a partial result (usually base callsign) OK1xxx. In this case, QLog // takes only selected fields from the callbook response. if ( queryCall.getBase() == data.call ) { qCDebug(runtime) << "Partial match for result - forwarding limited set of information"; CallbookResponseData newData; newData.call = currentQueryCallsign; newData.fname = data.fname; newData.lname = data.lname; newData.lic_year = data.lic_year; newData.qsl_via = data.qsl_via; newData.email = data.email; newData.born = data.born; newData.url = data.url; newData.name_fmt = data.name_fmt; newData.nick = data.nick; newData.image_url = data.image_url; CallbookResponseData *cdata = new CallbookResponseData; *cdata = newData; queryCache.insert(currentQueryCallsign, cdata); emit callsignResult(newData); } } QCache CallbookManager::queryCache(100); ================================================ FILE: core/CallbookManager.h ================================================ #ifndef QLOG_CORE_CALLBOOKMANAGER_H #define QLOG_CORE_CALLBOOKMANAGER_H #include #include #include "service/GenericCallbook.h" class CallbookManager : public QObject { Q_OBJECT public: explicit CallbookManager(QObject *parent = nullptr); void queryCallsign(const QString &callsign); bool isActive(); signals: void loginFailed(QString); void callsignResult(CallbookResponseData); void callsignNotFound(QString); void lookupError(QString); public slots: void initCallbooks(); void abortQuery(); private slots: void primaryCallbookCallsignNotFound(const QString&); void secondaryCallbookCallsignNotFound(const QString&); void processCallsignResult(const CallbookResponseData &data); private: GenericCallbook *createCallbook(const QString&); private: QPointer primaryCallbook; bool primaryCallbookAuthSuccess; QPointer secondaryCallbook; bool secondaryCallbookAuthSuccess; QString currentQueryCallsign; static QCache queryCache; }; #endif // QLOG_CORE_CALLBOOKMANAGER_H ================================================ FILE: core/CredentialStore.cpp ================================================ #include #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) #include #else #include #endif #include #include #include #include #include #include #include "CredentialStore.h" #include "core/debug.h" #include "core/PasswordCipher.h" #include "core/LogParam.h" #include "core/LogDatabase.h" MODULE_IDENTIFICATION("qlog.core.credentialstore"); using namespace QKeychain; CredentialRegistry &CredentialRegistry::instance() { static CredentialRegistry r; return r; } void CredentialRegistry::add(const QString &, const std::function()> &fn) { callbacks.append(fn); } QList CredentialRegistry::allDescriptors() const { FCT_IDENTIFICATION; QList result; for ( const std::function()> &fn : callbacks ) result.append(fn()); return result; } CredentialStore::CredentialStore(QObject *parent) : QObject(parent) { FCT_IDENTIFICATION; } int CredentialStore::savePassword(const QString &storage_key, const QString &user, const QString &pass) { FCT_IDENTIFICATION; qCDebug(function_parameters) << storage_key << " " << user; if ( user.isEmpty() || storage_key.isEmpty() || pass.isEmpty() ) return 1; QString locStorageKey = qApp->applicationName() + ":" + storage_key; QString locUser = user; QString locPass = pass; QEventLoop loop; // write a password to Credential Storage WritePasswordJob job(QLatin1String(locStorageKey.toStdString().c_str())); job.setAutoDelete(false); #ifdef Q_OS_WIN // see more qtkeychain issue #105 locUser.prepend(locStorageKey + ":"); #endif job.setKey(locUser); job.setTextData(locPass); job.connect(&job, &WritePasswordJob::finished, &loop, &QEventLoop::quit); job.start(); loop.exec(); if ( job.error() && job.error() != EntryNotFound ) { QMessageBox::critical(nullptr, QMessageBox::tr("QLog Critical"), QMessageBox::tr("Cannot save a password for %1 to the Credential Store").arg(storage_key) + "

" + job.errorString()); qWarning() << "Cannot save a password. Error " << job.errorString(); return 1; } return 0; } QString CredentialStore::getPassword(const QString &storage_key, const QString &user) { FCT_IDENTIFICATION; qCDebug(function_parameters) << storage_key << " " << user; if ( user.isEmpty() || storage_key.isEmpty() ) return QString(); QString locStorageKey = qApp->applicationName() + ":" + storage_key; QString locUser = user; QString pass; QEventLoop loop; // get a password from Credential Storage ReadPasswordJob job(QLatin1String(locStorageKey.toStdString().c_str())); job.setAutoDelete(false); #ifdef Q_OS_WIN // see more qtkeychain issue #105 locUser.prepend(locStorageKey + ":"); #endif job.setKey(locUser); job.connect(&job, &ReadPasswordJob::finished, &loop, &QEventLoop::quit); job.start(); loop.exec(); pass = job.textData(); if ( job.error() && job.error() != EntryNotFound ) { QMessageBox::critical(nullptr, QMessageBox::tr("QLog Critical"), QMessageBox::tr("Cannot get a password for %1 from the Credential Store").arg(storage_key) + "

" + job.errorString()); qCDebug(runtime) << "Cannot get a password. Error " << job.errorString(); return QString(); } return pass; } void CredentialStore::deletePassword(const QString &storage_key, const QString &user) { FCT_IDENTIFICATION; qCDebug(function_parameters) << storage_key << " " << user; if ( user.isEmpty() || storage_key.isEmpty() ) return; QString locStorageKey = qApp->applicationName() + ":" + storage_key; QString locUser = user; QEventLoop loop; // delete password from Secure Storage DeletePasswordJob job(QLatin1String(locStorageKey.toStdString().c_str())); job.setAutoDelete(false); #ifdef Q_OS_WIN // see more qtkeychain issue #105 locUser.prepend(locStorageKey + ":"); #endif job.setKey(locUser); job.connect(&job, &DeletePasswordJob::finished, &loop, &QEventLoop::quit); job.start(); loop.exec(); return; } bool CredentialStore::exportPasswords(const QString &passphrase) { FCT_IDENTIFICATION; /* * Password storage format: * 1. Build JSON object: * { * "credentials": [ * {"storagekey": "...", "username": "...", "password": "..."}, * ... * ], * "padding": "" * } * 2. Encrypt JSON with AES-256-GCM using passphrase (via PasswordCipher) * 3. Store base64-encoded ciphertext in LogParam ("security/encryptedpasswords") */ const QList list = CredentialRegistry::instance().allDescriptors(); QJsonArray credArray; for ( const CredentialDescriptor &desc : list ) { QString user = desc.usernameFn(); QString pass = getPassword(desc.storageKey, user); if ( !pass.isEmpty() ) { QJsonObject entry; entry["storagekey"] = desc.storageKey; entry["username"] = user; entry["password"] = pass; credArray.append(entry); } } // generate random padding (128-512 bytes) // add this padding to make it more difficult to estimate the length of passwords. unsigned char randLenBuf[2]; if ( RAND_bytes(randLenBuf, 2) != 1 ) { qWarning() << "RAND_bytes failed for padding length"; return false; } int paddingLen = 128 + (static_cast((randLenBuf[0] << 8) | randLenBuf[1]) % (512 - 128 + 1)); QByteArray paddingRaw(paddingLen, '\0'); if ( RAND_bytes(reinterpret_cast(paddingRaw.data()), paddingLen) != 1 ) { qWarning() << "RAND_bytes failed for padding data"; return false; } QString paddingB64 = QString::fromLatin1(paddingRaw.toBase64()); QJsonObject root; root["credentials"] = credArray; root["padding"] = paddingB64; const QByteArray json = QJsonDocument(root).toJson(QJsonDocument::Compact); QByteArray blobB64; if ( !PasswordCipher::encrypt(passphrase, json, blobB64) ) { qWarning() << "Password encryption failed"; return false; } LogParam::setEncryptedPasswords(blobB64); LogParam::setSourcePlatform(LogDatabase::currentPlatformId()); return true; } bool CredentialStore::importPasswords(const QString &passphrase) { FCT_IDENTIFICATION; /* * Import process (reverse of exportPasswords): * 1. Read base64-encoded ciphertext from LogParam ("security/encryptedpasswords") * 2. Decrypt with AES-256-GCM using passphrase (via PasswordCipher) * 3. Parse JSON and restore each credential to the credential store * (padding field is ignored) */ QByteArray blobB64 = LogParam::getEncryptedPasswords(); if ( blobB64.isEmpty() ) { qWarning() << "No encrypted passwords found"; return false; } QByteArray json; if ( !PasswordCipher::decrypt(passphrase, blobB64, json) ) { qWarning() << "Password decryption failed"; return false; } QJsonParseError parseError; QJsonDocument doc = QJsonDocument::fromJson(json, &parseError); if ( parseError.error != QJsonParseError::NoError ) { qWarning() << "JSON parse error:" << parseError.errorString(); return false; } QJsonObject root = doc.object(); QJsonArray credArray = root["credentials"].toArray(); for (const QJsonValue &val : static_cast(credArray)) { QJsonObject entry = val.toObject(); QString storageKey = entry["storagekey"].toString(); QString username = entry["username"].toString(); QString password = entry["password"].toString(); if ( !storageKey.isEmpty() && !username.isEmpty() && !password.isEmpty() ) savePassword(storageKey, username, password); } return true; } void CredentialStore::deleteAllPasswords() { FCT_IDENTIFICATION; const QList list = CredentialRegistry::instance().allDescriptors(); for (const CredentialDescriptor &desc : list) { QString user = desc.usernameFn(); if ( !user.isEmpty() ) deletePassword(desc.storageKey, user); } } void CredentialStore::saveImportPassphrase(const QString &passphrase) { FCT_IDENTIFICATION; // special one-time password when QLog import external DB savePassword(QStringLiteral("ImportPassphrase"), QStringLiteral("import"), passphrase); } QString CredentialStore::getImportPassphrase() { FCT_IDENTIFICATION; // special one-time password when QLog import external DB return getPassword(QStringLiteral("ImportPassphrase"), QStringLiteral("import")); } void CredentialStore::deleteImportPassphrase() { FCT_IDENTIFICATION; // special one-time password when QLog import external DB deletePassword(QStringLiteral("ImportPassphrase"), QStringLiteral("import")); } ================================================ FILE: core/CredentialStore.h ================================================ #ifndef QLOG_CORE_CREDENTIALSTORE_H #define QLOG_CORE_CREDENTIALSTORE_H #include #include // - Central registry of all services that use the CredentialStore. // - Each service registers its credential descriptor statically at load time // using the DECLARE_SECURE_SERVICE macro. // why: // - QtKeychain does not allow listing all stored passwords. // - To migrate data or validate access, we must know which services exist. // - This registry provides that authoritative list. struct CredentialDescriptor { QString storageKey; // unique key (e.g. "eQSL", "HRDLog") std::function usernameFn; // callback returning the username }; class CredentialRegistry { private: QList()>> callbacks; public: static CredentialRegistry &instance(); // Register a service descriptor callback. // Each service provides a lambda returning one or more CredentialDescriptors. void add(const QString &serviceName, const std::function()> &fn); // Returns all known credential descriptors (collected from all services). QList allDescriptors() const; }; // Macro used in each service to force the presence of registerCredentials() // and to auto-execute it during static initialization. // If the developer forgets to implement registerCredentials(), // linking will fail with "undefined reference". #define DECLARE_SECURE_SERVICE(cls) \ static void registerCredentials(); \ static int cls##ForceRegistration() { \ cls::registerCredentials(); \ return 0; \ } \ static int cls##RegistrationDummy; #define REGISTRATION_SECURE_SERVICE(cls) \ int cls::cls##RegistrationDummy = cls::cls##ForceRegistration() template class SecureServiceBase; // forward declaration // - Provides a single point of access to platform credential storage (QtKeychain). // - Ensures that only registered services can access credentials. // - Protected API: only subclasses (service classes) can call get/save/delete. // - Validation layer: prevents ad-hoc access from unregistered services. class CredentialStore : public QObject { Q_OBJECT template friend class SecureServiceBase; public: static CredentialStore* instance() { static CredentialStore instance; return &instance; }; bool exportPasswords(const QString &passphrase); bool importPasswords(const QString &passphrase); void deleteAllPasswords(); // Methods for storing import passphrase in SecureStore during application restart void saveImportPassphrase(const QString &passphrase); QString getImportPassphrase(); void deleteImportPassphrase(); private: explicit CredentialStore(QObject *parent = nullptr); // Protected methods — accessible only from derived service classes. // Direct use elsewhere is discouraged by design. int savePassword(const QString &storage_key, const QString &user, const QString &pass); QString getPassword(const QString &storage_key, const QString &user); void deletePassword(const QString &storage_key, const QString &user); private: // Verifies that the given service (storage_key) is registered. // Prevents code from querying or saving passwords. //bool isRegisteredService(const QString &storage_key) const; }; // - Base class for all components that manage user credentials. // - Enforces per-service registration and provides a safe API wrapper // for accessing the CredentialStore. // - Prevents ad-hoc direct usage of CredentialStore. // template class SecureServiceBase { public: virtual ~SecureServiceBase() {}; protected: static QString getPassword(const QString &storageKey, const QString &user) { return CredentialStore::instance()->getPassword(storageKey, user); } static void savePassword(const QString &storageKey, const QString &user, const QString &pass) { CredentialStore::instance()->savePassword(storageKey, user, pass); } static void deletePassword(const QString &storageKey, const QString &user) { CredentialStore::instance()->deletePassword(storageKey, user); } SecureServiceBase() { // --- Compile-time enforcement of registerCredentials() existence --- typedef void (*RegFn)(); static_assert( std::is_same::value, "Derived class must implement: DECLARE_SECURE_SERVICE macro;" ); } }; #endif // QLOG_CORE_CREDENTIALSTORE_H ================================================ FILE: core/EmergencyFrequency.cpp ================================================ #include "EmergencyFrequency.h" #include "core/debug.h" MODULE_IDENTIFICATION("qlog.core.emergencyfrequency"); double EmergencyFrequency::TOLERANCE_MHZ = 0.001; const QList &EmergencyFrequency::list() { FCT_IDENTIFICATION; static const QList freqs = { { 3.760, "LSB"}, { 7.110, "LSB"}, { 14.300, "USB"}, { 18.160, "USB"}, { 21.360, "USB"}, { 145.550, "FM"}, { 433.550, "FM"}, }; return freqs; } const EmergencyFreqEntry *EmergencyFrequency::inBand(double startMHz, double endMHz) { FCT_IDENTIFICATION; for ( const EmergencyFreqEntry &entry : list() ) if ( entry.frequency >= startMHz && entry.frequency <= endMHz ) return &entry; return nullptr; } const EmergencyFreqEntry *EmergencyFrequency::findEmergency(double freqMHz) { FCT_IDENTIFICATION; const QList &freqs = EmergencyFrequency::list(); for ( const EmergencyFreqEntry &entry : freqs ) if ( qAbs(freqMHz - entry.frequency) <= EmergencyFrequency::TOLERANCE_MHZ ) return &entry; return nullptr; } ================================================ FILE: core/EmergencyFrequency.h ================================================ #ifndef QLOG_CORE_EMERGENCYFREQUENCY_H #define QLOG_CORE_EMERGENCYFREQUENCY_H #include struct EmergencyFreqEntry { double frequency; // MHz QString mode; }; class EmergencyFrequency { public: static double TOLERANCE_MHZ; static const QList &list(); static const EmergencyFreqEntry *inBand(double startMHz, double endMHz); static const EmergencyFreqEntry *findEmergency(double freqMHz); }; #endif // QLOG_CORE_EMERGENCYFREQUENCY_H ================================================ FILE: core/FileCompressor.cpp ================================================ #include #include #include #include #include #include "FileCompressor.h" #include "core/debug.h" MODULE_IDENTIFICATION("qlog.core.filecompressor"); QByteArray FileCompressor::gzip(const QByteArray &in) { FCT_IDENTIFICATION; if ( in.isEmpty() ) return {}; z_stream strm{}; // 16 + MAX_WBITS = gzip format if ( deflateInit2(&strm, Z_BEST_SPEED, Z_DEFLATED, 16 + MAX_WBITS, 8, Z_DEFAULT_STRATEGY) != Z_OK ) return {}; strm.next_in = reinterpret_cast(const_cast(in.data())); strm.avail_in = static_cast(in.size()); QByteArray out; char buf[8192]; int ret; do { strm.next_out = reinterpret_cast(buf); strm.avail_out = sizeof(buf); ret = deflate(&strm, Z_FINISH); if ( ret == Z_OK || ret == Z_STREAM_END ) out.append(buf, static_cast(sizeof(buf) - strm.avail_out)); } while ( ret == Z_OK ); deflateEnd(&strm); return (ret == Z_STREAM_END) ? out : QByteArray{}; } QByteArray FileCompressor::gunzip(const QByteArray &in) { FCT_IDENTIFICATION; if ( in.isEmpty() ) return {}; z_stream strm{}; strm.next_in = reinterpret_cast(const_cast(in.data())); strm.avail_in = static_cast(in.size()); // 16 + MAX_WBITS tells zlib to parse gzip header/footer if ( inflateInit2(&strm, 16 + MAX_WBITS) != Z_OK ) return {}; QByteArray out; char buf[8192]; int ret = Z_OK; while ( ret == Z_OK ) { strm.next_out = reinterpret_cast(buf); strm.avail_out = sizeof(buf); ret = inflate(&strm, Z_NO_FLUSH); if ( ret == Z_OK || ret == Z_STREAM_END ) out.append(buf, static_cast(sizeof(buf) - strm.avail_out)); } inflateEnd(&strm); return out; } bool FileCompressor::gzipFile(const QString &sourceFile, const QString &destFile, const ProgressCallback &progress) { FCT_IDENTIFICATION; qCDebug(function_parameters) << sourceFile << "->" << destFile; QFile source(sourceFile); if ( !source.open(QIODevice::ReadOnly) ) { qWarning() << "Cannot open source file:" << sourceFile; return false; } QFile dest(destFile); if ( !dest.open(QIODevice::WriteOnly) ) { qWarning() << "Cannot create dest file:" << destFile; return false; } const qint64 totalSize = source.size(); // Initialize zlib for gzip compression z_stream strm{}; // 16 + MAX_WBITS = gzip format, if ( deflateInit2(&strm, Z_BEST_SPEED, Z_DEFLATED, 16 + MAX_WBITS, 8, Z_DEFAULT_STRATEGY) != Z_OK ) { qWarning() << "deflateInit2 failed"; return false; } char inBuffer[65536]; char outBuffer[65536]; qint64 bytesProcessed = 0; bool success = true; int ret; while ( success ) { qint64 bytesRead = source.read(inBuffer, sizeof(inBuffer)); if ( bytesRead < 0 ) { qWarning() << "Read error"; success = false; break; } strm.next_in = reinterpret_cast(inBuffer); strm.avail_in = static_cast(bytesRead); int flush = source.atEnd() ? Z_FINISH : Z_NO_FLUSH; do { strm.next_out = reinterpret_cast(outBuffer); strm.avail_out = sizeof(outBuffer); ret = deflate(&strm, flush); if ( ret == Z_STREAM_ERROR ) { qWarning() << "deflate error"; success = false; break; } qint64 have = sizeof(outBuffer) - strm.avail_out; if ( dest.write(outBuffer, have) != have ) { qWarning() << "Write error"; success = false; break; } } while ( strm.avail_out == 0 && success ); bytesProcessed += bytesRead; if ( progress && !progress(bytesProcessed, totalSize) ) { qCDebug(runtime) << "Compression cancelled by user"; success = false; break; } if ( source.atEnd() ) break; } deflateEnd(&strm); if ( !success || ret != Z_STREAM_END ) { dest.close(); QFile::remove(destFile); return false; } qCDebug(runtime) << "File compressed successfully"; return true; } bool FileCompressor::gunzipFile(const QString &sourceFile, const QString &destFile, const ProgressCallback &progress) { FCT_IDENTIFICATION; qCDebug(function_parameters) << sourceFile << "->" << destFile; QFile source(sourceFile); if ( !source.open(QIODevice::ReadOnly) ) { qWarning() << "Cannot open source file:" << sourceFile; return false; } QFile dest(destFile); if ( !dest.open(QIODevice::WriteOnly) ) { qWarning() << "Cannot create dest file:" << destFile; return false; } const qint64 totalSize = source.size(); // Initialize zlib for gzip decompression z_stream strm{}; // 16 + MAX_WBITS tells zlib to parse gzip header/footer if ( inflateInit2(&strm, 16 + MAX_WBITS) != Z_OK ) { qWarning() << "inflateInit2 failed"; return false; } char inBuffer[65536]; char outBuffer[65536]; qint64 bytesProcessed = 0; bool success = true; int ret = Z_OK; while ( ret != Z_STREAM_END && success ) { qint64 bytesRead = source.read(inBuffer, sizeof(inBuffer)); if ( bytesRead < 0 ) { qWarning() << "Read error"; success = false; break; } if ( bytesRead == 0 ) break; strm.next_in = reinterpret_cast(inBuffer); strm.avail_in = static_cast(bytesRead); do { strm.next_out = reinterpret_cast(outBuffer); strm.avail_out = sizeof(outBuffer); ret = inflate(&strm, Z_NO_FLUSH); if ( ret == Z_STREAM_ERROR || ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR ) { qWarning() << "inflate error:" << ret; success = false; break; } qint64 have = sizeof(outBuffer) - strm.avail_out; if ( dest.write(outBuffer, have) != have ) { qWarning() << "Write error"; success = false; break; } } while ( strm.avail_out == 0 && success ); bytesProcessed += bytesRead; if ( progress && !progress(bytesProcessed, totalSize) ) { qCDebug(runtime) << "Decompression cancelled by user"; success = false; break; } } inflateEnd(&strm); if ( !success ) { dest.close(); QFile::remove(destFile); return false; } qCDebug(runtime) << "File decompressed successfully"; return true; } bool FileCompressor::gzipFileWithProgress(const QString &sourceFile, const QString &destFile, QWidget *parent, const QString &title) { FCT_IDENTIFICATION; QProgressDialog progressDialog(title, QString(), 0, 100, parent); progressDialog.setWindowModality(Qt::WindowModal); progressDialog.setMinimumDuration(500); // Show after 500ms progressDialog.setValue(0); FileCompressor::ProgressCallback progressCallback = [&progressDialog](qint64 processed, qint64 total) { if ( total > 0 ) progressDialog.setValue(static_cast(processed * 100 / total)); QCoreApplication::processEvents(); return !progressDialog.wasCanceled(); }; bool result = gzipFile(sourceFile, destFile, progressCallback); progressDialog.setValue(100); return result; } bool FileCompressor::gunzipFileWithProgress(const QString &sourceFile, const QString &destFile, QWidget *parent, const QString &title) { FCT_IDENTIFICATION; QProgressDialog progressDialog(title, QString(), 0, 100, parent); progressDialog.setWindowModality(Qt::WindowModal); progressDialog.setMinimumDuration(500); // Show after 500ms progressDialog.setValue(0); FileCompressor::ProgressCallback progressCallback = [&progressDialog](qint64 processed, qint64 total) { if ( total > 0 ) progressDialog.setValue(static_cast(processed * 100 / total)); QCoreApplication::processEvents(); return !progressDialog.wasCanceled(); }; bool result = gunzipFile(sourceFile, destFile, progressCallback); progressDialog.setValue(100); return result; } ================================================ FILE: core/FileCompressor.h ================================================ #ifndef QLOG_CORE_FILECOMPRESSOR_H #define QLOG_CORE_FILECOMPRESSOR_H #include #include #include class QWidget; class FileCompressor { public: // Progress callback: (bytesProcessed, totalBytes); return false to cancel using ProgressCallback = std::function; // Compress data using gzip (in-memory) static QByteArray gzip(const QByteArray &in); // Decompress gzip data (in-memory) static QByteArray gunzip(const QByteArray &in); // Compress file using gzip (streaming) static bool gzipFile(const QString &sourceFile, const QString &destFile, const ProgressCallback &progress = nullptr); // Decompress gzip file (streaming) static bool gunzipFile(const QString &sourceFile, const QString &destFile, const ProgressCallback &progress = nullptr); // File compression/decompression with progress dialog static bool gzipFileWithProgress(const QString &sourceFile, const QString &destFile, QWidget *parent, const QString &title); static bool gunzipFileWithProgress(const QString &sourceFile, const QString &destFile, QWidget *parent, const QString &title); }; #endif // QLOG_CORE_FILECOMPRESSOR_H ================================================ FILE: core/FldigiTCPServer.cpp ================================================ #include #include #include #include #include #include "FldigiTCPServer.h" #include "logformat/AdiFormat.h" #include "debug.h" MODULE_IDENTIFICATION("qlog.core.fldigi"); FldigiTCPServer::FldigiTCPServer(QObject *parent) : QTcpServer(parent) { FCT_IDENTIFICATION; listen(QHostAddress::Any, 8421); } void FldigiTCPServer::incomingConnection(qintptr socket) { FCT_IDENTIFICATION; QTcpSocket* sock = new QTcpSocket(this); connect(sock, &QTcpSocket::readyRead, this, &FldigiTCPServer::readClient); connect(sock, &QTcpSocket::disconnected, this, &FldigiTCPServer::discardClient); sock->setSocketDescriptor(socket); } void FldigiTCPServer::readClient() { FCT_IDENTIFICATION; QTcpSocket* socket = qobject_cast(sender()); if ( !socket ) { qCWarning(runtime) << "socket is null"; return; } QString data = QString(socket->readAll()); qCDebug(runtime) << data; int split = data.indexOf("\r\n\r\n", 0); data.remove(0, split+4); /* WORKAROUND - FLDIGI has probably an issue. It seems that XML * that is generated by FLDIGI contains Processing Instruction (clientid) * without mandatory space */ data.replace("close(); if (socket->state() == QTcpSocket::UnconnectedState) socket->deleteLater(); } void FldigiTCPServer::discardClient() { FCT_IDENTIFICATION; QTcpSocket* socket = qobject_cast(sender()); if ( socket ) socket->deleteLater(); } void FldigiTCPServer::processMethodCall(QTcpSocket* sock, QXmlStreamReader& xml) { FCT_IDENTIFICATION; QByteArray response; while (!xml.atEnd() && !xml.hasError()) { xml.readNextStartElement(); if (xml.name() == QString("methodCall")) { qCDebug(runtime) << "method call"; } if (xml.name() == QString("methodName")) { qCDebug(runtime) << "methodName"; QString method = xml.readElementText(); if (method == "log.add_record") { qCDebug(runtime) << "log.add_record"; QString param = parseParam(xml); if (!param.isEmpty()) { response = addRecord(param); } } else if (method == "system.listMethods") { qCDebug(runtime) << "system.listMethods"; response = listMethods(); } } } qCDebug(runtime) << response; QTextStream out(sock); if (!response.isEmpty()) { out << "HTTP/1.1 200 OK\r\n"; out << "Content-Type: text/xml; charset=utf-8\r\n"; out << "Content-Length: " << response.length() << "\r\n"; out << "\r\n"; out << response; } else { out << "HTTP/1.1 400 Internal Error\r\n"; } } QString FldigiTCPServer::parseParam(QXmlStreamReader& xml) { FCT_IDENTIFICATION; while (!xml.atEnd() && !xml.hasError()) { xml.readNextStartElement(); if (xml.name() == QString("value")) { return xml.readElementText(); } } return QString(); } QByteArray FldigiTCPServer::listMethods() { FCT_IDENTIFICATION; QByteArray out; QXmlStreamWriter xml(&out); xml.writeStartDocument(); xml.writeStartElement("methodResponse"); xml.writeStartElement("params"); xml.writeStartElement("param"); xml.writeStartElement("value"); xml.writeStartElement("array"); xml.writeStartElement("data"); xml.writeTextElement("value", "log.add_record"); xml.writeTextElement("value", "system.listMethods"); xml.writeEndDocument(); return out; } QByteArray FldigiTCPServer::addRecord(QString data) { FCT_IDENTIFICATION; qCDebug(function_parameters) << data; QByteArray out; QXmlStreamWriter xml(&out); xml.writeStartDocument(); xml.writeStartElement("methodResponse"); xml.writeStartElement("params"); xml.writeStartElement("param"); xml.writeStartElement("value"); xml.writeEndDocument(); QSqlTableModel model; model.setTable("contacts"); model.removeColumn(model.fieldIndex("id")); QSqlRecord record = model.record(0); QTextStream in(&data); AdiFormat adif(in); adif.importNext(record); emit addContact(record); return out; } ================================================ FILE: core/FldigiTCPServer.h ================================================ #ifndef QLOG_CORE_FLDIGI_H #define QLOG_CORE_FLDIGI_H #include #include class QXmlStreamReader; class FldigiTCPServer : public QTcpServer { Q_OBJECT public: explicit FldigiTCPServer(QObject *parent = nullptr); signals: void addContact(QSqlRecord); public slots: void readClient(); void discardClient(); private: void processMethodCall(QTcpSocket* sock, QXmlStreamReader& xml); QString parseParam(QXmlStreamReader& xml); QByteArray listMethods(); QByteArray addRecord(QString data); void incomingConnection(qintptr socket) override; }; #endif // QLOG_CORE_FLDIGI_H ================================================ FILE: core/LOVDownloader.cpp ================================================ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "LogParam.h" #include "LOVDownloader.h" #include "FileCompressor.h" #include "debug.h" #include "data/Data.h" #include "core/csv.hpp" #include "core/FileCompressor.h" MODULE_IDENTIFICATION("qlog.core.lovdownloader"); LOVDownloader::LOVDownloader(QObject *parent) : QObject(parent), currentReply(nullptr), abortRequested(false), CTYPrefixSeperatorRe("[\\s;]"), CTYPrefixFormatRe("(=?)([A-Z0-9/]+)(?:\\((\\d+)\\))?(?:\\[(\\d+)\\])?$") { FCT_IDENTIFICATION; nam = new QNetworkAccessManager(this); connect(nam, &QNetworkAccessManager::finished, this, &LOVDownloader::processReply); } LOVDownloader::~LOVDownloader() { FCT_IDENTIFICATION; } void LOVDownloader::update(const SourceType & sourceType, bool force) { FCT_IDENTIFICATION; abortRequested = false; const SourceDefinition &sourceDef = sourceMapping[sourceType]; Q_ASSERT(sourceDef.type == sourceType); QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)); const QDate &last_update = LogParam::getLOVaParam(sourceDef.lastTimeConfigName); if ( !force && dir.exists(sourceDef.fileName) && last_update.isValid() && last_update.daysTo(QDate::currentDate()) < sourceDef.ageTime ) { if ( isTableFilled(sourceDef.tableName) ) { // nothing to do. qCDebug(runtime) << "Not needed to update " << sourceDef.fileName; emit noUpdate(); return; } qCDebug(runtime) << "using cached " << sourceDef.fileName << " at" << dir.path(); QTimer::singleShot(0, this, [this, sourceDef]() {loadData(sourceDef);}); } else { qCDebug(runtime) << sourceDef.fileName << " is too old or not exist - downloading"; download(sourceDef); } } void LOVDownloader::abortRequest() { FCT_IDENTIFICATION; if ( currentReply ) { currentReply->abort(); currentReply = nullptr; } abortRequested = true; } void LOVDownloader::loadData(const LOVDownloader::SourceDefinition &sourceDef) { FCT_IDENTIFICATION; const QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)); QFile file(dir.filePath(sourceDef.fileName)); if ( ! file.open(QIODevice::ReadOnly) ) { qWarning() << "Cannot open" << dir.filePath(sourceDef.fileName); return; } QByteArray data = file.readAll(); file.close(); if (sourceDef.fileName.endsWith(".gz", Qt::CaseInsensitive)) data = FileCompressor::gunzip(data); emit processingSize(data.size()); QTextStream stream(data); parseData(sourceDef, stream); emit finished(true); } bool LOVDownloader::isTableFilled(const QString &tableName) { FCT_IDENTIFICATION; qCDebug(function_parameters) << tableName; QSqlQuery query(QString("select exists( select 1 from %1)").arg(tableName)); int i = query.first() ? query.value(0).toInt() : 0; qCDebug(runtime) << i; return i==1; } bool LOVDownloader::deleteTable(const QString &tableName) { FCT_IDENTIFICATION; qCDebug(function_parameters) << tableName; QSqlQuery query; QString queryStatement("delete from %1"); if ( ! query.exec(queryStatement.arg(tableName)) ) { qWarning() << "Cannot delete " << tableName << query.lastError(); return false; } return true; } void LOVDownloader::download(const LOVDownloader::SourceDefinition &sourceDef) { FCT_IDENTIFICATION; QUrl url(sourceDef.URL); QNetworkRequest request(url); QString rheader = QString("QLog/%1").arg(VERSION); request.setRawHeader("User-Agent", rheader.toUtf8()); if ( currentReply ) { qCWarning(runtime) << "processing a new request but the previous one hasn't been completed yet !!!"; } currentReply = nam->get(request); currentReply->setProperty("sourceType", sourceDef.type); qCDebug(runtime) << "Downloading " << sourceDef.fileName << "from " << url.toString(); } void LOVDownloader::parseData(const SourceDefinition &sourceDef, QTextStream &data) { FCT_IDENTIFICATION; qCDebug(runtime) << "Parsing file " << sourceDef.fileName; switch ( sourceDef.type ) { case CTY: parseCTY(sourceDef, data); break; case SATLIST: parseSATLIST(sourceDef, data); break; case SOTASUMMITS: parseSOTASummits(sourceDef, data); break; case WWFFDIRECTORY: parseWWFFDirectory(sourceDef, data); break; case IOTALIST: parseIOTA(sourceDef, data); break; case POTADIRECTORY: parsePOTA(sourceDef, data); break; case MEMBERSHIPCONTENTLIST: parseMembershipContent(sourceDef, data); break; case CLUBLOGCTY: parseClubLogCTY(sourceDef, data); break; default: qWarning() << "Unssorted type to download" << sourceDef.type << sourceDef.fileName; } } void LOVDownloader::parseCTY(const SourceDefinition &sourceDef, QTextStream &data) { FCT_IDENTIFICATION; QRegularExpressionMatch matchExp; QSqlDatabase::database().transaction(); if ( ! deleteTable("dxcc_prefixes_ad1c") ) { qCWarning(runtime) << "dxcc_prefixes_ad1c delete failed - rollback"; QSqlDatabase::database().rollback(); return; } if ( ! deleteTable(sourceDef.tableName) ) { qCWarning(runtime) << sourceDef.tableName << " delete failed - rollback"; QSqlDatabase::database().rollback(); return; } QSqlQuery insertEntityQuery; if ( ! insertEntityQuery.prepare("INSERT INTO dxcc_entities_ad1c (id," " name," " prefix," " cont," " cqz," " ituz," " lat," " lon," " tz) " " VALUES ( :id," " :name," " :prefix," " :cont," " :cqz," " :ituz," " :lat," " :lon," " :tz)") ) { qWarning() << "cannot prepare Insert statement - Entity"; abortRequested = true; } QSqlQuery insertPrefixesQuery; if ( ! insertPrefixesQuery.prepare("INSERT INTO dxcc_prefixes_ad1c (" " prefix," " exact," " dxcc," " cqz," " ituz) " " VALUES ( :prefix," " :exact," " :dxcc," " :cqz," " :ituz)") ) { qWarning() << "cannot prepare Insert statement - Prefixes"; abortRequested = true; } unsigned int count = 0; while ( !data.atEnd() && !abortRequested ) { const QString &line = data.readLine(); const QStringList &fields = line.split(','); if ( fields.count() != 10 ) { qCDebug(runtime) << "Invalid line in the input file " << line; continue; } else if ( fields.at(0).startsWith("*") ) continue; qCDebug(runtime) << fields; int dxcc_id = fields.at(2).toInt(); insertEntityQuery.bindValue(":id", dxcc_id); insertEntityQuery.bindValue(":prefix", fields.at(0)); insertEntityQuery.bindValue(":name", fields.at(1)); insertEntityQuery.bindValue(":cont", fields.at(3)); insertEntityQuery.bindValue(":cqz", fields.at(4)); insertEntityQuery.bindValue(":ituz", fields.at(5)); insertEntityQuery.bindValue(":lat", fields.at(6).toFloat()); insertEntityQuery.bindValue(":lon", -fields.at(7).toFloat()); insertEntityQuery.bindValue(":tz", fields.at(8).toFloat()); if ( ! insertEntityQuery.exec() ) { qWarning() << "DXCC Entity insert error " << insertEntityQuery.lastError().text() << insertEntityQuery.lastQuery(); qCDebug(runtime) << fields; abortRequested = true; continue; } else count++; #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) const QStringList &prefixList = fields.at(9).split(CTYPrefixSeperatorRe, Qt::SkipEmptyParts); #else /* Due to ubuntu 20.04 where qt5.12 is present */ const QStringList &prefixList = fields.at(9).split(CTYPrefixSeperatorRe, QString::SkipEmptyParts); #endif qCDebug(runtime) << prefixList; QStringList dup; for ( auto &prefix : prefixList ) { matchExp = CTYPrefixFormatRe.match(prefix); if ( matchExp.hasMatch() ) { // removing duplicities in CTY file. const QString &pfx = matchExp.captured(2); if ( !dup.contains(pfx) ) { dup << pfx; insertPrefixesQuery.bindValue(":prefix", pfx); insertPrefixesQuery.bindValue(":exact", !matchExp.captured(1).isEmpty()); insertPrefixesQuery.bindValue(":dxcc", dxcc_id); insertPrefixesQuery.bindValue(":cqz", matchExp.captured(3).toInt()); insertPrefixesQuery.bindValue(":ituz", matchExp.captured(4).toInt()); if ( ! insertPrefixesQuery.exec() ) { qWarning() << "DXCC Prefix insert error " << insertPrefixesQuery.lastError().text() << insertPrefixesQuery.lastQuery(); qCDebug(runtime) << prefix << prefixList; abortRequested = true; } } else qCDebug(runtime) << "Removing non-unique prefix" << pfx; } else qCDebug(runtime) << "Failed to match " << prefix; } if ( count% 20 == 0 ) { emit progress(data.pos()); QCoreApplication::processEvents(); } emit progress(data.pos()); QCoreApplication::processEvents(); } if ( !abortRequested ) { qCDebug(runtime) << "DXCC update finished:" << count << "entities loaded."; QSqlDatabase::database().commit(); } else { //can be a result of abort qCWarning(runtime) << "DXCC update failed - rollback"; QSqlDatabase::database().rollback(); } } void LOVDownloader::parseSATLIST(const SourceDefinition &sourceDef, QTextStream &data) { FCT_IDENTIFICATION; QSqlDatabase::database().transaction(); if ( ! deleteTable(sourceDef.tableName) ) { qCWarning(runtime) << "Satlist delete failed - rollback"; QSqlDatabase::database().rollback(); return; } QSqlTableModel entityTableModel; entityTableModel.setTable(sourceDef.tableName); entityTableModel.setEditStrategy(QSqlTableModel::OnManualSubmit); QSqlRecord entityRecord = entityTableModel.record(); int count = 0; while ( !data.atEnd() && !abortRequested ) { QString line = data.readLine(); QStringList fields = line.split(';'); if ( fields.count() != 8 ) { qCDebug(runtime) << "Invalid line in the input file " << line; continue; } qCDebug(runtime) << fields; entityRecord.clearValues(); entityRecord.setValue("name", fields.at(0)); entityRecord.setValue("number", fields.at(1)); entityRecord.setValue("uplink", fields.at(2)); entityRecord.setValue("downlink", fields.at(3)); entityRecord.setValue("beacon", fields.at(4)); entityRecord.setValue("mode", fields.at(5)); entityRecord.setValue("callsign", fields.at(6)); entityRecord.setValue("status", fields.at(7)); if ( !entityTableModel.insertRecord(-1, entityRecord) ) { qWarning() << "Cannot insert a record to SATList Table - " << entityTableModel.lastError(); qCDebug(runtime) << entityRecord; } else { count++; } emit progress(data.pos()); QCoreApplication::processEvents(); } if ( entityTableModel.submitAll() && !abortRequested ) { QSqlDatabase::database().commit(); qCDebug(runtime) << "Satlist update finished:" << count << "entities loaded."; } else { //can be a result of abort qCWarning(runtime) << "Satlist update failed - rollback" << entityTableModel.lastError(); QSqlDatabase::database().rollback(); } } bool LOVDownloader::parseCSVGeneric(const SourceDefinition &sourceDef, QTextStream &data, const QString &insertSQL, const QStringList &csvColumns, csv::CSVFormat format, const QString &preValidateContains) { FCT_IDENTIFICATION; const std::string csvData = data.readAll().toStdString(); const qint64 totalBytes = static_cast(csvData.size()); const int totalRows = qMax(1, static_cast( std::count(csvData.begin(), csvData.end(), '\n'))); if ( !preValidateContains.isEmpty() && csvData.find(preValidateContains.toStdString()) == std::string::npos ) { qWarning() << "Unexpected file header for" << sourceDef.tableName; return false; } QSqlDatabase::database().transaction(); if ( !deleteTable(sourceDef.tableName) ) { qCWarning(runtime) << "Delete failed - rollback:" << sourceDef.tableName; QSqlDatabase::database().rollback(); return false; } QSqlQuery insertQuery; if ( !insertQuery.prepare(insertSQL) ) { qWarning() << "Cannot prepare insert statement for" << sourceDef.tableName; QSqlDatabase::database().rollback(); return false; } csv::CSVReader reader = csv::parse(csvData, format); const std::vector colNames = reader.get_col_names(); for ( const QString &col : csvColumns ) { if ( std::find(colNames.begin(), colNames.end(), col.toStdString()) == colNames.end() ) { qWarning() << "Missing column:" << col << "in" << sourceDef.tableName; QSqlDatabase::database().rollback(); return false; } } const int CHUNK = 5000; const int colCount = csvColumns.size(); std::vector stdCols; stdCols.reserve(colCount); for ( const QString &col : csvColumns ) stdCols.push_back(col.toStdString()); QVector columns(colCount); auto reserveAll = [&]() { for ( auto &col : columns ) col.reserve(CHUNK); }; auto flushChunk = [&]() -> bool { for ( const QVariantList &col : columns ) insertQuery.addBindValue(col); if ( !insertQuery.execBatch() ) { qWarning() << "Insert error for" << sourceDef.tableName << ":" << insertQuery.lastError().text(); return false; } for ( QVariantList &col : columns ) col.clear(); reserveAll(); return true; }; reserveAll(); int count = 0; for ( csv::CSVRow &row : reader ) { if ( abortRequested ) break; for ( int i = 0; i < colCount; ++i ) columns[i] << QString::fromStdString(row[stdCols[i]].get()); ++count; if ( count % CHUNK == 0 ) { if ( !flushChunk() ) { abortRequested = true; break; } emit progress(static_cast(count) * totalBytes / totalRows); QCoreApplication::processEvents(); } } if ( !abortRequested && !columns[0].isEmpty() ) { if ( !flushChunk() ) abortRequested = true; } if ( !abortRequested ) { QSqlDatabase::database().commit(); qCDebug(runtime) << sourceDef.tableName << "update finished:" << count << "entities loaded."; return true; } qCWarning(runtime) << sourceDef.tableName << "update failed - rollback"; QSqlDatabase::database().rollback(); return false; } void LOVDownloader::parseSOTASummits(const SourceDefinition &sourceDef, QTextStream &data) { FCT_IDENTIFICATION; csv::CSVFormat format; format.delimiter(',').quote('"').header_row(1); parseCSVGeneric(sourceDef, data, "INSERT INTO sota_summits(summit_code, association_name, region_name, summit_name," " altm, altft, gridref1, gridref2, longitude, latitude, points, bonus_points," " valid_from, valid_to) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)", {"SummitCode", "AssociationName", "RegionName", "SummitName", "AltM", "AltFt", "GridRef1", "GridRef2", "Longitude", "Latitude", "Points", "BonusPoints", "ValidFrom", "ValidTo"}, format, "SOTA Summits List"); // preValidateContains } void LOVDownloader::parseWWFFDirectory(const SourceDefinition &sourceDef, QTextStream &data) { FCT_IDENTIFICATION; csv::CSVFormat format; format.delimiter(',').quote('"').trim({' '}); parseCSVGeneric(sourceDef, data, "INSERT INTO wwff_directory(reference, status, name, program, dxcc, state," " county, continent, iota, iaruLocator, latitude, longitude, iucncat," " valid_from, valid_to) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", {"reference", "status", "name", "program", "dxcc", "state", "county", "continent", "iota", "iaruLocator", "latitude", "longitude", "IUCNcat", "validFrom", "validTo"}, format); } void LOVDownloader::parseIOTA(const SourceDefinition &sourceDef, QTextStream &data) { FCT_IDENTIFICATION; QSqlQuery insertQuery; if ( ! insertQuery.prepare("INSERT INTO IOTA(iotaid," " islandname)" " VALUES (?, ?)") ) { qWarning() << "cannot prepare Insert statement"; abortRequested = true; return; } QSqlDatabase::database().transaction(); if ( ! deleteTable(sourceDef.tableName) ) { qCWarning(runtime) << "IOTA List delete failed - rollback"; abortRequested = true; QSqlDatabase::database().rollback(); return; } unsigned int count = 0; QJsonDocument jsonDoc = QJsonDocument::fromJson(data.readAll().toUtf8()); if ( !jsonDoc.isArray() ) { qCDebug(runtime) << jsonDoc; qWarning() << "Unexpected IOTA JSON - aborting"; abortRequested = true; } else { QVariantList iota_id; QVariantList iota_islandname; const QJsonArray &jsonArray = jsonDoc.array(); for ( const QJsonValue &value : jsonArray ) { if ( !value.isObject() ) continue; const QJsonObject &obj = value.toObject(); const QJsonValue &refno = obj.value("refno"); const QJsonValue &name = obj.value("name"); qCDebug(runtime) << "IOTA Record" << refno << name; iota_id << refno; iota_islandname << name; if ( count%500 == 0 ) { emit progress(data.pos()); QCoreApplication::processEvents(); } count++; } insertQuery.addBindValue(iota_id); insertQuery.addBindValue(iota_islandname); if ( ! insertQuery.execBatch() ) { qInfo() << "IOTA Directory insert error " << insertQuery.lastError().text() << insertQuery.lastQuery(); abortRequested = true; } } if ( !abortRequested ) { QSqlDatabase::database().commit(); qCDebug(runtime) << "IOTA update finished:" << count << "entities loaded."; } else { qCWarning(runtime) << "IOTA update failed - rollback"; QSqlDatabase::database().rollback(); } } void LOVDownloader::parsePOTA(const SourceDefinition &sourceDef, QTextStream &data) { FCT_IDENTIFICATION; csv::CSVFormat format; format.delimiter(',').quote('"'); parseCSVGeneric(sourceDef, data, "INSERT INTO POTA_DIRECTORY(reference, name, active, entityID," " locationDesc, latitude, longitude, grid) VALUES (?,?,?,?,?,?,?,?)", {"reference", "name", "active", "entityId", "locationDesc", "latitude", "longitude", "grid"}, format); } void LOVDownloader::parseMembershipContent(const SourceDefinition &sourceDef, QTextStream &data) { FCT_IDENTIFICATION; QSqlDatabase::database().transaction(); if ( ! deleteTable(sourceDef.tableName) ) { qCWarning(runtime) << "Membership Directory delete failed - rollback"; QSqlDatabase::database().rollback(); return; } QSqlTableModel entityTableModel; entityTableModel.setTable(sourceDef.tableName); entityTableModel.setEditStrategy(QSqlTableModel::OnManualSubmit); QSqlRecord entityRecord = entityTableModel.record(); int count = 0; while ( !data.atEnd() && !abortRequested ) { QString line = data.readLine(); QStringList fields = line.split(','); if ( fields.count() != 5 ) { qCDebug(runtime) << "Invalid line in the input file " << line; continue; } qCDebug(runtime) << fields; entityRecord.clearValues(); entityRecord.setValue("short_desc", fields.at(0)); entityRecord.setValue("long_desc", fields.at(1)); entityRecord.setValue("filename", fields.at(2)); entityRecord.setValue("last_update", fields.at(3)); entityRecord.setValue("num_records", fields.at(4)); if ( !entityTableModel.insertRecord(-1, entityRecord) ) { qWarning() << "Cannot insert a record to Membership Directory Table - " << entityTableModel.lastError(); qCDebug(runtime) << entityRecord; } else { count++; } emit progress(data.pos()); QCoreApplication::processEvents(); } if ( entityTableModel.submitAll() && !abortRequested ) { QSqlDatabase::database().commit(); qCDebug(runtime) << "Membership Directory update finished:" << count << "entities loaded."; } else { //can be a result of abort qCWarning(runtime) << "Membership Directory update failed - rollback" << entityTableModel.lastError(); QSqlDatabase::database().rollback(); } } void LOVDownloader::parseClubLogCTY(const SourceDefinition &sourceDef, QTextStream &data) { FCT_IDENTIFICATION; if (sourceDef.type != CLUBLOGCTY) return; // Read whole text (it’s XML); QXmlStreamReader can also take QIODevice, but we // already have a QTextStream here. QXmlStreamReader xml(data.readAll()); // Clean all five tables inside one transaction QSqlDatabase::database().transaction(); auto rollback = [&]() { qCWarning(runtime) << "ClubLog CTY import failed - rollback"; QSqlDatabase::database().rollback(); }; if ( !deleteTable("dxcc_zone_exceptions_clublog") || !deleteTable("dxcc_prefixes_clublog") || !deleteTable("dxcc_entities_clublog")) { rollback(); return; } QSqlQuery insEntity, insPrefix,insZone; // prepared statements if (!insEntity.prepare( "INSERT INTO dxcc_entities_clublog(id, name, prefix, deleted, cqz, ituz, cont, lon, lat, start, \"end\")" "VALUES(:id, :name, :prefix, :deleted, :cqz, :ituz, :cont, :lon, :lat, :start, :end)")) { qWarning() << insEntity.lastError(); rollback(); return; } if (!insPrefix.prepare( "INSERT INTO dxcc_prefixes_clublog(prefix, exact, dxcc, cqz, cont, lon, lat, start, \"end\")" "VALUES(:prefix, :exact, :dxcc, :cqz, :cont, :lon, :lat, :start, :end)")) { qWarning() << insPrefix.lastError(); rollback(); return; } if (!insZone.prepare( "INSERT INTO dxcc_zone_exceptions_clublog(record, call, cqz, start, \"end\")" "VALUES(:record, :call, :cqz, :start, :end)")) { qWarning() << insZone.lastError(); rollback(); return; } auto readText = [&](QXmlStreamReader &x)->QString { return x.readElementText().trimmed(); }; auto readDate = [&](const QString &s)->QString { return s; }; // store ISO8601 text as-is // Cursor down to while (!xml.atEnd() && !(xml.isStartElement() && xml.name() == QStringLiteral("clublog"))) xml.readNext(); if (xml.atEnd()) { qWarning() << "ClubLog: not found"; rollback(); return; } quint32 readOp = 0; auto updateReadProgress = [&]() { readOp++; if ( readOp % 200 == 0 ) { emit progress(xml.characterOffset()); QCoreApplication::processEvents(); } }; // Iterate children of while (!xml.atEnd()) { xml.readNext(); if ( !xml.isStartElement() ) continue; const QString &top = xml.name().toString(); if ( top == "entities" ) { // ...... while (!(xml.isEndElement() && xml.name() == QStringLiteral("entities"))) { xml.readNext(); updateReadProgress(); if ( xml.isStartElement() && xml.name() == QStringLiteral("entity") ) { // parse one entity int adif = 0; QString name, prefix, cont; bool deleted=false; int cqz = 0; QString start, end; double lon=0.0, lat=0.0; while ( !(xml.isEndElement() && xml.name() == QStringLiteral("entity"))) { xml.readNext(); updateReadProgress(); if ( !xml.isStartElement() ) continue; const QString &tag = xml.name().toString(); if ( tag=="adif" ) adif = readText(xml).toInt(); else if ( tag=="name" ) name = readText(xml); else if ( tag=="prefix" ) prefix = readText(xml); else if ( tag=="deleted" ) deleted = (readText(xml).compare("true", Qt::CaseInsensitive) == 0); else if ( tag=="cqz" ) cqz = readText(xml).toInt(); else if ( tag=="cont" ) cont = readText(xml); else if ( tag=="long" ) lon = readText(xml).toDouble(); else if ( tag=="lat" ) lat = readText(xml).toDouble(); else if ( tag=="start" ) start = readDate(readText(xml)); else if ( tag=="end" ) end = readDate(readText(xml)); else xml.skipCurrentElement(); } insEntity.bindValue(":id", adif); const QString &nameModified = Data::instance()->dxccName(adif); insEntity.bindValue(":name", nameModified.isEmpty() ? name : nameModified); insEntity.bindValue(":prefix", prefix); insEntity.bindValue(":deleted", deleted ? 1 : 0); insEntity.bindValue(":cqz", cqz ? cqz : 0); insEntity.bindValue(":ituz", Data::instance()->dxccITUZ(adif)); insEntity.bindValue(":cont", cont.isEmpty()? QVariant(): cont); insEntity.bindValue(":lon", lon); insEntity.bindValue(":lat", lat); insEntity.bindValue(":start", start.isEmpty()? QVariant() : start); insEntity.bindValue(":end", end.isEmpty()? QVariant() : end); if (!insEntity.exec()) { qWarning() << insEntity.lastError(); rollback(); return; } } } } else if (top == "prefixes" || top == "exceptions") { // these two share the same internal structure: ... bool isPrefix = (top=="prefixes"); while (!(xml.isEndElement() && xml.name() == top)) { xml.readNext(); updateReadProgress(); if ( xml.isStartElement() && (xml.name() == QStringLiteral("prefix") || xml.name()==QStringLiteral("exception"))) { QString call, cont, start, end; int adif=0, cqz=0; double lon=0.0, lat=0.0; while ( !(xml.isEndElement() && (xml.name()==QStringLiteral("prefix") || xml.name()==QStringLiteral("exception")))) { xml.readNext(); updateReadProgress(); if ( !xml.isStartElement() ) continue; const QString tag = xml.name().toString(); if ( tag=="call" ) call = readText(xml); else if ( tag=="adif" ) adif = readText(xml).toInt(); else if ( tag=="cqz" ) cqz = readText(xml).toInt(); else if ( tag=="cont" ) cont = readText(xml); else if ( tag=="long" ) lon = readText(xml).toDouble(); else if ( tag=="lat" ) lat = readText(xml).toDouble(); else if ( tag=="start" ) start = readDate(readText(xml)); else if ( tag=="end" ) end = readDate(readText(xml)); else xml.skipCurrentElement(); } insPrefix.bindValue(":prefix", call); insPrefix.bindValue(":exact", !isPrefix); insPrefix.bindValue(":dxcc", adif); insPrefix.bindValue(":cqz", cqz ? cqz : 0); insPrefix.bindValue(":ituz", 0); insPrefix.bindValue(":cont", cont.isEmpty()? QVariant() : cont); insPrefix.bindValue(":lon", lon); insPrefix.bindValue(":lat", lat); insPrefix.bindValue(":start", start.isEmpty()? QVariant() : start); insPrefix.bindValue(":end", end.isEmpty()? QVariant() : end); if (!insPrefix.exec()) { qWarning() << insPrefix.lastError(); rollback(); return; } } } } else if ( top == "zone_exceptions" ) { while ( !(xml.isEndElement() && xml.name() == QStringLiteral("zone_exceptions")) ) { xml.readNext(); updateReadProgress(); if ( xml.isStartElement() && xml.name()==QStringLiteral("zone_exception") ) { bool ok=false; quint32 rec = xml.attributes().value("record").toUInt(&ok); QString call, start, end; int zone=0; while ( !(xml.isEndElement() && xml.name()==QStringLiteral("zone_exception")) ) { xml.readNext(); updateReadProgress(); if (!xml.isStartElement()) continue; const QString &tag = xml.name().toString(); if ( tag=="call" ) call = readText(xml); else if ( tag=="zone" ) zone = readText(xml).toInt(); else if ( tag=="start" ) start = readDate(readText(xml)); else if ( tag=="end" ) end = readDate(readText(xml)); else xml.skipCurrentElement(); } insZone.bindValue(":record", rec); insZone.bindValue(":call", call); insZone.bindValue(":cqz", zone); insZone.bindValue(":start", start); insZone.bindValue(":end", end); if (!insZone.exec()) { qWarning() << insZone.lastError(); rollback(); return; } } } } else xml.skipCurrentElement(); updateReadProgress(); } if ( xml.hasError() ) { qWarning() << "ClubLog XML error:" << xml.errorString(); rollback(); return; } QSqlDatabase::database().commit(); qCDebug(runtime) << "ClubLog CTY import finished."; } void LOVDownloader::processReply(QNetworkReply *reply) { FCT_IDENTIFICATION; currentReply = nullptr; QByteArray data = reply->readAll(); uint sourceTypeNum = reply->property("sourceType").toUInt(); qCDebug(runtime) << "Received Source type " << sourceTypeNum; SourceType sourceType = static_cast(sourceTypeNum); SourceDefinition sourceDef = sourceMapping[sourceType]; Q_ASSERT(sourceDef.type == sourceType); int replyStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); if ( reply->isFinished() && reply->error() == QNetworkReply::NoError && replyStatusCode >= 200 && replyStatusCode < 300) { qCDebug(runtime) << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); qCDebug(runtime) << reply->header(QNetworkRequest::KnownHeaders::LocationHeader); QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)); QFile file(dir.filePath(sourceDef.fileName)); file.open(QIODevice::WriteOnly); if (sourceType == CLUBLOGCTY) { QByteArray maybeXml = FileCompressor::gunzip(data); if ( !maybeXml.isEmpty() ) data = maybeXml; } file.write(data); file.flush(); file.close(); reply->deleteLater(); LogParam::setLOVParam(sourceDef.lastTimeConfigName, QDateTime::currentDateTimeUtc().date()); loadData(sourceDef); } else { qCDebug(runtime) << "HTTP Status Code" << replyStatusCode; qCDebug(runtime) << "Failed to download " << sourceDef.fileName; reply->deleteLater(); emit finished(false); } } ================================================ FILE: core/LOVDownloader.h ================================================ #ifndef QLOG_CORE_LOVDOWNLOADER_H #define QLOG_CORE_LOVDOWNLOADER_H #include #include #include #include "service/clublog/ClubLog.h" namespace csv { class CSVFormat; } class LOVDownloader : public QObject { Q_OBJECT public: enum SourceType { CTY = 0, SATLIST = 1, SOTASUMMITS = 2, WWFFDIRECTORY = 3, IOTALIST = 4, POTADIRECTORY = 5, MEMBERSHIPCONTENTLIST = 6, CLUBLOGCTY = 7, UNDEF = 8 }; public: LOVDownloader(QObject *parent = nullptr); ~LOVDownloader(); void update(const SourceType &, bool force = false); public slots: void abortRequest(); signals: void processingSize(qint64); void progress(qint64 count); void finished(bool result); void noUpdate(); private: class SourceDefinition { public: SourceDefinition(const SourceType type, const QString &URL, const QString &fileName, const QString &lastTimeConfigName, const QString &tableName, int ageTime) : type(type), URL(URL), fileName(fileName), lastTimeConfigName(lastTimeConfigName), tableName(tableName), ageTime(ageTime) {}; SourceDefinition() : type(LOVDownloader::UNDEF), ageTime(0){}; SourceType type; QString URL; QString fileName; QString lastTimeConfigName; QString tableName; int ageTime; }; QMap sourceMapping = { {CTY, SourceDefinition(CTY, "https://www.country-files.com/bigcty/cty.csv", "cty.csv", "LOV/last_cty_update", "dxcc_entities_ad1c", 7)}, {CLUBLOGCTY, SourceDefinition(CLUBLOGCTY, ClubLogBase::getCTYUrl(), "clublog_cty.xml", "LOV/last_clublogcty_update", "dxcc_entities_clublog", 7)}, {SATLIST, SourceDefinition(SATLIST, "https://raw.githubusercontent.com/foldynl/hamradio-value-lists/main/lists/satellites/satslist.csv", "satslist.csv", "LOV/last_sat_update", "sat_info", 10)}, {SOTASUMMITS, SourceDefinition(SOTASUMMITS, "https://raw.githubusercontent.com/foldynl/hamradio-value-lists/main/lists/SOTA/summitslist.csv.gz", "summitslist.csv.gz", "LOV/last_sotasummits_update", "sota_summits", 30)}, {WWFFDIRECTORY, SourceDefinition(WWFFDIRECTORY, "https://raw.githubusercontent.com/foldynl/hamradio-value-lists/main/lists/WWFF/wwff_directory.csv.gz", "wwff_directory.csv.gz", "LOV/last_wwffdirectory_update", "wwff_directory", 21)}, {IOTALIST, SourceDefinition(IOTALIST, "https://www.iota-world.org/islands-on-the-air/downloads/download-file.html?path=groups.json", "groups.json", "LOV/last_iota_update", "iota", 30)}, {POTADIRECTORY, SourceDefinition(POTADIRECTORY, "https://raw.githubusercontent.com/foldynl/hamradio-value-lists/main/lists/POTA/all_parks_ext.csv.gz", "all_parks_ext.csv.gz", "LOV/last_pota_update", "pota_directory", 30)}, {MEMBERSHIPCONTENTLIST, SourceDefinition(MEMBERSHIPCONTENTLIST, "https://raw.githubusercontent.com/foldynl/hamradio-membeship-lists/main/lists/content.csv", "content.csv", "LOV/last_membershipcontent_update", "membership_directory", 7)} }; QNetworkAccessManager* nam; QNetworkReply *currentReply; bool abortRequested; QRegularExpression CTYPrefixSeperatorRe; QRegularExpression CTYPrefixFormatRe; private: bool isTableFilled(const QString &); bool deleteTable(const QString &); void download(const SourceDefinition &); void parseData(const LOVDownloader::SourceDefinition &, QTextStream &); void parseCTY(const SourceDefinition &sourceDef, QTextStream& data); void parseSATLIST(const SourceDefinition &sourceDef, QTextStream& data); void parseSOTASummits(const SourceDefinition &sourceDef, QTextStream& data); void parseWWFFDirectory(const SourceDefinition &sourceDef, QTextStream& data); void parseIOTA(const SourceDefinition &sourceDef, QTextStream& data); void parsePOTA(const SourceDefinition &sourceDef, QTextStream& data); void parseMembershipContent(const SourceDefinition &sourceDef, QTextStream& data); void parseClubLogCTY(const SourceDefinition &sourceDef, QTextStream &data); bool parseCSVGeneric(const SourceDefinition &sourceDef, QTextStream &data, const QString &insertSQL, const QStringList &csvColumns, csv::CSVFormat format, const QString &preValidateContains = QString()); private slots: void processReply(QNetworkReply*); void loadData(const LOVDownloader::SourceDefinition &); }; #endif // QLOG_CORE_LOVDOWNLOADER_H ================================================ FILE: core/LogDatabase.cpp ================================================ #include #include #include #include #include #include #include #include #include #include #include "LogDatabase.h" #include "core/debug.h" #include "core/Migration.h" #include "core/LogParam.h" #include "core/CredentialStore.h" #include "core/PlatformParameterManager.h" MODULE_IDENTIFICATION("qlog.core.logdatabase"); QString LogDatabase::PLATFORM_WINDOWS = "Windows"; QString LogDatabase::PLATFORM_MACOS = "MacOS"; QString LogDatabase::PLATFORM_LINUX = "Linux"; QString LogDatabase::PLATFORM_LINUXFLATPAK = "LinuxFlatpak"; QDir LogDatabase::dbDirectory() { return QDir(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)); } QString LogDatabase::dbFilename() { return dbDirectory().filePath("qlog.db"); } QString LogDatabase::currentPlatformId() { #if defined(Q_OS_WIN) return LogDatabase::PLATFORM_WINDOWS; #elif defined(Q_OS_MACOS) return LogDatabase::PLATFORM_MACOS; #elif defined(QLOG_FLATPAK) return LogDatabase::PLATFORM_LINUXFLATPAK; #else return LogDatabase::PLATFORM_LINUX; #endif } LogDatabase::LogDatabase() { FCT_IDENTIFICATION; } bool LogDatabase::hadPasswordImportWarning() const { return passwordImportWarning; } bool LogDatabase::createSQLFunctions() { FCT_IDENTIFICATION; QVariant v = QSqlDatabase::database().driver()->handle(); if ( !v.isValid() || qstrcmp(v.typeName(), "sqlite3*") != 0 ) { qCritical() << "Cannot get SQLite driver handle"; return false; } sqlite3 *db_handle = *static_cast(v.data()); if ( db_handle == 0 ) { qCritical() << "Cannot define new SQLite functions"; return false; } sqlite3_initialize(); sqlite3_create_function(db_handle, "translate_to_locale", 1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, nullptr, [](sqlite3_context *ctx, int argc, sqlite3_value **argv) { if ( argc != 1 ) { sqlite3_result_error(ctx, "Invalid arguments", -1); return; } switch ( sqlite3_value_type(argv[0]) ) { case SQLITE_TEXT: { const char *text = reinterpret_cast(sqlite3_value_text(argv[0])); const QString &translatedText = QCoreApplication::translate("DBStrings", text); sqlite3_result_text(ctx, translatedText.toUtf8().constData(), -1, SQLITE_TRANSIENT); } break; case SQLITE_NULL: sqlite3_result_null(ctx); break; case SQLITE_INTEGER: sqlite3_result_int(ctx, sqlite3_value_int(argv[0])); break; case SQLITE_FLOAT: sqlite3_result_double(ctx, sqlite3_value_double(argv[0])); break; default: sqlite3_result_error(ctx, "Invalid arguments", -1); } }, nullptr, nullptr); sqlite3_create_collation(db_handle, "LOCALEAWARE", SQLITE_UTF16, nullptr, [](void *, int ll, const void * l, int rl, const void * r) { const QString &left = QString::fromUtf16(reinterpret_cast(l), ll/2); const QString &right = QString::fromUtf16(reinterpret_cast(r), rl/2); return QString::localeAwareCompare(left, right); // controlled by LC_COLLATE }); return true; } bool LogDatabase::atomicCopy(const QString &filename) { FCT_IDENTIFICATION; QSqlDatabase db = QSqlDatabase::database(); if ( !db.isOpen() ) { qWarning() << "Database is not opened"; return false; } // Validate filename — must be a plain filename, no path separators if ( filename.isEmpty() ) { qWarning() << "Invalid filename:" << filename; return false; } QVariant v = db.driver()->handle(); if ( !v.isValid() || qstrcmp(v.typeName(), "sqlite3*") != 0) { qWarning() << "Cannot get Database Driver Handler"; return false; } sqlite3 *srcHandle = *static_cast(v.data()); sqlite3 *dstHandle = nullptr; sqlite3_backup *backup = nullptr; bool ok = false; int stepSize = 5000; const QString newDBFilename = dbDirectory().filePath(filename); if (sqlite3_open_v2(newDBFilename.toUtf8().constData(), &dstHandle, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr) != SQLITE_OK) { qWarning() << "Cannot open a new database file:" << sqlite3_errmsg(dstHandle); goto cleanup; } backup = sqlite3_backup_init(dstHandle, "main", srcHandle, "main"); if ( !backup ) { qWarning() << "Cannot Init a backup" << sqlite3_errmsg(dstHandle); goto cleanup; } while (true) { int rc = sqlite3_backup_step(backup, stepSize); int total = sqlite3_backup_pagecount(backup); int remaining = sqlite3_backup_remaining(backup); int done = total - remaining; int percent = total > 0 ? (done * 100 / total) : 0; qCDebug(runtime) << percent; if (rc == SQLITE_DONE) { ok = true; break; } else if (rc == SQLITE_BUSY || rc == SQLITE_LOCKED) { sqlite3_sleep(50); continue; } else if (rc == SQLITE_OK) { continue; } else { qWarning() << "Backup Error" << sqlite3_errmsg(dstHandle); break; } } // sqlite3_backup_finish always releases the backup handle, // even on error — never call it twice if ( sqlite3_backup_finish(backup) != SQLITE_OK ) { qWarning() << "Cannot finalize the database copy" << sqlite3_errmsg(dstHandle); ok = false; } backup = nullptr; cleanup: if ( backup ) sqlite3_backup_finish(backup); if ( dstHandle ) sqlite3_close(dstHandle); if ( !ok ) QFile::remove(newDBFilename); return ok; } bool LogDatabase::openDatabase() { FCT_IDENTIFICATION; QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE"); db.setDatabaseName(dbFilename()); db.setConnectOptions("QSQLITE_ENABLE_REGEXP"); if ( !db.open() ) { qCritical() << db.lastError(); return false; } QSqlQuery query; if ( !query.exec("PRAGMA foreign_keys = ON") ) { qCritical() << "Cannot set PRAGMA foreign_keys"; return false; } if ( !query.exec("PRAGMA journal_mode = WAL") ) { qCritical() << "Cannot set PRAGMA journal_mode"; return false; } while ( query.next() ) { QString pragma = query.value(0).toString(); qCDebug(runtime) << "Pragma result:" << pragma; } return createSQLFunctions(); } bool LogDatabase::schemaVersionUpgrade(bool force) { FCT_IDENTIFICATION; DBSchemaMigration m; return m.run(force); } DatabaseInfo LogDatabase::inspectDatabase(const QString &filename) { FCT_IDENTIFICATION; qCDebug(function_parameters) << filename; DatabaseInfo info; const QString connectionName = QStringLiteral("InspectDB"); // Use do-while(false) pattern to ensure removeDatabase is called after db/query are out of scope do { QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", connectionName); db.setDatabaseName(filename); if ( !db.open() ) { info.errorMessage = db.lastError().text(); qWarning() << "Cannot open database for inspection:" << info.errorMessage; break; } // Check if it's a valid QLog database by looking for schema_versions table QSqlQuery query(db); if ( !query.exec("SELECT version FROM schema_versions ORDER BY version DESC LIMIT 1") ) { info.errorMessage = QObject::tr("Not a valid QLog database"); db.close(); break; } info.schemaVersion = ( query.first() ) ? query.value(0).toInt() : 0; // Check if schema version is too new if ( info.schemaVersion > DBSchemaMigration::latestVersion ) { info.errorMessage = QObject::tr("Database version too new (requires newer QLog version)"); db.close(); break; } // Read source platform and encrypted passwords from log_param if ( query.exec("SELECT value FROM log_param WHERE name = 'sourceplatform'") ) { if ( query.first() ) info.sourcePlatform = query.value(0).toString(); if ( info.sourcePlatform.isEmpty() ) { info.errorMessage = QObject::tr("Database is not QLog Export file"); db.close(); break; } } if ( query.exec("SELECT value FROM log_param WHERE name = 'security/encryptedpasswords'") ) { if ( query.first() && !query.value(0).toString().isEmpty() ) info.hasEncryptedPasswords = true; } info.valid = true; db.close(); } while (false); QSqlDatabase::removeDatabase(connectionName); qCDebug(runtime) << "Database info - valid:" << info.valid << "version:" << info.schemaVersion << "platform:" << info.sourcePlatform << "hasPasswords:" << info.hasEncryptedPasswords; return info; } QString LogDatabase::pendingImportPath() { return dbDirectory().filePath("qlog.db.pending"); } bool LogDatabase::hasPendingImport() { FCT_IDENTIFICATION; bool exists = QFile::exists(pendingImportPath()); qCDebug(runtime) << "Pending import exists:" << exists; return exists; } bool LogDatabase::processPendingImport() { FCT_IDENTIFICATION; const QString pendingPath = pendingImportPath(); if ( !QFile::exists(pendingPath) ) { qCDebug(runtime) << "No pending import"; return true; } qCDebug(runtime) << "Processing pending database import"; const QString currentDbPath = dbFilename(); const QString walPath = currentDbPath + "-wal"; // remove also support files const QString shmPath = currentDbPath + "-shm"; // remove also support files if ( QFile::exists(currentDbPath) ) { if ( !QFile::remove(currentDbPath) ) { qWarning() << "Cannot remove current database:" << currentDbPath; return false; } } if ( QFile::exists(walPath) ) QFile::remove(walPath); if ( QFile::exists(shmPath) ) QFile::remove(shmPath); // Rename pending to current if ( !QFile::rename(pendingPath, currentDbPath) ) { qWarning() << "Cannot rename pending database to current"; return false; } qCDebug(runtime) << "Pending database moved to current"; if ( !openDatabase() ) { qCritical() << "Cannot open imported database"; return false; } if ( !schemaVersionUpgrade() ) { qCritical() << "Schema migration failed"; return false; } const QString passphrase = CredentialStore::instance()->getImportPassphrase(); if ( !passphrase.isEmpty() ) { qCDebug(runtime) << "Importing passwords from encrypted store"; CredentialStore::instance()->deleteAllPasswords(); if ( !CredentialStore::instance()->importPasswords(passphrase) ) { qCWarning(runtime) << "Password import failed"; passwordImportWarning = true; } CredentialStore::instance()->deleteImportPassphrase(); } const QString paramsPath = PlatformParameterManager::pendingParametersPath(); if ( QFile::exists(paramsPath) ) { qCDebug(runtime) << "Applying platform-specific parameters"; QList params = PlatformParameterManager::loadParametersFromFile(paramsPath); PlatformParameterManager::applyParameters(params); QList profileParams = PlatformParameterManager::loadProfileParametersFromFile(paramsPath); PlatformParameterManager::applyProfileParameters(profileParams); QFile::remove(paramsPath); } // For Flatpak target: apply fixed paths (TQSL, rigctld_path) // This overrides any imported values with Flatpak-specific paths PlatformParameterManager::applyFlatpakFixedPaths(); LogParam::removeEncryptedPasswords(); LogParam::removeSourcePlatform(); // Generate new LogID because we want to uniquie identified every log QString newLogId = QUuid::createUuid().toString(QUuid::WithoutBraces); LogParam::setLogID(newLogId); qCDebug(runtime) << "New LogID generated:" << newLogId; qCDebug(runtime) << "Database import completed successfully"; return true; } ================================================ FILE: core/LogDatabase.h ================================================ #ifndef QLOG_CORE_LOGDATABASE_H #define QLOG_CORE_LOGDATABASE_H #include #include struct DatabaseInfo { bool valid = false; int schemaVersion = 0; QString sourcePlatform; bool hasEncryptedPasswords = false; QString errorMessage; }; class LogDatabase { public: static LogDatabase* instance() { static LogDatabase instance; return &instance; }; static QString PLATFORM_WINDOWS; static QString PLATFORM_MACOS; static QString PLATFORM_LINUX; static QString PLATFORM_LINUXFLATPAK; static QDir dbDirectory(); static QString dbFilename(); static QString currentPlatformId(); // Inspect a database file without opening it as main connection // Returns information about the database (schema version, platform, etc.) static DatabaseInfo inspectDatabase(const QString &filename); // Path where pending import database is stored static QString pendingImportPath(); // Check if there is a pending import to process static bool hasPendingImport(); // Process pending import (called at startup) // Returns true if import was successful or no import was pending bool processPendingImport(); // Returns true if the last processPendingImport() failed to import passwords // (passwords were deleted but could not be restored from the encrypted store) bool hadPasswordImportWarning() const; bool atomicCopy(const QString &filename); bool openDatabase(); bool schemaVersionUpgrade(bool force = false); bool createSQLFunctions(); private: LogDatabase(); bool passwordImportWarning = false; }; #endif // QLOG_CORE_LOGDATABASE_H ================================================ FILE: core/LogLocale.cpp ================================================ #include "LogLocale.h" #include "core/debug.h" MODULE_IDENTIFICATION("qlog.core.loglocale"); LogLocale::LogLocale() : regexp(QRegularExpression(R"(, tttt|\(t\)|\(tttt\)|\[tttt\]|\btttt\b|\btt\b|\bt\b)")), is24hUsed(!timeFormat(QLocale::ShortFormat).contains("ap", Qt::CaseInsensitive)), isMetricUnitUsed(measurementSystem() == QLocale::MetricSystem) { FCT_IDENTIFICATION; systemDateFormat = dateFormat(QLocale::ShortFormat); qCDebug(runtime) << systemDateFormat; if ( systemDateFormat.contains("yy") && !systemDateFormat.contains("yyyy") ) systemDateFormat.replace("yy", "yyyy"); } void LogLocale::changeTime12_24Format(QString &formatString) const { if ( getSettingUse24hformat() ) formatString.remove("ap", Qt::CaseInsensitive).remove("a", Qt::CaseInsensitive); else if ( is24hUsed ) { formatString += " AP"; formatString = formatString.toLower(); } } const QString LogLocale::formatTimeLongWithoutTZ() const { FCT_IDENTIFICATION; qCDebug(runtime) << timeFormat(QLocale::LongFormat); QString ret = timeFormat(QLocale::LongFormat).remove(regexp).trimmed(); changeTime12_24Format(ret); qCDebug(runtime) << "format:" << ret; return ret; } const QString LogLocale::formatTimeShort() const { FCT_IDENTIFICATION; qCDebug(runtime) << timeFormat(QLocale::ShortFormat); QString ret = timeFormat(QLocale::ShortFormat); changeTime12_24Format(ret); qCDebug(runtime) << "format:" << ret; return ret; } const QString LogLocale::formatTimeLong() const { FCT_IDENTIFICATION; QString ret = formatTimeLongWithoutTZ(); ret #if (QT_VERSION < QT_VERSION_CHECK(6, 5, 0)) .append(" t"); #else .append(" ttt"); #endif qCDebug(runtime) << "format:" << ret; return ret; } const QString LogLocale::formatDateShortWithYYYY() const { FCT_IDENTIFICATION; QString ret = (getSettingUseSystemDateFormat()) ? systemDateFormat : getSettingDateFormat(); qCDebug(runtime) << "format:" << ret; return ret; } const QString LogLocale::formatDateTimeShortWithYYYY() const { FCT_IDENTIFICATION; QString ret = formatDateShortWithYYYY() + " " + formatTimeShort(); qCDebug(runtime) << "format:" << ret; return ret; } bool LogLocale::getSettingUse24hformat() const { return settings.value("use24hformat", is24hUsed).toBool(); } void LogLocale::setSettingUse24hformat(bool value) { settings.setValue("use24hformat", value); } bool LogLocale::getSettingUseMetric() const { return settings.value("usemetricformat", isMetricUnitUsed).toBool(); } void LogLocale::setSettingUseMetric(bool value) { settings.setValue("usemetricformat", value); } bool LogLocale::getSettingUseSystemDateFormat() const { return settings.value("usesystemdateformat", true).toBool(); } void LogLocale::setSettingUseSystemDateFormat(bool value) { settings.setValue("usesystemdateformat", value); } const QString LogLocale::getSettingDateFormat() const { return settings.value("customdateformatstring", systemDateFormat).toString(); } void LogLocale::setSettingDateFormat(const QString &value) { settings.setValue("customdateformatstring", value); } ================================================ FILE: core/LogLocale.h ================================================ #ifndef QLOG_CORE_LOGLOCALE_H #define QLOG_CORE_LOGLOCALE_H #include #include #include class LogLocale : public QLocale { public: LogLocale(); const QString formatTimeLongWithoutTZ() const; const QString formatTimeShort() const; const QString formatTimeLong() const; const QString formatDateShortWithYYYY() const; const QString formatDateTimeShortWithYYYY() const; bool getSettingUse24hformat() const; void setSettingUse24hformat(bool value); bool getSettingUseSystemDateFormat() const; void setSettingUseSystemDateFormat(bool value); const QString getSettingDateFormat() const; void setSettingDateFormat(const QString &value); bool getSettingUseMetric() const; void setSettingUseMetric(bool value); private: const QRegularExpression regexp; bool is24hUsed; bool isMetricUnitUsed; QSettings settings; QString systemDateFormat; void changeTime12_24Format(QString &formatString) const; }; #endif // QLOG_CORE_LOGLOCALE_H ================================================ FILE: core/LogParam.cpp ================================================ #include #include #include #include "LogParam.h" #include "debug.h" #include "data/Data.h" #include "models/LogbookModel.h" #include "data/BandPlan.h" MODULE_IDENTIFICATION("qlog.core.logparam"); #define PARAMMUTEXLOCKER qCDebug(runtime) << "Waiting for mutex"; \ QMutexLocker locker(&cacheMutex); \ qCDebug(runtime) << "Using logparam" LogParam::LogParam(QObject *parent) : QObject(parent) { FCT_IDENTIFICATION; } bool LogParam::setLOVParam(const QString &LOVName, const QVariant &value) { return setParam(LOVName, value); } QDate LogParam::getLOVaParam(const QString &LOVName) { return getParam(LOVName).toDate(); } bool LogParam::setLastBackupDate(const QDate date) { return setParam("last_backup", date); } QDate LogParam::getLastBackupDate() { return getParam("last_backup").toDate(); } bool LogParam::setLogID(const QString &id) { return setParam("logid", id); } QString LogParam::getLogID() { return getParam("logid").toString(); } bool LogParam::setContestSeqnoType(const QVariant &data) { return setParam("contest/seqnotype", data); } int LogParam::getContestSeqnoType() { return getParam("contest/seqnotype", Data::SeqType::SINGLE).toInt(); } bool LogParam::setContestManuDupeType(const QVariant &data) { return setParam("contest/dupetype", data); } int LogParam::getContestDupeType() { return getParam("contest/dupetype", Data::DupeType::ALL_BANDS).toInt(); } bool LogParam::setContestLinkExchange(const QVariant &data) { return setParam("contest/linkexchangetype", data); } int LogParam::getContestLinkExchange() { return getParam("contest/linkexchangetype", LogbookModel::COLUMN_INVALID).toInt(); } bool LogParam::setContestFilter(const QString &filterName) { return setParam("contest/filter", filterName); } QString LogParam::getContestFilter() { return getParam("contest/filter").toString(); } bool LogParam::setContestID(const QString &contestID) { return setParam("contest/contestid", contestID); } QString LogParam::getContestID() { return getParam("contest/contestid", QString()).toString(); } bool LogParam::setContestDupeDate(const QDateTime date) { return setParam("contest/dupeDate", date); } QDateTime LogParam::getContestDupeDate() { return getParam("contest/dupeDate").toDateTime(); } void LogParam::removeConetstDupeDate() { removeParamGroup("contest/dupeDate"); } bool LogParam::getDxccConfirmedByLotwState() { return getParam("others/dxccconfirmedbylotw", true).toBool(); } bool LogParam::setDxccConfirmedByLotwState(bool state) { return setParam("others/dxccconfirmedbylotw", state); } bool LogParam::setDxccConfirmedByPaperState(bool state) { return setParam("others/dxccconfirmedbypaper", state); } bool LogParam::getDxccConfirmedByPaperState() { return getParam("others/dxccconfirmedbypaper", true).toBool(); } bool LogParam::setDxccConfirmedByEqslState(bool state) { return setParam("others/dxccconfirmedbyeqsl", state); } bool LogParam::getDxccConfirmedByEqslState() { return getParam("others/dxccconfirmedbyeqsl", false).toBool(); } int LogParam::getContestSeqno(const QString &band) { return getParam(( band.isEmpty() ) ? "contest/seqnos/single" : QString("contest/seqnos/%1").arg(band), 1).toInt(); } bool LogParam::setContestSeqno(int value, const QString &band) { return setParam(( band.isEmpty() ) ? "contest/seqnos/single" : QString("contest/seqnos/%1").arg(band), value); } void LogParam::removeContestSeqno() { removeParamGroup("contest/seqnos/"); } bool LogParam::setDXCTrendContinent(const QString &cont) { return setParam("dxc/trendContinent", cont); } QString LogParam::getDXCTrendContinent(const QString &def) { return getParam("dxc/trendContinent", def).toString(); } void LogParam::removeDXCTrendContinent() { removeParamGroup("dxc/trendContinent"); } QStringList LogParam::bandmapsWidgets() { return getKeys("bandmap/"); } void LogParam::removeBandmapWidgetGroup(const QString &group) { removeParamGroup("bandmap/" + group); } double LogParam::getBandmapScrollFreq(const QString &widgetID, const QString &bandName) { return getParam("bandmap/" + widgetID + "/" + bandName + "/scrollfreq" , 0.0).toDouble(); } bool LogParam::setBandmapScrollFreq(const QString &widgetID, const QString &bandName, double scroll) { return setParam("bandmap/" + widgetID + "/" + bandName + "/scrollfreq", scroll); } QVariant LogParam::getBandmapZoom(const QString &widgetID, const QString &bandName, const QVariant &defaultValue) { return getParam("bandmap/" + widgetID + "/" + bandName + "/zoom", defaultValue); } bool LogParam::setBandmapZoom(const QString &widgetID, const QString &bandName, const QVariant &zoom) { return setParam("bandmap/" + widgetID + "/" + bandName + "/zoom", zoom); } bool LogParam::setBandmapAging(const QString &widgetID, int aging) { return setParam("bandmap/" + widgetID + "/spotaging", aging); } int LogParam::getBandmapAging(const QString &widgetID) { return getParam("bandmap/" + widgetID + "/spotaging", 0).toInt(); } bool LogParam::setBandmapCenterRX(const QString &widgetID, bool centerRX) { return setParam("bandmap/" + widgetID + "/centerrx", centerRX); } bool LogParam::getBandmapCenterRX(const QString &widgetID) { return getParam("bandmap/" + widgetID + "/centerrx", true).toBool(); } bool LogParam::setBandmapShowEmergency(const QString &widgetID, bool show) { return setParam("bandmap/" + widgetID + "/showemergency", show); } bool LogParam::getBandmapShowEmergency(const QString &widgetID) { return getParam("bandmap/" + widgetID + "/showemergency", true).toBool(); } QString LogParam::getUploadQSOLastCall() { return getParam("uploadqso/lastcall").toString(); } void LogParam::setUploadQSOLastCall(const QString &call) { setParam("uploadqso/lastcall", call); } bool LogParam::getUploadeqslQSLComment() { return getParam("uploadqso/eqsl/last_checkcomment", false).toBool(); } void LogParam::setUploadeqslQSLComment(const bool state) { setParam("uploadqso/eqsl/last_checkcomment", state); } bool LogParam::getUploadeqslQSLMessage() { return getParam("uploadqso/eqsl/last_checkqslsmsg", false).toBool(); } QString LogParam::getUploadeqslQTHProfile() { return getParam("uploadqso/eqsl/last_QTHProfile").toString(); } void LogParam::setUploadeqslQTHProfile(const QString &qthProfile) { setParam("uploadqso/eqsl/last_QTHProfile", qthProfile); } void LogParam::setUploadeqslQSLMessage(const bool state) { setParam("uploadqso/eqsl/last_checkqslsmsg", state); } bool LogParam::getUploadServiceState(const QString &name) { return getParam("uploadqso/" + name + "/enabled", false).toBool(); } void LogParam::setUploadServiceState(const QString &name, bool state) { setParam("uploadqso/" + name + "/enabled", state); } int LogParam::getUploadQSOFilterType() { return getParam("uploadqso/filtertype").toInt(); } void LogParam::setUploadQSOFilterType(int filterID) { setParam("uploadqso/filtertype", filterID); } QString LogParam::getUploadLoTWLocation() { return getParam("uploadqso/lotw/last_location").toString(); } void LogParam::setUploadLoTWLocation(const QString &location) { setParam("uploadqso/lotw/last_location", location); } bool LogParam::getDownloadQSLServiceState(const QString &name) { return getParam("downloadqsl/" + name + "/enabled", false).toBool(); } void LogParam::setDownloadQSLServiceState(const QString &name, bool state) { setParam("downloadqsl/" + name + "/enabled", state); } QDate LogParam::getDownloadQSLServiceLastDate(const QString &name) { return getParam("downloadqsl/" + name + "/lastdate", QDate(1900, 1, 1)).toDate(); } void LogParam::setDownloadQSLServiceLastDate(const QString &name, const QDate &date) { setParam("downloadqsl/" + name + "/lastdate", date); } bool LogParam::getDownloadQSLServiceLastQSOQSL(const QString &name) { return getParam("downloadqsl/" + name + "/qsoqsl", true).toBool(); } void LogParam::setDownloadQSLServiceLastQSOQSL(const QString &name, bool state) { setParam("downloadqsl/" + name + "/qsoqsl", state); } QString LogParam::getDownloadQSLLoTWLastCall() { return getParam("downloadqsl/lotw/lastmycallsign").toString(); } void LogParam::setDownloadQSLLoTWLastCall(const QString &call) { setParam("downloadqsl/lotw/lastmycallsign", call); } QString LogParam::getDownloadQSLeQSLLastProfile() { return getParam("downloadqsl/eqsl/lastqthprofile").toString(); } void LogParam::setDownloadQSLeQSLLastProfile(const QString &profile) { setParam("downloadqsl/eqsl/lastqthprofile", profile); } QString LogParam::getQRZCOMCallbookUsername() { return getParam("services/qrzcom/callbook/username").toString().trimmed(); } void LogParam::setQRZCOMCallbookUsername(const QString &username) { setParam("services/qrzcom/callbook/username", username); } QStringList LogParam::getQRZCOMAPICallsignsList() { return getParamStringList("services/qrzcom/logbook/apicallsigns"); } void LogParam::setQRZCOMAPICallsignsList(const QStringList &list) { setParam("services/qrzcom/logbook/apicallsigns", list); } QString LogParam::getCloudlogAPIEndpoint() { return getParam("services/cloudlog/logbook/endpoint", "http://localhost").toString(); } void LogParam::setCloudlogAPIEndpoint(const QString &endpoint) { setParam("services/cloudlog/logbook/endpoint", endpoint); } uint LogParam::getCloudlogStationID() { return getParam("services/cloudlog/logbook/stationid").toUInt(); } void LogParam::setCloudlogStationID(uint stationID) { setParam("services/cloudlog/logbook/stationid", stationID); } QString LogParam::getClublogLogbookReqEmail() { return getParam("services/clublog/logbook/regemail").toString().trimmed(); } void LogParam::setClublogLogbookReqEmail(const QString &email) { setParam("services/clublog/logbook/regemail", email); } bool LogParam::getClublogUploadImmediatelyEnabled() { return getParam("services/clublog/logbook/uploadimmediately", false).toBool(); } void LogParam::setClublogUploadImmediatelyEnabled(bool state) { setParam("services/clublog/logbook/uploadimmediately", state); } QString LogParam::getEQSLLogbookUsername() { return getParam("services/eqsl/logbook/username").toString().trimmed(); } void LogParam::setEQSLLogbookUsername(const QString &username) { setParam("services/eqsl/logbook/username", username); } QString LogParam::getHamQTHCallbookUsername() { return getParam("services/hamqth/callbook/username").toString().trimmed(); } void LogParam::setHamQTHCallbookUsername(const QString &username) { setParam("services/hamqth/callbook/username", username); } QString LogParam::getHRDLogLogbookReqCallsign() { return getParam("services/hrdlog/logbook/regcallsign").toString().trimmed(); } void LogParam::setHRDLogLogbookReqCallsign(const QString &callsign) { setParam("services/hrdlog/logbook/regcallsign", callsign); } bool LogParam::getHRDLogOnAir() { return getParam("services/hrdlog/logbook/onair", false).toBool(); } void LogParam::setHRDLogOnAir(bool state) { setParam("services/hrdlog/logbook/onair", state); } QString LogParam::getKSTChatUsername() { return getParam("services/kst/chat/username").toString().trimmed(); } void LogParam::setKSTChatUsername(const QString &username) { setParam("services/kst/chat/username", username); } QString LogParam::getLoTWCallbookUsername() { return getParam("services/lotw/callbook/username").toString().trimmed(); } void LogParam::setLoTWCallbookUsername(const QString &username) { setParam("services/lotw/callbook/username", username); } QString LogParam::getLoTWTQSLPath(const QString &defaultPath) { return getParam("services/lotw/callbook/tqsl", defaultPath).toString().trimmed(); } void LogParam::setLoTWTQSLPath(const QString &path) { setParam("services/lotw/callbook/tqsl", path); } bool LogParam::isLoTWTQSLPathKey(const QString &key) { return key.compare("services/lotw/callbook/tqsl", Qt::CaseInsensitive) == 0; } QString LogParam::getPrimaryCallbook(const QString &defaultValue) { return getParam("callbook/primary", defaultValue).toString(); } void LogParam::setPrimaryCallbook(const QString &callbookName) { setParam("callbook/primary", callbookName); } QString LogParam::getSecondaryCallbook(const QString &defaultValue) { return getParam("callbook/secondary", defaultValue).toString(); } void LogParam::setSecondaryCallbook(const QString &callbookName) { setParam("callbook/secondary", callbookName); } QString LogParam::getCallbookWebLookupURL(const QString &defaultURL) { return getParam("callbook/weblookupurl", defaultURL).toString(); } void LogParam::setCallbookWebLookupURL(const QString &url) { setParam("callbook/weblookupurl", url); } QString LogParam::getNetworkNotifLogQSOAddrs() { return getParam("network/notif/log/qso/addrs").toString(); } void LogParam::setNetworkNotifLogQSOAddrs(const QString &addrs) { setParam("network/notif/log/qso/addrs", addrs); } QString LogParam::getNetworkNotifDXCSpotAddrs() { return getParam("network/notif/dxc/spot/addrs").toString(); } void LogParam::setNetworkNotifDXCSpotAddrs(const QString &addrs) { setParam("network/notif/dxc/spot/addrs", addrs); } QString LogParam::getNetworkNotifWSJTXCQSpotAddrs() { return getParam("network/notif/wsjtx/cqspot/addrs").toString(); } void LogParam::setNetworkNotifWSJTXCQSpotAddrs(const QString &addrs) { setParam("network/notif/wsjtx/cqspot/addrs", addrs); } QString LogParam::getNetworkNotifAlertsSpotAddrs() { return getParam("network/notif/alerts/spot/addrs").toString(); } void LogParam::setNetworkNotifAlertsSpotAddrs(const QString &addrs) { setParam("network/notif/alerts/spot/addrs", addrs); } QString LogParam::getNetworkNotifRigStateAddrs() { return getParam("network/notif/rig/state/addrs").toString(); } void LogParam::setNetworkNotifRigStateAddrs(const QString &addrs) { setParam("network/notif/rig/state/addrs", addrs); } int LogParam::getNetworkWsjtxListenerPort(int defaultPort) { return getParam("network/listener/wsjtx/port", defaultPort).toInt(); } void LogParam::setNetworkNotifRigStateAddrs(int port) { setParam("network/listener/wsjtx/port", port); } QString LogParam::getNetworkWsjtxForwardAddrs() { return getParam("network/forwarder/wsjtx/addrs").toString(); } void LogParam::setNetworkWsjtxForwardAddrs(const QString &addrs) { setParam("network/forwarder/wsjtx/addrs", addrs); } bool LogParam::getNetworkWsjtxListenerJoinMulticast() { return getParam("network/listener/wsjtx/multicast/join", false).toBool(); } void LogParam::setNetworkWsjtxListenerJoinMulticast(bool state) { setParam("network/listener/wsjtx/multicast/join", state); } QString LogParam::getNetworkWsjtxListenerMulticastAddr() { return getParam("network/listener/wsjtx/multicast/addr").toString(); } void LogParam::setNetworkWsjtxListenerMulticastAddr(const QString &addr) { setParam("network/listener/wsjtx/multicast/addr", addr); } int LogParam::getNetworkWsjtxListenerMulticastTTL() { return getParam("network/listener/wsjtx/multicast/ttl").toInt(); } void LogParam::setNetworkWsjtxListenerMulticastTTL(int ttl) { setParam("network/listener/wsjtx/multicast/ttl", ttl); } QStringList LogParam::getEnabledMemberlists() { return getParamStringList("memberlist/enabledlists"); } void LogParam::setEnabledMemberlists(const QStringList &list) { setParam("memberlist/enabledlists", list); } int LogParam::getAlertAging() { return getParam("alert/aging").toInt(); } void LogParam::setAlertAging(int aging) { setParam("alert/aging", aging); } QByteArray LogParam::getAlertWidgetState() { return QByteArray::fromBase64(getParam("alert/widgetstate").toByteArray()); } void LogParam::setAlertWidgetState(const QByteArray &state) { setParam("alert/widgetstate", state.toBase64()); } bool LogParam::getCWConsoleSendWord() { return getParam("cwconsole/sendword", false).toBool(); } void LogParam::setCWConsoleSendWord(bool state) { setParam("cwconsole/sendword", state); } bool LogParam::getChatSelectedRoom() { return getParam("chat/selectedroom", 0).toInt(); } void LogParam::setChatSelectedRoom(int room) { setParam("chat/selectedroom", room); } double LogParam::getNewContactFreq() { return getParam("newcontact/freq", 3.5).toDouble(); } void LogParam::setNewContactFreq(double freq) { setParam("newcontact/freq", freq); } QString LogParam::getNewContactMode() { return getParam("newcontact/mode", "CW").toString(); } void LogParam::setNewContactMode(const QString &mode) { setParam("newcontact/mode", mode); } QString LogParam::getNewContactSubMode() { return getParam("newcontact/submode").toString(); } void LogParam::setNewContactSubMode(const QString &submode) { setParam("newcontact/submode", submode); } double LogParam::getNewContactPower() { return getParam("newcontact/power", 100).toDouble(); } void LogParam::setNewContactPower(double power) { setParam("newcontact/power", power); } int LogParam::getNewContactTabIndex() { return getParam("newcontact/tabindex", 0).toInt(); } void LogParam::setNewContactTabIndex(int index) { setParam("newcontact/tabindex", index); } QString LogParam::getNewContactQSLSent() { return getParam("newcontact/sqlsent", "Q").toString(); } void LogParam::setNewContactQSLSent(const QString &qslsent) { setParam("newcontact/sqlsent", qslsent); } QString LogParam::getNewContactLoTWQSLSent() { return getParam("newcontact/lotwsqlsent", "Q").toString(); } void LogParam::setNewContactLoTWQSLSent(const QString &qslsent) { setParam("newcontact/lotwsqlsent", qslsent); } QString LogParam::getNewContactEQSLWQSLSent() { return getParam("newcontact/eqslsqlsent", "Q").toString(); } void LogParam::setNewContactEQSLQSLSent(const QString &qslsent) { setParam("newcontact/eqslsqlsent", qslsent); } QString LogParam::getNewContactQSLVia() { return getParam("newcontact/qslvia").toString(); } void LogParam::setNewContactQSLVia(const QString &qslvia) { setParam("newcontact/qslvia", qslvia); } QString LogParam::getNewContactPropMode() { return getParam("newcontact/propmode").toString(); } void LogParam::setNewContactPropMode(const QString &propmode) { setParam("newcontact/propmode", propmode); } bool LogParam::getNewContactTabsExpanded() { return getParam("newcontact/tabsexpand", true).toBool(); } void LogParam::setNewContactTabsExpanded(bool state) { setParam("newcontact/tabsexpand", state); } QString LogParam::getNewContactSatName() { return getParam("newcontact/satname").toString(); } void LogParam::setNewContactSatName(const QString &name) { setParam("newcontact/satname", name); } QStringList LogParam::getMapLayerStates(const QString &widgetID) { return getKeys(widgetID + "/layerstate/"); } bool LogParam::getMapLayerState(const QString &widgetID, const QString &layerName) { return getParam(widgetID + "/layerstate/" + layerName).toBool(); } void LogParam::setMapLayerState(const QString &widgetID, const QString &layerName, bool state) { setParam(widgetID + "/layerstate/" + layerName, state); } uint LogParam::getWsjtxFilterDxccStatus() { return getParam("wsjtx/filter/dxccstatus", (DxccStatus::NewEntity | DxccStatus::NewBand | DxccStatus::NewMode | DxccStatus::NewSlot | DxccStatus::Worked | DxccStatus::Confirmed)).toUInt(); } void LogParam::setWsjtxFilterDxccStatus(uint mask) { setParam("wsjtx/filter/dxccstatus", mask); } QString LogParam::getWsjtxFilterContRE() { return getParam("wsjtx/filter/contregexp", "NOTHING|AF|AN|AS|EU|NA|OC|SA").toString(); } void LogParam::setWsjtxFilterContRE(const QString &re) { setParam("wsjtx/filter/contregexp", re); } int LogParam::getWsjtxFilterDistance() { return getParam("wsjtx/filter/distance", 0).toInt(); } void LogParam::setWsjtxFilterDistance(int dist) { setParam("wsjtx/filter/distance", dist); } int LogParam::getWsjtxFilterSNR() { return getParam("wsjtx/filter/snr", -41).toInt(); } void LogParam::setWsjtxFilterSNR(int snr) { setParam("wsjtx/filter/snr", snr); } QStringList LogParam::getWsjtxMemberlists() { return getParamStringList("wsjtx/filter/memberlists"); } void LogParam::setWsjtxMemberlists(const QStringList &list) { setParam("wsjtx/filter/memberlists", list); } QByteArray LogParam::getWsjtxWidgetState() { return QByteArray::fromBase64(getParam("wsjtx/widgetstate").toByteArray()); } void LogParam::setWsjtxWidgetState(const QByteArray &state) { setParam("wsjtx/widgetstate", state.toBase64()); } bool LogParam::getWsjtxOutputColorCQSpot() { return getParam("wsjtx/output/colorcqspot", false).toBool(); } void LogParam::setWsjtxOutputColorCQSpot(bool state) { setParam("wsjtx/output/colorcqspot", state); } uint LogParam::getDXCFilterDxccStatus() { return getParam("dxc/filter/dx/dxccstatus", (DxccStatus::NewEntity | DxccStatus::NewBand | DxccStatus::NewMode | DxccStatus::NewSlot | DxccStatus::Worked | DxccStatus::Confirmed)).toUInt(); } void LogParam::setDXCFilterDxccStatus(uint mask) { setParam("dxc/filter/dx/dxccstatus", mask); } QString LogParam::getDXCFilterContRE() { return getParam("dxc/filter/dx/contregexp", "NOTHING|AF|AN|AS|EU|NA|OC|SA").toString(); } void LogParam::setDXCFilterContRE(const QString &re) { setParam("dxc/filter/dx/contregexp", re); } QString LogParam::getDXCFilterSpotterContRE() { return getParam("dxc/filter/spotter/contregexp", "NOTHING|AF|AN|AS|EU|NA|OC|SA").toString(); } void LogParam::setDXCFilterSpotterContRE(const QString &re) { setParam("dxc/filter/spotter/contregexp", re); } bool LogParam::getDXCFilterDedup() { return getParam("dxc/filter/dedup", false).toBool(); } void LogParam::setDXCFilterDedup(bool state) { setParam("dxc/filter/dedup", state); } int LogParam::getDXCFilterDedupTime(int defaultValue) { return getParam("dxc/filter/deduptime", defaultValue).toInt(); } void LogParam::setDXCFilterDedupTime(int value) { setParam("dxc/filter/deduptime", value); } int LogParam::getDXCFilterDedupFreq(int defaultValue) { return getParam("dxc/filter/dedupfreq", defaultValue).toInt(); } void LogParam::setDXCFilterDedupFreq(int value) { setParam("dxc/filter/dedupfreq", value); } QStringList LogParam::getDXCFilterMemberlists() { return getParamStringList("dxc/filter/dx/memberlists"); } void LogParam::setDXCFilterMemberlists(const QStringList &list) { setParam("dxc/filter/dx/memberlists", list); } bool LogParam::getDXCAutoconnectServer() { return getParam("dxc/autoconnect", false).toBool(); } void LogParam::setDXCAutoconnectServer(bool state) { setParam("dxc/autoconnect", state); } bool LogParam::getDXCKeepQSOs() { return getParam("dxc/keepqsos", false).toBool(); } void LogParam::setDXCKeepQSOs(bool state) { setParam("dxc/keepqsos", state); } QByteArray LogParam::getDXCDXTableState() { return QByteArray::fromBase64(getParam("dxc/dxtablestate").toByteArray()); } void LogParam::setDXCDXTableState(const QByteArray &state) { setParam("dxc/dxtablestate", state.toBase64()); } QByteArray LogParam::getDXCWCYTableState() { return QByteArray::fromBase64(getParam("dxc/wcytablestate").toByteArray()); } void LogParam::setDXCWCYTableState(const QByteArray &state) { setParam("dxc/wcytablestate", state.toBase64()); } QByteArray LogParam::getDXCWWVTableState() { return QByteArray::fromBase64(getParam("dxc/wwvtablestate").toByteArray()); } void LogParam::setDXCWWVTableState(const QByteArray &state) { setParam("dxc/wwvtablestate", state.toBase64()); } QByteArray LogParam::getDXCTOALLTableState() { return QByteArray::fromBase64(getParam("dxc/toalltablestate").toByteArray()); } void LogParam::setDXCTOALLTableState(const QByteArray &state) { setParam("dxc/toalltablestate", state.toBase64()); } int LogParam::getDXCConsoleFontSize() { return getParam("dxc/console/fontsize", -1).toInt(); } void LogParam::setDXCConsoleFontSize(int value) { setParam("dxc/console/fontsize", value); } QStringList LogParam::getDXCServerlist() { return getParamStringList("dxc/serverlist", {"hamqth.com:7300"}); } void LogParam::setDXCServerlist(const QStringList &list) { setParam("dxc/serverlist", list); } QString LogParam::getDXCLastServer() { return getParam("dxc/lastserver").toString(); } void LogParam::setDXCLastServer(const QString &server) { setParam("dxc/lastserver", server); } QString LogParam::getDXCFilterModeRE() { QString defaultValue = "NOTHING|" + BandPlan::MODE_GROUP_STRING_PHONE + "|" + BandPlan::MODE_GROUP_STRING_CW + "|" + BandPlan::MODE_GROUP_STRING_FTx + "|" + BandPlan::MODE_GROUP_STRING_DIGITAL; return getParam("dxc/filter/dx/moderegexp", defaultValue).toString(); } void LogParam::setDXCFilterModeRE(const QString &re) { setParam("dxc/filter/dx/moderegexp", re); } QStringList LogParam::getDXCExcludedBands() { return getParamStringList("dxc/filter/excludedbands"); } void LogParam::setDXCExcludedBands(const QStringList &excluded) { setParam("dxc/filter/excludedbands", excluded); } QSet LogParam::getExportColumnSet(const QString ¶mName, const QSet &defaultValue) { QSet set; QStringList defaultValueList; for (int val : defaultValue) defaultValueList << QString::number(val); const QStringList sourceList = getParamStringList("exportadi/" + paramName, defaultValueList); for (const QString &s : sourceList) set.insert(s.toInt()); return set; } void LogParam::setExportColumnSet(const QString ¶mName, const QSet &set) { QStringList valueList; for (int val : set) valueList << QString::number(val); setParam("exportadi/" + paramName, valueList); } QByteArray LogParam::getLogbookState() { return QByteArray::fromBase64(getParam("logbook/maintablestate").toByteArray()); } void LogParam::setLogbookState(const QByteArray &state) { setParam("logbook/maintablestate", state.toBase64()); } int LogParam::getLogbookFilterSearchType(int defaultValue) { return getParam("logbook/filter/searchtype", defaultValue).toInt(); } void LogParam::setLogbookFilterSearchType(int type) { setParam("logbook/filter/searchtype", type); } QString LogParam::getLogbookFilterBand() { return getParam("logbook/filter/band").toString(); } void LogParam::setLogbookFilterBand(const QString &name) { setParam("logbook/filter/band", name); } QString LogParam::getLogbookFilterMode() { return getParam("logbook/filter/mode").toString(); } void LogParam::setLogbookFilterMode(const QString &name) { setParam("logbook/filter/mode", name); } QString LogParam::getLogbookFilterCountry() { return getParam("logbook/filter/country").toString(); } void LogParam::setLogbookFilterCountry(const QString &name) { setParam("logbook/filter/country", name); } QString LogParam::getLogbookFilterUserFilter() { return getParam("logbook/filter/userfilter").toString(); } void LogParam::setLogbookFilterUserFilter(const QString &name) { setParam("logbook/filter/userfilter", name); } QString LogParam::getLogbookFilterClub() { return getParam("logbook/filter/club").toString(); } void LogParam::setLogbookFilterClub(const QString &name) { setParam("logbook/filter/club", name); } QByteArray LogParam::getEncryptedPasswords() { return QByteArray::fromBase64(getParam("security/encryptedpasswords").toByteArray()); } void LogParam::setEncryptedPasswords(const QByteArray &data) { setParam("security/encryptedpasswords", data.toBase64()); } void LogParam::removeEncryptedPasswords() { removeParamGroup("security/encryptedpasswords"); } QString LogParam::getSourcePlatform() { return getParam("sourceplatform").toString(); } void LogParam::setSourcePlatform(const QString &platform) { setParam("sourceplatform", platform); } void LogParam::removeSourcePlatform() { removeParamGroup("sourceplatform"); } bool LogParam::getMainWindowAlertBeep() { return getParam("mainwindow/alertbeep", false).toBool(); } void LogParam::setMainWindowAlertBeep(bool state) { setParam("mainwindow/alertbeep", state); } int LogParam::getMainWindowDarkMode() { return getParam("mainwindow/darkmode", 0).toInt(); } void LogParam::setMainWindowDarkMode(int state) { setParam("mainwindow/darkmode", state); } QByteArray LogParam::getMainWindowGeometry() { return QByteArray::fromBase64(getParam("mainwindow/geometry").toByteArray()); } void LogParam::setMainWindowGeometry(const QByteArray &state) { setParam("mainwindow/geometry", state.toBase64()); } QByteArray LogParam::getMainWindowState() { return QByteArray::fromBase64(getParam("mainwindow/state").toByteArray()); } void LogParam::setMainWindowState(const QByteArray &state) { setParam("mainwindow/state", state.toBase64()); } QString LogParam::getMainWindowBandmapWidgets() { return getParam("mainwindow/bandmapwidgets").toString(); } void LogParam::setMainWindowBandmapWidgets(const QString &value) { setParam("mainwindow/bandmapwidgets", value); } void LogParam::removeMainWindowBandmapWidgets() { removeParamGroup("mainwindow/bandmapwidgets"); } int LogParam::getQslLabelTemplate() { return getParam("qsllabel/template", 0).toInt(); } void LogParam::setQslLabelTemplate(int index) { setParam("qsllabel/template", index); } QString LogParam::getQslLabelFooterLeft() { return getParam("qsllabel/footerleft", "").toString(); } void LogParam::setQslLabelFooterLeft(const QString &text) { setParam("qsllabel/footerleft", text); } QString LogParam::getQslLabelFooterRight() { return getParam("qsllabel/footerright", "TNX & 73").toString(); } void LogParam::setQslLabelFooterRight(const QString &text) { setParam("qsllabel/footerright", text); } int LogParam::getQslLabelSkip() { return getParam("qsllabel/skip", 0).toInt(); } void LogParam::setQslLabelSkip(int count) { setParam("qsllabel/skip", count); } int LogParam::getQslLabelZoom() { return getParam("qsllabel/zoom", 100).toInt(); } void LogParam::setQslLabelZoom(int zoom) { setParam("qsllabel/zoom", zoom); } int LogParam::getQslLabelCustomPageSize() { return getParam("qsllabel/custom_pagesize", 0).toInt(); } void LogParam::setQslLabelCustomPageSize(int pageSizeIndex) { setParam("qsllabel/custom_pagesize", pageSizeIndex); } int LogParam::getQslLabelCustomCols() { return getParam("qsllabel/custom_cols", 3).toInt(); } void LogParam::setQslLabelCustomCols(int cols) { setParam("qsllabel/custom_cols", cols); } int LogParam::getQslLabelCustomRows() { return getParam("qsllabel/custom_rows", 8).toInt(); } void LogParam::setQslLabelCustomRows(int rows) { setParam("qsllabel/custom_rows", rows); } double LogParam::getQslLabelCustomLabelWidth() { return getParam("qsllabel/custom_labelwidth", 70.0).toDouble(); } void LogParam::setQslLabelCustomLabelWidth(double width) { setParam("qsllabel/custom_labelwidth", width); } double LogParam::getQslLabelCustomLabelHeight() { return getParam("qsllabel/custom_labelheight", 35.0).toDouble(); } void LogParam::setQslLabelCustomLabelHeight(double height) { setParam("qsllabel/custom_labelheight", height); } double LogParam::getQslLabelCustomLeftMargin() { return getParam("qsllabel/custom_leftmargin", 0.0).toDouble(); } void LogParam::setQslLabelCustomLeftMargin(double margin) { setParam("qsllabel/custom_leftmargin", margin); } double LogParam::getQslLabelCustomTopMargin() { return getParam("qsllabel/custom_topmargin", 0.0).toDouble(); } void LogParam::setQslLabelCustomTopMargin(double margin) { setParam("qsllabel/custom_topmargin", margin); } double LogParam::getQslLabelCustomHSpacing() { return getParam("qsllabel/custom_hspacing", 0.0).toDouble(); } void LogParam::setQslLabelCustomHSpacing(double spacing) { setParam("qsllabel/custom_hspacing", spacing); } double LogParam::getQslLabelCustomVSpacing() { return getParam("qsllabel/custom_vspacing", 0.0).toDouble(); } void LogParam::setQslLabelCustomVSpacing(double spacing) { setParam("qsllabel/custom_vspacing", spacing); } bool LogParam::getQslLabelPrintBorders() { return getParam("qsllabel/print_borders", false).toBool(); } void LogParam::setQslLabelPrintBorders(bool enabled) { setParam("qsllabel/print_borders", enabled); } QString LogParam::getQslLabelDateFormat() { return getParam("qsllabel/date_format", "yyyy-MM-dd").toString(); } void LogParam::setQslLabelDateFormat(const QString &format) { setParam("qsllabel/date_format", format); } QString LogParam::getQslLabelSansFont() { return getParam("qsllabel/sans_font", QString()).toString(); } void LogParam::setQslLabelSansFont(const QString &family) { setParam("qsllabel/sans_font", family); } QString LogParam::getQslLabelMonoFont() { return getParam("qsllabel/mono_font", QString()).toString(); } void LogParam::setQslLabelMonoFont(const QString &family) { setParam("qsllabel/mono_font", family); } QString LogParam::getQslLabelExtraColumn() { return getParam("qsllabel/extra_column", QString()).toString(); } void LogParam::setQslLabelExtraColumn(const QString &column) { setParam("qsllabel/extra_column", column); } QString LogParam::getQslLabelExtraColumnHeader() { return getParam("qsllabel/extra_column_header", QString()).toString(); } void LogParam::setQslLabelExtraColumnHeader(const QString &header) { setParam("qsllabel/extra_column_header", header); } QString LogParam::getQslLabelToRadioText() { return getParam("qsllabel/to_radio_text", QString()).toString(); } void LogParam::setQslLabelToRadioText(const QString &text) { setParam("qsllabel/to_radio_text", text); } QString LogParam::getQslLabelHdrDate() { return getParam("qsllabel/hdr_date", QString()).toString(); } void LogParam::setQslLabelHdrDate(const QString &text) { setParam("qsllabel/hdr_date", text); } QString LogParam::getQslLabelHdrTime() { return getParam("qsllabel/hdr_time", QString()).toString(); } void LogParam::setQslLabelHdrTime(const QString &text) { setParam("qsllabel/hdr_time", text); } QString LogParam::getQslLabelHdrBand() { return getParam("qsllabel/hdr_band", QString()).toString(); } void LogParam::setQslLabelHdrBand(const QString &text) { setParam("qsllabel/hdr_band", text); } QString LogParam::getQslLabelHdrMode() { return getParam("qsllabel/hdr_mode", QString()).toString(); } void LogParam::setQslLabelHdrMode(const QString &text) { setParam("qsllabel/hdr_mode", text); } QString LogParam::getQslLabelHdrQsl() { return getParam("qsllabel/hdr_qsl", QString()).toString(); } void LogParam::setQslLabelHdrQsl(const QString &text) { setParam("qsllabel/hdr_qsl", text); } int LogParam::getQslLabelMaxRows() { return getParam("qsllabel/max_rows", 4).toInt(); } void LogParam::setQslLabelMaxRows(int rows) { setParam("qsllabel/max_rows", rows); } double LogParam::getQslLabelFontSizeToRadio() { return getParam("qsllabel/font_size_to_radio", 7.5).toDouble(); } void LogParam::setQslLabelFontSizeToRadio(double size) { setParam("qsllabel/font_size_to_radio", size); } double LogParam::getQslLabelFontSizeCallsign() { return getParam("qsllabel/font_size_callsign", 14.0).toDouble(); } void LogParam::setQslLabelFontSizeCallsign(double size) { setParam("qsllabel/font_size_callsign", size); } double LogParam::getQslLabelFontSizeHeader() { return getParam("qsllabel/font_size_header", 7.0).toDouble(); } void LogParam::setQslLabelFontSizeHeader(double size) { setParam("qsllabel/font_size_header", size); } double LogParam::getQslLabelFontSizeData() { return getParam("qsllabel/font_size_data", 8.0).toDouble(); } void LogParam::setQslLabelFontSizeData(double size) { setParam("qsllabel/font_size_data", size); } bool LogParam::setParam(const QString &name, const QVariant &value) { FCT_IDENTIFICATION; qCDebug(function_parameters) << name << value; QSqlQuery query; if ( ! query.prepare("INSERT OR REPLACE INTO log_param (name, value) " "VALUES (:nam, :val)") ) { qWarning()<< "Cannot prepare insert parameter statement for parameter" << name; return false; } query.bindValue(":nam", name); query.bindValue(":val", value); PARAMMUTEXLOCKER; if ( !query.exec() ) { qWarning() << "SET - Cannot exec an insert parameter statement for" << name; return false; } localCache.remove(name); qCDebug(runtime) << "SET:" << name << "value" << value; return true; } QVariant LogParam::getParam(const QString &name, const QVariant &defaultValue) { FCT_IDENTIFICATION; qCDebug(function_parameters) << name; PARAMMUTEXLOCKER; const QVariant *valueCached = localCache.object(name); if ( valueCached ) { qCDebug(runtime) << "GET:" << name << "Cached value: " << *valueCached; return *valueCached; } QSqlQuery query; if ( ! query.prepare("SELECT value FROM log_param WHERE name = :nam") ) { qWarning()<< "GET - Cannot prepare select parameter statement for" << name; return defaultValue; } query.bindValue(":nam", name); if ( ! query.exec() ) { qWarning() << "GET - Cannot execute GetParam Select for" << name << "using default" << defaultValue; return defaultValue; } if ( query.first() ) { if ( !query.isNull(0) ) { QVariant dbValue = query.value(0); localCache.insert(name, new QVariant(dbValue)); qCDebug(runtime) << "GET:" << name << "DB value: " << dbValue; return dbValue; } qCDebug(runtime) << "GET:" << name << "NULL value in DB - using default " << defaultValue; } else qCDebug(runtime) << "GET:" << name << "Key not found in DB - using default " << defaultValue; //localCache.insert(name, new QVariant(defaultValue)); // Do not insert default value here because // in some use cases — for example, DXC and station_profile // the profile is not yet accessible during the first call, // and an empty value is incorrectly inserted into the cache. return defaultValue; } bool LogParam::setParam(const QString &name, const QStringList &value) { FCT_IDENTIFICATION; return setParam(name, serializeStringList(value)); } QStringList LogParam::getParamStringList(const QString &name, const QStringList &defaultValue) { FCT_IDENTIFICATION; return deserializeStringList(getParam(name, serializeStringList(defaultValue)).toString()); } void LogParam::removeParamGroup(const QString ¶mGroup) { FCT_IDENTIFICATION; qCDebug(function_parameters) << paramGroup; PARAMMUTEXLOCKER; const QStringList &keys = localCache.keys(); for ( const QString& key : keys ) if (key.startsWith(paramGroup)) localCache.remove(key); QSqlQuery query; if ( ! query.prepare("DELETE FROM log_param WHERE name LIKE :group ") ) { qWarning()<< "Cannot prepare delete parameter statement for" << paramGroup; return; } query.bindValue(":group", paramGroup + "%"); if ( ! query.exec() ) qWarning() << "Cannot execute removeParamGroup statement for" << paramGroup; return; } QStringList LogParam::getKeys(const QString &group) { FCT_IDENTIFICATION; qCDebug(function_parameters) << group; PARAMMUTEXLOCKER; QSet keys; // unique values; QSqlQuery query; if ( ! query.prepare("SELECT name FROM log_param WHERE name LIKE :group ") ) { qWarning()<< "Cannot prepare select parameter statement for" << group << query.lastError().text(); return QStringList(); } query.bindValue(":group", group + "%"); if ( ! query.exec() ) { qWarning() << "Cannot execute removeParamGroup statement for" << group; return QStringList(); } while ( query.next() ) { const QString ¶m = query.value(0).toString(); const QString &subKey = param.mid(group.length()).section("/", 0, 0); if ( !subKey.isEmpty() ) keys.insert(subKey); } #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) return QStringList(keys.begin(), keys.end()); #else return keys.toList(); #endif } QString LogParam::escapeString(const QString &input, QChar escapeChar, QChar delimiter) { QString result; for (QChar ch : input) { if (ch == escapeChar || ch == delimiter) result += escapeChar; result += ch; } return result; } QString LogParam::unescapeString(const QString &input, QChar escapeChar) { QString result; bool escaping = false; for ( QChar ch : input ) { if ( escaping ) { result += ch; escaping = false; } else if ( ch == escapeChar ) escaping = true; else result += ch; } return result; } QString LogParam::serializeStringList(const QStringList &list, QChar delimiter, QChar escapeChar) { QStringList escapedList; for ( const QString &item : list ) escapedList << escapeString(item, escapeChar, delimiter); return escapedList.join(delimiter); } QStringList LogParam::deserializeStringList(const QString &input, QChar delimiter, QChar escapeChar) { QStringList result; if ( !input.isEmpty() ) { QString current; bool escaping = false; for ( QChar ch : input ) { if ( escaping ) { current += ch; escaping = false; } else if ( ch == escapeChar ) escaping = true; else if ( ch == delimiter ) { result << current; current.clear(); } else current += ch; } result << current; } return result; } QCache LogParam::localCache(300); QMutex LogParam::cacheMutex; #undef PARAMMUTEXLOCKER ================================================ FILE: core/LogParam.h ================================================ #ifndef QLOG_CORE_LOGPARAM_H #define QLOG_CORE_LOGPARAM_H #include #include #include #include class LogParam : public QObject { Q_OBJECT public: explicit LogParam(QObject *parent = nullptr); /********* * LOV ********/ static bool setLOVParam(const QString &LOVName, const QVariant &value); static QDate getLOVaParam(const QString &LOVName); /********* * Backup ********/ static bool setLastBackupDate(const QDate date); static QDate getLastBackupDate(); /********* * LogID ********/ static bool setLogID(const QString &id); static QString getLogID(); /********* * Contest ********/ static bool setContestSeqnoType(const QVariant &data); static int getContestSeqnoType(); static bool setContestManuDupeType(const QVariant &data); static int getContestDupeType(); static bool setContestLinkExchange(const QVariant &data); static int getContestLinkExchange(); static bool setContestFilter(const QString &filterName); static QString getContestFilter(); static bool setContestID(const QString &contestID); static QString getContestID(); static bool setContestDupeDate(const QDateTime date); static QDateTime getContestDupeDate(); static void removeConetstDupeDate(); /******************** * DXCC Confirmation ********************/ static bool getDxccConfirmedByLotwState(); static bool setDxccConfirmedByLotwState(bool state); static bool setDxccConfirmedByPaperState(bool state); static bool getDxccConfirmedByPaperState(); static bool setDxccConfirmedByEqslState(bool state); static bool getDxccConfirmedByEqslState(); static int getContestSeqno(const QString &band = QString()); static bool setContestSeqno(int value, const QString &band = QString()); static void removeContestSeqno(); /********* * Bandmap *********/ static QStringList bandmapsWidgets(); static void removeBandmapWidgetGroup(const QString &group); static double getBandmapScrollFreq(const QString& widgetID, const QString &bandName); static bool setBandmapScrollFreq(const QString& widgetID, const QString &bandName, double scroll); static QVariant getBandmapZoom(const QString& widgetID, const QString &bandName, const QVariant &defaultValue); static bool setBandmapZoom(const QString& widgetID, const QString &bandName, const QVariant &zoom); static bool setBandmapAging(const QString& widgetID, int aging); static int getBandmapAging(const QString& widgetID); static bool setBandmapCenterRX(const QString& widgetID, bool centerRX); static bool getBandmapCenterRX(const QString& widgetID); static bool setBandmapShowEmergency(const QString& widgetID, bool show); static bool getBandmapShowEmergency(const QString& widgetID); /******************* * UploadQSO Dialog *******************/ static QString getUploadQSOLastCall(); static void setUploadQSOLastCall(const QString &call); static bool getUploadeqslQSLComment(); static void setUploadeqslQSLComment(const bool state); static bool getUploadeqslQSLMessage(); static QString getUploadeqslQTHProfile(); static void setUploadeqslQTHProfile(const QString &qthProfile); static void setUploadeqslQSLMessage(const bool state); static bool getUploadServiceState(const QString& name); static void setUploadServiceState(const QString& name, bool state); static int getUploadQSOFilterType(); static void setUploadQSOFilterType(int filterID); static QString getUploadLoTWLocation(); static void setUploadLoTWLocation(const QString &location); /********************* * DownloadQSL Dialog *********************/ static bool getDownloadQSLServiceState(const QString& name); static void setDownloadQSLServiceState(const QString& name, bool state); static QDate getDownloadQSLServiceLastDate(const QString& name); static void setDownloadQSLServiceLastDate(const QString& name, const QDate &date); static bool getDownloadQSLServiceLastQSOQSL(const QString& name); static void setDownloadQSLServiceLastQSOQSL(const QString& name, bool state); static QString getDownloadQSLLoTWLastCall(); static void setDownloadQSLLoTWLastCall(const QString &call); static QString getDownloadQSLeQSLLastProfile(); static void setDownloadQSLeQSLLastProfile(const QString &profile); /********* * QRZ ********/ static QString getQRZCOMCallbookUsername(); static void setQRZCOMCallbookUsername(const QString& username); static QStringList getQRZCOMAPICallsignsList(); static void setQRZCOMAPICallsignsList(const QStringList &list); /********** * Cloudlog **********/ static QString getCloudlogAPIEndpoint(); static void setCloudlogAPIEndpoint(const QString &endpoint); static uint getCloudlogStationID(); static void setCloudlogStationID(uint stationID); /********* * Clublog ********/ static QString getClublogLogbookReqEmail(); static void setClublogLogbookReqEmail(const QString& email); static bool getClublogUploadImmediatelyEnabled(); static void setClublogUploadImmediatelyEnabled(bool state); /********* * eQSL ********/ static QString getEQSLLogbookUsername(); static void setEQSLLogbookUsername(const QString& username); /********* * HamQTH ********/ static QString getHamQTHCallbookUsername(); static void setHamQTHCallbookUsername(const QString& username); /********* * HRDLog ********/ static QString getHRDLogLogbookReqCallsign(); static void setHRDLogLogbookReqCallsign(const QString& callsign); static bool getHRDLogOnAir(); static void setHRDLogOnAir(bool state); /********* * KSTChat ********/ static QString getKSTChatUsername(); static void setKSTChatUsername(const QString& username); /********* * LoTW ********/ static QString getLoTWCallbookUsername(); static void setLoTWCallbookUsername(const QString& username); static QString getLoTWTQSLPath(const QString &defaultPath); static void setLoTWTQSLPath(const QString& path); static bool isLoTWTQSLPathKey(const QString &key); /******************* * Callbook setting *******************/ static QString getPrimaryCallbook(const QString &defaultValue); static void setPrimaryCallbook(const QString& callbookName); static QString getSecondaryCallbook(const QString &defaultValue); static void setSecondaryCallbook(const QString& callbookName); static QString getCallbookWebLookupURL(const QString &defaultURL); static void setCallbookWebLookupURL(const QString& url); /************************ * Network Notifications ************************/ static QString getNetworkNotifLogQSOAddrs(); static void setNetworkNotifLogQSOAddrs(const QString &addrs); static QString getNetworkNotifDXCSpotAddrs(); static void setNetworkNotifDXCSpotAddrs(const QString &addrs); static QString getNetworkNotifWSJTXCQSpotAddrs(); static void setNetworkNotifWSJTXCQSpotAddrs(const QString &addrs); static QString getNetworkNotifAlertsSpotAddrs(); static void setNetworkNotifAlertsSpotAddrs(const QString &addrs); static QString getNetworkNotifRigStateAddrs(); static void setNetworkNotifRigStateAddrs(const QString &addrs); static int getNetworkWsjtxListenerPort(int defaultPort); static void setNetworkNotifRigStateAddrs(int port); static QString getNetworkWsjtxForwardAddrs(); static void setNetworkWsjtxForwardAddrs(const QString &addrs); static bool getNetworkWsjtxListenerJoinMulticast(); static void setNetworkWsjtxListenerJoinMulticast(bool state); static QString getNetworkWsjtxListenerMulticastAddr(); static void setNetworkWsjtxListenerMulticastAddr(const QString &addr); static int getNetworkWsjtxListenerMulticastTTL(); static void setNetworkWsjtxListenerMulticastTTL(int ttl); /******************** * Club Member Lists ********************/ static QStringList getEnabledMemberlists(); static void setEnabledMemberlists(const QStringList &list); static int getAlertAging(); static void setAlertAging(int aging); static QByteArray getAlertWidgetState(); /*************** * Alert Dialog ***************/ static void setAlertWidgetState(const QByteArray &state); /************* * CW Console *************/ static bool getCWConsoleSendWord(); static void setCWConsoleSendWord(bool state); /********* * Chat ********/ static bool getChatSelectedRoom(); static void setChatSelectedRoom(int room); /************* * NewContact *************/ static double getNewContactFreq(); static void setNewContactFreq(double freq); static QString getNewContactMode(); static void setNewContactMode(const QString &mode); static QString getNewContactSubMode(); static void setNewContactSubMode(const QString &submode); static double getNewContactPower(); static void setNewContactPower(double power); static int getNewContactTabIndex(); static void setNewContactTabIndex(int index); static QString getNewContactQSLSent(); static void setNewContactQSLSent(const QString &qslsent); static QString getNewContactLoTWQSLSent(); static void setNewContactLoTWQSLSent(const QString &qslsent); static QString getNewContactEQSLWQSLSent(); static void setNewContactEQSLQSLSent(const QString &qslsent); static QString getNewContactQSLVia(); static void setNewContactQSLVia(const QString &qslvia); static QString getNewContactPropMode(); static void setNewContactPropMode(const QString &propmode); static bool getNewContactTabsExpanded(); static void setNewContactTabsExpanded(bool state); static QString getNewContactSatName(); static void setNewContactSatName(const QString &name); /************* * Online Map *************/ static QStringList getMapLayerStates(const QString &widgetID); static bool getMapLayerState(const QString &widgetID, const QString &layerName); static void setMapLayerState(const QString &widgetID, const QString &layerName, bool state); /*************** * WSJTX Dialog ***************/ static uint getWsjtxFilterDxccStatus(); static void setWsjtxFilterDxccStatus(uint mask); static QString getWsjtxFilterContRE(); static void setWsjtxFilterContRE(const QString &re); static int getWsjtxFilterDistance(); static void setWsjtxFilterDistance(int dist); static int getWsjtxFilterSNR(); static void setWsjtxFilterSNR(int snr); static QStringList getWsjtxMemberlists(); static void setWsjtxMemberlists(const QStringList &list); static QByteArray getWsjtxWidgetState(); static void setWsjtxWidgetState(const QByteArray &state); static bool getWsjtxOutputColorCQSpot(); static void setWsjtxOutputColorCQSpot(bool state); /****************** * DXCluster Dialog ******************/ static uint getDXCFilterDxccStatus(); static void setDXCFilterDxccStatus(uint mask); static QString getDXCFilterContRE(); static void setDXCFilterContRE(const QString &re); static QString getDXCFilterSpotterContRE(); static void setDXCFilterSpotterContRE(const QString &re); static bool getDXCFilterDedup(); static void setDXCFilterDedup(bool state); static int getDXCFilterDedupTime(int defaultValue); static void setDXCFilterDedupTime(int value); static int getDXCFilterDedupFreq(int defaultValue); static void setDXCFilterDedupFreq(int value); static QStringList getDXCFilterMemberlists(); static void setDXCFilterMemberlists(const QStringList &list); static bool getDXCAutoconnectServer(); static void setDXCAutoconnectServer(bool state); static bool getDXCKeepQSOs(); static void setDXCKeepQSOs(bool state); static QByteArray getDXCDXTableState(); static void setDXCDXTableState(const QByteArray &state); static QByteArray getDXCWCYTableState(); static void setDXCWCYTableState(const QByteArray &state); static QByteArray getDXCWWVTableState(); static void setDXCWWVTableState(const QByteArray &state); static QByteArray getDXCTOALLTableState(); static void setDXCTOALLTableState(const QByteArray &state); static int getDXCConsoleFontSize(); static void setDXCConsoleFontSize(int value); static QStringList getDXCServerlist(); static void setDXCServerlist(const QStringList &list); static QString getDXCLastServer(); static void setDXCLastServer(const QString &server); static QString getDXCFilterModeRE(); static void setDXCFilterModeRE(const QString &re); static QStringList getDXCExcludedBands(); static void setDXCExcludedBands(const QStringList &excluded); static bool setDXCTrendContinent(const QString &cont); static QString getDXCTrendContinent(const QString &def); static void removeDXCTrendContinent(); /************** * Export ADIF **************/ static QSet getExportColumnSet(const QString ¶mName, const QSet &defaultValue); static void setExportColumnSet(const QString ¶mName, const QSet &set); /***************** * Logbook dialog *****************/ static QByteArray getLogbookState(); static void setLogbookState(const QByteArray &state); static int getLogbookFilterSearchType(int defaultValue); static void setLogbookFilterSearchType(int type); static QString getLogbookFilterBand(); static void setLogbookFilterBand(const QString &name); static QString getLogbookFilterMode(); static void setLogbookFilterMode(const QString &name); static QString getLogbookFilterCountry(); static void setLogbookFilterCountry(const QString &name); static QString getLogbookFilterUserFilter(); static void setLogbookFilterUserFilter(const QString &name); static QString getLogbookFilterClub(); static void setLogbookFilterClub(const QString &name); /************************ * Encrypted Passwords ************************/ static QByteArray getEncryptedPasswords(); static void setEncryptedPasswords(const QByteArray &data); static void removeEncryptedPasswords(); static QString getSourcePlatform(); static void setSourcePlatform(const QString &platform); static void removeSourcePlatform(); /************** * Main Window *************/ static bool getMainWindowAlertBeep(); static void setMainWindowAlertBeep(bool state); static int getMainWindowDarkMode(); static void setMainWindowDarkMode(int state); static QByteArray getMainWindowGeometry(); static void setMainWindowGeometry(const QByteArray &state); static QByteArray getMainWindowState(); static void setMainWindowState(const QByteArray &state); static QString getMainWindowBandmapWidgets(); static void setMainWindowBandmapWidgets(const QString &value); static void removeMainWindowBandmapWidgets(); /********************* * QSL Print Labels *********************/ static int getQslLabelTemplate(); static void setQslLabelTemplate(int index); static QString getQslLabelFooterLeft(); static void setQslLabelFooterLeft(const QString &text); static QString getQslLabelFooterRight(); static void setQslLabelFooterRight(const QString &text); static int getQslLabelSkip(); static void setQslLabelSkip(int count); static int getQslLabelZoom(); static void setQslLabelZoom(int zoom); static int getQslLabelCustomPageSize(); static void setQslLabelCustomPageSize(int pageSizeIndex); static int getQslLabelCustomCols(); static void setQslLabelCustomCols(int cols); static int getQslLabelCustomRows(); static void setQslLabelCustomRows(int rows); static double getQslLabelCustomLabelWidth(); static void setQslLabelCustomLabelWidth(double width); static double getQslLabelCustomLabelHeight(); static void setQslLabelCustomLabelHeight(double height); static double getQslLabelCustomLeftMargin(); static void setQslLabelCustomLeftMargin(double margin); static double getQslLabelCustomTopMargin(); static void setQslLabelCustomTopMargin(double margin); static double getQslLabelCustomHSpacing(); static void setQslLabelCustomHSpacing(double spacing); static double getQslLabelCustomVSpacing(); static void setQslLabelCustomVSpacing(double spacing); static bool getQslLabelPrintBorders(); static void setQslLabelPrintBorders(bool enabled); static QString getQslLabelDateFormat(); static void setQslLabelDateFormat(const QString &format); static QString getQslLabelSansFont(); static void setQslLabelSansFont(const QString &family); static QString getQslLabelMonoFont(); static void setQslLabelMonoFont(const QString &family); static QString getQslLabelExtraColumn(); static void setQslLabelExtraColumn(const QString &column); static QString getQslLabelExtraColumnHeader(); static void setQslLabelExtraColumnHeader(const QString &header); static QString getQslLabelToRadioText(); static void setQslLabelToRadioText(const QString &text); static QString getQslLabelHdrDate(); static void setQslLabelHdrDate(const QString &text); static QString getQslLabelHdrTime(); static void setQslLabelHdrTime(const QString &text); static QString getQslLabelHdrBand(); static void setQslLabelHdrBand(const QString &text); static QString getQslLabelHdrMode(); static void setQslLabelHdrMode(const QString &text); static QString getQslLabelHdrQsl(); static void setQslLabelHdrQsl(const QString &text); static int getQslLabelMaxRows(); static void setQslLabelMaxRows(int rows); static double getQslLabelFontSizeToRadio(); static void setQslLabelFontSizeToRadio(double size); static double getQslLabelFontSizeCallsign(); static void setQslLabelFontSizeCallsign(double size); static double getQslLabelFontSizeHeader(); static void setQslLabelFontSizeHeader(double size); static double getQslLabelFontSizeData(); static void setQslLabelFontSizeData(double size); private: static QCache localCache; static QMutex cacheMutex; static bool setParam(const QString&, const QVariant &); static bool setParam(const QString&, const QStringList &); static QVariant getParam(const QString&, const QVariant &defaultValue = QVariant()); static QStringList getParamStringList(const QString&, const QStringList &defaultValue = QStringList()); static void removeParamGroup(const QString&); static QStringList getKeys(const QString &); static QString escapeString(const QString &input, QChar escapeChar = '\\', QChar delimiter = ','); static QString unescapeString(const QString &input, QChar escapeChar = '\\'); static QString serializeStringList(const QStringList &list, QChar delimiter = ',', QChar escapeChar = '\\'); static QStringList deserializeStringList(const QString &input, QChar delimiter = ',', QChar escapeChar = '\\'); }; #endif // QLOG_CORE_LOGPARAM_H ================================================ FILE: core/MembershipQE.cpp ================================================ #include #include #include #include #include #include #include #include #include #include #include #include "MembershipQE.h" #include "core/debug.h" #include "core/LogDatabase.h" #include "data/Callsign.h" #include "LogParam.h" MODULE_IDENTIFICATION("qlog.core.membershipqe"); #define MEMBERLIST_BASE_URL "https://raw.githubusercontent.com/foldynl/hamradio-membeship-lists/main/lists" ClubInfo::ClubInfo(const QString &callsign, const QString &ID, const QDate &validFrom, const QDate &validTo, const QString &club): callsign(callsign), id(ID), validFrom(validFrom), validTo(validTo), club(club) { FCT_IDENTIFICATION; } const QString& ClubInfo::getCallsign() const { return callsign; } const QString& ClubInfo::getID() const { return id; } const QDate& ClubInfo::getValidFrom() const { return validFrom; } const QDate& ClubInfo::getValidTo() const { return validTo; } const QString& ClubInfo::getClubInfo() const { return club; } MembershipQE::MembershipQE(QObject *parent) : QObject{parent}, idClubQueryValid(false), nam(new QNetworkAccessManager(this)) { FCT_IDENTIFICATION; // this thead will help to obtain club status statusQuery.moveToThread(&statusQueryThread); statusQueryThread.start(); connect(&statusQuery, &ClubStatusQuery::status, this, &MembershipQE::statusQueryFinished); connect(nam.data(), &QNetworkAccessManager::finished, this, &MembershipQE::onFinishedListDownload); // prepare SQL query to increase Club query function performance idClubQueryValid = clubQuery.prepare("SELECT DISTINCT callsign, member_id, valid_from, valid_to, clubid FROM membership WHERE callsign = :callsign ORDER BY clubid"); } MembershipQE::~MembershipQE() { FCT_IDENTIFICATION; statusQueryThread.quit(); statusQueryThread.wait(); } // this function is called when async club status returns a result void MembershipQE::statusQueryFinished(const QString &callsign, QMap statuses) { FCT_IDENTIFICATION; emit clubStatusResult(callsign, statuses); } void MembershipQE::saveEnabledClubLists(const QStringList &enabledLists) { FCT_IDENTIFICATION; LogParam::setEnabledMemberlists(enabledLists); } QStringList MembershipQE::getEnabledClubLists() { FCT_IDENTIFICATION; return LogParam::getEnabledMemberlists(); } void MembershipQE::removeClubsFromEnabledClubLists(const QList> &toRemove) { FCT_IDENTIFICATION; qCDebug(runtime) << toRemove; QStringList current = getEnabledClubLists(); for ( const QPair& toRemoveClub : toRemove ) { current.removeAll(toRemoveClub.first); } saveEnabledClubLists(current); } // it is a sync in-thread function to obtain all clubs for an input callsign // it must be as fast as possible QList MembershipQE::query(const QString &in_callsign) { FCT_IDENTIFICATION; qCDebug(function_parameters) << in_callsign; QList ret; if ( !idClubQueryValid ) { qCDebug(runtime) << "Query is not prepared"; return ret; } Callsign qCall(in_callsign); clubQuery.bindValue(":callsign",( qCall.isValid() ) ? qCall.getBase() : in_callsign.toUpper()); if ( ! clubQuery.exec() ) { qCDebug(runtime) << "Cannot query callsign clubs "<< clubQuery.lastError().text(); return ret; } while ( clubQuery.next() ) { QString callsign = clubQuery.value(0).toString(); QString memberid = clubQuery.value(1).toString(); QDate validFrom = QDate::fromString(clubQuery.value(2).toString(), "yyyyMMdd"); QDate validTo = QDate::fromString(clubQuery.value(3).toString(), "yyyyMMdd"); QString clubid = clubQuery.value(4).toString(); qCDebug(runtime) << "Found membership record" << callsign << memberid << validFrom << validTo << clubid; ret << ClubInfo(in_callsign, memberid, validTo, validTo, clubid); } qCDebug(runtime) << "Done"; return ret; } // it is a async query function to obtain club statuses for an input callsign. // the result is returned via signal MembershipStatusQuery::status // it can take some time to obtain a result therefore it is solved in an isolated thread void MembershipQE::asyncQueryDetails(const QString &callsign, const QString &band, const QString &mode) { FCT_IDENTIFICATION; qCDebug(function_parameters) << callsign << band << mode; QMetaObject::invokeMethod(&statusQuery, "getClubStatus", Qt::QueuedConnection, Q_ARG(QString, callsign.toUpper()), Q_ARG(QString, band), Q_ARG(QString, mode), Q_ARG(bool, LogParam::getDxccConfirmedByLotwState()), Q_ARG(bool, LogParam::getDxccConfirmedByPaperState()), Q_ARG(bool, LogParam::getDxccConfirmedByEqslState()) ); } void MembershipQE::updateLists() { FCT_IDENTIFICATION; if ( updatePlan.size() > 0 ) { qWarning() << "Member Club lists are still downloaded. Aborting this update request."; } QStringList enabledLists = getEnabledClubLists(); if ( !removeDisabled(enabledLists) ) { QMessageBox::warning(nullptr, QMessageBox::tr("QLog Warning"), QMessageBox::tr("Club List Update failed. Cannot remove old records")); return; } if ( !planDownloads(enabledLists) ) { QMessageBox::warning(nullptr, QMessageBox::tr("QLog Warning"), QMessageBox::tr("Club List Update failed. Cannot plan new downloads")); return; } startPlannedDownload(); } bool MembershipQE::removeDisabled(const QStringList &enabledLists) { FCT_IDENTIFICATION; QSqlQuery query; if ( ! query.exec(QString("DELETE FROM membership WHERE clubid NOT IN ('%1');").arg(enabledLists.join("','")))) { qWarning() << "Cannot delete records "<< query.lastError().text(); Q_UNUSED(QSqlDatabase::database().rollback()); return false; } if ( ! query.exec(QString("DELETE FROM membership_versions WHERE clubid NOT IN (SELECT DISTINCT clubid FROM membership);"))) { qWarning() << "Cannot delete records from versioning"<< query.lastError().text(); Q_UNUSED(QSqlDatabase::database().rollback()); return false; } qCDebug(runtime) << "DONE"; Q_UNUSED(QSqlDatabase::database().commit()); return true; } bool MembershipQE::planDownloads(const QStringList &enabledLists) { FCT_IDENTIFICATION; QSqlQuery query; if ( ! query.exec(QString("WITH split(word, csv) AS ( SELECT '', '%1'||',' " " UNION ALL " " SELECT substr(csv, 0, instr(csv, ',')), substr(csv, instr(csv, ',') + 1) " " FROM split " " WHERE csv != '' ) " "SELECT clubid, filename " "FROM membership_directory d, " " (SELECT word clubid FROM split " " WHERE word!='' " " EXCEPT " " SELECT clubid " " FROM membership_versions l , " " membership_directory m " " WHERE (l.clubid = m.short_desc) " " AND (version >= m.last_update or m.last_update IS NULL)) a " "WHERE a.clubid = d.short_desc;").arg(enabledLists.join(",")))) { qCWarning(runtime) << "Cannot plan download" << query.lastError().text(); return false; } while ( query.next() ) { updatePlan << QPair(query.value(0).toString(),QString(MEMBERLIST_BASE_URL) + "/" + query.value(1).toString()); } qCDebug(runtime) << "DONE"; return true; } void MembershipQE::startPlannedDownload() { FCT_IDENTIFICATION; if ( updatePlan.size() == 0 ) return; qCDebug(runtime) << "Remaining downloads" << updatePlan.size() << updatePlan; // to prevent network overload, qlog will donwload files one-by-one QPair nextDownload = updatePlan.at(0); QNetworkRequest request(nextDownload.second); QString rheader = QString("QLog/%1").arg(VERSION); request.setRawHeader("User-Agent", rheader.toUtf8()); QNetworkReply *reply = nam->get(request); reply->setProperty("clubid", nextDownload.first); } void MembershipQE::onFinishedListDownload(QNetworkReply *reply) { FCT_IDENTIFICATION; QString clubid = reply->property("clubid").toString(); qCDebug(runtime) << "Received data for Club ID" << clubid << "from" << reply->url().toString(); if ( updatePlan.size() == 0 ) { // Update plan was canceled qCDebug(runtime) << "Download finished for already canceled download - ignore it"; return; } if ( updatePlan.at(0).first != clubid) { qCDebug(runtime) << "Received" << clubid << "but expected" << updatePlan.at(0).first; qCDebug(runtime) << "Canceling all downloads"; removeClubsFromEnabledClubLists(updatePlan); updatePlan.clear(); QMessageBox::critical(nullptr, QMessageBox::tr("QLog Critical"), QMessageBox::tr("Unexpected Club List download. Canceling next downloads")); } int replyStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); bool error = false; if ( reply->isFinished() && reply->error() == QNetworkReply::NoError && replyStatusCode >= 200 && replyStatusCode < 300) { QByteArray data = reply->readAll(); if ( ! importData(clubid, data) ) { QMessageBox::warning(nullptr, QMessageBox::tr("QLog Warning"), QMessageBox::tr("Unexpected Club List content for") + " " + clubid); error = true; } } else { QMessageBox::warning(nullptr, QMessageBox::tr("QLog Warning"), QMessageBox::tr("Network error. Cannot download Club List for") + " " + clubid); qCDebug(runtime) << "Network Error for club" << clubid << replyStatusCode << reply->error(); error = true; } if ( error ) { QList> tmp; tmp << QPair(clubid, ""); removeClubsFromEnabledClubLists(tmp); } if ( updatePlan.size() > 0 ) updatePlan.removeFirst(); reply->deleteLater(); startPlannedDownload(); } bool MembershipQE::importData(const QString &clubid, const QByteArray &data) { FCT_IDENTIFICATION; qCDebug(function_parameters) << clubid; QTextStream stream(data); Q_UNUSED(QSqlDatabase::database().transaction()); QSqlQuery query; QSqlQuery versionQuery; qCDebug(runtime) << "Delete Records for a club" << clubid; if ( ! query.exec(QString("DELETE FROM membership WHERE clubid = '%1';").arg(clubid))) { qWarning() << "Cannot delete records for " << clubid << query.lastError().text(); Q_UNUSED(QSqlDatabase::database().rollback()); return false; } QCoreApplication::processEvents(); qCDebug(runtime) << "Insert data into" << clubid; if ( ! query.prepare(QString("INSERT INTO membership(callsign," " member_id," " valid_from," " valid_to," " clubid" ")" " VALUES (:callsign," " :member_id," " :valid_from," " :valid_to," " '%1'" ")").arg(clubid)) ) { qWarning() << "Cannot prepare Insert statement for membership table" << query.lastError().text(); Q_UNUSED(QSqlDatabase::database().rollback()); return false; } if ( ! versionQuery.prepare(QString("REPLACE INTO membership_versions(clubid, version) " " VALUES (:clubid, :version)") ) ) { qWarning() << "Cannot prepare Insert statement for membership_version table" << versionQuery.lastError().text(); Q_UNUSED(QSqlDatabase::database().rollback()); return false; } QCoreApplication::processEvents(); QString line = stream.readLine(); QStringList fields = line.split(" "); int version; if ( fields.size() == 2 && fields.at(0).at(0) == QChar('#') && (version = fields.at(1).toInt()) != 0) { versionQuery.bindValue(":clubid", clubid); versionQuery.bindValue(":version", version); if ( ! versionQuery.exec() ) { qWarning() << "Membership version insert error " << versionQuery.lastError().text(); Q_UNUSED(QSqlDatabase::database().rollback()); return false; } } else { qCDebug(runtime) << "Unexpected header" << line; Q_UNUSED(QSqlDatabase::database().rollback()); return false; } // skip CSV header stream.readLine(); while ( !stream.atEnd() ) { line = stream.readLine(); fields = line.split(','); if ( fields.count() < 4 ) { qInfo(runtime) << "Invalid line in the input file " << line << clubid; continue; } query.bindValue(":callsign", fields.at(0).toUpper().simplified()); query.bindValue(":member_id", fields.at(1).simplified()); query.bindValue(":valid_from", fields.at(2)); query.bindValue(":valid_to", fields.at(3)); if ( ! query.exec() ) { qWarning() << "membership insert error " << query.lastError().text(); continue; } } qCDebug(runtime) << "DONE"; Q_UNUSED(QSqlDatabase::database().commit()); return true; } ClubStatusQuery::ClubStatusQuery(QObject *parent) : QObject(parent), dbConnectionName("queryThread"), dbConnected(false) { FCT_IDENTIFICATION; } ClubStatusQuery::~ClubStatusQuery() { FCT_IDENTIFICATION; } void ClubStatusQuery::getClubStatus(const QString &in_callsign, const QString &in_band, const QString &in_mode, bool lowtConfirmed, bool paperConfirmed, bool eqslConfirmed) { FCT_IDENTIFICATION; qCDebug(function_parameters) << in_callsign << in_band << in_mode; qCDebug(runtime) << "Waiting for lock"; QMutexLocker locker(&dbLock); qCDebug(runtime) << "Processing getClubStatus"; if ( !dbConnected ) { qCDebug(runtime) << "Opening connection to DB"; QSqlDatabase db1 = QSqlDatabase::addDatabase("QSQLITE", dbConnectionName); db1.setDatabaseName(LogDatabase::dbFilename()); dbConnected = db1.open(); if ( ! dbConnected) { qWarning() << "Cannot open DB Connection for Update"; emit status(in_callsign, QMap()); return; } } QSqlDatabase db1 = QSqlDatabase::database(dbConnectionName); QSqlQuery query(db1); const Callsign qCall(in_callsign); const QString &callModified = ( qCall.isValid() ) ? qCall.getBase() : in_callsign; QStringList dxccConfirmedByCond(QLatin1String("0=1")); // if no option is selected then always false if ( lowtConfirmed ) dxccConfirmedByCond << QLatin1String("c.lotw_qsl_rcvd = 'Y'"); if ( paperConfirmed ) dxccConfirmedByCond << QLatin1String("c.qsl_rcvd = 'Y'"); if ( eqslConfirmed ) dxccConfirmedByCond << QLatin1String("c.eqsl_qsl_rcvd = 'Y'"); if ( ! query.exec(QString("SELECT DISTINCT clubid, NULL band, NULL mode, " " NULL confirmed, NULL current_mode, member_id " "FROM membership WHERE callsign = '%1' " "UNION ALL " "SELECT DISTINCT clubid, c.band, o.dxcc mode, " " CASE WHEN (%2) THEN 1 ELSE 0 END confirmed, " " (SELECT modes.dxcc FROM modes WHERE modes.name = '%3' LIMIT 1) current_mode, " " NULL member_id " "FROM contacts c, " " contact_clubs_view con2club, " " modes o " "WHERE con2club.contactid = c.id " "AND o.name = c.mode " "AND con2club.clubid in (SELECT clubid FROM membership a WHERE a.callsign = '%4') order by 1, 3, 2, 4").arg(callModified, dxccConfirmedByCond.join(" OR "), in_mode, callModified))) { qCWarning(runtime) << "Cannot Get club status" << query.lastError().text(); emit status(in_callsign, QMap()); return; } QMap retMap; QString currentProcessedClub; QString currentMemberID; bool bandMatched = false; bool bandModeMatched = false; bool bandModeConfirmedMatched = false; bool modeMatched = false; unsigned long records = 0L; while ( ++records && query.next() ) { const QString &clubid = query.value(0).toString(); const QString &band = query.value(1).toString(); const QString &mode = query.value(2).toString(); const QVariant &confirmed = query.value(3); const QString ¤t_mode = query.value(4).toString(); const QString &memberID = query.value(5).toString(); qCDebug(runtime) << "Processing" << currentProcessedClub << clubid << band.isEmpty() << band << mode.isEmpty() << mode << confirmed.toString().isEmpty() << confirmed << current_mode.isEmpty(); // the select generates starting line for a new club // Changing the club if ( currentProcessedClub != clubid && band.isEmpty() && mode.isEmpty() && confirmed.toString().isEmpty() && current_mode.isEmpty() ) { if ( !currentProcessedClub.isEmpty() ) { retMap[currentProcessedClub] = ClubInfo(determineClubStatus(bandMatched, bandModeMatched, bandModeConfirmedMatched, modeMatched, records), currentMemberID); } currentProcessedClub = clubid; currentMemberID = memberID; bandMatched = bandModeMatched = bandModeConfirmedMatched = modeMatched = false; records = 0L; continue; } if ( currentProcessedClub == clubid ) { if ( band == in_band ) { bandMatched = true; if ( mode == current_mode ) { bandModeMatched = true; if ( confirmed.toInt() == 1 ) { bandModeConfirmedMatched = true; } } } if ( mode == current_mode ) { modeMatched = true; } } else { qCWarning(runtime) << "Unexpected branch" << currentProcessedClub << clubid; } } if ( !currentProcessedClub.isEmpty() ) { qCDebug(runtime) << "Last Club processing" << currentProcessedClub; retMap[currentProcessedClub] = ClubInfo(determineClubStatus(bandMatched, bandModeMatched, bandModeConfirmedMatched, modeMatched, records), currentMemberID); } qCDebug(runtime) << "DONE"; emit status(in_callsign, retMap); return; } ClubStatusQuery::ClubStatus ClubStatusQuery::determineClubStatus(bool bandMatched, bool bandModeMatched, bool bandModeConfirmedMatched, bool modeMatched, unsigned long records) { FCT_IDENTIFICATION; qCDebug(function_parameters) << bandMatched << bandModeMatched << bandModeConfirmedMatched << modeMatched << records; if ( !bandMatched && !bandModeMatched && !bandModeConfirmedMatched && !modeMatched && records == 1) // records == 1 means that there is only technical row in table { qCDebug(runtime) << "NewClub"; return ClubStatus::NewClub; } if ( !bandMatched ) { if ( !bandModeMatched ) { qCDebug(runtime) << "NewBandModeClub"; return ClubStatus::NewBandModeClub; } else { qCDebug(runtime) << "NewBandClub"; return ClubStatus::NewBandClub; } } if ( !modeMatched ) { qCDebug(runtime) << "NewModeClub"; return ClubStatus::NewModeClub; } if ( ! bandModeMatched ) { qCDebug(runtime) << "NewSlotClub"; return ClubStatus::NewSlotClub; } if ( ! bandModeConfirmedMatched ) { qCDebug(runtime) << "WorkedClub"; return ClubStatus::WorkedClub; } else { qCDebug(runtime) << "ConfirmedClub"; return ClubStatus::ConfirmedClub; } qCWarning(runtime) << "!!!!!!!! Unknown Status - never !!!!!!!!!!"; return ClubStatus::UnknownStatusClub; } ================================================ FILE: core/MembershipQE.h ================================================ #ifndef QLOG_CORE_MEMBERSHIPQE_H #define QLOG_CORE_MEMBERSHIPQE_H #include #include #include #include #include #include #include class ClubInfo { public: explicit ClubInfo(const QString &callsign, const QString &ID, const QDate &validFrom, const QDate &validTo, const QString &club); const QString& getCallsign() const; const QString& getID() const; const QDate& getValidFrom() const; const QDate& getValidTo() const; const QString& getClubInfo() const; private: QString callsign; QString id; QDate validFrom; QDate validTo; QString club; }; class ClubStatusQuery : public QObject { Q_OBJECT public: explicit ClubStatusQuery(QObject *parent = nullptr); ~ClubStatusQuery(); enum ClubStatus { NewClub = 0b1, NewBandClub = 0b10, NewModeClub = 0b100, NewBandModeClub = 0b110, NewSlotClub = 0b1000, WorkedClub = 0b10000, ConfirmedClub = 0b100000, UnknownStatusClub = 0b1000000, AllClub = 0b1111111 }; Q_ENUM(ClubStatus); struct ClubInfo { ClubInfo(){status = UnknownStatusClub;}; ClubInfo(const ClubStatus status, const QString &memberID) : status(status), membershipID(memberID) {}; ClubStatus status; QString membershipID; }; public slots: void getClubStatus(const QString &in_callsign, const QString &in_band, const QString &in_mode, bool lowtConfirmed, bool paperConfirmed, bool eqslConfirmed); signals: void status(QString, QMap); private: const QString dbConnectionName; bool dbConnected; QMutex dbLock; ClubStatus determineClubStatus(bool, bool, bool, bool, unsigned long); }; Q_DECLARE_METATYPE(ClubStatusQuery::ClubInfo) class MembershipQE : public QObject { Q_OBJECT public: static MembershipQE *instance() { static MembershipQE instance; return &instance; }; static void saveEnabledClubLists(const QStringList &enabledLists); static QStringList getEnabledClubLists(); // return only list of clubs where callsign is a member. QList query(const QString &in_callsign); // return Status for each club // Membership status details can take a long time (depend on the number of records in the log and membership lists) // therefore qlog runs this query in an isolated thread (do not block the main thread). The result is returned via clubStatusResult signal - if exists void asyncQueryDetails(const QString &callsign, const QString &band, const QString &mode); void updateLists(); signals: void clubStatusResult(QString, QMap); private: MembershipQE(QObject *parent = nullptr); ~MembershipQE(); private slots: void statusQueryFinished(const QString &, QMap); void onFinishedListDownload(QNetworkReply *); private: ClubStatusQuery statusQuery; QThread statusQueryThread; bool removeDisabled(const QStringList &enabledLists); bool planDownloads(const QStringList &enabledLists); void startPlannedDownload(); bool importData(const QString &clubid, const QByteArray &data); void removeClubsFromEnabledClubLists(const QList> &toRemove); QSqlQuery clubQuery; bool idClubQueryValid; QList> updatePlan; QScopedPointer nam; }; #endif // QLOG_CORE_MEMBERSHIPQE_H ================================================ FILE: core/Migration.cpp ================================================ #include #include #include #include #include #include "core/Migration.h" #include "debug.h" #include "data/Data.h" #include "LogParam.h" #include "LOVDownloader.h" #include "service/clublog/ClubLog.h" #include "service/hrdlog/HRDLog.h" #include "logformat/AdxFormat.h" #include "ui/DxWidget.h" #include "core/LogDatabase.h" MODULE_IDENTIFICATION("qlog.core.migration"); /** * Migrate the database to the latest schema version. * Returns true on success. */ bool DBSchemaMigration::run(bool force) { FCT_IDENTIFICATION; int currentVersion = getVersion(); if (currentVersion == latestVersion) { qCDebug(runtime) << "Database schema already up to date"; updateExternalResource(force); // temporarily added to create a trigger without calling db migration //refreshUploadStatusTrigger(); return true; } else if ( currentVersion > latestVersion ) { qCritical() << "Database from the future" << currentVersion; return false; } qCDebug(runtime) << "Backup before migration"; backupAllQSOsToADX(true); qCDebug(runtime) << "Starting database migration"; QProgressDialog progress("Migrating the database...", nullptr, currentVersion, latestVersion); progress.show(); while ((currentVersion = getVersion()) < latestVersion) { bool res = migrate(currentVersion+1); if ( !res || getVersion() == currentVersion ) { progress.close(); return false; } // sometimes (especially when DROP INDEX is called), it is needed to // reopen database. (issue occurs between DB versions 15 and 16) QSqlDatabase::database().close(); if ( !QSqlDatabase::database().open() ) { progress.close(); qCritical() << QSqlDatabase::database().lastError(); return false; } // Re-register custom SQL functions after connection reopen // (sqlite3_create_function bindings are lost on close/open) LogDatabase::instance()->createSQLFunctions(); progress.setValue(currentVersion); } if ( !refreshUploadStatusTrigger() ) { qCritical() << "Cannot refresh Upload Status Trigger"; progress.close(); return false; } progress.close(); updateExternalResource(force); qCDebug(runtime) << "Database migration successful"; return true; } bool DBSchemaMigration::backupAllQSOsToADX(bool force) { FCT_IDENTIFICATION; const int backupCount = 10; const int backupIntervalDays = 7; const QDate &lastBackupDate = LogParam::getLastBackupDate(); const QDate &now = QDate::currentDate(); qCDebug(runtime) << "The last backup date" << lastBackupDate << "Force" << force; if ( !force && lastBackupDate.isValid() && lastBackupDate.addDays(backupIntervalDays) > now ) { qCDebug(runtime) << "Backup skipped"; return true; } const QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)); const QString todayBackupName = "qlog_backup_" + QDateTime::currentDateTime().toString("yyyyMMdd") + ".adx"; const QString todayBackupPath = dir.filePath(todayBackupName); // old backup file had a timestamp YYYYMMDDHHmmSS // new backup file has only YYYYMMDD // to be able to clean new and old backup files, following regexp is needed const QRegularExpression regex("^qlog_backup_\\d{8}(\\d{6})?\\.adx$"); const QFileInfoList &fileList = dir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot); QFileInfoList filteredList; for ( const QFileInfo &fileInfo : fileList ) { if ( regex.match(fileInfo.fileName()).hasMatch() ) // clazy:exclude=use-static-qregularexpression { filteredList.append(fileInfo); } } qCDebug(runtime) << filteredList; // does today backup exists ? if ( !force ) { for ( const QFileInfo &fileInfo : filteredList ) { if ( fileInfo.fileName() == todayBackupName ) { qCDebug(runtime) << "Backup for today already exists: " << todayBackupName; return true; } } } /* Keep the minimum number of backups */ /* If a number of backups is greater than backupCount, remove oldest files */ if ( filteredList.size() >= backupCount ) { std::sort(filteredList.begin(), filteredList.end(), [](const QFileInfo &a, const QFileInfo &b) { return a.fileName() < b.fileName(); }); // remove old files while ( filteredList.size() > backupCount ) { QFileInfo oldestFile = filteredList.takeFirst(); const QString &filepath = oldestFile.absoluteFilePath(); if ( QFile::remove(filepath) ) qCDebug(runtime) << "Removing old backup file: " << filepath; else qWarning() << "Failed to remove old backup file: " << filepath; } } /* Make a backup file */ QFile backupFile(todayBackupPath); if ( !backupFile.open(QFile::ReadWrite | QIODevice::Text) ) { qWarning() << "Cannot open backup file " << todayBackupPath << " for writing"; return false; } qCDebug(runtime) << "Exporting a Database backup to " << todayBackupPath; QTextStream stream(&backupFile); AdxFormat adx(stream); adx.runExport(); stream.flush(); backupFile.close(); LogParam::setLastBackupDate(now); qCDebug(runtime) << "Database backup finished"; return true; } /** * Returns the current user_version of the database. */ int DBSchemaMigration::getVersion() { FCT_IDENTIFICATION; QSqlQuery query("SELECT version FROM schema_versions " "ORDER BY version DESC LIMIT 1"); int i = query.first() ? query.value(0).toInt() : 0; qCDebug(runtime) << i; return i; } /** * Changes the user_version of the database to version. * Returns true of the operation was successful. */ bool DBSchemaMigration::setVersion(int version) { FCT_IDENTIFICATION; qCDebug(function_parameters) << version; QSqlQuery query; if ( ! query.prepare("INSERT INTO schema_versions (version, updated) " "VALUES (:version, datetime('now'))") ) { qWarning() << "Cannot prepare Insert statement"; } query.bindValue(":version", version); if (!query.exec()) { qWarning() << "setting schema version failed" << query.lastError(); return false; } else { return true; } } /** * Migrate the database to the given version. * Returns true if the operation was successful. */ bool DBSchemaMigration::migrate(int toVersion) { FCT_IDENTIFICATION; qCDebug(runtime) << "migrate to" << toVersion; QSqlDatabase db = QSqlDatabase::database(); if (!db.transaction()) { qCritical() << "transaction failed"; return false; } QString migration_file = QString(":/res/sql/migration_%1.sql").arg(toVersion, 3, 10, QChar('0')); bool result = runSqlFile(migration_file); result = result && functionMigration(toVersion); if (result && setVersion(toVersion) && db.commit()) { return true; } else { if (!db.rollback()) { qCritical() << "rollback failed"; } return false; } } bool DBSchemaMigration::runSqlFile(QString filename) { FCT_IDENTIFICATION; qCDebug(function_parameters) << filename; QFile sqlFile(filename); sqlFile.open(QIODevice::ReadOnly | QIODevice::Text); const QStringList &sqlQueries = QTextStream(&sqlFile).readAll().split('\n').join(" ").split(';'); qCDebug(runtime) << sqlQueries; for (const QString &sqlQuery : sqlQueries) { if (sqlQuery.trimmed().isEmpty()) continue; qCDebug(runtime) << sqlQuery; QSqlQuery query; if (!query.exec(sqlQuery)) { qCDebug(runtime) << query.lastError(); return false; } query.finish(); } return true; } bool DBSchemaMigration::functionMigration(int version) { FCT_IDENTIFICATION; bool ret = false; switch ( version ) { case 4: ret = fixIntlFields(); break; case 6: ret = insertUUID(); break; case 15: ret = fillMyDXCC(); break; case 17: ret = createTriggers(); break; case 23: ret = importQSLCards2DB(); break; case 26: ret = fillCQITUZStationProfiles(); break; case 28: ret = resetConfigs(); break; case 29: ret = profiles2DB(); break; case 34: ret = settings2DB(); break; case 35: ret = removeSettings2DB(); break; default: ret = true; } return ret; } int DBSchemaMigration::tableRows(const QString &name) { FCT_IDENTIFICATION; int i = 0; QSqlQuery query(QString("SELECT count(*) FROM %1").arg(name)); i = query.first() ? query.value(0).toInt() : 0; qCDebug(runtime) << i; return i; } bool DBSchemaMigration::updateExternalResource(bool force) { FCT_IDENTIFICATION; LOVDownloader downloader; QProgressDialog progress; connect(&downloader, &LOVDownloader::processingSize, &progress, &QProgressDialog::setMaximum); connect(&downloader, &LOVDownloader::progress, &progress, &QProgressDialog::setValue); connect(&downloader, &LOVDownloader::finished, &progress, &QProgressDialog::done); connect(&downloader, &LOVDownloader::noUpdate, &progress, &QProgressDialog::cancel); connect(&progress, &QProgressDialog::canceled, &downloader, &LOVDownloader::abortRequest); updateExternalResourceProgress(progress, downloader, LOVDownloader::CTY, "(1/8)", force); updateExternalResourceProgress(progress, downloader, LOVDownloader::CLUBLOGCTY, "2/8", force); updateExternalResourceProgress(progress, downloader, LOVDownloader::SATLIST, "(3/8)", force); updateExternalResourceProgress(progress, downloader, LOVDownloader::SOTASUMMITS, "(4/8)", force); updateExternalResourceProgress(progress, downloader, LOVDownloader::WWFFDIRECTORY, "(5/8)", force); updateExternalResourceProgress(progress, downloader, LOVDownloader::IOTALIST, "(6/8)", force); updateExternalResourceProgress(progress, downloader, LOVDownloader::POTADIRECTORY, "(7/8)", force); updateExternalResourceProgress(progress, downloader, LOVDownloader::MEMBERSHIPCONTENTLIST, "(8/8)", force); return true; } void DBSchemaMigration::updateExternalResourceProgress(QProgressDialog& progress, LOVDownloader& downloader, const LOVDownloader::SourceType & sourceType, const QString &counter, bool force) { FCT_IDENTIFICATION; QString stringInfo; progress.reset(); switch ( sourceType ) { case LOVDownloader::SourceType::CTY: stringInfo = tr("DXCC Entities"); break; case LOVDownloader::SourceType::SATLIST: stringInfo = tr("Sats Info"); break; case LOVDownloader::SourceType::SOTASUMMITS: stringInfo = tr("SOTA Summits"); break; case LOVDownloader::SourceType::WWFFDIRECTORY: stringInfo = tr("WWFF Records"); break; case LOVDownloader::SourceType::IOTALIST: stringInfo = tr("IOTA Records"); break; case LOVDownloader::SourceType::POTADIRECTORY: stringInfo = tr("POTA Records"); break; case LOVDownloader::SourceType::MEMBERSHIPCONTENTLIST: stringInfo = tr("Membership Directory Records"); break; case LOVDownloader::SourceType::CLUBLOGCTY: stringInfo = tr("Clublog CTY.XML"); break; default: stringInfo = tr("List of Values"); } progress.setWindowFlags(Qt::Dialog | Qt::WindowStaysOnTopHint); progress.setLabelText(tr("Updating ") + stringInfo + " " + counter +" ..."); progress.setMinimum(0); progress.setModal(true); progress.show(); downloader.update(sourceType, force); if ( progress.wasCanceled() ) qCDebug(runtime) << "Update was canceled"; else { if ( !progress.exec() ) QMessageBox::warning(nullptr, QMessageBox::tr("QLog Warning"), stringInfo + tr(" Update Failed")); } } /* Fixing error when QLog stored UTF characters to non-Intl field of ADIF (contact) table */ /* The fix has two steps * 1) Update contact to move all non-intl to intl fields * 2) transform intl field to non-intl field by calloni removeAccents */ bool DBSchemaMigration::fixIntlFields() { FCT_IDENTIFICATION; QSqlQuery query; QSqlQuery update; if ( !query.prepare( "SELECT id, name, name_intl, " " qth, qth_intl, " " comment, comment_intl, " " my_antenna, my_antenna_intl," " my_city, my_city_intl," " my_rig, my_rig_intl," " my_sig, my_sig_intl," " my_sig_info, my_sig_info_intl," " sig, sig_intl," " sig_info, sig_info_intl " " FROM contacts" ) ) { qWarning()<< " Cannot prepare a migration script - fixIntlField 1"; return false; } if( !query.exec() ) { qWarning()<< "Cannot exec a migration script - fixIntlFields 1"; return false; } if ( !update.prepare("UPDATE contacts SET name = :name," " qth = :qth, " " comment = :comment," " my_antenna = :my_antenna," " my_city = :my_city," " my_rig = :my_rig," " my_sig = :my_sig," " my_sig_info = :my_sig_info," " sig = :sig," " sig_info = :sig_info " "WHERE id = :id") ) { qWarning()<< " Cannot prepare a migration script - fixIntlField 2"; return false; } while( query.next() ) { update.bindValue(":id", query.value("id").toInt()); update.bindValue(":name", fixIntlField(query, "name", "name_intl")); update.bindValue(":qth", fixIntlField(query, "qth", "qth_intl")); update.bindValue(":comment", fixIntlField(query, "comment", "comment_intl")); update.bindValue(":my_antenna", fixIntlField(query, "my_antenna", "my_antenna_intl")); update.bindValue(":my_city", fixIntlField(query, "my_city", "my_city_intl")); update.bindValue(":my_rig", fixIntlField(query, "my_rig", "my_rig_intl")); update.bindValue(":my_sig", fixIntlField(query, "my_sig", "my_sig_intl")); update.bindValue(":my_sig_info",fixIntlField(query, "my_sig_info", "my_sig_info_intl")); update.bindValue(":sig", fixIntlField(query, "sig", "sig_intl")); update.bindValue(":sig_info", fixIntlField(query, "sig_info", "sig_info_intl")); if ( !update.exec()) { qWarning() << "Cannot exec a migration script - fixIntlFields 2"; return false; } } return true; } bool DBSchemaMigration::insertUUID() { FCT_IDENTIFICATION; return LogParam::setLogID(QUuid::createUuid().toString()); } bool DBSchemaMigration::fillMyDXCC() { FCT_IDENTIFICATION; QSqlQuery query; QSqlQuery update; if ( !query.prepare( "SELECT DISTINCT station_callsign FROM contacts" ) ) { qWarning()<< " Cannot prepare a migration script - fillMyDXCC 1" << query.lastError(); return false; } if( !query.exec() ) { qWarning()<< "Cannot exec a migration script - fillMyDXCC 1" << query.lastError(); return false; } if ( !update.prepare("UPDATE contacts " "SET my_dxcc = CASE WHEN my_dxcc IS NULL THEN :my_dxcc ELSE my_dxcc END, " " my_itu_zone = CASE WHEN my_itu_zone IS NULL THEN :my_itu_zone ELSE my_itu_zone END, " " my_cq_zone = CASE WHEN my_cq_zone IS NULL THEN :my_cq_zone ELSE my_cq_zone END, " " my_country = CASE WHEN my_country IS NULL THEN :my_country ELSE my_country END, " " my_country_intl = CASE WHEN my_country_intl IS NULL THEN :my_country_intl ELSE my_country_intl END " "WHERE station_callsign = :station_callsign") ) { qWarning()<< " Cannot prepare a migration script - fillMyDXCC 2" << update.lastError(); return false; } // it is a hack because the migration is running before migration (start/stop database). // It caused that SQL prepared in contructor are incorrectly created. // Therefore, Data are create temporary here Data tmp; while( query.next() ) { QString myCallsign = query.value("station_callsign").toString(); DxccEntity dxccEntity = tmp.lookupDxcc(myCallsign); if ( dxccEntity.dxcc ) { update.bindValue(":my_dxcc", dxccEntity.dxcc); update.bindValue(":my_itu_zone", dxccEntity.ituz); update.bindValue(":my_cq_zone", dxccEntity.cqz); update.bindValue(":my_country_intl", dxccEntity.country); update.bindValue(":my_country", Data::removeAccents(dxccEntity.country)); update.bindValue(":station_callsign", myCallsign); if ( !update.exec()) { qWarning() << "Cannot exec a migration script - fillMyDXCC 2" << update.lastError(); return false; } } } return true; } bool DBSchemaMigration::createTriggers() { FCT_IDENTIFICATION; // Migration procedure does not support to execute SQL code with Triggers. // It will be safer to create triggers here than to fix the migration procedure QSqlQuery query; if ( ! query.exec(QString("CREATE TRIGGER update_callsign_contacts_autovalue " "AFTER UPDATE ON contacts " "FOR EACH ROW " "WHEN OLD.callsign <> NEW.callsign " "BEGIN " " INSERT OR REPLACE INTO contacts_autovalue (contactid, base_callsign) " " VALUES (NEW.id, (WITH tokenizedCallsign(word, csv) AS ( SELECT '', NEW.callsign||'/' " " UNION ALL " " SELECT substr(csv, 0, instr(csv, '/')), substr(csv, instr(csv, '/') + 1) " " FROM tokenizedCallsign " " WHERE csv != '' ) " " SELECT word FROM tokenizedCallsign " " WHERE word != '' AND word REGEXP '^([A-Z][0-9]|[A-Z]{1,2}|[0-9][A-Z])([0-9]|[0-9]+)([A-Z]+)$' LIMIT 1));" "END;"))) { qWarning() << "Cannot create trigger update_callsign_contacts_autovalue " << query.lastError().text(); return false; } if ( ! query.exec(QString("CREATE TRIGGER insert_contacts_autovalue " "AFTER INSERT ON contacts " "FOR EACH ROW " "BEGIN " " INSERT OR REPLACE INTO contacts_autovalue (contactid, base_callsign) " " VALUES (NEW.id, (WITH tokenizedCallsign(word, csv) AS ( SELECT '', NEW.callsign||'/' " " UNION ALL " " SELECT substr(csv, 0, instr(csv, '/')), substr(csv, instr(csv, '/') + 1) " " FROM tokenizedCallsign " " WHERE csv != '' ) " " SELECT word FROM tokenizedCallsign " " WHERE word != '' and word REGEXP '^([A-Z][0-9]|[A-Z]{1,2}|[0-9][A-Z])([0-9]|[0-9]+)([A-Z]+)$' LIMIT 1)); " "END;"))) { qWarning() << "Cannot create trigger update_callsign_contacts_autovalue " << query.lastError().text(); return false; } return true; } bool DBSchemaMigration::importQSLCards2DB() { FCT_IDENTIFICATION; QSettings settings; QSqlQuery insert; // don't migrate eqsl imagge because it is only a local cache. If the image is missing then // QLog will download it again static QRegularExpression re("^[0-9]{8}_([0-9]+)_.*_qsl_(.*)", QRegularExpression::CaseInsensitiveOption); QString qslFolder = settings.value("paperqsl/qslfolder", QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).toString(); QDir dir(qslFolder); const QFileInfoList &file_list = dir.entryInfoList(QStringList("????????_*_*_qsl_*.*"), QDir::Files, QDir::Name); if ( !insert.prepare("INSERT INTO contacts_qsl_cards (contactid, source, name, data) " " VALUES (:contactid, 0, :name, :data)" ) ) { qWarning()<< " Cannot prepare a migration script - importQSLCards2DB 2" << insert.lastError(); return false; } for ( auto &file : file_list ) { qCDebug(runtime) << "Processing file" << file.fileName(); QRegularExpressionMatch match = re.match(file.fileName()); if ( match.hasMatch() ) { qCDebug(runtime) << "File matched - importing"; QFile f(file.absoluteFilePath()); if ( !f.open(QFile::ReadOnly)) { qWarning() << "Cannot open file" << file.fileName(); continue; } QByteArray blob = f.readAll(); f.close(); //do not remove the file - the file may be used by another application //do not use PaperQSL Class here for saving. // this is a migration sequence for filesystem to DB migration with specific properties insert.bindValue(":contactid", match.captured(1)); insert.bindValue(":name", match.captured(2)); insert.bindValue(":data", blob.toBase64()); if ( !insert.exec() ) { qWarning() << "Cannot import QSL file" << file.absoluteFilePath(); continue; } qCDebug(runtime) << "File matched - imported"; } } settings.remove("paperqsl/qslfolder"); settings.remove("eqsl/qslfolder"); return true; } bool DBSchemaMigration::fillCQITUZStationProfiles() { FCT_IDENTIFICATION; QSqlQuery query; QSqlQuery update; if ( !query.prepare( "SELECT DISTINCT callsign FROM station_profiles" ) ) { qWarning()<< " Cannot prepare a migration script - fillCQITUZStationProfiles 1" << query.lastError(); return false; } if( !query.exec() ) { qWarning()<< "Cannot exec a migration script - fillCQITUZStationProfiles 1" << query.lastError(); return false; } if ( !update.prepare("UPDATE station_profiles " "SET ituz = :ituz, cqz = :cqz, dxcc = :dxcc, country = :country " "WHERE upper(callsign) = :callsign") ) { qWarning()<< " Cannot prepare a migration script - fillCQITUZStationProfiles 2" << update.lastError(); return false; } // it is a hack because the migration is running before migration (start/stop database). // It caused that SQL prepared in contructor are incorrectly created. // Therefore, Data are create temporary here Data tmp; while( query.next() ) { const QString &myCallsign = query.value("callsign").toString().toUpper(); const DxccEntity &dxccEntity = tmp.lookupDxcc(myCallsign); if ( dxccEntity.dxcc ) { update.bindValue(":ituz", dxccEntity.ituz); update.bindValue(":cqz", dxccEntity.cqz); update.bindValue(":dxcc", dxccEntity.dxcc); update.bindValue(":country", dxccEntity.country); update.bindValue(":callsign", myCallsign); if ( !update.exec()) { qWarning() << "Cannot exec a migration script - fillCQITUZStationProfiles 2" << update.lastError(); return false; } } } return true; } bool DBSchemaMigration::resetConfigs() { FCT_IDENTIFICATION; // to force redownload CTY file QSettings settings; settings.remove("last_cty_update"); // it changes to sortable view. // I don't know why, but when the layout is saved, the sort indicator // is not displayed when the sortable view is turned on. So I rather remove the view state settings.remove("wsjtx/state"); return true; } bool DBSchemaMigration::profiles2DB() { FCT_IDENTIFICATION; QSettings settings; setSelectedProfile("ant_profiles", settings.value("equipment/ant/profile1", QString()).toString()); settings.remove("equipment/ant/profile1"); setSelectedProfile("cwkey_profiles", settings.value("equipment/cwkey/profile1", QString()).toString()); settings.remove("equipment/cwkey/profile1"); setSelectedProfile("cwshortcut_profiles", settings.value("equipment/cwshortcut/profile1", QString()).toString()); settings.remove("equipment/cwshortcut/profile1"); setSelectedProfile("rig_profiles", settings.value("equipment/rig/profile1", QString()).toString()); settings.remove("equipment/rig/profile1"); setSelectedProfile("rot_profiles", settings.value("equipment/rot/profile1", QString()).toString()); settings.remove("equipment/rot/profile1"); setSelectedProfile("rot_user_buttons_profiles", settings.value("equipment/rotusrbuttons/profile1", QString()).toString()); settings.remove("equipment/rotusrbuttons/profile1"); setSelectedProfile("station_profiles", settings.value("station/profile1", QString()).toString()); settings.remove("station/profile1"); setSelectedProfile("main_layout_profiles", settings.value("newcontact/layoutprofile/profile1", QString()).toString()); settings.remove("newcontact/layoutprofile/profile1"); // LOVs to DB settings.remove("last_cty_update"); settings.remove("last_sat_update"); settings.remove("last_sotasummits_update"); settings.remove("last_wwffdirectory_update"); settings.remove("last_iota_update"); settings.remove("last_pota_update"); settings.remove("last_membershipcontent_update"); settings.remove("dxcc/start"); return true; } bool DBSchemaMigration::removeSettings2DB() { // all platform-independent settings are already in the DB, // no unusual errors occur. It's time to delete old values // ​​from the config. // Only platform-dependent settings remain in the config now. QSettings settings; settings.remove("qrzcom/username"); settings.remove("qrzcom/usernameapi"); settings.remove("clublog/email"); settings.remove("clublog/callsign"); settings.remove("clublog/upload_immediately"); settings.remove("eqsl/username"); settings.remove("eqsl/last_update"); settings.remove("eqsl/last_qsoqsl"); settings.remove("eqsl/last_QTHProfile"); settings.remove("hamqth/username"); settings.remove("hrdlog/callsign"); settings.remove("hrdlog/onair"); settings.remove("kst/username"); settings.remove("lotw/username"); settings.remove("lotw/tqsl"); settings.remove("lotw/last_update"); settings.remove("lotw/last_qsoqsl"); settings.remove("callbook/primary"); settings.remove("callbook/secondary"); settings.remove("callbook/weblookupurl"); settings.remove("network/notification/qso/adi_addrs"); settings.remove("network/notification/dxspot/addrs"); settings.remove("network/notification/wsjtx/cqspot/addrs"); settings.remove("network/notification/alerts/spot/addrs"); settings.remove("network/notification/rig/state/addrs"); settings.remove("network/wsjtx_port"); settings.remove("network/wsjtx_forward"); settings.remove("network/wsjtx_multicast"); settings.remove("network/wsjtx_multicast_addr"); settings.remove("network/wsjtx_multicast_ttl"); settings.remove("memberlists/enabled"); settings.remove("alert/alert_aging"); settings.remove("alert/state"); settings.remove("cwconsole/sendWord"); settings.remove("chat/last_selected_room"); settings.remove("newcontact/frequency"); settings.remove("newcontact/mode"); settings.remove("newcontact/submode"); settings.remove("newcontact/power"); settings.remove("newcontact/tabindex"); settings.remove("newcontact/qslsent"); settings.remove("newcontact/lotwqslsent"); settings.remove("newcontact/eqslqslsent"); settings.remove("newcontact/qslsentvia"); settings.remove("newcontact/propmode"); settings.remove("newcontact/tabsexpanded"); settings.remove("newcontact/satname"); settings.beginGroup("onlinemap"); settings.remove(""); settings.endGroup(); settings.beginGroup("qsodetail"); settings.remove(""); settings.endGroup(); settings.remove("wsjtx/filter_dxcc_status"); settings.remove("wsjtx/filter_cont_regexp"); settings.remove("wsjtx/filter_distance"); settings.remove("wsjtx/filter_snr"); settings.remove("wsjtx/filter_dx_member_list"); settings.remove("wsjtx/state"); settings.remove("dxc/filter_dxcc_status"); settings.remove("dxc/filter_cont_regexp"); settings.remove("dxc/filter_spotter_cont_regexp"); settings.remove("dxc/filter_deduplication"); settings.remove("dxc/filter_duplicationtime"); settings.remove("dxc/filter_deduplicationfreq"); settings.remove("dxc/filter_dx_member_list"); settings.remove("dxc/autoconnect"); settings.remove("dxc/keepqsos"); settings.remove("dxc/dxtablestate"); settings.remove("dxc/wcytablestate"); settings.remove("dxc/wwvtablestate"); settings.remove("dxc/toalltablestate"); settings.remove("dxc/consolefontsize"); settings.remove("dxc/servers"); settings.remove("dxc/last_server"); settings.remove("dxc/filter_mode_cw"); settings.remove("dxc/filter_mode_phone"); settings.remove("dxc/filter_mode_digital"); settings.remove("dxc/filter_mode_ft8"); settings.remove("equipment/rigconnected"); settings.remove("equipment/rotconnected"); settings.remove("equipment/cwkeyconnected"); settings.remove("equipment/keepoptions"); settings.remove("export/min"); settings.remove("export/qsl"); settings.remove("export/c1"); settings.remove("export/c2"); settings.remove("export/c3"); settings.remove("logbook/state"); settings.remove("logbook/filters/band"); settings.remove("logbook/filters/mode"); settings.remove("logbook/filters/country"); settings.remove("logbook/filters/user"); settings.remove("logbook/filters/member"); settings.remove("alertbeep"); settings.remove("darkmode"); settings.remove("geometry"); settings.remove("windowState"); settings.remove("bandmapwidgets"); return true; } bool DBSchemaMigration::settings2DB() { FCT_IDENTIFICATION; QSettings settings; if (settings.contains("qrzcom/username")) LogParam::setQRZCOMCallbookUsername(settings.value("qrzcom/username").toString().trimmed()); if (settings.contains("clublog/email")) LogParam::setClublogLogbookReqEmail(settings.value("clublog/email").toString().trimmed()); if (settings.contains("clublog/upload_immediately")) LogParam::setClublogUploadImmediatelyEnabled(settings.value("clublog/upload_immediately").toBool()); if (settings.contains("eqsl/username")) LogParam::setEQSLLogbookUsername(settings.value("eqsl/username").toString()); if (settings.contains("eqsl/last_update")) LogParam::setDownloadQSLServiceLastDate("eqsl", settings.value("eqsl/last_update").toDate()); if (settings.contains("eqsl/last_qsoqsl")) LogParam::setDownloadQSLServiceLastQSOQSL("eqsl", settings.value("eqsl/last_qsoqsl").toBool()); if (settings.contains("eqsl/last_QTHProfile")) { LogParam::setDownloadQSLeQSLLastProfile(settings.value("eqsl/last_QTHProfile").toString()); LogParam::setUploadeqslQTHProfile(settings.value("eqsl/last_QTHProfile").toString()); } if (settings.contains("hamqth/username")) LogParam::setHamQTHCallbookUsername(settings.value("hamqth/username").toString()); if (settings.contains("hrdlog/callsign")) LogParam::setHRDLogLogbookReqCallsign(settings.value("hrdlog/callsign").toString()); if (settings.contains("hrdlog/onair")) LogParam::setHRDLogOnAir(settings.value("hrdlog/onair").toBool()); if (settings.contains("kst/username")) LogParam::setKSTChatUsername(settings.value("kst/username").toString()); if (settings.contains("lotw/username")) LogParam::setLoTWCallbookUsername(settings.value("lotw/username").toString()); if (settings.contains("lotw/tqsl")) LogParam::setLoTWTQSLPath(settings.value("lotw/tqsl").toString()); if (settings.contains("lotw/last_update")) LogParam::setDownloadQSLServiceLastDate("lotw", settings.value("lotw/last_update").toDate()); if (settings.contains("lotw/last_qsoqsl")) LogParam::setDownloadQSLServiceLastQSOQSL("lotw", settings.value("lotw/last_qsoqsl").toBool()); if (settings.contains("callbook/primary")) LogParam::setPrimaryCallbook(settings.value("callbook/primary").toString()); if (settings.contains("callbook/secondary")) LogParam::setSecondaryCallbook(settings.value("callbook/secondary").toString()); if (settings.contains("callbook/weblookupurl")) LogParam::setCallbookWebLookupURL(settings.value("callbook/weblookupurl").toString()); if (settings.contains("network/notification/qso/adi_addrs")) LogParam::setNetworkNotifLogQSOAddrs(settings.value("network/notification/qso/adi_addrs").toString()); if (settings.contains("network/notification/dxspot/addrs")) LogParam::setNetworkNotifDXCSpotAddrs(settings.value("network/notification/dxspot/addrs").toString()); if (settings.contains("network/notification/wsjtx/cqspot/addrs")) LogParam::setNetworkNotifWSJTXCQSpotAddrs(settings.value("network/notification/wsjtx/cqspot/addrs").toString()); if (settings.contains("network/notification/alerts/spot/addrs")) LogParam::setNetworkNotifAlertsSpotAddrs(settings.value("network/notification/alerts/spot/addrs").toString()); if (settings.contains("network/notification/rig/state/addrs")) LogParam::setNetworkNotifRigStateAddrs(settings.value("network/notification/rig/state/addrs").toString()); if (settings.contains("network/wsjtx_port")) LogParam::setNetworkNotifRigStateAddrs(settings.value("network/wsjtx_port").toInt()); if (settings.contains("network/wsjtx_forward")) LogParam::setNetworkWsjtxForwardAddrs(settings.value("network/wsjtx_forward").toString()); if (settings.contains("network/wsjtx_multicast")) LogParam::setNetworkWsjtxListenerJoinMulticast(settings.value("network/wsjtx_multicast").toBool()); if (settings.contains("network/wsjtx_multicast_addr")) LogParam::setNetworkWsjtxListenerMulticastAddr(settings.value("network/wsjtx_multicast_addr").toString()); if (settings.contains("network/wsjtx_multicast_ttl")) LogParam::setNetworkWsjtxListenerMulticastTTL(settings.value("network/wsjtx_multicast_ttl").toInt()); if (settings.contains("memberlists/enabled")) LogParam::setEnabledMemberlists(settings.value("memberlists/enabled").toStringList()); if (settings.contains("alert/alert_aging")) LogParam::setAlertAging(settings.value("alert/alert_aging").toInt()); if (settings.contains("alert/state")) LogParam::setAlertWidgetState(settings.value("alert/state").toByteArray()); if (settings.contains("cwconsole/sendWord")) LogParam::setCWConsoleSendWord(settings.value("cwconsole/sendWord", false).toBool()); if (settings.contains("chat/last_selected_room")) LogParam::setChatSelectedRoom(settings.value("chat/last_selected_room", 0).toInt()); if (settings.contains("newcontact/frequency")) LogParam::setNewContactFreq(settings.value("newcontact/frequency").toDouble()); if (settings.contains("newcontact/mode")) LogParam::setNewContactMode(settings.value("newcontact/mode").toString()); if (settings.contains("newcontact/submode")) LogParam::setNewContactSubMode(settings.value("newcontact/submode").toString()); if (settings.contains("newcontact/power")) LogParam::setNewContactPower(settings.value("newcontact/power").toDouble()); if (settings.contains("newcontact/tabindex")) LogParam::setNewContactTabIndex(settings.value("newcontact/tabindex", 0).toInt()); if (settings.contains("newcontact/qslsent")) LogParam::setNewContactQSLSent(settings.value("newcontact/qslsent").toString()); if (settings.contains("newcontact/lotwqslsent")) LogParam::setNewContactLoTWQSLSent(settings.value("newcontact/lotwqslsent").toString()); if (settings.contains("newcontact/eqslqslsent")) LogParam::setNewContactEQSLQSLSent(settings.value("newcontact/eqslqslsent").toString()); if (settings.contains("newcontact/qslsentvia")) LogParam::setNewContactQSLVia(settings.value("newcontact/qslsentvia").toString()); if (settings.contains("newcontact/propmode")) LogParam::setNewContactPropMode(settings.value("newcontact/propmode").toString()); if (settings.contains("newcontact/tabsexpanded")) LogParam::setNewContactTabsExpanded(settings.value("newcontact/tabsexpanded").toBool()); if (settings.contains("newcontact/satname")) LogParam::setNewContactSatName(settings.value("newcontact/satname").toString()); settings.beginGroup("onlinemap/layerstate"); const QStringList &onlineLayerKeys = settings.allKeys(); for ( const QString &key : onlineLayerKeys) LogParam::setMapLayerState("onlinemap", key, settings.value(key).toBool()); settings.endGroup(); settings.beginGroup("qsodetail/layerstate"); const QStringList &qsoDetailLayerKeys = settings.allKeys(); for ( const QString &key : qsoDetailLayerKeys) LogParam::setMapLayerState("qsodetail", key, settings.value(key).toBool()); settings.endGroup(); if (settings.contains("wsjtx/filter_dxcc_status")) LogParam::setWsjtxFilterDxccStatus(settings.value("wsjtx/filter_dxcc_status").toUInt()); if (settings.contains("wsjtx/filter_cont_regexp")) LogParam::setWsjtxFilterContRE(settings.value("wsjtx/filter_cont_regexp").toString()); if (settings.contains("wsjtx/filter_distance")) LogParam::setWsjtxFilterDistance(settings.value("wsjtx/filter_distance").toInt()); if (settings.contains("wsjtx/filter_snr")) LogParam::setWsjtxFilterDistance(settings.value("wsjtx/filter_snr").toInt()); if (settings.contains("wsjtx/filter_dx_member_list")) LogParam::setWsjtxMemberlists(settings.value("wsjtx/filter_dx_member_list").toStringList()); if (settings.contains("wsjtx/state")) LogParam::setWsjtxWidgetState(settings.value("wsjtx/state").toByteArray()); if (settings.contains("dxc/filter_dxcc_status")) LogParam::setDXCFilterDxccStatus(settings.value("dxc/filter_dxcc_status").toUInt()); if (settings.contains("dxc/filter_cont_regexp")) LogParam::setDXCFilterContRE(settings.value("dxc/filter_cont_regexp").toString()); if (settings.contains("dxc/filter_spotter_cont_regexp")) LogParam::setDXCFilterSpotterContRE(settings.value("dxc/filter_spotter_cont_regexp").toString()); if (settings.contains("dxc/filter_deduplication")) LogParam::setDXCFilterDedup(settings.value("dxc/filter_deduplication").toBool()); if (settings.contains("dxc/filter_duplicationtime")) LogParam::setDXCFilterDedupTime(settings.value("dxc/filter_duplicationtime").toInt()); if (settings.contains("dxc/filter_deduplicationfreq")) LogParam::setDXCFilterDedupFreq(settings.value("dxc/filter_deduplicationfreq", DEDUPLICATION_FREQ_TOLERANCE).toInt()); if (settings.contains("dxc/filter_dx_member_list")) LogParam::setDXCFilterMemberlists(settings.value("dxc/filter_dx_member_list").toStringList()); if (settings.contains("dxc/autoconnect")) LogParam::setDXCAutoconnectServer(settings.value("dxc/autoconnect").toBool()); if (settings.contains("dxc/keepqsos")) LogParam::setDXCKeepQSOs(settings.value("dxc/keepqsos").toBool()); if (settings.contains("dxc/dxtablestate")) LogParam::setDXCDXTableState(settings.value("dxc/dxtablestate").toByteArray()); if (settings.contains("dxc/wcytablestate")) LogParam::setDXCWCYTableState(settings.value("dxc/wcytablestate").toByteArray()); if (settings.contains("dxc/wwvtablestate")) LogParam::setDXCWWVTableState(settings.value("dxc/wwvtablestate").toByteArray()); if (settings.contains("dxc/toalltablestate")) LogParam::setDXCTOALLTableState(settings.value("dxc/toalltablestate").toByteArray()); if (settings.contains("dxc/consolefontsize")) LogParam::setDXCConsoleFontSize(settings.value("dxc/consolefontsize").toInt()); if (settings.contains("dxc/servers")) LogParam::setDXCServerlist(settings.value("dxc/servers").toStringList()); if (settings.contains("dxc/last_server")) LogParam::setDXCLastServer(settings.value("dxc/last_server").toString()); if (settings.contains("dxc/filter_mode_cw") || settings.contains("dxc/filter_mode_phone") || settings.contains("dxc/filter_mode_digital") || settings.contains("dxc/filter_mode_ft8") ) { QString value("NOTHING"); if ( settings.value("dxc/filter_mode_cw",true).toBool() ) value.append("|" + BandPlan::MODE_GROUP_STRING_CW); if ( settings.value("dxc/filter_mode_phone",true).toBool() ) value.append("|" + BandPlan::MODE_GROUP_STRING_PHONE); if ( settings.value("dxc/filter_mode_digital",true).toBool() ) value.append("|" + BandPlan::MODE_GROUP_STRING_DIGITAL); if ( settings.value("dxc/filter_mode_ft8",true).toBool() ) value.append("|" + BandPlan::MODE_GROUP_STRING_FTx); LogParam::setDXCFilterModeRE(value); } const QList &bands = BandPlan::bandsList(false, true); QStringList excludedBandFilter; for ( const Band &band : bands ) { if ( !settings.value("dxc/filter_band_" + band.name,true).toBool()) excludedBandFilter << band.name; } if ( !excludedBandFilter.isEmpty() ) LogParam::setDXCExcludedBands(excludedBandFilter); if (settings.contains("export/min")) LogParam::setExportColumnSet("min", settings.value("export/min").value>()); if (settings.contains("export/sql")) LogParam::setExportColumnSet("sql", settings.value("export/sql").value>()); if (settings.contains("export/c1")) LogParam::setExportColumnSet("c1", settings.value("export/c1").value>()); if (settings.contains("export/c2")) LogParam::setExportColumnSet("c2", settings.value("export/c2").value>()); if (settings.contains("export/c3")) LogParam::setExportColumnSet("c3", settings.value("export/c3").value>()); if (settings.contains("logbook/state")) LogParam::setLogbookState(settings.value("logbook/state").toByteArray()); if (settings.contains("logbook/filters/band")) LogParam::setLogbookFilterBand(settings.value("logbook/filters/band").toString()); if (settings.contains("logbook/filters/mode")) LogParam::setLogbookFilterMode(settings.value("logbook/filters/mode").toString()); if (settings.contains("logbook/filters/country")) LogParam::setLogbookFilterCountry(settings.value("logbook/filters/country").toString()); if (settings.contains("logbook/filters/user")) LogParam::setLogbookFilterUserFilter(settings.value("logbook/filters/user").toString()); if (settings.contains("logbook/filters/member")) LogParam::setLogbookFilterClub(settings.value("logbook/filters/member").toString()); if (settings.contains("alertbeep")) LogParam::setMainWindowAlertBeep(settings.value("alertbeep").toBool()); if (settings.contains("darkmode")) LogParam::setMainWindowDarkMode(settings.value("darkmode").toBool()); if (settings.contains("geometry")) LogParam::setMainWindowGeometry(settings.value("geometry").toByteArray()); if (settings.contains("windowState")) LogParam::setMainWindowState(settings.value("windowState").toByteArray()); if (settings.contains("bandmapwidgets")) LogParam::setMainWindowBandmapWidgets(settings.value("bandmapwidgets").toString()); return true; } bool DBSchemaMigration::setSelectedProfile(const QString &tablename, const QString &profileName) { FCT_IDENTIFICATION; QSqlQuery query; if ( !query.prepare(QString("UPDATE %1 " "SET selected = CASE " " WHEN profile_name = :profileName THEN 1 " " ELSE NULL " " END " "WHERE selected = 1 OR profile_name = :profileName2").arg(tablename)) ) { qWarning() << "Cannot prepare Update statement for" << tablename; return false; } query.bindValue(":profileName", profileName); query.bindValue(":profileName2", profileName); if( !query.exec() ) { qWarning()<< "Cannot update the profile" << tablename << profileName << query.lastError(); return false; } return true; } QString DBSchemaMigration::fixIntlField(const QSqlQuery &query, const QString &columName, const QString &columnNameIntl) { FCT_IDENTIFICATION; QString retValue; QString fieldValue = query.value(columName).toString(); if ( !fieldValue.isEmpty() ) { retValue = Data::removeAccents(fieldValue); } else { retValue = Data::removeAccents(query.value(columnNameIntl).toString()); } return retValue; } bool DBSchemaMigration::refreshUploadStatusTrigger() { FCT_IDENTIFICATION; QSqlQuery query("SELECT name " "FROM PRAGMA_TABLE_INFO('contacts') c " "WHERE c.name NOT IN ('clublog_qso_upload_date', " " 'clublog_qso_upload_status', " " 'hrdlog_qso_upload_date', " " 'hrdlog_qso_upload_status', " " 'qrzcom_qso_upload_date', " " 'qrzcom_qso_upload_status', " " 'hamlogeu_qso_upload_date', " " 'hamlogeu_qso_upload_status', " " 'hamqth_qso_upload_date', " " 'hamqth_qso_upload_status')"); if ( !query.exec() ) { qWarning() << "Cannot get Contacts columns"; return false; } QStringList whenClause; QStringList afterUpdateClause; while ( query.next() ) { const QString &columnName = query.value(0).toString(); afterUpdateClause << QString("\"" + columnName + "\""); whenClause << QString("old.\"%1\" != new.\"%2\"").arg(columnName, columnName); } QSqlQuery stmt; if ( !stmt.exec("DROP TRIGGER IF EXISTS update_contacts_upload_status") ) { qWarning() << "Cannot drop all Update Contacts Upload Trigger"; return false; } auto generateListSpecificConditions = [](const QStringList &columns) { QStringList result; for (const QString &col : columns) result << QString("old.\"%1\" != new.\"%2\"").arg(col, col); return result.join(" OR "); }; if ( !stmt.exec(QString("CREATE TRIGGER update_contacts_upload_status " "AFTER UPDATE OF %1 " "ON contacts " "WHEN (%2) " "BEGIN " " UPDATE contacts " " SET hrdlog_qso_upload_status = CASE WHEN old.hrdlog_qso_upload_status = 'Y' AND (%3) THEN 'M' ELSE old.hrdlog_qso_upload_status END, " " qrzcom_qso_upload_status = CASE WHEN old.qrzcom_qso_upload_status = 'Y' THEN 'M' ELSE old.qrzcom_qso_upload_status END , " " hamlogeu_qso_upload_status = CASE WHEN old.hamlogeu_qso_upload_status = 'Y' THEN 'M' ELSE old.hamlogeu_qso_upload_status END , " " hamqth_qso_upload_status = CASE WHEN old.hamqth_qso_upload_status = 'Y' THEN 'M' ELSE old.hamqth_qso_upload_status END , " " clublog_qso_upload_status = CASE WHEN old.clublog_qso_upload_status = 'Y' AND (%4) THEN 'M' ELSE old.clublog_qso_upload_status END " " WHERE id = new.id;" " UPDATE contacts_autovalue " " SET wavelog_qso_upload_status = 'M' " " WHERE contactid = new.id AND wavelog_qso_upload_status = 'Y'; " "END").arg(afterUpdateClause.join(","), whenClause.join(" OR "), generateListSpecificConditions(HRDLogUploader::uploadedFields), generateListSpecificConditions(ClubLogUploader::uploadedFields))) ) { qWarning().noquote() << "Cannot create Update Contacts Upload Trigger" << stmt.lastQuery(); return false; } return true; } ================================================ FILE: core/Migration.h ================================================ #ifndef QLOG_CORE_MIGRATION_H #define QLOG_CORE_MIGRATION_H #include "core/LOVDownloader.h" class QProgressDialog; class DBSchemaMigration : public QObject { Q_OBJECT public: DBSchemaMigration(QObject *parent = nullptr) : QObject(parent) {} bool run(bool force = false); static bool backupAllQSOsToADX(bool force = false); static constexpr int latestVersion = 38; private: bool functionMigration(int version); bool migrate(int toVersion); int getVersion(); bool setVersion(int version); bool runSqlFile(QString filename); int tableRows(const QString &name); bool updateExternalResource(bool force = false); void updateExternalResourceProgress(QProgressDialog&, LOVDownloader&, const LOVDownloader::SourceType & sourceType, const QString &counter, bool force = false); bool fixIntlFields(); bool insertUUID(); bool fillMyDXCC(); bool createTriggers(); bool importQSLCards2DB(); bool fillCQITUZStationProfiles(); bool resetConfigs(); bool profiles2DB(); bool settings2DB(); bool removeSettings2DB(); bool setSelectedProfile(const QString &tablename, const QString &profileName); QString fixIntlField(const QSqlQuery &query, const QString &columName, const QString &columnNameIntl); bool refreshUploadStatusTrigger(); friend class MigrationSqlTest_FriendAccessor; }; #endif // QLOG_CORE_MIGRATION_H ================================================ FILE: core/NetworkNotification.cpp ================================================ #include #include "NetworkNotification.h" #include "debug.h" #include "LogParam.h" MODULE_IDENTIFICATION("qlog.ui.networknotification"); NetworkNotification::NetworkNotification(QObject *parent) : QObject(parent) { FCT_IDENTIFICATION; } QString NetworkNotification::getNotifQSOAdiAddrs() { FCT_IDENTIFICATION; return LogParam::getNetworkNotifLogQSOAddrs(); } void NetworkNotification::saveNotifQSOAdiAddrs(const QString &addresses) { FCT_IDENTIFICATION; LogParam::setNetworkNotifLogQSOAddrs(addresses); } QString NetworkNotification::getNotifDXSpotAddrs() { FCT_IDENTIFICATION; return LogParam::getNetworkNotifDXCSpotAddrs(); } void NetworkNotification::saveNotifDXSpotAddrs(const QString &addresses) { FCT_IDENTIFICATION; LogParam::setNetworkNotifDXCSpotAddrs(addresses); } QString NetworkNotification::getNotifWSJTXCQSpotAddrs() { FCT_IDENTIFICATION; return LogParam::getNetworkNotifWSJTXCQSpotAddrs(); } void NetworkNotification::saveNotifWSJTXCQSpotAddrs(const QString &addresses) { FCT_IDENTIFICATION; LogParam::setNetworkNotifWSJTXCQSpotAddrs(addresses); } QString NetworkNotification::getNotifSpotAlertAddrs() { FCT_IDENTIFICATION; return LogParam::getNetworkNotifAlertsSpotAddrs(); } void NetworkNotification::saveNotifSpotAlertAddrs(const QString &addresses) { FCT_IDENTIFICATION; LogParam::setNetworkNotifAlertsSpotAddrs(addresses); } QString NetworkNotification::getNotifRigStateAddrs() { FCT_IDENTIFICATION; return LogParam::getNetworkNotifRigStateAddrs(); } void NetworkNotification::saveNotifRigStateAddrs(const QString &addresses) { FCT_IDENTIFICATION; LogParam::setNetworkNotifRigStateAddrs(addresses); } void NetworkNotification::QSOInserted(const QSqlRecord &record) { FCT_IDENTIFICATION; qCDebug(function_parameters) << "Inserted: " << record; HostsPortString destList(getNotifQSOAdiAddrs()); if ( destList.getAddrList().size() > 0 ) { QSONotificationMsg qsoMsg(record, QSONotificationMsg::QSO_INSERT); send(qsoMsg.getJson(), destList); } } void NetworkNotification::QSOUpdated(const QSqlRecord &record) { FCT_IDENTIFICATION; qCDebug(function_parameters) << "Updated: " << record; HostsPortString destList(getNotifQSOAdiAddrs()); if ( destList.getAddrList().size() > 0 ) { QSONotificationMsg qsoMsg(record, QSONotificationMsg::QSO_UPDATE); send(qsoMsg.getJson(), destList); } } void NetworkNotification::QSODeleted(const QSqlRecord &record) { FCT_IDENTIFICATION; qCDebug(function_parameters) << "Deleted: " << record; HostsPortString destList(getNotifQSOAdiAddrs()); if ( destList.getAddrList().size() > 0 ) { QSONotificationMsg qsoMsg(record, QSONotificationMsg::QSO_DELETE); send(qsoMsg.getJson(), destList); } } void NetworkNotification::dxSpot(const DxSpot &spot) { FCT_IDENTIFICATION; qCDebug(function_parameters) << "DX Spot"; HostsPortString destList(getNotifDXSpotAddrs()); if ( destList.getAddrList().size() > 0 ) { DXSpotNotificationMsg dxSpotMsg(spot); send(dxSpotMsg.getJson(), destList); } } void NetworkNotification::wcySpot(const WCYSpot &spot) { FCT_IDENTIFICATION; qCDebug(function_parameters) << "WCY Spot"; HostsPortString destList(getNotifDXSpotAddrs()); if ( destList.getAddrList().size() > 0 ) { WCYSpotNotificationMsg WCYSpotMsg(spot); send(WCYSpotMsg.getJson(), destList); } } void NetworkNotification::wwvSpot(const WWVSpot &spot) { FCT_IDENTIFICATION; qCDebug(function_parameters) << "WWV Spot"; HostsPortString destList(getNotifDXSpotAddrs()); if ( destList.getAddrList().size() > 0 ) { WWVSpotNotificationMsg WWVSpotMsg(spot); send(WWVSpotMsg.getJson(), destList); } } void NetworkNotification::toAllSpot(const ToAllSpot &spot) { FCT_IDENTIFICATION; qCDebug(function_parameters) << "ToALL Spot"; HostsPortString destList(getNotifDXSpotAddrs()); if ( destList.getAddrList().size() > 0 ) { ToAllSpotNotificationMsg ToAllSpotMsg(spot); send(ToAllSpotMsg.getJson(), destList); } } void NetworkNotification::WSJTXCQSpot(const WsjtxEntry &spot) { FCT_IDENTIFICATION; qCDebug(function_parameters) << "WSJTX CQ Spot"; HostsPortString destList(getNotifWSJTXCQSpotAddrs()); if ( destList.getAddrList().size() > 0 ) { WSJTXCQSpotNotificationMsg dxSpotMsg(spot); send(dxSpotMsg.getJson(), destList); } } void NetworkNotification::spotAlert(const SpotAlert &spot) { FCT_IDENTIFICATION; qCDebug(function_parameters) << "Usert Alert"; HostsPortString destList(getNotifSpotAlertAddrs()); if ( destList.getAddrList().size() > 0 ) { SpotAlertNotificationMsg spotAlertMsg(spot); send(spotAlertMsg.getJson(), destList); } } void NetworkNotification::rigStatus(const Rig::Status &status) { FCT_IDENTIFICATION; qCDebug(function_parameters) << "Rig Status"; HostsPortString destList(getNotifRigStateAddrs()); if ( destList.getAddrList().size() > 0 ) { RigStatusNotificationMsg rigStatusMsg(status); send(rigStatusMsg.getJson(), destList); } } void NetworkNotification::send(const QByteArray &data, const HostsPortString &dests) { FCT_IDENTIFICATION; qCDebug(function_parameters) << QString(data); if ( data.isEmpty() ) return; const QList &addrList = dests.getAddrList(); for ( const HostPortAddress &addr : addrList ) { qCDebug(runtime) << "Sending to " << addr; udpSocket.writeDatagram(data, addr, addr.getPort()); } } GenericNotificationMsg::GenericNotificationMsg(QObject *parent) : QObject(parent) { FCT_IDENTIFICATION; msg["appid"] = "QLog"; msg["logid"] = LogParam::getLogID(); msg["time"] = QDateTime::currentMSecsSinceEpoch(); } /* QSO Notification Message * Example * { "appid":"QLog", "data":{ "operation":"insert", "rowid":355, "type":"adif", "value":"OK1TEST2022032018353620220320183557599599Testing NamePragueJO70GB152810.1264930mCWEU503Czech RepublicNNNN530m9.266243887046823NN10.12649N1.33PRAHAJO70GDmoje_noveLADAS94OK1MLG" }, "logid":"{2046e323-b340-4634-8d52-4e70a4231978}", "msgtype":"qso", "time":1647801358067 } * More info https://github.com/foldynl/QLog/wiki/Notifications */ QSONotificationMsg::QSONotificationMsg(const QSqlRecord &record, const QSOOperation operation, QObject *parent) : GenericNotificationMsg(parent) { FCT_IDENTIFICATION; QString data; QTextStream stream(&data, QIODevice::ReadWrite); /* currently only ADIF is supported */ /* ADX and JSON do not support export single record */ LogFormat* format = LogFormat::open(LogFormat::ADI, stream); if (!format) { qWarning() << "cannot created ADIF Export Formatter"; return; } //format->exportStart(); format->exportContact(record); stream.flush(); //format->exportEnd(); QJsonObject qsoData; qsoData["operation"] = QSOOperation2String.value(operation,"unknown"); qsoData["type"] = "adif"; qsoData["rowid"] = record.value(record.indexOf("id")).toInt(); qsoData["value"] = data.replace("\n", ""); msg["msgtype"] = "qso"; msg["data"] = qsoData; delete format; } /* DXC Spot Message * Example * { "appid": "QLog", "data": { "band": "40m", "comment": "tnx qso", "dx": { "call": "YB0AR", "cont": "OC", "country": "Indonesia", "cqz": 28, "dxcc": 327, "ituz": 54, "member": ["LoTW", "eQSLAG"] "pfx": "YB", "utcoffset": -7 }, "freq": "7.1880", "mode": "PHONE", "rcvtime": "20220316 20:04:30", "spotter": { "call": "G0DEF", "cont": "EU", "country": "England", "cqz": 14, "dxcc": 223, "ituz": 27, "pfx": "G", "utcoffset": 0 }, "status": "newentity" }, "logid":"{2046e323-b340-4634-8d52-4e70a4231978}", "msgtype": "dxspot", "time": 1647461070837 } * More info https://github.com/foldynl/QLog/wiki/Notifications */ DXSpotNotificationMsg::DXSpotNotificationMsg(const DxSpot &spot, QObject *parent) : GenericSpotNotificationMsg(parent) { FCT_IDENTIFICATION; QJsonObject spotData; spotData["rcvtime"] = spot.dateTime.toString("yyyyMMdd hh:mm:ss"); spotData["freq"] = qRound(spot.freq * 10000.0) / 10000.0; spotData["band"] = spot.band; spotData["mode"] = spot.modeGroupString; spotData["comment"] = spot.comment; spotData["status"] = DxccStatus2String.value(spot.status, "unknown"); QJsonObject dxInfo; dxInfo["call"] = spot.callsign; dxInfo["country"] = spot.dxcc.country; dxInfo["pfx"] = spot.dxcc.prefix; dxInfo["dxcc"] = spot.dxcc.dxcc; dxInfo["cont"] = spot.dxcc.cont; dxInfo["cqz"] = spot.dxcc.cqz; dxInfo["ituz"] = spot.dxcc.ituz; dxInfo["utcoffset"] = spot.dxcc.tz; dxInfo["member"] = QJsonArray::fromStringList(spot.memberList2StringList()); QJsonObject spotterInfo; spotterInfo["call"] = spot.spotter; spotterInfo["country"] = spot.dxcc_spotter.country; spotterInfo["pfx"] = spot.dxcc_spotter.prefix; spotterInfo["dxcc"] = spot.dxcc_spotter.dxcc; spotterInfo["cont"] = spot.dxcc_spotter.cont; spotterInfo["cqz"] = spot.dxcc_spotter.cqz; spotterInfo["ituz"] = spot.dxcc_spotter.ituz; spotterInfo["utcoffset"] = spot.dxcc_spotter.tz; spotData["spotter"] = spotterInfo; spotData["dx"] = dxInfo; msg["msgtype"] = "dxspot"; msg["data"] = spotData; } /* WSJTX Spot Message * Example * { "appid":"QLog", "data":{ "band":"80m", "comment":"CQ OK1MLG JO70", "dx":{ "call":"OK1MLG", "cont":"EU", "country":"Europe", "cqz":15, "dxcc":503, "grid":"JO70", "ituz":28, "member":["LOTW", "eQSLAG"] "pfx":"OK", "utcoffset":-2 }, "freq":"3.5730", "mode":"FT8", "rcvtime":"20220318 17:04:29", "status":"newband" }, "logid":"{2046e323-b340-4634-8d52-4e70a4231978}", "msgtype":"wsjtxcqspot", "time":1647623069705 } * More info https://github.com/foldynl/QLog/wiki/Notifications */ WSJTXCQSpotNotificationMsg::WSJTXCQSpotNotificationMsg(const WsjtxEntry &spot, QObject *parent) : GenericSpotNotificationMsg(parent) { FCT_IDENTIFICATION; QJsonObject spotData; spotData["rcvtime"] = spot.receivedTime.toString("yyyyMMdd hh:mm:ss"); spotData["freq"] = qRound(spot.freq * 10000.0) / 10000.0; spotData["band"] = spot.band; spotData["mode"] = spot.decodedMode; spotData["comment"] = spot.decode.message; spotData["status"] = DxccStatus2String.value(spot.status, "unknown"); QJsonObject dxInfo; dxInfo["call"] = spot.callsign; dxInfo["country"] = spot.dxcc.country; dxInfo["pfx"] = spot.dxcc.prefix; dxInfo["dxcc"] = spot.dxcc.dxcc; dxInfo["cont"] = spot.dxcc.cont; dxInfo["cqz"] = spot.dxcc.cqz; dxInfo["ituz"] = spot.dxcc.ituz; dxInfo["utcoffset"] = spot.dxcc.tz; dxInfo["grid"] = spot.grid; dxInfo["member"] = QJsonArray::fromStringList(spot.memberList2StringList()); spotData["dx"] = dxInfo; msg["msgtype"] = "wsjtxcqspot"; msg["data"] = spotData; } GenericSpotNotificationMsg::GenericSpotNotificationMsg(QObject *parent) : GenericNotificationMsg(parent) { FCT_IDENTIFICATION; } /* Spot Alert Message * Example * { "appid":"QLog", "data":{ "band":"17m", "comment":"CW 7 dB 25 WPM CQ ", "dx":{ "call":"ZL3CW", "cont":"OC", "country":"New Zealand", "cqz":32, "dxcc":170, "ituz":60, "member": ["LOTW", "eQSLAG"] "pfx":"ZL", "utcoffset":-12 }, "freq":18.073, "mode":"CW", "rcvtime":"20220510 08:42:34", "rules":[ "rule1", "rule2" ], "spotter":{ "call":"SM6FMB", "cont":"EU", "country":"Sweden", "cqz":14, "dxcc":284, "ituz":18, "pfx":"SM", "utcoffset":-1 }, "status":"newentity" }, "logid":"{2046e323-b340-4634-8d52-4e70a4231978}", "msgtype":"spotalert", "time":1652172154472 } * More info https://github.com/foldynl/QLog/wiki/Notifications */ SpotAlertNotificationMsg::SpotAlertNotificationMsg(const SpotAlert &alert, QObject *parent) : GenericSpotNotificationMsg(parent) { FCT_IDENTIFICATION; QJsonObject spotData; spotData["rcvtime"] = alert.spot.dateTime.toString("yyyyMMdd hh:mm:ss"); spotData["freq"] = qRound(alert.spot.freq * 10000.0) / 10000.0; spotData["band"] = alert.spot.band; spotData["mode"] = alert.spot.modeGroupString; spotData["comment"] = alert.spot.comment; spotData["status"] = DxccStatus2String.value(alert.spot.status, "unknown"); spotData["rules"] = QJsonArray::fromStringList(alert.ruleNameList); QJsonObject dxInfo; dxInfo["call"] = alert.spot.callsign; dxInfo["country"] = alert.spot.dxcc.country; dxInfo["pfx"] = alert.spot.dxcc.prefix; dxInfo["dxcc"] = alert.spot.dxcc.dxcc; dxInfo["cont"] = alert.spot.dxcc.cont; dxInfo["cqz"] = alert.spot.dxcc.cqz; dxInfo["ituz"] = alert.spot.dxcc.ituz; dxInfo["utcoffset"] = alert.spot.dxcc.tz; dxInfo["member"] = QJsonArray::fromStringList(alert.spot.memberList2StringList()); QJsonObject spotterInfo; spotterInfo["call"] = alert.spot.spotter; spotterInfo["country"] = alert.spot.dxcc_spotter.country; spotterInfo["pfx"] = alert.spot.dxcc_spotter.prefix; spotterInfo["dxcc"] = alert.spot.dxcc_spotter.dxcc; spotterInfo["cont"] = alert.spot.dxcc_spotter.cont; spotterInfo["cqz"] = alert.spot.dxcc_spotter.cqz; spotterInfo["ituz"] = alert.spot.dxcc_spotter.ituz; spotterInfo["utcoffset"] = alert.spot.dxcc_spotter.tz; spotData["spotter"] = spotterInfo; spotData["dx"] = dxInfo; msg["msgtype"] = "spotalert"; msg["data"] = spotData; } /* WCY Spot Message * Example * { "appid":"QLog", "data":{ "A":13, "Au":"no", "GMF":"act", "K":3, "R":0, "SA":"qui", "SFI":68, "expK":3, "rcvtime":"20220923 11:29:16" }, "logid":"{c804ab21-c1bf-4b7f-90a6-8927bdb10dd0}", "msgtype":"wcyspot", "time":1663932556260 } * More info https://github.com/foldynl/QLog/wiki/Notifications */ WCYSpotNotificationMsg::WCYSpotNotificationMsg(const WCYSpot &spot, QObject *parent) : GenericNotificationMsg(parent) { FCT_IDENTIFICATION; QJsonObject spotData; spotData["rcvtime"] = spot.time.toString("yyyyMMdd hh:mm:ss"); spotData["K"] = spot.KIndex; spotData["expK"] = spot.expK; spotData["A"] = spot.AIndex; spotData["R"] = spot.RIndex; spotData["SFI"] = spot.SFI; spotData["SA"] = spot.SA; spotData["GMF"] = spot.GMF; spotData["Au"] = spot.Au; msg["msgtype"] = "wcyspot"; msg["data"] = spotData; } /* WWVSpot Message * Example * { "appid":"QLog", "data":{ "A":12, "Info1":"No Storms", "Info2":"No Storms", "K":2, "SFI":68, "rcvtime":"20220923 11:29:16" }, "logid":"{c804ab21-c1bf-4b7f-90a6-8927bdb10dd0}", "msgtype":"wwvspot", "time":1663932556294 } * More info https://github.com/foldynl/QLog/wiki/Notifications */ WWVSpotNotificationMsg::WWVSpotNotificationMsg(const WWVSpot &spot, QObject *parent) : GenericNotificationMsg(parent) { FCT_IDENTIFICATION; QJsonObject spotData; spotData["rcvtime"] = spot.time.toString("yyyyMMdd hh:mm:ss"); spotData["SFI"] = spot.SFI; spotData["A"] = spot.AIndex; spotData["K"] = spot.KIndex; spotData["Info1"] = spot.info1; spotData["Info2"] = spot.info2; msg["msgtype"] = "wwvspot"; msg["data"] = spotData; } /* ToAllSpot Message * Example * { "appid":"QLog", "data":{ "message":"GB7RDX New Users Welcome. cluster.g3ldi.co.uk Port 7000", "rcvtime":"20220923 11:29:16", "spotter":{ "call":"G3LDI", "cont":"EU", "country":"England", "cqz":14, "dxcc":223, "ituz":27, "pfx":"G", "utcoffset":0 } }, "logid":"{c804ab21-c1bf-4b7f-90a6-8927bdb10dd0}", "msgtype":"toallspot", "time":1663932556327 } * More info https://github.com/foldynl/QLog/wiki/Notifications */ ToAllSpotNotificationMsg::ToAllSpotNotificationMsg(const ToAllSpot &spot, QObject *parent) : GenericNotificationMsg(parent) { FCT_IDENTIFICATION; QJsonObject spotData; spotData["rcvtime"] = spot.time.toString("yyyyMMdd hh:mm:ss"); spotData["message"] = spot.message; QJsonObject spotterInfo; spotterInfo["call"] = spot.spotter; spotterInfo["country"] = spot.dxcc_spotter.country; spotterInfo["pfx"] = spot.dxcc_spotter.prefix; spotterInfo["dxcc"] = spot.dxcc_spotter.dxcc; spotterInfo["cont"] = spot.dxcc_spotter.cont; spotterInfo["cqz"] = spot.dxcc_spotter.cqz; spotterInfo["ituz"] = spot.dxcc_spotter.ituz; spotterInfo["utcoffset"] = spot.dxcc_spotter.tz; spotData["spotter"] = spotterInfo; msg["msgtype"] = "toallspot"; msg["data"] = spotData; } /* Rig Status Message * Example * { "appid":"QLog", "msgtype":"rigstatus", "time":1647197319251, "data":{ "profile" : "IC7300", "connected" : true, "txvfo" : "CURR", "rxvfo" : "CURR", -- TX/RX VFOs have the same value - split is not supported "txpower" : 0.01 "keyspeed" : 23 "vfostates" : [ { "vfo" : "CURR", "freq" : "7.18800" "mode" : "FM", "rawmode" : "FM", "ptt" : false, "rit" : 0.1 "xit" : 0.0 "bandwidth" : 2400 }, { .... -- currently, the second VFO is not present } ] }, "logid":"{2046e323-b340-4634-8d52-4e70a4231978}" } * Empty/Zero fields are not sent. Sent is everything that is known at the given moment of event. * More info https://github.com/foldynl/QLog/wiki/Notifications */ RigStatusNotificationMsg::RigStatusNotificationMsg(const Rig::Status &status, QObject *parent) : GenericNotificationMsg(parent) { FCT_IDENTIFICATION; QJsonObject vfoState; auto addIfNoEmpty = [&](const QString &key, const QString &value, bool addCond = true) { if ( !value.isEmpty() && addCond) vfoState[key] = value; }; auto addBoolIfNoEmpty = [&](const QString &key, const qint8 &value, bool addCond = true) { if ( addCond ) vfoState[key] = ( value != 0 ); }; auto addDoubleIfNoEmpty = [&](const QString &key, const double &value, bool addCond = true) { if ( value != 0.0 && addCond ) vfoState[key] = value; }; addIfNoEmpty("vfo", status.vfo); addIfNoEmpty("freq", QString::number(status.freq, 'f', 5), status.freq != 0.0); addIfNoEmpty("mode", status.mode); addIfNoEmpty("submode", status.submode); addIfNoEmpty("rawmode", status.rawmode); addBoolIfNoEmpty("ptt", status.ptt, status.ptt != -1); addDoubleIfNoEmpty("rit", status.rit); addDoubleIfNoEmpty("xit", status.xit); addDoubleIfNoEmpty("bandwidth", status.bandwidth); QJsonArray vfoStates; vfoStates.append(vfoState); QJsonObject rigData; rigData["profile"] = status.profile; rigData["connected"] = status.isConnected; if ( !status.vfo.isEmpty() ) { rigData["txvfo"] = status.vfo; rigData["rxvfo"] = status.vfo; } rigData["split"] = status.splitEnabled; if ( status.splitEnabled && status.txFreq > 0.0 ) rigData["txfreq"] = status.txFreq; if ( status.power > 0.0 ) rigData["txpower"] = status.power; if ( vfoState.size() > 0 ) rigData["vfostates"] = vfoStates; if ( status.keySpeed > 0 ) rigData["keyspeed"] = status.keySpeed; msg["msgtype"] = "rigstatus"; msg["data"] = rigData; } ================================================ FILE: core/NetworkNotification.h ================================================ #ifndef QLOG_CORE_NETWORKNOTIFICATION_H #define QLOG_CORE_NETWORKNOTIFICATION_H #include #include #include #include #include #include "data/HostsPortString.h" #include "logformat/LogFormat.h" #include "data/DxSpot.h" #include "data/WsjtxEntry.h" #include "data/SpotAlert.h" #include "data/WCYSpot.h" #include "data/WWVSpot.h" #include "data/ToAllSpot.h" #include "rig/Rig.h" class GenericNotificationMsg : public QObject { Q_OBJECT public: explicit GenericNotificationMsg(QObject *parent = nullptr); QByteArray getJson() const { return QJsonDocument(msg).toJson(QJsonDocument::Compact); }; protected: QJsonObject msg; }; class QSONotificationMsg : public GenericNotificationMsg { public: enum QSOOperation { QSO_INSERT = 0, QSO_UPDATE = 1, QSO_DELETE = 2 }; explicit QSONotificationMsg(const QSqlRecord&, const QSOOperation, QObject *parent = nullptr); private: QMap QSOOperation2String = { {QSOOperation::QSO_INSERT, "insert"}, {QSOOperation::QSO_UPDATE, "update"}, {QSOOperation::QSO_DELETE, "delete"}, }; }; class GenericSpotNotificationMsg : public GenericNotificationMsg { public: explicit GenericSpotNotificationMsg(QObject *parent = nullptr); protected: QMap DxccStatus2String = { {DxccStatus::NewEntity, "newentity"}, {DxccStatus::NewBandMode, "newbandmode"}, {DxccStatus::NewBand, "newband"}, {DxccStatus::NewMode, "newmode"}, {DxccStatus::NewSlot, "newslot"}, {DxccStatus::Worked, "worked"}, {DxccStatus::Confirmed, "confirmed"}, {DxccStatus::UnknownStatus, "unknown"}, }; }; class DXSpotNotificationMsg : public GenericSpotNotificationMsg { public: explicit DXSpotNotificationMsg(const DxSpot&, QObject *parent = nullptr); }; class WSJTXCQSpotNotificationMsg : public GenericSpotNotificationMsg { public: explicit WSJTXCQSpotNotificationMsg(const WsjtxEntry&, QObject *parent = nullptr); }; class SpotAlertNotificationMsg : public GenericSpotNotificationMsg { public: explicit SpotAlertNotificationMsg(const SpotAlert&alert, QObject *parent = nullptr); }; class WCYSpotNotificationMsg : public GenericNotificationMsg { public: explicit WCYSpotNotificationMsg(const WCYSpot&, QObject *parent = nullptr); }; class WWVSpotNotificationMsg : public GenericNotificationMsg { public: explicit WWVSpotNotificationMsg(const WWVSpot&, QObject *parent = nullptr); }; class ToAllSpotNotificationMsg : public GenericNotificationMsg { public: explicit ToAllSpotNotificationMsg(const ToAllSpot&, QObject *parent = nullptr); }; class RigStatusNotificationMsg : public GenericNotificationMsg { public: explicit RigStatusNotificationMsg(const Rig::Status&, QObject *parent = nullptr); }; class NetworkNotification : public QObject { Q_OBJECT public: explicit NetworkNotification(QObject *parent = nullptr); static QString getNotifQSOAdiAddrs(); static void saveNotifQSOAdiAddrs(const QString &); static QString getNotifDXSpotAddrs(); static void saveNotifDXSpotAddrs(const QString &); static QString getNotifWSJTXCQSpotAddrs(); static void saveNotifWSJTXCQSpotAddrs(const QString &); static QString getNotifSpotAlertAddrs(); static void saveNotifSpotAlertAddrs(const QString &); static QString getNotifRigStateAddrs(); static void saveNotifRigStateAddrs(const QString &); public slots: void QSOInserted(const QSqlRecord &); void QSOUpdated(const QSqlRecord &); void QSODeleted(const QSqlRecord &); void dxSpot(const DxSpot&); void wcySpot(const WCYSpot&); void wwvSpot(const WWVSpot&); void toAllSpot(const ToAllSpot&); void WSJTXCQSpot(const WsjtxEntry&); void spotAlert(const SpotAlert&); void rigStatus(const Rig::Status&); private: QUdpSocket udpSocket; void send(const QByteArray &, const HostsPortString &); }; #endif // QLOG_CORE_NETWORKNOTIFICATION_H ================================================ FILE: core/PasswordCipher.cpp ================================================ #include #include #include #include #include #include #include "PasswordCipher.h" #include "core/debug.h" MODULE_IDENTIFICATION("qlog.core.passwordcipher"); namespace { const char kMagic[4] = {'P','M','G','R'}; const quint8 kVersion = 1; const quint8 kKdfPBKDF2 = 1; const quint8 kAeadAES256GCM = 1; inline QByteArray toUtf8Norm(const QString& s) { return s.toUtf8(); } inline bool randFill(QByteArray& out) { return RAND_bytes(reinterpret_cast(out.data()), out.size()) == 1; } inline void logOpensslError(const char* where) { unsigned long ec = ERR_get_error(); if (ec != 0) { char buf[256] = {0}; ERR_error_string_n(ec, buf, sizeof(buf)); qCDebug(runtime) << where << "openssl:" << buf; } else { qCDebug(runtime) << where << "failed"; } } } void PasswordCipher::cleanse(QByteArray& buf) { FCT_IDENTIFICATION; if (!buf.isEmpty()) OPENSSL_cleanse(buf.data(), static_cast(buf.size())); } bool PasswordCipher::kdf_pbkdf2_sha256(const QByteArray& passUtf8, const QByteArray& salt, int iters, QByteArray& outKey32) { FCT_IDENTIFICATION; outKey32.resize(kKeyLen); int ok = PKCS5_PBKDF2_HMAC(passUtf8.constData(), passUtf8.size(), reinterpret_cast(salt.constData()), salt.size(), iters, EVP_sha256(), outKey32.size(), reinterpret_cast(outKey32.data())); if (ok != 1) { logOpensslError("PKCS5_PBKDF2_HMAC"); outKey32.clear(); return false; } return true; } bool PasswordCipher::aes_gcm_encrypt(const QByteArray& key, const QByteArray& iv, const QByteArray& pt, QByteArray& ct, QByteArray& tag) { FCT_IDENTIFICATION; EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); if ( !ctx ) { logOpensslError("EVP_CIPHER_CTX_new"); return false; } bool ok = false; do { if (EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), nullptr, nullptr, nullptr) != 1) { logOpensslError("EncryptInit"); break; } if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, iv.size(), nullptr) != 1) { logOpensslError("SET_IVLEN"); break; } if (EVP_EncryptInit_ex(ctx, nullptr, nullptr, reinterpret_cast(key.constData()), reinterpret_cast(iv.constData())) != 1) { logOpensslError("EncryptInit key/iv"); break; } ct.resize(pt.size()); int outl = 0, total = 0; if (EVP_EncryptUpdate(ctx, reinterpret_cast(ct.data()), &outl, reinterpret_cast(pt.constData()), pt.size()) != 1) { logOpensslError("EncryptUpdate"); break; } total += outl; if (EVP_EncryptFinal_ex(ctx, reinterpret_cast(ct.data()) + total, &outl) != 1) { logOpensslError("EncryptFinal"); break; } total += outl; ct.resize(total); tag.resize(kTagLen); if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, tag.size(), tag.data()) != 1) { logOpensslError("GET_TAG"); break; } ok = true; } while(false); if (!ok) { cleanse(ct); cleanse(tag); } EVP_CIPHER_CTX_free(ctx); return ok; } bool PasswordCipher::aes_gcm_decrypt(const QByteArray& key, const QByteArray& iv, const QByteArray& ct, const QByteArray& tag, QByteArray& pt) { FCT_IDENTIFICATION; EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); if ( !ctx ) { logOpensslError("EVP_CIPHER_CTX_new"); return false; } bool ok = false; do { if (EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), nullptr, nullptr, nullptr) != 1) { logOpensslError("DecryptInit"); break; } if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, iv.size(), nullptr) != 1) { logOpensslError("SET_IVLEN"); break; } if (EVP_DecryptInit_ex(ctx, nullptr, nullptr, reinterpret_cast(key.constData()), reinterpret_cast(iv.constData())) != 1) { logOpensslError("DecryptInit key/iv"); break; } pt.resize(ct.size()); int outl = 0, total = 0; if (EVP_DecryptUpdate(ctx, reinterpret_cast(pt.data()), &outl, reinterpret_cast(ct.constData()), ct.size()) != 1) { logOpensslError("DecryptUpdate"); break; } total += outl; if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, tag.size(), const_cast(tag.constData())) != 1) { logOpensslError("SET_TAG"); break; } int finok = EVP_DecryptFinal_ex(ctx, reinterpret_cast(pt.data()) + total, &outl); if (finok != 1) { // Auth failed or wrong key/passphrase/iv/tag pt.clear(); logOpensslError("DecryptFinal (auth fail)"); break; } total += outl; pt.resize(total); ok = true; } while(false); EVP_CIPHER_CTX_free(ctx); return ok; } bool PasswordCipher::encrypt(const QString& passphrase, const QByteArray& plaintext, QByteArray& outBase64) { FCT_IDENTIFICATION; outBase64.clear(); if ( plaintext.isEmpty() ) return false; QByteArray pass = toUtf8Norm(passphrase); QByteArray salt(kSaltLen, 0); if (!randFill(salt)) { logOpensslError("RAND salt"); return false; } QByteArray key; if ( !kdf_pbkdf2_sha256(pass, salt, kPBKDF2Iters, key)) { cleanse(pass); return false; } QByteArray iv(kIVLen, 0); if (!randFill(iv)) { logOpensslError("RAND iv"); cleanse(key); cleanse(pass); return false; } QByteArray ct, tag; if (!aes_gcm_encrypt(key, iv, plaintext, ct, tag)) { cleanse(key); cleanse(pass); return false; } cleanse(pass); // Build blob QByteArray blob; blob.reserve(4+1+1+1+1+1+1+1+4 + salt.size() + iv.size() + ct.size() + tag.size()); QDataStream ds(&blob, QIODevice::WriteOnly); ds.setByteOrder(QDataStream::BigEndian); ds.writeRawData(kMagic, 4); ds << kVersion; ds << kKdfPBKDF2; ds << kAeadAES256GCM; ds << static_cast(salt.size()); ds << static_cast(iv.size()); ds << static_cast(tag.size()); ds << static_cast(0); // reserved ds << static_cast(kPBKDF2Iters); ds.writeRawData(salt.constData(), salt.size()); ds.writeRawData(iv.constData(), iv.size()); ds.writeRawData(ct.constData(), ct.size()); ds.writeRawData(tag.constData(), tag.size()); outBase64 = blob.toBase64(); cleanse(key); return true; } bool PasswordCipher::decrypt(const QString& passphrase, const QByteArray& inBase64, QByteArray& plaintext) { FCT_IDENTIFICATION; plaintext.clear(); QByteArray blob = QByteArray::fromBase64(inBase64); if (blob.size() < (int)(4+1+1+1+1+1+1+1+4)) return false; QDataStream ds(blob); ds.setByteOrder(QDataStream::BigEndian); char mg[4]; if (ds.readRawData(mg, 4) != 4) return false; if (memcmp(mg, kMagic, 4) != 0) return false; quint8 ver=0, kdf=0, aead=0, slen=0, ivlen=0, tlen=0, rsvd=0; quint32 iters=0; ds >> ver >> kdf >> aead >> slen >> ivlen >> tlen >> rsvd >> iters; if (ds.status()!=QDataStream::Ok) return false; if (ver != kVersion || kdf != kKdfPBKDF2 || aead != kAeadAES256GCM) return false; if (slen==0 || ivlen==0 || tlen==0) return false; if (iters < 10000) return false; // sanity QByteArray salt(slen, 0), iv(ivlen, 0); if (ds.readRawData(salt.data(), slen) != slen) return false; if (ds.readRawData(iv.data(), ivlen) != ivlen) return false; // Remaining = ct + tag int remain = blob.size() - (4+1+1+1+1+1+1+1+4 + slen + ivlen); if (remain <= tlen) return false; int ctlen = remain - tlen; QByteArray ct(ctlen, 0), tag(tlen, 0); if (ds.readRawData(ct.data(), ctlen) != ctlen) return false; if (ds.readRawData(tag.data(), tlen) != tlen) return false; QByteArray pass = toUtf8Norm(passphrase); QByteArray key; if (!kdf_pbkdf2_sha256(pass, salt, static_cast(iters), key)) { cleanse(pass); return false; } bool ok = aes_gcm_decrypt(key, iv, ct, tag, plaintext); cleanse(key); cleanse(pass); if (!ok) plaintext.clear(); return ok; } ================================================ FILE: core/PasswordCipher.h ================================================ #ifndef PASSWORDCIPHER_H #define PASSWORDCIPHER_H #include #include /* Blob layout (all big-endian where applicable): magic: "PMGR" (4 bytes) ver: 0x01 (1) kdf: 0x01 (1) // 1=PBKDF2-SHA256 aead: 0x01 (1) // 1=AES-256-GCM slen: 0x10 (1) // salt len (16) ivlen: 0x0C (1) // iv len (12) tlen: 0x10 (1) // tag len (16) rsvd: 0x00 (1) iters: uint32_be (4) // PBKDF2 iterations salt: slen bytes iv: ivlen bytes ct: N bytes tag: tlen bytes */ class PasswordCipher { public: static const int kSaltLen = 16; static const int kIVLen = 12; // GCM standard static const int kTagLen = 16; static const int kKeyLen = 32; // AES-256 static const int kPBKDF2Iters = 300000; static bool encrypt(const QString& passphrase, const QByteArray& plaintext, QByteArray& outBase64); static bool decrypt(const QString& passphrase, const QByteArray& inBase64, QByteArray& plaintext); private: static bool kdf_pbkdf2_sha256(const QByteArray& passUtf8, const QByteArray& salt, int iters, QByteArray& outKey32); static bool aes_gcm_encrypt(const QByteArray& key, const QByteArray& iv, const QByteArray& pt, QByteArray& ct, QByteArray& tag); static bool aes_gcm_decrypt(const QByteArray& key, const QByteArray& iv, const QByteArray& ct, const QByteArray& tag, QByteArray& pt); static void cleanse(QByteArray& buf); }; #endif // PASSWORDCIPHER_H ================================================ FILE: core/PlatformParameterManager.cpp ================================================ #include #include #include #include #include #include #include #include "PlatformParameterManager.h" #include "core/LogParam.h" #include "core/LogDatabase.h" #include "core/debug.h" MODULE_IDENTIFICATION("qlog.core.platformparametermanager"); QList PlatformParameterManager::knownParameters() { FCT_IDENTIFICATION; // List of known platform-dependent parameters. // These are the parameters that are stored by LogParam class return { { "services/lotw/callbook/tqsl", QObject::tr("TQSL Path"), true } }; } QList PlatformParameterManager::getParameters(const QString &importDbPath, const QString &sourcePlatform) { FCT_IDENTIFICATION; qCDebug(function_parameters) << importDbPath << sourcePlatform; QList result; const QString currentPlatform = LogDatabase::currentPlatformId(); const bool platformDiffers = (sourcePlatform != currentPlatform); const bool targetIsFlatpak = (currentPlatform == LogDatabase::PLATFORM_LINUXFLATPAK); const bool sourceIsFlatpak = (sourcePlatform == LogDatabase::PLATFORM_LINUXFLATPAK); qCDebug(runtime) << "Source platform:" << sourcePlatform << "Current platform:" << currentPlatform << "Differs:" << platformDiffers; // Open the imported database const QString connectionName = QStringLiteral("PlatformParamImport"); { QSqlDatabase importDb = QSqlDatabase::addDatabase("QSQLITE", connectionName); importDb.setDatabaseName(importDbPath); if ( !importDb.open() ) { qWarning() << "Cannot open import database:" << importDb.lastError().text(); QSqlDatabase::removeDatabase(connectionName); return result; } // params defined in LogParam const QList known = knownParameters(); for ( const PlatformParameterManager::KnownParamDef ¶m : known ) { // - If target is Flatpak: skip (path is fixed, e.g. /app/bin/tqsl) if ( param.isExecutablePath && targetIsFlatpak ) { qCDebug(runtime) << "Skipping" << param.key << "- target is Flatpak (fixed path)"; continue; } PlatformParameter p; p.key = param.key; p.displayName = param.displayName; p.requiresChange = platformDiffers; p.newValue = QString(); // Read the value from the imported database // do not use LogParam function here because the class gets value from the main DB. QSqlQuery query(importDb); if ( query.prepare("SELECT value FROM log_param WHERE name = :name") ) { query.bindValue(":name", p.key); if ( query.exec() && query.first() ) { // For executable paths from Flatpak source: path is not useful if ( param.isExecutablePath && sourceIsFlatpak ) p.currentValue = QObject::tr("(Flatpak internal path)"); else p.currentValue = query.value(0).toString(); } } result.append(p); } importDb.close(); } QSqlDatabase::removeDatabase(connectionName); return result; } void PlatformParameterManager::applyParameters(const QList ¶ms) { FCT_IDENTIFICATION; for ( const PlatformParameter &p : params ) { // Only apply if user provided a new value if ( !p.newValue.isEmpty() ) { qCDebug(runtime) << "Applying parameter:" << p.key << "=" << p.newValue; // Use LogParam to set the value in the current database // This method is called when the imported DB becomes the default DB. // Therefore, we can use the LogParam class. if ( LogParam::isLoTWTQSLPathKey(p.key) ) LogParam::setLoTWTQSLPath(p.newValue); // Add more parameter handlers here as needed } } } void PlatformParameterManager::applyFlatpakFixedPaths() { FCT_IDENTIFICATION; const QString currentPlatform = LogDatabase::currentPlatformId(); if ( currentPlatform != LogDatabase::PLATFORM_LINUXFLATPAK ) { qCDebug(runtime) << "Not Flatpak target, skipping fixed paths"; return; } qCDebug(runtime) << "Applying Flatpak fixed paths"; // Set TQSL path to fixed Flatpak location - empty is OK because flatpak has built-in value. LogParam::setLoTWTQSLPath(""); // Clear rigctld_path in all rig profiles (empty = autodetect will find /app/bin/rigctld) QSqlQuery query; if ( query.exec("UPDATE rig_profiles SET rigctld_path = NULL") ) qCDebug(runtime) << "Cleared rigctld_path in all rig profiles"; else qWarning() << "Failed to clear rigctld_path:" << query.lastError().text(); } QString PlatformParameterManager::pendingParametersPath() { return LogDatabase::dbDirectory().filePath("qlog.db.pending.params"); } QList PlatformParameterManager::getProfileParameters(const QString &importDbPath, const QString &sourcePlatform) { FCT_IDENTIFICATION; qCDebug(function_parameters) << importDbPath << sourcePlatform; QList result; const QString currentPlatform = LogDatabase::currentPlatformId(); const bool targetIsFlatpak = (currentPlatform == LogDatabase::PLATFORM_LINUXFLATPAK); const bool sourceIsFlatpak = (sourcePlatform == LogDatabase::PLATFORM_LINUXFLATPAK); const QString connectionName = QStringLiteral("ProfilePortParamImport"); { QSqlDatabase importDb = QSqlDatabase::addDatabase("QSQLITE", connectionName); importDb.setDatabaseName(importDbPath); if ( !importDb.open() ) { qWarning() << "Cannot open import database:" << importDb.lastError().text(); QSqlDatabase::removeDatabase(connectionName); return result; } // Define profile tables and their port columns struct ProfileDef { QString tableName; QString columnName; QString displayPrefix; bool isExecutablePath; // true for paths to executables (like rigctld_path) }; QList profileDefs = { { "rig_profiles", "port_pathname", QObject::tr("Rig"), false }, { "rig_profiles", "ptt_port_pathname", QObject::tr("Rig PTT"), false }, { "rig_profiles", "rigctld_path", QObject::tr("Rig rigctld"), true }, { "rot_profiles", "port_pathname", QObject::tr("Rotator"), false }, { "cwkey_profiles", "port_pathname", QObject::tr("CW Keyer"), false } }; for ( const ProfileDef &def : profileDefs ) { if ( def.isExecutablePath && targetIsFlatpak ) { qCDebug(runtime) << "Skipping" << def.columnName << "- target is Flatpak (fixed path)"; continue; } QSqlQuery query(importDb); QString sql = QString("SELECT profile_name, %1 FROM %2 WHERE %1 IS NOT NULL AND %1 != ''") .arg(def.columnName, def.tableName); if ( !query.exec(sql) ) { qCDebug(runtime) << "Query failed for" << def.tableName << ":" << query.lastError().text(); continue; } while ( query.next() ) { ProfileParameter p; p.tableName = def.tableName; p.profileName = query.value(0).toString(); p.columnName = def.columnName; p.displayName = QString("%1: %2").arg(def.displayPrefix, p.profileName); p.newValue = QString(); p.isExecutablePath = def.isExecutablePath; if ( def.isExecutablePath && sourceIsFlatpak ) p.currentValue = QObject::tr("(Flatpak internal path)"); else p.currentValue = query.value(1).toString(); result.append(p); } } importDb.close(); } QSqlDatabase::removeDatabase(connectionName); qCDebug(runtime) << "Found" << result.size() << "profile port parameters"; return result; } void PlatformParameterManager::applyProfileParameters(const QList ¶ms) { FCT_IDENTIFICATION; for ( const ProfileParameter &p : params ) { if ( p.newValue.isEmpty() ) continue; qCDebug(runtime) << "Applying profile port parameter:" << p.tableName << p.profileName << p.columnName << "=" << p.newValue; QSqlQuery query; QString sql = QString("UPDATE %1 SET %2 = :value WHERE profile_name = :profile") .arg(p.tableName, p.columnName); if ( !query.prepare(sql) ) { qWarning() << "Cannot prepare update query:" << query.lastError().text(); continue; } query.bindValue(":value", p.newValue); query.bindValue(":profile", p.profileName); if ( !query.exec() ) qWarning() << "Cannot update profile port:" << query.lastError().text(); } } bool PlatformParameterManager::saveParametersToFile(const QList ¶ms, const QList &profileParams, const QString &filePath) { FCT_IDENTIFICATION; qCDebug(function_parameters) << filePath; QJsonObject root; // Save log_param parameters QJsonArray paramArray; for ( const PlatformParameter &p : params ) { if ( !p.newValue.isEmpty() ) { QJsonObject obj; obj["key"] = p.key; obj["newValue"] = p.newValue; paramArray.append(obj); } } root["parameters"] = paramArray; // Save profile port parameters QJsonArray profileArray; for ( const ProfileParameter &p : profileParams ) { if ( !p.newValue.isEmpty() ) { QJsonObject obj; obj["tableName"] = p.tableName; obj["profileName"] = p.profileName; obj["columnName"] = p.columnName; obj["newValue"] = p.newValue; obj["isExec"] = p.isExecutablePath; profileArray.append(obj); } } root["profilePorts"] = profileArray; if ( paramArray.isEmpty() && profileArray.isEmpty() ) { qCDebug(runtime) << "No parameters to save"; return true; } QJsonDocument doc(root); QFile file(filePath); if ( !file.open(QIODevice::WriteOnly) ) { qWarning() << "Cannot open file for writing:" << filePath; return false; } file.write(doc.toJson(QJsonDocument::Compact)); file.close(); qCDebug(runtime) << "Saved" << paramArray.size() << "params and" << profileArray.size() << "profile ports to" << filePath; return true; } QList PlatformParameterManager::loadParametersFromFile(const QString &filePath) { FCT_IDENTIFICATION; qCDebug(function_parameters) << filePath; QList result; QFile file(filePath); if ( !file.exists() ) { qCDebug(runtime) << "No parameters file found"; return result; } if ( !file.open(QIODevice::ReadOnly) ) { qWarning() << "Cannot open parameters file:" << filePath; return result; } QByteArray data = file.readAll(); file.close(); QJsonParseError error; const QJsonDocument doc = QJsonDocument::fromJson(data, &error); if ( error.error != QJsonParseError::NoError ) { qWarning() << "JSON parse error:" << error.errorString(); return result; } const QJsonObject root = doc.object(); const QJsonArray array = root["parameters"].toArray(); for ( const QJsonValue &val : array ) { QJsonObject obj = val.toObject(); PlatformParameter p; p.key = obj["key"].toString(); p.newValue = obj["newValue"].toString(); p.requiresChange = false; result.append(p); } qCDebug(runtime) << "Loaded" << result.size() << "parameters from" << filePath; return result; } QList PlatformParameterManager::loadProfileParametersFromFile(const QString &filePath) { FCT_IDENTIFICATION; qCDebug(function_parameters) << filePath; QList result; QFile file(filePath); if ( !file.exists() ) { qCDebug(runtime) << "No parameters file found"; return result; } if ( !file.open(QIODevice::ReadOnly) ) { qWarning() << "Cannot open parameters file:" << filePath; return result; } QByteArray data = file.readAll(); file.close(); QJsonParseError error; const QJsonDocument doc = QJsonDocument::fromJson(data, &error); if ( error.error != QJsonParseError::NoError ) { qWarning() << "JSON parse error:" << error.errorString(); return result; } const QJsonObject root = doc.object(); const QJsonArray array = root["profilePorts"].toArray(); for ( const QJsonValue &val : array ) { QJsonObject obj = val.toObject(); ProfileParameter p; p.tableName = obj["tableName"].toString(); p.profileName = obj["profileName"].toString(); p.columnName = obj["columnName"].toString(); p.newValue = obj["newValue"].toString(); p.isExecutablePath = obj["isExec"].toBool(); result.append(p); } qCDebug(runtime) << "Loaded" << result.size() << "profile port parameters from" << filePath; return result; } ================================================ FILE: core/PlatformParameterManager.h ================================================ #ifndef QLOG_CORE_PLATFORMPARAMETERMANAGER_H #define QLOG_CORE_PLATFORMPARAMETERMANAGER_H #include #include #include struct PlatformParameter { QString key; // LogParam key (e.g. "services/lotw/callbook/tqsl") QString displayName; // Human-readable name (e.g. "TQSL Path") QString currentValue; // Value from imported DB QString newValue; // User-provided value for current platform bool requiresChange; // true if platform differs }; // Structure for profile-based port paths (rig, rotator, cwkey) struct ProfileParameter { QString tableName; // e.g. "rig_profiles" QString profileName; // e.g. "My Rig" QString columnName; // e.g. "port_pathname" QString displayName; // e.g. "Rig: My Rig - Serial Port" QString currentValue; // Value from imported DB QString newValue; // User-provided value for current platform bool isExecutablePath; // is exec - browse should be displayer }; class PlatformParameterManager { public: // Get list of platform-dependent parameters from imported DB // importDbPath: path to the imported database file // sourcePlatform: platform from which the DB was exported // Returns list of parameters that may need adjustment static QList getParameters(const QString &importDbPath, const QString &sourcePlatform); // Apply user-provided values to the current database // params: list of parameters with newValue set by user static void applyParameters(const QList ¶ms); // Apply fixed paths for Flatpak target (called during import) // Sets TQSL path to /app/bin/tqsl and clears rigctld_path in all profiles static void applyFlatpakFixedPaths(); // Get list of profile port parameters from imported DB // sourcePlatform: platform from which the DB was exported static QList getProfileParameters(const QString &importDbPath, const QString &sourcePlatform); // Apply profile port parameters to the current database static void applyProfileParameters(const QList ¶ms); // Save parameters to a JSON file for later application after restart static bool saveParametersToFile(const QList ¶ms, const QList &profileParams, const QString &filePath); // Load parameters from JSON file static QList loadParametersFromFile(const QString &filePath); // Load profile port parameters from JSON file static QList loadProfileParametersFromFile(const QString &filePath); // Get path to pending parameters file static QString pendingParametersPath(); private: // Structure for known platform-dependent parameters struct KnownParamDef { QString key; // LogParam key QString displayName; // Human-readable name bool isExecutablePath; // true for paths to executables }; // Template list of known platform-dependent parameters static QList knownParameters(); }; #endif // QLOG_CORE_PLATFORMPARAMETERMANAGER_H ================================================ FILE: core/PotaQE.cpp ================================================ #include #include #include #include "core/debug.h" #include "PotaQE.h" MODULE_IDENTIFICATION("qlog.core.potaqe"); PotaQE::PotaQE(QObject *parent) : QObject{parent} { FCT_IDENTIFICATION; refreshTimer.setInterval(REFRESHPERIOD); spotUpdater.moveToThread(&spotUpdaterThread); connect(&spotUpdaterThread, &QThread::started, &spotUpdater, &PotaAppActivatorDownloader::updateActivators); connect(&spotUpdaterThread, &QThread::started, &refreshTimer, QOverload<>::of(&QTimer::start)); connect(&spotUpdaterThread, &QThread::finished, &spotUpdater, &QObject::deleteLater); connect(&spotUpdater, &PotaAppActivatorDownloader::activatorsUpdated, this, &PotaQE::updateSpot); connect(&refreshTimer, &QTimer::timeout, &spotUpdater, &PotaAppActivatorDownloader::updateActivators); spotUpdaterThread.start(); } PotaQE::~PotaQE() { FCT_IDENTIFICATION; if ( spotUpdaterThread.isRunning() ) { spotUpdaterThread.quit(); spotUpdaterThread.wait(); } } // find park reference by callsign + freq +-5 kHz + mode. // Matching: // callsign ignores prefixes and suffixes (/P,/M,/QRP,...) (Based Callsign is used) // freq +- 5KHz const POTASpot PotaQE::findReferenceId(const Callsign &callsign, double freq) { FCT_IDENTIFICATION; const QString &baseCallsign = callsign.getBase(); if ( !callsign.isValid() || baseCallsign.isEmpty() ) return POTASpot(); QMutexLocker locker(&activatorLock); auto i = activatorSpots.constFind(baseCallsign); while ( i != activatorSpots.cend() && i.key() == baseCallsign ) { const POTASpot &spot = i.value(); if ( qAbs(spot.frequency - freq) <= FREQTOL ) return spot; i++; } return POTASpot(); } void PotaQE::updateSpot() { FCT_IDENTIFICATION; QMutexLocker locker(&activatorLock); spotUpdater.swapActivators(activatorSpots); } ================================================ FILE: core/PotaQE.h ================================================ #ifndef QLOG_CORE_POTAQE_H #define QLOG_CORE_POTAQE_H #include #include #include #include "data/Callsign.h" #include "service/potaapp/PotaApp.h" class PotaQE : public QObject { Q_OBJECT public: static PotaQE *instance() { static PotaQE instance; return &instance; }; const POTASpot findReferenceId(const Callsign &callsign, double freq); private slots: void updateSpot(); private: QThread spotUpdaterThread; PotaAppActivatorDownloader spotUpdater; QTimer refreshTimer; QMutex activatorLock; const double FREQTOL = 0.005; // in MHz const int REFRESHPERIOD = 60 * 1000; PotaAppActivatorDownloader::ActivatorStorage activatorSpots; explicit PotaQE(QObject *parent = nullptr); ~PotaQE(); }; #endif // QLOG_CORE_POTAQE_H ================================================ FILE: core/PropConditions.cpp ================================================ #include #include #include #include #include #include #include #include #include #include #include #include "PropConditions.h" #include "debug.h" #include "data/Data.h" //#define FLUX_URL "https://services.swpc.noaa.gov/products/summary/10cm-flux.json" #define K_INDEX_URL "https://www.hamqsl.com/solarxml.php" #define SOLAR_SUMMARY_IMG "https://www.hamqsl.com/solar101vhf.php" #define AURORA_MAP "https://services.swpc.noaa.gov/json/ovation_aurora_latest.json" #define MUF_POINTS "https://prop.kc2g.com/api/stations.json?maxage=2700" #define DXC_TRENDS "https://api.ure.es/v2/heatmap" // the resend mechanism was implemented only because of a issue with prop.kc2g.com // This site has IPv4 and IPv6 DNS record, and if the notebook is IPv4 only, QT uses an IPv6 // address for the first attempt and an IPv4 address for the second attempt. // This resulted in a long interval before information was obtained from this server. // Resend mechanism accelerates all this. #define RESEND_ATTEMPTS 3 //intervals are defined in seconds #define RESEND_BASE_INTERVAL 5 #define BASE_UPDATE_INTERVAL (15 * 60) // in seconds #define DXTRENDS_UPDATE_INTERVAL (5 * 60) // in seconds #define DXTRENDS_TIMEOUT 60 MODULE_IDENTIFICATION("qlog.core.conditions"); PropConditions::PropConditions(QObject *parent) : QObject(parent), agentString(QString("QLog/%1").arg(VERSION).toUtf8()) { FCT_IDENTIFICATION; nam = new QNetworkAccessManager(this); connect(nam, &QNetworkAccessManager::finished, this, &PropConditions::processReply); QTimer *timer = new QTimer(this); connect(timer, &QTimer::timeout, this, &PropConditions::update); update(); timer->start(BASE_UPDATE_INTERVAL * 1000); QTimer *timerTrends = new QTimer(this); connect(timerTrends, &QTimer::timeout, this, &PropConditions::updateDxTrends); updateDxTrends(); timerTrends->start(DXTRENDS_UPDATE_INTERVAL * 1000); connect(&dxTrendTimeoutTimer, &QTimer::timeout, this, &PropConditions::dxTrendTimeout); } void PropConditions::update() { FCT_IDENTIFICATION; nam->get(prepareRequest(QUrl(SOLAR_SUMMARY_IMG))); nam->get(prepareRequest(QUrl(K_INDEX_URL))); nam->get(prepareRequest(QUrl(AURORA_MAP))); nam->get(prepareRequest(QUrl(MUF_POINTS))); } void PropConditions::updateDxTrends() { FCT_IDENTIFICATION; dxTrendResult.clear(); dxTrendTimeoutTimer.stop(); dxTrendPendingConnections.clear(); // pending connections will be ignored. for ( const QString& continent : Data::getContinentList() ) dxTrendPendingConnections << nam->get(prepareRequest(QUrl(DXC_TRENDS + QString("/%0/15").arg(continent)))); dxTrendTimeoutTimer.start(DXTRENDS_TIMEOUT * 1000); } void PropConditions::processReply(QNetworkReply* reply) { FCT_IDENTIFICATION; QByteArray data = reply->readAll(); qCDebug(runtime) << data; int replyStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); qCDebug(runtime) << reply->error() << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute) << reply->url(); if ( reply->error() == QNetworkReply::NoError && replyStatusCode >= 200 && replyStatusCode < 300 ) { failedRequests[reply->url()] = 0; const QUrl &replyURL = reply->url(); if (replyURL == QUrl(SOLAR_SUMMARY_IMG)) { QFile file(solarSummaryFile()); if ( file.open(QIODevice::WriteOnly)) { file.write(data); file.flush(); file.close(); } } else if (replyURL == QUrl(K_INDEX_URL)) { QDomDocument doc; if ( !doc.setContent(data) ) { qWarning() << "Cannot parse response from " << K_INDEX_URL; return; } QDomNodeList solarData = doc.elementsByTagName("solardata"); QDomNode n = solarData.item(0); if ( n.isNull() ) { qWarning() << "Cannot find solardata in " << K_INDEX_URL; return; } QDomElement aindex = n.firstChildElement("aindex"); QDomElement kindex = n.firstChildElement("kindex"); QDomElement solarflux = n.firstChildElement("solarflux"); if ( !aindex.isNull() ) { a_index = aindex.text().toInt(); qCDebug(runtime) << "A-Index: " << a_index; a_index_last_update = QDateTime::currentDateTime(); emit AIndexUpdated(); } if ( !kindex.isNull() ) { k_index = kindex.text().toDouble(); qCDebug(runtime) << "K-Index: " << k_index; k_index_last_update = QDateTime::currentDateTime(); emit KIndexUpdated(); } if ( !solarflux.isNull() ) { flux = solarflux.text().toInt(); qCDebug(runtime) << "Flux: " << flux; flux_last_update = QDateTime::currentDateTime(); emit fluxUpdated(); } } else if (replyURL == QUrl(AURORA_MAP)) { auroraMap.clear(); QJsonDocument doc = QJsonDocument::fromJson(data); if ( ! doc.isNull() ) { double skipElement = 0.0; qCDebug(runtime) << "Aurora forecast Time:" << doc["Forecast Time"].toString(); const QJsonArray &jsonArray = doc["coordinates"].toArray(); for (const QJsonValue &value : jsonArray) { QJsonArray obj = value.toArray(); if ( obj.size() == 3 ) { double longitute = obj[0].toDouble(); double latitude = obj[1].toDouble(); double prob = obj[2].toDouble(); auroraMap.addPoint(longitute, latitude, prob, &skipElement); } } auroraMap_last_update = QDateTime::currentDateTime(); emit auroraMapUpdated(); } } else if (replyURL == QUrl(MUF_POINTS)) { mufMap.clear(); QJsonDocument doc = QJsonDocument::fromJson(data); if ( ! doc.isNull() ) { double skipElement = 0.0; const QJsonArray &jsonArray = doc.array(); for (const QJsonValue &value : jsonArray) { QJsonObject obj = value.toObject(); QJsonObject station = obj["station"].toObject(); double longitute = station["longitude"].toString().toDouble(); double latitude = station["latitude"].toString().toDouble(); double muf = obj["mufd"].toDouble(); mufMap.addPoint(longitute, latitude, muf, &skipElement); } mufMap_last_update = QDateTime::currentDateTime(); emit mufMapUpdated(); } } else if ( dxTrendPendingConnections.contains(reply) ) { dxTrendPendingConnections.remove(reply); QJsonDocument doc = QJsonDocument::fromJson(data); const QString &requestContinent = replyURL.path().section('/', -2, -2); if ( ! doc.isNull() ) { QJsonObject jsonObject = doc.object(); for ( auto continentIt = jsonObject.begin(); continentIt != jsonObject.end(); ++continentIt ) { const QString &toContinent = continentIt.key(); // "AF", "AS", "EU".... const QJsonObject &values = continentIt->toObject(); for ( auto valueIt = values.begin(); valueIt != values.end(); ++valueIt ) { const QString &band = valueIt.key() + "m"; // "10", "12", "15" ... int spotCount = valueIt->toString().toInt(); // Number of Spots dxTrendResult[requestContinent][toContinent][band] = spotCount; } } } if ( dxTrendPendingConnections.isEmpty() ) { dxTrendTimeoutTimer.stop(); qCDebug(runtime) << "DXTrend finalized"; emit dxTrendFinalized(dxTrendResult); } } reply->deleteLater(); emit conditionsUpdated(); } else { qCDebug(runtime) << "HTTP Status Code" << replyStatusCode; dxTrendPendingConnections.remove(reply); repeateRequest(reply->url()); reply->deleteLater(); } } void PropConditions::repeateRequest(const QUrl &url) { FCT_IDENTIFICATION; failedRequests[url]++; if ( failedRequests[url] <= RESEND_ATTEMPTS ) { int resendInterval = RESEND_BASE_INTERVAL * failedRequests[url]; qCDebug(runtime) << "Scheduled URL request resend" << resendInterval << "; URL:" << url.toString(); QTimer::singleShot(1000 * RESEND_BASE_INTERVAL * failedRequests[url], this, [this, url]() { qCDebug(runtime) << "Resending request" << url.toString(); QNetworkReply *req = nam->get(prepareRequest(url)); if ( url.toString().contains(DXC_TRENDS)) dxTrendPendingConnections << req; }); } else { qCDebug(runtime) << "Propagation - detected consecutive errors from" << url.toString(); } } QNetworkRequest PropConditions::prepareRequest(const QUrl &url) { FCT_IDENTIFICATION; QNetworkRequest req(url); req.setRawHeader("User-Agent", agentString); return req; } void PropConditions::dxTrendTimeout() { FCT_IDENTIFICATION; dxTrendTimeoutTimer.stop(); for ( auto it = dxTrendPendingConnections.begin(); it != dxTrendPendingConnections.end(); ) { QNetworkReply* reply = *it; it = dxTrendPendingConnections.erase(it); if ( reply ) { reply->abort(); reply->deleteLater(); } } dxTrendPendingConnections.clear(); dxTrendResult.clear(); emit dxTrendFinalized(dxTrendResult); // emit empty result } PropConditions::~PropConditions() { dxTrendTimeout(); nam->deleteLater(); } bool PropConditions::isFluxValid() { FCT_IDENTIFICATION; bool ret = false; qCDebug(runtime)<<"Date valid: " << flux_last_update.isValid() << " last_update: " << flux_last_update; ret = (flux_last_update.isValid() && flux_last_update.secsTo(QDateTime::currentDateTime()) < 20 * 60); qCDebug(runtime)<< "Result: " << ret; return ret; } bool PropConditions::isKIndexValid() { FCT_IDENTIFICATION; bool ret = false; qCDebug(runtime)<<"Date valid: " << k_index_last_update.isValid() << " last_update: " << k_index_last_update; ret = (k_index_last_update.isValid() && k_index_last_update.secsTo(QDateTime::currentDateTime()) < 20 * 60); qCDebug(runtime)<< "Result: " << ret; return ret; } bool PropConditions::isAIndexValid() { FCT_IDENTIFICATION; bool ret = false; qCDebug(runtime)<<"Date valid: " << a_index_last_update.isValid() << " last_update: " << a_index_last_update; ret = (a_index_last_update.isValid() && a_index_last_update.secsTo(QDateTime::currentDateTime()) < 20 * 60); qCDebug(runtime)<< "Result: " << ret; return ret; } bool PropConditions::isAuroraMapValid() { FCT_IDENTIFICATION; qCDebug(runtime)<<"Date valid: " << auroraMap_last_update.isValid() << " last_update: " << auroraMap_last_update << " aurora count: " << auroraMap.count(); bool ret = (auroraMap_last_update.isValid() && auroraMap_last_update.secsTo(QDateTime::currentDateTime()) < 20 * 60 && auroraMap.count() > 0); qCDebug(runtime)<< "Result: " << ret; return ret; } bool PropConditions::isMufMapValid() { FCT_IDENTIFICATION; qCDebug(runtime)<<"Date valid: " << mufMap_last_update.isValid() << " last_update: " << mufMap_last_update << " aurora count: " << mufMap.count(); bool ret = (mufMap_last_update.isValid() && mufMap_last_update.secsTo(QDateTime::currentDateTime()) < 20 * 60 && mufMap.count() > 0); qCDebug(runtime)<< "Result: " << ret; return ret; } int PropConditions::getFlux() { FCT_IDENTIFICATION; qCDebug(runtime)<<"Current Flux: " << flux << " last_update: " << flux_last_update; return flux; } int PropConditions::getAIndex() { FCT_IDENTIFICATION; qCDebug(runtime)<<"Current A-Index: " << a_index << " last_update: " << a_index_last_update; return a_index; } double PropConditions::getKIndex() { FCT_IDENTIFICATION; qCDebug(runtime)<<"Current K-Index: " << k_index << " last_update: " << k_index_last_update; return k_index; } QList::MapPoint> PropConditions::getAuroraPoints() const { FCT_IDENTIFICATION; return auroraMap.getMap(); } QList::MapPoint> PropConditions::getMUFPoints() const { return mufMap.getMap(); } QString PropConditions::solarSummaryFile() { FCT_IDENTIFICATION; QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)); return dir.filePath("solar101vhf.gif"); } ================================================ FILE: core/PropConditions.h ================================================ #ifndef QLOG_CORE_PROPCONDITIONS_H #define QLOG_CORE_PROPCONDITIONS_H #include #include #include #include class QNetworkAccessManager; class QNetworkReply; class QNetworkRequest; template class GenericValueMap { public: explicit GenericValueMap() {} ~GenericValueMap(){}; struct MapPoint { double longitude; double latitude; T value; }; virtual void addPoint(double longitude, double latitude, const T &value, const T *skipValue = nullptr) { if ( skipValue && value == *skipValue ) return; GenericValueMap::MapPoint point; point.longitude = longitude; point.latitude = latitude; point.value = value; map.append(point); }; QList getMap() const { return map; }; void clear() { map.clear(); }; int count() const { return map.size(); }; private: QList map; }; class PropConditions : public QObject { Q_OBJECT public: explicit PropConditions(QObject *parent = nullptr); ~PropConditions(); bool isFluxValid(); bool isKIndexValid(); bool isAIndexValid(); bool isAuroraMapValid(); bool isMufMapValid(); int getFlux(); int getAIndex(); double getKIndex(); QList::MapPoint> getAuroraPoints() const; QList::MapPoint> getMUFPoints() const; static QString solarSummaryFile(); signals: void conditionsUpdated(); void fluxUpdated(); void KIndexUpdated(); void AIndexUpdated(); void auroraMapUpdated(); void mufMapUpdated(); void dxTrendFinalized(QHash>>); public slots: void update(); void updateDxTrends(); void processReply(QNetworkReply* reply); private: QDateTime flux_last_update; QDateTime k_index_last_update; QDateTime a_index_last_update; QDateTime auroraMap_last_update; QDateTime mufMap_last_update; int flux; int a_index; double k_index; GenericValueMap auroraMap; GenericValueMap mufMap; QHash failedRequests; QSet dxTrendPendingConnections; QTimer dxTrendTimeoutTimer; QHash>> dxTrendResult; QByteArray agentString; void repeateRequest(const QUrl &); QNetworkRequest prepareRequest(const QUrl &); private slots: void dxTrendTimeout(); private: QNetworkAccessManager* nam; }; #endif // QLOG_CORE_PROPCONDITIONS_H ================================================ FILE: core/QSLPrintLabelRenderer.cpp ================================================ #include #include #include #include #include #include "QSLPrintLabelRenderer.h" #include "core/debug.h" MODULE_IDENTIFICATION("qlog.core.qslprintlabelrenderer"); QSLPrintLabelRenderer::QSLPrintLabelRenderer() { FCT_IDENTIFICATION; } void QSLPrintLabelRenderer::setTemplate(const LabelTemplate &tmpl) { FCT_IDENTIFICATION; qCDebug(function_parameters) << tmpl.name; labelTemplate = tmpl; } void QSLPrintLabelRenderer::setLabels(const QList &inLabels) { FCT_IDENTIFICATION; qCDebug(function_parameters) << labels.size(); labels = inLabels; } void QSLPrintLabelRenderer::setFooterLeft(const QString &text) { FCT_IDENTIFICATION; qCDebug(function_parameters) << text; footerLeft = text; } void QSLPrintLabelRenderer::setFooterRight(const QString &text) { FCT_IDENTIFICATION; qCDebug(function_parameters) << text; footerRight = text; } void QSLPrintLabelRenderer::setSkipLabels(int count) { FCT_IDENTIFICATION; qCDebug(function_parameters) << count; skipLabels = qMax(0, count); } void QSLPrintLabelRenderer::setPrintBorders(bool enabled) { FCT_IDENTIFICATION; qCDebug(function_parameters) << enabled; printBorders = enabled; } void QSLPrintLabelRenderer::setStyleOptions(const LabelStyleOptions &opts) { FCT_IDENTIFICATION; styleOptions = opts; } int QSLPrintLabelRenderer::labelsPerPage() const { FCT_IDENTIFICATION; return labelTemplate.cols * labelTemplate.rows; } int QSLPrintLabelRenderer::labelCount() const { FCT_IDENTIFICATION; return labels.size(); } int QSLPrintLabelRenderer::pageCount() const { FCT_IDENTIFICATION; int totalSlots = labels.size() + skipLabels; int perPage = labelsPerPage(); return ( perPage > 0 ) ? (totalSlots + perPage - 1) / perPage : 0; } qreal QSLPrintLabelRenderer::mmToUnits(const qreal mm, const QPaintDevice *device, bool yAxis) const { FCT_IDENTIFICATION; return mm * (yAxis ? device->logicalDpiY() : device->logicalDpiX()) / 25.4; // to inch - DPI (px/inch) } void QSLPrintLabelRenderer::drawLabel(QPainter *painter, const QRectF &labelRect, const QSLLabelData &label) { FCT_IDENTIFICATION; if ( !painter ) return; const QPaintDevice *device = painter->device(); if ( !device ) return; if ( printBorders ) { painter->save(); painter->setPen(QPen(Qt::black, 0.5)); painter->setBrush(Qt::NoBrush); painter->drawRect(labelRect); painter->restore(); } qreal padH = mmToUnits(2.0, device); qreal padV = mmToUnits(1.5, device, true); QRectF contentRect = labelRect.adjusted(padH, padV, -padH, -padV); if ( contentRect.width() <= 0 || contentRect.height() <= 0 ) return; // Fonts from style options QFont fontToRadio(styleOptions.sansFontFamily); fontToRadio.setPointSizeF(styleOptions.toRadioFontSize); QFont fontCallsign(styleOptions.sansFontFamily); fontCallsign.setPointSizeF(styleOptions.callsignFontSize); fontCallsign.setBold(true); QFont fontHeader(styleOptions.sansFontFamily); fontHeader.setPointSizeF(styleOptions.headerFontSize); fontHeader.setBold(true); QFont fontData(styleOptions.monoFontFamily.isEmpty() ? QFontDatabase::systemFont(QFontDatabase::FixedFont) : QFont(styleOptions.monoFontFamily)); fontData.setPointSizeF(styleOptions.dataFontSize); QFont fontFooter(styleOptions.sansFontFamily); fontFooter.setPointSizeF(styleOptions.headerFontSize); // Metrics QFontMetricsF fmToRadio(fontToRadio, device); QFontMetricsF fmCallsign(fontCallsign, device); QFontMetricsF fmHeader(fontHeader, device); QFontMetricsF fmData(fontData, device); QFontMetricsF fmFooter(fontFooter, device); // Vertical layout calculation const qreal lineToRadio = fmToRadio.height(); const qreal lineCallsign = fmCallsign.height(); const qreal lineHeader = fmHeader.height(); const qreal lineData = fmData.height(); const qreal lineFooter = fmFooter.height(); // Line 1: "To Radio" + callsign share the same vertical line // "To Radio" left-aligned, callsign centered const qreal line1Height = qMax(lineToRadio, lineCallsign); qreal currentY = contentRect.top(); // --- Line 1: "To Radio" + Callsign --- painter->setFont(fontToRadio); painter->setPen(Qt::black); const QRectF toRadioRect(contentRect.left(), currentY, contentRect.width(), line1Height); const QString toRadioText = styleOptions.toRadioText.isEmpty() ? "To Radio" : styleOptions.toRadioText; painter->drawText(toRadioRect, Qt::AlignLeft | Qt::AlignVCenter, toRadioText); painter->setFont(fontCallsign); painter->drawText(toRadioRect, Qt::AlignHCenter | Qt::AlignVCenter, label.callsign); currentY += line1Height; // --- Compute dynamic column widths --- // Column headers (from style options, with non-empty guard) const QString hdrDate = styleOptions.hdrDate.isEmpty() ? "Date" : styleOptions.hdrDate; const QString hdrTime = styleOptions.hdrTime.isEmpty() ? "Time" : styleOptions.hdrTime; const QString hdrBand = styleOptions.hdrBand.isEmpty() ? "Band" : styleOptions.hdrBand; const QString hdrMode = styleOptions.hdrMode.isEmpty() ? "Mode" : styleOptions.hdrMode; const QString hdrQsl = styleOptions.hdrQsl.isEmpty() ? "QSL" : styleOptions.hdrQsl; // Measure column widths using worst-case representative strings // to ensure consistent layout across all labels qreal colWidthDate = qMax(fmHeader.horizontalAdvance(hdrDate), fmData.horizontalAdvance("2025-07-26")); qreal colWidthTime = qMax(fmHeader.horizontalAdvance(hdrTime), fmData.horizontalAdvance("00:00")); qreal colWidthBand = qMax(fmHeader.horizontalAdvance(hdrBand), fmData.horizontalAdvance("160M")); qreal colWidthMode = qMax(fmHeader.horizontalAdvance(hdrMode), fmData.horizontalAdvance("SSB")); qreal colWidthQsl = qMax(fmHeader.horizontalAdvance(hdrQsl), fmData.horizontalAdvance("TNX")); const bool hasExtra = !styleOptions.extraColumnHeader.isEmpty(); qreal colWidthExtra = 0.0; if ( hasExtra ) colWidthExtra = qMax(fmHeader.horizontalAdvance(styleOptions.extraColumnHeader), fmData.horizontalAdvance("XXXXXXXXXX")); // Add inter-column spacing (1.5mm fixed gap) qreal colGap = mmToUnits(1.5, device); const qreal totalWidth = colWidthDate + colWidthTime + colWidthBand + colWidthMode + colWidthQsl + (hasExtra ? colWidthExtra : 0.0) + (hasExtra ? 5.0 : 4.0) * colGap; // Scale columns proportionally if total exceeds content width const qreal availWidth = contentRect.width(); if ( totalWidth > availWidth && totalWidth > 0 ) { qreal scale = availWidth / totalWidth; colWidthDate *= scale; colWidthTime *= scale; colWidthBand *= scale; colWidthMode *= scale; colWidthQsl *= scale; colWidthExtra *= scale; colGap *= scale; } // --- Line 2: Column headers --- const qreal headerRowHeight = lineHeader + mmToUnits(0.5, device, true); painter->setFont(fontHeader); qreal colX = contentRect.left(); painter->drawText(QRectF(colX, currentY, colWidthDate, headerRowHeight), Qt::AlignLeft | Qt::AlignVCenter, hdrDate); colX += colWidthDate + colGap; painter->drawText(QRectF(colX, currentY, colWidthTime, headerRowHeight), Qt::AlignLeft | Qt::AlignVCenter, hdrTime); colX += colWidthTime + colGap; painter->drawText(QRectF(colX, currentY, colWidthBand, headerRowHeight), Qt::AlignLeft | Qt::AlignVCenter, hdrBand); colX += colWidthBand + colGap; painter->drawText(QRectF(colX, currentY, colWidthMode, headerRowHeight), Qt::AlignLeft | Qt::AlignVCenter, hdrMode); colX += colWidthMode + colGap; painter->drawText(QRectF(colX, currentY, colWidthQsl, headerRowHeight), Qt::AlignLeft | Qt::AlignVCenter, hdrQsl); if ( hasExtra ) { colX += colWidthQsl + colGap; painter->drawText(QRectF(colX, currentY, colWidthExtra, headerRowHeight), Qt::AlignLeft | Qt::AlignVCenter, styleOptions.extraColumnHeader); } currentY += headerRowHeight; // --- QSO data rows (up to maxQsoRows) --- const qreal dataRowHeight = lineData + mmToUnits(0.3, device, true); painter->setFont(fontData); const int maxRows = qMin(label.qsos.size(), styleOptions.maxQsoRows); for ( int i = 0; i < styleOptions.maxQsoRows; ++i ) { if ( i < maxRows ) { const QSLLabelData::QsoRow &row = label.qsos.at(i); colX = contentRect.left(); painter->drawText(QRectF(colX, currentY, colWidthDate, dataRowHeight), Qt::AlignLeft | Qt::AlignVCenter, row.date); colX += colWidthDate + colGap; painter->drawText(QRectF(colX, currentY, colWidthTime, dataRowHeight), Qt::AlignLeft | Qt::AlignVCenter, row.time); colX += colWidthTime + colGap; painter->drawText(QRectF(colX, currentY, colWidthBand, dataRowHeight), Qt::AlignLeft | Qt::AlignVCenter, row.band); colX += colWidthBand + colGap; painter->drawText(QRectF(colX, currentY, colWidthMode, dataRowHeight), Qt::AlignLeft | Qt::AlignVCenter, row.mode); colX += colWidthMode + colGap; painter->drawText(QRectF(colX, currentY, colWidthQsl, dataRowHeight), Qt::AlignLeft | Qt::AlignVCenter, row.qsl); if ( hasExtra ) { colX += colWidthQsl + colGap; painter->drawText(QRectF(colX, currentY, colWidthExtra, dataRowHeight), Qt::AlignLeft | Qt::AlignVCenter, row.extra); } } currentY += dataRowHeight; } // --- Footer line --- const QRectF footerRect(contentRect.left(), contentRect.bottom() - lineFooter, contentRect.width(), lineFooter); painter->setFont(fontFooter); if ( !footerLeft.isEmpty() ) painter->drawText(footerRect, Qt::AlignLeft | Qt::AlignVCenter, footerLeft); if ( !footerRight.isEmpty() ) painter->drawText(footerRect, Qt::AlignRight | Qt::AlignVCenter, footerRight); } void QSLPrintLabelRenderer::drawPage(QPainter *painter, int pageIndex) { FCT_IDENTIFICATION; qCDebug(function_parameters) << pageIndex; if ( !painter ) return; QPaintDevice *device = painter->device(); if ( !device ) return; const int perPage = labelsPerPage(); if ( perPage <= 0 ) return; const int slotStart = pageIndex * perPage; for ( int row = 0; row < labelTemplate.rows; ++row ) { for ( int col = 0; col < labelTemplate.cols; ++col ) { const int slotIndex = slotStart + row * labelTemplate.cols + col; const int labelIndex = slotIndex - skipLabels; // Skip blank positions (for skip labels on first page) if ( labelIndex < 0 ) continue; // Past all labels if ( labelIndex >= labels.size() ) return; const qreal xMm = labelTemplate.leftMarginMm + col * (labelTemplate.labelWidthMm + labelTemplate.hSpacingMm); const qreal yMm = labelTemplate.topMarginMm + row * (labelTemplate.labelHeightMm + labelTemplate.vSpacingMm); const qreal x = mmToUnits(xMm, device); const qreal y = mmToUnits(yMm, device, true); const qreal w = mmToUnits(labelTemplate.labelWidthMm, device); const qreal h = mmToUnits(labelTemplate.labelHeightMm, device, true); const QRectF labelRect(x, y, w, h); drawLabel(painter, labelRect, labels.at(labelIndex)); } } } QImage QSLPrintLabelRenderer::renderPage(int pageIndex, int dpi) { FCT_IDENTIFICATION; qCDebug(function_parameters) << pageIndex << dpi; if ( dpi <= 0 ) { qCWarning(runtime) << "Invalid DPI" << dpi; return QImage(); } if ( pageIndex < 0 || pageIndex >= pageCount() ) { qCWarning(runtime) << "Invalid page index" << pageIndex; return QImage(); } QSizeF pageSizeMm = QPageSize(labelTemplate.pageSize).size(QPageSize::Millimeter); if ( labelTemplate.orientation == QPageLayout::Landscape ) pageSizeMm.transpose(); const int widthPx = qRound(pageSizeMm.width() * dpi / 25.4); const int heightPx = qRound(pageSizeMm.height() * dpi / 25.4); QImage image(widthPx, heightPx, QImage::Format_ARGB32_Premultiplied); image.setDotsPerMeterX(qRound(dpi / 25.4 * 1000.0)); image.setDotsPerMeterY(qRound(dpi / 25.4 * 1000.0)); image.fill(Qt::white); QPainter painter(&image); painter.setRenderHint(QPainter::Antialiasing, true); painter.setRenderHint(QPainter::TextAntialiasing, true); drawPage(&painter, pageIndex); painter.end(); return image; } void QSLPrintLabelRenderer::printAll(QPrinter *printer) { FCT_IDENTIFICATION; if ( !printer ) { qCWarning(runtime) << "Null printer"; return; } const int pages = pageCount(); if ( pages <= 0 ) { qCWarning(runtime) << "No pages to print"; return; } printer->setResolution(PRINTER_RESOLUTION); const QPageLayout layout(QPageSize(labelTemplate.pageSize), labelTemplate.orientation, QMarginsF(0, 0, 0, 0), QPageLayout::Millimeter); printer->setPageLayout(layout); QPainter painter(printer); if ( !painter.isActive() ) { qCWarning(runtime) << "Cannot begin painting on printer"; return; } for ( int i = 0; i < pages; ++i ) { if ( i > 0 ) printer->newPage(); drawPage(&painter, i); } painter.end(); } QList QSLPrintLabelRenderer::predefinedTemplates() { FCT_IDENTIFICATION; static const QList templates = { {"Avery 3664", QPageLayout::Portrait, QPageSize::A4, 3, 8, 70.0, 33.8, 4.3, 0.0, 0.0, 0.0}, {"Avery 3422", QPageLayout::Portrait, QPageSize::A4, 3, 8, 70.0, 35.0, 8.5, 0.0, 0.0, 0.0}, {"Avery 3474", QPageLayout::Portrait, QPageSize::A4, 2, 12, 105.0, 24.0, 4.5, 0.0, 0.0, 0.0}, {"Avery 5160 / 8160", QPageLayout::Portrait, QPageSize::Letter, 3, 10, 66.7, 25.4, 12.7, 4.8, 3.2, 0.0}, {"Avery 5163 / 8163", QPageLayout::Portrait, QPageSize::Letter, 2, 5, 101.6, 50.8, 12.70, 4.762, 3.175, 0.0}, {"Avery L7160", QPageLayout::Portrait, QPageSize::A4, 3, 7, 63.5, 38.1, 15.15, 7.25, 2.54, 0.0}, {"Avery L7161", QPageLayout::Portrait, QPageSize::A4, 3, 5, 63.5, 46.6, 15.15, 7.25, 2.54, 0.0}, {"Avery 5164", QPageLayout::Portrait, QPageSize::Letter, 2, 3, 101.6, 84.67, 12.70, 4.762, 3.175, 0.0}, }; return templates; } ================================================ FILE: core/QSLPrintLabelRenderer.h ================================================ #ifndef QLOG_CORE_QSLPRINTLABELRENDERER_H #define QLOG_CORE_QSLPRINTLABELRENDERER_H #include #include #include #include #include #include class QPainter; class QPrinter; class QPaintDevice; struct LabelTemplate { QString name; QPageLayout::Orientation orientation; QPageSize::PageSizeId pageSize; int cols; int rows; double labelWidthMm; double labelHeightMm; double topMarginMm; double leftMarginMm; double hSpacingMm; double vSpacingMm; }; struct LabelStyleOptions { QString sansFontFamily; // empty = system default QString monoFontFamily; // empty = system fixed font qreal toRadioFontSize = 7.5; qreal callsignFontSize = 14.0; qreal headerFontSize = 7.0; qreal dataFontSize = 8.0; QString extraColumnHeader; // empty = no extra column int maxQsoRows = 4; // 1-4 QSO data rows per label QString toRadioText = "To Radio"; QString hdrDate = "Date"; QString hdrTime = "Time"; QString hdrBand = "Band"; QString hdrMode = "Mode"; QString hdrQsl = "QSL"; }; struct QSLLabelData { QString callsign; struct QsoRow { QString date; // formatted by dialog according to user date format QString time; // "09:38" QString band; // "20M" QString mode; // "SSB" QString qsl; // "PSE" or "TNX" QString extra; // raw DB value for extra column, empty if unused }; QList qsos; }; class QSLPrintLabelRenderer { public: QSLPrintLabelRenderer(); void setTemplate(const LabelTemplate &tmpl); void setLabels(const QList &inLabels); void setFooterLeft(const QString &text); void setFooterRight(const QString &text); void setSkipLabels(int count); void setPrintBorders(bool enabled); void setStyleOptions(const LabelStyleOptions &opts); int pageCount() const; int labelCount() const; QImage renderPage(int pageIndex, int dpi = 150); void printAll(QPrinter *printer); static QList predefinedTemplates(); private: void drawLabel(QPainter *painter, const QRectF &labelRect, const QSLLabelData &label); void drawPage(QPainter *painter, int pageIndex); qreal mmToUnits(const qreal mm, const QPaintDevice *device, bool yAxis = false) const; int labelsPerPage() const; LabelTemplate labelTemplate; QList labels; QString footerLeft; QString footerRight; int skipLabels = 0; bool printBorders = false; LabelStyleOptions styleOptions; const int PRINTER_RESOLUTION = 300; }; #endif // QLOG_CORE_QSLPRINTLABELRENDERER_H ================================================ FILE: core/QSLStorage.cpp ================================================ #include #include #include #include #include #include "QSLStorage.h" #include "core/debug.h" MODULE_IDENTIFICATION("qlog.core.qslstorage"); QSLStorage::QSLStorage(QObject *parent) : QObject(parent) { FCT_IDENTIFICATION; } bool QSLStorage::add(const QSLObject &qslObject) { FCT_IDENTIFICATION; qCDebug(function_parameters) << qslObject.getQSOID() << qslObject.getSource() << qslObject.getQSLName(); QSqlQuery insert; if ( !insert.prepare("REPLACE INTO contacts_qsl_cards (contactid, source, name, data) " " VALUES (:contactid, :source, :name, :data)" ) ) { qCDebug(runtime) << " Cannot prepare INSERT for PaperQSL " << insert.lastError(); return false; } insert.bindValue(":contactid", qslObject.getQSOID()); insert.bindValue(":source", qslObject.getSource()); insert.bindValue(":name", qslObject.getQSLName()); insert.bindValue(":data", qslObject.getBLOB(QSLObject::BASE64FORM)); if ( !insert.exec() ) { qCDebug(runtime) << "Cannot import QSL" << insert.lastError(); return false; } return true; } bool QSLStorage::remove(const QSqlRecord &qso, const QSLObject::SourceType source, const QString &qslName) { FCT_IDENTIFICATION; QSqlQuery query; if ( !query.prepare("DELETE FROM contacts_qsl_cards " "WHERE source = :source " "AND contactid = :contactid " "AND name = :qsl_name")) { qCDebug(runtime) << "Cannot prepare SQL Statement"; return false; } query.bindValue(":source", source); query.bindValue(":contactid", qso.value("id")); query.bindValue(":qsl_name", qslName); if ( !query.exec() ) { qCDebug(runtime) << "Cannot delete QSL file" << qslName; return false; } return true; } QStringList QSLStorage::getAvailableQSLNames(const QSqlRecord &qso, const QSLObject::SourceType sourceFilter) const { FCT_IDENTIFICATION; QStringList ret; QSqlQuery query; if ( !query.prepare("SELECT name FROM contacts_qsl_cards " "WHERE source = :source " "AND contactid = :contactid " "ORDER BY name")) { qCDebug(runtime) << "Cannot prepare SQL Statement"; return ret; } query.bindValue(":source", sourceFilter); query.bindValue(":contactid", qso.value("id")); if ( query.exec() ) { while(query.next()) { ret << query.value(0).toString(); } } else { qCDebug(runtime) << "Error" << query.lastError(); } return ret; } QSLObject QSLStorage::getQSL(const QSqlRecord &qso, const QSLObject::SourceType source, const QString &qslName) const { FCT_IDENTIFICATION; QSqlQuery query; if ( !query.prepare("SELECT data FROM contacts_qsl_cards " "WHERE source = :source " "AND contactid = :contactid " "AND name = :qsl_name " "ORDER BY name LIMIT 1")) { qCDebug(runtime) << "Cannot prepare SQL Statement"; } else { query.bindValue(":source", source); query.bindValue(":contactid", qso.value("id")); query.bindValue(":qsl_name", qslName); if ( query.exec() && query.next() ) return QSLObject(qso, source, qslName, query.value(0).toByteArray(), QSLObject::BASE64FORM); } return QSLObject (qso, source, qslName, QByteArray(), QSLObject::RAWBYTES); } QSLStorage::FilterValues QSLStorage::getDistinctFilterValues() const { FCT_IDENTIFICATION; FilterValues ret; QSqlQuery query; if ( !query.prepare("SELECT DISTINCT translate_to_locale(c.country), strftime('%Y', c.start_time), " "strftime('%m', c.start_time), c.band, c.mode, c.cont, c.dxcc " "FROM contacts_qsl_cards q " "JOIN contacts c ON q.contactid = c.id") ) { qCDebug(runtime) << "Cannot prepare SQL Statement" << query.lastError(); return ret; } QSet bands, modes, continents; QMap> yearMonthSets; if ( query.exec() ) { while ( query.next() ) { const QString country = query.value(0).toString(); const QString year = query.value(1).toString(); const QString month = query.value(2).toString(); const QString band = query.value(3).toString(); const QString mode = query.value(4).toString(); const QString cont = query.value(5).toString(); const int dxcc = query.value(6).toInt(); if ( !country.isEmpty() && dxcc > 0 ) ret.countries.insert(country, dxcc); if ( !year.isEmpty() ) yearMonthSets[year].insert(month.isEmpty() ? "00" : month); if ( !band.isEmpty() ) bands.insert(band); if ( !mode.isEmpty() ) modes.insert(mode); if ( !cont.isEmpty() ) continents.insert(cont); } } else { qCDebug(runtime) << "Error" << query.lastError(); } ret.bands = bands.values(); ret.modes = modes.values(); ret.continents = continents.values(); // Build sorted yearMonths map for ( QMap>::const_iterator it = yearMonthSets.constBegin(); it != yearMonthSets.constEnd(); ++it ) { QStringList months = it.value().values(); std::sort(months.begin(), months.end()); ret.yearMonths.insert(it.key(), months); } std::sort(ret.bands.begin(), ret.bands.end()); std::sort(ret.modes.begin(), ret.modes.end()); std::sort(ret.continents.begin(), ret.continents.end()); return ret; } static QList executeGalleryQuery(QSqlQuery &query) { QList ret; if ( query.exec() ) { while ( query.next() ) { QSLGalleryItem item; item.contactId = query.value(0).toULongLong(); item.source = static_cast(query.value(1).toInt()); item.name = query.value(2).toString(); item.callsign = query.value(3).toString(); item.startTime = query.value(4).toDateTime(); item.country = query.value(5).toString(); item.favorite = query.value(6).toBool(); ret << item; } } return ret; } QList QSLStorage::getGalleryItems() const { FCT_IDENTIFICATION; QSqlQuery query; if ( !query.prepare(galleryBaseSQL + "ORDER BY c.start_time DESC") ) { qCDebug(runtime) << "Cannot prepare SQL Statement" << query.lastError(); return QList(); } QList ret = executeGalleryQuery(query); if ( ret.isEmpty() ) qCDebug(runtime) << "No gallery items found or error" << query.lastError(); return ret; } QList QSLStorage::getGalleryItemsByDxcc(int dxcc) const { FCT_IDENTIFICATION; qCDebug(function_parameters) << dxcc; QSqlQuery query; if ( !query.prepare(galleryBaseSQL + "WHERE c.dxcc = :dxcc ORDER BY c.start_time DESC") ) { qCDebug(runtime) << "Cannot prepare SQL Statement" << query.lastError(); return QList(); } query.bindValue(":dxcc", dxcc); QList ret = executeGalleryQuery(query); if ( ret.isEmpty() ) qCDebug(runtime) << "No gallery items for dxcc" << dxcc << query.lastError(); return ret; } QList QSLStorage::getGalleryItemsByYear(const QString &year) const { FCT_IDENTIFICATION; qCDebug(function_parameters) << year; QSqlQuery query; if ( !query.prepare(galleryBaseSQL + "WHERE strftime('%Y', c.start_time) = :year ORDER BY c.start_time DESC") ) { qCDebug(runtime) << "Cannot prepare SQL Statement" << query.lastError(); return QList(); } query.bindValue(":year", year); QList ret = executeGalleryQuery(query); if ( ret.isEmpty() ) qCDebug(runtime) << "No gallery items for year" << year << query.lastError(); return ret; } QList QSLStorage::getGalleryItemsByYearMonth(const QString &year, const QString &month) const { FCT_IDENTIFICATION; qCDebug(function_parameters) << year << month; QSqlQuery query; if ( !query.prepare(galleryBaseSQL + "WHERE strftime('%Y', c.start_time) = :year " "AND strftime('%m', c.start_time) = :month ORDER BY c.start_time DESC") ) { qCDebug(runtime) << "Cannot prepare SQL Statement" << query.lastError(); return QList(); } query.bindValue(":year", year); query.bindValue(":month", month); QList ret = executeGalleryQuery(query); if ( ret.isEmpty() ) qCDebug(runtime) << "No gallery items for" << year << month << query.lastError(); return ret; } QList QSLStorage::getGalleryItemsByBand(const QString &band) const { FCT_IDENTIFICATION; qCDebug(function_parameters) << band; QSqlQuery query; if ( !query.prepare(galleryBaseSQL + "WHERE c.band = :band ORDER BY c.start_time DESC") ) { qCDebug(runtime) << "Cannot prepare SQL Statement" << query.lastError(); return QList(); } query.bindValue(":band", band); QList ret = executeGalleryQuery(query); if ( ret.isEmpty() ) qCDebug(runtime) << "No gallery items for band" << band << query.lastError(); return ret; } QList QSLStorage::getGalleryItemsByMode(const QString &mode) const { FCT_IDENTIFICATION; qCDebug(function_parameters) << mode; QSqlQuery query; if ( !query.prepare(galleryBaseSQL + "WHERE c.mode = :mode ORDER BY c.start_time DESC") ) { qCDebug(runtime) << "Cannot prepare SQL Statement" << query.lastError(); return QList(); } query.bindValue(":mode", mode); QList ret = executeGalleryQuery(query); if ( ret.isEmpty() ) qCDebug(runtime) << "No gallery items for mode" << mode << query.lastError(); return ret; } QList QSLStorage::getGalleryItemsByContinent(const QString &continent) const { FCT_IDENTIFICATION; qCDebug(function_parameters) << continent; QSqlQuery query; if ( !query.prepare(galleryBaseSQL + "WHERE c.cont = :cont ORDER BY c.start_time DESC") ) { qCDebug(runtime) << "Cannot prepare SQL Statement" << query.lastError(); return QList(); } query.bindValue(":cont", continent); QList ret = executeGalleryQuery(query); if ( ret.isEmpty() ) qCDebug(runtime) << "No gallery items for continent" << continent << query.lastError(); return ret; } QByteArray QSLStorage::getQSLData(qulonglong contactId, int source, const QString &name) const { FCT_IDENTIFICATION; qCDebug(function_parameters) << contactId << source << name; QSqlQuery query; if ( !query.prepare("SELECT data FROM contacts_qsl_cards " "WHERE contactid = :contactid AND source = :source AND name = :name " "LIMIT 1") ) { qCDebug(runtime) << "Cannot prepare SQL Statement" << query.lastError(); return QByteArray(); } query.bindValue(":contactid", static_cast(contactId)); query.bindValue(":source", source); query.bindValue(":name", name); if ( query.exec() && query.next() ) return QByteArray::fromBase64(query.value(0).toByteArray()); qCDebug(runtime) << "QSL data not found" << query.lastError(); return QByteArray(); } QList QSLStorage::getGalleryItemsFavorite() const { FCT_IDENTIFICATION; QSqlQuery query; if ( !query.prepare(galleryBaseSQL + "WHERE q.favorite = 1 ORDER BY c.start_time DESC") ) { qCDebug(runtime) << "Cannot prepare SQL Statement" << query.lastError(); return QList(); } QList ret = executeGalleryQuery(query); if ( ret.isEmpty() ) qCDebug(runtime) << "No favorite gallery items found or error" << query.lastError(); return ret; } bool QSLStorage::setFavorite(qulonglong contactId, QSLObject::SourceType source, const QString &name, bool favorite) { FCT_IDENTIFICATION; qCDebug(function_parameters) << contactId << source << name << favorite; QSqlQuery query; if ( !query.prepare("UPDATE contacts_qsl_cards SET favorite = :fav " "WHERE contactid = :contactid AND source = :source AND name = :name") ) { qCDebug(runtime) << "Cannot prepare SQL Statement" << query.lastError(); return false; } query.bindValue(":fav", favorite ? 1 : 0); query.bindValue(":contactid", static_cast(contactId)); query.bindValue(":source", static_cast(source)); query.bindValue(":name", name); if ( !query.exec() ) { qCDebug(runtime) << "Cannot update favorite" << query.lastError(); return false; } return true; } bool QSLStorage::isFavorite(qulonglong contactId, QSLObject::SourceType source, const QString &name) const { FCT_IDENTIFICATION; QSqlQuery query; if ( !query.prepare("SELECT favorite FROM contacts_qsl_cards " "WHERE contactid = :contactid AND source = :source AND name = :name " "LIMIT 1") ) { qCDebug(runtime) << "Cannot prepare SQL Statement" << query.lastError(); return false; } query.bindValue(":contactid", static_cast(contactId)); query.bindValue(":source", static_cast(source)); query.bindValue(":name", name); if ( query.exec() && query.next() ) return query.value(0).toBool(); return false; } ================================================ FILE: core/QSLStorage.h ================================================ #ifndef QLOG_CORE_QSLSTORAGE_H #define QLOG_CORE_QSLSTORAGE_H #include #include #include #include #include #include class QSLObject { public: enum SourceType { QSLFILE = 0, EQSL = 1, }; enum BLOBFormat { BASE64FORM, RAWBYTES }; explicit QSLObject( const qulonglong &qsoID, const SourceType source, const QString &qslName, const QByteArray &inBlob, const BLOBFormat format) : qsoID(qsoID), source(source), qslName(qslName), blob((format == RAWBYTES) ? inBlob : QByteArray::fromBase64(inBlob)) {}; explicit QSLObject( const QSqlRecord &qso, const SourceType source, const QString &qslName, const QByteArray &inBlob, const BLOBFormat format) : QSLObject(qso.value("id").toULongLong(), source, qslName, inBlob, format) {} qulonglong getQSOID() const {return qsoID;}; QSLObject::SourceType getSource() const {return source;}; QString getQSLName() const {return qslName;}; QByteArray getBLOB(BLOBFormat format = RAWBYTES) const {return (format == BASE64FORM) ? blob.toBase64() : blob;}; private: qulonglong qsoID; SourceType source; QString qslName; QByteArray blob; }; struct QSLGalleryItem { qulonglong contactId; QSLObject::SourceType source; QString name; QString callsign; QDateTime startTime; QString country; bool favorite = false; }; class QSLStorage : public QObject { Q_OBJECT public: explicit QSLStorage(QObject *parent = nullptr); bool add(const QSLObject &); bool remove(const QSqlRecord &qso, const QSLObject::SourceType source, const QString &qslName); QStringList getAvailableQSLNames(const QSqlRecord &qso, const QSLObject::SourceType sourceFilter) const; QSLObject getQSL(const QSqlRecord &qso, const QSLObject::SourceType source, const QString &qslName) const; struct FilterValues { QMap countries; // localizedName -> dxcc_id QMap yearMonths; // year -> sorted list of months ("01".."12") QStringList bands; QStringList modes; QStringList continents; }; FilterValues getDistinctFilterValues() const; QList getGalleryItems() const; QList getGalleryItemsByDxcc(int dxcc) const; QList getGalleryItemsByYear(const QString &year) const; QList getGalleryItemsByYearMonth(const QString &year, const QString &month) const; QList getGalleryItemsFavorite() const; QList getGalleryItemsByBand(const QString &band) const; QList getGalleryItemsByMode(const QString &mode) const; QList getGalleryItemsByContinent(const QString &continent) const; QByteArray getQSLData(qulonglong contactId, int source, const QString &name) const; bool setFavorite(qulonglong contactId, QSLObject::SourceType source, const QString &name, bool favorite); bool isFavorite(qulonglong contactId, QSLObject::SourceType source, const QString &name) const; private: const QString galleryBaseSQL= "SELECT q.contactid, q.source, q.name, c.callsign, c.start_time, translate_to_locale(c.country), q.favorite " "FROM contacts_qsl_cards q " "JOIN contacts c ON q.contactid = c.id "; }; #endif // QLOG_CORE_QSLSTORAGE_H ================================================ FILE: core/QSOFilterManager.cpp ================================================ #include #include #include "QSOFilterManager.h" #include "core/debug.h" MODULE_IDENTIFICATION("qlog.core.qsofiltermanager"); QSOFilterManager::QSOFilterManager(QObject *parent) : QObject(parent), stmtsReady(true) { FCT_IDENTIFICATION; if ( !insertRuleStmt.prepare(QLatin1String("INSERT INTO qso_filter_rules(filter_name, table_field_index, operator_id, value) " "VALUES (:filterName, :tableFieldIndex, :operatorID, :valueString)") ) ) { qWarning() << "cannot preapre insert insertRuleStmt"; stmtsReady = false; } if ( !insertFilterStmt.prepare(QLatin1String("INSERT INTO qso_filters (filter_name, matching_type) VALUES (:filterName, :matchingType) " "ON CONFLICT(filter_name) DO UPDATE SET matching_type = :matchingType WHERE filter_name = :filterName") ) ) { qWarning() << "cannot preapre insert insertFilterStmt"; stmtsReady = false; } if ( !deleteFilterStmt.prepare(QLatin1String("DELETE FROM qso_filter_rules WHERE filter_name = :filterName") ) ) { qWarning() << "cannot preapre insert deleteFilterStmt"; stmtsReady = false; } } bool QSOFilterManager::deleteFilterRules(const QString &filterName) { FCT_IDENTIFICATION; qCDebug(function_parameters) << filterName; deleteFilterStmt.bindValue(":filterName", filterName); bool ret = deleteFilterStmt.exec(); if ( !ret ) qCDebug(runtime) << "SQL Error" << deleteFilterStmt.lastError().text(); return ret; } bool QSOFilterManager::replaceFilter(const QString &filterName, const int matchingType) { FCT_IDENTIFICATION; qCDebug(function_parameters) << filterName << matchingType; insertFilterStmt.bindValue(":filterName", filterName); insertFilterStmt.bindValue(":matchingType", matchingType); bool ret = insertFilterStmt.exec(); if ( !ret ) qCDebug(runtime) << "SQL Error" << insertFilterStmt.lastError().text(); return ret; } bool QSOFilterManager::insertFilterRule(const QString & filterName, const QSOFilterRule &rule) { FCT_IDENTIFICATION; qCDebug(function_parameters) << filterName << rule.tableFieldIndex << rule.operatorID << rule.value; insertRuleStmt.bindValue(":filterName", filterName); insertRuleStmt.bindValue(":tableFieldIndex", rule.tableFieldIndex); insertRuleStmt.bindValue(":operatorID", rule.operatorID); insertRuleStmt.bindValue(":valueString", (rule.value.isEmpty()) ? QVariant() : rule.value); bool ret = insertRuleStmt.exec(); if ( !ret ) qCDebug(runtime) << "SQL Error" << insertRuleStmt.lastError().text(); return ret; } bool QSOFilterManager::save(const QSOFilter &filter) { FCT_IDENTIFICATION; qCDebug(function_parameters) << filter.filterName << filter.machingType; if ( !stmtsReady ) return false; QSqlDatabase::database().transaction(); if ( !replaceFilter(filter.filterName, filter.machingType) ) { QSqlDatabase::database().rollback(); return false; } if ( !deleteFilterRules(filter.filterName) ) { QSqlDatabase::database().rollback(); return false; } for ( const QSOFilterRule &rule : filter.rules ) { if ( !insertFilterRule(filter.filterName, rule) ) { QSqlDatabase::database().rollback(); return false; } } QSqlDatabase::database().commit(); return true; } bool QSOFilterManager::remove(const QString &filterName) { FCT_IDENTIFICATION; qCDebug(function_parameters) << filterName; QSqlQuery filterStmt; if ( ! filterStmt.prepare(QLatin1String("DELETE FROM qso_filters " "WHERE filter_name = :filterName;")) ) { qWarning() << "Cannot prepare delete statement"; return false; } filterStmt.bindValue(":filterName", filterName); if ( ! filterStmt.exec() ) { qInfo()<< "Cannot get filters names from DB" << filterStmt.lastError(); return false; } return true; } QStringList QSOFilterManager::getFilterList() const { FCT_IDENTIFICATION; QStringList ret; QSqlQuery filterStmt; if ( ! filterStmt.prepare(QLatin1String("SELECT filter_name " "FROM qso_filters " "ORDER BY filter_name")) ) { qWarning() << "Cannot prepare select statement"; return ret; } if ( filterStmt.exec() ) { while ( filterStmt.next() ) ret << filterStmt.value(0).toString(); } else qInfo()<< "Cannot get filters names from DB" << filterStmt.lastError();; return ret; } QSOFilter QSOFilterManager::getFilter(const QString &filterName) const { FCT_IDENTIFICATION; qCDebug(function_parameters) << filterName; QSOFilter ret; QSqlQuery query; if ( ! query.prepare(QLatin1String("SELECT matching_type, table_field_index, operator_id, value " "FROM qso_filter_rules r, qso_filters f " "WHERE f.filter_name = :filter " " AND f.filter_name = r.filter_name")) ) { qWarning() << "Cannot prepare select statement"; return ret; } query.bindValue(":filter", filterName); if ( query.exec() ) { ret.filterName = filterName; while ( query.next() ) { QSOFilterRule rule; const QSqlRecord &record = query.record(); ret.machingType = record.value("matching_type").toInt(); rule.tableFieldIndex = record.value("table_field_index").toInt(); rule.operatorID = record.value("operator_id").toInt(); rule.value = record.value("value").toString(); ret.addRule(rule); } } else qCDebug(runtime) << "SQL execution error: " << query.lastError().text(); return ret; } QString QSOFilterManager::getWhereClause(const QString &filterName, const QString &columnPrefix) { FCT_IDENTIFICATION; QSqlQuery userFilterQuery; QString ret; QString finalColumnPfx = columnPrefix; finalColumnPfx += finalColumnPfx.isEmpty() ? "" : "."; if ( ! userFilterQuery.prepare( QString( "SELECT " "'(' || GROUP_CONCAT( ' ' || '" + finalColumnPfx + "' || c.name || ' ' || CASE WHEN r.value IS NULL AND o.sql_operator IN ('=', 'like') THEN 'IS' " " WHEN r.value IS NULL and r.operator_id NOT IN ('=', 'like') THEN 'IS NOT' " " WHEN o.sql_operator = ('starts with') THEN 'like' " " ELSE o.sql_operator END || " "' (' || quote(CASE o.sql_operator WHEN 'like' THEN '%' || r.value || '%' " " WHEN 'not like' THEN '%' || r.value || '%' " " WHEN 'starts with' THEN r.value || '%' " " ELSE r.value END) || ') ', m.sql_operator) || ')' " "FROM qso_filters f, qso_filter_rules r, " "qso_filter_operators o, qso_filter_matching_types m, " "PRAGMA_TABLE_INFO('contacts') c " "WHERE f.filter_name = :filterName " " AND f.filter_name = r.filter_name " " AND o.operator_id = r.operator_id " " AND m.matching_id = f.matching_type " " AND c.cid = r.table_field_index")) ) { qWarning() << "Cannot prepare select statement"; return ret; } userFilterQuery.bindValue(":filterName", filterName); qCDebug(runtime) << "User filter SQL: " << userFilterQuery.lastQuery(); if ( userFilterQuery.exec() ) { userFilterQuery.next(); ret = QString("( %1 )").arg(userFilterQuery.value(0).toString()); } else qCDebug(runtime) << "User filter error - " << userFilterQuery.lastError().text(); // This filter, when used with fields that contain time, only works by luck. // These fields are Timeon/Timeoff. They are stored by QSO Filter Dialog as values in the format // YYYY-MM-DDThh:mm:ss without a timezone. This is fine, since all times in QLog // are internally in UTC. The problem arises because this WHERE clause is later // used in a SELECT in the logbook. SQL does not compare the values as datetime, // but as strings — otherwise both sides would need to be proper datetime types. // Fortunately, both sides are strings in the same format, except that Timeon/Timeoff // includes a timezone at the end. Therefore, string comparison of the dates still works. return ret; } SqlListModel *QSOFilterManager::QSOFilterModel(const QString &firstValue, QObject *parent) { FCT_IDENTIFICATION; return new SqlListModel("SELECT filter_name " "FROM qso_filters " "ORDER BY filter_name COLLATE LOCALEAWARE ASC", firstValue, parent); } ================================================ FILE: core/QSOFilterManager.h ================================================ #ifndef QSOFILTERMANAGER_H #define QSOFILTERMANAGER_H #include #include #include #include "models/LogbookModel.h" #include "models/SqlListModel.h" struct QSOFilterRule { QSOFilterRule() {}; QSOFilterRule(int in_idx, int in_operatorID, const QString &in_value) : tableFieldIndex(in_idx), operatorID(in_operatorID), value(in_value){ }; int tableFieldIndex; int operatorID; QString value; }; class QSOFilter { public: QString filterName; int machingType; QList rules; QSOFilter() : machingType(0){}; void addRule(const QSOFilterRule &rule) { rules.append(rule); } static QSOFilterRule createFromDateRule(const QDateTime &date) { return QSOFilterRule(LogbookModel::COLUMN_TIME_ON, 4, date.toString("yyyy-MM-ddTHH:mm:ss")); // 4 - should be enum - later '4' > } static QSOFilterRule createNonEmptyContestRule(const QString &contestID) { return QSOFilterRule(LogbookModel::COLUMN_CONTEST_ID, 2, contestID);// '2' is like } static QSOFilterRule createToDateRule(const QDateTime &date) { return QSOFilterRule(LogbookModel::COLUMN_TIME_ON, 5, date.toString("yyyy-MM-ddTHH:mm:ss")); // 5 - should be enum - later '5' < // ON of OFF??? } static QSOFilter createFromDateContestFilter(const QString &contestID, const QDateTime &date) { QSOFilter ret; ret.filterName = QString("%1-%2").arg(contestID, date.toString("yyyy/MM/dd hh:mm")); ret.machingType = 0; // should be enum - later ret.addRule(createFromDateRule(date)); ret.addRule(createNonEmptyContestRule(contestID)); return ret; } }; class QSOFilterManager : public QObject { Q_OBJECT public: static QSOFilterManager * instance() { static QSOFilterManager instance; return &instance; } static QString getWhereClause(const QString &filterName, const QString &columnPrefix = {}); static SqlListModel* QSOFilterModel(const QString &firstValue, QObject *parent = nullptr); bool save(const QSOFilter &filter); bool remove(const QString &filterName); QStringList getFilterList() const; QSOFilter getFilter(const QString &filterName) const; private: QSOFilterManager(QObject *parent = nullptr); bool replaceFilter(const QString &filterName, const int matchingType); bool insertFilterRule(const QString &filterName, const QSOFilterRule &rule); bool deleteFilterRules(const QString &filterName); bool stmtsReady; QSqlQuery insertRuleStmt; QSqlQuery insertFilterStmt; QSqlQuery deleteFilterStmt; }; #endif // QSOFILTERMANAGER_H ================================================ FILE: core/QuadKeyCache.h ================================================ #ifndef QLOG_CORE_QUADKEYCACHE_H #define QLOG_CORE_QUADKEYCACHE_H #include #include #include #include template class QuadKeyCache : public QCache, QPair>, Value> { public: using Key = QPair, QPair>; void insert(const int keyA, const int keyB, const QString& keyC, const QString& keyD, Value* value) { Key key = qMakePair(qMakePair(keyA, keyB), qMakePair(keyC, keyD)); QCache::insert(key, value); } Value* value(const int keyA, const int keyB, const QString& keyC, const QString& keyD) const { Key key = qMakePair(qMakePair(keyA, keyB), qMakePair(keyC, keyD)); return QCache::object(key); } bool contains(const int keyA, const int keyB, const QString& keyC, const QString& keyD) const { Key key = qMakePair(qMakePair(keyA, keyB), qMakePair(keyC, keyD)); return QCache::contains(key); } int size() const { return QCache::size(); } void invalidate(const int keyA, const int keyB) { QList keysToRemove; for( const Key& key : QCache::keys() ) { if (key.first.first == keyA && key.first.second == keyB) keysToRemove.append(key); } for( const Key& key : keysToRemove ) QCache::remove(key); } }; #endif // QLOG_CORE_QUADKEYCACHE_H ================================================ FILE: core/WsjtxUDPReceiver.cpp ================================================ #include #include #include #include #include #include #include #include #include #include #include "WsjtxUDPReceiver.h" #include "data/Data.h" #include "debug.h" #include "data/HostsPortString.h" #include "rig/macros.h" #include "data/BandPlan.h" #include "logformat/AdiFormat.h" #include "core/LogParam.h" MODULE_IDENTIFICATION("qlog.core.wsjtx"); // https://github.com/saitohirga/WSJT-X/blob/master/Network/NetworkMessage.hpp WsjtxUDPReceiver::WsjtxUDPReceiver(QObject *parent) : QObject(parent), socket(new QUdpSocket(this)), isOutputColorCQSpotEnabled(false) { FCT_IDENTIFICATION; reloadSetting(); connect(socket, &QUdpSocket::readyRead, this, &WsjtxUDPReceiver::readPendingDatagrams); connect(&wsjtSQLRecord, &UpdatableSQLRecord::recordReady, this, &WsjtxUDPReceiver::contactReady); } void WsjtxUDPReceiver::openPort() { FCT_IDENTIFICATION; if ( ! socket ) { return; } if( socket->state() == QAbstractSocket::BoundState) { socket->close(); } int newPort = getConfigPort(); qCDebug(runtime) << "Listen port"<< newPort; bool multicastEnabled = getConfigMulticastJoin(); qCDebug(runtime) << (( multicastEnabled ) ? "Multicast" : "Unicast") << "enabled"; if ( multicastEnabled ) { if ( ! socket->bind(QHostAddress::AnyIPv4, newPort, QUdpSocket::ShareAddress|QUdpSocket::ReuseAddressHint) ) { qWarning() << "Cannot bind the Port for WSJTX"; } else { QHostAddress multicastAddress = QHostAddress(getConfigMulticastAddress()); const QList listNetworkIF = QNetworkInterface::allInterfaces(); /* Join Multicast Group on all Multicast-capable interfaces */ for ( const auto &networkIF : listNetworkIF ) { auto flags = QNetworkInterface::IsUp | QNetworkInterface::CanMulticast; if ( networkIF.isValid() && (networkIF.flags() & flags) ) { if ( ! socket->joinMulticastGroup(multicastAddress, networkIF) ) { qWarning() << "Cannot join the Multicast address" << networkIF.humanReadableName() << "; Address" << multicastAddress;; } else { qCDebug(runtime) << "Joined interface: " << networkIF.humanReadableName() << "; Address" << multicastAddress; } } } socket->setSocketOption(QAbstractSocket::MulticastTtlOption, getConfigMulticastTTL()); } } else { if ( ! socket->bind(QHostAddress::Any, newPort) ) { qWarning() << "Cannot bind the Port for WSJTX"; } else { qCDebug(runtime) << "Listening on all interfaces"; } } } void WsjtxUDPReceiver::forwardDatagram(const QNetworkDatagram &datagram) { FCT_IDENTIFICATION; HostsPortString forwardAddresses(getConfigForwardAddresses()); const QList &addrList = forwardAddresses.getAddrList(); for ( const HostPortAddress &addr : addrList ) { QUdpSocket udpSocket; qCDebug(runtime) << "Sending to " << addr; udpSocket.writeDatagram(datagram.data(), addr, addr.getPort()); } } float WsjtxUDPReceiver::modePeriodLength(const QString &mode) { FCT_IDENTIFICATION; float ret = 60; qCDebug(function_parameters) << mode; if ( mode == "FST4" || mode == "FT8" || mode == "MSK144" ) { ret = 15; } else if ( mode == "FT4" ) { ret = 7.5; } else if ( mode == "FT2" ) { ret = 3.8; } else if ( mode == "JT4" || mode == "JT9" || mode.contains("JT65") || mode == "QRA64" || mode == "ISCAT" ) { ret = 60; } else if ( mode == "FST4W" || mode == "WSPR" ) { ret = 120; } qCDebug(runtime) << "Period: " << ret; return ret; } quint16 WsjtxUDPReceiver::getConfigPort() { FCT_IDENTIFICATION; return LogParam::getNetworkWsjtxListenerPort(WsjtxUDPReceiver::DEFAULT_PORT); } void WsjtxUDPReceiver::saveConfigPort(quint16 port) { FCT_IDENTIFICATION; LogParam::setNetworkNotifRigStateAddrs(port); } QString WsjtxUDPReceiver::getConfigForwardAddresses() { FCT_IDENTIFICATION; return LogParam::getNetworkWsjtxForwardAddrs(); } void WsjtxUDPReceiver::saveConfigForwardAddresses(const QString &addresses) { FCT_IDENTIFICATION; LogParam::setNetworkWsjtxForwardAddrs(addresses); } void WsjtxUDPReceiver::saveConfigMulticastJoin(bool state) { FCT_IDENTIFICATION; LogParam::setNetworkWsjtxListenerJoinMulticast(state); } bool WsjtxUDPReceiver::getConfigMulticastJoin() { FCT_IDENTIFICATION; return LogParam::getNetworkWsjtxListenerJoinMulticast(); } void WsjtxUDPReceiver::saveConfigMulticastAddress(const QString &addr) { FCT_IDENTIFICATION; LogParam::setNetworkWsjtxListenerMulticastAddr(addr); } QString WsjtxUDPReceiver::getConfigMulticastAddress() { FCT_IDENTIFICATION; return LogParam::getNetworkWsjtxListenerMulticastAddr(); } void WsjtxUDPReceiver::saveConfigMulticastTTL(int ttl) { FCT_IDENTIFICATION; LogParam::setNetworkWsjtxListenerMulticastTTL(ttl); } int WsjtxUDPReceiver::getConfigMulticastTTL() { FCT_IDENTIFICATION; return LogParam::getNetworkWsjtxListenerMulticastTTL(); } bool WsjtxUDPReceiver::getConfigOutputColorCQSpot() { return LogParam::getWsjtxOutputColorCQSpot(); } void WsjtxUDPReceiver::saveConfigOutputColorCQSpot(bool status) { LogParam::setWsjtxOutputColorCQSpot(status); } void WsjtxUDPReceiver::readPendingDatagrams() { FCT_IDENTIFICATION; while (socket->hasPendingDatagrams()) { QNetworkDatagram datagram = socket->receiveDatagram(); /* remember WSJT receiving address becuase WSJT does not listen multicast address * but only UDP address. Therefore the command must be sent via UDP unicast */ wsjtxAddress = datagram.senderAddress(); wsjtxPort = datagram.senderPort(); qCDebug(runtime) << "Received from" << wsjtxAddress; QDataStream stream(datagram.data()); quint32 magic, schema, mtype; stream >> magic; stream >> schema; stream >> mtype; if (magic != 0xadbccbda) { qCDebug(runtime) << "Invalid wsjtx magic"; continue; } qCDebug(runtime) << "WSJT mtype << "<< mtype << " schema " << schema; forwardDatagram(datagram); switch (mtype) { /* WSJTX Status message */ case 1: { QByteArray id, mode, tx_mode, sub_mode, report, dx_call, dx_grid, de_call, de_grid, conf_name, tx_message; WsjtxStatus status; stream >> id >> status.dial_freq >> mode >> dx_call >> report >> tx_mode; stream >> status.tx_enabled >> status.transmitting >> status.decoding; stream >> status.rx_df >> status.tx_df >> de_call >> de_grid >> dx_grid; stream >> status.tx_watchdog >> sub_mode >> status.fast_mode >> status.special_op_mode >> status.freq_tolerance; stream >> status.tr_period >> conf_name >> tx_message; status.id = QString(id); status.mode = QString(mode); status.tx_mode = QString(mode); status.sub_mode = QString(sub_mode); status.report = QString(report); status.dx_call = QString(dx_call); status.dx_grid = QString(dx_grid); status.de_call = QString(de_call); status.de_grid = QString(de_grid); status.conf_name = QString(conf_name); status.tx_message = QString(tx_message); qCDebug(runtime)<> id >> decode.is_new >> decode.time >> decode.snr >> decode.dt >> decode.df; stream >> mode >> message >> decode.low_confidence >> decode.off_air; decode.id = QString(id); decode.mode = QString(mode); decode.message = QString(message); #if 0 if ( isJTDXId(decode.id) ) { /* It's a workaround for JTDX only. * JTDX sends the time without a time zone. Which is * interpreted by QLog as time in the local zone and * it is therefore recalculated, incorrectly, to UTC. * Therefore it is needed to add timezone to date information * received from JTDX */ // it is not needed to convert it here? } #endif qCDebug(runtime) << decode; emit decodeReceived(decode); break; } /* WSJTX Log message */ case 5: { QByteArray id, dx_call, dx_grid, mode, rprt_sent, rprt_rcvd, tx_pwr, comments; QByteArray name, op_call, my_call, my_grid, exch_sent, exch_rcvd, prop_mode; WsjtxLog log; stream >> id >> log.time_off >> dx_call >> dx_grid >> log.tx_freq >> mode >> rprt_sent; stream >> rprt_rcvd >> tx_pwr >> comments >> name >> log.time_on >> op_call; stream >> my_call >> my_grid >> exch_sent >> exch_rcvd >> prop_mode; log.id = QString(id); log.dx_call = QString(dx_call).toUpper(); log.dx_grid = QString(dx_grid).toUpper(); log.mode = QString(mode); log.rprt_sent = QString(rprt_sent); log.rprt_rcvd = QString(rprt_rcvd); log.tx_pwr = QString(tx_pwr); log.comments = QString(comments); log.name = QString(name); log.op_call = QString(op_call).toUpper(); log.my_call = QString(my_call).toUpper(); log.my_grid = QString(my_grid).toUpper(); log.exch_sent = QString(exch_sent); log.exch_rcvd = QString(exch_rcvd); log.prop_mode = QString(prop_mode); if ( isJTDXId(log.id) ) { /* It's a workaround for JTDX only. * JTDX sends the time without a time zone. Which is * interpreted by QLog as time in the local zone and * it is therefore recalculated, incorrectly, to UTC. * Therefore it is needed to add timezone to date information * received from JTDX */ qCDebug(runtime) << "JTDX detected - adding Timezone information"; log.time_on.setTimeZone(QTimeZone::utc()); log.time_off.setTimeZone(QTimeZone::utc()); } qCDebug(runtime) << log; insertContact(log); break; } /* WSJTX LogADIF message */ case 12: { QByteArray id, adif_text; WsjtxLogADIF log; const QString data = datagram.data(); if ( isCSNSat(data) ) { id = "CSN Sat"; int index = data.indexOf("> id >> adif_text; log.id = QString(id); log.log_adif = QString(adif_text); insertContact(log); break; } } } } void WsjtxUDPReceiver::insertContact(WsjtxLog log) { FCT_IDENTIFICATION; qCDebug(function_parameters) << log; QSqlTableModel model; model.setTable("contacts"); model.removeColumn(model.fieldIndex("id")); QSqlRecord record = model.record(0); double freq = Hz2MHz(static_cast(log.tx_freq)); record.setValue("freq", freq); record.setValue("band", BandPlan::freq2Band(freq).name); /* if field is empty then do not initialize it, leave it NULL * for database */ if ( !log.dx_call.isEmpty() ) { record.setValue("callsign", log.dx_call); } if ( !log.rprt_rcvd.isEmpty() ) { record.setValue("rst_rcvd", log.rprt_rcvd); } if ( !log.rprt_sent.isEmpty() ) { record.setValue("rst_sent", log.rprt_sent); } if ( !log.name.isEmpty() ) { record.setValue("name", Data::removeAccents(log.name)); record.setValue("name_intl", log.name); } if ( !log.dx_grid.isEmpty() ) { record.setValue("gridsquare", log.dx_grid); } if ( !log.mode.isEmpty() ) { QString mode = log.mode.toUpper(); QString submode; QPair legacy = Data::instance()->legacyMode(mode); if ( !legacy.first.isEmpty() ) { mode = legacy.first; submode = legacy.second; } record.setValue("mode", mode); record.setValue("submode", submode); } if ( log.time_on.isValid() ) { record.setValue("start_time", log.time_on); } if ( log.time_off.isValid() ) { record.setValue("end_time", log.time_off); } if ( !log.comments.isEmpty() ) { record.setValue("comment", Data::removeAccents(log.comments)); record.setValue("comment_intl", log.comments); } if ( !log.exch_sent.isEmpty() ) { record.setValue("stx_string", log.exch_sent); } if ( !log.exch_rcvd.isEmpty() ) { record.setValue("srx_string", log.exch_rcvd); } if ( !log.prop_mode.isEmpty() ) { record.setValue("prop_mode", log.prop_mode); } if ( !log.tx_pwr.isEmpty() ) { record.setValue("tx_pwr", log.tx_pwr); } if ( !log.op_call.isEmpty() ) { record.setValue("operator", Data::removeAccents(log.op_call)); } if ( !log.my_grid.isEmpty() ) { record.setValue("my_gridsquare", log.my_grid); } if ( !log.my_call.isEmpty() ) { record.setValue("station_callsign", log.my_call); } // The QSO record can be received in two formats from WSJTX (raw and ADIF). // Therefore, it is necessary to save the first record and possibly update it // with the second record and then emit the result. // For this we create an updatable SQLRecord wsjtSQLRecord.updateRecord(record); //emit addContact(record); } void WsjtxUDPReceiver::contactReady(QSqlRecord record) { FCT_IDENTIFICATION; emit addContact(record); } void WsjtxUDPReceiver::insertContact(WsjtxLogADIF log) { FCT_IDENTIFICATION; qCDebug(function_parameters) << log; QSqlTableModel model; model.setTable("contacts"); model.removeColumn(model.fieldIndex("id")); QSqlRecord record = model.record(0); QTextStream in(&log.log_adif); AdiFormat adif(in); adif.importNext(record); // the values ​​listed below have default values ​​defined in ADIF. // These are set to N. However, WSJTX and other apps do not send // these fields, which means that all records would be set to N - do not send. // It is unacceptable. So this piece of code deletes these defaults so that // in NewContact these values ​​are set from the values ​​in the GUI. // There is a risk that some clone will start sending these values ​​as well. // In this case, it will have to be handled differently. Currently it is OK record.remove(record.indexOf("qsl_sent")); record.remove(record.indexOf("lotw_qsl_sent")); record.remove(record.indexOf("eqsl_qsl_sent")); // The QSO record can be received in two formats from WSJTX (raw and ADIF). // Therefore, it is necessary to save the first record and possibly update it // with the second record and then emit the result. // For this we create an updatable SQLRecord wsjtSQLRecord.updateRecord(record); //emit addContact(record); } void WsjtxUDPReceiver::sendReply(const WsjtxEntry &entry) { FCT_IDENTIFICATION; const WsjtxDecode &decode = entry.decode; qCDebug(function_parameters) << decode; if (!socket) return; /* sending to WSJT to UDP address, not multicast address because * WSJTX does not listen multicast address */ qCDebug(runtime) << "Sending to" << wsjtxAddress; QByteArray data; QDataStream stream(&data, QIODevice::ReadWrite); stream.setVersion(QDataStream::Qt_5_4); // UDP_DEFAULT_SCHEMA_VERSION 3 stream << UDP_MAGIC_NUMBER; stream << UDP_DEFAULT_SCHEMA_VERSION; /* * from WSJTX: Network/NetworkMessage.hpp * Reply In 4 quint32 * Id (target unique key) utf8 * Time QTime * snr qint32 * Delta time (S) float (serialized as double) * Delta frequency (Hz) quint32 * Mode utf8 * Message utf8 * Low confidence bool * Modifiers quint8 * * In order for a server to provide a useful cooperative service * to WSJT-X it is possible for it to initiate a QSO by sending * this message to a client. WSJT-X filters this message and only * acts upon it if the message exactly describes a prior decode * and that decode is a CQ or QRZ message. The action taken is * exactly equivalent to the user double clicking the message in * the "Band activity" window. The intent of this message is for * servers to be able to provide an advanced look up of potential * QSO partners, for example determining if they have been worked * before or if working them may advance some objective like * award progress. The intention is not to provide a secondary * user interface for WSJT-X, it is expected that after QSO * initiation the rest of the QSO is carried out manually using * the normal WSJT-X user interface. * * The Modifiers field allows the equivalent of keyboard * modifiers to be sent "as if" those modifier keys where pressed * while double-clicking the specified decoded message. The * modifier values (hexadecimal) are as follows: * * no modifier 0x00 * SHIFT 0x02 * CTRL 0x04 CMD on Mac * ALT 0x08 * META 0x10 Windows key on MS Windows * KEYPAD 0x20 Keypad or arrows * Group switch 0x40 X11 only */ stream << UDP_COMMANDS::REPLY_CMD; stream << decode.id.toUtf8(); stream << decode.time; stream << decode.snr; stream << decode.dt; stream << decode.df; stream << decode.mode.toUtf8(); stream << decode.message.toUtf8(); stream << decode.low_confidence; stream << quint8(0); socket->writeDatagram(data, wsjtxAddress, wsjtxPort); } void WsjtxUDPReceiver::sendHighlightCallsign(const WsjtxEntry &entry) { FCT_IDENTIFICATION; // QColor() means that WSJTX clears the color sendHighlightCallsignColor(entry, Qt::black, Data::statusToColor(entry.status, false, QColor())); } void WsjtxUDPReceiver::sendClearHighlightCallsign(const WsjtxEntry &entry) { FCT_IDENTIFICATION; // QColor() means that WSJTX clears the color sendHighlightCallsignColor(entry, QColor(), QColor()); } void WsjtxUDPReceiver::sendClearAllHighlightCallsign() { FCT_IDENTIFICATION; WsjtxEntry tmp; tmp.callsign = "CLEARALL!"; sendHighlightCallsignColor(tmp, QColor(), QColor(), false); } void WsjtxUDPReceiver::sendHighlightCallsignColor(const WsjtxEntry &entry, const QColor &fgColor, const QColor &bgColor, bool highlightLast) { FCT_IDENTIFICATION; if ( !socket ) return; if ( !isOutputColorCQSpotEnabled ) { qCDebug(runtime) << "HighlightCallsign is disabled"; return; } const WsjtxDecode &decode = entry.decode; qCDebug(function_parameters) << decode << fgColor << bgColor << highlightLast; /* sending to WSJT to UDP address, not multicast address because * WSJTX does not listen multicast address */ qCDebug(runtime) << "Sending to" << wsjtxAddress; QByteArray data; QDataStream out(&data, QIODevice::WriteOnly); out.setVersion(QDataStream::Qt_5_4); // UDP_DEFAULT_SCHEMA_VERSION 3 out << UDP_MAGIC_NUMBER; out << UDP_DEFAULT_SCHEMA_VERSION; /* * from WSJTX: Network/NetworkMessage.hpp * Highlight Callsign In 13 quint32 * Id (unique key) utf8 * Callsign utf8 * Background Color QColor * Foreground Color QColor * Highlight last bool * * The server may send this message at any time. The message * specifies the background and foreground color that will be * used to highlight the specified callsign in the decoded * messages printed in the Band Activity panel. The WSJT-X * clients maintain a list of such instructions and apply them to * all decoded messages in the band activity window. To clear * and cancel highlighting send an invalid QColor value for * either or both of the background and foreground fields. When * using this mode the total number of callsign highlighting * requests should be limited otherwise the performance of WSJT-X * decoding may be impacted. A rough rule of thumb might be too * limit the number of active highlighting requests to no more * than 100. * * Using a callsign of "CLEARALL!" and anything for the * color values will clear the internal highlighting data. It will * NOT remove the highlighting on the screen, however. The exclamation * symbol is used to avoid accidental clearing of all highlighting * data via a decoded callsign, since an exclamation symbol is not * a valid character in a callsign. * * The "Highlight last" field allows the sender to request that * all instances of "Callsign" in the last period only, instead * of all instances in all periods, be highlighted. */ out << UDP_COMMANDS::HIGHLIGHT_CALLSIGN_CMD; out << decode.id.toUtf8(); out << entry.callsign.toUtf8(); out << bgColor; // Background Color out << fgColor; // Foreground Color out << true; // Highlight last socket->writeDatagram(data, wsjtxAddress, wsjtxPort); } void WsjtxUDPReceiver::reloadSetting() { FCT_IDENTIFICATION; openPort(); isOutputColorCQSpotEnabled = getConfigOutputColorCQSpot(); } ================================================ FILE: core/WsjtxUDPReceiver.h ================================================ #ifndef QLOG_CORE_WSJTXUDPRECEIVER_H #define QLOG_CORE_WSJTXUDPRECEIVER_H #include #include #include #include #include #include "data/UpdatableSQLRecord.h" #include "data/WsjtxStatus.h" #include "data/WsjtxDecode.h" #include "data/WsjtxLog.h" #include "data/WsjtxLogADIF.h" #include "data/WsjtxEntry.h" class Data; class QUdpSocket; class WsjtxUDPReceiver : public QObject { Q_OBJECT public: explicit WsjtxUDPReceiver(QObject *parent = nullptr); static float modePeriodLength(const QString &); static quint16 getConfigPort(); static void saveConfigPort(quint16); static QString getConfigForwardAddresses(); static void saveConfigForwardAddresses(const QString &); static void saveConfigMulticastJoin(bool); static bool getConfigMulticastJoin(); static void saveConfigMulticastAddress(const QString &); static QString getConfigMulticastAddress(); static void saveConfigMulticastTTL(int); static int getConfigMulticastTTL(); static bool getConfigOutputColorCQSpot(); static void saveConfigOutputColorCQSpot(bool); // identification of different variants of the WSJTX protocol based on packet ID static bool isJTDXId(const QString &id) { return id.contains("JTDX"); } static bool isWriteLogId(const QString &id) { return id.contains("WRITELOG"); } static bool isCSNSat(const QString &data) { return data.contains("CSN Sat"); } signals: void statusReceived(WsjtxStatus); void decodeReceived(WsjtxDecode); void addContact(QSqlRecord); public slots: void sendReply(const WsjtxEntry&); void sendHighlightCallsign(const WsjtxEntry&); void sendClearHighlightCallsign(const WsjtxEntry&); void sendClearAllHighlightCallsign(); void reloadSetting(); private slots: void readPendingDatagrams(); void insertContact(WsjtxLog log); void contactReady(QSqlRecord record); void insertContact(WsjtxLogADIF log); private: QUdpSocket* socket; QHostAddress wsjtxAddress; quint16 wsjtxPort; bool isOutputColorCQSpotEnabled; UpdatableSQLRecord wsjtSQLRecord; static const int DEFAULT_PORT = 2237; const quint32 UDP_MAGIC_NUMBER = 0xadbccbda; const quint32 UDP_DEFAULT_SCHEMA_VERSION = 3; enum UDP_COMMANDS { REPLY_CMD = quint32(4), HIGHLIGHT_CALLSIGN_CMD = quint32(13), }; void openPort(); void forwardDatagram(const QNetworkDatagram &); void sendHighlightCallsignColor(const WsjtxEntry &entry, const QColor &fgColor, const QColor &bgColor, bool highlightLast = true); }; #endif // QLOG_CORE_WSJTXUDPRECEIVER_H ================================================ FILE: core/csv.hpp ================================================ #pragma once /* CSV for C++, version 3.4.0 https://github.com/vincentlaucsb/csv-parser MIT License Copyright (c) 2017-2026 Vincent La Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef CSV_HPP #define CSV_HPP #include #include #include #include #include #include #include #include #include #include /** @file * @brief Defines functionality needed for basic CSV parsing */ #include #include #include #include #include #include #include #include #include #if !defined(CSV_ENABLE_THREADS) || CSV_ENABLE_THREADS #include #include #endif /** @file * @brief Contains the main CSV parsing algorithm and various utility functions */ #include #include #include #include #include #include #include #if !defined(__EMSCRIPTEN__) /* Copyright 2017 https://github.com/mandreyel * * Permission is hereby granted, free of charge, to any person obtaining a copy of this * software and associated documentation files (the "Software"), to deal in the Software * without restriction, including without limitation the rights to use, copy, modify, * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be included in all copies * or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* csv-parser local note: * * This vendored mio.hpp includes a minimal Windows-specific narrowing fix in * int64_high/int64_low to avoid -Wconversion failures under strict MinGW builds. * Keep this patch small and easy to rebase if/when upstream is updated. * * - Vincent La 3/31/2026 */ #ifndef MIO_MMAP_HEADER #define MIO_MMAP_HEADER // #include "mio/page.hpp" /* Copyright 2017 https://github.com/mandreyel * * Permission is hereby granted, free of charge, to any person obtaining a copy of this * software and associated documentation files (the "Software"), to deal in the Software * without restriction, including without limitation the rights to use, copy, modify, * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be included in all copies * or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef MIO_PAGE_HEADER #define MIO_PAGE_HEADER #ifdef _WIN32 # include #else # include #endif namespace mio { /** * This is used by `basic_mmap` to determine whether to create a read-only or * a read-write memory mapping. */ enum class access_mode { read, write }; /** * Determines the operating system's page allocation granularity. * * On the first call to this function, it invokes the operating system specific syscall * to determine the page size, caches the value, and returns it. Any subsequent call to * this function serves the cached value, so no further syscalls are made. */ inline size_t page_size() { static const size_t page_size = [] { #ifdef _WIN32 SYSTEM_INFO SystemInfo; GetSystemInfo(&SystemInfo); return SystemInfo.dwAllocationGranularity; #else return sysconf(_SC_PAGE_SIZE); #endif }(); return page_size; } /** * Alligns `offset` to the operating's system page size such that it subtracts the * difference until the nearest page boundary before `offset`, or does nothing if * `offset` is already page aligned. */ inline size_t make_offset_page_aligned(size_t offset) noexcept { const size_t page_size_ = page_size(); // Use integer division to round down to the nearest page alignment. return offset / page_size_ * page_size_; } } // namespace mio #endif // MIO_PAGE_HEADER #include #include #include #include #ifdef _WIN32 # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN # endif // WIN32_LEAN_AND_MEAN # include #else // ifdef _WIN32 # define INVALID_HANDLE_VALUE -1 #endif // ifdef _WIN32 namespace mio { // This value may be provided as the `length` parameter to the constructor or // `map`, in which case a memory mapping of the entire file is created. enum { map_entire_file = 0 }; #ifdef _WIN32 using file_handle_type = HANDLE; #else using file_handle_type = int; #endif // This value represents an invalid file handle type. This can be used to // determine whether `basic_mmap::file_handle` is valid, for example. const static file_handle_type invalid_handle = INVALID_HANDLE_VALUE; template struct basic_mmap { using value_type = ByteT; using size_type = size_t; using reference = value_type&; using const_reference = const value_type&; using pointer = value_type*; using const_pointer = const value_type*; using difference_type = std::ptrdiff_t; using iterator = pointer; using const_iterator = const_pointer; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; using iterator_category = std::random_access_iterator_tag; using handle_type = file_handle_type; static_assert(sizeof(ByteT) == sizeof(char), "ByteT must be the same size as char."); private: // Points to the first requested byte, and not to the actual start of the mapping. pointer data_ = nullptr; // Length--in bytes--requested by user (which may not be the length of the // full mapping) and the length of the full mapping. size_type length_ = 0; size_type mapped_length_ = 0; // Letting user map a file using both an existing file handle and a path // introcudes some complexity (see `is_handle_internal_`). // On POSIX, we only need a file handle to create a mapping, while on // Windows systems the file handle is necessary to retrieve a file mapping // handle, but any subsequent operations on the mapped region must be done // through the latter. handle_type file_handle_ = INVALID_HANDLE_VALUE; #ifdef _WIN32 handle_type file_mapping_handle_ = INVALID_HANDLE_VALUE; #endif // Letting user map a file using both an existing file handle and a path // introcudes some complexity in that we must not close the file handle if // user provided it, but we must close it if we obtained it using the // provided path. For this reason, this flag is used to determine when to // close `file_handle_`. bool is_handle_internal_; public: /** * The default constructed mmap object is in a non-mapped state, that is, * any operation that attempts to access nonexistent underlying data will * result in undefined behaviour/segmentation faults. */ basic_mmap() = default; #ifdef __cpp_exceptions /** * The same as invoking the `map` function, except any error that may occur * while establishing the mapping is wrapped in a `std::system_error` and is * thrown. */ template basic_mmap(const String& path, const size_type offset = 0, const size_type length = map_entire_file) { std::error_code error; map(path, offset, length, error); if(error) { throw std::system_error(error); } } /** * The same as invoking the `map` function, except any error that may occur * while establishing the mapping is wrapped in a `std::system_error` and is * thrown. */ basic_mmap(const handle_type handle, const size_type offset = 0, const size_type length = map_entire_file) { std::error_code error; map(handle, offset, length, error); if(error) { throw std::system_error(error); } } #endif // __cpp_exceptions /** * `basic_mmap` has single-ownership semantics, so transferring ownership * may only be accomplished by moving the object. */ basic_mmap(const basic_mmap&) = delete; basic_mmap(basic_mmap&&); basic_mmap& operator=(const basic_mmap&) = delete; basic_mmap& operator=(basic_mmap&&); /** * If this is a read-write mapping, the destructor invokes sync. Regardless * of the access mode, unmap is invoked as a final step. */ ~basic_mmap(); /** * On UNIX systems 'file_handle' and 'mapping_handle' are the same. On Windows, * however, a mapped region of a file gets its own handle, which is returned by * 'mapping_handle'. */ handle_type file_handle() const noexcept { return file_handle_; } handle_type mapping_handle() const noexcept; /** Returns whether a valid memory mapping has been created. */ bool is_open() const noexcept { return file_handle_ != invalid_handle; } /** * Returns true if no mapping was established, that is, conceptually the * same as though the length that was mapped was 0. This function is * provided so that this class has Container semantics. */ bool empty() const noexcept { return length() == 0; } /** Returns true if a mapping was established. */ bool is_mapped() const noexcept; /** * `size` and `length` both return the logical length, i.e. the number of bytes * user requested to be mapped, while `mapped_length` returns the actual number of * bytes that were mapped which is a multiple of the underlying operating system's * page allocation granularity. */ size_type size() const noexcept { return length(); } size_type length() const noexcept { return length_; } size_type mapped_length() const noexcept { return mapped_length_; } /** Returns the offset relative to the start of the mapping. */ size_type mapping_offset() const noexcept { return mapped_length_ - length_; } /** * Returns a pointer to the first requested byte, or `nullptr` if no memory mapping * exists. */ template< access_mode A = AccessMode, typename = typename std::enable_if::type > pointer data() noexcept { return data_; } const_pointer data() const noexcept { return data_; } /** * Returns an iterator to the first requested byte, if a valid memory mapping * exists, otherwise this function call is undefined behaviour. */ template< access_mode A = AccessMode, typename = typename std::enable_if::type > iterator begin() noexcept { return data(); } const_iterator begin() const noexcept { return data(); } const_iterator cbegin() const noexcept { return data(); } /** * Returns an iterator one past the last requested byte, if a valid memory mapping * exists, otherwise this function call is undefined behaviour. */ template< access_mode A = AccessMode, typename = typename std::enable_if::type > iterator end() noexcept { return data() + length(); } const_iterator end() const noexcept { return data() + length(); } const_iterator cend() const noexcept { return data() + length(); } /** * Returns a reverse iterator to the last memory mapped byte, if a valid * memory mapping exists, otherwise this function call is undefined * behaviour. */ template< access_mode A = AccessMode, typename = typename std::enable_if::type > reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); } const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(end()); } /** * Returns a reverse iterator past the first mapped byte, if a valid memory * mapping exists, otherwise this function call is undefined behaviour. */ template< access_mode A = AccessMode, typename = typename std::enable_if::type > reverse_iterator rend() noexcept { return reverse_iterator(begin()); } const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); } const_reverse_iterator crend() const noexcept { return const_reverse_iterator(begin()); } /** * Returns a reference to the `i`th byte from the first requested byte (as returned * by `data`). If this is invoked when no valid memory mapping has been created * prior to this call, undefined behaviour ensues. */ reference operator[](const size_type i) noexcept { return data_[i]; } const_reference operator[](const size_type i) const noexcept { return data_[i]; } /** * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the * reason is reported via `error` and the object remains in a state as if this * function hadn't been called. * * `path`, which must be a path to an existing file, is used to retrieve a file * handle (which is closed when the object destructs or `unmap` is called), which is * then used to memory map the requested region. Upon failure, `error` is set to * indicate the reason and the object remains in an unmapped state. * * `offset` is the number of bytes, relative to the start of the file, where the * mapping should begin. When specifying it, there is no need to worry about * providing a value that is aligned with the operating system's page allocation * granularity. This is adjusted by the implementation such that the first requested * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at * `offset` from the start of the file. * * `length` is the number of bytes to map. It may be `map_entire_file`, in which * case a mapping of the entire file is created. */ template void map(const String& path, const size_type offset, const size_type length, std::error_code& error); /** * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the * reason is reported via `error` and the object remains in a state as if this * function hadn't been called. * * `path`, which must be a path to an existing file, is used to retrieve a file * handle (which is closed when the object destructs or `unmap` is called), which is * then used to memory map the requested region. Upon failure, `error` is set to * indicate the reason and the object remains in an unmapped state. * * The entire file is mapped. */ template void map(const String& path, std::error_code& error) { map(path, 0, map_entire_file, error); } /** * Establishes a memory mapping with AccessMode. If the mapping is * unsuccesful, the reason is reported via `error` and the object remains in * a state as if this function hadn't been called. * * `handle`, which must be a valid file handle, which is used to memory map the * requested region. Upon failure, `error` is set to indicate the reason and the * object remains in an unmapped state. * * `offset` is the number of bytes, relative to the start of the file, where the * mapping should begin. When specifying it, there is no need to worry about * providing a value that is aligned with the operating system's page allocation * granularity. This is adjusted by the implementation such that the first requested * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at * `offset` from the start of the file. * * `length` is the number of bytes to map. It may be `map_entire_file`, in which * case a mapping of the entire file is created. */ void map(const handle_type handle, const size_type offset, const size_type length, std::error_code& error); /** * Establishes a memory mapping with AccessMode. If the mapping is * unsuccesful, the reason is reported via `error` and the object remains in * a state as if this function hadn't been called. * * `handle`, which must be a valid file handle, which is used to memory map the * requested region. Upon failure, `error` is set to indicate the reason and the * object remains in an unmapped state. * * The entire file is mapped. */ void map(const handle_type handle, std::error_code& error) { map(handle, 0, map_entire_file, error); } /** * If a valid memory mapping has been created prior to this call, this call * instructs the kernel to unmap the memory region and disassociate this object * from the file. * * The file handle associated with the file that is mapped is only closed if the * mapping was created using a file path. If, on the other hand, an existing * file handle was used to create the mapping, the file handle is not closed. */ void unmap(); void swap(basic_mmap& other); /** Flushes the memory mapped page to disk. Errors are reported via `error`. */ template typename std::enable_if::type sync(std::error_code& error); /** * All operators compare the address of the first byte and size of the two mapped * regions. */ private: template< access_mode A = AccessMode, typename = typename std::enable_if::type > pointer get_mapping_start() noexcept { return !data() ? nullptr : data() - mapping_offset(); } const_pointer get_mapping_start() const noexcept { return !data() ? nullptr : data() - mapping_offset(); } /** * The destructor syncs changes to disk if `AccessMode` is `write`, but not * if it's `read`, but since the destructor cannot be templated, we need to * do SFINAE in a dedicated function, where one syncs and the other is a noop. */ template typename std::enable_if::type conditional_sync(); template typename std::enable_if::type conditional_sync(); }; template bool operator==(const basic_mmap& a, const basic_mmap& b); template bool operator!=(const basic_mmap& a, const basic_mmap& b); template bool operator<(const basic_mmap& a, const basic_mmap& b); template bool operator<=(const basic_mmap& a, const basic_mmap& b); template bool operator>(const basic_mmap& a, const basic_mmap& b); template bool operator>=(const basic_mmap& a, const basic_mmap& b); /** * This is the basis for all read-only mmap objects and should be preferred over * directly using `basic_mmap`. */ template using basic_mmap_source = basic_mmap; /** * This is the basis for all read-write mmap objects and should be preferred over * directly using `basic_mmap`. */ template using basic_mmap_sink = basic_mmap; /** * These aliases cover the most common use cases, both representing a raw byte stream * (either with a char or an unsigned char/uint8_t). */ using mmap_source = basic_mmap_source; using ummap_source = basic_mmap_source; using mmap_sink = basic_mmap_sink; using ummap_sink = basic_mmap_sink; /** * Convenience factory method that constructs a mapping for any `basic_mmap` or * `basic_mmap` type. */ template< typename MMap, typename MappingToken > MMap make_mmap(const MappingToken& token, int64_t offset, int64_t length, std::error_code& error) { MMap mmap; mmap.map(token, offset, length, error); return mmap; } /** * Convenience factory method. * * MappingToken may be a String (`std::string`, `std::string_view`, `const char*`, * `std::filesystem::path`, `std::vector`, or similar), or a * `mmap_source::handle_type`. */ template mmap_source make_mmap_source(const MappingToken& token, mmap_source::size_type offset, mmap_source::size_type length, std::error_code& error) { return make_mmap(token, offset, length, error); } template mmap_source make_mmap_source(const MappingToken& token, std::error_code& error) { return make_mmap_source(token, 0, map_entire_file, error); } /** * Convenience factory method. * * MappingToken may be a String (`std::string`, `std::string_view`, `const char*`, * `std::filesystem::path`, `std::vector`, or similar), or a * `mmap_sink::handle_type`. */ template mmap_sink make_mmap_sink(const MappingToken& token, mmap_sink::size_type offset, mmap_sink::size_type length, std::error_code& error) { return make_mmap(token, offset, length, error); } template mmap_sink make_mmap_sink(const MappingToken& token, std::error_code& error) { return make_mmap_sink(token, 0, map_entire_file, error); } } // namespace mio // #include "detail/mmap.ipp" /* Copyright 2017 https://github.com/mandreyel * * Permission is hereby granted, free of charge, to any person obtaining a copy of this * software and associated documentation files (the "Software"), to deal in the Software * without restriction, including without limitation the rights to use, copy, modify, * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be included in all copies * or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef MIO_BASIC_MMAP_IMPL #define MIO_BASIC_MMAP_IMPL // #include "mio/mmap.hpp" // #include "mio/page.hpp" // #include "mio/detail/string_util.hpp" /* Copyright 2017 https://github.com/mandreyel * * Permission is hereby granted, free of charge, to any person obtaining a copy of this * software and associated documentation files (the "Software"), to deal in the Software * without restriction, including without limitation the rights to use, copy, modify, * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be included in all copies * or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef MIO_STRING_UTIL_HEADER #define MIO_STRING_UTIL_HEADER #include namespace mio { namespace detail { template< typename S, typename C = typename std::decay::type, typename = decltype(std::declval().data()), typename = typename std::enable_if< std::is_same::value #ifdef _WIN32 || std::is_same::value #endif >::type > struct char_type_helper { using type = typename C::value_type; }; template struct char_type { using type = typename char_type_helper::type; }; // TODO: can we avoid this brute force approach? template<> struct char_type { using type = char; }; template<> struct char_type { using type = char; }; template struct char_type { using type = char; }; template struct char_type { using type = char; }; #ifdef _WIN32 template<> struct char_type { using type = wchar_t; }; template<> struct char_type { using type = wchar_t; }; template struct char_type { using type = wchar_t; }; template struct char_type { using type = wchar_t; }; #endif // _WIN32 template struct is_c_str_helper { static constexpr bool value = std::is_same< CharT*, // TODO: I'm so sorry for this... Can this be made cleaner? typename std::add_pointer< typename std::remove_cv< typename std::remove_pointer< typename std::decay< S >::type >::type >::type >::type >::value; }; template struct is_c_str { static constexpr bool value = is_c_str_helper::value; }; #ifdef _WIN32 template struct is_c_wstr { static constexpr bool value = is_c_str_helper::value; }; #endif // _WIN32 template struct is_c_str_or_c_wstr { static constexpr bool value = is_c_str::value #ifdef _WIN32 || is_c_wstr::value #endif ; }; template< typename String, typename = decltype(std::declval().data()), typename = typename std::enable_if::value>::type > const typename char_type::type* c_str(const String& path) { return path.data(); } template< typename String, typename = decltype(std::declval().empty()), typename = typename std::enable_if::value>::type > bool empty(const String& path) { return path.empty(); } template< typename String, typename = typename std::enable_if::value>::type > const typename char_type::type* c_str(String path) { return path; } template< typename String, typename = typename std::enable_if::value>::type > bool empty(String path) { return !path || (*path == 0); } } // namespace detail } // namespace mio #endif // MIO_STRING_UTIL_HEADER #include #ifndef _WIN32 # include # include # include # include #endif namespace mio { namespace detail { #ifdef _WIN32 namespace win { /** Returns the 4 upper bytes of an 8-byte integer. */ inline DWORD int64_high(int64_t n) noexcept { return static_cast(static_cast(n) >> 32); } /** Returns the 4 lower bytes of an 8-byte integer. */ inline DWORD int64_low(int64_t n) noexcept { return static_cast(static_cast(n) & 0xffffffffULL); } template< typename String, typename = typename std::enable_if< std::is_same::type, char>::value >::type > file_handle_type open_file_helper(const String& path, const access_mode mode) { return ::CreateFileA(c_str(path), mode == access_mode::read ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); } template typename std::enable_if< std::is_same::type, wchar_t>::value, file_handle_type >::type open_file_helper(const String& path, const access_mode mode) { return ::CreateFileW(c_str(path), mode == access_mode::read ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); } } // win #endif // _WIN32 /** * Returns the last platform specific system error (errno on POSIX and * GetLastError on Win) as a `std::error_code`. */ inline std::error_code last_error() noexcept { std::error_code error; #ifdef _WIN32 error.assign(GetLastError(), std::system_category()); #else error.assign(errno, std::system_category()); #endif return error; } template file_handle_type open_file(const String& path, const access_mode mode, std::error_code& error) { error.clear(); if(detail::empty(path)) { error = std::make_error_code(std::errc::invalid_argument); return invalid_handle; } #ifdef _WIN32 const auto handle = win::open_file_helper(path, mode); #else // POSIX const auto handle = ::open(c_str(path), mode == access_mode::read ? O_RDONLY : O_RDWR); #endif if(handle == invalid_handle) { error = detail::last_error(); } return handle; } inline size_t query_file_size(file_handle_type handle, std::error_code& error) { error.clear(); #ifdef _WIN32 LARGE_INTEGER file_size; if(::GetFileSizeEx(handle, &file_size) == 0) { error = detail::last_error(); return 0; } return static_cast(file_size.QuadPart); #else // POSIX struct stat sbuf; if(::fstat(handle, &sbuf) == -1) { error = detail::last_error(); return 0; } return sbuf.st_size; #endif } struct mmap_context { char* data; int64_t length; int64_t mapped_length; #ifdef _WIN32 file_handle_type file_mapping_handle; #endif }; inline mmap_context memory_map(const file_handle_type file_handle, const int64_t offset, const int64_t length, const access_mode mode, std::error_code& error) { const int64_t aligned_offset = make_offset_page_aligned(offset); const int64_t length_to_map = offset - aligned_offset + length; #ifdef _WIN32 const int64_t max_file_size = offset + length; const auto file_mapping_handle = ::CreateFileMapping( file_handle, 0, mode == access_mode::read ? PAGE_READONLY : PAGE_READWRITE, win::int64_high(max_file_size), win::int64_low(max_file_size), 0); if(file_mapping_handle == invalid_handle) { error = detail::last_error(); return {}; } char* mapping_start = static_cast(::MapViewOfFile( file_mapping_handle, mode == access_mode::read ? FILE_MAP_READ : FILE_MAP_WRITE, win::int64_high(aligned_offset), win::int64_low(aligned_offset), length_to_map)); if(mapping_start == nullptr) { // Close file handle if mapping it failed. ::CloseHandle(file_mapping_handle); error = detail::last_error(); return {}; } #else // POSIX char* mapping_start = static_cast(::mmap( 0, // Don't give hint as to where to map. length_to_map, mode == access_mode::read ? PROT_READ : PROT_WRITE, MAP_SHARED, file_handle, aligned_offset)); if(mapping_start == MAP_FAILED) { error = detail::last_error(); return {}; } #endif mmap_context ctx; ctx.data = mapping_start + offset - aligned_offset; ctx.length = length; ctx.mapped_length = length_to_map; #ifdef _WIN32 ctx.file_mapping_handle = file_mapping_handle; #endif return ctx; } } // namespace detail // -- basic_mmap -- template basic_mmap::~basic_mmap() { conditional_sync(); unmap(); } template basic_mmap::basic_mmap(basic_mmap&& other) : data_(std::move(other.data_)) , length_(std::move(other.length_)) , mapped_length_(std::move(other.mapped_length_)) , file_handle_(std::move(other.file_handle_)) #ifdef _WIN32 , file_mapping_handle_(std::move(other.file_mapping_handle_)) #endif , is_handle_internal_(std::move(other.is_handle_internal_)) { other.data_ = nullptr; other.length_ = other.mapped_length_ = 0; other.file_handle_ = invalid_handle; #ifdef _WIN32 other.file_mapping_handle_ = invalid_handle; #endif } template basic_mmap& basic_mmap::operator=(basic_mmap&& other) { if(this != &other) { // First the existing mapping needs to be removed. unmap(); data_ = std::move(other.data_); length_ = std::move(other.length_); mapped_length_ = std::move(other.mapped_length_); file_handle_ = std::move(other.file_handle_); #ifdef _WIN32 file_mapping_handle_ = std::move(other.file_mapping_handle_); #endif is_handle_internal_ = std::move(other.is_handle_internal_); // The moved from basic_mmap's fields need to be reset, because // otherwise other's destructor will unmap the same mapping that was // just moved into this. other.data_ = nullptr; other.length_ = other.mapped_length_ = 0; other.file_handle_ = invalid_handle; #ifdef _WIN32 other.file_mapping_handle_ = invalid_handle; #endif other.is_handle_internal_ = false; } return *this; } template typename basic_mmap::handle_type basic_mmap::mapping_handle() const noexcept { #ifdef _WIN32 return file_mapping_handle_; #else return file_handle_; #endif } template template void basic_mmap::map(const String& path, const size_type offset, const size_type length, std::error_code& error) { error.clear(); if(detail::empty(path)) { error = std::make_error_code(std::errc::invalid_argument); return; } const auto handle = detail::open_file(path, AccessMode, error); if(error) { return; } map(handle, offset, length, error); // This MUST be after the call to map, as that sets this to true. if(!error) { is_handle_internal_ = true; } } template void basic_mmap::map(const handle_type handle, const size_type offset, const size_type length, std::error_code& error) { error.clear(); if(handle == invalid_handle) { error = std::make_error_code(std::errc::bad_file_descriptor); return; } const auto file_size = detail::query_file_size(handle, error); if(error) { return; } if(offset + length > file_size) { error = std::make_error_code(std::errc::invalid_argument); return; } const auto ctx = detail::memory_map(handle, offset, length == map_entire_file ? (file_size - offset) : length, AccessMode, error); if(!error) { // We must unmap the previous mapping that may have existed prior to this call. // Note that this must only be invoked after a new mapping has been created in // order to provide the strong guarantee that, should the new mapping fail, the // `map` function leaves this instance in a state as though the function had // never been invoked. unmap(); file_handle_ = handle; is_handle_internal_ = false; data_ = reinterpret_cast(ctx.data); length_ = ctx.length; mapped_length_ = ctx.mapped_length; #ifdef _WIN32 file_mapping_handle_ = ctx.file_mapping_handle; #endif } } template template typename std::enable_if::type basic_mmap::sync(std::error_code& error) { error.clear(); if(!is_open()) { error = std::make_error_code(std::errc::bad_file_descriptor); return; } if(data()) { #ifdef _WIN32 if(::FlushViewOfFile(get_mapping_start(), mapped_length_) == 0 || ::FlushFileBuffers(file_handle_) == 0) #else // POSIX if(::msync(get_mapping_start(), mapped_length_, MS_SYNC) != 0) #endif { error = detail::last_error(); return; } } #ifdef _WIN32 if(::FlushFileBuffers(file_handle_) == 0) { error = detail::last_error(); } #endif } template void basic_mmap::unmap() { if(!is_open()) { return; } // TODO do we care about errors here? #ifdef _WIN32 if(is_mapped()) { ::UnmapViewOfFile(get_mapping_start()); ::CloseHandle(file_mapping_handle_); } #else // POSIX if(data_) { ::munmap(const_cast(get_mapping_start()), mapped_length_); } #endif // If `file_handle_` was obtained by our opening it (when map is called with // a path, rather than an existing file handle), we need to close it, // otherwise it must not be closed as it may still be used outside this // instance. if(is_handle_internal_) { #ifdef _WIN32 ::CloseHandle(file_handle_); #else // POSIX ::close(file_handle_); #endif } // Reset fields to their default values. data_ = nullptr; length_ = mapped_length_ = 0; file_handle_ = invalid_handle; #ifdef _WIN32 file_mapping_handle_ = invalid_handle; #endif } template bool basic_mmap::is_mapped() const noexcept { #ifdef _WIN32 return file_mapping_handle_ != invalid_handle; #else // POSIX return is_open(); #endif } template void basic_mmap::swap(basic_mmap& other) { if(this != &other) { using std::swap; swap(data_, other.data_); swap(file_handle_, other.file_handle_); #ifdef _WIN32 swap(file_mapping_handle_, other.file_mapping_handle_); #endif swap(length_, other.length_); swap(mapped_length_, other.mapped_length_); swap(is_handle_internal_, other.is_handle_internal_); } } template template typename std::enable_if::type basic_mmap::conditional_sync() { // This is invoked from the destructor, so not much we can do about // failures here. std::error_code ec; sync(ec); } template template typename std::enable_if::type basic_mmap::conditional_sync() { // noop } template bool operator==(const basic_mmap& a, const basic_mmap& b) { return a.data() == b.data() && a.size() == b.size(); } template bool operator!=(const basic_mmap& a, const basic_mmap& b) { return !(a == b); } template bool operator<(const basic_mmap& a, const basic_mmap& b) { if(a.data() == b.data()) { return a.size() < b.size(); } return a.data() < b.data(); } template bool operator<=(const basic_mmap& a, const basic_mmap& b) { return !(a > b); } template bool operator>(const basic_mmap& a, const basic_mmap& b) { if(a.data() == b.data()) { return a.size() > b.size(); } return a.data() > b.data(); } template bool operator>=(const basic_mmap& a, const basic_mmap& b) { return !(a < b); } } // namespace mio #endif // MIO_BASIC_MMAP_IMPL #endif // MIO_MMAP_HEADER /* Copyright 2017 https://github.com/mandreyel * * Permission is hereby granted, free of charge, to any person obtaining a copy of this * software and associated documentation files (the "Software"), to deal in the Software * without restriction, including without limitation the rights to use, copy, modify, * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be included in all copies * or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef MIO_PAGE_HEADER #define MIO_PAGE_HEADER #ifdef _WIN32 # include #else # include #endif namespace mio { /** * This is used by `basic_mmap` to determine whether to create a read-only or * a read-write memory mapping. */ enum class access_mode { read, write }; /** * Determines the operating system's page allocation granularity. * * On the first call to this function, it invokes the operating system specific syscall * to determine the page size, caches the value, and returns it. Any subsequent call to * this function serves the cached value, so no further syscalls are made. */ inline size_t page_size() { static const size_t page_size = [] { #ifdef _WIN32 SYSTEM_INFO SystemInfo; GetSystemInfo(&SystemInfo); return SystemInfo.dwAllocationGranularity; #else return sysconf(_SC_PAGE_SIZE); #endif }(); return page_size; } /** * Alligns `offset` to the operating's system page size such that it subtracts the * difference until the nearest page boundary before `offset`, or does nothing if * `offset` is already page aligned. */ inline size_t make_offset_page_aligned(size_t offset) noexcept { const size_t page_size_ = page_size(); // Use integer division to round down to the nearest page alignment. return offset / page_size_ * page_size_; } } // namespace mio #endif // MIO_PAGE_HEADER /* Copyright 2017 https://github.com/mandreyel * * Permission is hereby granted, free of charge, to any person obtaining a copy of this * software and associated documentation files (the "Software"), to deal in the Software * without restriction, including without limitation the rights to use, copy, modify, * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be included in all copies * or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef MIO_SHARED_MMAP_HEADER #define MIO_SHARED_MMAP_HEADER // #include "mio/mmap.hpp" #include // std::error_code #include // std::shared_ptr namespace mio { /** * Exposes (nearly) the same interface as `basic_mmap`, but endowes it with * `std::shared_ptr` semantics. * * This is not the default behaviour of `basic_mmap` to avoid allocating on the heap if * shared semantics are not required. */ template< access_mode AccessMode, typename ByteT > class basic_shared_mmap { using impl_type = basic_mmap; std::shared_ptr pimpl_; public: using value_type = typename impl_type::value_type; using size_type = typename impl_type::size_type; using reference = typename impl_type::reference; using const_reference = typename impl_type::const_reference; using pointer = typename impl_type::pointer; using const_pointer = typename impl_type::const_pointer; using difference_type = typename impl_type::difference_type; using iterator = typename impl_type::iterator; using const_iterator = typename impl_type::const_iterator; using reverse_iterator = typename impl_type::reverse_iterator; using const_reverse_iterator = typename impl_type::const_reverse_iterator; using iterator_category = typename impl_type::iterator_category; using handle_type = typename impl_type::handle_type; using mmap_type = impl_type; basic_shared_mmap() = default; basic_shared_mmap(const basic_shared_mmap&) = default; basic_shared_mmap& operator=(const basic_shared_mmap&) = default; basic_shared_mmap(basic_shared_mmap&&) = default; basic_shared_mmap& operator=(basic_shared_mmap&&) = default; /** Takes ownership of an existing mmap object. */ basic_shared_mmap(mmap_type&& mmap) : pimpl_(std::make_shared(std::move(mmap))) {} /** Takes ownership of an existing mmap object. */ basic_shared_mmap& operator=(mmap_type&& mmap) { pimpl_ = std::make_shared(std::move(mmap)); return *this; } /** Initializes this object with an already established shared mmap. */ basic_shared_mmap(std::shared_ptr mmap) : pimpl_(std::move(mmap)) {} /** Initializes this object with an already established shared mmap. */ basic_shared_mmap& operator=(std::shared_ptr mmap) { pimpl_ = std::move(mmap); return *this; } #ifdef __cpp_exceptions /** * The same as invoking the `map` function, except any error that may occur * while establishing the mapping is wrapped in a `std::system_error` and is * thrown. */ template basic_shared_mmap(const String& path, const size_type offset = 0, const size_type length = map_entire_file) { std::error_code error; map(path, offset, length, error); if(error) { throw std::system_error(error); } } /** * The same as invoking the `map` function, except any error that may occur * while establishing the mapping is wrapped in a `std::system_error` and is * thrown. */ basic_shared_mmap(const handle_type handle, const size_type offset = 0, const size_type length = map_entire_file) { std::error_code error; map(handle, offset, length, error); if(error) { throw std::system_error(error); } } #endif // __cpp_exceptions /** * If this is a read-write mapping and the last reference to the mapping, * the destructor invokes sync. Regardless of the access mode, unmap is * invoked as a final step. */ ~basic_shared_mmap() = default; /** Returns the underlying `std::shared_ptr` instance that holds the mmap. */ std::shared_ptr get_shared_ptr() { return pimpl_; } /** * On UNIX systems 'file_handle' and 'mapping_handle' are the same. On Windows, * however, a mapped region of a file gets its own handle, which is returned by * 'mapping_handle'. */ handle_type file_handle() const noexcept { return pimpl_ ? pimpl_->file_handle() : invalid_handle; } handle_type mapping_handle() const noexcept { return pimpl_ ? pimpl_->mapping_handle() : invalid_handle; } /** Returns whether a valid memory mapping has been created. */ bool is_open() const noexcept { return pimpl_ && pimpl_->is_open(); } /** * Returns true if no mapping was established, that is, conceptually the * same as though the length that was mapped was 0. This function is * provided so that this class has Container semantics. */ bool empty() const noexcept { return !pimpl_ || pimpl_->empty(); } /** * `size` and `length` both return the logical length, i.e. the number of bytes * user requested to be mapped, while `mapped_length` returns the actual number of * bytes that were mapped which is a multiple of the underlying operating system's * page allocation granularity. */ size_type size() const noexcept { return pimpl_ ? pimpl_->length() : 0; } size_type length() const noexcept { return pimpl_ ? pimpl_->length() : 0; } size_type mapped_length() const noexcept { return pimpl_ ? pimpl_->mapped_length() : 0; } /** * Returns a pointer to the first requested byte, or `nullptr` if no memory mapping * exists. */ template< access_mode A = AccessMode, typename = typename std::enable_if::type > pointer data() noexcept { return pimpl_->data(); } const_pointer data() const noexcept { return pimpl_ ? pimpl_->data() : nullptr; } /** * Returns an iterator to the first requested byte, if a valid memory mapping * exists, otherwise this function call is undefined behaviour. */ iterator begin() noexcept { return pimpl_->begin(); } const_iterator begin() const noexcept { return pimpl_->begin(); } const_iterator cbegin() const noexcept { return pimpl_->cbegin(); } /** * Returns an iterator one past the last requested byte, if a valid memory mapping * exists, otherwise this function call is undefined behaviour. */ template< access_mode A = AccessMode, typename = typename std::enable_if::type > iterator end() noexcept { return pimpl_->end(); } const_iterator end() const noexcept { return pimpl_->end(); } const_iterator cend() const noexcept { return pimpl_->cend(); } /** * Returns a reverse iterator to the last memory mapped byte, if a valid * memory mapping exists, otherwise this function call is undefined * behaviour. */ template< access_mode A = AccessMode, typename = typename std::enable_if::type > reverse_iterator rbegin() noexcept { return pimpl_->rbegin(); } const_reverse_iterator rbegin() const noexcept { return pimpl_->rbegin(); } const_reverse_iterator crbegin() const noexcept { return pimpl_->crbegin(); } /** * Returns a reverse iterator past the first mapped byte, if a valid memory * mapping exists, otherwise this function call is undefined behaviour. */ template< access_mode A = AccessMode, typename = typename std::enable_if::type > reverse_iterator rend() noexcept { return pimpl_->rend(); } const_reverse_iterator rend() const noexcept { return pimpl_->rend(); } const_reverse_iterator crend() const noexcept { return pimpl_->crend(); } /** * Returns a reference to the `i`th byte from the first requested byte (as returned * by `data`). If this is invoked when no valid memory mapping has been created * prior to this call, undefined behaviour ensues. */ reference operator[](const size_type i) noexcept { return (*pimpl_)[i]; } const_reference operator[](const size_type i) const noexcept { return (*pimpl_)[i]; } /** * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the * reason is reported via `error` and the object remains in a state as if this * function hadn't been called. * * `path`, which must be a path to an existing file, is used to retrieve a file * handle (which is closed when the object destructs or `unmap` is called), which is * then used to memory map the requested region. Upon failure, `error` is set to * indicate the reason and the object remains in an unmapped state. * * `offset` is the number of bytes, relative to the start of the file, where the * mapping should begin. When specifying it, there is no need to worry about * providing a value that is aligned with the operating system's page allocation * granularity. This is adjusted by the implementation such that the first requested * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at * `offset` from the start of the file. * * `length` is the number of bytes to map. It may be `map_entire_file`, in which * case a mapping of the entire file is created. */ template void map(const String& path, const size_type offset, const size_type length, std::error_code& error) { map_impl(path, offset, length, error); } /** * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the * reason is reported via `error` and the object remains in a state as if this * function hadn't been called. * * `path`, which must be a path to an existing file, is used to retrieve a file * handle (which is closed when the object destructs or `unmap` is called), which is * then used to memory map the requested region. Upon failure, `error` is set to * indicate the reason and the object remains in an unmapped state. * * The entire file is mapped. */ template void map(const String& path, std::error_code& error) { map_impl(path, 0, map_entire_file, error); } /** * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the * reason is reported via `error` and the object remains in a state as if this * function hadn't been called. * * `handle`, which must be a valid file handle, which is used to memory map the * requested region. Upon failure, `error` is set to indicate the reason and the * object remains in an unmapped state. * * `offset` is the number of bytes, relative to the start of the file, where the * mapping should begin. When specifying it, there is no need to worry about * providing a value that is aligned with the operating system's page allocation * granularity. This is adjusted by the implementation such that the first requested * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at * `offset` from the start of the file. * * `length` is the number of bytes to map. It may be `map_entire_file`, in which * case a mapping of the entire file is created. */ void map(const handle_type handle, const size_type offset, const size_type length, std::error_code& error) { map_impl(handle, offset, length, error); } /** * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the * reason is reported via `error` and the object remains in a state as if this * function hadn't been called. * * `handle`, which must be a valid file handle, which is used to memory map the * requested region. Upon failure, `error` is set to indicate the reason and the * object remains in an unmapped state. * * The entire file is mapped. */ void map(const handle_type handle, std::error_code& error) { map_impl(handle, 0, map_entire_file, error); } /** * If a valid memory mapping has been created prior to this call, this call * instructs the kernel to unmap the memory region and disassociate this object * from the file. * * The file handle associated with the file that is mapped is only closed if the * mapping was created using a file path. If, on the other hand, an existing * file handle was used to create the mapping, the file handle is not closed. */ void unmap() { if(pimpl_) pimpl_->unmap(); } void swap(basic_shared_mmap& other) { pimpl_.swap(other.pimpl_); } /** Flushes the memory mapped page to disk. Errors are reported via `error`. */ template< access_mode A = AccessMode, typename = typename std::enable_if::type > void sync(std::error_code& error) { if(pimpl_) pimpl_->sync(error); } /** All operators compare the underlying `basic_mmap`'s addresses. */ friend bool operator==(const basic_shared_mmap& a, const basic_shared_mmap& b) { return a.pimpl_ == b.pimpl_; } friend bool operator!=(const basic_shared_mmap& a, const basic_shared_mmap& b) { return !(a == b); } friend bool operator<(const basic_shared_mmap& a, const basic_shared_mmap& b) { return a.pimpl_ < b.pimpl_; } friend bool operator<=(const basic_shared_mmap& a, const basic_shared_mmap& b) { return a.pimpl_ <= b.pimpl_; } friend bool operator>(const basic_shared_mmap& a, const basic_shared_mmap& b) { return a.pimpl_ > b.pimpl_; } friend bool operator>=(const basic_shared_mmap& a, const basic_shared_mmap& b) { return a.pimpl_ >= b.pimpl_; } private: template void map_impl(const MappingToken& token, const size_type offset, const size_type length, std::error_code& error) { if(!pimpl_) { mmap_type mmap = make_mmap(token, offset, length, error); if(error) { return; } pimpl_ = std::make_shared(std::move(mmap)); } else { pimpl_->map(token, offset, length, error); } } }; /** * This is the basis for all read-only mmap objects and should be preferred over * directly using basic_shared_mmap. */ template using basic_shared_mmap_source = basic_shared_mmap; /** * This is the basis for all read-write mmap objects and should be preferred over * directly using basic_shared_mmap. */ template using basic_shared_mmap_sink = basic_shared_mmap; /** * These aliases cover the most common use cases, both representing a raw byte stream * (either with a char or an unsigned char/uint8_t). */ using shared_mmap_source = basic_shared_mmap_source; using shared_ummap_source = basic_shared_mmap_source; using shared_mmap_sink = basic_shared_mmap_sink; using shared_ummap_sink = basic_shared_mmap_sink; } // namespace mio #endif // MIO_SHARED_MMAP_HEADER #endif /** @file * @brief SIMD-accelerated skip for runs of non-special CSV bytes. * * Conservative design: any byte that could be the delimiter, quote character, * \n, or \r causes an early return. * * Uses 4x cmpeq rather than a lookup-table shuffle because CSV has only four * sentinel characters. vpshufb (shuffle) truncates index bytes to their low * nibble, causing aliasing across 16-byte boundaries and silently skipping * real delimiters. The cmpeq approach is alias-free and equally fast for * small sentinel sets. * * UTF-8 safe: all CSV structural bytes are single-byte ASCII; multi-byte * sequences (values > 0x7F) are never misidentified as special. */ /** @file * A standalone header file containing shared code */ #include #include #include #include #include #if defined(_WIN32) # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN # endif # include # undef max # undef min #elif defined(__linux__) # include #endif /** Helper macro which should be #defined as "inline" * in the single header version */ #define CSV_INLINE inline #include #if defined(__EMSCRIPTEN__) #undef CSV_ENABLE_THREADS #define CSV_ENABLE_THREADS 0 #elif !defined(CSV_ENABLE_THREADS) #define CSV_ENABLE_THREADS 1 #endif // Minimal portability macros (Hedley subset) with CSV_ prefix. #if defined(__clang__) || defined(__GNUC__) #define CSV_CONST __attribute__((__const__)) #define CSV_PURE __attribute__((__pure__)) #if defined(_WIN32) #define CSV_PRIVATE #else #define CSV_PRIVATE __attribute__((__visibility__("hidden"))) #endif #define CSV_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__))) #elif defined(_MSC_VER) #define CSV_CONST #define CSV_PURE #define CSV_PRIVATE #define CSV_NON_NULL(...) #else #define CSV_CONST #define CSV_PURE #define CSV_PRIVATE #define CSV_NON_NULL(...) #endif // This library uses C++ exceptions for error reporting in public APIs. #if defined(__cpp_exceptions) || defined(_CPPUNWIND) || defined(__EXCEPTIONS) #define CSV_EXCEPTIONS_ENABLED 1 #else #define CSV_EXCEPTIONS_ENABLED 0 #endif #if !CSV_EXCEPTIONS_ENABLED #error "csv-parser requires C++ exceptions. Enable exception handling (for example, remove -fno-exceptions or use /EHsc)." #endif // Detect C++ standard version BEFORE namespace to properly include string_view // MSVC: __cplusplus == 199711L unless /Zc:__cplusplus is set; use _MSVC_LANG instead. #if defined(_MSVC_LANG) && _MSVC_LANG > __cplusplus # define CSV_CPLUSPLUS _MSVC_LANG #else # define CSV_CPLUSPLUS __cplusplus #endif #if CSV_CPLUSPLUS >= 202002L #define CSV_HAS_CXX20 #endif #if CSV_CPLUSPLUS >= 201703L #define CSV_HAS_CXX17 #endif #if CSV_CPLUSPLUS >= 201402L #define CSV_HAS_CXX14 #endif // Include string_view BEFORE csv namespace to avoid namespace pollution issues #ifdef CSV_HAS_CXX17 #include #else // Copyright 2017-2019 by Martin Moene // // string-view lite, a C++17-like string_view for C++98 and later. // For more information see https://github.com/martinmoene/string-view-lite // // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #ifndef NONSTD_SV_LITE_H_INCLUDED #define NONSTD_SV_LITE_H_INCLUDED #define string_view_lite_MAJOR 1 #define string_view_lite_MINOR 1 #define string_view_lite_PATCH 0 #define string_view_lite_VERSION nssv_STRINGIFY(string_view_lite_MAJOR) "." nssv_STRINGIFY(string_view_lite_MINOR) "." nssv_STRINGIFY(string_view_lite_PATCH) #define nssv_STRINGIFY( x ) nssv_STRINGIFY_( x ) #define nssv_STRINGIFY_( x ) #x // string-view lite configuration: #define nssv_STRING_VIEW_DEFAULT 0 #define nssv_STRING_VIEW_NONSTD 1 #define nssv_STRING_VIEW_STD 2 #if !defined( nssv_CONFIG_SELECT_STRING_VIEW ) # define nssv_CONFIG_SELECT_STRING_VIEW ( nssv_HAVE_STD_STRING_VIEW ? nssv_STRING_VIEW_STD : nssv_STRING_VIEW_NONSTD ) #endif #if defined( nssv_CONFIG_SELECT_STD_STRING_VIEW ) || defined( nssv_CONFIG_SELECT_NONSTD_STRING_VIEW ) # error nssv_CONFIG_SELECT_STD_STRING_VIEW and nssv_CONFIG_SELECT_NONSTD_STRING_VIEW are deprecated and removed, please use nssv_CONFIG_SELECT_STRING_VIEW=nssv_STRING_VIEW_... #endif #ifndef nssv_CONFIG_STD_SV_OPERATOR # define nssv_CONFIG_STD_SV_OPERATOR 0 #endif #ifndef nssv_CONFIG_USR_SV_OPERATOR # define nssv_CONFIG_USR_SV_OPERATOR 1 #endif #ifdef nssv_CONFIG_CONVERSION_STD_STRING # define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS nssv_CONFIG_CONVERSION_STD_STRING # define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS nssv_CONFIG_CONVERSION_STD_STRING #endif #ifndef nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS # define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS 1 #endif #ifndef nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS # define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS 1 #endif // Control presence of exception handling (try and auto discover): #ifndef nssv_CONFIG_NO_EXCEPTIONS # if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND) # define nssv_CONFIG_NO_EXCEPTIONS 0 # else # define nssv_CONFIG_NO_EXCEPTIONS 1 # endif #endif // C++ language version detection (C++20 is speculative): // Note: VC14.0/1900 (VS2015) lacks too much from C++14. #ifndef nssv_CPLUSPLUS # if defined(_MSVC_LANG ) && !defined(__clang__) # define nssv_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG ) # else # define nssv_CPLUSPLUS __cplusplus # endif #endif #define nssv_CPP98_OR_GREATER ( nssv_CPLUSPLUS >= 199711L ) #define nssv_CPP11_OR_GREATER ( nssv_CPLUSPLUS >= 201103L ) #define nssv_CPP11_OR_GREATER_ ( nssv_CPLUSPLUS >= 201103L ) #define nssv_CPP14_OR_GREATER ( nssv_CPLUSPLUS >= 201402L ) #define nssv_CPP17_OR_GREATER ( nssv_CPLUSPLUS >= 201703L ) #define nssv_CPP20_OR_GREATER ( nssv_CPLUSPLUS >= 202000L ) // use C++17 std::string_view if available and requested: #if nssv_CPP17_OR_GREATER && defined(__has_include ) # if __has_include( ) # define nssv_HAVE_STD_STRING_VIEW 1 # else # define nssv_HAVE_STD_STRING_VIEW 0 # endif #else # define nssv_HAVE_STD_STRING_VIEW 0 #endif #define nssv_USES_STD_STRING_VIEW ( (nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_STD) || ((nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_DEFAULT) && nssv_HAVE_STD_STRING_VIEW) ) #define nssv_HAVE_STARTS_WITH ( nssv_CPP20_OR_GREATER || !nssv_USES_STD_STRING_VIEW ) #define nssv_HAVE_ENDS_WITH nssv_HAVE_STARTS_WITH // // Use C++17 std::string_view: // #if nssv_USES_STD_STRING_VIEW #include // Extensions for std::string: #if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS namespace nonstd { template< class CharT, class Traits, class Allocator = std::allocator > std::basic_string to_string( std::basic_string_view v, Allocator const & a = Allocator() ) { return std::basic_string( v.begin(), v.end(), a ); } template< class CharT, class Traits, class Allocator > std::basic_string_view to_string_view( std::basic_string const & s ) { return std::basic_string_view( s.data(), s.size() ); } // Literal operators sv and _sv: #if nssv_CONFIG_STD_SV_OPERATOR using namespace std::literals::string_view_literals; #endif #if nssv_CONFIG_USR_SV_OPERATOR inline namespace literals { inline namespace string_view_literals { constexpr std::string_view operator ""_sv( const char* str, size_t len ) noexcept // (1) { return std::string_view{ str, len }; } constexpr std::u16string_view operator ""_sv( const char16_t* str, size_t len ) noexcept // (2) { return std::u16string_view{ str, len }; } constexpr std::u32string_view operator ""_sv( const char32_t* str, size_t len ) noexcept // (3) { return std::u32string_view{ str, len }; } constexpr std::wstring_view operator ""_sv( const wchar_t* str, size_t len ) noexcept // (4) { return std::wstring_view{ str, len }; } }} // namespace literals::string_view_literals #endif // nssv_CONFIG_USR_SV_OPERATOR } // namespace nonstd #endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS namespace nonstd { using std::string_view; using std::wstring_view; using std::u16string_view; using std::u32string_view; using std::basic_string_view; // literal "sv" and "_sv", see above using std::operator==; using std::operator!=; using std::operator<; using std::operator<=; using std::operator>; using std::operator>=; using std::operator<<; } // namespace nonstd #else // nssv_HAVE_STD_STRING_VIEW // // Before C++17: use string_view lite: // // Compiler versions: // // MSVC++ 6.0 _MSC_VER == 1200 (Visual Studio 6.0) // MSVC++ 7.0 _MSC_VER == 1300 (Visual Studio .NET 2002) // MSVC++ 7.1 _MSC_VER == 1310 (Visual Studio .NET 2003) // MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005) // MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008) // MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010) // MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012) // MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013) // MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015) // MSVC++ 14.1 _MSC_VER >= 1910 (Visual Studio 2017) #if defined(_MSC_VER ) && !defined(__clang__) # define nssv_COMPILER_MSVC_VER (_MSC_VER ) # define nssv_COMPILER_MSVC_VERSION (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900 ) ) ) #else # define nssv_COMPILER_MSVC_VER 0 # define nssv_COMPILER_MSVC_VERSION 0 #endif #define nssv_COMPILER_VERSION( major, minor, patch ) (10 * ( 10 * major + minor) + patch) #if defined(__clang__) # define nssv_COMPILER_CLANG_VERSION nssv_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__) #else # define nssv_COMPILER_CLANG_VERSION 0 #endif #if defined(__GNUC__) && !defined(__clang__) # define nssv_COMPILER_GNUC_VERSION nssv_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) #else # define nssv_COMPILER_GNUC_VERSION 0 #endif // half-open range [lo..hi): #define nssv_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) ) // Presence of language and library features: #ifdef _HAS_CPP0X # define nssv_HAS_CPP0X _HAS_CPP0X #else # define nssv_HAS_CPP0X 0 #endif // Unless defined otherwise below, consider VC14 as C++11 for variant-lite: #if nssv_COMPILER_MSVC_VER >= 1900 # undef nssv_CPP11_OR_GREATER # define nssv_CPP11_OR_GREATER 1 #endif #define nssv_CPP11_90 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1500) #define nssv_CPP11_100 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1600) #define nssv_CPP11_110 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1700) #define nssv_CPP11_120 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1800) #define nssv_CPP11_140 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1900) #define nssv_CPP11_141 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1910) #define nssv_CPP14_000 (nssv_CPP14_OR_GREATER) #define nssv_CPP17_000 (nssv_CPP17_OR_GREATER) // Presence of C++11 language features: #define nssv_HAVE_CONSTEXPR_11 nssv_CPP11_140 #define nssv_HAVE_EXPLICIT_CONVERSION nssv_CPP11_140 #define nssv_HAVE_INLINE_NAMESPACE nssv_CPP11_140 #define nssv_HAVE_NOEXCEPT nssv_CPP11_140 #define nssv_HAVE_NULLPTR nssv_CPP11_100 #define nssv_HAVE_REF_QUALIFIER nssv_CPP11_140 #define nssv_HAVE_UNICODE_LITERALS nssv_CPP11_140 #define nssv_HAVE_USER_DEFINED_LITERALS nssv_CPP11_140 #define nssv_HAVE_WCHAR16_T nssv_CPP11_100 #define nssv_HAVE_WCHAR32_T nssv_CPP11_100 #if ! ( ( nssv_CPP11 && nssv_COMPILER_CLANG_VERSION ) || nssv_BETWEEN( nssv_COMPILER_CLANG_VERSION, 300, 400 ) ) # define nssv_HAVE_STD_DEFINED_LITERALS nssv_CPP11_140 #endif // Presence of C++14 language features: #define nssv_HAVE_CONSTEXPR_14 nssv_CPP14_000 // Presence of C++17 language features: #define nssv_HAVE_NODISCARD nssv_CPP17_000 // Presence of C++ library features: #define nssv_HAVE_STD_HASH nssv_CPP11_120 // C++ feature usage: #if nssv_HAVE_CONSTEXPR_11 # define nssv_constexpr constexpr #else # define nssv_constexpr /*constexpr*/ #endif #if nssv_HAVE_CONSTEXPR_14 # define nssv_constexpr14 constexpr #else # define nssv_constexpr14 /*constexpr*/ #endif #if nssv_HAVE_EXPLICIT_CONVERSION # define nssv_explicit explicit #else # define nssv_explicit /*explicit*/ #endif #if nssv_HAVE_INLINE_NAMESPACE # define nssv_inline_ns inline #else # define nssv_inline_ns /*inline*/ #endif #if nssv_HAVE_NOEXCEPT # define nssv_noexcept noexcept #else # define nssv_noexcept /*noexcept*/ #endif //#if nssv_HAVE_REF_QUALIFIER //# define nssv_ref_qual & //# define nssv_refref_qual && //#else //# define nssv_ref_qual /*&*/ //# define nssv_refref_qual /*&&*/ //#endif #if nssv_HAVE_NULLPTR # define nssv_nullptr nullptr #else # define nssv_nullptr NULL #endif #if nssv_HAVE_NODISCARD # define nssv_nodiscard [[nodiscard]] #else # define nssv_nodiscard /*[[nodiscard]]*/ #endif // Additional includes: #include #include #include #include #include #include // std::char_traits<> #if ! nssv_CONFIG_NO_EXCEPTIONS # include #endif #if nssv_CPP11_OR_GREATER # include #endif // Clang, GNUC, MSVC warning suppression macros: #if defined(__clang__) # pragma clang diagnostic ignored "-Wreserved-user-defined-literal" # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wuser-defined-literals" #elif defined(__GNUC__) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wliteral-suffix" #endif // __clang__ #if nssv_COMPILER_MSVC_VERSION >= 140 # define nssv_SUPPRESS_MSGSL_WARNING(expr) [[gsl::suppress(expr)]] # define nssv_SUPPRESS_MSVC_WARNING(code, descr) __pragma(warning(suppress: code) ) # define nssv_DISABLE_MSVC_WARNINGS(codes) __pragma(warning(push)) __pragma(warning(disable: codes)) #else # define nssv_SUPPRESS_MSGSL_WARNING(expr) # define nssv_SUPPRESS_MSVC_WARNING(code, descr) # define nssv_DISABLE_MSVC_WARNINGS(codes) #endif #if defined(__clang__) # define nssv_RESTORE_WARNINGS() _Pragma("clang diagnostic pop") #elif defined(__GNUC__) # define nssv_RESTORE_WARNINGS() _Pragma("GCC diagnostic pop") #elif nssv_COMPILER_MSVC_VERSION >= 140 # define nssv_RESTORE_WARNINGS() __pragma(warning(pop )) #else # define nssv_RESTORE_WARNINGS() #endif // Suppress the following MSVC (GSL) warnings: // - C4455, non-gsl : 'operator ""sv': literal suffix identifiers that do not // start with an underscore are reserved // - C26472, gsl::t.1 : don't use a static_cast for arithmetic conversions; // use brace initialization, gsl::narrow_cast or gsl::narow // - C26481: gsl::b.1 : don't use pointer arithmetic. Use span instead nssv_DISABLE_MSVC_WARNINGS( 4455 26481 26472 ) //nssv_DISABLE_CLANG_WARNINGS( "-Wuser-defined-literals" ) //nssv_DISABLE_GNUC_WARNINGS( -Wliteral-suffix ) namespace nonstd { namespace sv_lite { template < class CharT, class Traits = std::char_traits > class basic_string_view; // // basic_string_view: // template < class CharT, class Traits /* = std::char_traits */ > class basic_string_view { public: // Member types: typedef Traits traits_type; typedef CharT value_type; typedef CharT * pointer; typedef CharT const * const_pointer; typedef CharT & reference; typedef CharT const & const_reference; typedef const_pointer iterator; typedef const_pointer const_iterator; typedef std::reverse_iterator< const_iterator > reverse_iterator; typedef std::reverse_iterator< const_iterator > const_reverse_iterator; typedef std::size_t size_type; typedef std::ptrdiff_t difference_type; // 24.4.2.1 Construction and assignment: nssv_constexpr basic_string_view() nssv_noexcept : data_( nssv_nullptr ) , size_( 0 ) {} #if nssv_CPP11_OR_GREATER nssv_constexpr basic_string_view( basic_string_view const & other ) nssv_noexcept = default; #else nssv_constexpr basic_string_view( basic_string_view const & other ) nssv_noexcept : data_( other.data_) , size_( other.size_) {} #endif nssv_constexpr basic_string_view( CharT const * s, size_type count ) : data_( s ) , size_( count ) {} nssv_constexpr basic_string_view( CharT const * s) : data_( s ) , size_( Traits::length(s) ) {} // Assignment: #if nssv_CPP11_OR_GREATER nssv_constexpr14 basic_string_view & operator=( basic_string_view const & other ) nssv_noexcept = default; #else nssv_constexpr14 basic_string_view & operator=( basic_string_view const & other ) nssv_noexcept { data_ = other.data_; size_ = other.size_; return *this; } #endif // 24.4.2.2 Iterator support: nssv_constexpr const_iterator begin() const nssv_noexcept { return data_; } nssv_constexpr const_iterator end() const nssv_noexcept { return data_ + size_; } nssv_constexpr const_iterator cbegin() const nssv_noexcept { return begin(); } nssv_constexpr const_iterator cend() const nssv_noexcept { return end(); } nssv_constexpr const_reverse_iterator rbegin() const nssv_noexcept { return const_reverse_iterator( end() ); } nssv_constexpr const_reverse_iterator rend() const nssv_noexcept { return const_reverse_iterator( begin() ); } nssv_constexpr const_reverse_iterator crbegin() const nssv_noexcept { return rbegin(); } nssv_constexpr const_reverse_iterator crend() const nssv_noexcept { return rend(); } // 24.4.2.3 Capacity: nssv_constexpr size_type size() const nssv_noexcept { return size_; } nssv_constexpr size_type length() const nssv_noexcept { return size_; } nssv_constexpr size_type max_size() const nssv_noexcept { return (std::numeric_limits< size_type >::max)(); } // since C++20 nssv_nodiscard nssv_constexpr bool empty() const nssv_noexcept { return 0 == size_; } // 24.4.2.4 Element access: nssv_constexpr const_reference operator[]( size_type pos ) const { return data_at( pos ); } nssv_constexpr14 const_reference at( size_type pos ) const { #if nssv_CONFIG_NO_EXCEPTIONS assert( pos < size() ); #else if ( pos >= size() ) { throw std::out_of_range("nonst::string_view::at()"); } #endif return data_at( pos ); } nssv_constexpr const_reference front() const { return data_at( 0 ); } nssv_constexpr const_reference back() const { return data_at( size() - 1 ); } nssv_constexpr const_pointer data() const nssv_noexcept { return data_; } // 24.4.2.5 Modifiers: nssv_constexpr14 void remove_prefix( size_type n ) { assert( n <= size() ); data_ += n; size_ -= n; } nssv_constexpr14 void remove_suffix( size_type n ) { assert( n <= size() ); size_ -= n; } nssv_constexpr14 void swap( basic_string_view & other ) nssv_noexcept { using std::swap; swap( data_, other.data_ ); swap( size_, other.size_ ); } // 24.4.2.6 String operations: size_type copy( CharT * dest, size_type n, size_type pos = 0 ) const { #if nssv_CONFIG_NO_EXCEPTIONS assert( pos <= size() ); #else if ( pos > size() ) { throw std::out_of_range("nonst::string_view::copy()"); } #endif const size_type rlen = (std::min)( n, size() - pos ); (void) Traits::copy( dest, data() + pos, rlen ); return rlen; } nssv_constexpr14 basic_string_view substr( size_type pos = 0, size_type n = npos ) const { #if nssv_CONFIG_NO_EXCEPTIONS assert( pos <= size() ); #else if ( pos > size() ) { throw std::out_of_range("nonst::string_view::substr()"); } #endif return basic_string_view( data() + pos, (std::min)( n, size() - pos ) ); } // compare(), 6x: nssv_constexpr14 int compare( basic_string_view other ) const nssv_noexcept // (1) { if ( const int result = Traits::compare( data(), other.data(), (std::min)( size(), other.size() ) ) ) return result; return size() == other.size() ? 0 : size() < other.size() ? -1 : 1; } nssv_constexpr int compare( size_type pos1, size_type n1, basic_string_view other ) const // (2) { return substr( pos1, n1 ).compare( other ); } nssv_constexpr int compare( size_type pos1, size_type n1, basic_string_view other, size_type pos2, size_type n2 ) const // (3) { return substr( pos1, n1 ).compare( other.substr( pos2, n2 ) ); } nssv_constexpr int compare( CharT const * s ) const // (4) { return compare( basic_string_view( s ) ); } nssv_constexpr int compare( size_type pos1, size_type n1, CharT const * s ) const // (5) { return substr( pos1, n1 ).compare( basic_string_view( s ) ); } nssv_constexpr int compare( size_type pos1, size_type n1, CharT const * s, size_type n2 ) const // (6) { return substr( pos1, n1 ).compare( basic_string_view( s, n2 ) ); } // 24.4.2.7 Searching: // starts_with(), 3x, since C++20: nssv_constexpr bool starts_with( basic_string_view v ) const nssv_noexcept // (1) { return size() >= v.size() && compare( 0, v.size(), v ) == 0; } nssv_constexpr bool starts_with( CharT c ) const nssv_noexcept // (2) { return starts_with( basic_string_view( &c, 1 ) ); } nssv_constexpr bool starts_with( CharT const * s ) const // (3) { return starts_with( basic_string_view( s ) ); } // ends_with(), 3x, since C++20: nssv_constexpr bool ends_with( basic_string_view v ) const nssv_noexcept // (1) { return size() >= v.size() && compare( size() - v.size(), npos, v ) == 0; } nssv_constexpr bool ends_with( CharT c ) const nssv_noexcept // (2) { return ends_with( basic_string_view( &c, 1 ) ); } nssv_constexpr bool ends_with( CharT const * s ) const // (3) { return ends_with( basic_string_view( s ) ); } // find(), 4x: nssv_constexpr14 size_type find( basic_string_view v, size_type pos = 0 ) const nssv_noexcept // (1) { return assert( v.size() == 0 || v.data() != nssv_nullptr ) , pos >= size() ? npos : to_pos( std::search( cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq ) ); } nssv_constexpr14 size_type find( CharT c, size_type pos = 0 ) const nssv_noexcept // (2) { return find( basic_string_view( &c, 1 ), pos ); } nssv_constexpr14 size_type find( CharT const * s, size_type pos, size_type n ) const // (3) { return find( basic_string_view( s, n ), pos ); } nssv_constexpr14 size_type find( CharT const * s, size_type pos = 0 ) const // (4) { return find( basic_string_view( s ), pos ); } // rfind(), 4x: nssv_constexpr14 size_type rfind( basic_string_view v, size_type pos = npos ) const nssv_noexcept // (1) { if ( size() < v.size() ) return npos; if ( v.empty() ) return (std::min)( size(), pos ); const_iterator last = cbegin() + (std::min)( size() - v.size(), pos ) + v.size(); const_iterator result = std::find_end( cbegin(), last, v.cbegin(), v.cend(), Traits::eq ); return result != last ? size_type( result - cbegin() ) : npos; } nssv_constexpr14 size_type rfind( CharT c, size_type pos = npos ) const nssv_noexcept // (2) { return rfind( basic_string_view( &c, 1 ), pos ); } nssv_constexpr14 size_type rfind( CharT const * s, size_type pos, size_type n ) const // (3) { return rfind( basic_string_view( s, n ), pos ); } nssv_constexpr14 size_type rfind( CharT const * s, size_type pos = npos ) const // (4) { return rfind( basic_string_view( s ), pos ); } // find_first_of(), 4x: nssv_constexpr size_type find_first_of( basic_string_view v, size_type pos = 0 ) const nssv_noexcept // (1) { return pos >= size() ? npos : to_pos( std::find_first_of( cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq ) ); } nssv_constexpr size_type find_first_of( CharT c, size_type pos = 0 ) const nssv_noexcept // (2) { return find_first_of( basic_string_view( &c, 1 ), pos ); } nssv_constexpr size_type find_first_of( CharT const * s, size_type pos, size_type n ) const // (3) { return find_first_of( basic_string_view( s, n ), pos ); } nssv_constexpr size_type find_first_of( CharT const * s, size_type pos = 0 ) const // (4) { return find_first_of( basic_string_view( s ), pos ); } // find_last_of(), 4x: nssv_constexpr size_type find_last_of( basic_string_view v, size_type pos = npos ) const nssv_noexcept // (1) { return empty() ? npos : pos >= size() ? find_last_of( v, size() - 1 ) : to_pos( std::find_first_of( const_reverse_iterator( cbegin() + pos + 1 ), crend(), v.cbegin(), v.cend(), Traits::eq ) ); } nssv_constexpr size_type find_last_of( CharT c, size_type pos = npos ) const nssv_noexcept // (2) { return find_last_of( basic_string_view( &c, 1 ), pos ); } nssv_constexpr size_type find_last_of( CharT const * s, size_type pos, size_type count ) const // (3) { return find_last_of( basic_string_view( s, count ), pos ); } nssv_constexpr size_type find_last_of( CharT const * s, size_type pos = npos ) const // (4) { return find_last_of( basic_string_view( s ), pos ); } // find_first_not_of(), 4x: nssv_constexpr size_type find_first_not_of( basic_string_view v, size_type pos = 0 ) const nssv_noexcept // (1) { return pos >= size() ? npos : to_pos( std::find_if( cbegin() + pos, cend(), not_in_view( v ) ) ); } nssv_constexpr size_type find_first_not_of( CharT c, size_type pos = 0 ) const nssv_noexcept // (2) { return find_first_not_of( basic_string_view( &c, 1 ), pos ); } nssv_constexpr size_type find_first_not_of( CharT const * s, size_type pos, size_type count ) const // (3) { return find_first_not_of( basic_string_view( s, count ), pos ); } nssv_constexpr size_type find_first_not_of( CharT const * s, size_type pos = 0 ) const // (4) { return find_first_not_of( basic_string_view( s ), pos ); } // find_last_not_of(), 4x: nssv_constexpr size_type find_last_not_of( basic_string_view v, size_type pos = npos ) const nssv_noexcept // (1) { return empty() ? npos : pos >= size() ? find_last_not_of( v, size() - 1 ) : to_pos( std::find_if( const_reverse_iterator( cbegin() + pos + 1 ), crend(), not_in_view( v ) ) ); } nssv_constexpr size_type find_last_not_of( CharT c, size_type pos = npos ) const nssv_noexcept // (2) { return find_last_not_of( basic_string_view( &c, 1 ), pos ); } nssv_constexpr size_type find_last_not_of( CharT const * s, size_type pos, size_type count ) const // (3) { return find_last_not_of( basic_string_view( s, count ), pos ); } nssv_constexpr size_type find_last_not_of( CharT const * s, size_type pos = npos ) const // (4) { return find_last_not_of( basic_string_view( s ), pos ); } // Constants: #if nssv_CPP17_OR_GREATER static nssv_constexpr size_type npos = size_type(-1); #elif nssv_CPP11_OR_GREATER enum : size_type { npos = size_type(-1) }; #else enum { npos = size_type(-1) }; #endif private: struct not_in_view { const basic_string_view v; nssv_constexpr not_in_view( basic_string_view v ) : v( v ) {} nssv_constexpr bool operator()( CharT c ) const { return npos == v.find_first_of( c ); } }; nssv_constexpr size_type to_pos( const_iterator it ) const { return it == cend() ? npos : size_type( it - cbegin() ); } nssv_constexpr size_type to_pos( const_reverse_iterator it ) const { return it == crend() ? npos : size_type( crend() - it - 1 ); } nssv_constexpr const_reference data_at( size_type pos ) const { #if nssv_BETWEEN( nssv_COMPILER_GNUC_VERSION, 1, 500 ) return data_[pos]; #else return assert( pos < size() ), data_[pos]; #endif } private: const_pointer data_; size_type size_; public: #if nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS template< class Allocator > basic_string_view( std::basic_string const & s ) nssv_noexcept : data_( s.data() ) , size_( s.size() ) {} #if nssv_HAVE_EXPLICIT_CONVERSION template< class Allocator > explicit operator std::basic_string() const { return to_string( Allocator() ); } #endif // nssv_HAVE_EXPLICIT_CONVERSION #if nssv_CPP11_OR_GREATER template< class Allocator = std::allocator > std::basic_string to_string( Allocator const & a = Allocator() ) const { return std::basic_string( begin(), end(), a ); } #else std::basic_string to_string() const { return std::basic_string( begin(), end() ); } template< class Allocator > std::basic_string to_string( Allocator const & a ) const { return std::basic_string( begin(), end(), a ); } #endif // nssv_CPP11_OR_GREATER #endif // nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS }; // // Non-member functions: // // 24.4.3 Non-member comparison functions: // lexicographically compare two string views (function template): template< class CharT, class Traits > nssv_constexpr bool operator== ( basic_string_view lhs, basic_string_view rhs ) nssv_noexcept { return lhs.compare( rhs ) == 0 ; } template< class CharT, class Traits > nssv_constexpr bool operator!= ( basic_string_view lhs, basic_string_view rhs ) nssv_noexcept { return lhs.compare( rhs ) != 0 ; } template< class CharT, class Traits > nssv_constexpr bool operator< ( basic_string_view lhs, basic_string_view rhs ) nssv_noexcept { return lhs.compare( rhs ) < 0 ; } template< class CharT, class Traits > nssv_constexpr bool operator<= ( basic_string_view lhs, basic_string_view rhs ) nssv_noexcept { return lhs.compare( rhs ) <= 0 ; } template< class CharT, class Traits > nssv_constexpr bool operator> ( basic_string_view lhs, basic_string_view rhs ) nssv_noexcept { return lhs.compare( rhs ) > 0 ; } template< class CharT, class Traits > nssv_constexpr bool operator>= ( basic_string_view lhs, basic_string_view rhs ) nssv_noexcept { return lhs.compare( rhs ) >= 0 ; } // Let S be basic_string_view, and sv be an instance of S. // Implementations shall provide sufficient additional overloads marked // constexpr and noexcept so that an object t with an implicit conversion // to S can be compared according to Table 67. #if nssv_CPP11_OR_GREATER && ! nssv_BETWEEN( nssv_COMPILER_MSVC_VERSION, 100, 141 ) #define nssv_BASIC_STRING_VIEW_I(T,U) typename std::decay< basic_string_view >::type #if nssv_BETWEEN( nssv_COMPILER_MSVC_VERSION, 140, 150 ) # define nssv_MSVC_ORDER(x) , int=x #else # define nssv_MSVC_ORDER(x) /*, int=x*/ #endif // == template< class CharT, class Traits nssv_MSVC_ORDER(1) > nssv_constexpr bool operator==( basic_string_view lhs, nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs ) nssv_noexcept { return lhs.compare( rhs ) == 0; } template< class CharT, class Traits nssv_MSVC_ORDER(2) > nssv_constexpr bool operator==( nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs, basic_string_view rhs ) nssv_noexcept { return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; } // != template< class CharT, class Traits nssv_MSVC_ORDER(1) > nssv_constexpr bool operator!= ( basic_string_view < CharT, Traits > lhs, nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept { return lhs.size() != rhs.size() || lhs.compare( rhs ) != 0 ; } template< class CharT, class Traits nssv_MSVC_ORDER(2) > nssv_constexpr bool operator!= ( nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs, basic_string_view < CharT, Traits > rhs ) nssv_noexcept { return lhs.compare( rhs ) != 0 ; } // < template< class CharT, class Traits nssv_MSVC_ORDER(1) > nssv_constexpr bool operator< ( basic_string_view < CharT, Traits > lhs, nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept { return lhs.compare( rhs ) < 0 ; } template< class CharT, class Traits nssv_MSVC_ORDER(2) > nssv_constexpr bool operator< ( nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs, basic_string_view < CharT, Traits > rhs ) nssv_noexcept { return lhs.compare( rhs ) < 0 ; } // <= template< class CharT, class Traits nssv_MSVC_ORDER(1) > nssv_constexpr bool operator<= ( basic_string_view < CharT, Traits > lhs, nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept { return lhs.compare( rhs ) <= 0 ; } template< class CharT, class Traits nssv_MSVC_ORDER(2) > nssv_constexpr bool operator<= ( nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs, basic_string_view < CharT, Traits > rhs ) nssv_noexcept { return lhs.compare( rhs ) <= 0 ; } // > template< class CharT, class Traits nssv_MSVC_ORDER(1) > nssv_constexpr bool operator> ( basic_string_view < CharT, Traits > lhs, nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept { return lhs.compare( rhs ) > 0 ; } template< class CharT, class Traits nssv_MSVC_ORDER(2) > nssv_constexpr bool operator> ( nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs, basic_string_view < CharT, Traits > rhs ) nssv_noexcept { return lhs.compare( rhs ) > 0 ; } // >= template< class CharT, class Traits nssv_MSVC_ORDER(1) > nssv_constexpr bool operator>= ( basic_string_view < CharT, Traits > lhs, nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept { return lhs.compare( rhs ) >= 0 ; } template< class CharT, class Traits nssv_MSVC_ORDER(2) > nssv_constexpr bool operator>= ( nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs, basic_string_view < CharT, Traits > rhs ) nssv_noexcept { return lhs.compare( rhs ) >= 0 ; } #undef nssv_MSVC_ORDER #undef nssv_BASIC_STRING_VIEW_I #endif // nssv_CPP11_OR_GREATER // 24.4.4 Inserters and extractors: namespace detail { template< class Stream > void write_padding( Stream & os, std::streamsize n ) { for ( std::streamsize i = 0; i < n; ++i ) os.rdbuf()->sputc( os.fill() ); } template< class Stream, class View > Stream & write_to_stream( Stream & os, View const & sv ) { typename Stream::sentry sentry( os ); if ( !os ) return os; const std::streamsize length = static_cast( sv.length() ); // Whether, and how, to pad: const bool pad = ( length < os.width() ); const bool left_pad = pad && ( os.flags() & std::ios_base::adjustfield ) == std::ios_base::right; if ( left_pad ) write_padding( os, os.width() - length ); // Write span characters: os.rdbuf()->sputn( sv.begin(), length ); if ( pad && !left_pad ) write_padding( os, os.width() - length ); // Reset output stream width: os.width( 0 ); return os; } } // namespace detail template< class CharT, class Traits > std::basic_ostream & operator<<( std::basic_ostream& os, basic_string_view sv ) { return detail::write_to_stream( os, sv ); } // Several typedefs for common character types are provided: typedef basic_string_view string_view; typedef basic_string_view wstring_view; #if nssv_HAVE_WCHAR16_T typedef basic_string_view u16string_view; typedef basic_string_view u32string_view; #endif }} // namespace nonstd::sv_lite // // 24.4.6 Suffix for basic_string_view literals: // #if nssv_HAVE_USER_DEFINED_LITERALS namespace nonstd { nssv_inline_ns namespace literals { nssv_inline_ns namespace string_view_literals { #if nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS nssv_constexpr nonstd::sv_lite::string_view operator "" sv( const char* str, size_t len ) nssv_noexcept // (1) { return nonstd::sv_lite::string_view{ str, len }; } nssv_constexpr nonstd::sv_lite::u16string_view operator "" sv( const char16_t* str, size_t len ) nssv_noexcept // (2) { return nonstd::sv_lite::u16string_view{ str, len }; } nssv_constexpr nonstd::sv_lite::u32string_view operator "" sv( const char32_t* str, size_t len ) nssv_noexcept // (3) { return nonstd::sv_lite::u32string_view{ str, len }; } nssv_constexpr nonstd::sv_lite::wstring_view operator "" sv( const wchar_t* str, size_t len ) nssv_noexcept // (4) { return nonstd::sv_lite::wstring_view{ str, len }; } #endif // nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS #if nssv_CONFIG_USR_SV_OPERATOR nssv_constexpr nonstd::sv_lite::string_view operator "" _sv( const char* str, size_t len ) nssv_noexcept // (1) { return nonstd::sv_lite::string_view{ str, len }; } nssv_constexpr nonstd::sv_lite::u16string_view operator "" _sv( const char16_t* str, size_t len ) nssv_noexcept // (2) { return nonstd::sv_lite::u16string_view{ str, len }; } nssv_constexpr nonstd::sv_lite::u32string_view operator "" _sv( const char32_t* str, size_t len ) nssv_noexcept // (3) { return nonstd::sv_lite::u32string_view{ str, len }; } nssv_constexpr nonstd::sv_lite::wstring_view operator "" _sv( const wchar_t* str, size_t len ) nssv_noexcept // (4) { return nonstd::sv_lite::wstring_view{ str, len }; } #endif // nssv_CONFIG_USR_SV_OPERATOR }}} // namespace nonstd::literals::string_view_literals #endif // // Extensions for std::string: // #if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS namespace nonstd { namespace sv_lite { // Exclude MSVC 14 (19.00): it yields ambiguous to_string(): #if nssv_CPP11_OR_GREATER && nssv_COMPILER_MSVC_VERSION != 140 template< class CharT, class Traits, class Allocator = std::allocator > std::basic_string to_string( basic_string_view v, Allocator const & a = Allocator() ) { return std::basic_string( v.begin(), v.end(), a ); } #else template< class CharT, class Traits > std::basic_string to_string( basic_string_view v ) { return std::basic_string( v.begin(), v.end() ); } template< class CharT, class Traits, class Allocator > std::basic_string to_string( basic_string_view v, Allocator const & a ) { return std::basic_string( v.begin(), v.end(), a ); } #endif // nssv_CPP11_OR_GREATER template< class CharT, class Traits, class Allocator > basic_string_view to_string_view( std::basic_string const & s ) { return basic_string_view( s.data(), s.size() ); } }} // namespace nonstd::sv_lite #endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS // // make types and algorithms available in namespace nonstd: // namespace nonstd { using sv_lite::basic_string_view; using sv_lite::string_view; using sv_lite::wstring_view; #if nssv_HAVE_WCHAR16_T using sv_lite::u16string_view; #endif #if nssv_HAVE_WCHAR32_T using sv_lite::u32string_view; #endif // literal "sv" using sv_lite::operator==; using sv_lite::operator!=; using sv_lite::operator<; using sv_lite::operator<=; using sv_lite::operator>; using sv_lite::operator>=; using sv_lite::operator<<; #if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS using sv_lite::to_string; using sv_lite::to_string_view; #endif } // namespace nonstd // 24.4.5 Hash support (C++11): // Note: The hash value of a string view object is equal to the hash value of // the corresponding string object. #if nssv_HAVE_STD_HASH #include namespace std { template<> struct hash< nonstd::string_view > { public: std::size_t operator()( nonstd::string_view v ) const nssv_noexcept { return std::hash()( std::string( v.data(), v.size() ) ); } }; template<> struct hash< nonstd::wstring_view > { public: std::size_t operator()( nonstd::wstring_view v ) const nssv_noexcept { return std::hash()( std::wstring( v.data(), v.size() ) ); } }; template<> struct hash< nonstd::u16string_view > { public: std::size_t operator()( nonstd::u16string_view v ) const nssv_noexcept { return std::hash()( std::u16string( v.data(), v.size() ) ); } }; template<> struct hash< nonstd::u32string_view > { public: std::size_t operator()( nonstd::u32string_view v ) const nssv_noexcept { return std::hash()( std::u32string( v.data(), v.size() ) ); } }; } // namespace std #endif // nssv_HAVE_STD_HASH nssv_RESTORE_WARNINGS() #endif // nssv_HAVE_STD_STRING_VIEW #endif // NONSTD_SV_LITE_H_INCLUDED #endif namespace csv { #ifdef _MSC_VER #pragma region Compatibility Macros #endif /** * @def IF_CONSTEXPR * Expands to `if constexpr` in C++17 and `if` otherwise * * @def CONSTEXPR_VALUE * Expands to `constexpr` in C++17 and `const` otherwise. * Mainly used for global variables. * * @def CONSTEXPR * Expands to `constexpr` in decent compilers and `inline` otherwise. * Intended for functions and methods. */ #define STATIC_ASSERT(x) static_assert(x, "Assertion failed") #ifdef CSV_HAS_CXX17 /** @typedef string_view * The string_view class used by this library. */ using string_view = std::string_view; #else /** @typedef string_view * The string_view class used by this library. */ using string_view = nonstd::string_view; #endif #ifdef CSV_HAS_CXX17 #define IF_CONSTEXPR if constexpr #define CONSTEXPR_VALUE constexpr #define CONSTEXPR_17 constexpr #else #define IF_CONSTEXPR if #define CONSTEXPR_VALUE const #define CONSTEXPR_17 inline #endif #ifdef CSV_HAS_CXX14 template using enable_if_t = std::enable_if_t; #define CONSTEXPR_14 constexpr #define CONSTEXPR_VALUE_14 constexpr #else template using enable_if_t = typename std::enable_if::type; #define CONSTEXPR_14 inline #define CONSTEXPR_VALUE_14 const #endif #ifdef CSV_HAS_CXX17 template using invoke_result_t = typename std::invoke_result::type; #else template using invoke_result_t = typename std::result_of::type; #endif // Resolves g++ bug with regard to constexpr methods. // Keep this gated to C++17+, since C++11/14 pedantic mode rejects constexpr // non-static members when the enclosing class is non-literal. // See: https://stackoverflow.com/questions/36489369/constexpr-non-static-member-function-with-non-constexpr-constructor-gcc-clang-d #if defined(__GNUC__) && !defined(__clang__) #if defined(CSV_HAS_CXX17) && (((__GNUC__ == 7) && (__GNUC_MINOR__ >= 2)) || (__GNUC__ >= 8)) #define CONSTEXPR constexpr #endif #else #ifdef CSV_HAS_CXX17 #define CONSTEXPR constexpr #endif #endif #ifndef CONSTEXPR #define CONSTEXPR inline #endif #ifdef _MSC_VER #pragma endregion #endif namespace internals { // PAGE_SIZE macro could be already defined by the host system. #if defined(PAGE_SIZE) #undef PAGE_SIZE #endif // Get operating system specific details #if defined(_WIN32) inline int getpagesize() { _SYSTEM_INFO sys_info = {}; GetSystemInfo(&sys_info); return std::max(sys_info.dwPageSize, sys_info.dwAllocationGranularity); } const int PAGE_SIZE = getpagesize(); #elif defined(__linux__) const int PAGE_SIZE = getpagesize(); #else /** Size of a memory page in bytes. Used by * csv::internals::CSVFieldArray when allocating blocks. */ const int PAGE_SIZE = 4096; #endif /** Chunk size for lazy-loading large CSV files * * The worker thread reads this many bytes at a time (10MB). * * CRITICAL INVARIANT: Field boundaries at chunk transitions must be preserved. * Bug #280 was caused by fields spanning chunk boundaries being corrupted. * * @note Tests must write >10MB of data to cross chunk boundaries * @see basic_csv_parser.cpp MmapParser::next() for chunk transition logic */ constexpr size_t ITERATION_CHUNK_SIZE = 10000000; // 10MB template inline bool is_equal(T a, T b, T epsilon = 0.001) { /** Returns true if two floating point values are about the same */ static_assert(std::is_floating_point::value, "T must be a floating point type."); return std::abs(a - b) < epsilon; } /** @typedef ParseFlags * An enum used for describing the significance of each character * with respect to CSV parsing * * @see quote_escape_flag */ enum class ParseFlags { QUOTE_ESCAPE_QUOTE = 0, /**< A quote inside or terminating a quote_escaped field */ QUOTE = 2 | 1, /**< Characters which may signify a quote escape */ NOT_SPECIAL = 4, /**< Characters with no special meaning or escaped delimiters and newlines */ DELIMITER = 4 | 2, /**< Characters which signify a new field */ NEWLINE = 4 | 2 | 1 /**< Characters which signify a new row */ }; /** Transform the ParseFlags given the context of whether or not the current * field is quote escaped */ constexpr ParseFlags quote_escape_flag(ParseFlags flag, bool quote_escape) noexcept { return (ParseFlags)((int)flag & ~((int)ParseFlags::QUOTE * quote_escape)); } // Assumed to be true by parsing functions: allows for testing // if an item is DELIMITER or NEWLINE with a >= statement STATIC_ASSERT(ParseFlags::DELIMITER < ParseFlags::NEWLINE); /** Optimizations for reducing branching in parsing loop * * Idea: The meaning of all non-quote characters changes depending * on whether or not the parser is in a quote-escaped mode (0 or 1) */ STATIC_ASSERT(quote_escape_flag(ParseFlags::NOT_SPECIAL, false) == ParseFlags::NOT_SPECIAL); STATIC_ASSERT(quote_escape_flag(ParseFlags::QUOTE, false) == ParseFlags::QUOTE); STATIC_ASSERT(quote_escape_flag(ParseFlags::DELIMITER, false) == ParseFlags::DELIMITER); STATIC_ASSERT(quote_escape_flag(ParseFlags::NEWLINE, false) == ParseFlags::NEWLINE); STATIC_ASSERT(quote_escape_flag(ParseFlags::NOT_SPECIAL, true) == ParseFlags::NOT_SPECIAL); STATIC_ASSERT(quote_escape_flag(ParseFlags::QUOTE, true) == ParseFlags::QUOTE_ESCAPE_QUOTE); STATIC_ASSERT(quote_escape_flag(ParseFlags::DELIMITER, true) == ParseFlags::NOT_SPECIAL); STATIC_ASSERT(quote_escape_flag(ParseFlags::NEWLINE, true) == ParseFlags::NOT_SPECIAL); /** An array which maps ASCII chars to a parsing flag */ using ParseFlagMap = std::array; /** An array which maps ASCII chars to a flag indicating if it is whitespace */ using WhitespaceMap = std::array; } /** Integer indicating a requested column wasn't found. */ constexpr int CSV_NOT_FOUND = -1; /** Offset to convert char into array index. */ constexpr unsigned CHAR_OFFSET = std::numeric_limits::is_signed ? 128 : 0; } #if (defined(__AVX2__) || defined(__SSE2__)) && !defined(CSV_NO_SIMD) #include // _tzcnt_u32 in GCC/Clang headers is __attribute__(__target__("bmi")), which // requires -mbmi at the call site. __builtin_ctz has no such restriction and // emits BSF/TZCNT as the optimizer sees fit. MSVC's _tzcnt_u32 has no // equivalent restriction, so keep it there. # ifdef _MSC_VER # define CSV_TZCNT32(x) _tzcnt_u32(x) # else # define CSV_TZCNT32(x) static_cast(__builtin_ctz(x)) # endif #endif namespace csv { namespace internals { // Precomputed SIMD broadcast vectors for the four CSV sentinel bytes. // Constructed once per parser instance and passed by const-ref into // find_next_non_special, amortizing broadcast cost across every field // scan — meaningful for CSVs with many short fields. // // When no_quote mode is active, set quote_char = delimiter so that // quote bytes are not mistakenly treated as sentinels (they are // NOT_SPECIAL in that mode and must not cause SIMD to stop early). struct SentinelVecs { SentinelVecs() noexcept : SentinelVecs(',', '"') {} SentinelVecs(char delimiter, char quote_char) noexcept { #if defined(__AVX2__) && !defined(CSV_NO_SIMD) v_delim = _mm256_set1_epi8(delimiter); v_quote = _mm256_set1_epi8(quote_char); v_lf = _mm256_set1_epi8('\n'); v_cr = _mm256_set1_epi8('\r'); #elif defined(__SSE2__) && !defined(CSV_NO_SIMD) v_delim = _mm_set1_epi8(delimiter); v_quote = _mm_set1_epi8(quote_char); v_lf = _mm_set1_epi8('\n'); v_cr = _mm_set1_epi8('\r'); #else (void)delimiter; (void)quote_char; #endif } #if defined(__AVX2__) && !defined(CSV_NO_SIMD) __m256i v_delim, v_quote, v_lf, v_cr; #elif defined(__SSE2__) && !defined(CSV_NO_SIMD) __m128i v_delim, v_quote, v_lf, v_cr; #endif }; // Free function — easy to unit test independently of IBasicCSVParser. // // SIMD-only fast-forward: skips pos forward past any bytes that are // definitely not one of the four CSV sentinel characters. Stops as // soon as a sentinel byte is found OR fewer bytes remain than one // SIMD lane. The caller is responsible for the scalar tail loop using // compound_parse_flag, which correctly handles quote_escape state. // // State-agnostic by design: stops conservatively at any sentinel byte // regardless of quote_escape. Inside a quoted field, delimiter and // newline bytes are NOT_SPECIAL under compound_parse_flag, so the // outer DFA loop re-enters parse_field immediately at zero cost. inline size_t find_next_non_special( csv::string_view data, size_t pos, const SentinelVecs& sentinels ) noexcept { #if defined(__AVX2__) && !defined(CSV_NO_SIMD) while (pos + 32 <= data.size()) { __m256i bytes = _mm256_loadu_si256(reinterpret_cast(data.data() + pos)); __m256i special = _mm256_cmpeq_epi8(bytes, sentinels.v_delim); special = _mm256_or_si256(special, _mm256_cmpeq_epi8(bytes, sentinels.v_quote)); special = _mm256_or_si256(special, _mm256_cmpeq_epi8(bytes, sentinels.v_lf)); special = _mm256_or_si256(special, _mm256_cmpeq_epi8(bytes, sentinels.v_cr)); int mask = _mm256_movemask_epi8(special); if (mask != 0) return pos + CSV_TZCNT32(static_cast(mask)); pos += 32; } #elif defined(__SSE2__) && !defined(CSV_NO_SIMD) while (pos + 16 <= data.size()) { __m128i bytes = _mm_loadu_si128(reinterpret_cast(data.data() + pos)); __m128i special = _mm_cmpeq_epi8(bytes, sentinels.v_delim); special = _mm_or_si128(special, _mm_cmpeq_epi8(bytes, sentinels.v_quote)); special = _mm_or_si128(special, _mm_cmpeq_epi8(bytes, sentinels.v_lf)); special = _mm_or_si128(special, _mm_cmpeq_epi8(bytes, sentinels.v_cr)); int mask = _mm_movemask_epi8(special); if (mask != 0) return pos + CSV_TZCNT32(static_cast(mask)); pos += 16; } #else (void)data; (void)sentinels; #endif return pos; } } } #include #include #include #include #include /** @file * Defines an object used to store CSV format settings */ #include #include #include #include namespace csv { namespace internals { class IBasicCSVParser; } class CSVReader; /** Determines how to handle rows that are shorter or longer than the majority */ enum class VariableColumnPolicy { THROW = -1, IGNORE_ROW = 0, KEEP = 1 }; /** Determines how column name lookups are performed */ enum class ColumnNamePolicy { EXACT = 0, /**< Case-sensitive match (default) */ CASE_INSENSITIVE = 1 /**< Case-insensitive match */ }; /** Stores the inferred format of a CSV file. */ struct CSVGuessResult { char delim; int header_row; }; /** Stores information about how to parse a CSV file. * Can be used to construct a csv::CSVReader. */ class CSVFormat { public: /** Settings for parsing a RFC 4180 CSV file */ CSVFormat() = default; /** Sets the delimiter of the CSV file * * @throws `std::runtime_error` thrown if trim, quote, or possible delimiting characters overlap */ CSVFormat& delimiter(char delim); /** Sets a list of potential delimiters * * @throws `std::runtime_error` thrown if trim, quote, or possible delimiting characters overlap * @param[in] delim An array of possible delimiters to try parsing the CSV with */ CSVFormat& delimiter(const std::vector & delim); /** Sets the whitespace characters to be trimmed * * @throws `std::runtime_error` thrown if trim, quote, or possible delimiting characters overlap * @param[in] ws An array of whitespace characters that should be trimmed */ CSVFormat& trim(const std::vector & ws); /** Sets the quote character * * @throws `std::runtime_error` thrown if trim, quote, or possible delimiting characters overlap */ CSVFormat& quote(char quote); /** Sets the column names. * * @note Unsets any values set by header_row() */ CSVFormat& column_names(const std::vector& names); /** Sets the header row * * @note Unsets any values set by column_names() */ CSVFormat& header_row(int row); /** Tells the parser that this CSV has no header row * * @note Equivalent to `header_row(-1)` * */ CSVFormat& no_header() { this->header_row(-1); return *this; } /** Turn quoting on or off */ CSVFormat& quote(bool use_quote) { this->no_quote = !use_quote; return *this; } /** Tells the parser how to handle columns of a different length than the others */ CONSTEXPR_14 CSVFormat& variable_columns(VariableColumnPolicy policy = VariableColumnPolicy::IGNORE_ROW) { this->variable_column_policy = policy; return *this; } /** Tells the parser how to handle columns of a different length than the others */ CONSTEXPR_14 CSVFormat& variable_columns(bool policy) { this->variable_column_policy = (VariableColumnPolicy)policy; return *this; } /** Sets the column name lookup policy. * * @param[in] policy Use ColumnNamePolicy::CASE_INSENSITIVE to allow * case-insensitive column lookups via CSVRow::operator[] * and CSVReader::index_of(). */ CONSTEXPR_14 CSVFormat& column_names_policy(ColumnNamePolicy policy) { this->_column_name_policy = policy; return *this; } /** Sets the chunk size used when reading the CSV * * @param[in] size Chunk size in bytes (minimum: 10MB = ITERATION_CHUNK_SIZE) * @throws std::invalid_argument if size < ITERATION_CHUNK_SIZE * * Use this when constructing a CSVReader from a filename and individual rows * may exceed the default 10MB chunk size. The value is passed to CSVReader at * construction time, before any data is read. */ CSVFormat& chunk_size(size_t size); #ifndef DOXYGEN_SHOULD_SKIP_THIS char get_delim() const { // This error should never be received by end users. if (this->possible_delimiters.size() > 1) { throw std::runtime_error("There is more than one possible delimiter."); } return this->possible_delimiters.at(0); } CONSTEXPR bool is_quoting_enabled() const { return !this->no_quote; } CONSTEXPR char get_quote_char() const { return this->quote_char; } CONSTEXPR int get_header() const { return this->header; } std::vector get_possible_delims() const { return this->possible_delimiters; } std::vector get_trim_chars() const { return this->trim_chars; } CONSTEXPR VariableColumnPolicy get_variable_column_policy() const { return this->variable_column_policy; } CONSTEXPR ColumnNamePolicy get_column_name_policy() const { return this->_column_name_policy; } CONSTEXPR size_t get_chunk_size() const { return this->_chunk_size; } #endif /** CSVFormat for guessing the delimiter */ CSV_INLINE static CSVFormat guess_csv() { CSVFormat format; format.delimiter({ ',', '|', '\t', ';', '^' }) .quote('"') .header_row(0); return format; } bool guess_delim() { return this->possible_delimiters.size() > 1; } friend CSVReader; friend internals::IBasicCSVParser; private: /**< Throws an error if delimiters and trim characters overlap */ void assert_no_char_overlap(); /**< Set of possible delimiters */ std::vector possible_delimiters = { ',' }; /**< Set of whitespace characters to trim */ std::vector trim_chars = {}; /**< Row number with columns (ignored if col_names is non-empty) */ int header = 0; /**< Whether or not to use quoting */ bool no_quote = false; /**< Quote character */ char quote_char = '"'; /**< Should be left empty unless file doesn't include header */ std::vector col_names = {}; /**< Allow variable length columns? */ VariableColumnPolicy variable_column_policy = VariableColumnPolicy::IGNORE_ROW; /**< Column name lookup policy */ ColumnNamePolicy _column_name_policy = ColumnNamePolicy::EXACT; /**< Chunk size for reading; passed to CSVReader at construction time */ size_t _chunk_size = internals::ITERATION_CHUNK_SIZE; }; } namespace csv { namespace internals { struct ColNames; using ColNamesPtr = std::shared_ptr; /** @struct ColNames * A data structure for handling column name information. * * These are created by CSVReader and passed (via smart pointer) * to CSVRow objects it creates, thus * allowing for indexing by column name. */ struct ColNames { public: ColNames() = default; ColNames(const std::vector& names) { set_col_names(names); } std::vector get_col_names() const; void set_col_names(const std::vector&); int index_of(csv::string_view) const; /** Sets the column name lookup policy. * Must be called before set_col_names() for CI policy to take effect. */ void set_policy(csv::ColumnNamePolicy policy); bool empty() const noexcept { return this->col_names.empty(); } size_t size() const noexcept; /** Retrieve column name by index. Throws if index is out of bounds. */ const std::string& operator[](size_t i) const; private: std::vector col_names; std::unordered_map col_pos; csv::ColumnNamePolicy _policy = csv::ColumnNamePolicy::EXACT; }; } } /** @file * Defines the data type used for storing information about a CSV row */ #include #include #include // For CSVField #include // For CSVField #if !defined(CSV_ENABLE_THREADS) || CSV_ENABLE_THREADS #include #endif #include #include #include #include #ifdef CSV_HAS_CXX20 #include #endif /** @file * @brief Implements data type parsing functionality */ #include #include #include #include namespace csv { /** Enumerates the different CSV field types that are * recognized by this library * * @note Overflowing integers will be stored and classified as doubles. * @note Unlike previous releases, integer enums here are platform agnostic. */ enum class DataType { UNKNOWN = -1, CSV_NULL, /**< Empty string */ CSV_STRING, /**< Non-numeric string */ CSV_INT8, /**< 8-bit integer */ CSV_INT16, /**< 16-bit integer (short on MSVC/GCC) */ CSV_INT32, /**< 32-bit integer (int on MSVC/GCC) */ CSV_INT64, /**< 64-bit integer (long long on MSVC/GCC) */ CSV_BIGINT, /**< Value too big to fit in a 64-bit in */ CSV_DOUBLE /**< Floating point value */ }; static_assert(DataType::CSV_STRING < DataType::CSV_INT8, "String type should come before numeric types."); static_assert(DataType::CSV_INT8 < DataType::CSV_INT64, "Smaller integer types should come before larger integer types."); static_assert(DataType::CSV_INT64 < DataType::CSV_DOUBLE, "Integer types should come before floating point value types."); namespace internals { /** Compute 10 to the power of n. * Only integral exponents are supported; fractional exponents * are never needed since CSV scientific notation exponents are * always integers (enforced by the CSV_INT8..CSV_INT64 guard * in _process_potential_exponential before calling this). */ template CSV_CONST CONSTEXPR_14 long double pow10(const T& n) noexcept { static_assert(std::is_integral::value, "pow10 only supports integral exponents"); long double multiplicand = n > 0 ? 10 : 0.1, ret = 1; // Make all numbers positive T iterations = n > 0 ? n : -n; for (T i = 0; i < iterations; i++) { ret *= multiplicand; } return ret; } /** Compute 10 to the power of n */ template<> CSV_CONST CONSTEXPR_14 long double pow10(const unsigned& n) noexcept { long double multiplicand = n > 0 ? 10 : 0.1, ret = 1; for (unsigned i = 0; i < n; i++) { ret *= multiplicand; } return ret; } #ifndef DOXYGEN_SHOULD_SKIP_THIS /** Private site-indexed array mapping byte sizes to an integer size enum */ constexpr DataType int_type_arr[8] = { DataType::CSV_INT8, // 1 DataType::CSV_INT16, // 2 DataType::UNKNOWN, DataType::CSV_INT32, // 4 DataType::UNKNOWN, DataType::UNKNOWN, DataType::UNKNOWN, DataType::CSV_INT64 // 8 }; template inline DataType type_num() { static_assert(std::is_integral::value, "T should be an integral type."); static_assert(sizeof(T) <= 8, "Byte size must be no greater than 8."); return int_type_arr[sizeof(T) - 1]; } template<> inline DataType type_num() { return DataType::CSV_DOUBLE; } template<> inline DataType type_num() { return DataType::CSV_DOUBLE; } template<> inline DataType type_num() { return DataType::CSV_DOUBLE; } template<> inline DataType type_num() { return DataType::CSV_NULL; } template<> inline DataType type_num() { return DataType::CSV_STRING; } CONSTEXPR_14 DataType data_type(csv::string_view in, long double* const out = nullptr, const char decimalsymbol = '.'); #endif /** Given a byte size, return the largest number than can be stored in * an integer of that size * * Note: Provides a platform-agnostic way of mapping names like "long int" to * byte sizes */ template CONSTEXPR_14 long double get_int_max() { static_assert(Bytes == 1 || Bytes == 2 || Bytes == 4 || Bytes == 8, "Bytes must be a power of 2 below 8."); #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4127) #endif IF_CONSTEXPR (sizeof(signed char) == Bytes) { return (long double)std::numeric_limits::max(); } else IF_CONSTEXPR (sizeof(short) == Bytes) { return (long double)std::numeric_limits::max(); } else IF_CONSTEXPR (sizeof(int) == Bytes) { return (long double)std::numeric_limits::max(); } else IF_CONSTEXPR (sizeof(long int) == Bytes) { return (long double)std::numeric_limits::max(); } else { return (long double)std::numeric_limits::max(); } #ifdef _MSC_VER #pragma warning(pop) #endif } /** Given a byte size, return the largest number than can be stored in * an unsigned integer of that size */ template CONSTEXPR_14 long double get_uint_max() { static_assert(Bytes == 1 || Bytes == 2 || Bytes == 4 || Bytes == 8, "Bytes must be a power of 2 below 8."); #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4127) #endif IF_CONSTEXPR(sizeof(unsigned char) == Bytes) { return (long double)std::numeric_limits::max(); } else IF_CONSTEXPR(sizeof(unsigned short) == Bytes) { return (long double)std::numeric_limits::max(); } else IF_CONSTEXPR(sizeof(unsigned int) == Bytes) { return (long double)std::numeric_limits::max(); } else IF_CONSTEXPR(sizeof(unsigned long int) == Bytes) { return (long double)std::numeric_limits::max(); } else { return (long double)std::numeric_limits::max(); } #ifdef _MSC_VER #pragma warning(pop) #endif } /** Largest number that can be stored in a 8-bit integer */ CONSTEXPR_VALUE_14 long double CSV_INT8_MAX = get_int_max<1>(); /** Largest number that can be stored in a 16-bit integer */ CONSTEXPR_VALUE_14 long double CSV_INT16_MAX = get_int_max<2>(); /** Largest number that can be stored in a 32-bit integer */ CONSTEXPR_VALUE_14 long double CSV_INT32_MAX = get_int_max<4>(); /** Largest number that can be stored in a 64-bit integer */ CONSTEXPR_VALUE_14 long double CSV_INT64_MAX = get_int_max<8>(); /** Largest number that can be stored in a 8-bit ungisned integer */ CONSTEXPR_VALUE_14 long double CSV_UINT8_MAX = get_uint_max<1>(); /** Largest number that can be stored in a 16-bit unsigned integer */ CONSTEXPR_VALUE_14 long double CSV_UINT16_MAX = get_uint_max<2>(); /** Largest number that can be stored in a 32-bit unsigned integer */ CONSTEXPR_VALUE_14 long double CSV_UINT32_MAX = get_uint_max<4>(); /** Largest number that can be stored in a 64-bit unsigned integer */ CONSTEXPR_VALUE_14 long double CSV_UINT64_MAX = get_uint_max<8>(); /** Given a pointer to the start of what is start of * the exponential part of a number written (possibly) in scientific notation * parse the exponent */ CSV_PRIVATE CONSTEXPR_14 DataType _process_potential_exponential( csv::string_view exponential_part, const long double& coeff, long double * const out) { long double exponent = 0; auto result = data_type(exponential_part, &exponent); // Exponents in scientific notation should not be decimal numbers if (result >= DataType::CSV_INT8 && result < DataType::CSV_DOUBLE) { if (out) *out = coeff * pow10(static_cast(exponent)); return DataType::CSV_DOUBLE; } return DataType::CSV_STRING; } /** Given the absolute value of an integer, determine what numeric type * it fits in */ CSV_PRIVATE CSV_PURE CONSTEXPR_14 DataType _determine_integral_type(const long double& number) noexcept { // We can assume number is always non-negative assert(number >= 0); if (number <= internals::CSV_INT8_MAX) return DataType::CSV_INT8; else if (number <= internals::CSV_INT16_MAX) return DataType::CSV_INT16; else if (number <= internals::CSV_INT32_MAX) return DataType::CSV_INT32; else if (number <= internals::CSV_INT64_MAX) return DataType::CSV_INT64; else // Conversion to long long will cause an overflow return DataType::CSV_BIGINT; } /** Distinguishes numeric from other text values. Used by various * type casting functions, like csv_parser::CSVReader::read_row() * * #### Rules * - Leading and trailing whitespace ("padding") ignored * - A string of just whitespace is NULL * * @param[in] in String value to be examined * @param[out] out Pointer to long double where results of numeric parsing * get stored * @param[in] decimalSymbol the character separating integral and decimal part, * defaults to '.' if omitted */ CONSTEXPR_14 DataType data_type(csv::string_view in, long double* const out, const char decimalSymbol) { // Empty string --> NULL if (in.size() == 0) return DataType::CSV_NULL; bool ws_allowed = true, dot_allowed = true, digit_allowed = true, is_negative = false, has_digit = false, prob_float = false; unsigned places_after_decimal = 0; long double integral_part = 0, decimal_part = 0; for (size_t i = 0, ilen = in.size(); i < ilen; i++) { const char& current = in[i]; switch (current) { case ' ': if (!ws_allowed) { if (isdigit(in[i - 1])) { digit_allowed = false; ws_allowed = true; } else { // Ex: '510 123 4567' return DataType::CSV_STRING; } } break; case '+': if (!ws_allowed) { return DataType::CSV_STRING; } break; case '-': if (!ws_allowed) { // Ex: '510-123-4567' return DataType::CSV_STRING; } is_negative = true; break; // case decimalSymbol: not allowed because decimalSymbol is not a literal, // it is handled in the default block case 'e': case 'E': // Process scientific notation if (prob_float || (i && i + 1 < ilen && isdigit(in[i - 1]))) { size_t exponent_start_idx = i + 1; prob_float = true; // Strip out plus sign if (in[i + 1] == '+') { exponent_start_idx++; } return _process_potential_exponential( in.substr(exponent_start_idx), is_negative ? -(integral_part + decimal_part) : integral_part + decimal_part, out ); } return DataType::CSV_STRING; break; default: short digit = static_cast(current - '0'); if (digit >= 0 && digit <= 9) { // Process digit has_digit = true; if (!digit_allowed) return DataType::CSV_STRING; else if (ws_allowed) // Ex: '510 456' ws_allowed = false; // Build current number if (prob_float) decimal_part += digit / pow10(++places_after_decimal); else integral_part = (integral_part * 10) + digit; } // case decimalSymbol: not allowed because decimalSymbol is not a literal. else if (dot_allowed && current == decimalSymbol) { dot_allowed = false; prob_float = true; } else { return DataType::CSV_STRING; } } } // No non-numeric/non-whitespace characters found if (has_digit) { long double number = integral_part + decimal_part; if (out) { *out = is_negative ? -number : number; } return prob_float ? DataType::CSV_DOUBLE : _determine_integral_type(number); } // Just whitespace return DataType::CSV_NULL; } } } /** @file * @brief Implements Functions related to hexadecimal parsing */ #include #include namespace csv { namespace internals { template bool try_parse_hex(csv::string_view sv, T& parsedValue) { static_assert(std::is_integral::value, "try_parse_hex only works with integral types (int, long, long long, etc.)"); size_t start = 0, end = 0; // Trim out whitespace chars for (; start < sv.size() && sv[start] == ' '; start++); for (end = start; end < sv.size() && sv[end] != ' '; end++); T value_ = 0; size_t digits = (end - start); size_t base16_exponent = digits - 1; if (digits == 0) return false; for (const auto& ch : sv.substr(start, digits)) { int digit = 0; switch (ch) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': digit = static_cast(ch - '0'); break; case 'a': case 'A': digit = 10; break; case 'b': case 'B': digit = 11; break; case 'c': case 'C': digit = 12; break; case 'd': case 'D': digit = 13; break; case 'e': case 'E': digit = 14; break; case 'f': case 'F': digit = 15; break; default: return false; } value_ += digit * (T)pow(16, (double)base16_exponent); base16_exponent--; } parsedValue = value_; return true; } } } /** @file * @brief Internal data structures for CSV parsing * * This file contains the low-level structures used by the parser to store * CSV data before it's exposed through the public CSVRow/CSVField API. * * Data flow: Parser → RawCSVData → CSVRow → CSVField */ #include #include #if !defined(CSV_ENABLE_THREADS) || CSV_ENABLE_THREADS #include #endif #include #include #include namespace csv { namespace internals { /** A barebones class used for describing CSV fields */ struct RawCSVField { RawCSVField() = default; RawCSVField(size_t _start, size_t _length, bool _double_quote = false) noexcept { start = _start; length = _length; has_double_quote = _double_quote; } /** The start of the field, relative to the beginning of the row */ size_t start; /** The length of the row, ignoring quote escape characters */ size_t length; /** Whether or not the field contains an escaped quote */ bool has_double_quote; }; /** A class used for efficiently storing RawCSVField objects and expanding as necessary * * @par Implementation * Stores fields in page-aligned chunks (~170 fields/chunk) via a vector of * unique_ptr: * - This design provides better cache locality when accessing sequential fields in a row * as well as much lower memory allocation overhead. * - The unique_ptr ensures STL container does not invalidate pointers to fields when resizing, * which is critical to ensure memory safety and correctness of the parser. * * @par Thread Safety * Cross-thread visibility is provided by the records queue mutex in * ThreadSafeDeque: the writer enqueues a RawCSVData only after all fields are * written, and the reader dequeues it only after the mutex unlock/lock pair, * which is a full happens-before edge. No additional atomics are needed here. * * @par Historical Bug (Issue #278, fixed Feb 2026) * Move constructor previously left _back pointing to moved-from buffer memory, causing * memory corruption on next emplace_back(). Fixed by recalculating _back from * _owned_blocks after move. */ class RawCSVFieldList { public: /** Construct a RawCSVFieldList which allocates blocks of a certain size */ RawCSVFieldList(size_t single_buffer_capacity = (size_t)(internals::PAGE_SIZE / sizeof(RawCSVField))) : _single_buffer_capacity(single_buffer_capacity) { const size_t max_fields = internals::ITERATION_CHUNK_SIZE + 1; const size_t block_capacity = (max_fields + _single_buffer_capacity - 1) / _single_buffer_capacity; _owned_blocks.reserve(block_capacity); this->allocate(); } // No copy constructor RawCSVFieldList(const RawCSVFieldList& other) = delete; // CSVFieldArrays may be moved RawCSVFieldList(RawCSVFieldList&& other) noexcept: _single_buffer_capacity(other._single_buffer_capacity) { this->_owned_blocks = std::move(other._owned_blocks); _current_buffer_size = other._current_buffer_size; _current_block = other._current_block; // Recalculate _back pointer to point into OUR blocks, not the moved-from ones if (!this->_owned_blocks.empty()) { _back = this->_owned_blocks[_current_block].get() + _current_buffer_size; } else { _back = nullptr; } // Invalidate moved-from state to prevent use-after-move bugs other._back = nullptr; other._current_buffer_size = 0; other._current_block = 0; } template void emplace_back(Args&&... args) { if (this->_current_buffer_size == this->_single_buffer_capacity) { this->allocate(); } assert(_back != nullptr); *(_back++) = RawCSVField(std::forward(args)...); _current_buffer_size++; } size_t size() const noexcept { return this->_current_buffer_size + (_current_block * this->_single_buffer_capacity); } /** Access a field by its index. This allows CSVRow objects to access fields * without knowing internal implementation details of RawCSVFieldList. */ RawCSVField& operator[](size_t n) const; private: const size_t _single_buffer_capacity; /** Owned field-storage blocks; pre-reserved to avoid reallocation. */ std::vector> _owned_blocks = {}; /** Number of items in the current block */ size_t _current_buffer_size = 0; /** Current block index */ size_t _current_block = 0; /** Pointer to the next empty field slot in the current block */ RawCSVField* _back = nullptr; /** Allocate a new page of memory */ void allocate(); }; /** A class for storing raw CSV data and associated metadata * * This structure is the bridge between the parser thread and the main thread. * Parser populates fields, data, and parse_flags; main thread reads via CSVRow. */ struct RawCSVData { std::shared_ptr _data = nullptr; csv::string_view data = ""; internals::RawCSVFieldList fields; /** Cached unescaped field values for fields with escaped quotes. * Thread-safe lazy initialization using double-check locking. * Lock is only held during rare concurrent initialization; reads are lock-free. */ std::unordered_map double_quote_fields = {}; #if CSV_ENABLE_THREADS mutable std::mutex double_quote_init_lock; ///< Protects lazy initialization only #endif internals::ColNamesPtr col_names = nullptr; internals::ParseFlagMap parse_flags; internals::WhitespaceMap ws_flags; /** True when at least one whitespace trim character is configured. * Used by get_field_impl() to skip trim work in the common no-trim case. */ bool has_ws_trimming = false; }; using RawCSVDataPtr = std::shared_ptr; } } #if CSV_ENABLE_THREADS #define CSV_INIT_WITH_OPTIONAL_DCL(data_ref, value_ref, ...) \ do { \ if ((value_ref).empty()) { \ std::lock_guard lock((data_ref).double_quote_init_lock); \ if ((value_ref).empty()) { \ __VA_ARGS__ \ } \ } \ } while (0) #else #define CSV_INIT_WITH_OPTIONAL_DCL(data_ref, value_ref, ...) \ do { \ (void)(data_ref); \ if ((value_ref).empty()) { \ __VA_ARGS__ \ } \ } while (0) #endif namespace csv { namespace internals { class IBasicCSVParser; static const std::string ERROR_NAN = "Not a number."; static const std::string ERROR_OVERFLOW = "Overflow error."; static const std::string ERROR_FLOAT_TO_INT = "Attempted to convert a floating point value to an integral type."; static const std::string ERROR_NEG_TO_UNSIGNED = "Negative numbers cannot be converted to unsigned types."; std::string json_escape_string(csv::string_view s) noexcept; // Inside CSVField::get() or wherever you materialize the value csv::string_view get_trimmed(csv::string_view sv, const WhitespaceMap& ws_flags) noexcept; } /** * @class CSVField * @brief Data type representing individual CSV values. * CSVFields can be obtained by using CSVRow::operator[] */ class CSVField { public: /** Constructs a CSVField from a string_view */ constexpr explicit CSVField(csv::string_view _sv) noexcept : sv(_sv) {} operator std::string() const { return std::string(this->sv); } /** Returns the value casted to the requested type, performing type checking before. * * \par Valid options for T * - std::string or csv::string_view * - signed integral types (signed char, short, int, long int, long long int) * - floating point types (float, double, long double) * - unsigned integers are not supported at this time, but may be in a later release * * \par Invalid conversions * - Converting non-numeric values to any numeric type * - Converting floating point values to integers * - Converting a large integer to a smaller type that will not hold it * * @note This method is capable of parsing scientific E-notation. * See [this page](md_docs_source_scientific_notation.html) * for more details. * * @throws std::runtime_error Thrown if an invalid conversion is performed. * * @warning Currently, conversions to floating point types are not * checked for loss of precision * * @warning Any string_views returned are only guaranteed to be valid * if the parent CSVRow is still alive. If you are concerned * about object lifetimes, then grab a std::string or a * numeric value. * */ template T get() { IF_CONSTEXPR(std::is_arithmetic::value) { // Note: this->type() also converts the CSV value to float if (this->type() <= DataType::CSV_STRING) { throw std::runtime_error(internals::ERROR_NAN); } } IF_CONSTEXPR(std::is_integral::value) { // Note: this->is_float() also converts the CSV value to float if (this->is_float()) { throw std::runtime_error(internals::ERROR_FLOAT_TO_INT); } IF_CONSTEXPR(std::is_unsigned::value) { if (this->value < 0) { throw std::runtime_error(internals::ERROR_NEG_TO_UNSIGNED); } } } // Allow fallthrough from previous if branch IF_CONSTEXPR(!std::is_floating_point::value) { IF_CONSTEXPR(std::is_unsigned::value) { // Quick hack to perform correct unsigned integer boundary checks if (this->value > internals::get_uint_max()) { throw std::runtime_error(internals::ERROR_OVERFLOW); } } else if (internals::type_num() < this->_type) { throw std::runtime_error(internals::ERROR_OVERFLOW); } } return static_cast(this->value); } /** Attempts to retrieve the value as the requested type without throwing exceptions. * * @param[out] out Output parameter that receives the converted value if successful * @return true if conversion succeeded, false otherwise * * \par Valid options for T * - std::string or csv::string_view * - signed integral types (signed char, short, int, long int, long long int) * - floating point types (float, double, long double) * - unsigned integers are not supported at this time, but may be in a later release * * \par When conversion fails (returns false) * - Converting non-numeric values to any numeric type * - Converting floating point values to integers * - Converting a large integer to a smaller type that will not hold it * - Converting negative values to unsigned types * * @note This method is capable of parsing scientific E-notation. * * @warning Currently, conversions to floating point types are not * checked for loss of precision * * @warning Any string_views returned are only guaranteed to be valid * if the parent CSVRow is still alive. * * Example: * @code * int value; * if (field.try_get(value)) { * // Use value safely * } else { * // Handle conversion failure * } * @endcode */ template bool try_get(T& out) noexcept { IF_CONSTEXPR(std::is_arithmetic::value) { // Check if value is numeric if (this->type() <= DataType::CSV_STRING) { return false; } } IF_CONSTEXPR(std::is_integral::value) { // Check for float-to-int conversion if (this->is_float()) { return false; } IF_CONSTEXPR(std::is_unsigned::value) { if (this->value < 0) { return false; } } } // Check for overflow IF_CONSTEXPR(!std::is_floating_point::value) { IF_CONSTEXPR(std::is_unsigned::value) { if (this->value > internals::get_uint_max()) { return false; } } else if (internals::type_num() < this->_type) { return false; } } out = static_cast(this->value); return true; } /** Parse a hexadecimal value, returning false if the value is not hex. * @tparam T An integral type (int, long, long long, etc.) */ template bool try_parse_hex(T& parsedValue) { static_assert(std::is_integral::value, "try_parse_hex only works with integral types (int, long, long long, etc.)"); return internals::try_parse_hex(this->sv, parsedValue); } /** Attempts to parse a decimal (or integer) value using the given symbol, * returning `true` if the value is numeric. * * @note This method also updates this field's type * */ bool try_parse_decimal(long double& dVal, const char decimalSymbol = '.'); /** Compares the contents of this field to a numeric value. If this * field does not contain a numeric value, then all comparisons return * false. * * @note Floating point values are considered equal if they are within * `0.000001` of each other. * * @warning Multiple numeric comparisons involving the same field can * be done more efficiently by calling the CSVField::get<>() method. * * @sa csv::CSVField::operator==(const char * other) * @sa csv::CSVField::operator==(csv::string_view other) */ template CONSTEXPR_14 bool operator==(T other) const noexcept { static_assert(std::is_arithmetic::value, "T should be a numeric value."); if (this->_type != DataType::UNKNOWN) { if (this->_type == DataType::CSV_STRING) { return false; } return internals::is_equal(value, static_cast(other), 0.000001L); } long double out = 0; if (internals::data_type(this->sv, &out) == DataType::CSV_STRING) { return false; } return internals::is_equal(out, static_cast(other), 0.000001L); } /** Return a string view over the field's contents */ CONSTEXPR csv::string_view get_sv() const noexcept { return this->sv; } /** Returns true if field is an empty string or string of whitespace characters */ CONSTEXPR_14 bool is_null() noexcept { return type() == DataType::CSV_NULL; } /** Returns true if field is a non-numeric, non-empty string */ CONSTEXPR_14 bool is_str() noexcept { return type() == DataType::CSV_STRING; } /** Returns true if field is an integer or float */ CONSTEXPR_14 bool is_num() noexcept { return type() >= DataType::CSV_INT8; } /** Returns true if field is an integer */ CONSTEXPR_14 bool is_int() noexcept { return (type() >= DataType::CSV_INT8) && (type() <= DataType::CSV_INT64); } /** Returns true if field is a floating point value */ CONSTEXPR_14 bool is_float() noexcept { return type() == DataType::CSV_DOUBLE; } /** Return the type of the underlying CSV data */ CONSTEXPR_14 DataType type() noexcept { this->get_value(); return _type; } private: long double value = 0; /**< Cached numeric value */ csv::string_view sv = ""; /**< A pointer to this field's text */ DataType _type = DataType::UNKNOWN; /**< Cached data type value */ CONSTEXPR_14 void get_value() noexcept { /* Check to see if value has been cached previously, if not * evaluate it */ if ((int)_type < 0) { this->_type = internals::data_type(this->sv, &this->value); } } }; /** Data structure for representing CSV rows */ class CSVRow { public: friend internals::IBasicCSVParser; CSVRow() = default; /** Construct a CSVRow from a RawCSVDataPtr */ CSVRow(internals::RawCSVDataPtr _data) : data(_data) {} CSVRow(internals::RawCSVDataPtr _data, size_t _data_start, size_t _field_bounds) : data(_data), data_start(_data_start), fields_start(_field_bounds) {} CSVRow(internals::RawCSVDataPtr _data, size_t _data_start, size_t _field_bounds, size_t _row_length) : data(_data), data_start(_data_start), fields_start(_field_bounds), row_length(_row_length) {} /** Indicates whether row is empty or not */ CONSTEXPR bool empty() const noexcept { return this->size() == 0; } /** Return the number of fields in this row */ CONSTEXPR size_t size() const noexcept { return row_length; } /** @name Value Retrieval */ ///@{ CSVField operator[](size_t n) const; CSVField operator[](const std::string&) const; std::string to_json(const std::vector& subset = {}) const; std::string to_json_array(const std::vector& subset = {}) const; /** Retrieve this row's associated column names */ std::vector get_col_names() const { return this->data->col_names->get_col_names(); } /** Convert this CSVRow into an unordered map. * The keys are the column names and the values are the corresponding field values. */ std::unordered_map to_unordered_map() const; /** Convert this CSVRow into an unordered map. * The keys are the column names and the values are the corresponding field values. * * @param[in] subset Vector of column names to include in the map. */ std::unordered_map to_unordered_map( const std::vector& subset ) const; #ifdef CSV_HAS_CXX20 /** Convert this CSVRow into a std::ranges::input_range of string_views. */ auto to_sv_range() const { return std::views::iota(size_t{0}, this->size()) | std::views::transform([this](size_t i) { return this->get_field(i); }); } #endif /** Convert this row into a `std::vector`. * * This conversion is primarily intended for write-side workflows, such as * reordering or selecting columns before forwarding the row to `CSVWriter`. * * @note This is less efficient than indexed access via `operator[]` because * it materializes all fields as owning strings. */ operator std::vector() const; ///@} /** A random access iterator over the contents of a CSV row. * Each iterator points to a CSVField. */ class iterator { public: #ifndef DOXYGEN_SHOULD_SKIP_THIS using value_type = CSVField; using difference_type = int; using pointer = std::shared_ptr; using reference = CSVField & ; using iterator_category = std::random_access_iterator_tag; #endif iterator(const CSVRow*, int i); reference operator*() const; pointer operator->() const; iterator operator++(int); iterator& operator++(); iterator operator--(int); iterator& operator--(); iterator operator+(difference_type n) const; iterator operator-(difference_type n) const; /** Two iterators are equal if they point to the same field */ CONSTEXPR bool operator==(const iterator& other) const noexcept { return this->i == other.i; }; CONSTEXPR bool operator!=(const iterator& other) const noexcept { return !operator==(other); } #ifndef NDEBUG friend CSVRow; #endif private: const CSVRow * daddy = nullptr; // Pointer to parent internals::RawCSVDataPtr data = nullptr; // Keep data alive for lifetime of iterator std::shared_ptr field = nullptr; // Current field pointed at int i = 0; // Index of current field }; /** A reverse iterator over the contents of a CSVRow. */ using reverse_iterator = std::reverse_iterator; /** @name Iterators * @brief Each iterator points to a CSVField object. */ ///@{ iterator begin() const; iterator end() const noexcept; reverse_iterator rbegin() const noexcept; reverse_iterator rend() const; ///@} private: /** Shared implementation for field access (handles quoting and caching). */ inline csv::string_view get_field_impl(size_t index, const internals::RawCSVDataPtr& _data) const { using internals::ParseFlags; if (index >= this->size()) throw std::runtime_error("Index out of bounds."); const size_t field_index = this->fields_start + index; auto field = _data->fields[field_index]; auto field_str = csv::string_view(_data->data).substr(this->data_start + field.start, field.length); if (field.has_double_quote) { auto& value = _data->double_quote_fields[field_index]; CSV_INIT_WITH_OPTIONAL_DCL((*_data), value, bool prev_ch_quote = false; for (size_t i = 0; i < field.length; i++) { if (_data->parse_flags[field_str[i] + CHAR_OFFSET] == ParseFlags::QUOTE) { if (prev_ch_quote) { prev_ch_quote = false; continue; } else { prev_ch_quote = true; } } value += field_str[i]; } ); if (_data->has_ws_trimming) return internals::get_trimmed(csv::string_view(value), _data->ws_flags); return value; } else if (_data->has_ws_trimming) { field_str = internals::get_trimmed(field_str, _data->ws_flags); } return field_str; } /** Retrieve a string view corresponding to the specified index */ csv::string_view get_field(size_t index) const; /** Iterator-safe field access using explicit data pointer * (prevents accessing freed data when CSVRow is reassigned) */ csv::string_view get_field_safe(size_t index, internals::RawCSVDataPtr _data) const; internals::RawCSVDataPtr data; /** Where in RawCSVData.data we start */ size_t data_start = 0; /** Where in the RawCSVDataPtr.fields array we start */ size_t fields_start = 0; /** How many columns this row spans */ size_t row_length = 0; }; #ifdef _MSC_VER #pragma region CSVField::get Specializations #endif /** Retrieve this field's original string */ template<> inline std::string CSVField::get() { return std::string(this->sv); } /** Retrieve a view over this field's string * * @warning This string_view is only guaranteed to be valid as long as this * CSVRow is still alive. */ template<> CONSTEXPR_14 csv::string_view CSVField::get() { return this->sv; } /** Retrieve this field's value as a long double */ template<> CONSTEXPR_14 long double CSVField::get() { if (!is_num()) throw std::runtime_error(internals::ERROR_NAN); return this->value; } /** Non-throwing retrieval of field as std::string */ template<> inline bool CSVField::try_get(std::string& out) noexcept { out = std::string(this->sv); return true; } /** Non-throwing retrieval of field as csv::string_view */ template<> CONSTEXPR_14 bool CSVField::try_get(csv::string_view& out) noexcept { out = this->sv; return true; } /** Non-throwing retrieval of field as long double */ template<> CONSTEXPR_14 bool CSVField::try_get(long double& out) noexcept { if (!is_num()) return false; out = this->value; return true; } #ifdef _MSC_VER #pragma endregion CSVField::get Specializations #endif /** Compares the contents of this field to a string */ template<> CONSTEXPR bool CSVField::operator==(const char * other) const noexcept { return this->sv == other; } /** Compares the contents of this field to a string */ template<> CONSTEXPR bool CSVField::operator==(csv::string_view other) const noexcept { return this->sv == other; } } #undef CSV_INIT_WITH_OPTIONAL_DCL /** @file * @brief Shared contracts for row deque implementations */ #if CSV_ENABLE_THREADS /** @file * @brief Thread-safe deque for producer-consumer patterns * * Generic container used for cross-thread communication in the CSV parser. * Parser thread pushes rows, main thread pops them. * * Design notes: see THREADSAFE_DEQUE_DESIGN.md for protocol details, * invariants, and producer/consumer timing diagrams. */ #include #include #include #include namespace csv { namespace internals { /** A std::deque wrapper which allows multiple read and write threads to concurrently * access it along with providing read threads the ability to wait for the deque * to become populated. * * Concurrency strategy: writer-side mutations (push_back/pop_front) are locked; * hot-path flags (empty/is_waitable) are atomic; operator[] and iterators are * not synchronized and must not run concurrently with writers. */ template class ThreadSafeDeque { public: ThreadSafeDeque(size_t notify_size = 100) : _notify_size(notify_size) {} ThreadSafeDeque(const ThreadSafeDeque& other) { this->data = other.data; this->_notify_size = other._notify_size; this->_is_empty.store(other._is_empty.load(std::memory_order_acquire), std::memory_order_release); } ThreadSafeDeque(const std::deque& source) : ThreadSafeDeque() { this->data = source; this->_is_empty.store(source.empty(), std::memory_order_release); } bool empty() const noexcept { return this->_is_empty.load(std::memory_order_acquire); } T& front() noexcept { std::lock_guard lock{ this->_lock }; return this->data.front(); } /** NOTE: operator[] is not synchronized. * Only call when no concurrent push_back/pop_front can occur. * std::deque can reallocate its internal map on push_back, which * makes concurrent operator[] access undefined behavior. */ T& operator[](size_t n) { return this->data[n]; } void push_back(T&& item) { std::lock_guard lock{ this->_lock }; this->data.push_back(std::move(item)); this->_is_empty.store(false, std::memory_order_release); if (this->data.size() >= _notify_size) { this->_cond.notify_all(); } } T pop_front() noexcept { std::lock_guard lock{ this->_lock }; T item = std::move(data.front()); data.pop_front(); if (this->data.empty()) { this->_is_empty.store(true, std::memory_order_release); } return item; } /** Returns true if a thread is actively pushing items to this deque */ bool is_waitable() const noexcept { return this->_is_waitable.load(std::memory_order_acquire); } void wait() { if (!is_waitable()) { return; } std::unique_lock lock{ this->_lock }; this->_cond.wait(lock, [this] { return this->data.size() >= _notify_size || !this->is_waitable(); }); lock.unlock(); } size_t size() const noexcept { std::lock_guard lock{ this->_lock }; return this->data.size(); } typename std::deque::iterator begin() noexcept { return this->data.begin(); } typename std::deque::iterator end() noexcept { return this->data.end(); } /** Tell listeners that this deque is actively being pushed to */ void notify_all() { std::lock_guard lock{ this->_lock }; this->_is_waitable.store(true, std::memory_order_release); this->_cond.notify_all(); } void kill_all() { std::lock_guard lock{ this->_lock }; this->_is_waitable.store(false, std::memory_order_release); this->_cond.notify_all(); } private: std::atomic _is_empty{ true }; // Lock-free empty() check std::atomic _is_waitable{ false }; // Lock-free is_waitable() check size_t _notify_size; mutable std::mutex _lock; std::condition_variable _cond; std::deque data; }; } } #else /** @file * @brief Single-threaded row deque implementation */ #include #include namespace csv { namespace internals { template class SingleThreadDeque { public: SingleThreadDeque(size_t notify_size = 100) : _notify_size(notify_size) {} SingleThreadDeque(const SingleThreadDeque& other) { this->data = other.data; this->_notify_size = other._notify_size; this->_is_empty = other._is_empty; this->_is_waitable = other._is_waitable; } SingleThreadDeque(const std::deque& source) : SingleThreadDeque() { this->data = source; this->_is_empty = source.empty(); } bool empty() const noexcept { return this->_is_empty; } T& front() noexcept { return this->data.front(); } T& operator[](size_t n) { return this->data[n]; } void push_back(T&& item) { this->data.push_back(std::move(item)); this->_is_empty = false; } T pop_front() noexcept { T item = std::move(data.front()); data.pop_front(); if (this->data.empty()) { this->_is_empty = true; } return item; } bool is_waitable() const noexcept { return this->_is_waitable; } void wait() { // No-op in single-thread mode. } size_t size() const noexcept { return this->data.size(); } typename std::deque::iterator begin() noexcept { return this->data.begin(); } typename std::deque::iterator end() noexcept { return this->data.end(); } void notify_all() { this->_is_waitable = true; } void kill_all() { this->_is_waitable = false; } private: bool _is_empty = true; bool _is_waitable = false; size_t _notify_size; std::deque data; }; } } #endif #include #include #include #ifdef CSV_HAS_CXX20 #include #endif namespace csv { namespace internals { #if !CSV_ENABLE_THREADS template using ThreadSafeDeque = SingleThreadDeque; #endif #ifdef CSV_HAS_CXX20 template concept RowDequeLike = requires(Q q, const Q cq, T item, size_t n) { { Q(100) }; { q.push_back(std::move(item)) } -> std::same_as; { q.pop_front() } -> std::same_as; { cq.empty() } -> std::same_as; { cq.is_waitable() } -> std::same_as; { q.wait() } -> std::same_as; { q.notify_all() } -> std::same_as; { q.kill_all() } -> std::same_as; { q.front() } -> std::same_as; { q[n] } -> std::same_as; { cq.size() } -> std::same_as; { q.begin() }; { q.end() }; }; #if CSV_ENABLE_THREADS static_assert(RowDequeLike, int>, "ThreadSafeDeque must satisfy RowDequeLike contract"); #else static_assert(RowDequeLike, int>, "SingleThreadDeque must satisfy RowDequeLike contract"); static_assert(RowDequeLike, int>, "Selected ThreadSafeDeque alias must satisfy RowDequeLike contract"); #endif #endif } } namespace csv { namespace internals { constexpr const int UNINITIALIZED_FIELD = -1; /** Helper constexpr function to initialize an array with all the elements set to value */ template CSV_CONST CONSTEXPR_17 OutArray arrayToDefault(T&& value) { OutArray a {}; for (auto& e : a) e = value; return a; } /** Create a vector v where each index i corresponds to the * ASCII number for a character and, v[i + 128] labels it according to * the CSVReader::ParseFlags enum */ CSV_CONST CONSTEXPR_17 ParseFlagMap make_parse_flags(char delimiter) { auto ret = arrayToDefault(ParseFlags::NOT_SPECIAL); ret[delimiter + CHAR_OFFSET] = ParseFlags::DELIMITER; ret['\r' + CHAR_OFFSET] = ParseFlags::NEWLINE; ret['\n' + CHAR_OFFSET] = ParseFlags::NEWLINE; return ret; } /** Create a vector v where each index i corresponds to the * ASCII number for a character and, v[i + 128] labels it according to * the CSVReader::ParseFlags enum */ CSV_CONST CONSTEXPR_17 ParseFlagMap make_parse_flags(char delimiter, char quote_char) { std::array ret = make_parse_flags(delimiter); ret[quote_char + CHAR_OFFSET] = ParseFlags::QUOTE; return ret; } inline char infer_delimiter(const ParseFlagMap& parse_flags) noexcept { for (int i = 0; i < 256; ++i) { char ch = static_cast(i); if (parse_flags[ch + CHAR_OFFSET] == ParseFlags::DELIMITER) { return ch; } } return ','; } // fallback is returned when no QUOTE flag exists in parse_flags (e.g. no_quote mode). // Pass the delimiter so SIMD stops there instead of on a byte that is NOT_SPECIAL. inline char infer_quote_char(const ParseFlagMap& parse_flags, char fallback = '"') noexcept { for (int i = 0; i < 256; ++i) { char ch = static_cast(i); if (parse_flags[ch + CHAR_OFFSET] == ParseFlags::QUOTE) { return ch; } } return fallback; } /** Create a vector v where each index i corresponds to the * ASCII number for a character c and, v[i + 128] is true if * c is a whitespace character */ CSV_CONST CONSTEXPR_17 WhitespaceMap make_ws_flags(const char* ws_chars, size_t n_chars) { auto ret = arrayToDefault(false); for (size_t j = 0; j < n_chars; j++) { ret[ws_chars[j] + CHAR_OFFSET] = true; } return ret; } inline WhitespaceMap make_ws_flags(const std::vector& flags) { return make_ws_flags(flags.data(), flags.size()); } CSV_INLINE size_t get_file_size(csv::string_view filename); CSV_INLINE std::string get_csv_head(csv::string_view filename); } /** Standard type for storing collection of rows */ using RowCollection = internals::ThreadSafeDeque; namespace internals { /** Abstract base class which provides CSV parsing logic. * * Concrete implementations may customize this logic across * different input sources, such as memory mapped files, stringstreams, * etc... */ class IBasicCSVParser { public: IBasicCSVParser() = default; IBasicCSVParser(const CSVFormat&, const ColNamesPtr&); IBasicCSVParser( const ParseFlagMap& parse_flags, const WhitespaceMap& ws_flags ) : parse_flags_(parse_flags), ws_flags_(ws_flags) { const char d = internals::infer_delimiter(parse_flags); simd_sentinels_ = SentinelVecs(d, internals::infer_quote_char(parse_flags, d)); has_ws_trimming_ = std::any_of(ws_flags.begin(), ws_flags.end(), [](bool b) { return b; }); } virtual ~IBasicCSVParser() {} /** Whether or not we have reached the end of source */ bool eof() { return this->eof_; } /** Parse the next block of data */ virtual void next(size_t bytes) = 0; /** Indicate the last block of data has been parsed */ void end_feed(); CONSTEXPR_17 ParseFlags parse_flag(const char ch) const noexcept { return parse_flags_.data()[ch + CHAR_OFFSET]; } CONSTEXPR_17 ParseFlags compound_parse_flag(const char ch) const noexcept { return quote_escape_flag(parse_flag(ch), this->quote_escape_); } /** Whether or not this CSV has a UTF-8 byte order mark */ CONSTEXPR bool utf8_bom() const { return this->utf8_bom_; } void set_output(RowCollection& rows) { this->records_ = &rows; } protected: /** @name Current Parser State */ ///@{ CSVRow current_row_; RawCSVDataPtr data_ptr_ = nullptr; ColNamesPtr col_names_ = nullptr; RawCSVFieldList* fields_ = nullptr; int field_start_ = UNINITIALIZED_FIELD; size_t field_length_ = 0; /** Precomputed SIMD broadcast vectors for find_next_non_special */ SentinelVecs simd_sentinels_; /** An array where the (i + 128)th slot gives the ParseFlags for ASCII character i */ ParseFlagMap parse_flags_; ///@} /** @name Current Stream/File State */ ///@{ bool eof_ = false; /** The size of the incoming CSV */ size_t source_size_ = 0; ///@} /** Whether or not source needs to be read in chunks */ CONSTEXPR bool no_chunk() const { return this->source_size_ < ITERATION_CHUNK_SIZE; } /** Parse the current chunk of data * * * @returns How many character were read that are part of complete rows */ size_t parse(); /** Create a new RawCSVDataPtr for a new chunk of data */ void reset_data_ptr(); private: /** An array where the (i + 128)th slot determines whether ASCII character i should * be trimmed */ WhitespaceMap ws_flags_; /** True when at least one whitespace trim character is configured. * Used to skip trim loops entirely in the common no-trim case. */ bool has_ws_trimming_ = false; bool quote_escape_ = false; bool field_has_double_quote_ = false; /** Where we are in the current data block */ size_t data_pos_ = 0; /** Whether or not an attempt to find Unicode BOM has been made */ bool unicode_bom_scan_ = false; bool utf8_bom_ = false; /** Where complete rows should be pushed to */ RowCollection* records_ = nullptr; CONSTEXPR_17 bool ws_flag(const char ch) const noexcept { return ws_flags_.data()[ch + CHAR_OFFSET]; } size_t& current_row_start() { return this->current_row_.data_start; } void parse_field() noexcept; /** Finish parsing the current field */ void push_field(); /** Finish parsing the current row */ void push_row(); /** Handle possible Unicode byte order mark */ void trim_utf8_bom(); }; /** Read up to 500KB from a stream without rewinding. * * Replaces the old get_csv_head(TStream&) which required seekg/tellg. * Works with any std::istream, including non-seekable sources such as * pipes and decompression filters. */ template::value, int> = 0> std::string read_head_buffer(TStream& source) { const size_t limit = 500000; std::string buf(limit, '\0'); source.read(&buf[0], (std::streamsize)limit); buf.resize(static_cast(source.gcount())); return buf; } /** Read the first 500KB of a CSV file */ CSV_INLINE std::string get_csv_head(csv::string_view filename, size_t file_size); /** A class for parsing CSV data from any std::istream, including * non-seekable sources such as pipes and decompression filters. * * @par Chunk boundary handling * parse() returns the byte offset of the start of the last incomplete * row in the current chunk (the "remainder"). Rather than seeking back * to re-read those bytes (which requires a seekable stream), they are * saved in `leftover_` and prepended to the next chunk. This is * semantically identical to the old seek-back approach but works on * any istream and avoids the syscall overhead of seekg(). * * @par Head buffer * init_from_stream() reads a head buffer for format guessing before * constructing this parser. That buffer is passed in as the initial * `leftover_`, so its bytes are fed into the first chunk as if they had * just been read from the stream. */ template class StreamParser: public IBasicCSVParser { using RowCollection = ThreadSafeDeque; public: StreamParser(TStream& source, const CSVFormat& format, const ColNamesPtr& col_names = nullptr, std::string head = {} ) : IBasicCSVParser(format, col_names), leftover_(std::move(head)), source_(source) {} StreamParser( TStream& source, internals::ParseFlagMap parse_flags, internals::WhitespaceMap ws_flags) : IBasicCSVParser(parse_flags, ws_flags), source_(source) {} ~StreamParser() {} void next(size_t bytes = ITERATION_CHUNK_SIZE) override { if (this->eof()) return; // Reset parser state this->field_start_ = UNINITIALIZED_FIELD; this->field_length_ = 0; this->reset_data_ptr(); this->data_ptr_->_data = std::make_shared(); auto& chunk = *static_cast(this->data_ptr_->_data.get()); // Prepend leftover bytes from the previous chunk's incomplete // trailing row, then read the next block from the stream. // Uses a raw buffer to avoid std::string::resize() zero-fill // on the full 10MB chunk size (critical for tiny inputs). chunk = std::move(leftover_); std::unique_ptr buf(new char[bytes]); source_.read(buf.get(), (std::streamsize)bytes); const size_t n = static_cast(source_.gcount()); if (n > 0) chunk.append(buf.get(), n); // Check for real I/O errors only (bad bit indicates unrecoverable error). // failbit alone is not fatal - it's set on EOF or when requesting bytes // beyond available data, which is normal behavior for stringstreams. if (source_.bad()) { throw std::runtime_error("StreamParser read failure"); } // Create string_view this->data_ptr_->data = chunk; // Parse this->current_row_ = CSVRow(this->data_ptr_); size_t remainder = this->parse(); if (source_.eof() || chunk.empty()) { this->eof_ = true; this->end_feed(); } else { // Save the tail bytes that begin an incomplete row so they // are prepended to the next chunk (see class-level comment). leftover_ = chunk.substr(remainder); } } private: // Bytes from the previous chunk that form the start of an incomplete // row, plus the initial head buffer on the first call. std::string leftover_; TStream& source_; }; #if !defined(__EMSCRIPTEN__) /** Parser for memory-mapped files * * @par Implementation * This class constructs moving windows over a file to avoid * creating massive memory maps which may require more RAM * than the user has available. It contains logic to automatically * re-align each memory map to the beginning of a CSV row. * */ class MmapParser : public IBasicCSVParser { public: MmapParser(csv::string_view filename, const CSVFormat& format, const ColNamesPtr& col_names = nullptr ) : IBasicCSVParser(format, col_names) { this->_filename = filename.data(); this->source_size_ = get_file_size(filename); }; ~MmapParser() {} void next(size_t bytes) override; private: std::string _filename; size_t mmap_pos = 0; }; #endif } } /** The all encompassing namespace */ namespace csv { /** Stuff that is generally not of interest to end-users */ namespace internals { std::string format_row(const std::vector& row, csv::string_view delim = ", "); std::vector _get_col_names( csv::string_view head, const CSVFormat format = CSVFormat::guess_csv()); struct GuessScore { double score; size_t header; }; CSV_INLINE GuessScore calculate_score(csv::string_view head, const CSVFormat& format); CSVGuessResult _guess_format(csv::string_view head, const std::vector& delims = { ',', '|', '\t', ';', '^', '~' }); } std::vector get_col_names( csv::string_view filename, const CSVFormat format = CSVFormat::guess_csv()); /** @brief Guess the delimiter and header row of a CSV file * * @param[in] filename Path to CSV file * @param[in] delims Candidate delimiters to test * @return CSVGuessResult containing the detected delimiter and header row index * * **Heuristic:** For each candidate delimiter, calculate a score based on * the most common row length (mode). The delimiter with the highest score wins. * * **Header Detection:** * - If the first row has >= columns than the mode, it's treated as the header * - Otherwise, the first row with the mode length is treated as the header * * This approach handles: * - Headers with trailing delimiters or optional columns (wider than data rows) * - Comment lines before the actual header (first row shorter than mode) * - Standard CSVs where first row is the header * * @note Score = (row_length × count_of_rows_with_that_length) */ CSVGuessResult guess_format(csv::string_view filename, const std::vector& delims = { ',', '|', '\t', ';', '^', '~' }); /** @class CSVReader * @brief Main class for parsing CSVs from files and in-memory sources * * All rows are compared to the column names for length consistency * - By default, rows that are too short or too long are dropped * - Custom behavior can be defined by overriding bad_row_handler in a subclass * * **Streaming semantics:** CSVReader is a single-pass streaming reader. Every read * operation — read_row(), the iterator interface — pulls rows permanently * from the internal queue. Rows consumed by one interface are not visible to another. * There is no rewind or seek. * * **Ownership and sharing:** CSVReader is non-copyable and move-enabled. It manages * live parsing state (worker thread, internal queue, and optional owned stream), so * ownership transfer should be explicit. To share or transfer a reader, wrap it in a * `std::unique_ptr`: * @code{.cpp} * auto reader = std::make_unique("data.csv"); * process(std::move(reader)); // transfer ownership * @endcode */ class CSVReader { public: /** * An input iterator capable of handling large files. * @note Created by CSVReader::begin() and CSVReader::end(). * * @par Iterating over a file * @snippet tests/test_csv_iterator.cpp CSVReader Iterator 1 * * @par Using with `` library * @snippet tests/test_csv_iterator.cpp CSVReader Iterator 2 * * @warning STREAMING CONSTRAINT - DO NOT ATTEMPT TO CACHE ALL DATA * This iterator is intentionally std::input_iterator_tag (single-pass) to support * streaming large CSV files that may exceed available RAM (e.g., 50+ GB files). * * @par CRITICAL DESIGN CONSTRAINT: * - The underlying RawCSVData chunks are automatically freed as the iterator advances * - Only the CURRENT row's data is kept alive (via CSVRow's RawCSVDataPtr member) * - Previous chunks are freed to allow unlimited file sizes with bounded memory * * @par WHY FORWARD ITERATOR IS NOT POSSIBLE: * - ForwardIterator requires multi-pass guarantees (can hold multiple valid positions) * - Supporting this would require caching ALL RawCSVDataPtr chunks in the iterator * - This defeats the streaming purpose: a 50 GB CSV would require 50+ GB of RAM * - The entire library design depends on automatic chunk cleanup for memory efficiency * * @par IMPLICATIONS FOR ALGORITHM USE: * - Algorithms requiring ForwardIterator (std::max_element, std::sort, etc.) may * appear to work in tests with small files, but will cause heap-use-after-free * when the algorithm tries to access iterators pointing to freed chunks * - CORRECT approach: Copy rows to std::vector first, then use algorithms * - Example: auto rows = std::vector(reader.begin(), reader.end()); * auto max_row = std::max_element(rows.begin(), rows.end(), ...); * * @par FOR AI AGENTS AND MAINTAINERS: * If you encounter heap-use-after-free with ForwardIterator algorithms, the solution * is NOT to cache all RawCSVDataPtr chunks - this violates the fundamental streaming * architecture. Instead, document the limitation or provide vector-based workarounds. */ class iterator { public: #ifndef DOXYGEN_SHOULD_SKIP_THIS using value_type = CSVRow; using difference_type = std::ptrdiff_t; using pointer = CSVRow * ; using reference = CSVRow & ; using iterator_category = std::input_iterator_tag; #endif iterator() = default; iterator(CSVReader* reader) : daddy(reader) {} iterator(CSVReader*, CSVRow&&); /** Access the CSVRow held by the iterator */ CONSTEXPR_14 reference operator*() { return this->row; } CONSTEXPR_14 reference operator*() const { return const_cast(this->row); } /** Return a pointer to the CSVRow the iterator has stopped at */ CONSTEXPR_14 pointer operator->() { return &(this->row); } CONSTEXPR_14 pointer operator->() const { return const_cast(&(this->row)); } iterator& operator++(); /**< Pre-increment iterator */ iterator operator++(int); /**< Post-increment iterator */ /** Returns true if iterators were constructed from the same CSVReader * and point to the same row */ CONSTEXPR bool operator==(const iterator& other) const noexcept { return (this->daddy == other.daddy) && (this->i == other.i); } CONSTEXPR bool operator!=(const iterator& other) const noexcept { return !operator==(other); } private: CSVReader * daddy = nullptr; // Pointer to parent CSVRow row; // Current row size_t i = 0; // Index of current row }; /** @name Constructors * Constructors for iterating over large files and parsing in-memory sources. */ ///@{ /** @brief Construct CSVReader from filename. * * Native builds use CODE PATH 1 of 2: MmapParser with mio for maximum performance. * Emscripten builds fall back to the stream-based implementation because mmap is unavailable. * * @note On native builds, bugs can exist in this path independently of the stream path * @note When writing tests that validate I/O behavior, test both filename and stream constructors * @see StreamParser for the stream-based alternative */ CSVReader(csv::string_view filename, CSVFormat format = CSVFormat::guess_csv()); /** @brief Construct CSVReader from std::istream * * Uses StreamParser. On native builds this is CODE PATH 2 of 2 and remains independent * from the filename-based mmap path. On Emscripten, the filename constructor also funnels * through this implementation. * * @tparam TStream An input stream deriving from `std::istream` * @note Delimiter/header guessing is still available by default via CSVFormat::guess_csv(). * For deterministic parsing of known dialects, pass an explicit CSVFormat. * @note On native builds, tests that validate I/O behavior should cover both constructors * @see MmapParser for the memory-mapped alternative */ template::value, int> = 0> CSVReader(TStream &source, CSVFormat format = CSVFormat::guess_csv()) : _format(format) { this->init_from_stream(source, format); } /** @brief Construct CSVReader from an owned std::istream * * This is an opt-in safety switch for stream lifetime management. * CSVReader takes ownership and guarantees the stream outlives parsing. */ CSVReader(std::unique_ptr source, const CSVFormat& format = CSVFormat::guess_csv()) : _format(format), owned_stream(std::move(source)) { if (!this->owned_stream) { throw std::invalid_argument("CSVReader requires a non-null stream"); } this->init_from_stream(*this->owned_stream, format); } ///@} CSVReader(const CSVReader&) = delete; ///< Not copyable CSVReader& operator=(const CSVReader&) = delete; ///< Not copyable /** Move constructor. * * Required so C++11 builds can return CSVReader by value from helpers like * csv::parse()/csv::parse_unsafe(), where copy elision is not guaranteed. * * Any active worker on the source is joined before moving parser state to * avoid a thread continuing to run against the source object's address. */ CSVReader(CSVReader&& other) noexcept : _format(std::move(other._format)), col_names(std::move(other.col_names)), parser(std::move(other.parser)), records(std::move(other.records)), owned_stream(std::move(other.owned_stream)), n_cols(other.n_cols), _n_rows(other._n_rows), header_trimmed(other.header_trimmed), _chunk_size(other._chunk_size), _read_requested(other._read_requested), read_csv_exception(other.take_read_csv_exception()) { #if CSV_ENABLE_THREADS if (other.read_csv_worker.joinable()) { other.read_csv_worker.join(); } #endif other.n_cols = 0; other._n_rows = 0; other.header_trimmed = false; other._read_requested = false; other._chunk_size = internals::ITERATION_CHUNK_SIZE; } /** Move assignment. * * Joins active workers on both sides before transferring parser state. */ CSVReader& operator=(CSVReader&& other) noexcept { if (this == &other) { return *this; } #if CSV_ENABLE_THREADS if (this->read_csv_worker.joinable()) { this->read_csv_worker.join(); } if (other.read_csv_worker.joinable()) { other.read_csv_worker.join(); } #endif this->_format = std::move(other._format); this->col_names = std::move(other.col_names); this->parser = std::move(other.parser); this->records = std::move(other.records); this->owned_stream = std::move(other.owned_stream); this->n_cols = other.n_cols; this->_n_rows = other._n_rows; this->header_trimmed = other.header_trimmed; this->_chunk_size = other._chunk_size; this->_read_requested = other._read_requested; this->read_csv_exception = other.take_read_csv_exception(); other.n_cols = 0; other._n_rows = 0; other.header_trimmed = false; other._read_requested = false; other._chunk_size = internals::ITERATION_CHUNK_SIZE; return *this; } ~CSVReader() { #if CSV_ENABLE_THREADS if (this->read_csv_worker.joinable()) { this->read_csv_worker.join(); } #endif } /** @name Retrieving CSV Rows */ ///@{ bool read_row(CSVRow &row); iterator begin(); CSV_CONST iterator end() const noexcept; /** Returns true if we have reached end of file */ bool eof() const noexcept { return this->parser->eof(); } ///@} /** @name CSV Metadata */ ///@{ CSVFormat get_format() const; std::vector get_col_names() const; int index_of(csv::string_view col_name) const; ///@} /** @name CSV Metadata: Attributes */ ///@{ /** Whether or not the file or stream contains valid CSV rows, * not including the header. * * @note Gives an accurate answer regardless of when it is called. * */ CONSTEXPR bool empty() const noexcept { return this->n_rows() == 0; } /** Retrieves the number of rows that have been read so far */ CONSTEXPR size_t n_rows() const noexcept { return this->_n_rows; } /** Whether or not CSV was prefixed with a UTF-8 bom */ bool utf8_bom() const noexcept { return this->parser->utf8_bom(); } ///@} protected: /** * \defgroup csv_internal CSV Parser Internals * @brief Internals of CSVReader. Only maintainers and those looking to * extend the parser should read this. * @{ */ /** Sets this reader's column names and associated data */ void set_col_names(const std::vector&); /** @name CSV Settings **/ ///@{ CSVFormat _format; ///@} /** @name Parser State */ ///@{ /** Pointer to a object containing column information */ internals::ColNamesPtr col_names = std::make_shared(); /** Helper class which actually does the parsing */ std::unique_ptr parser = nullptr; /** Queue of parsed CSV rows */ std::unique_ptr records{new RowCollection(100)}; /** * Optional owned stream used by two paths: * 1) Emscripten filename-constructor fallback to stream parsing * 2) Opt-in ownership constructor taking std::unique_ptr */ std::unique_ptr owned_stream = nullptr; size_t n_cols = 0; /**< The number of columns in this CSV */ size_t _n_rows = 0; /**< How many rows (minus header) have been read so far */ /** @name Multi-Threaded File Reading Functions */ ///@{ bool read_csv(size_t bytes = internals::ITERATION_CHUNK_SIZE); ///@} /**@}*/ private: /** Whether or not rows before header were trimmed */ bool header_trimmed = false; /** @name Multi-Threaded File Reading: Flags and State */ ///@{ #if CSV_ENABLE_THREADS std::thread read_csv_worker; /**< Worker thread for read_csv() */ #endif size_t _chunk_size = internals::ITERATION_CHUNK_SIZE; /**< Current chunk size in bytes */ bool _read_requested = false; /**< Flag to detect infinite read loops (Issue #218) */ ///@} /** If the worker thread throws, store it here and rethrow on the consumer thread. */ std::exception_ptr read_csv_exception = nullptr; #if CSV_ENABLE_THREADS std::mutex read_csv_exception_lock; #endif void set_read_csv_exception(std::exception_ptr eptr) { #if CSV_ENABLE_THREADS std::lock_guard lock(this->read_csv_exception_lock); #endif this->read_csv_exception = std::move(eptr); } std::exception_ptr take_read_csv_exception() { #if CSV_ENABLE_THREADS std::lock_guard lock(this->read_csv_exception_lock); #endif auto eptr = this->read_csv_exception; this->read_csv_exception = nullptr; return eptr; } void rethrow_read_csv_exception_if_any() { if (auto eptr = this->take_read_csv_exception()) { std::rethrow_exception(eptr); } } template::value, int> = 0> void init_from_stream(TStream& source, CSVFormat format) { // Read a head buffer without rewinding. Works with non-seekable streams // (pipes, decompression filters). The buffer is passed to StreamParser // as its initial leftover_ so those bytes are parsed as the first chunk. auto head = internals::read_head_buffer(source); using Parser = internals::StreamParser; // Apply chunk size from format before any reading occurs this->_chunk_size = format.get_chunk_size(); if (format.guess_delim()) { auto guess_result = internals::_guess_format(head, format.possible_delimiters); format.delimiter(guess_result.delim); // Only override header if user hasn't explicitly called no_header() // Note: column_names() also sets header=-1, but it populates col_names, // so we can distinguish: no_header() means header=-1 && col_names.empty() if (format.header != -1 || !format.col_names.empty()) { format.header = guess_result.header_row; } this->_format = format; } if (!format.col_names.empty()) { this->set_col_names(format.col_names); } #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4316) #endif this->parser = std::unique_ptr( new Parser(source, format, col_names, std::move(head))); // For C++11 #ifdef _MSC_VER #pragma warning(pop) #endif this->initial_read(); } /** Read initial chunk to get metadata */ void initial_read() { #if CSV_ENABLE_THREADS this->read_csv_worker = std::thread(&CSVReader::read_csv, this, this->_chunk_size); this->read_csv_worker.join(); #else this->read_csv(this->_chunk_size); #endif this->rethrow_read_csv_exception_if_any(); } void trim_header(); }; } namespace csv { namespace internals { template class is_hashable { private: template static auto test(int) -> decltype( std::hash{}(std::declval()), std::true_type{} ); template static std::false_type test(...); public: static constexpr bool value = decltype(test(0))::value; }; template class is_equality_comparable { private: template static auto test(int) -> decltype( std::declval() == std::declval(), std::true_type{} ); template static std::false_type test(...); public: static constexpr bool value = decltype(test(0))::value; }; } /** Allows configuration of DataFrame behavior. */ class DataFrameOptions { public: DataFrameOptions() = default; /** Policy for handling duplicate keys when creating a keyed DataFrame */ enum class DuplicateKeyPolicy { THROW, // Throw an error if a duplicate key is encountered OVERWRITE, // Overwrite the existing value with the new value KEEP_FIRST // Ignore the new value and keep the existing value }; DataFrameOptions& set_duplicate_key_policy(DuplicateKeyPolicy value) { this->duplicate_key_policy = value; return *this; } DuplicateKeyPolicy get_duplicate_key_policy() const { return this->duplicate_key_policy; } DataFrameOptions& set_key_column(const std::string& value) { this->key_column = value; return *this; } const std::string& get_key_column() const { return this->key_column; } DataFrameOptions& set_throw_on_missing_key(bool value) { this->throw_on_missing_key = value; return *this; } bool get_throw_on_missing_key() const { return this->throw_on_missing_key; } private: std::string key_column; DuplicateKeyPolicy duplicate_key_policy = DuplicateKeyPolicy::OVERWRITE; /** Whether to throw an error if a key column value is missing when creating a keyed DataFrame */ bool throw_on_missing_key = true; }; /** * Proxy class that wraps a CSVRow and intercepts field access to check for edits. * Provides transparent access to both original and edited cell values. */ template class DataFrameRow { public: /** Default constructor (creates an unbound proxy). */ DataFrameRow() : row(nullptr), row_edits(nullptr), key_ptr(nullptr) {} /** Construct a DataFrameRow wrapper. */ DataFrameRow( const CSVRow* _row, const std::unordered_map* _edits, const KeyType* _key ) : row(_row), row_edits(_edits), key_ptr(_key) {} /** * Access a field by column name, checking edits first. * * @param col Column name * @return CSVField representing the field value (edited if present, otherwise original) */ CSVField operator[](const std::string& col) const { // Find column index by searching column names auto col_names = row->get_col_names(); auto it = std::find(col_names.begin(), col_names.end(), col); if (it == col_names.end()) { // Column not found, let row handle the error return (*row)[col]; } size_t col_index = std::distance(col_names.begin(), it); if (row_edits) { auto edit_it = row_edits->find(col_index); if (edit_it != row_edits->end()) { return CSVField(csv::string_view(edit_it->second)); } } return (*row)[col]; } /** Access a field by position (positional access never checks edits). */ CSVField operator[](size_t n) const { return (*row)[n]; } /** Get the number of fields in the row. */ size_t size() const { return row->size(); } /** Check if the row is empty. */ bool empty() const { return row->empty(); } /** Get column names. */ std::vector get_col_names() const { return row->get_col_names(); } /** Get the underlying CSVRow for compatibility. */ const CSVRow& get_underlying_row() const { return *row; } /** Get the key for this row (only valid for keyed DataFrames). */ const KeyType& get_key() const { return *key_ptr; } /** Convert to vector of strings for CSVWriter compatibility. */ operator std::vector() const { std::vector result; result.reserve(row->size()); for (size_t i = 0; i < row->size(); i++) { // Check if this column has an edit if (row_edits) { auto it = row_edits->find(i); if (it != row_edits->end()) { result.push_back(it->second); continue; } } // Use original value result.push_back((*row)[i].get()); } return result; } /** Convert to JSON. */ std::string to_json(const std::vector& subset = {}) const { return row->to_json(subset); } /** Convert to JSON array. */ std::string to_json_array(const std::vector& subset = {}) const { return row->to_json_array(subset); } #ifdef CSV_HAS_CXX20 /** Convert this DataFrameRow into a std::ranges::input_range of string_views, * respecting the sparse overlay (edited values take precedence). */ auto to_sv_range() const { return std::views::iota(size_t{0}, this->size()) | std::views::transform([this](size_t i) { // Check if this column has an edit if (row_edits) { auto it = row_edits->find(i); if (it != row_edits->end()) { return csv::string_view(it->second); } } // Use original value return this->get_underlying_row().get_field(i); }); } #endif private: const CSVRow* row; const std::unordered_map* row_edits; const KeyType* key_ptr; }; template class DataFrame { public: /** Type alias for internal row storage: pair of key and CSVRow. */ using row_entry = std::pair; /** Row-wise iterator over DataFrameRow entries. Provides access to rows with edit support. */ class iterator { public: using value_type = DataFrameRow; using difference_type = std::ptrdiff_t; using pointer = const DataFrameRow*; using reference = const DataFrameRow&; using iterator_category = std::random_access_iterator_tag; iterator() = default; iterator( typename std::vector::iterator it, const std::unordered_map>* edits ) : iter(it), edits_map(edits) {} reference operator*() const { const std::unordered_map* row_edits = nullptr; if (edits_map) { auto it = edits_map->find(iter->first); if (it != edits_map->end()) { row_edits = &it->second; } } cached_row = DataFrameRow(&iter->second, row_edits, &iter->first); return cached_row; } pointer operator->() const { // Ensure cached_row is populated operator*(); return &cached_row; } iterator& operator++() { ++iter; return *this; } iterator operator++(int) { auto tmp = *this; ++iter; return tmp; } iterator& operator--() { --iter; return *this; } iterator operator--(int) { auto tmp = *this; --iter; return tmp; } iterator operator+(difference_type n) const { return iterator(iter + n, edits_map); } iterator operator-(difference_type n) const { return iterator(iter - n, edits_map); } difference_type operator-(const iterator& other) const { return iter - other.iter; } bool operator==(const iterator& other) const { return iter == other.iter; } bool operator!=(const iterator& other) const { return iter != other.iter; } private: typename std::vector::iterator iter; const std::unordered_map>* edits_map = nullptr; mutable DataFrameRow cached_row; }; /** Row-wise const iterator over DataFrameRow entries. Provides read-only access to rows with edit support. */ class const_iterator { public: using value_type = DataFrameRow; using difference_type = std::ptrdiff_t; using pointer = const DataFrameRow*; using reference = const DataFrameRow&; using iterator_category = std::random_access_iterator_tag; const_iterator() = default; const_iterator( typename std::vector::const_iterator it, const std::unordered_map>* edits ) : iter(it), edits_map(edits) {} reference operator*() const { const std::unordered_map* row_edits = nullptr; if (edits_map) { auto it = edits_map->find(iter->first); if (it != edits_map->end()) { row_edits = &it->second; } } cached_row = DataFrameRow(&iter->second, row_edits, &iter->first); return cached_row; } pointer operator->() const { // Ensure cached_row is populated operator*(); return &cached_row; } const_iterator& operator++() { ++iter; return *this; } const_iterator operator++(int) { auto tmp = *this; ++iter; return tmp; } const_iterator& operator--() { --iter; return *this; } const_iterator operator--(int) { auto tmp = *this; --iter; return tmp; } const_iterator operator+(difference_type n) const { return const_iterator(iter + n, edits_map); } const_iterator operator-(difference_type n) const { return const_iterator(iter - n, edits_map); } difference_type operator-(const const_iterator& other) const { return iter - other.iter; } bool operator==(const const_iterator& other) const { return iter == other.iter; } bool operator!=(const const_iterator& other) const { return iter != other.iter; } private: typename std::vector::const_iterator iter; const std::unordered_map>* edits_map = nullptr; mutable DataFrameRow cached_row; }; static_assert( internals::is_hashable::value, "DataFrame requires KeyType to be hashable (std::hash specialization required)." ); static_assert( internals::is_equality_comparable::value, "DataFrame requires KeyType to be equality comparable (operator== required)." ); static_assert( std::is_default_constructible::value, "DataFrame requires KeyType to be default-constructible." ); using DuplicateKeyPolicy = DataFrameOptions::DuplicateKeyPolicy; /** Construct an empty DataFrame. */ DataFrame() = default; /** * Construct an unkeyed DataFrame from a CSV reader. * Rows are accessible by position only. */ explicit DataFrame(CSVReader& reader) { this->init_unkeyed_from_reader(reader); } /** * Construct a keyed DataFrame from a CSV reader with options. * * @param reader CSV reader to consume * @param options Configuration including key column and duplicate policies * @throws std::runtime_error if key column is empty or not found */ explicit DataFrame(CSVReader& reader, const DataFrameOptions& options) { this->init_from_reader(reader, options); } /** * Construct a keyed DataFrame directly from a CSV file. * * @param filename Path to the CSV file * @param options Configuration including key column and duplicate policies * @param format CSV format specification (defaults to auto-detection) * @throws std::runtime_error if key column is empty or not found */ DataFrame( csv::string_view filename, const DataFrameOptions& options, CSVFormat format = CSVFormat::guess_csv() ) { CSVReader reader(filename, format); this->init_from_reader(reader, options); } /** * Construct a keyed DataFrame using a column name as the key. * * @param reader CSV reader to consume * @param _key_column Name of the column to use as the key * @param policy How to handle duplicate keys (default: OVERWRITE) * @param throw_on_missing_key Whether to throw if a key value cannot be parsed (default: true) * @throws std::runtime_error if key column is empty or not found */ DataFrame( CSVReader& reader, const std::string& _key_column, DuplicateKeyPolicy policy = DuplicateKeyPolicy::OVERWRITE, bool throw_on_missing_key = true ) : DataFrame( reader, DataFrameOptions() .set_key_column(_key_column) .set_duplicate_key_policy(policy) .set_throw_on_missing_key(throw_on_missing_key) ) {} /** * Construct a keyed DataFrame using a custom key function. * * @param reader CSV reader to consume * @param key_func Function that extracts a key from each row * @param policy How to handle duplicate keys (default: OVERWRITE) * @throws std::runtime_error if policy is THROW and duplicate keys are encountered */ template< typename KeyFunc, typename ResultType = invoke_result_t, csv::enable_if_t::value, int> = 0 > DataFrame( CSVReader& reader, KeyFunc key_func, DuplicateKeyPolicy policy = DuplicateKeyPolicy::OVERWRITE ) : col_names_(reader.get_col_names()) { this->is_keyed = true; this->build_from_key_function(reader, key_func, policy); } /** * Construct a keyed DataFrame using a custom key function with options. * * @param reader CSV reader to consume * @param key_func Function that extracts a key from each row * @param options Configuration for duplicate key policy */ template< typename KeyFunc, typename ResultType = invoke_result_t, csv::enable_if_t::value, int> = 0 > DataFrame( CSVReader& reader, KeyFunc key_func, const DataFrameOptions& options ) : DataFrame(reader, key_func, options.get_duplicate_key_policy()) {} /** Get the number of rows in the DataFrame. */ size_t size() const noexcept { return rows.size(); } /** Check if the DataFrame is empty (has no rows). */ bool empty() const noexcept { return rows.empty(); } /** Get the number of rows in the DataFrame. Alias for size(). */ size_t n_rows() const noexcept { return rows.size(); } /** Get the number of columns in the DataFrame. */ size_t n_cols() const noexcept { return col_names_.size(); } /** * Check if a column exists in the DataFrame. * * @param name Column name to check * @return true if the column exists, false otherwise */ bool has_column(const std::string& name) const { return std::find(col_names_.begin(), col_names_.end(), name) != col_names_.end(); } /** * Get the index of a column by name. * * @param name Column name to find * @return Column index (0-based) or CSV_NOT_FOUND if not found */ int index_of(const std::string& name) const { auto it = std::find(col_names_.begin(), col_names_.end(), name); if (it == col_names_.end()) return CSV_NOT_FOUND; return static_cast(std::distance(col_names_.begin(), it)); } /** Get the column names in order. */ const std::vector& columns() const noexcept { return col_names_; } /** Get the name of the key column (empty string if unkeyed). */ const std::string& key_name() const noexcept { return key_column; } /** * Access a row by position (unchecked). * * @note Disabled when KeyType is an integral type to prevent ambiguity with * operator[](const KeyType&). Use iloc() for positional access on * integer-keyed DataFrames. * * @param i Row index (0-based) * @return DataFrameRow proxy with edit support * @throws std::out_of_range if index is out of bounds (via std::vector::at) */ template::value, int> = 0> DataFrameRow operator[](size_t i) { static_assert(std::is_same::value, "Do not explicitly instantiate this template. Use iloc() for positional access."); return this->iloc(i); } /** Access a row by position (unchecked, const version). * Disabled when KeyType is an integral type — use iloc() instead. */ template::value, int> = 0> DataFrameRow operator[](size_t i) const { static_assert(std::is_same::value, "Do not explicitly instantiate this template. Use iloc() for positional access."); return this->iloc(i); } /** * Access a row by position with bounds checking. * * @param i Row index (0-based) * @return DataFrameRow proxy with edit support * @throws std::out_of_range if index is out of bounds */ DataFrameRow at(size_t i) { const std::unordered_map* row_edits = nullptr; if (is_keyed) { auto it = edits.find(rows.at(i).first); if (it != edits.end()) row_edits = &it->second; } return DataFrameRow(&rows.at(i).second, row_edits, &rows.at(i).first); } /** Access a row by position with bounds checking (const version). */ DataFrameRow at(size_t i) const { const std::unordered_map* row_edits = nullptr; if (is_keyed) { auto it = edits.find(rows.at(i).first); if (it != edits.end()) row_edits = &it->second; } return DataFrameRow(&rows.at(i).second, row_edits, &rows.at(i).first); } /** * Access a row by its key. * * @param key The row key to look up * @return DataFrameRow proxy with edit support * @throws std::runtime_error if the DataFrame was not created with a key column * @throws std::out_of_range if the key is not found */ DataFrameRow operator[](const KeyType& key) { this->require_keyed_frame(); auto position = this->position_of(key); const std::unordered_map* row_edits = nullptr; auto it = edits.find(key); if (it != edits.end()) row_edits = &it->second; return DataFrameRow(&rows[position].second, row_edits, &rows[position].first); } /** Access a row by its key (const version). */ DataFrameRow operator[](const KeyType& key) const { this->require_keyed_frame(); auto position = this->position_of(key); const std::unordered_map* row_edits = nullptr; auto it = edits.find(key); if (it != edits.end()) row_edits = &it->second; return DataFrameRow(&rows[position].second, row_edits, &rows[position].first); } /** * Access a row by position (iloc-style, pandas naming). * * @param i Row index (0-based) * @return DataFrameRow proxy with edit support * @throws std::out_of_range if index is out of bounds */ DataFrameRow iloc(size_t i) { const std::unordered_map* row_edits = nullptr; if (is_keyed) { auto it = edits.find(rows.at(i).first); if (it != edits.end()) row_edits = &it->second; } return DataFrameRow(&rows.at(i).second, row_edits, &rows.at(i).first); } /** Access a row by position (const version). */ DataFrameRow iloc(size_t i) const { const std::unordered_map* row_edits = nullptr; if (is_keyed) { auto it = edits.find(rows.at(i).first); if (it != edits.end()) row_edits = &it->second; } return DataFrameRow(&rows.at(i).second, row_edits, &rows.at(i).first); } /** * Attempt to access a row by position without throwing. * * @param i Row index (0-based) * @param out Output parameter that receives the DataFrameRow if found * @return true if the row exists, false otherwise */ bool try_get(size_t i, DataFrameRow& out) { if (i >= rows.size()) { return false; } const std::unordered_map* row_edits = nullptr; if (is_keyed) { auto it = edits.find(rows[i].first); if (it != edits.end()) row_edits = &it->second; } out = DataFrameRow(&rows[i].second, row_edits, &rows[i].first); return true; } /** Attempt to access a row by position without throwing (const version). */ bool try_get(size_t i, DataFrameRow& out) const { if (i >= rows.size()) { return false; } const std::unordered_map* row_edits = nullptr; if (is_keyed) { auto it = edits.find(rows[i].first); if (it != edits.end()) row_edits = &it->second; } out = DataFrameRow(&rows[i].second, row_edits, &rows[i].first); return true; } /** * Get the key for a row at a given position. * * @param i Row index (0-based) * @return Reference to the key * @throws std::runtime_error if the DataFrame was not created with a key column * @throws std::out_of_range if index is out of bounds */ const KeyType& key_at(size_t i) const { this->require_keyed_frame(); return rows.at(i).first; } /** * Check if a key exists in the DataFrame. * * @param key The key to check * @return true if the key exists, false otherwise * @throws std::runtime_error if the DataFrame was not created with a key column */ bool contains(const KeyType& key) const { this->require_keyed_frame(); this->ensure_key_index(); return key_index->find(key) != key_index->end(); } /** * Access a row by its key with bounds checking. * * @param key The row key to look up * @return DataFrameRow proxy with edit support * @throws std::runtime_error if the DataFrame was not created with a key column * @throws std::out_of_range if the key is not found */ DataFrameRow at(const KeyType& key) { this->require_keyed_frame(); auto position = this->position_of(key); const std::unordered_map* row_edits = nullptr; auto it = edits.find(key); if (it != edits.end()) row_edits = &it->second; return DataFrameRow(&rows.at(position).second, row_edits, &rows.at(position).first); } /** Access a row by its key with bounds checking (const version). */ DataFrameRow at(const KeyType& key) const { this->require_keyed_frame(); auto position = this->position_of(key); const std::unordered_map* row_edits = nullptr; auto it = edits.find(key); if (it != edits.end()) row_edits = &it->second; return DataFrameRow(&rows.at(position).second, row_edits, &rows.at(position).first); } /** * Attempt to access a row by key without throwing. * * @param key The row key to look up * @param out Output parameter that receives the DataFrameRow if found * @return true if the key exists, false otherwise * @throws std::runtime_error if the DataFrame was not created with a key column */ bool try_get(const KeyType& key, DataFrameRow& out) { this->require_keyed_frame(); this->ensure_key_index(); auto it = key_index->find(key); if (it == key_index->end()) { return false; } const std::unordered_map* row_edits = nullptr; auto edit_it = edits.find(key); if (edit_it != edits.end()) row_edits = &edit_it->second; out = DataFrameRow(&rows[it->second].second, row_edits, &rows[it->second].first); return true; } /** Attempt to access a row by key without throwing (const version). */ bool try_get(const KeyType& key, DataFrameRow& out) const { this->require_keyed_frame(); this->ensure_key_index(); auto it = key_index->find(key); if (it == key_index->end()) { return false; } const std::unordered_map* row_edits = nullptr; auto edit_it = edits.find(key); if (edit_it != edits.end()) row_edits = &edit_it->second; out = DataFrameRow(&rows[it->second].second, row_edits, &rows[it->second].first); return true; } /** * Get a cell value as a string, accounting for edits. * * @param key The row key * @param column The column name * @return Cell value as a string (edited value if present, otherwise original) * @throws std::runtime_error if the DataFrame was not created with a key column * @throws std::out_of_range if the key is not found */ std::string get(const KeyType& key, const std::string& column) const { this->require_keyed_frame(); auto col_names = (*this)[key].get_col_names(); auto col_it = std::find(col_names.begin(), col_names.end(), column); if (col_it == col_names.end()) { throw std::out_of_range("Column '" + column + "' not found"); } size_t col_idx = std::distance(col_names.begin(), col_it); auto row_edits = this->edits.find(key); if (row_edits != this->edits.end()) { auto value = row_edits->second.find(col_idx); if (value != row_edits->second.end()) { return value->second; } } return (*this)[key][column].template get(); } /** * Set a cell value (stored in edit overlay). * * @param key The row key * @param column The column name * @param value The new value as a string * @throws std::runtime_error if the DataFrame was not created with a key column * @throws std::out_of_range if the key is not found */ void set(const KeyType& key, const std::string& column, const std::string& value) { this->require_keyed_frame(); size_t row_idx = this->position_of(key); // Find column index auto col_names = rows[row_idx].second.get_col_names(); auto it = std::find(col_names.begin(), col_names.end(), column); if (it == col_names.end()) { throw std::out_of_range("Column '" + column + "' not found"); } size_t col_idx = std::distance(col_names.begin(), it); edits[key][col_idx] = value; } /** * Remove a row by its key. * * @param key The row key to remove * @return true if the row was removed, false if not found * @throws std::runtime_error if the DataFrame was not created with a key column */ bool erase_row(const KeyType& key) { this->require_keyed_frame(); this->ensure_key_index(); auto it = key_index->find(key); if (it == key_index->end()) { return false; } rows.erase(rows.begin() + it->second); edits.erase(key); this->invalidate_key_index(); return true; } /** * Remove a row by its position. * * @param i Row index (0-based) * @return true if the row was removed, false if index out of bounds */ bool erase_row_at(size_t i) { if (i >= rows.size()) return false; if (is_keyed) edits.erase(rows[i].first); rows.erase(rows.begin() + i); this->invalidate_key_index(); return true; } /** * Set a cell value by position (stored in edit overlay). * * @param i Row index (0-based) * @param column The column name * @param value The new value as a string * @throws std::runtime_error if the DataFrame was not created with a key column * @throws std::out_of_range if index is out of bounds */ void set_at(size_t i, const std::string& column, const std::string& value) { if (!is_keyed) { throw std::runtime_error("This DataFrame was created without a key column."); } if (i >= rows.size()) { throw std::out_of_range("Row index out of bounds."); } // Find column index auto col_names = rows[i].second.get_col_names(); auto it = std::find(col_names.begin(), col_names.end(), column); if (it == col_names.end()) { throw std::out_of_range("Column '" + column + "' not found"); } size_t col_idx = std::distance(col_names.begin(), it); edits[rows[i].first][col_idx] = value; } /** * Extract all values from a column with type conversion. * Accounts for edited values in the overlay. * * @tparam T Target type for conversion (default: std::string) * @param name Column name * @return Vector of values converted to type T * @throws std::runtime_error if column is not found */ template std::vector column(const std::string& name) const { auto col_it = std::find(col_names_.begin(), col_names_.end(), name); if (col_it == col_names_.end()) { throw std::runtime_error("Column not found: " + name); } size_t col_idx = std::distance(col_names_.begin(), col_it); std::vector values; values.reserve(rows.size()); for (const auto& entry : rows) { auto row_edits = this->edits.find(entry.first); if (row_edits != this->edits.end()) { auto value = row_edits->second.find(col_idx); if (value != row_edits->second.end()) { // Reuse CSVField parsing/validation on edited string values. CSVField edited_field(csv::string_view(value->second)); values.push_back(edited_field.template get()); continue; } } values.push_back(entry.second[name].template get()); } return values; } /** * Group row positions using an arbitrary grouping function. * * @tparam GroupFunc Callable that takes a CSVRow and returns a hashable key * @param group_func Function to extract group key from each row * @return Map of group key -> vector of row indices belonging to that group */ template< typename GroupFunc, typename GroupKey = invoke_result_t, csv::enable_if_t< internals::is_hashable::value && internals::is_equality_comparable::value, int > = 0 > std::unordered_map> group_by(GroupFunc group_func) const { std::unordered_map> grouped; for (size_t i = 0; i < rows.size(); i++) { GroupKey group_key = group_func(rows[i].second); grouped[group_key].push_back(i); } return grouped; } /** * Group row positions by the value of a column. * * @param name Column to group by * @param use_edits If true, use edited values when present (default: true) * @return Map of column value -> vector of row indices with that value * @throws std::runtime_error if column is not found */ std::unordered_map> group_by( const std::string& name, bool use_edits = true ) const { auto col_it = std::find(col_names_.begin(), col_names_.end(), name); if (col_it == col_names_.end()) { throw std::runtime_error("Column not found: " + name); } size_t col_idx = std::distance(col_names_.begin(), col_it); std::unordered_map> grouped; for (size_t i = 0; i < rows.size(); i++) { std::string group_key; bool has_group_key = false; if (use_edits) { auto row_edits = this->edits.find(rows[i].first); if (row_edits != this->edits.end()) { auto edited_value = row_edits->second.find(col_idx); if (edited_value != row_edits->second.end()) { group_key = edited_value->second; has_group_key = true; } } } if (!has_group_key) { group_key = rows[i].second[name].template get(); } grouped[group_key].push_back(i); } return grouped; } /** Get iterator to the first row. */ iterator begin() { return iterator(rows.begin(), is_keyed ? &edits : nullptr); } /** Get iterator past the last row. */ iterator end() { return iterator(rows.end(), is_keyed ? &edits : nullptr); } /** Get const iterator to the first row. */ const_iterator begin() const { return const_iterator(rows.begin(), is_keyed ? &edits : nullptr); } /** Get const iterator past the last row. */ const_iterator end() const { return const_iterator(rows.end(), is_keyed ? &edits : nullptr); } /** Get const iterator to the first row (explicit). */ const_iterator cbegin() const { return const_iterator(rows.begin(), is_keyed ? &edits : nullptr); } /** Get const iterator past the last row (explicit). */ const_iterator cend() const { return const_iterator(rows.end(), is_keyed ? &edits : nullptr); } private: /** Name of the key column (empty if unkeyed). */ std::string key_column; /** Whether this DataFrame was created with a key. */ bool is_keyed = false; /** Column names in order. */ std::vector col_names_; /** Internal storage: vector of (key, row) pairs. */ std::vector rows; /** Lazily-built index mapping keys to row positions (mutable for const methods). */ mutable std::unique_ptr> key_index; /** * Edit overlay: key -> column -> value. * Sparse storage for cell modifications without mutating original row data. */ std::unordered_map> edits; /** Initialize an unkeyed DataFrame from a CSV reader. */ void init_unkeyed_from_reader(CSVReader& reader) { this->col_names_ = reader.get_col_names(); for (auto& row : reader) { rows.push_back(row_entry{KeyType(), row}); } } /** Initialize a keyed DataFrame from a CSV reader using column-based key extraction. */ void init_from_reader(CSVReader& reader, const DataFrameOptions& options) { this->is_keyed = true; this->key_column = options.get_key_column(); this->col_names_ = reader.get_col_names(); if (key_column.empty()) { throw std::runtime_error("Key column cannot be empty."); } if (std::find(col_names_.begin(), col_names_.end(), key_column) == col_names_.end()) { throw std::runtime_error("Key column not found: " + key_column); } const bool throw_on_missing_key = options.get_throw_on_missing_key(); this->build_from_key_function( reader, [this, throw_on_missing_key](const CSVRow& row) -> KeyType { try { return row[this->key_column].template get(); } catch (const std::exception& e) { if (throw_on_missing_key) { throw std::runtime_error("Error retrieving key column value: " + std::string(e.what())); } return KeyType(); } }, options.get_duplicate_key_policy() ); } /** Build keyed DataFrame using a custom key extraction function. */ template void build_from_key_function( CSVReader& reader, KeyFunc key_func, DuplicateKeyPolicy policy ) { std::unordered_map key_to_pos; for (auto& row : reader) { KeyType key = key_func(row); auto existing = key_to_pos.find(key); if (existing != key_to_pos.end()) { if (policy == DuplicateKeyPolicy::THROW) { throw std::runtime_error("Duplicate key encountered."); } if (policy == DuplicateKeyPolicy::OVERWRITE) { rows[existing->second].second = row; } continue; } rows.push_back(row_entry{key, row}); key_to_pos[key] = rows.size() - 1; } } /** Validate that this DataFrame was created with a key column. */ void require_keyed_frame() const { if (!is_keyed) { throw std::runtime_error("This DataFrame was created without a key column."); } } /** Invalidate the lazy key index after structural changes. */ void invalidate_key_index() { key_index.reset(); } /** Build the key index if it doesn't exist (lazy initialization). */ void ensure_key_index() const { if (key_index) { return; } key_index = std::unique_ptr>( new std::unordered_map() ); for (size_t i = 0; i < rows.size(); i++) { (*key_index)[rows[i].first] = i; } } /** Find the position of a key in the rows vector. */ size_t position_of(const KeyType& key) const { this->ensure_key_index(); auto it = key_index->find(key); if (it == key_index->end()) { throw std::out_of_range("Key not found."); } return it->second; } }; } /** @file * Calculates statistics from CSV files */ #include #include #include namespace csv { /** Class for calculating statistics from CSV files and in-memory sources * * **Example** * \include programs/csv_stats.cpp * */ class CSVStat { public: using FreqCount = std::unordered_map; using TypeCount = std::unordered_map; std::vector get_mean() const; std::vector get_variance() const; std::vector get_mins() const; std::vector get_maxes() const; std::vector get_counts() const; std::vector get_dtypes() const; std::vector get_col_names() const { return this->reader.get_col_names(); } CSVStat(csv::string_view filename, CSVFormat format = CSVFormat::guess_csv()); CSVStat(std::stringstream& source, CSVFormat format = CSVFormat()); private: // An array of rolling averages // Each index corresponds to the rolling mean for the column at said index std::vector rolling_means; std::vector rolling_vars; std::vector mins; std::vector maxes; std::vector counts; std::vector dtypes; std::vector n; // Statistic calculators void variance(const long double&, const size_t&); void count(CSVField&, const size_t&); void min_max(const long double&, const size_t&); void dtype(CSVField&, const size_t&); void calc(); void calc_chunk(); void calc_worker(const size_t&); CSVReader reader; std::deque records = {}; }; } #include #include #include namespace csv { namespace internals { /** * streambuf adapter over csv::string_view with no data copy. * * The underlying memory must remain valid and immutable for the entire * lifetime of this stream object. */ class StringViewStreamBuf : public std::streambuf { public: explicit StringViewStreamBuf(csv::string_view view) { char* begin = const_cast(view.data()); char* end = begin + view.size(); this->setg(begin, begin, end); } protected: std::streamsize xsgetn(char* s, std::streamsize count) override { const std::streamsize avail = this->egptr() - this->gptr(); const std::streamsize n = std::min(avail, count); if (n > 0) { std::memcpy(s, this->gptr(), static_cast(n)); this->gbump(static_cast(n)); } return n; } pos_type seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode which = std::ios_base::in) override { if (!(which & std::ios_base::in)) { return pos_type(off_type(-1)); } const auto begin = this->eback(); const auto curr = this->gptr(); const auto end = this->egptr(); off_type base = 0; if (dir == std::ios_base::beg) { base = 0; } else if (dir == std::ios_base::cur) { base = static_cast(curr - begin); } else if (dir == std::ios_base::end) { base = static_cast(end - begin); } else { return pos_type(off_type(-1)); } const off_type next = base + off; const off_type size = static_cast(end - begin); if (next < 0 || next > size) { return pos_type(off_type(-1)); } this->setg(begin, begin + next, end); return pos_type(next); } pos_type seekpos(pos_type pos, std::ios_base::openmode which = std::ios_base::in) override { return this->seekoff(off_type(pos), std::ios_base::beg, which); } }; /** * Lightweight istream over csv::string_view with zero copy. * * WARNING: The caller is responsible for ensuring the backing memory * outlives this stream and any CSVReader parsing it. */ class StringViewStream : public std::istream { public: explicit StringViewStream(csv::string_view view) : std::istream(nullptr), _buf(view) { this->rdbuf(&_buf); } StringViewStream(const StringViewStream&) = delete; StringViewStream(StringViewStream&&) = delete; StringViewStream& operator=(const StringViewStream&) = delete; StringViewStream& operator=(StringViewStream&&) = delete; private: StringViewStreamBuf _buf; }; } } #include #include #include namespace csv { /** Returned by get_file_info() */ struct CSVFileInfo { std::string filename; /**< Filename */ std::vector col_names; /**< CSV column names */ char delim; /**< Delimiting character */ size_t n_rows; /**< Number of rows in a file */ size_t n_cols; /**< Number of columns in a CSV */ }; /** @name Shorthand Parsing Functions * @brief Convenience functions for parsing small strings */ ///@{ CSVReader operator ""_csv(const char*, size_t); CSVReader operator ""_csv_no_header(const char*, size_t); /** Parse CSV from a string view, copying the input into an owned buffer. * * Safe for any string_view regardless of the caller's ownership of the * underlying memory. */ CSVReader parse(csv::string_view in, CSVFormat format = CSVFormat::guess_csv()); /** Parse CSV from an in-memory view with zero copy. * * WARNING: Non-owning path. The caller must ensure `in`'s backing memory * remains valid and immutable while the reader may request additional rows * from the source stream. * * Already materialized CSVRows remain safe because parsed chunk data is * owned by RawCSVData, so make sure you grab all the CSVRows you need * before the underlying string is destroyed. */ CSVReader parse_unsafe(csv::string_view in, CSVFormat format = CSVFormat::guess_csv()); CSVReader parse_no_header(csv::string_view in); ///@} /** @name Utility Functions */ ///@{ std::unordered_map csv_data_types(const std::string&); CSVFileInfo get_file_info(const std::string& filename); int get_col_pos(csv::string_view filename, csv::string_view col_name, const CSVFormat& format = CSVFormat::guess_csv()); ///@} } /** @file * A standalone header file for writing delimiter-separated files */ #include #include #include #ifdef CSV_HAS_CXX20 #include #endif #include #include #include #include #include namespace csv { namespace internals { static int DECIMAL_PLACES = 5; /** * Calculate the absolute value of a number */ template inline T csv_abs(T x) { return abs(x); } template<> inline int csv_abs(int x) { return abs(x); } template<> inline long int csv_abs(long int x) { return labs(x); } template<> inline long long int csv_abs(long long int x) { return llabs(x); } template<> inline float csv_abs(float x) { return fabsf(x); } template<> inline double csv_abs(double x) { return fabs(x); } template<> inline long double csv_abs(long double x) { return fabsl(x); } /** * Calculate the number of digits in a number */ template< typename T, csv::enable_if_t::value, int> = 0 > int num_digits(T x) { x = csv_abs(x); int digits = 0; while (x >= 1) { x /= 10; digits++; } return digits; } /** to_string() for unsigned integers */ template::value, int> = 0> inline std::string to_string(T value) { std::string digits_reverse = ""; if (value == 0) return "0"; while (value > 0) { digits_reverse += (char)('0' + (value % 10)); value /= 10; } return std::string(digits_reverse.rbegin(), digits_reverse.rend()); } /** to_string() for signed integers */ template< typename T, csv::enable_if_t::value && std::is_signed::value, int> = 0 > inline std::string to_string(T value) { if (value >= 0) return to_string((size_t)value); return "-" + to_string((size_t)(value * -1)); } /** to_string() for floating point numbers */ template< typename T, csv::enable_if_t::value, int> = 0 > inline std::string to_string(T value) { #ifdef __clang__ return std::to_string(value); #else // TODO: Figure out why the below code doesn't work on clang std::string result = ""; T integral_part; T fractional_part = std::abs(std::modf(value, &integral_part)); integral_part = std::abs(integral_part); // Integral part if (value < 0) result = "-"; if (integral_part == 0) { result += "0"; } else { for (int n_digits = num_digits(integral_part); n_digits > 0; n_digits --) { int digit = (int)(std::fmod(integral_part, pow10(n_digits)) / pow10(n_digits - 1)); result += (char)('0' + digit); } } // Decimal part result += "."; if (fractional_part > 0) { fractional_part *= (T)(pow10(DECIMAL_PLACES)); for (int n_digits = DECIMAL_PLACES; n_digits > 0; n_digits--) { int digit = (int)(std::fmod(fractional_part, pow10(n_digits)) / pow10(n_digits - 1)); result += (char)('0' + digit); } } else { result += "0"; } return result; #endif } } /** Sets how many places after the decimal will be written for floating point numbers * * @param precision Number of decimal places */ #ifndef __clang__ inline static void set_decimal_places(int precision) { internals::DECIMAL_PLACES = precision; } #endif namespace internals { /** SFINAE trait: detects if a type is iterable (has std::begin/end). */ template struct is_iterable : std::false_type {}; template struct is_iterable::type> { private: template static auto test(int) -> decltype( std::begin(std::declval()), std::end(std::declval()), std::true_type{} ); template static std::false_type test(...); public: static constexpr bool value = decltype(test(0))::value; }; /** SFINAE trait: detects if a type is a std::tuple. */ template struct is_tuple : std::false_type {}; template struct is_tuple::type> { private: template static auto test(int) -> decltype(std::tuple_size::value, std::true_type{}); template static std::false_type test(...); public: static constexpr bool value = decltype(test(0))::value; }; } /** @name CSV Writing */ ///@{ /** * Class for writing delimiter separated values files * * To write formatted strings, one should * -# Initialize a DelimWriter with respect to some output stream * -# Call write_row() on std::vectors of unformatted text * * @tparam OutputStream The output stream, e.g. `std::ofstream`, `std::stringstream` * @tparam Delim The delimiter character * @tparam Quote The quote character * @tparam Flush True: flush after every writing function, * false: you need to flush explicitly if needed. * In both cases the destructor will flush. * * @par Hint * Use the aliases csv::CSVWriter to write CSV * formatted strings and csv::TSVWriter * to write tab separated strings * * @par Example w/ std::vector, std::deque, std::list * @snippet test_write_csv.cpp CSV Writer Example * * @par Example w/ std::tuple * @snippet test_write_csv.cpp CSV Writer Tuple Example */ template class DelimWriter { public: /** Construct a DelimWriter over the specified output stream * * @param _out Stream to write to * @param _quote_minimal Limit field quoting to only when necessary */ DelimWriter(OutputStream& _out, bool _quote_minimal = true) : out(&_out), quote_minimal(_quote_minimal) {} /** Construct a DelimWriter over the file * * @param[out] filename File to write to */ template::value, int> = 0> DelimWriter(const std::string& filename, bool _quote_minimal = true) : owned_out(new std::ofstream(filename, std::ios::out)), out(owned_out.get()), quote_minimal(_quote_minimal) { if (!owned_out->is_open()) throw std::runtime_error("Failed to open file for writing: " + filename); } DelimWriter(const DelimWriter&) = delete; DelimWriter& operator=(const DelimWriter&) = delete; DelimWriter(DelimWriter&& other) noexcept : owned_out(std::move(other.owned_out)), out(other.out), quote_minimal(other.quote_minimal) { if (owned_out) { out = owned_out.get(); } other.out = nullptr; other.quote_minimal = true; } DelimWriter& operator=(DelimWriter&& other) noexcept { if (this == &other) return *this; owned_out = std::move(other.owned_out); out = other.out; quote_minimal = other.quote_minimal; if (owned_out) { out = owned_out.get(); } other.out = nullptr; other.quote_minimal = true; return *this; } /** Destructor will flush remaining data * */ ~DelimWriter() { if (out) out->flush(); } /** Write a C-style array of strings as one delimited row. * * @tparam T Element type (typically std::string or csv::string_view) * @tparam N Array size (deduced) * @param record Array of strings * @return The current DelimWriter instance */ template DelimWriter& operator<<(const T (&record)[N]) { write_range_impl(record); return *this; } /** Write a std::array of strings as one delimited row. * * @tparam T Element type (typically std::string or csv::string_view) * @tparam N Array size (deduced) * @param record std::array of strings * @return The current DelimWriter instance */ template DelimWriter& operator<<(const std::array& record) { write_range_impl(record); return *this; } #ifdef CSV_HAS_CXX20 /** Write a range of string-like fields as one delimited row. * * Accepts any input_range whose elements are convertible to csv::string_view. * This includes std::vector, std::vector, * std::array, C++20 views, etc. */ template DelimWriter& operator<<(Range&& container) requires std::ranges::input_range && std::convertible_to, csv::string_view> { write_range_impl(container); return *this; } #else /** Write a range of string-like fields as one delimited row. * * Accepts any input_range whose elements are convertible to csv::string_view. * This includes std::vector, std::vector, * std::array, C++20 views, etc. */ template typename std::enable_if< internals::is_iterable::value && !internals::is_tuple::value && !std::is_same::value && !std::is_same::value, DelimWriter& >::type operator<<(const Range& record) { write_range_impl(record); return *this; } #endif /** @copydoc operator<< */ template DelimWriter& operator<<(const std::tuple& record) { this->write_tuple<0, T...>(record); return *this; } /** Flushes the written data * */ void flush() { out->flush(); } private: /** Helper to write a range of values, handling first element undelimited, * rest prefixed with delimiter. Inlines aggressively across both C++20 and * C++11 operator<< entry points. */ template inline void write_range_impl(const Range& record) { auto it = std::begin(record); auto end = std::end(record); if (it != end) { (*out) << csv_escape(*it); ++it; } for (; it != end; ++it) { (*out) << Delim << csv_escape(*it); } end_out(); } template< typename T, csv::enable_if_t< !std::is_convertible::value && !std::is_convertible::value , int> = 0 > std::string csv_escape(T in) { return internals::to_string(in); } template< typename T, csv::enable_if_t< std::is_convertible::value || std::is_convertible::value , int> = 0 > std::string csv_escape(T in) { IF_CONSTEXPR(std::is_convertible::value) { return _csv_escape(in); } else { return _csv_escape(std::string(in)); } } std::string _csv_escape(csv::string_view in) { /** Format a string to be RFC 4180-compliant * @param[in] in String to be CSV-formatted * @param[out] quote_minimal Only quote fields if necessary. * If False, everything is quoted. */ // Do we need a quote escape bool quote_escape = false; for (auto ch : in) { if (ch == Quote || ch == Delim || ch == '\r' || ch == '\n') { quote_escape = true; break; } } if (!quote_escape) { if (quote_minimal) return std::string(in); else { std::string ret(1, Quote); ret += in.data(); ret += Quote; return ret; } } // Start initial quote escape sequence std::string ret(1, Quote); for (auto ch: in) { if (ch == Quote) ret += std::string(2, Quote); else ret += ch; } // Finish off quote escape ret += Quote; return ret; } /** Recurisve template for writing std::tuples */ template typename std::enable_if::type write_tuple(const std::tuple& record) { (*out) << csv_escape(std::get(record)); #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4127) #endif IF_CONSTEXPR (Index + 1 < sizeof...(T)) (*out) << Delim; #ifdef _MSC_VER #pragma warning(pop) #endif this->write_tuple(record); } /** Base case for writing std::tuples */ template typename std::enable_if::type write_tuple(const std::tuple& record) { (void)record; end_out(); } /** Ends a line in 'out' and flushes, if Flush is true.*/ void end_out() { (*out) << '\n'; IF_CONSTEXPR(Flush) out->flush(); } /** * An owned output stream, if the writer owns it. * May be null if the writer does not own its output stream, i.e. * if it was initialized with an output stream reference instead of a filename. */ std::unique_ptr owned_out; /** Pointer to the output stream (which may or may not be owned by this writer). */ OutputStream* out; bool quote_minimal; }; /** An alias for csv::DelimWriter for writing standard CSV files * * @sa csv::DelimWriter::operator<<() * * @note Use `csv::make_csv_writer()` to in instatiate this class over * an actual output stream. */ template using CSVWriter = DelimWriter; /** Class for writing tab-separated values files * * @sa csv::DelimWriter::write_row() * @sa csv::DelimWriter::operator<<() * * @note Use `csv::make_tsv_writer()` to in instatiate this class over * an actual output stream. */ template using TSVWriter = DelimWriter; /** Return a csv::CSVWriter over the output stream */ template inline CSVWriter make_csv_writer(OutputStream& out, bool quote_minimal=true) { return CSVWriter(out, quote_minimal); } /** Return a buffered csv::CSVWriter over the output stream (does not auto flush) */ template inline CSVWriter make_csv_writer_buffered(OutputStream& out, bool quote_minimal=true) { return CSVWriter(out, quote_minimal); } /** Return a csv::TSVWriter over the output stream */ template inline TSVWriter make_tsv_writer(OutputStream& out, bool quote_minimal=true) { return TSVWriter(out, quote_minimal); } /** Return a buffered csv::TSVWriter over the output stream (does not auto flush) */ template inline TSVWriter make_tsv_writer_buffered(OutputStream& out, bool quote_minimal=true) { return TSVWriter(out, quote_minimal); } ///@} } /** @file * Fast CSVWriter overloads for CSVRow and DataFrame. */ #ifdef CSV_HAS_CXX20 namespace csv { template DelimWriter& operator<< (DelimWriter& writer, const CSVRow& row) { return writer << row.to_sv_range(); } /** Overload for writing a DataFrameRow (respects sparse overlay edits). */ template DelimWriter& operator<< (DelimWriter& writer, const DataFrameRow& row) { return writer << row.to_sv_range(); } } #endif /** @file * Defines an object used to store CSV format settings */ #include #include namespace csv { CSV_INLINE CSVFormat& CSVFormat::delimiter(char delim) { this->possible_delimiters = { delim }; this->assert_no_char_overlap(); return *this; } CSV_INLINE CSVFormat& CSVFormat::delimiter(const std::vector & delim) { this->possible_delimiters = delim; this->assert_no_char_overlap(); return *this; } CSV_INLINE CSVFormat& CSVFormat::quote(char quote) { this->no_quote = false; this->quote_char = quote; this->assert_no_char_overlap(); return *this; } CSV_INLINE CSVFormat& CSVFormat::trim(const std::vector & chars) { this->trim_chars = chars; this->assert_no_char_overlap(); return *this; } CSV_INLINE CSVFormat& CSVFormat::column_names(const std::vector& names) { this->col_names = names; this->header = -1; return *this; } CSV_INLINE CSVFormat& CSVFormat::header_row(int row) { if (row < 0) this->variable_column_policy = VariableColumnPolicy::KEEP; this->header = row; this->col_names = {}; return *this; } CSV_INLINE CSVFormat& CSVFormat::chunk_size(size_t size) { if (size < internals::ITERATION_CHUNK_SIZE) { throw std::invalid_argument( "Chunk size must be at least " + std::to_string(internals::ITERATION_CHUNK_SIZE) + " bytes (10MB). Provided: " + std::to_string(size) ); } this->_chunk_size = size; return *this; } CSV_INLINE void CSVFormat::assert_no_char_overlap() { auto delims = std::set( this->possible_delimiters.begin(), this->possible_delimiters.end()), trims = std::set( this->trim_chars.begin(), this->trim_chars.end()); // Stores intersection of possible delimiters and trim characters std::vector intersection = {}; // Find which characters overlap, if any std::set_intersection( delims.begin(), delims.end(), trims.begin(), trims.end(), std::back_inserter(intersection)); // Make sure quote character is not contained in possible delimiters // or whitespace characters if (delims.find(this->quote_char) != delims.end() || trims.find(this->quote_char) != trims.end()) { intersection.push_back(this->quote_char); } if (!intersection.empty()) { std::string err_msg = "There should be no overlap between the quote character, " "the set of possible delimiters " "and the set of whitespace characters. Offending characters: "; // Create a pretty error message with the list of overlapping // characters for (size_t i = 0; i < intersection.size(); i++) { err_msg += "'"; err_msg += intersection[i]; err_msg += "'"; if (i + 1 < intersection.size()) err_msg += ", "; } throw std::runtime_error(err_msg + '.'); } } } /** @file * Defines an input iterator for csv::CSVReader */ namespace csv { /** Return an iterator to the first row in the reader */ CSV_INLINE CSVReader::iterator CSVReader::begin() { CSVRow row; if (!this->read_row(row)) { return this->end(); } return CSVReader::iterator(this, std::move(row)); } /** A placeholder for the imaginary past the end row in a CSV. * Attempting to deference this will lead to bad things. */ CSV_INLINE CSV_CONST CSVReader::iterator CSVReader::end() const noexcept { return CSVReader::iterator(); } CSV_INLINE CSVReader::iterator::iterator(CSVReader* _daddy, CSVRow&& _row) : daddy(_daddy) { row = std::move(_row); } /** Advance the iterator by one row. If this CSVReader has an * associated file, then the iterator will lazily pull more data from * that file until the end of file is reached. * * @note This iterator does **not** block the thread responsible for parsing CSV. * */ CSV_INLINE CSVReader::iterator& CSVReader::iterator::operator++() { if (!daddy->read_row(this->row)) { this->daddy = nullptr; // this == end() } return *this; } /** Post-increment iterator */ CSV_INLINE CSVReader::iterator CSVReader::iterator::operator++(int) { auto temp = *this; if (!daddy->read_row(this->row)) { this->daddy = nullptr; // this == end() } return temp; } } /** @file * Implements JSON serialization abilities */ namespace csv { /* The implementations for json_extra_space() and json_escape_string() were modified from source code for JSON for Modern C++. The respective license is below: The code is licensed under the [MIT License](http://opensource.org/licenses/MIT): Copyright © 2013-2015 Niels Lohmann. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ namespace internals { /*! @brief calculates the extra space to escape a JSON string @param[in] s the string to escape @return the number of characters required to escape string @a s @complexity Linear in the length of string @a s. */ static std::size_t json_extra_space(csv::string_view& s) noexcept { std::size_t result = 0; for (const auto& c : s) { switch (c) { case '"': case '\\': case '\b': case '\f': case '\n': case '\r': case '\t': { // from c (1 byte) to \x (2 bytes) result += 1; break; } default: { if (c >= 0x00 && c <= 0x1f) { // from c (1 byte) to \uxxxx (6 bytes) result += 5; } break; } } } return result; } CSV_INLINE std::string json_escape_string(csv::string_view s) noexcept { const auto space = json_extra_space(s); if (space == 0) { return std::string(s); } // create a result string of necessary size size_t result_size = s.size() + space; std::string result(result_size, '\\'); std::size_t pos = 0; for (const auto& c : s) { switch (c) { // quotation mark (0x22) case '"': { result[pos + 1] = '"'; pos += 2; break; } // reverse solidus (0x5c) case '\\': { // nothing to change pos += 2; break; } // backspace (0x08) case '\b': { result[pos + 1] = 'b'; pos += 2; break; } // formfeed (0x0c) case '\f': { result[pos + 1] = 'f'; pos += 2; break; } // newline (0x0a) case '\n': { result[pos + 1] = 'n'; pos += 2; break; } // carriage return (0x0d) case '\r': { result[pos + 1] = 'r'; pos += 2; break; } // horizontal tab (0x09) case '\t': { result[pos + 1] = 't'; pos += 2; break; } default: { if (c >= 0x00 && c <= 0x1f) { // print character c as \uxxxx snprintf(&result[pos + 1], result_size - pos - 1, "u%04x", int(c)); pos += 6; // overwrite trailing null character result[pos] = '\\'; } else { // all other characters are added as-is result[pos++] = c; } break; } } } return result; } } /** Convert a CSV row to a JSON object, i.e. * `{"col1":"value1","col2":"value2"}` * * @note All strings are properly escaped. Numeric values are not quoted. * @param[in] subset A subset of columns to contain in the JSON. * Leave empty for original columns. */ CSV_INLINE std::string CSVRow::to_json(const std::vector& subset) const { std::vector col_names = subset; if (subset.empty()) { col_names = this->data ? this->get_col_names() : std::vector({}); } const size_t _n_cols = col_names.size(); std::string ret = "{"; for (size_t i = 0; i < _n_cols; i++) { auto& col = col_names[i]; auto field = this->operator[](col); // TODO: Possible performance enhancements by caching escaped column names ret += '"' + internals::json_escape_string(col) + "\":"; // Add quotes around strings but not numbers if (field.is_num()) ret += internals::json_escape_string(field.get()); else ret += '"' + internals::json_escape_string(field.get()) + '"'; // Do not add comma after last string if (i + 1 < _n_cols) ret += ','; } ret += '}'; return ret; } /** Convert a CSV row to a JSON array, i.e. * `["value1","value2",...]` * * @note All strings are properly escaped. Numeric values are not quoted. * @param[in] subset A subset of columns to contain in the JSON. * Leave empty for all columns. */ CSV_INLINE std::string CSVRow::to_json_array(const std::vector& subset) const { std::vector col_names = subset; if (subset.empty()) col_names = this->data ? this->get_col_names() : std::vector({}); const size_t _n_cols = col_names.size(); std::string ret = "["; for (size_t i = 0; i < _n_cols; i++) { auto field = this->operator[](col_names[i]); // Add quotes around strings but not numbers if (field.is_num()) ret += internals::json_escape_string(field.get()); else ret += '"' + internals::json_escape_string(field.get()) + '"'; // Do not add comma after last string if (i + 1 < _n_cols) ret += ','; } ret += ']'; return ret; } } #include #include #include namespace csv { /** Shorthand function for parsing an in-memory CSV string. * * Copies the input into an owned stringstream, so the caller's backing * memory may be freed immediately after this call returns. * * @return A collection of CSVRow objects * * @par Example * @snippet tests/test_read_csv.cpp Parse Example */ CSV_INLINE CSVReader parse(csv::string_view in, CSVFormat format) { std::unique_ptr ss(new std::stringstream(std::string(in))); return CSVReader(std::move(ss), format); } /** Parse CSV from an in-memory view with zero copy. * * Creates a non-owning stream adapter over the provided string_view. * The caller is responsible for keeping backing memory valid and immutable * while CSVReader may request additional rows. * * Already materialized CSVRows remain safe because parsed chunk data is * owned by RawCSVData. * * @return A collection of CSVRow objects */ CSV_INLINE CSVReader parse_unsafe(csv::string_view in, CSVFormat format) { std::unique_ptr stream(new internals::StringViewStream(in)); return CSVReader(std::move(stream), format); } /** Parses a CSV string with no headers * * @return A collection of CSVRow objects */ CSV_INLINE CSVReader parse_no_header(csv::string_view in) { CSVFormat format; format.header_row(-1); return parse(in, format); } /** Parse a RFC 4180 CSV string, returning a collection * of CSVRow objects. * * String literals have static storage duration, so the zero-copy path is * safe here. * * @par Example * @snippet tests/test_read_csv.cpp Escaped Comma * */ CSV_INLINE CSVReader operator ""_csv(const char* in, size_t n) { return parse_unsafe(csv::string_view(in, n)); } /** A shorthand for csv::parse_no_header(). * * String literals have static storage duration, so the zero-copy path is * safe here. */ CSV_INLINE CSVReader operator ""_csv_no_header(const char* in, size_t n) { CSVFormat format; format.header_row(-1); return parse_unsafe(csv::string_view(in, n), format); } /** * Find the position of a column in a CSV file or CSV_NOT_FOUND otherwise * * @param[in] filename Path to CSV file * @param[in] col_name Column whose position we should resolve * @param[in] format Format of the CSV file */ CSV_INLINE int get_col_pos( csv::string_view filename, csv::string_view col_name, const CSVFormat& format) { CSVReader reader(filename, format); return reader.index_of(col_name); } /** Get basic information about a CSV file * @include programs/csv_info.cpp */ CSV_INLINE CSVFileInfo get_file_info(const std::string& filename) { CSVReader reader(filename); CSVFormat format = reader.get_format(); for (auto it = reader.begin(); it != reader.end(); ++it); CSVFileInfo info = { filename, reader.get_col_names(), format.get_delim(), reader.n_rows(), reader.get_col_names().size() }; return info; } } /** @file * @brief Defines functionality needed for basic CSV parsing */ namespace csv { namespace internals { CSV_INLINE std::string format_row(const std::vector& row, csv::string_view delim) { /** Print a CSV row */ std::stringstream ret; for (size_t i = 0; i < row.size(); i++) { ret << row[i]; if (i + 1 < row.size()) ret << delim; else ret << '\n'; } ret.flush(); return ret.str(); } /** Return a CSV's column names * * @param[in] filename Path to CSV file * @param[in] format Format of the CSV file * */ CSV_INLINE std::vector _get_col_names(csv::string_view head, CSVFormat format) { // Parse the CSV auto trim_chars = format.get_trim_chars(); std::stringstream source(head.data()); RowCollection rows; StreamParser parser(source, format); parser.set_output(rows); parser.next(); return CSVRow(std::move(rows[format.get_header()])); } CSV_INLINE GuessScore calculate_score(csv::string_view head, const CSVFormat& format) { // Frequency counter of row length std::unordered_map row_tally = { { 0, 0 } }; // Map row lengths to row num where they first occurred std::unordered_map row_when = { { 0, 0 } }; // Parse the CSV std::stringstream source(head.data()); RowCollection rows; StreamParser parser(source, format); parser.set_output(rows); parser.next(); for (size_t i = 0; i < rows.size(); i++) { auto& row = rows[i]; // Ignore zero-length rows if (row.size() > 0) { if (row_tally.find(row.size()) != row_tally.end()) { row_tally[row.size()]++; } else { row_tally[row.size()] = 1; row_when[row.size()] = i; } } } double final_score = 0; size_t header_row = 0; size_t mode_row_length = 0; // Final score is equal to the largest // row size times rows of that size for (auto& pair : row_tally) { auto row_size = pair.first; auto row_count = pair.second; double score = (double)(row_size * row_count); if (score > final_score) { final_score = score; mode_row_length = row_size; header_row = row_when[row_size]; } } // Heuristic: If first row has >= columns than mode, use it as header // This handles headers with optional columns, trailing delimiters, etc. // while still supporting CSVs with comment lines before the header size_t first_row_length = rows.size() > 0 ? rows[0].size() : 0; if (first_row_length >= mode_row_length && first_row_length > 0) { header_row = 0; } return { final_score, header_row }; } /** Guess the delimiter used by a delimiter-separated values file */ CSV_INLINE CSVGuessResult _guess_format(csv::string_view head, const std::vector& delims) { /** For each delimiter, find out which row length was most common (mode). * The delimiter with the highest score (row_length × count) wins. * * Header detection: If first row has >= columns than mode, use row 0. * Otherwise use the first row with the mode length. * * See csv::guess_format() public API documentation for detailed heuristic explanation. */ CSVFormat format; size_t max_score = 0, header = 0; char current_delim = delims[0]; for (char cand_delim : delims) { auto result = calculate_score(head, format.delimiter(cand_delim)); if ((size_t)result.score > max_score) { max_score = (size_t)result.score; current_delim = cand_delim; header = result.header; } } return { current_delim, (int)header }; } } /** Return a CSV's column names * * @param[in] filename Path to CSV file * @param[in] format Format of the CSV file * */ CSV_INLINE std::vector get_col_names(csv::string_view filename, CSVFormat format) { auto head = internals::get_csv_head(filename); /** Guess delimiter and header row */ if (format.guess_delim()) { auto guess_result = guess_format(filename, format.get_possible_delims()); format.delimiter(guess_result.delim).header_row(guess_result.header_row); } return internals::_get_col_names(head, format); } /** Guess the delimiter used by a delimiter-separated values file */ CSV_INLINE CSVGuessResult guess_format(csv::string_view filename, const std::vector& delims) { auto head = internals::get_csv_head(filename); return internals::_guess_format(head, delims); } /** Reads an arbitrarily large CSV file using memory-mapped IO. * * **Details:** Reads the first block of a CSV file synchronously to get information * such as column names and delimiting character. * * @param[in] filename Path to CSV file * @param[in] format Format of the CSV file * * \snippet tests/test_read_csv.cpp CSVField Example * */ CSV_INLINE CSVReader::CSVReader(csv::string_view filename, CSVFormat format) : _format(format) { #if defined(__EMSCRIPTEN__) this->owned_stream = std::unique_ptr( new std::ifstream(std::string(filename), std::ios::binary) ); if (!(*this->owned_stream)) { throw std::runtime_error("Cannot open file " + std::string(filename)); } this->init_from_stream(*this->owned_stream, format); #else auto head = internals::get_csv_head(filename); using Parser = internals::MmapParser; // Apply chunk size from format before any reading occurs this->_chunk_size = format.get_chunk_size(); /** Guess delimiter and header row */ if (format.guess_delim()) { auto guess_result = internals::_guess_format(head, format.possible_delimiters); format.delimiter(guess_result.delim); // Only override header if user hasn't explicitly called no_header() // Note: column_names() also sets header=-1, but it populates col_names, // so we can distinguish: no_header() means header=-1 && col_names.empty() if (format.header != -1 || !format.col_names.empty()) { format.header = guess_result.header_row; } this->_format = format; } if (!format.col_names.empty()) this->set_col_names(format.col_names); this->parser = std::unique_ptr(new Parser(filename, format, this->col_names)); // For C++11 this->initial_read(); #endif } /** Return the format of the original raw CSV */ CSV_INLINE CSVFormat CSVReader::get_format() const { CSVFormat new_format = this->_format; // Since users are normally not allowed to set // column names and header row simulatenously, // we will set the backing variables directly here new_format.col_names = this->col_names->get_col_names(); new_format.header = this->_format.header; return new_format; } /** Return the CSV's column names as a vector of strings. */ CSV_INLINE std::vector CSVReader::get_col_names() const { if (this->col_names) { return this->col_names->get_col_names(); } return std::vector(); } /** Return the index of the column name if found or * csv::CSV_NOT_FOUND otherwise. */ CSV_INLINE int CSVReader::index_of(csv::string_view col_name) const { return this->col_names->index_of(col_name); } CSV_INLINE void CSVReader::trim_header() { if (!this->header_trimmed) { for (int i = 0; i <= this->_format.header && !this->records->empty(); i++) { if (i == this->_format.header && this->col_names->empty()) { this->set_col_names(this->records->pop_front()); } else { this->records->pop_front(); } } this->header_trimmed = true; } } /** * @param[in] names Column names */ CSV_INLINE void CSVReader::set_col_names(const std::vector& names) { this->col_names->set_policy(this->_format.get_column_name_policy()); this->col_names->set_col_names(names); this->n_cols = names.size(); } /** * Read a chunk of CSV data. * * @note This method is meant to be run on its own thread. Only one `read_csv()` thread * should be active at a time. * * @param[in] bytes Number of bytes to read. * * @see CSVReader::read_csv_worker * @see CSVReader::read_row() */ CSV_INLINE bool CSVReader::read_csv(size_t bytes) { // WORKER THREAD FUNCTION: Runs asynchronously to read CSV chunks // // Threading model: // 1. notify_all() - signals read_row() that worker is active // 2. parser->next() - reads and parses bytes (10MB chunks) // 3. kill_all() - signals read_row() that worker is done // // Exception handling: Exceptions thrown here MUST propagate to the calling // thread via std::exception_ptr. Bug #282 fixed cases where exceptions were // swallowed, causing std::terminate() instead of proper error handling. // Tell read_row() to listen for CSV rows this->records->notify_all(); try { this->parser->set_output(*this->records); this->parser->next(bytes); if (!this->header_trimmed) { this->trim_header(); } } catch (...) { // Never allow exceptions to escape the worker thread, or std::terminate will be invoked. // Store the exception and rethrow from the consumer thread (read_row / iterator). this->set_read_csv_exception(std::current_exception()); } // Tell read_row() to stop waiting this->records->kill_all(); return true; } /** * Retrieve rows as CSVRow objects, returning true if more rows are available. * * @par Performance Notes * - Reads chunks of data that are csv::internals::ITERATION_CHUNK_SIZE bytes large at a time * - For performance details, read the documentation for CSVRow and CSVField. * * @param[out] row The variable where the parsed row will be stored * @see CSVRow, CSVField * * **Example:** * \snippet tests/test_read_csv.cpp CSVField Example * */ CSV_INLINE bool CSVReader::read_row(CSVRow &row) { while (true) { if (this->records->empty()) { #if CSV_ENABLE_THREADS if (this->records->is_waitable()) { // Reading thread is currently active => wait for it to populate records this->records->wait(); continue; } #endif // Reading thread is not active #if CSV_ENABLE_THREADS if (this->read_csv_worker.joinable()) this->read_csv_worker.join(); #endif // If the worker thread failed, rethrow the error here this->rethrow_read_csv_exception_if_any(); if (this->parser->eof()) // End of file and no more records return false; // Detect infinite loop: a previous read was requested but records are still empty. // This fires when a single row spans more than 2 × _chunk_size bytes: // - chunk N fills without finding '\n' → _read_requested set to true // - chunk N+1 also fills without '\n' → guard fires here // Default _chunk_size is ITERATION_CHUNK_SIZE (10 MB), so the threshold is // rows > 20 MB. Use CSVFormat::chunk_size() to raise the limit. if (this->_read_requested && this->records->empty()) { throw std::runtime_error( "End of file not reached and no more records parsed. " "This likely indicates a CSV row larger than the chunk size of " + std::to_string(this->_chunk_size) + " bytes. " "Use CSVFormat::chunk_size() to increase the chunk size." ); } #if CSV_ENABLE_THREADS // Start another reading thread. // Mark as waitable before starting the thread to avoid a race where // read_row() observes is_waitable()==false immediately after thread creation. this->records->notify_all(); this->read_csv_worker = std::thread(&CSVReader::read_csv, this, this->_chunk_size); #else // Single-threaded mode parses synchronously on the caller thread. this->read_csv(this->_chunk_size); this->rethrow_read_csv_exception_if_any(); #endif this->_read_requested = true; continue; } else if (this->records->front().size() != this->n_cols && this->_format.variable_column_policy != VariableColumnPolicy::KEEP) { auto errored_row = this->records->pop_front(); if (this->_format.variable_column_policy == VariableColumnPolicy::THROW) { if (errored_row.size() < this->n_cols) throw std::runtime_error("Line too short " + internals::format_row(errored_row)); throw std::runtime_error("Line too long " + internals::format_row(errored_row)); } } else { row = this->records->pop_front(); this->_n_rows++; this->_read_requested = false; // Reset flag on successful read return true; } } return false; } } /** @file * Calculates statistics from CSV files */ #include #if CSV_ENABLE_THREADS #include #endif namespace csv { /** Calculate statistics for an arbitrarily large file. When this constructor * is called, CSVStat will process the entire file iteratively. Once finished, * methods like get_mean(), get_counts(), etc... can be used to retrieve statistics. */ CSV_INLINE CSVStat::CSVStat(csv::string_view filename, CSVFormat format) : reader(filename, format) { this->calc(); } /** Calculate statistics for a CSV stored in a std::stringstream */ CSV_INLINE CSVStat::CSVStat(std::stringstream& stream, CSVFormat format) : reader(stream, format) { this->calc(); } /** Return current means */ CSV_INLINE std::vector CSVStat::get_mean() const { std::vector ret; for (size_t i = 0; i < this->get_col_names().size(); i++) { ret.push_back(this->rolling_means[i]); } return ret; } /** Return current variances */ CSV_INLINE std::vector CSVStat::get_variance() const { std::vector ret; for (size_t i = 0; i < this->get_col_names().size(); i++) { ret.push_back(this->rolling_vars[i]/(this->n[i] - 1)); } return ret; } /** Return current mins */ CSV_INLINE std::vector CSVStat::get_mins() const { std::vector ret; for (size_t i = 0; i < this->get_col_names().size(); i++) { ret.push_back(this->mins[i]); } return ret; } /** Return current maxes */ CSV_INLINE std::vector CSVStat::get_maxes() const { std::vector ret; for (size_t i = 0; i < this->get_col_names().size(); i++) { ret.push_back(this->maxes[i]); } return ret; } /** Get counts for each column */ CSV_INLINE std::vector CSVStat::get_counts() const { std::vector ret; for (size_t i = 0; i < this->get_col_names().size(); i++) { ret.push_back(this->counts[i]); } return ret; } /** Get data type counts for each column */ CSV_INLINE std::vector CSVStat::get_dtypes() const { std::vector ret; for (size_t i = 0; i < this->get_col_names().size(); i++) { ret.push_back(this->dtypes[i]); } return ret; } CSV_INLINE void CSVStat::calc_chunk() { /** Only create stats counters the first time **/ if (dtypes.empty()) { /** Go through all records and calculate specified statistics */ for (size_t i = 0; i < this->get_col_names().size(); i++) { dtypes.push_back({}); counts.push_back({}); rolling_means.push_back(0); rolling_vars.push_back(0); mins.push_back(NAN); maxes.push_back(NAN); n.push_back(0); } } #if CSV_ENABLE_THREADS // Start threads std::vector pool; for (size_t i = 0; i < this->get_col_names().size(); i++) pool.push_back(std::thread(&CSVStat::calc_worker, this, i)); // Block until done for (auto& th : pool) th.join(); #else for (size_t i = 0; i < this->get_col_names().size(); i++) { this->calc_worker(i); } #endif this->records.clear(); } CSV_INLINE void CSVStat::calc() { constexpr size_t CALC_CHUNK_SIZE = 5000; for (auto& row : reader) { this->records.push_back(std::move(row)); /** Chunk rows */ if (this->records.size() == CALC_CHUNK_SIZE) { calc_chunk(); } } if (!this->records.empty()) { calc_chunk(); } } CSV_INLINE void CSVStat::calc_worker(const size_t &i) { /** Worker thread for CSVStat::calc() which calculates statistics for one column. * * @param[in] i Column index */ auto current_record = this->records.begin(); for (size_t processed = 0; current_record != this->records.end(); processed++) { if (current_record->size() == this->get_col_names().size()) { auto current_field = (*current_record)[i]; // Optimization: Don't count() if there's too many distinct values in the first 1000 rows if (processed < 1000 || this->counts[i].size() <= 500) this->count(current_field, i); this->dtype(current_field, i); // Numeric Stuff if (current_field.is_num()) { long double x_n = current_field.get(); // This actually calculates mean AND variance this->variance(x_n, i); this->min_max(x_n, i); } } else if (this->reader.get_format().get_variable_column_policy() == VariableColumnPolicy::THROW) { throw std::runtime_error("Line has different length than the others " + internals::format_row(*current_record)); } ++current_record; } } CSV_INLINE void CSVStat::dtype(CSVField& data, const size_t &i) { /** Given a record update the type counter * @param[in] record Data observation * @param[out] i The column index that should be updated */ auto type = data.type(); if (this->dtypes[i].find(type) != this->dtypes[i].end()) { // Increment count this->dtypes[i][type]++; } else { // Initialize count this->dtypes[i].insert(std::make_pair(type, 1)); } } CSV_INLINE void CSVStat::count(CSVField& data, const size_t &i) { /** Given a record update the frequency counter * @param[in] record Data observation * @param[out] i The column index that should be updated */ auto item = data.get(); if (this->counts[i].find(item) != this->counts[i].end()) { // Increment count this->counts[i][item]++; } else { // Initialize count this->counts[i].insert(std::make_pair(item, 1)); } } CSV_INLINE void CSVStat::min_max(const long double &x_n, const size_t &i) { /** Update current minimum and maximum * @param[in] x_n Data observation * @param[out] i The column index that should be updated */ if (std::isnan(this->mins[i])) this->mins[i] = x_n; if (std::isnan(this->maxes[i])) this->maxes[i] = x_n; if (x_n < this->mins[i]) this->mins[i] = x_n; else if (x_n > this->maxes[i]) this->maxes[i] = x_n; } CSV_INLINE void CSVStat::variance(const long double &x_n, const size_t &i) { /** Given a record update rolling mean and variance for all columns * using Welford's Algorithm * @param[in] x_n Data observation * @param[out] i The column index that should be updated */ long double& current_rolling_mean = this->rolling_means[i]; long double& current_rolling_var = this->rolling_vars[i]; long double& current_n = this->n[i]; long double delta; long double delta2; current_n++; if (current_n == 1) { current_rolling_mean = x_n; } else { delta = x_n - current_rolling_mean; current_rolling_mean += delta/current_n; delta2 = x_n - current_rolling_mean; current_rolling_var += delta*delta2; } } /** Useful for uploading CSV files to SQL databases. * * Return a data type for each column such that every value in a column can be * converted to the corresponding data type without data loss. * @param[in] filename The CSV file * * \return A mapping of column names to csv::DataType enums */ CSV_INLINE std::unordered_map csv_data_types(const std::string& filename) { CSVStat stat(filename); std::unordered_map csv_dtypes; auto col_names = stat.get_col_names(); auto temp = stat.get_dtypes(); for (size_t i = 0; i < stat.get_col_names().size(); i++) { auto& col = temp[i]; auto& col_name = col_names[i]; if (col[DataType::CSV_STRING]) csv_dtypes[col_name] = DataType::CSV_STRING; else if (col[DataType::CSV_INT64]) csv_dtypes[col_name] = DataType::CSV_INT64; else if (col[DataType::CSV_INT32]) csv_dtypes[col_name] = DataType::CSV_INT32; else if (col[DataType::CSV_INT16]) csv_dtypes[col_name] = DataType::CSV_INT16; else if (col[DataType::CSV_INT8]) csv_dtypes[col_name] = DataType::CSV_INT8; else csv_dtypes[col_name] = DataType::CSV_DOUBLE; } return csv_dtypes; } } /** @file * @brief Implementation of internal CSV data structures */ #include namespace csv { namespace internals { CSV_INLINE RawCSVField& RawCSVFieldList::operator[](size_t n) const { const size_t page_no = n / _single_buffer_capacity; const size_t buffer_idx = n % _single_buffer_capacity; return this->_owned_blocks[page_no].get()[buffer_idx]; } CSV_INLINE void RawCSVFieldList::allocate() { if (_back != nullptr) { _current_block++; } this->_owned_blocks.push_back(std::unique_ptr(new RawCSVField[_single_buffer_capacity])); _current_buffer_size = 0; _back = this->_owned_blocks.back().get(); } } } #include namespace csv { namespace internals { CSV_INLINE size_t get_file_size(csv::string_view filename) { std::ifstream infile(std::string(filename), std::ios::binary); if (!infile.is_open()) { throw std::runtime_error("Cannot open file " + std::string(filename)); } const auto start = infile.tellg(); infile.seekg(0, std::ios::end); const auto end = infile.tellg(); if (start < 0 || end < 0) { throw std::runtime_error("Cannot determine file size for " + std::string(filename)); } return static_cast(end - start); } CSV_INLINE std::string get_csv_head(csv::string_view filename) { return get_csv_head(filename, get_file_size(filename)); } CSV_INLINE std::string get_csv_head(csv::string_view filename, size_t file_size) { const size_t bytes = 500000; #if defined(__EMSCRIPTEN__) std::ifstream infile(std::string(filename), std::ios::binary); if (!infile.is_open()) { throw std::runtime_error("Cannot open file " + std::string(filename)); } const size_t length = std::min((size_t)file_size, bytes); std::string head(length, '\0'); infile.read(&head[0], (std::streamsize)length); head.resize((size_t)infile.gcount()); return head; #else std::error_code error; size_t length = std::min((size_t)file_size, bytes); auto mmap = mio::make_mmap_source(std::string(filename), 0, length, error); if (error) { throw std::runtime_error("Cannot open file " + std::string(filename)); } return std::string(mmap.begin(), mmap.end()); #endif } #ifdef _MSC_VER #pragma region IBasicCVParser #endif CSV_INLINE IBasicCSVParser::IBasicCSVParser( const CSVFormat& format, const ColNamesPtr& col_names ) : col_names_(col_names) { if (format.no_quote) { parse_flags_ = internals::make_parse_flags(format.get_delim()); } else { parse_flags_ = internals::make_parse_flags(format.get_delim(), format.quote_char); } // When no_quote, quote bytes are NOT_SPECIAL — use delimiter as safe dummy // so SIMD does not stop early on quote bytes and cause an infinite loop. const char eff_quote = format.no_quote ? format.get_delim() : format.quote_char; simd_sentinels_ = SentinelVecs(format.get_delim(), eff_quote); ws_flags_ = internals::make_ws_flags( format.trim_chars.data(), format.trim_chars.size() ); has_ws_trimming_ = !format.trim_chars.empty(); } CSV_INLINE void IBasicCSVParser::end_feed() { using internals::ParseFlags; bool empty_last_field = this->data_ptr_ && this->data_ptr_->_data && !this->data_ptr_->data.empty() && (parse_flag(this->data_ptr_->data.back()) == ParseFlags::DELIMITER || parse_flag(this->data_ptr_->data.back()) == ParseFlags::QUOTE); // Push field if (this->field_length_ > 0 || empty_last_field) { this->push_field(); } // Push row if (this->current_row_.size() > 0) this->push_row(); } CSV_INLINE void IBasicCSVParser::parse_field() noexcept { using internals::ParseFlags; auto& in = this->data_ptr_->data; if (field_start_ == UNINITIALIZED_FIELD) field_start_ = (int)(data_pos_ - current_row_start()); // Optimization: Since NOT_SPECIAL characters tend to occur in contiguous // sequences, use SIMD to skip long runs of them quickly. // find_next_non_special processes complete SIMD lanes and returns pos // unchanged for any tail shorter than one lane width. #if !defined(CSV_NO_SIMD) data_pos_ = find_next_non_special(in, data_pos_, this->simd_sentinels_); #endif // Scalar tail: handles remaining bytes after SIMD falls through, and // handles any byte that SIMD stopped at conservatively (e.g. a delimiter // inside a quoted field, which compound_parse_flag treats as NOT_SPECIAL). while (data_pos_ < in.size() && compound_parse_flag(in[data_pos_]) == ParseFlags::NOT_SPECIAL) data_pos_++; field_length_ = data_pos_ - (field_start_ + current_row_start()); // Whitespace trimming is deferred to get_field_impl() so callers that never // read field values (e.g. row counting) pay no trimming cost. } CSV_INLINE void IBasicCSVParser::push_field() { // Update fields_->emplace_back( field_start_ == UNINITIALIZED_FIELD ? 0 : (unsigned int)field_start_, field_length_, field_has_double_quote_ ); current_row_.row_length++; // Reset field state field_has_double_quote_ = false; field_start_ = UNINITIALIZED_FIELD; field_length_ = 0; } /** @return The number of characters parsed that belong to complete rows */ CSV_INLINE size_t IBasicCSVParser::parse() { using internals::ParseFlags; this->quote_escape_ = false; this->data_pos_ = 0; this->current_row_start() = 0; this->trim_utf8_bom(); auto& in = this->data_ptr_->data; while (this->data_pos_ < in.size()) { switch (compound_parse_flag(in[this->data_pos_])) { case ParseFlags::DELIMITER: this->push_field(); this->data_pos_++; break; case ParseFlags::NEWLINE: this->data_pos_++; // Catches CRLF (or LFLF, CRCRLF, or any other non-sensical combination of newlines) while (this->data_pos_ < in.size() && parse_flag(in[this->data_pos_]) == ParseFlags::NEWLINE) this->data_pos_++; // End of record -> Write record this->push_field(); this->push_row(); // Reset this->current_row_ = CSVRow(data_ptr_, this->data_pos_, fields_->size()); break; case ParseFlags::NOT_SPECIAL: this->parse_field(); break; case ParseFlags::QUOTE_ESCAPE_QUOTE: if (data_pos_ + 1 == in.size()) return this->current_row_start(); else if (data_pos_ + 1 < in.size()) { auto next_ch = parse_flag(in[data_pos_ + 1]); if (next_ch >= ParseFlags::DELIMITER) { quote_escape_ = false; data_pos_++; break; } else if (next_ch == ParseFlags::QUOTE) { // Case: Escaped quote data_pos_ += 2; this->field_length_ += 2; this->field_has_double_quote_ = true; break; } } // Case: Unescaped single quote => not strictly valid but we'll keep it this->field_length_++; data_pos_++; break; default: // Quote (currently not quote escaped) if (this->field_length_ == 0) { quote_escape_ = true; data_pos_++; if (field_start_ == UNINITIALIZED_FIELD && data_pos_ < in.size() && !ws_flag(in[data_pos_])) field_start_ = (int)(data_pos_ - current_row_start()); break; } // Case: Unescaped quote this->field_length_++; data_pos_++; break; } } return this->current_row_start(); } CSV_INLINE void IBasicCSVParser::push_row() { size_t row_len = fields_->size() - current_row_.fields_start; // Set row_length before pushing (immutable once created) current_row_.row_length = row_len; this->records_->push_back(std::move(current_row_)); } CSV_INLINE void IBasicCSVParser::reset_data_ptr() { this->data_ptr_ = std::make_shared(); this->data_ptr_->parse_flags = this->parse_flags_; this->data_ptr_->ws_flags = this->ws_flags_; this->data_ptr_->has_ws_trimming = this->has_ws_trimming_; this->data_ptr_->col_names = this->col_names_; this->fields_ = &(this->data_ptr_->fields); } CSV_INLINE void IBasicCSVParser::trim_utf8_bom() { auto& data = this->data_ptr_->data; if (!this->unicode_bom_scan_ && data.size() >= 3) { if (data[0] == '\xEF' && data[1] == '\xBB' && data[2] == '\xBF') { this->data_pos_ += 3; // Remove BOM from input string this->utf8_bom_ = true; } this->unicode_bom_scan_ = true; } } #ifdef _MSC_VER #pragma endregion #endif #ifdef _MSC_VER #pragma region Specializations #endif #if !defined(__EMSCRIPTEN__) CSV_INLINE void MmapParser::next(size_t bytes = ITERATION_CHUNK_SIZE) { // CRITICAL SECTION: Chunk Transition Logic // This function reads 10MB chunks and must correctly handle fields that span // chunk boundaries. The 'remainder' calculation below ensures partial fields // are preserved for the next chunk. // // Bug #280: Field corruption occurred here when chunk transitions incorrectly // split multi-byte characters or field boundaries. // Reset parser state this->field_start_ = UNINITIALIZED_FIELD; this->field_length_ = 0; this->reset_data_ptr(); // Create memory map const size_t offset = this->mmap_pos; const size_t remaining = (offset < this->source_size_) ? (this->source_size_ - offset) : 0; const size_t length = std::min(remaining, bytes); if (length == 0) { // No more data to read; mark EOF and end feed // (Prevent exception on empty mmap as reported by #267) this->eof_ = true; this->end_feed(); return; } std::error_code error; auto mmap = mio::make_mmap_source(this->_filename, offset, length, error); if (error) { std::string msg = "Memory mapping failed during CSV parsing: file='" + this->_filename + "' offset=" + std::to_string(offset) + " length=" + std::to_string(length); throw std::system_error(error, msg); } this->data_ptr_->_data = std::make_shared>(std::move(mmap)); this->mmap_pos += length; auto mmap_ptr = (mio::basic_mmap_source*)(this->data_ptr_->_data.get()); // Create string view this->data_ptr_->data = csv::string_view(mmap_ptr->data(), mmap_ptr->length()); // Parse this->current_row_ = CSVRow(this->data_ptr_); size_t remainder = this->parse(); if (this->mmap_pos == this->source_size_ || no_chunk()) { this->eof_ = true; this->end_feed(); } this->mmap_pos -= (length - remainder); } #endif #ifdef _MSC_VER #pragma endregion #endif } } /** @file * Defines the data type used for storing information about a CSV row */ #include #include namespace csv { namespace internals { CSV_INLINE csv::string_view get_trimmed(csv::string_view sv, const WhitespaceMap& ws_flags) noexcept { // Lazy trim only when requested size_t start = 0; while (start < sv.size() && ws_flags[sv[start] + CHAR_OFFSET]) { ++start; } size_t end = sv.size(); while (end > start && ws_flags[sv[end - 1] + CHAR_OFFSET]) { --end; } return sv.substr(start, end - start); } } /** Return a CSVField object corrsponding to the nth value in the row. * * @note This method performs bounds checking, and will throw an * `std::runtime_error` if n is invalid. * * @complexity * Constant, by calling csv::CSVRow::get_csv::string_view() * */ CSV_INLINE CSVField CSVRow::operator[](size_t n) const { return CSVField(this->get_field(n)); } /** Retrieve a value by its associated column name. If the column * specified can't be round, a runtime error is thrown. * * @complexity * Constant. This calls the other CSVRow::operator[]() after * converting column names into indices using a hash table. * * @param[in] col_name The column to look for */ CSV_INLINE CSVField CSVRow::operator[](const std::string& col_name) const { auto & col_names = this->data->col_names; auto col_pos = col_names->index_of(col_name); if (col_pos > -1) { return this->operator[](col_pos); } throw std::runtime_error("Can't find a column named " + col_name); } CSV_INLINE CSVRow::operator std::vector() const { std::vector ret; for (size_t i = 0; i < size(); i++) ret.push_back(std::string(this->get_field(i))); return ret; } /** Build a map from column names to values for a given row. */ CSV_INLINE std::unordered_map CSVRow::to_unordered_map() const { std::unordered_map row_map; row_map.reserve(this->size()); for (size_t i = 0; i < this->size(); i++) { auto col_name = (*this->data->col_names)[i]; row_map[col_name] = this->operator[](i).get(); } return row_map; } /** * Build a map from column names to values for a given row. * * @param[in] subset Vector of column names to include in the map. */ CSV_INLINE std::unordered_map CSVRow::to_unordered_map( const std::vector& subset ) const { std::unordered_map row_map; row_map.reserve(subset.size()); for (const auto& col_name : subset) row_map[col_name] = this->operator[](col_name).get(); return row_map; } CSV_INLINE csv::string_view CSVRow::get_field(size_t index) const { return this->get_field_impl(index, this->data); } CSV_INLINE csv::string_view CSVRow::get_field_safe(size_t index, internals::RawCSVDataPtr _data) const { return this->get_field_impl(index, _data); } CSV_INLINE bool CSVField::try_parse_decimal(long double& dVal, const char decimalSymbol) { // If field has already been parsed to empty, no need to do it aagin: if (this->_type == DataType::CSV_NULL) return false; // Not yet parsed or possibly parsed with other decimalSymbol if (this->_type == DataType::UNKNOWN || this->_type == DataType::CSV_STRING || this->_type == DataType::CSV_DOUBLE) this->_type = internals::data_type(this->sv, &this->value, decimalSymbol); // parse again // Integral types are not affected by decimalSymbol and need not be parsed again // Either we already had an integral type before, or we we just got any numeric type now. if (this->_type >= DataType::CSV_INT8 && this->_type <= DataType::CSV_DOUBLE) { dVal = this->value; return true; } // CSV_NULL or CSV_STRING, not numeric return false; } #ifdef _MSC_VER #pragma region CSVRow Iterator #endif /** Return an iterator pointing to the first field. */ CSV_INLINE CSVRow::iterator CSVRow::begin() const { return CSVRow::iterator(this, 0); } /** Return an iterator pointing to just after the end of the CSVRow. * * @warning Attempting to dereference the end iterator results * in dereferencing a null pointer. */ CSV_INLINE CSVRow::iterator CSVRow::end() const noexcept { return CSVRow::iterator(this, (int)this->size()); } CSV_INLINE CSVRow::reverse_iterator CSVRow::rbegin() const noexcept { return std::reverse_iterator(this->end()); } CSV_INLINE CSVRow::reverse_iterator CSVRow::rend() const { return std::reverse_iterator(this->begin()); } CSV_INLINE CSV_NON_NULL(2) CSVRow::iterator::iterator(const CSVRow* _reader, int _i) : daddy(_reader), data(_reader->data), i(_i) { if (_i < (int)this->daddy->size()) this->field = std::make_shared( CSVField(this->daddy->get_field_safe(_i, this->data))); else this->field = nullptr; } CSV_INLINE CSVRow::iterator::reference CSVRow::iterator::operator*() const { return *(this->field.get()); } CSV_INLINE CSVRow::iterator::pointer CSVRow::iterator::operator->() const { return this->field; } CSV_INLINE CSVRow::iterator& CSVRow::iterator::operator++() { // Pre-increment operator this->i++; if (this->i < (int)this->daddy->size()) this->field = std::make_shared( CSVField(this->daddy->get_field_safe(i, this->data))); else // Reached the end of row this->field = nullptr; return *this; } CSV_INLINE CSVRow::iterator CSVRow::iterator::operator++(int) { // Post-increment operator auto temp = *this; this->operator++(); return temp; } CSV_INLINE CSVRow::iterator& CSVRow::iterator::operator--() { // Pre-decrement operator this->i--; this->field = std::make_shared( CSVField(this->daddy->get_field_safe(this->i, this->data))); return *this; } CSV_INLINE CSVRow::iterator CSVRow::iterator::operator--(int) { // Post-decrement operator auto temp = *this; this->operator--(); return temp; } CSV_INLINE CSVRow::iterator CSVRow::iterator::operator+(difference_type n) const { // Allows for iterator arithmetic return CSVRow::iterator(this->daddy, i + (int)n); } CSV_INLINE CSVRow::iterator CSVRow::iterator::operator-(difference_type n) const { // Allows for iterator arithmetic return CSVRow::iterator::operator+(-n); } #ifdef _MSC_VER #pragma endregion CSVRow Iterator #endif } #include #include namespace csv { namespace internals { CSV_INLINE std::vector ColNames::get_col_names() const { return this->col_names; } CSV_INLINE void ColNames::set_col_names(const std::vector& cnames) { this->col_names = cnames; this->col_pos.clear(); for (size_t i = 0; i < cnames.size(); i++) { if (this->_policy == csv::ColumnNamePolicy::CASE_INSENSITIVE) { // For case-insensitive lookup, cache a lowercase version // of the column name in the map std::string lower(cnames[i]); std::transform(lower.begin(), lower.end(), lower.begin(), [](unsigned char c) { return static_cast(std::tolower(c)); }); this->col_pos[lower] = i; } else { this->col_pos[cnames[i]] = i; } } } CSV_INLINE int ColNames::index_of(csv::string_view col_name) const { if (this->_policy == csv::ColumnNamePolicy::CASE_INSENSITIVE) { std::string lower(col_name); std::transform(lower.begin(), lower.end(), lower.begin(), [](unsigned char c) { return static_cast(std::tolower(c)); }); auto pos = this->col_pos.find(lower); if (pos != this->col_pos.end()) return (int)pos->second; return CSV_NOT_FOUND; } auto pos = this->col_pos.find(col_name.data()); if (pos != this->col_pos.end()) return (int)pos->second; return CSV_NOT_FOUND; } CSV_INLINE void ColNames::set_policy(csv::ColumnNamePolicy policy) { this->_policy = policy; } CSV_INLINE size_t ColNames::size() const noexcept { return this->col_names.size(); } CSV_INLINE const std::string& ColNames::operator[](size_t i) const { if (i >= this->col_names.size()) throw std::out_of_range("Column index out of bounds."); return this->col_names[i]; } } } #endif ================================================ FILE: core/debug.cpp ================================================ #include "debug.h" static bool logToFile = false; static QString debugLogFilename; bool isLogToFileEnabled() { return logToFile; } void setLogToFile(bool enabled) { logToFile = enabled; } QString currentDebugLogFilename() { return debugLogFilename; } void setCurrentDebugLogFilename(const QString &filename) { debugLogFilename = filename; } void set_debug_level(DEBUG_LEVEL_TYPE type) { switch ( type ) { case LEVEL_DEBUG_MAX: QLoggingCategory::setFilterRules("*.debug=true \n" "*.function.parameters=true \n" "*.function.entered=true\n"); break; case LEVEL_DEBUG_FUNCTION_PARAMETERS: QLoggingCategory::setFilterRules("*.function.parameters=true \n" "*.function.entered=true\n"); break; case LEVEL_DEBUG_FUNCTION_CALLS: QLoggingCategory::setFilterRules("*.function.parameters=false\n"); break; case LEVEL_DEBUG_RUNTIME: QLoggingCategory::setFilterRules("*.function.*=false\n"); break; case LEVEL_PRODUCTION: default: QLoggingCategory::setFilterRules("*.debug=false\n"); } } ================================================ FILE: core/debug.h ================================================ #ifndef QLOG_CORE_DEBUG_LOG_H #define QLOG_CORE_DEBUG_LOG_H #include #include Q_DECLARE_LOGGING_CATEGORY(logGraphics) Q_DECLARE_LOGGING_CATEGORY(logPlugin) #define MODULE_IDENTIFICATION(m) static const char *mod_name = m; \ static const QLoggingCategory function_parameters(m".function.parameters"); \ static const QLoggingCategory runtime(m".runtime"); \ #define FCT_IDENTIFICATION QString logging_cat(mod_name); \ logging_cat.append(".function.entered"); \ QByteArray logging_cat_latin1 = logging_cat.toLatin1(); \ const char* category_name = logging_cat_latin1.isEmpty() ? "default_category" : logging_cat_latin1.constData(); \ QLoggingCategory log_category(category_name); \ qCDebug(log_category)<<"***" typedef enum debug_level { LEVEL_DEBUG_MAX, LEVEL_DEBUG_FUNCTION_PARAMETERS, LEVEL_DEBUG_FUNCTION_CALLS, LEVEL_DEBUG_RUNTIME, LEVEL_PRODUCTION } DEBUG_LEVEL_TYPE; void set_debug_level(DEBUG_LEVEL_TYPE); bool isLogToFileEnabled(); void setLogToFile(bool enabled); void closeDebugLogFile(); QString currentDebugLogFilename(); void setCurrentDebugLogFilename(const QString &filename); #endif // QLOG_CORE_DEBUG_LOG_H ================================================ FILE: core/main.cpp ================================================ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "debug.h" #include "Migration.h" #include "ui/MainWindow.h" #include "rig/Rig.h" #include "rotator/Rotator.h" #include "cwkey/CWKeyer.h" #include "AppGuard.h" #include "core/zonedetect.h" #include "ui/SplashScreen.h" #include "core/MembershipQE.h" #include "service/kstchat/KSTChat.h" #include "data/Data.h" #include "service/GenericCallbook.h" #include "core/LogDatabase.h" MODULE_IDENTIFICATION("qlog.core.main"); static QMutex debug_mutex; static QFile debugLogFile; static QTextStream debugLogStream; QTemporaryDir tempDir #ifdef QLOG_FLATPAK // hack: I don't know how to openn image file // in sandbox via QDesktop::openurl // therefore QLog creates a temp directory in home directory (home is allowed for flatpak) (QDir::homePath() + "/.qlogXXXXXX"); #else ; #endif static void setupTranslator(QApplication* app, const QString &lang, const QString &translationFile) { FCT_IDENTIFICATION; qCDebug(function_parameters) << lang << translationFile; QString localeLang = ( lang.isEmpty() ) ? QLocale::system().name() : lang; QTranslator* qtTranslator = new QTranslator(app); if ( qtTranslator->load("qt_" + localeLang, #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) QLibraryInfo::location(QLibraryInfo::TranslationsPath)) ) #else QLibraryInfo::path(QLibraryInfo::TranslationsPath)) ) #endif { app->installTranslator(qtTranslator); } // give translators the ability to dynamically load files. // first, try to load file from input parameter (if exsist) if ( !translationFile.isEmpty() ) { qCDebug(runtime) << "External translation file defined - trying to load it"; QTranslator* translator = new QTranslator(app); if ( translator->load(translationFile) ) { qCDebug(runtime) << "Loaded successfully" #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) << translator->filePath() #endif ; app->installTranslator(translator); return; } qWarning() << "External translation file not found"; translator->deleteLater(); } // searching in the following directories // Linux: // application_folder/i18n // "~/.local/share/hamradio/QLog/i18n", // "/usr/local/share/hamradion/QLog/i18n", // "/usr/share/hamradio/QLog/i18n" // // looking for filename // qlog.fr_ca.qm // qlog.fr_ca // qlog.fr.qm // qlog.fr // qlog.qm // qlog QStringList translationFolders; translationFolders << qApp->applicationDirPath() << QStandardPaths::standardLocations(QStandardPaths::AppLocalDataLocation); for ( const QString& folder : static_cast(translationFolders) ) { qCDebug(runtime) << "Looking for a translation in" << folder << QString("i18n%1qlog_%2").arg(QDir::separator(), localeLang); QTranslator* translator = new QTranslator(app); if ( translator->load(QStringLiteral("i18n%1qlog_%2").arg(QDir::separator(), localeLang), folder) ) { qCDebug(runtime) << "Loaded successfully" #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) << translator->filePath() #endif ; app->installTranslator(translator); return; } translator->deleteLater(); } // last attempt - build-in resources/i18n. qCDebug(runtime) << "Looking for a translation in QLog's resources"; QTranslator* translator = new QTranslator(app); if ( translator->load(":/i18n/qlog_" + localeLang) ) { qCDebug(runtime) << "Loaded successfully" #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) << translator->filePath() #endif ; app->installTranslator(translator); return; } translator->deleteLater(); qCDebug(runtime) << "Cannot find any translation file"; } static void createDataDirectory() { FCT_IDENTIFICATION; QDir dataDir(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)); qCDebug(runtime) << dataDir.path(); if (!dataDir.exists()) { dataDir.mkpath(dataDir.path()); } } static void startRigThread() { FCT_IDENTIFICATION; QThread* rigThread = new QThread; Rig* rig = Rig::instance(); rig->moveToThread(rigThread); QObject::connect(rigThread, SIGNAL(started()), rig, SLOT(start())); rigThread->start(); } static void startRotThread() { FCT_IDENTIFICATION; QThread* rotThread = new QThread; Rotator* rot = Rotator::instance(); rot->moveToThread(rotThread); QObject::connect(rotThread, SIGNAL(started()), rot, SLOT(start())); rotThread->start(); } static void startCWKeyerThread() { FCT_IDENTIFICATION; QThread* cwKeyerThread = new QThread; CWKeyer* cwKeyer = CWKeyer::instance(); cwKeyer->moveToThread(cwKeyerThread); QObject::connect(cwKeyerThread, SIGNAL(started()), cwKeyer, SLOT(start())); cwKeyerThread->start(); } void closeDebugLogFile() { QMutexLocker locker(&debug_mutex); if ( debugLogFile.isOpen() ) { debugLogStream.flush(); debugLogStream.setDevice(nullptr); debugLogFile.close(); } } static void debugMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) { QMutexLocker locker(&debug_mutex); if ( isLogToFileEnabled() && !debugLogFile.isOpen() ) { const QString filename = Data::debugFilename(); debugLogFile.setFileName(filename); if ( debugLogFile.open(QIODevice::WriteOnly | QIODevice::Text) ) { setCurrentDebugLogFilename(filename); debugLogStream.setDevice(&debugLogFile); debugLogStream << "App: " << QCoreApplication::applicationVersion() << "\n" #ifdef QLOG_FLATPAK << "Flatpak" << "\n" #endif << "QT: " << qVersion() << "\n" << "OS: " << QString("%1 %2 (%3)").arg(QSysInfo::prettyProductName(), QSysInfo::currentCpuArchitecture(), QGuiApplication::platformName() ) << "\n" << "SSL: " << QSslSocket::sslLibraryVersionString() << "\n\n"; } else { qWarning() << "Cannot open the file for log"; setLogToFile(false); } } const char *severity_string = nullptr; switch ( type ) { case QtDebugMsg: severity_string = "[DEBUG ]"; break; case QtInfoMsg: severity_string = "[INFO ]"; break; case QtWarningMsg: severity_string = "[WARNING ]"; break; case QtCriticalMsg: severity_string = "[CRITICAL]"; break; case QtFatalMsg: severity_string = "[FATAL ]"; break; default: severity_string = "[UNKNOWN ]"; } const QString &category = QString("[%1]").arg(context.category).leftJustified(50, ' '); const QString &logEntry = QString("%1 %2 [0x%3] %4 %5 [%6:%7:%8]\n") .arg(QTime::currentTime().toString("HH:mm:ss.zzz")) .arg(severity_string) .arg(QString::number(reinterpret_cast(QThread::currentThreadId()), 16)) .arg(category) .arg(msg) .arg(context.function ? context.function : "unknown") .arg(context.file ? context.file : "unknown") .arg(context.line); if ( isLogToFileEnabled() && debugLogFile.isOpen() ) { debugLogStream << logEntry; debugLogStream.flush(); } fprintf(stderr, "%s", logEntry.toUtf8().constData()); if ( type == QtFatalMsg ) { if (debugLogFile.isOpen()) debugLogFile.close(); abort(); } } #ifdef Q_OS_LINUX void wayland_hacks() { // due to QT's issue, Dock widget is not working (cannot be docked) under QT5, < ?6.7? on Linux // Therefore it is necessary to force set XCB (X11) const QByteArray &sessionType = qgetenv("XDG_SESSION_TYPE").toLower(); const QByteArray &disableXCBFallback = qgetenv("QLOG_DISABLE_XCB"); if ( sessionType.contains("wayland") && disableXCBFallback == QByteArray() ) { qInfo() << "Force XCB"; qputenv("QT_QPA_PLATFORM", "xcb"); } } #endif int main(int argc, char* argv[]) { #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); #endif #ifdef Q_OS_LINUX wayland_hacks(); #endif bool stylePresent = false; /* Style option is deleted in QApplication constructor. * Therefore test for the parameter presence has to be performed here */ for ( int i = 0; i < argc && !stylePresent; i++ ) { stylePresent = QString(argv[i]).contains("-style"); } QApplication app(argc, argv); app.setApplicationVersion(VERSION); QCommandLineParser parser; parser.setApplicationDescription(QCoreApplication::tr("QLog Help")); parser.addHelpOption(); parser.addVersionOption(); /* Undocumented param used for debugging. A developer can run QLog with * a specific namespace. This helps in cases where it is possible * to simultaneously run a test/develop and production versions of QLog. * * The parameter changes the Application name. It causes that all runtime * files, settings, passwords and DB file are created in a different directory/namespace. * * however, it remains necessary only one instance of QLog to run at a time. * More Notes below (AppGuard Comment). * * NOTE: This is not a preparation for the ability to run separate databases. * It's just to make it easier for developers and testers. */ QCommandLineOption environmentName(QStringList() << "n" << "namespace", QCoreApplication::translate("main", "Run with the specific namespace."), QCoreApplication::translate("main", "namespace")); QCommandLineOption translationFilename(QStringList() << "t" << "translation", QCoreApplication::translate("main", "Translation file - absolute or relative path and QM file name."), QCoreApplication::translate("main", "path/QM-filename")); QCommandLineOption forceLanguage(QStringList() << "l" << "language", QCoreApplication::translate("main", "Set language. example: 'en' or 'en_US'. Ignore environment setting."), QCoreApplication::translate("main", "code")); QCommandLineOption debugFile(QStringList() << "d" << "debug", QCoreApplication::translate("main", "Writes debug messages to the debug file")); QCommandLineOption importPending("import-pending", QCoreApplication::translate("main", "Process pending database import (internal use)")); QCommandLineOption forceLOVUpdate(QStringList() << "f" << "force-update", QCoreApplication::translate("main", "Force update of all value lists (DXCC, SATs, etc.)")); parser.addOption(environmentName); parser.addOption(translationFilename); parser.addOption(forceLanguage); parser.addOption(debugFile); parser.addOption(importPending); parser.addOption(forceLOVUpdate); parser.process(app); QString environment = parser.value(environmentName); QString translation_file = parser.value(translationFilename); QString lang = parser.value(forceLanguage); setLogToFile(parser.isSet(debugFile)); bool isImportPending = parser.isSet(importPending); bool isForceLOVUpdate = parser.isSet(forceLOVUpdate); // If started with --import-pending, wait a bit for the previous instance to fully terminate if ( isImportPending ) { qCDebug(runtime) << "Start postponed"; QCoreApplication::processEvents(); QThread::msleep(1000); } app.setOrganizationName("hamradio"); app.setApplicationName("QLog" + ((environment.isEmpty()) ? "" : environment.prepend("-"))); /* If the Style parameter is not present then use a default - Fusion style */ if ( !stylePresent ) { app.setStyle(QStyleFactory::create("Fusion")); } qInstallMessageHandler(debugMessageOutput); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType>(); qRegisterMetaType(); qRegisterMetaType(); #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) qRegisterMetaTypeStreamOperators>("QSet"); #endif qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); set_debug_level(LEVEL_PRODUCTION); // you can set more verbose rules via // environment variable QT_LOGGING_RULES (project setting/debug) setupTranslator(&app, lang, translation_file); /* Application Singleton * * Only one instance of QLog application is allowed * * It is always necessary to run only one QLog on the * system, because the FLDigi interface has a fixed port. * Therefore, in the case of two or more instances, * there is a port conflict. */ AppGuard guard( "QLog" ); if ( !guard.tryToRun() ) { QMessageBox::critical(nullptr, QMessageBox::tr("QLog Error"), QMessageBox::tr("QLog is already running")); return 1; } QPixmap pixmap(":/res/qlog.png"); SplashScreen splash(pixmap); splash.show(); splash.ensureFirstPaint(); createDataDirectory(); // Process pending database import if exists if ( LogDatabase::hasPendingImport() ) { splash.showMessage(QObject::tr("Importing Database"), Qt::AlignBottom|Qt::AlignCenter); QCoreApplication::processEvents(); if ( !LogDatabase::instance()->processPendingImport() ) { QMessageBox::critical(nullptr, QMessageBox::tr("QLog Error"), QMessageBox::tr("Failed to process pending database import.")); return 1; } if ( LogDatabase::instance()->hadPasswordImportWarning() ) { QMessageBox::warning(nullptr, QMessageBox::tr("QLog Warning"), QMessageBox::tr("The database was imported successfully, but the stored passwords " "could not be restored (decryption failed or the data is corrupted). " "All service passwords have been cleared and must be re-entered in Settings.")); } } else { splash.showMessage(QObject::tr("Opening Database"), Qt::AlignBottom|Qt::AlignCenter); QCoreApplication::processEvents(); if ( ! LogDatabase::instance()->openDatabase() ) { QMessageBox::critical(nullptr, QMessageBox::tr("QLog Error"), QMessageBox::tr("Could not connect to database.")); return 1; } splash.showMessage(QObject::tr("Backuping Database"), Qt::AlignBottom|Qt::AlignCenter); QCoreApplication::processEvents(); /* a migration can break a database therefore a backup is call before it */ if (!DBSchemaMigration::backupAllQSOsToADX()) { QMessageBox::critical(nullptr, QMessageBox::tr("QLog Error"), QMessageBox::tr("Could not export a QLog database to ADIF as a backup.

Try to export your log to ADIF manually")); } splash.showMessage(QObject::tr("Migrating Database"), Qt::AlignBottom|Qt::AlignCenter); QCoreApplication::processEvents(); if ( ! LogDatabase::instance()->schemaVersionUpgrade(isForceLOVUpdate) ) { QMessageBox::critical(nullptr, QMessageBox::tr("QLog Error"), QMessageBox::tr("Database migration failed.")); return 1; } } splash.showMessage(QObject::tr("Starting Application"), Qt::AlignBottom|Qt::AlignCenter); QCoreApplication::processEvents(); startRigThread(); startRotThread(); startCWKeyerThread(); MainWindow w; QIcon icon(":/res/qlog.png"); w.setWindowIcon(icon); splash.finish(&w); w.show(); w.setLayoutGeometry(); // check version only for Windows and MacOS. Linux has own distribution points #if defined(Q_OS_WIN) || defined(Q_OS_MAC) w.checkNewVersion(); #endif return app.exec(); } ================================================ FILE: core/zonedetect.c ================================================ /* * Copyright (c) 2018, Bertold Van den Bergh (vandenbergh@bertold.org) * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the author nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR DISTRIBUTOR BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* https://github.com/BertoldVdb/ZoneDetect/tree/master/library */ #include #include #include #include #include #include #include #if defined(_MSC_VER) || defined(__MINGW32__) #include #elif defined(__APPLE__) || defined(__linux__) || defined(__unix__) || defined(_POSIX_VERSION) #include #include #include #include #endif #include "zonedetect.h" enum ZDInternalError { ZD_OK, ZD_E_DB_OPEN, ZD_E_DB_SEEK, ZD_E_DB_MMAP, #if defined(_MSC_VER) || defined(__MINGW32__) ZD_E_DB_MMAP_MSVIEW, ZD_E_DB_MAP_EXCEPTION, ZD_E_DB_MUNMAP_MSVIEW, #endif ZD_E_DB_MUNMAP, ZD_E_DB_CLOSE, ZD_E_PARSE_HEADER }; struct ZoneDetectOpaque { #if defined(_MSC_VER) || defined(__MINGW32__) HANDLE fd; HANDLE fdMap; int32_t length; int32_t padding; #elif defined(__APPLE__) || defined(__linux__) || defined(__unix__) || defined(_POSIX_VERSION) int fd; off_t length; #else int length; #endif uint8_t closeType; uint8_t *mapping; uint8_t tableType; uint8_t version; uint8_t precision; uint8_t numFields; char *notice; char **fieldNames; uint32_t bboxOffset; uint32_t metadataOffset; uint32_t dataOffset; }; static void (*zdErrorHandler)(int, int); static void zdError(enum ZDInternalError errZD, int errNative) { if (zdErrorHandler) zdErrorHandler((int)errZD, errNative); } static int32_t ZDFloatToFixedPoint(float input, float scale, unsigned int precision) { const float inputScaled = input / scale; return (int32_t)(inputScaled * (float)(1 << (precision - 1))); } static float ZDFixedPointToFloat(int32_t input, float scale, unsigned int precision) { const float value = (float)input / (float)(1 << (precision - 1)); return value * scale; } static unsigned int ZDDecodeVariableLengthUnsigned(const ZoneDetect *library, uint32_t *index, uint64_t *result) { if(*index >= (uint32_t)library->length) { return 0; } uint64_t value = 0; unsigned int i = 0; #if defined(_MSC_VER) __try { #endif uint8_t *const buffer = library->mapping + *index; uint8_t *const bufferEnd = library->mapping + library->length - 1; unsigned int shift = 0; while(1) { value |= ((((uint64_t)buffer[i]) & UINT8_C(0x7F)) << shift); shift += 7u; if(!(buffer[i] & UINT8_C(0x80))) { break; } i++; if(buffer + i > bufferEnd) { return 0; } } #if defined(_MSC_VER) } __except(GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { /* file mapping SEH exception occurred */ zdError(ZD_E_DB_MAP_EXCEPTION, (int)GetLastError()); return 0; } #endif i++; *result = value; *index += i; return i; } static unsigned int ZDDecodeVariableLengthUnsignedReverse(const ZoneDetect *library, uint32_t *index, uint64_t *result) { uint32_t i = *index; if(*index >= (uint32_t)library->length) { return 0; } #if defined(_MSC_VER) __try { #endif if(library->mapping[i] & UINT8_C(0x80)) { return 0; } if(!i) { return 0; } i--; while(library->mapping[i] & UINT8_C(0x80)) { if(!i) { return 0; } i--; } #if defined(_MSC_VER) } __except(GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { /* file mapping SEH exception occurred */ zdError(ZD_E_DB_MAP_EXCEPTION, (int)GetLastError()); return 0; } #endif *index = i; i++; uint32_t i2 = i; return ZDDecodeVariableLengthUnsigned(library, &i2, result); } static int64_t ZDDecodeUnsignedToSigned(uint64_t value) { return (value & 1) ? -(int64_t)(value / 2) : (int64_t)(value / 2); } static unsigned int ZDDecodeVariableLengthSigned(const ZoneDetect *library, uint32_t *index, int32_t *result) { uint64_t value = 0; const unsigned int retVal = ZDDecodeVariableLengthUnsigned(library, index, &value); *result = (int32_t)ZDDecodeUnsignedToSigned(value); return retVal; } static char *ZDParseString(const ZoneDetect *library, uint32_t *index) { uint64_t strLength; if(!ZDDecodeVariableLengthUnsigned(library, index, &strLength)) { return NULL; } uint32_t strOffset = *index; unsigned int remoteStr = 0; if(strLength >= 256) { strOffset = library->metadataOffset + (uint32_t)strLength - 256; remoteStr = 1; if(!ZDDecodeVariableLengthUnsigned(library, &strOffset, &strLength)) { return NULL; } if(strLength > 256) { return NULL; } } char *const str = malloc((size_t)(strLength + 1)); if(str) { #if defined(_MSC_VER) __try { #endif size_t i; for(i = 0; i < strLength; i++) { str[i] = (char)(library->mapping[strOffset + i] ^ UINT8_C(0x80)); } #if defined(_MSC_VER) } __except(GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { /* file mapping SEH exception occurred */ zdError(ZD_E_DB_MAP_EXCEPTION, (int)GetLastError()); return 0; } #endif str[strLength] = 0; } if(!remoteStr) { *index += (uint32_t)strLength; } return str; } static int ZDParseHeader(ZoneDetect *library) { if(library->length < 7) { return -1; } #if defined(_MSC_VER) __try { #endif if(memcmp(library->mapping, "PLB", 3)) { return -1; } library->tableType = library->mapping[3]; library->version = library->mapping[4]; library->precision = library->mapping[5]; library->numFields = library->mapping[6]; #if defined(_MSC_VER) } __except(GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { /* file mapping SEH exception occurred */ zdError(ZD_E_DB_MAP_EXCEPTION, (int)GetLastError()); return 0; } #endif if(library->version >= 2) { return -1; } uint32_t index = UINT32_C(7); library->fieldNames = malloc(library->numFields * sizeof *library->fieldNames); if (!library->fieldNames) { return -1; } size_t i; for(i = 0; i < library->numFields; i++) { library->fieldNames[i] = ZDParseString(library, &index); } library->notice = ZDParseString(library, &index); if(!library->notice) { return -1; } uint64_t tmp; /* Read section sizes */ /* By memset: library->bboxOffset = 0 */ if(!ZDDecodeVariableLengthUnsigned(library, &index, &tmp)) return -1; library->metadataOffset = (uint32_t)tmp + library->bboxOffset; if(!ZDDecodeVariableLengthUnsigned(library, &index, &tmp))return -1; library->dataOffset = (uint32_t)tmp + library->metadataOffset; if(!ZDDecodeVariableLengthUnsigned(library, &index, &tmp)) return -1; /* Add header size to everything */ library->bboxOffset += index; library->metadataOffset += index; library->dataOffset += index; /* Verify file length */ if(tmp + library->dataOffset != (uint32_t)library->length) { return -2; } return 0; } static int ZDPointInBox(int32_t xl, int32_t x, int32_t xr, int32_t yl, int32_t y, int32_t yr) { if((xl <= x && x <= xr) || (xr <= x && x <= xl)) { if((yl <= y && y <= yr) || (yr <= y && y <= yl)) { return 1; } } return 0; } static uint32_t ZDUnshuffle(uint64_t w) { w &= 0x5555555555555555llu; w = (w | (w >> 1)) & 0x3333333333333333llu; w = (w | (w >> 2)) & 0x0F0F0F0F0F0F0F0Fllu; w = (w | (w >> 4)) & 0x00FF00FF00FF00FFllu; w = (w | (w >> 8)) & 0x0000FFFF0000FFFFllu; w = (w | (w >> 16)) & 0x00000000FFFFFFFFllu; return (uint32_t)w; } static void ZDDecodePoint(uint64_t point, int32_t* lat, int32_t* lon) { *lat = (int32_t)ZDDecodeUnsignedToSigned(ZDUnshuffle(point)); *lon = (int32_t)ZDDecodeUnsignedToSigned(ZDUnshuffle(point >> 1)); } struct Reader { const ZoneDetect *library; uint32_t polygonIndex; uint64_t numVertices; uint8_t done, first; uint32_t referenceStart, referenceEnd; int32_t referenceDirection; int32_t pointLat, pointLon; int32_t firstLat, firstLon; }; static void ZDReaderInit(struct Reader *reader, const ZoneDetect *library, uint32_t polygonIndex) { memset(reader, 0, sizeof(*reader)); reader->library = library; reader->polygonIndex = polygonIndex; reader->first = 1; } static int ZDReaderGetPoint(struct Reader *reader, int32_t *pointLat, int32_t *pointLon) { int32_t diffLat = 0, diffLon = 0; readNewPoint: if(reader->done > 1) { return 0; } if(reader->first && reader->library->version == 0) { if(!ZDDecodeVariableLengthUnsigned(reader->library, &reader->polygonIndex, &reader->numVertices)) return -1; if(!reader->numVertices) return -1; } uint8_t referenceDone = 0; if(reader->library->version == 1) { uint64_t point = 0; if(!reader->referenceDirection) { if(!ZDDecodeVariableLengthUnsigned(reader->library, &reader->polygonIndex, &point)) return -1; } else { if(reader->referenceDirection > 0) { /* Read reference forward */ if(!ZDDecodeVariableLengthUnsigned(reader->library, &reader->referenceStart, &point)) return -1; if(reader->referenceStart >= reader->referenceEnd) { referenceDone = 1; } } else if(reader->referenceDirection < 0) { /* Read reference backwards */ if(!ZDDecodeVariableLengthUnsignedReverse(reader->library, &reader->referenceStart, &point)) return -1; if(reader->referenceStart <= reader->referenceEnd) { referenceDone = 1; } } } if(!point) { /* This is a special marker, it is not allowed in reference mode */ if(reader->referenceDirection) { return -1; } uint64_t value; if(!ZDDecodeVariableLengthUnsigned(reader->library, &reader->polygonIndex, &value)) return -1; if(value == 0) { reader->done = 2; } else if(value == 1) { int32_t diff; int64_t start; if(!ZDDecodeVariableLengthUnsigned(reader->library, &reader->polygonIndex, (uint64_t*)&start)) return -1; if(!ZDDecodeVariableLengthSigned(reader->library, &reader->polygonIndex, &diff)) return -1; reader->referenceStart = reader->library->dataOffset+(uint32_t)start; reader->referenceEnd = reader->library->dataOffset+(uint32_t)(start + diff); reader->referenceDirection = diff; if(diff < 0) { reader->referenceStart--; reader->referenceEnd--; } goto readNewPoint; } } else { ZDDecodePoint(point, &diffLat, &diffLon); if(reader->referenceDirection < 0) { diffLat = -diffLat; diffLon = -diffLon; } } } if(reader->library->version == 0) { if(!ZDDecodeVariableLengthSigned(reader->library, &reader->polygonIndex, &diffLat)) return -1; if(!ZDDecodeVariableLengthSigned(reader->library, &reader->polygonIndex, &diffLon)) return -1; } if(!reader->done) { reader->pointLat += diffLat; reader->pointLon += diffLon; if(reader->first) { reader->firstLat = reader->pointLat; reader->firstLon = reader->pointLon; } } else { /* Close the polygon (the closing point is not encoded) */ reader->pointLat = reader->firstLat; reader->pointLon = reader->firstLon; reader->done = 2; } reader->first = 0; if(reader->library->version == 0) { reader->numVertices--; if(!reader->numVertices) { reader->done = 1; } if(!diffLat && !diffLon) { goto readNewPoint; } } if(referenceDone) { reader->referenceDirection = 0; } if(pointLat) { *pointLat = reader->pointLat; } if(pointLon) { *pointLon = reader->pointLon; } return 1; } static int ZDFindPolygon(const ZoneDetect *library, uint32_t wantedId, uint32_t* metadataIndexPtr, uint32_t* polygonIndexPtr) { uint32_t polygonId = 0; uint32_t bboxIndex = library->bboxOffset; uint32_t metadataIndex = 0, polygonIndex = 0; while(bboxIndex < library->metadataOffset) { uint64_t polygonIndexDelta; int32_t metadataIndexDelta; int32_t tmp; if(!ZDDecodeVariableLengthSigned(library, &bboxIndex, &tmp)) break; if(!ZDDecodeVariableLengthSigned(library, &bboxIndex, &tmp)) break; if(!ZDDecodeVariableLengthSigned(library, &bboxIndex, &tmp)) break; if(!ZDDecodeVariableLengthSigned(library, &bboxIndex, &tmp)) break; if(!ZDDecodeVariableLengthSigned(library, &bboxIndex, &metadataIndexDelta)) break; if(!ZDDecodeVariableLengthUnsigned(library, &bboxIndex, &polygonIndexDelta)) break; metadataIndex += (uint32_t)metadataIndexDelta; polygonIndex += (uint32_t)polygonIndexDelta; if(polygonId == wantedId) { if(metadataIndexPtr) { metadataIndex += library->metadataOffset; *metadataIndexPtr = metadataIndex; } if(polygonIndexPtr) { polygonIndex += library->dataOffset; *polygonIndexPtr = polygonIndex; } return 1; } polygonId ++; } return 0; } static int32_t* ZDPolygonToListInternal(const ZoneDetect *library, uint32_t polygonIndex, size_t* length) { struct Reader reader; ZDReaderInit(&reader, library, polygonIndex); size_t listLength = 2 * 100; size_t listIndex = 0; int32_t* list = malloc(sizeof(int32_t) * listLength); if(!list) { goto fail; } while(1) { int32_t pointLat, pointLon; int result = ZDReaderGetPoint(&reader, &pointLat, &pointLon); if(result < 0) { goto fail; } else if(result == 0) { break; } if(listIndex >= listLength) { listLength *= 2; if(listLength >= 1048576) { goto fail; } list = realloc(list, sizeof(int32_t) * listLength); if(!list) { goto fail; } } list[listIndex++] = pointLat; list[listIndex++] = pointLon; } if(length) { *length = listIndex; } return list; fail: if(list) { free(list); } return NULL; } float* ZDPolygonToList(const ZoneDetect *library, uint32_t polygonId, size_t* lengthPtr) { uint32_t polygonIndex; int32_t* data = NULL; float* flData = NULL; if(!ZDFindPolygon(library, polygonId, NULL, &polygonIndex)) { goto fail; } size_t length = 0; data = ZDPolygonToListInternal(library, polygonIndex, &length); if(!data) { goto fail; } flData = malloc(sizeof(float) * length); if(!flData) { goto fail; } size_t i; for(i = 0; iprecision); flData[i+1] = ZDFixedPointToFloat(lon, 180, library->precision); } if(lengthPtr) { *lengthPtr = length; } return flData; fail: if(data) { free(data); } if(flData) { free(flData); } return NULL; } static ZDLookupResult ZDPointInPolygon(const ZoneDetect *library, uint32_t polygonIndex, int32_t latFixedPoint, int32_t lonFixedPoint, uint64_t *distanceSqrMin) { int32_t pointLat, pointLon, prevLat = 0, prevLon = 0; int prevQuadrant = 0, winding = 0; uint8_t first = 1; struct Reader reader; ZDReaderInit(&reader, library, polygonIndex); while(1) { int result = ZDReaderGetPoint(&reader, &pointLat, &pointLon); if(result < 0) { return ZD_LOOKUP_PARSE_ERROR; } else if(result == 0) { break; } /* Check if point is ON the border */ if(pointLat == latFixedPoint && pointLon == lonFixedPoint) { if(distanceSqrMin) *distanceSqrMin = 0; return ZD_LOOKUP_ON_BORDER_VERTEX; } /* Find quadrant */ int quadrant; if(pointLat >= latFixedPoint) { if(pointLon >= lonFixedPoint) { quadrant = 0; } else { quadrant = 1; } } else { if(pointLon >= lonFixedPoint) { quadrant = 3; } else { quadrant = 2; } } if(!first) { int windingNeedCompare = 0, lineIsStraight = 0; float a = 0, b = 0; /* Calculate winding number */ if(quadrant == prevQuadrant) { /* Do nothing */ } else if(quadrant == (prevQuadrant + 1) % 4) { winding ++; } else if((quadrant + 1) % 4 == prevQuadrant) { winding --; } else { windingNeedCompare = 1; } /* Avoid horizontal and vertical lines */ if((pointLon == prevLon || pointLat == prevLat)) { lineIsStraight = 1; } /* Calculate the parameters of y=ax+b if needed */ if(!lineIsStraight && (distanceSqrMin || windingNeedCompare)) { a = ((float)pointLat - (float)prevLat) / ((float)pointLon - (float)prevLon); b = (float)pointLat - a * (float)pointLon; } int onStraight = ZDPointInBox(pointLat, latFixedPoint, prevLat, pointLon, lonFixedPoint, prevLon); if(lineIsStraight && (onStraight || windingNeedCompare)) { if(distanceSqrMin) *distanceSqrMin = 0; return ZD_LOOKUP_ON_BORDER_SEGMENT; } /* Jumped two quadrants. */ if(windingNeedCompare) { /* Check if the target is on the border */ const int32_t intersectLon = (int32_t)(((float)latFixedPoint - b) / a); if(intersectLon >= lonFixedPoint-1 && intersectLon <= lonFixedPoint+1) { if(distanceSqrMin) *distanceSqrMin = 0; return ZD_LOOKUP_ON_BORDER_SEGMENT; } /* Ok, it's not. In which direction did we go round the target? */ const int sign = (intersectLon < lonFixedPoint) ? 2 : -2; if(quadrant == 2 || quadrant == 3) { winding += sign; } else { winding -= sign; } } /* Calculate closest point on line (if needed) */ if(distanceSqrMin) { float closestLon, closestLat; if(!lineIsStraight) { closestLon = ((float)lonFixedPoint + a * (float)latFixedPoint - a * b) / (a * a + 1); closestLat = (a * ((float)lonFixedPoint + a * (float)latFixedPoint) + b) / (a * a + 1); } else { if(pointLon == prevLon) { closestLon = (float)pointLon; closestLat = (float)latFixedPoint; } else { closestLon = (float)lonFixedPoint; closestLat = (float)pointLat; } } const int closestInBox = ZDPointInBox(pointLon, (int32_t)closestLon, prevLon, pointLat, (int32_t)closestLat, prevLat); int64_t diffLat, diffLon; if(closestInBox) { /* Calculate squared distance to segment. */ diffLat = (int64_t)(closestLat - (float)latFixedPoint); diffLon = (int64_t)(closestLon - (float)lonFixedPoint); } else { /* * Calculate squared distance to vertices * It is enough to check the current point since the polygon is closed. */ diffLat = (int64_t)(pointLat - latFixedPoint); diffLon = (int64_t)(pointLon - lonFixedPoint); } /* Note: lon has half scale */ uint64_t distanceSqr = (uint64_t)(diffLat * diffLat) + (uint64_t)(diffLon * diffLon) * 4; if(distanceSqr < *distanceSqrMin) *distanceSqrMin = distanceSqr; } } prevQuadrant = quadrant; prevLat = pointLat; prevLon = pointLon; first = 0; }; if(winding == -4) { return ZD_LOOKUP_IN_ZONE; } else if(winding == 4) { return ZD_LOOKUP_IN_EXCLUDED_ZONE; } else if(winding == 0) { return ZD_LOOKUP_NOT_IN_ZONE; } /* Should not happen */ if(distanceSqrMin) *distanceSqrMin = 0; return ZD_LOOKUP_ON_BORDER_SEGMENT; } void ZDCloseDatabase(ZoneDetect *library) { if(library) { if(library->fieldNames) { size_t i; for(i = 0; i < (size_t)library->numFields; i++) { if(library->fieldNames[i]) { free(library->fieldNames[i]); } } free(library->fieldNames); } if(library->notice) { free(library->notice); } if(library->closeType == 0) { #if defined(_MSC_VER) || defined(__MINGW32__) if(library->mapping && !UnmapViewOfFile(library->mapping)) zdError(ZD_E_DB_MUNMAP_MSVIEW, (int)GetLastError()); if(library->fdMap && !CloseHandle(library->fdMap)) zdError(ZD_E_DB_MUNMAP, (int)GetLastError()); if(library->fd && !CloseHandle(library->fd)) zdError(ZD_E_DB_CLOSE, (int)GetLastError()); #elif defined(__APPLE__) || defined(__linux__) || defined(__unix__) || defined(_POSIX_VERSION) if(library->mapping && munmap(library->mapping, (size_t)(library->length))) zdError(ZD_E_DB_MUNMAP, 0); if(library->fd >= 0 && close(library->fd)) zdError(ZD_E_DB_CLOSE, 0); #endif } free(library); } } ZoneDetect *ZDOpenDatabaseFromMemory(void* buffer, size_t length) { ZoneDetect *const library = malloc(sizeof *library); if(library) { memset(library, 0, sizeof(*library)); library->closeType = 1; library->length = (long int)length; if(library->length <= 0) { #if defined(_MSC_VER) || defined(__MINGW32__) || defined(__APPLE__) || defined(__linux__) || defined(__unix__) || defined(_POSIX_VERSION) zdError(ZD_E_DB_SEEK, errno); #else zdError(ZD_E_DB_SEEK, 0); #endif goto fail; } library->mapping = buffer; /* Parse the header */ if(ZDParseHeader(library)) { zdError(ZD_E_PARSE_HEADER, 0); goto fail; } } return library; fail: ZDCloseDatabase(library); return NULL; } ZoneDetect *ZDOpenDatabase(const char *path) { ZoneDetect *const library = malloc(sizeof *library); if(library) { memset(library, 0, sizeof(*library)); #if defined(_MSC_VER) || defined(__MINGW32__) library->fd = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (library->fd == INVALID_HANDLE_VALUE) { zdError(ZD_E_DB_OPEN, (int)GetLastError()); goto fail; } const DWORD fsize = GetFileSize(library->fd, NULL); if (fsize == INVALID_FILE_SIZE) { zdError(ZD_E_DB_SEEK, (int)GetLastError()); goto fail; } library->length = (int32_t)fsize; library->fdMap = CreateFileMappingA(library->fd, NULL, PAGE_READONLY, 0, 0, NULL); if (!library->fdMap) { zdError(ZD_E_DB_MMAP, (int)GetLastError()); goto fail; } library->mapping = MapViewOfFile(library->fdMap, FILE_MAP_READ, 0, 0, 0); if (!library->mapping) { zdError(ZD_E_DB_MMAP_MSVIEW, (int)GetLastError()); goto fail; } #elif defined(__APPLE__) || defined(__linux__) || defined(__unix__) || defined(_POSIX_VERSION) library->fd = open(path, O_RDONLY | O_CLOEXEC); if(library->fd < 0) { zdError(ZD_E_DB_OPEN, errno); goto fail; } library->length = lseek(library->fd, 0, SEEK_END); if(library->length <= 0 || library->length > 50331648) { zdError(ZD_E_DB_SEEK, errno); goto fail; } lseek(library->fd, 0, SEEK_SET); library->mapping = mmap(NULL, (size_t)library->length, PROT_READ, MAP_PRIVATE | MAP_FILE, library->fd, 0); if(library->mapping == MAP_FAILED) { zdError(ZD_E_DB_MMAP, errno); goto fail; } #endif /* Parse the header */ if(ZDParseHeader(library)) { zdError(ZD_E_PARSE_HEADER, 0); goto fail; } } return library; fail: ZDCloseDatabase(library); return NULL; } ZoneDetectResult *ZDLookup(const ZoneDetect *library, float lat, float lon, float *safezone) { const int32_t latFixedPoint = ZDFloatToFixedPoint(lat, 90, library->precision); const int32_t lonFixedPoint = ZDFloatToFixedPoint(lon, 180, library->precision); size_t numResults = 0; uint64_t distanceSqrMin = (uint64_t)-1; /* Iterate over all polygons */ uint32_t bboxIndex = library->bboxOffset; uint32_t metadataIndex = 0; uint32_t polygonIndex = 0; ZoneDetectResult *results = malloc(sizeof *results); if(!results) { return NULL; } uint32_t polygonId = 0; while(bboxIndex < library->metadataOffset) { int32_t minLat, minLon, maxLat, maxLon, metadataIndexDelta; uint64_t polygonIndexDelta; if(!ZDDecodeVariableLengthSigned(library, &bboxIndex, &minLat)) break; if(!ZDDecodeVariableLengthSigned(library, &bboxIndex, &minLon)) break; if(!ZDDecodeVariableLengthSigned(library, &bboxIndex, &maxLat)) break; if(!ZDDecodeVariableLengthSigned(library, &bboxIndex, &maxLon)) break; if(!ZDDecodeVariableLengthSigned(library, &bboxIndex, &metadataIndexDelta)) break; if(!ZDDecodeVariableLengthUnsigned(library, &bboxIndex, &polygonIndexDelta)) break; metadataIndex += (uint32_t)metadataIndexDelta; polygonIndex += (uint32_t)polygonIndexDelta; if(latFixedPoint >= minLat) { if(latFixedPoint <= maxLat && lonFixedPoint >= minLon && lonFixedPoint <= maxLon) { const ZDLookupResult lookupResult = ZDPointInPolygon(library, library->dataOffset + polygonIndex, latFixedPoint, lonFixedPoint, (safezone) ? &distanceSqrMin : NULL); if(lookupResult == ZD_LOOKUP_PARSE_ERROR) { break; } else if(lookupResult != ZD_LOOKUP_NOT_IN_ZONE) { ZoneDetectResult *const newResults = realloc(results, sizeof *newResults * (numResults + 2)); if(newResults) { results = newResults; results[numResults].polygonId = polygonId; results[numResults].metaId = metadataIndex; results[numResults].numFields = library->numFields; results[numResults].fieldNames = library->fieldNames; results[numResults].lookupResult = lookupResult; numResults++; } else { break; } } } } else { /* The data is sorted along minLat */ break; } polygonId++; } /* Clean up results */ size_t i; for(i = 0; i < numResults; i++) { int insideSum = 0; ZDLookupResult overrideResult = ZD_LOOKUP_IGNORE; size_t j; for(j = i; j < numResults; j++) { if(results[i].metaId == results[j].metaId) { ZDLookupResult tmpResult = results[j].lookupResult; results[j].lookupResult = ZD_LOOKUP_IGNORE; /* This is the same result. Is it an exclusion zone? */ if(tmpResult == ZD_LOOKUP_IN_ZONE) { insideSum++; } else if(tmpResult == ZD_LOOKUP_IN_EXCLUDED_ZONE) { insideSum--; } else { /* If on the bodrder then the final result is on the border */ overrideResult = tmpResult; } } } if(overrideResult != ZD_LOOKUP_IGNORE) { results[i].lookupResult = overrideResult; } else { if(insideSum) { results[i].lookupResult = ZD_LOOKUP_IN_ZONE; } } } /* Remove zones to ignore */ size_t newNumResults = 0; for(i = 0; i < numResults; i++) { if(results[i].lookupResult != ZD_LOOKUP_IGNORE) { results[newNumResults] = results[i]; newNumResults++; } } numResults = newNumResults; /* Lookup metadata */ for(i = 0; i < numResults; i++) { uint32_t tmpIndex = library->metadataOffset + results[i].metaId; results[i].data = malloc(library->numFields * sizeof *results[i].data); if(results[i].data) { size_t j; for(j = 0; j < library->numFields; j++) { results[i].data[j] = ZDParseString(library, &tmpIndex); if (!results[i].data[j]) { /* free all allocated memory */ size_t m; for(m = 0; m < j; m++) { if(results[i].data[m]) { free(results[i].data[m]); } } size_t k; for(k = 0; k < i; k++) { size_t l; for(l = 0; l < (size_t)results[k].numFields; l++) { if(results[k].data[l]) { free(results[k].data[l]); } } if (results[k].data) { free(results[k].data); } } free(results); return NULL; } } } else { /* free all allocated memory */ size_t k; for(k = 0; k < i; k++) { size_t l; for(l = 0; l < (size_t)results[k].numFields; l++) { if(results[k].data[l]) { free(results[k].data[l]); } } if (results[k].data) { free(results[k].data); } } free(results); return NULL; } } /* Write end marker */ results[numResults].lookupResult = ZD_LOOKUP_END; results[numResults].numFields = 0; results[numResults].fieldNames = NULL; results[numResults].data = NULL; if(safezone) { *safezone = sqrtf((float)distanceSqrMin) * 90 / (float)(1 << (library->precision - 1)); } return results; } void ZDFreeResults(ZoneDetectResult *results) { unsigned int index = 0; if(!results) { return; } while(results[index].lookupResult != ZD_LOOKUP_END) { if(results[index].data) { size_t i; for(i = 0; i < (size_t)results[index].numFields; i++) { if(results[index].data[i]) { free(results[index].data[i]); } } free(results[index].data); } index++; } free(results); } const char *ZDGetNotice(const ZoneDetect *library) { return library->notice; } uint8_t ZDGetTableType(const ZoneDetect *library) { return library->tableType; } const char *ZDLookupResultToString(ZDLookupResult result) { switch(result) { case ZD_LOOKUP_IGNORE: return "Ignore"; case ZD_LOOKUP_END: return "End"; case ZD_LOOKUP_PARSE_ERROR: return "Parsing error"; case ZD_LOOKUP_NOT_IN_ZONE: return "Not in zone"; case ZD_LOOKUP_IN_ZONE: return "In zone"; case ZD_LOOKUP_IN_EXCLUDED_ZONE: return "In excluded zone"; case ZD_LOOKUP_ON_BORDER_VERTEX: return "Target point is border vertex"; case ZD_LOOKUP_ON_BORDER_SEGMENT: return "Target point is on border"; } return "Unknown"; } #define ZD_E_COULD_NOT(msg) "could not " msg const char *ZDGetErrorString(int errZD) { switch ((enum ZDInternalError)errZD) { default: assert(0); case ZD_OK : return ""; case ZD_E_DB_OPEN : return ZD_E_COULD_NOT("open database file"); case ZD_E_DB_SEEK : return ZD_E_COULD_NOT("retrieve database file size"); case ZD_E_DB_MMAP : return ZD_E_COULD_NOT("map database file to system memory"); #if defined(_MSC_VER) || defined(__MINGW32__) case ZD_E_DB_MMAP_MSVIEW : return ZD_E_COULD_NOT("open database file view"); case ZD_E_DB_MAP_EXCEPTION: return "I/O exception occurred while accessing database file view"; case ZD_E_DB_MUNMAP_MSVIEW: return ZD_E_COULD_NOT("close database file view"); #endif case ZD_E_DB_MUNMAP : return ZD_E_COULD_NOT("unmap database"); case ZD_E_DB_CLOSE : return ZD_E_COULD_NOT("close database file"); case ZD_E_PARSE_HEADER : return ZD_E_COULD_NOT("parse database header"); } } #undef ZD_E_COULD_NOT int ZDSetErrorHandler(void (*handler)(int, int)) { zdErrorHandler = handler; return 0; } char* ZDHelperSimpleLookupString(const ZoneDetect* library, float lat, float lon) { ZoneDetectResult *result = ZDLookup(library, lat, lon, NULL); if(!result) { return NULL; } char* output = NULL; if(result[0].lookupResult == ZD_LOOKUP_END) { goto done; } char* strings[2] = {NULL}; unsigned int i; for(i = 0; i < result[0].numFields; i++) { if(result[0].fieldNames[i] && result[0].data[i]) { if(library->tableType == 'T') { if(!strcmp(result[0].fieldNames[i], "TimezoneIdPrefix")) { strings[0] = result[0].data[i]; } if(!strcmp(result[0].fieldNames[i], "TimezoneId")) { strings[1] = result[0].data[i]; } } if(library->tableType == 'C') { if(!strcmp(result[0].fieldNames[i], "Name")) { strings[0] = result[0].data[i]; } } } } size_t length = 0; for(i=0; i 512) { goto done; } length += partLength; } } if(length == 0) { goto done; } length += 1; output = (char*)malloc(length); if(output) { output[0] = 0; for(i=0; i #include #ifndef INCL_ZONEDETECT_H_ #define INCL_ZONEDETECT_H_ #if !defined(ZD_EXPORT) #if defined(_MSC_VER) #define ZD_EXPORT __declspec(dllimport) #else #define ZD_EXPORT #endif #endif typedef enum { ZD_LOOKUP_IGNORE = -3, ZD_LOOKUP_END = -2, ZD_LOOKUP_PARSE_ERROR = -1, ZD_LOOKUP_NOT_IN_ZONE = 0, ZD_LOOKUP_IN_ZONE = 1, ZD_LOOKUP_IN_EXCLUDED_ZONE = 2, ZD_LOOKUP_ON_BORDER_VERTEX = 3, ZD_LOOKUP_ON_BORDER_SEGMENT = 4 } ZDLookupResult; typedef struct { ZDLookupResult lookupResult; uint32_t polygonId; uint32_t metaId; uint8_t numFields; char **fieldNames; char **data; } ZoneDetectResult; struct ZoneDetectOpaque; typedef struct ZoneDetectOpaque ZoneDetect; #ifdef __cplusplus extern "C" { #endif ZD_EXPORT ZoneDetect *ZDOpenDatabase(const char *path); ZD_EXPORT ZoneDetect *ZDOpenDatabaseFromMemory(void* buffer, size_t length); ZD_EXPORT void ZDCloseDatabase(ZoneDetect *library); ZD_EXPORT ZoneDetectResult *ZDLookup(const ZoneDetect *library, float lat, float lon, float *safezone); ZD_EXPORT void ZDFreeResults(ZoneDetectResult *results); ZD_EXPORT const char *ZDGetNotice(const ZoneDetect *library); ZD_EXPORT uint8_t ZDGetTableType(const ZoneDetect *library); ZD_EXPORT const char *ZDLookupResultToString(ZDLookupResult result); ZD_EXPORT int ZDSetErrorHandler(void (*handler)(int, int)); ZD_EXPORT const char *ZDGetErrorString(int errZD); ZD_EXPORT float* ZDPolygonToList(const ZoneDetect *library, uint32_t polygonId, size_t* length); ZD_EXPORT char* ZDHelperSimpleLookupString(const ZoneDetect* library, float lat, float lon); ZD_EXPORT void ZDHelperSimpleLookupStringFree(char* str); #ifdef __cplusplus } #endif #endif // INCL_ZONEDETECT_H_ ================================================ FILE: cwkey/CWKeyer.cpp ================================================ #include "CWKeyer.h" #include "cwkey/drivers/CWKey.h" #include "cwkey/drivers/CWDummyKey.h" #include "cwkey/drivers/CWWinKey.h" #include "cwkey/drivers/CWCatKey.h" #include "cwkey/drivers/CWDaemonKey.h" #include "cwkey/drivers/CWFldigiKey.h" #include "core/debug.h" #include "data/CWKeyProfile.h" MODULE_IDENTIFICATION("qlog.cwkey.cwkeyer"); #define TIME_PERIOD 1000 void CWKeyer::start() { FCT_IDENTIFICATION; timer = new QTimer(this); connect(timer, &QTimer::timeout, this, &CWKeyer::update); timer->start(TIME_PERIOD); } void CWKeyer::stopTimer() { FCT_IDENTIFICATION; bool check = QMetaObject::invokeMethod(CWKeyer::instance(), &CWKeyer::stopTimerImplt, Qt::QueuedConnection); Q_ASSERT( check ); } void CWKeyer::update() { FCT_IDENTIFICATION; qCDebug(runtime) << "Waiting for cwkey mutex"; if ( !cwKeyLock.tryLock(200) ) { qCDebug(runtime) << "Waited too long"; return; } qCDebug(runtime) << "Updating key state"; if ( !cwKey ) { cwKeyLock.unlock(); return; } CWKeyProfile currCWProfile = CWKeyProfilesManager::instance()->getCurProfile1(); /***********************************************************/ /* Is Opened Profile still the globbaly used CW Profile ? */ /* if NO then reconnect it */ /***********************************************************/ if ( currCWProfile != connectedCWKeyProfile) { /* CW Key Profile Changed * Need to reconnect CW Key */ qCDebug(runtime) << "Reconnecting to a new CW Key - " << currCWProfile.profileName << "; Old - " << connectedCWKeyProfile.profileName; __openCWKey(); } timer->start(TIME_PERIOD); cwKeyLock.unlock(); } void CWKeyer::open() { FCT_IDENTIFICATION; QMetaObject::invokeMethod(this, &CWKeyer::openImpl, Qt::QueuedConnection); } void CWKeyer::openImpl() { FCT_IDENTIFICATION; cwKeyLock.lock(); __openCWKey(); cwKeyLock.unlock(); } void CWKeyer::__openCWKey() { FCT_IDENTIFICATION; // if cw keys is active then close it __closeCWKey(); CWKeyProfile newProfile = CWKeyProfilesManager::instance()->getCurProfile1(); if ( newProfile == CWKeyProfile() ) { emit cwKeyerError(tr("No CW Keyer Profile selected"), QString()); return; } qCDebug(runtime) << "Opening profile name: " << newProfile.profileName; switch ( newProfile.model ) { case CWKey::DUMMY_KEYER: cwKey = new CWDummyKey(this); break; case CWKey::WINKEY_KEYER: cwKey = new CWWinKey(newProfile.portPath, newProfile.baudrate, newProfile.keyMode, newProfile.defaultSpeed, newProfile.paddleSwap, newProfile.paddleOnlySidetone, newProfile.sidetoneFrequency, this); break; case CWKey::MORSEOVERCAT: cwKey = new CWCatKey(newProfile.keyMode, newProfile.defaultSpeed, this); break; case CWKey::CWDAEMON_KEYER: cwKey = new CWDaemonKey(newProfile.hostname, newProfile.netport, newProfile.keyMode, newProfile.defaultSpeed, newProfile.sidetoneFrequency, this); break; case CWKey::FLDIGI_KEYER: cwKey = new CWFldigiKey(newProfile.hostname, newProfile.netport, newProfile.keyMode, newProfile.defaultSpeed, this); break; default: cwKey = nullptr; qWarning() << "Unsupported Key Model " << newProfile.model; } if ( !cwKey ) { // initialization failed emit cwKeyerError(tr("Initialization Error"), tr("Internal Error")); return; } if ( !cwKey->open() ) { emit cwKeyerError(tr("Connection Error"), tr("Cannot open the Keyer connection")); qWarning() << cwKey->lastError(); __closeCWKey(); return; } connect(cwKey, &CWKey::keyError, this, &CWKeyer::keyErrorHandler); connect(cwKey, &CWKey::keyChangedWPMSpeed, this, &CWKeyer::cwKeyWPMChangedHandler); connect(cwKey, &CWKey::keyEchoText, this, &CWKeyer::cwKeyEchoTextHandler); connect(cwKey, &CWKey::keyHWButton1Pressed, this, &CWKeyer::cwKeyHWButton1PressedHandler); connect(cwKey, &CWKey::keyHWButton2Pressed, this, &CWKeyer::cwKeyHWButton2PressedHandler); connect(cwKey, &CWKey::keyHWButton3Pressed, this, &CWKeyer::cwKeyHWButton3PressedHandler); connect(cwKey, &CWKey::keyHWButton4Pressed, this, &CWKeyer::cwKeyHWButton4PressedHandler); connectedCWKeyProfile = newProfile; emit cwKeyConnected(connectedCWKeyProfile.profileName); } void CWKeyer::close() { FCT_IDENTIFICATION; QMetaObject::invokeMethod(this, &CWKeyer::closeImpl, Qt::QueuedConnection); } bool CWKeyer::canStopSending() { FCT_IDENTIFICATION; qCDebug(runtime) << "Waiting for cwkey mutex"; QMutexLocker locker(&cwKeyLock); qCDebug(runtime) << "Using Key"; if ( !cwKey ) { return false; } bool ret = cwKey->canStopSending(); return ret; } bool CWKeyer::canEchoChar() { FCT_IDENTIFICATION; qCDebug(runtime) << "Waiting for cwkey mutex"; QMutexLocker locker(&cwKeyLock); qCDebug(runtime) << "Using Key"; if ( !cwKey ) { return false; } bool ret = cwKey->canEchoChar(); return ret; } bool CWKeyer::rigMustConnected() { FCT_IDENTIFICATION; qCDebug(runtime) << "Waiting for cwkey mutex"; QMutexLocker locker(&cwKeyLock); qCDebug(runtime) << "Using Key"; if ( !cwKey ) { return false; } bool ret = cwKey->mustRigConnected(); return ret; } bool CWKeyer::canSetSpeed() { FCT_IDENTIFICATION; qCDebug(runtime) << "Waiting for cwkey mutex"; QMutexLocker locker(&cwKeyLock); qCDebug(runtime) << "Using Key"; if ( !cwKey ) { return false; } bool ret = cwKey->canSetSpeed(); return ret; } void CWKeyer::closeImpl() { FCT_IDENTIFICATION; qCDebug(runtime) << "Waiting for cwkey mutex"; cwKeyLock.lock(); qCDebug(runtime) << "Using Key"; __closeCWKey(); cwKeyLock.unlock(); } void CWKeyer::__closeCWKey() { FCT_IDENTIFICATION; connectedCWKeyProfile = CWKeyProfile(); if ( cwKey ) { cwKey->close(); cwKey->deleteLater(); cwKey = nullptr; } emit cwKeyDisconnected(); } void CWKeyer::setSpeed(const qint16 wpm) { FCT_IDENTIFICATION; QMetaObject::invokeMethod(this, "setSpeedImpl", Qt::QueuedConnection, Q_ARG(qint16, wpm)); } void CWKeyer::setSpeedImpl(const qint16 wpm) { FCT_IDENTIFICATION; qCDebug(runtime) << "Waiting for cwkey mutex"; QMutexLocker locker(&cwKeyLock); qCDebug(runtime) << "Using Key"; if ( !cwKey ) return; cwKey->setWPM(wpm); } void CWKeyer::sendText(const QString &text) { FCT_IDENTIFICATION; QMetaObject::invokeMethod(this, "sendTextImpl", Qt::QueuedConnection, Q_ARG(QString, text)); } void CWKeyer::sendTextImpl(const QString &text) { FCT_IDENTIFICATION; qCDebug(runtime) << "Waiting for cwkey mutex"; QMutexLocker locker(&cwKeyLock); qCDebug(runtime) << "Using Key"; if ( !cwKey ) return; cwKey->sendText(text); } void CWKeyer::immediatelyStop() { FCT_IDENTIFICATION; QMetaObject::invokeMethod(this, "immediatelyStopImpl", Qt::QueuedConnection); } void CWKeyer::immediatelyStopImpl() { FCT_IDENTIFICATION; qCDebug(runtime) << "Waiting for cwkey mutex"; QMutexLocker locker(&cwKeyLock); qCDebug(runtime) << "Using Key"; if ( !cwKey ) return; cwKey->immediatelyStop(); } void CWKeyer::stopTimerImplt() { FCT_IDENTIFICATION; if ( timer ) { timer->stop(); timer->deleteLater(); timer = nullptr; } } void CWKeyer::keyErrorHandler(const QString &main, const QString &detail) { FCT_IDENTIFICATION; emit cwKeyerError(main, detail); closeImpl(); } void CWKeyer::cwKeyWPMChangedHandler(qint32 wpm) { FCT_IDENTIFICATION; emit cwKeyWPMChanged(wpm); } void CWKeyer::cwKeyEchoTextHandler(const QString &text) { FCT_IDENTIFICATION; emit cwKeyEchoText(text); } void CWKeyer::cwKeyHWButton1PressedHandler() { emit cwKeyHWButton(1); } void CWKeyer::cwKeyHWButton2PressedHandler() { emit cwKeyHWButton(2); } void CWKeyer::cwKeyHWButton3PressedHandler() { emit cwKeyHWButton(3); } void CWKeyer::cwKeyHWButton4PressedHandler() { emit cwKeyHWButton(4); } CWKeyer::CWKeyer(QObject *parent ) : QObject(parent), cwKey(nullptr), timer(nullptr) { FCT_IDENTIFICATION; } CWKeyer::~CWKeyer() { FCT_IDENTIFICATION; if ( cwKey ) { cwKey->deleteLater(); } } ================================================ FILE: cwkey/CWKeyer.h ================================================ #ifndef QLOG_CWKEY_CWKEYER_H #define QLOG_CWKEY_CWKEYER_H #include #include "cwkey/drivers/CWKey.h" #include "data/CWKeyProfile.h" class CWKeyer : public QObject { Q_OBJECT public: static CWKeyer* instance() { static CWKeyer instance; return &instance; }; void stopTimer(); signals: void cwKeyerError(QString, QString); void cwKeyConnected(QString); void cwKeyDisconnected(); void cwKeyWPMChanged(qint32); void cwKeyEchoText(QString); void cwKeyHWButton(int); void cwKeyHWHaltPressed(); public slots: void start(); void update(); void open(); void close(); bool canStopSending(); bool canEchoChar(); bool rigMustConnected(); bool canSetSpeed(); void setSpeed(const qint16 wpm); void sendText(const QString&); void immediatelyStop(); private slots: void openImpl(); void closeImpl(); void setSpeedImpl(const qint16 wpm); void sendTextImpl(const QString&); void immediatelyStopImpl(); void stopTimerImplt(); void keyErrorHandler(const QString&, const QString&); void cwKeyWPMChangedHandler(qint32); void cwKeyEchoTextHandler(const QString&); void cwKeyHWButton1PressedHandler(); void cwKeyHWButton2PressedHandler(); void cwKeyHWButton3PressedHandler(); void cwKeyHWButton4PressedHandler(); private: explicit CWKeyer(QObject *parent = nullptr); ~CWKeyer(); void __closeCWKey(); void __openCWKey(); CWKey *cwKey; CWKeyProfile connectedCWKeyProfile; QMutex cwKeyLock; QTimer* timer; }; #endif // QLOG_CWKEY_CWKEYER_H ================================================ FILE: cwkey/drivers/CWCatKey.cpp ================================================ #include "CWCatKey.h" #include "core/debug.h" #include "rig/Rig.h" MODULE_IDENTIFICATION("qlog.cwkey.driver.cwcatkey"); CWCatKey::CWCatKey(const CWKey::CWKeyModeID mode, const qint32 defaultSpeed, QObject *parent) : CWKey(mode, defaultSpeed, parent), isKeyConnected(false) { FCT_IDENTIFICATION; } CWCatKey::~CWCatKey() { FCT_IDENTIFICATION; } bool CWCatKey::open() { FCT_IDENTIFICATION; qCDebug(runtime) << "Waiting for Command Mutex"; QMutexLocker locker(&commandMutex); __close(); /********************************/ /* Test if any rig is connected */ /********************************/ if ( !Rig::instance()->isRigConnected() ) { qWarning() << "Rig is not connected"; lastErrorText = tr("No Rig is connected"); __close(); return false; } /**************************************/ /* Test if the rig has Morse over CAT */ /**************************************/ if ( !Rig::instance()->isMorseOverCatSupported() ) { qWarning() << "Rig does not support Morse over CAT"; lastErrorText = tr("Rig does not support Morse over CAT"); __close(); return false; } /********************************************/ /* Rig must be in CW mode */ /* swith the rig to CW mode ? */ /* Maybe Yes - we will see users reaction */ /********************************************/ //it has the side-effect that every time you connect, // it causes to switch to CW when the key is assigned. // So let's disable it and see what's worse //Rig::instance()->setMode("CW", QString()); /*******************/ /* set default WPM */ /*******************/ Rig::instance()->setKeySpeed(defaultWPMSpeed); /************************/ /* Test if hamlib > 4.0 */ /* Stop sending feature */ /************************/ #if (HAMLIBVERSION_MAJOR >= 4) stopSendingCap = 1; #endif rigMustConnectedCap = true; isKeyConnected = true; canSetKeySpeed = true; lastErrorText = QString(); connect(Rig::instance(), &Rig::keySpeedChanged, this, &CWCatKey::rigKeySpeedChanged); return true; } bool CWCatKey::close() { FCT_IDENTIFICATION; QMutexLocker locker(&commandMutex); isKeyConnected = false; return true; } QString CWCatKey::lastError() { FCT_IDENTIFICATION; return lastErrorText; } bool CWCatKey::sendText(const QString &text) { FCT_IDENTIFICATION; if ( !isKeyConnected ) { qCDebug(runtime) << "Cannot send "; emit keyError(tr("Cannot send Text to Rig"), tr("Keyer is not connected")); return false; } if ( !Rig::instance()->isRigConnected() ) { qCDebug(runtime) << "Cannot send"; emit keyError(tr("Cannot send Text to Rig"), tr("Rig is not connected")); return false; } if ( !Rig::instance()->isMorseOverCatSupported() ) { qCDebug(runtime) << "Cannot send"; emit keyError(tr("Cannot send Text to Rig"), tr("Rig does not support Morse over CAT")); return false; } QMutexLocker locker(&commandMutex); Rig::instance()->sendMorse(text); return true; } bool CWCatKey::setWPM(const qint16 wpm) { FCT_IDENTIFICATION; if ( !isKeyConnected ) { qCDebug(runtime) << "Cannot set WPM "; emit keyError(tr("Cannot set Keyer Speed"), tr("Keyer is not connected")); return false; } if ( !Rig::instance()->isRigConnected() ) { qCDebug(runtime) << "Cannot set WPM"; emit keyError(tr("Cannot set Keyer Speed"), tr("Rig is not connected")); return false; } QMutexLocker locker(&commandMutex); Rig::instance()->setKeySpeed(wpm); //cat can echo a new Speed therefore // emit keyChangedWPMSpeed is not emitted return true; } bool CWCatKey::immediatelyStop() { FCT_IDENTIFICATION; if ( !isKeyConnected ) { qCDebug(runtime) << "Cannot stop"; emit keyError(tr("Cannot stop Text Sending"), tr("Keyer is not connected")); return false; } if ( !Rig::instance()->isRigConnected() ) { qCDebug(runtime) << "Cannot stop"; emit keyError(tr("Cannot stop Text Sending"), tr("Rig is not connected")); return false; } if ( !Rig::instance()->isMorseOverCatSupported() ) { qCDebug(runtime) << "Cannot stop"; emit keyError(tr("Cannot stop Text Sending"), tr("Rig does not support Morse over CAT")); return false; } QMutexLocker locker(&commandMutex); Rig::instance()->stopMorse(); return true; } void CWCatKey::__close() { FCT_IDENTIFICATION; disconnect(Rig::instance(), &Rig::keySpeedChanged, this, &CWCatKey::rigKeySpeedChanged); isKeyConnected = false; } void CWCatKey::rigKeySpeedChanged(VFOID, unsigned int wpm) { FCT_IDENTIFICATION; emit keyChangedWPMSpeed(wpm); } ================================================ FILE: cwkey/drivers/CWCatKey.h ================================================ #ifndef QLOG_CWKEY_DRIVERS_CWCATKEY_H #define QLOG_CWKEY_DRIVERS_CWCATKEY_H #include #include #include "CWKey.h" #include "rig/Rig.h" class CWCatKey : public CWKey { Q_OBJECT public: explicit CWCatKey(const CWKey::CWKeyModeID mode, const qint32 defaultSpeed, QObject *parent = nullptr); virtual ~CWCatKey(); virtual bool open() override; virtual bool close() override; virtual QString lastError() override; virtual bool sendText(const QString &text) override; virtual bool setWPM(const qint16 wpm) override; virtual bool immediatelyStop() override; private: QMutex commandMutex; bool isKeyConnected; QString lastErrorText; //user only in open part of communication void __close(); private slots: void rigKeySpeedChanged(VFOID, unsigned int); }; #endif // QLOG_CWKEY_DRIVERS_CWCATKEY_H ================================================ FILE: cwkey/drivers/CWDaemonKey.cpp ================================================ #include "CWDaemonKey.h" #include "core/debug.h" MODULE_IDENTIFICATION("qlog.cwkey.driver.cwdaemonkey"); CWDaemonKey::CWDaemonKey(const QString &hostname, const quint16 port, const CWKey::CWKeyModeID mode, const qint32 defaultSpeed, qint32 sidetoneFrequency, QObject *parent) : CWKey(mode, defaultSpeed, parent), CWKeyUDPInterface(hostname, port), isOpen(false), ESCChar(27), sidetoneFrequency(sidetoneFrequency) { FCT_IDENTIFICATION; stopSendingCap = true; canSetKeySpeed = true; printKeyCaps(); } bool CWDaemonKey::open() { FCT_IDENTIFICATION; isOpen = isSocketReady(); if ( isOpen && sidetoneFrequency > 0 ) { QString toneCmd(ESCChar + QString("3") + QString::number(sidetoneFrequency)); sendData(toneCmd.toLatin1()); } return isOpen; } bool CWDaemonKey::close() { FCT_IDENTIFICATION; isOpen = false; return true; } QString CWDaemonKey::lastError() { FCT_IDENTIFICATION; qCDebug(runtime) << socket.error(); qCDebug(runtime) << lastLogicalError; return (lastLogicalError.isEmpty()) ? socket.errorString() : lastLogicalError; } bool CWDaemonKey::sendText(const QString &text) { FCT_IDENTIFICATION; qCDebug(function_parameters) << text; if ( text.isEmpty() ) return true; if ( !isOpen ) { qCWarning(runtime) << "Key is not connected"; lastLogicalError = tr("Keyer is not connected"); emit keyError(tr("Cannot send Text"), lastLogicalError); return false; } int pos = 0; QRegularExpressionMatchIterator it = speedMarkerRE().globalMatch(text); while ( it.hasNext() ) { QRegularExpressionMatch match = it.next(); QString segment = text.mid(pos, match.capturedStart() - pos); segment.remove('\n'); if ( !segment.isEmpty() ) sendData(segment.toLatin1()); setWPM(applySpeedMarker(match.captured(1))); pos = match.capturedEnd(); } QString lastSegment = text.mid(pos); lastSegment.remove('\n'); if ( !lastSegment.isEmpty() ) sendData(lastSegment.toLatin1()); return true; } bool CWDaemonKey::setWPM(const qint16 wpm) { FCT_IDENTIFICATION; qCDebug(function_parameters) << wpm; if ( !isOpen ) { qCWarning(runtime) << "Key is not connected"; lastLogicalError = tr("Keyer is not connected"); emit keyError(tr("Cannot set Keyer Speed"), lastLogicalError); return false; } currentWPM = wpm; QString sentString(ESCChar + QString("2") + QString::number(wpm)); emit keyChangedWPMSpeed(wpm); return (sendData(sentString.toLatin1()) > 0) ? true : false; } bool CWDaemonKey::immediatelyStop() { FCT_IDENTIFICATION; if ( !isOpen ) { qCWarning(runtime) << "Key is not connected"; lastLogicalError = tr("Keyer is not connected"); emit keyError(tr("Cannot stop Text Sending"), lastLogicalError); return false; } QString sentString(ESCChar + QString("4")); return (sendData(sentString.toLatin1()) > 0) ? true : false; } ================================================ FILE: cwkey/drivers/CWDaemonKey.h ================================================ #ifndef QLOG_CWKEY_DRIVERS_CWDAEMONKEY_H #define QLOG_CWKEY_DRIVERS_CWDAEMONKEY_H #include #include "CWKey.h" class CWDaemonKey : public CWKey, protected CWKeyUDPInterface { Q_OBJECT public: explicit CWDaemonKey(const QString &hostname, const quint16 port, const CWKey::CWKeyModeID mode, const qint32 defaultSpeed, qint32 sidetoneFrequency, QObject *parent = nullptr); virtual ~CWDaemonKey(){}; virtual bool open() override; virtual bool close() override; virtual QString lastError() override; virtual bool sendText(const QString &text) override; virtual bool setWPM(const qint16 wpm) override; virtual bool immediatelyStop() override; protected: QString lastLogicalError; bool isOpen; const QChar ESCChar; qint32 sidetoneFrequency; }; #endif // QLOG_CWKEY_DRIVER_CWDAEMONKEY_H ================================================ FILE: cwkey/drivers/CWDummyKey.cpp ================================================ #include "CWDummyKey.h" #include "core/debug.h" MODULE_IDENTIFICATION("qlog.cwkey.driver.cwdummykey"); CWDummyKey::CWDummyKey(QObject *parent) : CWKey(CWKey::IAMBIC_B, 25, parent), isUsed(false) { FCT_IDENTIFICATION; canSetKeySpeed = true; } bool CWDummyKey::open() { FCT_IDENTIFICATION; qInfo() << "Key is Connected"; isUsed = true; setWPM(defaultWPMSpeed); return true; } bool CWDummyKey::close() { FCT_IDENTIFICATION; qInfo() << "Key is Disconnected"; isUsed = false; return true; } bool CWDummyKey::sendText(const QString &text) { FCT_IDENTIFICATION; if ( isUsed ) { qInfo() << "Sending " << stripSpeedMarkers(text); } return true; } bool CWDummyKey::setWPM(const qint16 wpm) { FCT_IDENTIFICATION; if ( !isUsed ) return true; qInfo() << "Setting Speed " << wpm; emit keyChangedWPMSpeed(wpm); // dummy does not echo a new Speed //therefore keyChangedWPMSpeed informs the rest for QLog that //Key speed has been changed return true; } QString CWDummyKey::lastError() { FCT_IDENTIFICATION; return QString(); } bool CWDummyKey::immediatelyStop() { FCT_IDENTIFICATION; if ( isUsed ) { qInfo() << "immediately Stop"; } return true; } ================================================ FILE: cwkey/drivers/CWDummyKey.h ================================================ #ifndef QLOG_CWKEY_DRIVERS_CWDUMMYKEY_H #define QLOG_CWKEY_DRIVERS_CWDUMMYKEY_H #include "CWKey.h" class CWDummyKey : public CWKey { Q_OBJECT public: explicit CWDummyKey(QObject *parent = nullptr); virtual bool open() override; virtual bool close() override; virtual bool sendText(const QString &text) override; virtual bool setWPM(const qint16 wpm) override; virtual QString lastError() override; virtual bool immediatelyStop() override; private: bool isUsed; }; #endif // QLOG_CWKEY_DRIVER_CWDUMMYKEY_H ================================================ FILE: cwkey/drivers/CWFldigiKey.cpp ================================================ #include #include "CWFldigiKey.h" #include "core/debug.h" MODULE_IDENTIFICATION("qlog.cwkey.driver.cwfldigikey"); //response timeout in ms #define RESPONSE_TIMEOUT 10000 CWFldigiKey::CWFldigiKey(const QString &hostname, const quint16 port, const CWKey::CWKeyModeID mode, const qint32 defaultSpeed, QObject *parent) : CWKey(mode, defaultSpeed, parent), isOpen(false), nam(new QNetworkAccessManager(this)), hostname(hostname), port(port), RXString("^r"), transmittingText(false) { FCT_IDENTIFICATION; } bool CWFldigiKey::open() { FCT_IDENTIFICATION; if ( isOpen ) { return true; } QByteArray resp; // Check if QLog is connecting to FLDigi // And FLDigi contains all necessary commands if ( !sendXMLRPCCall("fldigi.list", resp) ) { qCDebug(runtime) << "Connection error"; return false; } if ( resp.contains("fldigi.list") && resp.contains("text.add_tx") && resp.contains("text.clear_tx") && resp.contains("main.tx") ) { qCDebug(runtime) << "Connection check OK"; } else { qCDebug(runtime) << "Connection checks failed"; lastLogicalError = tr("Connected device is not FLDigi"); return false; } if ( resp.contains("main.abort") ) { qCDebug(runtime) << "Enabling stopSendingCap"; stopSendingCap = true; } if ( resp.contains("tx.get_data") ) { qCDebug(runtime) << "Enabling echoCharsCap"; echoCharsCap = true; } printKeyCaps(); isOpen = true; getEcho(); return true; } bool CWFldigiKey::close() { FCT_IDENTIFICATION; isOpen = false; lastLogicalError = QString(); return true; } QString CWFldigiKey::lastError() { FCT_IDENTIFICATION; return lastLogicalError; } bool CWFldigiKey::sendText(const QString &text) { FCT_IDENTIFICATION; qCDebug(function_parameters) << text; if ( text.isEmpty() ) return true; if ( !isOpen ) { qCWarning(runtime) << "Key is not connected"; lastLogicalError = tr("Keyer is not connected"); emit keyError(tr("Cannot send Text to FLDigi"), lastLogicalError); return false; } QByteArray resp; if ( !transmittingText ) { if ( !sendXMLRPCCall("text.clear_tx", resp) ) { emit keyError(tr("Cannot send the Clear command to FLDigi"), lastError()); return false; } if ( !sendXMLRPCCall("main.tx", resp) ) { emit keyError(tr("Cannot send the TX command to FLDigi"), lastError()); return false; } transmittingText = true; } QList> params; QString chpString = stripSpeedMarkers(text); if ( chpString.contains("\n") ) { chpString.replace("\n", RXString); transmittingText = false; } params << QPair("string", chpString ); if ( !sendXMLRPCCall("text.add_tx", resp, ¶ms) ) { transmittingText = false; emit keyError(tr("Cannot send the Text command to FLDigi"), lastError()); return false; } return true; } bool CWFldigiKey::setWPM(const qint16) { FCT_IDENTIFICATION; // currently I don't know which FLDigi function I should call. return true; } bool CWFldigiKey::immediatelyStop() { FCT_IDENTIFICATION; if ( ! stopSendingCap ) { qCDebug(runtime) << "STOP is not supported by FLDigi"; return true; } if ( !isOpen ) { qCWarning(runtime) << "Key is not connected"; lastLogicalError = tr("Keyer is not connected"); emit keyError(tr("Cannot send the Stop command to FLDigi"), lastLogicalError); return false; } QByteArray resp; if ( !sendXMLRPCCall("main.abort", resp) ) { emit keyError(tr("Cannot send the Abort command to FLDigi"), lastError()); return false; } if ( !sendXMLRPCCall("text.clear_tx", resp) ) { emit keyError(tr("Cannot send the Clear command to FLDigi"), lastError()); return false; } return true; } void CWFldigiKey::getEcho() { FCT_IDENTIFICATION; if ( ! echoCharsCap ) { qCDebug(runtime) << "Echo Char is not supported by FLDigi"; return; } if ( !isOpen ) { qCDebug(runtime) << "Key is not connected"; return; } QByteArray resp; if ( !sendXMLRPCCall("tx.get_data", resp) ) { qWarning() << "Cannot receive the TX Data from FLDigi"; emit keyError(tr("Cannot receive data from FLDigi"), lastError()); return; } qsizetype start = resp.indexOf(""); qsizetype stop = resp.indexOf(""); if ( start == -1 || stop == -1 || stop < start ) { qCDebug(runtime) << "based64 block not found"; // TODO: EMIT ERROR and disconnect? currently without an error, I will see return; } QString echoString(QByteArray::fromBase64(resp.mid(start + 8, stop - start - 8))); qCDebug(runtime) << "\tEcho String" << echoString; emit keyEchoText(echoString); // check periodically QTimer::singleShot(1 * 1000, this, &CWFldigiKey::getEcho); } bool CWFldigiKey::sendXMLRPCCall(const QString & methodName, QByteArray &response, const QList> *params) { FCT_IDENTIFICATION; QEventLoop loop; QString ret; QXmlStreamWriter writer(&ret); QNetworkRequest request(QUrl(QString("http://%1:%2/RPC").arg(hostname, QString::number(port)))); QNetworkReply* reply = nullptr; QTimer timer; timer.setSingleShot(true); response = QByteArray(); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/xml"); writer.writeStartDocument(); writer.writeStartElement("methodCall"); writer.writeTextElement("methodName", methodName); if ( params && !params->isEmpty() ) { writer.writeStartElement("params"); for( const QPair& param : static_cast>&>(*params) ) { writer.writeStartElement("param"); writer.writeStartElement("value"); writer.writeTextElement(param.first, param.second); writer.writeEndElement(); writer.writeEndElement(); } writer.writeEndElement(); } writer.writeEndElement(); writer.writeEndDocument(); qCDebug(runtime) << ret.toLocal8Bit(); reply= nam->post(request, ret.toLocal8Bit()); // blocking call is used connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); connect(reply , &QNetworkReply::finished, &loop, &QEventLoop::quit); timer.start(RESPONSE_TIMEOUT); // timer for logical timeout - in ms loop.exec(); // Timeout occurred if (!timer.isActive()) { disconnect(reply , &QNetworkReply::finished, &loop, &QEventLoop::quit); qWarning() << "XMLRPC Call Timeout" << RESPONSE_TIMEOUT << "ms"; lastLogicalError = tr("FLDigi connection timeout"); reply->abort(); reply->deleteLater(); return false; } timer.stop(); int replyStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); if ( reply->error() != QNetworkReply::NoError || replyStatusCode < 200 || replyStatusCode >= 300 ) { qWarning() << "XMLRPC Call Reply Error" << reply->errorString(); qCDebug(runtime) << "HTTP Status Code" << replyStatusCode; lastLogicalError = tr("FLDigi connection error"); reply->deleteLater(); return false; } response = reply->readAll(); qCDebug(runtime) << response; if ( response.contains("") ) { qWarning() << "XMLRPC Call Logical Error" << response; lastLogicalError = tr("FLDigi command error"); reply->deleteLater(); return false; } reply->deleteLater(); return true; } ================================================ FILE: cwkey/drivers/CWFldigiKey.h ================================================ #ifndef QLOG_CWKEY_DRIVERS_CWFLDIGIKEY_H #define QLOG_CWKEY_DRIVERS_CWFLDIGIKEY_H #include #include #include "CWKey.h" class CWFldigiKey : public CWKey { public: CWFldigiKey(const QString &hostname, const quint16 port, const CWKey::CWKeyModeID mode, const qint32 defaultSpeed, QObject *parent = nullptr); virtual ~CWFldigiKey(){ nam->deleteLater();}; virtual bool open() override; virtual bool close() override; virtual QString lastError() override; virtual bool sendText(const QString &text) override; virtual bool setWPM(const qint16 wpm) override; virtual bool immediatelyStop() override; protected: QString lastLogicalError; bool isOpen; QNetworkAccessManager *nam; QString hostname; quint16 port; const QString RXString; bool transmittingText; private: bool sendXMLRPCCall(const QString&, QByteArray &, const QList>* = nullptr); private slots: void getEcho(); }; #endif // QLOG_CWKEY_DRIVERS_CWFLDIGIKEY_H ================================================ FILE: cwkey/drivers/CWKey.cpp ================================================ #include #include #include "CWKey.h" #include "core/debug.h" MODULE_IDENTIFICATION("qlog.cwkey.driver.cwkey"); CWKey::CWKey(CWKeyModeID mode, qint32 defaultWPM, QObject *parent) : QObject(parent), keyMode(mode), defaultWPMSpeed(defaultWPM), currentWPM(static_cast(defaultWPM)), stopSendingCap(false), echoCharsCap(false), rigMustConnectedCap(false), canSetKeySpeed(false) { FCT_IDENTIFICATION; qCDebug(function_parameters) << mode << defaultWPM; } void CWKey::printKeyCaps() { FCT_IDENTIFICATION; qCDebug(runtime) << "stopSendingCap" << stopSendingCap; qCDebug(runtime) << "echoCharsCap" << echoCharsCap; qCDebug(runtime) << "rigMustConnectedCap" << rigMustConnectedCap; qCDebug(runtime) << "canSetKeySpeed" << canSetKeySpeed; } CWKey::CWKeyTypeID CWKey::intToTypeID(int i) { FCT_IDENTIFICATION; if ( i < DUMMY_KEYER || i > LAST_MODEL ) { qCWarning(runtime) << "Unknown Mode" << i; return LAST_MODEL; } return static_cast(i); } CWKey::CWKeyModeID CWKey::intToModeID(int i) { FCT_IDENTIFICATION; if ( i < SINGLE_PADDLE || i > LAST_MODE ) { qCWarning(runtime) << "Unknown Mode" << i; return LAST_MODE; } return static_cast(i); } bool CWKey::isNetworkKey(const CWKeyTypeID &type) { FCT_IDENTIFICATION; bool ret = (type == CWDAEMON_KEYER || type == FLDIGI_KEYER ); qCDebug(runtime) << ret; return ret; } const QRegularExpression &CWKey::speedMarkerRE() { static QRegularExpression re("<(\\++|-+)>"); return re; } qint16 CWKey::applySpeedMarker(const QString &markerCapture) { qint16 delta = static_cast( markerCapture.length() * ( markerCapture[0] == QLatin1Char('+') ? 5 : -5 )); currentWPM = qBound(minWPM(), static_cast(currentWPM + delta), maxWPM()); return currentWPM; } QString CWKey::stripSpeedMarkers(const QString &text) { QString result(text); result.remove(speedMarkerRE()); return result; } QDataStream& operator>>(QDataStream &in, CWKey::CWKeyModeID &v) { int i; in >> i; v = static_cast(i); return in; } QDataStream& operator<<(QDataStream &out, const CWKey::CWKeyModeID &v) { out << static_cast(v); return out; } QDataStream& operator>>(QDataStream &in, CWKey::CWKeyTypeID &v) { int i; in >> i; v = static_cast(i); return in; } QDataStream& operator<<(QDataStream &out, const CWKey::CWKeyTypeID &v) { out << static_cast(v); return out; } CWKeySerialInterface::CWKeySerialInterface(const QString &portName, const qint32 baudrate, const qint32 timeout) : timeout(timeout) { FCT_IDENTIFICATION; qCDebug(function_parameters) << portName << baudrate << timeout; serial.setPortName(portName); if ( ! serial.setBaudRate(baudrate, QSerialPort::AllDirections) ) { qWarning() << "Cannot set Baudrate for Serial port"; } if ( ! serial.setDataBits(QSerialPort::Data8) ) { qWarning() << "Cannot set Data Bits for Serial port"; } if ( ! serial.setParity(QSerialPort::NoParity) ) { qWarning() << "Cannot set Parity for Serial port"; } if ( ! serial.setFlowControl(QSerialPort::NoFlowControl) ) { qWarning() << "Cannot set Flow Control for Serial port"; } if ( ! serial.setStopBits(QSerialPort::TwoStop) ) { qWarning() << "Cannot set Stop Bits for Serial port"; } } qint64 CWKeySerialInterface::sendDataAndWait(const QByteArray &data) { FCT_IDENTIFICATION; qCDebug(runtime) << "Waiting for Port Mutex"; QMutexLocker locker(&portMutex); if ( ! serial.isOpen() ) { qCDebug(runtime) << "Serial port is not opened"; return - 1; } qCDebug(runtime) << "\t<<<<<< SND Sync: " << data; qint64 ret = serial.write(data); if ( ret == -1 ) { qWarning() << "Serial Port Error: " << serial.errorString() << serial.error(); } else { if ( !serial.waitForBytesWritten(timeout) ) { qCDebug(runtime) << "Serial Port Timeout"; return -1; } } return ret; } qint64 CWKeySerialInterface::receiveDataAndWait(QByteArray &data) { FCT_IDENTIFICATION; qCDebug(runtime) << "Waiting for Port Mutex"; QMutexLocker locker(&portMutex); if ( ! serial.isOpen() ) { qCDebug(runtime) << "Serial port is not opened"; return - 1; } if ( serial.waitForReadyRead(timeout)) { data = serial.readAll(); while ( serial.waitForReadyRead(10) ) { data += serial.readAll(); } } else { qCDebug(runtime) << "Serial Port Timeout"; return -1; } qCDebug(runtime) << "\t>>>>>> RCV Sync: " << data; return data.size(); } qint64 CWKeySerialInterface::writeAsyncData(const QByteArray &data) { FCT_IDENTIFICATION; qCDebug(runtime) << "Waiting for Port Mutex"; QMutexLocker locker(&portMutex); if ( ! serial.isOpen() ) { qCDebug(runtime) << "Serial port is not opened"; return - 1; } qCDebug(runtime) << "\t<<<<<< SND Async:" << data; qint64 ret = serial.write(data); if ( ret == -1 ) { qWarning() << "Serial Port Error: " << serial.errorString() << serial.error(); } return ret; } CWKeyUDPInterface::CWKeyUDPInterface(const QString &hostname, const quint16 port) : CWKeyIPInterface(hostname, port) { FCT_IDENTIFICATION; } qint64 CWKeyUDPInterface::sendData(const QByteArray &data) { FCT_IDENTIFICATION; qCDebug(runtime) << "\t<<<<<< SND: " << data << "(" << serverName << "port:" << port << ")"; return socket.writeDatagram(data, serverName, port); } bool CWKeyUDPInterface::isSocketReady() { FCT_IDENTIFICATION; qCDebug(runtime) << hasIPAddress; return hasIPAddress; } CWKeyIPInterface::CWKeyIPInterface(const QString &hostname, const quint16 port) : serverName(QHostAddress()), port(port), hasIPAddress(false) { FCT_IDENTIFICATION; qCDebug(function_parameters) << hostname << port; QHostInfo info = QHostInfo::fromName(hostname); if (info.error() == QHostInfo::NoError) { serverName = QHostAddress(info.addresses().at(0)); qCDebug(runtime) << "Using IP address" << serverName; hasIPAddress = true; } } ================================================ FILE: cwkey/drivers/CWKey.h ================================================ #ifndef QLOG_CWKEY_DRIVERS_CWKEY_H #define QLOG_CWKEY_DRIVERS_CWKEY_H #include #include #include #include class CWKeySerialInterface { public: explicit CWKeySerialInterface(const QString &portName, const qint32 baudrate, const qint32 timeout); ~CWKeySerialInterface() {}; protected: virtual qint64 sendDataAndWait(const QByteArray &data); virtual qint64 receiveDataAndWait(QByteArray &data); virtual qint64 writeAsyncData(const QByteArray &); QSerialPort serial; qint32 timeout; QMutex portMutex; }; class CWKeyIPInterface { public: explicit CWKeyIPInterface(const QString &hostname, const quint16 port); ~CWKeyIPInterface() {}; protected: virtual qint64 sendData(const QByteArray &data) = 0; virtual bool isSocketReady() = 0; QHostAddress serverName; quint16 port; bool hasIPAddress; }; class CWKeyUDPInterface : public CWKeyIPInterface { public: explicit CWKeyUDPInterface(const QString &hostname, const quint16 port); ~CWKeyUDPInterface() {}; protected: virtual qint64 sendData(const QByteArray &data) override; virtual bool isSocketReady() override; QUdpSocket socket; }; class CWKey : public QObject { Q_OBJECT; public: enum CWKeyTypeID { DUMMY_KEYER = 0, WINKEY_KEYER = 1, MORSEOVERCAT = 2, CWDAEMON_KEYER = 3, FLDIGI_KEYER = 4, LAST_MODEL = 4 }; enum CWKeyModeID { SINGLE_PADDLE = 0, IAMBIC_A = 1, IAMBIC_B = 2, ULTIMATE = 3, LAST_MODE = 4 }; signals: void keyError(QString, QString); void keyChangedWPMSpeed(qint32); void keyEchoText(QString); void keyHWButton1Pressed(); void keyHWButton2Pressed(); void keyHWButton3Pressed(); void keyHWButton4Pressed(); public: explicit CWKey(CWKeyModeID mode, qint32 defaultWPM, QObject *parent = nullptr); virtual ~CWKey() {}; virtual bool open() = 0; virtual bool close() = 0; virtual bool sendText(const QString &text) = 0; virtual bool setWPM(const qint16 wpm) = 0; virtual QString lastError() = 0; virtual bool immediatelyStop() = 0; virtual bool canStopSending() { return stopSendingCap;} virtual bool canEchoChar() { return echoCharsCap;} virtual bool mustRigConnected() { return rigMustConnectedCap;} virtual bool canSetSpeed() { return canSetKeySpeed; }; void printKeyCaps(); static CWKeyTypeID intToTypeID(int); static CWKeyModeID intToModeID(int); static bool isNetworkKey(const CWKeyTypeID &type); static QString stripSpeedMarkers(const QString &text); virtual qint16 minWPM() const { return 5; } virtual qint16 maxWPM() const { return 99; } friend QDataStream& operator<<(QDataStream& out, const CWKeyTypeID& v); friend QDataStream& operator>>(QDataStream& in, CWKeyTypeID& v); friend QDataStream& operator<<(QDataStream& out, const CWKeyModeID& v); friend QDataStream& operator>>(QDataStream& in, CWKeyModeID& v); protected: CWKeyModeID keyMode; qint32 defaultWPMSpeed; qint16 currentWPM; bool stopSendingCap; bool echoCharsCap; bool rigMustConnectedCap; bool canSetKeySpeed; static const QRegularExpression &speedMarkerRE(); qint16 applySpeedMarker(const QString &markerCapture); }; #endif // QLOG_CWKEY_DRIVERS_CWKEY_H ================================================ FILE: cwkey/drivers/CWWinKey.cpp ================================================ #include #include "CWWinKey.h" #include "core/debug.h" /* Based on WinKey Spec * https://k1el.tripod.com/files/Winkey10.pdf * https://k1el.tripod.com/WinkeyUSBman.pdf */ MODULE_IDENTIFICATION("qlog.cwkey.driver.cwwinkey"); CWWinKey::CWWinKey(const QString &portName, const qint32 baudrate, const CWKey::CWKeyModeID mode, const qint32 defaultSpeed, bool paddleSwap, bool paddleOnlySidetone, qint32 sidetoneFrequency, QObject *parent) : CWKey(mode, defaultSpeed, parent), CWKeySerialInterface(portName, baudrate, 5000), isInHostMode(false), xoff(false), paddleSwap(paddleSwap), paddleOnlySidetone(paddleOnlySidetone), sidetoneFrequency(sidetoneFrequency), version(0) { FCT_IDENTIFICATION; minWPMRange = defaultSpeed - 15; // Winkey has 31 steps for POT, it means that 15 steps to left // and 15 steps to right if ( minWPMRange <= 0 ) minWPMRange = 1; stopSendingCap = true; echoCharsCap = true; canSetKeySpeed = true; } CWWinKey::~CWWinKey() { FCT_IDENTIFICATION; } bool CWWinKey::open() { FCT_IDENTIFICATION; qCDebug(runtime) << "Waiting for Command Mutex"; QMutexLocker locker(&commandMutex); __close(); /***************/ /* Open Port */ /***************/ qCDebug(runtime) << "Opening Serial" << serial.portName(); if ( !serial.open(QIODevice::ReadWrite) ) { qWarning() << "Cannot open " << serial.portName() << " Error code " << serial.error(); return false; } serial.setReadBufferSize(1); // WinKey Responses are 1B. // It is important to set correct Buffer size beucase message below /* https://forum.qt.io/topic/137268/solved-qserialport-readyread-not-emitted-qserialport-waitforreadyread-always-return-false-with-ch340 monegator Jun 16, 2022, 3:36 PM Hello there, i opened this thread so that others facing the same issue may find a solution. In the past we always used USB-UART cables based on FTDI232 chips (TTL-232R-5V-WE). However, due to a huge price increase in the last couple of years (5€ for a cable became 35€, now dropped to 25€) we decided to ditch FTDI and bought cables that use the CH340 chip. And why shouldn't we, they cost less than 2€ each. Our software based on VB6 worked flawlessly, even better (reduced latency) however we had issues with our Qt5 based software (Qt 5.15.2). Even if the data is available (QSerialPort::bytesAvailable return values greater than zero) the readyRead signal is never emitted, and waitForReadyRead always return false. The issue was that readBufferSize was set to zero (default value), and there must be something in the interaction between CH340 driver, windows COM port object and Qt that prevented the event from being raised, but that was never an issue for FTDI cables. The solution was to set readBufferSize to 1. Once readBufferSize is different than zero readyRead works again. */ serial.setDataTerminalReady(true); serial.setRequestToSend(false); qCDebug(runtime) << "Serial port has been opened"; QThread::msleep(200); QByteArray cmd; /***********************/ /* Echo Test */ /* Testing whether */ /* the opposite site */ /* is Winkey */ /***********************/ qCDebug(runtime) << "Echo Test"; cmd.resize(3); cmd[0] = 0x00; cmd[1] = 0x04; cmd[2] = 0xF1u; if ( sendDataAndWait(cmd) != 3 ) { qWarning() << "Unexpected size of write response or communication error"; qCDebug(runtime) << lastError(); __close(); return false; } if ( receiveDataAndWait(cmd) < 1 ) { qWarning() << "Unexpected size of response or communication error"; qCDebug(runtime) << lastError(); __close(); return false; } if ( (unsigned char)cmd.at(0) != 0xF1 ) { qWarning() << "Connected device is not the Winkey type"; lastLogicalError = tr("Connected device is not WinKey"); __close(); return false; } qCDebug(runtime) << "Echo Test OK"; /********************/ /* Enable Host Mode */ /********************/ qCDebug(runtime) << "Enabling Host Mode"; cmd.resize(2); cmd[0] = 0x00; cmd[1] = 0x02; if ( sendDataAndWait(cmd) != 2 ) { qWarning() << "Unexpected size of write response or communication error"; qCDebug(runtime) << lastError(); __close(); return false; } /* Based on the WinKey Spec, it is needed to call read: The host must wait for this return code before any other commands or data can be sent to Winkeyer */ if ( receiveDataAndWait(cmd) < 1 ) { qWarning() << "Unexpected size of response or communication error"; qCDebug(runtime) << lastError(); __close(); return false; } version = (unsigned char)cmd.at(0); qCDebug(runtime) << "Winkey version" << version; lastLogicalError = QString(); qCDebug(runtime) << "Host Mode has been enabled"; /******************/ /* Mode Setting */ /******************/ qCDebug(runtime) << "Mode Setting"; cmd.resize(2); cmd[0] = 0x0E; cmd[1] = buildWKModeByte(); if ( sendDataAndWait(cmd) != 2 ) { qWarning() << "Unexpected size of write response or communication error"; qCDebug(runtime) << lastError(); __close(); return false; } //receiveDataAndWait(cmd); /* it is not needed to read here - no response */ qCDebug(runtime) << "Mode has been set"; /******************/ /* WK2 Mode Status*/ /******************/ if ( version >= 20 ) { qCDebug(runtime) << "WK2 PB Mode Setting"; cmd.resize(2); cmd[0] = 0x00; cmd[1] = 0x0B; if ( sendDataAndWait(cmd) != 2 ) { qWarning() << "Unexpected size of write response or communication error"; qCDebug(runtime) << lastError(); __close(); return false; } // receiveDataAndWait(cmd); /* it is not needed to read here - no response */ qCDebug(runtime) << "WK2 PB Mode has been set"; } QThread::msleep(200); /* Starting Async Flow for WinKey */ /* From this point, all Serial port functions must be Async */ connect(&serial, &QSerialPort::readyRead, this, &CWWinKey::handleReadyRead); connect(&serial, &QSerialPort::bytesWritten, this, &CWWinKey::handleBytesWritten); connect(&serial, &QSerialPort::errorOccurred, this, &CWWinKey::handleError); isInHostMode = true; /* Force send current status */ __sendStatusRequest(); /* Set POT Range */ __setPOTRange(); /* Sidetone Setting */ __setSidetone(!paddleOnlySidetone); /* Set Default value */ __setWPM(defaultWPMSpeed); return true; } bool CWWinKey::close() { FCT_IDENTIFICATION; qCDebug(runtime) << "Waiting for Command Mutex"; QMutexLocker locker(&commandMutex); __close(); return true; } bool CWWinKey::sendText(const QString &text) { FCT_IDENTIFICATION; qCDebug(function_parameters) << text; qCDebug(runtime) << "Waiting for Command Mutex"; QMutexLocker locker(&commandMutex); if ( !isInHostMode ) { qCWarning(runtime) << "Key is not in Host Mode"; emit keyError(tr("Cannot send Text to Rig"), tr("Keyer is not connected")); return false; } qCDebug(runtime) << "Waiting for WriteBuffer Mutex"; writeBufferMutex.lock(); qCDebug(runtime) << "Appending input string"; int pos = 0; QRegularExpressionMatchIterator it = speedMarkerRE().globalMatch(text); while ( it.hasNext() ) { QRegularExpressionMatch match = it.next(); QString segment = text.mid(pos, match.capturedStart() - pos); segment.remove('\n'); writeBuffer.append(segment.toLatin1()); qint16 newWPM = applySpeedMarker(match.captured(1)); writeBuffer.append(static_cast(0x1C)); // WinKey buffered speed change writeBuffer.append(static_cast(newWPM)); emit keyChangedWPMSpeed(newWPM); pos = match.capturedEnd(); } QString lastSegment = text.mid(pos); lastSegment.remove('\n'); writeBuffer.append(lastSegment.toLatin1()); writeBufferMutex.unlock(); tryAsyncWrite(); return true; } void CWWinKey::tryAsyncWrite() { FCT_IDENTIFICATION; qCDebug(runtime) << "Waiting for WriteBuffer Mutex"; writeBufferMutex.lock(); qCDebug(runtime) << "WBuffer Size: " << writeBuffer.size() << "; XOFF: " << xoff; if ( writeBuffer.isEmpty() || xoff ) { writeBufferMutex.unlock(); qCDebug(runtime) << "Skipping write call"; return; } qint64 size = writeAsyncData(QByteArray(writeBuffer.constData(),1)); if ( size != 1 ) { qWarning() << "Unexpected size of write response or communication error"; qCDebug(runtime) << lastError(); } writeBuffer.remove(0, size); writeBufferMutex.unlock(); QCoreApplication::processEvents(); } void CWWinKey::handleBytesWritten(qint64 bytes) { FCT_IDENTIFICATION; qCDebug(function_parameters) << bytes; tryAsyncWrite(); } void CWWinKey::handleReadyRead() { FCT_IDENTIFICATION; unsigned char rcvByte; qCDebug(runtime) << "Waiting for Port Mutex"; portMutex.lock(); qCDebug(runtime) << "Reading from Port"; serial.read((char*)&rcvByte,1); portMutex.unlock(); qCDebug(runtime) << "\t>>>>>> RCV Async:" << QByteArray::fromRawData((char*)(&rcvByte),1); if ( (rcvByte & 0xC0) == 0xC0 ) { qCDebug(runtime) << "\tStatus Information Message:"; xoff = false; if ( rcvByte == 0xC0 ) qCDebug(runtime) << "\t\tIdle"; else { if ( version >= 20 && rcvByte & 0x08 ) { qCDebug(runtime) << "\tPushButton Status Byte"; if ( rcvByte & 0x01 ) { qCDebug(runtime) << "\t\tButton1 pressed"; emit keyHWButton1Pressed(); } if ( rcvByte & 0x02 ) { qCDebug(runtime) << "\t\tButton2 pressed"; emit keyHWButton2Pressed(); } if ( rcvByte & 0x04 ) { qCDebug(runtime) << "\t\tButton3 pressed"; emit keyHWButton3Pressed(); } if ( rcvByte & 0x10 ) { qCDebug(runtime) << "\t\tButton4 pressed"; emit keyHWButton4Pressed(); } } else { qCDebug(runtime) << "\tStatus Byte"; if ( rcvByte & 0x01 ) { qCDebug(runtime) << "\t\tBuffer 2/3 full"; xoff = true; //slow down in sending Write Buffer - to block tryAsyncWrite } if ( rcvByte & 0x02 ) qCDebug(runtime) << "\t\tBrk-in"; if ( rcvByte & 0x04 ) qCDebug(runtime) << "\t\tKey Busy"; if ( rcvByte & 0x08 ) qCDebug(runtime) << "\t\tTunning"; if ( rcvByte & 0x0F ) qCDebug(runtime) << "\t\tWaiting"; } } } else if ( (rcvByte & 0xC0) == 0x80 ) { qint32 potValue = (rcvByte & 0x7F); qint32 potWPM = minWPMRange + potValue; qCDebug(runtime) << "\tPot: " << potValue << "; WPM=" << potWPM; setWPM(potWPM); } else { qCDebug(runtime) << "\tEcho Char"; emit keyEchoText(QString(char(rcvByte))); } tryAsyncWrite(); } void CWWinKey::handleError(QSerialPort::SerialPortError serialPortError) { FCT_IDENTIFICATION; QString detail = serial.errorString(); if ( serialPortError == QSerialPort::ReadError ) { qWarning() << "An I/O error occurred while reading: " << detail; } else if ( serialPortError == QSerialPort::WriteError ) { qWarning() << "An I/O error occurred while writing: " << detail; } else if ( serialPortError != QSerialPort::NoError ) { qWarning() << "An I/O error occurred: " << detail; } /* Emit error */ emit keyError(tr("Communication Error"), detail); } bool CWWinKey::setWPM(const qint16 wpm) { FCT_IDENTIFICATION; qCDebug(function_parameters) << wpm; qCDebug(runtime) << "Waiting for Command Mutex"; QMutexLocker locker(&commandMutex); bool ret = __setWPM(wpm); if ( ret ) { emit keyChangedWPMSpeed(wpm); //Winkey does not echo a new Speed //therefore keyChangedWPMSpeed informs the rest for QLog that //Key speed has been changed } return ret; } bool CWWinKey::__setWPM(const qint16 wpm) { FCT_IDENTIFICATION; qCDebug(function_parameters) << wpm; if ( !isInHostMode ) { qCWarning(runtime) << "Key is not in Host Mode"; emit keyError(tr("Cannot set Keyer Speed"), tr("Keyer is not connected")); return false; } QByteArray cmd; cmd.resize(2); cmd[0] = 0x02; cmd[1] = static_cast(wpm); qint64 size = writeAsyncData(cmd); if ( size != 2 ) { qWarning() << "Unexpected size of write response or communication error"; qCDebug(runtime) << lastError(); return false; } currentWPM = wpm; return true; } QString CWWinKey::lastError() { FCT_IDENTIFICATION; qCDebug(runtime) << serial.error(); qCDebug(runtime) << lastLogicalError; return (lastLogicalError.isEmpty()) ? serial.errorString() : lastLogicalError; } bool CWWinKey::immediatelyStop() { FCT_IDENTIFICATION; qCDebug(runtime) << "Waiting for Command Mutex"; QMutexLocker locker(&commandMutex); if ( !isInHostMode ) { qCWarning(runtime) << "Key is not in Host Mode"; emit keyError(tr("Cannot stop Text Sending"), tr("Keyer is not connected")); return false; } qCDebug(runtime) << "Waiting for WriteBuffer Mutex"; writeBufferMutex.lock(); qCDebug(runtime) << "Clearing Buffer"; writeBuffer.clear(); writeBufferMutex.unlock(); QByteArray cmd; cmd.resize(3); cmd[0] = 0x06; /* Stop */ cmd[1] = 0x01; cmd[2] = 0x0A; /* Clear */ qint64 size = writeAsyncData(cmd); if ( size != 3 ) { qWarning() << "Unexpected size of write response or communication error"; qCDebug(runtime) << lastError(); } return true; } void CWWinKey::__close() { FCT_IDENTIFICATION; qCDebug(runtime) << "Waiting for WriteBuffer Mutex"; writeBufferMutex.lock(); qCDebug(runtime) << "Clearing Buffer"; writeBuffer.clear(); writeBufferMutex.unlock(); if ( serial.isOpen() ) { /* Switch to Sync Mode */ disconnect(&serial, &QSerialPort::bytesWritten, this, &CWWinKey::handleBytesWritten); disconnect(&serial, &QSerialPort::errorOccurred, this, &CWWinKey::handleError); disconnect(&serial, &QSerialPort::readyRead, this, &CWWinKey::handleReadyRead); if ( isInHostMode ) { QByteArray cmd; /***********************/ /* clear buffer */ /***********************/ qCDebug(runtime) << "Clearing Buffer"; cmd.resize(3); cmd[0] = 0x06; /* Stop */ cmd[1] = 0x01; cmd[2] = 0x0A; /* Clear */ if ( sendDataAndWait(cmd) != 3 ) { qWarning() << "Unexpected size of write response or communication error"; qCDebug(runtime) << lastError(); } else { qCDebug(runtime) << "Buffered has been cleared"; } /***********************/ /* Disabling Host Mode */ /***********************/ qCDebug(runtime) << "Disabling Host Mode"; cmd.resize(2); cmd[0] = 0x00; cmd[1] = 0x03; if ( sendDataAndWait(cmd) != 2 ) { qWarning() << "Unexpected size of write response or communication error"; qCDebug(runtime) << lastError(); } else { qCDebug(runtime) << "Host Mode has been disabled"; } } QThread::msleep(200); serial.setDataTerminalReady(false); serial.close(); } else { qCDebug(runtime) << "Port is already closed"; } isInHostMode = false; xoff = false; version = 0; lastLogicalError = QString(); } unsigned char CWWinKey::buildWKModeByte() const { FCT_IDENTIFICATION; /* 7 (MSB) Disable Paddle watchdog 6 Paddle Echoback (1=Enabled, 0=Disabled) 5,4 Key Mode: 00 = Iambic B 01 = Iambic A 10 = Ultimatic 11 = Bug Mode 3 Paddle Swap (1=Swap, 0=Normal) 2 Serial Echoback (1=Enabled, 0=Disabled) 1 Autospace (1=Enabled, 0=Disabled) 0 (LSB) CT Spacing when=1, Normal Wordspace when=0 */ unsigned char settingByte = 0; settingByte |= 1 << 7; // Disabled Paddle Watchdog settingByte |= 0 << 6; // Paddle Echoback - Even Disable, characters are sent - K3NG Keyer switch (keyMode) // Key Mode { case IAMBIC_B: case LAST_MODE: //no action 00 break; case IAMBIC_A: settingByte |= 1 << 4; break; case ULTIMATE: settingByte |= 1 << 5; break; case SINGLE_PADDLE: settingByte |= 1 << 5; settingByte |= 1 << 4; break; } settingByte |= paddleSwap << 3; // Paddle Swap Normal settingByte |= 1 << 2; // Serial Echoback Enabled - must be //1 = 0 // Autospace Disabled //0 = 0 // Normal Wordspace return settingByte; } bool CWWinKey::__sendStatusRequest() { FCT_IDENTIFICATION; if ( !isInHostMode ) { qCWarning(runtime) << "Key is not in Host Mode"; return false; } QByteArray cmd; cmd.resize(1); cmd[0] = 0x15; qint64 size = writeAsyncData(cmd); if ( size != 1 ) { qWarning() << "Unexpected size of write response or communication error"; qCDebug(runtime) << lastError(); } return true; } bool CWWinKey::__setPOTRange() { FCT_IDENTIFICATION; if ( !isInHostMode ) { qCWarning(runtime) << "Key is not in Host Mode"; return false; } if ( defaultWPMSpeed <= 0 ) { qCDebug(runtime) << "Default WPM Speed is negative" << defaultWPMSpeed; return false; } qCDebug(runtime) << "Key POT Range will be" << minWPMRange << minWPMRange + 31; QByteArray cmd; cmd.resize(4); cmd[0] = 0x05; cmd[1] = minWPMRange; cmd[2] = 31; cmd[3] = 0xFFu; qint64 size = writeAsyncData(cmd); if ( size != 4 ) { qWarning() << "Unexpected size of write response or communication error"; qCDebug(runtime) << lastError(); } return true; } QList> CWWinKey::sidetoneFrequencies() { return { {tr("4000 Hz"), 4000}, {tr("2000 Hz"), 2000}, {tr("1333 Hz"), 1333}, {tr("1000 Hz"), 1000}, {tr("800 Hz"), 800}, {tr("666 Hz"), 666}, {tr("571 Hz"), 571}, {tr("500 Hz"), 500}, {tr("444 Hz"), 444}, {tr("400 Hz"), 400} }; } bool CWWinKey::__setSidetone(bool enabled) { FCT_IDENTIFICATION; /* * Sidetone Control command: 0x01 * Available on WinKey v2+ only (WK2 sidetone is always enabled). * * nn bit layout (Table 1 from WinKey spec): * Bit 7 : Paddle Only Sidetone - when 1, sidetone is muted for CW * sourced from the host port; paddle entry still has sidetone. * Bits 6-4 : Unused, set to zero. * Bits 3-0 : Sidetone frequency N (Table 2): * 0x1=4000Hz, 0x2=2000Hz, 0x3=1333Hz, 0x4=1000Hz, * 0x5=800Hz, 0x6=666Hz, 0x7=571Hz, 0x8=500Hz, * 0x9=444Hz, 0xA=400Hz * * enabled=false -> Paddle Only Sidetone (0x80 | freqCode): * host CW is muted, paddle sidetone at configured frequency. * enabled=true -> normal sidetone at configured frequency (freqCode). */ if ( version < 20 ) { qCDebug(runtime) << "Sidetone control not supported on WK1"; return false; } if ( !isInHostMode ) { qCWarning(runtime) << "Key is not in Host Mode"; return false; } /* Convert Hz to WinKey frequency code (Table 2 of WinKey spec). * sidetoneFrequency is stored in Hz; the combo shares Hz with CWDaemon. */ static const int hzToCode[][2] = { {4000, 1}, {2000, 2}, {1333, 3}, {1000, 4}, {800, 5}, {666, 6}, { 571, 7}, { 500, 8}, { 444, 9}, {400, 10} }; int freqCode = 5; // fallback: 800 Hz for ( const int (&pair)[2] : hzToCode ) { if ( pair[0] == sidetoneFrequency ) { freqCode = pair[1]; break; } } unsigned char freqCodeByte = static_cast(freqCode & 0x0F); QByteArray cmd; cmd.resize(2); cmd[0] = 0x01; cmd[1] = enabled ? freqCodeByte : static_cast(0x80u | freqCodeByte); qint64 size = writeAsyncData(cmd); if ( size != 2 ) { qWarning() << "Unexpected size of write response or communication error"; qCDebug(runtime) << lastError(); return false; } return true; } ================================================ FILE: cwkey/drivers/CWWinKey.h ================================================ #ifndef QLOG_CWKEY_DRIVERS_CWWINKEY_H #define QLOG_CWKEY_DRIVERS_CWWINKEY_H #include #include "CWKey.h" class CWWinKey : public CWKey, protected CWKeySerialInterface { Q_OBJECT public: explicit CWWinKey(const QString &portName, const qint32 baudrate, const CWKey::CWKeyModeID mode, const qint32 defaultSpeed, bool paddleSwap, bool paddleOnlySidetone, qint32 sidetoneFrequency, QObject *parent = nullptr); virtual ~CWWinKey(); virtual bool open() override; virtual bool close() override; virtual QString lastError() override; virtual bool sendText(const QString &text) override; virtual bool setWPM(const qint16 wpm) override; virtual bool immediatelyStop() override; static QList> sidetoneFrequencies(); static int defaultSidetoneFrequency() { return 800; } private: bool isInHostMode; bool xoff; bool paddleSwap; bool paddleOnlySidetone; qint32 sidetoneFrequency; QMutex writeBufferMutex; QMutex commandMutex; QByteArray writeBuffer; qint32 minWPMRange; QString lastLogicalError; void tryAsyncWrite(); unsigned char buildWKModeByte() const; bool __sendStatusRequest(); bool __setPOTRange(); bool __setWPM(const qint16 wpm); bool __setSidetone(bool enabled); void __close(); unsigned char version; private slots: void handleBytesWritten(qint64 bytes); void handleError(QSerialPort::SerialPortError serialPortError); void handleReadyRead(); }; #endif // QLOG_CWKEY_DRIVERS_CWWINKEY_H ================================================ FILE: data/Accents.cpp ================================================ #include "Data.h" #include "core/debug.h" MODULE_IDENTIFICATION("qlog.data.data"); const char Data::translitTab[] = { 1, ' ', 1, '!', 2, 'C', '/', 2, 'P', 'S', 2, '$', '?', 2, 'Y', '=', 1, '|', 2, 'S', 'S', 1, '\"', 3, '(', 'c', ')', 1, 'a', 2, '<', '<', 1, '!', 3, '(', 'r', ')', 1, '-', 3, 'd', 'e', 'g', 2, '+', '-', 1, '2', 1, '3', 1, '\'', 1, 'u', 1, 'P', 1, '*', 1, ',', 1, '1', 1, 'o', 2, '>', '>', 5, ' ', '1', '/', '4', ' ', 5, ' ', '1', '/', '2', ' ', 5, ' ', '3', '/', '4', ' ', 1, '?', 1, 'A', 1, 'A', 1, 'A', 1, 'A', 1, 'A', 1, 'A', 2, 'A', 'E', 1, 'C', 1, 'E', 1, 'E', 1, 'E', 1, 'E', 1, 'I', 1, 'I', 1, 'I', 1, 'I', 1, 'D', 1, 'N', 1, 'O', 1, 'O', 1, 'O', 1, 'O', 1, 'O', 1, 'x', 1, 'O', 1, 'U', 1, 'U', 1, 'U', 1, 'U', 1, 'Y', 2, 'T', 'h', 2, 's', 's', 1, 'a', 1, 'a', 1, 'a', 1, 'a', 1, 'a', 1, 'a', 2, 'a', 'e', 1, 'c', 1, 'e', 1, 'e', 1, 'e', 1, 'e', 1, 'i', 1, 'i', 1, 'i', 1, 'i', 1, 'd', 1, 'n', 1, 'o', 1, 'o', 1, 'o', 1, 'o', 1, 'o', 1, '/', 1, 'o', 1, 'u', 1, 'u', 1, 'u', 1, 'u', 1, 'y', 2, 't', 'h', 1, 'y', 1, 'A', 1, 'a', 1, 'A', 1, 'a', 1, 'A', 1, 'a', 1, 'C', 1, 'c', 1, 'C', 1, 'c', 1, 'C', 1, 'c', 1, 'C', 1, 'c', 1, 'D', 1, 'd', 1, 'D', 1, 'd', 1, 'E', 1, 'e', 1, 'E', 1, 'e', 1, 'E', 1, 'e', 1, 'E', 1, 'e', 1, 'E', 1, 'e', 1, 'G', 1, 'g', 1, 'G', 1, 'g', 1, 'G', 1, 'g', 1, 'G', 1, 'g', 1, 'H', 1, 'h', 1, 'H', 1, 'h', 1, 'I', 1, 'i', 1, 'I', 1, 'i', 1, 'I', 1, 'i', 1, 'I', 1, 'i', 1, 'I', 1, 'i', 2, 'I', 'J', 2, 'i', 'j', 1, 'J', 1, 'j', 1, 'K', 1, 'k', 1, 'k', 1, 'L', 1, 'l', 1, 'L', 1, 'l', 1, 'L', 1, 'l', 1, 'L', 1, 'l', 1, 'L', 1, 'l', 1, 'N', 1, 'n', 1, 'N', 1, 'n', 1, 'N', 1, 'n', 2, '\'', 'n', 2, 'n', 'g', 2, 'N', 'G', 1, 'O', 1, 'o', 1, 'O', 1, 'o', 1, 'O', 1, 'o', 2, 'O', 'E', 2, 'o', 'e', 1, 'R', 1, 'r', 1, 'R', 1, 'r', 1, 'R', 1, 'r', 1, 'S', 1, 's', 1, 'S', 1, 's', 1, 'S', 1, 's', 1, 'S', 1, 's', 1, 'T', 1, 't', 1, 'T', 1, 't', 1, 'T', 1, 't', 1, 'U', 1, 'u', 1, 'U', 1, 'u', 1, 'U', 1, 'u', 1, 'U', 1, 'u', 1, 'U', 1, 'u', 1, 'U', 1, 'u', 1, 'W', 1, 'w', 1, 'Y', 1, 'y', 1, 'Y', 1, 'Z', 1, 'z', 1, 'Z', 1, 'z', 1, 'Z', 1, 'z', 1, 's', 1, 'b', 1, 'B', 1, 'B', 1, 'b', 1, '6', 1, '6', 1, 'O', 1, 'C', 1, 'c', 1, 'D', 1, 'D', 1, 'D', 1, 'd', 1, 'd', 1, '3', 1, '@', 1, 'E', 1, 'F', 1, 'f', 1, 'G', 1, 'G', 2, 'h', 'v', 1, 'I', 1, 'I', 1, 'K', 1, 'k', 1, 'l', 1, 'l', 1, 'W', 1, 'N', 1, 'n', 1, 'O', 1, 'O', 1, 'o', 2, 'O', 'I', 2, 'o', 'i', 1, 'P', 1, 'p', 2, 'Y', 'R', 1, '2', 1, '2', 2, 'S', 'H', 2, 's', 'h', 1, 't', 1, 'T', 1, 't', 1, 'T', 1, 'U', 1, 'u', 1, 'Y', 1, 'V', 1, 'Y', 1, 'y', 1, 'Z', 1, 'z', 2, 'Z', 'H', 2, 'Z', 'H', 2, 'z', 'h', 2, 'z', 'h', 1, '2', 1, '5', 1, '5', 2, 't', 's', 1, 'w', 1, '|', 2, '|', '|', 2, '|', '=', 1, '!', 2, 'D', 'Z', 2, 'D', 'z', 2, 'd', 'z', 2, 'L', 'J', 2, 'L', 'j', 2, 'l', 'j', 2, 'N', 'J', 2, 'N', 'j', 2, 'n', 'j', 1, 'A', 1, 'a', 1, 'I', 1, 'i', 1, 'O', 1, 'o', 1, 'U', 1, 'u', 1, 'U', 1, 'u', 1, 'U', 1, 'u', 1, 'U', 1, 'u', 1, 'U', 1, 'u', 1, '@', 1, 'A', 1, 'a', 1, 'A', 1, 'a', 2, 'A', 'E', 2, 'a', 'e', 1, 'G', 1, 'g', 1, 'G', 1, 'g', 1, 'K', 1, 'k', 1, 'O', 1, 'o', 1, 'O', 1, 'o', 2, 'Z', 'H', 2, 'z', 'h', 1, 'j', 2, 'D', 'Z', 2, 'D', 'z', 2, 'd', 'z', 1, 'G', 1, 'g', 2, 'H', 'V', 1, 'W', 1, 'N', 1, 'n', 1, 'A', 1, 'a', 2, 'A', 'E', 2, 'a', 'e', 1, 'O', 1, 'o', 1, 'A', 1, 'a', 1, 'A', 1, 'a', 1, 'E', 1, 'e', 1, 'E', 1, 'e', 1, 'I', 1, 'i', 1, 'I', 1, 'i', 1, 'O', 1, 'o', 1, 'O', 1, 'o', 1, 'R', 1, 'r', 1, 'R', 1, 'r', 1, 'U', 1, 'u', 1, 'U', 1, 'u', 1, 'S', 1, 's', 1, 'T', 1, 't', 1, 'Y', 1, 'y', 1, 'H', 1, 'h', 1, 'N', 1, 'd', 2, 'O', 'U', 2, 'o', 'u', 1, 'Z', 1, 'z', 1, 'A', 1, 'a', 1, 'E', 1, 'e', 1, 'O', 1, 'o', 1, 'O', 1, 'o', 1, 'O', 1, 'o', 1, 'O', 1, 'o', 1, 'Y', 1, 'y', 1, 'l', 1, 'n', 1, 't', 1, 'j', 2, 'd', 'b', 2, 'q', 'p', 1, 'A', 1, 'C', 1, 'c', 1, 'L', 1, 'T', 1, 's', 1, 'z', 1, 'B', 1, 'U', 1, '^', 1, 'E', 1, 'e', 1, 'J', 1, 'j', 1, 'q', 1, 'q', 1, 'R', 1, 'r', 1, 'Y', 1, 'y', 1, 'a', 1, 'a', 1, 'a', 1, 'b', 1, 'o', 1, 'c', 1, 'd', 1, 'd', 1, 'e', 1, '@', 1, '@', 1, 'e', 1, 'e', 1, 'e', 1, 'e', 1, 'j', 1, 'g', 1, 'g', 1, 'g', 1, 'g', 1, 'u', 1, 'Y', 1, 'h', 1, 'h', 1, 'i', 1, 'i', 1, 'I', 1, 'l', 1, 'l', 1, 'l', 2, 'l', 'Z', 1, 'W', 1, 'W', 1, 'm', 1, 'n', 1, 'n', 1, 'n', 1, 'o', 2, 'O', 'E', 1, 'O', 1, 'F', 1, 'r', 1, 'r', 1, 'r', 1, 'r', 1, 'r', 1, 'r', 1, 'r', 1, 'R', 1, 'R', 1, 's', 1, 'S', 1, 'j', 1, 'S', 1, 'S', 1, 't', 1, 't', 1, 'u', 1, 'U', 1, 'v', 1, '^', 1, 'w', 1, 'y', 1, 'Y', 1, 'z', 1, 'z', 1, 'Z', 1, 'Z', 1, '?', 1, '?', 1, '?', 1, 'C', 1, '@', 1, 'B', 1, 'E', 1, 'G', 1, 'H', 1, 'j', 1, 'k', 1, 'L', 1, 'q', 1, '?', 1, '?', 2, 'd', 'z', 2, 'd', 'Z', 2, 'd', 'z', 2, 't', 's', 2, 't', 'S', 2, 't', 'C', 2, 'f', 'N', 2, 'l', 's', 2, 'l', 'z', 2, 'W', 'W', 2, ']', ']', 1, 'h', 1, 'h', 1, 'h', 1, 'h', 1, 'j', 1, 'r', 1, 'r', 1, 'r', 1, 'r', 1, 'w', 1, 'y', 1, '\'', 1, '\"', 1, '`', 1, '\'', 1, '`', 1, '`', 1, '\'', 1, '?', 1, '?', 1, '<', 1, '>', 1, '^', 1, 'V', 1, '^', 1, 'V', 1, '\'', 1, '-', 1, '/', 1, '\\', 1, ',', 1, '_', 1, '\\', 1, '/', 1, ':', 1, '.', 1, '`', 1, '\'', 1, '^', 1, 'V', 1, '+', 1, '-', 1, 'V', 1, '.', 1, '@', 1, ',', 1, '~', 1, '\"', 1, 'R', 1, 'X', 1, 'G', 1, 'l', 1, 's', 1, 'x', 1, '?', 1, 'V', 1, '=', 1, '\"', 1, 'a', 1, 'e', 1, 'i', 1, 'o', 1, 'u', 1, 'c', 1, 'd', 1, 'h', 1, 'm', 1, 'r', 1, 't', 1, 'v', 1, 'x', 1, '\'', 1, ',', 1, '?', 1, 'A', 1, ';', 1, 'E', 1, 'E', 1, 'I', 1, 'O', 1, 'U', 1, 'O', 1, 'I', 1, 'A', 1, 'B', 1, 'G', 1, 'D', 1, 'E', 1, 'Z', 1, 'E', 2, 'T', 'h', 1, 'I', 1, 'K', 1, 'L', 1, 'M', 1, 'N', 2, 'K', 's', 1, 'O', 1, 'P', 1, 'R', 1, 'S', 1, 'T', 1, 'U', 2, 'P', 'h', 2, 'K', 'h', 2, 'P', 's', 1, 'O', 1, 'I', 1, 'U', 1, 'a', 1, 'e', 1, 'e', 1, 'i', 1, 'u', 1, 'a', 1, 'b', 1, 'g', 1, 'd', 1, 'e', 1, 'z', 1, 'e', 2, 't', 'h', 1, 'i', 1, 'k', 1, 'l', 1, 'm', 1, 'n', 1, 'x', 1, 'o', 1, 'p', 1, 'r', 1, 's', 1, 's', 1, 't', 1, 'u', 2, 'p', 'h', 2, 'k', 'h', 2, 'p', 's', 1, 'o', 1, 'i', 1, 'u', 1, 'o', 1, 'u', 1, 'o', 1, 'b', 2, 't', 'h', 1, 'U', 1, 'U', 1, 'U', 2, 'p', 'h', 1, 'p', 1, '&', 2, 'S', 't', 2, 's', 't', 1, 'W', 1, 'w', 1, 'Q', 1, 'q', 2, 'S', 'p', 2, 's', 'p', 2, 'S', 'h', 2, 's', 'h', 1, 'F', 1, 'f', 2, 'K', 'h', 2, 'k', 'h', 1, 'H', 1, 'h', 1, 'G', 1, 'g', 2, 'C', 'H', 2, 'c', 'h', 2, 'T', 'i', 2, 't', 'i', 1, 'k', 1, 'r', 1, 'c', 1, 'j', 2, 'I', 'e', 2, 'I', 'o', 2, 'D', 'j', 2, 'G', 'j', 2, 'I', 'e', 2, 'D', 'z', 1, 'I', 2, 'Y', 'i', 1, 'J', 2, 'L', 'j', 2, 'N', 'j', 3, 'T', 's', 'h', 2, 'K', 'j', 1, 'I', 1, 'U', 3, 'D', 'z', 'h', 1, 'A', 1, 'B', 1, 'V', 1, 'G', 1, 'D', 1, 'E', 2, 'Z', 'h', 1, 'Z', 1, 'I', 1, 'I', 1, 'K', 1, 'L', 1, 'M', 1, 'N', 1, 'O', 1, 'P', 1, 'R', 1, 'S', 1, 'T', 1, 'U', 1, 'F', 2, 'K', 'h', 2, 'T', 's', 2, 'C', 'h', 2, 'S', 'h', 4, 'S', 'h', 'c', 'h', 1, '\'', 1, 'Y', 1, '\'', 1, 'E', 2, 'I', 'u', 2, 'I', 'a', 1, 'a', 1, 'b', 1, 'v', 1, 'g', 1, 'd', 1, 'e', 2, 'z', 'h', 1, 'z', 1, 'i', 1, 'i', 1, 'k', 1, 'l', 1, 'm', 1, 'n', 1, 'o', 1, 'p', 1, 'r', 1, 's', 1, 't', 1, 'u', 1, 'f', 2, 'k', 'h', 2, 't', 's', 2, 'c', 'h', 2, 's', 'h', 4, 's', 'h', 'c', 'h', 1, '\'', 1, 'y', 1, '\'', 1, 'e', 2, 'i', 'u', 2, 'i', 'a', 2, 'i', 'e', 2, 'i', 'o', 2, 'd', 'j', 2, 'g', 'j', 2, 'i', 'e', 2, 'd', 'z', 1, 'i', 2, 'y', 'i', 1, 'j', 2, 'l', 'j', 2, 'n', 'j', 3, 't', 's', 'h', 2, 'k', 'j', 1, 'i', 1, 'u', 3, 'd', 'z', 'h', 1, 'O', 1, 'o', 1, 'E', 1, 'e', 2, 'I', 'e', 2, 'i', 'e', 1, 'E', 1, 'e', 2, 'I', 'e', 2, 'i', 'e', 1, 'O', 1, 'o', 2, 'I', 'o', 2, 'i', 'o', 2, 'K', 's', 2, 'k', 's', 2, 'P', 's', 2, 'p', 's', 1, 'F', 1, 'f', 1, 'Y', 1, 'y', 1, 'Y', 1, 'y', 1, 'u', 1, 'u', 1, 'O', 1, 'o', 1, 'O', 1, 'o', 2, 'O', 't', 2, 'o', 't', 1, 'Q', 1, 'q', 6, '*', '1', '0', '0', '0', '*', 9, '*', '1', '0', '0', '.', '0', '0', '0', '*', 11, '*', '1', '.', '0', '0', '0', '.', '0', '0', '0', '*', 1, '\"', 1, '\"', 2, 'R', '\'', 2, 'r', '\'', 2, 'G', '\'', 2, 'g', '\'', 2, 'G', '\'', 2, 'g', '\'', 2, 'G', '\'', 2, 'g', '\'', 3, 'Z', 'h', '\'', 3, 'z', 'h', '\'', 2, 'Z', '\'', 2, 'z', '\'', 2, 'K', '\'', 2, 'k', '\'', 2, 'K', '\'', 2, 'k', '\'', 2, 'K', '\'', 2, 'k', '\'', 2, 'K', '\'', 2, 'k', '\'', 2, 'N', '\'', 2, 'n', '\'', 2, 'N', 'g', 2, 'n', 'g', 2, 'P', '\'', 2, 'p', '\'', 2, 'K', 'h', 2, 'k', 'h', 2, 'S', '\'', 2, 's', '\'', 2, 'T', '\'', 2, 't', '\'', 1, 'U', 1, 'u', 2, 'U', '\'', 2, 'u', '\'', 3, 'K', 'h', '\'', 3, 'k', 'h', '\'', 3, 'T', 't', 's', 3, 't', 't', 's', 3, 'C', 'h', '\'', 3, 'c', 'h', '\'', 3, 'C', 'h', '\'', 3, 'c', 'h', '\'', 1, 'H', 1, 'h', 2, 'C', 'h', 2, 'c', 'h', 3, 'C', 'h', '\'', 3, 'c', 'h', '\'', 1, '`', 2, 'Z', 'h', 2, 'z', 'h', 2, 'K', '\'', 2, 'k', '\'', 2, 'N', '\'', 2, 'n', '\'', 2, 'C', 'h', 2, 'c', 'h', 1, 'a', 1, 'a', 1, 'A', 1, 'a', 2, 'A', 'e', 2, 'a', 'e', 2, 'I', 'e', 2, 'i', 'e', 1, '@', 1, '@', 1, '@', 1, '@', 2, 'Z', 'h', 2, 'z', 'h', 1, 'Z', 1, 'z', 2, 'D', 'z', 2, 'd', 'z', 1, 'I', 1, 'i', 1, 'I', 1, 'i', 1, 'O', 1, 'o', 1, 'O', 1, 'o', 1, 'O', 1, 'o', 1, 'E', 1, 'e', 1, 'U', 1, 'u', 1, 'U', 1, 'u', 1, 'U', 1, 'u', 2, 'C', 'h', 2, 'c', 'h', 1, 'Y', 1, 'y', 1, 'A', 1, 'B', 1, 'G', 1, 'D', 1, 'E', 1, 'Z', 1, 'E', 1, 'E', 2, 'T', '`', 2, 'Z', 'h', 1, 'I', 1, 'L', 2, 'K', 'h', 2, 'T', 's', 1, 'K', 1, 'H', 2, 'D', 'z', 2, 'G', 'h', 2, 'C', 'h', 1, 'M', 1, 'Y', 1, 'N', 2, 'S', 'h', 1, 'O', 3, 'C', 'h', '`', 1, 'P', 1, 'J', 2, 'R', 'h', 1, 'S', 1, 'V', 1, 'T', 1, 'R', 3, 'T', 's', '`', 1, 'W', 2, 'P', '`', 2, 'K', '`', 1, 'O', 1, 'F', 1, '<', 1, '\'', 1, '/', 1, '!', 1, ',', 1, '?', 1, '.', 1, 'a', 1, 'b', 1, 'g', 1, 'd', 1, 'e', 1, 'z', 1, 'e', 1, 'e', 2, 't', '`', 2, 'z', 'h', 1, 'i', 1, 'l', 2, 'k', 'h', 2, 't', 's', 1, 'k', 1, 'h', 2, 'd', 'z', 2, 'g', 'h', 2, 'c', 'h', 1, 'm', 1, 'y', 1, 'n', 2, 's', 'h', 1, 'o', 3, 'c', 'h', '`', 1, 'p', 1, 'j', 2, 'r', 'h', 1, 's', 1, 'v', 1, 't', 1, 'r', 3, 't', 's', '`', 1, 'w', 2, 'p', '`', 2, 'k', '`', 1, 'o', 1, 'f', 2, 'e', 'w', 1, ':', 1, '-', 1, 'e', 1, 'a', 1, 'o', 1, 'i', 1, 'e', 1, 'e', 1, 'a', 1, 'a', 1, 'o', 1, 'o', 1, 'u', 1, '-', 1, '|', 1, '.', 1, 'n', 1, 'o', 1, 'A', 1, 'b', 1, 'g', 1, 'd', 1, 'h', 1, 'v', 1, 'z', 1, 'H', 1, 'T', 1, 'y', 2, 'K', 'H', 2, 'K', 'H', 1, 'l', 1, 'm', 1, 'm', 1, 'n', 1, 'n', 1, 's', 1, '`', 1, 'p', 1, 'p', 2, 'T', 'S', 2, 'T', 'S', 1, 'k', 1, 'r', 2, 'S', 'H', 1, 't', 3, 'Y', 'Y', 'Y', 1, 'V', 2, 'O', 'Y', 2, 'E', 'Y', 1, '\'', 1, '\"', 1, ',', 1, ';', 1, '?', 1, 'a', 1, '\'', 2, 'w', '\'', 2, 'y', '\'', 1, 'b', 1, '@', 1, 't', 2, 't', 'h', 1, 'j', 1, 'H', 2, 'k', 'h', 1, 'd', 2, 'd', 'h', 1, 'r', 1, 'z', 1, 's', 2, 's', 'h', 1, 'S', 1, 'D', 1, 'T', 1, 'Z', 1, '`', 1, 'G', 1, 'f', 1, 'q', 1, 'k', 1, 'l', 1, 'm', 1, 'n', 1, 'h', 1, 'w', 1, '~', 1, 'y', 2, 'a', 'n', 2, 'u', 'n', 2, 'i', 'n', 1, 'a', 1, 'u', 1, 'i', 1, 'W', 1, '\'', 1, '\'', 1, '0', 1, '1', 1, '2', 1, '3', 1, '4', 1, '5', 1, '6', 1, '7', 1, '8', 1, '9', 1, '%', 1, '.', 1, ',', 1, '*', 1, '\'', 1, '\'', 1, '\'', 1, '\'', 2, '\'', 'w', 2, '\'', 'u', 2, '\'', 'y', 2, 't', 't', 3, 't', 't', 'h', 1, 'b', 1, 't', 1, 'T', 1, 'p', 2, 't', 'h', 2, 'b', 'h', 2, '\'', 'h', 1, 'H', 2, 'n', 'y', 2, 'd', 'y', 1, 'H', 2, 'c', 'h', 3, 'c', 'c', 'h', 2, 'd', 'd', 1, 'D', 1, 'D', 2, 'D', 't', 2, 'd', 'h', 3, 'd', 'd', 'h', 1, 'd', 1, 'D', 1, 'D', 2, 'r', 'r', 1, 'R', 1, 'R', 1, 'R', 1, 'R', 1, 'R', 1, 'R', 1, 'j', 1, 'R', 1, 'S', 1, 'S', 1, 'S', 1, 'S', 1, 'S', 1, 'T', 2, 'G', 'H', 1, 'F', 1, 'F', 1, 'F', 1, 'v', 1, 'f', 2, 'p', 'h', 1, 'Q', 1, 'Q', 2, 'k', 'h', 1, 'k', 1, 'K', 1, 'K', 2, 'n', 'g', 1, 'K', 1, 'g', 1, 'G', 1, 'N', 1, 'G', 1, 'G', 1, 'G', 1, 'L', 1, 'L', 1, 'L', 1, 'L', 1, 'N', 1, 'N', 1, 'N', 1, 'N', 1, 'N', 1, 'h', 2, 'C', 'h', 2, 'h', 'y', 1, 'h', 1, 'H', 1, '@', 1, 'W', 2, 'o', 'e', 2, 'o', 'e', 1, 'u', 2, 'y', 'u', 2, 'y', 'u', 1, 'W', 1, 'v', 1, 'y', 1, 'Y', 1, 'Y', 1, 'W', 1, 'y', 2, 'y', '\'', 1, '.', 2, 'a', 'e', 1, '@', 1, '#', 1, '^', 1, '0', 1, '1', 1, '2', 1, '3', 1, '4', 1, '5', 1, '6', 1, '7', 1, '8', 1, '9', 2, 'S', 'h', 1, 'D', 2, 'G', 'h', 1, '&', 2, '+', 'm', 2, '/', '/', 1, '/', 1, ',', 1, '!', 1, '!', 1, '-', 1, ',', 1, ',', 1, ';', 1, '?', 1, '~', 1, '{', 1, '}', 1, '*', 1, '\'', 1, 'b', 1, 'g', 1, 'g', 1, 'd', 1, 'd', 1, 'h', 1, 'w', 1, 'z', 1, 'H', 1, 't', 1, 't', 1, 'y', 2, 'y', 'h', 1, 'k', 1, 'l', 1, 'm', 1, 'n', 1, 's', 1, 's', 1, '`', 1, 'p', 1, 'p', 1, 'S', 1, 'q', 1, 'r', 2, 's', 'h', 1, 't', 1, 'a', 1, 'a', 1, 'a', 1, 'A', 1, 'A', 1, 'A', 1, 'e', 1, 'e', 1, 'e', 1, 'E', 1, 'i', 1, 'i', 1, 'u', 1, 'u', 1, 'u', 1, 'o', 1, '`', 1, '\'', 1, 'X', 1, 'Q', 1, '@', 1, '@', 1, '|', 1, '+', 1, 'h', 2, 's', 'h', 1, 'n', 1, 'r', 1, 'b', 1, 'L', 1, 'k', 1, '\'', 1, 'v', 1, 'm', 1, 'f', 2, 'd', 'h', 2, 't', 'h', 1, 'l', 1, 'g', 2, 'n', 'y', 1, 's', 1, 'd', 1, 'z', 1, 't', 1, 'y', 1, 'p', 1, 'j', 2, 'c', 'h', 2, 't', 't', 2, 'h', 'h', 2, 'k', 'h', 2, 't', 'h', 1, 'z', 2, 's', 'h', 1, 's', 1, 'd', 1, 't', 1, 'z', 1, '`', 2, 'g', 'h', 1, 'q', 1, 'w', 1, 'a', 2, 'a', 'a', 1, 'i', 2, 'e', 'e', 1, 'u', 2, 'o', 'o', 1, 'e', 2, 'e', 'y', 1, 'o', 2, 'o', 'a', 1, 'N', 1, 'N', 1, 'H', 1, 'a', 2, 'a', 'a', 1, 'i', 2, 'i', 'i', 1, 'u', 2, 'u', 'u', 1, 'R', 1, 'L', 2, 'e', 'N', 1, 'e', 1, 'e', 2, 'a', 'i', 2, 'o', 'N', 1, 'o', 1, 'o', 2, 'a', 'u', 1, 'k', 2, 'k', 'h', 1, 'g', 2, 'g', 'h', 2, 'n', 'g', 1, 'c', 2, 'c', 'h', 1, 'j', 2, 'j', 'h', 2, 'n', 'y', 2, 't', 't', 3, 't', 't', 'h', 2, 'd', 'd', 3, 'd', 'd', 'h', 2, 'n', 'n', 1, 't', 2, 't', 'h', 1, 'd', 2, 'd', 'h', 1, 'n', 3, 'n', 'n', 'n', 1, 'p', 2, 'p', 'h', 1, 'b', 2, 'b', 'h', 1, 'm', 1, 'y', 1, 'r', 2, 'r', 'r', 1, 'l', 1, 'l', 3, 'l', 'l', 'l', 1, 'v', 2, 's', 'h', 2, 's', 's', 1, 's', 1, 'h', 1, '\'', 1, '\'', 2, 'a', 'a', 1, 'i', 2, 'i', 'i', 1, 'u', 2, 'u', 'u', 1, 'R', 2, 'R', 'R', 2, 'e', 'N', 1, 'e', 1, 'e', 2, 'a', 'i', 2, 'o', 'N', 1, 'o', 1, 'o', 2, 'a', 'u', 3, 'A', 'U', 'M', 1, '\'', 1, '\'', 1, '`', 1, '\'', 1, 'q', 3, 'k', 'h', 'h', 3, 'g', 'h', 'h', 1, 'z', 4, 'd', 'd', 'd', 'h', 2, 'r', 'h', 1, 'f', 2, 'y', 'y', 2, 'R', 'R', 2, 'L', 'L', 1, 'L', 2, 'L', 'L', 3, ' ', '/', ' ', 4, ' ', '/', '/', ' ', 1, '0', 1, '1', 1, '2', 1, '3', 1, '4', 1, '5', 1, '6', 1, '7', 1, '8', 1, '9', 1, '.', 1, 'N', 1, 'N', 1, 'H', 1, 'a', 2, 'a', 'a', 1, 'i', 2, 'i', 'i', 1, 'u', 2, 'u', 'u', 1, 'R', 2, 'R', 'R', 1, 'e', 2, 'a', 'i', 1, 'o', 2, 'a', 'u', 1, 'k', 2, 'k', 'h', 1, 'g', 2, 'g', 'h', 2, 'n', 'g', 1, 'c', 2, 'c', 'h', 1, 'j', 2, 'j', 'h', 2, 'n', 'y', 2, 't', 't', 3, 't', 't', 'h', 2, 'd', 'd', 3, 'd', 'd', 'h', 2, 'n', 'n', 1, 't', 2, 't', 'h', 1, 'd', 2, 'd', 'h', 1, 'n', 1, 'p', 2, 'p', 'h', 1, 'b', 2, 'b', 'h', 1, 'm', 1, 'y', 1, 'r', 1, 'l', 2, 's', 'h', 2, 's', 's', 1, 's', 1, 'h', 1, '\'', 2, 'a', 'a', 1, 'i', 2, 'i', 'i', 1, 'u', 2, 'u', 'u', 1, 'R', 2, 'R', 'R', 1, 'e', 2, 'a', 'i', 1, 'o', 2, 'a', 'u', 1, '+', 2, 'r', 'r', 2, 'r', 'h', 2, 'y', 'y', 2, 'R', 'R', 2, 'L', 'L', 1, 'L', 2, 'L', 'L', 1, '0', 1, '1', 1, '2', 1, '3', 1, '4', 1, '5', 1, '6', 1, '7', 1, '8', 1, '9', 2, 'r', '\'', 2, 'r', '`', 2, 'R', 's', 2, 'R', 's', 2, '1', '/', 2, '2', '/', 2, '3', '/', 2, '4', '/', 7, ' ', '1', ' ', '-', ' ', '1', '/', 3, '/', '1', '6', 1, 'N', 1, 'a', 2, 'a', 'a', 1, 'i', 2, 'i', 'i', 1, 'u', 2, 'u', 'u', 2, 'e', 'e', 2, 'a', 'i', 2, 'o', 'o', 2, 'a', 'u', 1, 'k', 2, 'k', 'h', 1, 'g', 2, 'g', 'h', 2, 'n', 'g', 1, 'c', 2, 'c', 'h', 1, 'j', 2, 'j', 'h', 2, 'n', 'y', 2, 't', 't', 3, 't', 't', 'h', 2, 'd', 'd', 3, 'd', 'd', 'h', 2, 'n', 'n', 1, 't', 2, 't', 'h', 1, 'd', 2, 'd', 'h', 1, 'n', 1, 'p', 2, 'p', 'h', 1, 'b', 2, 'b', 'b', 1, 'm', 1, 'y', 1, 'r', 1, 'l', 2, 'l', 'l', 1, 'v', 2, 's', 'h', 1, 's', 1, 'h', 1, '\'', 2, 'a', 'a', 1, 'i', 2, 'i', 'i', 1, 'u', 2, 'u', 'u', 2, 'e', 'e', 2, 'a', 'i', 2, 'o', 'o', 2, 'a', 'u', 3, 'k', 'h', 'h', 3, 'g', 'h', 'h', 1, 'z', 2, 'r', 'r', 1, 'f', 1, '0', 1, '1', 1, '2', 1, '3', 1, '4', 1, '5', 1, '6', 1, '7', 1, '8', 1, '9', 1, 'N', 1, 'H', 6, 'G', '.', 'E', '.', 'O', '.', 1, 'N', 1, 'N', 1, 'H', 1, 'a', 2, 'a', 'a', 1, 'i', 2, 'i', 'i', 1, 'u', 2, 'u', 'u', 1, 'R', 2, 'e', 'N', 1, 'e', 2, 'a', 'i', 2, 'o', 'N', 1, 'o', 2, 'a', 'u', 1, 'k', 2, 'k', 'h', 1, 'g', 2, 'g', 'h', 2, 'n', 'g', 1, 'c', 2, 'c', 'h', 1, 'j', 2, 'j', 'h', 2, 'n', 'y', 2, 't', 't', 3, 't', 't', 'h', 2, 'd', 'd', 3, 'd', 'd', 'h', 2, 'n', 'n', 1, 't', 2, 't', 'h', 1, 'd', 2, 'd', 'h', 1, 'n', 1, 'p', 2, 'p', 'h', 1, 'b', 2, 'b', 'h', 1, 'm', 2, 'y', 'a', 1, 'r', 1, 'l', 2, 'l', 'l', 1, 'v', 2, 's', 'h', 2, 's', 's', 1, 's', 1, 'h', 1, '\'', 1, '\'', 2, 'a', 'a', 1, 'i', 2, 'i', 'i', 1, 'u', 2, 'u', 'u', 1, 'R', 2, 'R', 'R', 2, 'e', 'N', 1, 'e', 2, 'a', 'i', 2, 'o', 'N', 1, 'o', 2, 'a', 'u', 3, 'A', 'U', 'M', 2, 'R', 'R', 1, '0', 1, '1', 1, '2', 1, '3', 1, '4', 1, '5', 1, '6', 1, '7', 1, '8', 1, '9', 1, 'N', 1, 'N', 1, 'H', 1, 'a', 2, 'a', 'a', 1, 'i', 2, 'i', 'i', 1, 'u', 2, 'u', 'u', 1, 'R', 1, 'L', 1, 'e', 2, 'a', 'i', 1, 'o', 2, 'a', 'u', 1, 'k', 2, 'k', 'h', 1, 'g', 2, 'g', 'h', 2, 'n', 'g', 1, 'c', 2, 'c', 'h', 1, 'j', 2, 'j', 'h', 2, 'n', 'y', 2, 't', 't', 3, 't', 't', 'h', 2, 'd', 'd', 3, 'd', 'd', 'h', 2, 'n', 'n', 1, 't', 2, 't', 'h', 1, 'd', 2, 'd', 'h', 1, 'n', 1, 'p', 2, 'p', 'h', 1, 'b', 2, 'b', 'h', 1, 'm', 1, 'y', 1, 'r', 1, 'l', 2, 'l', 'l', 2, 's', 'h', 2, 's', 's', 1, 's', 1, 'h', 1, '\'', 1, '\'', 2, 'a', 'a', 1, 'i', 2, 'i', 'i', 1, 'u', 2, 'u', 'u', 1, 'R', 1, 'e', 2, 'a', 'i', 1, 'o', 2, 'a', 'u', 1, '+', 1, '+', 2, 'r', 'r', 2, 'r', 'h', 2, 'y', 'y', 2, 'R', 'R', 2, 'L', 'L', 1, '0', 1, '1', 1, '2', 1, '3', 1, '4', 1, '5', 1, '6', 1, '7', 1, '8', 1, '9', 1, 'N', 1, 'H', 1, 'a', 2, 'a', 'a', 1, 'i', 2, 'i', 'i', 1, 'u', 2, 'u', 'u', 1, 'e', 2, 'e', 'e', 2, 'a', 'i', 1, 'o', 2, 'o', 'o', 2, 'a', 'u', 1, 'k', 2, 'n', 'g', 1, 'c', 1, 'j', 2, 'n', 'y', 2, 't', 't', 2, 'n', 'n', 1, 't', 1, 'n', 3, 'n', 'n', 'n', 1, 'p', 1, 'm', 1, 'y', 1, 'r', 2, 'r', 'r', 1, 'l', 2, 'l', 'l', 3, 'l', 'l', 'l', 1, 'v', 2, 's', 's', 1, 's', 1, 'h', 2, 'a', 'a', 1, 'i', 2, 'i', 'i', 1, 'u', 2, 'u', 'u', 1, 'e', 2, 'e', 'e', 2, 'a', 'i', 1, 'o', 2, 'o', 'o', 2, 'a', 'u', 1, '+', 1, '0', 1, '1', 1, '2', 1, '3', 1, '4', 1, '5', 1, '6', 1, '7', 1, '8', 1, '9', 4, '+', '1', '0', '+', 5, '+', '1', '0', '0', '+', 6, '+', '1', '0', '0', '0', '+', 1, 'N', 1, 'N', 1, 'H', 1, 'a', 2, 'a', 'a', 1, 'i', 2, 'i', 'i', 1, 'u', 2, 'u', 'u', 1, 'R', 1, 'L', 1, 'e', 2, 'e', 'e', 2, 'a', 'i', 1, 'o', 2, 'o', 'o', 2, 'a', 'u', 1, 'k', 2, 'k', 'h', 1, 'g', 2, 'g', 'h', 2, 'n', 'g', 1, 'c', 2, 'c', 'h', 1, 'j', 2, 'j', 'h', 2, 'n', 'y', 2, 't', 't', 3, 't', 't', 'h', 2, 'd', 'd', 3, 'd', 'd', 'h', 2, 'n', 'n', 1, 't', 2, 't', 'h', 1, 'd', 2, 'd', 'h', 1, 'n', 1, 'p', 2, 'p', 'h', 1, 'b', 2, 'b', 'h', 1, 'm', 1, 'y', 1, 'r', 2, 'r', 'r', 1, 'l', 2, 'l', 'l', 1, 'v', 2, 's', 'h', 2, 's', 's', 1, 's', 1, 'h', 2, 'a', 'a', 1, 'i', 2, 'i', 'i', 1, 'u', 2, 'u', 'u', 1, 'R', 2, 'R', 'R', 1, 'e', 2, 'e', 'e', 2, 'a', 'i', 1, 'o', 2, 'o', 'o', 2, 'a', 'u', 1, '+', 1, '+', 2, 'R', 'R', 2, 'L', 'L', 1, '0', 1, '1', 1, '2', 1, '3', 1, '4', 1, '5', 1, '6', 1, '7', 1, '8', 1, '9', 1, 'N', 1, 'H', 1, 'a', 2, 'a', 'a', 1, 'i', 2, 'i', 'i', 1, 'u', 2, 'u', 'u', 1, 'R', 1, 'L', 1, 'e', 2, 'e', 'e', 2, 'a', 'i', 1, 'o', 2, 'o', 'o', 2, 'a', 'u', 1, 'k', 2, 'k', 'h', 1, 'g', 2, 'g', 'h', 2, 'n', 'g', 1, 'c', 2, 'c', 'h', 1, 'j', 2, 'j', 'h', 2, 'n', 'y', 2, 't', 't', 3, 't', 't', 'h', 2, 'd', 'd', 3, 'd', 'd', 'h', 2, 'n', 'n', 1, 't', 2, 't', 'h', 1, 'd', 2, 'd', 'h', 1, 'n', 1, 'p', 2, 'p', 'h', 1, 'b', 2, 'b', 'h', 1, 'm', 1, 'y', 1, 'r', 2, 'r', 'r', 1, 'l', 2, 'l', 'l', 1, 'v', 2, 's', 'h', 2, 's', 's', 1, 's', 1, 'h', 2, 'a', 'a', 1, 'i', 2, 'i', 'i', 1, 'u', 2, 'u', 'u', 1, 'R', 2, 'R', 'R', 1, 'e', 2, 'e', 'e', 2, 'a', 'i', 1, 'o', 2, 'o', 'o', 2, 'a', 'u', 1, '+', 1, '+', 3, 'l', 'l', 'l', 2, 'R', 'R', 2, 'L', 'L', 1, '0', 1, '1', 1, '2', 1, '3', 1, '4', 1, '5', 1, '6', 1, '7', 1, '8', 1, '9', 1, 'N', 1, 'H', 1, 'a', 2, 'a', 'a', 1, 'i', 2, 'i', 'i', 1, 'u', 2, 'u', 'u', 1, 'R', 1, 'L', 1, 'e', 2, 'e', 'e', 2, 'a', 'i', 1, 'o', 2, 'o', 'o', 2, 'a', 'u', 1, 'k', 2, 'k', 'h', 1, 'g', 2, 'g', 'h', 2, 'n', 'g', 1, 'c', 2, 'c', 'h', 1, 'j', 2, 'j', 'h', 2, 'n', 'y', 2, 't', 't', 3, 't', 't', 'h', 2, 'd', 'd', 3, 'd', 'd', 'h', 2, 'n', 'n', 1, 't', 2, 't', 'h', 1, 'd', 2, 'd', 'h', 1, 'n', 1, 'p', 2, 'p', 'h', 1, 'b', 2, 'b', 'h', 1, 'm', 1, 'y', 1, 'r', 2, 'r', 'r', 1, 'l', 2, 'l', 'l', 3, 'l', 'l', 'l', 1, 'v', 2, 's', 'h', 2, 's', 's', 1, 's', 1, 'h', 2, 'a', 'a', 1, 'i', 2, 'i', 'i', 1, 'u', 2, 'u', 'u', 1, 'R', 1, 'e', 2, 'e', 'e', 2, 'a', 'i', 1, 'o', 2, 'o', 'o', 2, 'a', 'u', 1, '+', 2, 'R', 'R', 2, 'L', 'L', 1, '0', 1, '1', 1, '2', 1, '3', 1, '4', 1, '5', 1, '6', 1, '7', 1, '8', 1, '9', 1, 'N', 1, 'H', 1, 'a', 2, 'a', 'a', 2, 'a', 'e', 3, 'a', 'a', 'e', 1, 'i', 2, 'i', 'i', 1, 'u', 2, 'u', 'u', 1, 'R', 2, 'R', 'R', 1, 'L', 2, 'L', 'L', 1, 'e', 2, 'e', 'e', 2, 'a', 'i', 1, 'o', 2, 'o', 'o', 2, 'a', 'u', 1, 'k', 2, 'k', 'h', 1, 'g', 2, 'g', 'h', 2, 'n', 'g', 3, 'n', 'n', 'g', 1, 'c', 2, 'c', 'h', 1, 'j', 2, 'j', 'h', 2, 'n', 'y', 3, 'j', 'n', 'y', 3, 'n', 'y', 'j', 2, 't', 't', 3, 't', 't', 'h', 2, 'd', 'd', 3, 'd', 'd', 'h', 2, 'n', 'n', 4, 'n', 'n', 'd', 'd', 1, 't', 2, 't', 'h', 1, 'd', 2, 'd', 'h', 1, 'n', 2, 'n', 'd', 1, 'p', 2, 'p', 'h', 1, 'b', 2, 'b', 'h', 1, 'm', 2, 'm', 'b', 1, 'y', 1, 'r', 1, 'l', 1, 'v', 2, 's', 'h', 2, 's', 's', 1, 's', 1, 'h', 2, 'l', 'l', 1, 'f', 2, 'a', 'a', 2, 'a', 'e', 3, 'a', 'a', 'e', 1, 'i', 2, 'i', 'i', 1, 'u', 2, 'u', 'u', 1, 'R', 1, 'e', 2, 'e', 'e', 2, 'a', 'i', 1, 'o', 2, 'o', 'o', 2, 'a', 'u', 1, 'L', 2, 'R', 'R', 2, 'L', 'L', 3, ' ', '.', ' ', 1, 'k', 2, 'k', 'h', 2, 'k', 'h', 2, 'k', 'h', 2, 'k', 'h', 2, 'k', 'h', 2, 'n', 'g', 3, 'c', 'c', 'h', 2, 'c', 'h', 2, 'c', 'h', 2, 'c', 'h', 2, 'c', 'h', 1, 'y', 1, 'd', 1, 't', 2, 't', 'h', 2, 't', 'h', 2, 't', 'h', 1, 'n', 1, 'd', 1, 't', 2, 't', 'h', 2, 't', 'h', 2, 't', 'h', 1, 'n', 1, 'b', 1, 'p', 2, 'p', 'h', 1, 'f', 2, 'p', 'h', 1, 'f', 2, 'p', 'h', 1, 'm', 1, 'y', 1, 'r', 1, 'R', 1, 'l', 1, 'L', 1, 'w', 1, 's', 1, 's', 1, 's', 1, 'h', 1, 'l', 1, '`', 1, 'h', 1, '~', 1, 'a', 1, 'a', 2, 'a', 'a', 2, 'a', 'm', 1, 'i', 2, 'i', 'i', 2, 'u', 'e', 3, 'u', 'u', 'e', 1, 'u', 2, 'u', 'u', 1, '\'', 3, 'B', 'h', '.', 1, 'e', 2, 'a', 'e', 1, 'o', 2, 'a', 'i', 2, 'a', 'i', 2, 'a', 'o', 1, '+', 1, 'M', 3, ' ', '*', ' ', 1, '0', 1, '1', 1, '2', 1, '3', 1, '4', 1, '5', 1, '6', 1, '7', 1, '8', 1, '9', 4, ' ', '/', '/', ' ', 5, ' ', '/', '/', '/', ' ', 1, 'k', 2, 'k', 'h', 2, 'k', 'h', 2, 'n', 'g', 2, 'c', 'h', 1, 's', 2, 'n', 'y', 1, 'd', 1, 'h', 2, 't', 'h', 2, 't', 'h', 1, 'n', 1, 'b', 1, 'p', 2, 'p', 'h', 1, 'f', 2, 'p', 'h', 1, 'f', 1, 'm', 1, 'y', 1, 'r', 1, 'l', 1, 'w', 1, 's', 1, 'h', 1, '`', 1, '~', 1, 'a', 2, 'a', 'a', 2, 'a', 'm', 1, 'i', 2, 'i', 'i', 1, 'y', 2, 'y', 'y', 1, 'u', 2, 'u', 'u', 1, 'o', 1, 'l', 2, 'n', 'y', 1, 'e', 2, 'e', 'i', 1, 'o', 2, 'a', 'y', 2, 'a', 'i', 1, '+', 1, 'M', 1, '0', 1, '1', 1, '2', 1, '3', 1, '4', 1, '5', 1, '6', 1, '7', 1, '8', 1, '9', 2, 'h', 'n', 2, 'h', 'm', 3, 'A', 'U', 'M', 4, ' ', '/', '/', ' ', 3, ' ', '*', ' ', 1, '-', 3, ' ', '/', ' ', 3, ' ', '/', ' ', 4, ' ', '/', '/', ' ', 4, ' ', '-', '/', ' ', 4, ' ', '+', '/', ' ', 4, ' ', 'X', '/', ' ', 6, ' ', '/', 'X', 'X', '/', ' ', 5, ' ', '/', 'X', '/', ' ', 2, ',', ' ', 1, '0', 1, '1', 1, '2', 1, '3', 1, '4', 1, '5', 1, '6', 1, '7', 1, '8', 1, '9', 2, '.', '5', 3, '1', '.', '5', 3, '2', '.', '5', 3, '3', '.', '5', 3, '4', '.', '5', 3, '5', '.', '5', 3, '6', '.', '5', 3, '7', '.', '5', 3, '8', '.', '5', 3, '-', '.', '5', 1, '+', 1, '*', 1, '^', 1, '_', 1, '~', 1, ']', 2, '[', '[', 2, ']', ']', 1, 'k', 2, 'k', 'h', 1, 'g', 2, 'g', 'h', 2, 'n', 'g', 1, 'c', 2, 'c', 'h', 1, 'j', 2, 'n', 'y', 2, 't', 't', 3, 't', 't', 'h', 2, 'd', 'd', 3, 'd', 'd', 'h', 2, 'n', 'n', 1, 't', 2, 't', 'h', 1, 'd', 2, 'd', 'h', 1, 'n', 1, 'p', 2, 'p', 'h', 1, 'b', 2, 'b', 'h', 1, 'm', 2, 't', 's', 3, 't', 's', 'h', 2, 'd', 'z', 3, 'd', 'z', 'h', 1, 'w', 2, 'z', 'h', 1, 'z', 1, '\'', 1, 'y', 1, 'r', 1, 'l', 2, 's', 'h', 3, 's', 's', 'h', 1, 's', 1, 'h', 1, 'a', 3, 'k', 's', 's', 1, 'r', 2, 'a', 'a', 1, 'i', 2, 'i', 'i', 1, 'u', 2, 'u', 'u', 1, 'R', 2, 'R', 'R', 1, 'L', 2, 'L', 'L', 1, 'e', 2, 'e', 'e', 1, 'o', 2, 'o', 'o', 1, 'M', 1, 'H', 1, 'i', 2, 'i', 'i', 1, 'k', 2, 'k', 'h', 1, 'g', 2, 'g', 'h', 2, 'n', 'g', 1, 'c', 2, 'c', 'h', 1, 'j', 2, 'n', 'y', 2, 't', 't', 3, 't', 't', 'h', 2, 'd', 'd', 3, 'd', 'd', 'h', 2, 'n', 'n', 1, 't', 2, 't', 'h', 1, 'd', 2, 'd', 'h', 1, 'n', 1, 'p', 2, 'p', 'h', 1, 'b', 2, 'b', 'h', 1, 'm', 2, 't', 's', 3, 't', 's', 'h', 2, 'd', 'z', 3, 'd', 'z', 'h', 1, 'w', 2, 'z', 'h', 1, 'z', 1, '\'', 1, 'y', 1, 'r', 1, 'l', 2, 's', 'h', 2, 's', 's', 1, 's', 1, 'h', 1, 'a', 3, 'k', 's', 's', 1, 'w', 1, 'y', 1, 'r', 1, 'X', 5, ' ', ':', 'X', ':', ' ', 5, ' ', '/', 'O', '/', ' ', 5, ' ', '/', 'o', '/', ' ', 5, ' ', '\\', 'o', '\\', ' ', 5, ' ', '(', 'O', ')', ' ', 1, 'k', 2, 'k', 'h', 1, 'g', 2, 'g', 'h', 2, 'n', 'g', 1, 'c', 2, 'c', 'h', 1, 'j', 2, 'j', 'h', 2, 'n', 'y', 3, 'n', 'n', 'y', 2, 't', 't', 3, 't', 't', 'h', 2, 'd', 'd', 3, 'd', 'd', 'h', 2, 'n', 'n', 2, 't', 't', 2, 't', 'h', 1, 'd', 2, 'd', 'h', 1, 'n', 1, 'p', 2, 'p', 'h', 1, 'b', 2, 'b', 'h', 1, 'm', 1, 'y', 1, 'r', 1, 'l', 1, 'w', 1, 's', 1, 'h', 2, 'l', 'l', 1, 'a', 1, 'i', 2, 'i', 'i', 1, 'u', 2, 'u', 'u', 1, 'e', 1, 'o', 2, 'a', 'u', 2, 'a', 'a', 1, 'i', 2, 'i', 'i', 1, 'u', 2, 'u', 'u', 1, 'e', 2, 'a', 'i', 1, 'N', 1, '\'', 1, ':', 1, '0', 1, '1', 1, '2', 1, '3', 1, '4', 1, '5', 1, '6', 1, '7', 1, '8', 1, '9', 3, ' ', '/', ' ', 4, ' ', '/', '/', ' ', 2, 'n', '*', 2, 'r', '*', 2, 'l', '*', 2, 'e', '*', 2, 's', 'h', 2, 's', 's', 1, 'R', 2, 'R', 'R', 1, 'L', 2, 'L', 'L', 1, 'R', 2, 'R', 'R', 1, 'L', 2, 'L', 'L', 1, 'A', 1, 'B', 1, 'G', 1, 'D', 1, 'E', 1, 'V', 1, 'Z', 2, 'T', '`', 1, 'I', 1, 'K', 1, 'L', 1, 'M', 1, 'N', 1, 'O', 1, 'P', 2, 'Z', 'h', 1, 'R', 1, 'S', 1, 'T', 1, 'U', 2, 'P', '`', 2, 'K', '`', 2, 'G', '\'', 1, 'Q', 2, 'S', 'h', 3, 'C', 'h', '`', 2, 'C', '`', 2, 'Z', '\'', 1, 'C', 2, 'C', 'h', 1, 'X', 1, 'J', 1, 'H', 1, 'E', 1, 'Y', 1, 'W', 2, 'X', 'h', 2, 'O', 'E', 1, 'a', 1, 'b', 1, 'g', 1, 'd', 1, 'e', 1, 'v', 1, 'z', 2, 't', '`', 1, 'i', 1, 'k', 1, 'l', 1, 'm', 1, 'n', 1, 'o', 1, 'p', 2, 'z', 'h', 1, 'r', 1, 's', 1, 't', 1, 'u', 2, 'p', '`', 2, 'k', '`', 2, 'g', '\'', 1, 'q', 2, 's', 'h', 3, 'c', 'h', '`', 2, 'c', '`', 2, 'z', '\'', 1, 'c', 2, 'c', 'h', 1, 'x', 1, 'j', 1, 'h', 1, 'e', 1, 'y', 1, 'w', 2, 'x', 'h', 2, 'o', 'e', 1, 'f', 4, ' ', '/', '/', ' ', 1, 'g', 2, 'g', 'g', 1, 'n', 1, 'd', 2, 'd', 'd', 1, 'r', 1, 'm', 1, 'b', 2, 'b', 'b', 1, 's', 2, 's', 's', 1, 'j', 2, 'j', 'j', 1, 'c', 1, 'k', 1, 't', 1, 'p', 1, 'h', 2, 'n', 'g', 2, 'n', 'n', 2, 'n', 'd', 2, 'n', 'b', 2, 'd', 'g', 2, 'r', 'n', 2, 'r', 'r', 2, 'r', 'h', 2, 'r', 'N', 2, 'm', 'b', 2, 'm', 'N', 2, 'b', 'g', 2, 'b', 'n', 2, 'b', 's', 3, 'b', 's', 'g', 3, 'b', 's', 't', 3, 'b', 's', 'b', 3, 'b', 's', 's', 3, 'b', 's', 'j', 2, 'b', 'j', 2, 'b', 'c', 2, 'b', 't', 2, 'b', 'p', 2, 'b', 'N', 3, 'b', 'b', 'N', 2, 's', 'g', 2, 's', 'n', 2, 's', 'd', 2, 's', 'r', 2, 's', 'm', 2, 's', 'b', 3, 's', 'b', 'g', 3, 's', 's', 's', 1, 's', 2, 's', 'j', 2, 's', 'c', 2, 's', 'k', 2, 's', 't', 2, 's', 'p', 2, 's', 'h', 1, 'Z', 1, 'g', 1, 'd', 1, 'm', 1, 'b', 1, 's', 1, 'Z', 1, 'j', 1, 'c', 1, 't', 1, 'p', 1, 'N', 1, 'j', 2, 'c', 'k', 2, 'c', 'h', 2, 'p', 'b', 2, 'p', 'N', 2, 'h', 'h', 1, 'Q', 1, 'a', 2, 'a', 'e', 2, 'y', 'a', 3, 'y', 'a', 'e', 2, 'e', 'o', 1, 'e', 3, 'y', 'e', 'o', 2, 'y', 'e', 1, 'o', 2, 'w', 'a', 3, 'w', 'a', 'e', 2, 'o', 'e', 2, 'y', 'o', 1, 'u', 3, 'w', 'e', 'o', 2, 'w', 'e', 2, 'w', 'i', 2, 'y', 'u', 2, 'e', 'u', 2, 'y', 'i', 1, 'i', 3, 'a', '-', 'o', 3, 'a', '-', 'u', 4, 'y', 'a', '-', 'o', 5, 'y', 'a', '-', 'y', 'o', 4, 'e', 'o', '-', 'o', 4, 'e', 'o', '-', 'u', 5, 'e', 'o', '-', 'e', 'u', 5, 'y', 'e', 'o', '-', 'o', 5, 'y', 'e', 'o', '-', 'u', 4, 'o', '-', 'e', 'o', 3, 'o', '-', 'e', 4, 'o', '-', 'y', 'e', 3, 'o', '-', 'o', 3, 'o', '-', 'u', 5, 'y', 'o', '-', 'y', 'a', 6, 'y', 'o', '-', 'y', 'a', 'e', 6, 'y', 'o', '-', 'y', 'e', 'o', 4, 'y', 'o', '-', 'o', 4, 'y', 'o', '-', 'i', 3, 'u', '-', 'a', 4, 'u', '-', 'a', 'e', 7, 'u', '-', 'e', 'o', '-', 'e', 'u', 4, 'u', '-', 'y', 'e', 3, 'u', '-', 'u', 4, 'y', 'u', '-', 'a', 5, 'y', 'u', '-', 'e', 'o', 4, 'y', 'u', '-', 'e', 6, 'y', 'u', '-', 'y', 'e', 'o', 5, 'y', 'u', '-', 'y', 'e', 4, 'y', 'u', '-', 'u', 4, 'y', 'u', '-', 'i', 4, 'e', 'u', '-', 'u', 5, 'e', 'u', '-', 'e', 'u', 4, 'y', 'i', '-', 'u', 3, 'i', '-', 'a', 4, 'i', '-', 'y', 'a', 3, 'i', '-', 'o', 3, 'i', '-', 'u', 4, 'i', '-', 'e', 'u', 3, 'i', '-', 'U', 1, 'U', 4, 'U', '-', 'e', 'o', 3, 'U', '-', 'u', 3, 'U', '-', 'i', 2, 'U', 'U', 1, 'g', 2, 'g', 'g', 2, 'g', 's', 1, 'n', 2, 'n', 'j', 2, 'n', 'h', 1, 'd', 1, 'l', 2, 'l', 'g', 2, 'l', 'm', 2, 'l', 'b', 2, 'l', 's', 2, 'l', 't', 2, 'l', 'p', 2, 'l', 'h', 1, 'm', 1, 'b', 2, 'b', 's', 1, 's', 2, 's', 's', 2, 'n', 'g', 1, 'j', 1, 'c', 1, 'k', 1, 't', 1, 'p', 1, 'h', 2, 'g', 'l', 3, 'g', 's', 'g', 2, 'n', 'g', 2, 'n', 'd', 2, 'n', 's', 2, 'n', 'Z', 2, 'n', 't', 2, 'd', 'g', 2, 't', 'l', 3, 'l', 'g', 's', 2, 'l', 'n', 2, 'l', 'd', 3, 'l', 't', 'h', 2, 'l', 'l', 3, 'l', 'm', 'g', 3, 'l', 'm', 's', 3, 'l', 'b', 's', 3, 'l', 'b', 'h', 3, 'r', 'N', 'p', 3, 'l', 's', 's', 2, 'l', 'Z', 2, 'l', 'k', 2, 'l', 'Q', 2, 'm', 'g', 2, 'm', 'l', 2, 'm', 'b', 2, 'm', 's', 3, 'm', 's', 's', 2, 'm', 'Z', 2, 'm', 'c', 2, 'm', 'h', 2, 'm', 'N', 2, 'b', 'l', 2, 'b', 'p', 2, 'p', 'h', 2, 'p', 'N', 2, 's', 'g', 2, 's', 'd', 2, 's', 'l', 2, 's', 'b', 1, 'Z', 1, 'g', 2, 's', 's', 2, 'k', 'h', 1, 'N', 2, 'N', 's', 2, 'N', 'Z', 2, 'p', 'b', 2, 'p', 'N', 2, 'h', 'n', 2, 'h', 'l', 2, 'h', 'm', 2, 'h', 'b', 1, 'Q', 2, 'h', 'a', 2, 'h', 'u', 2, 'h', 'i', 3, 'h', 'a', 'a', 3, 'h', 'e', 'e', 2, 'h', 'e', 2, 'h', 'o', 2, 'l', 'a', 2, 'l', 'u', 2, 'l', 'i', 3, 'l', 'a', 'a', 3, 'l', 'e', 'e', 2, 'l', 'e', 2, 'l', 'o', 3, 'l', 'w', 'a', 3, 'h', 'h', 'a', 3, 'h', 'h', 'u', 3, 'h', 'h', 'i', 4, 'h', 'h', 'a', 'a', 4, 'h', 'h', 'e', 'e', 3, 'h', 'h', 'e', 3, 'h', 'h', 'o', 4, 'h', 'h', 'w', 'a', 2, 'm', 'a', 2, 'm', 'u', 2, 'm', 'i', 3, 'm', 'a', 'a', 3, 'm', 'e', 'e', 2, 'm', 'e', 2, 'm', 'o', 3, 'm', 'w', 'a', 3, 's', 'z', 'a', 3, 's', 'z', 'u', 3, 's', 'z', 'i', 4, 's', 'z', 'a', 'a', 4, 's', 'z', 'e', 'e', 3, 's', 'z', 'e', 3, 's', 'z', 'o', 4, 's', 'z', 'w', 'a', 2, 'r', 'a', 2, 'r', 'u', 2, 'r', 'i', 3, 'r', 'a', 'a', 3, 'r', 'e', 'e', 2, 'r', 'e', 2, 'r', 'o', 3, 'r', 'w', 'a', 2, 's', 'a', 2, 's', 'u', 2, 's', 'i', 3, 's', 'a', 'a', 3, 's', 'e', 'e', 2, 's', 'e', 2, 's', 'o', 3, 's', 'w', 'a', 3, 's', 'h', 'a', 3, 's', 'h', 'u', 3, 's', 'h', 'i', 4, 's', 'h', 'a', 'a', 4, 's', 'h', 'e', 'e', 3, 's', 'h', 'e', 3, 's', 'h', 'o', 4, 's', 'h', 'w', 'a', 2, 'q', 'a', 2, 'q', 'u', 2, 'q', 'i', 3, 'q', 'a', 'a', 3, 'q', 'e', 'e', 2, 'q', 'e', 2, 'q', 'o', 3, 'q', 'w', 'a', 3, 'q', 'w', 'i', 4, 'q', 'w', 'a', 'a', 4, 'q', 'w', 'e', 'e', 3, 'q', 'w', 'e', 3, 'q', 'h', 'a', 3, 'q', 'h', 'u', 3, 'q', 'h', 'i', 4, 'q', 'h', 'a', 'a', 4, 'q', 'h', 'e', 'e', 3, 'q', 'h', 'e', 3, 'q', 'h', 'o', 4, 'q', 'h', 'w', 'a', 4, 'q', 'h', 'w', 'i', 5, 'q', 'h', 'w', 'a', 'a', 5, 'q', 'h', 'w', 'e', 'e', 4, 'q', 'h', 'w', 'e', 2, 'b', 'a', 2, 'b', 'u', 2, 'b', 'i', 3, 'b', 'a', 'a', 3, 'b', 'e', 'e', 2, 'b', 'e', 2, 'b', 'o', 3, 'b', 'w', 'a', 2, 'v', 'a', 2, 'v', 'u', 2, 'v', 'i', 3, 'v', 'a', 'a', 3, 'v', 'e', 'e', 2, 'v', 'e', 2, 'v', 'o', 3, 'v', 'w', 'a', 2, 't', 'a', 2, 't', 'u', 2, 't', 'i', 3, 't', 'a', 'a', 3, 't', 'e', 'e', 2, 't', 'e', 2, 't', 'o', 3, 't', 'w', 'a', 2, 'c', 'a', 2, 'c', 'u', 2, 'c', 'i', 3, 'c', 'a', 'a', 3, 'c', 'e', 'e', 2, 'c', 'e', 2, 'c', 'o', 3, 'c', 'w', 'a', 2, 'x', 'a', 2, 'x', 'u', 2, 'x', 'i', 3, 'x', 'a', 'a', 3, 'x', 'e', 'e', 2, 'x', 'e', 2, 'x', 'o', 3, 'x', 'w', 'a', 3, 'x', 'w', 'i', 4, 'x', 'w', 'a', 'a', 4, 'x', 'w', 'e', 'e', 3, 'x', 'w', 'e', 2, 'n', 'a', 2, 'n', 'u', 2, 'n', 'i', 3, 'n', 'a', 'a', 3, 'n', 'e', 'e', 2, 'n', 'e', 2, 'n', 'o', 3, 'n', 'w', 'a', 3, 'n', 'y', 'a', 3, 'n', 'y', 'u', 3, 'n', 'y', 'i', 4, 'n', 'y', 'a', 'a', 4, 'n', 'y', 'e', 'e', 3, 'n', 'y', 'e', 3, 'n', 'y', 'o', 4, 'n', 'y', 'w', 'a', 2, '\'', 'a', 2, '\'', 'u', 3, '\'', 'a', 'a', 3, '\'', 'e', 'e', 2, '\'', 'e', 2, '\'', 'o', 3, '\'', 'w', 'a', 2, 'k', 'a', 2, 'k', 'u', 2, 'k', 'i', 3, 'k', 'a', 'a', 3, 'k', 'e', 'e', 2, 'k', 'e', 2, 'k', 'o', 3, 'k', 'w', 'a', 3, 'k', 'w', 'i', 4, 'k', 'w', 'a', 'a', 4, 'k', 'w', 'e', 'e', 3, 'k', 'w', 'e', 3, 'k', 'x', 'a', 3, 'k', 'x', 'u', 3, 'k', 'x', 'i', 4, 'k', 'x', 'a', 'a', 4, 'k', 'x', 'e', 'e', 3, 'k', 'x', 'e', 3, 'k', 'x', 'o', 4, 'k', 'x', 'w', 'a', 4, 'k', 'x', 'w', 'i', 5, 'k', 'x', 'w', 'a', 'a', 5, 'k', 'x', 'w', 'e', 'e', 4, 'k', 'x', 'w', 'e', 2, 'w', 'a', 2, 'w', 'u', 2, 'w', 'i', 3, 'w', 'a', 'a', 3, 'w', 'e', 'e', 2, 'w', 'e', 2, 'w', 'o', 2, '`', 'a', 2, '`', 'u', 2, '`', 'i', 3, '`', 'a', 'a', 3, '`', 'e', 'e', 2, '`', 'e', 2, '`', 'o', 2, 'z', 'a', 2, 'z', 'u', 2, 'z', 'i', 3, 'z', 'a', 'a', 3, 'z', 'e', 'e', 2, 'z', 'e', 2, 'z', 'o', 3, 'z', 'w', 'a', 3, 'z', 'h', 'a', 3, 'z', 'h', 'u', 3, 'z', 'h', 'i', 4, 'z', 'h', 'a', 'a', 4, 'z', 'h', 'e', 'e', 3, 'z', 'h', 'e', 3, 'z', 'h', 'o', 4, 'z', 'h', 'w', 'a', 2, 'y', 'a', 2, 'y', 'u', 2, 'y', 'i', 3, 'y', 'a', 'a', 3, 'y', 'e', 'e', 2, 'y', 'e', 2, 'y', 'o', 2, 'd', 'a', 2, 'd', 'u', 2, 'd', 'i', 3, 'd', 'a', 'a', 3, 'd', 'e', 'e', 2, 'd', 'e', 2, 'd', 'o', 3, 'd', 'w', 'a', 3, 'd', 'd', 'a', 3, 'd', 'd', 'u', 3, 'd', 'd', 'i', 4, 'd', 'd', 'a', 'a', 4, 'd', 'd', 'e', 'e', 3, 'd', 'd', 'e', 3, 'd', 'd', 'o', 4, 'd', 'd', 'w', 'a', 2, 'j', 'a', 2, 'j', 'u', 2, 'j', 'i', 3, 'j', 'a', 'a', 3, 'j', 'e', 'e', 2, 'j', 'e', 2, 'j', 'o', 3, 'j', 'w', 'a', 2, 'g', 'a', 2, 'g', 'u', 2, 'g', 'i', 3, 'g', 'a', 'a', 3, 'g', 'e', 'e', 2, 'g', 'e', 2, 'g', 'o', 3, 'g', 'w', 'a', 3, 'g', 'w', 'i', 4, 'g', 'w', 'a', 'a', 4, 'g', 'w', 'e', 'e', 3, 'g', 'w', 'e', 3, 'g', 'g', 'a', 3, 'g', 'g', 'u', 3, 'g', 'g', 'i', 4, 'g', 'g', 'a', 'a', 4, 'g', 'g', 'e', 'e', 3, 'g', 'g', 'e', 3, 'g', 'g', 'o', 3, 't', 'h', 'a', 3, 't', 'h', 'u', 3, 't', 'h', 'i', 4, 't', 'h', 'a', 'a', 4, 't', 'h', 'e', 'e', 3, 't', 'h', 'e', 3, 't', 'h', 'o', 4, 't', 'h', 'w', 'a', 3, 'c', 'h', 'a', 3, 'c', 'h', 'u', 3, 'c', 'h', 'i', 4, 'c', 'h', 'a', 'a', 4, 'c', 'h', 'e', 'e', 3, 'c', 'h', 'e', 3, 'c', 'h', 'o', 4, 'c', 'h', 'w', 'a', 3, 'p', 'h', 'a', 3, 'p', 'h', 'u', 3, 'p', 'h', 'i', 4, 'p', 'h', 'a', 'a', 4, 'p', 'h', 'e', 'e', 3, 'p', 'h', 'e', 3, 'p', 'h', 'o', 4, 'p', 'h', 'w', 'a', 3, 't', 's', 'a', 3, 't', 's', 'u', 3, 't', 's', 'i', 4, 't', 's', 'a', 'a', 4, 't', 's', 'e', 'e', 3, 't', 's', 'e', 3, 't', 's', 'o', 4, 't', 's', 'w', 'a', 3, 't', 'z', 'a', 3, 't', 'z', 'u', 3, 't', 'z', 'i', 4, 't', 'z', 'a', 'a', 4, 't', 'z', 'e', 'e', 3, 't', 'z', 'e', 3, 't', 'z', 'o', 2, 'f', 'a', 2, 'f', 'u', 2, 'f', 'i', 3, 'f', 'a', 'a', 3, 'f', 'e', 'e', 2, 'f', 'e', 2, 'f', 'o', 3, 'f', 'w', 'a', 2, 'p', 'a', 2, 'p', 'u', 2, 'p', 'i', 3, 'p', 'a', 'a', 3, 'p', 'e', 'e', 2, 'p', 'e', 2, 'p', 'o', 3, 'p', 'w', 'a', 3, 'r', 'y', 'a', 3, 'm', 'y', 'a', 3, 'f', 'y', 'a', 1, ' ', 1, '.', 1, ',', 1, ';', 1, ':', 3, ':', ':', ' ', 1, '?', 2, '/', '/', 1, '1', 1, '2', 1, '3', 1, '4', 1, '5', 1, '6', 1, '7', 1, '8', 1, '9', 3, '1', '0', '+', 3, '2', '0', '+', 3, '3', '0', '+', 3, '4', '0', '+', 3, '5', '0', '+', 3, '6', '0', '+', 3, '7', '0', '+', 3, '8', '0', '+', 3, '9', '0', '+', 4, '1', '0', '0', '+', 7, '1', '0', ',', '0', '0', '0', '+', 1, 'a', 1, 'e', 1, 'i', 1, 'o', 1, 'u', 1, 'v', 2, 'g', 'a', 2, 'k', 'a', 2, 'g', 'e', 2, 'g', 'i', 2, 'g', 'o', 2, 'g', 'u', 2, 'g', 'v', 2, 'h', 'a', 2, 'h', 'e', 2, 'h', 'i', 2, 'h', 'o', 2, 'h', 'u', 2, 'h', 'v', 2, 'l', 'a', 2, 'l', 'e', 2, 'l', 'i', 2, 'l', 'o', 2, 'l', 'u', 2, 'l', 'v', 2, 'm', 'a', 2, 'm', 'e', 2, 'm', 'i', 2, 'm', 'o', 2, 'm', 'u', 2, 'n', 'a', 3, 'h', 'n', 'a', 3, 'n', 'a', 'h', 2, 'n', 'e', 2, 'n', 'i', 2, 'n', 'o', 2, 'n', 'u', 2, 'n', 'v', 3, 'q', 'u', 'a', 3, 'q', 'u', 'e', 3, 'q', 'u', 'i', 3, 'q', 'u', 'o', 3, 'q', 'u', 'u', 3, 'q', 'u', 'v', 2, 's', 'a', 1, 's', 2, 's', 'e', 2, 's', 'i', 2, 's', 'o', 2, 's', 'u', 2, 's', 'v', 2, 'd', 'a', 2, 't', 'a', 2, 'd', 'e', 2, 't', 'e', 2, 'd', 'i', 2, 't', 'i', 2, 'd', 'o', 2, 'd', 'u', 2, 'd', 'v', 3, 'd', 'l', 'a', 3, 't', 'l', 'a', 3, 't', 'l', 'e', 3, 't', 'l', 'i', 3, 't', 'l', 'o', 3, 't', 'l', 'u', 3, 't', 'l', 'v', 3, 't', 's', 'a', 3, 't', 's', 'e', 3, 't', 's', 'i', 3, 't', 's', 'o', 3, 't', 's', 'u', 3, 't', 's', 'v', 2, 'w', 'a', 2, 'w', 'e', 2, 'w', 'i', 2, 'w', 'o', 2, 'w', 'u', 2, 'w', 'v', 2, 'y', 'a', 2, 'y', 'e', 2, 'y', 'i', 2, 'y', 'o', 2, 'y', 'u', 2, 'y', 'v', 1, 'e', 3, 'a', 'a', 'i', 1, 'i', 2, 'i', 'i', 1, 'o', 2, 'o', 'o', 2, 'o', 'o', 2, 'e', 'e', 1, 'i', 1, 'a', 2, 'a', 'a', 2, 'w', 'e', 2, 'w', 'e', 2, 'w', 'i', 2, 'w', 'i', 3, 'w', 'i', 'i', 3, 'w', 'i', 'i', 2, 'w', 'o', 2, 'w', 'o', 3, 'w', 'o', 'o', 3, 'w', 'o', 'o', 3, 'w', 'o', 'o', 2, 'w', 'a', 2, 'w', 'a', 3, 'w', 'a', 'a', 3, 'w', 'a', 'a', 3, 'w', 'a', 'a', 2, 'a', 'i', 1, 'w', 1, '\'', 1, 't', 1, 'k', 2, 's', 'h', 1, 's', 1, 'n', 1, 'w', 1, 'n', 1, 'w', 1, 'c', 1, '?', 1, 'l', 2, 'e', 'n', 2, 'i', 'n', 2, 'o', 'n', 2, 'a', 'n', 2, 'p', 'e', 4, 'p', 'a', 'a', 'i', 2, 'p', 'i', 3, 'p', 'i', 'i', 2, 'p', 'o', 3, 'p', 'o', 'o', 3, 'p', 'o', 'o', 3, 'h', 'e', 'e', 2, 'h', 'i', 2, 'p', 'a', 3, 'p', 'a', 'a', 3, 'p', 'w', 'e', 3, 'p', 'w', 'e', 3, 'p', 'w', 'i', 3, 'p', 'w', 'i', 4, 'p', 'w', 'i', 'i', 4, 'p', 'w', 'i', 'i', 3, 'p', 'w', 'o', 3, 'p', 'w', 'o', 4, 'p', 'w', 'o', 'o', 4, 'p', 'w', 'o', 'o', 3, 'p', 'w', 'a', 3, 'p', 'w', 'a', 4, 'p', 'w', 'a', 'a', 4, 'p', 'w', 'a', 'a', 4, 'p', 'w', 'a', 'a', 1, 'p', 1, 'p', 1, 'h', 2, 't', 'e', 4, 't', 'a', 'a', 'i', 2, 't', 'i', 3, 't', 'i', 'i', 2, 't', 'o', 3, 't', 'o', 'o', 3, 't', 'o', 'o', 3, 'd', 'e', 'e', 2, 'd', 'i', 2, 't', 'a', 3, 't', 'a', 'a', 3, 't', 'w', 'e', 3, 't', 'w', 'e', 3, 't', 'w', 'i', 3, 't', 'w', 'i', 4, 't', 'w', 'i', 'i', 4, 't', 'w', 'i', 'i', 3, 't', 'w', 'o', 3, 't', 'w', 'o', 4, 't', 'w', 'o', 'o', 4, 't', 'w', 'o', 'o', 3, 't', 'w', 'a', 3, 't', 'w', 'a', 4, 't', 'w', 'a', 'a', 4, 't', 'w', 'a', 'a', 4, 't', 'w', 'a', 'a', 1, 't', 3, 't', 't', 'e', 3, 't', 't', 'i', 3, 't', 't', 'o', 3, 't', 't', 'a', 2, 'k', 'e', 4, 'k', 'a', 'a', 'i', 2, 'k', 'i', 3, 'k', 'i', 'i', 2, 'k', 'o', 3, 'k', 'o', 'o', 3, 'k', 'o', 'o', 2, 'k', 'a', 3, 'k', 'a', 'a', 3, 'k', 'w', 'e', 3, 'k', 'w', 'e', 3, 'k', 'w', 'i', 3, 'k', 'w', 'i', 4, 'k', 'w', 'i', 'i', 4, 'k', 'w', 'i', 'i', 3, 'k', 'w', 'o', 3, 'k', 'w', 'o', 4, 'k', 'w', 'o', 'o', 4, 'k', 'w', 'o', 'o', 3, 'k', 'w', 'a', 3, 'k', 'w', 'a', 4, 'k', 'w', 'a', 'a', 4, 'k', 'w', 'a', 'a', 4, 'k', 'w', 'a', 'a', 1, 'k', 2, 'k', 'w', 3, 'k', 'e', 'h', 3, 'k', 'i', 'h', 3, 'k', 'o', 'h', 3, 'k', 'a', 'h', 2, 'c', 'e', 4, 'c', 'a', 'a', 'i', 2, 'c', 'i', 3, 'c', 'i', 'i', 2, 'c', 'o', 3, 'c', 'o', 'o', 3, 'c', 'o', 'o', 2, 'c', 'a', 3, 'c', 'a', 'a', 3, 'c', 'w', 'e', 3, 'c', 'w', 'e', 3, 'c', 'w', 'i', 3, 'c', 'w', 'i', 4, 'c', 'w', 'i', 'i', 4, 'c', 'w', 'i', 'i', 3, 'c', 'w', 'o', 3, 'c', 'w', 'o', 4, 'c', 'w', 'o', 'o', 4, 'c', 'w', 'o', 'o', 3, 'c', 'w', 'a', 3, 'c', 'w', 'a', 4, 'c', 'w', 'a', 'a', 4, 'c', 'w', 'a', 'a', 4, 'c', 'w', 'a', 'a', 1, 'c', 2, 't', 'h', 2, 'm', 'e', 4, 'm', 'a', 'a', 'i', 2, 'm', 'i', 3, 'm', 'i', 'i', 2, 'm', 'o', 3, 'm', 'o', 'o', 3, 'm', 'o', 'o', 2, 'm', 'a', 3, 'm', 'a', 'a', 3, 'm', 'w', 'e', 3, 'm', 'w', 'e', 3, 'm', 'w', 'i', 3, 'm', 'w', 'i', 4, 'm', 'w', 'i', 'i', 4, 'm', 'w', 'i', 'i', 3, 'm', 'w', 'o', 3, 'm', 'w', 'o', 4, 'm', 'w', 'o', 'o', 4, 'm', 'w', 'o', 'o', 3, 'm', 'w', 'a', 3, 'm', 'w', 'a', 4, 'm', 'w', 'a', 'a', 4, 'm', 'w', 'a', 'a', 4, 'm', 'w', 'a', 'a', 1, 'm', 1, 'm', 2, 'm', 'h', 1, 'm', 1, 'm', 2, 'n', 'e', 4, 'n', 'a', 'a', 'i', 2, 'n', 'i', 3, 'n', 'i', 'i', 2, 'n', 'o', 3, 'n', 'o', 'o', 3, 'n', 'o', 'o', 2, 'n', 'a', 3, 'n', 'a', 'a', 3, 'n', 'w', 'e', 3, 'n', 'w', 'e', 3, 'n', 'w', 'a', 3, 'n', 'w', 'a', 4, 'n', 'w', 'a', 'a', 4, 'n', 'w', 'a', 'a', 4, 'n', 'w', 'a', 'a', 1, 'n', 2, 'n', 'g', 2, 'n', 'h', 2, 'l', 'e', 4, 'l', 'a', 'a', 'i', 2, 'l', 'i', 3, 'l', 'i', 'i', 2, 'l', 'o', 3, 'l', 'o', 'o', 3, 'l', 'o', 'o', 2, 'l', 'a', 3, 'l', 'a', 'a', 3, 'l', 'w', 'e', 3, 'l', 'w', 'e', 3, 'l', 'w', 'i', 3, 'l', 'w', 'i', 4, 'l', 'w', 'i', 'i', 4, 'l', 'w', 'i', 'i', 3, 'l', 'w', 'o', 3, 'l', 'w', 'o', 4, 'l', 'w', 'o', 'o', 4, 'l', 'w', 'o', 'o', 3, 'l', 'w', 'a', 3, 'l', 'w', 'a', 4, 'l', 'w', 'a', 'a', 4, 'l', 'w', 'a', 'a', 1, 'l', 1, 'l', 1, 'l', 2, 's', 'e', 4, 's', 'a', 'a', 'i', 2, 's', 'i', 3, 's', 'i', 'i', 2, 's', 'o', 3, 's', 'o', 'o', 3, 's', 'o', 'o', 2, 's', 'a', 3, 's', 'a', 'a', 3, 's', 'w', 'e', 3, 's', 'w', 'e', 3, 's', 'w', 'i', 3, 's', 'w', 'i', 4, 's', 'w', 'i', 'i', 4, 's', 'w', 'i', 'i', 3, 's', 'w', 'o', 3, 's', 'w', 'o', 4, 's', 'w', 'o', 'o', 4, 's', 'w', 'o', 'o', 3, 's', 'w', 'a', 3, 's', 'w', 'a', 4, 's', 'w', 'a', 'a', 4, 's', 'w', 'a', 'a', 4, 's', 'w', 'a', 'a', 1, 's', 1, 's', 2, 's', 'w', 1, 's', 2, 's', 'k', 3, 's', 'k', 'w', 2, 's', 'W', 4, 's', 'p', 'w', 'a', 4, 's', 't', 'w', 'a', 4, 's', 'k', 'w', 'a', 4, 's', 'c', 'w', 'a', 3, 's', 'h', 'e', 3, 's', 'h', 'i', 4, 's', 'h', 'i', 'i', 3, 's', 'h', 'o', 4, 's', 'h', 'o', 'o', 3, 's', 'h', 'a', 4, 's', 'h', 'a', 'a', 4, 's', 'h', 'w', 'e', 4, 's', 'h', 'w', 'e', 4, 's', 'h', 'w', 'i', 4, 's', 'h', 'w', 'i', 5, 's', 'h', 'w', 'i', 'i', 5, 's', 'h', 'w', 'i', 'i', 4, 's', 'h', 'w', 'o', 4, 's', 'h', 'w', 'o', 5, 's', 'h', 'w', 'o', 'o', 5, 's', 'h', 'w', 'o', 'o', 4, 's', 'h', 'w', 'a', 4, 's', 'h', 'w', 'a', 5, 's', 'h', 'w', 'a', 'a', 5, 's', 'h', 'w', 'a', 'a', 2, 's', 'h', 2, 'y', 'e', 4, 'y', 'a', 'a', 'i', 2, 'y', 'i', 3, 'y', 'i', 'i', 2, 'y', 'o', 3, 'y', 'o', 'o', 3, 'y', 'o', 'o', 2, 'y', 'a', 3, 'y', 'a', 'a', 3, 'y', 'w', 'e', 3, 'y', 'w', 'e', 3, 'y', 'w', 'i', 3, 'y', 'w', 'i', 4, 'y', 'w', 'i', 'i', 4, 'y', 'w', 'i', 'i', 3, 'y', 'w', 'o', 3, 'y', 'w', 'o', 4, 'y', 'w', 'o', 'o', 4, 'y', 'w', 'o', 'o', 3, 'y', 'w', 'a', 3, 'y', 'w', 'a', 4, 'y', 'w', 'a', 'a', 4, 'y', 'w', 'a', 'a', 4, 'y', 'w', 'a', 'a', 1, 'y', 1, 'y', 1, 'y', 2, 'y', 'i', 2, 'r', 'e', 2, 'r', 'e', 2, 'l', 'e', 4, 'r', 'a', 'a', 'i', 2, 'r', 'i', 3, 'r', 'i', 'i', 2, 'r', 'o', 3, 'r', 'o', 'o', 2, 'l', 'o', 2, 'r', 'a', 3, 'r', 'a', 'a', 2, 'l', 'a', 4, 'r', 'w', 'a', 'a', 4, 'r', 'w', 'a', 'a', 1, 'r', 1, 'r', 1, 'r', 2, 'f', 'e', 4, 'f', 'a', 'a', 'i', 2, 'f', 'i', 3, 'f', 'i', 'i', 2, 'f', 'o', 3, 'f', 'o', 'o', 2, 'f', 'a', 3, 'f', 'a', 'a', 4, 'f', 'w', 'a', 'a', 4, 'f', 'w', 'a', 'a', 1, 'f', 3, 't', 'h', 'e', 3, 't', 'h', 'e', 3, 't', 'h', 'i', 3, 't', 'h', 'i', 4, 't', 'h', 'i', 'i', 4, 't', 'h', 'i', 'i', 3, 't', 'h', 'o', 4, 't', 'h', 'o', 'o', 3, 't', 'h', 'a', 4, 't', 'h', 'a', 'a', 5, 't', 'h', 'w', 'a', 'a', 5, 't', 'h', 'w', 'a', 'a', 2, 't', 'h', 4, 't', 't', 'h', 'e', 4, 't', 't', 'h', 'i', 4, 't', 't', 'h', 'o', 4, 't', 't', 'h', 'a', 3, 't', 't', 'h', 3, 't', 'y', 'e', 3, 't', 'y', 'i', 3, 't', 'y', 'o', 3, 't', 'y', 'a', 2, 'h', 'e', 2, 'h', 'i', 3, 'h', 'i', 'i', 2, 'h', 'o', 3, 'h', 'o', 'o', 2, 'h', 'a', 3, 'h', 'a', 'a', 1, 'h', 1, 'h', 2, 'h', 'k', 4, 'q', 'a', 'a', 'i', 2, 'q', 'i', 3, 'q', 'i', 'i', 2, 'q', 'o', 3, 'q', 'o', 'o', 2, 'q', 'a', 3, 'q', 'a', 'a', 1, 'q', 4, 't', 'l', 'h', 'e', 4, 't', 'l', 'h', 'i', 4, 't', 'l', 'h', 'o', 4, 't', 'l', 'h', 'a', 2, 'r', 'e', 2, 'r', 'i', 2, 'r', 'o', 2, 'r', 'a', 5, 'n', 'g', 'a', 'a', 'i', 3, 'n', 'g', 'i', 4, 'n', 'g', 'i', 'i', 3, 'n', 'g', 'o', 4, 'n', 'g', 'o', 'o', 3, 'n', 'g', 'a', 4, 'n', 'g', 'a', 'a', 2, 'n', 'g', 3, 'n', 'n', 'g', 3, 's', 'h', 'e', 3, 's', 'h', 'i', 3, 's', 'h', 'o', 3, 's', 'h', 'a', 3, 't', 'h', 'e', 3, 't', 'h', 'i', 3, 't', 'h', 'o', 3, 't', 'h', 'a', 2, 't', 'h', 3, 'l', 'h', 'i', 4, 'l', 'h', 'i', 'i', 3, 'l', 'h', 'o', 4, 'l', 'h', 'o', 'o', 3, 'l', 'h', 'a', 4, 'l', 'h', 'a', 'a', 2, 'l', 'h', 3, 't', 'h', 'e', 3, 't', 'h', 'i', 4, 't', 'h', 'i', 'i', 3, 't', 'h', 'o', 4, 't', 'h', 'o', 'o', 3, 't', 'h', 'a', 4, 't', 'h', 'a', 'a', 2, 't', 'h', 1, 'b', 1, 'e', 1, 'i', 1, 'o', 1, 'a', 2, 'w', 'e', 2, 'w', 'i', 2, 'w', 'o', 2, 'w', 'a', 2, 'n', 'e', 2, 'n', 'i', 2, 'n', 'o', 2, 'n', 'a', 2, 'k', 'e', 2, 'k', 'i', 2, 'k', 'o', 2, 'k', 'a', 2, 'h', 'e', 2, 'h', 'i', 2, 'h', 'o', 2, 'h', 'a', 3, 'g', 'h', 'u', 3, 'g', 'h', 'o', 3, 'g', 'h', 'e', 4, 'g', 'h', 'e', 'e', 3, 'g', 'h', 'i', 3, 'g', 'h', 'a', 2, 'r', 'u', 2, 'r', 'o', 2, 'r', 'e', 3, 'r', 'e', 'e', 2, 'r', 'i', 2, 'r', 'a', 2, 'w', 'u', 2, 'w', 'o', 2, 'w', 'e', 3, 'w', 'e', 'e', 2, 'w', 'i', 2, 'w', 'a', 3, 'h', 'w', 'u', 3, 'h', 'w', 'o', 3, 'h', 'w', 'e', 4, 'h', 'w', 'e', 'e', 3, 'h', 'w', 'i', 3, 'h', 'w', 'a', 3, 't', 'h', 'u', 3, 't', 'h', 'o', 3, 't', 'h', 'e', 4, 't', 'h', 'e', 'e', 3, 't', 'h', 'i', 3, 't', 'h', 'a', 3, 't', 't', 'u', 3, 't', 't', 'o', 3, 't', 't', 'e', 4, 't', 't', 'e', 'e', 3, 't', 't', 'i', 3, 't', 't', 'a', 2, 'p', 'u', 2, 'p', 'o', 2, 'p', 'e', 3, 'p', 'e', 'e', 2, 'p', 'i', 2, 'p', 'a', 1, 'p', 2, 'g', 'u', 2, 'g', 'o', 2, 'g', 'e', 3, 'g', 'e', 'e', 2, 'g', 'i', 2, 'g', 'a', 3, 'k', 'h', 'u', 3, 'k', 'h', 'o', 3, 'k', 'h', 'e', 4, 'k', 'h', 'e', 'e', 3, 'k', 'h', 'i', 3, 'k', 'h', 'a', 3, 'k', 'k', 'u', 3, 'k', 'k', 'o', 3, 'k', 'k', 'e', 4, 'k', 'k', 'e', 'e', 3, 'k', 'k', 'i', 3, 'k', 'k', 'a', 2, 'k', 'k', 2, 'n', 'u', 2, 'n', 'o', 2, 'n', 'e', 3, 'n', 'e', 'e', 2, 'n', 'i', 2, 'n', 'a', 2, 'm', 'u', 2, 'm', 'o', 2, 'm', 'e', 3, 'm', 'e', 'e', 2, 'm', 'i', 2, 'm', 'a', 2, 'y', 'u', 2, 'y', 'o', 2, 'y', 'e', 3, 'y', 'e', 'e', 2, 'y', 'i', 2, 'y', 'a', 2, 'j', 'u', 2, 'j', 'u', 2, 'j', 'o', 2, 'j', 'e', 3, 'j', 'e', 'e', 2, 'j', 'i', 2, 'j', 'i', 2, 'j', 'a', 3, 'j', 'j', 'u', 3, 'j', 'j', 'o', 3, 'j', 'j', 'e', 4, 'j', 'j', 'e', 'e', 3, 'j', 'j', 'i', 3, 'j', 'j', 'a', 2, 'l', 'u', 2, 'l', 'o', 2, 'l', 'e', 3, 'l', 'e', 'e', 2, 'l', 'i', 2, 'l', 'a', 3, 'd', 'l', 'u', 3, 'd', 'l', 'o', 3, 'd', 'l', 'e', 4, 'd', 'l', 'e', 'e', 3, 'd', 'l', 'i', 3, 'd', 'l', 'a', 3, 'l', 'h', 'u', 3, 'l', 'h', 'o', 3, 'l', 'h', 'e', 4, 'l', 'h', 'e', 'e', 3, 'l', 'h', 'i', 3, 'l', 'h', 'a', 4, 't', 'l', 'h', 'u', 4, 't', 'l', 'h', 'o', 4, 't', 'l', 'h', 'e', 5, 't', 'l', 'h', 'e', 'e', 4, 't', 'l', 'h', 'i', 4, 't', 'l', 'h', 'a', 3, 't', 'l', 'u', 3, 't', 'l', 'o', 3, 't', 'l', 'e', 4, 't', 'l', 'e', 'e', 3, 't', 'l', 'i', 3, 't', 'l', 'a', 2, 'z', 'u', 2, 'z', 'o', 2, 'z', 'e', 3, 'z', 'e', 'e', 2, 'z', 'i', 2, 'z', 'a', 1, 'z', 1, 'z', 3, 'd', 'z', 'u', 3, 'd', 'z', 'o', 3, 'd', 'z', 'e', 4, 'd', 'z', 'e', 'e', 3, 'd', 'z', 'i', 3, 'd', 'z', 'a', 2, 's', 'u', 2, 's', 'o', 2, 's', 'e', 3, 's', 'e', 'e', 2, 's', 'i', 2, 's', 'a', 3, 's', 'h', 'u', 3, 's', 'h', 'o', 3, 's', 'h', 'e', 4, 's', 'h', 'e', 'e', 3, 's', 'h', 'i', 3, 's', 'h', 'a', 2, 's', 'h', 3, 't', 's', 'u', 3, 't', 's', 'o', 3, 't', 's', 'e', 4, 't', 's', 'e', 'e', 3, 't', 's', 'i', 3, 't', 's', 'a', 3, 'c', 'h', 'u', 3, 'c', 'h', 'o', 3, 'c', 'h', 'e', 4, 'c', 'h', 'e', 'e', 3, 'c', 'h', 'i', 3, 'c', 'h', 'a', 4, 't', 't', 's', 'u', 4, 't', 't', 's', 'o', 4, 't', 't', 's', 'e', 5, 't', 't', 's', 'e', 'e', 4, 't', 't', 's', 'i', 4, 't', 't', 's', 'a', 1, 'X', 1, '.', 3, 'q', 'a', 'i', 4, 'n', 'g', 'a', 'i', 4, 'n', 'n', 'g', 'i', 5, 'n', 'n', 'g', 'i', 'i', 4, 'n', 'n', 'g', 'o', 5, 'n', 'n', 'g', 'o', 'o', 4, 'n', 'n', 'g', 'a', 5, 'n', 'n', 'g', 'a', 'a', 1, ' ', 1, 'b', 1, 'l', 1, 'f', 1, 's', 1, 'n', 1, 'h', 1, 'd', 1, 't', 1, 'c', 1, 'q', 1, 'm', 1, 'g', 2, 'n', 'g', 1, 'z', 1, 'r', 1, 'a', 1, 'o', 1, 'u', 1, 'e', 1, 'i', 2, 'c', 'h', 2, 't', 'h', 2, 'p', 'h', 1, 'p', 1, 'x', 1, 'p', 1, '<', 1, '>', 1, 'f', 1, 'v', 1, 'u', 2, 'y', 'r', 1, 'y', 1, 'w', 2, 't', 'h', 2, 't', 'h', 1, 'a', 1, 'o', 2, 'a', 'c', 2, 'a', 'e', 1, 'o', 1, 'o', 1, 'o', 2, 'o', 'e', 2, 'o', 'n', 1, 'r', 1, 'k', 1, 'c', 1, 'k', 1, 'g', 2, 'n', 'g', 1, 'g', 1, 'g', 1, 'w', 1, 'h', 1, 'h', 1, 'h', 1, 'h', 1, 'n', 1, 'n', 1, 'n', 1, 'i', 1, 'e', 1, 'j', 1, 'g', 2, 'a', 'e', 1, 'a', 2, 'e', 'o', 1, 'p', 1, 'z', 1, 's', 1, 's', 1, 's', 1, 'c', 1, 'z', 1, 't', 1, 't', 1, 'd', 1, 'b', 1, 'b', 1, 'p', 1, 'p', 1, 'e', 1, 'm', 1, 'm', 1, 'm', 1, 'l', 1, 'l', 2, 'n', 'g', 2, 'n', 'g', 1, 'd', 1, 'o', 3, 'e', 'a', 'r', 3, 'i', 'o', 'r', 2, 'q', 'u', 2, 'q', 'u', 2, 'q', 'u', 1, 's', 2, 'y', 'r', 2, 'y', 'r', 2, 'y', 'r', 1, 'q', 1, 'x', 1, '.', 1, ':', 1, '+', 2, '1', '7', 2, '1', '8', 2, '1', '9', 1, 'k', 2, 'k', 'h', 1, 'g', 2, 'g', 'h', 2, 'n', 'g', 1, 'c', 2, 'c', 'h', 1, 'j', 2, 'j', 'h', 2, 'n', 'y', 1, 't', 3, 't', 't', 'h', 1, 'd', 3, 'd', 'd', 'h', 2, 'n', 'n', 1, 't', 2, 't', 'h', 1, 'd', 2, 'd', 'h', 1, 'n', 1, 'p', 2, 'p', 'h', 1, 'b', 2, 'b', 'h', 1, 'm', 1, 'y', 1, 'r', 1, 'l', 1, 'v', 2, 's', 'h', 2, 's', 's', 1, 's', 1, 'h', 1, 'l', 1, 'q', 1, 'a', 2, 'a', 'a', 1, 'i', 2, 'i', 'i', 1, 'u', 2, 'u', 'k', 2, 'u', 'u', 3, 'u', 'u', 'v', 2, 'r', 'y', 3, 'r', 'y', 'y', 2, 'l', 'y', 3, 'l', 'y', 'y', 1, 'e', 2, 'a', 'i', 2, 'o', 'o', 2, 'o', 'o', 2, 'a', 'u', 1, 'a', 2, 'a', 'a', 2, 'a', 'a', 1, 'i', 2, 'i', 'i', 1, 'y', 2, 'y', 'y', 1, 'u', 2, 'u', 'u', 2, 'u', 'a', 2, 'o', 'e', 2, 'y', 'a', 2, 'i', 'e', 1, 'e', 2, 'a', 'e', 2, 'a', 'i', 2, 'o', 'o', 2, 'a', 'u', 1, 'M', 1, 'H', 2, 'a', '`', 1, 'r', 1, '!', 1, '.', 4, ' ', '/', '/', ' ', 1, ':', 1, '+', 2, '+', '+', 3, ' ', '*', ' ', 5, ' ', '/', '/', '/', ' ', 2, 'K', 'R', 1, '\'', 1, '0', 1, '1', 1, '2', 1, '3', 1, '4', 1, '5', 1, '6', 1, '7', 1, '8', 1, '9', 3, ' ', '@', ' ', 5, ' ', '.', '.', '.', ' ', 2, ',', ' ', 2, '.', ' ', 2, ':', ' ', 4, ' ', '/', '/', ' ', 1, '-', 2, ',', ' ', 2, '.', ' ', 1, '0', 1, '1', 1, '2', 1, '3', 1, '4', 1, '5', 1, '6', 1, '7', 1, '8', 1, '9', 1, 'a', 1, 'e', 1, 'i', 1, 'o', 1, 'u', 1, 'O', 1, 'U', 2, 'e', 'e', 1, 'n', 2, 'n', 'g', 1, 'b', 1, 'p', 1, 'q', 1, 'g', 1, 'm', 1, 'l', 1, 's', 2, 's', 'h', 1, 't', 1, 'd', 2, 'c', 'h', 1, 'j', 1, 'y', 1, 'r', 1, 'w', 1, 'f', 1, 'k', 3, 'k', 'h', 'a', 2, 't', 's', 1, 'z', 1, 'h', 2, 'z', 'r', 2, 'l', 'h', 2, 'z', 'h', 2, 'c', 'h', 1, '-', 1, 'e', 1, 'i', 1, 'o', 1, 'u', 1, 'O', 1, 'U', 2, 'n', 'g', 1, 'b', 1, 'p', 1, 'q', 1, 'g', 1, 'm', 1, 't', 1, 'd', 2, 'c', 'h', 1, 'j', 2, 't', 's', 1, 'y', 1, 'w', 1, 'k', 1, 'g', 1, 'h', 2, 'j', 'y', 2, 'n', 'y', 2, 'd', 'z', 1, 'e', 1, 'i', 2, 'i', 'y', 1, 'U', 1, 'u', 2, 'n', 'g', 1, 'k', 1, 'g', 1, 'h', 1, 'p', 2, 's', 'h', 1, 't', 1, 'd', 1, 'j', 1, 'f', 1, 'g', 1, 'h', 2, 't', 's', 1, 'z', 1, 'r', 2, 'c', 'h', 2, 'z', 'h', 1, 'i', 1, 'k', 1, 'r', 1, 'f', 2, 'z', 'h', 1, 'H', 1, 'X', 1, 'W', 1, 'M', 3, ' ', '3', ' ', 5, ' ', '3', '3', '3', ' ', 1, 'a', 1, 'i', 1, 'k', 2, 'n', 'g', 1, 'c', 2, 't', 't', 3, 't', 't', 'h', 2, 'd', 'd', 2, 'n', 'n', 1, 't', 1, 'd', 1, 'p', 2, 'p', 'h', 2, 's', 's', 2, 'z', 'h', 1, 'z', 1, 'a', 1, 't', 2, 'z', 'h', 2, 'g', 'h', 2, 'n', 'g', 1, 'c', 2, 'j', 'h', 3, 't', 't', 'a', 3, 'd', 'd', 'h', 1, 't', 2, 'd', 'h', 2, 's', 's', 2, 'c', 'y', 2, 'z', 'h', 1, 'z', 1, 'u', 1, 'y', 2, 'b', 'h', 1, '\'', 1, 'A', 2, 'A', 'E', 2, 'a', 'e', 1, 'B', 1, 'C', 1, 'D', 1, 'D', 1, 'E', 1, 'e', 1, 'i', 1, 'J', 1, 'K', 1, 'L', 1, 'M', 1, 'N', 1, 'O', 1, 'O', 1, 'O', 2, 'O', 'e', 2, 'O', 'u', 1, 'P', 1, 'R', 1, 'R', 1, 'T', 1, 'U', 1, 'u', 1, 'u', 1, 'm', 1, 'V', 1, 'W', 1, 'Z', 1, 'A', 2, 'A', 'E', 1, 'B', 1, 'B', 1, 'D', 1, 'E', 1, 'E', 1, 'G', 1, 'H', 1, 'I', 1, 'J', 1, 'K', 1, 'L', 1, 'M', 1, 'N', 1, 'N', 1, 'O', 2, 'O', 'u', 1, 'P', 1, 'R', 1, 'T', 1, 'U', 1, 'W', 1, 'a', 1, 'a', 1, 'a', 2, 'a', 'e', 1, 'b', 1, 'd', 1, 'e', 1, 'e', 1, 'e', 1, 'g', 1, 'i', 1, 'k', 1, 'm', 1, 'o', 1, 'p', 1, 't', 1, 'u', 1, 'u', 1, 'm', 1, 'v', 1, 'b', 1, 'g', 1, 'd', 1, 'f', 1, 'i', 1, 'r', 1, 'u', 1, 'v', 1, 'b', 1, 'g', 1, 'r', 1, 'f', 1, 'b', 1, 'd', 1, 'f', 1, 'm', 1, 'n', 1, 'p', 1, 'r', 1, 'r', 1, 's', 1, 't', 1, 'z', 1, 'g', 1, 'p', 1, 'b', 1, 'd', 1, 'f', 1, 'g', 1, 'k', 1, 'l', 1, 'm', 1, 'n', 1, 'p', 1, 'r', 1, 's', 1, 'v', 1, 'x', 1, 'z', 1, 'A', 1, 'a', 1, 'B', 1, 'b', 1, 'B', 1, 'b', 1, 'B', 1, 'b', 1, 'C', 1, 'c', 1, 'D', 1, 'd', 1, 'D', 1, 'd', 1, 'D', 1, 'd', 1, 'D', 1, 'd', 1, 'D', 1, 'd', 1, 'E', 1, 'e', 1, 'E', 1, 'e', 1, 'E', 1, 'e', 1, 'E', 1, 'e', 1, 'E', 1, 'e', 1, 'F', 1, 'f', 1, 'G', 1, 'g', 1, 'H', 1, 'h', 1, 'H', 1, 'h', 1, 'H', 1, 'h', 1, 'H', 1, 'h', 1, 'H', 1, 'h', 1, 'I', 1, 'i', 1, 'I', 1, 'i', 1, 'K', 1, 'k', 1, 'K', 1, 'k', 1, 'K', 1, 'k', 1, 'L', 1, 'l', 1, 'L', 1, 'l', 1, 'L', 1, 'l', 1, 'L', 1, 'l', 1, 'M', 1, 'm', 1, 'M', 1, 'm', 1, 'M', 1, 'm', 1, 'N', 1, 'n', 1, 'N', 1, 'n', 1, 'N', 1, 'n', 1, 'N', 1, 'n', 1, 'O', 1, 'o', 1, 'O', 1, 'o', 1, 'O', 1, 'o', 1, 'O', 1, 'o', 1, 'P', 1, 'p', 1, 'P', 1, 'p', 1, 'R', 1, 'r', 1, 'R', 1, 'r', 1, 'R', 1, 'r', 1, 'R', 1, 'r', 1, 'S', 1, 's', 1, 'S', 1, 's', 1, 'S', 1, 's', 1, 'S', 1, 's', 1, 'S', 1, 's', 1, 'T', 1, 't', 1, 'T', 1, 't', 1, 'T', 1, 't', 1, 'T', 1, 't', 1, 'U', 1, 'u', 1, 'U', 1, 'u', 1, 'U', 1, 'u', 1, 'U', 1, 'u', 1, 'U', 1, 'u', 1, 'V', 1, 'v', 1, 'V', 1, 'v', 1, 'W', 1, 'w', 1, 'W', 1, 'w', 1, 'W', 1, 'w', 1, 'W', 1, 'w', 1, 'W', 1, 'w', 1, 'X', 1, 'x', 1, 'X', 1, 'x', 1, 'Y', 1, 'y', 1, 'Z', 1, 'z', 1, 'Z', 1, 'z', 1, 'Z', 1, 'z', 1, 'h', 1, 't', 1, 'w', 1, 'y', 1, 'a', 1, 'S', 2, 'S', 's', 1, 'A', 1, 'a', 1, 'A', 1, 'a', 1, 'A', 1, 'a', 1, 'A', 1, 'a', 1, 'A', 1, 'a', 1, 'A', 1, 'a', 1, 'A', 1, 'a', 1, 'A', 1, 'a', 1, 'A', 1, 'a', 1, 'A', 1, 'a', 1, 'A', 1, 'a', 1, 'A', 1, 'a', 1, 'E', 1, 'e', 1, 'E', 1, 'e', 1, 'E', 1, 'e', 1, 'E', 1, 'e', 1, 'E', 1, 'e', 1, 'E', 1, 'e', 1, 'E', 1, 'e', 1, 'E', 1, 'e', 1, 'I', 1, 'i', 1, 'I', 1, 'i', 1, 'O', 1, 'o', 1, 'O', 1, 'o', 1, 'O', 1, 'o', 1, 'O', 1, 'o', 1, 'O', 1, 'o', 1, 'O', 1, 'o', 1, 'O', 1, 'o', 1, 'O', 1, 'o', 1, 'O', 1, 'o', 1, 'O', 1, 'o', 1, 'O', 1, 'o', 1, 'O', 1, 'o', 1, 'U', 1, 'u', 1, 'U', 1, 'u', 1, 'U', 1, 'u', 1, 'U', 1, 'u', 1, 'U', 1, 'u', 1, 'U', 1, 'u', 1, 'U', 1, 'u', 1, 'Y', 1, 'y', 1, 'Y', 1, 'y', 1, 'Y', 1, 'y', 1, 'Y', 1, 'y', 1, 'a', 1, 'a', 1, 'a', 1, 'a', 1, 'a', 1, 'a', 1, 'a', 1, 'a', 1, 'A', 1, 'A', 1, 'A', 1, 'A', 1, 'A', 1, 'A', 1, 'A', 1, 'A', 1, 'e', 1, 'e', 1, 'e', 1, 'e', 1, 'e', 1, 'e', 1, 'E', 1, 'E', 1, 'E', 1, 'E', 1, 'E', 1, 'E', 1, 'e', 1, 'e', 1, 'e', 1, 'e', 1, 'e', 1, 'e', 1, 'e', 1, 'e', 1, 'E', 1, 'E', 1, 'E', 1, 'E', 1, 'E', 1, 'E', 1, 'E', 1, 'E', 1, 'i', 1, 'i', 1, 'i', 1, 'i', 1, 'i', 1, 'i', 1, 'i', 1, 'i', 1, 'I', 1, 'I', 1, 'I', 1, 'I', 1, 'I', 1, 'I', 1, 'I', 1, 'I', 1, 'o', 1, 'o', 1, 'o', 1, 'o', 1, 'o', 1, 'o', 1, 'O', 1, 'O', 1, 'O', 1, 'O', 1, 'O', 1, 'O', 1, 'u', 1, 'u', 1, 'u', 1, 'u', 1, 'u', 1, 'u', 1, 'u', 1, 'u', 1, 'U', 1, 'U', 1, 'U', 1, 'U', 1, 'o', 1, 'o', 1, 'o', 1, 'o', 1, 'o', 1, 'o', 1, 'o', 1, 'o', 1, 'O', 1, 'O', 1, 'O', 1, 'O', 1, 'O', 1, 'O', 1, 'O', 1, 'O', 1, 'a', 1, 'a', 1, 'e', 1, 'e', 1, 'e', 1, 'e', 1, 'i', 1, 'i', 1, 'o', 1, 'o', 1, 'u', 1, 'u', 1, 'o', 1, 'o', 1, 'a', 1, 'a', 1, 'a', 1, 'a', 1, 'a', 1, 'a', 1, 'a', 1, 'a', 1, 'A', 1, 'A', 1, 'A', 1, 'A', 1, 'A', 1, 'A', 1, 'A', 1, 'A', 1, 'e', 1, 'e', 1, 'e', 1, 'e', 1, 'e', 1, 'e', 1, 'e', 1, 'e', 1, 'E', 1, 'E', 1, 'E', 1, 'E', 1, 'E', 1, 'E', 1, 'E', 1, 'E', 1, 'o', 1, 'o', 1, 'o', 1, 'o', 1, 'o', 1, 'o', 1, 'o', 1, 'o', 1, 'O', 1, 'O', 1, 'O', 1, 'O', 1, 'O', 1, 'O', 1, 'O', 1, 'O', 1, 'a', 1, 'a', 1, 'a', 1, 'a', 1, 'a', 1, 'a', 1, 'a', 1, 'A', 1, 'A', 1, 'A', 1, 'A', 1, 'A', 1, '\'', 1, 'i', 1, '\'', 1, '~', 2, '\"', '~', 1, 'e', 1, 'e', 1, 'e', 1, 'e', 1, 'e', 1, 'E', 1, 'E', 1, 'E', 1, 'E', 1, 'E', 2, '\'', '`', 2, '\'', '\'', 2, '\'', '~', 1, 'i', 1, 'i', 1, 'i', 1, 'i', 1, 'i', 1, 'i', 1, 'I', 1, 'I', 1, 'I', 1, 'I', 2, '`', '\'', 2, '`', '\'', 2, '`', '~', 1, 'u', 1, 'u', 1, 'u', 1, 'u', 1, 'R', 1, 'R', 1, 'u', 1, 'u', 1, 'U', 1, 'U', 1, 'U', 1, 'U', 1, 'R', 2, '\"', '`', 2, '\"', '\'', 1, '`', 1, 'o', 1, 'o', 1, 'o', 1, 'o', 1, 'o', 1, 'O', 1, 'O', 1, 'O', 1, 'O', 1, 'O', 1, '\'', 1, '`', 1, ' ', 1, ' ', 1, ' ', 1, ' ', 1, ' ', 1, ' ', 1, ' ', 1, ' ', 1, ' ', 1, ' ', 1, ' ', 1, ' ', 1, '-', 1, '-', 1, '-', 1, '-', 2, '-', '-', 2, '-', '-', 2, '|', '|', 1, '_', 1, '\'', 1, '\'', 1, ',', 1, '\'', 1, '\"', 1, '\"', 2, ',', ',', 1, '\"', 1, '+', 2, '+', '+', 1, '*', 2, '*', '>', 1, '.', 2, '.', '.', 3, '.', '.', '.', 1, '.', 1, ' ', 2, ' ', ' ', 1, ' ', 2, '%', '0', 3, '%', '0', '0', 1, '\'', 2, '\'', '\'', 3, '\'', '\'', '\'', 1, '`', 2, '`', '`', 3, '`', '`', '`', 1, '^', 1, '<', 1, '>', 1, '*', 2, '!', '!', 2, '!', '?', 1, '-', 1, '_', 1, '-', 1, '^', 3, '*', '*', '*', 2, '-', '-', 1, '/', 2, '-', '[', 2, ']', '-', 2, '?', '?', 2, '?', '!', 2, '!', '?', 1, '&', 2, 'P', 'P', 2, '(', ']', 2, '[', ')', 1, '*', 1, '%', 1, '~', 4, '\'', '\'', '\'', '\'', 1, ' ', 1, '0', 1, 'i', 1, '4', 1, '5', 1, '6', 1, '7', 1, '8', 1, '9', 1, '+', 1, '-', 1, '=', 1, '(', 1, ')', 1, 'n', 1, '0', 1, '1', 1, '2', 1, '3', 1, '4', 1, '5', 1, '6', 1, '7', 1, '8', 1, '9', 1, '+', 1, '-', 1, '=', 1, '(', 1, ')', 1, 'a', 1, 'e', 1, 'o', 1, 'x', 1, 'h', 1, 'k', 1, 'l', 1, 'm', 1, 'n', 1, 'p', 1, 's', 1, 't', 3, 'E', 'C', 'U', 2, 'C', 'L', 2, 'C', 'r', 2, 'F', 'F', 1, 'L', 3, 'm', 'i', 'l', 1, 'N', 3, 'P', 't', 's', 2, 'R', 's', 1, 'W', 2, 'N', 'S', 1, 'D', 3, 'E', 'U', 'R', 1, 'K', 1, 'T', 2, 'D', 'r', 2, 'P', 'f', 1, 'P', 1, 'G', 1, 'A', 3, 'U', 'A', 'H', 2, 'C', '|', 1, 'L', 2, 'S', 'm', 1, 'T', 2, 'R', 's', 1, 'L', 1, 'M', 1, 'm', 1, 'R', 1, 'l', 3, 'B', 'T', 'C', 5, ' ', 'a', '/', 'c', ' ', 5, ' ', 'a', '/', 's', ' ', 1, 'C', 5, ' ', 'c', '/', 'o', ' ', 5, ' ', 'c', '/', 'u', ' ', 1, 'g', 1, 'H', 1, 'H', 1, 'H', 1, 'h', 1, 'I', 1, 'I', 1, 'L', 1, 'l', 1, 'N', 4, 'N', 'o', '.', ' ', 1, 'P', 1, 'Q', 1, 'R', 1, 'R', 1, 'R', 4, '(', 's', 'm', ')', 3, 'T', 'E', 'L', 4, '(', 't', 'm', ')', 1, 'Z', 1, 'Z', 1, 'K', 1, 'A', 1, 'B', 1, 'C', 1, 'e', 1, 'e', 1, 'E', 1, 'F', 1, 'F', 1, 'M', 1, 'o', 1, 'i', 3, 'F', 'A', 'X', 1, 'D', 1, 'd', 1, 'e', 1, 'i', 1, 'j', 1, 'F', 5, ' ', '1', '/', '7', ' ', 5, ' ', '1', '/', '9', ' ', 6, ' ', '1', '/', '1', '0', ' ', 5, ' ', '1', '/', '3', ' ', 5, ' ', '2', '/', '3', ' ', 5, ' ', '1', '/', '5', ' ', 5, ' ', '2', '/', '5', ' ', 5, ' ', '3', '/', '5', ' ', 5, ' ', '4', '/', '5', ' ', 5, ' ', '1', '/', '6', ' ', 5, ' ', '5', '/', '6', ' ', 5, ' ', '1', '/', '8', ' ', 5, ' ', '3', '/', '8', ' ', 5, ' ', '5', '/', '8', ' ', 5, ' ', '7', '/', '8', ' ', 3, ' ', '1', '/', 1, 'I', 2, 'I', 'I', 3, 'I', 'I', 'I', 2, 'I', 'V', 1, 'V', 2, 'V', 'I', 3, 'V', 'I', 'I', 4, 'V', 'I', 'I', 'I', 2, 'I', 'X', 1, 'X', 2, 'X', 'I', 3, 'X', 'I', 'I', 1, 'L', 1, 'C', 1, 'D', 1, 'M', 1, 'i', 2, 'i', 'i', 3, 'i', 'i', 'i', 2, 'i', 'v', 1, 'v', 2, 'v', 'i', 3, 'v', 'i', 'i', 4, 'v', 'i', 'i', 'i', 2, 'i', 'x', 1, 'x', 2, 'x', 'i', 3, 'x', 'i', 'i', 1, 'l', 1, 'c', 1, 'd', 1, 'm', 2, '(', 'D', 2, 'D', ')', 5, '(', '(', '|', ')', ')', 1, ')', 5, ' ', '0', '/', '3', ' ', 1, '-', 1, '|', 1, '-', 1, '|', 1, '-', 1, '|', 1, '\\', 1, '/', 1, '\\', 1, '/', 1, '-', 1, '-', 1, '~', 1, '~', 1, '-', 1, '|', 1, '-', 1, '|', 1, '-', 1, '-', 1, '-', 1, '|', 1, '-', 1, '|', 1, '|', 1, '-', 1, '-', 1, '-', 1, '-', 1, '-', 1, '-', 1, '|', 1, '|', 1, '|', 1, '|', 1, '|', 1, '|', 1, '|', 1, '^', 1, 'V', 1, '\\', 1, '=', 1, 'V', 1, '^', 1, '-', 1, '-', 1, '|', 1, '|', 1, '-', 1, '-', 1, '|', 1, '|', 1, '=', 1, '|', 1, '=', 1, '=', 1, '|', 1, '=', 1, '|', 1, '=', 1, '=', 1, '=', 1, '=', 1, '=', 1, '=', 1, '|', 1, '=', 1, '|', 1, '=', 1, '|', 1, '\\', 1, '/', 1, '\\', 1, '/', 1, '=', 1, '=', 1, '~', 1, '~', 1, '|', 1, '|', 1, '-', 1, '|', 1, '-', 1, '|', 1, '-', 1, '-', 1, '-', 1, '|', 1, '-', 1, '|', 1, '|', 1, '|', 1, '|', 1, '|', 1, '|', 1, '|', 1, '-', 1, '\\', 1, '\\', 1, '|', 1, '-', 1, '/', 1, '\\', 1, '*', 1, '|', 1, ':', 1, '~', 2, '<', '=', 2, '>', '=', 2, '<', '=', 2, '>', '=', 1, '^', 1, '<', 2, '>', ' ', 1, '1', 1, '2', 1, '3', 1, '4', 1, '5', 1, '6', 1, '7', 1, '8', 1, '9', 2, '1', '0', 2, '1', '1', 2, '1', '2', 2, '1', '3', 2, '1', '4', 2, '1', '5', 2, '1', '6', 2, '1', '7', 2, '1', '8', 2, '1', '9', 2, '2', '0', 3, '(', '1', ')', 3, '(', '2', ')', 3, '(', '3', ')', 3, '(', '4', ')', 3, '(', '5', ')', 3, '(', '6', ')', 3, '(', '7', ')', 3, '(', '8', ')', 3, '(', '9', ')', 4, '(', '1', '0', ')', 4, '(', '1', '1', ')', 4, '(', '1', '2', ')', 4, '(', '1', '3', ')', 4, '(', '1', '4', ')', 4, '(', '1', '5', ')', 4, '(', '1', '6', ')', 4, '(', '1', '7', ')', 4, '(', '1', '8', ')', 4, '(', '1', '9', ')', 4, '(', '2', '0', ')', 2, '1', '.', 2, '2', '.', 2, '3', '.', 2, '4', '.', 2, '5', '.', 2, '6', '.', 2, '7', '.', 2, '8', '.', 2, '9', '.', 3, '1', '0', '.', 3, '1', '1', '.', 3, '1', '2', '.', 3, '1', '3', '.', 3, '1', '4', '.', 3, '1', '5', '.', 3, '1', '6', '.', 3, '1', '7', '.', 3, '1', '8', '.', 3, '1', '9', '.', 3, '2', '0', '.', 3, '(', 'a', ')', 3, '(', 'b', ')', 3, '(', 'c', ')', 3, '(', 'd', ')', 3, '(', 'e', ')', 3, '(', 'f', ')', 3, '(', 'g', ')', 3, '(', 'h', ')', 3, '(', 'i', ')', 3, '(', 'j', ')', 3, '(', 'k', ')', 3, '(', 'l', ')', 3, '(', 'm', ')', 3, '(', 'n', ')', 3, '(', 'o', ')', 3, '(', 'p', ')', 3, '(', 'q', ')', 3, '(', 'r', ')', 3, '(', 's', ')', 3, '(', 't', ')', 3, '(', 'u', ')', 3, '(', 'v', ')', 3, '(', 'w', ')', 3, '(', 'x', ')', 3, '(', 'y', ')', 3, '(', 'z', ')', 1, 'A', 1, 'B', 1, 'C', 1, 'D', 1, 'E', 1, 'F', 1, 'G', 1, 'H', 1, 'I', 1, 'J', 1, 'K', 1, 'L', 1, 'M', 1, 'N', 1, 'O', 1, 'P', 1, 'Q', 1, 'R', 1, 'S', 1, 'T', 1, 'U', 1, 'V', 1, 'W', 1, 'X', 1, 'Y', 1, 'Z', 1, 'a', 1, 'b', 1, 'c', 1, 'd', 1, 'e', 1, 'f', 1, 'g', 1, 'h', 1, 'i', 1, 'j', 1, 'k', 1, 'l', 1, 'm', 1, 'n', 1, 'o', 1, 'p', 1, 'q', 1, 'r', 1, 's', 1, 't', 1, 'u', 1, 'v', 1, 'w', 1, 'x', 1, 'y', 1, 'z', 1, '0', 2, '1', '1', 2, '1', '2', 2, '1', '3', 2, '1', '4', 2, '1', '5', 2, '1', '6', 2, '1', '7', 2, '1', '8', 2, '1', '9', 2, '2', '0', 1, '1', 1, '2', 1, '3', 1, '4', 1, '5', 1, '6', 1, '7', 1, '8', 1, '9', 2, '1', '0', 1, '0', 1, '-', 1, '-', 1, '|', 1, '|', 1, '-', 1, '-', 1, '|', 1, '|', 1, '-', 1, '-', 1, '|', 1, '|', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '-', 1, '-', 1, '|', 1, '|', 1, '-', 1, '|', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '+', 1, '/', 1, '\\', 1, 'X', 1, '-', 1, '|', 1, '-', 1, '|', 1, '-', 1, '|', 1, '-', 1, '|', 1, '-', 1, '|', 1, '-', 1, '|', 1, '#', 1, '#', 1, '#', 1, '#', 1, '#', 1, '#', 1, '#', 1, '#', 1, '#', 1, '#', 1, '#', 1, '#', 1, '#', 1, '#', 1, '#', 1, '#', 1, '#', 1, '#', 1, '#', 1, '#', 1, '-', 1, '|', 1, '#', 1, '#', 1, '#', 1, '#', 1, '#', 1, '#', 1, '#', 1, '#', 1, '#', 1, '#', 1, '#', 1, '#', 1, '#', 1, '#', 1, '#', 1, '#', 1, '#', 1, '#', 1, '^', 1, '^', 1, '^', 1, '^', 1, '>', 1, '>', 1, '>', 1, '>', 1, '>', 1, '>', 1, 'V', 1, 'V', 1, 'V', 1, 'V', 1, '<', 1, '<', 1, '<', 1, '<', 1, '<', 1, '<', 1, '*', 1, '*', 1, '*', 1, '*', 1, '*', 1, '*', 1, '*', 1, '*', 1, '*', 1, '*', 1, '*', 1, '*', 1, '*', 1, '*', 1, '*', 1, '*', 1, '*', 1, '*', 1, '*', 1, '*', 1, '*', 1, '*', 1, '*', 1, '*', 1, '*', 1, '*', 1, '*', 1, '*', 1, '*', 1, '*', 1, '*', 1, '*', 1, '*', 1, '#', 1, '#', 1, '#', 1, '#', 1, '#', 1, '^', 1, '^', 1, '^', 1, 'O', 1, '#', 1, '#', 1, '#', 1, '#', 1, '#', 1, '#', 1, '#', 1, '#', 1, '#', 1, '*', 1, '|', 1, '\'', 1, '\"', 1, '\"', 1, ',', 2, ',', ',', 1, '!', 1, '[', 1, '<', 2, '>', ' ', 1, ' ', 1, 'a', 1, '1', 1, 'b', 1, '\'', 1, 'k', 1, '2', 1, 'l', 1, '@', 1, 'c', 1, 'i', 1, 'f', 1, '/', 1, 'm', 1, 's', 1, 'p', 1, '\"', 1, 'e', 1, '3', 1, 'h', 1, '9', 1, 'o', 1, '6', 1, 'r', 1, '^', 1, 'd', 1, 'j', 1, 'g', 1, '>', 1, 'n', 1, 't', 1, 'q', 1, ',', 1, '*', 1, '5', 1, '<', 1, '-', 1, 'u', 1, '8', 1, 'v', 1, '.', 1, '%', 1, '[', 1, '$', 1, '+', 1, 'x', 1, '!', 1, '&', 1, ';', 1, ':', 1, '4', 1, '\\', 1, '0', 1, 'z', 1, '7', 1, '(', 1, '_', 1, '?', 1, 'w', 1, ']', 1, '#', 1, 'y', 1, ')', 1, '=', 4, '[', 'd', '7', ']', 5, '[', 'd', '1', '7', ']', 5, '[', 'd', '2', '7', ']', 6, '[', 'd', '1', '2', '7', ']', 5, '[', 'd', '3', '7', ']', 6, '[', 'd', '1', '3', '7', ']', 6, '[', 'd', '2', '3', '7', ']', 7, '[', 'd', '1', '2', '3', '7', ']', 5, '[', 'd', '4', '7', ']', 6, '[', 'd', '1', '4', '7', ']', 6, '[', 'd', '2', '4', '7', ']', 7, '[', 'd', '1', '2', '4', '7', ']', 6, '[', 'd', '3', '4', '7', ']', 7, '[', 'd', '1', '3', '4', '7', ']', 7, '[', 'd', '2', '3', '4', '7', ']', 8, '[', 'd', '1', '2', '3', '4', '7', ']', 5, '[', 'd', '5', '7', ']', 6, '[', 'd', '1', '5', '7', ']', 6, '[', 'd', '2', '5', '7', ']', 7, '[', 'd', '1', '2', '5', '7', ']', 6, '[', 'd', '3', '5', '7', ']', 7, '[', 'd', '1', '3', '5', '7', ']', 7, '[', 'd', '2', '3', '5', '7', ']', 8, '[', 'd', '1', '2', '3', '5', '7', ']', 6, '[', 'd', '4', '5', '7', ']', 7, '[', 'd', '1', '4', '5', '7', ']', 7, '[', 'd', '2', '4', '5', '7', ']', 8, '[', 'd', '1', '2', '4', '5', '7', ']', 7, '[', 'd', '3', '4', '5', '7', ']', 8, '[', 'd', '1', '3', '4', '5', '7', ']', 8, '[', 'd', '2', '3', '4', '5', '7', ']', 9, '[', 'd', '1', '2', '3', '4', '5', '7', ']', 5, '[', 'd', '6', '7', ']', 6, '[', 'd', '1', '6', '7', ']', 6, '[', 'd', '2', '6', '7', ']', 7, '[', 'd', '1', '2', '6', '7', ']', 6, '[', 'd', '3', '6', '7', ']', 7, '[', 'd', '1', '3', '6', '7', ']', 7, '[', 'd', '2', '3', '6', '7', ']', 8, '[', 'd', '1', '2', '3', '6', '7', ']', 6, '[', 'd', '4', '6', '7', ']', 7, '[', 'd', '1', '4', '6', '7', ']', 7, '[', 'd', '2', '4', '6', '7', ']', 8, '[', 'd', '1', '2', '4', '6', '7', ']', 7, '[', 'd', '3', '4', '6', '7', ']', 8, '[', 'd', '1', '3', '4', '6', '7', ']', 8, '[', 'd', '2', '3', '4', '6', '7', ']', 9, '[', 'd', '1', '2', '3', '4', '6', '7', ']', 6, '[', 'd', '5', '6', '7', ']', 7, '[', 'd', '1', '5', '6', '7', ']', 7, '[', 'd', '2', '5', '6', '7', ']', 8, '[', 'd', '1', '2', '5', '6', '7', ']', 7, '[', 'd', '3', '5', '6', '7', ']', 8, '[', 'd', '1', '3', '5', '6', '7', ']', 8, '[', 'd', '2', '3', '5', '6', '7', ']', 9, '[', 'd', '1', '2', '3', '5', '6', '7', ']', 7, '[', 'd', '4', '5', '6', '7', ']', 8, '[', 'd', '1', '4', '5', '6', '7', ']', 8, '[', 'd', '2', '4', '5', '6', '7', ']', 9, '[', 'd', '1', '2', '4', '5', '6', '7', ']', 8, '[', 'd', '3', '4', '5', '6', '7', ']', 9, '[', 'd', '1', '3', '4', '5', '6', '7', ']', 9, '[', 'd', '2', '3', '4', '5', '6', '7', ']', 10, '[', 'd', '1', '2', '3', '4', '5', '6', '7', ']', 4, '[', 'd', '8', ']', 5, '[', 'd', '1', '8', ']', 5, '[', 'd', '2', '8', ']', 6, '[', 'd', '1', '2', '8', ']', 5, '[', 'd', '3', '8', ']', 6, '[', 'd', '1', '3', '8', ']', 6, '[', 'd', '2', '3', '8', ']', 7, '[', 'd', '1', '2', '3', '8', ']', 5, '[', 'd', '4', '8', ']', 6, '[', 'd', '1', '4', '8', ']', 6, '[', 'd', '2', '4', '8', ']', 7, '[', 'd', '1', '2', '4', '8', ']', 6, '[', 'd', '3', '4', '8', ']', 7, '[', 'd', '1', '3', '4', '8', ']', 7, '[', 'd', '2', '3', '4', '8', ']', 8, '[', 'd', '1', '2', '3', '4', '8', ']', 5, '[', 'd', '5', '8', ']', 6, '[', 'd', '1', '5', '8', ']', 6, '[', 'd', '2', '5', '8', ']', 7, '[', 'd', '1', '2', '5', '8', ']', 6, '[', 'd', '3', '5', '8', ']', 7, '[', 'd', '1', '3', '5', '8', ']', 7, '[', 'd', '2', '3', '5', '8', ']', 8, '[', 'd', '1', '2', '3', '5', '8', ']', 6, '[', 'd', '4', '5', '8', ']', 7, '[', 'd', '1', '4', '5', '8', ']', 7, '[', 'd', '2', '4', '5', '8', ']', 8, '[', 'd', '1', '2', '4', '5', '8', ']', 7, '[', 'd', '3', '4', '5', '8', ']', 8, '[', 'd', '1', '3', '4', '5', '8', ']', 8, '[', 'd', '2', '3', '4', '5', '8', ']', 9, '[', 'd', '1', '2', '3', '4', '5', '8', ']', 5, '[', 'd', '6', '8', ']', 6, '[', 'd', '1', '6', '8', ']', 6, '[', 'd', '2', '6', '8', ']', 7, '[', 'd', '1', '2', '6', '8', ']', 6, '[', 'd', '3', '6', '8', ']', 7, '[', 'd', '1', '3', '6', '8', ']', 7, '[', 'd', '2', '3', '6', '8', ']', 8, '[', 'd', '1', '2', '3', '6', '8', ']', 6, '[', 'd', '4', '6', '8', ']', 7, '[', 'd', '1', '4', '6', '8', ']', 7, '[', 'd', '2', '4', '6', '8', ']', 8, '[', 'd', '1', '2', '4', '6', '8', ']', 7, '[', 'd', '3', '4', '6', '8', ']', 8, '[', 'd', '1', '3', '4', '6', '8', ']', 8, '[', 'd', '2', '3', '4', '6', '8', ']', 9, '[', 'd', '1', '2', '3', '4', '6', '8', ']', 6, '[', 'd', '5', '6', '8', ']', 7, '[', 'd', '1', '5', '6', '8', ']', 7, '[', 'd', '2', '5', '6', '8', ']', 8, '[', 'd', '1', '2', '5', '6', '8', ']', 7, '[', 'd', '3', '5', '6', '8', ']', 8, '[', 'd', '1', '3', '5', '6', '8', ']', 8, '[', 'd', '2', '3', '5', '6', '8', ']', 9, '[', 'd', '1', '2', '3', '5', '6', '8', ']', 7, '[', 'd', '4', '5', '6', '8', ']', 8, '[', 'd', '1', '4', '5', '6', '8', ']', 8, '[', 'd', '2', '4', '5', '6', '8', ']', 9, '[', 'd', '1', '2', '4', '5', '6', '8', ']', 8, '[', 'd', '3', '4', '5', '6', '8', ']', 9, '[', 'd', '1', '3', '4', '5', '6', '8', ']', 9, '[', 'd', '2', '3', '4', '5', '6', '8', ']', 10, '[', 'd', '1', '2', '3', '4', '5', '6', '8', ']', 5, '[', 'd', '7', '8', ']', 6, '[', 'd', '1', '7', '8', ']', 6, '[', 'd', '2', '7', '8', ']', 7, '[', 'd', '1', '2', '7', '8', ']', 6, '[', 'd', '3', '7', '8', ']', 7, '[', 'd', '1', '3', '7', '8', ']', 7, '[', 'd', '2', '3', '7', '8', ']', 8, '[', 'd', '1', '2', '3', '7', '8', ']', 6, '[', 'd', '4', '7', '8', ']', 7, '[', 'd', '1', '4', '7', '8', ']', 7, '[', 'd', '2', '4', '7', '8', ']', 8, '[', 'd', '1', '2', '4', '7', '8', ']', 7, '[', 'd', '3', '4', '7', '8', ']', 8, '[', 'd', '1', '3', '4', '7', '8', ']', 8, '[', 'd', '2', '3', '4', '7', '8', ']', 9, '[', 'd', '1', '2', '3', '4', '7', '8', ']', 6, '[', 'd', '5', '7', '8', ']', 7, '[', 'd', '1', '5', '7', '8', ']', 7, '[', 'd', '2', '5', '7', '8', ']', 8, '[', 'd', '1', '2', '5', '7', '8', ']', 7, '[', 'd', '3', '5', '7', '8', ']', 8, '[', 'd', '1', '3', '5', '7', '8', ']', 8, '[', 'd', '2', '3', '5', '7', '8', ']', 9, '[', 'd', '1', '2', '3', '5', '7', '8', ']', 7, '[', 'd', '4', '5', '7', '8', ']', 8, '[', 'd', '1', '4', '5', '7', '8', ']', 8, '[', 'd', '2', '4', '5', '7', '8', ']', 9, '[', 'd', '1', '2', '4', '5', '7', '8', ']', 8, '[', 'd', '3', '4', '5', '7', '8', ']', 9, '[', 'd', '1', '3', '4', '5', '7', '8', ']', 9, '[', 'd', '2', '3', '4', '5', '7', '8', ']', 10, '[', 'd', '1', '2', '3', '4', '5', '7', '8', ']', 6, '[', 'd', '6', '7', '8', ']', 7, '[', 'd', '1', '6', '7', '8', ']', 7, '[', 'd', '2', '6', '7', '8', ']', 8, '[', 'd', '1', '2', '6', '7', '8', ']', 7, '[', 'd', '3', '6', '7', '8', ']', 8, '[', 'd', '1', '3', '6', '7', '8', ']', 8, '[', 'd', '2', '3', '6', '7', '8', ']', 9, '[', 'd', '1', '2', '3', '6', '7', '8', ']', 7, '[', 'd', '4', '6', '7', '8', ']', 8, '[', 'd', '1', '4', '6', '7', '8', ']', 8, '[', 'd', '2', '4', '6', '7', '8', ']', 9, '[', 'd', '1', '2', '4', '6', '7', '8', ']', 8, '[', 'd', '3', '4', '6', '7', '8', ']', 9, '[', 'd', '1', '3', '4', '6', '7', '8', ']', 9, '[', 'd', '2', '3', '4', '6', '7', '8', ']', 10, '[', 'd', '1', '2', '3', '4', '6', '7', '8', ']', 7, '[', 'd', '5', '6', '7', '8', ']', 8, '[', 'd', '1', '5', '6', '7', '8', ']', 8, '[', 'd', '2', '5', '6', '7', '8', ']', 9, '[', 'd', '1', '2', '5', '6', '7', '8', ']', 8, '[', 'd', '3', '5', '6', '7', '8', ']', 9, '[', 'd', '1', '3', '5', '6', '7', '8', ']', 9, '[', 'd', '2', '3', '5', '6', '7', '8', ']', 10, '[', 'd', '1', '2', '3', '5', '6', '7', '8', ']', 8, '[', 'd', '4', '5', '6', '7', '8', ']', 9, '[', 'd', '1', '4', '5', '6', '7', '8', ']', 9, '[', 'd', '2', '4', '5', '6', '7', '8', ']', 10, '[', 'd', '1', '2', '4', '5', '6', '7', '8', ']', 9, '[', 'd', '3', '4', '5', '6', '7', '8', ']', 10, '[', 'd', '1', '3', '4', '5', '6', '7', '8', ']', 10, '[', 'd', '2', '3', '4', '5', '6', '7', '8', ']', 11, '[', 'd', '1', '2', '3', '4', '5', '6', '7', '8', ']', 1, '{', 2, '}', ' ', 3, ':', ':', '=', 2, '=', '=', 3, '=', '=', '=', 1, 'L', 1, 'l', 1, 'L', 1, 'P', 1, 'R', 1, 'a', 1, 't', 1, 'H', 1, 'h', 1, 'K', 1, 'k', 1, 'Z', 1, 'z', 1, 'M', 1, 'A', 1, 'r', 2, 'r', '.', 1, 'T', 2, 'T', '.', 1, 's', 2, '[', ']', 1, '\\', 1, '/', 2, '_', '_', 1, '>', 1, '%', 1, '>', 1, '=', 1, '/', 1, '-', 1, '~', 1, '\\', 1, '/', 1, '~', 1, '~', 2, '|', '-', 2, '-', '|', 2, '<', '=', 2, '=', '>', 2, '(', '(', 2, ')', ')', 2, ':', ':', 1, '?', 1, '\'', 1, 'o', 1, '.', 1, ',', 1, '.', 1, ',', 1, ';', 4, '-', '-', '-', '-', 6, '-', '-', '-', '-', '-', '-', 1, 'x', 1, '|', 1, '=', 1, ',', 1, '\"', 3, '`', '-', '-', 1, ' ', 2, ',', ' ', 2, '.', ' ', 1, '\"', 5, '[', 'J', 'I', 'S', ']', 1, '\"', 1, '/', 1, '0', 1, '<', 2, '>', ' ', 2, '<', '<', 3, '>', '>', ' ', 1, '[', 2, ']', ' ', 1, '{', 2, '}', ' ', 2, '[', '(', 3, ')', ']', ' ', 1, '@', 2, 'X', ' ', 1, '[', 2, ']', ' ', 2, '[', '[', 3, ']', ']', ' ', 2, '(', '(', 3, ')', ')', ' ', 2, '[', '[', 3, ']', ']', ' ', 2, '~', ' ', 2, '`', '`', 2, '\'', '\'', 2, ',', ',', 1, '@', 1, '1', 1, '2', 1, '3', 1, '4', 1, '5', 1, '6', 1, '7', 1, '8', 1, '9', 1, '~', 1, '+', 1, '+', 1, '+', 1, '+', 1, '@', 4, ' ', '/', '/', ' ', 4, '+', '1', '0', '+', 4, '+', '2', '0', '+', 4, '+', '3', '0', '+', 1, 'a', 1, 'a', 1, 'i', 1, 'i', 1, 'u', 1, 'u', 1, 'e', 1, 'e', 1, 'o', 1, 'o', 2, 'k', 'a', 2, 'g', 'a', 2, 'k', 'i', 2, 'g', 'i', 2, 'k', 'u', 2, 'g', 'u', 2, 'k', 'e', 2, 'g', 'e', 2, 'k', 'o', 2, 'g', 'o', 2, 's', 'a', 2, 'z', 'a', 3, 's', 'h', 'i', 2, 'z', 'i', 2, 's', 'u', 2, 'z', 'u', 2, 's', 'e', 2, 'z', 'e', 2, 's', 'o', 2, 'z', 'o', 2, 't', 'a', 2, 'd', 'a', 3, 'c', 'h', 'i', 2, 'd', 'i', 3, 't', 's', 'u', 3, 't', 's', 'u', 2, 'd', 'u', 2, 't', 'e', 2, 'd', 'e', 2, 't', 'o', 2, 'd', 'o', 2, 'n', 'a', 2, 'n', 'i', 2, 'n', 'u', 2, 'n', 'e', 2, 'n', 'o', 2, 'h', 'a', 2, 'b', 'a', 2, 'p', 'a', 2, 'h', 'i', 2, 'b', 'i', 2, 'p', 'i', 2, 'h', 'u', 2, 'b', 'u', 2, 'p', 'u', 2, 'h', 'e', 2, 'b', 'e', 2, 'p', 'e', 2, 'h', 'o', 2, 'b', 'o', 2, 'p', 'o', 2, 'm', 'a', 2, 'm', 'i', 2, 'm', 'u', 2, 'm', 'e', 2, 'm', 'o', 2, 'y', 'a', 2, 'y', 'a', 2, 'y', 'u', 2, 'y', 'u', 2, 'y', 'o', 2, 'y', 'o', 2, 'r', 'a', 2, 'r', 'i', 2, 'r', 'u', 2, 'r', 'e', 2, 'r', 'o', 2, 'w', 'a', 2, 'w', 'a', 2, 'w', 'i', 2, 'w', 'e', 2, 'w', 'o', 1, 'n', 2, 'v', 'u', 1, '\"', 1, '\"', 1, 'a', 1, 'a', 1, 'i', 1, 'i', 1, 'u', 1, 'u', 1, 'e', 1, 'e', 1, 'o', 1, 'o', 2, 'k', 'a', 2, 'g', 'a', 2, 'k', 'i', 2, 'g', 'i', 2, 'k', 'u', 2, 'g', 'u', 2, 'k', 'e', 2, 'g', 'e', 2, 'k', 'o', 2, 'g', 'o', 2, 's', 'a', 2, 'z', 'a', 3, 's', 'h', 'i', 2, 'z', 'i', 2, 's', 'u', 2, 'z', 'u', 2, 's', 'e', 2, 'z', 'e', 2, 's', 'o', 2, 'z', 'o', 2, 't', 'a', 2, 'd', 'a', 3, 'c', 'h', 'i', 2, 'd', 'i', 3, 't', 's', 'u', 3, 't', 's', 'u', 2, 'd', 'u', 2, 't', 'e', 2, 'd', 'e', 2, 't', 'o', 2, 'd', 'o', 2, 'n', 'a', 2, 'n', 'i', 2, 'n', 'u', 2, 'n', 'e', 2, 'n', 'o', 2, 'h', 'a', 2, 'b', 'a', 2, 'p', 'a', 2, 'h', 'i', 2, 'b', 'i', 2, 'p', 'i', 2, 'h', 'u', 2, 'b', 'u', 2, 'p', 'u', 2, 'h', 'e', 2, 'b', 'e', 2, 'p', 'e', 2, 'h', 'o', 2, 'b', 'o', 2, 'p', 'o', 2, 'm', 'a', 2, 'm', 'i', 2, 'm', 'u', 2, 'm', 'e', 2, 'm', 'o', 2, 'y', 'a', 2, 'y', 'a', 2, 'y', 'u', 2, 'y', 'u', 2, 'y', 'o', 2, 'y', 'o', 2, 'r', 'a', 2, 'r', 'i', 2, 'r', 'u', 2, 'r', 'e', 2, 'r', 'o', 2, 'w', 'a', 2, 'w', 'a', 2, 'w', 'i', 2, 'w', 'e', 2, 'w', 'o', 1, 'n', 2, 'v', 'u', 2, 'k', 'a', 2, 'k', 'e', 2, 'v', 'a', 2, 'v', 'i', 2, 'v', 'e', 2, 'v', 'o', 1, '\"', 1, '\"', 1, 'B', 1, 'P', 1, 'M', 1, 'F', 1, 'D', 1, 'T', 1, 'N', 1, 'L', 1, 'G', 1, 'K', 1, 'H', 1, 'J', 1, 'Q', 1, 'X', 2, 'Z', 'H', 2, 'C', 'H', 2, 'S', 'H', 1, 'R', 1, 'Z', 1, 'C', 1, 'S', 1, 'A', 1, 'O', 1, 'E', 2, 'E', 'H', 2, 'A', 'I', 2, 'E', 'I', 2, 'A', 'U', 2, 'O', 'U', 2, 'A', 'N', 2, 'E', 'N', 3, 'A', 'N', 'G', 3, 'E', 'N', 'G', 2, 'E', 'R', 1, 'I', 1, 'U', 2, 'I', 'U', 1, 'V', 2, 'N', 'G', 2, 'G', 'N', 1, 'g', 2, 'g', 'g', 2, 'g', 's', 1, 'n', 2, 'n', 'j', 2, 'n', 'h', 1, 'd', 2, 'd', 'd', 1, 'r', 2, 'l', 'g', 2, 'l', 'm', 2, 'l', 'b', 2, 'l', 's', 2, 'l', 't', 2, 'l', 'p', 2, 'r', 'h', 1, 'm', 1, 'b', 2, 'b', 'b', 2, 'b', 's', 1, 's', 2, 's', 's', 1, 'j', 2, 'j', 'j', 1, 'c', 1, 'k', 1, 't', 1, 'p', 1, 'h', 1, 'a', 2, 'a', 'e', 2, 'y', 'a', 3, 'y', 'a', 'e', 2, 'e', 'o', 1, 'e', 3, 'y', 'e', 'o', 2, 'y', 'e', 1, 'o', 2, 'w', 'a', 3, 'w', 'a', 'e', 2, 'o', 'e', 2, 'y', 'o', 1, 'u', 3, 'w', 'e', 'o', 2, 'w', 'e', 2, 'w', 'i', 2, 'y', 'u', 2, 'e', 'u', 2, 'y', 'i', 1, 'i', 2, 'n', 'n', 2, 'n', 'd', 2, 'n', 's', 2, 'n', 'Z', 3, 'l', 'g', 's', 2, 'l', 'd', 3, 'l', 'b', 's', 2, 'l', 'Z', 2, 'l', 'Q', 2, 'm', 'b', 2, 'm', 's', 2, 'm', 'Z', 2, 'm', 'N', 2, 'b', 'g', 3, 'b', 's', 'g', 3, 'b', 's', 't', 2, 'b', 'j', 2, 'b', 't', 2, 'b', 'N', 3, 'b', 'b', 'N', 2, 's', 'g', 2, 's', 'n', 2, 's', 'd', 2, 's', 'b', 2, 's', 'j', 1, 'Z', 1, 'N', 2, 'N', 's', 2, 'N', 'Z', 2, 'p', 'N', 2, 'h', 'h', 1, 'Q', 5, 'y', 'o', '-', 'y', 'a', 6, 'y', 'o', '-', 'y', 'a', 'e', 4, 'y', 'o', '-', 'i', 6, 'y', 'u', '-', 'y', 'e', 'o', 5, 'y', 'u', '-', 'y', 'e', 4, 'y', 'u', '-', 'i', 1, 'U', 3, 'U', '-', 'i', 2, 'B', 'U', 2, 'Z', 'I', 2, 'J', 'I', 2, 'G', 'U', 2, 'E', 'E', 3, 'E', 'N', 'N', 2, 'O', 'O', 3, 'O', 'N', 'N', 2, 'I', 'R', 3, 'A', 'N', 'N', 3, 'I', 'N', 'N', 3, 'U', 'N', 'N', 2, 'I', 'M', 3, 'N', 'G', 'G', 4, 'A', 'I', 'N', 'N', 4, 'A', 'U', 'N', 'N', 2, 'A', 'M', 2, 'O', 'M', 3, 'O', 'N', 'G', 4, 'I', 'N', 'N', 'N', 1, 'P', 1, 'T', 1, 'K', 1, 'H', 3, '(', 'g', ')', 3, '(', 'n', ')', 3, '(', 'd', ')', 3, '(', 'r', ')', 3, '(', 'm', ')', 3, '(', 'b', ')', 3, '(', 's', ')', 2, '(', ')', 3, '(', 'j', ')', 3, '(', 'c', ')', 3, '(', 'k', ')', 3, '(', 't', ')', 3, '(', 'p', ')', 3, '(', 'h', ')', 4, '(', 'g', 'a', ')', 4, '(', 'n', 'a', ')', 4, '(', 'd', 'a', ')', 4, '(', 'r', 'a', ')', 4, '(', 'm', 'a', ')', 4, '(', 'b', 'a', ')', 4, '(', 's', 'a', ')', 3, '(', 'a', ')', 4, '(', 'j', 'a', ')', 4, '(', 'c', 'a', ')', 4, '(', 'k', 'a', ')', 4, '(', 't', 'a', ')', 4, '(', 'p', 'a', ')', 4, '(', 'h', 'a', ')', 4, '(', 'j', 'u', ')', 4, '(', '1', ')', ' ', 4, '(', '2', ')', ' ', 4, '(', '3', ')', ' ', 4, '(', '4', ')', ' ', 4, '(', '5', ')', ' ', 4, '(', '6', ')', ' ', 4, '(', '7', ')', ' ', 4, '(', '8', ')', ' ', 4, '(', '9', ')', ' ', 5, '(', '1', '0', ')', ' ', 6, '(', 'Y', 'u', 'e', ')', ' ', 6, '(', 'H', 'u', 'o', ')', ' ', 7, '(', 'S', 'h', 'u', 'i', ')', ' ', 5, '(', 'M', 'u', ')', ' ', 6, '(', 'J', 'i', 'n', ')', ' ', 5, '(', 'T', 'u', ')', ' ', 5, '(', 'R', 'i', ')', ' ', 6, '(', 'Z', 'h', 'u', ')', ' ', 6, '(', 'Y', 'o', 'u', ')', ' ', 6, '(', 'S', 'h', 'e', ')', ' ', 7, '(', 'M', 'i', 'n', 'g', ')', ' ', 5, '(', 'T', 'e', ')', ' ', 6, '(', 'C', 'a', 'i', ')', ' ', 6, '(', 'Z', 'h', 'u', ')', ' ', 6, '(', 'L', 'a', 'o', ')', ' ', 6, '(', 'D', 'a', 'i', ')', ' ', 5, '(', 'H', 'u', ')', ' ', 6, '(', 'X', 'u', 'e', ')', ' ', 7, '(', 'J', 'i', 'a', 'n', ')', ' ', 5, '(', 'Q', 'i', ')', ' ', 5, '(', 'Z', 'i', ')', ' ', 6, '(', 'X', 'i', 'e', ')', ' ', 5, '(', 'J', 'i', ')', ' ', 6, '(', 'X', 'i', 'u', ')', ' ', 2, '<', '<', 2, '>', '>', 2, '2', '1', 2, '2', '2', 2, '2', '3', 2, '2', '4', 2, '2', '5', 2, '2', '6', 2, '2', '7', 2, '2', '8', 2, '2', '9', 2, '3', '0', 2, '3', '1', 2, '3', '2', 2, '3', '3', 2, '3', '4', 2, '3', '5', 3, '(', 'g', ')', 3, '(', 'n', ')', 3, '(', 'd', ')', 3, '(', 'r', ')', 3, '(', 'm', ')', 3, '(', 'b', ')', 3, '(', 's', ')', 2, '(', ')', 3, '(', 'j', ')', 3, '(', 'c', ')', 3, '(', 'k', ')', 3, '(', 't', ')', 3, '(', 'p', ')', 3, '(', 'h', ')', 4, '(', 'g', 'a', ')', 4, '(', 'n', 'a', ')', 4, '(', 'd', 'a', ')', 4, '(', 'r', 'a', ')', 4, '(', 'm', 'a', ')', 4, '(', 'b', 'a', ')', 4, '(', 's', 'a', ')', 3, '(', 'a', ')', 4, '(', 'j', 'a', ')', 4, '(', 'c', 'a', ')', 4, '(', 'k', 'a', ')', 4, '(', 't', 'a', ')', 4, '(', 'p', 'a', ')', 4, '(', 'h', 'a', ')', 4, 'K', 'I', 'S', ' ', 4, '(', '1', ')', ' ', 4, '(', '2', ')', ' ', 4, '(', '3', ')', ' ', 4, '(', '4', ')', ' ', 4, '(', '5', ')', ' ', 4, '(', '6', ')', ' ', 4, '(', '7', ')', ' ', 4, '(', '8', ')', ' ', 4, '(', '9', ')', ' ', 5, '(', '1', '0', ')', ' ', 6, '(', 'Y', 'u', 'e', ')', ' ', 6, '(', 'H', 'u', 'o', ')', ' ', 7, '(', 'S', 'h', 'u', 'i', ')', ' ', 5, '(', 'M', 'u', ')', ' ', 6, '(', 'J', 'i', 'n', ')', ' ', 5, '(', 'T', 'u', ')', ' ', 5, '(', 'R', 'i', ')', ' ', 6, '(', 'Z', 'h', 'u', ')', ' ', 6, '(', 'Y', 'o', 'u', ')', ' ', 6, '(', 'S', 'h', 'e', ')', ' ', 7, '(', 'M', 'i', 'n', 'g', ')', ' ', 5, '(', 'T', 'e', ')', ' ', 6, '(', 'C', 'a', 'i', ')', ' ', 6, '(', 'Z', 'h', 'u', ')', ' ', 6, '(', 'L', 'a', 'o', ')', ' ', 5, '(', 'M', 'i', ')', ' ', 6, '(', 'N', 'a', 'n', ')', ' ', 5, '(', 'N', 'u', ')', ' ', 6, '(', 'S', 'h', 'i', ')', ' ', 6, '(', 'Y', 'o', 'u', ')', ' ', 6, '(', 'Y', 'i', 'n', ')', ' ', 6, '(', 'Z', 'h', 'u', ')', ' ', 8, '(', 'X', 'i', 'a', 'n', 'g', ')', ' ', 6, '(', 'X', 'i', 'u', ')', ' ', 6, '(', 'X', 'i', 'e', ')', ' ', 8, '(', 'Z', 'h', 'e', 'n', 'g', ')', ' ', 8, '(', 'S', 'h', 'a', 'n', 'g', ')', ' ', 8, '(', 'Z', 'h', 'o', 'n', 'g', ')', ' ', 6, '(', 'X', 'i', 'a', ')', ' ', 6, '(', 'Z', 'u', 'o', ')', ' ', 6, '(', 'Y', 'o', 'u', ')', ' ', 5, '(', 'Y', 'i', ')', ' ', 7, '(', 'Z', 'o', 'n', 'g', ')', ' ', 6, '(', 'X', 'u', 'e', ')', ' ', 7, '(', 'J', 'i', 'a', 'n', ')', ' ', 5, '(', 'Q', 'i', ')', ' ', 5, '(', 'Z', 'i', ')', ' ', 6, '(', 'X', 'i', 'e', ')', ' ', 5, '(', 'Y', 'e', ')', ' ', 2, '3', '6', 2, '3', '7', 2, '3', '8', 2, '3', '9', 2, '4', '0', 2, '4', '1', 2, '4', '2', 2, '4', '3', 2, '4', '4', 2, '4', '5', 2, '4', '6', 2, '4', '7', 2, '4', '8', 2, '4', '9', 2, '5', '0', 2, '1', 'M', 2, '2', 'M', 2, '3', 'M', 2, '4', 'M', 2, '5', 'M', 2, '6', 'M', 2, '7', 'M', 2, '8', 'M', 2, '9', 'M', 3, '1', '0', 'M', 3, '1', '1', 'M', 3, '1', '2', 'M', 2, 'H', 'g', 3, 'e', 'r', 'g', 2, 'e', 'V', 3, 'L', 'T', 'D', 1, 'a', 1, 'i', 1, 'u', 1, 'u', 1, 'o', 2, 'k', 'a', 2, 'k', 'i', 2, 'k', 'u', 2, 'k', 'e', 2, 'k', 'o', 2, 's', 'a', 2, 's', 'i', 2, 's', 'u', 2, 's', 'e', 2, 's', 'o', 2, 't', 'a', 2, 't', 'i', 2, 't', 'u', 2, 't', 'e', 2, 't', 'o', 2, 'n', 'a', 2, 'n', 'i', 2, 'n', 'u', 2, 'n', 'e', 2, 'n', 'o', 2, 'h', 'a', 2, 'h', 'i', 2, 'h', 'u', 2, 'h', 'e', 2, 'h', 'o', 2, 'm', 'a', 2, 'm', 'i', 2, 'm', 'u', 2, 'm', 'e', 2, 'm', 'o', 2, 'y', 'a', 2, 'y', 'u', 2, 'y', 'o', 2, 'r', 'a', 2, 'r', 'i', 2, 'r', 'u', 2, 'r', 'e', 2, 'r', 'o', 2, 'w', 'a', 2, 'w', 'i', 2, 'w', 'e', 2, 'w', 'o', 9, 'a', 'p', 'a', 'r', 't', 'm', 'e', 'n', 't', 5, 'a', 'l', 'p', 'h', 'a', 6, 'a', 'm', 'p', 'e', 'r', 'e', 3, 'a', 'r', 'e', 6, 'i', 'n', 'n', 'i', 'n', 'g', 4, 'i', 'n', 'c', 'h', 3, 'w', 'o', 'n', 6, 'e', 's', 'c', 'u', 'd', 'o', 4, 'a', 'c', 'r', 'e', 5, 'o', 'u', 'n', 'c', 'e', 3, 'o', 'h', 'm', 6, 'k', 'a', 'i', '-', 'r', 'i', 5, 'c', 'a', 'r', 'a', 't', 7, 'c', 'a', 'l', 'o', 'r', 'i', 'e', 6, 'g', 'a', 'l', 'l', 'o', 'n', 5, 'g', 'a', 'm', 'm', 'a', 4, 'g', 'i', 'g', 'a', 6, 'g', 'u', 'i', 'n', 'e', 'a', 5, 'c', 'u', 'r', 'i', 'e', 7, 'g', 'u', 'i', 'l', 'd', 'e', 'r', 4, 'k', 'i', 'l', 'o', 8, 'k', 'i', 'l', 'o', 'g', 'r', 'a', 'm', 9, 'k', 'i', 'l', 'o', 'm', 'e', 't', 'e', 'r', 8, 'k', 'i', 'l', 'o', 'w', 'a', 't', 't', 4, 'g', 'r', 'a', 'm', 8, 'g', 'r', 'a', 'm', ' ', 't', 'o', 'n', 8, 'c', 'r', 'u', 'z', 'e', 'i', 'r', 'o', 5, 'k', 'r', 'o', 'n', 'e', 4, 'c', 'a', 's', 'e', 6, 'k', 'o', 'r', 'u', 'n', 'a', 5, 'c', 'o', '-', 'o', 'p', 5, 'c', 'y', 'c', 'l', 'e', 7, 'c', 'e', 'n', 't', 'i', 'm', 'e', 8, 's', 'h', 'i', 'l', 'l', 'i', 'n', 'g', 5, 'c', 'e', 'n', 't', 'i', 4, 'c', 'e', 'n', 't', 5, 'd', 'o', 'z', 'e', 'n', 4, 'd', 'e', 's', 'i', 6, 'd', 'o', 'l', 'l', 'a', 'r', 3, 't', 'o', 'n', 4, 'n', 'a', 'n', 'o', 4, 'k', 'n', 'o', 't', 7, 'h', 'e', 'i', 'g', 'h', 't', 's', 7, 'p', 'e', 'r', 'c', 'e', 'n', 't', 5, 'p', 'a', 'r', 't', 's', 6, 'b', 'a', 'r', 'r', 'e', 'l', 7, 'p', 'i', 'a', 's', 't', 'e', 'r', 5, 'p', 'i', 'c', 'u', 'l', 4, 'p', 'i', 'c', 'o', 8, 'b', 'u', 'i', 'l', 'd', 'i', 'n', 'g', 5, 'f', 'a', 'r', 'a', 'd', 4, 'f', 'e', 'e', 't', 6, 'b', 'u', 's', 'h', 'e', 'l', 5, 'f', 'r', 'a', 'n', 'c', 7, 'h', 'e', 'c', 't', 'a', 'r', 'e', 4, 'p', 'e', 's', 'o', 7, 'p', 'f', 'e', 'n', 'n', 'i', 'g', 5, 'h', 'e', 'r', 't', 'z', 5, 'p', 'e', 'n', 'c', 'e', 4, 'p', 'a', 'g', 'e', 4, 'b', 'e', 't', 'a', 5, 'p', 'o', 'i', 'n', 't', 4, 'v', 'o', 'l', 't', 3, 'h', 'o', 'n', 5, 'p', 'o', 'u', 'n', 'd', 4, 'h', 'a', 'l', 'l', 4, 'h', 'o', 'r', 'n', 5, 'm', 'i', 'c', 'r', 'o', 4, 'm', 'i', 'l', 'e', 4, 'm', 'a', 'c', 'h', 4, 'm', 'a', 'r', 'k', 7, 'm', 'a', 'n', 's', 'i', 'o', 'n', 6, 'm', 'i', 'c', 'r', 'o', 'n', 5, 'm', 'i', 'l', 'l', 'i', 8, 'm', 'i', 'l', 'l', 'i', 'b', 'a', 'r', 4, 'm', 'e', 'g', 'a', 7, 'm', 'e', 'g', 'a', 't', 'o', 'n', 5, 'm', 'e', 't', 'e', 'r', 4, 'y', 'a', 'r', 'd', 4, 'y', 'a', 'r', 'd', 4, 'y', 'u', 'a', 'n', 5, 'l', 'i', 't', 'e', 'r', 4, 'l', 'i', 'r', 'a', 5, 'r', 'u', 'p', 'e', 'e', 5, 'r', 'u', 'b', 'l', 'e', 3, 'r', 'e', 'm', 8, 'r', 'o', 'e', 'n', 't', 'g', 'e', 'n', 4, 'w', 'a', 't', 't', 2, '0', 'h', 2, '1', 'h', 2, '2', 'h', 2, '3', 'h', 2, '4', 'h', 2, '5', 'h', 2, '6', 'h', 2, '7', 'h', 2, '8', 'h', 2, '9', 'h', 3, '1', '0', 'h', 3, '1', '1', 'h', 3, '1', '2', 'h', 3, '1', '3', 'h', 3, '1', '4', 'h', 3, '1', '5', 'h', 3, '1', '6', 'h', 3, '1', '7', 'h', 3, '1', '8', 'h', 3, '1', '9', 'h', 3, '2', '0', 'h', 3, '2', '1', 'h', 3, '2', '2', 'h', 3, '2', '3', 'h', 3, '2', '4', 'h', 3, 'h', 'P', 'a', 2, 'd', 'a', 2, 'A', 'U', 3, 'b', 'a', 'r', 2, 'o', 'V', 2, 'p', 'c', 2, 'd', 'm', 4, 'd', 'm', '^', '2', 4, 'd', 'm', '^', '3', 2, 'I', 'U', 6, 'H', 'e', 'i', 's', 'e', 'i', 6, 'S', 'y', 'o', 'u', 'w', 'a', 7, 'T', 'a', 'i', 's', 'y', 'o', 'u', 5, 'M', 'e', 'i', 'j', 'i', 4, 'I', 'n', 'c', '.', 2, 'p', 'A', 2, 'n', 'A', 2, 'u', 'A', 2, 'm', 'A', 2, 'k', 'A', 2, 'k', 'B', 2, 'M', 'B', 2, 'G', 'B', 3, 'c', 'a', 'l', 4, 'k', 'c', 'a', 'l', 2, 'p', 'F', 2, 'n', 'F', 2, 'u', 'F', 2, 'u', 'g', 2, 'm', 'g', 2, 'k', 'g', 2, 'H', 'z', 3, 'k', 'H', 'z', 3, 'M', 'H', 'z', 3, 'G', 'H', 'z', 3, 'T', 'H', 'z', 2, 'u', 'l', 2, 'm', 'l', 2, 'd', 'l', 2, 'k', 'l', 2, 'f', 'm', 2, 'n', 'm', 2, 'u', 'm', 2, 'm', 'm', 2, 'c', 'm', 2, 'k', 'm', 4, 'm', 'm', '^', '2', 4, 'c', 'm', '^', '2', 3, 'm', '^', '2', 4, 'k', 'm', '^', '2', 4, 'm', 'm', '^', '3', 4, 'c', 'm', '^', '3', 3, 'm', '^', '3', 4, 'k', 'm', '^', '3', 3, 'm', '/', 's', 5, 'm', '/', 's', '^', '2', 2, 'P', 'a', 3, 'k', 'P', 'a', 3, 'M', 'P', 'a', 3, 'G', 'P', 'a', 3, 'r', 'a', 'd', 5, 'r', 'a', 'd', '/', 's', 7, 'r', 'a', 'd', '/', 's', '^', '2', 2, 'p', 's', 2, 'n', 's', 2, 'u', 's', 2, 'm', 's', 2, 'p', 'V', 2, 'n', 'V', 2, 'u', 'V', 2, 'm', 'V', 2, 'k', 'V', 2, 'M', 'V', 2, 'p', 'W', 2, 'n', 'W', 2, 'u', 'W', 2, 'm', 'W', 2, 'k', 'W', 2, 'M', 'W', 4, 'k', 'O', 'h', 'm', 4, 'M', 'O', 'h', 'm', 4, 'a', '.', 'm', '.', 2, 'B', 'q', 2, 'c', 'c', 2, 'c', 'd', 4, 'C', '/', 'k', 'g', 3, 'C', 'o', '.', 2, 'd', 'B', 2, 'G', 'y', 2, 'h', 'a', 2, 'H', 'P', 2, 'i', 'n', 4, 'K', '.', 'K', '.', 2, 'K', 'M', 2, 'k', 't', 2, 'l', 'm', 2, 'l', 'n', 3, 'l', 'o', 'g', 2, 'l', 'x', 2, 'm', 'b', 3, 'm', 'i', 'l', 3, 'm', 'o', 'l', 2, 'p', 'H', 4, 'p', '.', 'm', '.', 3, 'P', 'P', 'M', 2, 'P', 'R', 2, 's', 'r', 2, 'S', 'v', 2, 'W', 'b', 3, 'V', '/', 'm', 3, 'A', '/', 'm', 2, '1', 'd', 2, '2', 'd', 2, '3', 'd', 2, '4', 'd', 2, '5', 'd', 2, '6', 'd', 2, '7', 'd', 2, '8', 'd', 2, '9', 'd', 3, '1', '0', 'd', 3, '1', '1', 'd', 3, '1', '2', 'd', 3, '1', '3', 'd', 3, '1', '4', 'd', 3, '1', '5', 'd', 3, '1', '6', 'd', 3, '1', '7', 'd', 3, '1', '8', 'd', 3, '1', '9', 'd', 3, '2', '0', 'd', 3, '2', '1', 'd', 3, '2', '2', 'd', 3, '2', '3', 'd', 3, '2', '4', 'd', 3, '2', '5', 'd', 3, '2', '6', 'd', 3, '2', '7', 'd', 3, '2', '8', 'd', 3, '2', '9', 'd', 3, '3', '0', 'd', 3, '3', '1', 'd', 3, 'g', 'a', 'l', 3, 'Y', 'i', ' ', 5, 'D', 'i', 'n', 'g', ' ', 4, 'K', 'a', 'o', ' ', 3, 'Q', 'i', ' ', 6, 'S', 'h', 'a', 'n', 'g', ' ', 4, 'X', 'i', 'a', ' ', 3, 'M', 'o', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 4, 'S', 'a', 'n', ' ', 6, 'S', 'h', 'a', 'n', 'g', ' ', 4, 'X', 'i', 'a', ' ', 3, 'J', 'i', ' ', 3, 'B', 'u', ' ', 3, 'Y', 'u', ' ', 5, 'M', 'i', 'a', 'n', ' ', 4, 'G', 'a', 'i', ' ', 5, 'C', 'h', 'o', 'u', ' ', 5, 'C', 'h', 'o', 'u', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 4, 'Q', 'i', 'e', ' ', 3, 'P', 'i', ' ', 4, 'S', 'h', 'i', ' ', 4, 'S', 'h', 'i', ' ', 4, 'Q', 'i', 'u', ' ', 5, 'B', 'i', 'n', 'g', ' ', 3, 'Y', 'e', ' ', 5, 'C', 'o', 'n', 'g', ' ', 5, 'D', 'o', 'n', 'g', ' ', 3, 'S', 'i', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 4, 'D', 'i', 'u', ' ', 4, 'Q', 'i', 'u', ' ', 6, 'L', 'i', 'a', 'n', 'g', ' ', 4, 'D', 'i', 'u', ' ', 4, 'Y', 'o', 'u', ' ', 6, 'L', 'i', 'a', 'n', 'g', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'B', 'i', 'n', 'g', ' ', 5, 'S', 'a', 'n', 'g', ' ', 4, 'G', 'u', 'n', ' ', 4, 'J', 'i', 'u', ' ', 3, 'G', 'e', ' ', 3, 'Y', 'a', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 3, 'J', 'i', ' ', 4, 'J', 'i', 'e', ' ', 5, 'F', 'e', 'n', 'g', ' ', 5, 'G', 'u', 'a', 'n', ' ', 6, 'C', 'h', 'u', 'a', 'n', ' ', 5, 'C', 'h', 'a', 'n', ' ', 4, 'L', 'i', 'n', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 4, 'Z', 'h', 'u', ' ', 3, 'H', 'a', ' ', 4, 'W', 'a', 'n', ' ', 4, 'D', 'a', 'n', ' ', 4, 'W', 'e', 'i', ' ', 4, 'Z', 'h', 'u', ' ', 5, 'J', 'i', 'n', 'g', ' ', 3, 'L', 'i', ' ', 3, 'J', 'u', ' ', 4, 'P', 'i', 'e', ' ', 3, 'F', 'u', ' ', 3, 'Y', 'i', ' ', 3, 'Y', 'i', ' ', 4, 'N', 'a', 'i', ' ', 6, 'S', 'h', 'i', 'm', 'e', ' ', 4, 'J', 'i', 'u', ' ', 4, 'J', 'i', 'u', ' ', 4, 'Z', 'h', 'e', ' ', 4, 'Y', 'a', 'o', ' ', 3, 'Y', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'W', 'u', ' ', 4, 'Z', 'h', 'a', ' ', 3, 'H', 'u', ' ', 3, 'F', 'a', ' ', 3, 'L', 'e', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 5, 'P', 'i', 'n', 'g', ' ', 5, 'P', 'a', 'n', 'g', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 3, 'H', 'u', ' ', 5, 'G', 'u', 'a', 'i', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'M', 'i', 'e', ' ', 4, 'J', 'i', 'u', ' ', 3, 'Q', 'i', ' ', 3, 'Y', 'e', ' ', 3, 'X', 'i', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 4, 'G', 'a', 'i', ' ', 4, 'D', 'i', 'u', ' ', 4, 'H', 'a', 'l', ' ', 4, 'S', 'h', 'u', ' ', 5, 'T', 'w', 'u', 'l', ' ', 4, 'S', 'h', 'i', ' ', 3, 'J', 'i', ' ', 5, 'N', 'a', 'n', 'g', ' ', 4, 'J', 'i', 'a', ' ', 4, 'K', 'e', 'l', ' ', 4, 'S', 'h', 'i', ' ', 3, 'O', 'l', ' ', 4, 'M', 'a', 'i', ' ', 5, 'L', 'u', 'a', 'n', ' ', 4, 'C', 'a', 'l', ' ', 3, 'R', 'u', ' ', 4, 'X', 'u', 'e', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'F', 'u', ' ', 4, 'S', 'h', 'a', ' ', 3, 'N', 'a', ' ', 4, 'G', 'a', 'n', ' ', 4, 'S', 'o', 'l', ' ', 3, 'E', 'l', ' ', 5, 'C', 'w', 'u', 'l', ' ', 4, 'G', 'a', 'n', ' ', 4, 'C', 'h', 'i', ' ', 4, 'G', 'u', 'i', ' ', 4, 'G', 'a', 'n', ' ', 5, 'L', 'u', 'a', 'n', ' ', 4, 'L', 'i', 'n', ' ', 3, 'Y', 'i', ' ', 4, 'J', 'u', 'e', ' ', 5, 'L', 'i', 'a', 'o', ' ', 3, 'M', 'a', ' ', 3, 'Y', 'u', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 4, 'S', 'h', 'i', ' ', 4, 'S', 'h', 'i', ' ', 3, 'E', 'r', ' ', 4, 'C', 'h', 'u', ' ', 3, 'Y', 'u', ' ', 3, 'Y', 'u', ' ', 3, 'Y', 'u', ' ', 4, 'Y', 'u', 'n', ' ', 3, 'H', 'u', ' ', 3, 'Q', 'i', ' ', 3, 'W', 'u', ' ', 5, 'J', 'i', 'n', 'g', ' ', 3, 'S', 'i', ' ', 4, 'S', 'u', 'i', ' ', 4, 'G', 'e', 'n', ' ', 4, 'G', 'e', 'n', ' ', 3, 'Y', 'a', ' ', 4, 'X', 'i', 'e', ' ', 3, 'Y', 'a', ' ', 3, 'Q', 'i', ' ', 3, 'Y', 'a', ' ', 3, 'J', 'i', ' ', 4, 'T', 'o', 'u', ' ', 5, 'W', 'a', 'n', 'g', ' ', 5, 'K', 'a', 'n', 'g', ' ', 3, 'T', 'a', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'H', 'a', 'i', ' ', 3, 'Y', 'i', ' ', 5, 'C', 'h', 'a', 'n', ' ', 5, 'H', 'e', 'n', 'g', ' ', 3, 'M', 'u', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 5, 'J', 'i', 'n', 'g', ' ', 5, 'T', 'i', 'n', 'g', ' ', 6, 'L', 'i', 'a', 'n', 'g', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 5, 'J', 'i', 'n', 'g', ' ', 3, 'Y', 'e', ' ', 4, 'Q', 'i', 'n', ' ', 3, 'B', 'o', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'X', 'i', 'e', ' ', 4, 'D', 'a', 'n', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'D', 'u', 'o', ' ', 4, 'W', 'e', 'i', ' ', 4, 'R', 'e', 'n', ' ', 4, 'R', 'e', 'n', ' ', 3, 'J', 'i', ' ', 3, 'L', 'a', ' ', 5, 'W', 'a', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 4, 'S', 'h', 'i', ' ', 4, 'R', 'e', 'n', ' ', 3, 'L', 'e', ' ', 5, 'D', 'i', 'n', 'g', ' ', 3, 'Z', 'e', ' ', 4, 'J', 'i', 'n', ' ', 3, 'P', 'u', ' ', 5, 'C', 'h', 'o', 'u', ' ', 3, 'B', 'a', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 4, 'J', 'i', 'n', ' ', 4, 'J', 'i', 'e', ' ', 5, 'B', 'i', 'n', 'g', ' ', 5, 'R', 'e', 'n', 'g', ' ', 5, 'C', 'o', 'n', 'g', ' ', 3, 'F', 'o', ' ', 4, 'S', 'a', 'n', ' ', 4, 'L', 'u', 'n', ' ', 4, 'S', 'y', 'a', ' ', 5, 'C', 'a', 'n', 'g', ' ', 3, 'Z', 'i', ' ', 4, 'S', 'h', 'i', ' ', 3, 'T', 'a', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 3, 'F', 'u', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'T', 'u', 'o', ' ', 5, 'H', 'o', 'n', 'g', ' ', 5, 'T', 'o', 'n', 'g', ' ', 4, 'R', 'e', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'G', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 3, 'D', 'i', ' ', 4, 'D', 'a', 'i', ' ', 5, 'L', 'i', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 5, 'C', 'h', 'a', 'o', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 3, 'S', 'a', ' ', 3, 'Y', 'i', ' ', 3, 'M', 'u', ' ', 4, 'M', 'e', 'n', ' ', 4, 'R', 'e', 'n', ' ', 4, 'J', 'i', 'a', ' ', 5, 'C', 'h', 'a', 'o', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 3, 'P', 'i', ' ', 4, 'W', 'a', 'n', ' ', 3, 'W', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'J', 'i', 'e', ' ', 4, 'Y', 'a', 'o', ' ', 5, 'F', 'e', 'n', 'g', ' ', 5, 'C', 'a', 'n', 'g', ' ', 4, 'R', 'e', 'n', ' ', 5, 'W', 'a', 'n', 'g', ' ', 4, 'F', 'e', 'n', ' ', 3, 'D', 'i', ' ', 5, 'F', 'a', 'n', 'g', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 3, 'Q', 'i', ' ', 4, 'P', 'e', 'i', ' ', 3, 'Y', 'u', ' ', 5, 'D', 'i', 'a', 'o', ' ', 4, 'D', 'u', 'n', ' ', 4, 'W', 'e', 'n', ' ', 3, 'Y', 'i', ' ', 4, 'X', 'i', 'n', ' ', 5, 'K', 'a', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 3, 'J', 'i', ' ', 3, 'A', 'i', ' ', 3, 'W', 'u', ' ', 3, 'J', 'i', ' ', 3, 'F', 'u', ' ', 3, 'F', 'a', ' ', 4, 'X', 'i', 'u', ' ', 4, 'J', 'i', 'n', ' ', 4, 'B', 'e', 'i', ' ', 4, 'D', 'a', 'n', ' ', 3, 'F', 'u', ' ', 5, 'T', 'a', 'n', 'g', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'H', 'u', 'o', ' ', 4, 'H', 'u', 'i', ' ', 3, 'Y', 'u', ' ', 4, 'C', 'u', 'i', ' ', 6, 'C', 'h', 'u', 'a', 'n', ' ', 4, 'S', 'a', 'n', ' ', 4, 'W', 'e', 'i', ' ', 6, 'C', 'h', 'u', 'a', 'n', ' ', 4, 'C', 'h', 'e', ' ', 3, 'Y', 'a', ' ', 5, 'X', 'i', 'a', 'n', ' ', 6, 'S', 'h', 'a', 'n', 'g', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 4, 'L', 'u', 'n', ' ', 5, 'C', 'a', 'n', 'g', ' ', 4, 'X', 'u', 'n', ' ', 4, 'X', 'i', 'n', ' ', 4, 'W', 'e', 'i', ' ', 4, 'Z', 'h', 'u', ' ', 5, 'X', 'u', 'a', 'n', ' ', 3, 'N', 'u', ' ', 3, 'B', 'o', ' ', 3, 'G', 'u', ' ', 3, 'N', 'i', ' ', 3, 'N', 'i', ' ', 4, 'X', 'i', 'e', ' ', 4, 'B', 'a', 'n', ' ', 3, 'X', 'u', ' ', 5, 'L', 'i', 'n', 'g', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 5, 'S', 'h', 'e', 'n', ' ', 3, 'Q', 'u', ' ', 3, 'S', 'i', ' ', 5, 'B', 'e', 'n', 'g', ' ', 3, 'S', 'i', ' ', 4, 'J', 'i', 'a', ' ', 3, 'P', 'i', ' ', 3, 'Y', 'i', ' ', 3, 'S', 'i', ' ', 3, 'A', 'i', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 5, 'D', 'i', 'a', 'n', ' ', 4, 'H', 'a', 'n', ' ', 4, 'M', 'a', 'i', ' ', 4, 'D', 'a', 'n', ' ', 4, 'Z', 'h', 'u', ' ', 3, 'B', 'u', ' ', 3, 'Q', 'u', ' ', 3, 'B', 'i', ' ', 5, 'S', 'h', 'a', 'o', ' ', 3, 'C', 'i', ' ', 4, 'W', 'e', 'i', ' ', 3, 'D', 'i', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'Z', 'u', 'o', ' ', 4, 'Y', 'o', 'u', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 3, 'T', 'i', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 3, 'H', 'e', ' ', 3, 'B', 'i', ' ', 4, 'T', 'u', 'o', ' ', 4, 'S', 'h', 'e', ' ', 3, 'Y', 'u', ' ', 3, 'Y', 'i', ' ', 3, 'F', 'o', ' ', 4, 'Z', 'u', 'o', ' ', 4, 'K', 'o', 'u', ' ', 5, 'N', 'i', 'n', 'g', ' ', 5, 'T', 'o', 'n', 'g', ' ', 3, 'N', 'i', ' ', 5, 'X', 'u', 'a', 'n', ' ', 3, 'Q', 'u', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 3, 'W', 'a', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 3, 'K', 'a', ' ', 4, 'P', 'e', 'i', ' ', 5, 'H', 'u', 'a', 'i', ' ', 3, 'H', 'e', ' ', 4, 'L', 'a', 'o', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 3, 'G', 'e', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 4, 'B', 'a', 'i', ' ', 3, 'F', 'a', ' ', 5, 'M', 'i', 'n', 'g', ' ', 4, 'J', 'i', 'a', ' ', 3, 'E', 'r', ' ', 5, 'B', 'i', 'n', 'g', ' ', 3, 'J', 'i', ' ', 4, 'H', 'e', 'n', ' ', 4, 'H', 'u', 'o', ' ', 4, 'G', 'u', 'i', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 5, 'T', 'i', 'a', 'o', ' ', 5, 'J', 'i', 'a', 'o', ' ', 3, 'C', 'i', ' ', 3, 'Y', 'i', ' ', 4, 'S', 'h', 'i', ' ', 5, 'X', 'i', 'n', 'g', ' ', 5, 'S', 'h', 'e', 'n', ' ', 4, 'T', 'u', 'o', ' ', 4, 'K', 'a', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'G', 'a', 'i', ' ', 4, 'L', 'a', 'i', ' ', 3, 'Y', 'i', ' ', 4, 'C', 'h', 'i', ' ', 4, 'K', 'u', 'a', ' ', 6, 'G', 'u', 'a', 'n', 'g', ' ', 3, 'L', 'i', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'S', 'h', 'i', ' ', 3, 'M', 'i', ' ', 4, 'Z', 'h', 'u', ' ', 3, 'X', 'u', ' ', 4, 'Y', 'o', 'u', ' ', 3, 'A', 'n', ' ', 3, 'L', 'u', ' ', 4, 'M', 'o', 'u', ' ', 3, 'E', 'r', ' ', 4, 'L', 'u', 'n', ' ', 5, 'T', 'o', 'n', 'g', ' ', 4, 'C', 'h', 'a', ' ', 4, 'C', 'h', 'i', ' ', 4, 'X', 'u', 'n', ' ', 5, 'G', 'o', 'n', 'g', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 3, 'Y', 'i', ' ', 3, 'R', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'X', 'i', 'a', ' ', 4, 'J', 'i', 'a', ' ', 4, 'Z', 'a', 'i', ' ', 3, 'L', 'u', ' ', 3, 'K', 'o', ' ', 5, 'J', 'i', 'a', 'o', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 3, 'C', 'e', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 5, 'K', 'u', 'a', 'i', ' ', 5, 'C', 'h', 'a', 'i', ' ', 5, 'N', 'i', 'n', 'g', ' ', 5, 'N', 'o', 'n', 'g', ' ', 4, 'J', 'i', 'n', ' ', 3, 'W', 'u', ' ', 4, 'H', 'o', 'u', ' ', 6, 'J', 'i', 'o', 'n', 'g', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 4, 'Z', 'u', 'o', ' ', 5, 'C', 'h', 'o', 'u', ' ', 4, 'Q', 'i', 'n', ' ', 3, 'L', 'u', ' ', 3, 'J', 'u', ' ', 4, 'S', 'h', 'u', ' ', 5, 'T', 'i', 'n', 'g', ' ', 5, 'S', 'h', 'e', 'n', ' ', 4, 'T', 'u', 'o', ' ', 3, 'B', 'o', ' ', 4, 'N', 'a', 'n', ' ', 4, 'H', 'a', 'o', ' ', 5, 'B', 'i', 'a', 'n', ' ', 4, 'T', 'u', 'i', ' ', 3, 'Y', 'u', ' ', 3, 'X', 'i', ' ', 3, 'C', 'u', ' ', 2, 'E', ' ', 4, 'Q', 'i', 'u', ' ', 3, 'X', 'u', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 3, 'K', 'u', ' ', 3, 'W', 'u', ' ', 4, 'J', 'u', 'n', ' ', 3, 'Y', 'i', ' ', 3, 'F', 'u', ' ', 5, 'L', 'a', 'n', 'g', ' ', 3, 'Z', 'u', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 3, 'L', 'i', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 4, 'H', 'u', 'n', ' ', 5, 'J', 'i', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'S', 'a', 'n', ' ', 4, 'P', 'a', 'i', ' ', 3, 'S', 'u', ' ', 3, 'F', 'u', ' ', 3, 'X', 'i', ' ', 3, 'L', 'i', ' ', 3, 'F', 'u', ' ', 5, 'P', 'i', 'n', 'g', ' ', 4, 'B', 'a', 'o', ' ', 3, 'Y', 'u', ' ', 3, 'S', 'i', ' ', 4, 'X', 'i', 'a', ' ', 4, 'X', 'i', 'n', ' ', 4, 'X', 'i', 'u', ' ', 3, 'Y', 'u', ' ', 3, 'T', 'i', ' ', 4, 'C', 'h', 'e', ' ', 5, 'C', 'h', 'o', 'u', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'L', 'i', 'a', ' ', 3, 'L', 'i', ' ', 4, 'L', 'a', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'X', 'i', 'u', ' ', 3, 'F', 'u', ' ', 3, 'H', 'e', ' ', 3, 'J', 'u', ' ', 5, 'X', 'i', 'a', 'o', ' ', 4, 'P', 'a', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'B', 'i', 'a', 'o', ' ', 4, 'C', 'h', 'u', ' ', 4, 'F', 'e', 'i', ' ', 5, 'F', 'e', 'n', 'g', ' ', 3, 'Y', 'a', ' ', 3, 'A', 'n', ' ', 4, 'B', 'e', 'i', ' ', 3, 'Y', 'u', ' ', 4, 'X', 'i', 'n', ' ', 3, 'B', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 4, 'C', 'h', 'i', ' ', 5, 'B', 'i', 'n', 'g', ' ', 4, 'Z', 'a', 'n', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'C', 'u', 'i', ' ', 4, 'L', 'i', 'a', ' ', 4, 'W', 'a', 'n', ' ', 4, 'L', 'a', 'i', ' ', 5, 'C', 'a', 'n', 'g', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 3, 'G', 'e', ' ', 5, 'G', 'u', 'a', 'n', ' ', 4, 'B', 'e', 'i', ' ', 5, 'T', 'i', 'a', 'n', ' ', 4, 'S', 'h', 'u', ' ', 4, 'S', 'h', 'u', ' ', 4, 'M', 'e', 'n', ' ', 4, 'D', 'a', 'o', ' ', 4, 'T', 'a', 'n', ' ', 4, 'J', 'u', 'e', ' ', 5, 'C', 'h', 'u', 'i', ' ', 5, 'X', 'i', 'n', 'g', ' ', 5, 'P', 'e', 'n', 'g', ' ', 5, 'T', 'a', 'n', 'g', ' ', 4, 'H', 'o', 'u', ' ', 3, 'Y', 'i', ' ', 3, 'Q', 'i', ' ', 3, 'T', 'i', ' ', 4, 'G', 'a', 'n', ' ', 5, 'J', 'i', 'n', 'g', ' ', 4, 'J', 'i', 'e', ' ', 4, 'S', 'u', 'i', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 4, 'J', 'i', 'e', ' ', 5, 'F', 'a', 'n', 'g', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'K', 'o', 'n', 'g', ' ', 5, 'J', 'u', 'a', 'n', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 3, 'J', 'u', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 3, 'N', 'i', ' ', 4, 'L', 'u', 'n', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 4, 'W', 'e', 'i', ' ', 4, 'L', 'u', 'o', ' ', 5, 'S', 'o', 'n', 'g', ' ', 5, 'L', 'e', 'n', 'g', ' ', 4, 'H', 'u', 'n', ' ', 5, 'D', 'o', 'n', 'g', ' ', 3, 'Z', 'i', ' ', 4, 'B', 'e', 'n', ' ', 3, 'W', 'u', ' ', 3, 'J', 'u', ' ', 4, 'N', 'a', 'i', ' ', 4, 'C', 'a', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'Z', 'h', 'a', 'i', ' ', 3, 'Y', 'e', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'S', 'h', 'a', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'N', 'u', 'a', 'n', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 5, 'C', 'h', 'u', 'n', ' ', 4, 'J', 'i', 'a', ' ', 4, 'J', 'i', 'e', ' ', 4, 'W', 'e', 'i', ' ', 3, 'Y', 'u', ' ', 5, 'B', 'i', 'n', 'g', ' ', 4, 'R', 'u', 'o', ' ', 3, 'T', 'i', ' ', 4, 'W', 'e', 'i', ' ', 5, 'P', 'i', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'F', 'e', 'n', 'g', ' ', 5, 'T', 'a', 'n', 'g', ' ', 3, 'W', 'o', ' ', 2, 'E', ' ', 4, 'X', 'i', 'e', ' ', 4, 'C', 'h', 'e', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 4, 'K', 'a', 'n', ' ', 3, 'D', 'i', ' ', 4, 'Z', 'u', 'o', ' ', 4, 'C', 'h', 'a', ' ', 5, 'T', 'i', 'n', 'g', ' ', 4, 'B', 'e', 'i', ' ', 3, 'Y', 'e', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 4, 'Y', 'a', 'o', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 5, 'C', 'h', 'o', 'u', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'Y', 'o', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'X', 'u', ' ', 4, 'Z', 'h', 'a', ' ', 3, 'C', 'i', ' ', 3, 'F', 'u', ' ', 3, 'B', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 5, 'M', 'i', 'a', 'n', ' ', 3, 'J', 'i', ' ', 3, 'Y', 'i', ' ', 4, 'X', 'i', 'e', ' ', 4, 'X', 'u', 'n', ' ', 3, 'S', 'i', ' ', 5, 'D', 'u', 'a', 'n', ' ', 3, 'C', 'e', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 3, 'O', 'u', ' ', 4, 'T', 'o', 'u', ' ', 4, 'T', 'o', 'u', ' ', 4, 'B', 'e', 'i', ' ', 3, 'Z', 'a', ' ', 3, 'L', 'u', ' ', 4, 'J', 'i', 'e', ' ', 4, 'W', 'e', 'i', ' ', 4, 'F', 'e', 'n', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 4, 'G', 'u', 'i', ' ', 4, 'S', 'o', 'u', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'S', 'u', ' ', 4, 'X', 'i', 'a', ' ', 3, 'F', 'u', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 5, 'R', 'o', 'n', 'g', ' ', 3, 'L', 'i', ' ', 3, 'R', 'u', ' ', 4, 'Y', 'u', 'n', ' ', 4, 'G', 'o', 'u', ' ', 3, 'M', 'a', ' ', 5, 'B', 'a', 'n', 'g', ' ', 5, 'D', 'i', 'a', 'n', ' ', 5, 'T', 'a', 'n', 'g', ' ', 4, 'H', 'a', 'o', ' ', 4, 'J', 'i', 'e', ' ', 3, 'X', 'i', ' ', 5, 'S', 'h', 'a', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'J', 'u', 'e', ' ', 5, 'C', 'a', 'n', 'g', ' ', 4, 'C', 'h', 'u', ' ', 4, 'S', 'a', 'n', ' ', 4, 'B', 'e', 'i', ' ', 5, 'X', 'i', 'a', 'o', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'T', 'a', 'n', ' ', 4, 'S', 'u', 'o', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 3, 'F', 'a', ' ', 5, 'B', 'i', 'n', 'g', ' ', 4, 'J', 'i', 'a', ' ', 4, 'D', 'a', 'i', ' ', 4, 'Z', 'a', 'i', ' ', 5, 'T', 'a', 'n', 'g', ' ', 4, 'B', 'i', 'n', ' ', 4, 'C', 'h', 'u', ' ', 4, 'N', 'u', 'o', ' ', 4, 'C', 'a', 'n', ' ', 4, 'L', 'e', 'i', ' ', 4, 'C', 'u', 'i', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 4, 'Z', 'a', 'o', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 5, 'P', 'e', 'n', 'g', ' ', 5, 'S', 'o', 'n', 'g', ' ', 3, 'A', 'o', ' ', 6, 'C', 'h', 'u', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 5, 'Z', 'h', 'a', 'i', ' ', 4, 'C', 'o', 'u', ' ', 6, 'S', 'h', 'a', 'n', 'g', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 5, 'J', 'i', 'n', 'g', ' ', 4, 'C', 'h', 'i', ' ', 4, 'S', 'h', 'a', ' ', 4, 'H', 'a', 'n', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'D', 'i', ' ', 3, 'X', 'i', ' ', 3, 'L', 'u', ' ', 4, 'B', 'e', 'i', ' ', 5, 'P', 'i', 'a', 'o', ' ', 4, 'J', 'i', 'n', ' ', 5, 'L', 'i', 'a', 'n', ' ', 3, 'L', 'u', ' ', 4, 'M', 'a', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'T', 'a', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 5, 'D', 'o', 'n', 'g', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 5, 'S', 'h', 'a', 'n', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 6, 'J', 'i', 'o', 'n', 'g', ' ', 4, 'T', 'u', 'i', ' ', 4, 'Z', 'u', 'n', ' ', 3, 'P', 'u', ' ', 3, 'X', 'i', ' ', 4, 'L', 'a', 'o', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 6, 'G', 'u', 'a', 'n', 'g', ' ', 5, 'L', 'i', 'a', 'o', ' ', 3, 'Q', 'i', ' ', 5, 'D', 'e', 'n', 'g', ' ', 5, 'C', 'h', 'a', 'n', ' ', 4, 'W', 'e', 'i', ' ', 3, 'J', 'i', ' ', 4, 'F', 'a', 'n', ' ', 4, 'H', 'u', 'i', ' ', 6, 'C', 'h', 'u', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'D', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'J', 'i', 'u', ' ', 5, 'S', 'e', 'n', 'g', ' ', 4, 'F', 'e', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'J', 'u', 'e', ' ', 2, 'E', ' ', 5, 'J', 'i', 'a', 'o', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'T', 'o', 'n', 'g', ' ', 4, 'L', 'i', 'n', ' ', 3, 'B', 'o', ' ', 3, 'G', 'u', ' ', 3, 'S', 'u', ' ', 5, 'X', 'i', 'a', 'n', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 4, 'M', 'i', 'n', ' ', 3, 'Y', 'e', ' ', 4, 'J', 'i', 'n', ' ', 4, 'J', 'i', 'a', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 3, 'P', 'i', ' ', 5, 'F', 'e', 'n', 'g', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 3, 'A', 'i', ' ', 4, 'S', 'a', 'i', ' ', 3, 'Y', 'i', ' ', 4, 'J', 'u', 'n', ' ', 5, 'N', 'o', 'n', 'g', ' ', 5, 'C', 'h', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 5, 'D', 'a', 'n', 'g', ' ', 5, 'J', 'i', 'n', 'g', ' ', 5, 'X', 'u', 'a', 'n', ' ', 5, 'K', 'u', 'a', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'C', 'h', 'u', ' ', 4, 'D', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'S', 'h', 'a', ' ', 4, 'Z', 'a', 'i', ' ', 4, 'B', 'i', 'n', ' ', 3, 'A', 'n', ' ', 3, 'R', 'u', ' ', 4, 'T', 'a', 'i', ' ', 5, 'C', 'h', 'o', 'u', ' ', 5, 'C', 'h', 'a', 'i', ' ', 4, 'L', 'a', 'n', ' ', 3, 'N', 'i', ' ', 4, 'J', 'i', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'M', 'e', 'n', 'g', ' ', 3, 'W', 'u', ' ', 5, 'N', 'i', 'n', 'g', ' ', 6, 'Q', 'i', 'o', 'n', 'g', ' ', 3, 'N', 'i', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 4, 'L', 'i', 'e', ' ', 4, 'L', 'e', 'i', ' ', 3, 'L', 'u', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 4, 'B', 'a', 'o', ' ', 3, 'D', 'u', ' ', 5, 'B', 'i', 'a', 'o', ' ', 4, 'Z', 'a', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'S', 'i', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'H', 'a', 'o', ' ', 5, 'C', 'h', 'e', 'n', ' ', 5, 'C', 'h', 'e', 'n', ' ', 3, 'L', 'i', ' ', 5, 'T', 'e', 'n', 'g', ' ', 4, 'W', 'e', 'i', ' ', 5, 'L', 'o', 'n', 'g', ' ', 4, 'C', 'h', 'u', ' ', 5, 'C', 'h', 'a', 'n', ' ', 5, 'R', 'a', 'n', 'g', ' ', 4, 'S', 'h', 'u', ' ', 4, 'H', 'u', 'i', ' ', 3, 'L', 'i', ' ', 4, 'L', 'u', 'o', ' ', 4, 'Z', 'a', 'n', ' ', 4, 'N', 'u', 'o', ' ', 5, 'T', 'a', 'n', 'g', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'L', 'e', 'i', ' ', 5, 'N', 'a', 'n', 'g', ' ', 3, 'E', 'r', ' ', 3, 'W', 'u', ' ', 4, 'Y', 'u', 'n', ' ', 4, 'Z', 'a', 'n', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 6, 'X', 'i', 'o', 'n', 'g', ' ', 6, 'C', 'h', 'o', 'n', 'g', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 6, 'X', 'i', 'o', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'n', ' ', 6, 'G', 'u', 'a', 'n', 'g', ' ', 4, 'D', 'u', 'i', ' ', 3, 'K', 'e', ' ', 4, 'D', 'u', 'i', ' ', 5, 'M', 'i', 'a', 'n', ' ', 3, 'T', 'u', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 3, 'E', 'r', ' ', 4, 'D', 'u', 'i', ' ', 3, 'E', 'r', ' ', 4, 'X', 'i', 'n', ' ', 3, 'T', 'u', ' ', 3, 'S', 'i', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'S', 'h', 'i', ' ', 4, 'S', 'h', 'i', ' ', 5, 'D', 'a', 'n', 'g', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'D', 'o', 'u', ' ', 4, 'F', 'e', 'n', ' ', 4, 'M', 'a', 'o', ' ', 5, 'S', 'h', 'e', 'n', ' ', 4, 'D', 'o', 'u', ' ', 4, 'B', 'a', 'i', ' ', 5, 'J', 'i', 'n', 'g', ' ', 3, 'L', 'i', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 3, 'R', 'u', ' ', 5, 'W', 'a', 'n', 'g', ' ', 4, 'N', 'e', 'i', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 6, 'L', 'i', 'a', 'n', 'g', ' ', 3, 'Y', 'u', ' ', 3, 'B', 'a', ' ', 5, 'G', 'o', 'n', 'g', ' ', 4, 'L', 'i', 'u', ' ', 3, 'X', 'i', ' ', 4, 'L', 'a', 'n', ' ', 5, 'G', 'o', 'n', 'g', ' ', 5, 'T', 'i', 'a', 'n', ' ', 5, 'G', 'u', 'a', 'n', ' ', 5, 'X', 'i', 'n', 'g', ' ', 5, 'B', 'i', 'n', 'g', ' ', 3, 'Q', 'i', ' ', 3, 'J', 'u', ' ', 5, 'D', 'i', 'a', 'n', ' ', 3, 'Z', 'i', ' ', 6, 'P', 'p', 'w', 'u', 'n', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'S', 'h', 'o', 'u', ' ', 3, 'J', 'i', ' ', 3, 'Y', 'i', ' ', 3, 'J', 'i', ' ', 5, 'C', 'h', 'a', 'n', ' ', 6, 'J', 'i', 'o', 'n', 'g', ' ', 4, 'M', 'a', 'o', ' ', 4, 'R', 'a', 'n', ' ', 4, 'N', 'e', 'i', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'M', 'a', 'o', ' ', 5, 'G', 'a', 'n', 'g', ' ', 4, 'R', 'a', 'n', ' ', 3, 'C', 'e', ' ', 6, 'J', 'i', 'o', 'n', 'g', ' ', 3, 'C', 'e', ' ', 4, 'Z', 'a', 'i', ' ', 4, 'G', 'u', 'a', ' ', 6, 'J', 'i', 'o', 'n', 'g', ' ', 4, 'M', 'a', 'o', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 4, 'M', 'o', 'u', ' ', 4, 'G', 'o', 'u', ' ', 3, 'X', 'u', ' ', 5, 'M', 'i', 'a', 'n', ' ', 3, 'M', 'i', ' ', 5, 'R', 'o', 'n', 'g', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'X', 'i', 'e', ' ', 4, 'K', 'a', 'n', ' ', 4, 'J', 'u', 'n', ' ', 5, 'N', 'o', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 3, 'M', 'i', ' ', 4, 'S', 'h', 'i', ' ', 5, 'G', 'u', 'a', 'n', ' ', 5, 'M', 'e', 'n', 'g', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 3, 'J', 'u', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 5, 'M', 'i', 'n', 'g', ' ', 4, 'K', 'o', 'u', ' ', 4, 'L', 'a', 'm', ' ', 3, 'F', 'u', ' ', 4, 'X', 'i', 'e', ' ', 3, 'M', 'i', ' ', 5, 'B', 'i', 'n', 'g', ' ', 5, 'D', 'o', 'n', 'g', ' ', 4, 'T', 'a', 'i', ' ', 5, 'G', 'a', 'n', 'g', ' ', 5, 'F', 'e', 'n', 'g', ' ', 5, 'B', 'i', 'n', 'g', ' ', 3, 'H', 'u', ' ', 6, 'C', 'h', 'o', 'n', 'g', ' ', 4, 'J', 'u', 'e', ' ', 3, 'H', 'u', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 3, 'Y', 'e', ' ', 5, 'L', 'e', 'n', 'g', ' ', 4, 'P', 'a', 'n', ' ', 3, 'F', 'u', ' ', 4, 'M', 'i', 'n', ' ', 5, 'D', 'o', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'L', 'i', 'e', ' ', 4, 'X', 'i', 'a', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'J', 'i', 'n', 'g', ' ', 4, 'S', 'h', 'u', ' ', 4, 'M', 'e', 'i', ' ', 3, 'T', 'u', ' ', 3, 'Q', 'i', ' ', 3, 'G', 'u', ' ', 5, 'Z', 'h', 'u', 'n', ' ', 5, 'S', 'o', 'n', 'g', ' ', 5, 'J', 'i', 'n', 'g', ' ', 6, 'L', 'i', 'a', 'n', 'g', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 5, 'D', 'i', 'a', 'o', ' ', 5, 'L', 'i', 'n', 'g', ' ', 5, 'D', 'o', 'n', 'g', ' ', 4, 'G', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'C', 'o', 'u', ' ', 3, 'Y', 'i', ' ', 3, 'L', 'i', ' ', 5, 'C', 'a', 'n', 'g', ' ', 5, 'M', 'i', 'n', 'g', ' ', 6, 'Z', 'h', 'u', 'e', 'n', ' ', 4, 'C', 'u', 'i', ' ', 3, 'S', 'i', ' ', 4, 'D', 'u', 'o', ' ', 4, 'J', 'i', 'n', ' ', 4, 'L', 'i', 'n', ' ', 4, 'L', 'i', 'n', ' ', 5, 'N', 'i', 'n', 'g', ' ', 3, 'X', 'i', ' ', 3, 'D', 'u', ' ', 3, 'J', 'i', ' ', 4, 'F', 'a', 'n', ' ', 4, 'F', 'a', 'n', ' ', 4, 'F', 'a', 'n', ' ', 5, 'F', 'e', 'n', 'g', ' ', 3, 'J', 'u', ' ', 4, 'C', 'h', 'u', ' ', 5, 'T', 'a', 'k', 'o', ' ', 5, 'F', 'e', 'n', 'g', ' ', 4, 'M', 'o', 'k', ' ', 3, 'C', 'i', ' ', 3, 'F', 'u', ' ', 5, 'F', 'e', 'n', 'g', ' ', 5, 'P', 'i', 'n', 'g', ' ', 5, 'F', 'e', 'n', 'g', ' ', 4, 'K', 'a', 'i', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 4, 'K', 'a', 'i', ' ', 4, 'G', 'a', 'n', ' ', 5, 'D', 'e', 'n', 'g', ' ', 5, 'P', 'i', 'n', 'g', ' ', 3, 'Q', 'u', ' ', 6, 'X', 'i', 'o', 'n', 'g', ' ', 5, 'K', 'u', 'a', 'i', ' ', 3, 'T', 'u', ' ', 3, 'A', 'o', ' ', 4, 'C', 'h', 'u', ' ', 3, 'J', 'i', ' ', 5, 'D', 'a', 'n', 'g', ' ', 4, 'H', 'a', 'n', ' ', 4, 'H', 'a', 'n', ' ', 4, 'Z', 'a', 'o', ' ', 4, 'D', 'a', 'o', ' ', 5, 'D', 'i', 'a', 'o', ' ', 4, 'D', 'a', 'o', ' ', 4, 'R', 'e', 'n', ' ', 4, 'R', 'e', 'n', ' ', 7, 'C', 'h', 'u', 'a', 'n', 'g', ' ', 4, 'F', 'e', 'n', ' ', 4, 'Q', 'i', 'e', ' ', 3, 'Y', 'i', ' ', 3, 'J', 'i', ' ', 4, 'K', 'a', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'C', 'u', 'n', ' ', 4, 'C', 'h', 'u', ' ', 4, 'W', 'e', 'n', ' ', 3, 'J', 'i', ' ', 4, 'D', 'a', 'n', ' ', 5, 'X', 'i', 'n', 'g', ' ', 4, 'H', 'u', 'a', ' ', 4, 'W', 'a', 'n', ' ', 4, 'J', 'u', 'e', ' ', 3, 'L', 'i', ' ', 4, 'Y', 'u', 'e', ' ', 4, 'L', 'i', 'e', ' ', 4, 'L', 'i', 'u', ' ', 3, 'Z', 'e', ' ', 5, 'G', 'a', 'n', 'g', ' ', 7, 'C', 'h', 'u', 'a', 'n', 'g', ' ', 3, 'F', 'u', ' ', 4, 'C', 'h', 'u', ' ', 3, 'Q', 'u', ' ', 3, 'J', 'u', ' ', 5, 'S', 'h', 'a', 'n', ' ', 4, 'M', 'i', 'n', ' ', 5, 'L', 'i', 'n', 'g', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 4, 'P', 'a', 'n', ' ', 4, 'B', 'i', 'e', ' ', 4, 'J', 'i', 'e', ' ', 4, 'J', 'i', 'e', ' ', 4, 'B', 'a', 'o', ' ', 3, 'L', 'i', ' ', 5, 'S', 'h', 'a', 'n', ' ', 4, 'B', 'i', 'e', ' ', 5, 'C', 'h', 'a', 'n', ' ', 5, 'J', 'i', 'n', 'g', ' ', 4, 'G', 'u', 'a', ' ', 4, 'G', 'e', 'n', ' ', 4, 'D', 'a', 'o', ' ', 7, 'C', 'h', 'u', 'a', 'n', 'g', ' ', 4, 'K', 'u', 'i', ' ', 3, 'K', 'u', ' ', 4, 'D', 'u', 'o', ' ', 3, 'E', 'r', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'S', 'h', 'u', 'a', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 4, 'C', 'h', 'a', ' ', 3, 'C', 'i', ' ', 3, 'K', 'e', ' ', 4, 'J', 'i', 'e', ' ', 4, 'G', 'u', 'i', ' ', 3, 'C', 'i', ' ', 4, 'G', 'u', 'i', ' ', 4, 'K', 'a', 'i', ' ', 4, 'D', 'u', 'o', ' ', 3, 'J', 'i', ' ', 3, 'T', 'i', ' ', 5, 'J', 'i', 'n', 'g', ' ', 4, 'L', 'o', 'u', ' ', 4, 'G', 'e', 'n', ' ', 3, 'Z', 'e', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'C', 'u', 'o', ' ', 4, 'X', 'u', 'e', ' ', 3, 'K', 'e', ' ', 3, 'L', 'a', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'C', 'h', 'a', ' ', 7, 'C', 'h', 'u', 'a', 'n', 'g', ' ', 4, 'G', 'u', 'a', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'C', 'u', 'o', ' ', 3, 'L', 'i', ' ', 3, 'T', 'i', ' ', 4, 'F', 'e', 'i', ' ', 4, 'P', 'o', 'u', ' ', 5, 'C', 'h', 'a', 'n', ' ', 3, 'Q', 'i', ' ', 7, 'C', 'h', 'u', 'a', 'n', 'g', ' ', 3, 'Z', 'i', ' ', 5, 'G', 'a', 'n', 'g', ' ', 4, 'W', 'a', 'n', ' ', 3, 'B', 'o', ' ', 3, 'J', 'i', ' ', 4, 'D', 'u', 'o', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'J', 'i', ' ', 3, 'B', 'o', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'J', 'u', ' ', 4, 'H', 'u', 'o', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'D', 'u', 'o', ' ', 5, 'D', 'u', 'a', 'n', ' ', 3, 'W', 'u', ' ', 4, 'G', 'u', 'a', ' ', 3, 'F', 'u', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'G', 'e', ' ', 4, 'Z', 'h', 'a', ' ', 4, 'K', 'a', 'i', ' ', 7, 'C', 'h', 'u', 'a', 'n', 'g', ' ', 5, 'J', 'u', 'a', 'n', ' ', 5, 'C', 'h', 'a', 'n', ' ', 5, 'T', 'u', 'a', 'n', ' ', 3, 'L', 'u', ' ', 3, 'L', 'i', ' ', 4, 'F', 'o', 'u', ' ', 5, 'S', 'h', 'a', 'n', ' ', 5, 'P', 'i', 'a', 'o', ' ', 4, 'K', 'o', 'u', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'G', 'u', 'a', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 4, 'J', 'u', 'e', ' ', 4, 'H', 'u', 'a', ' ', 4, 'Z', 'h', 'a', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 5, 'L', 'i', 'a', 'n', ' ', 3, 'J', 'u', ' ', 3, 'P', 'i', ' ', 4, 'L', 'i', 'u', ' ', 4, 'G', 'u', 'i', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'G', 'u', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'T', 'a', 'n', 'g', ' ', 4, 'H', 'u', 'o', ' ', 3, 'J', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'C', 'h', 'a', 'n', ' ', 5, 'C', 'u', 'a', 'n', ' ', 3, 'M', 'o', ' ', 3, 'L', 'i', ' ', 4, 'Z', 'h', 'u', ' ', 3, 'L', 'i', ' ', 3, 'Y', 'a', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 4, 'B', 'a', 'n', ' ', 5, 'G', 'o', 'n', 'g', ' ', 4, 'J', 'i', 'a', ' ', 3, 'W', 'u', ' ', 4, 'M', 'a', 'i', ' ', 4, 'L', 'i', 'e', ' ', 4, 'J', 'i', 'n', ' ', 5, 'K', 'e', 'n', 'g', ' ', 4, 'X', 'i', 'e', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'D', 'o', 'n', 'g', ' ', 4, 'Z', 'h', 'u', ' ', 3, 'N', 'u', ' ', 4, 'J', 'i', 'e', ' ', 3, 'Q', 'u', ' ', 5, 'S', 'h', 'a', 'o', ' ', 3, 'Y', 'i', ' ', 4, 'Z', 'h', 'u', ' ', 5, 'M', 'i', 'a', 'o', ' ', 3, 'L', 'i', ' ', 5, 'J', 'i', 'n', 'g', ' ', 4, 'L', 'a', 'o', ' ', 4, 'L', 'a', 'o', ' ', 5, 'J', 'u', 'a', 'n', ' ', 4, 'K', 'o', 'u', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 3, 'W', 'a', ' ', 5, 'X', 'i', 'a', 'o', ' ', 4, 'M', 'o', 'u', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 4, 'J', 'i', 'e', ' ', 4, 'L', 'i', 'e', ' ', 3, 'H', 'e', ' ', 4, 'S', 'h', 'i', ' ', 3, 'K', 'e', ' ', 5, 'J', 'i', 'n', 'g', ' ', 4, 'H', 'a', 'o', ' ', 3, 'B', 'o', ' ', 4, 'M', 'i', 'n', ' ', 4, 'C', 'h', 'i', ' ', 5, 'L', 'a', 'n', 'g', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 5, 'M', 'i', 'a', 'n', ' ', 3, 'K', 'e', ' ', 4, 'X', 'u', 'n', ' ', 5, 'J', 'u', 'a', 'n', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 3, 'L', 'u', ' ', 4, 'P', 'o', 'u', ' ', 5, 'M', 'e', 'n', 'g', ' ', 4, 'L', 'a', 'i', ' ', 3, 'L', 'e', ' ', 4, 'K', 'a', 'i', ' ', 5, 'M', 'i', 'a', 'n', ' ', 5, 'D', 'o', 'n', 'g', ' ', 3, 'X', 'u', ' ', 3, 'X', 'u', ' ', 4, 'K', 'a', 'n', ' ', 3, 'W', 'u', ' ', 3, 'Y', 'i', ' ', 4, 'X', 'u', 'n', ' ', 5, 'W', 'e', 'n', 'g', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 4, 'L', 'a', 'o', ' ', 3, 'M', 'u', ' ', 3, 'L', 'u', ' ', 5, 'P', 'i', 'a', 'o', ' ', 4, 'S', 'h', 'i', ' ', 3, 'J', 'i', ' ', 4, 'Q', 'i', 'n', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'o', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 4, 'J', 'u', 'e', ' ', 4, 'F', 'a', 'n', ' ', 5, 'J', 'u', 'a', 'n', ' ', 5, 'T', 'o', 'n', 'g', ' ', 3, 'J', 'u', ' ', 4, 'D', 'a', 'n', ' ', 4, 'X', 'i', 'e', ' ', 4, 'M', 'a', 'i', ' ', 4, 'X', 'u', 'n', ' ', 4, 'X', 'u', 'n', ' ', 3, 'L', 'u', ' ', 3, 'L', 'i', ' ', 4, 'C', 'h', 'e', ' ', 5, 'R', 'a', 'n', 'g', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 4, 'B', 'a', 'o', ' ', 5, 'S', 'h', 'a', 'o', ' ', 4, 'Y', 'u', 'n', ' ', 4, 'J', 'i', 'u', ' ', 4, 'B', 'a', 'o', ' ', 4, 'G', 'o', 'u', ' ', 3, 'W', 'u', ' ', 4, 'Y', 'u', 'n', ' ', 5, 'M', 'w', 'u', 'n', ' ', 4, 'N', 'a', 'y', ' ', 4, 'G', 'a', 'i', ' ', 4, 'G', 'a', 'i', ' ', 4, 'B', 'a', 'o', ' ', 5, 'C', 'o', 'n', 'g', ' ', 6, 'X', 'i', 'o', 'n', 'g', ' ', 5, 'P', 'e', 'n', 'g', ' ', 3, 'J', 'u', ' ', 4, 'T', 'a', 'o', ' ', 3, 'G', 'e', ' ', 3, 'P', 'u', ' ', 3, 'A', 'n', ' ', 4, 'P', 'a', 'o', ' ', 3, 'F', 'u', ' ', 5, 'G', 'o', 'n', 'g', ' ', 3, 'D', 'a', ' ', 4, 'J', 'i', 'u', ' ', 6, 'Q', 'i', 'o', 'n', 'g', ' ', 3, 'B', 'i', ' ', 4, 'H', 'u', 'a', ' ', 4, 'B', 'e', 'i', ' ', 4, 'N', 'a', 'o', ' ', 4, 'C', 'h', 'i', ' ', 5, 'F', 'a', 'n', 'g', ' ', 4, 'J', 'i', 'u', ' ', 3, 'Y', 'i', ' ', 3, 'Z', 'a', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 5, 'K', 'a', 'n', 'g', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 3, 'H', 'u', ' ', 4, 'X', 'i', 'a', ' ', 3, 'Q', 'u', ' ', 5, 'B', 'i', 'a', 'n', ' ', 4, 'G', 'u', 'i', ' ', 4, 'Q', 'i', 'e', ' ', 5, 'Z', 'a', 'n', 'g', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 4, 'F', 'e', 'i', ' ', 3, 'H', 'u', ' ', 4, 'T', 'o', 'u', ' ', 4, 'G', 'u', 'i', ' ', 4, 'G', 'u', 'i', ' ', 4, 'H', 'u', 'i', ' ', 4, 'D', 'a', 'n', ' ', 4, 'G', 'u', 'i', ' ', 5, 'L', 'i', 'a', 'n', ' ', 5, 'L', 'i', 'a', 'n', ' ', 5, 'S', 'u', 'a', 'n', ' ', 3, 'D', 'u', ' ', 4, 'J', 'i', 'u', ' ', 3, 'Q', 'u', ' ', 3, 'X', 'i', ' ', 3, 'P', 'i', ' ', 3, 'Q', 'u', ' ', 3, 'Y', 'i', ' ', 4, 'Q', 'i', 'a', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'B', 'i', 'a', 'n', ' ', 3, 'N', 'i', ' ', 3, 'Q', 'u', ' ', 4, 'S', 'h', 'i', ' ', 4, 'X', 'i', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'N', 'i', 'a', 'n', ' ', 3, 'S', 'a', ' ', 3, 'Z', 'u', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 3, 'W', 'u', ' ', 4, 'H', 'u', 'i', ' ', 4, 'B', 'a', 'n', ' ', 4, 'S', 'h', 'i', ' ', 3, 'X', 'i', ' ', 4, 'W', 'a', 'n', ' ', 4, 'H', 'u', 'a', ' ', 4, 'X', 'i', 'e', ' ', 4, 'W', 'a', 'n', ' ', 4, 'B', 'e', 'i', ' ', 3, 'Z', 'u', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 4, 'X', 'i', 'e', ' ', 4, 'D', 'a', 'n', ' ', 4, 'M', 'a', 'i', ' ', 4, 'N', 'a', 'n', ' ', 4, 'D', 'a', 'n', ' ', 3, 'J', 'i', ' ', 3, 'B', 'o', ' ', 6, 'S', 'h', 'u', 'a', 'i', ' ', 3, 'B', 'u', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 5, 'B', 'i', 'a', 'n', ' ', 3, 'B', 'u', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 4, 'Q', 'i', 'a', ' ', 3, 'L', 'u', ' ', 4, 'Y', 'o', 'u', ' ', 3, 'L', 'u', ' ', 3, 'X', 'i', ' ', 4, 'G', 'u', 'a', ' ', 3, 'W', 'o', ' ', 4, 'X', 'i', 'e', ' ', 4, 'J', 'i', 'e', ' ', 4, 'J', 'i', 'e', ' ', 4, 'W', 'e', 'i', ' ', 4, 'A', 'n', 'g', ' ', 6, 'Q', 'i', 'o', 'n', 'g', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'M', 'a', 'o', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'W', 'e', 'i', ' ', 5, 'S', 'h', 'a', 'o', ' ', 3, 'J', 'i', ' ', 4, 'Q', 'u', 'e', ' ', 5, 'L', 'u', 'a', 'n', ' ', 4, 'S', 'h', 'i', ' ', 5, 'J', 'u', 'a', 'n', ' ', 4, 'X', 'i', 'e', ' ', 3, 'X', 'u', ' ', 4, 'J', 'i', 'n', ' ', 4, 'Q', 'u', 'e', ' ', 3, 'W', 'u', ' ', 3, 'J', 'i', ' ', 2, 'E', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 3, 'X', 'i', ' ', 4, 'H', 'a', 'n', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 2, 'E', ' ', 5, 'T', 'i', 'n', 'g', ' ', 3, 'L', 'i', ' ', 4, 'Z', 'h', 'e', ' ', 4, 'H', 'a', 'n', ' ', 3, 'L', 'i', ' ', 3, 'Y', 'a', ' ', 3, 'Y', 'a', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'S', 'h', 'e', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'Z', 'h', 'a', ' ', 5, 'P', 'a', 'n', 'g', ' ', 3, 'H', 'e', ' ', 3, 'Y', 'a', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'C', 'e', ' ', 5, 'P', 'a', 'n', 'g', ' ', 3, 'T', 'i', ' ', 3, 'L', 'i', ' ', 4, 'S', 'h', 'e', ' ', 4, 'H', 'o', 'u', ' ', 5, 'T', 'i', 'n', 'g', ' ', 4, 'Z', 'u', 'i', ' ', 4, 'C', 'u', 'o', ' ', 4, 'F', 'e', 'i', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 3, 'C', 'e', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'L', 'i', ' ', 4, 'J', 'u', 'e', ' ', 4, 'S', 'h', 'a', ' ', 5, 'D', 'i', 'a', 'n', ' ', 4, 'C', 'h', 'u', ' ', 4, 'J', 'i', 'u', ' ', 4, 'Q', 'i', 'n', ' ', 3, 'A', 'o', ' ', 4, 'G', 'u', 'i', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'S', 'i', ' ', 3, 'L', 'i', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 4, 'L', 'a', 'n', ' ', 3, 'L', 'i', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 3, 'S', 'i', ' ', 5, 'G', 'o', 'n', 'g', ' ', 4, 'L', 'i', 'n', ' ', 4, 'Q', 'i', 'u', ' ', 3, 'Q', 'u', ' ', 3, 'Q', 'u', ' ', 3, 'U', 'k', ' ', 4, 'L', 'e', 'i', ' ', 3, 'D', 'u', ' ', 5, 'X', 'i', 'a', 'n', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 4, 'S', 'a', 'n', ' ', 4, 'C', 'a', 'n', ' ', 4, 'C', 'a', 'n', ' ', 4, 'C', 'a', 'n', ' ', 4, 'C', 'a', 'n', ' ', 3, 'A', 'i', ' ', 4, 'D', 'a', 'i', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'C', 'h', 'a', ' ', 3, 'J', 'i', ' ', 4, 'Y', 'o', 'u', ' ', 7, 'S', 'h', 'u', 'a', 'n', 'g', ' ', 4, 'F', 'a', 'n', ' ', 5, 'S', 'h', 'o', 'u', ' ', 5, 'G', 'u', 'a', 'i', ' ', 3, 'B', 'a', ' ', 3, 'F', 'a', ' ', 4, 'R', 'u', 'o', ' ', 4, 'S', 'h', 'i', ' ', 4, 'S', 'h', 'u', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 3, 'Q', 'u', ' ', 5, 'S', 'h', 'o', 'u', ' ', 5, 'B', 'i', 'a', 'n', ' ', 3, 'X', 'u', ' ', 4, 'J', 'i', 'a', ' ', 4, 'P', 'a', 'n', ' ', 4, 'S', 'o', 'u', ' ', 4, 'G', 'a', 'o', ' ', 4, 'W', 'e', 'i', ' ', 4, 'S', 'o', 'u', ' ', 4, 'D', 'i', 'e', ' ', 4, 'R', 'u', 'i', ' ', 5, 'C', 'o', 'n', 'g', ' ', 4, 'K', 'o', 'u', ' ', 3, 'G', 'u', ' ', 3, 'J', 'u', ' ', 5, 'L', 'i', 'n', 'g', ' ', 4, 'G', 'u', 'a', ' ', 4, 'T', 'a', 'o', ' ', 4, 'K', 'o', 'u', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'J', 'i', 'a', 'o', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 3, 'B', 'a', ' ', 5, 'D', 'i', 'n', 'g', ' ', 3, 'K', 'e', ' ', 4, 'T', 'a', 'i', ' ', 4, 'C', 'h', 'i', ' ', 4, 'S', 'h', 'i', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'Q', 'i', 'u', ' ', 3, 'P', 'o', ' ', 4, 'X', 'i', 'e', ' ', 4, 'H', 'a', 'o', ' ', 3, 'S', 'i', ' ', 4, 'T', 'a', 'n', ' ', 4, 'C', 'h', 'i', ' ', 3, 'L', 'e', ' ', 5, 'D', 'i', 'a', 'o', ' ', 3, 'J', 'i', ' ', 5, 'H', 'o', 'n', 'g', ' ', 4, 'M', 'i', 'e', ' ', 3, 'X', 'u', ' ', 5, 'M', 'a', 'n', 'g', ' ', 4, 'C', 'h', 'i', ' ', 3, 'G', 'e', ' ', 5, 'X', 'u', 'a', 'n', ' ', 4, 'Y', 'a', 'o', ' ', 3, 'Z', 'i', ' ', 3, 'H', 'e', ' ', 3, 'J', 'i', ' ', 5, 'D', 'i', 'a', 'o', ' ', 4, 'C', 'u', 'n', ' ', 5, 'T', 'o', 'n', 'g', ' ', 5, 'M', 'i', 'n', 'g', ' ', 4, 'H', 'o', 'u', ' ', 3, 'L', 'i', ' ', 3, 'T', 'u', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 4, 'Z', 'h', 'a', ' ', 4, 'X', 'i', 'a', ' ', 3, 'Y', 'e', ' ', 3, 'L', 'u', ' ', 2, 'A', ' ', 3, 'M', 'a', ' ', 3, 'O', 'u', ' ', 4, 'X', 'u', 'e', ' ', 3, 'Y', 'i', ' ', 4, 'J', 'u', 'n', ' ', 5, 'C', 'h', 'o', 'u', ' ', 4, 'L', 'i', 'n', ' ', 4, 'T', 'u', 'n', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'F', 'e', 'i', ' ', 3, 'B', 'i', ' ', 4, 'Q', 'i', 'n', ' ', 4, 'Q', 'i', 'n', ' ', 4, 'J', 'i', 'e', ' ', 3, 'B', 'u', ' ', 4, 'F', 'o', 'u', ' ', 3, 'B', 'a', ' ', 4, 'D', 'u', 'n', ' ', 4, 'F', 'e', 'n', ' ', 2, 'E', ' ', 4, 'H', 'a', 'n', ' ', 5, 'T', 'i', 'n', 'g', ' ', 5, 'H', 'a', 'n', 'g', ' ', 5, 'S', 'h', 'u', 'n', ' ', 3, 'Q', 'i', ' ', 5, 'H', 'o', 'n', 'g', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'S', 'h', 'e', 'n', ' ', 3, 'W', 'u', ' ', 3, 'W', 'u', ' ', 5, 'C', 'h', 'a', 'o', ' ', 3, 'N', 'e', ' ', 4, 'X', 'u', 'e', ' ', 3, 'X', 'i', ' ', 5, 'C', 'h', 'u', 'i', ' ', 4, 'D', 'o', 'u', ' ', 4, 'W', 'e', 'n', ' ', 4, 'H', 'o', 'u', ' ', 3, 'O', 'u', ' ', 3, 'W', 'u', ' ', 4, 'G', 'a', 'o', ' ', 3, 'Y', 'a', ' ', 4, 'J', 'u', 'n', ' ', 3, 'L', 'u', ' ', 2, 'E', ' ', 3, 'G', 'e', ' ', 4, 'M', 'e', 'i', ' ', 3, 'A', 'i', ' ', 3, 'Q', 'i', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 3, 'W', 'u', ' ', 4, 'G', 'a', 'o', ' ', 3, 'F', 'u', ' ', 5, 'J', 'i', 'a', 'o', ' ', 5, 'H', 'o', 'n', 'g', ' ', 4, 'C', 'h', 'i', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 3, 'N', 'e', ' ', 4, 'T', 'u', 'n', ' ', 3, 'F', 'u', ' ', 3, 'Y', 'i', ' ', 4, 'D', 'a', 'i', ' ', 3, 'O', 'u', ' ', 3, 'L', 'i', ' ', 4, 'B', 'a', 'i', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 5, 'K', 'u', 'a', 'i', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 3, 'W', 'u', ' ', 2, 'E', ' ', 4, 'S', 'h', 'i', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 4, 'P', 'e', 'n', ' ', 4, 'W', 'e', 'n', ' ', 3, 'N', 'i', ' ', 2, 'M', ' ', 5, 'L', 'i', 'n', 'g', ' ', 4, 'R', 'a', 'n', ' ', 4, 'Y', 'o', 'u', ' ', 3, 'D', 'i', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 4, 'S', 'h', 'i', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 4, 'T', 'i', 'e', ' ', 3, 'X', 'i', ' ', 3, 'Y', 'i', ' ', 3, 'Q', 'i', ' ', 5, 'P', 'i', 'n', 'g', ' ', 3, 'Z', 'i', ' ', 3, 'G', 'u', ' ', 3, 'Z', 'i', ' ', 4, 'W', 'e', 'i', ' ', 3, 'X', 'u', ' ', 3, 'H', 'e', ' ', 4, 'N', 'a', 'o', ' ', 4, 'X', 'i', 'a', ' ', 4, 'P', 'e', 'i', ' ', 3, 'Y', 'i', ' ', 5, 'X', 'i', 'a', 'o', ' ', 5, 'S', 'h', 'e', 'n', ' ', 3, 'H', 'u', ' ', 5, 'M', 'i', 'n', 'g', ' ', 3, 'D', 'a', ' ', 3, 'Q', 'u', ' ', 3, 'J', 'u', ' ', 4, 'G', 'e', 'm', ' ', 3, 'Z', 'a', ' ', 4, 'T', 'u', 'o', ' ', 4, 'D', 'u', 'o', ' ', 4, 'P', 'o', 'u', ' ', 4, 'P', 'a', 'o', ' ', 3, 'B', 'i', ' ', 3, 'F', 'u', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 3, 'H', 'e', ' ', 4, 'Z', 'h', 'a', ' ', 3, 'H', 'e', ' ', 4, 'H', 'a', 'i', ' ', 4, 'J', 'i', 'u', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 3, 'F', 'u', ' ', 4, 'Q', 'u', 'e', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 3, 'W', 'a', ' ', 3, 'K', 'a', ' ', 3, 'G', 'u', ' ', 3, 'K', 'a', ' ', 4, 'Z', 'u', 'o', ' ', 3, 'B', 'u', ' ', 5, 'L', 'o', 'n', 'g', ' ', 5, 'D', 'o', 'n', 'g', ' ', 5, 'N', 'i', 'n', 'g', ' ', 4, 'T', 'h', 'a', ' ', 3, 'S', 'i', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'H', 'u', 'o', ' ', 3, 'Q', 'i', ' ', 3, 'E', 'r', ' ', 2, 'E', ' ', 6, 'G', 'u', 'a', 'n', 'g', ' ', 4, 'Z', 'h', 'a', ' ', 3, 'X', 'i', ' ', 3, 'Y', 'i', ' ', 4, 'L', 'i', 'e', ' ', 3, 'Z', 'i', ' ', 4, 'M', 'i', 'e', ' ', 3, 'M', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'Y', 'a', 'o', ' ', 3, 'J', 'i', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 3, 'G', 'e', ' ', 6, 'S', 'h', 'u', 'a', 'i', ' ', 4, 'Z', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'o', ' ', 3, 'K', 'e', ' ', 4, 'H', 'u', 'i', ' ', 4, 'K', 'u', 'a', ' ', 5, 'H', 'u', 'a', 'i', ' ', 4, 'T', 'a', 'o', ' ', 5, 'X', 'i', 'a', 'n', ' ', 2, 'E', ' ', 5, 'X', 'u', 'a', 'n', ' ', 4, 'X', 'i', 'u', ' ', 4, 'W', 'a', 'i', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'L', 'a', 'o', ' ', 3, 'Y', 'i', ' ', 3, 'A', 'i', ' ', 4, 'P', 'i', 'n', ' ', 5, 'S', 'h', 'e', 'n', ' ', 5, 'T', 'o', 'n', 'g', ' ', 5, 'H', 'o', 'n', 'g', ' ', 6, 'X', 'i', 'o', 'n', 'g', ' ', 4, 'C', 'h', 'i', ' ', 3, 'W', 'a', ' ', 3, 'H', 'a', ' ', 4, 'Z', 'a', 'i', ' ', 3, 'Y', 'u', ' ', 3, 'D', 'i', ' ', 4, 'P', 'a', 'i', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 3, 'A', 'i', ' ', 4, 'H', 'e', 'n', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 3, 'Y', 'a', ' ', 3, 'D', 'a', ' ', 5, 'X', 'i', 'a', 'o', ' ', 3, 'B', 'i', ' ', 4, 'Y', 'u', 'e', ' ', 4, 'H', 'u', 'a', ' ', 6, 'S', 'a', 's', 'o', 'u', ' ', 5, 'K', 'u', 'a', 'i', ' ', 4, 'D', 'u', 'o', ' ', 3, 'J', 'i', ' ', 5, 'N', 'o', 'n', 'g', ' ', 4, 'M', 'o', 'u', ' ', 3, 'Y', 'o', ' ', 4, 'H', 'a', 'o', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 5, 'L', 'o', 'n', 'g', ' ', 4, 'P', 'o', 'u', ' ', 5, 'M', 'a', 'n', 'g', ' ', 3, 'G', 'e', ' ', 2, 'E', ' ', 4, 'C', 'h', 'i', ' ', 5, 'S', 'h', 'a', 'o', ' ', 3, 'L', 'i', ' ', 3, 'N', 'a', ' ', 3, 'Z', 'u', ' ', 3, 'H', 'e', ' ', 3, 'K', 'u', ' ', 5, 'X', 'i', 'a', 'o', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'L', 'a', 'o', ' ', 3, 'B', 'o', ' ', 4, 'Z', 'h', 'e', ' ', 4, 'Z', 'h', 'a', ' ', 6, 'L', 'i', 'a', 'n', 'g', ' ', 3, 'B', 'a', ' ', 4, 'M', 'i', 'e', ' ', 3, 'L', 'e', ' ', 4, 'S', 'u', 'i', ' ', 4, 'F', 'o', 'u', ' ', 3, 'B', 'u', ' ', 4, 'H', 'a', 'n', ' ', 5, 'H', 'e', 'n', 'g', ' ', 5, 'G', 'e', 'n', 'g', ' ', 5, 'S', 'h', 'u', 'o', ' ', 3, 'G', 'e', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'G', 'u', ' ', 3, 'G', 'u', ' ', 4, 'B', 'a', 'i', ' ', 4, 'H', 'a', 'n', ' ', 4, 'S', 'u', 'o', ' ', 5, 'C', 'h', 'u', 'n', ' ', 3, 'Y', 'i', ' ', 3, 'A', 'i', ' ', 4, 'J', 'i', 'a', ' ', 3, 'T', 'u', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'H', 'u', 'a', 'n', ' ', 3, 'L', 'i', ' ', 3, 'X', 'i', ' ', 5, 'T', 'a', 'n', 'g', ' ', 4, 'Z', 'u', 'o', ' ', 4, 'Q', 'i', 'u', ' ', 4, 'C', 'h', 'e', ' ', 3, 'W', 'u', ' ', 4, 'Z', 'a', 'o', ' ', 3, 'Y', 'a', ' ', 4, 'D', 'o', 'u', ' ', 3, 'Q', 'i', ' ', 3, 'D', 'i', ' ', 4, 'Q', 'i', 'n', ' ', 3, 'M', 'a', ' ', 4, 'M', 'a', 'l', ' ', 5, 'H', 'o', 'n', 'g', ' ', 4, 'D', 'o', 'u', ' ', 4, 'K', 'e', 's', ' ', 4, 'L', 'a', 'o', ' ', 6, 'L', 'i', 'a', 'n', 'g', ' ', 4, 'S', 'u', 'o', ' ', 4, 'Z', 'a', 'o', ' ', 5, 'H', 'u', 'a', 'n', ' ', 5, 'L', 'a', 'n', 'g', ' ', 4, 'S', 'h', 'a', ' ', 3, 'J', 'i', ' ', 4, 'Z', 'u', 'o', ' ', 3, 'W', 'o', ' ', 5, 'F', 'e', 'n', 'g', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'H', 'u', ' ', 3, 'Q', 'i', ' ', 5, 'S', 'h', 'o', 'u', ' ', 4, 'W', 'e', 'i', ' ', 5, 'S', 'h', 'u', 'a', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 3, 'E', 'r', ' ', 3, 'L', 'i', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 3, 'A', 'n', ' ', 4, 'J', 'i', 'e', ' ', 3, 'Y', 'o', ' ', 5, 'N', 'i', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 5, 'T', 'i', 'a', 'n', ' ', 4, 'L', 'a', 'i', ' ', 4, 'S', 'h', 'a', ' ', 3, 'X', 'i', ' ', 4, 'T', 'u', 'o', ' ', 3, 'H', 'u', ' ', 3, 'A', 'i', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 4, 'N', 'o', 'u', ' ', 4, 'K', 'e', 'n', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 6, 'S', 'h', 'a', 'n', 'g', ' ', 3, 'D', 'i', ' ', 5, 'H', 'e', 'n', 'g', ' ', 4, 'L', 'a', 'n', ' ', 2, 'A', ' ', 5, 'X', 'i', 'a', 'o', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 4, 'T', 'u', 'n', ' ', 3, 'W', 'u', ' ', 4, 'W', 'e', 'n', ' ', 4, 'C', 'u', 'i', ' ', 4, 'S', 'h', 'a', ' ', 3, 'H', 'u', ' ', 3, 'Q', 'i', ' ', 3, 'Q', 'i', ' ', 4, 'T', 'a', 'o', ' ', 4, 'D', 'a', 'n', ' ', 4, 'D', 'a', 'n', ' ', 3, 'Y', 'e', ' ', 3, 'Z', 'i', ' ', 3, 'B', 'i', ' ', 4, 'C', 'u', 'i', ' ', 5, 'C', 'h', 'u', 'o', ' ', 3, 'H', 'e', ' ', 3, 'Y', 'a', ' ', 3, 'Q', 'i', ' ', 4, 'Z', 'h', 'e', ' ', 4, 'P', 'e', 'i', ' ', 6, 'L', 'i', 'a', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'P', 'i', ' ', 4, 'S', 'h', 'a', ' ', 3, 'L', 'a', ' ', 3, 'Z', 'e', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 4, 'G', 'u', 'a', ' ', 3, 'P', 'a', ' ', 4, 'Z', 'h', 'e', ' ', 3, 'S', 'e', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 4, 'N', 'i', 'e', ' ', 4, 'G', 'u', 'o', ' ', 4, 'L', 'u', 'o', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'D', 'i', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 4, 'T', 'a', 'n', ' ', 3, 'B', 'o', ' ', 5, 'D', 'i', 'n', 'g', ' ', 5, 'L', 'a', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'o', ' ', 5, 'T', 'a', 'n', 'g', ' ', 4, 'C', 'h', 'i', ' ', 3, 'T', 'i', ' ', 3, 'A', 'n', ' ', 4, 'J', 'i', 'u', ' ', 4, 'D', 'a', 'n', ' ', 3, 'K', 'e', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 4, 'W', 'e', 'i', ' ', 4, 'N', 'a', 'n', ' ', 5, 'S', 'h', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 4, 'Z', 'h', 'e', ' ', 3, 'L', 'a', ' ', 4, 'J', 'i', 'e', ' ', 4, 'H', 'o', 'u', ' ', 4, 'H', 'a', 'n', ' ', 4, 'D', 'i', 'e', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 5, 'C', 'h', 'a', 'i', ' ', 4, 'W', 'a', 'i', ' ', 3, 'R', 'e', ' ', 3, 'Y', 'u', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'Z', 'a', 'n', ' ', 4, 'Y', 'a', 'o', ' ', 3, 'W', 'o', ' ', 5, 'M', 'i', 'a', 'n', ' ', 3, 'H', 'u', ' ', 4, 'Y', 'u', 'n', ' ', 6, 'C', 'h', 'u', 'a', 'n', ' ', 4, 'H', 'u', 'i', ' ', 5, 'H', 'u', 'a', 'n', ' ', 5, 'H', 'u', 'a', 'n', ' ', 3, 'X', 'i', ' ', 3, 'H', 'e', ' ', 3, 'J', 'i', ' ', 4, 'K', 'u', 'i', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 4, 'W', 'e', 'i', ' ', 4, 'S', 'h', 'a', ' ', 3, 'X', 'u', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 3, 'D', 'u', ' ', 4, 'N', 'i', 'e', ' ', 5, 'X', 'u', 'a', 'n', ' ', 6, 'L', 'i', 'a', 'n', 'g', ' ', 3, 'Y', 'u', ' ', 5, 'S', 'a', 'n', 'g', ' ', 4, 'C', 'h', 'i', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'D', 'a', 'n', ' ', 4, 'P', 'e', 'n', ' ', 4, 'C', 'a', 'n', ' ', 3, 'L', 'i', ' ', 3, 'Y', 'o', ' ', 4, 'Z', 'h', 'a', ' ', 4, 'W', 'e', 'i', ' ', 5, 'M', 'i', 'a', 'o', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'P', 'e', 'n', ' ', 5, 'P', 'h', 'o', 's', ' ', 4, 'K', 'u', 'i', ' ', 3, 'X', 'i', ' ', 3, 'Y', 'u', ' ', 4, 'J', 'i', 'e', ' ', 4, 'L', 'o', 'u', ' ', 3, 'K', 'u', ' ', 4, 'S', 'a', 'o', ' ', 4, 'H', 'u', 'o', ' ', 3, 'T', 'i', ' ', 4, 'Y', 'a', 'o', ' ', 3, 'H', 'e', ' ', 2, 'A', ' ', 4, 'X', 'i', 'u', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 3, 'S', 'e', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 3, 'S', 'u', ' ', 5, 'H', 'o', 'n', 'g', ' ', 4, 'X', 'i', 'e', ' ', 3, 'Y', 'i', ' ', 4, 'S', 'u', 'o', ' ', 3, 'M', 'a', ' ', 4, 'C', 'h', 'a', ' ', 4, 'H', 'a', 'i', ' ', 3, 'K', 'e', ' ', 3, 'T', 'a', ' ', 5, 'S', 'a', 'n', 'g', ' ', 5, 'T', 'i', 'a', 'n', ' ', 3, 'R', 'u', ' ', 4, 'S', 'o', 'u', ' ', 3, 'W', 'a', ' ', 3, 'J', 'i', ' ', 5, 'P', 'a', 'n', 'g', ' ', 3, 'W', 'u', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'S', 'h', 'i', ' ', 3, 'G', 'e', ' ', 3, 'Z', 'i', ' ', 4, 'J', 'i', 'e', ' ', 4, 'L', 'u', 'o', ' ', 5, 'W', 'e', 'n', 'g', ' ', 3, 'W', 'a', ' ', 3, 'S', 'i', ' ', 4, 'C', 'h', 'i', ' ', 4, 'H', 'a', 'o', ' ', 4, 'S', 'u', 'o', ' ', 4, 'J', 'i', 'a', ' ', 4, 'H', 'a', 'i', ' ', 4, 'S', 'u', 'o', ' ', 4, 'Q', 'i', 'n', ' ', 4, 'N', 'i', 'e', ' ', 3, 'H', 'e', ' ', 4, 'C', 'i', 's', ' ', 4, 'S', 'a', 'i', ' ', 3, 'N', 'g', ' ', 3, 'G', 'e', ' ', 3, 'N', 'a', ' ', 4, 'D', 'i', 'a', ' ', 3, 'A', 'i', ' ', 5, 'T', 'o', 'n', 'g', ' ', 3, 'B', 'i', ' ', 3, 'A', 'o', ' ', 3, 'A', 'o', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'C', 'u', 'i', ' ', 4, 'Z', 'h', 'e', ' ', 3, 'M', 'o', ' ', 4, 'S', 'o', 'u', ' ', 4, 'S', 'o', 'u', ' ', 4, 'T', 'a', 'n', ' ', 3, 'D', 'i', ' ', 3, 'Q', 'i', ' ', 5, 'J', 'i', 'a', 'o', ' ', 6, 'C', 'h', 'o', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'K', 'a', 'i', ' ', 4, 'T', 'a', 'n', ' ', 4, 'S', 'a', 'n', ' ', 4, 'C', 'a', 'o', ' ', 4, 'J', 'i', 'a', ' ', 3, 'A', 'i', ' ', 5, 'X', 'i', 'a', 'o', ' ', 5, 'P', 'i', 'a', 'o', ' ', 4, 'L', 'o', 'u', ' ', 3, 'G', 'a', ' ', 3, 'G', 'u', ' ', 5, 'X', 'i', 'a', 'o', ' ', 3, 'H', 'u', ' ', 4, 'H', 'u', 'i', ' ', 4, 'G', 'u', 'o', ' ', 3, 'O', 'u', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'Z', 'e', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 3, 'X', 'u', ' ', 3, 'P', 'o', ' ', 3, 'D', 'e', ' ', 3, 'M', 'a', ' ', 3, 'M', 'a', ' ', 3, 'H', 'u', ' ', 4, 'L', 'e', 'i', ' ', 3, 'D', 'u', ' ', 3, 'G', 'a', ' ', 5, 'T', 'a', 'n', 'g', ' ', 3, 'Y', 'e', ' ', 5, 'B', 'e', 'n', 'g', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 5, 'S', 'a', 'a', 'i', ' ', 5, 'J', 'i', 'a', 'o', ' ', 3, 'M', 'i', ' ', 5, 'X', 'i', 'a', 'o', ' ', 4, 'H', 'u', 'a', ' ', 4, 'M', 'a', 'i', ' ', 4, 'R', 'a', 'n', ' ', 4, 'Z', 'u', 'o', ' ', 5, 'P', 'e', 'n', 'g', ' ', 4, 'L', 'a', 'o', ' ', 5, 'X', 'i', 'a', 'o', ' ', 3, 'J', 'i', ' ', 4, 'Z', 'h', 'u', ' ', 5, 'C', 'h', 'a', 'o', ' ', 4, 'K', 'u', 'i', ' ', 4, 'Z', 'u', 'i', ' ', 5, 'X', 'i', 'a', 'o', ' ', 3, 'S', 'i', ' ', 4, 'H', 'a', 'o', ' ', 3, 'F', 'u', ' ', 5, 'L', 'i', 'a', 'o', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 3, 'X', 'i', ' ', 4, 'X', 'i', 'u', ' ', 4, 'T', 'a', 'n', ' ', 4, 'T', 'a', 'n', ' ', 3, 'M', 'o', ' ', 4, 'X', 'u', 'n', ' ', 2, 'E', ' ', 4, 'Z', 'u', 'n', ' ', 4, 'F', 'a', 'n', ' ', 4, 'C', 'h', 'i', ' ', 4, 'H', 'u', 'i', ' ', 4, 'Z', 'a', 'n', ' ', 7, 'C', 'h', 'u', 'a', 'n', 'g', ' ', 3, 'C', 'u', ' ', 4, 'D', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 4, 'T', 'u', 'n', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'o', ' ', 3, 'Y', 'e', ' ', 3, 'X', 'i', ' ', 3, 'Q', 'i', ' ', 4, 'H', 'a', 'o', ' ', 5, 'L', 'i', 'a', 'n', ' ', 3, 'X', 'u', ' ', 5, 'D', 'e', 'n', 'g', ' ', 4, 'H', 'u', 'i', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'P', 'u', ' ', 4, 'J', 'u', 'e', ' ', 4, 'Q', 'i', 'n', ' ', 4, 'X', 'u', 'n', ' ', 4, 'N', 'i', 'e', ' ', 3, 'L', 'u', ' ', 3, 'S', 'i', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 3, 'D', 'a', ' ', 4, 'D', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 4, 'J', 'i', 'n', ' ', 5, 'N', 'o', 'n', 'g', ' ', 4, 'Y', 'u', 'e', ' ', 4, 'H', 'u', 'i', ' ', 3, 'Q', 'i', ' ', 2, 'E', ' ', 4, 'Z', 'a', 'o', ' ', 3, 'Y', 'i', ' ', 4, 'S', 'h', 'i', ' ', 5, 'J', 'i', 'a', 'o', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 3, 'A', 'i', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 4, 'J', 'u', 'e', ' ', 5, 'K', 'u', 'a', 'i', ' ', 3, 'Y', 'u', ' ', 4, 'P', 'e', 'n', ' ', 4, 'D', 'a', 'o', ' ', 3, 'G', 'e', ' ', 4, 'X', 'i', 'n', ' ', 4, 'D', 'u', 'n', ' ', 5, 'D', 'a', 'n', 'g', ' ', 4, 'S', 'i', 'n', ' ', 4, 'S', 'a', 'i', ' ', 3, 'P', 'i', ' ', 3, 'P', 'i', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'Z', 'u', 'i', ' ', 5, 'N', 'i', 'n', 'g', ' ', 3, 'D', 'i', ' ', 4, 'L', 'a', 'n', ' ', 3, 'T', 'a', ' ', 4, 'H', 'u', 'o', ' ', 3, 'R', 'u', ' ', 4, 'H', 'a', 'o', ' ', 4, 'X', 'i', 'a', ' ', 3, 'Y', 'a', ' ', 4, 'D', 'u', 'o', ' ', 3, 'X', 'i', ' ', 5, 'C', 'h', 'o', 'u', ' ', 3, 'J', 'i', ' ', 4, 'J', 'i', 'n', ' ', 4, 'H', 'a', 'o', ' ', 3, 'T', 'i', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 3, 'C', 'a', ' ', 3, 'T', 'i', ' ', 3, 'L', 'u', ' ', 4, 'H', 'u', 'i', ' ', 3, 'B', 'o', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'N', 'i', 'e', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'H', 'u', ' ', 3, 'M', 'o', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 4, 'Z', 'h', 'e', ' ', 3, 'L', 'i', ' ', 4, 'L', 'i', 'u', ' ', 5, 'H', 'a', 'a', 'i', ' ', 5, 'N', 'a', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'o', ' ', 3, 'M', 'o', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'L', 'i', ' ', 3, 'L', 'u', ' ', 5, 'L', 'o', 'n', 'g', ' ', 3, 'F', 'u', ' ', 4, 'D', 'a', 'n', ' ', 5, 'C', 'h', 'e', 'n', ' ', 4, 'P', 'i', 'n', ' ', 3, 'P', 'i', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 4, 'H', 'u', 'o', ' ', 3, 'M', 'o', ' ', 3, 'X', 'i', ' ', 4, 'D', 'u', 'o', ' ', 3, 'K', 'u', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'C', 'h', 'a', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 5, 'R', 'a', 'n', 'g', ' ', 5, 'D', 'i', 'a', 'n', ' ', 3, 'L', 'a', ' ', 3, 'T', 'a', ' ', 5, 'X', 'i', 'a', 'o', ' ', 5, 'J', 'i', 'a', 'o', ' ', 5, 'C', 'h', 'u', 'o', ' ', 5, 'H', 'u', 'a', 'n', ' ', 4, 'H', 'u', 'o', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 4, 'N', 'i', 'e', ' ', 5, 'X', 'i', 'a', 'o', ' ', 3, 'C', 'a', ' ', 3, 'L', 'i', ' ', 5, 'C', 'h', 'a', 'n', ' ', 5, 'C', 'h', 'a', 'i', ' ', 3, 'L', 'i', ' ', 3, 'Y', 'i', ' ', 4, 'L', 'u', 'o', ' ', 5, 'N', 'a', 'n', 'g', ' ', 4, 'Z', 'a', 'n', ' ', 3, 'S', 'u', ' ', 3, 'X', 'i', ' ', 3, 'S', 'o', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'Z', 'a', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'L', 'a', 'n', ' ', 4, 'N', 'i', 'e', ' ', 5, 'N', 'a', 'n', 'g', ' ', 4, 'W', 'e', 'i', ' ', 4, 'H', 'u', 'i', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'Q', 'i', 'u', ' ', 3, 'S', 'i', ' ', 4, 'N', 'i', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'H', 'u', 'i', ' ', 4, 'X', 'i', 'n', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'N', 'a', 'n', ' ', 5, 'T', 'u', 'a', 'n', ' ', 5, 'T', 'u', 'a', 'n', ' ', 4, 'D', 'u', 'n', ' ', 5, 'K', 'a', 'n', 'g', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 6, 'J', 'i', 'o', 'n', 'g', ' ', 5, 'P', 'i', 'a', 'n', ' ', 4, 'Y', 'u', 'n', ' ', 5, 'C', 'o', 'n', 'g', ' ', 3, 'H', 'u', ' ', 4, 'H', 'u', 'i', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'G', 'u', 'o', ' ', 4, 'K', 'u', 'n', ' ', 5, 'C', 'o', 'n', 'g', ' ', 4, 'W', 'e', 'i', ' ', 3, 'T', 'u', ' ', 4, 'W', 'e', 'i', ' ', 4, 'L', 'u', 'n', ' ', 4, 'G', 'u', 'o', ' ', 4, 'Q', 'u', 'n', ' ', 3, 'R', 'i', ' ', 5, 'L', 'i', 'n', 'g', ' ', 3, 'G', 'u', ' ', 4, 'G', 'u', 'o', ' ', 4, 'T', 'a', 'i', ' ', 4, 'G', 'u', 'o', ' ', 3, 'T', 'u', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'G', 'u', 'o', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'H', 'u', 'n', ' ', 3, 'P', 'u', ' ', 3, 'Y', 'u', ' ', 4, 'H', 'a', 'n', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'L', 'u', 'n', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 4, 'G', 'u', 'o', ' ', 6, 'C', 'h', 'u', 'a', 'n', ' ', 4, 'W', 'e', 'i', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 3, 'K', 'u', ' ', 3, 'F', 'u', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 2, 'E', ' ', 3, 'T', 'u', ' ', 3, 'T', 'u', ' ', 3, 'T', 'u', ' ', 5, 'T', 'u', 'a', 'n', ' ', 4, 'L', 'u', 'e', ' ', 4, 'H', 'u', 'i', ' ', 3, 'Y', 'i', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 5, 'L', 'u', 'a', 'n', ' ', 5, 'L', 'u', 'a', 'n', ' ', 3, 'T', 'u', ' ', 3, 'Y', 'a', ' ', 3, 'T', 'u', ' ', 5, 'T', 'i', 'n', 'g', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 3, 'P', 'u', ' ', 3, 'L', 'u', ' ', 4, 'I', 'r', 'i', ' ', 3, 'Y', 'a', ' ', 4, 'Z', 'a', 'i', ' ', 4, 'W', 'e', 'i', ' ', 3, 'G', 'e', ' ', 3, 'Y', 'u', ' ', 3, 'W', 'u', ' ', 4, 'G', 'u', 'i', ' ', 3, 'P', 'i', ' ', 3, 'Y', 'i', ' ', 3, 'D', 'i', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 5, 'D', 'a', 'n', 'g', ' ', 4, 'Q', 'i', 'a', ' ', 7, 'A', 'k', 'u', 't', 's', 'u', ' ', 5, 'Y', 'a', 'm', 'a', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 3, 'Q', 'i', ' ', 4, 'N', 'i', 'e', ' ', 3, 'M', 'o', ' ', 3, 'J', 'i', ' ', 4, 'J', 'i', 'a', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'B', 'a', 'n', ' ', 4, 'X', 'u', 'n', ' ', 4, 'T', 'o', 'u', ' ', 4, 'Q', 'i', 'n', ' ', 4, 'F', 'e', 'n', ' ', 4, 'J', 'u', 'n', ' ', 5, 'K', 'e', 'n', 'g', ' ', 4, 'T', 'u', 'n', ' ', 5, 'F', 'a', 'n', 'g', ' ', 4, 'F', 'e', 'n', ' ', 4, 'B', 'e', 'n', ' ', 4, 'T', 'a', 'n', ' ', 4, 'K', 'a', 'n', ' ', 3, 'P', 'i', ' ', 4, 'Z', 'u', 'o', ' ', 5, 'K', 'e', 'n', 'g', ' ', 3, 'B', 'i', ' ', 5, 'X', 'i', 'n', 'g', ' ', 3, 'D', 'i', ' ', 5, 'J', 'i', 'n', 'g', ' ', 3, 'J', 'i', ' ', 5, 'K', 'u', 'a', 'i', ' ', 3, 'D', 'i', ' ', 5, 'J', 'i', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'T', 'a', 'n', ' ', 3, 'L', 'i', ' ', 3, 'B', 'a', ' ', 3, 'W', 'u', ' ', 4, 'F', 'e', 'n', ' ', 5, 'Z', 'h', 'u', 'i', ' ', 3, 'P', 'o', ' ', 4, 'P', 'a', 'n', ' ', 5, 'T', 'a', 'n', 'g', ' ', 4, 'K', 'u', 'n', ' ', 3, 'Q', 'u', ' ', 4, 'T', 'a', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'T', 'u', 'o', ' ', 4, 'G', 'a', 'n', ' ', 5, 'P', 'i', 'n', 'g', ' ', 5, 'D', 'i', 'a', 'n', ' ', 4, 'G', 'u', 'a', ' ', 3, 'N', 'i', ' ', 4, 'T', 'a', 'i', ' ', 3, 'P', 'i', ' ', 6, 'J', 'i', 'o', 'n', 'g', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 3, 'F', 'o', ' ', 3, 'A', 'o', ' ', 4, 'L', 'i', 'u', ' ', 4, 'Q', 'i', 'u', ' ', 3, 'M', 'u', ' ', 3, 'K', 'e', ' ', 4, 'G', 'o', 'u', ' ', 4, 'X', 'u', 'e', ' ', 3, 'B', 'a', ' ', 4, 'C', 'h', 'i', ' ', 4, 'C', 'h', 'e', ' ', 5, 'L', 'i', 'n', 'g', ' ', 4, 'Z', 'h', 'u', ' ', 3, 'F', 'u', ' ', 3, 'H', 'u', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'C', 'h', 'u', 'i', ' ', 3, 'L', 'a', ' ', 5, 'L', 'o', 'n', 'g', ' ', 5, 'L', 'o', 'n', 'g', ' ', 3, 'L', 'u', ' ', 3, 'A', 'o', ' ', 4, 'T', 'a', 'y', ' ', 4, 'P', 'a', 'o', ' ', 5, 'X', 'i', 'n', 'g', ' ', 5, 'D', 'o', 'n', 'g', ' ', 3, 'J', 'i', ' ', 3, 'K', 'e', ' ', 3, 'L', 'u', ' ', 3, 'C', 'i', ' ', 4, 'C', 'h', 'i', ' ', 4, 'L', 'e', 'i', ' ', 4, 'G', 'a', 'i', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'H', 'o', 'u', ' ', 4, 'D', 'u', 'i', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 3, 'F', 'u', ' ', 6, 'G', 'u', 'a', 'n', 'g', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'D', 'u', 'o', ' ', 4, 'D', 'u', 'o', ' ', 4, 'G', 'u', 'i', ' ', 4, 'C', 'h', 'a', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'F', 'a', ' ', 4, 'G', 'o', 'u', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'D', 'i', 'e', ' ', 4, 'X', 'i', 'e', ' ', 4, 'K', 'e', 'n', ' ', 6, 'J', 'i', 'o', 'n', 'g', ' ', 5, 'S', 'h', 'o', 'u', ' ', 2, 'E', ' ', 3, 'H', 'a', ' ', 5, 'D', 'i', 'a', 'n', ' ', 5, 'H', 'o', 'n', 'g', ' ', 3, 'W', 'u', ' ', 4, 'K', 'u', 'a', ' ', 4, 'T', 'a', 'o', ' ', 5, 'D', 'a', 'n', 'g', ' ', 4, 'K', 'a', 'i', ' ', 5, 'G', 'a', 'k', 'e', ' ', 4, 'N', 'a', 'o', ' ', 3, 'A', 'n', ' ', 5, 'X', 'i', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'H', 'u', 'a', 'n', ' ', 5, 'B', 'a', 'n', 'g', ' ', 4, 'P', 'e', 'i', ' ', 3, 'B', 'a', ' ', 3, 'Y', 'i', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'H', 'a', 'n', ' ', 3, 'X', 'u', ' ', 5, 'C', 'h', 'u', 'i', ' ', 4, 'C', 'e', 'n', ' ', 5, 'G', 'e', 'n', 'g', ' ', 3, 'A', 'i', ' ', 5, 'P', 'e', 'n', 'g', ' ', 5, 'F', 'a', 'n', 'g', ' ', 4, 'Q', 'u', 'e', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 4, 'X', 'u', 'n', ' ', 4, 'J', 'i', 'a', ' ', 3, 'D', 'i', ' ', 4, 'M', 'a', 'i', ' ', 5, 'L', 'a', 'n', 'g', ' ', 5, 'X', 'u', 'a', 'n', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'J', 'i', 'n', ' ', 4, 'Z', 'h', 'e', ' ', 4, 'L', 'e', 'i', ' ', 4, 'L', 'i', 'e', ' ', 3, 'B', 'u', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 5, 'G', 'o', 'm', 'i', ' ', 3, 'B', 'u', ' ', 4, 'S', 'h', 'i', ' ', 4, 'X', 'u', 'n', ' ', 4, 'G', 'u', 'o', ' ', 6, 'J', 'i', 'o', 'n', 'g', ' ', 3, 'Y', 'e', ' ', 5, 'N', 'i', 'a', 'n', ' ', 3, 'D', 'i', ' ', 3, 'Y', 'u', ' ', 3, 'B', 'u', ' ', 3, 'Y', 'a', ' ', 5, 'J', 'u', 'a', 'n', ' ', 4, 'S', 'u', 'i', ' ', 3, 'P', 'i', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 4, 'W', 'a', 'n', ' ', 3, 'J', 'u', ' ', 4, 'L', 'u', 'n', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 5, 'K', 'o', 'n', 'g', ' ', 6, 'C', 'h', 'o', 'n', 'g', ' ', 5, 'D', 'o', 'n', 'g', ' ', 4, 'D', 'a', 'i', ' ', 4, 'T', 'a', 'n', ' ', 3, 'A', 'n', ' ', 4, 'C', 'a', 'i', ' ', 4, 'S', 'h', 'u', ' ', 5, 'B', 'e', 'n', 'g', ' ', 4, 'K', 'a', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'D', 'u', 'o', ' ', 3, 'Y', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'Y', 'i', ' ', 4, 'P', 'e', 'i', ' ', 3, 'J', 'i', ' ', 5, 'Z', 'h', 'u', 'n', ' ', 3, 'Q', 'i', ' ', 4, 'S', 'a', 'o', ' ', 3, 'J', 'u', ' ', 3, 'N', 'i', ' ', 3, 'K', 'u', ' ', 3, 'K', 'e', ' ', 5, 'T', 'a', 'n', 'g', ' ', 4, 'K', 'u', 'n', ' ', 3, 'N', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'D', 'u', 'i', ' ', 4, 'J', 'i', 'n', ' ', 5, 'G', 'a', 'n', 'g', ' ', 3, 'Y', 'u', ' ', 2, 'E', ' ', 5, 'P', 'e', 'n', 'g', ' ', 3, 'G', 'u', ' ', 3, 'T', 'u', ' ', 5, 'L', 'e', 'n', 'g', ' ', 3, 'Y', 'a', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 3, 'A', 'n', ' ', 4, 'D', 'u', 'o', ' ', 4, 'N', 'a', 'o', ' ', 3, 'T', 'u', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'H', 'u', 'n', ' ', 3, 'B', 'i', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'G', 'u', 'o', ' ', 4, 'D', 'i', 'e', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 4, 'H', 'o', 'u', ' ', 4, 'B', 'a', 'o', ' ', 4, 'B', 'a', 'o', ' ', 3, 'Y', 'u', ' ', 3, 'D', 'i', ' ', 4, 'M', 'a', 'o', ' ', 4, 'J', 'i', 'e', ' ', 5, 'R', 'u', 'a', 'n', ' ', 2, 'E', ' ', 5, 'G', 'e', 'n', 'g', ' ', 4, 'K', 'a', 'n', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 3, 'Y', 'u', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 2, 'E', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'B', 'a', 'o', ' ', 3, 'J', 'i', ' ', 4, 'M', 'e', 'i', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 3, 'D', 'u', ' ', 4, 'T', 'u', 'o', ' ', 4, 'Y', 'i', 'n', ' ', 5, 'F', 'e', 'n', 'g', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 4, 'J', 'i', 'e', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 5, 'F', 'e', 'n', 'g', ' ', 5, 'G', 'a', 'n', 'g', ' ', 6, 'C', 'h', 'u', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 6, 'P', 'y', 'e', 'n', 'g', ' ', 7, 'T', 'o', 'r', 'i', 'd', 'e', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 5, 'L', 'e', 'n', 'g', ' ', 5, 'D', 'u', 'a', 'n', ' ', 5, 'X', 'u', 'a', 'n', ' ', 3, 'J', 'i', ' ', 3, 'J', 'i', ' ', 5, 'K', 'u', 'a', 'i', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 3, 'T', 'a', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 4, 'K', 'a', 'i', ' ', 3, 'S', 'u', ' ', 3, 'S', 'u', ' ', 4, 'S', 'h', 'i', ' ', 3, 'M', 'i', ' ', 3, 'T', 'a', ' ', 5, 'W', 'e', 'n', 'g', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 3, 'T', 'u', ' ', 5, 'T', 'a', 'n', 'g', ' ', 4, 'Q', 'u', 'e', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 3, 'L', 'i', ' ', 5, 'P', 'e', 'n', 'g', ' ', 5, 'B', 'a', 'n', 'g', ' ', 4, 'S', 'a', 'i', ' ', 5, 'Z', 'a', 'n', 'g', ' ', 4, 'D', 'u', 'i', ' ', 5, 'T', 'i', 'a', 'n', ' ', 3, 'W', 'u', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 4, 'X', 'u', 'n', ' ', 3, 'G', 'e', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 3, 'A', 'i', ' ', 5, 'G', 'o', 'n', 'g', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'K', 'a', 'n', ' ', 5, 'T', 'i', 'a', 'n', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'W', 'e', 'n', ' ', 4, 'X', 'i', 'e', ' ', 4, 'L', 'i', 'u', ' ', 4, 'A', 'm', 'a', ' ', 5, 'L', 'a', 'n', 'g', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 5, 'P', 'e', 'n', 'g', ' ', 5, 'B', 'e', 'n', 'g', ' ', 5, 'C', 'h', 'e', 'n', ' ', 3, 'C', 'u', ' ', 3, 'L', 'u', ' ', 3, 'O', 'u', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'M', 'e', 'i', ' ', 3, 'M', 'o', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 7, 'S', 'h', 'u', 'a', 'n', 'g', ' ', 4, 'S', 'h', 'u', ' ', 4, 'L', 'o', 'u', ' ', 4, 'C', 'h', 'i', ' ', 4, 'M', 'a', 'n', ' ', 5, 'B', 'i', 'a', 'o', ' ', 5, 'J', 'i', 'n', 'g', ' ', 3, 'Q', 'i', ' ', 4, 'S', 'h', 'u', ' ', 3, 'D', 'i', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 4, 'K', 'a', 'n', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 5, 'D', 'i', 'a', 'n', ' ', 5, 'C', 'h', 'e', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'X', 'i', ' ', 4, 'G', 'u', 'o', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 4, 'J', 'i', 'n', ' ', 3, 'D', 'i', ' ', 6, 'S', 'h', 'a', 'n', 'g', ' ', 3, 'M', 'u', ' ', 4, 'C', 'u', 'i', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'T', 'a', ' ', 5, 'Z', 'e', 'n', 'g', ' ', 3, 'Q', 'i', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 6, 'L', 'i', 'a', 'n', 'g', ' ', 5, 'Z', 'h', 'u', 'i', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 5, 'Z', 'e', 'n', 'g', ' ', 3, 'X', 'u', ' ', 5, 'S', 'h', 'a', 'n', ' ', 5, 'S', 'h', 'a', 'n', ' ', 3, 'B', 'a', ' ', 3, 'P', 'u', ' ', 5, 'K', 'u', 'a', 'i', ' ', 5, 'D', 'o', 'n', 'g', ' ', 4, 'F', 'a', 'n', ' ', 4, 'Q', 'u', 'e', ' ', 3, 'M', 'o', ' ', 4, 'D', 'u', 'n', ' ', 4, 'D', 'u', 'n', ' ', 4, 'D', 'u', 'n', ' ', 3, 'D', 'i', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 4, 'D', 'u', 'o', ' ', 4, 'D', 'u', 'o', ' ', 4, 'T', 'a', 'n', ' ', 5, 'D', 'e', 'n', 'g', ' ', 3, 'W', 'u', ' ', 4, 'F', 'e', 'n', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 4, 'T', 'a', 'n', ' ', 3, 'D', 'a', ' ', 3, 'Y', 'e', ' ', 4, 'S', 'h', 'o', ' ', 5, 'M', 'a', 'm', 'a', ' ', 3, 'Y', 'u', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 3, 'J', 'i', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 4, 'K', 'e', 'n', ' ', 3, 'Y', 'i', ' ', 3, 'P', 'i', ' ', 3, 'B', 'i', ' ', 5, 'D', 'i', 'a', 'n', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 3, 'Y', 'e', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 3, 'B', 'o', ' ', 4, 'T', 'a', 'n', ' ', 4, 'L', 'a', 'n', ' ', 3, 'J', 'u', ' ', 5, 'H', 'u', 'a', 'i', ' ', 5, 'D', 'a', 'n', 'g', ' ', 5, 'R', 'a', 'n', 'g', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'X', 'u', 'n', ' ', 4, 'L', 'a', 'n', ' ', 3, 'X', 'i', ' ', 3, 'H', 'e', ' ', 3, 'A', 'i', ' ', 3, 'Y', 'a', ' ', 4, 'D', 'a', 'o', ' ', 4, 'H', 'a', 'o', ' ', 5, 'R', 'u', 'a', 'n', ' ', 5, 'M', 'a', 'm', 'a', ' ', 4, 'L', 'e', 'i', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 3, 'L', 'u', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'T', 'a', 'n', ' ', 4, 'W', 'e', 'i', ' ', 5, 'H', 'u', 'a', 'i', ' ', 5, 'L', 'o', 'n', 'g', ' ', 5, 'L', 'o', 'n', 'g', ' ', 4, 'R', 'u', 'i', ' ', 3, 'L', 'i', ' ', 4, 'L', 'i', 'n', ' ', 5, 'R', 'a', 'n', 'g', ' ', 4, 'T', 'e', 'n', ' ', 4, 'X', 'u', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'L', 'e', 'i', ' ', 3, 'B', 'a', ' ', 4, 'S', 'h', 'i', ' ', 4, 'R', 'e', 'n', ' ', 7, 'Z', 'h', 'u', 'a', 'n', 'g', ' ', 7, 'Z', 'h', 'u', 'a', 'n', 'g', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 4, 'M', 'a', 'i', ' ', 3, 'K', 'e', ' ', 4, 'Z', 'h', 'u', ' ', 7, 'Z', 'h', 'u', 'a', 'n', 'g', ' ', 3, 'H', 'u', ' ', 3, 'H', 'u', ' ', 4, 'K', 'u', 'n', ' ', 3, 'Y', 'i', ' ', 3, 'H', 'u', ' ', 3, 'X', 'u', ' ', 4, 'K', 'u', 'n', ' ', 5, 'S', 'h', 'o', 'u', ' ', 5, 'M', 'a', 'n', 'g', ' ', 4, 'Z', 'u', 'n', ' ', 5, 'S', 'h', 'o', 'u', ' ', 3, 'Y', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'G', 'u', ' ', 4, 'C', 'h', 'u', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 5, 'F', 'e', 'n', 'g', ' ', 4, 'B', 'e', 'i', ' ', 4, 'C', 'a', 'y', ' ', 5, 'B', 'i', 'a', 'n', ' ', 4, 'S', 'u', 'i', ' ', 4, 'Q', 'u', 'n', ' ', 5, 'L', 'i', 'n', 'g', ' ', 3, 'F', 'u', ' ', 4, 'Z', 'u', 'o', ' ', 4, 'X', 'i', 'a', ' ', 6, 'X', 'i', 'o', 'n', 'g', ' ', 4, 'N', 'a', 'o', ' ', 4, 'X', 'i', 'a', ' ', 4, 'K', 'u', 'i', ' ', 3, 'X', 'i', ' ', 4, 'W', 'a', 'i', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'M', 'a', 'o', ' ', 3, 'S', 'u', ' ', 4, 'D', 'u', 'o', ' ', 4, 'D', 'u', 'o', ' ', 3, 'Y', 'e', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 4, 'U', 'y', 's', ' ', 4, 'G', 'o', 'u', ' ', 4, 'G', 'o', 'u', ' ', 3, 'Q', 'i', ' ', 5, 'M', 'e', 'n', 'g', ' ', 5, 'M', 'e', 'n', 'g', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'H', 'u', 'o', ' ', 5, 'C', 'h', 'e', 'n', ' ', 3, 'D', 'a', ' ', 3, 'Z', 'e', ' ', 5, 'T', 'i', 'a', 'n', ' ', 4, 'T', 'a', 'i', ' ', 3, 'F', 'u', ' ', 5, 'G', 'u', 'a', 'i', ' ', 4, 'Y', 'a', 'o', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 5, 'H', 'a', 'n', 'g', ' ', 4, 'G', 'a', 'o', ' ', 4, 'S', 'h', 'i', ' ', 4, 'B', 'e', 'n', ' ', 4, 'T', 'a', 'i', ' ', 4, 'T', 'o', 'u', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'B', 'i', ' ', 3, 'Y', 'i', ' ', 4, 'K', 'u', 'a', ' ', 4, 'J', 'i', 'a', ' ', 4, 'D', 'u', 'o', ' ', 4, 'K', 'w', 'u', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 4, 'Y', 'u', 'n', ' ', 4, 'J', 'i', 'a', ' ', 3, 'P', 'a', ' ', 3, 'E', 'n', ' ', 5, 'L', 'i', 'a', 'n', ' ', 5, 'H', 'u', 'a', 'n', ' ', 3, 'D', 'i', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'P', 'a', 'o', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 3, 'Q', 'i', ' ', 4, 'N', 'a', 'i', ' ', 5, 'F', 'e', 'n', 'g', ' ', 4, 'X', 'i', 'e', ' ', 4, 'F', 'e', 'n', ' ', 5, 'D', 'i', 'a', 'n', ' ', 4, 'K', 'u', 'i', ' ', 4, 'Z', 'o', 'u', ' ', 5, 'H', 'u', 'a', 'n', ' ', 3, 'Q', 'i', ' ', 4, 'K', 'a', 'i', ' ', 4, 'Z', 'h', 'a', ' ', 4, 'B', 'e', 'n', ' ', 3, 'Y', 'i', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 4, 'T', 'a', 'o', ' ', 5, 'Z', 'a', 'n', 'g', ' ', 4, 'B', 'e', 'n', ' ', 3, 'X', 'i', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 4, 'F', 'e', 'i', ' ', 5, 'D', 'i', 'a', 'o', ' ', 4, 'X', 'u', 'n', ' ', 5, 'K', 'e', 'n', 'g', ' ', 5, 'D', 'i', 'a', 'n', ' ', 3, 'A', 'o', ' ', 4, 'S', 'h', 'e', ' ', 5, 'W', 'e', 'n', 'g', ' ', 4, 'P', 'a', 'n', ' ', 3, 'A', 'o', ' ', 3, 'W', 'u', ' ', 3, 'A', 'o', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'D', 'u', 'o', ' ', 4, 'Y', 'u', 'n', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 4, 'S', 'h', 'i', ' ', 4, 'F', 'e', 'n', ' ', 4, 'H', 'u', 'o', ' ', 3, 'B', 'i', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'D', 'u', 'o', ' ', 3, 'N', 'u', ' ', 3, 'N', 'u', ' ', 5, 'D', 'i', 'n', 'g', ' ', 4, 'N', 'a', 'i', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'T', 'a', ' ', 4, 'J', 'i', 'u', ' ', 4, 'N', 'a', 'n', ' ', 4, 'C', 'h', 'a', ' ', 4, 'H', 'a', 'o', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'F', 'a', 'n', ' ', 3, 'J', 'i', ' ', 5, 'S', 'h', 'u', 'o', ' ', 3, 'R', 'u', ' ', 4, 'F', 'e', 'i', ' ', 5, 'W', 'a', 'n', 'g', ' ', 5, 'H', 'o', 'n', 'g', ' ', 7, 'Z', 'h', 'u', 'a', 'n', 'g', ' ', 3, 'F', 'u', ' ', 3, 'M', 'a', ' ', 4, 'D', 'a', 'n', ' ', 4, 'R', 'e', 'n', ' ', 3, 'F', 'u', ' ', 5, 'J', 'i', 'n', 'g', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'X', 'i', 'e', ' ', 4, 'W', 'e', 'n', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 3, 'P', 'a', ' ', 3, 'D', 'u', ' ', 3, 'J', 'i', ' ', 5, 'K', 'e', 'n', 'g', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'J', 'i', 'n', ' ', 4, 'Y', 'u', 'n', ' ', 5, 'M', 'i', 'a', 'o', ' ', 4, 'P', 'e', 'i', ' ', 4, 'S', 'h', 'i', ' ', 4, 'Y', 'u', 'e', ' ', 7, 'Z', 'h', 'u', 'a', 'n', 'g', ' ', 4, 'N', 'i', 'u', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'N', 'a', ' ', 4, 'X', 'i', 'n', ' ', 4, 'F', 'e', 'n', ' ', 3, 'B', 'i', ' ', 3, 'Y', 'u', ' ', 4, 'T', 'u', 'o', ' ', 5, 'F', 'e', 'n', 'g', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 5, 'F', 'a', 'n', 'g', ' ', 3, 'W', 'u', ' ', 3, 'Y', 'u', ' ', 4, 'G', 'u', 'i', ' ', 3, 'D', 'u', ' ', 3, 'B', 'a', ' ', 3, 'N', 'i', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 3, 'D', 'a', ' ', 4, 'N', 'a', 'i', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'T', 'o', 'u', ' ', 5, 'X', 'u', 'a', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 2, 'E', ' ', 4, 'M', 'e', 'i', ' ', 3, 'M', 'o', ' ', 3, 'Q', 'i', ' ', 3, 'B', 'i', ' ', 5, 'S', 'h', 'e', 'n', ' ', 4, 'Q', 'i', 'e', ' ', 2, 'E', ' ', 3, 'H', 'e', ' ', 3, 'X', 'u', ' ', 3, 'F', 'a', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 4, 'M', 'i', 'n', ' ', 4, 'B', 'a', 'n', ' ', 3, 'M', 'u', ' ', 3, 'F', 'u', ' ', 5, 'L', 'i', 'n', 'g', ' ', 3, 'Z', 'i', ' ', 3, 'Z', 'i', ' ', 4, 'S', 'h', 'i', ' ', 4, 'R', 'a', 'n', ' ', 5, 'S', 'h', 'a', 'n', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 4, 'M', 'a', 'n', ' ', 4, 'J', 'i', 'e', ' ', 3, 'G', 'u', ' ', 3, 'S', 'i', ' ', 5, 'X', 'i', 'n', 'g', ' ', 4, 'W', 'e', 'i', ' ', 3, 'Z', 'i', ' ', 3, 'J', 'u', ' ', 5, 'S', 'h', 'a', 'n', ' ', 4, 'P', 'i', 'n', ' ', 4, 'R', 'e', 'n', ' ', 4, 'Y', 'a', 'o', ' ', 5, 'T', 'o', 'n', 'g', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 4, 'S', 'h', 'u', ' ', 3, 'J', 'i', ' ', 4, 'G', 'a', 'i', ' ', 6, 'S', 'h', 'a', 'n', 'g', ' ', 4, 'K', 'u', 'o', ' ', 5, 'J', 'u', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'G', 'o', 'u', ' ', 3, 'M', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 5, 'N', 'i', 'a', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'J', 'i', ' ', 3, 'J', 'i', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'H', 'e', 'n', 'g', ' ', 6, 'G', 'u', 'a', 'n', 'g', ' ', 4, 'J', 'u', 'n', ' ', 4, 'K', 'u', 'a', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'M', 'i', 'n', 'g', ' ', 4, 'L', 'i', 'e', ' ', 4, 'P', 'e', 'i', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'C', 'h', 'a', ' ', 5, 'S', 'h', 'e', 'n', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'C', 'h', 'i', ' ', 4, 'G', 'u', 'i', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 3, 'Z', 'i', ' ', 5, 'S', 'o', 'n', 'g', ' ', 4, 'W', 'e', 'i', ' ', 5, 'H', 'o', 'n', 'g', ' ', 3, 'W', 'a', ' ', 4, 'L', 'o', 'u', ' ', 3, 'Y', 'a', ' ', 4, 'R', 'a', 'o', ' ', 5, 'J', 'i', 'a', 'o', ' ', 5, 'L', 'u', 'a', 'n', ' ', 5, 'P', 'i', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'S', 'h', 'a', 'o', ' ', 3, 'L', 'i', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'o', ' ', 5, 'M', 'a', 'n', 'g', ' ', 3, 'F', 'u', ' ', 4, 'S', 'u', 'o', ' ', 3, 'W', 'u', ' ', 4, 'W', 'e', 'i', ' ', 3, 'K', 'e', ' ', 4, 'L', 'a', 'i', ' ', 5, 'C', 'h', 'u', 'o', ' ', 5, 'D', 'i', 'n', 'g', ' ', 6, 'N', 'i', 'a', 'n', 'g', ' ', 5, 'X', 'i', 'n', 'g', ' ', 4, 'N', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 4, 'N', 'u', 'o', ' ', 4, 'P', 'e', 'i', ' ', 4, 'N', 'e', 'i', ' ', 5, 'J', 'u', 'a', 'n', ' ', 5, 'S', 'h', 'e', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'H', 'a', 'n', ' ', 3, 'D', 'i', ' ', 7, 'Z', 'h', 'u', 'a', 'n', 'g', ' ', 2, 'E', ' ', 4, 'P', 'i', 'n', ' ', 4, 'T', 'u', 'i', ' ', 4, 'H', 'a', 'n', ' ', 5, 'M', 'i', 'a', 'n', ' ', 3, 'W', 'u', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'W', 'u', ' ', 3, 'X', 'i', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 3, 'S', 'i', ' ', 3, 'Y', 'u', ' ', 3, 'W', 'a', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'J', 'u', ' ', 3, 'Q', 'u', ' ', 5, 'S', 'h', 'u', 'i', ' ', 3, 'Q', 'i', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'Z', 'h', 'u', 'i', ' ', 5, 'D', 'o', 'n', 'g', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 3, 'L', 'u', ' ', 3, 'A', 'i', ' ', 2, 'E', ' ', 2, 'E', ' ', 4, 'L', 'o', 'u', ' ', 5, 'M', 'i', 'a', 'n', ' ', 5, 'C', 'o', 'n', 'g', ' ', 4, 'P', 'o', 'u', ' ', 3, 'J', 'u', ' ', 3, 'P', 'o', ' ', 4, 'C', 'a', 'i', ' ', 5, 'D', 'i', 'n', 'g', ' ', 4, 'W', 'a', 'n', ' ', 5, 'B', 'i', 'a', 'o', ' ', 5, 'X', 'i', 'a', 'o', ' ', 4, 'S', 'h', 'u', ' ', 3, 'Q', 'i', ' ', 4, 'H', 'u', 'i', ' ', 3, 'F', 'u', ' ', 2, 'E', ' ', 3, 'W', 'o', ' ', 4, 'T', 'a', 'n', ' ', 4, 'F', 'e', 'i', ' ', 4, 'W', 'e', 'i', ' ', 4, 'J', 'i', 'e', ' ', 5, 'T', 'i', 'a', 'n', ' ', 3, 'N', 'i', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 5, 'J', 'i', 'n', 'g', ' ', 4, 'H', 'u', 'n', ' ', 5, 'J', 'i', 'n', 'g', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'D', 'i', 'a', 'n', ' ', 5, 'X', 'i', 'n', 'g', ' ', 3, 'H', 'u', ' ', 3, 'W', 'a', ' ', 4, 'L', 'a', 'i', ' ', 3, 'B', 'i', ' ', 4, 'Y', 'i', 'n', ' ', 5, 'C', 'h', 'o', 'u', ' ', 5, 'C', 'h', 'u', 'o', ' ', 3, 'F', 'u', ' ', 5, 'J', 'i', 'n', 'g', ' ', 4, 'L', 'u', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'L', 'a', 'n', ' ', 4, 'K', 'u', 'n', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'Y', 'a', ' ', 3, 'J', 'u', ' ', 3, 'L', 'i', ' ', 5, 'D', 'i', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'H', 'w', 'a', ' ', 4, 'H', 'u', 'a', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 5, 'C', 'h', 'a', 'n', ' ', 5, 'S', 'h', 'e', 'n', ' ', 5, 'T', 'i', 'n', 'g', ' ', 5, 'D', 'a', 'n', 'g', ' ', 4, 'Y', 'a', 'o', ' ', 3, 'W', 'u', ' ', 4, 'N', 'a', 'n', ' ', 4, 'R', 'u', 'o', ' ', 4, 'J', 'i', 'a', ' ', 4, 'T', 'o', 'u', ' ', 3, 'X', 'u', ' ', 3, 'Y', 'u', ' ', 4, 'W', 'e', 'i', ' ', 3, 'T', 'i', ' ', 4, 'R', 'o', 'u', ' ', 4, 'M', 'e', 'i', ' ', 4, 'D', 'a', 'n', ' ', 5, 'R', 'u', 'a', 'n', ' ', 4, 'Q', 'i', 'n', ' ', 4, 'H', 'u', 'i', ' ', 3, 'W', 'u', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'C', 'h', 'u', 'n', ' ', 4, 'M', 'a', 'o', ' ', 3, 'F', 'u', ' ', 4, 'J', 'i', 'e', ' ', 5, 'D', 'u', 'a', 'n', ' ', 3, 'X', 'i', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 4, 'M', 'e', 'i', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 5, 'M', 'i', 'a', 'n', ' ', 3, 'A', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 5, 'X', 'u', 'a', 'n', ' ', 4, 'J', 'i', 'e', ' ', 4, 'W', 'e', 'i', ' ', 4, 'M', 'e', 'i', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 4, 'Q', 'i', 'u', ' ', 3, 'T', 'i', ' ', 4, 'X', 'i', 'e', ' ', 4, 'T', 'u', 'o', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'M', 'a', 'o', ' ', 4, 'R', 'a', 'n', ' ', 3, 'S', 'i', ' ', 5, 'P', 'i', 'a', 'n', ' ', 4, 'W', 'e', 'i', ' ', 3, 'W', 'a', ' ', 4, 'J', 'i', 'u', ' ', 3, 'H', 'u', ' ', 3, 'A', 'o', ' ', 4, 'B', 'o', 'u', ' ', 3, 'X', 'u', ' ', 4, 'T', 'o', 'u', ' ', 4, 'G', 'u', 'i', ' ', 4, 'Z', 'o', 'u', ' ', 4, 'Y', 'a', 'o', ' ', 3, 'P', 'i', ' ', 3, 'X', 'i', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 5, 'R', 'o', 'n', 'g', ' ', 3, 'R', 'u', ' ', 4, 'C', 'h', 'i', ' ', 4, 'L', 'i', 'u', ' ', 4, 'M', 'e', 'i', ' ', 4, 'P', 'a', 'n', ' ', 3, 'A', 'o', ' ', 3, 'M', 'a', ' ', 4, 'G', 'o', 'u', ' ', 4, 'K', 'u', 'i', ' ', 4, 'Q', 'i', 'n', ' ', 4, 'J', 'i', 'a', ' ', 4, 'S', 'a', 'o', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'C', 'h', 'a', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 5, 'M', 'i', 'n', 'g', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 3, 'J', 'i', ' ', 3, 'S', 'u', ' ', 5, 'N', 'i', 'a', 'o', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'T', 'a', 'o', ' ', 5, 'P', 'a', 'n', 'g', ' ', 5, 'L', 'a', 'n', 'g', ' ', 4, 'N', 'a', 'o', ' ', 4, 'B', 'a', 'o', ' ', 3, 'A', 'i', ' ', 3, 'P', 'i', ' ', 4, 'P', 'i', 'n', ' ', 3, 'Y', 'i', ' ', 5, 'P', 'i', 'a', 'o', ' ', 3, 'Y', 'u', ' ', 4, 'L', 'e', 'i', ' ', 5, 'X', 'u', 'a', 'n', ' ', 4, 'M', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 5, 'K', 'a', 'n', 'g', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 3, 'N', 'i', ' ', 3, 'L', 'i', ' ', 3, 'D', 'i', ' ', 4, 'G', 'u', 'i', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'J', 'i', 'n', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 3, 'C', 'e', ' ', 4, 'H', 'a', 'n', ' ', 4, 'N', 'e', 'n', ' ', 4, 'L', 'a', 'o', ' ', 3, 'M', 'o', ' ', 4, 'Z', 'h', 'e', ' ', 3, 'H', 'u', ' ', 3, 'H', 'u', ' ', 3, 'A', 'o', ' ', 4, 'N', 'e', 'n', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 3, 'M', 'a', ' ', 4, 'P', 'i', 'e', ' ', 3, 'G', 'u', ' ', 3, 'W', 'u', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'T', 'u', 'o', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 4, 'M', 'a', 'o', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'M', 'o', ' ', 5, 'L', 'i', 'a', 'o', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'H', 'u', 'a', ' ', 4, 'G', 'u', 'i', ' ', 5, 'D', 'e', 'n', 'g', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'X', 'u', ' ', 3, 'Y', 'i', ' ', 4, 'H', 'u', 'a', ' ', 3, 'X', 'i', ' ', 4, 'H', 'u', 'i', ' ', 4, 'R', 'a', 'o', ' ', 3, 'X', 'i', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'C', 'h', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'M', 'e', 'i', ' ', 4, 'F', 'a', 'n', ' ', 4, 'F', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 4, 'W', 'e', 'i', ' ', 5, 'J', 'i', 'a', 'o', ' ', 3, 'F', 'u', ' ', 4, 'S', 'h', 'i', ' ', 3, 'B', 'i', ' ', 5, 'S', 'h', 'a', 'n', ' ', 4, 'S', 'u', 'i', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 5, 'L', 'i', 'a', 'n', ' ', 5, 'H', 'u', 'a', 'n', ' ', 4, 'X', 'i', 'n', ' ', 5, 'N', 'i', 'a', 'o', ' ', 5, 'D', 'o', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 4, 'C', 'a', 'n', ' ', 3, 'A', 'i', ' ', 6, 'N', 'i', 'a', 'n', 'g', ' ', 5, 'N', 'e', 'n', 'g', ' ', 3, 'M', 'a', ' ', 5, 'T', 'i', 'a', 'o', ' ', 5, 'C', 'h', 'o', 'u', ' ', 4, 'J', 'i', 'n', ' ', 3, 'C', 'i', ' ', 3, 'Y', 'u', ' ', 4, 'P', 'i', 'n', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 3, 'X', 'u', ' ', 4, 'N', 'a', 'i', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'T', 'a', 'i', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'C', 'a', 'n', ' ', 5, 'N', 'i', 'a', 'o', ' ', 3, 'W', 'o', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 5, 'M', 'i', 'a', 'n', ' ', 5, 'K', 'a', 'k', 'a', ' ', 3, 'M', 'a', ' ', 5, 'S', 'h', 'e', 'n', ' ', 5, 'X', 'i', 'n', 'g', ' ', 3, 'N', 'i', ' ', 3, 'D', 'u', ' ', 4, 'L', 'i', 'u', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'L', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 7, 'S', 'h', 'u', 'a', 'n', 'g', ' ', 5, 'L', 'i', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'o', ' ', 6, 'N', 'i', 'a', 'n', 'g', ' ', 4, 'L', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 7, 'S', 'h', 'u', 'a', 'n', 'g', ' ', 6, 'S', 'h', 'u', 'a', 'i', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 3, 'M', 'i', ' ', 3, 'L', 'i', ' ', 5, 'L', 'u', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'L', 'a', 'n', ' ', 3, 'Z', 'i', ' ', 4, 'J', 'i', 'e', ' ', 4, 'J', 'u', 'e', ' ', 4, 'J', 'u', 'e', ' ', 5, 'K', 'o', 'n', 'g', ' ', 4, 'Y', 'u', 'n', ' ', 3, 'Z', 'i', ' ', 3, 'Z', 'i', ' ', 4, 'C', 'u', 'n', ' ', 4, 'S', 'u', 'n', ' ', 3, 'F', 'u', ' ', 4, 'B', 'e', 'i', ' ', 3, 'Z', 'i', ' ', 5, 'X', 'i', 'a', 'o', ' ', 4, 'X', 'i', 'n', ' ', 5, 'M', 'e', 'n', 'g', ' ', 3, 'S', 'i', ' ', 4, 'T', 'a', 'i', ' ', 4, 'B', 'a', 'o', ' ', 3, 'J', 'i', ' ', 3, 'G', 'u', ' ', 3, 'N', 'u', ' ', 4, 'X', 'u', 'e', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 4, 'H', 'a', 'i', ' ', 5, 'L', 'u', 'a', 'n', ' ', 4, 'S', 'u', 'n', ' ', 5, 'H', 'u', 'a', 'i', ' ', 4, 'M', 'i', 'e', ' ', 5, 'C', 'o', 'n', 'g', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'S', 'h', 'u', ' ', 5, 'C', 'h', 'a', 'n', ' ', 3, 'Y', 'a', ' ', 3, 'Z', 'i', ' ', 3, 'N', 'i', ' ', 3, 'F', 'u', ' ', 3, 'Z', 'i', ' ', 3, 'L', 'i', ' ', 4, 'X', 'u', 'e', ' ', 3, 'B', 'o', ' ', 3, 'R', 'u', ' ', 4, 'L', 'a', 'i', ' ', 4, 'N', 'i', 'e', ' ', 4, 'N', 'i', 'e', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 5, 'L', 'u', 'a', 'n', ' ', 5, 'M', 'i', 'a', 'n', ' ', 4, 'Z', 'h', 'u', ' ', 5, 'R', 'o', 'n', 'g', ' ', 3, 'T', 'a', ' ', 4, 'G', 'u', 'i', ' ', 5, 'Z', 'h', 'a', 'i', ' ', 6, 'Q', 'i', 'o', 'n', 'g', ' ', 3, 'Y', 'u', ' ', 5, 'S', 'h', 'o', 'u', ' ', 3, 'A', 'n', ' ', 3, 'T', 'u', ' ', 5, 'S', 'o', 'n', 'g', ' ', 4, 'W', 'a', 'n', ' ', 4, 'R', 'o', 'u', ' ', 4, 'Y', 'a', 'o', ' ', 5, 'H', 'o', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 5, 'J', 'i', 'n', 'g', ' ', 5, 'Z', 'h', 'u', 'n', ' ', 3, 'M', 'i', ' ', 4, 'Z', 'h', 'u', ' ', 5, 'D', 'a', 'n', 'g', ' ', 5, 'H', 'o', 'n', 'g', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 5, 'G', 'u', 'a', 'n', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 5, 'D', 'i', 'n', 'g', ' ', 4, 'W', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 4, 'B', 'a', 'o', ' ', 4, 'S', 'h', 'i', ' ', 4, 'S', 'h', 'i', ' ', 6, 'C', 'h', 'o', 'n', 'g', ' ', 5, 'S', 'h', 'e', 'n', ' ', 3, 'K', 'e', ' ', 5, 'X', 'u', 'a', 'n', ' ', 4, 'S', 'h', 'i', ' ', 4, 'Y', 'o', 'u', ' ', 5, 'H', 'u', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 5, 'T', 'i', 'a', 'o', ' ', 4, 'S', 'h', 'i', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'G', 'o', 'n', 'g', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 4, 'Q', 'u', 'n', ' ', 5, 'G', 'o', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'o', ' ', 4, 'Z', 'a', 'i', ' ', 4, 'Z', 'h', 'a', ' ', 4, 'B', 'a', 'o', ' ', 4, 'H', 'a', 'i', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'o', ' ', 4, 'J', 'i', 'a', ' ', 5, 'S', 'h', 'e', 'n', ' ', 5, 'C', 'h', 'e', 'n', ' ', 5, 'R', 'o', 'n', 'g', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 3, 'M', 'i', ' ', 4, 'K', 'o', 'u', ' ', 5, 'K', 'u', 'a', 'n', ' ', 4, 'B', 'i', 'n', ' ', 3, 'S', 'u', ' ', 4, 'C', 'a', 'i', ' ', 4, 'Z', 'a', 'n', ' ', 3, 'J', 'i', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 3, 'J', 'i', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'M', 'i', ' ', 4, 'K', 'o', 'u', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 4, 'Q', 'u', 'e', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'F', 'u', ' ', 5, 'N', 'i', 'n', 'g', ' ', 5, 'B', 'i', 'n', 'g', ' ', 5, 'H', 'u', 'a', 'n', ' ', 4, 'M', 'e', 'i', ' ', 4, 'Q', 'i', 'n', ' ', 4, 'H', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 4, 'S', 'h', 'i', ' ', 5, 'N', 'i', 'n', 'g', ' ', 4, 'Q', 'i', 'n', ' ', 5, 'N', 'i', 'n', 'g', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'Y', 'u', ' ', 4, 'B', 'a', 'o', ' ', 5, 'K', 'u', 'a', 'n', ' ', 5, 'N', 'i', 'n', 'g', ' ', 4, 'Q', 'i', 'n', ' ', 3, 'M', 'o', ' ', 4, 'C', 'h', 'a', ' ', 3, 'J', 'u', ' ', 4, 'G', 'u', 'a', ' ', 4, 'Q', 'i', 'n', ' ', 3, 'H', 'u', ' ', 3, 'W', 'u', ' ', 5, 'L', 'i', 'a', 'o', ' ', 4, 'S', 'h', 'i', ' ', 4, 'Z', 'h', 'u', ' ', 5, 'Z', 'h', 'a', 'i', ' ', 5, 'S', 'h', 'e', 'n', ' ', 4, 'W', 'e', 'i', ' ', 4, 'X', 'i', 'e', ' ', 5, 'K', 'u', 'a', 'n', ' ', 4, 'H', 'u', 'i', ' ', 5, 'L', 'i', 'a', 'o', ' ', 4, 'J', 'u', 'n', ' ', 5, 'H', 'u', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 3, 'Y', 'i', ' ', 4, 'B', 'a', 'o', ' ', 4, 'Q', 'i', 'n', ' ', 6, 'C', 'h', 'o', 'n', 'g', ' ', 4, 'B', 'a', 'o', ' ', 5, 'F', 'e', 'n', 'g', ' ', 4, 'C', 'u', 'n', ' ', 4, 'D', 'u', 'i', ' ', 3, 'S', 'i', ' ', 4, 'X', 'u', 'n', ' ', 4, 'D', 'a', 'o', ' ', 3, 'L', 'u', ' ', 4, 'D', 'u', 'i', ' ', 5, 'S', 'h', 'o', 'u', ' ', 3, 'P', 'o', ' ', 5, 'F', 'e', 'n', 'g', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 3, 'F', 'u', ' ', 4, 'S', 'h', 'e', ' ', 3, 'K', 'e', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 4, 'W', 'e', 'i', ' ', 4, 'Z', 'u', 'n', ' ', 4, 'X', 'u', 'n', ' ', 4, 'S', 'h', 'u', ' ', 4, 'D', 'u', 'i', ' ', 4, 'D', 'a', 'o', ' ', 5, 'X', 'i', 'a', 'o', ' ', 3, 'J', 'i', ' ', 5, 'S', 'h', 'a', 'o', ' ', 3, 'E', 'r', ' ', 3, 'E', 'r', ' ', 3, 'E', 'r', ' ', 3, 'G', 'a', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'S', 'h', 'u', ' ', 5, 'C', 'h', 'e', 'n', ' ', 6, 'S', 'h', 'a', 'n', 'g', ' ', 6, 'S', 'h', 'a', 'n', 'g', ' ', 3, 'M', 'o', ' ', 3, 'G', 'a', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 5, 'L', 'i', 'a', 'o', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'W', 'a', 'n', 'g', ' ', 5, 'W', 'a', 'n', 'g', ' ', 4, 'Y', 'o', 'u', ' ', 5, 'L', 'i', 'a', 'o', ' ', 5, 'L', 'i', 'a', 'o', ' ', 4, 'Y', 'a', 'o', ' ', 5, 'M', 'a', 'n', 'g', ' ', 5, 'W', 'a', 'n', 'g', ' ', 5, 'W', 'a', 'n', 'g', ' ', 5, 'W', 'a', 'n', 'g', ' ', 3, 'G', 'a', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'D', 'u', 'o', ' ', 4, 'K', 'u', 'i', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 4, 'J', 'i', 'u', ' ', 4, 'G', 'a', 'n', ' ', 3, 'G', 'u', ' ', 4, 'G', 'a', 'n', ' ', 4, 'T', 'u', 'i', ' ', 4, 'G', 'a', 'n', ' ', 4, 'G', 'a', 'n', ' ', 4, 'S', 'h', 'i', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'C', 'h', 'i', ' ', 4, 'K', 'a', 'o', ' ', 3, 'N', 'i', ' ', 4, 'J', 'i', 'n', ' ', 4, 'W', 'e', 'i', ' ', 5, 'N', 'i', 'a', 'o', ' ', 3, 'J', 'u', ' ', 3, 'P', 'i', ' ', 5, 'C', 'e', 'n', 'g', ' ', 3, 'X', 'i', ' ', 3, 'B', 'i', ' ', 3, 'J', 'u', ' ', 4, 'J', 'i', 'e', ' ', 5, 'T', 'i', 'a', 'n', ' ', 3, 'Q', 'u', ' ', 3, 'T', 'i', ' ', 4, 'J', 'i', 'e', ' ', 3, 'W', 'u', ' ', 5, 'D', 'i', 'a', 'o', ' ', 4, 'S', 'h', 'i', ' ', 4, 'S', 'h', 'i', ' ', 5, 'P', 'i', 'n', 'g', ' ', 3, 'J', 'i', ' ', 4, 'X', 'i', 'e', ' ', 5, 'C', 'h', 'e', 'n', ' ', 3, 'X', 'i', ' ', 3, 'N', 'i', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 3, 'X', 'i', ' ', 4, 'M', 'a', 'n', ' ', 2, 'E', ' ', 4, 'L', 'o', 'u', ' ', 5, 'P', 'i', 'n', 'g', ' ', 3, 'T', 'i', ' ', 4, 'F', 'e', 'i', ' ', 4, 'S', 'h', 'u', ' ', 4, 'X', 'i', 'e', ' ', 3, 'T', 'u', ' ', 3, 'L', 'u', ' ', 3, 'L', 'u', ' ', 3, 'X', 'i', ' ', 5, 'C', 'e', 'n', 'g', ' ', 3, 'L', 'u', ' ', 3, 'J', 'u', ' ', 4, 'X', 'i', 'e', ' ', 3, 'J', 'u', ' ', 4, 'J', 'u', 'e', ' ', 5, 'L', 'i', 'a', 'o', ' ', 4, 'J', 'u', 'e', ' ', 4, 'S', 'h', 'u', ' ', 3, 'X', 'i', ' ', 4, 'C', 'h', 'e', ' ', 4, 'T', 'u', 'n', ' ', 3, 'N', 'i', ' ', 5, 'S', 'h', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'L', 'i', ' ', 4, 'X', 'u', 'e', ' ', 5, 'N', 'a', 't', 'a', ' ', 5, 'L', 'o', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 3, 'Q', 'i', ' ', 4, 'R', 'e', 'n', ' ', 3, 'W', 'u', ' ', 4, 'H', 'a', 'n', ' ', 5, 'S', 'h', 'e', 'n', ' ', 3, 'Y', 'u', ' ', 4, 'C', 'h', 'u', ' ', 4, 'S', 'u', 'i', ' ', 3, 'Q', 'i', ' ', 4, 'Y', 'u', 'e', ' ', 4, 'B', 'a', 'n', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'A', 'n', 'g', ' ', 3, 'Y', 'a', ' ', 3, 'W', 'u', ' ', 4, 'J', 'i', 'e', ' ', 2, 'E', ' ', 3, 'J', 'i', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'F', 'e', 'n', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 3, 'Q', 'i', ' ', 4, 'C', 'e', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 3, 'Q', 'i', ' ', 4, 'C', 'h', 'a', ' ', 4, 'J', 'i', 'e', ' ', 3, 'Q', 'u', ' ', 5, 'G', 'a', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'A', 'o', ' ', 4, 'L', 'a', 'n', ' ', 4, 'D', 'a', 'o', ' ', 3, 'B', 'a', ' ', 4, 'Z', 'u', 'o', ' ', 4, 'Z', 'u', 'o', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 3, 'J', 'u', ' ', 5, 'G', 'a', 'n', 'g', ' ', 3, 'K', 'e', ' ', 4, 'G', 'o', 'u', ' ', 4, 'X', 'u', 'e', ' ', 4, 'B', 'e', 'i', ' ', 3, 'L', 'i', ' ', 5, 'T', 'i', 'a', 'o', ' ', 3, 'J', 'u', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'F', 'u', ' ', 4, 'X', 'i', 'u', ' ', 4, 'J', 'i', 'a', ' ', 5, 'L', 'i', 'n', 'g', ' ', 4, 'T', 'u', 'o', ' ', 4, 'P', 'e', 'i', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'D', 'a', 'i', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 4, 'Y', 'u', 'e', ' ', 3, 'Q', 'u', ' ', 3, 'H', 'u', ' ', 3, 'P', 'o', ' ', 4, 'M', 'i', 'n', ' ', 3, 'A', 'n', ' ', 5, 'T', 'i', 'a', 'o', ' ', 5, 'L', 'i', 'n', 'g', ' ', 4, 'C', 'h', 'i', ' ', 5, 'Y', 'u', 'r', 'i', ' ', 5, 'D', 'o', 'n', 'g', ' ', 4, 'C', 'e', 'm', ' ', 4, 'K', 'u', 'i', ' ', 4, 'X', 'i', 'u', ' ', 4, 'M', 'a', 'o', ' ', 5, 'T', 'o', 'n', 'g', ' ', 4, 'X', 'u', 'e', ' ', 3, 'Y', 'i', ' ', 5, 'K', 'u', 'r', 'a', ' ', 3, 'H', 'e', ' ', 3, 'K', 'e', ' ', 4, 'L', 'u', 'o', ' ', 2, 'E', ' ', 3, 'F', 'u', ' ', 4, 'X', 'u', 'n', ' ', 4, 'D', 'i', 'e', ' ', 3, 'L', 'u', ' ', 3, 'A', 'n', ' ', 3, 'E', 'r', ' ', 4, 'G', 'a', 'i', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 5, 'T', 'o', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 3, 'M', 'u', ' ', 4, 'S', 'h', 'i', ' ', 3, 'A', 'n', ' ', 4, 'W', 'e', 'i', ' ', 3, 'H', 'u', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'M', 'i', ' ', 3, 'L', 'i', ' ', 3, 'J', 'i', ' ', 5, 'T', 'o', 'n', 'g', ' ', 4, 'W', 'e', 'i', ' ', 4, 'Y', 'o', 'u', ' ', 5, 'S', 'a', 'n', 'g', ' ', 4, 'X', 'i', 'a', ' ', 3, 'L', 'i', ' ', 4, 'Y', 'a', 'o', ' ', 5, 'J', 'i', 'a', 'o', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 5, 'L', 'u', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'o', ' ', 2, 'E', ' ', 2, 'E', ' ', 3, 'Y', 'u', ' ', 3, 'Y', 'e', ' ', 3, 'B', 'u', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 4, 'Q', 'u', 'n', ' ', 5, 'F', 'e', 'n', 'g', ' ', 5, 'F', 'e', 'n', 'g', ' ', 4, 'N', 'a', 'o', ' ', 3, 'L', 'i', ' ', 4, 'Y', 'o', 'u', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'H', 'o', 'n', 'g', ' ', 4, 'D', 'a', 'o', ' ', 5, 'S', 'h', 'e', 'n', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 3, 'T', 'u', ' ', 5, 'G', 'e', 'n', 'g', ' ', 4, 'J', 'u', 'n', ' ', 4, 'H', 'a', 'o', ' ', 4, 'X', 'i', 'a', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'Y', 'u', ' ', 5, 'L', 'a', 'n', 'g', ' ', 4, 'K', 'a', 'n', ' ', 4, 'L', 'a', 'o', ' ', 4, 'L', 'a', 'i', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'Q', 'u', 'e', ' ', 5, 'K', 'o', 'n', 'g', ' ', 6, 'C', 'h', 'o', 'n', 'g', ' ', 6, 'C', 'h', 'o', 'n', 'g', ' ', 3, 'T', 'a', ' ', 4, 'L', 'i', 'n', ' ', 4, 'H', 'u', 'a', ' ', 3, 'J', 'u', ' ', 4, 'L', 'a', 'i', ' ', 3, 'Q', 'i', ' ', 4, 'M', 'i', 'n', ' ', 4, 'K', 'u', 'n', ' ', 4, 'K', 'u', 'n', ' ', 3, 'Z', 'u', ' ', 3, 'G', 'u', ' ', 4, 'C', 'u', 'i', ' ', 3, 'Y', 'a', ' ', 3, 'Y', 'a', ' ', 5, 'G', 'a', 'n', 'g', ' ', 4, 'L', 'u', 'n', ' ', 4, 'L', 'u', 'n', ' ', 5, 'L', 'e', 'n', 'g', ' ', 4, 'J', 'u', 'e', ' ', 4, 'D', 'u', 'o', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 4, 'G', 'u', 'o', ' ', 4, 'Y', 'i', 'n', ' ', 5, 'D', 'o', 'n', 'g', ' ', 4, 'H', 'a', 'n', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 4, 'W', 'e', 'i', ' ', 4, 'Y', 'a', 'o', ' ', 3, 'P', 'i', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'S', 'o', 'n', 'g', ' ', 4, 'J', 'i', 'e', ' ', 5, 'B', 'e', 'n', 'g', ' ', 3, 'Z', 'u', ' ', 4, 'J', 'u', 'e', ' ', 5, 'D', 'o', 'n', 'g', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 3, 'G', 'u', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'Z', 'e', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 3, 'Y', 'u', ' ', 4, 'W', 'e', 'i', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 5, 'F', 'e', 'n', 'g', ' ', 4, 'Q', 'i', 'u', ' ', 4, 'D', 'u', 'n', ' ', 3, 'T', 'i', ' ', 3, 'Y', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'S', 'h', 'i', ' ', 4, 'Z', 'a', 'i', ' ', 4, 'Y', 'a', 'o', ' ', 2, 'E', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'K', 'a', 'n', ' ', 3, 'L', 'u', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'M', 'e', 'i', ' ', 4, 'G', 'a', 'n', ' ', 3, 'J', 'i', ' ', 3, 'J', 'i', ' ', 5, 'H', 'u', 'a', 'n', ' ', 5, 'T', 'i', 'n', 'g', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 4, 'M', 'e', 'i', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 3, 'W', 'u', ' ', 3, 'Y', 'u', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 4, 'L', 'a', 'n', ' ', 4, 'J', 'u', 'e', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'W', 'e', 'i', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 4, 'C', 'h', 'a', ' ', 4, 'S', 'u', 'i', ' ', 5, 'R', 'o', 'n', 'g', ' ', 10, 'Y', 'a', 'm', 'a', 's', 'h', 'i', 'n', 'a', ' ', 4, 'Q', 'i', 'n', ' ', 3, 'Y', 'u', ' ', 9, 'K', 'e', 'w', 'a', 's', 'h', 'i', 'i', ' ', 4, 'L', 'o', 'u', ' ', 3, 'T', 'u', ' ', 4, 'D', 'u', 'i', ' ', 3, 'X', 'i', ' ', 5, 'W', 'e', 'n', 'g', ' ', 5, 'C', 'a', 'n', 'g', ' ', 5, 'D', 'a', 'n', 'g', ' ', 5, 'H', 'o', 'n', 'g', ' ', 4, 'J', 'i', 'e', ' ', 3, 'A', 'i', ' ', 4, 'L', 'i', 'u', ' ', 3, 'W', 'u', ' ', 5, 'S', 'o', 'n', 'g', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 3, 'Z', 'i', ' ', 4, 'W', 'e', 'i', ' ', 5, 'B', 'e', 'n', 'g', ' ', 5, 'D', 'i', 'a', 'n', ' ', 4, 'C', 'u', 'o', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 4, 'N', 'i', 'e', ' ', 4, 'C', 'u', 'o', ' ', 3, 'J', 'i', ' ', 4, 'T', 'a', 'o', ' ', 5, 'S', 'o', 'n', 'g', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 5, 'L', 'i', 'a', 'o', ' ', 5, 'K', 'a', 'n', 'g', ' ', 5, 'C', 'h', 'a', 'n', ' ', 4, 'D', 'i', 'e', ' ', 4, 'C', 'e', 'n', ' ', 5, 'D', 'i', 'n', 'g', ' ', 3, 'T', 'u', ' ', 4, 'L', 'o', 'u', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 3, 'A', 'o', ' ', 4, 'C', 'a', 'o', ' ', 3, 'Q', 'u', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 4, 'Z', 'u', 'i', ' ', 4, 'Z', 'u', 'i', ' ', 4, 'D', 'a', 'o', ' ', 4, 'D', 'a', 'o', ' ', 3, 'X', 'i', ' ', 3, 'Y', 'u', ' ', 3, 'B', 'o', ' ', 5, 'L', 'o', 'n', 'g', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 5, 'C', 'e', 'n', 'g', ' ', 3, 'B', 'o', ' ', 4, 'Q', 'i', 'n', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'L', 'a', 'o', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 4, 'L', 'i', 'n', ' ', 5, 'L', 'i', 'a', 'o', ' ', 5, 'L', 'i', 'a', 'o', ' ', 4, 'J', 'i', 'n', ' ', 5, 'D', 'e', 'n', 'g', ' ', 4, 'D', 'u', 'o', ' ', 4, 'Z', 'u', 'n', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'G', 'u', 'i', ' ', 4, 'Y', 'a', 'o', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'J', 'u', 'e', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 4, 'X', 'u', 'e', ' ', 4, 'N', 'a', 'o', ' ', 3, 'Y', 'e', ' ', 3, 'Y', 'e', ' ', 3, 'Y', 'i', ' ', 2, 'E', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'J', 'i', ' ', 4, 'X', 'i', 'e', ' ', 3, 'K', 'e', ' ', 3, 'X', 'i', ' ', 3, 'D', 'i', ' ', 3, 'A', 'o', ' ', 4, 'Z', 'u', 'i', ' ', 3, 'N', 'i', ' ', 5, 'R', 'o', 'n', 'g', ' ', 4, 'D', 'a', 'o', ' ', 5, 'L', 'i', 'n', 'g', ' ', 3, 'Z', 'a', ' ', 3, 'Y', 'u', ' ', 4, 'Y', 'u', 'e', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'J', 'i', 'e', ' ', 3, 'L', 'i', ' ', 4, 'S', 'u', 'i', ' ', 5, 'L', 'o', 'n', 'g', ' ', 5, 'L', 'o', 'n', 'g', ' ', 5, 'D', 'i', 'a', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 3, 'X', 'i', ' ', 3, 'J', 'u', ' ', 5, 'C', 'h', 'a', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'K', 'u', 'i', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'W', 'e', 'i', ' ', 4, 'N', 'a', 'o', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 5, 'C', 'h', 'a', 'o', ' ', 5, 'C', 'u', 'a', 'n', ' ', 5, 'L', 'u', 'a', 'n', ' ', 5, 'D', 'i', 'a', 'n', ' ', 5, 'D', 'i', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'N', 'a', 'o', ' ', 4, 'Y', 'a', 'n', ' ', 6, 'C', 'h', 'u', 'a', 'n', ' ', 4, 'G', 'u', 'i', ' ', 6, 'C', 'h', 'u', 'a', 'n', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 5, 'J', 'i', 'n', 'g', ' ', 4, 'X', 'u', 'n', ' ', 5, 'C', 'h', 'a', 'o', ' ', 5, 'C', 'h', 'a', 'o', ' ', 4, 'L', 'i', 'e', ' ', 5, 'G', 'o', 'n', 'g', ' ', 4, 'Z', 'u', 'o', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 3, 'J', 'u', ' ', 5, 'G', 'o', 'n', 'g', ' ', 4, 'K', 'e', 'k', ' ', 3, 'W', 'u', ' ', 4, 'P', 'w', 'u', ' ', 4, 'P', 'w', 'u', ' ', 5, 'C', 'h', 'a', 'i', ' ', 4, 'Q', 'i', 'u', ' ', 4, 'Q', 'i', 'u', ' ', 3, 'J', 'i', ' ', 3, 'Y', 'i', ' ', 3, 'S', 'i', ' ', 3, 'B', 'a', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 4, 'J', 'i', 'n', ' ', 4, 'X', 'u', 'n', ' ', 5, 'J', 'u', 'a', 'n', ' ', 5, 'P', 'h', 'a', 's', ' ', 4, 'X', 'u', 'n', ' ', 4, 'J', 'i', 'n', ' ', 3, 'F', 'u', ' ', 3, 'Z', 'a', ' ', 3, 'B', 'i', ' ', 4, 'S', 'h', 'i', ' ', 3, 'B', 'u', ' ', 5, 'D', 'i', 'n', 'g', ' ', 6, 'S', 'h', 'u', 'a', 'i', ' ', 4, 'F', 'a', 'n', ' ', 4, 'N', 'i', 'e', ' ', 4, 'S', 'h', 'i', ' ', 4, 'F', 'e', 'n', ' ', 3, 'P', 'a', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'X', 'i', ' ', 3, 'H', 'u', ' ', 4, 'D', 'a', 'n', ' ', 4, 'W', 'e', 'i', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 5, 'T', 'a', 'n', 'g', ' ', 4, 'D', 'a', 'i', ' ', 3, 'M', 'a', ' ', 4, 'P', 'e', 'i', ' ', 3, 'P', 'a', ' ', 4, 'T', 'i', 'e', ' ', 3, 'F', 'u', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 3, 'B', 'o', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'D', 'i', ' ', 3, 'M', 'o', ' ', 3, 'Y', 'i', ' ', 3, 'Y', 'i', ' ', 5, 'P', 'i', 'n', 'g', ' ', 4, 'Q', 'i', 'a', ' ', 5, 'J', 'u', 'a', 'n', ' ', 3, 'R', 'u', ' ', 6, 'S', 'h', 'u', 'a', 'i', ' ', 4, 'D', 'a', 'i', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 5, 'S', 'h', 'u', 'i', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 4, 'S', 'h', 'i', ' ', 4, 'Q', 'u', 'n', ' ', 3, 'X', 'i', ' ', 5, 'B', 'a', 'n', 'g', ' ', 4, 'D', 'a', 'i', ' ', 4, 'G', 'u', 'i', ' ', 5, 'C', 'h', 'o', 'u', ' ', 5, 'P', 'i', 'n', 'g', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 4, 'S', 'h', 'a', ' ', 4, 'W', 'a', 'n', ' ', 4, 'D', 'a', 'i', ' ', 4, 'W', 'e', 'i', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 4, 'S', 'h', 'a', ' ', 3, 'Q', 'i', ' ', 3, 'Z', 'e', ' ', 4, 'G', 'u', 'o', ' ', 4, 'M', 'a', 'o', ' ', 3, 'D', 'u', ' ', 4, 'H', 'o', 'u', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 3, 'X', 'u', ' ', 3, 'M', 'i', ' ', 4, 'W', 'e', 'i', ' ', 3, 'W', 'o', ' ', 3, 'F', 'u', ' ', 3, 'Y', 'i', ' ', 5, 'B', 'a', 'n', 'g', ' ', 5, 'P', 'i', 'n', 'g', ' ', 7, 'T', 'a', 'z', 'u', 'n', 'a', ' ', 5, 'G', 'o', 'n', 'g', ' ', 4, 'P', 'a', 'n', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 4, 'D', 'a', 'o', ' ', 3, 'M', 'i', ' ', 4, 'J', 'i', 'a', ' ', 5, 'T', 'e', 'n', 'g', ' ', 4, 'H', 'u', 'i', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 5, 'S', 'h', 'a', 'n', ' ', 4, 'M', 'a', 'n', ' ', 3, 'M', 'u', ' ', 5, 'B', 'i', 'a', 'o', ' ', 4, 'G', 'u', 'o', ' ', 3, 'Z', 'e', ' ', 3, 'M', 'u', ' ', 5, 'B', 'a', 'n', 'g', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 6, 'J', 'i', 'o', 'n', 'g', ' ', 5, 'C', 'h', 'a', 'n', ' ', 3, 'F', 'u', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'H', 'u', ' ', 4, 'F', 'a', 'n', ' ', 7, 'C', 'h', 'u', 'a', 'n', 'g', ' ', 3, 'B', 'i', ' ', 4, 'H', 'e', 'i', ' ', 3, 'M', 'i', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 5, 'C', 'h', 'a', 'n', ' ', 4, 'F', 'e', 'n', ' ', 5, 'M', 'e', 'n', 'g', ' ', 5, 'B', 'a', 'n', 'g', ' ', 5, 'C', 'h', 'o', 'u', ' ', 4, 'M', 'i', 'e', ' ', 4, 'C', 'h', 'u', ' ', 4, 'J', 'i', 'e', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'L', 'a', 'n', ' ', 4, 'G', 'a', 'n', ' ', 5, 'P', 'i', 'n', 'g', ' ', 5, 'N', 'i', 'a', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'B', 'i', 'n', 'g', ' ', 5, 'B', 'i', 'n', 'g', ' ', 5, 'X', 'i', 'n', 'g', ' ', 4, 'G', 'a', 'n', ' ', 4, 'Y', 'a', 'o', ' ', 5, 'H', 'u', 'a', 'n', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'Y', 'o', 'u', ' ', 3, 'J', 'i', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'P', 'i', ' ', 5, 'T', 'i', 'n', 'g', ' ', 3, 'Z', 'e', ' ', 6, 'G', 'u', 'a', 'n', 'g', ' ', 7, 'Z', 'h', 'u', 'a', 'n', 'g', ' ', 3, 'M', 'o', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 3, 'B', 'i', ' ', 4, 'Q', 'i', 'n', ' ', 4, 'D', 'u', 'n', ' ', 7, 'C', 'h', 'u', 'a', 'n', 'g', ' ', 4, 'G', 'u', 'i', ' ', 3, 'Y', 'a', ' ', 4, 'B', 'a', 'i', ' ', 4, 'J', 'i', 'e', ' ', 3, 'X', 'u', ' ', 3, 'L', 'u', ' ', 3, 'W', 'u', ' ', 3, 'K', 'u', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 3, 'D', 'i', ' ', 4, 'P', 'a', 'o', ' ', 5, 'D', 'i', 'a', 'n', ' ', 3, 'Y', 'a', ' ', 5, 'M', 'i', 'a', 'o', ' ', 5, 'G', 'e', 'n', 'g', ' ', 3, 'C', 'i', ' ', 3, 'F', 'u', ' ', 5, 'T', 'o', 'n', 'g', ' ', 5, 'P', 'a', 'n', 'g', ' ', 4, 'F', 'e', 'i', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'T', 'i', 'a', 'o', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'X', 'i', 'u', ' ', 3, 'D', 'u', ' ', 4, 'Z', 'u', 'o', ' ', 5, 'X', 'i', 'a', 'o', ' ', 3, 'T', 'u', ' ', 4, 'G', 'u', 'i', ' ', 3, 'K', 'u', ' ', 5, 'P', 'a', 'n', 'g', ' ', 5, 'T', 'i', 'n', 'g', ' ', 4, 'Y', 'o', 'u', ' ', 3, 'B', 'u', ' ', 5, 'D', 'i', 'n', 'g', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 4, 'L', 'a', 'i', ' ', 4, 'B', 'e', 'i', ' ', 3, 'J', 'i', ' ', 3, 'A', 'n', ' ', 4, 'S', 'h', 'u', ' ', 5, 'K', 'a', 'n', 'g', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 4, 'T', 'u', 'o', ' ', 5, 'S', 'o', 'n', 'g', ' ', 4, 'S', 'h', 'u', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 3, 'Y', 'u', ' ', 3, 'Y', 'u', ' ', 5, 'M', 'i', 'a', 'o', ' ', 4, 'S', 'o', 'u', ' ', 3, 'C', 'e', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 4, 'F', 'e', 'i', ' ', 4, 'J', 'i', 'u', ' ', 3, 'H', 'e', ' ', 4, 'H', 'u', 'i', ' ', 4, 'L', 'i', 'u', ' ', 4, 'S', 'h', 'a', ' ', 5, 'L', 'i', 'a', 'n', ' ', 5, 'L', 'a', 'n', 'g', ' ', 4, 'S', 'o', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'P', 'o', 'u', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 4, 'J', 'i', 'u', ' ', 4, 'J', 'i', 'u', ' ', 4, 'Q', 'i', 'n', ' ', 3, 'A', 'o', ' ', 4, 'K', 'u', 'o', ' ', 4, 'L', 'o', 'u', ' ', 4, 'Y', 'i', 'n', ' ', 5, 'L', 'i', 'a', 'o', ' ', 4, 'D', 'a', 'i', ' ', 3, 'L', 'u', ' ', 3, 'Y', 'i', ' ', 4, 'C', 'h', 'u', ' ', 5, 'C', 'h', 'a', 'n', ' ', 3, 'T', 'u', ' ', 3, 'S', 'i', ' ', 4, 'X', 'i', 'n', ' ', 5, 'M', 'i', 'a', 'o', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 3, 'W', 'u', ' ', 4, 'F', 'e', 'i', ' ', 6, 'G', 'u', 'a', 'n', 'g', ' ', 4, 'K', 'o', 'c', ' ', 5, 'K', 'u', 'a', 'i', ' ', 3, 'B', 'i', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 4, 'X', 'i', 'e', ' ', 4, 'L', 'i', 'n', ' ', 4, 'L', 'i', 'n', ' ', 5, 'L', 'i', 'a', 'o', ' ', 3, 'L', 'u', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'T', 'i', 'n', 'g', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 3, 'L', 'i', ' ', 5, 'T', 'i', 'n', 'g', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'X', 'u', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'T', 'i', 'n', 'g', ' ', 3, 'D', 'i', ' ', 3, 'P', 'o', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'H', 'u', 'i', ' ', 4, 'N', 'a', 'i', ' ', 4, 'H', 'u', 'i', ' ', 5, 'G', 'o', 'n', 'g', ' ', 5, 'N', 'i', 'a', 'n', ' ', 4, 'K', 'a', 'i', ' ', 5, 'B', 'i', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 3, 'Q', 'i', ' ', 5, 'N', 'o', 'n', 'g', ' ', 4, 'F', 'e', 'n', ' ', 3, 'J', 'u', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 5, 'Z', 'a', 'n', 'g', ' ', 3, 'B', 'i', ' ', 3, 'Y', 'i', ' ', 3, 'Y', 'i', ' ', 3, 'E', 'r', ' ', 4, 'S', 'a', 'n', ' ', 4, 'S', 'h', 'i', ' ', 3, 'E', 'r', ' ', 4, 'S', 'h', 'i', ' ', 4, 'S', 'h', 'i', ' ', 5, 'G', 'o', 'n', 'g', ' ', 5, 'D', 'i', 'a', 'o', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'H', 'u', ' ', 3, 'F', 'u', ' ', 5, 'H', 'o', 'n', 'g', ' ', 3, 'W', 'u', ' ', 4, 'T', 'u', 'i', ' ', 4, 'C', 'h', 'i', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 3, 'B', 'a', ' ', 5, 'S', 'h', 'e', 'n', ' ', 3, 'D', 'i', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 4, 'J', 'u', 'e', ' ', 4, 'T', 'a', 'o', ' ', 3, 'F', 'u', ' ', 3, 'D', 'i', ' ', 3, 'M', 'i', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'H', 'u', ' ', 5, 'C', 'h', 'a', 'o', ' ', 3, 'N', 'u', ' ', 5, 'J', 'i', 'n', 'g', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 3, 'Y', 'i', ' ', 3, 'M', 'i', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 4, 'W', 'a', 'n', ' ', 5, 'S', 'h', 'a', 'o', ' ', 4, 'R', 'u', 'o', ' ', 5, 'X', 'u', 'a', 'n', ' ', 5, 'J', 'i', 'n', 'g', ' ', 4, 'D', 'u', 'n', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 5, 'P', 'e', 'n', 'g', ' ', 4, 'D', 'a', 'n', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 3, 'B', 'i', ' ', 3, 'B', 'i', ' ', 4, 'S', 'h', 'e', ' ', 4, 'D', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'G', 'o', 'u', ' ', 4, 'S', 'e', 'i', ' ', 3, 'F', 'a', ' ', 3, 'B', 'i', ' ', 4, 'K', 'o', 'u', ' ', 5, 'N', 'a', 'g', 'i', ' ', 4, 'B', 'i', 'e', ' ', 5, 'X', 'i', 'a', 'o', ' ', 4, 'D', 'a', 'n', ' ', 4, 'K', 'u', 'o', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 5, 'H', 'o', 'n', 'g', ' ', 3, 'M', 'i', ' ', 4, 'K', 'u', 'o', ' ', 4, 'W', 'a', 'n', ' ', 4, 'J', 'u', 'e', ' ', 3, 'J', 'i', ' ', 3, 'J', 'i', ' ', 4, 'G', 'u', 'i', ' ', 5, 'D', 'a', 'n', 'g', ' ', 3, 'L', 'u', ' ', 3, 'L', 'u', ' ', 5, 'T', 'u', 'a', 'n', ' ', 4, 'H', 'u', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'H', 'u', 'i', ' ', 4, 'H', 'u', 'i', ' ', 3, 'Y', 'i', ' ', 3, 'Y', 'i', ' ', 3, 'Y', 'i', ' ', 3, 'Y', 'i', ' ', 4, 'H', 'u', 'o', ' ', 4, 'H', 'u', 'o', ' ', 5, 'S', 'h', 'a', 'n', ' ', 5, 'X', 'i', 'n', 'g', ' ', 4, 'W', 'e', 'n', ' ', 5, 'T', 'o', 'n', 'g', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 4, 'C', 'h', 'i', ' ', 4, 'C', 'a', 'i', ' ', 5, 'B', 'i', 'a', 'o', ' ', 5, 'D', 'i', 'a', 'o', ' ', 4, 'B', 'i', 'n', ' ', 5, 'P', 'e', 'n', 'g', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 5, 'P', 'i', 'a', 'o', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'C', 'h', 'i', ' ', 4, 'C', 'h', 'i', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 4, 'T', 'u', 'o', ' ', 3, 'J', 'i', ' ', 5, 'P', 'a', 'n', 'g', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 5, 'W', 'a', 'n', 'g', ' ', 4, 'C', 'h', 'e', ' ', 3, 'B', 'i', ' ', 4, 'C', 'h', 'i', ' ', 5, 'L', 'i', 'n', 'g', ' ', 3, 'F', 'u', ' ', 5, 'W', 'a', 'n', 'g', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 3, 'C', 'u', ' ', 5, 'W', 'a', 'n', 'g', ' ', 5, 'J', 'i', 'n', 'g', ' ', 4, 'D', 'a', 'i', ' ', 3, 'X', 'i', ' ', 4, 'X', 'u', 'n', ' ', 4, 'H', 'e', 'n', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 5, 'H', 'u', 'a', 'i', ' ', 3, 'L', 'u', ' ', 4, 'H', 'o', 'u', ' ', 3, 'W', 'a', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'X', 'u', ' ', 5, 'J', 'i', 'n', 'g', ' ', 3, 'T', 'u', ' ', 5, 'C', 'o', 'n', 'g', ' ', 4, 'L', 'a', 'i', ' ', 5, 'C', 'o', 'n', 'g', ' ', 3, 'D', 'e', ' ', 4, 'P', 'a', 'i', ' ', 3, 'X', 'i', ' ', 3, 'Q', 'i', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'C', 'o', 'n', 'g', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 4, 'L', 'a', 'i', ' ', 3, 'Y', 'u', ' ', 4, 'X', 'i', 'e', ' ', 4, 'J', 'i', 'e', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'C', 'h', 'i', ' ', 4, 'J', 'i', 'a', ' ', 5, 'B', 'i', 'a', 'n', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 3, 'F', 'u', ' ', 4, 'X', 'u', 'n', ' ', 4, 'W', 'e', 'i', ' ', 5, 'P', 'a', 'n', 'g', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'W', 'e', 'i', ' ', 3, 'X', 'i', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 5, 'P', 'i', 'a', 'o', ' ', 4, 'C', 'h', 'i', ' ', 3, 'D', 'e', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 4, 'B', 'i', 'e', ' ', 3, 'D', 'e', ' ', 6, 'C', 'h', 'o', 'n', 'g', ' ', 4, 'C', 'h', 'e', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'W', 'e', 'i', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'H', 'u', 'i', ' ', 4, 'M', 'e', 'i', ' ', 5, 'L', 'o', 'n', 'g', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 4, 'B', 'a', 'o', ' ', 3, 'Q', 'u', ' ', 4, 'X', 'i', 'n', ' ', 4, 'S', 'h', 'u', ' ', 3, 'B', 'i', ' ', 3, 'Y', 'i', ' ', 3, 'L', 'e', ' ', 4, 'R', 'e', 'n', ' ', 4, 'D', 'a', 'o', ' ', 5, 'D', 'i', 'n', 'g', ' ', 4, 'G', 'a', 'i', ' ', 3, 'J', 'i', ' ', 4, 'R', 'e', 'n', ' ', 4, 'R', 'e', 'n', ' ', 5, 'C', 'h', 'a', 'n', ' ', 4, 'T', 'a', 'n', ' ', 3, 'T', 'e', ' ', 3, 'T', 'e', ' ', 4, 'G', 'a', 'n', ' ', 3, 'Q', 'i', ' ', 4, 'S', 'h', 'i', ' ', 4, 'C', 'u', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'W', 'a', 'n', 'g', ' ', 5, 'M', 'a', 'n', 'g', ' ', 3, 'X', 'i', ' ', 4, 'F', 'a', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 5, 'T', 'i', 'a', 'n', ' ', 4, 'M', 'i', 'n', ' ', 4, 'M', 'i', 'n', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 6, 'C', 'h', 'o', 'n', 'g', ' ', 3, 'W', 'u', ' ', 3, 'J', 'i', ' ', 3, 'W', 'u', ' ', 3, 'X', 'i', ' ', 3, 'Y', 'e', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'W', 'a', 'n', ' ', 5, 'C', 'o', 'n', 'g', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 5, 'K', 'u', 'a', 'i', ' ', 3, 'Y', 'u', ' ', 5, 'B', 'i', 'a', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'Q', 'i', ' ', 4, 'C', 'u', 'i', ' ', 5, 'C', 'h', 'e', 'n', ' ', 4, 'T', 'a', 'i', ' ', 4, 'T', 'u', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'N', 'i', 'a', 'n', ' ', 4, 'H', 'u', 'n', ' ', 6, 'X', 'i', 'o', 'n', 'g', ' ', 4, 'N', 'i', 'u', ' ', 5, 'W', 'a', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'X', 'i', 'n', ' ', 5, 'K', 'a', 'n', 'g', ' ', 3, 'H', 'u', ' ', 4, 'K', 'a', 'i', ' ', 4, 'F', 'e', 'n', ' ', 5, 'H', 'u', 'a', 'i', ' ', 4, 'T', 'a', 'i', ' ', 5, 'S', 'o', 'n', 'g', ' ', 3, 'W', 'u', ' ', 3, 'O', 'u', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 7, 'C', 'h', 'u', 'a', 'n', 'g', ' ', 3, 'J', 'u', ' ', 3, 'Y', 'i', ' ', 4, 'B', 'a', 'o', ' ', 5, 'C', 'h', 'a', 'o', ' ', 4, 'M', 'i', 'n', ' ', 4, 'P', 'e', 'i', ' ', 4, 'Z', 'u', 'o', ' ', 4, 'Z', 'e', 'n', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 4, 'K', 'o', 'u', ' ', 4, 'B', 'a', 'n', ' ', 3, 'N', 'u', ' ', 4, 'N', 'a', 'o', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 3, 'P', 'a', ' ', 3, 'B', 'u', ' ', 4, 'T', 'i', 'e', ' ', 3, 'G', 'u', ' ', 3, 'H', 'u', ' ', 3, 'J', 'u', ' ', 3, 'D', 'a', ' ', 5, 'L', 'i', 'a', 'n', ' ', 3, 'S', 'i', ' ', 5, 'C', 'h', 'o', 'u', ' ', 3, 'D', 'i', ' ', 4, 'D', 'a', 'i', ' ', 3, 'Y', 'i', ' ', 3, 'T', 'u', ' ', 4, 'Y', 'o', 'u', ' ', 3, 'F', 'u', ' ', 3, 'J', 'i', ' ', 5, 'P', 'e', 'n', 'g', ' ', 5, 'X', 'i', 'n', 'g', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 3, 'N', 'i', ' ', 5, 'G', 'u', 'a', 'i', ' ', 3, 'F', 'u', ' ', 3, 'X', 'i', ' ', 3, 'B', 'i', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'Q', 'i', 'e', ' ', 5, 'X', 'u', 'a', 'n', ' ', 5, 'C', 'o', 'n', 'g', ' ', 5, 'B', 'i', 'n', 'g', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 3, 'X', 'u', ' ', 4, 'C', 'h', 'u', ' ', 3, 'P', 'i', ' ', 3, 'X', 'i', ' ', 3, 'X', 'i', ' ', 4, 'T', 'a', 'n', ' ', 8, 'K', 'o', 'r', 'a', 'e', 'r', 'u', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 4, 'D', 'u', 'i', ' ', 3, 'K', 'i', ' ', 3, 'Y', 'i', ' ', 4, 'C', 'h', 'i', ' ', 4, 'R', 'e', 'n', ' ', 4, 'X', 'u', 'n', ' ', 4, 'S', 'h', 'i', ' ', 3, 'X', 'i', ' ', 4, 'L', 'a', 'o', ' ', 5, 'H', 'e', 'n', 'g', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 3, 'M', 'u', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'X', 'i', 'e', ' ', 5, 'L', 'i', 'a', 'n', ' ', 5, 'T', 'i', 'a', 'o', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 4, 'D', 'i', 'e', ' ', 4, 'H', 'a', 'o', ' ', 5, 'K', 'o', 'n', 'g', ' ', 4, 'G', 'u', 'i', ' ', 5, 'H', 'e', 'n', 'g', ' ', 3, 'X', 'i', ' ', 5, 'X', 'i', 'a', 'o', ' ', 4, 'S', 'h', 'u', ' ', 2, 'S', ' ', 4, 'K', 'u', 'a', ' ', 4, 'Q', 'i', 'u', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 4, 'H', 'u', 'i', ' ', 4, 'H', 'u', 'i', ' ', 4, 'C', 'h', 'i', ' ', 4, 'J', 'i', 'a', ' ', 3, 'Y', 'i', ' ', 6, 'X', 'i', 'o', 'n', 'g', ' ', 5, 'G', 'u', 'a', 'i', ' ', 4, 'L', 'i', 'n', ' ', 4, 'H', 'u', 'i', ' ', 3, 'Z', 'i', ' ', 3, 'X', 'u', ' ', 4, 'C', 'h', 'i', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 3, 'N', 'u', ' ', 4, 'H', 'e', 'n', ' ', 3, 'E', 'n', ' ', 3, 'K', 'e', ' ', 5, 'T', 'o', 'n', 'g', ' ', 5, 'T', 'i', 'a', 'n', ' ', 5, 'G', 'o', 'n', 'g', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 3, 'X', 'i', ' ', 4, 'Q', 'i', 'a', ' ', 4, 'Y', 'u', 'e', ' ', 5, 'P', 'e', 'n', 'g', ' ', 4, 'K', 'e', 'n', ' ', 3, 'D', 'e', ' ', 4, 'H', 'u', 'i', ' ', 2, 'E', ' ', 5, 'K', 'y', 'u', 'u', ' ', 5, 'T', 'o', 'n', 'g', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'K', 'a', 'i', ' ', 3, 'C', 'e', ' ', 4, 'N', 'a', 'o', ' ', 4, 'Y', 'u', 'n', ' ', 5, 'M', 'a', 'n', 'g', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 3, 'P', 'i', ' ', 4, 'K', 'u', 'n', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 4, 'Y', 'u', 'e', ' ', 3, 'Y', 'u', ' ', 3, 'Y', 'u', ' ', 4, 'J', 'i', 'e', ' ', 3, 'X', 'i', ' ', 4, 'Z', 'h', 'e', ' ', 4, 'L', 'i', 'n', ' ', 3, 'T', 'i', ' ', 4, 'H', 'a', 'n', ' ', 4, 'H', 'a', 'o', ' ', 4, 'Q', 'i', 'e', ' ', 3, 'T', 'i', ' ', 3, 'B', 'u', ' ', 3, 'Y', 'i', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'H', 'u', 'i', ' ', 3, 'X', 'i', ' ', 4, 'B', 'e', 'i', ' ', 4, 'M', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 5, 'H', 'e', 'n', 'g', ' ', 5, 'S', 'o', 'n', 'g', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 4, 'H', 'u', 'i', ' ', 3, 'W', 'u', ' ', 3, 'W', 'u', ' ', 4, 'Y', 'o', 'u', ' ', 3, 'L', 'i', ' ', 6, 'L', 'i', 'a', 'n', 'g', ' ', 5, 'H', 'u', 'a', 'n', ' ', 5, 'C', 'o', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 4, 'Y', 'u', 'e', ' ', 3, 'L', 'i', ' ', 4, 'N', 'i', 'n', ' ', 4, 'N', 'a', 'o', ' ', 2, 'E', ' ', 4, 'Q', 'u', 'e', ' ', 5, 'X', 'u', 'a', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 3, 'W', 'u', ' ', 4, 'M', 'i', 'n', ' ', 5, 'C', 'o', 'n', 'g', ' ', 4, 'F', 'e', 'i', ' ', 4, 'B', 'e', 'i', ' ', 4, 'D', 'u', 'o', ' ', 4, 'C', 'u', 'i', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 4, 'M', 'e', 'n', ' ', 3, 'L', 'i', ' ', 3, 'J', 'i', ' ', 5, 'G', 'u', 'a', 'n', ' ', 5, 'G', 'u', 'a', 'n', ' ', 5, 'X', 'i', 'n', 'g', ' ', 4, 'D', 'a', 'o', ' ', 3, 'Q', 'i', ' ', 5, 'K', 'o', 'n', 'g', ' ', 5, 'T', 'i', 'a', 'n', ' ', 4, 'L', 'u', 'n', ' ', 3, 'X', 'i', ' ', 4, 'K', 'a', 'n', ' ', 4, 'K', 'u', 'n', ' ', 3, 'N', 'i', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 5, 'C', 'h', 'o', 'u', ' ', 4, 'D', 'u', 'n', ' ', 4, 'G', 'u', 'o', ' ', 5, 'C', 'h', 'a', 'n', ' ', 6, 'L', 'i', 'a', 'n', 'g', ' ', 4, 'W', 'a', 'n', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'J', 'i', 'n', ' ', 3, 'J', 'i', ' ', 4, 'L', 'i', 'n', ' ', 3, 'Y', 'u', ' ', 4, 'H', 'u', 'o', ' ', 3, 'H', 'e', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 4, 'T', 'a', 'n', ' ', 3, 'T', 'i', ' ', 3, 'T', 'i', ' ', 4, 'N', 'i', 'e', ' ', 5, 'W', 'a', 'n', 'g', ' ', 5, 'C', 'h', 'u', 'o', ' ', 3, 'B', 'u', ' ', 4, 'H', 'u', 'n', ' ', 3, 'X', 'i', ' ', 5, 'T', 'a', 'n', 'g', ' ', 4, 'X', 'i', 'n', ' ', 4, 'W', 'e', 'i', ' ', 4, 'H', 'u', 'i', ' ', 2, 'E', ' ', 4, 'R', 'u', 'i', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 5, 'D', 'i', 'a', 'n', ' ', 3, 'J', 'u', ' ', 4, 'C', 'a', 'n', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 3, 'D', 'e', ' ', 4, 'B', 'e', 'i', ' ', 4, 'Q', 'i', 'e', ' ', 4, 'C', 'a', 'n', ' ', 4, 'D', 'a', 'n', ' ', 5, 'G', 'u', 'a', 'n', ' ', 4, 'D', 'u', 'o', ' ', 4, 'N', 'a', 'o', ' ', 4, 'Y', 'u', 'n', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 5, 'Z', 'h', 'u', 'i', ' ', 4, 'D', 'i', 'e', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 5, 'C', 'h', 'u', 'n', ' ', 6, 'Q', 'i', 'o', 'n', 'g', ' ', 3, 'R', 'e', ' ', 5, 'X', 'i', 'n', 'g', ' ', 3, 'C', 'e', ' ', 5, 'B', 'i', 'a', 'n', ' ', 4, 'H', 'u', 'n', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 3, 'T', 'i', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 5, 'C', 'h', 'o', 'u', ' ', 4, 'B', 'e', 'i', ' ', 5, 'X', 'u', 'a', 'n', ' ', 4, 'W', 'e', 'i', ' ', 3, 'G', 'e', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'W', 'e', 'i', ' ', 3, 'Y', 'u', ' ', 3, 'Y', 'u', ' ', 3, 'B', 'i', ' ', 5, 'X', 'u', 'a', 'n', ' ', 5, 'H', 'u', 'a', 'n', ' ', 4, 'M', 'i', 'n', ' ', 3, 'B', 'i', ' ', 3, 'Y', 'i', ' ', 5, 'M', 'i', 'a', 'n', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 4, 'K', 'a', 'i', ' ', 5, 'D', 'a', 'n', 'g', ' ', 4, 'Y', 'i', 'n', ' ', 2, 'E', ' ', 5, 'C', 'h', 'e', 'n', ' ', 4, 'M', 'o', 'u', ' ', 3, 'K', 'e', ' ', 3, 'K', 'e', ' ', 3, 'Y', 'u', ' ', 3, 'A', 'i', ' ', 4, 'Q', 'i', 'e', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'N', 'u', 'o', ' ', 4, 'G', 'a', 'n', ' ', 4, 'Y', 'u', 'n', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 4, 'S', 'a', 'i', ' ', 5, 'L', 'e', 'n', 'g', ' ', 4, 'F', 'e', 'n', ' ', 4, 'K', 'u', 'i', ' ', 4, 'K', 'u', 'i', ' ', 4, 'Q', 'u', 'e', ' ', 5, 'G', 'o', 'n', 'g', ' ', 4, 'Y', 'u', 'n', ' ', 3, 'S', 'u', ' ', 3, 'S', 'u', ' ', 3, 'Q', 'i', ' ', 4, 'Y', 'a', 'o', ' ', 5, 'S', 'o', 'n', 'g', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 3, 'J', 'i', ' ', 3, 'G', 'u', ' ', 3, 'J', 'u', ' ', 7, 'C', 'h', 'u', 'a', 'n', 'g', ' ', 3, 'N', 'i', ' ', 4, 'X', 'i', 'e', ' ', 4, 'K', 'a', 'i', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 4, 'C', 'a', 'o', ' ', 4, 'S', 'u', 'n', ' ', 5, 'S', 'h', 'e', 'n', ' ', 3, 'B', 'o', ' ', 4, 'K', 'a', 'i', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'X', 'i', 'e', ' ', 4, 'H', 'u', 'n', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 3, 'L', 'i', ' ', 4, 'S', 'a', 'o', ' ', 4, 'T', 'a', 'o', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'C', 'i', ' ', 3, 'X', 'u', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'T', 'a', 'i', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 4, 'Y', 'u', 'n', ' ', 5, 'S', 'h', 'e', 'n', ' ', 5, 'M', 'i', 'n', 'g', ' ', 4, 'S', 'h', 'e', ' ', 5, 'C', 'o', 'n', 'g', ' ', 5, 'P', 'i', 'a', 'o', ' ', 3, 'M', 'o', ' ', 3, 'M', 'u', ' ', 4, 'G', 'u', 'o', ' ', 4, 'C', 'h', 'i', ' ', 4, 'C', 'a', 'n', ' ', 4, 'C', 'a', 'n', ' ', 4, 'C', 'a', 'n', ' ', 4, 'C', 'u', 'i', ' ', 4, 'M', 'i', 'n', ' ', 3, 'T', 'e', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 5, 'T', 'o', 'n', 'g', ' ', 3, 'A', 'o', ' ', 7, 'S', 'h', 'u', 'a', 'n', 'g', ' ', 4, 'M', 'a', 'n', ' ', 5, 'G', 'u', 'a', 'n', ' ', 4, 'Q', 'u', 'e', ' ', 4, 'Z', 'a', 'o', ' ', 4, 'J', 'i', 'u', ' ', 4, 'H', 'u', 'i', ' ', 4, 'K', 'a', 'i', ' ', 5, 'L', 'i', 'a', 'n', ' ', 3, 'O', 'u', ' ', 5, 'S', 'o', 'n', 'g', ' ', 4, 'J', 'i', 'n', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'L', 'u', ' ', 6, 'S', 'h', 'a', 'n', 'g', ' ', 4, 'W', 'e', 'i', ' ', 5, 'T', 'u', 'a', 'n', ' ', 4, 'M', 'a', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'S', 'h', 'e', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 5, 'K', 'a', 'n', 'g', ' ', 3, 'D', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'L', 'o', 'u', ' ', 5, 'J', 'u', 'a', 'n', ' ', 3, 'Q', 'i', ' ', 3, 'Q', 'i', ' ', 3, 'Y', 'u', ' ', 5, 'P', 'i', 'n', 'g', ' ', 5, 'L', 'i', 'a', 'o', ' ', 5, 'C', 'o', 'n', 'g', ' ', 4, 'Y', 'o', 'u', ' ', 6, 'C', 'h', 'o', 'n', 'g', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'T', 'o', 'n', 'g', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 3, 'Q', 'i', ' ', 3, 'Q', 'u', ' ', 5, 'P', 'e', 'n', 'g', ' ', 4, 'B', 'e', 'i', ' ', 4, 'B', 'i', 'e', ' ', 5, 'C', 'h', 'u', 'n', ' ', 5, 'J', 'i', 'a', 'o', ' ', 5, 'Z', 'e', 'n', 'g', ' ', 4, 'C', 'h', 'i', ' ', 5, 'L', 'i', 'a', 'n', ' ', 5, 'P', 'i', 'n', 'g', ' ', 4, 'K', 'u', 'i', ' ', 4, 'H', 'u', 'i', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'X', 'i', ' ', 3, 'X', 'i', ' ', 4, 'D', 'a', 'n', ' ', 4, 'T', 'a', 'n', ' ', 4, 'D', 'u', 'o', ' ', 4, 'D', 'u', 'i', ' ', 4, 'D', 'u', 'i', ' ', 3, 'S', 'u', ' ', 4, 'J', 'u', 'e', ' ', 3, 'C', 'e', ' ', 5, 'X', 'i', 'a', 'o', ' ', 4, 'F', 'a', 'n', ' ', 4, 'F', 'e', 'n', ' ', 4, 'L', 'a', 'o', ' ', 4, 'L', 'a', 'o', ' ', 6, 'C', 'h', 'o', 'n', 'g', ' ', 4, 'H', 'a', 'n', ' ', 3, 'Q', 'i', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'M', 'i', 'n', ' ', 5, 'J', 'i', 'n', 'g', ' ', 5, 'L', 'i', 'a', 'o', ' ', 3, 'W', 'u', ' ', 4, 'C', 'a', 'n', ' ', 4, 'J', 'u', 'e', ' ', 3, 'C', 'u', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'T', 'a', 'n', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 3, 'P', 'i', ' ', 3, 'Y', 'i', ' ', 4, 'C', 'h', 'u', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'N', 'a', 'o', ' ', 4, 'D', 'a', 'n', ' ', 4, 'T', 'a', 'n', ' ', 5, 'J', 'i', 'n', 'g', ' ', 5, 'S', 'o', 'n', 'g', ' ', 4, 'H', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'W', 'a', 'i', ' ', 5, 'H', 'u', 'a', 'n', ' ', 5, 'D', 'o', 'n', 'g', ' ', 4, 'Q', 'i', 'n', ' ', 4, 'Q', 'i', 'n', ' ', 3, 'Q', 'u', ' ', 4, 'C', 'a', 'o', ' ', 4, 'K', 'e', 'n', ' ', 4, 'X', 'i', 'e', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 3, 'A', 'o', ' ', 4, 'M', 'a', 'o', ' ', 3, 'Y', 'i', ' ', 4, 'L', 'i', 'n', ' ', 3, 'S', 'e', ' ', 4, 'J', 'u', 'n', ' ', 5, 'H', 'u', 'a', 'i', ' ', 4, 'M', 'e', 'n', ' ', 4, 'L', 'a', 'n', ' ', 3, 'A', 'i', ' ', 4, 'L', 'i', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'G', 'u', 'a', ' ', 4, 'X', 'i', 'a', ' ', 4, 'C', 'h', 'i', ' ', 3, 'Y', 'u', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'D', 'a', 'i', ' ', 5, 'M', 'e', 'n', 'g', ' ', 3, 'A', 'i', ' ', 5, 'M', 'e', 'n', 'g', ' ', 4, 'D', 'u', 'i', ' ', 3, 'Q', 'i', ' ', 3, 'M', 'o', ' ', 4, 'L', 'a', 'n', ' ', 4, 'M', 'e', 'n', ' ', 5, 'C', 'h', 'o', 'u', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'N', 'u', 'o', ' ', 4, 'N', 'u', 'o', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 3, 'B', 'o', ' ', 4, 'Z', 'h', 'i', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 4, 'Y', 'o', 'u', ' ', 3, 'F', 'u', ' ', 4, 'L', 'i', 'u', ' ', 4, 'M', 'i', 'e', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 5, 'C', 'h', 'a', 'n', ' ', 5, 'M', 'e', 'n', 'g', ' ', 4, 'L', 'a', 'n', ' ', 5, 'H', 'u', 'a', 'i', ' ', 5, 'X', 'u', 'a', 'n', ' ', 5, 'R', 'a', 'n', 'g', ' ', 5, 'C', 'h', 'a', 'n', ' ', 3, 'J', 'i', ' ', 3, 'J', 'u', ' ', 5, 'H', 'u', 'a', 'n', ' ', 4, 'S', 'h', 'e', ' ', 3, 'Y', 'i', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'N', 'a', 'n', ' ', 3, 'M', 'i', ' ', 5, 'T', 'a', 'n', 'g', ' ', 4, 'J', 'u', 'e', ' ', 5, 'G', 'a', 'n', 'g', ' ', 5, 'G', 'a', 'n', 'g', ' ', 5, 'G', 'a', 'n', 'g', ' ', 3, 'G', 'e', ' ', 4, 'Y', 'u', 'e', ' ', 3, 'W', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'X', 'u', ' ', 4, 'S', 'h', 'u', ' ', 5, 'R', 'o', 'n', 'g', ' ', 3, 'X', 'i', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 3, 'W', 'o', ' ', 4, 'J', 'i', 'e', ' ', 3, 'G', 'e', ' ', 5, 'J', 'i', 'a', 'n', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 4, 'H', 'u', 'o', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 5, 'D', 'o', 'n', 'g', ' ', 3, 'Q', 'i', ' ', 4, 'J', 'i', 'a', ' ', 4, 'D', 'i', 'e', ' ', 4, 'Z', 'e', 'i', ' ', 4, 'J', 'i', 'a', ' ', 3, 'J', 'i', ' ', 4, 'S', 'h', 'i', ' ', 4, 'K', 'a', 'n', ' ', 3, 'J', 'i', ' ', 4, 'K', 'u', 'i', ' ', 4, 'G', 'a', 'i', ' ', 5, 'D', 'e', 'n', 'g', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 7, 'C', 'h', 'u', 'a', 'n', 'g', ' ', 3, 'G', 'e', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'J', 'i', 'e', ' ', 3, 'Y', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'L', 'u', ' ', 3, 'X', 'i', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 3, 'X', 'i', ' ', 3, 'X', 'i', ' ', 5, 'C', 'h', 'u', 'o', ' ', 4, 'D', 'a', 'i', ' ', 3, 'Q', 'u', ' ', 3, 'H', 'u', ' ', 3, 'H', 'u', ' ', 3, 'H', 'u', ' ', 2, 'E', ' ', 4, 'S', 'h', 'i', ' ', 3, 'L', 'i', ' ', 4, 'M', 'a', 'o', ' ', 3, 'H', 'u', ' ', 3, 'L', 'i', ' ', 5, 'F', 'a', 'n', 'g', ' ', 4, 'S', 'u', 'o', ' ', 5, 'B', 'i', 'a', 'n', ' ', 5, 'D', 'i', 'a', 'n', ' ', 6, 'J', 'i', 'o', 'n', 'g', ' ', 6, 'S', 'h', 'a', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 3, 'Y', 'i', ' ', 5, 'S', 'h', 'a', 'n', ' ', 3, 'H', 'u', ' ', 4, 'F', 'e', 'i', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'S', 'h', 'o', 'u', ' ', 2, 'T', ' ', 4, 'C', 'a', 'i', ' ', 4, 'Z', 'h', 'a', ' ', 4, 'Q', 'i', 'u', ' ', 3, 'L', 'e', ' ', 3, 'B', 'u', ' ', 3, 'B', 'a', ' ', 3, 'D', 'a', ' ', 5, 'R', 'e', 'n', 'g', ' ', 3, 'F', 'u', ' ', 7, 'H', 'a', 'm', 'e', 'r', 'u', ' ', 4, 'Z', 'a', 'i', ' ', 4, 'T', 'u', 'o', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 5, 'D', 'i', 'a', 'o', ' ', 5, 'K', 'a', 'n', 'g', ' ', 3, 'Y', 'u', ' ', 3, 'K', 'u', ' ', 4, 'H', 'a', 'n', ' ', 5, 'S', 'h', 'e', 'n', ' ', 4, 'C', 'h', 'a', ' ', 3, 'Y', 'i', ' ', 3, 'G', 'u', ' ', 4, 'K', 'o', 'u', ' ', 3, 'W', 'u', ' ', 4, 'T', 'u', 'o', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'R', 'e', 'n', ' ', 4, 'K', 'u', 'o', ' ', 4, 'M', 'e', 'n', ' ', 4, 'S', 'a', 'o', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 4, 'N', 'i', 'u', ' ', 4, 'B', 'a', 'n', ' ', 4, 'C', 'h', 'e', ' ', 4, 'R', 'a', 'o', ' ', 3, 'X', 'i', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'B', 'a', 'n', ' ', 4, 'J', 'i', 'a', ' ', 3, 'Y', 'u', ' ', 3, 'F', 'u', ' ', 3, 'A', 'o', ' ', 3, 'X', 'i', ' ', 3, 'P', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'Z', 'i', ' ', 2, 'E', ' ', 4, 'D', 'u', 'n', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 3, 'J', 'i', ' ', 4, 'Y', 'a', 'n', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 5, 'B', 'i', 'a', 'n', ' ', 5, 'C', 'h', 'a', 'o', ' ', 3, 'J', 'u', ' ', 4, 'W', 'e', 'n', ' ', 3, 'H', 'u', ' ', 4, 'Y', 'u', 'e', ' ', 4, 'J', 'u', 'e', ' ', 3, 'B', 'a', ' ', 4, 'Q', 'i', 'n', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 4, 'Y', 'u', 'n', ' ', 4, 'W', 'a', 'n', ' ', 3, 'N', 'u', ' ', 3, 'Y', 'i', ' ', 4, 'S', 'h', 'u', ' ', 5, 'Z', 'h', 'u', 'a', ' ', 4, 'P', 'o', 'u', ' ', 4, 'T', 'o', 'u', ' ', 4, 'D', 'o', 'u', ' ', 5, 'K', 'a', 'n', 'g', ' ', 4, 'Z', 'h', 'e', ' ', 4, 'P', 'o', 'u', ' ', 3, 'F', 'u', ' ', 4, 'P', 'a', 'o', ' ', 3, 'B', 'a', ' ', 3, 'A', 'o', ' ', 3, 'Z', 'e', ' ', 5, 'T', 'u', 'a', 'n', ' ', 4, 'K', 'o', 'u', ' ', 4, 'L', 'u', 'n', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 3, 'H', 'u', ' ', 4, 'B', 'a', 'o', ' ', 5, 'B', 'i', 'n', 'g', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'P', 'e', 'n', 'g', ' ', 4, 'T', 'a', 'n', ' ', 3, 'P', 'u', ' ', 3, 'P', 'i', ' ', 4, 'T', 'a', 'i', ' ', 4, 'Y', 'a', 'o', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 4, 'Z', 'h', 'a', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 4, 'B', 'a', 'o', ' ', 3, 'H', 'e', ' ', 3, 'N', 'i', ' ', 3, 'Y', 'i', ' ', 3, 'D', 'i', ' ', 4, 'C', 'h', 'i', ' ', 3, 'P', 'i', ' ', 3, 'Z', 'a', ' ', 3, 'M', 'o', ' ', 3, 'M', 'o', ' ', 5, 'S', 'h', 'e', 'n', ' ', 3, 'Y', 'a', ' ', 5, 'C', 'h', 'o', 'u', ' ', 3, 'Q', 'u', ' ', 4, 'M', 'i', 'n', ' ', 4, 'C', 'h', 'u', ' ', 4, 'J', 'i', 'a', ' ', 3, 'F', 'u', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'D', 'a', 'n', ' ', 5, 'C', 'h', 'a', 'i', ' ', 3, 'M', 'u', ' ', 5, 'N', 'i', 'a', 'n', ' ', 3, 'L', 'a', ' ', 3, 'F', 'u', ' ', 4, 'P', 'a', 'o', ' ', 4, 'B', 'a', 'n', ' ', 4, 'P', 'a', 'i', ' ', 5, 'L', 'i', 'n', 'g', ' ', 3, 'N', 'a', ' ', 5, 'G', 'u', 'a', 'i', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 3, 'J', 'u', ' ', 4, 'T', 'u', 'o', ' ', 3, 'B', 'a', ' ', 4, 'T', 'u', 'o', ' ', 4, 'T', 'u', 'o', ' ', 3, 'A', 'o', ' ', 3, 'J', 'u', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 4, 'P', 'a', 'n', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 4, 'B', 'a', 'i', ' ', 4, 'B', 'a', 'i', ' ', 3, 'D', 'i', ' ', 3, 'N', 'i', ' ', 3, 'J', 'u', ' ', 4, 'K', 'u', 'o', ' ', 5, 'L', 'o', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 4, 'L', 'a', 'n', ' ', 5, 'N', 'i', 'n', 'g', ' ', 3, 'B', 'o', ' ', 3, 'Z', 'e', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'H', 'e', 'n', ' ', 4, 'G', 'u', 'a', ' ', 4, 'S', 'h', 'i', ' ', 4, 'J', 'i', 'e', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 4, 'N', 'i', 'n', ' ', 5, 'G', 'o', 'n', 'g', ' ', 5, 'G', 'o', 'n', 'g', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 6, 'S', 'h', 'u', 'a', 'n', ' ', 4, 'C', 'u', 'n', ' ', 4, 'Z', 'a', 'n', ' ', 4, 'K', 'a', 'o', ' ', 4, 'C', 'h', 'i', ' ', 4, 'X', 'i', 'e', ' ', 3, 'C', 'e', ' ', 4, 'H', 'u', 'i', ' ', 4, 'P', 'i', 'n', ' ', 6, 'Z', 'h', 'u', 'a', 'i', ' ', 4, 'S', 'h', 'i', ' ', 3, 'N', 'a', ' ', 3, 'B', 'o', ' ', 4, 'C', 'h', 'i', ' ', 4, 'G', 'u', 'a', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'K', 'u', 'o', ' ', 4, 'D', 'u', 'o', ' ', 4, 'D', 'u', 'o', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'Q', 'i', 'e', ' ', 3, 'A', 'n', ' ', 5, 'N', 'o', 'n', 'g', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 3, 'G', 'e', ' ', 5, 'J', 'i', 'a', 'o', ' ', 3, 'K', 'u', ' ', 5, 'D', 'o', 'n', 'g', ' ', 3, 'R', 'u', ' ', 5, 'T', 'i', 'a', 'o', ' ', 4, 'L', 'i', 'e', ' ', 4, 'Z', 'h', 'a', ' ', 3, 'L', 'u', ' ', 4, 'D', 'i', 'e', ' ', 3, 'W', 'a', ' ', 4, 'J', 'u', 'e', ' ', 8, 'M', 'u', 's', 'h', 'i', 'r', 'u', ' ', 3, 'J', 'u', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'L', 'u', 'a', 'n', ' ', 3, 'Y', 'a', ' ', 5, 'Z', 'h', 'u', 'a', ' ', 3, 'T', 'a', ' ', 4, 'X', 'i', 'e', ' ', 4, 'N', 'a', 'o', ' ', 5, 'D', 'a', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'o', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 3, 'J', 'i', ' ', 4, 'H', 'u', 'i', ' ', 4, 'X', 'u', 'n', ' ', 3, 'K', 'u', ' ', 3, 'A', 'i', ' ', 4, 'T', 'u', 'o', ' ', 4, 'N', 'u', 'o', ' ', 4, 'C', 'u', 'o', ' ', 3, 'B', 'o', ' ', 5, 'G', 'e', 'n', 'g', ' ', 3, 'T', 'i', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 4, 'S', 'u', 'o', ' ', 4, 'S', 'u', 'o', ' ', 5, 'K', 'e', 'n', 'g', ' ', 4, 'M', 'e', 'i', ' ', 5, 'L', 'o', 'n', 'g', ' ', 3, 'J', 'u', ' ', 5, 'P', 'e', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 5, 'T', 'i', 'n', 'g', ' ', 5, 'S', 'h', 'a', 'n', ' ', 4, 'N', 'u', 'o', ' ', 4, 'W', 'a', 'n', ' ', 4, 'X', 'i', 'e', ' ', 4, 'C', 'h', 'a', ' ', 5, 'F', 'e', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'o', ' ', 3, 'W', 'u', ' ', 4, 'J', 'u', 'n', ' ', 4, 'J', 'i', 'u', ' ', 5, 'T', 'o', 'n', 'g', ' ', 4, 'K', 'u', 'n', ' ', 4, 'H', 'u', 'o', ' ', 3, 'T', 'u', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 4, 'P', 'o', 'u', ' ', 3, 'L', 'e', ' ', 3, 'B', 'a', ' ', 4, 'H', 'a', 'n', ' ', 5, 'S', 'h', 'a', 'o', ' ', 4, 'N', 'i', 'e', ' ', 5, 'J', 'u', 'a', 'n', ' ', 3, 'Z', 'e', ' ', 5, 'S', 'o', 'n', 'g', ' ', 3, 'Y', 'e', ' ', 4, 'J', 'u', 'e', ' ', 3, 'B', 'u', ' ', 5, 'H', 'u', 'a', 'n', ' ', 3, 'B', 'u', ' ', 4, 'Z', 'u', 'n', ' ', 3, 'Y', 'i', ' ', 5, 'Z', 'h', 'a', 'i', ' ', 3, 'L', 'u', ' ', 4, 'S', 'o', 'u', ' ', 4, 'T', 'u', 'o', ' ', 4, 'L', 'a', 'o', ' ', 4, 'S', 'u', 'n', ' ', 5, 'B', 'a', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'H', 'u', 'a', 'n', ' ', 4, 'D', 'a', 'o', ' ', 4, 'W', 'a', 'n', ' ', 4, 'Q', 'i', 'n', ' ', 5, 'P', 'e', 'n', 'g', ' ', 4, 'S', 'h', 'e', ' ', 4, 'L', 'i', 'e', ' ', 4, 'M', 'i', 'n', ' ', 4, 'M', 'e', 'n', ' ', 3, 'F', 'u', ' ', 4, 'B', 'a', 'i', ' ', 3, 'J', 'u', ' ', 4, 'D', 'a', 'o', ' ', 3, 'W', 'o', ' ', 3, 'A', 'i', ' ', 5, 'J', 'u', 'a', 'n', ' ', 4, 'Y', 'u', 'e', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 5, 'C', 'h', 'e', 'n', ' ', 5, 'C', 'h', 'u', 'i', ' ', 4, 'J', 'i', 'e', ' ', 3, 'T', 'u', ' ', 4, 'B', 'e', 'n', ' ', 3, 'N', 'a', ' ', 5, 'N', 'i', 'a', 'n', ' ', 4, 'N', 'u', 'o', ' ', 3, 'Z', 'u', ' ', 3, 'W', 'o', ' ', 3, 'X', 'i', ' ', 5, 'X', 'i', 'a', 'n', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 5, 'D', 'i', 'a', 'n', ' ', 4, 'S', 'a', 'o', ' ', 4, 'L', 'u', 'n', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 5, 'G', 'a', 'n', 'g', ' ', 4, 'D', 'u', 'o', ' ', 5, 'S', 'h', 'o', 'u', ' ', 5, 'D', 'i', 'a', 'o', ' ', 4, 'P', 'o', 'u', ' ', 3, 'D', 'i', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 4, 'G', 'u', 'n', ' ', 3, 'J', 'i', ' ', 4, 'T', 'a', 'o', ' ', 4, 'Q', 'i', 'a', ' ', 3, 'Q', 'i', ' ', 4, 'P', 'a', 'i', ' ', 4, 'S', 'h', 'u', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'L', 'i', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 3, 'Y', 'a', ' ', 4, 'J', 'u', 'e', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 6, 'L', 'i', 'a', 'n', 'g', ' ', 4, 'G', 'u', 'a', ' ', 3, 'Y', 'i', ' ', 4, 'H', 'u', 'o', ' ', 5, 'S', 'h', 'a', 'n', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 4, 'L', 'u', 'e', ' ', 4, 'C', 'a', 'i', ' ', 4, 'T', 'a', 'n', ' ', 4, 'C', 'h', 'e', ' ', 5, 'B', 'i', 'n', 'g', ' ', 4, 'J', 'i', 'e', ' ', 3, 'T', 'i', ' ', 5, 'K', 'o', 'n', 'g', ' ', 4, 'T', 'u', 'i', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'C', 'u', 'o', ' ', 4, 'Z', 'o', 'u', ' ', 3, 'J', 'u', ' ', 5, 'T', 'i', 'a', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'K', 'e', 'n', ' ', 4, 'B', 'a', 'i', ' ', 5, 'S', 'h', 'o', 'u', ' ', 4, 'J', 'i', 'e', ' ', 3, 'L', 'u', ' ', 4, 'G', 'u', 'o', ' ', 5, 'H', 'a', 'b', 'a', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'D', 'a', 'n', ' ', 5, 'M', 'a', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'S', 'a', 'o', ' ', 5, 'G', 'u', 'a', 'n', ' ', 5, 'P', 'e', 'n', 'g', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'N', 'u', 'o', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 4, 'J', 'i', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'K', 'u', 'i', ' ', 4, 'N', 'a', 'n', ' ', 5, 'H', 'o', 'n', 'g', ' ', 4, 'R', 'o', 'u', ' ', 3, 'P', 'i', ' ', 4, 'W', 'e', 'i', ' ', 4, 'S', 'a', 'i', ' ', 4, 'Z', 'o', 'u', ' ', 5, 'X', 'u', 'a', 'n', ' ', 5, 'M', 'i', 'a', 'o', ' ', 3, 'T', 'i', ' ', 4, 'N', 'i', 'e', ' ', 4, 'C', 'h', 'a', ' ', 4, 'S', 'h', 'i', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 3, 'Y', 'i', ' ', 5, 'S', 'h', 'u', 'n', ' ', 5, 'H', 'e', 'n', 'g', ' ', 5, 'B', 'i', 'a', 'n', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 5, 'H', 'u', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'Z', 'u', 'a', 'n', ' ', 3, 'A', 'n', ' ', 3, 'X', 'u', ' ', 3, 'Y', 'a', ' ', 3, 'W', 'o', ' ', 3, 'K', 'e', ' ', 6, 'C', 'h', 'u', 'a', 'i', ' ', 3, 'J', 'i', ' ', 3, 'T', 'i', ' ', 3, 'L', 'a', ' ', 3, 'L', 'a', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 4, 'K', 'a', 'i', ' ', 4, 'J', 'i', 'u', ' ', 4, 'J', 'i', 'u', ' ', 3, 'T', 'u', ' ', 4, 'J', 'i', 'e', ' ', 4, 'H', 'u', 'i', ' ', 5, 'G', 'e', 'n', 'g', ' ', 6, 'C', 'h', 'o', 'n', 'g', ' ', 5, 'S', 'h', 'u', 'o', ' ', 4, 'S', 'h', 'e', ' ', 4, 'X', 'i', 'e', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 3, 'Y', 'e', ' ', 4, 'C', 'h', 'a', ' ', 4, 'Z', 'h', 'a', ' ', 4, 'B', 'e', 'i', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'L', 'a', 'n', ' ', 4, 'W', 'e', 'n', ' ', 4, 'Q', 'i', 'n', ' ', 5, 'C', 'h', 'a', 'n', ' ', 3, 'G', 'e', ' ', 4, 'L', 'o', 'u', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 5, 'G', 'e', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'G', 'o', 'u', ' ', 4, 'Q', 'i', 'n', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 4, 'Q', 'u', 'e', ' ', 5, 'C', 'h', 'o', 'u', ' ', 4, 'C', 'h', 'i', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 4, 'S', 'u', 'n', ' ', 4, 'S', 'u', 'n', ' ', 3, 'B', 'o', ' ', 4, 'C', 'h', 'u', ' ', 5, 'R', 'o', 'n', 'g', ' ', 5, 'B', 'e', 'n', 'g', ' ', 4, 'C', 'u', 'o', ' ', 4, 'S', 'a', 'o', ' ', 3, 'K', 'e', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'D', 'a', 'o', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'N', 'u', ' ', 4, 'X', 'i', 'e', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'S', 'o', 'u', ' ', 4, 'Q', 'i', 'u', ' ', 4, 'G', 'a', 'o', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'S', 'h', 'u', 'o', ' ', 5, 'S', 'a', 'n', 'g', ' ', 4, 'J', 'i', 'n', ' ', 4, 'M', 'i', 'e', ' ', 2, 'E', ' ', 5, 'C', 'h', 'u', 'i', ' ', 4, 'N', 'u', 'o', ' ', 5, 'S', 'h', 'a', 'n', ' ', 3, 'T', 'a', ' ', 4, 'J', 'i', 'e', ' ', 5, 'T', 'a', 'n', 'g', ' ', 4, 'P', 'a', 'n', ' ', 4, 'B', 'a', 'n', ' ', 3, 'D', 'a', ' ', 3, 'L', 'i', ' ', 4, 'T', 'a', 'o', ' ', 3, 'H', 'u', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'W', 'a', ' ', 4, 'X', 'i', 'a', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'W', 'e', 'n', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 5, 'T', 'i', 'a', 'n', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 2, 'E', ' ', 3, 'X', 'i', ' ', 4, 'N', 'u', 'o', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 4, 'C', 'h', 'a', ' ', 4, 'Z', 'h', 'a', ' ', 3, 'G', 'e', ' ', 3, 'W', 'u', ' ', 3, 'E', 'n', ' ', 4, 'S', 'h', 'e', ' ', 5, 'K', 'a', 'n', 'g', ' ', 4, 'S', 'h', 'e', ' ', 4, 'S', 'h', 'u', ' ', 4, 'B', 'a', 'i', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'B', 'i', 'n', ' ', 4, 'S', 'o', 'u', ' ', 4, 'T', 'a', 'n', ' ', 3, 'S', 'a', ' ', 5, 'C', 'h', 'a', 'n', ' ', 4, 'S', 'u', 'o', ' ', 5, 'L', 'i', 'a', 'o', ' ', 6, 'C', 'h', 'o', 'n', 'g', ' ', 7, 'C', 'h', 'u', 'a', 'n', 'g', ' ', 4, 'G', 'u', 'o', ' ', 5, 'B', 'i', 'n', 'g', ' ', 5, 'F', 'e', 'n', 'g', ' ', 6, 'S', 'h', 'u', 'a', 'i', ' ', 3, 'D', 'i', ' ', 3, 'Q', 'i', ' ', 4, 'S', 'o', 'u', ' ', 5, 'Z', 'h', 'a', 'i', ' ', 5, 'L', 'i', 'a', 'n', ' ', 5, 'T', 'a', 'n', 'g', ' ', 4, 'C', 'h', 'i', ' ', 5, 'G', 'u', 'a', 'n', ' ', 3, 'L', 'u', ' ', 4, 'L', 'u', 'o', ' ', 4, 'L', 'o', 'u', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 4, 'G', 'a', 'i', ' ', 3, 'H', 'u', ' ', 4, 'Z', 'h', 'a', ' ', 7, 'C', 'h', 'u', 'a', 'n', 'g', ' ', 5, 'T', 'a', 'n', 'g', ' ', 4, 'H', 'u', 'a', ' ', 4, 'C', 'u', 'i', ' ', 4, 'N', 'a', 'i', ' ', 3, 'M', 'o', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 4, 'G', 'u', 'i', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'A', 'o', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'N', 'i', 'e', ' ', 4, 'M', 'a', 'n', ' ', 5, 'S', 'h', 'a', 'n', ' ', 4, 'K', 'o', 'u', ' ', 4, 'S', 'h', 'u', ' ', 4, 'S', 'u', 'o', ' ', 5, 'T', 'u', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'o', ' ', 3, 'M', 'o', ' ', 3, 'M', 'o', ' ', 4, 'Z', 'h', 'e', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'K', 'e', 'n', 'g', ' ', 5, 'P', 'i', 'a', 'o', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'G', 'o', 'u', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'L', 'u', 'e', ' ', 3, 'J', 'i', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'J', 'u', 'e', ' ', 4, 'P', 'i', 'e', ' ', 4, 'P', 'i', 'e', ' ', 4, 'L', 'a', 'o', ' ', 4, 'D', 'u', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'R', 'u', 'a', 'n', ' ', 4, 'K', 'u', 'i', ' ', 4, 'Z', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 4, 'X', 'u', 'n', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 3, 'S', 'a', ' ', 4, 'N', 'a', 'o', ' ', 5, 'H', 'e', 'n', 'g', ' ', 3, 'S', 'i', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 3, 'D', 'a', ' ', 4, 'Z', 'u', 'n', ' ', 5, 'N', 'i', 'a', 'n', ' ', 4, 'L', 'i', 'n', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 4, 'H', 'u', 'i', ' ', 7, 'Z', 'h', 'u', 'a', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'o', ' ', 3, 'J', 'i', ' ', 4, 'C', 'a', 'o', ' ', 4, 'D', 'a', 'n', ' ', 4, 'D', 'a', 'n', ' ', 4, 'C', 'h', 'e', ' ', 3, 'B', 'o', ' ', 4, 'C', 'h', 'e', ' ', 4, 'J', 'u', 'e', ' ', 5, 'X', 'i', 'a', 'o', ' ', 5, 'L', 'i', 'a', 'o', ' ', 4, 'B', 'e', 'n', ' ', 3, 'F', 'u', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 3, 'B', 'o', ' ', 4, 'C', 'u', 'o', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 4, 'T', 'u', 'o', ' ', 3, 'P', 'u', ' ', 4, 'Q', 'i', 'n', ' ', 4, 'D', 'u', 'n', ' ', 5, 'N', 'i', 'a', 'n', ' ', 4, 'X', 'i', 'e', ' ', 3, 'L', 'u', ' ', 5, 'J', 'i', 'a', 'o', ' ', 5, 'C', 'u', 'a', 'n', ' ', 3, 'T', 'a', ' ', 4, 'H', 'a', 'n', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 5, 'Z', 'h', 'u', 'a', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'G', 'a', 'n', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 4, 'L', 'e', 'i', ' ', 4, 'K', 'u', 'o', ' ', 3, 'L', 'u', ' ', 5, 'S', 'h', 'a', 'n', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 3, 'Z', 'e', ' ', 3, 'P', 'u', ' ', 5, 'C', 'h', 'u', 'o', ' ', 3, 'J', 'i', ' ', 5, 'D', 'a', 'n', 'g', ' ', 4, 'S', 'u', 'o', ' ', 4, 'C', 'a', 'o', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 5, 'J', 'i', 'n', 'g', ' ', 5, 'H', 'u', 'a', 'n', ' ', 4, 'J', 'i', 'e', ' ', 4, 'Q', 'i', 'n', ' ', 5, 'K', 'u', 'a', 'i', ' ', 4, 'D', 'a', 'n', ' ', 3, 'X', 'i', ' ', 3, 'G', 'e', ' ', 3, 'P', 'i', ' ', 3, 'B', 'o', ' ', 3, 'A', 'o', ' ', 3, 'J', 'u', ' ', 3, 'Y', 'e', ' ', 5, 'M', 'a', 'n', 'g', ' ', 4, 'S', 'o', 'u', ' ', 3, 'M', 'i', ' ', 3, 'J', 'i', ' ', 4, 'T', 'a', 'i', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 4, 'D', 'a', 'o', ' ', 5, 'X', 'i', 'n', 'g', ' ', 4, 'L', 'a', 'n', ' ', 3, 'C', 'a', ' ', 3, 'J', 'u', ' ', 3, 'Y', 'e', ' ', 3, 'R', 'u', ' ', 3, 'Y', 'e', ' ', 3, 'Y', 'e', ' ', 3, 'N', 'i', ' ', 3, 'H', 'u', ' ', 3, 'J', 'i', ' ', 4, 'B', 'i', 'n', ' ', 5, 'N', 'i', 'n', 'g', ' ', 3, 'G', 'e', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'J', 'i', 'e', ' ', 4, 'K', 'u', 'o', ' ', 3, 'M', 'o', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'X', 'i', 'e', ' ', 4, 'L', 'i', 'e', ' ', 4, 'T', 'a', 'n', ' ', 4, 'B', 'a', 'i', ' ', 4, 'S', 'o', 'u', ' ', 3, 'L', 'u', ' ', 4, 'L', 'u', 'e', ' ', 4, 'R', 'a', 'o', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'P', 'a', 'n', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 4, 'L', 'e', 'i', ' ', 3, 'S', 'a', ' ', 4, 'S', 'h', 'u', ' ', 4, 'Z', 'a', 'n', ' ', 5, 'N', 'i', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'J', 'u', 'n', ' ', 4, 'H', 'u', 'o', ' ', 3, 'L', 'i', ' ', 3, 'L', 'a', ' ', 4, 'H', 'a', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 3, 'L', 'u', ' ', 5, 'L', 'o', 'n', 'g', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'Z', 'a', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'L', 'a', 'n', ' ', 4, 'S', 'a', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'M', 'e', 'i', ' ', 5, 'R', 'a', 'n', 'g', ' ', 5, 'C', 'h', 'a', 'n', ' ', 5, 'C', 'u', 'a', 'n', ' ', 3, 'X', 'i', ' ', 4, 'S', 'h', 'e', ' ', 4, 'L', 'u', 'o', ' ', 4, 'J', 'u', 'n', ' ', 3, 'M', 'i', ' ', 3, 'L', 'i', ' ', 4, 'Z', 'a', 'n', ' ', 5, 'L', 'u', 'a', 'n', ' ', 4, 'T', 'a', 'n', ' ', 5, 'Z', 'u', 'a', 'n', ' ', 3, 'L', 'i', ' ', 5, 'D', 'i', 'a', 'n', ' ', 3, 'W', 'a', ' ', 5, 'D', 'a', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'J', 'u', 'e', ' ', 4, 'L', 'a', 'n', ' ', 3, 'L', 'i', ' ', 5, 'N', 'a', 'n', 'g', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'G', 'u', 'i', ' ', 4, 'G', 'u', 'i', ' ', 3, 'Q', 'i', ' ', 4, 'X', 'i', 'n', ' ', 3, 'P', 'u', ' ', 4, 'S', 'u', 'i', ' ', 5, 'S', 'h', 'o', 'u', ' ', 4, 'K', 'a', 'o', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'G', 'a', 'i', ' ', 3, 'Y', 'i', ' ', 5, 'G', 'o', 'n', 'g', ' ', 4, 'G', 'a', 'n', ' ', 4, 'B', 'a', 'n', ' ', 5, 'F', 'a', 'n', 'g', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 3, 'B', 'o', ' ', 5, 'D', 'i', 'a', 'n', ' ', 4, 'K', 'o', 'u', ' ', 4, 'M', 'i', 'n', ' ', 3, 'W', 'u', ' ', 3, 'G', 'u', ' ', 3, 'H', 'e', ' ', 3, 'C', 'e', ' ', 5, 'X', 'i', 'a', 'o', ' ', 3, 'M', 'i', ' ', 4, 'C', 'h', 'u', ' ', 3, 'G', 'e', ' ', 3, 'D', 'i', ' ', 3, 'X', 'u', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'M', 'i', 'n', ' ', 5, 'C', 'h', 'e', 'n', ' ', 4, 'J', 'i', 'u', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 4, 'D', 'u', 'o', ' ', 3, 'Y', 'u', ' ', 4, 'C', 'h', 'i', ' ', 3, 'A', 'o', ' ', 4, 'B', 'a', 'i', ' ', 3, 'X', 'u', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'D', 'u', 'o', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'N', 'i', 'e', ' ', 3, 'B', 'i', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 5, 'D', 'i', 'a', 'n', ' ', 4, 'D', 'u', 'o', ' ', 3, 'Y', 'i', ' ', 4, 'G', 'a', 'n', ' ', 4, 'S', 'a', 'n', ' ', 3, 'K', 'e', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'D', 'u', 'n', ' ', 3, 'Q', 'i', ' ', 4, 'D', 'o', 'u', ' ', 5, 'X', 'i', 'a', 'o', ' ', 4, 'D', 'u', 'o', ' ', 5, 'J', 'i', 'a', 'o', ' ', 5, 'J', 'i', 'n', 'g', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 4, 'X', 'i', 'a', ' ', 4, 'M', 'i', 'n', ' ', 4, 'S', 'h', 'u', ' ', 3, 'A', 'i', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 3, 'A', 'i', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 3, 'D', 'i', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 3, 'F', 'u', ' ', 4, 'S', 'h', 'u', ' ', 5, 'L', 'i', 'a', 'o', ' ', 3, 'Q', 'u', ' ', 6, 'X', 'i', 'o', 'n', 'g', ' ', 3, 'X', 'i', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'S', 'e', 'n', ' ', 5, 'J', 'i', 'a', 'o', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 3, 'Y', 'i', ' ', 5, 'L', 'i', 'a', 'n', ' ', 3, 'B', 'i', ' ', 3, 'L', 'i', ' ', 5, 'X', 'i', 'a', 'o', ' ', 5, 'X', 'i', 'a', 'o', ' ', 4, 'W', 'e', 'n', ' ', 4, 'X', 'u', 'e', ' ', 3, 'Q', 'i', ' ', 3, 'Q', 'i', ' ', 5, 'Z', 'h', 'a', 'i', ' ', 4, 'B', 'i', 'n', ' ', 4, 'J', 'u', 'e', ' ', 5, 'Z', 'h', 'a', 'i', ' ', 4, 'F', 'e', 'i', ' ', 4, 'B', 'a', 'n', ' ', 4, 'B', 'a', 'n', ' ', 4, 'L', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 4, 'L', 'a', 'n', ' ', 4, 'W', 'e', 'i', ' ', 4, 'D', 'o', 'u', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 5, 'L', 'i', 'a', 'o', ' ', 4, 'J', 'i', 'a', ' ', 3, 'H', 'u', ' ', 4, 'X', 'i', 'e', ' ', 4, 'J', 'i', 'a', ' ', 3, 'Y', 'u', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 5, 'J', 'i', 'a', 'o', ' ', 3, 'W', 'o', ' ', 4, 'T', 'o', 'u', ' ', 4, 'C', 'h', 'u', ' ', 4, 'J', 'i', 'n', ' ', 4, 'C', 'h', 'i', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'F', 'u', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 3, 'Q', 'u', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 5, 'D', 'u', 'a', 'n', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 3, 'S', 'i', ' ', 4, 'X', 'i', 'n', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 4, 'Q', 'i', 'n', ' ', 4, 'L', 'i', 'n', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 4, 'C', 'h', 'u', ' ', 5, 'D', 'u', 'a', 'n', ' ', 4, 'Z', 'h', 'u', ' ', 5, 'F', 'a', 'n', 'g', ' ', 4, 'X', 'i', 'e', ' ', 5, 'H', 'a', 'n', 'g', ' ', 3, 'Y', 'u', ' ', 4, 'S', 'h', 'i', ' ', 4, 'P', 'e', 'i', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'M', 'y', 'e', ' ', 5, 'P', 'a', 'n', 'g', ' ', 3, 'Q', 'i', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 4, 'M', 'a', 'o', ' ', 3, 'L', 'u', ' ', 4, 'P', 'e', 'i', ' ', 3, 'P', 'i', ' ', 4, 'L', 'i', 'u', ' ', 3, 'F', 'u', ' ', 5, 'F', 'a', 'n', 'g', ' ', 5, 'X', 'u', 'a', 'n', ' ', 5, 'J', 'i', 'n', 'g', ' ', 5, 'J', 'i', 'n', 'g', ' ', 3, 'N', 'i', ' ', 3, 'Z', 'u', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 3, 'Y', 'i', ' ', 4, 'L', 'i', 'u', ' ', 5, 'S', 'h', 'a', 'o', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'E', 's', ' ', 3, 'Y', 'i', ' ', 3, 'Q', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'F', 'a', 'n', ' ', 5, 'P', 'i', 'a', 'o', ' ', 4, 'F', 'a', 'n', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 5, 'G', 'u', 'a', 'i', ' ', 4, 'S', 'u', 'i', ' ', 3, 'Y', 'u', ' ', 3, 'W', 'u', ' ', 3, 'J', 'i', ' ', 3, 'J', 'i', ' ', 3, 'J', 'i', ' ', 4, 'H', 'u', 'o', ' ', 3, 'R', 'i', ' ', 4, 'D', 'a', 'n', ' ', 4, 'J', 'i', 'u', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'Z', 'a', 'o', ' ', 4, 'X', 'i', 'e', ' ', 5, 'T', 'i', 'a', 'o', ' ', 4, 'X', 'u', 'n', ' ', 3, 'X', 'u', ' ', 3, 'X', 'u', ' ', 3, 'X', 'u', ' ', 4, 'G', 'a', 'n', ' ', 4, 'H', 'a', 'n', ' ', 4, 'T', 'a', 'i', ' ', 3, 'D', 'i', ' ', 3, 'X', 'u', ' ', 5, 'C', 'h', 'a', 'n', ' ', 4, 'S', 'h', 'i', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 4, 'S', 'h', 'i', ' ', 5, 'W', 'a', 'n', 'g', ' ', 4, 'M', 'i', 'n', ' ', 4, 'M', 'i', 'n', ' ', 4, 'T', 'u', 'n', ' ', 5, 'C', 'h', 'u', 'n', ' ', 3, 'W', 'u', ' ', 4, 'Y', 'u', 'n', ' ', 4, 'B', 'e', 'i', ' ', 4, 'A', 'n', 'g', ' ', 3, 'Z', 'e', ' ', 4, 'B', 'a', 'n', ' ', 4, 'J', 'i', 'e', ' ', 4, 'K', 'u', 'n', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 3, 'H', 'u', ' ', 5, 'F', 'a', 'n', 'g', ' ', 4, 'H', 'a', 'o', ' ', 4, 'G', 'u', 'i', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 5, 'X', 'u', 'a', 'n', ' ', 5, 'M', 'i', 'n', 'g', ' ', 4, 'H', 'u', 'n', ' ', 4, 'F', 'e', 'n', ' ', 4, 'Q', 'i', 'n', ' ', 3, 'H', 'u', ' ', 3, 'Y', 'i', ' ', 3, 'X', 'i', ' ', 4, 'X', 'i', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'Z', 'e', ' ', 5, 'F', 'a', 'n', 'g', ' ', 4, 'T', 'a', 'n', ' ', 5, 'S', 'h', 'e', 'n', ' ', 3, 'J', 'u', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 4, 'Z', 'a', 'n', ' ', 5, 'B', 'i', 'n', 'g', ' ', 5, 'X', 'i', 'n', 'g', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 5, 'X', 'u', 'a', 'n', ' ', 4, 'P', 'e', 'i', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 5, 'L', 'i', 'n', 'g', ' ', 5, 'C', 'h', 'u', 'n', ' ', 4, 'H', 'a', 'o', ' ', 4, 'M', 'e', 'i', ' ', 4, 'Z', 'u', 'o', ' ', 3, 'M', 'o', ' ', 5, 'B', 'i', 'a', 'n', ' ', 3, 'X', 'u', ' ', 4, 'H', 'u', 'n', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 4, 'S', 'h', 'i', ' ', 4, 'S', 'h', 'i', ' ', 3, 'Y', 'u', ' ', 4, 'F', 'e', 'i', ' ', 4, 'D', 'i', 'e', ' ', 4, 'M', 'a', 'o', ' ', 3, 'N', 'i', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 4, 'W', 'e', 'n', ' ', 5, 'D', 'o', 'n', 'g', ' ', 3, 'A', 'i', ' ', 5, 'B', 'i', 'n', 'g', ' ', 4, 'A', 'n', 'g', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 5, 'L', 'o', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'n', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 5, 'T', 'i', 'a', 'o', ' ', 5, 'C', 'h', 'a', 'o', ' ', 4, 'S', 'h', 'i', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 5, 'X', 'u', 'a', 'n', ' ', 4, 'K', 'u', 'i', ' ', 3, 'X', 'u', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'J', 'i', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'J', 'i', 'n', ' ', 6, 'S', 'h', 'a', 'n', 'g', ' ', 5, 'T', 'o', 'n', 'g', ' ', 5, 'H', 'o', 'n', 'g', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'G', 'a', 'i', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 5, 'S', 'h', 'a', 'i', ' ', 5, 'X', 'i', 'a', 'o', ' ', 3, 'Y', 'e', ' ', 4, 'Y', 'u', 'n', ' ', 4, 'H', 'u', 'i', ' ', 4, 'H', 'a', 'n', ' ', 4, 'H', 'a', 'n', ' ', 4, 'J', 'u', 'n', ' ', 4, 'W', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'K', 'u', 'n', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 3, 'X', 'i', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 3, 'B', 'u', ' ', 4, 'Z', 'h', 'e', ' ', 4, 'Z', 'h', 'e', ' ', 3, 'W', 'u', ' ', 4, 'H', 'a', 'n', ' ', 4, 'H', 'u', 'i', ' ', 4, 'H', 'a', 'o', ' ', 5, 'C', 'h', 'e', 'n', ' ', 4, 'W', 'a', 'n', ' ', 5, 'T', 'i', 'a', 'n', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 4, 'Z', 'u', 'i', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 3, 'P', 'u', ' ', 5, 'J', 'i', 'n', 'g', ' ', 3, 'X', 'i', ' ', 5, 'S', 'h', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 3, 'X', 'i', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 3, 'Q', 'i', ' ', 5, 'J', 'i', 'n', 'g', ' ', 4, 'G', 'u', 'i', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 3, 'Y', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'A', 'n', ' ', 4, 'W', 'a', 'n', ' ', 4, 'L', 'i', 'n', ' ', 6, 'L', 'i', 'a', 'n', 'g', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 5, 'W', 'a', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'o', ' ', 4, 'Z', 'a', 'n', ' ', 3, 'H', 'i', ' ', 5, 'X', 'u', 'a', 'n', ' ', 5, 'X', 'u', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 4, 'X', 'i', 'a', ' ', 4, 'Y', 'u', 'n', ' ', 4, 'H', 'u', 'i', ' ', 3, 'F', 'u', ' ', 4, 'M', 'i', 'n', ' ', 4, 'K', 'u', 'i', ' ', 3, 'H', 'e', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 3, 'D', 'u', ' ', 4, 'W', 'e', 'i', ' ', 4, 'S', 'h', 'u', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 4, 'M', 'a', 'o', ' ', 4, 'N', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'N', 'u', 'a', 'n', ' ', 3, 'A', 'n', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 5, 'C', 'h', 'u', 'n', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'S', 'u', 'o', ' ', 4, 'J', 'i', 'n', ' ', 5, 'M', 'i', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'K', 'a', 'i', ' ', 4, 'G', 'a', 'o', ' ', 5, 'W', 'e', 'n', 'g', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 3, 'Q', 'i', ' ', 4, 'H', 'a', 'o', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'L', 'i', ' ', 3, 'A', 'i', ' ', 3, 'J', 'i', ' ', 4, 'G', 'u', 'i', ' ', 4, 'M', 'e', 'n', ' ', 4, 'Z', 'a', 'n', ' ', 4, 'X', 'i', 'e', ' ', 4, 'H', 'a', 'o', ' ', 3, 'M', 'u', ' ', 3, 'M', 'o', ' ', 5, 'C', 'o', 'n', 'g', ' ', 3, 'N', 'i', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 4, 'H', 'u', 'i', ' ', 4, 'B', 'a', 'o', ' ', 4, 'H', 'a', 'n', ' ', 5, 'X', 'u', 'a', 'n', ' ', 6, 'C', 'h', 'u', 'a', 'n', ' ', 5, 'L', 'i', 'a', 'o', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'D', 'a', 'n', ' ', 5, 'J', 'i', 'n', 'g', ' ', 4, 'P', 'i', 'e', ' ', 4, 'L', 'i', 'n', ' ', 4, 'T', 'u', 'n', ' ', 3, 'X', 'i', ' ', 3, 'Y', 'i', ' ', 3, 'J', 'i', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 4, 'T', 'a', 'i', ' ', 3, 'Y', 'e', ' ', 3, 'Y', 'e', ' ', 3, 'L', 'i', ' ', 4, 'T', 'a', 'n', ' ', 5, 'T', 'o', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'o', ' ', 4, 'F', 'e', 'i', ' ', 4, 'Q', 'i', 'n', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 4, 'H', 'a', 'o', ' ', 3, 'Y', 'i', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 5, 'X', 'i', 'n', 'g', ' ', 4, 'S', 'e', 'n', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'B', 'a', 'o', ' ', 5, 'J', 'i', 'n', 'g', ' ', 5, 'Y', 'i', 'a', 'n', ' ', 3, 'A', 'i', ' ', 3, 'Y', 'e', ' ', 3, 'R', 'u', ' ', 4, 'S', 'h', 'u', ' ', 5, 'M', 'e', 'n', 'g', ' ', 4, 'X', 'u', 'n', ' ', 4, 'Y', 'a', 'o', ' ', 3, 'P', 'u', ' ', 3, 'L', 'i', ' ', 5, 'C', 'h', 'e', 'n', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 4, 'D', 'i', 'e', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'H', 'u', 'o', ' ', 3, 'L', 'u', ' ', 3, 'X', 'i', ' ', 5, 'R', 'o', 'n', 'g', ' ', 5, 'L', 'o', 'n', 'g', ' ', 5, 'N', 'a', 'n', 'g', ' ', 4, 'L', 'u', 'o', ' ', 5, 'L', 'u', 'a', 'n', ' ', 5, 'S', 'h', 'a', 'i', ' ', 5, 'T', 'a', 'n', 'g', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'C', 'h', 'u', ' ', 4, 'Y', 'u', 'e', ' ', 4, 'Y', 'u', 'e', ' ', 3, 'Q', 'u', ' ', 3, 'Y', 'i', ' ', 5, 'G', 'e', 'n', 'g', ' ', 3, 'Y', 'e', ' ', 3, 'H', 'u', ' ', 3, 'H', 'e', ' ', 4, 'S', 'h', 'u', ' ', 4, 'C', 'a', 'o', ' ', 4, 'C', 'a', 'o', ' ', 7, 'N', 'o', 'b', 'o', 'r', 'u', ' ', 4, 'M', 'a', 'n', ' ', 5, 'C', 'e', 'n', 'g', ' ', 5, 'C', 'e', 'n', 'g', ' ', 3, 'T', 'i', ' ', 4, 'Z', 'u', 'i', ' ', 4, 'C', 'a', 'n', ' ', 3, 'X', 'u', ' ', 4, 'H', 'u', 'i', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'Q', 'i', 'e', ' ', 4, 'F', 'e', 'n', ' ', 3, 'P', 'i', ' ', 4, 'Y', 'u', 'e', ' ', 4, 'Y', 'o', 'u', ' ', 5, 'R', 'u', 'a', 'n', ' ', 5, 'P', 'e', 'n', 'g', ' ', 4, 'B', 'a', 'n', ' ', 3, 'F', 'u', ' ', 5, 'L', 'i', 'n', 'g', ' ', 4, 'F', 'e', 'i', ' ', 3, 'Q', 'u', ' ', 3, 'N', 'u', ' ', 5, 'T', 'i', 'a', 'o', ' ', 5, 'S', 'h', 'u', 'o', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 5, 'L', 'a', 'n', 'g', ' ', 5, 'L', 'a', 'n', 'g', ' ', 5, 'J', 'u', 'a', 'n', ' ', 5, 'M', 'i', 'n', 'g', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 5, 'W', 'a', 'n', 'g', ' ', 4, 'T', 'u', 'n', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 3, 'J', 'i', ' ', 3, 'Q', 'i', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 5, 'W', 'a', 'n', 'g', ' ', 5, 'T', 'o', 'n', 'g', ' ', 5, 'L', 'a', 'n', 'g', ' ', 5, 'M', 'e', 'n', 'g', ' ', 5, 'L', 'o', 'n', 'g', ' ', 3, 'M', 'u', ' ', 5, 'D', 'e', 'n', 'g', ' ', 4, 'W', 'e', 'i', ' ', 3, 'M', 'o', ' ', 4, 'B', 'e', 'n', ' ', 4, 'Z', 'h', 'a', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'R', 'e', 'n', ' ', 3, 'B', 'a', ' ', 3, 'P', 'o', ' ', 4, 'D', 'u', 'o', ' ', 4, 'D', 'u', 'o', ' ', 4, 'D', 'a', 'o', ' ', 3, 'L', 'i', ' ', 4, 'Q', 'i', 'u', ' ', 3, 'J', 'i', ' ', 4, 'J', 'i', 'u', ' ', 3, 'B', 'i', ' ', 4, 'X', 'i', 'u', ' ', 5, 'T', 'i', 'n', 'g', ' ', 3, 'C', 'i', ' ', 4, 'S', 'h', 'a', ' ', 6, 'E', 'b', 'u', 'r', 'i', ' ', 3, 'Z', 'a', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 4, 'G', 'a', 'n', ' ', 3, 'W', 'u', ' ', 4, 'C', 'h', 'a', ' ', 5, 'S', 'h', 'a', 'n', ' ', 4, 'X', 'u', 'n', ' ', 4, 'F', 'a', 'n', ' ', 3, 'W', 'u', ' ', 3, 'Z', 'i', ' ', 3, 'L', 'i', ' ', 5, 'X', 'i', 'n', 'g', ' ', 4, 'C', 'a', 'i', ' ', 4, 'C', 'u', 'n', ' ', 4, 'R', 'e', 'n', ' ', 5, 'S', 'h', 'a', 'o', ' ', 4, 'T', 'u', 'o', ' ', 3, 'D', 'i', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 5, 'M', 'a', 'n', 'g', ' ', 4, 'C', 'h', 'i', ' ', 3, 'Y', 'i', ' ', 3, 'G', 'u', ' ', 5, 'G', 'o', 'n', 'g', ' ', 3, 'D', 'u', ' ', 3, 'Y', 'i', ' ', 3, 'Q', 'i', ' ', 4, 'S', 'h', 'u', ' ', 5, 'G', 'a', 'n', 'g', ' ', 5, 'T', 'i', 'a', 'o', ' ', 5, 'M', 'o', 'k', 'u', ' ', 5, 'S', 'o', 'm', 'a', ' ', 6, 'T', 'o', 'c', 'h', 'i', ' ', 4, 'L', 'a', 'i', ' ', 5, 'S', 'u', 'g', 'i', ' ', 5, 'M', 'a', 'n', 'g', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 3, 'M', 'a', ' ', 5, 'M', 'i', 'a', 'o', ' ', 3, 'S', 'i', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 5, 'H', 'a', 'n', 'g', ' ', 4, 'F', 'e', 'i', ' ', 4, 'B', 'e', 'i', ' ', 4, 'J', 'i', 'e', ' ', 5, 'D', 'o', 'n', 'g', ' ', 4, 'G', 'a', 'o', ' ', 4, 'Y', 'a', 'o', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'C', 'h', 'u', ' ', 4, 'Q', 'u', 'n', ' ', 3, 'P', 'a', ' ', 4, 'S', 'h', 'u', ' ', 4, 'H', 'u', 'a', ' ', 4, 'X', 'i', 'n', ' ', 5, 'C', 'h', 'o', 'u', ' ', 4, 'Z', 'h', 'u', ' ', 5, 'C', 'h', 'o', 'u', ' ', 5, 'S', 'o', 'n', 'g', ' ', 4, 'B', 'a', 'n', ' ', 5, 'S', 'o', 'n', 'g', ' ', 3, 'J', 'i', ' ', 4, 'Y', 'u', 'e', ' ', 4, 'J', 'i', 'n', ' ', 4, 'G', 'o', 'u', ' ', 3, 'J', 'i', ' ', 4, 'M', 'a', 'o', ' ', 3, 'P', 'i', ' ', 3, 'B', 'i', ' ', 5, 'W', 'a', 'n', 'g', ' ', 4, 'A', 'n', 'g', ' ', 5, 'F', 'a', 'n', 'g', ' ', 4, 'F', 'e', 'n', ' ', 3, 'Y', 'i', ' ', 3, 'F', 'u', ' ', 4, 'N', 'a', 'n', ' ', 3, 'X', 'i', ' ', 3, 'H', 'u', ' ', 3, 'Y', 'a', ' ', 4, 'D', 'o', 'u', ' ', 4, 'X', 'u', 'n', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'L', 'i', 'n', ' ', 4, 'R', 'u', 'i', ' ', 2, 'E', ' ', 4, 'M', 'e', 'i', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 4, 'G', 'u', 'o', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'C', 'o', 'n', 'g', ' ', 4, 'Y', 'u', 'n', ' ', 5, 'W', 'a', 'k', 'u', ' ', 4, 'D', 'o', 'u', ' ', 4, 'S', 'h', 'u', ' ', 4, 'Z', 'a', 'o', ' ', 3, 'L', 'i', ' ', 5, 'H', 'a', 'z', 'e', ' ', 5, 'J', 'i', 'a', 'n', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 6, 'M', 'a', 't', 's', 'u', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 5, 'F', 'e', 'n', 'g', ' ', 4, 'N', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'o', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'K', 'u', ' ', 5, 'P', 'i', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 3, 'X', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'G', 'u', 'a', 'i', ' ', 5, 'X', 'i', 'a', 'o', ' ', 4, 'J', 'i', 'a', ' ', 4, 'J', 'i', 'a', ' ', 4, 'G', 'o', 'u', ' ', 3, 'F', 'u', ' ', 3, 'M', 'o', ' ', 3, 'Y', 'i', ' ', 3, 'Y', 'e', ' ', 3, 'Y', 'e', ' ', 4, 'S', 'h', 'i', ' ', 4, 'N', 'i', 'e', ' ', 3, 'B', 'i', ' ', 4, 'D', 'u', 'o', ' ', 3, 'Y', 'i', ' ', 5, 'L', 'i', 'n', 'g', ' ', 5, 'B', 'i', 'n', 'g', ' ', 3, 'N', 'i', ' ', 3, 'L', 'a', ' ', 3, 'H', 'e', ' ', 4, 'P', 'a', 'n', ' ', 4, 'F', 'a', 'n', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 4, 'D', 'a', 'i', ' ', 3, 'C', 'i', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 3, 'F', 'u', ' ', 3, 'B', 'o', ' ', 4, 'M', 'o', 'u', ' ', 4, 'G', 'a', 'n', ' ', 3, 'Q', 'i', ' ', 4, 'R', 'a', 'n', ' ', 4, 'R', 'o', 'u', ' ', 4, 'M', 'a', 'o', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 5, 'S', 'o', 'n', 'g', ' ', 4, 'Z', 'h', 'e', ' ', 4, 'X', 'i', 'a', ' ', 4, 'Y', 'o', 'u', ' ', 5, 'S', 'h', 'e', 'n', ' ', 3, 'J', 'u', ' ', 4, 'T', 'u', 'o', ' ', 4, 'Z', 'u', 'o', ' ', 4, 'N', 'a', 'n', ' ', 5, 'N', 'i', 'n', 'g', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 3, 'D', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'Z', 'h', 'a', ' ', 4, 'C', 'h', 'a', ' ', 4, 'D', 'a', 'n', ' ', 3, 'G', 'u', ' ', 3, 'P', 'u', ' ', 4, 'J', 'i', 'u', ' ', 3, 'A', 'o', ' ', 3, 'F', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'B', 'o', ' ', 4, 'D', 'u', 'o', ' ', 3, 'K', 'e', ' ', 4, 'N', 'a', 'i', ' ', 4, 'Z', 'h', 'u', ' ', 3, 'B', 'i', ' ', 4, 'L', 'i', 'u', ' ', 5, 'C', 'h', 'a', 'i', ' ', 4, 'Z', 'h', 'a', ' ', 3, 'S', 'i', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'P', 'e', 'i', ' ', 4, 'S', 'h', 'i', ' ', 5, 'G', 'u', 'a', 'i', ' ', 4, 'C', 'h', 'a', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'J', 'u', 'e', ' ', 4, 'J', 'i', 'u', ' ', 4, 'S', 'h', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'L', 'i', 'u', ' ', 4, 'M', 'e', 'i', ' ', 4, 'H', 'o', 'y', ' ', 5, 'R', 'o', 'n', 'g', ' ', 4, 'Z', 'h', 'a', ' ', 5, 'B', 'i', 'a', 'o', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 4, 'J', 'i', 'e', ' ', 5, 'L', 'o', 'n', 'g', ' ', 5, 'D', 'o', 'n', 'g', ' ', 3, 'L', 'u', ' ', 6, 'S', 'a', 'y', 'n', 'g', ' ', 3, 'L', 'i', ' ', 4, 'L', 'a', 'n', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 4, 'S', 'h', 'u', ' ', 4, 'X', 'u', 'n', ' ', 6, 'S', 'h', 'u', 'a', 'n', ' ', 3, 'Q', 'i', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 3, 'Q', 'i', ' ', 3, 'L', 'i', ' ', 3, 'Y', 'i', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 3, 'L', 'i', ' ', 3, 'S', 'u', ' ', 4, 'G', 'u', 'a', ' ', 4, 'K', 'a', 'n', ' ', 5, 'B', 'i', 'n', 'g', ' ', 4, 'R', 'e', 'n', ' ', 5, 'X', 'i', 'a', 'o', ' ', 3, 'B', 'o', ' ', 4, 'R', 'e', 'n', ' ', 5, 'B', 'i', 'n', 'g', ' ', 3, 'Z', 'i', ' ', 5, 'C', 'h', 'o', 'u', ' ', 3, 'Y', 'i', ' ', 4, 'J', 'i', 'e', ' ', 3, 'X', 'u', ' ', 4, 'Z', 'h', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'Z', 'u', 'i', ' ', 3, 'E', 'r', ' ', 3, 'E', 'r', ' ', 4, 'Y', 'o', 'u', ' ', 3, 'F', 'a', ' ', 5, 'G', 'o', 'n', 'g', ' ', 4, 'K', 'a', 'o', ' ', 4, 'L', 'a', 'o', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 3, 'L', 'i', ' ', 4, 'Y', 'i', 'n', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 3, 'H', 'e', ' ', 4, 'G', 'e', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'C', 'h', 'i', ' ', 3, 'G', 'e', ' ', 4, 'Z', 'a', 'i', ' ', 5, 'L', 'u', 'a', 'n', ' ', 3, 'F', 'u', ' ', 4, 'J', 'i', 'e', ' ', 5, 'H', 'a', 'n', 'g', ' ', 4, 'G', 'u', 'i', ' ', 4, 'T', 'a', 'o', ' ', 6, 'G', 'u', 'a', 'n', 'g', ' ', 4, 'W', 'e', 'i', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 3, 'R', 'u', ' ', 3, 'A', 'n', ' ', 3, 'A', 'n', ' ', 5, 'J', 'u', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 3, 'K', 'u', ' ', 4, 'Z', 'h', 'i', ' ', 6, 'Q', 'i', 'o', 'n', 'g', ' ', 5, 'T', 'o', 'n', 'g', ' ', 5, 'S', 'a', 'n', 'g', ' ', 5, 'S', 'a', 'n', 'g', ' ', 5, 'H', 'u', 'a', 'n', ' ', 4, 'J', 'i', 'e', ' ', 4, 'J', 'i', 'u', ' ', 4, 'X', 'u', 'e', ' ', 4, 'D', 'u', 'o', ' ', 5, 'Z', 'h', 'u', 'i', ' ', 3, 'Y', 'u', ' ', 4, 'Z', 'a', 'n', ' ', 6, 'K', 'a', 's', 'e', 'i', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 5, 'M', 'a', 's', 'u', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 3, 'Y', 'a', ' ', 4, 'N', 'a', 'o', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 5, 'D', 'a', 'n', 'g', ' ', 3, 'Q', 'i', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 4, 'H', 'u', 'a', ' ', 5, 'K', 'u', 'a', 'i', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 7, 'Z', 'h', 'u', 'a', 'n', 'g', ' ', 4, 'X', 'u', 'n', ' ', 4, 'S', 'u', 'o', ' ', 4, 'S', 'h', 'a', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 4, 'B', 'e', 'i', ' ', 5, 'T', 'i', 'n', 'g', ' ', 4, 'G', 'u', 'a', ' ', 5, 'J', 'i', 'n', 'g', ' ', 3, 'B', 'o', ' ', 4, 'B', 'e', 'n', ' ', 3, 'F', 'u', ' ', 4, 'R', 'u', 'i', ' ', 5, 'T', 'o', 'n', 'g', ' ', 4, 'J', 'u', 'e', ' ', 3, 'X', 'i', ' ', 5, 'L', 'a', 'n', 'g', ' ', 4, 'L', 'i', 'u', ' ', 5, 'F', 'e', 'n', 'g', ' ', 3, 'Q', 'i', ' ', 4, 'W', 'e', 'n', ' ', 4, 'J', 'u', 'n', ' ', 4, 'G', 'a', 'n', ' ', 3, 'C', 'u', ' ', 6, 'L', 'i', 'a', 'n', 'g', ' ', 4, 'Q', 'i', 'u', ' ', 5, 'T', 'i', 'n', 'g', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'M', 'e', 'i', ' ', 5, 'B', 'a', 'n', 'g', ' ', 5, 'L', 'o', 'n', 'g', ' ', 5, 'P', 'e', 'n', 'g', ' ', 7, 'Z', 'h', 'u', 'a', 'n', 'g', ' ', 3, 'D', 'i', ' ', 5, 'X', 'u', 'a', 'n', ' ', 3, 'T', 'u', ' ', 4, 'Z', 'a', 'o', ' ', 3, 'A', 'o', ' ', 3, 'G', 'u', ' ', 3, 'B', 'i', ' ', 3, 'D', 'i', ' ', 4, 'H', 'a', 'n', ' ', 3, 'Z', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'R', 'e', 'n', ' ', 4, 'B', 'e', 'i', ' ', 5, 'G', 'e', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'H', 'u', 'a', 'n', ' ', 4, 'W', 'a', 'n', ' ', 4, 'N', 'u', 'o', ' ', 4, 'J', 'i', 'a', ' ', 5, 'T', 'i', 'a', 'o', ' ', 3, 'J', 'i', ' ', 5, 'X', 'i', 'a', 'o', ' ', 3, 'L', 'u', ' ', 5, 'H', 'u', 'a', 'n', ' ', 5, 'S', 'h', 'a', 'o', ' ', 4, 'C', 'e', 'n', ' ', 4, 'F', 'e', 'n', ' ', 5, 'S', 'o', 'n', 'g', ' ', 5, 'M', 'e', 'n', 'g', ' ', 3, 'W', 'u', ' ', 3, 'L', 'i', ' ', 3, 'L', 'i', ' ', 4, 'D', 'o', 'u', ' ', 4, 'C', 'e', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'S', 'u', 'o', ' ', 3, 'J', 'u', ' ', 3, 'T', 'i', ' ', 4, 'J', 'i', 'e', ' ', 4, 'K', 'u', 'n', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 4, 'S', 'h', 'u', ' ', 5, 'C', 'h', 'a', 'n', ' ', 4, 'F', 'a', 'n', ' ', 4, 'W', 'e', 'i', ' ', 5, 'J', 'i', 'n', 'g', ' ', 3, 'L', 'i', ' ', 5, 'B', 'i', 'n', 'g', ' ', 7, 'F', 'u', 'm', 'o', 't', 'o', ' ', 8, 'S', 'h', 'i', 'k', 'i', 'm', 'i', ' ', 4, 'T', 'a', 'o', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'L', 'a', 'i', ' ', 5, 'L', 'i', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 5, 'L', 'i', 'n', 'g', ' ', 3, 'L', 'i', ' ', 3, 'Q', 'i', ' ', 5, 'B', 'i', 'n', 'g', ' ', 5, 'Z', 'h', 'u', 'n', ' ', 5, 'C', 'o', 'n', 'g', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'M', 'i', 'a', 'n', ' ', 3, 'Q', 'i', ' ', 3, 'Q', 'i', ' ', 4, 'C', 'a', 'i', ' ', 4, 'G', 'u', 'n', ' ', 5, 'C', 'h', 'a', 'n', ' ', 3, 'T', 'e', ' ', 4, 'F', 'e', 'i', ' ', 4, 'P', 'a', 'i', ' ', 5, 'B', 'a', 'n', 'g', ' ', 4, 'P', 'o', 'u', ' ', 4, 'H', 'u', 'n', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 4, 'Z', 'a', 'o', ' ', 3, 'J', 'i', ' ', 3, 'L', 'i', ' ', 5, 'P', 'e', 'n', 'g', ' ', 3, 'Y', 'u', ' ', 3, 'Y', 'u', ' ', 3, 'G', 'u', ' ', 4, 'H', 'u', 'n', ' ', 5, 'D', 'o', 'n', 'g', ' ', 5, 'T', 'a', 'n', 'g', ' ', 5, 'G', 'a', 'n', 'g', ' ', 5, 'W', 'a', 'n', 'g', ' ', 3, 'D', 'i', ' ', 3, 'X', 'i', ' ', 4, 'F', 'a', 'n', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 3, 'Q', 'i', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 4, 'S', 'e', 'n', ' ', 4, 'R', 'e', 'n', ' ', 5, 'C', 'h', 'u', 'i', ' ', 5, 'L', 'e', 'n', 'g', ' ', 3, 'Q', 'i', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 3, 'F', 'u', ' ', 3, 'K', 'e', ' ', 4, 'L', 'a', 'i', ' ', 4, 'Z', 'o', 'u', ' ', 4, 'Z', 'o', 'u', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 5, 'G', 'u', 'a', 'n', ' ', 4, 'F', 'e', 'n', ' ', 4, 'F', 'e', 'n', ' ', 5, 'C', 'h', 'e', 'n', ' ', 6, 'Q', 'i', 'o', 'n', 'g', ' ', 4, 'N', 'i', 'e', ' ', 4, 'W', 'a', 'n', ' ', 4, 'G', 'u', 'o', ' ', 3, 'L', 'u', ' ', 4, 'H', 'a', 'o', ' ', 4, 'J', 'i', 'e', ' ', 3, 'Y', 'i', ' ', 5, 'C', 'h', 'o', 'u', ' ', 3, 'J', 'u', ' ', 3, 'J', 'u', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 4, 'Z', 'u', 'o', ' ', 6, 'L', 'i', 'a', 'n', 'g', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'Z', 'h', 'u', 'i', ' ', 3, 'Y', 'a', ' ', 3, 'J', 'u', ' ', 4, 'B', 'e', 'i', ' ', 5, 'J', 'i', 'a', 'o', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 3, 'Z', 'i', ' ', 4, 'B', 'i', 'n', ' ', 5, 'P', 'e', 'n', 'g', ' ', 5, 'D', 'i', 'n', 'g', ' ', 4, 'C', 'h', 'u', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 7, 'K', 'u', 'n', 'u', 'g', 'i', ' ', 7, 'M', 'o', 'm', 'i', 'j', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'G', 'u', 'i', ' ', 3, 'X', 'i', ' ', 3, 'D', 'u', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 7, 'K', 'u', 'n', 'u', 'g', 'i', ' ', 5, 'S', 'o', 'k', 'o', ' ', 6, 'S', 'h', 'i', 'd', 'e', ' ', 4, 'L', 'u', 'o', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'K', 'e', 'n', ' ', 6, 'M', 'y', 'e', 'n', 'g', ' ', 5, 'T', 'a', 'f', 'u', ' ', 5, 'P', 'e', 'n', 'g', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 4, 'T', 'u', 'o', ' ', 4, 'S', 'e', 'n', ' ', 4, 'D', 'u', 'o', ' ', 3, 'Y', 'e', ' ', 4, 'F', 'o', 'u', ' ', 4, 'W', 'e', 'i', ' ', 4, 'W', 'e', 'i', ' ', 5, 'D', 'u', 'a', 'n', ' ', 4, 'J', 'i', 'a', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 5, 'S', 'h', 'e', 'n', ' ', 3, 'X', 'i', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 6, 'C', 'h', 'u', 'a', 'n', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 5, 'C', 'h', 'u', 'n', ' ', 3, 'Y', 'u', ' ', 3, 'H', 'e', ' ', 4, 'Z', 'h', 'a', ' ', 3, 'W', 'o', ' ', 5, 'P', 'i', 'a', 'n', ' ', 3, 'B', 'i', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'H', 'u', 'o', ' ', 3, 'X', 'u', ' ', 4, 'R', 'u', 'o', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 3, 'L', 'a', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'B', 'e', 'n', ' ', 4, 'H', 'u', 'n', ' ', 4, 'K', 'u', 'i', ' ', 4, 'J', 'i', 'e', ' ', 4, 'K', 'u', 'i', ' ', 3, 'S', 'i', ' ', 5, 'F', 'e', 'n', 'g', ' ', 4, 'X', 'i', 'e', ' ', 4, 'T', 'u', 'o', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'M', 'u', ' ', 4, 'M', 'a', 'o', ' ', 4, 'C', 'h', 'u', ' ', 3, 'H', 'u', ' ', 3, 'H', 'u', ' ', 5, 'L', 'i', 'a', 'n', ' ', 5, 'L', 'e', 'n', 'g', ' ', 5, 'T', 'i', 'n', 'g', ' ', 4, 'N', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'M', 'e', 'i', ' ', 5, 'S', 'o', 'n', 'g', ' ', 5, 'X', 'u', 'a', 'n', ' ', 5, 'X', 'u', 'a', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 5, 'P', 'i', 'a', 'n', ' ', 3, 'Y', 'e', ' ', 3, 'J', 'i', ' ', 4, 'J', 'i', 'e', ' ', 3, 'Y', 'e', ' ', 4, 'C', 'h', 'u', ' ', 5, 'S', 'h', 'u', 'n', ' ', 3, 'Y', 'u', ' ', 4, 'C', 'o', 'u', ' ', 4, 'W', 'e', 'i', ' ', 4, 'M', 'e', 'i', ' ', 3, 'D', 'i', ' ', 3, 'J', 'i', ' ', 4, 'J', 'i', 'e', ' ', 4, 'K', 'a', 'i', ' ', 4, 'Q', 'i', 'u', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'R', 'o', 'u', ' ', 5, 'H', 'e', 'n', 'g', ' ', 4, 'L', 'o', 'u', ' ', 3, 'L', 'e', ' ', 6, 'H', 'a', 'z', 'o', 'u', ' ', 8, 'K', 'a', 't', 's', 'u', 'r', 'a', ' ', 4, 'P', 'i', 'n', ' ', 5, 'M', 'u', 'r', 'o', ' ', 4, 'G', 'a', 'i', ' ', 4, 'T', 'a', 'n', ' ', 4, 'L', 'a', 'n', ' ', 4, 'Y', 'u', 'n', ' ', 3, 'Y', 'u', ' ', 5, 'C', 'h', 'e', 'n', ' ', 3, 'L', 'u', ' ', 3, 'J', 'u', ' ', 7, 'S', 'a', 'k', 'a', 'k', 'i', ' ', 3, 'P', 'i', ' ', 4, 'X', 'i', 'e', ' ', 4, 'J', 'i', 'a', ' ', 3, 'Y', 'i', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 3, 'F', 'u', ' ', 4, 'N', 'a', 'i', ' ', 3, 'M', 'i', ' ', 5, 'L', 'a', 'n', 'g', ' ', 5, 'R', 'o', 'n', 'g', ' ', 3, 'G', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'J', 'u', ' ', 3, 'T', 'a', ' ', 4, 'Y', 'a', 'o', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 5, 'B', 'a', 'n', 'g', ' ', 4, 'S', 'h', 'a', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 3, 'Z', 'i', ' ', 5, 'M', 'i', 'n', 'g', ' ', 3, 'S', 'u', ' ', 4, 'J', 'i', 'a', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'J', 'i', 'e', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 4, 'G', 'a', 'n', ' ', 4, 'F', 'e', 'i', ' ', 4, 'Z', 'h', 'a', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 3, 'M', 'a', ' ', 4, 'S', 'u', 'n', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'X', 'i', 'e', ' ', 5, 'R', 'o', 'n', 'g', ' ', 4, 'S', 'h', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'C', 'u', 'i', ' ', 4, 'Y', 'u', 'n', ' ', 5, 'T', 'i', 'n', 'g', ' ', 4, 'L', 'i', 'u', ' ', 5, 'R', 'o', 'n', 'g', ' ', 5, 'T', 'a', 'n', 'g', ' ', 4, 'Q', 'u', 'e', ' ', 5, 'Z', 'h', 'a', 'i', ' ', 3, 'S', 'i', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 3, 'T', 'a', ' ', 3, 'K', 'e', ' ', 3, 'X', 'i', ' ', 3, 'G', 'u', ' ', 3, 'Q', 'i', ' ', 4, 'K', 'a', 'o', ' ', 4, 'G', 'a', 'o', ' ', 4, 'S', 'u', 'n', ' ', 4, 'P', 'a', 'n', ' ', 4, 'T', 'a', 'o', ' ', 3, 'G', 'e', ' ', 4, 'X', 'u', 'n', ' ', 5, 'D', 'i', 'a', 'n', ' ', 4, 'N', 'o', 'u', ' ', 3, 'J', 'i', ' ', 5, 'S', 'h', 'u', 'o', ' ', 4, 'G', 'o', 'u', ' ', 5, 'C', 'h', 'u', 'i', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 4, 'C', 'h', 'a', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'H', 'u', 'a', 'i', ' ', 4, 'M', 'e', 'i', ' ', 3, 'X', 'u', ' ', 5, 'G', 'a', 'n', 'g', ' ', 4, 'G', 'a', 'o', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 4, 'T', 'u', 'o', ' ', 6, 'H', 'a', 's', 'h', 'i', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 5, 'D', 'i', 'a', 'n', ' ', 4, 'J', 'i', 'a', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'Z', 'u', 'i', ' ', 6, 'K', 'a', 's', 'h', 'i', ' ', 4, 'O', 'r', 'i', ' ', 4, 'B', 'i', 'n', ' ', 4, 'Z', 'h', 'u', ' ', 3, 'X', 'i', ' ', 3, 'Q', 'i', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'H', 'u', 'i', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'G', 'u', 'o', ' ', 4, 'G', 'a', 'i', ' ', 4, 'G', 'a', 'i', ' ', 5, 'T', 'u', 'a', 'n', ' ', 4, 'H', 'u', 'a', ' ', 3, 'C', 'u', ' ', 4, 'S', 'e', 'n', ' ', 4, 'C', 'u', 'i', ' ', 5, 'B', 'e', 'n', 'g', ' ', 4, 'Y', 'o', 'u', ' ', 3, 'H', 'u', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 3, 'H', 'u', ' ', 5, 'H', 'u', 'a', 'n', ' ', 4, 'K', 'u', 'i', ' ', 3, 'Y', 'i', ' ', 4, 'N', 'i', 'e', ' ', 4, 'G', 'a', 'o', ' ', 5, 'K', 'a', 'n', 'g', ' ', 4, 'G', 'u', 'i', ' ', 4, 'G', 'u', 'i', ' ', 4, 'C', 'a', 'o', ' ', 4, 'M', 'a', 'n', ' ', 4, 'J', 'i', 'n', ' ', 3, 'D', 'i', ' ', 7, 'Z', 'h', 'u', 'a', 'n', 'g', ' ', 3, 'L', 'e', ' ', 5, 'L', 'a', 'n', 'g', ' ', 5, 'C', 'h', 'e', 'n', ' ', 5, 'C', 'o', 'n', 'g', ' ', 3, 'L', 'i', ' ', 4, 'X', 'i', 'u', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 7, 'S', 'h', 'u', 'a', 'n', 'g', ' ', 4, 'F', 'a', 'n', ' ', 5, 'T', 'o', 'n', 'g', ' ', 5, 'G', 'u', 'a', 'n', ' ', 3, 'J', 'i', ' ', 4, 'S', 'u', 'o', ' ', 4, 'L', 'e', 'i', ' ', 3, 'L', 'u', ' ', 6, 'L', 'i', 'a', 'n', 'g', ' ', 3, 'M', 'i', ' ', 4, 'L', 'o', 'u', ' ', 5, 'C', 'h', 'a', 'o', ' ', 3, 'S', 'u', ' ', 3, 'K', 'e', ' ', 4, 'S', 'h', 'u', ' ', 5, 'T', 'a', 'n', 'g', ' ', 5, 'B', 'i', 'a', 'o', ' ', 3, 'L', 'u', ' ', 4, 'J', 'i', 'u', ' ', 4, 'S', 'h', 'u', ' ', 4, 'Z', 'h', 'a', ' ', 4, 'S', 'h', 'u', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 4, 'M', 'e', 'n', ' ', 3, 'M', 'o', ' ', 5, 'N', 'i', 'a', 'o', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 5, 'T', 'i', 'a', 'o', ' ', 5, 'P', 'e', 'n', 'g', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'S', 'h', 'a', ' ', 3, 'X', 'i', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 5, 'H', 'e', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'C', 'o', 'n', 'g', ' ', 7, 'H', 'o', 'k', 'u', 's', 'o', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 5, 'T', 'a', 'r', 'a', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 3, 'E', 'r', ' ', 4, 'X', 'i', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 4, 'Z', 'u', 'i', ' ', 5, 'C', 'o', 'n', 'g', ' ', 3, 'P', 'u', ' ', 4, 'S', 'h', 'u', ' ', 4, 'H', 'u', 'a', ' ', 4, 'K', 'u', 'i', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 4, 'Z', 'u', 'n', ' ', 4, 'Y', 'u', 'e', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 3, 'X', 'i', ' ', 4, 'X', 'u', 'n', ' ', 5, 'D', 'i', 'a', 'n', ' ', 3, 'F', 'a', ' ', 4, 'G', 'a', 'n', ' ', 3, 'M', 'o', ' ', 3, 'W', 'u', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 4, 'N', 'a', 'o', ' ', 4, 'L', 'i', 'n', ' ', 4, 'L', 'i', 'u', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'R', 'u', 'n', ' ', 4, 'F', 'a', 'n', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 4, 'T', 'u', 'o', ' ', 4, 'L', 'a', 'o', ' ', 4, 'Y', 'u', 'n', ' ', 5, 'S', 'h', 'u', 'n', ' ', 4, 'T', 'u', 'i', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 5, 'T', 'a', 'n', 'g', ' ', 5, 'M', 'e', 'n', 'g', ' ', 3, 'J', 'u', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 3, 'S', 'u', ' ', 4, 'J', 'u', 'e', ' ', 4, 'J', 'u', 'e', ' ', 4, 'T', 'a', 'n', ' ', 4, 'H', 'u', 'i', ' ', 3, 'J', 'i', ' ', 4, 'N', 'u', 'o', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 4, 'T', 'u', 'o', ' ', 5, 'N', 'i', 'n', 'g', ' ', 4, 'R', 'u', 'i', ' ', 4, 'Z', 'h', 'u', ' ', 7, 'C', 'h', 'u', 'a', 'n', 'g', ' ', 5, 'Z', 'e', 'n', 'g', ' ', 4, 'F', 'e', 'n', ' ', 6, 'Q', 'i', 'o', 'n', 'g', ' ', 4, 'R', 'a', 'n', ' ', 5, 'H', 'e', 'n', 'g', ' ', 4, 'C', 'e', 'n', ' ', 3, 'G', 'u', ' ', 4, 'L', 'i', 'u', ' ', 4, 'L', 'a', 'o', ' ', 4, 'G', 'a', 'o', ' ', 4, 'C', 'h', 'u', ' ', 5, 'Z', 'u', 's', 'a', ' ', 5, 'N', 'u', 'd', 'e', ' ', 3, 'C', 'a', ' ', 4, 'S', 'a', 'n', ' ', 3, 'J', 'i', ' ', 4, 'D', 'o', 'u', ' ', 5, 'S', 'h', 'o', 'u', ' ', 3, 'L', 'u', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 3, 'T', 'a', ' ', 4, 'S', 'h', 'u', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 4, 'T', 'a', 'n', ' ', 4, 'L', 'i', 'n', ' ', 5, 'N', 'o', 'n', 'g', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'X', 'i', ' ', 4, 'S', 'u', 'i', ' ', 5, 'S', 'h', 'a', 'n', ' ', 4, 'Z', 'u', 'i', ' ', 5, 'X', 'u', 'a', 'n', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 4, 'G', 'a', 'n', ' ', 3, 'J', 'u', ' ', 4, 'Z', 'u', 'i', ' ', 3, 'Y', 'i', ' ', 4, 'Q', 'i', 'n', ' ', 3, 'P', 'u', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'L', 'e', 'i', ' ', 5, 'F', 'e', 'n', 'g', ' ', 4, 'H', 'u', 'i', ' ', 5, 'D', 'a', 'n', 'g', ' ', 3, 'J', 'i', ' ', 4, 'S', 'u', 'i', ' ', 3, 'B', 'o', ' ', 3, 'B', 'i', ' ', 5, 'D', 'i', 'n', 'g', ' ', 4, 'C', 'h', 'u', ' ', 5, 'Z', 'h', 'u', 'a', ' ', 5, 'K', 'u', 'a', 'i', ' ', 3, 'J', 'i', ' ', 4, 'J', 'i', 'e', ' ', 4, 'J', 'i', 'a', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 4, 'Z', 'h', 'e', ' ', 5, 'J', 'i', 'a', 'n', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 4, 'D', 'a', 'o', ' ', 3, 'Y', 'i', ' ', 5, 'B', 'i', 'a', 'o', ' ', 5, 'S', 'o', 'n', 'g', ' ', 4, 'S', 'h', 'e', ' ', 4, 'L', 'i', 'n', ' ', 7, 'K', 'u', 'n', 'u', 'g', 'i', ' ', 4, 'C', 'h', 'a', ' ', 5, 'M', 'e', 'n', 'g', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'T', 'a', 'o', ' ', 4, 'T', 'a', 'i', ' ', 5, 'M', 'i', 'a', 'n', ' ', 3, 'Q', 'i', ' ', 5, 'T', 'o', 'a', 'n', ' ', 4, 'B', 'i', 'n', ' ', 4, 'H', 'u', 'o', ' ', 3, 'J', 'i', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 3, 'M', 'i', ' ', 5, 'N', 'i', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 4, 'G', 'a', 'o', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'E', 'r', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'Q', 'i', ' ', 3, 'M', 'i', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 4, 'G', 'u', 'i', ' ', 5, 'C', 'h', 'u', 'n', ' ', 3, 'J', 'i', ' ', 4, 'K', 'u', 'i', ' ', 3, 'P', 'o', ' ', 5, 'D', 'e', 'n', 'g', ' ', 4, 'C', 'h', 'u', ' ', 5, 'M', 'i', 'a', 'n', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'Z', 'h', 'i', ' ', 6, 'G', 'u', 'a', 'n', 'g', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'L', 'e', 'i', ' ', 4, 'L', 'e', 'i', ' ', 3, 'S', 'a', ' ', 3, 'L', 'u', ' ', 3, 'L', 'i', ' ', 5, 'C', 'u', 'a', 'n', ' ', 3, 'L', 'u', ' ', 4, 'M', 'i', 'e', ' ', 4, 'H', 'u', 'i', ' ', 3, 'O', 'u', ' ', 3, 'L', 'u', ' ', 4, 'J', 'i', 'e', ' ', 4, 'G', 'a', 'o', ' ', 3, 'D', 'u', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 3, 'L', 'i', ' ', 4, 'F', 'e', 'i', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 4, 'S', 'o', 'u', ' ', 5, 'L', 'i', 'a', 'n', ' ', 5, 'T', 'a', 'm', 'o', ' ', 4, 'C', 'h', 'u', ' ', 4, 'Z', 'h', 'u', ' ', 3, 'L', 'u', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'L', 'i', ' ', 4, 'Z', 'h', 'u', ' ', 5, 'C', 'h', 'e', 'n', ' ', 4, 'J', 'i', 'e', ' ', 2, 'E', ' ', 3, 'S', 'u', ' ', 5, 'H', 'u', 'a', 'i', ' ', 4, 'N', 'i', 'e', ' ', 3, 'Y', 'u', ' ', 5, 'L', 'o', 'n', 'g', ' ', 4, 'L', 'a', 'i', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'K', 'w', 'i', ' ', 3, 'J', 'u', ' ', 5, 'X', 'i', 'a', 'o', ' ', 5, 'L', 'i', 'n', 'g', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'Y', 'o', 'u', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 5, 'N', 'o', 'n', 'g', ' ', 3, 'B', 'o', ' ', 5, 'C', 'h', 'a', 'n', ' ', 4, 'L', 'a', 'n', ' ', 3, 'J', 'u', ' ', 7, 'S', 'h', 'u', 'a', 'n', 'g', ' ', 4, 'S', 'h', 'e', ' ', 4, 'W', 'e', 'i', ' ', 5, 'C', 'o', 'n', 'g', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 3, 'Q', 'u', ' ', 5, 'C', 'a', 'n', 'g', ' ', 3, 'Y', 'u', ' ', 4, 'L', 'u', 'o', ' ', 3, 'L', 'i', ' ', 4, 'Z', 'a', 'n', ' ', 5, 'L', 'u', 'a', 'n', ' ', 5, 'D', 'a', 'n', 'g', ' ', 4, 'J', 'u', 'e', ' ', 3, 'E', 'm', ' ', 4, 'L', 'a', 'n', ' ', 4, 'L', 'a', 'n', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'L', 'e', 'i', ' ', 3, 'L', 'i', ' ', 3, 'B', 'a', ' ', 5, 'N', 'a', 'n', 'g', ' ', 3, 'Y', 'u', ' ', 5, 'L', 'i', 'n', 'g', ' ', 6, 'T', 's', 'u', 'k', 'i', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 3, 'C', 'i', ' ', 5, 'H', 'u', 'a', 'n', ' ', 4, 'X', 'i', 'n', ' ', 3, 'Y', 'u', ' ', 3, 'Y', 'u', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 3, 'O', 'u', ' ', 3, 'X', 'u', ' ', 5, 'C', 'h', 'a', 'o', ' ', 4, 'C', 'h', 'u', ' ', 4, 'C', 'h', 'i', ' ', 4, 'K', 'a', 'i', ' ', 3, 'Y', 'i', ' ', 4, 'J', 'u', 'e', ' ', 3, 'X', 'i', ' ', 3, 'X', 'u', ' ', 4, 'X', 'i', 'a', ' ', 3, 'Y', 'u', ' ', 5, 'K', 'u', 'a', 'i', ' ', 5, 'L', 'a', 'n', 'g', ' ', 5, 'K', 'u', 'a', 'n', ' ', 5, 'S', 'h', 'u', 'o', ' ', 3, 'X', 'i', ' ', 3, 'A', 'i', ' ', 3, 'Y', 'i', ' ', 3, 'Q', 'i', ' ', 3, 'H', 'u', ' ', 4, 'C', 'h', 'i', ' ', 4, 'Q', 'i', 'n', ' ', 5, 'K', 'u', 'a', 'n', ' ', 4, 'K', 'a', 'n', ' ', 5, 'K', 'u', 'a', 'n', ' ', 4, 'K', 'a', 'n', ' ', 6, 'C', 'h', 'u', 'a', 'n', ' ', 4, 'S', 'h', 'a', ' ', 4, 'G', 'u', 'a', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'X', 'i', 'n', ' ', 4, 'X', 'i', 'e', ' ', 3, 'Y', 'u', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'o', ' ', 3, 'Y', 'i', ' ', 3, 'G', 'e', ' ', 3, 'W', 'u', ' ', 4, 'T', 'a', 'n', ' ', 4, 'J', 'i', 'n', ' ', 3, 'O', 'u', ' ', 3, 'H', 'u', ' ', 3, 'T', 'i', ' ', 5, 'H', 'u', 'a', 'n', ' ', 3, 'X', 'u', ' ', 4, 'P', 'e', 'n', ' ', 3, 'X', 'i', ' ', 5, 'X', 'i', 'a', 'o', ' ', 3, 'X', 'u', ' ', 3, 'X', 'i', ' ', 4, 'S', 'e', 'n', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'C', 'h', 'u', ' ', 3, 'Y', 'i', ' ', 4, 'K', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 5, 'C', 'h', 'u', 'o', ' ', 5, 'H', 'u', 'a', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 3, 'C', 'i', ' ', 3, 'B', 'u', ' ', 3, 'W', 'u', ' ', 3, 'Q', 'i', ' ', 3, 'B', 'u', ' ', 3, 'B', 'u', ' ', 4, 'W', 'a', 'i', ' ', 3, 'J', 'u', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'C', 'h', 'i', ' ', 3, 'S', 'e', ' ', 4, 'C', 'h', 'i', ' ', 3, 'S', 'e', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 4, 'S', 'u', 'i', ' ', 4, 'S', 'u', 'i', ' ', 3, 'L', 'i', ' ', 4, 'C', 'u', 'o', ' ', 3, 'Y', 'u', ' ', 3, 'L', 'i', ' ', 4, 'G', 'u', 'i', ' ', 4, 'D', 'a', 'i', ' ', 4, 'D', 'a', 'i', ' ', 3, 'S', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'Z', 'h', 'e', ' ', 3, 'M', 'o', ' ', 3, 'M', 'o', ' ', 4, 'Y', 'a', 'o', ' ', 3, 'M', 'o', ' ', 3, 'C', 'u', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 5, 'T', 'i', 'a', 'n', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 4, 'D', 'a', 'i', ' ', 6, 'S', 'h', 'a', 'n', 'g', ' ', 3, 'X', 'u', ' ', 4, 'X', 'u', 'n', ' ', 4, 'S', 'h', 'u', ' ', 4, 'C', 'a', 'n', ' ', 4, 'J', 'u', 'e', ' ', 5, 'P', 'i', 'a', 'o', ' ', 4, 'Q', 'i', 'a', ' ', 4, 'Q', 'i', 'u', ' ', 3, 'S', 'u', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 4, 'Y', 'u', 'n', ' ', 5, 'L', 'i', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 4, 'F', 'o', 'u', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'Y', 'e', ' ', 4, 'C', 'a', 'n', ' ', 4, 'H', 'u', 'n', ' ', 4, 'D', 'a', 'n', ' ', 3, 'J', 'i', ' ', 3, 'Y', 'e', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 4, 'Y', 'u', 'n', ' ', 4, 'W', 'e', 'n', ' ', 5, 'C', 'h', 'o', 'u', ' ', 4, 'B', 'i', 'n', ' ', 3, 'T', 'i', ' ', 4, 'J', 'i', 'n', ' ', 6, 'S', 'h', 'a', 'n', 'g', ' ', 4, 'Y', 'i', 'n', ' ', 5, 'D', 'i', 'a', 'o', ' ', 3, 'C', 'u', ' ', 4, 'H', 'u', 'i', ' ', 5, 'C', 'u', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 4, 'D', 'a', 'n', ' ', 3, 'D', 'u', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'B', 'i', 'n', ' ', 3, 'D', 'u', ' ', 8, 'T', 's', 'u', 'k', 'u', 's', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'S', 'h', 'u', ' ', 3, 'O', 'u', ' ', 5, 'D', 'u', 'a', 'n', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'Y', 'i', 'n', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 4, 'S', 'h', 'a', ' ', 4, 'Q', 'u', 'e', ' ', 3, 'K', 'e', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'J', 'u', 'n', ' ', 5, 'D', 'i', 'a', 'n', ' ', 4, 'H', 'u', 'i', ' ', 4, 'H', 'u', 'i', ' ', 3, 'G', 'u', ' ', 4, 'Q', 'u', 'e', ' ', 3, 'J', 'i', ' ', 3, 'Y', 'i', ' ', 3, 'O', 'u', ' ', 4, 'H', 'u', 'i', ' ', 5, 'D', 'u', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 5, 'X', 'i', 'a', 'o', ' ', 3, 'W', 'u', ' ', 5, 'G', 'u', 'a', 'n', ' ', 3, 'M', 'u', ' ', 4, 'M', 'e', 'i', ' ', 4, 'M', 'e', 'i', ' ', 3, 'A', 'i', ' ', 4, 'Z', 'u', 'o', ' ', 3, 'D', 'u', ' ', 3, 'Y', 'u', ' ', 3, 'B', 'i', ' ', 3, 'B', 'i', ' ', 3, 'B', 'i', ' ', 3, 'P', 'i', ' ', 3, 'P', 'i', ' ', 3, 'B', 'i', ' ', 5, 'C', 'h', 'a', 'n', ' ', 4, 'M', 'a', 'o', ' ', 3, 'P', 'u', ' ', 8, 'M', 'u', 's', 'h', 'i', 'r', 'u', ' ', 4, 'J', 'i', 'a', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 4, 'S', 'a', 'i', ' ', 3, 'M', 'u', ' ', 4, 'T', 'u', 'o', ' ', 4, 'X', 'u', 'n', ' ', 3, 'E', 'r', ' ', 5, 'R', 'o', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'J', 'u', ' ', 3, 'M', 'u', ' ', 4, 'H', 'a', 'o', ' ', 4, 'Q', 'i', 'u', ' ', 4, 'D', 'o', 'u', ' ', 8, 'M', 'u', 's', 'h', 'i', 'r', 'u', ' ', 4, 'T', 'a', 'n', ' ', 4, 'P', 'e', 'i', ' ', 3, 'J', 'u', ' ', 4, 'D', 'u', 'o', ' ', 4, 'C', 'u', 'i', ' ', 3, 'B', 'i', ' ', 4, 'S', 'a', 'n', ' ', 4, 'M', 'a', 'o', ' ', 4, 'S', 'u', 'i', ' ', 3, 'Y', 'u', ' ', 3, 'Y', 'u', ' ', 4, 'T', 'u', 'o', ' ', 3, 'H', 'e', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'T', 'a', ' ', 4, 'S', 'a', 'n', ' ', 3, 'L', 'u', ' ', 3, 'M', 'u', ' ', 3, 'L', 'i', ' ', 5, 'T', 'o', 'n', 'g', ' ', 5, 'R', 'o', 'n', 'g', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 3, 'P', 'u', ' ', 4, 'L', 'u', 'o', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 4, 'S', 'a', 'o', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 5, 'M', 'e', 'n', 'g', ' ', 4, 'L', 'u', 'o', ' ', 3, 'Q', 'u', ' ', 4, 'D', 'i', 'e', ' ', 4, 'S', 'h', 'i', ' ', 3, 'D', 'i', ' ', 4, 'M', 'i', 'n', ' ', 4, 'J', 'u', 'e', ' ', 5, 'M', 'a', 'n', 'g', ' ', 3, 'Q', 'i', ' ', 4, 'P', 'i', 'e', ' ', 4, 'N', 'a', 'i', ' ', 3, 'Q', 'i', ' ', 4, 'D', 'a', 'o', ' ', 5, 'X', 'i', 'a', 'n', ' ', 6, 'C', 'h', 'u', 'a', 'n', ' ', 4, 'F', 'e', 'n', ' ', 3, 'R', 'i', ' ', 4, 'N', 'e', 'i', ' ', 3, 'F', 'u', ' ', 5, 'S', 'h', 'e', 'n', ' ', 5, 'D', 'o', 'n', 'g', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 3, 'Q', 'i', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'X', 'i', ' ', 4, 'H', 'a', 'i', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 3, 'A', 'n', ' ', 3, 'Y', 'a', ' ', 3, 'K', 'e', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 3, 'Y', 'a', ' ', 5, 'D', 'o', 'n', 'g', ' ', 4, 'D', 'a', 'n', ' ', 3, 'L', 'u', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 4, 'Y', 'u', 'n', ' ', 4, 'Y', 'u', 'n', ' ', 5, 'S', 'h', 'u', 'i', ' ', 4, 'S', 'a', 'n', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 5, 'B', 'i', 'n', 'g', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 5, 'D', 'a', 'n', 'g', ' ', 10, 'S', 'h', 'i', 't', 'a', 'm', 'i', 'z', 'u', ' ', 3, 'L', 'e', ' ', 3, 'N', 'i', ' ', 4, 'T', 'u', 'n', ' ', 4, 'F', 'a', 'n', ' ', 4, 'G', 'u', 'i', ' ', 5, 'T', 'i', 'n', 'g', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'Q', 'i', 'u', ' ', 4, 'B', 'i', 'n', ' ', 3, 'Z', 'e', ' ', 5, 'M', 'i', 'a', 'n', ' ', 5, 'C', 'u', 'a', 'n', ' ', 4, 'H', 'u', 'i', ' ', 5, 'D', 'i', 'a', 'o', ' ', 3, 'Y', 'i', ' ', 4, 'C', 'h', 'a', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 6, 'C', 'h', 'u', 'a', 'n', ' ', 4, 'W', 'a', 'n', ' ', 4, 'F', 'a', 'n', ' ', 4, 'D', 'a', 'i', ' ', 3, 'X', 'i', ' ', 4, 'T', 'u', 'o', ' ', 5, 'M', 'a', 'n', 'g', ' ', 4, 'Q', 'i', 'u', ' ', 3, 'Q', 'i', ' ', 5, 'S', 'h', 'a', 'n', ' ', 4, 'P', 'a', 'i', ' ', 4, 'H', 'a', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 3, 'W', 'u', ' ', 3, 'W', 'u', ' ', 4, 'X', 'u', 'n', ' ', 3, 'S', 'i', ' ', 3, 'R', 'u', ' ', 5, 'G', 'o', 'n', 'g', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 4, 'C', 'h', 'i', ' ', 3, 'W', 'u', ' ', 7, 'T', 's', 'u', 'c', 'h', 'i', ' ', 5, 'T', 'a', 'n', 'g', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'C', 'h', 'i', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 3, 'M', 'i', ' ', 3, 'Y', 'u', ' ', 5, 'W', 'a', 'n', 'g', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 5, 'J', 'i', 'n', 'g', ' ', 4, 'R', 'u', 'i', ' ', 4, 'J', 'u', 'n', ' ', 5, 'H', 'o', 'n', 'g', ' ', 4, 'T', 'a', 'i', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 3, 'J', 'i', ' ', 5, 'B', 'i', 'a', 'n', ' ', 5, 'B', 'i', 'a', 'n', ' ', 4, 'G', 'a', 'n', ' ', 4, 'W', 'e', 'n', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 5, 'F', 'a', 'n', 'g', ' ', 6, 'X', 'i', 'o', 'n', 'g', ' ', 4, 'J', 'u', 'e', ' ', 5, 'H', 'a', 'n', 'g', ' ', 5, 'N', 'i', 'o', 'u', ' ', 3, 'Q', 'i', ' ', 4, 'F', 'e', 'n', ' ', 3, 'X', 'u', ' ', 3, 'X', 'u', ' ', 4, 'Q', 'i', 'n', ' ', 3, 'Y', 'i', ' ', 3, 'W', 'o', ' ', 4, 'Y', 'u', 'n', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 5, 'H', 'a', 'n', 'g', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'C', 'h', 'e', 'n', ' ', 5, 'C', 'h', 'e', 'n', ' ', 4, 'D', 'a', 'n', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'D', 'u', 'n', ' ', 3, 'H', 'u', ' ', 4, 'H', 'u', 'o', ' ', 4, 'Q', 'i', 'e', ' ', 3, 'M', 'u', ' ', 4, 'R', 'o', 'u', ' ', 4, 'M', 'e', 'i', ' ', 3, 'T', 'a', ' ', 5, 'M', 'i', 'a', 'n', ' ', 3, 'W', 'u', ' ', 6, 'C', 'h', 'o', 'n', 'g', ' ', 5, 'T', 'i', 'a', 'n', ' ', 3, 'B', 'i', ' ', 4, 'S', 'h', 'a', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'P', 'e', 'i', ' ', 4, 'P', 'a', 'n', ' ', 5, 'Z', 'h', 'u', 'i', ' ', 3, 'Z', 'a', ' ', 4, 'G', 'o', 'u', ' ', 4, 'L', 'i', 'u', ' ', 4, 'M', 'e', 'i', ' ', 3, 'Z', 'e', ' ', 5, 'F', 'e', 'n', 'g', ' ', 3, 'O', 'u', ' ', 3, 'L', 'i', ' ', 4, 'L', 'u', 'n', ' ', 5, 'C', 'a', 'n', 'g', ' ', 5, 'F', 'e', 'n', 'g', ' ', 4, 'W', 'e', 'i', ' ', 3, 'H', 'u', ' ', 3, 'M', 'o', ' ', 4, 'M', 'e', 'i', ' ', 4, 'S', 'h', 'u', ' ', 3, 'J', 'u', ' ', 4, 'Z', 'a', 'n', ' ', 4, 'T', 'u', 'o', ' ', 4, 'T', 'u', 'o', ' ', 4, 'T', 'u', 'o', ' ', 3, 'H', 'e', ' ', 3, 'L', 'i', ' ', 3, 'M', 'i', ' ', 3, 'Y', 'i', ' ', 3, 'F', 'a', ' ', 4, 'F', 'e', 'i', ' ', 4, 'Y', 'o', 'u', ' ', 5, 'T', 'i', 'a', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 3, 'G', 'u', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'S', 'i', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 6, 'J', 'i', 'o', 'n', 'g', ' ', 3, 'J', 'u', ' ', 4, 'X', 'i', 'e', ' ', 4, 'Q', 'i', 'u', ' ', 3, 'Y', 'i', ' ', 4, 'J', 'i', 'a', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 3, 'B', 'o', ' ', 4, 'H', 'u', 'i', ' ', 3, 'M', 'i', ' ', 4, 'B', 'e', 'n', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 4, 'C', 'h', 'u', ' ', 3, 'L', 'e', ' ', 4, 'Y', 'o', 'u', ' ', 3, 'G', 'u', ' ', 5, 'H', 'o', 'n', 'g', ' ', 4, 'G', 'a', 'n', ' ', 3, 'F', 'a', ' ', 4, 'M', 'a', 'o', ' ', 3, 'S', 'i', ' ', 3, 'H', 'u', ' ', 5, 'P', 'i', 'n', 'g', ' ', 3, 'C', 'i', ' ', 4, 'F', 'a', 'n', ' ', 4, 'C', 'h', 'i', ' ', 3, 'S', 'u', ' ', 5, 'N', 'i', 'n', 'g', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 5, 'L', 'i', 'n', 'g', ' ', 4, 'P', 'a', 'o', ' ', 3, 'B', 'o', ' ', 3, 'Q', 'i', ' ', 3, 'S', 'i', ' ', 3, 'N', 'i', ' ', 3, 'J', 'u', ' ', 4, 'Y', 'u', 'e', ' ', 4, 'Z', 'h', 'u', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 4, 'L', 'e', 'i', ' ', 5, 'X', 'u', 'a', 'n', ' ', 4, 'X', 'u', 'e', ' ', 3, 'F', 'u', ' ', 4, 'P', 'a', 'n', ' ', 4, 'M', 'i', 'n', ' ', 4, 'T', 'a', 'i', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 3, 'J', 'i', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 5, 'G', 'u', 'a', 'n', ' ', 5, 'B', 'e', 'n', 'g', ' ', 4, 'X', 'u', 'e', ' ', 5, 'L', 'o', 'n', 'g', ' ', 3, 'L', 'u', ' ', 3, 'B', 'o', ' ', 4, 'X', 'i', 'e', ' ', 3, 'P', 'o', ' ', 3, 'Z', 'e', ' ', 5, 'J', 'i', 'n', 'g', ' ', 4, 'Y', 'i', 'n', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 3, 'J', 'i', ' ', 3, 'Y', 'i', ' ', 4, 'H', 'u', 'i', ' ', 4, 'H', 'u', 'i', ' ', 4, 'Z', 'u', 'i', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'W', 'e', 'i', ' ', 4, 'H', 'o', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 4, 'L', 'i', 'e', ' ', 3, 'S', 'i', ' ', 3, 'J', 'i', ' ', 3, 'E', 'r', ' ', 5, 'X', 'i', 'n', 'g', ' ', 3, 'F', 'u', ' ', 3, 'S', 'a', ' ', 4, 'S', 'u', 'o', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'W', 'u', ' ', 3, 'X', 'i', ' ', 4, 'K', 'a', 'o', ' ', 4, 'Z', 'h', 'u', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 4, 'L', 'u', 'o', ' ', 3, 'A', 'n', ' ', 5, 'D', 'o', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 4, 'M', 'o', 'u', ' ', 4, 'L', 'e', 'i', ' ', 3, 'Y', 'i', ' ', 3, 'M', 'i', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 4, 'J', 'i', 'n', ' ', 3, 'M', 'o', ' ', 4, 'W', 'e', 'i', ' ', 5, 'X', 'i', 'a', 'o', ' ', 4, 'X', 'i', 'e', ' ', 5, 'H', 'o', 'n', 'g', ' ', 3, 'X', 'u', ' ', 5, 'S', 'h', 'u', 'o', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 4, 'T', 'a', 'o', ' ', 4, 'Q', 'i', 'e', ' ', 3, 'J', 'u', ' ', 3, 'E', 'r', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 3, 'R', 'u', ' ', 5, 'P', 'i', 'n', 'g', ' ', 4, 'X', 'u', 'n', ' ', 6, 'X', 'i', 'o', 'n', 'g', ' ', 4, 'Z', 'h', 'i', ' ', 6, 'G', 'u', 'a', 'n', 'g', ' ', 5, 'H', 'u', 'a', 'n', ' ', 5, 'M', 'i', 'n', 'g', ' ', 4, 'H', 'u', 'o', ' ', 3, 'W', 'a', ' ', 4, 'Q', 'i', 'a', ' ', 4, 'P', 'a', 'i', ' ', 3, 'W', 'u', ' ', 3, 'Q', 'u', ' ', 4, 'L', 'i', 'u', ' ', 3, 'Y', 'i', ' ', 4, 'J', 'i', 'a', ' ', 5, 'J', 'i', 'n', 'g', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'o', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 4, 'S', 'h', 'i', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 3, 'C', 'e', ' ', 4, 'P', 'a', 'l', ' ', 5, 'K', 'u', 'a', 'i', ' ', 3, 'J', 'i', ' ', 4, 'L', 'i', 'u', ' ', 5, 'C', 'h', 'a', 'n', ' ', 4, 'H', 'u', 'n', ' ', 3, 'H', 'u', ' ', 5, 'N', 'o', 'n', 'g', ' ', 4, 'X', 'u', 'n', ' ', 4, 'J', 'i', 'n', ' ', 4, 'L', 'i', 'e', ' ', 4, 'Q', 'i', 'u', ' ', 4, 'W', 'e', 'i', ' ', 4, 'Z', 'h', 'e', ' ', 4, 'J', 'u', 'n', ' ', 4, 'H', 'a', 'n', ' ', 5, 'B', 'a', 'n', 'g', ' ', 5, 'M', 'a', 'n', 'g', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 4, 'Y', 'o', 'u', ' ', 3, 'X', 'i', ' ', 3, 'B', 'o', ' ', 4, 'D', 'o', 'u', ' ', 4, 'W', 'a', 'n', ' ', 5, 'H', 'o', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 3, 'P', 'u', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'L', 'a', 'n', ' ', 4, 'H', 'a', 'o', ' ', 5, 'L', 'a', 'n', 'g', ' ', 4, 'H', 'a', 'n', ' ', 3, 'L', 'i', ' ', 5, 'G', 'e', 'n', 'g', ' ', 3, 'F', 'u', ' ', 3, 'W', 'u', ' ', 5, 'L', 'i', 'a', 'n', ' ', 5, 'C', 'h', 'u', 'n', ' ', 5, 'F', 'e', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 3, 'Y', 'u', ' ', 5, 'T', 'o', 'n', 'g', ' ', 4, 'L', 'a', 'o', ' ', 4, 'H', 'a', 'i', ' ', 4, 'J', 'i', 'n', ' ', 4, 'J', 'i', 'a', ' ', 6, 'C', 'h', 'o', 'n', 'g', ' ', 5, 'W', 'e', 'n', 'g', ' ', 4, 'M', 'e', 'i', ' ', 4, 'S', 'u', 'i', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 4, 'P', 'e', 'i', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'S', 'h', 'e', 'n', ' ', 3, 'T', 'u', ' ', 4, 'K', 'u', 'n', ' ', 4, 'P', 'i', 'n', ' ', 4, 'N', 'i', 'e', ' ', 4, 'H', 'a', 'n', ' ', 5, 'J', 'i', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'o', ' ', 4, 'S', 'h', 'e', ' ', 5, 'N', 'i', 'a', 'n', ' ', 3, 'T', 'u', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'o', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'T', 'i', 'n', 'g', ' ', 2, 'E', ' ', 3, 'S', 'u', ' ', 4, 'T', 'u', 'n', ' ', 5, 'J', 'u', 'a', 'n', ' ', 4, 'C', 'e', 'n', ' ', 3, 'T', 'i', ' ', 3, 'L', 'i', ' ', 5, 'S', 'h', 'u', 'i', ' ', 3, 'S', 'i', ' ', 4, 'L', 'e', 'i', ' ', 5, 'S', 'h', 'u', 'i', ' ', 4, 'T', 'a', 'o', ' ', 3, 'D', 'u', ' ', 4, 'L', 'a', 'o', ' ', 4, 'L', 'a', 'i', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'W', 'e', 'i', ' ', 3, 'W', 'o', ' ', 4, 'Y', 'u', 'n', ' ', 5, 'H', 'u', 'a', 'n', ' ', 3, 'D', 'i', ' ', 4, 'R', 'u', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 3, 'S', 'e', ' ', 3, 'F', 'u', ' ', 5, 'G', 'u', 'a', 'n', ' ', 5, 'X', 'i', 'n', 'g', ' ', 5, 'S', 'h', 'o', 'u', ' ', 6, 'S', 'h', 'u', 'a', 'n', ' ', 3, 'Y', 'a', ' ', 5, 'C', 'h', 'u', 'o', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 3, 'Y', 'e', ' ', 5, 'K', 'o', 'n', 'g', ' ', 3, 'W', 'o', ' ', 4, 'H', 'a', 'n', ' ', 4, 'T', 'u', 'o', ' ', 5, 'D', 'o', 'n', 'g', ' ', 3, 'H', 'e', ' ', 3, 'W', 'o', ' ', 3, 'J', 'u', ' ', 4, 'G', 'a', 'n', ' ', 6, 'L', 'i', 'a', 'n', 'g', ' ', 4, 'H', 'u', 'n', ' ', 3, 'T', 'a', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 5, 'D', 'i', 'a', 'n', ' ', 4, 'Q', 'i', 'e', ' ', 3, 'D', 'e', ' ', 5, 'J', 'u', 'a', 'n', ' ', 3, 'Z', 'i', ' ', 3, 'X', 'i', ' ', 4, 'Y', 'a', 'o', ' ', 3, 'Q', 'i', ' ', 3, 'G', 'u', ' ', 4, 'G', 'u', 'o', ' ', 4, 'H', 'a', 'n', ' ', 4, 'L', 'i', 'n', ' ', 5, 'T', 'a', 'n', 'g', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 5, 'P', 'e', 'n', 'g', ' ', 4, 'H', 'a', 'o', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 4, 'S', 'h', 'u', ' ', 3, 'Q', 'i', ' ', 5, 'F', 'a', 'n', 'g', ' ', 4, 'C', 'h', 'i', ' ', 3, 'L', 'u', ' ', 4, 'N', 'a', 'o', ' ', 3, 'J', 'u', ' ', 4, 'T', 'a', 'o', ' ', 5, 'C', 'o', 'n', 'g', ' ', 4, 'L', 'e', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'P', 'e', 'n', 'g', ' ', 4, 'F', 'e', 'i', ' ', 5, 'S', 'o', 'n', 'g', ' ', 5, 'T', 'i', 'a', 'n', ' ', 3, 'P', 'i', ' ', 4, 'D', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 3, 'N', 'i', ' ', 3, 'Y', 'u', ' ', 3, 'L', 'u', ' ', 4, 'G', 'a', 'n', ' ', 3, 'M', 'i', ' ', 5, 'J', 'i', 'n', 'g', ' ', 5, 'L', 'i', 'n', 'g', ' ', 4, 'L', 'u', 'n', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'C', 'u', 'i', ' ', 3, 'Q', 'u', ' ', 5, 'H', 'u', 'a', 'i', ' ', 3, 'Y', 'u', ' ', 5, 'N', 'i', 'a', 'n', ' ', 5, 'S', 'h', 'e', 'n', ' ', 5, 'P', 'i', 'a', 'o', ' ', 5, 'C', 'h', 'u', 'n', ' ', 3, 'W', 'a', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'L', 'a', 'i', ' ', 4, 'H', 'u', 'n', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'T', 'i', 'a', 'n', ' ', 5, 'M', 'i', 'a', 'o', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'M', 'i', ' ', 4, 'B', 'e', 'n', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'W', 'e', 'n', ' ', 3, 'R', 'e', ' ', 4, 'F', 'e', 'i', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 3, 'K', 'e', ' ', 3, 'J', 'i', ' ', 4, 'S', 'h', 'e', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 7, 'S', 'h', 'i', 'b', 'u', 'i', ' ', 3, 'L', 'u', ' ', 3, 'Z', 'i', ' ', 3, 'D', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'M', 'i', 'n', ' ', 3, 'P', 'i', ' ', 5, 'T', 'a', 'n', 'i', ' ', 3, 'Y', 'u', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 5, 'S', 'h', 'e', 'n', ' ', 5, 'S', 'h', 'e', 'n', ' ', 4, 'R', 'o', 'u', ' ', 5, 'H', 'u', 'a', 'n', ' ', 4, 'Z', 'h', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'N', 'u', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 4, 'Q', 'i', 'u', ' ', 5, 'T', 'i', 'n', 'g', ' ', 3, 'Q', 'u', ' ', 3, 'D', 'u', ' ', 5, 'F', 'e', 'n', 'g', ' ', 4, 'Z', 'h', 'a', ' ', 3, 'B', 'o', ' ', 3, 'W', 'o', ' ', 3, 'W', 'o', ' ', 3, 'D', 'i', ' ', 4, 'W', 'e', 'i', ' ', 4, 'W', 'e', 'n', ' ', 3, 'R', 'u', ' ', 4, 'X', 'i', 'e', ' ', 3, 'C', 'e', ' ', 4, 'W', 'e', 'i', ' ', 3, 'G', 'e', ' ', 5, 'G', 'a', 'n', 'g', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'H', 'o', 'n', 'g', ' ', 5, 'X', 'u', 'a', 'n', ' ', 3, 'M', 'i', ' ', 3, 'K', 'e', ' ', 4, 'M', 'a', 'o', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'Y', 'o', 'u', ' ', 5, 'H', 'o', 'n', 'g', ' ', 5, 'M', 'i', 'a', 'o', ' ', 5, 'X', 'i', 'n', 'g', ' ', 4, 'M', 'e', 'i', ' ', 4, 'Z', 'a', 'i', ' ', 4, 'H', 'u', 'n', ' ', 4, 'N', 'a', 'i', ' ', 4, 'K', 'u', 'i', ' ', 4, 'S', 'h', 'i', ' ', 2, 'E', ' ', 4, 'P', 'a', 'i', ' ', 4, 'M', 'e', 'i', ' ', 5, 'L', 'i', 'a', 'n', ' ', 3, 'Q', 'i', ' ', 3, 'Q', 'i', ' ', 4, 'M', 'e', 'i', ' ', 5, 'T', 'i', 'a', 'n', ' ', 4, 'C', 'o', 'u', ' ', 4, 'W', 'e', 'i', ' ', 4, 'C', 'a', 'n', ' ', 5, 'T', 'u', 'a', 'n', ' ', 5, 'M', 'i', 'a', 'n', ' ', 4, 'H', 'u', 'i', ' ', 3, 'M', 'o', ' ', 3, 'X', 'u', ' ', 3, 'J', 'i', ' ', 4, 'P', 'e', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'H', 'u', ' ', 5, 'F', 'e', 'n', 'g', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 4, 'Y', 'i', 'n', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 4, 'S', 'h', 'i', ' ', 4, 'J', 'i', 'e', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 4, 'T', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 3, 'B', 'i', ' ', 4, 'M', 'i', 'n', ' ', 4, 'S', 'h', 'i', ' ', 3, 'T', 'u', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 3, 'Q', 'u', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 5, 'S', 'u', 'e', 'i', ' ', 4, 'J', 'i', 'u', ' ', 5, 'J', 'i', 'a', 'o', ' ', 5, 'Q', 'i', 'o', 'u', ' ', 4, 'Y', 'i', 'n', ' ', 5, 'T', 'a', 'n', 'g', ' ', 5, 'L', 'o', 'n', 'g', ' ', 4, 'H', 'u', 'o', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'N', 'a', 'n', ' ', 4, 'B', 'a', 'n', ' ', 4, 'Y', 'o', 'u', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 5, 'C', 'h', 'u', 'i', ' ', 6, 'L', 'i', 'a', 'n', 'g', ' ', 5, 'C', 'h', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'C', 'h', 'u', 'n', ' ', 4, 'N', 'i', 'e', ' ', 3, 'Z', 'i', ' ', 4, 'W', 'a', 'n', ' ', 4, 'S', 'h', 'i', ' ', 4, 'M', 'a', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 6, 'R', 'a', 't', 's', 'u', ' ', 4, 'K', 'u', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'X', 'u', ' ', 3, 'L', 'u', ' ', 4, 'G', 'u', 'i', ' ', 4, 'G', 'a', 'i', ' ', 3, 'P', 'o', ' ', 4, 'J', 'i', 'n', ' ', 4, 'G', 'u', 'i', ' ', 5, 'T', 'a', 'n', 'g', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'S', 'u', 'o', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'Y', 'a', 'o', ' ', 5, 'M', 'e', 'n', 'g', ' ', 5, 'Z', 'h', 'u', 'n', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 3, 'K', 'e', ' ', 4, 'T', 'a', 'i', ' ', 3, 'D', 'a', ' ', 3, 'W', 'a', ' ', 4, 'L', 'i', 'u', ' ', 4, 'G', 'o', 'u', ' ', 4, 'S', 'a', 'o', ' ', 5, 'M', 'i', 'n', 'g', ' ', 4, 'Z', 'h', 'a', ' ', 4, 'S', 'h', 'i', ' ', 3, 'Y', 'i', ' ', 4, 'L', 'u', 'n', ' ', 3, 'M', 'a', ' ', 3, 'P', 'u', ' ', 4, 'W', 'e', 'i', ' ', 3, 'L', 'i', ' ', 4, 'C', 'a', 'i', ' ', 3, 'W', 'u', ' ', 3, 'X', 'i', ' ', 4, 'W', 'e', 'n', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 3, 'Z', 'e', ' ', 4, 'S', 'h', 'i', ' ', 3, 'S', 'u', ' ', 3, 'Y', 'i', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 4, 'S', 'o', 'u', ' ', 4, 'Y', 'u', 'n', ' ', 4, 'X', 'i', 'u', ' ', 4, 'Y', 'i', 'n', ' ', 5, 'R', 'o', 'n', 'g', ' ', 4, 'H', 'u', 'n', ' ', 3, 'S', 'u', ' ', 3, 'S', 'u', ' ', 3, 'N', 'i', ' ', 3, 'T', 'a', ' ', 4, 'S', 'h', 'i', ' ', 3, 'R', 'u', ' ', 4, 'W', 'e', 'i', ' ', 4, 'P', 'a', 'n', ' ', 4, 'C', 'h', 'u', ' ', 4, 'C', 'h', 'u', ' ', 5, 'P', 'a', 'n', 'g', ' ', 5, 'W', 'e', 'n', 'g', ' ', 5, 'C', 'a', 'n', 'g', ' ', 4, 'M', 'i', 'e', ' ', 3, 'H', 'e', ' ', 5, 'D', 'i', 'a', 'n', ' ', 4, 'H', 'a', 'o', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 3, 'X', 'i', ' ', 3, 'Z', 'i', ' ', 3, 'D', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 3, 'F', 'u', ' ', 4, 'J', 'i', 'e', ' ', 4, 'H', 'u', 'a', ' ', 3, 'G', 'e', ' ', 3, 'Z', 'i', ' ', 4, 'T', 'a', 'o', ' ', 5, 'T', 'e', 'n', 'g', ' ', 4, 'S', 'u', 'i', ' ', 3, 'B', 'i', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'H', 'u', 'i', ' ', 4, 'G', 'u', 'n', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'G', 'a', 'o', ' ', 5, 'L', 'o', 'n', 'g', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'S', 'h', 'e', ' ', 4, 'M', 'a', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 5, 'C', 'h', 'u', 'n', ' ', 3, 'L', 'u', ' ', 4, 'L', 'a', 'n', ' ', 5, 'L', 'u', 'a', 'n', ' ', 4, 'B', 'i', 'n', ' ', 4, 'T', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 4, 'S', 'o', 'u', ' ', 3, 'H', 'u', ' ', 3, 'B', 'i', ' ', 5, 'B', 'i', 'a', 'o', ' ', 4, 'Z', 'h', 'i', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 4, 'K', 'o', 'u', ' ', 5, 'S', 'h', 'e', 'n', ' ', 6, 'S', 'h', 'a', 'n', 'g', ' ', 3, 'D', 'i', ' ', 3, 'M', 'i', ' ', 3, 'A', 'o', ' ', 3, 'L', 'u', ' ', 3, 'H', 'u', ' ', 3, 'H', 'u', ' ', 4, 'Y', 'o', 'u', ' ', 5, 'C', 'h', 'a', 'n', ' ', 4, 'F', 'a', 'n', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 4, 'G', 'u', 'n', ' ', 4, 'M', 'a', 'n', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 3, 'Y', 'u', ' ', 5, 'P', 'i', 'a', 'o', ' ', 3, 'J', 'i', ' ', 3, 'Y', 'a', ' ', 5, 'J', 'i', 'a', 'o', ' ', 3, 'Q', 'i', ' ', 3, 'X', 'i', ' ', 3, 'J', 'i', ' ', 3, 'L', 'u', ' ', 3, 'L', 'u', ' ', 5, 'L', 'o', 'n', 'g', ' ', 4, 'J', 'i', 'n', ' ', 4, 'G', 'u', 'o', ' ', 5, 'C', 'o', 'n', 'g', ' ', 4, 'L', 'o', 'u', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'G', 'a', 'i', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 3, 'L', 'i', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'C', 'a', 'o', ' ', 5, 'J', 'i', 'a', 'o', ' ', 5, 'C', 'o', 'n', 'g', ' ', 4, 'Q', 'u', 'n', ' ', 5, 'T', 'u', 'a', 'n', ' ', 3, 'O', 'u', ' ', 5, 'T', 'e', 'n', 'g', ' ', 3, 'Y', 'e', ' ', 3, 'X', 'i', ' ', 3, 'M', 'i', ' ', 5, 'T', 'a', 'n', 'g', ' ', 3, 'M', 'o', ' ', 6, 'S', 'h', 'a', 'n', 'g', ' ', 4, 'H', 'a', 'n', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'L', 'a', 'n', ' ', 3, 'W', 'a', ' ', 3, 'L', 'i', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'F', 'e', 'n', 'g', ' ', 5, 'X', 'u', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 4, 'M', 'a', 'n', ' ', 3, 'Z', 'i', ' ', 5, 'M', 'a', 'n', 'g', ' ', 5, 'K', 'a', 'n', 'g', ' ', 4, 'L', 'e', 'i', ' ', 5, 'P', 'e', 'n', 'g', ' ', 4, 'S', 'h', 'u', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 6, 'C', 'h', 'o', 'n', 'g', ' ', 3, 'X', 'u', ' ', 5, 'H', 'u', 'a', 'n', ' ', 4, 'K', 'u', 'o', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 7, 'C', 'h', 'u', 'a', 'n', 'g', ' ', 5, 'L', 'i', 'a', 'o', ' ', 4, 'C', 'u', 'i', ' ', 3, 'T', 'i', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 5, 'C', 'o', 'n', 'g', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 5, 'H', 'o', 'n', 'g', ' ', 4, 'X', 'u', 'n', ' ', 4, 'S', 'h', 'u', ' ', 5, 'G', 'u', 'a', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'o', ' ', 3, 'X', 'u', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'W', 'e', 'i', ' ', 3, 'P', 'i', ' ', 4, 'J', 'u', 'e', ' ', 5, 'J', 'i', 'a', 'o', ' ', 3, 'P', 'o', ' ', 5, 'D', 'a', 'n', 'g', ' ', 4, 'H', 'u', 'i', ' ', 4, 'J', 'i', 'e', ' ', 3, 'W', 'u', ' ', 3, 'P', 'a', ' ', 3, 'J', 'i', ' ', 4, 'P', 'a', 'n', ' ', 4, 'G', 'u', 'i', ' ', 5, 'X', 'i', 'a', 'o', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 3, 'X', 'i', ' ', 3, 'L', 'u', ' ', 3, 'X', 'i', ' ', 5, 'X', 'u', 'a', 'n', ' ', 4, 'D', 'u', 'n', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 4, 'M', 'i', 'n', ' ', 4, 'R', 'u', 'n', ' ', 3, 'S', 'u', ' ', 5, 'L', 'i', 'a', 'o', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 3, 'D', 'i', ' ', 4, 'W', 'a', 'n', ' ', 4, 'D', 'a', 'n', ' ', 4, 'T', 'a', 'n', ' ', 5, 'C', 'h', 'a', 'o', ' ', 4, 'X', 'u', 'n', ' ', 4, 'K', 'u', 'i', ' ', 4, 'Y', 'i', 'e', ' ', 5, 'S', 'h', 'a', 'o', ' ', 3, 'T', 'u', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'S', 'a', 'n', ' ', 4, 'H', 'e', 'i', ' ', 3, 'B', 'i', ' ', 5, 'S', 'h', 'a', 'n', ' ', 5, 'C', 'h', 'a', 'n', ' ', 5, 'C', 'h', 'a', 'n', ' ', 4, 'S', 'h', 'u', ' ', 5, 'T', 'o', 'n', 'g', ' ', 3, 'P', 'u', ' ', 4, 'L', 'i', 'n', ' ', 4, 'W', 'e', 'i', ' ', 3, 'S', 'e', ' ', 3, 'S', 'e', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 6, 'J', 'i', 'o', 'n', 'g', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 4, 'H', 'u', 'a', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'L', 'a', 'o', ' ', 4, 'C', 'h', 'e', ' ', 4, 'G', 'a', 'n', ' ', 4, 'C', 'u', 'n', ' ', 5, 'H', 'e', 'n', 'g', ' ', 3, 'S', 'i', ' ', 4, 'S', 'h', 'u', ' ', 5, 'P', 'e', 'n', 'g', ' ', 4, 'H', 'a', 'n', ' ', 4, 'Y', 'u', 'n', ' ', 4, 'L', 'i', 'u', ' ', 5, 'H', 'o', 'n', 'g', ' ', 3, 'F', 'u', ' ', 4, 'H', 'a', 'o', ' ', 3, 'H', 'e', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'S', 'h', 'a', 'n', ' ', 3, 'X', 'i', ' ', 4, 'O', 'k', 'i', ' ', 4, 'L', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 4, 'L', 'i', 'n', ' ', 4, 'M', 'i', 'n', ' ', 4, 'Z', 'a', 'o', ' ', 5, 'D', 'a', 'n', 'g', ' ', 4, 'W', 'a', 'n', ' ', 3, 'Z', 'e', ' ', 4, 'X', 'i', 'e', ' ', 3, 'Y', 'u', ' ', 3, 'L', 'i', ' ', 4, 'S', 'h', 'i', ' ', 4, 'X', 'u', 'e', ' ', 5, 'L', 'i', 'n', 'g', ' ', 4, 'M', 'a', 'n', ' ', 3, 'Z', 'i', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 5, 'K', 'u', 'a', 'i', ' ', 4, 'C', 'a', 'n', ' ', 5, 'L', 'i', 'a', 'n', ' ', 5, 'D', 'i', 'a', 'n', ' ', 3, 'Y', 'e', ' ', 3, 'A', 'o', ' ', 5, 'H', 'u', 'a', 'n', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 5, 'C', 'h', 'a', 'n', ' ', 4, 'M', 'a', 'n', ' ', 4, 'D', 'a', 'n', ' ', 4, 'D', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 4, 'S', 'u', 'i', ' ', 3, 'P', 'i', ' ', 3, 'J', 'u', ' ', 3, 'T', 'a', ' ', 4, 'Q', 'i', 'n', ' ', 3, 'J', 'i', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 5, 'L', 'i', 'a', 'n', ' ', 5, 'N', 'o', 'n', 'g', ' ', 4, 'G', 'u', 'o', ' ', 4, 'J', 'i', 'n', ' ', 4, 'F', 'e', 'n', ' ', 3, 'S', 'e', ' ', 3, 'J', 'i', ' ', 4, 'S', 'u', 'i', ' ', 4, 'H', 'u', 'i', ' ', 4, 'C', 'h', 'u', ' ', 3, 'T', 'a', ' ', 5, 'S', 'o', 'n', 'g', ' ', 5, 'D', 'i', 'n', 'g', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'L', 'a', 'i', ' ', 4, 'B', 'i', 'n', ' ', 5, 'L', 'i', 'a', 'n', ' ', 3, 'M', 'i', ' ', 4, 'S', 'h', 'i', ' ', 4, 'S', 'h', 'u', ' ', 3, 'M', 'i', ' ', 5, 'N', 'i', 'n', 'g', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 5, 'M', 'e', 'n', 'g', ' ', 4, 'J', 'i', 'n', ' ', 3, 'Q', 'i', ' ', 3, 'P', 'i', ' ', 3, 'J', 'i', ' ', 4, 'H', 'a', 'o', ' ', 3, 'R', 'u', ' ', 4, 'Z', 'u', 'i', ' ', 3, 'W', 'o', ' ', 4, 'T', 'a', 'o', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'D', 'u', 'i', ' ', 3, 'C', 'i', ' ', 4, 'H', 'u', 'o', ' ', 5, 'J', 'i', 'n', 'g', ' ', 4, 'L', 'a', 'n', ' ', 4, 'J', 'u', 'n', ' ', 3, 'A', 'i', ' ', 3, 'P', 'u', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 4, 'W', 'e', 'i', ' ', 4, 'B', 'i', 'n', ' ', 3, 'G', 'u', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'X', 'i', 'n', 'g', ' ', 5, 'H', 'a', 'm', 'a', ' ', 4, 'K', 'u', 'o', ' ', 4, 'F', 'e', 'i', ' ', 5, 'B', 'o', 'k', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'W', 'e', 'i', ' ', 4, 'L', 'u', 'o', ' ', 4, 'Z', 'a', 'n', ' ', 3, 'L', 'u', ' ', 3, 'L', 'i', ' ', 4, 'Y', 'o', 'u', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 3, 'L', 'u', ' ', 3, 'S', 'i', ' ', 4, 'J', 'i', 'e', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 3, 'D', 'u', ' ', 5, 'W', 'a', 'n', 'g', ' ', 4, 'H', 'u', 'i', ' ', 4, 'X', 'i', 'e', ' ', 4, 'P', 'a', 'n', ' ', 5, 'S', 'h', 'e', 'n', ' ', 5, 'B', 'i', 'a', 'o', ' ', 5, 'C', 'h', 'a', 'n', ' ', 3, 'M', 'o', ' ', 4, 'L', 'i', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'P', 'u', ' ', 3, 'S', 'e', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 3, 'G', 'u', ' ', 4, 'B', 'i', 'n', ' ', 4, 'H', 'u', 'o', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'L', 'u', ' ', 4, 'Q', 'i', 'n', ' ', 4, 'H', 'a', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 3, 'L', 'i', ' ', 5, 'J', 'i', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'o', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'S', 'u', 'i', ' ', 4, 'W', 'e', 'i', ' ', 4, 'X', 'i', 'e', ' ', 5, 'H', 'u', 'a', 'i', ' ', 4, 'H', 'a', 'o', ' ', 4, 'Z', 'h', 'u', ' ', 5, 'L', 'o', 'n', 'g', ' ', 4, 'L', 'a', 'i', ' ', 4, 'D', 'u', 'i', ' ', 4, 'F', 'a', 'n', ' ', 3, 'H', 'u', ' ', 4, 'L', 'a', 'i', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 3, 'M', 'i', ' ', 3, 'J', 'i', ' ', 5, 'L', 'i', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'F', 'e', 'n', ' ', 4, 'L', 'i', 'n', ' ', 3, 'Y', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'Y', 'u', 'e', ' ', 5, 'C', 'h', 'a', 'n', ' ', 4, 'D', 'a', 'i', ' ', 5, 'R', 'a', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'L', 'a', 'n', ' ', 4, 'F', 'a', 'n', ' ', 7, 'S', 'h', 'u', 'a', 'n', 'g', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 5, 'F', 'e', 'n', 'g', ' ', 4, 'S', 'h', 'e', ' ', 4, 'L', 'e', 'i', ' ', 4, 'L', 'a', 'n', ' ', 5, 'C', 'o', 'n', 'g', ' ', 3, 'Q', 'u', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 3, 'F', 'a', ' ', 5, 'G', 'u', 'a', 'n', ' ', 4, 'Q', 'u', 'e', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'H', 'a', 'o', ' ', 6, 'H', 'y', 'e', 'n', 'g', ' ', 3, 'S', 'a', ' ', 4, 'Z', 'a', 'n', ' ', 5, 'L', 'u', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'L', 'i', ' ', 3, 'M', 'i', ' ', 5, 'S', 'h', 'a', 'n', ' ', 4, 'T', 'a', 'n', ' ', 5, 'D', 'a', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'o', ' ', 5, 'C', 'h', 'a', 'n', ' ', 4, 'H', 'a', 'o', ' ', 3, 'B', 'a', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'L', 'a', 'n', ' ', 4, 'L', 'a', 'n', ' ', 5, 'N', 'a', 'n', 'g', ' ', 4, 'W', 'a', 'n', ' ', 5, 'L', 'u', 'a', 'n', ' ', 4, 'X', 'u', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'G', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 4, 'H', 'u', 'o', ' ', 3, 'S', 'i', ' ', 4, 'M', 'i', 'e', ' ', 6, 'G', 'u', 'a', 'n', 'g', ' ', 5, 'D', 'e', 'n', 'g', ' ', 4, 'H', 'u', 'i', ' ', 5, 'X', 'i', 'a', 'o', ' ', 5, 'X', 'i', 'a', 'o', ' ', 3, 'H', 'u', ' ', 5, 'H', 'o', 'n', 'g', ' ', 5, 'L', 'i', 'n', 'g', ' ', 4, 'Z', 'a', 'o', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 4, 'J', 'i', 'u', ' ', 4, 'Z', 'h', 'a', ' ', 4, 'X', 'i', 'e', ' ', 4, 'C', 'h', 'i', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 4, 'Z', 'a', 'i', ' ', 4, 'Z', 'a', 'i', ' ', 4, 'C', 'a', 'n', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 3, 'Q', 'i', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 4, 'F', 'e', 'n', ' ', 4, 'N', 'i', 'u', ' ', 6, 'J', 'i', 'o', 'n', 'g', ' ', 4, 'W', 'e', 'n', ' ', 3, 'P', 'o', ' ', 3, 'Y', 'i', ' ', 3, 'L', 'u', ' ', 5, 'C', 'h', 'u', 'i', ' ', 3, 'P', 'i', ' ', 4, 'K', 'a', 'i', ' ', 4, 'P', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'K', 'a', 'i', ' ', 5, 'P', 'a', 'n', 'g', ' ', 3, 'M', 'u', ' ', 5, 'C', 'h', 'a', 'o', ' ', 5, 'L', 'i', 'a', 'o', ' ', 4, 'G', 'u', 'i', ' ', 5, 'K', 'a', 'n', 'g', ' ', 4, 'T', 'u', 'n', ' ', 6, 'G', 'u', 'a', 'n', 'g', ' ', 4, 'X', 'i', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 6, 'G', 'u', 'a', 'n', 'g', ' ', 6, 'G', 'u', 'a', 'n', 'g', ' ', 4, 'W', 'e', 'i', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 3, 'D', 'a', ' ', 4, 'X', 'i', 'a', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 4, 'Z', 'h', 'u', ' ', 3, 'K', 'e', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 3, 'F', 'u', ' ', 3, 'B', 'a', ' ', 4, 'D', 'u', 'o', ' ', 4, 'D', 'u', 'o', ' ', 5, 'L', 'i', 'n', 'g', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 5, 'X', 'u', 'a', 'n', ' ', 3, 'J', 'u', ' ', 4, 'T', 'a', 'n', ' ', 4, 'P', 'a', 'o', ' ', 6, 'J', 'i', 'o', 'n', 'g', ' ', 4, 'P', 'a', 'o', ' ', 4, 'T', 'a', 'i', ' ', 4, 'T', 'a', 'i', ' ', 5, 'B', 'i', 'n', 'g', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 5, 'T', 'o', 'n', 'g', ' ', 4, 'H', 'a', 'n', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'Z', 'h', 'a', ' ', 5, 'D', 'i', 'a', 'n', ' ', 4, 'W', 'e', 'i', ' ', 4, 'S', 'h', 'i', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'C', 'h', 'i', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 3, 'H', 'u', ' ', 5, 'S', 'h', 'u', 'o', ' ', 4, 'L', 'a', 'n', ' ', 5, 'J', 'i', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'o', ' ', 3, 'X', 'u', ' ', 5, 'X', 'i', 'n', 'g', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 4, 'L', 'i', 'e', ' ', 5, 'H', 'u', 'a', 'n', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'o', ' ', 4, 'X', 'i', 'u', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'W', 'u', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'S', 'h', 'i', ' ', 4, 'W', 'e', 'i', ' ', 5, 'T', 'o', 'n', 'g', ' ', 4, 'X', 'u', 'e', ' ', 4, 'Z', 'a', 'i', ' ', 4, 'K', 'a', 'i', ' ', 5, 'H', 'o', 'n', 'g', ' ', 4, 'L', 'u', 'o', ' ', 4, 'X', 'i', 'a', ' ', 4, 'Z', 'h', 'u', ' ', 5, 'X', 'u', 'a', 'n', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 3, 'P', 'o', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'H', 'u', 'i', ' ', 6, 'G', 'u', 'a', 'n', 'g', ' ', 4, 'Z', 'h', 'e', ' ', 4, 'H', 'u', 'i', ' ', 4, 'K', 'a', 'o', ' ', 4, 'F', 'a', 'n', ' ', 5, 'S', 'h', 'a', 'o', ' ', 3, 'Y', 'e', ' ', 4, 'H', 'u', 'i', ' ', 5, 'T', 'a', 'n', 'g', ' ', 4, 'J', 'i', 'n', ' ', 3, 'R', 'e', ' ', 3, 'X', 'i', ' ', 3, 'F', 'u', ' ', 6, 'J', 'i', 'o', 'n', 'g', ' ', 4, 'C', 'h', 'e', ' ', 3, 'P', 'u', ' ', 5, 'J', 'i', 'n', 'g', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 5, 'T', 'i', 'n', 'g', ' ', 4, 'W', 'a', 'n', ' ', 4, 'H', 'a', 'i', ' ', 5, 'P', 'e', 'n', 'g', ' ', 5, 'L', 'a', 'n', 'g', ' ', 5, 'S', 'h', 'a', 'n', ' ', 3, 'H', 'u', ' ', 5, 'F', 'e', 'n', 'g', ' ', 4, 'C', 'h', 'i', ' ', 5, 'R', 'o', 'n', 'g', ' ', 3, 'H', 'u', ' ', 3, 'X', 'i', ' ', 4, 'S', 'h', 'u', ' ', 3, 'H', 'e', ' ', 4, 'X', 'u', 'n', ' ', 3, 'K', 'u', ' ', 4, 'J', 'u', 'e', ' ', 5, 'X', 'i', 'a', 'o', ' ', 3, 'X', 'i', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'H', 'a', 'n', ' ', 7, 'Z', 'h', 'u', 'a', 'n', 'g', ' ', 4, 'J', 'u', 'n', ' ', 3, 'D', 'i', ' ', 4, 'X', 'i', 'e', ' ', 3, 'J', 'i', ' ', 3, 'W', 'u', ' ', 4, 'H', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'H', 'u', 'a', 'n', ' ', 4, 'M', 'e', 'n', ' ', 3, 'J', 'u', ' ', 5, 'C', 'h', 'o', 'u', ' ', 4, 'B', 'e', 'i', ' ', 4, 'F', 'e', 'n', ' ', 4, 'L', 'i', 'n', ' ', 4, 'K', 'u', 'n', ' ', 4, 'H', 'u', 'n', ' ', 4, 'T', 'u', 'n', ' ', 3, 'X', 'i', ' ', 4, 'C', 'u', 'i', ' ', 3, 'W', 'u', ' ', 5, 'H', 'o', 'n', 'g', ' ', 3, 'J', 'u', ' ', 3, 'F', 'u', ' ', 3, 'W', 'o', ' ', 5, 'J', 'i', 'a', 'o', ' ', 5, 'C', 'o', 'n', 'g', ' ', 5, 'F', 'e', 'n', 'g', ' ', 5, 'P', 'i', 'n', 'g', ' ', 6, 'Q', 'i', 'o', 'n', 'g', ' ', 4, 'R', 'u', 'o', ' ', 3, 'X', 'i', ' ', 6, 'Q', 'i', 'o', 'n', 'g', ' ', 4, 'X', 'i', 'n', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 4, 'J', 'u', 'e', ' ', 3, 'Y', 'u', ' ', 5, 'G', 'a', 'n', 'g', ' ', 4, 'R', 'a', 'n', ' ', 3, 'P', 'i', ' ', 3, 'G', 'u', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 5, 'S', 'h', 'a', 'o', ' ', 5, 'C', 'h', 'e', 'n', ' ', 3, 'H', 'e', ' ', 4, 'K', 'u', 'i', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 5, 'D', 'u', 'a', 'n', ' ', 4, 'X', 'i', 'a', ' ', 4, 'H', 'u', 'i', ' ', 5, 'F', 'e', 'n', 'g', ' ', 5, 'L', 'i', 'a', 'n', ' ', 5, 'X', 'u', 'a', 'n', ' ', 5, 'X', 'i', 'n', 'g', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'o', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'B', 'i', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'W', 'e', 'i', ' ', 5, 'T', 'u', 'a', 'n', ' ', 5, 'T', 'i', 'a', 'n', ' ', 3, 'X', 'i', ' ', 5, 'N', 'u', 'a', 'n', ' ', 5, 'N', 'u', 'a', 'n', ' ', 5, 'C', 'h', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 6, 'J', 'i', 'o', 'n', 'g', ' ', 6, 'J', 'i', 'o', 'n', 'g', ' ', 3, 'Y', 'u', ' ', 4, 'M', 'e', 'i', ' ', 4, 'S', 'h', 'a', ' ', 4, 'W', 'e', 'i', ' ', 3, 'Y', 'e', ' ', 4, 'X', 'i', 'n', ' ', 6, 'Q', 'i', 'o', 'n', 'g', ' ', 4, 'R', 'o', 'u', ' ', 4, 'M', 'e', 'i', ' ', 5, 'H', 'u', 'a', 'n', ' ', 3, 'X', 'u', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 4, 'W', 'e', 'i', ' ', 4, 'F', 'a', 'n', ' ', 4, 'Q', 'i', 'u', ' ', 4, 'S', 'u', 'i', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 4, 'L', 'i', 'e', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'J', 'i', 'e', ' ', 4, 'G', 'a', 'o', ' ', 4, 'G', 'u', 'a', ' ', 4, 'B', 'a', 'o', ' ', 3, 'H', 'u', ' ', 4, 'Y', 'u', 'n', ' ', 4, 'X', 'i', 'a', ' ', 5, 'B', 'i', 'a', 'n', ' ', 4, 'G', 'o', 'u', ' ', 4, 'T', 'u', 'i', ' ', 5, 'T', 'a', 'n', 'g', ' ', 5, 'C', 'h', 'a', 'o', ' ', 5, 'S', 'h', 'a', 'n', ' ', 2, 'N', ' ', 3, 'B', 'o', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 4, 'X', 'i', 'e', ' ', 3, 'X', 'i', ' ', 3, 'W', 'u', ' ', 3, 'X', 'i', ' ', 4, 'Y', 'u', 'n', ' ', 3, 'H', 'e', ' ', 3, 'H', 'e', ' ', 3, 'X', 'i', ' ', 4, 'Y', 'u', 'n', ' ', 6, 'X', 'i', 'o', 'n', 'g', ' ', 4, 'N', 'a', 'i', ' ', 5, 'S', 'h', 'a', 'n', ' ', 6, 'Q', 'i', 'o', 'n', 'g', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'X', 'u', 'n', ' ', 3, 'M', 'i', ' ', 5, 'L', 'i', 'a', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'W', 'e', 'n', ' ', 5, 'R', 'o', 'n', 'g', ' ', 8, 'O', 'o', 'z', 'u', 't', 's', 'u', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 4, 'L', 'i', 'u', ' ', 3, 'X', 'i', ' ', 3, 'B', 'i', ' ', 5, 'B', 'i', 'a', 'o', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 3, 'L', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'S', 'h', 'o', 'u', ' ', 3, 'Y', 'i', ' ', 4, 'L', 'o', 'u', ' ', 5, 'F', 'e', 'n', 'g', ' ', 4, 'S', 'u', 'i', ' ', 3, 'Y', 'i', ' ', 5, 'T', 'o', 'n', 'g', ' ', 4, 'J', 'u', 'e', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 4, 'Y', 'u', 'n', ' ', 3, 'H', 'u', ' ', 3, 'Y', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'A', 'o', ' ', 4, 'W', 'e', 'i', ' ', 5, 'L', 'i', 'a', 'o', ' ', 4, 'H', 'a', 'n', ' ', 3, 'O', 'u', ' ', 3, 'R', 'e', ' ', 6, 'J', 'i', 'o', 'n', 'g', ' ', 4, 'M', 'a', 'n', ' ', 6, 'S', 'h', 'a', 'n', 'g', ' ', 5, 'C', 'u', 'a', 'n', ' ', 5, 'Z', 'e', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'X', 'i', ' ', 3, 'X', 'i', ' ', 3, 'X', 'i', ' ', 3, 'Y', 'i', ' ', 5, 'X', 'i', 'a', 'o', ' ', 4, 'C', 'h', 'i', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 5, 'C', 'h', 'a', 'n', ' ', 3, 'Y', 'e', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'R', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 4, 'Z', 'u', 'n', ' ', 5, 'D', 'e', 'n', 'g', ' ', 4, 'D', 'u', 'n', ' ', 5, 'S', 'h', 'e', 'n', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'F', 'e', 'n', ' ', 3, 'S', 'i', ' ', 5, 'L', 'i', 'a', 'o', ' ', 3, 'Y', 'u', ' ', 4, 'L', 'i', 'n', ' ', 5, 'T', 'o', 'n', 'g', ' ', 5, 'S', 'h', 'a', 'o', ' ', 4, 'F', 'e', 'n', ' ', 4, 'F', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'X', 'u', 'n', ' ', 4, 'L', 'a', 'n', ' ', 4, 'M', 'e', 'i', ' ', 5, 'T', 'a', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 5, 'J', 'i', 'n', 'g', ' ', 4, 'M', 'e', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 3, 'Y', 'u', ' ', 3, 'Y', 'i', ' ', 4, 'X', 'u', 'e', ' ', 4, 'L', 'a', 'n', ' ', 4, 'T', 'a', 'i', ' ', 4, 'Z', 'a', 'o', ' ', 4, 'C', 'a', 'n', ' ', 4, 'S', 'u', 'i', ' ', 3, 'X', 'i', ' ', 4, 'Q', 'u', 'e', ' ', 5, 'C', 'o', 'n', 'g', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'H', 'u', 'i', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'X', 'i', 'e', ' ', 5, 'L', 'i', 'n', 'g', ' ', 4, 'W', 'e', 'i', ' ', 3, 'Y', 'i', ' ', 4, 'X', 'i', 'e', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 4, 'H', 'u', 'i', ' ', 6, 'T', 'a', 't', 's', 'u', ' ', 5, 'N', 'u', 'n', 'g', ' ', 4, 'L', 'a', 'n', ' ', 3, 'R', 'u', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'K', 'a', 'o', ' ', 4, 'X', 'u', 'n', ' ', 4, 'J', 'i', 'n', ' ', 5, 'C', 'h', 'o', 'u', ' ', 5, 'C', 'h', 'o', 'u', ' ', 4, 'Y', 'a', 'o', ' ', 3, 'H', 'e', ' ', 4, 'L', 'a', 'n', ' ', 5, 'B', 'i', 'a', 'o', ' ', 5, 'R', 'o', 'n', 'g', ' ', 3, 'L', 'i', ' ', 3, 'M', 'o', ' ', 4, 'B', 'a', 'o', ' ', 4, 'R', 'u', 'o', ' ', 3, 'L', 'u', ' ', 3, 'L', 'a', ' ', 3, 'A', 'o', ' ', 4, 'X', 'u', 'n', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 5, 'S', 'h', 'u', 'o', ' ', 3, 'L', 'i', ' ', 3, 'L', 'u', ' ', 4, 'J', 'u', 'e', ' ', 5, 'L', 'i', 'a', 'o', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'X', 'i', ' ', 4, 'X', 'i', 'e', ' ', 5, 'L', 'o', 'n', 'g', ' ', 3, 'Y', 'e', ' ', 5, 'R', 'a', 'n', 'g', ' ', 4, 'Y', 'u', 'e', ' ', 4, 'L', 'a', 'n', ' ', 5, 'C', 'o', 'n', 'g', ' ', 4, 'J', 'u', 'e', ' ', 5, 'T', 'o', 'n', 'g', ' ', 5, 'G', 'u', 'a', 'n', ' ', 4, 'C', 'h', 'e', ' ', 3, 'M', 'i', ' ', 5, 'T', 'a', 'n', 'g', ' ', 4, 'L', 'a', 'n', ' ', 4, 'Z', 'h', 'u', ' ', 5, 'L', 'i', 'n', 'g', ' ', 5, 'C', 'u', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 5, 'Z', 'h', 'u', 'a', ' ', 13, 'T', 's', 'u', 'm', 'e', 'k', 'a', 'n', 'm', 'u', 'r', 'i', ' ', 3, 'P', 'a', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 4, 'P', 'a', 'o', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 3, 'A', 'i', ' ', 4, 'W', 'e', 'i', ' ', 4, 'J', 'u', 'e', ' ', 4, 'J', 'u', 'e', ' ', 3, 'F', 'u', ' ', 3, 'Y', 'e', ' ', 3, 'B', 'a', ' ', 4, 'D', 'i', 'e', ' ', 3, 'Y', 'e', ' ', 4, 'Y', 'a', 'o', ' ', 3, 'Z', 'u', ' ', 7, 'S', 'h', 'u', 'a', 'n', 'g', ' ', 3, 'E', 'r', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 7, 'C', 'h', 'u', 'a', 'n', 'g', ' ', 3, 'G', 'e', ' ', 5, 'Z', 'a', 'n', 'g', ' ', 4, 'D', 'i', 'e', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 5, 'P', 'i', 'a', 'n', ' ', 4, 'B', 'a', 'n', ' ', 4, 'P', 'a', 'n', ' ', 5, 'S', 'h', 'a', 'o', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'P', 'a', 'i', ' ', 3, 'D', 'u', ' ', 7, 'C', 'h', 'u', 'a', 'n', 'g', ' ', 4, 'T', 'o', 'u', ' ', 4, 'Z', 'h', 'a', ' ', 5, 'B', 'i', 'a', 'n', ' ', 4, 'D', 'i', 'e', ' ', 5, 'B', 'a', 'n', 'g', ' ', 3, 'B', 'o', ' ', 7, 'C', 'h', 'u', 'a', 'n', 'g', ' ', 4, 'Y', 'o', 'u', ' ', 3, 'D', 'u', ' ', 3, 'Y', 'a', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 4, 'N', 'i', 'u', ' ', 8, 'U', 's', 'h', 'i', 'h', 'e', 'n', ' ', 4, 'P', 'i', 'n', ' ', 4, 'J', 'i', 'u', ' ', 4, 'M', 'o', 'u', ' ', 4, 'T', 'u', 'o', ' ', 3, 'M', 'u', ' ', 4, 'L', 'a', 'o', ' ', 4, 'R', 'e', 'n', ' ', 5, 'M', 'a', 'n', 'g', ' ', 5, 'F', 'a', 'n', 'g', ' ', 4, 'M', 'a', 'o', ' ', 3, 'M', 'u', ' ', 5, 'G', 'a', 'n', 'g', ' ', 3, 'W', 'u', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'G', 'e', ' ', 4, 'B', 'e', 'i', ' ', 3, 'S', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'G', 'u', ' ', 4, 'Y', 'o', 'u', ' ', 3, 'G', 'e', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 3, 'M', 'u', ' ', 3, 'D', 'i', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 3, 'Z', 'i', ' ', 3, 'T', 'e', ' ', 3, 'X', 'i', ' ', 5, 'M', 'a', 'n', 'g', ' ', 5, 'K', 'e', 'n', 'g', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 3, 'W', 'u', ' ', 3, 'G', 'u', ' ', 3, 'X', 'i', ' ', 3, 'L', 'i', ' ', 3, 'L', 'i', ' ', 4, 'P', 'o', 'u', ' ', 3, 'J', 'i', ' ', 5, 'G', 'a', 'n', 'g', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'B', 'e', 'n', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 4, 'R', 'u', 'n', ' ', 3, 'D', 'u', ' ', 3, 'J', 'u', ' ', 4, 'J', 'i', 'a', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'F', 'e', 'n', 'g', ' ', 5, 'P', 'i', 'a', 'n', ' ', 3, 'K', 'e', ' ', 3, 'J', 'u', ' ', 4, 'K', 'a', 'o', ' ', 4, 'C', 'h', 'u', ' ', 3, 'X', 'i', ' ', 4, 'B', 'e', 'i', ' ', 4, 'L', 'u', 'o', ' ', 4, 'J', 'i', 'e', ' ', 3, 'M', 'a', ' ', 4, 'S', 'a', 'n', ' ', 4, 'W', 'e', 'i', ' ', 3, 'L', 'i', ' ', 4, 'D', 'u', 'n', ' ', 5, 'T', 'o', 'n', 'g', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 7, 'I', 'k', 'e', 'n', 'i', 'e', ' ', 3, 'L', 'i', ' ', 3, 'D', 'u', ' ', 4, 'L', 'i', 'e', ' ', 3, 'P', 'i', ' ', 5, 'P', 'i', 'a', 'o', ' ', 4, 'B', 'a', 'o', ' ', 3, 'X', 'i', ' ', 5, 'C', 'h', 'o', 'u', ' ', 4, 'W', 'e', 'i', ' ', 4, 'K', 'u', 'i', ' ', 5, 'C', 'h', 'o', 'u', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 4, 'F', 'a', 'n', ' ', 3, 'B', 'a', ' ', 4, 'F', 'a', 'n', ' ', 4, 'Q', 'i', 'u', ' ', 3, 'J', 'i', ' ', 4, 'C', 'a', 'i', ' ', 5, 'C', 'h', 'u', 'o', ' ', 3, 'A', 'n', ' ', 4, 'J', 'i', 'e', ' ', 7, 'Z', 'h', 'u', 'a', 'n', 'g', ' ', 6, 'G', 'u', 'a', 'n', 'g', ' ', 3, 'M', 'a', ' ', 4, 'Y', 'o', 'u', ' ', 5, 'K', 'a', 'n', 'g', ' ', 3, 'B', 'o', ' ', 4, 'H', 'o', 'u', ' ', 3, 'Y', 'a', ' ', 4, 'Y', 'i', 'n', ' ', 5, 'H', 'u', 'a', 'n', ' ', 7, 'Z', 'h', 'u', 'a', 'n', 'g', ' ', 4, 'Y', 'u', 'n', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 4, 'N', 'i', 'u', ' ', 3, 'D', 'i', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 3, 'M', 'u', ' ', 4, 'B', 'e', 'i', ' ', 3, 'P', 'i', ' ', 3, 'J', 'u', ' ', 3, 'N', 'i', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 4, 'P', 'a', 'o', ' ', 4, 'X', 'i', 'a', ' ', 4, 'T', 'u', 'o', ' ', 3, 'H', 'u', ' ', 5, 'L', 'i', 'n', 'g', ' ', 4, 'F', 'e', 'i', ' ', 3, 'P', 'i', ' ', 3, 'N', 'i', ' ', 3, 'A', 'o', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'G', 'o', 'u', ' ', 4, 'Y', 'u', 'e', ' ', 3, 'J', 'u', ' ', 4, 'D', 'a', 'n', ' ', 3, 'P', 'o', ' ', 3, 'G', 'u', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'N', 'i', 'n', 'g', ' ', 5, 'H', 'u', 'a', 'n', ' ', 4, 'H', 'e', 'n', ' ', 5, 'J', 'i', 'a', 'o', ' ', 3, 'H', 'e', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 3, 'J', 'i', ' ', 4, 'X', 'u', 'n', ' ', 5, 'S', 'h', 'a', 'n', ' ', 3, 'T', 'a', ' ', 5, 'R', 'o', 'n', 'g', ' ', 5, 'S', 'h', 'o', 'u', ' ', 5, 'T', 'o', 'n', 'g', ' ', 4, 'L', 'a', 'o', ' ', 3, 'D', 'u', ' ', 4, 'X', 'i', 'a', ' ', 4, 'S', 'h', 'i', ' ', 4, 'H', 'u', 'a', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 3, 'Y', 'u', ' ', 4, 'S', 'u', 'n', ' ', 3, 'Y', 'u', ' ', 3, 'B', 'i', ' ', 5, 'M', 'a', 'n', 'g', ' ', 3, 'X', 'i', ' ', 5, 'J', 'u', 'a', 'n', ' ', 3, 'L', 'i', ' ', 4, 'X', 'i', 'a', ' ', 4, 'Y', 'i', 'n', ' ', 5, 'S', 'u', 'a', 'n', ' ', 5, 'L', 'a', 'n', 'g', ' ', 4, 'B', 'e', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'S', 'h', 'a', ' ', 3, 'L', 'i', ' ', 4, 'H', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'J', 'i', 'n', 'g', ' ', 4, 'P', 'a', 'i', ' ', 4, 'F', 'e', 'i', ' ', 4, 'Y', 'a', 'o', ' ', 3, 'B', 'a', ' ', 3, 'Q', 'i', ' ', 3, 'N', 'i', ' ', 5, 'B', 'i', 'a', 'o', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'L', 'a', 'i', ' ', 3, 'X', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 4, 'K', 'u', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'G', 'u', 'o', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 3, 'M', 'i', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 3, 'Y', 'a', ' ', 5, 'M', 'e', 'n', 'g', ' ', 4, 'C', 'a', 'i', ' ', 3, 'C', 'u', ' ', 4, 'S', 'h', 'e', ' ', 5, 'K', 'a', 'r', 'i', ' ', 4, 'C', 'e', 'n', ' ', 4, 'L', 'u', 'o', ' ', 3, 'H', 'u', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 3, 'J', 'i', ' ', 4, 'W', 'e', 'i', ' ', 5, 'F', 'e', 'n', 'g', ' ', 3, 'W', 'o', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 5, 'X', 'i', 'n', 'g', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'M', 'a', 'o', ' ', 4, 'W', 'e', 'i', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'T', 'u', 'a', 'n', ' ', 3, 'Y', 'a', ' ', 4, 'N', 'a', 'o', ' ', 4, 'X', 'i', 'e', ' ', 4, 'J', 'i', 'a', ' ', 4, 'H', 'o', 'u', ' ', 5, 'B', 'i', 'a', 'n', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'M', 'e', 'i', ' ', 4, 'Z', 'h', 'a', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'S', 'u', 'n', ' ', 3, 'B', 'o', ' ', 5, 'M', 'i', 'n', 'g', ' ', 4, 'H', 'u', 'a', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'S', 'o', 'u', ' ', 3, 'M', 'a', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'D', 'a', 'i', ' ', 3, 'Y', 'u', ' ', 4, 'S', 'h', 'i', ' ', 4, 'H', 'a', 'o', ' ', 3, 'Y', 'i', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 7, 'C', 'h', 'u', 'a', 'n', 'g', ' ', 4, 'H', 'a', 'o', ' ', 4, 'M', 'a', 'n', ' ', 5, 'J', 'i', 'n', 'g', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 3, 'M', 'u', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 5, 'C', 'h', 'a', 'n', ' ', 3, 'A', 'o', ' ', 3, 'A', 'o', ' ', 4, 'H', 'a', 'o', ' ', 4, 'C', 'u', 'i', ' ', 4, 'F', 'e', 'n', ' ', 4, 'J', 'u', 'e', ' ', 3, 'B', 'i', ' ', 3, 'B', 'i', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 3, 'P', 'u', ' ', 4, 'L', 'i', 'n', ' ', 3, 'Y', 'u', ' ', 5, 'T', 'o', 'n', 'g', ' ', 4, 'Y', 'a', 'o', ' ', 5, 'L', 'i', 'a', 'o', ' ', 5, 'S', 'h', 'u', 'o', ' ', 5, 'X', 'i', 'a', 'o', ' ', 4, 'S', 'w', 'u', ' ', 4, 'T', 'o', 'n', ' ', 3, 'X', 'i', ' ', 3, 'G', 'e', ' ', 5, 'J', 'u', 'a', 'n', ' ', 3, 'D', 'u', ' ', 4, 'H', 'u', 'i', ' ', 5, 'K', 'u', 'a', 'i', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'X', 'i', 'e', ' ', 3, 'T', 'a', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'X', 'u', 'n', ' ', 5, 'N', 'i', 'n', 'g', ' ', 4, 'P', 'i', 'n', ' ', 4, 'H', 'u', 'o', ' ', 4, 'N', 'o', 'u', ' ', 5, 'M', 'e', 'n', 'g', ' ', 4, 'L', 'i', 'e', ' ', 4, 'N', 'a', 'o', ' ', 6, 'G', 'u', 'a', 'n', 'g', ' ', 5, 'S', 'h', 'o', 'u', ' ', 3, 'L', 'u', ' ', 3, 'T', 'a', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'M', 'i', ' ', 5, 'R', 'a', 'n', 'g', ' ', 5, 'H', 'u', 'a', 'n', ' ', 4, 'N', 'a', 'o', ' ', 4, 'L', 'u', 'o', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'Q', 'i', ' ', 4, 'J', 'u', 'e', ' ', 5, 'X', 'u', 'a', 'n', ' ', 5, 'M', 'i', 'a', 'o', ' ', 3, 'Z', 'i', ' ', 3, 'L', 'u', ' ', 3, 'L', 'u', ' ', 3, 'Y', 'u', ' ', 3, 'S', 'u', ' ', 5, 'W', 'a', 'n', 'g', ' ', 4, 'Q', 'i', 'u', ' ', 3, 'G', 'a', ' ', 5, 'D', 'i', 'n', 'g', ' ', 3, 'L', 'e', ' ', 3, 'B', 'a', ' ', 3, 'J', 'i', ' ', 5, 'H', 'o', 'n', 'g', ' ', 3, 'D', 'i', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 4, 'G', 'a', 'n', ' ', 4, 'J', 'i', 'u', ' ', 3, 'Y', 'u', ' ', 3, 'J', 'i', ' ', 3, 'Y', 'u', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 3, 'M', 'a', ' ', 5, 'G', 'o', 'n', 'g', ' ', 3, 'W', 'u', ' ', 3, 'F', 'u', ' ', 4, 'W', 'e', 'n', ' ', 4, 'J', 'i', 'e', ' ', 3, 'Y', 'a', ' ', 4, 'F', 'e', 'n', ' ', 5, 'B', 'i', 'a', 'n', ' ', 5, 'B', 'e', 'n', 'g', ' ', 4, 'Y', 'u', 'e', ' ', 4, 'J', 'u', 'e', ' ', 4, 'Y', 'u', 'n', ' ', 4, 'J', 'u', 'e', ' ', 4, 'W', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'M', 'e', 'i', ' ', 4, 'D', 'a', 'n', ' ', 3, 'P', 'i', ' ', 4, 'W', 'e', 'i', ' ', 5, 'H', 'u', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 5, 'L', 'i', 'n', 'g', ' ', 4, 'D', 'a', 'i', ' ', 3, 'Y', 'i', ' ', 3, 'A', 'n', ' ', 5, 'P', 'i', 'n', 'g', ' ', 5, 'D', 'i', 'a', 'n', ' ', 3, 'F', 'u', ' ', 5, 'X', 'u', 'a', 'n', ' ', 3, 'X', 'i', ' ', 3, 'B', 'o', ' ', 3, 'C', 'i', ' ', 4, 'G', 'o', 'u', ' ', 4, 'J', 'i', 'a', ' ', 5, 'S', 'h', 'a', 'o', ' ', 3, 'P', 'o', ' ', 3, 'C', 'i', ' ', 3, 'K', 'e', ' ', 4, 'R', 'a', 'n', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 5, 'S', 'h', 'e', 'n', ' ', 3, 'Y', 'i', ' ', 3, 'Z', 'u', ' ', 4, 'J', 'i', 'a', ' ', 4, 'M', 'i', 'n', ' ', 5, 'S', 'h', 'a', 'n', ' ', 4, 'L', 'i', 'u', ' ', 3, 'B', 'i', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 4, 'J', 'u', 'e', ' ', 3, 'F', 'a', ' ', 5, 'L', 'o', 'n', 'g', ' ', 4, 'J', 'i', 'n', ' ', 5, 'J', 'i', 'a', 'o', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'L', 'i', ' ', 6, 'G', 'u', 'a', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 5, 'G', 'o', 'n', 'g', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'X', 'i', 'u', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 3, 'X', 'u', ' ', 4, 'L', 'u', 'o', ' ', 3, 'S', 'u', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'Q', 'i', 'n', ' ', 4, 'K', 'e', 'n', ' ', 4, 'X', 'u', 'n', ' ', 4, 'B', 'a', 'o', ' ', 3, 'E', 'r', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'X', 'i', 'a', ' ', 5, 'H', 'e', 'n', 'g', ' ', 4, 'G', 'u', 'i', ' ', 6, 'C', 'h', 'o', 'n', 'g', ' ', 3, 'X', 'u', ' ', 4, 'B', 'a', 'n', ' ', 4, 'P', 'e', 'i', ' ', 5, 'D', 'a', 'n', 'g', ' ', 3, 'E', 'i', ' ', 4, 'H', 'u', 'n', ' ', 4, 'W', 'e', 'n', ' ', 2, 'E', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 3, 'T', 'i', ' ', 3, 'W', 'u', ' ', 3, 'W', 'u', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 4, 'J', 'u', 'n', ' ', 4, 'M', 'e', 'i', ' ', 4, 'B', 'e', 'i', ' ', 5, 'T', 'i', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'C', 'h', 'u', 'o', ' ', 4, 'H', 'a', 'n', ' ', 5, 'X', 'u', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'Q', 'i', 'u', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 5, 'L', 'a', 'n', 'g', ' ', 3, 'L', 'i', ' ', 4, 'X', 'i', 'u', ' ', 3, 'F', 'u', ' ', 4, 'L', 'i', 'u', ' ', 3, 'Y', 'e', ' ', 3, 'X', 'i', ' ', 5, 'L', 'i', 'n', 'g', ' ', 3, 'L', 'i', ' ', 4, 'J', 'i', 'n', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'S', 'u', 'o', ' ', 8, 'C', 'h', 'i', 'i', 's', 'a', 'i', ' ', 4, 'W', 'a', 'n', ' ', 5, 'D', 'i', 'a', 'n', ' ', 4, 'P', 'i', 'n', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 4, 'C', 'u', 'i', ' ', 4, 'M', 'i', 'n', ' ', 3, 'Y', 'u', ' ', 3, 'J', 'u', ' ', 5, 'C', 'h', 'e', 'n', ' ', 4, 'L', 'a', 'i', ' ', 4, 'W', 'e', 'n', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 4, 'W', 'e', 'i', ' ', 5, 'D', 'i', 'a', 'n', ' ', 4, 'C', 'h', 'u', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 4, 'P', 'e', 'i', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 3, 'H', 'u', ' ', 3, 'Q', 'i', ' ', 2, 'E', ' ', 4, 'K', 'u', 'n', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 3, 'Q', 'i', ' ', 5, 'B', 'e', 'n', 'g', ' ', 4, 'W', 'a', 'n', ' ', 3, 'L', 'u', ' ', 5, 'C', 'o', 'n', 'g', ' ', 5, 'G', 'u', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'D', 'i', 'a', 'o', ' ', 4, 'B', 'e', 'i', ' ', 4, 'L', 'i', 'n', ' ', 4, 'Q', 'i', 'n', ' ', 3, 'P', 'i', ' ', 3, 'P', 'a', ' ', 4, 'Q', 'u', 'e', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 4, 'Q', 'i', 'n', ' ', 3, 'F', 'a', ' ', 6, 'Q', 'i', 'o', 'n', 'g', ' ', 3, 'D', 'u', ' ', 4, 'J', 'i', 'e', ' ', 4, 'H', 'u', 'n', ' ', 3, 'Y', 'u', ' ', 4, 'M', 'a', 'o', ' ', 4, 'M', 'e', 'i', ' ', 5, 'C', 'h', 'u', 'n', ' ', 5, 'X', 'u', 'a', 'n', ' ', 3, 'T', 'i', ' ', 5, 'X', 'i', 'n', 'g', ' ', 4, 'D', 'a', 'i', ' ', 4, 'R', 'o', 'u', ' ', 4, 'M', 'i', 'n', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 4, 'W', 'e', 'i', ' ', 5, 'R', 'u', 'a', 'n', ' ', 5, 'H', 'u', 'a', 'n', ' ', 4, 'J', 'i', 'e', ' ', 6, 'C', 'h', 'u', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 5, 'L', 'i', 'a', 'n', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 4, 'X', 'i', 'a', ' ', 5, 'D', 'u', 'a', 'n', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 3, 'Y', 'e', ' ', 4, 'N', 'a', 'o', ' ', 3, 'H', 'u', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 3, 'Y', 'u', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 4, 'R', 'u', 'i', ' ', 3, 'S', 'e', ' ', 4, 'L', 'i', 'u', ' ', 4, 'S', 'h', 'i', ' ', 5, 'R', 'o', 'n', 'g', ' ', 4, 'S', 'u', 'o', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'W', 'e', 'n', ' ', 3, 'W', 'u', ' ', 4, 'J', 'i', 'n', ' ', 4, 'J', 'i', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 3, 'M', 'a', ' ', 4, 'T', 'a', 'o', ' ', 4, 'L', 'i', 'u', ' ', 5, 'T', 'a', 'n', 'g', ' ', 3, 'L', 'i', ' ', 5, 'L', 'a', 'n', 'g', ' ', 4, 'G', 'u', 'i', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 4, 'C', 'u', 'o', ' ', 4, 'J', 'u', 'e', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 4, 'Y', 'a', 'o', ' ', 3, 'A', 'i', ' ', 4, 'B', 'i', 'n', ' ', 3, 'T', 'u', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 4, 'K', 'u', 'n', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 5, 'C', 'o', 'n', 'g', ' ', 4, 'J', 'i', 'n', ' ', 3, 'Y', 'i', ' ', 4, 'C', 'u', 'i', ' ', 5, 'C', 'o', 'n', 'g', ' ', 3, 'Q', 'i', ' ', 3, 'L', 'i', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'S', 'u', 'o', ' ', 4, 'Q', 'i', 'u', ' ', 5, 'X', 'u', 'a', 'n', ' ', 3, 'A', 'o', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'M', 'a', 'n', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 4, 'Y', 'i', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'L', 'u', ' ', 3, 'W', 'u', ' ', 5, 'D', 'e', 'n', 'g', ' ', 5, 'X', 'i', 'o', 'u', ' ', 5, 'Z', 'e', 'n', 'g', ' ', 4, 'X', 'u', 'n', ' ', 3, 'Q', 'u', ' ', 5, 'D', 'a', 'n', 'g', ' ', 4, 'L', 'i', 'n', ' ', 5, 'L', 'i', 'a', 'o', ' ', 6, 'Q', 'i', 'o', 'n', 'g', ' ', 3, 'S', 'u', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 4, 'G', 'u', 'i', ' ', 3, 'P', 'u', ' ', 5, 'J', 'i', 'n', 'g', ' ', 4, 'F', 'a', 'n', ' ', 4, 'J', 'i', 'n', ' ', 4, 'L', 'i', 'u', ' ', 3, 'J', 'i', ' ', 5, 'J', 'i', 'n', 'g', ' ', 3, 'A', 'i', ' ', 3, 'B', 'i', ' ', 4, 'C', 'a', 'n', ' ', 3, 'Q', 'u', ' ', 4, 'Z', 'a', 'o', ' ', 5, 'D', 'a', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'G', 'u', 'n', ' ', 4, 'T', 'a', 'n', ' ', 4, 'H', 'u', 'i', ' ', 5, 'H', 'u', 'a', 'n', ' ', 3, 'S', 'e', ' ', 4, 'S', 'u', 'i', ' ', 5, 'T', 'i', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 4, 'J', 'i', 'n', ' ', 3, 'L', 'u', ' ', 4, 'B', 'i', 'n', ' ', 5, 'S', 'h', 'o', 'u', ' ', 4, 'W', 'e', 'n', ' ', 4, 'Z', 'u', 'i', ' ', 4, 'L', 'a', 'n', ' ', 3, 'X', 'i', ' ', 3, 'J', 'i', ' ', 5, 'X', 'u', 'a', 'n', ' ', 5, 'R', 'u', 'a', 'n', ' ', 4, 'H', 'u', 'o', ' ', 4, 'G', 'a', 'i', ' ', 4, 'L', 'e', 'i', ' ', 3, 'D', 'u', ' ', 3, 'L', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'R', 'o', 'u', ' ', 3, 'L', 'i', ' ', 4, 'Z', 'a', 'n', ' ', 6, 'Q', 'i', 'o', 'n', 'g', ' ', 4, 'Z', 'h', 'e', ' ', 4, 'G', 'u', 'i', ' ', 4, 'S', 'u', 'i', ' ', 3, 'L', 'a', ' ', 5, 'L', 'o', 'n', 'g', ' ', 3, 'L', 'u', ' ', 3, 'L', 'i', ' ', 4, 'Z', 'a', 'n', ' ', 4, 'L', 'a', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 3, 'M', 'i', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 3, 'X', 'i', ' ', 5, 'G', 'u', 'a', 'n', ' ', 4, 'D', 'a', 'o', ' ', 4, 'Z', 'a', 'n', ' ', 5, 'H', 'u', 'a', 'n', ' ', 4, 'G', 'u', 'a', ' ', 3, 'B', 'o', ' ', 4, 'D', 'i', 'e', ' ', 4, 'B', 'a', 'o', ' ', 3, 'H', 'u', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'P', 'i', 'a', 'o', ' ', 4, 'B', 'a', 'n', ' ', 5, 'R', 'a', 'n', 'g', ' ', 3, 'L', 'i', ' ', 3, 'W', 'a', ' ', 11, 'D', 'e', 'k', 'a', 'g', 'u', 'r', 'a', 'm', 'u', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'F', 'a', 'n', ' ', 4, 'P', 'e', 'n', ' ', 5, 'F', 'a', 'n', 'g', ' ', 4, 'D', 'a', 'n', ' ', 5, 'W', 'e', 'n', 'g', ' ', 3, 'O', 'u', ' ', 12, 'D', 'e', 's', 'h', 'i', 'g', 'u', 'r', 'a', 'm', 'u', ' ', 11, 'M', 'i', 'r', 'i', 'g', 'u', 'r', 'a', 'm', 'u', ' ', 5, 'T', 'h', 'o', 'n', ' ', 3, 'H', 'u', ' ', 5, 'L', 'i', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 5, 'P', 'i', 'n', 'g', ' ', 3, 'C', 'i', ' ', 11, 'H', 'e', 'k', 'u', 't', 'o', 'g', 'u', 'r', 'a', ' ', 5, 'J', 'u', 'a', 'n', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 4, 'C', 'h', 'i', ' ', 7, 'S', 'a', 'r', 'a', 'k', 'e', ' ', 5, 'D', 'a', 'n', 'g', ' ', 5, 'M', 'e', 'n', 'g', ' ', 4, 'P', 'o', 'u', ' ', 5, 'Z', 'h', 'u', 'i', ' ', 5, 'P', 'i', 'n', 'g', ' ', 5, 'B', 'i', 'a', 'n', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 11, 'S', 'e', 'n', 'c', 'h', 'i', 'g', 'u', 'r', 'a', ' ', 3, 'C', 'i', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 3, 'Q', 'i', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'L', 'o', 'u', ' ', 3, 'D', 'i', ' ', 3, 'O', 'u', ' ', 5, 'M', 'e', 'n', 'g', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 5, 'P', 'e', 'n', 'g', ' ', 4, 'L', 'i', 'n', ' ', 5, 'Z', 'e', 'n', 'g', ' ', 3, 'W', 'u', ' ', 3, 'P', 'i', ' ', 4, 'D', 'a', 'n', ' ', 5, 'W', 'e', 'n', 'g', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'G', 'a', 'n', ' ', 4, 'D', 'a', 'i', ' ', 5, 'S', 'h', 'e', 'n', ' ', 5, 'T', 'i', 'a', 'n', ' ', 5, 'T', 'i', 'a', 'n', ' ', 4, 'H', 'a', 'n', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 5, 'C', 'h', 'a', 'n', ' ', 5, 'C', 'h', 'a', 'n', ' ', 4, 'R', 'u', 'i', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 3, 'S', 'u', ' ', 4, 'S', 'e', 'n', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 6, 'S', 'h', 'u', 'a', 'i', ' ', 3, 'L', 'u', ' ', 3, 'F', 'u', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 5, 'B', 'e', 'n', 'g', ' ', 5, 'F', 'e', 'n', 'g', ' ', 5, 'N', 'i', 'n', 'g', ' ', 5, 'T', 'i', 'a', 'n', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'J', 'i', 'a', ' ', 5, 'S', 'h', 'e', 'n', ' ', 4, 'Z', 'h', 'a', ' ', 5, 'D', 'i', 'a', 'n', ' ', 3, 'F', 'u', ' ', 4, 'N', 'a', 'n', ' ', 5, 'D', 'i', 'a', 'n', ' ', 5, 'P', 'i', 'n', 'g', ' ', 5, 'T', 'i', 'n', 'g', ' ', 4, 'H', 'u', 'a', ' ', 5, 'T', 'i', 'n', 'g', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 3, 'Z', 'i', ' ', 5, 'M', 'e', 'n', 'g', ' ', 3, 'B', 'i', ' ', 3, 'Q', 'i', ' ', 4, 'L', 'i', 'u', ' ', 4, 'X', 'u', 'n', ' ', 4, 'L', 'i', 'u', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 3, 'M', 'u', ' ', 4, 'Y', 'u', 'n', ' ', 4, 'F', 'a', 'n', ' ', 3, 'F', 'u', ' ', 5, 'G', 'e', 'n', 'g', ' ', 5, 'T', 'i', 'a', 'n', ' ', 4, 'J', 'i', 'e', ' ', 4, 'J', 'i', 'e', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 4, 'W', 'e', 'i', ' ', 3, 'F', 'u', ' ', 5, 'T', 'i', 'a', 'n', ' ', 3, 'M', 'u', ' ', 4, 'T', 'a', 'p', ' ', 4, 'P', 'a', 'n', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 3, 'W', 'a', ' ', 3, 'D', 'a', ' ', 4, 'N', 'a', 'n', ' ', 4, 'L', 'i', 'u', ' ', 4, 'B', 'e', 'n', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 4, 'C', 'h', 'u', ' ', 3, 'M', 'u', ' ', 3, 'M', 'u', ' ', 3, 'C', 'e', ' ', 4, 'C', 'e', 'n', ' ', 4, 'G', 'a', 'i', ' ', 3, 'B', 'i', ' ', 3, 'D', 'a', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'L', 'u', 'e', ' ', 3, 'Q', 'i', ' ', 4, 'L', 'u', 'e', ' ', 4, 'P', 'a', 'n', ' ', 5, 'K', 'e', 's', 'a', ' ', 4, 'F', 'a', 'n', ' ', 4, 'H', 'u', 'a', ' ', 3, 'Y', 'u', ' ', 3, 'Y', 'u', ' ', 3, 'M', 'u', ' ', 4, 'J', 'u', 'n', ' ', 3, 'Y', 'i', ' ', 4, 'L', 'i', 'u', ' ', 3, 'Y', 'u', ' ', 4, 'D', 'i', 'e', ' ', 5, 'C', 'h', 'o', 'u', ' ', 4, 'H', 'u', 'a', ' ', 5, 'D', 'a', 'n', 'g', ' ', 5, 'C', 'h', 'u', 'o', ' ', 3, 'J', 'i', ' ', 4, 'W', 'a', 'n', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 5, 'T', 'u', 'a', 'n', ' ', 4, 'L', 'e', 'i', ' ', 3, 'J', 'i', ' ', 4, 'C', 'h', 'a', ' ', 4, 'L', 'i', 'u', ' ', 7, 'T', 'a', 't', 'a', 'm', 'u', ' ', 5, 'T', 'u', 'a', 'n', ' ', 4, 'L', 'i', 'n', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 5, 'C', 'h', 'o', 'u', ' ', 3, 'B', 'o', ' ', 4, 'D', 'i', 'e', ' ', 4, 'D', 'i', 'e', ' ', 3, 'P', 'i', ' ', 4, 'N', 'i', 'e', ' ', 4, 'D', 'a', 'n', ' ', 4, 'S', 'h', 'u', ' ', 4, 'S', 'h', 'u', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'Y', 'i', ' ', 7, 'C', 'h', 'u', 'a', 'n', 'g', ' ', 4, 'N', 'a', 'i', ' ', 5, 'D', 'i', 'n', 'g', ' ', 3, 'B', 'i', ' ', 4, 'J', 'i', 'e', ' ', 5, 'L', 'i', 'a', 'o', ' ', 5, 'G', 'o', 'n', 'g', ' ', 3, 'G', 'e', ' ', 4, 'J', 'i', 'u', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 4, 'X', 'i', 'a', ' ', 5, 'S', 'h', 'a', 'n', ' ', 3, 'X', 'u', ' ', 4, 'N', 'u', 'e', ' ', 3, 'L', 'i', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 5, 'C', 'h', 'e', 'n', ' ', 4, 'Y', 'o', 'u', ' ', 3, 'B', 'a', ' ', 4, 'J', 'i', 'e', ' ', 4, 'J', 'u', 'e', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'X', 'i', 'a', ' ', 4, 'C', 'u', 'i', ' ', 3, 'B', 'i', ' ', 3, 'Y', 'i', ' ', 3, 'L', 'i', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 7, 'C', 'h', 'u', 'a', 'n', 'g', ' ', 5, 'F', 'e', 'n', 'g', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'P', 'a', 'o', ' ', 3, 'P', 'i', ' ', 4, 'G', 'a', 'n', ' ', 3, 'K', 'e', ' ', 3, 'C', 'i', ' ', 4, 'X', 'i', 'e', ' ', 3, 'Q', 'i', ' ', 4, 'D', 'a', 'n', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 3, 'F', 'a', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'T', 'e', 'n', 'g', ' ', 3, 'J', 'u', ' ', 3, 'J', 'i', ' ', 4, 'F', 'e', 'i', ' ', 3, 'Q', 'u', ' ', 5, 'D', 'i', 'a', 'n', ' ', 4, 'J', 'i', 'a', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'C', 'h', 'a', ' ', 5, 'B', 'i', 'n', 'g', ' ', 3, 'N', 'i', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 5, 'J', 'i', 'n', 'g', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 6, 'C', 'h', 'o', 'n', 'g', ' ', 5, 'T', 'o', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 4, 'K', 'a', 'i', ' ', 4, 'W', 'e', 'i', ' ', 4, 'H', 'u', 'i', ' ', 4, 'D', 'u', 'o', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 4, 'C', 'h', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'H', 'e', 'n', ' ', 3, 'Y', 'a', ' ', 4, 'M', 'e', 'i', ' ', 4, 'D', 'o', 'u', ' ', 5, 'J', 'i', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'o', ' ', 5, 'T', 'o', 'n', 'g', ' ', 3, 'T', 'u', ' ', 5, 'M', 'a', 'n', 'g', ' ', 3, 'P', 'i', ' ', 5, 'X', 'i', 'a', 'o', ' ', 5, 'S', 'u', 'a', 'n', ' ', 3, 'P', 'u', ' ', 3, 'L', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'C', 'u', 'o', ' ', 4, 'D', 'u', 'o', ' ', 3, 'W', 'u', ' ', 4, 'S', 'h', 'a', ' ', 4, 'L', 'a', 'o', ' ', 5, 'S', 'h', 'o', 'u', ' ', 5, 'H', 'u', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 5, 'P', 'e', 'n', 'g', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 5, 'G', 'u', 'a', 'n', ' ', 4, 'T', 'a', 'n', ' ', 4, 'F', 'e', 'i', ' ', 3, 'M', 'a', ' ', 4, 'L', 'i', 'n', ' ', 4, 'C', 'h', 'i', ' ', 3, 'J', 'i', ' ', 5, 'D', 'i', 'a', 'n', ' ', 3, 'A', 'n', ' ', 4, 'C', 'h', 'i', ' ', 3, 'B', 'i', ' ', 4, 'B', 'e', 'i', ' ', 4, 'M', 'i', 'n', ' ', 3, 'G', 'u', ' ', 4, 'D', 'u', 'i', ' ', 2, 'E', ' ', 4, 'W', 'e', 'i', ' ', 3, 'Y', 'u', ' ', 4, 'C', 'u', 'i', ' ', 3, 'Y', 'a', ' ', 4, 'Z', 'h', 'u', ' ', 3, 'C', 'u', ' ', 4, 'D', 'a', 'n', ' ', 5, 'S', 'h', 'e', 'n', ' ', 6, 'Z', 'h', 'u', 'n', 'g', ' ', 3, 'J', 'i', ' ', 3, 'Y', 'u', ' ', 4, 'H', 'o', 'u', ' ', 5, 'F', 'e', 'n', 'g', ' ', 3, 'L', 'a', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 5, 'S', 'h', 'e', 'n', ' ', 3, 'T', 'u', ' ', 3, 'Y', 'u', ' ', 4, 'G', 'u', 'a', ' ', 4, 'W', 'e', 'n', ' ', 5, 'H', 'u', 'a', 'n', ' ', 3, 'K', 'u', ' ', 4, 'J', 'i', 'a', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'Y', 'i', ' ', 3, 'L', 'u', ' ', 4, 'S', 'a', 'o', ' ', 4, 'J', 'u', 'e', ' ', 4, 'C', 'h', 'i', ' ', 3, 'X', 'i', ' ', 5, 'G', 'u', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 4, 'W', 'e', 'n', ' ', 3, 'J', 'i', ' ', 7, 'C', 'h', 'u', 'a', 'n', 'g', ' ', 4, 'B', 'a', 'n', ' ', 4, 'L', 'e', 'i', ' ', 4, 'L', 'i', 'u', ' ', 5, 'C', 'h', 'a', 'i', ' ', 5, 'S', 'h', 'o', 'u', ' ', 4, 'N', 'u', 'e', ' ', 5, 'D', 'i', 'a', 'n', ' ', 3, 'D', 'a', ' ', 4, 'P', 'i', 'e', ' ', 4, 'T', 'a', 'n', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 5, 'B', 'i', 'a', 'o', ' ', 5, 'S', 'h', 'e', 'n', ' ', 3, 'C', 'u', ' ', 4, 'L', 'u', 'o', ' ', 3, 'Y', 'i', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 5, 'C', 'h', 'o', 'u', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 5, 'Z', 'h', 'a', 'i', ' ', 4, 'S', 'o', 'u', ' ', 4, 'S', 'u', 'o', ' ', 4, 'Q', 'u', 'e', ' ', 5, 'D', 'i', 'a', 'o', ' ', 4, 'L', 'o', 'u', ' ', 3, 'L', 'u', ' ', 3, 'M', 'o', ' ', 4, 'J', 'i', 'n', ' ', 4, 'Y', 'i', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 3, 'F', 'u', ' ', 5, 'L', 'i', 'a', 'o', ' ', 5, 'L', 'o', 'n', 'g', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 4, 'L', 'i', 'u', ' ', 4, 'L', 'a', 'o', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'F', 'e', 'i', ' ', 4, 'D', 'a', 'n', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'H', 'e', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'B', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'G', 'u', 'a', 'n', ' ', 5, 'G', 'u', 'a', 'i', ' ', 5, 'N', 'o', 'n', 'g', ' ', 3, 'Y', 'u', ' ', 4, 'W', 'e', 'i', ' ', 3, 'Y', 'i', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 3, 'P', 'i', ' ', 4, 'L', 'e', 'i', ' ', 3, 'L', 'i', ' ', 4, 'S', 'h', 'u', ' ', 4, 'D', 'a', 'n', ' ', 4, 'L', 'i', 'n', ' ', 5, 'D', 'i', 'a', 'n', ' ', 4, 'L', 'i', 'n', ' ', 4, 'L', 'a', 'i', ' ', 4, 'P', 'i', 'e', ' ', 3, 'J', 'i', ' ', 4, 'C', 'h', 'i', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'J', 'i', 'e', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 3, 'L', 'i', ' ', 4, 'H', 'u', 'o', ' ', 4, 'L', 'a', 'i', ' ', 6, 'S', 'h', 'a', 'k', 'u', ' ', 5, 'D', 'i', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'Q', 'u', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 4, 'T', 'a', 'n', ' ', 5, 'D', 'i', 'a', 'n', ' ', 4, 'L', 'u', 'o', ' ', 5, 'L', 'u', 'a', 'n', ' ', 5, 'L', 'u', 'a', 'n', ' ', 3, 'B', 'o', ' ', 4, 'G', 'u', 'i', ' ', 3, 'P', 'o', ' ', 3, 'F', 'a', ' ', 5, 'D', 'e', 'n', 'g', ' ', 3, 'F', 'a', ' ', 4, 'B', 'a', 'i', ' ', 4, 'B', 'a', 'i', ' ', 4, 'Q', 'i', 'e', ' ', 3, 'B', 'i', ' ', 4, 'Z', 'a', 'o', ' ', 4, 'Z', 'a', 'o', ' ', 4, 'M', 'a', 'o', ' ', 3, 'D', 'e', ' ', 3, 'P', 'a', ' ', 4, 'J', 'i', 'e', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 4, 'G', 'u', 'i', ' ', 3, 'C', 'i', ' ', 5, 'L', 'i', 'n', 'g', ' ', 4, 'G', 'a', 'o', ' ', 3, 'M', 'o', ' ', 3, 'J', 'i', ' ', 5, 'J', 'i', 'a', 'o', ' ', 5, 'P', 'e', 'n', 'g', ' ', 4, 'G', 'a', 'o', ' ', 3, 'A', 'i', ' ', 2, 'E', ' ', 4, 'H', 'a', 'o', ' ', 4, 'H', 'a', 'n', ' ', 3, 'B', 'i', ' ', 4, 'W', 'a', 'n', ' ', 5, 'C', 'h', 'o', 'u', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 3, 'X', 'i', ' ', 3, 'A', 'i', ' ', 6, 'J', 'i', 'o', 'n', 'g', ' ', 4, 'H', 'a', 'o', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 4, 'H', 'a', 'o', ' ', 3, 'Z', 'e', ' ', 4, 'C', 'u', 'i', ' ', 4, 'H', 'a', 'o', ' ', 5, 'X', 'i', 'a', 'o', ' ', 3, 'Y', 'e', ' ', 3, 'P', 'o', ' ', 4, 'H', 'a', 'o', ' ', 5, 'J', 'i', 'a', 'o', ' ', 3, 'A', 'i', ' ', 5, 'X', 'i', 'n', 'g', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 3, 'L', 'i', ' ', 5, 'P', 'i', 'a', 'o', ' ', 3, 'H', 'e', ' ', 5, 'J', 'i', 'a', 'o', ' ', 3, 'P', 'i', ' ', 4, 'G', 'a', 'n', ' ', 4, 'P', 'a', 'o', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 4, 'J', 'u', 'n', ' ', 4, 'Q', 'i', 'u', ' ', 4, 'C', 'u', 'n', ' ', 4, 'Q', 'u', 'e', ' ', 4, 'Z', 'h', 'a', ' ', 3, 'G', 'u', ' ', 4, 'J', 'u', 'n', ' ', 4, 'J', 'u', 'n', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 4, 'Z', 'h', 'a', ' ', 3, 'G', 'u', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 3, 'D', 'u', ' ', 4, 'M', 'i', 'n', ' ', 3, 'Q', 'i', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 3, 'Y', 'u', ' ', 4, 'B', 'e', 'i', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 4, 'P', 'e', 'n', ' ', 3, 'H', 'e', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 3, 'H', 'e', ' ', 3, 'Y', 'i', ' ', 3, 'B', 'o', ' ', 4, 'W', 'a', 'n', ' ', 3, 'H', 'e', ' ', 4, 'A', 'n', 'g', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'H', 'e', ' ', 3, 'Y', 'u', ' ', 4, 'K', 'u', 'i', ' ', 4, 'F', 'a', 'n', ' ', 4, 'G', 'a', 'i', ' ', 4, 'D', 'a', 'o', ' ', 4, 'P', 'a', 'n', ' ', 3, 'F', 'u', ' ', 4, 'Q', 'i', 'u', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 4, 'D', 'a', 'o', ' ', 3, 'L', 'u', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 5, 'M', 'e', 'n', 'g', ' ', 3, 'L', 'i', ' ', 4, 'J', 'i', 'n', ' ', 3, 'X', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'P', 'a', 'n', ' ', 5, 'G', 'u', 'a', 'n', ' ', 3, 'A', 'n', ' ', 3, 'L', 'u', ' ', 4, 'S', 'h', 'u', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 5, 'D', 'a', 'n', 'g', ' ', 3, 'A', 'n', ' ', 3, 'G', 'u', ' ', 3, 'L', 'i', ' ', 3, 'M', 'u', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 4, 'G', 'a', 'n', ' ', 3, 'X', 'u', ' ', 5, 'M', 'a', 'n', 'g', ' ', 5, 'M', 'a', 'n', 'g', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'Q', 'i', ' ', 5, 'R', 'u', 'a', 'n', ' ', 5, 'T', 'i', 'a', 'n', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 4, 'D', 'u', 'n', ' ', 4, 'X', 'i', 'n', ' ', 3, 'X', 'i', ' ', 4, 'P', 'a', 'n', ' ', 5, 'F', 'e', 'n', 'g', ' ', 4, 'D', 'u', 'n', ' ', 4, 'M', 'i', 'n', ' ', 5, 'M', 'i', 'n', 'g', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 4, 'S', 'h', 'i', ' ', 4, 'Y', 'u', 'n', ' ', 5, 'M', 'i', 'a', 'n', ' ', 4, 'P', 'a', 'n', ' ', 5, 'F', 'a', 'n', 'g', ' ', 5, 'M', 'i', 'a', 'o', ' ', 4, 'D', 'a', 'n', ' ', 4, 'M', 'e', 'i', ' ', 4, 'M', 'a', 'o', ' ', 4, 'K', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'O', 'u', ' ', 4, 'S', 'h', 'i', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 4, 'Y', 'a', 'o', ' ', 5, 'S', 'h', 'e', 'n', ' ', 4, 'H', 'u', 'o', ' ', 3, 'D', 'a', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 3, 'J', 'u', ' ', 5, 'S', 'h', 'e', 'n', ' ', 4, 'C', 'h', 'i', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 4, 'M', 'e', 'i', ' ', 3, 'M', 'o', ' ', 4, 'Z', 'h', 'u', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 5, 'M', 'i', 'a', 'n', ' ', 3, 'D', 'i', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'D', 'i', 'e', ' ', 3, 'Y', 'i', ' ', 3, 'Z', 'i', ' ', 3, 'Z', 'i', ' ', 5, 'C', 'h', 'a', 'o', ' ', 4, 'Z', 'h', 'a', ' ', 5, 'X', 'u', 'a', 'n', ' ', 5, 'B', 'i', 'n', 'g', ' ', 3, 'M', 'i', ' ', 5, 'L', 'o', 'n', 'g', ' ', 4, 'S', 'u', 'i', ' ', 5, 'D', 'o', 'n', 'g', ' ', 3, 'M', 'i', ' ', 4, 'D', 'i', 'e', ' ', 3, 'Y', 'i', ' ', 3, 'E', 'r', ' ', 5, 'M', 'i', 'n', 'g', ' ', 5, 'X', 'u', 'a', 'n', ' ', 4, 'C', 'h', 'i', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 5, 'J', 'u', 'a', 'n', ' ', 4, 'M', 'o', 'u', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 5, 'T', 'i', 'a', 'o', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'M', 'o', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 4, 'M', 'a', 'i', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 4, 'M', 'e', 'i', ' ', 4, 'J', 'u', 'n', ' ', 5, 'S', 'h', 'a', 'o', ' ', 4, 'H', 'a', 'n', ' ', 5, 'H', 'u', 'a', 'n', ' ', 3, 'D', 'i', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 4, 'C', 'u', 'o', ' ', 5, 'J', 'u', 'a', 'n', ' ', 2, 'E', ' ', 4, 'W', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'X', 'i', ' ', 4, 'K', 'u', 'n', ' ', 4, 'L', 'a', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'S', 'h', 'a', 'n', ' ', 5, 'T', 'i', 'a', 'n', ' ', 4, 'H', 'u', 'n', ' ', 4, 'W', 'a', 'n', ' ', 5, 'L', 'i', 'n', 'g', ' ', 4, 'S', 'h', 'i', ' ', 6, 'Q', 'i', 'o', 'n', 'g', ' ', 4, 'L', 'i', 'e', ' ', 4, 'Y', 'a', 'i', ' ', 5, 'J', 'i', 'n', 'g', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 3, 'L', 'i', ' ', 4, 'L', 'a', 'i', ' ', 4, 'S', 'u', 'i', ' ', 5, 'J', 'u', 'a', 'n', ' ', 5, 'S', 'h', 'u', 'i', ' ', 4, 'S', 'u', 'i', ' ', 3, 'D', 'u', ' ', 3, 'B', 'i', ' ', 3, 'B', 'i', ' ', 3, 'M', 'u', ' ', 4, 'H', 'u', 'n', ' ', 3, 'N', 'i', ' ', 3, 'L', 'u', ' ', 3, 'Y', 'i', ' ', 4, 'J', 'i', 'e', ' ', 4, 'C', 'a', 'i', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 3, 'Y', 'u', ' ', 4, 'H', 'u', 'n', ' ', 3, 'M', 'a', ' ', 4, 'X', 'i', 'a', ' ', 5, 'X', 'i', 'n', 'g', ' ', 3, 'X', 'i', ' ', 4, 'G', 'u', 'n', ' ', 4, 'C', 'a', 'i', ' ', 5, 'C', 'h', 'u', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'M', 'e', 'i', ' ', 3, 'D', 'u', ' ', 4, 'H', 'o', 'u', ' ', 5, 'X', 'u', 'a', 'n', ' ', 3, 'T', 'i', ' ', 4, 'K', 'u', 'i', ' ', 4, 'G', 'a', 'o', ' ', 4, 'R', 'u', 'i', ' ', 4, 'M', 'o', 'u', ' ', 3, 'X', 'u', ' ', 3, 'F', 'a', ' ', 4, 'W', 'e', 'n', ' ', 5, 'M', 'i', 'a', 'o', ' ', 5, 'C', 'h', 'o', 'u', ' ', 4, 'K', 'u', 'i', ' ', 3, 'M', 'i', ' ', 5, 'W', 'e', 'n', 'g', ' ', 4, 'K', 'o', 'u', ' ', 5, 'D', 'a', 'n', 'g', ' ', 5, 'C', 'h', 'e', 'n', ' ', 3, 'K', 'e', ' ', 4, 'S', 'o', 'u', ' ', 4, 'X', 'i', 'a', ' ', 6, 'Q', 'i', 'o', 'n', 'g', ' ', 4, 'M', 'a', 'o', ' ', 5, 'M', 'i', 'n', 'g', ' ', 4, 'M', 'a', 'n', ' ', 5, 'S', 'h', 'u', 'i', ' ', 3, 'Z', 'e', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 5, 'D', 'i', 'a', 'o', ' ', 3, 'O', 'u', ' ', 3, 'M', 'o', ' ', 5, 'S', 'h', 'u', 'n', ' ', 5, 'C', 'o', 'n', 'g', ' ', 4, 'L', 'o', 'u', ' ', 4, 'C', 'h', 'i', ' ', 4, 'M', 'a', 'n', ' ', 5, 'P', 'i', 'a', 'o', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 3, 'J', 'i', ' ', 5, 'M', 'e', 'n', 'g', ' ', 4, 'R', 'u', 'n', ' ', 4, 'P', 'i', 'e', ' ', 3, 'X', 'i', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 3, 'P', 'u', ' ', 4, 'Z', 'h', 'u', ' ', 5, 'D', 'e', 'n', 'g', ' ', 5, 'S', 'h', 'e', 'n', ' ', 5, 'S', 'h', 'u', 'n', ' ', 5, 'L', 'i', 'a', 'o', ' ', 4, 'C', 'h', 'e', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'K', 'a', 'n', ' ', 3, 'Y', 'e', ' ', 3, 'X', 'u', ' ', 5, 'T', 'o', 'n', 'g', ' ', 4, 'M', 'o', 'u', ' ', 4, 'L', 'i', 'n', ' ', 4, 'K', 'u', 'i', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'Y', 'e', ' ', 3, 'A', 'i', ' ', 4, 'H', 'u', 'i', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'G', 'u', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 3, 'Q', 'u', ' ', 4, 'W', 'e', 'i', ' ', 5, 'C', 'h', 'o', 'u', ' ', 4, 'S', 'a', 'o', ' ', 5, 'N', 'i', 'n', 'g', ' ', 4, 'X', 'u', 'n', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'H', 'u', 'o', ' ', 5, 'M', 'e', 'n', 'g', ' ', 5, 'M', 'i', 'a', 'n', ' ', 4, 'B', 'i', 'n', ' ', 5, 'M', 'i', 'a', 'n', ' ', 3, 'L', 'i', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 4, 'J', 'u', 'e', ' ', 5, 'X', 'u', 'a', 'n', ' ', 5, 'M', 'i', 'a', 'n', ' ', 4, 'H', 'u', 'o', ' ', 3, 'L', 'u', ' ', 5, 'M', 'e', 'n', 'g', ' ', 5, 'L', 'o', 'n', 'g', ' ', 5, 'G', 'u', 'a', 'n', ' ', 4, 'M', 'a', 'n', ' ', 3, 'X', 'i', ' ', 4, 'C', 'h', 'u', ' ', 5, 'T', 'a', 'n', 'g', ' ', 4, 'K', 'a', 'n', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'M', 'a', 'o', ' ', 4, 'J', 'i', 'n', ' ', 4, 'L', 'i', 'n', ' ', 3, 'Y', 'u', ' ', 5, 'S', 'h', 'u', 'o', ' ', 3, 'C', 'e', ' ', 4, 'J', 'u', 'e', ' ', 4, 'S', 'h', 'i', ' ', 3, 'Y', 'i', ' ', 5, 'S', 'h', 'e', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'H', 'o', 'u', ' ', 5, 'S', 'h', 'e', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 3, 'J', 'u', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'C', 'u', 'o', ' ', 5, 'D', 'u', 'a', 'n', ' ', 3, 'A', 'i', ' ', 5, 'J', 'i', 'a', 'o', ' ', 5, 'Z', 'e', 'n', 'g', ' ', 4, 'H', 'u', 'o', ' ', 4, 'B', 'a', 'i', ' ', 4, 'S', 'h', 'i', ' ', 5, 'D', 'i', 'n', 'g', ' ', 3, 'Q', 'i', ' ', 3, 'J', 'i', ' ', 3, 'Z', 'i', ' ', 4, 'G', 'a', 'n', ' ', 3, 'W', 'u', ' ', 4, 'T', 'u', 'o', ' ', 3, 'K', 'u', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 3, 'X', 'i', ' ', 4, 'F', 'a', 'n', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 5, 'D', 'a', 'n', 'g', ' ', 3, 'M', 'a', ' ', 4, 'S', 'h', 'a', ' ', 4, 'D', 'a', 'n', ' ', 4, 'J', 'u', 'e', ' ', 3, 'L', 'i', ' ', 3, 'F', 'u', ' ', 4, 'M', 'i', 'n', ' ', 4, 'N', 'u', 'o', ' ', 4, 'H', 'u', 'o', ' ', 5, 'K', 'a', 'n', 'g', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'Q', 'i', ' ', 4, 'K', 'a', 'n', ' ', 4, 'J', 'i', 'e', ' ', 4, 'F', 'e', 'n', ' ', 2, 'E', ' ', 3, 'Y', 'a', ' ', 3, 'P', 'i', ' ', 4, 'Z', 'h', 'e', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'S', 'u', 'i', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 4, 'C', 'h', 'e', ' ', 4, 'D', 'u', 'n', ' ', 4, 'P', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'F', 'e', 'n', 'g', ' ', 3, 'F', 'a', ' ', 3, 'M', 'o', ' ', 4, 'Z', 'h', 'a', ' ', 3, 'Q', 'u', ' ', 3, 'Y', 'u', ' ', 4, 'L', 'u', 'o', ' ', 4, 'T', 'u', 'o', ' ', 4, 'T', 'u', 'o', ' ', 3, 'D', 'i', ' ', 5, 'Z', 'h', 'a', 'i', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 3, 'A', 'i', ' ', 4, 'F', 'e', 'i', ' ', 3, 'M', 'u', ' ', 4, 'Z', 'h', 'u', ' ', 3, 'L', 'i', ' ', 5, 'B', 'i', 'a', 'n', ' ', 3, 'N', 'u', ' ', 5, 'P', 'i', 'n', 'g', ' ', 5, 'P', 'e', 'n', 'g', ' ', 5, 'L', 'i', 'n', 'g', ' ', 4, 'P', 'a', 'o', ' ', 3, 'L', 'e', ' ', 3, 'P', 'o', ' ', 3, 'B', 'o', ' ', 3, 'P', 'o', ' ', 5, 'S', 'h', 'e', 'n', ' ', 3, 'Z', 'a', ' ', 4, 'N', 'u', 'o', ' ', 3, 'L', 'i', ' ', 5, 'L', 'o', 'n', 'g', ' ', 5, 'T', 'o', 'n', 'g', ' ', 3, 'L', 'i', ' ', 8, 'A', 'r', 'a', 'g', 'a', 'n', 'e', ' ', 4, 'C', 'h', 'u', ' ', 5, 'K', 'e', 'n', 'g', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 4, 'Z', 'h', 'u', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 4, 'H', 'u', 'o', ' ', 2, 'E', ' ', 4, 'N', 'a', 'o', ' ', 4, 'J', 'i', 'a', ' ', 3, 'L', 'u', ' ', 4, 'W', 'e', 'i', ' ', 3, 'A', 'i', ' ', 4, 'L', 'u', 'o', ' ', 4, 'K', 'e', 'n', ' ', 5, 'X', 'i', 'n', 'g', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'T', 'o', 'n', 'g', ' ', 5, 'P', 'e', 'n', 'g', ' ', 3, 'X', 'i', ' ', 5, 'H', 'o', 'n', 'g', ' ', 5, 'S', 'h', 'u', 'o', ' ', 4, 'X', 'i', 'a', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 4, 'W', 'e', 'i', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 5, 'K', 'e', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'o', ' ', 4, 'Q', 'u', 'e', ' ', 5, 'C', 'h', 'a', 'n', ' ', 5, 'L', 'a', 'n', 'g', ' ', 5, 'H', 'o', 'n', 'g', ' ', 3, 'Y', 'u', ' ', 5, 'X', 'i', 'a', 'o', ' ', 4, 'X', 'i', 'a', ' ', 5, 'M', 'a', 'n', 'g', ' ', 5, 'L', 'o', 'n', 'g', ' ', 5, 'I', 'o', 'n', 'g', ' ', 4, 'C', 'h', 'e', ' ', 4, 'C', 'h', 'e', ' ', 2, 'E', ' ', 4, 'L', 'i', 'u', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 5, 'M', 'a', 'n', 'g', ' ', 4, 'Q', 'u', 'e', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'S', 'h', 'a', ' ', 4, 'K', 'u', 'n', ' ', 3, 'Y', 'u', ' ', 5, 'K', 'a', 'k', 'i', ' ', 3, 'L', 'u', ' ', 5, 'C', 'h', 'e', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'N', 'u', 'e', ' ', 5, 'S', 'o', 'n', 'g', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 5, 'K', 'e', 'n', 'g', ' ', 5, 'P', 'e', 'n', 'g', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'Z', 'h', 'u', 'i', ' ', 5, 'K', 'o', 'n', 'g', ' ', 5, 'C', 'e', 'n', 'g', ' ', 3, 'Q', 'i', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 4, 'L', 'i', 'n', ' ', 4, 'J', 'u', 'n', ' ', 3, 'B', 'o', ' ', 5, 'D', 'i', 'n', 'g', ' ', 4, 'M', 'i', 'n', ' ', 5, 'D', 'i', 'a', 'o', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'H', 'e', ' ', 3, 'L', 'u', ' ', 3, 'A', 'i', ' ', 4, 'S', 'u', 'i', ' ', 4, 'Q', 'u', 'e', ' ', 5, 'L', 'i', 'n', 'g', ' ', 4, 'B', 'e', 'i', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'D', 'u', 'i', ' ', 3, 'W', 'u', ' ', 3, 'Q', 'i', ' ', 4, 'L', 'u', 'n', ' ', 4, 'W', 'a', 'n', ' ', 5, 'D', 'i', 'a', 'n', ' ', 5, 'G', 'a', 'n', 'g', ' ', 4, 'P', 'e', 'i', ' ', 3, 'Q', 'i', ' ', 5, 'C', 'h', 'e', 'n', ' ', 5, 'R', 'u', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'D', 'i', 'e', ' ', 5, 'D', 'i', 'n', 'g', ' ', 3, 'D', 'u', ' ', 4, 'T', 'u', 'o', ' ', 4, 'J', 'i', 'e', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 5, 'B', 'i', 'a', 'n', ' ', 3, 'K', 'e', ' ', 3, 'B', 'i', ' ', 4, 'W', 'e', 'i', ' ', 5, 'S', 'h', 'u', 'o', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 5, 'D', 'u', 'a', 'n', ' ', 4, 'X', 'i', 'a', ' ', 5, 'D', 'a', 'n', 'g', ' ', 3, 'T', 'i', ' ', 4, 'N', 'a', 'o', ' ', 5, 'P', 'e', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'D', 'i', ' ', 4, 'T', 'a', 'n', ' ', 4, 'C', 'h', 'a', ' ', 5, 'S', 'e', 'k', 'i', ' ', 3, 'Q', 'i', ' ', 5, 'F', 'e', 'n', 'g', ' ', 5, 'X', 'u', 'a', 'n', ' ', 4, 'Q', 'u', 'e', ' ', 4, 'Q', 'u', 'e', ' ', 3, 'M', 'a', ' ', 5, 'G', 'o', 'n', 'g', ' ', 5, 'N', 'i', 'a', 'n', ' ', 3, 'S', 'u', ' ', 2, 'E', ' ', 3, 'C', 'i', ' ', 4, 'L', 'i', 'u', ' ', 3, 'S', 'i', ' ', 5, 'T', 'a', 'n', 'g', ' ', 5, 'B', 'a', 'n', 'g', ' ', 4, 'H', 'u', 'a', ' ', 3, 'P', 'i', ' ', 4, 'W', 'e', 'i', ' ', 5, 'S', 'a', 'n', 'g', ' ', 4, 'L', 'e', 'i', ' ', 4, 'C', 'u', 'o', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 4, 'X', 'i', 'a', ' ', 3, 'Q', 'i', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'P', 'a', 'n', ' ', 4, 'W', 'e', 'i', ' ', 4, 'Y', 'u', 'n', ' ', 4, 'D', 'u', 'i', ' ', 4, 'Z', 'h', 'e', ' ', 3, 'K', 'e', ' ', 3, 'L', 'a', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 4, 'G', 'u', 'n', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 5, 'C', 'h', 'a', 'n', ' ', 3, 'Q', 'i', ' ', 3, 'A', 'o', ' ', 5, 'P', 'e', 'n', 'g', ' ', 3, 'L', 'u', ' ', 3, 'L', 'u', ' ', 4, 'K', 'a', 'n', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 5, 'C', 'h', 'e', 'n', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'L', 'e', 'i', ' ', 5, 'B', 'i', 'a', 'o', ' ', 3, 'Q', 'i', ' ', 3, 'M', 'o', ' ', 3, 'Q', 'i', ' ', 4, 'C', 'u', 'i', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 5, 'C', 'h', 'u', 'o', ' ', 3, 'J', 'i', ' ', 5, 'S', 'h', 'a', 'n', ' ', 4, 'L', 'a', 'o', ' ', 3, 'Q', 'u', ' ', 5, 'Z', 'e', 'n', 'g', ' ', 5, 'D', 'e', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'X', 'i', ' ', 4, 'L', 'i', 'n', ' ', 5, 'D', 'i', 'n', 'g', ' ', 5, 'D', 'i', 'a', 'n', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 4, 'P', 'a', 'n', ' ', 3, 'Z', 'a', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 3, 'D', 'i', ' ', 3, 'L', 'i', ' ', 5, 'T', 'a', 'n', 'i', ' ', 5, 'J', 'i', 'a', 'o', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 4, 'D', 'u', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 5, 'Z', 'h', 'u', 'i', ' ', 3, 'H', 'e', ' ', 4, 'H', 'u', 'o', ' ', 5, 'Z', 'h', 'a', 'i', ' ', 4, 'L', 'e', 'i', ' ', 3, 'K', 'e', ' ', 4, 'C', 'h', 'u', ' ', 3, 'J', 'i', ' ', 4, 'Q', 'u', 'e', ' ', 5, 'D', 'a', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 3, 'P', 'i', ' ', 3, 'P', 'i', ' ', 3, 'Y', 'u', ' ', 4, 'P', 'i', 'n', ' ', 3, 'Q', 'i', ' ', 3, 'A', 'i', ' ', 4, 'K', 'a', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 5, 'R', 'u', 'a', 'n', ' ', 5, 'M', 'e', 'n', 'g', ' ', 4, 'P', 'a', 'o', ' ', 3, 'C', 'i', ' ', 4, 'M', 'i', 'e', ' ', 3, 'C', 'a', ' ', 5, 'X', 'i', 'a', 'n', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 4, 'L', 'e', 'i', ' ', 4, 'L', 'e', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'L', 'i', ' ', 3, 'L', 'i', ' ', 4, 'F', 'a', 'n', ' ', 4, 'Q', 'u', 'e', ' ', 4, 'P', 'a', 'o', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 3, 'L', 'i', ' ', 5, 'L', 'o', 'n', 'g', ' ', 5, 'L', 'o', 'n', 'g', ' ', 3, 'M', 'o', ' ', 3, 'B', 'o', ' ', 7, 'S', 'h', 'u', 'a', 'n', 'g', ' ', 5, 'G', 'u', 'a', 'n', ' ', 4, 'L', 'a', 'n', ' ', 4, 'Z', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'S', 'h', 'i', ' ', 4, 'S', 'h', 'i', ' ', 3, 'L', 'i', ' ', 5, 'R', 'e', 'n', 'g', ' ', 4, 'S', 'h', 'e', ' ', 4, 'Y', 'u', 'e', ' ', 3, 'S', 'i', ' ', 3, 'Q', 'i', ' ', 3, 'T', 'a', ' ', 3, 'M', 'a', ' ', 4, 'X', 'i', 'e', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'Q', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'B', 'e', 'n', 'g', ' ', 4, 'D', 'u', 'i', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 4, 'S', 'h', 'i', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'T', 'i', 'a', 'o', ' ', 3, 'F', 'u', ' ', 3, 'F', 'u', ' ', 3, 'M', 'i', ' ', 3, 'Z', 'u', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'S', 'u', 'a', 'n', ' ', 4, 'M', 'e', 'i', ' ', 4, 'Z', 'u', 'o', ' ', 3, 'Q', 'u', ' ', 3, 'H', 'u', ' ', 4, 'Z', 'h', 'u', ' ', 5, 'S', 'h', 'e', 'n', ' ', 4, 'S', 'u', 'i', ' ', 3, 'C', 'i', ' ', 5, 'C', 'h', 'a', 'i', ' ', 3, 'M', 'i', ' ', 3, 'L', 'u', ' ', 3, 'Y', 'u', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 3, 'W', 'u', ' ', 5, 'T', 'i', 'a', 'o', ' ', 5, 'P', 'i', 'a', 'o', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'G', 'u', 'i', ' ', 4, 'X', 'i', 'a', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'J', 'i', ' ', 4, 'G', 'a', 'o', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 4, 'G', 'a', 'o', ' ', 5, 'S', 'h', 'u', 'i', ' ', 4, 'J', 'i', 'n', ' ', 5, 'C', 'h', 'e', 'n', ' ', 4, 'G', 'a', 'i', ' ', 4, 'K', 'u', 'n', ' ', 3, 'D', 'i', ' ', 4, 'D', 'a', 'o', ' ', 4, 'H', 'u', 'o', ' ', 4, 'T', 'a', 'o', ' ', 3, 'Q', 'i', ' ', 3, 'G', 'u', ' ', 5, 'G', 'u', 'a', 'n', ' ', 4, 'Z', 'u', 'i', ' ', 5, 'L', 'i', 'n', 'g', ' ', 3, 'L', 'u', ' ', 5, 'B', 'i', 'n', 'g', ' ', 4, 'J', 'i', 'n', ' ', 4, 'D', 'a', 'o', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'L', 'u', ' ', 5, 'S', 'h', 'a', 'n', ' ', 4, 'B', 'e', 'i', ' ', 4, 'Z', 'h', 'e', ' ', 4, 'H', 'u', 'i', ' ', 4, 'Y', 'o', 'u', ' ', 3, 'X', 'i', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'Z', 'i', ' ', 4, 'H', 'u', 'o', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 3, 'F', 'u', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 3, 'W', 'u', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 3, 'T', 'i', ' ', 3, 'Y', 'i', ' ', 4, 'M', 'e', 'i', ' ', 3, 'S', 'i', ' ', 3, 'D', 'i', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 3, 'J', 'i', ' ', 4, 'G', 'a', 'o', ' ', 5, 'T', 'a', 'n', 'g', ' ', 3, 'S', 'i', ' ', 3, 'M', 'a', ' ', 3, 'T', 'a', ' ', 5, 'X', 'u', 'a', 'n', ' ', 3, 'Q', 'i', ' ', 3, 'Y', 'u', ' ', 3, 'X', 'i', ' ', 3, 'J', 'i', ' ', 3, 'S', 'i', ' ', 5, 'C', 'h', 'a', 'n', ' ', 4, 'T', 'a', 'n', ' ', 5, 'K', 'u', 'a', 'i', ' ', 4, 'S', 'u', 'i', ' ', 3, 'L', 'i', ' ', 5, 'N', 'o', 'n', 'g', ' ', 3, 'N', 'i', ' ', 4, 'D', 'a', 'o', ' ', 3, 'L', 'i', ' ', 5, 'R', 'a', 'n', 'g', ' ', 4, 'Y', 'u', 'e', ' ', 3, 'T', 'i', ' ', 4, 'Z', 'a', 'n', ' ', 4, 'L', 'e', 'i', ' ', 4, 'R', 'o', 'u', ' ', 3, 'Y', 'u', ' ', 3, 'Y', 'u', ' ', 4, 'C', 'h', 'i', ' ', 4, 'X', 'i', 'e', ' ', 4, 'Q', 'i', 'n', ' ', 3, 'H', 'e', ' ', 3, 'T', 'u', ' ', 4, 'X', 'i', 'u', ' ', 3, 'S', 'i', ' ', 4, 'R', 'e', 'n', ' ', 3, 'T', 'u', ' ', 3, 'Z', 'i', ' ', 4, 'C', 'h', 'a', ' ', 4, 'G', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'B', 'i', 'n', 'g', ' ', 5, 'N', 'i', 'a', 'n', ' ', 4, 'Q', 'i', 'u', ' ', 4, 'Q', 'i', 'u', ' ', 6, 'C', 'h', 'o', 'n', 'g', ' ', 4, 'F', 'e', 'n', ' ', 4, 'H', 'a', 'o', ' ', 4, 'Y', 'u', 'n', ' ', 3, 'K', 'e', ' ', 5, 'M', 'i', 'a', 'o', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'G', 'e', 'n', 'g', ' ', 3, 'B', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'Y', 'u', ' ', 3, 'M', 'i', ' ', 3, 'K', 'u', ' ', 4, 'B', 'a', 'n', ' ', 3, 'P', 'i', ' ', 3, 'N', 'i', ' ', 3, 'L', 'i', ' ', 4, 'Y', 'o', 'u', ' ', 3, 'Z', 'u', ' ', 3, 'P', 'i', ' ', 3, 'B', 'a', ' ', 5, 'L', 'i', 'n', 'g', ' ', 3, 'M', 'o', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 5, 'N', 'i', 'a', 'n', ' ', 4, 'Q', 'i', 'n', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 4, 'Z', 'u', 'o', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'S', 'h', 'u', ' ', 3, 'J', 'u', ' ', 3, 'Z', 'i', ' ', 4, 'H', 'u', 'o', ' ', 3, 'J', 'i', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 5, 'T', 'o', 'n', 'g', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'H', 'u', 'o', ' ', 3, 'H', 'e', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'Z', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'J', 'i', 'e', ' ', 4, 'R', 'e', 'n', ' ', 3, 'D', 'u', ' ', 3, 'Y', 'i', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'H', 'u', 'i', ' ', 5, 'N', 'o', 'n', 'g', ' ', 3, 'F', 'u', ' ', 3, 'X', 'i', ' ', 4, 'K', 'a', 'o', ' ', 5, 'L', 'a', 'n', 'g', ' ', 3, 'F', 'u', ' ', 3, 'Z', 'e', ' ', 5, 'S', 'h', 'u', 'i', ' ', 3, 'L', 'u', ' ', 4, 'K', 'u', 'n', ' ', 4, 'G', 'a', 'n', ' ', 5, 'G', 'e', 'n', 'g', ' ', 3, 'T', 'i', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 3, 'T', 'u', ' ', 5, 'S', 'h', 'a', 'o', ' ', 5, 'S', 'h', 'u', 'i', ' ', 3, 'Y', 'a', ' ', 4, 'L', 'u', 'n', ' ', 3, 'L', 'u', ' ', 3, 'G', 'u', ' ', 4, 'Z', 'u', 'o', ' ', 4, 'R', 'e', 'n', ' ', 5, 'Z', 'h', 'u', 'n', ' ', 5, 'B', 'a', 'n', 'g', ' ', 4, 'B', 'a', 'i', ' ', 3, 'J', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'K', 'u', 'n', ' ', 5, 'L', 'e', 'n', 'g', ' ', 5, 'P', 'e', 'n', 'g', ' ', 3, 'K', 'e', ' ', 5, 'B', 'i', 'n', 'g', ' ', 5, 'C', 'h', 'o', 'u', ' ', 3, 'Z', 'u', ' ', 3, 'Y', 'u', ' ', 3, 'S', 'u', ' ', 4, 'L', 'u', 'e', ' ', 3, 'Y', 'i', ' ', 3, 'X', 'i', ' ', 5, 'B', 'i', 'a', 'n', ' ', 3, 'J', 'i', ' ', 3, 'F', 'u', ' ', 3, 'B', 'i', ' ', 4, 'N', 'u', 'o', ' ', 4, 'J', 'i', 'e', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 3, 'X', 'u', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 4, 'D', 'a', 'o', ' ', 4, 'W', 'e', 'n', ' ', 5, 'L', 'i', 'a', 'n', ' ', 3, 'Z', 'i', ' ', 3, 'Y', 'u', ' ', 3, 'J', 'i', ' ', 3, 'X', 'u', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'D', 'a', 'o', ' ', 4, 'J', 'i', 'a', ' ', 3, 'J', 'i', ' ', 4, 'G', 'a', 'o', ' ', 4, 'G', 'a', 'o', ' ', 3, 'G', 'u', ' ', 5, 'R', 'o', 'n', 'g', ' ', 4, 'S', 'u', 'i', ' ', 4, 'Y', 'o', 'u', ' ', 3, 'J', 'i', ' ', 5, 'K', 'a', 'n', 'g', ' ', 3, 'M', 'u', ' ', 5, 'S', 'h', 'a', 'n', ' ', 4, 'M', 'e', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'J', 'i', ' ', 3, 'L', 'u', ' ', 3, 'S', 'u', ' ', 3, 'J', 'i', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'W', 'e', 'n', ' ', 4, 'Q', 'i', 'u', ' ', 3, 'S', 'e', ' ', 3, 'Y', 'i', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 4, 'Q', 'i', 'e', ' ', 3, 'J', 'i', ' ', 4, 'S', 'u', 'i', ' ', 5, 'X', 'i', 'a', 'o', ' ', 3, 'P', 'u', ' ', 5, 'J', 'i', 'a', 'o', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 5, 'T', 'o', 'n', 'g', ' ', 4, 'S', 'a', 'i', ' ', 3, 'L', 'u', ' ', 4, 'S', 'u', 'i', ' ', 5, 'N', 'o', 'n', 'g', ' ', 3, 'S', 'e', ' ', 4, 'H', 'u', 'i', ' ', 5, 'R', 'a', 'n', 'g', ' ', 4, 'N', 'u', 'o', ' ', 3, 'Y', 'u', ' ', 4, 'B', 'i', 'n', ' ', 3, 'J', 'i', ' ', 4, 'T', 'u', 'i', ' ', 4, 'W', 'e', 'n', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 4, 'H', 'u', 'o', ' ', 5, 'G', 'o', 'n', 'g', ' ', 3, 'L', 'u', ' ', 5, 'B', 'i', 'a', 'o', ' ', 5, 'R', 'a', 'n', 'g', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 3, 'L', 'i', ' ', 4, 'Z', 'a', 'n', ' ', 4, 'X', 'u', 'e', ' ', 3, 'W', 'a', ' ', 4, 'J', 'i', 'u', ' ', 6, 'Q', 'i', 'o', 'n', 'g', ' ', 3, 'X', 'i', ' ', 6, 'Q', 'i', 'o', 'n', 'g', ' ', 5, 'K', 'o', 'n', 'g', ' ', 3, 'Y', 'u', ' ', 4, 'S', 'e', 'n', ' ', 5, 'J', 'i', 'n', 'g', ' ', 4, 'Y', 'a', 'o', ' ', 6, 'C', 'h', 'u', 'a', 'n', ' ', 5, 'Z', 'h', 'u', 'n', ' ', 3, 'T', 'u', ' ', 4, 'L', 'a', 'o', ' ', 4, 'Q', 'i', 'e', ' ', 5, 'Z', 'h', 'a', 'i', ' ', 4, 'Y', 'a', 'o', ' ', 5, 'B', 'i', 'a', 'n', ' ', 4, 'B', 'a', 'o', ' ', 4, 'Y', 'a', 'o', ' ', 5, 'B', 'i', 'n', 'g', ' ', 3, 'W', 'a', ' ', 4, 'Z', 'h', 'u', ' ', 5, 'J', 'i', 'a', 'o', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 5, 'D', 'i', 'a', 'o', ' ', 3, 'W', 'u', ' ', 4, 'G', 'u', 'i', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'Z', 'h', 'i', ' ', 7, 'C', 'h', 'u', 'a', 'n', 'g', ' ', 4, 'Y', 'a', 'o', ' ', 5, 'T', 'i', 'a', 'o', ' ', 5, 'J', 'i', 'a', 'o', ' ', 7, 'C', 'h', 'u', 'a', 'n', 'g', ' ', 6, 'J', 'i', 'o', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'o', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 4, 'K', 'o', 'u', ' ', 5, 'C', 'u', 'a', 'n', ' ', 3, 'W', 'o', ' ', 4, 'D', 'a', 'n', ' ', 3, 'K', 'u', ' ', 3, 'K', 'e', ' ', 5, 'Z', 'h', 'u', 'i', ' ', 3, 'X', 'u', ' ', 3, 'S', 'u', ' ', 5, 'G', 'u', 'a', 'n', ' ', 4, 'K', 'u', 'i', ' ', 4, 'D', 'o', 'u', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'W', 'o', ' ', 3, 'W', 'a', ' ', 3, 'Y', 'a', ' ', 3, 'Y', 'u', ' ', 3, 'J', 'u', ' ', 6, 'Q', 'i', 'o', 'n', 'g', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'Y', 'a', 'o', ' ', 5, 'T', 'i', 'a', 'o', ' ', 5, 'C', 'h', 'a', 'o', ' ', 3, 'Y', 'u', ' ', 5, 'T', 'i', 'a', 'n', ' ', 5, 'D', 'i', 'a', 'o', ' ', 3, 'J', 'u', ' ', 5, 'L', 'i', 'a', 'o', ' ', 3, 'X', 'i', ' ', 3, 'W', 'u', ' ', 4, 'K', 'u', 'i', ' ', 7, 'C', 'h', 'u', 'a', 'n', 'g', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 5, 'K', 'u', 'a', 'n', ' ', 5, 'L', 'o', 'n', 'g', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 4, 'C', 'u', 'i', ' ', 5, 'P', 'i', 'a', 'o', ' ', 4, 'Z', 'a', 'o', ' ', 5, 'C', 'u', 'a', 'n', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 6, 'Q', 'i', 'o', 'n', 'g', ' ', 4, 'D', 'o', 'u', ' ', 4, 'Z', 'a', 'o', ' ', 5, 'L', 'o', 'n', 'g', ' ', 4, 'Q', 'i', 'e', ' ', 3, 'L', 'i', ' ', 4, 'C', 'h', 'u', ' ', 4, 'S', 'h', 'i', ' ', 4, 'F', 'o', 'u', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'C', 'h', 'u', ' ', 5, 'H', 'o', 'n', 'g', ' ', 3, 'Q', 'i', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'G', 'o', 'n', 'g', ' ', 4, 'S', 'h', 'i', ' ', 4, 'S', 'h', 'u', ' ', 5, 'M', 'i', 'a', 'o', ' ', 3, 'J', 'u', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 4, 'Z', 'h', 'u', ' ', 5, 'L', 'i', 'n', 'g', ' ', 5, 'L', 'o', 'n', 'g', ' ', 5, 'B', 'i', 'n', 'g', ' ', 5, 'J', 'i', 'n', 'g', ' ', 5, 'J', 'i', 'n', 'g', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 3, 'S', 'i', ' ', 4, 'J', 'u', 'n', ' ', 5, 'H', 'o', 'n', 'g', ' ', 5, 'T', 'o', 'n', 'g', ' ', 5, 'S', 'o', 'n', 'g', ' ', 5, 'J', 'i', 'n', 'g', ' ', 5, 'D', 'i', 'a', 'o', ' ', 3, 'Y', 'i', ' ', 4, 'S', 'h', 'u', ' ', 5, 'J', 'i', 'n', 'g', ' ', 3, 'Q', 'u', ' ', 4, 'J', 'i', 'e', ' ', 5, 'P', 'i', 'n', 'g', ' ', 5, 'D', 'u', 'a', 'n', ' ', 5, 'S', 'h', 'a', 'o', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 5, 'C', 'e', 'n', 'g', ' ', 5, 'D', 'e', 'n', 'g', ' ', 4, 'C', 'u', 'i', ' ', 5, 'H', 'u', 'a', 'i', ' ', 5, 'J', 'i', 'n', 'g', ' ', 4, 'K', 'a', 'n', ' ', 5, 'J', 'i', 'n', 'g', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'Z', 'h', 'u', ' ', 3, 'L', 'e', ' ', 5, 'P', 'e', 'n', 'g', ' ', 3, 'Y', 'u', ' ', 4, 'C', 'h', 'i', ' ', 4, 'G', 'a', 'n', ' ', 5, 'M', 'a', 'n', 'g', ' ', 4, 'Z', 'h', 'u', ' ', 7, 'U', 't', 's', 'u', 'b', 'o', ' ', 3, 'D', 'u', ' ', 3, 'J', 'i', ' ', 5, 'X', 'i', 'a', 'o', ' ', 3, 'B', 'a', ' ', 5, 'S', 'u', 'a', 'n', ' ', 3, 'J', 'i', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 4, 'S', 'u', 'n', ' ', 3, 'Y', 'a', ' ', 5, 'Z', 'h', 'u', 'i', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 3, 'H', 'u', ' ', 5, 'G', 'a', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'o', ' ', 4, 'C', 'e', 'n', ' ', 3, 'P', 'i', ' ', 3, 'B', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 5, 'D', 'o', 'n', 'g', ' ', 5, 'S', 'h', 'a', 'n', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 4, 'X', 'i', 'a', ' ', 3, 'D', 'i', ' ', 4, 'Z', 'h', 'u', ' ', 3, 'N', 'a', ' ', 4, 'C', 'h', 'i', ' ', 3, 'G', 'u', ' ', 3, 'L', 'i', ' ', 4, 'Q', 'i', 'e', ' ', 4, 'M', 'i', 'n', ' ', 4, 'B', 'a', 'o', ' ', 5, 'T', 'i', 'a', 'o', ' ', 3, 'S', 'i', ' ', 3, 'F', 'u', ' ', 3, 'C', 'e', ' ', 4, 'B', 'e', 'n', ' ', 4, 'P', 'e', 'i', ' ', 3, 'D', 'a', ' ', 3, 'Z', 'i', ' ', 3, 'D', 'i', ' ', 5, 'L', 'i', 'n', 'g', ' ', 3, 'Z', 'e', ' ', 3, 'N', 'u', ' ', 3, 'F', 'u', ' ', 4, 'G', 'o', 'u', ' ', 4, 'F', 'a', 'n', ' ', 4, 'J', 'i', 'a', ' ', 3, 'G', 'e', ' ', 4, 'F', 'a', 'n', ' ', 4, 'S', 'h', 'i', ' ', 4, 'M', 'a', 'o', ' ', 3, 'P', 'o', ' ', 4, 'S', 'e', 'y', ' ', 5, 'J', 'i', 'a', 'n', ' ', 6, 'Q', 'i', 'o', 'n', 'g', ' ', 5, 'L', 'o', 'n', 'g', ' ', 6, 'S', 'o', 'u', 'k', 'e', ' ', 5, 'B', 'i', 'a', 'n', ' ', 4, 'L', 'u', 'o', ' ', 4, 'G', 'u', 'i', ' ', 3, 'Q', 'u', ' ', 4, 'C', 'h', 'i', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'Y', 'a', 'o', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'B', 'i', ' ', 6, 'Q', 'i', 'o', 'n', 'g', ' ', 4, 'G', 'u', 'a', ' ', 5, 'D', 'e', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'J', 'i', 'n', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 4, 'S', 'u', 'n', ' ', 3, 'R', 'u', ' ', 3, 'F', 'a', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 4, 'Z', 'h', 'u', ' ', 5, 'T', 'o', 'n', 'g', ' ', 3, 'J', 'i', ' ', 3, 'D', 'a', ' ', 5, 'X', 'i', 'n', 'g', ' ', 3, 'C', 'e', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 4, 'K', 'o', 'u', ' ', 4, 'L', 'a', 'i', ' ', 3, 'B', 'i', ' ', 5, 'S', 'h', 'a', 'i', ' ', 5, 'D', 'a', 'n', 'g', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 3, 'C', 'e', ' ', 3, 'F', 'u', ' ', 4, 'Y', 'u', 'n', ' ', 3, 'T', 'u', ' ', 3, 'P', 'a', ' ', 3, 'L', 'i', ' ', 5, 'L', 'a', 'n', 'g', ' ', 3, 'J', 'u', ' ', 5, 'G', 'u', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'H', 'a', 'n', ' ', 5, 'T', 'o', 'n', 'g', ' ', 4, 'X', 'i', 'a', ' ', 4, 'Z', 'h', 'i', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 5, 'S', 'u', 'a', 'n', ' ', 4, 'S', 'h', 'i', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'Z', 'u', 'o', ' ', 5, 'X', 'i', 'a', 'o', ' ', 5, 'S', 'h', 'a', 'o', ' ', 5, 'T', 'i', 'n', 'g', ' ', 3, 'C', 'e', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'G', 'a', 'o', ' ', 5, 'K', 'u', 'a', 'i', ' ', 4, 'G', 'a', 'n', ' ', 5, 'C', 'h', 'o', 'u', ' ', 5, 'K', 'a', 'g', 'o', ' ', 5, 'G', 'a', 'n', 'g', ' ', 4, 'Y', 'u', 'n', ' ', 2, 'O', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'o', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'P', 'u', ' ', 4, 'L', 'a', 'i', ' ', 4, 'Z', 'o', 'u', ' ', 3, 'B', 'i', ' ', 3, 'B', 'i', ' ', 3, 'B', 'i', ' ', 3, 'G', 'e', ' ', 4, 'C', 'h', 'i', ' ', 5, 'G', 'u', 'a', 'i', ' ', 3, 'Y', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 3, 'G', 'u', ' ', 4, 'C', 'h', 'i', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 5, 'J', 'i', 'n', 'g', ' ', 4, 'S', 'h', 'a', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 3, 'L', 'u', ' ', 3, 'B', 'o', ' ', 3, 'J', 'i', ' ', 4, 'L', 'i', 'n', ' ', 5, 'S', 'u', 'a', 'n', ' ', 4, 'J', 'u', 'n', ' ', 3, 'F', 'u', ' ', 4, 'Z', 'h', 'a', ' ', 3, 'G', 'u', ' ', 5, 'K', 'o', 'n', 'g', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 4, 'J', 'u', 'n', ' ', 5, 'C', 'h', 'u', 'i', ' ', 5, 'G', 'u', 'a', 'n', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 3, 'C', 'e', ' ', 3, 'J', 'u', ' ', 3, 'B', 'o', ' ', 3, 'Z', 'e', ' ', 4, 'Q', 'i', 'e', ' ', 4, 'T', 'u', 'o', ' ', 4, 'L', 'u', 'o', ' ', 4, 'D', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'o', ' ', 4, 'R', 'u', 'o', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'X', 'u', 'a', 'n', ' ', 5, 'B', 'i', 'a', 'n', ' ', 4, 'S', 'u', 'n', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'P', 'i', 'n', 'g', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 3, 'H', 'u', ' ', 4, 'S', 'h', 'i', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'Y', 'u', 'e', ' ', 5, 'C', 'h', 'u', 'n', ' ', 3, 'L', 'u', ' ', 3, 'W', 'u', ' ', 5, 'D', 'o', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'o', ' ', 3, 'J', 'i', ' ', 4, 'J', 'i', 'e', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 5, 'X', 'i', 'n', 'g', ' ', 4, 'M', 'e', 'i', ' ', 4, 'F', 'a', 'n', ' ', 5, 'C', 'h', 'u', 'i', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 5, 'P', 'i', 'a', 'n', ' ', 5, 'F', 'e', 'n', 'g', ' ', 4, 'Z', 'h', 'u', ' ', 5, 'H', 'o', 'n', 'g', ' ', 4, 'Q', 'i', 'e', ' ', 4, 'H', 'o', 'u', ' ', 4, 'Q', 'i', 'u', ' ', 5, 'M', 'i', 'a', 'o', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'K', 'u', 'i', ' ', 4, 'S', 'i', 'k', ' ', 4, 'L', 'o', 'u', ' ', 4, 'Y', 'u', 'n', ' ', 3, 'H', 'e', ' ', 5, 'T', 'a', 'n', 'g', ' ', 4, 'Y', 'u', 'e', ' ', 5, 'C', 'h', 'o', 'u', ' ', 4, 'G', 'a', 'o', ' ', 4, 'F', 'e', 'i', ' ', 4, 'R', 'u', 'o', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 4, 'G', 'o', 'u', ' ', 4, 'N', 'i', 'e', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'o', ' ', 5, 'C', 'u', 'a', 'n', ' ', 5, 'G', 'o', 'n', 'g', ' ', 5, 'P', 'a', 'n', 'g', ' ', 3, 'D', 'u', ' ', 3, 'L', 'i', ' ', 3, 'B', 'i', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 4, 'C', 'h', 'u', ' ', 5, 'S', 'h', 'a', 'i', ' ', 4, 'C', 'h', 'i', ' ', 4, 'Z', 'h', 'u', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 5, 'L', 'o', 'n', 'g', ' ', 4, 'L', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'B', 'u', ' ', 3, 'L', 'i', ' ', 4, 'H', 'u', 'i', ' ', 3, 'B', 'i', ' ', 3, 'D', 'i', ' ', 5, 'C', 'o', 'n', 'g', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'P', 'e', 'n', 'g', ' ', 4, 'S', 'e', 'n', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 4, 'P', 'a', 'i', ' ', 5, 'P', 'i', 'a', 'o', ' ', 4, 'D', 'o', 'u', ' ', 3, 'Y', 'u', ' ', 4, 'M', 'i', 'e', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 3, 'Z', 'e', ' ', 3, 'X', 'i', ' ', 4, 'G', 'u', 'o', ' ', 3, 'Y', 'i', ' ', 3, 'H', 'u', ' ', 5, 'C', 'h', 'a', 'n', ' ', 4, 'K', 'o', 'u', ' ', 3, 'C', 'u', ' ', 5, 'P', 'i', 'n', 'g', ' ', 5, 'C', 'h', 'o', 'u', ' ', 3, 'J', 'i', ' ', 4, 'G', 'u', 'i', ' ', 3, 'S', 'u', ' ', 4, 'L', 'o', 'u', ' ', 4, 'Z', 'h', 'a', ' ', 3, 'L', 'u', ' ', 5, 'N', 'i', 'a', 'n', ' ', 4, 'S', 'u', 'o', ' ', 5, 'C', 'u', 'a', 'n', ' ', 7, 'S', 'a', 's', 'a', 'r', 'a', ' ', 4, 'S', 'u', 'o', ' ', 3, 'L', 'e', ' ', 5, 'D', 'u', 'a', 'n', ' ', 5, 'Y', 'a', 'n', 'a', ' ', 5, 'X', 'i', 'a', 'o', ' ', 3, 'B', 'o', ' ', 3, 'M', 'i', ' ', 3, 'S', 'i', ' ', 5, 'D', 'a', 'n', 'g', ' ', 5, 'L', 'i', 'a', 'o', ' ', 4, 'D', 'a', 'n', ' ', 5, 'D', 'i', 'a', 'n', ' ', 3, 'F', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'M', 'i', 'n', ' ', 4, 'K', 'u', 'i', ' ', 4, 'D', 'a', 'i', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 5, 'D', 'e', 'n', 'g', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 4, 'S', 'u', 'n', ' ', 4, 'L', 'a', 'o', ' ', 4, 'Z', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'o', ' ', 3, 'D', 'u', ' ', 4, 'S', 'h', 'i', ' ', 4, 'Z', 'a', 'n', ' ', 4, 'P', 'a', 'i', ' ', 5, 'H', 'a', 't', 'a', ' ', 4, 'P', 'a', 'i', ' ', 4, 'G', 'a', 'n', ' ', 3, 'J', 'u', ' ', 3, 'D', 'u', ' ', 3, 'L', 'u', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'B', 'o', ' ', 5, 'D', 'a', 'n', 'g', ' ', 4, 'S', 'a', 'i', ' ', 3, 'K', 'e', ' ', 5, 'L', 'o', 'n', 'g', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'L', 'i', 'a', 'n', ' ', 3, 'B', 'o', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 4, 'L', 'a', 'i', ' ', 4, 'L', 'a', 'n', ' ', 4, 'K', 'u', 'i', ' ', 3, 'Y', 'u', ' ', 4, 'Y', 'u', 'e', ' ', 4, 'H', 'a', 'o', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 4, 'T', 'a', 'i', ' ', 3, 'T', 'i', ' ', 3, 'M', 'i', ' ', 5, 'C', 'h', 'o', 'u', ' ', 3, 'J', 'i', ' ', 5, 'H', 'a', 't', 'a', ' ', 5, 'T', 'e', 'n', 'g', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 4, 'F', 'a', 'n', ' ', 4, 'S', 'o', 'u', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 5, 'K', 'u', 'j', 'i', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 5, 'T', 'e', 'n', 'g', ' ', 3, 'L', 'u', ' ', 3, 'L', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'T', 'u', 'o', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 3, 'Y', 'u', ' ', 4, 'L', 'a', 'i', ' ', 5, 'L', 'o', 'n', 'g', ' ', 8, 'S', 'h', 'i', 'n', 's', 'h', 'i', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'L', 'a', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'Y', 'u', 'e', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 3, 'Q', 'u', ' ', 5, 'L', 'i', 'a', 'n', ' ', 5, 'B', 'i', 'a', 'n', ' ', 5, 'D', 'u', 'a', 'n', ' ', 5, 'Z', 'u', 'a', 'n', ' ', 3, 'L', 'i', ' ', 3, 'S', 'i', ' ', 4, 'L', 'u', 'o', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'Y', 'u', 'e', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 3, 'X', 'u', ' ', 3, 'M', 'i', ' ', 3, 'D', 'i', ' ', 4, 'F', 'a', 'n', ' ', 5, 'S', 'h', 'e', 'n', ' ', 4, 'Z', 'h', 'e', ' ', 5, 'S', 'h', 'e', 'n', ' ', 3, 'N', 'u', ' ', 4, 'X', 'i', 'e', ' ', 4, 'L', 'e', 'i', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'Z', 'i', ' ', 3, 'N', 'i', ' ', 4, 'C', 'u', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'K', 'u', 'm', 'e', ' ', 3, 'B', 'i', ' ', 4, 'B', 'a', 'n', ' ', 3, 'W', 'u', ' ', 4, 'S', 'h', 'a', ' ', 5, 'K', 'a', 'n', 'g', ' ', 4, 'R', 'o', 'u', ' ', 4, 'F', 'e', 'n', ' ', 3, 'B', 'i', ' ', 4, 'C', 'u', 'i', ' ', 3, 'L', 'i', ' ', 4, 'C', 'h', 'i', ' ', 9, 'N', 'u', 'k', 'a', 'm', 'i', 's', 'o', ' ', 3, 'R', 'o', ' ', 3, 'B', 'a', ' ', 3, 'L', 'i', ' ', 4, 'G', 'a', 'n', ' ', 3, 'J', 'u', ' ', 3, 'P', 'o', ' ', 3, 'M', 'o', ' ', 3, 'C', 'u', ' ', 5, 'N', 'i', 'a', 'n', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 3, 'L', 'i', ' ', 3, 'S', 'u', ' ', 5, 'T', 'i', 'a', 'o', ' ', 3, 'L', 'i', ' ', 3, 'Q', 'i', ' ', 3, 'S', 'u', ' ', 5, 'H', 'o', 'n', 'g', ' ', 5, 'T', 'o', 'n', 'g', ' ', 3, 'Z', 'i', ' ', 3, 'C', 'e', ' ', 4, 'Y', 'u', 'e', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 4, 'L', 'i', 'n', ' ', 7, 'Z', 'h', 'u', 'a', 'n', 'g', ' ', 4, 'B', 'a', 'i', ' ', 4, 'F', 'e', 'n', ' ', 3, 'J', 'i', ' ', 7, 'S', 'u', 'k', 'u', 'm', 'o', ' ', 6, 'L', 'i', 'a', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'F', 'u', ' ', 6, 'L', 'i', 'a', 'n', 'g', ' ', 4, 'C', 'a', 'n', ' ', 5, 'G', 'e', 'n', 'g', ' ', 3, 'L', 'i', ' ', 4, 'Y', 'u', 'e', ' ', 3, 'L', 'u', ' ', 3, 'J', 'u', ' ', 3, 'Q', 'i', ' ', 4, 'C', 'u', 'i', ' ', 4, 'B', 'a', 'i', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 4, 'L', 'i', 'n', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 5, 'J', 'i', 'n', 'g', ' ', 4, 'G', 'u', 'o', ' ', 6, 'K', 'o', 'u', 'j', 'i', ' ', 4, 'S', 'a', 'n', ' ', 4, 'S', 'a', 'n', ' ', 5, 'T', 'a', 'n', 'g', ' ', 5, 'B', 'i', 'a', 'n', ' ', 4, 'R', 'o', 'u', ' ', 5, 'M', 'i', 'a', 'n', ' ', 4, 'H', 'o', 'u', ' ', 3, 'X', 'u', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 3, 'H', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'Z', 'a', 'n', ' ', 3, 'C', 'i', ' ', 3, 'L', 'i', ' ', 4, 'X', 'i', 'e', ' ', 3, 'F', 'u', ' ', 3, 'N', 'i', ' ', 4, 'B', 'e', 'i', ' ', 3, 'G', 'u', ' ', 4, 'X', 'i', 'u', ' ', 4, 'G', 'a', 'o', ' ', 5, 'T', 'a', 'n', 'g', ' ', 4, 'Q', 'i', 'u', ' ', 7, 'S', 'u', 'k', 'u', 'm', 'o', ' ', 4, 'C', 'a', 'o', ' ', 7, 'Z', 'h', 'u', 'a', 'n', 'g', ' ', 5, 'T', 'a', 'n', 'g', ' ', 3, 'M', 'i', ' ', 4, 'S', 'a', 'n', ' ', 4, 'F', 'e', 'n', ' ', 4, 'Z', 'a', 'o', ' ', 5, 'K', 'a', 'n', 'g', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 3, 'M', 'o', ' ', 4, 'S', 'a', 'n', ' ', 4, 'S', 'a', 'n', ' ', 4, 'N', 'u', 'o', ' ', 3, 'X', 'i', ' ', 6, 'L', 'i', 'a', 'n', 'g', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 5, 'K', 'u', 'a', 'i', ' ', 3, 'B', 'o', ' ', 5, 'H', 'u', 'a', 'n', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'N', 'u', 'o', ' ', 5, 'T', 'u', 'a', 'n', ' ', 4, 'N', 'i', 'e', ' ', 3, 'L', 'i', ' ', 4, 'Z', 'u', 'o', ' ', 3, 'D', 'i', ' ', 4, 'N', 'i', 'e', ' ', 5, 'T', 'i', 'a', 'o', ' ', 4, 'L', 'a', 'n', ' ', 3, 'M', 'i', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'J', 'i', 'u', ' ', 3, 'X', 'i', ' ', 5, 'G', 'o', 'n', 'g', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 4, 'J', 'i', 'u', ' ', 4, 'Y', 'o', 'u', ' ', 3, 'J', 'i', ' ', 4, 'C', 'h', 'a', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 4, 'X', 'u', 'n', ' ', 4, 'Y', 'u', 'e', ' ', 5, 'H', 'o', 'n', 'g', ' ', 3, 'Y', 'u', ' ', 3, 'H', 'e', ' ', 4, 'W', 'a', 'n', ' ', 4, 'R', 'e', 'n', ' ', 4, 'W', 'e', 'n', ' ', 4, 'W', 'e', 'n', ' ', 4, 'Q', 'i', 'u', ' ', 3, 'N', 'a', ' ', 3, 'Z', 'i', ' ', 4, 'T', 'o', 'u', ' ', 4, 'N', 'i', 'u', ' ', 4, 'F', 'o', 'u', ' ', 4, 'J', 'i', 'e', ' ', 4, 'S', 'h', 'u', ' ', 5, 'C', 'h', 'u', 'n', ' ', 3, 'P', 'i', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'S', 'h', 'a', ' ', 5, 'H', 'o', 'n', 'g', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'J', 'i', ' ', 4, 'F', 'e', 'n', ' ', 4, 'Y', 'u', 'n', ' ', 4, 'R', 'e', 'n', ' ', 4, 'D', 'a', 'n', ' ', 4, 'J', 'i', 'n', ' ', 3, 'S', 'u', ' ', 5, 'F', 'a', 'n', 'g', ' ', 4, 'S', 'u', 'o', ' ', 4, 'C', 'u', 'i', ' ', 4, 'J', 'i', 'u', ' ', 4, 'Z', 'h', 'a', ' ', 5, 'K', 'i', 'n', 'u', ' ', 4, 'J', 'i', 'n', ' ', 3, 'F', 'u', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'C', 'i', ' ', 3, 'Z', 'i', ' ', 5, 'C', 'h', 'o', 'u', ' ', 5, 'H', 'o', 'n', 'g', ' ', 4, 'Z', 'h', 'a', ' ', 4, 'L', 'e', 'i', ' ', 3, 'X', 'i', ' ', 3, 'F', 'u', ' ', 4, 'X', 'i', 'e', ' ', 5, 'S', 'h', 'e', 'n', ' ', 4, 'B', 'e', 'i', ' ', 4, 'Z', 'h', 'u', ' ', 3, 'Q', 'u', ' ', 5, 'L', 'i', 'n', 'g', ' ', 4, 'Z', 'h', 'u', ' ', 5, 'S', 'h', 'a', 'o', ' ', 4, 'G', 'a', 'n', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 3, 'F', 'u', ' ', 4, 'T', 'u', 'o', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 4, 'D', 'a', 'i', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 4, 'S', 'h', 'i', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'Z', 'u', ' ', 6, 'J', 'i', 'o', 'n', 'g', ' ', 4, 'B', 'a', 'n', ' ', 3, 'J', 'u', ' ', 3, 'M', 'o', ' ', 4, 'S', 'h', 'u', ' ', 4, 'Z', 'u', 'i', ' ', 5, 'W', 'a', 't', 'a', ' ', 5, 'J', 'i', 'n', 'g', ' ', 4, 'R', 'e', 'n', ' ', 5, 'H', 'e', 'n', 'g', ' ', 4, 'X', 'i', 'e', ' ', 4, 'J', 'i', 'e', ' ', 4, 'Z', 'h', 'u', ' ', 5, 'C', 'h', 'o', 'u', ' ', 4, 'G', 'u', 'a', ' ', 4, 'B', 'a', 'i', ' ', 4, 'J', 'u', 'e', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 3, 'H', 'u', ' ', 3, 'C', 'i', ' ', 5, 'G', 'e', 'n', 'g', ' ', 5, 'G', 'e', 'n', 'g', ' ', 4, 'T', 'a', 'o', ' ', 4, 'X', 'i', 'e', ' ', 3, 'K', 'u', ' ', 5, 'J', 'i', 'a', 'o', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 4, 'G', 'a', 'i', ' ', 4, 'L', 'u', 'o', ' ', 5, 'X', 'u', 'a', 'n', ' ', 5, 'B', 'i', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'F', 'u', ' ', 4, 'G', 'e', 'i', ' ', 5, 'T', 'o', 'n', 'g', ' ', 5, 'R', 'o', 'n', 'g', ' ', 5, 'T', 'i', 'a', 'o', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'L', 'e', 'i', ' ', 4, 'X', 'i', 'e', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 3, 'X', 'u', ' ', 4, 'L', 'u', 'n', ' ', 4, 'D', 'i', 'e', ' ', 5, 'T', 'o', 'n', 'g', ' ', 3, 'S', 'i', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 4, 'H', 'u', 'i', ' ', 4, 'J', 'u', 'e', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'J', 'u', 'a', 'n', ' ', 4, 'C', 'h', 'i', ' ', 5, 'M', 'i', 'a', 'n', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 3, 'L', 'u', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 4, 'Q', 'i', 'u', ' ', 4, 'S', 'h', 'u', ' ', 5, 'B', 'a', 'n', 'g', ' ', 5, 'T', 'o', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'o', ' ', 4, 'W', 'a', 'n', ' ', 4, 'Q', 'i', 'n', ' ', 5, 'G', 'e', 'n', 'g', ' ', 4, 'X', 'i', 'u', ' ', 3, 'T', 'i', ' ', 4, 'X', 'i', 'u', ' ', 4, 'X', 'i', 'e', ' ', 5, 'H', 'o', 'n', 'g', ' ', 3, 'X', 'i', ' ', 3, 'F', 'u', ' ', 5, 'T', 'i', 'n', 'g', ' ', 4, 'S', 'u', 'i', ' ', 4, 'D', 'u', 'i', ' ', 4, 'K', 'u', 'n', ' ', 3, 'F', 'u', ' ', 5, 'J', 'i', 'n', 'g', ' ', 3, 'H', 'u', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'Y', 'a', 'n', ' ', 6, 'J', 'i', 'o', 'n', 'g', ' ', 5, 'F', 'e', 'n', 'g', ' ', 3, 'J', 'i', ' ', 4, 'S', 'o', 'k', ' ', 5, 'K', 'a', 's', 'e', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 4, 'L', 'i', 'n', ' ', 4, 'D', 'u', 'o', ' ', 3, 'L', 'i', ' ', 3, 'L', 'u', ' ', 6, 'L', 'i', 'a', 'n', 'g', ' ', 5, 'C', 'h', 'o', 'u', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 5, 'S', 'h', 'a', 'o', ' ', 3, 'Q', 'i', ' ', 3, 'Q', 'i', ' ', 5, 'Z', 'h', 'u', 'n', ' ', 3, 'Q', 'i', ' ', 4, 'W', 'a', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'S', 'h', 'o', 'u', ' ', 4, 'W', 'e', 'i', ' ', 3, 'Q', 'i', ' ', 4, 'T', 'a', 'o', ' ', 4, 'W', 'a', 'n', ' ', 5, 'G', 'a', 'n', 'g', ' ', 5, 'W', 'a', 'n', 'g', ' ', 5, 'B', 'e', 'n', 'g', ' ', 5, 'Z', 'h', 'u', 'i', ' ', 4, 'C', 'a', 'i', ' ', 4, 'G', 'u', 'o', ' ', 4, 'C', 'u', 'i', ' ', 4, 'L', 'u', 'n', ' ', 4, 'L', 'i', 'u', ' ', 3, 'Q', 'i', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 4, 'B', 'e', 'i', ' ', 5, 'C', 'h', 'u', 'o', ' ', 5, 'L', 'i', 'n', 'g', ' ', 5, 'M', 'i', 'a', 'n', ' ', 3, 'Q', 'i', ' ', 4, 'Q', 'i', 'e', ' ', 4, 'T', 'a', 'n', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 4, 'G', 'u', 'n', ' ', 4, 'Z', 'o', 'u', ' ', 3, 'Y', 'i', ' ', 3, 'Z', 'i', ' ', 5, 'X', 'i', 'n', 'g', ' ', 6, 'L', 'i', 'a', 'n', 'g', ' ', 4, 'J', 'i', 'n', ' ', 4, 'F', 'e', 'i', ' ', 4, 'R', 'u', 'i', ' ', 4, 'M', 'i', 'n', ' ', 3, 'Y', 'u', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 4, 'F', 'a', 'n', ' ', 3, 'L', 'u', ' ', 3, 'X', 'u', ' ', 6, 'Y', 'i', 'n', 'g', 'l', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 7, 'K', 'a', 's', 'u', 'r', 'i', ' ', 3, 'X', 'u', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'K', 'e', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'R', 'u', 'a', 'n', ' ', 5, 'M', 'i', 'a', 'n', ' ', 3, 'Q', 'i', ' ', 5, 'D', 'u', 'a', 'n', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 3, 'D', 'i', ' ', 4, 'M', 'i', 'n', ' ', 5, 'M', 'i', 'a', 'o', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'X', 'i', 'e', ' ', 4, 'B', 'a', 'o', ' ', 3, 'S', 'i', ' ', 4, 'Q', 'i', 'u', ' ', 5, 'B', 'i', 'a', 'n', ' ', 5, 'H', 'u', 'a', 'n', ' ', 5, 'G', 'e', 'n', 'g', ' ', 5, 'C', 'o', 'n', 'g', ' ', 5, 'M', 'i', 'a', 'n', ' ', 4, 'W', 'e', 'i', ' ', 3, 'F', 'u', ' ', 4, 'W', 'e', 'i', ' ', 3, 'Y', 'u', ' ', 4, 'G', 'o', 'u', ' ', 5, 'M', 'i', 'a', 'o', ' ', 4, 'X', 'i', 'e', ' ', 5, 'L', 'i', 'a', 'n', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 5, 'B', 'i', 'a', 'n', ' ', 4, 'Y', 'u', 'n', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'T', 'i', ' ', 4, 'G', 'u', 'a', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'Y', 'u', 'n', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 5, 'C', 'h', 'a', 'n', ' ', 4, 'D', 'a', 'i', ' ', 4, 'X', 'i', 'a', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 3, 'X', 'u', ' ', 5, 'N', 'a', 'w', 'a', ' ', 7, 'O', 'd', 'o', 's', 'h', 'i', ' ', 5, 'G', 'e', 'n', 'g', ' ', 4, 'S', 'e', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'J', 'i', 'n', ' ', 3, 'Y', 'i', ' ', 5, 'Z', 'h', 'u', 'i', ' ', 3, 'N', 'i', ' ', 5, 'B', 'a', 'n', 'g', ' ', 3, 'G', 'u', ' ', 4, 'P', 'a', 'n', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'C', 'u', 'o', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 7, 'S', 'h', 'u', 'a', 'n', 'g', ' ', 4, 'Y', 'u', 'n', ' ', 4, 'X', 'i', 'a', ' ', 6, 'S', 'h', 'u', 'a', 'i', ' ', 3, 'X', 'i', ' ', 5, 'R', 'o', 'n', 'g', ' ', 4, 'T', 'a', 'o', ' ', 3, 'F', 'u', ' ', 4, 'Y', 'u', 'n', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 4, 'G', 'a', 'o', ' ', 3, 'R', 'u', ' ', 3, 'H', 'u', ' ', 4, 'Z', 'a', 'i', ' ', 5, 'T', 'e', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'S', 'u', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 4, 'T', 'a', 'o', ' ', 5, 'H', 'o', 'r', 'o', ' ', 4, 'C', 'a', 'i', ' ', 3, 'B', 'i', ' ', 5, 'F', 'e', 'n', 'g', ' ', 3, 'C', 'u', ' ', 3, 'L', 'i', ' ', 4, 'S', 'u', 'o', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'X', 'i', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 4, 'L', 'e', 'i', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'M', 'a', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'L', 'u', ' ', 3, 'M', 'o', ' ', 5, 'P', 'i', 'a', 'o', ' ', 5, 'L', 'i', 'a', 'n', ' ', 3, 'M', 'i', ' ', 5, 'X', 'u', 'a', 'n', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 3, 'J', 'i', ' ', 5, 'S', 'h', 'a', 'n', ' ', 4, 'S', 'u', 'i', ' ', 4, 'F', 'a', 'n', ' ', 6, 'S', 'h', 'u', 'a', 'i', ' ', 5, 'B', 'e', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 4, 'S', 'a', 'o', ' ', 4, 'M', 'o', 'u', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 4, 'H', 'u', 'n', ' ', 4, 'S', 'e', 'm', ' ', 3, 'X', 'i', ' ', 5, 'J', 'u', 'n', 'g', ' ', 4, 'X', 'i', 'u', ' ', 4, 'R', 'a', 'n', ' ', 5, 'X', 'u', 'a', 'n', ' ', 4, 'H', 'u', 'i', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 5, 'Z', 'e', 'n', 'g', ' ', 4, 'Z', 'u', 'o', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'S', 'h', 'a', 'n', ' ', 4, 'S', 'a', 'n', ' ', 4, 'L', 'i', 'n', ' ', 3, 'Y', 'u', ' ', 4, 'F', 'a', 'n', ' ', 5, 'L', 'i', 'a', 'o', ' ', 5, 'C', 'h', 'u', 'o', ' ', 4, 'Z', 'u', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'R', 'a', 'o', ' ', 5, 'C', 'h', 'a', 'n', ' ', 4, 'R', 'u', 'i', ' ', 4, 'X', 'i', 'u', ' ', 4, 'H', 'u', 'i', ' ', 4, 'H', 'u', 'a', ' ', 5, 'Z', 'u', 'a', 'n', ' ', 3, 'X', 'i', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 3, 'U', 'n', ' ', 3, 'D', 'a', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 4, 'H', 'u', 'i', ' ', 3, 'X', 'i', ' ', 3, 'S', 'e', ' ', 5, 'J', 'i', 'a', 'n', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 5, 'H', 'u', 'a', 'n', ' ', 4, 'Z', 'a', 'o', ' ', 5, 'C', 'o', 'n', 'g', ' ', 4, 'J', 'i', 'e', ' ', 5, 'J', 'i', 'a', 'o', ' ', 3, 'B', 'o', ' ', 5, 'C', 'h', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 4, 'N', 'a', 'o', ' ', 4, 'S', 'u', 'i', ' ', 3, 'Y', 'i', ' ', 5, 'S', 'h', 'a', 'i', ' ', 3, 'X', 'u', ' ', 3, 'J', 'i', ' ', 4, 'B', 'i', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'L', 'a', 'n', ' ', 3, 'P', 'u', ' ', 4, 'X', 'u', 'n', ' ', 5, 'Z', 'u', 'a', 'n', ' ', 3, 'Q', 'i', ' ', 5, 'P', 'e', 'n', 'g', ' ', 3, 'L', 'i', ' ', 3, 'M', 'o', ' ', 4, 'L', 'e', 'i', ' ', 4, 'X', 'i', 'e', ' ', 5, 'Z', 'u', 'a', 'n', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 4, 'Y', 'o', 'u', ' ', 3, 'X', 'u', ' ', 4, 'L', 'e', 'i', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'C', 'h', 'a', 'n', ' ', 4, 'K', 'o', 'u', ' ', 3, 'L', 'u', ' ', 5, 'C', 'h', 'a', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'C', 'a', 'i', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'Z', 'u', 'i', ' ', 5, 'Z', 'u', 'a', 'n', ' ', 4, 'L', 'u', 'o', ' ', 3, 'X', 'i', ' ', 4, 'D', 'a', 'o', ' ', 4, 'L', 'a', 'n', ' ', 4, 'L', 'e', 'i', ' ', 5, 'L', 'i', 'a', 'n', ' ', 3, 'S', 'i', ' ', 4, 'J', 'i', 'u', ' ', 3, 'Y', 'u', ' ', 5, 'H', 'o', 'n', 'g', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'H', 'e', ' ', 4, 'Y', 'u', 'e', ' ', 3, 'J', 'i', ' ', 4, 'W', 'a', 'n', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 3, 'J', 'i', ' ', 4, 'R', 'e', 'n', ' ', 4, 'W', 'e', 'i', ' ', 4, 'Y', 'u', 'n', ' ', 5, 'H', 'o', 'n', 'g', ' ', 5, 'C', 'h', 'u', 'n', ' ', 3, 'P', 'i', ' ', 4, 'S', 'h', 'a', ' ', 5, 'G', 'a', 'n', 'g', ' ', 3, 'N', 'a', ' ', 4, 'R', 'e', 'n', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 4, 'L', 'u', 'n', ' ', 4, 'F', 'e', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'W', 'e', 'n', ' ', 5, 'F', 'a', 'n', 'g', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'N', 'i', 'u', ' ', 4, 'S', 'h', 'u', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'G', 'a', 'n', ' ', 4, 'X', 'i', 'e', ' ', 3, 'F', 'u', ' ', 5, 'L', 'i', 'a', 'n', ' ', 3, 'Z', 'u', ' ', 5, 'S', 'h', 'e', 'n', ' ', 3, 'X', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 4, 'B', 'a', 'n', ' ', 3, 'F', 'u', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 5, 'S', 'h', 'a', 'o', ' ', 3, 'Y', 'i', ' ', 5, 'J', 'i', 'n', 'g', ' ', 4, 'D', 'a', 'i', ' ', 5, 'B', 'a', 'n', 'g', ' ', 5, 'R', 'o', 'n', 'g', ' ', 4, 'J', 'i', 'e', ' ', 3, 'K', 'u', ' ', 4, 'R', 'a', 'o', ' ', 4, 'D', 'i', 'e', ' ', 5, 'H', 'e', 'n', 'g', ' ', 4, 'H', 'u', 'i', ' ', 4, 'G', 'e', 'i', ' ', 5, 'X', 'u', 'a', 'n', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 4, 'L', 'u', 'o', ' ', 4, 'J', 'u', 'e', ' ', 5, 'J', 'i', 'a', 'o', ' ', 5, 'T', 'o', 'n', 'g', ' ', 5, 'G', 'e', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'o', ' ', 5, 'J', 'u', 'a', 'n', ' ', 4, 'X', 'i', 'u', ' ', 3, 'X', 'i', ' ', 4, 'S', 'u', 'i', ' ', 4, 'T', 'a', 'o', ' ', 3, 'J', 'i', ' ', 3, 'T', 'i', ' ', 3, 'J', 'i', ' ', 3, 'X', 'u', ' ', 5, 'L', 'i', 'n', 'g', ' ', 3, 'X', 'u', ' ', 3, 'Q', 'i', ' ', 4, 'F', 'e', 'i', ' ', 5, 'C', 'h', 'u', 'o', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 4, 'G', 'u', 'n', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 4, 'W', 'e', 'i', ' ', 5, 'M', 'i', 'a', 'n', ' ', 5, 'S', 'h', 'o', 'u', ' ', 5, 'B', 'e', 'n', 'g', ' ', 5, 'C', 'h', 'o', 'u', ' ', 4, 'T', 'a', 'o', ' ', 4, 'L', 'i', 'u', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 4, 'W', 'a', 'n', ' ', 3, 'L', 'u', ' ', 5, 'Z', 'h', 'u', 'i', ' ', 3, 'Z', 'i', ' ', 3, 'K', 'e', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'M', 'i', 'a', 'n', ' ', 4, 'L', 'a', 'n', ' ', 3, 'T', 'i', ' ', 5, 'M', 'i', 'a', 'o', ' ', 3, 'Q', 'i', ' ', 4, 'Y', 'u', 'n', ' ', 4, 'H', 'u', 'i', ' ', 3, 'S', 'i', ' ', 4, 'D', 'u', 'o', ' ', 5, 'D', 'u', 'a', 'n', ' ', 5, 'B', 'i', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'G', 'o', 'u', ' ', 5, 'Z', 'h', 'u', 'i', ' ', 5, 'H', 'u', 'a', 'n', ' ', 3, 'D', 'i', ' ', 3, 'L', 'u', ' ', 5, 'B', 'i', 'a', 'n', ' ', 4, 'M', 'i', 'n', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'J', 'i', 'n', ' ', 3, 'F', 'u', ' ', 3, 'R', 'u', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 5, 'F', 'e', 'n', 'g', ' ', 6, 'S', 'h', 'u', 'a', 'i', ' ', 4, 'G', 'a', 'o', ' ', 5, 'C', 'h', 'a', 'n', ' ', 3, 'L', 'i', ' ', 3, 'Y', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'B', 'i', 'n', ' ', 5, 'P', 'i', 'a', 'o', ' ', 4, 'M', 'a', 'n', ' ', 4, 'L', 'e', 'i', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'S', 'u', 'o', ' ', 4, 'M', 'o', 'u', ' ', 4, 'S', 'a', 'o', ' ', 4, 'X', 'i', 'e', ' ', 5, 'L', 'i', 'a', 'o', ' ', 5, 'S', 'h', 'a', 'n', ' ', 5, 'Z', 'e', 'n', 'g', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'Z', 'a', 'o', ' ', 5, 'H', 'u', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'o', ' ', 5, 'Z', 'u', 'a', 'n', ' ', 4, 'F', 'o', 'u', ' ', 4, 'X', 'i', 'e', ' ', 5, 'G', 'a', 'n', 'g', ' ', 4, 'F', 'o', 'u', ' ', 4, 'Q', 'u', 'e', ' ', 4, 'F', 'o', 'u', ' ', 8, 'K', 'a', 'a', 'k', 'e', 'r', 'u', ' ', 3, 'B', 'o', ' ', 5, 'P', 'i', 'n', 'g', ' ', 4, 'H', 'o', 'u', ' ', 5, 'G', 'a', 'n', 'g', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 4, 'X', 'i', 'a', ' ', 5, 'G', 'u', 'a', 'n', ' ', 4, 'Z', 'u', 'n', ' ', 4, 'T', 'a', 'n', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 3, 'Q', 'i', ' ', 5, 'W', 'e', 'n', 'g', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'L', 'e', 'i', ' ', 4, 'T', 'a', 'n', ' ', 3, 'L', 'u', ' ', 5, 'G', 'u', 'a', 'n', ' ', 5, 'W', 'a', 'n', 'g', ' ', 5, 'W', 'a', 'n', 'g', ' ', 5, 'G', 'a', 'n', 'g', ' ', 5, 'W', 'a', 'n', 'g', ' ', 4, 'H', 'a', 'n', ' ', 4, 'L', 'u', 'o', ' ', 3, 'F', 'u', ' ', 3, 'M', 'i', ' ', 3, 'F', 'a', ' ', 3, 'G', 'u', ' ', 4, 'Z', 'h', 'u', ' ', 3, 'J', 'u', ' ', 4, 'M', 'a', 'o', ' ', 3, 'G', 'u', ' ', 4, 'M', 'i', 'n', ' ', 5, 'G', 'a', 'n', 'g', ' ', 3, 'B', 'a', ' ', 4, 'G', 'u', 'a', ' ', 3, 'T', 'i', ' ', 5, 'J', 'u', 'a', 'n', ' ', 3, 'F', 'u', ' ', 4, 'L', 'i', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 4, 'Z', 'u', 'i', ' ', 4, 'G', 'u', 'a', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 3, 'Y', 'u', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'A', 'n', ' ', 3, 'F', 'a', ' ', 4, 'N', 'a', 'n', ' ', 4, 'S', 'h', 'u', ' ', 3, 'S', 'i', ' ', 3, 'P', 'i', ' ', 3, 'M', 'a', ' ', 4, 'L', 'i', 'u', ' ', 3, 'B', 'a', ' ', 3, 'F', 'a', ' ', 3, 'L', 'i', ' ', 5, 'C', 'h', 'a', 'o', ' ', 4, 'W', 'e', 'i', ' ', 3, 'B', 'i', ' ', 3, 'J', 'i', ' ', 5, 'Z', 'e', 'n', 'g', ' ', 5, 'T', 'o', 'n', 'g', ' ', 4, 'L', 'i', 'u', ' ', 3, 'J', 'i', ' ', 5, 'J', 'u', 'a', 'n', ' ', 3, 'M', 'i', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 4, 'L', 'u', 'o', ' ', 3, 'P', 'i', ' ', 3, 'J', 'i', ' ', 3, 'J', 'i', ' ', 5, 'L', 'u', 'a', 'n', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 4, 'M', 'i', 'e', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 3, 'T', 'a', ' ', 4, 'M', 'e', 'i', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'F', 'e', 'n', ' ', 3, 'B', 'a', ' ', 4, 'G', 'a', 'o', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 3, 'G', 'u', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 5, 'Z', 'a', 'n', 'g', ' ', 4, 'G', 'a', 'o', ' ', 5, 'L', 'i', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 4, 'Z', 'h', 'u', ' ', 3, 'D', 'i', ' ', 4, 'X', 'i', 'u', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'R', 'o', 'n', 'g', ' ', 4, 'Q', 'u', 'n', ' ', 4, 'Q', 'u', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'H', 'u', 'a', 'n', ' ', 4, 'Z', 'u', 'i', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 9, 'Y', 'a', 's', 'h', 'i', 'n', 'a', 'u', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 5, 'G', 'e', 'n', 'g', ' ', 4, 'J', 'i', 'e', ' ', 5, 'T', 'a', 'n', 'g', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 3, 'X', 'i', ' ', 4, 'F', 'a', 'n', ' ', 5, 'S', 'h', 'a', 'n', ' ', 4, 'F', 'e', 'n', ' ', 5, 'S', 'h', 'a', 'n', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'L', 'e', 'i', ' ', 5, 'G', 'e', 'n', 'g', ' ', 4, 'N', 'o', 'u', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 5, 'C', 'h', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 5, 'G', 'o', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 6, 'C', 'h', 'o', 'n', 'g', ' ', 5, 'W', 'e', 'n', 'g', ' ', 4, 'F', 'e', 'n', ' ', 5, 'H', 'o', 'n', 'g', ' ', 4, 'C', 'h', 'i', ' ', 4, 'C', 'h', 'i', ' ', 4, 'C', 'u', 'i', ' ', 3, 'F', 'u', ' ', 4, 'X', 'i', 'a', ' ', 4, 'P', 'e', 'n', ' ', 3, 'Y', 'i', ' ', 3, 'L', 'a', ' ', 3, 'Y', 'i', ' ', 3, 'P', 'i', ' ', 5, 'L', 'i', 'n', 'g', ' ', 4, 'L', 'i', 'u', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'Q', 'u', ' ', 3, 'X', 'i', ' ', 4, 'X', 'i', 'e', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 3, 'X', 'i', ' ', 3, 'X', 'i', ' ', 3, 'Q', 'i', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 4, 'H', 'u', 'i', ' ', 4, 'H', 'u', 'i', ' ', 5, 'X', 'i', 'a', 'o', ' ', 3, 'S', 'e', ' ', 5, 'H', 'o', 'n', 'g', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 3, 'D', 'i', ' ', 4, 'C', 'u', 'i', ' ', 4, 'F', 'e', 'i', ' ', 4, 'T', 'a', 'o', ' ', 4, 'S', 'h', 'a', ' ', 4, 'C', 'h', 'i', ' ', 4, 'Z', 'h', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'X', 'u', 'a', 'n', ' ', 4, 'S', 'h', 'i', ' ', 5, 'P', 'i', 'a', 'n', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 4, 'W', 'a', 'n', ' ', 4, 'H', 'u', 'i', ' ', 4, 'H', 'o', 'u', ' ', 3, 'H', 'e', ' ', 3, 'H', 'e', ' ', 4, 'H', 'a', 'n', ' ', 3, 'A', 'o', ' ', 5, 'P', 'i', 'a', 'o', ' ', 3, 'Y', 'i', ' ', 5, 'L', 'i', 'a', 'n', ' ', 3, 'Q', 'u', ' ', 4, 'L', 'i', 'n', ' ', 4, 'P', 'e', 'n', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 3, 'A', 'o', ' ', 4, 'F', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 4, 'H', 'u', 'i', ' ', 5, 'X', 'u', 'a', 'n', ' ', 4, 'D', 'a', 'o', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'L', 'a', 'o', ' ', 4, 'K', 'a', 'o', ' ', 4, 'M', 'a', 'o', ' ', 4, 'Z', 'h', 'e', ' ', 3, 'Q', 'i', ' ', 4, 'G', 'o', 'u', ' ', 4, 'G', 'o', 'u', ' ', 4, 'G', 'o', 'u', ' ', 4, 'D', 'i', 'e', ' ', 4, 'D', 'i', 'e', ' ', 3, 'E', 'r', ' ', 5, 'S', 'h', 'u', 'a', ' ', 5, 'R', 'u', 'a', 'n', ' ', 3, 'E', 'r', ' ', 4, 'N', 'a', 'i', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 4, 'L', 'e', 'i', ' ', 5, 'T', 'i', 'n', 'g', ' ', 3, 'Z', 'i', ' ', 5, 'G', 'e', 'n', 'g', ' ', 5, 'C', 'h', 'a', 'o', ' ', 4, 'H', 'a', 'o', ' ', 4, 'Y', 'u', 'n', ' ', 3, 'P', 'a', ' ', 3, 'P', 'i', ' ', 4, 'C', 'h', 'i', ' ', 3, 'S', 'i', ' ', 4, 'C', 'h', 'u', ' ', 4, 'J', 'i', 'a', ' ', 3, 'J', 'u', ' ', 3, 'H', 'e', ' ', 4, 'C', 'h', 'u', ' ', 4, 'L', 'a', 'o', ' ', 4, 'L', 'u', 'n', ' ', 3, 'J', 'i', ' ', 5, 'T', 'a', 'n', 'g', ' ', 3, 'O', 'u', ' ', 4, 'L', 'o', 'u', ' ', 4, 'N', 'o', 'u', ' ', 4, 'G', 'o', 'u', ' ', 5, 'P', 'a', 'n', 'g', ' ', 3, 'Z', 'e', ' ', 4, 'L', 'o', 'u', ' ', 3, 'J', 'i', ' ', 4, 'L', 'a', 'o', ' ', 4, 'H', 'u', 'o', ' ', 4, 'Y', 'o', 'u', ' ', 3, 'M', 'o', ' ', 5, 'H', 'u', 'a', 'i', ' ', 3, 'E', 'r', ' ', 4, 'Z', 'h', 'e', ' ', 5, 'T', 'i', 'n', 'g', ' ', 3, 'Y', 'e', ' ', 3, 'D', 'a', ' ', 5, 'S', 'o', 'n', 'g', ' ', 4, 'Q', 'i', 'n', ' ', 4, 'Y', 'u', 'n', ' ', 4, 'C', 'h', 'i', ' ', 4, 'D', 'a', 'n', ' ', 4, 'D', 'a', 'n', ' ', 5, 'H', 'o', 'n', 'g', ' ', 5, 'G', 'e', 'n', 'g', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'N', 'i', 'e', ' ', 4, 'D', 'a', 'n', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 4, 'C', 'h', 'e', ' ', 5, 'L', 'i', 'n', 'g', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 4, 'Y', 'o', 'u', ' ', 3, 'W', 'a', ' ', 5, 'L', 'i', 'a', 'o', ' ', 5, 'L', 'o', 'n', 'g', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'N', 'i', 'n', 'g', ' ', 5, 'T', 'i', 'a', 'o', ' ', 3, 'E', 'r', ' ', 3, 'Y', 'a', ' ', 4, 'D', 'i', 'e', ' ', 4, 'G', 'u', 'a', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'H', 'a', 'o', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 4, 'L', 'i', 'e', ' ', 4, 'P', 'i', 'n', ' ', 5, 'J', 'i', 'n', 'g', ' ', 3, 'J', 'u', ' ', 3, 'B', 'i', ' ', 3, 'D', 'i', ' ', 4, 'G', 'u', 'o', ' ', 4, 'W', 'e', 'n', ' ', 3, 'X', 'u', ' ', 5, 'P', 'i', 'n', 'g', ' ', 5, 'C', 'o', 'n', 'g', ' ', 8, 'S', 'h', 'i', 'k', 'a', 't', 'o', ' ', 5, 'T', 'i', 'n', 'g', ' ', 3, 'Y', 'u', ' ', 5, 'C', 'o', 'n', 'g', ' ', 4, 'K', 'u', 'i', ' ', 10, 'T', 's', 'u', 'r', 'a', 'n', 'e', 'r', 'u', ' ', 4, 'K', 'u', 'i', ' ', 5, 'C', 'o', 'n', 'g', ' ', 5, 'L', 'i', 'a', 'n', ' ', 5, 'W', 'e', 'n', 'g', ' ', 4, 'K', 'u', 'i', ' ', 5, 'L', 'i', 'a', 'n', ' ', 5, 'L', 'i', 'a', 'n', ' ', 5, 'C', 'o', 'n', 'g', ' ', 3, 'A', 'o', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 5, 'S', 'o', 'n', 'g', ' ', 5, 'T', 'i', 'n', 'g', ' ', 4, 'K', 'u', 'i', ' ', 4, 'N', 'i', 'e', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'D', 'a', 'n', ' ', 5, 'N', 'i', 'n', 'g', ' ', 4, 'Q', 'i', 'e', ' ', 3, 'J', 'i', ' ', 5, 'T', 'i', 'n', 'g', ' ', 5, 'T', 'i', 'n', 'g', ' ', 5, 'L', 'o', 'n', 'g', ' ', 3, 'Y', 'u', ' ', 3, 'Y', 'u', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 3, 'S', 'i', ' ', 3, 'S', 'u', ' ', 3, 'Y', 'i', ' ', 3, 'S', 'u', ' ', 3, 'S', 'i', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 4, 'R', 'o', 'u', ' ', 3, 'Y', 'i', ' ', 3, 'L', 'e', ' ', 3, 'J', 'i', ' ', 4, 'Q', 'i', 'u', ' ', 4, 'K', 'e', 'n', ' ', 4, 'C', 'a', 'o', ' ', 3, 'G', 'e', ' ', 3, 'D', 'i', ' ', 5, 'H', 'u', 'a', 'n', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 4, 'R', 'e', 'n', ' ', 5, 'X', 'i', 'a', 'o', ' ', 3, 'R', 'u', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 3, 'D', 'u', ' ', 5, 'G', 'a', 'n', 'g', ' ', 5, 'R', 'o', 'n', 'g', ' ', 4, 'G', 'a', 'n', ' ', 4, 'C', 'h', 'a', ' ', 3, 'W', 'o', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 3, 'G', 'u', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'H', 'a', 'n', ' ', 3, 'F', 'u', ' ', 4, 'F', 'e', 'i', ' ', 4, 'F', 'e', 'n', ' ', 4, 'P', 'e', 'i', ' ', 5, 'P', 'a', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'F', 'a', 'n', 'g', ' ', 5, 'Z', 'h', 'u', 'n', ' ', 4, 'Y', 'o', 'u', ' ', 3, 'N', 'a', ' ', 5, 'H', 'a', 'n', 'g', ' ', 4, 'K', 'e', 'n', ' ', 4, 'R', 'a', 'n', ' ', 5, 'G', 'o', 'n', 'g', ' ', 3, 'Y', 'u', ' ', 4, 'W', 'e', 'n', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'J', 'i', 'n', ' ', 3, 'P', 'i', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 3, 'X', 'i', ' ', 3, 'X', 'i', ' ', 4, 'F', 'e', 'i', ' ', 4, 'K', 'e', 'n', ' ', 5, 'J', 'i', 'n', 'g', ' ', 4, 'T', 'a', 'i', ' ', 5, 'S', 'h', 'e', 'n', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 4, 'X', 'i', 'e', ' ', 5, 'S', 'h', 'e', 'n', ' ', 4, 'W', 'e', 'i', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 4, 'D', 'i', 'e', ' ', 4, 'D', 'a', 'n', ' ', 4, 'F', 'e', 'i', ' ', 3, 'B', 'a', ' ', 3, 'B', 'o', ' ', 3, 'Q', 'u', ' ', 5, 'T', 'i', 'a', 'n', ' ', 4, 'B', 'e', 'i', ' ', 4, 'G', 'u', 'a', ' ', 4, 'T', 'a', 'i', ' ', 3, 'Z', 'i', ' ', 3, 'K', 'u', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'N', 'i', ' ', 5, 'P', 'i', 'n', 'g', ' ', 3, 'Z', 'i', ' ', 3, 'F', 'u', ' ', 5, 'P', 'a', 'n', 'g', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'Z', 'u', 'o', ' ', 4, 'P', 'e', 'i', ' ', 4, 'J', 'i', 'a', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'B', 'a', 'o', ' ', 3, 'M', 'u', ' ', 3, 'Q', 'u', ' ', 3, 'H', 'u', ' ', 3, 'K', 'e', ' ', 3, 'Y', 'i', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'X', 'u', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 5, 'L', 'o', 'n', 'g', ' ', 5, 'D', 'o', 'n', 'g', ' ', 3, 'K', 'a', ' ', 3, 'L', 'u', ' ', 5, 'J', 'i', 'n', 'g', ' ', 3, 'N', 'u', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'P', 'a', 'n', 'g', ' ', 4, 'K', 'u', 'a', ' ', 3, 'Y', 'i', ' ', 6, 'G', 'u', 'a', 'n', 'g', ' ', 4, 'G', 'a', 'i', ' ', 3, 'G', 'e', ' ', 5, 'D', 'o', 'n', 'g', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'X', 'i', 'a', 'o', ' ', 6, 'X', 'i', 'o', 'n', 'g', ' ', 6, 'X', 'i', 'o', 'n', 'g', ' ', 3, 'E', 'r', ' ', 2, 'E', ' ', 5, 'X', 'i', 'n', 'g', ' ', 5, 'P', 'i', 'a', 'n', ' ', 5, 'N', 'e', 'n', 'g', ' ', 3, 'Z', 'i', ' ', 4, 'G', 'u', 'i', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 5, 'T', 'i', 'a', 'o', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'C', 'u', 'i', ' ', 4, 'M', 'e', 'i', ' ', 4, 'X', 'i', 'e', ' ', 4, 'C', 'u', 'i', ' ', 4, 'X', 'i', 'e', ' ', 3, 'M', 'o', ' ', 4, 'M', 'a', 'i', ' ', 3, 'J', 'i', ' ', 11, 'O', 'b', 'i', 'y', 'a', 'a', 'k', 'a', 's', 'u', ' ', 5, 'K', 'u', 'a', 'i', ' ', 3, 'S', 'a', ' ', 5, 'Z', 'a', 'n', 'g', ' ', 3, 'Q', 'i', ' ', 4, 'N', 'a', 'o', ' ', 3, 'M', 'i', ' ', 5, 'N', 'o', 'n', 'g', ' ', 5, 'L', 'u', 'a', 'n', ' ', 4, 'W', 'a', 'n', ' ', 3, 'B', 'o', ' ', 4, 'W', 'e', 'n', ' ', 5, 'G', 'u', 'a', 'n', ' ', 4, 'Q', 'i', 'u', ' ', 5, 'J', 'i', 'a', 'o', ' ', 5, 'J', 'i', 'n', 'g', ' ', 4, 'R', 'o', 'u', ' ', 5, 'H', 'e', 'n', 'g', ' ', 4, 'C', 'u', 'o', ' ', 4, 'L', 'i', 'e', ' ', 5, 'S', 'h', 'a', 'n', ' ', 5, 'T', 'i', 'n', 'g', ' ', 4, 'M', 'e', 'i', ' ', 5, 'C', 'h', 'u', 'n', ' ', 5, 'S', 'h', 'e', 'n', ' ', 4, 'X', 'i', 'e', ' ', 3, 'D', 'e', ' ', 4, 'Z', 'u', 'i', ' ', 3, 'C', 'u', ' ', 4, 'X', 'i', 'u', ' ', 4, 'X', 'i', 'n', ' ', 4, 'T', 'u', 'o', ' ', 4, 'P', 'a', 'o', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 4, 'N', 'e', 'i', ' ', 3, 'F', 'u', ' ', 4, 'D', 'o', 'u', ' ', 4, 'T', 'u', 'o', ' ', 5, 'N', 'i', 'a', 'o', ' ', 4, 'N', 'o', 'y', ' ', 3, 'P', 'i', ' ', 3, 'G', 'u', ' ', 4, 'G', 'u', 'a', ' ', 3, 'L', 'i', ' ', 5, 'L', 'i', 'a', 'n', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 4, 'C', 'u', 'i', ' ', 4, 'J', 'i', 'e', ' ', 6, 'L', 'i', 'a', 'n', 'g', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 3, 'P', 'i', ' ', 5, 'B', 'i', 'a', 'o', ' ', 4, 'L', 'u', 'n', ' ', 5, 'P', 'i', 'a', 'n', ' ', 4, 'G', 'u', 'o', ' ', 4, 'K', 'u', 'i', ' ', 5, 'C', 'h', 'u', 'i', ' ', 4, 'D', 'a', 'n', ' ', 5, 'T', 'i', 'a', 'n', ' ', 4, 'N', 'e', 'i', ' ', 5, 'J', 'i', 'n', 'g', ' ', 4, 'J', 'i', 'e', ' ', 3, 'L', 'a', ' ', 3, 'Y', 'i', ' ', 3, 'A', 'n', ' ', 4, 'R', 'e', 'n', ' ', 5, 'S', 'h', 'e', 'n', ' ', 5, 'C', 'h', 'u', 'o', ' ', 3, 'F', 'u', ' ', 3, 'F', 'u', ' ', 3, 'J', 'u', ' ', 4, 'F', 'e', 'i', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 4, 'W', 'a', 'n', ' ', 5, 'D', 'o', 'n', 'g', ' ', 3, 'P', 'i', ' ', 4, 'G', 'u', 'o', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 5, 'D', 'i', 'n', 'g', ' ', 3, 'W', 'u', ' ', 4, 'M', 'e', 'i', ' ', 5, 'R', 'u', 'a', 'n', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'C', 'o', 'u', ' ', 4, 'G', 'u', 'a', ' ', 3, 'O', 'u', ' ', 3, 'D', 'i', ' ', 3, 'A', 'n', ' ', 5, 'X', 'i', 'n', 'g', ' ', 4, 'N', 'a', 'o', ' ', 3, 'Y', 'u', ' ', 6, 'C', 'h', 'u', 'a', 'n', ' ', 4, 'N', 'a', 'n', ' ', 4, 'Y', 'u', 'n', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 4, 'R', 'o', 'u', ' ', 2, 'E', ' ', 4, 'S', 'a', 'i', ' ', 3, 'T', 'u', ' ', 4, 'Y', 'a', 'o', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'W', 'e', 'i', ' ', 5, 'J', 'i', 'a', 'o', ' ', 3, 'Y', 'u', ' ', 4, 'J', 'i', 'a', ' ', 5, 'D', 'u', 'a', 'n', ' ', 3, 'B', 'i', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 3, 'F', 'u', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'N', 'i', ' ', 5, 'M', 'i', 'a', 'n', ' ', 3, 'W', 'a', ' ', 5, 'T', 'e', 'n', 'g', ' ', 4, 'T', 'u', 'i', ' ', 5, 'B', 'a', 'n', 'g', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 3, 'L', 'u', ' ', 3, 'W', 'a', ' ', 4, 'S', 'o', 'u', ' ', 5, 'T', 'a', 'n', 'g', ' ', 3, 'S', 'u', ' ', 5, 'Z', 'h', 'u', 'i', ' ', 3, 'G', 'e', ' ', 3, 'Y', 'i', ' ', 3, 'B', 'o', ' ', 5, 'L', 'i', 'a', 'o', ' ', 3, 'J', 'i', ' ', 3, 'P', 'i', ' ', 4, 'X', 'i', 'e', ' ', 4, 'G', 'a', 'o', ' ', 3, 'L', 'u', ' ', 4, 'B', 'i', 'n', ' ', 3, 'O', 'u', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 3, 'L', 'u', ' ', 4, 'G', 'u', 'o', ' ', 5, 'P', 'a', 'n', 'g', ' ', 6, 'C', 'h', 'u', 'a', 'i', ' ', 5, 'P', 'i', 'a', 'o', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 3, 'F', 'u', ' ', 5, 'T', 'a', 'n', 'g', ' ', 3, 'M', 'o', ' ', 3, 'X', 'i', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 3, 'L', 'u', ' ', 5, 'J', 'i', 'a', 'o', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 3, 'L', 'u', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'T', 'a', 'r', 'a', ' ', 5, 'C', 'h', 'u', 'n', ' ', 5, 'L', 'i', 'a', 'n', ' ', 5, 'T', 'o', 'n', 'g', ' ', 5, 'P', 'e', 'n', 'g', ' ', 3, 'N', 'i', ' ', 4, 'Z', 'h', 'a', ' ', 5, 'L', 'i', 'a', 'o', ' ', 4, 'C', 'u', 'i', ' ', 4, 'G', 'u', 'i', ' ', 5, 'X', 'i', 'a', 'o', ' ', 5, 'T', 'e', 'n', 'g', ' ', 4, 'F', 'a', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'J', 'i', 'a', 'o', ' ', 5, 'S', 'h', 'a', 'n', ' ', 3, 'W', 'u', ' ', 4, 'C', 'u', 'i', ' ', 4, 'R', 'u', 'n', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 4, 'S', 'u', 'i', ' ', 4, 'F', 'e', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'T', 'a', 'n', ' ', 5, 'Z', 'h', 'u', 'a', ' ', 4, 'D', 'a', 'n', ' ', 5, 'K', 'u', 'a', 'i', ' ', 5, 'N', 'o', 'n', 'g', ' ', 4, 'T', 'u', 'n', ' ', 5, 'L', 'i', 'a', 'n', ' ', 3, 'B', 'i', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 4, 'J', 'u', 'e', ' ', 4, 'C', 'h', 'u', ' ', 3, 'Y', 'i', ' ', 5, 'J', 'u', 'a', 'n', ' ', 3, 'L', 'a', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'S', 'a', 'o', ' ', 4, 'T', 'u', 'n', ' ', 3, 'G', 'u', ' ', 3, 'Q', 'i', ' ', 4, 'C', 'u', 'i', ' ', 4, 'B', 'i', 'n', ' ', 4, 'X', 'u', 'n', ' ', 3, 'R', 'u', ' ', 4, 'H', 'u', 'o', ' ', 5, 'Z', 'a', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'B', 'i', 'a', 'o', ' ', 5, 'X', 'i', 'n', 'g', ' ', 5, 'K', 'u', 'a', 'n', ' ', 3, 'L', 'a', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'L', 'u', ' ', 4, 'H', 'u', 'o', ' ', 5, 'Z', 'a', 'n', 'g', ' ', 4, 'L', 'u', 'o', ' ', 3, 'Q', 'u', ' ', 5, 'Z', 'a', 'n', 'g', ' ', 5, 'L', 'u', 'a', 'n', ' ', 3, 'N', 'i', ' ', 5, 'Z', 'a', 'n', 'g', ' ', 5, 'C', 'h', 'e', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 3, 'W', 'o', ' ', 6, 'G', 'u', 'a', 'n', 'g', ' ', 5, 'Z', 'a', 'n', 'g', ' ', 4, 'L', 'i', 'n', ' ', 6, 'G', 'u', 'a', 'n', 'g', ' ', 3, 'Z', 'i', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'N', 'i', 'e', ' ', 5, 'C', 'h', 'o', 'u', ' ', 3, 'J', 'i', ' ', 4, 'G', 'a', 'o', ' ', 5, 'C', 'h', 'o', 'u', ' ', 5, 'M', 'i', 'a', 'n', ' ', 4, 'N', 'i', 'e', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'G', 'e', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'D', 'i', 'e', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'X', 'i', 'u', ' ', 4, 'T', 'a', 'i', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 4, 'J', 'i', 'u', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 4, 'C', 'h', 'a', ' ', 4, 'Y', 'a', 'o', ' ', 3, 'Y', 'u', ' ', 6, 'C', 'h', 'o', 'n', 'g', ' ', 3, 'X', 'i', ' ', 3, 'X', 'i', ' ', 4, 'J', 'i', 'u', ' ', 3, 'Y', 'u', ' ', 3, 'Y', 'u', ' ', 5, 'X', 'i', 'n', 'g', ' ', 3, 'J', 'u', ' ', 4, 'J', 'i', 'u', ' ', 4, 'X', 'i', 'n', ' ', 4, 'S', 'h', 'e', ' ', 4, 'S', 'h', 'e', ' ', 7, 'Y', 'a', 'd', 'o', 'r', 'u', ' ', 4, 'J', 'i', 'u', ' ', 4, 'S', 'h', 'i', ' ', 4, 'T', 'a', 'n', ' ', 4, 'S', 'h', 'u', ' ', 4, 'S', 'h', 'i', ' ', 5, 'T', 'i', 'a', 'n', ' ', 4, 'D', 'a', 'n', ' ', 3, 'P', 'u', ' ', 3, 'P', 'u', ' ', 5, 'G', 'u', 'a', 'n', ' ', 4, 'H', 'u', 'a', ' ', 4, 'T', 'a', 'n', ' ', 6, 'C', 'h', 'u', 'a', 'n', ' ', 5, 'S', 'h', 'u', 'n', ' ', 4, 'X', 'i', 'a', ' ', 3, 'W', 'u', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 4, 'D', 'a', 'o', ' ', 5, 'G', 'a', 'n', 'g', ' ', 5, 'S', 'h', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 3, 'P', 'a', ' ', 4, 'T', 'a', 'i', ' ', 4, 'F', 'a', 'n', ' ', 4, 'B', 'a', 'n', ' ', 6, 'C', 'h', 'u', 'a', 'n', ' ', 5, 'H', 'a', 'n', 'g', ' ', 5, 'F', 'a', 'n', 'g', ' ', 4, 'B', 'a', 'n', ' ', 4, 'Q', 'u', 'e', ' ', 7, 'H', 'e', 's', 'a', 'k', 'i', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'C', 'a', 'n', 'g', ' ', 5, 'L', 'i', 'n', 'g', ' ', 4, 'Z', 'h', 'u', ' ', 3, 'Z', 'e', ' ', 4, 'D', 'u', 'o', ' ', 3, 'B', 'o', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'G', 'e', ' ', 6, 'C', 'h', 'u', 'a', 'n', ' ', 4, 'J', 'i', 'a', ' ', 3, 'L', 'u', ' ', 5, 'H', 'o', 'n', 'g', ' ', 5, 'P', 'a', 'n', 'g', ' ', 3, 'X', 'i', ' ', 3, 'F', 'u', ' ', 4, 'Z', 'a', 'o', ' ', 5, 'F', 'e', 'n', 'g', ' ', 3, 'L', 'i', ' ', 5, 'S', 'h', 'a', 'o', ' ', 3, 'Y', 'u', ' ', 5, 'L', 'a', 'n', 'g', ' ', 5, 'T', 'i', 'n', 'g', ' ', 4, 'W', 'e', 'i', ' ', 3, 'B', 'o', ' ', 5, 'M', 'e', 'n', 'g', ' ', 5, 'N', 'i', 'a', 'n', ' ', 3, 'J', 'u', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 5, 'S', 'h', 'o', 'u', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 5, 'B', 'i', 'a', 'n', ' ', 4, 'M', 'a', 'o', ' ', 4, 'D', 'i', 'e', ' ', 5, 'B', 'a', 'n', 'g', ' ', 4, 'C', 'h', 'a', ' ', 3, 'Y', 'i', ' ', 4, 'S', 'a', 'o', ' ', 5, 'C', 'a', 'n', 'g', ' ', 4, 'C', 'a', 'o', ' ', 4, 'L', 'o', 'u', ' ', 4, 'D', 'a', 'i', ' ', 5, 'S', 'o', 'r', 'i', ' ', 4, 'Y', 'a', 'o', ' ', 5, 'T', 'o', 'n', 'g', ' ', 7, 'Y', 'o', 'f', 'u', 'n', 'e', ' ', 5, 'D', 'a', 'n', 'g', ' ', 4, 'T', 'a', 'n', ' ', 3, 'L', 'u', ' ', 3, 'Y', 'i', ' ', 4, 'J', 'i', 'e', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'H', 'u', 'o', ' ', 5, 'M', 'e', 'n', 'g', ' ', 3, 'Q', 'i', ' ', 3, 'L', 'u', ' ', 3, 'L', 'u', ' ', 5, 'C', 'h', 'a', 'n', ' ', 7, 'S', 'h', 'u', 'a', 'n', 'g', ' ', 4, 'G', 'e', 'n', ' ', 6, 'L', 'i', 'a', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'S', 'e', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'F', 'u', ' ', 5, 'P', 'i', 'n', 'g', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'C', 'a', 'o', ' ', 4, 'C', 'a', 'o', ' ', 3, 'Y', 'i', ' ', 3, 'L', 'e', ' ', 5, 'T', 'i', 'n', 'g', ' ', 4, 'Q', 'i', 'u', ' ', 3, 'A', 'i', ' ', 4, 'N', 'a', 'i', ' ', 5, 'T', 'i', 'a', 'o', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'J', 'i', 'e', ' ', 5, 'P', 'e', 'n', 'g', ' ', 4, 'W', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 5, 'C', 'h', 'a', 'i', ' ', 5, 'M', 'i', 'a', 'n', ' ', 4, 'M', 'i', 'e', ' ', 4, 'G', 'a', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 3, 'Y', 'u', ' ', 5, 'S', 'h', 'u', 'o', ' ', 6, 'Q', 'i', 'o', 'n', 'g', ' ', 3, 'T', 'u', ' ', 4, 'X', 'i', 'a', ' ', 3, 'Q', 'i', ' ', 5, 'M', 'a', 'n', 'g', ' ', 3, 'Z', 'i', ' ', 4, 'H', 'u', 'i', ' ', 4, 'S', 'u', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 3, 'B', 'i', ' ', 3, 'F', 'u', ' ', 4, 'T', 'u', 'n', ' ', 4, 'W', 'e', 'i', ' ', 3, 'W', 'u', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'Q', 'i', ' ', 5, 'S', 'h', 'a', 'n', ' ', 4, 'W', 'e', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'R', 'e', 'n', ' ', 4, 'F', 'o', 'u', ' ', 4, 'K', 'o', 'u', ' ', 4, 'J', 'i', 'e', ' ', 3, 'L', 'u', ' ', 3, 'X', 'u', ' ', 3, 'J', 'i', ' ', 4, 'Q', 'i', 'n', ' ', 3, 'Q', 'i', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'F', 'e', 'n', ' ', 3, 'B', 'a', ' ', 4, 'R', 'u', 'i', ' ', 4, 'X', 'i', 'n', ' ', 3, 'J', 'i', ' ', 4, 'H', 'u', 'a', ' ', 4, 'H', 'u', 'a', ' ', 5, 'F', 'a', 'n', 'g', ' ', 3, 'W', 'u', ' ', 4, 'J', 'u', 'e', ' ', 4, 'G', 'o', 'u', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'Y', 'u', 'n', ' ', 4, 'Q', 'i', 'n', ' ', 3, 'A', 'o', ' ', 4, 'C', 'h', 'u', ' ', 4, 'M', 'a', 'o', ' ', 3, 'Y', 'a', ' ', 4, 'F', 'e', 'i', ' ', 5, 'R', 'e', 'n', 'g', ' ', 5, 'H', 'a', 'n', 'g', ' ', 5, 'C', 'o', 'n', 'g', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'Y', 'o', 'u', ' ', 5, 'B', 'i', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 5, 'S', 'u', 's', 'a', ' ', 4, 'W', 'e', 'i', ' ', 3, 'L', 'i', ' ', 3, 'P', 'i', ' ', 2, 'E', ' ', 5, 'X', 'i', 'a', 'n', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 5, 'C', 'a', 'n', 'g', ' ', 5, 'M', 'e', 'n', 'g', ' ', 3, 'S', 'u', ' ', 3, 'Y', 'i', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'R', 'a', 'n', ' ', 5, 'L', 'i', 'n', 'g', ' ', 4, 'T', 'a', 'i', ' ', 5, 'T', 'i', 'a', 'o', ' ', 3, 'D', 'i', ' ', 5, 'M', 'i', 'a', 'o', ' ', 6, 'Q', 'i', 'o', 'n', 'g', ' ', 3, 'L', 'i', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 3, 'K', 'e', ' ', 3, 'M', 'u', ' ', 4, 'P', 'e', 'i', ' ', 4, 'B', 'a', 'o', ' ', 4, 'G', 'o', 'u', ' ', 4, 'M', 'i', 'n', ' ', 3, 'Y', 'i', ' ', 3, 'Y', 'i', ' ', 3, 'J', 'u', ' ', 3, 'P', 'i', ' ', 4, 'R', 'u', 'o', ' ', 3, 'K', 'u', ' ', 4, 'Z', 'h', 'u', ' ', 3, 'N', 'i', ' ', 3, 'B', 'o', ' ', 5, 'B', 'i', 'n', 'g', ' ', 5, 'S', 'h', 'a', 'n', ' ', 4, 'Q', 'i', 'u', ' ', 4, 'Y', 'a', 'o', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'B', 'e', 'n', ' ', 5, 'H', 'o', 'n', 'g', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'Z', 'h', 'a', ' ', 5, 'D', 'o', 'n', 'g', ' ', 3, 'J', 'u', ' ', 4, 'D', 'i', 'e', ' ', 4, 'N', 'i', 'e', ' ', 4, 'G', 'a', 'n', ' ', 3, 'H', 'u', ' ', 5, 'P', 'i', 'n', 'g', ' ', 4, 'M', 'e', 'i', ' ', 3, 'F', 'u', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 3, 'G', 'u', ' ', 3, 'B', 'i', ' ', 4, 'W', 'e', 'i', ' ', 3, 'F', 'u', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 4, 'M', 'a', 'o', ' ', 4, 'F', 'a', 'n', ' ', 4, 'Q', 'i', 'e', ' ', 4, 'M', 'a', 'o', ' ', 4, 'M', 'a', 'o', ' ', 3, 'B', 'a', ' ', 3, 'Z', 'i', ' ', 3, 'M', 'o', ' ', 3, 'Z', 'i', ' ', 3, 'D', 'i', ' ', 4, 'C', 'h', 'i', ' ', 3, 'J', 'i', ' ', 5, 'J', 'i', 'n', 'g', ' ', 5, 'L', 'o', 'n', 'g', ' ', 5, 'N', 'i', 'a', 'o', ' ', 4, 'X', 'u', 'e', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 6, 'Q', 'i', 'o', 'n', 'g', ' ', 3, 'G', 'e', ' ', 5, 'M', 'i', 'n', 'g', ' ', 3, 'L', 'i', ' ', 5, 'R', 'o', 'n', 'g', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'G', 'e', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'C', 'h', 'a', 'i', ' ', 5, 'C', 'h', 'e', 'n', ' ', 3, 'Y', 'u', ' ', 4, 'X', 'i', 'u', ' ', 3, 'Z', 'i', ' ', 4, 'L', 'i', 'e', ' ', 3, 'W', 'u', ' ', 3, 'J', 'i', ' ', 4, 'K', 'u', 'i', ' ', 3, 'C', 'e', ' ', 6, 'C', 'h', 'o', 'n', 'g', ' ', 3, 'C', 'i', ' ', 4, 'G', 'o', 'u', ' ', 6, 'G', 'u', 'a', 'n', 'g', ' ', 5, 'M', 'a', 'n', 'g', ' ', 4, 'C', 'h', 'i', ' ', 5, 'J', 'i', 'a', 'o', ' ', 5, 'J', 'i', 'a', 'o', ' ', 3, 'F', 'u', ' ', 3, 'Y', 'u', ' ', 4, 'Z', 'h', 'u', ' ', 3, 'Z', 'i', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 4, 'H', 'u', 'i', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'C', 'h', 'a', ' ', 3, 'F', 'a', ' ', 5, 'R', 'o', 'n', 'g', ' ', 3, 'R', 'u', ' ', 6, 'C', 'h', 'o', 'n', 'g', ' ', 5, 'M', 'a', 'n', 'g', ' ', 5, 'T', 'o', 'n', 'g', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'X', 'u', 'n', ' ', 5, 'H', 'u', 'a', 'n', ' ', 4, 'K', 'u', 'a', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 4, 'G', 'a', 'i', ' ', 3, 'D', 'a', ' ', 5, 'J', 'i', 'n', 'g', ' ', 5, 'X', 'i', 'n', 'g', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 4, 'C', 'a', 'o', ' ', 5, 'J', 'i', 'n', 'g', ' ', 3, 'E', 'r', ' ', 3, 'A', 'n', ' ', 5, 'S', 'h', 'o', 'u', ' ', 4, 'C', 'h', 'i', ' ', 4, 'R', 'e', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'T', 'i', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 5, 'P', 'i', 'n', 'g', ' ', 3, 'L', 'i', ' ', 4, 'J', 'i', 'n', ' ', 4, 'L', 'a', 'o', ' ', 4, 'S', 'h', 'u', ' ', 7, 'Z', 'h', 'u', 'a', 'n', 'g', ' ', 3, 'D', 'a', ' ', 4, 'J', 'i', 'a', ' ', 4, 'R', 'a', 'o', ' ', 3, 'B', 'i', ' ', 3, 'Z', 'e', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 4, 'H', 'u', 'i', ' ', 3, 'Q', 'i', ' ', 5, 'D', 'a', 'n', 'g', ' ', 5, 'R', 'o', 'n', 'g', ' ', 4, 'H', 'u', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'L', 'u', 'o', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'X', 'u', 'n', ' ', 4, 'J', 'i', 'n', ' ', 4, 'S', 'u', 'n', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'M', 'a', 'i', ' ', 5, 'H', 'o', 'n', 'g', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 4, 'Y', 'a', 'o', ' ', 3, 'D', 'u', ' ', 4, 'W', 'e', 'i', ' ', 4, 'C', 'h', 'u', ' ', 4, 'D', 'o', 'u', ' ', 3, 'F', 'u', ' ', 4, 'R', 'e', 'n', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'H', 'e', ' ', 3, 'B', 'i', ' ', 3, 'B', 'u', ' ', 4, 'Y', 'u', 'n', ' ', 3, 'D', 'i', ' ', 3, 'T', 'u', ' ', 4, 'S', 'u', 'i', ' ', 4, 'S', 'u', 'i', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 5, 'C', 'h', 'e', 'n', ' ', 3, 'W', 'u', ' ', 4, 'B', 'i', 'e', ' ', 3, 'X', 'i', ' ', 5, 'G', 'e', 'n', 'g', ' ', 3, 'L', 'i', ' ', 3, 'F', 'u', ' ', 4, 'Z', 'h', 'u', ' ', 3, 'M', 'o', ' ', 3, 'L', 'i', ' ', 7, 'Z', 'h', 'u', 'a', 'n', 'g', ' ', 3, 'J', 'i', ' ', 4, 'D', 'u', 'o', ' ', 4, 'Q', 'i', 'u', ' ', 4, 'S', 'h', 'a', ' ', 4, 'S', 'u', 'o', ' ', 5, 'C', 'h', 'e', 'n', ' ', 5, 'F', 'e', 'n', 'g', ' ', 3, 'J', 'u', ' ', 4, 'M', 'e', 'i', ' ', 5, 'M', 'e', 'n', 'g', ' ', 5, 'X', 'i', 'n', 'g', ' ', 5, 'J', 'i', 'n', 'g', ' ', 4, 'C', 'h', 'e', ' ', 4, 'X', 'i', 'n', ' ', 4, 'J', 'u', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'T', 'i', 'n', 'g', ' ', 5, 'D', 'i', 'a', 'o', ' ', 4, 'C', 'u', 'o', ' ', 4, 'W', 'a', 'n', ' ', 4, 'H', 'a', 'n', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'C', 'u', 'o', ' ', 4, 'J', 'i', 'a', ' ', 5, 'W', 'a', 'n', 'g', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'N', 'i', 'u', ' ', 5, 'S', 'h', 'a', 'o', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'L', 'a', 'n', 'g', ' ', 3, 'F', 'u', ' ', 2, 'E', ' ', 3, 'M', 'o', ' ', 4, 'W', 'e', 'n', ' ', 4, 'J', 'i', 'e', ' ', 4, 'N', 'a', 'n', ' ', 3, 'M', 'u', ' ', 4, 'K', 'a', 'n', ' ', 4, 'L', 'a', 'i', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'S', 'h', 'i', ' ', 3, 'W', 'o', ' ', 6, 'U', 's', 'a', 'g', 'i', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'H', 'u', 'o', ' ', 4, 'Y', 'o', 'u', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'N', 'u', 'c', ' ', 5, 'C', 'h', 'u', 'n', ' ', 5, 'M', 'a', 'n', 'g', ' ', 5, 'M', 'a', 'n', 'g', ' ', 3, 'C', 'i', ' ', 4, 'W', 'a', 'n', ' ', 5, 'J', 'i', 'n', 'g', ' ', 3, 'D', 'i', ' ', 3, 'Q', 'u', ' ', 5, 'D', 'o', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'Z', 'o', 'u', ' ', 3, 'G', 'u', ' ', 3, 'L', 'a', ' ', 3, 'L', 'u', ' ', 3, 'J', 'u', ' ', 4, 'W', 'e', 'i', ' ', 4, 'J', 'u', 'n', ' ', 4, 'N', 'i', 'e', ' ', 4, 'K', 'u', 'n', ' ', 3, 'H', 'e', ' ', 3, 'P', 'u', ' ', 3, 'Z', 'i', ' ', 4, 'G', 'a', 'o', ' ', 4, 'G', 'u', 'o', ' ', 3, 'F', 'u', ' ', 4, 'L', 'u', 'n', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 5, 'C', 'h', 'o', 'u', ' ', 5, 'S', 'o', 'n', 'g', ' ', 5, 'C', 'h', 'u', 'i', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 4, 'M', 'e', 'n', ' ', 4, 'C', 'a', 'i', ' ', 3, 'B', 'a', ' ', 3, 'L', 'i', ' ', 3, 'T', 'u', ' ', 3, 'B', 'o', ' ', 4, 'H', 'a', 'n', ' ', 4, 'B', 'a', 'o', ' ', 4, 'Q', 'i', 'n', ' ', 5, 'J', 'u', 'a', 'n', ' ', 3, 'X', 'i', ' ', 4, 'Q', 'i', 'n', ' ', 3, 'D', 'i', ' ', 4, 'J', 'i', 'e', ' ', 3, 'P', 'u', ' ', 5, 'D', 'a', 'n', 'g', ' ', 4, 'J', 'i', 'n', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 4, 'T', 'a', 'i', ' ', 5, 'G', 'e', 'n', 'g', ' ', 4, 'H', 'u', 'a', ' ', 3, 'G', 'u', ' ', 5, 'L', 'i', 'n', 'g', ' ', 4, 'F', 'e', 'i', ' ', 4, 'J', 'i', 'n', ' ', 3, 'A', 'n', ' ', 5, 'W', 'a', 'n', 'g', ' ', 5, 'B', 'e', 'n', 'g', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'J', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'L', 'i', 'n', ' ', 4, 'T', 'a', 'n', ' ', 4, 'S', 'h', 'u', ' ', 5, 'T', 'i', 'a', 'n', ' ', 4, 'D', 'a', 'o', ' ', 3, 'H', 'u', ' ', 3, 'Q', 'i', ' ', 3, 'H', 'e', ' ', 4, 'C', 'u', 'i', ' ', 4, 'T', 'a', 'o', ' ', 5, 'C', 'h', 'u', 'n', ' ', 4, 'B', 'e', 'i', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 5, 'H', 'u', 'a', 'n', ' ', 4, 'F', 'e', 'i', ' ', 4, 'L', 'a', 'i', ' ', 3, 'Q', 'i', ' ', 5, 'M', 'e', 'n', 'g', ' ', 5, 'P', 'i', 'n', 'g', ' ', 4, 'W', 'e', 'i', ' ', 4, 'D', 'a', 'n', ' ', 4, 'S', 'h', 'a', ' ', 5, 'H', 'u', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 5, 'T', 'i', 'a', 'o', ' ', 3, 'Q', 'i', ' ', 4, 'W', 'a', 'n', ' ', 3, 'C', 'e', ' ', 4, 'N', 'a', 'i', ' ', 11, 'K', 'u', 't', 'a', 'b', 'i', 'r', 'e', 'r', 'u', ' ', 4, 'T', 'u', 'o', ' ', 4, 'J', 'i', 'u', ' ', 4, 'T', 'i', 'e', ' ', 4, 'L', 'u', 'o', ' ', 5, 'M', 'e', 'n', 'g', ' ', 5, 'Y', 'a', 'j', 'i', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'o', ' ', 3, 'S', 'a', ' ', 4, 'Q', 'i', 'u', ' ', 3, 'K', 'e', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 4, 'W', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 3, 'Y', 'u', ' ', 3, 'F', 'u', ' ', 5, 'L', 'i', 'a', 'n', ' ', 5, 'X', 'u', 'a', 'n', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'N', 'a', 'n', ' ', 3, 'Z', 'e', ' ', 3, 'W', 'o', ' ', 5, 'C', 'h', 'u', 'n', ' ', 5, 'X', 'i', 'a', 'o', ' ', 3, 'Y', 'u', ' ', 5, 'P', 'i', 'a', 'n', ' ', 4, 'M', 'a', 'o', ' ', 3, 'A', 'n', ' ', 2, 'E', ' ', 4, 'L', 'u', 'o', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'H', 'u', 'o', ' ', 4, 'G', 'u', 'a', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 5, 'M', 'i', 'a', 'n', ' ', 4, 'Z', 'u', 'o', ' ', 4, 'Z', 'u', 'o', ' ', 3, 'J', 'u', ' ', 4, 'B', 'a', 'o', ' ', 4, 'R', 'o', 'u', ' ', 3, 'X', 'i', ' ', 4, 'X', 'i', 'e', ' ', 3, 'A', 'n', ' ', 3, 'Q', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'F', 'u', ' ', 3, 'L', 'u', ' ', 5, 'J', 'i', 'n', 'g', ' ', 4, 'P', 'e', 'n', ' ', 5, 'F', 'e', 'n', 'g', ' ', 5, 'H', 'o', 'n', 'g', ' ', 5, 'H', 'o', 'n', 'g', ' ', 4, 'H', 'o', 'u', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'T', 'u', ' ', 4, 'Z', 'h', 'u', ' ', 3, 'Z', 'i', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 5, 'S', 'h', 'e', 'n', ' ', 3, 'G', 'e', ' ', 4, 'J', 'i', 'e', ' ', 5, 'J', 'i', 'n', 'g', ' ', 3, 'M', 'i', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 5, 'S', 'h', 'e', 'n', ' ', 3, 'P', 'u', ' ', 4, 'G', 'a', 'i', ' ', 5, 'D', 'o', 'n', 'g', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'W', 'e', 'i', ' ', 3, 'B', 'o', ' ', 4, 'W', 'e', 'i', ' ', 3, 'P', 'a', ' ', 3, 'J', 'i', ' ', 3, 'H', 'u', ' ', 5, 'Z', 'a', 'n', 'g', ' ', 4, 'J', 'i', 'a', ' ', 5, 'D', 'u', 'a', 'n', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'J', 'u', 'n', ' ', 5, 'C', 'o', 'n', 'g', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 4, 'W', 'e', 'i', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'K', 'u', 'i', ' ', 5, 'T', 'i', 'n', 'g', ' ', 4, 'H', 'u', 'n', ' ', 3, 'X', 'i', ' ', 4, 'S', 'h', 'i', ' ', 3, 'Q', 'i', ' ', 4, 'L', 'a', 'n', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 4, 'Y', 'a', 'o', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'M', 'e', 'i', ' ', 4, 'Y', 'u', 'n', ' ', 4, 'S', 'h', 'u', ' ', 3, 'D', 'i', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 5, 'G', 'u', 'a', 'n', ' ', 7, 'S', 'u', 'k', 'u', 'm', 'o', ' ', 4, 'X', 'u', 'e', ' ', 5, 'C', 'h', 'a', 'n', ' ', 4, 'K', 'a', 'i', ' ', 4, 'K', 'u', 'i', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 4, 'L', 'o', 'u', ' ', 4, 'W', 'e', 'i', ' ', 4, 'P', 'a', 'i', ' ', 4, 'S', 'o', 'u', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'S', 'h', 'i', ' ', 5, 'C', 'h', 'u', 'n', ' ', 4, 'S', 'h', 'i', ' ', 4, 'Y', 'u', 'n', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 5, 'L', 'a', 'n', 'g', ' ', 3, 'N', 'u', ' ', 5, 'M', 'e', 'n', 'g', ' ', 3, 'H', 'e', ' ', 4, 'Q', 'u', 'e', ' ', 5, 'S', 'u', 'a', 'n', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 3, 'L', 'i', ' ', 3, 'J', 'u', ' ', 3, 'X', 'i', ' ', 5, 'P', 'a', 'n', 'g', ' ', 4, 'C', 'h', 'u', ' ', 3, 'X', 'u', ' ', 3, 'T', 'u', ' ', 4, 'L', 'i', 'u', ' ', 3, 'W', 'o', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 3, 'Z', 'u', ' ', 3, 'P', 'o', ' ', 4, 'C', 'u', 'o', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'C', 'h', 'u', ' ', 3, 'Y', 'u', ' ', 5, 'K', 'u', 'a', 'i', ' ', 4, 'P', 'a', 'n', ' ', 3, 'P', 'u', ' ', 3, 'P', 'u', ' ', 3, 'N', 'a', ' ', 5, 'S', 'h', 'u', 'o', ' ', 3, 'X', 'i', ' ', 4, 'F', 'e', 'n', ' ', 4, 'Y', 'u', 'n', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'J', 'i', ' ', 4, 'R', 'u', 'o', ' ', 5, 'C', 'a', 'n', 'g', ' ', 3, 'E', 'n', ' ', 3, 'M', 'i', ' ', 4, 'H', 'a', 'o', ' ', 4, 'S', 'u', 'n', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 5, 'M', 'i', 'n', 'g', ' ', 4, 'S', 'o', 'u', ' ', 3, 'X', 'u', ' ', 4, 'L', 'i', 'u', ' ', 3, 'X', 'i', ' ', 3, 'G', 'u', ' ', 5, 'L', 'a', 'n', 'g', ' ', 5, 'R', 'o', 'n', 'g', ' ', 5, 'W', 'e', 'n', 'g', ' ', 4, 'G', 'a', 'i', ' ', 4, 'C', 'u', 'o', ' ', 4, 'S', 'h', 'i', ' ', 5, 'T', 'a', 'n', 'g', ' ', 4, 'L', 'u', 'o', ' ', 3, 'R', 'u', ' ', 4, 'S', 'u', 'o', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'B', 'e', 'i', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'G', 'u', 'i', ' ', 3, 'B', 'i', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 4, 'G', 'u', 'n', ' ', 3, 'Z', 'a', ' ', 4, 'X', 'i', 'u', ' ', 3, 'C', 'e', ' ', 4, 'H', 'a', 'i', ' ', 4, 'L', 'a', 'n', ' ', 3, 'J', 'i', ' ', 3, 'L', 'i', ' ', 4, 'C', 'a', 'n', ' ', 5, 'L', 'a', 'n', 'g', ' ', 3, 'Y', 'u', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 3, 'M', 'o', ' ', 5, 'D', 'i', 'a', 'o', ' ', 5, 'T', 'i', 'a', 'o', ' ', 4, 'M', 'a', 'o', ' ', 5, 'T', 'o', 'n', 'g', ' ', 4, 'Z', 'h', 'u', ' ', 5, 'P', 'e', 'n', 'g', ' ', 3, 'A', 'n', ' ', 5, 'L', 'i', 'a', 'n', ' ', 5, 'C', 'o', 'n', 'g', ' ', 3, 'X', 'i', ' ', 5, 'P', 'i', 'n', 'g', ' ', 4, 'Q', 'i', 'u', ' ', 4, 'J', 'i', 'n', ' ', 5, 'C', 'h', 'u', 'n', ' ', 4, 'J', 'i', 'e', ' ', 4, 'W', 'e', 'i', ' ', 4, 'T', 'u', 'i', ' ', 4, 'C', 'a', 'o', ' ', 3, 'Y', 'u', ' ', 3, 'Y', 'i', ' ', 3, 'J', 'i', ' ', 5, 'L', 'i', 'a', 'o', ' ', 3, 'B', 'i', ' ', 3, 'L', 'u', ' ', 3, 'S', 'u', ' ', 3, 'B', 'u', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 4, 'L', 'u', 'o', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 4, 'M', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'L', 'i', 'n', 'g', ' ', 3, 'J', 'i', ' ', 5, 'P', 'i', 'a', 'o', ' ', 4, 'G', 'u', 'n', ' ', 4, 'H', 'a', 'n', ' ', 3, 'D', 'i', ' ', 3, 'S', 'u', ' ', 3, 'L', 'u', ' ', 4, 'S', 'h', 'e', ' ', 6, 'S', 'h', 'a', 'n', 'g', ' ', 3, 'D', 'i', ' ', 4, 'M', 'i', 'e', ' ', 4, 'X', 'u', 'n', ' ', 4, 'M', 'a', 'n', ' ', 3, 'B', 'o', ' ', 3, 'D', 'i', ' ', 4, 'C', 'u', 'o', ' ', 4, 'Z', 'h', 'e', ' ', 4, 'S', 'e', 'n', ' ', 5, 'X', 'u', 'a', 'n', ' ', 4, 'W', 'e', 'i', ' ', 3, 'H', 'u', ' ', 3, 'A', 'o', ' ', 3, 'M', 'i', ' ', 4, 'L', 'o', 'u', ' ', 3, 'C', 'u', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 4, 'C', 'a', 'i', ' ', 3, 'P', 'o', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 3, 'M', 'i', ' ', 5, 'C', 'o', 'n', 'g', ' ', 5, 'N', 'i', 'a', 'o', ' ', 4, 'H', 'u', 'i', ' ', 4, 'J', 'u', 'n', ' ', 4, 'Y', 'i', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'S', 'h', 'u', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'K', 'u', 'i', ' ', 5, 'C', 'h', 'e', 'n', ' ', 3, 'H', 'u', ' ', 4, 'S', 'h', 'a', ' ', 4, 'K', 'o', 'u', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 3, 'M', 'a', ' ', 5, 'Z', 'a', 'n', 'g', ' ', 7, 'S', 'o', 'n', 'o', 'k', 'o', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 4, 'D', 'o', 'u', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'L', 'i', 'n', ' ', 4, 'K', 'o', 'u', ' ', 3, 'A', 'i', ' ', 3, 'B', 'i', ' ', 3, 'L', 'i', ' ', 4, 'W', 'e', 'i', ' ', 3, 'J', 'i', ' ', 4, 'X', 'u', 'n', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 4, 'F', 'a', 'n', ' ', 5, 'M', 'e', 'n', 'g', ' ', 3, 'O', 'u', ' ', 5, 'C', 'h', 'a', 'n', ' ', 5, 'D', 'i', 'a', 'n', ' ', 4, 'X', 'u', 'n', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'R', 'u', 'i', ' ', 4, 'R', 'u', 'i', ' ', 4, 'L', 'e', 'i', ' ', 3, 'Y', 'u', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 4, 'C', 'h', 'u', ' ', 4, 'H', 'u', 'a', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'M', 'a', 'i', ' ', 4, 'Y', 'u', 'n', ' ', 4, 'B', 'a', 'o', ' ', 4, 'Y', 'o', 'u', ' ', 3, 'Q', 'u', ' ', 3, 'L', 'u', ' ', 4, 'R', 'a', 'o', ' ', 4, 'H', 'u', 'i', ' ', 2, 'E', ' ', 5, 'T', 'e', 'n', 'g', ' ', 4, 'F', 'e', 'i', ' ', 4, 'J', 'u', 'e', ' ', 4, 'Z', 'u', 'i', ' ', 3, 'F', 'a', ' ', 3, 'R', 'u', ' ', 4, 'F', 'e', 'n', ' ', 4, 'K', 'u', 'i', ' ', 5, 'S', 'h', 'u', 'n', ' ', 4, 'R', 'u', 'i', ' ', 3, 'Y', 'a', ' ', 3, 'X', 'u', ' ', 3, 'F', 'u', ' ', 4, 'J', 'u', 'e', ' ', 5, 'D', 'a', 'n', 'g', ' ', 3, 'W', 'u', ' ', 5, 'T', 'o', 'n', 'g', ' ', 3, 'S', 'i', ' ', 5, 'X', 'i', 'a', 'o', ' ', 3, 'X', 'i', ' ', 5, 'L', 'o', 'n', 'g', ' ', 4, 'Y', 'u', 'n', ' ', 3, 'Q', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'Y', 'u', 'n', ' ', 4, 'S', 'u', 'n', ' ', 5, 'L', 'i', 'n', 'g', ' ', 3, 'Y', 'u', ' ', 4, 'X', 'i', 'a', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 3, 'J', 'i', ' ', 5, 'H', 'o', 'n', 'g', ' ', 3, 'S', 'i', ' ', 5, 'N', 'o', 'n', 'g', ' ', 4, 'L', 'e', 'i', ' ', 5, 'X', 'u', 'a', 'n', ' ', 4, 'Y', 'u', 'n', ' ', 3, 'Y', 'u', ' ', 3, 'X', 'i', ' ', 4, 'H', 'a', 'o', ' ', 3, 'B', 'o', ' ', 4, 'H', 'a', 'o', ' ', 3, 'A', 'i', ' ', 4, 'W', 'e', 'i', ' ', 4, 'H', 'u', 'i', ' ', 4, 'W', 'e', 'i', ' ', 3, 'J', 'i', ' ', 3, 'C', 'i', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 5, 'L', 'u', 'a', 'n', ' ', 4, 'M', 'i', 'e', ' ', 3, 'Y', 'i', ' ', 5, 'L', 'e', 'n', 'g', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 4, 'C', 'a', 'n', ' ', 5, 'S', 'h', 'e', 'n', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 5, 'L', 'i', 'a', 'n', ' ', 3, 'K', 'e', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 3, 'D', 'a', ' ', 3, 'T', 'i', ' ', 5, 'T', 'a', 'n', 'g', ' ', 4, 'X', 'i', 'e', ' ', 3, 'B', 'i', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 4, 'S', 'u', 'n', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'F', 'a', 'n', ' ', 5, 'D', 'i', 'n', 'g', ' ', 4, 'J', 'i', 'e', ' ', 3, 'G', 'u', ' ', 4, 'X', 'i', 'e', ' ', 4, 'S', 'h', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'K', 'a', 'o', ' ', 5, 'H', 'o', 'n', 'g', ' ', 3, 'S', 'a', ' ', 4, 'X', 'i', 'n', ' ', 4, 'X', 'u', 'n', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'H', 'i', 'e', ' ', 4, 'S', 'o', 'u', ' ', 4, 'S', 'h', 'u', ' ', 4, 'X', 'u', 'n', ' ', 4, 'D', 'u', 'i', ' ', 4, 'P', 'i', 'n', ' ', 4, 'W', 'e', 'i', ' ', 5, 'N', 'e', 'n', 'g', ' ', 5, 'C', 'h', 'o', 'u', ' ', 4, 'M', 'a', 'i', ' ', 3, 'R', 'u', ' ', 5, 'P', 'i', 'a', 'o', ' ', 4, 'T', 'a', 'i', ' ', 3, 'Q', 'i', ' ', 4, 'Z', 'a', 'o', ' ', 5, 'C', 'h', 'e', 'n', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 3, 'E', 'r', ' ', 3, 'N', 'i', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'G', 'a', 'o', ' ', 5, 'C', 'o', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'o', ' ', 3, 'Q', 'i', ' ', 3, 'F', 'a', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'X', 'u', ' ', 4, 'K', 'u', 'i', ' ', 4, 'J', 'i', 'e', ' ', 5, 'B', 'i', 'a', 'n', ' ', 5, 'D', 'i', 'a', 'o', ' ', 3, 'M', 'i', ' ', 4, 'L', 'a', 'n', ' ', 4, 'J', 'i', 'n', ' ', 5, 'C', 'a', 'n', 'g', ' ', 5, 'M', 'i', 'a', 'o', ' ', 6, 'Q', 'i', 'o', 'n', 'g', ' ', 4, 'Q', 'i', 'e', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'O', 'u', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'S', 'u', ' ', 3, 'L', 'u', ' ', 3, 'Y', 'i', ' ', 3, 'X', 'u', ' ', 4, 'X', 'i', 'e', ' ', 3, 'L', 'i', ' ', 3, 'Y', 'i', ' ', 3, 'L', 'a', ' ', 4, 'L', 'e', 'i', ' ', 5, 'X', 'i', 'a', 'o', ' ', 3, 'D', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'B', 'e', 'i', ' ', 5, 'T', 'e', 'n', 'g', ' ', 4, 'Y', 'a', 'o', ' ', 3, 'M', 'o', ' ', 5, 'H', 'u', 'a', 'n', ' ', 5, 'P', 'i', 'a', 'o', ' ', 4, 'F', 'a', 'n', ' ', 4, 'S', 'o', 'u', ' ', 4, 'T', 'a', 'n', ' ', 4, 'T', 'u', 'i', ' ', 6, 'Q', 'i', 'o', 'n', 'g', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 4, 'W', 'e', 'i', ' ', 4, 'L', 'i', 'u', ' ', 4, 'H', 'u', 'i', ' ', 4, 'G', 'a', 'o', ' ', 4, 'Y', 'u', 'n', ' ', 3, 'L', 'i', ' ', 4, 'S', 'h', 'u', ' ', 4, 'C', 'h', 'u', ' ', 3, 'A', 'i', ' ', 4, 'L', 'i', 'n', ' ', 4, 'Z', 'a', 'o', ' ', 5, 'X', 'u', 'a', 'n', ' ', 5, 'C', 'h', 'e', 'n', ' ', 4, 'L', 'a', 'i', ' ', 4, 'H', 'u', 'o', ' ', 4, 'T', 'u', 'o', ' ', 3, 'W', 'u', ' ', 4, 'R', 'u', 'i', ' ', 4, 'R', 'u', 'i', ' ', 3, 'Q', 'i', ' ', 5, 'H', 'e', 'n', 'g', ' ', 3, 'L', 'u', ' ', 3, 'S', 'u', ' ', 4, 'T', 'u', 'i', ' ', 5, 'M', 'a', 'n', 'g', ' ', 4, 'Y', 'u', 'n', ' ', 4, 'P', 'i', 'n', ' ', 3, 'Y', 'u', ' ', 4, 'X', 'u', 'n', ' ', 3, 'J', 'i', ' ', 6, 'J', 'i', 'o', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'M', 'o', ' ', 5, 'H', 'a', 'g', 'i', ' ', 3, 'S', 'u', ' ', 6, 'J', 'i', 'o', 'n', 'g', ' ', 4, 'N', 'i', 'e', ' ', 3, 'B', 'o', ' ', 5, 'R', 'a', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 3, 'J', 'u', ' ', 5, 'L', 'i', 'a', 'n', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'Y', 'i', 'n', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 5, 'L', 'o', 'n', 'g', ' ', 5, 'T', 'o', 'n', 'g', ' ', 4, 'W', 'e', 'i', ' ', 4, 'Y', 'u', 'e', ' ', 5, 'L', 'i', 'n', 'g', ' ', 3, 'Q', 'u', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'F', 'a', 'n', ' ', 3, 'M', 'i', ' ', 4, 'L', 'a', 'n', ' ', 4, 'K', 'u', 'i', ' ', 4, 'L', 'a', 'n', ' ', 3, 'J', 'i', ' ', 5, 'D', 'a', 'n', 'g', ' ', 8, 'K', 'a', 't', 's', 'u', 'r', 'a', ' ', 4, 'L', 'e', 'i', ' ', 4, 'L', 'e', 'i', ' ', 4, 'H', 'u', 'a', ' ', 5, 'F', 'e', 'n', 'g', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'W', 'e', 'i', ' ', 4, 'K', 'u', 'i', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 5, 'H', 'u', 'a', 'i', ' ', 3, 'L', 'i', ' ', 3, 'J', 'i', ' ', 3, 'M', 'i', ' ', 4, 'L', 'e', 'i', ' ', 5, 'H', 'u', 'a', 'i', ' ', 4, 'L', 'u', 'o', ' ', 3, 'J', 'i', ' ', 4, 'K', 'u', 'i', ' ', 3, 'L', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'S', 'a', 'n', ' ', 4, 'L', 'e', 'i', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'o', ' ', 3, 'Y', 'i', ' ', 5, 'L', 'u', 'a', 'n', ' ', 4, 'M', 'e', 'n', ' ', 4, 'B', 'i', 'e', ' ', 3, 'H', 'u', ' ', 3, 'H', 'u', ' ', 3, 'L', 'u', ' ', 4, 'N', 'u', 'e', ' ', 3, 'L', 'u', ' ', 3, 'S', 'i', ' ', 5, 'X', 'i', 'a', 'o', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'C', 'h', 'u', ' ', 3, 'H', 'u', ' ', 3, 'X', 'u', ' ', 4, 'C', 'u', 'o', ' ', 3, 'F', 'u', ' ', 3, 'X', 'u', ' ', 3, 'X', 'u', ' ', 3, 'L', 'u', ' ', 3, 'H', 'u', ' ', 3, 'Y', 'u', ' ', 4, 'H', 'a', 'o', ' ', 5, 'J', 'i', 'a', 'o', ' ', 3, 'J', 'u', ' ', 4, 'G', 'u', 'o', ' ', 4, 'B', 'a', 'o', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 4, 'K', 'u', 'i', ' ', 4, 'B', 'a', 'n', ' ', 3, 'X', 'i', ' ', 4, 'S', 'h', 'u', ' ', 6, 'C', 'h', 'o', 'n', 'g', ' ', 4, 'Q', 'i', 'u', ' ', 5, 'D', 'i', 'a', 'o', ' ', 3, 'J', 'i', ' ', 4, 'Q', 'i', 'u', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 4, 'S', 'h', 'i', ' ', 3, 'D', 'i', ' ', 4, 'Z', 'h', 'e', ' ', 4, 'S', 'h', 'e', ' ', 3, 'Y', 'u', ' ', 4, 'G', 'a', 'n', ' ', 3, 'Z', 'i', ' ', 5, 'H', 'o', 'n', 'g', ' ', 4, 'H', 'u', 'i', ' ', 5, 'M', 'e', 'n', 'g', ' ', 3, 'G', 'e', ' ', 4, 'S', 'u', 'i', ' ', 4, 'X', 'i', 'a', ' ', 5, 'C', 'h', 'a', 'i', ' ', 4, 'S', 'h', 'i', ' ', 3, 'Y', 'i', ' ', 3, 'M', 'a', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 5, 'F', 'a', 'n', 'g', ' ', 2, 'E', ' ', 3, 'P', 'a', ' ', 4, 'C', 'h', 'i', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'W', 'e', 'n', ' ', 4, 'W', 'e', 'n', ' ', 4, 'R', 'u', 'i', ' ', 5, 'B', 'a', 'n', 'g', ' ', 3, 'B', 'i', ' ', 4, 'Y', 'u', 'e', ' ', 4, 'Y', 'u', 'e', ' ', 4, 'J', 'u', 'n', ' ', 3, 'Q', 'i', ' ', 4, 'R', 'a', 'n', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'Q', 'i', ' ', 5, 'T', 'i', 'a', 'n', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'J', 'u', 'e', ' ', 4, 'H', 'u', 'i', ' ', 4, 'Q', 'i', 'n', ' ', 3, 'Q', 'i', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 3, 'Y', 'a', ' ', 3, 'C', 'i', ' ', 3, 'M', 'u', ' ', 5, 'W', 'a', 'n', 'g', ' ', 4, 'F', 'e', 'n', ' ', 4, 'F', 'e', 'n', ' ', 5, 'H', 'a', 'n', 'g', ' ', 5, 'G', 'o', 'n', 'g', ' ', 4, 'Z', 'a', 'o', ' ', 3, 'F', 'u', ' ', 4, 'R', 'a', 'n', ' ', 4, 'J', 'i', 'e', ' ', 3, 'F', 'u', ' ', 4, 'C', 'h', 'i', ' ', 4, 'D', 'o', 'u', ' ', 5, 'P', 'i', 'a', 'o', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'N', 'i', ' ', 3, 'T', 'e', ' ', 4, 'Q', 'i', 'u', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'Z', 'h', 'a', ' ', 5, 'P', 'i', 'n', 'g', ' ', 4, 'C', 'h', 'i', ' ', 4, 'Y', 'o', 'u', ' ', 3, 'H', 'e', ' ', 4, 'H', 'a', 'n', ' ', 3, 'J', 'u', ' ', 3, 'L', 'i', ' ', 3, 'F', 'u', ' ', 4, 'R', 'a', 'n', ' ', 4, 'Z', 'h', 'a', ' ', 4, 'G', 'o', 'u', ' ', 3, 'P', 'i', ' ', 3, 'B', 'o', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'Z', 'h', 'u', ' ', 5, 'D', 'i', 'a', 'o', ' ', 4, 'B', 'i', 'e', ' ', 5, 'B', 'i', 'n', 'g', ' ', 3, 'G', 'u', ' ', 4, 'R', 'a', 'n', ' ', 3, 'Q', 'u', ' ', 4, 'S', 'h', 'e', ' ', 4, 'T', 'i', 'e', ' ', 5, 'L', 'i', 'n', 'g', ' ', 3, 'G', 'u', ' ', 4, 'D', 'a', 'n', ' ', 3, 'G', 'u', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 3, 'L', 'i', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 3, 'Q', 'u', ' ', 4, 'M', 'o', 'u', ' ', 3, 'G', 'e', ' ', 3, 'C', 'i', ' ', 4, 'H', 'u', 'i', ' ', 4, 'H', 'u', 'i', ' ', 5, 'M', 'a', 'n', 'g', ' ', 3, 'F', 'u', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 3, 'W', 'a', ' ', 4, 'L', 'i', 'e', ' ', 4, 'Z', 'h', 'u', ' ', 3, 'Y', 'i', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'K', 'u', 'o', ' ', 5, 'J', 'i', 'a', 'o', ' ', 3, 'L', 'i', ' ', 3, 'Y', 'i', ' ', 5, 'P', 'i', 'n', 'g', ' ', 3, 'J', 'i', ' ', 3, 'H', 'a', ' ', 4, 'S', 'h', 'e', ' ', 3, 'Y', 'i', ' ', 5, 'W', 'a', 'n', 'g', ' ', 3, 'M', 'o', ' ', 6, 'Q', 'i', 'o', 'n', 'g', ' ', 4, 'Q', 'i', 'e', ' ', 4, 'G', 'u', 'i', ' ', 5, 'G', 'o', 'n', 'g', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'M', 'a', 'n', ' ', 4, 'E', 'b', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'J', 'i', 'a', ' ', 4, 'R', 'a', 'o', ' ', 3, 'S', 'i', ' ', 3, 'Q', 'i', ' ', 5, 'X', 'i', 'n', 'g', ' ', 4, 'L', 'i', 'e', ' ', 4, 'Q', 'i', 'u', ' ', 5, 'S', 'h', 'a', 'o', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 4, 'J', 'i', 'a', ' ', 5, 'S', 'h', 'u', 'i', ' ', 4, 'C', 'h', 'e', ' ', 4, 'B', 'a', 'i', ' ', 2, 'E', ' ', 4, 'H', 'a', 'n', ' ', 4, 'S', 'h', 'u', ' ', 5, 'X', 'u', 'a', 'n', ' ', 5, 'F', 'e', 'n', 'g', ' ', 5, 'S', 'h', 'e', 'n', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 3, 'F', 'u', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'Z', 'h', 'e', ' ', 3, 'W', 'u', ' ', 3, 'F', 'u', ' ', 3, 'L', 'i', ' ', 5, 'L', 'a', 'n', 'g', ' ', 3, 'B', 'i', ' ', 4, 'C', 'h', 'u', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'J', 'i', 'e', ' ', 4, 'D', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'T', 'i', 'n', 'g', ' ', 5, 'D', 'i', 'a', 'n', ' ', 5, 'S', 'h', 'u', 'i', ' ', 4, 'H', 'u', 'i', ' ', 4, 'G', 'u', 'a', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'S', 'o', 'n', 'g', ' ', 4, 'F', 'e', 'i', ' ', 3, 'J', 'u', ' ', 3, 'M', 'i', ' ', 3, 'Q', 'i', ' ', 3, 'Q', 'i', ' ', 3, 'Y', 'u', ' ', 4, 'J', 'u', 'n', ' ', 4, 'Z', 'h', 'a', ' ', 5, 'M', 'e', 'n', 'g', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 3, 'S', 'i', ' ', 3, 'X', 'i', ' ', 4, 'L', 'u', 'n', ' ', 3, 'L', 'i', ' ', 4, 'D', 'i', 'e', ' ', 5, 'T', 'i', 'a', 'o', ' ', 4, 'T', 'a', 'o', ' ', 4, 'K', 'u', 'n', ' ', 4, 'G', 'a', 'n', ' ', 4, 'H', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 5, 'B', 'a', 'n', 'g', ' ', 4, 'F', 'e', 'i', ' ', 3, 'P', 'i', ' ', 4, 'W', 'e', 'i', ' ', 4, 'D', 'u', 'n', ' ', 3, 'Y', 'i', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 3, 'S', 'u', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'R', 'u', 'i', ' ', 3, 'N', 'i', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 4, 'W', 'e', 'i', ' ', 6, 'L', 'i', 'a', 'n', 'g', ' ', 4, 'G', 'u', 'o', ' ', 4, 'W', 'a', 'n', ' ', 5, 'D', 'o', 'n', 'g', ' ', 2, 'E', ' ', 4, 'B', 'a', 'n', ' ', 3, 'D', 'i', ' ', 5, 'W', 'a', 'n', 'g', ' ', 4, 'C', 'a', 'n', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'G', 'u', 'o', ' ', 5, 'C', 'h', 'a', 'n', ' ', 3, 'L', 'a', ' ', 3, 'K', 'e', ' ', 3, 'J', 'i', ' ', 3, 'H', 'e', ' ', 5, 'T', 'i', 'n', 'g', ' ', 4, 'M', 'a', 'i', ' ', 3, 'X', 'u', ' ', 5, 'M', 'i', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 4, 'J', 'i', 'e', ' ', 4, 'S', 'h', 'i', ' ', 5, 'X', 'u', 'a', 'n', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'B', 'i', 'a', 'n', ' ', 4, 'R', 'o', 'u', ' ', 4, 'W', 'e', 'i', ' ', 3, 'F', 'u', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'M', 'e', 'i', ' ', 4, 'W', 'e', 'i', ' ', 3, 'F', 'u', ' ', 5, 'R', 'u', 'a', 'n', ' ', 4, 'X', 'i', 'e', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'Q', 'i', 'u', ' ', 4, 'M', 'a', 'o', ' ', 4, 'X', 'i', 'a', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'S', 'h', 'i', ' ', 6, 'C', 'h', 'o', 'n', 'g', ' ', 5, 'T', 'a', 'n', 'g', ' ', 4, 'Z', 'h', 'u', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 3, 'T', 'i', ' ', 3, 'F', 'u', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'H', 'u', 'i', ' ', 5, 'M', 'e', 'n', 'g', ' ', 3, 'L', 'a', ' ', 3, 'D', 'u', ' ', 3, 'H', 'u', ' ', 4, 'Q', 'i', 'u', ' ', 4, 'D', 'i', 'e', ' ', 3, 'L', 'i', ' ', 4, 'G', 'u', 'a', ' ', 4, 'Y', 'u', 'n', ' ', 3, 'J', 'u', ' ', 4, 'N', 'a', 'n', ' ', 4, 'L', 'o', 'u', ' ', 4, 'Q', 'u', 'n', ' ', 5, 'R', 'o', 'n', 'g', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 5, 'L', 'a', 'n', 'g', ' ', 5, 'P', 'a', 'n', 'g', ' ', 3, 'S', 'i', ' ', 3, 'X', 'i', ' ', 3, 'C', 'i', ' ', 3, 'X', 'i', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 5, 'W', 'e', 'n', 'g', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'S', 'o', 'u', ' ', 4, 'B', 'a', 'n', ' ', 5, 'R', 'o', 'n', 'g', ' ', 5, 'R', 'o', 'n', 'g', ' ', 3, 'J', 'i', ' ', 3, 'W', 'u', ' ', 4, 'Q', 'i', 'u', ' ', 4, 'H', 'a', 'n', ' ', 4, 'Q', 'i', 'n', ' ', 3, 'Y', 'i', ' ', 3, 'B', 'i', ' ', 4, 'H', 'u', 'a', ' ', 5, 'T', 'a', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 3, 'D', 'u', ' ', 4, 'N', 'a', 'i', ' ', 3, 'H', 'e', ' ', 3, 'H', 'u', ' ', 4, 'H', 'u', 'i', ' ', 3, 'M', 'a', ' ', 5, 'M', 'i', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 4, 'W', 'e', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 5, 'T', 'e', 'n', 'g', ' ', 3, 'Y', 'u', ' ', 5, 'C', 'a', 'n', 'g', ' ', 3, 'S', 'o', ' ', 4, 'E', 'b', 'i', ' ', 4, 'M', 'a', 'n', ' ', 6, 'S', 'h', 'a', 'n', 'g', ' ', 4, 'Z', 'h', 'e', ' ', 4, 'C', 'a', 'o', ' ', 4, 'C', 'h', 'i', ' ', 3, 'D', 'i', ' ', 3, 'A', 'o', ' ', 3, 'L', 'u', ' ', 4, 'W', 'e', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'T', 'a', 'n', 'g', ' ', 5, 'C', 'h', 'e', 'n', ' ', 5, 'P', 'i', 'a', 'o', ' ', 3, 'Q', 'u', ' ', 3, 'P', 'i', ' ', 3, 'Y', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'L', 'u', 'o', ' ', 4, 'L', 'o', 'u', ' ', 4, 'Q', 'i', 'n', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 4, 'Y', 'i', 'n', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 6, 'S', 'h', 'u', 'a', 'i', ' ', 4, 'W', 'e', 'n', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'W', 'a', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'Z', 'h', 'e', ' ', 3, 'M', 'a', ' ', 3, 'M', 'a', ' ', 4, 'G', 'u', 'o', ' ', 4, 'L', 'i', 'u', ' ', 4, 'M', 'a', 'o', ' ', 3, 'X', 'i', ' ', 5, 'C', 'o', 'n', 'g', ' ', 3, 'L', 'i', ' ', 4, 'M', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'o', ' ', 9, 'K', 'a', 'm', 'a', 'k', 'i', 'r', 'i', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 5, 'M', 'a', 'n', 'g', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 3, 'M', 'o', ' ', 4, 'Z', 'u', 'i', ' ', 3, 'S', 'i', ' ', 4, 'Q', 'i', 'u', ' ', 3, 'T', 'e', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'P', 'e', 'n', 'g', ' ', 5, 'P', 'e', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'o', ' ', 3, 'Q', 'u', ' ', 4, 'B', 'i', 'e', ' ', 5, 'L', 'i', 'a', 'o', ' ', 4, 'P', 'a', 'n', ' ', 4, 'G', 'u', 'i', ' ', 3, 'X', 'i', ' ', 3, 'J', 'i', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 4, 'F', 'e', 'i', ' ', 4, 'L', 'a', 'o', ' ', 4, 'J', 'u', 'e', ' ', 4, 'J', 'u', 'e', ' ', 4, 'H', 'u', 'i', ' ', 4, 'Y', 'i', 'n', ' ', 5, 'C', 'h', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'o', ' ', 5, 'S', 'h', 'a', 'n', ' ', 4, 'R', 'a', 'o', ' ', 5, 'X', 'i', 'a', 'o', ' ', 4, 'M', 'o', 'u', ' ', 6, 'C', 'h', 'o', 'n', 'g', ' ', 4, 'X', 'u', 'n', ' ', 3, 'S', 'i', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 5, 'D', 'a', 'n', 'g', ' ', 3, 'L', 'i', ' ', 4, 'X', 'i', 'e', ' ', 5, 'S', 'h', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 5, 'J', 'i', 'n', 'g', ' ', 3, 'D', 'a', ' ', 5, 'C', 'h', 'a', 'n', ' ', 3, 'Q', 'i', ' ', 3, 'C', 'i', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 4, 'S', 'h', 'e', ' ', 4, 'L', 'u', 'o', ' ', 4, 'Q', 'i', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 5, 'C', 'h', 'a', 'i', ' ', 3, 'L', 'i', ' ', 3, 'Z', 'e', ' ', 5, 'X', 'u', 'a', 'n', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'Z', 'h', 'u', ' ', 3, 'Z', 'e', ' ', 4, 'X', 'i', 'e', ' ', 5, 'M', 'a', 'n', 'g', ' ', 4, 'X', 'i', 'e', ' ', 3, 'Q', 'i', ' ', 5, 'R', 'o', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'M', 'e', 'n', 'g', ' ', 4, 'H', 'a', 'o', ' ', 5, 'R', 'u', 'a', 'n', ' ', 4, 'H', 'u', 'o', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 4, 'J', 'i', 'e', ' ', 4, 'B', 'i', 'n', ' ', 3, 'H', 'e', ' ', 4, 'M', 'i', 'e', ' ', 4, 'F', 'a', 'n', ' ', 4, 'L', 'e', 'i', ' ', 4, 'J', 'i', 'e', ' ', 3, 'L', 'a', ' ', 3, 'M', 'i', ' ', 3, 'L', 'i', ' ', 5, 'C', 'h', 'u', 'n', ' ', 3, 'L', 'i', ' ', 4, 'Q', 'i', 'u', ' ', 4, 'N', 'i', 'e', ' ', 3, 'L', 'u', ' ', 3, 'D', 'u', ' ', 5, 'X', 'i', 'a', 'o', ' ', 4, 'Z', 'h', 'u', ' ', 5, 'L', 'o', 'n', 'g', ' ', 3, 'L', 'i', ' ', 5, 'L', 'o', 'n', 'g', ' ', 5, 'F', 'e', 'n', 'g', ' ', 3, 'Y', 'e', ' ', 5, 'B', 'e', 'n', 'g', ' ', 6, 'S', 'h', 'a', 'n', 'g', ' ', 3, 'G', 'u', ' ', 5, 'J', 'u', 'a', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 3, 'X', 'i', ' ', 4, 'C', 'a', 'n', ' ', 3, 'Q', 'u', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 3, 'D', 'u', ' ', 4, 'C', 'a', 'n', ' ', 4, 'M', 'a', 'n', ' ', 4, 'J', 'u', 'e', ' ', 4, 'J', 'i', 'e', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'Z', 'h', 'a', ' ', 4, 'X', 'i', 'e', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 4, 'N', 'i', 'u', ' ', 4, 'P', 'e', 'i', ' ', 3, 'N', 'u', ' ', 4, 'X', 'i', 'n', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 3, 'M', 'o', ' ', 3, 'E', 'r', ' ', 3, 'K', 'e', ' ', 4, 'M', 'i', 'e', ' ', 3, 'X', 'i', ' ', 5, 'X', 'i', 'n', 'g', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'K', 'a', 'n', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 5, 'L', 'i', 'n', 'g', ' ', 5, 'X', 'u', 'a', 'n', ' ', 4, 'S', 'h', 'u', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'T', 'o', 'n', 'g', ' ', 5, 'L', 'o', 'n', 'g', ' ', 4, 'J', 'i', 'e', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'Y', 'a', ' ', 3, 'H', 'u', ' ', 4, 'W', 'e', 'i', ' ', 4, 'D', 'a', 'o', ' ', 6, 'C', 'h', 'o', 'n', 'g', ' ', 4, 'W', 'e', 'i', ' ', 4, 'D', 'a', 'o', ' ', 5, 'Z', 'h', 'u', 'n', ' ', 5, 'H', 'e', 'n', 'g', ' ', 3, 'Q', 'u', ' ', 3, 'Y', 'i', ' ', 3, 'Y', 'i', ' ', 3, 'B', 'u', ' ', 4, 'G', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 5, 'B', 'i', 'a', 'o', ' ', 4, 'C', 'h', 'a', ' ', 3, 'Y', 'i', ' ', 5, 'S', 'h', 'a', 'n', ' ', 5, 'C', 'h', 'e', 'n', ' ', 3, 'F', 'u', ' ', 4, 'G', 'u', 'n', ' ', 4, 'F', 'e', 'n', ' ', 6, 'S', 'h', 'u', 'a', 'i', ' ', 4, 'J', 'i', 'e', ' ', 3, 'N', 'a', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 4, 'D', 'a', 'n', ' ', 3, 'R', 'i', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 4, 'X', 'i', 'e', ' ', 3, 'Q', 'i', ' ', 4, 'X', 'i', 'e', ' ', 4, 'R', 'a', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'R', 'e', 'n', ' ', 4, 'Q', 'i', 'n', ' ', 4, 'J', 'i', 'n', ' ', 4, 'J', 'u', 'n', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'M', 'e', 'i', ' ', 5, 'C', 'h', 'a', 'i', ' ', 3, 'A', 'o', ' ', 5, 'N', 'i', 'a', 'o', ' ', 4, 'H', 'u', 'i', ' ', 4, 'R', 'a', 'n', ' ', 4, 'J', 'i', 'a', ' ', 4, 'T', 'u', 'o', ' ', 5, 'L', 'i', 'n', 'g', ' ', 4, 'D', 'a', 'i', ' ', 4, 'B', 'a', 'o', ' ', 4, 'P', 'a', 'o', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'Z', 'u', 'o', ' ', 3, 'B', 'i', ' ', 5, 'S', 'h', 'a', 'o', ' ', 4, 'T', 'a', 'n', ' ', 3, 'J', 'u', ' ', 3, 'H', 'e', ' ', 4, 'S', 'h', 'u', ' ', 4, 'X', 'i', 'u', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 3, 'Y', 'i', ' ', 3, 'P', 'a', ' ', 3, 'B', 'o', ' ', 3, 'D', 'i', ' ', 3, 'W', 'a', ' ', 3, 'F', 'u', ' ', 4, 'G', 'u', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'R', 'a', 'n', ' ', 4, 'P', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 4, 'M', 'a', 'o', ' ', 4, 'T', 'u', 'o', ' ', 3, 'N', 'a', ' ', 4, 'K', 'o', 'u', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'C', 'h', 'a', 'n', ' ', 3, 'Q', 'u', ' ', 4, 'B', 'e', 'i', ' ', 4, 'G', 'u', 'n', ' ', 3, 'X', 'i', ' ', 3, 'N', 'e', ' ', 3, 'B', 'o', ' ', 5, 'H', 'o', 'r', 'o', ' ', 3, 'F', 'u', ' ', 3, 'Y', 'i', ' ', 4, 'C', 'h', 'i', ' ', 3, 'K', 'u', ' ', 4, 'R', 'e', 'n', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 4, 'J', 'i', 'a', ' ', 4, 'C', 'u', 'n', ' ', 3, 'M', 'o', ' ', 4, 'J', 'i', 'e', ' ', 3, 'E', 'r', ' ', 4, 'L', 'u', 'o', ' ', 3, 'R', 'u', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'G', 'u', 'i', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'C', 'a', 'i', ' ', 4, 'L', 'i', 'e', ' ', 10, 'K', 'a', 'm', 'i', 's', 'h', 'i', 'm', 'o', ' ', 5, 'Y', 'u', 'k', 'i', ' ', 7, 'Z', 'h', 'u', 'a', 'n', 'g', ' ', 5, 'D', 'a', 'n', 'g', ' ', 4, 'K', 'u', 'n', ' ', 4, 'K', 'e', 'n', ' ', 5, 'N', 'i', 'a', 'o', ' ', 4, 'S', 'h', 'u', ' ', 4, 'J', 'i', 'a', ' ', 4, 'K', 'u', 'n', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 3, 'L', 'i', ' ', 5, 'J', 'u', 'a', 'n', ' ', 5, 'S', 'h', 'e', 'n', ' ', 4, 'P', 'o', 'u', ' ', 3, 'G', 'e', ' ', 3, 'Y', 'i', ' ', 3, 'Y', 'u', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 4, 'L', 'i', 'u', ' ', 4, 'Q', 'i', 'u', ' ', 4, 'Q', 'u', 'n', ' ', 3, 'J', 'i', ' ', 3, 'Y', 'i', ' ', 3, 'B', 'u', ' ', 7, 'Z', 'h', 'u', 'a', 'n', 'g', ' ', 5, 'S', 'h', 'u', 'i', ' ', 4, 'S', 'h', 'a', ' ', 4, 'Q', 'u', 'n', ' ', 3, 'L', 'i', ' ', 5, 'L', 'i', 'a', 'n', ' ', 5, 'L', 'i', 'a', 'n', ' ', 3, 'K', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'F', 'o', 'u', ' ', 5, 'C', 'h', 'a', 'n', ' ', 3, 'B', 'i', ' ', 4, 'G', 'u', 'n', ' ', 4, 'T', 'a', 'o', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 5, 'L', 'i', 'n', 'g', ' ', 4, 'C', 'h', 'i', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 5, 'C', 'h', 'o', 'u', ' ', 4, 'D', 'u', 'o', ' ', 5, 'B', 'i', 'a', 'o', ' ', 6, 'L', 'i', 'a', 'n', 'g', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 4, 'P', 'e', 'i', ' ', 4, 'P', 'e', 'i', ' ', 4, 'F', 'e', 'i', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'L', 'u', 'o', ' ', 4, 'G', 'u', 'o', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'D', 'u', ' ', 3, 'X', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'J', 'u', ' ', 3, 'Q', 'i', ' ', 3, 'J', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'G', 'u', 'a', ' ', 4, 'K', 'e', 'n', ' ', 4, 'C', 'h', 'e', ' ', 3, 'T', 'i', ' ', 3, 'T', 'i', ' ', 3, 'F', 'u', ' ', 6, 'C', 'h', 'o', 'n', 'g', ' ', 4, 'X', 'i', 'e', ' ', 5, 'B', 'i', 'a', 'n', ' ', 4, 'D', 'i', 'e', ' ', 4, 'K', 'u', 'n', ' ', 5, 'D', 'u', 'a', 'n', ' ', 4, 'X', 'i', 'u', ' ', 4, 'X', 'i', 'u', ' ', 3, 'H', 'e', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'B', 'a', 'o', ' ', 4, 'B', 'a', 'o', ' ', 3, 'F', 'u', ' ', 3, 'Y', 'u', ' ', 5, 'T', 'u', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'H', 'u', 'i', ' ', 4, 'B', 'e', 'i', ' ', 4, 'C', 'h', 'u', ' ', 3, 'L', 'u', ' ', 4, 'E', 'n', 'a', ' ', 6, 'H', 'i', 't', 'o', 'e', ' ', 4, 'Y', 'u', 'n', ' ', 3, 'D', 'a', ' ', 4, 'G', 'o', 'u', ' ', 3, 'D', 'a', ' ', 5, 'H', 'u', 'a', 'i', ' ', 5, 'R', 'o', 'n', 'g', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 3, 'R', 'u', ' ', 4, 'N', 'a', 'i', ' ', 6, 'J', 'i', 'o', 'n', 'g', ' ', 4, 'S', 'u', 'o', ' ', 4, 'B', 'a', 'n', ' ', 4, 'T', 'u', 'n', ' ', 4, 'C', 'h', 'i', ' ', 5, 'S', 'a', 'n', 'g', ' ', 5, 'N', 'i', 'a', 'o', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'J', 'i', 'e', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'H', 'u', 'a', 'i', ' ', 3, 'K', 'u', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'B', 'a', 'o', ' ', 3, 'L', 'i', ' ', 4, 'Z', 'h', 'e', ' ', 4, 'S', 'h', 'i', ' ', 3, 'L', 'u', ' ', 3, 'Y', 'i', ' ', 4, 'D', 'i', 'e', ' ', 4, 'X', 'i', 'e', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'W', 'e', 'i', ' ', 5, 'B', 'i', 'a', 'o', ' ', 4, 'C', 'a', 'o', ' ', 3, 'J', 'i', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 4, 'S', 'e', 'n', ' ', 4, 'B', 'a', 'o', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 8, 'C', 'h', 'i', 'h', 'a', 'y', 'a', ' ', 3, 'P', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'Z', 'u', 'i', ' ', 3, 'J', 'i', ' ', 4, 'D', 'a', 'n', ' ', 3, 'Z', 'a', ' ', 4, 'F', 'a', 'n', ' ', 3, 'B', 'o', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 4, 'X', 'i', 'n', ' ', 4, 'B', 'i', 'e', ' ', 4, 'R', 'a', 'o', ' ', 4, 'M', 'a', 'n', ' ', 4, 'L', 'a', 'n', ' ', 3, 'A', 'o', ' ', 4, 'D', 'u', 'o', ' ', 4, 'G', 'u', 'i', ' ', 4, 'C', 'a', 'o', ' ', 4, 'S', 'u', 'i', ' ', 5, 'N', 'o', 'n', 'g', ' ', 5, 'C', 'h', 'a', 'n', ' ', 5, 'L', 'i', 'a', 'n', ' ', 3, 'B', 'i', ' ', 4, 'J', 'i', 'n', ' ', 5, 'D', 'a', 'n', 'g', ' ', 4, 'S', 'h', 'u', ' ', 4, 'T', 'a', 'n', ' ', 3, 'B', 'i', ' ', 4, 'L', 'a', 'n', ' ', 3, 'P', 'u', ' ', 3, 'R', 'u', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'S', 'h', 'u', ' ', 3, 'W', 'a', ' ', 4, 'S', 'h', 'i', ' ', 4, 'B', 'a', 'i', ' ', 4, 'X', 'i', 'e', ' ', 3, 'B', 'o', ' ', 5, 'C', 'h', 'e', 'n', ' ', 4, 'L', 'a', 'i', ' ', 5, 'L', 'o', 'n', 'g', ' ', 3, 'X', 'i', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'L', 'a', 'n', ' ', 4, 'Z', 'h', 'e', ' ', 4, 'D', 'a', 'i', ' ', 7, 'T', 'a', 's', 'u', 'k', 'i', ' ', 4, 'Z', 'a', 'n', ' ', 4, 'S', 'h', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'P', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 4, 'R', 'a', 'n', ' ', 3, 'Y', 'a', ' ', 3, 'X', 'i', ' ', 3, 'X', 'i', ' ', 4, 'Y', 'a', 'o', ' ', 5, 'F', 'e', 'n', 'g', ' ', 4, 'T', 'a', 'n', ' ', 5, 'B', 'i', 'a', 'o', ' ', 3, 'F', 'u', ' ', 3, 'B', 'a', ' ', 3, 'H', 'e', ' ', 3, 'J', 'i', ' ', 3, 'J', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'G', 'u', 'a', 'n', ' ', 5, 'B', 'i', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'G', 'u', 'i', ' ', 4, 'J', 'u', 'e', ' ', 5, 'P', 'i', 'a', 'n', ' ', 4, 'M', 'a', 'o', ' ', 3, 'M', 'i', ' ', 3, 'M', 'i', ' ', 4, 'M', 'i', 'e', ' ', 4, 'S', 'h', 'i', ' ', 3, 'S', 'i', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 4, 'L', 'u', 'o', ' ', 4, 'J', 'u', 'e', ' ', 3, 'M', 'i', ' ', 5, 'T', 'i', 'a', 'o', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'J', 'u', 'n', ' ', 3, 'X', 'i', ' ', 5, 'S', 'h', 'a', 'n', ' ', 4, 'W', 'e', 'i', ' ', 3, 'X', 'i', ' ', 5, 'T', 'i', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 4, 'L', 'a', 'n', ' ', 2, 'E', ' ', 3, 'D', 'u', ' ', 4, 'Q', 'i', 'n', ' ', 5, 'P', 'a', 'n', 'g', ' ', 3, 'J', 'i', ' ', 5, 'M', 'i', 'n', 'g', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'G', 'o', 'u', ' ', 3, 'Q', 'u', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 4, 'J', 'i', 'n', ' ', 5, 'G', 'u', 'a', 'n', ' ', 5, 'D', 'e', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'L', 'u', 'o', ' ', 3, 'Q', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'W', 'e', 'i', ' ', 4, 'J', 'u', 'e', ' ', 3, 'Q', 'u', ' ', 4, 'L', 'u', 'o', ' ', 4, 'L', 'a', 'n', ' ', 5, 'S', 'h', 'e', 'n', ' ', 3, 'D', 'i', ' ', 5, 'G', 'u', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'G', 'u', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'G', 'u', 'i', ' ', 3, 'M', 'i', ' ', 4, 'S', 'h', 'i', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 4, 'L', 'a', 'n', ' ', 4, 'J', 'u', 'e', ' ', 3, 'J', 'i', ' ', 3, 'X', 'i', ' ', 3, 'D', 'i', ' ', 5, 'T', 'i', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 4, 'G', 'o', 'u', ' ', 4, 'J', 'i', 'n', ' ', 3, 'Q', 'u', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'J', 'i', 'u', ' ', 4, 'J', 'i', 'n', ' ', 3, 'C', 'u', ' ', 4, 'J', 'u', 'e', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'C', 'h', 'a', 'o', ' ', 3, 'J', 'i', ' ', 3, 'G', 'u', ' ', 4, 'D', 'a', 'n', ' ', 4, 'Z', 'u', 'i', ' ', 3, 'D', 'i', ' ', 6, 'S', 'h', 'a', 'n', 'g', ' ', 4, 'H', 'u', 'a', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 3, 'G', 'e', ' ', 4, 'C', 'h', 'i', ' ', 4, 'J', 'i', 'e', ' ', 4, 'G', 'u', 'i', ' ', 5, 'G', 'o', 'n', 'g', ' ', 5, 'H', 'o', 'n', 'g', ' ', 4, 'J', 'i', 'e', ' ', 4, 'H', 'u', 'n', ' ', 4, 'Q', 'i', 'u', ' ', 5, 'X', 'i', 'n', 'g', ' ', 3, 'S', 'u', ' ', 3, 'N', 'i', ' ', 3, 'J', 'i', ' ', 3, 'L', 'u', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'Z', 'h', 'a', ' ', 3, 'B', 'i', ' ', 5, 'X', 'i', 'n', 'g', ' ', 3, 'H', 'u', ' ', 6, 'S', 'h', 'a', 'n', 'g', ' ', 5, 'G', 'o', 'n', 'g', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'X', 'u', 'e', ' ', 4, 'C', 'h', 'u', ' ', 3, 'X', 'i', ' ', 3, 'Y', 'i', ' ', 3, 'L', 'u', ' ', 4, 'J', 'u', 'e', ' ', 3, 'X', 'i', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'X', 'i', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'D', 'i', 'n', 'g', ' ', 3, 'F', 'u', ' ', 4, 'Q', 'i', 'u', ' ', 4, 'Q', 'i', 'u', ' ', 5, 'J', 'i', 'a', 'o', ' ', 5, 'H', 'o', 'n', 'g', ' ', 3, 'J', 'i', ' ', 4, 'F', 'a', 'n', ' ', 4, 'X', 'u', 'n', ' ', 5, 'D', 'i', 'a', 'o', ' ', 5, 'H', 'o', 'n', 'g', ' ', 4, 'C', 'h', 'a', ' ', 4, 'T', 'a', 'o', ' ', 3, 'X', 'u', ' ', 4, 'J', 'i', 'e', ' ', 3, 'Y', 'i', ' ', 4, 'R', 'e', 'n', ' ', 4, 'X', 'u', 'n', ' ', 4, 'Y', 'i', 'n', ' ', 5, 'S', 'h', 'a', 'n', ' ', 3, 'Q', 'i', ' ', 4, 'T', 'u', 'o', ' ', 3, 'J', 'i', ' ', 4, 'X', 'u', 'n', ' ', 4, 'Y', 'i', 'n', ' ', 2, 'E', ' ', 4, 'F', 'e', 'n', ' ', 3, 'Y', 'a', ' ', 4, 'Y', 'a', 'o', ' ', 5, 'S', 'o', 'n', 'g', ' ', 5, 'S', 'h', 'e', 'n', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'X', 'i', 'n', ' ', 4, 'J', 'u', 'e', ' ', 5, 'X', 'i', 'a', 'o', ' ', 3, 'N', 'e', ' ', 5, 'C', 'h', 'e', 'n', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'Z', 'h', 'i', ' ', 6, 'X', 'i', 'o', 'n', 'g', ' ', 5, 'F', 'a', 'n', 'g', ' ', 4, 'X', 'i', 'n', ' ', 5, 'C', 'h', 'a', 'o', ' ', 4, 'S', 'h', 'e', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'S', 'h', 'a', ' ', 4, 'T', 'u', 'n', ' ', 3, 'X', 'u', ' ', 3, 'Y', 'i', ' ', 3, 'Y', 'i', ' ', 3, 'S', 'u', ' ', 4, 'C', 'h', 'i', ' ', 3, 'H', 'e', ' ', 5, 'S', 'h', 'e', 'n', ' ', 3, 'H', 'e', ' ', 3, 'X', 'u', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 4, 'Z', 'h', 'u', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 4, 'G', 'o', 'u', ' ', 3, 'Z', 'i', ' ', 3, 'Z', 'i', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 3, 'G', 'u', ' ', 3, 'F', 'u', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 4, 'D', 'i', 'e', ' ', 5, 'L', 'i', 'n', 'g', ' ', 3, 'D', 'i', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 3, 'L', 'i', ' ', 4, 'N', 'a', 'o', ' ', 4, 'P', 'a', 'n', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 4, 'G', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 3, 'J', 'u', ' ', 3, 'A', 'o', ' ', 4, 'Z', 'h', 'a', ' ', 4, 'T', 'u', 'o', ' ', 3, 'Y', 'i', ' ', 3, 'Q', 'u', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 5, 'P', 'i', 'n', 'g', ' ', 3, 'B', 'i', ' ', 6, 'X', 'i', 'o', 'n', 'g', ' ', 3, 'Q', 'u', ' ', 3, 'B', 'a', ' ', 3, 'D', 'a', ' ', 3, 'Z', 'u', ' ', 4, 'T', 'a', 'o', ' ', 4, 'Z', 'h', 'u', ' ', 3, 'C', 'i', ' ', 4, 'Z', 'h', 'e', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 3, 'X', 'u', ' ', 4, 'X', 'u', 'n', ' ', 3, 'Y', 'i', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 3, 'H', 'e', ' ', 4, 'S', 'h', 'i', ' ', 4, 'C', 'h', 'a', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'S', 'h', 'i', ' ', 4, 'H', 'e', 'n', ' ', 4, 'C', 'h', 'a', ' ', 4, 'G', 'o', 'u', ' ', 4, 'G', 'u', 'i', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 4, 'H', 'u', 'i', ' ', 4, 'J', 'i', 'e', ' ', 4, 'H', 'u', 'a', ' ', 4, 'G', 'a', 'i', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 4, 'W', 'e', 'i', ' ', 5, 'S', 'h', 'e', 'n', ' ', 5, 'C', 'h', 'o', 'u', ' ', 5, 'T', 'o', 'n', 'g', ' ', 3, 'M', 'i', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 5, 'M', 'i', 'n', 'g', ' ', 2, 'E', ' ', 4, 'H', 'u', 'i', ' ', 4, 'Y', 'a', 'n', ' ', 6, 'X', 'i', 'o', 'n', 'g', ' ', 4, 'G', 'u', 'a', ' ', 3, 'E', 'r', ' ', 5, 'B', 'e', 'n', 'g', ' ', 5, 'T', 'i', 'a', 'o', ' ', 4, 'C', 'h', 'i', ' ', 4, 'L', 'e', 'i', ' ', 4, 'Z', 'h', 'u', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 4, 'K', 'u', 'a', ' ', 3, 'W', 'u', ' ', 3, 'Y', 'u', ' ', 5, 'T', 'e', 'n', 'g', ' ', 3, 'J', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'R', 'e', 'n', ' ', 3, 'S', 'u', ' ', 5, 'L', 'a', 'n', 'g', ' ', 2, 'E', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 2, 'E', ' ', 4, 'S', 'h', 'i', ' ', 5, 'T', 'i', 'n', 'g', ' ', 4, 'D', 'a', 'n', ' ', 3, 'B', 'o', ' ', 5, 'C', 'h', 'a', 'n', ' ', 4, 'Y', 'o', 'u', ' ', 5, 'H', 'e', 'n', 'g', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 4, 'Q', 'i', 'n', ' ', 5, 'S', 'h', 'u', 'a', ' ', 3, 'A', 'n', ' ', 3, 'Y', 'u', ' ', 5, 'X', 'i', 'a', 'o', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 4, 'J', 'i', 'e', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'W', 'u', ' ', 3, 'W', 'u', ' ', 4, 'G', 'a', 'o', ' ', 5, 'S', 'o', 'n', 'g', ' ', 3, 'P', 'u', ' ', 4, 'H', 'u', 'i', ' ', 5, 'J', 'i', 'n', 'g', ' ', 5, 'S', 'h', 'u', 'o', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 5, 'S', 'h', 'u', 'o', ' ', 3, 'D', 'u', ' ', 8, 'Y', 'a', 's', 'a', 's', 'h', 'i', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 5, 'S', 'h', 'u', 'i', ' ', 4, 'J', 'i', 'e', ' ', 3, 'K', 'e', ' ', 3, 'Q', 'u', ' ', 5, 'C', 'o', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'o', ' ', 4, 'S', 'u', 'i', ' ', 5, 'W', 'a', 'n', 'g', ' ', 5, 'X', 'u', 'a', 'n', ' ', 4, 'F', 'e', 'i', ' ', 4, 'C', 'h', 'i', ' ', 3, 'T', 'a', ' ', 3, 'Y', 'i', ' ', 3, 'N', 'a', ' ', 4, 'Y', 'i', 'n', ' ', 5, 'D', 'i', 'a', 'o', ' ', 3, 'P', 'i', ' ', 5, 'C', 'h', 'u', 'o', ' ', 5, 'C', 'h', 'a', 'n', ' ', 5, 'C', 'h', 'e', 'n', ' ', 5, 'Z', 'h', 'u', 'n', ' ', 3, 'J', 'i', ' ', 3, 'Q', 'i', ' ', 4, 'T', 'a', 'n', ' ', 5, 'Z', 'h', 'u', 'i', ' ', 4, 'W', 'e', 'i', ' ', 3, 'J', 'u', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'n', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 3, 'Z', 'e', ' ', 4, 'Z', 'o', 'u', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 6, 'L', 'i', 'a', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'H', 'a', 'o', ' ', 4, 'L', 'u', 'n', ' ', 5, 'S', 'h', 'e', 'n', ' ', 5, 'B', 'i', 'a', 'o', ' ', 5, 'H', 'u', 'a', 'i', ' ', 5, 'P', 'i', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 4, 'D', 'i', 'e', ' ', 3, 'X', 'u', ' ', 5, 'P', 'i', 'a', 'n', ' ', 4, 'S', 'h', 'i', ' ', 5, 'X', 'u', 'a', 'n', ' ', 4, 'S', 'h', 'i', ' ', 4, 'H', 'u', 'n', ' ', 4, 'H', 'u', 'a', ' ', 2, 'E', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 3, 'D', 'i', ' ', 4, 'X', 'i', 'e', ' ', 3, 'F', 'u', ' ', 3, 'P', 'u', ' ', 5, 'T', 'i', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'Q', 'i', ' ', 3, 'Y', 'u', ' ', 3, 'Z', 'i', ' ', 6, 'C', 'h', 'u', 'a', 'n', ' ', 3, 'X', 'i', ' ', 4, 'H', 'u', 'i', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'A', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'N', 'a', 'n', ' ', 5, 'C', 'h', 'e', 'n', ' ', 5, 'F', 'e', 'n', 'g', ' ', 4, 'Z', 'h', 'u', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'H', 'e', 'n', 'g', ' ', 5, 'X', 'u', 'a', 'n', ' ', 3, 'G', 'e', ' ', 4, 'N', 'u', 'o', ' ', 3, 'Q', 'i', ' ', 4, 'M', 'o', 'u', ' ', 3, 'Y', 'e', ' ', 4, 'W', 'e', 'i', ' ', 5, 'T', 'e', 'n', 'g', ' ', 4, 'Z', 'o', 'u', ' ', 5, 'S', 'h', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'B', 'o', ' ', 3, 'K', 'u', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 4, 'H', 'u', 'o', ' ', 3, 'G', 'e', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 3, 'M', 'i', ' ', 5, 'X', 'i', 'a', 'o', ' ', 3, 'M', 'i', ' ', 3, 'X', 'i', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 5, 'C', 'h', 'e', 'n', ' ', 4, 'N', 'u', 'e', ' ', 3, 'T', 'i', ' ', 3, 'S', 'u', ' ', 5, 'B', 'a', 'n', 'g', ' ', 4, 'C', 'h', 'i', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'S', 'h', 'i', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'X', 'i', 'e', ' ', 4, 'X', 'u', 'e', ' ', 4, 'T', 'a', 'o', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'Y', 'a', 'o', ' ', 3, 'Y', 'u', ' ', 5, 'B', 'i', 'a', 'o', ' ', 5, 'C', 'o', 'n', 'g', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 3, 'L', 'i', ' ', 3, 'M', 'o', ' ', 3, 'M', 'o', ' ', 6, 'S', 'h', 'a', 'n', 'g', ' ', 4, 'Z', 'h', 'e', ' ', 4, 'M', 'i', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'Z', 'e', ' ', 4, 'J', 'i', 'e', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'L', 'o', 'u', ' ', 4, 'C', 'a', 'n', ' ', 3, 'O', 'u', ' ', 5, 'G', 'u', 'a', 'n', ' ', 3, 'X', 'i', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 3, 'A', 'o', ' ', 3, 'A', 'o', ' ', 4, 'J', 'i', 'n', ' ', 4, 'Z', 'h', 'e', ' ', 3, 'Y', 'i', ' ', 3, 'H', 'u', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 4, 'M', 'a', 'n', ' ', 5, 'C', 'h', 'a', 'o', ' ', 4, 'H', 'a', 'n', ' ', 4, 'H', 'u', 'a', ' ', 5, 'C', 'h', 'a', 'n', ' ', 3, 'X', 'u', ' ', 5, 'Z', 'e', 'n', 'g', ' ', 3, 'S', 'e', ' ', 3, 'X', 'i', ' ', 4, 'S', 'h', 'e', ' ', 4, 'D', 'u', 'i', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 4, 'N', 'a', 'o', ' ', 4, 'L', 'a', 'n', ' ', 2, 'E', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'J', 'u', 'e', ' ', 3, 'J', 'i', ' ', 4, 'Z', 'u', 'n', ' ', 5, 'J', 'i', 'a', 'o', ' ', 3, 'B', 'o', ' ', 4, 'H', 'u', 'i', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 3, 'M', 'u', ' ', 4, 'Z', 'e', 'n', ' ', 4, 'Z', 'h', 'a', ' ', 4, 'S', 'h', 'i', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 4, 'T', 'a', 'n', ' ', 4, 'Z', 'e', 'n', ' ', 3, 'P', 'u', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 5, 'X', 'u', 'a', 'n', ' ', 4, 'Z', 'a', 'o', ' ', 4, 'T', 'a', 'n', ' ', 5, 'D', 'a', 'n', 'g', ' ', 4, 'S', 'u', 'i', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 3, 'J', 'i', ' ', 5, 'J', 'i', 'a', 'o', ' ', 5, 'J', 'i', 'n', 'g', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'N', 'o', 'u', ' ', 3, 'Y', 'i', ' ', 3, 'A', 'i', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 3, 'P', 'i', ' ', 4, 'H', 'u', 'i', ' ', 4, 'H', 'u', 'a', ' ', 3, 'Y', 'i', ' ', 3, 'Y', 'i', ' ', 5, 'S', 'h', 'a', 'n', ' ', 5, 'R', 'a', 'n', 'g', ' ', 4, 'N', 'o', 'u', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'Z', 'h', 'u', 'i', ' ', 3, 'T', 'a', ' ', 3, 'H', 'u', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 4, 'H', 'a', 'o', ' ', 3, 'Y', 'e', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'H', 'u', 'i', ' ', 3, 'D', 'u', ' ', 4, 'Z', 'h', 'e', ' ', 5, 'X', 'u', 'a', 'n', ' ', 4, 'Z', 'a', 'n', ' ', 4, 'L', 'e', 'i', ' ', 5, 'S', 'h', 'e', 'n', ' ', 4, 'W', 'e', 'i', ' ', 5, 'C', 'h', 'a', 'n', ' ', 3, 'L', 'i', ' ', 3, 'Y', 'i', ' ', 5, 'B', 'i', 'a', 'n', ' ', 4, 'Z', 'h', 'e', ' ', 4, 'Y', 'a', 'n', ' ', 2, 'E', ' ', 5, 'C', 'h', 'o', 'u', ' ', 4, 'W', 'e', 'i', ' ', 5, 'C', 'h', 'o', 'u', ' ', 4, 'Y', 'a', 'o', ' ', 5, 'C', 'h', 'a', 'n', ' ', 5, 'R', 'a', 'n', 'g', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'L', 'a', 'n', ' ', 5, 'C', 'h', 'e', 'n', ' ', 4, 'H', 'u', 'o', ' ', 4, 'Z', 'h', 'e', ' ', 5, 'H', 'u', 'a', 'n', ' ', 4, 'Z', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 5, 'D', 'a', 'n', 'g', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'D', 'u', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'J', 'i', ' ', 5, 'D', 'i', 'n', 'g', ' ', 3, 'F', 'u', ' ', 4, 'R', 'e', 'n', ' ', 3, 'J', 'i', ' ', 4, 'J', 'i', 'e', ' ', 5, 'H', 'o', 'n', 'g', ' ', 4, 'T', 'a', 'o', ' ', 5, 'R', 'a', 'n', 'g', ' ', 5, 'S', 'h', 'a', 'n', ' ', 3, 'Q', 'i', ' ', 4, 'T', 'u', 'o', ' ', 4, 'X', 'u', 'n', ' ', 3, 'Y', 'i', ' ', 4, 'X', 'u', 'n', ' ', 3, 'J', 'i', ' ', 4, 'R', 'e', 'n', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 4, 'H', 'u', 'i', ' ', 3, 'O', 'u', ' ', 3, 'J', 'u', ' ', 3, 'Y', 'a', ' ', 3, 'N', 'e', ' ', 3, 'X', 'u', ' ', 2, 'E', ' ', 4, 'L', 'u', 'n', ' ', 6, 'X', 'i', 'o', 'n', 'g', ' ', 5, 'S', 'o', 'n', 'g', ' ', 5, 'F', 'e', 'n', 'g', ' ', 4, 'S', 'h', 'e', ' ', 5, 'F', 'a', 'n', 'g', ' ', 4, 'J', 'u', 'e', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 3, 'G', 'u', ' ', 3, 'H', 'e', ' ', 5, 'P', 'i', 'n', 'g', ' ', 3, 'Z', 'u', ' ', 4, 'S', 'h', 'i', ' ', 6, 'X', 'i', 'o', 'n', 'g', ' ', 4, 'Z', 'h', 'a', ' ', 3, 'S', 'u', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 3, 'D', 'i', ' ', 4, 'Z', 'o', 'u', ' ', 3, 'C', 'i', ' ', 3, 'Q', 'u', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 3, 'B', 'i', ' ', 3, 'Y', 'i', ' ', 3, 'Y', 'i', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 4, 'L', 'e', 'i', ' ', 4, 'S', 'h', 'i', ' ', 4, 'G', 'u', 'a', ' ', 4, 'S', 'h', 'i', ' ', 4, 'J', 'i', 'e', ' ', 4, 'H', 'u', 'i', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 4, 'Z', 'h', 'u', ' ', 5, 'S', 'h', 'e', 'n', ' ', 4, 'H', 'u', 'a', ' ', 4, 'D', 'a', 'n', ' ', 4, 'G', 'o', 'u', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 4, 'G', 'u', 'i', ' ', 4, 'X', 'u', 'n', ' ', 3, 'Y', 'i', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 4, 'G', 'a', 'i', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 4, 'C', 'h', 'a', ' ', 4, 'H', 'u', 'n', ' ', 3, 'X', 'u', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 4, 'J', 'i', 'e', ' ', 3, 'W', 'u', ' ', 3, 'Y', 'u', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 3, 'W', 'u', ' ', 4, 'G', 'a', 'o', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'H', 'u', 'i', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 5, 'S', 'h', 'u', 'o', ' ', 5, 'S', 'o', 'n', 'g', ' ', 3, 'A', 'i', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'Z', 'o', 'u', ' ', 4, 'N', 'u', 'o', ' ', 3, 'D', 'u', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 4, 'F', 'e', 'i', ' ', 3, 'K', 'e', ' ', 4, 'W', 'e', 'i', ' ', 3, 'Y', 'u', ' ', 5, 'S', 'h', 'u', 'i', ' ', 5, 'S', 'h', 'e', 'n', ' ', 5, 'D', 'i', 'a', 'o', ' ', 5, 'C', 'h', 'a', 'n', ' ', 6, 'L', 'i', 'a', 'n', 'g', ' ', 5, 'Z', 'h', 'u', 'n', ' ', 4, 'S', 'u', 'i', ' ', 4, 'T', 'a', 'n', ' ', 5, 'S', 'h', 'e', 'n', ' ', 3, 'Y', 'i', ' ', 4, 'M', 'o', 'u', ' ', 5, 'C', 'h', 'e', 'n', ' ', 4, 'D', 'i', 'e', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'X', 'i', 'e', ' ', 4, 'N', 'u', 'e', ' ', 3, 'Y', 'e', ' ', 4, 'W', 'e', 'i', ' ', 2, 'E', ' ', 3, 'Y', 'u', ' ', 5, 'X', 'u', 'a', 'n', ' ', 5, 'C', 'h', 'a', 'n', ' ', 3, 'Z', 'i', ' ', 3, 'A', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'D', 'i', ' ', 3, 'M', 'i', ' ', 5, 'P', 'i', 'a', 'n', ' ', 3, 'X', 'u', ' ', 3, 'M', 'o', ' ', 5, 'D', 'a', 'n', 'g', ' ', 3, 'S', 'u', ' ', 4, 'X', 'i', 'e', ' ', 4, 'Y', 'a', 'o', ' ', 5, 'B', 'a', 'n', 'g', ' ', 4, 'S', 'h', 'i', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 3, 'M', 'i', ' ', 4, 'J', 'i', 'n', ' ', 4, 'M', 'a', 'n', ' ', 4, 'Z', 'h', 'e', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'M', 'i', 'u', ' ', 4, 'T', 'a', 'n', ' ', 4, 'Z', 'e', 'n', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 4, 'L', 'a', 'n', ' ', 3, 'P', 'u', ' ', 4, 'J', 'u', 'e', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 5, 'C', 'h', 'e', 'n', ' ', 3, 'G', 'u', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'H', 'o', 'n', 'g', ' ', 4, 'X', 'i', 'a', ' ', 4, 'J', 'u', 'e', ' ', 5, 'H', 'o', 'n', 'g', ' ', 4, 'H', 'a', 'n', ' ', 5, 'H', 'o', 'n', 'g', ' ', 3, 'X', 'i', ' ', 3, 'X', 'i', ' ', 4, 'H', 'u', 'o', ' ', 5, 'L', 'i', 'a', 'o', ' ', 4, 'H', 'a', 'n', ' ', 3, 'D', 'u', ' ', 5, 'L', 'o', 'n', 'g', ' ', 4, 'D', 'o', 'u', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 3, 'Q', 'i', ' ', 4, 'S', 'h', 'i', ' ', 3, 'L', 'i', ' ', 5, 'D', 'e', 'n', 'g', ' ', 4, 'W', 'a', 'n', ' ', 3, 'B', 'i', ' ', 4, 'S', 'h', 'u', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'F', 'e', 'n', 'g', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'S', 'h', 'i', ' ', 4, 'C', 'h', 'u', ' ', 4, 'H', 'u', 'i', ' ', 4, 'T', 'u', 'n', ' ', 3, 'Y', 'i', ' ', 4, 'T', 'u', 'n', ' ', 3, 'Y', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'B', 'a', ' ', 4, 'H', 'o', 'u', ' ', 2, 'E', ' ', 3, 'C', 'u', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 5, 'H', 'u', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'K', 'e', 'n', ' ', 4, 'G', 'a', 'i', ' ', 3, 'Q', 'u', ' ', 3, 'F', 'u', ' ', 3, 'X', 'i', ' ', 4, 'B', 'i', 'n', ' ', 4, 'H', 'a', 'o', ' ', 3, 'Y', 'u', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'J', 'i', 'a', ' ', 3, 'X', 'i', ' ', 3, 'B', 'o', ' ', 4, 'W', 'e', 'n', ' ', 5, 'H', 'u', 'a', 'n', ' ', 4, 'B', 'i', 'n', ' ', 3, 'D', 'i', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 4, 'F', 'e', 'n', ' ', 3, 'Y', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'B', 'a', 'o', ' ', 5, 'C', 'h', 'a', 'i', ' ', 4, 'H', 'a', 'n', ' ', 3, 'P', 'i', ' ', 3, 'N', 'a', ' ', 3, 'P', 'i', ' ', 4, 'G', 'o', 'u', ' ', 3, 'N', 'a', ' ', 4, 'Y', 'o', 'u', ' ', 5, 'D', 'i', 'a', 'o', ' ', 3, 'M', 'o', ' ', 3, 'S', 'i', ' ', 4, 'X', 'i', 'u', ' ', 5, 'H', 'u', 'a', 'n', ' ', 4, 'K', 'u', 'n', ' ', 3, 'H', 'e', ' ', 3, 'H', 'e', ' ', 3, 'M', 'o', ' ', 4, 'H', 'a', 'n', ' ', 4, 'M', 'a', 'o', ' ', 3, 'L', 'i', ' ', 3, 'N', 'i', ' ', 3, 'B', 'i', ' ', 3, 'Y', 'u', ' ', 4, 'J', 'i', 'a', ' ', 5, 'T', 'u', 'a', 'n', ' ', 4, 'M', 'a', 'o', ' ', 3, 'P', 'i', ' ', 3, 'X', 'i', ' ', 2, 'E', ' ', 3, 'J', 'u', ' ', 3, 'M', 'o', ' ', 4, 'C', 'h', 'u', ' ', 4, 'T', 'a', 'n', ' ', 5, 'H', 'u', 'a', 'n', ' ', 4, 'J', 'u', 'e', ' ', 4, 'B', 'e', 'i', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 3, 'F', 'u', ' ', 4, 'C', 'a', 'i', ' ', 5, 'G', 'o', 'n', 'g', ' ', 3, 'T', 'e', ' ', 3, 'Y', 'i', ' ', 5, 'H', 'a', 'n', 'g', ' ', 4, 'W', 'a', 'n', ' ', 4, 'P', 'i', 'n', ' ', 4, 'H', 'u', 'o', ' ', 4, 'F', 'a', 'n', ' ', 4, 'T', 'a', 'n', ' ', 5, 'G', 'u', 'a', 'n', ' ', 3, 'Z', 'e', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'E', 'r', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'S', 'h', 'i', ' ', 3, 'B', 'i', ' ', 3, 'Z', 'i', ' ', 3, 'E', 'r', ' ', 4, 'G', 'u', 'i', ' ', 5, 'P', 'i', 'a', 'n', ' ', 5, 'B', 'i', 'a', 'n', ' ', 4, 'M', 'a', 'i', ' ', 4, 'D', 'a', 'i', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 4, 'F', 'e', 'i', ' ', 4, 'T', 'i', 'e', ' ', 3, 'Y', 'i', ' ', 4, 'C', 'h', 'i', ' ', 4, 'M', 'a', 'o', ' ', 3, 'H', 'e', ' ', 3, 'B', 'i', ' ', 3, 'L', 'u', ' ', 4, 'R', 'e', 'n', ' ', 4, 'H', 'u', 'i', ' ', 4, 'G', 'a', 'i', ' ', 5, 'P', 'i', 'a', 'n', ' ', 3, 'Z', 'i', ' ', 4, 'J', 'i', 'a', ' ', 3, 'X', 'u', ' ', 4, 'Z', 'e', 'i', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'G', 'a', 'i', ' ', 5, 'Z', 'a', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'X', 'u', 'n', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 4, 'S', 'h', 'e', ' ', 4, 'B', 'i', 'n', ' ', 4, 'B', 'i', 'n', ' ', 4, 'Q', 'i', 'u', ' ', 4, 'S', 'h', 'e', ' ', 6, 'C', 'h', 'u', 'a', 'n', ' ', 5, 'Z', 'a', 'n', 'g', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 4, 'L', 'a', 'i', ' ', 4, 'Z', 'a', 'n', ' ', 3, 'S', 'i', ' ', 5, 'C', 'h', 'e', 'n', ' ', 6, 'S', 'h', 'a', 'n', 'g', ' ', 5, 'T', 'i', 'a', 'n', ' ', 4, 'P', 'e', 'i', ' ', 5, 'G', 'e', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'M', 'a', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'S', 'u', 'i', ' ', 3, 'F', 'u', ' ', 4, 'T', 'a', 'n', ' ', 5, 'C', 'o', 'n', 'g', ' ', 5, 'C', 'o', 'n', 'g', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'J', 'i', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 3, 'D', 'u', ' ', 4, 'J', 'i', 'n', ' ', 6, 'X', 'i', 'o', 'n', 'g', ' ', 5, 'S', 'h', 'u', 'n', ' ', 4, 'Y', 'u', 'n', ' ', 4, 'B', 'a', 'o', ' ', 4, 'Z', 'a', 'i', ' ', 4, 'L', 'a', 'i', ' ', 5, 'F', 'e', 'n', 'g', ' ', 5, 'C', 'a', 'n', 'g', ' ', 3, 'J', 'i', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 3, 'A', 'i', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 3, 'F', 'u', ' ', 4, 'G', 'o', 'u', ' ', 4, 'S', 'a', 'i', ' ', 3, 'Z', 'e', ' ', 5, 'L', 'i', 'a', 'o', ' ', 4, 'W', 'e', 'i', ' ', 4, 'B', 'a', 'i', ' ', 5, 'C', 'h', 'e', 'n', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'Z', 'h', 'u', 'i', ' ', 5, 'B', 'i', 'a', 'o', ' ', 4, 'Y', 'u', 'n', ' ', 5, 'Z', 'e', 'n', 'g', ' ', 4, 'T', 'a', 'n', ' ', 4, 'Z', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'S', 'h', 'a', 'n', ' ', 4, 'W', 'a', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'J', 'i', 'n', ' ', 4, 'G', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'Z', 'a', 'n', 'g', ' ', 3, 'B', 'i', ' ', 3, 'D', 'u', ' ', 4, 'S', 'h', 'u', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'X', 'u', 'a', 'n', ' ', 5, 'L', 'o', 'n', 'g', ' ', 4, 'G', 'a', 'n', ' ', 5, 'Z', 'a', 'n', 'g', ' ', 4, 'B', 'e', 'i', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 3, 'F', 'u', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 5, 'G', 'o', 'n', 'g', ' ', 4, 'C', 'a', 'i', ' ', 3, 'Z', 'e', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'B', 'a', 'i', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 4, 'H', 'u', 'o', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'F', 'a', 'n', ' ', 4, 'T', 'a', 'n', ' ', 4, 'P', 'i', 'n', ' ', 5, 'B', 'i', 'a', 'n', ' ', 4, 'G', 'o', 'u', ' ', 4, 'Z', 'h', 'u', ' ', 5, 'G', 'u', 'a', 'n', ' ', 3, 'E', 'r', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'B', 'i', ' ', 4, 'S', 'h', 'i', ' ', 4, 'T', 'i', 'e', ' ', 4, 'G', 'u', 'i', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 4, 'D', 'a', 'i', ' ', 4, 'M', 'a', 'o', ' ', 4, 'F', 'e', 'i', ' ', 3, 'H', 'e', ' ', 3, 'Y', 'i', ' ', 4, 'Z', 'e', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'J', 'i', 'a', ' ', 4, 'H', 'u', 'i', ' ', 3, 'Z', 'i', ' ', 4, 'R', 'e', 'n', ' ', 3, 'L', 'u', ' ', 5, 'Z', 'a', 'n', 'g', ' ', 3, 'Z', 'i', ' ', 4, 'G', 'a', 'i', ' ', 4, 'J', 'i', 'n', ' ', 4, 'Q', 'i', 'u', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 4, 'L', 'a', 'i', ' ', 4, 'S', 'h', 'e', ' ', 3, 'F', 'u', ' ', 3, 'D', 'u', ' ', 3, 'J', 'i', ' ', 4, 'S', 'h', 'u', ' ', 6, 'S', 'h', 'a', 'n', 'g', ' ', 3, 'S', 'i', ' ', 3, 'B', 'i', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 5, 'G', 'e', 'n', 'g', ' ', 4, 'P', 'e', 'i', ' ', 4, 'T', 'a', 'n', ' ', 4, 'L', 'a', 'i', ' ', 5, 'F', 'e', 'n', 'g', ' ', 5, 'Z', 'h', 'u', 'i', ' ', 3, 'F', 'u', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 4, 'S', 'a', 'i', ' ', 3, 'Z', 'e', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'Z', 'a', 'n', ' ', 4, 'Y', 'u', 'n', ' ', 5, 'Z', 'e', 'n', 'g', ' ', 5, 'S', 'h', 'a', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'G', 'a', 'n', ' ', 4, 'C', 'h', 'i', ' ', 3, 'X', 'i', ' ', 4, 'S', 'h', 'e', ' ', 4, 'N', 'a', 'n', ' ', 6, 'X', 'i', 'o', 'n', 'g', ' ', 3, 'X', 'i', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 3, 'H', 'e', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 4, 'Z', 'h', 'e', ' ', 4, 'X', 'i', 'a', ' ', 5, 'T', 'a', 'n', 'g', ' ', 4, 'Z', 'o', 'u', ' ', 4, 'Z', 'o', 'u', ' ', 3, 'L', 'i', ' ', 4, 'J', 'i', 'u', ' ', 3, 'F', 'u', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 4, 'G', 'a', 'n', ' ', 3, 'Q', 'i', ' ', 5, 'S', 'h', 'a', 'n', ' ', 6, 'Q', 'i', 'o', 'n', 'g', ' ', 4, 'Q', 'i', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'C', 'i', ' ', 4, 'J', 'u', 'e', ' ', 4, 'Q', 'i', 'n', ' ', 4, 'C', 'h', 'i', ' ', 3, 'C', 'i', ' ', 5, 'C', 'h', 'e', 'n', ' ', 5, 'C', 'h', 'e', 'n', ' ', 4, 'D', 'i', 'e', ' ', 3, 'J', 'u', ' ', 5, 'C', 'h', 'a', 'o', ' ', 3, 'D', 'i', ' ', 3, 'S', 'e', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'Y', 'u', 'e', ' ', 3, 'Q', 'u', ' ', 4, 'J', 'i', 'e', ' ', 4, 'C', 'h', 'i', ' ', 4, 'C', 'h', 'u', ' ', 4, 'G', 'u', 'a', ' ', 4, 'X', 'u', 'e', ' ', 3, 'C', 'i', ' ', 5, 'T', 'i', 'a', 'o', ' ', 4, 'D', 'u', 'o', ' ', 4, 'L', 'i', 'e', ' ', 4, 'G', 'a', 'n', ' ', 4, 'S', 'u', 'o', ' ', 3, 'C', 'u', ' ', 3, 'X', 'i', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 3, 'S', 'u', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'J', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'Q', 'u', 'e', ' ', 5, 'T', 'a', 'n', 'g', ' ', 5, 'C', 'h', 'u', 'o', ' ', 4, 'C', 'u', 'i', ' ', 3, 'L', 'u', ' ', 3, 'Q', 'u', ' ', 5, 'D', 'a', 'n', 'g', ' ', 4, 'Q', 'i', 'u', ' ', 3, 'Z', 'i', ' ', 3, 'T', 'i', ' ', 3, 'Q', 'u', ' ', 4, 'C', 'h', 'i', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'Z', 'a', 'o', ' ', 3, 'T', 'i', ' ', 4, 'Z', 'a', 'n', ' ', 4, 'Z', 'a', 'n', ' ', 3, 'Z', 'u', ' ', 3, 'P', 'a', ' ', 4, 'B', 'a', 'o', ' ', 3, 'K', 'u', ' ', 3, 'K', 'e', ' ', 4, 'D', 'u', 'n', ' ', 4, 'J', 'u', 'e', ' ', 3, 'F', 'u', ' ', 5, 'C', 'h', 'e', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'F', 'a', 'n', 'g', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'S', 'a', ' ', 4, 'Y', 'u', 'e', ' ', 3, 'P', 'a', ' ', 3, 'Q', 'i', ' ', 4, 'Y', 'u', 'e', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 4, 'T', 'u', 'o', ' ', 4, 'T', 'a', 'i', ' ', 3, 'Y', 'i', ' ', 5, 'N', 'i', 'a', 'n', ' ', 5, 'L', 'i', 'n', 'g', ' ', 4, 'M', 'e', 'i', ' ', 3, 'B', 'a', ' ', 4, 'D', 'i', 'e', ' ', 3, 'K', 'u', ' ', 4, 'T', 'u', 'o', ' ', 4, 'J', 'i', 'a', ' ', 3, 'C', 'i', ' ', 4, 'P', 'a', 'o', ' ', 4, 'Q', 'i', 'a', ' ', 4, 'Z', 'h', 'u', ' ', 3, 'J', 'u', ' ', 4, 'D', 'i', 'e', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'F', 'u', ' ', 4, 'P', 'a', 'n', ' ', 3, 'J', 'u', ' ', 5, 'S', 'h', 'a', 'n', ' ', 3, 'B', 'o', ' ', 3, 'N', 'i', ' ', 3, 'J', 'u', ' ', 3, 'L', 'i', ' ', 4, 'G', 'e', 'n', ' ', 3, 'Y', 'i', ' ', 3, 'J', 'i', ' ', 4, 'D', 'a', 'i', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'D', 'u', 'o', ' ', 4, 'Z', 'h', 'u', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 4, 'K', 'u', 'a', ' ', 6, 'Z', 'h', 'u', 'a', 'i', ' ', 4, 'G', 'u', 'i', ' ', 6, 'Q', 'i', 'o', 'n', 'g', ' ', 4, 'K', 'u', 'i', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 4, 'C', 'h', 'i', ' ', 3, 'L', 'u', ' ', 5, 'B', 'e', 'n', 'g', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'J', 'i', 'a', ' ', 5, 'T', 'i', 'a', 'o', ' ', 4, 'C', 'a', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'T', 'a', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 3, 'B', 'i', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'D', 'u', 'o', ' ', 3, 'J', 'i', ' ', 3, 'J', 'u', ' ', 3, 'J', 'i', ' ', 4, 'S', 'h', 'u', ' ', 3, 'T', 'u', ' ', 4, 'C', 'h', 'u', ' ', 5, 'J', 'i', 'n', 'g', ' ', 4, 'N', 'i', 'e', ' ', 5, 'X', 'i', 'a', 'o', ' ', 3, 'B', 'o', ' ', 4, 'C', 'h', 'i', ' ', 4, 'Q', 'u', 'n', ' ', 4, 'M', 'o', 'u', ' ', 4, 'S', 'h', 'u', ' ', 5, 'L', 'a', 'n', 'g', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'o', ' ', 5, 'C', 'h', 'o', 'u', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 3, 'T', 'a', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'Q', 'i', ' ', 3, 'W', 'o', ' ', 4, 'W', 'e', 'i', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 4, 'J', 'i', 'e', ' ', 3, 'J', 'i', ' ', 4, 'N', 'i', 'e', ' ', 3, 'J', 'u', ' ', 3, 'J', 'u', ' ', 4, 'L', 'u', 'n', ' ', 3, 'L', 'u', ' ', 5, 'L', 'e', 'n', 'g', ' ', 5, 'H', 'u', 'a', 'i', ' ', 3, 'J', 'u', ' ', 4, 'C', 'h', 'i', ' ', 4, 'W', 'a', 'n', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 3, 'T', 'i', ' ', 3, 'B', 'o', ' ', 3, 'Z', 'u', ' ', 4, 'Q', 'i', 'e', ' ', 3, 'J', 'i', ' ', 3, 'C', 'u', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 4, 'C', 'a', 'i', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 5, 'P', 'e', 'n', 'g', ' ', 4, 'Z', 'h', 'i', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 5, 'D', 'i', 'a', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'Y', 'u', ' ', 4, 'D', 'u', 'o', ' ', 4, 'D', 'u', 'n', ' ', 5, 'C', 'h', 'u', 'n', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 3, 'D', 'i', ' ', 4, 'Z', 'h', 'e', ' ', 5, 'C', 'h', 'e', 'n', ' ', 6, 'C', 'h', 'u', 'a', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'G', 'u', 'a', ' ', 5, 'T', 'a', 'n', 'g', ' ', 3, 'J', 'u', ' ', 3, 'F', 'u', ' ', 3, 'Z', 'u', ' ', 4, 'D', 'i', 'e', ' ', 5, 'P', 'i', 'a', 'n', ' ', 4, 'R', 'o', 'u', ' ', 4, 'N', 'u', 'o', ' ', 3, 'T', 'i', ' ', 4, 'C', 'h', 'a', ' ', 4, 'T', 'u', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'D', 'a', 'o', ' ', 4, 'C', 'u', 'o', ' ', 3, 'X', 'i', ' ', 3, 'T', 'a', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 5, 'D', 'i', 'a', 'n', ' ', 3, 'T', 'i', ' ', 3, 'J', 'i', ' ', 4, 'N', 'i', 'e', ' ', 4, 'M', 'a', 'n', ' ', 4, 'L', 'i', 'u', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 3, 'B', 'i', ' ', 6, 'C', 'h', 'o', 'n', 'g', ' ', 3, 'L', 'u', ' ', 5, 'L', 'i', 'a', 'o', ' ', 3, 'C', 'u', ' ', 5, 'T', 'a', 'n', 'g', ' ', 4, 'D', 'a', 'i', ' ', 4, 'S', 'u', 'o', ' ', 3, 'X', 'i', ' ', 4, 'K', 'u', 'i', ' ', 3, 'J', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 3, 'D', 'i', ' ', 4, 'M', 'a', 'n', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 5, 'L', 'i', 'a', 'n', ' ', 5, 'B', 'e', 'n', 'g', ' ', 4, 'Z', 'a', 'o', ' ', 5, 'N', 'i', 'a', 'n', ' ', 4, 'B', 'i', 'e', ' ', 4, 'T', 'u', 'i', ' ', 3, 'J', 'u', ' ', 5, 'D', 'e', 'n', 'g', ' ', 5, 'C', 'e', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'F', 'a', 'n', ' ', 4, 'C', 'h', 'u', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 4, 'D', 'u', 'n', ' ', 3, 'B', 'o', ' ', 3, 'C', 'u', ' ', 3, 'Z', 'u', ' ', 4, 'J', 'u', 'e', ' ', 4, 'J', 'u', 'e', ' ', 4, 'L', 'i', 'n', ' ', 3, 'T', 'a', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 3, 'P', 'u', ' ', 5, 'L', 'i', 'a', 'o', ' ', 4, 'D', 'u', 'n', ' ', 5, 'C', 'u', 'a', 'n', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 4, 'Z', 'a', 'o', ' ', 3, 'T', 'a', ' ', 3, 'B', 'i', ' ', 3, 'B', 'i', ' ', 4, 'Z', 'h', 'u', ' ', 3, 'J', 'u', ' ', 4, 'C', 'h', 'u', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 4, 'D', 'u', 'n', ' ', 5, 'C', 'h', 'o', 'u', ' ', 3, 'J', 'i', ' ', 3, 'W', 'u', ' ', 4, 'Y', 'u', 'e', ' ', 5, 'N', 'i', 'a', 'n', ' ', 4, 'L', 'i', 'n', ' ', 4, 'L', 'i', 'e', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'L', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'C', 'h', 'a', 'n', ' ', 4, 'C', 'h', 'u', ' ', 5, 'D', 'u', 'a', 'n', ' ', 4, 'W', 'e', 'i', ' ', 5, 'L', 'o', 'n', 'g', ' ', 4, 'L', 'i', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'W', 'e', 'i', ' ', 5, 'Z', 'u', 'a', 'n', ' ', 4, 'L', 'a', 'n', ' ', 4, 'X', 'i', 'e', ' ', 5, 'R', 'a', 'n', 'g', ' ', 4, 'X', 'i', 'e', ' ', 4, 'N', 'i', 'e', ' ', 3, 'T', 'a', ' ', 3, 'Q', 'u', ' ', 4, 'J', 'i', 'e', ' ', 5, 'C', 'u', 'a', 'n', ' ', 5, 'Z', 'u', 'a', 'n', ' ', 3, 'X', 'i', ' ', 4, 'K', 'u', 'i', ' ', 4, 'J', 'u', 'e', ' ', 4, 'L', 'i', 'n', ' ', 5, 'S', 'h', 'e', 'n', ' ', 5, 'G', 'o', 'n', 'g', ' ', 4, 'D', 'a', 'n', ' ', 7, 'S', 'e', 'g', 'a', 'r', 'e', ' ', 3, 'Q', 'u', ' ', 3, 'T', 'i', ' ', 4, 'D', 'u', 'o', ' ', 4, 'D', 'u', 'o', ' ', 5, 'G', 'o', 'n', 'g', ' ', 5, 'L', 'a', 'n', 'g', ' ', 6, 'N', 'e', 'r', 'a', 'u', ' ', 4, 'L', 'u', 'o', ' ', 3, 'A', 'i', ' ', 3, 'J', 'i', ' ', 3, 'J', 'u', ' ', 5, 'T', 'a', 'n', 'g', ' ', 7, 'U', 't', 's', 'u', 'k', 'e', ' ', 4, 'Y', 'a', 'n', ' ', 9, 'S', 'h', 'i', 't', 's', 'u', 'k', 'e', ' ', 5, 'K', 'a', 'n', 'g', ' ', 3, 'Q', 'u', ' ', 4, 'L', 'o', 'u', ' ', 4, 'L', 'a', 'o', ' ', 4, 'T', 'u', 'o', ' ', 4, 'Z', 'h', 'i', ' ', 7, 'Y', 'a', 'g', 'a', 't', 'e', ' ', 3, 'T', 'i', ' ', 4, 'D', 'a', 'o', ' ', 7, 'Y', 'a', 'g', 'a', 't', 'e', ' ', 3, 'Y', 'u', ' ', 4, 'C', 'h', 'e', ' ', 3, 'Y', 'a', ' ', 4, 'G', 'u', 'i', ' ', 4, 'J', 'u', 'n', ' ', 4, 'W', 'e', 'i', ' ', 4, 'Y', 'u', 'e', ' ', 4, 'X', 'i', 'n', ' ', 3, 'D', 'i', ' ', 5, 'X', 'u', 'a', 'n', ' ', 4, 'F', 'a', 'n', ' ', 4, 'R', 'e', 'n', ' ', 5, 'S', 'h', 'a', 'n', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 4, 'S', 'h', 'u', ' ', 4, 'T', 'u', 'n', ' ', 5, 'C', 'h', 'e', 'n', ' ', 4, 'D', 'a', 'i', ' ', 2, 'E', ' ', 3, 'N', 'a', ' ', 3, 'Q', 'i', ' ', 4, 'M', 'a', 'o', ' ', 5, 'R', 'u', 'a', 'n', ' ', 4, 'R', 'e', 'n', ' ', 4, 'F', 'a', 'n', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 5, 'H', 'o', 'n', 'g', ' ', 3, 'H', 'u', ' ', 3, 'Q', 'u', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 3, 'D', 'i', ' ', 5, 'L', 'i', 'n', 'g', ' ', 4, 'D', 'a', 'i', ' ', 3, 'A', 'o', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 4, 'F', 'a', 'n', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 4, 'A', 'n', 'g', ' ', 5, 'P', 'e', 'n', 'g', ' ', 4, 'B', 'e', 'i', ' ', 3, 'G', 'u', ' ', 3, 'K', 'u', ' ', 4, 'P', 'a', 'o', ' ', 4, 'Z', 'h', 'u', ' ', 5, 'R', 'o', 'n', 'g', ' ', 2, 'E', ' ', 3, 'B', 'a', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'Y', 'a', 'o', ' ', 3, 'K', 'e', ' ', 3, 'Y', 'i', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 4, 'S', 'h', 'i', ' ', 5, 'P', 'i', 'n', 'g', ' ', 3, 'E', 'r', ' ', 6, 'Q', 'i', 'o', 'n', 'g', ' ', 3, 'J', 'u', ' ', 5, 'J', 'i', 'a', 'o', ' ', 6, 'G', 'u', 'a', 'n', 'g', ' ', 3, 'L', 'u', ' ', 4, 'K', 'a', 'i', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 4, 'Z', 'a', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'S', 'h', 'e', ' ', 6, 'L', 'i', 'a', 'n', 'g', ' ', 3, 'Y', 'u', ' ', 5, 'S', 'h', 'a', 'o', ' ', 4, 'Y', 'o', 'u', ' ', 5, 'H', 'u', 'a', 'n', ' ', 4, 'Y', 'u', 'n', ' ', 4, 'Z', 'h', 'e', ' ', 4, 'W', 'a', 'n', ' ', 3, 'F', 'u', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 3, 'N', 'i', ' ', 5, 'L', 'i', 'n', 'g', ' ', 4, 'Z', 'h', 'e', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 6, 'L', 'i', 'a', 'n', 'g', ' ', 3, 'Z', 'i', ' ', 4, 'H', 'u', 'i', ' ', 5, 'W', 'a', 'n', 'g', ' ', 5, 'C', 'h', 'u', 'o', ' ', 4, 'G', 'u', 'o', ' ', 4, 'K', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 5, 'P', 'e', 'n', 'g', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'G', 'u', 'n', ' ', 5, 'N', 'i', 'a', 'n', ' ', 5, 'P', 'i', 'a', 'n', ' ', 5, 'G', 'u', 'a', 'n', ' ', 4, 'B', 'e', 'i', ' ', 4, 'L', 'u', 'n', ' ', 4, 'P', 'a', 'i', ' ', 6, 'L', 'i', 'a', 'n', 'g', ' ', 5, 'R', 'u', 'a', 'n', ' ', 4, 'R', 'o', 'u', ' ', 3, 'J', 'i', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'n', ' ', 6, 'C', 'h', 'u', 'a', 'n', ' ', 4, 'C', 'o', 'u', ' ', 4, 'Q', 'u', 'n', ' ', 3, 'G', 'e', ' ', 4, 'Y', 'o', 'u', ' ', 5, 'H', 'o', 'n', 'g', ' ', 4, 'S', 'h', 'u', ' ', 3, 'F', 'u', ' ', 3, 'Z', 'i', ' ', 3, 'F', 'u', ' ', 4, 'W', 'e', 'n', ' ', 4, 'B', 'e', 'n', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 4, 'W', 'e', 'n', ' ', 4, 'T', 'a', 'o', ' ', 3, 'G', 'u', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 4, 'X', 'i', 'a', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 3, 'L', 'u', ' ', 4, 'J', 'i', 'u', ' ', 5, 'C', 'h', 'a', 'o', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 4, 'W', 'e', 'i', ' ', 4, 'H', 'u', 'n', ' ', 5, 'S', 'o', 'r', 'i', ' ', 4, 'C', 'h', 'e', ' ', 5, 'J', 'i', 'a', 'o', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 3, 'P', 'u', ' ', 4, 'L', 'a', 'o', ' ', 4, 'F', 'e', 'n', ' ', 4, 'F', 'a', 'n', ' ', 4, 'L', 'i', 'n', ' ', 3, 'G', 'e', ' ', 3, 'S', 'e', ' ', 4, 'K', 'a', 'n', ' ', 5, 'H', 'u', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 3, 'J', 'i', ' ', 4, 'D', 'u', 'i', ' ', 3, 'E', 'r', ' ', 3, 'Y', 'u', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'H', 'o', 'n', 'g', ' ', 4, 'L', 'e', 'i', ' ', 4, 'P', 'e', 'i', ' ', 3, 'L', 'i', ' ', 3, 'L', 'i', ' ', 3, 'L', 'u', ' ', 4, 'L', 'i', 'n', ' ', 4, 'C', 'h', 'e', ' ', 3, 'Y', 'a', ' ', 4, 'G', 'u', 'i', ' ', 5, 'X', 'u', 'a', 'n', ' ', 3, 'D', 'i', ' ', 4, 'R', 'e', 'n', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 2, 'E', ' ', 4, 'L', 'u', 'n', ' ', 5, 'R', 'u', 'a', 'n', ' ', 5, 'H', 'o', 'n', 'g', ' ', 3, 'K', 'u', ' ', 3, 'K', 'e', ' ', 3, 'L', 'u', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'Y', 'i', ' ', 3, 'H', 'u', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 3, 'L', 'i', ' ', 4, 'Y', 'a', 'o', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 4, 'S', 'h', 'i', ' ', 4, 'Z', 'a', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'J', 'i', 'a', 'o', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 3, 'L', 'u', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'Z', 'h', 'e', ' ', 3, 'F', 'u', ' ', 6, 'L', 'i', 'a', 'n', 'g', ' ', 5, 'N', 'i', 'a', 'n', ' ', 4, 'B', 'e', 'i', ' ', 4, 'H', 'u', 'i', ' ', 4, 'G', 'u', 'n', ' ', 5, 'W', 'a', 'n', 'g', ' ', 6, 'L', 'i', 'a', 'n', 'g', ' ', 5, 'C', 'h', 'u', 'o', ' ', 3, 'Z', 'i', ' ', 4, 'C', 'o', 'u', ' ', 3, 'F', 'u', ' ', 3, 'J', 'i', ' ', 4, 'W', 'e', 'n', ' ', 4, 'S', 'h', 'u', ' ', 4, 'P', 'e', 'i', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'X', 'i', 'a', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 3, 'L', 'u', ' ', 4, 'C', 'h', 'e', ' ', 4, 'L', 'i', 'n', ' ', 4, 'X', 'i', 'n', ' ', 3, 'G', 'u', ' ', 3, 'C', 'i', ' ', 3, 'C', 'i', ' ', 3, 'P', 'i', ' ', 4, 'Z', 'u', 'i', ' ', 5, 'B', 'i', 'a', 'n', ' ', 3, 'L', 'a', ' ', 3, 'L', 'a', ' ', 3, 'C', 'i', ' ', 4, 'X', 'u', 'e', ' ', 4, 'B', 'a', 'n', ' ', 5, 'B', 'i', 'a', 'n', ' ', 5, 'B', 'i', 'a', 'n', ' ', 5, 'B', 'i', 'a', 'n', ' ', 5, 'B', 'i', 'a', 'n', ' ', 4, 'B', 'a', 'n', ' ', 3, 'C', 'i', ' ', 5, 'B', 'i', 'a', 'n', ' ', 5, 'B', 'i', 'a', 'n', ' ', 5, 'C', 'h', 'e', 'n', ' ', 3, 'R', 'u', ' ', 5, 'N', 'o', 'n', 'g', ' ', 5, 'N', 'o', 'n', 'g', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 5, 'C', 'h', 'u', 'o', ' ', 5, 'C', 'h', 'u', 'o', ' ', 7, 'S', 'u', 'b', 'e', 'r', 'u', ' ', 5, 'R', 'e', 'n', 'g', ' ', 5, 'B', 'i', 'a', 'n', ' ', 5, 'B', 'i', 'a', 'n', ' ', 4, 'S', 'i', 'p', ' ', 3, 'I', 'p', ' ', 5, 'L', 'i', 'a', 'o', ' ', 3, 'D', 'a', ' ', 5, 'C', 'h', 'a', 'n', ' ', 4, 'G', 'a', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 3, 'Y', 'u', ' ', 3, 'Q', 'i', ' ', 4, 'X', 'u', 'n', ' ', 3, 'Y', 'i', ' ', 4, 'G', 'u', 'o', ' ', 4, 'M', 'a', 'i', ' ', 3, 'Q', 'i', ' ', 3, 'Z', 'a', ' ', 5, 'W', 'a', 'n', 'g', ' ', 4, 'J', 'i', 'a', ' ', 5, 'Z', 'h', 'u', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 3, 'T', 'i', ' ', 4, 'Y', 'u', 'n', ' ', 4, 'J', 'i', 'n', ' ', 5, 'H', 'a', 'n', 'g', ' ', 3, 'Y', 'a', ' ', 4, 'F', 'a', 'n', ' ', 3, 'W', 'u', ' ', 3, 'D', 'a', ' ', 2, 'E', ' ', 5, 'H', 'u', 'a', 'n', ' ', 4, 'Z', 'h', 'e', ' ', 7, 'T', 'o', 't', 'e', 'm', 'o', ' ', 4, 'J', 'i', 'n', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'W', 'e', 'i', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'C', 'h', 'i', ' ', 4, 'C', 'h', 'e', ' ', 3, 'N', 'i', ' ', 5, 'T', 'i', 'a', 'o', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'Y', 'i', ' ', 6, 'J', 'i', 'o', 'n', 'g', ' ', 4, 'J', 'i', 'a', ' ', 5, 'C', 'h', 'e', 'n', ' ', 4, 'D', 'a', 'i', ' ', 3, 'E', 'r', ' ', 3, 'D', 'i', ' ', 3, 'P', 'o', ' ', 5, 'W', 'a', 'n', 'g', ' ', 4, 'D', 'i', 'e', ' ', 3, 'Z', 'e', ' ', 4, 'T', 'a', 'o', ' ', 4, 'S', 'h', 'u', ' ', 4, 'T', 'u', 'o', ' ', 4, 'K', 'e', 'p', ' ', 5, 'J', 'i', 'n', 'g', ' ', 4, 'H', 'u', 'i', ' ', 5, 'T', 'o', 'n', 'g', ' ', 4, 'Y', 'o', 'u', ' ', 3, 'M', 'i', ' ', 5, 'B', 'e', 'n', 'g', ' ', 3, 'J', 'i', ' ', 4, 'N', 'a', 'i', ' ', 3, 'Y', 'i', ' ', 4, 'J', 'i', 'e', ' ', 5, 'Z', 'h', 'u', 'i', ' ', 4, 'L', 'i', 'e', ' ', 4, 'X', 'u', 'n', ' ', 4, 'T', 'u', 'i', ' ', 5, 'S', 'o', 'n', 'g', ' ', 4, 'G', 'u', 'a', ' ', 4, 'T', 'a', 'o', ' ', 5, 'P', 'a', 'n', 'g', ' ', 4, 'H', 'o', 'u', ' ', 3, 'N', 'i', ' ', 4, 'D', 'u', 'n', ' ', 6, 'J', 'i', 'o', 'n', 'g', ' ', 5, 'X', 'u', 'a', 'n', ' ', 4, 'X', 'u', 'n', ' ', 3, 'B', 'u', ' ', 4, 'Y', 'o', 'u', ' ', 5, 'X', 'i', 'a', 'o', ' ', 4, 'Q', 'i', 'u', ' ', 4, 'T', 'o', 'u', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'Q', 'i', 'u', ' ', 3, 'D', 'i', ' ', 3, 'D', 'i', ' ', 3, 'T', 'u', ' ', 5, 'J', 'i', 'n', 'g', ' ', 3, 'T', 'i', ' ', 4, 'D', 'o', 'u', ' ', 3, 'Y', 'i', ' ', 4, 'Z', 'h', 'e', ' ', 5, 'T', 'o', 'n', 'g', ' ', 6, 'G', 'u', 'a', 'n', 'g', ' ', 3, 'W', 'u', ' ', 4, 'S', 'h', 'i', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 3, 'S', 'u', ' ', 4, 'Z', 'a', 'o', ' ', 4, 'Q', 'u', 'n', ' ', 5, 'F', 'e', 'n', 'g', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'S', 'u', 'o', ' ', 4, 'H', 'u', 'i', ' ', 3, 'L', 'i', ' ', 5, 'S', 'a', 'k', 'o', ' ', 4, 'L', 'a', 'i', ' ', 4, 'B', 'e', 'n', ' ', 4, 'C', 'u', 'o', ' ', 4, 'J', 'u', 'e', ' ', 5, 'B', 'e', 'n', 'g', ' ', 5, 'H', 'u', 'a', 'n', ' ', 4, 'D', 'a', 'i', ' ', 3, 'L', 'u', ' ', 4, 'Y', 'o', 'u', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 4, 'J', 'i', 'n', ' ', 3, 'Y', 'u', ' ', 5, 'C', 'h', 'u', 'o', ' ', 4, 'K', 'u', 'i', ' ', 4, 'W', 'e', 'i', ' ', 3, 'T', 'i', ' ', 3, 'Y', 'i', ' ', 3, 'D', 'a', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'L', 'u', 'o', ' ', 3, 'B', 'i', ' ', 4, 'N', 'u', 'o', ' ', 3, 'Y', 'u', ' ', 5, 'D', 'a', 'n', 'g', ' ', 4, 'S', 'u', 'i', ' ', 4, 'D', 'u', 'n', ' ', 4, 'S', 'u', 'i', ' ', 4, 'Y', 'a', 'n', ' ', 6, 'C', 'h', 'u', 'a', 'n', ' ', 4, 'C', 'h', 'i', ' ', 3, 'T', 'i', ' ', 3, 'Y', 'u', ' ', 4, 'S', 'h', 'i', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'Y', 'u', 'n', ' ', 2, 'E', ' ', 5, 'B', 'i', 'a', 'n', ' ', 4, 'G', 'u', 'o', ' ', 2, 'E', ' ', 4, 'X', 'i', 'a', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 4, 'Q', 'i', 'u', ' ', 4, 'D', 'a', 'o', ' ', 3, 'D', 'a', ' ', 4, 'W', 'e', 'i', ' ', 7, 'A', 'p', 'p', 'a', 'r', 'e', ' ', 3, 'Y', 'i', ' ', 4, 'G', 'o', 'u', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'C', 'h', 'u', ' ', 4, 'L', 'i', 'u', ' ', 4, 'X', 'u', 'n', ' ', 3, 'T', 'a', ' ', 3, 'D', 'i', ' ', 4, 'C', 'h', 'i', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 3, 'S', 'u', ' ', 3, 'T', 'a', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'Y', 'a', 'o', ' ', 5, 'G', 'u', 'a', 'n', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 3, 'A', 'o', ' ', 4, 'S', 'h', 'i', ' ', 3, 'C', 'e', ' ', 4, 'C', 'h', 'i', ' ', 3, 'S', 'u', ' ', 4, 'Z', 'a', 'o', ' ', 4, 'Z', 'h', 'e', ' ', 4, 'D', 'u', 'n', ' ', 3, 'D', 'i', ' ', 4, 'L', 'o', 'u', ' ', 4, 'C', 'h', 'i', ' ', 4, 'C', 'u', 'o', ' ', 4, 'L', 'i', 'n', ' ', 4, 'Z', 'u', 'n', ' ', 4, 'R', 'a', 'o', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'X', 'u', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 3, 'Y', 'i', ' ', 3, 'W', 'u', ' ', 5, 'L', 'i', 'a', 'o', ' ', 3, 'J', 'u', ' ', 4, 'S', 'h', 'i', ' ', 3, 'B', 'i', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'M', 'a', 'i', ' ', 4, 'X', 'i', 'e', ' ', 4, 'S', 'u', 'i', ' ', 5, 'H', 'u', 'a', 'n', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 5, 'T', 'e', 'n', 'g', ' ', 3, 'E', 'r', ' ', 5, 'M', 'i', 'a', 'o', ' ', 5, 'B', 'i', 'a', 'n', ' ', 5, 'B', 'i', 'a', 'n', ' ', 3, 'L', 'a', ' ', 3, 'L', 'i', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'L', 'u', 'o', ' ', 3, 'L', 'i', ' ', 3, 'Y', 'i', ' ', 5, 'T', 'i', 'n', 'g', ' ', 5, 'D', 'e', 'n', 'g', ' ', 3, 'Q', 'i', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 5, 'S', 'h', 'a', 'n', ' ', 4, 'H', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 5, 'M', 'a', 'n', 'g', ' ', 3, 'R', 'u', ' ', 6, 'Q', 'i', 'o', 'n', 'g', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 3, 'F', 'u', ' ', 5, 'K', 'a', 'n', 'g', ' ', 4, 'B', 'i', 'n', ' ', 5, 'F', 'a', 'n', 'g', ' ', 5, 'X', 'i', 'n', 'g', ' ', 3, 'N', 'a', ' ', 4, 'X', 'i', 'n', ' ', 5, 'S', 'h', 'e', 'n', ' ', 5, 'B', 'a', 'n', 'g', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'C', 'u', 'n', ' ', 4, 'H', 'u', 'o', ' ', 4, 'X', 'i', 'e', ' ', 5, 'B', 'a', 'n', 'g', ' ', 3, 'W', 'u', ' ', 3, 'J', 'u', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'H', 'a', 'n', ' ', 4, 'T', 'a', 'i', ' ', 4, 'Q', 'i', 'u', ' ', 3, 'B', 'i', ' ', 4, 'P', 'e', 'i', ' ', 5, 'B', 'i', 'n', 'g', ' ', 5, 'S', 'h', 'a', 'o', ' ', 4, 'B', 'e', 'i', ' ', 3, 'W', 'a', ' ', 3, 'D', 'i', ' ', 4, 'Z', 'o', 'u', ' ', 3, 'Y', 'e', ' ', 4, 'L', 'i', 'n', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 4, 'G', 'u', 'i', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'S', 'h', 'i', ' ', 3, 'K', 'u', ' ', 3, 'Y', 'u', ' ', 4, 'G', 'a', 'i', ' ', 3, 'G', 'e', ' ', 3, 'X', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'J', 'i', ' ', 4, 'X', 'u', 'n', ' ', 4, 'H', 'o', 'u', ' ', 5, 'X', 'i', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'o', ' ', 3, 'X', 'i', ' ', 4, 'G', 'u', 'i', ' ', 4, 'N', 'u', 'o', ' ', 5, 'L', 'a', 'n', 'g', ' ', 4, 'J', 'i', 'a', ' ', 5, 'K', 'u', 'a', 'i', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 6, 'O', 't', 'o', 'k', 'o', ' ', 4, 'Y', 'u', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 4, 'D', 'o', 'u', ' ', 4, 'C', 'h', 'i', ' ', 3, 'L', 'u', ' ', 3, 'F', 'u', ' ', 3, 'W', 'u', ' ', 3, 'F', 'u', ' ', 4, 'G', 'a', 'o', ' ', 4, 'H', 'a', 'o', ' ', 5, 'L', 'a', 'n', 'g', ' ', 4, 'J', 'i', 'a', ' ', 5, 'G', 'e', 'n', 'g', ' ', 4, 'J', 'u', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 3, 'B', 'o', ' ', 3, 'X', 'i', ' ', 4, 'B', 'e', 'i', ' ', 3, 'L', 'i', ' ', 4, 'Y', 'u', 'n', ' ', 3, 'B', 'u', ' ', 5, 'X', 'i', 'a', 'o', ' ', 3, 'Q', 'i', ' ', 3, 'P', 'i', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 4, 'G', 'u', 'o', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 4, 'T', 'a', 'n', ' ', 4, 'Z', 'o', 'u', ' ', 5, 'P', 'i', 'n', 'g', ' ', 4, 'L', 'a', 'i', ' ', 3, 'N', 'i', ' ', 5, 'C', 'h', 'e', 'n', ' ', 4, 'Y', 'o', 'u', ' ', 3, 'B', 'u', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 4, 'D', 'a', 'n', ' ', 3, 'J', 'u', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 3, 'Y', 'i', ' ', 3, 'D', 'u', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'M', 'e', 'i', ' ', 4, 'R', 'u', 'o', ' ', 4, 'B', 'e', 'i', ' ', 2, 'E', ' ', 3, 'Y', 'u', ' ', 5, 'J', 'u', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 4, 'Y', 'u', 'n', ' ', 4, 'H', 'o', 'u', ' ', 4, 'K', 'u', 'i', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 4, 'S', 'o', 'u', ' ', 5, 'T', 'a', 'n', 'g', ' ', 5, 'M', 'i', 'n', 'g', ' ', 3, 'X', 'i', ' ', 3, 'R', 'u', ' ', 4, 'C', 'h', 'u', ' ', 3, 'Z', 'i', ' ', 4, 'Z', 'o', 'u', ' ', 3, 'J', 'u', ' ', 3, 'W', 'u', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 4, 'Y', 'u', 'n', ' ', 4, 'H', 'a', 'o', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 3, 'B', 'i', ' ', 3, 'M', 'o', ' ', 5, 'C', 'h', 'a', 'o', ' ', 3, 'F', 'u', ' ', 5, 'L', 'i', 'a', 'o', ' ', 4, 'Y', 'i', 'n', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 3, 'H', 'u', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 4, 'Y', 'a', 'n', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 4, 'F', 'a', 'n', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 3, 'X', 'u', ' ', 5, 'D', 'e', 'n', 'g', ' ', 3, 'B', 'i', ' ', 4, 'X', 'i', 'n', ' ', 3, 'B', 'i', ' ', 5, 'C', 'e', 'n', 'g', ' ', 4, 'W', 'e', 'i', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 4, 'M', 'a', 'o', ' ', 5, 'S', 'h', 'a', 'n', ' ', 4, 'L', 'i', 'n', ' ', 3, 'P', 'o', ' ', 4, 'D', 'a', 'n', ' ', 5, 'M', 'e', 'n', 'g', ' ', 3, 'Y', 'e', ' ', 4, 'C', 'a', 'o', ' ', 5, 'K', 'u', 'a', 'i', ' ', 5, 'F', 'e', 'n', 'g', ' ', 5, 'M', 'e', 'n', 'g', ' ', 4, 'Z', 'o', 'u', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'Z', 'a', 'n', ' ', 5, 'C', 'h', 'a', 'n', ' ', 4, 'Y', 'o', 'u', ' ', 3, 'Q', 'i', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'C', 'h', 'a', 'n', ' ', 4, 'Z', 'a', 'n', ' ', 5, 'L', 'i', 'n', 'g', ' ', 5, 'H', 'u', 'a', 'n', ' ', 3, 'X', 'i', ' ', 5, 'F', 'e', 'n', 'g', ' ', 4, 'Z', 'a', 'n', ' ', 3, 'L', 'i', ' ', 4, 'Y', 'o', 'u', ' ', 5, 'D', 'i', 'n', 'g', ' ', 4, 'Q', 'i', 'u', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 4, 'P', 'e', 'i', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 3, 'Y', 'i', ' ', 5, 'H', 'a', 'n', 'g', ' ', 3, 'Y', 'u', ' ', 4, 'J', 'i', 'u', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'Z', 'u', 'i', ' ', 4, 'M', 'a', 'o', ' ', 4, 'D', 'a', 'n', ' ', 3, 'X', 'u', ' ', 4, 'T', 'o', 'u', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 4, 'F', 'e', 'n', ' ', 11, 'S', 'a', 'k', 'e', 'n', 'o', 'm', 'o', 't', 'o', ' ', 4, 'Y', 'u', 'n', ' ', 4, 'T', 'a', 'i', ' ', 5, 'T', 'i', 'a', 'n', ' ', 4, 'Q', 'i', 'a', ' ', 4, 'T', 'u', 'o', ' ', 4, 'Z', 'u', 'o', ' ', 4, 'H', 'a', 'n', ' ', 3, 'G', 'u', ' ', 3, 'S', 'u', ' ', 3, 'P', 'o', ' ', 5, 'C', 'h', 'o', 'u', ' ', 4, 'Z', 'a', 'i', ' ', 5, 'M', 'i', 'n', 'g', ' ', 4, 'L', 'u', 'o', ' ', 5, 'C', 'h', 'u', 'o', ' ', 5, 'C', 'h', 'o', 'u', ' ', 4, 'Y', 'o', 'u', ' ', 5, 'T', 'o', 'n', 'g', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'X', 'i', 'a', 'n', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'T', 'u', ' ', 5, 'X', 'i', 'a', 'o', ' ', 4, 'M', 'e', 'i', ' ', 3, 'K', 'u', ' ', 5, 'S', 'u', 'a', 'n', ' ', 4, 'L', 'e', 'i', ' ', 3, 'P', 'u', ' ', 4, 'Z', 'u', 'i', ' ', 4, 'H', 'a', 'i', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'X', 'i', ' ', 6, 'N', 'i', 'a', 'n', 'g', ' ', 4, 'W', 'e', 'i', ' ', 3, 'L', 'u', ' ', 4, 'L', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'T', 'a', 'o', ' ', 4, 'P', 'e', 'i', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 5, 'C', 'h', 'u', 'n', ' ', 4, 'T', 'a', 'n', ' ', 4, 'Z', 'u', 'i', ' ', 5, 'C', 'h', 'u', 'o', ' ', 3, 'C', 'u', ' ', 4, 'K', 'u', 'n', ' ', 3, 'T', 'i', ' ', 5, 'M', 'i', 'a', 'n', ' ', 3, 'D', 'u', ' ', 3, 'H', 'u', ' ', 3, 'X', 'u', ' ', 5, 'X', 'i', 'n', 'g', ' ', 4, 'T', 'a', 'n', ' ', 4, 'J', 'i', 'u', ' ', 5, 'C', 'h', 'u', 'n', ' ', 4, 'Y', 'u', 'n', ' ', 3, 'P', 'o', ' ', 3, 'K', 'e', ' ', 4, 'S', 'o', 'u', ' ', 3, 'M', 'i', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 5, 'C', 'h', 'o', 'u', ' ', 4, 'C', 'u', 'o', ' ', 4, 'Y', 'u', 'n', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 4, 'A', 'n', 'g', ' ', 4, 'Z', 'h', 'a', ' ', 4, 'H', 'a', 'i', ' ', 5, 'T', 'a', 'n', 'g', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 5, 'P', 'i', 'a', 'o', ' ', 5, 'S', 'h', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 3, 'L', 'i', ' ', 4, 'Z', 'a', 'o', ' ', 4, 'L', 'a', 'o', ' ', 3, 'Y', 'i', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 3, 'P', 'u', ' ', 5, 'J', 'i', 'a', 'o', ' ', 3, 'X', 'i', ' ', 4, 'T', 'a', 'n', ' ', 3, 'P', 'o', ' ', 5, 'N', 'o', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 3, 'L', 'i', ' ', 3, 'J', 'u', ' ', 5, 'J', 'i', 'a', 'o', ' ', 3, 'Y', 'i', ' ', 6, 'N', 'i', 'a', 'n', 'g', ' ', 3, 'R', 'u', ' ', 4, 'X', 'u', 'n', ' ', 5, 'C', 'h', 'o', 'u', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'L', 'i', 'n', 'g', ' ', 3, 'M', 'i', ' ', 3, 'M', 'i', ' ', 6, 'N', 'i', 'a', 'n', 'g', ' ', 4, 'X', 'i', 'n', ' ', 5, 'J', 'i', 'a', 'o', ' ', 3, 'X', 'i', ' ', 3, 'M', 'i', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'B', 'i', 'a', 'n', ' ', 4, 'C', 'a', 'i', ' ', 4, 'S', 'h', 'i', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'S', 'h', 'i', ' ', 4, 'S', 'h', 'i', ' ', 3, 'L', 'i', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 3, 'Y', 'e', ' ', 6, 'L', 'i', 'a', 'n', 'g', ' ', 3, 'L', 'i', ' ', 4, 'J', 'i', 'n', ' ', 4, 'J', 'i', 'n', ' ', 4, 'Q', 'i', 'u', ' ', 3, 'Y', 'i', ' ', 5, 'D', 'i', 'a', 'o', ' ', 4, 'D', 'a', 'o', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 5, 'D', 'i', 'n', 'g', ' ', 3, 'P', 'o', ' ', 4, 'Q', 'i', 'u', ' ', 3, 'H', 'e', ' ', 3, 'F', 'u', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'B', 'a', ' ', 5, 'L', 'u', 'a', 'n', ' ', 3, 'F', 'u', ' ', 4, 'N', 'a', 'i', ' ', 5, 'D', 'i', 'a', 'o', ' ', 5, 'S', 'h', 'a', 'n', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 4, 'K', 'o', 'u', ' ', 6, 'C', 'h', 'u', 'a', 'n', ' ', 3, 'Z', 'i', ' ', 4, 'F', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 4, 'H', 'u', 'a', ' ', 4, 'H', 'a', 'n', ' ', 5, 'G', 'o', 'n', 'g', ' ', 3, 'Q', 'i', ' ', 5, 'M', 'a', 'n', 'g', ' ', 3, 'R', 'i', ' ', 3, 'D', 'i', ' ', 3, 'S', 'i', ' ', 3, 'X', 'i', ' ', 3, 'Y', 'i', ' ', 5, 'C', 'h', 'a', 'i', ' ', 4, 'S', 'h', 'i', ' ', 3, 'T', 'u', ' ', 3, 'X', 'i', ' ', 3, 'N', 'u', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 9, 'I', 's', 'h', 'i', 'y', 'u', 'm', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'P', 'i', ' ', 3, 'Y', 'e', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'B', 'a', ' ', 5, 'F', 'a', 'n', 'g', ' ', 5, 'C', 'h', 'e', 'n', ' ', 5, 'X', 'i', 'n', 'g', ' ', 4, 'T', 'o', 'u', ' ', 4, 'Y', 'u', 'e', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'F', 'u', ' ', 3, 'P', 'i', ' ', 3, 'N', 'a', ' ', 4, 'X', 'i', 'n', ' ', 2, 'E', ' ', 4, 'J', 'u', 'e', ' ', 4, 'D', 'u', 'n', ' ', 4, 'G', 'o', 'u', ' ', 4, 'Y', 'i', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'B', 'a', 'n', ' ', 3, 'J', 'i', ' ', 4, 'R', 'e', 'n', ' ', 5, 'C', 'h', 'a', 'o', ' ', 4, 'N', 'i', 'u', ' ', 4, 'F', 'e', 'n', ' ', 4, 'Y', 'u', 'n', ' ', 3, 'J', 'i', ' ', 4, 'Q', 'i', 'n', ' ', 3, 'P', 'i', ' ', 4, 'G', 'u', 'o', ' ', 5, 'H', 'o', 'n', 'g', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'J', 'u', 'n', ' ', 4, 'S', 'h', 'i', ' ', 3, 'Y', 'i', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 4, 'N', 'i', 'e', ' ', 4, 'G', 'a', 'i', ' ', 3, 'R', 'i', ' ', 4, 'H', 'u', 'o', ' ', 4, 'T', 'a', 'i', ' ', 5, 'K', 'a', 'n', 'g', ' ', 7, 'H', 'a', 'b', 'a', 'k', 'i', ' ', 6, 'I', 'r', 'o', 'r', 'i', ' ', 6, 'N', 'g', 'a', 'a', 'k', ' ', 4, 'D', 'u', 'o', ' ', 3, 'Z', 'i', ' ', 3, 'N', 'i', ' ', 3, 'T', 'u', ' ', 4, 'S', 'h', 'i', ' ', 4, 'M', 'i', 'n', ' ', 3, 'G', 'u', ' ', 2, 'E', ' ', 5, 'L', 'i', 'n', 'g', ' ', 5, 'B', 'i', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 3, 'G', 'u', ' ', 3, 'B', 'a', ' ', 3, 'P', 'i', ' ', 3, 'Y', 'u', ' ', 3, 'S', 'i', ' ', 4, 'Z', 'u', 'o', ' ', 3, 'B', 'u', ' ', 4, 'Y', 'o', 'u', ' ', 5, 'D', 'i', 'a', 'n', ' ', 4, 'J', 'i', 'a', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 4, 'S', 'h', 'i', ' ', 4, 'S', 'h', 'i', ' ', 4, 'T', 'i', 'e', ' ', 3, 'J', 'u', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 4, 'S', 'h', 'i', ' ', 4, 'S', 'h', 'e', ' ', 5, 'X', 'u', 'a', 'n', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 4, 'B', 'a', 'o', ' ', 3, 'H', 'e', ' ', 3, 'B', 'i', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 4, 'C', 'h', 'u', ' ', 4, 'S', 'h', 'i', ' ', 3, 'B', 'o', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'C', 'h', 'i', ' ', 3, 'Z', 'a', ' ', 3, 'P', 'o', ' ', 5, 'T', 'o', 'n', 'g', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 3, 'F', 'u', ' ', 5, 'Z', 'h', 'a', 'i', ' ', 4, 'L', 'i', 'u', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 3, 'F', 'u', ' ', 3, 'L', 'i', ' ', 4, 'Y', 'u', 'e', ' ', 3, 'P', 'i', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 4, 'B', 'a', 'n', ' ', 3, 'B', 'o', ' ', 4, 'J', 'i', 'e', ' ', 4, 'G', 'o', 'u', ' ', 4, 'S', 'h', 'u', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 3, 'M', 'u', ' ', 3, 'N', 'i', ' ', 4, 'N', 'i', 'e', ' ', 3, 'D', 'i', ' ', 4, 'J', 'i', 'a', ' ', 3, 'M', 'u', ' ', 4, 'D', 'a', 'n', ' ', 5, 'S', 'h', 'e', 'n', ' ', 3, 'Y', 'i', ' ', 3, 'S', 'i', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 3, 'K', 'a', ' ', 4, 'B', 'e', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'T', 'o', 'n', 'g', ' ', 5, 'X', 'i', 'n', 'g', ' ', 5, 'H', 'o', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'C', 'h', 'i', ' ', 3, 'E', 'r', ' ', 3, 'G', 'e', ' ', 5, 'B', 'i', 'n', 'g', ' ', 4, 'S', 'h', 'i', ' ', 4, 'M', 'o', 'u', ' ', 4, 'J', 'i', 'a', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'J', 'u', 'n', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 6, 'C', 'h', 'o', 'n', 'g', ' ', 6, 'S', 'h', 'a', 'n', 'g', ' ', 5, 'T', 'o', 'n', 'g', ' ', 3, 'M', 'o', ' ', 4, 'L', 'e', 'i', ' ', 3, 'J', 'i', ' ', 3, 'Y', 'u', ' ', 3, 'X', 'u', ' ', 4, 'R', 'e', 'n', ' ', 4, 'Z', 'u', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 6, 'Q', 'i', 'o', 'n', 'g', ' ', 5, 'S', 'h', 'a', 'n', ' ', 4, 'C', 'h', 'i', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'X', 'i', 'n', 'g', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 3, 'P', 'i', ' ', 4, 'T', 'i', 'e', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'H', 'o', 'u', ' ', 5, 'M', 'i', 'n', 'g', ' ', 4, 'K', 'u', 'a', ' ', 4, 'Y', 'a', 'o', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'X', 'i', 'u', ' ', 4, 'J', 'u', 'n', ' ', 4, 'C', 'h', 'a', ' ', 4, 'L', 'a', 'o', ' ', 3, 'J', 'i', ' ', 3, 'P', 'i', ' ', 3, 'R', 'u', ' ', 3, 'M', 'i', ' ', 3, 'Y', 'i', ' ', 4, 'Y', 'i', 'n', ' ', 6, 'G', 'u', 'a', 'n', 'g', ' ', 3, 'A', 'n', ' ', 5, 'D', 'i', 'o', 'u', ' ', 4, 'Y', 'o', 'u', ' ', 3, 'S', 'e', ' ', 4, 'K', 'a', 'o', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'L', 'u', 'a', 'n', ' ', 8, 'K', 'a', 's', 'u', 'g', 'a', 'i', ' ', 3, 'A', 'i', ' ', 5, 'D', 'i', 'a', 'o', ' ', 4, 'H', 'a', 'n', ' ', 4, 'R', 'u', 'i', ' ', 4, 'S', 'h', 'i', ' ', 5, 'K', 'e', 'n', 'g', ' ', 4, 'Q', 'i', 'u', ' ', 5, 'X', 'i', 'a', 'o', ' ', 4, 'Z', 'h', 'e', ' ', 4, 'X', 'i', 'u', ' ', 5, 'Z', 'a', 'n', 'g', ' ', 3, 'T', 'i', ' ', 4, 'C', 'u', 'o', ' ', 4, 'G', 'u', 'a', ' ', 5, 'G', 'o', 'n', 'g', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 4, 'D', 'o', 'u', ' ', 3, 'L', 'u', ' ', 4, 'M', 'e', 'i', ' ', 5, 'L', 'a', 'n', 'g', ' ', 4, 'W', 'a', 'n', ' ', 4, 'X', 'i', 'n', ' ', 4, 'Y', 'u', 'n', ' ', 4, 'B', 'e', 'i', ' ', 3, 'W', 'u', ' ', 3, 'S', 'u', ' ', 3, 'Y', 'u', ' ', 5, 'C', 'h', 'a', 'n', ' ', 5, 'T', 'i', 'n', 'g', ' ', 3, 'B', 'o', ' ', 4, 'H', 'a', 'n', ' ', 4, 'J', 'i', 'a', ' ', 5, 'H', 'o', 'n', 'g', ' ', 5, 'C', 'u', 'a', 'n', ' ', 5, 'F', 'e', 'n', 'g', ' ', 5, 'C', 'h', 'a', 'n', ' ', 4, 'W', 'a', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'S', 'i', ' ', 5, 'X', 'u', 'a', 'n', ' ', 3, 'W', 'u', ' ', 3, 'W', 'u', ' ', 5, 'T', 'i', 'a', 'o', ' ', 5, 'G', 'o', 'n', 'g', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 4, 'L', 'u', 'e', ' ', 5, 'X', 'i', 'n', 'g', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'S', 'h', 'e', 'n', ' ', 4, 'H', 'a', 'n', ' ', 4, 'L', 'u', 'e', ' ', 4, 'X', 'i', 'e', ' ', 4, 'C', 'h', 'u', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 3, 'J', 'u', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'T', 'i', 'e', ' ', 5, 'M', 'a', 'n', 'g', ' ', 3, 'P', 'u', ' ', 3, 'L', 'i', ' ', 4, 'P', 'a', 'n', ' ', 4, 'R', 'u', 'i', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 4, 'G', 'a', 'o', ' ', 3, 'L', 'i', ' ', 3, 'T', 'e', ' ', 6, 'P', 'y', 'e', 'n', 'g', ' ', 4, 'Z', 'h', 'u', ' ', 3, 'T', 'u', ' ', 4, 'L', 'i', 'u', ' ', 4, 'Z', 'u', 'i', ' ', 3, 'J', 'u', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'G', 'a', 'n', 'g', ' ', 5, 'D', 'i', 'a', 'o', ' ', 4, 'T', 'a', 'o', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 4, 'L', 'u', 'n', ' ', 4, 'K', 'u', 'a', ' ', 5, 'L', 'i', 'n', 'g', ' ', 4, 'B', 'e', 'i', ' ', 3, 'L', 'u', ' ', 3, 'L', 'i', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 4, 'P', 'o', 'u', ' ', 5, 'J', 'u', 'a', 'n', ' ', 4, 'M', 'i', 'n', ' ', 4, 'Z', 'u', 'i', ' ', 5, 'P', 'e', 'n', 'g', ' ', 3, 'A', 'n', ' ', 3, 'P', 'i', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'Y', 'a', ' ', 5, 'Z', 'h', 'u', 'i', ' ', 4, 'L', 'e', 'i', ' ', 2, 'A', ' ', 5, 'K', 'o', 'n', 'g', ' ', 3, 'T', 'a', ' ', 4, 'K', 'u', 'n', ' ', 3, 'D', 'u', ' ', 4, 'W', 'e', 'i', ' ', 5, 'C', 'h', 'u', 'i', ' ', 3, 'Z', 'i', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 4, 'B', 'e', 'n', ' ', 4, 'N', 'i', 'e', ' ', 5, 'C', 'o', 'n', 'g', ' ', 4, 'Q', 'u', 'n', ' ', 4, 'T', 'a', 'n', ' ', 5, 'D', 'i', 'n', 'g', ' ', 3, 'Q', 'i', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 3, 'Q', 'i', ' ', 3, 'Y', 'u', ' ', 4, 'J', 'i', 'n', ' ', 5, 'G', 'u', 'a', 'n', ' ', 4, 'M', 'a', 'o', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 5, 'T', 'i', 'a', 'n', ' ', 3, 'X', 'i', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'T', 'a', 'o', ' ', 3, 'G', 'u', ' ', 4, 'C', 'u', 'o', ' ', 4, 'S', 'h', 'u', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 3, 'L', 'u', ' ', 5, 'M', 'e', 'n', 'g', ' ', 3, 'L', 'u', ' ', 4, 'H', 'u', 'a', ' ', 5, 'B', 'i', 'a', 'o', ' ', 3, 'G', 'a', ' ', 4, 'L', 'a', 'i', ' ', 4, 'K', 'e', 'n', ' ', 7, 'K', 'a', 'z', 'a', 'r', 'i', ' ', 3, 'B', 'u', ' ', 4, 'N', 'a', 'i', ' ', 4, 'W', 'a', 'n', ' ', 4, 'Z', 'a', 'n', ' ', 3, 'D', 'e', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'H', 'u', 'o', ' ', 6, 'L', 'i', 'a', 'n', 'g', ' ', 4, 'M', 'e', 'n', ' ', 4, 'K', 'a', 'i', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 3, 'D', 'i', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'G', 'u', 'o', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'D', 'u', ' ', 3, 'T', 'u', ' ', 4, 'W', 'e', 'i', ' ', 5, 'C', 'o', 'n', 'g', ' ', 3, 'F', 'u', ' ', 4, 'R', 'o', 'u', ' ', 3, 'J', 'i', ' ', 2, 'E', ' ', 4, 'R', 'o', 'u', ' ', 5, 'C', 'h', 'e', 'n', ' ', 3, 'T', 'i', ' ', 4, 'Z', 'h', 'a', ' ', 5, 'H', 'o', 'n', 'g', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 5, 'D', 'u', 'a', 'n', ' ', 4, 'X', 'i', 'a', ' ', 3, 'Y', 'u', ' ', 5, 'K', 'e', 'n', 'g', ' ', 5, 'X', 'i', 'n', 'g', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 4, 'W', 'e', 'i', ' ', 3, 'F', 'u', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 4, 'C', 'h', 'a', ' ', 4, 'Q', 'i', 'e', ' ', 4, 'S', 'h', 'e', ' ', 5, 'H', 'o', 'n', 'g', ' ', 4, 'K', 'u', 'i', ' ', 5, 'T', 'i', 'a', 'n', ' ', 4, 'M', 'o', 'u', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 4, 'H', 'o', 'u', ' ', 4, 'T', 'o', 'u', ' ', 5, 'C', 'o', 'n', 'g', ' ', 5, 'H', 'u', 'a', 'n', ' ', 3, 'Y', 'e', ' ', 4, 'M', 'i', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'D', 'u', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'S', 'o', 'n', 'g', ' ', 4, 'K', 'u', 'i', ' ', 3, 'H', 'u', ' ', 5, 'X', 'u', 'a', 'n', ' ', 4, 'D', 'u', 'o', ' ', 4, 'J', 'i', 'e', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 5, 'B', 'i', 'a', 'n', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 3, 'Z', 'i', ' ', 4, 'X', 'i', 'u', ' ', 3, 'Y', 'e', ' ', 4, 'M', 'e', 'i', ' ', 4, 'P', 'a', 'i', ' ', 3, 'A', 'i', ' ', 4, 'J', 'i', 'e', ' ', 4, 'M', 'e', 'i', ' ', 5, 'C', 'h', 'u', 'o', ' ', 3, 'T', 'a', ' ', 5, 'B', 'a', 'n', 'g', ' ', 4, 'X', 'i', 'a', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'S', 'u', 'o', ' ', 3, 'X', 'i', ' ', 4, 'L', 'i', 'u', ' ', 3, 'Z', 'u', ' ', 3, 'Y', 'e', ' ', 4, 'N', 'o', 'u', ' ', 5, 'W', 'e', 'n', 'g', ' ', 5, 'R', 'o', 'n', 'g', ' ', 5, 'T', 'a', 'n', 'g', ' ', 4, 'S', 'u', 'o', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 3, 'G', 'e', ' ', 5, 'S', 'h', 'u', 'o', ' ', 5, 'C', 'h', 'u', 'i', ' ', 3, 'B', 'o', ' ', 4, 'P', 'a', 'n', ' ', 3, 'S', 'a', ' ', 3, 'B', 'i', ' ', 5, 'S', 'a', 'n', 'g', ' ', 5, 'G', 'a', 'n', 'g', ' ', 3, 'Z', 'i', ' ', 3, 'W', 'u', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 5, 'T', 'i', 'a', 'o', ' ', 4, 'L', 'i', 'u', ' ', 4, 'K', 'a', 'i', ' ', 4, 'S', 'u', 'n', ' ', 4, 'S', 'h', 'a', ' ', 4, 'S', 'o', 'u', ' ', 4, 'W', 'a', 'n', ' ', 4, 'H', 'a', 'o', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 4, 'L', 'u', 'o', ' ', 3, 'Y', 'i', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 5, 'T', 'a', 'n', 'g', ' ', 4, 'N', 'i', 'e', ' ', 3, 'X', 'i', ' ', 4, 'J', 'i', 'a', ' ', 3, 'G', 'e', ' ', 3, 'M', 'a', ' ', 5, 'J', 'u', 'a', 'n', ' ', 8, 'K', 'a', 's', 'u', 'g', 'a', 'i', ' ', 7, 'H', 'a', 'b', 'a', 'k', 'i', ' ', 4, 'S', 'u', 'o', ' ', 3, 'N', 'a', ' ', 3, 'L', 'u', ' ', 4, 'S', 'u', 'o', ' ', 3, 'O', 'u', ' ', 3, 'Z', 'u', ' ', 5, 'T', 'u', 'a', 'n', ' ', 4, 'X', 'i', 'u', ' ', 5, 'G', 'u', 'a', 'n', ' ', 5, 'X', 'u', 'a', 'n', ' ', 5, 'L', 'i', 'a', 'n', ' ', 5, 'S', 'h', 'o', 'u', ' ', 3, 'A', 'o', ' ', 4, 'M', 'a', 'n', ' ', 3, 'M', 'o', ' ', 4, 'L', 'u', 'o', ' ', 3, 'B', 'i', ' ', 4, 'W', 'e', 'i', ' ', 4, 'L', 'i', 'u', ' ', 3, 'D', 'i', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 5, 'C', 'o', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 3, 'L', 'u', ' ', 3, 'A', 'o', ' ', 5, 'K', 'e', 'n', 'g', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 4, 'C', 'u', 'i', ' ', 3, 'Q', 'i', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 5, 'T', 'a', 'n', 'g', ' ', 4, 'M', 'a', 'n', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 5, 'C', 'h', 'a', 'n', ' ', 5, 'F', 'e', 'n', 'g', ' ', 5, 'J', 'i', 'n', 'g', ' ', 5, 'B', 'i', 'a', 'o', ' ', 4, 'S', 'h', 'u', ' ', 4, 'L', 'o', 'u', ' ', 4, 'X', 'i', 'u', ' ', 5, 'C', 'o', 'n', 'g', ' ', 5, 'L', 'o', 'n', 'g', ' ', 4, 'Z', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'C', 'a', 'o', ' ', 3, 'L', 'i', ' ', 4, 'X', 'i', 'a', ' ', 3, 'X', 'i', ' ', 5, 'K', 'a', 'n', 'g', ' ', 5, 'B', 'e', 'n', 'g', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 3, 'L', 'u', ' ', 4, 'H', 'u', 'a', ' ', 3, 'J', 'i', ' ', 3, 'P', 'u', ' ', 4, 'H', 'u', 'i', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 3, 'P', 'o', ' ', 4, 'L', 'i', 'n', ' ', 4, 'S', 'u', 'o', ' ', 4, 'X', 'i', 'u', ' ', 4, 'S', 'a', 'n', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 4, 'K', 'u', 'i', ' ', 3, 'S', 'i', ' ', 4, 'L', 'i', 'u', ' ', 4, 'N', 'a', 'o', ' ', 5, 'H', 'e', 'n', 'g', ' ', 4, 'P', 'i', 'e', ' ', 4, 'S', 'u', 'i', ' ', 4, 'F', 'a', 'n', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 5, 'T', 'a', 'n', 'g', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 4, 'J', 'u', 'e', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'Z', 'u', 'n', ' ', 5, 'L', 'i', 'a', 'o', ' ', 4, 'J', 'i', 'e', ' ', 4, 'L', 'a', 'o', ' ', 4, 'D', 'u', 'i', ' ', 4, 'T', 'a', 'n', ' ', 4, 'Z', 'a', 'n', ' ', 3, 'J', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 5, 'D', 'e', 'n', 'g', ' ', 3, 'Y', 'a', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'D', 'u', 'i', ' ', 4, 'J', 'u', 'e', ' ', 4, 'N', 'o', 'u', ' ', 3, 'T', 'i', ' ', 3, 'P', 'u', ' ', 4, 'T', 'i', 'e', ' ', 5, 'D', 'i', 'n', 'g', ' ', 5, 'S', 'h', 'a', 'n', ' ', 4, 'K', 'a', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'F', 'e', 'i', ' ', 4, 'S', 'u', 'i', ' ', 3, 'L', 'u', ' ', 5, 'J', 'u', 'a', 'n', ' ', 4, 'H', 'u', 'i', ' ', 3, 'Y', 'u', ' ', 5, 'L', 'i', 'a', 'n', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 4, 'L', 'e', 'i', ' ', 3, 'B', 'i', ' ', 4, 'T', 'i', 'e', ' ', 5, 'H', 'u', 'a', 'n', ' ', 3, 'Y', 'e', ' ', 4, 'D', 'u', 'o', ' ', 4, 'G', 'u', 'o', ' ', 5, 'D', 'a', 'n', 'g', ' ', 3, 'J', 'u', ' ', 4, 'F', 'e', 'n', ' ', 3, 'D', 'a', ' ', 4, 'B', 'e', 'i', ' ', 3, 'Y', 'i', ' ', 3, 'A', 'i', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 4, 'X', 'u', 'n', ' ', 5, 'D', 'i', 'a', 'o', ' ', 4, 'Z', 'h', 'u', ' ', 5, 'H', 'e', 'n', 'g', ' ', 5, 'Z', 'h', 'u', 'i', ' ', 3, 'J', 'i', ' ', 4, 'N', 'i', 'e', ' ', 3, 'T', 'a', ' ', 4, 'H', 'u', 'o', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 4, 'B', 'i', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'K', 'u', 'i', ' ', 5, 'N', 'i', 'n', 'g', ' ', 3, 'X', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'Y', 'a', 'r', 'i', ' ', 4, 'C', 'h', 'a', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'M', 'i', 'e', ' ', 3, 'L', 'i', ' ', 4, 'L', 'e', 'i', ' ', 3, 'J', 'i', ' ', 5, 'Z', 'u', 'a', 'n', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 6, 'S', 'h', 'a', 'n', 'g', ' ', 5, 'P', 'e', 'n', 'g', ' ', 3, 'L', 'a', ' ', 3, 'D', 'u', ' ', 5, 'S', 'h', 'u', 'o', ' ', 5, 'C', 'h', 'u', 'o', ' ', 3, 'L', 'u', ' ', 5, 'B', 'i', 'a', 'o', ' ', 4, 'B', 'a', 'o', ' ', 3, 'L', 'u', ' ', 5, 'L', 'o', 'n', 'g', ' ', 2, 'E', ' ', 3, 'L', 'u', ' ', 4, 'X', 'i', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'L', 'a', 'n', ' ', 3, 'B', 'o', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'Y', 'a', 'o', ' ', 5, 'C', 'h', 'a', 'n', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'X', 'i', ' ', 5, 'G', 'u', 'a', 'n', ' ', 5, 'C', 'a', 'n', 'g', ' ', 4, 'N', 'i', 'e', ' ', 4, 'L', 'e', 'i', ' ', 5, 'C', 'u', 'a', 'n', ' ', 3, 'Q', 'u', ' ', 4, 'P', 'a', 'n', ' ', 4, 'L', 'u', 'o', ' ', 5, 'Z', 'u', 'a', 'n', ' ', 5, 'L', 'u', 'a', 'n', ' ', 4, 'Z', 'a', 'o', ' ', 4, 'N', 'i', 'e', ' ', 4, 'J', 'u', 'e', ' ', 5, 'T', 'a', 'n', 'g', ' ', 4, 'S', 'h', 'u', ' ', 4, 'L', 'a', 'n', ' ', 4, 'J', 'i', 'n', ' ', 4, 'Q', 'i', 'u', ' ', 3, 'Y', 'i', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 5, 'D', 'i', 'n', 'g', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 3, 'P', 'o', ' ', 5, 'D', 'i', 'a', 'o', ' ', 3, 'T', 'u', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 6, 'C', 'h', 'u', 'a', 'n', ' ', 5, 'S', 'h', 'a', 'n', ' ', 3, 'J', 'i', ' ', 4, 'F', 'a', 'n', ' ', 5, 'D', 'i', 'a', 'o', ' ', 4, 'M', 'e', 'n', ' ', 3, 'N', 'u', ' ', 3, 'X', 'i', ' ', 5, 'C', 'h', 'a', 'i', ' ', 5, 'X', 'i', 'n', 'g', ' ', 4, 'G', 'a', 'i', ' ', 3, 'B', 'u', ' ', 4, 'T', 'a', 'i', ' ', 3, 'J', 'u', ' ', 4, 'D', 'u', 'n', ' ', 5, 'C', 'h', 'a', 'o', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 3, 'N', 'a', ' ', 4, 'B', 'e', 'i', ' ', 5, 'G', 'a', 'n', 'g', ' ', 4, 'B', 'a', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'Q', 'i', 'n', ' ', 4, 'J', 'u', 'n', ' ', 3, 'W', 'u', ' ', 4, 'G', 'o', 'u', ' ', 5, 'K', 'a', 'n', 'g', ' ', 5, 'F', 'a', 'n', 'g', ' ', 4, 'H', 'u', 'o', ' ', 4, 'T', 'o', 'u', ' ', 4, 'N', 'i', 'u', ' ', 3, 'B', 'a', ' ', 3, 'Y', 'u', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 3, 'G', 'u', ' ', 3, 'B', 'o', ' ', 2, 'E', ' ', 3, 'P', 'o', ' ', 3, 'B', 'u', ' ', 3, 'B', 'a', ' ', 4, 'Y', 'u', 'e', ' ', 5, 'Z', 'u', 'a', 'n', ' ', 3, 'M', 'u', ' ', 4, 'D', 'a', 'n', ' ', 4, 'J', 'i', 'a', ' ', 5, 'D', 'i', 'a', 'n', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'T', 'i', 'e', ' ', 3, 'B', 'o', ' ', 5, 'L', 'i', 'n', 'g', ' ', 5, 'S', 'h', 'u', 'o', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'L', 'i', 'u', ' ', 4, 'B', 'a', 'o', ' ', 4, 'S', 'h', 'i', ' ', 5, 'X', 'u', 'a', 'n', ' ', 4, 'S', 'h', 'e', ' ', 3, 'B', 'i', ' ', 3, 'N', 'i', ' ', 3, 'P', 'i', ' ', 4, 'D', 'u', 'o', ' ', 5, 'X', 'i', 'n', 'g', ' ', 4, 'K', 'a', 'o', ' ', 4, 'L', 'a', 'o', ' ', 3, 'E', 'r', ' ', 5, 'M', 'a', 'n', 'g', ' ', 3, 'Y', 'a', ' ', 4, 'Y', 'o', 'u', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 4, 'J', 'i', 'a', ' ', 3, 'Y', 'e', ' ', 4, 'N', 'a', 'o', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'D', 'a', 'n', 'g', ' ', 5, 'T', 'o', 'n', 'g', ' ', 3, 'L', 'u', ' ', 5, 'D', 'i', 'a', 'o', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'K', 'a', 'i', ' ', 4, 'Z', 'h', 'a', ' ', 4, 'Z', 'h', 'u', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'T', 'i', 'n', 'g', ' ', 4, 'D', 'i', 'u', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'H', 'u', 'a', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 4, 'S', 'h', 'a', ' ', 4, 'J', 'i', 'a', ' ', 4, 'Y', 'a', 'o', ' ', 3, 'G', 'e', ' ', 5, 'M', 'i', 'n', 'g', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 3, 'S', 'e', ' ', 5, 'J', 'i', 'a', 'o', ' ', 3, 'Y', 'i', ' ', 5, 'C', 'h', 'a', 'n', ' ', 6, 'C', 'h', 'o', 'n', 'g', ' ', 5, 'T', 'a', 'n', 'g', ' ', 3, 'A', 'n', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'R', 'u', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'L', 'a', 'o', ' ', 3, 'P', 'u', ' ', 3, 'W', 'u', ' ', 4, 'L', 'a', 'i', ' ', 3, 'T', 'e', ' ', 5, 'L', 'i', 'a', 'n', ' ', 5, 'K', 'e', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'o', ' ', 4, 'S', 'u', 'o', ' ', 3, 'L', 'i', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 4, 'C', 'h', 'u', ' ', 4, 'G', 'u', 'o', ' ', 4, 'G', 'a', 'o', ' ', 4, 'T', 'i', 'e', ' ', 4, 'X', 'i', 'u', ' ', 4, 'C', 'u', 'o', ' ', 4, 'L', 'u', 'e', ' ', 5, 'F', 'e', 'n', 'g', ' ', 4, 'X', 'i', 'n', ' ', 4, 'L', 'i', 'u', ' ', 4, 'K', 'a', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'R', 'u', 'i', ' ', 3, 'T', 'i', ' ', 5, 'L', 'a', 'n', 'g', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 3, 'J', 'u', ' ', 2, 'A', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 4, 'D', 'u', 'o', ' ', 5, 'T', 'i', 'a', 'n', ' ', 4, 'C', 'u', 'o', ' ', 4, 'M', 'a', 'o', ' ', 4, 'B', 'e', 'n', ' ', 3, 'Q', 'i', ' ', 3, 'D', 'e', ' ', 4, 'K', 'u', 'a', ' ', 4, 'K', 'u', 'n', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 3, 'X', 'i', ' ', 3, 'G', 'u', ' ', 4, 'L', 'u', 'o', ' ', 5, 'C', 'h', 'u', 'i', ' ', 5, 'Z', 'h', 'u', 'i', ' ', 4, 'J', 'i', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'J', 'u', 'a', 'n', ' ', 4, 'H', 'u', 'o', ' ', 4, 'P', 'o', 'u', ' ', 4, 'T', 'a', 'n', ' ', 5, 'D', 'i', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'J', 'u', ' ', 5, 'M', 'e', 'n', 'g', ' ', 3, 'Z', 'i', ' ', 4, 'Q', 'i', 'e', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'K', 'a', 'i', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 5, 'S', 'o', 'n', 'g', ' ', 2, 'E', ' ', 4, 'C', 'h', 'a', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 5, 'D', 'u', 'a', 'n', ' ', 4, 'S', 'o', 'u', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 5, 'H', 'u', 'a', 'n', ' ', 3, 'A', 'i', ' ', 3, 'D', 'u', ' ', 4, 'M', 'e', 'i', ' ', 4, 'L', 'o', 'u', ' ', 3, 'Z', 'i', ' ', 4, 'F', 'e', 'i', ' ', 4, 'M', 'e', 'i', ' ', 3, 'M', 'o', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 3, 'B', 'o', ' ', 3, 'G', 'e', ' ', 4, 'N', 'i', 'e', ' ', 5, 'T', 'a', 'n', 'g', ' ', 5, 'J', 'u', 'a', 'n', ' ', 4, 'N', 'i', 'e', ' ', 3, 'N', 'a', ' ', 4, 'L', 'i', 'u', ' ', 4, 'H', 'a', 'o', ' ', 5, 'B', 'a', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 4, 'J', 'i', 'a', ' ', 4, 'B', 'i', 'n', ' ', 5, 'R', 'o', 'n', 'g', ' ', 5, 'B', 'i', 'a', 'o', ' ', 5, 'T', 'a', 'n', 'g', ' ', 4, 'M', 'a', 'n', ' ', 4, 'L', 'u', 'o', ' ', 5, 'B', 'e', 'n', 'g', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 5, 'J', 'i', 'n', 'g', ' ', 3, 'D', 'i', ' ', 3, 'Z', 'u', ' ', 5, 'X', 'u', 'a', 'n', ' ', 4, 'L', 'i', 'u', ' ', 4, 'T', 'a', 'n', ' ', 4, 'J', 'u', 'e', ' ', 5, 'L', 'i', 'a', 'o', ' ', 3, 'P', 'u', ' ', 3, 'L', 'u', ' ', 4, 'D', 'u', 'i', ' ', 4, 'L', 'a', 'n', ' ', 3, 'P', 'u', ' ', 5, 'C', 'u', 'a', 'n', ' ', 6, 'Q', 'i', 'a', 'n', 'g', ' ', 5, 'D', 'e', 'n', 'g', ' ', 4, 'H', 'u', 'o', ' ', 4, 'L', 'e', 'i', ' ', 5, 'H', 'u', 'a', 'n', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 5, 'L', 'i', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 4, 'C', 'h', 'a', ' ', 5, 'B', 'i', 'a', 'o', ' ', 3, 'L', 'a', ' ', 5, 'C', 'h', 'a', 'n', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 4, 'J', 'i', 'u', ' ', 3, 'A', 'o', ' ', 4, 'D', 'i', 'e', ' ', 3, 'Q', 'u', ' ', 5, 'L', 'i', 'a', 'o', ' ', 3, 'M', 'i', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 4, 'M', 'e', 'n', ' ', 3, 'M', 'a', ' ', 6, 'S', 'h', 'u', 'a', 'n', ' ', 5, 'S', 'h', 'a', 'n', ' ', 4, 'H', 'u', 'o', ' ', 4, 'M', 'e', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'B', 'i', ' ', 4, 'H', 'a', 'n', ' ', 3, 'B', 'i', ' ', 4, 'S', 'a', 'n', ' ', 4, 'K', 'a', 'i', ' ', 5, 'K', 'a', 'n', 'g', ' ', 5, 'B', 'e', 'n', 'g', ' ', 5, 'H', 'o', 'n', 'g', ' ', 4, 'R', 'u', 'n', ' ', 4, 'S', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'M', 'i', 'n', ' ', 4, 'X', 'i', 'a', ' ', 5, 'Y', 'u', 'r', 'u', ' ', 4, 'D', 'o', 'u', ' ', 4, 'Z', 'h', 'a', ' ', 4, 'N', 'a', 'o', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'P', 'e', 'n', 'g', ' ', 4, 'X', 'i', 'a', ' ', 5, 'L', 'i', 'n', 'g', ' ', 5, 'B', 'i', 'a', 'n', ' ', 3, 'B', 'i', ' ', 4, 'R', 'u', 'n', ' ', 3, 'H', 'e', ' ', 5, 'G', 'u', 'a', 'n', ' ', 3, 'G', 'e', ' ', 3, 'G', 'e', ' ', 3, 'F', 'a', ' ', 4, 'C', 'h', 'u', ' ', 5, 'H', 'o', 'n', 'g', ' ', 4, 'G', 'u', 'i', ' ', 4, 'M', 'i', 'n', ' ', 3, 'S', 'e', ' ', 4, 'K', 'u', 'n', ' ', 5, 'L', 'a', 'n', 'g', ' ', 3, 'L', 'u', ' ', 5, 'T', 'i', 'n', 'g', ' ', 4, 'S', 'h', 'a', ' ', 3, 'J', 'u', ' ', 4, 'Y', 'u', 'e', ' ', 4, 'Y', 'u', 'e', ' ', 5, 'C', 'h', 'a', 'n', ' ', 3, 'Q', 'u', ' ', 4, 'L', 'i', 'n', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 5, 'S', 'h', 'a', 'i', ' ', 4, 'K', 'u', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'M', 'i', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 2, 'E', ' ', 4, 'H', 'u', 'n', ' ', 3, 'Y', 'u', ' ', 4, 'W', 'e', 'n', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 4, 'B', 'a', 'o', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 3, 'Q', 'u', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'W', 'e', 'n', ' ', 4, 'B', 'a', 'n', ' ', 3, 'A', 'n', ' ', 4, 'W', 'e', 'i', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'K', 'u', 'o', ' ', 4, 'Q', 'u', 'e', ' ', 4, 'L', 'a', 'n', ' ', 3, 'D', 'u', ' ', 7, 'P', 'h', 'w', 'u', 'n', 'g', ' ', 5, 'T', 'i', 'a', 'n', ' ', 4, 'N', 'i', 'e', ' ', 3, 'T', 'a', ' ', 4, 'K', 'a', 'i', ' ', 3, 'H', 'e', ' ', 4, 'Q', 'u', 'e', ' ', 7, 'C', 'h', 'u', 'a', 'n', 'g', ' ', 5, 'G', 'u', 'a', 'n', ' ', 4, 'D', 'o', 'u', ' ', 3, 'Q', 'i', ' ', 4, 'K', 'u', 'i', ' ', 5, 'T', 'a', 'n', 'g', ' ', 5, 'G', 'u', 'a', 'n', ' ', 5, 'P', 'i', 'a', 'o', ' ', 4, 'K', 'a', 'n', ' ', 3, 'X', 'i', ' ', 4, 'H', 'u', 'i', ' ', 5, 'C', 'h', 'a', 'n', ' ', 3, 'P', 'i', ' ', 5, 'D', 'a', 'n', 'g', ' ', 5, 'H', 'u', 'a', 'n', ' ', 3, 'T', 'a', ' ', 4, 'W', 'e', 'n', ' ', 4, 'M', 'e', 'n', ' ', 6, 'S', 'h', 'u', 'a', 'n', ' ', 5, 'S', 'h', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'H', 'a', 'n', ' ', 3, 'B', 'i', ' ', 4, 'W', 'e', 'n', ' ', 7, 'C', 'h', 'u', 'a', 'n', 'g', ' ', 4, 'R', 'u', 'n', ' ', 4, 'W', 'e', 'i', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'H', 'o', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'M', 'i', 'n', ' ', 5, 'K', 'a', 'n', 'g', ' ', 4, 'M', 'e', 'n', ' ', 4, 'Z', 'h', 'a', ' ', 4, 'N', 'a', 'o', ' ', 4, 'G', 'u', 'i', ' ', 4, 'W', 'e', 'n', ' ', 3, 'T', 'a', ' ', 4, 'M', 'i', 'n', ' ', 3, 'L', 'u', ' ', 4, 'K', 'a', 'i', ' ', 3, 'F', 'a', ' ', 3, 'G', 'e', ' ', 3, 'H', 'e', ' ', 4, 'K', 'u', 'n', ' ', 4, 'J', 'i', 'u', ' ', 4, 'Y', 'u', 'e', ' ', 5, 'L', 'a', 'n', 'g', ' ', 3, 'D', 'u', ' ', 3, 'Y', 'u', ' ', 4, 'Y', 'a', 'n', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 3, 'X', 'i', ' ', 4, 'W', 'e', 'n', ' ', 4, 'H', 'u', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 2, 'E', ' ', 5, 'C', 'h', 'a', 'n', ' ', 4, 'L', 'a', 'n', ' ', 3, 'Q', 'u', ' ', 4, 'H', 'u', 'i', ' ', 4, 'K', 'u', 'o', ' ', 4, 'Q', 'u', 'e', ' ', 3, 'G', 'e', ' ', 5, 'T', 'i', 'a', 'n', ' ', 3, 'T', 'a', ' ', 4, 'Q', 'u', 'e', ' ', 4, 'K', 'a', 'n', ' ', 5, 'H', 'u', 'a', 'n', ' ', 3, 'F', 'u', ' ', 3, 'F', 'u', ' ', 3, 'L', 'e', ' ', 4, 'D', 'u', 'i', ' ', 4, 'X', 'i', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 3, 'W', 'u', ' ', 3, 'Y', 'i', ' ', 4, 'T', 'u', 'o', ' ', 4, 'Y', 'i', 'n', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 4, 'D', 'o', 'u', ' ', 2, 'E', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 4, 'B', 'a', 'n', ' ', 4, 'P', 'e', 'i', ' ', 5, 'K', 'e', 'n', 'g', ' ', 4, 'Y', 'u', 'n', ' ', 5, 'R', 'u', 'a', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'P', 'i', ' ', 5, 'J', 'i', 'n', 'g', ' ', 5, 'F', 'a', 'n', 'g', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 4, 'Y', 'i', 'n', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 4, 'J', 'i', 'e', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 2, 'E', ' ', 3, 'Q', 'u', ' ', 3, 'D', 'i', ' ', 3, 'Z', 'u', ' ', 4, 'Z', 'u', 'o', ' ', 5, 'D', 'i', 'a', 'n', ' ', 5, 'L', 'i', 'n', 'g', ' ', 2, 'A', ' ', 4, 'T', 'u', 'o', ' ', 4, 'T', 'u', 'o', ' ', 3, 'P', 'o', ' ', 5, 'B', 'i', 'n', 'g', ' ', 3, 'F', 'u', ' ', 3, 'J', 'i', ' ', 3, 'L', 'u', ' ', 5, 'L', 'o', 'n', 'g', ' ', 5, 'C', 'h', 'e', 'n', ' ', 5, 'X', 'i', 'n', 'g', ' ', 4, 'D', 'u', 'o', ' ', 4, 'L', 'o', 'u', ' ', 3, 'M', 'o', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 4, 'S', 'h', 'u', ' ', 4, 'D', 'u', 'o', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'E', 'r', ' ', 4, 'G', 'u', 'i', ' ', 3, 'Y', 'u', ' ', 4, 'G', 'a', 'i', ' ', 5, 'S', 'h', 'a', 'n', ' ', 4, 'X', 'u', 'n', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 5, 'X', 'i', 'n', 'g', ' ', 5, 'C', 'h', 'u', 'n', ' ', 3, 'F', 'u', ' ', 3, 'B', 'i', ' ', 4, 'X', 'i', 'a', ' ', 5, 'S', 'h', 'a', 'n', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'P', 'u', ' ', 4, 'D', 'o', 'u', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 4, 'C', 'h', 'u', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'T', 'o', 'u', ' ', 4, 'N', 'i', 'e', ' ', 4, 'Y', 'u', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'P', 'e', 'i', ' ', 4, 'P', 'e', 'i', ' ', 4, 'Z', 'o', 'u', ' ', 3, 'Y', 'i', ' ', 4, 'D', 'u', 'i', ' ', 4, 'L', 'u', 'n', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'J', 'u', ' ', 5, 'C', 'h', 'u', 'i', ' ', 5, 'C', 'h', 'e', 'n', ' ', 3, 'P', 'i', ' ', 5, 'L', 'i', 'n', 'g', ' ', 4, 'T', 'a', 'o', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'L', 'u', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'Z', 'h', 'u', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 5, 'R', 'e', 'n', 'g', ' ', 5, 'S', 'h', 'a', 'n', ' ', 6, 'C', 'h', 'o', 'n', 'g', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'Y', 'u', ' ', 3, 'T', 'i', ' ', 3, 'Y', 'u', ' ', 5, 'L', 'o', 'n', 'g', ' ', 4, 'W', 'e', 'i', ' ', 4, 'W', 'e', 'i', ' ', 4, 'N', 'i', 'e', ' ', 4, 'D', 'u', 'i', ' ', 4, 'S', 'u', 'i', ' ', 3, 'A', 'n', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 4, 'J', 'i', 'e', ' ', 4, 'S', 'u', 'i', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'G', 'a', 'i', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'H', 'u', 'i', ' ', 3, 'G', 'e', ' ', 4, 'Y', 'u', 'n', ' ', 3, 'W', 'u', ' ', 4, 'W', 'e', 'i', ' ', 3, 'A', 'i', ' ', 3, 'X', 'i', ' ', 5, 'T', 'a', 'n', 'g', ' ', 3, 'J', 'i', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 4, 'D', 'a', 'o', ' ', 3, 'A', 'o', ' ', 3, 'X', 'i', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'R', 'a', 'o', ' ', 4, 'L', 'i', 'n', ' ', 4, 'T', 'u', 'i', ' ', 5, 'D', 'e', 'n', 'g', ' ', 3, 'P', 'i', ' ', 4, 'S', 'u', 'i', ' ', 4, 'S', 'u', 'i', ' ', 3, 'Y', 'u', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'F', 'e', 'n', ' ', 3, 'N', 'i', ' ', 3, 'E', 'r', ' ', 3, 'J', 'i', ' ', 4, 'D', 'a', 'o', ' ', 3, 'X', 'i', ' ', 4, 'Y', 'i', 'n', ' ', 2, 'E', ' ', 4, 'H', 'u', 'i', ' ', 5, 'L', 'o', 'n', 'g', ' ', 3, 'X', 'i', ' ', 3, 'L', 'i', ' ', 3, 'L', 'i', ' ', 3, 'L', 'i', ' ', 5, 'Z', 'h', 'u', 'i', ' ', 3, 'H', 'e', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'Z', 'h', 'u', 'n', ' ', 4, 'J', 'u', 'n', ' ', 4, 'N', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 4, 'Q', 'u', 'e', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 3, 'Y', 'a', ' ', 6, 'X', 'i', 'o', 'n', 'g', ' ', 3, 'Y', 'a', ' ', 3, 'J', 'i', ' ', 3, 'G', 'u', ' ', 5, 'H', 'u', 'a', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'G', 'o', 'u', ' ', 4, 'J', 'u', 'n', ' ', 3, 'C', 'i', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 3, 'J', 'u', ' ', 4, 'C', 'h', 'u', ' ', 3, 'H', 'u', ' ', 3, 'Z', 'a', ' ', 4, 'L', 'u', 'o', ' ', 3, 'Y', 'u', ' ', 5, 'C', 'h', 'o', 'u', ' ', 5, 'D', 'i', 'a', 'o', ' ', 4, 'S', 'u', 'i', ' ', 4, 'H', 'a', 'n', ' ', 4, 'H', 'u', 'o', ' ', 7, 'S', 'h', 'u', 'a', 'n', 'g', ' ', 5, 'G', 'u', 'a', 'n', ' ', 4, 'C', 'h', 'u', ' ', 3, 'Z', 'a', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 3, 'J', 'i', ' ', 3, 'X', 'i', ' ', 5, 'C', 'h', 'o', 'u', ' ', 4, 'L', 'i', 'u', ' ', 3, 'L', 'i', ' ', 4, 'N', 'a', 'n', ' ', 4, 'X', 'u', 'e', ' ', 3, 'Z', 'a', ' ', 3, 'J', 'i', ' ', 3, 'J', 'i', ' ', 3, 'Y', 'u', ' ', 3, 'Y', 'u', ' ', 4, 'X', 'u', 'e', ' ', 3, 'N', 'a', ' ', 4, 'F', 'o', 'u', ' ', 3, 'S', 'e', ' ', 3, 'M', 'u', ' ', 4, 'W', 'e', 'n', ' ', 4, 'F', 'e', 'n', ' ', 5, 'P', 'a', 'n', 'g', ' ', 4, 'Y', 'u', 'n', ' ', 3, 'L', 'i', ' ', 3, 'L', 'i', ' ', 4, 'A', 'n', 'g', ' ', 5, 'L', 'i', 'n', 'g', ' ', 4, 'L', 'e', 'i', ' ', 3, 'A', 'n', ' ', 4, 'B', 'a', 'o', ' ', 5, 'M', 'e', 'n', 'g', ' ', 5, 'D', 'i', 'a', 'n', ' ', 5, 'D', 'a', 'n', 'g', ' ', 5, 'X', 'i', 'n', 'g', ' ', 3, 'W', 'u', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 3, 'X', 'u', ' ', 3, 'J', 'i', ' ', 3, 'M', 'u', ' ', 5, 'C', 'h', 'e', 'n', ' ', 5, 'X', 'i', 'a', 'o', ' ', 4, 'Z', 'h', 'a', ' ', 5, 'T', 'i', 'n', 'g', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 4, 'P', 'e', 'i', ' ', 4, 'M', 'e', 'i', ' ', 5, 'L', 'i', 'n', 'g', ' ', 3, 'Q', 'i', ' ', 5, 'C', 'h', 'o', 'u', ' ', 4, 'H', 'u', 'o', ' ', 4, 'S', 'h', 'a', ' ', 4, 'F', 'e', 'i', ' ', 5, 'W', 'e', 'n', 'g', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'N', 'i', ' ', 5, 'C', 'h', 'o', 'u', ' ', 4, 'T', 'u', 'n', ' ', 4, 'L', 'i', 'n', ' ', 5, 'D', 'o', 'n', 'g', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 3, 'W', 'u', ' ', 5, 'L', 'i', 'n', 'g', ' ', 7, 'S', 'h', 'u', 'a', 'n', 'g', ' ', 5, 'L', 'i', 'n', 'g', ' ', 4, 'X', 'i', 'a', ' ', 5, 'H', 'o', 'n', 'g', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'M', 'o', ' ', 4, 'M', 'a', 'i', ' ', 4, 'Y', 'u', 'n', ' ', 4, 'L', 'i', 'u', ' ', 5, 'M', 'e', 'n', 'g', ' ', 4, 'B', 'i', 'n', ' ', 3, 'W', 'u', ' ', 4, 'W', 'e', 'i', ' ', 4, 'H', 'u', 'o', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'X', 'i', ' ', 3, 'Y', 'i', ' ', 3, 'A', 'i', ' ', 4, 'D', 'a', 'n', ' ', 5, 'D', 'e', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 3, 'L', 'u', ' ', 5, 'L', 'o', 'n', 'g', ' ', 4, 'D', 'a', 'i', ' ', 3, 'J', 'i', ' ', 5, 'P', 'a', 'n', 'g', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 3, 'B', 'a', ' ', 3, 'P', 'i', ' ', 4, 'W', 'e', 'i', ' ', 3, 'X', 'i', ' ', 3, 'J', 'i', ' ', 4, 'M', 'a', 'i', ' ', 5, 'M', 'e', 'n', 'g', ' ', 5, 'M', 'e', 'n', 'g', ' ', 4, 'L', 'e', 'i', ' ', 3, 'L', 'i', ' ', 4, 'H', 'u', 'o', ' ', 3, 'A', 'i', ' ', 4, 'F', 'e', 'i', ' ', 4, 'D', 'a', 'i', ' ', 5, 'L', 'o', 'n', 'g', ' ', 5, 'L', 'i', 'n', 'g', ' ', 3, 'A', 'i', ' ', 5, 'F', 'e', 'n', 'g', ' ', 3, 'L', 'i', ' ', 4, 'B', 'a', 'o', ' ', 3, 'H', 'e', ' ', 3, 'H', 'e', ' ', 5, 'B', 'i', 'n', 'g', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 5, 'J', 'i', 'n', 'g', ' ', 5, 'T', 'i', 'a', 'n', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 5, 'J', 'i', 'n', 'g', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 5, 'J', 'i', 'n', 'g', ' ', 5, 'J', 'i', 'n', 'g', ' ', 5, 'D', 'i', 'a', 'n', ' ', 5, 'J', 'i', 'n', 'g', ' ', 5, 'T', 'i', 'a', 'n', ' ', 4, 'F', 'e', 'i', ' ', 4, 'F', 'e', 'i', ' ', 4, 'K', 'a', 'o', ' ', 3, 'M', 'i', ' ', 5, 'M', 'i', 'a', 'n', ' ', 5, 'M', 'i', 'a', 'n', ' ', 4, 'P', 'a', 'o', ' ', 3, 'Y', 'e', ' ', 5, 'T', 'i', 'a', 'n', ' ', 4, 'H', 'u', 'i', ' ', 3, 'Y', 'e', ' ', 3, 'G', 'e', ' ', 5, 'D', 'i', 'n', 'g', ' ', 4, 'C', 'h', 'a', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'R', 'e', 'n', ' ', 3, 'D', 'i', ' ', 3, 'D', 'u', ' ', 3, 'W', 'u', ' ', 4, 'R', 'e', 'n', ' ', 4, 'Q', 'i', 'n', ' ', 4, 'J', 'i', 'n', ' ', 4, 'X', 'u', 'e', ' ', 4, 'N', 'i', 'u', ' ', 3, 'B', 'a', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'S', 'a', ' ', 3, 'N', 'a', ' ', 3, 'M', 'o', ' ', 3, 'Z', 'u', ' ', 3, 'D', 'a', ' ', 4, 'B', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'T', 'a', 'o', ' ', 4, 'T', 'u', 'o', ' ', 4, 'J', 'i', 'a', ' ', 5, 'H', 'o', 'n', 'g', ' ', 4, 'P', 'a', 'o', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 5, 'T', 'o', 'm', 'o', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'J', 'i', 'a', ' ', 4, 'T', 'a', 'o', ' ', 3, 'J', 'i', ' ', 4, 'X', 'i', 'e', ' ', 3, 'A', 'n', ' ', 3, 'A', 'n', ' ', 4, 'H', 'e', 'n', ' ', 5, 'G', 'o', 'n', 'g', ' ', 7, 'K', 'o', 'h', 'a', 'z', 'e', ' ', 3, 'D', 'a', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 5, 'T', 'i', 'n', 'g', ' ', 4, 'W', 'a', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'S', 'u', 'i', ' ', 5, 'T', 'i', 'a', 'o', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 5, 'X', 'u', 'a', 'n', ' ', 5, 'K', 'o', 'n', 'g', ' ', 5, 'B', 'e', 'n', 'g', ' ', 3, 'T', 'a', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 5, 'B', 'i', 'n', 'g', ' ', 4, 'K', 'u', 'o', ' ', 3, 'J', 'u', ' ', 3, 'L', 'a', ' ', 4, 'X', 'i', 'e', ' ', 4, 'R', 'o', 'u', ' ', 5, 'B', 'a', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 4, 'Q', 'i', 'u', ' ', 4, 'Q', 'i', 'u', ' ', 3, 'H', 'e', ' ', 5, 'X', 'i', 'a', 'o', ' ', 3, 'M', 'u', ' ', 3, 'J', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'B', 'i', 'a', 'n', ' ', 3, 'D', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'O', 'n', ' ', 4, 'T', 'a', 'o', ' ', 4, 'G', 'o', 'u', ' ', 3, 'T', 'a', ' ', 4, 'B', 'e', 'i', ' ', 4, 'X', 'i', 'e', ' ', 4, 'P', 'a', 'n', ' ', 3, 'G', 'e', ' ', 3, 'B', 'i', ' ', 4, 'K', 'u', 'o', ' ', 5, 'T', 'a', 'n', 'g', ' ', 4, 'L', 'o', 'u', ' ', 4, 'G', 'u', 'i', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 4, 'X', 'u', 'e', ' ', 3, 'J', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 5, 'C', 'h', 'a', 'n', ' ', 3, 'D', 'a', ' ', 4, 'H', 'u', 'o', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 3, 'D', 'u', ' ', 3, 'W', 'a', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'L', 'a', 'n', ' ', 4, 'W', 'e', 'i', ' ', 4, 'R', 'e', 'n', ' ', 3, 'F', 'u', ' ', 4, 'M', 'e', 'i', ' ', 5, 'J', 'u', 'a', 'n', ' ', 3, 'G', 'e', ' ', 4, 'W', 'e', 'i', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 4, 'H', 'a', 'n', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 4, 'R', 'o', 'u', ' ', 4, 'X', 'u', 'n', ' ', 4, 'S', 'h', 'e', ' ', 4, 'W', 'e', 'i', ' ', 3, 'G', 'e', ' ', 4, 'B', 'e', 'i', ' ', 4, 'T', 'a', 'o', ' ', 4, 'G', 'o', 'u', ' ', 4, 'Y', 'u', 'n', ' ', 3, 'B', 'i', ' ', 4, 'W', 'e', 'i', ' ', 4, 'H', 'u', 'i', ' ', 3, 'D', 'u', ' ', 3, 'W', 'a', ' ', 3, 'D', 'u', ' ', 4, 'W', 'e', 'i', ' ', 4, 'R', 'e', 'n', ' ', 3, 'F', 'u', ' ', 4, 'H', 'a', 'n', ' ', 4, 'W', 'e', 'i', ' ', 4, 'Y', 'u', 'n', ' ', 4, 'T', 'a', 'o', ' ', 4, 'J', 'i', 'u', ' ', 4, 'J', 'i', 'u', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'X', 'i', 'e', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'J', 'i', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'Z', 'a', ' ', 4, 'Y', 'u', 'n', ' ', 5, 'S', 'h', 'a', 'o', ' ', 3, 'L', 'e', ' ', 5, 'P', 'e', 'n', 'g', ' ', 5, 'H', 'e', 'n', 'g', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'Y', 'u', 'n', ' ', 5, 'P', 'e', 'n', 'g', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'Y', 'i', 'n', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 3, 'H', 'u', ' ', 3, 'Y', 'e', ' ', 5, 'D', 'i', 'n', 'g', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 4, 'P', 'a', 'n', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 5, 'S', 'h', 'u', 'n', ' ', 4, 'H', 'a', 'n', ' ', 3, 'X', 'u', ' ', 3, 'Y', 'i', ' ', 3, 'X', 'u', ' ', 3, 'G', 'u', ' ', 5, 'S', 'o', 'n', 'g', ' ', 4, 'K', 'u', 'i', ' ', 3, 'Q', 'i', ' ', 5, 'H', 'a', 'n', 'g', ' ', 3, 'Y', 'u', ' ', 4, 'W', 'a', 'n', ' ', 4, 'B', 'a', 'n', ' ', 4, 'D', 'u', 'n', ' ', 3, 'D', 'i', ' ', 4, 'D', 'a', 'n', ' ', 4, 'P', 'a', 'n', ' ', 3, 'P', 'o', ' ', 5, 'L', 'i', 'n', 'g', ' ', 3, 'C', 'e', ' ', 5, 'J', 'i', 'n', 'g', ' ', 4, 'L', 'e', 'i', ' ', 3, 'H', 'e', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 2, 'E', ' ', 2, 'E', ' ', 4, 'W', 'e', 'i', ' ', 4, 'J', 'i', 'e', ' ', 4, 'G', 'u', 'a', ' ', 5, 'S', 'h', 'e', 'n', ' ', 3, 'Y', 'i', ' ', 5, 'S', 'h', 'e', 'n', ' ', 4, 'H', 'a', 'i', ' ', 4, 'D', 'u', 'i', ' ', 5, 'P', 'i', 'a', 'n', ' ', 5, 'P', 'i', 'n', 'g', ' ', 4, 'L', 'e', 'i', ' ', 3, 'F', 'u', ' ', 4, 'J', 'i', 'a', ' ', 4, 'T', 'o', 'u', ' ', 4, 'H', 'u', 'i', ' ', 4, 'K', 'u', 'i', ' ', 4, 'J', 'i', 'a', ' ', 3, 'L', 'e', ' ', 5, 'T', 'i', 'a', 'n', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'J', 'u', 'n', ' ', 3, 'H', 'u', ' ', 4, 'H', 'a', 'n', ' ', 5, 'J', 'i', 'n', 'g', ' ', 4, 'T', 'u', 'i', ' ', 4, 'T', 'u', 'i', ' ', 4, 'P', 'i', 'n', ' ', 4, 'L', 'a', 'i', ' ', 4, 'T', 'u', 'i', ' ', 3, 'Z', 'i', ' ', 3, 'Z', 'i', ' ', 5, 'C', 'h', 'u', 'i', ' ', 5, 'D', 'i', 'n', 'g', ' ', 4, 'L', 'a', 'i', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'H', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'K', 'e', ' ', 4, 'C', 'u', 'i', ' ', 6, 'J', 'i', 'o', 'n', 'g', ' ', 4, 'Q', 'i', 'n', ' ', 3, 'Y', 'i', ' ', 4, 'S', 'a', 'i', ' ', 3, 'T', 'i', ' ', 2, 'E', ' ', 2, 'E', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'H', 'u', 'n', ' ', 4, 'K', 'a', 'n', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'X', 'i', 'n', ' ', 3, 'Y', 'i', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 5, 'S', 'a', 'n', 'g', ' ', 5, 'D', 'i', 'a', 'n', ' ', 5, 'D', 'i', 'a', 'n', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 3, 'K', 'u', ' ', 4, 'L', 'e', 'i', ' ', 5, 'L', 'i', 'a', 'o', ' ', 5, 'P', 'i', 'a', 'o', ' ', 3, 'Y', 'i', ' ', 4, 'M', 'a', 'n', ' ', 3, 'Q', 'i', ' ', 4, 'R', 'a', 'o', ' ', 4, 'H', 'a', 'o', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 3, 'G', 'u', ' ', 4, 'X', 'u', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'H', 'u', 'i', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 3, 'R', 'u', ' ', 5, 'H', 'o', 'n', 'g', ' ', 4, 'B', 'i', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'P', 'i', 'n', ' ', 3, 'L', 'u', ' ', 4, 'L', 'a', 'n', ' ', 4, 'N', 'i', 'e', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 3, 'Y', 'e', ' ', 5, 'D', 'i', 'n', 'g', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 4, 'H', 'a', 'n', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 5, 'S', 'h', 'u', 'n', ' ', 3, 'X', 'u', ' ', 3, 'X', 'u', ' ', 4, 'W', 'a', 'n', ' ', 3, 'G', 'u', ' ', 4, 'D', 'u', 'n', ' ', 3, 'Q', 'i', ' ', 4, 'B', 'a', 'n', ' ', 5, 'S', 'o', 'n', 'g', ' ', 5, 'H', 'a', 'n', 'g', ' ', 3, 'Y', 'u', ' ', 3, 'L', 'u', ' ', 5, 'L', 'i', 'n', 'g', ' ', 3, 'P', 'o', ' ', 5, 'J', 'i', 'n', 'g', ' ', 4, 'J', 'i', 'e', ' ', 4, 'J', 'i', 'a', ' ', 5, 'T', 'i', 'a', 'n', ' ', 4, 'H', 'a', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 6, 'J', 'i', 'o', 'n', 'g', ' ', 4, 'H', 'a', 'i', ' ', 3, 'Y', 'i', ' ', 4, 'P', 'i', 'n', ' ', 4, 'H', 'u', 'i', ' ', 4, 'T', 'u', 'i', ' ', 4, 'H', 'a', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 3, 'K', 'e', ' ', 3, 'T', 'i', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 2, 'E', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 2, 'E', ' ', 4, 'N', 'i', 'e', ' ', 4, 'M', 'a', 'n', ' ', 5, 'D', 'i', 'a', 'n', ' ', 5, 'S', 'a', 'n', 'g', ' ', 4, 'H', 'a', 'o', ' ', 4, 'L', 'e', 'i', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 3, 'R', 'u', ' ', 4, 'P', 'i', 'n', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 5, 'F', 'e', 'n', 'g', ' ', 5, 'B', 'i', 'a', 'o', ' ', 7, 'O', 'r', 'o', 's', 'h', 'i', ' ', 3, 'F', 'u', ' ', 4, 'X', 'i', 'a', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 5, 'B', 'i', 'a', 'o', ' ', 3, 'S', 'a', ' ', 3, 'B', 'a', ' ', 4, 'T', 'a', 'i', ' ', 4, 'L', 'i', 'e', ' ', 4, 'G', 'u', 'a', ' ', 5, 'X', 'u', 'a', 'n', ' ', 5, 'S', 'h', 'a', 'o', ' ', 3, 'J', 'u', ' ', 3, 'B', 'i', ' ', 3, 'S', 'i', ' ', 4, 'W', 'e', 'i', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'S', 'o', 'u', ' ', 4, 'K', 'a', 'i', ' ', 4, 'S', 'a', 'o', ' ', 4, 'F', 'a', 'n', ' ', 4, 'L', 'i', 'u', ' ', 3, 'X', 'i', ' ', 5, 'L', 'i', 'a', 'o', ' ', 5, 'P', 'i', 'a', 'o', ' ', 5, 'P', 'i', 'a', 'o', ' ', 4, 'L', 'i', 'u', ' ', 5, 'B', 'i', 'a', 'o', ' ', 5, 'B', 'i', 'a', 'o', ' ', 5, 'B', 'i', 'a', 'o', ' ', 5, 'L', 'i', 'a', 'o', ' ', 3, 'S', 'e', ' ', 5, 'F', 'e', 'n', 'g', ' ', 5, 'B', 'i', 'a', 'o', ' ', 5, 'F', 'e', 'n', 'g', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 5, 'B', 'i', 'a', 'o', ' ', 3, 'S', 'a', ' ', 3, 'J', 'u', ' ', 3, 'S', 'i', ' ', 4, 'S', 'o', 'u', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'L', 'i', 'u', ' ', 5, 'P', 'i', 'a', 'o', ' ', 5, 'B', 'i', 'a', 'o', ' ', 5, 'B', 'i', 'a', 'o', ' ', 4, 'F', 'e', 'i', ' ', 4, 'F', 'a', 'n', ' ', 4, 'F', 'e', 'i', ' ', 4, 'F', 'e', 'i', ' ', 4, 'S', 'h', 'i', ' ', 4, 'S', 'h', 'i', ' ', 4, 'C', 'a', 'n', ' ', 3, 'J', 'i', ' ', 5, 'D', 'i', 'n', 'g', ' ', 3, 'S', 'i', ' ', 4, 'T', 'u', 'o', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 4, 'S', 'u', 'n', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 4, 'T', 'u', 'n', ' ', 4, 'R', 'e', 'n', ' ', 3, 'Y', 'u', ' ', 5, 'J', 'u', 'a', 'n', ' ', 4, 'C', 'h', 'i', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'F', 'a', 'n', ' ', 4, 'F', 'a', 'n', ' ', 4, 'S', 'u', 'n', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'Z', 'h', 'u', ' ', 3, 'Y', 'i', ' ', 5, 'Z', 'h', 'a', 'i', ' ', 3, 'B', 'i', ' ', 4, 'J', 'i', 'e', ' ', 4, 'T', 'a', 'o', ' ', 4, 'L', 'i', 'u', ' ', 3, 'C', 'i', ' ', 4, 'T', 'i', 'e', ' ', 3, 'S', 'i', ' ', 4, 'B', 'a', 'o', ' ', 4, 'S', 'h', 'i', ' ', 4, 'D', 'u', 'o', ' ', 4, 'H', 'a', 'i', ' ', 4, 'R', 'e', 'n', ' ', 5, 'T', 'i', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'J', 'i', 'a', ' ', 5, 'B', 'i', 'n', 'g', ' ', 4, 'Y', 'a', 'o', ' ', 5, 'T', 'o', 'n', 'g', ' ', 3, 'C', 'i', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 3, 'E', 'r', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'L', 'e', ' ', 3, 'Y', 'i', ' ', 4, 'C', 'a', 'n', ' ', 3, 'B', 'o', ' ', 4, 'N', 'e', 'i', ' ', 2, 'E', ' ', 3, 'B', 'u', ' ', 4, 'J', 'u', 'n', ' ', 4, 'D', 'o', 'u', ' ', 3, 'S', 'u', ' ', 3, 'Y', 'u', ' ', 4, 'S', 'h', 'i', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'H', 'u', 'n', ' ', 4, 'G', 'u', 'o', ' ', 4, 'S', 'h', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'Z', 'h', 'u', 'i', ' ', 5, 'B', 'i', 'n', 'g', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'B', 'u', ' ', 3, 'Y', 'e', ' ', 4, 'T', 'a', 'n', ' ', 4, 'F', 'e', 'i', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 4, 'W', 'e', 'i', ' ', 5, 'G', 'u', 'a', 'n', ' ', 2, 'E', ' ', 5, 'N', 'u', 'a', 'n', ' ', 4, 'H', 'u', 'n', ' ', 3, 'H', 'u', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 4, 'T', 'i', 'e', ' ', 4, 'H', 'u', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'H', 'o', 'u', ' ', 3, 'H', 'e', ' ', 5, 'X', 'i', 'n', 'g', ' ', 4, 'F', 'e', 'n', ' ', 4, 'W', 'e', 'i', ' ', 3, 'G', 'u', ' ', 4, 'C', 'h', 'a', ' ', 5, 'S', 'o', 'n', 'g', ' ', 5, 'T', 'a', 'n', 'g', ' ', 3, 'B', 'o', ' ', 4, 'G', 'a', 'o', ' ', 3, 'X', 'i', ' ', 4, 'K', 'u', 'i', ' ', 4, 'L', 'i', 'u', ' ', 4, 'S', 'o', 'u', ' ', 4, 'T', 'a', 'o', ' ', 3, 'Y', 'e', ' ', 4, 'Y', 'u', 'n', ' ', 3, 'M', 'o', ' ', 5, 'T', 'a', 'n', 'g', ' ', 4, 'M', 'a', 'n', ' ', 3, 'B', 'i', ' ', 3, 'Y', 'u', ' ', 4, 'X', 'i', 'u', ' ', 4, 'J', 'i', 'n', ' ', 4, 'S', 'a', 'n', ' ', 4, 'K', 'u', 'i', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 5, 'S', 'h', 'a', 'n', ' ', 4, 'C', 'h', 'i', ' ', 4, 'D', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 3, 'J', 'i', ' ', 4, 'R', 'a', 'o', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 4, 'T', 'a', 'o', ' ', 4, 'H', 'u', 'i', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 4, 'F', 'e', 'n', ' ', 4, 'H', 'a', 'i', ' ', 5, 'M', 'e', 'n', 'g', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'M', 'o', ' ', 5, 'C', 'h', 'a', 'n', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 4, 'L', 'u', 'o', ' ', 5, 'Z', 'u', 'a', 'n', ' ', 5, 'N', 'a', 'n', 'g', ' ', 4, 'S', 'h', 'i', ' ', 5, 'D', 'i', 'n', 'g', ' ', 3, 'J', 'i', ' ', 4, 'T', 'u', 'o', ' ', 5, 'X', 'i', 'n', 'g', ' ', 4, 'T', 'u', 'n', ' ', 3, 'X', 'i', ' ', 4, 'R', 'e', 'n', ' ', 3, 'Y', 'u', ' ', 4, 'C', 'h', 'i', ' ', 4, 'F', 'a', 'n', ' ', 4, 'Y', 'i', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'S', 'h', 'i', ' ', 4, 'B', 'a', 'o', ' ', 3, 'S', 'i', ' ', 4, 'D', 'u', 'o', ' ', 3, 'Y', 'i', ' ', 3, 'E', 'r', ' ', 4, 'R', 'a', 'o', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 4, 'J', 'i', 'a', ' ', 3, 'L', 'e', ' ', 5, 'J', 'i', 'a', 'o', ' ', 3, 'Y', 'i', ' ', 5, 'B', 'i', 'n', 'g', ' ', 3, 'B', 'o', ' ', 4, 'D', 'o', 'u', ' ', 2, 'E', ' ', 3, 'Y', 'u', ' ', 4, 'N', 'e', 'i', ' ', 4, 'J', 'u', 'n', ' ', 4, 'G', 'u', 'o', ' ', 4, 'H', 'u', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'G', 'u', 'a', 'n', ' ', 4, 'C', 'h', 'a', ' ', 4, 'K', 'u', 'i', ' ', 3, 'G', 'u', ' ', 4, 'S', 'o', 'u', ' ', 5, 'C', 'h', 'a', 'n', ' ', 3, 'Y', 'e', ' ', 3, 'M', 'o', ' ', 3, 'B', 'o', ' ', 4, 'L', 'i', 'u', ' ', 4, 'X', 'i', 'u', ' ', 4, 'J', 'i', 'n', ' ', 4, 'M', 'a', 'n', ' ', 4, 'S', 'a', 'n', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 5, 'N', 'a', 'n', 'g', ' ', 5, 'S', 'h', 'o', 'u', ' ', 4, 'K', 'u', 'i', ' ', 4, 'G', 'u', 'o', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 4, 'F', 'e', 'n', ' ', 3, 'B', 'a', ' ', 3, 'N', 'i', ' ', 3, 'B', 'i', ' ', 3, 'B', 'o', ' ', 3, 'T', 'u', ' ', 4, 'H', 'a', 'n', ' ', 4, 'F', 'e', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'A', 'n', ' ', 3, 'A', 'i', ' ', 3, 'F', 'u', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'W', 'e', 'n', ' ', 4, 'X', 'i', 'n', ' ', 4, 'F', 'e', 'n', ' ', 4, 'B', 'i', 'n', ' ', 5, 'X', 'i', 'n', 'g', ' ', 3, 'M', 'a', ' ', 3, 'Y', 'u', ' ', 5, 'F', 'e', 'n', 'g', ' ', 4, 'H', 'a', 'n', ' ', 3, 'D', 'i', ' ', 4, 'T', 'u', 'o', ' ', 4, 'T', 'u', 'o', ' ', 4, 'C', 'h', 'i', ' ', 4, 'X', 'u', 'n', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'P', 'e', 'i', ' ', 4, 'X', 'i', 'n', ' ', 3, 'R', 'i', ' ', 3, 'S', 'a', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'W', 'e', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'D', 'a', 'n', ' ', 3, 'L', 'u', ' ', 4, 'Y', 'o', 'u', ' ', 3, 'B', 'o', ' ', 4, 'B', 'a', 'o', ' ', 5, 'K', 'u', 'a', 'i', ' ', 4, 'T', 'u', 'o', ' ', 3, 'Y', 'i', ' ', 3, 'Q', 'u', ' ', 3, 'Q', 'u', ' ', 6, 'J', 'i', 'o', 'n', 'g', ' ', 3, 'B', 'o', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 5, 'P', 'e', 'n', 'g', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 3, 'J', 'u', ' ', 4, 'Z', 'h', 'u', ' ', 3, 'N', 'u', ' ', 3, 'J', 'u', ' ', 3, 'P', 'i', ' ', 5, 'Z', 'a', 'n', 'g', ' ', 4, 'J', 'i', 'a', ' ', 5, 'L', 'i', 'n', 'g', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 4, 'T', 'a', 'i', ' ', 3, 'F', 'u', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 4, 'S', 'h', 'i', ' ', 3, 'B', 'i', ' ', 4, 'T', 'u', 'o', ' ', 4, 'T', 'u', 'o', ' ', 3, 'S', 'i', ' ', 4, 'L', 'i', 'u', ' ', 3, 'M', 'a', ' ', 5, 'P', 'i', 'a', 'n', ' ', 4, 'T', 'a', 'o', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'R', 'o', 'n', 'g', ' ', 5, 'T', 'e', 'n', 'g', ' ', 5, 'D', 'o', 'n', 'g', ' ', 4, 'X', 'u', 'n', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 5, 'S', 'h', 'e', 'n', ' ', 6, 'J', 'i', 'o', 'n', 'g', ' ', 3, 'E', 'r', ' ', 4, 'H', 'a', 'i', ' ', 3, 'B', 'o', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'L', 'u', 'o', ' ', 5, 'S', 'h', 'u', 'u', ' ', 4, 'D', 'a', 'n', ' ', 4, 'X', 'i', 'e', ' ', 4, 'L', 'i', 'u', ' ', 3, 'J', 'u', ' ', 5, 'S', 'o', 'n', 'g', ' ', 4, 'Q', 'i', 'n', ' ', 5, 'M', 'a', 'n', 'g', ' ', 6, 'L', 'i', 'a', 'n', 'g', ' ', 4, 'H', 'a', 'n', ' ', 3, 'T', 'u', ' ', 5, 'X', 'u', 'a', 'n', ' ', 4, 'T', 'u', 'i', ' ', 4, 'J', 'u', 'n', ' ', 2, 'E', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 4, 'X', 'i', 'n', ' ', 3, 'A', 'i', ' ', 3, 'L', 'u', ' ', 5, 'Z', 'h', 'u', 'i', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 4, 'S', 'h', 'e', ' ', 5, 'P', 'i', 'a', 'n', ' ', 4, 'K', 'u', 'n', ' ', 4, 'T', 'a', 'o', ' ', 4, 'L', 'a', 'i', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 3, 'K', 'e', ' ', 3, 'Q', 'i', ' ', 3, 'Q', 'i', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'F', 'e', 'i', ' ', 4, 'S', 'a', 'o', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'J', 'i', 'e', ' ', 4, 'Y', 'a', 'o', ' ', 3, 'W', 'u', ' ', 5, 'P', 'i', 'a', 'n', ' ', 5, 'C', 'o', 'n', 'g', ' ', 5, 'P', 'i', 'a', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'F', 'e', 'i', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'H', 'u', 'o', ' ', 3, 'Y', 'u', ' ', 3, 'T', 'i', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 4, 'X', 'i', 'a', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 4, 'K', 'u', 'i', ' ', 4, 'R', 'o', 'u', ' ', 3, 'S', 'i', ' ', 4, 'G', 'u', 'a', ' ', 4, 'T', 'u', 'o', ' ', 4, 'K', 'u', 'i', ' ', 4, 'S', 'o', 'u', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'L', 'i', 'u', ' ', 5, 'P', 'a', 'n', 'g', ' ', 5, 'T', 'e', 'n', 'g', ' ', 3, 'X', 'i', ' ', 4, 'C', 'a', 'o', ' ', 3, 'D', 'u', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'Z', 'o', 'u', ' ', 4, 'S', 'a', 'o', ' ', 5, 'S', 'h', 'a', 'n', ' ', 3, 'L', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 7, 'S', 'h', 'u', 'a', 'n', 'g', ' ', 3, 'L', 'u', ' ', 3, 'X', 'i', ' ', 4, 'L', 'u', 'o', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 3, 'M', 'o', ' ', 3, 'A', 'o', ' ', 4, 'C', 'a', 'n', ' ', 5, 'P', 'i', 'a', 'o', ' ', 5, 'C', 'o', 'n', 'g', ' ', 3, 'Q', 'u', ' ', 3, 'B', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'Y', 'u', ' ', 3, 'X', 'u', ' ', 4, 'H', 'u', 'a', ' ', 3, 'B', 'o', ' ', 3, 'S', 'u', ' ', 5, 'X', 'i', 'a', 'o', ' ', 4, 'L', 'i', 'n', ' ', 5, 'C', 'h', 'a', 'n', ' ', 4, 'D', 'u', 'n', ' ', 4, 'L', 'i', 'u', ' ', 4, 'T', 'u', 'o', ' ', 5, 'Z', 'e', 'n', 'g', ' ', 4, 'T', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'T', 'i', 'e', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'L', 'u', 'o', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 5, 'J', 'i', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 3, 'Y', 'e', ' ', 4, 'T', 'u', 'o', ' ', 4, 'B', 'i', 'n', ' ', 4, 'Z', 'o', 'u', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'P', 'e', 'n', 'g', ' ', 3, 'L', 'u', ' ', 5, 'T', 'e', 'n', 'g', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 3, 'J', 'i', ' ', 7, 'S', 'h', 'u', 'a', 'n', 'g', ' ', 3, 'J', 'u', ' ', 3, 'X', 'i', ' ', 5, 'H', 'u', 'a', 'n', ' ', 3, 'L', 'i', ' ', 5, 'B', 'i', 'a', 'o', ' ', 3, 'M', 'a', ' ', 3, 'Y', 'u', ' ', 4, 'T', 'u', 'o', ' ', 4, 'X', 'u', 'n', ' ', 4, 'C', 'h', 'i', ' ', 3, 'Q', 'u', ' ', 3, 'R', 'i', ' ', 3, 'B', 'o', ' ', 3, 'L', 'u', ' ', 5, 'Z', 'a', 'n', 'g', ' ', 4, 'S', 'h', 'i', ' ', 3, 'S', 'i', ' ', 3, 'F', 'u', ' ', 3, 'J', 'u', ' ', 4, 'Z', 'o', 'u', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'T', 'u', 'o', ' ', 3, 'N', 'u', ' ', 4, 'J', 'i', 'a', ' ', 3, 'Y', 'i', ' ', 4, 'T', 'a', 'i', ' ', 5, 'X', 'i', 'a', 'o', ' ', 3, 'M', 'a', ' ', 4, 'Y', 'i', 'n', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'H', 'u', 'a', ' ', 4, 'L', 'u', 'o', ' ', 4, 'H', 'a', 'i', ' ', 5, 'P', 'i', 'a', 'n', ' ', 5, 'B', 'i', 'a', 'o', ' ', 3, 'L', 'i', ' ', 6, 'C', 'h', 'e', 'n', 'g', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'X', 'i', 'n', ' ', 4, 'Q', 'i', 'n', ' ', 4, 'J', 'u', 'n', ' ', 3, 'Q', 'i', ' ', 3, 'Q', 'i', ' ', 3, 'K', 'e', ' ', 5, 'Z', 'h', 'u', 'i', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 3, 'S', 'u', ' ', 4, 'C', 'a', 'n', ' ', 5, 'P', 'i', 'a', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'K', 'u', 'i', ' ', 4, 'S', 'a', 'o', ' ', 3, 'W', 'u', ' ', 3, 'A', 'o', ' ', 4, 'L', 'i', 'u', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'S', 'h', 'a', 'n', ' ', 5, 'P', 'i', 'a', 'o', ' ', 4, 'L', 'u', 'o', ' ', 5, 'C', 'o', 'n', 'g', ' ', 5, 'C', 'h', 'a', 'n', ' ', 4, 'Z', 'o', 'u', ' ', 3, 'J', 'i', ' ', 7, 'S', 'h', 'u', 'a', 'n', 'g', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 3, 'G', 'u', ' ', 4, 'W', 'e', 'i', ' ', 4, 'W', 'e', 'i', ' ', 4, 'W', 'e', 'i', ' ', 3, 'Y', 'u', ' ', 4, 'G', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 4, 'A', 'n', 'g', ' ', 4, 'T', 'o', 'u', ' ', 4, 'X', 'i', 'e', ' ', 4, 'B', 'a', 'o', ' ', 3, 'B', 'i', ' ', 4, 'C', 'h', 'i', ' ', 3, 'T', 'i', ' ', 3, 'D', 'i', ' ', 3, 'K', 'u', ' ', 4, 'H', 'a', 'i', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 4, 'G', 'o', 'u', ' ', 4, 'K', 'u', 'a', ' ', 3, 'G', 'e', ' ', 4, 'T', 'u', 'i', ' ', 5, 'G', 'e', 'n', 'g', ' ', 5, 'P', 'i', 'a', 'n', ' ', 3, 'B', 'i', ' ', 3, 'K', 'e', ' ', 3, 'K', 'a', ' ', 3, 'Y', 'u', ' ', 4, 'S', 'u', 'i', ' ', 4, 'L', 'o', 'u', ' ', 3, 'B', 'o', ' ', 5, 'X', 'i', 'a', 'o', ' ', 5, 'P', 'a', 'n', 'g', ' ', 3, 'B', 'o', ' ', 3, 'C', 'i', ' ', 5, 'K', 'u', 'a', 'n', ' ', 4, 'B', 'i', 'n', ' ', 3, 'M', 'o', ' ', 5, 'L', 'i', 'a', 'o', ' ', 4, 'L', 'o', 'u', ' ', 4, 'N', 'a', 'o', ' ', 3, 'D', 'u', ' ', 5, 'Z', 'a', 'n', 'g', ' ', 4, 'S', 'u', 'i', ' ', 3, 'T', 'i', ' ', 4, 'B', 'i', 'n', ' ', 5, 'K', 'u', 'a', 'n', ' ', 3, 'L', 'u', ' ', 4, 'G', 'a', 'o', ' ', 4, 'G', 'a', 'o', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 4, 'K', 'a', 'o', ' ', 5, 'Q', 'i', 'a', 'o', ' ', 4, 'L', 'a', 'o', ' ', 4, 'Z', 'a', 'o', ' ', 5, 'B', 'i', 'a', 'o', ' ', 4, 'K', 'u', 'n', ' ', 4, 'K', 'u', 'n', ' ', 3, 'T', 'i', ' ', 5, 'F', 'a', 'n', 'g', ' ', 4, 'X', 'i', 'u', ' ', 4, 'R', 'a', 'n', ' ', 4, 'M', 'a', 'o', ' ', 4, 'D', 'a', 'n', ' ', 4, 'K', 'u', 'n', ' ', 4, 'B', 'i', 'n', ' ', 3, 'F', 'a', ' ', 5, 'T', 'i', 'a', 'o', ' ', 5, 'P', 'e', 'n', 'g', ' ', 3, 'Z', 'i', ' ', 3, 'F', 'a', ' ', 4, 'R', 'a', 'n', ' ', 3, 'T', 'i', ' ', 4, 'P', 'a', 'o', ' ', 3, 'P', 'i', ' ', 4, 'M', 'a', 'o', ' ', 3, 'F', 'u', ' ', 3, 'E', 'r', ' ', 5, 'R', 'o', 'n', 'g', ' ', 3, 'Q', 'u', ' ', 5, 'G', 'o', 'n', 'g', ' ', 4, 'X', 'i', 'u', ' ', 4, 'G', 'u', 'a', ' ', 3, 'J', 'i', ' ', 5, 'P', 'e', 'n', 'g', ' ', 5, 'Z', 'h', 'u', 'a', ' ', 5, 'S', 'h', 'a', 'o', ' ', 4, 'S', 'h', 'a', ' ', 3, 'T', 'i', ' ', 3, 'L', 'i', ' ', 4, 'B', 'i', 'n', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 3, 'T', 'i', ' ', 5, 'P', 'e', 'n', 'g', ' ', 5, 'S', 'o', 'n', 'g', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 5, 'S', 'h', 'u', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'D', 'u', 'o', ' ', 3, 'H', 'u', ' ', 3, 'L', 'a', ' ', 4, 'J', 'i', 'u', ' ', 3, 'Q', 'i', ' ', 5, 'L', 'i', 'a', 'n', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 4, 'B', 'i', 'n', ' ', 5, 'P', 'e', 'n', 'g', ' ', 3, 'M', 'o', ' ', 4, 'S', 'a', 'n', ' ', 4, 'M', 'a', 'n', ' ', 4, 'M', 'a', 'n', ' ', 5, 'S', 'e', 'n', 'g', ' ', 3, 'X', 'u', ' ', 4, 'L', 'i', 'e', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'N', 'o', 'n', 'g', ' ', 5, 'H', 'u', 'a', 'n', ' ', 5, 'K', 'u', 'a', 'i', ' ', 5, 'N', 'i', 'n', 'g', ' ', 4, 'B', 'i', 'n', ' ', 4, 'L', 'i', 'e', ' ', 5, 'R', 'a', 'n', 'g', ' ', 4, 'D', 'o', 'u', ' ', 4, 'D', 'o', 'u', ' ', 4, 'N', 'a', 'o', ' ', 5, 'H', 'o', 'n', 'g', ' ', 3, 'X', 'i', ' ', 4, 'D', 'o', 'u', ' ', 4, 'H', 'a', 'n', ' ', 4, 'D', 'o', 'u', ' ', 4, 'D', 'o', 'u', ' ', 4, 'J', 'i', 'u', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 3, 'Y', 'u', ' ', 3, 'Y', 'u', ' ', 3, 'L', 'i', ' ', 5, 'J', 'u', 'a', 'n', ' ', 3, 'F', 'u', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'G', 'u', 'i', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 4, 'L', 'i', 'u', ' ', 4, 'G', 'u', 'i', ' ', 6, 'S', 'h', 'a', 'n', 'g', ' ', 3, 'Y', 'u', ' ', 4, 'G', 'u', 'i', ' ', 4, 'M', 'e', 'i', ' ', 3, 'J', 'i', ' ', 3, 'Q', 'i', ' ', 4, 'J', 'i', 'e', ' ', 4, 'K', 'u', 'i', ' ', 4, 'H', 'u', 'n', ' ', 3, 'B', 'a', ' ', 3, 'P', 'o', ' ', 4, 'M', 'e', 'i', ' ', 3, 'X', 'u', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'o', ' ', 6, 'L', 'i', 'a', 'n', 'g', ' ', 3, 'Y', 'u', ' ', 4, 'T', 'u', 'i', ' ', 3, 'Q', 'i', ' ', 5, 'W', 'a', 'n', 'g', ' ', 6, 'L', 'i', 'a', 'n', 'g', ' ', 4, 'W', 'e', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'C', 'h', 'i', ' ', 5, 'P', 'i', 'a', 'o', ' ', 3, 'B', 'i', ' ', 3, 'M', 'o', ' ', 3, 'J', 'i', ' ', 3, 'X', 'u', ' ', 5, 'C', 'h', 'o', 'u', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 4, 'D', 'a', 'o', ' ', 4, 'R', 'e', 'n', ' ', 3, 'J', 'i', ' ', 4, 'E', 'r', 'i', ' ', 5, 'G', 'o', 'n', 'g', ' ', 4, 'T', 'u', 'o', ' ', 5, 'D', 'i', 'a', 'o', ' ', 3, 'J', 'i', ' ', 3, 'X', 'u', ' ', 2, 'E', ' ', 2, 'E', ' ', 4, 'S', 'h', 'a', ' ', 5, 'H', 'a', 'n', 'g', ' ', 4, 'T', 'u', 'n', ' ', 3, 'M', 'o', ' ', 4, 'J', 'i', 'e', ' ', 5, 'S', 'h', 'e', 'n', ' ', 4, 'F', 'a', 'n', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 3, 'B', 'i', ' ', 3, 'L', 'u', ' ', 4, 'W', 'e', 'n', ' ', 3, 'H', 'u', ' ', 3, 'L', 'u', ' ', 3, 'Z', 'a', ' ', 5, 'F', 'a', 'n', 'g', ' ', 4, 'F', 'e', 'n', ' ', 3, 'N', 'a', ' ', 4, 'Y', 'o', 'u', ' ', 7, 'N', 'a', 'm', 'a', 'z', 'u', ' ', 5, 'T', 'o', 'd', 'o', ' ', 3, 'H', 'e', ' ', 4, 'X', 'i', 'a', ' ', 3, 'Q', 'u', ' ', 4, 'H', 'a', 'n', ' ', 3, 'P', 'i', ' ', 5, 'L', 'i', 'n', 'g', ' ', 4, 'T', 'u', 'o', ' ', 3, 'B', 'o', ' ', 4, 'Q', 'i', 'u', ' ', 5, 'P', 'i', 'n', 'g', ' ', 3, 'F', 'u', ' ', 3, 'B', 'i', ' ', 3, 'J', 'i', ' ', 4, 'W', 'e', 'i', ' ', 3, 'J', 'u', ' ', 5, 'D', 'i', 'a', 'o', ' ', 3, 'B', 'o', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'G', 'u', 'n', ' ', 3, 'P', 'i', ' ', 5, 'N', 'i', 'a', 'n', ' ', 5, 'X', 'i', 'n', 'g', ' ', 4, 'T', 'a', 'i', ' ', 4, 'B', 'a', 'o', ' ', 3, 'F', 'u', ' ', 4, 'Z', 'h', 'a', ' ', 3, 'J', 'u', ' ', 3, 'G', 'u', ' ', 7, 'K', 'a', 'j', 'i', 'k', 'a', ' ', 5, 'T', 'o', 'n', 'g', ' ', 3, 'T', 'a', ' ', 4, 'J', 'i', 'e', ' ', 4, 'S', 'h', 'u', ' ', 4, 'H', 'o', 'u', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 3, 'E', 'r', ' ', 3, 'A', 'n', ' ', 4, 'W', 'e', 'i', ' ', 5, 'T', 'i', 'a', 'o', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'L', 'i', 'e', ' ', 4, 'L', 'u', 'o', ' ', 5, 'T', 'o', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 3, 'Q', 'i', ' ', 5, 'B', 'i', 'n', 'g', ' ', 4, 'W', 'e', 'i', ' ', 5, 'J', 'i', 'a', 'o', ' ', 3, 'B', 'u', ' ', 4, 'G', 'u', 'i', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'G', 'e', ' ', 4, 'H', 'u', 'i', ' ', 5, 'B', 'o', 'r', 'a', ' ', 5, 'M', 'a', 't', 'e', ' ', 4, 'K', 'a', 'o', ' ', 5, 'G', 'o', 'r', 'i', ' ', 4, 'D', 'u', 'o', ' ', 4, 'J', 'u', 'n', ' ', 3, 'T', 'i', ' ', 4, 'M', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'o', ' ', 3, 'Z', 'a', ' ', 4, 'S', 'h', 'a', ' ', 4, 'Q', 'i', 'n', ' ', 3, 'Y', 'u', ' ', 4, 'N', 'e', 'i', ' ', 4, 'Z', 'h', 'e', ' ', 4, 'G', 'u', 'n', ' ', 5, 'G', 'e', 'n', 'g', ' ', 3, 'S', 'u', ' ', 3, 'W', 'u', ' ', 4, 'Q', 'i', 'u', ' ', 5, 'T', 'i', 'n', 'g', ' ', 3, 'F', 'u', ' ', 4, 'W', 'a', 'n', ' ', 4, 'Y', 'o', 'u', ' ', 3, 'L', 'i', ' ', 4, 'S', 'h', 'a', ' ', 4, 'S', 'h', 'a', ' ', 4, 'G', 'a', 'o', ' ', 5, 'M', 'e', 'n', 'g', ' ', 5, 'U', 'g', 'u', 'i', ' ', 6, 'A', 's', 'a', 'r', 'i', ' ', 10, 'S', 'u', 'b', 'a', 's', 'h', 'i', 'r', 'i', ' ', 9, 'K', 'a', 'z', 'u', 'n', 'o', 'k', 'o', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 3, 'N', 'i', ' ', 3, 'Z', 'i', ' ', 3, 'Q', 'i', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 4, 'N', 'e', 'i', ' ', 5, 'C', 'h', 'u', 'n', ' ', 3, 'J', 'i', ' ', 5, 'D', 'i', 'a', 'o', ' ', 4, 'Q', 'i', 'e', ' ', 3, 'G', 'u', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 5, 'D', 'o', 'n', 'g', ' ', 4, 'L', 'a', 'i', ' ', 4, 'F', 'e', 'i', ' ', 3, 'N', 'i', ' ', 3, 'Y', 'i', ' ', 4, 'K', 'u', 'n', ' ', 3, 'L', 'u', ' ', 4, 'J', 'i', 'u', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 5, 'J', 'i', 'n', 'g', ' ', 4, 'L', 'u', 'n', ' ', 5, 'L', 'i', 'n', 'g', ' ', 4, 'Z', 'o', 'u', ' ', 3, 'L', 'i', ' ', 5, 'M', 'e', 'n', 'g', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'N', 'i', 'a', 'n', ' ', 7, 'S', 'h', 'a', 'c', 'h', 'i', ' ', 6, 'D', 'o', 'j', 'o', 'u', ' ', 8, 'S', 'u', 'k', 'e', 's', 'o', 'u', ' ', 4, 'S', 'h', 'i', ' ', 5, 'S', 'h', 'e', 'n', ' ', 4, 'H', 'u', 'n', ' ', 4, 'S', 'h', 'i', ' ', 4, 'H', 'o', 'u', ' ', 5, 'X', 'i', 'n', 'g', ' ', 4, 'Z', 'h', 'u', ' ', 3, 'L', 'a', ' ', 5, 'Z', 'o', 'n', 'g', ' ', 3, 'J', 'i', ' ', 5, 'B', 'i', 'a', 'n', ' ', 5, 'B', 'i', 'a', 'n', ' ', 5, 'H', 'u', 'a', 'n', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 3, 'Z', 'e', ' ', 4, 'W', 'e', 'i', ' ', 4, 'W', 'e', 'i', ' ', 3, 'Y', 'u', ' ', 4, 'Q', 'u', 'n', ' ', 4, 'R', 'o', 'u', ' ', 4, 'D', 'i', 'e', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 5, 'L', 'i', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'Q', 'i', 'u', ' ', 4, 'Q', 'i', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'B', 'i', ' ', 2, 'E', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 3, 'F', 'u', ' ', 4, 'S', 'a', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'X', 'i', 'a', ' ', 4, 'T', 'u', 'o', ' ', 3, 'H', 'u', ' ', 8, 'M', 'u', 'r', 'o', 'a', 'j', 'i', ' ', 4, 'R', 'u', 'o', ' ', 7, 'H', 'a', 'r', 'a', 'k', 'a', ' ', 4, 'W', 'e', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'H', 'a', 'o', ' ', 3, 'W', 'u', ' ', 5, 'F', 'a', 'n', 'g', ' ', 4, 'S', 'a', 'o', ' ', 4, 'L', 'i', 'u', ' ', 3, 'M', 'a', ' ', 4, 'S', 'h', 'i', ' ', 4, 'S', 'h', 'i', ' ', 4, 'Y', 'i', 'n', ' ', 2, 'Z', ' ', 5, 'T', 'e', 'n', 'g', ' ', 3, 'T', 'a', ' ', 4, 'Y', 'a', 'o', ' ', 3, 'G', 'e', ' ', 5, 'R', 'o', 'n', 'g', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 3, 'Q', 'i', ' ', 4, 'W', 'e', 'n', ' ', 4, 'R', 'u', 'o', ' ', 9, 'H', 'a', 't', 'a', 'h', 'a', 't', 'a', ' ', 5, 'L', 'i', 'a', 'n', ' ', 3, 'A', 'o', ' ', 3, 'L', 'e', ' ', 4, 'H', 'u', 'i', ' ', 4, 'M', 'i', 'n', ' ', 3, 'J', 'i', ' ', 5, 'T', 'i', 'a', 'o', ' ', 3, 'Q', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'S', 'a', 'o', ' ', 4, 'M', 'a', 'n', ' ', 3, 'X', 'i', ' ', 4, 'Q', 'i', 'u', ' ', 5, 'B', 'i', 'a', 'o', ' ', 3, 'J', 'i', ' ', 3, 'J', 'i', ' ', 4, 'Z', 'h', 'u', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 4, 'Q', 'i', 'u', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 5, 'K', 'a', 'n', 'g', ' ', 4, 'X', 'u', 'e', ' ', 4, 'B', 'i', 'e', ' ', 4, 'J', 'u', 'e', ' ', 3, 'Q', 'u', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 3, 'B', 'o', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'X', 'u', 'n', ' ', 3, 'S', 'u', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 4, 'Z', 'u', 'n', ' ', 5, 'S', 'h', 'a', 'n', ' ', 5, 'S', 'h', 'a', 'n', ' ', 4, 'F', 'a', 'n', ' ', 4, 'J', 'u', 'e', ' ', 4, 'L', 'i', 'n', ' ', 4, 'X', 'u', 'n', ' ', 5, 'M', 'i', 'a', 'o', ' ', 3, 'X', 'i', ' ', 4, 'E', 's', 'o', ' ', 5, 'K', 'y', 'o', 'u', ' ', 4, 'F', 'e', 'n', ' ', 5, 'G', 'u', 'a', 'n', ' ', 4, 'H', 'o', 'u', ' ', 5, 'K', 'u', 'a', 'i', ' ', 4, 'Z', 'e', 'i', ' ', 4, 'S', 'a', 'o', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 4, 'G', 'a', 'n', ' ', 4, 'G', 'u', 'i', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 3, 'L', 'i', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 9, 'H', 'a', 't', 'a', 'h', 'a', 't', 'a', ' ', 7, 'S', 'h', 'i', 'i', 'r', 'a', ' ', 6, 'M', 'u', 't', 's', 'u', ' ', 3, 'R', 'u', ' ', 3, 'J', 'i', ' ', 3, 'X', 'u', ' ', 4, 'H', 'u', 'o', ' ', 7, 'S', 'h', 'i', 'i', 'r', 'a', ' ', 3, 'L', 'i', ' ', 4, 'L', 'i', 'e', ' ', 3, 'L', 'i', ' ', 4, 'M', 'i', 'e', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 2, 'E', ' ', 3, 'L', 'u', ' ', 5, 'G', 'u', 'a', 'n', ' ', 3, 'L', 'i', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 4, 'D', 'a', 'o', ' ', 3, 'J', 'i', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'T', 'u', 'n', ' ', 3, 'L', 'u', ' ', 5, 'F', 'a', 'n', 'g', ' ', 3, 'B', 'a', ' ', 3, 'H', 'e', ' ', 3, 'B', 'o', ' ', 5, 'P', 'i', 'n', 'g', ' ', 5, 'N', 'i', 'a', 'n', ' ', 3, 'L', 'u', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'Z', 'h', 'a', ' ', 3, 'F', 'u', ' ', 3, 'B', 'o', ' ', 4, 'B', 'a', 'o', ' ', 4, 'H', 'o', 'u', ' ', 3, 'P', 'i', ' ', 4, 'T', 'a', 'i', ' ', 4, 'G', 'u', 'i', ' ', 4, 'J', 'i', 'e', ' ', 4, 'K', 'a', 'o', ' ', 4, 'W', 'e', 'i', ' ', 3, 'E', 'r', ' ', 5, 'T', 'o', 'n', 'g', ' ', 3, 'Z', 'e', ' ', 4, 'H', 'o', 'u', ' ', 5, 'K', 'u', 'a', 'i', ' ', 3, 'J', 'i', ' ', 5, 'J', 'i', 'a', 'o', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'Z', 'a', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 4, 'X', 'u', 'n', ' ', 5, 'G', 'e', 'n', 'g', ' ', 3, 'L', 'i', ' ', 5, 'L', 'i', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'L', 'i', ' ', 4, 'S', 'h', 'i', ' ', 5, 'T', 'i', 'a', 'o', ' ', 4, 'G', 'u', 'n', ' ', 4, 'S', 'h', 'a', ' ', 4, 'W', 'a', 'n', ' ', 4, 'J', 'u', 'n', ' ', 3, 'J', 'i', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 5, 'L', 'i', 'n', 'g', ' ', 3, 'Q', 'i', ' ', 4, 'Z', 'o', 'u', ' ', 4, 'F', 'e', 'i', ' ', 4, 'K', 'u', 'n', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 3, 'G', 'u', ' ', 3, 'N', 'i', ' ', 5, 'N', 'i', 'a', 'n', ' ', 5, 'D', 'i', 'a', 'o', ' ', 5, 'J', 'i', 'n', 'g', ' ', 5, 'S', 'h', 'e', 'n', ' ', 4, 'S', 'h', 'i', ' ', 3, 'Z', 'i', ' ', 4, 'F', 'e', 'n', ' ', 4, 'D', 'i', 'e', ' ', 3, 'B', 'i', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 4, 'S', 'h', 'i', ' ', 4, 'W', 'e', 'n', ' ', 4, 'W', 'e', 'i', ' ', 4, 'S', 'a', 'i', ' ', 2, 'E', ' ', 4, 'Q', 'i', 'u', ' ', 3, 'F', 'u', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 6, 'J', 'i', 'a', 'n', 'g', ' ', 5, 'B', 'i', 'a', 'n', ' ', 4, 'S', 'a', 'o', ' ', 3, 'A', 'o', ' ', 3, 'Q', 'i', ' ', 3, 'T', 'a', ' ', 4, 'Y', 'i', 'n', ' ', 4, 'Y', 'a', 'o', ' ', 5, 'F', 'a', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'L', 'e', ' ', 5, 'B', 'i', 'a', 'o', ' ', 4, 'X', 'u', 'e', ' ', 4, 'B', 'i', 'e', ' ', 4, 'M', 'a', 'n', ' ', 4, 'M', 'i', 'n', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 4, 'W', 'e', 'i', ' ', 3, 'X', 'i', ' ', 4, 'J', 'u', 'e', ' ', 5, 'S', 'h', 'a', 'n', ' ', 4, 'L', 'i', 'n', ' ', 4, 'Z', 'u', 'n', ' ', 4, 'H', 'u', 'o', ' ', 4, 'G', 'a', 'n', ' ', 3, 'L', 'i', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 5, 'G', 'u', 'a', 'n', ' ', 5, 'N', 'i', 'a', 'o', ' ', 3, 'Y', 'i', ' ', 3, 'F', 'u', ' ', 3, 'L', 'i', ' ', 4, 'J', 'i', 'u', ' ', 3, 'B', 'u', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'F', 'u', ' ', 5, 'D', 'i', 'a', 'o', ' ', 3, 'J', 'i', ' ', 5, 'F', 'e', 'n', 'g', ' ', 4, 'N', 'i', 'o', ' ', 4, 'G', 'a', 'n', ' ', 4, 'S', 'h', 'i', ' ', 5, 'F', 'e', 'n', 'g', ' ', 5, 'M', 'i', 'n', 'g', ' ', 4, 'B', 'a', 'o', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'H', 'u', ' ', 4, 'Q', 'i', 'n', ' ', 3, 'F', 'u', ' ', 4, 'F', 'e', 'n', ' ', 4, 'W', 'e', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'S', 'h', 'i', ' ', 3, 'Y', 'u', ' ', 4, 'F', 'o', 'u', ' ', 5, 'Y', 'i', 'a', 'o', ' ', 4, 'J', 'u', 'e', ' ', 4, 'J', 'u', 'e', ' ', 3, 'P', 'i', ' ', 5, 'H', 'u', 'a', 'n', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 4, 'B', 'a', 'o', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'Y', 'a', ' ', 6, 'Z', 'h', 'e', 'n', 'g', ' ', 5, 'F', 'a', 'n', 'g', ' ', 5, 'F', 'e', 'n', 'g', ' ', 4, 'W', 'e', 'n', ' ', 3, 'O', 'u', ' ', 3, 'T', 'e', ' ', 4, 'J', 'i', 'a', ' ', 3, 'N', 'u', ' ', 5, 'L', 'i', 'n', 'g', ' ', 4, 'M', 'i', 'e', ' ', 3, 'F', 'u', ' ', 4, 'T', 'u', 'o', ' ', 4, 'W', 'e', 'n', ' ', 3, 'L', 'i', ' ', 5, 'B', 'i', 'a', 'n', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'G', 'e', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 3, 'Z', 'i', ' ', 3, 'Q', 'u', ' ', 5, 'X', 'i', 'a', 'o', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'D', 'a', 'n', ' ', 3, 'J', 'u', ' ', 4, 'Y', 'o', 'u', ' ', 3, 'G', 'u', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 3, 'Y', 'u', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 5, 'R', 'o', 'n', 'g', ' ', 3, 'Y', 'a', ' ', 4, 'T', 'i', 'e', ' ', 3, 'Y', 'u', ' ', 6, 'S', 'h', 'i', 'g', 'i', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 5, 'Z', 'h', 'u', 'i', ' ', 3, 'W', 'u', ' ', 3, 'E', 'r', ' ', 4, 'G', 'u', 'a', ' ', 3, 'A', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'H', 'e', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'o', ' ', 3, 'J', 'i', ' ', 4, 'L', 'i', 'e', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'R', 'e', 'n', ' ', 3, 'Y', 'i', ' ', 5, 'H', 'o', 'n', 'g', ' ', 4, 'L', 'u', 'o', ' ', 3, 'R', 'u', ' ', 4, 'M', 'o', 'u', ' ', 3, 'G', 'e', ' ', 4, 'R', 'e', 'n', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'X', 'i', 'u', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'L', 'u', 'o', ' ', 8, 'C', 'h', 'i', 'd', 'o', 'r', 'i', ' ', 5, 'T', 'o', 'k', 'i', ' ', 4, 'T', 'e', 'n', ' ', 5, 'L', 'u', 'a', 'n', ' ', 4, 'J', 'i', 'a', ' ', 3, 'J', 'i', ' ', 3, 'Y', 'u', ' ', 5, 'H', 'u', 'a', 'n', ' ', 4, 'T', 'u', 'o', ' ', 3, 'B', 'u', ' ', 3, 'W', 'u', ' ', 5, 'J', 'u', 'a', 'n', ' ', 3, 'Y', 'u', ' ', 3, 'B', 'o', ' ', 4, 'X', 'u', 'n', ' ', 4, 'X', 'u', 'n', ' ', 3, 'B', 'i', ' ', 3, 'X', 'i', ' ', 4, 'J', 'u', 'n', ' ', 3, 'J', 'u', ' ', 3, 'T', 'u', ' ', 5, 'J', 'i', 'n', 'g', ' ', 3, 'T', 'i', ' ', 2, 'E', ' ', 2, 'E', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 3, 'H', 'u', ' ', 3, 'W', 'u', ' ', 5, 'S', 'h', 'e', 'n', ' ', 4, 'L', 'a', 'i', ' ', 8, 'I', 'k', 'a', 'r', 'u', 'g', 'a', ' ', 7, 'K', 'a', 'k', 'e', 's', 'u', ' ', 3, 'L', 'u', ' ', 5, 'P', 'i', 'n', 'g', ' ', 4, 'S', 'h', 'u', ' ', 3, 'F', 'u', ' ', 3, 'A', 'n', ' ', 5, 'Z', 'h', 'a', 'o', ' ', 5, 'P', 'e', 'n', 'g', ' ', 4, 'Q', 'i', 'n', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'B', 'e', 'i', ' ', 5, 'D', 'i', 'a', 'o', ' ', 3, 'L', 'u', ' ', 4, 'Q', 'u', 'e', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'J', 'u', ' ', 3, 'T', 'u', ' ', 3, 'Y', 'a', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 3, 'Q', 'i', ' ', 3, 'L', 'i', ' ', 3, 'Y', 'e', ' ', 5, 'Z', 'h', 'u', 'i', ' ', 5, 'K', 'o', 'n', 'g', ' ', 5, 'Z', 'h', 'u', 'i', ' ', 4, 'K', 'u', 'n', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 3, 'Q', 'i', ' ', 5, 'J', 'i', 'n', 'g', ' ', 3, 'Y', 'i', ' ', 3, 'Y', 'i', ' ', 5, 'J', 'i', 'n', 'g', ' ', 3, 'Z', 'i', ' ', 4, 'L', 'a', 'i', ' ', 5, 'D', 'o', 'n', 'g', ' ', 3, 'Q', 'i', ' ', 5, 'C', 'h', 'u', 'n', ' ', 5, 'G', 'e', 'n', 'g', ' ', 3, 'J', 'u', ' ', 3, 'Q', 'u', ' ', 6, 'I', 's', 'u', 'k', 'a', ' ', 12, 'K', 'i', 'k', 'u', 'i', 't', 'a', 'd', 'a', 'k', 'i', ' ', 3, 'J', 'i', ' ', 4, 'S', 'h', 'u', ' ', 4, 'C', 'h', 'i', ' ', 5, 'M', 'i', 'a', 'o', ' ', 4, 'R', 'o', 'u', ' ', 3, 'A', 'n', ' ', 4, 'Q', 'i', 'u', ' ', 3, 'T', 'i', ' ', 3, 'H', 'u', ' ', 3, 'T', 'i', ' ', 2, 'E', ' ', 4, 'J', 'i', 'e', ' ', 4, 'M', 'a', 'o', ' ', 3, 'F', 'u', ' ', 5, 'C', 'h', 'u', 'n', ' ', 3, 'T', 'u', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'H', 'e', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 5, 'P', 'i', 'a', 'n', ' ', 4, 'Y', 'u', 'n', ' ', 4, 'M', 'e', 'i', ' ', 3, 'H', 'u', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'D', 'u', 'n', ' ', 3, 'M', 'u', ' ', 3, 'J', 'u', ' ', 8, 'T', 's', 'u', 'g', 'u', 'm', 'i', ' ', 5, 'C', 'a', 'n', 'g', ' ', 5, 'F', 'a', 'n', 'g', ' ', 3, 'G', 'u', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 5, 'X', 'u', 'a', 'n', ' ', 5, 'W', 'e', 'n', 'g', ' ', 4, 'S', 'h', 'i', ' ', 3, 'H', 'e', ' ', 4, 'C', 'h', 'u', ' ', 5, 'T', 'a', 'n', 'g', ' ', 4, 'X', 'i', 'a', ' ', 4, 'R', 'u', 'o', ' ', 4, 'L', 'i', 'u', ' ', 3, 'J', 'i', ' ', 3, 'G', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'Z', 'h', 'u', 'n', ' ', 4, 'H', 'a', 'n', ' ', 3, 'Z', 'i', ' ', 3, 'Z', 'i', ' ', 3, 'N', 'i', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'J', 'i', ' ', 3, 'L', 'i', ' ', 5, 'T', 'i', 'a', 'n', ' ', 4, 'K', 'o', 'u', ' ', 3, 'T', 'i', ' ', 3, 'T', 'i', ' ', 3, 'N', 'i', ' ', 3, 'T', 'u', ' ', 3, 'M', 'a', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'G', 'a', 'o', ' ', 5, 'T', 'i', 'a', 'n', ' ', 5, 'C', 'h', 'e', 'n', ' ', 3, 'L', 'i', ' ', 6, 'Z', 'h', 'u', 'a', 'n', ' ', 4, 'Z', 'h', 'e', ' ', 3, 'A', 'o', ' ', 4, 'Y', 'a', 'o', ' ', 3, 'Y', 'i', ' ', 3, 'O', 'u', ' ', 4, 'C', 'h', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'L', 'i', 'a', 'o', ' ', 5, 'R', 'o', 'n', 'g', ' ', 4, 'L', 'o', 'u', ' ', 3, 'B', 'i', ' ', 7, 'S', 'h', 'u', 'a', 'n', 'g', ' ', 5, 'Z', 'h', 'u', 'o', ' ', 3, 'Y', 'u', ' ', 3, 'W', 'u', ' ', 4, 'J', 'u', 'e', ' ', 4, 'Y', 'i', 'n', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 3, 'S', 'i', ' ', 5, 'J', 'i', 'a', 'o', ' ', 3, 'Y', 'i', ' ', 4, 'H', 'u', 'a', ' ', 3, 'B', 'i', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 3, 'S', 'u', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 4, 'F', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'o', ' ', 5, 'L', 'i', 'a', 'o', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'K', 'a', 'o', ' ', 4, 'J', 'i', 'u', ' ', 5, 'X', 'i', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'T', 'u', ' ', 4, 'M', 'a', 'i', ' ', 4, 'Z', 'u', 'n', ' ', 3, 'Y', 'u', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 3, 'L', 'u', ' ', 5, 'T', 'u', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'X', 'u', 'e', ' ', 3, 'Y', 'i', ' ', 3, 'P', 'i', ' ', 4, 'S', 'h', 'u', ' ', 4, 'L', 'u', 'o', ' ', 3, 'Q', 'i', ' ', 3, 'Y', 'i', ' ', 3, 'J', 'i', ' ', 4, 'Z', 'h', 'e', ' ', 3, 'Y', 'u', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 3, 'Y', 'e', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 3, 'P', 'i', ' ', 5, 'N', 'i', 'n', 'g', ' ', 4, 'H', 'u', 'o', ' ', 3, 'M', 'i', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 5, 'M', 'e', 'n', 'g', ' ', 3, 'D', 'i', ' ', 4, 'Y', 'u', 'e', ' ', 3, 'Y', 'u', ' ', 4, 'L', 'e', 'i', ' ', 4, 'B', 'a', 'o', ' ', 3, 'L', 'u', ' ', 3, 'H', 'e', ' ', 5, 'L', 'o', 'n', 'g', ' ', 7, 'S', 'h', 'u', 'a', 'n', 'g', ' ', 4, 'Y', 'u', 'e', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 5, 'G', 'u', 'a', 'n', ' ', 3, 'Q', 'u', ' ', 3, 'L', 'i', ' ', 5, 'L', 'u', 'a', 'n', ' ', 5, 'N', 'i', 'a', 'o', ' ', 4, 'J', 'i', 'u', ' ', 3, 'J', 'i', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 5, 'M', 'i', 'n', 'g', ' ', 4, 'S', 'h', 'i', ' ', 3, 'O', 'u', ' ', 3, 'Y', 'a', ' ', 5, 'C', 'a', 'n', 'g', ' ', 4, 'B', 'a', 'o', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 3, 'G', 'u', ' ', 5, 'D', 'o', 'n', 'g', ' ', 3, 'L', 'u', ' ', 3, 'Y', 'a', ' ', 5, 'X', 'i', 'a', 'o', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 5, 'L', 'i', 'n', 'g', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'Q', 'u', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 4, 'X', 'u', 'e', ' ', 4, 'T', 'u', 'o', ' ', 3, 'S', 'i', ' ', 4, 'Z', 'h', 'i', ' ', 3, 'E', 'r', ' ', 4, 'G', 'u', 'a', ' ', 4, 'X', 'i', 'u', ' ', 5, 'H', 'e', 'n', 'g', ' ', 5, 'Z', 'h', 'o', 'u', ' ', 3, 'G', 'e', ' ', 5, 'L', 'u', 'a', 'n', ' ', 5, 'H', 'o', 'n', 'g', ' ', 3, 'W', 'u', ' ', 3, 'B', 'o', ' ', 3, 'L', 'i', ' ', 5, 'J', 'u', 'a', 'n', ' ', 3, 'H', 'u', ' ', 2, 'E', ' ', 3, 'Y', 'u', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'T', 'i', ' ', 3, 'W', 'u', ' ', 4, 'Q', 'u', 'e', ' ', 5, 'M', 'i', 'a', 'o', ' ', 3, 'A', 'n', ' ', 4, 'K', 'u', 'n', ' ', 4, 'B', 'e', 'i', ' ', 5, 'P', 'e', 'n', 'g', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 5, 'C', 'h', 'u', 'n', ' ', 5, 'G', 'e', 'n', 'g', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 3, 'S', 'u', ' ', 3, 'H', 'u', ' ', 3, 'H', 'e', ' ', 2, 'E', ' ', 3, 'G', 'u', ' ', 4, 'Q', 'i', 'u', ' ', 3, 'Z', 'i', ' ', 4, 'M', 'e', 'i', ' ', 3, 'M', 'u', ' ', 3, 'N', 'i', ' ', 4, 'Y', 'a', 'o', ' ', 5, 'W', 'e', 'n', 'g', ' ', 4, 'L', 'i', 'u', ' ', 3, 'J', 'i', ' ', 3, 'N', 'i', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'H', 'e', ' ', 3, 'Y', 'i', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'Z', 'h', 'e', ' ', 5, 'L', 'i', 'a', 'o', ' ', 5, 'L', 'i', 'a', 'o', ' ', 5, 'J', 'i', 'a', 'o', ' ', 4, 'J', 'i', 'u', ' ', 3, 'Y', 'u', ' ', 3, 'L', 'u', ' ', 5, 'X', 'u', 'a', 'n', ' ', 5, 'Z', 'h', 'a', 'n', ' ', 5, 'Y', 'i', 'n', 'g', ' ', 4, 'H', 'u', 'o', ' ', 5, 'M', 'e', 'n', 'g', ' ', 5, 'G', 'u', 'a', 'n', ' ', 7, 'S', 'h', 'u', 'a', 'n', 'g', ' ', 3, 'L', 'u', ' ', 4, 'J', 'i', 'n', ' ', 5, 'L', 'i', 'n', 'g', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'C', 'u', 'o', ' ', 5, 'J', 'i', 'a', 'n', ' ', 5, 'J', 'i', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'C', 'u', 'o', ' ', 3, 'L', 'u', ' ', 4, 'Y', 'o', 'u', ' ', 3, 'C', 'u', ' ', 3, 'J', 'i', ' ', 5, 'B', 'i', 'a', 'o', ' ', 3, 'C', 'u', ' ', 5, 'B', 'i', 'a', 'o', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'J', 'u', 'n', ' ', 4, 'Z', 'h', 'u', ' ', 5, 'J', 'i', 'a', 'n', ' ', 3, 'M', 'i', ' ', 3, 'M', 'i', ' ', 3, 'W', 'u', ' ', 4, 'L', 'i', 'u', ' ', 5, 'C', 'h', 'e', 'n', ' ', 4, 'J', 'u', 'n', ' ', 4, 'L', 'i', 'n', ' ', 3, 'N', 'i', ' ', 3, 'Q', 'i', ' ', 3, 'L', 'u', ' ', 4, 'J', 'i', 'u', ' ', 4, 'J', 'u', 'n', ' ', 5, 'J', 'i', 'n', 'g', ' ', 3, 'L', 'i', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'J', 'i', 'a', ' ', 3, 'M', 'i', ' ', 3, 'L', 'i', ' ', 4, 'S', 'h', 'e', ' ', 6, 'Z', 'h', 'a', 'n', 'g', ' ', 4, 'L', 'i', 'n', ' ', 5, 'J', 'i', 'n', 'g', ' ', 3, 'J', 'i', ' ', 5, 'L', 'i', 'n', 'g', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'C', 'u', ' ', 4, 'M', 'a', 'i', ' ', 4, 'M', 'a', 'i', ' ', 3, 'G', 'e', ' ', 5, 'C', 'h', 'a', 'o', ' ', 3, 'F', 'u', ' ', 5, 'M', 'i', 'a', 'n', ' ', 5, 'M', 'i', 'a', 'n', ' ', 3, 'F', 'u', ' ', 4, 'P', 'a', 'o', ' ', 3, 'Q', 'u', ' ', 3, 'Q', 'u', ' ', 4, 'M', 'o', 'u', ' ', 3, 'F', 'u', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'L', 'a', 'i', ' ', 3, 'Q', 'u', ' ', 5, 'M', 'i', 'a', 'n', ' ', 5, 'F', 'e', 'n', 'g', ' ', 3, 'F', 'u', ' ', 3, 'Q', 'u', ' ', 5, 'M', 'i', 'a', 'n', ' ', 3, 'M', 'a', ' ', 3, 'M', 'o', ' ', 3, 'M', 'o', ' ', 4, 'H', 'u', 'i', ' ', 3, 'M', 'a', ' ', 4, 'Z', 'o', 'u', ' ', 4, 'N', 'e', 'n', ' ', 4, 'F', 'e', 'n', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 6, 'H', 'u', 'a', 'n', 'g', ' ', 4, 'J', 'i', 'n', ' ', 6, 'G', 'u', 'a', 'n', 'g', ' ', 5, 'T', 'i', 'a', 'n', ' ', 4, 'T', 'o', 'u', ' ', 5, 'H', 'e', 'n', 'g', ' ', 3, 'X', 'i', ' ', 6, 'K', 'u', 'a', 'n', 'g', ' ', 5, 'H', 'e', 'n', 'g', ' ', 4, 'S', 'h', 'u', ' ', 3, 'L', 'i', ' ', 5, 'N', 'i', 'a', 'n', ' ', 4, 'C', 'h', 'i', ' ', 4, 'H', 'e', 'i', ' ', 4, 'H', 'e', 'i', ' ', 3, 'Y', 'i', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'D', 'a', 'n', ' ', 3, 'X', 'i', ' ', 5, 'T', 'u', 'a', 'n', ' ', 3, 'M', 'o', ' ', 3, 'M', 'o', ' ', 5, 'Q', 'i', 'a', 'n', ' ', 4, 'D', 'a', 'i', ' ', 4, 'C', 'h', 'u', ' ', 4, 'Y', 'o', 'u', ' ', 5, 'D', 'i', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 4, 'X', 'i', 'a', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'Q', 'u', ' ', 4, 'M', 'e', 'i', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'J', 'i', 'n', 'g', ' ', 3, 'Y', 'u', ' ', 3, 'L', 'i', ' ', 5, 'D', 'a', 'n', 'g', ' ', 3, 'D', 'u', ' ', 4, 'C', 'a', 'n', ' ', 4, 'Y', 'i', 'n', ' ', 3, 'A', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'T', 'a', 'n', ' ', 3, 'A', 'n', ' ', 5, 'Z', 'h', 'e', 'n', ' ', 4, 'D', 'a', 'i', ' ', 4, 'C', 'a', 'n', ' ', 3, 'Y', 'i', ' ', 4, 'M', 'e', 'i', ' ', 4, 'D', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'D', 'u', ' ', 3, 'L', 'u', ' ', 4, 'Z', 'h', 'i', ' ', 4, 'F', 'e', 'n', ' ', 3, 'F', 'u', ' ', 3, 'F', 'u', ' ', 4, 'M', 'i', 'n', ' ', 4, 'M', 'i', 'n', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 3, 'C', 'u', ' ', 3, 'Q', 'u', ' ', 5, 'C', 'h', 'a', 'o', ' ', 3, 'W', 'a', ' ', 4, 'Z', 'h', 'u', ' ', 4, 'Z', 'h', 'i', ' ', 5, 'M', 'a', 'n', 'g', ' ', 3, 'A', 'o', ' ', 4, 'B', 'i', 'e', ' ', 4, 'T', 'u', 'o', ' ', 3, 'B', 'i', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 5, 'C', 'h', 'a', 'o', ' ', 4, 'T', 'u', 'o', ' ', 5, 'D', 'i', 'n', 'g', ' ', 3, 'M', 'i', ' ', 4, 'N', 'a', 'i', ' ', 5, 'D', 'i', 'n', 'g', ' ', 3, 'Z', 'i', ' ', 3, 'G', 'u', ' ', 3, 'G', 'u', ' ', 5, 'D', 'o', 'n', 'g', ' ', 4, 'F', 'e', 'n', ' ', 4, 'T', 'a', 'o', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 3, 'P', 'i', ' ', 6, 'C', 'h', 'a', 'n', 'g', ' ', 4, 'G', 'a', 'o', ' ', 3, 'Q', 'i', ' ', 5, 'Y', 'u', 'a', 'n', ' ', 5, 'T', 'a', 'n', 'g', ' ', 5, 'T', 'e', 'n', 'g', ' ', 4, 'S', 'h', 'u', ' ', 4, 'S', 'h', 'u', ' ', 4, 'F', 'e', 'n', ' ', 4, 'F', 'e', 'i', ' ', 4, 'W', 'e', 'n', ' ', 3, 'B', 'a', ' ', 5, 'D', 'i', 'a', 'o', ' ', 4, 'T', 'u', 'o', ' ', 5, 'T', 'o', 'n', 'g', ' ', 3, 'Q', 'u', ' ', 6, 'S', 'h', 'e', 'n', 'g', ' ', 4, 'S', 'h', 'i', ' ', 4, 'Y', 'o', 'u', ' ', 4, 'S', 'h', 'i', ' ', 5, 'T', 'i', 'n', 'g', ' ', 3, 'W', 'u', ' ', 5, 'N', 'i', 'a', 'n', ' ', 5, 'J', 'i', 'n', 'g', ' ', 4, 'H', 'u', 'n', ' ', 3, 'J', 'u', ' ', 4, 'Y', 'a', 'n', ' ', 3, 'T', 'u', ' ', 3, 'T', 'i', ' ', 3, 'X', 'i', ' ', 5, 'X', 'i', 'a', 'n', ' ', 4, 'Y', 'a', 'n', ' ', 4, 'L', 'e', 'i', ' ', 3, 'B', 'i', ' ', 4, 'Y', 'a', 'o', ' ', 4, 'Q', 'i', 'u', ' ', 4, 'H', 'a', 'n', ' ', 3, 'W', 'u', ' ', 3, 'W', 'u', ' ', 4, 'H', 'o', 'u', ' ', 3, 'X', 'i', ' ', 3, 'G', 'e', ' ', 4, 'Z', 'h', 'a', ' ', 4, 'X', 'i', 'u', ' ', 5, 'W', 'e', 'n', 'g', ' ', 4, 'Z', 'h', 'a', ' ', 5, 'N', 'o', 'n', 'g', ' ', 5, 'N', 'a', 'n', 'g', ' ', 3, 'Q', 'i', ' ', 5, 'Z', 'h', 'a', 'i', ' ', 3, 'J', 'i', ' ', 3, 'Z', 'i', ' ', 3, 'J', 'i', ' ', 3, 'J', 'i', ' ', 3, 'Q', 'i', ' ', 3, 'J', 'i', ' ', 4, 'C', 'h', 'i', ' ', 5, 'C', 'h', 'e', 'n', ' ', 5, 'C', 'h', 'e', 'n', ' ', 3, 'H', 'e', ' ', 3, 'Y', 'a', ' ', 4, 'K', 'e', 'n', ' ', 4, 'X', 'i', 'e', ' ', 4, 'P', 'a', 'o', ' ', 4, 'C', 'u', 'o', ' ', 4, 'S', 'h', 'i', ' ', 3, 'Z', 'i', ' ', 4, 'C', 'h', 'i', ' ', 5, 'N', 'i', 'a', 'n', ' ', 3, 'J', 'u', ' ', 5, 'T', 'i', 'a', 'o', ' ', 5, 'L', 'i', 'n', 'g', ' ', 5, 'L', 'i', 'n', 'g', ' ', 4, 'C', 'h', 'u', ' ', 5, 'Q', 'u', 'a', 'n', ' ', 4, 'X', 'i', 'e', ' ', 4, 'K', 'e', 'n', ' ', 4, 'N', 'i', 'e', ' ', 4, 'J', 'i', 'u', ' ', 4, 'Y', 'a', 'o', ' ', 5, 'C', 'h', 'u', 'o', ' ', 4, 'K', 'u', 'n', ' ', 3, 'Y', 'u', ' ', 4, 'C', 'h', 'u', ' ', 3, 'Y', 'i', ' ', 3, 'N', 'i', ' ', 4, 'C', 'u', 'o', ' ', 4, 'Z', 'o', 'u', ' ', 3, 'Q', 'u', ' ', 4, 'N', 'e', 'n', ' ', 5, 'X', 'i', 'a', 'n', ' ', 3, 'O', 'u', ' ', 2, 'E', ' ', 3, 'W', 'o', ' ', 3, 'Y', 'i', ' ', 5, 'C', 'h', 'u', 'o', ' ', 4, 'Z', 'o', 'u', ' ', 5, 'D', 'i', 'a', 'n', ' ', 4, 'C', 'h', 'u', ' ', 4, 'J', 'i', 'n', ' ', 3, 'Y', 'a', ' ', 4, 'C', 'h', 'i', ' ', 5, 'C', 'h', 'e', 'n', ' ', 3, 'H', 'e', ' ', 4, 'K', 'e', 'n', ' ', 3, 'J', 'u', ' ', 5, 'L', 'i', 'n', 'g', ' ', 4, 'P', 'a', 'o', ' ', 5, 'T', 'i', 'a', 'o', ' ', 3, 'Z', 'i', ' ', 4, 'K', 'e', 'n', ' ', 3, 'Y', 'u', ' ', 5, 'C', 'h', 'u', 'o', ' ', 3, 'Q', 'u', ' ', 3, 'W', 'o', ' ', 5, 'L', 'o', 'n', 'g', ' ', 5, 'P', 'a', 'n', 'g', ' ', 5, 'G', 'o', 'n', 'g', ' ', 5, 'P', 'a', 'n', 'g', ' ', 4, 'Y', 'a', 'n', ' ', 5, 'L', 'o', 'n', 'g', ' ', 5, 'L', 'o', 'n', 'g', ' ', 5, 'G', 'o', 'n', 'g', ' ', 4, 'K', 'a', 'n', ' ', 3, 'T', 'a', ' ', 5, 'L', 'i', 'n', 'g', ' ', 3, 'T', 'a', ' ', 5, 'L', 'o', 'n', 'g', ' ', 5, 'G', 'o', 'n', 'g', ' ', 4, 'K', 'a', 'n', ' ', 4, 'G', 'u', 'i', ' ', 4, 'Q', 'i', 'u', ' ', 4, 'B', 'i', 'e', ' ', 4, 'G', 'u', 'i', ' ', 4, 'Y', 'u', 'e', ' ', 5, 'C', 'h', 'u', 'i', ' ', 3, 'H', 'e', ' ', 4, 'J', 'u', 'e', ' ', 4, 'X', 'i', 'e', ' ', 3, 'Y', 'u', ' ', 2, 'i', 't', 2, 'i', 'x', 1, 'i', 2, 'i', 'p', 3, 'i', 'e', 't', 3, 'i', 'e', 'x', 2, 'i', 'e', 3, 'i', 'e', 'p', 2, 'a', 't', 2, 'a', 'x', 1, 'a', 2, 'a', 'p', 3, 'u', 'o', 'x', 2, 'u', 'o', 3, 'u', 'o', 'p', 2, 'o', 't', 2, 'o', 'x', 1, 'o', 2, 'o', 'p', 2, 'e', 'x', 1, 'e', 2, 'w', 'u', 3, 'b', 'i', 't', 3, 'b', 'i', 'x', 2, 'b', 'i', 3, 'b', 'i', 'p', 4, 'b', 'i', 'e', 't', 4, 'b', 'i', 'e', 'x', 3, 'b', 'i', 'e', 4, 'b', 'i', 'e', 'p', 3, 'b', 'a', 't', 3, 'b', 'a', 'x', 2, 'b', 'a', 3, 'b', 'a', 'p', 4, 'b', 'u', 'o', 'x', 3, 'b', 'u', 'o', 4, 'b', 'u', 'o', 'p', 3, 'b', 'o', 't', 3, 'b', 'o', 'x', 2, 'b', 'o', 3, 'b', 'o', 'p', 3, 'b', 'e', 'x', 2, 'b', 'e', 3, 'b', 'e', 'p', 3, 'b', 'u', 't', 3, 'b', 'u', 'x', 2, 'b', 'u', 3, 'b', 'u', 'p', 4, 'b', 'u', 'r', 'x', 3, 'b', 'u', 'r', 3, 'b', 'y', 't', 3, 'b', 'y', 'x', 2, 'b', 'y', 3, 'b', 'y', 'p', 4, 'b', 'y', 'r', 'x', 3, 'b', 'y', 'r', 3, 'p', 'i', 't', 3, 'p', 'i', 'x', 2, 'p', 'i', 3, 'p', 'i', 'p', 4, 'p', 'i', 'e', 'x', 3, 'p', 'i', 'e', 4, 'p', 'i', 'e', 'p', 3, 'p', 'a', 't', 3, 'p', 'a', 'x', 2, 'p', 'a', 3, 'p', 'a', 'p', 4, 'p', 'u', 'o', 'x', 3, 'p', 'u', 'o', 4, 'p', 'u', 'o', 'p', 3, 'p', 'o', 't', 3, 'p', 'o', 'x', 2, 'p', 'o', 3, 'p', 'o', 'p', 3, 'p', 'u', 't', 3, 'p', 'u', 'x', 2, 'p', 'u', 3, 'p', 'u', 'p', 4, 'p', 'u', 'r', 'x', 3, 'p', 'u', 'r', 3, 'p', 'y', 't', 3, 'p', 'y', 'x', 2, 'p', 'y', 3, 'p', 'y', 'p', 4, 'p', 'y', 'r', 'x', 3, 'p', 'y', 'r', 4, 'b', 'b', 'i', 't', 4, 'b', 'b', 'i', 'x', 3, 'b', 'b', 'i', 4, 'b', 'b', 'i', 'p', 5, 'b', 'b', 'i', 'e', 't', 5, 'b', 'b', 'i', 'e', 'x', 4, 'b', 'b', 'i', 'e', 5, 'b', 'b', 'i', 'e', 'p', 4, 'b', 'b', 'a', 't', 4, 'b', 'b', 'a', 'x', 3, 'b', 'b', 'a', 4, 'b', 'b', 'a', 'p', 5, 'b', 'b', 'u', 'o', 'x', 4, 'b', 'b', 'u', 'o', 5, 'b', 'b', 'u', 'o', 'p', 4, 'b', 'b', 'o', 't', 4, 'b', 'b', 'o', 'x', 3, 'b', 'b', 'o', 4, 'b', 'b', 'o', 'p', 4, 'b', 'b', 'e', 'x', 3, 'b', 'b', 'e', 4, 'b', 'b', 'e', 'p', 4, 'b', 'b', 'u', 't', 4, 'b', 'b', 'u', 'x', 3, 'b', 'b', 'u', 4, 'b', 'b', 'u', 'p', 5, 'b', 'b', 'u', 'r', 'x', 4, 'b', 'b', 'u', 'r', 4, 'b', 'b', 'y', 't', 4, 'b', 'b', 'y', 'x', 3, 'b', 'b', 'y', 4, 'b', 'b', 'y', 'p', 4, 'n', 'b', 'i', 't', 4, 'n', 'b', 'i', 'x', 3, 'n', 'b', 'i', 4, 'n', 'b', 'i', 'p', 5, 'n', 'b', 'i', 'e', 'x', 4, 'n', 'b', 'i', 'e', 5, 'n', 'b', 'i', 'e', 'p', 4, 'n', 'b', 'a', 't', 4, 'n', 'b', 'a', 'x', 3, 'n', 'b', 'a', 4, 'n', 'b', 'a', 'p', 4, 'n', 'b', 'o', 't', 4, 'n', 'b', 'o', 'x', 3, 'n', 'b', 'o', 4, 'n', 'b', 'o', 'p', 4, 'n', 'b', 'u', 't', 4, 'n', 'b', 'u', 'x', 3, 'n', 'b', 'u', 4, 'n', 'b', 'u', 'p', 5, 'n', 'b', 'u', 'r', 'x', 4, 'n', 'b', 'u', 'r', 4, 'n', 'b', 'y', 't', 4, 'n', 'b', 'y', 'x', 3, 'n', 'b', 'y', 4, 'n', 'b', 'y', 'p', 5, 'n', 'b', 'y', 'r', 'x', 4, 'n', 'b', 'y', 'r', 4, 'h', 'm', 'i', 't', 4, 'h', 'm', 'i', 'x', 3, 'h', 'm', 'i', 4, 'h', 'm', 'i', 'p', 5, 'h', 'm', 'i', 'e', 'x', 4, 'h', 'm', 'i', 'e', 5, 'h', 'm', 'i', 'e', 'p', 4, 'h', 'm', 'a', 't', 4, 'h', 'm', 'a', 'x', 3, 'h', 'm', 'a', 4, 'h', 'm', 'a', 'p', 5, 'h', 'm', 'u', 'o', 'x', 4, 'h', 'm', 'u', 'o', 5, 'h', 'm', 'u', 'o', 'p', 4, 'h', 'm', 'o', 't', 4, 'h', 'm', 'o', 'x', 3, 'h', 'm', 'o', 4, 'h', 'm', 'o', 'p', 4, 'h', 'm', 'u', 't', 4, 'h', 'm', 'u', 'x', 3, 'h', 'm', 'u', 4, 'h', 'm', 'u', 'p', 5, 'h', 'm', 'u', 'r', 'x', 4, 'h', 'm', 'u', 'r', 4, 'h', 'm', 'y', 'x', 3, 'h', 'm', 'y', 4, 'h', 'm', 'y', 'p', 5, 'h', 'm', 'y', 'r', 'x', 4, 'h', 'm', 'y', 'r', 3, 'm', 'i', 't', 3, 'm', 'i', 'x', 2, 'm', 'i', 3, 'm', 'i', 'p', 4, 'm', 'i', 'e', 'x', 3, 'm', 'i', 'e', 4, 'm', 'i', 'e', 'p', 3, 'm', 'a', 't', 3, 'm', 'a', 'x', 2, 'm', 'a', 3, 'm', 'a', 'p', 4, 'm', 'u', 'o', 't', 4, 'm', 'u', 'o', 'x', 3, 'm', 'u', 'o', 4, 'm', 'u', 'o', 'p', 3, 'm', 'o', 't', 3, 'm', 'o', 'x', 2, 'm', 'o', 3, 'm', 'o', 'p', 3, 'm', 'e', 'x', 2, 'm', 'e', 3, 'm', 'u', 't', 3, 'm', 'u', 'x', 2, 'm', 'u', 3, 'm', 'u', 'p', 4, 'm', 'u', 'r', 'x', 3, 'm', 'u', 'r', 3, 'm', 'y', 't', 3, 'm', 'y', 'x', 2, 'm', 'y', 3, 'm', 'y', 'p', 3, 'f', 'i', 't', 3, 'f', 'i', 'x', 2, 'f', 'i', 3, 'f', 'i', 'p', 3, 'f', 'a', 't', 3, 'f', 'a', 'x', 2, 'f', 'a', 3, 'f', 'a', 'p', 3, 'f', 'o', 'x', 2, 'f', 'o', 3, 'f', 'o', 'p', 3, 'f', 'u', 't', 3, 'f', 'u', 'x', 2, 'f', 'u', 3, 'f', 'u', 'p', 4, 'f', 'u', 'r', 'x', 3, 'f', 'u', 'r', 3, 'f', 'y', 't', 3, 'f', 'y', 'x', 2, 'f', 'y', 3, 'f', 'y', 'p', 3, 'v', 'i', 't', 3, 'v', 'i', 'x', 2, 'v', 'i', 3, 'v', 'i', 'p', 4, 'v', 'i', 'e', 't', 4, 'v', 'i', 'e', 'x', 3, 'v', 'i', 'e', 4, 'v', 'i', 'e', 'p', 3, 'v', 'a', 't', 3, 'v', 'a', 'x', 2, 'v', 'a', 3, 'v', 'a', 'p', 3, 'v', 'o', 't', 3, 'v', 'o', 'x', 2, 'v', 'o', 3, 'v', 'o', 'p', 3, 'v', 'e', 'x', 3, 'v', 'e', 'p', 3, 'v', 'u', 't', 3, 'v', 'u', 'x', 2, 'v', 'u', 3, 'v', 'u', 'p', 4, 'v', 'u', 'r', 'x', 3, 'v', 'u', 'r', 3, 'v', 'y', 't', 3, 'v', 'y', 'x', 2, 'v', 'y', 3, 'v', 'y', 'p', 4, 'v', 'y', 'r', 'x', 3, 'v', 'y', 'r', 3, 'd', 'i', 't', 3, 'd', 'i', 'x', 2, 'd', 'i', 3, 'd', 'i', 'p', 4, 'd', 'i', 'e', 'x', 3, 'd', 'i', 'e', 4, 'd', 'i', 'e', 'p', 3, 'd', 'a', 't', 3, 'd', 'a', 'x', 2, 'd', 'a', 3, 'd', 'a', 'p', 4, 'd', 'u', 'o', 'x', 3, 'd', 'u', 'o', 3, 'd', 'o', 't', 3, 'd', 'o', 'x', 2, 'd', 'o', 3, 'd', 'o', 'p', 3, 'd', 'e', 'x', 2, 'd', 'e', 3, 'd', 'e', 'p', 3, 'd', 'u', 't', 3, 'd', 'u', 'x', 2, 'd', 'u', 3, 'd', 'u', 'p', 4, 'd', 'u', 'r', 'x', 3, 'd', 'u', 'r', 3, 't', 'i', 't', 3, 't', 'i', 'x', 2, 't', 'i', 3, 't', 'i', 'p', 4, 't', 'i', 'e', 'x', 3, 't', 'i', 'e', 4, 't', 'i', 'e', 'p', 3, 't', 'a', 't', 3, 't', 'a', 'x', 2, 't', 'a', 3, 't', 'a', 'p', 4, 't', 'u', 'o', 't', 4, 't', 'u', 'o', 'x', 3, 't', 'u', 'o', 4, 't', 'u', 'o', 'p', 3, 't', 'o', 't', 3, 't', 'o', 'x', 2, 't', 'o', 3, 't', 'o', 'p', 3, 't', 'e', 'x', 2, 't', 'e', 3, 't', 'e', 'p', 3, 't', 'u', 't', 3, 't', 'u', 'x', 2, 't', 'u', 3, 't', 'u', 'p', 4, 't', 'u', 'r', 'x', 3, 't', 'u', 'r', 4, 'd', 'd', 'i', 't', 4, 'd', 'd', 'i', 'x', 3, 'd', 'd', 'i', 4, 'd', 'd', 'i', 'p', 5, 'd', 'd', 'i', 'e', 'x', 4, 'd', 'd', 'i', 'e', 5, 'd', 'd', 'i', 'e', 'p', 4, 'd', 'd', 'a', 't', 4, 'd', 'd', 'a', 'x', 3, 'd', 'd', 'a', 4, 'd', 'd', 'a', 'p', 5, 'd', 'd', 'u', 'o', 'x', 4, 'd', 'd', 'u', 'o', 5, 'd', 'd', 'u', 'o', 'p', 4, 'd', 'd', 'o', 't', 4, 'd', 'd', 'o', 'x', 3, 'd', 'd', 'o', 4, 'd', 'd', 'o', 'p', 4, 'd', 'd', 'e', 'x', 3, 'd', 'd', 'e', 4, 'd', 'd', 'e', 'p', 4, 'd', 'd', 'u', 't', 4, 'd', 'd', 'u', 'x', 3, 'd', 'd', 'u', 4, 'd', 'd', 'u', 'p', 5, 'd', 'd', 'u', 'r', 'x', 4, 'd', 'd', 'u', 'r', 4, 'n', 'd', 'i', 't', 4, 'n', 'd', 'i', 'x', 3, 'n', 'd', 'i', 4, 'n', 'd', 'i', 'p', 5, 'n', 'd', 'i', 'e', 'x', 4, 'n', 'd', 'i', 'e', 4, 'n', 'd', 'a', 't', 4, 'n', 'd', 'a', 'x', 3, 'n', 'd', 'a', 4, 'n', 'd', 'a', 'p', 4, 'n', 'd', 'o', 't', 4, 'n', 'd', 'o', 'x', 3, 'n', 'd', 'o', 4, 'n', 'd', 'o', 'p', 4, 'n', 'd', 'e', 'x', 3, 'n', 'd', 'e', 4, 'n', 'd', 'e', 'p', 4, 'n', 'd', 'u', 't', 4, 'n', 'd', 'u', 'x', 3, 'n', 'd', 'u', 4, 'n', 'd', 'u', 'p', 5, 'n', 'd', 'u', 'r', 'x', 4, 'n', 'd', 'u', 'r', 4, 'h', 'n', 'i', 't', 4, 'h', 'n', 'i', 'x', 3, 'h', 'n', 'i', 4, 'h', 'n', 'i', 'p', 5, 'h', 'n', 'i', 'e', 't', 5, 'h', 'n', 'i', 'e', 'x', 4, 'h', 'n', 'i', 'e', 5, 'h', 'n', 'i', 'e', 'p', 4, 'h', 'n', 'a', 't', 4, 'h', 'n', 'a', 'x', 3, 'h', 'n', 'a', 4, 'h', 'n', 'a', 'p', 5, 'h', 'n', 'u', 'o', 'x', 4, 'h', 'n', 'u', 'o', 4, 'h', 'n', 'o', 't', 4, 'h', 'n', 'o', 'x', 4, 'h', 'n', 'o', 'p', 4, 'h', 'n', 'e', 'x', 3, 'h', 'n', 'e', 4, 'h', 'n', 'e', 'p', 4, 'h', 'n', 'u', 't', 3, 'n', 'i', 't', 3, 'n', 'i', 'x', 2, 'n', 'i', 3, 'n', 'i', 'p', 4, 'n', 'i', 'e', 'x', 3, 'n', 'i', 'e', 4, 'n', 'i', 'e', 'p', 3, 'n', 'a', 'x', 2, 'n', 'a', 3, 'n', 'a', 'p', 4, 'n', 'u', 'o', 'x', 3, 'n', 'u', 'o', 4, 'n', 'u', 'o', 'p', 3, 'n', 'o', 't', 3, 'n', 'o', 'x', 2, 'n', 'o', 3, 'n', 'o', 'p', 3, 'n', 'e', 'x', 2, 'n', 'e', 3, 'n', 'e', 'p', 3, 'n', 'u', 't', 3, 'n', 'u', 'x', 2, 'n', 'u', 3, 'n', 'u', 'p', 4, 'n', 'u', 'r', 'x', 3, 'n', 'u', 'r', 4, 'h', 'l', 'i', 't', 4, 'h', 'l', 'i', 'x', 3, 'h', 'l', 'i', 4, 'h', 'l', 'i', 'p', 5, 'h', 'l', 'i', 'e', 'x', 4, 'h', 'l', 'i', 'e', 5, 'h', 'l', 'i', 'e', 'p', 4, 'h', 'l', 'a', 't', 4, 'h', 'l', 'a', 'x', 3, 'h', 'l', 'a', 4, 'h', 'l', 'a', 'p', 5, 'h', 'l', 'u', 'o', 'x', 4, 'h', 'l', 'u', 'o', 5, 'h', 'l', 'u', 'o', 'p', 4, 'h', 'l', 'o', 'x', 3, 'h', 'l', 'o', 4, 'h', 'l', 'o', 'p', 4, 'h', 'l', 'e', 'x', 3, 'h', 'l', 'e', 4, 'h', 'l', 'e', 'p', 4, 'h', 'l', 'u', 't', 4, 'h', 'l', 'u', 'x', 3, 'h', 'l', 'u', 4, 'h', 'l', 'u', 'p', 5, 'h', 'l', 'u', 'r', 'x', 4, 'h', 'l', 'u', 'r', 4, 'h', 'l', 'y', 't', 4, 'h', 'l', 'y', 'x', 3, 'h', 'l', 'y', 4, 'h', 'l', 'y', 'p', 5, 'h', 'l', 'y', 'r', 'x', 4, 'h', 'l', 'y', 'r', 3, 'l', 'i', 't', 3, 'l', 'i', 'x', 2, 'l', 'i', 3, 'l', 'i', 'p', 4, 'l', 'i', 'e', 't', 4, 'l', 'i', 'e', 'x', 3, 'l', 'i', 'e', 4, 'l', 'i', 'e', 'p', 3, 'l', 'a', 't', 3, 'l', 'a', 'x', 2, 'l', 'a', 3, 'l', 'a', 'p', 4, 'l', 'u', 'o', 't', 4, 'l', 'u', 'o', 'x', 3, 'l', 'u', 'o', 4, 'l', 'u', 'o', 'p', 3, 'l', 'o', 't', 3, 'l', 'o', 'x', 2, 'l', 'o', 3, 'l', 'o', 'p', 3, 'l', 'e', 'x', 2, 'l', 'e', 3, 'l', 'e', 'p', 3, 'l', 'u', 't', 3, 'l', 'u', 'x', 2, 'l', 'u', 3, 'l', 'u', 'p', 4, 'l', 'u', 'r', 'x', 3, 'l', 'u', 'r', 3, 'l', 'y', 't', 3, 'l', 'y', 'x', 2, 'l', 'y', 3, 'l', 'y', 'p', 4, 'l', 'y', 'r', 'x', 3, 'l', 'y', 'r', 3, 'g', 'i', 't', 3, 'g', 'i', 'x', 2, 'g', 'i', 3, 'g', 'i', 'p', 4, 'g', 'i', 'e', 't', 4, 'g', 'i', 'e', 'x', 3, 'g', 'i', 'e', 4, 'g', 'i', 'e', 'p', 3, 'g', 'a', 't', 3, 'g', 'a', 'x', 2, 'g', 'a', 3, 'g', 'a', 'p', 4, 'g', 'u', 'o', 't', 4, 'g', 'u', 'o', 'x', 3, 'g', 'u', 'o', 4, 'g', 'u', 'o', 'p', 3, 'g', 'o', 't', 3, 'g', 'o', 'x', 2, 'g', 'o', 3, 'g', 'o', 'p', 3, 'g', 'e', 't', 3, 'g', 'e', 'x', 2, 'g', 'e', 3, 'g', 'e', 'p', 3, 'g', 'u', 't', 3, 'g', 'u', 'x', 2, 'g', 'u', 3, 'g', 'u', 'p', 4, 'g', 'u', 'r', 'x', 3, 'g', 'u', 'r', 3, 'k', 'i', 't', 3, 'k', 'i', 'x', 2, 'k', 'i', 3, 'k', 'i', 'p', 4, 'k', 'i', 'e', 'x', 3, 'k', 'i', 'e', 4, 'k', 'i', 'e', 'p', 3, 'k', 'a', 't', 3, 'k', 'a', 'x', 2, 'k', 'a', 3, 'k', 'a', 'p', 4, 'k', 'u', 'o', 'x', 3, 'k', 'u', 'o', 4, 'k', 'u', 'o', 'p', 3, 'k', 'o', 't', 3, 'k', 'o', 'x', 2, 'k', 'o', 3, 'k', 'o', 'p', 3, 'k', 'e', 't', 3, 'k', 'e', 'x', 2, 'k', 'e', 3, 'k', 'e', 'p', 3, 'k', 'u', 't', 3, 'k', 'u', 'x', 2, 'k', 'u', 3, 'k', 'u', 'p', 4, 'k', 'u', 'r', 'x', 3, 'k', 'u', 'r', 4, 'g', 'g', 'i', 't', 4, 'g', 'g', 'i', 'x', 3, 'g', 'g', 'i', 5, 'g', 'g', 'i', 'e', 'x', 4, 'g', 'g', 'i', 'e', 5, 'g', 'g', 'i', 'e', 'p', 4, 'g', 'g', 'a', 't', 4, 'g', 'g', 'a', 'x', 3, 'g', 'g', 'a', 4, 'g', 'g', 'a', 'p', 5, 'g', 'g', 'u', 'o', 't', 5, 'g', 'g', 'u', 'o', 'x', 4, 'g', 'g', 'u', 'o', 5, 'g', 'g', 'u', 'o', 'p', 4, 'g', 'g', 'o', 't', 4, 'g', 'g', 'o', 'x', 3, 'g', 'g', 'o', 4, 'g', 'g', 'o', 'p', 4, 'g', 'g', 'e', 't', 4, 'g', 'g', 'e', 'x', 3, 'g', 'g', 'e', 4, 'g', 'g', 'e', 'p', 4, 'g', 'g', 'u', 't', 4, 'g', 'g', 'u', 'x', 3, 'g', 'g', 'u', 4, 'g', 'g', 'u', 'p', 5, 'g', 'g', 'u', 'r', 'x', 4, 'g', 'g', 'u', 'r', 5, 'm', 'g', 'i', 'e', 'x', 4, 'm', 'g', 'i', 'e', 4, 'm', 'g', 'a', 't', 4, 'm', 'g', 'a', 'x', 3, 'm', 'g', 'a', 4, 'm', 'g', 'a', 'p', 5, 'm', 'g', 'u', 'o', 'x', 4, 'm', 'g', 'u', 'o', 5, 'm', 'g', 'u', 'o', 'p', 4, 'm', 'g', 'o', 't', 4, 'm', 'g', 'o', 'x', 3, 'm', 'g', 'o', 4, 'm', 'g', 'o', 'p', 4, 'm', 'g', 'e', 'x', 3, 'm', 'g', 'e', 4, 'm', 'g', 'e', 'p', 4, 'm', 'g', 'u', 't', 4, 'm', 'g', 'u', 'x', 3, 'm', 'g', 'u', 4, 'm', 'g', 'u', 'p', 5, 'm', 'g', 'u', 'r', 'x', 4, 'm', 'g', 'u', 'r', 4, 'h', 'x', 'i', 't', 4, 'h', 'x', 'i', 'x', 3, 'h', 'x', 'i', 4, 'h', 'x', 'i', 'p', 5, 'h', 'x', 'i', 'e', 't', 5, 'h', 'x', 'i', 'e', 'x', 4, 'h', 'x', 'i', 'e', 5, 'h', 'x', 'i', 'e', 'p', 4, 'h', 'x', 'a', 't', 4, 'h', 'x', 'a', 'x', 3, 'h', 'x', 'a', 4, 'h', 'x', 'a', 'p', 5, 'h', 'x', 'u', 'o', 't', 5, 'h', 'x', 'u', 'o', 'x', 4, 'h', 'x', 'u', 'o', 5, 'h', 'x', 'u', 'o', 'p', 4, 'h', 'x', 'o', 't', 4, 'h', 'x', 'o', 'x', 3, 'h', 'x', 'o', 4, 'h', 'x', 'o', 'p', 4, 'h', 'x', 'e', 'x', 3, 'h', 'x', 'e', 4, 'h', 'x', 'e', 'p', 5, 'n', 'g', 'i', 'e', 'x', 4, 'n', 'g', 'i', 'e', 5, 'n', 'g', 'i', 'e', 'p', 4, 'n', 'g', 'a', 't', 4, 'n', 'g', 'a', 'x', 3, 'n', 'g', 'a', 4, 'n', 'g', 'a', 'p', 5, 'n', 'g', 'u', 'o', 't', 5, 'n', 'g', 'u', 'o', 'x', 4, 'n', 'g', 'u', 'o', 4, 'n', 'g', 'o', 't', 4, 'n', 'g', 'o', 'x', 3, 'n', 'g', 'o', 4, 'n', 'g', 'o', 'p', 4, 'n', 'g', 'e', 'x', 3, 'n', 'g', 'e', 4, 'n', 'g', 'e', 'p', 3, 'h', 'i', 't', 4, 'h', 'i', 'e', 'x', 3, 'h', 'i', 'e', 3, 'h', 'a', 't', 3, 'h', 'a', 'x', 2, 'h', 'a', 3, 'h', 'a', 'p', 4, 'h', 'u', 'o', 't', 4, 'h', 'u', 'o', 'x', 3, 'h', 'u', 'o', 4, 'h', 'u', 'o', 'p', 3, 'h', 'o', 't', 3, 'h', 'o', 'x', 2, 'h', 'o', 3, 'h', 'o', 'p', 3, 'h', 'e', 'x', 2, 'h', 'e', 3, 'h', 'e', 'p', 3, 'w', 'a', 't', 3, 'w', 'a', 'x', 2, 'w', 'a', 3, 'w', 'a', 'p', 4, 'w', 'u', 'o', 'x', 3, 'w', 'u', 'o', 4, 'w', 'u', 'o', 'p', 3, 'w', 'o', 'x', 2, 'w', 'o', 3, 'w', 'o', 'p', 3, 'w', 'e', 'x', 2, 'w', 'e', 3, 'w', 'e', 'p', 3, 'z', 'i', 't', 3, 'z', 'i', 'x', 2, 'z', 'i', 3, 'z', 'i', 'p', 4, 'z', 'i', 'e', 'x', 3, 'z', 'i', 'e', 4, 'z', 'i', 'e', 'p', 3, 'z', 'a', 't', 3, 'z', 'a', 'x', 2, 'z', 'a', 3, 'z', 'a', 'p', 4, 'z', 'u', 'o', 'x', 3, 'z', 'u', 'o', 4, 'z', 'u', 'o', 'p', 3, 'z', 'o', 't', 3, 'z', 'o', 'x', 2, 'z', 'o', 3, 'z', 'o', 'p', 3, 'z', 'e', 'x', 2, 'z', 'e', 3, 'z', 'e', 'p', 3, 'z', 'u', 't', 3, 'z', 'u', 'x', 2, 'z', 'u', 3, 'z', 'u', 'p', 4, 'z', 'u', 'r', 'x', 3, 'z', 'u', 'r', 3, 'z', 'y', 't', 3, 'z', 'y', 'x', 2, 'z', 'y', 3, 'z', 'y', 'p', 4, 'z', 'y', 'r', 'x', 3, 'z', 'y', 'r', 3, 'c', 'i', 't', 3, 'c', 'i', 'x', 2, 'c', 'i', 3, 'c', 'i', 'p', 4, 'c', 'i', 'e', 't', 4, 'c', 'i', 'e', 'x', 3, 'c', 'i', 'e', 4, 'c', 'i', 'e', 'p', 3, 'c', 'a', 't', 3, 'c', 'a', 'x', 2, 'c', 'a', 3, 'c', 'a', 'p', 4, 'c', 'u', 'o', 'x', 3, 'c', 'u', 'o', 4, 'c', 'u', 'o', 'p', 3, 'c', 'o', 't', 3, 'c', 'o', 'x', 2, 'c', 'o', 3, 'c', 'o', 'p', 3, 'c', 'e', 'x', 2, 'c', 'e', 3, 'c', 'e', 'p', 3, 'c', 'u', 't', 3, 'c', 'u', 'x', 2, 'c', 'u', 3, 'c', 'u', 'p', 4, 'c', 'u', 'r', 'x', 3, 'c', 'u', 'r', 3, 'c', 'y', 't', 3, 'c', 'y', 'x', 2, 'c', 'y', 3, 'c', 'y', 'p', 4, 'c', 'y', 'r', 'x', 3, 'c', 'y', 'r', 4, 'z', 'z', 'i', 't', 4, 'z', 'z', 'i', 'x', 3, 'z', 'z', 'i', 4, 'z', 'z', 'i', 'p', 5, 'z', 'z', 'i', 'e', 't', 5, 'z', 'z', 'i', 'e', 'x', 4, 'z', 'z', 'i', 'e', 5, 'z', 'z', 'i', 'e', 'p', 4, 'z', 'z', 'a', 't', 4, 'z', 'z', 'a', 'x', 3, 'z', 'z', 'a', 4, 'z', 'z', 'a', 'p', 4, 'z', 'z', 'o', 'x', 3, 'z', 'z', 'o', 4, 'z', 'z', 'o', 'p', 4, 'z', 'z', 'e', 'x', 3, 'z', 'z', 'e', 4, 'z', 'z', 'e', 'p', 4, 'z', 'z', 'u', 'x', 3, 'z', 'z', 'u', 4, 'z', 'z', 'u', 'p', 5, 'z', 'z', 'u', 'r', 'x', 4, 'z', 'z', 'u', 'r', 4, 'z', 'z', 'y', 't', 4, 'z', 'z', 'y', 'x', 3, 'z', 'z', 'y', 4, 'z', 'z', 'y', 'p', 5, 'z', 'z', 'y', 'r', 'x', 4, 'z', 'z', 'y', 'r', 4, 'n', 'z', 'i', 't', 4, 'n', 'z', 'i', 'x', 3, 'n', 'z', 'i', 4, 'n', 'z', 'i', 'p', 5, 'n', 'z', 'i', 'e', 'x', 4, 'n', 'z', 'i', 'e', 5, 'n', 'z', 'i', 'e', 'p', 4, 'n', 'z', 'a', 't', 4, 'n', 'z', 'a', 'x', 3, 'n', 'z', 'a', 4, 'n', 'z', 'a', 'p', 5, 'n', 'z', 'u', 'o', 'x', 4, 'n', 'z', 'u', 'o', 4, 'n', 'z', 'o', 'x', 4, 'n', 'z', 'o', 'p', 4, 'n', 'z', 'e', 'x', 3, 'n', 'z', 'e', 4, 'n', 'z', 'u', 'x', 3, 'n', 'z', 'u', 4, 'n', 'z', 'u', 'p', 5, 'n', 'z', 'u', 'r', 'x', 4, 'n', 'z', 'u', 'r', 4, 'n', 'z', 'y', 't', 4, 'n', 'z', 'y', 'x', 3, 'n', 'z', 'y', 4, 'n', 'z', 'y', 'p', 5, 'n', 'z', 'y', 'r', 'x', 4, 'n', 'z', 'y', 'r', 3, 's', 'i', 't', 3, 's', 'i', 'x', 2, 's', 'i', 3, 's', 'i', 'p', 4, 's', 'i', 'e', 'x', 3, 's', 'i', 'e', 4, 's', 'i', 'e', 'p', 3, 's', 'a', 't', 3, 's', 'a', 'x', 2, 's', 'a', 3, 's', 'a', 'p', 4, 's', 'u', 'o', 'x', 3, 's', 'u', 'o', 4, 's', 'u', 'o', 'p', 3, 's', 'o', 't', 3, 's', 'o', 'x', 2, 's', 'o', 3, 's', 'o', 'p', 3, 's', 'e', 'x', 2, 's', 'e', 3, 's', 'e', 'p', 3, 's', 'u', 't', 3, 's', 'u', 'x', 2, 's', 'u', 3, 's', 'u', 'p', 4, 's', 'u', 'r', 'x', 3, 's', 'u', 'r', 3, 's', 'y', 't', 3, 's', 'y', 'x', 2, 's', 'y', 3, 's', 'y', 'p', 4, 's', 'y', 'r', 'x', 3, 's', 'y', 'r', 4, 's', 's', 'i', 't', 4, 's', 's', 'i', 'x', 3, 's', 's', 'i', 4, 's', 's', 'i', 'p', 5, 's', 's', 'i', 'e', 'x', 4, 's', 's', 'i', 'e', 5, 's', 's', 'i', 'e', 'p', 4, 's', 's', 'a', 't', 4, 's', 's', 'a', 'x', 3, 's', 's', 'a', 4, 's', 's', 'a', 'p', 4, 's', 's', 'o', 't', 4, 's', 's', 'o', 'x', 3, 's', 's', 'o', 4, 's', 's', 'o', 'p', 4, 's', 's', 'e', 'x', 3, 's', 's', 'e', 4, 's', 's', 'e', 'p', 4, 's', 's', 'u', 't', 4, 's', 's', 'u', 'x', 3, 's', 's', 'u', 4, 's', 's', 'u', 'p', 4, 's', 's', 'y', 't', 4, 's', 's', 'y', 'x', 3, 's', 's', 'y', 4, 's', 's', 'y', 'p', 5, 's', 's', 'y', 'r', 'x', 4, 's', 's', 'y', 'r', 4, 'z', 'h', 'a', 't', 4, 'z', 'h', 'a', 'x', 3, 'z', 'h', 'a', 4, 'z', 'h', 'a', 'p', 5, 'z', 'h', 'u', 'o', 'x', 4, 'z', 'h', 'u', 'o', 5, 'z', 'h', 'u', 'o', 'p', 4, 'z', 'h', 'o', 't', 4, 'z', 'h', 'o', 'x', 3, 'z', 'h', 'o', 4, 'z', 'h', 'o', 'p', 4, 'z', 'h', 'e', 't', 4, 'z', 'h', 'e', 'x', 3, 'z', 'h', 'e', 4, 'z', 'h', 'e', 'p', 4, 'z', 'h', 'u', 't', 4, 'z', 'h', 'u', 'x', 3, 'z', 'h', 'u', 4, 'z', 'h', 'u', 'p', 5, 'z', 'h', 'u', 'r', 'x', 4, 'z', 'h', 'u', 'r', 4, 'z', 'h', 'y', 't', 4, 'z', 'h', 'y', 'x', 3, 'z', 'h', 'y', 4, 'z', 'h', 'y', 'p', 5, 'z', 'h', 'y', 'r', 'x', 4, 'z', 'h', 'y', 'r', 4, 'c', 'h', 'a', 't', 4, 'c', 'h', 'a', 'x', 3, 'c', 'h', 'a', 4, 'c', 'h', 'a', 'p', 5, 'c', 'h', 'u', 'o', 't', 5, 'c', 'h', 'u', 'o', 'x', 4, 'c', 'h', 'u', 'o', 5, 'c', 'h', 'u', 'o', 'p', 4, 'c', 'h', 'o', 't', 4, 'c', 'h', 'o', 'x', 3, 'c', 'h', 'o', 4, 'c', 'h', 'o', 'p', 4, 'c', 'h', 'e', 't', 4, 'c', 'h', 'e', 'x', 3, 'c', 'h', 'e', 4, 'c', 'h', 'e', 'p', 4, 'c', 'h', 'u', 'x', 3, 'c', 'h', 'u', 4, 'c', 'h', 'u', 'p', 5, 'c', 'h', 'u', 'r', 'x', 4, 'c', 'h', 'u', 'r', 4, 'c', 'h', 'y', 't', 4, 'c', 'h', 'y', 'x', 3, 'c', 'h', 'y', 4, 'c', 'h', 'y', 'p', 5, 'c', 'h', 'y', 'r', 'x', 4, 'c', 'h', 'y', 'r', 4, 'r', 'r', 'a', 'x', 3, 'r', 'r', 'a', 5, 'r', 'r', 'u', 'o', 'x', 4, 'r', 'r', 'u', 'o', 4, 'r', 'r', 'o', 't', 4, 'r', 'r', 'o', 'x', 3, 'r', 'r', 'o', 4, 'r', 'r', 'o', 'p', 4, 'r', 'r', 'e', 't', 4, 'r', 'r', 'e', 'x', 3, 'r', 'r', 'e', 4, 'r', 'r', 'e', 'p', 4, 'r', 'r', 'u', 't', 4, 'r', 'r', 'u', 'x', 3, 'r', 'r', 'u', 4, 'r', 'r', 'u', 'p', 5, 'r', 'r', 'u', 'r', 'x', 4, 'r', 'r', 'u', 'r', 4, 'r', 'r', 'y', 't', 4, 'r', 'r', 'y', 'x', 3, 'r', 'r', 'y', 4, 'r', 'r', 'y', 'p', 5, 'r', 'r', 'y', 'r', 'x', 4, 'r', 'r', 'y', 'r', 4, 'n', 'r', 'a', 't', 4, 'n', 'r', 'a', 'x', 3, 'n', 'r', 'a', 4, 'n', 'r', 'a', 'p', 4, 'n', 'r', 'o', 'x', 3, 'n', 'r', 'o', 4, 'n', 'r', 'o', 'p', 4, 'n', 'r', 'e', 't', 4, 'n', 'r', 'e', 'x', 3, 'n', 'r', 'e', 4, 'n', 'r', 'e', 'p', 4, 'n', 'r', 'u', 't', 4, 'n', 'r', 'u', 'x', 3, 'n', 'r', 'u', 4, 'n', 'r', 'u', 'p', 5, 'n', 'r', 'u', 'r', 'x', 4, 'n', 'r', 'u', 'r', 4, 'n', 'r', 'y', 't', 4, 'n', 'r', 'y', 'x', 3, 'n', 'r', 'y', 4, 'n', 'r', 'y', 'p', 5, 'n', 'r', 'y', 'r', 'x', 4, 'n', 'r', 'y', 'r', 4, 's', 'h', 'a', 't', 4, 's', 'h', 'a', 'x', 3, 's', 'h', 'a', 4, 's', 'h', 'a', 'p', 5, 's', 'h', 'u', 'o', 'x', 4, 's', 'h', 'u', 'o', 5, 's', 'h', 'u', 'o', 'p', 4, 's', 'h', 'o', 't', 4, 's', 'h', 'o', 'x', 3, 's', 'h', 'o', 4, 's', 'h', 'o', 'p', 4, 's', 'h', 'e', 't', 4, 's', 'h', 'e', 'x', 3, 's', 'h', 'e', 4, 's', 'h', 'e', 'p', 4, 's', 'h', 'u', 't', 4, 's', 'h', 'u', 'x', 3, 's', 'h', 'u', 4, 's', 'h', 'u', 'p', 5, 's', 'h', 'u', 'r', 'x', 4, 's', 'h', 'u', 'r', 4, 's', 'h', 'y', 't', 4, 's', 'h', 'y', 'x', 3, 's', 'h', 'y', 4, 's', 'h', 'y', 'p', 5, 's', 'h', 'y', 'r', 'x', 4, 's', 'h', 'y', 'r', 3, 'r', 'a', 't', 3, 'r', 'a', 'x', 2, 'r', 'a', 3, 'r', 'a', 'p', 4, 'r', 'u', 'o', 'x', 3, 'r', 'u', 'o', 4, 'r', 'u', 'o', 'p', 3, 'r', 'o', 't', 3, 'r', 'o', 'x', 2, 'r', 'o', 3, 'r', 'o', 'p', 3, 'r', 'e', 'x', 2, 'r', 'e', 3, 'r', 'e', 'p', 3, 'r', 'u', 't', 3, 'r', 'u', 'x', 2, 'r', 'u', 3, 'r', 'u', 'p', 4, 'r', 'u', 'r', 'x', 3, 'r', 'u', 'r', 3, 'r', 'y', 't', 3, 'r', 'y', 'x', 2, 'r', 'y', 3, 'r', 'y', 'p', 4, 'r', 'y', 'r', 'x', 3, 'r', 'y', 'r', 3, 'j', 'i', 't', 3, 'j', 'i', 'x', 2, 'j', 'i', 3, 'j', 'i', 'p', 4, 'j', 'i', 'e', 't', 4, 'j', 'i', 'e', 'x', 3, 'j', 'i', 'e', 4, 'j', 'i', 'e', 'p', 4, 'j', 'u', 'o', 't', 4, 'j', 'u', 'o', 'x', 3, 'j', 'u', 'o', 4, 'j', 'u', 'o', 'p', 3, 'j', 'o', 't', 3, 'j', 'o', 'x', 2, 'j', 'o', 3, 'j', 'o', 'p', 3, 'j', 'u', 't', 3, 'j', 'u', 'x', 2, 'j', 'u', 3, 'j', 'u', 'p', 4, 'j', 'u', 'r', 'x', 3, 'j', 'u', 'r', 3, 'j', 'y', 't', 3, 'j', 'y', 'x', 2, 'j', 'y', 3, 'j', 'y', 'p', 4, 'j', 'y', 'r', 'x', 3, 'j', 'y', 'r', 3, 'q', 'i', 't', 3, 'q', 'i', 'x', 2, 'q', 'i', 3, 'q', 'i', 'p', 4, 'q', 'i', 'e', 't', 4, 'q', 'i', 'e', 'x', 3, 'q', 'i', 'e', 4, 'q', 'i', 'e', 'p', 4, 'q', 'u', 'o', 't', 4, 'q', 'u', 'o', 'x', 3, 'q', 'u', 'o', 4, 'q', 'u', 'o', 'p', 3, 'q', 'o', 't', 3, 'q', 'o', 'x', 2, 'q', 'o', 3, 'q', 'o', 'p', 3, 'q', 'u', 't', 3, 'q', 'u', 'x', 2, 'q', 'u', 3, 'q', 'u', 'p', 4, 'q', 'u', 'r', 'x', 3, 'q', 'u', 'r', 3, 'q', 'y', 't', 3, 'q', 'y', 'x', 2, 'q', 'y', 3, 'q', 'y', 'p', 4, 'q', 'y', 'r', 'x', 3, 'q', 'y', 'r', 4, 'j', 'j', 'i', 't', 4, 'j', 'j', 'i', 'x', 3, 'j', 'j', 'i', 4, 'j', 'j', 'i', 'p', 5, 'j', 'j', 'i', 'e', 't', 5, 'j', 'j', 'i', 'e', 'x', 4, 'j', 'j', 'i', 'e', 5, 'j', 'j', 'i', 'e', 'p', 5, 'j', 'j', 'u', 'o', 'x', 4, 'j', 'j', 'u', 'o', 5, 'j', 'j', 'u', 'o', 'p', 4, 'j', 'j', 'o', 't', 4, 'j', 'j', 'o', 'x', 3, 'j', 'j', 'o', 4, 'j', 'j', 'o', 'p', 4, 'j', 'j', 'u', 't', 4, 'j', 'j', 'u', 'x', 3, 'j', 'j', 'u', 4, 'j', 'j', 'u', 'p', 5, 'j', 'j', 'u', 'r', 'x', 4, 'j', 'j', 'u', 'r', 4, 'j', 'j', 'y', 't', 4, 'j', 'j', 'y', 'x', 3, 'j', 'j', 'y', 4, 'j', 'j', 'y', 'p', 4, 'n', 'j', 'i', 't', 4, 'n', 'j', 'i', 'x', 3, 'n', 'j', 'i', 4, 'n', 'j', 'i', 'p', 5, 'n', 'j', 'i', 'e', 't', 5, 'n', 'j', 'i', 'e', 'x', 4, 'n', 'j', 'i', 'e', 5, 'n', 'j', 'i', 'e', 'p', 5, 'n', 'j', 'u', 'o', 'x', 4, 'n', 'j', 'u', 'o', 4, 'n', 'j', 'o', 't', 4, 'n', 'j', 'o', 'x', 3, 'n', 'j', 'o', 4, 'n', 'j', 'o', 'p', 4, 'n', 'j', 'u', 'x', 3, 'n', 'j', 'u', 4, 'n', 'j', 'u', 'p', 5, 'n', 'j', 'u', 'r', 'x', 4, 'n', 'j', 'u', 'r', 4, 'n', 'j', 'y', 't', 4, 'n', 'j', 'y', 'x', 3, 'n', 'j', 'y', 4, 'n', 'j', 'y', 'p', 5, 'n', 'j', 'y', 'r', 'x', 4, 'n', 'j', 'y', 'r', 4, 'n', 'y', 'i', 't', 4, 'n', 'y', 'i', 'x', 3, 'n', 'y', 'i', 4, 'n', 'y', 'i', 'p', 5, 'n', 'y', 'i', 'e', 't', 5, 'n', 'y', 'i', 'e', 'x', 4, 'n', 'y', 'i', 'e', 5, 'n', 'y', 'i', 'e', 'p', 5, 'n', 'y', 'u', 'o', 'x', 4, 'n', 'y', 'u', 'o', 5, 'n', 'y', 'u', 'o', 'p', 4, 'n', 'y', 'o', 't', 4, 'n', 'y', 'o', 'x', 3, 'n', 'y', 'o', 4, 'n', 'y', 'o', 'p', 4, 'n', 'y', 'u', 't', 4, 'n', 'y', 'u', 'x', 3, 'n', 'y', 'u', 4, 'n', 'y', 'u', 'p', 3, 'x', 'i', 't', 3, 'x', 'i', 'x', 2, 'x', 'i', 3, 'x', 'i', 'p', 4, 'x', 'i', 'e', 't', 4, 'x', 'i', 'e', 'x', 3, 'x', 'i', 'e', 4, 'x', 'i', 'e', 'p', 4, 'x', 'u', 'o', 'x', 3, 'x', 'u', 'o', 3, 'x', 'o', 't', 3, 'x', 'o', 'x', 2, 'x', 'o', 3, 'x', 'o', 'p', 3, 'x', 'y', 't', 3, 'x', 'y', 'x', 2, 'x', 'y', 3, 'x', 'y', 'p', 4, 'x', 'y', 'r', 'x', 3, 'x', 'y', 'r', 3, 'y', 'i', 't', 3, 'y', 'i', 'x', 2, 'y', 'i', 3, 'y', 'i', 'p', 4, 'y', 'i', 'e', 't', 4, 'y', 'i', 'e', 'x', 3, 'y', 'i', 'e', 4, 'y', 'i', 'e', 'p', 4, 'y', 'u', 'o', 't', 4, 'y', 'u', 'o', 'x', 3, 'y', 'u', 'o', 4, 'y', 'u', 'o', 'p', 3, 'y', 'o', 't', 3, 'y', 'o', 'x', 2, 'y', 'o', 3, 'y', 'o', 'p', 3, 'y', 'u', 't', 3, 'y', 'u', 'x', 2, 'y', 'u', 3, 'y', 'u', 'p', 4, 'y', 'u', 'r', 'x', 3, 'y', 'u', 'r', 3, 'y', 'y', 't', 3, 'y', 'y', 'x', 2, 'y', 'y', 3, 'y', 'y', 'p', 4, 'y', 'y', 'r', 'x', 3, 'y', 'y', 'r', 3, 'Q', 'o', 't', 2, 'L', 'i', 3, 'K', 'i', 't', 4, 'N', 'y', 'i', 'p', 3, 'C', 'y', 'p', 3, 'S', 's', 'i', 4, 'G', 'g', 'o', 'p', 3, 'G', 'e', 'p', 2, 'M', 'i', 4, 'H', 'x', 'i', 't', 3, 'L', 'y', 'r', 4, 'B', 'b', 'u', 't', 3, 'M', 'o', 'p', 2, 'Y', 'o', 3, 'P', 'u', 't', 4, 'H', 'x', 'u', 'o', 3, 'T', 'a', 't', 2, 'G', 'a', 4, 'D', 'd', 'u', 'r', 3, 'B', 'u', 'r', 4, 'G', 'g', 'u', 'o', 4, 'N', 'y', 'o', 'p', 2, 'T', 'u', 2, 'O', 'p', 4, 'J', 'j', 'u', 't', 3, 'Z', 'o', 't', 3, 'P', 'y', 't', 3, 'H', 'm', 'o', 3, 'Y', 'i', 't', 3, 'V', 'u', 'r', 3, 'S', 'h', 'y', 3, 'V', 'e', 'p', 2, 'Z', 'a', 2, 'J', 'o', 3, 'J', 'j', 'y', 3, 'G', 'o', 't', 4, 'J', 'j', 'i', 'e', 2, 'W', 'o', 2, 'D', 'u', 4, 'S', 'h', 'u', 'r', 3, 'L', 'i', 'e', 2, 'C', 'y', 4, 'C', 'u', 'o', 'p', 3, 'C', 'i', 'p', 4, 'H', 'x', 'o', 'p', 4, 'S', 'h', 'a', 't', 4, 'S', 'h', 'o', 'p', 3, 'C', 'h', 'e', 5, 'Z', 'z', 'i', 'e', 't', 2, 'K', 'e', 2, 'g', 'a', 3, 'g', 'a', 'g', 4, 'g', 'a', 'g', 'g', 4, 'g', 'a', 'g', 's', 3, 'g', 'a', 'n', 4, 'g', 'a', 'n', 'j', 4, 'g', 'a', 'n', 'h', 3, 'g', 'a', 'd', 3, 'g', 'a', 'l', 4, 'g', 'a', 'l', 'g', 4, 'g', 'a', 'l', 'm', 4, 'g', 'a', 'l', 'b', 4, 'g', 'a', 'l', 's', 4, 'g', 'a', 'l', 't', 4, 'g', 'a', 'l', 'p', 4, 'g', 'a', 'l', 'h', 3, 'g', 'a', 'm', 3, 'g', 'a', 'b', 4, 'g', 'a', 'b', 's', 3, 'g', 'a', 's', 4, 'g', 'a', 's', 's', 4, 'g', 'a', 'n', 'g', 3, 'g', 'a', 'j', 3, 'g', 'a', 'c', 3, 'g', 'a', 'k', 3, 'g', 'a', 't', 3, 'g', 'a', 'p', 3, 'g', 'a', 'h', 3, 'g', 'a', 'e', 4, 'g', 'a', 'e', 'g', 5, 'g', 'a', 'e', 'g', 'g', 5, 'g', 'a', 'e', 'g', 's', 4, 'g', 'a', 'e', 'n', 5, 'g', 'a', 'e', 'n', 'j', 5, 'g', 'a', 'e', 'n', 'h', 4, 'g', 'a', 'e', 'd', 4, 'g', 'a', 'e', 'l', 5, 'g', 'a', 'e', 'l', 'g', 5, 'g', 'a', 'e', 'l', 'm', 5, 'g', 'a', 'e', 'l', 'b', 5, 'g', 'a', 'e', 'l', 's', 5, 'g', 'a', 'e', 'l', 't', 5, 'g', 'a', 'e', 'l', 'p', 5, 'g', 'a', 'e', 'l', 'h', 4, 'g', 'a', 'e', 'm', 4, 'g', 'a', 'e', 'b', 5, 'g', 'a', 'e', 'b', 's', 4, 'g', 'a', 'e', 's', 5, 'g', 'a', 'e', 's', 's', 5, 'g', 'a', 'e', 'n', 'g', 4, 'g', 'a', 'e', 'j', 4, 'g', 'a', 'e', 'c', 4, 'g', 'a', 'e', 'k', 4, 'g', 'a', 'e', 't', 4, 'g', 'a', 'e', 'p', 4, 'g', 'a', 'e', 'h', 3, 'g', 'y', 'a', 4, 'g', 'y', 'a', 'g', 5, 'g', 'y', 'a', 'g', 'g', 5, 'g', 'y', 'a', 'g', 's', 4, 'g', 'y', 'a', 'n', 5, 'g', 'y', 'a', 'n', 'j', 5, 'g', 'y', 'a', 'n', 'h', 4, 'g', 'y', 'a', 'd', 4, 'g', 'y', 'a', 'l', 5, 'g', 'y', 'a', 'l', 'g', 5, 'g', 'y', 'a', 'l', 'm', 5, 'g', 'y', 'a', 'l', 'b', 5, 'g', 'y', 'a', 'l', 's', 5, 'g', 'y', 'a', 'l', 't', 5, 'g', 'y', 'a', 'l', 'p', 5, 'g', 'y', 'a', 'l', 'h', 4, 'g', 'y', 'a', 'm', 4, 'g', 'y', 'a', 'b', 5, 'g', 'y', 'a', 'b', 's', 4, 'g', 'y', 'a', 's', 5, 'g', 'y', 'a', 's', 's', 5, 'g', 'y', 'a', 'n', 'g', 4, 'g', 'y', 'a', 'j', 4, 'g', 'y', 'a', 'c', 4, 'g', 'y', 'a', 'k', 4, 'g', 'y', 'a', 't', 4, 'g', 'y', 'a', 'p', 4, 'g', 'y', 'a', 'h', 4, 'g', 'y', 'a', 'e', 5, 'g', 'y', 'a', 'e', 'g', 6, 'g', 'y', 'a', 'e', 'g', 'g', 6, 'g', 'y', 'a', 'e', 'g', 's', 5, 'g', 'y', 'a', 'e', 'n', 6, 'g', 'y', 'a', 'e', 'n', 'j', 6, 'g', 'y', 'a', 'e', 'n', 'h', 5, 'g', 'y', 'a', 'e', 'd', 5, 'g', 'y', 'a', 'e', 'l', 6, 'g', 'y', 'a', 'e', 'l', 'g', 6, 'g', 'y', 'a', 'e', 'l', 'm', 6, 'g', 'y', 'a', 'e', 'l', 'b', 6, 'g', 'y', 'a', 'e', 'l', 's', 6, 'g', 'y', 'a', 'e', 'l', 't', 6, 'g', 'y', 'a', 'e', 'l', 'p', 6, 'g', 'y', 'a', 'e', 'l', 'h', 5, 'g', 'y', 'a', 'e', 'm', 5, 'g', 'y', 'a', 'e', 'b', 6, 'g', 'y', 'a', 'e', 'b', 's', 5, 'g', 'y', 'a', 'e', 's', 6, 'g', 'y', 'a', 'e', 's', 's', 6, 'g', 'y', 'a', 'e', 'n', 'g', 5, 'g', 'y', 'a', 'e', 'j', 5, 'g', 'y', 'a', 'e', 'c', 5, 'g', 'y', 'a', 'e', 'k', 5, 'g', 'y', 'a', 'e', 't', 5, 'g', 'y', 'a', 'e', 'p', 5, 'g', 'y', 'a', 'e', 'h', 3, 'g', 'e', 'o', 4, 'g', 'e', 'o', 'g', 5, 'g', 'e', 'o', 'g', 'g', 5, 'g', 'e', 'o', 'g', 's', 4, 'g', 'e', 'o', 'n', 5, 'g', 'e', 'o', 'n', 'j', 5, 'g', 'e', 'o', 'n', 'h', 4, 'g', 'e', 'o', 'd', 4, 'g', 'e', 'o', 'l', 5, 'g', 'e', 'o', 'l', 'g', 5, 'g', 'e', 'o', 'l', 'm', 5, 'g', 'e', 'o', 'l', 'b', 5, 'g', 'e', 'o', 'l', 's', 5, 'g', 'e', 'o', 'l', 't', 5, 'g', 'e', 'o', 'l', 'p', 5, 'g', 'e', 'o', 'l', 'h', 4, 'g', 'e', 'o', 'm', 4, 'g', 'e', 'o', 'b', 5, 'g', 'e', 'o', 'b', 's', 4, 'g', 'e', 'o', 's', 5, 'g', 'e', 'o', 's', 's', 5, 'g', 'e', 'o', 'n', 'g', 4, 'g', 'e', 'o', 'j', 4, 'g', 'e', 'o', 'c', 4, 'g', 'e', 'o', 'k', 4, 'g', 'e', 'o', 't', 4, 'g', 'e', 'o', 'p', 4, 'g', 'e', 'o', 'h', 2, 'g', 'e', 3, 'g', 'e', 'g', 4, 'g', 'e', 'g', 'g', 4, 'g', 'e', 'g', 's', 3, 'g', 'e', 'n', 4, 'g', 'e', 'n', 'j', 4, 'g', 'e', 'n', 'h', 3, 'g', 'e', 'd', 3, 'g', 'e', 'l', 4, 'g', 'e', 'l', 'g', 4, 'g', 'e', 'l', 'm', 4, 'g', 'e', 'l', 'b', 4, 'g', 'e', 'l', 's', 4, 'g', 'e', 'l', 't', 4, 'g', 'e', 'l', 'p', 4, 'g', 'e', 'l', 'h', 3, 'g', 'e', 'm', 3, 'g', 'e', 'b', 4, 'g', 'e', 'b', 's', 3, 'g', 'e', 's', 4, 'g', 'e', 's', 's', 4, 'g', 'e', 'n', 'g', 3, 'g', 'e', 'j', 3, 'g', 'e', 'c', 3, 'g', 'e', 'k', 3, 'g', 'e', 't', 3, 'g', 'e', 'p', 3, 'g', 'e', 'h', 4, 'g', 'y', 'e', 'o', 5, 'g', 'y', 'e', 'o', 'g', 6, 'g', 'y', 'e', 'o', 'g', 'g', 6, 'g', 'y', 'e', 'o', 'g', 's', 5, 'g', 'y', 'e', 'o', 'n', 6, 'g', 'y', 'e', 'o', 'n', 'j', 6, 'g', 'y', 'e', 'o', 'n', 'h', 5, 'g', 'y', 'e', 'o', 'd', 5, 'g', 'y', 'e', 'o', 'l', 6, 'g', 'y', 'e', 'o', 'l', 'g', 6, 'g', 'y', 'e', 'o', 'l', 'm', 6, 'g', 'y', 'e', 'o', 'l', 'b', 6, 'g', 'y', 'e', 'o', 'l', 's', 6, 'g', 'y', 'e', 'o', 'l', 't', 6, 'g', 'y', 'e', 'o', 'l', 'p', 6, 'g', 'y', 'e', 'o', 'l', 'h', 5, 'g', 'y', 'e', 'o', 'm', 5, 'g', 'y', 'e', 'o', 'b', 6, 'g', 'y', 'e', 'o', 'b', 's', 5, 'g', 'y', 'e', 'o', 's', 6, 'g', 'y', 'e', 'o', 's', 's', 6, 'g', 'y', 'e', 'o', 'n', 'g', 5, 'g', 'y', 'e', 'o', 'j', 5, 'g', 'y', 'e', 'o', 'c', 5, 'g', 'y', 'e', 'o', 'k', 5, 'g', 'y', 'e', 'o', 't', 5, 'g', 'y', 'e', 'o', 'p', 5, 'g', 'y', 'e', 'o', 'h', 3, 'g', 'y', 'e', 4, 'g', 'y', 'e', 'g', 5, 'g', 'y', 'e', 'g', 'g', 5, 'g', 'y', 'e', 'g', 's', 4, 'g', 'y', 'e', 'n', 5, 'g', 'y', 'e', 'n', 'j', 5, 'g', 'y', 'e', 'n', 'h', 4, 'g', 'y', 'e', 'd', 4, 'g', 'y', 'e', 'l', 5, 'g', 'y', 'e', 'l', 'g', 5, 'g', 'y', 'e', 'l', 'm', 5, 'g', 'y', 'e', 'l', 'b', 5, 'g', 'y', 'e', 'l', 's', 5, 'g', 'y', 'e', 'l', 't', 5, 'g', 'y', 'e', 'l', 'p', 5, 'g', 'y', 'e', 'l', 'h', 4, 'g', 'y', 'e', 'm', 4, 'g', 'y', 'e', 'b', 5, 'g', 'y', 'e', 'b', 's', 4, 'g', 'y', 'e', 's', 5, 'g', 'y', 'e', 's', 's', 5, 'g', 'y', 'e', 'n', 'g', 4, 'g', 'y', 'e', 'j', 4, 'g', 'y', 'e', 'c', 4, 'g', 'y', 'e', 'k', 4, 'g', 'y', 'e', 't', 4, 'g', 'y', 'e', 'p', 4, 'g', 'y', 'e', 'h', 2, 'g', 'o', 3, 'g', 'o', 'g', 4, 'g', 'o', 'g', 'g', 4, 'g', 'o', 'g', 's', 3, 'g', 'o', 'n', 4, 'g', 'o', 'n', 'j', 4, 'g', 'o', 'n', 'h', 3, 'g', 'o', 'd', 3, 'g', 'o', 'l', 4, 'g', 'o', 'l', 'g', 4, 'g', 'o', 'l', 'm', 4, 'g', 'o', 'l', 'b', 4, 'g', 'o', 'l', 's', 4, 'g', 'o', 'l', 't', 4, 'g', 'o', 'l', 'p', 4, 'g', 'o', 'l', 'h', 3, 'g', 'o', 'm', 3, 'g', 'o', 'b', 4, 'g', 'o', 'b', 's', 3, 'g', 'o', 's', 4, 'g', 'o', 's', 's', 4, 'g', 'o', 'n', 'g', 3, 'g', 'o', 'j', 3, 'g', 'o', 'c', 3, 'g', 'o', 'k', 3, 'g', 'o', 't', 3, 'g', 'o', 'p', 3, 'g', 'o', 'h', 3, 'g', 'w', 'a', 4, 'g', 'w', 'a', 'g', 5, 'g', 'w', 'a', 'g', 'g', 5, 'g', 'w', 'a', 'g', 's', 4, 'g', 'w', 'a', 'n', 5, 'g', 'w', 'a', 'n', 'j', 5, 'g', 'w', 'a', 'n', 'h', 4, 'g', 'w', 'a', 'd', 4, 'g', 'w', 'a', 'l', 5, 'g', 'w', 'a', 'l', 'g', 5, 'g', 'w', 'a', 'l', 'm', 5, 'g', 'w', 'a', 'l', 'b', 5, 'g', 'w', 'a', 'l', 's', 5, 'g', 'w', 'a', 'l', 't', 5, 'g', 'w', 'a', 'l', 'p', 5, 'g', 'w', 'a', 'l', 'h', 4, 'g', 'w', 'a', 'm', 4, 'g', 'w', 'a', 'b', 5, 'g', 'w', 'a', 'b', 's', 4, 'g', 'w', 'a', 's', 5, 'g', 'w', 'a', 's', 's', 5, 'g', 'w', 'a', 'n', 'g', 4, 'g', 'w', 'a', 'j', 4, 'g', 'w', 'a', 'c', 4, 'g', 'w', 'a', 'k', 4, 'g', 'w', 'a', 't', 4, 'g', 'w', 'a', 'p', 4, 'g', 'w', 'a', 'h', 4, 'g', 'w', 'a', 'e', 5, 'g', 'w', 'a', 'e', 'g', 6, 'g', 'w', 'a', 'e', 'g', 'g', 6, 'g', 'w', 'a', 'e', 'g', 's', 5, 'g', 'w', 'a', 'e', 'n', 6, 'g', 'w', 'a', 'e', 'n', 'j', 6, 'g', 'w', 'a', 'e', 'n', 'h', 5, 'g', 'w', 'a', 'e', 'd', 5, 'g', 'w', 'a', 'e', 'l', 6, 'g', 'w', 'a', 'e', 'l', 'g', 6, 'g', 'w', 'a', 'e', 'l', 'm', 6, 'g', 'w', 'a', 'e', 'l', 'b', 6, 'g', 'w', 'a', 'e', 'l', 's', 6, 'g', 'w', 'a', 'e', 'l', 't', 6, 'g', 'w', 'a', 'e', 'l', 'p', 6, 'g', 'w', 'a', 'e', 'l', 'h', 5, 'g', 'w', 'a', 'e', 'm', 5, 'g', 'w', 'a', 'e', 'b', 6, 'g', 'w', 'a', 'e', 'b', 's', 5, 'g', 'w', 'a', 'e', 's', 6, 'g', 'w', 'a', 'e', 's', 's', 6, 'g', 'w', 'a', 'e', 'n', 'g', 5, 'g', 'w', 'a', 'e', 'j', 5, 'g', 'w', 'a', 'e', 'c', 5, 'g', 'w', 'a', 'e', 'k', 5, 'g', 'w', 'a', 'e', 't', 5, 'g', 'w', 'a', 'e', 'p', 5, 'g', 'w', 'a', 'e', 'h', 3, 'g', 'o', 'e', 4, 'g', 'o', 'e', 'g', 5, 'g', 'o', 'e', 'g', 'g', 5, 'g', 'o', 'e', 'g', 's', 4, 'g', 'o', 'e', 'n', 5, 'g', 'o', 'e', 'n', 'j', 5, 'g', 'o', 'e', 'n', 'h', 4, 'g', 'o', 'e', 'd', 4, 'g', 'o', 'e', 'l', 5, 'g', 'o', 'e', 'l', 'g', 5, 'g', 'o', 'e', 'l', 'm', 5, 'g', 'o', 'e', 'l', 'b', 5, 'g', 'o', 'e', 'l', 's', 5, 'g', 'o', 'e', 'l', 't', 5, 'g', 'o', 'e', 'l', 'p', 5, 'g', 'o', 'e', 'l', 'h', 4, 'g', 'o', 'e', 'm', 4, 'g', 'o', 'e', 'b', 5, 'g', 'o', 'e', 'b', 's', 4, 'g', 'o', 'e', 's', 5, 'g', 'o', 'e', 's', 's', 5, 'g', 'o', 'e', 'n', 'g', 4, 'g', 'o', 'e', 'j', 4, 'g', 'o', 'e', 'c', 4, 'g', 'o', 'e', 'k', 4, 'g', 'o', 'e', 't', 4, 'g', 'o', 'e', 'p', 4, 'g', 'o', 'e', 'h', 3, 'g', 'y', 'o', 4, 'g', 'y', 'o', 'g', 5, 'g', 'y', 'o', 'g', 'g', 5, 'g', 'y', 'o', 'g', 's', 4, 'g', 'y', 'o', 'n', 5, 'g', 'y', 'o', 'n', 'j', 5, 'g', 'y', 'o', 'n', 'h', 4, 'g', 'y', 'o', 'd', 4, 'g', 'y', 'o', 'l', 5, 'g', 'y', 'o', 'l', 'g', 5, 'g', 'y', 'o', 'l', 'm', 5, 'g', 'y', 'o', 'l', 'b', 5, 'g', 'y', 'o', 'l', 's', 5, 'g', 'y', 'o', 'l', 't', 5, 'g', 'y', 'o', 'l', 'p', 5, 'g', 'y', 'o', 'l', 'h', 4, 'g', 'y', 'o', 'm', 4, 'g', 'y', 'o', 'b', 5, 'g', 'y', 'o', 'b', 's', 4, 'g', 'y', 'o', 's', 5, 'g', 'y', 'o', 's', 's', 5, 'g', 'y', 'o', 'n', 'g', 4, 'g', 'y', 'o', 'j', 4, 'g', 'y', 'o', 'c', 4, 'g', 'y', 'o', 'k', 4, 'g', 'y', 'o', 't', 4, 'g', 'y', 'o', 'p', 4, 'g', 'y', 'o', 'h', 2, 'g', 'u', 3, 'g', 'u', 'g', 4, 'g', 'u', 'g', 'g', 4, 'g', 'u', 'g', 's', 3, 'g', 'u', 'n', 4, 'g', 'u', 'n', 'j', 4, 'g', 'u', 'n', 'h', 3, 'g', 'u', 'd', 3, 'g', 'u', 'l', 4, 'g', 'u', 'l', 'g', 4, 'g', 'u', 'l', 'm', 4, 'g', 'u', 'l', 'b', 4, 'g', 'u', 'l', 's', 4, 'g', 'u', 'l', 't', 4, 'g', 'u', 'l', 'p', 4, 'g', 'u', 'l', 'h', 3, 'g', 'u', 'm', 3, 'g', 'u', 'b', 4, 'g', 'u', 'b', 's', 3, 'g', 'u', 's', 4, 'g', 'u', 's', 's', 4, 'g', 'u', 'n', 'g', 3, 'g', 'u', 'j', 3, 'g', 'u', 'c', 3, 'g', 'u', 'k', 3, 'g', 'u', 't', 3, 'g', 'u', 'p', 3, 'g', 'u', 'h', 4, 'g', 'w', 'e', 'o', 5, 'g', 'w', 'e', 'o', 'g', 6, 'g', 'w', 'e', 'o', 'g', 'g', 6, 'g', 'w', 'e', 'o', 'g', 's', 5, 'g', 'w', 'e', 'o', 'n', 6, 'g', 'w', 'e', 'o', 'n', 'j', 6, 'g', 'w', 'e', 'o', 'n', 'h', 5, 'g', 'w', 'e', 'o', 'd', 5, 'g', 'w', 'e', 'o', 'l', 6, 'g', 'w', 'e', 'o', 'l', 'g', 6, 'g', 'w', 'e', 'o', 'l', 'm', 6, 'g', 'w', 'e', 'o', 'l', 'b', 6, 'g', 'w', 'e', 'o', 'l', 's', 6, 'g', 'w', 'e', 'o', 'l', 't', 6, 'g', 'w', 'e', 'o', 'l', 'p', 6, 'g', 'w', 'e', 'o', 'l', 'h', 5, 'g', 'w', 'e', 'o', 'm', 5, 'g', 'w', 'e', 'o', 'b', 6, 'g', 'w', 'e', 'o', 'b', 's', 5, 'g', 'w', 'e', 'o', 's', 6, 'g', 'w', 'e', 'o', 's', 's', 6, 'g', 'w', 'e', 'o', 'n', 'g', 5, 'g', 'w', 'e', 'o', 'j', 5, 'g', 'w', 'e', 'o', 'c', 5, 'g', 'w', 'e', 'o', 'k', 5, 'g', 'w', 'e', 'o', 't', 5, 'g', 'w', 'e', 'o', 'p', 5, 'g', 'w', 'e', 'o', 'h', 3, 'g', 'w', 'e', 4, 'g', 'w', 'e', 'g', 5, 'g', 'w', 'e', 'g', 'g', 5, 'g', 'w', 'e', 'g', 's', 4, 'g', 'w', 'e', 'n', 5, 'g', 'w', 'e', 'n', 'j', 5, 'g', 'w', 'e', 'n', 'h', 4, 'g', 'w', 'e', 'd', 4, 'g', 'w', 'e', 'l', 5, 'g', 'w', 'e', 'l', 'g', 5, 'g', 'w', 'e', 'l', 'm', 5, 'g', 'w', 'e', 'l', 'b', 5, 'g', 'w', 'e', 'l', 's', 5, 'g', 'w', 'e', 'l', 't', 5, 'g', 'w', 'e', 'l', 'p', 5, 'g', 'w', 'e', 'l', 'h', 4, 'g', 'w', 'e', 'm', 4, 'g', 'w', 'e', 'b', 5, 'g', 'w', 'e', 'b', 's', 4, 'g', 'w', 'e', 's', 5, 'g', 'w', 'e', 's', 's', 5, 'g', 'w', 'e', 'n', 'g', 4, 'g', 'w', 'e', 'j', 4, 'g', 'w', 'e', 'c', 4, 'g', 'w', 'e', 'k', 4, 'g', 'w', 'e', 't', 4, 'g', 'w', 'e', 'p', 4, 'g', 'w', 'e', 'h', 3, 'g', 'w', 'i', 4, 'g', 'w', 'i', 'g', 5, 'g', 'w', 'i', 'g', 'g', 5, 'g', 'w', 'i', 'g', 's', 4, 'g', 'w', 'i', 'n', 5, 'g', 'w', 'i', 'n', 'j', 5, 'g', 'w', 'i', 'n', 'h', 4, 'g', 'w', 'i', 'd', 4, 'g', 'w', 'i', 'l', 5, 'g', 'w', 'i', 'l', 'g', 5, 'g', 'w', 'i', 'l', 'm', 5, 'g', 'w', 'i', 'l', 'b', 5, 'g', 'w', 'i', 'l', 's', 5, 'g', 'w', 'i', 'l', 't', 5, 'g', 'w', 'i', 'l', 'p', 5, 'g', 'w', 'i', 'l', 'h', 4, 'g', 'w', 'i', 'm', 4, 'g', 'w', 'i', 'b', 5, 'g', 'w', 'i', 'b', 's', 4, 'g', 'w', 'i', 's', 5, 'g', 'w', 'i', 's', 's', 5, 'g', 'w', 'i', 'n', 'g', 4, 'g', 'w', 'i', 'j', 4, 'g', 'w', 'i', 'c', 4, 'g', 'w', 'i', 'k', 4, 'g', 'w', 'i', 't', 4, 'g', 'w', 'i', 'p', 4, 'g', 'w', 'i', 'h', 3, 'g', 'y', 'u', 4, 'g', 'y', 'u', 'g', 5, 'g', 'y', 'u', 'g', 'g', 5, 'g', 'y', 'u', 'g', 's', 4, 'g', 'y', 'u', 'n', 5, 'g', 'y', 'u', 'n', 'j', 5, 'g', 'y', 'u', 'n', 'h', 4, 'g', 'y', 'u', 'd', 4, 'g', 'y', 'u', 'l', 5, 'g', 'y', 'u', 'l', 'g', 5, 'g', 'y', 'u', 'l', 'm', 5, 'g', 'y', 'u', 'l', 'b', 5, 'g', 'y', 'u', 'l', 's', 5, 'g', 'y', 'u', 'l', 't', 5, 'g', 'y', 'u', 'l', 'p', 5, 'g', 'y', 'u', 'l', 'h', 4, 'g', 'y', 'u', 'm', 4, 'g', 'y', 'u', 'b', 5, 'g', 'y', 'u', 'b', 's', 4, 'g', 'y', 'u', 's', 5, 'g', 'y', 'u', 's', 's', 5, 'g', 'y', 'u', 'n', 'g', 4, 'g', 'y', 'u', 'j', 4, 'g', 'y', 'u', 'c', 4, 'g', 'y', 'u', 'k', 4, 'g', 'y', 'u', 't', 4, 'g', 'y', 'u', 'p', 4, 'g', 'y', 'u', 'h', 3, 'g', 'e', 'u', 4, 'g', 'e', 'u', 'g', 5, 'g', 'e', 'u', 'g', 'g', 5, 'g', 'e', 'u', 'g', 's', 4, 'g', 'e', 'u', 'n', 5, 'g', 'e', 'u', 'n', 'j', 5, 'g', 'e', 'u', 'n', 'h', 4, 'g', 'e', 'u', 'd', 4, 'g', 'e', 'u', 'l', 5, 'g', 'e', 'u', 'l', 'g', 5, 'g', 'e', 'u', 'l', 'm', 5, 'g', 'e', 'u', 'l', 'b', 5, 'g', 'e', 'u', 'l', 's', 5, 'g', 'e', 'u', 'l', 't', 5, 'g', 'e', 'u', 'l', 'p', 5, 'g', 'e', 'u', 'l', 'h', 4, 'g', 'e', 'u', 'm', 4, 'g', 'e', 'u', 'b', 5, 'g', 'e', 'u', 'b', 's', 4, 'g', 'e', 'u', 's', 5, 'g', 'e', 'u', 's', 's', 5, 'g', 'e', 'u', 'n', 'g', 4, 'g', 'e', 'u', 'j', 4, 'g', 'e', 'u', 'c', 4, 'g', 'e', 'u', 'k', 4, 'g', 'e', 'u', 't', 4, 'g', 'e', 'u', 'p', 4, 'g', 'e', 'u', 'h', 3, 'g', 'y', 'i', 4, 'g', 'y', 'i', 'g', 5, 'g', 'y', 'i', 'g', 'g', 5, 'g', 'y', 'i', 'g', 's', 4, 'g', 'y', 'i', 'n', 5, 'g', 'y', 'i', 'n', 'j', 5, 'g', 'y', 'i', 'n', 'h', 4, 'g', 'y', 'i', 'd', 4, 'g', 'y', 'i', 'l', 5, 'g', 'y', 'i', 'l', 'g', 5, 'g', 'y', 'i', 'l', 'm', 5, 'g', 'y', 'i', 'l', 'b', 5, 'g', 'y', 'i', 'l', 's', 5, 'g', 'y', 'i', 'l', 't', 5, 'g', 'y', 'i', 'l', 'p', 5, 'g', 'y', 'i', 'l', 'h', 4, 'g', 'y', 'i', 'm', 4, 'g', 'y', 'i', 'b', 5, 'g', 'y', 'i', 'b', 's', 4, 'g', 'y', 'i', 's', 5, 'g', 'y', 'i', 's', 's', 5, 'g', 'y', 'i', 'n', 'g', 4, 'g', 'y', 'i', 'j', 4, 'g', 'y', 'i', 'c', 4, 'g', 'y', 'i', 'k', 4, 'g', 'y', 'i', 't', 4, 'g', 'y', 'i', 'p', 4, 'g', 'y', 'i', 'h', 2, 'g', 'i', 3, 'g', 'i', 'g', 4, 'g', 'i', 'g', 'g', 4, 'g', 'i', 'g', 's', 3, 'g', 'i', 'n', 4, 'g', 'i', 'n', 'j', 4, 'g', 'i', 'n', 'h', 3, 'g', 'i', 'd', 3, 'g', 'i', 'l', 4, 'g', 'i', 'l', 'g', 4, 'g', 'i', 'l', 'm', 4, 'g', 'i', 'l', 'b', 4, 'g', 'i', 'l', 's', 4, 'g', 'i', 'l', 't', 4, 'g', 'i', 'l', 'p', 4, 'g', 'i', 'l', 'h', 3, 'g', 'i', 'm', 3, 'g', 'i', 'b', 4, 'g', 'i', 'b', 's', 3, 'g', 'i', 's', 4, 'g', 'i', 's', 's', 4, 'g', 'i', 'n', 'g', 3, 'g', 'i', 'j', 3, 'g', 'i', 'c', 3, 'g', 'i', 'k', 3, 'g', 'i', 't', 3, 'g', 'i', 'p', 3, 'g', 'i', 'h', 3, 'g', 'g', 'a', 4, 'g', 'g', 'a', 'g', 5, 'g', 'g', 'a', 'g', 'g', 5, 'g', 'g', 'a', 'g', 's', 4, 'g', 'g', 'a', 'n', 5, 'g', 'g', 'a', 'n', 'j', 5, 'g', 'g', 'a', 'n', 'h', 4, 'g', 'g', 'a', 'd', 4, 'g', 'g', 'a', 'l', 5, 'g', 'g', 'a', 'l', 'g', 5, 'g', 'g', 'a', 'l', 'm', 5, 'g', 'g', 'a', 'l', 'b', 5, 'g', 'g', 'a', 'l', 's', 5, 'g', 'g', 'a', 'l', 't', 5, 'g', 'g', 'a', 'l', 'p', 5, 'g', 'g', 'a', 'l', 'h', 4, 'g', 'g', 'a', 'm', 4, 'g', 'g', 'a', 'b', 5, 'g', 'g', 'a', 'b', 's', 4, 'g', 'g', 'a', 's', 5, 'g', 'g', 'a', 's', 's', 5, 'g', 'g', 'a', 'n', 'g', 4, 'g', 'g', 'a', 'j', 4, 'g', 'g', 'a', 'c', 4, 'g', 'g', 'a', 'k', 4, 'g', 'g', 'a', 't', 4, 'g', 'g', 'a', 'p', 4, 'g', 'g', 'a', 'h', 4, 'g', 'g', 'a', 'e', 5, 'g', 'g', 'a', 'e', 'g', 6, 'g', 'g', 'a', 'e', 'g', 'g', 6, 'g', 'g', 'a', 'e', 'g', 's', 5, 'g', 'g', 'a', 'e', 'n', 6, 'g', 'g', 'a', 'e', 'n', 'j', 6, 'g', 'g', 'a', 'e', 'n', 'h', 5, 'g', 'g', 'a', 'e', 'd', 5, 'g', 'g', 'a', 'e', 'l', 6, 'g', 'g', 'a', 'e', 'l', 'g', 6, 'g', 'g', 'a', 'e', 'l', 'm', 6, 'g', 'g', 'a', 'e', 'l', 'b', 6, 'g', 'g', 'a', 'e', 'l', 's', 6, 'g', 'g', 'a', 'e', 'l', 't', 6, 'g', 'g', 'a', 'e', 'l', 'p', 6, 'g', 'g', 'a', 'e', 'l', 'h', 5, 'g', 'g', 'a', 'e', 'm', 5, 'g', 'g', 'a', 'e', 'b', 6, 'g', 'g', 'a', 'e', 'b', 's', 5, 'g', 'g', 'a', 'e', 's', 6, 'g', 'g', 'a', 'e', 's', 's', 6, 'g', 'g', 'a', 'e', 'n', 'g', 5, 'g', 'g', 'a', 'e', 'j', 5, 'g', 'g', 'a', 'e', 'c', 5, 'g', 'g', 'a', 'e', 'k', 5, 'g', 'g', 'a', 'e', 't', 5, 'g', 'g', 'a', 'e', 'p', 5, 'g', 'g', 'a', 'e', 'h', 4, 'g', 'g', 'y', 'a', 5, 'g', 'g', 'y', 'a', 'g', 6, 'g', 'g', 'y', 'a', 'g', 'g', 6, 'g', 'g', 'y', 'a', 'g', 's', 5, 'g', 'g', 'y', 'a', 'n', 6, 'g', 'g', 'y', 'a', 'n', 'j', 6, 'g', 'g', 'y', 'a', 'n', 'h', 5, 'g', 'g', 'y', 'a', 'd', 5, 'g', 'g', 'y', 'a', 'l', 6, 'g', 'g', 'y', 'a', 'l', 'g', 6, 'g', 'g', 'y', 'a', 'l', 'm', 6, 'g', 'g', 'y', 'a', 'l', 'b', 6, 'g', 'g', 'y', 'a', 'l', 's', 6, 'g', 'g', 'y', 'a', 'l', 't', 6, 'g', 'g', 'y', 'a', 'l', 'p', 6, 'g', 'g', 'y', 'a', 'l', 'h', 5, 'g', 'g', 'y', 'a', 'm', 5, 'g', 'g', 'y', 'a', 'b', 6, 'g', 'g', 'y', 'a', 'b', 's', 5, 'g', 'g', 'y', 'a', 's', 6, 'g', 'g', 'y', 'a', 's', 's', 6, 'g', 'g', 'y', 'a', 'n', 'g', 5, 'g', 'g', 'y', 'a', 'j', 5, 'g', 'g', 'y', 'a', 'c', 5, 'g', 'g', 'y', 'a', 'k', 5, 'g', 'g', 'y', 'a', 't', 5, 'g', 'g', 'y', 'a', 'p', 5, 'g', 'g', 'y', 'a', 'h', 5, 'g', 'g', 'y', 'a', 'e', 6, 'g', 'g', 'y', 'a', 'e', 'g', 7, 'g', 'g', 'y', 'a', 'e', 'g', 'g', 7, 'g', 'g', 'y', 'a', 'e', 'g', 's', 6, 'g', 'g', 'y', 'a', 'e', 'n', 7, 'g', 'g', 'y', 'a', 'e', 'n', 'j', 7, 'g', 'g', 'y', 'a', 'e', 'n', 'h', 6, 'g', 'g', 'y', 'a', 'e', 'd', 6, 'g', 'g', 'y', 'a', 'e', 'l', 7, 'g', 'g', 'y', 'a', 'e', 'l', 'g', 7, 'g', 'g', 'y', 'a', 'e', 'l', 'm', 7, 'g', 'g', 'y', 'a', 'e', 'l', 'b', 7, 'g', 'g', 'y', 'a', 'e', 'l', 's', 7, 'g', 'g', 'y', 'a', 'e', 'l', 't', 7, 'g', 'g', 'y', 'a', 'e', 'l', 'p', 7, 'g', 'g', 'y', 'a', 'e', 'l', 'h', 6, 'g', 'g', 'y', 'a', 'e', 'm', 6, 'g', 'g', 'y', 'a', 'e', 'b', 7, 'g', 'g', 'y', 'a', 'e', 'b', 's', 6, 'g', 'g', 'y', 'a', 'e', 's', 7, 'g', 'g', 'y', 'a', 'e', 's', 's', 7, 'g', 'g', 'y', 'a', 'e', 'n', 'g', 6, 'g', 'g', 'y', 'a', 'e', 'j', 6, 'g', 'g', 'y', 'a', 'e', 'c', 6, 'g', 'g', 'y', 'a', 'e', 'k', 6, 'g', 'g', 'y', 'a', 'e', 't', 6, 'g', 'g', 'y', 'a', 'e', 'p', 6, 'g', 'g', 'y', 'a', 'e', 'h', 4, 'g', 'g', 'e', 'o', 5, 'g', 'g', 'e', 'o', 'g', 6, 'g', 'g', 'e', 'o', 'g', 'g', 6, 'g', 'g', 'e', 'o', 'g', 's', 5, 'g', 'g', 'e', 'o', 'n', 6, 'g', 'g', 'e', 'o', 'n', 'j', 6, 'g', 'g', 'e', 'o', 'n', 'h', 5, 'g', 'g', 'e', 'o', 'd', 5, 'g', 'g', 'e', 'o', 'l', 6, 'g', 'g', 'e', 'o', 'l', 'g', 6, 'g', 'g', 'e', 'o', 'l', 'm', 6, 'g', 'g', 'e', 'o', 'l', 'b', 6, 'g', 'g', 'e', 'o', 'l', 's', 6, 'g', 'g', 'e', 'o', 'l', 't', 6, 'g', 'g', 'e', 'o', 'l', 'p', 6, 'g', 'g', 'e', 'o', 'l', 'h', 5, 'g', 'g', 'e', 'o', 'm', 5, 'g', 'g', 'e', 'o', 'b', 6, 'g', 'g', 'e', 'o', 'b', 's', 5, 'g', 'g', 'e', 'o', 's', 6, 'g', 'g', 'e', 'o', 's', 's', 6, 'g', 'g', 'e', 'o', 'n', 'g', 5, 'g', 'g', 'e', 'o', 'j', 5, 'g', 'g', 'e', 'o', 'c', 5, 'g', 'g', 'e', 'o', 'k', 5, 'g', 'g', 'e', 'o', 't', 5, 'g', 'g', 'e', 'o', 'p', 5, 'g', 'g', 'e', 'o', 'h', 3, 'g', 'g', 'e', 4, 'g', 'g', 'e', 'g', 5, 'g', 'g', 'e', 'g', 'g', 5, 'g', 'g', 'e', 'g', 's', 4, 'g', 'g', 'e', 'n', 5, 'g', 'g', 'e', 'n', 'j', 5, 'g', 'g', 'e', 'n', 'h', 4, 'g', 'g', 'e', 'd', 4, 'g', 'g', 'e', 'l', 5, 'g', 'g', 'e', 'l', 'g', 5, 'g', 'g', 'e', 'l', 'm', 5, 'g', 'g', 'e', 'l', 'b', 5, 'g', 'g', 'e', 'l', 's', 5, 'g', 'g', 'e', 'l', 't', 5, 'g', 'g', 'e', 'l', 'p', 5, 'g', 'g', 'e', 'l', 'h', 4, 'g', 'g', 'e', 'm', 4, 'g', 'g', 'e', 'b', 5, 'g', 'g', 'e', 'b', 's', 4, 'g', 'g', 'e', 's', 5, 'g', 'g', 'e', 's', 's', 5, 'g', 'g', 'e', 'n', 'g', 4, 'g', 'g', 'e', 'j', 4, 'g', 'g', 'e', 'c', 4, 'g', 'g', 'e', 'k', 4, 'g', 'g', 'e', 't', 4, 'g', 'g', 'e', 'p', 4, 'g', 'g', 'e', 'h', 5, 'g', 'g', 'y', 'e', 'o', 6, 'g', 'g', 'y', 'e', 'o', 'g', 7, 'g', 'g', 'y', 'e', 'o', 'g', 'g', 7, 'g', 'g', 'y', 'e', 'o', 'g', 's', 6, 'g', 'g', 'y', 'e', 'o', 'n', 7, 'g', 'g', 'y', 'e', 'o', 'n', 'j', 7, 'g', 'g', 'y', 'e', 'o', 'n', 'h', 6, 'g', 'g', 'y', 'e', 'o', 'd', 6, 'g', 'g', 'y', 'e', 'o', 'l', 7, 'g', 'g', 'y', 'e', 'o', 'l', 'g', 7, 'g', 'g', 'y', 'e', 'o', 'l', 'm', 7, 'g', 'g', 'y', 'e', 'o', 'l', 'b', 7, 'g', 'g', 'y', 'e', 'o', 'l', 's', 7, 'g', 'g', 'y', 'e', 'o', 'l', 't', 7, 'g', 'g', 'y', 'e', 'o', 'l', 'p', 7, 'g', 'g', 'y', 'e', 'o', 'l', 'h', 6, 'g', 'g', 'y', 'e', 'o', 'm', 6, 'g', 'g', 'y', 'e', 'o', 'b', 7, 'g', 'g', 'y', 'e', 'o', 'b', 's', 6, 'g', 'g', 'y', 'e', 'o', 's', 7, 'g', 'g', 'y', 'e', 'o', 's', 's', 7, 'g', 'g', 'y', 'e', 'o', 'n', 'g', 6, 'g', 'g', 'y', 'e', 'o', 'j', 6, 'g', 'g', 'y', 'e', 'o', 'c', 6, 'g', 'g', 'y', 'e', 'o', 'k', 6, 'g', 'g', 'y', 'e', 'o', 't', 6, 'g', 'g', 'y', 'e', 'o', 'p', 6, 'g', 'g', 'y', 'e', 'o', 'h', 4, 'g', 'g', 'y', 'e', 5, 'g', 'g', 'y', 'e', 'g', 6, 'g', 'g', 'y', 'e', 'g', 'g', 6, 'g', 'g', 'y', 'e', 'g', 's', 5, 'g', 'g', 'y', 'e', 'n', 6, 'g', 'g', 'y', 'e', 'n', 'j', 6, 'g', 'g', 'y', 'e', 'n', 'h', 5, 'g', 'g', 'y', 'e', 'd', 5, 'g', 'g', 'y', 'e', 'l', 6, 'g', 'g', 'y', 'e', 'l', 'g', 6, 'g', 'g', 'y', 'e', 'l', 'm', 6, 'g', 'g', 'y', 'e', 'l', 'b', 6, 'g', 'g', 'y', 'e', 'l', 's', 6, 'g', 'g', 'y', 'e', 'l', 't', 6, 'g', 'g', 'y', 'e', 'l', 'p', 6, 'g', 'g', 'y', 'e', 'l', 'h', 5, 'g', 'g', 'y', 'e', 'm', 5, 'g', 'g', 'y', 'e', 'b', 6, 'g', 'g', 'y', 'e', 'b', 's', 5, 'g', 'g', 'y', 'e', 's', 6, 'g', 'g', 'y', 'e', 's', 's', 6, 'g', 'g', 'y', 'e', 'n', 'g', 5, 'g', 'g', 'y', 'e', 'j', 5, 'g', 'g', 'y', 'e', 'c', 5, 'g', 'g', 'y', 'e', 'k', 5, 'g', 'g', 'y', 'e', 't', 5, 'g', 'g', 'y', 'e', 'p', 5, 'g', 'g', 'y', 'e', 'h', 3, 'g', 'g', 'o', 4, 'g', 'g', 'o', 'g', 5, 'g', 'g', 'o', 'g', 'g', 5, 'g', 'g', 'o', 'g', 's', 4, 'g', 'g', 'o', 'n', 5, 'g', 'g', 'o', 'n', 'j', 5, 'g', 'g', 'o', 'n', 'h', 4, 'g', 'g', 'o', 'd', 4, 'g', 'g', 'o', 'l', 5, 'g', 'g', 'o', 'l', 'g', 5, 'g', 'g', 'o', 'l', 'm', 5, 'g', 'g', 'o', 'l', 'b', 5, 'g', 'g', 'o', 'l', 's', 5, 'g', 'g', 'o', 'l', 't', 5, 'g', 'g', 'o', 'l', 'p', 5, 'g', 'g', 'o', 'l', 'h', 4, 'g', 'g', 'o', 'm', 4, 'g', 'g', 'o', 'b', 5, 'g', 'g', 'o', 'b', 's', 4, 'g', 'g', 'o', 's', 5, 'g', 'g', 'o', 's', 's', 5, 'g', 'g', 'o', 'n', 'g', 4, 'g', 'g', 'o', 'j', 4, 'g', 'g', 'o', 'c', 4, 'g', 'g', 'o', 'k', 4, 'g', 'g', 'o', 't', 4, 'g', 'g', 'o', 'p', 4, 'g', 'g', 'o', 'h', 4, 'g', 'g', 'w', 'a', 5, 'g', 'g', 'w', 'a', 'g', 6, 'g', 'g', 'w', 'a', 'g', 'g', 6, 'g', 'g', 'w', 'a', 'g', 's', 5, 'g', 'g', 'w', 'a', 'n', 6, 'g', 'g', 'w', 'a', 'n', 'j', 6, 'g', 'g', 'w', 'a', 'n', 'h', 5, 'g', 'g', 'w', 'a', 'd', 5, 'g', 'g', 'w', 'a', 'l', 6, 'g', 'g', 'w', 'a', 'l', 'g', 6, 'g', 'g', 'w', 'a', 'l', 'm', 6, 'g', 'g', 'w', 'a', 'l', 'b', 6, 'g', 'g', 'w', 'a', 'l', 's', 6, 'g', 'g', 'w', 'a', 'l', 't', 6, 'g', 'g', 'w', 'a', 'l', 'p', 6, 'g', 'g', 'w', 'a', 'l', 'h', 5, 'g', 'g', 'w', 'a', 'm', 5, 'g', 'g', 'w', 'a', 'b', 6, 'g', 'g', 'w', 'a', 'b', 's', 5, 'g', 'g', 'w', 'a', 's', 6, 'g', 'g', 'w', 'a', 's', 's', 6, 'g', 'g', 'w', 'a', 'n', 'g', 5, 'g', 'g', 'w', 'a', 'j', 5, 'g', 'g', 'w', 'a', 'c', 5, 'g', 'g', 'w', 'a', 'k', 5, 'g', 'g', 'w', 'a', 't', 5, 'g', 'g', 'w', 'a', 'p', 5, 'g', 'g', 'w', 'a', 'h', 5, 'g', 'g', 'w', 'a', 'e', 6, 'g', 'g', 'w', 'a', 'e', 'g', 7, 'g', 'g', 'w', 'a', 'e', 'g', 'g', 7, 'g', 'g', 'w', 'a', 'e', 'g', 's', 6, 'g', 'g', 'w', 'a', 'e', 'n', 7, 'g', 'g', 'w', 'a', 'e', 'n', 'j', 7, 'g', 'g', 'w', 'a', 'e', 'n', 'h', 6, 'g', 'g', 'w', 'a', 'e', 'd', 6, 'g', 'g', 'w', 'a', 'e', 'l', 7, 'g', 'g', 'w', 'a', 'e', 'l', 'g', 7, 'g', 'g', 'w', 'a', 'e', 'l', 'm', 7, 'g', 'g', 'w', 'a', 'e', 'l', 'b', 7, 'g', 'g', 'w', 'a', 'e', 'l', 's', 7, 'g', 'g', 'w', 'a', 'e', 'l', 't', 7, 'g', 'g', 'w', 'a', 'e', 'l', 'p', 7, 'g', 'g', 'w', 'a', 'e', 'l', 'h', 6, 'g', 'g', 'w', 'a', 'e', 'm', 6, 'g', 'g', 'w', 'a', 'e', 'b', 7, 'g', 'g', 'w', 'a', 'e', 'b', 's', 6, 'g', 'g', 'w', 'a', 'e', 's', 7, 'g', 'g', 'w', 'a', 'e', 's', 's', 7, 'g', 'g', 'w', 'a', 'e', 'n', 'g', 6, 'g', 'g', 'w', 'a', 'e', 'j', 6, 'g', 'g', 'w', 'a', 'e', 'c', 6, 'g', 'g', 'w', 'a', 'e', 'k', 6, 'g', 'g', 'w', 'a', 'e', 't', 6, 'g', 'g', 'w', 'a', 'e', 'p', 6, 'g', 'g', 'w', 'a', 'e', 'h', 4, 'g', 'g', 'o', 'e', 5, 'g', 'g', 'o', 'e', 'g', 6, 'g', 'g', 'o', 'e', 'g', 'g', 6, 'g', 'g', 'o', 'e', 'g', 's', 5, 'g', 'g', 'o', 'e', 'n', 6, 'g', 'g', 'o', 'e', 'n', 'j', 6, 'g', 'g', 'o', 'e', 'n', 'h', 5, 'g', 'g', 'o', 'e', 'd', 5, 'g', 'g', 'o', 'e', 'l', 6, 'g', 'g', 'o', 'e', 'l', 'g', 6, 'g', 'g', 'o', 'e', 'l', 'm', 6, 'g', 'g', 'o', 'e', 'l', 'b', 6, 'g', 'g', 'o', 'e', 'l', 's', 6, 'g', 'g', 'o', 'e', 'l', 't', 6, 'g', 'g', 'o', 'e', 'l', 'p', 6, 'g', 'g', 'o', 'e', 'l', 'h', 5, 'g', 'g', 'o', 'e', 'm', 5, 'g', 'g', 'o', 'e', 'b', 6, 'g', 'g', 'o', 'e', 'b', 's', 5, 'g', 'g', 'o', 'e', 's', 6, 'g', 'g', 'o', 'e', 's', 's', 6, 'g', 'g', 'o', 'e', 'n', 'g', 5, 'g', 'g', 'o', 'e', 'j', 5, 'g', 'g', 'o', 'e', 'c', 5, 'g', 'g', 'o', 'e', 'k', 5, 'g', 'g', 'o', 'e', 't', 5, 'g', 'g', 'o', 'e', 'p', 5, 'g', 'g', 'o', 'e', 'h', 4, 'g', 'g', 'y', 'o', 5, 'g', 'g', 'y', 'o', 'g', 6, 'g', 'g', 'y', 'o', 'g', 'g', 6, 'g', 'g', 'y', 'o', 'g', 's', 5, 'g', 'g', 'y', 'o', 'n', 6, 'g', 'g', 'y', 'o', 'n', 'j', 6, 'g', 'g', 'y', 'o', 'n', 'h', 5, 'g', 'g', 'y', 'o', 'd', 5, 'g', 'g', 'y', 'o', 'l', 6, 'g', 'g', 'y', 'o', 'l', 'g', 6, 'g', 'g', 'y', 'o', 'l', 'm', 6, 'g', 'g', 'y', 'o', 'l', 'b', 6, 'g', 'g', 'y', 'o', 'l', 's', 6, 'g', 'g', 'y', 'o', 'l', 't', 6, 'g', 'g', 'y', 'o', 'l', 'p', 6, 'g', 'g', 'y', 'o', 'l', 'h', 5, 'g', 'g', 'y', 'o', 'm', 5, 'g', 'g', 'y', 'o', 'b', 6, 'g', 'g', 'y', 'o', 'b', 's', 5, 'g', 'g', 'y', 'o', 's', 6, 'g', 'g', 'y', 'o', 's', 's', 6, 'g', 'g', 'y', 'o', 'n', 'g', 5, 'g', 'g', 'y', 'o', 'j', 5, 'g', 'g', 'y', 'o', 'c', 5, 'g', 'g', 'y', 'o', 'k', 5, 'g', 'g', 'y', 'o', 't', 5, 'g', 'g', 'y', 'o', 'p', 5, 'g', 'g', 'y', 'o', 'h', 3, 'g', 'g', 'u', 4, 'g', 'g', 'u', 'g', 5, 'g', 'g', 'u', 'g', 'g', 5, 'g', 'g', 'u', 'g', 's', 4, 'g', 'g', 'u', 'n', 5, 'g', 'g', 'u', 'n', 'j', 5, 'g', 'g', 'u', 'n', 'h', 4, 'g', 'g', 'u', 'd', 4, 'g', 'g', 'u', 'l', 5, 'g', 'g', 'u', 'l', 'g', 5, 'g', 'g', 'u', 'l', 'm', 5, 'g', 'g', 'u', 'l', 'b', 5, 'g', 'g', 'u', 'l', 's', 5, 'g', 'g', 'u', 'l', 't', 5, 'g', 'g', 'u', 'l', 'p', 5, 'g', 'g', 'u', 'l', 'h', 4, 'g', 'g', 'u', 'm', 4, 'g', 'g', 'u', 'b', 5, 'g', 'g', 'u', 'b', 's', 4, 'g', 'g', 'u', 's', 5, 'g', 'g', 'u', 's', 's', 5, 'g', 'g', 'u', 'n', 'g', 4, 'g', 'g', 'u', 'j', 4, 'g', 'g', 'u', 'c', 4, 'g', 'g', 'u', 'k', 4, 'g', 'g', 'u', 't', 4, 'g', 'g', 'u', 'p', 4, 'g', 'g', 'u', 'h', 5, 'g', 'g', 'w', 'e', 'o', 6, 'g', 'g', 'w', 'e', 'o', 'g', 7, 'g', 'g', 'w', 'e', 'o', 'g', 'g', 7, 'g', 'g', 'w', 'e', 'o', 'g', 's', 6, 'g', 'g', 'w', 'e', 'o', 'n', 7, 'g', 'g', 'w', 'e', 'o', 'n', 'j', 7, 'g', 'g', 'w', 'e', 'o', 'n', 'h', 6, 'g', 'g', 'w', 'e', 'o', 'd', 6, 'g', 'g', 'w', 'e', 'o', 'l', 7, 'g', 'g', 'w', 'e', 'o', 'l', 'g', 7, 'g', 'g', 'w', 'e', 'o', 'l', 'm', 7, 'g', 'g', 'w', 'e', 'o', 'l', 'b', 7, 'g', 'g', 'w', 'e', 'o', 'l', 's', 7, 'g', 'g', 'w', 'e', 'o', 'l', 't', 7, 'g', 'g', 'w', 'e', 'o', 'l', 'p', 7, 'g', 'g', 'w', 'e', 'o', 'l', 'h', 6, 'g', 'g', 'w', 'e', 'o', 'm', 6, 'g', 'g', 'w', 'e', 'o', 'b', 7, 'g', 'g', 'w', 'e', 'o', 'b', 's', 6, 'g', 'g', 'w', 'e', 'o', 's', 7, 'g', 'g', 'w', 'e', 'o', 's', 's', 7, 'g', 'g', 'w', 'e', 'o', 'n', 'g', 6, 'g', 'g', 'w', 'e', 'o', 'j', 6, 'g', 'g', 'w', 'e', 'o', 'c', 6, 'g', 'g', 'w', 'e', 'o', 'k', 6, 'g', 'g', 'w', 'e', 'o', 't', 6, 'g', 'g', 'w', 'e', 'o', 'p', 6, 'g', 'g', 'w', 'e', 'o', 'h', 4, 'g', 'g', 'w', 'e', 5, 'g', 'g', 'w', 'e', 'g', 6, 'g', 'g', 'w', 'e', 'g', 'g', 6, 'g', 'g', 'w', 'e', 'g', 's', 5, 'g', 'g', 'w', 'e', 'n', 6, 'g', 'g', 'w', 'e', 'n', 'j', 6, 'g', 'g', 'w', 'e', 'n', 'h', 5, 'g', 'g', 'w', 'e', 'd', 5, 'g', 'g', 'w', 'e', 'l', 6, 'g', 'g', 'w', 'e', 'l', 'g', 6, 'g', 'g', 'w', 'e', 'l', 'm', 6, 'g', 'g', 'w', 'e', 'l', 'b', 6, 'g', 'g', 'w', 'e', 'l', 's', 6, 'g', 'g', 'w', 'e', 'l', 't', 6, 'g', 'g', 'w', 'e', 'l', 'p', 6, 'g', 'g', 'w', 'e', 'l', 'h', 5, 'g', 'g', 'w', 'e', 'm', 5, 'g', 'g', 'w', 'e', 'b', 6, 'g', 'g', 'w', 'e', 'b', 's', 5, 'g', 'g', 'w', 'e', 's', 6, 'g', 'g', 'w', 'e', 's', 's', 6, 'g', 'g', 'w', 'e', 'n', 'g', 5, 'g', 'g', 'w', 'e', 'j', 5, 'g', 'g', 'w', 'e', 'c', 5, 'g', 'g', 'w', 'e', 'k', 5, 'g', 'g', 'w', 'e', 't', 5, 'g', 'g', 'w', 'e', 'p', 5, 'g', 'g', 'w', 'e', 'h', 4, 'g', 'g', 'w', 'i', 5, 'g', 'g', 'w', 'i', 'g', 6, 'g', 'g', 'w', 'i', 'g', 'g', 6, 'g', 'g', 'w', 'i', 'g', 's', 5, 'g', 'g', 'w', 'i', 'n', 6, 'g', 'g', 'w', 'i', 'n', 'j', 6, 'g', 'g', 'w', 'i', 'n', 'h', 5, 'g', 'g', 'w', 'i', 'd', 5, 'g', 'g', 'w', 'i', 'l', 6, 'g', 'g', 'w', 'i', 'l', 'g', 6, 'g', 'g', 'w', 'i', 'l', 'm', 6, 'g', 'g', 'w', 'i', 'l', 'b', 6, 'g', 'g', 'w', 'i', 'l', 's', 6, 'g', 'g', 'w', 'i', 'l', 't', 6, 'g', 'g', 'w', 'i', 'l', 'p', 6, 'g', 'g', 'w', 'i', 'l', 'h', 5, 'g', 'g', 'w', 'i', 'm', 5, 'g', 'g', 'w', 'i', 'b', 6, 'g', 'g', 'w', 'i', 'b', 's', 5, 'g', 'g', 'w', 'i', 's', 6, 'g', 'g', 'w', 'i', 's', 's', 6, 'g', 'g', 'w', 'i', 'n', 'g', 5, 'g', 'g', 'w', 'i', 'j', 5, 'g', 'g', 'w', 'i', 'c', 5, 'g', 'g', 'w', 'i', 'k', 5, 'g', 'g', 'w', 'i', 't', 5, 'g', 'g', 'w', 'i', 'p', 5, 'g', 'g', 'w', 'i', 'h', 4, 'g', 'g', 'y', 'u', 5, 'g', 'g', 'y', 'u', 'g', 6, 'g', 'g', 'y', 'u', 'g', 'g', 6, 'g', 'g', 'y', 'u', 'g', 's', 5, 'g', 'g', 'y', 'u', 'n', 6, 'g', 'g', 'y', 'u', 'n', 'j', 6, 'g', 'g', 'y', 'u', 'n', 'h', 5, 'g', 'g', 'y', 'u', 'd', 5, 'g', 'g', 'y', 'u', 'l', 6, 'g', 'g', 'y', 'u', 'l', 'g', 6, 'g', 'g', 'y', 'u', 'l', 'm', 6, 'g', 'g', 'y', 'u', 'l', 'b', 6, 'g', 'g', 'y', 'u', 'l', 's', 6, 'g', 'g', 'y', 'u', 'l', 't', 6, 'g', 'g', 'y', 'u', 'l', 'p', 6, 'g', 'g', 'y', 'u', 'l', 'h', 5, 'g', 'g', 'y', 'u', 'm', 5, 'g', 'g', 'y', 'u', 'b', 6, 'g', 'g', 'y', 'u', 'b', 's', 5, 'g', 'g', 'y', 'u', 's', 6, 'g', 'g', 'y', 'u', 's', 's', 6, 'g', 'g', 'y', 'u', 'n', 'g', 5, 'g', 'g', 'y', 'u', 'j', 5, 'g', 'g', 'y', 'u', 'c', 5, 'g', 'g', 'y', 'u', 'k', 5, 'g', 'g', 'y', 'u', 't', 5, 'g', 'g', 'y', 'u', 'p', 5, 'g', 'g', 'y', 'u', 'h', 4, 'g', 'g', 'e', 'u', 5, 'g', 'g', 'e', 'u', 'g', 6, 'g', 'g', 'e', 'u', 'g', 'g', 6, 'g', 'g', 'e', 'u', 'g', 's', 5, 'g', 'g', 'e', 'u', 'n', 6, 'g', 'g', 'e', 'u', 'n', 'j', 6, 'g', 'g', 'e', 'u', 'n', 'h', 5, 'g', 'g', 'e', 'u', 'd', 5, 'g', 'g', 'e', 'u', 'l', 6, 'g', 'g', 'e', 'u', 'l', 'g', 6, 'g', 'g', 'e', 'u', 'l', 'm', 6, 'g', 'g', 'e', 'u', 'l', 'b', 6, 'g', 'g', 'e', 'u', 'l', 's', 6, 'g', 'g', 'e', 'u', 'l', 't', 6, 'g', 'g', 'e', 'u', 'l', 'p', 6, 'g', 'g', 'e', 'u', 'l', 'h', 5, 'g', 'g', 'e', 'u', 'm', 5, 'g', 'g', 'e', 'u', 'b', 6, 'g', 'g', 'e', 'u', 'b', 's', 5, 'g', 'g', 'e', 'u', 's', 6, 'g', 'g', 'e', 'u', 's', 's', 6, 'g', 'g', 'e', 'u', 'n', 'g', 5, 'g', 'g', 'e', 'u', 'j', 5, 'g', 'g', 'e', 'u', 'c', 5, 'g', 'g', 'e', 'u', 'k', 5, 'g', 'g', 'e', 'u', 't', 5, 'g', 'g', 'e', 'u', 'p', 5, 'g', 'g', 'e', 'u', 'h', 4, 'g', 'g', 'y', 'i', 5, 'g', 'g', 'y', 'i', 'g', 6, 'g', 'g', 'y', 'i', 'g', 'g', 6, 'g', 'g', 'y', 'i', 'g', 's', 5, 'g', 'g', 'y', 'i', 'n', 6, 'g', 'g', 'y', 'i', 'n', 'j', 6, 'g', 'g', 'y', 'i', 'n', 'h', 5, 'g', 'g', 'y', 'i', 'd', 5, 'g', 'g', 'y', 'i', 'l', 6, 'g', 'g', 'y', 'i', 'l', 'g', 6, 'g', 'g', 'y', 'i', 'l', 'm', 6, 'g', 'g', 'y', 'i', 'l', 'b', 6, 'g', 'g', 'y', 'i', 'l', 's', 6, 'g', 'g', 'y', 'i', 'l', 't', 6, 'g', 'g', 'y', 'i', 'l', 'p', 6, 'g', 'g', 'y', 'i', 'l', 'h', 5, 'g', 'g', 'y', 'i', 'm', 5, 'g', 'g', 'y', 'i', 'b', 6, 'g', 'g', 'y', 'i', 'b', 's', 5, 'g', 'g', 'y', 'i', 's', 6, 'g', 'g', 'y', 'i', 's', 's', 6, 'g', 'g', 'y', 'i', 'n', 'g', 5, 'g', 'g', 'y', 'i', 'j', 5, 'g', 'g', 'y', 'i', 'c', 5, 'g', 'g', 'y', 'i', 'k', 5, 'g', 'g', 'y', 'i', 't', 5, 'g', 'g', 'y', 'i', 'p', 5, 'g', 'g', 'y', 'i', 'h', 3, 'g', 'g', 'i', 4, 'g', 'g', 'i', 'g', 5, 'g', 'g', 'i', 'g', 'g', 5, 'g', 'g', 'i', 'g', 's', 4, 'g', 'g', 'i', 'n', 5, 'g', 'g', 'i', 'n', 'j', 5, 'g', 'g', 'i', 'n', 'h', 4, 'g', 'g', 'i', 'd', 4, 'g', 'g', 'i', 'l', 5, 'g', 'g', 'i', 'l', 'g', 5, 'g', 'g', 'i', 'l', 'm', 5, 'g', 'g', 'i', 'l', 'b', 5, 'g', 'g', 'i', 'l', 's', 5, 'g', 'g', 'i', 'l', 't', 5, 'g', 'g', 'i', 'l', 'p', 5, 'g', 'g', 'i', 'l', 'h', 4, 'g', 'g', 'i', 'm', 4, 'g', 'g', 'i', 'b', 5, 'g', 'g', 'i', 'b', 's', 4, 'g', 'g', 'i', 's', 5, 'g', 'g', 'i', 's', 's', 5, 'g', 'g', 'i', 'n', 'g', 4, 'g', 'g', 'i', 'j', 4, 'g', 'g', 'i', 'c', 4, 'g', 'g', 'i', 'k', 4, 'g', 'g', 'i', 't', 4, 'g', 'g', 'i', 'p', 4, 'g', 'g', 'i', 'h', 2, 'n', 'a', 3, 'n', 'a', 'g', 4, 'n', 'a', 'g', 'g', 4, 'n', 'a', 'g', 's', 3, 'n', 'a', 'n', 4, 'n', 'a', 'n', 'j', 4, 'n', 'a', 'n', 'h', 3, 'n', 'a', 'd', 3, 'n', 'a', 'l', 4, 'n', 'a', 'l', 'g', 4, 'n', 'a', 'l', 'm', 4, 'n', 'a', 'l', 'b', 4, 'n', 'a', 'l', 's', 4, 'n', 'a', 'l', 't', 4, 'n', 'a', 'l', 'p', 4, 'n', 'a', 'l', 'h', 3, 'n', 'a', 'm', 3, 'n', 'a', 'b', 4, 'n', 'a', 'b', 's', 3, 'n', 'a', 's', 4, 'n', 'a', 's', 's', 4, 'n', 'a', 'n', 'g', 3, 'n', 'a', 'j', 3, 'n', 'a', 'c', 3, 'n', 'a', 'k', 3, 'n', 'a', 't', 3, 'n', 'a', 'p', 3, 'n', 'a', 'h', 3, 'n', 'a', 'e', 4, 'n', 'a', 'e', 'g', 5, 'n', 'a', 'e', 'g', 'g', 5, 'n', 'a', 'e', 'g', 's', 4, 'n', 'a', 'e', 'n', 5, 'n', 'a', 'e', 'n', 'j', 5, 'n', 'a', 'e', 'n', 'h', 4, 'n', 'a', 'e', 'd', 4, 'n', 'a', 'e', 'l', 5, 'n', 'a', 'e', 'l', 'g', 5, 'n', 'a', 'e', 'l', 'm', 5, 'n', 'a', 'e', 'l', 'b', 5, 'n', 'a', 'e', 'l', 's', 5, 'n', 'a', 'e', 'l', 't', 5, 'n', 'a', 'e', 'l', 'p', 5, 'n', 'a', 'e', 'l', 'h', 4, 'n', 'a', 'e', 'm', 4, 'n', 'a', 'e', 'b', 5, 'n', 'a', 'e', 'b', 's', 4, 'n', 'a', 'e', 's', 5, 'n', 'a', 'e', 's', 's', 5, 'n', 'a', 'e', 'n', 'g', 4, 'n', 'a', 'e', 'j', 4, 'n', 'a', 'e', 'c', 4, 'n', 'a', 'e', 'k', 4, 'n', 'a', 'e', 't', 4, 'n', 'a', 'e', 'p', 4, 'n', 'a', 'e', 'h', 3, 'n', 'y', 'a', 4, 'n', 'y', 'a', 'g', 5, 'n', 'y', 'a', 'g', 'g', 5, 'n', 'y', 'a', 'g', 's', 4, 'n', 'y', 'a', 'n', 5, 'n', 'y', 'a', 'n', 'j', 5, 'n', 'y', 'a', 'n', 'h', 4, 'n', 'y', 'a', 'd', 4, 'n', 'y', 'a', 'l', 5, 'n', 'y', 'a', 'l', 'g', 5, 'n', 'y', 'a', 'l', 'm', 5, 'n', 'y', 'a', 'l', 'b', 5, 'n', 'y', 'a', 'l', 's', 5, 'n', 'y', 'a', 'l', 't', 5, 'n', 'y', 'a', 'l', 'p', 5, 'n', 'y', 'a', 'l', 'h', 4, 'n', 'y', 'a', 'm', 4, 'n', 'y', 'a', 'b', 5, 'n', 'y', 'a', 'b', 's', 4, 'n', 'y', 'a', 's', 5, 'n', 'y', 'a', 's', 's', 5, 'n', 'y', 'a', 'n', 'g', 4, 'n', 'y', 'a', 'j', 4, 'n', 'y', 'a', 'c', 4, 'n', 'y', 'a', 'k', 4, 'n', 'y', 'a', 't', 4, 'n', 'y', 'a', 'p', 4, 'n', 'y', 'a', 'h', 4, 'n', 'y', 'a', 'e', 5, 'n', 'y', 'a', 'e', 'g', 6, 'n', 'y', 'a', 'e', 'g', 'g', 6, 'n', 'y', 'a', 'e', 'g', 's', 5, 'n', 'y', 'a', 'e', 'n', 6, 'n', 'y', 'a', 'e', 'n', 'j', 6, 'n', 'y', 'a', 'e', 'n', 'h', 5, 'n', 'y', 'a', 'e', 'd', 5, 'n', 'y', 'a', 'e', 'l', 6, 'n', 'y', 'a', 'e', 'l', 'g', 6, 'n', 'y', 'a', 'e', 'l', 'm', 6, 'n', 'y', 'a', 'e', 'l', 'b', 6, 'n', 'y', 'a', 'e', 'l', 's', 6, 'n', 'y', 'a', 'e', 'l', 't', 6, 'n', 'y', 'a', 'e', 'l', 'p', 6, 'n', 'y', 'a', 'e', 'l', 'h', 5, 'n', 'y', 'a', 'e', 'm', 5, 'n', 'y', 'a', 'e', 'b', 6, 'n', 'y', 'a', 'e', 'b', 's', 5, 'n', 'y', 'a', 'e', 's', 6, 'n', 'y', 'a', 'e', 's', 's', 6, 'n', 'y', 'a', 'e', 'n', 'g', 5, 'n', 'y', 'a', 'e', 'j', 5, 'n', 'y', 'a', 'e', 'c', 5, 'n', 'y', 'a', 'e', 'k', 5, 'n', 'y', 'a', 'e', 't', 5, 'n', 'y', 'a', 'e', 'p', 5, 'n', 'y', 'a', 'e', 'h', 3, 'n', 'e', 'o', 4, 'n', 'e', 'o', 'g', 5, 'n', 'e', 'o', 'g', 'g', 5, 'n', 'e', 'o', 'g', 's', 4, 'n', 'e', 'o', 'n', 5, 'n', 'e', 'o', 'n', 'j', 5, 'n', 'e', 'o', 'n', 'h', 4, 'n', 'e', 'o', 'd', 4, 'n', 'e', 'o', 'l', 5, 'n', 'e', 'o', 'l', 'g', 5, 'n', 'e', 'o', 'l', 'm', 5, 'n', 'e', 'o', 'l', 'b', 5, 'n', 'e', 'o', 'l', 's', 5, 'n', 'e', 'o', 'l', 't', 5, 'n', 'e', 'o', 'l', 'p', 5, 'n', 'e', 'o', 'l', 'h', 4, 'n', 'e', 'o', 'm', 4, 'n', 'e', 'o', 'b', 5, 'n', 'e', 'o', 'b', 's', 4, 'n', 'e', 'o', 's', 5, 'n', 'e', 'o', 's', 's', 5, 'n', 'e', 'o', 'n', 'g', 4, 'n', 'e', 'o', 'j', 4, 'n', 'e', 'o', 'c', 4, 'n', 'e', 'o', 'k', 4, 'n', 'e', 'o', 't', 4, 'n', 'e', 'o', 'p', 4, 'n', 'e', 'o', 'h', 2, 'n', 'e', 3, 'n', 'e', 'g', 4, 'n', 'e', 'g', 'g', 4, 'n', 'e', 'g', 's', 3, 'n', 'e', 'n', 4, 'n', 'e', 'n', 'j', 4, 'n', 'e', 'n', 'h', 3, 'n', 'e', 'd', 3, 'n', 'e', 'l', 4, 'n', 'e', 'l', 'g', 4, 'n', 'e', 'l', 'm', 4, 'n', 'e', 'l', 'b', 4, 'n', 'e', 'l', 's', 4, 'n', 'e', 'l', 't', 4, 'n', 'e', 'l', 'p', 4, 'n', 'e', 'l', 'h', 3, 'n', 'e', 'm', 3, 'n', 'e', 'b', 4, 'n', 'e', 'b', 's', 3, 'n', 'e', 's', 4, 'n', 'e', 's', 's', 4, 'n', 'e', 'n', 'g', 3, 'n', 'e', 'j', 3, 'n', 'e', 'c', 3, 'n', 'e', 'k', 3, 'n', 'e', 't', 3, 'n', 'e', 'p', 3, 'n', 'e', 'h', 4, 'n', 'y', 'e', 'o', 5, 'n', 'y', 'e', 'o', 'g', 6, 'n', 'y', 'e', 'o', 'g', 'g', 6, 'n', 'y', 'e', 'o', 'g', 's', 5, 'n', 'y', 'e', 'o', 'n', 6, 'n', 'y', 'e', 'o', 'n', 'j', 6, 'n', 'y', 'e', 'o', 'n', 'h', 5, 'n', 'y', 'e', 'o', 'd', 5, 'n', 'y', 'e', 'o', 'l', 6, 'n', 'y', 'e', 'o', 'l', 'g', 6, 'n', 'y', 'e', 'o', 'l', 'm', 6, 'n', 'y', 'e', 'o', 'l', 'b', 6, 'n', 'y', 'e', 'o', 'l', 's', 6, 'n', 'y', 'e', 'o', 'l', 't', 6, 'n', 'y', 'e', 'o', 'l', 'p', 6, 'n', 'y', 'e', 'o', 'l', 'h', 5, 'n', 'y', 'e', 'o', 'm', 5, 'n', 'y', 'e', 'o', 'b', 6, 'n', 'y', 'e', 'o', 'b', 's', 5, 'n', 'y', 'e', 'o', 's', 6, 'n', 'y', 'e', 'o', 's', 's', 6, 'n', 'y', 'e', 'o', 'n', 'g', 5, 'n', 'y', 'e', 'o', 'j', 5, 'n', 'y', 'e', 'o', 'c', 5, 'n', 'y', 'e', 'o', 'k', 5, 'n', 'y', 'e', 'o', 't', 5, 'n', 'y', 'e', 'o', 'p', 5, 'n', 'y', 'e', 'o', 'h', 3, 'n', 'y', 'e', 4, 'n', 'y', 'e', 'g', 5, 'n', 'y', 'e', 'g', 'g', 5, 'n', 'y', 'e', 'g', 's', 4, 'n', 'y', 'e', 'n', 5, 'n', 'y', 'e', 'n', 'j', 5, 'n', 'y', 'e', 'n', 'h', 4, 'n', 'y', 'e', 'd', 4, 'n', 'y', 'e', 'l', 5, 'n', 'y', 'e', 'l', 'g', 5, 'n', 'y', 'e', 'l', 'm', 5, 'n', 'y', 'e', 'l', 'b', 5, 'n', 'y', 'e', 'l', 's', 5, 'n', 'y', 'e', 'l', 't', 5, 'n', 'y', 'e', 'l', 'p', 5, 'n', 'y', 'e', 'l', 'h', 4, 'n', 'y', 'e', 'm', 4, 'n', 'y', 'e', 'b', 5, 'n', 'y', 'e', 'b', 's', 4, 'n', 'y', 'e', 's', 5, 'n', 'y', 'e', 's', 's', 5, 'n', 'y', 'e', 'n', 'g', 4, 'n', 'y', 'e', 'j', 4, 'n', 'y', 'e', 'c', 4, 'n', 'y', 'e', 'k', 4, 'n', 'y', 'e', 't', 4, 'n', 'y', 'e', 'p', 4, 'n', 'y', 'e', 'h', 2, 'n', 'o', 3, 'n', 'o', 'g', 4, 'n', 'o', 'g', 'g', 4, 'n', 'o', 'g', 's', 3, 'n', 'o', 'n', 4, 'n', 'o', 'n', 'j', 4, 'n', 'o', 'n', 'h', 3, 'n', 'o', 'd', 3, 'n', 'o', 'l', 4, 'n', 'o', 'l', 'g', 4, 'n', 'o', 'l', 'm', 4, 'n', 'o', 'l', 'b', 4, 'n', 'o', 'l', 's', 4, 'n', 'o', 'l', 't', 4, 'n', 'o', 'l', 'p', 4, 'n', 'o', 'l', 'h', 3, 'n', 'o', 'm', 3, 'n', 'o', 'b', 4, 'n', 'o', 'b', 's', 3, 'n', 'o', 's', 4, 'n', 'o', 's', 's', 4, 'n', 'o', 'n', 'g', 3, 'n', 'o', 'j', 3, 'n', 'o', 'c', 3, 'n', 'o', 'k', 3, 'n', 'o', 't', 3, 'n', 'o', 'p', 3, 'n', 'o', 'h', 3, 'n', 'w', 'a', 4, 'n', 'w', 'a', 'g', 5, 'n', 'w', 'a', 'g', 'g', 5, 'n', 'w', 'a', 'g', 's', 4, 'n', 'w', 'a', 'n', 5, 'n', 'w', 'a', 'n', 'j', 5, 'n', 'w', 'a', 'n', 'h', 4, 'n', 'w', 'a', 'd', 4, 'n', 'w', 'a', 'l', 5, 'n', 'w', 'a', 'l', 'g', 5, 'n', 'w', 'a', 'l', 'm', 5, 'n', 'w', 'a', 'l', 'b', 5, 'n', 'w', 'a', 'l', 's', 5, 'n', 'w', 'a', 'l', 't', 5, 'n', 'w', 'a', 'l', 'p', 5, 'n', 'w', 'a', 'l', 'h', 4, 'n', 'w', 'a', 'm', 4, 'n', 'w', 'a', 'b', 5, 'n', 'w', 'a', 'b', 's', 4, 'n', 'w', 'a', 's', 5, 'n', 'w', 'a', 's', 's', 5, 'n', 'w', 'a', 'n', 'g', 4, 'n', 'w', 'a', 'j', 4, 'n', 'w', 'a', 'c', 4, 'n', 'w', 'a', 'k', 4, 'n', 'w', 'a', 't', 4, 'n', 'w', 'a', 'p', 4, 'n', 'w', 'a', 'h', 4, 'n', 'w', 'a', 'e', 5, 'n', 'w', 'a', 'e', 'g', 6, 'n', 'w', 'a', 'e', 'g', 'g', 6, 'n', 'w', 'a', 'e', 'g', 's', 5, 'n', 'w', 'a', 'e', 'n', 6, 'n', 'w', 'a', 'e', 'n', 'j', 6, 'n', 'w', 'a', 'e', 'n', 'h', 5, 'n', 'w', 'a', 'e', 'd', 5, 'n', 'w', 'a', 'e', 'l', 6, 'n', 'w', 'a', 'e', 'l', 'g', 6, 'n', 'w', 'a', 'e', 'l', 'm', 6, 'n', 'w', 'a', 'e', 'l', 'b', 6, 'n', 'w', 'a', 'e', 'l', 's', 6, 'n', 'w', 'a', 'e', 'l', 't', 6, 'n', 'w', 'a', 'e', 'l', 'p', 6, 'n', 'w', 'a', 'e', 'l', 'h', 5, 'n', 'w', 'a', 'e', 'm', 5, 'n', 'w', 'a', 'e', 'b', 6, 'n', 'w', 'a', 'e', 'b', 's', 5, 'n', 'w', 'a', 'e', 's', 6, 'n', 'w', 'a', 'e', 's', 's', 6, 'n', 'w', 'a', 'e', 'n', 'g', 5, 'n', 'w', 'a', 'e', 'j', 5, 'n', 'w', 'a', 'e', 'c', 5, 'n', 'w', 'a', 'e', 'k', 5, 'n', 'w', 'a', 'e', 't', 5, 'n', 'w', 'a', 'e', 'p', 5, 'n', 'w', 'a', 'e', 'h', 3, 'n', 'o', 'e', 4, 'n', 'o', 'e', 'g', 5, 'n', 'o', 'e', 'g', 'g', 5, 'n', 'o', 'e', 'g', 's', 4, 'n', 'o', 'e', 'n', 5, 'n', 'o', 'e', 'n', 'j', 5, 'n', 'o', 'e', 'n', 'h', 4, 'n', 'o', 'e', 'd', 4, 'n', 'o', 'e', 'l', 5, 'n', 'o', 'e', 'l', 'g', 5, 'n', 'o', 'e', 'l', 'm', 5, 'n', 'o', 'e', 'l', 'b', 5, 'n', 'o', 'e', 'l', 's', 5, 'n', 'o', 'e', 'l', 't', 5, 'n', 'o', 'e', 'l', 'p', 5, 'n', 'o', 'e', 'l', 'h', 4, 'n', 'o', 'e', 'm', 4, 'n', 'o', 'e', 'b', 5, 'n', 'o', 'e', 'b', 's', 4, 'n', 'o', 'e', 's', 5, 'n', 'o', 'e', 's', 's', 5, 'n', 'o', 'e', 'n', 'g', 4, 'n', 'o', 'e', 'j', 4, 'n', 'o', 'e', 'c', 4, 'n', 'o', 'e', 'k', 4, 'n', 'o', 'e', 't', 4, 'n', 'o', 'e', 'p', 4, 'n', 'o', 'e', 'h', 3, 'n', 'y', 'o', 4, 'n', 'y', 'o', 'g', 5, 'n', 'y', 'o', 'g', 'g', 5, 'n', 'y', 'o', 'g', 's', 4, 'n', 'y', 'o', 'n', 5, 'n', 'y', 'o', 'n', 'j', 5, 'n', 'y', 'o', 'n', 'h', 4, 'n', 'y', 'o', 'd', 4, 'n', 'y', 'o', 'l', 5, 'n', 'y', 'o', 'l', 'g', 5, 'n', 'y', 'o', 'l', 'm', 5, 'n', 'y', 'o', 'l', 'b', 5, 'n', 'y', 'o', 'l', 's', 5, 'n', 'y', 'o', 'l', 't', 5, 'n', 'y', 'o', 'l', 'p', 5, 'n', 'y', 'o', 'l', 'h', 4, 'n', 'y', 'o', 'm', 4, 'n', 'y', 'o', 'b', 5, 'n', 'y', 'o', 'b', 's', 4, 'n', 'y', 'o', 's', 5, 'n', 'y', 'o', 's', 's', 5, 'n', 'y', 'o', 'n', 'g', 4, 'n', 'y', 'o', 'j', 4, 'n', 'y', 'o', 'c', 4, 'n', 'y', 'o', 'k', 4, 'n', 'y', 'o', 't', 4, 'n', 'y', 'o', 'p', 4, 'n', 'y', 'o', 'h', 2, 'n', 'u', 3, 'n', 'u', 'g', 4, 'n', 'u', 'g', 'g', 4, 'n', 'u', 'g', 's', 3, 'n', 'u', 'n', 4, 'n', 'u', 'n', 'j', 4, 'n', 'u', 'n', 'h', 3, 'n', 'u', 'd', 3, 'n', 'u', 'l', 4, 'n', 'u', 'l', 'g', 4, 'n', 'u', 'l', 'm', 4, 'n', 'u', 'l', 'b', 4, 'n', 'u', 'l', 's', 4, 'n', 'u', 'l', 't', 4, 'n', 'u', 'l', 'p', 4, 'n', 'u', 'l', 'h', 3, 'n', 'u', 'm', 3, 'n', 'u', 'b', 4, 'n', 'u', 'b', 's', 3, 'n', 'u', 's', 4, 'n', 'u', 's', 's', 4, 'n', 'u', 'n', 'g', 3, 'n', 'u', 'j', 3, 'n', 'u', 'c', 3, 'n', 'u', 'k', 3, 'n', 'u', 't', 3, 'n', 'u', 'p', 3, 'n', 'u', 'h', 4, 'n', 'w', 'e', 'o', 5, 'n', 'w', 'e', 'o', 'g', 6, 'n', 'w', 'e', 'o', 'g', 'g', 6, 'n', 'w', 'e', 'o', 'g', 's', 5, 'n', 'w', 'e', 'o', 'n', 6, 'n', 'w', 'e', 'o', 'n', 'j', 6, 'n', 'w', 'e', 'o', 'n', 'h', 5, 'n', 'w', 'e', 'o', 'd', 5, 'n', 'w', 'e', 'o', 'l', 6, 'n', 'w', 'e', 'o', 'l', 'g', 6, 'n', 'w', 'e', 'o', 'l', 'm', 6, 'n', 'w', 'e', 'o', 'l', 'b', 6, 'n', 'w', 'e', 'o', 'l', 's', 6, 'n', 'w', 'e', 'o', 'l', 't', 6, 'n', 'w', 'e', 'o', 'l', 'p', 6, 'n', 'w', 'e', 'o', 'l', 'h', 5, 'n', 'w', 'e', 'o', 'm', 5, 'n', 'w', 'e', 'o', 'b', 6, 'n', 'w', 'e', 'o', 'b', 's', 5, 'n', 'w', 'e', 'o', 's', 6, 'n', 'w', 'e', 'o', 's', 's', 6, 'n', 'w', 'e', 'o', 'n', 'g', 5, 'n', 'w', 'e', 'o', 'j', 5, 'n', 'w', 'e', 'o', 'c', 5, 'n', 'w', 'e', 'o', 'k', 5, 'n', 'w', 'e', 'o', 't', 5, 'n', 'w', 'e', 'o', 'p', 5, 'n', 'w', 'e', 'o', 'h', 3, 'n', 'w', 'e', 4, 'n', 'w', 'e', 'g', 5, 'n', 'w', 'e', 'g', 'g', 5, 'n', 'w', 'e', 'g', 's', 4, 'n', 'w', 'e', 'n', 5, 'n', 'w', 'e', 'n', 'j', 5, 'n', 'w', 'e', 'n', 'h', 4, 'n', 'w', 'e', 'd', 4, 'n', 'w', 'e', 'l', 5, 'n', 'w', 'e', 'l', 'g', 5, 'n', 'w', 'e', 'l', 'm', 5, 'n', 'w', 'e', 'l', 'b', 5, 'n', 'w', 'e', 'l', 's', 5, 'n', 'w', 'e', 'l', 't', 5, 'n', 'w', 'e', 'l', 'p', 5, 'n', 'w', 'e', 'l', 'h', 4, 'n', 'w', 'e', 'm', 4, 'n', 'w', 'e', 'b', 5, 'n', 'w', 'e', 'b', 's', 4, 'n', 'w', 'e', 's', 5, 'n', 'w', 'e', 's', 's', 5, 'n', 'w', 'e', 'n', 'g', 4, 'n', 'w', 'e', 'j', 4, 'n', 'w', 'e', 'c', 4, 'n', 'w', 'e', 'k', 4, 'n', 'w', 'e', 't', 4, 'n', 'w', 'e', 'p', 4, 'n', 'w', 'e', 'h', 3, 'n', 'w', 'i', 4, 'n', 'w', 'i', 'g', 5, 'n', 'w', 'i', 'g', 'g', 5, 'n', 'w', 'i', 'g', 's', 4, 'n', 'w', 'i', 'n', 5, 'n', 'w', 'i', 'n', 'j', 5, 'n', 'w', 'i', 'n', 'h', 4, 'n', 'w', 'i', 'd', 4, 'n', 'w', 'i', 'l', 5, 'n', 'w', 'i', 'l', 'g', 5, 'n', 'w', 'i', 'l', 'm', 5, 'n', 'w', 'i', 'l', 'b', 5, 'n', 'w', 'i', 'l', 's', 5, 'n', 'w', 'i', 'l', 't', 5, 'n', 'w', 'i', 'l', 'p', 5, 'n', 'w', 'i', 'l', 'h', 4, 'n', 'w', 'i', 'm', 4, 'n', 'w', 'i', 'b', 5, 'n', 'w', 'i', 'b', 's', 4, 'n', 'w', 'i', 's', 5, 'n', 'w', 'i', 's', 's', 5, 'n', 'w', 'i', 'n', 'g', 4, 'n', 'w', 'i', 'j', 4, 'n', 'w', 'i', 'c', 4, 'n', 'w', 'i', 'k', 4, 'n', 'w', 'i', 't', 4, 'n', 'w', 'i', 'p', 4, 'n', 'w', 'i', 'h', 3, 'n', 'y', 'u', 4, 'n', 'y', 'u', 'g', 5, 'n', 'y', 'u', 'g', 'g', 5, 'n', 'y', 'u', 'g', 's', 4, 'n', 'y', 'u', 'n', 5, 'n', 'y', 'u', 'n', 'j', 5, 'n', 'y', 'u', 'n', 'h', 4, 'n', 'y', 'u', 'd', 4, 'n', 'y', 'u', 'l', 5, 'n', 'y', 'u', 'l', 'g', 5, 'n', 'y', 'u', 'l', 'm', 5, 'n', 'y', 'u', 'l', 'b', 5, 'n', 'y', 'u', 'l', 's', 5, 'n', 'y', 'u', 'l', 't', 5, 'n', 'y', 'u', 'l', 'p', 5, 'n', 'y', 'u', 'l', 'h', 4, 'n', 'y', 'u', 'm', 4, 'n', 'y', 'u', 'b', 5, 'n', 'y', 'u', 'b', 's', 4, 'n', 'y', 'u', 's', 5, 'n', 'y', 'u', 's', 's', 5, 'n', 'y', 'u', 'n', 'g', 4, 'n', 'y', 'u', 'j', 4, 'n', 'y', 'u', 'c', 4, 'n', 'y', 'u', 'k', 4, 'n', 'y', 'u', 't', 4, 'n', 'y', 'u', 'p', 4, 'n', 'y', 'u', 'h', 3, 'n', 'e', 'u', 4, 'n', 'e', 'u', 'g', 5, 'n', 'e', 'u', 'g', 'g', 5, 'n', 'e', 'u', 'g', 's', 4, 'n', 'e', 'u', 'n', 5, 'n', 'e', 'u', 'n', 'j', 5, 'n', 'e', 'u', 'n', 'h', 4, 'n', 'e', 'u', 'd', 4, 'n', 'e', 'u', 'l', 5, 'n', 'e', 'u', 'l', 'g', 5, 'n', 'e', 'u', 'l', 'm', 5, 'n', 'e', 'u', 'l', 'b', 5, 'n', 'e', 'u', 'l', 's', 5, 'n', 'e', 'u', 'l', 't', 5, 'n', 'e', 'u', 'l', 'p', 5, 'n', 'e', 'u', 'l', 'h', 4, 'n', 'e', 'u', 'm', 4, 'n', 'e', 'u', 'b', 5, 'n', 'e', 'u', 'b', 's', 4, 'n', 'e', 'u', 's', 5, 'n', 'e', 'u', 's', 's', 5, 'n', 'e', 'u', 'n', 'g', 4, 'n', 'e', 'u', 'j', 4, 'n', 'e', 'u', 'c', 4, 'n', 'e', 'u', 'k', 4, 'n', 'e', 'u', 't', 4, 'n', 'e', 'u', 'p', 4, 'n', 'e', 'u', 'h', 3, 'n', 'y', 'i', 4, 'n', 'y', 'i', 'g', 5, 'n', 'y', 'i', 'g', 'g', 5, 'n', 'y', 'i', 'g', 's', 4, 'n', 'y', 'i', 'n', 5, 'n', 'y', 'i', 'n', 'j', 5, 'n', 'y', 'i', 'n', 'h', 4, 'n', 'y', 'i', 'd', 4, 'n', 'y', 'i', 'l', 5, 'n', 'y', 'i', 'l', 'g', 5, 'n', 'y', 'i', 'l', 'm', 5, 'n', 'y', 'i', 'l', 'b', 5, 'n', 'y', 'i', 'l', 's', 5, 'n', 'y', 'i', 'l', 't', 5, 'n', 'y', 'i', 'l', 'p', 5, 'n', 'y', 'i', 'l', 'h', 4, 'n', 'y', 'i', 'm', 4, 'n', 'y', 'i', 'b', 5, 'n', 'y', 'i', 'b', 's', 4, 'n', 'y', 'i', 's', 5, 'n', 'y', 'i', 's', 's', 5, 'n', 'y', 'i', 'n', 'g', 4, 'n', 'y', 'i', 'j', 4, 'n', 'y', 'i', 'c', 4, 'n', 'y', 'i', 'k', 4, 'n', 'y', 'i', 't', 4, 'n', 'y', 'i', 'p', 4, 'n', 'y', 'i', 'h', 2, 'n', 'i', 3, 'n', 'i', 'g', 4, 'n', 'i', 'g', 'g', 4, 'n', 'i', 'g', 's', 3, 'n', 'i', 'n', 4, 'n', 'i', 'n', 'j', 4, 'n', 'i', 'n', 'h', 3, 'n', 'i', 'd', 3, 'n', 'i', 'l', 4, 'n', 'i', 'l', 'g', 4, 'n', 'i', 'l', 'm', 4, 'n', 'i', 'l', 'b', 4, 'n', 'i', 'l', 's', 4, 'n', 'i', 'l', 't', 4, 'n', 'i', 'l', 'p', 4, 'n', 'i', 'l', 'h', 3, 'n', 'i', 'm', 3, 'n', 'i', 'b', 4, 'n', 'i', 'b', 's', 3, 'n', 'i', 's', 4, 'n', 'i', 's', 's', 4, 'n', 'i', 'n', 'g', 3, 'n', 'i', 'j', 3, 'n', 'i', 'c', 3, 'n', 'i', 'k', 3, 'n', 'i', 't', 3, 'n', 'i', 'p', 3, 'n', 'i', 'h', 2, 'd', 'a', 3, 'd', 'a', 'g', 4, 'd', 'a', 'g', 'g', 4, 'd', 'a', 'g', 's', 3, 'd', 'a', 'n', 4, 'd', 'a', 'n', 'j', 4, 'd', 'a', 'n', 'h', 3, 'd', 'a', 'd', 3, 'd', 'a', 'l', 4, 'd', 'a', 'l', 'g', 4, 'd', 'a', 'l', 'm', 4, 'd', 'a', 'l', 'b', 4, 'd', 'a', 'l', 's', 4, 'd', 'a', 'l', 't', 4, 'd', 'a', 'l', 'p', 4, 'd', 'a', 'l', 'h', 3, 'd', 'a', 'm', 3, 'd', 'a', 'b', 4, 'd', 'a', 'b', 's', 3, 'd', 'a', 's', 4, 'd', 'a', 's', 's', 4, 'd', 'a', 'n', 'g', 3, 'd', 'a', 'j', 3, 'd', 'a', 'c', 3, 'd', 'a', 'k', 3, 'd', 'a', 't', 3, 'd', 'a', 'p', 3, 'd', 'a', 'h', 3, 'd', 'a', 'e', 4, 'd', 'a', 'e', 'g', 5, 'd', 'a', 'e', 'g', 'g', 5, 'd', 'a', 'e', 'g', 's', 4, 'd', 'a', 'e', 'n', 5, 'd', 'a', 'e', 'n', 'j', 5, 'd', 'a', 'e', 'n', 'h', 4, 'd', 'a', 'e', 'd', 4, 'd', 'a', 'e', 'l', 5, 'd', 'a', 'e', 'l', 'g', 5, 'd', 'a', 'e', 'l', 'm', 5, 'd', 'a', 'e', 'l', 'b', 5, 'd', 'a', 'e', 'l', 's', 5, 'd', 'a', 'e', 'l', 't', 5, 'd', 'a', 'e', 'l', 'p', 5, 'd', 'a', 'e', 'l', 'h', 4, 'd', 'a', 'e', 'm', 4, 'd', 'a', 'e', 'b', 5, 'd', 'a', 'e', 'b', 's', 4, 'd', 'a', 'e', 's', 5, 'd', 'a', 'e', 's', 's', 5, 'd', 'a', 'e', 'n', 'g', 4, 'd', 'a', 'e', 'j', 4, 'd', 'a', 'e', 'c', 4, 'd', 'a', 'e', 'k', 4, 'd', 'a', 'e', 't', 4, 'd', 'a', 'e', 'p', 4, 'd', 'a', 'e', 'h', 3, 'd', 'y', 'a', 4, 'd', 'y', 'a', 'g', 5, 'd', 'y', 'a', 'g', 'g', 5, 'd', 'y', 'a', 'g', 's', 4, 'd', 'y', 'a', 'n', 5, 'd', 'y', 'a', 'n', 'j', 5, 'd', 'y', 'a', 'n', 'h', 4, 'd', 'y', 'a', 'd', 4, 'd', 'y', 'a', 'l', 5, 'd', 'y', 'a', 'l', 'g', 5, 'd', 'y', 'a', 'l', 'm', 5, 'd', 'y', 'a', 'l', 'b', 5, 'd', 'y', 'a', 'l', 's', 5, 'd', 'y', 'a', 'l', 't', 5, 'd', 'y', 'a', 'l', 'p', 5, 'd', 'y', 'a', 'l', 'h', 4, 'd', 'y', 'a', 'm', 4, 'd', 'y', 'a', 'b', 5, 'd', 'y', 'a', 'b', 's', 4, 'd', 'y', 'a', 's', 5, 'd', 'y', 'a', 's', 's', 5, 'd', 'y', 'a', 'n', 'g', 4, 'd', 'y', 'a', 'j', 4, 'd', 'y', 'a', 'c', 4, 'd', 'y', 'a', 'k', 4, 'd', 'y', 'a', 't', 4, 'd', 'y', 'a', 'p', 4, 'd', 'y', 'a', 'h', 4, 'd', 'y', 'a', 'e', 5, 'd', 'y', 'a', 'e', 'g', 6, 'd', 'y', 'a', 'e', 'g', 'g', 6, 'd', 'y', 'a', 'e', 'g', 's', 5, 'd', 'y', 'a', 'e', 'n', 6, 'd', 'y', 'a', 'e', 'n', 'j', 6, 'd', 'y', 'a', 'e', 'n', 'h', 5, 'd', 'y', 'a', 'e', 'd', 5, 'd', 'y', 'a', 'e', 'l', 6, 'd', 'y', 'a', 'e', 'l', 'g', 6, 'd', 'y', 'a', 'e', 'l', 'm', 6, 'd', 'y', 'a', 'e', 'l', 'b', 6, 'd', 'y', 'a', 'e', 'l', 's', 6, 'd', 'y', 'a', 'e', 'l', 't', 6, 'd', 'y', 'a', 'e', 'l', 'p', 6, 'd', 'y', 'a', 'e', 'l', 'h', 5, 'd', 'y', 'a', 'e', 'm', 5, 'd', 'y', 'a', 'e', 'b', 6, 'd', 'y', 'a', 'e', 'b', 's', 5, 'd', 'y', 'a', 'e', 's', 6, 'd', 'y', 'a', 'e', 's', 's', 6, 'd', 'y', 'a', 'e', 'n', 'g', 5, 'd', 'y', 'a', 'e', 'j', 5, 'd', 'y', 'a', 'e', 'c', 5, 'd', 'y', 'a', 'e', 'k', 5, 'd', 'y', 'a', 'e', 't', 5, 'd', 'y', 'a', 'e', 'p', 5, 'd', 'y', 'a', 'e', 'h', 3, 'd', 'e', 'o', 4, 'd', 'e', 'o', 'g', 5, 'd', 'e', 'o', 'g', 'g', 5, 'd', 'e', 'o', 'g', 's', 4, 'd', 'e', 'o', 'n', 5, 'd', 'e', 'o', 'n', 'j', 5, 'd', 'e', 'o', 'n', 'h', 4, 'd', 'e', 'o', 'd', 4, 'd', 'e', 'o', 'l', 5, 'd', 'e', 'o', 'l', 'g', 5, 'd', 'e', 'o', 'l', 'm', 5, 'd', 'e', 'o', 'l', 'b', 5, 'd', 'e', 'o', 'l', 's', 5, 'd', 'e', 'o', 'l', 't', 5, 'd', 'e', 'o', 'l', 'p', 5, 'd', 'e', 'o', 'l', 'h', 4, 'd', 'e', 'o', 'm', 4, 'd', 'e', 'o', 'b', 5, 'd', 'e', 'o', 'b', 's', 4, 'd', 'e', 'o', 's', 5, 'd', 'e', 'o', 's', 's', 5, 'd', 'e', 'o', 'n', 'g', 4, 'd', 'e', 'o', 'j', 4, 'd', 'e', 'o', 'c', 4, 'd', 'e', 'o', 'k', 4, 'd', 'e', 'o', 't', 4, 'd', 'e', 'o', 'p', 4, 'd', 'e', 'o', 'h', 2, 'd', 'e', 3, 'd', 'e', 'g', 4, 'd', 'e', 'g', 'g', 4, 'd', 'e', 'g', 's', 3, 'd', 'e', 'n', 4, 'd', 'e', 'n', 'j', 4, 'd', 'e', 'n', 'h', 3, 'd', 'e', 'd', 3, 'd', 'e', 'l', 4, 'd', 'e', 'l', 'g', 4, 'd', 'e', 'l', 'm', 4, 'd', 'e', 'l', 'b', 4, 'd', 'e', 'l', 's', 4, 'd', 'e', 'l', 't', 4, 'd', 'e', 'l', 'p', 4, 'd', 'e', 'l', 'h', 3, 'd', 'e', 'm', 3, 'd', 'e', 'b', 4, 'd', 'e', 'b', 's', 3, 'd', 'e', 's', 4, 'd', 'e', 's', 's', 4, 'd', 'e', 'n', 'g', 3, 'd', 'e', 'j', 3, 'd', 'e', 'c', 3, 'd', 'e', 'k', 3, 'd', 'e', 't', 3, 'd', 'e', 'p', 3, 'd', 'e', 'h', 4, 'd', 'y', 'e', 'o', 5, 'd', 'y', 'e', 'o', 'g', 6, 'd', 'y', 'e', 'o', 'g', 'g', 6, 'd', 'y', 'e', 'o', 'g', 's', 5, 'd', 'y', 'e', 'o', 'n', 6, 'd', 'y', 'e', 'o', 'n', 'j', 6, 'd', 'y', 'e', 'o', 'n', 'h', 5, 'd', 'y', 'e', 'o', 'd', 5, 'd', 'y', 'e', 'o', 'l', 6, 'd', 'y', 'e', 'o', 'l', 'g', 6, 'd', 'y', 'e', 'o', 'l', 'm', 6, 'd', 'y', 'e', 'o', 'l', 'b', 6, 'd', 'y', 'e', 'o', 'l', 's', 6, 'd', 'y', 'e', 'o', 'l', 't', 6, 'd', 'y', 'e', 'o', 'l', 'p', 6, 'd', 'y', 'e', 'o', 'l', 'h', 5, 'd', 'y', 'e', 'o', 'm', 5, 'd', 'y', 'e', 'o', 'b', 6, 'd', 'y', 'e', 'o', 'b', 's', 5, 'd', 'y', 'e', 'o', 's', 6, 'd', 'y', 'e', 'o', 's', 's', 6, 'd', 'y', 'e', 'o', 'n', 'g', 5, 'd', 'y', 'e', 'o', 'j', 5, 'd', 'y', 'e', 'o', 'c', 5, 'd', 'y', 'e', 'o', 'k', 5, 'd', 'y', 'e', 'o', 't', 5, 'd', 'y', 'e', 'o', 'p', 5, 'd', 'y', 'e', 'o', 'h', 3, 'd', 'y', 'e', 4, 'd', 'y', 'e', 'g', 5, 'd', 'y', 'e', 'g', 'g', 5, 'd', 'y', 'e', 'g', 's', 4, 'd', 'y', 'e', 'n', 5, 'd', 'y', 'e', 'n', 'j', 5, 'd', 'y', 'e', 'n', 'h', 4, 'd', 'y', 'e', 'd', 4, 'd', 'y', 'e', 'l', 5, 'd', 'y', 'e', 'l', 'g', 5, 'd', 'y', 'e', 'l', 'm', 5, 'd', 'y', 'e', 'l', 'b', 5, 'd', 'y', 'e', 'l', 's', 5, 'd', 'y', 'e', 'l', 't', 5, 'd', 'y', 'e', 'l', 'p', 5, 'd', 'y', 'e', 'l', 'h', 4, 'd', 'y', 'e', 'm', 4, 'd', 'y', 'e', 'b', 5, 'd', 'y', 'e', 'b', 's', 4, 'd', 'y', 'e', 's', 5, 'd', 'y', 'e', 's', 's', 5, 'd', 'y', 'e', 'n', 'g', 4, 'd', 'y', 'e', 'j', 4, 'd', 'y', 'e', 'c', 4, 'd', 'y', 'e', 'k', 4, 'd', 'y', 'e', 't', 4, 'd', 'y', 'e', 'p', 4, 'd', 'y', 'e', 'h', 2, 'd', 'o', 3, 'd', 'o', 'g', 4, 'd', 'o', 'g', 'g', 4, 'd', 'o', 'g', 's', 3, 'd', 'o', 'n', 4, 'd', 'o', 'n', 'j', 4, 'd', 'o', 'n', 'h', 3, 'd', 'o', 'd', 3, 'd', 'o', 'l', 4, 'd', 'o', 'l', 'g', 4, 'd', 'o', 'l', 'm', 4, 'd', 'o', 'l', 'b', 4, 'd', 'o', 'l', 's', 4, 'd', 'o', 'l', 't', 4, 'd', 'o', 'l', 'p', 4, 'd', 'o', 'l', 'h', 3, 'd', 'o', 'm', 3, 'd', 'o', 'b', 4, 'd', 'o', 'b', 's', 3, 'd', 'o', 's', 4, 'd', 'o', 's', 's', 4, 'd', 'o', 'n', 'g', 3, 'd', 'o', 'j', 3, 'd', 'o', 'c', 3, 'd', 'o', 'k', 3, 'd', 'o', 't', 3, 'd', 'o', 'p', 3, 'd', 'o', 'h', 3, 'd', 'w', 'a', 4, 'd', 'w', 'a', 'g', 5, 'd', 'w', 'a', 'g', 'g', 5, 'd', 'w', 'a', 'g', 's', 4, 'd', 'w', 'a', 'n', 5, 'd', 'w', 'a', 'n', 'j', 5, 'd', 'w', 'a', 'n', 'h', 4, 'd', 'w', 'a', 'd', 4, 'd', 'w', 'a', 'l', 5, 'd', 'w', 'a', 'l', 'g', 5, 'd', 'w', 'a', 'l', 'm', 5, 'd', 'w', 'a', 'l', 'b', 5, 'd', 'w', 'a', 'l', 's', 5, 'd', 'w', 'a', 'l', 't', 5, 'd', 'w', 'a', 'l', 'p', 5, 'd', 'w', 'a', 'l', 'h', 4, 'd', 'w', 'a', 'm', 4, 'd', 'w', 'a', 'b', 5, 'd', 'w', 'a', 'b', 's', 4, 'd', 'w', 'a', 's', 5, 'd', 'w', 'a', 's', 's', 5, 'd', 'w', 'a', 'n', 'g', 4, 'd', 'w', 'a', 'j', 4, 'd', 'w', 'a', 'c', 4, 'd', 'w', 'a', 'k', 4, 'd', 'w', 'a', 't', 4, 'd', 'w', 'a', 'p', 4, 'd', 'w', 'a', 'h', 4, 'd', 'w', 'a', 'e', 5, 'd', 'w', 'a', 'e', 'g', 6, 'd', 'w', 'a', 'e', 'g', 'g', 6, 'd', 'w', 'a', 'e', 'g', 's', 5, 'd', 'w', 'a', 'e', 'n', 6, 'd', 'w', 'a', 'e', 'n', 'j', 6, 'd', 'w', 'a', 'e', 'n', 'h', 5, 'd', 'w', 'a', 'e', 'd', 5, 'd', 'w', 'a', 'e', 'l', 6, 'd', 'w', 'a', 'e', 'l', 'g', 6, 'd', 'w', 'a', 'e', 'l', 'm', 6, 'd', 'w', 'a', 'e', 'l', 'b', 6, 'd', 'w', 'a', 'e', 'l', 's', 6, 'd', 'w', 'a', 'e', 'l', 't', 6, 'd', 'w', 'a', 'e', 'l', 'p', 6, 'd', 'w', 'a', 'e', 'l', 'h', 5, 'd', 'w', 'a', 'e', 'm', 5, 'd', 'w', 'a', 'e', 'b', 6, 'd', 'w', 'a', 'e', 'b', 's', 5, 'd', 'w', 'a', 'e', 's', 6, 'd', 'w', 'a', 'e', 's', 's', 6, 'd', 'w', 'a', 'e', 'n', 'g', 5, 'd', 'w', 'a', 'e', 'j', 5, 'd', 'w', 'a', 'e', 'c', 5, 'd', 'w', 'a', 'e', 'k', 5, 'd', 'w', 'a', 'e', 't', 5, 'd', 'w', 'a', 'e', 'p', 5, 'd', 'w', 'a', 'e', 'h', 3, 'd', 'o', 'e', 4, 'd', 'o', 'e', 'g', 5, 'd', 'o', 'e', 'g', 'g', 5, 'd', 'o', 'e', 'g', 's', 4, 'd', 'o', 'e', 'n', 5, 'd', 'o', 'e', 'n', 'j', 5, 'd', 'o', 'e', 'n', 'h', 4, 'd', 'o', 'e', 'd', 4, 'd', 'o', 'e', 'l', 5, 'd', 'o', 'e', 'l', 'g', 5, 'd', 'o', 'e', 'l', 'm', 5, 'd', 'o', 'e', 'l', 'b', 5, 'd', 'o', 'e', 'l', 's', 5, 'd', 'o', 'e', 'l', 't', 5, 'd', 'o', 'e', 'l', 'p', 5, 'd', 'o', 'e', 'l', 'h', 4, 'd', 'o', 'e', 'm', 4, 'd', 'o', 'e', 'b', 5, 'd', 'o', 'e', 'b', 's', 4, 'd', 'o', 'e', 's', 5, 'd', 'o', 'e', 's', 's', 5, 'd', 'o', 'e', 'n', 'g', 4, 'd', 'o', 'e', 'j', 4, 'd', 'o', 'e', 'c', 4, 'd', 'o', 'e', 'k', 4, 'd', 'o', 'e', 't', 4, 'd', 'o', 'e', 'p', 4, 'd', 'o', 'e', 'h', 3, 'd', 'y', 'o', 4, 'd', 'y', 'o', 'g', 5, 'd', 'y', 'o', 'g', 'g', 5, 'd', 'y', 'o', 'g', 's', 4, 'd', 'y', 'o', 'n', 5, 'd', 'y', 'o', 'n', 'j', 5, 'd', 'y', 'o', 'n', 'h', 4, 'd', 'y', 'o', 'd', 4, 'd', 'y', 'o', 'l', 5, 'd', 'y', 'o', 'l', 'g', 5, 'd', 'y', 'o', 'l', 'm', 5, 'd', 'y', 'o', 'l', 'b', 5, 'd', 'y', 'o', 'l', 's', 5, 'd', 'y', 'o', 'l', 't', 5, 'd', 'y', 'o', 'l', 'p', 5, 'd', 'y', 'o', 'l', 'h', 4, 'd', 'y', 'o', 'm', 4, 'd', 'y', 'o', 'b', 5, 'd', 'y', 'o', 'b', 's', 4, 'd', 'y', 'o', 's', 5, 'd', 'y', 'o', 's', 's', 5, 'd', 'y', 'o', 'n', 'g', 4, 'd', 'y', 'o', 'j', 4, 'd', 'y', 'o', 'c', 4, 'd', 'y', 'o', 'k', 4, 'd', 'y', 'o', 't', 4, 'd', 'y', 'o', 'p', 4, 'd', 'y', 'o', 'h', 2, 'd', 'u', 3, 'd', 'u', 'g', 4, 'd', 'u', 'g', 'g', 4, 'd', 'u', 'g', 's', 3, 'd', 'u', 'n', 4, 'd', 'u', 'n', 'j', 4, 'd', 'u', 'n', 'h', 3, 'd', 'u', 'd', 3, 'd', 'u', 'l', 4, 'd', 'u', 'l', 'g', 4, 'd', 'u', 'l', 'm', 4, 'd', 'u', 'l', 'b', 4, 'd', 'u', 'l', 's', 4, 'd', 'u', 'l', 't', 4, 'd', 'u', 'l', 'p', 4, 'd', 'u', 'l', 'h', 3, 'd', 'u', 'm', 3, 'd', 'u', 'b', 4, 'd', 'u', 'b', 's', 3, 'd', 'u', 's', 4, 'd', 'u', 's', 's', 4, 'd', 'u', 'n', 'g', 3, 'd', 'u', 'j', 3, 'd', 'u', 'c', 3, 'd', 'u', 'k', 3, 'd', 'u', 't', 3, 'd', 'u', 'p', 3, 'd', 'u', 'h', 4, 'd', 'w', 'e', 'o', 5, 'd', 'w', 'e', 'o', 'g', 6, 'd', 'w', 'e', 'o', 'g', 'g', 6, 'd', 'w', 'e', 'o', 'g', 's', 5, 'd', 'w', 'e', 'o', 'n', 6, 'd', 'w', 'e', 'o', 'n', 'j', 6, 'd', 'w', 'e', 'o', 'n', 'h', 5, 'd', 'w', 'e', 'o', 'd', 5, 'd', 'w', 'e', 'o', 'l', 6, 'd', 'w', 'e', 'o', 'l', 'g', 6, 'd', 'w', 'e', 'o', 'l', 'm', 6, 'd', 'w', 'e', 'o', 'l', 'b', 6, 'd', 'w', 'e', 'o', 'l', 's', 6, 'd', 'w', 'e', 'o', 'l', 't', 6, 'd', 'w', 'e', 'o', 'l', 'p', 6, 'd', 'w', 'e', 'o', 'l', 'h', 5, 'd', 'w', 'e', 'o', 'm', 5, 'd', 'w', 'e', 'o', 'b', 6, 'd', 'w', 'e', 'o', 'b', 's', 5, 'd', 'w', 'e', 'o', 's', 6, 'd', 'w', 'e', 'o', 's', 's', 6, 'd', 'w', 'e', 'o', 'n', 'g', 5, 'd', 'w', 'e', 'o', 'j', 5, 'd', 'w', 'e', 'o', 'c', 5, 'd', 'w', 'e', 'o', 'k', 5, 'd', 'w', 'e', 'o', 't', 5, 'd', 'w', 'e', 'o', 'p', 5, 'd', 'w', 'e', 'o', 'h', 3, 'd', 'w', 'e', 4, 'd', 'w', 'e', 'g', 5, 'd', 'w', 'e', 'g', 'g', 5, 'd', 'w', 'e', 'g', 's', 4, 'd', 'w', 'e', 'n', 5, 'd', 'w', 'e', 'n', 'j', 5, 'd', 'w', 'e', 'n', 'h', 4, 'd', 'w', 'e', 'd', 4, 'd', 'w', 'e', 'l', 5, 'd', 'w', 'e', 'l', 'g', 5, 'd', 'w', 'e', 'l', 'm', 5, 'd', 'w', 'e', 'l', 'b', 5, 'd', 'w', 'e', 'l', 's', 5, 'd', 'w', 'e', 'l', 't', 5, 'd', 'w', 'e', 'l', 'p', 5, 'd', 'w', 'e', 'l', 'h', 4, 'd', 'w', 'e', 'm', 4, 'd', 'w', 'e', 'b', 5, 'd', 'w', 'e', 'b', 's', 4, 'd', 'w', 'e', 's', 5, 'd', 'w', 'e', 's', 's', 5, 'd', 'w', 'e', 'n', 'g', 4, 'd', 'w', 'e', 'j', 4, 'd', 'w', 'e', 'c', 4, 'd', 'w', 'e', 'k', 4, 'd', 'w', 'e', 't', 4, 'd', 'w', 'e', 'p', 4, 'd', 'w', 'e', 'h', 3, 'd', 'w', 'i', 4, 'd', 'w', 'i', 'g', 5, 'd', 'w', 'i', 'g', 'g', 5, 'd', 'w', 'i', 'g', 's', 4, 'd', 'w', 'i', 'n', 5, 'd', 'w', 'i', 'n', 'j', 5, 'd', 'w', 'i', 'n', 'h', 4, 'd', 'w', 'i', 'd', 4, 'd', 'w', 'i', 'l', 5, 'd', 'w', 'i', 'l', 'g', 5, 'd', 'w', 'i', 'l', 'm', 5, 'd', 'w', 'i', 'l', 'b', 5, 'd', 'w', 'i', 'l', 's', 5, 'd', 'w', 'i', 'l', 't', 5, 'd', 'w', 'i', 'l', 'p', 5, 'd', 'w', 'i', 'l', 'h', 4, 'd', 'w', 'i', 'm', 4, 'd', 'w', 'i', 'b', 5, 'd', 'w', 'i', 'b', 's', 4, 'd', 'w', 'i', 's', 5, 'd', 'w', 'i', 's', 's', 5, 'd', 'w', 'i', 'n', 'g', 4, 'd', 'w', 'i', 'j', 4, 'd', 'w', 'i', 'c', 4, 'd', 'w', 'i', 'k', 4, 'd', 'w', 'i', 't', 4, 'd', 'w', 'i', 'p', 4, 'd', 'w', 'i', 'h', 3, 'd', 'y', 'u', 4, 'd', 'y', 'u', 'g', 5, 'd', 'y', 'u', 'g', 'g', 5, 'd', 'y', 'u', 'g', 's', 4, 'd', 'y', 'u', 'n', 5, 'd', 'y', 'u', 'n', 'j', 5, 'd', 'y', 'u', 'n', 'h', 4, 'd', 'y', 'u', 'd', 4, 'd', 'y', 'u', 'l', 5, 'd', 'y', 'u', 'l', 'g', 5, 'd', 'y', 'u', 'l', 'm', 5, 'd', 'y', 'u', 'l', 'b', 5, 'd', 'y', 'u', 'l', 's', 5, 'd', 'y', 'u', 'l', 't', 5, 'd', 'y', 'u', 'l', 'p', 5, 'd', 'y', 'u', 'l', 'h', 4, 'd', 'y', 'u', 'm', 4, 'd', 'y', 'u', 'b', 5, 'd', 'y', 'u', 'b', 's', 4, 'd', 'y', 'u', 's', 5, 'd', 'y', 'u', 's', 's', 5, 'd', 'y', 'u', 'n', 'g', 4, 'd', 'y', 'u', 'j', 4, 'd', 'y', 'u', 'c', 4, 'd', 'y', 'u', 'k', 4, 'd', 'y', 'u', 't', 4, 'd', 'y', 'u', 'p', 4, 'd', 'y', 'u', 'h', 3, 'd', 'e', 'u', 4, 'd', 'e', 'u', 'g', 5, 'd', 'e', 'u', 'g', 'g', 5, 'd', 'e', 'u', 'g', 's', 4, 'd', 'e', 'u', 'n', 5, 'd', 'e', 'u', 'n', 'j', 5, 'd', 'e', 'u', 'n', 'h', 4, 'd', 'e', 'u', 'd', 4, 'd', 'e', 'u', 'l', 5, 'd', 'e', 'u', 'l', 'g', 5, 'd', 'e', 'u', 'l', 'm', 5, 'd', 'e', 'u', 'l', 'b', 5, 'd', 'e', 'u', 'l', 's', 5, 'd', 'e', 'u', 'l', 't', 5, 'd', 'e', 'u', 'l', 'p', 5, 'd', 'e', 'u', 'l', 'h', 4, 'd', 'e', 'u', 'm', 4, 'd', 'e', 'u', 'b', 5, 'd', 'e', 'u', 'b', 's', 4, 'd', 'e', 'u', 's', 5, 'd', 'e', 'u', 's', 's', 5, 'd', 'e', 'u', 'n', 'g', 4, 'd', 'e', 'u', 'j', 4, 'd', 'e', 'u', 'c', 4, 'd', 'e', 'u', 'k', 4, 'd', 'e', 'u', 't', 4, 'd', 'e', 'u', 'p', 4, 'd', 'e', 'u', 'h', 3, 'd', 'y', 'i', 4, 'd', 'y', 'i', 'g', 5, 'd', 'y', 'i', 'g', 'g', 5, 'd', 'y', 'i', 'g', 's', 4, 'd', 'y', 'i', 'n', 5, 'd', 'y', 'i', 'n', 'j', 5, 'd', 'y', 'i', 'n', 'h', 4, 'd', 'y', 'i', 'd', 4, 'd', 'y', 'i', 'l', 5, 'd', 'y', 'i', 'l', 'g', 5, 'd', 'y', 'i', 'l', 'm', 5, 'd', 'y', 'i', 'l', 'b', 5, 'd', 'y', 'i', 'l', 's', 5, 'd', 'y', 'i', 'l', 't', 5, 'd', 'y', 'i', 'l', 'p', 5, 'd', 'y', 'i', 'l', 'h', 4, 'd', 'y', 'i', 'm', 4, 'd', 'y', 'i', 'b', 5, 'd', 'y', 'i', 'b', 's', 4, 'd', 'y', 'i', 's', 5, 'd', 'y', 'i', 's', 's', 5, 'd', 'y', 'i', 'n', 'g', 4, 'd', 'y', 'i', 'j', 4, 'd', 'y', 'i', 'c', 4, 'd', 'y', 'i', 'k', 4, 'd', 'y', 'i', 't', 4, 'd', 'y', 'i', 'p', 4, 'd', 'y', 'i', 'h', 2, 'd', 'i', 3, 'd', 'i', 'g', 4, 'd', 'i', 'g', 'g', 4, 'd', 'i', 'g', 's', 3, 'd', 'i', 'n', 4, 'd', 'i', 'n', 'j', 4, 'd', 'i', 'n', 'h', 3, 'd', 'i', 'd', 3, 'd', 'i', 'l', 4, 'd', 'i', 'l', 'g', 4, 'd', 'i', 'l', 'm', 4, 'd', 'i', 'l', 'b', 4, 'd', 'i', 'l', 's', 4, 'd', 'i', 'l', 't', 4, 'd', 'i', 'l', 'p', 4, 'd', 'i', 'l', 'h', 3, 'd', 'i', 'm', 3, 'd', 'i', 'b', 4, 'd', 'i', 'b', 's', 3, 'd', 'i', 's', 4, 'd', 'i', 's', 's', 4, 'd', 'i', 'n', 'g', 3, 'd', 'i', 'j', 3, 'd', 'i', 'c', 3, 'd', 'i', 'k', 3, 'd', 'i', 't', 3, 'd', 'i', 'p', 3, 'd', 'i', 'h', 3, 'd', 'd', 'a', 4, 'd', 'd', 'a', 'g', 5, 'd', 'd', 'a', 'g', 'g', 5, 'd', 'd', 'a', 'g', 's', 4, 'd', 'd', 'a', 'n', 5, 'd', 'd', 'a', 'n', 'j', 5, 'd', 'd', 'a', 'n', 'h', 4, 'd', 'd', 'a', 'd', 4, 'd', 'd', 'a', 'l', 5, 'd', 'd', 'a', 'l', 'g', 5, 'd', 'd', 'a', 'l', 'm', 5, 'd', 'd', 'a', 'l', 'b', 5, 'd', 'd', 'a', 'l', 's', 5, 'd', 'd', 'a', 'l', 't', 5, 'd', 'd', 'a', 'l', 'p', 5, 'd', 'd', 'a', 'l', 'h', 4, 'd', 'd', 'a', 'm', 4, 'd', 'd', 'a', 'b', 5, 'd', 'd', 'a', 'b', 's', 4, 'd', 'd', 'a', 's', 5, 'd', 'd', 'a', 's', 's', 5, 'd', 'd', 'a', 'n', 'g', 4, 'd', 'd', 'a', 'j', 4, 'd', 'd', 'a', 'c', 4, 'd', 'd', 'a', 'k', 4, 'd', 'd', 'a', 't', 4, 'd', 'd', 'a', 'p', 4, 'd', 'd', 'a', 'h', 4, 'd', 'd', 'a', 'e', 5, 'd', 'd', 'a', 'e', 'g', 6, 'd', 'd', 'a', 'e', 'g', 'g', 6, 'd', 'd', 'a', 'e', 'g', 's', 5, 'd', 'd', 'a', 'e', 'n', 6, 'd', 'd', 'a', 'e', 'n', 'j', 6, 'd', 'd', 'a', 'e', 'n', 'h', 5, 'd', 'd', 'a', 'e', 'd', 5, 'd', 'd', 'a', 'e', 'l', 6, 'd', 'd', 'a', 'e', 'l', 'g', 6, 'd', 'd', 'a', 'e', 'l', 'm', 6, 'd', 'd', 'a', 'e', 'l', 'b', 6, 'd', 'd', 'a', 'e', 'l', 's', 6, 'd', 'd', 'a', 'e', 'l', 't', 6, 'd', 'd', 'a', 'e', 'l', 'p', 6, 'd', 'd', 'a', 'e', 'l', 'h', 5, 'd', 'd', 'a', 'e', 'm', 5, 'd', 'd', 'a', 'e', 'b', 6, 'd', 'd', 'a', 'e', 'b', 's', 5, 'd', 'd', 'a', 'e', 's', 6, 'd', 'd', 'a', 'e', 's', 's', 6, 'd', 'd', 'a', 'e', 'n', 'g', 5, 'd', 'd', 'a', 'e', 'j', 5, 'd', 'd', 'a', 'e', 'c', 5, 'd', 'd', 'a', 'e', 'k', 5, 'd', 'd', 'a', 'e', 't', 5, 'd', 'd', 'a', 'e', 'p', 5, 'd', 'd', 'a', 'e', 'h', 4, 'd', 'd', 'y', 'a', 5, 'd', 'd', 'y', 'a', 'g', 6, 'd', 'd', 'y', 'a', 'g', 'g', 6, 'd', 'd', 'y', 'a', 'g', 's', 5, 'd', 'd', 'y', 'a', 'n', 6, 'd', 'd', 'y', 'a', 'n', 'j', 6, 'd', 'd', 'y', 'a', 'n', 'h', 5, 'd', 'd', 'y', 'a', 'd', 5, 'd', 'd', 'y', 'a', 'l', 6, 'd', 'd', 'y', 'a', 'l', 'g', 6, 'd', 'd', 'y', 'a', 'l', 'm', 6, 'd', 'd', 'y', 'a', 'l', 'b', 6, 'd', 'd', 'y', 'a', 'l', 's', 6, 'd', 'd', 'y', 'a', 'l', 't', 6, 'd', 'd', 'y', 'a', 'l', 'p', 6, 'd', 'd', 'y', 'a', 'l', 'h', 5, 'd', 'd', 'y', 'a', 'm', 5, 'd', 'd', 'y', 'a', 'b', 6, 'd', 'd', 'y', 'a', 'b', 's', 5, 'd', 'd', 'y', 'a', 's', 6, 'd', 'd', 'y', 'a', 's', 's', 6, 'd', 'd', 'y', 'a', 'n', 'g', 5, 'd', 'd', 'y', 'a', 'j', 5, 'd', 'd', 'y', 'a', 'c', 5, 'd', 'd', 'y', 'a', 'k', 5, 'd', 'd', 'y', 'a', 't', 5, 'd', 'd', 'y', 'a', 'p', 5, 'd', 'd', 'y', 'a', 'h', 5, 'd', 'd', 'y', 'a', 'e', 6, 'd', 'd', 'y', 'a', 'e', 'g', 7, 'd', 'd', 'y', 'a', 'e', 'g', 'g', 7, 'd', 'd', 'y', 'a', 'e', 'g', 's', 6, 'd', 'd', 'y', 'a', 'e', 'n', 7, 'd', 'd', 'y', 'a', 'e', 'n', 'j', 7, 'd', 'd', 'y', 'a', 'e', 'n', 'h', 6, 'd', 'd', 'y', 'a', 'e', 'd', 6, 'd', 'd', 'y', 'a', 'e', 'l', 7, 'd', 'd', 'y', 'a', 'e', 'l', 'g', 7, 'd', 'd', 'y', 'a', 'e', 'l', 'm', 7, 'd', 'd', 'y', 'a', 'e', 'l', 'b', 7, 'd', 'd', 'y', 'a', 'e', 'l', 's', 7, 'd', 'd', 'y', 'a', 'e', 'l', 't', 7, 'd', 'd', 'y', 'a', 'e', 'l', 'p', 7, 'd', 'd', 'y', 'a', 'e', 'l', 'h', 6, 'd', 'd', 'y', 'a', 'e', 'm', 6, 'd', 'd', 'y', 'a', 'e', 'b', 7, 'd', 'd', 'y', 'a', 'e', 'b', 's', 6, 'd', 'd', 'y', 'a', 'e', 's', 7, 'd', 'd', 'y', 'a', 'e', 's', 's', 7, 'd', 'd', 'y', 'a', 'e', 'n', 'g', 6, 'd', 'd', 'y', 'a', 'e', 'j', 6, 'd', 'd', 'y', 'a', 'e', 'c', 6, 'd', 'd', 'y', 'a', 'e', 'k', 6, 'd', 'd', 'y', 'a', 'e', 't', 6, 'd', 'd', 'y', 'a', 'e', 'p', 6, 'd', 'd', 'y', 'a', 'e', 'h', 4, 'd', 'd', 'e', 'o', 5, 'd', 'd', 'e', 'o', 'g', 6, 'd', 'd', 'e', 'o', 'g', 'g', 6, 'd', 'd', 'e', 'o', 'g', 's', 5, 'd', 'd', 'e', 'o', 'n', 6, 'd', 'd', 'e', 'o', 'n', 'j', 6, 'd', 'd', 'e', 'o', 'n', 'h', 5, 'd', 'd', 'e', 'o', 'd', 5, 'd', 'd', 'e', 'o', 'l', 6, 'd', 'd', 'e', 'o', 'l', 'g', 6, 'd', 'd', 'e', 'o', 'l', 'm', 6, 'd', 'd', 'e', 'o', 'l', 'b', 6, 'd', 'd', 'e', 'o', 'l', 's', 6, 'd', 'd', 'e', 'o', 'l', 't', 6, 'd', 'd', 'e', 'o', 'l', 'p', 6, 'd', 'd', 'e', 'o', 'l', 'h', 5, 'd', 'd', 'e', 'o', 'm', 5, 'd', 'd', 'e', 'o', 'b', 6, 'd', 'd', 'e', 'o', 'b', 's', 5, 'd', 'd', 'e', 'o', 's', 6, 'd', 'd', 'e', 'o', 's', 's', 6, 'd', 'd', 'e', 'o', 'n', 'g', 5, 'd', 'd', 'e', 'o', 'j', 5, 'd', 'd', 'e', 'o', 'c', 5, 'd', 'd', 'e', 'o', 'k', 5, 'd', 'd', 'e', 'o', 't', 5, 'd', 'd', 'e', 'o', 'p', 5, 'd', 'd', 'e', 'o', 'h', 3, 'd', 'd', 'e', 4, 'd', 'd', 'e', 'g', 5, 'd', 'd', 'e', 'g', 'g', 5, 'd', 'd', 'e', 'g', 's', 4, 'd', 'd', 'e', 'n', 5, 'd', 'd', 'e', 'n', 'j', 5, 'd', 'd', 'e', 'n', 'h', 4, 'd', 'd', 'e', 'd', 4, 'd', 'd', 'e', 'l', 5, 'd', 'd', 'e', 'l', 'g', 5, 'd', 'd', 'e', 'l', 'm', 5, 'd', 'd', 'e', 'l', 'b', 5, 'd', 'd', 'e', 'l', 's', 5, 'd', 'd', 'e', 'l', 't', 5, 'd', 'd', 'e', 'l', 'p', 5, 'd', 'd', 'e', 'l', 'h', 4, 'd', 'd', 'e', 'm', 4, 'd', 'd', 'e', 'b', 5, 'd', 'd', 'e', 'b', 's', 4, 'd', 'd', 'e', 's', 5, 'd', 'd', 'e', 's', 's', 5, 'd', 'd', 'e', 'n', 'g', 4, 'd', 'd', 'e', 'j', 4, 'd', 'd', 'e', 'c', 4, 'd', 'd', 'e', 'k', 4, 'd', 'd', 'e', 't', 4, 'd', 'd', 'e', 'p', 4, 'd', 'd', 'e', 'h', 5, 'd', 'd', 'y', 'e', 'o', 6, 'd', 'd', 'y', 'e', 'o', 'g', 7, 'd', 'd', 'y', 'e', 'o', 'g', 'g', 7, 'd', 'd', 'y', 'e', 'o', 'g', 's', 6, 'd', 'd', 'y', 'e', 'o', 'n', 7, 'd', 'd', 'y', 'e', 'o', 'n', 'j', 7, 'd', 'd', 'y', 'e', 'o', 'n', 'h', 6, 'd', 'd', 'y', 'e', 'o', 'd', 6, 'd', 'd', 'y', 'e', 'o', 'l', 7, 'd', 'd', 'y', 'e', 'o', 'l', 'g', 7, 'd', 'd', 'y', 'e', 'o', 'l', 'm', 7, 'd', 'd', 'y', 'e', 'o', 'l', 'b', 7, 'd', 'd', 'y', 'e', 'o', 'l', 's', 7, 'd', 'd', 'y', 'e', 'o', 'l', 't', 7, 'd', 'd', 'y', 'e', 'o', 'l', 'p', 7, 'd', 'd', 'y', 'e', 'o', 'l', 'h', 6, 'd', 'd', 'y', 'e', 'o', 'm', 6, 'd', 'd', 'y', 'e', 'o', 'b', 7, 'd', 'd', 'y', 'e', 'o', 'b', 's', 6, 'd', 'd', 'y', 'e', 'o', 's', 7, 'd', 'd', 'y', 'e', 'o', 's', 's', 7, 'd', 'd', 'y', 'e', 'o', 'n', 'g', 6, 'd', 'd', 'y', 'e', 'o', 'j', 6, 'd', 'd', 'y', 'e', 'o', 'c', 6, 'd', 'd', 'y', 'e', 'o', 'k', 6, 'd', 'd', 'y', 'e', 'o', 't', 6, 'd', 'd', 'y', 'e', 'o', 'p', 6, 'd', 'd', 'y', 'e', 'o', 'h', 4, 'd', 'd', 'y', 'e', 5, 'd', 'd', 'y', 'e', 'g', 6, 'd', 'd', 'y', 'e', 'g', 'g', 6, 'd', 'd', 'y', 'e', 'g', 's', 5, 'd', 'd', 'y', 'e', 'n', 6, 'd', 'd', 'y', 'e', 'n', 'j', 6, 'd', 'd', 'y', 'e', 'n', 'h', 5, 'd', 'd', 'y', 'e', 'd', 5, 'd', 'd', 'y', 'e', 'l', 6, 'd', 'd', 'y', 'e', 'l', 'g', 6, 'd', 'd', 'y', 'e', 'l', 'm', 6, 'd', 'd', 'y', 'e', 'l', 'b', 6, 'd', 'd', 'y', 'e', 'l', 's', 6, 'd', 'd', 'y', 'e', 'l', 't', 6, 'd', 'd', 'y', 'e', 'l', 'p', 6, 'd', 'd', 'y', 'e', 'l', 'h', 5, 'd', 'd', 'y', 'e', 'm', 5, 'd', 'd', 'y', 'e', 'b', 6, 'd', 'd', 'y', 'e', 'b', 's', 5, 'd', 'd', 'y', 'e', 's', 6, 'd', 'd', 'y', 'e', 's', 's', 6, 'd', 'd', 'y', 'e', 'n', 'g', 5, 'd', 'd', 'y', 'e', 'j', 5, 'd', 'd', 'y', 'e', 'c', 5, 'd', 'd', 'y', 'e', 'k', 5, 'd', 'd', 'y', 'e', 't', 5, 'd', 'd', 'y', 'e', 'p', 5, 'd', 'd', 'y', 'e', 'h', 3, 'd', 'd', 'o', 4, 'd', 'd', 'o', 'g', 5, 'd', 'd', 'o', 'g', 'g', 5, 'd', 'd', 'o', 'g', 's', 4, 'd', 'd', 'o', 'n', 5, 'd', 'd', 'o', 'n', 'j', 5, 'd', 'd', 'o', 'n', 'h', 4, 'd', 'd', 'o', 'd', 4, 'd', 'd', 'o', 'l', 5, 'd', 'd', 'o', 'l', 'g', 5, 'd', 'd', 'o', 'l', 'm', 5, 'd', 'd', 'o', 'l', 'b', 5, 'd', 'd', 'o', 'l', 's', 5, 'd', 'd', 'o', 'l', 't', 5, 'd', 'd', 'o', 'l', 'p', 5, 'd', 'd', 'o', 'l', 'h', 4, 'd', 'd', 'o', 'm', 4, 'd', 'd', 'o', 'b', 5, 'd', 'd', 'o', 'b', 's', 4, 'd', 'd', 'o', 's', 5, 'd', 'd', 'o', 's', 's', 5, 'd', 'd', 'o', 'n', 'g', 4, 'd', 'd', 'o', 'j', 4, 'd', 'd', 'o', 'c', 4, 'd', 'd', 'o', 'k', 4, 'd', 'd', 'o', 't', 4, 'd', 'd', 'o', 'p', 4, 'd', 'd', 'o', 'h', 4, 'd', 'd', 'w', 'a', 5, 'd', 'd', 'w', 'a', 'g', 6, 'd', 'd', 'w', 'a', 'g', 'g', 6, 'd', 'd', 'w', 'a', 'g', 's', 5, 'd', 'd', 'w', 'a', 'n', 6, 'd', 'd', 'w', 'a', 'n', 'j', 6, 'd', 'd', 'w', 'a', 'n', 'h', 5, 'd', 'd', 'w', 'a', 'd', 5, 'd', 'd', 'w', 'a', 'l', 6, 'd', 'd', 'w', 'a', 'l', 'g', 6, 'd', 'd', 'w', 'a', 'l', 'm', 6, 'd', 'd', 'w', 'a', 'l', 'b', 6, 'd', 'd', 'w', 'a', 'l', 's', 6, 'd', 'd', 'w', 'a', 'l', 't', 6, 'd', 'd', 'w', 'a', 'l', 'p', 6, 'd', 'd', 'w', 'a', 'l', 'h', 5, 'd', 'd', 'w', 'a', 'm', 5, 'd', 'd', 'w', 'a', 'b', 6, 'd', 'd', 'w', 'a', 'b', 's', 5, 'd', 'd', 'w', 'a', 's', 6, 'd', 'd', 'w', 'a', 's', 's', 6, 'd', 'd', 'w', 'a', 'n', 'g', 5, 'd', 'd', 'w', 'a', 'j', 5, 'd', 'd', 'w', 'a', 'c', 5, 'd', 'd', 'w', 'a', 'k', 5, 'd', 'd', 'w', 'a', 't', 5, 'd', 'd', 'w', 'a', 'p', 5, 'd', 'd', 'w', 'a', 'h', 5, 'd', 'd', 'w', 'a', 'e', 6, 'd', 'd', 'w', 'a', 'e', 'g', 7, 'd', 'd', 'w', 'a', 'e', 'g', 'g', 7, 'd', 'd', 'w', 'a', 'e', 'g', 's', 6, 'd', 'd', 'w', 'a', 'e', 'n', 7, 'd', 'd', 'w', 'a', 'e', 'n', 'j', 7, 'd', 'd', 'w', 'a', 'e', 'n', 'h', 6, 'd', 'd', 'w', 'a', 'e', 'd', 6, 'd', 'd', 'w', 'a', 'e', 'l', 7, 'd', 'd', 'w', 'a', 'e', 'l', 'g', 7, 'd', 'd', 'w', 'a', 'e', 'l', 'm', 7, 'd', 'd', 'w', 'a', 'e', 'l', 'b', 7, 'd', 'd', 'w', 'a', 'e', 'l', 's', 7, 'd', 'd', 'w', 'a', 'e', 'l', 't', 7, 'd', 'd', 'w', 'a', 'e', 'l', 'p', 7, 'd', 'd', 'w', 'a', 'e', 'l', 'h', 6, 'd', 'd', 'w', 'a', 'e', 'm', 6, 'd', 'd', 'w', 'a', 'e', 'b', 7, 'd', 'd', 'w', 'a', 'e', 'b', 's', 6, 'd', 'd', 'w', 'a', 'e', 's', 7, 'd', 'd', 'w', 'a', 'e', 's', 's', 7, 'd', 'd', 'w', 'a', 'e', 'n', 'g', 6, 'd', 'd', 'w', 'a', 'e', 'j', 6, 'd', 'd', 'w', 'a', 'e', 'c', 6, 'd', 'd', 'w', 'a', 'e', 'k', 6, 'd', 'd', 'w', 'a', 'e', 't', 6, 'd', 'd', 'w', 'a', 'e', 'p', 6, 'd', 'd', 'w', 'a', 'e', 'h', 4, 'd', 'd', 'o', 'e', 5, 'd', 'd', 'o', 'e', 'g', 6, 'd', 'd', 'o', 'e', 'g', 'g', 6, 'd', 'd', 'o', 'e', 'g', 's', 5, 'd', 'd', 'o', 'e', 'n', 6, 'd', 'd', 'o', 'e', 'n', 'j', 6, 'd', 'd', 'o', 'e', 'n', 'h', 5, 'd', 'd', 'o', 'e', 'd', 5, 'd', 'd', 'o', 'e', 'l', 6, 'd', 'd', 'o', 'e', 'l', 'g', 6, 'd', 'd', 'o', 'e', 'l', 'm', 6, 'd', 'd', 'o', 'e', 'l', 'b', 6, 'd', 'd', 'o', 'e', 'l', 's', 6, 'd', 'd', 'o', 'e', 'l', 't', 6, 'd', 'd', 'o', 'e', 'l', 'p', 6, 'd', 'd', 'o', 'e', 'l', 'h', 5, 'd', 'd', 'o', 'e', 'm', 5, 'd', 'd', 'o', 'e', 'b', 6, 'd', 'd', 'o', 'e', 'b', 's', 5, 'd', 'd', 'o', 'e', 's', 6, 'd', 'd', 'o', 'e', 's', 's', 6, 'd', 'd', 'o', 'e', 'n', 'g', 5, 'd', 'd', 'o', 'e', 'j', 5, 'd', 'd', 'o', 'e', 'c', 5, 'd', 'd', 'o', 'e', 'k', 5, 'd', 'd', 'o', 'e', 't', 5, 'd', 'd', 'o', 'e', 'p', 5, 'd', 'd', 'o', 'e', 'h', 4, 'd', 'd', 'y', 'o', 5, 'd', 'd', 'y', 'o', 'g', 6, 'd', 'd', 'y', 'o', 'g', 'g', 6, 'd', 'd', 'y', 'o', 'g', 's', 5, 'd', 'd', 'y', 'o', 'n', 6, 'd', 'd', 'y', 'o', 'n', 'j', 6, 'd', 'd', 'y', 'o', 'n', 'h', 5, 'd', 'd', 'y', 'o', 'd', 5, 'd', 'd', 'y', 'o', 'l', 6, 'd', 'd', 'y', 'o', 'l', 'g', 6, 'd', 'd', 'y', 'o', 'l', 'm', 6, 'd', 'd', 'y', 'o', 'l', 'b', 6, 'd', 'd', 'y', 'o', 'l', 's', 6, 'd', 'd', 'y', 'o', 'l', 't', 6, 'd', 'd', 'y', 'o', 'l', 'p', 6, 'd', 'd', 'y', 'o', 'l', 'h', 5, 'd', 'd', 'y', 'o', 'm', 5, 'd', 'd', 'y', 'o', 'b', 6, 'd', 'd', 'y', 'o', 'b', 's', 5, 'd', 'd', 'y', 'o', 's', 6, 'd', 'd', 'y', 'o', 's', 's', 6, 'd', 'd', 'y', 'o', 'n', 'g', 5, 'd', 'd', 'y', 'o', 'j', 5, 'd', 'd', 'y', 'o', 'c', 5, 'd', 'd', 'y', 'o', 'k', 5, 'd', 'd', 'y', 'o', 't', 5, 'd', 'd', 'y', 'o', 'p', 5, 'd', 'd', 'y', 'o', 'h', 3, 'd', 'd', 'u', 4, 'd', 'd', 'u', 'g', 5, 'd', 'd', 'u', 'g', 'g', 5, 'd', 'd', 'u', 'g', 's', 4, 'd', 'd', 'u', 'n', 5, 'd', 'd', 'u', 'n', 'j', 5, 'd', 'd', 'u', 'n', 'h', 4, 'd', 'd', 'u', 'd', 4, 'd', 'd', 'u', 'l', 5, 'd', 'd', 'u', 'l', 'g', 5, 'd', 'd', 'u', 'l', 'm', 5, 'd', 'd', 'u', 'l', 'b', 5, 'd', 'd', 'u', 'l', 's', 5, 'd', 'd', 'u', 'l', 't', 5, 'd', 'd', 'u', 'l', 'p', 5, 'd', 'd', 'u', 'l', 'h', 4, 'd', 'd', 'u', 'm', 4, 'd', 'd', 'u', 'b', 5, 'd', 'd', 'u', 'b', 's', 4, 'd', 'd', 'u', 's', 5, 'd', 'd', 'u', 's', 's', 5, 'd', 'd', 'u', 'n', 'g', 4, 'd', 'd', 'u', 'j', 4, 'd', 'd', 'u', 'c', 4, 'd', 'd', 'u', 'k', 4, 'd', 'd', 'u', 't', 4, 'd', 'd', 'u', 'p', 4, 'd', 'd', 'u', 'h', 5, 'd', 'd', 'w', 'e', 'o', 6, 'd', 'd', 'w', 'e', 'o', 'g', 7, 'd', 'd', 'w', 'e', 'o', 'g', 'g', 7, 'd', 'd', 'w', 'e', 'o', 'g', 's', 6, 'd', 'd', 'w', 'e', 'o', 'n', 7, 'd', 'd', 'w', 'e', 'o', 'n', 'j', 7, 'd', 'd', 'w', 'e', 'o', 'n', 'h', 6, 'd', 'd', 'w', 'e', 'o', 'd', 6, 'd', 'd', 'w', 'e', 'o', 'l', 7, 'd', 'd', 'w', 'e', 'o', 'l', 'g', 7, 'd', 'd', 'w', 'e', 'o', 'l', 'm', 7, 'd', 'd', 'w', 'e', 'o', 'l', 'b', 7, 'd', 'd', 'w', 'e', 'o', 'l', 's', 7, 'd', 'd', 'w', 'e', 'o', 'l', 't', 7, 'd', 'd', 'w', 'e', 'o', 'l', 'p', 7, 'd', 'd', 'w', 'e', 'o', 'l', 'h', 6, 'd', 'd', 'w', 'e', 'o', 'm', 6, 'd', 'd', 'w', 'e', 'o', 'b', 7, 'd', 'd', 'w', 'e', 'o', 'b', 's', 6, 'd', 'd', 'w', 'e', 'o', 's', 7, 'd', 'd', 'w', 'e', 'o', 's', 's', 7, 'd', 'd', 'w', 'e', 'o', 'n', 'g', 6, 'd', 'd', 'w', 'e', 'o', 'j', 6, 'd', 'd', 'w', 'e', 'o', 'c', 6, 'd', 'd', 'w', 'e', 'o', 'k', 6, 'd', 'd', 'w', 'e', 'o', 't', 6, 'd', 'd', 'w', 'e', 'o', 'p', 6, 'd', 'd', 'w', 'e', 'o', 'h', 4, 'd', 'd', 'w', 'e', 5, 'd', 'd', 'w', 'e', 'g', 6, 'd', 'd', 'w', 'e', 'g', 'g', 6, 'd', 'd', 'w', 'e', 'g', 's', 5, 'd', 'd', 'w', 'e', 'n', 6, 'd', 'd', 'w', 'e', 'n', 'j', 6, 'd', 'd', 'w', 'e', 'n', 'h', 5, 'd', 'd', 'w', 'e', 'd', 5, 'd', 'd', 'w', 'e', 'l', 6, 'd', 'd', 'w', 'e', 'l', 'g', 6, 'd', 'd', 'w', 'e', 'l', 'm', 6, 'd', 'd', 'w', 'e', 'l', 'b', 6, 'd', 'd', 'w', 'e', 'l', 's', 6, 'd', 'd', 'w', 'e', 'l', 't', 6, 'd', 'd', 'w', 'e', 'l', 'p', 6, 'd', 'd', 'w', 'e', 'l', 'h', 5, 'd', 'd', 'w', 'e', 'm', 5, 'd', 'd', 'w', 'e', 'b', 6, 'd', 'd', 'w', 'e', 'b', 's', 5, 'd', 'd', 'w', 'e', 's', 6, 'd', 'd', 'w', 'e', 's', 's', 6, 'd', 'd', 'w', 'e', 'n', 'g', 5, 'd', 'd', 'w', 'e', 'j', 5, 'd', 'd', 'w', 'e', 'c', 5, 'd', 'd', 'w', 'e', 'k', 5, 'd', 'd', 'w', 'e', 't', 5, 'd', 'd', 'w', 'e', 'p', 5, 'd', 'd', 'w', 'e', 'h', 4, 'd', 'd', 'w', 'i', 5, 'd', 'd', 'w', 'i', 'g', 6, 'd', 'd', 'w', 'i', 'g', 'g', 6, 'd', 'd', 'w', 'i', 'g', 's', 5, 'd', 'd', 'w', 'i', 'n', 6, 'd', 'd', 'w', 'i', 'n', 'j', 6, 'd', 'd', 'w', 'i', 'n', 'h', 5, 'd', 'd', 'w', 'i', 'd', 5, 'd', 'd', 'w', 'i', 'l', 6, 'd', 'd', 'w', 'i', 'l', 'g', 6, 'd', 'd', 'w', 'i', 'l', 'm', 6, 'd', 'd', 'w', 'i', 'l', 'b', 6, 'd', 'd', 'w', 'i', 'l', 's', 6, 'd', 'd', 'w', 'i', 'l', 't', 6, 'd', 'd', 'w', 'i', 'l', 'p', 6, 'd', 'd', 'w', 'i', 'l', 'h', 5, 'd', 'd', 'w', 'i', 'm', 5, 'd', 'd', 'w', 'i', 'b', 6, 'd', 'd', 'w', 'i', 'b', 's', 5, 'd', 'd', 'w', 'i', 's', 6, 'd', 'd', 'w', 'i', 's', 's', 6, 'd', 'd', 'w', 'i', 'n', 'g', 5, 'd', 'd', 'w', 'i', 'j', 5, 'd', 'd', 'w', 'i', 'c', 5, 'd', 'd', 'w', 'i', 'k', 5, 'd', 'd', 'w', 'i', 't', 5, 'd', 'd', 'w', 'i', 'p', 5, 'd', 'd', 'w', 'i', 'h', 4, 'd', 'd', 'y', 'u', 5, 'd', 'd', 'y', 'u', 'g', 6, 'd', 'd', 'y', 'u', 'g', 'g', 6, 'd', 'd', 'y', 'u', 'g', 's', 5, 'd', 'd', 'y', 'u', 'n', 6, 'd', 'd', 'y', 'u', 'n', 'j', 6, 'd', 'd', 'y', 'u', 'n', 'h', 5, 'd', 'd', 'y', 'u', 'd', 5, 'd', 'd', 'y', 'u', 'l', 6, 'd', 'd', 'y', 'u', 'l', 'g', 6, 'd', 'd', 'y', 'u', 'l', 'm', 6, 'd', 'd', 'y', 'u', 'l', 'b', 6, 'd', 'd', 'y', 'u', 'l', 's', 6, 'd', 'd', 'y', 'u', 'l', 't', 6, 'd', 'd', 'y', 'u', 'l', 'p', 6, 'd', 'd', 'y', 'u', 'l', 'h', 5, 'd', 'd', 'y', 'u', 'm', 5, 'd', 'd', 'y', 'u', 'b', 6, 'd', 'd', 'y', 'u', 'b', 's', 5, 'd', 'd', 'y', 'u', 's', 6, 'd', 'd', 'y', 'u', 's', 's', 6, 'd', 'd', 'y', 'u', 'n', 'g', 5, 'd', 'd', 'y', 'u', 'j', 5, 'd', 'd', 'y', 'u', 'c', 5, 'd', 'd', 'y', 'u', 'k', 5, 'd', 'd', 'y', 'u', 't', 5, 'd', 'd', 'y', 'u', 'p', 5, 'd', 'd', 'y', 'u', 'h', 4, 'd', 'd', 'e', 'u', 5, 'd', 'd', 'e', 'u', 'g', 6, 'd', 'd', 'e', 'u', 'g', 'g', 6, 'd', 'd', 'e', 'u', 'g', 's', 5, 'd', 'd', 'e', 'u', 'n', 6, 'd', 'd', 'e', 'u', 'n', 'j', 6, 'd', 'd', 'e', 'u', 'n', 'h', 5, 'd', 'd', 'e', 'u', 'd', 5, 'd', 'd', 'e', 'u', 'l', 6, 'd', 'd', 'e', 'u', 'l', 'g', 6, 'd', 'd', 'e', 'u', 'l', 'm', 6, 'd', 'd', 'e', 'u', 'l', 'b', 6, 'd', 'd', 'e', 'u', 'l', 's', 6, 'd', 'd', 'e', 'u', 'l', 't', 6, 'd', 'd', 'e', 'u', 'l', 'p', 6, 'd', 'd', 'e', 'u', 'l', 'h', 5, 'd', 'd', 'e', 'u', 'm', 5, 'd', 'd', 'e', 'u', 'b', 6, 'd', 'd', 'e', 'u', 'b', 's', 5, 'd', 'd', 'e', 'u', 's', 6, 'd', 'd', 'e', 'u', 's', 's', 6, 'd', 'd', 'e', 'u', 'n', 'g', 5, 'd', 'd', 'e', 'u', 'j', 5, 'd', 'd', 'e', 'u', 'c', 5, 'd', 'd', 'e', 'u', 'k', 5, 'd', 'd', 'e', 'u', 't', 5, 'd', 'd', 'e', 'u', 'p', 5, 'd', 'd', 'e', 'u', 'h', 4, 'd', 'd', 'y', 'i', 5, 'd', 'd', 'y', 'i', 'g', 6, 'd', 'd', 'y', 'i', 'g', 'g', 6, 'd', 'd', 'y', 'i', 'g', 's', 5, 'd', 'd', 'y', 'i', 'n', 6, 'd', 'd', 'y', 'i', 'n', 'j', 6, 'd', 'd', 'y', 'i', 'n', 'h', 5, 'd', 'd', 'y', 'i', 'd', 5, 'd', 'd', 'y', 'i', 'l', 6, 'd', 'd', 'y', 'i', 'l', 'g', 6, 'd', 'd', 'y', 'i', 'l', 'm', 6, 'd', 'd', 'y', 'i', 'l', 'b', 6, 'd', 'd', 'y', 'i', 'l', 's', 6, 'd', 'd', 'y', 'i', 'l', 't', 6, 'd', 'd', 'y', 'i', 'l', 'p', 6, 'd', 'd', 'y', 'i', 'l', 'h', 5, 'd', 'd', 'y', 'i', 'm', 5, 'd', 'd', 'y', 'i', 'b', 6, 'd', 'd', 'y', 'i', 'b', 's', 5, 'd', 'd', 'y', 'i', 's', 6, 'd', 'd', 'y', 'i', 's', 's', 6, 'd', 'd', 'y', 'i', 'n', 'g', 5, 'd', 'd', 'y', 'i', 'j', 5, 'd', 'd', 'y', 'i', 'c', 5, 'd', 'd', 'y', 'i', 'k', 5, 'd', 'd', 'y', 'i', 't', 5, 'd', 'd', 'y', 'i', 'p', 5, 'd', 'd', 'y', 'i', 'h', 3, 'd', 'd', 'i', 4, 'd', 'd', 'i', 'g', 5, 'd', 'd', 'i', 'g', 'g', 5, 'd', 'd', 'i', 'g', 's', 4, 'd', 'd', 'i', 'n', 5, 'd', 'd', 'i', 'n', 'j', 5, 'd', 'd', 'i', 'n', 'h', 4, 'd', 'd', 'i', 'd', 4, 'd', 'd', 'i', 'l', 5, 'd', 'd', 'i', 'l', 'g', 5, 'd', 'd', 'i', 'l', 'm', 5, 'd', 'd', 'i', 'l', 'b', 5, 'd', 'd', 'i', 'l', 's', 5, 'd', 'd', 'i', 'l', 't', 5, 'd', 'd', 'i', 'l', 'p', 5, 'd', 'd', 'i', 'l', 'h', 4, 'd', 'd', 'i', 'm', 4, 'd', 'd', 'i', 'b', 5, 'd', 'd', 'i', 'b', 's', 4, 'd', 'd', 'i', 's', 5, 'd', 'd', 'i', 's', 's', 5, 'd', 'd', 'i', 'n', 'g', 4, 'd', 'd', 'i', 'j', 4, 'd', 'd', 'i', 'c', 4, 'd', 'd', 'i', 'k', 4, 'd', 'd', 'i', 't', 4, 'd', 'd', 'i', 'p', 4, 'd', 'd', 'i', 'h', 2, 'r', 'a', 3, 'r', 'a', 'g', 4, 'r', 'a', 'g', 'g', 4, 'r', 'a', 'g', 's', 3, 'r', 'a', 'n', 4, 'r', 'a', 'n', 'j', 4, 'r', 'a', 'n', 'h', 3, 'r', 'a', 'd', 3, 'r', 'a', 'l', 4, 'r', 'a', 'l', 'g', 4, 'r', 'a', 'l', 'm', 4, 'r', 'a', 'l', 'b', 4, 'r', 'a', 'l', 's', 4, 'r', 'a', 'l', 't', 4, 'r', 'a', 'l', 'p', 4, 'r', 'a', 'l', 'h', 3, 'r', 'a', 'm', 3, 'r', 'a', 'b', 4, 'r', 'a', 'b', 's', 3, 'r', 'a', 's', 4, 'r', 'a', 's', 's', 4, 'r', 'a', 'n', 'g', 3, 'r', 'a', 'j', 3, 'r', 'a', 'c', 3, 'r', 'a', 'k', 3, 'r', 'a', 't', 3, 'r', 'a', 'p', 3, 'r', 'a', 'h', 3, 'r', 'a', 'e', 4, 'r', 'a', 'e', 'g', 5, 'r', 'a', 'e', 'g', 'g', 5, 'r', 'a', 'e', 'g', 's', 4, 'r', 'a', 'e', 'n', 5, 'r', 'a', 'e', 'n', 'j', 5, 'r', 'a', 'e', 'n', 'h', 4, 'r', 'a', 'e', 'd', 4, 'r', 'a', 'e', 'l', 5, 'r', 'a', 'e', 'l', 'g', 5, 'r', 'a', 'e', 'l', 'm', 5, 'r', 'a', 'e', 'l', 'b', 5, 'r', 'a', 'e', 'l', 's', 5, 'r', 'a', 'e', 'l', 't', 5, 'r', 'a', 'e', 'l', 'p', 5, 'r', 'a', 'e', 'l', 'h', 4, 'r', 'a', 'e', 'm', 4, 'r', 'a', 'e', 'b', 5, 'r', 'a', 'e', 'b', 's', 4, 'r', 'a', 'e', 's', 5, 'r', 'a', 'e', 's', 's', 5, 'r', 'a', 'e', 'n', 'g', 4, 'r', 'a', 'e', 'j', 4, 'r', 'a', 'e', 'c', 4, 'r', 'a', 'e', 'k', 4, 'r', 'a', 'e', 't', 4, 'r', 'a', 'e', 'p', 4, 'r', 'a', 'e', 'h', 3, 'r', 'y', 'a', 4, 'r', 'y', 'a', 'g', 5, 'r', 'y', 'a', 'g', 'g', 5, 'r', 'y', 'a', 'g', 's', 4, 'r', 'y', 'a', 'n', 5, 'r', 'y', 'a', 'n', 'j', 5, 'r', 'y', 'a', 'n', 'h', 4, 'r', 'y', 'a', 'd', 4, 'r', 'y', 'a', 'l', 5, 'r', 'y', 'a', 'l', 'g', 5, 'r', 'y', 'a', 'l', 'm', 5, 'r', 'y', 'a', 'l', 'b', 5, 'r', 'y', 'a', 'l', 's', 5, 'r', 'y', 'a', 'l', 't', 5, 'r', 'y', 'a', 'l', 'p', 5, 'r', 'y', 'a', 'l', 'h', 4, 'r', 'y', 'a', 'm', 4, 'r', 'y', 'a', 'b', 5, 'r', 'y', 'a', 'b', 's', 4, 'r', 'y', 'a', 's', 5, 'r', 'y', 'a', 's', 's', 5, 'r', 'y', 'a', 'n', 'g', 4, 'r', 'y', 'a', 'j', 4, 'r', 'y', 'a', 'c', 4, 'r', 'y', 'a', 'k', 4, 'r', 'y', 'a', 't', 4, 'r', 'y', 'a', 'p', 4, 'r', 'y', 'a', 'h', 4, 'r', 'y', 'a', 'e', 5, 'r', 'y', 'a', 'e', 'g', 6, 'r', 'y', 'a', 'e', 'g', 'g', 6, 'r', 'y', 'a', 'e', 'g', 's', 5, 'r', 'y', 'a', 'e', 'n', 6, 'r', 'y', 'a', 'e', 'n', 'j', 6, 'r', 'y', 'a', 'e', 'n', 'h', 5, 'r', 'y', 'a', 'e', 'd', 5, 'r', 'y', 'a', 'e', 'l', 6, 'r', 'y', 'a', 'e', 'l', 'g', 6, 'r', 'y', 'a', 'e', 'l', 'm', 6, 'r', 'y', 'a', 'e', 'l', 'b', 6, 'r', 'y', 'a', 'e', 'l', 's', 6, 'r', 'y', 'a', 'e', 'l', 't', 6, 'r', 'y', 'a', 'e', 'l', 'p', 6, 'r', 'y', 'a', 'e', 'l', 'h', 5, 'r', 'y', 'a', 'e', 'm', 5, 'r', 'y', 'a', 'e', 'b', 6, 'r', 'y', 'a', 'e', 'b', 's', 5, 'r', 'y', 'a', 'e', 's', 6, 'r', 'y', 'a', 'e', 's', 's', 6, 'r', 'y', 'a', 'e', 'n', 'g', 5, 'r', 'y', 'a', 'e', 'j', 5, 'r', 'y', 'a', 'e', 'c', 5, 'r', 'y', 'a', 'e', 'k', 5, 'r', 'y', 'a', 'e', 't', 5, 'r', 'y', 'a', 'e', 'p', 5, 'r', 'y', 'a', 'e', 'h', 3, 'r', 'e', 'o', 4, 'r', 'e', 'o', 'g', 5, 'r', 'e', 'o', 'g', 'g', 5, 'r', 'e', 'o', 'g', 's', 4, 'r', 'e', 'o', 'n', 5, 'r', 'e', 'o', 'n', 'j', 5, 'r', 'e', 'o', 'n', 'h', 4, 'r', 'e', 'o', 'd', 4, 'r', 'e', 'o', 'l', 5, 'r', 'e', 'o', 'l', 'g', 5, 'r', 'e', 'o', 'l', 'm', 5, 'r', 'e', 'o', 'l', 'b', 5, 'r', 'e', 'o', 'l', 's', 5, 'r', 'e', 'o', 'l', 't', 5, 'r', 'e', 'o', 'l', 'p', 5, 'r', 'e', 'o', 'l', 'h', 4, 'r', 'e', 'o', 'm', 4, 'r', 'e', 'o', 'b', 5, 'r', 'e', 'o', 'b', 's', 4, 'r', 'e', 'o', 's', 5, 'r', 'e', 'o', 's', 's', 5, 'r', 'e', 'o', 'n', 'g', 4, 'r', 'e', 'o', 'j', 4, 'r', 'e', 'o', 'c', 4, 'r', 'e', 'o', 'k', 4, 'r', 'e', 'o', 't', 4, 'r', 'e', 'o', 'p', 4, 'r', 'e', 'o', 'h', 2, 'r', 'e', 3, 'r', 'e', 'g', 4, 'r', 'e', 'g', 'g', 4, 'r', 'e', 'g', 's', 3, 'r', 'e', 'n', 4, 'r', 'e', 'n', 'j', 4, 'r', 'e', 'n', 'h', 3, 'r', 'e', 'd', 3, 'r', 'e', 'l', 4, 'r', 'e', 'l', 'g', 4, 'r', 'e', 'l', 'm', 4, 'r', 'e', 'l', 'b', 4, 'r', 'e', 'l', 's', 4, 'r', 'e', 'l', 't', 4, 'r', 'e', 'l', 'p', 4, 'r', 'e', 'l', 'h', 3, 'r', 'e', 'm', 3, 'r', 'e', 'b', 4, 'r', 'e', 'b', 's', 3, 'r', 'e', 's', 4, 'r', 'e', 's', 's', 4, 'r', 'e', 'n', 'g', 3, 'r', 'e', 'j', 3, 'r', 'e', 'c', 3, 'r', 'e', 'k', 3, 'r', 'e', 't', 3, 'r', 'e', 'p', 3, 'r', 'e', 'h', 4, 'r', 'y', 'e', 'o', 5, 'r', 'y', 'e', 'o', 'g', 6, 'r', 'y', 'e', 'o', 'g', 'g', 6, 'r', 'y', 'e', 'o', 'g', 's', 5, 'r', 'y', 'e', 'o', 'n', 6, 'r', 'y', 'e', 'o', 'n', 'j', 6, 'r', 'y', 'e', 'o', 'n', 'h', 5, 'r', 'y', 'e', 'o', 'd', 5, 'r', 'y', 'e', 'o', 'l', 6, 'r', 'y', 'e', 'o', 'l', 'g', 6, 'r', 'y', 'e', 'o', 'l', 'm', 6, 'r', 'y', 'e', 'o', 'l', 'b', 6, 'r', 'y', 'e', 'o', 'l', 's', 6, 'r', 'y', 'e', 'o', 'l', 't', 6, 'r', 'y', 'e', 'o', 'l', 'p', 6, 'r', 'y', 'e', 'o', 'l', 'h', 5, 'r', 'y', 'e', 'o', 'm', 5, 'r', 'y', 'e', 'o', 'b', 6, 'r', 'y', 'e', 'o', 'b', 's', 5, 'r', 'y', 'e', 'o', 's', 6, 'r', 'y', 'e', 'o', 's', 's', 6, 'r', 'y', 'e', 'o', 'n', 'g', 5, 'r', 'y', 'e', 'o', 'j', 5, 'r', 'y', 'e', 'o', 'c', 5, 'r', 'y', 'e', 'o', 'k', 5, 'r', 'y', 'e', 'o', 't', 5, 'r', 'y', 'e', 'o', 'p', 5, 'r', 'y', 'e', 'o', 'h', 3, 'r', 'y', 'e', 4, 'r', 'y', 'e', 'g', 5, 'r', 'y', 'e', 'g', 'g', 5, 'r', 'y', 'e', 'g', 's', 4, 'r', 'y', 'e', 'n', 5, 'r', 'y', 'e', 'n', 'j', 5, 'r', 'y', 'e', 'n', 'h', 4, 'r', 'y', 'e', 'd', 4, 'r', 'y', 'e', 'l', 5, 'r', 'y', 'e', 'l', 'g', 5, 'r', 'y', 'e', 'l', 'm', 5, 'r', 'y', 'e', 'l', 'b', 5, 'r', 'y', 'e', 'l', 's', 5, 'r', 'y', 'e', 'l', 't', 5, 'r', 'y', 'e', 'l', 'p', 5, 'r', 'y', 'e', 'l', 'h', 4, 'r', 'y', 'e', 'm', 4, 'r', 'y', 'e', 'b', 5, 'r', 'y', 'e', 'b', 's', 4, 'r', 'y', 'e', 's', 5, 'r', 'y', 'e', 's', 's', 5, 'r', 'y', 'e', 'n', 'g', 4, 'r', 'y', 'e', 'j', 4, 'r', 'y', 'e', 'c', 4, 'r', 'y', 'e', 'k', 4, 'r', 'y', 'e', 't', 4, 'r', 'y', 'e', 'p', 4, 'r', 'y', 'e', 'h', 2, 'r', 'o', 3, 'r', 'o', 'g', 4, 'r', 'o', 'g', 'g', 4, 'r', 'o', 'g', 's', 3, 'r', 'o', 'n', 4, 'r', 'o', 'n', 'j', 4, 'r', 'o', 'n', 'h', 3, 'r', 'o', 'd', 3, 'r', 'o', 'l', 4, 'r', 'o', 'l', 'g', 4, 'r', 'o', 'l', 'm', 4, 'r', 'o', 'l', 'b', 4, 'r', 'o', 'l', 's', 4, 'r', 'o', 'l', 't', 4, 'r', 'o', 'l', 'p', 4, 'r', 'o', 'l', 'h', 3, 'r', 'o', 'm', 3, 'r', 'o', 'b', 4, 'r', 'o', 'b', 's', 3, 'r', 'o', 's', 4, 'r', 'o', 's', 's', 4, 'r', 'o', 'n', 'g', 3, 'r', 'o', 'j', 3, 'r', 'o', 'c', 3, 'r', 'o', 'k', 3, 'r', 'o', 't', 3, 'r', 'o', 'p', 3, 'r', 'o', 'h', 3, 'r', 'w', 'a', 4, 'r', 'w', 'a', 'g', 5, 'r', 'w', 'a', 'g', 'g', 5, 'r', 'w', 'a', 'g', 's', 4, 'r', 'w', 'a', 'n', 5, 'r', 'w', 'a', 'n', 'j', 5, 'r', 'w', 'a', 'n', 'h', 4, 'r', 'w', 'a', 'd', 4, 'r', 'w', 'a', 'l', 5, 'r', 'w', 'a', 'l', 'g', 5, 'r', 'w', 'a', 'l', 'm', 5, 'r', 'w', 'a', 'l', 'b', 5, 'r', 'w', 'a', 'l', 's', 5, 'r', 'w', 'a', 'l', 't', 5, 'r', 'w', 'a', 'l', 'p', 5, 'r', 'w', 'a', 'l', 'h', 4, 'r', 'w', 'a', 'm', 4, 'r', 'w', 'a', 'b', 5, 'r', 'w', 'a', 'b', 's', 4, 'r', 'w', 'a', 's', 5, 'r', 'w', 'a', 's', 's', 5, 'r', 'w', 'a', 'n', 'g', 4, 'r', 'w', 'a', 'j', 4, 'r', 'w', 'a', 'c', 4, 'r', 'w', 'a', 'k', 4, 'r', 'w', 'a', 't', 4, 'r', 'w', 'a', 'p', 4, 'r', 'w', 'a', 'h', 4, 'r', 'w', 'a', 'e', 5, 'r', 'w', 'a', 'e', 'g', 6, 'r', 'w', 'a', 'e', 'g', 'g', 6, 'r', 'w', 'a', 'e', 'g', 's', 5, 'r', 'w', 'a', 'e', 'n', 6, 'r', 'w', 'a', 'e', 'n', 'j', 6, 'r', 'w', 'a', 'e', 'n', 'h', 5, 'r', 'w', 'a', 'e', 'd', 5, 'r', 'w', 'a', 'e', 'l', 6, 'r', 'w', 'a', 'e', 'l', 'g', 6, 'r', 'w', 'a', 'e', 'l', 'm', 6, 'r', 'w', 'a', 'e', 'l', 'b', 6, 'r', 'w', 'a', 'e', 'l', 's', 6, 'r', 'w', 'a', 'e', 'l', 't', 6, 'r', 'w', 'a', 'e', 'l', 'p', 6, 'r', 'w', 'a', 'e', 'l', 'h', 5, 'r', 'w', 'a', 'e', 'm', 5, 'r', 'w', 'a', 'e', 'b', 6, 'r', 'w', 'a', 'e', 'b', 's', 5, 'r', 'w', 'a', 'e', 's', 6, 'r', 'w', 'a', 'e', 's', 's', 6, 'r', 'w', 'a', 'e', 'n', 'g', 5, 'r', 'w', 'a', 'e', 'j', 5, 'r', 'w', 'a', 'e', 'c', 5, 'r', 'w', 'a', 'e', 'k', 5, 'r', 'w', 'a', 'e', 't', 5, 'r', 'w', 'a', 'e', 'p', 5, 'r', 'w', 'a', 'e', 'h', 3, 'r', 'o', 'e', 4, 'r', 'o', 'e', 'g', 5, 'r', 'o', 'e', 'g', 'g', 5, 'r', 'o', 'e', 'g', 's', 4, 'r', 'o', 'e', 'n', 5, 'r', 'o', 'e', 'n', 'j', 5, 'r', 'o', 'e', 'n', 'h', 4, 'r', 'o', 'e', 'd', 4, 'r', 'o', 'e', 'l', 5, 'r', 'o', 'e', 'l', 'g', 5, 'r', 'o', 'e', 'l', 'm', 5, 'r', 'o', 'e', 'l', 'b', 5, 'r', 'o', 'e', 'l', 's', 5, 'r', 'o', 'e', 'l', 't', 5, 'r', 'o', 'e', 'l', 'p', 5, 'r', 'o', 'e', 'l', 'h', 4, 'r', 'o', 'e', 'm', 4, 'r', 'o', 'e', 'b', 5, 'r', 'o', 'e', 'b', 's', 4, 'r', 'o', 'e', 's', 5, 'r', 'o', 'e', 's', 's', 5, 'r', 'o', 'e', 'n', 'g', 4, 'r', 'o', 'e', 'j', 4, 'r', 'o', 'e', 'c', 4, 'r', 'o', 'e', 'k', 4, 'r', 'o', 'e', 't', 4, 'r', 'o', 'e', 'p', 4, 'r', 'o', 'e', 'h', 3, 'r', 'y', 'o', 4, 'r', 'y', 'o', 'g', 5, 'r', 'y', 'o', 'g', 'g', 5, 'r', 'y', 'o', 'g', 's', 4, 'r', 'y', 'o', 'n', 5, 'r', 'y', 'o', 'n', 'j', 5, 'r', 'y', 'o', 'n', 'h', 4, 'r', 'y', 'o', 'd', 4, 'r', 'y', 'o', 'l', 5, 'r', 'y', 'o', 'l', 'g', 5, 'r', 'y', 'o', 'l', 'm', 5, 'r', 'y', 'o', 'l', 'b', 5, 'r', 'y', 'o', 'l', 's', 5, 'r', 'y', 'o', 'l', 't', 5, 'r', 'y', 'o', 'l', 'p', 5, 'r', 'y', 'o', 'l', 'h', 4, 'r', 'y', 'o', 'm', 4, 'r', 'y', 'o', 'b', 5, 'r', 'y', 'o', 'b', 's', 4, 'r', 'y', 'o', 's', 5, 'r', 'y', 'o', 's', 's', 5, 'r', 'y', 'o', 'n', 'g', 4, 'r', 'y', 'o', 'j', 4, 'r', 'y', 'o', 'c', 4, 'r', 'y', 'o', 'k', 4, 'r', 'y', 'o', 't', 4, 'r', 'y', 'o', 'p', 4, 'r', 'y', 'o', 'h', 2, 'r', 'u', 3, 'r', 'u', 'g', 4, 'r', 'u', 'g', 'g', 4, 'r', 'u', 'g', 's', 3, 'r', 'u', 'n', 4, 'r', 'u', 'n', 'j', 4, 'r', 'u', 'n', 'h', 3, 'r', 'u', 'd', 3, 'r', 'u', 'l', 4, 'r', 'u', 'l', 'g', 4, 'r', 'u', 'l', 'm', 4, 'r', 'u', 'l', 'b', 4, 'r', 'u', 'l', 's', 4, 'r', 'u', 'l', 't', 4, 'r', 'u', 'l', 'p', 4, 'r', 'u', 'l', 'h', 3, 'r', 'u', 'm', 3, 'r', 'u', 'b', 4, 'r', 'u', 'b', 's', 3, 'r', 'u', 's', 4, 'r', 'u', 's', 's', 4, 'r', 'u', 'n', 'g', 3, 'r', 'u', 'j', 3, 'r', 'u', 'c', 3, 'r', 'u', 'k', 3, 'r', 'u', 't', 3, 'r', 'u', 'p', 3, 'r', 'u', 'h', 4, 'r', 'w', 'e', 'o', 5, 'r', 'w', 'e', 'o', 'g', 6, 'r', 'w', 'e', 'o', 'g', 'g', 6, 'r', 'w', 'e', 'o', 'g', 's', 5, 'r', 'w', 'e', 'o', 'n', 6, 'r', 'w', 'e', 'o', 'n', 'j', 6, 'r', 'w', 'e', 'o', 'n', 'h', 5, 'r', 'w', 'e', 'o', 'd', 5, 'r', 'w', 'e', 'o', 'l', 6, 'r', 'w', 'e', 'o', 'l', 'g', 6, 'r', 'w', 'e', 'o', 'l', 'm', 6, 'r', 'w', 'e', 'o', 'l', 'b', 6, 'r', 'w', 'e', 'o', 'l', 's', 6, 'r', 'w', 'e', 'o', 'l', 't', 6, 'r', 'w', 'e', 'o', 'l', 'p', 6, 'r', 'w', 'e', 'o', 'l', 'h', 5, 'r', 'w', 'e', 'o', 'm', 5, 'r', 'w', 'e', 'o', 'b', 6, 'r', 'w', 'e', 'o', 'b', 's', 5, 'r', 'w', 'e', 'o', 's', 6, 'r', 'w', 'e', 'o', 's', 's', 6, 'r', 'w', 'e', 'o', 'n', 'g', 5, 'r', 'w', 'e', 'o', 'j', 5, 'r', 'w', 'e', 'o', 'c', 5, 'r', 'w', 'e', 'o', 'k', 5, 'r', 'w', 'e', 'o', 't', 5, 'r', 'w', 'e', 'o', 'p', 5, 'r', 'w', 'e', 'o', 'h', 3, 'r', 'w', 'e', 4, 'r', 'w', 'e', 'g', 5, 'r', 'w', 'e', 'g', 'g', 5, 'r', 'w', 'e', 'g', 's', 4, 'r', 'w', 'e', 'n', 5, 'r', 'w', 'e', 'n', 'j', 5, 'r', 'w', 'e', 'n', 'h', 4, 'r', 'w', 'e', 'd', 4, 'r', 'w', 'e', 'l', 5, 'r', 'w', 'e', 'l', 'g', 5, 'r', 'w', 'e', 'l', 'm', 5, 'r', 'w', 'e', 'l', 'b', 5, 'r', 'w', 'e', 'l', 's', 5, 'r', 'w', 'e', 'l', 't', 5, 'r', 'w', 'e', 'l', 'p', 5, 'r', 'w', 'e', 'l', 'h', 4, 'r', 'w', 'e', 'm', 4, 'r', 'w', 'e', 'b', 5, 'r', 'w', 'e', 'b', 's', 4, 'r', 'w', 'e', 's', 5, 'r', 'w', 'e', 's', 's', 5, 'r', 'w', 'e', 'n', 'g', 4, 'r', 'w', 'e', 'j', 4, 'r', 'w', 'e', 'c', 4, 'r', 'w', 'e', 'k', 4, 'r', 'w', 'e', 't', 4, 'r', 'w', 'e', 'p', 4, 'r', 'w', 'e', 'h', 3, 'r', 'w', 'i', 4, 'r', 'w', 'i', 'g', 5, 'r', 'w', 'i', 'g', 'g', 5, 'r', 'w', 'i', 'g', 's', 4, 'r', 'w', 'i', 'n', 5, 'r', 'w', 'i', 'n', 'j', 5, 'r', 'w', 'i', 'n', 'h', 4, 'r', 'w', 'i', 'd', 4, 'r', 'w', 'i', 'l', 5, 'r', 'w', 'i', 'l', 'g', 5, 'r', 'w', 'i', 'l', 'm', 5, 'r', 'w', 'i', 'l', 'b', 5, 'r', 'w', 'i', 'l', 's', 5, 'r', 'w', 'i', 'l', 't', 5, 'r', 'w', 'i', 'l', 'p', 5, 'r', 'w', 'i', 'l', 'h', 4, 'r', 'w', 'i', 'm', 4, 'r', 'w', 'i', 'b', 5, 'r', 'w', 'i', 'b', 's', 4, 'r', 'w', 'i', 's', 5, 'r', 'w', 'i', 's', 's', 5, 'r', 'w', 'i', 'n', 'g', 4, 'r', 'w', 'i', 'j', 4, 'r', 'w', 'i', 'c', 4, 'r', 'w', 'i', 'k', 4, 'r', 'w', 'i', 't', 4, 'r', 'w', 'i', 'p', 4, 'r', 'w', 'i', 'h', 3, 'r', 'y', 'u', 4, 'r', 'y', 'u', 'g', 5, 'r', 'y', 'u', 'g', 'g', 5, 'r', 'y', 'u', 'g', 's', 4, 'r', 'y', 'u', 'n', 5, 'r', 'y', 'u', 'n', 'j', 5, 'r', 'y', 'u', 'n', 'h', 4, 'r', 'y', 'u', 'd', 4, 'r', 'y', 'u', 'l', 5, 'r', 'y', 'u', 'l', 'g', 5, 'r', 'y', 'u', 'l', 'm', 5, 'r', 'y', 'u', 'l', 'b', 5, 'r', 'y', 'u', 'l', 's', 5, 'r', 'y', 'u', 'l', 't', 5, 'r', 'y', 'u', 'l', 'p', 5, 'r', 'y', 'u', 'l', 'h', 4, 'r', 'y', 'u', 'm', 4, 'r', 'y', 'u', 'b', 5, 'r', 'y', 'u', 'b', 's', 4, 'r', 'y', 'u', 's', 5, 'r', 'y', 'u', 's', 's', 5, 'r', 'y', 'u', 'n', 'g', 4, 'r', 'y', 'u', 'j', 4, 'r', 'y', 'u', 'c', 4, 'r', 'y', 'u', 'k', 4, 'r', 'y', 'u', 't', 4, 'r', 'y', 'u', 'p', 4, 'r', 'y', 'u', 'h', 3, 'r', 'e', 'u', 4, 'r', 'e', 'u', 'g', 5, 'r', 'e', 'u', 'g', 'g', 5, 'r', 'e', 'u', 'g', 's', 4, 'r', 'e', 'u', 'n', 5, 'r', 'e', 'u', 'n', 'j', 5, 'r', 'e', 'u', 'n', 'h', 4, 'r', 'e', 'u', 'd', 4, 'r', 'e', 'u', 'l', 5, 'r', 'e', 'u', 'l', 'g', 5, 'r', 'e', 'u', 'l', 'm', 5, 'r', 'e', 'u', 'l', 'b', 5, 'r', 'e', 'u', 'l', 's', 5, 'r', 'e', 'u', 'l', 't', 5, 'r', 'e', 'u', 'l', 'p', 5, 'r', 'e', 'u', 'l', 'h', 4, 'r', 'e', 'u', 'm', 4, 'r', 'e', 'u', 'b', 5, 'r', 'e', 'u', 'b', 's', 4, 'r', 'e', 'u', 's', 5, 'r', 'e', 'u', 's', 's', 5, 'r', 'e', 'u', 'n', 'g', 4, 'r', 'e', 'u', 'j', 4, 'r', 'e', 'u', 'c', 4, 'r', 'e', 'u', 'k', 4, 'r', 'e', 'u', 't', 4, 'r', 'e', 'u', 'p', 4, 'r', 'e', 'u', 'h', 3, 'r', 'y', 'i', 4, 'r', 'y', 'i', 'g', 5, 'r', 'y', 'i', 'g', 'g', 5, 'r', 'y', 'i', 'g', 's', 4, 'r', 'y', 'i', 'n', 5, 'r', 'y', 'i', 'n', 'j', 5, 'r', 'y', 'i', 'n', 'h', 4, 'r', 'y', 'i', 'd', 4, 'r', 'y', 'i', 'l', 5, 'r', 'y', 'i', 'l', 'g', 5, 'r', 'y', 'i', 'l', 'm', 5, 'r', 'y', 'i', 'l', 'b', 5, 'r', 'y', 'i', 'l', 's', 5, 'r', 'y', 'i', 'l', 't', 5, 'r', 'y', 'i', 'l', 'p', 5, 'r', 'y', 'i', 'l', 'h', 4, 'r', 'y', 'i', 'm', 4, 'r', 'y', 'i', 'b', 5, 'r', 'y', 'i', 'b', 's', 4, 'r', 'y', 'i', 's', 5, 'r', 'y', 'i', 's', 's', 5, 'r', 'y', 'i', 'n', 'g', 4, 'r', 'y', 'i', 'j', 4, 'r', 'y', 'i', 'c', 4, 'r', 'y', 'i', 'k', 4, 'r', 'y', 'i', 't', 4, 'r', 'y', 'i', 'p', 4, 'r', 'y', 'i', 'h', 2, 'r', 'i', 3, 'r', 'i', 'g', 4, 'r', 'i', 'g', 'g', 4, 'r', 'i', 'g', 's', 3, 'r', 'i', 'n', 4, 'r', 'i', 'n', 'j', 4, 'r', 'i', 'n', 'h', 3, 'r', 'i', 'd', 3, 'r', 'i', 'l', 4, 'r', 'i', 'l', 'g', 4, 'r', 'i', 'l', 'm', 4, 'r', 'i', 'l', 'b', 4, 'r', 'i', 'l', 's', 4, 'r', 'i', 'l', 't', 4, 'r', 'i', 'l', 'p', 4, 'r', 'i', 'l', 'h', 3, 'r', 'i', 'm', 3, 'r', 'i', 'b', 4, 'r', 'i', 'b', 's', 3, 'r', 'i', 's', 4, 'r', 'i', 's', 's', 4, 'r', 'i', 'n', 'g', 3, 'r', 'i', 'j', 3, 'r', 'i', 'c', 3, 'r', 'i', 'k', 3, 'r', 'i', 't', 3, 'r', 'i', 'p', 3, 'r', 'i', 'h', 2, 'm', 'a', 3, 'm', 'a', 'g', 4, 'm', 'a', 'g', 'g', 4, 'm', 'a', 'g', 's', 3, 'm', 'a', 'n', 4, 'm', 'a', 'n', 'j', 4, 'm', 'a', 'n', 'h', 3, 'm', 'a', 'd', 3, 'm', 'a', 'l', 4, 'm', 'a', 'l', 'g', 4, 'm', 'a', 'l', 'm', 4, 'm', 'a', 'l', 'b', 4, 'm', 'a', 'l', 's', 4, 'm', 'a', 'l', 't', 4, 'm', 'a', 'l', 'p', 4, 'm', 'a', 'l', 'h', 3, 'm', 'a', 'm', 3, 'm', 'a', 'b', 4, 'm', 'a', 'b', 's', 3, 'm', 'a', 's', 4, 'm', 'a', 's', 's', 4, 'm', 'a', 'n', 'g', 3, 'm', 'a', 'j', 3, 'm', 'a', 'c', 3, 'm', 'a', 'k', 3, 'm', 'a', 't', 3, 'm', 'a', 'p', 3, 'm', 'a', 'h', 3, 'm', 'a', 'e', 4, 'm', 'a', 'e', 'g', 5, 'm', 'a', 'e', 'g', 'g', 5, 'm', 'a', 'e', 'g', 's', 4, 'm', 'a', 'e', 'n', 5, 'm', 'a', 'e', 'n', 'j', 5, 'm', 'a', 'e', 'n', 'h', 4, 'm', 'a', 'e', 'd', 4, 'm', 'a', 'e', 'l', 5, 'm', 'a', 'e', 'l', 'g', 5, 'm', 'a', 'e', 'l', 'm', 5, 'm', 'a', 'e', 'l', 'b', 5, 'm', 'a', 'e', 'l', 's', 5, 'm', 'a', 'e', 'l', 't', 5, 'm', 'a', 'e', 'l', 'p', 5, 'm', 'a', 'e', 'l', 'h', 4, 'm', 'a', 'e', 'm', 4, 'm', 'a', 'e', 'b', 5, 'm', 'a', 'e', 'b', 's', 4, 'm', 'a', 'e', 's', 5, 'm', 'a', 'e', 's', 's', 5, 'm', 'a', 'e', 'n', 'g', 4, 'm', 'a', 'e', 'j', 4, 'm', 'a', 'e', 'c', 4, 'm', 'a', 'e', 'k', 4, 'm', 'a', 'e', 't', 4, 'm', 'a', 'e', 'p', 4, 'm', 'a', 'e', 'h', 3, 'm', 'y', 'a', 4, 'm', 'y', 'a', 'g', 5, 'm', 'y', 'a', 'g', 'g', 5, 'm', 'y', 'a', 'g', 's', 4, 'm', 'y', 'a', 'n', 5, 'm', 'y', 'a', 'n', 'j', 5, 'm', 'y', 'a', 'n', 'h', 4, 'm', 'y', 'a', 'd', 4, 'm', 'y', 'a', 'l', 5, 'm', 'y', 'a', 'l', 'g', 5, 'm', 'y', 'a', 'l', 'm', 5, 'm', 'y', 'a', 'l', 'b', 5, 'm', 'y', 'a', 'l', 's', 5, 'm', 'y', 'a', 'l', 't', 5, 'm', 'y', 'a', 'l', 'p', 5, 'm', 'y', 'a', 'l', 'h', 4, 'm', 'y', 'a', 'm', 4, 'm', 'y', 'a', 'b', 5, 'm', 'y', 'a', 'b', 's', 4, 'm', 'y', 'a', 's', 5, 'm', 'y', 'a', 's', 's', 5, 'm', 'y', 'a', 'n', 'g', 4, 'm', 'y', 'a', 'j', 4, 'm', 'y', 'a', 'c', 4, 'm', 'y', 'a', 'k', 4, 'm', 'y', 'a', 't', 4, 'm', 'y', 'a', 'p', 4, 'm', 'y', 'a', 'h', 4, 'm', 'y', 'a', 'e', 5, 'm', 'y', 'a', 'e', 'g', 6, 'm', 'y', 'a', 'e', 'g', 'g', 6, 'm', 'y', 'a', 'e', 'g', 's', 5, 'm', 'y', 'a', 'e', 'n', 6, 'm', 'y', 'a', 'e', 'n', 'j', 6, 'm', 'y', 'a', 'e', 'n', 'h', 5, 'm', 'y', 'a', 'e', 'd', 5, 'm', 'y', 'a', 'e', 'l', 6, 'm', 'y', 'a', 'e', 'l', 'g', 6, 'm', 'y', 'a', 'e', 'l', 'm', 6, 'm', 'y', 'a', 'e', 'l', 'b', 6, 'm', 'y', 'a', 'e', 'l', 's', 6, 'm', 'y', 'a', 'e', 'l', 't', 6, 'm', 'y', 'a', 'e', 'l', 'p', 6, 'm', 'y', 'a', 'e', 'l', 'h', 5, 'm', 'y', 'a', 'e', 'm', 5, 'm', 'y', 'a', 'e', 'b', 6, 'm', 'y', 'a', 'e', 'b', 's', 5, 'm', 'y', 'a', 'e', 's', 6, 'm', 'y', 'a', 'e', 's', 's', 6, 'm', 'y', 'a', 'e', 'n', 'g', 5, 'm', 'y', 'a', 'e', 'j', 5, 'm', 'y', 'a', 'e', 'c', 5, 'm', 'y', 'a', 'e', 'k', 5, 'm', 'y', 'a', 'e', 't', 5, 'm', 'y', 'a', 'e', 'p', 5, 'm', 'y', 'a', 'e', 'h', 3, 'm', 'e', 'o', 4, 'm', 'e', 'o', 'g', 5, 'm', 'e', 'o', 'g', 'g', 5, 'm', 'e', 'o', 'g', 's', 4, 'm', 'e', 'o', 'n', 5, 'm', 'e', 'o', 'n', 'j', 5, 'm', 'e', 'o', 'n', 'h', 4, 'm', 'e', 'o', 'd', 4, 'm', 'e', 'o', 'l', 5, 'm', 'e', 'o', 'l', 'g', 5, 'm', 'e', 'o', 'l', 'm', 5, 'm', 'e', 'o', 'l', 'b', 5, 'm', 'e', 'o', 'l', 's', 5, 'm', 'e', 'o', 'l', 't', 5, 'm', 'e', 'o', 'l', 'p', 5, 'm', 'e', 'o', 'l', 'h', 4, 'm', 'e', 'o', 'm', 4, 'm', 'e', 'o', 'b', 5, 'm', 'e', 'o', 'b', 's', 4, 'm', 'e', 'o', 's', 5, 'm', 'e', 'o', 's', 's', 5, 'm', 'e', 'o', 'n', 'g', 4, 'm', 'e', 'o', 'j', 4, 'm', 'e', 'o', 'c', 4, 'm', 'e', 'o', 'k', 4, 'm', 'e', 'o', 't', 4, 'm', 'e', 'o', 'p', 4, 'm', 'e', 'o', 'h', 2, 'm', 'e', 3, 'm', 'e', 'g', 4, 'm', 'e', 'g', 'g', 4, 'm', 'e', 'g', 's', 3, 'm', 'e', 'n', 4, 'm', 'e', 'n', 'j', 4, 'm', 'e', 'n', 'h', 3, 'm', 'e', 'd', 3, 'm', 'e', 'l', 4, 'm', 'e', 'l', 'g', 4, 'm', 'e', 'l', 'm', 4, 'm', 'e', 'l', 'b', 4, 'm', 'e', 'l', 's', 4, 'm', 'e', 'l', 't', 4, 'm', 'e', 'l', 'p', 4, 'm', 'e', 'l', 'h', 3, 'm', 'e', 'm', 3, 'm', 'e', 'b', 4, 'm', 'e', 'b', 's', 3, 'm', 'e', 's', 4, 'm', 'e', 's', 's', 4, 'm', 'e', 'n', 'g', 3, 'm', 'e', 'j', 3, 'm', 'e', 'c', 3, 'm', 'e', 'k', 3, 'm', 'e', 't', 3, 'm', 'e', 'p', 3, 'm', 'e', 'h', 4, 'm', 'y', 'e', 'o', 5, 'm', 'y', 'e', 'o', 'g', 6, 'm', 'y', 'e', 'o', 'g', 'g', 6, 'm', 'y', 'e', 'o', 'g', 's', 5, 'm', 'y', 'e', 'o', 'n', 6, 'm', 'y', 'e', 'o', 'n', 'j', 6, 'm', 'y', 'e', 'o', 'n', 'h', 5, 'm', 'y', 'e', 'o', 'd', 5, 'm', 'y', 'e', 'o', 'l', 6, 'm', 'y', 'e', 'o', 'l', 'g', 6, 'm', 'y', 'e', 'o', 'l', 'm', 6, 'm', 'y', 'e', 'o', 'l', 'b', 6, 'm', 'y', 'e', 'o', 'l', 's', 6, 'm', 'y', 'e', 'o', 'l', 't', 6, 'm', 'y', 'e', 'o', 'l', 'p', 6, 'm', 'y', 'e', 'o', 'l', 'h', 5, 'm', 'y', 'e', 'o', 'm', 5, 'm', 'y', 'e', 'o', 'b', 6, 'm', 'y', 'e', 'o', 'b', 's', 5, 'm', 'y', 'e', 'o', 's', 6, 'm', 'y', 'e', 'o', 's', 's', 6, 'm', 'y', 'e', 'o', 'n', 'g', 5, 'm', 'y', 'e', 'o', 'j', 5, 'm', 'y', 'e', 'o', 'c', 5, 'm', 'y', 'e', 'o', 'k', 5, 'm', 'y', 'e', 'o', 't', 5, 'm', 'y', 'e', 'o', 'p', 5, 'm', 'y', 'e', 'o', 'h', 3, 'm', 'y', 'e', 4, 'm', 'y', 'e', 'g', 5, 'm', 'y', 'e', 'g', 'g', 5, 'm', 'y', 'e', 'g', 's', 4, 'm', 'y', 'e', 'n', 5, 'm', 'y', 'e', 'n', 'j', 5, 'm', 'y', 'e', 'n', 'h', 4, 'm', 'y', 'e', 'd', 4, 'm', 'y', 'e', 'l', 5, 'm', 'y', 'e', 'l', 'g', 5, 'm', 'y', 'e', 'l', 'm', 5, 'm', 'y', 'e', 'l', 'b', 5, 'm', 'y', 'e', 'l', 's', 5, 'm', 'y', 'e', 'l', 't', 5, 'm', 'y', 'e', 'l', 'p', 5, 'm', 'y', 'e', 'l', 'h', 4, 'm', 'y', 'e', 'm', 4, 'm', 'y', 'e', 'b', 5, 'm', 'y', 'e', 'b', 's', 4, 'm', 'y', 'e', 's', 5, 'm', 'y', 'e', 's', 's', 5, 'm', 'y', 'e', 'n', 'g', 4, 'm', 'y', 'e', 'j', 4, 'm', 'y', 'e', 'c', 4, 'm', 'y', 'e', 'k', 4, 'm', 'y', 'e', 't', 4, 'm', 'y', 'e', 'p', 4, 'm', 'y', 'e', 'h', 2, 'm', 'o', 3, 'm', 'o', 'g', 4, 'm', 'o', 'g', 'g', 4, 'm', 'o', 'g', 's', 3, 'm', 'o', 'n', 4, 'm', 'o', 'n', 'j', 4, 'm', 'o', 'n', 'h', 3, 'm', 'o', 'd', 3, 'm', 'o', 'l', 4, 'm', 'o', 'l', 'g', 4, 'm', 'o', 'l', 'm', 4, 'm', 'o', 'l', 'b', 4, 'm', 'o', 'l', 's', 4, 'm', 'o', 'l', 't', 4, 'm', 'o', 'l', 'p', 4, 'm', 'o', 'l', 'h', 3, 'm', 'o', 'm', 3, 'm', 'o', 'b', 4, 'm', 'o', 'b', 's', 3, 'm', 'o', 's', 4, 'm', 'o', 's', 's', 4, 'm', 'o', 'n', 'g', 3, 'm', 'o', 'j', 3, 'm', 'o', 'c', 3, 'm', 'o', 'k', 3, 'm', 'o', 't', 3, 'm', 'o', 'p', 3, 'm', 'o', 'h', 3, 'm', 'w', 'a', 4, 'm', 'w', 'a', 'g', 5, 'm', 'w', 'a', 'g', 'g', 5, 'm', 'w', 'a', 'g', 's', 4, 'm', 'w', 'a', 'n', 5, 'm', 'w', 'a', 'n', 'j', 5, 'm', 'w', 'a', 'n', 'h', 4, 'm', 'w', 'a', 'd', 4, 'm', 'w', 'a', 'l', 5, 'm', 'w', 'a', 'l', 'g', 5, 'm', 'w', 'a', 'l', 'm', 5, 'm', 'w', 'a', 'l', 'b', 5, 'm', 'w', 'a', 'l', 's', 5, 'm', 'w', 'a', 'l', 't', 5, 'm', 'w', 'a', 'l', 'p', 5, 'm', 'w', 'a', 'l', 'h', 4, 'm', 'w', 'a', 'm', 4, 'm', 'w', 'a', 'b', 5, 'm', 'w', 'a', 'b', 's', 4, 'm', 'w', 'a', 's', 5, 'm', 'w', 'a', 's', 's', 5, 'm', 'w', 'a', 'n', 'g', 4, 'm', 'w', 'a', 'j', 4, 'm', 'w', 'a', 'c', 4, 'm', 'w', 'a', 'k', 4, 'm', 'w', 'a', 't', 4, 'm', 'w', 'a', 'p', 4, 'm', 'w', 'a', 'h', 4, 'm', 'w', 'a', 'e', 5, 'm', 'w', 'a', 'e', 'g', 6, 'm', 'w', 'a', 'e', 'g', 'g', 6, 'm', 'w', 'a', 'e', 'g', 's', 5, 'm', 'w', 'a', 'e', 'n', 6, 'm', 'w', 'a', 'e', 'n', 'j', 6, 'm', 'w', 'a', 'e', 'n', 'h', 5, 'm', 'w', 'a', 'e', 'd', 5, 'm', 'w', 'a', 'e', 'l', 6, 'm', 'w', 'a', 'e', 'l', 'g', 6, 'm', 'w', 'a', 'e', 'l', 'm', 6, 'm', 'w', 'a', 'e', 'l', 'b', 6, 'm', 'w', 'a', 'e', 'l', 's', 6, 'm', 'w', 'a', 'e', 'l', 't', 6, 'm', 'w', 'a', 'e', 'l', 'p', 6, 'm', 'w', 'a', 'e', 'l', 'h', 5, 'm', 'w', 'a', 'e', 'm', 5, 'm', 'w', 'a', 'e', 'b', 6, 'm', 'w', 'a', 'e', 'b', 's', 5, 'm', 'w', 'a', 'e', 's', 6, 'm', 'w', 'a', 'e', 's', 's', 6, 'm', 'w', 'a', 'e', 'n', 'g', 5, 'm', 'w', 'a', 'e', 'j', 5, 'm', 'w', 'a', 'e', 'c', 5, 'm', 'w', 'a', 'e', 'k', 5, 'm', 'w', 'a', 'e', 't', 5, 'm', 'w', 'a', 'e', 'p', 5, 'm', 'w', 'a', 'e', 'h', 3, 'm', 'o', 'e', 4, 'm', 'o', 'e', 'g', 5, 'm', 'o', 'e', 'g', 'g', 5, 'm', 'o', 'e', 'g', 's', 4, 'm', 'o', 'e', 'n', 5, 'm', 'o', 'e', 'n', 'j', 5, 'm', 'o', 'e', 'n', 'h', 4, 'm', 'o', 'e', 'd', 4, 'm', 'o', 'e', 'l', 5, 'm', 'o', 'e', 'l', 'g', 5, 'm', 'o', 'e', 'l', 'm', 5, 'm', 'o', 'e', 'l', 'b', 5, 'm', 'o', 'e', 'l', 's', 5, 'm', 'o', 'e', 'l', 't', 5, 'm', 'o', 'e', 'l', 'p', 5, 'm', 'o', 'e', 'l', 'h', 4, 'm', 'o', 'e', 'm', 4, 'm', 'o', 'e', 'b', 5, 'm', 'o', 'e', 'b', 's', 4, 'm', 'o', 'e', 's', 5, 'm', 'o', 'e', 's', 's', 5, 'm', 'o', 'e', 'n', 'g', 4, 'm', 'o', 'e', 'j', 4, 'm', 'o', 'e', 'c', 4, 'm', 'o', 'e', 'k', 4, 'm', 'o', 'e', 't', 4, 'm', 'o', 'e', 'p', 4, 'm', 'o', 'e', 'h', 3, 'm', 'y', 'o', 4, 'm', 'y', 'o', 'g', 5, 'm', 'y', 'o', 'g', 'g', 5, 'm', 'y', 'o', 'g', 's', 4, 'm', 'y', 'o', 'n', 5, 'm', 'y', 'o', 'n', 'j', 5, 'm', 'y', 'o', 'n', 'h', 4, 'm', 'y', 'o', 'd', 4, 'm', 'y', 'o', 'l', 5, 'm', 'y', 'o', 'l', 'g', 5, 'm', 'y', 'o', 'l', 'm', 5, 'm', 'y', 'o', 'l', 'b', 5, 'm', 'y', 'o', 'l', 's', 5, 'm', 'y', 'o', 'l', 't', 5, 'm', 'y', 'o', 'l', 'p', 5, 'm', 'y', 'o', 'l', 'h', 4, 'm', 'y', 'o', 'm', 4, 'm', 'y', 'o', 'b', 5, 'm', 'y', 'o', 'b', 's', 4, 'm', 'y', 'o', 's', 5, 'm', 'y', 'o', 's', 's', 5, 'm', 'y', 'o', 'n', 'g', 4, 'm', 'y', 'o', 'j', 4, 'm', 'y', 'o', 'c', 4, 'm', 'y', 'o', 'k', 4, 'm', 'y', 'o', 't', 4, 'm', 'y', 'o', 'p', 4, 'm', 'y', 'o', 'h', 2, 'm', 'u', 3, 'm', 'u', 'g', 4, 'm', 'u', 'g', 'g', 4, 'm', 'u', 'g', 's', 3, 'm', 'u', 'n', 4, 'm', 'u', 'n', 'j', 4, 'm', 'u', 'n', 'h', 3, 'm', 'u', 'd', 3, 'm', 'u', 'l', 4, 'm', 'u', 'l', 'g', 4, 'm', 'u', 'l', 'm', 4, 'm', 'u', 'l', 'b', 4, 'm', 'u', 'l', 's', 4, 'm', 'u', 'l', 't', 4, 'm', 'u', 'l', 'p', 4, 'm', 'u', 'l', 'h', 3, 'm', 'u', 'm', 3, 'm', 'u', 'b', 4, 'm', 'u', 'b', 's', 3, 'm', 'u', 's', 4, 'm', 'u', 's', 's', 4, 'm', 'u', 'n', 'g', 3, 'm', 'u', 'j', 3, 'm', 'u', 'c', 3, 'm', 'u', 'k', 3, 'm', 'u', 't', 3, 'm', 'u', 'p', 3, 'm', 'u', 'h', 4, 'm', 'w', 'e', 'o', 5, 'm', 'w', 'e', 'o', 'g', 6, 'm', 'w', 'e', 'o', 'g', 'g', 6, 'm', 'w', 'e', 'o', 'g', 's', 5, 'm', 'w', 'e', 'o', 'n', 6, 'm', 'w', 'e', 'o', 'n', 'j', 6, 'm', 'w', 'e', 'o', 'n', 'h', 5, 'm', 'w', 'e', 'o', 'd', 5, 'm', 'w', 'e', 'o', 'l', 6, 'm', 'w', 'e', 'o', 'l', 'g', 6, 'm', 'w', 'e', 'o', 'l', 'm', 6, 'm', 'w', 'e', 'o', 'l', 'b', 6, 'm', 'w', 'e', 'o', 'l', 's', 6, 'm', 'w', 'e', 'o', 'l', 't', 6, 'm', 'w', 'e', 'o', 'l', 'p', 6, 'm', 'w', 'e', 'o', 'l', 'h', 5, 'm', 'w', 'e', 'o', 'm', 5, 'm', 'w', 'e', 'o', 'b', 6, 'm', 'w', 'e', 'o', 'b', 's', 5, 'm', 'w', 'e', 'o', 's', 6, 'm', 'w', 'e', 'o', 's', 's', 6, 'm', 'w', 'e', 'o', 'n', 'g', 5, 'm', 'w', 'e', 'o', 'j', 5, 'm', 'w', 'e', 'o', 'c', 5, 'm', 'w', 'e', 'o', 'k', 5, 'm', 'w', 'e', 'o', 't', 5, 'm', 'w', 'e', 'o', 'p', 5, 'm', 'w', 'e', 'o', 'h', 3, 'm', 'w', 'e', 4, 'm', 'w', 'e', 'g', 5, 'm', 'w', 'e', 'g', 'g', 5, 'm', 'w', 'e', 'g', 's', 4, 'm', 'w', 'e', 'n', 5, 'm', 'w', 'e', 'n', 'j', 5, 'm', 'w', 'e', 'n', 'h', 4, 'm', 'w', 'e', 'd', 4, 'm', 'w', 'e', 'l', 5, 'm', 'w', 'e', 'l', 'g', 5, 'm', 'w', 'e', 'l', 'm', 5, 'm', 'w', 'e', 'l', 'b', 5, 'm', 'w', 'e', 'l', 's', 5, 'm', 'w', 'e', 'l', 't', 5, 'm', 'w', 'e', 'l', 'p', 5, 'm', 'w', 'e', 'l', 'h', 4, 'm', 'w', 'e', 'm', 4, 'm', 'w', 'e', 'b', 5, 'm', 'w', 'e', 'b', 's', 4, 'm', 'w', 'e', 's', 5, 'm', 'w', 'e', 's', 's', 5, 'm', 'w', 'e', 'n', 'g', 4, 'm', 'w', 'e', 'j', 4, 'm', 'w', 'e', 'c', 4, 'm', 'w', 'e', 'k', 4, 'm', 'w', 'e', 't', 4, 'm', 'w', 'e', 'p', 4, 'm', 'w', 'e', 'h', 3, 'm', 'w', 'i', 4, 'm', 'w', 'i', 'g', 5, 'm', 'w', 'i', 'g', 'g', 5, 'm', 'w', 'i', 'g', 's', 4, 'm', 'w', 'i', 'n', 5, 'm', 'w', 'i', 'n', 'j', 5, 'm', 'w', 'i', 'n', 'h', 4, 'm', 'w', 'i', 'd', 4, 'm', 'w', 'i', 'l', 5, 'm', 'w', 'i', 'l', 'g', 5, 'm', 'w', 'i', 'l', 'm', 5, 'm', 'w', 'i', 'l', 'b', 5, 'm', 'w', 'i', 'l', 's', 5, 'm', 'w', 'i', 'l', 't', 5, 'm', 'w', 'i', 'l', 'p', 5, 'm', 'w', 'i', 'l', 'h', 4, 'm', 'w', 'i', 'm', 4, 'm', 'w', 'i', 'b', 5, 'm', 'w', 'i', 'b', 's', 4, 'm', 'w', 'i', 's', 5, 'm', 'w', 'i', 's', 's', 5, 'm', 'w', 'i', 'n', 'g', 4, 'm', 'w', 'i', 'j', 4, 'm', 'w', 'i', 'c', 4, 'm', 'w', 'i', 'k', 4, 'm', 'w', 'i', 't', 4, 'm', 'w', 'i', 'p', 4, 'm', 'w', 'i', 'h', 3, 'm', 'y', 'u', 4, 'm', 'y', 'u', 'g', 5, 'm', 'y', 'u', 'g', 'g', 5, 'm', 'y', 'u', 'g', 's', 4, 'm', 'y', 'u', 'n', 5, 'm', 'y', 'u', 'n', 'j', 5, 'm', 'y', 'u', 'n', 'h', 4, 'm', 'y', 'u', 'd', 4, 'm', 'y', 'u', 'l', 5, 'm', 'y', 'u', 'l', 'g', 5, 'm', 'y', 'u', 'l', 'm', 5, 'm', 'y', 'u', 'l', 'b', 5, 'm', 'y', 'u', 'l', 's', 5, 'm', 'y', 'u', 'l', 't', 5, 'm', 'y', 'u', 'l', 'p', 5, 'm', 'y', 'u', 'l', 'h', 4, 'm', 'y', 'u', 'm', 4, 'm', 'y', 'u', 'b', 5, 'm', 'y', 'u', 'b', 's', 4, 'm', 'y', 'u', 's', 5, 'm', 'y', 'u', 's', 's', 5, 'm', 'y', 'u', 'n', 'g', 4, 'm', 'y', 'u', 'j', 4, 'm', 'y', 'u', 'c', 4, 'm', 'y', 'u', 'k', 4, 'm', 'y', 'u', 't', 4, 'm', 'y', 'u', 'p', 4, 'm', 'y', 'u', 'h', 3, 'm', 'e', 'u', 4, 'm', 'e', 'u', 'g', 5, 'm', 'e', 'u', 'g', 'g', 5, 'm', 'e', 'u', 'g', 's', 4, 'm', 'e', 'u', 'n', 5, 'm', 'e', 'u', 'n', 'j', 5, 'm', 'e', 'u', 'n', 'h', 4, 'm', 'e', 'u', 'd', 4, 'm', 'e', 'u', 'l', 5, 'm', 'e', 'u', 'l', 'g', 5, 'm', 'e', 'u', 'l', 'm', 5, 'm', 'e', 'u', 'l', 'b', 5, 'm', 'e', 'u', 'l', 's', 5, 'm', 'e', 'u', 'l', 't', 5, 'm', 'e', 'u', 'l', 'p', 5, 'm', 'e', 'u', 'l', 'h', 4, 'm', 'e', 'u', 'm', 4, 'm', 'e', 'u', 'b', 5, 'm', 'e', 'u', 'b', 's', 4, 'm', 'e', 'u', 's', 5, 'm', 'e', 'u', 's', 's', 5, 'm', 'e', 'u', 'n', 'g', 4, 'm', 'e', 'u', 'j', 4, 'm', 'e', 'u', 'c', 4, 'm', 'e', 'u', 'k', 4, 'm', 'e', 'u', 't', 4, 'm', 'e', 'u', 'p', 4, 'm', 'e', 'u', 'h', 3, 'm', 'y', 'i', 4, 'm', 'y', 'i', 'g', 5, 'm', 'y', 'i', 'g', 'g', 5, 'm', 'y', 'i', 'g', 's', 4, 'm', 'y', 'i', 'n', 5, 'm', 'y', 'i', 'n', 'j', 5, 'm', 'y', 'i', 'n', 'h', 4, 'm', 'y', 'i', 'd', 4, 'm', 'y', 'i', 'l', 5, 'm', 'y', 'i', 'l', 'g', 5, 'm', 'y', 'i', 'l', 'm', 5, 'm', 'y', 'i', 'l', 'b', 5, 'm', 'y', 'i', 'l', 's', 5, 'm', 'y', 'i', 'l', 't', 5, 'm', 'y', 'i', 'l', 'p', 5, 'm', 'y', 'i', 'l', 'h', 4, 'm', 'y', 'i', 'm', 4, 'm', 'y', 'i', 'b', 5, 'm', 'y', 'i', 'b', 's', 4, 'm', 'y', 'i', 's', 5, 'm', 'y', 'i', 's', 's', 5, 'm', 'y', 'i', 'n', 'g', 4, 'm', 'y', 'i', 'j', 4, 'm', 'y', 'i', 'c', 4, 'm', 'y', 'i', 'k', 4, 'm', 'y', 'i', 't', 4, 'm', 'y', 'i', 'p', 4, 'm', 'y', 'i', 'h', 2, 'm', 'i', 3, 'm', 'i', 'g', 4, 'm', 'i', 'g', 'g', 4, 'm', 'i', 'g', 's', 3, 'm', 'i', 'n', 4, 'm', 'i', 'n', 'j', 4, 'm', 'i', 'n', 'h', 3, 'm', 'i', 'd', 3, 'm', 'i', 'l', 4, 'm', 'i', 'l', 'g', 4, 'm', 'i', 'l', 'm', 4, 'm', 'i', 'l', 'b', 4, 'm', 'i', 'l', 's', 4, 'm', 'i', 'l', 't', 4, 'm', 'i', 'l', 'p', 4, 'm', 'i', 'l', 'h', 3, 'm', 'i', 'm', 3, 'm', 'i', 'b', 4, 'm', 'i', 'b', 's', 3, 'm', 'i', 's', 4, 'm', 'i', 's', 's', 4, 'm', 'i', 'n', 'g', 3, 'm', 'i', 'j', 3, 'm', 'i', 'c', 3, 'm', 'i', 'k', 3, 'm', 'i', 't', 3, 'm', 'i', 'p', 3, 'm', 'i', 'h', 2, 'b', 'a', 3, 'b', 'a', 'g', 4, 'b', 'a', 'g', 'g', 4, 'b', 'a', 'g', 's', 3, 'b', 'a', 'n', 4, 'b', 'a', 'n', 'j', 4, 'b', 'a', 'n', 'h', 3, 'b', 'a', 'd', 3, 'b', 'a', 'l', 4, 'b', 'a', 'l', 'g', 4, 'b', 'a', 'l', 'm', 4, 'b', 'a', 'l', 'b', 4, 'b', 'a', 'l', 's', 4, 'b', 'a', 'l', 't', 4, 'b', 'a', 'l', 'p', 4, 'b', 'a', 'l', 'h', 3, 'b', 'a', 'm', 3, 'b', 'a', 'b', 4, 'b', 'a', 'b', 's', 3, 'b', 'a', 's', 4, 'b', 'a', 's', 's', 4, 'b', 'a', 'n', 'g', 3, 'b', 'a', 'j', 3, 'b', 'a', 'c', 3, 'b', 'a', 'k', 3, 'b', 'a', 't', 3, 'b', 'a', 'p', 3, 'b', 'a', 'h', 3, 'b', 'a', 'e', 4, 'b', 'a', 'e', 'g', 5, 'b', 'a', 'e', 'g', 'g', 5, 'b', 'a', 'e', 'g', 's', 4, 'b', 'a', 'e', 'n', 5, 'b', 'a', 'e', 'n', 'j', 5, 'b', 'a', 'e', 'n', 'h', 4, 'b', 'a', 'e', 'd', 4, 'b', 'a', 'e', 'l', 5, 'b', 'a', 'e', 'l', 'g', 5, 'b', 'a', 'e', 'l', 'm', 5, 'b', 'a', 'e', 'l', 'b', 5, 'b', 'a', 'e', 'l', 's', 5, 'b', 'a', 'e', 'l', 't', 5, 'b', 'a', 'e', 'l', 'p', 5, 'b', 'a', 'e', 'l', 'h', 4, 'b', 'a', 'e', 'm', 4, 'b', 'a', 'e', 'b', 5, 'b', 'a', 'e', 'b', 's', 4, 'b', 'a', 'e', 's', 5, 'b', 'a', 'e', 's', 's', 5, 'b', 'a', 'e', 'n', 'g', 4, 'b', 'a', 'e', 'j', 4, 'b', 'a', 'e', 'c', 4, 'b', 'a', 'e', 'k', 4, 'b', 'a', 'e', 't', 4, 'b', 'a', 'e', 'p', 4, 'b', 'a', 'e', 'h', 3, 'b', 'y', 'a', 4, 'b', 'y', 'a', 'g', 5, 'b', 'y', 'a', 'g', 'g', 5, 'b', 'y', 'a', 'g', 's', 4, 'b', 'y', 'a', 'n', 5, 'b', 'y', 'a', 'n', 'j', 5, 'b', 'y', 'a', 'n', 'h', 4, 'b', 'y', 'a', 'd', 4, 'b', 'y', 'a', 'l', 5, 'b', 'y', 'a', 'l', 'g', 5, 'b', 'y', 'a', 'l', 'm', 5, 'b', 'y', 'a', 'l', 'b', 5, 'b', 'y', 'a', 'l', 's', 5, 'b', 'y', 'a', 'l', 't', 5, 'b', 'y', 'a', 'l', 'p', 5, 'b', 'y', 'a', 'l', 'h', 4, 'b', 'y', 'a', 'm', 4, 'b', 'y', 'a', 'b', 5, 'b', 'y', 'a', 'b', 's', 4, 'b', 'y', 'a', 's', 5, 'b', 'y', 'a', 's', 's', 5, 'b', 'y', 'a', 'n', 'g', 4, 'b', 'y', 'a', 'j', 4, 'b', 'y', 'a', 'c', 4, 'b', 'y', 'a', 'k', 4, 'b', 'y', 'a', 't', 4, 'b', 'y', 'a', 'p', 4, 'b', 'y', 'a', 'h', 4, 'b', 'y', 'a', 'e', 5, 'b', 'y', 'a', 'e', 'g', 6, 'b', 'y', 'a', 'e', 'g', 'g', 6, 'b', 'y', 'a', 'e', 'g', 's', 5, 'b', 'y', 'a', 'e', 'n', 6, 'b', 'y', 'a', 'e', 'n', 'j', 6, 'b', 'y', 'a', 'e', 'n', 'h', 5, 'b', 'y', 'a', 'e', 'd', 5, 'b', 'y', 'a', 'e', 'l', 6, 'b', 'y', 'a', 'e', 'l', 'g', 6, 'b', 'y', 'a', 'e', 'l', 'm', 6, 'b', 'y', 'a', 'e', 'l', 'b', 6, 'b', 'y', 'a', 'e', 'l', 's', 6, 'b', 'y', 'a', 'e', 'l', 't', 6, 'b', 'y', 'a', 'e', 'l', 'p', 6, 'b', 'y', 'a', 'e', 'l', 'h', 5, 'b', 'y', 'a', 'e', 'm', 5, 'b', 'y', 'a', 'e', 'b', 6, 'b', 'y', 'a', 'e', 'b', 's', 5, 'b', 'y', 'a', 'e', 's', 6, 'b', 'y', 'a', 'e', 's', 's', 6, 'b', 'y', 'a', 'e', 'n', 'g', 5, 'b', 'y', 'a', 'e', 'j', 5, 'b', 'y', 'a', 'e', 'c', 5, 'b', 'y', 'a', 'e', 'k', 5, 'b', 'y', 'a', 'e', 't', 5, 'b', 'y', 'a', 'e', 'p', 5, 'b', 'y', 'a', 'e', 'h', 3, 'b', 'e', 'o', 4, 'b', 'e', 'o', 'g', 5, 'b', 'e', 'o', 'g', 'g', 5, 'b', 'e', 'o', 'g', 's', 4, 'b', 'e', 'o', 'n', 5, 'b', 'e', 'o', 'n', 'j', 5, 'b', 'e', 'o', 'n', 'h', 4, 'b', 'e', 'o', 'd', 4, 'b', 'e', 'o', 'l', 5, 'b', 'e', 'o', 'l', 'g', 5, 'b', 'e', 'o', 'l', 'm', 5, 'b', 'e', 'o', 'l', 'b', 5, 'b', 'e', 'o', 'l', 's', 5, 'b', 'e', 'o', 'l', 't', 5, 'b', 'e', 'o', 'l', 'p', 5, 'b', 'e', 'o', 'l', 'h', 4, 'b', 'e', 'o', 'm', 4, 'b', 'e', 'o', 'b', 5, 'b', 'e', 'o', 'b', 's', 4, 'b', 'e', 'o', 's', 5, 'b', 'e', 'o', 's', 's', 5, 'b', 'e', 'o', 'n', 'g', 4, 'b', 'e', 'o', 'j', 4, 'b', 'e', 'o', 'c', 4, 'b', 'e', 'o', 'k', 4, 'b', 'e', 'o', 't', 4, 'b', 'e', 'o', 'p', 4, 'b', 'e', 'o', 'h', 2, 'b', 'e', 3, 'b', 'e', 'g', 4, 'b', 'e', 'g', 'g', 4, 'b', 'e', 'g', 's', 3, 'b', 'e', 'n', 4, 'b', 'e', 'n', 'j', 4, 'b', 'e', 'n', 'h', 3, 'b', 'e', 'd', 3, 'b', 'e', 'l', 4, 'b', 'e', 'l', 'g', 4, 'b', 'e', 'l', 'm', 4, 'b', 'e', 'l', 'b', 4, 'b', 'e', 'l', 's', 4, 'b', 'e', 'l', 't', 4, 'b', 'e', 'l', 'p', 4, 'b', 'e', 'l', 'h', 3, 'b', 'e', 'm', 3, 'b', 'e', 'b', 4, 'b', 'e', 'b', 's', 3, 'b', 'e', 's', 4, 'b', 'e', 's', 's', 4, 'b', 'e', 'n', 'g', 3, 'b', 'e', 'j', 3, 'b', 'e', 'c', 3, 'b', 'e', 'k', 3, 'b', 'e', 't', 3, 'b', 'e', 'p', 3, 'b', 'e', 'h', 4, 'b', 'y', 'e', 'o', 5, 'b', 'y', 'e', 'o', 'g', 6, 'b', 'y', 'e', 'o', 'g', 'g', 6, 'b', 'y', 'e', 'o', 'g', 's', 5, 'b', 'y', 'e', 'o', 'n', 6, 'b', 'y', 'e', 'o', 'n', 'j', 6, 'b', 'y', 'e', 'o', 'n', 'h', 5, 'b', 'y', 'e', 'o', 'd', 5, 'b', 'y', 'e', 'o', 'l', 6, 'b', 'y', 'e', 'o', 'l', 'g', 6, 'b', 'y', 'e', 'o', 'l', 'm', 6, 'b', 'y', 'e', 'o', 'l', 'b', 6, 'b', 'y', 'e', 'o', 'l', 's', 6, 'b', 'y', 'e', 'o', 'l', 't', 6, 'b', 'y', 'e', 'o', 'l', 'p', 6, 'b', 'y', 'e', 'o', 'l', 'h', 5, 'b', 'y', 'e', 'o', 'm', 5, 'b', 'y', 'e', 'o', 'b', 6, 'b', 'y', 'e', 'o', 'b', 's', 5, 'b', 'y', 'e', 'o', 's', 6, 'b', 'y', 'e', 'o', 's', 's', 6, 'b', 'y', 'e', 'o', 'n', 'g', 5, 'b', 'y', 'e', 'o', 'j', 5, 'b', 'y', 'e', 'o', 'c', 5, 'b', 'y', 'e', 'o', 'k', 5, 'b', 'y', 'e', 'o', 't', 5, 'b', 'y', 'e', 'o', 'p', 5, 'b', 'y', 'e', 'o', 'h', 3, 'b', 'y', 'e', 4, 'b', 'y', 'e', 'g', 5, 'b', 'y', 'e', 'g', 'g', 5, 'b', 'y', 'e', 'g', 's', 4, 'b', 'y', 'e', 'n', 5, 'b', 'y', 'e', 'n', 'j', 5, 'b', 'y', 'e', 'n', 'h', 4, 'b', 'y', 'e', 'd', 4, 'b', 'y', 'e', 'l', 5, 'b', 'y', 'e', 'l', 'g', 5, 'b', 'y', 'e', 'l', 'm', 5, 'b', 'y', 'e', 'l', 'b', 5, 'b', 'y', 'e', 'l', 's', 5, 'b', 'y', 'e', 'l', 't', 5, 'b', 'y', 'e', 'l', 'p', 5, 'b', 'y', 'e', 'l', 'h', 4, 'b', 'y', 'e', 'm', 4, 'b', 'y', 'e', 'b', 5, 'b', 'y', 'e', 'b', 's', 4, 'b', 'y', 'e', 's', 5, 'b', 'y', 'e', 's', 's', 5, 'b', 'y', 'e', 'n', 'g', 4, 'b', 'y', 'e', 'j', 4, 'b', 'y', 'e', 'c', 4, 'b', 'y', 'e', 'k', 4, 'b', 'y', 'e', 't', 4, 'b', 'y', 'e', 'p', 4, 'b', 'y', 'e', 'h', 2, 'b', 'o', 3, 'b', 'o', 'g', 4, 'b', 'o', 'g', 'g', 4, 'b', 'o', 'g', 's', 3, 'b', 'o', 'n', 4, 'b', 'o', 'n', 'j', 4, 'b', 'o', 'n', 'h', 3, 'b', 'o', 'd', 3, 'b', 'o', 'l', 4, 'b', 'o', 'l', 'g', 4, 'b', 'o', 'l', 'm', 4, 'b', 'o', 'l', 'b', 4, 'b', 'o', 'l', 's', 4, 'b', 'o', 'l', 't', 4, 'b', 'o', 'l', 'p', 4, 'b', 'o', 'l', 'h', 3, 'b', 'o', 'm', 3, 'b', 'o', 'b', 4, 'b', 'o', 'b', 's', 3, 'b', 'o', 's', 4, 'b', 'o', 's', 's', 4, 'b', 'o', 'n', 'g', 3, 'b', 'o', 'j', 3, 'b', 'o', 'c', 3, 'b', 'o', 'k', 3, 'b', 'o', 't', 3, 'b', 'o', 'p', 3, 'b', 'o', 'h', 3, 'b', 'w', 'a', 4, 'b', 'w', 'a', 'g', 5, 'b', 'w', 'a', 'g', 'g', 5, 'b', 'w', 'a', 'g', 's', 4, 'b', 'w', 'a', 'n', 5, 'b', 'w', 'a', 'n', 'j', 5, 'b', 'w', 'a', 'n', 'h', 4, 'b', 'w', 'a', 'd', 4, 'b', 'w', 'a', 'l', 5, 'b', 'w', 'a', 'l', 'g', 5, 'b', 'w', 'a', 'l', 'm', 5, 'b', 'w', 'a', 'l', 'b', 5, 'b', 'w', 'a', 'l', 's', 5, 'b', 'w', 'a', 'l', 't', 5, 'b', 'w', 'a', 'l', 'p', 5, 'b', 'w', 'a', 'l', 'h', 4, 'b', 'w', 'a', 'm', 4, 'b', 'w', 'a', 'b', 5, 'b', 'w', 'a', 'b', 's', 4, 'b', 'w', 'a', 's', 5, 'b', 'w', 'a', 's', 's', 5, 'b', 'w', 'a', 'n', 'g', 4, 'b', 'w', 'a', 'j', 4, 'b', 'w', 'a', 'c', 4, 'b', 'w', 'a', 'k', 4, 'b', 'w', 'a', 't', 4, 'b', 'w', 'a', 'p', 4, 'b', 'w', 'a', 'h', 4, 'b', 'w', 'a', 'e', 5, 'b', 'w', 'a', 'e', 'g', 6, 'b', 'w', 'a', 'e', 'g', 'g', 6, 'b', 'w', 'a', 'e', 'g', 's', 5, 'b', 'w', 'a', 'e', 'n', 6, 'b', 'w', 'a', 'e', 'n', 'j', 6, 'b', 'w', 'a', 'e', 'n', 'h', 5, 'b', 'w', 'a', 'e', 'd', 5, 'b', 'w', 'a', 'e', 'l', 6, 'b', 'w', 'a', 'e', 'l', 'g', 6, 'b', 'w', 'a', 'e', 'l', 'm', 6, 'b', 'w', 'a', 'e', 'l', 'b', 6, 'b', 'w', 'a', 'e', 'l', 's', 6, 'b', 'w', 'a', 'e', 'l', 't', 6, 'b', 'w', 'a', 'e', 'l', 'p', 6, 'b', 'w', 'a', 'e', 'l', 'h', 5, 'b', 'w', 'a', 'e', 'm', 5, 'b', 'w', 'a', 'e', 'b', 6, 'b', 'w', 'a', 'e', 'b', 's', 5, 'b', 'w', 'a', 'e', 's', 6, 'b', 'w', 'a', 'e', 's', 's', 6, 'b', 'w', 'a', 'e', 'n', 'g', 5, 'b', 'w', 'a', 'e', 'j', 5, 'b', 'w', 'a', 'e', 'c', 5, 'b', 'w', 'a', 'e', 'k', 5, 'b', 'w', 'a', 'e', 't', 5, 'b', 'w', 'a', 'e', 'p', 5, 'b', 'w', 'a', 'e', 'h', 3, 'b', 'o', 'e', 4, 'b', 'o', 'e', 'g', 5, 'b', 'o', 'e', 'g', 'g', 5, 'b', 'o', 'e', 'g', 's', 4, 'b', 'o', 'e', 'n', 5, 'b', 'o', 'e', 'n', 'j', 5, 'b', 'o', 'e', 'n', 'h', 4, 'b', 'o', 'e', 'd', 4, 'b', 'o', 'e', 'l', 5, 'b', 'o', 'e', 'l', 'g', 5, 'b', 'o', 'e', 'l', 'm', 5, 'b', 'o', 'e', 'l', 'b', 5, 'b', 'o', 'e', 'l', 's', 5, 'b', 'o', 'e', 'l', 't', 5, 'b', 'o', 'e', 'l', 'p', 5, 'b', 'o', 'e', 'l', 'h', 4, 'b', 'o', 'e', 'm', 4, 'b', 'o', 'e', 'b', 5, 'b', 'o', 'e', 'b', 's', 4, 'b', 'o', 'e', 's', 5, 'b', 'o', 'e', 's', 's', 5, 'b', 'o', 'e', 'n', 'g', 4, 'b', 'o', 'e', 'j', 4, 'b', 'o', 'e', 'c', 4, 'b', 'o', 'e', 'k', 4, 'b', 'o', 'e', 't', 4, 'b', 'o', 'e', 'p', 4, 'b', 'o', 'e', 'h', 3, 'b', 'y', 'o', 4, 'b', 'y', 'o', 'g', 5, 'b', 'y', 'o', 'g', 'g', 5, 'b', 'y', 'o', 'g', 's', 4, 'b', 'y', 'o', 'n', 5, 'b', 'y', 'o', 'n', 'j', 5, 'b', 'y', 'o', 'n', 'h', 4, 'b', 'y', 'o', 'd', 4, 'b', 'y', 'o', 'l', 5, 'b', 'y', 'o', 'l', 'g', 5, 'b', 'y', 'o', 'l', 'm', 5, 'b', 'y', 'o', 'l', 'b', 5, 'b', 'y', 'o', 'l', 's', 5, 'b', 'y', 'o', 'l', 't', 5, 'b', 'y', 'o', 'l', 'p', 5, 'b', 'y', 'o', 'l', 'h', 4, 'b', 'y', 'o', 'm', 4, 'b', 'y', 'o', 'b', 5, 'b', 'y', 'o', 'b', 's', 4, 'b', 'y', 'o', 's', 5, 'b', 'y', 'o', 's', 's', 5, 'b', 'y', 'o', 'n', 'g', 4, 'b', 'y', 'o', 'j', 4, 'b', 'y', 'o', 'c', 4, 'b', 'y', 'o', 'k', 4, 'b', 'y', 'o', 't', 4, 'b', 'y', 'o', 'p', 4, 'b', 'y', 'o', 'h', 2, 'b', 'u', 3, 'b', 'u', 'g', 4, 'b', 'u', 'g', 'g', 4, 'b', 'u', 'g', 's', 3, 'b', 'u', 'n', 4, 'b', 'u', 'n', 'j', 4, 'b', 'u', 'n', 'h', 3, 'b', 'u', 'd', 3, 'b', 'u', 'l', 4, 'b', 'u', 'l', 'g', 4, 'b', 'u', 'l', 'm', 4, 'b', 'u', 'l', 'b', 4, 'b', 'u', 'l', 's', 4, 'b', 'u', 'l', 't', 4, 'b', 'u', 'l', 'p', 4, 'b', 'u', 'l', 'h', 3, 'b', 'u', 'm', 3, 'b', 'u', 'b', 4, 'b', 'u', 'b', 's', 3, 'b', 'u', 's', 4, 'b', 'u', 's', 's', 4, 'b', 'u', 'n', 'g', 3, 'b', 'u', 'j', 3, 'b', 'u', 'c', 3, 'b', 'u', 'k', 3, 'b', 'u', 't', 3, 'b', 'u', 'p', 3, 'b', 'u', 'h', 4, 'b', 'w', 'e', 'o', 5, 'b', 'w', 'e', 'o', 'g', 6, 'b', 'w', 'e', 'o', 'g', 'g', 6, 'b', 'w', 'e', 'o', 'g', 's', 5, 'b', 'w', 'e', 'o', 'n', 6, 'b', 'w', 'e', 'o', 'n', 'j', 6, 'b', 'w', 'e', 'o', 'n', 'h', 5, 'b', 'w', 'e', 'o', 'd', 5, 'b', 'w', 'e', 'o', 'l', 6, 'b', 'w', 'e', 'o', 'l', 'g', 6, 'b', 'w', 'e', 'o', 'l', 'm', 6, 'b', 'w', 'e', 'o', 'l', 'b', 6, 'b', 'w', 'e', 'o', 'l', 's', 6, 'b', 'w', 'e', 'o', 'l', 't', 6, 'b', 'w', 'e', 'o', 'l', 'p', 6, 'b', 'w', 'e', 'o', 'l', 'h', 5, 'b', 'w', 'e', 'o', 'm', 5, 'b', 'w', 'e', 'o', 'b', 6, 'b', 'w', 'e', 'o', 'b', 's', 5, 'b', 'w', 'e', 'o', 's', 6, 'b', 'w', 'e', 'o', 's', 's', 6, 'b', 'w', 'e', 'o', 'n', 'g', 5, 'b', 'w', 'e', 'o', 'j', 5, 'b', 'w', 'e', 'o', 'c', 5, 'b', 'w', 'e', 'o', 'k', 5, 'b', 'w', 'e', 'o', 't', 5, 'b', 'w', 'e', 'o', 'p', 5, 'b', 'w', 'e', 'o', 'h', 3, 'b', 'w', 'e', 4, 'b', 'w', 'e', 'g', 5, 'b', 'w', 'e', 'g', 'g', 5, 'b', 'w', 'e', 'g', 's', 4, 'b', 'w', 'e', 'n', 5, 'b', 'w', 'e', 'n', 'j', 5, 'b', 'w', 'e', 'n', 'h', 4, 'b', 'w', 'e', 'd', 4, 'b', 'w', 'e', 'l', 5, 'b', 'w', 'e', 'l', 'g', 5, 'b', 'w', 'e', 'l', 'm', 5, 'b', 'w', 'e', 'l', 'b', 5, 'b', 'w', 'e', 'l', 's', 5, 'b', 'w', 'e', 'l', 't', 5, 'b', 'w', 'e', 'l', 'p', 5, 'b', 'w', 'e', 'l', 'h', 4, 'b', 'w', 'e', 'm', 4, 'b', 'w', 'e', 'b', 5, 'b', 'w', 'e', 'b', 's', 4, 'b', 'w', 'e', 's', 5, 'b', 'w', 'e', 's', 's', 5, 'b', 'w', 'e', 'n', 'g', 4, 'b', 'w', 'e', 'j', 4, 'b', 'w', 'e', 'c', 4, 'b', 'w', 'e', 'k', 4, 'b', 'w', 'e', 't', 4, 'b', 'w', 'e', 'p', 4, 'b', 'w', 'e', 'h', 3, 'b', 'w', 'i', 4, 'b', 'w', 'i', 'g', 5, 'b', 'w', 'i', 'g', 'g', 5, 'b', 'w', 'i', 'g', 's', 4, 'b', 'w', 'i', 'n', 5, 'b', 'w', 'i', 'n', 'j', 5, 'b', 'w', 'i', 'n', 'h', 4, 'b', 'w', 'i', 'd', 4, 'b', 'w', 'i', 'l', 5, 'b', 'w', 'i', 'l', 'g', 5, 'b', 'w', 'i', 'l', 'm', 5, 'b', 'w', 'i', 'l', 'b', 5, 'b', 'w', 'i', 'l', 's', 5, 'b', 'w', 'i', 'l', 't', 5, 'b', 'w', 'i', 'l', 'p', 5, 'b', 'w', 'i', 'l', 'h', 4, 'b', 'w', 'i', 'm', 4, 'b', 'w', 'i', 'b', 5, 'b', 'w', 'i', 'b', 's', 4, 'b', 'w', 'i', 's', 5, 'b', 'w', 'i', 's', 's', 5, 'b', 'w', 'i', 'n', 'g', 4, 'b', 'w', 'i', 'j', 4, 'b', 'w', 'i', 'c', 4, 'b', 'w', 'i', 'k', 4, 'b', 'w', 'i', 't', 4, 'b', 'w', 'i', 'p', 4, 'b', 'w', 'i', 'h', 3, 'b', 'y', 'u', 4, 'b', 'y', 'u', 'g', 5, 'b', 'y', 'u', 'g', 'g', 5, 'b', 'y', 'u', 'g', 's', 4, 'b', 'y', 'u', 'n', 5, 'b', 'y', 'u', 'n', 'j', 5, 'b', 'y', 'u', 'n', 'h', 4, 'b', 'y', 'u', 'd', 4, 'b', 'y', 'u', 'l', 5, 'b', 'y', 'u', 'l', 'g', 5, 'b', 'y', 'u', 'l', 'm', 5, 'b', 'y', 'u', 'l', 'b', 5, 'b', 'y', 'u', 'l', 's', 5, 'b', 'y', 'u', 'l', 't', 5, 'b', 'y', 'u', 'l', 'p', 5, 'b', 'y', 'u', 'l', 'h', 4, 'b', 'y', 'u', 'm', 4, 'b', 'y', 'u', 'b', 5, 'b', 'y', 'u', 'b', 's', 4, 'b', 'y', 'u', 's', 5, 'b', 'y', 'u', 's', 's', 5, 'b', 'y', 'u', 'n', 'g', 4, 'b', 'y', 'u', 'j', 4, 'b', 'y', 'u', 'c', 4, 'b', 'y', 'u', 'k', 4, 'b', 'y', 'u', 't', 4, 'b', 'y', 'u', 'p', 4, 'b', 'y', 'u', 'h', 3, 'b', 'e', 'u', 4, 'b', 'e', 'u', 'g', 5, 'b', 'e', 'u', 'g', 'g', 5, 'b', 'e', 'u', 'g', 's', 4, 'b', 'e', 'u', 'n', 5, 'b', 'e', 'u', 'n', 'j', 5, 'b', 'e', 'u', 'n', 'h', 4, 'b', 'e', 'u', 'd', 4, 'b', 'e', 'u', 'l', 5, 'b', 'e', 'u', 'l', 'g', 5, 'b', 'e', 'u', 'l', 'm', 5, 'b', 'e', 'u', 'l', 'b', 5, 'b', 'e', 'u', 'l', 's', 5, 'b', 'e', 'u', 'l', 't', 5, 'b', 'e', 'u', 'l', 'p', 5, 'b', 'e', 'u', 'l', 'h', 4, 'b', 'e', 'u', 'm', 4, 'b', 'e', 'u', 'b', 5, 'b', 'e', 'u', 'b', 's', 4, 'b', 'e', 'u', 's', 5, 'b', 'e', 'u', 's', 's', 5, 'b', 'e', 'u', 'n', 'g', 4, 'b', 'e', 'u', 'j', 4, 'b', 'e', 'u', 'c', 4, 'b', 'e', 'u', 'k', 4, 'b', 'e', 'u', 't', 4, 'b', 'e', 'u', 'p', 4, 'b', 'e', 'u', 'h', 3, 'b', 'y', 'i', 4, 'b', 'y', 'i', 'g', 5, 'b', 'y', 'i', 'g', 'g', 5, 'b', 'y', 'i', 'g', 's', 4, 'b', 'y', 'i', 'n', 5, 'b', 'y', 'i', 'n', 'j', 5, 'b', 'y', 'i', 'n', 'h', 4, 'b', 'y', 'i', 'd', 4, 'b', 'y', 'i', 'l', 5, 'b', 'y', 'i', 'l', 'g', 5, 'b', 'y', 'i', 'l', 'm', 5, 'b', 'y', 'i', 'l', 'b', 5, 'b', 'y', 'i', 'l', 's', 5, 'b', 'y', 'i', 'l', 't', 5, 'b', 'y', 'i', 'l', 'p', 5, 'b', 'y', 'i', 'l', 'h', 4, 'b', 'y', 'i', 'm', 4, 'b', 'y', 'i', 'b', 5, 'b', 'y', 'i', 'b', 's', 4, 'b', 'y', 'i', 's', 5, 'b', 'y', 'i', 's', 's', 5, 'b', 'y', 'i', 'n', 'g', 4, 'b', 'y', 'i', 'j', 4, 'b', 'y', 'i', 'c', 4, 'b', 'y', 'i', 'k', 4, 'b', 'y', 'i', 't', 4, 'b', 'y', 'i', 'p', 4, 'b', 'y', 'i', 'h', 2, 'b', 'i', 3, 'b', 'i', 'g', 4, 'b', 'i', 'g', 'g', 4, 'b', 'i', 'g', 's', 3, 'b', 'i', 'n', 4, 'b', 'i', 'n', 'j', 4, 'b', 'i', 'n', 'h', 3, 'b', 'i', 'd', 3, 'b', 'i', 'l', 4, 'b', 'i', 'l', 'g', 4, 'b', 'i', 'l', 'm', 4, 'b', 'i', 'l', 'b', 4, 'b', 'i', 'l', 's', 4, 'b', 'i', 'l', 't', 4, 'b', 'i', 'l', 'p', 4, 'b', 'i', 'l', 'h', 3, 'b', 'i', 'm', 3, 'b', 'i', 'b', 4, 'b', 'i', 'b', 's', 3, 'b', 'i', 's', 4, 'b', 'i', 's', 's', 4, 'b', 'i', 'n', 'g', 3, 'b', 'i', 'j', 3, 'b', 'i', 'c', 3, 'b', 'i', 'k', 3, 'b', 'i', 't', 3, 'b', 'i', 'p', 3, 'b', 'i', 'h', 3, 'b', 'b', 'a', 4, 'b', 'b', 'a', 'g', 5, 'b', 'b', 'a', 'g', 'g', 5, 'b', 'b', 'a', 'g', 's', 4, 'b', 'b', 'a', 'n', 5, 'b', 'b', 'a', 'n', 'j', 5, 'b', 'b', 'a', 'n', 'h', 4, 'b', 'b', 'a', 'd', 4, 'b', 'b', 'a', 'l', 5, 'b', 'b', 'a', 'l', 'g', 5, 'b', 'b', 'a', 'l', 'm', 5, 'b', 'b', 'a', 'l', 'b', 5, 'b', 'b', 'a', 'l', 's', 5, 'b', 'b', 'a', 'l', 't', 5, 'b', 'b', 'a', 'l', 'p', 5, 'b', 'b', 'a', 'l', 'h', 4, 'b', 'b', 'a', 'm', 4, 'b', 'b', 'a', 'b', 5, 'b', 'b', 'a', 'b', 's', 4, 'b', 'b', 'a', 's', 5, 'b', 'b', 'a', 's', 's', 5, 'b', 'b', 'a', 'n', 'g', 4, 'b', 'b', 'a', 'j', 4, 'b', 'b', 'a', 'c', 4, 'b', 'b', 'a', 'k', 4, 'b', 'b', 'a', 't', 4, 'b', 'b', 'a', 'p', 4, 'b', 'b', 'a', 'h', 4, 'b', 'b', 'a', 'e', 5, 'b', 'b', 'a', 'e', 'g', 6, 'b', 'b', 'a', 'e', 'g', 'g', 6, 'b', 'b', 'a', 'e', 'g', 's', 5, 'b', 'b', 'a', 'e', 'n', 6, 'b', 'b', 'a', 'e', 'n', 'j', 6, 'b', 'b', 'a', 'e', 'n', 'h', 5, 'b', 'b', 'a', 'e', 'd', 5, 'b', 'b', 'a', 'e', 'l', 6, 'b', 'b', 'a', 'e', 'l', 'g', 6, 'b', 'b', 'a', 'e', 'l', 'm', 6, 'b', 'b', 'a', 'e', 'l', 'b', 6, 'b', 'b', 'a', 'e', 'l', 's', 6, 'b', 'b', 'a', 'e', 'l', 't', 6, 'b', 'b', 'a', 'e', 'l', 'p', 6, 'b', 'b', 'a', 'e', 'l', 'h', 5, 'b', 'b', 'a', 'e', 'm', 5, 'b', 'b', 'a', 'e', 'b', 6, 'b', 'b', 'a', 'e', 'b', 's', 5, 'b', 'b', 'a', 'e', 's', 6, 'b', 'b', 'a', 'e', 's', 's', 6, 'b', 'b', 'a', 'e', 'n', 'g', 5, 'b', 'b', 'a', 'e', 'j', 5, 'b', 'b', 'a', 'e', 'c', 5, 'b', 'b', 'a', 'e', 'k', 5, 'b', 'b', 'a', 'e', 't', 5, 'b', 'b', 'a', 'e', 'p', 5, 'b', 'b', 'a', 'e', 'h', 4, 'b', 'b', 'y', 'a', 5, 'b', 'b', 'y', 'a', 'g', 6, 'b', 'b', 'y', 'a', 'g', 'g', 6, 'b', 'b', 'y', 'a', 'g', 's', 5, 'b', 'b', 'y', 'a', 'n', 6, 'b', 'b', 'y', 'a', 'n', 'j', 6, 'b', 'b', 'y', 'a', 'n', 'h', 5, 'b', 'b', 'y', 'a', 'd', 5, 'b', 'b', 'y', 'a', 'l', 6, 'b', 'b', 'y', 'a', 'l', 'g', 6, 'b', 'b', 'y', 'a', 'l', 'm', 6, 'b', 'b', 'y', 'a', 'l', 'b', 6, 'b', 'b', 'y', 'a', 'l', 's', 6, 'b', 'b', 'y', 'a', 'l', 't', 6, 'b', 'b', 'y', 'a', 'l', 'p', 6, 'b', 'b', 'y', 'a', 'l', 'h', 5, 'b', 'b', 'y', 'a', 'm', 5, 'b', 'b', 'y', 'a', 'b', 6, 'b', 'b', 'y', 'a', 'b', 's', 5, 'b', 'b', 'y', 'a', 's', 6, 'b', 'b', 'y', 'a', 's', 's', 6, 'b', 'b', 'y', 'a', 'n', 'g', 5, 'b', 'b', 'y', 'a', 'j', 5, 'b', 'b', 'y', 'a', 'c', 5, 'b', 'b', 'y', 'a', 'k', 5, 'b', 'b', 'y', 'a', 't', 5, 'b', 'b', 'y', 'a', 'p', 5, 'b', 'b', 'y', 'a', 'h', 5, 'b', 'b', 'y', 'a', 'e', 6, 'b', 'b', 'y', 'a', 'e', 'g', 7, 'b', 'b', 'y', 'a', 'e', 'g', 'g', 7, 'b', 'b', 'y', 'a', 'e', 'g', 's', 6, 'b', 'b', 'y', 'a', 'e', 'n', 7, 'b', 'b', 'y', 'a', 'e', 'n', 'j', 7, 'b', 'b', 'y', 'a', 'e', 'n', 'h', 6, 'b', 'b', 'y', 'a', 'e', 'd', 6, 'b', 'b', 'y', 'a', 'e', 'l', 7, 'b', 'b', 'y', 'a', 'e', 'l', 'g', 7, 'b', 'b', 'y', 'a', 'e', 'l', 'm', 7, 'b', 'b', 'y', 'a', 'e', 'l', 'b', 7, 'b', 'b', 'y', 'a', 'e', 'l', 's', 7, 'b', 'b', 'y', 'a', 'e', 'l', 't', 7, 'b', 'b', 'y', 'a', 'e', 'l', 'p', 7, 'b', 'b', 'y', 'a', 'e', 'l', 'h', 6, 'b', 'b', 'y', 'a', 'e', 'm', 6, 'b', 'b', 'y', 'a', 'e', 'b', 7, 'b', 'b', 'y', 'a', 'e', 'b', 's', 6, 'b', 'b', 'y', 'a', 'e', 's', 7, 'b', 'b', 'y', 'a', 'e', 's', 's', 7, 'b', 'b', 'y', 'a', 'e', 'n', 'g', 6, 'b', 'b', 'y', 'a', 'e', 'j', 6, 'b', 'b', 'y', 'a', 'e', 'c', 6, 'b', 'b', 'y', 'a', 'e', 'k', 6, 'b', 'b', 'y', 'a', 'e', 't', 6, 'b', 'b', 'y', 'a', 'e', 'p', 6, 'b', 'b', 'y', 'a', 'e', 'h', 4, 'b', 'b', 'e', 'o', 5, 'b', 'b', 'e', 'o', 'g', 6, 'b', 'b', 'e', 'o', 'g', 'g', 6, 'b', 'b', 'e', 'o', 'g', 's', 5, 'b', 'b', 'e', 'o', 'n', 6, 'b', 'b', 'e', 'o', 'n', 'j', 6, 'b', 'b', 'e', 'o', 'n', 'h', 5, 'b', 'b', 'e', 'o', 'd', 5, 'b', 'b', 'e', 'o', 'l', 6, 'b', 'b', 'e', 'o', 'l', 'g', 6, 'b', 'b', 'e', 'o', 'l', 'm', 6, 'b', 'b', 'e', 'o', 'l', 'b', 6, 'b', 'b', 'e', 'o', 'l', 's', 6, 'b', 'b', 'e', 'o', 'l', 't', 6, 'b', 'b', 'e', 'o', 'l', 'p', 6, 'b', 'b', 'e', 'o', 'l', 'h', 5, 'b', 'b', 'e', 'o', 'm', 5, 'b', 'b', 'e', 'o', 'b', 6, 'b', 'b', 'e', 'o', 'b', 's', 5, 'b', 'b', 'e', 'o', 's', 6, 'b', 'b', 'e', 'o', 's', 's', 6, 'b', 'b', 'e', 'o', 'n', 'g', 5, 'b', 'b', 'e', 'o', 'j', 5, 'b', 'b', 'e', 'o', 'c', 5, 'b', 'b', 'e', 'o', 'k', 5, 'b', 'b', 'e', 'o', 't', 5, 'b', 'b', 'e', 'o', 'p', 5, 'b', 'b', 'e', 'o', 'h', 3, 'b', 'b', 'e', 4, 'b', 'b', 'e', 'g', 5, 'b', 'b', 'e', 'g', 'g', 5, 'b', 'b', 'e', 'g', 's', 4, 'b', 'b', 'e', 'n', 5, 'b', 'b', 'e', 'n', 'j', 5, 'b', 'b', 'e', 'n', 'h', 4, 'b', 'b', 'e', 'd', 4, 'b', 'b', 'e', 'l', 5, 'b', 'b', 'e', 'l', 'g', 5, 'b', 'b', 'e', 'l', 'm', 5, 'b', 'b', 'e', 'l', 'b', 5, 'b', 'b', 'e', 'l', 's', 5, 'b', 'b', 'e', 'l', 't', 5, 'b', 'b', 'e', 'l', 'p', 5, 'b', 'b', 'e', 'l', 'h', 4, 'b', 'b', 'e', 'm', 4, 'b', 'b', 'e', 'b', 5, 'b', 'b', 'e', 'b', 's', 4, 'b', 'b', 'e', 's', 5, 'b', 'b', 'e', 's', 's', 5, 'b', 'b', 'e', 'n', 'g', 4, 'b', 'b', 'e', 'j', 4, 'b', 'b', 'e', 'c', 4, 'b', 'b', 'e', 'k', 4, 'b', 'b', 'e', 't', 4, 'b', 'b', 'e', 'p', 4, 'b', 'b', 'e', 'h', 5, 'b', 'b', 'y', 'e', 'o', 6, 'b', 'b', 'y', 'e', 'o', 'g', 7, 'b', 'b', 'y', 'e', 'o', 'g', 'g', 7, 'b', 'b', 'y', 'e', 'o', 'g', 's', 6, 'b', 'b', 'y', 'e', 'o', 'n', 7, 'b', 'b', 'y', 'e', 'o', 'n', 'j', 7, 'b', 'b', 'y', 'e', 'o', 'n', 'h', 6, 'b', 'b', 'y', 'e', 'o', 'd', 6, 'b', 'b', 'y', 'e', 'o', 'l', 7, 'b', 'b', 'y', 'e', 'o', 'l', 'g', 7, 'b', 'b', 'y', 'e', 'o', 'l', 'm', 7, 'b', 'b', 'y', 'e', 'o', 'l', 'b', 7, 'b', 'b', 'y', 'e', 'o', 'l', 's', 7, 'b', 'b', 'y', 'e', 'o', 'l', 't', 7, 'b', 'b', 'y', 'e', 'o', 'l', 'p', 7, 'b', 'b', 'y', 'e', 'o', 'l', 'h', 6, 'b', 'b', 'y', 'e', 'o', 'm', 6, 'b', 'b', 'y', 'e', 'o', 'b', 7, 'b', 'b', 'y', 'e', 'o', 'b', 's', 6, 'b', 'b', 'y', 'e', 'o', 's', 7, 'b', 'b', 'y', 'e', 'o', 's', 's', 7, 'b', 'b', 'y', 'e', 'o', 'n', 'g', 6, 'b', 'b', 'y', 'e', 'o', 'j', 6, 'b', 'b', 'y', 'e', 'o', 'c', 6, 'b', 'b', 'y', 'e', 'o', 'k', 6, 'b', 'b', 'y', 'e', 'o', 't', 6, 'b', 'b', 'y', 'e', 'o', 'p', 6, 'b', 'b', 'y', 'e', 'o', 'h', 4, 'b', 'b', 'y', 'e', 5, 'b', 'b', 'y', 'e', 'g', 6, 'b', 'b', 'y', 'e', 'g', 'g', 6, 'b', 'b', 'y', 'e', 'g', 's', 5, 'b', 'b', 'y', 'e', 'n', 6, 'b', 'b', 'y', 'e', 'n', 'j', 6, 'b', 'b', 'y', 'e', 'n', 'h', 5, 'b', 'b', 'y', 'e', 'd', 5, 'b', 'b', 'y', 'e', 'l', 6, 'b', 'b', 'y', 'e', 'l', 'g', 6, 'b', 'b', 'y', 'e', 'l', 'm', 6, 'b', 'b', 'y', 'e', 'l', 'b', 6, 'b', 'b', 'y', 'e', 'l', 's', 6, 'b', 'b', 'y', 'e', 'l', 't', 6, 'b', 'b', 'y', 'e', 'l', 'p', 6, 'b', 'b', 'y', 'e', 'l', 'h', 5, 'b', 'b', 'y', 'e', 'm', 5, 'b', 'b', 'y', 'e', 'b', 6, 'b', 'b', 'y', 'e', 'b', 's', 5, 'b', 'b', 'y', 'e', 's', 6, 'b', 'b', 'y', 'e', 's', 's', 6, 'b', 'b', 'y', 'e', 'n', 'g', 5, 'b', 'b', 'y', 'e', 'j', 5, 'b', 'b', 'y', 'e', 'c', 5, 'b', 'b', 'y', 'e', 'k', 5, 'b', 'b', 'y', 'e', 't', 5, 'b', 'b', 'y', 'e', 'p', 5, 'b', 'b', 'y', 'e', 'h', 3, 'b', 'b', 'o', 4, 'b', 'b', 'o', 'g', 5, 'b', 'b', 'o', 'g', 'g', 5, 'b', 'b', 'o', 'g', 's', 4, 'b', 'b', 'o', 'n', 5, 'b', 'b', 'o', 'n', 'j', 5, 'b', 'b', 'o', 'n', 'h', 4, 'b', 'b', 'o', 'd', 4, 'b', 'b', 'o', 'l', 5, 'b', 'b', 'o', 'l', 'g', 5, 'b', 'b', 'o', 'l', 'm', 5, 'b', 'b', 'o', 'l', 'b', 5, 'b', 'b', 'o', 'l', 's', 5, 'b', 'b', 'o', 'l', 't', 5, 'b', 'b', 'o', 'l', 'p', 5, 'b', 'b', 'o', 'l', 'h', 4, 'b', 'b', 'o', 'm', 4, 'b', 'b', 'o', 'b', 5, 'b', 'b', 'o', 'b', 's', 4, 'b', 'b', 'o', 's', 5, 'b', 'b', 'o', 's', 's', 5, 'b', 'b', 'o', 'n', 'g', 4, 'b', 'b', 'o', 'j', 4, 'b', 'b', 'o', 'c', 4, 'b', 'b', 'o', 'k', 4, 'b', 'b', 'o', 't', 4, 'b', 'b', 'o', 'p', 4, 'b', 'b', 'o', 'h', 4, 'b', 'b', 'w', 'a', 5, 'b', 'b', 'w', 'a', 'g', 6, 'b', 'b', 'w', 'a', 'g', 'g', 6, 'b', 'b', 'w', 'a', 'g', 's', 5, 'b', 'b', 'w', 'a', 'n', 6, 'b', 'b', 'w', 'a', 'n', 'j', 6, 'b', 'b', 'w', 'a', 'n', 'h', 5, 'b', 'b', 'w', 'a', 'd', 5, 'b', 'b', 'w', 'a', 'l', 6, 'b', 'b', 'w', 'a', 'l', 'g', 6, 'b', 'b', 'w', 'a', 'l', 'm', 6, 'b', 'b', 'w', 'a', 'l', 'b', 6, 'b', 'b', 'w', 'a', 'l', 's', 6, 'b', 'b', 'w', 'a', 'l', 't', 6, 'b', 'b', 'w', 'a', 'l', 'p', 6, 'b', 'b', 'w', 'a', 'l', 'h', 5, 'b', 'b', 'w', 'a', 'm', 5, 'b', 'b', 'w', 'a', 'b', 6, 'b', 'b', 'w', 'a', 'b', 's', 5, 'b', 'b', 'w', 'a', 's', 6, 'b', 'b', 'w', 'a', 's', 's', 6, 'b', 'b', 'w', 'a', 'n', 'g', 5, 'b', 'b', 'w', 'a', 'j', 5, 'b', 'b', 'w', 'a', 'c', 5, 'b', 'b', 'w', 'a', 'k', 5, 'b', 'b', 'w', 'a', 't', 5, 'b', 'b', 'w', 'a', 'p', 5, 'b', 'b', 'w', 'a', 'h', 5, 'b', 'b', 'w', 'a', 'e', 6, 'b', 'b', 'w', 'a', 'e', 'g', 7, 'b', 'b', 'w', 'a', 'e', 'g', 'g', 7, 'b', 'b', 'w', 'a', 'e', 'g', 's', 6, 'b', 'b', 'w', 'a', 'e', 'n', 7, 'b', 'b', 'w', 'a', 'e', 'n', 'j', 7, 'b', 'b', 'w', 'a', 'e', 'n', 'h', 6, 'b', 'b', 'w', 'a', 'e', 'd', 6, 'b', 'b', 'w', 'a', 'e', 'l', 7, 'b', 'b', 'w', 'a', 'e', 'l', 'g', 7, 'b', 'b', 'w', 'a', 'e', 'l', 'm', 7, 'b', 'b', 'w', 'a', 'e', 'l', 'b', 7, 'b', 'b', 'w', 'a', 'e', 'l', 's', 7, 'b', 'b', 'w', 'a', 'e', 'l', 't', 7, 'b', 'b', 'w', 'a', 'e', 'l', 'p', 7, 'b', 'b', 'w', 'a', 'e', 'l', 'h', 6, 'b', 'b', 'w', 'a', 'e', 'm', 6, 'b', 'b', 'w', 'a', 'e', 'b', 7, 'b', 'b', 'w', 'a', 'e', 'b', 's', 6, 'b', 'b', 'w', 'a', 'e', 's', 7, 'b', 'b', 'w', 'a', 'e', 's', 's', 7, 'b', 'b', 'w', 'a', 'e', 'n', 'g', 6, 'b', 'b', 'w', 'a', 'e', 'j', 6, 'b', 'b', 'w', 'a', 'e', 'c', 6, 'b', 'b', 'w', 'a', 'e', 'k', 6, 'b', 'b', 'w', 'a', 'e', 't', 6, 'b', 'b', 'w', 'a', 'e', 'p', 6, 'b', 'b', 'w', 'a', 'e', 'h', 4, 'b', 'b', 'o', 'e', 5, 'b', 'b', 'o', 'e', 'g', 6, 'b', 'b', 'o', 'e', 'g', 'g', 6, 'b', 'b', 'o', 'e', 'g', 's', 5, 'b', 'b', 'o', 'e', 'n', 6, 'b', 'b', 'o', 'e', 'n', 'j', 6, 'b', 'b', 'o', 'e', 'n', 'h', 5, 'b', 'b', 'o', 'e', 'd', 5, 'b', 'b', 'o', 'e', 'l', 6, 'b', 'b', 'o', 'e', 'l', 'g', 6, 'b', 'b', 'o', 'e', 'l', 'm', 6, 'b', 'b', 'o', 'e', 'l', 'b', 6, 'b', 'b', 'o', 'e', 'l', 's', 6, 'b', 'b', 'o', 'e', 'l', 't', 6, 'b', 'b', 'o', 'e', 'l', 'p', 6, 'b', 'b', 'o', 'e', 'l', 'h', 5, 'b', 'b', 'o', 'e', 'm', 5, 'b', 'b', 'o', 'e', 'b', 6, 'b', 'b', 'o', 'e', 'b', 's', 5, 'b', 'b', 'o', 'e', 's', 6, 'b', 'b', 'o', 'e', 's', 's', 6, 'b', 'b', 'o', 'e', 'n', 'g', 5, 'b', 'b', 'o', 'e', 'j', 5, 'b', 'b', 'o', 'e', 'c', 5, 'b', 'b', 'o', 'e', 'k', 5, 'b', 'b', 'o', 'e', 't', 5, 'b', 'b', 'o', 'e', 'p', 5, 'b', 'b', 'o', 'e', 'h', 4, 'b', 'b', 'y', 'o', 5, 'b', 'b', 'y', 'o', 'g', 6, 'b', 'b', 'y', 'o', 'g', 'g', 6, 'b', 'b', 'y', 'o', 'g', 's', 5, 'b', 'b', 'y', 'o', 'n', 6, 'b', 'b', 'y', 'o', 'n', 'j', 6, 'b', 'b', 'y', 'o', 'n', 'h', 5, 'b', 'b', 'y', 'o', 'd', 5, 'b', 'b', 'y', 'o', 'l', 6, 'b', 'b', 'y', 'o', 'l', 'g', 6, 'b', 'b', 'y', 'o', 'l', 'm', 6, 'b', 'b', 'y', 'o', 'l', 'b', 6, 'b', 'b', 'y', 'o', 'l', 's', 6, 'b', 'b', 'y', 'o', 'l', 't', 6, 'b', 'b', 'y', 'o', 'l', 'p', 6, 'b', 'b', 'y', 'o', 'l', 'h', 5, 'b', 'b', 'y', 'o', 'm', 5, 'b', 'b', 'y', 'o', 'b', 6, 'b', 'b', 'y', 'o', 'b', 's', 5, 'b', 'b', 'y', 'o', 's', 6, 'b', 'b', 'y', 'o', 's', 's', 6, 'b', 'b', 'y', 'o', 'n', 'g', 5, 'b', 'b', 'y', 'o', 'j', 5, 'b', 'b', 'y', 'o', 'c', 5, 'b', 'b', 'y', 'o', 'k', 5, 'b', 'b', 'y', 'o', 't', 5, 'b', 'b', 'y', 'o', 'p', 5, 'b', 'b', 'y', 'o', 'h', 3, 'b', 'b', 'u', 4, 'b', 'b', 'u', 'g', 5, 'b', 'b', 'u', 'g', 'g', 5, 'b', 'b', 'u', 'g', 's', 4, 'b', 'b', 'u', 'n', 5, 'b', 'b', 'u', 'n', 'j', 5, 'b', 'b', 'u', 'n', 'h', 4, 'b', 'b', 'u', 'd', 4, 'b', 'b', 'u', 'l', 5, 'b', 'b', 'u', 'l', 'g', 5, 'b', 'b', 'u', 'l', 'm', 5, 'b', 'b', 'u', 'l', 'b', 5, 'b', 'b', 'u', 'l', 's', 5, 'b', 'b', 'u', 'l', 't', 5, 'b', 'b', 'u', 'l', 'p', 5, 'b', 'b', 'u', 'l', 'h', 4, 'b', 'b', 'u', 'm', 4, 'b', 'b', 'u', 'b', 5, 'b', 'b', 'u', 'b', 's', 4, 'b', 'b', 'u', 's', 5, 'b', 'b', 'u', 's', 's', 5, 'b', 'b', 'u', 'n', 'g', 4, 'b', 'b', 'u', 'j', 4, 'b', 'b', 'u', 'c', 4, 'b', 'b', 'u', 'k', 4, 'b', 'b', 'u', 't', 4, 'b', 'b', 'u', 'p', 4, 'b', 'b', 'u', 'h', 5, 'b', 'b', 'w', 'e', 'o', 6, 'b', 'b', 'w', 'e', 'o', 'g', 7, 'b', 'b', 'w', 'e', 'o', 'g', 'g', 7, 'b', 'b', 'w', 'e', 'o', 'g', 's', 6, 'b', 'b', 'w', 'e', 'o', 'n', 7, 'b', 'b', 'w', 'e', 'o', 'n', 'j', 7, 'b', 'b', 'w', 'e', 'o', 'n', 'h', 6, 'b', 'b', 'w', 'e', 'o', 'd', 6, 'b', 'b', 'w', 'e', 'o', 'l', 7, 'b', 'b', 'w', 'e', 'o', 'l', 'g', 7, 'b', 'b', 'w', 'e', 'o', 'l', 'm', 7, 'b', 'b', 'w', 'e', 'o', 'l', 'b', 7, 'b', 'b', 'w', 'e', 'o', 'l', 's', 7, 'b', 'b', 'w', 'e', 'o', 'l', 't', 7, 'b', 'b', 'w', 'e', 'o', 'l', 'p', 7, 'b', 'b', 'w', 'e', 'o', 'l', 'h', 6, 'b', 'b', 'w', 'e', 'o', 'm', 6, 'b', 'b', 'w', 'e', 'o', 'b', 7, 'b', 'b', 'w', 'e', 'o', 'b', 's', 6, 'b', 'b', 'w', 'e', 'o', 's', 7, 'b', 'b', 'w', 'e', 'o', 's', 's', 7, 'b', 'b', 'w', 'e', 'o', 'n', 'g', 6, 'b', 'b', 'w', 'e', 'o', 'j', 6, 'b', 'b', 'w', 'e', 'o', 'c', 6, 'b', 'b', 'w', 'e', 'o', 'k', 6, 'b', 'b', 'w', 'e', 'o', 't', 6, 'b', 'b', 'w', 'e', 'o', 'p', 6, 'b', 'b', 'w', 'e', 'o', 'h', 4, 'b', 'b', 'w', 'e', 5, 'b', 'b', 'w', 'e', 'g', 6, 'b', 'b', 'w', 'e', 'g', 'g', 6, 'b', 'b', 'w', 'e', 'g', 's', 5, 'b', 'b', 'w', 'e', 'n', 6, 'b', 'b', 'w', 'e', 'n', 'j', 6, 'b', 'b', 'w', 'e', 'n', 'h', 5, 'b', 'b', 'w', 'e', 'd', 5, 'b', 'b', 'w', 'e', 'l', 6, 'b', 'b', 'w', 'e', 'l', 'g', 6, 'b', 'b', 'w', 'e', 'l', 'm', 6, 'b', 'b', 'w', 'e', 'l', 'b', 6, 'b', 'b', 'w', 'e', 'l', 's', 6, 'b', 'b', 'w', 'e', 'l', 't', 6, 'b', 'b', 'w', 'e', 'l', 'p', 6, 'b', 'b', 'w', 'e', 'l', 'h', 5, 'b', 'b', 'w', 'e', 'm', 5, 'b', 'b', 'w', 'e', 'b', 6, 'b', 'b', 'w', 'e', 'b', 's', 5, 'b', 'b', 'w', 'e', 's', 6, 'b', 'b', 'w', 'e', 's', 's', 6, 'b', 'b', 'w', 'e', 'n', 'g', 5, 'b', 'b', 'w', 'e', 'j', 5, 'b', 'b', 'w', 'e', 'c', 5, 'b', 'b', 'w', 'e', 'k', 5, 'b', 'b', 'w', 'e', 't', 5, 'b', 'b', 'w', 'e', 'p', 5, 'b', 'b', 'w', 'e', 'h', 4, 'b', 'b', 'w', 'i', 5, 'b', 'b', 'w', 'i', 'g', 6, 'b', 'b', 'w', 'i', 'g', 'g', 6, 'b', 'b', 'w', 'i', 'g', 's', 5, 'b', 'b', 'w', 'i', 'n', 6, 'b', 'b', 'w', 'i', 'n', 'j', 6, 'b', 'b', 'w', 'i', 'n', 'h', 5, 'b', 'b', 'w', 'i', 'd', 5, 'b', 'b', 'w', 'i', 'l', 6, 'b', 'b', 'w', 'i', 'l', 'g', 6, 'b', 'b', 'w', 'i', 'l', 'm', 6, 'b', 'b', 'w', 'i', 'l', 'b', 6, 'b', 'b', 'w', 'i', 'l', 's', 6, 'b', 'b', 'w', 'i', 'l', 't', 6, 'b', 'b', 'w', 'i', 'l', 'p', 6, 'b', 'b', 'w', 'i', 'l', 'h', 5, 'b', 'b', 'w', 'i', 'm', 5, 'b', 'b', 'w', 'i', 'b', 6, 'b', 'b', 'w', 'i', 'b', 's', 5, 'b', 'b', 'w', 'i', 's', 6, 'b', 'b', 'w', 'i', 's', 's', 6, 'b', 'b', 'w', 'i', 'n', 'g', 5, 'b', 'b', 'w', 'i', 'j', 5, 'b', 'b', 'w', 'i', 'c', 5, 'b', 'b', 'w', 'i', 'k', 5, 'b', 'b', 'w', 'i', 't', 5, 'b', 'b', 'w', 'i', 'p', 5, 'b', 'b', 'w', 'i', 'h', 4, 'b', 'b', 'y', 'u', 5, 'b', 'b', 'y', 'u', 'g', 6, 'b', 'b', 'y', 'u', 'g', 'g', 6, 'b', 'b', 'y', 'u', 'g', 's', 5, 'b', 'b', 'y', 'u', 'n', 6, 'b', 'b', 'y', 'u', 'n', 'j', 6, 'b', 'b', 'y', 'u', 'n', 'h', 5, 'b', 'b', 'y', 'u', 'd', 5, 'b', 'b', 'y', 'u', 'l', 6, 'b', 'b', 'y', 'u', 'l', 'g', 6, 'b', 'b', 'y', 'u', 'l', 'm', 6, 'b', 'b', 'y', 'u', 'l', 'b', 6, 'b', 'b', 'y', 'u', 'l', 's', 6, 'b', 'b', 'y', 'u', 'l', 't', 6, 'b', 'b', 'y', 'u', 'l', 'p', 6, 'b', 'b', 'y', 'u', 'l', 'h', 5, 'b', 'b', 'y', 'u', 'm', 5, 'b', 'b', 'y', 'u', 'b', 6, 'b', 'b', 'y', 'u', 'b', 's', 5, 'b', 'b', 'y', 'u', 's', 6, 'b', 'b', 'y', 'u', 's', 's', 6, 'b', 'b', 'y', 'u', 'n', 'g', 5, 'b', 'b', 'y', 'u', 'j', 5, 'b', 'b', 'y', 'u', 'c', 5, 'b', 'b', 'y', 'u', 'k', 5, 'b', 'b', 'y', 'u', 't', 5, 'b', 'b', 'y', 'u', 'p', 5, 'b', 'b', 'y', 'u', 'h', 4, 'b', 'b', 'e', 'u', 5, 'b', 'b', 'e', 'u', 'g', 6, 'b', 'b', 'e', 'u', 'g', 'g', 6, 'b', 'b', 'e', 'u', 'g', 's', 5, 'b', 'b', 'e', 'u', 'n', 6, 'b', 'b', 'e', 'u', 'n', 'j', 6, 'b', 'b', 'e', 'u', 'n', 'h', 5, 'b', 'b', 'e', 'u', 'd', 5, 'b', 'b', 'e', 'u', 'l', 6, 'b', 'b', 'e', 'u', 'l', 'g', 6, 'b', 'b', 'e', 'u', 'l', 'm', 6, 'b', 'b', 'e', 'u', 'l', 'b', 6, 'b', 'b', 'e', 'u', 'l', 's', 6, 'b', 'b', 'e', 'u', 'l', 't', 6, 'b', 'b', 'e', 'u', 'l', 'p', 6, 'b', 'b', 'e', 'u', 'l', 'h', 5, 'b', 'b', 'e', 'u', 'm', 5, 'b', 'b', 'e', 'u', 'b', 6, 'b', 'b', 'e', 'u', 'b', 's', 5, 'b', 'b', 'e', 'u', 's', 6, 'b', 'b', 'e', 'u', 's', 's', 6, 'b', 'b', 'e', 'u', 'n', 'g', 5, 'b', 'b', 'e', 'u', 'j', 5, 'b', 'b', 'e', 'u', 'c', 5, 'b', 'b', 'e', 'u', 'k', 5, 'b', 'b', 'e', 'u', 't', 5, 'b', 'b', 'e', 'u', 'p', 5, 'b', 'b', 'e', 'u', 'h', 4, 'b', 'b', 'y', 'i', 5, 'b', 'b', 'y', 'i', 'g', 6, 'b', 'b', 'y', 'i', 'g', 'g', 6, 'b', 'b', 'y', 'i', 'g', 's', 5, 'b', 'b', 'y', 'i', 'n', 6, 'b', 'b', 'y', 'i', 'n', 'j', 6, 'b', 'b', 'y', 'i', 'n', 'h', 5, 'b', 'b', 'y', 'i', 'd', 5, 'b', 'b', 'y', 'i', 'l', 6, 'b', 'b', 'y', 'i', 'l', 'g', 6, 'b', 'b', 'y', 'i', 'l', 'm', 6, 'b', 'b', 'y', 'i', 'l', 'b', 6, 'b', 'b', 'y', 'i', 'l', 's', 6, 'b', 'b', 'y', 'i', 'l', 't', 6, 'b', 'b', 'y', 'i', 'l', 'p', 6, 'b', 'b', 'y', 'i', 'l', 'h', 5, 'b', 'b', 'y', 'i', 'm', 5, 'b', 'b', 'y', 'i', 'b', 6, 'b', 'b', 'y', 'i', 'b', 's', 5, 'b', 'b', 'y', 'i', 's', 6, 'b', 'b', 'y', 'i', 's', 's', 6, 'b', 'b', 'y', 'i', 'n', 'g', 5, 'b', 'b', 'y', 'i', 'j', 5, 'b', 'b', 'y', 'i', 'c', 5, 'b', 'b', 'y', 'i', 'k', 5, 'b', 'b', 'y', 'i', 't', 5, 'b', 'b', 'y', 'i', 'p', 5, 'b', 'b', 'y', 'i', 'h', 3, 'b', 'b', 'i', 4, 'b', 'b', 'i', 'g', 5, 'b', 'b', 'i', 'g', 'g', 5, 'b', 'b', 'i', 'g', 's', 4, 'b', 'b', 'i', 'n', 5, 'b', 'b', 'i', 'n', 'j', 5, 'b', 'b', 'i', 'n', 'h', 4, 'b', 'b', 'i', 'd', 4, 'b', 'b', 'i', 'l', 5, 'b', 'b', 'i', 'l', 'g', 5, 'b', 'b', 'i', 'l', 'm', 5, 'b', 'b', 'i', 'l', 'b', 5, 'b', 'b', 'i', 'l', 's', 5, 'b', 'b', 'i', 'l', 't', 5, 'b', 'b', 'i', 'l', 'p', 5, 'b', 'b', 'i', 'l', 'h', 4, 'b', 'b', 'i', 'm', 4, 'b', 'b', 'i', 'b', 5, 'b', 'b', 'i', 'b', 's', 4, 'b', 'b', 'i', 's', 5, 'b', 'b', 'i', 's', 's', 5, 'b', 'b', 'i', 'n', 'g', 4, 'b', 'b', 'i', 'j', 4, 'b', 'b', 'i', 'c', 4, 'b', 'b', 'i', 'k', 4, 'b', 'b', 'i', 't', 4, 'b', 'b', 'i', 'p', 4, 'b', 'b', 'i', 'h', 2, 's', 'a', 3, 's', 'a', 'g', 4, 's', 'a', 'g', 'g', 4, 's', 'a', 'g', 's', 3, 's', 'a', 'n', 4, 's', 'a', 'n', 'j', 4, 's', 'a', 'n', 'h', 3, 's', 'a', 'd', 3, 's', 'a', 'l', 4, 's', 'a', 'l', 'g', 4, 's', 'a', 'l', 'm', 4, 's', 'a', 'l', 'b', 4, 's', 'a', 'l', 's', 4, 's', 'a', 'l', 't', 4, 's', 'a', 'l', 'p', 4, 's', 'a', 'l', 'h', 3, 's', 'a', 'm', 3, 's', 'a', 'b', 4, 's', 'a', 'b', 's', 3, 's', 'a', 's', 4, 's', 'a', 's', 's', 4, 's', 'a', 'n', 'g', 3, 's', 'a', 'j', 3, 's', 'a', 'c', 3, 's', 'a', 'k', 3, 's', 'a', 't', 3, 's', 'a', 'p', 3, 's', 'a', 'h', 3, 's', 'a', 'e', 4, 's', 'a', 'e', 'g', 5, 's', 'a', 'e', 'g', 'g', 5, 's', 'a', 'e', 'g', 's', 4, 's', 'a', 'e', 'n', 5, 's', 'a', 'e', 'n', 'j', 5, 's', 'a', 'e', 'n', 'h', 4, 's', 'a', 'e', 'd', 4, 's', 'a', 'e', 'l', 5, 's', 'a', 'e', 'l', 'g', 5, 's', 'a', 'e', 'l', 'm', 5, 's', 'a', 'e', 'l', 'b', 5, 's', 'a', 'e', 'l', 's', 5, 's', 'a', 'e', 'l', 't', 5, 's', 'a', 'e', 'l', 'p', 5, 's', 'a', 'e', 'l', 'h', 4, 's', 'a', 'e', 'm', 4, 's', 'a', 'e', 'b', 5, 's', 'a', 'e', 'b', 's', 4, 's', 'a', 'e', 's', 5, 's', 'a', 'e', 's', 's', 5, 's', 'a', 'e', 'n', 'g', 4, 's', 'a', 'e', 'j', 4, 's', 'a', 'e', 'c', 4, 's', 'a', 'e', 'k', 4, 's', 'a', 'e', 't', 4, 's', 'a', 'e', 'p', 4, 's', 'a', 'e', 'h', 3, 's', 'y', 'a', 4, 's', 'y', 'a', 'g', 5, 's', 'y', 'a', 'g', 'g', 5, 's', 'y', 'a', 'g', 's', 4, 's', 'y', 'a', 'n', 5, 's', 'y', 'a', 'n', 'j', 5, 's', 'y', 'a', 'n', 'h', 4, 's', 'y', 'a', 'd', 4, 's', 'y', 'a', 'l', 5, 's', 'y', 'a', 'l', 'g', 5, 's', 'y', 'a', 'l', 'm', 5, 's', 'y', 'a', 'l', 'b', 5, 's', 'y', 'a', 'l', 's', 5, 's', 'y', 'a', 'l', 't', 5, 's', 'y', 'a', 'l', 'p', 5, 's', 'y', 'a', 'l', 'h', 4, 's', 'y', 'a', 'm', 4, 's', 'y', 'a', 'b', 5, 's', 'y', 'a', 'b', 's', 4, 's', 'y', 'a', 's', 5, 's', 'y', 'a', 's', 's', 5, 's', 'y', 'a', 'n', 'g', 4, 's', 'y', 'a', 'j', 4, 's', 'y', 'a', 'c', 4, 's', 'y', 'a', 'k', 4, 's', 'y', 'a', 't', 4, 's', 'y', 'a', 'p', 4, 's', 'y', 'a', 'h', 4, 's', 'y', 'a', 'e', 5, 's', 'y', 'a', 'e', 'g', 6, 's', 'y', 'a', 'e', 'g', 'g', 6, 's', 'y', 'a', 'e', 'g', 's', 5, 's', 'y', 'a', 'e', 'n', 6, 's', 'y', 'a', 'e', 'n', 'j', 6, 's', 'y', 'a', 'e', 'n', 'h', 5, 's', 'y', 'a', 'e', 'd', 5, 's', 'y', 'a', 'e', 'l', 6, 's', 'y', 'a', 'e', 'l', 'g', 6, 's', 'y', 'a', 'e', 'l', 'm', 6, 's', 'y', 'a', 'e', 'l', 'b', 6, 's', 'y', 'a', 'e', 'l', 's', 6, 's', 'y', 'a', 'e', 'l', 't', 6, 's', 'y', 'a', 'e', 'l', 'p', 6, 's', 'y', 'a', 'e', 'l', 'h', 5, 's', 'y', 'a', 'e', 'm', 5, 's', 'y', 'a', 'e', 'b', 6, 's', 'y', 'a', 'e', 'b', 's', 5, 's', 'y', 'a', 'e', 's', 6, 's', 'y', 'a', 'e', 's', 's', 6, 's', 'y', 'a', 'e', 'n', 'g', 5, 's', 'y', 'a', 'e', 'j', 5, 's', 'y', 'a', 'e', 'c', 5, 's', 'y', 'a', 'e', 'k', 5, 's', 'y', 'a', 'e', 't', 5, 's', 'y', 'a', 'e', 'p', 5, 's', 'y', 'a', 'e', 'h', 3, 's', 'e', 'o', 4, 's', 'e', 'o', 'g', 5, 's', 'e', 'o', 'g', 'g', 5, 's', 'e', 'o', 'g', 's', 4, 's', 'e', 'o', 'n', 5, 's', 'e', 'o', 'n', 'j', 5, 's', 'e', 'o', 'n', 'h', 4, 's', 'e', 'o', 'd', 4, 's', 'e', 'o', 'l', 5, 's', 'e', 'o', 'l', 'g', 5, 's', 'e', 'o', 'l', 'm', 5, 's', 'e', 'o', 'l', 'b', 5, 's', 'e', 'o', 'l', 's', 5, 's', 'e', 'o', 'l', 't', 5, 's', 'e', 'o', 'l', 'p', 5, 's', 'e', 'o', 'l', 'h', 4, 's', 'e', 'o', 'm', 4, 's', 'e', 'o', 'b', 5, 's', 'e', 'o', 'b', 's', 4, 's', 'e', 'o', 's', 5, 's', 'e', 'o', 's', 's', 5, 's', 'e', 'o', 'n', 'g', 4, 's', 'e', 'o', 'j', 4, 's', 'e', 'o', 'c', 4, 's', 'e', 'o', 'k', 4, 's', 'e', 'o', 't', 4, 's', 'e', 'o', 'p', 4, 's', 'e', 'o', 'h', 2, 's', 'e', 3, 's', 'e', 'g', 4, 's', 'e', 'g', 'g', 4, 's', 'e', 'g', 's', 3, 's', 'e', 'n', 4, 's', 'e', 'n', 'j', 4, 's', 'e', 'n', 'h', 3, 's', 'e', 'd', 3, 's', 'e', 'l', 4, 's', 'e', 'l', 'g', 4, 's', 'e', 'l', 'm', 4, 's', 'e', 'l', 'b', 4, 's', 'e', 'l', 's', 4, 's', 'e', 'l', 't', 4, 's', 'e', 'l', 'p', 4, 's', 'e', 'l', 'h', 3, 's', 'e', 'm', 3, 's', 'e', 'b', 4, 's', 'e', 'b', 's', 3, 's', 'e', 's', 4, 's', 'e', 's', 's', 4, 's', 'e', 'n', 'g', 3, 's', 'e', 'j', 3, 's', 'e', 'c', 3, 's', 'e', 'k', 3, 's', 'e', 't', 3, 's', 'e', 'p', 3, 's', 'e', 'h', 4, 's', 'y', 'e', 'o', 5, 's', 'y', 'e', 'o', 'g', 6, 's', 'y', 'e', 'o', 'g', 'g', 6, 's', 'y', 'e', 'o', 'g', 's', 5, 's', 'y', 'e', 'o', 'n', 6, 's', 'y', 'e', 'o', 'n', 'j', 6, 's', 'y', 'e', 'o', 'n', 'h', 5, 's', 'y', 'e', 'o', 'd', 5, 's', 'y', 'e', 'o', 'l', 6, 's', 'y', 'e', 'o', 'l', 'g', 6, 's', 'y', 'e', 'o', 'l', 'm', 6, 's', 'y', 'e', 'o', 'l', 'b', 6, 's', 'y', 'e', 'o', 'l', 's', 6, 's', 'y', 'e', 'o', 'l', 't', 6, 's', 'y', 'e', 'o', 'l', 'p', 6, 's', 'y', 'e', 'o', 'l', 'h', 5, 's', 'y', 'e', 'o', 'm', 5, 's', 'y', 'e', 'o', 'b', 6, 's', 'y', 'e', 'o', 'b', 's', 5, 's', 'y', 'e', 'o', 's', 6, 's', 'y', 'e', 'o', 's', 's', 6, 's', 'y', 'e', 'o', 'n', 'g', 5, 's', 'y', 'e', 'o', 'j', 5, 's', 'y', 'e', 'o', 'c', 5, 's', 'y', 'e', 'o', 'k', 5, 's', 'y', 'e', 'o', 't', 5, 's', 'y', 'e', 'o', 'p', 5, 's', 'y', 'e', 'o', 'h', 3, 's', 'y', 'e', 4, 's', 'y', 'e', 'g', 5, 's', 'y', 'e', 'g', 'g', 5, 's', 'y', 'e', 'g', 's', 4, 's', 'y', 'e', 'n', 5, 's', 'y', 'e', 'n', 'j', 5, 's', 'y', 'e', 'n', 'h', 4, 's', 'y', 'e', 'd', 4, 's', 'y', 'e', 'l', 5, 's', 'y', 'e', 'l', 'g', 5, 's', 'y', 'e', 'l', 'm', 5, 's', 'y', 'e', 'l', 'b', 5, 's', 'y', 'e', 'l', 's', 5, 's', 'y', 'e', 'l', 't', 5, 's', 'y', 'e', 'l', 'p', 5, 's', 'y', 'e', 'l', 'h', 4, 's', 'y', 'e', 'm', 4, 's', 'y', 'e', 'b', 5, 's', 'y', 'e', 'b', 's', 4, 's', 'y', 'e', 's', 5, 's', 'y', 'e', 's', 's', 5, 's', 'y', 'e', 'n', 'g', 4, 's', 'y', 'e', 'j', 4, 's', 'y', 'e', 'c', 4, 's', 'y', 'e', 'k', 4, 's', 'y', 'e', 't', 4, 's', 'y', 'e', 'p', 4, 's', 'y', 'e', 'h', 2, 's', 'o', 3, 's', 'o', 'g', 4, 's', 'o', 'g', 'g', 4, 's', 'o', 'g', 's', 3, 's', 'o', 'n', 4, 's', 'o', 'n', 'j', 4, 's', 'o', 'n', 'h', 3, 's', 'o', 'd', 3, 's', 'o', 'l', 4, 's', 'o', 'l', 'g', 4, 's', 'o', 'l', 'm', 4, 's', 'o', 'l', 'b', 4, 's', 'o', 'l', 's', 4, 's', 'o', 'l', 't', 4, 's', 'o', 'l', 'p', 4, 's', 'o', 'l', 'h', 3, 's', 'o', 'm', 3, 's', 'o', 'b', 4, 's', 'o', 'b', 's', 3, 's', 'o', 's', 4, 's', 'o', 's', 's', 4, 's', 'o', 'n', 'g', 3, 's', 'o', 'j', 3, 's', 'o', 'c', 3, 's', 'o', 'k', 3, 's', 'o', 't', 3, 's', 'o', 'p', 3, 's', 'o', 'h', 3, 's', 'w', 'a', 4, 's', 'w', 'a', 'g', 5, 's', 'w', 'a', 'g', 'g', 5, 's', 'w', 'a', 'g', 's', 4, 's', 'w', 'a', 'n', 5, 's', 'w', 'a', 'n', 'j', 5, 's', 'w', 'a', 'n', 'h', 4, 's', 'w', 'a', 'd', 4, 's', 'w', 'a', 'l', 5, 's', 'w', 'a', 'l', 'g', 5, 's', 'w', 'a', 'l', 'm', 5, 's', 'w', 'a', 'l', 'b', 5, 's', 'w', 'a', 'l', 's', 5, 's', 'w', 'a', 'l', 't', 5, 's', 'w', 'a', 'l', 'p', 5, 's', 'w', 'a', 'l', 'h', 4, 's', 'w', 'a', 'm', 4, 's', 'w', 'a', 'b', 5, 's', 'w', 'a', 'b', 's', 4, 's', 'w', 'a', 's', 5, 's', 'w', 'a', 's', 's', 5, 's', 'w', 'a', 'n', 'g', 4, 's', 'w', 'a', 'j', 4, 's', 'w', 'a', 'c', 4, 's', 'w', 'a', 'k', 4, 's', 'w', 'a', 't', 4, 's', 'w', 'a', 'p', 4, 's', 'w', 'a', 'h', 4, 's', 'w', 'a', 'e', 5, 's', 'w', 'a', 'e', 'g', 6, 's', 'w', 'a', 'e', 'g', 'g', 6, 's', 'w', 'a', 'e', 'g', 's', 5, 's', 'w', 'a', 'e', 'n', 6, 's', 'w', 'a', 'e', 'n', 'j', 6, 's', 'w', 'a', 'e', 'n', 'h', 5, 's', 'w', 'a', 'e', 'd', 5, 's', 'w', 'a', 'e', 'l', 6, 's', 'w', 'a', 'e', 'l', 'g', 6, 's', 'w', 'a', 'e', 'l', 'm', 6, 's', 'w', 'a', 'e', 'l', 'b', 6, 's', 'w', 'a', 'e', 'l', 's', 6, 's', 'w', 'a', 'e', 'l', 't', 6, 's', 'w', 'a', 'e', 'l', 'p', 6, 's', 'w', 'a', 'e', 'l', 'h', 5, 's', 'w', 'a', 'e', 'm', 5, 's', 'w', 'a', 'e', 'b', 6, 's', 'w', 'a', 'e', 'b', 's', 5, 's', 'w', 'a', 'e', 's', 6, 's', 'w', 'a', 'e', 's', 's', 6, 's', 'w', 'a', 'e', 'n', 'g', 5, 's', 'w', 'a', 'e', 'j', 5, 's', 'w', 'a', 'e', 'c', 5, 's', 'w', 'a', 'e', 'k', 5, 's', 'w', 'a', 'e', 't', 5, 's', 'w', 'a', 'e', 'p', 5, 's', 'w', 'a', 'e', 'h', 3, 's', 'o', 'e', 4, 's', 'o', 'e', 'g', 5, 's', 'o', 'e', 'g', 'g', 5, 's', 'o', 'e', 'g', 's', 4, 's', 'o', 'e', 'n', 5, 's', 'o', 'e', 'n', 'j', 5, 's', 'o', 'e', 'n', 'h', 4, 's', 'o', 'e', 'd', 4, 's', 'o', 'e', 'l', 5, 's', 'o', 'e', 'l', 'g', 5, 's', 'o', 'e', 'l', 'm', 5, 's', 'o', 'e', 'l', 'b', 5, 's', 'o', 'e', 'l', 's', 5, 's', 'o', 'e', 'l', 't', 5, 's', 'o', 'e', 'l', 'p', 5, 's', 'o', 'e', 'l', 'h', 4, 's', 'o', 'e', 'm', 4, 's', 'o', 'e', 'b', 5, 's', 'o', 'e', 'b', 's', 4, 's', 'o', 'e', 's', 5, 's', 'o', 'e', 's', 's', 5, 's', 'o', 'e', 'n', 'g', 4, 's', 'o', 'e', 'j', 4, 's', 'o', 'e', 'c', 4, 's', 'o', 'e', 'k', 4, 's', 'o', 'e', 't', 4, 's', 'o', 'e', 'p', 4, 's', 'o', 'e', 'h', 3, 's', 'y', 'o', 4, 's', 'y', 'o', 'g', 5, 's', 'y', 'o', 'g', 'g', 5, 's', 'y', 'o', 'g', 's', 4, 's', 'y', 'o', 'n', 5, 's', 'y', 'o', 'n', 'j', 5, 's', 'y', 'o', 'n', 'h', 4, 's', 'y', 'o', 'd', 4, 's', 'y', 'o', 'l', 5, 's', 'y', 'o', 'l', 'g', 5, 's', 'y', 'o', 'l', 'm', 5, 's', 'y', 'o', 'l', 'b', 5, 's', 'y', 'o', 'l', 's', 5, 's', 'y', 'o', 'l', 't', 5, 's', 'y', 'o', 'l', 'p', 5, 's', 'y', 'o', 'l', 'h', 4, 's', 'y', 'o', 'm', 4, 's', 'y', 'o', 'b', 5, 's', 'y', 'o', 'b', 's', 4, 's', 'y', 'o', 's', 5, 's', 'y', 'o', 's', 's', 5, 's', 'y', 'o', 'n', 'g', 4, 's', 'y', 'o', 'j', 4, 's', 'y', 'o', 'c', 4, 's', 'y', 'o', 'k', 4, 's', 'y', 'o', 't', 4, 's', 'y', 'o', 'p', 4, 's', 'y', 'o', 'h', 2, 's', 'u', 3, 's', 'u', 'g', 4, 's', 'u', 'g', 'g', 4, 's', 'u', 'g', 's', 3, 's', 'u', 'n', 4, 's', 'u', 'n', 'j', 4, 's', 'u', 'n', 'h', 3, 's', 'u', 'd', 3, 's', 'u', 'l', 4, 's', 'u', 'l', 'g', 4, 's', 'u', 'l', 'm', 4, 's', 'u', 'l', 'b', 4, 's', 'u', 'l', 's', 4, 's', 'u', 'l', 't', 4, 's', 'u', 'l', 'p', 4, 's', 'u', 'l', 'h', 3, 's', 'u', 'm', 3, 's', 'u', 'b', 4, 's', 'u', 'b', 's', 3, 's', 'u', 's', 4, 's', 'u', 's', 's', 4, 's', 'u', 'n', 'g', 3, 's', 'u', 'j', 3, 's', 'u', 'c', 3, 's', 'u', 'k', 3, 's', 'u', 't', 3, 's', 'u', 'p', 3, 's', 'u', 'h', 4, 's', 'w', 'e', 'o', 5, 's', 'w', 'e', 'o', 'g', 6, 's', 'w', 'e', 'o', 'g', 'g', 6, 's', 'w', 'e', 'o', 'g', 's', 5, 's', 'w', 'e', 'o', 'n', 6, 's', 'w', 'e', 'o', 'n', 'j', 6, 's', 'w', 'e', 'o', 'n', 'h', 5, 's', 'w', 'e', 'o', 'd', 5, 's', 'w', 'e', 'o', 'l', 6, 's', 'w', 'e', 'o', 'l', 'g', 6, 's', 'w', 'e', 'o', 'l', 'm', 6, 's', 'w', 'e', 'o', 'l', 'b', 6, 's', 'w', 'e', 'o', 'l', 's', 6, 's', 'w', 'e', 'o', 'l', 't', 6, 's', 'w', 'e', 'o', 'l', 'p', 6, 's', 'w', 'e', 'o', 'l', 'h', 5, 's', 'w', 'e', 'o', 'm', 5, 's', 'w', 'e', 'o', 'b', 6, 's', 'w', 'e', 'o', 'b', 's', 5, 's', 'w', 'e', 'o', 's', 6, 's', 'w', 'e', 'o', 's', 's', 6, 's', 'w', 'e', 'o', 'n', 'g', 5, 's', 'w', 'e', 'o', 'j', 5, 's', 'w', 'e', 'o', 'c', 5, 's', 'w', 'e', 'o', 'k', 5, 's', 'w', 'e', 'o', 't', 5, 's', 'w', 'e', 'o', 'p', 5, 's', 'w', 'e', 'o', 'h', 3, 's', 'w', 'e', 4, 's', 'w', 'e', 'g', 5, 's', 'w', 'e', 'g', 'g', 5, 's', 'w', 'e', 'g', 's', 4, 's', 'w', 'e', 'n', 5, 's', 'w', 'e', 'n', 'j', 5, 's', 'w', 'e', 'n', 'h', 4, 's', 'w', 'e', 'd', 4, 's', 'w', 'e', 'l', 5, 's', 'w', 'e', 'l', 'g', 5, 's', 'w', 'e', 'l', 'm', 5, 's', 'w', 'e', 'l', 'b', 5, 's', 'w', 'e', 'l', 's', 5, 's', 'w', 'e', 'l', 't', 5, 's', 'w', 'e', 'l', 'p', 5, 's', 'w', 'e', 'l', 'h', 4, 's', 'w', 'e', 'm', 4, 's', 'w', 'e', 'b', 5, 's', 'w', 'e', 'b', 's', 4, 's', 'w', 'e', 's', 5, 's', 'w', 'e', 's', 's', 5, 's', 'w', 'e', 'n', 'g', 4, 's', 'w', 'e', 'j', 4, 's', 'w', 'e', 'c', 4, 's', 'w', 'e', 'k', 4, 's', 'w', 'e', 't', 4, 's', 'w', 'e', 'p', 4, 's', 'w', 'e', 'h', 3, 's', 'w', 'i', 4, 's', 'w', 'i', 'g', 5, 's', 'w', 'i', 'g', 'g', 5, 's', 'w', 'i', 'g', 's', 4, 's', 'w', 'i', 'n', 5, 's', 'w', 'i', 'n', 'j', 5, 's', 'w', 'i', 'n', 'h', 4, 's', 'w', 'i', 'd', 4, 's', 'w', 'i', 'l', 5, 's', 'w', 'i', 'l', 'g', 5, 's', 'w', 'i', 'l', 'm', 5, 's', 'w', 'i', 'l', 'b', 5, 's', 'w', 'i', 'l', 's', 5, 's', 'w', 'i', 'l', 't', 5, 's', 'w', 'i', 'l', 'p', 5, 's', 'w', 'i', 'l', 'h', 4, 's', 'w', 'i', 'm', 4, 's', 'w', 'i', 'b', 5, 's', 'w', 'i', 'b', 's', 4, 's', 'w', 'i', 's', 5, 's', 'w', 'i', 's', 's', 5, 's', 'w', 'i', 'n', 'g', 4, 's', 'w', 'i', 'j', 4, 's', 'w', 'i', 'c', 4, 's', 'w', 'i', 'k', 4, 's', 'w', 'i', 't', 4, 's', 'w', 'i', 'p', 4, 's', 'w', 'i', 'h', 3, 's', 'y', 'u', 4, 's', 'y', 'u', 'g', 5, 's', 'y', 'u', 'g', 'g', 5, 's', 'y', 'u', 'g', 's', 4, 's', 'y', 'u', 'n', 5, 's', 'y', 'u', 'n', 'j', 5, 's', 'y', 'u', 'n', 'h', 4, 's', 'y', 'u', 'd', 4, 's', 'y', 'u', 'l', 5, 's', 'y', 'u', 'l', 'g', 5, 's', 'y', 'u', 'l', 'm', 5, 's', 'y', 'u', 'l', 'b', 5, 's', 'y', 'u', 'l', 's', 5, 's', 'y', 'u', 'l', 't', 5, 's', 'y', 'u', 'l', 'p', 5, 's', 'y', 'u', 'l', 'h', 4, 's', 'y', 'u', 'm', 4, 's', 'y', 'u', 'b', 5, 's', 'y', 'u', 'b', 's', 4, 's', 'y', 'u', 's', 5, 's', 'y', 'u', 's', 's', 5, 's', 'y', 'u', 'n', 'g', 4, 's', 'y', 'u', 'j', 4, 's', 'y', 'u', 'c', 4, 's', 'y', 'u', 'k', 4, 's', 'y', 'u', 't', 4, 's', 'y', 'u', 'p', 4, 's', 'y', 'u', 'h', 3, 's', 'e', 'u', 4, 's', 'e', 'u', 'g', 5, 's', 'e', 'u', 'g', 'g', 5, 's', 'e', 'u', 'g', 's', 4, 's', 'e', 'u', 'n', 5, 's', 'e', 'u', 'n', 'j', 5, 's', 'e', 'u', 'n', 'h', 4, 's', 'e', 'u', 'd', 4, 's', 'e', 'u', 'l', 5, 's', 'e', 'u', 'l', 'g', 5, 's', 'e', 'u', 'l', 'm', 5, 's', 'e', 'u', 'l', 'b', 5, 's', 'e', 'u', 'l', 's', 5, 's', 'e', 'u', 'l', 't', 5, 's', 'e', 'u', 'l', 'p', 5, 's', 'e', 'u', 'l', 'h', 4, 's', 'e', 'u', 'm', 4, 's', 'e', 'u', 'b', 5, 's', 'e', 'u', 'b', 's', 4, 's', 'e', 'u', 's', 5, 's', 'e', 'u', 's', 's', 5, 's', 'e', 'u', 'n', 'g', 4, 's', 'e', 'u', 'j', 4, 's', 'e', 'u', 'c', 4, 's', 'e', 'u', 'k', 4, 's', 'e', 'u', 't', 4, 's', 'e', 'u', 'p', 4, 's', 'e', 'u', 'h', 3, 's', 'y', 'i', 4, 's', 'y', 'i', 'g', 5, 's', 'y', 'i', 'g', 'g', 5, 's', 'y', 'i', 'g', 's', 4, 's', 'y', 'i', 'n', 5, 's', 'y', 'i', 'n', 'j', 5, 's', 'y', 'i', 'n', 'h', 4, 's', 'y', 'i', 'd', 4, 's', 'y', 'i', 'l', 5, 's', 'y', 'i', 'l', 'g', 5, 's', 'y', 'i', 'l', 'm', 5, 's', 'y', 'i', 'l', 'b', 5, 's', 'y', 'i', 'l', 's', 5, 's', 'y', 'i', 'l', 't', 5, 's', 'y', 'i', 'l', 'p', 5, 's', 'y', 'i', 'l', 'h', 4, 's', 'y', 'i', 'm', 4, 's', 'y', 'i', 'b', 5, 's', 'y', 'i', 'b', 's', 4, 's', 'y', 'i', 's', 5, 's', 'y', 'i', 's', 's', 5, 's', 'y', 'i', 'n', 'g', 4, 's', 'y', 'i', 'j', 4, 's', 'y', 'i', 'c', 4, 's', 'y', 'i', 'k', 4, 's', 'y', 'i', 't', 4, 's', 'y', 'i', 'p', 4, 's', 'y', 'i', 'h', 2, 's', 'i', 3, 's', 'i', 'g', 4, 's', 'i', 'g', 'g', 4, 's', 'i', 'g', 's', 3, 's', 'i', 'n', 4, 's', 'i', 'n', 'j', 4, 's', 'i', 'n', 'h', 3, 's', 'i', 'd', 3, 's', 'i', 'l', 4, 's', 'i', 'l', 'g', 4, 's', 'i', 'l', 'm', 4, 's', 'i', 'l', 'b', 4, 's', 'i', 'l', 's', 4, 's', 'i', 'l', 't', 4, 's', 'i', 'l', 'p', 4, 's', 'i', 'l', 'h', 3, 's', 'i', 'm', 3, 's', 'i', 'b', 4, 's', 'i', 'b', 's', 3, 's', 'i', 's', 4, 's', 'i', 's', 's', 4, 's', 'i', 'n', 'g', 3, 's', 'i', 'j', 3, 's', 'i', 'c', 3, 's', 'i', 'k', 3, 's', 'i', 't', 3, 's', 'i', 'p', 3, 's', 'i', 'h', 3, 's', 's', 'a', 4, 's', 's', 'a', 'g', 5, 's', 's', 'a', 'g', 'g', 5, 's', 's', 'a', 'g', 's', 4, 's', 's', 'a', 'n', 5, 's', 's', 'a', 'n', 'j', 5, 's', 's', 'a', 'n', 'h', 4, 's', 's', 'a', 'd', 4, 's', 's', 'a', 'l', 5, 's', 's', 'a', 'l', 'g', 5, 's', 's', 'a', 'l', 'm', 5, 's', 's', 'a', 'l', 'b', 5, 's', 's', 'a', 'l', 's', 5, 's', 's', 'a', 'l', 't', 5, 's', 's', 'a', 'l', 'p', 5, 's', 's', 'a', 'l', 'h', 4, 's', 's', 'a', 'm', 4, 's', 's', 'a', 'b', 5, 's', 's', 'a', 'b', 's', 4, 's', 's', 'a', 's', 5, 's', 's', 'a', 's', 's', 5, 's', 's', 'a', 'n', 'g', 4, 's', 's', 'a', 'j', 4, 's', 's', 'a', 'c', 4, 's', 's', 'a', 'k', 4, 's', 's', 'a', 't', 4, 's', 's', 'a', 'p', 4, 's', 's', 'a', 'h', 4, 's', 's', 'a', 'e', 5, 's', 's', 'a', 'e', 'g', 6, 's', 's', 'a', 'e', 'g', 'g', 6, 's', 's', 'a', 'e', 'g', 's', 5, 's', 's', 'a', 'e', 'n', 6, 's', 's', 'a', 'e', 'n', 'j', 6, 's', 's', 'a', 'e', 'n', 'h', 5, 's', 's', 'a', 'e', 'd', 5, 's', 's', 'a', 'e', 'l', 6, 's', 's', 'a', 'e', 'l', 'g', 6, 's', 's', 'a', 'e', 'l', 'm', 6, 's', 's', 'a', 'e', 'l', 'b', 6, 's', 's', 'a', 'e', 'l', 's', 6, 's', 's', 'a', 'e', 'l', 't', 6, 's', 's', 'a', 'e', 'l', 'p', 6, 's', 's', 'a', 'e', 'l', 'h', 5, 's', 's', 'a', 'e', 'm', 5, 's', 's', 'a', 'e', 'b', 6, 's', 's', 'a', 'e', 'b', 's', 5, 's', 's', 'a', 'e', 's', 6, 's', 's', 'a', 'e', 's', 's', 6, 's', 's', 'a', 'e', 'n', 'g', 5, 's', 's', 'a', 'e', 'j', 5, 's', 's', 'a', 'e', 'c', 5, 's', 's', 'a', 'e', 'k', 5, 's', 's', 'a', 'e', 't', 5, 's', 's', 'a', 'e', 'p', 5, 's', 's', 'a', 'e', 'h', 4, 's', 's', 'y', 'a', 5, 's', 's', 'y', 'a', 'g', 6, 's', 's', 'y', 'a', 'g', 'g', 6, 's', 's', 'y', 'a', 'g', 's', 5, 's', 's', 'y', 'a', 'n', 6, 's', 's', 'y', 'a', 'n', 'j', 6, 's', 's', 'y', 'a', 'n', 'h', 5, 's', 's', 'y', 'a', 'd', 5, 's', 's', 'y', 'a', 'l', 6, 's', 's', 'y', 'a', 'l', 'g', 6, 's', 's', 'y', 'a', 'l', 'm', 6, 's', 's', 'y', 'a', 'l', 'b', 6, 's', 's', 'y', 'a', 'l', 's', 6, 's', 's', 'y', 'a', 'l', 't', 6, 's', 's', 'y', 'a', 'l', 'p', 6, 's', 's', 'y', 'a', 'l', 'h', 5, 's', 's', 'y', 'a', 'm', 5, 's', 's', 'y', 'a', 'b', 6, 's', 's', 'y', 'a', 'b', 's', 5, 's', 's', 'y', 'a', 's', 6, 's', 's', 'y', 'a', 's', 's', 6, 's', 's', 'y', 'a', 'n', 'g', 5, 's', 's', 'y', 'a', 'j', 5, 's', 's', 'y', 'a', 'c', 5, 's', 's', 'y', 'a', 'k', 5, 's', 's', 'y', 'a', 't', 5, 's', 's', 'y', 'a', 'p', 5, 's', 's', 'y', 'a', 'h', 5, 's', 's', 'y', 'a', 'e', 6, 's', 's', 'y', 'a', 'e', 'g', 7, 's', 's', 'y', 'a', 'e', 'g', 'g', 7, 's', 's', 'y', 'a', 'e', 'g', 's', 6, 's', 's', 'y', 'a', 'e', 'n', 7, 's', 's', 'y', 'a', 'e', 'n', 'j', 7, 's', 's', 'y', 'a', 'e', 'n', 'h', 6, 's', 's', 'y', 'a', 'e', 'd', 6, 's', 's', 'y', 'a', 'e', 'l', 7, 's', 's', 'y', 'a', 'e', 'l', 'g', 7, 's', 's', 'y', 'a', 'e', 'l', 'm', 7, 's', 's', 'y', 'a', 'e', 'l', 'b', 7, 's', 's', 'y', 'a', 'e', 'l', 's', 7, 's', 's', 'y', 'a', 'e', 'l', 't', 7, 's', 's', 'y', 'a', 'e', 'l', 'p', 7, 's', 's', 'y', 'a', 'e', 'l', 'h', 6, 's', 's', 'y', 'a', 'e', 'm', 6, 's', 's', 'y', 'a', 'e', 'b', 7, 's', 's', 'y', 'a', 'e', 'b', 's', 6, 's', 's', 'y', 'a', 'e', 's', 7, 's', 's', 'y', 'a', 'e', 's', 's', 7, 's', 's', 'y', 'a', 'e', 'n', 'g', 6, 's', 's', 'y', 'a', 'e', 'j', 6, 's', 's', 'y', 'a', 'e', 'c', 6, 's', 's', 'y', 'a', 'e', 'k', 6, 's', 's', 'y', 'a', 'e', 't', 6, 's', 's', 'y', 'a', 'e', 'p', 6, 's', 's', 'y', 'a', 'e', 'h', 4, 's', 's', 'e', 'o', 5, 's', 's', 'e', 'o', 'g', 6, 's', 's', 'e', 'o', 'g', 'g', 6, 's', 's', 'e', 'o', 'g', 's', 5, 's', 's', 'e', 'o', 'n', 6, 's', 's', 'e', 'o', 'n', 'j', 6, 's', 's', 'e', 'o', 'n', 'h', 5, 's', 's', 'e', 'o', 'd', 5, 's', 's', 'e', 'o', 'l', 6, 's', 's', 'e', 'o', 'l', 'g', 6, 's', 's', 'e', 'o', 'l', 'm', 6, 's', 's', 'e', 'o', 'l', 'b', 6, 's', 's', 'e', 'o', 'l', 's', 6, 's', 's', 'e', 'o', 'l', 't', 6, 's', 's', 'e', 'o', 'l', 'p', 6, 's', 's', 'e', 'o', 'l', 'h', 5, 's', 's', 'e', 'o', 'm', 5, 's', 's', 'e', 'o', 'b', 6, 's', 's', 'e', 'o', 'b', 's', 5, 's', 's', 'e', 'o', 's', 6, 's', 's', 'e', 'o', 's', 's', 6, 's', 's', 'e', 'o', 'n', 'g', 5, 's', 's', 'e', 'o', 'j', 5, 's', 's', 'e', 'o', 'c', 5, 's', 's', 'e', 'o', 'k', 5, 's', 's', 'e', 'o', 't', 5, 's', 's', 'e', 'o', 'p', 5, 's', 's', 'e', 'o', 'h', 3, 's', 's', 'e', 4, 's', 's', 'e', 'g', 5, 's', 's', 'e', 'g', 'g', 5, 's', 's', 'e', 'g', 's', 4, 's', 's', 'e', 'n', 5, 's', 's', 'e', 'n', 'j', 5, 's', 's', 'e', 'n', 'h', 4, 's', 's', 'e', 'd', 4, 's', 's', 'e', 'l', 5, 's', 's', 'e', 'l', 'g', 5, 's', 's', 'e', 'l', 'm', 5, 's', 's', 'e', 'l', 'b', 5, 's', 's', 'e', 'l', 's', 5, 's', 's', 'e', 'l', 't', 5, 's', 's', 'e', 'l', 'p', 5, 's', 's', 'e', 'l', 'h', 4, 's', 's', 'e', 'm', 4, 's', 's', 'e', 'b', 5, 's', 's', 'e', 'b', 's', 4, 's', 's', 'e', 's', 5, 's', 's', 'e', 's', 's', 5, 's', 's', 'e', 'n', 'g', 4, 's', 's', 'e', 'j', 4, 's', 's', 'e', 'c', 4, 's', 's', 'e', 'k', 4, 's', 's', 'e', 't', 4, 's', 's', 'e', 'p', 4, 's', 's', 'e', 'h', 5, 's', 's', 'y', 'e', 'o', 6, 's', 's', 'y', 'e', 'o', 'g', 7, 's', 's', 'y', 'e', 'o', 'g', 'g', 7, 's', 's', 'y', 'e', 'o', 'g', 's', 6, 's', 's', 'y', 'e', 'o', 'n', 7, 's', 's', 'y', 'e', 'o', 'n', 'j', 7, 's', 's', 'y', 'e', 'o', 'n', 'h', 6, 's', 's', 'y', 'e', 'o', 'd', 6, 's', 's', 'y', 'e', 'o', 'l', 7, 's', 's', 'y', 'e', 'o', 'l', 'g', 7, 's', 's', 'y', 'e', 'o', 'l', 'm', 7, 's', 's', 'y', 'e', 'o', 'l', 'b', 7, 's', 's', 'y', 'e', 'o', 'l', 's', 7, 's', 's', 'y', 'e', 'o', 'l', 't', 7, 's', 's', 'y', 'e', 'o', 'l', 'p', 7, 's', 's', 'y', 'e', 'o', 'l', 'h', 6, 's', 's', 'y', 'e', 'o', 'm', 6, 's', 's', 'y', 'e', 'o', 'b', 7, 's', 's', 'y', 'e', 'o', 'b', 's', 6, 's', 's', 'y', 'e', 'o', 's', 7, 's', 's', 'y', 'e', 'o', 's', 's', 7, 's', 's', 'y', 'e', 'o', 'n', 'g', 6, 's', 's', 'y', 'e', 'o', 'j', 6, 's', 's', 'y', 'e', 'o', 'c', 6, 's', 's', 'y', 'e', 'o', 'k', 6, 's', 's', 'y', 'e', 'o', 't', 6, 's', 's', 'y', 'e', 'o', 'p', 6, 's', 's', 'y', 'e', 'o', 'h', 4, 's', 's', 'y', 'e', 5, 's', 's', 'y', 'e', 'g', 6, 's', 's', 'y', 'e', 'g', 'g', 6, 's', 's', 'y', 'e', 'g', 's', 5, 's', 's', 'y', 'e', 'n', 6, 's', 's', 'y', 'e', 'n', 'j', 6, 's', 's', 'y', 'e', 'n', 'h', 5, 's', 's', 'y', 'e', 'd', 5, 's', 's', 'y', 'e', 'l', 6, 's', 's', 'y', 'e', 'l', 'g', 6, 's', 's', 'y', 'e', 'l', 'm', 6, 's', 's', 'y', 'e', 'l', 'b', 6, 's', 's', 'y', 'e', 'l', 's', 6, 's', 's', 'y', 'e', 'l', 't', 6, 's', 's', 'y', 'e', 'l', 'p', 6, 's', 's', 'y', 'e', 'l', 'h', 5, 's', 's', 'y', 'e', 'm', 5, 's', 's', 'y', 'e', 'b', 6, 's', 's', 'y', 'e', 'b', 's', 5, 's', 's', 'y', 'e', 's', 6, 's', 's', 'y', 'e', 's', 's', 6, 's', 's', 'y', 'e', 'n', 'g', 5, 's', 's', 'y', 'e', 'j', 5, 's', 's', 'y', 'e', 'c', 5, 's', 's', 'y', 'e', 'k', 5, 's', 's', 'y', 'e', 't', 5, 's', 's', 'y', 'e', 'p', 5, 's', 's', 'y', 'e', 'h', 3, 's', 's', 'o', 4, 's', 's', 'o', 'g', 5, 's', 's', 'o', 'g', 'g', 5, 's', 's', 'o', 'g', 's', 4, 's', 's', 'o', 'n', 5, 's', 's', 'o', 'n', 'j', 5, 's', 's', 'o', 'n', 'h', 4, 's', 's', 'o', 'd', 4, 's', 's', 'o', 'l', 5, 's', 's', 'o', 'l', 'g', 5, 's', 's', 'o', 'l', 'm', 5, 's', 's', 'o', 'l', 'b', 5, 's', 's', 'o', 'l', 's', 5, 's', 's', 'o', 'l', 't', 5, 's', 's', 'o', 'l', 'p', 5, 's', 's', 'o', 'l', 'h', 4, 's', 's', 'o', 'm', 4, 's', 's', 'o', 'b', 5, 's', 's', 'o', 'b', 's', 4, 's', 's', 'o', 's', 5, 's', 's', 'o', 's', 's', 5, 's', 's', 'o', 'n', 'g', 4, 's', 's', 'o', 'j', 4, 's', 's', 'o', 'c', 4, 's', 's', 'o', 'k', 4, 's', 's', 'o', 't', 4, 's', 's', 'o', 'p', 4, 's', 's', 'o', 'h', 4, 's', 's', 'w', 'a', 5, 's', 's', 'w', 'a', 'g', 6, 's', 's', 'w', 'a', 'g', 'g', 6, 's', 's', 'w', 'a', 'g', 's', 5, 's', 's', 'w', 'a', 'n', 6, 's', 's', 'w', 'a', 'n', 'j', 6, 's', 's', 'w', 'a', 'n', 'h', 5, 's', 's', 'w', 'a', 'd', 5, 's', 's', 'w', 'a', 'l', 6, 's', 's', 'w', 'a', 'l', 'g', 6, 's', 's', 'w', 'a', 'l', 'm', 6, 's', 's', 'w', 'a', 'l', 'b', 6, 's', 's', 'w', 'a', 'l', 's', 6, 's', 's', 'w', 'a', 'l', 't', 6, 's', 's', 'w', 'a', 'l', 'p', 6, 's', 's', 'w', 'a', 'l', 'h', 5, 's', 's', 'w', 'a', 'm', 5, 's', 's', 'w', 'a', 'b', 6, 's', 's', 'w', 'a', 'b', 's', 5, 's', 's', 'w', 'a', 's', 6, 's', 's', 'w', 'a', 's', 's', 6, 's', 's', 'w', 'a', 'n', 'g', 5, 's', 's', 'w', 'a', 'j', 5, 's', 's', 'w', 'a', 'c', 5, 's', 's', 'w', 'a', 'k', 5, 's', 's', 'w', 'a', 't', 5, 's', 's', 'w', 'a', 'p', 5, 's', 's', 'w', 'a', 'h', 5, 's', 's', 'w', 'a', 'e', 6, 's', 's', 'w', 'a', 'e', 'g', 7, 's', 's', 'w', 'a', 'e', 'g', 'g', 7, 's', 's', 'w', 'a', 'e', 'g', 's', 6, 's', 's', 'w', 'a', 'e', 'n', 7, 's', 's', 'w', 'a', 'e', 'n', 'j', 7, 's', 's', 'w', 'a', 'e', 'n', 'h', 6, 's', 's', 'w', 'a', 'e', 'd', 6, 's', 's', 'w', 'a', 'e', 'l', 7, 's', 's', 'w', 'a', 'e', 'l', 'g', 7, 's', 's', 'w', 'a', 'e', 'l', 'm', 7, 's', 's', 'w', 'a', 'e', 'l', 'b', 7, 's', 's', 'w', 'a', 'e', 'l', 's', 7, 's', 's', 'w', 'a', 'e', 'l', 't', 7, 's', 's', 'w', 'a', 'e', 'l', 'p', 7, 's', 's', 'w', 'a', 'e', 'l', 'h', 6, 's', 's', 'w', 'a', 'e', 'm', 6, 's', 's', 'w', 'a', 'e', 'b', 7, 's', 's', 'w', 'a', 'e', 'b', 's', 6, 's', 's', 'w', 'a', 'e', 's', 7, 's', 's', 'w', 'a', 'e', 's', 's', 7, 's', 's', 'w', 'a', 'e', 'n', 'g', 6, 's', 's', 'w', 'a', 'e', 'j', 6, 's', 's', 'w', 'a', 'e', 'c', 6, 's', 's', 'w', 'a', 'e', 'k', 6, 's', 's', 'w', 'a', 'e', 't', 6, 's', 's', 'w', 'a', 'e', 'p', 6, 's', 's', 'w', 'a', 'e', 'h', 4, 's', 's', 'o', 'e', 5, 's', 's', 'o', 'e', 'g', 6, 's', 's', 'o', 'e', 'g', 'g', 6, 's', 's', 'o', 'e', 'g', 's', 5, 's', 's', 'o', 'e', 'n', 6, 's', 's', 'o', 'e', 'n', 'j', 6, 's', 's', 'o', 'e', 'n', 'h', 5, 's', 's', 'o', 'e', 'd', 5, 's', 's', 'o', 'e', 'l', 6, 's', 's', 'o', 'e', 'l', 'g', 6, 's', 's', 'o', 'e', 'l', 'm', 6, 's', 's', 'o', 'e', 'l', 'b', 6, 's', 's', 'o', 'e', 'l', 's', 6, 's', 's', 'o', 'e', 'l', 't', 6, 's', 's', 'o', 'e', 'l', 'p', 6, 's', 's', 'o', 'e', 'l', 'h', 5, 's', 's', 'o', 'e', 'm', 5, 's', 's', 'o', 'e', 'b', 6, 's', 's', 'o', 'e', 'b', 's', 5, 's', 's', 'o', 'e', 's', 6, 's', 's', 'o', 'e', 's', 's', 6, 's', 's', 'o', 'e', 'n', 'g', 5, 's', 's', 'o', 'e', 'j', 5, 's', 's', 'o', 'e', 'c', 5, 's', 's', 'o', 'e', 'k', 5, 's', 's', 'o', 'e', 't', 5, 's', 's', 'o', 'e', 'p', 5, 's', 's', 'o', 'e', 'h', 4, 's', 's', 'y', 'o', 5, 's', 's', 'y', 'o', 'g', 6, 's', 's', 'y', 'o', 'g', 'g', 6, 's', 's', 'y', 'o', 'g', 's', 5, 's', 's', 'y', 'o', 'n', 6, 's', 's', 'y', 'o', 'n', 'j', 6, 's', 's', 'y', 'o', 'n', 'h', 5, 's', 's', 'y', 'o', 'd', 5, 's', 's', 'y', 'o', 'l', 6, 's', 's', 'y', 'o', 'l', 'g', 6, 's', 's', 'y', 'o', 'l', 'm', 6, 's', 's', 'y', 'o', 'l', 'b', 6, 's', 's', 'y', 'o', 'l', 's', 6, 's', 's', 'y', 'o', 'l', 't', 6, 's', 's', 'y', 'o', 'l', 'p', 6, 's', 's', 'y', 'o', 'l', 'h', 5, 's', 's', 'y', 'o', 'm', 5, 's', 's', 'y', 'o', 'b', 6, 's', 's', 'y', 'o', 'b', 's', 5, 's', 's', 'y', 'o', 's', 6, 's', 's', 'y', 'o', 's', 's', 6, 's', 's', 'y', 'o', 'n', 'g', 5, 's', 's', 'y', 'o', 'j', 5, 's', 's', 'y', 'o', 'c', 5, 's', 's', 'y', 'o', 'k', 5, 's', 's', 'y', 'o', 't', 5, 's', 's', 'y', 'o', 'p', 5, 's', 's', 'y', 'o', 'h', 3, 's', 's', 'u', 4, 's', 's', 'u', 'g', 5, 's', 's', 'u', 'g', 'g', 5, 's', 's', 'u', 'g', 's', 4, 's', 's', 'u', 'n', 5, 's', 's', 'u', 'n', 'j', 5, 's', 's', 'u', 'n', 'h', 4, 's', 's', 'u', 'd', 4, 's', 's', 'u', 'l', 5, 's', 's', 'u', 'l', 'g', 5, 's', 's', 'u', 'l', 'm', 5, 's', 's', 'u', 'l', 'b', 5, 's', 's', 'u', 'l', 's', 5, 's', 's', 'u', 'l', 't', 5, 's', 's', 'u', 'l', 'p', 5, 's', 's', 'u', 'l', 'h', 4, 's', 's', 'u', 'm', 4, 's', 's', 'u', 'b', 5, 's', 's', 'u', 'b', 's', 4, 's', 's', 'u', 's', 5, 's', 's', 'u', 's', 's', 5, 's', 's', 'u', 'n', 'g', 4, 's', 's', 'u', 'j', 4, 's', 's', 'u', 'c', 4, 's', 's', 'u', 'k', 4, 's', 's', 'u', 't', 4, 's', 's', 'u', 'p', 4, 's', 's', 'u', 'h', 5, 's', 's', 'w', 'e', 'o', 6, 's', 's', 'w', 'e', 'o', 'g', 7, 's', 's', 'w', 'e', 'o', 'g', 'g', 7, 's', 's', 'w', 'e', 'o', 'g', 's', 6, 's', 's', 'w', 'e', 'o', 'n', 7, 's', 's', 'w', 'e', 'o', 'n', 'j', 7, 's', 's', 'w', 'e', 'o', 'n', 'h', 6, 's', 's', 'w', 'e', 'o', 'd', 6, 's', 's', 'w', 'e', 'o', 'l', 7, 's', 's', 'w', 'e', 'o', 'l', 'g', 7, 's', 's', 'w', 'e', 'o', 'l', 'm', 7, 's', 's', 'w', 'e', 'o', 'l', 'b', 7, 's', 's', 'w', 'e', 'o', 'l', 's', 7, 's', 's', 'w', 'e', 'o', 'l', 't', 7, 's', 's', 'w', 'e', 'o', 'l', 'p', 7, 's', 's', 'w', 'e', 'o', 'l', 'h', 6, 's', 's', 'w', 'e', 'o', 'm', 6, 's', 's', 'w', 'e', 'o', 'b', 7, 's', 's', 'w', 'e', 'o', 'b', 's', 6, 's', 's', 'w', 'e', 'o', 's', 7, 's', 's', 'w', 'e', 'o', 's', 's', 7, 's', 's', 'w', 'e', 'o', 'n', 'g', 6, 's', 's', 'w', 'e', 'o', 'j', 6, 's', 's', 'w', 'e', 'o', 'c', 6, 's', 's', 'w', 'e', 'o', 'k', 6, 's', 's', 'w', 'e', 'o', 't', 6, 's', 's', 'w', 'e', 'o', 'p', 6, 's', 's', 'w', 'e', 'o', 'h', 4, 's', 's', 'w', 'e', 5, 's', 's', 'w', 'e', 'g', 6, 's', 's', 'w', 'e', 'g', 'g', 6, 's', 's', 'w', 'e', 'g', 's', 5, 's', 's', 'w', 'e', 'n', 6, 's', 's', 'w', 'e', 'n', 'j', 6, 's', 's', 'w', 'e', 'n', 'h', 5, 's', 's', 'w', 'e', 'd', 5, 's', 's', 'w', 'e', 'l', 6, 's', 's', 'w', 'e', 'l', 'g', 6, 's', 's', 'w', 'e', 'l', 'm', 6, 's', 's', 'w', 'e', 'l', 'b', 6, 's', 's', 'w', 'e', 'l', 's', 6, 's', 's', 'w', 'e', 'l', 't', 6, 's', 's', 'w', 'e', 'l', 'p', 6, 's', 's', 'w', 'e', 'l', 'h', 5, 's', 's', 'w', 'e', 'm', 5, 's', 's', 'w', 'e', 'b', 6, 's', 's', 'w', 'e', 'b', 's', 5, 's', 's', 'w', 'e', 's', 6, 's', 's', 'w', 'e', 's', 's', 6, 's', 's', 'w', 'e', 'n', 'g', 5, 's', 's', 'w', 'e', 'j', 5, 's', 's', 'w', 'e', 'c', 5, 's', 's', 'w', 'e', 'k', 5, 's', 's', 'w', 'e', 't', 5, 's', 's', 'w', 'e', 'p', 5, 's', 's', 'w', 'e', 'h', 4, 's', 's', 'w', 'i', 5, 's', 's', 'w', 'i', 'g', 6, 's', 's', 'w', 'i', 'g', 'g', 6, 's', 's', 'w', 'i', 'g', 's', 5, 's', 's', 'w', 'i', 'n', 6, 's', 's', 'w', 'i', 'n', 'j', 6, 's', 's', 'w', 'i', 'n', 'h', 5, 's', 's', 'w', 'i', 'd', 5, 's', 's', 'w', 'i', 'l', 6, 's', 's', 'w', 'i', 'l', 'g', 6, 's', 's', 'w', 'i', 'l', 'm', 6, 's', 's', 'w', 'i', 'l', 'b', 6, 's', 's', 'w', 'i', 'l', 's', 6, 's', 's', 'w', 'i', 'l', 't', 6, 's', 's', 'w', 'i', 'l', 'p', 6, 's', 's', 'w', 'i', 'l', 'h', 5, 's', 's', 'w', 'i', 'm', 5, 's', 's', 'w', 'i', 'b', 6, 's', 's', 'w', 'i', 'b', 's', 5, 's', 's', 'w', 'i', 's', 6, 's', 's', 'w', 'i', 's', 's', 6, 's', 's', 'w', 'i', 'n', 'g', 5, 's', 's', 'w', 'i', 'j', 5, 's', 's', 'w', 'i', 'c', 5, 's', 's', 'w', 'i', 'k', 5, 's', 's', 'w', 'i', 't', 5, 's', 's', 'w', 'i', 'p', 5, 's', 's', 'w', 'i', 'h', 4, 's', 's', 'y', 'u', 5, 's', 's', 'y', 'u', 'g', 6, 's', 's', 'y', 'u', 'g', 'g', 6, 's', 's', 'y', 'u', 'g', 's', 5, 's', 's', 'y', 'u', 'n', 6, 's', 's', 'y', 'u', 'n', 'j', 6, 's', 's', 'y', 'u', 'n', 'h', 5, 's', 's', 'y', 'u', 'd', 5, 's', 's', 'y', 'u', 'l', 6, 's', 's', 'y', 'u', 'l', 'g', 6, 's', 's', 'y', 'u', 'l', 'm', 6, 's', 's', 'y', 'u', 'l', 'b', 6, 's', 's', 'y', 'u', 'l', 's', 6, 's', 's', 'y', 'u', 'l', 't', 6, 's', 's', 'y', 'u', 'l', 'p', 6, 's', 's', 'y', 'u', 'l', 'h', 5, 's', 's', 'y', 'u', 'm', 5, 's', 's', 'y', 'u', 'b', 6, 's', 's', 'y', 'u', 'b', 's', 5, 's', 's', 'y', 'u', 's', 6, 's', 's', 'y', 'u', 's', 's', 6, 's', 's', 'y', 'u', 'n', 'g', 5, 's', 's', 'y', 'u', 'j', 5, 's', 's', 'y', 'u', 'c', 5, 's', 's', 'y', 'u', 'k', 5, 's', 's', 'y', 'u', 't', 5, 's', 's', 'y', 'u', 'p', 5, 's', 's', 'y', 'u', 'h', 4, 's', 's', 'e', 'u', 5, 's', 's', 'e', 'u', 'g', 6, 's', 's', 'e', 'u', 'g', 'g', 6, 's', 's', 'e', 'u', 'g', 's', 5, 's', 's', 'e', 'u', 'n', 6, 's', 's', 'e', 'u', 'n', 'j', 6, 's', 's', 'e', 'u', 'n', 'h', 5, 's', 's', 'e', 'u', 'd', 5, 's', 's', 'e', 'u', 'l', 6, 's', 's', 'e', 'u', 'l', 'g', 6, 's', 's', 'e', 'u', 'l', 'm', 6, 's', 's', 'e', 'u', 'l', 'b', 6, 's', 's', 'e', 'u', 'l', 's', 6, 's', 's', 'e', 'u', 'l', 't', 6, 's', 's', 'e', 'u', 'l', 'p', 6, 's', 's', 'e', 'u', 'l', 'h', 5, 's', 's', 'e', 'u', 'm', 5, 's', 's', 'e', 'u', 'b', 6, 's', 's', 'e', 'u', 'b', 's', 5, 's', 's', 'e', 'u', 's', 6, 's', 's', 'e', 'u', 's', 's', 6, 's', 's', 'e', 'u', 'n', 'g', 5, 's', 's', 'e', 'u', 'j', 5, 's', 's', 'e', 'u', 'c', 5, 's', 's', 'e', 'u', 'k', 5, 's', 's', 'e', 'u', 't', 5, 's', 's', 'e', 'u', 'p', 5, 's', 's', 'e', 'u', 'h', 4, 's', 's', 'y', 'i', 5, 's', 's', 'y', 'i', 'g', 6, 's', 's', 'y', 'i', 'g', 'g', 6, 's', 's', 'y', 'i', 'g', 's', 5, 's', 's', 'y', 'i', 'n', 6, 's', 's', 'y', 'i', 'n', 'j', 6, 's', 's', 'y', 'i', 'n', 'h', 5, 's', 's', 'y', 'i', 'd', 5, 's', 's', 'y', 'i', 'l', 6, 's', 's', 'y', 'i', 'l', 'g', 6, 's', 's', 'y', 'i', 'l', 'm', 6, 's', 's', 'y', 'i', 'l', 'b', 6, 's', 's', 'y', 'i', 'l', 's', 6, 's', 's', 'y', 'i', 'l', 't', 6, 's', 's', 'y', 'i', 'l', 'p', 6, 's', 's', 'y', 'i', 'l', 'h', 5, 's', 's', 'y', 'i', 'm', 5, 's', 's', 'y', 'i', 'b', 6, 's', 's', 'y', 'i', 'b', 's', 5, 's', 's', 'y', 'i', 's', 6, 's', 's', 'y', 'i', 's', 's', 6, 's', 's', 'y', 'i', 'n', 'g', 5, 's', 's', 'y', 'i', 'j', 5, 's', 's', 'y', 'i', 'c', 5, 's', 's', 'y', 'i', 'k', 5, 's', 's', 'y', 'i', 't', 5, 's', 's', 'y', 'i', 'p', 5, 's', 's', 'y', 'i', 'h', 3, 's', 's', 'i', 4, 's', 's', 'i', 'g', 5, 's', 's', 'i', 'g', 'g', 5, 's', 's', 'i', 'g', 's', 4, 's', 's', 'i', 'n', 5, 's', 's', 'i', 'n', 'j', 5, 's', 's', 'i', 'n', 'h', 4, 's', 's', 'i', 'd', 4, 's', 's', 'i', 'l', 5, 's', 's', 'i', 'l', 'g', 5, 's', 's', 'i', 'l', 'm', 5, 's', 's', 'i', 'l', 'b', 5, 's', 's', 'i', 'l', 's', 5, 's', 's', 'i', 'l', 't', 5, 's', 's', 'i', 'l', 'p', 5, 's', 's', 'i', 'l', 'h', 4, 's', 's', 'i', 'm', 4, 's', 's', 'i', 'b', 5, 's', 's', 'i', 'b', 's', 4, 's', 's', 'i', 's', 5, 's', 's', 'i', 's', 's', 5, 's', 's', 'i', 'n', 'g', 4, 's', 's', 'i', 'j', 4, 's', 's', 'i', 'c', 4, 's', 's', 'i', 'k', 4, 's', 's', 'i', 't', 4, 's', 's', 'i', 'p', 4, 's', 's', 'i', 'h', 1, 'a', 2, 'a', 'g', 3, 'a', 'g', 'g', 3, 'a', 'g', 's', 2, 'a', 'n', 3, 'a', 'n', 'j', 3, 'a', 'n', 'h', 2, 'a', 'd', 2, 'a', 'l', 3, 'a', 'l', 'g', 3, 'a', 'l', 'm', 3, 'a', 'l', 'b', 3, 'a', 'l', 's', 3, 'a', 'l', 't', 3, 'a', 'l', 'p', 3, 'a', 'l', 'h', 2, 'a', 'm', 2, 'a', 'b', 3, 'a', 'b', 's', 2, 'a', 's', 3, 'a', 's', 's', 3, 'a', 'n', 'g', 2, 'a', 'j', 2, 'a', 'c', 2, 'a', 'k', 2, 'a', 't', 2, 'a', 'p', 2, 'a', 'h', 2, 'a', 'e', 3, 'a', 'e', 'g', 4, 'a', 'e', 'g', 'g', 4, 'a', 'e', 'g', 's', 3, 'a', 'e', 'n', 4, 'a', 'e', 'n', 'j', 4, 'a', 'e', 'n', 'h', 3, 'a', 'e', 'd', 3, 'a', 'e', 'l', 4, 'a', 'e', 'l', 'g', 4, 'a', 'e', 'l', 'm', 4, 'a', 'e', 'l', 'b', 4, 'a', 'e', 'l', 's', 4, 'a', 'e', 'l', 't', 4, 'a', 'e', 'l', 'p', 4, 'a', 'e', 'l', 'h', 3, 'a', 'e', 'm', 3, 'a', 'e', 'b', 4, 'a', 'e', 'b', 's', 3, 'a', 'e', 's', 4, 'a', 'e', 's', 's', 4, 'a', 'e', 'n', 'g', 3, 'a', 'e', 'j', 3, 'a', 'e', 'c', 3, 'a', 'e', 'k', 3, 'a', 'e', 't', 3, 'a', 'e', 'p', 3, 'a', 'e', 'h', 2, 'y', 'a', 3, 'y', 'a', 'g', 4, 'y', 'a', 'g', 'g', 4, 'y', 'a', 'g', 's', 3, 'y', 'a', 'n', 4, 'y', 'a', 'n', 'j', 4, 'y', 'a', 'n', 'h', 3, 'y', 'a', 'd', 3, 'y', 'a', 'l', 4, 'y', 'a', 'l', 'g', 4, 'y', 'a', 'l', 'm', 4, 'y', 'a', 'l', 'b', 4, 'y', 'a', 'l', 's', 4, 'y', 'a', 'l', 't', 4, 'y', 'a', 'l', 'p', 4, 'y', 'a', 'l', 'h', 3, 'y', 'a', 'm', 3, 'y', 'a', 'b', 4, 'y', 'a', 'b', 's', 3, 'y', 'a', 's', 4, 'y', 'a', 's', 's', 4, 'y', 'a', 'n', 'g', 3, 'y', 'a', 'j', 3, 'y', 'a', 'c', 3, 'y', 'a', 'k', 3, 'y', 'a', 't', 3, 'y', 'a', 'p', 3, 'y', 'a', 'h', 3, 'y', 'a', 'e', 4, 'y', 'a', 'e', 'g', 5, 'y', 'a', 'e', 'g', 'g', 5, 'y', 'a', 'e', 'g', 's', 4, 'y', 'a', 'e', 'n', 5, 'y', 'a', 'e', 'n', 'j', 5, 'y', 'a', 'e', 'n', 'h', 4, 'y', 'a', 'e', 'd', 4, 'y', 'a', 'e', 'l', 5, 'y', 'a', 'e', 'l', 'g', 5, 'y', 'a', 'e', 'l', 'm', 5, 'y', 'a', 'e', 'l', 'b', 5, 'y', 'a', 'e', 'l', 's', 5, 'y', 'a', 'e', 'l', 't', 5, 'y', 'a', 'e', 'l', 'p', 5, 'y', 'a', 'e', 'l', 'h', 4, 'y', 'a', 'e', 'm', 4, 'y', 'a', 'e', 'b', 5, 'y', 'a', 'e', 'b', 's', 4, 'y', 'a', 'e', 's', 5, 'y', 'a', 'e', 's', 's', 5, 'y', 'a', 'e', 'n', 'g', 4, 'y', 'a', 'e', 'j', 4, 'y', 'a', 'e', 'c', 4, 'y', 'a', 'e', 'k', 4, 'y', 'a', 'e', 't', 4, 'y', 'a', 'e', 'p', 4, 'y', 'a', 'e', 'h', 2, 'e', 'o', 3, 'e', 'o', 'g', 4, 'e', 'o', 'g', 'g', 4, 'e', 'o', 'g', 's', 3, 'e', 'o', 'n', 4, 'e', 'o', 'n', 'j', 4, 'e', 'o', 'n', 'h', 3, 'e', 'o', 'd', 3, 'e', 'o', 'l', 4, 'e', 'o', 'l', 'g', 4, 'e', 'o', 'l', 'm', 4, 'e', 'o', 'l', 'b', 4, 'e', 'o', 'l', 's', 4, 'e', 'o', 'l', 't', 4, 'e', 'o', 'l', 'p', 4, 'e', 'o', 'l', 'h', 3, 'e', 'o', 'm', 3, 'e', 'o', 'b', 4, 'e', 'o', 'b', 's', 3, 'e', 'o', 's', 4, 'e', 'o', 's', 's', 4, 'e', 'o', 'n', 'g', 3, 'e', 'o', 'j', 3, 'e', 'o', 'c', 3, 'e', 'o', 'k', 3, 'e', 'o', 't', 3, 'e', 'o', 'p', 3, 'e', 'o', 'h', 1, 'e', 2, 'e', 'g', 3, 'e', 'g', 'g', 3, 'e', 'g', 's', 2, 'e', 'n', 3, 'e', 'n', 'j', 3, 'e', 'n', 'h', 2, 'e', 'd', 2, 'e', 'l', 3, 'e', 'l', 'g', 3, 'e', 'l', 'm', 3, 'e', 'l', 'b', 3, 'e', 'l', 's', 3, 'e', 'l', 't', 3, 'e', 'l', 'p', 3, 'e', 'l', 'h', 2, 'e', 'm', 2, 'e', 'b', 3, 'e', 'b', 's', 2, 'e', 's', 3, 'e', 's', 's', 3, 'e', 'n', 'g', 2, 'e', 'j', 2, 'e', 'c', 2, 'e', 'k', 2, 'e', 't', 2, 'e', 'p', 2, 'e', 'h', 3, 'y', 'e', 'o', 4, 'y', 'e', 'o', 'g', 5, 'y', 'e', 'o', 'g', 'g', 5, 'y', 'e', 'o', 'g', 's', 4, 'y', 'e', 'o', 'n', 5, 'y', 'e', 'o', 'n', 'j', 5, 'y', 'e', 'o', 'n', 'h', 4, 'y', 'e', 'o', 'd', 4, 'y', 'e', 'o', 'l', 5, 'y', 'e', 'o', 'l', 'g', 5, 'y', 'e', 'o', 'l', 'm', 5, 'y', 'e', 'o', 'l', 'b', 5, 'y', 'e', 'o', 'l', 's', 5, 'y', 'e', 'o', 'l', 't', 5, 'y', 'e', 'o', 'l', 'p', 5, 'y', 'e', 'o', 'l', 'h', 4, 'y', 'e', 'o', 'm', 4, 'y', 'e', 'o', 'b', 5, 'y', 'e', 'o', 'b', 's', 4, 'y', 'e', 'o', 's', 5, 'y', 'e', 'o', 's', 's', 5, 'y', 'e', 'o', 'n', 'g', 4, 'y', 'e', 'o', 'j', 4, 'y', 'e', 'o', 'c', 4, 'y', 'e', 'o', 'k', 4, 'y', 'e', 'o', 't', 4, 'y', 'e', 'o', 'p', 4, 'y', 'e', 'o', 'h', 2, 'y', 'e', 3, 'y', 'e', 'g', 4, 'y', 'e', 'g', 'g', 4, 'y', 'e', 'g', 's', 3, 'y', 'e', 'n', 4, 'y', 'e', 'n', 'j', 4, 'y', 'e', 'n', 'h', 3, 'y', 'e', 'd', 3, 'y', 'e', 'l', 4, 'y', 'e', 'l', 'g', 4, 'y', 'e', 'l', 'm', 4, 'y', 'e', 'l', 'b', 4, 'y', 'e', 'l', 's', 4, 'y', 'e', 'l', 't', 4, 'y', 'e', 'l', 'p', 4, 'y', 'e', 'l', 'h', 3, 'y', 'e', 'm', 3, 'y', 'e', 'b', 4, 'y', 'e', 'b', 's', 3, 'y', 'e', 's', 4, 'y', 'e', 's', 's', 4, 'y', 'e', 'n', 'g', 3, 'y', 'e', 'j', 3, 'y', 'e', 'c', 3, 'y', 'e', 'k', 3, 'y', 'e', 't', 3, 'y', 'e', 'p', 3, 'y', 'e', 'h', 1, 'o', 2, 'o', 'g', 3, 'o', 'g', 'g', 3, 'o', 'g', 's', 2, 'o', 'n', 3, 'o', 'n', 'j', 3, 'o', 'n', 'h', 2, 'o', 'd', 2, 'o', 'l', 3, 'o', 'l', 'g', 3, 'o', 'l', 'm', 3, 'o', 'l', 'b', 3, 'o', 'l', 's', 3, 'o', 'l', 't', 3, 'o', 'l', 'p', 3, 'o', 'l', 'h', 2, 'o', 'm', 2, 'o', 'b', 3, 'o', 'b', 's', 2, 'o', 's', 3, 'o', 's', 's', 3, 'o', 'n', 'g', 2, 'o', 'j', 2, 'o', 'c', 2, 'o', 'k', 2, 'o', 't', 2, 'o', 'p', 2, 'o', 'h', 2, 'w', 'a', 3, 'w', 'a', 'g', 4, 'w', 'a', 'g', 'g', 4, 'w', 'a', 'g', 's', 3, 'w', 'a', 'n', 4, 'w', 'a', 'n', 'j', 4, 'w', 'a', 'n', 'h', 3, 'w', 'a', 'd', 3, 'w', 'a', 'l', 4, 'w', 'a', 'l', 'g', 4, 'w', 'a', 'l', 'm', 4, 'w', 'a', 'l', 'b', 4, 'w', 'a', 'l', 's', 4, 'w', 'a', 'l', 't', 4, 'w', 'a', 'l', 'p', 4, 'w', 'a', 'l', 'h', 3, 'w', 'a', 'm', 3, 'w', 'a', 'b', 4, 'w', 'a', 'b', 's', 3, 'w', 'a', 's', 4, 'w', 'a', 's', 's', 4, 'w', 'a', 'n', 'g', 3, 'w', 'a', 'j', 3, 'w', 'a', 'c', 3, 'w', 'a', 'k', 3, 'w', 'a', 't', 3, 'w', 'a', 'p', 3, 'w', 'a', 'h', 3, 'w', 'a', 'e', 4, 'w', 'a', 'e', 'g', 5, 'w', 'a', 'e', 'g', 'g', 5, 'w', 'a', 'e', 'g', 's', 4, 'w', 'a', 'e', 'n', 5, 'w', 'a', 'e', 'n', 'j', 5, 'w', 'a', 'e', 'n', 'h', 4, 'w', 'a', 'e', 'd', 4, 'w', 'a', 'e', 'l', 5, 'w', 'a', 'e', 'l', 'g', 5, 'w', 'a', 'e', 'l', 'm', 5, 'w', 'a', 'e', 'l', 'b', 5, 'w', 'a', 'e', 'l', 's', 5, 'w', 'a', 'e', 'l', 't', 5, 'w', 'a', 'e', 'l', 'p', 5, 'w', 'a', 'e', 'l', 'h', 4, 'w', 'a', 'e', 'm', 4, 'w', 'a', 'e', 'b', 5, 'w', 'a', 'e', 'b', 's', 4, 'w', 'a', 'e', 's', 5, 'w', 'a', 'e', 's', 's', 5, 'w', 'a', 'e', 'n', 'g', 4, 'w', 'a', 'e', 'j', 4, 'w', 'a', 'e', 'c', 4, 'w', 'a', 'e', 'k', 4, 'w', 'a', 'e', 't', 4, 'w', 'a', 'e', 'p', 4, 'w', 'a', 'e', 'h', 2, 'o', 'e', 3, 'o', 'e', 'g', 4, 'o', 'e', 'g', 'g', 4, 'o', 'e', 'g', 's', 3, 'o', 'e', 'n', 4, 'o', 'e', 'n', 'j', 4, 'o', 'e', 'n', 'h', 3, 'o', 'e', 'd', 3, 'o', 'e', 'l', 4, 'o', 'e', 'l', 'g', 4, 'o', 'e', 'l', 'm', 4, 'o', 'e', 'l', 'b', 4, 'o', 'e', 'l', 's', 4, 'o', 'e', 'l', 't', 4, 'o', 'e', 'l', 'p', 4, 'o', 'e', 'l', 'h', 3, 'o', 'e', 'm', 3, 'o', 'e', 'b', 4, 'o', 'e', 'b', 's', 3, 'o', 'e', 's', 4, 'o', 'e', 's', 's', 4, 'o', 'e', 'n', 'g', 3, 'o', 'e', 'j', 3, 'o', 'e', 'c', 3, 'o', 'e', 'k', 3, 'o', 'e', 't', 3, 'o', 'e', 'p', 3, 'o', 'e', 'h', 2, 'y', 'o', 3, 'y', 'o', 'g', 4, 'y', 'o', 'g', 'g', 4, 'y', 'o', 'g', 's', 3, 'y', 'o', 'n', 4, 'y', 'o', 'n', 'j', 4, 'y', 'o', 'n', 'h', 3, 'y', 'o', 'd', 3, 'y', 'o', 'l', 4, 'y', 'o', 'l', 'g', 4, 'y', 'o', 'l', 'm', 4, 'y', 'o', 'l', 'b', 4, 'y', 'o', 'l', 's', 4, 'y', 'o', 'l', 't', 4, 'y', 'o', 'l', 'p', 4, 'y', 'o', 'l', 'h', 3, 'y', 'o', 'm', 3, 'y', 'o', 'b', 4, 'y', 'o', 'b', 's', 3, 'y', 'o', 's', 4, 'y', 'o', 's', 's', 4, 'y', 'o', 'n', 'g', 3, 'y', 'o', 'j', 3, 'y', 'o', 'c', 3, 'y', 'o', 'k', 3, 'y', 'o', 't', 3, 'y', 'o', 'p', 3, 'y', 'o', 'h', 1, 'u', 2, 'u', 'g', 3, 'u', 'g', 'g', 3, 'u', 'g', 's', 2, 'u', 'n', 3, 'u', 'n', 'j', 3, 'u', 'n', 'h', 2, 'u', 'd', 2, 'u', 'l', 3, 'u', 'l', 'g', 3, 'u', 'l', 'm', 3, 'u', 'l', 'b', 3, 'u', 'l', 's', 3, 'u', 'l', 't', 3, 'u', 'l', 'p', 3, 'u', 'l', 'h', 2, 'u', 'm', 2, 'u', 'b', 3, 'u', 'b', 's', 2, 'u', 's', 3, 'u', 's', 's', 3, 'u', 'n', 'g', 2, 'u', 'j', 2, 'u', 'c', 2, 'u', 'k', 2, 'u', 't', 2, 'u', 'p', 2, 'u', 'h', 3, 'w', 'e', 'o', 4, 'w', 'e', 'o', 'g', 5, 'w', 'e', 'o', 'g', 'g', 5, 'w', 'e', 'o', 'g', 's', 4, 'w', 'e', 'o', 'n', 5, 'w', 'e', 'o', 'n', 'j', 5, 'w', 'e', 'o', 'n', 'h', 4, 'w', 'e', 'o', 'd', 4, 'w', 'e', 'o', 'l', 5, 'w', 'e', 'o', 'l', 'g', 5, 'w', 'e', 'o', 'l', 'm', 5, 'w', 'e', 'o', 'l', 'b', 5, 'w', 'e', 'o', 'l', 's', 5, 'w', 'e', 'o', 'l', 't', 5, 'w', 'e', 'o', 'l', 'p', 5, 'w', 'e', 'o', 'l', 'h', 4, 'w', 'e', 'o', 'm', 4, 'w', 'e', 'o', 'b', 5, 'w', 'e', 'o', 'b', 's', 4, 'w', 'e', 'o', 's', 5, 'w', 'e', 'o', 's', 's', 5, 'w', 'e', 'o', 'n', 'g', 4, 'w', 'e', 'o', 'j', 4, 'w', 'e', 'o', 'c', 4, 'w', 'e', 'o', 'k', 4, 'w', 'e', 'o', 't', 4, 'w', 'e', 'o', 'p', 4, 'w', 'e', 'o', 'h', 2, 'w', 'e', 3, 'w', 'e', 'g', 4, 'w', 'e', 'g', 'g', 4, 'w', 'e', 'g', 's', 3, 'w', 'e', 'n', 4, 'w', 'e', 'n', 'j', 4, 'w', 'e', 'n', 'h', 3, 'w', 'e', 'd', 3, 'w', 'e', 'l', 4, 'w', 'e', 'l', 'g', 4, 'w', 'e', 'l', 'm', 4, 'w', 'e', 'l', 'b', 4, 'w', 'e', 'l', 's', 4, 'w', 'e', 'l', 't', 4, 'w', 'e', 'l', 'p', 4, 'w', 'e', 'l', 'h', 3, 'w', 'e', 'm', 3, 'w', 'e', 'b', 4, 'w', 'e', 'b', 's', 3, 'w', 'e', 's', 4, 'w', 'e', 's', 's', 4, 'w', 'e', 'n', 'g', 3, 'w', 'e', 'j', 3, 'w', 'e', 'c', 3, 'w', 'e', 'k', 3, 'w', 'e', 't', 3, 'w', 'e', 'p', 3, 'w', 'e', 'h', 2, 'w', 'i', 3, 'w', 'i', 'g', 4, 'w', 'i', 'g', 'g', 4, 'w', 'i', 'g', 's', 3, 'w', 'i', 'n', 4, 'w', 'i', 'n', 'j', 4, 'w', 'i', 'n', 'h', 3, 'w', 'i', 'd', 3, 'w', 'i', 'l', 4, 'w', 'i', 'l', 'g', 4, 'w', 'i', 'l', 'm', 4, 'w', 'i', 'l', 'b', 4, 'w', 'i', 'l', 's', 4, 'w', 'i', 'l', 't', 4, 'w', 'i', 'l', 'p', 4, 'w', 'i', 'l', 'h', 3, 'w', 'i', 'm', 3, 'w', 'i', 'b', 4, 'w', 'i', 'b', 's', 3, 'w', 'i', 's', 4, 'w', 'i', 's', 's', 4, 'w', 'i', 'n', 'g', 3, 'w', 'i', 'j', 3, 'w', 'i', 'c', 3, 'w', 'i', 'k', 3, 'w', 'i', 't', 3, 'w', 'i', 'p', 3, 'w', 'i', 'h', 2, 'y', 'u', 3, 'y', 'u', 'g', 4, 'y', 'u', 'g', 'g', 4, 'y', 'u', 'g', 's', 3, 'y', 'u', 'n', 4, 'y', 'u', 'n', 'j', 4, 'y', 'u', 'n', 'h', 3, 'y', 'u', 'd', 3, 'y', 'u', 'l', 4, 'y', 'u', 'l', 'g', 4, 'y', 'u', 'l', 'm', 4, 'y', 'u', 'l', 'b', 4, 'y', 'u', 'l', 's', 4, 'y', 'u', 'l', 't', 4, 'y', 'u', 'l', 'p', 4, 'y', 'u', 'l', 'h', 3, 'y', 'u', 'm', 3, 'y', 'u', 'b', 4, 'y', 'u', 'b', 's', 3, 'y', 'u', 's', 4, 'y', 'u', 's', 's', 4, 'y', 'u', 'n', 'g', 3, 'y', 'u', 'j', 3, 'y', 'u', 'c', 3, 'y', 'u', 'k', 3, 'y', 'u', 't', 3, 'y', 'u', 'p', 3, 'y', 'u', 'h', 2, 'e', 'u', 3, 'e', 'u', 'g', 4, 'e', 'u', 'g', 'g', 4, 'e', 'u', 'g', 's', 3, 'e', 'u', 'n', 4, 'e', 'u', 'n', 'j', 4, 'e', 'u', 'n', 'h', 3, 'e', 'u', 'd', 3, 'e', 'u', 'l', 4, 'e', 'u', 'l', 'g', 4, 'e', 'u', 'l', 'm', 4, 'e', 'u', 'l', 'b', 4, 'e', 'u', 'l', 's', 4, 'e', 'u', 'l', 't', 4, 'e', 'u', 'l', 'p', 4, 'e', 'u', 'l', 'h', 3, 'e', 'u', 'm', 3, 'e', 'u', 'b', 4, 'e', 'u', 'b', 's', 3, 'e', 'u', 's', 4, 'e', 'u', 's', 's', 4, 'e', 'u', 'n', 'g', 3, 'e', 'u', 'j', 3, 'e', 'u', 'c', 3, 'e', 'u', 'k', 3, 'e', 'u', 't', 3, 'e', 'u', 'p', 3, 'e', 'u', 'h', 2, 'y', 'i', 3, 'y', 'i', 'g', 4, 'y', 'i', 'g', 'g', 4, 'y', 'i', 'g', 's', 3, 'y', 'i', 'n', 4, 'y', 'i', 'n', 'j', 4, 'y', 'i', 'n', 'h', 3, 'y', 'i', 'd', 3, 'y', 'i', 'l', 4, 'y', 'i', 'l', 'g', 4, 'y', 'i', 'l', 'm', 4, 'y', 'i', 'l', 'b', 4, 'y', 'i', 'l', 's', 4, 'y', 'i', 'l', 't', 4, 'y', 'i', 'l', 'p', 4, 'y', 'i', 'l', 'h', 3, 'y', 'i', 'm', 3, 'y', 'i', 'b', 4, 'y', 'i', 'b', 's', 3, 'y', 'i', 's', 4, 'y', 'i', 's', 's', 4, 'y', 'i', 'n', 'g', 3, 'y', 'i', 'j', 3, 'y', 'i', 'c', 3, 'y', 'i', 'k', 3, 'y', 'i', 't', 3, 'y', 'i', 'p', 3, 'y', 'i', 'h', 1, 'i', 2, 'i', 'g', 3, 'i', 'g', 'g', 3, 'i', 'g', 's', 2, 'i', 'n', 3, 'i', 'n', 'j', 3, 'i', 'n', 'h', 2, 'i', 'd', 2, 'i', 'l', 3, 'i', 'l', 'g', 3, 'i', 'l', 'm', 3, 'i', 'l', 'b', 3, 'i', 'l', 's', 3, 'i', 'l', 't', 3, 'i', 'l', 'p', 3, 'i', 'l', 'h', 2, 'i', 'm', 2, 'i', 'b', 3, 'i', 'b', 's', 2, 'i', 's', 3, 'i', 's', 's', 3, 'i', 'n', 'g', 2, 'i', 'j', 2, 'i', 'c', 2, 'i', 'k', 2, 'i', 't', 2, 'i', 'p', 2, 'i', 'h', 2, 'j', 'a', 3, 'j', 'a', 'g', 4, 'j', 'a', 'g', 'g', 4, 'j', 'a', 'g', 's', 3, 'j', 'a', 'n', 4, 'j', 'a', 'n', 'j', 4, 'j', 'a', 'n', 'h', 3, 'j', 'a', 'd', 3, 'j', 'a', 'l', 4, 'j', 'a', 'l', 'g', 4, 'j', 'a', 'l', 'm', 4, 'j', 'a', 'l', 'b', 4, 'j', 'a', 'l', 's', 4, 'j', 'a', 'l', 't', 4, 'j', 'a', 'l', 'p', 4, 'j', 'a', 'l', 'h', 3, 'j', 'a', 'm', 3, 'j', 'a', 'b', 4, 'j', 'a', 'b', 's', 3, 'j', 'a', 's', 4, 'j', 'a', 's', 's', 4, 'j', 'a', 'n', 'g', 3, 'j', 'a', 'j', 3, 'j', 'a', 'c', 3, 'j', 'a', 'k', 3, 'j', 'a', 't', 3, 'j', 'a', 'p', 3, 'j', 'a', 'h', 3, 'j', 'a', 'e', 4, 'j', 'a', 'e', 'g', 5, 'j', 'a', 'e', 'g', 'g', 5, 'j', 'a', 'e', 'g', 's', 4, 'j', 'a', 'e', 'n', 5, 'j', 'a', 'e', 'n', 'j', 5, 'j', 'a', 'e', 'n', 'h', 4, 'j', 'a', 'e', 'd', 4, 'j', 'a', 'e', 'l', 5, 'j', 'a', 'e', 'l', 'g', 5, 'j', 'a', 'e', 'l', 'm', 5, 'j', 'a', 'e', 'l', 'b', 5, 'j', 'a', 'e', 'l', 's', 5, 'j', 'a', 'e', 'l', 't', 5, 'j', 'a', 'e', 'l', 'p', 5, 'j', 'a', 'e', 'l', 'h', 4, 'j', 'a', 'e', 'm', 4, 'j', 'a', 'e', 'b', 5, 'j', 'a', 'e', 'b', 's', 4, 'j', 'a', 'e', 's', 5, 'j', 'a', 'e', 's', 's', 5, 'j', 'a', 'e', 'n', 'g', 4, 'j', 'a', 'e', 'j', 4, 'j', 'a', 'e', 'c', 4, 'j', 'a', 'e', 'k', 4, 'j', 'a', 'e', 't', 4, 'j', 'a', 'e', 'p', 4, 'j', 'a', 'e', 'h', 3, 'j', 'y', 'a', 4, 'j', 'y', 'a', 'g', 5, 'j', 'y', 'a', 'g', 'g', 5, 'j', 'y', 'a', 'g', 's', 4, 'j', 'y', 'a', 'n', 5, 'j', 'y', 'a', 'n', 'j', 5, 'j', 'y', 'a', 'n', 'h', 4, 'j', 'y', 'a', 'd', 4, 'j', 'y', 'a', 'l', 5, 'j', 'y', 'a', 'l', 'g', 5, 'j', 'y', 'a', 'l', 'm', 5, 'j', 'y', 'a', 'l', 'b', 5, 'j', 'y', 'a', 'l', 's', 5, 'j', 'y', 'a', 'l', 't', 5, 'j', 'y', 'a', 'l', 'p', 5, 'j', 'y', 'a', 'l', 'h', 4, 'j', 'y', 'a', 'm', 4, 'j', 'y', 'a', 'b', 5, 'j', 'y', 'a', 'b', 's', 4, 'j', 'y', 'a', 's', 5, 'j', 'y', 'a', 's', 's', 5, 'j', 'y', 'a', 'n', 'g', 4, 'j', 'y', 'a', 'j', 4, 'j', 'y', 'a', 'c', 4, 'j', 'y', 'a', 'k', 4, 'j', 'y', 'a', 't', 4, 'j', 'y', 'a', 'p', 4, 'j', 'y', 'a', 'h', 4, 'j', 'y', 'a', 'e', 5, 'j', 'y', 'a', 'e', 'g', 6, 'j', 'y', 'a', 'e', 'g', 'g', 6, 'j', 'y', 'a', 'e', 'g', 's', 5, 'j', 'y', 'a', 'e', 'n', 6, 'j', 'y', 'a', 'e', 'n', 'j', 6, 'j', 'y', 'a', 'e', 'n', 'h', 5, 'j', 'y', 'a', 'e', 'd', 5, 'j', 'y', 'a', 'e', 'l', 6, 'j', 'y', 'a', 'e', 'l', 'g', 6, 'j', 'y', 'a', 'e', 'l', 'm', 6, 'j', 'y', 'a', 'e', 'l', 'b', 6, 'j', 'y', 'a', 'e', 'l', 's', 6, 'j', 'y', 'a', 'e', 'l', 't', 6, 'j', 'y', 'a', 'e', 'l', 'p', 6, 'j', 'y', 'a', 'e', 'l', 'h', 5, 'j', 'y', 'a', 'e', 'm', 5, 'j', 'y', 'a', 'e', 'b', 6, 'j', 'y', 'a', 'e', 'b', 's', 5, 'j', 'y', 'a', 'e', 's', 6, 'j', 'y', 'a', 'e', 's', 's', 6, 'j', 'y', 'a', 'e', 'n', 'g', 5, 'j', 'y', 'a', 'e', 'j', 5, 'j', 'y', 'a', 'e', 'c', 5, 'j', 'y', 'a', 'e', 'k', 5, 'j', 'y', 'a', 'e', 't', 5, 'j', 'y', 'a', 'e', 'p', 5, 'j', 'y', 'a', 'e', 'h', 3, 'j', 'e', 'o', 4, 'j', 'e', 'o', 'g', 5, 'j', 'e', 'o', 'g', 'g', 5, 'j', 'e', 'o', 'g', 's', 4, 'j', 'e', 'o', 'n', 5, 'j', 'e', 'o', 'n', 'j', 5, 'j', 'e', 'o', 'n', 'h', 4, 'j', 'e', 'o', 'd', 4, 'j', 'e', 'o', 'l', 5, 'j', 'e', 'o', 'l', 'g', 5, 'j', 'e', 'o', 'l', 'm', 5, 'j', 'e', 'o', 'l', 'b', 5, 'j', 'e', 'o', 'l', 's', 5, 'j', 'e', 'o', 'l', 't', 5, 'j', 'e', 'o', 'l', 'p', 5, 'j', 'e', 'o', 'l', 'h', 4, 'j', 'e', 'o', 'm', 4, 'j', 'e', 'o', 'b', 5, 'j', 'e', 'o', 'b', 's', 4, 'j', 'e', 'o', 's', 5, 'j', 'e', 'o', 's', 's', 5, 'j', 'e', 'o', 'n', 'g', 4, 'j', 'e', 'o', 'j', 4, 'j', 'e', 'o', 'c', 4, 'j', 'e', 'o', 'k', 4, 'j', 'e', 'o', 't', 4, 'j', 'e', 'o', 'p', 4, 'j', 'e', 'o', 'h', 2, 'j', 'e', 3, 'j', 'e', 'g', 4, 'j', 'e', 'g', 'g', 4, 'j', 'e', 'g', 's', 3, 'j', 'e', 'n', 4, 'j', 'e', 'n', 'j', 4, 'j', 'e', 'n', 'h', 3, 'j', 'e', 'd', 3, 'j', 'e', 'l', 4, 'j', 'e', 'l', 'g', 4, 'j', 'e', 'l', 'm', 4, 'j', 'e', 'l', 'b', 4, 'j', 'e', 'l', 's', 4, 'j', 'e', 'l', 't', 4, 'j', 'e', 'l', 'p', 4, 'j', 'e', 'l', 'h', 3, 'j', 'e', 'm', 3, 'j', 'e', 'b', 4, 'j', 'e', 'b', 's', 3, 'j', 'e', 's', 4, 'j', 'e', 's', 's', 4, 'j', 'e', 'n', 'g', 3, 'j', 'e', 'j', 3, 'j', 'e', 'c', 3, 'j', 'e', 'k', 3, 'j', 'e', 't', 3, 'j', 'e', 'p', 3, 'j', 'e', 'h', 4, 'j', 'y', 'e', 'o', 5, 'j', 'y', 'e', 'o', 'g', 6, 'j', 'y', 'e', 'o', 'g', 'g', 6, 'j', 'y', 'e', 'o', 'g', 's', 5, 'j', 'y', 'e', 'o', 'n', 6, 'j', 'y', 'e', 'o', 'n', 'j', 6, 'j', 'y', 'e', 'o', 'n', 'h', 5, 'j', 'y', 'e', 'o', 'd', 5, 'j', 'y', 'e', 'o', 'l', 6, 'j', 'y', 'e', 'o', 'l', 'g', 6, 'j', 'y', 'e', 'o', 'l', 'm', 6, 'j', 'y', 'e', 'o', 'l', 'b', 6, 'j', 'y', 'e', 'o', 'l', 's', 6, 'j', 'y', 'e', 'o', 'l', 't', 6, 'j', 'y', 'e', 'o', 'l', 'p', 6, 'j', 'y', 'e', 'o', 'l', 'h', 5, 'j', 'y', 'e', 'o', 'm', 5, 'j', 'y', 'e', 'o', 'b', 6, 'j', 'y', 'e', 'o', 'b', 's', 5, 'j', 'y', 'e', 'o', 's', 6, 'j', 'y', 'e', 'o', 's', 's', 6, 'j', 'y', 'e', 'o', 'n', 'g', 5, 'j', 'y', 'e', 'o', 'j', 5, 'j', 'y', 'e', 'o', 'c', 5, 'j', 'y', 'e', 'o', 'k', 5, 'j', 'y', 'e', 'o', 't', 5, 'j', 'y', 'e', 'o', 'p', 5, 'j', 'y', 'e', 'o', 'h', 3, 'j', 'y', 'e', 4, 'j', 'y', 'e', 'g', 5, 'j', 'y', 'e', 'g', 'g', 5, 'j', 'y', 'e', 'g', 's', 4, 'j', 'y', 'e', 'n', 5, 'j', 'y', 'e', 'n', 'j', 5, 'j', 'y', 'e', 'n', 'h', 4, 'j', 'y', 'e', 'd', 4, 'j', 'y', 'e', 'l', 5, 'j', 'y', 'e', 'l', 'g', 5, 'j', 'y', 'e', 'l', 'm', 5, 'j', 'y', 'e', 'l', 'b', 5, 'j', 'y', 'e', 'l', 's', 5, 'j', 'y', 'e', 'l', 't', 5, 'j', 'y', 'e', 'l', 'p', 5, 'j', 'y', 'e', 'l', 'h', 4, 'j', 'y', 'e', 'm', 4, 'j', 'y', 'e', 'b', 5, 'j', 'y', 'e', 'b', 's', 4, 'j', 'y', 'e', 's', 5, 'j', 'y', 'e', 's', 's', 5, 'j', 'y', 'e', 'n', 'g', 4, 'j', 'y', 'e', 'j', 4, 'j', 'y', 'e', 'c', 4, 'j', 'y', 'e', 'k', 4, 'j', 'y', 'e', 't', 4, 'j', 'y', 'e', 'p', 4, 'j', 'y', 'e', 'h', 2, 'j', 'o', 3, 'j', 'o', 'g', 4, 'j', 'o', 'g', 'g', 4, 'j', 'o', 'g', 's', 3, 'j', 'o', 'n', 4, 'j', 'o', 'n', 'j', 4, 'j', 'o', 'n', 'h', 3, 'j', 'o', 'd', 3, 'j', 'o', 'l', 4, 'j', 'o', 'l', 'g', 4, 'j', 'o', 'l', 'm', 4, 'j', 'o', 'l', 'b', 4, 'j', 'o', 'l', 's', 4, 'j', 'o', 'l', 't', 4, 'j', 'o', 'l', 'p', 4, 'j', 'o', 'l', 'h', 3, 'j', 'o', 'm', 3, 'j', 'o', 'b', 4, 'j', 'o', 'b', 's', 3, 'j', 'o', 's', 4, 'j', 'o', 's', 's', 4, 'j', 'o', 'n', 'g', 3, 'j', 'o', 'j', 3, 'j', 'o', 'c', 3, 'j', 'o', 'k', 3, 'j', 'o', 't', 3, 'j', 'o', 'p', 3, 'j', 'o', 'h', 3, 'j', 'w', 'a', 4, 'j', 'w', 'a', 'g', 5, 'j', 'w', 'a', 'g', 'g', 5, 'j', 'w', 'a', 'g', 's', 4, 'j', 'w', 'a', 'n', 5, 'j', 'w', 'a', 'n', 'j', 5, 'j', 'w', 'a', 'n', 'h', 4, 'j', 'w', 'a', 'd', 4, 'j', 'w', 'a', 'l', 5, 'j', 'w', 'a', 'l', 'g', 5, 'j', 'w', 'a', 'l', 'm', 5, 'j', 'w', 'a', 'l', 'b', 5, 'j', 'w', 'a', 'l', 's', 5, 'j', 'w', 'a', 'l', 't', 5, 'j', 'w', 'a', 'l', 'p', 5, 'j', 'w', 'a', 'l', 'h', 4, 'j', 'w', 'a', 'm', 4, 'j', 'w', 'a', 'b', 5, 'j', 'w', 'a', 'b', 's', 4, 'j', 'w', 'a', 's', 5, 'j', 'w', 'a', 's', 's', 5, 'j', 'w', 'a', 'n', 'g', 4, 'j', 'w', 'a', 'j', 4, 'j', 'w', 'a', 'c', 4, 'j', 'w', 'a', 'k', 4, 'j', 'w', 'a', 't', 4, 'j', 'w', 'a', 'p', 4, 'j', 'w', 'a', 'h', 4, 'j', 'w', 'a', 'e', 5, 'j', 'w', 'a', 'e', 'g', 6, 'j', 'w', 'a', 'e', 'g', 'g', 6, 'j', 'w', 'a', 'e', 'g', 's', 5, 'j', 'w', 'a', 'e', 'n', 6, 'j', 'w', 'a', 'e', 'n', 'j', 6, 'j', 'w', 'a', 'e', 'n', 'h', 5, 'j', 'w', 'a', 'e', 'd', 5, 'j', 'w', 'a', 'e', 'l', 6, 'j', 'w', 'a', 'e', 'l', 'g', 6, 'j', 'w', 'a', 'e', 'l', 'm', 6, 'j', 'w', 'a', 'e', 'l', 'b', 6, 'j', 'w', 'a', 'e', 'l', 's', 6, 'j', 'w', 'a', 'e', 'l', 't', 6, 'j', 'w', 'a', 'e', 'l', 'p', 6, 'j', 'w', 'a', 'e', 'l', 'h', 5, 'j', 'w', 'a', 'e', 'm', 5, 'j', 'w', 'a', 'e', 'b', 6, 'j', 'w', 'a', 'e', 'b', 's', 5, 'j', 'w', 'a', 'e', 's', 6, 'j', 'w', 'a', 'e', 's', 's', 6, 'j', 'w', 'a', 'e', 'n', 'g', 5, 'j', 'w', 'a', 'e', 'j', 5, 'j', 'w', 'a', 'e', 'c', 5, 'j', 'w', 'a', 'e', 'k', 5, 'j', 'w', 'a', 'e', 't', 5, 'j', 'w', 'a', 'e', 'p', 5, 'j', 'w', 'a', 'e', 'h', 3, 'j', 'o', 'e', 4, 'j', 'o', 'e', 'g', 5, 'j', 'o', 'e', 'g', 'g', 5, 'j', 'o', 'e', 'g', 's', 4, 'j', 'o', 'e', 'n', 5, 'j', 'o', 'e', 'n', 'j', 5, 'j', 'o', 'e', 'n', 'h', 4, 'j', 'o', 'e', 'd', 4, 'j', 'o', 'e', 'l', 5, 'j', 'o', 'e', 'l', 'g', 5, 'j', 'o', 'e', 'l', 'm', 5, 'j', 'o', 'e', 'l', 'b', 5, 'j', 'o', 'e', 'l', 's', 5, 'j', 'o', 'e', 'l', 't', 5, 'j', 'o', 'e', 'l', 'p', 5, 'j', 'o', 'e', 'l', 'h', 4, 'j', 'o', 'e', 'm', 4, 'j', 'o', 'e', 'b', 5, 'j', 'o', 'e', 'b', 's', 4, 'j', 'o', 'e', 's', 5, 'j', 'o', 'e', 's', 's', 5, 'j', 'o', 'e', 'n', 'g', 4, 'j', 'o', 'e', 'j', 4, 'j', 'o', 'e', 'c', 4, 'j', 'o', 'e', 'k', 4, 'j', 'o', 'e', 't', 4, 'j', 'o', 'e', 'p', 4, 'j', 'o', 'e', 'h', 3, 'j', 'y', 'o', 4, 'j', 'y', 'o', 'g', 5, 'j', 'y', 'o', 'g', 'g', 5, 'j', 'y', 'o', 'g', 's', 4, 'j', 'y', 'o', 'n', 5, 'j', 'y', 'o', 'n', 'j', 5, 'j', 'y', 'o', 'n', 'h', 4, 'j', 'y', 'o', 'd', 4, 'j', 'y', 'o', 'l', 5, 'j', 'y', 'o', 'l', 'g', 5, 'j', 'y', 'o', 'l', 'm', 5, 'j', 'y', 'o', 'l', 'b', 5, 'j', 'y', 'o', 'l', 's', 5, 'j', 'y', 'o', 'l', 't', 5, 'j', 'y', 'o', 'l', 'p', 5, 'j', 'y', 'o', 'l', 'h', 4, 'j', 'y', 'o', 'm', 4, 'j', 'y', 'o', 'b', 5, 'j', 'y', 'o', 'b', 's', 4, 'j', 'y', 'o', 's', 5, 'j', 'y', 'o', 's', 's', 5, 'j', 'y', 'o', 'n', 'g', 4, 'j', 'y', 'o', 'j', 4, 'j', 'y', 'o', 'c', 4, 'j', 'y', 'o', 'k', 4, 'j', 'y', 'o', 't', 4, 'j', 'y', 'o', 'p', 4, 'j', 'y', 'o', 'h', 2, 'j', 'u', 3, 'j', 'u', 'g', 4, 'j', 'u', 'g', 'g', 4, 'j', 'u', 'g', 's', 3, 'j', 'u', 'n', 4, 'j', 'u', 'n', 'j', 4, 'j', 'u', 'n', 'h', 3, 'j', 'u', 'd', 3, 'j', 'u', 'l', 4, 'j', 'u', 'l', 'g', 4, 'j', 'u', 'l', 'm', 4, 'j', 'u', 'l', 'b', 4, 'j', 'u', 'l', 's', 4, 'j', 'u', 'l', 't', 4, 'j', 'u', 'l', 'p', 4, 'j', 'u', 'l', 'h', 3, 'j', 'u', 'm', 3, 'j', 'u', 'b', 4, 'j', 'u', 'b', 's', 3, 'j', 'u', 's', 4, 'j', 'u', 's', 's', 4, 'j', 'u', 'n', 'g', 3, 'j', 'u', 'j', 3, 'j', 'u', 'c', 3, 'j', 'u', 'k', 3, 'j', 'u', 't', 3, 'j', 'u', 'p', 3, 'j', 'u', 'h', 4, 'j', 'w', 'e', 'o', 5, 'j', 'w', 'e', 'o', 'g', 6, 'j', 'w', 'e', 'o', 'g', 'g', 6, 'j', 'w', 'e', 'o', 'g', 's', 5, 'j', 'w', 'e', 'o', 'n', 6, 'j', 'w', 'e', 'o', 'n', 'j', 6, 'j', 'w', 'e', 'o', 'n', 'h', 5, 'j', 'w', 'e', 'o', 'd', 5, 'j', 'w', 'e', 'o', 'l', 6, 'j', 'w', 'e', 'o', 'l', 'g', 6, 'j', 'w', 'e', 'o', 'l', 'm', 6, 'j', 'w', 'e', 'o', 'l', 'b', 6, 'j', 'w', 'e', 'o', 'l', 's', 6, 'j', 'w', 'e', 'o', 'l', 't', 6, 'j', 'w', 'e', 'o', 'l', 'p', 6, 'j', 'w', 'e', 'o', 'l', 'h', 5, 'j', 'w', 'e', 'o', 'm', 5, 'j', 'w', 'e', 'o', 'b', 6, 'j', 'w', 'e', 'o', 'b', 's', 5, 'j', 'w', 'e', 'o', 's', 6, 'j', 'w', 'e', 'o', 's', 's', 6, 'j', 'w', 'e', 'o', 'n', 'g', 5, 'j', 'w', 'e', 'o', 'j', 5, 'j', 'w', 'e', 'o', 'c', 5, 'j', 'w', 'e', 'o', 'k', 5, 'j', 'w', 'e', 'o', 't', 5, 'j', 'w', 'e', 'o', 'p', 5, 'j', 'w', 'e', 'o', 'h', 3, 'j', 'w', 'e', 4, 'j', 'w', 'e', 'g', 5, 'j', 'w', 'e', 'g', 'g', 5, 'j', 'w', 'e', 'g', 's', 4, 'j', 'w', 'e', 'n', 5, 'j', 'w', 'e', 'n', 'j', 5, 'j', 'w', 'e', 'n', 'h', 4, 'j', 'w', 'e', 'd', 4, 'j', 'w', 'e', 'l', 5, 'j', 'w', 'e', 'l', 'g', 5, 'j', 'w', 'e', 'l', 'm', 5, 'j', 'w', 'e', 'l', 'b', 5, 'j', 'w', 'e', 'l', 's', 5, 'j', 'w', 'e', 'l', 't', 5, 'j', 'w', 'e', 'l', 'p', 5, 'j', 'w', 'e', 'l', 'h', 4, 'j', 'w', 'e', 'm', 4, 'j', 'w', 'e', 'b', 5, 'j', 'w', 'e', 'b', 's', 4, 'j', 'w', 'e', 's', 5, 'j', 'w', 'e', 's', 's', 5, 'j', 'w', 'e', 'n', 'g', 4, 'j', 'w', 'e', 'j', 4, 'j', 'w', 'e', 'c', 4, 'j', 'w', 'e', 'k', 4, 'j', 'w', 'e', 't', 4, 'j', 'w', 'e', 'p', 4, 'j', 'w', 'e', 'h', 3, 'j', 'w', 'i', 4, 'j', 'w', 'i', 'g', 5, 'j', 'w', 'i', 'g', 'g', 5, 'j', 'w', 'i', 'g', 's', 4, 'j', 'w', 'i', 'n', 5, 'j', 'w', 'i', 'n', 'j', 5, 'j', 'w', 'i', 'n', 'h', 4, 'j', 'w', 'i', 'd', 4, 'j', 'w', 'i', 'l', 5, 'j', 'w', 'i', 'l', 'g', 5, 'j', 'w', 'i', 'l', 'm', 5, 'j', 'w', 'i', 'l', 'b', 5, 'j', 'w', 'i', 'l', 's', 5, 'j', 'w', 'i', 'l', 't', 5, 'j', 'w', 'i', 'l', 'p', 5, 'j', 'w', 'i', 'l', 'h', 4, 'j', 'w', 'i', 'm', 4, 'j', 'w', 'i', 'b', 5, 'j', 'w', 'i', 'b', 's', 4, 'j', 'w', 'i', 's', 5, 'j', 'w', 'i', 's', 's', 5, 'j', 'w', 'i', 'n', 'g', 4, 'j', 'w', 'i', 'j', 4, 'j', 'w', 'i', 'c', 4, 'j', 'w', 'i', 'k', 4, 'j', 'w', 'i', 't', 4, 'j', 'w', 'i', 'p', 4, 'j', 'w', 'i', 'h', 3, 'j', 'y', 'u', 4, 'j', 'y', 'u', 'g', 5, 'j', 'y', 'u', 'g', 'g', 5, 'j', 'y', 'u', 'g', 's', 4, 'j', 'y', 'u', 'n', 5, 'j', 'y', 'u', 'n', 'j', 5, 'j', 'y', 'u', 'n', 'h', 4, 'j', 'y', 'u', 'd', 4, 'j', 'y', 'u', 'l', 5, 'j', 'y', 'u', 'l', 'g', 5, 'j', 'y', 'u', 'l', 'm', 5, 'j', 'y', 'u', 'l', 'b', 5, 'j', 'y', 'u', 'l', 's', 5, 'j', 'y', 'u', 'l', 't', 5, 'j', 'y', 'u', 'l', 'p', 5, 'j', 'y', 'u', 'l', 'h', 4, 'j', 'y', 'u', 'm', 4, 'j', 'y', 'u', 'b', 5, 'j', 'y', 'u', 'b', 's', 4, 'j', 'y', 'u', 's', 5, 'j', 'y', 'u', 's', 's', 5, 'j', 'y', 'u', 'n', 'g', 4, 'j', 'y', 'u', 'j', 4, 'j', 'y', 'u', 'c', 4, 'j', 'y', 'u', 'k', 4, 'j', 'y', 'u', 't', 4, 'j', 'y', 'u', 'p', 4, 'j', 'y', 'u', 'h', 3, 'j', 'e', 'u', 4, 'j', 'e', 'u', 'g', 5, 'j', 'e', 'u', 'g', 'g', 5, 'j', 'e', 'u', 'g', 's', 4, 'j', 'e', 'u', 'n', 5, 'j', 'e', 'u', 'n', 'j', 5, 'j', 'e', 'u', 'n', 'h', 4, 'j', 'e', 'u', 'd', 4, 'j', 'e', 'u', 'l', 5, 'j', 'e', 'u', 'l', 'g', 5, 'j', 'e', 'u', 'l', 'm', 5, 'j', 'e', 'u', 'l', 'b', 5, 'j', 'e', 'u', 'l', 's', 5, 'j', 'e', 'u', 'l', 't', 5, 'j', 'e', 'u', 'l', 'p', 5, 'j', 'e', 'u', 'l', 'h', 4, 'j', 'e', 'u', 'm', 4, 'j', 'e', 'u', 'b', 5, 'j', 'e', 'u', 'b', 's', 4, 'j', 'e', 'u', 's', 5, 'j', 'e', 'u', 's', 's', 5, 'j', 'e', 'u', 'n', 'g', 4, 'j', 'e', 'u', 'j', 4, 'j', 'e', 'u', 'c', 4, 'j', 'e', 'u', 'k', 4, 'j', 'e', 'u', 't', 4, 'j', 'e', 'u', 'p', 4, 'j', 'e', 'u', 'h', 3, 'j', 'y', 'i', 4, 'j', 'y', 'i', 'g', 5, 'j', 'y', 'i', 'g', 'g', 5, 'j', 'y', 'i', 'g', 's', 4, 'j', 'y', 'i', 'n', 5, 'j', 'y', 'i', 'n', 'j', 5, 'j', 'y', 'i', 'n', 'h', 4, 'j', 'y', 'i', 'd', 4, 'j', 'y', 'i', 'l', 5, 'j', 'y', 'i', 'l', 'g', 5, 'j', 'y', 'i', 'l', 'm', 5, 'j', 'y', 'i', 'l', 'b', 5, 'j', 'y', 'i', 'l', 's', 5, 'j', 'y', 'i', 'l', 't', 5, 'j', 'y', 'i', 'l', 'p', 5, 'j', 'y', 'i', 'l', 'h', 4, 'j', 'y', 'i', 'm', 4, 'j', 'y', 'i', 'b', 5, 'j', 'y', 'i', 'b', 's', 4, 'j', 'y', 'i', 's', 5, 'j', 'y', 'i', 's', 's', 5, 'j', 'y', 'i', 'n', 'g', 4, 'j', 'y', 'i', 'j', 4, 'j', 'y', 'i', 'c', 4, 'j', 'y', 'i', 'k', 4, 'j', 'y', 'i', 't', 4, 'j', 'y', 'i', 'p', 4, 'j', 'y', 'i', 'h', 2, 'j', 'i', 3, 'j', 'i', 'g', 4, 'j', 'i', 'g', 'g', 4, 'j', 'i', 'g', 's', 3, 'j', 'i', 'n', 4, 'j', 'i', 'n', 'j', 4, 'j', 'i', 'n', 'h', 3, 'j', 'i', 'd', 3, 'j', 'i', 'l', 4, 'j', 'i', 'l', 'g', 4, 'j', 'i', 'l', 'm', 4, 'j', 'i', 'l', 'b', 4, 'j', 'i', 'l', 's', 4, 'j', 'i', 'l', 't', 4, 'j', 'i', 'l', 'p', 4, 'j', 'i', 'l', 'h', 3, 'j', 'i', 'm', 3, 'j', 'i', 'b', 4, 'j', 'i', 'b', 's', 3, 'j', 'i', 's', 4, 'j', 'i', 's', 's', 4, 'j', 'i', 'n', 'g', 3, 'j', 'i', 'j', 3, 'j', 'i', 'c', 3, 'j', 'i', 'k', 3, 'j', 'i', 't', 3, 'j', 'i', 'p', 3, 'j', 'i', 'h', 3, 'j', 'j', 'a', 4, 'j', 'j', 'a', 'g', 5, 'j', 'j', 'a', 'g', 'g', 5, 'j', 'j', 'a', 'g', 's', 4, 'j', 'j', 'a', 'n', 5, 'j', 'j', 'a', 'n', 'j', 5, 'j', 'j', 'a', 'n', 'h', 4, 'j', 'j', 'a', 'd', 4, 'j', 'j', 'a', 'l', 5, 'j', 'j', 'a', 'l', 'g', 5, 'j', 'j', 'a', 'l', 'm', 5, 'j', 'j', 'a', 'l', 'b', 5, 'j', 'j', 'a', 'l', 's', 5, 'j', 'j', 'a', 'l', 't', 5, 'j', 'j', 'a', 'l', 'p', 5, 'j', 'j', 'a', 'l', 'h', 4, 'j', 'j', 'a', 'm', 4, 'j', 'j', 'a', 'b', 5, 'j', 'j', 'a', 'b', 's', 4, 'j', 'j', 'a', 's', 5, 'j', 'j', 'a', 's', 's', 5, 'j', 'j', 'a', 'n', 'g', 4, 'j', 'j', 'a', 'j', 4, 'j', 'j', 'a', 'c', 4, 'j', 'j', 'a', 'k', 4, 'j', 'j', 'a', 't', 4, 'j', 'j', 'a', 'p', 4, 'j', 'j', 'a', 'h', 4, 'j', 'j', 'a', 'e', 5, 'j', 'j', 'a', 'e', 'g', 6, 'j', 'j', 'a', 'e', 'g', 'g', 6, 'j', 'j', 'a', 'e', 'g', 's', 5, 'j', 'j', 'a', 'e', 'n', 6, 'j', 'j', 'a', 'e', 'n', 'j', 6, 'j', 'j', 'a', 'e', 'n', 'h', 5, 'j', 'j', 'a', 'e', 'd', 5, 'j', 'j', 'a', 'e', 'l', 6, 'j', 'j', 'a', 'e', 'l', 'g', 6, 'j', 'j', 'a', 'e', 'l', 'm', 6, 'j', 'j', 'a', 'e', 'l', 'b', 6, 'j', 'j', 'a', 'e', 'l', 's', 6, 'j', 'j', 'a', 'e', 'l', 't', 6, 'j', 'j', 'a', 'e', 'l', 'p', 6, 'j', 'j', 'a', 'e', 'l', 'h', 5, 'j', 'j', 'a', 'e', 'm', 5, 'j', 'j', 'a', 'e', 'b', 6, 'j', 'j', 'a', 'e', 'b', 's', 5, 'j', 'j', 'a', 'e', 's', 6, 'j', 'j', 'a', 'e', 's', 's', 6, 'j', 'j', 'a', 'e', 'n', 'g', 5, 'j', 'j', 'a', 'e', 'j', 5, 'j', 'j', 'a', 'e', 'c', 5, 'j', 'j', 'a', 'e', 'k', 5, 'j', 'j', 'a', 'e', 't', 5, 'j', 'j', 'a', 'e', 'p', 5, 'j', 'j', 'a', 'e', 'h', 4, 'j', 'j', 'y', 'a', 5, 'j', 'j', 'y', 'a', 'g', 6, 'j', 'j', 'y', 'a', 'g', 'g', 6, 'j', 'j', 'y', 'a', 'g', 's', 5, 'j', 'j', 'y', 'a', 'n', 6, 'j', 'j', 'y', 'a', 'n', 'j', 6, 'j', 'j', 'y', 'a', 'n', 'h', 5, 'j', 'j', 'y', 'a', 'd', 5, 'j', 'j', 'y', 'a', 'l', 6, 'j', 'j', 'y', 'a', 'l', 'g', 6, 'j', 'j', 'y', 'a', 'l', 'm', 6, 'j', 'j', 'y', 'a', 'l', 'b', 6, 'j', 'j', 'y', 'a', 'l', 's', 6, 'j', 'j', 'y', 'a', 'l', 't', 6, 'j', 'j', 'y', 'a', 'l', 'p', 6, 'j', 'j', 'y', 'a', 'l', 'h', 5, 'j', 'j', 'y', 'a', 'm', 5, 'j', 'j', 'y', 'a', 'b', 6, 'j', 'j', 'y', 'a', 'b', 's', 5, 'j', 'j', 'y', 'a', 's', 6, 'j', 'j', 'y', 'a', 's', 's', 6, 'j', 'j', 'y', 'a', 'n', 'g', 5, 'j', 'j', 'y', 'a', 'j', 5, 'j', 'j', 'y', 'a', 'c', 5, 'j', 'j', 'y', 'a', 'k', 5, 'j', 'j', 'y', 'a', 't', 5, 'j', 'j', 'y', 'a', 'p', 5, 'j', 'j', 'y', 'a', 'h', 5, 'j', 'j', 'y', 'a', 'e', 6, 'j', 'j', 'y', 'a', 'e', 'g', 7, 'j', 'j', 'y', 'a', 'e', 'g', 'g', 7, 'j', 'j', 'y', 'a', 'e', 'g', 's', 6, 'j', 'j', 'y', 'a', 'e', 'n', 7, 'j', 'j', 'y', 'a', 'e', 'n', 'j', 7, 'j', 'j', 'y', 'a', 'e', 'n', 'h', 6, 'j', 'j', 'y', 'a', 'e', 'd', 6, 'j', 'j', 'y', 'a', 'e', 'l', 7, 'j', 'j', 'y', 'a', 'e', 'l', 'g', 7, 'j', 'j', 'y', 'a', 'e', 'l', 'm', 7, 'j', 'j', 'y', 'a', 'e', 'l', 'b', 7, 'j', 'j', 'y', 'a', 'e', 'l', 's', 7, 'j', 'j', 'y', 'a', 'e', 'l', 't', 7, 'j', 'j', 'y', 'a', 'e', 'l', 'p', 7, 'j', 'j', 'y', 'a', 'e', 'l', 'h', 6, 'j', 'j', 'y', 'a', 'e', 'm', 6, 'j', 'j', 'y', 'a', 'e', 'b', 7, 'j', 'j', 'y', 'a', 'e', 'b', 's', 6, 'j', 'j', 'y', 'a', 'e', 's', 7, 'j', 'j', 'y', 'a', 'e', 's', 's', 7, 'j', 'j', 'y', 'a', 'e', 'n', 'g', 6, 'j', 'j', 'y', 'a', 'e', 'j', 6, 'j', 'j', 'y', 'a', 'e', 'c', 6, 'j', 'j', 'y', 'a', 'e', 'k', 6, 'j', 'j', 'y', 'a', 'e', 't', 6, 'j', 'j', 'y', 'a', 'e', 'p', 6, 'j', 'j', 'y', 'a', 'e', 'h', 4, 'j', 'j', 'e', 'o', 5, 'j', 'j', 'e', 'o', 'g', 6, 'j', 'j', 'e', 'o', 'g', 'g', 6, 'j', 'j', 'e', 'o', 'g', 's', 5, 'j', 'j', 'e', 'o', 'n', 6, 'j', 'j', 'e', 'o', 'n', 'j', 6, 'j', 'j', 'e', 'o', 'n', 'h', 5, 'j', 'j', 'e', 'o', 'd', 5, 'j', 'j', 'e', 'o', 'l', 6, 'j', 'j', 'e', 'o', 'l', 'g', 6, 'j', 'j', 'e', 'o', 'l', 'm', 6, 'j', 'j', 'e', 'o', 'l', 'b', 6, 'j', 'j', 'e', 'o', 'l', 's', 6, 'j', 'j', 'e', 'o', 'l', 't', 6, 'j', 'j', 'e', 'o', 'l', 'p', 6, 'j', 'j', 'e', 'o', 'l', 'h', 5, 'j', 'j', 'e', 'o', 'm', 5, 'j', 'j', 'e', 'o', 'b', 6, 'j', 'j', 'e', 'o', 'b', 's', 5, 'j', 'j', 'e', 'o', 's', 6, 'j', 'j', 'e', 'o', 's', 's', 6, 'j', 'j', 'e', 'o', 'n', 'g', 5, 'j', 'j', 'e', 'o', 'j', 5, 'j', 'j', 'e', 'o', 'c', 5, 'j', 'j', 'e', 'o', 'k', 5, 'j', 'j', 'e', 'o', 't', 5, 'j', 'j', 'e', 'o', 'p', 5, 'j', 'j', 'e', 'o', 'h', 3, 'j', 'j', 'e', 4, 'j', 'j', 'e', 'g', 5, 'j', 'j', 'e', 'g', 'g', 5, 'j', 'j', 'e', 'g', 's', 4, 'j', 'j', 'e', 'n', 5, 'j', 'j', 'e', 'n', 'j', 5, 'j', 'j', 'e', 'n', 'h', 4, 'j', 'j', 'e', 'd', 4, 'j', 'j', 'e', 'l', 5, 'j', 'j', 'e', 'l', 'g', 5, 'j', 'j', 'e', 'l', 'm', 5, 'j', 'j', 'e', 'l', 'b', 5, 'j', 'j', 'e', 'l', 's', 5, 'j', 'j', 'e', 'l', 't', 5, 'j', 'j', 'e', 'l', 'p', 5, 'j', 'j', 'e', 'l', 'h', 4, 'j', 'j', 'e', 'm', 4, 'j', 'j', 'e', 'b', 5, 'j', 'j', 'e', 'b', 's', 4, 'j', 'j', 'e', 's', 5, 'j', 'j', 'e', 's', 's', 5, 'j', 'j', 'e', 'n', 'g', 4, 'j', 'j', 'e', 'j', 4, 'j', 'j', 'e', 'c', 4, 'j', 'j', 'e', 'k', 4, 'j', 'j', 'e', 't', 4, 'j', 'j', 'e', 'p', 4, 'j', 'j', 'e', 'h', 5, 'j', 'j', 'y', 'e', 'o', 6, 'j', 'j', 'y', 'e', 'o', 'g', 7, 'j', 'j', 'y', 'e', 'o', 'g', 'g', 7, 'j', 'j', 'y', 'e', 'o', 'g', 's', 6, 'j', 'j', 'y', 'e', 'o', 'n', 7, 'j', 'j', 'y', 'e', 'o', 'n', 'j', 7, 'j', 'j', 'y', 'e', 'o', 'n', 'h', 6, 'j', 'j', 'y', 'e', 'o', 'd', 6, 'j', 'j', 'y', 'e', 'o', 'l', 7, 'j', 'j', 'y', 'e', 'o', 'l', 'g', 7, 'j', 'j', 'y', 'e', 'o', 'l', 'm', 7, 'j', 'j', 'y', 'e', 'o', 'l', 'b', 7, 'j', 'j', 'y', 'e', 'o', 'l', 's', 7, 'j', 'j', 'y', 'e', 'o', 'l', 't', 7, 'j', 'j', 'y', 'e', 'o', 'l', 'p', 7, 'j', 'j', 'y', 'e', 'o', 'l', 'h', 6, 'j', 'j', 'y', 'e', 'o', 'm', 6, 'j', 'j', 'y', 'e', 'o', 'b', 7, 'j', 'j', 'y', 'e', 'o', 'b', 's', 6, 'j', 'j', 'y', 'e', 'o', 's', 7, 'j', 'j', 'y', 'e', 'o', 's', 's', 7, 'j', 'j', 'y', 'e', 'o', 'n', 'g', 6, 'j', 'j', 'y', 'e', 'o', 'j', 6, 'j', 'j', 'y', 'e', 'o', 'c', 6, 'j', 'j', 'y', 'e', 'o', 'k', 6, 'j', 'j', 'y', 'e', 'o', 't', 6, 'j', 'j', 'y', 'e', 'o', 'p', 6, 'j', 'j', 'y', 'e', 'o', 'h', 4, 'j', 'j', 'y', 'e', 5, 'j', 'j', 'y', 'e', 'g', 6, 'j', 'j', 'y', 'e', 'g', 'g', 6, 'j', 'j', 'y', 'e', 'g', 's', 5, 'j', 'j', 'y', 'e', 'n', 6, 'j', 'j', 'y', 'e', 'n', 'j', 6, 'j', 'j', 'y', 'e', 'n', 'h', 5, 'j', 'j', 'y', 'e', 'd', 5, 'j', 'j', 'y', 'e', 'l', 6, 'j', 'j', 'y', 'e', 'l', 'g', 6, 'j', 'j', 'y', 'e', 'l', 'm', 6, 'j', 'j', 'y', 'e', 'l', 'b', 6, 'j', 'j', 'y', 'e', 'l', 's', 6, 'j', 'j', 'y', 'e', 'l', 't', 6, 'j', 'j', 'y', 'e', 'l', 'p', 6, 'j', 'j', 'y', 'e', 'l', 'h', 5, 'j', 'j', 'y', 'e', 'm', 5, 'j', 'j', 'y', 'e', 'b', 6, 'j', 'j', 'y', 'e', 'b', 's', 5, 'j', 'j', 'y', 'e', 's', 6, 'j', 'j', 'y', 'e', 's', 's', 6, 'j', 'j', 'y', 'e', 'n', 'g', 5, 'j', 'j', 'y', 'e', 'j', 5, 'j', 'j', 'y', 'e', 'c', 5, 'j', 'j', 'y', 'e', 'k', 5, 'j', 'j', 'y', 'e', 't', 5, 'j', 'j', 'y', 'e', 'p', 5, 'j', 'j', 'y', 'e', 'h', 3, 'j', 'j', 'o', 4, 'j', 'j', 'o', 'g', 5, 'j', 'j', 'o', 'g', 'g', 5, 'j', 'j', 'o', 'g', 's', 4, 'j', 'j', 'o', 'n', 5, 'j', 'j', 'o', 'n', 'j', 5, 'j', 'j', 'o', 'n', 'h', 4, 'j', 'j', 'o', 'd', 4, 'j', 'j', 'o', 'l', 5, 'j', 'j', 'o', 'l', 'g', 5, 'j', 'j', 'o', 'l', 'm', 5, 'j', 'j', 'o', 'l', 'b', 5, 'j', 'j', 'o', 'l', 's', 5, 'j', 'j', 'o', 'l', 't', 5, 'j', 'j', 'o', 'l', 'p', 5, 'j', 'j', 'o', 'l', 'h', 4, 'j', 'j', 'o', 'm', 4, 'j', 'j', 'o', 'b', 5, 'j', 'j', 'o', 'b', 's', 4, 'j', 'j', 'o', 's', 5, 'j', 'j', 'o', 's', 's', 5, 'j', 'j', 'o', 'n', 'g', 4, 'j', 'j', 'o', 'j', 4, 'j', 'j', 'o', 'c', 4, 'j', 'j', 'o', 'k', 4, 'j', 'j', 'o', 't', 4, 'j', 'j', 'o', 'p', 4, 'j', 'j', 'o', 'h', 4, 'j', 'j', 'w', 'a', 5, 'j', 'j', 'w', 'a', 'g', 6, 'j', 'j', 'w', 'a', 'g', 'g', 6, 'j', 'j', 'w', 'a', 'g', 's', 5, 'j', 'j', 'w', 'a', 'n', 6, 'j', 'j', 'w', 'a', 'n', 'j', 6, 'j', 'j', 'w', 'a', 'n', 'h', 5, 'j', 'j', 'w', 'a', 'd', 5, 'j', 'j', 'w', 'a', 'l', 6, 'j', 'j', 'w', 'a', 'l', 'g', 6, 'j', 'j', 'w', 'a', 'l', 'm', 6, 'j', 'j', 'w', 'a', 'l', 'b', 6, 'j', 'j', 'w', 'a', 'l', 's', 6, 'j', 'j', 'w', 'a', 'l', 't', 6, 'j', 'j', 'w', 'a', 'l', 'p', 6, 'j', 'j', 'w', 'a', 'l', 'h', 5, 'j', 'j', 'w', 'a', 'm', 5, 'j', 'j', 'w', 'a', 'b', 6, 'j', 'j', 'w', 'a', 'b', 's', 5, 'j', 'j', 'w', 'a', 's', 6, 'j', 'j', 'w', 'a', 's', 's', 6, 'j', 'j', 'w', 'a', 'n', 'g', 5, 'j', 'j', 'w', 'a', 'j', 5, 'j', 'j', 'w', 'a', 'c', 5, 'j', 'j', 'w', 'a', 'k', 5, 'j', 'j', 'w', 'a', 't', 5, 'j', 'j', 'w', 'a', 'p', 5, 'j', 'j', 'w', 'a', 'h', 5, 'j', 'j', 'w', 'a', 'e', 6, 'j', 'j', 'w', 'a', 'e', 'g', 7, 'j', 'j', 'w', 'a', 'e', 'g', 'g', 7, 'j', 'j', 'w', 'a', 'e', 'g', 's', 6, 'j', 'j', 'w', 'a', 'e', 'n', 7, 'j', 'j', 'w', 'a', 'e', 'n', 'j', 7, 'j', 'j', 'w', 'a', 'e', 'n', 'h', 6, 'j', 'j', 'w', 'a', 'e', 'd', 6, 'j', 'j', 'w', 'a', 'e', 'l', 7, 'j', 'j', 'w', 'a', 'e', 'l', 'g', 7, 'j', 'j', 'w', 'a', 'e', 'l', 'm', 7, 'j', 'j', 'w', 'a', 'e', 'l', 'b', 7, 'j', 'j', 'w', 'a', 'e', 'l', 's', 7, 'j', 'j', 'w', 'a', 'e', 'l', 't', 7, 'j', 'j', 'w', 'a', 'e', 'l', 'p', 7, 'j', 'j', 'w', 'a', 'e', 'l', 'h', 6, 'j', 'j', 'w', 'a', 'e', 'm', 6, 'j', 'j', 'w', 'a', 'e', 'b', 7, 'j', 'j', 'w', 'a', 'e', 'b', 's', 6, 'j', 'j', 'w', 'a', 'e', 's', 7, 'j', 'j', 'w', 'a', 'e', 's', 's', 7, 'j', 'j', 'w', 'a', 'e', 'n', 'g', 6, 'j', 'j', 'w', 'a', 'e', 'j', 6, 'j', 'j', 'w', 'a', 'e', 'c', 6, 'j', 'j', 'w', 'a', 'e', 'k', 6, 'j', 'j', 'w', 'a', 'e', 't', 6, 'j', 'j', 'w', 'a', 'e', 'p', 6, 'j', 'j', 'w', 'a', 'e', 'h', 4, 'j', 'j', 'o', 'e', 5, 'j', 'j', 'o', 'e', 'g', 6, 'j', 'j', 'o', 'e', 'g', 'g', 6, 'j', 'j', 'o', 'e', 'g', 's', 5, 'j', 'j', 'o', 'e', 'n', 6, 'j', 'j', 'o', 'e', 'n', 'j', 6, 'j', 'j', 'o', 'e', 'n', 'h', 5, 'j', 'j', 'o', 'e', 'd', 5, 'j', 'j', 'o', 'e', 'l', 6, 'j', 'j', 'o', 'e', 'l', 'g', 6, 'j', 'j', 'o', 'e', 'l', 'm', 6, 'j', 'j', 'o', 'e', 'l', 'b', 6, 'j', 'j', 'o', 'e', 'l', 's', 6, 'j', 'j', 'o', 'e', 'l', 't', 6, 'j', 'j', 'o', 'e', 'l', 'p', 6, 'j', 'j', 'o', 'e', 'l', 'h', 5, 'j', 'j', 'o', 'e', 'm', 5, 'j', 'j', 'o', 'e', 'b', 6, 'j', 'j', 'o', 'e', 'b', 's', 5, 'j', 'j', 'o', 'e', 's', 6, 'j', 'j', 'o', 'e', 's', 's', 6, 'j', 'j', 'o', 'e', 'n', 'g', 5, 'j', 'j', 'o', 'e', 'j', 5, 'j', 'j', 'o', 'e', 'c', 5, 'j', 'j', 'o', 'e', 'k', 5, 'j', 'j', 'o', 'e', 't', 5, 'j', 'j', 'o', 'e', 'p', 5, 'j', 'j', 'o', 'e', 'h', 4, 'j', 'j', 'y', 'o', 5, 'j', 'j', 'y', 'o', 'g', 6, 'j', 'j', 'y', 'o', 'g', 'g', 6, 'j', 'j', 'y', 'o', 'g', 's', 5, 'j', 'j', 'y', 'o', 'n', 6, 'j', 'j', 'y', 'o', 'n', 'j', 6, 'j', 'j', 'y', 'o', 'n', 'h', 5, 'j', 'j', 'y', 'o', 'd', 5, 'j', 'j', 'y', 'o', 'l', 6, 'j', 'j', 'y', 'o', 'l', 'g', 6, 'j', 'j', 'y', 'o', 'l', 'm', 6, 'j', 'j', 'y', 'o', 'l', 'b', 6, 'j', 'j', 'y', 'o', 'l', 's', 6, 'j', 'j', 'y', 'o', 'l', 't', 6, 'j', 'j', 'y', 'o', 'l', 'p', 6, 'j', 'j', 'y', 'o', 'l', 'h', 5, 'j', 'j', 'y', 'o', 'm', 5, 'j', 'j', 'y', 'o', 'b', 6, 'j', 'j', 'y', 'o', 'b', 's', 5, 'j', 'j', 'y', 'o', 's', 6, 'j', 'j', 'y', 'o', 's', 's', 6, 'j', 'j', 'y', 'o', 'n', 'g', 5, 'j', 'j', 'y', 'o', 'j', 5, 'j', 'j', 'y', 'o', 'c', 5, 'j', 'j', 'y', 'o', 'k', 5, 'j', 'j', 'y', 'o', 't', 5, 'j', 'j', 'y', 'o', 'p', 5, 'j', 'j', 'y', 'o', 'h', 3, 'j', 'j', 'u', 4, 'j', 'j', 'u', 'g', 5, 'j', 'j', 'u', 'g', 'g', 5, 'j', 'j', 'u', 'g', 's', 4, 'j', 'j', 'u', 'n', 5, 'j', 'j', 'u', 'n', 'j', 5, 'j', 'j', 'u', 'n', 'h', 4, 'j', 'j', 'u', 'd', 4, 'j', 'j', 'u', 'l', 5, 'j', 'j', 'u', 'l', 'g', 5, 'j', 'j', 'u', 'l', 'm', 5, 'j', 'j', 'u', 'l', 'b', 5, 'j', 'j', 'u', 'l', 's', 5, 'j', 'j', 'u', 'l', 't', 5, 'j', 'j', 'u', 'l', 'p', 5, 'j', 'j', 'u', 'l', 'h', 4, 'j', 'j', 'u', 'm', 4, 'j', 'j', 'u', 'b', 5, 'j', 'j', 'u', 'b', 's', 4, 'j', 'j', 'u', 's', 5, 'j', 'j', 'u', 's', 's', 5, 'j', 'j', 'u', 'n', 'g', 4, 'j', 'j', 'u', 'j', 4, 'j', 'j', 'u', 'c', 4, 'j', 'j', 'u', 'k', 4, 'j', 'j', 'u', 't', 4, 'j', 'j', 'u', 'p', 4, 'j', 'j', 'u', 'h', 5, 'j', 'j', 'w', 'e', 'o', 6, 'j', 'j', 'w', 'e', 'o', 'g', 7, 'j', 'j', 'w', 'e', 'o', 'g', 'g', 7, 'j', 'j', 'w', 'e', 'o', 'g', 's', 6, 'j', 'j', 'w', 'e', 'o', 'n', 7, 'j', 'j', 'w', 'e', 'o', 'n', 'j', 7, 'j', 'j', 'w', 'e', 'o', 'n', 'h', 6, 'j', 'j', 'w', 'e', 'o', 'd', 6, 'j', 'j', 'w', 'e', 'o', 'l', 7, 'j', 'j', 'w', 'e', 'o', 'l', 'g', 7, 'j', 'j', 'w', 'e', 'o', 'l', 'm', 7, 'j', 'j', 'w', 'e', 'o', 'l', 'b', 7, 'j', 'j', 'w', 'e', 'o', 'l', 's', 7, 'j', 'j', 'w', 'e', 'o', 'l', 't', 7, 'j', 'j', 'w', 'e', 'o', 'l', 'p', 7, 'j', 'j', 'w', 'e', 'o', 'l', 'h', 6, 'j', 'j', 'w', 'e', 'o', 'm', 6, 'j', 'j', 'w', 'e', 'o', 'b', 7, 'j', 'j', 'w', 'e', 'o', 'b', 's', 6, 'j', 'j', 'w', 'e', 'o', 's', 7, 'j', 'j', 'w', 'e', 'o', 's', 's', 7, 'j', 'j', 'w', 'e', 'o', 'n', 'g', 6, 'j', 'j', 'w', 'e', 'o', 'j', 6, 'j', 'j', 'w', 'e', 'o', 'c', 6, 'j', 'j', 'w', 'e', 'o', 'k', 6, 'j', 'j', 'w', 'e', 'o', 't', 6, 'j', 'j', 'w', 'e', 'o', 'p', 6, 'j', 'j', 'w', 'e', 'o', 'h', 4, 'j', 'j', 'w', 'e', 5, 'j', 'j', 'w', 'e', 'g', 6, 'j', 'j', 'w', 'e', 'g', 'g', 6, 'j', 'j', 'w', 'e', 'g', 's', 5, 'j', 'j', 'w', 'e', 'n', 6, 'j', 'j', 'w', 'e', 'n', 'j', 6, 'j', 'j', 'w', 'e', 'n', 'h', 5, 'j', 'j', 'w', 'e', 'd', 5, 'j', 'j', 'w', 'e', 'l', 6, 'j', 'j', 'w', 'e', 'l', 'g', 6, 'j', 'j', 'w', 'e', 'l', 'm', 6, 'j', 'j', 'w', 'e', 'l', 'b', 6, 'j', 'j', 'w', 'e', 'l', 's', 6, 'j', 'j', 'w', 'e', 'l', 't', 6, 'j', 'j', 'w', 'e', 'l', 'p', 6, 'j', 'j', 'w', 'e', 'l', 'h', 5, 'j', 'j', 'w', 'e', 'm', 5, 'j', 'j', 'w', 'e', 'b', 6, 'j', 'j', 'w', 'e', 'b', 's', 5, 'j', 'j', 'w', 'e', 's', 6, 'j', 'j', 'w', 'e', 's', 's', 6, 'j', 'j', 'w', 'e', 'n', 'g', 5, 'j', 'j', 'w', 'e', 'j', 5, 'j', 'j', 'w', 'e', 'c', 5, 'j', 'j', 'w', 'e', 'k', 5, 'j', 'j', 'w', 'e', 't', 5, 'j', 'j', 'w', 'e', 'p', 5, 'j', 'j', 'w', 'e', 'h', 4, 'j', 'j', 'w', 'i', 5, 'j', 'j', 'w', 'i', 'g', 6, 'j', 'j', 'w', 'i', 'g', 'g', 6, 'j', 'j', 'w', 'i', 'g', 's', 5, 'j', 'j', 'w', 'i', 'n', 6, 'j', 'j', 'w', 'i', 'n', 'j', 6, 'j', 'j', 'w', 'i', 'n', 'h', 5, 'j', 'j', 'w', 'i', 'd', 5, 'j', 'j', 'w', 'i', 'l', 6, 'j', 'j', 'w', 'i', 'l', 'g', 6, 'j', 'j', 'w', 'i', 'l', 'm', 6, 'j', 'j', 'w', 'i', 'l', 'b', 6, 'j', 'j', 'w', 'i', 'l', 's', 6, 'j', 'j', 'w', 'i', 'l', 't', 6, 'j', 'j', 'w', 'i', 'l', 'p', 6, 'j', 'j', 'w', 'i', 'l', 'h', 5, 'j', 'j', 'w', 'i', 'm', 5, 'j', 'j', 'w', 'i', 'b', 6, 'j', 'j', 'w', 'i', 'b', 's', 5, 'j', 'j', 'w', 'i', 's', 6, 'j', 'j', 'w', 'i', 's', 's', 6, 'j', 'j', 'w', 'i', 'n', 'g', 5, 'j', 'j', 'w', 'i', 'j', 5, 'j', 'j', 'w', 'i', 'c', 5, 'j', 'j', 'w', 'i', 'k', 5, 'j', 'j', 'w', 'i', 't', 5, 'j', 'j', 'w', 'i', 'p', 5, 'j', 'j', 'w', 'i', 'h', 4, 'j', 'j', 'y', 'u', 5, 'j', 'j', 'y', 'u', 'g', 6, 'j', 'j', 'y', 'u', 'g', 'g', 6, 'j', 'j', 'y', 'u', 'g', 's', 5, 'j', 'j', 'y', 'u', 'n', 6, 'j', 'j', 'y', 'u', 'n', 'j', 6, 'j', 'j', 'y', 'u', 'n', 'h', 5, 'j', 'j', 'y', 'u', 'd', 5, 'j', 'j', 'y', 'u', 'l', 6, 'j', 'j', 'y', 'u', 'l', 'g', 6, 'j', 'j', 'y', 'u', 'l', 'm', 6, 'j', 'j', 'y', 'u', 'l', 'b', 6, 'j', 'j', 'y', 'u', 'l', 's', 6, 'j', 'j', 'y', 'u', 'l', 't', 6, 'j', 'j', 'y', 'u', 'l', 'p', 6, 'j', 'j', 'y', 'u', 'l', 'h', 5, 'j', 'j', 'y', 'u', 'm', 5, 'j', 'j', 'y', 'u', 'b', 6, 'j', 'j', 'y', 'u', 'b', 's', 5, 'j', 'j', 'y', 'u', 's', 6, 'j', 'j', 'y', 'u', 's', 's', 6, 'j', 'j', 'y', 'u', 'n', 'g', 5, 'j', 'j', 'y', 'u', 'j', 5, 'j', 'j', 'y', 'u', 'c', 5, 'j', 'j', 'y', 'u', 'k', 5, 'j', 'j', 'y', 'u', 't', 5, 'j', 'j', 'y', 'u', 'p', 5, 'j', 'j', 'y', 'u', 'h', 4, 'j', 'j', 'e', 'u', 5, 'j', 'j', 'e', 'u', 'g', 6, 'j', 'j', 'e', 'u', 'g', 'g', 6, 'j', 'j', 'e', 'u', 'g', 's', 5, 'j', 'j', 'e', 'u', 'n', 6, 'j', 'j', 'e', 'u', 'n', 'j', 6, 'j', 'j', 'e', 'u', 'n', 'h', 5, 'j', 'j', 'e', 'u', 'd', 5, 'j', 'j', 'e', 'u', 'l', 6, 'j', 'j', 'e', 'u', 'l', 'g', 6, 'j', 'j', 'e', 'u', 'l', 'm', 6, 'j', 'j', 'e', 'u', 'l', 'b', 6, 'j', 'j', 'e', 'u', 'l', 's', 6, 'j', 'j', 'e', 'u', 'l', 't', 6, 'j', 'j', 'e', 'u', 'l', 'p', 6, 'j', 'j', 'e', 'u', 'l', 'h', 5, 'j', 'j', 'e', 'u', 'm', 5, 'j', 'j', 'e', 'u', 'b', 6, 'j', 'j', 'e', 'u', 'b', 's', 5, 'j', 'j', 'e', 'u', 's', 6, 'j', 'j', 'e', 'u', 's', 's', 6, 'j', 'j', 'e', 'u', 'n', 'g', 5, 'j', 'j', 'e', 'u', 'j', 5, 'j', 'j', 'e', 'u', 'c', 5, 'j', 'j', 'e', 'u', 'k', 5, 'j', 'j', 'e', 'u', 't', 5, 'j', 'j', 'e', 'u', 'p', 5, 'j', 'j', 'e', 'u', 'h', 4, 'j', 'j', 'y', 'i', 5, 'j', 'j', 'y', 'i', 'g', 6, 'j', 'j', 'y', 'i', 'g', 'g', 6, 'j', 'j', 'y', 'i', 'g', 's', 5, 'j', 'j', 'y', 'i', 'n', 6, 'j', 'j', 'y', 'i', 'n', 'j', 6, 'j', 'j', 'y', 'i', 'n', 'h', 5, 'j', 'j', 'y', 'i', 'd', 5, 'j', 'j', 'y', 'i', 'l', 6, 'j', 'j', 'y', 'i', 'l', 'g', 6, 'j', 'j', 'y', 'i', 'l', 'm', 6, 'j', 'j', 'y', 'i', 'l', 'b', 6, 'j', 'j', 'y', 'i', 'l', 's', 6, 'j', 'j', 'y', 'i', 'l', 't', 6, 'j', 'j', 'y', 'i', 'l', 'p', 6, 'j', 'j', 'y', 'i', 'l', 'h', 5, 'j', 'j', 'y', 'i', 'm', 5, 'j', 'j', 'y', 'i', 'b', 6, 'j', 'j', 'y', 'i', 'b', 's', 5, 'j', 'j', 'y', 'i', 's', 6, 'j', 'j', 'y', 'i', 's', 's', 6, 'j', 'j', 'y', 'i', 'n', 'g', 5, 'j', 'j', 'y', 'i', 'j', 5, 'j', 'j', 'y', 'i', 'c', 5, 'j', 'j', 'y', 'i', 'k', 5, 'j', 'j', 'y', 'i', 't', 5, 'j', 'j', 'y', 'i', 'p', 5, 'j', 'j', 'y', 'i', 'h', 3, 'j', 'j', 'i', 4, 'j', 'j', 'i', 'g', 5, 'j', 'j', 'i', 'g', 'g', 5, 'j', 'j', 'i', 'g', 's', 4, 'j', 'j', 'i', 'n', 5, 'j', 'j', 'i', 'n', 'j', 5, 'j', 'j', 'i', 'n', 'h', 4, 'j', 'j', 'i', 'd', 4, 'j', 'j', 'i', 'l', 5, 'j', 'j', 'i', 'l', 'g', 5, 'j', 'j', 'i', 'l', 'm', 5, 'j', 'j', 'i', 'l', 'b', 5, 'j', 'j', 'i', 'l', 's', 5, 'j', 'j', 'i', 'l', 't', 5, 'j', 'j', 'i', 'l', 'p', 5, 'j', 'j', 'i', 'l', 'h', 4, 'j', 'j', 'i', 'm', 4, 'j', 'j', 'i', 'b', 5, 'j', 'j', 'i', 'b', 's', 4, 'j', 'j', 'i', 's', 5, 'j', 'j', 'i', 's', 's', 5, 'j', 'j', 'i', 'n', 'g', 4, 'j', 'j', 'i', 'j', 4, 'j', 'j', 'i', 'c', 4, 'j', 'j', 'i', 'k', 4, 'j', 'j', 'i', 't', 4, 'j', 'j', 'i', 'p', 4, 'j', 'j', 'i', 'h', 2, 'c', 'a', 3, 'c', 'a', 'g', 4, 'c', 'a', 'g', 'g', 4, 'c', 'a', 'g', 's', 3, 'c', 'a', 'n', 4, 'c', 'a', 'n', 'j', 4, 'c', 'a', 'n', 'h', 3, 'c', 'a', 'd', 3, 'c', 'a', 'l', 4, 'c', 'a', 'l', 'g', 4, 'c', 'a', 'l', 'm', 4, 'c', 'a', 'l', 'b', 4, 'c', 'a', 'l', 's', 4, 'c', 'a', 'l', 't', 4, 'c', 'a', 'l', 'p', 4, 'c', 'a', 'l', 'h', 3, 'c', 'a', 'm', 3, 'c', 'a', 'b', 4, 'c', 'a', 'b', 's', 3, 'c', 'a', 's', 4, 'c', 'a', 's', 's', 4, 'c', 'a', 'n', 'g', 3, 'c', 'a', 'j', 3, 'c', 'a', 'c', 3, 'c', 'a', 'k', 3, 'c', 'a', 't', 3, 'c', 'a', 'p', 3, 'c', 'a', 'h', 3, 'c', 'a', 'e', 4, 'c', 'a', 'e', 'g', 5, 'c', 'a', 'e', 'g', 'g', 5, 'c', 'a', 'e', 'g', 's', 4, 'c', 'a', 'e', 'n', 5, 'c', 'a', 'e', 'n', 'j', 5, 'c', 'a', 'e', 'n', 'h', 4, 'c', 'a', 'e', 'd', 4, 'c', 'a', 'e', 'l', 5, 'c', 'a', 'e', 'l', 'g', 5, 'c', 'a', 'e', 'l', 'm', 5, 'c', 'a', 'e', 'l', 'b', 5, 'c', 'a', 'e', 'l', 's', 5, 'c', 'a', 'e', 'l', 't', 5, 'c', 'a', 'e', 'l', 'p', 5, 'c', 'a', 'e', 'l', 'h', 4, 'c', 'a', 'e', 'm', 4, 'c', 'a', 'e', 'b', 5, 'c', 'a', 'e', 'b', 's', 4, 'c', 'a', 'e', 's', 5, 'c', 'a', 'e', 's', 's', 5, 'c', 'a', 'e', 'n', 'g', 4, 'c', 'a', 'e', 'j', 4, 'c', 'a', 'e', 'c', 4, 'c', 'a', 'e', 'k', 4, 'c', 'a', 'e', 't', 4, 'c', 'a', 'e', 'p', 4, 'c', 'a', 'e', 'h', 3, 'c', 'y', 'a', 4, 'c', 'y', 'a', 'g', 5, 'c', 'y', 'a', 'g', 'g', 5, 'c', 'y', 'a', 'g', 's', 4, 'c', 'y', 'a', 'n', 5, 'c', 'y', 'a', 'n', 'j', 5, 'c', 'y', 'a', 'n', 'h', 4, 'c', 'y', 'a', 'd', 4, 'c', 'y', 'a', 'l', 5, 'c', 'y', 'a', 'l', 'g', 5, 'c', 'y', 'a', 'l', 'm', 5, 'c', 'y', 'a', 'l', 'b', 5, 'c', 'y', 'a', 'l', 's', 5, 'c', 'y', 'a', 'l', 't', 5, 'c', 'y', 'a', 'l', 'p', 5, 'c', 'y', 'a', 'l', 'h', 4, 'c', 'y', 'a', 'm', 4, 'c', 'y', 'a', 'b', 5, 'c', 'y', 'a', 'b', 's', 4, 'c', 'y', 'a', 's', 5, 'c', 'y', 'a', 's', 's', 5, 'c', 'y', 'a', 'n', 'g', 4, 'c', 'y', 'a', 'j', 4, 'c', 'y', 'a', 'c', 4, 'c', 'y', 'a', 'k', 4, 'c', 'y', 'a', 't', 4, 'c', 'y', 'a', 'p', 4, 'c', 'y', 'a', 'h', 4, 'c', 'y', 'a', 'e', 5, 'c', 'y', 'a', 'e', 'g', 6, 'c', 'y', 'a', 'e', 'g', 'g', 6, 'c', 'y', 'a', 'e', 'g', 's', 5, 'c', 'y', 'a', 'e', 'n', 6, 'c', 'y', 'a', 'e', 'n', 'j', 6, 'c', 'y', 'a', 'e', 'n', 'h', 5, 'c', 'y', 'a', 'e', 'd', 5, 'c', 'y', 'a', 'e', 'l', 6, 'c', 'y', 'a', 'e', 'l', 'g', 6, 'c', 'y', 'a', 'e', 'l', 'm', 6, 'c', 'y', 'a', 'e', 'l', 'b', 6, 'c', 'y', 'a', 'e', 'l', 's', 6, 'c', 'y', 'a', 'e', 'l', 't', 6, 'c', 'y', 'a', 'e', 'l', 'p', 6, 'c', 'y', 'a', 'e', 'l', 'h', 5, 'c', 'y', 'a', 'e', 'm', 5, 'c', 'y', 'a', 'e', 'b', 6, 'c', 'y', 'a', 'e', 'b', 's', 5, 'c', 'y', 'a', 'e', 's', 6, 'c', 'y', 'a', 'e', 's', 's', 6, 'c', 'y', 'a', 'e', 'n', 'g', 5, 'c', 'y', 'a', 'e', 'j', 5, 'c', 'y', 'a', 'e', 'c', 5, 'c', 'y', 'a', 'e', 'k', 5, 'c', 'y', 'a', 'e', 't', 5, 'c', 'y', 'a', 'e', 'p', 5, 'c', 'y', 'a', 'e', 'h', 3, 'c', 'e', 'o', 4, 'c', 'e', 'o', 'g', 5, 'c', 'e', 'o', 'g', 'g', 5, 'c', 'e', 'o', 'g', 's', 4, 'c', 'e', 'o', 'n', 5, 'c', 'e', 'o', 'n', 'j', 5, 'c', 'e', 'o', 'n', 'h', 4, 'c', 'e', 'o', 'd', 4, 'c', 'e', 'o', 'l', 5, 'c', 'e', 'o', 'l', 'g', 5, 'c', 'e', 'o', 'l', 'm', 5, 'c', 'e', 'o', 'l', 'b', 5, 'c', 'e', 'o', 'l', 's', 5, 'c', 'e', 'o', 'l', 't', 5, 'c', 'e', 'o', 'l', 'p', 5, 'c', 'e', 'o', 'l', 'h', 4, 'c', 'e', 'o', 'm', 4, 'c', 'e', 'o', 'b', 5, 'c', 'e', 'o', 'b', 's', 4, 'c', 'e', 'o', 's', 5, 'c', 'e', 'o', 's', 's', 5, 'c', 'e', 'o', 'n', 'g', 4, 'c', 'e', 'o', 'j', 4, 'c', 'e', 'o', 'c', 4, 'c', 'e', 'o', 'k', 4, 'c', 'e', 'o', 't', 4, 'c', 'e', 'o', 'p', 4, 'c', 'e', 'o', 'h', 2, 'c', 'e', 3, 'c', 'e', 'g', 4, 'c', 'e', 'g', 'g', 4, 'c', 'e', 'g', 's', 3, 'c', 'e', 'n', 4, 'c', 'e', 'n', 'j', 4, 'c', 'e', 'n', 'h', 3, 'c', 'e', 'd', 3, 'c', 'e', 'l', 4, 'c', 'e', 'l', 'g', 4, 'c', 'e', 'l', 'm', 4, 'c', 'e', 'l', 'b', 4, 'c', 'e', 'l', 's', 4, 'c', 'e', 'l', 't', 4, 'c', 'e', 'l', 'p', 4, 'c', 'e', 'l', 'h', 3, 'c', 'e', 'm', 3, 'c', 'e', 'b', 4, 'c', 'e', 'b', 's', 3, 'c', 'e', 's', 4, 'c', 'e', 's', 's', 4, 'c', 'e', 'n', 'g', 3, 'c', 'e', 'j', 3, 'c', 'e', 'c', 3, 'c', 'e', 'k', 3, 'c', 'e', 't', 3, 'c', 'e', 'p', 3, 'c', 'e', 'h', 4, 'c', 'y', 'e', 'o', 5, 'c', 'y', 'e', 'o', 'g', 6, 'c', 'y', 'e', 'o', 'g', 'g', 6, 'c', 'y', 'e', 'o', 'g', 's', 5, 'c', 'y', 'e', 'o', 'n', 6, 'c', 'y', 'e', 'o', 'n', 'j', 6, 'c', 'y', 'e', 'o', 'n', 'h', 5, 'c', 'y', 'e', 'o', 'd', 5, 'c', 'y', 'e', 'o', 'l', 6, 'c', 'y', 'e', 'o', 'l', 'g', 6, 'c', 'y', 'e', 'o', 'l', 'm', 6, 'c', 'y', 'e', 'o', 'l', 'b', 6, 'c', 'y', 'e', 'o', 'l', 's', 6, 'c', 'y', 'e', 'o', 'l', 't', 6, 'c', 'y', 'e', 'o', 'l', 'p', 6, 'c', 'y', 'e', 'o', 'l', 'h', 5, 'c', 'y', 'e', 'o', 'm', 5, 'c', 'y', 'e', 'o', 'b', 6, 'c', 'y', 'e', 'o', 'b', 's', 5, 'c', 'y', 'e', 'o', 's', 6, 'c', 'y', 'e', 'o', 's', 's', 6, 'c', 'y', 'e', 'o', 'n', 'g', 5, 'c', 'y', 'e', 'o', 'j', 5, 'c', 'y', 'e', 'o', 'c', 5, 'c', 'y', 'e', 'o', 'k', 5, 'c', 'y', 'e', 'o', 't', 5, 'c', 'y', 'e', 'o', 'p', 5, 'c', 'y', 'e', 'o', 'h', 3, 'c', 'y', 'e', 4, 'c', 'y', 'e', 'g', 5, 'c', 'y', 'e', 'g', 'g', 5, 'c', 'y', 'e', 'g', 's', 4, 'c', 'y', 'e', 'n', 5, 'c', 'y', 'e', 'n', 'j', 5, 'c', 'y', 'e', 'n', 'h', 4, 'c', 'y', 'e', 'd', 4, 'c', 'y', 'e', 'l', 5, 'c', 'y', 'e', 'l', 'g', 5, 'c', 'y', 'e', 'l', 'm', 5, 'c', 'y', 'e', 'l', 'b', 5, 'c', 'y', 'e', 'l', 's', 5, 'c', 'y', 'e', 'l', 't', 5, 'c', 'y', 'e', 'l', 'p', 5, 'c', 'y', 'e', 'l', 'h', 4, 'c', 'y', 'e', 'm', 4, 'c', 'y', 'e', 'b', 5, 'c', 'y', 'e', 'b', 's', 4, 'c', 'y', 'e', 's', 5, 'c', 'y', 'e', 's', 's', 5, 'c', 'y', 'e', 'n', 'g', 4, 'c', 'y', 'e', 'j', 4, 'c', 'y', 'e', 'c', 4, 'c', 'y', 'e', 'k', 4, 'c', 'y', 'e', 't', 4, 'c', 'y', 'e', 'p', 4, 'c', 'y', 'e', 'h', 2, 'c', 'o', 3, 'c', 'o', 'g', 4, 'c', 'o', 'g', 'g', 4, 'c', 'o', 'g', 's', 3, 'c', 'o', 'n', 4, 'c', 'o', 'n', 'j', 4, 'c', 'o', 'n', 'h', 3, 'c', 'o', 'd', 3, 'c', 'o', 'l', 4, 'c', 'o', 'l', 'g', 4, 'c', 'o', 'l', 'm', 4, 'c', 'o', 'l', 'b', 4, 'c', 'o', 'l', 's', 4, 'c', 'o', 'l', 't', 4, 'c', 'o', 'l', 'p', 4, 'c', 'o', 'l', 'h', 3, 'c', 'o', 'm', 3, 'c', 'o', 'b', 4, 'c', 'o', 'b', 's', 3, 'c', 'o', 's', 4, 'c', 'o', 's', 's', 4, 'c', 'o', 'n', 'g', 3, 'c', 'o', 'j', 3, 'c', 'o', 'c', 3, 'c', 'o', 'k', 3, 'c', 'o', 't', 3, 'c', 'o', 'p', 3, 'c', 'o', 'h', 3, 'c', 'w', 'a', 4, 'c', 'w', 'a', 'g', 5, 'c', 'w', 'a', 'g', 'g', 5, 'c', 'w', 'a', 'g', 's', 4, 'c', 'w', 'a', 'n', 5, 'c', 'w', 'a', 'n', 'j', 5, 'c', 'w', 'a', 'n', 'h', 4, 'c', 'w', 'a', 'd', 4, 'c', 'w', 'a', 'l', 5, 'c', 'w', 'a', 'l', 'g', 5, 'c', 'w', 'a', 'l', 'm', 5, 'c', 'w', 'a', 'l', 'b', 5, 'c', 'w', 'a', 'l', 's', 5, 'c', 'w', 'a', 'l', 't', 5, 'c', 'w', 'a', 'l', 'p', 5, 'c', 'w', 'a', 'l', 'h', 4, 'c', 'w', 'a', 'm', 4, 'c', 'w', 'a', 'b', 5, 'c', 'w', 'a', 'b', 's', 4, 'c', 'w', 'a', 's', 5, 'c', 'w', 'a', 's', 's', 5, 'c', 'w', 'a', 'n', 'g', 4, 'c', 'w', 'a', 'j', 4, 'c', 'w', 'a', 'c', 4, 'c', 'w', 'a', 'k', 4, 'c', 'w', 'a', 't', 4, 'c', 'w', 'a', 'p', 4, 'c', 'w', 'a', 'h', 4, 'c', 'w', 'a', 'e', 5, 'c', 'w', 'a', 'e', 'g', 6, 'c', 'w', 'a', 'e', 'g', 'g', 6, 'c', 'w', 'a', 'e', 'g', 's', 5, 'c', 'w', 'a', 'e', 'n', 6, 'c', 'w', 'a', 'e', 'n', 'j', 6, 'c', 'w', 'a', 'e', 'n', 'h', 5, 'c', 'w', 'a', 'e', 'd', 5, 'c', 'w', 'a', 'e', 'l', 6, 'c', 'w', 'a', 'e', 'l', 'g', 6, 'c', 'w', 'a', 'e', 'l', 'm', 6, 'c', 'w', 'a', 'e', 'l', 'b', 6, 'c', 'w', 'a', 'e', 'l', 's', 6, 'c', 'w', 'a', 'e', 'l', 't', 6, 'c', 'w', 'a', 'e', 'l', 'p', 6, 'c', 'w', 'a', 'e', 'l', 'h', 5, 'c', 'w', 'a', 'e', 'm', 5, 'c', 'w', 'a', 'e', 'b', 6, 'c', 'w', 'a', 'e', 'b', 's', 5, 'c', 'w', 'a', 'e', 's', 6, 'c', 'w', 'a', 'e', 's', 's', 6, 'c', 'w', 'a', 'e', 'n', 'g', 5, 'c', 'w', 'a', 'e', 'j', 5, 'c', 'w', 'a', 'e', 'c', 5, 'c', 'w', 'a', 'e', 'k', 5, 'c', 'w', 'a', 'e', 't', 5, 'c', 'w', 'a', 'e', 'p', 5, 'c', 'w', 'a', 'e', 'h', 3, 'c', 'o', 'e', 4, 'c', 'o', 'e', 'g', 5, 'c', 'o', 'e', 'g', 'g', 5, 'c', 'o', 'e', 'g', 's', 4, 'c', 'o', 'e', 'n', 5, 'c', 'o', 'e', 'n', 'j', 5, 'c', 'o', 'e', 'n', 'h', 4, 'c', 'o', 'e', 'd', 4, 'c', 'o', 'e', 'l', 5, 'c', 'o', 'e', 'l', 'g', 5, 'c', 'o', 'e', 'l', 'm', 5, 'c', 'o', 'e', 'l', 'b', 5, 'c', 'o', 'e', 'l', 's', 5, 'c', 'o', 'e', 'l', 't', 5, 'c', 'o', 'e', 'l', 'p', 5, 'c', 'o', 'e', 'l', 'h', 4, 'c', 'o', 'e', 'm', 4, 'c', 'o', 'e', 'b', 5, 'c', 'o', 'e', 'b', 's', 4, 'c', 'o', 'e', 's', 5, 'c', 'o', 'e', 's', 's', 5, 'c', 'o', 'e', 'n', 'g', 4, 'c', 'o', 'e', 'j', 4, 'c', 'o', 'e', 'c', 4, 'c', 'o', 'e', 'k', 4, 'c', 'o', 'e', 't', 4, 'c', 'o', 'e', 'p', 4, 'c', 'o', 'e', 'h', 3, 'c', 'y', 'o', 4, 'c', 'y', 'o', 'g', 5, 'c', 'y', 'o', 'g', 'g', 5, 'c', 'y', 'o', 'g', 's', 4, 'c', 'y', 'o', 'n', 5, 'c', 'y', 'o', 'n', 'j', 5, 'c', 'y', 'o', 'n', 'h', 4, 'c', 'y', 'o', 'd', 4, 'c', 'y', 'o', 'l', 5, 'c', 'y', 'o', 'l', 'g', 5, 'c', 'y', 'o', 'l', 'm', 5, 'c', 'y', 'o', 'l', 'b', 5, 'c', 'y', 'o', 'l', 's', 5, 'c', 'y', 'o', 'l', 't', 5, 'c', 'y', 'o', 'l', 'p', 5, 'c', 'y', 'o', 'l', 'h', 4, 'c', 'y', 'o', 'm', 4, 'c', 'y', 'o', 'b', 5, 'c', 'y', 'o', 'b', 's', 4, 'c', 'y', 'o', 's', 5, 'c', 'y', 'o', 's', 's', 5, 'c', 'y', 'o', 'n', 'g', 4, 'c', 'y', 'o', 'j', 4, 'c', 'y', 'o', 'c', 4, 'c', 'y', 'o', 'k', 4, 'c', 'y', 'o', 't', 4, 'c', 'y', 'o', 'p', 4, 'c', 'y', 'o', 'h', 2, 'c', 'u', 3, 'c', 'u', 'g', 4, 'c', 'u', 'g', 'g', 4, 'c', 'u', 'g', 's', 3, 'c', 'u', 'n', 4, 'c', 'u', 'n', 'j', 4, 'c', 'u', 'n', 'h', 3, 'c', 'u', 'd', 3, 'c', 'u', 'l', 4, 'c', 'u', 'l', 'g', 4, 'c', 'u', 'l', 'm', 4, 'c', 'u', 'l', 'b', 4, 'c', 'u', 'l', 's', 4, 'c', 'u', 'l', 't', 4, 'c', 'u', 'l', 'p', 4, 'c', 'u', 'l', 'h', 3, 'c', 'u', 'm', 3, 'c', 'u', 'b', 4, 'c', 'u', 'b', 's', 3, 'c', 'u', 's', 4, 'c', 'u', 's', 's', 4, 'c', 'u', 'n', 'g', 3, 'c', 'u', 'j', 3, 'c', 'u', 'c', 3, 'c', 'u', 'k', 3, 'c', 'u', 't', 3, 'c', 'u', 'p', 3, 'c', 'u', 'h', 4, 'c', 'w', 'e', 'o', 5, 'c', 'w', 'e', 'o', 'g', 6, 'c', 'w', 'e', 'o', 'g', 'g', 6, 'c', 'w', 'e', 'o', 'g', 's', 5, 'c', 'w', 'e', 'o', 'n', 6, 'c', 'w', 'e', 'o', 'n', 'j', 6, 'c', 'w', 'e', 'o', 'n', 'h', 5, 'c', 'w', 'e', 'o', 'd', 5, 'c', 'w', 'e', 'o', 'l', 6, 'c', 'w', 'e', 'o', 'l', 'g', 6, 'c', 'w', 'e', 'o', 'l', 'm', 6, 'c', 'w', 'e', 'o', 'l', 'b', 6, 'c', 'w', 'e', 'o', 'l', 's', 6, 'c', 'w', 'e', 'o', 'l', 't', 6, 'c', 'w', 'e', 'o', 'l', 'p', 6, 'c', 'w', 'e', 'o', 'l', 'h', 5, 'c', 'w', 'e', 'o', 'm', 5, 'c', 'w', 'e', 'o', 'b', 6, 'c', 'w', 'e', 'o', 'b', 's', 5, 'c', 'w', 'e', 'o', 's', 6, 'c', 'w', 'e', 'o', 's', 's', 6, 'c', 'w', 'e', 'o', 'n', 'g', 5, 'c', 'w', 'e', 'o', 'j', 5, 'c', 'w', 'e', 'o', 'c', 5, 'c', 'w', 'e', 'o', 'k', 5, 'c', 'w', 'e', 'o', 't', 5, 'c', 'w', 'e', 'o', 'p', 5, 'c', 'w', 'e', 'o', 'h', 3, 'c', 'w', 'e', 4, 'c', 'w', 'e', 'g', 5, 'c', 'w', 'e', 'g', 'g', 5, 'c', 'w', 'e', 'g', 's', 4, 'c', 'w', 'e', 'n', 5, 'c', 'w', 'e', 'n', 'j', 5, 'c', 'w', 'e', 'n', 'h', 4, 'c', 'w', 'e', 'd', 4, 'c', 'w', 'e', 'l', 5, 'c', 'w', 'e', 'l', 'g', 5, 'c', 'w', 'e', 'l', 'm', 5, 'c', 'w', 'e', 'l', 'b', 5, 'c', 'w', 'e', 'l', 's', 5, 'c', 'w', 'e', 'l', 't', 5, 'c', 'w', 'e', 'l', 'p', 5, 'c', 'w', 'e', 'l', 'h', 4, 'c', 'w', 'e', 'm', 4, 'c', 'w', 'e', 'b', 5, 'c', 'w', 'e', 'b', 's', 4, 'c', 'w', 'e', 's', 5, 'c', 'w', 'e', 's', 's', 5, 'c', 'w', 'e', 'n', 'g', 4, 'c', 'w', 'e', 'j', 4, 'c', 'w', 'e', 'c', 4, 'c', 'w', 'e', 'k', 4, 'c', 'w', 'e', 't', 4, 'c', 'w', 'e', 'p', 4, 'c', 'w', 'e', 'h', 3, 'c', 'w', 'i', 4, 'c', 'w', 'i', 'g', 5, 'c', 'w', 'i', 'g', 'g', 5, 'c', 'w', 'i', 'g', 's', 4, 'c', 'w', 'i', 'n', 5, 'c', 'w', 'i', 'n', 'j', 5, 'c', 'w', 'i', 'n', 'h', 4, 'c', 'w', 'i', 'd', 4, 'c', 'w', 'i', 'l', 5, 'c', 'w', 'i', 'l', 'g', 5, 'c', 'w', 'i', 'l', 'm', 5, 'c', 'w', 'i', 'l', 'b', 5, 'c', 'w', 'i', 'l', 's', 5, 'c', 'w', 'i', 'l', 't', 5, 'c', 'w', 'i', 'l', 'p', 5, 'c', 'w', 'i', 'l', 'h', 4, 'c', 'w', 'i', 'm', 4, 'c', 'w', 'i', 'b', 5, 'c', 'w', 'i', 'b', 's', 4, 'c', 'w', 'i', 's', 5, 'c', 'w', 'i', 's', 's', 5, 'c', 'w', 'i', 'n', 'g', 4, 'c', 'w', 'i', 'j', 4, 'c', 'w', 'i', 'c', 4, 'c', 'w', 'i', 'k', 4, 'c', 'w', 'i', 't', 4, 'c', 'w', 'i', 'p', 4, 'c', 'w', 'i', 'h', 3, 'c', 'y', 'u', 4, 'c', 'y', 'u', 'g', 5, 'c', 'y', 'u', 'g', 'g', 5, 'c', 'y', 'u', 'g', 's', 4, 'c', 'y', 'u', 'n', 5, 'c', 'y', 'u', 'n', 'j', 5, 'c', 'y', 'u', 'n', 'h', 4, 'c', 'y', 'u', 'd', 4, 'c', 'y', 'u', 'l', 5, 'c', 'y', 'u', 'l', 'g', 5, 'c', 'y', 'u', 'l', 'm', 5, 'c', 'y', 'u', 'l', 'b', 5, 'c', 'y', 'u', 'l', 's', 5, 'c', 'y', 'u', 'l', 't', 5, 'c', 'y', 'u', 'l', 'p', 5, 'c', 'y', 'u', 'l', 'h', 4, 'c', 'y', 'u', 'm', 4, 'c', 'y', 'u', 'b', 5, 'c', 'y', 'u', 'b', 's', 4, 'c', 'y', 'u', 's', 5, 'c', 'y', 'u', 's', 's', 5, 'c', 'y', 'u', 'n', 'g', 4, 'c', 'y', 'u', 'j', 4, 'c', 'y', 'u', 'c', 4, 'c', 'y', 'u', 'k', 4, 'c', 'y', 'u', 't', 4, 'c', 'y', 'u', 'p', 4, 'c', 'y', 'u', 'h', 3, 'c', 'e', 'u', 4, 'c', 'e', 'u', 'g', 5, 'c', 'e', 'u', 'g', 'g', 5, 'c', 'e', 'u', 'g', 's', 4, 'c', 'e', 'u', 'n', 5, 'c', 'e', 'u', 'n', 'j', 5, 'c', 'e', 'u', 'n', 'h', 4, 'c', 'e', 'u', 'd', 4, 'c', 'e', 'u', 'l', 5, 'c', 'e', 'u', 'l', 'g', 5, 'c', 'e', 'u', 'l', 'm', 5, 'c', 'e', 'u', 'l', 'b', 5, 'c', 'e', 'u', 'l', 's', 5, 'c', 'e', 'u', 'l', 't', 5, 'c', 'e', 'u', 'l', 'p', 5, 'c', 'e', 'u', 'l', 'h', 4, 'c', 'e', 'u', 'm', 4, 'c', 'e', 'u', 'b', 5, 'c', 'e', 'u', 'b', 's', 4, 'c', 'e', 'u', 's', 5, 'c', 'e', 'u', 's', 's', 5, 'c', 'e', 'u', 'n', 'g', 4, 'c', 'e', 'u', 'j', 4, 'c', 'e', 'u', 'c', 4, 'c', 'e', 'u', 'k', 4, 'c', 'e', 'u', 't', 4, 'c', 'e', 'u', 'p', 4, 'c', 'e', 'u', 'h', 3, 'c', 'y', 'i', 4, 'c', 'y', 'i', 'g', 5, 'c', 'y', 'i', 'g', 'g', 5, 'c', 'y', 'i', 'g', 's', 4, 'c', 'y', 'i', 'n', 5, 'c', 'y', 'i', 'n', 'j', 5, 'c', 'y', 'i', 'n', 'h', 4, 'c', 'y', 'i', 'd', 4, 'c', 'y', 'i', 'l', 5, 'c', 'y', 'i', 'l', 'g', 5, 'c', 'y', 'i', 'l', 'm', 5, 'c', 'y', 'i', 'l', 'b', 5, 'c', 'y', 'i', 'l', 's', 5, 'c', 'y', 'i', 'l', 't', 5, 'c', 'y', 'i', 'l', 'p', 5, 'c', 'y', 'i', 'l', 'h', 4, 'c', 'y', 'i', 'm', 4, 'c', 'y', 'i', 'b', 5, 'c', 'y', 'i', 'b', 's', 4, 'c', 'y', 'i', 's', 5, 'c', 'y', 'i', 's', 's', 5, 'c', 'y', 'i', 'n', 'g', 4, 'c', 'y', 'i', 'j', 4, 'c', 'y', 'i', 'c', 4, 'c', 'y', 'i', 'k', 4, 'c', 'y', 'i', 't', 4, 'c', 'y', 'i', 'p', 4, 'c', 'y', 'i', 'h', 2, 'c', 'i', 3, 'c', 'i', 'g', 4, 'c', 'i', 'g', 'g', 4, 'c', 'i', 'g', 's', 3, 'c', 'i', 'n', 4, 'c', 'i', 'n', 'j', 4, 'c', 'i', 'n', 'h', 3, 'c', 'i', 'd', 3, 'c', 'i', 'l', 4, 'c', 'i', 'l', 'g', 4, 'c', 'i', 'l', 'm', 4, 'c', 'i', 'l', 'b', 4, 'c', 'i', 'l', 's', 4, 'c', 'i', 'l', 't', 4, 'c', 'i', 'l', 'p', 4, 'c', 'i', 'l', 'h', 3, 'c', 'i', 'm', 3, 'c', 'i', 'b', 4, 'c', 'i', 'b', 's', 3, 'c', 'i', 's', 4, 'c', 'i', 's', 's', 4, 'c', 'i', 'n', 'g', 3, 'c', 'i', 'j', 3, 'c', 'i', 'c', 3, 'c', 'i', 'k', 3, 'c', 'i', 't', 3, 'c', 'i', 'p', 3, 'c', 'i', 'h', 2, 'k', 'a', 3, 'k', 'a', 'g', 4, 'k', 'a', 'g', 'g', 4, 'k', 'a', 'g', 's', 3, 'k', 'a', 'n', 4, 'k', 'a', 'n', 'j', 4, 'k', 'a', 'n', 'h', 3, 'k', 'a', 'd', 3, 'k', 'a', 'l', 4, 'k', 'a', 'l', 'g', 4, 'k', 'a', 'l', 'm', 4, 'k', 'a', 'l', 'b', 4, 'k', 'a', 'l', 's', 4, 'k', 'a', 'l', 't', 4, 'k', 'a', 'l', 'p', 4, 'k', 'a', 'l', 'h', 3, 'k', 'a', 'm', 3, 'k', 'a', 'b', 4, 'k', 'a', 'b', 's', 3, 'k', 'a', 's', 4, 'k', 'a', 's', 's', 4, 'k', 'a', 'n', 'g', 3, 'k', 'a', 'j', 3, 'k', 'a', 'c', 3, 'k', 'a', 'k', 3, 'k', 'a', 't', 3, 'k', 'a', 'p', 3, 'k', 'a', 'h', 3, 'k', 'a', 'e', 4, 'k', 'a', 'e', 'g', 5, 'k', 'a', 'e', 'g', 'g', 5, 'k', 'a', 'e', 'g', 's', 4, 'k', 'a', 'e', 'n', 5, 'k', 'a', 'e', 'n', 'j', 5, 'k', 'a', 'e', 'n', 'h', 4, 'k', 'a', 'e', 'd', 4, 'k', 'a', 'e', 'l', 5, 'k', 'a', 'e', 'l', 'g', 5, 'k', 'a', 'e', 'l', 'm', 5, 'k', 'a', 'e', 'l', 'b', 5, 'k', 'a', 'e', 'l', 's', 5, 'k', 'a', 'e', 'l', 't', 5, 'k', 'a', 'e', 'l', 'p', 5, 'k', 'a', 'e', 'l', 'h', 4, 'k', 'a', 'e', 'm', 4, 'k', 'a', 'e', 'b', 5, 'k', 'a', 'e', 'b', 's', 4, 'k', 'a', 'e', 's', 5, 'k', 'a', 'e', 's', 's', 5, 'k', 'a', 'e', 'n', 'g', 4, 'k', 'a', 'e', 'j', 4, 'k', 'a', 'e', 'c', 4, 'k', 'a', 'e', 'k', 4, 'k', 'a', 'e', 't', 4, 'k', 'a', 'e', 'p', 4, 'k', 'a', 'e', 'h', 3, 'k', 'y', 'a', 4, 'k', 'y', 'a', 'g', 5, 'k', 'y', 'a', 'g', 'g', 5, 'k', 'y', 'a', 'g', 's', 4, 'k', 'y', 'a', 'n', 5, 'k', 'y', 'a', 'n', 'j', 5, 'k', 'y', 'a', 'n', 'h', 4, 'k', 'y', 'a', 'd', 4, 'k', 'y', 'a', 'l', 5, 'k', 'y', 'a', 'l', 'g', 5, 'k', 'y', 'a', 'l', 'm', 5, 'k', 'y', 'a', 'l', 'b', 5, 'k', 'y', 'a', 'l', 's', 5, 'k', 'y', 'a', 'l', 't', 5, 'k', 'y', 'a', 'l', 'p', 5, 'k', 'y', 'a', 'l', 'h', 4, 'k', 'y', 'a', 'm', 4, 'k', 'y', 'a', 'b', 5, 'k', 'y', 'a', 'b', 's', 4, 'k', 'y', 'a', 's', 5, 'k', 'y', 'a', 's', 's', 5, 'k', 'y', 'a', 'n', 'g', 4, 'k', 'y', 'a', 'j', 4, 'k', 'y', 'a', 'c', 4, 'k', 'y', 'a', 'k', 4, 'k', 'y', 'a', 't', 4, 'k', 'y', 'a', 'p', 4, 'k', 'y', 'a', 'h', 4, 'k', 'y', 'a', 'e', 5, 'k', 'y', 'a', 'e', 'g', 6, 'k', 'y', 'a', 'e', 'g', 'g', 6, 'k', 'y', 'a', 'e', 'g', 's', 5, 'k', 'y', 'a', 'e', 'n', 6, 'k', 'y', 'a', 'e', 'n', 'j', 6, 'k', 'y', 'a', 'e', 'n', 'h', 5, 'k', 'y', 'a', 'e', 'd', 5, 'k', 'y', 'a', 'e', 'l', 6, 'k', 'y', 'a', 'e', 'l', 'g', 6, 'k', 'y', 'a', 'e', 'l', 'm', 6, 'k', 'y', 'a', 'e', 'l', 'b', 6, 'k', 'y', 'a', 'e', 'l', 's', 6, 'k', 'y', 'a', 'e', 'l', 't', 6, 'k', 'y', 'a', 'e', 'l', 'p', 6, 'k', 'y', 'a', 'e', 'l', 'h', 5, 'k', 'y', 'a', 'e', 'm', 5, 'k', 'y', 'a', 'e', 'b', 6, 'k', 'y', 'a', 'e', 'b', 's', 5, 'k', 'y', 'a', 'e', 's', 6, 'k', 'y', 'a', 'e', 's', 's', 6, 'k', 'y', 'a', 'e', 'n', 'g', 5, 'k', 'y', 'a', 'e', 'j', 5, 'k', 'y', 'a', 'e', 'c', 5, 'k', 'y', 'a', 'e', 'k', 5, 'k', 'y', 'a', 'e', 't', 5, 'k', 'y', 'a', 'e', 'p', 5, 'k', 'y', 'a', 'e', 'h', 3, 'k', 'e', 'o', 4, 'k', 'e', 'o', 'g', 5, 'k', 'e', 'o', 'g', 'g', 5, 'k', 'e', 'o', 'g', 's', 4, 'k', 'e', 'o', 'n', 5, 'k', 'e', 'o', 'n', 'j', 5, 'k', 'e', 'o', 'n', 'h', 4, 'k', 'e', 'o', 'd', 4, 'k', 'e', 'o', 'l', 5, 'k', 'e', 'o', 'l', 'g', 5, 'k', 'e', 'o', 'l', 'm', 5, 'k', 'e', 'o', 'l', 'b', 5, 'k', 'e', 'o', 'l', 's', 5, 'k', 'e', 'o', 'l', 't', 5, 'k', 'e', 'o', 'l', 'p', 5, 'k', 'e', 'o', 'l', 'h', 4, 'k', 'e', 'o', 'm', 4, 'k', 'e', 'o', 'b', 5, 'k', 'e', 'o', 'b', 's', 4, 'k', 'e', 'o', 's', 5, 'k', 'e', 'o', 's', 's', 5, 'k', 'e', 'o', 'n', 'g', 4, 'k', 'e', 'o', 'j', 4, 'k', 'e', 'o', 'c', 4, 'k', 'e', 'o', 'k', 4, 'k', 'e', 'o', 't', 4, 'k', 'e', 'o', 'p', 4, 'k', 'e', 'o', 'h', 2, 'k', 'e', 3, 'k', 'e', 'g', 4, 'k', 'e', 'g', 'g', 4, 'k', 'e', 'g', 's', 3, 'k', 'e', 'n', 4, 'k', 'e', 'n', 'j', 4, 'k', 'e', 'n', 'h', 3, 'k', 'e', 'd', 3, 'k', 'e', 'l', 4, 'k', 'e', 'l', 'g', 4, 'k', 'e', 'l', 'm', 4, 'k', 'e', 'l', 'b', 4, 'k', 'e', 'l', 's', 4, 'k', 'e', 'l', 't', 4, 'k', 'e', 'l', 'p', 4, 'k', 'e', 'l', 'h', 3, 'k', 'e', 'm', 3, 'k', 'e', 'b', 4, 'k', 'e', 'b', 's', 3, 'k', 'e', 's', 4, 'k', 'e', 's', 's', 4, 'k', 'e', 'n', 'g', 3, 'k', 'e', 'j', 3, 'k', 'e', 'c', 3, 'k', 'e', 'k', 3, 'k', 'e', 't', 3, 'k', 'e', 'p', 3, 'k', 'e', 'h', 4, 'k', 'y', 'e', 'o', 5, 'k', 'y', 'e', 'o', 'g', 6, 'k', 'y', 'e', 'o', 'g', 'g', 6, 'k', 'y', 'e', 'o', 'g', 's', 5, 'k', 'y', 'e', 'o', 'n', 6, 'k', 'y', 'e', 'o', 'n', 'j', 6, 'k', 'y', 'e', 'o', 'n', 'h', 5, 'k', 'y', 'e', 'o', 'd', 5, 'k', 'y', 'e', 'o', 'l', 6, 'k', 'y', 'e', 'o', 'l', 'g', 6, 'k', 'y', 'e', 'o', 'l', 'm', 6, 'k', 'y', 'e', 'o', 'l', 'b', 6, 'k', 'y', 'e', 'o', 'l', 's', 6, 'k', 'y', 'e', 'o', 'l', 't', 6, 'k', 'y', 'e', 'o', 'l', 'p', 6, 'k', 'y', 'e', 'o', 'l', 'h', 5, 'k', 'y', 'e', 'o', 'm', 5, 'k', 'y', 'e', 'o', 'b', 6, 'k', 'y', 'e', 'o', 'b', 's', 5, 'k', 'y', 'e', 'o', 's', 6, 'k', 'y', 'e', 'o', 's', 's', 6, 'k', 'y', 'e', 'o', 'n', 'g', 5, 'k', 'y', 'e', 'o', 'j', 5, 'k', 'y', 'e', 'o', 'c', 5, 'k', 'y', 'e', 'o', 'k', 5, 'k', 'y', 'e', 'o', 't', 5, 'k', 'y', 'e', 'o', 'p', 5, 'k', 'y', 'e', 'o', 'h', 3, 'k', 'y', 'e', 4, 'k', 'y', 'e', 'g', 5, 'k', 'y', 'e', 'g', 'g', 5, 'k', 'y', 'e', 'g', 's', 4, 'k', 'y', 'e', 'n', 5, 'k', 'y', 'e', 'n', 'j', 5, 'k', 'y', 'e', 'n', 'h', 4, 'k', 'y', 'e', 'd', 4, 'k', 'y', 'e', 'l', 5, 'k', 'y', 'e', 'l', 'g', 5, 'k', 'y', 'e', 'l', 'm', 5, 'k', 'y', 'e', 'l', 'b', 5, 'k', 'y', 'e', 'l', 's', 5, 'k', 'y', 'e', 'l', 't', 5, 'k', 'y', 'e', 'l', 'p', 5, 'k', 'y', 'e', 'l', 'h', 4, 'k', 'y', 'e', 'm', 4, 'k', 'y', 'e', 'b', 5, 'k', 'y', 'e', 'b', 's', 4, 'k', 'y', 'e', 's', 5, 'k', 'y', 'e', 's', 's', 5, 'k', 'y', 'e', 'n', 'g', 4, 'k', 'y', 'e', 'j', 4, 'k', 'y', 'e', 'c', 4, 'k', 'y', 'e', 'k', 4, 'k', 'y', 'e', 't', 4, 'k', 'y', 'e', 'p', 4, 'k', 'y', 'e', 'h', 2, 'k', 'o', 3, 'k', 'o', 'g', 4, 'k', 'o', 'g', 'g', 4, 'k', 'o', 'g', 's', 3, 'k', 'o', 'n', 4, 'k', 'o', 'n', 'j', 4, 'k', 'o', 'n', 'h', 3, 'k', 'o', 'd', 3, 'k', 'o', 'l', 4, 'k', 'o', 'l', 'g', 4, 'k', 'o', 'l', 'm', 4, 'k', 'o', 'l', 'b', 4, 'k', 'o', 'l', 's', 4, 'k', 'o', 'l', 't', 4, 'k', 'o', 'l', 'p', 4, 'k', 'o', 'l', 'h', 3, 'k', 'o', 'm', 3, 'k', 'o', 'b', 4, 'k', 'o', 'b', 's', 3, 'k', 'o', 's', 4, 'k', 'o', 's', 's', 4, 'k', 'o', 'n', 'g', 3, 'k', 'o', 'j', 3, 'k', 'o', 'c', 3, 'k', 'o', 'k', 3, 'k', 'o', 't', 3, 'k', 'o', 'p', 3, 'k', 'o', 'h', 3, 'k', 'w', 'a', 4, 'k', 'w', 'a', 'g', 5, 'k', 'w', 'a', 'g', 'g', 5, 'k', 'w', 'a', 'g', 's', 4, 'k', 'w', 'a', 'n', 5, 'k', 'w', 'a', 'n', 'j', 5, 'k', 'w', 'a', 'n', 'h', 4, 'k', 'w', 'a', 'd', 4, 'k', 'w', 'a', 'l', 5, 'k', 'w', 'a', 'l', 'g', 5, 'k', 'w', 'a', 'l', 'm', 5, 'k', 'w', 'a', 'l', 'b', 5, 'k', 'w', 'a', 'l', 's', 5, 'k', 'w', 'a', 'l', 't', 5, 'k', 'w', 'a', 'l', 'p', 5, 'k', 'w', 'a', 'l', 'h', 4, 'k', 'w', 'a', 'm', 4, 'k', 'w', 'a', 'b', 5, 'k', 'w', 'a', 'b', 's', 4, 'k', 'w', 'a', 's', 5, 'k', 'w', 'a', 's', 's', 5, 'k', 'w', 'a', 'n', 'g', 4, 'k', 'w', 'a', 'j', 4, 'k', 'w', 'a', 'c', 4, 'k', 'w', 'a', 'k', 4, 'k', 'w', 'a', 't', 4, 'k', 'w', 'a', 'p', 4, 'k', 'w', 'a', 'h', 4, 'k', 'w', 'a', 'e', 5, 'k', 'w', 'a', 'e', 'g', 6, 'k', 'w', 'a', 'e', 'g', 'g', 6, 'k', 'w', 'a', 'e', 'g', 's', 5, 'k', 'w', 'a', 'e', 'n', 6, 'k', 'w', 'a', 'e', 'n', 'j', 6, 'k', 'w', 'a', 'e', 'n', 'h', 5, 'k', 'w', 'a', 'e', 'd', 5, 'k', 'w', 'a', 'e', 'l', 6, 'k', 'w', 'a', 'e', 'l', 'g', 6, 'k', 'w', 'a', 'e', 'l', 'm', 6, 'k', 'w', 'a', 'e', 'l', 'b', 6, 'k', 'w', 'a', 'e', 'l', 's', 6, 'k', 'w', 'a', 'e', 'l', 't', 6, 'k', 'w', 'a', 'e', 'l', 'p', 6, 'k', 'w', 'a', 'e', 'l', 'h', 5, 'k', 'w', 'a', 'e', 'm', 5, 'k', 'w', 'a', 'e', 'b', 6, 'k', 'w', 'a', 'e', 'b', 's', 5, 'k', 'w', 'a', 'e', 's', 6, 'k', 'w', 'a', 'e', 's', 's', 6, 'k', 'w', 'a', 'e', 'n', 'g', 5, 'k', 'w', 'a', 'e', 'j', 5, 'k', 'w', 'a', 'e', 'c', 5, 'k', 'w', 'a', 'e', 'k', 5, 'k', 'w', 'a', 'e', 't', 5, 'k', 'w', 'a', 'e', 'p', 5, 'k', 'w', 'a', 'e', 'h', 3, 'k', 'o', 'e', 4, 'k', 'o', 'e', 'g', 5, 'k', 'o', 'e', 'g', 'g', 5, 'k', 'o', 'e', 'g', 's', 4, 'k', 'o', 'e', 'n', 5, 'k', 'o', 'e', 'n', 'j', 5, 'k', 'o', 'e', 'n', 'h', 4, 'k', 'o', 'e', 'd', 4, 'k', 'o', 'e', 'l', 5, 'k', 'o', 'e', 'l', 'g', 5, 'k', 'o', 'e', 'l', 'm', 5, 'k', 'o', 'e', 'l', 'b', 5, 'k', 'o', 'e', 'l', 's', 5, 'k', 'o', 'e', 'l', 't', 5, 'k', 'o', 'e', 'l', 'p', 5, 'k', 'o', 'e', 'l', 'h', 4, 'k', 'o', 'e', 'm', 4, 'k', 'o', 'e', 'b', 5, 'k', 'o', 'e', 'b', 's', 4, 'k', 'o', 'e', 's', 5, 'k', 'o', 'e', 's', 's', 5, 'k', 'o', 'e', 'n', 'g', 4, 'k', 'o', 'e', 'j', 4, 'k', 'o', 'e', 'c', 4, 'k', 'o', 'e', 'k', 4, 'k', 'o', 'e', 't', 4, 'k', 'o', 'e', 'p', 4, 'k', 'o', 'e', 'h', 3, 'k', 'y', 'o', 4, 'k', 'y', 'o', 'g', 5, 'k', 'y', 'o', 'g', 'g', 5, 'k', 'y', 'o', 'g', 's', 4, 'k', 'y', 'o', 'n', 5, 'k', 'y', 'o', 'n', 'j', 5, 'k', 'y', 'o', 'n', 'h', 4, 'k', 'y', 'o', 'd', 4, 'k', 'y', 'o', 'l', 5, 'k', 'y', 'o', 'l', 'g', 5, 'k', 'y', 'o', 'l', 'm', 5, 'k', 'y', 'o', 'l', 'b', 5, 'k', 'y', 'o', 'l', 's', 5, 'k', 'y', 'o', 'l', 't', 5, 'k', 'y', 'o', 'l', 'p', 5, 'k', 'y', 'o', 'l', 'h', 4, 'k', 'y', 'o', 'm', 4, 'k', 'y', 'o', 'b', 5, 'k', 'y', 'o', 'b', 's', 4, 'k', 'y', 'o', 's', 5, 'k', 'y', 'o', 's', 's', 5, 'k', 'y', 'o', 'n', 'g', 4, 'k', 'y', 'o', 'j', 4, 'k', 'y', 'o', 'c', 4, 'k', 'y', 'o', 'k', 4, 'k', 'y', 'o', 't', 4, 'k', 'y', 'o', 'p', 4, 'k', 'y', 'o', 'h', 2, 'k', 'u', 3, 'k', 'u', 'g', 4, 'k', 'u', 'g', 'g', 4, 'k', 'u', 'g', 's', 3, 'k', 'u', 'n', 4, 'k', 'u', 'n', 'j', 4, 'k', 'u', 'n', 'h', 3, 'k', 'u', 'd', 3, 'k', 'u', 'l', 4, 'k', 'u', 'l', 'g', 4, 'k', 'u', 'l', 'm', 4, 'k', 'u', 'l', 'b', 4, 'k', 'u', 'l', 's', 4, 'k', 'u', 'l', 't', 4, 'k', 'u', 'l', 'p', 4, 'k', 'u', 'l', 'h', 3, 'k', 'u', 'm', 3, 'k', 'u', 'b', 4, 'k', 'u', 'b', 's', 3, 'k', 'u', 's', 4, 'k', 'u', 's', 's', 4, 'k', 'u', 'n', 'g', 3, 'k', 'u', 'j', 3, 'k', 'u', 'c', 3, 'k', 'u', 'k', 3, 'k', 'u', 't', 3, 'k', 'u', 'p', 3, 'k', 'u', 'h', 4, 'k', 'w', 'e', 'o', 5, 'k', 'w', 'e', 'o', 'g', 6, 'k', 'w', 'e', 'o', 'g', 'g', 6, 'k', 'w', 'e', 'o', 'g', 's', 5, 'k', 'w', 'e', 'o', 'n', 6, 'k', 'w', 'e', 'o', 'n', 'j', 6, 'k', 'w', 'e', 'o', 'n', 'h', 5, 'k', 'w', 'e', 'o', 'd', 5, 'k', 'w', 'e', 'o', 'l', 6, 'k', 'w', 'e', 'o', 'l', 'g', 6, 'k', 'w', 'e', 'o', 'l', 'm', 6, 'k', 'w', 'e', 'o', 'l', 'b', 6, 'k', 'w', 'e', 'o', 'l', 's', 6, 'k', 'w', 'e', 'o', 'l', 't', 6, 'k', 'w', 'e', 'o', 'l', 'p', 6, 'k', 'w', 'e', 'o', 'l', 'h', 5, 'k', 'w', 'e', 'o', 'm', 5, 'k', 'w', 'e', 'o', 'b', 6, 'k', 'w', 'e', 'o', 'b', 's', 5, 'k', 'w', 'e', 'o', 's', 6, 'k', 'w', 'e', 'o', 's', 's', 6, 'k', 'w', 'e', 'o', 'n', 'g', 5, 'k', 'w', 'e', 'o', 'j', 5, 'k', 'w', 'e', 'o', 'c', 5, 'k', 'w', 'e', 'o', 'k', 5, 'k', 'w', 'e', 'o', 't', 5, 'k', 'w', 'e', 'o', 'p', 5, 'k', 'w', 'e', 'o', 'h', 3, 'k', 'w', 'e', 4, 'k', 'w', 'e', 'g', 5, 'k', 'w', 'e', 'g', 'g', 5, 'k', 'w', 'e', 'g', 's', 4, 'k', 'w', 'e', 'n', 5, 'k', 'w', 'e', 'n', 'j', 5, 'k', 'w', 'e', 'n', 'h', 4, 'k', 'w', 'e', 'd', 4, 'k', 'w', 'e', 'l', 5, 'k', 'w', 'e', 'l', 'g', 5, 'k', 'w', 'e', 'l', 'm', 5, 'k', 'w', 'e', 'l', 'b', 5, 'k', 'w', 'e', 'l', 's', 5, 'k', 'w', 'e', 'l', 't', 5, 'k', 'w', 'e', 'l', 'p', 5, 'k', 'w', 'e', 'l', 'h', 4, 'k', 'w', 'e', 'm', 4, 'k', 'w', 'e', 'b', 5, 'k', 'w', 'e', 'b', 's', 4, 'k', 'w', 'e', 's', 5, 'k', 'w', 'e', 's', 's', 5, 'k', 'w', 'e', 'n', 'g', 4, 'k', 'w', 'e', 'j', 4, 'k', 'w', 'e', 'c', 4, 'k', 'w', 'e', 'k', 4, 'k', 'w', 'e', 't', 4, 'k', 'w', 'e', 'p', 4, 'k', 'w', 'e', 'h', 3, 'k', 'w', 'i', 4, 'k', 'w', 'i', 'g', 5, 'k', 'w', 'i', 'g', 'g', 5, 'k', 'w', 'i', 'g', 's', 4, 'k', 'w', 'i', 'n', 5, 'k', 'w', 'i', 'n', 'j', 5, 'k', 'w', 'i', 'n', 'h', 4, 'k', 'w', 'i', 'd', 4, 'k', 'w', 'i', 'l', 5, 'k', 'w', 'i', 'l', 'g', 5, 'k', 'w', 'i', 'l', 'm', 5, 'k', 'w', 'i', 'l', 'b', 5, 'k', 'w', 'i', 'l', 's', 5, 'k', 'w', 'i', 'l', 't', 5, 'k', 'w', 'i', 'l', 'p', 5, 'k', 'w', 'i', 'l', 'h', 4, 'k', 'w', 'i', 'm', 4, 'k', 'w', 'i', 'b', 5, 'k', 'w', 'i', 'b', 's', 4, 'k', 'w', 'i', 's', 5, 'k', 'w', 'i', 's', 's', 5, 'k', 'w', 'i', 'n', 'g', 4, 'k', 'w', 'i', 'j', 4, 'k', 'w', 'i', 'c', 4, 'k', 'w', 'i', 'k', 4, 'k', 'w', 'i', 't', 4, 'k', 'w', 'i', 'p', 4, 'k', 'w', 'i', 'h', 3, 'k', 'y', 'u', 4, 'k', 'y', 'u', 'g', 5, 'k', 'y', 'u', 'g', 'g', 5, 'k', 'y', 'u', 'g', 's', 4, 'k', 'y', 'u', 'n', 5, 'k', 'y', 'u', 'n', 'j', 5, 'k', 'y', 'u', 'n', 'h', 4, 'k', 'y', 'u', 'd', 4, 'k', 'y', 'u', 'l', 5, 'k', 'y', 'u', 'l', 'g', 5, 'k', 'y', 'u', 'l', 'm', 5, 'k', 'y', 'u', 'l', 'b', 5, 'k', 'y', 'u', 'l', 's', 5, 'k', 'y', 'u', 'l', 't', 5, 'k', 'y', 'u', 'l', 'p', 5, 'k', 'y', 'u', 'l', 'h', 4, 'k', 'y', 'u', 'm', 4, 'k', 'y', 'u', 'b', 5, 'k', 'y', 'u', 'b', 's', 4, 'k', 'y', 'u', 's', 5, 'k', 'y', 'u', 's', 's', 5, 'k', 'y', 'u', 'n', 'g', 4, 'k', 'y', 'u', 'j', 4, 'k', 'y', 'u', 'c', 4, 'k', 'y', 'u', 'k', 4, 'k', 'y', 'u', 't', 4, 'k', 'y', 'u', 'p', 4, 'k', 'y', 'u', 'h', 3, 'k', 'e', 'u', 4, 'k', 'e', 'u', 'g', 5, 'k', 'e', 'u', 'g', 'g', 5, 'k', 'e', 'u', 'g', 's', 4, 'k', 'e', 'u', 'n', 5, 'k', 'e', 'u', 'n', 'j', 5, 'k', 'e', 'u', 'n', 'h', 4, 'k', 'e', 'u', 'd', 4, 'k', 'e', 'u', 'l', 5, 'k', 'e', 'u', 'l', 'g', 5, 'k', 'e', 'u', 'l', 'm', 5, 'k', 'e', 'u', 'l', 'b', 5, 'k', 'e', 'u', 'l', 's', 5, 'k', 'e', 'u', 'l', 't', 5, 'k', 'e', 'u', 'l', 'p', 5, 'k', 'e', 'u', 'l', 'h', 4, 'k', 'e', 'u', 'm', 4, 'k', 'e', 'u', 'b', 5, 'k', 'e', 'u', 'b', 's', 4, 'k', 'e', 'u', 's', 5, 'k', 'e', 'u', 's', 's', 5, 'k', 'e', 'u', 'n', 'g', 4, 'k', 'e', 'u', 'j', 4, 'k', 'e', 'u', 'c', 4, 'k', 'e', 'u', 'k', 4, 'k', 'e', 'u', 't', 4, 'k', 'e', 'u', 'p', 4, 'k', 'e', 'u', 'h', 3, 'k', 'y', 'i', 4, 'k', 'y', 'i', 'g', 5, 'k', 'y', 'i', 'g', 'g', 5, 'k', 'y', 'i', 'g', 's', 4, 'k', 'y', 'i', 'n', 5, 'k', 'y', 'i', 'n', 'j', 5, 'k', 'y', 'i', 'n', 'h', 4, 'k', 'y', 'i', 'd', 4, 'k', 'y', 'i', 'l', 5, 'k', 'y', 'i', 'l', 'g', 5, 'k', 'y', 'i', 'l', 'm', 5, 'k', 'y', 'i', 'l', 'b', 5, 'k', 'y', 'i', 'l', 's', 5, 'k', 'y', 'i', 'l', 't', 5, 'k', 'y', 'i', 'l', 'p', 5, 'k', 'y', 'i', 'l', 'h', 4, 'k', 'y', 'i', 'm', 4, 'k', 'y', 'i', 'b', 5, 'k', 'y', 'i', 'b', 's', 4, 'k', 'y', 'i', 's', 5, 'k', 'y', 'i', 's', 's', 5, 'k', 'y', 'i', 'n', 'g', 4, 'k', 'y', 'i', 'j', 4, 'k', 'y', 'i', 'c', 4, 'k', 'y', 'i', 'k', 4, 'k', 'y', 'i', 't', 4, 'k', 'y', 'i', 'p', 4, 'k', 'y', 'i', 'h', 2, 'k', 'i', 3, 'k', 'i', 'g', 4, 'k', 'i', 'g', 'g', 4, 'k', 'i', 'g', 's', 3, 'k', 'i', 'n', 4, 'k', 'i', 'n', 'j', 4, 'k', 'i', 'n', 'h', 3, 'k', 'i', 'd', 3, 'k', 'i', 'l', 4, 'k', 'i', 'l', 'g', 4, 'k', 'i', 'l', 'm', 4, 'k', 'i', 'l', 'b', 4, 'k', 'i', 'l', 's', 4, 'k', 'i', 'l', 't', 4, 'k', 'i', 'l', 'p', 4, 'k', 'i', 'l', 'h', 3, 'k', 'i', 'm', 3, 'k', 'i', 'b', 4, 'k', 'i', 'b', 's', 3, 'k', 'i', 's', 4, 'k', 'i', 's', 's', 4, 'k', 'i', 'n', 'g', 3, 'k', 'i', 'j', 3, 'k', 'i', 'c', 3, 'k', 'i', 'k', 3, 'k', 'i', 't', 3, 'k', 'i', 'p', 3, 'k', 'i', 'h', 2, 't', 'a', 3, 't', 'a', 'g', 4, 't', 'a', 'g', 'g', 4, 't', 'a', 'g', 's', 3, 't', 'a', 'n', 4, 't', 'a', 'n', 'j', 4, 't', 'a', 'n', 'h', 3, 't', 'a', 'd', 3, 't', 'a', 'l', 4, 't', 'a', 'l', 'g', 4, 't', 'a', 'l', 'm', 4, 't', 'a', 'l', 'b', 4, 't', 'a', 'l', 's', 4, 't', 'a', 'l', 't', 4, 't', 'a', 'l', 'p', 4, 't', 'a', 'l', 'h', 3, 't', 'a', 'm', 3, 't', 'a', 'b', 4, 't', 'a', 'b', 's', 3, 't', 'a', 's', 4, 't', 'a', 's', 's', 4, 't', 'a', 'n', 'g', 3, 't', 'a', 'j', 3, 't', 'a', 'c', 3, 't', 'a', 'k', 3, 't', 'a', 't', 3, 't', 'a', 'p', 3, 't', 'a', 'h', 3, 't', 'a', 'e', 4, 't', 'a', 'e', 'g', 5, 't', 'a', 'e', 'g', 'g', 5, 't', 'a', 'e', 'g', 's', 4, 't', 'a', 'e', 'n', 5, 't', 'a', 'e', 'n', 'j', 5, 't', 'a', 'e', 'n', 'h', 4, 't', 'a', 'e', 'd', 4, 't', 'a', 'e', 'l', 5, 't', 'a', 'e', 'l', 'g', 5, 't', 'a', 'e', 'l', 'm', 5, 't', 'a', 'e', 'l', 'b', 5, 't', 'a', 'e', 'l', 's', 5, 't', 'a', 'e', 'l', 't', 5, 't', 'a', 'e', 'l', 'p', 5, 't', 'a', 'e', 'l', 'h', 4, 't', 'a', 'e', 'm', 4, 't', 'a', 'e', 'b', 5, 't', 'a', 'e', 'b', 's', 4, 't', 'a', 'e', 's', 5, 't', 'a', 'e', 's', 's', 5, 't', 'a', 'e', 'n', 'g', 4, 't', 'a', 'e', 'j', 4, 't', 'a', 'e', 'c', 4, 't', 'a', 'e', 'k', 4, 't', 'a', 'e', 't', 4, 't', 'a', 'e', 'p', 4, 't', 'a', 'e', 'h', 3, 't', 'y', 'a', 4, 't', 'y', 'a', 'g', 5, 't', 'y', 'a', 'g', 'g', 5, 't', 'y', 'a', 'g', 's', 4, 't', 'y', 'a', 'n', 5, 't', 'y', 'a', 'n', 'j', 5, 't', 'y', 'a', 'n', 'h', 4, 't', 'y', 'a', 'd', 4, 't', 'y', 'a', 'l', 5, 't', 'y', 'a', 'l', 'g', 5, 't', 'y', 'a', 'l', 'm', 5, 't', 'y', 'a', 'l', 'b', 5, 't', 'y', 'a', 'l', 's', 5, 't', 'y', 'a', 'l', 't', 5, 't', 'y', 'a', 'l', 'p', 5, 't', 'y', 'a', 'l', 'h', 4, 't', 'y', 'a', 'm', 4, 't', 'y', 'a', 'b', 5, 't', 'y', 'a', 'b', 's', 4, 't', 'y', 'a', 's', 5, 't', 'y', 'a', 's', 's', 5, 't', 'y', 'a', 'n', 'g', 4, 't', 'y', 'a', 'j', 4, 't', 'y', 'a', 'c', 4, 't', 'y', 'a', 'k', 4, 't', 'y', 'a', 't', 4, 't', 'y', 'a', 'p', 4, 't', 'y', 'a', 'h', 4, 't', 'y', 'a', 'e', 5, 't', 'y', 'a', 'e', 'g', 6, 't', 'y', 'a', 'e', 'g', 'g', 6, 't', 'y', 'a', 'e', 'g', 's', 5, 't', 'y', 'a', 'e', 'n', 6, 't', 'y', 'a', 'e', 'n', 'j', 6, 't', 'y', 'a', 'e', 'n', 'h', 5, 't', 'y', 'a', 'e', 'd', 5, 't', 'y', 'a', 'e', 'l', 6, 't', 'y', 'a', 'e', 'l', 'g', 6, 't', 'y', 'a', 'e', 'l', 'm', 6, 't', 'y', 'a', 'e', 'l', 'b', 6, 't', 'y', 'a', 'e', 'l', 's', 6, 't', 'y', 'a', 'e', 'l', 't', 6, 't', 'y', 'a', 'e', 'l', 'p', 6, 't', 'y', 'a', 'e', 'l', 'h', 5, 't', 'y', 'a', 'e', 'm', 5, 't', 'y', 'a', 'e', 'b', 6, 't', 'y', 'a', 'e', 'b', 's', 5, 't', 'y', 'a', 'e', 's', 6, 't', 'y', 'a', 'e', 's', 's', 6, 't', 'y', 'a', 'e', 'n', 'g', 5, 't', 'y', 'a', 'e', 'j', 5, 't', 'y', 'a', 'e', 'c', 5, 't', 'y', 'a', 'e', 'k', 5, 't', 'y', 'a', 'e', 't', 5, 't', 'y', 'a', 'e', 'p', 5, 't', 'y', 'a', 'e', 'h', 3, 't', 'e', 'o', 4, 't', 'e', 'o', 'g', 5, 't', 'e', 'o', 'g', 'g', 5, 't', 'e', 'o', 'g', 's', 4, 't', 'e', 'o', 'n', 5, 't', 'e', 'o', 'n', 'j', 5, 't', 'e', 'o', 'n', 'h', 4, 't', 'e', 'o', 'd', 4, 't', 'e', 'o', 'l', 5, 't', 'e', 'o', 'l', 'g', 5, 't', 'e', 'o', 'l', 'm', 5, 't', 'e', 'o', 'l', 'b', 5, 't', 'e', 'o', 'l', 's', 5, 't', 'e', 'o', 'l', 't', 5, 't', 'e', 'o', 'l', 'p', 5, 't', 'e', 'o', 'l', 'h', 4, 't', 'e', 'o', 'm', 4, 't', 'e', 'o', 'b', 5, 't', 'e', 'o', 'b', 's', 4, 't', 'e', 'o', 's', 5, 't', 'e', 'o', 's', 's', 5, 't', 'e', 'o', 'n', 'g', 4, 't', 'e', 'o', 'j', 4, 't', 'e', 'o', 'c', 4, 't', 'e', 'o', 'k', 4, 't', 'e', 'o', 't', 4, 't', 'e', 'o', 'p', 4, 't', 'e', 'o', 'h', 2, 't', 'e', 3, 't', 'e', 'g', 4, 't', 'e', 'g', 'g', 4, 't', 'e', 'g', 's', 3, 't', 'e', 'n', 4, 't', 'e', 'n', 'j', 4, 't', 'e', 'n', 'h', 3, 't', 'e', 'd', 3, 't', 'e', 'l', 4, 't', 'e', 'l', 'g', 4, 't', 'e', 'l', 'm', 4, 't', 'e', 'l', 'b', 4, 't', 'e', 'l', 's', 4, 't', 'e', 'l', 't', 4, 't', 'e', 'l', 'p', 4, 't', 'e', 'l', 'h', 3, 't', 'e', 'm', 3, 't', 'e', 'b', 4, 't', 'e', 'b', 's', 3, 't', 'e', 's', 4, 't', 'e', 's', 's', 4, 't', 'e', 'n', 'g', 3, 't', 'e', 'j', 3, 't', 'e', 'c', 3, 't', 'e', 'k', 3, 't', 'e', 't', 3, 't', 'e', 'p', 3, 't', 'e', 'h', 4, 't', 'y', 'e', 'o', 5, 't', 'y', 'e', 'o', 'g', 6, 't', 'y', 'e', 'o', 'g', 'g', 6, 't', 'y', 'e', 'o', 'g', 's', 5, 't', 'y', 'e', 'o', 'n', 6, 't', 'y', 'e', 'o', 'n', 'j', 6, 't', 'y', 'e', 'o', 'n', 'h', 5, 't', 'y', 'e', 'o', 'd', 5, 't', 'y', 'e', 'o', 'l', 6, 't', 'y', 'e', 'o', 'l', 'g', 6, 't', 'y', 'e', 'o', 'l', 'm', 6, 't', 'y', 'e', 'o', 'l', 'b', 6, 't', 'y', 'e', 'o', 'l', 's', 6, 't', 'y', 'e', 'o', 'l', 't', 6, 't', 'y', 'e', 'o', 'l', 'p', 6, 't', 'y', 'e', 'o', 'l', 'h', 5, 't', 'y', 'e', 'o', 'm', 5, 't', 'y', 'e', 'o', 'b', 6, 't', 'y', 'e', 'o', 'b', 's', 5, 't', 'y', 'e', 'o', 's', 6, 't', 'y', 'e', 'o', 's', 's', 6, 't', 'y', 'e', 'o', 'n', 'g', 5, 't', 'y', 'e', 'o', 'j', 5, 't', 'y', 'e', 'o', 'c', 5, 't', 'y', 'e', 'o', 'k', 5, 't', 'y', 'e', 'o', 't', 5, 't', 'y', 'e', 'o', 'p', 5, 't', 'y', 'e', 'o', 'h', 3, 't', 'y', 'e', 4, 't', 'y', 'e', 'g', 5, 't', 'y', 'e', 'g', 'g', 5, 't', 'y', 'e', 'g', 's', 4, 't', 'y', 'e', 'n', 5, 't', 'y', 'e', 'n', 'j', 5, 't', 'y', 'e', 'n', 'h', 4, 't', 'y', 'e', 'd', 4, 't', 'y', 'e', 'l', 5, 't', 'y', 'e', 'l', 'g', 5, 't', 'y', 'e', 'l', 'm', 5, 't', 'y', 'e', 'l', 'b', 5, 't', 'y', 'e', 'l', 's', 5, 't', 'y', 'e', 'l', 't', 5, 't', 'y', 'e', 'l', 'p', 5, 't', 'y', 'e', 'l', 'h', 4, 't', 'y', 'e', 'm', 4, 't', 'y', 'e', 'b', 5, 't', 'y', 'e', 'b', 's', 4, 't', 'y', 'e', 's', 5, 't', 'y', 'e', 's', 's', 5, 't', 'y', 'e', 'n', 'g', 4, 't', 'y', 'e', 'j', 4, 't', 'y', 'e', 'c', 4, 't', 'y', 'e', 'k', 4, 't', 'y', 'e', 't', 4, 't', 'y', 'e', 'p', 4, 't', 'y', 'e', 'h', 2, 't', 'o', 3, 't', 'o', 'g', 4, 't', 'o', 'g', 'g', 4, 't', 'o', 'g', 's', 3, 't', 'o', 'n', 4, 't', 'o', 'n', 'j', 4, 't', 'o', 'n', 'h', 3, 't', 'o', 'd', 3, 't', 'o', 'l', 4, 't', 'o', 'l', 'g', 4, 't', 'o', 'l', 'm', 4, 't', 'o', 'l', 'b', 4, 't', 'o', 'l', 's', 4, 't', 'o', 'l', 't', 4, 't', 'o', 'l', 'p', 4, 't', 'o', 'l', 'h', 3, 't', 'o', 'm', 3, 't', 'o', 'b', 4, 't', 'o', 'b', 's', 3, 't', 'o', 's', 4, 't', 'o', 's', 's', 4, 't', 'o', 'n', 'g', 3, 't', 'o', 'j', 3, 't', 'o', 'c', 3, 't', 'o', 'k', 3, 't', 'o', 't', 3, 't', 'o', 'p', 3, 't', 'o', 'h', 3, 't', 'w', 'a', 4, 't', 'w', 'a', 'g', 5, 't', 'w', 'a', 'g', 'g', 5, 't', 'w', 'a', 'g', 's', 4, 't', 'w', 'a', 'n', 5, 't', 'w', 'a', 'n', 'j', 5, 't', 'w', 'a', 'n', 'h', 4, 't', 'w', 'a', 'd', 4, 't', 'w', 'a', 'l', 5, 't', 'w', 'a', 'l', 'g', 5, 't', 'w', 'a', 'l', 'm', 5, 't', 'w', 'a', 'l', 'b', 5, 't', 'w', 'a', 'l', 's', 5, 't', 'w', 'a', 'l', 't', 5, 't', 'w', 'a', 'l', 'p', 5, 't', 'w', 'a', 'l', 'h', 4, 't', 'w', 'a', 'm', 4, 't', 'w', 'a', 'b', 5, 't', 'w', 'a', 'b', 's', 4, 't', 'w', 'a', 's', 5, 't', 'w', 'a', 's', 's', 5, 't', 'w', 'a', 'n', 'g', 4, 't', 'w', 'a', 'j', 4, 't', 'w', 'a', 'c', 4, 't', 'w', 'a', 'k', 4, 't', 'w', 'a', 't', 4, 't', 'w', 'a', 'p', 4, 't', 'w', 'a', 'h', 4, 't', 'w', 'a', 'e', 5, 't', 'w', 'a', 'e', 'g', 6, 't', 'w', 'a', 'e', 'g', 'g', 6, 't', 'w', 'a', 'e', 'g', 's', 5, 't', 'w', 'a', 'e', 'n', 6, 't', 'w', 'a', 'e', 'n', 'j', 6, 't', 'w', 'a', 'e', 'n', 'h', 5, 't', 'w', 'a', 'e', 'd', 5, 't', 'w', 'a', 'e', 'l', 6, 't', 'w', 'a', 'e', 'l', 'g', 6, 't', 'w', 'a', 'e', 'l', 'm', 6, 't', 'w', 'a', 'e', 'l', 'b', 6, 't', 'w', 'a', 'e', 'l', 's', 6, 't', 'w', 'a', 'e', 'l', 't', 6, 't', 'w', 'a', 'e', 'l', 'p', 6, 't', 'w', 'a', 'e', 'l', 'h', 5, 't', 'w', 'a', 'e', 'm', 5, 't', 'w', 'a', 'e', 'b', 6, 't', 'w', 'a', 'e', 'b', 's', 5, 't', 'w', 'a', 'e', 's', 6, 't', 'w', 'a', 'e', 's', 's', 6, 't', 'w', 'a', 'e', 'n', 'g', 5, 't', 'w', 'a', 'e', 'j', 5, 't', 'w', 'a', 'e', 'c', 5, 't', 'w', 'a', 'e', 'k', 5, 't', 'w', 'a', 'e', 't', 5, 't', 'w', 'a', 'e', 'p', 5, 't', 'w', 'a', 'e', 'h', 3, 't', 'o', 'e', 4, 't', 'o', 'e', 'g', 5, 't', 'o', 'e', 'g', 'g', 5, 't', 'o', 'e', 'g', 's', 4, 't', 'o', 'e', 'n', 5, 't', 'o', 'e', 'n', 'j', 5, 't', 'o', 'e', 'n', 'h', 4, 't', 'o', 'e', 'd', 4, 't', 'o', 'e', 'l', 5, 't', 'o', 'e', 'l', 'g', 5, 't', 'o', 'e', 'l', 'm', 5, 't', 'o', 'e', 'l', 'b', 5, 't', 'o', 'e', 'l', 's', 5, 't', 'o', 'e', 'l', 't', 5, 't', 'o', 'e', 'l', 'p', 5, 't', 'o', 'e', 'l', 'h', 4, 't', 'o', 'e', 'm', 4, 't', 'o', 'e', 'b', 5, 't', 'o', 'e', 'b', 's', 4, 't', 'o', 'e', 's', 5, 't', 'o', 'e', 's', 's', 5, 't', 'o', 'e', 'n', 'g', 4, 't', 'o', 'e', 'j', 4, 't', 'o', 'e', 'c', 4, 't', 'o', 'e', 'k', 4, 't', 'o', 'e', 't', 4, 't', 'o', 'e', 'p', 4, 't', 'o', 'e', 'h', 3, 't', 'y', 'o', 4, 't', 'y', 'o', 'g', 5, 't', 'y', 'o', 'g', 'g', 5, 't', 'y', 'o', 'g', 's', 4, 't', 'y', 'o', 'n', 5, 't', 'y', 'o', 'n', 'j', 5, 't', 'y', 'o', 'n', 'h', 4, 't', 'y', 'o', 'd', 4, 't', 'y', 'o', 'l', 5, 't', 'y', 'o', 'l', 'g', 5, 't', 'y', 'o', 'l', 'm', 5, 't', 'y', 'o', 'l', 'b', 5, 't', 'y', 'o', 'l', 's', 5, 't', 'y', 'o', 'l', 't', 5, 't', 'y', 'o', 'l', 'p', 5, 't', 'y', 'o', 'l', 'h', 4, 't', 'y', 'o', 'm', 4, 't', 'y', 'o', 'b', 5, 't', 'y', 'o', 'b', 's', 4, 't', 'y', 'o', 's', 5, 't', 'y', 'o', 's', 's', 5, 't', 'y', 'o', 'n', 'g', 4, 't', 'y', 'o', 'j', 4, 't', 'y', 'o', 'c', 4, 't', 'y', 'o', 'k', 4, 't', 'y', 'o', 't', 4, 't', 'y', 'o', 'p', 4, 't', 'y', 'o', 'h', 2, 't', 'u', 3, 't', 'u', 'g', 4, 't', 'u', 'g', 'g', 4, 't', 'u', 'g', 's', 3, 't', 'u', 'n', 4, 't', 'u', 'n', 'j', 4, 't', 'u', 'n', 'h', 3, 't', 'u', 'd', 3, 't', 'u', 'l', 4, 't', 'u', 'l', 'g', 4, 't', 'u', 'l', 'm', 4, 't', 'u', 'l', 'b', 4, 't', 'u', 'l', 's', 4, 't', 'u', 'l', 't', 4, 't', 'u', 'l', 'p', 4, 't', 'u', 'l', 'h', 3, 't', 'u', 'm', 3, 't', 'u', 'b', 4, 't', 'u', 'b', 's', 3, 't', 'u', 's', 4, 't', 'u', 's', 's', 4, 't', 'u', 'n', 'g', 3, 't', 'u', 'j', 3, 't', 'u', 'c', 3, 't', 'u', 'k', 3, 't', 'u', 't', 3, 't', 'u', 'p', 3, 't', 'u', 'h', 4, 't', 'w', 'e', 'o', 5, 't', 'w', 'e', 'o', 'g', 6, 't', 'w', 'e', 'o', 'g', 'g', 6, 't', 'w', 'e', 'o', 'g', 's', 5, 't', 'w', 'e', 'o', 'n', 6, 't', 'w', 'e', 'o', 'n', 'j', 6, 't', 'w', 'e', 'o', 'n', 'h', 5, 't', 'w', 'e', 'o', 'd', 5, 't', 'w', 'e', 'o', 'l', 6, 't', 'w', 'e', 'o', 'l', 'g', 6, 't', 'w', 'e', 'o', 'l', 'm', 6, 't', 'w', 'e', 'o', 'l', 'b', 6, 't', 'w', 'e', 'o', 'l', 's', 6, 't', 'w', 'e', 'o', 'l', 't', 6, 't', 'w', 'e', 'o', 'l', 'p', 6, 't', 'w', 'e', 'o', 'l', 'h', 5, 't', 'w', 'e', 'o', 'm', 5, 't', 'w', 'e', 'o', 'b', 6, 't', 'w', 'e', 'o', 'b', 's', 5, 't', 'w', 'e', 'o', 's', 6, 't', 'w', 'e', 'o', 's', 's', 6, 't', 'w', 'e', 'o', 'n', 'g', 5, 't', 'w', 'e', 'o', 'j', 5, 't', 'w', 'e', 'o', 'c', 5, 't', 'w', 'e', 'o', 'k', 5, 't', 'w', 'e', 'o', 't', 5, 't', 'w', 'e', 'o', 'p', 5, 't', 'w', 'e', 'o', 'h', 3, 't', 'w', 'e', 4, 't', 'w', 'e', 'g', 5, 't', 'w', 'e', 'g', 'g', 5, 't', 'w', 'e', 'g', 's', 4, 't', 'w', 'e', 'n', 5, 't', 'w', 'e', 'n', 'j', 5, 't', 'w', 'e', 'n', 'h', 4, 't', 'w', 'e', 'd', 4, 't', 'w', 'e', 'l', 5, 't', 'w', 'e', 'l', 'g', 5, 't', 'w', 'e', 'l', 'm', 5, 't', 'w', 'e', 'l', 'b', 5, 't', 'w', 'e', 'l', 's', 5, 't', 'w', 'e', 'l', 't', 5, 't', 'w', 'e', 'l', 'p', 5, 't', 'w', 'e', 'l', 'h', 4, 't', 'w', 'e', 'm', 4, 't', 'w', 'e', 'b', 5, 't', 'w', 'e', 'b', 's', 4, 't', 'w', 'e', 's', 5, 't', 'w', 'e', 's', 's', 5, 't', 'w', 'e', 'n', 'g', 4, 't', 'w', 'e', 'j', 4, 't', 'w', 'e', 'c', 4, 't', 'w', 'e', 'k', 4, 't', 'w', 'e', 't', 4, 't', 'w', 'e', 'p', 4, 't', 'w', 'e', 'h', 3, 't', 'w', 'i', 4, 't', 'w', 'i', 'g', 5, 't', 'w', 'i', 'g', 'g', 5, 't', 'w', 'i', 'g', 's', 4, 't', 'w', 'i', 'n', 5, 't', 'w', 'i', 'n', 'j', 5, 't', 'w', 'i', 'n', 'h', 4, 't', 'w', 'i', 'd', 4, 't', 'w', 'i', 'l', 5, 't', 'w', 'i', 'l', 'g', 5, 't', 'w', 'i', 'l', 'm', 5, 't', 'w', 'i', 'l', 'b', 5, 't', 'w', 'i', 'l', 's', 5, 't', 'w', 'i', 'l', 't', 5, 't', 'w', 'i', 'l', 'p', 5, 't', 'w', 'i', 'l', 'h', 4, 't', 'w', 'i', 'm', 4, 't', 'w', 'i', 'b', 5, 't', 'w', 'i', 'b', 's', 4, 't', 'w', 'i', 's', 5, 't', 'w', 'i', 's', 's', 5, 't', 'w', 'i', 'n', 'g', 4, 't', 'w', 'i', 'j', 4, 't', 'w', 'i', 'c', 4, 't', 'w', 'i', 'k', 4, 't', 'w', 'i', 't', 4, 't', 'w', 'i', 'p', 4, 't', 'w', 'i', 'h', 3, 't', 'y', 'u', 4, 't', 'y', 'u', 'g', 5, 't', 'y', 'u', 'g', 'g', 5, 't', 'y', 'u', 'g', 's', 4, 't', 'y', 'u', 'n', 5, 't', 'y', 'u', 'n', 'j', 5, 't', 'y', 'u', 'n', 'h', 4, 't', 'y', 'u', 'd', 4, 't', 'y', 'u', 'l', 5, 't', 'y', 'u', 'l', 'g', 5, 't', 'y', 'u', 'l', 'm', 5, 't', 'y', 'u', 'l', 'b', 5, 't', 'y', 'u', 'l', 's', 5, 't', 'y', 'u', 'l', 't', 5, 't', 'y', 'u', 'l', 'p', 5, 't', 'y', 'u', 'l', 'h', 4, 't', 'y', 'u', 'm', 4, 't', 'y', 'u', 'b', 5, 't', 'y', 'u', 'b', 's', 4, 't', 'y', 'u', 's', 5, 't', 'y', 'u', 's', 's', 5, 't', 'y', 'u', 'n', 'g', 4, 't', 'y', 'u', 'j', 4, 't', 'y', 'u', 'c', 4, 't', 'y', 'u', 'k', 4, 't', 'y', 'u', 't', 4, 't', 'y', 'u', 'p', 4, 't', 'y', 'u', 'h', 3, 't', 'e', 'u', 4, 't', 'e', 'u', 'g', 5, 't', 'e', 'u', 'g', 'g', 5, 't', 'e', 'u', 'g', 's', 4, 't', 'e', 'u', 'n', 5, 't', 'e', 'u', 'n', 'j', 5, 't', 'e', 'u', 'n', 'h', 4, 't', 'e', 'u', 'd', 4, 't', 'e', 'u', 'l', 5, 't', 'e', 'u', 'l', 'g', 5, 't', 'e', 'u', 'l', 'm', 5, 't', 'e', 'u', 'l', 'b', 5, 't', 'e', 'u', 'l', 's', 5, 't', 'e', 'u', 'l', 't', 5, 't', 'e', 'u', 'l', 'p', 5, 't', 'e', 'u', 'l', 'h', 4, 't', 'e', 'u', 'm', 4, 't', 'e', 'u', 'b', 5, 't', 'e', 'u', 'b', 's', 4, 't', 'e', 'u', 's', 5, 't', 'e', 'u', 's', 's', 5, 't', 'e', 'u', 'n', 'g', 4, 't', 'e', 'u', 'j', 4, 't', 'e', 'u', 'c', 4, 't', 'e', 'u', 'k', 4, 't', 'e', 'u', 't', 4, 't', 'e', 'u', 'p', 4, 't', 'e', 'u', 'h', 3, 't', 'y', 'i', 4, 't', 'y', 'i', 'g', 5, 't', 'y', 'i', 'g', 'g', 5, 't', 'y', 'i', 'g', 's', 4, 't', 'y', 'i', 'n', 5, 't', 'y', 'i', 'n', 'j', 5, 't', 'y', 'i', 'n', 'h', 4, 't', 'y', 'i', 'd', 4, 't', 'y', 'i', 'l', 5, 't', 'y', 'i', 'l', 'g', 5, 't', 'y', 'i', 'l', 'm', 5, 't', 'y', 'i', 'l', 'b', 5, 't', 'y', 'i', 'l', 's', 5, 't', 'y', 'i', 'l', 't', 5, 't', 'y', 'i', 'l', 'p', 5, 't', 'y', 'i', 'l', 'h', 4, 't', 'y', 'i', 'm', 4, 't', 'y', 'i', 'b', 5, 't', 'y', 'i', 'b', 's', 4, 't', 'y', 'i', 's', 5, 't', 'y', 'i', 's', 's', 5, 't', 'y', 'i', 'n', 'g', 4, 't', 'y', 'i', 'j', 4, 't', 'y', 'i', 'c', 4, 't', 'y', 'i', 'k', 4, 't', 'y', 'i', 't', 4, 't', 'y', 'i', 'p', 4, 't', 'y', 'i', 'h', 2, 't', 'i', 3, 't', 'i', 'g', 4, 't', 'i', 'g', 'g', 4, 't', 'i', 'g', 's', 3, 't', 'i', 'n', 4, 't', 'i', 'n', 'j', 4, 't', 'i', 'n', 'h', 3, 't', 'i', 'd', 3, 't', 'i', 'l', 4, 't', 'i', 'l', 'g', 4, 't', 'i', 'l', 'm', 4, 't', 'i', 'l', 'b', 4, 't', 'i', 'l', 's', 4, 't', 'i', 'l', 't', 4, 't', 'i', 'l', 'p', 4, 't', 'i', 'l', 'h', 3, 't', 'i', 'm', 3, 't', 'i', 'b', 4, 't', 'i', 'b', 's', 3, 't', 'i', 's', 4, 't', 'i', 's', 's', 4, 't', 'i', 'n', 'g', 3, 't', 'i', 'j', 3, 't', 'i', 'c', 3, 't', 'i', 'k', 3, 't', 'i', 't', 3, 't', 'i', 'p', 3, 't', 'i', 'h', 2, 'p', 'a', 3, 'p', 'a', 'g', 4, 'p', 'a', 'g', 'g', 4, 'p', 'a', 'g', 's', 3, 'p', 'a', 'n', 4, 'p', 'a', 'n', 'j', 4, 'p', 'a', 'n', 'h', 3, 'p', 'a', 'd', 3, 'p', 'a', 'l', 4, 'p', 'a', 'l', 'g', 4, 'p', 'a', 'l', 'm', 4, 'p', 'a', 'l', 'b', 4, 'p', 'a', 'l', 's', 4, 'p', 'a', 'l', 't', 4, 'p', 'a', 'l', 'p', 4, 'p', 'a', 'l', 'h', 3, 'p', 'a', 'm', 3, 'p', 'a', 'b', 4, 'p', 'a', 'b', 's', 3, 'p', 'a', 's', 4, 'p', 'a', 's', 's', 4, 'p', 'a', 'n', 'g', 3, 'p', 'a', 'j', 3, 'p', 'a', 'c', 3, 'p', 'a', 'k', 3, 'p', 'a', 't', 3, 'p', 'a', 'p', 3, 'p', 'a', 'h', 3, 'p', 'a', 'e', 4, 'p', 'a', 'e', 'g', 5, 'p', 'a', 'e', 'g', 'g', 5, 'p', 'a', 'e', 'g', 's', 4, 'p', 'a', 'e', 'n', 5, 'p', 'a', 'e', 'n', 'j', 5, 'p', 'a', 'e', 'n', 'h', 4, 'p', 'a', 'e', 'd', 4, 'p', 'a', 'e', 'l', 5, 'p', 'a', 'e', 'l', 'g', 5, 'p', 'a', 'e', 'l', 'm', 5, 'p', 'a', 'e', 'l', 'b', 5, 'p', 'a', 'e', 'l', 's', 5, 'p', 'a', 'e', 'l', 't', 5, 'p', 'a', 'e', 'l', 'p', 5, 'p', 'a', 'e', 'l', 'h', 4, 'p', 'a', 'e', 'm', 4, 'p', 'a', 'e', 'b', 5, 'p', 'a', 'e', 'b', 's', 4, 'p', 'a', 'e', 's', 5, 'p', 'a', 'e', 's', 's', 5, 'p', 'a', 'e', 'n', 'g', 4, 'p', 'a', 'e', 'j', 4, 'p', 'a', 'e', 'c', 4, 'p', 'a', 'e', 'k', 4, 'p', 'a', 'e', 't', 4, 'p', 'a', 'e', 'p', 4, 'p', 'a', 'e', 'h', 3, 'p', 'y', 'a', 4, 'p', 'y', 'a', 'g', 5, 'p', 'y', 'a', 'g', 'g', 5, 'p', 'y', 'a', 'g', 's', 4, 'p', 'y', 'a', 'n', 5, 'p', 'y', 'a', 'n', 'j', 5, 'p', 'y', 'a', 'n', 'h', 4, 'p', 'y', 'a', 'd', 4, 'p', 'y', 'a', 'l', 5, 'p', 'y', 'a', 'l', 'g', 5, 'p', 'y', 'a', 'l', 'm', 5, 'p', 'y', 'a', 'l', 'b', 5, 'p', 'y', 'a', 'l', 's', 5, 'p', 'y', 'a', 'l', 't', 5, 'p', 'y', 'a', 'l', 'p', 5, 'p', 'y', 'a', 'l', 'h', 4, 'p', 'y', 'a', 'm', 4, 'p', 'y', 'a', 'b', 5, 'p', 'y', 'a', 'b', 's', 4, 'p', 'y', 'a', 's', 5, 'p', 'y', 'a', 's', 's', 5, 'p', 'y', 'a', 'n', 'g', 4, 'p', 'y', 'a', 'j', 4, 'p', 'y', 'a', 'c', 4, 'p', 'y', 'a', 'k', 4, 'p', 'y', 'a', 't', 4, 'p', 'y', 'a', 'p', 4, 'p', 'y', 'a', 'h', 4, 'p', 'y', 'a', 'e', 5, 'p', 'y', 'a', 'e', 'g', 6, 'p', 'y', 'a', 'e', 'g', 'g', 6, 'p', 'y', 'a', 'e', 'g', 's', 5, 'p', 'y', 'a', 'e', 'n', 6, 'p', 'y', 'a', 'e', 'n', 'j', 6, 'p', 'y', 'a', 'e', 'n', 'h', 5, 'p', 'y', 'a', 'e', 'd', 5, 'p', 'y', 'a', 'e', 'l', 6, 'p', 'y', 'a', 'e', 'l', 'g', 6, 'p', 'y', 'a', 'e', 'l', 'm', 6, 'p', 'y', 'a', 'e', 'l', 'b', 6, 'p', 'y', 'a', 'e', 'l', 's', 6, 'p', 'y', 'a', 'e', 'l', 't', 6, 'p', 'y', 'a', 'e', 'l', 'p', 6, 'p', 'y', 'a', 'e', 'l', 'h', 5, 'p', 'y', 'a', 'e', 'm', 5, 'p', 'y', 'a', 'e', 'b', 6, 'p', 'y', 'a', 'e', 'b', 's', 5, 'p', 'y', 'a', 'e', 's', 6, 'p', 'y', 'a', 'e', 's', 's', 6, 'p', 'y', 'a', 'e', 'n', 'g', 5, 'p', 'y', 'a', 'e', 'j', 5, 'p', 'y', 'a', 'e', 'c', 5, 'p', 'y', 'a', 'e', 'k', 5, 'p', 'y', 'a', 'e', 't', 5, 'p', 'y', 'a', 'e', 'p', 5, 'p', 'y', 'a', 'e', 'h', 3, 'p', 'e', 'o', 4, 'p', 'e', 'o', 'g', 5, 'p', 'e', 'o', 'g', 'g', 5, 'p', 'e', 'o', 'g', 's', 4, 'p', 'e', 'o', 'n', 5, 'p', 'e', 'o', 'n', 'j', 5, 'p', 'e', 'o', 'n', 'h', 4, 'p', 'e', 'o', 'd', 4, 'p', 'e', 'o', 'l', 5, 'p', 'e', 'o', 'l', 'g', 5, 'p', 'e', 'o', 'l', 'm', 5, 'p', 'e', 'o', 'l', 'b', 5, 'p', 'e', 'o', 'l', 's', 5, 'p', 'e', 'o', 'l', 't', 5, 'p', 'e', 'o', 'l', 'p', 5, 'p', 'e', 'o', 'l', 'h', 4, 'p', 'e', 'o', 'm', 4, 'p', 'e', 'o', 'b', 5, 'p', 'e', 'o', 'b', 's', 4, 'p', 'e', 'o', 's', 5, 'p', 'e', 'o', 's', 's', 5, 'p', 'e', 'o', 'n', 'g', 4, 'p', 'e', 'o', 'j', 4, 'p', 'e', 'o', 'c', 4, 'p', 'e', 'o', 'k', 4, 'p', 'e', 'o', 't', 4, 'p', 'e', 'o', 'p', 4, 'p', 'e', 'o', 'h', 2, 'p', 'e', 3, 'p', 'e', 'g', 4, 'p', 'e', 'g', 'g', 4, 'p', 'e', 'g', 's', 3, 'p', 'e', 'n', 4, 'p', 'e', 'n', 'j', 4, 'p', 'e', 'n', 'h', 3, 'p', 'e', 'd', 3, 'p', 'e', 'l', 4, 'p', 'e', 'l', 'g', 4, 'p', 'e', 'l', 'm', 4, 'p', 'e', 'l', 'b', 4, 'p', 'e', 'l', 's', 4, 'p', 'e', 'l', 't', 4, 'p', 'e', 'l', 'p', 4, 'p', 'e', 'l', 'h', 3, 'p', 'e', 'm', 3, 'p', 'e', 'b', 4, 'p', 'e', 'b', 's', 3, 'p', 'e', 's', 4, 'p', 'e', 's', 's', 4, 'p', 'e', 'n', 'g', 3, 'p', 'e', 'j', 3, 'p', 'e', 'c', 3, 'p', 'e', 'k', 3, 'p', 'e', 't', 3, 'p', 'e', 'p', 3, 'p', 'e', 'h', 4, 'p', 'y', 'e', 'o', 5, 'p', 'y', 'e', 'o', 'g', 6, 'p', 'y', 'e', 'o', 'g', 'g', 6, 'p', 'y', 'e', 'o', 'g', 's', 5, 'p', 'y', 'e', 'o', 'n', 6, 'p', 'y', 'e', 'o', 'n', 'j', 6, 'p', 'y', 'e', 'o', 'n', 'h', 5, 'p', 'y', 'e', 'o', 'd', 5, 'p', 'y', 'e', 'o', 'l', 6, 'p', 'y', 'e', 'o', 'l', 'g', 6, 'p', 'y', 'e', 'o', 'l', 'm', 6, 'p', 'y', 'e', 'o', 'l', 'b', 6, 'p', 'y', 'e', 'o', 'l', 's', 6, 'p', 'y', 'e', 'o', 'l', 't', 6, 'p', 'y', 'e', 'o', 'l', 'p', 6, 'p', 'y', 'e', 'o', 'l', 'h', 5, 'p', 'y', 'e', 'o', 'm', 5, 'p', 'y', 'e', 'o', 'b', 6, 'p', 'y', 'e', 'o', 'b', 's', 5, 'p', 'y', 'e', 'o', 's', 6, 'p', 'y', 'e', 'o', 's', 's', 6, 'p', 'y', 'e', 'o', 'n', 'g', 5, 'p', 'y', 'e', 'o', 'j', 5, 'p', 'y', 'e', 'o', 'c', 5, 'p', 'y', 'e', 'o', 'k', 5, 'p', 'y', 'e', 'o', 't', 5, 'p', 'y', 'e', 'o', 'p', 5, 'p', 'y', 'e', 'o', 'h', 3, 'p', 'y', 'e', 4, 'p', 'y', 'e', 'g', 5, 'p', 'y', 'e', 'g', 'g', 5, 'p', 'y', 'e', 'g', 's', 4, 'p', 'y', 'e', 'n', 5, 'p', 'y', 'e', 'n', 'j', 5, 'p', 'y', 'e', 'n', 'h', 4, 'p', 'y', 'e', 'd', 4, 'p', 'y', 'e', 'l', 5, 'p', 'y', 'e', 'l', 'g', 5, 'p', 'y', 'e', 'l', 'm', 5, 'p', 'y', 'e', 'l', 'b', 5, 'p', 'y', 'e', 'l', 's', 5, 'p', 'y', 'e', 'l', 't', 5, 'p', 'y', 'e', 'l', 'p', 5, 'p', 'y', 'e', 'l', 'h', 4, 'p', 'y', 'e', 'm', 4, 'p', 'y', 'e', 'b', 5, 'p', 'y', 'e', 'b', 's', 4, 'p', 'y', 'e', 's', 5, 'p', 'y', 'e', 's', 's', 5, 'p', 'y', 'e', 'n', 'g', 4, 'p', 'y', 'e', 'j', 4, 'p', 'y', 'e', 'c', 4, 'p', 'y', 'e', 'k', 4, 'p', 'y', 'e', 't', 4, 'p', 'y', 'e', 'p', 4, 'p', 'y', 'e', 'h', 2, 'p', 'o', 3, 'p', 'o', 'g', 4, 'p', 'o', 'g', 'g', 4, 'p', 'o', 'g', 's', 3, 'p', 'o', 'n', 4, 'p', 'o', 'n', 'j', 4, 'p', 'o', 'n', 'h', 3, 'p', 'o', 'd', 3, 'p', 'o', 'l', 4, 'p', 'o', 'l', 'g', 4, 'p', 'o', 'l', 'm', 4, 'p', 'o', 'l', 'b', 4, 'p', 'o', 'l', 's', 4, 'p', 'o', 'l', 't', 4, 'p', 'o', 'l', 'p', 4, 'p', 'o', 'l', 'h', 3, 'p', 'o', 'm', 3, 'p', 'o', 'b', 4, 'p', 'o', 'b', 's', 3, 'p', 'o', 's', 4, 'p', 'o', 's', 's', 4, 'p', 'o', 'n', 'g', 3, 'p', 'o', 'j', 3, 'p', 'o', 'c', 3, 'p', 'o', 'k', 3, 'p', 'o', 't', 3, 'p', 'o', 'p', 3, 'p', 'o', 'h', 3, 'p', 'w', 'a', 4, 'p', 'w', 'a', 'g', 5, 'p', 'w', 'a', 'g', 'g', 5, 'p', 'w', 'a', 'g', 's', 4, 'p', 'w', 'a', 'n', 5, 'p', 'w', 'a', 'n', 'j', 5, 'p', 'w', 'a', 'n', 'h', 4, 'p', 'w', 'a', 'd', 4, 'p', 'w', 'a', 'l', 5, 'p', 'w', 'a', 'l', 'g', 5, 'p', 'w', 'a', 'l', 'm', 5, 'p', 'w', 'a', 'l', 'b', 5, 'p', 'w', 'a', 'l', 's', 5, 'p', 'w', 'a', 'l', 't', 5, 'p', 'w', 'a', 'l', 'p', 5, 'p', 'w', 'a', 'l', 'h', 4, 'p', 'w', 'a', 'm', 4, 'p', 'w', 'a', 'b', 5, 'p', 'w', 'a', 'b', 's', 4, 'p', 'w', 'a', 's', 5, 'p', 'w', 'a', 's', 's', 5, 'p', 'w', 'a', 'n', 'g', 4, 'p', 'w', 'a', 'j', 4, 'p', 'w', 'a', 'c', 4, 'p', 'w', 'a', 'k', 4, 'p', 'w', 'a', 't', 4, 'p', 'w', 'a', 'p', 4, 'p', 'w', 'a', 'h', 4, 'p', 'w', 'a', 'e', 5, 'p', 'w', 'a', 'e', 'g', 6, 'p', 'w', 'a', 'e', 'g', 'g', 6, 'p', 'w', 'a', 'e', 'g', 's', 5, 'p', 'w', 'a', 'e', 'n', 6, 'p', 'w', 'a', 'e', 'n', 'j', 6, 'p', 'w', 'a', 'e', 'n', 'h', 5, 'p', 'w', 'a', 'e', 'd', 5, 'p', 'w', 'a', 'e', 'l', 6, 'p', 'w', 'a', 'e', 'l', 'g', 6, 'p', 'w', 'a', 'e', 'l', 'm', 6, 'p', 'w', 'a', 'e', 'l', 'b', 6, 'p', 'w', 'a', 'e', 'l', 's', 6, 'p', 'w', 'a', 'e', 'l', 't', 6, 'p', 'w', 'a', 'e', 'l', 'p', 6, 'p', 'w', 'a', 'e', 'l', 'h', 5, 'p', 'w', 'a', 'e', 'm', 5, 'p', 'w', 'a', 'e', 'b', 6, 'p', 'w', 'a', 'e', 'b', 's', 5, 'p', 'w', 'a', 'e', 's', 6, 'p', 'w', 'a', 'e', 's', 's', 6, 'p', 'w', 'a', 'e', 'n', 'g', 5, 'p', 'w', 'a', 'e', 'j', 5, 'p', 'w', 'a', 'e', 'c', 5, 'p', 'w', 'a', 'e', 'k', 5, 'p', 'w', 'a', 'e', 't', 5, 'p', 'w', 'a', 'e', 'p', 5, 'p', 'w', 'a', 'e', 'h', 3, 'p', 'o', 'e', 4, 'p', 'o', 'e', 'g', 5, 'p', 'o', 'e', 'g', 'g', 5, 'p', 'o', 'e', 'g', 's', 4, 'p', 'o', 'e', 'n', 5, 'p', 'o', 'e', 'n', 'j', 5, 'p', 'o', 'e', 'n', 'h', 4, 'p', 'o', 'e', 'd', 4, 'p', 'o', 'e', 'l', 5, 'p', 'o', 'e', 'l', 'g', 5, 'p', 'o', 'e', 'l', 'm', 5, 'p', 'o', 'e', 'l', 'b', 5, 'p', 'o', 'e', 'l', 's', 5, 'p', 'o', 'e', 'l', 't', 5, 'p', 'o', 'e', 'l', 'p', 5, 'p', 'o', 'e', 'l', 'h', 4, 'p', 'o', 'e', 'm', 4, 'p', 'o', 'e', 'b', 5, 'p', 'o', 'e', 'b', 's', 4, 'p', 'o', 'e', 's', 5, 'p', 'o', 'e', 's', 's', 5, 'p', 'o', 'e', 'n', 'g', 4, 'p', 'o', 'e', 'j', 4, 'p', 'o', 'e', 'c', 4, 'p', 'o', 'e', 'k', 4, 'p', 'o', 'e', 't', 4, 'p', 'o', 'e', 'p', 4, 'p', 'o', 'e', 'h', 3, 'p', 'y', 'o', 4, 'p', 'y', 'o', 'g', 5, 'p', 'y', 'o', 'g', 'g', 5, 'p', 'y', 'o', 'g', 's', 4, 'p', 'y', 'o', 'n', 5, 'p', 'y', 'o', 'n', 'j', 5, 'p', 'y', 'o', 'n', 'h', 4, 'p', 'y', 'o', 'd', 4, 'p', 'y', 'o', 'l', 5, 'p', 'y', 'o', 'l', 'g', 5, 'p', 'y', 'o', 'l', 'm', 5, 'p', 'y', 'o', 'l', 'b', 5, 'p', 'y', 'o', 'l', 's', 5, 'p', 'y', 'o', 'l', 't', 5, 'p', 'y', 'o', 'l', 'p', 5, 'p', 'y', 'o', 'l', 'h', 4, 'p', 'y', 'o', 'm', 4, 'p', 'y', 'o', 'b', 5, 'p', 'y', 'o', 'b', 's', 4, 'p', 'y', 'o', 's', 5, 'p', 'y', 'o', 's', 's', 5, 'p', 'y', 'o', 'n', 'g', 4, 'p', 'y', 'o', 'j', 4, 'p', 'y', 'o', 'c', 4, 'p', 'y', 'o', 'k', 4, 'p', 'y', 'o', 't', 4, 'p', 'y', 'o', 'p', 4, 'p', 'y', 'o', 'h', 2, 'p', 'u', 3, 'p', 'u', 'g', 4, 'p', 'u', 'g', 'g', 4, 'p', 'u', 'g', 's', 3, 'p', 'u', 'n', 4, 'p', 'u', 'n', 'j', 4, 'p', 'u', 'n', 'h', 3, 'p', 'u', 'd', 3, 'p', 'u', 'l', 4, 'p', 'u', 'l', 'g', 4, 'p', 'u', 'l', 'm', 4, 'p', 'u', 'l', 'b', 4, 'p', 'u', 'l', 's', 4, 'p', 'u', 'l', 't', 4, 'p', 'u', 'l', 'p', 4, 'p', 'u', 'l', 'h', 3, 'p', 'u', 'm', 3, 'p', 'u', 'b', 4, 'p', 'u', 'b', 's', 3, 'p', 'u', 's', 4, 'p', 'u', 's', 's', 4, 'p', 'u', 'n', 'g', 3, 'p', 'u', 'j', 3, 'p', 'u', 'c', 3, 'p', 'u', 'k', 3, 'p', 'u', 't', 3, 'p', 'u', 'p', 3, 'p', 'u', 'h', 4, 'p', 'w', 'e', 'o', 5, 'p', 'w', 'e', 'o', 'g', 6, 'p', 'w', 'e', 'o', 'g', 'g', 6, 'p', 'w', 'e', 'o', 'g', 's', 5, 'p', 'w', 'e', 'o', 'n', 6, 'p', 'w', 'e', 'o', 'n', 'j', 6, 'p', 'w', 'e', 'o', 'n', 'h', 5, 'p', 'w', 'e', 'o', 'd', 5, 'p', 'w', 'e', 'o', 'l', 6, 'p', 'w', 'e', 'o', 'l', 'g', 6, 'p', 'w', 'e', 'o', 'l', 'm', 6, 'p', 'w', 'e', 'o', 'l', 'b', 6, 'p', 'w', 'e', 'o', 'l', 's', 6, 'p', 'w', 'e', 'o', 'l', 't', 6, 'p', 'w', 'e', 'o', 'l', 'p', 6, 'p', 'w', 'e', 'o', 'l', 'h', 5, 'p', 'w', 'e', 'o', 'm', 5, 'p', 'w', 'e', 'o', 'b', 6, 'p', 'w', 'e', 'o', 'b', 's', 5, 'p', 'w', 'e', 'o', 's', 6, 'p', 'w', 'e', 'o', 's', 's', 6, 'p', 'w', 'e', 'o', 'n', 'g', 5, 'p', 'w', 'e', 'o', 'j', 5, 'p', 'w', 'e', 'o', 'c', 5, 'p', 'w', 'e', 'o', 'k', 5, 'p', 'w', 'e', 'o', 't', 5, 'p', 'w', 'e', 'o', 'p', 5, 'p', 'w', 'e', 'o', 'h', 3, 'p', 'w', 'e', 4, 'p', 'w', 'e', 'g', 5, 'p', 'w', 'e', 'g', 'g', 5, 'p', 'w', 'e', 'g', 's', 4, 'p', 'w', 'e', 'n', 5, 'p', 'w', 'e', 'n', 'j', 5, 'p', 'w', 'e', 'n', 'h', 4, 'p', 'w', 'e', 'd', 4, 'p', 'w', 'e', 'l', 5, 'p', 'w', 'e', 'l', 'g', 5, 'p', 'w', 'e', 'l', 'm', 5, 'p', 'w', 'e', 'l', 'b', 5, 'p', 'w', 'e', 'l', 's', 5, 'p', 'w', 'e', 'l', 't', 5, 'p', 'w', 'e', 'l', 'p', 5, 'p', 'w', 'e', 'l', 'h', 4, 'p', 'w', 'e', 'm', 4, 'p', 'w', 'e', 'b', 5, 'p', 'w', 'e', 'b', 's', 4, 'p', 'w', 'e', 's', 5, 'p', 'w', 'e', 's', 's', 5, 'p', 'w', 'e', 'n', 'g', 4, 'p', 'w', 'e', 'j', 4, 'p', 'w', 'e', 'c', 4, 'p', 'w', 'e', 'k', 4, 'p', 'w', 'e', 't', 4, 'p', 'w', 'e', 'p', 4, 'p', 'w', 'e', 'h', 3, 'p', 'w', 'i', 4, 'p', 'w', 'i', 'g', 5, 'p', 'w', 'i', 'g', 'g', 5, 'p', 'w', 'i', 'g', 's', 4, 'p', 'w', 'i', 'n', 5, 'p', 'w', 'i', 'n', 'j', 5, 'p', 'w', 'i', 'n', 'h', 4, 'p', 'w', 'i', 'd', 4, 'p', 'w', 'i', 'l', 5, 'p', 'w', 'i', 'l', 'g', 5, 'p', 'w', 'i', 'l', 'm', 5, 'p', 'w', 'i', 'l', 'b', 5, 'p', 'w', 'i', 'l', 's', 5, 'p', 'w', 'i', 'l', 't', 5, 'p', 'w', 'i', 'l', 'p', 5, 'p', 'w', 'i', 'l', 'h', 4, 'p', 'w', 'i', 'm', 4, 'p', 'w', 'i', 'b', 5, 'p', 'w', 'i', 'b', 's', 4, 'p', 'w', 'i', 's', 5, 'p', 'w', 'i', 's', 's', 5, 'p', 'w', 'i', 'n', 'g', 4, 'p', 'w', 'i', 'j', 4, 'p', 'w', 'i', 'c', 4, 'p', 'w', 'i', 'k', 4, 'p', 'w', 'i', 't', 4, 'p', 'w', 'i', 'p', 4, 'p', 'w', 'i', 'h', 3, 'p', 'y', 'u', 4, 'p', 'y', 'u', 'g', 5, 'p', 'y', 'u', 'g', 'g', 5, 'p', 'y', 'u', 'g', 's', 4, 'p', 'y', 'u', 'n', 5, 'p', 'y', 'u', 'n', 'j', 5, 'p', 'y', 'u', 'n', 'h', 4, 'p', 'y', 'u', 'd', 4, 'p', 'y', 'u', 'l', 5, 'p', 'y', 'u', 'l', 'g', 5, 'p', 'y', 'u', 'l', 'm', 5, 'p', 'y', 'u', 'l', 'b', 5, 'p', 'y', 'u', 'l', 's', 5, 'p', 'y', 'u', 'l', 't', 5, 'p', 'y', 'u', 'l', 'p', 5, 'p', 'y', 'u', 'l', 'h', 4, 'p', 'y', 'u', 'm', 4, 'p', 'y', 'u', 'b', 5, 'p', 'y', 'u', 'b', 's', 4, 'p', 'y', 'u', 's', 5, 'p', 'y', 'u', 's', 's', 5, 'p', 'y', 'u', 'n', 'g', 4, 'p', 'y', 'u', 'j', 4, 'p', 'y', 'u', 'c', 4, 'p', 'y', 'u', 'k', 4, 'p', 'y', 'u', 't', 4, 'p', 'y', 'u', 'p', 4, 'p', 'y', 'u', 'h', 3, 'p', 'e', 'u', 4, 'p', 'e', 'u', 'g', 5, 'p', 'e', 'u', 'g', 'g', 5, 'p', 'e', 'u', 'g', 's', 4, 'p', 'e', 'u', 'n', 5, 'p', 'e', 'u', 'n', 'j', 5, 'p', 'e', 'u', 'n', 'h', 4, 'p', 'e', 'u', 'd', 4, 'p', 'e', 'u', 'l', 5, 'p', 'e', 'u', 'l', 'g', 5, 'p', 'e', 'u', 'l', 'm', 5, 'p', 'e', 'u', 'l', 'b', 5, 'p', 'e', 'u', 'l', 's', 5, 'p', 'e', 'u', 'l', 't', 5, 'p', 'e', 'u', 'l', 'p', 5, 'p', 'e', 'u', 'l', 'h', 4, 'p', 'e', 'u', 'm', 4, 'p', 'e', 'u', 'b', 5, 'p', 'e', 'u', 'b', 's', 4, 'p', 'e', 'u', 's', 5, 'p', 'e', 'u', 's', 's', 5, 'p', 'e', 'u', 'n', 'g', 4, 'p', 'e', 'u', 'j', 4, 'p', 'e', 'u', 'c', 4, 'p', 'e', 'u', 'k', 4, 'p', 'e', 'u', 't', 4, 'p', 'e', 'u', 'p', 4, 'p', 'e', 'u', 'h', 3, 'p', 'y', 'i', 4, 'p', 'y', 'i', 'g', 5, 'p', 'y', 'i', 'g', 'g', 5, 'p', 'y', 'i', 'g', 's', 4, 'p', 'y', 'i', 'n', 5, 'p', 'y', 'i', 'n', 'j', 5, 'p', 'y', 'i', 'n', 'h', 4, 'p', 'y', 'i', 'd', 4, 'p', 'y', 'i', 'l', 5, 'p', 'y', 'i', 'l', 'g', 5, 'p', 'y', 'i', 'l', 'm', 5, 'p', 'y', 'i', 'l', 'b', 5, 'p', 'y', 'i', 'l', 's', 5, 'p', 'y', 'i', 'l', 't', 5, 'p', 'y', 'i', 'l', 'p', 5, 'p', 'y', 'i', 'l', 'h', 4, 'p', 'y', 'i', 'm', 4, 'p', 'y', 'i', 'b', 5, 'p', 'y', 'i', 'b', 's', 4, 'p', 'y', 'i', 's', 5, 'p', 'y', 'i', 's', 's', 5, 'p', 'y', 'i', 'n', 'g', 4, 'p', 'y', 'i', 'j', 4, 'p', 'y', 'i', 'c', 4, 'p', 'y', 'i', 'k', 4, 'p', 'y', 'i', 't', 4, 'p', 'y', 'i', 'p', 4, 'p', 'y', 'i', 'h', 2, 'p', 'i', 3, 'p', 'i', 'g', 4, 'p', 'i', 'g', 'g', 4, 'p', 'i', 'g', 's', 3, 'p', 'i', 'n', 4, 'p', 'i', 'n', 'j', 4, 'p', 'i', 'n', 'h', 3, 'p', 'i', 'd', 3, 'p', 'i', 'l', 4, 'p', 'i', 'l', 'g', 4, 'p', 'i', 'l', 'm', 4, 'p', 'i', 'l', 'b', 4, 'p', 'i', 'l', 's', 4, 'p', 'i', 'l', 't', 4, 'p', 'i', 'l', 'p', 4, 'p', 'i', 'l', 'h', 3, 'p', 'i', 'm', 3, 'p', 'i', 'b', 4, 'p', 'i', 'b', 's', 3, 'p', 'i', 's', 4, 'p', 'i', 's', 's', 4, 'p', 'i', 'n', 'g', 3, 'p', 'i', 'j', 3, 'p', 'i', 'c', 3, 'p', 'i', 'k', 3, 'p', 'i', 't', 3, 'p', 'i', 'p', 3, 'p', 'i', 'h', 2, 'h', 'a', 3, 'h', 'a', 'g', 4, 'h', 'a', 'g', 'g', 4, 'h', 'a', 'g', 's', 3, 'h', 'a', 'n', 4, 'h', 'a', 'n', 'j', 4, 'h', 'a', 'n', 'h', 3, 'h', 'a', 'd', 3, 'h', 'a', 'l', 4, 'h', 'a', 'l', 'g', 4, 'h', 'a', 'l', 'm', 4, 'h', 'a', 'l', 'b', 4, 'h', 'a', 'l', 's', 4, 'h', 'a', 'l', 't', 4, 'h', 'a', 'l', 'p', 4, 'h', 'a', 'l', 'h', 3, 'h', 'a', 'm', 3, 'h', 'a', 'b', 4, 'h', 'a', 'b', 's', 3, 'h', 'a', 's', 4, 'h', 'a', 's', 's', 4, 'h', 'a', 'n', 'g', 3, 'h', 'a', 'j', 3, 'h', 'a', 'c', 3, 'h', 'a', 'k', 3, 'h', 'a', 't', 3, 'h', 'a', 'p', 3, 'h', 'a', 'h', 3, 'h', 'a', 'e', 4, 'h', 'a', 'e', 'g', 5, 'h', 'a', 'e', 'g', 'g', 5, 'h', 'a', 'e', 'g', 's', 4, 'h', 'a', 'e', 'n', 5, 'h', 'a', 'e', 'n', 'j', 5, 'h', 'a', 'e', 'n', 'h', 4, 'h', 'a', 'e', 'd', 4, 'h', 'a', 'e', 'l', 5, 'h', 'a', 'e', 'l', 'g', 5, 'h', 'a', 'e', 'l', 'm', 5, 'h', 'a', 'e', 'l', 'b', 5, 'h', 'a', 'e', 'l', 's', 5, 'h', 'a', 'e', 'l', 't', 5, 'h', 'a', 'e', 'l', 'p', 5, 'h', 'a', 'e', 'l', 'h', 4, 'h', 'a', 'e', 'm', 4, 'h', 'a', 'e', 'b', 5, 'h', 'a', 'e', 'b', 's', 4, 'h', 'a', 'e', 's', 5, 'h', 'a', 'e', 's', 's', 5, 'h', 'a', 'e', 'n', 'g', 4, 'h', 'a', 'e', 'j', 4, 'h', 'a', 'e', 'c', 4, 'h', 'a', 'e', 'k', 4, 'h', 'a', 'e', 't', 4, 'h', 'a', 'e', 'p', 4, 'h', 'a', 'e', 'h', 3, 'h', 'y', 'a', 4, 'h', 'y', 'a', 'g', 5, 'h', 'y', 'a', 'g', 'g', 5, 'h', 'y', 'a', 'g', 's', 4, 'h', 'y', 'a', 'n', 5, 'h', 'y', 'a', 'n', 'j', 5, 'h', 'y', 'a', 'n', 'h', 4, 'h', 'y', 'a', 'd', 4, 'h', 'y', 'a', 'l', 5, 'h', 'y', 'a', 'l', 'g', 5, 'h', 'y', 'a', 'l', 'm', 5, 'h', 'y', 'a', 'l', 'b', 5, 'h', 'y', 'a', 'l', 's', 5, 'h', 'y', 'a', 'l', 't', 5, 'h', 'y', 'a', 'l', 'p', 5, 'h', 'y', 'a', 'l', 'h', 4, 'h', 'y', 'a', 'm', 4, 'h', 'y', 'a', 'b', 5, 'h', 'y', 'a', 'b', 's', 4, 'h', 'y', 'a', 's', 5, 'h', 'y', 'a', 's', 's', 5, 'h', 'y', 'a', 'n', 'g', 4, 'h', 'y', 'a', 'j', 4, 'h', 'y', 'a', 'c', 4, 'h', 'y', 'a', 'k', 4, 'h', 'y', 'a', 't', 4, 'h', 'y', 'a', 'p', 4, 'h', 'y', 'a', 'h', 4, 'h', 'y', 'a', 'e', 5, 'h', 'y', 'a', 'e', 'g', 6, 'h', 'y', 'a', 'e', 'g', 'g', 6, 'h', 'y', 'a', 'e', 'g', 's', 5, 'h', 'y', 'a', 'e', 'n', 6, 'h', 'y', 'a', 'e', 'n', 'j', 6, 'h', 'y', 'a', 'e', 'n', 'h', 5, 'h', 'y', 'a', 'e', 'd', 5, 'h', 'y', 'a', 'e', 'l', 6, 'h', 'y', 'a', 'e', 'l', 'g', 6, 'h', 'y', 'a', 'e', 'l', 'm', 6, 'h', 'y', 'a', 'e', 'l', 'b', 6, 'h', 'y', 'a', 'e', 'l', 's', 6, 'h', 'y', 'a', 'e', 'l', 't', 6, 'h', 'y', 'a', 'e', 'l', 'p', 6, 'h', 'y', 'a', 'e', 'l', 'h', 5, 'h', 'y', 'a', 'e', 'm', 5, 'h', 'y', 'a', 'e', 'b', 6, 'h', 'y', 'a', 'e', 'b', 's', 5, 'h', 'y', 'a', 'e', 's', 6, 'h', 'y', 'a', 'e', 's', 's', 6, 'h', 'y', 'a', 'e', 'n', 'g', 5, 'h', 'y', 'a', 'e', 'j', 5, 'h', 'y', 'a', 'e', 'c', 5, 'h', 'y', 'a', 'e', 'k', 5, 'h', 'y', 'a', 'e', 't', 5, 'h', 'y', 'a', 'e', 'p', 5, 'h', 'y', 'a', 'e', 'h', 3, 'h', 'e', 'o', 4, 'h', 'e', 'o', 'g', 5, 'h', 'e', 'o', 'g', 'g', 5, 'h', 'e', 'o', 'g', 's', 4, 'h', 'e', 'o', 'n', 5, 'h', 'e', 'o', 'n', 'j', 5, 'h', 'e', 'o', 'n', 'h', 4, 'h', 'e', 'o', 'd', 4, 'h', 'e', 'o', 'l', 5, 'h', 'e', 'o', 'l', 'g', 5, 'h', 'e', 'o', 'l', 'm', 5, 'h', 'e', 'o', 'l', 'b', 5, 'h', 'e', 'o', 'l', 's', 5, 'h', 'e', 'o', 'l', 't', 5, 'h', 'e', 'o', 'l', 'p', 5, 'h', 'e', 'o', 'l', 'h', 4, 'h', 'e', 'o', 'm', 4, 'h', 'e', 'o', 'b', 5, 'h', 'e', 'o', 'b', 's', 4, 'h', 'e', 'o', 's', 5, 'h', 'e', 'o', 's', 's', 5, 'h', 'e', 'o', 'n', 'g', 4, 'h', 'e', 'o', 'j', 4, 'h', 'e', 'o', 'c', 4, 'h', 'e', 'o', 'k', 4, 'h', 'e', 'o', 't', 4, 'h', 'e', 'o', 'p', 4, 'h', 'e', 'o', 'h', 2, 'h', 'e', 3, 'h', 'e', 'g', 4, 'h', 'e', 'g', 'g', 4, 'h', 'e', 'g', 's', 3, 'h', 'e', 'n', 4, 'h', 'e', 'n', 'j', 4, 'h', 'e', 'n', 'h', 3, 'h', 'e', 'd', 3, 'h', 'e', 'l', 4, 'h', 'e', 'l', 'g', 4, 'h', 'e', 'l', 'm', 4, 'h', 'e', 'l', 'b', 4, 'h', 'e', 'l', 's', 4, 'h', 'e', 'l', 't', 4, 'h', 'e', 'l', 'p', 4, 'h', 'e', 'l', 'h', 3, 'h', 'e', 'm', 3, 'h', 'e', 'b', 4, 'h', 'e', 'b', 's', 3, 'h', 'e', 's', 4, 'h', 'e', 's', 's', 4, 'h', 'e', 'n', 'g', 3, 'h', 'e', 'j', 3, 'h', 'e', 'c', 3, 'h', 'e', 'k', 3, 'h', 'e', 't', 3, 'h', 'e', 'p', 3, 'h', 'e', 'h', 4, 'h', 'y', 'e', 'o', 5, 'h', 'y', 'e', 'o', 'g', 6, 'h', 'y', 'e', 'o', 'g', 'g', 6, 'h', 'y', 'e', 'o', 'g', 's', 5, 'h', 'y', 'e', 'o', 'n', 6, 'h', 'y', 'e', 'o', 'n', 'j', 6, 'h', 'y', 'e', 'o', 'n', 'h', 5, 'h', 'y', 'e', 'o', 'd', 5, 'h', 'y', 'e', 'o', 'l', 6, 'h', 'y', 'e', 'o', 'l', 'g', 6, 'h', 'y', 'e', 'o', 'l', 'm', 6, 'h', 'y', 'e', 'o', 'l', 'b', 6, 'h', 'y', 'e', 'o', 'l', 's', 6, 'h', 'y', 'e', 'o', 'l', 't', 6, 'h', 'y', 'e', 'o', 'l', 'p', 6, 'h', 'y', 'e', 'o', 'l', 'h', 5, 'h', 'y', 'e', 'o', 'm', 5, 'h', 'y', 'e', 'o', 'b', 6, 'h', 'y', 'e', 'o', 'b', 's', 5, 'h', 'y', 'e', 'o', 's', 6, 'h', 'y', 'e', 'o', 's', 's', 6, 'h', 'y', 'e', 'o', 'n', 'g', 5, 'h', 'y', 'e', 'o', 'j', 5, 'h', 'y', 'e', 'o', 'c', 5, 'h', 'y', 'e', 'o', 'k', 5, 'h', 'y', 'e', 'o', 't', 5, 'h', 'y', 'e', 'o', 'p', 5, 'h', 'y', 'e', 'o', 'h', 3, 'h', 'y', 'e', 4, 'h', 'y', 'e', 'g', 5, 'h', 'y', 'e', 'g', 'g', 5, 'h', 'y', 'e', 'g', 's', 4, 'h', 'y', 'e', 'n', 5, 'h', 'y', 'e', 'n', 'j', 5, 'h', 'y', 'e', 'n', 'h', 4, 'h', 'y', 'e', 'd', 4, 'h', 'y', 'e', 'l', 5, 'h', 'y', 'e', 'l', 'g', 5, 'h', 'y', 'e', 'l', 'm', 5, 'h', 'y', 'e', 'l', 'b', 5, 'h', 'y', 'e', 'l', 's', 5, 'h', 'y', 'e', 'l', 't', 5, 'h', 'y', 'e', 'l', 'p', 5, 'h', 'y', 'e', 'l', 'h', 4, 'h', 'y', 'e', 'm', 4, 'h', 'y', 'e', 'b', 5, 'h', 'y', 'e', 'b', 's', 4, 'h', 'y', 'e', 's', 5, 'h', 'y', 'e', 's', 's', 5, 'h', 'y', 'e', 'n', 'g', 4, 'h', 'y', 'e', 'j', 4, 'h', 'y', 'e', 'c', 4, 'h', 'y', 'e', 'k', 4, 'h', 'y', 'e', 't', 4, 'h', 'y', 'e', 'p', 4, 'h', 'y', 'e', 'h', 2, 'h', 'o', 3, 'h', 'o', 'g', 4, 'h', 'o', 'g', 'g', 4, 'h', 'o', 'g', 's', 3, 'h', 'o', 'n', 4, 'h', 'o', 'n', 'j', 4, 'h', 'o', 'n', 'h', 3, 'h', 'o', 'd', 3, 'h', 'o', 'l', 4, 'h', 'o', 'l', 'g', 4, 'h', 'o', 'l', 'm', 4, 'h', 'o', 'l', 'b', 4, 'h', 'o', 'l', 's', 4, 'h', 'o', 'l', 't', 4, 'h', 'o', 'l', 'p', 4, 'h', 'o', 'l', 'h', 3, 'h', 'o', 'm', 3, 'h', 'o', 'b', 4, 'h', 'o', 'b', 's', 3, 'h', 'o', 's', 4, 'h', 'o', 's', 's', 4, 'h', 'o', 'n', 'g', 3, 'h', 'o', 'j', 3, 'h', 'o', 'c', 3, 'h', 'o', 'k', 3, 'h', 'o', 't', 3, 'h', 'o', 'p', 3, 'h', 'o', 'h', 3, 'h', 'w', 'a', 4, 'h', 'w', 'a', 'g', 5, 'h', 'w', 'a', 'g', 'g', 5, 'h', 'w', 'a', 'g', 's', 4, 'h', 'w', 'a', 'n', 5, 'h', 'w', 'a', 'n', 'j', 5, 'h', 'w', 'a', 'n', 'h', 4, 'h', 'w', 'a', 'd', 4, 'h', 'w', 'a', 'l', 5, 'h', 'w', 'a', 'l', 'g', 5, 'h', 'w', 'a', 'l', 'm', 5, 'h', 'w', 'a', 'l', 'b', 5, 'h', 'w', 'a', 'l', 's', 5, 'h', 'w', 'a', 'l', 't', 5, 'h', 'w', 'a', 'l', 'p', 5, 'h', 'w', 'a', 'l', 'h', 4, 'h', 'w', 'a', 'm', 4, 'h', 'w', 'a', 'b', 5, 'h', 'w', 'a', 'b', 's', 4, 'h', 'w', 'a', 's', 5, 'h', 'w', 'a', 's', 's', 5, 'h', 'w', 'a', 'n', 'g', 4, 'h', 'w', 'a', 'j', 4, 'h', 'w', 'a', 'c', 4, 'h', 'w', 'a', 'k', 4, 'h', 'w', 'a', 't', 4, 'h', 'w', 'a', 'p', 4, 'h', 'w', 'a', 'h', 4, 'h', 'w', 'a', 'e', 5, 'h', 'w', 'a', 'e', 'g', 6, 'h', 'w', 'a', 'e', 'g', 'g', 6, 'h', 'w', 'a', 'e', 'g', 's', 5, 'h', 'w', 'a', 'e', 'n', 6, 'h', 'w', 'a', 'e', 'n', 'j', 6, 'h', 'w', 'a', 'e', 'n', 'h', 5, 'h', 'w', 'a', 'e', 'd', 5, 'h', 'w', 'a', 'e', 'l', 6, 'h', 'w', 'a', 'e', 'l', 'g', 6, 'h', 'w', 'a', 'e', 'l', 'm', 6, 'h', 'w', 'a', 'e', 'l', 'b', 6, 'h', 'w', 'a', 'e', 'l', 's', 6, 'h', 'w', 'a', 'e', 'l', 't', 6, 'h', 'w', 'a', 'e', 'l', 'p', 6, 'h', 'w', 'a', 'e', 'l', 'h', 5, 'h', 'w', 'a', 'e', 'm', 5, 'h', 'w', 'a', 'e', 'b', 6, 'h', 'w', 'a', 'e', 'b', 's', 5, 'h', 'w', 'a', 'e', 's', 6, 'h', 'w', 'a', 'e', 's', 's', 6, 'h', 'w', 'a', 'e', 'n', 'g', 5, 'h', 'w', 'a', 'e', 'j', 5, 'h', 'w', 'a', 'e', 'c', 5, 'h', 'w', 'a', 'e', 'k', 5, 'h', 'w', 'a', 'e', 't', 5, 'h', 'w', 'a', 'e', 'p', 5, 'h', 'w', 'a', 'e', 'h', 3, 'h', 'o', 'e', 4, 'h', 'o', 'e', 'g', 5, 'h', 'o', 'e', 'g', 'g', 5, 'h', 'o', 'e', 'g', 's', 4, 'h', 'o', 'e', 'n', 5, 'h', 'o', 'e', 'n', 'j', 5, 'h', 'o', 'e', 'n', 'h', 4, 'h', 'o', 'e', 'd', 4, 'h', 'o', 'e', 'l', 5, 'h', 'o', 'e', 'l', 'g', 5, 'h', 'o', 'e', 'l', 'm', 5, 'h', 'o', 'e', 'l', 'b', 5, 'h', 'o', 'e', 'l', 's', 5, 'h', 'o', 'e', 'l', 't', 5, 'h', 'o', 'e', 'l', 'p', 5, 'h', 'o', 'e', 'l', 'h', 4, 'h', 'o', 'e', 'm', 4, 'h', 'o', 'e', 'b', 5, 'h', 'o', 'e', 'b', 's', 4, 'h', 'o', 'e', 's', 5, 'h', 'o', 'e', 's', 's', 5, 'h', 'o', 'e', 'n', 'g', 4, 'h', 'o', 'e', 'j', 4, 'h', 'o', 'e', 'c', 4, 'h', 'o', 'e', 'k', 4, 'h', 'o', 'e', 't', 4, 'h', 'o', 'e', 'p', 4, 'h', 'o', 'e', 'h', 3, 'h', 'y', 'o', 4, 'h', 'y', 'o', 'g', 5, 'h', 'y', 'o', 'g', 'g', 5, 'h', 'y', 'o', 'g', 's', 4, 'h', 'y', 'o', 'n', 5, 'h', 'y', 'o', 'n', 'j', 5, 'h', 'y', 'o', 'n', 'h', 4, 'h', 'y', 'o', 'd', 4, 'h', 'y', 'o', 'l', 5, 'h', 'y', 'o', 'l', 'g', 5, 'h', 'y', 'o', 'l', 'm', 5, 'h', 'y', 'o', 'l', 'b', 5, 'h', 'y', 'o', 'l', 's', 5, 'h', 'y', 'o', 'l', 't', 5, 'h', 'y', 'o', 'l', 'p', 5, 'h', 'y', 'o', 'l', 'h', 4, 'h', 'y', 'o', 'm', 4, 'h', 'y', 'o', 'b', 5, 'h', 'y', 'o', 'b', 's', 4, 'h', 'y', 'o', 's', 5, 'h', 'y', 'o', 's', 's', 5, 'h', 'y', 'o', 'n', 'g', 4, 'h', 'y', 'o', 'j', 4, 'h', 'y', 'o', 'c', 4, 'h', 'y', 'o', 'k', 4, 'h', 'y', 'o', 't', 4, 'h', 'y', 'o', 'p', 4, 'h', 'y', 'o', 'h', 2, 'h', 'u', 3, 'h', 'u', 'g', 4, 'h', 'u', 'g', 'g', 4, 'h', 'u', 'g', 's', 3, 'h', 'u', 'n', 4, 'h', 'u', 'n', 'j', 4, 'h', 'u', 'n', 'h', 3, 'h', 'u', 'd', 3, 'h', 'u', 'l', 4, 'h', 'u', 'l', 'g', 4, 'h', 'u', 'l', 'm', 4, 'h', 'u', 'l', 'b', 4, 'h', 'u', 'l', 's', 4, 'h', 'u', 'l', 't', 4, 'h', 'u', 'l', 'p', 4, 'h', 'u', 'l', 'h', 3, 'h', 'u', 'm', 3, 'h', 'u', 'b', 4, 'h', 'u', 'b', 's', 3, 'h', 'u', 's', 4, 'h', 'u', 's', 's', 4, 'h', 'u', 'n', 'g', 3, 'h', 'u', 'j', 3, 'h', 'u', 'c', 3, 'h', 'u', 'k', 3, 'h', 'u', 't', 3, 'h', 'u', 'p', 3, 'h', 'u', 'h', 4, 'h', 'w', 'e', 'o', 5, 'h', 'w', 'e', 'o', 'g', 6, 'h', 'w', 'e', 'o', 'g', 'g', 6, 'h', 'w', 'e', 'o', 'g', 's', 5, 'h', 'w', 'e', 'o', 'n', 6, 'h', 'w', 'e', 'o', 'n', 'j', 6, 'h', 'w', 'e', 'o', 'n', 'h', 5, 'h', 'w', 'e', 'o', 'd', 5, 'h', 'w', 'e', 'o', 'l', 6, 'h', 'w', 'e', 'o', 'l', 'g', 6, 'h', 'w', 'e', 'o', 'l', 'm', 6, 'h', 'w', 'e', 'o', 'l', 'b', 6, 'h', 'w', 'e', 'o', 'l', 's', 6, 'h', 'w', 'e', 'o', 'l', 't', 6, 'h', 'w', 'e', 'o', 'l', 'p', 6, 'h', 'w', 'e', 'o', 'l', 'h', 5, 'h', 'w', 'e', 'o', 'm', 5, 'h', 'w', 'e', 'o', 'b', 6, 'h', 'w', 'e', 'o', 'b', 's', 5, 'h', 'w', 'e', 'o', 's', 6, 'h', 'w', 'e', 'o', 's', 's', 6, 'h', 'w', 'e', 'o', 'n', 'g', 5, 'h', 'w', 'e', 'o', 'j', 5, 'h', 'w', 'e', 'o', 'c', 5, 'h', 'w', 'e', 'o', 'k', 5, 'h', 'w', 'e', 'o', 't', 5, 'h', 'w', 'e', 'o', 'p', 5, 'h', 'w', 'e', 'o', 'h', 3, 'h', 'w', 'e', 4, 'h', 'w', 'e', 'g', 5, 'h', 'w', 'e', 'g', 'g', 5, 'h', 'w', 'e', 'g', 's', 4, 'h', 'w', 'e', 'n', 5, 'h', 'w', 'e', 'n', 'j', 5, 'h', 'w', 'e', 'n', 'h', 4, 'h', 'w', 'e', 'd', 4, 'h', 'w', 'e', 'l', 5, 'h', 'w', 'e', 'l', 'g', 5, 'h', 'w', 'e', 'l', 'm', 5, 'h', 'w', 'e', 'l', 'b', 5, 'h', 'w', 'e', 'l', 's', 5, 'h', 'w', 'e', 'l', 't', 5, 'h', 'w', 'e', 'l', 'p', 5, 'h', 'w', 'e', 'l', 'h', 4, 'h', 'w', 'e', 'm', 4, 'h', 'w', 'e', 'b', 5, 'h', 'w', 'e', 'b', 's', 4, 'h', 'w', 'e', 's', 5, 'h', 'w', 'e', 's', 's', 5, 'h', 'w', 'e', 'n', 'g', 4, 'h', 'w', 'e', 'j', 4, 'h', 'w', 'e', 'c', 4, 'h', 'w', 'e', 'k', 4, 'h', 'w', 'e', 't', 4, 'h', 'w', 'e', 'p', 4, 'h', 'w', 'e', 'h', 3, 'h', 'w', 'i', 4, 'h', 'w', 'i', 'g', 5, 'h', 'w', 'i', 'g', 'g', 5, 'h', 'w', 'i', 'g', 's', 4, 'h', 'w', 'i', 'n', 5, 'h', 'w', 'i', 'n', 'j', 5, 'h', 'w', 'i', 'n', 'h', 4, 'h', 'w', 'i', 'd', 4, 'h', 'w', 'i', 'l', 5, 'h', 'w', 'i', 'l', 'g', 5, 'h', 'w', 'i', 'l', 'm', 5, 'h', 'w', 'i', 'l', 'b', 5, 'h', 'w', 'i', 'l', 's', 5, 'h', 'w', 'i', 'l', 't', 5, 'h', 'w', 'i', 'l', 'p', 5, 'h', 'w', 'i', 'l', 'h', 4, 'h', 'w', 'i', 'm', 4, 'h', 'w', 'i', 'b', 5, 'h', 'w', 'i', 'b', 's', 4, 'h', 'w', 'i', 's', 5, 'h', 'w', 'i', 's', 's', 5, 'h', 'w', 'i', 'n', 'g', 4, 'h', 'w', 'i', 'j', 4, 'h', 'w', 'i', 'c', 4, 'h', 'w', 'i', 'k', 4, 'h', 'w', 'i', 't', 4, 'h', 'w', 'i', 'p', 4, 'h', 'w', 'i', 'h', 3, 'h', 'y', 'u', 4, 'h', 'y', 'u', 'g', 5, 'h', 'y', 'u', 'g', 'g', 5, 'h', 'y', 'u', 'g', 's', 4, 'h', 'y', 'u', 'n', 5, 'h', 'y', 'u', 'n', 'j', 5, 'h', 'y', 'u', 'n', 'h', 4, 'h', 'y', 'u', 'd', 4, 'h', 'y', 'u', 'l', 5, 'h', 'y', 'u', 'l', 'g', 5, 'h', 'y', 'u', 'l', 'm', 5, 'h', 'y', 'u', 'l', 'b', 5, 'h', 'y', 'u', 'l', 's', 5, 'h', 'y', 'u', 'l', 't', 5, 'h', 'y', 'u', 'l', 'p', 5, 'h', 'y', 'u', 'l', 'h', 4, 'h', 'y', 'u', 'm', 4, 'h', 'y', 'u', 'b', 5, 'h', 'y', 'u', 'b', 's', 4, 'h', 'y', 'u', 's', 5, 'h', 'y', 'u', 's', 's', 5, 'h', 'y', 'u', 'n', 'g', 4, 'h', 'y', 'u', 'j', 4, 'h', 'y', 'u', 'c', 4, 'h', 'y', 'u', 'k', 4, 'h', 'y', 'u', 't', 4, 'h', 'y', 'u', 'p', 4, 'h', 'y', 'u', 'h', 3, 'h', 'e', 'u', 4, 'h', 'e', 'u', 'g', 5, 'h', 'e', 'u', 'g', 'g', 5, 'h', 'e', 'u', 'g', 's', 4, 'h', 'e', 'u', 'n', 5, 'h', 'e', 'u', 'n', 'j', 5, 'h', 'e', 'u', 'n', 'h', 4, 'h', 'e', 'u', 'd', 4, 'h', 'e', 'u', 'l', 5, 'h', 'e', 'u', 'l', 'g', 5, 'h', 'e', 'u', 'l', 'm', 5, 'h', 'e', 'u', 'l', 'b', 5, 'h', 'e', 'u', 'l', 's', 5, 'h', 'e', 'u', 'l', 't', 5, 'h', 'e', 'u', 'l', 'p', 5, 'h', 'e', 'u', 'l', 'h', 4, 'h', 'e', 'u', 'm', 4, 'h', 'e', 'u', 'b', 5, 'h', 'e', 'u', 'b', 's', 4, 'h', 'e', 'u', 's', 5, 'h', 'e', 'u', 's', 's', 5, 'h', 'e', 'u', 'n', 'g', 4, 'h', 'e', 'u', 'j', 4, 'h', 'e', 'u', 'c', 4, 'h', 'e', 'u', 'k', 4, 'h', 'e', 'u', 't', 4, 'h', 'e', 'u', 'p', 4, 'h', 'e', 'u', 'h', 3, 'h', 'y', 'i', 4, 'h', 'y', 'i', 'g', 5, 'h', 'y', 'i', 'g', 'g', 5, 'h', 'y', 'i', 'g', 's', 4, 'h', 'y', 'i', 'n', 5, 'h', 'y', 'i', 'n', 'j', 5, 'h', 'y', 'i', 'n', 'h', 4, 'h', 'y', 'i', 'd', 4, 'h', 'y', 'i', 'l', 5, 'h', 'y', 'i', 'l', 'g', 5, 'h', 'y', 'i', 'l', 'm', 5, 'h', 'y', 'i', 'l', 'b', 5, 'h', 'y', 'i', 'l', 's', 5, 'h', 'y', 'i', 'l', 't', 5, 'h', 'y', 'i', 'l', 'p', 5, 'h', 'y', 'i', 'l', 'h', 4, 'h', 'y', 'i', 'm', 4, 'h', 'y', 'i', 'b', 5, 'h', 'y', 'i', 'b', 's', 4, 'h', 'y', 'i', 's', 5, 'h', 'y', 'i', 's', 's', 5, 'h', 'y', 'i', 'n', 'g', 4, 'h', 'y', 'i', 'j', 4, 'h', 'y', 'i', 'c', 4, 'h', 'y', 'i', 'k', 4, 'h', 'y', 'i', 't', 4, 'h', 'y', 'i', 'p', 4, 'h', 'y', 'i', 'h', 2, 'h', 'i', 3, 'h', 'i', 'g', 4, 'h', 'i', 'g', 'g', 4, 'h', 'i', 'g', 's', 3, 'h', 'i', 'n', 4, 'h', 'i', 'n', 'j', 4, 'h', 'i', 'n', 'h', 3, 'h', 'i', 'd', 3, 'h', 'i', 'l', 4, 'h', 'i', 'l', 'g', 4, 'h', 'i', 'l', 'm', 4, 'h', 'i', 'l', 'b', 4, 'h', 'i', 'l', 's', 4, 'h', 'i', 'l', 't', 4, 'h', 'i', 'l', 'p', 4, 'h', 'i', 'l', 'h', 3, 'h', 'i', 'm', 3, 'h', 'i', 'b', 4, 'h', 'i', 'b', 's', 3, 'h', 'i', 's', 4, 'h', 'i', 's', 's', 4, 'h', 'i', 'n', 'g', 3, 'h', 'i', 'j', 3, 'h', 'i', 'c', 3, 'h', 'i', 'k', 3, 'h', 'i', 't', 3, 'h', 'i', 'p', 3, 'h', 'i', 'h', 4, 'K', 'a', 'y', ' ', 6, 'K', 'a', 'y', 'n', 'g', ' ', 3, 'K', 'e', ' ', 3, 'K', 'o', ' ', 4, 'K', 'o', 'l', ' ', 4, 'K', 'o', 'c', ' ', 4, 'K', 'w', 'i', ' ', 4, 'K', 'w', 'i', ' ', 5, 'K', 'y', 'u', 'n', ' ', 4, 'K', 'u', 'l', ' ', 4, 'K', 'u', 'm', ' ', 3, 'N', 'a', ' ', 3, 'N', 'a', ' ', 3, 'N', 'a', ' ', 3, 'L', 'a', ' ', 3, 'N', 'a', ' ', 3, 'N', 'a', ' ', 3, 'N', 'a', ' ', 3, 'N', 'a', ' ', 3, 'N', 'a', ' ', 4, 'N', 'a', 'k', ' ', 4, 'N', 'a', 'k', ' ', 4, 'N', 'a', 'k', ' ', 4, 'N', 'a', 'k', ' ', 4, 'N', 'a', 'k', ' ', 4, 'N', 'a', 'k', ' ', 4, 'N', 'a', 'k', ' ', 4, 'N', 'a', 'n', ' ', 4, 'N', 'a', 'n', ' ', 4, 'N', 'a', 'n', ' ', 4, 'N', 'a', 'n', ' ', 4, 'N', 'a', 'n', ' ', 4, 'N', 'a', 'n', ' ', 4, 'N', 'a', 'm', ' ', 4, 'N', 'a', 'm', ' ', 4, 'N', 'a', 'm', ' ', 4, 'N', 'a', 'm', ' ', 4, 'N', 'a', 'p', ' ', 4, 'N', 'a', 'p', ' ', 4, 'N', 'a', 'p', ' ', 5, 'N', 'a', 'n', 'g', ' ', 5, 'N', 'a', 'n', 'g', ' ', 5, 'N', 'a', 'n', 'g', ' ', 5, 'N', 'a', 'n', 'g', ' ', 5, 'N', 'a', 'n', 'g', ' ', 4, 'N', 'a', 'y', ' ', 6, 'N', 'a', 'y', 'n', 'g', ' ', 3, 'N', 'o', ' ', 3, 'N', 'o', ' ', 3, 'N', 'o', ' ', 3, 'N', 'o', ' ', 3, 'N', 'o', ' ', 3, 'N', 'o', ' ', 3, 'N', 'o', ' ', 3, 'N', 'o', ' ', 3, 'N', 'o', ' ', 3, 'N', 'o', ' ', 3, 'N', 'o', ' ', 3, 'N', 'o', ' ', 4, 'N', 'o', 'k', ' ', 4, 'N', 'o', 'k', ' ', 4, 'N', 'o', 'k', ' ', 4, 'N', 'o', 'k', ' ', 4, 'N', 'o', 'k', ' ', 4, 'N', 'o', 'k', ' ', 4, 'N', 'o', 'n', ' ', 5, 'N', 'o', 'n', 'g', ' ', 5, 'N', 'o', 'n', 'g', ' ', 5, 'N', 'o', 'n', 'g', ' ', 5, 'N', 'o', 'n', 'g', ' ', 4, 'N', 'o', 'y', ' ', 4, 'N', 'o', 'y', ' ', 4, 'N', 'o', 'y', ' ', 4, 'N', 'o', 'y', ' ', 4, 'N', 'w', 'u', ' ', 4, 'N', 'w', 'u', ' ', 4, 'N', 'w', 'u', ' ', 4, 'N', 'w', 'u', ' ', 4, 'N', 'w', 'u', ' ', 4, 'N', 'w', 'u', ' ', 4, 'N', 'w', 'u', ' ', 4, 'N', 'w', 'u', ' ', 4, 'N', 'u', 'k', ' ', 4, 'N', 'u', 'k', ' ', 4, 'N', 'u', 'm', ' ', 5, 'N', 'u', 'n', 'g', ' ', 5, 'N', 'u', 'n', 'g', ' ', 5, 'N', 'u', 'n', 'g', ' ', 5, 'N', 'u', 'n', 'g', ' ', 5, 'N', 'u', 'n', 'g', ' ', 4, 'T', 'w', 'u', ' ', 3, 'L', 'a', ' ', 4, 'L', 'a', 'k', ' ', 4, 'L', 'a', 'k', ' ', 4, 'L', 'a', 'n', ' ', 6, 'L', 'y', 'e', 'n', 'g', ' ', 3, 'L', 'o', ' ', 5, 'L', 'y', 'u', 'l', ' ', 3, 'L', 'i', ' ', 4, 'P', 'e', 'y', ' ', 4, 'P', 'e', 'n', ' ', 5, 'P', 'y', 'e', 'n', ' ', 4, 'P', 'w', 'u', ' ', 5, 'P', 'w', 'u', 'l', ' ', 3, 'P', 'i', ' ', 4, 'S', 'a', 'k', ' ', 4, 'S', 'a', 'k', ' ', 4, 'S', 'a', 'm', ' ', 5, 'S', 'a', 'y', 'k', ' ', 6, 'S', 'a', 'y', 'n', 'g', ' ', 4, 'S', 'e', 'p', ' ', 4, 'S', 'e', 'y', ' ', 5, 'S', 'w', 'a', 'y', ' ', 4, 'S', 'i', 'n', ' ', 4, 'S', 'i', 'm', ' ', 4, 'S', 'i', 'p', ' ', 3, 'Y', 'a', ' ', 4, 'Y', 'a', 'k', ' ', 4, 'Y', 'a', 'k', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 5, 'Y', 'a', 'n', 'g', ' ', 3, 'Y', 'e', ' ', 3, 'Y', 'e', ' ', 3, 'Y', 'e', ' ', 3, 'Y', 'e', ' ', 3, 'Y', 'e', ' ', 3, 'Y', 'e', ' ', 3, 'Y', 'e', ' ', 3, 'Y', 'e', ' ', 3, 'Y', 'e', ' ', 3, 'Y', 'e', ' ', 3, 'Y', 'e', ' ', 4, 'Y', 'e', 'k', ' ', 4, 'Y', 'e', 'k', ' ', 4, 'Y', 'e', 'k', ' ', 4, 'Y', 'e', 'k', ' ', 4, 'Y', 'e', 'n', ' ', 4, 'Y', 'e', 'n', ' ', 4, 'Y', 'e', 'n', ' ', 4, 'Y', 'e', 'n', ' ', 4, 'Y', 'e', 'n', ' ', 4, 'Y', 'e', 'n', ' ', 4, 'Y', 'e', 'n', ' ', 4, 'Y', 'e', 'n', ' ', 4, 'Y', 'e', 'n', ' ', 4, 'Y', 'e', 'n', ' ', 4, 'Y', 'e', 'n', ' ', 4, 'Y', 'e', 'n', ' ', 4, 'Y', 'e', 'n', ' ', 4, 'Y', 'e', 'n', ' ', 4, 'Y', 'e', 'l', ' ', 4, 'Y', 'e', 'l', ' ', 4, 'Y', 'e', 'l', ' ', 4, 'Y', 'e', 'l', ' ', 4, 'Y', 'e', 'l', ' ', 4, 'Y', 'e', 'l', ' ', 4, 'Y', 'e', 'm', ' ', 4, 'Y', 'e', 'm', ' ', 4, 'Y', 'e', 'm', ' ', 4, 'Y', 'e', 'm', ' ', 4, 'Y', 'e', 'm', ' ', 4, 'Y', 'e', 'p', ' ', 5, 'Y', 'e', 'n', 'g', ' ', 5, 'Y', 'e', 'n', 'g', ' ', 5, 'Y', 'e', 'n', 'g', ' ', 5, 'Y', 'e', 'n', 'g', ' ', 5, 'Y', 'e', 'n', 'g', ' ', 5, 'Y', 'e', 'n', 'g', ' ', 5, 'Y', 'e', 'n', 'g', ' ', 5, 'Y', 'e', 'n', 'g', ' ', 5, 'Y', 'e', 'n', 'g', ' ', 5, 'Y', 'e', 'n', 'g', ' ', 5, 'Y', 'e', 'n', 'g', ' ', 5, 'Y', 'e', 'n', 'g', ' ', 5, 'Y', 'e', 'n', 'g', ' ', 4, 'Y', 'e', 'y', ' ', 4, 'Y', 'e', 'y', ' ', 4, 'Y', 'e', 'y', ' ', 4, 'Y', 'e', 'y', ' ', 2, 'O', ' ', 3, 'Y', 'o', ' ', 3, 'Y', 'o', ' ', 3, 'Y', 'o', ' ', 3, 'Y', 'o', ' ', 3, 'Y', 'o', ' ', 3, 'Y', 'o', ' ', 3, 'Y', 'o', ' ', 3, 'Y', 'o', ' ', 3, 'Y', 'o', ' ', 3, 'Y', 'o', ' ', 5, 'Y', 'o', 'n', 'g', ' ', 4, 'W', 'u', 'n', ' ', 4, 'W', 'e', 'n', ' ', 3, 'Y', 'u', ' ', 3, 'Y', 'u', ' ', 3, 'Y', 'u', ' ', 3, 'Y', 'u', ' ', 3, 'Y', 'u', ' ', 3, 'Y', 'u', ' ', 3, 'Y', 'u', ' ', 3, 'Y', 'u', ' ', 3, 'Y', 'u', ' ', 3, 'Y', 'u', ' ', 4, 'Y', 'u', 'k', ' ', 4, 'Y', 'u', 'k', ' ', 4, 'Y', 'u', 'k', ' ', 4, 'Y', 'u', 'n', ' ', 4, 'Y', 'u', 'n', ' ', 4, 'Y', 'u', 'n', ' ', 4, 'Y', 'u', 'n', ' ', 4, 'Y', 'u', 'l', ' ', 4, 'Y', 'u', 'l', ' ', 4, 'Y', 'u', 'l', ' ', 4, 'Y', 'u', 'l', ' ', 5, 'Y', 'u', 'n', 'g', ' ', 2, 'I', ' ', 2, 'I', ' ', 2, 'I', ' ', 2, 'I', ' ', 2, 'I', ' ', 2, 'I', ' ', 2, 'I', ' ', 2, 'I', ' ', 2, 'I', ' ', 2, 'I', ' ', 2, 'I', ' ', 2, 'I', ' ', 2, 'I', ' ', 2, 'I', ' ', 3, 'I', 'k', ' ', 3, 'I', 'k', ' ', 3, 'I', 'n', ' ', 3, 'I', 'n', ' ', 3, 'I', 'n', ' ', 3, 'I', 'n', ' ', 3, 'I', 'n', ' ', 3, 'I', 'n', ' ', 3, 'I', 'n', ' ', 3, 'I', 'm', ' ', 3, 'I', 'm', ' ', 3, 'I', 'm', ' ', 3, 'I', 'p', ' ', 3, 'I', 'p', ' ', 3, 'I', 'p', ' ', 5, 'C', 'a', 'n', 'g', ' ', 4, 'C', 'e', 'k', ' ', 3, 'C', 'i', ' ', 4, 'C', 'i', 'p', ' ', 4, 'C', 'h', 'a', ' ', 5, 'C', 'h', 'e', 'k', ' ', 5, 'C', 'h', 'e', 'y', ' ', 5, 'T', 'h', 'a', 'k', ' ', 5, 'T', 'h', 'a', 'k', ' ', 6, 'T', 'h', 'a', 'n', 'g', ' ', 6, 'T', 'h', 'a', 'y', 'k', ' ', 6, 'T', 'h', 'o', 'n', 'g', ' ', 4, 'P', 'h', 'o', ' ', 5, 'P', 'h', 'o', 'k', ' ', 5, 'H', 'a', 'n', 'g', ' ', 5, 'H', 'a', 'n', 'g', ' ', 5, 'H', 'y', 'e', 'n', ' ', 5, 'H', 'w', 'a', 'k', ' ', 3, 'W', 'u', ' ', 4, 'H', 'u', 'o', ' ', 6, 'Z', 'h', 'o', 'n', 'g', ' ', 5, 'Q', 'i', 'n', 'g', ' ', 3, 'X', 'i', ' ', 4, 'Z', 'h', 'u', ' ', 3, 'Y', 'i', ' ', 3, 'L', 'i', ' ', 5, 'S', 'h', 'e', 'n', ' ', 6, 'X', 'i', 'a', 'n', 'g', ' ', 3, 'F', 'u', ' ', 5, 'J', 'i', 'n', 'g', ' ', 5, 'J', 'i', 'n', 'g', ' ', 3, 'Y', 'u', ' ', 5, 'H', 'a', 'g', 'i', ' ', 4, 'Z', 'h', 'u', ' ', 3, 'Y', 'i', ' ', 3, 'D', 'u', ' ', 4, 'F', 'a', 'n', ' ', 3, 'S', 'i', ' ', 5, 'G', 'u', 'a', 'n', ' ', 2, 'f', 'f', 2, 'f', 'i', 2, 'f', 'l', 3, 'f', 'f', 'i', 3, 'f', 'f', 'l', 2, 's', 't', 2, 's', 't', 2, 'm', 'n', 2, 'm', 'e', 2, 'm', 'i', 2, 'v', 'n', 3, 'm', 'k', 'h', 1, 'i', 2, 'A', 'Y', 1, '`', 1, 'A', 1, 'd', 1, 'h', 2, 'K', 'H', 1, 'l', 1, 'm', 1, 'r', 1, 't', 1, '+', 2, 'S', 'H', 1, 'S', 2, 'S', 'H', 1, 'S', 1, 'a', 1, 'a', 1, 'A', 1, 'b', 1, 'g', 1, 'd', 1, 'h', 1, 'v', 1, 'z', 1, 't', 1, 'y', 2, 'K', 'H', 2, 'K', 'H', 1, 'l', 1, 'm', 1, 'n', 1, 's', 1, 'p', 1, 'p', 2, 'T', 'S', 1, 'k', 1, 'r', 2, 'S', 'H', 1, 't', 1, 'o', 1, 'v', 2, 'K', 'H', 1, 'f', 2, 'E', 'L', 1, '~', 2, '.', '.', 2, '-', '-', 1, '-', 1, '_', 1, '_', 1, '(', 2, ')', ' ', 1, '{', 2, '}', ' ', 1, '[', 2, ']', ' ', 2, '[', '(', 3, ')', ']', ' ', 2, '<', '<', 3, '>', '>', ' ', 1, '<', 2, '>', ' ', 1, '[', 2, ']', ' ', 1, '{', 1, '}', 1, ',', 1, ',', 1, '.', 1, ';', 1, ':', 1, '?', 1, '!', 1, '-', 1, '(', 1, ')', 1, '{', 1, '}', 1, '{', 1, '}', 1, '#', 1, '&', 1, '*', 1, '+', 1, '-', 1, '<', 1, '>', 1, '=', 1, '\\', 1, '$', 1, '%', 1, '@', 1, '!', 1, '\"', 1, '#', 1, '$', 1, '%', 1, '&', 1, '\'', 1, '(', 1, ')', 1, '*', 1, '+', 1, ',', 1, '-', 1, '.', 1, '/', 1, '0', 1, '1', 1, '2', 1, '3', 1, '4', 1, '5', 1, '6', 1, '7', 1, '8', 1, '9', 1, ':', 1, ';', 1, '<', 1, '=', 1, '>', 1, '?', 1, '@', 1, 'A', 1, 'B', 1, 'C', 1, 'D', 1, 'E', 1, 'F', 1, 'G', 1, 'H', 1, 'I', 1, 'J', 1, 'K', 1, 'L', 1, 'M', 1, 'N', 1, 'O', 1, 'P', 1, 'Q', 1, 'R', 1, 'S', 1, 'T', 1, 'U', 1, 'V', 1, 'W', 1, 'X', 1, 'Y', 1, 'Z', 1, '[', 1, '\\', 1, ']', 1, '^', 1, '_', 1, '`', 1, 'a', 1, 'b', 1, 'c', 1, 'd', 1, 'e', 1, 'f', 1, 'g', 1, 'h', 1, 'i', 1, 'j', 1, 'k', 1, 'l', 1, 'm', 1, 'n', 1, 'o', 1, 'p', 1, 'q', 1, 'r', 1, 's', 1, 't', 1, 'u', 1, 'v', 1, 'w', 1, 'x', 1, 'y', 1, 'z', 1, '{', 1, '|', 1, '}', 1, '~', 1, '.', 1, '[', 1, ']', 1, ',', 1, '*', 2, 'w', 'o', 1, 'a', 1, 'i', 1, 'u', 1, 'e', 1, 'o', 2, 'y', 'a', 2, 'y', 'u', 2, 'y', 'o', 2, 't', 'u', 1, '+', 1, 'a', 1, 'i', 1, 'u', 1, 'e', 1, 'o', 2, 'k', 'a', 2, 'k', 'i', 2, 'k', 'u', 2, 'k', 'e', 2, 'k', 'o', 2, 's', 'a', 2, 's', 'i', 2, 's', 'u', 2, 's', 'e', 2, 's', 'o', 2, 't', 'a', 2, 't', 'i', 2, 't', 'u', 2, 't', 'e', 2, 't', 'o', 2, 'n', 'a', 2, 'n', 'i', 2, 'n', 'u', 2, 'n', 'e', 2, 'n', 'o', 2, 'h', 'a', 2, 'h', 'i', 2, 'h', 'u', 2, 'h', 'e', 2, 'h', 'o', 2, 'm', 'a', 2, 'm', 'i', 2, 'm', 'u', 2, 'm', 'e', 2, 'm', 'o', 2, 'y', 'a', 2, 'y', 'u', 2, 'y', 'o', 2, 'r', 'a', 2, 'r', 'i', 2, 'r', 'u', 2, 'r', 'e', 2, 'r', 'o', 2, 'w', 'a', 1, 'n', 1, ':', 1, ';', 1, 'g', 2, 'g', 'g', 2, 'g', 's', 1, 'n', 2, 'n', 'j', 2, 'n', 'h', 1, 'd', 2, 'd', 'd', 1, 'r', 2, 'l', 'g', 2, 'l', 'm', 2, 'l', 'b', 2, 'l', 's', 2, 'l', 't', 2, 'l', 'p', 2, 'r', 'h', 1, 'm', 1, 'b', 2, 'b', 'b', 2, 'b', 's', 1, 's', 2, 's', 's', 1, 'j', 2, 'j', 'j', 1, 'c', 1, 'k', 1, 't', 1, 'p', 1, 'h', 1, 'a', 2, 'a', 'e', 2, 'y', 'a', 3, 'y', 'a', 'e', 2, 'e', 'o', 1, 'e', 3, 'y', 'e', 'o', 2, 'y', 'e', 1, 'o', 2, 'w', 'a', 3, 'w', 'a', 'e', 2, 'o', 'e', 2, 'y', 'o', 1, 'u', 3, 'w', 'e', 'o', 2, 'w', 'e', 2, 'w', 'i', 2, 'y', 'u', 2, 'e', 'u', 2, 'y', 'i', 1, 'i', 2, '/', 'C', 2, 'P', 'S', 1, '!', 1, '-', 1, '|', 2, 'Y', '=', 2, 'W', '=', 1, '|', 1, '-', 1, '|', 1, '-', 1, '|', 1, '#', 1, 'O', 1, '{', 1, '|', 1, '}', }; const int Data::tranlitIndexMap[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 2, 4, 7, 10, 13, 16, 18, 21, 23, 27, 29, 32, -1, 34, 38, 40, 44, 47, 49, 51, 53, 55, 57, 59, 61, 63, 65, 68, 74, 80, 86, 88, 90, 92, 94, 96, 98, 100, 103, 105, 107, 109, 111, 113, 115, 117, 119, 121, 123, 125, 127, 129, 131, 133, 135, 137, 139, 141, 143, 145, 147, 149, 152, 155, 157, 159, 161, 163, 165, 167, 170, 172, 174, 176, 178, 180, 182, 184, 186, 188, 190, 192, 194, 196, 198, 200, 202, 204, 206, 208, 210, 212, 214, 216, 219, 221, 223, 225, 227, 229, 231, 233, 235, 237, 239, 241, 243, 245, 247, 249, 251, 253, 255, 257, 259, 261, 263, 265, 267, 269, 271, 273, 275, 277, 279, 281, 283, 285, 287, 289, 291, 293, 295, 297, 299, 301, 303, 305, 307, 309, 311, 313, 315, 317, 319, 321, 324, 327, 329, 331, 333, 335, 337, 339, 341, 343, 345, 347, 349, 351, 353, 355, 357, 359, 361, 363, 365, 367, 369, 372, 375, 378, 380, 382, 384, 386, 388, 390, 393, 396, 398, 400, 402, 404, 406, 408, 410, 412, 414, 416, 418, 420, 422, 424, 426, 428, 430, 432, 434, 436, 438, 440, 442, 444, 446, 448, 450, 452, 454, 456, 458, 460, 462, 464, 466, 468, 470, 472, 474, 476, 478, 480, 482, 484, 486, 488, 490, 492, 494, 496, 498, 500, 502, 504, 506, 508, 510, 512, 514, 516, 518, 520, 522, 524, 526, 529, 531, 533, 535, 537, 539, 541, 543, 545, 547, 549, 551, 553, 556, 559, 561, 563, 566, 568, 570, 573, 576, 578, 580, 582, 584, 586, 588, 590, 592, 594, 596, 598, 600, 603, 606, 609, 612, 614, 616, 618, 621, 623, 625, 628, 631, 633, 636, 639, 642, 645, 648, 651, 654, 657, 660, 662, 664, 666, 668, 670, 672, 674, 676, 678, 680, 682, 684, 686, 688, 690, 692, 694, 696, 698, 700, 702, 705, 708, 710, 712, 714, 716, 718, 720, 722, 724, 726, 728, 731, 734, 736, 739, 742, 745, 747, 749, 752, 754, 756, 758, 760, 762, 765, 768, 770, 772, 774, 776, 778, 780, 782, 784, 786, 788, 790, 792, 794, 796, 798, 800, 802, 804, 806, 808, 810, 812, 814, 816, 818, 820, 822, 824, 826, 828, 830, 832, 834, 836, 838, 840, 843, 846, 848, 850, 852, 854, 856, 858, 860, 862, 864, 866, 868, 870, 872, 874, 876, 878, 880, 882, 884, 886, 889, 892, 894, 896, 898, 900, 902, 904, -1, -1, 906, 908, 910, 912, 914, 916, 918, 920, 922, 924, 926, 928, 930, 932, 934, 936, 938, 940, 942, 944, 946, 948, 950, 952, 954, 956, 958, 960, 962, 964, 966, 968, 970, 972, 974, 976, 978, 980, 982, 984, 986, 988, 990, 992, 995, 997, 999, 1001, 1003, 1005, 1007, 1009, 1012, 1014, 1016, 1018, 1020, 1022, 1024, 1026, 1028, 1030, 1032, 1034, 1036, 1038, 1040, 1042, 1044, 1046, 1048, 1050, 1052, 1054, 1056, 1058, 1060, 1062, 1064, 1066, 1068, 1070, 1072, 1074, 1076, 1078, 1080, 1082, 1084, 1086, 1088, 1090, 1092, 1094, 1096, 1098, 1100, 1103, 1106, 1109, 1112, 1115, 1118, 1121, 1124, 1127, 1130, 1133, 1135, 1137, 1139, 1141, 1143, 1145, 1147, 1149, 1151, 1153, 1155, 1157, 1159, 1161, 1163, 1165, 1167, 1169, 1171, 1173, 1175, 1177, 1179, 1181, 1183, 1185, 1187, 1189, 1191, 1193, 1195, 1197, 1199, 1201, 1203, 1205, 1207, 1209, 1211, 1213, 1215, 1217, 1219, 1221, 1223, 1225, 1227, 1229, 1231, 1233, 1235, 1237, 1239, 1241, -1, -1, -1, -1, -1, -1, -1, 1243, 1245, 1247, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1249, 1251, 1253, 1255, 1257, 1259, 1261, 1263, 1265, 1267, 1269, 1271, 1273, -1, -1, -1, -1, 1275, 1277, -1, -1, -1, -1, -1, -1, -1, -1, 1279, -1, -1, -1, -1, -1, -1, -1, 1281, 1283, 1285, 1287, 1289, -1, 1291, -1, 1293, 1295, 1297, 1299, 1301, 1303, 1305, 1307, 1309, 1311, 1313, 1316, 1318, 1320, 1322, 1324, 1326, 1329, 1331, 1333, -1, 1335, 1337, 1339, 1341, 1344, 1347, 1350, 1352, 1354, 1356, 1358, 1360, 1362, 1364, 1366, 1368, 1370, 1372, 1374, 1376, 1378, 1380, 1383, 1385, 1387, 1389, 1391, 1393, 1395, 1397, 1399, 1401, 1403, 1405, 1407, 1409, 1412, 1415, 1418, 1420, 1422, 1424, 1426, 1428, -1, 1430, 1432, 1435, 1437, 1439, 1441, 1444, 1446, -1, -1, 1448, 1451, 1454, 1456, 1458, 1460, 1462, 1465, 1468, 1471, 1474, 1476, 1478, 1481, 1484, 1486, 1488, 1490, 1492, 1495, 1498, 1501, 1504, 1506, 1508, 1510, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1512, 1515, 1518, 1521, 1524, 1527, 1530, 1532, 1535, 1537, 1540, 1543, 1547, 1550, 1552, 1554, 1558, 1560, 1562, 1564, 1566, 1568, 1570, 1573, 1575, 1577, 1579, 1581, 1583, 1585, 1587, 1589, 1591, 1593, 1595, 1597, 1599, 1601, 1604, 1607, 1610, 1613, 1618, 1620, 1622, 1624, 1626, 1629, 1632, 1634, 1636, 1638, 1640, 1642, 1644, 1647, 1649, 1651, 1653, 1655, 1657, 1659, 1661, 1663, 1665, 1667, 1669, 1671, 1673, 1675, 1678, 1681, 1684, 1687, 1692, 1694, 1696, 1698, 1700, 1703, 1706, 1709, 1712, 1715, 1718, 1721, 1724, 1726, 1729, 1731, 1734, 1737, 1741, 1744, 1746, 1748, 1752, 1754, 1756, 1758, 1760, 1763, 1766, 1768, 1770, 1773, 1776, 1778, 1780, 1783, 1786, 1789, 1792, 1795, 1798, 1800, 1802, 1804, 1806, 1808, 1810, 1812, 1814, 1816, 1818, 1820, 1822, 1825, 1828, 1830, 1832, -1, -1, -1, -1, -1, 1839, 1849, -1, -1, 1861, 1863, 1865, 1868, 1871, 1874, 1877, 1880, 1883, 1886, 1889, 1893, 1897, 1900, 1903, 1906, 1909, 1912, 1915, 1918, 1921, 1924, 1927, 1930, 1933, 1936, 1939, 1942, 1945, 1948, 1951, 1954, 1957, 1960, 1963, 1965, 1967, 1970, 1973, 1977, 1981, 1985, 1989, 1993, 1997, 2001, 2005, 2007, 2009, 2012, 2015, 2019, 2023, 2025, 2028, 2031, 2034, -1, -1, 2037, 2040, -1, -1, 2043, 2046, -1, -1, -1, 2049, 2051, 2053, 2055, 2057, 2060, 2063, 2066, 2069, 2071, 2073, 2075, 2077, 2080, 2083, 2085, 2087, 2090, 2093, 2095, 2097, 2099, 2101, 2103, 2105, 2107, 2109, 2111, 2113, 2115, 2117, 2119, 2121, 2123, 2125, 2127, 2129, 2132, -1, -1, 2135, 2137, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2139, 2141, 2143, 2145, 2147, 2149, 2151, 2153, 2155, 2158, 2161, 2163, 2165, 2168, 2171, 2173, 2175, 2178, 2181, 2184, 2186, 2188, 2190, 2193, 2195, 2199, 2201, 2203, 2206, 2208, 2210, 2212, 2214, 2218, 2220, 2223, 2226, 2228, -1, -1, 2230, 2232, 2234, 2236, 2238, 2240, 2242, -1, 2244, 2246, 2248, 2250, 2252, 2254, 2256, 2258, 2260, 2263, 2266, 2268, 2270, 2273, 2276, 2278, 2280, 2283, 2286, 2289, 2291, 2293, 2295, 2298, 2300, 2304, 2306, 2308, 2311, 2313, 2315, 2317, 2319, 2323, 2325, 2328, 2331, 2333, 2335, -1, 2338, 2340, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2342, 2344, 2346, 2348, 2350, 2352, 2354, 2356, 2358, 2360, 2362, -1, -1, 2364, -1, 2366, -1, -1, 2368, -1, -1, 2370, 2372, -1, -1, -1, -1, -1, -1, -1, -1, 2374, 2376, 2378, 2380, 2382, 2384, 2386, 2388, 2390, 2392, 2394, 2397, 2400, 2402, 2404, 2406, 2408, 2410, 2412, 2414, 2416, 2418, 2421, 2424, 2426, 2428, 2431, -1, -1, -1, -1, 2433, 2437, 2439, 2442, 2445, 2447, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2449, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2451, -1, -1, -1, 2453, -1, -1, 2455, 2457, 2459, -1, 2462, -1, 2465, 2467, 2469, 2471, 2474, 2476, 2478, 2481, 2483, 2486, 2488, 2490, 2492, 2495, 2497, 2499, 2501, 2503, 2505, -1, -1, -1, -1, -1, -1, 2507, 2509, 2511, 2513, 2515, 2517, 2519, 2521, 2523, 2525, 2527, 2530, 2533, 2536, 2538, 2540, 2542, -1, -1, 2544, 2546, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2548, 2550, 2552, 2554, 2556, 2558, 2560, 2562, 2564, 2566, 2568, 2570, 2572, 2574, -1, -1, -1, 2576, 2578, 2580, -1, 2582, 2584, 2587, 2590, 2593, 2596, 2600, 2602, 2604, 2606, 2608, 2611, 2614, 2617, 2619, 2622, 2625, 2627, 2630, 2634, 2637, 2639, 2641, 2644, 2647, 2651, 2653, 2655, 2657, 2660, 2662, 2664, 2666, 2668, 2670, 2672, 2674, 2676, 2678, 2680, 2682, 2684, 2686, 2688, 2691, 2693, 2695, 2697, 2699, 2701, 2704, 2706, 2708, 2711, 2713, 2715, 2717, 2720, 2722, 2724, 2726, 2728, 2730, 2732, 2734, 2736, 2738, 2740, 2742, 2744, 2746, 2748, 2750, 2752, 2754, 2757, 2760, 2762, 2764, 2766, 2768, 2771, 2774, 2776, 2779, 2782, 2784, 2786, 2788, 2790, 2792, -1, -1, 2794, 2796, 2799, 2801, -1, -1, -1, -1, -1, -1, -1, 2804, 2806, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2808, -1, -1, -1, -1, -1, -1, 2810, 2812, 2814, 2816, 2818, 2820, 2822, 2824, 2826, 2828, 2830, 2833, 2835, 2838, 2840, -1, 2843, 2846, 2848, 2850, 2852, 2854, 2856, 2858, 2860, 2862, 2864, 2866, 2868, 2870, -1, -1, 2872, -1, 2874, 2876, 2878, 2880, 2882, 2884, 2886, 2888, 2890, 2892, 2894, 2896, 2898, 2901, 2903, 2905, 2907, 2909, 2911, 2913, 2915, 2917, 2919, 2921, 2923, 2925, 2928, -1, -1, -1, 2930, 2932, 2934, 2936, 2938, 2940, 2942, 2944, 2946, 2948, 2950, 2952, 2954, 2956, 2958, 2960, -1, 2962, 2964, -1, -1, 2966, 2968, 2970, 2972, 2974, 2976, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2978, 2980, 2983, 2985, 2987, 2989, 2991, 2993, 2995, 2997, 2999, 3001, 3004, 3007, 3009, 3011, 3014, 3016, 3018, 3020, 3022, 3024, 3026, 3028, 3031, 3034, 3037, 3040, 3043, 3045, 3048, 3050, 3052, 3054, 3056, 3058, 3061, 3063, 3065, 3067, 3070, 3072, 3075, 3077, 3080, 3082, 3085, 3087, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3090, 3092, 3094, -1, 3096, 3098, 3101, 3103, 3106, 3108, 3111, 3113, 3115, 3118, 3120, 3122, 3125, 3128, 3130, 3132, 3135, 3137, 3140, 3142, 3145, 3148, 3150, 3153, 3155, 3158, 3161, 3164, 3168, 3171, 3175, 3178, 3180, 3183, 3185, 3188, 3190, 3194, 3196, 3199, 3201, 3204, 3206, 3208, 3210, 3213, 3215, 3217, 3221, 3223, 3226, 3229, 3231, -1, -1, 3233, 3235, 3237, 3240, 3242, 3245, 3247, 3250, 3252, 3255, 3258, 3260, 3262, 3265, 3268, 3270, 3272, -1, -1, -1, 3275, 3279, 3281, 3283, 3285, -1, -1, -1, 3287, 3289, 3293, 3297, 3299, 3304, 3307, 3309, 3312, 3315, 3318, 3320, 3323, 3327, 3332, 3334, 3336, 3338, 3340, 3342, 3344, 3346, 3348, 3350, 3352, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3354, 3356, 3358, -1, 3360, 3362, 3365, 3367, 3370, 3372, 3375, 3377, -1, -1, 3380, 3382, -1, -1, 3385, 3387, 3390, 3392, 3395, 3397, 3400, 3403, 3405, 3408, 3410, 3413, 3416, 3419, 3423, 3426, 3430, 3433, 3435, 3438, 3440, 3443, -1, 3445, 3447, 3450, 3452, 3455, 3457, 3459, -1, 3461, -1, -1, -1, 3463, 3466, 3469, 3471, -1, -1, 3473, -1, 3475, 3478, 3480, 3483, 3485, 3488, 3490, -1, -1, 3493, 3495, -1, -1, 3498, 3500, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3503, -1, -1, -1, -1, 3505, 3508, -1, 3511, 3514, 3517, 3520, 3522, -1, -1, 3525, 3527, 3529, 3531, 3533, 3535, 3537, 3539, 3541, 3543, 3545, 3548, 3551, 3554, 3557, 3560, 3563, 3566, 3569, 3577, -1, -1, -1, -1, -1, -1, -1, -1, 3581, -1, -1, 3583, 3585, 3588, 3590, 3593, 3595, -1, -1, -1, -1, 3598, 3601, -1, -1, 3604, 3607, 3610, 3612, 3615, 3617, 3620, 3623, 3625, 3628, 3630, 3633, 3636, 3639, 3643, 3646, 3650, 3653, 3655, 3658, 3660, 3663, -1, 3665, 3667, 3670, 3672, 3675, 3677, 3679, -1, 3681, 3683, -1, 3686, 3688, -1, 3691, 3693, -1, -1, 3695, -1, 3697, 3700, 3702, 3705, 3707, -1, -1, -1, -1, 3710, 3713, -1, -1, 3716, 3719, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3722, 3726, 3730, 3732, -1, 3735, -1, -1, -1, -1, -1, -1, -1, 3737, 3739, 3741, 3743, 3745, 3747, 3749, 3751, 3753, 3755, 3757, 3759, -1, -1, 3761, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3768, 3770, 3772, -1, 3774, 3776, 3779, 3781, 3784, 3786, 3789, -1, 3791, -1, 3794, 3796, 3799, -1, 3802, 3804, 3807, 3809, 3812, 3814, 3817, 3820, 3822, 3825, 3827, 3830, 3833, 3836, 3840, 3843, 3847, 3850, 3852, 3855, 3857, 3860, -1, 3862, 3864, 3867, 3869, 3872, 3874, 3877, -1, 3879, 3881, -1, 3884, 3886, 3889, 3892, 3894, -1, -1, 3896, 3898, 3900, 3903, 3905, 3908, 3910, 3913, 3915, 3918, -1, 3921, 3923, 3926, -1, 3929, 3931, -1, -1, -1, 3934, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3938, -1, -1, -1, -1, -1, 3941, 3943, 3945, 3947, 3949, 3951, 3953, 3955, 3957, 3959, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3961, 3963, 3965, -1, 3967, 3969, 3972, 3974, 3977, 3979, 3982, 3984, -1, -1, 3986, 3988, -1, -1, 3991, 3993, 3996, 3998, 4001, 4003, 4006, 4009, 4011, 4014, 4016, 4019, 4022, 4025, 4029, 4032, 4036, 4039, 4041, 4044, 4046, 4049, -1, 4051, 4053, 4056, 4058, 4061, 4063, 4065, -1, 4067, 4069, -1, -1, 4072, 4075, 4078, 4080, -1, -1, 4082, 4084, 4086, 4089, 4091, 4094, 4096, 4099, -1, -1, -1, 4101, 4103, -1, -1, 4106, 4108, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4111, 4113, -1, -1, -1, -1, 4115, 4118, -1, 4121, 4124, 4127, -1, -1, -1, -1, 4130, 4132, 4134, 4136, 4138, 4140, 4142, 4144, 4146, 4148, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4150, 4152, -1, 4154, 4156, 4159, 4161, 4164, 4166, -1, -1, -1, 4169, 4171, 4174, -1, 4177, 4179, 4182, 4185, -1, -1, -1, 4187, 4190, -1, 4192, -1, 4194, 4197, -1, -1, -1, 4200, 4203, -1, -1, -1, 4205, 4207, 4211, -1, -1, -1, 4213, 4215, 4217, 4219, 4222, 4224, 4227, 4231, -1, 4233, 4236, 4238, -1, -1, -1, -1, 4240, 4243, 4245, 4248, 4250, -1, -1, -1, 4253, 4255, 4258, -1, 4261, 4263, 4266, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4269, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4271, 4273, 4275, 4277, 4279, 4281, 4283, 4285, 4287, 4289, 4291, 4296, 4302, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4309, 4311, 4313, -1, 4315, 4317, 4320, 4322, 4325, 4327, 4330, 4332, -1, 4334, 4336, 4339, -1, 4342, 4344, 4347, 4350, 4352, 4355, 4357, 4360, 4363, 4365, 4368, 4370, 4373, 4376, 4379, 4383, 4386, 4390, 4393, 4395, 4398, 4400, 4403, -1, 4405, 4407, 4410, 4412, 4415, 4417, 4419, 4421, 4424, 4426, -1, 4429, 4431, 4434, 4437, 4439, -1, -1, -1, -1, 4441, 4444, 4446, 4449, 4451, 4454, 4456, -1, 4459, 4461, 4464, -1, 4467, 4469, 4472, -1, -1, -1, -1, -1, -1, -1, -1, 4475, 4477, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4479, 4482, -1, -1, -1, -1, 4485, 4487, 4489, 4491, 4493, 4495, 4497, 4499, 4501, 4503, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4505, 4507, -1, 4509, 4511, 4514, 4516, 4519, 4521, 4524, 4526, -1, 4528, 4530, 4533, -1, 4536, 4538, 4541, 4544, 4546, 4549, 4551, 4554, 4557, 4559, 4562, 4564, 4567, 4570, 4573, 4577, 4580, 4584, 4587, 4589, 4592, 4594, 4597, -1, 4599, 4601, 4604, 4606, 4609, 4611, 4613, 4615, 4618, 4620, -1, 4623, 4625, 4628, 4631, 4633, -1, -1, -1, -1, 4635, 4638, 4640, 4643, 4645, 4648, 4650, -1, 4653, 4655, 4658, -1, 4661, 4663, 4666, -1, -1, -1, -1, -1, -1, -1, -1, 4669, 4671, -1, -1, -1, -1, -1, -1, -1, 4673, -1, 4677, 4680, -1, -1, -1, -1, 4683, 4685, 4687, 4689, 4691, 4693, 4695, 4697, 4699, 4701, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4703, 4705, -1, 4707, 4709, 4712, 4714, 4717, 4719, 4722, 4724, -1, 4726, 4728, 4731, -1, 4734, 4736, 4739, 4742, 4744, 4747, 4749, 4752, 4755, 4757, 4760, 4762, 4765, 4768, 4771, 4775, 4778, 4782, 4785, 4787, 4790, 4792, 4795, -1, 4797, 4799, 4802, 4804, 4807, 4809, 4811, 4813, 4816, 4818, 4821, 4825, 4827, 4830, 4833, 4835, -1, -1, -1, -1, 4837, 4840, 4842, 4845, 4847, 4850, -1, -1, 4852, 4854, 4857, -1, 4860, 4862, 4865, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4868, -1, -1, -1, -1, -1, -1, -1, -1, 4870, 4873, -1, -1, -1, -1, 4876, 4878, 4880, 4882, 4884, 4886, 4888, 4890, 4892, 4894, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4896, 4898, -1, 4900, 4902, 4905, 4908, 4912, 4914, 4917, 4919, 4922, 4924, 4927, 4929, 4932, 4934, 4937, 4940, 4942, 4945, -1, -1, -1, 4948, 4950, 4953, 4955, 4958, 4961, 4965, 4967, 4970, 4972, 4975, 4978, 4982, 4986, 4989, 4993, 4996, 5000, 5003, 5008, 5010, 5013, 5015, 5018, -1, 5020, 5023, 5025, 5028, 5030, 5033, 5035, 5038, 5040, -1, 5042, -1, -1, 5044, 5046, 5049, 5052, 5054, 5056, 5059, -1, -1, -1, -1, -1, -1, -1, -1, 5061, 5064, 5067, 5071, 5073, 5076, -1, 5078, -1, 5081, 5083, 5085, 5088, 5091, 5093, 5096, 5099, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 5101, 5104, 5107, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 5111, 5113, 5116, 5119, 5122, 5125, 5128, 5131, 5135, 5138, 5141, 5144, 5147, 5149, 5151, 5153, 5156, 5159, 5162, 5164, 5166, 5168, 5171, 5174, 5177, 5179, 5181, 5183, 5186, 5188, 5191, 5193, 5196, 5198, 5200, 5202, 5204, 5206, 5208, 5210, 5212, 5214, 5216, 5218, 5220, 5222, 5224, 5226, 5228, 5230, 5233, 5236, 5238, 5241, 5244, 5248, 5250, 5253, -1, -1, -1, -1, 5255, 5259, 5261, 5264, 5266, 5269, 5272, 5275, -1, -1, -1, -1, -1, -1, 5277, -1, 5279, 5283, 5285, 5287, 5289, 5291, 5293, 5295, 5297, 5299, 5301, 5303, 5308, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 5314, 5316, -1, 5319, -1, -1, 5322, 5325, -1, 5328, -1, -1, 5330, -1, -1, -1, -1, -1, -1, 5333, 5335, 5337, 5340, -1, 5343, 5345, 5347, 5349, 5352, 5354, 5357, -1, 5359, 5361, 5363, -1, 5365, -1, 5367, -1, -1, 5369, 5371, -1, 5373, -1, 5375, 5377, -1, 5379, 5382, 5385, 5387, 5390, 5392, 5395, 5397, -1, 5400, 5402, 5404, -1, -1, 5407, 5409, 5412, 5414, 5417, -1, 5420, -1, -1, -1, -1, -1, -1, 5422, -1, -1, 5424, 5426, 5428, 5430, 5432, 5434, 5436, 5438, 5440, 5442, -1, -1, 5444, 5447, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 5450, -1, -1, -1, -1, -1, -1, -1, 5454, 5459, -1, 5463, 5465, 5469, 5473, 5478, 5483, 5488, 5493, 5500, 5506, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 5509, 5511, 5513, 5515, 5517, 5519, 5521, 5523, 5525, 5527, 5529, 5532, 5536, 5540, 5544, 5548, 5552, 5556, 5560, 5564, 5568, 5570, 5572, 5574, -1, 5576, -1, 5578, 5580, 5583, -1, -1, 5586, 5588, 5591, 5593, 5596, 5599, 5601, 5604, -1, 5606, 5609, 5612, 5616, 5619, 5623, 5626, 5628, 5631, 5633, 5636, 5638, 5640, 5643, 5645, 5648, 5650, 5653, 5657, 5660, 5664, 5666, 5669, 5671, 5673, 5675, 5677, 5679, 5682, 5686, 5688, 5690, 5692, 5696, -1, -1, -1, -1, -1, -1, 5698, 5701, 5703, 5706, 5708, 5711, 5713, 5716, 5718, 5721, 5723, 5726, 5728, 5731, 5733, 5735, 5737, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 5740, 5742, 5745, 5747, 5750, 5753, 5755, 5758, -1, 5760, 5763, 5766, 5770, 5773, 5777, 5780, 5782, 5785, 5787, 5790, 5792, 5794, 5797, 5799, 5802, 5804, 5807, 5811, 5814, 5818, 5820, 5823, 5825, 5827, 5829, 5831, 5833, 5836, 5839, 5841, 5843, 5845, 5849, 5851, 5853, -1, 5855, 5857, 5863, 5869, 5875, 5881, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 5887, 5889, 5892, 5894, 5897, 5900, 5902, 5905, 5907, 5910, 5913, 5917, 5920, 5924, 5927, 5931, 5934, 5937, 5940, 5942, 5945, 5947, 5949, 5952, 5954, 5957, 5959, 5961, 5963, 5965, 5967, 5969, 5971, 5974, -1, 5976, 5978, 5981, 5983, 5986, -1, 5988, 5990, -1, 5993, 5996, 5998, 6001, 6003, 6006, 6008, -1, -1, -1, 6011, 6013, 6015, -1, -1, -1, -1, -1, -1, -1, 6017, 6019, 6021, 6023, 6025, 6027, 6029, 6031, 6033, 6035, 6037, 6041, 6046, 6049, 6052, 6055, 6058, 6061, 6064, 6066, 6069, 6071, 6074, 6076, 6079, 6081, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 6084, 6086, 6088, 6090, 6092, 6094, 6096, 6098, 6101, 6103, 6105, 6107, 6109, 6111, 6113, 6115, 6118, 6120, 6122, 6124, 6126, 6129, 6132, 6135, 6137, 6140, 6144, 6147, 6150, 6152, 6155, 6157, 6159, 6161, 6163, 6165, 6167, 6170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 6173, 6175, 6177, 6179, 6181, 6183, 6185, 6187, 6190, 6192, 6194, 6196, 6198, 6200, 6202, 6204, 6207, 6209, 6211, 6213, 6215, 6218, 6221, 6224, 6226, 6229, 6233, 6236, 6239, 6241, 6244, 6246, 6248, 6250, 6252, 6254, 6256, 6259, 6262, -1, -1, -1, -1, 6264, -1, -1, -1, -1, 6269, 6271, 6274, 6276, 6278, 6281, 6283, 6285, 6287, 6290, 6292, -1, 6295, 6297, 6300, 6302, 6304, 6306, 6308, 6310, 6313, 6316, 6319, 6322, 6325, 6328, 6331, 6334, 6337, 6340, 6343, 6346, -1, 6349, 6352, 6356, 6360, 6364, 6368, 6372, 6375, 6378, 6381, 6384, 6387, 6391, 6394, 6397, 6400, 6403, 6406, 6409, 6413, 6417, 6419, 6422, 6425, 6428, 6431, 6434, -1, -1, -1, -1, 6437, 6439, 6441, 6443, 6445, 6447, 6449, -1, 6451, 6453, 6455, 6457, 6459, 6461, -1, -1, -1, -1, 6463, 6466, -1, -1, 6469, 6472, 6475, 6478, -1, -1, -1, -1, -1, -1, -1, 6480, 6482, 6485, 6488, 6492, 6495, 6497, 6501, 6504, 6506, 6509, 6513, 6516, 6519, 6521, 6525, 6528, 6531, 6534, 6537, 6540, 6542, 6546, 6550, 6555, 6561, 6566, 6571, 6577, 6583, 6589, 6594, 6598, 6603, 6607, 6611, 6617, 6624, 6631, 6636, 6641, 6645, 6650, 6658, 6663, 6667, 6672, 6678, 6683, 6690, 6696, 6701, 6706, 6711, 6717, 6722, 6726, 6731, 6735, 6739, 6744, 6748, 6750, 6755, 6759, 6763, -1, -1, -1, -1, -1, 6766, 6768, 6771, 6774, 6776, 6779, 6782, 6784, 6786, 6789, 6792, 6795, 6798, 6801, 6804, 6807, 6809, 6811, 6814, 6816, 6819, 6822, 6824, 6826, 6828, 6830, 6832, 6834, 6837, 6841, 6844, 6847, 6850, 6853, 6856, 6859, 6862, 6866, 6869, 6872, 6876, 6879, 6883, 6887, 6891, 6895, 6899, 6903, 6906, 6909, 6912, 6915, 6918, 6921, 6924, 6928, 6931, 6934, 6937, 6940, 6943, 6946, 6949, 6952, 6955, 6958, 6961, 6964, 6966, 6968, -1, 6971, 6974, 6976, 6979, 6982, 6985, 6988, 6991, 6994, 6997, 7000, -1, -1, -1, -1, -1, -1, 7002, 7005, 7008, 7011, 7015, 7019, 7022, -1, 7025, 7028, 7031, 7034, 7038, 7042, 7045, 7048, 7052, 7056, 7060, 7064, 7069, 7074, 7078, 7082, 7087, 7090, 7093, 7096, 7100, 7104, 7107, 7110, 7114, 7118, 7122, 7126, 7131, 7136, 7140, 7144, 7149, 7152, 7155, 7158, 7162, 7166, 7169, 7172, 7176, 7179, 7182, 7185, 7189, 7193, 7196, 7199, 7203, 7207, 7211, 7215, 7220, 7225, 7229, 7233, 7238, 7241, 7244, 7247, 7251, 7255, 7258, -1, 7261, -1, 7265, 7269, 7274, 7279, -1, -1, 7283, 7287, 7291, 7295, 7300, 7305, 7309, -1, 7313, -1, 7318, 7323, 7329, 7335, -1, -1, 7340, 7343, 7346, 7349, 7353, 7357, 7360, 7363, 7367, 7370, 7373, 7376, 7380, 7384, 7387, 7390, 7394, 7397, 7400, 7403, 7407, 7411, 7414, 7417, 7421, 7424, 7427, 7430, 7434, 7438, 7441, 7444, 7448, 7451, 7454, 7457, 7461, 7465, 7468, -1, 7471, -1, 7475, 7479, 7484, 7489, -1, -1, 7493, 7496, 7499, 7502, 7506, 7510, 7513, 7516, 7520, 7524, 7528, 7532, 7537, 7542, 7546, 7550, 7555, 7558, -1, 7561, 7565, 7569, 7572, 7575, 7579, 7582, 7585, 7588, 7592, 7596, 7599, -1, 7602, -1, 7606, 7610, 7615, 7620, -1, -1, 7624, 7628, 7632, 7636, 7641, 7646, 7650, -1, 7654, -1, 7659, 7664, 7670, 7676, -1, -1, 7681, 7684, 7687, 7690, 7694, 7698, 7701, -1, 7704, 7707, 7710, 7713, 7717, 7721, 7724, -1, 7727, 7730, 7733, 7736, 7740, 7744, 7747, 7750, 7754, 7758, 7762, 7766, 7771, 7776, 7780, 7784, 7789, 7792, 7795, 7798, 7802, 7806, 7809, -1, 7812, 7815, 7818, 7821, 7825, 7829, 7832, 7835, 7839, 7843, 7847, 7851, 7856, 7861, 7865, 7869, 7874, 7877, 7880, 7883, 7887, 7891, 7894, 7897, 7901, 7904, 7907, 7910, 7914, 7918, 7921, -1, 7924, -1, 7928, 7932, 7937, 7942, -1, -1, 7946, 7950, 7954, 7958, 7963, 7968, 7972, -1, 7976, 7980, 7984, 7988, 7993, 7998, 8002, 8006, 8011, 8015, 8019, 8023, 8028, 8033, 8037, 8041, 8046, 8050, 8054, 8058, 8063, 8068, 8072, 8076, 8081, 8085, 8089, 8093, 8098, 8103, 8107, 8111, 8116, 8120, 8124, 8128, 8133, 8138, 8142, -1, 8146, 8149, 8152, 8155, 8159, 8163, 8166, 8169, 8173, 8176, 8179, 8182, 8186, 8190, 8193, 8196, 8200, 8204, 8208, -1, -1, -1, -1, -1, -1, 8212, 8214, 8216, 8218, 8220, 8222, 8226, 8228, 8231, 8233, 8235, 8237, 8239, 8241, 8243, 8245, 8247, 8249, 8253, 8257, 8261, 8265, 8269, 8273, 8277, 8281, 8285, 8290, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 8298, 8300, 8302, 8304, 8306, 8308, 8310, 8313, 8316, 8319, 8322, 8325, 8328, 8331, 8334, 8337, 8340, 8343, 8346, 8349, 8352, 8355, 8358, 8361, 8364, 8367, 8370, 8373, 8376, 8379, 8382, 8385, 8389, 8393, 8396, 8399, 8402, 8405, 8408, 8412, 8416, 8420, 8424, 8428, 8432, 8435, 8437, 8440, 8443, 8446, 8449, 8452, 8455, 8458, 8461, 8464, 8467, 8470, 8473, 8476, 8479, 8483, 8487, 8491, 8495, 8499, 8503, 8507, 8511, 8515, 8519, 8523, 8527, 8531, 8534, 8537, 8540, 8543, 8546, 8549, 8552, 8555, 8558, 8561, 8564, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 8567, 8569, 8573, 8575, 8578, 8580, 8583, 8586, 8589, 8591, 8593, 8596, 8599, 8602, 8605, 8608, 8612, 8616, 8619, 8622, 8626, 8630, 8634, 8637, 8640, 8644, 8648, 8652, 8655, 8657, 8659, 8661, 8663, 8666, 8668, 8670, 8672, -1, 8674, 8676, 8678, 8680, 8682, 8685, 8688, 8691, 8694, 8697, 8702, 8705, 8709, 8712, 8716, 8720, 8724, 8727, 8730, 8734, 8738, 8742, 8746, 8750, 8755, 8760, 8764, 8768, 8773, 8778, 8782, 8786, 8791, 8796, 8801, 8803, 8805, 8807, 8810, 8815, 8818, 8822, 8825, 8829, 8833, 8837, 8840, 8843, 8847, 8851, 8855, 8859, 8863, 8868, 8873, 8877, 8881, 8886, 8891, 8895, 8899, 8904, 8909, 8914, 8916, 8920, 8924, 8928, 8932, 8935, 8940, 8943, 8947, 8950, 8954, 8958, 8961, 8965, 8969, 8973, 8977, 8981, 8986, 8991, 8995, 8999, 9004, 9009, 9013, 9017, 9022, 9027, 9032, 9034, 9037, 9041, 9045, 9049, 9053, 9056, 9061, 9064, 9068, 9071, 9075, 9079, 9082, 9086, 9090, 9094, 9098, 9102, 9107, 9112, 9116, 9120, 9125, 9130, 9134, 9138, 9143, 9148, 9153, 9155, 9158, 9161, 9166, 9169, 9173, 9176, 9180, 9184, 9187, 9191, 9195, 9199, 9203, 9207, 9212, 9217, 9221, 9225, 9230, 9235, 9239, 9243, 9248, 9253, 9258, 9260, 9262, 9265, 9267, 9269, 9272, 9277, 9280, 9284, 9287, 9291, 9295, 9298, 9302, 9306, 9310, 9314, 9318, 9323, 9328, 9333, 9335, 9338, 9341, 9344, 9349, 9352, 9356, 9359, 9363, 9367, 9370, 9374, 9378, 9382, 9386, 9390, 9395, 9400, 9404, 9408, 9413, 9418, 9422, 9426, 9431, 9436, 9438, 9440, 9442, 9445, 9450, 9453, 9457, 9460, 9464, 9468, 9471, 9475, 9479, 9483, 9487, 9491, 9496, 9501, 9505, 9509, 9514, 9519, 9523, 9527, 9532, 9537, 9542, 9544, 9546, 9549, 9551, 9554, 9558, 9561, 9566, 9571, 9576, 9581, 9585, 9589, 9594, 9598, 9603, 9607, 9612, 9617, 9622, 9627, 9632, 9638, 9644, 9649, 9654, 9660, 9666, 9671, 9676, 9682, 9688, 9691, 9694, 9699, 9702, 9706, 9709, 9713, 9717, 9720, 9724, 9728, 9732, 9736, 9740, 9745, 9750, 9754, 9758, 9763, 9768, 9772, 9776, 9781, 9786, 9791, 9793, 9795, 9797, 9800, 9803, 9806, 9809, 9814, 9817, 9821, 9824, 9828, 9831, 9834, 9838, 9841, 9846, 9851, 9853, 9855, 9857, 9860, 9865, 9868, 9872, 9875, 9879, 9882, 9886, 9891, 9896, 9898, 9902, 9906, 9910, 9914, 9919, 9924, 9928, 9933, 9937, 9942, 9948, 9954, 9957, 9962, 9967, 9972, 9977, 9981, 9985, 9989, 9993, 9997, 10000, 10003, 10007, 10010, 10014, 10017, 10021, 10023, 10025, 10028, 10033, 10036, 10040, 10043, 10047, 10050, 10054, 10056, 10061, 10066, 10071, 10076, 10079, 10082, 10085, 10088, 10094, 10098, 10103, 10107, 10112, 10116, 10121, 10124, 10128, 10132, 10136, 10140, 10144, 10148, 10152, 10156, 10160, 10163, 10167, 10172, 10176, 10181, 10185, 10190, 10193, 10197, 10201, 10206, 10210, 10215, 10219, 10224, 10227, 10229, 10231, 10233, 10235, 10237, 10240, 10243, 10246, 10249, 10252, 10255, 10258, 10261, 10264, 10267, 10270, 10273, 10276, 10279, 10282, 10285, 10289, 10293, 10297, 10302, 10306, 10310, 10313, 10316, 10319, 10323, 10326, 10329, 10332, 10335, 10338, 10342, 10345, 10348, 10352, 10356, 10360, 10365, 10369, 10373, 10377, 10381, 10385, 10390, 10394, 10398, 10402, 10406, 10410, 10415, 10419, 10423, 10426, 10429, 10432, 10436, 10439, 10442, 10444, 10447, 10450, 10453, 10457, 10460, 10463, 10467, 10471, 10475, 10480, 10484, 10488, 10492, 10496, 10500, 10505, 10509, 10513, 10516, 10519, 10522, 10525, 10529, 10532, 10535, 10538, 10541, 10544, 10548, 10551, 10554, 10557, 10560, 10563, 10567, 10570, 10573, 10576, 10579, 10582, 10585, 10589, 10592, 10595, 10598, 10602, 10606, 10610, 10615, 10619, 10623, 10626, 10629, 10632, 10636, 10639, 10642, 10646, 10650, 10654, 10659, 10663, 10667, 10671, 10675, 10679, 10684, 10688, 10692, 10697, 10702, 10707, 10713, 10718, 10723, 10727, 10731, 10735, 10740, 10744, 10748, 10751, 10754, 10757, 10761, 10764, 10767, 10769, 10771, 10775, 10779, 10783, 10788, 10792, 10796, 10799, 10802, 10805, 10809, 10812, 10815, 10819, 10823, 10827, 10832, 10836, 10840, 10843, 10847, 10851, 10855, 10860, 10864, 10868, 10872, 10876, 10880, 10885, 10889, 10893, 10898, 10903, 10908, 10914, 10919, 10924, 10926, 10928, 10932, 10937, 10942, 10948, 10953, 10959, 10964, -1, -1, -1, -1, -1, -1, -1, -1, -1, 10970, 10972, 10974, 10976, 10978, 10980, 10982, 10984, 10986, 10988, 10990, 10992, 10994, 10996, 10999, 11001, 11003, 11005, 11007, 11009, 11011, 11013, 11016, 11019, 11022, 11024, 11026, 11028, 11030, -1, -1, -1, 11032, 11034, 11036, 11038, 11041, 11043, 11045, 11048, 11051, 11053, 11055, 11058, 11061, 11063, 11065, 11067, 11070, 11073, 11075, 11077, 11079, 11081, 11083, 11086, 11088, 11090, 11092, 11094, 11096, 11098, 11100, 11102, 11104, 11106, 11108, 11110, 11112, 11114, 11117, 11119, 11122, 11124, 11126, 11128, 11130, 11132, 11134, 11136, 11138, 11140, 11142, 11144, 11146, 11148, 11150, 11152, 11154, 11156, 11158, 11160, 11162, 11165, 11168, 11170, 11172, 11176, 11180, 11183, 11186, 11189, 11191, 11194, 11197, 11200, 11202, 11204, 11206, 11208, 11210, 11213, 11216, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 11219, 11221, 11224, 11226, 11229, 11232, 11234, 11237, 11239, 11242, 11245, 11247, 11251, 11253, 11257, 11260, 11262, 11265, 11267, 11270, 11272, 11274, 11277, 11279, 11282, 11284, 11286, 11288, 11290, 11292, 11295, 11298, 11300, 11302, 11304, 11306, 11308, 11311, 11313, 11316, 11318, 11321, 11324, 11328, 11331, 11335, 11338, 11342, 11344, 11347, 11350, 11353, 11356, 11358, 11361, 11364, 11366, 11369, 11371, 11374, 11376, 11379, 11382, 11385, 11388, 11391, 11393, 11396, 11399, 11402, 11405, 11407, 11409, -1, -1, -1, 11412, -1, 11414, -1, -1, -1, -1, -1, 11416, 11418, 11423, 11425, 11427, 11430, 11434, 11440, 11443, -1, -1, -1, 11445, 11447, 11449, 11451, 11453, 11455, 11457, 11459, 11461, 11463, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 11465, 11469, 11475, 11478, 11481, 11484, -1, 11489, 11491, 11494, -1, -1, -1, -1, -1, -1, 11497, 11499, 11501, 11503, 11505, 11507, 11509, 11511, 11513, 11515, -1, -1, -1, -1, -1, -1, 11517, 11519, 11521, 11523, 11525, 11527, 11529, 11531, 11534, 11536, 11539, 11541, 11543, 11545, 11547, 11549, 11551, 11553, 11556, 11558, 11560, 11563, 11565, 11567, 11569, 11571, 11573, 11575, 11579, 11582, 11584, 11586, 11589, 11592, 11595, 11598, 11600, 11602, 11604, 11606, 11608, 11610, 11612, 11615, 11617, 11619, 11621, 11623, 11625, 11627, 11629, 11632, 11634, 11637, 11639, 11641, 11643, 11645, 11647, 11650, 11653, 11656, 11658, 11660, 11663, 11665, 11667, 11670, 11672, 11674, 11676, 11678, 11681, 11683, 11685, 11687, 11689, 11691, 11693, 11696, 11698, 11700, 11703, 11706, 11708, 11710, 11712, 11714, -1, -1, -1, -1, -1, -1, -1, -1, -1, 11717, 11719, 11721, 11723, 11725, 11729, 11735, 11737, 11739, 11741, 11744, 11746, 11749, 11753, 11756, 11759, 11761, 11763, 11765, 11768, 11771, 11774, 11776, 11778, 11780, 11783, 11786, 11789, 11791, 11794, 11798, 11802, 11804, 11807, 11810, 11813, 11816, 11818, 11820, 11822, 11825, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 11827, 11829, 11832, 11835, 11837, 11839, 11841, 11843, 11845, 11847, 11849, 11851, 11853, 11855, 11857, 11859, -1, 11861, -1, 11863, 11865, 11868, -1, -1, 11871, 11873, 11875, 11877, 11879, 11881, 11883, 11885, 11887, 11889, 11891, -1, -1, -1, -1, -1, -1, -1, -1, -1, 11893, 11895, 11898, 11900, 11902, 11904, 11906, 11908, 11910, 11912, 11914, 11916, 11918, 11920, 11922, 11924, 11926, 11928, 11931, 11933, 11935, 11937, 11939, 11941, 11943, 11945, 11947, 11950, 11952, 11954, -1, 11956, 11958, 11960, 11962, 11964, 11966, -1, 11968, -1, -1, -1, 11970, 11972, 11974, 11976, 11978, 11980, -1, 11982, 11984, 11986, 11988, -1, 11990, 11992, 11994, 11996, 11998, 12000, 12002, 12004, -1, -1, 12006, 12008, 12010, 12012, 12014, 12016, 12018, 12020, 12022, 12024, 12026, 12028, -1, -1, -1, -1, -1, 12030, -1, -1, 12032, 12034, 12036, 12038, 12040, 12042, 12044, 12046, 12048, 12050, 12052, -1, 12054, 12056, 12058, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 12060, 12062, 12064, 12066, 12068, 12070, 12072, 12074, 12076, 12078, 12080, 12082, 12084, 12086, 12088, 12090, 12092, 12094, 12096, 12098, 12100, 12102, 12104, 12106, 12108, 12110, 12112, 12114, 12116, 12118, 12120, 12122, 12124, 12126, 12128, 12130, 12132, 12134, 12136, 12138, 12140, 12142, 12144, 12146, 12148, 12150, 12152, 12154, 12156, 12158, 12160, 12162, 12164, 12166, 12168, 12170, 12172, 12174, 12176, 12178, 12180, 12182, 12184, 12186, 12188, 12190, 12192, 12194, 12196, 12198, 12200, 12202, 12204, 12206, 12208, 12210, 12212, 12214, 12216, 12218, 12220, 12222, 12224, 12226, 12228, 12230, 12232, 12234, 12236, 12238, 12240, 12242, 12244, 12246, 12248, 12250, 12252, 12254, 12256, 12258, 12260, 12262, 12264, 12266, 12268, 12270, 12272, 12274, 12276, 12278, 12280, 12282, 12284, 12286, 12288, 12290, 12292, 12294, 12296, 12298, 12300, 12302, 12304, 12306, 12308, 12310, 12312, 12314, 12316, 12318, 12320, 12322, 12324, 12326, 12328, 12330, 12332, 12334, 12336, 12338, 12340, 12342, 12344, 12346, 12348, 12350, 12352, 12354, 12356, 12358, 12360, 12362, 12364, 12366, 12368, 12370, -1, -1, 12372, -1, 12375, 12377, 12379, 12381, 12383, 12385, 12387, 12389, 12391, 12393, 12395, 12397, 12399, 12401, 12403, 12405, 12407, 12409, 12411, 12413, 12415, 12417, 12419, 12421, 12423, 12425, 12427, 12429, 12431, 12433, 12435, 12437, 12439, 12441, 12443, 12445, 12447, 12449, 12451, 12453, 12455, 12457, 12459, 12461, 12463, 12465, 12467, 12469, 12471, 12473, 12475, 12477, 12479, 12481, 12483, 12485, 12487, 12489, 12491, 12493, 12495, 12497, 12499, 12501, 12503, 12505, 12507, 12509, 12511, 12513, 12515, 12517, 12519, 12521, 12523, 12525, 12527, 12529, 12531, 12533, 12535, 12537, 12539, 12541, 12543, 12545, 12547, 12549, 12551, 12553, -1, -1, -1, -1, -1, -1, 12555, 12557, 12559, 12561, 12563, 12565, 12567, 12569, 12571, 12573, 12575, 12577, 12579, 12581, 12583, 12585, 12587, 12589, 12591, 12593, 12595, 12597, -1, -1, 12599, 12601, 12603, 12605, 12607, 12609, -1, -1, 12611, 12613, 12615, 12617, 12619, 12621, 12623, 12625, 12627, 12629, 12631, 12633, 12635, 12637, 12639, 12641, 12643, 12645, 12647, 12649, 12651, 12653, 12655, 12657, 12659, 12661, 12663, 12665, 12667, 12669, 12671, 12673, 12675, 12677, 12679, 12681, 12683, 12685, -1, -1, 12687, 12689, 12691, 12693, 12695, 12697, -1, -1, 12699, 12701, 12703, 12705, 12707, 12709, 12711, 12713, -1, 12715, -1, 12717, -1, 12719, -1, 12721, 12723, 12725, 12727, 12729, 12731, 12733, 12735, 12737, 12739, 12741, 12743, 12745, 12747, 12749, 12751, 12753, 12755, 12757, 12759, 12761, 12763, 12765, 12767, 12769, 12771, 12773, 12775, 12777, 12779, 12781, -1, -1, 12783, 12785, 12787, 12789, 12791, 12793, 12795, 12797, 12799, 12801, 12803, 12805, 12807, 12809, 12811, 12813, 12815, 12817, 12819, 12821, 12823, 12825, 12827, 12829, 12831, 12833, 12835, 12837, 12839, 12841, 12843, 12845, 12847, 12849, 12851, 12853, 12855, 12857, 12859, 12861, 12863, 12865, 12867, 12869, 12871, 12873, 12875, 12877, 12879, 12881, 12883, 12885, 12887, -1, 12889, 12891, 12893, 12895, 12897, 12899, 12901, 12903, 12905, 12907, 12909, 12911, 12914, 12916, 12918, -1, 12920, 12922, 12924, 12926, 12928, 12930, 12932, 12934, 12937, 12940, 12943, 12945, 12947, 12949, -1, -1, 12951, 12953, 12955, 12957, 12959, 12961, -1, 12963, 12966, 12969, 12972, 12974, 12976, 12978, 12980, 12982, 12984, 12986, 12988, 12990, 12992, 12994, 12996, 12998, 13001, 13004, -1, -1, 13006, 13008, 13010, -1, 13012, 13014, 13016, 13018, 13020, 13022, 13024, 13026, 13028, -1, 13030, 13032, 13034, 13036, 13038, 13040, 13042, 13044, 13046, 13048, 13050, 13052, -1, -1, -1, -1, 13054, 13056, 13058, 13060, 13062, 13065, 13068, 13071, 13073, 13075, 13077, 13079, 13081, 13083, 13085, 13088, 13090, 13092, 13095, 13097, 13100, 13102, 13105, 13109, 13111, 13113, -1, -1, -1, -1, -1, 13116, 13118, 13121, 13125, 13127, 13130, 13134, 13136, 13139, 13143, 13145, 13147, 13149, 13151, 13154, 13157, 13159, 13161, 13163, 13165, 13169, 13172, 13174, 13177, 13180, 13183, 13186, 13189, 13191, 13194, 13197, 13200, -1, -1, -1, 13202, 13204, -1, -1, -1, 13206, -1, -1, -1, -1, -1, -1, -1, 13211, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 13213, 13215, -1, -1, 13217, 13219, 13221, 13223, 13225, 13227, 13229, 13231, 13233, 13235, 13237, 13239, 13241, 13243, 13245, 13247, 13249, 13251, 13253, 13255, 13257, 13259, 13261, 13263, 13265, 13267, 13269, -1, 13271, 13273, 13275, 13277, -1, 13279, 13281, 13283, 13285, 13287, 13289, 13291, 13293, -1, -1, -1, 13295, 13299, 13302, 13305, 13308, 13310, 13314, 13316, 13320, 13323, 13325, 13328, 13330, 13334, 13336, 13338, 13341, 13344, 13346, 13348, 13350, 13354, 13357, 13359, 13362, 13364, 13367, 13369, 13371, 13373, 13375, 13377, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 13381, 13387, 13393, -1, -1, 13395, 13401, -1, -1, -1, 13407, 13409, 13411, 13413, 13415, -1, 13417, 13419, 13421, 13423, -1, 13425, 13427, -1, -1, 13432, 13434, 13436, 13438, 13440, -1, -1, 13442, 13447, 13451, -1, 13456, -1, -1, -1, 13458, -1, 13460, 13462, 13464, 13466, 13468, 13470, 13472, 13474, 13476, 13478, 13480, -1, -1, -1, -1, 13482, -1, 13484, -1, -1, -1, -1, -1, -1, -1, -1, -1, 13488, 13490, 13492, 13494, 13496, -1, -1, -1, -1, 13498, -1, 13500, 13506, 13512, 13519, 13525, 13531, 13537, 13543, 13549, 13555, 13561, 13567, 13573, 13579, 13585, 13591, 13595, 13597, 13600, 13604, 13607, 13609, 13612, 13616, 13621, 13624, 13626, 13629, 13633, 13635, 13637, 13639, 13641, 13643, 13646, 13650, 13653, 13655, 13658, 13662, 13667, 13670, 13672, 13675, 13679, 13681, 13683, 13685, 13687, 13690, 13693, 13699, -1, -1, -1, -1, -1, 13701, -1, -1, -1, -1, -1, -1, 13707, 13709, 13711, 13713, 13715, 13717, 13719, 13721, 13723, 13725, 13727, 13729, 13731, 13733, 13735, 13737, 13739, 13741, 13743, 13745, 13747, 13749, 13751, 13753, 13755, 13757, 13759, 13761, 13763, 13765, 13767, 13769, 13771, 13773, 13775, 13777, 13779, 13781, 13783, 13785, 13787, 13789, 13791, 13793, 13795, 13797, 13799, 13801, 13803, 13805, 13807, 13809, 13811, 13813, 13815, 13817, 13819, 13821, 13823, 13825, 13827, 13829, 13831, 13833, 13835, 13837, 13839, 13841, 13843, 13845, 13847, 13849, 13851, 13853, 13855, 13857, 13859, 13861, 13863, 13865, 13867, 13869, 13871, 13873, 13875, 13877, 13879, 13881, 13883, 13885, 13887, 13889, 13891, 13893, 13895, 13897, 13899, 13901, 13903, 13905, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 13907, -1, -1, 13909, 13911, 13913, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 13915, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 13917, -1, -1, -1, -1, -1, 13919, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 13921, 13924, 13927, 13930, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 13933, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 13935, 13937, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 13940, 13942, 13944, 13946, 13948, 13950, 13952, 13954, 13956, 13958, 13961, 13964, 13967, 13970, 13973, 13976, 13979, 13982, 13985, 13988, 13991, 13995, 13999, 14003, 14007, 14011, 14015, 14019, 14023, 14027, 14032, 14037, 14042, 14047, 14052, 14057, 14062, 14067, 14072, 14077, 14082, 14085, 14088, 14091, 14094, 14097, 14100, 14103, 14106, 14109, 14113, 14117, 14121, 14125, 14129, 14133, 14137, 14141, 14145, 14149, 14153, 14157, 14161, 14165, 14169, 14173, 14177, 14181, 14185, 14189, 14193, 14197, 14201, 14205, 14209, 14213, 14217, 14221, 14225, 14229, 14233, 14237, 14241, 14245, 14249, 14253, 14257, 14259, 14261, 14263, 14265, 14267, 14269, 14271, 14273, 14275, 14277, 14279, 14281, 14283, 14285, 14287, 14289, 14291, 14293, 14295, 14297, 14299, 14301, 14303, 14305, 14307, 14309, 14311, 14313, 14315, 14317, 14319, 14321, 14323, 14325, 14327, 14329, 14331, 14333, 14335, 14337, 14339, 14341, 14343, 14345, 14347, 14349, 14351, 14353, 14355, 14357, 14359, 14361, 14363, 14366, 14369, 14372, 14375, 14378, 14381, 14384, 14387, 14390, 14393, 14395, 14397, 14399, 14401, 14403, 14405, 14407, 14409, 14411, 14414, 14416, 14418, 14420, 14422, 14424, 14426, 14428, 14430, 14432, 14434, 14436, 14438, 14440, 14442, 14444, 14446, 14448, 14450, 14452, 14454, 14456, 14458, 14460, 14462, 14464, 14466, 14468, 14470, 14472, 14474, 14476, 14478, 14480, 14482, 14484, 14486, 14488, 14490, 14492, 14494, 14496, 14498, 14500, 14502, 14504, 14506, 14508, 14510, 14512, 14514, 14516, 14518, 14520, 14522, 14524, 14526, 14528, 14530, 14532, 14534, 14536, 14538, 14540, 14542, 14544, 14546, 14548, 14550, 14552, 14554, 14556, 14558, 14560, 14562, 14564, 14566, 14568, 14570, 14572, 14574, 14576, 14578, 14580, 14582, 14584, 14586, 14588, 14590, 14592, 14594, 14596, 14598, 14600, 14602, 14604, 14606, 14608, 14610, 14612, 14614, 14616, 14618, 14620, 14622, 14624, 14626, 14628, 14630, 14632, 14634, 14636, 14638, 14640, 14642, 14644, 14646, 14648, 14650, 14652, 14654, 14656, 14658, 14660, 14662, 14664, 14666, 14668, 14670, 14672, 14674, 14676, 14678, 14680, 14682, 14684, 14686, 14688, 14690, 14692, 14694, 14696, 14698, 14700, 14702, 14704, 14706, 14708, 14710, 14712, 14714, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 14716, 14718, 14720, 14722, 14724, 14726, 14728, 14730, 14732, 14734, 14736, 14738, 14740, 14742, 14744, 14746, 14748, 14750, 14752, 14754, 14756, 14758, 14760, 14762, 14764, 14766, 14768, 14770, 14772, 14774, 14776, 14778, 14780, 14782, 14784, 14786, 14788, 14790, 14792, 14794, 14796, 14798, 14800, 14802, 14804, 14806, 14808, 14810, 14812, 14814, 14816, 14818, 14820, 14822, 14824, 14826, 14828, 14830, 14832, 14834, 14836, 14838, 14840, 14842, 14844, 14846, 14848, 14850, 14852, 14854, 14856, 14858, 14860, 14862, 14864, 14866, 14868, 14870, 14872, 14874, 14876, 14878, 14880, 14882, 14884, 14886, 14888, 14890, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 14892, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 14894, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 14896, -1, -1, -1, 14898, 14900, 14902, 14904, 14906, -1, 14909, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 14911, -1, 14913, 14915, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 14918, 14920, 14922, 14924, 14926, 14928, 14930, 14932, 14934, 14936, 14938, 14940, 14942, 14944, 14946, 14948, 14950, 14952, 14954, 14956, 14958, 14960, 14962, 14964, 14966, 14968, 14970, 14972, 14974, 14976, 14978, 14980, 14982, 14984, 14986, 14988, 14990, 14992, 14994, 14996, 14998, 15000, 15002, 15004, 15006, 15008, 15010, 15012, 15014, 15016, 15018, 15020, 15022, 15024, 15026, 15028, 15030, 15032, 15034, 15036, 15038, 15040, 15042, 15044, 15046, 15051, 15057, 15063, 15070, 15076, 15083, 15090, 15098, 15104, 15111, 15118, 15126, 15133, 15141, 15149, 15158, 15164, 15171, 15178, 15186, 15193, 15201, 15209, 15218, 15225, 15233, 15241, 15250, 15258, 15267, 15276, 15286, 15292, 15299, 15306, 15314, 15321, 15329, 15337, 15346, 15353, 15361, 15369, 15378, 15386, 15395, 15404, 15414, 15421, 15429, 15437, 15446, 15454, 15463, 15472, 15482, 15490, 15499, 15508, 15518, 15527, 15537, 15547, 15558, 15563, 15569, 15575, 15582, 15588, 15595, 15602, 15610, 15616, 15623, 15630, 15638, 15645, 15653, 15661, 15670, 15676, 15683, 15690, 15698, 15705, 15713, 15721, 15730, 15737, 15745, 15753, 15762, 15770, 15779, 15788, 15798, 15804, 15811, 15818, 15826, 15833, 15841, 15849, 15858, 15865, 15873, 15881, 15890, 15898, 15907, 15916, 15926, 15933, 15941, 15949, 15958, 15966, 15975, 15984, 15994, 16002, 16011, 16020, 16030, 16039, 16049, 16059, 16070, 16076, 16083, 16090, 16098, 16105, 16113, 16121, 16130, 16137, 16145, 16153, 16162, 16170, 16179, 16188, 16198, 16205, 16213, 16221, 16230, 16238, 16247, 16256, 16266, 16274, 16283, 16292, 16302, 16311, 16321, 16331, 16342, 16349, 16357, 16365, 16374, 16382, 16391, 16400, 16410, 16418, 16427, 16436, 16446, 16455, 16465, 16475, 16486, 16494, 16503, 16512, 16522, 16531, 16541, 16551, 16562, 16571, 16581, 16591, 16602, 16612, 16623, 16634, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 16646, 16648, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 16651, 16655, 16658, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 16662, 16664, 16666, 16668, 16670, 16672, 16674, 16676, 16678, 16680, 16682, 16684, 16686, -1, 16688, 16690, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 16692, 16694, -1, -1, -1, -1, 16697, 16699, -1, 16702, -1, 16704, 16707, 16709, -1, 16711, -1, -1, 16714, 16716, -1, -1, 16718, 16720, -1, 16722, 16724, 16726, 16728, 16730, 16732, 16734, 16736, 16739, -1, -1, -1, -1, 16742, 16745, 16748, 16751, -1, -1, 16754, -1, 16757, 16759, 16761, 16763, 16765, 16767, 16769, 16771, -1, -1, -1, -1, 16773, 16778, 16785, 16787, -1, -1, 16789, 16791, 16793, 16795, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 16799, 16801, 16804, 16807, 16809, 16815, 16817, 16819, 16821, 16823, 16826, 16829, 16833, 16835, 16838, 16840, 16843, 16846, 16850, 16852, 16855, 16857, 16860, 16863, 16867, 16870, 16874, 16877, 16881, 16884, 16887, 16890, 16893, 16895, 16897, 16899, 16901, 16903, 16905, 16907, 16909, 16911, -1, -1, -1, -1, -1, -1, 16913, 16915, 16917, 16919, 16921, -1, 16923, 16925, 16930, 16935, 16940, -1, -1, -1, -1, -1, -1, 16945, 16947, 16949, 16951, 16953, 16955, 16957, 16959, 16961, 16963, 16965, 16968, 16971, 16974, 16977, 16980, 16983, 16986, 16989, 16992, 16995, 16998, 17001, 17005, 17008, 17011, 17014, 17017, 17020, 17023, 17026, 17029, 17032, 17036, 17039, 17043, 17047, 17050, 17053, 17056, 17059, 17062, 17065, 17068, 17071, 17074, 17077, 17080, 17083, 17086, 17089, 17092, 17095, 17098, 17101, 17104, 17107, 17110, 17113, 17116, 17119, 17122, 17125, 17128, 17131, 17134, 17137, 17140, 17143, 17146, 17149, 17152, 17155, 17158, 17161, 17164, 17167, 17170, 17173, 17176, 17179, 17182, 17185, 17187, -1, -1, -1, -1, -1, -1, -1, -1, 17190, 17192, -1, -1, 17194, 17196, 17198, 17200, 17202, 17204, 17206, 17208, 17210, 17212, 17214, 17217, 17220, 17223, 17226, 17229, 17232, 17235, 17238, 17241, 17244, 17247, 17250, 17254, 17257, 17260, 17263, 17266, 17269, 17272, 17275, 17278, 17281, 17285, 17288, 17292, 17296, 17299, 17302, 17305, 17308, 17311, 17314, 17317, 17320, 17323, 17326, 17329, 17332, 17335, 17338, 17341, 17344, 17347, 17350, 17353, 17356, 17359, 17362, 17365, 17368, 17371, 17374, 17377, 17380, 17383, 17386, 17389, 17392, 17395, 17398, 17401, 17404, 17407, 17410, 17413, 17416, 17419, 17422, 17425, 17428, 17431, 17434, 17436, 17439, 17442, 17445, 17448, 17451, 17454, -1, -1, 17457, 17459, -1, -1, -1, -1, -1, -1, 17461, 17463, 17465, 17467, 17469, 17471, 17473, 17475, 17477, 17479, 17481, 17483, 17485, 17487, 17489, 17492, 17495, 17498, 17500, 17502, 17504, 17506, 17508, 17510, 17512, 17515, 17518, 17521, 17524, 17527, 17530, 17533, 17537, 17541, 17544, 17546, 17548, 17551, 17553, 17556, -1, -1, -1, -1, 17559, 17561, 17564, 17567, 17569, 17572, 17575, 17577, 17580, 17582, 17585, 17588, 17591, 17594, 17597, 17600, 17603, 17605, 17607, 17610, 17613, 17615, -1, 17618, 17620, 17623, 17625, 17627, 17629, 17631, 17633, 17635, 17638, 17641, 17645, 17648, 17650, 17654, 17657, 17659, 17662, 17666, 17669, 17672, 17674, 17678, 17681, 17684, 17687, 17690, 17693, -1, 17695, 17698, 17701, 17704, 17707, 17711, 17714, 17718, 17721, 17724, 17727, 17730, 17733, 17736, -1, 17739, 17743, 17747, 17750, 17753, 17756, 17760, 17763, 17766, 17769, 17772, 17775, -1, 17777, 17779, 17782, 17785, 17788, 17791, 17793, 17799, 17806, 17811, 17818, 17824, 17829, 17831, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 17835, 17838, 17841, 17844, 17847, 17850, 17854, 17857, 17861, 17864, 17868, 17872, 17876, 17879, 17883, 17888, 17893, 17896, 17899, 17903, 17908, 17910, 17912, 17914, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 17916, 17920, 17924, 17928, 17932, 17936, 17940, 17944, 17947, 17951, 17955, 17959, 17963, 17967, 17971, 17976, 17981, 17986, 17991, 17996, 18001, 18006, 18010, 18015, 18020, 18025, 18030, 18035, 18040, -1, -1, -1, 18045, 18050, 18055, 18060, 18065, 18070, 18075, 18080, 18085, 18090, 18096, 18103, 18110, 18118, 18124, 18131, 18137, 18143, 18150, 18157, 18164, 18172, 18178, 18185, 18192, 18199, 18206, 18212, 18219, 18227, 18233, 18239, 18246, 18252, 18259, 18262, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 18265, 18268, 18271, 18274, 18277, 18280, 18283, 18286, 18289, 18292, 18295, 18298, 18301, 18304, 18307, 18310, 18314, 18318, 18322, 18326, 18330, 18334, 18338, 18341, 18345, 18349, 18353, 18357, 18361, 18365, 18370, 18375, 18380, 18385, 18390, 18395, 18400, 18404, 18409, 18414, 18419, 18424, 18429, -1, -1, -1, 18434, 18439, 18444, 18449, 18454, 18459, 18464, 18469, 18474, 18479, 18484, 18490, 18497, 18504, 18512, 18518, 18525, 18531, 18537, 18544, 18551, 18558, 18566, 18572, 18579, 18586, 18593, 18599, 18606, 18612, 18619, 18626, 18633, 18640, 18649, 18656, 18663, 18672, 18681, 18690, 18697, 18704, 18711, 18717, 18725, 18732, 18740, 18746, 18752, 18759, 18765, 18768, 18771, 18774, 18777, 18780, 18783, 18786, 18789, 18792, 18795, 18798, 18801, 18804, 18807, 18810, 18813, 18816, 18819, 18822, 18825, 18828, 18831, 18834, 18837, 18841, 18845, 18849, 18852, 18856, 18859, 18863, 18865, 18867, 18869, 18871, 18873, 18876, 18879, 18882, 18885, 18888, 18891, 18894, 18897, 18900, 18903, 18906, 18909, 18912, 18915, 18918, 18921, 18924, 18927, 18930, 18933, 18936, 18939, 18942, 18945, 18948, 18951, 18954, 18957, 18960, 18963, 18966, 18969, 18972, 18975, 18978, 18981, 18984, 18987, 18990, 18993, 18996, -1, 18999, 19009, 19015, 19022, 19026, 19033, 19038, 19042, 19049, 19054, 19060, 19064, 19071, 19077, 19085, 19092, 19098, 19103, 19110, 19116, 19124, 19129, 19138, 19148, 19157, 19162, 19171, 19180, 19186, 19191, 19198, 19204, 19210, 19218, 19227, 19233, 19238, 19244, 19249, 19256, 19260, 19265, 19270, 19278, 19286, 19292, 19299, 19307, 19313, 19318, 19327, 19333, 19338, 19345, 19351, 19359, 19364, 19372, 19378, 19384, 19389, 19394, 19400, 19405, 19409, 19415, 19420, 19425, 19431, 19436, 19441, 19446, 19454, 19461, 19467, 19476, 19481, 19489, 19495, 19500, 19505, 19510, 19516, 19521, 19527, 19533, 19537, 19546, 19551, 19554, 19557, 19560, 19563, 19566, 19569, 19572, 19575, 19578, 19581, 19585, 19589, 19593, 19597, 19601, 19605, 19609, 19613, 19617, 19621, 19625, 19629, 19633, 19637, 19641, 19645, 19648, 19651, 19655, 19658, 19661, 19664, 19669, 19674, 19677, 19684, 19691, 19699, 19705, 19710, 19713, 19716, 19719, 19722, 19725, 19728, 19731, 19734, 19738, 19743, 19746, 19749, 19752, 19755, 19758, 19761, 19764, 19768, 19772, 19776, 19780, 19783, 19786, 19789, 19792, 19795, 19798, 19801, 19804, 19807, 19810, 19815, 19820, 19824, 19829, 19834, 19839, 19843, 19848, 19852, 19858, 19861, 19865, 19869, 19873, 19877, 19883, 19891, 19894, 19897, 19900, 19903, 19906, 19909, 19912, 19915, 19918, 19921, 19924, 19927, 19930, 19933, 19936, 19939, 19944, 19949, 19954, 19957, 19960, 19963, 19968, 19972, 19975, 19978, 19981, 19984, 19987, 19992, 19995, 19998, 20001, 20004, 20008, 20011, 20014, 20018, 20022, 20025, 20030, 20034, 20037, 20040, 20043, 20046, 20050, 20054, 20057, 20060, 20063, 20066, 20069, 20072, 20075, 20078, 20081, 20085, 20089, 20093, 20097, 20101, 20105, 20109, 20113, 20117, 20121, 20125, 20129, 20133, 20137, 20141, 20145, 20149, 20153, 20157, 20161, 20165, 20169, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 20173, 20177, 20183, 20188, 20192, 20199, -1, 20204, 20208, 20215, 20220, 20227, 20232, 20236, 20240, 20244, 20250, 20255, 20261, 20267, 20274, 20279, 20283, 20288, 20293, 20298, 20304, 20308, 20314, 20320, 20324, 20331, 20336, 20341, 20348, 20353, 20358, 20365, 20370, 20376, 20382, 20387, 20392, 20396, 20400, 20407, 20414, 20418, 20423, 20429, 20435, 20442, 20448, 20453, 20459, 20464, 20468, 20473, 20478, 20483, 20488, 20494, 20498, 20502, 20507, 20511, 20515, 20519, 20524, 20531, 20536, 20541, 20546, 20551, -1, 20555, 20560, 20564, 20569, 20573, 20577, 20581, 20588, 20594, 20600, 20606, 20610, 20616, 20623, 20630, 20634, -1, 20639, 20644, 20649, 20653, 20657, 20661, 20668, 20673, 20678, -1, 20683, 20688, 20694, 20699, 20703, 20709, 20714, 20719, -1, 20724, 20728, 20733, 20739, 20744, 20748, 20753, 20758, 20762, 20767, 20771, 20776, 20781, 20785, -1, 20791, 20796, 20801, 20806, 20811, 20817, 20822, 20826, 20831, 20837, 20841, 20845, 20852, 20857, 20862, 20866, 20871, 20875, 20879, 20883, 20888, 20892, 20896, 20900, 20906, 20910, 20915, 20920, 20925, 20929, 20934, 20938, 20942, 20946, 20950, 20955, 20961, 20967, 20971, 20977, 20982, 20986, 20992, 20998, -1, 21002, 21009, 21015, 21021, 21028, 21035, 21041, 21045, 21050, 21054, 21059, 21064, 21069, 21075, 21080, 21085, 21090, 21095, 21099, 21103, 21109, 21113, 21118, 21123, 21127, 21133, 21137, 21142, 21146, 21152, 21156, 21163, 21168, 21173, 21179, 21185, 21191, 21195, 21200, 21205, 21210, 21216, 21220, 21225, 21229, 21236, 21240, 21246, 21252, 21257, 21263, 21269, 21274, 21280, 21285, 21289, 21293, 21298, 21304, 21308, 21314, 21321, -1, 21325, 21329, 21333, 21338, 21343, 21348, 21354, 21360, 21366, 21373, 21377, 21382, 21386, 21392, 21397, 21402, 21408, 21414, 21419, 21425, 21430, 21434, 21440, 21447, 21451, 21456, 21460, 21466, 21471, 21476, 21480, 21485, 21491, 21495, 21499, 21503, 21507, 21511, 21515, 21519, 21524, 21529, 21534, 21539, 21543, 21549, 21556, 21561, 21566, 21571, 21575, 21580, 21587, 21592, 21597, 21604, 21609, 21613, 21619, 21626, 21633, 21638, 21644, 21649, 21654, 21659, -1, 21664, 21670, 21674, 21678, 21682, 21686, 21690, 21695, 21700, 21704, 21710, 21716, 21722, 21726, 21730, 21736, 21740, 21745, 21749, 21753, 21757, 21761, 21768, 21774, 21779, 21784, 21789, 21794, 21798, 21802, 21806, 21812, 21816, 21821, 21825, 21830, 21835, 21840, 21846, 21850, 21856, 21860, 21864, 21869, 21874, 21878, 21882, 21886, 21891, 21896, 21902, 21908, 21912, 21918, 21922, 21928, 21932, -1, 21938, -1, 21942, 21947, 21953, 21957, 21962, 21969, 21973, 21979, 21984, 21988, 21994, 21999, 22003, 22009, 22013, 22018, 22023, 22028, 22034, 22040, 22046, 22050, 22054, 22059, 22065, 22071, 22076, 22081, 22086, 22091, 22096, 22100, 22105, 22110, 22117, 22121, 22126, 22131, 22135, 22140, 22144, 22149, 22153, 22157, 22162, 22166, 22171, 22177, 22182, 22187, 22192, 22198, 22204, 22208, 22212, 22218, 22223, 22228, 22233, 22237, 22241, 22247, 22253, 22257, 22263, 22269, 22275, 22281, 22287, 22292, 22296, 22301, 22308, 22315, 22321, 22326, 22332, 22337, 22341, 22345, 22350, 22356, 22362, 22367, 22371, 22376, 22381, 22387, 22392, 22396, 22400, 22404, 22407, 22412, 22416, 22423, 22427, 22431, 22436, 22440, 22444, 22450, 22454, 22460, 22464, 22470, 22475, 22481, 22487, 22492, 22497, 22501, 22505, 22509, 22513, 22517, 22523, 22528, 22532, 22536, 22541, 22546, 22551, 22555, 22559, 22564, -1, 22570, 22575, 22580, 22584, -1, 22589, 22595, 22600, 22604, 22608, 22612, 22618, 22623, 22629, 22635, 22640, 22645, 22651, 22655, 22659, 22664, 22668, 22673, 22677, 22683, 22690, 22695, 22701, 22706, 22711, 22716, 22721, 22726, 22731, 22737, 22743, 22747, 22753, 22758, 22764, 22769, 22774, 22779, 22784, 22789, 22794, 22800, 22806, 22812, 22818, 22823, 22827, 22831, 22835, 22840, 22846, 22851, 22856, 22863, 22868, 22874, 22879, 22885, 22891, 22897, 22901, 22907, 22911, 22916, 22922, 22927, 22932, 22938, 22944, 22949, 22955, 22959, 22964, 22968, 22972, 22977, 22982, 22988, 22994, 22998, 23003, 23008, -1, 23014, 23020, 23027, 23033, 23038, 23044, 23051, 23057, 23062, 23067, 23072, 23076, 23082, 23087, 23091, 23096, 23102, 23107, 23113, 23119, 23123, 23126, 23131, 23136, 23143, 23148, 23152, 23157, 23162, 23168, 23173, 23177, 23184, 23189, 23195, 23201, 23206, 23211, 23217, 23221, 23226, 23230, 23234, 23238, 23243, 23249, 23255, 23259, 23263, 23268, 23273, 23277, 23283, 23287, 23293, 23297, 23302, 23307, 23312, 23316, 23320, 23325, 23330, 23335, 23342, 23347, 23352, 23357, 23361, 23366, 23370, 23376, 23382, 23386, 23390, 23395, 23400, 23404, 23410, 23416, 23422, 23427, 23432, 23436, 23442, 23448, 23453, 23459, 23464, 23469, 23474, 23480, 23486, 23491, 23496, 23501, 23507, 23511, 23517, 23522, 23527, 23532, -1, 23538, 23543, 23548, 23553, 23558, 23563, 23568, 23574, 23579, 23585, 23591, 23597, 23601, 23608, 23612, 23618, 23623, 23630, 23637, 23643, 23648, 23653, 23658, 23665, 23671, 23676, 23680, 23684, 23688, 23693, 23699, 23704, 23710, 23714, 23719, 23725, 23731, 23736, 23742, 23748, 23755, 23762, 23768, 23774, 23781, 23786, 23791, 23795, 23799, 23804, 23811, 23818, 23824, 23828, 23834, 23840, 23845, 23849, 23854, 23859, 23866, 23872, 23877, 23883, 23888, 23894, 23899, 23905, 23910, 23913, 23919, 23925, 23931, 23936, 23940, -1, 23944, 23948, 23954, 23961, 23966, 23970, 23975, 23980, 23986, 23990, 23996, 24002, 24006, 24011, 24015, 24020, 24026, 24032, 24036, 24042, 24048, 24054, 24060, 24066, 24071, 24076, 24082, 24087, -1, 24092, 24097, 24101, 24105, 24110, 24116, 24122, 24127, 24131, 24136, 24142, 24148, 24152, 24158, 24165, 24169, 24176, 24181, 24186, 24190, 24197, 24202, 24206, 24212, 24217, 24222, 24226, 24231, 24236, 24242, 24248, 24252, 24258, 24263, 24269, 24274, 24280, 24286, 24291, 24296, 24300, 24305, 24310, 24315, 24321, 24326, 24331, 24337, 24341, 24345, 24350, 24355, 24361, 24368, 24375, 24381, 24388, 24394, 24401, 24406, 24410, 24415, 24421, 24425, 24432, 24436, 24441, 24445, 24450, 24454, 24458, 24463, 24468, 24473, 24478, 24484, 24490, 24495, 24500, 24505, 24511, 24516, 24521, 24527, 24531, 24538, 24542, 24548, 24553, 24559, 24566, 24570, 24574, 24580, 24585, -1, 24589, 24594, 24600, 24606, 24612, 24618, 24624, 24628, 24632, 24638, 24642, 24649, 24655, 24661, 24667, 24671, 24675, 24679, 24685, 24692, 24697, 24702, 24707, 24713, 24718, 24724, 24729, 24733, 24740, 24744, 24749, 24754, 24761, 24766, 24772, 24777, 24782, 24786, 24792, 24796, 24802, 24807, 24812, 24817, 24822, 24828, 24832, 24836, 24841, 24847, 24853, 24860, 24864, 24870, 24876, 24881, 24886, 24890, 24895, 24899, 24905, 24911, 24916, 24922, 24928, 24934, 24938, 24945, 24950, 24954, 24961, 24965, 24971, 24976, 24980, 24985, 24991, 24997, 25002, 25007, 25013, 25019, 25024, 25029, 25033, 25037, 25041, 25047, 25053, 25059, 25066, 25072, 25078, 25084, 25090, 25095, 25101, 25106, 25111, 25115, 25119, 25125, 25131, 25138, 25143, 25147, 25152, 25157, 25162, 25167, 25173, 25177, 25181, 25185, 25190, 25195, 25200, 25206, 25210, 25215, 25221, 25227, 25232, 25236, 25240, 25246, 25252, 25258, 25263, 25270, 25275, 25280, 25286, 25292, 25296, 25303, 25309, 25313, 25317, 25322, 25326, 25332, 25337, 25342, 25347, 25352, 25358, 25363, 25368, 25373, 25381, 25386, 25391, 25395, 25399, 25404, 25410, 25415, 25420, 25425, 25429, 25434, 25440, 25445, 25450, 25455, 25459, 25464, 25469, 25474, 25478, 25484, 25492, 25496, 25501, 25505, 25509, 25515, 25520, 25526, 25533, 25538, 25543, 25548, 25553, 25558, 25562, 25568, 25573, 25579, 25585, 25590, 25595, 25600, 25608, 25613, 25617, 25622, 25626, 25631, 25637, 25643, 25648, 25652, 25656, 25661, 25666, 25670, 25675, 25680, 25685, 25689, 25693, 25699, 25704, 25709, 25713, 25719, 25724, 25729, 25733, 25737, 25743, 25748, 25756, 25761, 25767, 25772, 25776, 25780, 25785, 25790, 25796, 25800, 25808, 25812, 25818, 25823, 25827, 25831, 25836, 25842, 25847, 25853, 25859, 25863, 25867, 25872, 25876, 25881, 25888, 25894, 25899, 25905, 25909, 25914, 25918, 25925, 25931, 25935, 25940, 25945, 25953, 25959, 25965, 25971, 25975, 25979, 25984, 25990, 25996, 26001, 26007, 26012, 26018, 26023, 26028, 26033, 26039, 26045, 26049, 26053, 26058, 26063, 26069, 26074, 26080, 26086, 26092, 26097, 26101, 26107, 26111, 26117, 26122, 26128, 26134, 26138, 26142, 26147, 26151, 26155, 26161, 26166, 26172, 26177, 26181, 26186, 26191, 26196, 26202, 26207, 26212, 26218, 26223, 26227, 26232, 26236, 26242, 26246, 26251, 26257, 26261, 26267, 26272, 26277, 26283, 26288, 26294, 26298, 26304, 26309, 26316, 26321, 26326, 26330, 26335, 26339, 26345, 26350, 26354, 26359, 26364, 26370, 26376, 26382, 26388, 26392, 26397, 26403, 26409, 26413, 26418, 26424, 26429, 26433, 26438, 26444, 26450, 26454, 26458, 26463, 26467, 26471, 26476, 26482, 26489, 26494, 26498, 26502, 26508, 26513, 26517, 26522, 26529, 26535, 26541, 26547, 26551, 26556, 26561, 26567, 26573, 26577, 26582, 26587, 26592, 26597, 26602, 26606, 26610, 26615, 26621, 26627, 26632, 26638, 26643, 26648, 26653, 26658, 26662, 26667, 26673, 26678, 26683, 26688, 26693, -1, 26699, 26706, 26712, 26716, 26721, 26725, 26729, 26733, 26738, 26742, 26748, 26752, 26757, 26764, 26768, 26773, 26778, 26783, 26788, 26794, 26799, 26803, 26807, 26814, 26820, 26827, 26834, 26838, 26843, 26847, 26853, 26858, 26863, 26869, 26876, 26881, 26885, 26890, 26895, 26900, 26905, 26910, 26915, 26921, 26927, 26933, 26937, 26942, 26946, 26950, 26954, 26958, 26962, 26967, 26972, 26978, 26982, 26986, 26991, 26996, 27002, 27008, 27012, 27016, 27023, 27027, 27032, 27037, 27042, 27046, 27051, 27056, 27061, 27066, 27071, 27075, 27081, 27086, 27091, 27096, 27101, 27106, 27110, 27114, 27121, 27125, 27132, 27138, 27142, 27148, 27153, 27157, 27162, 27166, 27170, 27175, 27179, 27184, 27189, 27194, 27199, 27204, 27211, 27216, 27221, 27226, 27231, 27237, 27241, 27246, 27252, 27257, 27263, 27268, 27272, 27277, 27282, 27286, 27290, 27293, 27299, -1, 27303, 27308, 27314, 27317, 27323, 27327, 27332, 27337, 27341, 27345, 27349, 27354, 27359, 27364, 27369, -1, 27375, 27379, 27383, 27388, 27392, 27398, 27402, 27406, 27411, 27416, 27422, 27427, 27432, 27437, 27443, 27447, 27453, 27460, 27465, 27469, 27474, 27479, 27485, 27490, 27495, 27500, 27504, 27509, 27514, 27518, 27522, 27529, 27534, 27538, 27543, 27548, 27554, 27558, 27564, 27569, 27574, 27578, 27582, 27586, 27591, 27595, 27601, 27608, 27613, 27618, 27623, 27628, 27633, 27637, 27642, 27647, 27652, 27656, 27661, 27669, 27674, 27680, 27686, 27690, 27694, 27699, 27704, 27709, 27715, 27719, 27725, 27731, 27735, 27740, 27745, 27750, 27755, 27760, 27765, 27770, 27775, 27781, 27786, 27790, 27794, 27800, 27805, 27810, 27815, 27820, 27826, 27832, 27836, 27842, 27846, 27851, 27856, 27861, 27866, 27871, 27875, 27880, 27885, 27889, 27894, 27899, 27903, 27909, -1, 27913, 27919, 27924, 27928, 27934, 27939, 27943, 27949, 27954, 27958, 27962, 27966, 27972, 27977, 27983, 27989, 27994, 27998, 28002, 28009, 28014, 28019, 28023, 28027, 28030, 28034, 28038, 28043, 28047, 28052, 28058, 28063, 28068, 28073, 28078, 28082, 28087, 28092, 28097, 28101, 28106, 28110, 28115, 28120, 28123, 28128, 28134, 28140, 28146, 28150, 28156, 28161, 28167, 28171, 28175, 28181, 28185, 28190, 28194, 28200, 28205, 28210, 28215, 28219, 28223, 28228, 28232, 28237, 28241, 28244, 28248, 28253, 28257, 28261, 28268, 28272, 28277, 28281, 28287, 28293, 28298, 28305, 28309, 28314, 28318, 28322, 28327, 28331, 28335, 28340, 28346, -1, 28352, 28359, 28363, 28366, 28371, 28377, 28382, 28387, 28391, 28394, 28400, 28405, 28410, 28414, 28420, 28425, 28431, 28436, 28440, 28444, 28448, 28454, 28458, 28462, 28466, 28471, 28475, 28479, 28484, 28489, 28494, 28498, 28504, 28510, 28514, 28520, 28524, 28528, 28532, 28537, 28541, 28546, 28551, 28556, 28561, 28565, 28569, 28575, 28579, 28584, 28588, 28593, 28598, 28604, 28608, 28613, 28619, 28623, 28627, 28631, 28635, 28640, 28644, 28650, 28656, 28662, 28667, 28671, 28677, 28682, 28686, 28690, 28693, 28700, 28705, 28709, 28713, 28718, 28722, 28727, 28731, 28736, 28741, 28745, 28751, 28755, 28762, 28767, 28773, 28777, 28782, 28787, 28793, 28798, 28804, 28807, 28813, 28818, 28823, 28828, 28833, 28837, 28841, 28846, 28852, 28858, 28864, 28871, 28876, 28880, 28884, 28889, 28893, 28897, 28902, 28909, 28913, 28918, 28925, 28929, 28933, 28939, 28943, -1, 28948, 28953, 28960, 28966, -1, 28971, 28975, 28981, 28986, 28990, 28995, 29001, 29007, 29012, 29018, 29022, 29025, 29030, 29036, 29040, 29044, 29048, 29052, 29056, 29062, 29068, 29073, 29077, 29082, 29087, 29094, 29098, 29103, 29107, 29112, 29117, 29121, 29126, 29132, 29138, 29144, 29148, 29153, 29158, 29162, 29166, 29171, 29176, 29181, 29187, 29191, 29195, 29200, 29204, 29210, 29216, 29220, 29224, 29230, 29235, 29240, 29245, 29249, 29254, 29258, 29263, 29267, 29271, 29276, 29280, 29285, 29291, 29296, 29301, 29306, 29313, 29318, 29323, 29329, 29335, 29340, 29344, 29349, 29353, 29359, 29364, 29368, 29372, 29378, 29383, 29389, 29396, 29400, 29404, 29411, 29415, 29420, 29424, 29430, 29434, 29440, 29445, 29450, 29454, 29459, 29463, 29467, 29473, 29478, 29483, 29489, 29495, 29502, 29506, 29512, 29517, 29520, 29526, 29533, 29538, 29542, 29547, 29552, 29557, 29561, 29565, 29569, 29574, 29579, 29584, 29588, 29592, 29596, 29601, 29607, 29611, 29615, 29619, 29624, 29629, 29636, 29642, 29646, 29651, 29655, 29659, 29665, 29670, 29674, 29679, 29683, 29690, 29695, 29700, 29705, 29710, 29714, 29720, 29725, 29729, 29735, 29741, -1, 29747, 29753, 29758, 29762, 29766, 29771, 29776, 29780, 29786, 29791, 29796, 29802, 29806, 29811, 29815, 29820, 29825, 29830, 29835, 29841, 29847, 29852, 29856, 29860, 29865, 29870, 29875, 29879, 29885, 29889, 29894, 29901, 29906, 29912, 29918, 29922, 29926, 29930, 29935, 29942, 29947, 29952, 29956, 29963, 29967, 29972, 29978, 29985, 29989, 29995, 30000, 30006, 30011, 30016, 30021, 30026, 30030, 30034, 30039, 30044, 30050, 30056, 30061, 30067, 30072, 30076, 30080, 30085, 30090, 30094, 30099, 30104, 30108, 30113, 30117, 30120, 30125, 30132, 30136, 30142, 30146, 30152, 30157, 30161, 30166, 30170, 30175, 30180, 30184, 30188, 30194, 30200, 30204, 30209, 30213, 30217, 30223, 30227, 30233, 30238, 30242, 30246, 30251, 30256, 30262, 30266, 30270, 30275, 30280, 30285, 30290, 30295, 30300, 30305, 30310, 30314, 30319, 30324, 30328, 30332, 30336, 30341, -1, 30345, 30351, 30355, 30359, 30363, 30369, 30374, 30379, 30383, 30388, 30393, 30398, 30402, 30406, 30412, 30419, 30425, 30430, 30435, 30440, 30445, 30450, 30454, 30460, 30466, 30471, 30475, 30479, 30485, 30489, 30494, 30499, 30503, 30509, 30513, 30520, 30524, 30528, 30532, 30536, 30540, 30544, 30549, 30553, 30557, 30563, 30567, 30573, 30579, 30585, 30591, 30595, 30601, 30606, 30611, 30616, 30621, 30627, 30632, 30638, 30642, 30647, 30653, 30658, 30663, 30669, 30673, 30678, 30682, 30688, 30694, 30698, 30703, 30708, 30713, 30717, 30722, 30725, 30730, 30735, 30740, 30745, 30750, 30758, 30762, 30767, 30771, 30776, 30783, 30789, 30793, 30797, 30801, 30806, 30812, 30816, 30822, 30827, 30832, 30836, 30841, 30846, 30851, 30856, 30860, 30864, 30869, 30875, 30879, 30884, 30888, 30894, 30899, 30905, 30910, 30915, 30919, 30922, 30927, 30931, 30936, 30942, 30948, 30952, 30958, 30963, 30969, 30973, 30978, 30983, 30987, 30992, 30997, 31003, 31008, 31013, 31017, 31021, 31026, 31031, 31037, 31041, 31046, 31050, 31055, 31059, 31064, 31069, 31073, 31078, 31082, 31088, 31092, 31097, 31102, 31106, -1, -1, 31113, 31117, 31121, 31125, 31130, 31134, 31139, 31144, 31149, 31153, 31157, 31164, 31169, 31173, 31178, 31184, 31190, 31196, 31200, 31205, 31209, 31213, 31219, 31223, 31228, 31234, 31239, 31243, 31250, 31255, 31259, 31263, 31268, 31272, 31277, 31283, 31289, 31295, 31301, 31305, 31309, 31315, 31321, 31327, 31333, 31338, 31345, 31350, 31356, 31360, 31364, 31370, 31376, 31380, 31384, 31389, 31395, 31400, 31404, 31408, 31412, 31418, 31422, 31427, 31432, 31437, -1, -1, 31443, 31448, 31453, 31458, 31463, 31467, 31472, 31478, 31483, 31488, 31493, 31498, 31504, 31510, 31515, 31521, 31527, 31534, 31540, 31545, 31551, 31555, 31560, 31566, 31571, 31576, 31581, 31587, 31592, 31596, 31601, 31606, 31611, 31616, 31620, 31626, 31630, 31635, 31640, 31645, 31649, 31654, 31659, 31664, 31669, 31673, 31677, 31682, 31688, 31693, 31699, 31703, 31709, 31714, 31721, 31726, 31732, 31738, 31742, 31746, 31752, 31758, 31761, 31765, 31769, 31773, 31779, 31784, 31789, 31793, 31799, 31805, 31811, 31815, 31819, 31823, 31829, 31836, 31840, 31844, 31849, 31853, 31858, 31863, 31867, 31871, 31875, 31880, 31884, 31888, 31892, 31898, 31904, 31910, 31916, 31922, 31927, 31935, 31941, 31948, 31955, 31959, 31964, 31968, 31972, 31977, 31982, 31987, 31992, 31997, 32002, 32007, 32012, 32017, 32023, 32028, 32034, 32039, 32044, 32049, 32054, 32058, 32063, 32069, 32073, 32079, 32083, 32089, 32093, 32099, 32103, 32109, 32115, 32120, 32124, 32128, 32132, 32137, 32143, 32147, 32152, 32158, 32163, 32167, 32172, 32177, 32182, 32187, 32193, 32199, 32204, 32208, 32213, 32217, 32224, 32230, 32234, 32238, 32243, 32248, 32252, 32256, 32261, 32266, 32270, 32275, 32280, 32286, 32291, 32295, 32299, 32304, 32310, 32314, 32320, 32326, 32330, 32334, 32339, -1, 32344, 32350, 32356, 32360, 32364, 32368, 32372, 32377, 32382, 32387, 32392, 32397, 32402, 32408, 32412, 32419, 32424, 32429, 32434, 32439, 32444, 32450, 32455, 32459, 32464, 32470, 32475, 32480, 32485, 32492, 32498, 32501, 32505, 32511, 32517, 32521, -1, 32526, 32531, 32537, 32542, 32548, 32553, 32557, 32563, 32569, 32575, 32581, 32586, 32590, 32594, 32599, 32604, 32608, 32614, 32619, 32625, 32629, 32635, 32641, 32646, 32652, 32657, 32662, 32666, 32671, 32677, 32683, 32690, 32695, 32700, 32705, 32710, 32715, 32719, 32726, 32732, 32736, 32741, 32746, 32751, 32758, 32762, 32768, 32772, 32776, 32780, 32784, 32790, 32795, 32799, 32806, 32811, 32815, 32820, 32827, 32833, 32840, 32846, 32851, 32856, 32860, 32865, 32870, 32876, 32881, 32886, 32891, 32895, 32900, 32904, 32909, 32913, 32919, 32923, 32928, 32932, 32936, 32940, 32944, 32950, 32955, 32959, 32965, 32970, 32975, 32981, 32985, 32988, 32994, 32998, 33002, -1, 33008, 33012, -1, 33018, -1, 33022, 33027, 33032, 33036, 33043, 33048, 33053, 33057, 33063, 33068, 33073, 33080, 33085, 33090, 33095, 33099, 33103, 33108, 33113, 33119, 33122, 33128, 33133, 33139, 33143, 33150, 33153, 33158, 33163, 33168, 33172, 33177, 33184, 33188, 33193, 33198, 33204, 33211, 33216, 33222, 33228, 33234, 33241, 33247, 33254, 33262, 33269, 33276, 33282, -1, 33288, 33294, 33298, 33302, 33308, 33314, 33318, 33325, 33331, 33336, 33340, 33344, 33349, 33353, 33357, 33363, 33370, 33374, 33380, 33385, 33392, 33396, 33402, 33408, 33413, 33419, 33424, 33430, 33434, 33441, 33446, 33450, 33456, 33460, 33466, 33471, 33476, 33482, 33488, 33493, 33498, 33503, 33508, 33514, 33521, 33527, 33533, 33539, 33543, 33547, 33551, 33557, 33562, 33566, 33573, 33581, 33586, 33591, 33596, 33601, 33607, 33613, 33617, 33622, 33626, 33633, 33638, 33644, 33650, 33656, 33661, 33665, 33670, 33677, 33682, 33686, 33693, 33697, 33702, 33707, 33711, 33717, 33721, 33728, -1, 33735, 33741, 33747, 33753, 33757, 33763, 33769, 33773, 33777, 33783, 33789, 33794, 33799, 33803, 33808, 33813, 33818, 33822, 33829, 33834, 33839, 33844, 33850, 33854, 33859, 33866, 33871, 33875, 33879, 33884, 33890, 33894, 33901, 33905, 33911, 33916, 33920, 33924, 33928, 33934, 33941, 33945, 33951, 33955, 33960, 33965, 33969, 33975, 33981, 33987, 33993, 33998, 34003, 34007, 34011, 34015, 34019, 34024, 34029, 34035, 34041, 34046, 34053, 34057, 34062, 34067, 34072, 34078, 34084, 34090, 34095, 34099, 34104, 34110, 34115, 34120, 34125, 34130, -1, 34134, 34139, -1, 34144, 34152, 34160, 34167, 34171, 34176, 34180, 34185, 34193, 34197, 34201, 34206, 34210, 34214, 34218, 34223, 34229, 34235, 34240, 34246, 34250, 34255, 34259, 34264, 34271, 34277, 34282, 34287, 34293, 34298, 34303, 34309, 34313, 34318, 34323, -1, 34330, 34335, 34340, 34345, 34349, 34354, 34360, 34365, 34369, 34374, 34379, 34383, 34389, 34394, 34399, 34404, 34408, 34414, 34420, 34425, 34430, 34436, 34440, 34444, 34450, 34455, 34459, 34465, 34470, 34476, 34482, 34487, 34492, 34497, 34502, 34507, 34512, 34516, 34520, 34525, 34530, 34535, 34540, 34547, 34552, 34557, 34561, 34565, 34571, 34577, 34581, 34586, 34591, 34597, 34601, 34606, 34612, 34617, 34622, -1, 34628, 34633, 34638, 34644, 34648, 34653, 34658, 34663, 34667, 34674, 34679, 34685, 34690, 34694, 34701, 34706, 34712, 34717, 34723, 34729, 34733, 34738, 34744, 34749, 34753, 34757, 34761, 34768, 34774, 34779, 34784, 34791, 34796, 34801, 34806, 34810, 34816, 34821, 34825, 34829, 34835, 34840, 34846, 34852, 34856, 34861, 34866, 34871, 34876, 34882, 34887, 34891, 34897, 34901, 34906, 34912, 34918, 34926, 34930, 34934, 34939, 34944, 34948, 34954, 34959, 34964, 34969, 34976, 34980, 34984, 34988, 34994, 35001, 35006, 35011, 35016, 35022, 35027, 35032, 35037, 35045, 35050, 35055, 35059, 35064, 35069, 35073, 35077, 35082, 35088, 35094, 35100, 35104, 35108, 35113, 35117, 35121, 35125, 35131, 35137, 35143, 35147, 35152, 35158, 35163, 35169, 35174, 35177, 35182, 35186, 35190, 35194, 35200, 35205, 35208, 35212, 35216, 35220, 35227, 35232, 35237, 35241, 35245, 35251, 35255, 35259, 35264, 35269, 35275, 35281, 35286, 35291, 35295, 35299, 35305, 35310, 35314, 35318, 35324, 35329, 35334, 35339, 35345, 35352, 35357, 35361, 35366, 35373, 35378, 35384, 35390, 35395, 35399, 35405, 35411, 35415, 35421, 35426, 35430, 35434, 35440, 35446, 35453, 35458, 35463, 35468, 35474, 35479, 35484, 35489, 35494, 35499, 35504, 35510, 35515, 35520, 35525, 35531, 35535, 35541, 35546, 35552, 35556, 35561, 35565, 35570, 35576, 35582, 35588, 35594, 35600, 35604, 35611, 35617, 35623, 35627, 35632, 35636, 35641, 35645, 35650, 35656, 35662, 35669, 35675, 35680, 35684, 35689, 35694, 35699, 35705, 35711, 35716, 35721, 35725, 35733, 35736, 35741, 35746, 35751, 35757, 35761, 35766, 35770, 35774, 35779, 35783, 35787, 35791, -1, 35795, 35801, 35805, 35809, 35815, 35819, 35825, 35831, 35837, 35844, 35848, 35852, 35855, 35858, 35863, 35869, 35875, 35880, 35884, 35888, 35893, 35899, 35904, 35910, 35916, 35921, 35925, 35930, 35934, 35937, 35941, 35946, 35951, 35956, 35961, 35967, 35971, 35977, 35983, 35988, 35994, 36000, 36006, 36012, 36016, 36020, 36025, 36029, 36034, 36040, 36046, 36050, 36056, 36061, 36066, 36071, 36076, 36081, 36085, 36089, 36093, 36099, 36105, 36110, 36115, 36121, 36127, 36133, 36139, 36145, 36150, 36154, 36159, 36164, 36169, 36174, 36178, 36182, 36187, 36191, 36196, 36201, 36206, 36212, 36217, 36222, 36226, 36232, 36238, 36243, 36247, 36252, 36258, 36262, 36269, 36274, 36281, 36287, 36291, 36297, 36303, 36308, 36313, 36318, 36324, 36330, 36335, 36339, 36344, 36349, 36355, 36360, 36365, 36369, 36375, 36380, 36384, 36389, 36393, -1, 36397, 36402, 36406, 36411, 36416, 36421, 36426, 36430, 36434, 36440, 36446, 36452, 36456, 36461, 36466, 36471, 36476, 36480, 36484, 36489, 36494, 36499, 36504, 36509, 36515, 36521, 36526, 36532, 36538, 36544, 36548, 36552, 36558, 36564, 36569, 36575, 36581, 36586, 36591, 36595, 36599, 36604, 36608, 36614, 36618, 36623, 36629, 36634, 36638, 36645, 36651, 36657, 36661, 36665, 36669, 36674, 36679, 36684, 36691, 36698, 36702, 36707, 36712, 36717, 36721, 36726, 36730, 36734, 36738, 36743, 36750, 36754, 36759, 36763, 36767, 36773, 36778, 36784, 36789, 36795, 36801, 36805, 36811, 36817, 36822, 36827, 36833, 36838, 36842, 36846, 36851, 36855, 36860, 36865, 36869, 36874, 36880, 36886, 36891, 36896, 36901, 36907, 36911, 36916, 36922, 36926, 36931, 36935, 36941, 36946, 36953, 36959, 36965, 36970, 36976, 36982, 36986, 36991, 36995, 37002, 37008, 37012, 37018, 37024, 37029, 37033, 37037, 37042, 37048, 37052, 37057, 37062, 37067, 37073, 37078, 37084, 37088, 37094, 37100, 37106, 37110, 37116, 37122, 37126, 37130, 37135, 37141, 37146, 37151, 37159, 37165, 37171, 37178, 37183, 37189, 37195, 37203, 37210, 37216, 37220, 37224, 37230, 37235, 37240, 37245, 37249, 37254, 37259, 37264, 37270, 37275, 37279, 37283, 37288, 37293, 37297, 37302, 37306, 37312, 37317, 37323, 37327, 37332, 37337, 37341, 37345, 37349, -1, 37354, 37361, 37366, 37372, 37377, 37383, 37388, 37394, 37400, 37405, 37411, 37415, 37419, 37423, 37427, 37431, 37435, 37440, 37444, 37448, 37453, 37458, 37463, 37469, 37475, 37481, 37486, 37492, 37496, 37501, 37507, 37514, 37518, 37524, 37528, 37532, 37538, 37543, 37548, 37553, 37559, 37563, 37569, 37575, 37579, 37584, 37590, 37596, 37602, 37608, 37614, 37620, 37625, 37629, 37634, 37639, 37644, 37651, 37657, 37661, 37667, 37672, 37677, 37683, 37687, 37693, 37698, 37704, 37710, 37717, 37722, 37728, 37734, 37739, 37744, 37749, 37754, 37759, 37765, 37770, 37776, 37782, 37788, 37795, 37799, 37804, 37810, 37815, 37819, 37824, 37829, 37833, 37839, 37843, 37848, 37852, 37857, 37863, 37868, 37874, 37880, 37884, 37890, 37896, 37902, 37907, 37912, 37917, 37921, 37926, 37932, 37937, 37943, 37948, 37952, 37957, 37963, 37969, 37974, 37978, 37983, 37987, 37992, 37997, 38001, 38005, 38011, 38016, 38021, 38027, 38033, 38038, 38043, 38049, 38054, 38060, 38065, 38071, 38075, 38079, 38084, 38089, 38096, 38101, 38107, 38112, 38117, 38121, 38126, 38131, 38135, 38140, 38146, 38150, 38156, 38163, 38167, 38172, 38176, 38183, 38190, 38197, 38202, 38207, 38212, 38217, 38222, 38227, 38233, 38237, 38243, 38247, 38251, 38255, 38259, 38265, 38270, 38276, 38283, 38290, 38294, 38298, 38305, 38311, 38317, -1, 38323, 38329, 38335, 38340, 38346, 38352, 38357, 38363, 38369, 38375, 38381, 38385, 38390, 38395, 38400, 38407, 38412, 38417, 38421, 38426, 38431, 38436, 38441, 38446, 38451, 38456, 38461, 38465, 38470, 38475, 38481, 38485, 38489, 38495, 38499, 38503, 38507, 38512, 38518, 38522, 38526, 38531, 38535, 38541, 38546, 38551, 38557, 38561, 38566, 38572, 38576, 38580, 38586, -1, 38590, 38595, 38598, 38603, 38609, 38613, 38618, 38623, 38628, 38632, 38636, 38640, 38644, 38650, 38654, 38658, 38663, 38667, 38672, 38678, 38683, 38688, 38692, 38697, 38702, 38706, -1, 38712, 38718, 38722, 38727, -1, 38733, 38739, 38743, 38747, 38752, 38756, 38761, 38767, 38771, 38776, 38781, -1, 38785, 38790, 38795, 38800, 38805, 38809, 38813, 38818, 38821, 38825, 38831, 38836, 38842, 38846, 38851, 38857, 38861, 38866, 38871, 38875, 38881, 38887, 38891, 38896, 38901, 38905, 38910, 38915, 38921, 38925, 38931, 38935, 38940, 38945, 38950, 38954, 38960, 38964, 38969, 38973, 38978, 38983, 38989, 38994, 38999, 39004, 39009, 39016, 39021, 39025, 39029, 39033, 39038, 39042, 39048, 39054, 39059, 39065, 39071, 39076, 39081, 39086, 39091, 39097, 39102, 39106, 39112, 39116, 39120, 39125, 39128, 39132, 39137, 39142, 39146, 39150, 39154, 39159, 39165, 39171, 39175, 39179, 39184, 39188, 39193, 39197, 39202, 39206, 39210, 39214, 39220, 39225, 39230, 39236, 39241, 39245, 39250, 39256, 39263, 39269, 39275, 39278, 39281, 39285, 39289, 39293, 39299, 39304, 39310, 39316, 39321, 39325, 39330, 39336, 39342, 39347, 39353, 39360, 39364, 39370, 39375, 39380, 39385, 39390, 39394, 39400, 39405, 39410, 39415, 39421, 39426, 39432, 39439, 39446, 39450, 39455, 39460, 39464, 39469, 39473, 39478, 39483, 39488, 39492, 39496, 39501, 39505, 39509, 39515, 39520, 39525, 39531, 39536, 39541, 39548, 39553, 39558, 39564, 39569, 39576, 39581, 39586, 39590, 39595, 39601, 39606, 39612, 39616, 39621, 39627, 39633, 39637, -1, 39642, 39646, 39653, 39657, 39662, 39668, 39674, 39679, 39684, 39688, 39692, 39697, 39702, 39707, 39712, 39715, 39720, 39725, 39729, 39734, 39739, 39744, 39748, 39752, 39758, 39764, 39771, 39776, 39782, 39786, 39790, 39796, 39801, 39806, 39811, 39816, 39821, 39827, 39832, 39837, 39843, 39854, 39859, 39863, 39873, 39878, 39882, 39887, 39891, 39897, 39903, 39909, 39915, 39920, 39924, 39929, 39933, 39939, 39945, 39949, 39954, 39960, 39966, 39971, 39977, 39983, 39988, 39993, -1, 39997, 40002, 40008, 40014, 40021, 40027, 40033, 40039, 40044, 40049, 40055, 40059, 40064, 40071, 40077, 40083, 40087, 40092, 40096, 40103, 40108, 40113, 40118, 40123, 40127, 40131, 40135, 40141, 40148, 40154, 40158, 40163, 40169, 40174, 40179, 40185, 40190, 40196, 40202, 40207, 40213, 40218, 40223, 40229, 40234, 40239, 40245, 40250, 40255, 40261, 40265, 40270, 40275, 40279, 40283, 40287, 40290, 40296, 40300, 40305, 40309, 40313, 40317, 40321, -1, 40326, 40330, 40336, 40341, 40347, 40351, 40355, 40360, -1, 40365, 40370, 40374, 40379, 40385, 40391, 40397, 40403, 40407, 40411, 40417, 40423, 40428, 40433, 40438, 40443, 40449, 40455, 40461, 40467, 40473, -1, 40479, 40484, 40489, 40494, 40499, 40504, 40511, 40516, 40523, 40529, 40536, 40542, 40547, 40553, 40559, 40564, 40570, 40575, 40581, 40585, 40591, 40596, 40600, 40605, 40610, 40616, 40621, 40626, 40630, 40634, 40638, 40642, 40647, 40653, 40660, 40664, 40669, 40674, 40680, 40686, 40691, 40696, 40700, 40704, 40708, 40713, 40717, 40723, 40730, 40735, 40740, 40745, 40750, 40754, 40759, 40763, 40767, 40772, 40777, 40784, 40790, 40795, 40799, 40804, 40808, 40813, 40817, 40823, 40828, 40834, 40838, 40843, 40847, 40851, 40855, 40859, 40865, 40870, 40876, 40880, 40887, 40892, 40899, 40905, 40911, 40917, 40922, 40927, 40931, 40937, 40942, 40947, 40953, 40959, 40966, 40971, 40976, 40981, 40986, 40993, 40998, 41002, 41006, 41011, 41016, 41020, 41025, 41032, 41036, 41040, 41045, 41049, 41053, 41057, 41063, 41069, 41077, 41083, 41088, 41095, 41100, 41104, 41109, 41115, 41120, 41127, 41133, 41138, 41142, 41148, 41153, 41157, 41161, 41167, 41174, 41181, 41187, 41191, 41196, 41200, 41205, 41213, 41217, -1, 41222, 41226, 41232, 41238, 41243, 41249, 41255, 41261, 41266, 41271, 41276, 41282, 41287, 41292, 41298, 41304, 41310, 41316, 41322, 41328, 41333, 41338, 41344, 41349, 41354, 41358, 41363, 41367, 41373, 41377, 41384, 41392, 41396, 41402, 41406, 41411, 41416, 41424, 41429, 41433, 41438, 41443, 41447, 41451, -1, 41455, 41459, 41465, 41469, 41474, 41480, 41484, 41490, 41496, 41500, 41504, 41510, 41516, 41521, 41528, 41532, 41537, 41543, 41548, 41553, 41557, 41562, 41568, 41572, 41577, 41581, 41587, 41593, 41598, 41602, 41608, 41615, 41620, 41625, 41629, 41633, 41638, 41644, 41650, 41655, 41661, 41666, 41672, 41676, 41680, 41686, 41691, 41695, 41702, 41707, 41712, 41716, 41721, 41726, 41731, 41737, 41743, 41748, 41754, 41759, 41765, 41770, 41775, 41780, 41784, 41789, 41794, 41799, 41805, 41810, 41814, 41818, 41823, 41829, 41833, 41837, 41842, 41848, 41855, 41859, 41864, 41871, 41876, 41882, 41886, 41893, 41898, 41903, 41908, 41914, -1, 41918, 41924, 41930, 41936, 41942, 41946, 41952, 41957, 41962, 41967, 41973, 41977, 41981, 41987, 41992, 41997, 42002, 42008, 42014, 42019, 42025, 42029, 42033, 42039, 42044, 42048, 42053, 42057, 42063, 42067, 42071, 42075, 42079, 42084, 42089, 42093, 42098, 42103, 42109, 42115, 42120, 42124, 42128, 42134, 42138, 42143, 42148, 42155, 42159, 42165, 42169, 42176, 42181, 42186, 42190, 42194, 42198, 42204, 42208, 42214, 42218, 42224, 42230, 42234, 42238, 42244, 42249, 42255, 42260, 42266, 42272, 42277, 42284, 42291, 42298, 42304, 42309, 42316, 42320, 42324, 42329, 42334, 42340, 42345, 42350, 42354, 42358, 42363, 42369, 42374, 42380, 42385, 42390, 42397, 42403, 42407, 42412, 42417, 42422, 42426, 42430, 42435, 42441, 42445, 42449, 42455, 42460, 42465, 42470, 42475, 42479, 42483, 42487, 42491, 42496, 42501, 42507, 42513, 42518, 42524, 42529, 42534, 42538, 42543, 42548, 42554, 42560, 42565, 42571, 42577, 42583, 42590, 42596, 42601, 42606, 42612, 42617, 42621, 42627, 42634, 42638, 42644, 42649, 42653, 42658, 42664, 42668, 42674, 42681, 42685, 42691, 42697, 42702, 42706, 42711, 42716, 42722, 42728, 42732, 42737, 42741, 42748, 42753, 42757, 42763, 42767, -1, 42773, 42778, 42784, 42788, 42793, -1, 42797, 42801, 42808, 42813, 42819, 42825, 42830, 42834, 42839, 42844, 42850, 42855, 42860, 42866, 42873, 42877, 42882, 42887, 42893, 42898, 42903, 42907, 42914, 42920, 42925, 42929, 42936, 42943, 42948, 42952, 42959, 42964, 42970, 42975, 42981, 42986, 42991, 42997, 43004, 43009, 43013, 43018, 43023, 43027, 43031, 43035, 43040, 43045, 43051, 43056, 43060, 43065, 43070, 43076, 43081, 43085, 43089, 43094, 43098, 43103, 43108, 43113, 43119, 43125, 43129, 43134, 43140, 43146, 43151, 43156, 43163, 43170, 43174, 43178, 43182, 43186, 43190, 43195, 43200, 43206, 43213, 43219, 43223, 43229, 43234, 43238, 43243, 43249, 43254, 43259, 43265, 43271, 43276, 43283, 43288, 43294, 43300, 43305, 43311, 43315, 43320, 43325, 43331, 43336, 43342, 43346, 43350, 43357, 43365, 43369, 43373, 43378, 43384, 43389, 43394, 43399, 43404, 43410, 43415, 43420, 43424, 43429, 43436, 43440, 43444, 43449, 43453, 43457, 43461, 43465, 43471, 43475, 43481, 43485, 43490, 43494, 43498, 43503, 43507, 43511, 43517, 43523, 43529, 43533, 43539, 43543, 43547, 43551, 43556, 43561, 43567, 43573, 43579, 43586, 43590, 43595, 43599, 43603, 43607, 43612, 43621, 43627, -1, 43632, 43636, 43640, 43645, 43650, 43655, 43660, 43664, 43669, 43675, 43682, 43686, 43691, 43696, 43702, 43708, 43715, 43720, 43725, 43731, 43736, 43742, 43746, 43752, 43757, 43760, 43765, 43770, 43776, 43781, 43786, 43791, 43796, 43800, 43807, 43813, 43818, 43823, 43827, 43831, 43836, 43843, 43847, 43852, 43856, 43860, 43866, 43872, 43878, 43884, 43888, 43893, 43898, 43904, 43909, 43913, 43918, 43921, 43927, 43933, 43938, 43943, 43947, 43952, 43957, 43963, 43969, 43975, 43981, 43985, 43990, 43996, 44001, 44005, 44009, 44014, 44018, 44023, 44028, 44032, 44037, 44042, 44047, 44051, 44055, 44059, 44065, 44070, 44074, 44079, 44084, 44088, 44094, 44100, 44106, 44113, 44118, 44122, 44126, 44131, 44135, 44142, 44148, 44154, 44158, 44163, 44167, 44172, 44177, 44180, 44185, 44191, 44197, 44201, 44206, 44212, 44217, 44222, 44227, 44232, 44239, 44244, 44248, 44252, 44258, 44264, 44270, 44275, 44279, 44285, 44291, 44296, 44300, 44305, 44310, 44314, 44320, 44326, 44331, 44336, 44342, 44349, 44354, 44360, 44365, 44369, 44374, 44378, 44383, 44387, 44393, 44398, 44402, 44406, 44411, 44417, 44423, 44427, 44432, 44436, 44442, 44447, 44452, 44457, 44460, 44465, 44471, 44477, 44483, 44489, 44493, 44498, 44505, 44509, 44514, 44519, 44524, 44529, 44535, 44540, 44545, 44550, 44557, 44563, 44568, 44575, 44581, 44588, 44592, 44598, 44602, 44608, 44613, 44619, 44623, 44629, 44635, 44640, 44646, 44651, 44655, 44661, 44666, 44670, 44674, 44678, 44684, 44690, 44695, 44699, 44703, 44709, 44715, 44720, 44726, 44731, 44734, 44740, 44745, 44749, 44753, 44757, 44761, 44766, 44771, 44776, 44781, 44786, 44792, 44797, 44803, -1, 44808, 44813, 44818, 44823, 44829, 44834, 44838, 44842, 44846, 44851, 44857, 44864, 44868, 44872, 44876, 44884, 44888, 44893, 44898, 44905, 44911, 44916, 44921, 44927, 44931, 44936, 44942, 44947, 44952, 44958, 44964, 44968, 44973, 44978, 44983, 44987, 44991, 44997, 45002, 45009, 45014, 45020, -1, 45026, 45031, 45037, 45043, 45047, 45051, 45056, 45061, 45066, 45071, 45076, 45081, 45086, 45090, 45097, 45103, 45107, 45115, 45120, 45126, 45131, 45136, 45141, 45146, 45151, 45157, 45161, 45167, 45172, 45177, 45181, 45188, 45193, 45199, 45204, 45210, 45215, 45221, 45227, 45233, 45237, 45242, 45247, 45253, 45257, 45261, 45265, 45271, 45277, 45283, 45288, 45295, 45300, 45306, 45313, 45317, 45321, 45327, 45332, 45337, 45343, 45349, 45355, 45360, 45366, 45372, 45377, 45382, 45388, 45395, 45400, 45405, 45409, 45413, 45418, 45423, 45428, 45433, 45438, 45442, 45447, 45451, 45457, 45462, 45467, 45472, 45477, 45484, 45489, 45493, 45499, 45504, 45510, 45516, 45520, 45525, 45530, 45534, 45540, 45545, 45552, 45556, 45560, 45565, 45571, 45576, 45581, 45586, 45592, 45598, 45603, 45609, 45614, 45620, 45626, 45631, 45636, 45640, 45645, 45650, 45655, 45661, 45665, 45670, 45674, 45679, 45683, 45688, 45694, 45699, 45704, 45708, 45713, 45718, 45723, 45728, 45733, 45737, 45742, 45747, 45753, 45757, 45763, 45768, 45772, 45776, 45781, 45786, 45792, 45797, 45802, 45807, 45812, 45818, 45822, 45827, 45834, 45841, 45846, 45850, 45855, 45860, -1, 45867, 45873, 45879, 45884, 45890, 45896, 45902, 45908, 45912, 45916, 45922, 45927, 45931, 45937, 45942, 45946, 45952, 45957, 45963, 45969, 45975, 45979, 45984, 45988, 45994, 45998, 46003, 46009, 46013, 46020, 46024, 46029, 46033, 46039, 46046, 46051, 46058, 46064, 46070, 46074, 46079, 46084, 46089, 46094, 46098, 46103, 46108, 46112, 46117, 46122, 46128, 46134, 46142, 46146, 46152, 46157, 46161, 46167, 46172, 46176, 46180, 46186, 46190, 46194, 46200, 46205, 46209, 46213, 46217, 46221, 46224, 46229, 46233, 46238, 46242, 46246, 46252, 46257, 46263, 46269, 46276, 46283, 46287, 46291, 46297, 46301, 46306, 46311, 46317, 46320, 46325, 46330, 46335, 46339, 46343, 46347, 46351, 46357, 46361, 46369, 46374, 46379, 46386, 46392, 46398, 46402, 46406, 46411, 46417, 46422, 46426, 46430, 46435, 46439, 46444, 46450, 46455, 46460, 46465, 46470, 46475, 46481, 46486, 46491, 46496, 46501, 46505, 46511, 46516, 46521, 46525, 46529, 46533, 46537, 46541, 46546, 46550, 46553, 46558, 46564, 46571, 46575, 46580, 46587, 46593, 46599, 46603, 46608, 46612, 46617, 46622, 46626, 46631, 46637, 46644, 46649, 46654, 46658, 46662, 46667, 46673, 46678, 46683, 46688, 46694, 46699, 46704, 46708, 46713, 46717, 46721, 46725, 46731, 46736, 46741, -1, 46748, 46752, 46757, 46763, 46768, 46774, 46779, 46783, 46787, 46792, 46797, 46803, 46808, 46814, 46819, 46823, 46827, 46831, 46835, 46840, 46844, 46848, 46852, 46856, 46862, 46866, 46872, 46876, 46881, 46886, 46891, 46895, 46901, 46906, 46911, 46917, 46921, 46927, 46931, 46935, 46940, 46945, 46950, 46956, 46960, 46966, 46972, 46976, 46981, 46985, 46990, 46995, 46999, 47003, 47009, 47014, 47020, 47025, 47030, 47034, 47038, 47042, 47047, 47053, -1, 47059, 47065, 47070, 47076, 47080, 47084, 47090, 47095, 47100, 47105, 47110, 47117, 47122, 47128, 47134, 47140, 47147, 47152, 47157, 47162, 47167, 47172, 47176, 47181, 47186, 47193, 47198, 47202, 47206, 47211, 47216, 47221, 47226, 47231, 47236, 47241, 47246, 47250, 47256, 47262, 47266, 47272, 47276, 47282, 47286, 47292, 47297, 47302, 47306, 47311, 47315, 47320, 47329, 47333, 47338, 47344, 47348, 47354, 47358, 47363, 47368, 47374, 47380, 47387, 47391, 47396, 47401, 47405, 47409, 47414, 47419, 47424, 47428, 47434, 47438, 47444, 47451, 47456, 47461, 47467, 47472, 47478, 47482, 47488, 47494, 47498, 47504, 47510, 47515, 47520, 47525, 47530, 47536, 47542, 47546, 47551, 47556, 47562, 47567, 47572, 47576, 47582, 47587, 47591, 47595, 47600, 47606, 47611, 47617, 47621, 47627, 47631, 47636, 47640, 47646, 47650, 47655, 47659, 47665, 47669, 47674, 47679, 47684, 47689, 47695, 47701, 47707, -1, 47712, 47717, 47722, 47728, 47733, 47738, 47743, 47748, 47752, 47757, 47761, 47766, 47770, 47774, 47780, 47785, 47791, 47797, 47803, 47808, 47812, 47817, 47821, 47827, 47832, 47836, 47840, 47844, 47850, 47857, 47863, 47868, 47873, 47879, 47885, 47890, 47896, 47902, 47907, 47911, 47918, 47923, 47927, 47932, 47937, 47941, 47946, 47951, 47957, 47963, 47967, 47971, 47976, 47983, 47990, 47995, 47999, 48004, 48010, 48017, 48022, 48027, 48032, 48037, 48043, 48048, 48052, 48058, 48063, 48068, 48073, 48078, 48082, 48088, 48094, 48099, 48104, 48110, 48115, 48119, 48124, -1, 48130, 48135, 48140, 48146, 48152, 48157, 48163, 48169, 48175, 48180, 48186, 48192, 48197, 48203, 48207, 48212, 48217, 48222, 48228, 48233, 48237, 48242, 48247, 48252, 48258, 48264, 48268, 48273, 48278, 48283, 48289, 48295, 48299, 48305, 48311, 48317, 48323, 48329, 48334, 48340, 48344, 48348, 48352, 48356, 48360, 48367, 48371, 48375, 48379, 48383, 48390, 48395, 48400, 48405, 48409, 48414, 48419, 48425, 48432, 48438, 48443, 48448, 48454, 48460, 48464, 48469, 48474, 48479, -1, -1, 48484, 48489, 48494, 48499, 48505, 48509, 48514, 48520, 48526, 48532, 48537, 48542, 48548, 48553, 48559, 48564, 48570, 48575, 48580, 48584, 48589, 48595, 48601, 48606, 48611, 48615, 48620, 48625, 48630, 48634, 48639, 48645, 48650, 48655, 48660, 48666, 48672, 48678, 48683, 48688, 48691, 48697, 48702, 48708, 48712, 48717, 48723, 48728, 48733, 48737, 48741, 48746, 48750, 48755, 48759, 48764, 48770, 48775, 48782, 48788, 48794, 48797, 48801, 48806, 48812, 48817, 48822, 48826, 48830, 48834, 48839, 48845, 48850, 48855, 48860, 48865, 48870, 48875, 48880, 48884, 48890, 48895, 48901, 48908, 48916, 48921, 48927, 48933, 48940, 48944, 48948, 48953, 48959, 48965, 48971, 48976, 48982, 48986, 48991, 48996, 49002, 49007, 49011, 49016, 49024, 49030, 49035, 49040, 49045, 49049, 49056, 49061, 49067, 49072, 49076, 49081, 49086, 49091, 49097, 49102, 49107, 49112, 49118, 49124, 49128, 49132, 49137, 49143, 49149, 49155, 49162, 49167, 49172, 49178, 49183, 49187, 49193, 49198, 49203, 49208, 49213, 49218, 49224, 49230, 49235, 49240, 49244, 49249, 49256, 49263, 49267, 49272, 49278, 49282, 49288, 49295, 49299, 49304, 49310, 49315, 49322, 49327, 49335, 49341, 49345, 49350, 49355, 49360, 49365, 49369, 49374, 49379, 49385, 49391, 49396, 49400, 49406, 49410, 49415, 49421, 49428, 49433, 49437, 49442, 49447, -1, 49453, 49458, 49462, 49468, 49474, 49478, 49483, 49489, 49495, 49501, 49506, 49512, 49517, 49522, 49526, 49532, 49538, 49542, 49546, 49552, 49556, 49562, 49567, 49572, 49578, 49584, 49590, 49595, 49600, 49606, 49611, 49615, 49619, 49623, 49627, 49631, 49635, -1, 49639, 49645, 49650, 49654, 49658, 49663, 49669, 49674, 49680, 49685, 49689, 49693, 49697, 49701, 49705, 49709, 49713, 49717, 49721, 49726, 49732, 49736, 49741, 49746, 49751, 49755, 49761, 49766, 49771, 49776, 49781, 49786, 49790, 49795, 49800, 49805, 49810, 49816, 49821, 49825, 49830, 49835, 49841, 49847, 49852, 49857, 49861, 49865, 49870, 49876, 49880, 49886, 49892, 49898, 49903, 49909, 49914, 49919, 49925, 49930, 49936, -1, 49942, 49948, 49952, 49957, 49962, 49967, 49971, 49975, 49980, 49986, 49991, 49997, 50001, 50007, 50011, 50017, 50023, 50028, 50033, 50037, 50043, 50048, 50053, 50058, 50062, 50067, 50071, 50076, 50082, 50087, 50092, 50097, 50101, 50107, 50112, 50117, 50123, 50130, 50134, 50140, 50145, 50150, 50154, 50158, 50162, 50166, 50172, 50176, 50181, 50185, 50189, 50193, 50199, 50204, 50210, 50215, 50221, 50226, 50230, 50235, 50239, 50244, 50248, 50254, 50259, 50265, 50270, 50274, 50281, 50287, 50292, 50296, 50301, 50306, 50310, 50315, 50320, 50324, 50329, 50335, 50340, 50346, 50352, 50358, 50363, 50368, 50373, 50377, 50383, 50387, 50394, 50398, 50404, 50408, 50413, 50419, 50423, 50430, 50434, 50440, 50445, 50451, 50457, 50461, 50467, 50471, 50475, 50481, 50487, 50492, 50497, 50501, 50505, 50511, 50516, 50521, -1, 50527, 50532, 50537, 50542, 50547, 50551, 50556, 50561, 50566, 50573, 50579, 50584, 50588, 50593, 50598, 50602, 50608, 50614, 50618, 50623, 50628, 50633, 50638, 50643, 50647, 50654, 50660, 50664, 50670, 50676, 50682, 50688, 50692, 50697, 50703, 50709, 50714, 50719, 50725, 50730, 50736, 50741, 50747, 50752, 50758, 50762, 50767, 50772, 50777, 50782, 50788, 50792, 50798, 50803, 50807, 50812, 50816, 50821, 50825, 50831, 50837, 50843, 50849, 50853, 50857, 50863, 50867, 50872, 50878, 50884, 50888, 50892, 50896, 50901, 50906, 50912, 50917, 50923, 50929, 50934, 50938, 50942, 50946, 50950, 50954, 50959, 50963, 50968, 50973, 50978, 50983, 50988, 50994, 50999, 51003, 51007, 51011, 51016, 51021, 51026, 51030, 51034, 51040, 51045, 51052, 51058, 51063, 51069, 51074, 51079, 51084, 51090, 51094, 51099, 51104, 51109, 51113, 51118, 51123, 51128, 51135, 51139, 51145, 51150, 51155, 51162, 51168, 51174, 51179, 51184, 51189, 51193, 51197, 51201, 51206, 51211, 51215, 51221, 51226, 51232, 51236, 51242, 51247, 51253, 51259, 51265, 51271, 51276, 51282, 51288, 51294, 51299, 51304, 51309, 51313, 51319, 51323, 51328, 51334, 51340, 51345, 51350, 51354, 51359, 51364, 51369, 51373, 51380, 51385, 51391, 51395, 51401, 51406, 51412, 51418, 51424, 51431, 51437, 51443, 51448, 51455, 51462, 51468, 51473, 51477, 51483, 51488, 51493, 51498, 51505, 51511, 51517, 51522, 51527, 51534, 51540, 51546, 51550, 51555, 51560, 51565, 51570, 51575, 51580, 51586, 51591, 51597, 51601, 51608, 51615, 51619, 51624, 51629, 51633, 51638, 51643, 51648, 51654, 51659, 51665, 51671, 51676, 51682, 51686, 51692, 51696, 51702, 51706, 51710, 51716, 51720, 51726, 51731, 51737, 51741, 51746, 51750, 51755, 51760, 51767, 51774, 51780, 51786, 51791, 51795, 51801, 51807, 51811, 51816, 51821, 51826, 51830, 51835, 51840, 51844, 51850, 51854, 51859, 51864, 51870, 51875, 51880, 51886, 51892, 51896, 51902, 51908, 51913, 51918, 51923, 51929, 51935, 51940, 51945, 51951, 51958, 51962, 51967, 51972, 51976, 51980, 51984, 51989, 51994, 51999, 52004, 52009, 52013, 52017, 52023, 52027, 52034, 52039, 52044, 52049, 52055, 52062, 52068, 52074, 52079, 52085, 52090, 52095, 52100, 52104, 52108, 52112, 52119, 52124, 52128, 52132, 52136, 52141, 52147, 52153, 52158, 52163, 52169, 52174, 52178, 52185, 52191, 52196, 52202, 52207, 52213, 52219, 52223, 52227, 52231, 52236, 52242, 52247, 52252, 52256, 52260, 52266, 52273, -1, 52278, 52283, 52288, 52292, 52296, 52302, 52308, 52314, 52319, 52325, 52331, 52337, 52342, 52347, 52352, 52357, 52361, 52365, 52371, 52375, 52379, 52383, 52388, 52393, 52398, 52406, 52411, 52417, 52423, 52427, 52432, 52437, 52441, 52446, 52451, 52456, 52461, 52465, 52470, 52475, 52481, 52487, 52492, 52496, 52502, 52507, -1, 52511, 52515, 52521, 52527, 52533, 52539, 52545, 52551, 52557, 52564, 52570, 52575, 52581, 52585, 52589, 52595, 52601, 52607, 52613, -1, 52619, 52625, 52631, 52635, 52641, 52646, 52650, 52655, 52660, 52665, -1, 52670, 52675, 52680, 52684, 52688, 52693, 52698, 52703, 52707, 52712, 52716, 52721, 52725, 52730, 52736, 52740, 52745, 52752, 52756, 52762, 52768, 52772, 52777, 52781, 52786, 52792, 52797, 52802, 52806, 52810, 52814, 52820, 52825, 52830, 52835, 52841, 52846, 52850, 52857, 52863, 52868, 52872, 52876, 52882, 52886, 52890, 52894, 52899, 52905, 52911, 52917, 52923, 52930, 52935, 52941, 52947, 52953, 52957, 52963, 52967, 52973, 52979, 52984, 52989, 52994, 53000, 53005, 53010, 53016, 53021, 53026, 53030, 53035, 53040, 53045, 53051, 53056, 53062, 53068, 53073, 53079, 53083, 53088, 53093, 53098, 53102, 53107, 53111, 53115, 53121, 53126, 53132, 53137, 53141, 53145, 53150, 53154, 53158, 53162, 53167, 53172, 53178, 53183, 53188, 53193, 53196, 53201, 53207, 53212, 53217, 53223, 53228, 53234, 53239, 53244, -1, 53249, 53253, 53259, 53265, 53272, 53279, 53286, 53292, 53297, 53303, 53309, 53313, 53319, 53323, 53327, 53332, 53338, 53344, 53349, 53354, 53359, 53363, 53367, 53371, 53375, 53379, 53384, 53389, 53393, 53398, 53402, 53408, 53414, 53418, 53422, 53426, 53431, 53436, 53443, 53448, 53452, 53458, 53462, 53466, 53471, 53476, 53480, 53485, 53490, 53495, 53501, 53507, 53512, 53517, 53522, 53528, 53532, 53537, 53542, 53547, 53553, 53559, 53563, 53568, 53573, 53578, 53583, 53587, 53591, 53596, 53600, 53604, 53610, 53614, 53619, 53623, 53628, 53633, 53637, 53642, 53648, 53653, 53657, 53662, 53667, 53672, 53678, 53683, 53688, 53693, 53698, 53703, 53708, 53713, 53718, 53723, 53729, -1, 53734, 53740, 53746, 53751, 53757, 53763, 53767, 53774, 53778, 53783, 53789, 53794, 53799, 53806, 53810, 53816, 53820, 53824, 53828, 53835, 53841, 53845, 53849, 53854, 53859, 53865, 53870, 53876, 53880, 53885, 53891, 53895, 53901, 53905, 53910, 53914, 53919, 53925, 53930, 53934, 53938, 53943, 53947, 53953, 53958, 53963, 53969, 53973, 53978, 53984, 53988, 53993, 53998, 54003, 54007, 54012, 54018, 54022, 54027, 54033, 54038, 54043, 54050, 54055, 54062, 54066, 54070, 54074, 54080, 54084, 54090, 54094, 54099, 54106, 54112, 54118, 54124, 54130, 54135, 54140, 54145, 54150, 54156, 54160, 54165, 54172, 54178, -1, 54184, 54190, 54194, 54199, 54205, 54211, 54215, 54221, 54226, 54232, 54239, 54247, 54252, 54257, 54262, 54268, 54273, 54279, 54284, 54290, 54294, 54299, 54303, 54308, 54314, 54319, 54323, 54329, 54334, 54340, 54344, 54349, 54354, 54359, 54363, 54370, 54375, 54381, 54386, 54391, 54397, 54403, 54409, 54417, 54421, 54427, 54431, 54436, 54440, 54444, 54448, 54452, 54457, 54461, 54466, 54471, 54476, 54482, 54488, 54494, 54499, 54504, 54509, 54515, 54519, 54525, 54529, 54535, 54541, 54546, 54551, 54557, 54563, 54567, 54571, 54575, 54580, 54585, 54591, 54596, 54600, 54604, 54609, 54614, 54620, 54625, 54631, 54636, 54641, 54647, 54651, 54657, 54665, 54674, 54679, 54684, 54689, 54695, 54701, 54707, 54713, 54717, 54721, 54727, 54733, 54739, 54745, 54751, 54755, 54759, 54764, 54769, 54775, 54779, 54784, 54789, 54795, 54800, 54805, 54811, 54818, 54823, 54827, 54831, 54837, 54841, 54845, 54849, 54854, 54860, 54866, 54872, 54878, 54882, 54886, 54891, 54898, 54904, 54908, 54914, 54919, 54923, 54929, 54933, 54938, 54943, 54949, 54955, 54959, 54965, 54969, 54973, 54978, 54983, 54988, 54994, 55000, 55005, 55010, 55016, 55023, 55028, 55033, 55038, 55042, 55047, 55052, 55056, 55062, 55066, 55070, 55077, 55082, 55089, 55096, 55101, 55107, 55111, 55115, 55120, 55126, 55132, 55136, 55141, 55147, 55153, 55158, 55165, 55173, 55181, 55187, 55192, 55196, 55200, 55206, 55214, 55220, 55227, 55232, 55237, 55242, 55249, -1, 55255, 55261, -1, 55267, 55272, 55277, 55282, 55286, 55291, 55296, 55301, 55307, 55312, 55318, 55324, 55328, 55334, 55338, 55343, 55348, 55355, 55361, 55367, 55371, 55375, 55380, 55384, 55390, 55394, 55399, 55404, 55408, 55413, 55419, 55423, 55428, 55433, 55438, 55443, 55448, 55453, 55457, 55463, 55468, 55473, 55478, 55484, 55488, 55493, 55498, 55502, 55506, 55512, 55518, 55524, 55529, 55533, 55538, 55543, 55549, 55555, 55561, 55567, 55573, 55579, 55583, 55587, 55592, 55596, 55601, 55607, 55611, 55616, 55621, 55626, 55630, 55634, 55639, 55644, 55649, 55655, 55660, 55666, 55671, 55675, 55682, 55691, 55696, 55702, 55707, 55712, 55717, 55722, 55726, 55732, 55736, 55740, -1, 55748, 55752, 55757, 55762, 55766, 55772, 55776, 55781, 55785, 55791, 55797, 55801, 55807, 55811, 55815, 55820, 55826, 55832, 55837, 55843, 55847, 55853, 55857, 55862, 55867, 55872, 55879, 55884, 55889, 55894, 55900, 55904, 55909, 55915, 55920, 55926, 55931, 55936, 55941, 55946, 55952, 55957, 55963, 55969, 55974, 55980, 55984, 55991, 55995, 55999, 56003, 56007, 56011, 56016, 56021, 56026, 56031, 56036, 56040, 56045, 56051, 56056, 56060, 56066, 56071, 56077, 56084, 56089, 56095, 56101, 56106, 56110, 56116, 56121, 56127, 56132, 56139, 56145, 56151, 56156, 56162, 56167, 56174, 56179, 56184, -1, 56189, 56193, 56197, 56203, 56208, 56214, 56220, 56225, 56230, 56235, 56241, 56246, 56250, 56255, 56260, 56266, 56271, 56275, 56282, 56286, 56292, 56297, 56301, 56306, 56311, 56317, 56322, 56327, 56332, 56337, 56342, 56346, 56354, 56358, 56364, 56370, 56376, 56380, 56385, 56391, 56399, 56404, 56410, 56416, 56420, 56425, 56430, 56434, 56441, 56445, 56450, 56456, 56460, 56464, 56469, 56475, 56481, 56485, 56490, 56495, 56500, 56505, 56512, 56517, 56521, 56527, 56533, 56539, 56545, 56550, 56555, 56559, 56565, 56571, 56577, -1, 56583, 56591, 56598, 56604, 56610, 56614, 56619, 56624, 56630, 56635, 56641, 56645, 56650, 56655, 56660, 56666, 56671, 56676, 56682, 56686, 56691, 56697, 56701, 56706, 56710, 56714, 56720, 56725, 56730, 56735, 56741, 56747, 56752, 56757, 56763, 56768, 56773, 56778, 56784, 56789, 56796, 56802, 56808, 56812, 56819, 56823, 56828, 56833, 56838, 56843, 56847, 56852, 56859, 56864, 56870, 56875, 56880, 56888, 56894, 56899, 56906, 56911, 56917, 56922, 56926, 56931, 56936, 56941, 56946, 56952, 56958, 56962, 56967, 56971, 56976, 56982, -1, -1, 56986, 56992, 56996, 57001, 57008, 57013, 57018, 57024, 57029, 57033, 57038, 57044, 57049, 57055, 57062, 57067, 57071, 57076, 57080, 57085, 57089, 57094, 57099, 57105, 57110, 57116, 57120, 57125, 57129, 57133, 57139, 57144, 57150, 57156, 57160, 57165, 57170, 57176, 57181, 57187, 57194, 57199, 57203, 57209, 57215, 57220, 57225, 57233, 57238, 57244, 57249, 57254, 57259, 57265, 57269, 57275, 57280, 57285, 57289, 57295, 57299, 57305, 57309, 57314, 57320, 57325, 57329, 57335, 57340, 57344, 57348, 57354, 57359, 57365, 57369, 57374, 57378, 57384, -1, 57389, 57395, 57400, 57405, 57412, 57418, 57423, 57428, 57432, 57436, 57440, 57446, 57450, 57455, 57460, 57464, 57468, 57473, 57478, 57482, 57488, 57492, 57497, 57503, 57508, 57514, 57520, -1, 57525, 57530, 57534, 57539, 57543, 57548, 57554, 57559, 57562, 57566, 57572, 57577, 57581, 57587, -1, 57592, 57598, 57603, 57607, 57613, 57619, 57625, 57631, 57636, 57641, 57647, 57654, 57660, 57664, 57670, 57675, 57679, 57687, 57692, 57697, 57703, 57709, 57713, -1, 57719, 57723, 57728, 57732, 57737, 57743, 57749, 57754, 57758, 57763, 57768, 57773, 57778, 57782, 57786, 57792, 57796, 57802, 57809, 57815, 57819, 57825, 57830, 57834, 57838, 57844, 57848, 57852, 57858, 57863, 57868, 57873, 57877, 57882, 57886, 57890, 57895, 57899, 57905, 57911, 57917, 57923, 57927, 57931, 57935, 57939, 57943, 57948, 57953, 57959, 57964, 57970, 57975, 57982, 57987, 57992, 57997, 58002, 58007, 58011, 58017, 58023, 58027, 58031, 58035, 58040, 58045, 58049, 58053, 58057, 58063, 58067, 58072, 58076, 58082, 58086, 58090, 58095, 58101, 58106, 58110, 58115, 58119, 58125, 58131, 58136, 58143, 58147, 58151, 58155, 58159, 58163, 58167, 58172, 58176, 58182, 58187, 58191, 58196, 58200, 58207, 58212, 58217, 58221, 58226, 58230, 58234, 58239, 58244, 58249, 58253, 58259, 58264, 58268, 58272, 58277, 58281, 58285, 58291, 58297, 58304, 58309, 58316, 58320, 58325, 58330, 58335, 58340, 58346, 58351, 58356, 58360, 58366, 58371, 58377, 58381, 58386, 58391, 58395, 58400, 58405, 58410, 58414, 58418, 58424, 58429, 58434, 58440, 58445, 58449, 58454, 58461, 58466, 58472, 58476, 58481, 58487, 58491, 58496, 58500, 58507, 58513, 58518, 58522, 58531, 58537, 58542, 58546, 58552, 58557, 58562, 58568, 58572, 58577, 58582, 58586, 58591, 58596, 58602, 58607, 58612, 58616, 58621, 58625, 58629, 58633, 58638, 58644, 58648, 58654, 58658, 58664, 58668, 58673, 58678, 58682, 58687, 58691, 58695, 58699, 58703, 58707, 58711, 58715, 58719, 58725, -1, -1, 58730, 58734, 58743, 58748, 58754, 58759, 58763, 58768, 58773, 58777, 58783, 58789, 58793, 58797, 58802, 58807, 58812, 58821, 58826, 58831, 58835, 58840, 58845, 58849, -1, 58854, 58859, 58864, 58868, 58872, 58877, 58881, 58887, 58891, 58896, 58900, 58904, 58908, 58914, 58920, 58927, 58931, 58936, 58942, 58947, 58953, 58959, 58964, 58968, 58973, 58978, 58982, 58987, 58992, 58998, 59002, 59007, 59012, 59016, 59021, 59027, 59034, 59039, 59043, -1, 59048, 59052, 59058, 59064, 59070, 59074, 59079, 59083, 59088, 59094, 59098, 59102, 59106, 59112, 59116, 59122, 59127, 59131, 59137, 59143, 59148, 59153, 59159, 59164, 59171, 59177, 59183, 59189, 59200, 59204, 59208, 59213, 59218, 59223, 59229, 59234, 59239, 59244, 59248, 59254, 59260, 59265, 59271, 59275, 59280, 59286, 59293, 59298, 59303, 59308, 59312, 59317, 59323, 59328, 59332, 59338, 59343, 59348, 59354, 59358, 59362, 59367, 59371, 59375, 59381, 59388, 59393, 59397, -1, 59405, 59411, 59416, 59421, 59427, 59431, 59435, 59441, 59447, 59453, 59458, 59463, 59469, 59474, 59480, 59484, 59490, 59496, 59501, 59506, 59513, 59519, 59526, 59531, 59537, 59543, 59547, 59552, 59556, 59560, 59565, 59569, 59573, 59578, 59584, 59590, 59595, 59601, 59607, 59612, 59617, 59622, 59626, 59631, 59636, 59640, 59645, 59650, 59654, 59660, 59664, 59671, 59677, 59681, 59686, 59691, 59696, 59701, 59707, 59711, 59716, 59721, 59726, 59730, 59736, 59740, 59744, 59749, 59755, 59761, 59766, 59770, 59774, 59779, 59784, 59788, 59793, 59798, 59803, 59808, 59812, 59816, 59820, 59824, 59828, 59833, 59838, 59844, 59849, 59855, 59859, 59865, 59870, 59874, 59881, 59888, 59892, 59897, 59902, 59906, 59911, 59918, 59924, 59928, 59933, 59937, 59942, 59948, 59953, 59957, 59962, 59966, 59972, 59977, 59981, 59986, 59990, 59994, 60000, 60004, 60009, 60014, 60018, 60024, 60031, 60037, 60042, 60046, 60050, 60054, 60058, 60062, 60067, 60072, 60079, 60084, 60090, 60095, 60099, 60104, 60109, 60114, 60120, 60124, 60130, 60136, 60142, 60147, 60153, -1, 60157, 60161, 60166, 60170, 60174, 60180, 60185, 60191, 60195, 60199, 60204, 60209, 60214, 60221, 60226, 60231, 60236, 60242, 60248, 60253, 60257, 60261, 60265, 60271, 60275, 60279, 60284, 60289, 60294, 60298, 60302, 60307, 60312, 60319, -1, 60324, 60328, 60334, 60338, 60343, 60348, 60352, 60356, 60362, 60367, 60371, 60376, 60382, 60387, 60393, 60397, 60403, 60410, 60415, 60420, 60424, 60428, 60434, 60438, 60444, 60449, 60456, 60461, 60468, 60474, 60480, 60485, 60489, 60494, 60499, 60503, 60507, 60512, 60516, 60521, 60527, 60533, 60540, 60546, 60553, 60558, 60564, 60568, 60573, 60579, 60583, 60588, 60594, 60599, 60603, 60609, 60614, 60619, 60624, 60629, 60634, 60639, 60644, 60649, 60655, 60661, 60667, 60672, 60676, 60680, 60685, 60690, 60696, 60700, 60704, 60710, 60715, 60720, 60726, 60731, 60735, 60741, 60745, 60749, 60755, 60761, 60767, 60771, 60775, 60781, 60786, 60791, 60796, 60801, 60808, 60814, 60819, 60824, 60831, 60836, 60842, 60848, 60852, 60857, 60862, 60867, 60872, 60878, 60884, 60889, 60895, 60899, 60905, 60911, 60917, 60923, 60926, 60930, 60935, 60941, 60946, 60950, 60954, 60960, 60964, 60969, 60975, 60980, 60984, 60989, 60994, 61000, 61005, 61009, 61014, 61020, -1, 61024, 61029, 61035, 61042, 61046, 61050, 61056, 61062, 61068, 61075, 61079, 61085, 61092, 61096, 61102, 61106, 61111, 61116, 61122, 61126, 61130, 61134, 61139, 61146, 61151, 61155, 61161, 61167, 61172, 61176, 61182, 61186, 61190, 61195, 61199, 61203, 61208, 61213, 61218, 61224, 61230, 61236, 61241, 61248, 61253, 61257, 61263, 61268, 61272, 61277, 61281, 61286, 61292, 61297, 61302, 61308, 61313, 61319, 61325, 61329, 61334, 61338, 61342, 61346, 61350, 61355, 61359, 61365, 61371, 61376, 61381, 61386, 61390, 61396, 61400, 61406, 61412, 61418, 61424, 61428, 61434, 61439, 61444, 61450, 61455, 61461, 61467, 61473, 61478, 61483, 61487, 61492, 61498, 61503, 61507, 61512, 61518, 61524, 61528, 61532, 61537, 61543, 61551, 61555, 61559, -1, 61563, 61569, 61574, 61578, 61584, 61588, 61594, 61600, 61606, 61611, 61617, 61622, 61628, 61634, 61638, 61643, 61649, 61653, 61657, 61663, 61668, 61672, 61676, 61680, 61684, 61689, 61694, 61698, 61703, 61707, 61712, 61716, 61722, 61727, 61733, 61739, 61743, 61747, 61752, 61758, 61763, 61768, 61774, 61780, 61786, 61791, 61796, 61801, 61806, 61811, 61816, 61819, 61824, 61829, 61835, 61839, 61843, 61848, 61854, 61859, 61864, 61869, 61875, 61881, 61886, 61890, 61894, 61898, 61903, 61909, 61915, 61919, 61925, 61932, 61936, 61941, 61947, 61952, 61957, 61964, 61971, 61976, 61980, 61984, 61989, 61994, 61998, 62005, 62011, 62015, 62022, 62028, 62033, 62039, 62045, 62050, 62056, 62062, 62067, 62073, 62078, 62083, 62088, 62094, 62100, 62107, 62113, 62118, 62124, 62129, 62133, 62138, 62143, 62148, 62154, 62161, -1, 62166, 62172, 62176, 62180, 62185, -1, -1, 62190, 62194, 62199, 62204, 62210, 62216, 62221, 62227, 62233, 62238, 62244, 62250, 62257, 62261, 62266, 62270, 62274, 62279, 62284, 62289, 62295, 62300, 62305, 62309, 62314, 62318, 62322, 62327, 62331, 62336, 62340, 62344, 62349, 62356, 62360, 62365, 62369, 62373, 62379, 62384, 62389, 62394, 62399, 62405, 62410, 62414, 62418, 62422, 62426, 62431, 62435, 62440, 62445, 62450, 62455, 62461, 62467, 62473, 62478, 62482, 62488, 62493, 62500, 62504, 62508, 62512, 62517, 62523, 62527, 62532, 62537, 62541, 62545, 62550, 62556, 62561, 62565, 62571, 62576, 62581, 62586, 62591, 62597, 62602, 62607, 62612, 62617, 62623, 62629, 62633, 62638, -1, 62644, 62649, 62654, 62658, 62663, 62667, 62671, 62677, 62682, 62689, 62694, 62700, 62707, 62711, 62715, 62719, 62723, 62727, 62731, 62736, 62742, 62747, 62753, 62758, 62763, 62769, 62773, 62779, 62783, 62787, 62793, 62797, 62801, 62805, 62809, 62813, 62819, 62824, 62829, 62835, 62840, 62845, 62850, 62857, 62861, 62866, 62871, 62877, 62883, 62888, 62894, 62898, 62904, 62908, 62912, 62916, 62922, 62926, 62933, 62938, 62944, 62949, 62953, 62957, 62963, 62969, 62975, 62979, 62984, 62988, 62994, 63000, 63005, 63011, 63016, 63023, 63030, 63037, 63041, 63047, 63052, 63058, 63063, 63071, 63077, 63082, 63086, 63092, 63099, 63105, 63111, 63117, 63122, 63127, 63133, 63139, -1, -1, 63145, 63149, 63155, 63160, 63165, 63169, 63174, 63180, 63184, 63190, 63195, 63200, 63204, 63208, 63212, 63217, 63222, 63228, 63234, 63240, 63244, 63248, 63252, 63258, 63263, 63270, 63275, 63280, 63284, 63290, 63296, 63303, 63307, 63311, 63316, 63321, 63326, 63332, 63337, 63342, 63347, 63353, 63357, 63362, 63367, 63372, 63376, 63382, 63388, 63394, 63399, 63405, 63409, 63414, 63419, 63423, 63427, 63434, 63441, 63448, 63453, 63459, 63464, 63469, 63474, 63479, 63485, 63489, 63494, 63500, 63505, 63510, 63515, 63521, 63525, 63530, 63534, 63540, 63546, 63552, 63556, -1, 63561, -1, 63566, 63570, 63575, 63580, 63585, 63591, 63596, 63600, 63605, 63609, 63613, 63618, 63623, 63629, 63634, 63638, 63644, 63650, 63655, 63661, 63667, 63671, 63675, 63681, 63687, 63693, 63698, 63703, 63708, 63712, 63717, 63721, 63725, 63729, 63734, 63738, 63744, 63750, 63756, 63761, 63766, 63771, 63775, 63779, 63784, 63789, 63794, 63798, 63804, -1, 63810, 63815, 63820, 63825, 63831, 63835, 63840, 63845, 63849, 63855, 63861, 63867, 63873, 63878, 63882, 63886, 63890, 63895, 63899, 63904, 63908, 63913, 63918, 63923, 63928, 63932, 63937, 63943, 63948, 63953, 63957, 63961, 63967, 63972, 63977, 63981, 63987, 63993, 63999, 64004, -1, 64009, 64015, 64021, 64026, 64031, 64036, 64040, 64044, 64049, 64055, 64059, 64063, 64068, 64074, 64078, 64084, 64089, 64094, 64099, 64105, 64111, 64117, 64121, 64126, 64132, 64136, 64140, 64147, 64151, 64156, 64161, 64167, 64171, 64176, 64181, 64187, 64193, 64197, 64203, 64209, 64215, 64220, 64225, 64230, 64236, 64241, 64246, 64252, 64257, 64262, 64267, 64271, -1, -1, 64276, 64282, 64286, 64290, 64296, 64302, 64308, 64313, 64318, 64322, 64328, 64333, 64339, 64344, 64350, 64356, 64361, 64366, 64374, 64380, 64386, 64392, 64397, 64402, 64407, 64413, 64417, 64423, 64429, 64433, 64439, 64444, 64449, 64454, 64461, 64465, 64470, 64476, 64481, 64485, 64489, 64495, 64500, 64506, 64512, -1, 64518, 64523, 64527, 64532, 64537, 64542, 64548, 64553, 64559, 64564, 64570, 64575, 64580, 64585, 64589, 64594, 64598, 64603, 64610, 64616, 64621, 64627, 64633, 64637, 64643, 64649, 64654, 64661, 64666, 64671, 64676, 64681, 64687, 64692, 64697, 64702, 64708, 64712, 64719, 64724, 64729, 64736, 64741, 64745, 64749, 64753, 64759, 64763, 64768, 64773, 64778, 64783, 64789, 64793, 64799, 64805, 64810, 64816, 64821, 64828, 64833, 64838, 64845, 64852, 64857, -1, 64864, 64868, 64873, 64880, 64885, 64889, 64895, 64899, 64903, 64908, 64913, 64919, 64925, 64931, 64935, 64940, 64945, 64952, 64957, 64962, 64967, 64973, 64979, 64985, 64990, 64995, 65000, 65006, 65011, 65016, 65022, 65027, -1, 65034, 65038, 65044, 65049, 65055, 65061, 65065, 65071, 65077, 65082, 65088, 65094, 65100, 65105, 65111, 65116, 65120, 65126, 65131, 65136, 65141, 65147, 65152, 65157, 65162, 65168, 65173, 65178, 65183, 65189, 65196, 65200, 65205, 65210, 65217, 65222, 65227, -1, 65232, 65237, 65243, 65247, -1, 65252, 65258, 65263, -1, 65267, 65271, 65275, 65282, 65287, 65291, 65297, 65303, 65309, 65314, 65319, 65325, 65331, 65337, 65341, 65347, 65352, 65358, 65362, 65366, 65371, 65375, 65380, 65384, 65389, 65395, 65399, 65404, 65409, 65417, 65422, 65426, 65431, 65435, -1, -1, 65439, 65444, 65449, 65455, 65460, 65464, 65470, 65475, 65480, 65485, 65490, 65495, 65500, 65504, 65509, 65513, 65519, 65523, 65527, 65531, 65537, 65543, 65549, 65555, 65562, 65567, 65571, 65578, 65583, 65589, 65594, 65599, 65603, 65608, 65612, 65618, 65623, 65627, -1, 65631, 65638, 65645, -1, -1, -1, -1, 65651, 65657, 65661, 65666, 65673, 65679, 65684, 65689, 65695, 65701, 65707, 65713, 65720, 65726, 65732, 65736, 65742, 65747, 65752, 65758, 65764, 65768, 65774, 65780, 65786, 65791, 65798, 65805, 65809, 65814, 65819, 65824, 65828, 65833, 65840, 65845, 65850, 65856, 65860, 65866, 65871, 65876, 65881, 65886, 65892, 65897, 65902, 65907, 65912, 65917, 65922, 65926, 65931, -1, -1, 65936, 65942, 65947, 65952, 65958, 65964, 65970, 65973, 65977, 65984, 65989, 65993, 65997, 66001, 66006, 66010, 66014, 66018, 66023, 66030, 66035, 66041, 66048, 66053, 66058, 66062, 66068, 66074, 66079, 66085, -1, 66094, 66101, 66106, 66110, 66114, 66120, 66126, 66130, 66136, 66142, 66146, 66151, 66157, 66162, 66166, 66172, 66177, 66183, 66188, 66192, 66196, 66201, 66205, 66210, 66216, 66221, 66225, 66229, 66236, -1, 66241, 66248, 66254, 66260, 66266, 66270, 66274, 66278, 66282, 66288, 66293, 66300, 66306, 66310, 66316, 66321, 66326, 66332, 66338, 66343, 66349, 66354, 66360, 66366, 66371, 66375, 66381, 66385, 66390, 66396, 66402, 66407, 66412, 66417, 66422, 66427, 66432, 66438, 66442, 66448, -1, -1, 66453, 66459, 66463, 66467, 66472, 66477, 66482, 66487, 66492, 66497, 66501, 66506, 66512, 66518, 66523, 66528, 66533, 66539, 66544, 66548, 66553, 66559, 66564, 66571, 66577, 66582, 66586, 66592, 66597, 66602, 66607, 66613, 66619, 66624, 66628, 66633, 66639, 66645, 66649, 66653, 66658, 66663, 66667, 66671, 66675, 66680, 66687, -1, 66693, 66697, 66701, 66706, 66712, 66717, 66721, 66726, 66732, -1, 66736, 66742, 66747, 66752, 66758, 66763, 66769, -1, 66775, 66780, 66784, 66790, 66795, -1, 66800, 66806, 66812, 66816, 66822, 66836, 66840, 66847, 66852, 66859, 66865, 66869, -1, 66874, 66879, 66884, 66888, 66892, 66896, 66901, 66905, 66910, 66914, 66922, 66926, 66933, 66941, 66945, 66951, 66956, 66963, 66969, 66976, 66982, 66987, 66992, 66998, 67004, 67009, 67013, 67021, 67026, 67031, 67037, 67042, 67048, 67052, 67060, -1, 67065, 67069, 67073, 67080, 67085, 67094, 67099, 67104, 67109, 67114, 67118, 67123, 67128, 67134, 67140, 67145, 67149, 67155, 67159, 67164, 67168, 67173, 67177, 67183, 67187, 67192, 67196, 67203, 67207, 67211, 67217, 67223, 67229, 67233, 67237, 67241, 67247, 67253, 67259, 67263, 67267, 67271, 67275, 67279, 67284, 67288, 67294, 67299, 67304, 67310, 67315, 67319, 67323, 67328, 67334, 67340, 67346, 67350, 67354, 67359, 67364, 67368, 67373, 67378, 67383, 67387, 67392, 67397, 67401, 67406, -1, 67412, 67419, 67427, 67431, 67435, 67440, 67444, 67450, 67455, 67459, 67465, 67470, 67475, 67481, 67487, 67492, 67496, 67501, 67506, 67510, 67515, 67521, 67525, 67530, 67538, 67545, 67549, 67554, 67560, 67564, 67569, 67573, 67578, 67584, 67592, 67597, 67604, 67609, 67613, 67619, 67626, 67630, 67635, 67639, 67643, 67647, 67654, 67659, 67664, 67669, 67673, 67679, 67684, 67688, 67692, 67696, 67701, 67706, 67711, 67715, 67720, 67724, 67728, 67734, 67740, 67746, 67751, 67757, 67761, 67767, 67771, 67776, 67782, 67786, 67792, 67798, 67804, 67809, 67813, 67818, 67823, 67828, 67835, 67839, 67844, 67848, 67852, 67858, 67862, 67868, 67872, 67877, 67882, 67888, 67894, 67899, 67904, 67909, 67914, 67918, 67923, 67929, 67935, 67940, 67945, 67950, 67954, 67958, 67962, 67968, 67973, 67978, 67982, 67988, 67995, 68000, 68005, 68010, 68016, 68020, 68027, 68031, 68036, 68043, 68047, 68053, 68058, 68062, 68067, 68073, 68078, 68083, 68087, 68093, 68097, 68102, 68108, 68112, 68118, 68124, 68129, 68134, 68139, 68145, 68151, 68157, 68161, 68166, 68171, 68176, 68181, 68187, 68192, 68197, 68202, 68207, 68212, 68217, 68221, 68227, 68232, 68238, 68243, 68247, 68253, 68258, 68262, 68267, -1, 68272, 68276, 68282, 68290, 68295, 68300, 68306, 68313, 68317, 68324, 68330, 68334, 68338, 68343, 68348, 68353, 68358, 68362, 68366, 68373, 68377, 68382, 68386, 68392, 68397, 68403, 68409, 68415, 68420, 68425, 68429, 68433, 68439, 68443, 68448, 68454, 68460, 68465, 68469, 68475, 68480, 68486, 68491, 68496, 68501, 68507, 68512, 68517, 68524, 68530, 68534, 68538, 68544, 68548, 68554, 68560, 68565, 68570, 68576, 68580, 68585, 68591, 68597, 68601, 68605, 68609, 68613, 68617, 68623, 68628, 68632, 68638, 68642, 68646, 68650, 68656, 68660, 68666, 68671, 68676, 68680, 68684, 68688, 68694, 68698, 68704, 68708, 68712, 68717, 68722, 68726, 68731, 68737, 68743, 68748, 68753, 68758, 68763, 68768, 68774, 68779, 68784, 68788, 68793, 68799, 68805, 68812, 68818, 68823, 68827, 68831, 68837, 68843, 68847, 68853, 68857, 68861, 68865, 68870, 68875, 68881, 68885, 68889, 68893, 68898, 68905, 68911, 68915, 68919, 68924, 68929, 68935, 68940, 68944, 68950, 68956, 68961, 68965, 68971, 68976, 68982, 68988, 68992, 68999, 69005, 69011, 69017, 69022, 69027, 69033, 69037, 69042, 69046, 69051, 69056, 69061, 69066, 69071, 69075, 69082, 69087, 69092, 69098, 69103, 69110, 69114, 69119, -1, 69124, 69130, 69134, 69139, 69144, 69147, 69154, 69158, 69162, 69166, 69173, 69178, 69183, 69188, 69194, 69200, 69206, 69211, 69217, 69222, 69227, 69233, 69239, 69243, 69248, 69252, 69257, 69261, 69265, 69271, 69275, 69280, 69286, 69291, -1, 69300, 69305, 69311, 69316, 69322, 69327, 69332, 69336, 69340, 69346, 69351, 69356, 69363, 69368, 69374, 69379, 69385, 69390, 69397, 69401, 69405, 69408, 69413, 69420, 69424, 69430, 69435, 69439, 69445, 69451, 69456, 69462, 69467, 69472, 69477, 69481, 69485, 69490, 69496, 69501, -1, 69505, 69512, 69516, 69521, 69526, 69530, 69535, 69540, 69546, 69552, 69556, 69562, 69567, 69572, 69577, 69583, 69588, 69594, 69600, 69605, 69612, 69618, 69625, 69631, 69637, 69643, 69648, 69654, 69660, 69664, 69669, 69673, 69679, 69683, 69690, 69695, 69699, 69704, 69709, 69715, 69720, 69725, 69730, 69734, 69739, 69744, 69750, 69754, 69759, 69764, 69770, 69774, 69780, 69785, 69791, 69798, 69803, 69808, 69814, 69819, 69823, 69828, 69832, 69839, 69844, 69851, 69857, 69862, 69866, 69871, 69877, 69881, 69885, 69891, 69896, 69901, 69907, 69911, 69917, 69922, 69929, -1, 69934, 69940, 69945, 69949, 69953, 69959, 69965, 69971, 69976, 69980, 69986, 69991, 69997, 70004, 70008, 70015, 70020, 70024, 70030, 70035, 70040, 70045, -1, 70049, 70055, 70059, 70063, 70068, 70072, 70077, 70083, 70089, 70094, 70099, 70104, 70110, 70114, 70119, -1, 70125, 70129, 70134, 70138, 70143, 70149, 70154, 70159, 70164, 70168, 70172, 70178, 70184, 70189, 70194, 70199, 70203, 70207, 70212, 70217, 70221, 70226, 70233, 70238, 70243, 70248, 70252, 70258, 70262, 70266, 70271, 70276, 70282, 70286, 70293, 70297, 70303, 70308, 70313, 70319, 70324, 70328, 70333, 70338, 70342, 70347, 70353, 70358, 70364, 70368, 70372, 70384, 70391, 70397, 70402, 70407, 70413, 70418, 70424, 70428, 70441, 70453, 70459, 70463, 70469, 70473, 70479, 70483, 70495, 70501, 70508, 70513, 70521, 70527, 70533, 70538, 70544, 70550, 70556, 70562, 70568, 70580, 70584, 70590, 70594, 70600, 70605, 70609, 70613, 70619, 70626, 70632, 70637, 70643, 70647, 70651, 70656, 70662, 70668, 70673, 70678, 70683, 70689, 70695, 70701, 70706, 70713, 70720, 70726, 70733, 70739, 70745, 70750, 70757, 70761, 70766, 70772, 70779, 70783, 70787, 70793, 70799, 70805, 70811, 70817, 70822, 70827, 70833, 70838, 70844, 70848, 70853, 70859, 70865, 70871, 70876, 70882, 70888, 70892, 70898, 70902, 70906, 70911, 70916, 70921, 70928, 70932, 70937, 70942, 70946, 70952, 70958, 70963, 70968, 70974, 70979, 70983, 70989, 70993, 70998, 71003, 71010, 71014, 71018, 71023, 71028, 71033, 71039, 71044, 71048, 71052, 71056, 71061, 71066, 71070, 71074, 71079, 71084, 71088, 71093, 71098, 71104, 71109, 71114, 71118, 71122, 71126, 71131, 71135, 71140, 71144, 71149, 71155, 71160, 71166, 71172, 71176, 71181, 71188, 71195, 71202, 71208, 71213, 71217, 71222, 71227, 71235, 71241, 71246, 71253, 71260, 71266, 71270, 71275, 71280, 71284, 71289, 71294, 71299, 71304, 71309, 71313, 71321, 71326, 71332, 71336, 71341, 71347, 71353, 71357, 71362, 71368, 71373, 71379, 71383, 71388, 71392, 71398, 71404, 71409, 71413, 71418, 71423, 71428, 71433, 71438, 71442, 71446, 71450, 71456, 71464, 71470, 71475, 71480, 71484, 71489, 71493, 71497, 71502, 71506, 71511, 71517, 71521, 71526, 71532, 71536, 71540, 71545, 71549, 71555, 71560, 71566, 71571, 71577, 71581, 71588, 71594, 71600, 71606, 71613, 71619, 71623, 71628, 71633, 71638, 71643, 71649, 71654, 71659, 71664, 71668, 71673, 71678, 71684, 71690, 71696, 71700, 71706, 71710, 71716, 71722, 71726, 71730, 71735, 71740, 71745, 71749, 71754, 71759, 71765, 71771, 71777, 71781, 71787, 71794, 71800, 71805, 71810, 71814, 71819, 71824, 71828, 71834, 71838, 71843, 71847, 71852, 71857, 71861, 71866, 71869, 71874, 71878, 71883, 71887, 71892, 71896, 71901, 71907, 71914, 71918, 71922, 71927, 71933, 71937, 71943, 71949, 71953, 71957, 71962, 71967, 71973, 71977, 71982, 71987, 71991, 71995, 72000, 72005, 72010, 72014, 72020, 72024, 72029, 72033, 72041, 72046, 72051, 72056, 72062, 72068, 72073, 72079, 72083, 72088, 72093, 72100, 72106, 72112, 72116, 72121, 72125, 72131, 72137, 72144, 72150, 72155, 72160, 72165, 72171, 72176, 72180, 72184, 72189, 72194, 72200, 72207, 72211, 72217, 72223, 72229, 72234, 72239, 72245, 72250, 72255, 72260, 72264, 72269, 72274, 72280, 72286, 72292, 72298, 72302, 72307, 72311, 72317, 72321, 72326, 72330, 72335, 72340, 72345, 72351, 72356, 72361, 72366, 72370, 72375, 72381, 72387, 72392, -1, 72399, 72403, 72408, 72413, 72420, 72426, 72432, 72438, 72443, 72447, 72453, 72458, 72464, 72469, 72475, 72481, -1, 72485, 72490, 72494, 72498, 72504, 72508, 72513, 72518, 72523, 72527, 72532, 72537, 72542, 72546, 72550, 72555, 72562, 72567, 72571, 72577, 72582, 72586, 72590, 72596, 72602, 72607, 72611, 72614, 72619, 72624, 72628, 72633, 72639, 72645, 72649, 72653, 72660, 72665, 72672, 72677, 72681, 72686, 72691, 72697, 72701, 72705, 72710, 72716, 72720, 72726, 72733, 72737, 72743, 72747, 72753, 72757, 72762, 72767, 72773, 72778, 72783, 72788, 72793, 72798, 72802, 72807, 72812, 72818, 72823, 72827, 72833, 72837, 72842, 72846, 72852, 72856, 72861, 72867, 72874, 72879, 72883, 72889, 72893, 72897, 72901, 72906, 72910, 72915, 72921, 72926, 72932, 72936, 72940, 72945, 72950, 72955, 72960, 72965, 72969, 72974, 72981, 72986, 72990, 72996, 73002, 73006, 73011, 73015, 73021, 73026, 73032, 73036, 73040, 73045, 73051, 73057, 73061, 73065, 73069, 73073, 73080, 73085, 73089, 73095, 73101, 73106, 73110, 73116, 73122, 73129, 73134, 73139, 73143, 73148, 73154, 73159, 73164, 73170, 73177, 73182, 73187, 73193, 73198, 73204, 73210, 73215, 73220, 73225, 73230, 73236, 73240, 73245, 73251, 73258, 73263, 73269, 73274, 73278, 73284, 73291, 73295, 73301, 73306, 73313, 73318, 73322, 73327, 73333, 73339, 73345, 73349, 73355, 73360, 73364, 73368, 73372, 73378, 73383, 73389, 73395, 73399, 73405, 73410, 73416, 73420, 73425, 73429, 73433, 73439, 73445, 73450, 73457, 73463, 73468, 73474, 73480, 73486, 73491, 73495, 73502, 73507, 73513, 73520, 73525, 73530, 73536, 73541, 73547, 73551, 73558, 73563, 73569, 73572, 73577, 73583, 73587, 73592, 73597, 73603, 73609, 73615, 73620, 73625, 73631, 73636, 73643, 73648, 73653, 73659, 73666, 73670, 73675, 73680, 73686, 73692, 73697, 73701, 73705, 73709, 73713, 73718, 73722, 73726, 73730, 73735, 73740, 73746, 73750, 73755, 73759, 73764, 73770, 73774, 73779, 73784, 73790, 73796, 73801, 73805, 73810, 73816, 73820, 73825, 73830, 73835, 73840, 73844, 73848, 73853, 73859, 73865, 73870, 73874, 73880, 73885, 73891, 73897, 73901, 73906, 73911, 73918, 73923, 73929, 73934, 73940, 73944, 73951, 73955, 73961, 73965, 73969, 73975, 73981, 73986, 73991, 73996, 74002, 74009, 74013, -1, 74019, 74024, 74029, 74033, 74039, 74043, 74048, 74054, 74060, 74066, 74072, 74077, 74083, 74088, 74092, 74096, 74102, 74107, 74112, 74117, 74123, 74127, 74131, 74136, 74142, 74148, 74152, 74158, 74162, 74167, 74173, 74178, 74184, 74189, 74194, 74199, 74205, 74211, 74216, 74222, 74226, 74233, 74238, 74244, 74250, 74255, 74259, 74265, 74271, 74277, 74282, 74286, 74291, 74297, 74302, 74307, 74312, 74317, 74322, 74326, 74332, 74336, 74341, 74346, 74350, 74356, 74361, 74366, 74372, 74378, 74382, 74388, 74394, 74399, 74405, 74409, 74415, 74421, 74426, 74431, 74436, 74442, 74446, 74450, 74454, 74459, 74463, 74468, 74472, 74479, 74483, 74488, 74495, 74501, 74505, 74510, 74515, 74520, 74524, 74528, 74533, 74538, 74543, 74549, 74554, 74558, 74563, 74568, 74573, 74576, 74580, 74584, 74589, 74594, 74599, 74606, 74611, 74616, 74621, -1, 74626, 74632, 74636, 74640, 74645, 74649, 74653, 74658, 74663, 74668, 74672, 74678, 74684, 74688, 74693, 74697, 74702, 74706, 74712, 74716, 74722, 74728, 74734, 74739, 74743, 74747, 74751, 74755, 74761, 74765, 74770, 74774, 74780, -1, 74786, 74790, 74799, 74804, 74810, 74816, 74821, 74828, 74833, 74836, 74841, 74846, 74850, 74855, 74859, 74864, 74869, 74875, 74880, 74886, 74892, -1, 74896, 74902, 74908, 74913, -1, 74919, 74924, -1, 74930, 74936, 74942, 74947, 74953, 74959, 74965, 74969, 74975, 74980, 74986, 74992, 74998, 75003, 75008, 75011, 75016, 75022, 75028, 75033, 75038, 75043, 75048, -1, 75052, 75058, 75062, 75068, 75074, 75079, 75085, 75091, 75097, 75103, 75108, 75114, 75120, 75126, 75130, 75136, 75142, 75147, 75152, 75156, 75162, 75167, 75173, 75179, 75183, 75187, 75191, 75196, 75201, 75207, 75212, 75217, 75222, 75226, 75230, 75235, 75240, 75246, 75252, 75257, 75261, 75267, 75273, 75278, 75283, 75289, 75293, 75298, 75303, 75309, 75315, 75319, 75323, 75328, 75334, 75340, 75346, 75351, 75357, 75361, 75366, 75372, 75378, 75382, 75387, 75392, 75398, -1, 75402, 75408, 75414, 75419, 75424, 75428, 75434, 75440, 75444, 75447, 75451, 75456, 75460, 75466, 75472, 75477, 75481, 75486, 75492, 75497, 75502, 75508, 75513, 75517, 75523, 75528, 75533, 75538, 75543, 75548, 75552, -1, 75556, 75562, 75567, 75574, 75580, 75584, 75588, 75594, 75598, 75602, 75607, 75614, 75620, 75625, 75630, 75636, 75640, 75644, 75648, 75653, 75659, 75665, -1, 75671, 75675, 75681, 75686, 75690, 75696, 75702, 75708, 75712, 75717, 75723, 75729, 75736, 75741, 75745, 75751, 75755, 75759, 75765, -1, 75771, 75778, 75784, 75789, 75795, 75799, 75805, 75809, 75814, 75820, 75825, 75829, 75834, 75838, 75843, 75849, 75853, 75860, 75864, 75868, 75872, 75877, 75881, 75885, 75890, 75896, 75900, 75906, 75912, 75917, -1, -1, 75921, 75926, 75930, 75936, 75943, 75948, 75953, 75958, 75962, 75966, 75971, 75976, 75981, 75987, 75991, 75997, 76003, 76007, 76011, 76019, 76025, 76030, 76035, 76040, 76045, 76050, 76054, 76060, 76065, 76070, 76074, 76078, 76082, 76086, 76091, 76097, 76103, 76108, 76112, 76117, 76123, 76128, -1, 76135, 76139, 76144, 76149, 76154, 76160, 76164, 76168, 76172, 76176, 76181, 76187, 76192, 76197, 76201, 76205, 76210, 76216, 76221, 76225, 76231, 76235, 76239, 76243, 76250, 76254, 76260, 76266, 76271, 76276, 76281, 76286, 76290, 76295, 76301, 76306, 76312, 76317, 76323, 76328, 76333, 76337, 76342, 76347, 76352, 76356, 76360, 76366, 76371, 76377, 76381, 76387, 76392, 76397, 76402, 76406, 76412, 76417, 76422, 76427, 76432, 76436, 76441, 76445, 76450, 76456, 76460, 76466, 76470, 76476, 76482, 76486, 76490, 76495, 76499, -1, 76503, 76509, 76515, 76521, 76525, 76530, 76536, 76540, 76544, -1, 76548, 76554, 76558, 76562, 76566, 76570, 76574, 76580, 76585, 76591, 76596, 76600, 76606, 76610, 76615, 76619, 76625, 76630, 76634, 76639, 76644, 76649, 76653, 76657, 76662, 76667, 76672, 76676, 76680, 76685, 76689, 76694, 76698, 76702, 76707, 76712, 76716, 76722, 76728, 76734, 76739, 76744, 76751, 76756, 76761, 76766, 76770, 76776, 76781, 76787, 76791, 76796, 76800, 76804, 76808, 76813, 76817, 76821, 76825, 76830, 76834, 76838, 76842, 76848, 76852, 76859, 76865, 76870, 76876, 76881, 76886, 76891, 76896, 76900, 76904, 76909, 76913, 76920, 76926, 76931, 76936, 76940, 76945, 76949, 76954, 76959, 76964, 76968, 76972, 76977, 76982, 76988, 76992, 76996, 77001, 77007, 77011, 77015, 77021, 77025, 77030, 77035, 77041, 77045, 77052, 77056, 77062, 77068, 77072, 77077, 77081, 77085, 77090, 77095, 77101, 77107, 77112, 77116, 77121, 77126, 77131, 77137, 77143, 77147, 77153, 77159, 77163, 77167, 77171, -1, 77176, 77180, 77184, 77190, 77194, 77198, 77202, 77207, 77212, 77219, 77225, 77229, 77236, 77241, 77246, 77252, 77256, 77260, 77264, 77268, 77274, 77279, 77284, 77289, 77293, 77298, 77303, 77307, 77313, 77318, 77323, 77327, 77333, 77337, 77343, 77348, 77353, 77357, 77361, 77365, 77369, 77375, 77380, 77385, -1, 77389, 77393, 77400, 77405, 77409, 77414, 77420, 77424, 77430, 77436, 77442, 77447, 77451, 77456, 77462, 77466, 77471, 77477, 77482, 77486, 77491, 77495, 77500, 77505, 77512, 77517, 77523, 77527, -1, 77533, 77539, 77545, 77549, 77554, 77559, 77563, 77568, 77575, 77579, 77586, 77592, 77596, 77601, 77607, 77612, 77619, 77625, 77629, 77634, 77639, 77645, 77650, 77656, 77661, 77666, 77672, 77676, 77681, 77687, 77693, 77699, 77703, 77708, 77713, 77718, 77726, 77731, 77737, 77743, 77751, 77758, 77764, 77771, 77776, 77782, 77786, 77791, 77795, 77799, 77805, 77809, 77813, 77819, 77824, -1, 77829, 77834, 77838, 77842, 77846, 77850, 77854, 77861, 77866, 77871, 77877, 77883, 77887, 77893, 77899, 77903, 77909, 77913, 77917, 77922, 77930, -1, 77936, 77942, 77948, 77955, 77960, 77966, 77971, 77977, 77983, 77990, 77995, 78000, 78006, 78011, 78015, 78020, 78025, 78030, 78036, 78041, 78047, 78051, 78057, 78063, 78068, 78073, 78079, 78083, 78089, 78094, 78100, 78106, 78112, 78118, 78124, 78131, 78135, 78139, 78144, 78150, 78156, 78162, 78168, 78174, 78178, 78183, 78189, 78193, 78198, 78204, 78210, 78216, 78223, 78229, 78235, 78240, 78246, 78252, 78257, 78263, 78268, 78273, 78277, 78283, 78287, 78292, 78297, 78303, 78308, 78316, 78320, 78324, 78330, 78334, 78340, 78344, 78350, 78356, 78361, 78365, 78371, 78377, 78381, 78387, 78393, 78398, 78402, 78406, 78412, 78416, 78422, 78428, 78435, 78440, 78444, 78449, 78453, 78458, 78462, 78466, 78471, 78476, 78481, 78487, 78491, 78495, 78499, 78504, 78509, 78513, 78517, 78521, 78527, 78531, 78535, 78539, 78544, 78549, 78554, 78558, 78563, 78568, 78573, 78577, 78582, 78588, 78595, 78601, 78608, 78614, 78619, 78624, 78628, 78633, 78638, 78643, 78649, 78653, 78660, 78665, 78671, 78677, 78682, 78688, 78693, 78697, 78701, 78708, 78713, 78719, 78723, 78727, 78733, 78737, 78744, 78749, 78754, 78758, 78764, 78770, 78777, 78781, 78785, 78790, 78794, 78798, 78802, 78808, 78812, 78818, 78824, 78829, 78835, 78840, 78845, 78852, 78858, 78863, 78868, 78873, 78879, 78885, 78891, 78895, 78900, 78905, 78911, 78916, 78922, 78928, 78934, 78939, 78942, 78948, 78954, 78960, 78964, 78969, 78974, 78978, 78982, 78986, 78990, 78995, 79001, 79005, 79011, 79017, 79021, 79026, 79033, 79039, 79044, 79050, 79054, 79058, 79062, 79067, 79073, 79078, 79082, 79087, 79091, 79097, 79103, 79109, 79114, 79120, 79126, 79132, 79136, 79140, 79144, 79148, 79153, 79158, 79163, 79168, 79174, 79179, 79185, 79191, 79197, 79202, 79209, 79215, 79221, 79227, 79234, 79238, 79243, 79248, 79253, 79259, 79263, 79267, 79273, 79279, 79283, 79288, 79295, 79301, 79306, 79311, 79317, 79324, 79330, 79336, 79341, 79347, 79352, 79357, 79362, 79368, -1, 79374, 79379, 79384, 79389, 79394, 79398, 79404, 79409, 79415, 79420, 79425, 79430, 79437, 79442, 79447, 79453, 79459, 79465, 79471, 79477, 79481, 79485, 79489, 79495, 79500, 79506, 79511, 79516, 79523, 79529, 79534, 79540, 79544, 79548, 79553, 79557, 79561, 79567, 79572, 79578, 79583, 79590, 79595, 79601, 79606, 79610, 79615, 79622, 79626, 79630, 79635, 79639, 79643, 79649, 79654, 79658, 79664, 79670, 79674, 79679, 79683, 79688, 79693, 79697, 79703, 79708, 79714, 79722, 79727, 79731, 79737, 79743, 79749, 79753, 79757, 79761, 79767, 79773, 79778, 79784, 79788, 79794, 79799, 79804, 79809, 79815, 79821, 79828, 79833, 79838, 79843, 79849, 79853, 79858, -1, 79863, 79868, 79874, 79879, 79884, 79888, 79892, 79896, 79901, 79905, 79911, 79916, 79920, 79926, 79932, 79938, 79942, 79948, -1, 79953, 79958, 79963, 79967, 79972, 79977, 79983, 79988, 79992, 79996, 80002, -1, 80006, 80012, 80018, 80025, 80031, 80036, 80041, 80047, 80053, 80059, 80065, 80069, 80073, 80079, 80084, 80090, 80094, 80099, 80105, 80114, 80120, 80125, 80131, 80136, 80143, 80147, 80153, 80159, 80165, 80171, 80175, 80179, 80184, 80190, 80195, 80201, 80205, 80209, 80213, 80218, 80224, 80229, 80235, 80239, 80244, 80249, 80255, 80259, 80263, -1, 80268, 80274, 80280, 80284, 80289, 80293, 80298, 80304, 80309, 80314, 80318, -1, 80323, 80327, 80332, 80342, 80346, 80350, 80354, 80359, 80363, 80367, 80371, 80375, 80381, 80387, 80391, 80395, 80401, 80405, 80409, 80413, 80419, 80425, 80429, 80433, 80438, 80444, 80449, 80457, -1, 80462, 80467, -1, 80471, 80479, 80486, 80492, 80496, 80503, 80508, 80514, 80518, 80523, 80527, 80531, 80535, 80540, 80545, 80552, 80557, 80563, 80569, 80574, 80581, 80586, 80591, 80597, 80603, 80608, 80614, 80619, 80623, 80629, 80633, 80639, 80644, 80648, 80652, 80657, 80661, 80665, 80670, 80674, 80679, 80684, 80690, 80695, 80703, 80708, 80716, 80722, 80726, 80731, 80736, 80741, 80747, 80754, 80758, 80763, 80768, 80773, 80777, 80784, 80791, 80797, 80801, -1, 80807, 80813, 80819, 80824, 80830, 80835, 80839, 80844, 80848, 80853, 80859, 80864, 80868, 80874, 80879, 80883, 80889, 80896, 80901, 80906, 80910, 80915, 80921, 80926, 80931, 80937, 80941, 80945, 80950, 80955, 80960, 80965, 80970, 80974, 80978, 80983, 80988, 80993, 80998, 81003, 81009, 81013, 81018, 81023, 81029, 81034, 81038, 81043, 81048, 81053, 81058, 81063, 81067, 81073, 81078, 81083, 81088, 81093, 81099, 81104, 81108, 81113, 81117, 81121, 81127, 81133, 81138, 81143, 81147, 81151, 81156, 81162, 81167, 81172, 81176, 81182, 81187, 81193, 81198, 81204, 81208, 81213, 81219, 81224, 81230, 81235, 81242, 81248, 81252, 81259, 81264, 81268, 81272, 81277, 81282, 81288, 81294, 81299, 81305, 81310, 81315, 81320, 81326, 81331, 81336, 81341, 81348, 81352, 81356, 81362, 81368, 81373, 81378, 81382, 81388, 81394, 81399, 81404, 81410, 81416, 81422, 81426, 81431, 81437, 81443, 81449, 81454, 81459, 81464, 81470, 81474, 81479, 81484, 81490, 81494, 81501, 81508, 81513, 81518, 81523, 81529, 81535, 81540, 81546, 81552, 81556, 81563, 81568, 81573, 81579, 81585, 81591, 81596, 81601, 81607, 81612, 81616, 81621, 81626, 81632, 81636, 81640, 81646, 81651, 81656, 81661, 81665, 81671, 81675, 81680, 81685, 81692, 81698, 81702, 81707, 81713, 81719, 81724, 81729, 81733, 81737, 81744, 81750, 81756, 81762, 81766, 81770, 81776, 81780, 81785, 81791, 81797, 81803, 81808, 81812, 81817, 81822, 81828, 81834, 81840, 81846, 81851, 81856, 81861, 81866, 81871, 81875, 81881, 81886, 81892, 81898, 81904, 81908, 81913, 81918, 81924, 81929, 81934, 81938, 81942, 81948, 81955, 81960, 81965, 81970, 81975, 81979, 81985, 81990, 81994, 81998, 82005, 82012, 82020, 82024, 82031, 82037, 82041, 82047, 82053, 82059, 82063, 82069, 82076, 82080, 82085, 82091, 82097, 82102, 82107, 82111, 82116, 82122, 82128, 82134, 82140, 82146, 82151, 82155, 82160, 82164, 82169, 82175, 82180, 82186, 82192, 82198, 82203, 82208, 82212, 82217, 82222, 82227, 82234, 82240, 82245, 82250, 82256, 82262, 82266, 82272, 82280, 82286, 82291, 82297, 82302, 82306, 82312, 82316, 82322, 82326, 82331, 82337, 82343, 82348, 82354, 82362, 82367, 82372, 82379, 82383, 82389, 82394, 82398, 82403, 82409, 82414, 82418, 82422, 82427, 82433, 82439, 82443, 82449, 82455, 82460, 82466, 82471, 82475, 82481, 82485, 82489, 82494, 82499, 82503, 82509, 82514, 82521, 82527, 82532, 82537, 82541, 82545, 82551, 82557, 82561, 82567, 82573, 82577, 82583, 82588, 82593, 82600, 82606, 82610, 82615, 82620, 82626, 82633, 82638, 82643, 82647, 82653, 82658, 82663, 82669, 82674, 82680, 82686, 82691, 82696, 82702, 82707, 82712, 82716, 82721, 82727, 82733, 82738, 82744, 82749, 82755, 82760, 82765, 82770, 82775, 82781, 82785, 82792, 82796, 82800, 82807, 82812, 82816, 82820, 82826, 82833, 82839, 82844, 82850, 82855, 82861, 82865, 82871, 82875, 82880, 82885, 82889, 82895, 82899, 82903, 82908, 82914, 82919, 82923, 82928, 82934, 82938, 82944, 82948, 82952, 82957, 82962, 82968, 82975, 82980, 82984, 82989, 82995, 83001, 83006, 83010, 83016, 83022, 83027, 83034, 83040, 83045, 83051, 83056, 83060, 83065, 83070, 83075, 83081, 83085, 83090, 83094, 83100, 83106, 83112, 83116, 83121, 83125, 83130, 83137, 83141, 83146, 83151, 83156, 83162, 83168, 83172, 83177, 83183, 83187, 83192, 83198, 83203, 83208, 83213, 83218, 83224, 83229, 83234, 83239, 83244, 83250, 83255, 83260, 83264, 83270, 83274, 83280, 83284, 83289, 83296, 83302, 83307, 83311, 83317, 83323, 83327, 83333, 83338, 83344, 83350, 83355, 83359, 83364, 83369, 83375, 83380, 83385, 83391, 83398, 83403, 83408, 83414, 83420, 83426, 83432, 83438, 83443, 83447, 83452, 83457, 83461, 83465, 83469, 83473, -1, 83479, 83483, 83487, 83492, 83498, 83505, 83510, 83517, 83522, 83528, 83534, 83540, 83546, 83551, 83556, 83562, 83568, 83574, 83579, 83583, 83589, 83593, 83597, 83604, 83610, 83616, 83621, 83625, 83631, 83635, 83640, 83645, 83649, 83654, 83660, 83666, 83672, 83677, 83683, 83689, 83693, 83697, 83703, 83708, 83714, 83719, 83723, 83727, 83733, 83739, 83746, 83751, 83757, 83761, 83765, 83771, 83776, 83782, 83787, 83792, 83798, 83803, 83808, 83813, 83818, 83824, 83830, 83836, 83843, 83849, 83854, 83860, 83866, 83872, 83877, 83882, 83888, 83893, 83898, 83903, 83912, 83916, 83922, -1, 83927, 83933, 83939, 83945, 83951, 83956, 83962, 83967, 83972, 83979, 83983, 83989, 83995, 84000, 84005, 84009, 84015, 84021, 84027, 84033, 84039, -1, 84044, 84049, 84053, 84057, 84061, 84065, 84070, 84074, 84079, 84083, 84088, 84094, 84098, 84103, 84107, 84113, 84117, 84122, 84127, 84133, 84138, 84143, 84149, 84153, 84158, 84162, 84166, 84171, 84176, 84180, 84184, 84188, 84193, 84197, 84201, 84205, 84211, 84216, 84220, 84224, 84230, 84236, 84241, 84245, 84251, 84255, 84261, 84266, 84270, 84274, 84278, 84284, 84290, 84295, 84302, 84306, 84311, 84317, 84322, 84327, 84332, 84336, 84341, 84347, 84351, 84358, 84364, 84369, 84375, 84379, 84384, 84388, 84393, 84399, 84403, 84409, 84415, 84420, 84425, 84431, 84437, 84442, 84448, 84452, 84462, 84469, 84475, 84479, 84485, 84490, 84496, 84502, 84506, 84511, 84517, 84522, 84528, 84534, 84539, 84545, 84550, 84557, 84563, 84567, 84573, 84577, 84584, 84590, 84595, 84601, 84606, 84611, 84616, 84620, 84625, 84630, 84634, 84638, 84642, 84646, 84652, 84657, 84662, 84666, 84670, 84675, 84682, 84686, 84690, 84694, 84700, 84705, 84710, 84716, 84720, 84726, 84733, 84737, 84742, 84747, 84752, 84757, 84762, 84767, 84773, 84779, 84784, 84790, 84796, 84801, 84806, 84811, 84815, 84819, 84824, 84828, 84834, 84838, 84844, -1, 84848, 84853, 84858, 84864, 84868, 84873, 84877, 84882, 84888, 84893, 84898, -1, 84903, 84908, 84913, 84918, 84922, 84927, 84932, 84937, 84942, 84947, 84951, 84957, 84963, 84967, 84972, 84979, 84984, 84990, 84994, 85000, 85006, 85011, 85016, 85020, 85024, 85029, 85033, 85038, 85043, 85047, 85051, 85056, 85061, 85066, 85070, 85076, 85080, 85085, 85090, 85095, 85101, 85105, 85110, 85114, 85119, 85124, 85129, 85133, 85139, 85143, 85148, 85154, 85158, 85162, 85168, 85173, 85178, 85183, 85188, 85193, 85199, 85205, -1, 85210, 85215, 85220, 85226, 85231, 85237, 85244, 85249, 85253, 85259, 85265, 85270, 85276, 85282, 85286, 85290, 85295, -1, 85300, 85306, 85311, 85318, 85323, 85328, 85334, 85338, 85342, 85346, 85351, 85356, 85360, 85366, 85372, -1, 85381, 85387, 85391, 85397, 85402, 85413, 85418, 85424, 85430, 85436, 85441, 85447, 85453, 85459, 85463, 85470, 85476, 85482, 85487, 85492, 85497, 85502, 85508, 85513, 85517, 85523, 85529, 85535, 85539, 85543, 85549, 85553, 85557, 85561, 85565, 85569, 85575, 85581, 85586, 85590, 85594, 85598, 85603, 85608, 85613, 85617, 85621, 85627, 85634, 85638, 85643, 85649, 85653, 85659, 85665, 85669, 85675, 85681, 85686, 85691, 85695, 85702, 85706, 85711, 85716, 85720, 85725, 85730, 85735, 85741, 85747, 85753, 85759, 85764, 85768, 85774, 85779, 85784, 85790, 85794, 85799, 85804, 85809, 85813, 85819, 85823, 85827, 85832, 85837, 85843, 85848, 85854, 85861, 85868, 85873, 85879, 85884, 85890, 85895, 85900, 85905, 85909, 85913, 85917, 85923, 85928, 85933, 85938, 85942, 85946, 85951, 85955, 85961, 85965, 85969, 85975, 85981, 85987, 85992, 85997, 86002, 86009, 86014, 86019, 86023, 86027, 86031, 86035, 86039, 86044, 86048, 86054, 86060, 86066, 86070, 86074, 86080, 86084, 86089, 86095, 86100, 86104, 86111, 86116, 86120, 86126, 86131, 86137, 86144, 86151, 86155, 86158, 86164, 86170, 86176, 86180, 86185, 86192, 86198, 86203, 86208, 86213, 86218, 86223, 86228, 86232, 86237, 86241, -1, 86253, 86259, 86263, 86269, 86273, 86278, 86282, 86288, 86294, 86299, 86303, 86308, 86314, 86319, 86325, 86331, 86336, 86342, 86347, 86352, 86358, 86364, 86369, 86375, 86381, 86386, 86390, 86395, 86399, 86404, 86409, 86414, 86419, 86426, 86431, 86435, 86440, 86445, 86451, 86456, 86460, 86464, 86469, 86473, 86479, 86486, 86491, 86496, 86503, 86509, 86513, 86519, 86524, 86530, 86535, 86540, 86546, 86551, 86557, 86562, 86568, 86573, 86577, 86581, 86585, 86590, 86596, 86602, 86606, 86610, 86614, 86619, 86626, 86631, 86637, 86641, 86646, 86652, 86658, 86662, 86667, 86673, 86680, 86685, 86690, 86695, 86699, 86703, 86707, 86713, 86718, 86722, 86729, 86734, 86739, 86746, 86751, 86754, 86759, 86763, 86768, 86774, 86779, 86785, 86789, 86794, 86800, 86804, 86811, 86815, 86821, 86825, 86831, 86835, 86841, 86846, 86852, 86858, 86862, 86866, 86871, 86877, 86881, 86887, 86891, 86895, 86899, 86905, 86909, 86913, 86918, 86923, 86927, 86932, 86936, 86943, 86947, 86952, 86958, 86965, 86971, 86978, 86982, 86988, 86992, 86996, 87003, 87007, 87013, 87019, 87023, 87028, 87034, 87040, 87046, 87052, 87058, 87062, 87067, 87073, 87078, 87083, 87089, 87095, 87100, 87105, 87111, 87117, 87121, 87126, 87131, 87138, 87143, 87148, 87154, 87159, 87165, 87170, 87176, 87182, 87187, 87193, 87197, 87203, 87208, 87213, 87217, 87223, 87227, 87233, 87238, 87243, 87247, 87251, 87256, 87261, 87266, 87270, 87275, 87281, 87287, 87293, 87299, 87305, 87309, 87314, 87318, 87323, 87329, 87334, 87338, 87344, 87350, 87354, 87360, 87366, 87372, 87376, 87383, 87389, 87394, 87401, 87405, 87411, 87416, 87422, 87426, 87431, 87437, 87443, 87448, 87453, 87458, 87462, 87468, 87473, 87478, 87483, 87488, 87494, 87499, 87505, 87509, 87514, 87519, 87523, 87530, 87534, 87538, 87543, 87547, 87551, 87557, 87561, 87566, 87571, 87576, 87581, 87589, 87594, 87599, 87604, 87609, 87614, 87620, 87625, 87629, 87633, 87639, 87644, 87649, 87656, 87662, 87667, 87671, 87677, 87682, 87688, 87694, -1, 87698, 87702, 87707, 87712, 87717, 87724, 87730, 87736, 87741, 87746, 87754, 87761, 87767, 87773, 87779, 87784, 87788, 87793, 87797, 87803, 87807, 87814, 87819, 87823, 87829, 87835, -1, 87839, 87843, 87848, 87854, 87858, 87864, 87868, 87874, -1, 87880, 87885, 87889, 87895, 87901, 87905, 87912, 87918, 87924, 87930, 87935, -1, 87940, 87946, 87951, 87955, 87960, 87966, 87971, 87976, 87981, 87987, 87992, 87998, 88006, 88012, 88017, 88021, 88025, 88030, 88036, 88041, 88047, 88051, 88055, 88059, 88065, 88073, 88078, 88085, 88091, 88097, 88101, 88106, 88110, 88116, 88121, 88126, 88131, 88136, 88140, 88144, 88150, 88155, 88159, 88164, 88170, 88176, 88181, 88187, 88192, 88196, 88202, 88208, 88213, 88218, 88224, 88228, 88232, 88238, 88245, 88249, 88254, 88258, 88264, 88268, 88273, 88278, 88283, 88290, 88294, 88298, 88303, 88308, 88312, 88317, 88321, 88327, 88332, 88338, 88343, 88348, 88353, 88358, 88362, 88366, 88370, 88375, 88379, 88385, 88390, 88394, 88399, 88404, 88408, 88413, 88418, 88424, 88428, 88433, 88438, 88443, 88448, 88453, 88457, 88462, 88467, 88471, 88476, 88482, 88488, 88494, 88499, 88504, 88510, 88514, 88520, 88525, 88529, 88533, 88536, 88542, 88549, 88555, 88561, 88565, 88569, 88575, 88580, 88586, 88591, 88597, 88601, 88607, 88614, 88618, 88624, 88628, 88632, 88637, 88642, 88647, 88652, 88656, 88660, 88664, 88668, 88673, 88677, 88682, 88686, 88690, 88696, 88702, 88707, 88712, 88718, 88723, 88729, 88735, 88740, 88746, 88750, 88755, 88760, 88765, 88769, 88775, 88780, 88784, 88791, 88795, 88799, 88804, 88808, 88814, 88819, 88824, 88829, 88834, 88839, 88843, 88847, 88851, 88855, 88859, 88864, 88868, 88874, -1, 88880, -1, 88886, 88891, 88897, 88904, 88908, 88914, 88918, 88924, 88929, 88934, 88940, 88946, 88952, 88956, 88961, 88965, 88970, 88974, 88978, 88983, 88987, 88994, 88998, 89003, 89010, 89016, 89021, 89027, 89033, 89037, 89041, 89046, 89050, 89057, 89062, 89067, 89072, 89076, 89082, 89086, 89093, 89099, 89105, -1, 89112, 89117, 89122, 89128, 89133, 89139, 89144, 89148, 89154, 89160, 89166, 89171, 89177, 89181, 89185, 89191, 89196, 89201, 89207, 89211, 89218, 89224, 89228, 89233, 89238, 89243, 89251, 89255, 89260, 89265, 89269, 89273, 89279, 89284, 89288, -1, 89294, 89300, 89305, 89311, 89316, 89322, 89327, 89332, 89337, 89342, 89347, 89353, 89359, 89364, 89368, 89373, 89378, 89383, 89387, 89392, 89397, 89401, 89405, 89409, 89414, 89418, 89422, 89427, 89432, 89439, 89445, 89449, 89454, 89458, 89464, 89468, 89472, 89477, 89481, 89485, 89493, 89497, 89502, 89507, 89512, 89517, 89523, 89529, 89533, 89538, 89544, 89550, 89556, 89561, 89566, 89571, 89576, 89582, 89588, 89593, 89598, 89603, 89608, 89613, 89618, 89624, 89629, 89634, 89640, 89646, 89652, 89656, 89659, 89663, 89668, 89673, 89678, 89682, 89687, 89692, 89698, 89703, 89707, 89714, 89720, 89725, 89730, 89736, 89742, 89747, 89753, 89759, 89765, 89769, 89774, 89780, 89784, 89788, 89794, 89800, 89805, 89809, 89813, 89817, 89821, 89826, 89831, 89836, 89841, 89845, 89849, 89853, 89858, 89863, 89867, 89872, 89879, 89885, 89891, 89897, 89903, 89908, 89913, 89917, 89921, 89925, 89929, 89934, 89939, 89944, 89950, 89954, 89959, 89963, 89968, 89972, 89978, 89983, 89989, 89994, 90000, 90005, 90009, 90015, 90020, 90025, 90029, 90035, 90041, 90047, 90052, 90056, 90062, 90067, 90072, 90077, 90083, 90088, 90092, 90096, 90100, 90105, 90110, 90116, 90121, 90128, 90134, 90139, 90144, 90148, 90154, 90160, 90165, 90170, 90175, 90181, 90186, 90190, 90196, 90200, 90205, 90209, 90214, 90226, 90231, 90236, 90241, -1, -1, 90246, -1, 90252, -1, 90258, 90264, 90270, 90276, 90282, 90286, 90291, 90295, 90302, 90307, 90311, 90315, 90319, 90325, 90331, 90337, 90342, 90346, 90350, 90356, 90362, 90366, 90372, 90377, 90381, 90384, 90389, 90395, 90400, 90405, 90412, 90418, 90423, 90428, 90432, 90437, 90442, 90446, 90451, 90455, 90459, 90465, 90469, 90473, 90479, 90484, 90490, 90496, 90502, 90507, 90512, 90516, 90521, 90525, 90532, 90538, 90542, 90547, 90553, 90557, 90564, 90570, 90574, 90579, 90585, 90591, 90597, 90602, 90606, 90611, 90615, 90619, 90623, 90629, 90634, 90640, 90645, 90650, 90656, 90662, 90667, 90673, 90678, 90684, 90689, 90693, 90698, 90702, 90707, 90713, 90718, 90724, 90729, 90734, 90739, 90743, 90750, 90756, 90764, 90769, 90775, 90780, -1, 90785, 90792, 90797, 90802, -1, 90807, 90812, 90817, 90822, 90828, 90833, 90838, 90844, 90850, 90854, 90860, 90864, 90869, 90875, 90881, 90885, 90889, 90893, 90899, 90904, 90908, 90912, 90917, 90921, 90927, 90933, 90937, 90941, 90946, 90952, 90957, 90961, 90967, 90972, 90976, 90980, 90984, 90990, 90994, 90999, 91004, 91011, 91017, 91021, 91026, 91032, 91036, 91040, 91045, 91050, 91056, 91062, 91067, 91071, 91076, 91080, 91084, 91090, 91096, 91102, 91107, 91112, 91117, 91123, 91128, 91132, 91137, 91143, 91148, 91153, 91158, 91162, 91168, 91173, 91177, 91182, 91186, 91191, -1, 91196, 91200, 91204, 91209, 91215, -1, 91219, 91225, 91229, 91235, 91241, 91246, 91252, 91257, 91263, 91267, 91273, 91279, 91283, 91289, 91294, 91299, 91305, 91310, 91315, 91320, 91325, 91329, 91333, 91337, 91343, 91347, 91351, 91355, 91359, 91366, 91371, 91378, 91383, 91388, 91394, 91398, 91404, 91409, 91414, 91418, 91422, 91426, 91431, 91438, 91442, 91447, 91452, 91457, 91461, 91465, 91470, 91475, 91480, 91486, 91491, 91495, 91499, 91503, 91508, 91512, 91519, 91524, 91528, 91535, 91539, 91545, 91551, 91556, 91561, 91566, 91572, 91577, 91582, 91587, 91592, 91598, 91602, 91607, 91612, 91618, 91622, 91628, 91636, 91643, 91648, 91654, 91659, 91664, 91668, 91672, 91676, 91681, 91685, 91690, 91697, 91702, 91708, 91712, 91718, 91724, 91729, 91735, 91740, 91745, 91750, 91754, 91760, 91765, 91770, 91776, 91781, 91786, 91791, 91796, 91800, 91804, 91809, 91814, 91817, 91823, 91828, 91833, 91838, 91842, 91846, 91851, 91856, 91862, 91867, 91871, 91875, 91879, 91884, 91890, 91894, 91900, 91904, 91910, 91914, 91920, -1, 91925, 91929, 91935, 91940, 91945, 91951, 91955, 91960, 91966, 91970, 91976, 91980, 91986, 91991, 91997, 92002, 92006, 92010, 92015, 92019, 92024, 92028, 92033, 92038, 92043, 92047, 92051, 92058, 92064, 92069, 92073, 92079, 92086, 92091, 92097, 92104, 92110, 92114, 92120, 92124, 92128, 92134, 92139, 92143, 92149, 92154, 92160, 92165, 92171, 92176, 92180, 92185, 92190, 92196, 92201, 92207, 92211, 92216, 92221, 92226, 92231, 92236, 92241, 92246, 92251, 92256, 92261, 92267, 92273, 92278, 92282, 92288, 92293, 92297, 92302, 92308, 92314, 92318, 92322, 92328, 92333, 92339, 92345, 92349, 92353, 92359, 92363, 92368, 92373, 92379, 92385, 92389, 92394, 92399, 92405, 92411, 92418, 92423, -1, 92429, 92433, 92439, 92443, 92447, 92451, 92455, 92460, 92464, 92468, 92472, 92477, 92483, 92487, 92492, 92497, 92503, 92508, 92512, 92518, 92524, 92529, 92534, 92539, 92544, 92551, 92557, 92562, 92567, -1, 92572, 92577, -1, 92582, 92586, 92591, 92596, 92600, 92605, 92610, 92616, 92622, 92627, 92632, 92637, 92641, 92646, 92651, 92655, 92661, 92665, 92669, 92674, 92680, 92685, 92690, 92694, 92699, 92703, 92710, 92716, 92720, 92726, 92730, -1, 92737, 92742, 92746, 92752, 92756, 92762, 92766, 92770, 92776, 92782, 92787, 92794, 92800, 92806, 92812, 92817, 92822, 92828, 92832, 92837, 92842, 92846, 92851, 92856, 92861, 92865, 92871, 92880, 92885, 92890, 92895, 92901, 92906, 92911, 92916, 92922, 92928, 92932, 92936, 92940, 92945, 92951, 92956, 92960, 92965, 92969, 92975, -1, 92980, 92985, 92991, 92997, 93001, 93007, 93012, 93017, 93021, 93025, 93029, 93034, 93038, 93042, 93048, 93054, 93059, 93063, 93067, 93072, 93076, 93080, 93084, 93088, 93092, 93096, 93101, 93107, 93111, 93116, 93121, 93126, 93132, 93138, 93143, 93148, 93152, 93157, 93164, 93169, 93175, 93179, 93184, 93191, -1, 93196, 93200, 93205, 93210, 93214, 93219, 93223, 93229, 93234, 93240, 93244, 93249, 93254, 93260, 93265, 93269, 93273, 93280, 93286, 93289, 93293, 93298, 93304, 93309, 93314, 93319, 93325, 93329, 93334, 93339, 93344, 93348, 93353, 93358, 93362, 93368, 93374, 93379, 93384, 93389, 93393, 93400, 93404, 93408, 93412, 93418, 93423, 93428, 93434, 93440, 93445, 93449, 93454, 93459, 93463, 93468, 93473, 93479, 93485, 93489, 93493, 93498, 93503, 93508, 93514, 93519, 93524, 93528, 93533, 93537, 93541, 93545, 93550, 93555, 93560, 93564, 93568, 93574, 93579, 93585, 93590, 93596, 93600, 93605, 93609, 93614, 93619, 93625, 93629, 93634, 93638, 93644, 93648, 93655, 93659, 93664, 93668, 93672, 93677, 93682, 93688, 93692, 93698, 93702, 93707, 93712, 93716, 93722, 93727, 93733, 93737, 93741, 93747, 93751, 93755, 93760, 93764, 93770, 93774, 93781, 93786, 93791, 93797, 93802, 93807, 93812, 93817, 93822, 93827, 93831, 93835, 93841, 93846, 93851, 93857, 93863, 93868, 93874, 93879, 93884, 93887, 93892, 93897, 93903, 93909, 93915, 93921, 93925, 93931, 93936, 93940, 93944, 93948, 93954, 93958, 93963, 93969, 93974, 93979, 93984, 93989, 93995, 94001, 94007, 94012, 94017, 94022, 94028, 94033, 94037, 94041, 94045, 94049, 94053, 94058, 94063, 94069, 94076, 94080, 94084, 94089, 94093, 94098, 94104, 94109, 94114, 94119, 94124, 94128, 94134, 94139, 94143, 94148, 94153, 94157, 94163, 94167, 94173, 94179, 94184, 94188, 94194, 94199, 94206, 94211, 94216, 94222, 94225, 94230, 94234, 94240, 94245, 94251, 94257, 94262, -1, 94268, 94272, 94276, 94280, 94284, 94290, 94295, 94299, 94305, 94309, 94314, 94319, 94325, 94332, 94337, 94343, 94348, 94353, 94357, 94363, 94368, 94373, 94377, 94383, 94388, 94393, 94398, 94403, 94408, 94414, 94419, 94426, 94432, 94437, 94443, 94447, 94451, 94457, 94462, 94468, 94472, 94476, 94480, 94485, 94490, 94494, 94499, 94504, 94508, 94513, 94518, 94523, 94529, 94535, -1, 94542, 94548, 94554, 94558, 94562, 94566, 94570, 94576, 94582, 94588, 94593, 94598, 94604, 94610, 94614, 94618, 94623, 94628, 94633, 94637, 94641, 94646, 94652, 94656, 94660, 94665, 94669, 94673, 94678, 94682, 94688, 94692, 94697, 94703, 94709, 94713, 94719, 94723, 94728, -1, 94733, 94740, 94745, 94750, 94755, 94759, 94763, 94767, 94772, 94777, 94783, 94789, 94795, 94799, 94803, 94807, 94813, 94818, 94823, 94828, 94835, 94840, 94847, 94854, 94859, 94865, 94870, 94875, 94880, 94884, 94888, 94893, 94898, 94903, 94907, 94913, 94917, 94922, 94928, 94938, 94945, 94951, 94958, 94962, 94967, 94971, 94976, 94980, 94985, 94991, 94997, 95003, 95007, 95012, 95018, 95023, 95028, 95032, 95036, 95043, 95050, 95055, 95060, 95065, 95070, 95075, 95080, 95086, 95092, 95098, 95103, 95109, 95114, 95121, 95126, -1, 95130, 95137, 95143, 95147, 95152, 95158, 95162, 95168, 95172, 95178, 95182, 95186, 95193, 95198, 95203, 95208, 95214, 95220, 95224, 95228, 95234, 95240, 95245, 95249, 95254, 95260, 95265, 95269, 95275, 95281, 95287, 95292, 95298, 95303, 95309, 95314, 95319, 95323, 95328, 95333, 95338, 95343, 95347, 95351, 95355, 95361, 95365, 95370, 95375, 95379, 95383, 95389, 95394, 95400, 95404, 95410, 95416, 95420, 95426, 95433, 95437, 95443, -1, 95449, 95453, 95458, 95462, 95468, 95472, 95477, 95482, 95487, 95492, 95497, 95502, 95507, 95514, 95519, 95524, 95528, 95533, 95540, 95544, 95548, 95552, 95557, 95561, 95567, 95572, 95577, -1, 95583, 95589, 95595, 95600, 95606, 95612, 95618, 95623, 95629, 95633, 95637, 95642, 95647, 95654, 95659, 95664, 95670, 95676, 95680, 95684, 95688, 95692, 95697, 95701, 95707, 95712, 95716, 95722, 95728, 95732, 95737, 95742, 95749, 95754, 95758, 95765, 95770, 95774, 95781, 95788, 95793, 95797, 95802, 95807, 95812, 95817, 95822, 95827, 95832, 95838, 95843, 95849, 95853, 95859, 95864, 95869, 95874, 95879, 95885, 95890, 95895, 95900, 95905, 95910, 95914, 95920, 95925, 95929, 95933, 95938, 95943, 95949, 95953, 95957, 95961, 95965, 95969, 95973, 95978, 95983, 95988, 95993, 95998, 96002, 96007, 96012, 96016, 96021, 96027, 96033, 96037, 96042, 96047, 96051, 96055, 96059, 96065, 96069, 96073, 96078, 96082, 96087, 96094, 96099, 96104, 96108, 96113, 96117, 96122, 96126, 96131, 96136, 96141, 96146, 96151, 96162, 96168, 96176, -1, 96182, 96187, 96192, 96198, 96203, 96208, 96213, 96220, 96224, 96230, 96236, 96241, 96245, 96249, 96253, 96259, 96264, 96269, 96274, 96278, 96282, 96286, 96294, 96300, 96305, 96310, 96314, 96320, 96326, 96330, 96336, 96341, 96347, 96351, 96356, 96361, 96367, 96373, 96378, 96385, 96391, 96396, 96402, 96409, 96416, 96421, 96426, 96431, 96437, 96442, 96447, 96452, 96456, 96460, 96465, 96469, 96473, 96477, 96482, 96487, 96492, 96497, 96501, 96505, 96509, 96516, 96521, 96527, 96532, 96537, 96543, 96548, 96553, 96557, 96563, 96568, 96573, 96577, 96581, 96587, 96592, 96597, 96602, 96607, 96611, 96616, 96623, 96628, 96632, 96637, 96641, 96647, 96653, 96659, 96663, 96668, 96675, 96680, 96685, 96690, 96695, 96701, 96707, 96713, 96718, 96724, 96730, 96734, 96740, 96745, 96749, 96754, 96759, 96763, 96767, 96772, 96777, 96783, 96788, 96794, 96799, 96803, 96810, 96815, 96820, 96827, 96836, 96840, 96846, 96853, 96859, 96864, 96868, 96873, 96877, 96882, 96886, 96893, 96898, 96903, 96908, 96913, 96918, 96922, 96927, 96932, 96937, 96942, 96948, 96954, 96960, 96964, 96969, 96975, 96980, 96985, 96989, 96994, 96998, 97002, -1, 97007, 97012, 97016, 97021, 97026, 97031, 97035, 97041, 97046, 97052, 97056, 97062, 97067, 97072, 97077, 97085, 97090, 97095, 97101, 97106, 97110, 97115, 97119, 97123, 97127, 97132, 97138, -1, 97143, 97149, 97153, 97157, 97161, 97165, 97169, 97175, 97181, 97187, 97192, 97197, 97202, 97208, 97213, 97217, 97221, 97226, 97231, 97235, 97241, 97246, 97251, 97255, 97261, 97267, 97272, 97277, 97282, 97286, 97292, 97297, 97301, 97307, 97311, 97316, 97319, 97323, 97328, 97334, 97338, 97344, 97350, 97355, 97359, 97365, 97370, 97376, 97382, 97388, 97393, 97397, 97403, 97408, 97413, 97417, 97422, 97427, 97433, 97437, 97443, 97449, 97455, 97460, 97465, 97469, 97474, 97480, 97485, 97490, 97494, 97498, 97502, 97508, 97512, 97517, 97522, 97526, 97532, 97537, 97542, 97546, 97551, 97556, 97562, 97566, 97570, 97575, 97580, 97584, 97591, 97596, 97602, 97606, 97611, 97616, 97621, 97627, 97633, 97638, 97643, 97648, 97654, 97658, 97662, 97666, 97670, 97675, 97680, 97684, 97690, 97694, 97701, 97707, 97712, 97717, 97722, 97726, 97730, 97734, 97739, 97743, 97748, 97752, 97757, 97762, 97768, 97772, 97777, 97782, 97788, 97794, 97798, 97803, 97808, 97814, 97820, 97825, 97830, 97834, 97839, 97843, 97848, 97853, 97858, 97864, 97868, 97873, 97877, 97882, 97887, 97890, 97895, 97899, 97904, 97910, 97916, 97921, 97926, 97931, 97937, 97941, 97947, 97952, 97957, 97964, 97970, 97975, 97981, 97986, 97992, 97997, 98002, 98006, 98010, 98014, 98018, 98023, 98027, 98033, 98037, 98041, 98047, 98052, 98059, 98064, 98068, 98072, 98078, 98082, 98086, 98092, 98097, 98103, 98107, 98113, 98117, 98122, 98127, 98133, 98138, 98142, 98146, 98150, 98155, 98160, 98164, 98168, 98174, 98180, 98184, 98191, 98195, 98199, 98203, 98207, 98212, 98217, 98221, 98226, 98232, 98236, 98241, 98245, 98252, 98256, 98261, 98266, 98272, 98277, 98282, 98287, 98292, 98297, 98303, 98308, 98313, 98318, 98323, 98330, 98335, 98341, 98347, 98353, 98357, 98363, 98369, 98372, 98377, 98382, 98389, 98394, 98398, 98404, 98410, 98415, 98420, 98425, 98432, 98437, 98441, 98445, 98451, 98455, 98460, 98465, 98469, 98475, 98478, 98485, 98488, 98493, 98499, 98504, 98508, 98514, 98519, 98525, 98531, 98536, 98542, 98546, 98550, 98556, 98563, 98568, 98574, 98578, 98582, 98587, 98593, 98597, 98602, 98608, 98614, 98620, 98626, 98630, 98639, 98646, 98652, 98657, 98661, 98665, 98671, 98677, 98682, 98688, 98694, 98699, 98704, 98708, 98712, 98716, 98721, 98727, 98731, 98737, 98743, 98749, 98755, 98759, 98763, 98768, 98774, 98779, 98783, 98789, 98795, 98802, 98806, 98811, 98817, 98823, 98830, 98836, 98841, 98846, 98851, 98857, 98863, 98869, 98875, 98879, 98884, 98888, 98894, 98899, 98905, 98910, 98915, 98920, 98923, 98930, 98934, 98939, 98943, 98947, 98953, 98959, 98963, 98967, 98971, 98978, 98982, 98987, 98992, 98996, 99002, 99007, 99013, 99019, 99024, 99030, 99035, 99041, 99047, 99051, 99056, 99060, 99065, 99069, -1, 99074, 99080, 99085, 99091, 99097, 99101, 99105, 99112, 99117, 99121, 99127, 99131, 99137, 99141, 99145, 99152, 99158, 99163, 99167, 99171, 99177, 99182, 99188, 99193, 99200, 99206, 99211, 99216, 99221, 99226, -1, 99231, 99235, 99241, 99247, 99253, 99257, 99261, 99265, 99272, 99277, 99282, 99288, 99292, 99297, 99303, 99308, 99313, 99317, 99323, 99327, 99333, 99337, 99341, 99346, 99351, 99355, 99359, 99366, 99371, 99377, 99382, 99387, 99393, 99397, 99403, 99407, 99411, 99416, 99421, 99428, 99433, 99438, 99441, 99447, 99452, 99456, 99461, 99467, 99471, 99476, 99483, 99487, 99492, 99497, 99502, 99508, 99513, 99518, 99522, 99529, 99535, 99540, 99545, 99551, 99556, 99562, 99566, 99572, 99578, 99584, 99589, 99593, 99597, 99603, 99607, 99612, 99617, 99621, 99625, 99631, 99637, 99642, 99648, 99654, 99658, 99662, 99668, 99673, 99677, 99683, 99689, 99693, 99699, 99704, 99708, 99713, 99719, 99724, 99729, 99735, 99740, 99746, 99750, 99754, 99760, 99765, 99770, 99773, 99779, 99784, 99790, 99795, 99801, 99807, 99812, 99817, 99823, 99828, 99833, 99839, 99844, 99848, 99854, 99860, 99865, 99869, 99874, 99878, 99884, 99888, 99893, 99897, 99902, 99908, 99913, 99919, 99925, 99929, 99934, 99939, 99943, 99948, 99952, 99957, 99964, 99969, 99973, 99977, 99981, 99985, 99989, 99992, 99997, 100004, 100010, 100016, 100021, 100027, 100032, 100039, 100043, 100047, 100053, 100057, 100062, 100069, 100074, 100078, 100084, 100088, 100093, 100097, 100101, 100107, 100111, 100115, 100119, 100126, 100131, 100136, 100141, 100146, 100151, 100156, 100163, 100168, 100174, 100179, 100184, 100189, 100195, 100200, 100205, 100209, 100216, 100221, 100228, 100233, 100238, 100242, 100248, 100253, 100257, 100261, 100267, 100271, 100276, 100281, 100286, 100293, 100299, 100305, 100309, 100315, 100320, 100325, 100330, 100334, 100340, 100345, 100349, 100354, 100358, 100364, 100370, 100376, 100382, 100389, 100395, 100400, 100405, 100411, 100415, 100420, 100426, 100431, 100438, 100444, 100449, 100454, 100458, 100463, 100466, 100470, 100476, 100482, 100486, 100490, 100495, 100499, 100503, 100509, 100513, 100517, 100523, 100527, 100532, 100537, 100543, 100548, 100554, 100558, 100563, 100568, 100573, 100579, 100584, 100589, 100594, 100600, 100605, 100609, 100614, 100619, 100625, 100631, 100637, 100641, 100647, 100653, 100658, 100663, 100669, 100674, 100680, 100684, 100688, 100693, 100699, 100704, 100708, 100714, 100719, 100726, 100730, 100735, 100739, 100745, 100750, 100754, 100759, 100765, 100771, 100776, 100781, 100786, 100791, 100796, 100801, 100806, 100811, 100815, 100820, 100824, 100830, 100834, 100839, 100842, 100846, 100853, 100859, 100865, 100870, 100875, 100879, 100883, 100887, 100892, 100897, 100901, 100906, -1, 100911, 100915, 100919, 100924, 100930, 100935, 100939, 100945, 100950, 100954, 100959, 100964, 100970, 100975, 100979, 100983, 100987, 100992, 100996, 101001, 101007, 101011, 101015, 101020, 101026, 101031, 101035, 101039, 101043, 101048, 101053, 101057, 101061, 101065, 101069, 101074, 101080, 101085, 101089, 101093, 101096, 101100, 101104, 101109, 101114, 101120, 101125, 101130, 101136, 101142, 101146, 101151, 101157, 101161, 101165, 101171, 101176, 101181, 101186, 101191, 101196, 101202, 101206, 101211, 101215, 101220, 101225, 101229, 101233, 101237, 101242, 101248, 101254, 101259, 101264, 101271, 101278, 101283, 101288, 101292, 101297, 101302, 101306, 101310, 101314, 101319, 101324, 101329, 101335, 101339, 101344, 101348, 101353, 101359, 101364, 101370, 101376, 101382, 101387, 101393, 101398, 101403, 101408, 101413, 101418, 101425, 101431, 101437, 101442, 101447, 101451, 101457, 101464, 101470, 101475, 101481, 101487, 101492, 101498, 101503, 101507, 101512, 101518, 101524, 101529, 101533, 101540, 101544, 101549, 101556, 101562, 101567, 101572, 101577, 101582, 101588, 101594, 101598, 101605, 101609, 101616, 101620, 101625, 101630, 101634, 101640, 101645, 101650, 101656, 101663, 101668, 101674, 101680, 101685, 101691, 101696, 101701, -1, 101706, 101712, 101717, 101723, 101728, 101733, 101739, 101745, 101749, 101753, 101758, -1, 101763, 101769, 101775, 101780, 101786, 101791, 101797, 101801, 101807, 101813, 101818, 101822, 101828, 101833, 101840, 101845, 101850, 101855, 101860, 101865, 101871, 101876, 101881, 101887, 101891, 101897, 101901, 101906, 101911, 101916, 101923, 101928, 101933, 101938, 101942, 101946, 101951, 101956, 101961, 101966, 101970, 101975, 101979, 101985, 101989, 101994, 101999, 102004, 102010, 102015, 102020, 102024, 102028, 102032, 102037, 102044, 102048, 102052, 102058, 102064, 102069, 102074, 102079, 102085, 102091, 102095, 102102, 102107, 102111, 102116, 102121, 102126, 102132, 102138, 102144, 102149, 102154, 102158, 102163, 102168, 102175, 102179, 102186, 102190, 102197, 102202, 102207, 102213, 102218, 102223, 102227, 102232, 102236, 102242, 102247, 102251, 102257, 102264, 102269, 102275, 102279, 102284, 102289, 102294, 102298, 102304, 102310, 102315, 102319, 102325, 102329, 102333, 102339, 102344, 102349, 102353, 102358, 102363, 102368, 102373, 102378, 102382, 102388, 102393, 102398, 102403, 102408, 102412, 102416, 102422, 102426, 102431, 102435, 102441, 102446, 102452, 102458, 102463, 102467, 102471, 102477, 102482, 102486, 102490, 102494, 102499, 102506, 102512, 102518, 102523, 102528, -1, 102532, 102537, 102542, 102546, 102550, 102555, 102559, 102563, 102568, 102573, 102577, 102583, 102589, 102595, 102600, 102604, 102609, 102613, 102617, 102622, 102629, 102634, 102639, 102643, 102649, 102655, 102660, 102664, 102669, 102673, 102678, 102683, 102687, 102692, 102697, 102702, 102706, 102711, 102716, 102720, 102725, 102729, 102735, 102739, 102743, 102747, 102751, 102756, 102760, 102764, 102769, 102775, 102781, 102786, 102791, 102798, 102803, 102810, 102815, 102822, 102827, 102834, 102839, 102843, 102849, 102854, 102859, 102865, 102870, 102876, 102880, 102886, 102890, 102896, 102901, 102905, 102909, 102913, 102918, 102922, 102927, 102933, 102938, 102944, 102948, 102953, 102958, 102963, 102968, 102974, 102980, 102986, 102992, -1, 102998, 103002, 103008, 103012, 103016, 103021, 103027, 103032, 103036, 103041, 103045, 103049, 103054, 103058, 103064, 103070, 103074, 103079, 103084, 103090, 103094, 103098, 103102, 103107, 103111, 103115, 103121, 103126, 103132, 103138, 103143, 103150, 103156, 103161, 103165, 103170, 103175, 103181, 103187, 103194, 103198, 103203, 103209, 103216, 103222, 103227, 103233, 103237, 103241, 103245, 103250, 103256, 103261, 103266, 103270, 103275, 103280, 103286, 103291, 103296, 103300, 103304, 103311, 103317, 103323, 103327, 103331, 103336, 103341, 103346, 103352, 103356, 103363, 103367, 103373, 103377, 103383, 103388, 103393, 103397, 103402, 103406, 103411, 103418, 103422, 103427, 103433, 103439, 103445, 103450, 103456, 103461, 103466, 103470, 103476, 103482, 103488, 103493, 103498, 103505, 103510, 103514, 103518, 103522, 103527, 103532, 103537, 103541, 103547, 103553, 103557, 103563, 103568, 103574, 103581, 103586, 103590, 103594, 103598, 103603, 103607, 103612, 103618, 103623, 103629, 103633, 103637, 103642, 103648, 103653, 103658, 103663, 103667, 103672, 103678, 103683, 103689, 103694, 103700, 103705, 103711, 103716, 103722, 103727, 103732, 103738, 103743, 103748, 103752, 103756, 103761, 103767, 103773, 103777, 103782, 103787, 103792, 103798, 103804, 103809, 103817, 103821, 103825, 103830, 103835, 103841, 103847, 103854, 103859, 103863, 103867, 103871, 103877, -1, 103885, 103890, 103900, 103906, 103910, 103915, 103920, 103925, 103930, 103938, 103942, 103947, 103955, 103959, 103964, 103968, 103973, 103978, 103983, 103988, 103993, 103997, 104003, 104008, 104013, 104019, 104026, 104031, 104036, 104042, 104047, 104050, 104054, 104058, 104063, 104069, 104074, 104079, 104086, 104092, 104096, 104100, 104107, 104111, 104117, 104122, 104126, 104132, 104137, 104144, 104149, 104155, 104160, 104164, 104168, 104173, 104178, 104184, 104187, 104191, 104197, 104202, 104207, 104211, 104215, 104221, 104226, 104232, 104236, 104243, 104247, 104253, 104260, 104264, 104269, 104275, 104281, 104286, 104291, 104296, 104303, 104307, 104313, 104318, 104324, 104329, 104334, 104339, 104343, 104349, 104355, 104359, 104365, 104370, 104376, 104383, 104387, 104392, 104398, 104404, 104409, 104414, 104418, 104424, 104430, 104435, 104441, 104447, 104453, 104458, 104463, 104468, 104475, 104481, 104486, 104490, 104496, 104502, 104509, 104514, 104519, 104523, 104528, 104534, 104539, 104543, 104547, 104551, 104556, 104561, 104567, 104571, 104576, 104581, 104585, 104591, 104596, 104602, 104606, 104611, 104617, 104624, 104629, 104634, 104640, 104645, 104651, 104657, 104661, 104666, 104671, 104676, 104681, 104685, 104689, 104694, 104700, 104704, 104708, 104713, 104717, 104721, 104727, 104733, 104738, 104743, 104747, 104751, 104755, 104760, 104765, 104769, 104774, 104780, 104784, 104789, 104796, 104799, 104804, 104810, 104816, 104820, 104824, 104828, 104834, 104839, 104843, 104847, 104853, 104857, 104862, 104868, 104873, 104878, 104883, 104889, 104895, 104901, 104905, 104911, 104916, 104920, 104927, 104933, 104938, 104943, 104948, 104954, 104961, 104967, 104971, 104976, 104980, 104984, 104989, 104994, 104999, 105005, 105010, 105016, 105020, 105025, 105030, 105035, 105039, 105043, 105047, 105051, 105056, 105062, 105066, 105070, 105074, 105079, 105084, 105090, 105096, -1, 105102, 105108, 105113, 105117, 105123, 105129, 105135, 105139, 105145, 105151, 105157, 105163, 105169, 105177, 105183, 105189, 105195, 105200, 105204, 105210, 105214, 105220, 105225, 105231, 105235, 105239, 105243, 105248, 105252, 105257, 105262, 105266, 105270, 105276, 105281, 105287, 105293, 105297, 105302, 105307, 105313, 105317, 105322, 105326, 105330, 105333, 105339, 105344, 105352, 105357, 105363, 105368, 105374, 105379, 105384, 105388, 105394, 105399, 105403, 105410, 105415, 105421, 105426, 105430, 105434, 105438, 105444, 105449, 105453, 105458, 105463, 105468, 105473, 105479, 105484, 105490, 105495, 105499, 105505, 105509, 105514, 105518, 105523, 105529, 105534, 105539, 105544, 105550, 105555, 105560, 105566, 105571, 105575, 105580, 105587, 105593, 105598, 105602, 105607, 105613, 105618, 105623, 105628, 105633, 105637, 105641, 105645, 105651, 105655, 105660, 105664, 105669, 105675, 105682, 105686, 105691, 105698, 105702, 105707, 105712, 105718, 105724, 105729, 105734, 105738, 105744, 105749, 105754, 105759, 105764, 105770, 105776, 105781, 105785, 105790, 105796, 105801, 105805, 105811, 105816, 105821, 105825, 105829, 105833, 105839, 105844, 105848, 105853, 105857, 105863, 105868, 105873, 105878, 105883, 105890, 105895, 105899, 105903, 105908, 105914, 105919, 105924, 105927, 105933, 105938, 105941, 105946, 105953, 105958, 105963, 105967, 105972, 105980, 105984, 105989, 105994, 105999, 106004, 106009, 106013, 106017, 106022, 106028, 106032, 106036, -1, 106042, 106047, 106053, 106060, 106064, 106069, 106073, 106078, 106082, 106087, 106092, 106097, 106101, 106106, 106111, 106116, 106121, 106126, 106131, 106137, 106143, 106147, 106151, 106155, 106161, 106165, 106170, 106174, 106179, 106184, 106189, 106194, 106200, 106206, 106212, 106216, 106222, 106228, 106234, 106238, 106242, 106248, 106253, 106258, 106262, 106266, 106272, 106278, 106282, 106288, 106294, 106299, 106303, 106309, 106313, -1, 106320, 106327, 106331, 106337, 106342, 106348, 106354, 106358, 106363, 106369, 106375, 106381, 106386, 106391, 106396, 106402, 106406, 106410, 106415, 106420, 106425, 106430, 106434, 106439, 106445, 106451, 106456, 106460, 106464, 106469, 106473, 106478, 106485, 106490, 106495, 106500, 106504, 106508, 106513, 106517, 106521, 106526, 106530, 106535, 106540, 106546, 106552, 106556, 106561, 106566, 106572, 106577, 106583, 106590, 106597, 106602, 106607, 106614, 106619, 106624, 106628, 106632, 106636, 106640, 106645, 106650, 106656, 106661, 106667, 106672, 106678, 106682, 106686, 106691, 106695, 106700, 106704, 106710, 106714, 106718, 106724, 106729, 106735, 106740, 106745, 106751, 106756, 106760, 106766, 106771, 106775, 106782, 106787, 106791, 106797, 106803, 106807, 106811, 106816, 106821, 106826, 106831, 106834, 106838, 106844, 106848, 106853, 106858, 106863, 106870, 106877, 106882, 106888, 106894, 106898, 106902, 106907, 106911, 106916, 106920, 106924, 106931, 106936, 106941, 106947, 106951, 106955, 106961, 106965, 106971, 106976, 106983, 106987, 106993, 106998, 107005, 107010, 107016, 107020, 107026, 107030, 107035, 107039, 107045, 107050, 107057, 107062, 107068, 107073, 107077, 107082, 107088, 107092, 107097, 107103, 107109, 107115, 107120, 107127, 107133, 107138, 107144, 107149, 107153, 107158, 107164, 107169, 107175, 107181, 107185, 107191, 107196, 107200, 107205, 107211, 107216, 107222, 107227, 107233, 107237, 107243, 107247, 107252, 107257, 107262, 107267, 107272, 107276, 107281, 107287, 107292, -1, 107304, 107309, 107314, 107320, 107325, 107330, 107335, 107340, 107344, 107348, 107352, 107358, 107363, 107369, 107374, 107380, 107386, 107391, 107397, 107402, 107408, 107415, 107422, 107427, 107431, 107437, 107442, 107446, 107452, 107457, 107461, 107466, 107471, 107476, 107480, 107487, 107492, 107496, 107501, 107506, 107511, 107516, 107522, 107528, 107533, 107538, 107544, 107548, 107553, 107557, 107563, 107567, 107571, 107575, 107581, 107586, 107591, 107597, 107602, 107606, 107610, 107615, 107619, 107625, 107631, 107636, 107641, 107647, 107652, 107657, 107662, 107668, 107675, 107681, 107687, 107691, 107695, 107700, 107705, 107709, 107716, 107720, 107726, 107730, 107735, 107739, 107745, 107749, 107753, 107757, 107763, 107767, 107774, 107778, 107783, 107789, 107794, 107800, 107804, 107808, 107815, 107820, 107826, 107830, 107834, 107839, 107845, 107850, 107855, 107860, 107865, 107870, 107874, 107881, 107885, 107892, 107896, 107901, 107906, 107911, 107915, 107921, 107926, 107932, 107938, 107942, 107947, 107951, 107955, 107961, 107966, 107970, 107976, 107980, 107985, 107991, 107997, 108003, 108008, 108015, 108019, 108024, 108028, 108033, 108038, 108044, 108048, 108054, 108058, 108062, 108066, 108070, 108074, 108080, 108085, 108089, 108093, 108097, 108103, 108113, 108119, 108123, 108127, 108132, 108136, 108142, 108148, 108154, 108159, 108164, 108169, 108173, 108177, 108181, 108186, 108189, 108194, 108199, 108204, 108209, 108215, 108220, 108224, 108229, 108235, 108240, 108245, 108250, 108254, 108259, 108263, 108268, 108274, 108279, 108284, 108289, 108293, 108300, 108305, 108310, 108314, 108319, 108324, 108330, 108338, 108345, -1, 108352, 108357, 108361, 108365, 108369, 108374, 108379, 108383, 108386, 108392, 108398, 108402, 108406, 108410, 108414, 108418, 108422, 108427, 108431, 108436, 108442, 108447, 108453, 108458, 108463, 108468, 108472, 108478, 108483, 108488, 108494, 108500, 108505, 108509, 108513, 108520, 108525, 108530, 108534, 108539, 108544, 108548, 108552, 108558, 108564, 108568, 108574, 108579, 108585, 108589, 108593, 108598, 108602, 108608, 108613, 108617, 108622, 108627, 108632, 108639, 108643, 108647, 108652, 108656, 108661, 108665, 108670, 108676, 108680, 108684, 108691, 108695, 108700, 108706, 108712, 108718, 108724, 108730, 108735, 108739, 108743, 108749, 108754, 108759, 108764, 108769, 108774, 108780, 108787, 108794, 108800, 108804, 108809, 108813, 108817, 108821, 108826, 108831, 108836, 108843, 108849, 108854, 108860, 108866, 108872, 108876, 108881, 108886, 108891, 108897, 108902, 108907, 108913, 108919, 108924, 108929, 108934, 108939, 108943, 108947, 108951, 108955, 108959, 108964, 108971, 108975, 108981, 108986, 108990, 108995, 109001, 109007, 109016, 109020, 109026, 109031, 109036, 109041, 109047, 109052, 109058, 109063, 109068, 109074, 109078, 109083, 109088, 109094, 109101, 109106, 109110, 109115, 109121, 109126, 109131, 109136, 109141, 109145, 109149, 109153, 109159, 109165, 109169, 109174, 109179, 109185, 109191, 109197, 109203, 109208, 109213, 109217, 109223, 109227, 109231, 109237, 109243, 109249, 109254, 109260, 109266, 109272, 109277, 109282, 109287, 109292, 109299, 109303, 109309, 109314, 109320, 109324, 109328, 109333, 109338, 109345, 109350, 109354, 109358, 109365, -1, 109370, 109374, 109379, 109384, 109388, 109395, 109401, 109407, 109413, 109419, 109424, 109431, 109436, 109441, 109447, 109452, 109456, 109460, 109467, 109472, 109478, 109483, 109488, 109494, 109498, 109502, 109508, 109512, 109518, 109523, 109526, 109532, 109536, 109541, 109545, 109550, 109556, 109560, 109567, 109572, 109577, 109583, 109588, 109593, 109599, 109603, 109609, 109615, 109619, 109623, 109628, 109634, 109639, 109646, 109652, 109656, 109662, 109667, 109671, 109676, 109681, 109687, 109691, 109697, 109701, 109706, 109712, 109716, 109721, 109726, 109734, 109738, 109743, 109748, -1, 109753, 109757, -1, 109763, 109768, -1, 109775, 109780, 109785, 109791, 109795, 109801, 109806, 109812, 109816, 109820, 109825, 109831, 109835, 109840, 109844, 109847, 109852, 109858, 109862, 109867, 109873, 109879, 109885, 109890, 109894, 109900, 109906, 109913, 109918, 109922, 109928, 109933, 109938, 109943, 109949, 109954, 109960, 109965, 109971, 109977, 109982, 109987, 109993, 109999, 110003, 110008, 110014, 110020, 110026, 110032, 110037, 110041, 110047, 110052, 110057, 110063, 110069, 110076, 110080, 110085, 110089, 110094, 110099, 110103, -1, 110108, 110113, 110119, 110123, 110129, 110134, 110140, 110145, 110149, 110154, 110158, 110162, 110167, 110173, 110179, 110185, 110190, 110197, 110201, 110207, 110213, 110217, 110222, 110226, 110230, 110236, 110242, 110246, 110250, 110256, 110263, 110269, 110274, 110279, 110284, 110289, 110294, 110299, 110304, 110310, 110316, 110321, 110325, 110331, 110337, 110342, 110346, 110351, 110355, 110359, 110365, 110374, 110382, -1, -1, -1, 110387, 110391, 110395, 110400, 110404, 110408, 110414, 110419, 110425, 110431, 110437, 110443, 110447, 110452, 110456, 110461, 110465, 110470, 110475, 110479, 110485, 110491, 110495, 110499, 110503, 110509, 110516, 110521, 110525, 110532, 110538, 110543, 110549, 110555, 110561, 110567, 110573, 110578, 110583, 110588, 110594, 110600, 110605, 110611, 110616, 110620, 110625, 110629, -1, 110635, -1, -1, 110641, 110648, 110652, 110657, 110661, 110665, 110670, 110677, 110681, 110686, 110691, 110696, 110701, 110708, 110713, 110717, 110722, 110727, 110733, 110738, 110743, 110748, 110754, 110760, 110766, 110772, 110779, 110784, 110790, 110795, 110801, 110806, 110811, 110816, 110821, 110826, 110830, 110836, 110843, 110849, 110853, 110859, 110864, 110869, 110874, 110878, 110882, -1, -1, 110887, 110893, 110899, 110904, 110910, 110915, 110920, 110924, 110930, 110935, 110939, 110945, 110951, 110957, 110963, 110969, 110974, 110978, 110983, 110989, 110993, 110998, 111003, 111009, 111013, 111018, 111022, 111027, 111031, 111035, 111041, 111046, 111052, 111057, 111063, 111069, 111073, 111078, 111082, 111087, 111093, 111098, 111104, 111109, 111115, 111119, 111125, 111131, 111137, 111142, 111147, 111152, 111156, 111161, 111165, 111171, 111178, 111185, 111191, 111195, 111199, 111205, 111211, 111215, 111221, 111226, -1, -1, 111230, 111236, 111239, 111243, 111248, 111254, 111259, 111263, 111269, 111274, 111280, 111287, 111293, 111297, 111303, 111309, 111314, 111319, 111325, 111329, 111334, 111339, 111345, 111351, 111356, 111361, 111366, 111372, 111377, 111382, 111387, 111392, 111396, 111402, 111408, 111414, 111418, 111424, 111428, 111434, 111441, 111447, 111451, 111456, 111462, 111467, 111471, 111475, 111481, 111487, 111492, 111496, 111501, 111505, 111510, 111516, 111523, 111527, 111532, 111538, 111543, 111549, 111554, 111559, 111564, 111568, 111573, 111579, 111585, 111590, 111595, 111600, 111604, 111608, 111614, 111621, 111627, 111631, 111635, 111638, 111642, 111646, 111650, 111655, 111661, 111665, 111670, 111675, 111681, 111686, 111691, 111695, 111701, 111707, 111713, 111718, 111723, 111728, 111734, 111739, 111743, 111747, 111751, 111756, 111762, 111767, 111772, 111776, 111782, 111786, 111791, 111798, 111803, 111807, 111812, 111817, 111823, 111829, 111833, 111839, 111844, 111849, 111854, 111859, 111865, 111871, 111876, 111882, 111887, 111893, 111898, 111903, 111908, 111912, 111918, 111925, 111929, 111935, 111939, 111945, 111952, 111958, 111962, 111967, 111971, 111976, 111981, 111985, 111989, 111994, 111998, 112004, 112010, 112016, 112021, 112025, 112032, 112037, 112042, 112047, 112052, 112057, 112062, 112067, 112073, 112078, 112083, 112088, 112094, 112099, 112103, 112109, 112115, 112119, 112122, 112129, 112134, 112140, 112145, 112150, 112155, 112159, 112163, 112168, 112173, 112180, 112184, 112188, 112193, 112199, 112205, 112210, 112215, 112221, 112227, 112232, 112237, 112242, 112248, 112254, 112258, 112264, 112268, 112273, 112279, 112284, 112291, 112297, 112300, 112305, 112311, 112318, 112324, 112329, 112336, 112342, 112346, 112350, 112355, 112360, 112364, 112369, 112374, 112378, 112384, 112388, 112392, 112397, 112403, 112409, 112414, 112418, 112423, 112428, 112434, 112438, 112443, 112448, 112454, 112460, 112466, 112471, 112476, 112482, 112488, 112494, 112498, 112502, 112508, 112513, 112518, 112523, 112529, 112533, 112537, 112542, 112547, 112551, 112557, 112564, 112570, 112575, 112580, 112586, 112592, 112598, 112602, 112607, 112613, 112617, 112623, 112630, 112637, 112644, 112649, 112653, 112658, 112662, 112668, 112672, 112679, 112684, 112688, 112695, 112701, 112706, 112711, 112716, 112720, 112725, 112729, 112734, 112739, 112745, 112751, 112757, 112762, 112767, 112773, 112779, 112785, 112790, 112795, 112801, 112806, 112811, 112816, 112822, 112828, 112833, 112839, 112845, 112849, 112854, 112858, 112864, 112868, 112872, 112876, 112881, 112887, 112892, 112897, 112901, 112906, 112912, 112916, 112922, 112927, 112931, 112936, 112941, 112947, 112951, 112956, 112963, 112969, 112974, 112979, 112984, 112989, 112992, 112997, 113001, 113006, 113013, 113018, 113025, 113029, 113034, 113039, 113044, 113048, 113053, 113058, 113063, 113068, 113073, -1, 113077, 113085, 113091, 113096, 113100, 113105, 113109, 113114, 113122, 113128, 113133, 113137, 113142, 113148, 113154, 113160, 113165, 113169, 113174, 113180, 113184, 113190, 113196, 113200, -1, 113205, 113210, 113217, 113223, 113228, 113233, 113237, 113242, 113250, 113255, 113260, 113266, 113272, 113278, 113283, 113289, 113294, 113299, 113304, 113309, 113314, 113318, 113323, 113327, 113332, 113336, 113340, 113344, 113349, 113354, 113359, 113365, 113369, 113373, 113378, 113385, 113389, 113394, 113399, 113404, 113407, 113413, 113418, 113422, 113427, 113432, 113437, 113441, 113447, 113451, 113456, 113461, 113467, 113471, 113475, 113479, 113484, 113489, 113495, 113499, 113503, 113508, 113513, 113519, 113524, 113527, 113534, 113539, 113544, 113550, 113555, 113561, 113566, 113570, 113576, 113582, 113588, 113593, 113599, 113604, 113611, 113614, 113618, 113622, 113626, 113631, 113637, 113643, 113646, 113651, 113656, 113660, 113666, 113670, 113674, 113678, 113684, 113690, 113696, 113701, 113706, 113710, 113717, 113722, 113727, 113733, 113737, 113742, 113746, 113751, 113757, 113762, 113768, 113774, 113780, 113784, 113788, 113793, 113799, 113806, 113811, 113815, 113820, 113826, 113832, 113837, 113843, 113848, 113853, 113858, 113864, 113869, 113874, 113879, 113883, 113888, 113893, 113898, 113902, 113908, 113914, 113918, 113924, 113929, 113935, 113939, 113946, 113952, 113957, 113962, 113968, 113974, 113980, 113987, 113992, 113997, 114001, 114005, 114009, 114015, 114020, 114025, 114030, 114035, 114040, 114044, 114051, 114056, 114061, 114066, 114071, 114076, 114081, 114085, 114090, 114094, 114099, 114103, 114107, 114113, 114117, 114124, 114129, 114133, 114137, -1, 114142, 114147, 114152, 114157, 114163, 114167, 114172, 114177, 114181, 114187, 114192, 114196, 114200, 114204, 114209, 114213, 114218, 114221, 114226, 114232, 114236, 114240, 114244, 114248, 114254, 114258, 114263, 114269, 114274, 114279, 114283, 114288, 114293, 114299, 114303, 114310, 114314, 114318, 114322, 114328, 114333, 114338, 114343, 114347, 114353, 114357, 114362, 114366, 114370, 114375, 114379, 114385, 114391, 114396, 114401, 114406, 114414, 114420, 114425, 114429, 114435, 114439, 114443, 114449, 114454, 114458, 114463, 114468, 114472, 114476, 114480, 114484, 114488, 114493, 114497, 114502, 114506, 114510, 114515, 114520, 114526, 114531, 114535, 114539, 114544, 114550, 114555, 114559, 114564, 114570, 114576, 114582, 114588, 114592, 114598, 114602, 114606, 114610, 114616, 114622, 114627, 114633, 114639, 114644, 114649, 114655, 114659, 114665, 114670, 114675, 114680, 114686, 114692, 114697, 114701, 114707, 114712, -1, 114717, 114723, 114729, 114733, 114739, 114747, 114753, 114758, 114764, 114769, 114773, 114778, 114783, 114788, 114794, 114799, 114803, 114808, 114813, 114818, 114822, 114826, 114830, 114835, 114841, 114847, 114851, 114855, 114861, 114866, 114870, 114876, 114882, 114886, 114890, -1, 114895, 114899, 114903, 114908, 114914, 114920, 114925, 114929, 114934, 114938, 114943, 114948, 114954, 114960, 114964, 114970, 114974, -1, 114979, 114983, 114987, 114993, 114999, 115005, 115011, 115017, 115023, 115029, 115036, 115042, 115048, 115054, 115060, 115066, 115072, 115077, 115082, 115087, 115091, 115097, 115103, 115108, 115112, 115118, 115123, 115127, 115131, 115137, 115142, 115148, 115153, 115157, 115161, 115165, 115170, 115175, 115180, 115185, 115190, 115194, 115199, 115203, 115207, 115211, 115215, 115219, 115224, 115228, 115233, 115238, 115243, 115248, 115254, 115259, 115265, 115271, 115276, 115281, 115286, 115290, 115295, 115299, 115303, 115308, 115314, 115322, 115326, 115332, 115338, 115343, 115349, 115354, 115360, 115366, 115372, 115378, 115384, 115388, 115395, 115401, 115406, 115410, 115414, 115419, 115424, 115430, 115434, 115439, 115444, 115448, 115454, 115458, 115462, 115468, 115474, 115478, 115484, 115488, 115493, 115498, 115502, 115507, 115512, 115517, 115521, 115525, 115530, 115536, 115541, 115546, 115552, 115557, 115561, 115567, 115574, 115580, 115584, 115589, 115595, 115601, 115605, 115609, 115615, 115620, 115625, 115630, 115634, 115639, 115645, 115649, 115654, 115660, 115665, -1, 115672, 115677, 115682, 115687, 115692, 115696, 115701, 115706, 115711, -1, 115716, 115720, 115725, 115730, 115734, 115738, 115742, 115747, 115752, 115756, 115761, 115766, 115771, 115776, 115781, 115786, 115792, 115797, 115803, 115807, 115812, 115816, 115821, 115827, 115831, 115837, 115843, 115849, 115854, 115860, 115865, 115870, 115877, 115881, 115885, 115891, 115897, 115902, 115909, 115915, 115920, 115924, 115928, 115932, 115936, 115942, 115947, 115951, 115957, 115961, 115966, 115971, 115976, 115980, 115985, 115990, 115994, 116000, 116004, 116010, 116015, 116019, 116025, 116028, 116031, 116036, 116041, 116046, 116052, 116056, 116062, 116067, 116072, 116078, 116084, 116089, 116093, 116098, 116103, 116108, 116113, 116118, 116122, 116128, 116135, 116141, 116146, 116150, 116155, 116161, 116166, 116171, 116176, 116181, 116186, 116190, 116194, 116200, 116206, 116211, 116216, 116221, 116227, 116231, 116236, 116243, 116248, 116252, 116257, 116261, 116264, 116267, 116272, 116277, 116282, 116288, 116295, 116300, 116306, 116311, 116315, 116321, 116327, 116333, 116339, 116346, 116350, 116355, 116361, 116367, 116371, 116376, 116380, 116385, 116390, 116396, 116400, 116405, 116411, 116416, 116422, 116426, 116432, 116437, 116443, 116448, 116452, 116457, 116462, 116468, 116472, 116478, 116484, 116489, 116496, 116502, 116506, 116510, 116515, 116519, 116524, 116528, 116533, 116539, 116545, 116549, 116553, 116559, 116563, 116569, 116574, 116579, 116585, 116590, 116596, 116603, 116608, 116612, 116617, 116622, 116627, 116632, 116638, 116644, 116648, 116652, 116658, 116661, 116668, 116673, 116676, 116681, 116686, 116692, 116698, 116703, 116708, 116714, 116718, 116723, 116729, 116735, 116741, 116749, 116753, 116758, 116764, 116770, 116774, 116778, 116783, 116788, 116793, 116799, 116805, 116809, 116813, 116817, 116822, 116828, 116833, 116838, 116843, 116848, 116853, 116858, 116862, 116868, 116874, 116880, 116885, 116891, 116897, 116903, -1, 116909, 116913, 116919, 116925, 116931, 116937, 116943, 116949, 116953, 116957, 116961, 116966, 116971, 116976, 116982, 116988, 116994, 116999, 117004, 117009, 117014, 117019, 117024, 117029, 117033, 117039, 117043, 117048, 117054, 117059, 117066, 117071, 117076, 117080, 117086, 117091, 117096, 117101, 117106, 117111, 117116, 117121, 117125, 117131, 117135, 117140, 117145, 117150, 117154, 117159, 117163, 117168, 117173, 117178, 117183, 117188, 117194, 117200, 117205, 117211, 117216, 117222, 117226, 117233, 117239, 117245, 117249, 117254, 117258, 117262, 117267, 117271, 117276, 117279, 117283, 117288, 117293, 117297, 117301, 117306, 117311, 117316, 117321, 117326, 117332, 117338, 117344, 117350, 117354, 117358, 117363, 117368, 117375, 117380, 117386, 117389, 117395, 117400, 117404, 117411, 117416, 117421, 117427, 117432, 117436, 117442, 117447, 117452, 117456, 117461, 117467, 117473, 117477, 117482, 117486, 117491, 117496, 117501, 117506, 117510, 117515, 117519, 117525, 117530, 117534, 117538, 117543, 117548, 117553, 117558, 117565, 117571, 117576, 117581, 117585, 117589, 117594, 117601, 117607, 117612, 117617, 117624, 117630, 117635, 117640, 117646, 117651, 117655, 117661, 117668, 117673, 117679, 117685, 117690, 117696, 117700, 117705, 117711, 117716, 117720, 117725, 117729, 117734, 117739, 117744, 117750, 117755, 117760, 117764, 117769, 117773, 117777, 117782, 117789, 117794, 117798, 117804, 117808, 117814, 117818, 117823, 117826, 117830, 117835, 117840, 117845, 117850, 117856, 117862, 117867, 117872, 117876, 117881, 117887, 117891, 117895, 117899, 117904, 117909, 117914, 117919, 117924, 117931, 117937, 117943, 117948, 117953, 117960, 117965, 117969, 117973, 117977, 117981, 117985, 117990, 117995, 118001, 118005, 118009, 118013, 118019, 118024, 118029, 118034, 118039, 118045, 118049, 118053, 118059, 118064, 118068, 118073, 118078, 118083, 118088, 118093, 118098, 118103, 118108, 118112, 118116, 118121, 118126, 118131, 118136, 118140, 118145, 118149, 118154, 118160, 118165, 118169, -1, 118173, 118177, 118184, 118188, 118194, 118200, 118206, 118212, 118216, 118221, 118225, 118229, 118233, 118239, 118244, 118250, 118256, 118261, 118265, 118271, 118276, 118280, 118285, 118290, 118294, 118299, 118303, 118309, 118314, 118319, 118325, 118331, 118337, 118342, 118348, 118354, 118361, 118365, 118370, 118374, 118379, 118384, 118389, 118395, 118400, 118405, 118410, 118414, 118420, 118425, 118431, 118438, 118443, 118447, 118453, 118458, 118463, 118466, 118473, 118478, 118482, 118486, 118492, 118498, 118503, 118509, 118514, 118519, 118524, 118530, 118534, 118538, 118542, 118547, 118552, 118557, 118562, 118567, 118572, 118576, 118582, 118588, 118594, 118600, 118605, 118612, 118618, 118623, 118627, 118631, 118637, 118642, 118648, 118653, 118658, 118662, 118667, 118672, 118677, 118682, 118688, 118695, 118700, 118705, 118711, 118717, 118721, 118726, 118730, 118735, 118741, 118746, 118751, 118757, 118761, 118766, 118774, 118778, 118782, 118787, 118794, 118798, 118802, 118807, 118813, 118819, 118823, 118827, 118832, 118836, 118840, 118845, 118849, 118853, 118859, 118864, 118870, 118875, 118880, 118885, 118891, 118896, 118902, 118907, 118912, 118917, 118923, 118929, 118933, 118937, 118942, 118947, 118952, 118957, 118963, 118967, 118973, 118980, 118984, 118992, 118996, 119000, 119006, 119010, 119016, 119020, 119024, 119029, 119034, 119039, 119043, 119047, 119051, 119055, 119061, 119066, 119070, 119074, 119078, 119083, 119088, 119093, 119097, 119102, 119106, 119111, 119117, 119121, 119126, 119132, 119137, 119142, 119147, 119153, 119159, 119163, 119170, 119175, 119180, 119185, 119190, 119194, 119198, 119202, 119208, 119214, 119218, 119223, 119229, 119234, 119239, 119244, 119248, 119252, 119257, 119263, 119269, 119275, 119280, 119286, 119292, 119297, 119301, 119309, 119316, 119320, 119325, 119330, 119335, 119339, 119344, 119348, 119353, 119358, 119363, 119368, 119372, 119377, 119381, 119385, 119389, 119394, 119400, 119405, 119410, 119414, 119419, 119425, 119431, 119435, 119439, 119443, 119447, 119452, 119457, 119461, 119467, 119473, 119477, 119481, 119487, 119492, 119496, 119502, 119507, 119512, 119516, 119522, 119527, 119531, 119536, 119542, 119546, 119551, 119556, 119562, 119567, 119573, 119578, 119583, 119589, 119594, 119599, 119603, 119609, 119614, 119619, 119624, 119629, 119634, 119639, 119643, 119649, 119655, 119659, 119663, 119668, 119672, 119677, 119681, 119686, 119690, 119694, 119700, 119704, 119710, 119715, 119720, 119724, 119730, 119736, 119742, 119747, 119751, 119755, 119760, 119766, 119770, 119776, 119782, 119789, 119795, 119801, 119807, 119813, 119818, 119822, 119826, 119831, 119835, 119841, 119847, 119852, 119858, 119862, 119867, 119872, 119877, 119883, 119887, 119892, 119898, 119904, 119910, 119916, 119922, 119928, 119933, 119938, 119944, 119949, 119954, 119959, 119965, 119969, 119974, 119979, 119984, 119989, 119994, 120001, 120005, 120009, 120013, 120019, 120023, 120029, 120034, 120040, 120045, 120050, 120057, 120061, 120066, 120071, 120075, 120079, 120084, 120089, 120094, 120098, 120102, 120107, 120111, 120116, 120122, 120129, 120133, 120138, 120142, 120148, 120155, 120160, 120166, 120171, 120177, 120181, 120185, 120189, 120193, 120199, 120204, 120210, 120214, 120219, 120224, 120228, 120233, 120239, 120244, 120250, 120254, 120258, 120261, 120264, 120269, 120275, 120280, 120284, 120289, 120295, 120300, 120306, 120310, 120314, 120319, 120323, 120327, 120331, 120337, 120342, 120346, 120351, 120359, 120365, 120369, 120374, 120378, 120383, 120387, 120393, 120398, 120402, 120407, 120413, 120417, 120421, 120425, 120430, 120434, 120440, 120444, 120449, 120454, 120458, 120464, 120470, 120475, 120480, 120484, 120489, 120493, 120497, 120505, -1, 120511, 120515, 120520, 120525, 120530, 120537, 120541, 120545, 120550, 120556, 120561, 120566, 120571, 120576, 120582, 120586, 120590, 120596, 120601, 120607, 120611, 120616, 120622, 120626, 120631, 120637, 120643, 120648, 120654, 120659, 120664, 120668, 120673, 120679, 120683, 120688, 120693, 120697, 120702, 120707, 120712, 120718, 120722, 120726, 120731, 120737, 120741, 120746, 120751, 120755, 120760, 120765, 120770, 120776, 120782, 120789, 120800, 120810, 120816, 120820, 120824, 120828, 120834, 120841, 120846, 120852, 120856, 120862, 120867, 120871, 120877, 120883, 120888, 120893, 120897, 120901, 120906, 120910, 120915, 120922, 120928, 120933, 120939, 120944, 120948, 120954, 120960, 120965, 120971, 120979, 120986, 120995, 121000, 121006, 121011, 121016, 121021, 121027, 121032, 121036, 121042, 121046, 121052, 121058, 121064, 121070, 121074, 121079, 121084, 121088, 121093, 121098, 121103, 121110, 121116, 121121, 121126, 121131, 121137, 121141, 121144, 121150, 121154, 121159, 121165, 121170, 121175, 121179, 121188, 121193, 121201, 121206, 121212, 121217, 121221, 121227, 121232, 121237, 121241, 121246, 121251, 121256, 121259, 121265, 121269, 121274, 121278, 121284, 121290, 121294, 121299, 121304, 121314, 121320, 121324, 121328, 121333, 121338, 121342, 121348, 121352, 121358, 121363, 121368, 121372, 121377, 121383, 121387, 121391, 121396, 121403, 121408, 121415, 121421, 121428, 121434, 121439, 121444, 121449, 121453, 121460, 121464, 121470, 121475, 121479, 121486, 121491, 121497, 121503, 121508, 121513, 121518, 121523, 121529, 121533, 121538, 121544, 121549, 121555, 121560, 121566, 121571, 121576, 121582, 121587, 121592, 121599, 121603, 121610, 121620, 121628, 121635, 121639, 121643, 121647, 121652, 121660, 121664, 121669, 121673, 121678, 121684, 121691, 121694, 121698, 121704, 121708, 121714, 121718, 121723, 121727, 121732, 121737, 121741, 121747, 121751, 121755, 121759, 121765, 121771, 121775, 121780, 121785, 121789, 121793, 121798, 121803, 121807, 121812, 121817, 121822, 121827, 121832, 121836, 121842, 121846, 121851, 121857, 121861, 121867, 121873, 121877, 121884, 121889, 121895, 121899, 121905, 121911, 121915, 121920, 121926, 121931, 121936, 121941, 121946, 121950, 121956, 121962, 121968, 121972, 121977, 121982, 121987, 121994, 121998, 122002, 122008, 122014, 122020, 122026, 122031, 122035, 122040, 122045, 122049, 122056, 122061, 122066, 122071, 122076, 122079, 122084, 122088, 122095, 122101, 122108, 122114, 122119, 122123, 122127, 122131, 122136, 122141, 122147, 122153, 122157, 122163, 122168, 122173, 122178, 122183, 122189, 122194, 122198, 122203, 122209, 122214, 122219, 122224, 122229, 122233, 122239, 122245, 122251, 122255, 122259, 122263, 122268, 122272, 122277, 122281, 122287, 122291, 122297, 122302, 122307, 122312, 122318, 122324, 122329, 122335, 122340, 122344, 122349, 122353, 122358, 122363, 122369, 122374, 122378, 122383, 122389, 122394, 122399, 122403, 122409, 122415, 122420, 122425, 122429, 122436, 122442, 122448, 122453, 122457, 122461, 122466, 122470, 122476, 122481, 122485, 122490, 122495, 122499, 122505, 122510, 122514, 122520, 122524, 122528, 122534, 122539, 122544, 122548, 122553, 122557, 122564, 122568, 122574, 122580, 122584, 122589, 122593, 122600, 122606, 122612, 122616, 122620, 122625, 122629, 122634, 122639, 122645, 122651, 122655, 122660, 122665, 122670, 122674, 122680, 122685, 122689, 122694, 122698, 122703, 122709, 122714, 122720, 122725, 122730, 122739, 122745, 122750, 122756, 122761, 122765, 122769, 122775, 122780, 122784, 122788, 122794, 122798, 122802, 122807, 122812, 122816, 122820, 122825, 122829, 122833, 122839, 122843, 122846, 122849, 122856, 122860, 122864, 122870, 122875, 122884, 122892, 122896, 122902, 122907, 122911, 122915, 122921, 122927, 122932, 122938, 122943, 122949, 122953, 122958, 122964, 122968, 122972, 122976, 122982, 122986, 122990, 122994, 123000, 123006, 123012, 123017, 123024, 123028, 123034, 123038, 123042, 123048, 123052, 123057, 123063, 123067, 123073, 123079, 123083, 123087, 123094, 123107, 123111, -1, 123116, 123121, 123127, 123132, 123136, 123141, 123145, 123149, 123153, 123156, 123161, 123166, 123170, 123176, 123180, 123185, 123189, 123195, 123201, 123206, 123211, 123215, 123221, 123226, 123230, 123234, 123243, 123249, 123255, 123259, 123265, 123271, 123277, 123283, 123288, 123292, 123297, 123303, 123308, 123313, 123318, 123322, 123326, 123332, 123338, 123343, 123347, 123351, 123355, 123360, 123365, 123369, 123373, 123379, 123384, 123388, 123392, 123396, 123400, 123404, 123410, 123415, 123421, 123427, 123431, 123438, 123443, 123447, 123452, 123456, 123460, 123465, 123470, 123476, 123482, 123487, 123491, 123499, 123505, 123509, 123513, 123518, 123523, 123529, 123533, 123539, 123543, 123548, 123552, 123558, 123562, 123569, 123574, 123580, 123586, 123591, 123596, 123601, 123607, 123613, 123617, 123622, 123627, 123631, 123637, 123641, 123647, 123653, 123658, 123662, 123666, 123671, 123676, 123680, 123684, 123688, 123693, 123697, 123703, 123707, 123713, 123717, 123723, 123728, 123732, 123738, 123744, 123748, 123753, 123757, 123762, 123767, 123771, 123775, 123781, 123789, 123794, 123800, 123806, 123810, 123814, 123820, 123826, 123831, 123835, 123841, 123847, 123852, 123856, 123860, 123866, 123871, 123877, 123881, 123887, 123891, 123895, 123901, 123907, 123913, 123918, 123922, 123928, 123933, 123938, 123942, 123947, 123951, 123956, 123961, 123967, 123973, 123977, 123983, 123989, 123993, 123997, 124001, 124007, 124011, 124014, 124018, 124024, 124028, 124032, 124037, 124043, 124047, 124052, 124057, 124063, 124069, 124075, 124081, 124087, 124091, 124095, 124099, 124102, 124106, 124111, 124115, 124120, 124124, 124128, 124133, 124139, 124144, 124148, 124152, 124158, 124162, 124166, 124172, 124177, 124183, 124189, 124195, 124200, 124204, 124208, 124214, 124220, 124226, 124231, 124237, 124243, 124251, 124255, 124260, 124266, 124272, 124278, 124283, 124289, 124295, 124300, 124305, 124309, 124314, 124318, 124322, 124328, 124332, 124338, 124343, 124348, 124353, 124359, 124363, 124367, 124371, 124376, 124382, 124387, 124392, 124396, 124400, 124404, 124409, 124414, 124420, 124424, 124431, 124436, 124441, 124445, 124449, 124454, 124461, 124466, 124472, 124476, 124482, 124487, 124491, 124496, 124501, 124505, 124511, 124515, 124521, 124527, 124531, 124536, 124540, 124544, 124549, 124553, 124559, 124564, 124568, -1, 124574, 124580, 124584, 124588, 124594, 124598, 124602, 124606, 124611, 124615, 124620, 124625, 124630, 124637, 124644, 124649, 124656, 124662, 124667, 124673, 124677, 124684, 124690, 124695, 124699, 124705, 124710, 124715, 124720, 124724, 124730, 124735, 124739, 124745, 124749, 124753, 124759, 124764, 124769, 124774, 124780, 124784, 124789, 124794, 124798, 124803, 124808, 124814, 124818, 124822, 124828, 124832, 124837, 124842, 124846, 124851, 124856, 124860, 124866, 124871, 124876, 124880, 124885, 124890, 124895, 124899, 124903, 124908, 124913, 124917, 124921, 124926, 124931, 124937, 124941, 124945, 124951, 124955, 124960, 124965, 124971, 124975, 124980, 124985, 124989, 124995, 125001, 125006, 125012, 125016, 125021, 125027, 125031, 125035, 125039, 125045, 125050, 125055, 125061, 125065, 125072, 125077, 125081, 125087, 125093, 125099, 125104, 125109, 125114, 125119, 125124, 125128, 125134, 125139, 125145, 125149, 125156, 125161, 125166, 125171, 125177, 125181, 125187, 125193, 125198, 125202, 125207, 125211, 125215, 125219, 125225, 125230, 125235, 125239, 125244, 125249, 125254, 125258, 125262, 125267, 125271, 125275, 125280, 125285, 125291, 125296, 125302, 125308, 125312, 125318, 125322, 125326, 125330, 125334, 125338, 125342, 125347, 125353, 125359, 125363, 125367, 125372, 125377, 125382, 125387, 125392, 125396, 125401, 125407, 125411, 125417, 125423, 125429, 125434, 125440, 125445, 125450, 125455, 125460, 125465, 125471, 125476, 125480, 125485, 125489, 125493, 125498, 125503, 125507, 125512, 125518, 125522, 125525, 125529, 125533, 125539, 125544, 125550, 125555, 125560, 125564, 125569, 125575, 125579, 125584, 125588, 125594, 125599, 125605, 125609, 125614, 125618, 125624, 125628, 125632, 125638, 125644, 125650, 125656, 125661, 125667, 125673, 125679, 125684, 125688, 125694, 125698, 125704, 125710, 125715, 125720, 125725, 125730, 125735, 125740, 125746, 125750, 125755, 125760, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 125764, 125767, 125770, 125772, 125775, 125779, 125783, 125786, 125790, 125793, 125796, 125798, 125801, 125805, 125808, 125812, 125815, 125818, 125820, 125823, 125826, 125828, 125831, 125835, 125839, 125842, 125846, 125851, 125856, 125860, 125865, 125869, 125873, 125876, 125880, 125885, 125889, 125894, 125898, 125902, 125905, 125909, 125913, 125916, 125920, 125924, 125928, 125931, 125935, 125940, 125944, 125948, 125952, 125955, 125959, 125964, 125968, 125972, 125976, 125979, 125983, 125988, 125992, 125997, 126001, 126005, 126008, 126012, 126017, 126021, 126026, 126030, 126034, 126037, 126041, 126045, 126049, 126052, 126056, 126061, 126065, 126069, 126073, 126076, 126080, 126085, 126089, 126094, 126099, 126103, 126108, 126114, 126120, 126125, 126131, 126136, 126141, 126145, 126150, 126156, 126161, 126167, 126172, 126177, 126181, 126186, 126191, 126195, 126200, 126205, 126210, 126214, 126219, 126225, 126230, 126235, 126240, 126244, 126249, 126254, 126259, 126263, 126268, 126274, 126279, 126285, 126290, 126295, 126299, 126304, 126309, 126314, 126318, 126323, 126328, 126333, 126337, 126342, 126348, 126353, 126358, 126363, 126367, 126372, 126378, 126383, 126388, 126393, 126397, 126402, 126408, 126413, 126419, 126424, 126429, 126433, 126438, 126444, 126449, 126455, 126460, 126465, 126469, 126474, 126479, 126484, 126488, 126493, 126499, 126504, 126509, 126513, 126518, 126524, 126529, 126533, 126537, 126540, 126544, 126549, 126553, 126558, 126562, 126566, 126569, 126573, 126578, 126583, 126587, 126592, 126596, 126600, 126603, 126607, 126611, 126614, 126618, 126622, 126625, 126629, 126634, 126638, 126642, 126646, 126649, 126653, 126657, 126661, 126664, 126668, 126672, 126676, 126679, 126683, 126687, 126690, 126694, 126698, 126702, 126705, 126709, 126714, 126718, 126722, 126726, 126729, 126733, 126737, 126741, 126744, 126748, 126753, 126758, 126762, 126767, 126771, 126775, 126778, 126782, 126786, 126790, 126793, 126797, 126801, 126805, 126809, 126813, 126816, 126820, 126825, 126829, 126833, 126837, 126840, 126844, 126849, 126853, 126857, 126861, 126864, 126868, 126873, 126877, 126882, 126886, 126890, 126893, 126897, 126902, 126906, 126910, 126914, 126917, 126921, 126925, 126928, 126932, 126936, 126940, 126943, 126947, 126952, 126956, 126960, 126964, 126967, 126971, 126976, 126980, 126985, 126989, 126993, 126996, 127000, 127005, 127010, 127014, 127019, 127023, 127027, 127030, 127034, 127038, 127041, 127045, 127049, 127053, 127056, 127060, 127065, 127069, 127074, 127079, 127083, 127088, 127094, 127099, 127105, 127110, 127115, 127119, 127124, 127130, 127135, 127141, 127146, 127151, 127155, 127160, 127165, 127169, 127174, 127179, 127184, 127188, 127193, 127199, 127204, 127209, 127214, 127218, 127223, 127229, 127234, 127239, 127244, 127248, 127253, 127258, 127263, 127267, 127272, 127277, 127281, 127286, 127291, 127296, 127300, 127305, 127311, 127316, 127321, 127326, 127330, 127335, 127341, 127347, 127352, 127358, 127363, 127368, 127372, 127377, 127383, 127388, 127393, 127398, 127403, 127408, 127412, 127417, 127422, 127426, 127430, 127433, 127437, 127442, 127446, 127451, 127455, 127458, 127462, 127467, 127471, 127476, 127480, 127484, 127487, 127491, 127495, 127498, 127502, 127506, 127510, 127513, 127517, 127522, 127526, 127531, 127536, 127540, 127545, 127551, 127556, 127562, 127567, 127572, 127576, 127581, 127587, 127592, 127598, 127603, 127607, 127612, 127617, 127621, 127626, 127631, 127636, 127640, 127645, 127651, 127656, 127661, 127666, 127670, 127675, 127681, 127686, 127690, 127694, 127697, 127701, 127706, 127711, 127715, 127720, 127724, 127728, 127731, 127735, 127740, 127745, 127749, 127754, 127758, 127762, 127765, 127769, 127773, 127776, 127780, 127784, 127788, 127791, 127795, 127800, 127804, 127808, 127812, 127815, 127819, 127824, 127828, 127832, 127836, 127839, 127843, 127848, 127853, 127857, 127862, 127866, 127870, 127873, 127877, 127882, 127887, 127891, 127896, 127900, 127904, 127907, 127911, 127915, 127919, 127922, 127926, 127930, 127934, 127937, 127941, 127946, 127950, 127954, 127958, 127961, 127965, 127970, 127974, 127979, 127983, 127987, 127990, 127994, 127999, 128003, 128008, 128012, 128016, 128019, 128023, 128027, 128031, 128034, 128038, 128042, 128046, 128049, 128053, 128058, 128062, 128067, 128072, 128076, 128082, 128087, 128093, 128098, 128103, 128107, 128112, 128118, 128124, 128129, 128135, 128140, 128145, 128149, 128154, 128159, 128164, 128168, 128173, 128178, 128183, 128187, 128192, 128198, 128203, 128209, 128214, 128219, 128224, 128228, 128233, 128239, 128244, 128250, 128255, 128260, 128264, 128269, 128274, 128278, 128283, 128288, 128293, 128297, 128302, 128308, 128313, 128318, 128323, 128327, 128332, 128338, 128344, 128349, 128355, 128360, 128365, 128369, 128374, 128380, 128386, 128391, 128397, 128402, 128407, 128411, 128416, 128421, 128425, 128430, 128436, 128441, 128447, 128452, 128457, 128461, 128466, 128472, 128478, 128483, 128488, 128493, 128497, 128502, 128507, 128511, 128516, 128520, 128525, 128529, 128533, 128537, 128540, 128544, 128549, 128554, 128558, 128563, 128567, 128571, 128574, 128578, 128582, 128585, 128589, 128593, 128597, 128600, 128604, 128609, 128613, 128618, 128622, 128625, 128629, 128633, 128636, 128640, 128644, 128648, 128651, 128655, 128660, 128664, 128669, 128673, 128677, 128680, 128684, 128689, 128693, 128698, 128702, 128706, 128709, 128713, 128717, 128720, 128724, 128728, 128732, 128735, 128739, 128744, 128748, 128752, 128756, 128759, 128763, 128768, 128772, 128776, 128780, 128783, 128787, 128792, 128797, 128801, 128806, 128810, 128814, 128817, 128821, 128826, 128830, 128835, 128839, 128843, 128846, 128850, 128854, 128857, 128861, 128865, 128869, 128872, 128876, 128881, 128885, 128889, 128893, 128896, 128900, 128905, 128909, 128914, 128919, 128923, 128928, 128934, 128940, 128945, 128951, 128956, 128961, 128965, 128970, 128975, 128979, 128984, 128989, 128993, 128998, 129003, 129007, 129012, 129018, 129023, 129028, 129033, 129037, 129042, 129048, 129053, 129058, 129063, 129067, 129072, 129078, 129083, 129089, 129094, 129099, 129103, 129108, 129114, 129119, 129124, 129129, 129134, 129138, 129143, 129147, 129152, 129158, 129163, 129168, 129173, 129177, 129182, 129188, 129193, 129197, 129201, 129204, 129208, 129213, 129217, 129222, 129226, 129230, 129233, 129237, 129242, 129246, 129251, 129255, 129259, 129262, 129266, 129270, 129273, 129277, 129281, 129285, 129288, 129292, 129297, 129301, 129305, 129309, 129312, 129316, 129321, 129325, 129330, 129335, 129339, 129344, 129350, 129355, 129361, 129366, 129371, 129375, 129380, 129385, 129390, 129394, 129399, 129404, 129408, 129413, 129418, 129423, 129427, 129432, 129437, 129442, 129446, 129451, 129457, 129462, 129467, 129472, 129476, 129481, 129487, 129492, 129498, 129503, 129508, 129512, 129517, 129522, 129527, 129531, 129536, 129541, 129546, 129550, 129555, 129561, 129566, 129571, 129576, 129580, 129585, 129591, 129596, 129601, 129606, 129610, 129615, 129621, 129627, 129632, 129638, 129643, 129648, 129652, 129657, 129662, 129667, 129671, 129676, 129681, 129685, 129690, 129696, 129701, 129706, 129711, 129715, 129720, 129726, 129731, 129736, 129740, 129746, 129751, 129756, 129761, 129765, 129770, 129775, 129780, 129784, 129789, 129794, 129799, 129803, 129808, 129814, 129819, 129824, 129829, 129833, 129838, 129844, 129849, 129854, 129859, 129863, 129868, 129873, 129877, 129882, 129887, 129892, 129896, 129901, 129906, 129911, 129915, 129920, 129926, 129931, 129936, 129941, 129945, 129950, 129956, 129961, 129966, 129971, 129975, 129980, 129986, 129991, 129997, 130002, 130007, 130011, 130016, 130021, 130026, 130030, 130035, 130040, 130045, 130049, 130054, 130060, 130065, 130070, 130075, 130079, 130084, 130090, 130095, 130099, 130103, 130106, 130110, 130115, 130119, 130124, 130128, 130132, 130135, 130139, 130143, 130146, 130150, 130154, 130158, 130161, 130165, 130170, 130174, 130178, 130182, 130185, 130189, 130194, 130198, 130202, 130206, 130209, 130213, 130218, 130223, 130227, 130232, 130237, 130242, 130246, 130251, 130255, 130259, 130262, 130266, 130270, 130274, 130277, 130281, 130286, 130290, 130294, 130298, 130301, 130305, 130310, 130314, 130318, 130322, 130325, 130329, 130334, 130339, 130343, 130348, 130353, 130358, 130362, 130367, 130371, 130375, 130378, 130382, 130386, 130390, 130393, 130397, 130402, 130406, 130410, 130414, 130417, 130421, 130426, 130430, 130435, 130440, 130444, 130449, 130455, 130461, 130466, 130472, 130478, 130483, 130489, 130494, 130499, 130503, 130508, 130513, 130518, 130522, 130527, 130533, 130538, 130543, 130548, 130552, 130557, 130562, 130567, 130571, 130576, 130582, 130588, 130593, 130599, 130605, 130610, 130615, 130620, 130624, 130629, 130634, 130638, 130643, 130649, 130654, 130659, 130664, 130668, 130673, 130679, 130684, 130689, 130694, 130698, 130703, 130709, 130715, 130720, 130726, 130732, 130737, 130743, 130748, 130753, 130757, 130762, 130767, 130772, 130776, 130781, 130785, 130789, 130792, 130796, 130801, 130806, 130810, 130815, 130820, 130824, 130828, 130832, 130835, 130839, 130843, 130847, 130850, 130854, 130859, 130863, 130867, 130871, 130874, 130878, 130883, 130888, 130892, 130897, 130902, 130907, 130911, 130916, 130920, 130924, 130927, 130931, 130935, 130939, 130942, 130946, 130951, 130955, 130959, 130963, 130966, 130970, 130975, -1, -1, -1, 130979, 130983, 130986, 130990, 130995, 130999, 131003, 131008, 131012, 131015, 131020, 131024, 131029, 131033, 131036, 131040, 131045, 131049, -1, -1, 131052, 131057, 131061, 131066, 131071, 131074, 131077, 131082, 131086, 131090, 131094, 131098, 131102, 131106, 131110, 131113, -1, 131116, 131120, 131124, 131129, 131132, 131135, 131140, 131144, 131147, 131152, 131156, 131161, -1, 131166, 131171, 131175, -1, 131181, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 131184, 131187, 131191, 131196, 131201, 131205, 131210, 131215, 131219, 131223, 131228, 131233, 131238, 131243, 131248, 131253, 131258, 131262, 131266, 131271, 131275, 131280, 131285, 131289, 131293, 131297, 131301, 131305, 131309, 131313, 131318, 131324, 131330, 131335, 131341, 131347, 131352, 131357, 131363, 131369, 131375, 131381, 131387, 131393, 131399, 131404, 131409, 131415, 131420, 131426, 131432, 131437, 131442, 131447, 131452, 131457, 131462, 131466, 131471, 131477, 131483, 131488, 131494, 131500, 131505, 131510, 131516, 131522, 131528, 131534, 131540, 131546, 131552, 131557, 131562, 131568, 131573, 131579, 131585, 131590, 131595, 131600, 131605, 131610, 131615, 131620, 131626, 131633, 131640, 131646, 131653, 131660, 131666, 131672, 131679, 131686, 131693, 131700, 131707, 131714, 131721, 131727, 131733, 131740, 131746, 131753, 131760, 131766, 131772, 131778, 131784, 131790, 131796, 131800, 131805, 131811, 131817, 131822, 131828, 131834, 131839, 131844, 131850, 131856, 131862, 131868, 131874, 131880, 131886, 131891, 131896, 131902, 131907, 131913, 131919, 131924, 131929, 131934, 131939, 131944, 131949, 131952, 131956, 131961, 131966, 131970, 131975, 131980, 131984, 131988, 131993, 131998, 132003, 132008, 132013, 132018, 132023, 132027, 132031, 132036, 132040, 132045, 132050, 132054, 132058, 132062, 132066, 132070, 132074, 132079, 132085, 132092, 132099, 132105, 132112, 132119, 132125, 132131, 132138, 132145, 132152, 132159, 132166, 132173, 132180, 132186, 132192, 132199, 132205, 132212, 132219, 132225, 132231, 132237, 132243, 132249, 132255, 132259, 132264, 132270, 132276, 132281, 132287, 132293, 132298, 132303, 132309, 132315, 132321, 132327, 132333, 132339, 132345, 132350, 132355, 132361, 132366, 132372, 132378, 132383, 132388, 132393, 132398, 132403, 132408, 132411, 132415, 132420, 132425, 132429, 132434, 132439, 132443, 132447, 132452, 132457, 132462, 132467, 132472, 132477, 132482, 132486, 132490, 132495, 132499, 132504, 132509, 132513, 132517, 132521, 132525, 132529, 132533, 132537, 132542, 132548, 132554, 132559, 132565, 132571, 132576, 132581, 132587, 132593, 132599, 132605, 132611, 132617, 132623, 132628, 132633, 132639, 132644, 132650, 132656, 132661, 132666, 132671, 132676, 132681, 132686, 132691, 132697, 132704, 132711, 132717, 132724, 132731, 132737, 132743, 132750, 132757, 132764, 132771, 132778, 132785, 132792, 132798, 132804, 132811, 132817, 132824, 132831, 132837, 132843, 132849, 132855, 132861, 132867, 132871, 132876, 132882, 132888, 132893, 132899, 132905, 132910, 132915, 132921, 132927, 132933, 132939, 132945, 132951, 132957, 132962, 132967, 132973, 132978, 132984, 132990, 132995, 133000, 133005, 133010, 133015, 133020, 133024, 133029, 133035, 133041, 133046, 133052, 133058, 133063, 133068, 133074, 133080, 133086, 133092, 133098, 133104, 133110, 133115, 133120, 133126, 133131, 133137, 133143, 133148, 133153, 133158, 133163, 133168, 133173, 133176, 133180, 133185, 133190, 133194, 133199, 133204, 133208, 133212, 133217, 133222, 133227, 133232, 133237, 133242, 133247, 133251, 133255, 133260, 133264, 133269, 133274, 133278, 133282, 133286, 133290, 133294, 133298, 133303, 133309, 133316, 133323, 133329, 133336, 133343, 133349, 133355, 133362, 133369, 133376, 133383, 133390, 133397, 133404, 133410, 133416, 133423, 133429, 133436, 133443, 133449, 133455, 133461, 133467, 133473, 133479, 133483, 133488, 133494, 133500, 133505, 133511, 133517, 133522, 133527, 133533, 133539, 133545, 133551, 133557, 133563, 133569, 133574, 133579, 133585, 133590, 133596, 133602, 133607, 133612, 133617, 133622, 133627, 133632, 133636, 133641, 133647, 133653, 133658, 133664, 133670, 133675, 133680, 133686, 133692, 133698, 133704, 133710, 133716, 133722, 133727, 133732, 133738, 133743, 133749, 133755, 133760, 133765, 133770, 133775, 133780, 133785, 133789, 133794, 133800, 133806, 133811, 133817, 133823, 133828, 133833, 133839, 133845, 133851, 133857, 133863, 133869, 133875, 133880, 133885, 133891, 133896, 133902, 133908, 133913, 133918, 133923, 133928, 133933, 133938, 133942, 133947, 133953, 133959, 133964, 133970, 133976, 133981, 133986, 133992, 133998, 134004, 134010, 134016, 134022, 134028, 134033, 134038, 134044, 134049, 134055, 134061, 134066, 134071, 134076, 134081, 134086, 134091, 134095, 134100, 134106, 134112, 134117, 134123, 134129, 134134, 134139, 134145, 134151, 134157, 134163, 134169, 134175, 134181, 134186, 134191, 134197, 134202, 134208, 134214, 134219, 134224, 134229, 134234, 134239, 134244, 134247, 134251, 134256, 134261, 134265, 134270, 134275, 134279, 134283, 134288, 134293, 134298, 134303, 134308, 134313, 134318, 134322, 134326, 134331, 134335, 134340, 134345, 134349, 134353, 134357, 134361, 134365, 134369, 134373, 134378, 134384, 134390, 134395, 134401, 134407, 134412, 134417, 134423, 134429, 134435, 134441, 134447, 134453, 134459, 134464, 134469, 134475, 134480, 134486, 134492, 134497, 134502, 134507, 134512, 134517, 134522, 134527, 134533, 134540, 134547, 134553, 134560, 134567, 134573, 134579, 134586, 134593, 134600, 134607, 134614, 134621, 134628, 134634, 134640, 134647, 134653, 134660, 134667, 134673, 134679, 134685, 134691, 134697, 134703, 134708, 134714, 134721, 134728, 134734, 134741, 134748, 134754, 134760, 134767, 134774, 134781, 134788, 134795, 134802, 134809, 134815, 134821, 134828, 134834, 134841, 134848, 134854, 134860, 134866, 134872, 134878, 134884, 134890, 134897, 134905, 134913, 134920, 134928, 134936, 134943, 134950, 134958, 134966, 134974, 134982, 134990, 134998, 135006, 135013, 135020, 135028, 135035, 135043, 135051, 135058, 135065, 135072, 135079, 135086, 135093, 135098, 135104, 135111, 135118, 135124, 135131, 135138, 135144, 135150, 135157, 135164, 135171, 135178, 135185, 135192, 135199, 135205, 135211, 135218, 135224, 135231, 135238, 135244, 135250, 135256, 135262, 135268, 135274, 135278, 135283, 135289, 135295, 135300, 135306, 135312, 135317, 135322, 135328, 135334, 135340, 135346, 135352, 135358, 135364, 135369, 135374, 135380, 135385, 135391, 135397, 135402, 135407, 135412, 135417, 135422, 135427, 135433, 135440, 135448, 135456, 135463, 135471, 135479, 135486, 135493, 135501, 135509, 135517, 135525, 135533, 135541, 135549, 135556, 135563, 135571, 135578, 135586, 135594, 135601, 135608, 135615, 135622, 135629, 135636, 135641, 135647, 135654, 135661, 135667, 135674, 135681, 135687, 135693, 135700, 135707, 135714, 135721, 135728, 135735, 135742, 135748, 135754, 135761, 135767, 135774, 135781, 135787, 135793, 135799, 135805, 135811, 135817, 135821, 135826, 135832, 135838, 135843, 135849, 135855, 135860, 135865, 135871, 135877, 135883, 135889, 135895, 135901, 135907, 135912, 135917, 135923, 135928, 135934, 135940, 135945, 135950, 135955, 135960, 135965, 135970, 135975, 135981, 135988, 135995, 136001, 136008, 136015, 136021, 136027, 136034, 136041, 136048, 136055, 136062, 136069, 136076, 136082, 136088, 136095, 136101, 136108, 136115, 136121, 136127, 136133, 136139, 136145, 136151, 136157, 136164, 136172, 136180, 136187, 136195, 136203, 136210, 136217, 136225, 136233, 136241, 136249, 136257, 136265, 136273, 136280, 136287, 136295, 136302, 136310, 136318, 136325, 136332, 136339, 136346, 136353, 136360, 136365, 136371, 136378, 136385, 136391, 136398, 136405, 136411, 136417, 136424, 136431, 136438, 136445, 136452, 136459, 136466, 136472, 136478, 136485, 136491, 136498, 136505, 136511, 136517, 136523, 136529, 136535, 136541, 136546, 136552, 136559, 136566, 136572, 136579, 136586, 136592, 136598, 136605, 136612, 136619, 136626, 136633, 136640, 136647, 136653, 136659, 136666, 136672, 136679, 136686, 136692, 136698, 136704, 136710, 136716, 136722, 136726, 136731, 136737, 136743, 136748, 136754, 136760, 136765, 136770, 136776, 136782, 136788, 136794, 136800, 136806, 136812, 136817, 136822, 136828, 136833, 136839, 136845, 136850, 136855, 136860, 136865, 136870, 136875, 136881, 136888, 136896, 136904, 136911, 136919, 136927, 136934, 136941, 136949, 136957, 136965, 136973, 136981, 136989, 136997, 137004, 137011, 137019, 137026, 137034, 137042, 137049, 137056, 137063, 137070, 137077, 137084, 137089, 137095, 137102, 137109, 137115, 137122, 137129, 137135, 137141, 137148, 137155, 137162, 137169, 137176, 137183, 137190, 137196, 137202, 137209, 137215, 137222, 137229, 137235, 137241, 137247, 137253, 137259, 137265, 137270, 137276, 137283, 137290, 137296, 137303, 137310, 137316, 137322, 137329, 137336, 137343, 137350, 137357, 137364, 137371, 137377, 137383, 137390, 137396, 137403, 137410, 137416, 137422, 137428, 137434, 137440, 137446, 137451, 137457, 137464, 137471, 137477, 137484, 137491, 137497, 137503, 137510, 137517, 137524, 137531, 137538, 137545, 137552, 137558, 137564, 137571, 137577, 137584, 137591, 137597, 137603, 137609, 137615, 137621, 137627, 137632, 137638, 137645, 137652, 137658, 137665, 137672, 137678, 137684, 137691, 137698, 137705, 137712, 137719, 137726, 137733, 137739, 137745, 137752, 137758, 137765, 137772, 137778, 137784, 137790, 137796, 137802, 137808, 137813, 137819, 137826, 137833, 137839, 137846, 137853, 137859, 137865, 137872, 137879, 137886, 137893, 137900, 137907, 137914, 137920, 137926, 137933, 137939, 137946, 137953, 137959, 137965, 137971, 137977, 137983, 137989, 137993, 137998, 138004, 138010, 138015, 138021, 138027, 138032, 138037, 138043, 138049, 138055, 138061, 138067, 138073, 138079, 138084, 138089, 138095, 138100, 138106, 138112, 138117, 138122, 138127, 138132, 138137, 138142, 138145, 138149, 138154, 138159, 138163, 138168, 138173, 138177, 138181, 138186, 138191, 138196, 138201, 138206, 138211, 138216, 138220, 138224, 138229, 138233, 138238, 138243, 138247, 138251, 138255, 138259, 138263, 138267, 138271, 138276, 138282, 138288, 138293, 138299, 138305, 138310, 138315, 138321, 138327, 138333, 138339, 138345, 138351, 138357, 138362, 138367, 138373, 138378, 138384, 138390, 138395, 138400, 138405, 138410, 138415, 138420, 138424, 138429, 138435, 138441, 138446, 138452, 138458, 138463, 138468, 138474, 138480, 138486, 138492, 138498, 138504, 138510, 138515, 138520, 138526, 138531, 138537, 138543, 138548, 138553, 138558, 138563, 138568, 138573, 138578, 138584, 138591, 138598, 138604, 138611, 138618, 138624, 138630, 138637, 138644, 138651, 138658, 138665, 138672, 138679, 138685, 138691, 138698, 138704, 138711, 138718, 138724, 138730, 138736, 138742, 138748, 138754, 138758, 138763, 138769, 138775, 138780, 138786, 138792, 138797, 138802, 138808, 138814, 138820, 138826, 138832, 138838, 138844, 138849, 138854, 138860, 138865, 138871, 138877, 138882, 138887, 138892, 138897, 138902, 138907, 138910, 138914, 138919, 138924, 138928, 138933, 138938, 138942, 138946, 138951, 138956, 138961, 138966, 138971, 138976, 138981, 138985, 138989, 138994, 138998, 139003, 139008, 139012, 139016, 139020, 139024, 139028, 139032, 139037, 139043, 139050, 139057, 139063, 139070, 139077, 139083, 139089, 139096, 139103, 139110, 139117, 139124, 139131, 139138, 139144, 139150, 139157, 139163, 139170, 139177, 139183, 139189, 139195, 139201, 139207, 139213, 139217, 139222, 139228, 139234, 139239, 139245, 139251, 139256, 139261, 139267, 139273, 139279, 139285, 139291, 139297, 139303, 139308, 139313, 139319, 139324, 139330, 139336, 139341, 139346, 139351, 139356, 139361, 139366, 139369, 139373, 139378, 139383, 139387, 139392, 139397, 139401, 139405, 139410, 139415, 139420, 139425, 139430, 139435, 139440, 139444, 139448, 139453, 139457, 139462, 139467, 139471, 139475, 139479, 139483, 139487, 139491, 139495, 139500, 139506, 139512, 139517, 139523, 139529, 139534, 139539, 139545, 139551, 139557, 139563, 139569, 139575, 139581, 139586, 139591, 139597, 139602, 139608, 139614, 139619, 139624, 139629, 139634, 139639, 139644, 139649, 139655, 139662, 139669, 139675, 139682, 139689, 139695, 139701, 139708, 139715, 139722, 139729, 139736, 139743, 139750, 139756, 139762, 139769, 139775, 139782, 139789, 139795, 139801, 139807, 139813, 139819, 139825, 139829, 139834, 139840, 139846, 139851, 139857, 139863, 139868, 139873, 139879, 139885, 139891, 139897, 139903, 139909, 139915, 139920, 139925, 139931, 139936, 139942, 139948, 139953, 139958, 139963, 139968, 139973, 139978, 139982, 139987, 139993, 139999, 140004, 140010, 140016, 140021, 140026, 140032, 140038, 140044, 140050, 140056, 140062, 140068, 140073, 140078, 140084, 140089, 140095, 140101, 140106, 140111, 140116, 140121, 140126, 140131, 140134, 140138, 140143, 140148, 140152, 140157, 140162, 140166, 140170, 140175, 140180, 140185, 140190, 140195, 140200, 140205, 140209, 140213, 140218, 140222, 140227, 140232, 140236, 140240, 140244, 140248, 140252, 140256, 140261, 140267, 140274, 140281, 140287, 140294, 140301, 140307, 140313, 140320, 140327, 140334, 140341, 140348, 140355, 140362, 140368, 140374, 140381, 140387, 140394, 140401, 140407, 140413, 140419, 140425, 140431, 140437, 140441, 140446, 140452, 140458, 140463, 140469, 140475, 140480, 140485, 140491, 140497, 140503, 140509, 140515, 140521, 140527, 140532, 140537, 140543, 140548, 140554, 140560, 140565, 140570, 140575, 140580, 140585, 140590, 140594, 140599, 140605, 140611, 140616, 140622, 140628, 140633, 140638, 140644, 140650, 140656, 140662, 140668, 140674, 140680, 140685, 140690, 140696, 140701, 140707, 140713, 140718, 140723, 140728, 140733, 140738, 140743, 140747, 140752, 140758, 140764, 140769, 140775, 140781, 140786, 140791, 140797, 140803, 140809, 140815, 140821, 140827, 140833, 140838, 140843, 140849, 140854, 140860, 140866, 140871, 140876, 140881, 140886, 140891, 140896, 140900, 140905, 140911, 140917, 140922, 140928, 140934, 140939, 140944, 140950, 140956, 140962, 140968, 140974, 140980, 140986, 140991, 140996, 141002, 141007, 141013, 141019, 141024, 141029, 141034, 141039, 141044, 141049, 141053, 141058, 141064, 141070, 141075, 141081, 141087, 141092, 141097, 141103, 141109, 141115, 141121, 141127, 141133, 141139, 141144, 141149, 141155, 141160, 141166, 141172, 141177, 141182, 141187, 141192, 141197, 141202, 141205, 141209, 141214, 141219, 141223, 141228, 141233, 141237, 141241, 141246, 141251, 141256, 141261, 141266, 141271, 141276, 141280, 141284, 141289, 141293, 141298, 141303, 141307, 141311, 141315, 141319, 141323, 141327, 141330, 141334, 141339, 141344, 141348, 141353, 141358, 141362, 141366, 141371, 141376, 141381, 141386, 141391, 141396, 141401, 141405, 141409, 141414, 141418, 141423, 141428, 141432, 141436, 141440, 141444, 141448, 141452, 141456, 141461, 141467, 141473, 141478, 141484, 141490, 141495, 141500, 141506, 141512, 141518, 141524, 141530, 141536, 141542, 141547, 141552, 141558, 141563, 141569, 141575, 141580, 141585, 141590, 141595, 141600, 141605, 141609, 141614, 141620, 141626, 141631, 141637, 141643, 141648, 141653, 141659, 141665, 141671, 141677, 141683, 141689, 141695, 141700, 141705, 141711, 141716, 141722, 141728, 141733, 141738, 141743, 141748, 141753, 141758, 141763, 141769, 141776, 141783, 141789, 141796, 141803, 141809, 141815, 141822, 141829, 141836, 141843, 141850, 141857, 141864, 141870, 141876, 141883, 141889, 141896, 141903, 141909, 141915, 141921, 141927, 141933, 141939, 141943, 141948, 141954, 141960, 141965, 141971, 141977, 141982, 141987, 141993, 141999, 142005, 142011, 142017, 142023, 142029, 142034, 142039, 142045, 142050, 142056, 142062, 142067, 142072, 142077, 142082, 142087, 142092, 142095, 142099, 142104, 142109, 142113, 142118, 142123, 142127, 142131, 142136, 142141, 142146, 142151, 142156, 142161, 142166, 142170, 142174, 142179, 142183, 142188, 142193, 142197, 142201, 142205, 142209, 142213, 142217, 142222, 142228, 142235, 142242, 142248, 142255, 142262, 142268, 142274, 142281, 142288, 142295, 142302, 142309, 142316, 142323, 142329, 142335, 142342, 142348, 142355, 142362, 142368, 142374, 142380, 142386, 142392, 142398, 142402, 142407, 142413, 142419, 142424, 142430, 142436, 142441, 142446, 142452, 142458, 142464, 142470, 142476, 142482, 142488, 142493, 142498, 142504, 142509, 142515, 142521, 142526, 142531, 142536, 142541, 142546, 142551, 142554, 142558, 142563, 142568, 142572, 142577, 142582, 142586, 142590, 142595, 142600, 142605, 142610, 142615, 142620, 142625, 142629, 142633, 142638, 142642, 142647, 142652, 142656, 142660, 142664, 142668, 142672, 142676, 142680, 142685, 142691, 142697, 142702, 142708, 142714, 142719, 142724, 142730, 142736, 142742, 142748, 142754, 142760, 142766, 142771, 142776, 142782, 142787, 142793, 142799, 142804, 142809, 142814, 142819, 142824, 142829, 142834, 142840, 142847, 142854, 142860, 142867, 142874, 142880, 142886, 142893, 142900, 142907, 142914, 142921, 142928, 142935, 142941, 142947, 142954, 142960, 142967, 142974, 142980, 142986, 142992, 142998, 143004, 143010, 143014, 143019, 143025, 143031, 143036, 143042, 143048, 143053, 143058, 143064, 143070, 143076, 143082, 143088, 143094, 143100, 143105, 143110, 143116, 143121, 143127, 143133, 143138, 143143, 143148, 143153, 143158, 143163, 143167, 143172, 143178, 143184, 143189, 143195, 143201, 143206, 143211, 143217, 143223, 143229, 143235, 143241, 143247, 143253, 143258, 143263, 143269, 143274, 143280, 143286, 143291, 143296, 143301, 143306, 143311, 143316, 143319, 143323, 143328, 143333, 143337, 143342, 143347, 143351, 143355, 143360, 143365, 143370, 143375, 143380, 143385, 143390, 143394, 143398, 143403, 143407, 143412, 143417, 143421, 143425, 143429, 143433, 143437, 143441, 143446, 143452, 143459, 143466, 143472, 143479, 143486, 143492, 143498, 143505, 143512, 143519, 143526, 143533, 143540, 143547, 143553, 143559, 143566, 143572, 143579, 143586, 143592, 143598, 143604, 143610, 143616, 143622, 143626, 143631, 143637, 143643, 143648, 143654, 143660, 143665, 143670, 143676, 143682, 143688, 143694, 143700, 143706, 143712, 143717, 143722, 143728, 143733, 143739, 143745, 143750, 143755, 143760, 143765, 143770, 143775, 143779, 143784, 143790, 143796, 143801, 143807, 143813, 143818, 143823, 143829, 143835, 143841, 143847, 143853, 143859, 143865, 143870, 143875, 143881, 143886, 143892, 143898, 143903, 143908, 143913, 143918, 143923, 143928, 143932, 143937, 143943, 143949, 143954, 143960, 143966, 143971, 143976, 143982, 143988, 143994, 144000, 144006, 144012, 144018, 144023, 144028, 144034, 144039, 144045, 144051, 144056, 144061, 144066, 144071, 144076, 144081, 144085, 144090, 144096, 144102, 144107, 144113, 144119, 144124, 144129, 144135, 144141, 144147, 144153, 144159, 144165, 144171, 144176, 144181, 144187, 144192, 144198, 144204, 144209, 144214, 144219, 144224, 144229, 144234, 144238, 144243, 144249, 144255, 144260, 144266, 144272, 144277, 144282, 144288, 144294, 144300, 144306, 144312, 144318, 144324, 144329, 144334, 144340, 144345, 144351, 144357, 144362, 144367, 144372, 144377, 144382, 144387, 144390, 144394, 144399, 144404, 144408, 144413, 144418, 144422, 144426, 144431, 144436, 144441, 144446, 144451, 144456, 144461, 144465, 144469, 144474, 144478, 144483, 144488, 144492, 144496, 144500, 144504, 144508, 144512, 144516, 144521, 144527, 144533, 144538, 144544, 144550, 144555, 144560, 144566, 144572, 144578, 144584, 144590, 144596, 144602, 144607, 144612, 144618, 144623, 144629, 144635, 144640, 144645, 144650, 144655, 144660, 144665, 144670, 144676, 144683, 144690, 144696, 144703, 144710, 144716, 144722, 144729, 144736, 144743, 144750, 144757, 144764, 144771, 144777, 144783, 144790, 144796, 144803, 144810, 144816, 144822, 144828, 144834, 144840, 144846, 144851, 144857, 144864, 144871, 144877, 144884, 144891, 144897, 144903, 144910, 144917, 144924, 144931, 144938, 144945, 144952, 144958, 144964, 144971, 144977, 144984, 144991, 144997, 145003, 145009, 145015, 145021, 145027, 145033, 145040, 145048, 145056, 145063, 145071, 145079, 145086, 145093, 145101, 145109, 145117, 145125, 145133, 145141, 145149, 145156, 145163, 145171, 145178, 145186, 145194, 145201, 145208, 145215, 145222, 145229, 145236, 145241, 145247, 145254, 145261, 145267, 145274, 145281, 145287, 145293, 145300, 145307, 145314, 145321, 145328, 145335, 145342, 145348, 145354, 145361, 145367, 145374, 145381, 145387, 145393, 145399, 145405, 145411, 145417, 145421, 145426, 145432, 145438, 145443, 145449, 145455, 145460, 145465, 145471, 145477, 145483, 145489, 145495, 145501, 145507, 145512, 145517, 145523, 145528, 145534, 145540, 145545, 145550, 145555, 145560, 145565, 145570, 145576, 145583, 145591, 145599, 145606, 145614, 145622, 145629, 145636, 145644, 145652, 145660, 145668, 145676, 145684, 145692, 145699, 145706, 145714, 145721, 145729, 145737, 145744, 145751, 145758, 145765, 145772, 145779, 145784, 145790, 145797, 145804, 145810, 145817, 145824, 145830, 145836, 145843, 145850, 145857, 145864, 145871, 145878, 145885, 145891, 145897, 145904, 145910, 145917, 145924, 145930, 145936, 145942, 145948, 145954, 145960, 145964, 145969, 145975, 145981, 145986, 145992, 145998, 146003, 146008, 146014, 146020, 146026, 146032, 146038, 146044, 146050, 146055, 146060, 146066, 146071, 146077, 146083, 146088, 146093, 146098, 146103, 146108, 146113, 146118, 146124, 146131, 146138, 146144, 146151, 146158, 146164, 146170, 146177, 146184, 146191, 146198, 146205, 146212, 146219, 146225, 146231, 146238, 146244, 146251, 146258, 146264, 146270, 146276, 146282, 146288, 146294, 146300, 146307, 146315, 146323, 146330, 146338, 146346, 146353, 146360, 146368, 146376, 146384, 146392, 146400, 146408, 146416, 146423, 146430, 146438, 146445, 146453, 146461, 146468, 146475, 146482, 146489, 146496, 146503, 146508, 146514, 146521, 146528, 146534, 146541, 146548, 146554, 146560, 146567, 146574, 146581, 146588, 146595, 146602, 146609, 146615, 146621, 146628, 146634, 146641, 146648, 146654, 146660, 146666, 146672, 146678, 146684, 146689, 146695, 146702, 146709, 146715, 146722, 146729, 146735, 146741, 146748, 146755, 146762, 146769, 146776, 146783, 146790, 146796, 146802, 146809, 146815, 146822, 146829, 146835, 146841, 146847, 146853, 146859, 146865, 146869, 146874, 146880, 146886, 146891, 146897, 146903, 146908, 146913, 146919, 146925, 146931, 146937, 146943, 146949, 146955, 146960, 146965, 146971, 146976, 146982, 146988, 146993, 146998, 147003, 147008, 147013, 147018, 147024, 147031, 147039, 147047, 147054, 147062, 147070, 147077, 147084, 147092, 147100, 147108, 147116, 147124, 147132, 147140, 147147, 147154, 147162, 147169, 147177, 147185, 147192, 147199, 147206, 147213, 147220, 147227, 147232, 147238, 147245, 147252, 147258, 147265, 147272, 147278, 147284, 147291, 147298, 147305, 147312, 147319, 147326, 147333, 147339, 147345, 147352, 147358, 147365, 147372, 147378, 147384, 147390, 147396, 147402, 147408, 147413, 147419, 147426, 147433, 147439, 147446, 147453, 147459, 147465, 147472, 147479, 147486, 147493, 147500, 147507, 147514, 147520, 147526, 147533, 147539, 147546, 147553, 147559, 147565, 147571, 147577, 147583, 147589, 147594, 147600, 147607, 147614, 147620, 147627, 147634, 147640, 147646, 147653, 147660, 147667, 147674, 147681, 147688, 147695, 147701, 147707, 147714, 147720, 147727, 147734, 147740, 147746, 147752, 147758, 147764, 147770, 147775, 147781, 147788, 147795, 147801, 147808, 147815, 147821, 147827, 147834, 147841, 147848, 147855, 147862, 147869, 147876, 147882, 147888, 147895, 147901, 147908, 147915, 147921, 147927, 147933, 147939, 147945, 147951, 147956, 147962, 147969, 147976, 147982, 147989, 147996, 148002, 148008, 148015, 148022, 148029, 148036, 148043, 148050, 148057, 148063, 148069, 148076, 148082, 148089, 148096, 148102, 148108, 148114, 148120, 148126, 148132, 148136, 148141, 148147, 148153, 148158, 148164, 148170, 148175, 148180, 148186, 148192, 148198, 148204, 148210, 148216, 148222, 148227, 148232, 148238, 148243, 148249, 148255, 148260, 148265, 148270, 148275, 148280, 148285, 148288, 148292, 148297, 148302, 148306, 148311, 148316, 148320, 148324, 148329, 148334, 148339, 148344, 148349, 148354, 148359, 148363, 148367, 148372, 148376, 148381, 148386, 148390, 148394, 148398, 148402, 148406, 148410, 148414, 148419, 148425, 148431, 148436, 148442, 148448, 148453, 148458, 148464, 148470, 148476, 148482, 148488, 148494, 148500, 148505, 148510, 148516, 148521, 148527, 148533, 148538, 148543, 148548, 148553, 148558, 148563, 148567, 148572, 148578, 148584, 148589, 148595, 148601, 148606, 148611, 148617, 148623, 148629, 148635, 148641, 148647, 148653, 148658, 148663, 148669, 148674, 148680, 148686, 148691, 148696, 148701, 148706, 148711, 148716, 148721, 148727, 148734, 148741, 148747, 148754, 148761, 148767, 148773, 148780, 148787, 148794, 148801, 148808, 148815, 148822, 148828, 148834, 148841, 148847, 148854, 148861, 148867, 148873, 148879, 148885, 148891, 148897, 148901, 148906, 148912, 148918, 148923, 148929, 148935, 148940, 148945, 148951, 148957, 148963, 148969, 148975, 148981, 148987, 148992, 148997, 149003, 149008, 149014, 149020, 149025, 149030, 149035, 149040, 149045, 149050, 149053, 149057, 149062, 149067, 149071, 149076, 149081, 149085, 149089, 149094, 149099, 149104, 149109, 149114, 149119, 149124, 149128, 149132, 149137, 149141, 149146, 149151, 149155, 149159, 149163, 149167, 149171, 149175, 149180, 149186, 149193, 149200, 149206, 149213, 149220, 149226, 149232, 149239, 149246, 149253, 149260, 149267, 149274, 149281, 149287, 149293, 149300, 149306, 149313, 149320, 149326, 149332, 149338, 149344, 149350, 149356, 149360, 149365, 149371, 149377, 149382, 149388, 149394, 149399, 149404, 149410, 149416, 149422, 149428, 149434, 149440, 149446, 149451, 149456, 149462, 149467, 149473, 149479, 149484, 149489, 149494, 149499, 149504, 149509, 149512, 149516, 149521, 149526, 149530, 149535, 149540, 149544, 149548, 149553, 149558, 149563, 149568, 149573, 149578, 149583, 149587, 149591, 149596, 149600, 149605, 149610, 149614, 149618, 149622, 149626, 149630, 149634, 149638, 149643, 149649, 149655, 149660, 149666, 149672, 149677, 149682, 149688, 149694, 149700, 149706, 149712, 149718, 149724, 149729, 149734, 149740, 149745, 149751, 149757, 149762, 149767, 149772, 149777, 149782, 149787, 149792, 149798, 149805, 149812, 149818, 149825, 149832, 149838, 149844, 149851, 149858, 149865, 149872, 149879, 149886, 149893, 149899, 149905, 149912, 149918, 149925, 149932, 149938, 149944, 149950, 149956, 149962, 149968, 149972, 149977, 149983, 149989, 149994, 150000, 150006, 150011, 150016, 150022, 150028, 150034, 150040, 150046, 150052, 150058, 150063, 150068, 150074, 150079, 150085, 150091, 150096, 150101, 150106, 150111, 150116, 150121, 150125, 150130, 150136, 150142, 150147, 150153, 150159, 150164, 150169, 150175, 150181, 150187, 150193, 150199, 150205, 150211, 150216, 150221, 150227, 150232, 150238, 150244, 150249, 150254, 150259, 150264, 150269, 150274, 150277, 150281, 150286, 150291, 150295, 150300, 150305, 150309, 150313, 150318, 150323, 150328, 150333, 150338, 150343, 150348, 150352, 150356, 150361, 150365, 150370, 150375, 150379, 150383, 150387, 150391, 150395, 150399, 150404, 150410, 150417, 150424, 150430, 150437, 150444, 150450, 150456, 150463, 150470, 150477, 150484, 150491, 150498, 150505, 150511, 150517, 150524, 150530, 150537, 150544, 150550, 150556, 150562, 150568, 150574, 150580, 150584, 150589, 150595, 150601, 150606, 150612, 150618, 150623, 150628, 150634, 150640, 150646, 150652, 150658, 150664, 150670, 150675, 150680, 150686, 150691, 150697, 150703, 150708, 150713, 150718, 150723, 150728, 150733, 150737, 150742, 150748, 150754, 150759, 150765, 150771, 150776, 150781, 150787, 150793, 150799, 150805, 150811, 150817, 150823, 150828, 150833, 150839, 150844, 150850, 150856, 150861, 150866, 150871, 150876, 150881, 150886, 150890, 150895, 150901, 150907, 150912, 150918, 150924, 150929, 150934, 150940, 150946, 150952, 150958, 150964, 150970, 150976, 150981, 150986, 150992, 150997, 151003, 151009, 151014, 151019, 151024, 151029, 151034, 151039, 151043, 151048, 151054, 151060, 151065, 151071, 151077, 151082, 151087, 151093, 151099, 151105, 151111, 151117, 151123, 151129, 151134, 151139, 151145, 151150, 151156, 151162, 151167, 151172, 151177, 151182, 151187, 151192, 151196, 151201, 151207, 151213, 151218, 151224, 151230, 151235, 151240, 151246, 151252, 151258, 151264, 151270, 151276, 151282, 151287, 151292, 151298, 151303, 151309, 151315, 151320, 151325, 151330, 151335, 151340, 151345, 151348, 151352, 151357, 151362, 151366, 151371, 151376, 151380, 151384, 151389, 151394, 151399, 151404, 151409, 151414, 151419, 151423, 151427, 151432, 151436, 151441, 151446, 151450, 151454, 151458, 151462, 151466, 151470, 151473, 151477, 151482, 151487, 151491, 151496, 151501, 151505, 151509, 151514, 151519, 151524, 151529, 151534, 151539, 151544, 151548, 151552, 151557, 151561, 151566, 151571, 151575, 151579, 151583, 151587, 151591, 151595, 151599, 151604, 151610, 151616, 151621, 151627, 151633, 151638, 151643, 151649, 151655, 151661, 151667, 151673, 151679, 151685, 151690, 151695, 151701, 151706, 151712, 151718, 151723, 151728, 151733, 151738, 151743, 151748, 151752, 151757, 151763, 151769, 151774, 151780, 151786, 151791, 151796, 151802, 151808, 151814, 151820, 151826, 151832, 151838, 151843, 151848, 151854, 151859, 151865, 151871, 151876, 151881, 151886, 151891, 151896, 151901, 151906, 151912, 151919, 151926, 151932, 151939, 151946, 151952, 151958, 151965, 151972, 151979, 151986, 151993, 152000, 152007, 152013, 152019, 152026, 152032, 152039, 152046, 152052, 152058, 152064, 152070, 152076, 152082, 152086, 152091, 152097, 152103, 152108, 152114, 152120, 152125, 152130, 152136, 152142, 152148, 152154, 152160, 152166, 152172, 152177, 152182, 152188, 152193, 152199, 152205, 152210, 152215, 152220, 152225, 152230, 152235, 152238, 152242, 152247, 152252, 152256, 152261, 152266, 152270, 152274, 152279, 152284, 152289, 152294, 152299, 152304, 152309, 152313, 152317, 152322, 152326, 152331, 152336, 152340, 152344, 152348, 152352, 152356, 152360, 152365, 152371, 152378, 152385, 152391, 152398, 152405, 152411, 152417, 152424, 152431, 152438, 152445, 152452, 152459, 152466, 152472, 152478, 152485, 152491, 152498, 152505, 152511, 152517, 152523, 152529, 152535, 152541, 152545, 152550, 152556, 152562, 152567, 152573, 152579, 152584, 152589, 152595, 152601, 152607, 152613, 152619, 152625, 152631, 152636, 152641, 152647, 152652, 152658, 152664, 152669, 152674, 152679, 152684, 152689, 152694, 152697, 152701, 152706, 152711, 152715, 152720, 152725, 152729, 152733, 152738, 152743, 152748, 152753, 152758, 152763, 152768, 152772, 152776, 152781, 152785, 152790, 152795, 152799, 152803, 152807, 152811, 152815, 152819, 152823, 152828, 152834, 152840, 152845, 152851, 152857, 152862, 152867, 152873, 152879, 152885, 152891, 152897, 152903, 152909, 152914, 152919, 152925, 152930, 152936, 152942, 152947, 152952, 152957, 152962, 152967, 152972, 152977, 152983, 152990, 152997, 153003, 153010, 153017, 153023, 153029, 153036, 153043, 153050, 153057, 153064, 153071, 153078, 153084, 153090, 153097, 153103, 153110, 153117, 153123, 153129, 153135, 153141, 153147, 153153, 153157, 153162, 153168, 153174, 153179, 153185, 153191, 153196, 153201, 153207, 153213, 153219, 153225, 153231, 153237, 153243, 153248, 153253, 153259, 153264, 153270, 153276, 153281, 153286, 153291, 153296, 153301, 153306, 153310, 153315, 153321, 153327, 153332, 153338, 153344, 153349, 153354, 153360, 153366, 153372, 153378, 153384, 153390, 153396, 153401, 153406, 153412, 153417, 153423, 153429, 153434, 153439, 153444, 153449, 153454, 153459, 153462, 153466, 153471, 153476, 153480, 153485, 153490, 153494, 153498, 153503, 153508, 153513, 153518, 153523, 153528, 153533, 153537, 153541, 153546, 153550, 153555, 153560, 153564, 153568, 153572, 153576, 153580, 153584, 153589, 153595, 153602, 153609, 153615, 153622, 153629, 153635, 153641, 153648, 153655, 153662, 153669, 153676, 153683, 153690, 153696, 153702, 153709, 153715, 153722, 153729, 153735, 153741, 153747, 153753, 153759, 153765, 153769, 153774, 153780, 153786, 153791, 153797, 153803, 153808, 153813, 153819, 153825, 153831, 153837, 153843, 153849, 153855, 153860, 153865, 153871, 153876, 153882, 153888, 153893, 153898, 153903, 153908, 153913, 153918, 153922, 153927, 153933, 153939, 153944, 153950, 153956, 153961, 153966, 153972, 153978, 153984, 153990, 153996, 154002, 154008, 154013, 154018, 154024, 154029, 154035, 154041, 154046, 154051, 154056, 154061, 154066, 154071, 154075, 154080, 154086, 154092, 154097, 154103, 154109, 154114, 154119, 154125, 154131, 154137, 154143, 154149, 154155, 154161, 154166, 154171, 154177, 154182, 154188, 154194, 154199, 154204, 154209, 154214, 154219, 154224, 154228, 154233, 154239, 154245, 154250, 154256, 154262, 154267, 154272, 154278, 154284, 154290, 154296, 154302, 154308, 154314, 154319, 154324, 154330, 154335, 154341, 154347, 154352, 154357, 154362, 154367, 154372, 154377, 154381, 154386, 154392, 154398, 154403, 154409, 154415, 154420, 154425, 154431, 154437, 154443, 154449, 154455, 154461, 154467, 154472, 154477, 154483, 154488, 154494, 154500, 154505, 154510, 154515, 154520, 154525, 154530, 154533, 154537, 154542, 154547, 154551, 154556, 154561, 154565, 154569, 154574, 154579, 154584, 154589, 154594, 154599, 154604, 154608, 154612, 154617, 154621, 154626, 154631, 154635, 154639, 154643, 154647, 154651, 154655, 154658, 154662, 154667, 154672, 154676, 154681, 154686, 154690, 154694, 154699, 154704, 154709, 154714, 154719, 154724, 154729, 154733, 154737, 154742, 154746, 154751, 154756, 154760, 154764, 154768, 154772, 154776, 154780, 154784, 154789, 154795, 154801, 154806, 154812, 154818, 154823, 154828, 154834, 154840, 154846, 154852, 154858, 154864, 154870, 154875, 154880, 154886, 154891, 154897, 154903, 154908, 154913, 154918, 154923, 154928, 154933, 154937, 154942, 154948, 154954, 154959, 154965, 154971, 154976, 154981, 154987, 154993, 154999, 155005, 155011, 155017, 155023, 155028, 155033, 155039, 155044, 155050, 155056, 155061, 155066, 155071, 155076, 155081, 155086, 155091, 155097, 155104, 155111, 155117, 155124, 155131, 155137, 155143, 155150, 155157, 155164, 155171, 155178, 155185, 155192, 155198, 155204, 155211, 155217, 155224, 155231, 155237, 155243, 155249, 155255, 155261, 155267, 155271, 155276, 155282, 155288, 155293, 155299, 155305, 155310, 155315, 155321, 155327, 155333, 155339, 155345, 155351, 155357, 155362, 155367, 155373, 155378, 155384, 155390, 155395, 155400, 155405, 155410, 155415, 155420, 155423, 155427, 155432, 155437, 155441, 155446, 155451, 155455, 155459, 155464, 155469, 155474, 155479, 155484, 155489, 155494, 155498, 155502, 155507, 155511, 155516, 155521, 155525, 155529, 155533, 155537, 155541, 155545, 155550, 155556, 155563, 155570, 155576, 155583, 155590, 155596, 155602, 155609, 155616, 155623, 155630, 155637, 155644, 155651, 155657, 155663, 155670, 155676, 155683, 155690, 155696, 155702, 155708, 155714, 155720, 155726, 155730, 155735, 155741, 155747, 155752, 155758, 155764, 155769, 155774, 155780, 155786, 155792, 155798, 155804, 155810, 155816, 155821, 155826, 155832, 155837, 155843, 155849, 155854, 155859, 155864, 155869, 155874, 155879, 155882, 155886, 155891, 155896, 155900, 155905, 155910, 155914, 155918, 155923, 155928, 155933, 155938, 155943, 155948, 155953, 155957, 155961, 155966, 155970, 155975, 155980, 155984, 155988, 155992, 155996, 156000, 156004, 156008, 156013, 156019, 156025, 156030, 156036, 156042, 156047, 156052, 156058, 156064, 156070, 156076, 156082, 156088, 156094, 156099, 156104, 156110, 156115, 156121, 156127, 156132, 156137, 156142, 156147, 156152, 156157, 156162, 156168, 156175, 156182, 156188, 156195, 156202, 156208, 156214, 156221, 156228, 156235, 156242, 156249, 156256, 156263, 156269, 156275, 156282, 156288, 156295, 156302, 156308, 156314, 156320, 156326, 156332, 156338, 156342, 156347, 156353, 156359, 156364, 156370, 156376, 156381, 156386, 156392, 156398, 156404, 156410, 156416, 156422, 156428, 156433, 156438, 156444, 156449, 156455, 156461, 156466, 156471, 156476, 156481, 156486, 156491, 156495, 156500, 156506, 156512, 156517, 156523, 156529, 156534, 156539, 156545, 156551, 156557, 156563, 156569, 156575, 156581, 156586, 156591, 156597, 156602, 156608, 156614, 156619, 156624, 156629, 156634, 156639, 156644, 156647, 156651, 156656, 156661, 156665, 156670, 156675, 156679, 156683, 156688, 156693, 156698, 156703, 156708, 156713, 156718, 156722, 156726, 156731, 156735, 156740, 156745, 156749, 156753, 156757, 156761, 156765, 156769, 156774, 156780, 156787, 156794, 156800, 156807, 156814, 156820, 156826, 156833, 156840, 156847, 156854, 156861, 156868, 156875, 156881, 156887, 156894, 156900, 156907, 156914, 156920, 156926, 156932, 156938, 156944, 156950, 156954, 156959, 156965, 156971, 156976, 156982, 156988, 156993, 156998, 157004, 157010, 157016, 157022, 157028, 157034, 157040, 157045, 157050, 157056, 157061, 157067, 157073, 157078, 157083, 157088, 157093, 157098, 157103, 157107, 157112, 157118, 157124, 157129, 157135, 157141, 157146, 157151, 157157, 157163, 157169, 157175, 157181, 157187, 157193, 157198, 157203, 157209, 157214, 157220, 157226, 157231, 157236, 157241, 157246, 157251, 157256, 157260, 157265, 157271, 157277, 157282, 157288, 157294, 157299, 157304, 157310, 157316, 157322, 157328, 157334, 157340, 157346, 157351, 157356, 157362, 157367, 157373, 157379, 157384, 157389, 157394, 157399, 157404, 157409, 157413, 157418, 157424, 157430, 157435, 157441, 157447, 157452, 157457, 157463, 157469, 157475, 157481, 157487, 157493, 157499, 157504, 157509, 157515, 157520, 157526, 157532, 157537, 157542, 157547, 157552, 157557, 157562, 157566, 157571, 157577, 157583, 157588, 157594, 157600, 157605, 157610, 157616, 157622, 157628, 157634, 157640, 157646, 157652, 157657, 157662, 157668, 157673, 157679, 157685, 157690, 157695, 157700, 157705, 157710, 157715, 157718, 157722, 157727, 157732, 157736, 157741, 157746, 157750, 157754, 157759, 157764, 157769, 157774, 157779, 157784, 157789, 157793, 157797, 157802, 157806, 157811, 157816, 157820, 157824, 157828, 157832, 157836, 157840, 157844, 157849, 157855, 157861, 157866, 157872, 157878, 157883, 157888, 157894, 157900, 157906, 157912, 157918, 157924, 157930, 157935, 157940, 157946, 157951, 157957, 157963, 157968, 157973, 157978, 157983, 157988, 157993, 157998, 158004, 158011, 158018, 158024, 158031, 158038, 158044, 158050, 158057, 158064, 158071, 158078, 158085, 158092, 158099, 158105, 158111, 158118, 158124, 158131, 158138, 158144, 158150, 158156, 158162, 158168, 158174, 158179, 158185, 158192, 158199, 158205, 158212, 158219, 158225, 158231, 158238, 158245, 158252, 158259, 158266, 158273, 158280, 158286, 158292, 158299, 158305, 158312, 158319, 158325, 158331, 158337, 158343, 158349, 158355, 158361, 158368, 158376, 158384, 158391, 158399, 158407, 158414, 158421, 158429, 158437, 158445, 158453, 158461, 158469, 158477, 158484, 158491, 158499, 158506, 158514, 158522, 158529, 158536, 158543, 158550, 158557, 158564, 158569, 158575, 158582, 158589, 158595, 158602, 158609, 158615, 158621, 158628, 158635, 158642, 158649, 158656, 158663, 158670, 158676, 158682, 158689, 158695, 158702, 158709, 158715, 158721, 158727, 158733, 158739, 158745, 158749, 158754, 158760, 158766, 158771, 158777, 158783, 158788, 158793, 158799, 158805, 158811, 158817, 158823, 158829, 158835, 158840, 158845, 158851, 158856, 158862, 158868, 158873, 158878, 158883, 158888, 158893, 158898, 158904, 158911, 158919, 158927, 158934, 158942, 158950, 158957, 158964, 158972, 158980, 158988, 158996, 159004, 159012, 159020, 159027, 159034, 159042, 159049, 159057, 159065, 159072, 159079, 159086, 159093, 159100, 159107, 159112, 159118, 159125, 159132, 159138, 159145, 159152, 159158, 159164, 159171, 159178, 159185, 159192, 159199, 159206, 159213, 159219, 159225, 159232, 159238, 159245, 159252, 159258, 159264, 159270, 159276, 159282, 159288, 159292, 159297, 159303, 159309, 159314, 159320, 159326, 159331, 159336, 159342, 159348, 159354, 159360, 159366, 159372, 159378, 159383, 159388, 159394, 159399, 159405, 159411, 159416, 159421, 159426, 159431, 159436, 159441, 159446, 159452, 159459, 159466, 159472, 159479, 159486, 159492, 159498, 159505, 159512, 159519, 159526, 159533, 159540, 159547, 159553, 159559, 159566, 159572, 159579, 159586, 159592, 159598, 159604, 159610, 159616, 159622, 159628, 159635, 159643, 159651, 159658, 159666, 159674, 159681, 159688, 159696, 159704, 159712, 159720, 159728, 159736, 159744, 159751, 159758, 159766, 159773, 159781, 159789, 159796, 159803, 159810, 159817, 159824, 159831, 159836, 159842, 159849, 159856, 159862, 159869, 159876, 159882, 159888, 159895, 159902, 159909, 159916, 159923, 159930, 159937, 159943, 159949, 159956, 159962, 159969, 159976, 159982, 159988, 159994, 160000, 160006, 160012, 160017, 160023, 160030, 160037, 160043, 160050, 160057, 160063, 160069, 160076, 160083, 160090, 160097, 160104, 160111, 160118, 160124, 160130, 160137, 160143, 160150, 160157, 160163, 160169, 160175, 160181, 160187, 160193, 160197, 160202, 160208, 160214, 160219, 160225, 160231, 160236, 160241, 160247, 160253, 160259, 160265, 160271, 160277, 160283, 160288, 160293, 160299, 160304, 160310, 160316, 160321, 160326, 160331, 160336, 160341, 160346, 160352, 160359, 160367, 160375, 160382, 160390, 160398, 160405, 160412, 160420, 160428, 160436, 160444, 160452, 160460, 160468, 160475, 160482, 160490, 160497, 160505, 160513, 160520, 160527, 160534, 160541, 160548, 160555, 160560, 160566, 160573, 160580, 160586, 160593, 160600, 160606, 160612, 160619, 160626, 160633, 160640, 160647, 160654, 160661, 160667, 160673, 160680, 160686, 160693, 160700, 160706, 160712, 160718, 160724, 160730, 160736, 160741, 160747, 160754, 160761, 160767, 160774, 160781, 160787, 160793, 160800, 160807, 160814, 160821, 160828, 160835, 160842, 160848, 160854, 160861, 160867, 160874, 160881, 160887, 160893, 160899, 160905, 160911, 160917, 160922, 160928, 160935, 160942, 160948, 160955, 160962, 160968, 160974, 160981, 160988, 160995, 161002, 161009, 161016, 161023, 161029, 161035, 161042, 161048, 161055, 161062, 161068, 161074, 161080, 161086, 161092, 161098, 161103, 161109, 161116, 161123, 161129, 161136, 161143, 161149, 161155, 161162, 161169, 161176, 161183, 161190, 161197, 161204, 161210, 161216, 161223, 161229, 161236, 161243, 161249, 161255, 161261, 161267, 161273, 161279, 161284, 161290, 161297, 161304, 161310, 161317, 161324, 161330, 161336, 161343, 161350, 161357, 161364, 161371, 161378, 161385, 161391, 161397, 161404, 161410, 161417, 161424, 161430, 161436, 161442, 161448, 161454, 161460, 161464, 161469, 161475, 161481, 161486, 161492, 161498, 161503, 161508, 161514, 161520, 161526, 161532, 161538, 161544, 161550, 161555, 161560, 161566, 161571, 161577, 161583, 161588, 161593, 161598, 161603, 161608, 161613, 161616, 161620, 161625, 161630, 161634, 161639, 161644, 161648, 161652, 161657, 161662, 161667, 161672, 161677, 161682, 161687, 161691, 161695, 161700, 161704, 161709, 161714, 161718, 161722, 161726, 161730, 161734, 161738, 161742, 161747, 161753, 161759, 161764, 161770, 161776, 161781, 161786, 161792, 161798, 161804, 161810, 161816, 161822, 161828, 161833, 161838, 161844, 161849, 161855, 161861, 161866, 161871, 161876, 161881, 161886, 161891, 161895, 161900, 161906, 161912, 161917, 161923, 161929, 161934, 161939, 161945, 161951, 161957, 161963, 161969, 161975, 161981, 161986, 161991, 161997, 162002, 162008, 162014, 162019, 162024, 162029, 162034, 162039, 162044, 162049, 162055, 162062, 162069, 162075, 162082, 162089, 162095, 162101, 162108, 162115, 162122, 162129, 162136, 162143, 162150, 162156, 162162, 162169, 162175, 162182, 162189, 162195, 162201, 162207, 162213, 162219, 162225, 162229, 162234, 162240, 162246, 162251, 162257, 162263, 162268, 162273, 162279, 162285, 162291, 162297, 162303, 162309, 162315, 162320, 162325, 162331, 162336, 162342, 162348, 162353, 162358, 162363, 162368, 162373, 162378, 162381, 162385, 162390, 162395, 162399, 162404, 162409, 162413, 162417, 162422, 162427, 162432, 162437, 162442, 162447, 162452, 162456, 162460, 162465, 162469, 162474, 162479, 162483, 162487, 162491, 162495, 162499, 162503, 162508, 162514, 162521, 162528, 162534, 162541, 162548, 162554, 162560, 162567, 162574, 162581, 162588, 162595, 162602, 162609, 162615, 162621, 162628, 162634, 162641, 162648, 162654, 162660, 162666, 162672, 162678, 162684, 162688, 162693, 162699, 162705, 162710, 162716, 162722, 162727, 162732, 162738, 162744, 162750, 162756, 162762, 162768, 162774, 162779, 162784, 162790, 162795, 162801, 162807, 162812, 162817, 162822, 162827, 162832, 162837, 162840, 162844, 162849, 162854, 162858, 162863, 162868, 162872, 162876, 162881, 162886, 162891, 162896, 162901, 162906, 162911, 162915, 162919, 162924, 162928, 162933, 162938, 162942, 162946, 162950, 162954, 162958, 162962, 162966, 162971, 162977, 162983, 162988, 162994, 163000, 163005, 163010, 163016, 163022, 163028, 163034, 163040, 163046, 163052, 163057, 163062, 163068, 163073, 163079, 163085, 163090, 163095, 163100, 163105, 163110, 163115, 163120, 163126, 163133, 163140, 163146, 163153, 163160, 163166, 163172, 163179, 163186, 163193, 163200, 163207, 163214, 163221, 163227, 163233, 163240, 163246, 163253, 163260, 163266, 163272, 163278, 163284, 163290, 163296, 163300, 163305, 163311, 163317, 163322, 163328, 163334, 163339, 163344, 163350, 163356, 163362, 163368, 163374, 163380, 163386, 163391, 163396, 163402, 163407, 163413, 163419, 163424, 163429, 163434, 163439, 163444, 163449, 163453, 163458, 163464, 163470, 163475, 163481, 163487, 163492, 163497, 163503, 163509, 163515, 163521, 163527, 163533, 163539, 163544, 163549, 163555, 163560, 163566, 163572, 163577, 163582, 163587, 163592, 163597, 163602, 163605, 163609, 163614, 163619, 163623, 163628, 163633, 163637, 163641, 163646, 163651, 163656, 163661, 163666, 163671, 163676, 163680, 163684, 163689, 163693, 163698, 163703, 163707, 163711, 163715, 163719, 163723, 163727, 163732, 163738, 163745, 163752, 163758, 163765, 163772, 163778, 163784, 163791, 163798, 163805, 163812, 163819, 163826, 163833, 163839, 163845, 163852, 163858, 163865, 163872, 163878, 163884, 163890, 163896, 163902, 163908, 163912, 163917, 163923, 163929, 163934, 163940, 163946, 163951, 163956, 163962, 163968, 163974, 163980, 163986, 163992, 163998, 164003, 164008, 164014, 164019, 164025, 164031, 164036, 164041, 164046, 164051, 164056, 164061, 164065, 164070, 164076, 164082, 164087, 164093, 164099, 164104, 164109, 164115, 164121, 164127, 164133, 164139, 164145, 164151, 164156, 164161, 164167, 164172, 164178, 164184, 164189, 164194, 164199, 164204, 164209, 164214, 164218, 164223, 164229, 164235, 164240, 164246, 164252, 164257, 164262, 164268, 164274, 164280, 164286, 164292, 164298, 164304, 164309, 164314, 164320, 164325, 164331, 164337, 164342, 164347, 164352, 164357, 164362, 164367, 164371, 164376, 164382, 164388, 164393, 164399, 164405, 164410, 164415, 164421, 164427, 164433, 164439, 164445, 164451, 164457, 164462, 164467, 164473, 164478, 164484, 164490, 164495, 164500, 164505, 164510, 164515, 164520, 164524, 164529, 164535, 164541, 164546, 164552, 164558, 164563, 164568, 164574, 164580, 164586, 164592, 164598, 164604, 164610, 164615, 164620, 164626, 164631, 164637, 164643, 164648, 164653, 164658, 164663, 164668, 164673, 164676, 164680, 164685, 164690, 164694, 164699, 164704, 164708, 164712, 164717, 164722, 164727, 164732, 164737, 164742, 164747, 164751, 164755, 164760, 164764, 164769, 164774, 164778, 164782, 164786, 164790, 164794, 164798, 164802, 164807, 164813, 164819, 164824, 164830, 164836, 164841, 164846, 164852, 164858, 164864, 164870, 164876, 164882, 164888, 164893, 164898, 164904, 164909, 164915, 164921, 164926, 164931, 164936, 164941, 164946, 164951, 164956, 164962, 164969, 164976, 164982, 164989, 164996, 165002, 165008, 165015, 165022, 165029, 165036, 165043, 165050, 165057, 165063, 165069, 165076, 165082, 165089, 165096, 165102, 165108, 165114, 165120, 165126, 165132, 165137, 165143, 165150, 165157, 165163, 165170, 165177, 165183, 165189, 165196, 165203, 165210, 165217, 165224, 165231, 165238, 165244, 165250, 165257, 165263, 165270, 165277, 165283, 165289, 165295, 165301, 165307, 165313, 165319, 165326, 165334, 165342, 165349, 165357, 165365, 165372, 165379, 165387, 165395, 165403, 165411, 165419, 165427, 165435, 165442, 165449, 165457, 165464, 165472, 165480, 165487, 165494, 165501, 165508, 165515, 165522, 165527, 165533, 165540, 165547, 165553, 165560, 165567, 165573, 165579, 165586, 165593, 165600, 165607, 165614, 165621, 165628, 165634, 165640, 165647, 165653, 165660, 165667, 165673, 165679, 165685, 165691, 165697, 165703, 165707, 165712, 165718, 165724, 165729, 165735, 165741, 165746, 165751, 165757, 165763, 165769, 165775, 165781, 165787, 165793, 165798, 165803, 165809, 165814, 165820, 165826, 165831, 165836, 165841, 165846, 165851, 165856, 165862, 165869, 165877, 165885, 165892, 165900, 165908, 165915, 165922, 165930, 165938, 165946, 165954, 165962, 165970, 165978, 165985, 165992, 166000, 166007, 166015, 166023, 166030, 166037, 166044, 166051, 166058, 166065, 166070, 166076, 166083, 166090, 166096, 166103, 166110, 166116, 166122, 166129, 166136, 166143, 166150, 166157, 166164, 166171, 166177, 166183, 166190, 166196, 166203, 166210, 166216, 166222, 166228, 166234, 166240, 166246, 166250, 166255, 166261, 166267, 166272, 166278, 166284, 166289, 166294, 166300, 166306, 166312, 166318, 166324, 166330, 166336, 166341, 166346, 166352, 166357, 166363, 166369, 166374, 166379, 166384, 166389, 166394, 166399, 166404, 166410, 166417, 166424, 166430, 166437, 166444, 166450, 166456, 166463, 166470, 166477, 166484, 166491, 166498, 166505, 166511, 166517, 166524, 166530, 166537, 166544, 166550, 166556, 166562, 166568, 166574, 166580, 166586, 166593, 166601, 166609, 166616, 166624, 166632, 166639, 166646, 166654, 166662, 166670, 166678, 166686, 166694, 166702, 166709, 166716, 166724, 166731, 166739, 166747, 166754, 166761, 166768, 166775, 166782, 166789, 166794, 166800, 166807, 166814, 166820, 166827, 166834, 166840, 166846, 166853, 166860, 166867, 166874, 166881, 166888, 166895, 166901, 166907, 166914, 166920, 166927, 166934, 166940, 166946, 166952, 166958, 166964, 166970, 166975, 166981, 166988, 166995, 167001, 167008, 167015, 167021, 167027, 167034, 167041, 167048, 167055, 167062, 167069, 167076, 167082, 167088, 167095, 167101, 167108, 167115, 167121, 167127, 167133, 167139, 167145, 167151, 167155, 167160, 167166, 167172, 167177, 167183, 167189, 167194, 167199, 167205, 167211, 167217, 167223, 167229, 167235, 167241, 167246, 167251, 167257, 167262, 167268, 167274, 167279, 167284, 167289, 167294, 167299, 167304, 167310, 167317, 167325, 167333, 167340, 167348, 167356, 167363, 167370, 167378, 167386, 167394, 167402, 167410, 167418, 167426, 167433, 167440, 167448, 167455, 167463, 167471, 167478, 167485, 167492, 167499, 167506, 167513, 167518, 167524, 167531, 167538, 167544, 167551, 167558, 167564, 167570, 167577, 167584, 167591, 167598, 167605, 167612, 167619, 167625, 167631, 167638, 167644, 167651, 167658, 167664, 167670, 167676, 167682, 167688, 167694, 167699, 167705, 167712, 167719, 167725, 167732, 167739, 167745, 167751, 167758, 167765, 167772, 167779, 167786, 167793, 167800, 167806, 167812, 167819, 167825, 167832, 167839, 167845, 167851, 167857, 167863, 167869, 167875, 167880, 167886, 167893, 167900, 167906, 167913, 167920, 167926, 167932, 167939, 167946, 167953, 167960, 167967, 167974, 167981, 167987, 167993, 168000, 168006, 168013, 168020, 168026, 168032, 168038, 168044, 168050, 168056, 168061, 168067, 168074, 168081, 168087, 168094, 168101, 168107, 168113, 168120, 168127, 168134, 168141, 168148, 168155, 168162, 168168, 168174, 168181, 168187, 168194, 168201, 168207, 168213, 168219, 168225, 168231, 168237, 168242, 168248, 168255, 168262, 168268, 168275, 168282, 168288, 168294, 168301, 168308, 168315, 168322, 168329, 168336, 168343, 168349, 168355, 168362, 168368, 168375, 168382, 168388, 168394, 168400, 168406, 168412, 168418, 168422, 168427, 168433, 168439, 168444, 168450, 168456, 168461, 168466, 168472, 168478, 168484, 168490, 168496, 168502, 168508, 168513, 168518, 168524, 168529, 168535, 168541, 168546, 168551, 168556, 168561, 168566, 168571, 168573, 168576, 168580, 168584, 168587, 168591, 168595, 168598, 168601, 168605, 168609, 168613, 168617, 168621, 168625, 168629, 168632, 168635, 168639, 168642, 168646, 168650, 168653, 168656, 168659, 168662, 168665, 168668, 168671, 168675, 168680, 168685, 168689, 168694, 168699, 168703, 168707, 168712, 168717, 168722, 168727, 168732, 168737, 168742, 168746, 168750, 168755, 168759, 168764, 168769, 168773, 168777, 168781, 168785, 168789, 168793, 168796, 168800, 168805, 168810, 168814, 168819, 168824, 168828, 168832, 168837, 168842, 168847, 168852, 168857, 168862, 168867, 168871, 168875, 168880, 168884, 168889, 168894, 168898, 168902, 168906, 168910, 168914, 168918, 168922, 168927, 168933, 168939, 168944, 168950, 168956, 168961, 168966, 168972, 168978, 168984, 168990, 168996, 169002, 169008, 169013, 169018, 169024, 169029, 169035, 169041, 169046, 169051, 169056, 169061, 169066, 169071, 169074, 169078, 169083, 169088, 169092, 169097, 169102, 169106, 169110, 169115, 169120, 169125, 169130, 169135, 169140, 169145, 169149, 169153, 169158, 169162, 169167, 169172, 169176, 169180, 169184, 169188, 169192, 169196, 169198, 169201, 169205, 169209, 169212, 169216, 169220, 169223, 169226, 169230, 169234, 169238, 169242, 169246, 169250, 169254, 169257, 169260, 169264, 169267, 169271, 169275, 169278, 169281, 169284, 169287, 169290, 169293, 169297, 169302, 169308, 169314, 169319, 169325, 169331, 169336, 169341, 169347, 169353, 169359, 169365, 169371, 169377, 169383, 169388, 169393, 169399, 169404, 169410, 169416, 169421, 169426, 169431, 169436, 169441, 169446, 169449, 169453, 169458, 169463, 169467, 169472, 169477, 169481, 169485, 169490, 169495, 169500, 169505, 169510, 169515, 169520, 169524, 169528, 169533, 169537, 169542, 169547, 169551, 169555, 169559, 169563, 169567, 169571, 169573, 169576, 169580, 169584, 169587, 169591, 169595, 169598, 169601, 169605, 169609, 169613, 169617, 169621, 169625, 169629, 169632, 169635, 169639, 169642, 169646, 169650, 169653, 169656, 169659, 169662, 169665, 169668, 169671, 169675, 169680, 169685, 169689, 169694, 169699, 169703, 169707, 169712, 169717, 169722, 169727, 169732, 169737, 169742, 169746, 169750, 169755, 169759, 169764, 169769, 169773, 169777, 169781, 169785, 169789, 169793, 169797, 169802, 169808, 169814, 169819, 169825, 169831, 169836, 169841, 169847, 169853, 169859, 169865, 169871, 169877, 169883, 169888, 169893, 169899, 169904, 169910, 169916, 169921, 169926, 169931, 169936, 169941, 169946, 169949, 169953, 169958, 169963, 169967, 169972, 169977, 169981, 169985, 169990, 169995, 170000, 170005, 170010, 170015, 170020, 170024, 170028, 170033, 170037, 170042, 170047, 170051, 170055, 170059, 170063, 170067, 170071, 170074, 170078, 170083, 170088, 170092, 170097, 170102, 170106, 170110, 170115, 170120, 170125, 170130, 170135, 170140, 170145, 170149, 170153, 170158, 170162, 170167, 170172, 170176, 170180, 170184, 170188, 170192, 170196, 170198, 170201, 170205, 170209, 170212, 170216, 170220, 170223, 170226, 170230, 170234, 170238, 170242, 170246, 170250, 170254, 170257, 170260, 170264, 170267, 170271, 170275, 170278, 170281, 170284, 170287, 170290, 170293, 170297, 170302, 170308, 170314, 170319, 170325, 170331, 170336, 170341, 170347, 170353, 170359, 170365, 170371, 170377, 170383, 170388, 170393, 170399, 170404, 170410, 170416, 170421, 170426, 170431, 170436, 170441, 170446, 170449, 170453, 170458, 170463, 170467, 170472, 170477, 170481, 170485, 170490, 170495, 170500, 170505, 170510, 170515, 170520, 170524, 170528, 170533, 170537, 170542, 170547, 170551, 170555, 170559, 170563, 170567, 170571, 170574, 170578, 170583, 170588, 170592, 170597, 170602, 170606, 170610, 170615, 170620, 170625, 170630, 170635, 170640, 170645, 170649, 170653, 170658, 170662, 170667, 170672, 170676, 170680, 170684, 170688, 170692, 170696, 170699, 170703, 170708, 170713, 170717, 170722, 170727, 170731, 170735, 170740, 170745, 170750, 170755, 170760, 170765, 170770, 170774, 170778, 170783, 170787, 170792, 170797, 170801, 170805, 170809, 170813, 170817, 170821, 170824, 170828, 170833, 170838, 170842, 170847, 170852, 170856, 170860, 170865, 170870, 170875, 170880, 170885, 170890, 170895, 170899, 170903, 170908, 170912, 170917, 170922, 170926, 170930, 170934, 170938, 170942, 170946, 170949, 170953, 170958, 170963, 170967, 170972, 170977, 170981, 170985, 170990, 170995, 171000, 171005, 171010, 171015, 171020, 171024, 171028, 171033, 171037, 171042, 171047, 171051, 171055, 171059, 171063, 171067, 171071, 171073, 171076, 171080, 171084, 171087, 171091, 171095, 171098, 171101, 171105, 171109, 171113, 171117, 171121, 171125, 171129, 171132, 171135, 171139, 171142, 171146, 171150, 171153, 171156, 171159, 171162, 171165, 171168, 171171, 171175, 171180, 171185, 171189, 171194, 171199, 171203, 171207, 171212, 171217, 171222, 171227, 171232, 171237, 171242, 171246, 171250, 171255, 171259, 171264, 171269, 171273, 171277, 171281, 171285, 171289, 171293, 171297, 171302, 171308, 171314, 171319, 171325, 171331, 171336, 171341, 171347, 171353, 171359, 171365, 171371, 171377, 171383, 171388, 171393, 171399, 171404, 171410, 171416, 171421, 171426, 171431, 171436, 171441, 171446, 171450, 171455, 171461, 171467, 171472, 171478, 171484, 171489, 171494, 171500, 171506, 171512, 171518, 171524, 171530, 171536, 171541, 171546, 171552, 171557, 171563, 171569, 171574, 171579, 171584, 171589, 171594, 171599, 171604, 171610, 171617, 171624, 171630, 171637, 171644, 171650, 171656, 171663, 171670, 171677, 171684, 171691, 171698, 171705, 171711, 171717, 171724, 171730, 171737, 171744, 171750, 171756, 171762, 171768, 171774, 171780, 171784, 171789, 171795, 171801, 171806, 171812, 171818, 171823, 171828, 171834, 171840, 171846, 171852, 171858, 171864, 171870, 171875, 171880, 171886, 171891, 171897, 171903, 171908, 171913, 171918, 171923, 171928, 171933, 171936, 171940, 171945, 171950, 171954, 171959, 171964, 171968, 171972, 171977, 171982, 171987, 171992, 171997, 172002, 172007, 172011, 172015, 172020, 172024, 172029, 172034, 172038, 172042, 172046, 172050, 172054, 172058, 172063, 172069, 172076, 172083, 172089, 172096, 172103, 172109, 172115, 172122, 172129, 172136, 172143, 172150, 172157, 172164, 172170, 172176, 172183, 172189, 172196, 172203, 172209, 172215, 172221, 172227, 172233, 172239, 172243, 172248, 172254, 172260, 172265, 172271, 172277, 172282, 172287, 172293, 172299, 172305, 172311, 172317, 172323, 172329, 172334, 172339, 172345, 172350, 172356, 172362, 172367, 172372, 172377, 172382, 172387, 172392, 172395, 172399, 172404, 172409, 172413, 172418, 172423, 172427, 172431, 172436, 172441, 172446, 172451, 172456, 172461, 172466, 172470, 172474, 172479, 172483, 172488, 172493, 172497, 172501, 172505, 172509, 172513, 172517, 172521, 172526, 172532, 172538, 172543, 172549, 172555, 172560, 172565, 172571, 172577, 172583, 172589, 172595, 172601, 172607, 172612, 172617, 172623, 172628, 172634, 172640, 172645, 172650, 172655, 172660, 172665, 172670, 172675, 172681, 172688, 172695, 172701, 172708, 172715, 172721, 172727, 172734, 172741, 172748, 172755, 172762, 172769, 172776, 172782, 172788, 172795, 172801, 172808, 172815, 172821, 172827, 172833, 172839, 172845, 172851, 172855, 172860, 172866, 172872, 172877, 172883, 172889, 172894, 172899, 172905, 172911, 172917, 172923, 172929, 172935, 172941, 172946, 172951, 172957, 172962, 172968, 172974, 172979, 172984, 172989, 172994, 172999, 173004, 173008, 173013, 173019, 173025, 173030, 173036, 173042, 173047, 173052, 173058, 173064, 173070, 173076, 173082, 173088, 173094, 173099, 173104, 173110, 173115, 173121, 173127, 173132, 173137, 173142, 173147, 173152, 173157, 173160, 173164, 173169, 173174, 173178, 173183, 173188, 173192, 173196, 173201, 173206, 173211, 173216, 173221, 173226, 173231, 173235, 173239, 173244, 173248, 173253, 173258, 173262, 173266, 173270, 173274, 173278, 173282, 173287, 173293, 173300, 173307, 173313, 173320, 173327, 173333, 173339, 173346, 173353, 173360, 173367, 173374, 173381, 173388, 173394, 173400, 173407, 173413, 173420, 173427, 173433, 173439, 173445, 173451, 173457, 173463, 173467, 173472, 173478, 173484, 173489, 173495, 173501, 173506, 173511, 173517, 173523, 173529, 173535, 173541, 173547, 173553, 173558, 173563, 173569, 173574, 173580, 173586, 173591, 173596, 173601, 173606, 173611, 173616, 173620, 173625, 173631, 173637, 173642, 173648, 173654, 173659, 173664, 173670, 173676, 173682, 173688, 173694, 173700, 173706, 173711, 173716, 173722, 173727, 173733, 173739, 173744, 173749, 173754, 173759, 173764, 173769, 173773, 173778, 173784, 173790, 173795, 173801, 173807, 173812, 173817, 173823, 173829, 173835, 173841, 173847, 173853, 173859, 173864, 173869, 173875, 173880, 173886, 173892, 173897, 173902, 173907, 173912, 173917, 173922, 173926, 173931, 173937, 173943, 173948, 173954, 173960, 173965, 173970, 173976, 173982, 173988, 173994, 174000, 174006, 174012, 174017, 174022, 174028, 174033, 174039, 174045, 174050, 174055, 174060, 174065, 174070, 174075, 174079, 174084, 174090, 174096, 174101, 174107, 174113, 174118, 174123, 174129, 174135, 174141, 174147, 174153, 174159, 174165, 174170, 174175, 174181, 174186, 174192, 174198, 174203, 174208, 174213, 174218, 174223, 174228, 174231, 174235, 174240, 174245, 174249, 174254, 174259, 174263, 174267, 174272, 174277, 174282, 174287, 174292, 174297, 174302, 174306, 174310, 174315, 174319, 174324, 174329, 174333, 174337, 174341, 174345, 174349, 174353, 174357, 174362, 174368, 174374, 174379, 174385, 174391, 174396, 174401, 174407, 174413, 174419, 174425, 174431, 174437, 174443, 174448, 174453, 174459, 174464, 174470, 174476, 174481, 174486, 174491, 174496, 174501, 174506, 174511, 174517, 174524, 174531, 174537, 174544, 174551, 174557, 174563, 174570, 174577, 174584, 174591, 174598, 174605, 174612, 174618, 174624, 174631, 174637, 174644, 174651, 174657, 174663, 174669, 174675, 174681, 174687, 174692, 174698, 174705, 174712, 174718, 174725, 174732, 174738, 174744, 174751, 174758, 174765, 174772, 174779, 174786, 174793, 174799, 174805, 174812, 174818, 174825, 174832, 174838, 174844, 174850, 174856, 174862, 174868, 174874, 174881, 174889, 174897, 174904, 174912, 174920, 174927, 174934, 174942, 174950, 174958, 174966, 174974, 174982, 174990, 174997, 175004, 175012, 175019, 175027, 175035, 175042, 175049, 175056, 175063, 175070, 175077, 175082, 175088, 175095, 175102, 175108, 175115, 175122, 175128, 175134, 175141, 175148, 175155, 175162, 175169, 175176, 175183, 175189, 175195, 175202, 175208, 175215, 175222, 175228, 175234, 175240, 175246, 175252, 175258, 175262, 175267, 175273, 175279, 175284, 175290, 175296, 175301, 175306, 175312, 175318, 175324, 175330, 175336, 175342, 175348, 175353, 175358, 175364, 175369, 175375, 175381, 175386, 175391, 175396, 175401, 175406, 175411, 175417, 175424, 175432, 175440, 175447, 175455, 175463, 175470, 175477, 175485, 175493, 175501, 175509, 175517, 175525, 175533, 175540, 175547, 175555, 175562, 175570, 175578, 175585, 175592, 175599, 175606, 175613, 175620, 175625, 175631, 175638, 175645, 175651, 175658, 175665, 175671, 175677, 175684, 175691, 175698, 175705, 175712, 175719, 175726, 175732, 175738, 175745, 175751, 175758, 175765, 175771, 175777, 175783, 175789, 175795, 175801, 175805, 175810, 175816, 175822, 175827, 175833, 175839, 175844, 175849, 175855, 175861, 175867, 175873, 175879, 175885, 175891, 175896, 175901, 175907, 175912, 175918, 175924, 175929, 175934, 175939, 175944, 175949, 175954, 175959, 175965, 175972, 175979, 175985, 175992, 175999, 176005, 176011, 176018, 176025, 176032, 176039, 176046, 176053, 176060, 176066, 176072, 176079, 176085, 176092, 176099, 176105, 176111, 176117, 176123, 176129, 176135, 176141, 176148, 176156, 176164, 176171, 176179, 176187, 176194, 176201, 176209, 176217, 176225, 176233, 176241, 176249, 176257, 176264, 176271, 176279, 176286, 176294, 176302, 176309, 176316, 176323, 176330, 176337, 176344, 176349, 176355, 176362, 176369, 176375, 176382, 176389, 176395, 176401, 176408, 176415, 176422, 176429, 176436, 176443, 176450, 176456, 176462, 176469, 176475, 176482, 176489, 176495, 176501, 176507, 176513, 176519, 176525, 176530, 176536, 176543, 176550, 176556, 176563, 176570, 176576, 176582, 176589, 176596, 176603, 176610, 176617, 176624, 176631, 176637, 176643, 176650, 176656, 176663, 176670, 176676, 176682, 176688, 176694, 176700, 176706, 176710, 176715, 176721, 176727, 176732, 176738, 176744, 176749, 176754, 176760, 176766, 176772, 176778, 176784, 176790, 176796, 176801, 176806, 176812, 176817, 176823, 176829, 176834, 176839, 176844, 176849, 176854, 176859, 176865, 176872, 176880, 176888, 176895, 176903, 176911, 176918, 176925, 176933, 176941, 176949, 176957, 176965, 176973, 176981, 176988, 176995, 177003, 177010, 177018, 177026, 177033, 177040, 177047, 177054, 177061, 177068, 177073, 177079, 177086, 177093, 177099, 177106, 177113, 177119, 177125, 177132, 177139, 177146, 177153, 177160, 177167, 177174, 177180, 177186, 177193, 177199, 177206, 177213, 177219, 177225, 177231, 177237, 177243, 177249, 177254, 177260, 177267, 177274, 177280, 177287, 177294, 177300, 177306, 177313, 177320, 177327, 177334, 177341, 177348, 177355, 177361, 177367, 177374, 177380, 177387, 177394, 177400, 177406, 177412, 177418, 177424, 177430, 177435, 177441, 177448, 177455, 177461, 177468, 177475, 177481, 177487, 177494, 177501, 177508, 177515, 177522, 177529, 177536, 177542, 177548, 177555, 177561, 177568, 177575, 177581, 177587, 177593, 177599, 177605, 177611, 177616, 177622, 177629, 177636, 177642, 177649, 177656, 177662, 177668, 177675, 177682, 177689, 177696, 177703, 177710, 177717, 177723, 177729, 177736, 177742, 177749, 177756, 177762, 177768, 177774, 177780, 177786, 177792, 177797, 177803, 177810, 177817, 177823, 177830, 177837, 177843, 177849, 177856, 177863, 177870, 177877, 177884, 177891, 177898, 177904, 177910, 177917, 177923, 177930, 177937, 177943, 177949, 177955, 177961, 177967, 177973, 177977, 177982, 177988, 177994, 177999, 178005, 178011, 178016, 178021, 178027, 178033, 178039, 178045, 178051, 178057, 178063, 178068, 178073, 178079, 178084, 178090, 178096, 178101, 178106, 178111, 178116, 178121, 178126, 178129, 178133, 178138, 178143, 178147, 178152, 178157, 178161, 178165, 178170, 178175, 178180, 178185, 178190, 178195, 178200, 178204, 178208, 178213, 178217, 178222, 178227, 178231, 178235, 178239, 178243, 178247, 178251, 178255, 178260, 178266, 178272, 178277, 178283, 178289, 178294, 178299, 178305, 178311, 178317, 178323, 178329, 178335, 178341, 178346, 178351, 178357, 178362, 178368, 178374, 178379, 178384, 178389, 178394, 178399, 178404, 178408, 178413, 178419, 178425, 178430, 178436, 178442, 178447, 178452, 178458, 178464, 178470, 178476, 178482, 178488, 178494, 178499, 178504, 178510, 178515, 178521, 178527, 178532, 178537, 178542, 178547, 178552, 178557, 178562, 178568, 178575, 178582, 178588, 178595, 178602, 178608, 178614, 178621, 178628, 178635, 178642, 178649, 178656, 178663, 178669, 178675, 178682, 178688, 178695, 178702, 178708, 178714, 178720, 178726, 178732, 178738, 178742, 178747, 178753, 178759, 178764, 178770, 178776, 178781, 178786, 178792, 178798, 178804, 178810, 178816, 178822, 178828, 178833, 178838, 178844, 178849, 178855, 178861, 178866, 178871, 178876, 178881, 178886, 178891, 178894, 178898, 178903, 178908, 178912, 178917, 178922, 178926, 178930, 178935, 178940, 178945, 178950, 178955, 178960, 178965, 178969, 178973, 178978, 178982, 178987, 178992, 178996, 179000, 179004, 179008, 179012, 179016, 179021, 179027, 179034, 179041, 179047, 179054, 179061, 179067, 179073, 179080, 179087, 179094, 179101, 179108, 179115, 179122, 179128, 179134, 179141, 179147, 179154, 179161, 179167, 179173, 179179, 179185, 179191, 179197, 179201, 179206, 179212, 179218, 179223, 179229, 179235, 179240, 179245, 179251, 179257, 179263, 179269, 179275, 179281, 179287, 179292, 179297, 179303, 179308, 179314, 179320, 179325, 179330, 179335, 179340, 179345, 179350, 179353, 179357, 179362, 179367, 179371, 179376, 179381, 179385, 179389, 179394, 179399, 179404, 179409, 179414, 179419, 179424, 179428, 179432, 179437, 179441, 179446, 179451, 179455, 179459, 179463, 179467, 179471, 179475, 179479, 179484, 179490, 179496, 179501, 179507, 179513, 179518, 179523, 179529, 179535, 179541, 179547, 179553, 179559, 179565, 179570, 179575, 179581, 179586, 179592, 179598, 179603, 179608, 179613, 179618, 179623, 179628, 179633, 179639, 179646, 179653, 179659, 179666, 179673, 179679, 179685, 179692, 179699, 179706, 179713, 179720, 179727, 179734, 179740, 179746, 179753, 179759, 179766, 179773, 179779, 179785, 179791, 179797, 179803, 179809, 179813, 179818, 179824, 179830, 179835, 179841, 179847, 179852, 179857, 179863, 179869, 179875, 179881, 179887, 179893, 179899, 179904, 179909, 179915, 179920, 179926, 179932, 179937, 179942, 179947, 179952, 179957, 179962, 179966, 179971, 179977, 179983, 179988, 179994, 180000, 180005, 180010, 180016, 180022, 180028, 180034, 180040, 180046, 180052, 180057, 180062, 180068, 180073, 180079, 180085, 180090, 180095, 180100, 180105, 180110, 180115, 180118, 180122, 180127, 180132, 180136, 180141, 180146, 180150, 180154, 180159, 180164, 180169, 180174, 180179, 180184, 180189, 180193, 180197, 180202, 180206, 180211, 180216, 180220, 180224, 180228, 180232, 180236, 180240, 180245, 180251, 180258, 180265, 180271, 180278, 180285, 180291, 180297, 180304, 180311, 180318, 180325, 180332, 180339, 180346, 180352, 180358, 180365, 180371, 180378, 180385, 180391, 180397, 180403, 180409, 180415, 180421, 180425, 180430, 180436, 180442, 180447, 180453, 180459, 180464, 180469, 180475, 180481, 180487, 180493, 180499, 180505, 180511, 180516, 180521, 180527, 180532, 180538, 180544, 180549, 180554, 180559, 180564, 180569, 180574, 180578, 180583, 180589, 180595, 180600, 180606, 180612, 180617, 180622, 180628, 180634, 180640, 180646, 180652, 180658, 180664, 180669, 180674, 180680, 180685, 180691, 180697, 180702, 180707, 180712, 180717, 180722, 180727, 180731, 180736, 180742, 180748, 180753, 180759, 180765, 180770, 180775, 180781, 180787, 180793, 180799, 180805, 180811, 180817, 180822, 180827, 180833, 180838, 180844, 180850, 180855, 180860, 180865, 180870, 180875, 180880, 180884, 180889, 180895, 180901, 180906, 180912, 180918, 180923, 180928, 180934, 180940, 180946, 180952, 180958, 180964, 180970, 180975, 180980, 180986, 180991, 180997, 181003, 181008, 181013, 181018, 181023, 181028, 181033, 181037, 181042, 181048, 181054, 181059, 181065, 181071, 181076, 181081, 181087, 181093, 181099, 181105, 181111, 181117, 181123, 181128, 181133, 181139, 181144, 181150, 181156, 181161, 181166, 181171, 181176, 181181, 181186, 181189, 181193, 181198, 181203, 181207, 181212, 181217, 181221, 181225, 181230, 181235, 181240, 181245, 181250, 181255, 181260, 181264, 181268, 181273, 181277, 181282, 181287, 181291, 181295, 181299, 181303, 181307, 181311, 181314, 181318, 181323, 181328, 181332, 181337, 181342, 181346, 181350, 181355, 181360, 181365, 181370, 181375, 181380, 181385, 181389, 181393, 181398, 181402, 181407, 181412, 181416, 181420, 181424, 181428, 181432, 181436, 181440, 181445, 181451, 181457, 181462, 181468, 181474, 181479, 181484, 181490, 181496, 181502, 181508, 181514, 181520, 181526, 181531, 181536, 181542, 181547, 181553, 181559, 181564, 181569, 181574, 181579, 181584, 181589, 181593, 181598, 181604, 181610, 181615, 181621, 181627, 181632, 181637, 181643, 181649, 181655, 181661, 181667, 181673, 181679, 181684, 181689, 181695, 181700, 181706, 181712, 181717, 181722, 181727, 181732, 181737, 181742, 181747, 181753, 181760, 181767, 181773, 181780, 181787, 181793, 181799, 181806, 181813, 181820, 181827, 181834, 181841, 181848, 181854, 181860, 181867, 181873, 181880, 181887, 181893, 181899, 181905, 181911, 181917, 181923, 181927, 181932, 181938, 181944, 181949, 181955, 181961, 181966, 181971, 181977, 181983, 181989, 181995, 182001, 182007, 182013, 182018, 182023, 182029, 182034, 182040, 182046, 182051, 182056, 182061, 182066, 182071, 182076, 182079, 182083, 182088, 182093, 182097, 182102, 182107, 182111, 182115, 182120, 182125, 182130, 182135, 182140, 182145, 182150, 182154, 182158, 182163, 182167, 182172, 182177, 182181, 182185, 182189, 182193, 182197, 182201, 182206, 182212, 182219, 182226, 182232, 182239, 182246, 182252, 182258, 182265, 182272, 182279, 182286, 182293, 182300, 182307, 182313, 182319, 182326, 182332, 182339, 182346, 182352, 182358, 182364, 182370, 182376, 182382, 182386, 182391, 182397, 182403, 182408, 182414, 182420, 182425, 182430, 182436, 182442, 182448, 182454, 182460, 182466, 182472, 182477, 182482, 182488, 182493, 182499, 182505, 182510, 182515, 182520, 182525, 182530, 182535, 182538, 182542, 182547, 182552, 182556, 182561, 182566, 182570, 182574, 182579, 182584, 182589, 182594, 182599, 182604, 182609, 182613, 182617, 182622, 182626, 182631, 182636, 182640, 182644, 182648, 182652, 182656, 182660, 182664, 182669, 182675, 182681, 182686, 182692, 182698, 182703, 182708, 182714, 182720, 182726, 182732, 182738, 182744, 182750, 182755, 182760, 182766, 182771, 182777, 182783, 182788, 182793, 182798, 182803, 182808, 182813, 182818, 182824, 182831, 182838, 182844, 182851, 182858, 182864, 182870, 182877, 182884, 182891, 182898, 182905, 182912, 182919, 182925, 182931, 182938, 182944, 182951, 182958, 182964, 182970, 182976, 182982, 182988, 182994, 182998, 183003, 183009, 183015, 183020, 183026, 183032, 183037, 183042, 183048, 183054, 183060, 183066, 183072, 183078, 183084, 183089, 183094, 183100, 183105, 183111, 183117, 183122, 183127, 183132, 183137, 183142, 183147, 183151, 183156, 183162, 183168, 183173, 183179, 183185, 183190, 183195, 183201, 183207, 183213, 183219, 183225, 183231, 183237, 183242, 183247, 183253, 183258, 183264, 183270, 183275, 183280, 183285, 183290, 183295, 183300, 183303, 183307, 183312, 183317, 183321, 183326, 183331, 183335, 183339, 183344, 183349, 183354, 183359, 183364, 183369, 183374, 183378, 183382, 183387, 183391, 183396, 183401, 183405, 183409, 183413, 183417, 183421, 183425, 183430, 183436, 183443, 183450, 183456, 183463, 183470, 183476, 183482, 183489, 183496, 183503, 183510, 183517, 183524, 183531, 183537, 183543, 183550, 183556, 183563, 183570, 183576, 183582, 183588, 183594, 183600, 183606, 183610, 183615, 183621, 183627, 183632, 183638, 183644, 183649, 183654, 183660, 183666, 183672, 183678, 183684, 183690, 183696, 183701, 183706, 183712, 183717, 183723, 183729, 183734, 183739, 183744, 183749, 183754, 183759, 183763, 183768, 183774, 183780, 183785, 183791, 183797, 183802, 183807, 183813, 183819, 183825, 183831, 183837, 183843, 183849, 183854, 183859, 183865, 183870, 183876, 183882, 183887, 183892, 183897, 183902, 183907, 183912, 183916, 183921, 183927, 183933, 183938, 183944, 183950, 183955, 183960, 183966, 183972, 183978, 183984, 183990, 183996, 184002, 184007, 184012, 184018, 184023, 184029, 184035, 184040, 184045, 184050, 184055, 184060, 184065, 184069, 184074, 184080, 184086, 184091, 184097, 184103, 184108, 184113, 184119, 184125, 184131, 184137, 184143, 184149, 184155, 184160, 184165, 184171, 184176, 184182, 184188, 184193, 184198, 184203, 184208, 184213, 184218, 184222, 184227, 184233, 184239, 184244, 184250, 184256, 184261, 184266, 184272, 184278, 184284, 184290, 184296, 184302, 184308, 184313, 184318, 184324, 184329, 184335, 184341, 184346, 184351, 184356, 184361, 184366, 184371, 184374, 184378, 184383, 184388, 184392, 184397, 184402, 184406, 184410, 184415, 184420, 184425, 184430, 184435, 184440, 184445, 184449, 184453, 184458, 184462, 184467, 184472, 184476, 184480, 184484, 184488, 184492, 184496, 184499, 184503, 184508, 184513, 184517, 184522, 184527, 184531, 184535, 184540, 184545, 184550, 184555, 184560, 184565, 184570, 184574, 184578, 184583, 184587, 184592, 184597, 184601, 184605, 184609, 184613, 184617, 184621, 184625, 184630, 184636, 184642, 184647, 184653, 184659, 184664, 184669, 184675, 184681, 184687, 184693, 184699, 184705, 184711, 184716, 184721, 184727, 184732, 184738, 184744, 184749, 184754, 184759, 184764, 184769, 184774, 184778, 184783, 184789, 184795, 184800, 184806, 184812, 184817, 184822, 184828, 184834, 184840, 184846, 184852, 184858, 184864, 184869, 184874, 184880, 184885, 184891, 184897, 184902, 184907, 184912, 184917, 184922, 184927, 184932, 184938, 184945, 184952, 184958, 184965, 184972, 184978, 184984, 184991, 184998, 185005, 185012, 185019, 185026, 185033, 185039, 185045, 185052, 185058, 185065, 185072, 185078, 185084, 185090, 185096, 185102, 185108, 185112, 185117, 185123, 185129, 185134, 185140, 185146, 185151, 185156, 185162, 185168, 185174, 185180, 185186, 185192, 185198, 185203, 185208, 185214, 185219, 185225, 185231, 185236, 185241, 185246, 185251, 185256, 185261, 185264, 185268, 185273, 185278, 185282, 185287, 185292, 185296, 185300, 185305, 185310, 185315, 185320, 185325, 185330, 185335, 185339, 185343, 185348, 185352, 185357, 185362, 185366, 185370, 185374, 185378, 185382, 185386, 185391, 185397, 185404, 185411, 185417, 185424, 185431, 185437, 185443, 185450, 185457, 185464, 185471, 185478, 185485, 185492, 185498, 185504, 185511, 185517, 185524, 185531, 185537, 185543, 185549, 185555, 185561, 185567, 185571, 185576, 185582, 185588, 185593, 185599, 185605, 185610, 185615, 185621, 185627, 185633, 185639, 185645, 185651, 185657, 185662, 185667, 185673, 185678, 185684, 185690, 185695, 185700, 185705, 185710, 185715, 185720, 185723, 185727, 185732, 185737, 185741, 185746, 185751, 185755, 185759, 185764, 185769, 185774, 185779, 185784, 185789, 185794, 185798, 185802, 185807, 185811, 185816, 185821, 185825, 185829, 185833, 185837, 185841, 185845, 185849, 185854, 185860, 185866, 185871, 185877, 185883, 185888, 185893, 185899, 185905, 185911, 185917, 185923, 185929, 185935, 185940, 185945, 185951, 185956, 185962, 185968, 185973, 185978, 185983, 185988, 185993, 185998, 186003, 186009, 186016, 186023, 186029, 186036, 186043, 186049, 186055, 186062, 186069, 186076, 186083, 186090, 186097, 186104, 186110, 186116, 186123, 186129, 186136, 186143, 186149, 186155, 186161, 186167, 186173, 186179, 186183, 186188, 186194, 186200, 186205, 186211, 186217, 186222, 186227, 186233, 186239, 186245, 186251, 186257, 186263, 186269, 186274, 186279, 186285, 186290, 186296, 186302, 186307, 186312, 186317, 186322, 186327, 186332, 186336, 186341, 186347, 186353, 186358, 186364, 186370, 186375, 186380, 186386, 186392, 186398, 186404, 186410, 186416, 186422, 186427, 186432, 186438, 186443, 186449, 186455, 186460, 186465, 186470, 186475, 186480, 186485, 186488, 186492, 186497, 186502, 186506, 186511, 186516, 186520, 186524, 186529, 186534, 186539, 186544, 186549, 186554, 186559, 186563, 186567, 186572, 186576, 186581, 186586, 186590, 186594, 186598, 186602, 186606, 186610, 186615, 186621, 186628, 186635, 186641, 186648, 186655, 186661, 186667, 186674, 186681, 186688, 186695, 186702, 186709, 186716, 186722, 186728, 186735, 186741, 186748, 186755, 186761, 186767, 186773, 186779, 186785, 186791, 186795, 186800, 186806, 186812, 186817, 186823, 186829, 186834, 186839, 186845, 186851, 186857, 186863, 186869, 186875, 186881, 186886, 186891, 186897, 186902, 186908, 186914, 186919, 186924, 186929, 186934, 186939, 186944, 186948, 186953, 186959, 186965, 186970, 186976, 186982, 186987, 186992, 186998, 187004, 187010, 187016, 187022, 187028, 187034, 187039, 187044, 187050, 187055, 187061, 187067, 187072, 187077, 187082, 187087, 187092, 187097, 187101, 187106, 187112, 187118, 187123, 187129, 187135, 187140, 187145, 187151, 187157, 187163, 187169, 187175, 187181, 187187, 187192, 187197, 187203, 187208, 187214, 187220, 187225, 187230, 187235, 187240, 187245, 187250, 187254, 187259, 187265, 187271, 187276, 187282, 187288, 187293, 187298, 187304, 187310, 187316, 187322, 187328, 187334, 187340, 187345, 187350, 187356, 187361, 187367, 187373, 187378, 187383, 187388, 187393, 187398, 187403, 187407, 187412, 187418, 187424, 187429, 187435, 187441, 187446, 187451, 187457, 187463, 187469, 187475, 187481, 187487, 187493, 187498, 187503, 187509, 187514, 187520, 187526, 187531, 187536, 187541, 187546, 187551, 187556, 187559, 187563, 187568, 187573, 187577, 187582, 187587, 187591, 187595, 187600, 187605, 187610, 187615, 187620, 187625, 187630, 187634, 187638, 187643, 187647, 187652, 187657, 187661, 187665, 187669, 187673, 187677, 187681, 187684, 187688, 187693, 187698, 187702, 187707, 187712, 187716, 187720, 187725, 187730, 187735, 187740, 187745, 187750, 187755, 187759, 187763, 187768, 187772, 187777, 187782, 187786, 187790, 187794, 187798, 187802, 187806, 187810, 187815, 187821, 187827, 187832, 187838, 187844, 187849, 187854, 187860, 187866, 187872, 187878, 187884, 187890, 187896, 187901, 187906, 187912, 187917, 187923, 187929, 187934, 187939, 187944, 187949, 187954, 187959, 187963, 187968, 187974, 187980, 187985, 187991, 187997, 188002, 188007, 188013, 188019, 188025, 188031, 188037, 188043, 188049, 188054, 188059, 188065, 188070, 188076, 188082, 188087, 188092, 188097, 188102, 188107, 188112, 188117, 188123, 188130, 188137, 188143, 188150, 188157, 188163, 188169, 188176, 188183, 188190, 188197, 188204, 188211, 188218, 188224, 188230, 188237, 188243, 188250, 188257, 188263, 188269, 188275, 188281, 188287, 188293, 188297, 188302, 188308, 188314, 188319, 188325, 188331, 188336, 188341, 188347, 188353, 188359, 188365, 188371, 188377, 188383, 188388, 188393, 188399, 188404, 188410, 188416, 188421, 188426, 188431, 188436, 188441, 188446, 188449, 188453, 188458, 188463, 188467, 188472, 188477, 188481, 188485, 188490, 188495, 188500, 188505, 188510, 188515, 188520, 188524, 188528, 188533, 188537, 188542, 188547, 188551, 188555, 188559, 188563, 188567, 188571, 188576, 188582, 188589, 188596, 188602, 188609, 188616, 188622, 188628, 188635, 188642, 188649, 188656, 188663, 188670, 188677, 188683, 188689, 188696, 188702, 188709, 188716, 188722, 188728, 188734, 188740, 188746, 188752, 188756, 188761, 188767, 188773, 188778, 188784, 188790, 188795, 188800, 188806, 188812, 188818, 188824, 188830, 188836, 188842, 188847, 188852, 188858, 188863, 188869, 188875, 188880, 188885, 188890, 188895, 188900, 188905, 188908, 188912, 188917, 188922, 188926, 188931, 188936, 188940, 188944, 188949, 188954, 188959, 188964, 188969, 188974, 188979, 188983, 188987, 188992, 188996, 189001, 189006, 189010, 189014, 189018, 189022, 189026, 189030, 189034, 189039, 189045, 189051, 189056, 189062, 189068, 189073, 189078, 189084, 189090, 189096, 189102, 189108, 189114, 189120, 189125, 189130, 189136, 189141, 189147, 189153, 189158, 189163, 189168, 189173, 189178, 189183, 189188, 189194, 189201, 189208, 189214, 189221, 189228, 189234, 189240, 189247, 189254, 189261, 189268, 189275, 189282, 189289, 189295, 189301, 189308, 189314, 189321, 189328, 189334, 189340, 189346, 189352, 189358, 189364, 189368, 189373, 189379, 189385, 189390, 189396, 189402, 189407, 189412, 189418, 189424, 189430, 189436, 189442, 189448, 189454, 189459, 189464, 189470, 189475, 189481, 189487, 189492, 189497, 189502, 189507, 189512, 189517, 189521, 189526, 189532, 189538, 189543, 189549, 189555, 189560, 189565, 189571, 189577, 189583, 189589, 189595, 189601, 189607, 189612, 189617, 189623, 189628, 189634, 189640, 189645, 189650, 189655, 189660, 189665, 189670, 189673, 189677, 189682, 189687, 189691, 189696, 189701, 189705, 189709, 189714, 189719, 189724, 189729, 189734, 189739, 189744, 189748, 189752, 189757, 189761, 189766, 189771, 189775, 189779, 189783, 189787, 189791, 189795, 189800, 189806, 189813, 189820, 189826, 189833, 189840, 189846, 189852, 189859, 189866, 189873, 189880, 189887, 189894, 189901, 189907, 189913, 189920, 189926, 189933, 189940, 189946, 189952, 189958, 189964, 189970, 189976, 189980, 189985, 189991, 189997, 190002, 190008, 190014, 190019, 190024, 190030, 190036, 190042, 190048, 190054, 190060, 190066, 190071, 190076, 190082, 190087, 190093, 190099, 190104, 190109, 190114, 190119, 190124, 190129, 190133, 190138, 190144, 190150, 190155, 190161, 190167, 190172, 190177, 190183, 190189, 190195, 190201, 190207, 190213, 190219, 190224, 190229, 190235, 190240, 190246, 190252, 190257, 190262, 190267, 190272, 190277, 190282, 190286, 190291, 190297, 190303, 190308, 190314, 190320, 190325, 190330, 190336, 190342, 190348, 190354, 190360, 190366, 190372, 190377, 190382, 190388, 190393, 190399, 190405, 190410, 190415, 190420, 190425, 190430, 190435, 190439, 190444, 190450, 190456, 190461, 190467, 190473, 190478, 190483, 190489, 190495, 190501, 190507, 190513, 190519, 190525, 190530, 190535, 190541, 190546, 190552, 190558, 190563, 190568, 190573, 190578, 190583, 190588, 190592, 190597, 190603, 190609, 190614, 190620, 190626, 190631, 190636, 190642, 190648, 190654, 190660, 190666, 190672, 190678, 190683, 190688, 190694, 190699, 190705, 190711, 190716, 190721, 190726, 190731, 190736, 190741, 190744, 190748, 190753, 190758, 190762, 190767, 190772, 190776, 190780, 190785, 190790, 190795, 190800, 190805, 190810, 190815, 190819, 190823, 190828, 190832, 190837, 190842, 190846, 190850, 190854, 190858, 190862, 190866, 190869, 190873, 190878, 190883, 190887, 190892, 190897, 190901, 190905, 190910, 190915, 190920, 190925, 190930, 190935, 190940, 190944, 190948, 190953, 190957, 190962, 190967, 190971, 190975, 190979, 190983, 190987, 190991, 190995, 191000, 191006, 191012, 191017, 191023, 191029, 191034, 191039, 191045, 191051, 191057, 191063, 191069, 191075, 191081, 191086, 191091, 191097, 191102, 191108, 191114, 191119, 191124, 191129, 191134, 191139, 191144, 191148, 191153, 191159, 191165, 191170, 191176, 191182, 191187, 191192, 191198, 191204, 191210, 191216, 191222, 191228, 191234, 191239, 191244, 191250, 191255, 191261, 191267, 191272, 191277, 191282, 191287, 191292, 191297, 191302, 191308, 191315, 191322, 191328, 191335, 191342, 191348, 191354, 191361, 191368, 191375, 191382, 191389, 191396, 191403, 191409, 191415, 191422, 191428, 191435, 191442, 191448, 191454, 191460, 191466, 191472, 191478, 191482, 191487, 191493, 191499, 191504, 191510, 191516, 191521, 191526, 191532, 191538, 191544, 191550, 191556, 191562, 191568, 191573, 191578, 191584, 191589, 191595, 191601, 191606, 191611, 191616, 191621, 191626, 191631, 191634, 191638, 191643, 191648, 191652, 191657, 191662, 191666, 191670, 191675, 191680, 191685, 191690, 191695, 191700, 191705, 191709, 191713, 191718, 191722, 191727, 191732, 191736, 191740, 191744, 191748, 191752, 191756, 191761, 191767, 191774, 191781, 191787, 191794, 191801, 191807, 191813, 191820, 191827, 191834, 191841, 191848, 191855, 191862, 191868, 191874, 191881, 191887, 191894, 191901, 191907, 191913, 191919, 191925, 191931, 191937, 191941, 191946, 191952, 191958, 191963, 191969, 191975, 191980, 191985, 191991, 191997, 192003, 192009, 192015, 192021, 192027, 192032, 192037, 192043, 192048, 192054, 192060, 192065, 192070, 192075, 192080, 192085, 192090, 192093, 192097, 192102, 192107, 192111, 192116, 192121, 192125, 192129, 192134, 192139, 192144, 192149, 192154, 192159, 192164, 192168, 192172, 192177, 192181, 192186, 192191, 192195, 192199, 192203, 192207, 192211, 192215, 192219, 192224, 192230, 192236, 192241, 192247, 192253, 192258, 192263, 192269, 192275, 192281, 192287, 192293, 192299, 192305, 192310, 192315, 192321, 192326, 192332, 192338, 192343, 192348, 192353, 192358, 192363, 192368, 192373, 192379, 192386, 192393, 192399, 192406, 192413, 192419, 192425, 192432, 192439, 192446, 192453, 192460, 192467, 192474, 192480, 192486, 192493, 192499, 192506, 192513, 192519, 192525, 192531, 192537, 192543, 192549, 192553, 192558, 192564, 192570, 192575, 192581, 192587, 192592, 192597, 192603, 192609, 192615, 192621, 192627, 192633, 192639, 192644, 192649, 192655, 192660, 192666, 192672, 192677, 192682, 192687, 192692, 192697, 192702, 192706, 192711, 192717, 192723, 192728, 192734, 192740, 192745, 192750, 192756, 192762, 192768, 192774, 192780, 192786, 192792, 192797, 192802, 192808, 192813, 192819, 192825, 192830, 192835, 192840, 192845, 192850, 192855, 192858, 192862, 192867, 192872, 192876, 192881, 192886, 192890, 192894, 192899, 192904, 192909, 192914, 192919, 192924, 192929, 192933, 192937, 192942, 192946, 192951, 192956, 192960, 192964, 192968, 192972, 192976, 192980, 192985, 192991, 192998, 193005, 193011, 193018, 193025, 193031, 193037, 193044, 193051, 193058, 193065, 193072, 193079, 193086, 193092, 193098, 193105, 193111, 193118, 193125, 193131, 193137, 193143, 193149, 193155, 193161, 193165, 193170, 193176, 193182, 193187, 193193, 193199, 193204, 193209, 193215, 193221, 193227, 193233, 193239, 193245, 193251, 193256, 193261, 193267, 193272, 193278, 193284, 193289, 193294, 193299, 193304, 193309, 193314, 193318, 193323, 193329, 193335, 193340, 193346, 193352, 193357, 193362, 193368, 193374, 193380, 193386, 193392, 193398, 193404, 193409, 193414, 193420, 193425, 193431, 193437, 193442, 193447, 193452, 193457, 193462, 193467, 193471, 193476, 193482, 193488, 193493, 193499, 193505, 193510, 193515, 193521, 193527, 193533, 193539, 193545, 193551, 193557, 193562, 193567, 193573, 193578, 193584, 193590, 193595, 193600, 193605, 193610, 193615, 193620, 193624, 193629, 193635, 193641, 193646, 193652, 193658, 193663, 193668, 193674, 193680, 193686, 193692, 193698, 193704, 193710, 193715, 193720, 193726, 193731, 193737, 193743, 193748, 193753, 193758, 193763, 193768, 193773, 193777, 193782, 193788, 193794, 193799, 193805, 193811, 193816, 193821, 193827, 193833, 193839, 193845, 193851, 193857, 193863, 193868, 193873, 193879, 193884, 193890, 193896, 193901, 193906, 193911, 193916, 193921, 193926, 193929, 193933, 193938, 193943, 193947, 193952, 193957, 193961, 193965, 193970, 193975, 193980, 193985, 193990, 193995, 194000, 194004, 194008, 194013, 194017, 194022, 194027, 194031, 194035, 194039, 194043, 194047, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 194051, 194056, 194063, 194067, 194071, 194076, 194081, 194086, 194091, 194097, 194102, 194107, 194111, 194115, 194119, 194123, 194127, 194131, 194135, 194139, 194143, 194148, 194153, 194158, 194163, 194168, 194173, 194178, 194183, 194188, 194193, 194198, 194203, 194208, 194213, 194218, 194223, 194228, 194233, 194238, 194243, 194249, 194255, 194261, 194267, 194273, 194278, 194285, 194289, 194293, 194297, 194301, 194305, 194309, 194313, 194317, 194321, 194325, 194329, 194333, 194338, 194343, 194348, 194353, 194358, 194363, 194368, 194374, 194380, 194386, 194392, 194397, 194402, 194407, 194412, 194417, 194422, 194427, 194432, 194437, 194442, 194447, 194452, 194457, 194462, 194467, 194473, 194479, 194485, 194491, 194497, 194502, 194506, 194511, 194516, 194521, 194528, 194532, 194538, 194542, 194547, 194552, 194558, 194563, 194569, 194573, 194578, 194583, 194588, 194594, 194601, 194606, 194611, 194617, 194622, 194627, 194632, 194636, 194641, 194646, 194652, 194658, 194664, 194670, 194676, 194682, 194688, 194694, 194698, 194702, 194706, 194710, 194714, 194718, 194722, 194726, 194730, 194734, 194738, 194743, 194748, 194753, 194758, 194763, 194768, 194773, 194778, 194783, 194788, 194793, 194798, 194803, 194808, 194813, 194818, 194823, 194828, 194833, 194838, 194843, 194848, 194853, 194858, 194863, 194868, 194873, 194878, 194883, 194888, 194894, 194900, 194906, 194912, 194918, 194924, 194930, 194936, 194942, 194948, 194954, 194960, 194966, 194971, 194976, 194981, 194986, 194989, 194993, 194997, 195001, 195005, 195009, 195013, 195017, 195021, 195025, 195029, 195035, 195040, 195045, 195049, 195053, 195057, 195061, 195065, 195069, 195073, 195077, 195081, 195085, 195090, 195095, 195100, 195105, 195110, 195115, 195120, 195125, 195130, 195135, 195140, 195146, 195149, 195152, 195155, 195158, 195161, 195164, 195167, 195170, 195173, 195176, 195179, 195182, 195185, 195188, 195192, 195196, 195200, 195204, 195208, 195212, 195216, 195220, 195224, 195228, 195232, 195236, 195240, 195244, 195248, 195254, 195259, 195263, 195268, 195273, 195279, 195285, 195291, 195297, 195304, 195311, 195318, 195323, 195329, 195335, 195341, 195347, 195353, 195357, -1, -1, 195362, -1, 195369, -1, -1, 195375, 195379, 195384, 195388, 195392, 195398, 195405, 195409, 195415, 195421, -1, 195425, -1, 195431, -1, -1, 195436, 195440, -1, -1, -1, 195444, 195449, 195453, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 195459, 195462, 195465, 195468, 195472, 195476, 195479, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 195482, 195485, 195488, 195491, 195494, -1, -1, -1, -1, -1, 195498, -1, 195500, 195503, 195505, 195507, 195509, 195511, 195514, 195516, 195518, 195520, 195522, 195524, 195527, 195529, 195532, 195534, 195536, 195538, 195540, 195542, 195544, 195546, 195548, 195550, -1, 195552, 195554, 195556, 195559, 195562, -1, 195564, -1, 195566, 195568, -1, 195570, 195572, -1, 195574, 195577, 195579, 195581, 195584, 195586, 195588, 195590, 195593, 195595, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 195598, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 195600, 195603, 195606, 195608, 195610, 195612, 195614, 195617, 195619, 195622, 195624, 195627, 195630, 195634, 195637, 195641, 195643, 195646, 195648, 195651, 195653, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 195655, 195657, 195659, -1, 195661, 195663, 195665, 195667, 195669, 195671, 195673, 195675, 195677, 195679, 195681, 195683, 195685, 195687, 195689, 195691, 195693, 195695, 195697, -1, 195699, 195701, 195703, 195705, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 195707, 195709, 195711, 195713, 195715, 195717, 195719, 195721, 195723, 195725, 195727, 195729, 195731, 195733, 195735, 195737, 195739, 195741, 195743, 195745, 195747, 195749, 195751, 195753, 195755, 195757, 195759, 195761, 195763, 195765, 195767, 195769, 195771, 195773, 195775, 195777, 195779, 195781, 195783, 195785, 195787, 195789, 195791, 195793, 195795, 195797, 195799, 195801, 195803, 195805, 195807, 195809, 195811, 195813, 195815, 195817, 195819, 195821, 195823, 195825, 195827, 195829, 195831, 195833, 195835, 195837, 195839, 195841, 195843, 195845, 195847, 195849, 195851, 195853, 195855, 195857, 195859, 195861, 195863, 195865, 195867, 195869, 195871, 195873, 195875, 195877, 195879, 195881, 195883, 195885, 195887, 195889, 195891, 195893, -1, -1, 195895, 195897, 195899, 195901, 195903, 195905, 195908, 195910, 195912, 195914, 195916, 195918, 195921, 195924, 195927, 195930, 195932, 195934, 195936, 195938, 195940, 195942, 195945, 195948, 195951, 195954, 195957, 195960, 195963, 195966, 195969, 195972, 195975, 195978, 195981, 195984, 195987, 195990, 195993, 195996, 195999, 196002, 196005, 196008, 196011, 196014, 196017, 196020, 196023, 196026, 196029, 196032, 196035, 196038, 196041, 196044, 196047, 196050, 196053, 196056, 196059, 196061, 196063, -1, 196065, 196067, 196070, 196073, 196075, 196078, 196081, 196083, 196086, 196088, 196091, 196094, 196097, 196100, 196103, 196106, 196109, 196111, 196113, 196116, 196119, 196121, -1, 196124, 196126, 196129, 196131, 196133, 196135, 196137, -1, -1, -1, 196139, 196141, 196144, 196147, 196151, 196154, -1, -1, 196156, 196160, 196163, 196165, 196168, 196172, -1, -1, 196175, 196178, 196180, 196184, 196187, 196190, -1, -1, 196193, 196196, 196199, -1, -1, -1, 196201, 196204, 196207, 196209, 196211, 196213, 196216, -1, 196219, 196221, 196223, 196225, 196227, 196229, 196231, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 196233, 196235, 196237, -1, -1, -1, }; QString Data::removeAccents(const QString &input) { FCT_IDENTIFICATION; // Based on iconv algorithm. // However, I don't want to use iconv here, because the library is too complex for what I actually need if ( input.isEmpty() ) return QString(); QString ret; for ( const QChar &character : input ) { const char16_t charInt = character.unicode(); // skip the non-printable chars if ( charInt < 32 ) continue; // basic ASCII are mapped 1:1 if ( charInt < 128 ) { ret.append(character); } else // the rest is mapped based on translitTab { const int offset = charInt - 128; const int maxIndex = static_cast(sizeof(tranlitIndexMap) / sizeof(tranlitIndexMap[0])); if ( offset < maxIndex ) { const int index = tranlitIndexMap[offset]; if ( index >= 0 ) { const char size = translitTab[index]; if ( size > 0 ) { for (ushort i = 0; i < size; i++) ret.append(translitTab[index + 1 + i]); } else { ret.append("?"); } } else { ret.append("?"); } } else { ret.append("?"); } } } /* If stripped string is empty then QString to store NULL value do DB */ if ( ret.isEmpty() ) return QString(); return ret; } ================================================ FILE: data/ActivityProfile.cpp ================================================ #include "ActivityProfile.h" #include "core/debug.h" #include "data/ProfileManager.h" #include "data/AntProfile.h" #include "data/MainLayoutProfile.h" #include "data/RigProfile.h" #include "data/RotProfile.h" #include "data/StationProfile.h" MODULE_IDENTIFICATION("qlog.data.activityprofile"); // a compilation issue under 20.04 Ubuntu because qt 5.12 does not // support >> for QHash but it is not used now. // QDataStream& operator<<(QDataStream& out, const ActivityProfile& v) // { // out << v.profileName // << v.profiles // << v.fieldValues; // return out; // } // QDataStream& operator<<(QDataStream& out, const ActivityProfile::ProfileRecord& v) // { // out << v.name // << v.params; // return out; // } // QDataStream& operator>>(QDataStream& in, ActivityProfile& v) // { // in >> v.profileName; // in >> v.profiles; // in >> v.fieldValues; // return in; // } // QDataStream& operator>>(QDataStream& in, ActivityProfile::ProfileRecord& v) // { // in >> v.name; // in >> v.params; // return in; // } ActivityProfile::ActivityProfile(const QString &name, const QJsonDocument &config) { /* Config example * { * "activityName" = "name", * "profiles" = [ * { "profileType" = 1, * "name" = "name", * "params" = [ * { * "name" = "connect" * "value" : true * } * ] * } * ] * "fieldValues" = [ * { "fieldID" = 12, * "value" = "Contest" * } * ] * } */ if ( config.isEmpty() ) return; profileName = name; const QJsonArray &profilesArray = config["profiles"].toArray(); for ( const QJsonValue &value : profilesArray ) { const QJsonObject &obj = value.toObject(); ActivityProfile::ProfileRecord profileRec; profileRec.name = obj["name"].toString(); const QJsonArray &profilesParamsArray = value["params"].toArray(); for ( const QJsonValue ¶mValue : profilesParamsArray ) { const QJsonObject &inner = paramValue.toObject(); profileRec.params[getParamID(inner["name"].toString())] = inner["value"].toVariant(); } profiles[static_cast(obj["profileType"].toInt())] = profileRec; } const QJsonArray &fieldValuesArray = config["fieldValues"].toArray(); for ( const QJsonValue &fieldValue : fieldValuesArray ) { const QJsonObject &obj = fieldValue.toObject(); fieldValues[obj["fieldID"].toInt()] = obj["value"].toString(); } } QByteArray ActivityProfile::toJson() const { /* Config example * { * "activityName" = "name", * "profiles" = [ * { "profileType" = 1, * "name" = "name", * "params" = [ * { * "name" = "connect" * "value" : true * } * ] * } * ] * "fieldValues" = [ * { "fieldID" = 12, * "value" = "Contest" * } * ] * } */ QJsonObject activityObject; activityObject["activityName"] = profileName; if ( !profiles.isEmpty() ) { QJsonArray profilesArray; for (auto i = profiles.begin(); i != profiles.end(); ++i) { QJsonObject profileObject; profileObject["profileType"] = i.key(); profileObject["name"] = i.value().name; if ( !i.value().params.isEmpty() ) { QJsonArray paramsArray; for (auto j = i.value().params.begin(); j != i.value().params.end(); ++j ) { QJsonObject profileParamObject; profileParamObject["name"] = getParamName(j.key()); profileParamObject["value"] = j.value().toJsonValue(); paramsArray.push_back(profileParamObject); } profileObject["params"] = paramsArray; } profilesArray.push_back(profileObject); } activityObject["profiles"] = profilesArray; } if ( !fieldValues.isEmpty() ) { QJsonArray fieldValuesArray; for (auto i = fieldValues.begin(); i != fieldValues.end(); i++ ) { QJsonObject fieldObject; fieldObject["fieldID"] = i.key(); fieldObject["value"] = i.value().toJsonValue(); fieldValuesArray.push_back(fieldObject); } activityObject["fieldValues"] = fieldValuesArray; } QJsonDocument doc(activityObject); return doc.toJson(); } bool ActivityProfile::operator==(const ActivityProfile &profile) { return ( profile.profileName == this->profileName && profile.profiles == this->profiles && profile.fieldValues == this->fieldValues ); } bool ActivityProfile::operator!=(const ActivityProfile &profile) { return !operator==(profile); } void ActivityProfilesManager::setAllProfiles() { ActivityProfile currActivity = getCurProfile1(); if (currActivity == ActivityProfile() ) return; for( auto i = currActivity.profiles.begin(); i != currActivity.profiles.end(); i++ ) { qCDebug(runtime) << i.key() << i.value().name; switch ( i.key() ) { case ActivityProfile::ANTENNA_PROFILE: AntProfilesManager::instance()->setCurProfile1(i.value().name); break; case ActivityProfile::STATION_PROFILE: StationProfilesManager::instance()->setCurProfile1(i.value().name); break; case ActivityProfile::RIG_PROFILE: RigProfilesManager::instance()->setCurProfile1(i.value().name); break; case ActivityProfile::ROT_PROFILE: RotProfilesManager::instance()->setCurProfile1(i.value().name); break; case ActivityProfile::MAIN_LAYOUT_PROFILE: MainLayoutProfilesManager::instance()->setCurProfile1(i.value().name); break; default: qWarning() << "Unknown Activity profile" << i.key(); } } emit changeFinished(currActivity.profileName); } ActivityProfilesManager::ActivityProfilesManager() : ProfileManagerSQL("activity_profiles") { FCT_IDENTIFICATION; QSqlQuery profileQuery; if ( ! profileQuery.prepare("SELECT profile_name, config FROM activity_profiles") ) qWarning()<< "Cannot prepare select"; if ( profileQuery.exec() ) { while (profileQuery.next()) { ActivityProfile profileDB(profileQuery.value(0).toString(), QJsonDocument::fromJson(profileQuery.value(1).toByteArray())); addProfile(profileDB.profileName, profileDB); } } else qInfo() << "Activity Profile DB select error " << profileQuery.lastError().text(); connect (this, &ActivityProfilesManager::profileChanged, this, &ActivityProfilesManager::setAllProfiles); } void ActivityProfilesManager::save() { FCT_IDENTIFICATION; QSqlQuery deleteQuery; QSqlQuery insertQuery; if ( ! deleteQuery.prepare("DELETE FROM activity_profiles") ) { qWarning() << "cannot prepare Delete statement"; return; } if ( ! insertQuery.prepare("INSERT INTO activity_profiles(profile_name, config) " "VALUES (:profile_name, :config)") ) { qWarning() << "cannot prepare Insert statement"; return; } if ( deleteQuery.exec() ) { const QStringList &keys = profileNameList(); for ( const QString &key: keys ) { const ActivityProfile &activityProfile = getProfile(key); insertQuery.bindValue(":profile_name", key); insertQuery.bindValue(":config", activityProfile.toJson()); if ( ! insertQuery.exec() ) qInfo() << "Station Profile DB insert error " << insertQuery.lastError().text() << insertQuery.lastQuery(); } } else qInfo() << "Activity Profile DB delete error " << deleteQuery.lastError().text(); saveCurProfile1(); } ================================================ FILE: data/ActivityProfile.h ================================================ #ifndef Q_LOG_DATA_ACTIVITYPROFILE_H #define Q_LOG_DATA_ACTIVITYPROFILE_H #include #include "data/ProfileManager.h" class ActivityProfile { public: enum ProfileType { ANTENNA_PROFILE = 0, STATION_PROFILE = 1, RIG_PROFILE = 2, ROT_PROFILE = 3, MAIN_LAYOUT_PROFILE = 4 }; enum ProfileParamType { CONNECT = 0, }; struct ProfileRecord { QString name; QHash params; bool operator==(const ProfileRecord &other) const { return name == other.name && params == other.params; } }; ActivityProfile() {}; ActivityProfile(const QString &name, const QJsonDocument &config); QByteArray toJson() const; QString profileName; QHash profiles; QHash fieldValues; QVariant getProfileParam(ProfileType profileType, ProfileParamType profileParam) const { return profiles.value(profileType).params.value(profileParam); }; bool operator== (const ActivityProfile &profile); bool operator!= (const ActivityProfile &profile); private: // friend QDataStream& operator<<(QDataStream& out, const ActivityProfile& v); // friend QDataStream& operator<<(QDataStream& out, const ActivityProfile::ProfileRecord& v); // friend QDataStream& operator>>(QDataStream& in, ActivityProfile& v); // friend QDataStream& operator>>(QDataStream& in, ActivityProfile::ProfileRecord& v); QString getParamName(ProfileParamType id) const { return profileParamsNameMapping.value(id);} ; ProfileParamType getParamID(const QString ¶mName) const { return profileParamsNameMapping.key(paramName);}; QHash profileParamsNameMapping = {{CONNECT, "connect"}}; }; Q_DECLARE_METATYPE(ActivityProfile); class ActivityProfilesManager : public ProfileManagerSQL { Q_OBJECT signals: void changeFinished(const QString &name); public slots: void setAllProfiles(); public: explicit ActivityProfilesManager(); ~ActivityProfilesManager() { }; static ActivityProfilesManager* instance() { static ActivityProfilesManager instance; return &instance; }; void save(); }; #endif // Q_LOG_DATA_ACTIVITYPROFILE_H ================================================ FILE: data/AntProfile.cpp ================================================ #include #include #include #include "AntProfile.h" #include "core/debug.h" MODULE_IDENTIFICATION("qlog.data.antprofile"); QDataStream& operator<<(QDataStream& out, const AntProfile& v) { out << v.profileName << v.description << v.azimuthBeamWidth << v.azimuthOffset; return out; } QDataStream& operator>>(QDataStream& in, AntProfile& v) { in >> v.profileName; in >> v.description; in >> v.azimuthBeamWidth; in >> v.azimuthOffset; return in; } AntProfilesManager::AntProfilesManager() : ProfileManagerSQL("ant_profiles") { FCT_IDENTIFICATION; QSqlQuery profileQuery; if ( ! profileQuery.prepare("SELECT profile_name, desc, azimuth_beamwidth, azimuth_offset FROM ant_profiles") ) { qWarning()<< "Cannot prepare select"; } if ( profileQuery.exec() ) { while (profileQuery.next()) { AntProfile profileDB; profileDB.profileName = profileQuery.value(0).toString(); profileDB.description = profileQuery.value(1).toString(); profileDB.azimuthBeamWidth = profileQuery.value(2).toDouble(); profileDB.azimuthOffset = profileQuery.value(3).toDouble(); addProfile(profileDB.profileName, profileDB); } } else { qInfo() << "Station Profile DB select error " << profileQuery.lastError().text(); } } void AntProfilesManager::save() { FCT_IDENTIFICATION; QSqlQuery deleteQuery; QSqlQuery insertQuery; if ( ! deleteQuery.prepare("DELETE FROM ant_profiles") ) { qWarning() << "cannot prepare Delete statement"; return; } if ( ! insertQuery.prepare("INSERT INTO ant_profiles(profile_name, desc, azimuth_beamwidth, azimuth_offset) " "VALUES (:profile_name, :desc, :azimuth_beamwidth, :azimuth_offset)") ) { qWarning() << "cannot prepare Insert statement"; return; } if ( deleteQuery.exec() ) { const QStringList &keys = profileNameList(); for ( const QString &key: keys ) { const AntProfile &antProfile = getProfile(key); insertQuery.bindValue(":profile_name", key); insertQuery.bindValue(":desc", antProfile.description); insertQuery.bindValue(":azimuth_beamwidth", antProfile.azimuthBeamWidth); insertQuery.bindValue(":azimuth_offset", antProfile.azimuthOffset); if ( ! insertQuery.exec() ) { qInfo() << "Station Profile DB insert error " << insertQuery.lastError().text() << insertQuery.lastQuery(); } } } else { qInfo() << "Station Profile DB delete error " << deleteQuery.lastError().text(); } saveCurProfile1(); } bool AntProfile::operator==(const AntProfile &profile) { return (profile.profileName == this->profileName && profile.description == this->description && profile.azimuthBeamWidth == this->azimuthBeamWidth && profile.azimuthOffset == this->azimuthOffset); } bool AntProfile::operator!=(const AntProfile &profile) { return !operator==(profile); } ================================================ FILE: data/AntProfile.h ================================================ #ifndef QLOG_DATA_ANTPROFILE_H #define QLOG_DATA_ANTPROFILE_H #include #include #include #include "data/ProfileManager.h" #define DEFAULT_ROT_MODEL 1 class AntProfile { public: AntProfile() : azimuthBeamWidth(0.0), azimuthOffset(0.0) {}; QString profileName; QString description; double azimuthBeamWidth; double azimuthOffset; bool operator== (const AntProfile &profile); bool operator!= (const AntProfile &profile); private: friend QDataStream& operator<<(QDataStream& out, const AntProfile& v); friend QDataStream& operator>>(QDataStream& in, AntProfile& v); }; Q_DECLARE_METATYPE(AntProfile) class AntProfilesManager : public ProfileManagerSQL { Q_OBJECT public: explicit AntProfilesManager(); ~AntProfilesManager() { }; static AntProfilesManager *instance() { static AntProfilesManager instance; return &instance; }; void save(); }; #endif // QLOG_DATA_ANTPROFILE_H ================================================ FILE: data/Band.h ================================================ #ifndef QLOG_DATA_BAND_H #define QLOG_DATA_BAND_H #include class Band { public: QString name; double start; double end; QString satDesignator; bool operator==(const Band &band) const { return ( this->name == band.name && this->start == band.start && this->end == band.end && this->satDesignator == band.satDesignator ); } }; Q_DECLARE_METATYPE(Band); #endif // QLOG_DATA_BAND_H ================================================ FILE: data/BandPlan.cpp ================================================ #include #include #include "BandPlan.h" #include "core/debug.h" MODULE_IDENTIFICATION("qlog.data.bandplan"); struct BandModeRange { double start; double end; BandPlan::BandPlanMode mode; }; // currectly only IARU Region 1 is implemented // https://www.iaru-r1.org/wp-content/uploads/2019/08/hf_r1_bandplan.pdf // https://www.oevsv.at/export/shared/.content/.galleries/pdf-Downloads/OVSV-Bandplan_05-2019.pdf static const BandModeRange r1BandModeTable[] = { // 2200m {0.1357, 0.1378, BandPlan::BAND_MODE_CW}, // 630m {0.472, 0.475, BandPlan::BAND_MODE_CW}, {0.475, 0.479, BandPlan::BAND_MODE_DIGITAL}, // 160m {1.800, 1.838, BandPlan::BAND_MODE_CW}, {1.838, 1.840, BandPlan::BAND_MODE_DIGITAL}, {1.840, 1.843, BandPlan::BAND_MODE_FT8}, {1.843, 2.000, BandPlan::BAND_MODE_LSB}, // 80m {3.500, 3.570, BandPlan::BAND_MODE_CW}, {3.570, 3.573, BandPlan::BAND_MODE_DIGITAL}, {3.573, 3.576, BandPlan::BAND_MODE_FT8}, {3.576, 3.579, BandPlan::BAND_MODE_FT4}, {3.579, 3.600, BandPlan::BAND_MODE_DIGITAL}, {3.600, 4.000, BandPlan::BAND_MODE_LSB}, // 60m {5.3515, 5.356, BandPlan::BAND_MODE_CW}, {5.356, 5.361, BandPlan::BAND_MODE_FT8}, {5.361, 5.500, BandPlan::BAND_MODE_LSB}, // 40m {7.000, 7.040, BandPlan::BAND_MODE_CW}, {7.040, 7.047, BandPlan::BAND_MODE_DIGITAL}, {7.047, 7.050, BandPlan::BAND_MODE_FT4}, {7.050, 7.060, BandPlan::BAND_MODE_DIGITAL}, {7.060, 7.074, BandPlan::BAND_MODE_LSB}, {7.074, 7.077, BandPlan::BAND_MODE_FT8}, {7.077, 7.300, BandPlan::BAND_MODE_LSB}, // 30m {10.100, 10.130, BandPlan::BAND_MODE_CW}, {10.130, 10.136, BandPlan::BAND_MODE_DIGITAL}, {10.136, 10.139, BandPlan::BAND_MODE_FT8}, {10.139, 10.140, BandPlan::BAND_MODE_DIGITAL}, {10.140, 10.143, BandPlan::BAND_MODE_FT4}, {10.143, 10.150, BandPlan::BAND_MODE_DIGITAL}, // 20m {14.000, 14.070, BandPlan::BAND_MODE_CW}, {14.070, 14.074, BandPlan::BAND_MODE_DIGITAL}, {14.074, 14.080, BandPlan::BAND_MODE_FT8}, {14.080, 14.083, BandPlan::BAND_MODE_FT4}, {14.083, 14.099, BandPlan::BAND_MODE_DIGITAL}, {14.099, 14.101, BandPlan::BAND_MODE_CW}, {14.101, 14.350, BandPlan::BAND_MODE_USB}, // 17m {18.068, 18.095, BandPlan::BAND_MODE_CW}, {18.095, 18.100, BandPlan::BAND_MODE_DIGITAL}, {18.100, 18.103, BandPlan::BAND_MODE_FT8}, {18.103, 18.106, BandPlan::BAND_MODE_FT4}, {18.106, 18.109, BandPlan::BAND_MODE_DIGITAL}, {18.109, 18.111, BandPlan::BAND_MODE_CW}, {18.111, 18.168, BandPlan::BAND_MODE_USB}, // 15m {21.000, 21.070, BandPlan::BAND_MODE_CW}, {21.070, 21.074, BandPlan::BAND_MODE_DIGITAL}, {21.074, 21.077, BandPlan::BAND_MODE_FT8}, {21.077, 21.140, BandPlan::BAND_MODE_DIGITAL}, {21.140, 21.143, BandPlan::BAND_MODE_FT4}, {21.143, 21.149, BandPlan::BAND_MODE_DIGITAL}, {21.149, 21.151, BandPlan::BAND_MODE_CW}, {21.151, 21.450, BandPlan::BAND_MODE_USB}, // 12m {24.890, 24.915, BandPlan::BAND_MODE_CW}, {24.915, 24.919, BandPlan::BAND_MODE_FT8}, {24.919, 24.922, BandPlan::BAND_MODE_FT4}, {24.922, 24.929, BandPlan::BAND_MODE_DIGITAL}, {24.929, 24.931, BandPlan::BAND_MODE_CW}, {24.931, 24.990, BandPlan::BAND_MODE_USB}, // 10m {28.000, 28.070, BandPlan::BAND_MODE_CW}, {28.070, 28.074, BandPlan::BAND_MODE_DIGITAL}, {28.074, 28.077, BandPlan::BAND_MODE_FT8}, {28.077, 28.180, BandPlan::BAND_MODE_DIGITAL}, {28.180, 28.183, BandPlan::BAND_MODE_FT4}, {28.183, 28.190, BandPlan::BAND_MODE_DIGITAL}, {28.190, 28.225, BandPlan::BAND_MODE_CW}, {28.225, 29.700, BandPlan::BAND_MODE_USB}, // 6m {50.000, 50.100, BandPlan::BAND_MODE_CW}, {50.100, 50.313, BandPlan::BAND_MODE_USB}, {50.313, 50.318, BandPlan::BAND_MODE_FT8}, {50.318, 50.321, BandPlan::BAND_MODE_FT4}, {50.321, 50.400, BandPlan::BAND_MODE_DIGITAL}, {50.400, 50.500, BandPlan::BAND_MODE_CW}, {50.500, 54.000, BandPlan::BAND_MODE_PHONE}, // 4m {70.000, 70.100, BandPlan::BAND_MODE_CW}, {70.100, 70.102, BandPlan::BAND_MODE_FT8}, {70.102, 70.500, BandPlan::BAND_MODE_USB}, // 2m {144.000, 144.100, BandPlan::BAND_MODE_CW}, {144.100, 144.120, BandPlan::BAND_MODE_USB}, {144.120, 144.123, BandPlan::BAND_MODE_FT4}, {144.123, 144.174, BandPlan::BAND_MODE_USB}, {144.174, 144.176, BandPlan::BAND_MODE_FT8}, {144.176, 144.360, BandPlan::BAND_MODE_USB}, {144.360, 144.400, BandPlan::BAND_MODE_DIGITAL}, {144.400, 144.491, BandPlan::BAND_MODE_CW}, {144.491, 144.975, BandPlan::BAND_MODE_DIGITAL}, {144.975, 148.000, BandPlan::BAND_MODE_USB}, // 1.25m {222.000, 222.150, BandPlan::BAND_MODE_CW}, {222.150, 225.000, BandPlan::BAND_MODE_USB}, // 70cm {430.000, 432.000, BandPlan::BAND_MODE_USB}, {432.000, 432.065, BandPlan::BAND_MODE_CW}, {432.065, 432.067, BandPlan::BAND_MODE_FT8}, {432.067, 432.100, BandPlan::BAND_MODE_CW}, {432.100, 440.000, BandPlan::BAND_MODE_USB}, // 33cm {902.000, 928.000, BandPlan::BAND_MODE_USB}, // 23cm {1240.000, 1296.150, BandPlan::BAND_MODE_USB}, {1296.150, 1296.400, BandPlan::BAND_MODE_CW}, {1296.400, 1300.000, BandPlan::BAND_MODE_PHONE}, // 3cm QO100 // at the moment there is no other satellite that would be used, so we can afford it. // It will not affect tropo operation, because it is in the lower part of the band. {10489.505, 10489.540, BandPlan::BAND_MODE_CW}, {10489.540, 10489.580, BandPlan::BAND_MODE_FT8}, {10489.580, 10489.650, BandPlan::BAND_MODE_DIGITAL}, {10489.650, 10489.745, BandPlan::BAND_MODE_USB}, {10489.755, 10489.850, BandPlan::BAND_MODE_USB}, {10489.850, 10489.990, BandPlan::BAND_MODE_PHONE} }; static int bandModeTableSize = sizeof(r1BandModeTable) / sizeof(r1BandModeTable[0]); BandPlan::BandPlanMode BandPlan::freq2BandMode(const double freq) { FCT_IDENTIFICATION; qCDebug(function_parameters) << freq; int left = 0; int right = bandModeTableSize - 1; while ( left <= right ) { int mid = (left + right) / 2; const BandModeRange &range = r1BandModeTable[mid]; if (freq < range.start) right = mid - 1; else if (freq >= range.end) left = mid + 1; else return range.mode; } // fallback return BandPlan::BAND_MODE_PHONE; } const QString BandPlan::bandMode2BandModeGroupString(const BandPlanMode &bandPlanMode) { FCT_IDENTIFICATION; switch ( bandPlanMode ) { case BAND_MODE_CW: return BandPlan::MODE_GROUP_STRING_CW; case BAND_MODE_DIGITAL: return BandPlan::MODE_GROUP_STRING_DIGITAL; case BAND_MODE_FT8: case BAND_MODE_FT4: case BAND_MODE_FT2: return BandPlan::MODE_GROUP_STRING_FTx; case BAND_MODE_PHONE: case BAND_MODE_LSB: case BAND_MODE_USB: return BandPlan::MODE_GROUP_STRING_PHONE; case BAND_MODE_UNKNOWN: return QString(); } return QString(); } const QString BandPlan::freq2BandModeGroupString(const double freq) { FCT_IDENTIFICATION; qCDebug(function_parameters) << freq; return bandMode2BandModeGroupString(freq2BandMode(freq)); } const QString BandPlan::bandPlanMode2ExpectedMode(const BandPlanMode &bandPlanMode, QString &submode) { FCT_IDENTIFICATION; submode = QString(); switch ( bandPlanMode ) { case BAND_MODE_CW: {submode = QString(); return "CW";} case BAND_MODE_LSB: {submode = "LSB"; return "SSB";} case BAND_MODE_USB: {submode = "USB"; return "SSB";} case BAND_MODE_FT8: {return "FT8";} case BAND_MODE_FT4: {submode = "FT4";return "MFSK";} case BAND_MODE_FT2: // not currently specified - use USB case BAND_MODE_DIGITAL: {submode = "USB"; return "SSB";} // imprecise, but let's try this //case BAND_MODE_PHONE: // it can be FM, SSB, AM - no Mode Change default: submode = QString(); } return QString(); } const QString BandPlan::freq2ExpectedMode(const double freq, QString &submode) { FCT_IDENTIFICATION; qCDebug(function_parameters) << freq; return bandPlanMode2ExpectedMode(freq2BandMode(freq), submode); } const Band BandPlan::freq2Band(double freq) { FCT_IDENTIFICATION; qCDebug(function_parameters) << freq; QSqlQuery query; if ( ! query.prepare("SELECT name, start_freq, end_freq, sat_designator " "FROM bands " "WHERE :freq BETWEEN start_freq AND end_freq") ) { qWarning() << "Cannot prepare Select statement"; return Band(); } query.bindValue(0, freq); if ( ! query.exec() ) { qWarning() << "Cannot execute select statement" << query.lastError(); return Band(); } if ( query.next() ) { Band band; band.name = query.value(0).toString(); band.start = query.value(1).toDouble(); band.end = query.value(2).toDouble(); band.satDesignator = query.value(3).toString(); return band; } return Band(); } const Band BandPlan::bandName2Band(const QString &name) { FCT_IDENTIFICATION; qCDebug(function_parameters) << name; QSqlQuery query; if ( ! query.prepare("SELECT name, start_freq, end_freq, sat_designator " "FROM bands " "WHERE name = :name LIMIT 1") ) { qWarning() << "Cannot prepare Select statement"; return Band(); } query.bindValue(0, name.toLower()); if ( ! query.exec() ) { qWarning() << "Cannot execute select statement" << query.lastError(); return Band(); } if ( query.next() ) { Band band; band.name = query.value(0).toString(); band.start = query.value(1).toDouble(); band.end = query.value(2).toDouble(); band.satDesignator = query.value(3).toString(); return band; } return Band(); } const QList BandPlan::bandsList(const bool onlyDXCCBands, const bool onlyEnabled) { FCT_IDENTIFICATION; qCDebug(function_parameters) << onlyDXCCBands << onlyEnabled; QSqlQuery query; QList ret; QString stmt(QLatin1String("SELECT name, start_freq, end_freq, sat_designator " "FROM bands WHERE 1 = 1 ")); if ( onlyEnabled ) stmt.append("AND enabled = 1 "); if ( onlyDXCCBands ) { stmt.append("AND ((1.9 between start_freq and end_freq) " " OR (3.6 between start_freq and end_freq) " " OR (7.1 between start_freq and end_freq) " " OR (10.1 between start_freq and end_freq) " " OR (14.1 between start_freq and end_freq) " " OR (18.1 between start_freq and end_freq) " " OR (21.1 between start_freq and end_freq) " " OR (24.9 between start_freq and end_freq) " " OR (28.1 between start_freq and end_freq) " " OR (50.1 between start_freq and end_freq) " " OR (145.1 between start_freq and end_freq) " " OR (421.1 between start_freq and end_freq) " " OR (1241.0 between start_freq and end_freq) " " OR (2301.0 between start_freq and end_freq) " " OR (10001.0 between start_freq and end_freq)) "); } stmt.append("ORDER BY start_freq "); qCDebug(runtime) << stmt; if ( ! query.prepare(stmt) ) { qWarning() << "Cannot prepare Select statement"; return ret; } if ( ! query.exec() ) { qWarning() << "Cannot execute select statement" << query.lastError(); return ret; } while ( query.next() ) { Band band; band.name = query.value(0).toString(); band.start = query.value(1).toDouble(); band.end = query.value(2).toDouble(); band.satDesignator = query.value(3).toString(); ret << band; } return ret; } const QString BandPlan::modeToDXCCModeGroup(const QString &mode) { FCT_IDENTIFICATION; qCDebug(function_parameters) << mode; if ( mode.isEmpty() ) return QString(); QSqlQuery query; if ( !query.prepare("SELECT modes.dxcc " "FROM modes " "WHERE modes.name = :mode LIMIT 1")) { qWarning() << "Cannot prepare Select statement"; return QString(); } query.bindValue(0, mode); if ( query.exec() ) { QString ret; query.next(); ret = query.value(0).toString(); return ret; } return QString(); } const QString BandPlan::modeToModeGroup(const QString &mode) { FCT_IDENTIFICATION; return isFTxMode(mode) ? BandPlan::MODE_GROUP_STRING_FTx : BandPlan::modeToDXCCModeGroup(mode); } bool BandPlan::isFTxMode(const QString &mode) { return mode == "FT8" || mode == "FT4" || mode == "FT2"; } bool BandPlan::isFTxBandMode(BandPlanMode mode) { return mode == BAND_MODE_FT8 || mode == BAND_MODE_FT4 || mode == BAND_MODE_FT2; } BandPlan::BandPlan() { FCT_IDENTIFICATION; } const QString BandPlan::MODE_GROUP_STRING_CW = "CW"; const QString BandPlan::MODE_GROUP_STRING_DIGITAL = "DIGITAL"; const QString BandPlan::MODE_GROUP_STRING_FTx = "FTx"; const QString BandPlan::MODE_GROUP_STRING_PHONE = "PHONE"; ================================================ FILE: data/BandPlan.h ================================================ #ifndef QLOG_DATA_BANDPLAN_H #define QLOG_DATA_BANDPLAN_H #include #include "Band.h" class BandPlan { public: static const QString MODE_GROUP_STRING_CW; static const QString MODE_GROUP_STRING_DIGITAL; static const QString MODE_GROUP_STRING_FTx; static const QString MODE_GROUP_STRING_PHONE; enum BandPlanMode { BAND_MODE_UNKNOWN, BAND_MODE_CW, BAND_MODE_DIGITAL, BAND_MODE_FT8, BAND_MODE_FT4, BAND_MODE_FT2, BAND_MODE_LSB, BAND_MODE_USB, BAND_MODE_PHONE }; static BandPlanMode freq2BandMode(const double freq); static const QString bandMode2BandModeGroupString(const BandPlan::BandPlanMode &bandPlanMode); static const QString freq2BandModeGroupString(const double freq); static const QString bandPlanMode2ExpectedMode(const BandPlan::BandPlanMode &bandPlanMode, QString &submode); static const QString freq2ExpectedMode(const double freq, QString &submode); static const Band freq2Band(double freq); static const Band bandName2Band(const QString& name); static const QList bandsList(const bool onlyDXCCBands = false, const bool onlyEnabled = false); static const QString modeToDXCCModeGroup(const QString &mode); static const QString modeToModeGroup(const QString &mode); static bool isFTxMode(const QString &mode); static bool isFTxBandMode(BandPlanMode mode); BandPlan(); }; Q_DECLARE_METATYPE(BandPlan::BandPlanMode); #endif // QLOG_DATA_BANDPLAN_H ================================================ FILE: data/CWKeyProfile.cpp ================================================ #include #include #include "CWKeyProfile.h" #include "core/debug.h" MODULE_IDENTIFICATION("qlog.data.cwkeyprofile"); QDataStream& operator<<(QDataStream& out, const CWKeyProfile& v) { out << v.profileName << v.model << v.defaultSpeed << v.keyMode << v.portPath << v.baudrate << v.hostname << v.netport << v.paddleSwap << v.paddleOnlySidetone << v.sidetoneFrequency; return out; } QDataStream& operator>>(QDataStream& in, CWKeyProfile& v) { in >> v.profileName; in >> v.model; in >> v.defaultSpeed; in >> v.keyMode; in >> v.portPath; in >> v.baudrate; in >> v.hostname; in >> v.netport; in >> v.paddleSwap; in >> v.paddleOnlySidetone; in >> v.sidetoneFrequency; return in; } CWKeyProfilesManager::CWKeyProfilesManager() : ProfileManagerSQL("cwkey_profiles") { FCT_IDENTIFICATION; QSqlQuery profileQuery; if ( ! profileQuery.prepare("SELECT profile_name, model, default_speed, " " key_mode, port_pathname, baudrate, hostname, netport, paddle_swap, paddle_only_sidetone, sidetone_frequency " "FROM cwkey_profiles") ) { qWarning()<< "Cannot prepare select"; } if ( profileQuery.exec() ) { while (profileQuery.next()) { CWKeyProfile profileDB; profileDB.profileName = profileQuery.value(0).toString(); profileDB.model = CWKey::intToTypeID(profileQuery.value(1).toInt()); profileDB.defaultSpeed = profileQuery.value(2).toInt(); profileDB.keyMode = CWKey::intToModeID(profileQuery.value(3).toInt()); profileDB.portPath = profileQuery.value(4).toString(); profileDB.baudrate = profileQuery.value(5).toUInt(); profileDB.hostname = profileQuery.value(6).toString(); profileDB.netport = profileQuery.value(7).toUInt(); profileDB.paddleSwap = profileQuery.value(8).toBool(); profileDB.paddleOnlySidetone = profileQuery.value(9).toBool(); profileDB.sidetoneFrequency = profileQuery.value(10).toInt(); addProfile(profileDB.profileName, profileDB); } } else { qInfo() << "CW Key Profile DB select error " << profileQuery.lastError().text(); } } void CWKeyProfilesManager::save() { FCT_IDENTIFICATION; QSqlQuery deleteQuery; QSqlQuery insertQuery; if ( ! deleteQuery.prepare("DELETE FROM cwkey_profiles") ) { qWarning() << "Cannot prepare Delete statement"; return; } if ( ! insertQuery.prepare("INSERT INTO cwkey_profiles(profile_name, model, default_speed, key_mode, port_pathname, baudrate, hostname, netport, paddle_swap, paddle_only_sidetone, sidetone_frequency) " "VALUES (:profile_name, :model, :default_speed, :key_mode, :port_pathname, :baudrate, :hostname, :netport, :paddle_swap, :paddle_only_sidetone, :sidetone_frequency)") ) { qWarning() << "Cannot prepare Insert statement"; return; } if ( deleteQuery.exec() ) { const QStringList &keys = profileNameList(); for ( const QString &key: keys ) { const CWKeyProfile &cwKeyProfile = getProfile(key); insertQuery.bindValue(":profile_name", key); insertQuery.bindValue(":model", cwKeyProfile.model); insertQuery.bindValue(":default_speed", cwKeyProfile.defaultSpeed); insertQuery.bindValue(":key_mode", cwKeyProfile.keyMode); insertQuery.bindValue(":port_pathname", cwKeyProfile.portPath); insertQuery.bindValue(":baudrate", cwKeyProfile.baudrate); insertQuery.bindValue(":hostname", cwKeyProfile.hostname); insertQuery.bindValue(":netport", cwKeyProfile.netport); insertQuery.bindValue(":paddle_swap", cwKeyProfile.paddleSwap); insertQuery.bindValue(":paddle_only_sidetone", cwKeyProfile.paddleOnlySidetone); insertQuery.bindValue(":sidetone_frequency", cwKeyProfile.sidetoneFrequency); if ( ! insertQuery.exec() ) { qInfo() << "CW Key Profile DB insert error " << insertQuery.lastError().text() << insertQuery.lastQuery(); } } } else { qInfo() << "CW Key Profile DB delete error " << deleteQuery.lastError().text(); } saveCurProfile1(); } bool CWKeyProfile::operator==(const CWKeyProfile &profile) { return (profile.profileName == this->profileName && profile.model == this->model && profile.defaultSpeed == this->defaultSpeed && profile.keyMode == this->keyMode && profile.portPath == this->portPath && profile.baudrate == this->baudrate && profile.hostname == this->hostname && profile.netport == this->netport && profile.paddleSwap == this->paddleSwap && profile.paddleOnlySidetone == this->paddleOnlySidetone && profile.sidetoneFrequency == this->sidetoneFrequency); } bool CWKeyProfile::operator!=(const CWKeyProfile &profile) { return !operator==(profile); } ================================================ FILE: data/CWKeyProfile.h ================================================ #ifndef QLOG_DATA_CWKEYPROFILE_H #define QLOG_DATA_CWKEYPROFILE_H #include #include #include #include "data/ProfileManager.h" #include "cwkey/drivers/CWKey.h" #define DEFAULT_CWKEY_MODEL 0 // this is a hack. We have non-empty text to set empty value in Setting dialog->Rig's Assigned CW Key combo // therefore QLog use one space as a profile that meams no CW is assigned to Rig #define EMPTY_PROFILE_NAME " " class CWKeyProfile { public: CWKeyProfile() : model(CWKey::DUMMY_KEYER), keyMode(CWKey::IAMBIC_B) { defaultSpeed = 0; baudrate = 0; netport = 0; paddleSwap = false; paddleOnlySidetone = false; sidetoneFrequency = 800; }; QString profileName; CWKey::CWKeyTypeID model; qint32 defaultSpeed; CWKey::CWKeyModeID keyMode; QString portPath; quint32 baudrate; QString hostname; quint16 netport; bool paddleSwap; bool paddleOnlySidetone; qint32 sidetoneFrequency; bool operator== (const CWKeyProfile &profile); bool operator!= (const CWKeyProfile &profile); private: friend QDataStream& operator<<(QDataStream& out, const CWKeyProfile& v); friend QDataStream& operator>>(QDataStream& in, CWKeyProfile& v); }; Q_DECLARE_METATYPE(CWKeyProfile); class CWKeyProfilesManager : public ProfileManagerSQL { Q_OBJECT public: explicit CWKeyProfilesManager(); ~CWKeyProfilesManager() { }; static CWKeyProfilesManager* instance() { static CWKeyProfilesManager instance; return &instance; }; void save(); }; #endif // QLOG_DATA_CWKEYPROFILE_H ================================================ FILE: data/CWShortcutProfile.cpp ================================================ #include #include #include "CWShortcutProfile.h" #include "core/debug.h" MODULE_IDENTIFICATION("qlog.data.cwshortcutprofile"); QDataStream& operator<<(QDataStream& out, const CWShortcutProfile& v) { out << v.profileName; for( int i = 0; i < v.shortDescs.size(); i++ ) { out << v.shortDescs[i]; } for( int i = 0; i < v.macros.size(); i++ ) { out << v.macros[i]; } return out; } QDataStream& operator>>(QDataStream& in, CWShortcutProfile& v) { in >> v.profileName; for( int i = 0; i < v.shortDescs.size(); i++ ) { in >> v.shortDescs[i]; } for( int i = 0; i < v.macros.size(); i++ ) { in >> v.macros[i]; } return in; } CWShortcutProfilesManager::CWShortcutProfilesManager() : ProfileManagerSQL("cwshortcut_profiles") { FCT_IDENTIFICATION; QSqlQuery profileQuery; if ( ! profileQuery.prepare("SELECT profile_name, f1_short, f1_macro, f2_short, f2_macro, " "f3_short, f3_macro, f4_short, f4_macro, f5_short, f5_macro, " "f6_short, f6_macro, f7_short, f7_macro FROM cwshortcut_profiles") ) { qWarning()<< "Cannot prepare select"; } if ( profileQuery.exec() ) { while (profileQuery.next()) { CWShortcutProfile profileDB; int column = 0; profileDB.profileName = profileQuery.value(column++).toString(); for ( int i = 0; i < profileDB.shortDescs.size(); i++ ) { profileDB.shortDescs[i] = profileQuery.value(column++).toString(); profileDB.macros[i] = profileQuery.value(column++).toString(); } addProfile(profileDB.profileName, profileDB); } } else { qInfo() << "CW Shortcut Profile DB select error " << profileQuery.lastError().text(); } } void CWShortcutProfilesManager::save() { FCT_IDENTIFICATION; QSqlQuery deleteQuery; QSqlQuery insertQuery; if ( ! deleteQuery.prepare("DELETE FROM cwshortcut_profiles") ) { qWarning() << "Cannot prepare Delete statement"; return; } if ( ! insertQuery.prepare("INSERT INTO cwshortcut_profiles(profile_name, f1_short, f1_macro, f2_short, f2_macro," "f3_short, f3_macro, f4_short, f4_macro, f5_short, f5_macro," "f6_short, f6_macro, f7_short, f7_macro)" "VALUES (:profile_name, :f1_short, :f1_macro, :f2_short, :f2_macro," ":f3_short, :f3_macro, :f4_short, :f4_macro, :f5_short, :f5_macro," ":f6_short, :f6_macro, :f7_short, :f7_macro)") ) { qWarning() << "Cannot prepare Insert statement"; return; } if ( deleteQuery.exec() ) { const QStringList &keys = profileNameList(); for ( const QString &key: keys ) { const CWShortcutProfile &cwShortcutProfile = getProfile(key); insertQuery.bindValue(":profile_name", key); for ( int i = 0; i < cwShortcutProfile.shortDescs.size(); i++ ) { insertQuery.bindValue(QString(":f%1_short").arg(i+1), cwShortcutProfile.shortDescs[i]); } for ( int i = 0; i < cwShortcutProfile.macros.size(); i++ ) { insertQuery.bindValue(QString(":f%1_macro").arg(i+1), cwShortcutProfile.macros[i]); } if ( ! insertQuery.exec() ) { qInfo() << "CW Shortcut Profile DB insert error " << insertQuery.lastError().text() << insertQuery.lastQuery(); } } } else { qInfo() << "CW Shortcut Profile DB delete error " << deleteQuery.lastError().text(); } saveCurProfile1(); } bool CWShortcutProfile::operator==(const CWShortcutProfile &profile) { return (profile.profileName == this->profileName && profile.shortDescs == this->shortDescs && profile.macros == this->macros); } bool CWShortcutProfile::operator!=(const CWShortcutProfile &profile) { return !operator==(profile); } ================================================ FILE: data/CWShortcutProfile.h ================================================ #ifndef QLOG_DATA_CWSHORTCUTPROFILE_H #define QLOG_DATA_CWSHORTCUTPROFILE_H #include #include #include #include "data/ProfileManager.h" #define MAX_SHORTCUT_KEYS 7 class CWShortcutProfile { public: CWShortcutProfile() { shortDescs.resize(MAX_SHORTCUT_KEYS); macros.resize(MAX_SHORTCUT_KEYS); }; QString profileName; QVector shortDescs; QVector macros; bool operator== (const CWShortcutProfile &profile); bool operator!= (const CWShortcutProfile &profile); private: friend QDataStream& operator<<(QDataStream& out, const CWShortcutProfile& v); friend QDataStream& operator>>(QDataStream& in, CWShortcutProfile& v); }; Q_DECLARE_METATYPE(CWShortcutProfile); class CWShortcutProfilesManager : public ProfileManagerSQL { Q_OBJECT public: explicit CWShortcutProfilesManager(); ~CWShortcutProfilesManager() { }; static CWShortcutProfilesManager* instance() { static CWShortcutProfilesManager instance; return &instance; }; void save(); }; #endif // QLOG_DATA_CWSHORTCUTPROFILE_H ================================================ FILE: data/Callsign.cpp ================================================ #include "Callsign.h" #include "core/debug.h" MODULE_IDENTIFICATION("qlog.core.callsign"); Callsign::Callsign(const QString &callsign, QObject *parent) : QObject{parent}, fullCallsign(callsign.toUpper()), valid(false) { FCT_IDENTIFICATION; QRegularExpression callsignRE = callsignRegEx(); QRegularExpressionMatch match = callsignRE.match(fullCallsign); if ( match.hasMatch() ) { //it is a valid callsign valid = true; hostPrefixWithDelimiter = match.captured(1); hostPrefix = match.captured(2); base = match.captured(3); basePrefix = match.captured(4); basePrefixNumber = match.captured(5); suffixWithDelimiter = match.captured(7); suffix = match.captured(8); qCDebug(runtime) << hostPrefix << base << suffix; } else { //it is an invalid callsign fullCallsign = QString(); } } const QRegularExpression Callsign::callsignRegEx() { FCT_IDENTIFICATION; return QRegularExpression(callsignRegExString(), QRegularExpression::CaseInsensitiveOption); } const QString Callsign::callsignRegExString() { FCT_IDENTIFICATION; return QString("^(([A-Z0-9]+)[\\/])?(([A-Z][0-9]|[A-Z]{1,2}|[0-9][A-Z])([0-9]|[0-9]+)([A-Z]+))([\\/]([A-Z0-9]+))?"); } const QString Callsign::getCallsign() const { FCT_IDENTIFICATION; return fullCallsign; } const QString Callsign::getHostPrefix() const { FCT_IDENTIFICATION; return hostPrefix; } const QString Callsign::getHostPrefixWithDelimiter() const { FCT_IDENTIFICATION; return hostPrefixWithDelimiter; } const QString Callsign::getBase() const { FCT_IDENTIFICATION; return base; } const QString Callsign::getBasePrefix() const { FCT_IDENTIFICATION; return basePrefix; } const QString Callsign::getBasePrefixNumber() const { FCT_IDENTIFICATION; return basePrefixNumber; } const QString Callsign::getSuffix() const { FCT_IDENTIFICATION; return suffix; } const QString Callsign::getSuffixWithDelimiter() const { FCT_IDENTIFICATION; return suffixWithDelimiter; } const QString Callsign::getWPXPrefix() const { FCT_IDENTIFICATION; if ( !isValid() ) return QString(); // defined here // https://www.cqwpx.com/rules.htm // inspired here // https://git.fkurz.net/dj1yfk/yfklog/src/branch/develop/yfksubs.pl#L605 /********************* * ONLY BASE CALLSIGN *********************/ if ( getBase() != QString() && getHostPrefix() == QString() && getSuffix() == QString() ) { // only callsign // return callsign prefix + prefix number // OL80ABC -> OL80 // OK1ABC -> OK1 return getBasePrefix() + getBasePrefixNumber(); } /********************* * HOST PREFIX PRESENT *********************/ if ( getHostPrefix() != QString() ) { // callsign has a Host prefix SP/OK1XXX // we do not look at the suffix and assign automatically HostPrefix + '0' if ( getHostPrefix().back().isDigit() ) return getHostPrefix(); return getHostPrefix() + QString("0"); } /**************** * SUFFIX PRESENT ****************/ if ( getSuffix().length() == 1) // some countries add single numbers as suffix to designate a call area, e.g. /4 { bool isNumber = false; (void)suffix.toInt(&isNumber); if ( isNumber ) { // callsign suffix is a number // VE7ABC/2 -> VE2 return getBasePrefix() + getSuffix(); } // callsign suffix is not a number // OK1ABC/P -> OK1 return getBasePrefix() + getBasePrefixNumber(); } /*************************** * SUFFIX PRESENT LENGTH > 1 ***************************/ if ( secondarySpecialSuffixes.contains(getSuffix()) ) { // QRP, MM etc. // OK1ABC/AM -> OK1 return getBasePrefix() + getBasePrefixNumber(); } // valid prefix should contain a number in the last position - check it // and prefix is not just a number // N8ABC/KH9 -> KH9 bool isNumber = false; (void)getSuffix().toInt(&isNumber); if ( isNumber ) { // suffix contains 2 and more numbers - ignore it return getBasePrefix() + getBasePrefixNumber(); } // suffix is combination letters and digits and last position is a number if ( getSuffix().back().isDigit() ) return getSuffix(); // prefix does not contain a number - add "0" return getSuffix() + QString("0"); } bool Callsign::isValid() const { FCT_IDENTIFICATION; return valid; } // Based on wiki information // https://en.wikipedia.org/wiki/Amateur_radio_call_signs const QStringList Callsign::secondarySpecialSuffixes = { "A", // operator at a secondary location registered with the licensing authorities "AM", // aeronautical mobile "M", // mobile operation "MM", // marine mobile "P", // portable operation "QRP", // QRP - unofficial "R", // repeaters "B", // beacon "LGT" // 'LIGHTHOUSE' or 'LIGHTSHIP' - unofficial }; ================================================ FILE: data/Callsign.h ================================================ #ifndef QLOG_DATA_CALLSIGN_H #define QLOG_DATA_CALLSIGN_H #include #include class Callsign : public QObject { Q_OBJECT public: explicit Callsign(const QString &callsign, QObject *parent = nullptr); static const QRegularExpression callsignRegEx(); static const QString callsignRegExString(); static const QStringList secondarySpecialSuffixes; const QString getCallsign() const; const QString getHostPrefix() const; const QString getHostPrefixWithDelimiter() const; const QString getBase() const; const QString getBasePrefix() const; const QString getBasePrefixNumber() const; const QString getSuffix() const; const QString getSuffixWithDelimiter() const; const QString getWPXPrefix() const; bool isValid() const; private: QString fullCallsign; QString hostPrefix; QString hostPrefixWithDelimiter; QString base; QString basePrefix; QString basePrefixNumber; QString suffix; QString suffixWithDelimiter; bool valid; }; #endif // QLOG_DATA_CALLSIGN_H ================================================ FILE: data/Data.cpp ================================================ #include #include #include #include #include #include "Data.h" #include "data/Callsign.h" #include "core/debug.h" #include "BandPlan.h" #include "data/StationProfile.h" #include "core/LogParam.h" MODULE_IDENTIFICATION("qlog.data.data"); Data::Data(QObject *parent) : QObject(parent), zd(nullptr), isDXCCQueryValid(false) { FCT_IDENTIFICATION; loadContests(); loadPropagationModes(); loadLegacyModes(); loadDxccFlags(); loadSatModes(); loadIOTA(); loadSOTA(); loadWWFF(); loadPOTA(); loadTZ(); // dxcc_prefixes_ad1c.exact DESC, dxcc_prefixes_ad1c.prefix DESC // is used because it prefers an exact-match record over a partial-match records isDXCCQueryValid = queryDXCC.prepare( "SELECT " " dxcc_entities_ad1c.id, " " dxcc_entities_ad1c.name, " " dxcc_entities_ad1c.prefix, " " dxcc_entities_ad1c.cont, " " CASE " " WHEN (dxcc_prefixes_ad1c.cqz != 0) " " THEN dxcc_prefixes_ad1c.cqz " " ELSE dxcc_entities_ad1c.cqz " " END AS cqz, " " CASE " " WHEN (dxcc_prefixes_ad1c.ituz != 0) " " THEN dxcc_prefixes_ad1c.ituz " " ELSE dxcc_entities_ad1c.ituz " " END AS ituz , " " dxcc_entities_ad1c.lat, " " dxcc_entities_ad1c.lon, " " dxcc_entities_ad1c.tz, " " dxcc_prefixes_ad1c.exact " "FROM dxcc_prefixes_ad1c " "INNER JOIN dxcc_entities_ad1c ON (dxcc_prefixes_ad1c.dxcc = dxcc_entities_ad1c.id) " "WHERE (dxcc_prefixes_ad1c.prefix = :callsign and dxcc_prefixes_ad1c.exact = true) " " OR (dxcc_prefixes_ad1c.exact = false and :callsign LIKE dxcc_prefixes_ad1c.prefix || '%') " "ORDER BY dxcc_prefixes_ad1c.exact DESC, dxcc_prefixes_ad1c.prefix DESC " "LIMIT 1 " ); isDXCCClublogQueryValid = queryDXCCClublog.prepare( "SELECT e.id, " " e.name, " " e.prefix, " " e.cont, " " COALESCE(z.cqz, " " CASE WHEN (p.cqz != 0) THEN p.cqz ELSE e.cqz END) AS cqz, " " CASE WHEN z.cqz IS NOT NULL THEN NULL " " WHEN (p.ituz != 0) THEN p.ituz " " ELSE e.ituz END AS ituz, " " e.lat, " " e.lon, " " p.exact " " FROM dxcc_prefixes_clublog p" " INNER JOIN dxcc_entities_clublog e ON (p.dxcc = e.id) " " LEFT JOIN dxcc_zone_exceptions_clublog z ON ( z.call = :exactcall AND :dxccdate BETWEEN COALESCE(z.start, '0001-01-01 00:00:00') " " AND COALESCE(z.end, '9999-12-31 23:59:59')) " " WHERE ((p.prefix = :exactcall and p.exact = 1) " " OR (p.exact = 0 and :modifiedcall LIKE p.prefix || '%')) " " AND :dxccdate BETWEEN COALESCE(p.start, '0001-01-01 00:00:00') " " AND COALESCE(p.end, '9999-12-31 23:59:59') " " ORDER BY p.exact DESC, p.prefix DESC LIMIT 1" ); isDXCCIDAD1CQueryValid = queryDXCCIDAD1C.prepare( " SELECT dxcc_entities_ad1c.id, dxcc_entities_ad1c.name, dxcc_entities_ad1c.prefix, dxcc_entities_ad1c.cont, " " dxcc_entities_ad1c.cqz, dxcc_entities_ad1c.ituz, dxcc_entities_ad1c.lat, dxcc_entities_ad1c.lon, dxcc_entities_ad1c.tz " " FROM dxcc_entities_ad1c " " WHERE dxcc_entities_ad1c.id = :dxccid" ); isDXCCIDClublogQueryValid = queryDXCCIDClublog.prepare( " SELECT c.id, c.name, c.prefix, c.cont, " " c.cqz, c.ituz, c.lat, c.lon " " FROM dxcc_entities_clublog c " " WHERE c.id = :dxccid" ); isSOTAQueryValid = querySOTA.prepare( "SELECT summit_code," " association_name," " region_name," " summit_name," " altm," " altft," " gridref1," " gridref2," " longitude," " latitude," " points," " bonus_points," " valid_from," " valid_to " "FROM sota_summits " "WHERE summit_code = :code" ); isPOTAQueryValid = queryPOTA.prepare( "SELECT reference," " name," " active," " entityID," " locationDesc," " latitude," " longitude," " grid " "FROM pota_directory " "WHERE reference = :code" ); isWWFFQueryValid = queryWWFF.prepare( "SELECT reference," " status," " name," " program," " dxcc," " state," " county," " continent," " iota," " iaruLocator," " latitude," " longitude," " iucncat," " valid_from," " valid_to " "FROM wwff_directory " "WHERE reference = :reference" ); } Data::~Data() { FCT_IDENTIFICATION; if ( zd ) { ZDCloseDatabase(zd); } } #define RETCODE(a) \ dxccStatusCache.insert(dxcc, myDXCC, band, mode, new DxccStatus(a)); \ return ((a)); DxccStatus Data::dxccStatus(int dxcc, const QString &band, const QString &mode) { FCT_IDENTIFICATION; qCDebug(function_parameters) << dxcc << " " << band << " " << mode; const int myDXCC = StationProfilesManager::instance()->getCurProfile1().dxcc; DxccStatus *statusFromCache = dxccStatusCache.value(dxcc, myDXCC, band, mode); if ( statusFromCache ) return *statusFromCache; // FTx modes (FT8, FT4, FT2) are stored in contacts with modes.dxcc = 'DIGITAL', // so we use DIGITAL as the effective mode group when querying for FTx. const QString modeForQuery = ( mode == BandPlan::MODE_GROUP_STRING_FTx ) ? BandPlan::MODE_GROUP_STRING_DIGITAL : mode; QString sql_mode(":mode"); if ( modeForQuery != BandPlan::MODE_GROUP_STRING_CW && modeForQuery != BandPlan::MODE_GROUP_STRING_PHONE && modeForQuery != BandPlan::MODE_GROUP_STRING_DIGITAL ) { sql_mode = "(SELECT modes.dxcc FROM modes WHERE modes.name = :mode LIMIT 1) "; } QStringList dxccConfirmedByCond(QLatin1String("0=1")); // if no option is selected then always false if ( LogParam::getDxccConfirmedByLotwState() ) dxccConfirmedByCond << QLatin1String("all_dxcc_qsos.lotw_qsl_rcvd = 'Y'"); if ( LogParam::getDxccConfirmedByPaperState() ) dxccConfirmedByCond << QLatin1String("all_dxcc_qsos.qsl_rcvd = 'Y'"); if ( LogParam::getDxccConfirmedByEqslState() ) dxccConfirmedByCond << QLatin1String("all_dxcc_qsos.eqsl_qsl_rcvd = 'Y'"); QSqlQuery query; QString sqlStatement = QString("WITH all_dxcc_qsos AS (SELECT DISTINCT contacts.mode, contacts.band, " " contacts.qsl_rcvd, contacts.lotw_qsl_rcvd, contacts.eqsl_qsl_rcvd " " FROM contacts " " WHERE dxcc = :dxcc %1) " " SELECT (SELECT 1 FROM all_dxcc_qsos LIMIT 1) as entity," " (SELECT 1 FROM all_dxcc_qsos WHERE band = :band LIMIT 1) as band, " " (SELECT 1 FROM all_dxcc_qsos INNER JOIN modes ON (modes.name = all_dxcc_qsos.mode) " " WHERE modes.dxcc = %2 LIMIT 1) as mode, " " (SELECT 1 FROM all_dxcc_qsos INNER JOIN modes ON (modes.name = all_dxcc_qsos.mode) " " WHERE modes.dxcc = %3 AND all_dxcc_qsos.band = :band LIMIT 1) as slot, " " (SELECT 1 FROM all_dxcc_qsos INNER JOIN modes ON (modes.name = all_dxcc_qsos.mode) " " WHERE modes.dxcc = %4 AND all_dxcc_qsos.band = :band " " AND (%5) LIMIT 1) as confirmed") .arg(( myDXCC != 0 ) ? QString(" AND my_dxcc = %1").arg(myDXCC) : "", sql_mode, sql_mode, sql_mode, dxccConfirmedByCond.join(" OR ")); if ( ! query.prepare(sqlStatement) ) { qWarning() << "Cannot prepare Select statement"; return DxccStatus::UnknownStatus; } query.bindValue(":dxcc", dxcc); query.bindValue(":band", band); query.bindValue(":mode", modeForQuery); if ( ! query.exec() ) { qWarning() << "Cannot execute Select statement" << query.lastError(); return DxccStatus::UnknownStatus; } if ( query.next() ) { if ( query.value(0).toString().isEmpty() ) { RETCODE(DxccStatus::NewEntity); } if ( query.value(1).toString().isEmpty() ) { if ( query.value(2).toString().isEmpty() ) { RETCODE(DxccStatus::NewBandMode); } else { RETCODE(DxccStatus::NewBand); } } if ( query.value(2).toString().isEmpty() ) { RETCODE(DxccStatus::NewMode); } if ( query.value(3).toString().isEmpty() ) { RETCODE(DxccStatus::NewSlot); } if ( query.value(4).toString().isEmpty() ) { RETCODE(DxccStatus::Worked); } else { RETCODE(DxccStatus::Confirmed); } } RETCODE(DxccStatus::UnknownStatus); } #undef RETCODE QStringList Data::contestList() { FCT_IDENTIFICATION; QStringList contestLOV; QSqlQuery query(QLatin1String("SELECT DISTINCT contest_id FROM contacts ORDER BY 1 COLLATE LOCALEAWARE ASC")); while ( query.next() ) contestLOV << query.value(0).toString(); return contestLOV + contests.keys(); } #define RETURNCODE(a) \ qCDebug(runtime) << "new DXCC Status: " << (a); \ return ((a)) DxccStatus Data::dxccNewStatusWhenQSOAdded(const DxccStatus &oldStatus, const qint32 oldDxcc, const QString &oldBand, const QString &oldMode, const qint32 newDxcc, const QString &newBand, const QString &newMode) { FCT_IDENTIFICATION; qCDebug(function_parameters) << oldStatus << oldDxcc << oldBand << oldMode << newDxcc << newBand << newMode; if ( oldDxcc != newDxcc ) { /* No change */ RETURNCODE(oldStatus); } if ( oldBand == newBand && oldMode == newMode ) { if ( oldStatus < DxccStatus::Worked ) { RETURNCODE(DxccStatus::Worked); } else { RETURNCODE(oldStatus); } } /*************/ /* NewEntity */ /*************/ if ( oldStatus == DxccStatus::NewEntity ) { if ( oldBand != newBand && oldMode != newMode ) { RETURNCODE(DxccStatus::NewBandMode); } if ( oldBand != newBand && oldMode == newMode ) { RETURNCODE(DxccStatus::NewBand); } if ( oldBand == newBand && oldMode != newMode ) { RETURNCODE(DxccStatus::NewMode); } } /***************/ /* NewBandMode */ /***************/ if ( oldStatus == DxccStatus::NewBandMode ) { if ( oldBand != newBand && oldMode != newMode ) { RETURNCODE(DxccStatus::NewBandMode); } if ( oldBand != newBand && oldMode == newMode ) { RETURNCODE(DxccStatus::NewBand); } if ( oldBand == newBand && oldMode != newMode ) { RETURNCODE(DxccStatus::NewMode); } } /*************/ /* NewBand */ /*************/ if ( oldStatus == DxccStatus::NewBand ) { if ( oldBand != newBand && oldMode != newMode ) { RETURNCODE(DxccStatus::NewBand); } if ( oldBand != newBand && oldMode == newMode ) { RETURNCODE(DxccStatus::NewBand); } if ( oldBand == newBand && oldMode != newMode ) { RETURNCODE(DxccStatus::NewSlot); } } /*************/ /* NewMode */ /*************/ if ( oldStatus == DxccStatus::NewMode ) { if ( oldBand != newBand && oldMode != newMode ) { RETURNCODE(DxccStatus::NewMode); } if ( oldBand != newBand && oldMode == newMode ) { RETURNCODE(DxccStatus::NewSlot); } if ( oldBand == newBand && oldMode != newMode ) { RETURNCODE(DxccStatus::NewMode); } } /*************/ /* NewSlot */ /*************/ if ( oldStatus == DxccStatus::NewSlot ) { RETURNCODE(DxccStatus::NewSlot); } /*************/ /* Worked */ /*************/ if ( oldStatus == DxccStatus::Worked ) { RETURNCODE(DxccStatus::Worked); } /***************/ /* Confirmed */ /***************/ if ( oldStatus == DxccStatus::Confirmed ) { RETURNCODE(DxccStatus::Confirmed); } RETURNCODE(DxccStatus::UnknownStatus); } qulonglong Data::dupeNewCountWhenQSOAdded(qulonglong oldCounter, const QString &oldBand, const QString &oldMode, const QString &addedBand, const QString &addedMode) { FCT_IDENTIFICATION; qCDebug(function_parameters) << oldCounter << oldBand << oldMode << addedBand << addedMode; int dupeType = LogParam::getContestDupeType(); const QString &contestID = LogParam::getContestID(); qCDebug(runtime) << dupeType << contestID; if ( contestID.isEmpty() ) return oldCounter; bool shouldIncrease = (dupeType == DupeType::ALL_BANDS) || (dupeType == DupeType::EACH_BAND && oldBand == addedBand) || (dupeType == DupeType::EACH_BAND_MODE && oldBand == addedBand && oldMode == addedMode); RETURNCODE( shouldIncrease ? oldCounter + 1 : oldCounter); } qulonglong Data::dupeNewCountWhenQSODelected(qulonglong oldCounter, const QString &oldBand, const QString &oldMode, const QString &deletedBand, const QString &deletedMode) { FCT_IDENTIFICATION; qCDebug(function_parameters) << oldCounter << oldBand << oldMode << deletedBand << deletedMode; if ( oldCounter == 0 ) return 0; int dupeType = LogParam::getContestDupeType(); const QString &contestID = LogParam::getContestID(); qCDebug(runtime) << dupeType << contestID; if ( contestID.isEmpty() ) return oldCounter; bool shouldDecrease = (dupeType == DupeType::ALL_BANDS) || (dupeType == DupeType::EACH_BAND && oldBand == deletedBand) || (dupeType == DupeType::EACH_BAND_MODE && oldBand == deletedBand && oldMode == deletedMode); RETURNCODE( shouldDecrease ? oldCounter - 1 : oldCounter); } #undef RETURNCODE QColor Data::statusToColor(const DxccStatus &status, bool isDupe, const QColor &defaultColor) { FCT_IDENTIFICATION; qCDebug(function_parameters) << status << isDupe; if ( isDupe ) return QColor(109, 109, 109, 100); switch (status) { case DxccStatus::NewEntity: return QColor(255, 58, 9); case DxccStatus::NewBand: case DxccStatus::NewMode: case DxccStatus::NewBandMode: return QColor(76, 200, 80); case DxccStatus::NewSlot: return QColor(30, 180, 230); case DxccStatus::Worked: return QColor(255,165,0); default: return defaultColor; } } QString Data::colorToHTMLColor(const QColor &in_color) { FCT_IDENTIFICATION; return in_color.name(QColor::HexRgb); } QString Data::statusToText(const DxccStatus &status) { FCT_IDENTIFICATION; qCDebug(function_parameters) << status; switch (status) { case DxccStatus::NewEntity: return tr("New Entity"); case DxccStatus::NewBand: return tr("New Band"); case DxccStatus::NewMode: return tr("New Mode"); case DxccStatus::NewBandMode: return tr("New Band&Mode"); case DxccStatus::NewSlot: return tr("New Slot"); case DxccStatus::Confirmed: return tr("Confirmed"); case DxccStatus::Worked: return tr("Worked"); default: return QString("Unknown"); } } int Data::getITUZMin() { FCT_IDENTIFICATION; qCDebug(runtime) << 1; return 1; } int Data::getITUZMax() { FCT_IDENTIFICATION; qCDebug(runtime) << 90; return 90; } int Data::getCQZMin() { FCT_IDENTIFICATION; qCDebug(runtime) << 1; return 1; } int Data::getCQZMax() { FCT_IDENTIFICATION; qCDebug(runtime) << 40; return 40; } QString Data::debugFilename() { QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)); const QString debugFilename = "qlog_debug_" + QDateTime::currentDateTime().toString("yyyyMMddhhmmss") + ".log"; return dir.filePath(debugFilename); } double Data::MHz2UserFriendlyFreq(double freqMHz, QString &unit, unsigned char &efectiveDecP) { FCT_IDENTIFICATION; if ( freqMHz < 0.001 ) { unit = tr("Hz"); efectiveDecP = 0; return freqMHz * 1000000.0; } if ( freqMHz < 1 ) { unit = tr("kHz"); efectiveDecP = 3; return freqMHz * 1000.0; } if ( freqMHz >= 1000 ) { unit = tr("GHz"); efectiveDecP = 3; return freqMHz / 1000.0; } unit = tr("MHz"); efectiveDecP = 3; return freqMHz; } const QStringList &Data::getContinentList() { static const QStringList continents{"AF", "AN", "AS", "EU", "NA", "OC", "SA"}; return continents; } QPair Data::legacyMode(const QString &mode) { FCT_IDENTIFICATION; // used in the case of external programs that generate an invalid ADIF modes. // Database Mode Table cannot be used because these programs have different mode-strings. qCDebug(function_parameters) << mode; return legacyModes.value(mode); } QString Data::getIANATimeZone(double lat, double lon) { FCT_IDENTIFICATION; qCDebug(function_parameters) << lat << lon; QString ret; if ( zd ) { ret = ZDHelperSimpleLookupString(zd, static_cast(lat), static_cast(lon)); } qCDebug(runtime) << ret; return ret; } QStringList Data::sigIDList() { FCT_IDENTIFICATION; QStringList sigLOV; QSqlQuery query(QLatin1String("SELECT DISTINCT sig_intl FROM contacts")); while ( query.next() ) sigLOV << query.value(0).toString(); return sigLOV; } void Data::invalidateDXCCStatusCache(const QSqlRecord &record) { FCT_IDENTIFICATION; dxccStatusCache.invalidate(record.value("dxcc").toInt(), StationProfilesManager::instance()->getCurProfile1().dxcc); } void Data::invalidateSetOfDXCCStatusCache(const QSet &entities) { FCT_IDENTIFICATION; int myDXCC = StationProfilesManager::instance()->getCurProfile1().dxcc; for ( uint entity : entities ) dxccStatusCache.invalidate(entity, myDXCC); } void Data::clearDXCCStatusCache() { FCT_IDENTIFICATION; dxccStatusCache.clear(); } qulonglong Data::countDupe(const QString &callsign, const QString &band, const QString &mode) { FCT_IDENTIFICATION; qCDebug(function_parameters) << callsign << band << mode; int dupeType = LogParam::getContestDupeType(); const QString &contestID = LogParam::getContestID(); const QDateTime &dupeStartTime = LogParam::getContestDupeDate(); qCDebug(runtime) << dupeType << dupeStartTime << contestID; if ( contestID.isEmpty() || dupeType == DupeType::NO_CHECK || !dupeStartTime.isValid() ) return false; QStringList whereClause = { "callsign = :callsign", "contest_id = :contestid" }; if ( dupeType >= DupeType::ALL_BANDS ) whereClause << QLatin1String("datetime(start_time) >= datetime(:date)"); if ( dupeType >= DupeType::EACH_BAND) whereClause << QLatin1String("band = :band"); // FTx modes are stored with modes.dxcc = 'DIGITAL', so map FTx to DIGITAL. const QString modeForQuery = ( mode == BandPlan::MODE_GROUP_STRING_FTx ) ? BandPlan::MODE_GROUP_STRING_DIGITAL : mode; if ( dupeType >= DupeType::EACH_BAND_MODE ) { QString sql_mode = (modeForQuery != BandPlan::MODE_GROUP_STRING_CW && modeForQuery != BandPlan::MODE_GROUP_STRING_PHONE && modeForQuery != BandPlan::MODE_GROUP_STRING_DIGITAL) ? "(SELECT modes.dxcc FROM modes WHERE modes.name = :mode LIMIT 1)" : ":mode"; whereClause << QString("m.dxcc = %1").arg(sql_mode); } QString queryString("SELECT COUNT(1) " "FROM contacts c " "INNER JOIN modes m ON (m.name = c.mode) " "WHERE %1 "); QSqlQuery query; if ( ! query.prepare(queryString.arg(whereClause.join(" AND ")))) { qWarning() << "Cannot prepare Select statement" << queryString.arg(whereClause.join(" AND ")); return false; } query.bindValue(":callsign", callsign); query.bindValue(":date", dupeStartTime); query.bindValue(":band", band); query.bindValue(":mode", modeForQuery); query.bindValue(":contestid", contestID); if ( ! query.exec() ) { qWarning() << "Cannot execute Select statement" << query.lastError() << query.lastQuery(); return false; } return (query.first()) ? query.value(0).toULongLong() : 0ULL; } QString Data::safeQueryString(const QUrlQuery &query) { FCT_IDENTIFICATION; QUrlQuery safe; const QList> &items = query.queryItems(QUrl::FullyDecoded); for ( const auto &item : items ) { if ( item.first.compare("password", Qt::CaseInsensitive) == 0 || item.first.compare("code", Qt::CaseInsensitive) == 0) safe.addQueryItem(item.first, "***MASKED***"); else safe.addQueryItem(item.first, item.second); } return safe.query(QUrl::FullyEncoded); } void Data::loadContests() { FCT_IDENTIFICATION; QFile file(":/res/data/contests.json"); file.open(QIODevice::ReadOnly | QIODevice::Text); QByteArray data = file.readAll(); const QList objectList = QJsonDocument::fromJson(data).toVariant().toList(); for ( const QVariant &object : objectList ) { const QVariantMap &contestData = object.toMap(); const QString &id = contestData.value("id").toString(); const QString &name = contestData.value("name").toString(); contests.insert(id, name); } } void Data::loadPropagationModes() { FCT_IDENTIFICATION; QFile file(":/res/data/propagation_modes.json"); file.open(QIODevice::ReadOnly | QIODevice::Text); QByteArray data = file.readAll(); const QList objects = QJsonDocument::fromJson(data).toVariant().toList(); for ( const QVariant &object : objects ) { const QVariantMap &propagationModeData = object.toMap(); const QString &id = propagationModeData.value("id").toString(); const QString &name = tr(propagationModeData.value("name").toString().toUtf8().constData()); propagationModes.insert(id, name); } } void Data::loadLegacyModes() { FCT_IDENTIFICATION; // Load conversion table from non-ADIF mode to ADIF mode/submode // used in the case of external programs that generate an invalid ADIF modes. // Database Mode Table cannot be used because these programs have different mode-strings. QFile file(":/res/data/legacy_modes.json"); file.open(QIODevice::ReadOnly | QIODevice::Text); QByteArray data = file.readAll(); QVariantMap extModes = QJsonDocument::fromJson(data).toVariant().toMap(); const QList keys = extModes.keys(); for ( const QString &key : keys ) { const QVariantMap &legacyModeData = extModes[key].toMap(); const QString &mode = legacyModeData.value("mode").toString(); const QString &submode = legacyModeData.value("submode").toString(); QPair modes = QPair(mode, submode); legacyModes.insert(key, modes); } } void Data::loadDxccFlags() { FCT_IDENTIFICATION; QFile file(":/res/data/dxcc.json"); file.open(QIODevice::ReadOnly | QIODevice::Text); QByteArray data = file.readAll(); const QList &objects = QJsonDocument::fromJson(data).toVariant().toList(); for ( const QVariant &object : objects ) { const QVariantMap &dxccData = object.toMap(); int id = dxccData.value("id").toInt(); dxccEntityStaticInfo.insert(id, dxccData); } } void Data::loadSatModes() { FCT_IDENTIFICATION; QFile file(":/res/data/sat_modes.json"); file.open(QIODevice::ReadOnly | QIODevice::Text); QByteArray data = file.readAll(); const QList &objects = QJsonDocument::fromJson(data).toVariant().toList(); for ( const QVariant &object : objects ) { const QVariantMap &satModesData = object.toMap(); const QString &id = satModesData.value("id").toString(); const QString &name = satModesData.value("name").toString(); satModes.insert(id, name); } } void Data::loadIOTA() { FCT_IDENTIFICATION; QSqlQuery query("SELECT iotaid, islandname FROM iota"); while ( query.next() ) { const QString &iotaID = query.value(0).toString(); const QString &islandName = query.value(1).toString(); iotaRef.insert(iotaID, islandName); } } void Data::loadSOTA() { FCT_IDENTIFICATION; QSqlQuery query("SELECT summit_code FROM sota_summits"); while ( query.next() ) { const QString &summitCode = query.value(0).toString(); sotaRefID.insert(summitCode, QString()); } } void Data::loadWWFF() { QSqlQuery query("SELECT reference FROM wwff_directory"); while ( query.next() ) { const QString &reference = query.value(0).toString(); wwffRefID.insert(reference, QString()); } } void Data::loadPOTA() { FCT_IDENTIFICATION; QSqlQuery query("SELECT reference FROM pota_directory"); while ( query.next() ) { const QString &reference = query.value(0).toString(); potaRefID.insert(reference, QString()); } } QCompleter* Data::createCountyCompleter(int dxcc, QObject *parent) { FCT_IDENTIFICATION; QSqlQuery query; if ( dxcc != 0 ) { query.prepare("SELECT code FROM adif_enum_secondary_subdivision " "WHERE dxcc = :dxcc ORDER BY code"); query.bindValue(":dxcc", dxcc); } else query.prepare("SELECT code FROM adif_enum_secondary_subdivision ORDER BY code"); if ( !query.exec() ) { qCWarning(runtime) << query.lastError().text(); return nullptr; } QStringList list; while ( query.next() ) list << query.value(0).toString(); if ( list.isEmpty() ) return nullptr; QCompleter *completer = new QCompleter(list, parent); completer->setCaseSensitivity(Qt::CaseInsensitive); completer->setFilterMode(Qt::MatchStartsWith); completer->setModelSorting(QCompleter::CaseSensitivelySortedModel); return completer; } void Data::loadTZ() { FCT_IDENTIFICATION; QFile file (":/res/data/timezone21.bin"); file.open(QIODevice::ReadOnly); uchar *tzMap = file.map(0, file.size()); if ( tzMap ) { zd = ZDOpenDatabaseFromMemory(tzMap, file.size()); if ( !zd ) { qWarning() << "Cannot open TZ Database"; } } else { qWarning() << "Cannot map TZ File to memory"; } } DxccEntity Data::lookupDxcc(const QString &callsign) { FCT_IDENTIFICATION; #if 0 //qInfo() << "Start AD1C"; DxccEntity ad1cDXCCData = lookupDxccAD1C(callsign); //qInfo() << "Finished AD1C"; DxccEntity clublogDXCCData = lookupDxccClublog(callsign); //qInfo() << "Finished Clublog"; if ( ad1cDXCCData.dxcc != clublogDXCCData.dxcc || ad1cDXCCData.cqz != clublogDXCCData.cqz || ad1cDXCCData.ituz != clublogDXCCData.ituz) { qInfo(runtime) << "DIFF for call " << callsign << "AD1C:" << ad1cDXCCData.dxcc << ad1cDXCCData.cqz << ad1cDXCCData.ituz << "Clublog:" << clublogDXCCData.dxcc << clublogDXCCData.cqz << clublogDXCCData.ituz; } //qInfo() << "AD1C:" << ad1cDXCCData.dxcc << ad1cDXCCData.cqz << ad1cDXCCData.ituz // << "Clublog:" << clublogDXCCData.dxcc << clublogDXCCData.cqz << clublogDXCCData.ituz; return ad1cDXCCData; #else // LF: The AD1C method seems more accurate for current data regarding ITU and CQZ. // The Clublog method is good for determining historical data and CQZ, but unfortunately, // the ITU zone is not reliable in this method because it’s not listed and has to be // calculated. Therefore, I decided to use the AD1C method as the general approach and only // use Clublog in exceptional cases. Once Clublog starts distributing ITUZ, we should // completely switch to the Clublog method. // I also believes that the AD1C method is more accurate in determining special exceptions // for American callsigns. return lookupDxccAD1C(callsign); #endif } DxccEntity Data::lookupDxccAD1C(const QString &callsign) { FCT_IDENTIFICATION; static QCache localCache(1000); qCDebug(function_parameters) << callsign; if ( callsign.isEmpty()) return DxccEntity(); DxccEntity dxccRet; DxccEntity *dxccCached = localCache.object(callsign); if ( dxccCached ) { dxccRet = *dxccCached; } else { if ( ! isDXCCQueryValid ) { qWarning() << "Cannot prepare Select statement"; return DxccEntity(); } QString lookupPrefix = callsign; // use the callsign with optional prefix as default to find the dxcc const Callsign parsedCallsign(callsign); // use Callsign to split the callsign into its parts if ( parsedCallsign.isValid() ) { QString suffix = parsedCallsign.getSuffix(); if ( suffix.length() == 1 ) // some countries add single numbers as suffix to designate a call area, e.g. /4 { bool isNumber = false; (void)suffix.toInt(&isNumber); if ( isNumber ) { lookupPrefix = parsedCallsign.getBasePrefix() + suffix; // use the call prefix and the number from the suffix to find the dxcc } } else if ( suffix.length() > 1 && !parsedCallsign.secondarySpecialSuffixes.contains(suffix) ) // if there is more than one character and it is not one of the special suffixes, we definitely have a call prefix as suffix { lookupPrefix = suffix; } } queryDXCC.bindValue(":callsign", lookupPrefix); if ( ! queryDXCC.exec() ) { qWarning() << "Cannot execute Select statement" << queryDXCC.lastError() << queryDXCC.lastQuery(); return DxccEntity(); } if ( queryDXCC.next() ) { dxccRet.dxcc = queryDXCC.value(0).toInt(); dxccRet.country = queryDXCC.value(1).toString(); dxccRet.prefix = queryDXCC.value(2).toString(); dxccRet.cont = queryDXCC.value(3).toString(); dxccRet.cqz = queryDXCC.value(4).toInt(); dxccRet.ituz = queryDXCC.value(5).toInt(); dxccRet.latlon[0] = queryDXCC.value(6).toDouble(); dxccRet.latlon[1] = queryDXCC.value(7).toDouble(); dxccRet.tz = queryDXCC.value(8).toFloat(); bool isExactMatch = queryDXCC.value(9).toBool(); dxccRet.flag = dxccFlag(dxccRet.dxcc); if ( !isExactMatch ) { // find the exceptions to the exceptions if ( dxccRet.prefix == "KG4" && parsedCallsign.getBase().size() != 5 ) { //only KG4AA - KG4ZZ are US Navy in Guantanamo Bay. Other KG4s are USA dxccRet = lookupDxccID(291); // USA //do not overwrite the original prefix dxccRet.prefix = "KG4"; } } dxccCached = new DxccEntity; if ( dxccCached ) { *dxccCached = dxccRet; localCache.insert(callsign, dxccCached); } } else { dxccRet.dxcc = 0; dxccRet.ituz = 0; dxccRet.cqz = 0; dxccRet.tz = 0; } } return dxccRet; } DxccEntity Data::lookupDxccIDAD1C(const int dxccID) { FCT_IDENTIFICATION; qCDebug(function_parameters) << dxccID; if ( !isDXCCIDAD1CQueryValid ) { qWarning() << "Cannot prepare Select statement"; return DxccEntity(); } queryDXCCIDAD1C.bindValue(":dxccid", dxccID); if ( ! queryDXCCIDAD1C.exec() ) { qWarning() << "Cannot execte Select statement" << queryDXCCIDAD1C.lastError(); return DxccEntity(); } DxccEntity dxccRet; if ( queryDXCCIDAD1C.next() ) { dxccRet.dxcc = queryDXCCIDAD1C.value(0).toInt(); dxccRet.country = queryDXCCIDAD1C.value(1).toString(); dxccRet.prefix = queryDXCCIDAD1C.value(2).toString(); dxccRet.cont = queryDXCCIDAD1C.value(3).toString(); dxccRet.cqz = queryDXCCIDAD1C.value(4).toInt(); dxccRet.ituz = queryDXCCIDAD1C.value(5).toInt(); dxccRet.latlon[0] = queryDXCCIDAD1C.value(6).toDouble(); dxccRet.latlon[1] = queryDXCCIDAD1C.value(7).toDouble(); dxccRet.tz = queryDXCCIDAD1C.value(8).toFloat(); dxccRet.flag = dxccFlag(dxccRet.dxcc); } else { dxccRet.dxcc = 0; dxccRet.ituz = 0; dxccRet.cqz = 0; dxccRet.tz = 0; } return dxccRet; } DxccEntity Data::lookupDxccIDClublog(const int dxccID) { FCT_IDENTIFICATION; qCDebug(function_parameters) << dxccID; if ( !isDXCCIDClublogQueryValid ) { qWarning() << "Cannot prepare Select statement"; return DxccEntity(); } queryDXCCIDClublog.bindValue(":dxccid", dxccID); if ( ! queryDXCCIDClublog.exec() ) { qWarning() << "Cannot execte Select statement" << queryDXCCIDClublog.lastError(); return DxccEntity(); } DxccEntity dxccRet; if ( queryDXCCIDClublog.next() ) { dxccRet.dxcc = queryDXCCIDClublog.value(0).toInt(); dxccRet.country = queryDXCCIDClublog.value(1).toString(); dxccRet.prefix = queryDXCCIDClublog.value(2).toString(); dxccRet.cont = queryDXCCIDClublog.value(3).toString(); dxccRet.cqz = queryDXCCIDClublog.value(4).toInt(); dxccRet.ituz = queryDXCCIDClublog.value(5).toInt(); dxccRet.latlon[0] = queryDXCCIDClublog.value(6).toDouble(); dxccRet.latlon[1] = queryDXCCIDClublog.value(7).toDouble(); dxccRet.tz = queryDXCCIDClublog.value(8).toFloat(); dxccRet.flag = dxccFlag(dxccRet.dxcc); } else { dxccRet.dxcc = 0; dxccRet.ituz = 0; dxccRet.cqz = 0; dxccRet.tz = 0; } return dxccRet; } DxccEntity Data::lookupDxccID(const int dxccID) { FCT_IDENTIFICATION; // LF: The AD1C method seems more accurate for current data regarding ITU and CQZ. // The Clublog method is good for determining historical data and CQZ, but unfortunately, // the ITU zone is not reliable in this method because it’s not listed and has to be // calculated. Therefore, I decided to use the AD1C method as the general approach and only // use Clublog in exceptional cases. Once Clublog starts distributing ITUZ, we should // completely switch to the Clublog method. // I also believes that the AD1C method is more accurate in determining special exceptions // for American callsigns. return lookupDxccIDAD1C(dxccID); } DxccEntity Data::lookupDxccClublog(const QString &callsign, const QDateTime &date) { FCT_IDENTIFICATION; qCDebug(function_parameters) << callsign; if ( callsign.isEmpty()) return DxccEntity(); if ( ! isDXCCClublogQueryValid ) { qWarning() << "Cannot prepare Select statement"; return DxccEntity(); } QString lookupPrefix = callsign; // use the callsign with optional prefix as default to find the dxcc const Callsign parsedCallsign(callsign); // use Callsign to split the callsign into its parts if ( parsedCallsign.isValid() ) { QString suffix = parsedCallsign.getSuffix(); if ( suffix.length() == 1 ) // some countries add single numbers as suffix to designate a call area, e.g. /4 { bool isNumber = false; (void)suffix.toInt(&isNumber); if ( isNumber ) { lookupPrefix = parsedCallsign.getBasePrefix() + suffix; // use the call prefix and the number from the suffix to find the dxcc } } else if ( suffix.length() > 1 && !parsedCallsign.secondarySpecialSuffixes.contains(suffix) ) // if there is more than one character and it is not one of the special suffixes, we definitely have a call prefix as suffix { lookupPrefix = suffix; } } queryDXCCClublog.bindValue(":modifiedcall", lookupPrefix); queryDXCCClublog.bindValue(":exactcall", callsign); queryDXCCClublog.bindValue(":dxccdate", date); if ( ! queryDXCCClublog.exec() ) { qWarning() << "Cannot execute Select statement" << queryDXCCClublog.lastError() << queryDXCCClublog.lastQuery(); return DxccEntity(); } DxccEntity dxccRet; const DxccEntity &ad1cDXCCData = lookupDxccAD1C(callsign); if ( queryDXCCClublog.first() ) { dxccRet.dxcc = queryDXCCClublog.value(0).toInt(); dxccRet.country = queryDXCCClublog.value(1).toString(); dxccRet.prefix = queryDXCCClublog.value(2).toString(); dxccRet.cont = queryDXCCClublog.value(3).toString(); dxccRet.cqz = queryDXCCClublog.value(4).toInt(); dxccRet.ituz = queryDXCCClublog.value(5).toInt(); dxccRet.latlon[0] = queryDXCCClublog.value(6).toDouble(); dxccRet.latlon[1] = queryDXCCClublog.value(7).toDouble(); dxccRet.tz = queryDXCCClublog.value(8).toFloat(); bool isExactMatch = queryDXCCClublog.value(9).toBool(); dxccRet.flag = dxccFlag(dxccRet.dxcc); if ( !isExactMatch ) { // find the exceptions to the exceptions if ( dxccRet.prefix == "KG4" && parsedCallsign.getBase().size() != 5 ) { //only KG4AA - KG4ZZ are US Navy in Guantanamo Bay. Other KG4s are USA dxccRet = lookupDxccID(291); // USA //do not overwrite the original prefix dxccRet.prefix = "KG4"; } } } else { qCDebug(runtime) << "DXCC not found for " << lookupPrefix << "; Using AD1C Info"; dxccRet = ad1cDXCCData; } if ( dxccRet.ituz == 0 ) { qCDebug(runtime) << "Zone exception, using AD1C Info Info to find ITUZ for " << callsign; if ( ad1cDXCCData.dxcc == dxccRet.dxcc && ad1cDXCCData.cqz == dxccRet.cqz ) { qCDebug(runtime) << "Fixing ITUZ for call" << callsign; dxccRet.ituz = ad1cDXCCData.ituz; } else { qCDebug(runtime) << "not match" << callsign << ad1cDXCCData.dxcc << dxccRet.dxcc << ad1cDXCCData.cqz << dxccRet.cqz; } } if ( ad1cDXCCData.dxcc == dxccRet.dxcc && ad1cDXCCData.cqz == dxccRet.cqz ) dxccRet.ituz = ad1cDXCCData.ituz; return dxccRet; } SOTAEntity Data::lookupSOTA(const QString &SOTACode) { FCT_IDENTIFICATION; if ( ! isSOTAQueryValid ) { qWarning() << "Cannot prepare Select statement"; return SOTAEntity(); } querySOTA.bindValue(":code", SOTACode.toUpper()); if ( ! querySOTA.exec() ) { qWarning() << "Cannot execte Select statement" << querySOTA.lastError(); return SOTAEntity(); } SOTAEntity SOTARet; if (querySOTA.next()) { SOTARet.summitCode = querySOTA.value(0).toString(); SOTARet.associationName = querySOTA.value(1).toString(); SOTARet.regionName = querySOTA.value(2).toString(); SOTARet.summitName = querySOTA.value(3).toString(); SOTARet.altm = querySOTA.value(4).toInt(); SOTARet.altft = querySOTA.value(5).toInt(); SOTARet.gridref1 = querySOTA.value(6).toDouble(); SOTARet.gridref2 = querySOTA.value(7).toDouble(); SOTARet.longitude = querySOTA.value(8).toDouble(); SOTARet.latitude = querySOTA.value(9).toDouble(); SOTARet.points = querySOTA.value(10).toInt(); SOTARet.bonusPoints = querySOTA.value(11).toInt(); SOTARet.validFrom = querySOTA.value(12).toDate(); SOTARet.validTo = querySOTA.value(13).toDate(); } else { SOTARet.altft = 0; SOTARet.altm = 0; SOTARet.gridref1 = 0.0; SOTARet.gridref2 = 0.0; SOTARet.longitude = 0.0; SOTARet.latitude = 0.0; SOTARet.points = 0; SOTARet.bonusPoints = 0; } return SOTARet; } POTAEntity Data::lookupPOTA(const QString &POTACode) { FCT_IDENTIFICATION; if ( ! isPOTAQueryValid ) { qWarning() << "Cannot prepare Select statement"; return POTAEntity(); } queryPOTA.bindValue(":code", POTACode.toUpper()); if ( ! queryPOTA.exec() ) { qWarning() << "Cannot execte Select statement" << queryPOTA.lastError(); return POTAEntity(); } POTAEntity POTARet; if (queryPOTA.next()) { POTARet.reference = queryPOTA.value(0).toString(); POTARet.name = queryPOTA.value(1).toString(); POTARet.active = queryPOTA.value(2).toBool(); POTARet.entityID = queryPOTA.value(3).toInt(); POTARet.locationDesc = queryPOTA.value(4).toString(); POTARet.longitude = queryPOTA.value(5).toDouble(); POTARet.latitude = queryPOTA.value(6).toDouble(); POTARet.grid = queryPOTA.value(7).toString(); } else { POTARet.active = false; POTARet.entityID = 0; POTARet.longitude = 0.0; POTARet.latitude = 0.0; } return POTARet; } WWFFEntity Data::lookupWWFF(const QString &reference) { FCT_IDENTIFICATION; if ( ! isWWFFQueryValid ) { qWarning() << "Cannot prepare Select statement"; return WWFFEntity(); } queryWWFF.bindValue(":reference", reference.toUpper()); if ( ! queryWWFF.exec() ) { qWarning() << "Cannot execte Select statement" << queryWWFF.lastError(); return WWFFEntity(); } WWFFEntity WWFFRet; if (queryWWFF.next()) { WWFFRet.reference = queryWWFF.value(0).toString(); WWFFRet.status = queryWWFF.value(1).toString(); WWFFRet.name = queryWWFF.value(2).toString(); WWFFRet.program = queryWWFF.value(3).toString(); WWFFRet.dxcc = queryWWFF.value(4).toString(); WWFFRet.state = queryWWFF.value(5).toString(); WWFFRet.county = queryWWFF.value(6).toString(); WWFFRet.continent = queryWWFF.value(7).toString(); WWFFRet.iota = queryWWFF.value(8).toString(); WWFFRet.iaruLocator = queryWWFF.value(9).toString(); WWFFRet.latitude = queryWWFF.value(10).toDouble(); WWFFRet.longitude = queryWWFF.value(11).toDouble(); WWFFRet.iucncat = queryWWFF.value(12).toString(); WWFFRet.validFrom = queryWWFF.value(13).toDate(); WWFFRet.validTo = queryWWFF.value(14).toDate(); } else { WWFFRet.longitude = 0.0; WWFFRet.latitude = 0.0; } return WWFFRet; } ================================================ FILE: data/Data.h ================================================ #ifndef QLOG_DATA_DATA_H #define QLOG_DATA_DATA_H #include #include #include "Dxcc.h" #include "SOTAEntity.h" #include "WWFFEntity.h" #include "POTAEntity.h" #include "core/zonedetect.h" #include "core/QuadKeyCache.h" class QCompleter; class Data : public QObject { Q_OBJECT public: enum DupeType { ALL_BANDS = 1, EACH_BAND = 2, EACH_BAND_MODE = 3, NO_CHECK = 4 }; enum SeqType { SINGLE = 1, PER_BAND = 2 }; const QMap qslSentEnum = { {"Y", tr("Yes")}, {"N", tr("No")}, {"R", tr("Requested")}, {"Q", tr("Queued")}, {"I", tr("Invalid")} }; const QMap qslSentViaEnum = { {"B", tr("Bureau")}, {"D", tr("Direct")}, {"E", tr("Electronic")}, {" ", tr("Blank")} }; const QMap qslRcvdEnum = { {"Y", tr("Yes")}, {"N", tr("No")}, {"R", tr("Requested")}, {"I", tr("Invalid")} }; const QMap uploadStatusEnum = { {"Y", tr("Yes")}, {"N", tr("No")}, {"M", tr("Modified")}, {" ", tr("Blank")} }; const QMap antPathEnum = { {"G", tr("Grayline")}, {"O", tr("Other")}, {"S", tr("Short Path")}, {"L", tr("Long Path")}, {" ", tr("Blank")} }; const QMap boolEnum = { {"Y", tr("Yes")}, {"N", tr("No")}, {" ", tr("Blank")} }; const QMap qsoCompleteEnum = { {"Y", tr("Yes")}, {"N", tr("No")}, {"Nil", tr("Not Heard")}, {"?", tr("Uncertain")}, {" ", tr("Blank")} }; const QMap morseKeyTypeEnum = { {"SK", tr("Straight Key")}, {"SS", tr("Sideswiper")}, {"BUG", tr("Mechanical semi-automatic keyer or Bug")}, {"FAB", tr("Mechanical fully-automatic keyer or Bug")}, {"SP", tr("Single Paddle")}, {"DP", tr("Dual Paddle")}, {"CPU", tr("Computer Driven")}, {" ", tr("Blank")} }; const QMap downloadStatusEnum = { {"Y", tr("Yes")}, {"N", tr("No")}, {"I", tr("Invalid")}, {" ", tr("Blank")} }; const QMap eqslAgEnum = { {"Y", tr("Confirmed (AG)")}, {"N", tr("Confirmed (no AG)")}, {"U", tr("Unknown")}, {" ", tr("Blank")} }; explicit Data(QObject *parent = nullptr); ~Data(); static Data* instance() { static Data instance; return &instance; }; static DxccStatus dxccNewStatusWhenQSOAdded(const DxccStatus &oldStatus, const qint32 oldDxcc, const QString &oldBand, const QString &oldMode, const qint32 newDxcc, const QString &newBand, const QString &newMode); static qulonglong dupeNewCountWhenQSOAdded(qulonglong oldCounter, const QString &oldBand, const QString &oldMode, const QString &addedBand, const QString &addedMode); static qulonglong dupeNewCountWhenQSODelected(qulonglong oldCounter, const QString &oldBand, const QString &oldMode, const QString &deletedBand, const QString &deletedMode); static QColor statusToColor(const DxccStatus &status, bool isDupe, const QColor &defaultColor); static QString colorToHTMLColor(const QColor&); static QString statusToText(const DxccStatus &status); static QString removeAccents(const QString &input); static int getITUZMin(); static int getITUZMax(); static int getCQZMin(); static int getCQZMax(); static QString debugFilename(); static double MHz2UserFriendlyFreq(double, QString &unit, unsigned char &efectiveDecP); static const QStringList& getContinentList(); static qulonglong countDupe(const QString& callsign, const QString &band, const QString &mode); static QString safeQueryString(const QUrlQuery &query); DxccStatus dxccStatus(int dxcc, const QString &band, const QString &mode); QStringList contestList(); QStringList propagationModesList() const { return QStringList{""} + propagationModes.values(); } QStringList propagationModesIDList() const { return QStringList{""} + propagationModes.keys(); } QString propagationModeTextToID(const QString &propagationText) const { return propagationModes.key(propagationText);} QString propagationModeIDToText(const QString &propagationID) const { return propagationModes.value(propagationID);} DxccEntity lookupDxcc(const QString &callsign); DxccEntity lookupDxccID(const int dxccID); DxccEntity lookupDxccAD1C(const QString &callsign); DxccEntity lookupDxccIDAD1C(const int dxccID); DxccEntity lookupDxccClublog(const QString &callsign, const QDateTime &date = QDateTime::currentDateTimeUtc()); DxccEntity lookupDxccIDClublog(const int dxccID); SOTAEntity lookupSOTA(const QString &SOTACode); POTAEntity lookupPOTA(const QString &POTACode); WWFFEntity lookupWWFF(const QString &reference); const QString dxccFlag(int dxcc) const {return dxccEntityStaticInfo.value(dxcc).value("flag").toString();}; const QString dxccName(int dxcc) const {return dxccEntityStaticInfo.value(dxcc).value("name").toString();}; int dxccITUZ(int dxcc) const {return dxccEntityStaticInfo.value(dxcc).value("ituz").toInt();}; QPair legacyMode(const QString &mode); QStringList satModeList() { return satModes.values();} QStringList satModesIDList() { return satModes.keys(); } QString satModeTextToID(const QString &satModeText) { return satModes.key(satModeText);} QString satModeIDToText(const QString &satModeID) { return satModes.value(satModeID);} QStringList iotaList() { return iotaRef.values();} QStringList iotaIDList() { return iotaRef.keys();} QString iotaTextToID(const QString &iotaText) { return iotaRef.key(iotaText);} QStringList sotaIDList() { return sotaRefID.keys();} QStringList wwffIDList() { return wwffRefID.keys();} QStringList potaIDList() { return potaRefID.keys();} QString getIANATimeZone(double, double); QStringList sigIDList(); static QCompleter* createCountyCompleter(int dxcc, QObject *parent = nullptr); signals: public slots: void invalidateDXCCStatusCache(const QSqlRecord &record); void invalidateSetOfDXCCStatusCache(const QSet &entities); void clearDXCCStatusCache(); private: void loadContests(); void loadPropagationModes(); void loadLegacyModes(); void loadDxccFlags(); void loadSatModes(); void loadIOTA(); void loadSOTA(); void loadWWFF(); void loadPOTA(); void loadTZ(); QHash dxccEntityStaticInfo; QMap contests; QMap propagationModes; QMap> legacyModes; QMap satModes; QMap iotaRef; QMap sotaRefID; QMap wwffRefID; QMap potaRefID; ZoneDetect * zd; QSqlQuery queryDXCC; QSqlQuery queryDXCCIDAD1C; QSqlQuery queryDXCCIDClublog; QSqlQuery queryDXCCClublog; QSqlQuery querySOTA; QSqlQuery queryWWFF; QSqlQuery queryPOTA; bool isDXCCQueryValid; bool isDXCCClublogQueryValid; bool isSOTAQueryValid; bool isWWFFQueryValid; bool isPOTAQueryValid; bool isDXCCIDAD1CQueryValid; bool isDXCCIDClublogQueryValid; QuadKeyCache dxccStatusCache; static const char translitTab[]; static const int tranlitIndexMap[]; }; #endif // QLOG_DATA_DATA_H ================================================ FILE: data/DxServerString.cpp ================================================ #include #include "DxServerString.h" #include "core/debug.h" MODULE_IDENTIFICATION("qlog.core.dxserverstring"); DxServerString::DxServerString(const QString &connectString, const QString &defaultUsername) : port(7300), valid(false) { FCT_IDENTIFICATION; if ( !isValidServerString(connectString) ) return; // serverSelect format is: // [username@]hostname:port // username is not mandatory QStringList serverElements = connectString.split(":"); username = defaultUsername; if ( serverElements[0].contains(QStringLiteral("@")) ) { QStringList hostNameElements = serverElements[0].split(QStringLiteral("@")); username = hostNameElements[0]; hostname = hostNameElements[1]; } else { hostname = serverElements[0]; } port = serverElements[1].toInt(); // servername is verified, therefore it is not needed to check // whether the variable "server" contains hostname and port valid = true; } const QRegularExpression DxServerString::serverStringRegEx() { FCT_IDENTIFICATION; return QRegularExpression(QStringLiteral("^([a-z0-9\\-._~%!$&'()*+,;=]+@)?(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?):([0-9]{1,5})$|^([a-z0-9\\-._~%!$&'()*+,;=]+@)?(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\\-]*[A-Za-z0-9]):([0-9]{1,5})$"), QRegularExpression::CaseInsensitiveOption); } bool DxServerString::isValidServerString(const QString &connectString) { FCT_IDENTIFICATION; QRegularExpressionMatch stringMatch = serverStringRegEx().match(connectString); bool ret = stringMatch.hasMatch(); qCDebug(runtime) << ret; return ret; } const QString DxServerString::getPasswordStorageKey() const { FCT_IDENTIFICATION; if ( !isValid() ) return QString(); return getHostname() + ":" + QString::number(getPort()); } ================================================ FILE: data/DxServerString.h ================================================ #ifndef QLOG_DATA_DXSERVERSTRING_H #define QLOG_DATA_DXSERVERSTRING_H #include class DxServerString { public: explicit DxServerString(const QString &connectString, const QString &defaultUsername = QString()); static bool isValidServerString(const QString &); bool isValid() const {return valid;}; QString getUsername() const {return username;}; QString getHostname() const {return hostname;}; int getPort() const {return port;}; const QString getPasswordStorageKey() const; private: static const QRegularExpression serverStringRegEx(); QString username, hostname; int port; bool valid; }; #endif // QLOG_DATA_DXSERVERSTRING_H ================================================ FILE: data/DxSpot.h ================================================ #ifndef QLOG_DATA_DXSPOT_H #define QLOG_DATA_DXSPOT_H #include #include "Dxcc.h" #include "core/MembershipQE.h" #include "data/BandPlan.h" class DxSpot { public: QDateTime dateTime; QString callsign; QList callsign_member; double freq; double freqTX; QString band; QString modeGroupString; BandPlan::BandPlanMode bandPlanMode; QString spotter; QString comment; qulonglong dupeCount = 0; QString wwffRef; QString potaRef; QString sotaRef; QString iotaRef; DxccEntity dxcc; DxccEntity dxcc_spotter; DxccStatus status; bool containsWWFF; bool containsPOTA; bool containsSOTA; bool containsIOTA; DxSpot() : freq(0.0), freqTX(0.0), bandPlanMode(BandPlan::BAND_MODE_UNKNOWN), status(DxccStatus::UnknownStatus), containsWWFF(false), containsPOTA(false), containsSOTA(false), containsIOTA(false) {}; QStringList memberList2StringList() const { QStringList ret; for ( const ClubInfo &member : static_cast&>(callsign_member) ) ret << member.getClubInfo(); return ret; }; QSet memberList2Set() const { QSet ret; for ( const ClubInfo &member : static_cast&>(callsign_member) ) ret << member.getClubInfo(); return ret; } operator QString() const { return QString("DxSpot ") + "Country: " + QString::number(dxcc.dxcc) + " " + "CQZ: " + QString::number(dxcc.cqz) + " " + "ITUZ: " + QString::number(dxcc.ituz) + " " + "Status: " + QString::number(status) + " " + "ModeGroup: " + modeGroupString + " " + "Band: " + band + " " + "spotter Country: " + QString::number(dxcc_spotter.dxcc) + " " + "Continent: " + dxcc.cont + " " + "Spotter Continent: " + dxcc_spotter.cont + " " + "Callsign: " + callsign + " " + "FreqTX: " + QString::number(freqTX) + " " + "Message: " + comment + " " + "DX Member: " + memberList2StringList().join(", ") + " " + "POTA: " + potaRef + " " + "POTA present: " + (containsPOTA ? "true" : "false") + " " + "SOTA: " + sotaRef + " " + "SOTA present: " + (containsSOTA ? "true" : "false") + " " + "WWFF: " + wwffRef + " " + "WWFF present: " + (containsWWFF ? "true" : "false") + " " + "IOTA: " + iotaRef + " " + "IOTA present: " + (containsIOTA ? "true" : "false") + " "; } }; Q_DECLARE_METATYPE(DxSpot); #endif // QLOG_DATA_DXSPOT_H ================================================ FILE: data/Dxcc.h ================================================ #ifndef QLOG_DATA_DXCC_H #define QLOG_DATA_DXCC_H #include enum DxccStatus { NewEntity = 0b1, NewBand = 0b10, NewMode = 0b100, NewBandMode = 0b110, NewSlot = 0b1000, Worked = 0b10000, Confirmed = 0b100000, UnknownStatus = 0b1000000, All = 0b1111111 }; class DxccEntity { public: QString country; QString prefix; qint32 dxcc; QString cont; qint32 cqz; qint32 ituz; double latlon[2]; float tz; QString flag; }; struct DxccPrefix { public: QString prefix; bool exact; qint32 dxcc; qint32 cqz; qint32 ituz; QString cont; double latlon[2]; }; #endif // QLOG_DATA_DXCC_H ================================================ FILE: data/Gridsquare.cpp ================================================ #include "Gridsquare.h" #include #include #include #include #include MODULE_IDENTIFICATION("qlog.core.gridsquare"); Gridsquare::Gridsquare(const QString &in_grid) : validGrid(false), lat(qQNaN()), lon(qQNaN()) { FCT_IDENTIFICATION; if ( !in_grid.isEmpty() ) { grid = in_grid.toUpper(); if ( gridRegEx().match(grid).hasMatch() ) { lon = (grid.at(0).toLatin1() - 'A') * 20 - 180; lat = (grid.at(1).toLatin1() - 'A') * 10 - 90; if ( grid.size() >= 4 ) { lon += (grid.at(2).toLatin1() - '0') * 2; lat += (grid.at(3).toLatin1() - '0') * 1; if ( grid.size() >= 6 ) { lon += (grid.at(4).toLatin1() - 'A') * (5.0/60.0); lat += (grid.at(5).toLatin1() - 'A') * (2.5/60.0); if ( grid.size() >= 8 ) { lon += (grid.at(6).toLatin1() - '0') * (30.0/3600.0); lat += (grid.at(7).toLatin1() - '0') * (15.0/3600.0); // move to the center lon += 15.0/3600.0; lat += 7.5/3600.0; } else { // move to the center lon += 2.5/60.0; lat += 1.25/60.0; } } else { // move to the center lon += 1; lat += 0.5; } } else { // move 0to the center lon += 10; lat += 5; } validGrid = true; } else { /* not valid grid */ grid = QString(); } } } Gridsquare::Gridsquare(const double inlat, const double inlon) : validGrid(false), lat(inlat), lon(inlon) { FCT_IDENTIFICATION; QString U = "ABCDEFGHIJKLMNOPQRSTUVWX"; if ( qIsNaN(inlat) || qIsNaN(inlon) || qAbs(inlat) >= 90.0 || qAbs(inlon) >= 180.0 ) { qCDebug(runtime) << "Invalid Grid lat/lon" << inlat << inlon; this->lat = this->lon = qQNaN(); } else { // currently user only for SOTA where only 6 chars are enough double modifiedLat = lat + 90.0; double modifiedLon = lon + 180.0; QString grid1 = U.at(static_cast(modifiedLon/20)); QString grid2 = U.at(static_cast(modifiedLat/10)); QString grid3 = QString::number(static_cast(fmod((modifiedLon/2), 10.0))); QString grid4 = QString::number(static_cast(fmod(modifiedLat,10.0))); double rLat = (modifiedLat - static_cast(modifiedLat)) * 60; double rLon = (modifiedLon - 2*static_cast(modifiedLon/2)) *60; QString grid5 = U.at((int)(rLon/5)); QString grid6 = U.at((int)(rLat/2.5)); grid = grid1 + grid2 + grid3 + grid4 + grid5 + grid6; qCDebug(runtime) << grid; validGrid = true; } } const QRegularExpression Gridsquare::gridRegEx() { FCT_IDENTIFICATION; return QRegularExpression("^[A-Ra-r]{2}(?:[0-9]{2}|[0-9]{2}[A-Xa-x]{2}|[0-9]{2}[A-Xa-x]{2}[0-9]{2})?$"); //return QRegularExpression("^[A-Ra-r]{2}[0-9]{2}([A-Xa-x]{2})?([0-9]{2})?$"); } const QRegularExpression Gridsquare::gridVUCCRegEx() { FCT_IDENTIFICATION; return QRegularExpression("^[A-Ra-r]{2}(?:[0-9]{2}|[0-9]{2}[A-Xa-x]{2}),[ ]*[A-Ra-r]{2}(?:[0-9]{2}|[0-9]{2}[A-Xa-x]{2})$|" "^[A-Ra-r]{2}(?:[0-9]{2}|[0-9]{2}[A-Xa-x]{2}),[ ]*[A-Ra-r]{2}(?:[0-9]{2}|[0-9]{2}[A-Xa-x]{2}),[ ]*[A-Ra-r]{2}(?:[0-9]{2}|[0-9]{2}[A-Xa-x]{2}),[ ]*[A-Ra-r]{2}(?:[0-9]{2}|[0-9]{2}[A-Xa-x]{2})$"); } const QRegularExpression Gridsquare::gridExtRegEx() { FCT_IDENTIFICATION; return QRegularExpression("^[A-Xa-x]{2}?([0-9]{2})?$"); } double Gridsquare::distance2localeUnitDistance(double km, QString &unit, const LogLocale &locale) { FCT_IDENTIFICATION; unit = QObject::tr("km"); double ret = km; // All imperial systems if ( ! locale.getSettingUseMetric() ) { unit = QObject::tr("miles"); ret = km * localeDistanceCoef(locale); } return ret; } double Gridsquare::localeDistanceCoef(const LogLocale &locale) { FCT_IDENTIFICATION; return ( ! locale.getSettingUseMetric() ) ? 0.6213711922 : 1.0; } bool Gridsquare::isValid() const { FCT_IDENTIFICATION; return validGrid; } bool Gridsquare::distanceTo(double lat, double lon, double &distance) const { FCT_IDENTIFICATION; if ( !isValid() ) { distance = 0.0; return false; } /* https://www.movable-type.co.uk/scripts/latlong.html */ double dLat = (lat - this->getLatitude()) * M_PI / 180; double dLon = (lon - this->getLongitude()) * M_PI / 180; double lat1 = this->getLatitude() * M_PI / 180; double lat2 = lat * M_PI / 180; double a = sin(dLat / 2) * sin(dLat / 2) + sin(dLon / 2) * sin(dLon / 2) * cos(lat1) * cos(lat2); double c = 2 * atan2(sqrt(a), sqrt(1-a)); // Based on IARU Rules // The centre of the Large Locator Square (e.g. IO84MM to IO91MM) is used for distance calculations. // In order to make contest scores comparable, for the conversion from degrees to kilometres a factor // of 111.2 should be used when calculating distances with the aid of the spherical geometry equation. // It means that 111.2km/° * 360 =40032km // It means that R = 40032 / (2*PI) distance = (40032.0 / (2 * M_PI)) * c; return true; } bool Gridsquare::distanceTo(const Gridsquare &in_grid, double &distance) const { FCT_IDENTIFICATION; if ( !in_grid.isValid() ) { distance = 0.0; return false; } return distanceTo(in_grid.getLatitude(), in_grid.getLongitude(), distance); } bool Gridsquare::bearingTo(double lat, double lon, double &bearing) const { FCT_IDENTIFICATION; if ( !isValid() ) { bearing = 0.0; return false; } double dLon = (lon - this->getLongitude()) * M_PI / 180; double lat1 = this->getLatitude() * M_PI / 180; double lat2 = lat * M_PI / 180; double y = sin(dLon) * cos(lat2); double x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dLon); bearing = fmod((180.0 * atan2(y, x) / M_PI + 360.0), 360.0); return true; } bool Gridsquare::bearingTo(const Gridsquare &in_grid, double &bearing) const { FCT_IDENTIFICATION; if ( ! in_grid.isValid() ) { bearing = 0.0; return false; } return bearingTo(in_grid.getLatitude(), in_grid.getLongitude(), bearing); } ================================================ FILE: data/Gridsquare.h ================================================ #ifndef QLOG_CORE_GRIDSQUARE_H #define QLOG_CORE_GRIDSQUARE_H #include #include #include #include "core/LogLocale.h" class Gridsquare { public: explicit Gridsquare(const QString &in_grid = QString()); explicit Gridsquare(const double inlat,const double inlon); ~Gridsquare() {}; static const QRegularExpression gridRegEx(); static const QRegularExpression gridVUCCRegEx(); static const QRegularExpression gridExtRegEx(); static double distance2localeUnitDistance(double km, QString &unit, const LogLocale &locale); static double localeDistanceCoef(const LogLocale &locale); bool isValid() const; double getLongitude() const {return lon;}; double getLatitude() const {return lat;}; const QString getGrid() const { return grid;}; bool distanceTo(const Gridsquare &in_grid, double &distance) const; bool distanceTo(double lat, double lon, double &distance) const; bool bearingTo(const Gridsquare &in_grid, double &bearing) const; bool bearingTo(double lat, double lon, double &bearing) const ; operator QString() const { return QString("Gridsquare: grid[%1]; valid[%2]; lat[%3]; lon[%4]") .arg(grid).arg(validGrid).arg(lat).arg(lon);}; private: QString grid; bool validGrid; double lat, lon; }; #endif // QLOG_CORE_GRIDSQUARE_H ================================================ FILE: data/HostsPortString.cpp ================================================ #include #include #include "HostsPortString.h" #include "core/debug.h" MODULE_IDENTIFICATION("qlog.core.hostsportstring"); const QRegularExpression HostsPortString::hostsPortRegEx() { return QRegularExpression("^((((25[0-5]|(2[0-4]|1[0-9]|[1-9]|)[0-9])\\.?\\b){4}):[0-9]{1,5}\\s*)+$"); } HostsPortString::HostsPortString(const QString &addressesString, QObject *parent) : QObject(parent) { FCT_IDENTIFICATION; qDebug(function_parameters) << addressesString; if ( addressesString.isEmpty() ) { return; } const QStringList &addressTokens = addressesString.split(" "); for ( const QString &addrToken : addressTokens ) { qCDebug(runtime) << "Processing address " << addrToken; QStringList addressPair = addrToken.split(":"); if ( addressPair.size() == 2 ) { bool isPortOK = false; uint port = addressPair.at(1).toUInt(&isPortOK); if ( isPortOK && port < 65536 ) { qCDebug(runtime) << "Adding address" << addrToken; addressList << HostPortAddress(addressPair.at(0), port); } else { qCInfo(runtime) << "Malformed port "<< addressPair.at(1) << "for address " << addrToken << " - skipping"; } } else { qCInfo(runtime) << "Malformed address " << addrToken << " - skipping"; } } } bool HostsPortString::hasLocalIPWithPort(int port) const { FCT_IDENTIFICATION; qDebug(function_parameters) << port; QList localAddresses = QNetworkInterface::allAddresses(); localAddresses.append(QHostAddress("0.0.0.0")); qCDebug(runtime) << "My IPs:" << localAddresses; for ( const QHostAddress& addr : static_cast&>(localAddresses) ) if ( addressList.contains(HostPortAddress(addr, port)) ) return true; return false; } QList HostsPortString::getAddrList() const { FCT_IDENTIFICATION; return addressList; } HostPortAddress::HostPortAddress(const QString &hostAddress, quint16 hostPort) : QHostAddress(hostAddress), port(hostPort) { FCT_IDENTIFICATION; } HostPortAddress::HostPortAddress(const QHostAddress &host, quint16 port) : QHostAddress(host), port(port) { FCT_IDENTIFICATION; } void HostPortAddress::setPort(quint16 hostPort) { FCT_IDENTIFICATION; qCDebug(function_parameters) << hostPort; port = hostPort; } quint16 HostPortAddress::getPort() const { FCT_IDENTIFICATION; return port; } bool HostPortAddress::operator==(const HostPortAddress &other) const { return QHostAddress::operator==(other) && port == other.port; } ================================================ FILE: data/HostsPortString.h ================================================ #ifndef QLOG_DATA_HOSTSPORTSTRING_H #define QLOG_DATA_HOSTSPORTSTRING_H #include #include class HostPortAddress : public QHostAddress { public: explicit HostPortAddress(const QString &, quint16); HostPortAddress(const QHostAddress &host, quint16 port); void setPort(quint16); quint16 getPort() const; bool operator==(const HostPortAddress &other) const; private: quint16 port; }; class HostsPortString : public QObject { Q_OBJECT public: static const QRegularExpression hostsPortRegEx(); explicit HostsPortString(const QString &, QObject *parent=nullptr); bool hasLocalIPWithPort(int port) const; QList getAddrList() const; private: QList addressList; }; #endif // QLOG_DATA_HOSTSPORTSTRING_H ================================================ FILE: data/MainLayoutProfile.cpp ================================================ #include #include #include #include "MainLayoutProfile.h" #include "core/debug.h" #include "models/LogbookModel.h" MODULE_IDENTIFICATION("qlog.data.mainlayoutprofile"); QDataStream& operator<<(QDataStream& out, const MainLayoutProfile& v) { out << v.profileName << v.rowA << v.rowB << v.detailColA << v.detailColB << v.detailColC << v.mainGeometry << v.mainState << v.darkMode << v.tabsexpanded << v.addlBandmaps; return out; } QDataStream& operator>>(QDataStream& in, MainLayoutProfile& v) { in >> v.profileName; in >> v.rowA; in >> v.rowB; in >> v.detailColA; in >> v.detailColB; in >> v.detailColC; in >> v.mainGeometry; in >> v.mainState; in >> v.darkMode; in >> v.tabsexpanded; in >> v.addlBandmaps; return in; } MainLayoutProfilesManager::MainLayoutProfilesManager() : ProfileManagerSQL("main_layout_profiles") { FCT_IDENTIFICATION; QSqlQuery profileQuery; if ( ! profileQuery.prepare("SELECT profile_name, row_A, row_B, detail_col_A, " "detail_col_B, detail_col_C, main_geometry, main_state, " "dark_mode, tabsexpanded, addlbandmaps " "FROM main_layout_profiles") ) { qWarning()<< "Cannot prepare select"; } if ( profileQuery.exec() ) { while (profileQuery.next()) { MainLayoutProfile profileDB; profileDB.profileName = profileQuery.value(0).toString(); profileDB.rowA = toIntList(profileQuery.value(1).toString()); profileDB.rowB = toIntList(profileQuery.value(2).toString()); profileDB.detailColA = toIntList(profileQuery.value(3).toString()); profileDB.detailColB = toIntList(profileQuery.value(4).toString()); profileDB.detailColC = toIntList(profileQuery.value(5).toString()); profileDB.mainGeometry = QByteArray::fromBase64(profileQuery.value(6).toString().toUtf8()); profileDB.mainState = QByteArray::fromBase64(profileQuery.value(7).toString().toUtf8()); profileDB.darkMode = profileQuery.value(8).toInt(); profileDB.tabsexpanded = profileQuery.value(9).toBool(); profileDB.addlBandmaps = toPairStringList(profileQuery.value(10).toString()); addProfile(profileDB.profileName, profileDB); } } else { qInfo() << "MainLayout Profile DB select error " << profileQuery.lastError().text(); } } void MainLayoutProfilesManager::save() { FCT_IDENTIFICATION; QSqlQuery deleteQuery; QSqlQuery insertQuery; if ( ! deleteQuery.prepare("DELETE FROM main_layout_profiles") ) { qWarning() << "Cannot prepare Delete statement"; return; } if ( ! insertQuery.prepare("INSERT INTO main_layout_profiles(profile_name, row_A, row_B, detail_col_A, detail_col_B, detail_col_C, main_geometry, main_state, dark_mode, tabsexpanded, addlbandmaps) " "VALUES (:profile_name, :row_A, :row_B, :detail_col_A, :detail_col_B, :detail_col_C, :main_geometry, :main_state, :dark_mode, :tabsexpanded, :addlbandmaps)") ) { qWarning() << "Cannot prepare Insert statement"; return; } if ( deleteQuery.exec() ) { const QStringList &keys = profileNameList(); for ( const QString &key: keys ) { const MainLayoutProfile &layoutProfile = getProfile(key); insertQuery.bindValue(":profile_name", key); insertQuery.bindValue(":row_A", toDBStringList(layoutProfile.rowA)); insertQuery.bindValue(":row_B", toDBStringList(layoutProfile.rowB)); insertQuery.bindValue(":detail_col_A", toDBStringList(layoutProfile.detailColA)); insertQuery.bindValue(":detail_col_B", toDBStringList(layoutProfile.detailColB)); insertQuery.bindValue(":detail_col_C", toDBStringList(layoutProfile.detailColC)); insertQuery.bindValue(":main_geometry", layoutProfile.mainGeometry.toBase64()); insertQuery.bindValue(":main_state", layoutProfile.mainState.toBase64()); insertQuery.bindValue(":dark_mode", layoutProfile.darkMode); insertQuery.bindValue(":tabsexpanded", layoutProfile.tabsexpanded); insertQuery.bindValue(":addlbandmaps", toDBStringList(layoutProfile.addlBandmaps)); if ( ! insertQuery.exec() ) { qInfo() << "MainLayoutProfile DB insert error " << insertQuery.lastError().text() << insertQuery.lastQuery(); } } } else { qInfo() << "MainLayoutProfile Profile DB delete error " << deleteQuery.lastError().text(); } saveCurProfile1(); } QString MainLayoutProfilesManager::toDBStringList(const QList &list) { FCT_IDENTIFICATION; qCDebug(function_parameters) << list; QStringList stringsList; for ( const int item : list) { stringsList << QString::number(item); } qCDebug(runtime) << "return:" << stringsList; return stringsList.join(","); } QString MainLayoutProfilesManager::toDBStringList(const QList > &list) { FCT_IDENTIFICATION; qCDebug(function_parameters) << list; QStringList stringsList; for ( const QPair &item : list) stringsList << item.first + "/" + item.second; qCDebug(runtime) << "return:" << stringsList; return stringsList.join(","); } QList MainLayoutProfilesManager::toIntList(const QString &list) const { FCT_IDENTIFICATION; qCDebug(function_parameters) << list; QList retList; if ( list.isEmpty() ) return retList; const QStringList splitList = list.split(","); for ( const QString &item : splitList ) { retList << item.toInt(); } qCDebug(runtime) << "return:" << retList; return retList; } QList > MainLayoutProfilesManager::toPairStringList(const QString &list) { FCT_IDENTIFICATION; qCDebug(function_parameters) << list; QList> retList; if ( list.isEmpty() ) return retList; const QStringList &elements = list.split(","); for ( const QString &elementParams : elements ) { const QStringList ¶ms = elementParams.split("/"); if ( params.size() == 2 ) retList << QPair(params.at(0), params.at(1)); else qWarning() << "Unexpected number of Bandmap widget params" << elementParams; } return retList; } bool MainLayoutProfile::operator==(const MainLayoutProfile &profile) { return (profile.profileName == this->profileName && profile.rowA == this->rowA && profile.rowB == this->rowB && profile.detailColA == this->detailColA && profile.detailColB == this->detailColB && profile.detailColC == this->detailColC && profile.mainGeometry == this->mainGeometry && profile.mainState == this->mainState && profile.darkMode == this->darkMode && profile.tabsexpanded == this->tabsexpanded && profile.addlBandmaps == this->addlBandmaps); } bool MainLayoutProfile::operator!=(const MainLayoutProfile &profile) { return !operator==(profile); } MainLayoutProfile MainLayoutProfile::getClassicLayout() { MainLayoutProfile ret; ret.rowA << LogbookModel::COLUMN_NAME_INTL << LogbookModel::COLUMN_QTH_INTL << LogbookModel::COLUMN_GRID << LogbookModel::COLUMN_COMMENT_INTL; ret.detailColA << LogbookModel::COLUMN_CONTINENT << LogbookModel::COLUMN_ITUZ << LogbookModel::COLUMN_CQZ << LogbookModel::COLUMN_STATE << LogbookModel::COLUMN_COUNTY << LogbookModel::COLUMN_AGE << LogbookModel::COLUMN_VUCC_GRIDS; ret.detailColB << LogbookModel::COLUMN_DARC_DOK << LogbookModel::COLUMN_IOTA << LogbookModel::COLUMN_POTA_REF << LogbookModel::COLUMN_SOTA_REF << LogbookModel::COLUMN_WWFF_REF << LogbookModel::COLUMN_SIG_INTL << LogbookModel::COLUMN_SIG_INFO_INTL; ret.detailColC << LogbookModel::COLUMN_EMAIL << LogbookModel::COLUMN_WEB; return ret; } ================================================ FILE: data/MainLayoutProfile.h ================================================ #ifndef QLOG_DATA_MAINLAYOUTPROFILE_H #define QLOG_DATA_MAINLAYOUTPROFILE_H #include #include #include #include "data/ProfileManager.h" class MainLayoutProfile { public: MainLayoutProfile() { darkMode = 0; tabsexpanded = true; }; QString profileName; QList rowA; QList rowB; QList detailColA; QList detailColB; QList detailColC; QByteArray mainGeometry; QByteArray mainState; int darkMode; bool tabsexpanded; QList> addlBandmaps; bool operator== (const MainLayoutProfile &profile); bool operator!= (const MainLayoutProfile &profile); static MainLayoutProfile getClassicLayout(); private: friend QDataStream& operator<<(QDataStream& out, const MainLayoutProfile& v); friend QDataStream& operator>>(QDataStream& in, MainLayoutProfile& v); }; Q_DECLARE_METATYPE(MainLayoutProfile); class MainLayoutProfilesManager : public ProfileManagerSQL { Q_OBJECT public: explicit MainLayoutProfilesManager(); ~MainLayoutProfilesManager() { }; static MainLayoutProfilesManager* instance() { static MainLayoutProfilesManager instance; return &instance; }; void save(); static QString toDBStringList(const QList &list); static QString toDBStringList(const QList> &list); QList toIntList(const QString &list) const; static QList> toPairStringList(const QString &list); }; #endif // QLOG_DATA_MAINLAYOUTPROFILE_H ================================================ FILE: data/POTAEntity.h ================================================ #ifndef QLOG_DATA_POTAENTITY_H #define QLOG_DATA_POTAENTITY_H #include class POTAEntity { public: QString reference; QString name; bool active; qint16 entityID; QString locationDesc; double longitude; double latitude; QString grid; }; #endif // QLOG_DATA_POTAENTITY_H ================================================ FILE: data/POTASpot.h ================================================ #ifndef QLOG_DATA_POTASPOT_H #define QLOG_DATA_POTASPOT_H #include class POTASpot { public: quint64 spotId = 0; QString activator; QString activatorBaseCallsign; double frequency = 0.0; QString mode; QString reference; QString parkName; QDateTime spotTime; QString spotter; QString comments; QString source; QString name; QString locationDesc; }; #endif // QLOG_DATA_POTASPOT_H ================================================ FILE: data/ProfileManager.h ================================================ #ifndef QLOG_DATA_PROFILEMANAGER_H #define QLOG_DATA_PROFILEMANAGER_H #include #include #include #include #include /* the header file contains function implementation because * https://stackoverflow.com/questions/8752837/undefined-reference-to-template-class-constructor */ /* Question: * when I build, the compiler throws errors in every instance of the template class: * * undefined reference to `cola(float)::cola()'... (it's actually cola'<'float'>'::cola(), * but this doesn't let me use it like that.) */ /* * This is a common question in C++ programming. There are two valid answers to this. There are advantages * and disadvantages to both answers and your choice will depend on context. The common answer is to put all * the implementation in the header file, but there's another approach will will be suitable in some cases. * The choice is yours. * * The code in a template is merely a 'pattern' known to the compiler. The compiler won't compile the * constructors cola::cola(...) and cola::cola(...) until it is forced to do so. * And we must ensure that this compilation happens for the constructors at least once in the entire * compilation process, or we will get the 'undefined reference' error. (This applies to the other * methods of cola also.) * * Understanding the problem: * * The problem is caused by the fact that main.cpp and cola.cpp will be compiled separately first. * In main.cpp, the compiler will implicitly instantiate the template classes cola and * cola because those particular instantiations are used in main.cpp. The bad news is that * the implementations of those member functions are not in main.cpp, nor in any header file included * in main.cpp, and therefore the compiler can't include complete versions of those functions in main.o. * When compiling cola.cpp, the compiler won't compile those instantiations either, because there are no * implicit or explicit instantiations of cola or cola. Remember, when compiling cola.cpp, * the compiler has no clue which instantiations will be needed; and we can't expect it to compile for * every type in order to ensure this problem never happens! (cola, cola, cola, * cola< cola > ... and so on ...) * * The two answers are: * Tell the compiler, at the end of cola.cpp, which particular template classes will be required, * forcing it to compile cola and cola. * Put the implementation of the member functions in a header file that will be included every * time any other 'translation unit' (such as main.cpp) uses the template class. * * Answer 1: Explicitly instantiate the template, and its member definitions * At the end of cola.cpp, you should add lines explicitly instantiating all the relevant templates, such as * * template class cola; * template class cola; * * and you add the following two lines at the end of nodo_colaypila.cpp: * * template class nodo_colaypila; * template class nodo_colaypila; * * This will ensure that, when the compiler is compiling cola.cpp that it will explicitly compile * all the code for the cola and cola classes. Similarly, nodo_colaypila.cpp * contains the implementations of the nodo_colaypila<...> classes. * * In this approach, you should ensure that all the of the implementation is placed into * one .cpp file (i.e. one translation unit) and that the explicit instantation is placed * after the definition of all the functions (i.e. at the end of the file). * * Answer 2: Copy the code into the relevant header file * The common answer is to move all the code from the implementation files cola.cpp and * nodo_colaypila.cpp into cola.h and nodo_colaypila.h. In the long run, this is more * flexible as it means you can use extra instantiations (e.g. cola) without any * more work. But it could mean the same functions are compiled many times, once in * each translation unit. This is not a big problem, as the linker will correctly ignore * the duplicate implementations. But it might slow down the compilation a little. * * Summary * The default answer, used by the STL for example and in most of the code that any * of us will write, is to put all the implementations in the header files. But in a more * private project, you will have more knowledge and control of which particular template * classes will be instantiated. In fact, this 'bug' might be seen as a feature, as it * stops users of your code from accidentally using instantiations you have not tested * for or planned for ("I know this works for cola and cola, if you want * to use something else, tell me first and will can verify it works before enabling it."). */ class ProfileSignalSlot : public QObject { Q_OBJECT signals: void profileChanged(const QString &profileName); }; template class ProfileManagerSQL : public ProfileSignalSlot { public: explicit ProfileManagerSQL(const QString &tableName) : tableName(tableName) { QSqlQuery query(QString("SELECT profile_name FROM %1 WHERE IFNULL(selected, 0) = 1").arg(tableName)); currentProfile1 = query.first() ? query.value(0).toString() : QString(); if ( currentProfile1.isEmpty() ) qDebug() << "Empty profile name for " << tableName << "SQL Error" << query.lastError().text(); }; const T getCurProfile1() { return ( ! currentProfile1.isEmpty() ) ? getProfile(currentProfile1) : T(); }; bool __setCurProfile1(const QString &profileName) { bool ret = false; if ( profiles.contains(profileName) || profileName.isEmpty() ) { QSqlQuery query; // atomic change if ( !query.prepare(QString("UPDATE %1 " "SET selected = CASE " " WHEN profile_name = :profileName THEN 1 " " ELSE NULL " " END " "WHERE selected = 1 OR profile_name = :profileName2").arg(tableName)) ) { qWarning() << "Cannot prepare Update statement for" << tableName; return ret; } query.bindValue(":profileName", profileName); query.bindValue(":profileName2", profileName); if ( query.exec() ) { currentProfile1 = profileName; ret = true; } else qWarning() << "Cannot set the selected profile for " << tableName << query.lastError().text(); } else qWarning() << "Cannot set Current Profile to " << profileName << "because is not not a valid profile name" << tableName; return ret; }; void setCurProfile1(const QString &profileName) { currProfMutex.lock(); bool changed = __setCurProfile1(profileName); currProfMutex.unlock(); if ( changed ) emit profileChanged(currentProfile1); }; void saveCurProfile1() { currProfMutex.lock(); bool changed = __setCurProfile1(currentProfile1); currProfMutex.unlock(); if ( changed ) emit profileChanged(currentProfile1); }; const T getProfile(const QString &profileName) { if ( profiles.contains(profileName) ) { profilesMutex.lock(); T ret = profiles.value(profileName).template value(); profilesMutex.unlock(); return ret; } else { if ( !profileName.isEmpty() ) qWarning() << "Profile " << profileName << " not found" << tableName; return T(); } }; void addProfile(const QString &profileName, T profile) { profilesMutex.lock(); profiles.insert(profileName, QVariant::fromValue(profile)); profilesMutex.unlock(); }; int removeProfile(const QString &profileName) { currProfMutex.lock(); if ( currentProfile1 == profileName ) { __setCurProfile1(QString()); } currProfMutex.unlock(); profilesMutex.lock(); int ret = profiles.remove(profileName); profilesMutex.unlock(); return ret; }; const QStringList profileNameList() { profilesMutex.lock(); QStringList ret(profiles.keys()); profilesMutex.unlock(); return ret; }; private: QMap profiles; QString currentProfile1; QString tableName; QMutex profilesMutex; QMutex currProfMutex; }; #endif // QLOG_DATA_PROFILEMANAGER_H ================================================ FILE: data/RigProfile.cpp ================================================ #include #include #include "RigProfile.h" #include "core/debug.h" #include "data/ProfileManager.h" #include "rig/Rig.h" #include "SerialPort.h" MODULE_IDENTIFICATION("qlog.data.rigprofile"); QDataStream& operator<<(QDataStream& out, const RigProfile& v) { out << v.profileName << v.model << v.portPath << v.hostname << v.netport << v.baudrate << v.databits << v.stopbits << v.flowcontrol << v.parity << v.pollInterval << v.txFreqStart << v.txFreqEnd << v.getFreqInfo << v.getModeInfo << v.getVFOInfo << v.getPWRInfo << v.ritOffset << v.xitOffset << v.getRITInfo << v.getXITInfo << v.defaultPWR << v.getPTTInfo << v.QSYWiping << v.getKeySpeed << v.assignedCWKey << v.keySpeedSync << v.driver << v.dxSpot2Rig << v.pttType << v.pttPortPath << v.rts << v.dtr << v.civAddr << v.shareRigctld << v.rigctldPort << v.rigctldPath << v.rigctldArgs << v.getSplitInfo; return out; } QDataStream& operator>>(QDataStream& in, RigProfile& v) { in >> v.profileName; in >> v.model; in >> v.portPath; in >> v.hostname; in >> v.netport; in >> v.baudrate; in >> v.databits; in >> v.stopbits; in >> v.flowcontrol; in >> v.parity; in >> v.pollInterval; in >> v.txFreqStart; in >> v.txFreqEnd; in >> v.getFreqInfo; in >> v.getModeInfo; in >> v.getVFOInfo; in >> v.getPWRInfo; in >> v.ritOffset; in >> v.xitOffset; in >> v.getRITInfo; in >> v.getXITInfo; in >> v.defaultPWR; in >> v.getPTTInfo; in >> v.QSYWiping; in >> v.getKeySpeed; in >> v.assignedCWKey; in >> v.keySpeedSync; in >> v.driver; in >> v.dxSpot2Rig; in >> v.pttType; in >> v.pttPortPath; in >> v.rts; in >> v.dtr; in >> v.civAddr; in >> v.shareRigctld; in >> v.rigctldPort; in >> v.rigctldPath; in >> v.rigctldArgs; in >> v.getSplitInfo; return in; } RigProfilesManager::RigProfilesManager() : ProfileManagerSQL("rig_profiles") { FCT_IDENTIFICATION; QSqlQuery profileQuery; if ( ! profileQuery.prepare(QString("SELECT profile_name, model, port_pathname, hostname, " "netport, baudrate, databits, stopbits, flowcontrol, parity, " "pollinterval, txfreq_start, txfreq_end, get_freq, get_mode, " "get_vfo, get_pwr, rit_offset, xit_offset, get_rit, get_xit, " "default_pwr, get_ptt, qsy_wiping, get_key_speed, assigned_cw_key, " "key_speed_sync, driver, dxspot2rig, ptt_type, ptt_port_pathname, " "IFNULL(rts, '%0'), IFNULL(dtr, '%0'), IFNULL(civaddr, -1), " "IFNULL(share_rigctld, 0), IFNULL(rigctld_port, 4532), " "IFNULL(rigctld_path, ''), IFNULL(rigctld_args, ''), get_split " "FROM rig_profiles").arg(SerialPort::SERIAL_SIGNAL_NONE))) { qWarning()<< "Cannot prepare select"; } if ( profileQuery.exec() ) { while (profileQuery.next()) { RigProfile profileDB; profileDB.profileName = profileQuery.value(0).toString(); profileDB.model = profileQuery.value(1).toInt(); profileDB.portPath = profileQuery.value(2).toString(); profileDB.hostname = profileQuery.value(3).toString(); profileDB.netport = profileQuery.value(4).toUInt(); profileDB.baudrate = profileQuery.value(5).toUInt(); profileDB.databits = profileQuery.value(6).toUInt(); profileDB.stopbits = profileQuery.value(7).toFloat(); profileDB.flowcontrol = profileQuery.value(8).toString(); profileDB.parity = profileQuery.value(9).toString(); profileDB.pollInterval = profileQuery.value(10).toUInt(); profileDB.txFreqStart = profileQuery.value(11).toFloat(); profileDB.txFreqEnd = profileQuery.value(12).toFloat(); profileDB.getFreqInfo = profileQuery.value(13).toBool(); profileDB.getModeInfo = profileQuery.value(14).toBool(); profileDB.getVFOInfo = profileQuery.value(15).toBool(); profileDB.getPWRInfo = profileQuery.value(16).toBool(); profileDB.ritOffset = profileQuery.value(17).toDouble(); profileDB.xitOffset = profileQuery.value(18).toDouble(); profileDB.getRITInfo = profileQuery.value(19).toBool(); profileDB.getXITInfo = profileQuery.value(20).toBool(); profileDB.defaultPWR = profileQuery.value(21).toDouble(); profileDB.getPTTInfo = profileQuery.value(22).toBool(); profileDB.QSYWiping = profileQuery.value(23).toBool(); profileDB.getKeySpeed = profileQuery.value(24).toBool(); profileDB.assignedCWKey = profileQuery.value(25).toString(); profileDB.keySpeedSync = profileQuery.value(26).toBool(); profileDB.driver = profileQuery.value(27).toInt(); profileDB.dxSpot2Rig = profileQuery.value(28).toBool(); profileDB.pttType = profileQuery.value(29).toString(); profileDB.pttPortPath = profileQuery.value(30).toString(); profileDB.rts = profileQuery.value(31).toString(); profileDB.dtr = profileQuery.value(32).toString(); profileDB.civAddr = profileQuery.value(33).toInt(); profileDB.shareRigctld = profileQuery.value(34).toBool(); profileDB.rigctldPort = profileQuery.value(35).toUInt(); profileDB.rigctldPath = profileQuery.value(36).toString(); profileDB.rigctldArgs = profileQuery.value(37).toString(); profileDB.getSplitInfo = profileQuery.value(38).toBool(); addProfile(profileDB.profileName, profileDB); } } else { qInfo() << "Station Profile DB select error " << profileQuery.lastError().text(); } } void RigProfilesManager::save() { FCT_IDENTIFICATION; QSqlQuery deleteQuery; QSqlQuery insertQuery; if ( ! deleteQuery.prepare("DELETE FROM rig_profiles") ) { qWarning() << "cannot prepare Delete statement"; return; } if ( ! insertQuery.prepare("INSERT INTO rig_profiles(profile_name, model, port_pathname, hostname, netport, " "baudrate, databits, stopbits, flowcontrol, parity, pollinterval, txfreq_start, " "txfreq_end, get_freq, get_mode, get_vfo, get_pwr, rit_offset, xit_offset, get_rit, " "get_xit, default_pwr, get_ptt, qsy_wiping, get_key_speed, assigned_cw_key, key_speed_sync, " "driver, dxSpot2Rig, ptt_type, ptt_port_pathname, rts, dtr, civaddr, " "share_rigctld, rigctld_port, rigctld_path, rigctld_args, get_split ) " "VALUES (:profile_name, :model, :port_pathname, :hostname, :netport, " ":baudrate, :databits, :stopbits, :flowcontrol, :parity, :pollinterval, :txfreq_start, " ":txfreq_end, :get_freq, :get_mode, :get_vfo, :get_pwr, :rit_offset, :xit_offset, :get_rit, " ":get_xit, :default_pwr, :get_ptt, :qsy_wiping, :get_key_speed, :assigned_cw_key, :key_speed_sync, " ":driver, :dxSpot2Rig, :ptt_type, :ptt_port_pathname, :rts, :dtr, :civaddr, " ":share_rigctld, :rigctld_port, :rigctld_path, :rigctld_args, :get_split )") ) { qWarning() << "cannot prepare Insert statement"; return; } if ( deleteQuery.exec() ) { const QStringList &keys = profileNameList(); for ( const QString &key: keys ) { const RigProfile &rigProfile = getProfile(key); insertQuery.bindValue(":profile_name", key); insertQuery.bindValue(":model", rigProfile.model); insertQuery.bindValue(":port_pathname", rigProfile.portPath); insertQuery.bindValue(":hostname", rigProfile.hostname); insertQuery.bindValue(":netport", rigProfile.netport); insertQuery.bindValue(":baudrate", rigProfile.baudrate); insertQuery.bindValue(":databits", rigProfile.databits); insertQuery.bindValue(":stopbits", rigProfile.stopbits); insertQuery.bindValue(":flowcontrol", rigProfile.flowcontrol); insertQuery.bindValue(":parity", rigProfile.parity); insertQuery.bindValue(":pollinterval", rigProfile.pollInterval); insertQuery.bindValue(":txfreq_start", rigProfile.txFreqStart); insertQuery.bindValue(":txfreq_end", rigProfile.txFreqEnd); insertQuery.bindValue(":get_freq", rigProfile.getFreqInfo); insertQuery.bindValue(":get_mode", rigProfile.getModeInfo); insertQuery.bindValue(":get_vfo", rigProfile.getVFOInfo); insertQuery.bindValue(":get_pwr", rigProfile.getPWRInfo); insertQuery.bindValue(":rit_offset", rigProfile.ritOffset); insertQuery.bindValue(":xit_offset", rigProfile.xitOffset); insertQuery.bindValue(":get_rit", rigProfile.getRITInfo); insertQuery.bindValue(":get_xit", rigProfile.getXITInfo); insertQuery.bindValue(":default_pwr", rigProfile.defaultPWR); insertQuery.bindValue(":get_ptt", rigProfile.getPTTInfo); insertQuery.bindValue(":qsy_wiping", rigProfile.QSYWiping); insertQuery.bindValue(":get_key_speed", rigProfile.getKeySpeed); insertQuery.bindValue(":assigned_cw_key", rigProfile.assignedCWKey); insertQuery.bindValue(":key_speed_sync", rigProfile.keySpeedSync); insertQuery.bindValue(":driver", rigProfile.driver); insertQuery.bindValue(":dxSpot2Rig", rigProfile.dxSpot2Rig); insertQuery.bindValue(":ptt_type", rigProfile.pttType); insertQuery.bindValue(":ptt_port_pathname", rigProfile.pttPortPath); insertQuery.bindValue(":rts", rigProfile.rts); insertQuery.bindValue(":dtr", rigProfile.dtr); insertQuery.bindValue(":civaddr", (rigProfile.civAddr >= 0) ? rigProfile.civAddr : QVariant()); // 0x0 is valid CIV Address, NULL will be Auto insertQuery.bindValue(":share_rigctld", rigProfile.shareRigctld); insertQuery.bindValue(":rigctld_port", rigProfile.rigctldPort); insertQuery.bindValue(":rigctld_path", rigProfile.rigctldPath); insertQuery.bindValue(":rigctld_args", rigProfile.rigctldArgs); insertQuery.bindValue(":get_split", rigProfile.getSplitInfo); if ( ! insertQuery.exec() ) { qInfo() << "Station Profile DB insert error " << insertQuery.lastError().text() << insertQuery.lastQuery(); } } } else { qInfo() << "Station Profile DB delete error " << deleteQuery.lastError().text(); } saveCurProfile1(); } bool RigProfile::operator==(const RigProfile &profile) { return (profile.profileName == this->profileName && profile.model == this->model && profile.portPath == this->portPath && profile.hostname == this->hostname && profile.netport == this->netport && profile.baudrate == this->baudrate && profile.databits == this->databits && profile.stopbits == this->stopbits && profile.flowcontrol == this->flowcontrol && profile.parity == this->parity && profile.pollInterval == this->pollInterval && profile.txFreqStart == this->txFreqStart && profile.txFreqEnd == this->txFreqEnd && profile.getFreqInfo == this->getFreqInfo && profile.getModeInfo == this->getModeInfo && profile.getVFOInfo == this->getVFOInfo && profile.getPWRInfo == this->getPWRInfo && profile.ritOffset == this->ritOffset && profile.xitOffset == this->xitOffset && profile.getRITInfo == this->getRITInfo && profile.getXITInfo == this->getXITInfo && profile.defaultPWR == this->defaultPWR && profile.getPTTInfo == this->getPTTInfo && profile.QSYWiping == this->QSYWiping && profile.getKeySpeed == this->getKeySpeed && profile.assignedCWKey == this->assignedCWKey && profile.keySpeedSync == this->keySpeedSync && profile.driver == this->driver && profile.dxSpot2Rig == this->dxSpot2Rig && profile.pttType == this->pttType && profile.pttPortPath == this->pttPortPath && profile.rts == this->rts && profile.dtr == this->dtr && profile.civAddr == this->civAddr && profile.shareRigctld == this->shareRigctld && profile.rigctldPort == this->rigctldPort && profile.rigctldPath == this->rigctldPath && profile.rigctldArgs == this->rigctldArgs && profile.getSplitInfo == this->getSplitInfo ); } bool RigProfile::operator!=(const RigProfile &profile) { return !operator==(profile); } QString RigProfile::toHTMLString() const { QString ret = "" + QObject::tr("My Rig") + ": " + profileName + "
"; return ret; } RigProfile::rigPortType RigProfile::getPortType() const { FCT_IDENTIFICATION; if ( driver == Rig::OMNIRIG_DRIVER || driver == Rig::OMNIRIGV2_DRIVER ) return RigProfile::SPECIAL_OMNIRIG_ATTACHED; if ( !hostname.isEmpty() && portPath.isEmpty() ) { return RigProfile::NETWORK_ATTACHED; } return RigProfile::SERIAL_ATTACHED; } ================================================ FILE: data/RigProfile.h ================================================ #ifndef QLOG_DATA_RIGPROFILE_H #define QLOG_DATA_RIGPROFILE_H #include #include #include #include #include #include "data/ProfileManager.h" class RigProfile { public: enum rigPortType { SERIAL_ATTACHED, NETWORK_ATTACHED, SPECIAL_OMNIRIG_ATTACHED }; RigProfile() { model = 1; netport = 0; baudrate = 0; databits = 0; stopbits = 0.0; pollInterval = 0; txFreqStart = 0.0; txFreqEnd = 0.0; getFreqInfo = false; getModeInfo = false; getVFOInfo = false; getPWRInfo = false; ritOffset = 0.0; xitOffset = 0.0, getRITInfo = false; getXITInfo = true; defaultPWR = 0.0, getPTTInfo = false; QSYWiping = false, getKeySpeed = false, keySpeedSync = false; driver = 0, dxSpot2Rig = false, civAddr = -1; shareRigctld = false; rigctldPort = 4532; getSplitInfo = false; }; QString profileName; QString portPath; QString hostname; QString flowcontrol; QString parity; double ritOffset; double xitOffset; double defaultPWR; QString assignedCWKey; QString pttType; QString pttPortPath; QString rts; QString dtr; QString rigctldPath; // empty = autodetect QString rigctldArgs; // additional arguments qint32 model; quint32 baudrate; float stopbits; quint32 pollInterval; float txFreqStart; float txFreqEnd; qint32 driver; quint16 netport; qint16 civAddr; // -1 = AUTO; otherwise address quint16 rigctldPort; quint8 databits; bool getFreqInfo; bool getModeInfo; bool getVFOInfo; bool getPWRInfo; bool getRITInfo; bool getXITInfo; bool getPTTInfo; bool QSYWiping; bool getKeySpeed; bool keySpeedSync; bool dxSpot2Rig; bool shareRigctld; bool getSplitInfo; bool operator== (const RigProfile &profile); bool operator!= (const RigProfile &profile); QString toHTMLString() const; rigPortType getPortType() const; private: friend QDataStream& operator<<(QDataStream& out, const RigProfile& v); friend QDataStream& operator>>(QDataStream& in, RigProfile& v); }; Q_DECLARE_METATYPE(RigProfile) class RigProfilesManager : public ProfileManagerSQL { Q_OBJECT public: explicit RigProfilesManager(); ~RigProfilesManager() { }; static RigProfilesManager* instance() { static RigProfilesManager instance; return &instance; }; void save(); }; #endif // QLOG_DATA_RIGPROFILE_H ================================================ FILE: data/RotProfile.cpp ================================================ #include #include #include "RotProfile.h" #include "core/debug.h" MODULE_IDENTIFICATION("qlog.data.rotprofile"); QDataStream& operator<<(QDataStream& out, const RotProfile& v) { out << v.profileName << v.model << v.portPath << v.hostname << v.netport << v.baudrate << v.databits << v.stopbits << v.flowcontrol << v.parity << v.driver; return out; } QDataStream& operator>>(QDataStream& in, RotProfile& v) { in >> v.profileName; in >> v.model; in >> v.portPath; in >> v.hostname; in >> v.netport; in >> v.baudrate; in >> v.databits; in >> v.stopbits; in >> v.flowcontrol; in >> v.parity; in >> v.driver; return in; } RotProfilesManager::RotProfilesManager() : ProfileManagerSQL("rot_profiles") { FCT_IDENTIFICATION; QSqlQuery profileQuery; if ( ! profileQuery.prepare("SELECT profile_name, model, port_pathname, hostname, netport, baudrate, databits, stopbits, flowcontrol, parity, driver FROM rot_profiles") ) { qWarning()<< "Cannot prepare select"; } if ( profileQuery.exec() ) { while (profileQuery.next()) { RotProfile profileDB; profileDB.profileName = profileQuery.value(0).toString(); profileDB.model = profileQuery.value(1).toInt(); profileDB.portPath = profileQuery.value(2).toString(); profileDB.hostname = profileQuery.value(3).toString(); profileDB.netport = profileQuery.value(4).toUInt(); profileDB.baudrate = profileQuery.value(5).toUInt(); profileDB.databits = profileQuery.value(6).toUInt(); profileDB.stopbits = profileQuery.value(7).toFloat(); profileDB.flowcontrol = profileQuery.value(8).toString(); profileDB.parity = profileQuery.value(9).toString(); profileDB.driver = profileQuery.value(10).toInt(); addProfile(profileDB.profileName, profileDB); } } else { qInfo() << "Rot Profile DB select error " << profileQuery.lastError().text(); } } RotProfilesManager *RotProfilesManager::instance() { FCT_IDENTIFICATION; static RotProfilesManager instance; return &instance; } void RotProfilesManager::save() { FCT_IDENTIFICATION; QSqlQuery deleteQuery; QSqlQuery insertQuery; if ( ! deleteQuery.prepare("DELETE FROM rot_profiles") ) { qWarning() << "cannot prepare Delete statement"; return; } if ( ! insertQuery.prepare("INSERT INTO rot_profiles(profile_name, model, port_pathname, hostname, netport, baudrate, databits, stopbits, flowcontrol, parity, driver) " "VALUES (:profile_name, :model, :port_pathname, :hostname, :netport, :baudrate, :databits, :stopbits, :flowcontrol, :parity, :driver)") ) { qWarning() << "cannot prepare Insert statement"; return; } if ( deleteQuery.exec() ) { const QStringList &keys = profileNameList(); for ( const QString &key: keys ) { const RotProfile &rigProfile = getProfile(key); insertQuery.bindValue(":profile_name", key); insertQuery.bindValue(":model", rigProfile.model); insertQuery.bindValue(":port_pathname", rigProfile.portPath); insertQuery.bindValue(":hostname", rigProfile.hostname); insertQuery.bindValue(":netport", rigProfile.netport); insertQuery.bindValue(":baudrate", rigProfile.baudrate); insertQuery.bindValue(":databits", rigProfile.databits); insertQuery.bindValue(":stopbits", rigProfile.stopbits); insertQuery.bindValue(":flowcontrol", rigProfile.flowcontrol); insertQuery.bindValue(":parity", rigProfile.parity); insertQuery.bindValue(":driver", rigProfile.driver); if ( ! insertQuery.exec() ) { qInfo() << "Station Profile DB insert error " << insertQuery.lastError().text() << insertQuery.lastQuery(); } } } else { qInfo() << "Rot Profile DB delete error " << deleteQuery.lastError().text(); } saveCurProfile1(); } bool RotProfile::operator==(const RotProfile &profile) { return (profile.profileName == this->profileName && profile.model == this->model && profile.portPath == this->portPath && profile.hostname == this->hostname && profile.netport == this->netport && profile.baudrate == this->baudrate && profile.databits == this->databits && profile.stopbits == this->stopbits && profile.flowcontrol == this->flowcontrol && profile.parity == this->parity && profile.driver == this->driver); } bool RotProfile::operator!=(const RotProfile &profile) { return !operator==(profile); } RotProfile::rotPortType RotProfile::getPortType() const { FCT_IDENTIFICATION; if ( !hostname.isEmpty() && portPath.isEmpty() ) { return RotProfile::NETWORK_ATTACHED; } return RotProfile::SERIAL_ATTACHED; } ================================================ FILE: data/RotProfile.h ================================================ #ifndef QLOG_DATA_ROTPROFILE_H #define QLOG_DATA_ROTPROFILE_H #include #include #include #include #include "data/ProfileManager.h" #define DEFAULT_ROT_MODEL 1 class RotProfile { public: enum rotPortType { SERIAL_ATTACHED, NETWORK_ATTACHED }; RotProfile() { model = DEFAULT_ROT_MODEL; netport = 0; baudrate = 0; databits = 0; stopbits = 0.0; driver = 0;}; QString profileName; qint32 model; QString portPath; QString hostname; quint16 netport; quint32 baudrate; quint8 databits; float stopbits; QString flowcontrol; QString parity; qint32 driver; bool operator== (const RotProfile &profile); bool operator!= (const RotProfile &profile); rotPortType getPortType() const; private: friend QDataStream& operator<<(QDataStream& out, const RotProfile& v); friend QDataStream& operator>>(QDataStream& in, RotProfile& v); }; Q_DECLARE_METATYPE(RotProfile); class RotProfilesManager : public ProfileManagerSQL { Q_OBJECT public: explicit RotProfilesManager(); ~RotProfilesManager() { }; static RotProfilesManager* instance(); void save(); }; #endif // QLOG_DATA_ROTPROFILE_H ================================================ FILE: data/RotUsrButtonsProfile.cpp ================================================ #include #include #include "RotUsrButtonsProfile.h" #include "core/debug.h" MODULE_IDENTIFICATION("qlog.data.rotusrbuttonsprofile"); QDataStream& operator<<(QDataStream& out, const RotUsrButtonsProfile& v) { out << v.profileName; for( int i = 0; i < v.shortDescs.size(); i++ ) { out << v.shortDescs[i]; } for( int i = 0; i < v.bearings.size(); i++ ) { out << v.bearings[i]; } return out; } QDataStream& operator>>(QDataStream& in, RotUsrButtonsProfile& v) { in >> v.profileName; for( int i = 0; i < v.shortDescs.size(); i++ ) { in >> v.shortDescs[i]; } for( int i = 0; i < v.bearings.size(); i++ ) { in >> v.bearings[i]; } return in; } RotUsrButtonsProfilesManager::RotUsrButtonsProfilesManager() : ProfileManagerSQL("rot_user_buttons_profiles") { FCT_IDENTIFICATION; QSqlQuery profileQuery; if ( ! profileQuery.prepare("SELECT profile_name, button1_short, button1_value, button2_short, button2_value, " "button3_short, button3_value, button4_short, button4_value " "FROM rot_user_buttons_profiles") ) { qWarning()<< "Cannot prepare select"; } if ( profileQuery.exec() ) { while (profileQuery.next()) { RotUsrButtonsProfile profileDB; int column = 0; profileDB.profileName = profileQuery.value(column++).toString(); for ( int i = 0; i < profileDB.shortDescs.size(); i++ ) { profileDB.shortDescs[i] = profileQuery.value(column++).toString(); profileDB.bearings[i] = profileQuery.value(column++).toDouble(); } addProfile(profileDB.profileName, profileDB); } } else { qInfo() << "Rot User Button Profile DB select error " << profileQuery.lastError().text(); } } void RotUsrButtonsProfilesManager::save() { FCT_IDENTIFICATION; QSqlQuery deleteQuery; QSqlQuery insertQuery; if ( ! deleteQuery.prepare("DELETE FROM rot_user_buttons_profiles") ) { qWarning() << "Cannot prepare Delete statement"; return; } if ( ! insertQuery.prepare("INSERT INTO rot_user_buttons_profiles(profile_name, button1_short, button1_value, button2_short, button2_value," "button3_short, button3_value, button4_short, button4_value)" "VALUES (:profile_name, :b1_short, :b1_value, :b2_short, :b2_value," ":b3_short, :b3_value, :b4_short, :b4_value)") ) { qWarning() << "Cannot prepare Insert statement"; return; } if ( deleteQuery.exec() ) { const QStringList &keys = profileNameList(); for ( const QString &key: keys ) { const RotUsrButtonsProfile &rotUsrButtonProfile = getProfile(key); insertQuery.bindValue(":profile_name", key); for ( int i = 0; i < rotUsrButtonProfile.shortDescs.size(); i++ ) { insertQuery.bindValue(QString(":b%1_short").arg(i+1), rotUsrButtonProfile.shortDescs[i]); } for ( int i = 0; i < rotUsrButtonProfile.bearings.size(); i++ ) { insertQuery.bindValue(QString(":b%1_value").arg(i+1), rotUsrButtonProfile.bearings[i]); } if ( ! insertQuery.exec() ) { qInfo() << "Rot User Button Profile DB insert error " << insertQuery.lastError().text() << insertQuery.lastQuery(); } } } else { qInfo() << "Rot User Button Profile DB delete error " << deleteQuery.lastError().text(); } saveCurProfile1(); } bool RotUsrButtonsProfile::operator==(const RotUsrButtonsProfile &profile) { return (profile.profileName == this->profileName && profile.shortDescs == this->shortDescs && profile.bearings == this->bearings); } bool RotUsrButtonsProfile::operator!=(const RotUsrButtonsProfile &profile) { return !operator==(profile); } ================================================ FILE: data/RotUsrButtonsProfile.h ================================================ #ifndef QLOG_DATA_ROTUSRBUTTONSPROFILE_H #define QLOG_DATA_ROTUSRBUTTONSPROFILE_H #include #include #include #include "data/ProfileManager.h" #define MAX_ROT_USER_BUTTONS 4 class RotUsrButtonsProfile { public: RotUsrButtonsProfile() { shortDescs.resize(MAX_ROT_USER_BUTTONS); bearings.resize(MAX_ROT_USER_BUTTONS); } QString profileName; QVector shortDescs; QVector bearings; bool operator== (const RotUsrButtonsProfile &profile); bool operator!= (const RotUsrButtonsProfile &profile); private: friend QDataStream& operator<<(QDataStream& out, const RotUsrButtonsProfile& v); friend QDataStream& operator>>(QDataStream& in, RotUsrButtonsProfile& v); }; Q_DECLARE_METATYPE(RotUsrButtonsProfile); class RotUsrButtonsProfilesManager : public ProfileManagerSQL { Q_OBJECT public: explicit RotUsrButtonsProfilesManager(); ~RotUsrButtonsProfilesManager() { }; static RotUsrButtonsProfilesManager* instance() { static RotUsrButtonsProfilesManager instance; return &instance; }; void save(); }; #endif // QLOG_DATA_ROTUSRBUTTONSPROFILE_H ================================================ FILE: data/SOTAEntity.h ================================================ #ifndef QLOG_DATA_SOTAENTITY_H #define QLOG_DATA_SOTAENTITY_H #include class SOTAEntity { public: QString summitCode; QString associationName; QString regionName; QString summitName; qint32 altm; qint32 altft; double gridref1; double gridref2; double longitude; double latitude; qint16 points; qint16 bonusPoints; QDate validFrom; QDate validTo; }; #endif // QLOG_DATA_SOTAENTITY_H ================================================ FILE: data/SerialPort.cpp ================================================ #include "SerialPort.h" #include "core/debug.h" MODULE_IDENTIFICATION("qlog.core.serialport"); SerialPort::SerialPort(QObject *parent) : QObject{parent} { FCT_IDENTIFICATION; } const QString SerialPort::SERIAL_FLOWCONTROL_NONE = "none"; const QString SerialPort::SERIAL_FLOWCONTROL_HARDWARE = "hardware"; const QString SerialPort::SERIAL_FLOWCONTROL_SOFTWARE = "software"; const QString SerialPort::SERIAL_PARITY_EVEN = "even"; const QString SerialPort::SERIAL_PARITY_ODD = "odd"; const QString SerialPort::SERIAL_PARITY_MARK = "mark"; const QString SerialPort::SERIAL_PARITY_SPACE = "space"; const QString SerialPort::SERIAL_PARITY_NO = "no"; const QString SerialPort::SERIAL_SIGNAL_NONE = "none"; const QString SerialPort::SERIAL_SIGNAL_HIGH = "high"; const QString SerialPort::SERIAL_SIGNAL_LOW = "low"; ================================================ FILE: data/SerialPort.h ================================================ #ifndef QLOG_DATA_SERIALPORT_H #define QLOG_DATA_SERIALPORT_H #include #include class SerialPort : public QObject { Q_OBJECT public: explicit SerialPort(QObject *parent = nullptr); static const QString SERIAL_FLOWCONTROL_NONE; static const QString SERIAL_FLOWCONTROL_SOFTWARE; static const QString SERIAL_FLOWCONTROL_HARDWARE; static const QString SERIAL_PARITY_EVEN; static const QString SERIAL_PARITY_ODD; static const QString SERIAL_PARITY_MARK; static const QString SERIAL_PARITY_SPACE; static const QString SERIAL_PARITY_NO; static const QString SERIAL_SIGNAL_NONE; static const QString SERIAL_SIGNAL_HIGH; static const QString SERIAL_SIGNAL_LOW; }; #endif // QLOG_DATA_SERIALPORT_H ================================================ FILE: data/SpotAlert.h ================================================ #ifndef QLOG_DATA_SPOTALERT_H #define QLOG_DATA_SPOTALERT_H #include #include "data/WsjtxEntry.h" #include "data/DxSpot.h" class SpotAlert { public: enum ALERTSOURCETYPE { UKNOWN = 0, DXSPOT = 0b1, WSJTXCQSPOT = 0b10 }; ALERTSOURCETYPE source; QStringList ruleNameList; WsjtxEntry spot; SpotAlert() : source(DXSPOT) {}; SpotAlert(const QStringList &ruleList, const DxSpot &sourceSpot) : spot(sourceSpot) { source = SpotAlert::ALERTSOURCETYPE::DXSPOT; ruleNameList = ruleList; }; SpotAlert(const QStringList &ruleList, const WsjtxEntry &sourceWsjtx) : spot(sourceWsjtx) { source = SpotAlert::ALERTSOURCETYPE::WSJTXCQSPOT; ruleNameList = ruleList; }; const DxSpot& getDxSpot() const {return spot;}; private: }; Q_DECLARE_METATYPE(SpotAlert); #endif // QLOG_DATA_SPOTALERT_H ================================================ FILE: data/StationProfile.cpp ================================================ #include "data/StationProfile.h" #include "core/debug.h" #include #include #include MODULE_IDENTIFICATION("qlog.data.stationprofile"); QDataStream& operator<<(QDataStream& out, const StationProfile& v) { out << v.profileName << v.callsign << v.locator << v.operatorName << v.operatorCallsign << v.qthName << v.iota << v.sota << v.sig << v.sigInfo << v.vucc << v.wwff << v.pota << v.ituz << v.cqz << v.dxcc << v.country << v.county << v.darcDOK; return out; } QDataStream& operator>>(QDataStream& in, StationProfile& v) { in >> v.profileName; in >> v.callsign; in >> v.locator; in >> v.operatorName; in >> v.operatorCallsign; in >> v.qthName; in >> v.iota; in >> v.sota; in >> v.sig; in >> v.sigInfo; in >> v.vucc; in >> v.wwff; in >> v.pota; in >> v.ituz; in >> v.cqz; in >> v.dxcc; in >> v.country; in >> v.county; in >> v.darcDOK; return in; } StationProfilesManager::StationProfilesManager() : ProfileManagerSQL("station_profiles") { FCT_IDENTIFICATION; QSqlQuery profileQuery; if ( ! profileQuery.prepare("SELECT profile_name, callsign, locator, " "operator_name, qth_name, iota, sota, sig, sig_info, vucc, pota, " "ituz, cqz, dxcc, country, county, operator_callsign, darc_dok " "FROM station_profiles") ) { qWarning()<< "Cannot prepare select"; } if ( profileQuery.exec() ) { while (profileQuery.next()) { StationProfile profileDB; profileDB.profileName = profileQuery.value(0).toString(); profileDB.callsign = profileQuery.value(1).toString(); profileDB.locator = profileQuery.value(2).toString(); profileDB.operatorName = profileQuery.value(3).toString(); profileDB.qthName = profileQuery.value(4).toString(); profileDB.iota = profileQuery.value(5).toString(); profileDB.sota = profileQuery.value(6).toString(); profileDB.sig = profileQuery.value(7).toString(); profileDB.sigInfo = profileQuery.value(8).toString(); profileDB.vucc = profileQuery.value(9).toString(); profileDB.pota = profileQuery.value(10).toString(); profileDB.ituz = profileQuery.value(11).toInt(); profileDB.cqz = profileQuery.value(12).toInt(); profileDB.dxcc = profileQuery.value(13).toInt(); profileDB.country = profileQuery.value(14).toString(); profileDB.county = profileQuery.value(15).toString(); profileDB.operatorCallsign = profileQuery.value(16).toString(); profileDB.darcDOK = profileQuery.value(17).toString(); addProfile(profileDB.profileName, profileDB); } } else { qInfo() << "Station Profile DB select error " << profileQuery.lastError().text(); } } void StationProfilesManager::save() { FCT_IDENTIFICATION; QSqlQuery deleteQuery; QSqlQuery insertQuery; if ( ! deleteQuery.prepare("DELETE FROM station_profiles") ) { qWarning() << "cannot prepare Delete statement"; return; } if ( ! insertQuery.prepare("INSERT INTO station_profiles(profile_name, callsign, locator, operator_name, qth_name, iota, sota, sig, sig_info, vucc, wwff, pota, ituz, cqz, dxcc, country, county, operator_callsign, darc_dok) " "VALUES (:profile_name, :callsign, :locator, :operator_name, :qth_name, :iota, :sota, :sig, :sig_info, :vucc, :wwff, :pota, :ituz, :cqz, :dxcc, :country, :county, :operator_callsign, :darc_dok)") ) { qWarning() << "cannot prepare Insert statement"; return; } if ( deleteQuery.exec() ) { const QStringList &keys = profileNameList(); for ( const QString &key : keys ) { StationProfile stationProfile = getProfile(key); insertQuery.bindValue(":profile_name", key); insertQuery.bindValue(":callsign", stationProfile.callsign); insertQuery.bindValue(":locator", stationProfile.locator); insertQuery.bindValue(":operator_name", stationProfile.operatorName); insertQuery.bindValue(":qth_name", stationProfile.qthName); insertQuery.bindValue(":iota", stationProfile.iota); insertQuery.bindValue(":sota", stationProfile.sota); insertQuery.bindValue(":sig", stationProfile.sig); insertQuery.bindValue(":sig_info", stationProfile.sigInfo); insertQuery.bindValue(":vucc", stationProfile.vucc); insertQuery.bindValue(":wwff", stationProfile.wwff); insertQuery.bindValue(":pota", stationProfile.pota); insertQuery.bindValue(":ituz", stationProfile.ituz); insertQuery.bindValue(":cqz", stationProfile.cqz); insertQuery.bindValue(":dxcc", stationProfile.dxcc); insertQuery.bindValue(":country", stationProfile.country); insertQuery.bindValue(":county", stationProfile.county); insertQuery.bindValue(":operator_callsign", stationProfile.operatorCallsign); insertQuery.bindValue(":darc_dok", stationProfile.darcDOK); if ( ! insertQuery.exec() ) { qInfo() << "Station Profile DB insert error " << insertQuery.lastError().text() << insertQuery.lastQuery(); } } } else { qInfo() << "Station Profile DB delete error " << deleteQuery.lastError().text(); } saveCurProfile1(); } StationProfile StationProfilesManager::findByCallsign(const QString &callsign) { FCT_IDENTIFICATION; qCDebug(function_parameters) << callsign; const QStringList &names = profileNameList(); for ( const QString &name : names ) { StationProfile profile = getProfile(name); if ( profile.callsign.compare(callsign, Qt::CaseInsensitive) == 0 ) return profile; } return StationProfile(); } bool StationProfile::operator==(const StationProfile &profile) { return (profile.profileName == this->profileName && profile.callsign == this->callsign && profile.locator == this->locator && profile.operatorName == this->operatorName && profile.qthName == this->qthName && profile.iota == this->iota && profile.sota == this->sota && profile.sig == this->sig && profile.sigInfo == this->sigInfo && profile.vucc == this->vucc && profile.wwff == this->wwff && profile.pota == this->pota && profile.ituz == this->ituz && profile.cqz == this->cqz && profile.dxcc == this->dxcc && profile.country == this->country && profile.county == this->county && profile.operatorCallsign == this->operatorCallsign && profile.darcDOK == this->darcDOK ); } bool StationProfile::operator!=(const StationProfile &profile) { return !operator==(profile); } QString StationProfile::toHTMLString() const { QString ret = "" + QObject::tr("Logging Station Callsign") + ": " + callsign + "
" + ((!locator.isEmpty()) ? "" + QObject::tr("My Gridsquare") + ": " + locator + "
" : "") + ((!operatorName.isEmpty()) ? "" + QObject::tr("Operator Name") + ": " + operatorName + "
" : "") + ((!operatorCallsign.isEmpty()) ? "" + QObject::tr("Operator Callsign") + ": " + operatorCallsign + "
" : "") + ((!qthName.isEmpty()) ? "" + QObject::tr("My City") + ": " + qthName + "
" : "") + ((!country.isEmpty()) ? "" + QObject::tr("My Country") + ": " + country + "
" : "") + ((!county.isEmpty()) ? "" + QObject::tr("My County") + ": " + county + "
" : "") + ((!iota.isEmpty()) ? "" + QObject::tr("My IOTA") + ": " + iota + "
" : "") + ((!sota.isEmpty()) ? "" + QObject::tr("My SOTA") + ": " + sota + "
" : "" ) + ((!sig.isEmpty()) ? "" + QObject::tr("My Special Interest Activity") + ": " + sig + "
" : "" )+ ((!sigInfo.isEmpty()) ? "" + QObject::tr("My Spec. Interes Activity Info") + ": " + sigInfo + "
" : "" )+ ((!vucc.isEmpty()) ? "" + QObject::tr("My VUCC Grids") + ": " + vucc + "
" : "") + ((!wwff.isEmpty()) ? "" + QObject::tr("My WWFF") + ": " + wwff + "
" : "") + ((!pota.isEmpty()) ? "" + QObject::tr("My POTA Ref") + ": " + pota : "") + ((!pota.isEmpty()) ? "" + QObject::tr("My DARC DOK") + ": " + darcDOK : "") + ((ituz != 0) ? "" + QObject::tr("My ITU") + ": " + QString::number(ituz) : "") + " " + ((cqz != 0) ? "" + QObject::tr("My CQZ") + ": " + QString::number(cqz) : "") + " " + ((dxcc != 0) ? "" + QObject::tr("My DXCC") + ": " + QString::number(dxcc) : ""); return ret; } QString StationProfile::getContactInnerJoin() const { QStringList ret({"contacts.station_callsign = station_profiles.callsign", "contacts.my_gridsquare = station_profiles.locator"}); auto addIfNoEmpty = [&](const QString &field, const QString& contact, const QString &profile) { if ( ! field.isEmpty() ) ret << "contacts." + contact + " = station_profiles." + profile; }; auto addIfNoEmptyNumber = [&](int field, const QString& contact, const QString &profile) { if ( field ) ret << "contacts." + contact + " = station_profiles." + profile; }; addIfNoEmpty(operatorName, "my_name_intl", "operator_name"); addIfNoEmpty(qthName, "my_city_intl", "qth_name"); addIfNoEmpty(iota, "my_iota", "iota"); addIfNoEmpty(sota, "my_sota", "sota"); addIfNoEmpty(sig, "my_sig_intl", "sig"); addIfNoEmpty(sigInfo, "my_sig_info_intl", "sig_info"); addIfNoEmpty(vucc, "my_vucc_grids", "vucc"); addIfNoEmpty(wwff, "my_wwff_ref", "wwff"); addIfNoEmpty(pota, "my_pota_ref", "pota"); addIfNoEmptyNumber(ituz, "my_itu_zone", "ituz"); addIfNoEmptyNumber(cqz, "my_cq_zone", "cqz"); addIfNoEmptyNumber(dxcc, "my_dxcc", "dxcc"); // skipping Country - depends on dxcc addIfNoEmpty(county, "my_cnty", "county"); addIfNoEmpty(operatorCallsign, "operator", "operator_callsign"); addIfNoEmpty(darcDOK, "my_darc_dok", "darc_doc"); return "(" + ret.join(" AND ") + ")"; } ================================================ FILE: data/StationProfile.h ================================================ #ifndef QLOG_DATA_STATIONPROFILE_H #define QLOG_DATA_STATIONPROFILE_H #include #include #include #include #include #include #include "data/ProfileManager.h" class StationProfile { public: StationProfile() : ituz(0), cqz(0), dxcc(0) {}; QString profileName; QString callsign; QString locator; QString operatorName; QString operatorCallsign; QString qthName; QString iota; QString pota; QString sota; QString sig; QString sigInfo; QString vucc; QString wwff; int ituz; int cqz; int dxcc; QString country; QString county; QString darcDOK; bool operator== (const StationProfile &profile); bool operator!= (const StationProfile &profile); QString toHTMLString() const; QString getContactInnerJoin() const; private: friend QDataStream& operator<<(QDataStream& out, const StationProfile& v); friend QDataStream& operator>>(QDataStream& in, StationProfile& v); }; Q_DECLARE_METATYPE(StationProfile) class StationProfilesManager : public ProfileManagerSQL { Q_OBJECT public: explicit StationProfilesManager(); ~StationProfilesManager() {}; static StationProfilesManager* instance() { static StationProfilesManager instance; return &instance; }; void save(); StationProfile findByCallsign(const QString &callsign); }; #endif // QLOG_DATA_STATIONPROFILE_H ================================================ FILE: data/ToAllSpot.h ================================================ #ifndef QLOG_DATA_TOALLSPOT_H #define QLOG_DATA_TOALLSPOT_H #include #include "data/Dxcc.h" class ToAllSpot { public: QDateTime time; DxccEntity dxcc_spotter; QString spotter; QString message; }; #endif // QLOG_DATA_TOALLSPOT_H ================================================ FILE: data/UpdatableSQLRecord.cpp ================================================ #include #include "UpdatableSQLRecord.h" #include "core/debug.h" MODULE_IDENTIFICATION("qlog.core.updatableqslrecord"); UpdatableSQLRecord::UpdatableSQLRecord(int interval, QObject *parent) : QObject{parent}, interval(interval) { FCT_IDENTIFICATION; connect(&timer, &QTimer::timeout, this, &UpdatableSQLRecord::emitStoreRecord); } UpdatableSQLRecord::~UpdatableSQLRecord() { FCT_IDENTIFICATION; timer.stop(); } void UpdatableSQLRecord::updateRecord(const QSqlRecord &record) { FCT_IDENTIFICATION; if ( internalRecord.isEmpty() ) { internalRecord = record; qCDebug(runtime) << "Record is empty, starting timer" << interval; timer.start(interval); return; } else if ( !matchQSO(QSOMatchingType, record) ) { qCDebug(runtime) << "Records do not match"; timer.stop(); emitStoreRecord(); internalRecord = record; } else { qCDebug(runtime) << "Records match"; timer.stop(); for ( int i = 0; i < record.count(); ++i ) { const QString &fieldName = record.fieldName(i); if ( !internalRecord.contains(fieldName) ) internalRecord.append(record.field(i)); else if ( !record.value(i).toString().isEmpty() && internalRecord.value(fieldName).toString().isEmpty() ) internalRecord.setValue(fieldName, record.value(i)); } } qCDebug(runtime) << "starting timer" << interval; timer.start(interval); } void UpdatableSQLRecord::emitStoreRecord() { FCT_IDENTIFICATION; timer.stop(); if ( internalRecord.isEmpty() ) return; qCDebug(runtime) << "emitting record"; emit recordReady(internalRecord); internalRecord.clear(); } bool UpdatableSQLRecord::matchQSO(const MatchingType matchingType, const QSqlRecord &record) { FCT_IDENTIFICATION; const QStringList &fields = matchingFields.value(matchingType); for ( const QString &fieldName : fields ) { qCDebug(runtime) << "compare field name " << fieldName << "In value" << internalRecord.value(fieldName) << "New value" << record.value(fieldName); if ( internalRecord.value(fieldName) != record.value(fieldName)) return false; } return true; } ================================================ FILE: data/UpdatableSQLRecord.h ================================================ #ifndef QLOG_DATA_UPDATABLESQLRECORD_H #define QLOG_DATA_UPDATABLESQLRECORD_H #include #include #include #include class UpdatableSQLRecord : public QObject { Q_OBJECT public: explicit UpdatableSQLRecord(int interval = 500, QObject *parent = nullptr); ~UpdatableSQLRecord(); void updateRecord(const QSqlRecord &record); signals: void recordReady( QSqlRecord ); private slots: void emitStoreRecord(); private: enum MatchingType{ QSOMatchingType }; QHash matchingFields { {QSOMatchingType, {"callsign", "mode", "submode"}} }; bool matchQSO(const MatchingType, const QSqlRecord &); QSqlRecord internalRecord; QTimer timer; int interval; }; #endif // QLOG_DATA_UPDATABLESQLRECORD_H ================================================ FILE: data/WCYSpot.h ================================================ #ifndef QLOG_DATA_WCYSPOT_H #define QLOG_DATA_WCYSPOT_H #include class WCYSpot { public: QDateTime time; quint16 KIndex; quint16 expK; quint16 AIndex; quint16 RIndex; quint16 SFI; QString SA; QString GMF; QString Au; }; #endif // QLOG_DATA_WCYSPOT_H ================================================ FILE: data/WWFFEntity.h ================================================ #ifndef QLOG_DATA_WWFFENTITY_H #define QLOG_DATA_WWFFENTITY_H #include class WWFFEntity { public: QString reference; QString status; QString name; QString program; QString dxcc; QString state; QString county; QString continent; QString iota; QString iaruLocator; double latitude; double longitude; QString iucncat; QDate validFrom; QDate validTo; }; #endif // QLOG_DATA_WWFFENTITY_H ================================================ FILE: data/WWVSpot.h ================================================ #ifndef QLOG_DATA_WWVSPOT_H #define QLOG_DATA_WWVSPOT_H #include class WWVSpot { public: QDateTime time; quint16 SFI; quint16 AIndex; quint16 KIndex; QString info1; QString info2; }; #endif // QLOG_DATA_WWVSPOT_H ================================================ FILE: data/WsjtxDecode.h ================================================ #ifndef QLOG_DATA_WSJTXDECODE_H #define QLOG_DATA_WSJTXDECODE_H #include #include class WsjtxDecode { public: QString id, mode, message; bool is_new, low_confidence, off_air; QTime time; qint32 snr; quint32 df; double dt; operator QString() const { return QString("WsjtxDecode: ") + "(" + "ID: " + id + "; " + "IsNew: " + QString::number(is_new) + "; " + "Time: " + time.toString() + "; " + "SNR: " + QString::number(snr) + "; " + "DeltaTime: " + QString::number(dt) + "; " + "DeltaFreq: " + QString::number(df) + "; " + "Mode: " + mode + "; " + "Message: " + message + "; " + "LowConfidence: " + QString::number(low_confidence) + "; " + "OffAir: " + QString::number(off_air) + "; " + ")"; } }; #endif // QLOG_DATA_WSJTXDECODE_H ================================================ FILE: data/WsjtxEntry.h ================================================ #ifndef QLOG_DATA_WSJTXENTRY_H #define QLOG_DATA_WSJTXENTRY_H #include "data/WsjtxDecode.h" #include "data/DxSpot.h" class WsjtxEntry : public DxSpot { public: WsjtxDecode decode; QString grid; double distance; QDateTime receivedTime; QString decodedMode; WsjtxEntry() : DxSpot(), distance(0.0){}; WsjtxEntry(const DxSpot &other) : DxSpot(other), distance(0.0) {}; operator QString() const { return QString("WsjtxEntry ") + "Country: " + QString::number(dxcc.dxcc) + " " + "CQZ" + QString::number(dxcc.cqz) + " " + "ITUZ" + QString::number(dxcc.ituz) + " " + "Status: " + QString::number(status) + " " + "Band: " + band + " " + "ModeGroup: " + (BandPlan::isFTxMode(decodedMode) ? BandPlan::MODE_GROUP_STRING_FTx : BandPlan::MODE_GROUP_STRING_DIGITAL ) + " " + "spotter Country: " + QString::number(dxcc_spotter.dxcc) + " " + "Continent: " + dxcc.cont + " " + "Spotter Continent: " + dxcc_spotter.cont + " " + "Callsign: " + callsign + " " + "Message: " + decode.message + " " + "DX Member: " + memberList2StringList().join(", "); }; }; #endif // QLOG_DATA_WSJTXENTRY_H ================================================ FILE: data/WsjtxLog.h ================================================ #ifndef QLOG_DATA_WSJTXLOG_H #define QLOG_DATA_WSJTXLOG_H #include #include class WsjtxLog { public: QString id, dx_call, dx_grid, mode, rprt_sent, rprt_rcvd; QString tx_pwr, comments, name, op_call, my_call, my_grid, prop_mode; QString exch_sent, exch_rcvd; QDateTime time_on, time_off; quint64 tx_freq; operator QString() const { return QString("WsjtxLog: ") + "(" + "ID: " + id + "; " + "DateTimeOff: " + time_off.toString() + "; " + "DXCall: " + dx_call + "; " + "DXGrid: " + dx_grid + "; " + "TXFreq: " + QString::number(tx_freq) + "; " + "Mode: " + mode + "; " + "RrpSent: " + rprt_sent + "; " + "RrpRcvd: " + rprt_rcvd + "; " + "TxPower: " + tx_pwr + "; " + "Comments: " + comments + "; " + "Name: " + name + "; " + "DateTimeOn: " + time_on.toString() + "; " + "OpCall: " + op_call + "; " + "MyCall: " + my_call + "; " + "MyGrid: " + my_grid + "; " + "ExchSent: " + exch_sent + "; " + "ExchRcvd: " + exch_rcvd + "; " + "ADIFPropMode: " + prop_mode + "; " + ")"; } }; #endif // QLOG_DATA_WSJTXLOG_H ================================================ FILE: data/WsjtxLogADIF.h ================================================ #ifndef QLOG_DATA_WSJTXLOGADIF_H #define QLOG_DATA_WSJTXLOGADIF_H #include class WsjtxLogADIF { public: QString id, log_adif; operator QString() const { return QString("WsjtxLogADIF") + "(" + "ID: " + id + ";" + "ADIF: " + log_adif + ";" + ")"; } }; #endif // QLOG_DATA_WSJTXLOGADIF_H ================================================ FILE: data/WsjtxStatus.h ================================================ #ifndef QLOG_DATA_WSJTXSTATUS_H #define QLOG_DATA_WSJTXSTATUS_H #include class WsjtxStatus { public: QString id, mode, tx_mode, sub_mode; QString dx_call, dx_grid, de_call, de_grid; QString report; quint64 dial_freq; qint32 rx_df, tx_df; bool tx_enabled, transmitting, decoding; bool tx_watchdog, fast_mode; quint8 special_op_mode; quint32 freq_tolerance, tr_period; QString conf_name, tx_message; operator QString() const { return QString("WsjtxStatus: ") + "(" + "ID: " + id + "; " + "Dial: " + QString::number(dial_freq) + "; " + "Mode: " + mode + "; " + "DXCall: " + dx_call + "; " + "Report: " + report + "; " + "TXMode: " + tx_mode + "; " + "TXEnabled: " + QString::number(tx_enabled) + "; " + "Transmitting: " + QString::number(transmitting) + "; " + "Decoding: " + QString::number(decoding) + "; " + "RxDF: " + QString::number(rx_df) + "; " + "TxDF: " + QString::number(tx_df) + "; " + "DECall: " + de_call + "; " + "DEGrid: " + de_grid + "; " + "DXGrid: " + dx_grid + "; " + "TXWatchdog: " + QString::number(tx_watchdog) + "; " + "SubMode: " + sub_mode + "; " + "FastMode: " + QString::number(fast_mode) + "; " + "SpecOpMode: " + QString::number(special_op_mode) + "; " + "FreqTolerance: "+ QString::number(freq_tolerance) + "; " + "TRPeriod: " + QString::number(tr_period) + "; " + "ConfName: " + conf_name + "; " + "TXMessage: " + tx_message + "; " + ")"; } }; #endif // QLOG_DATA_WSJTXSTATUS_H ================================================ FILE: debian/changelog ================================================ qlog (0.50.0-1) UNRELEASED; urgency=low * [NEW] - Added Split detection * [NEW] - Added Developer and Support tools (PR #991 @aa5sh @foldynl) * [NEW] - Added a simple QSL label printing dialog (issue #562) * [NEW] - Added Cabrillo contest export * [NEW] - Added direct labeling of QSL Requested and QSL Received to the QSO context menu (PR #982 @aa5sh) * [NEW] - DXCC Submission Report (PR #967 @aa5sh) * [NEW] - DXC - Search pre-fills the callsign from New Contact if callsign is present * [NEW] - Settings - Added Danger Zone tab with Delete all passwords and QSOs * [CHANGED] - Mutex-free Omnirig1 and Omnirig2 * [CHANGED] - POTA/SOTA/WWFF/SAT list are download from QLog LOV-repo * [CHANGED] - Used an external lib for CSV parsing for LOV Download * [CHANGED] - Import - ADIF import updates DXCC only if it is missing in the QSO (issue #983) * [REMOVED] - Import - ADIF import does not include the option to update DXCC during the import - no longer needed * Fixed Callsign disappears when calling a DX in a split via VFO-B (issue #799) * Fixing Rendering issue (issue #989) * Updated debian packaging (PR @979 thx @dawkagaming) * Hamlib rigctld switching between VFO A/B doesnt change the band mode - added workaround (issue #999) * Awards internal redesign * Removed AN from the WAC award (issue #1010) -- foldynl Sun, 26 Apr 2026 10:25:10 +0200 qlog (0.49.1-1) UNRELEASED; urgency=low * Fixed Online Map OSM Access Blocked banner (issue #956) * Fixed Package build process issue - openssl-dev is missing (issue #957) * Fixed Missing dependence for QTKeychain (issue #964) * Added French Translation (PR #969 thx @fillods) -- foldynl Thu, 19 Mar 2026 12:18:46 +0100 qlog (0.49.0-1) UNRELEASED; urgency=low * [NEW] - Added Pack and Unpack Data and Setting - Computer to Computer Migration (issue #535) * [NEW] - Added Rig Sharing via Rigctld (PR #736 issue #159 @aa5sh @foldynl) * [NEW] - Added QSL Gallery * [NEW] - QSO Filter - Added REGEXP operator * [NEW] - Settings - TQSL - Added Path auto-detect and validation * [NEW] - Upload QSO - Added LoTW Station Location Combo (PR #929 @TrgoSk @foldynl) * [NEW] - QSO Export - Added Station Profile Filter * [NEW] - BandMap shows emergency frequencies (issue #462) * [NEW] - Added Speed Up Down Macros - WinKey and CWDaemon (issue #491) * [NEW] - Settings Winkey v2 - Added PaddleOnly Sidetone (issue #739) * [NEW] - Settings Winkey v2 and CWDaemon - Added Sidetone Freq * [NEW] - All County fields contain Completer for Ukraine, US, Japan, NZ, Spanish, Russia (PR #785 @aa5sh @foldynl) * [NEW] - Added County Awards for Ukraine, US, Japan, NZ, Spanish, Russia (PR #785 @aa5sh @foldynl) * [NEW] - ADIF 3.1.7 - Added new modes FT2, FREEDATA, RIBBIT_PIX, RIBBIT_SMS (issue #934) * [CHANGED] - Generic FTx for FT8/FT4 (FT2) in Alert and DXC filter (issue #937) * [CHANGED] - LoTW QSL matching algorithm uses Mode and Mode Group matching (issue #942) * [CHANGED] - Reduced download period for DXCC Entities from 21 to 7 days * LogbookWidget: Delete Performance Optimization * Fixed Awards POTA Activator Filter * Fixed man page * Fixing SATmode Activity is blank (issue #948) -- foldynl Fri, 13 Mar 2026 18:56:51 +0100 qlog (0.48.0-1) UNRELEASED; urgency=low * [NEW] - Rig Widget - tuning rig with mouse (issue #855) * [NEW] - Awards - Added User Filter Combo (issue #870) * [NEW] - Statistics - Added User Filter; new Dialog layout (issue #870) * [NEW] - Alerts - Added detailed Log Status settings (issue #817) * [NEW] - Settings - Enable/Disable sending color-coded status indicators back to WSJT-X (issue #885 @aa5sh @foldynl) * [CHANGED] - Omnirig Drivers: Removed QT AXContainers - pure Windows API code * [CHANGED] - Enabled Chekbox in Alerts Table (issue #867) * [CHANGED] - Serial Port Completer contains Horizontal ScrollBar * [CHANGED] - TCI - Settings - Default PWR defines the maximum output power * Fixed Omnirig drivers keep sending frequency change (issue #872) * Fixed ADIF import does not accept a default operator name (issue #884) * Fixed HamQTH URL from http to https (PR #886 @aa5sh) * Fixed Missing Band value when QSO from FLDigi (issue #892) -- foldynl Fri, 30 Jan 2026 16:28:43 +0100 qlog (0.47.1-1) UNRELEASED; urgency=low * [CHANGED] - DXC - VE7CC-type Cluster is correctly detected (PR #851 @kyleboyle) * [CHANGED] - Chat - Changed ON4KST URL (issue #857) * Fixed missing clear button for Search Callsign (issue #753) * Fixed Updated QSOs are not sent to wavelog (issue #847) * Fixed Fixed Omnirig(v2) getFreq, getVFO, setFreq (issue #853) * Fixed ADI Header does not follow ADIF Spec (issue #859) * Fixed OmniRig settings are not saved (issue #862) -- foldynl Fri, 19 Dec 2025 15:56:45 +0100 qlog (0.47.0-1) UNRELEASED; urgency=low * [NEW] - Adds theme options - native, light, dark (PR #718 @kyleboyle) * [NEW] - Implemented ADIF 3.1.6 * [NEW] - ADIF 3.1.6 - Added new modes FSK and MTONE * [NEW] - ADIF 3.1.6 - Added new contest IDs * [NEW] - ADIF 3.1.6 - Added new column EQSL_AG (Import/Export only) * [NEW] - Statistics, QSODetail contain deleted DXCC Entities (PR #728 @aa5sh @foldynl) * [NEW] - Settings Hamlib - Added support to define CIV Addr for Icom and Ten-Tec (issue #747) * [NEW] - Settings Hamlib - Added RTS and DTR control (PR #809 @aa5sh) * [NEW] - Settings GUI - Added options to switch distance unit * [NEW] - Added Linux manpage (PR #791 @dawkagaming) * [NEW] - Added a link to the GitHub release notes under Help-Whats New * [NEW] - Added workaround for WriteLog - always call callbook lookups (PR #833 @sjwoodr) * [NEW] - Awards - Added Not-Confirmed filter (PR #836 @aa5sh) * [NEW] - Rotator Widget - Added buttons to change button profiles * [CHANGED] - Settings - Added network loop detection for WSJTX Forward (issue #815 @aa5sh @foldynl) * [CHANGED] - Rotator - Rotator timeout is set to 5s (PR #823 @aa5sh) * [CHANGED] - Double Spin Boxes - the decimal separator is forced to be a period * [DELETED] - WSJTX Widget - Removed Freq and Mode * Fixed QSODetail - Anti-meridian bug on map (issue #786) * Fixed Statistics: Center maps on QTH longitude (issue #824) * Fixed OmniRig disconnecting when its status changed unexpectedly (issue #832) -- foldynl Sat, 6 Dec 2025 12:59:15 +0100 qlog (0.46.2-1) UNRELEASED; urgency=high * Fixed Spaces after QRZ.com name (issue #767) * Fixed SPID Rot1Prog Rotator more 360 deg (issue #775) * Fixed build instructions on Debian (PR #777 @leventelist) * Fixed Bandmap truncated output despite having space (issue #779) * Fixed Statistics - Anti-meridian bug on map (issue #786) * Fixed Binary file in the tarballs (issue #794) * Fixed LOTW and eQSL upload status updated even when nothing is uploaded (issue #807) * Fixed Imported QSOs contain incorrect My DXCC info (issue #812) * Fixed Statistics Dialog does not show confirmed grids -- foldynl Fri, 31 Oct 2025 15:24:57 +0100 qlog (0.46.1-1) UNRELEASED; urgency=low * Fixed QSO filter incorrectly displays inserted date (issue #752) * Fixed Logbook Search Icon is not centered (issue #753) * Fixed Using CQRLog API Key for Clublog (issue #759) * Fixed Online Service Password leaking via debug files (issue #760) * Fixed Tabs are not showed properly (PR #762 @aa5sh) * Fixed inability to edit Power in QSO Detail (issue #763) -- foldynl Fri, 26 Sep 2025 17:14:06 +0200 qlog (0.46.0-1) UNRELEASED; urgency=low * [NEW] - NewContact: POTA/SOTA/WWFF/IOTA info is taken from the nearest spot (@aa5sh @foldynl) * [NEW] - DXSpots are sent to Flex Radio (PR #694 @aa5sh) * [NEW] - CWConsole - Added support for automatic periodic sending of CW Macros (issue #708) * [NEW] - Added mapping of Winkey hardware buttons to CW macro keys F1–F4 (issue #711) * [NEW] - Added Search Types to the Logbook Search Editbox (issue #733) * [NEW] - Rig - Periodic Rig status reporting independent of its changes (PR #730 @aa5sh) * [NEW] - Settings - Serial Port Completer for MacOS and Linux (PR #737 @aa5sh) * [NEW] - Hamlib - Attempt to send Power On when connecting the Rig * [NEW] - POTA Spots Info is received from the API at api.pota.app to improve POTA detection * [CHANGED] - Logbook Search Filter Widget - Added Search Widget * [CHANGED] - QSO Detail Country boxes use the new Search Filter Widget * [CHANGED] - NewContact - RST field uses the Overwrite Mode * [CHANGED] - NewContact - Default cursor position in RST is at the first character * [CHANGED] - Setting Dialog - Modes - Default Report can define cursor position * [CHANGED] - HamlibRot - Changed Poll Interval from 500 to 2000ms * Fixed DUPE is not detected for the first Contest QSO (issue #699) * Fixed FREQ_RX/BAND_RX is present only when RX and TX freqs are different (issue #714) * Fixed Repeated log recording (issue #722) * Fixed QSO Filter contains untranslated QSO items (issue #732) -- foldynl Fri, 12 Sep 2025 16:58:08 +0200 qlog (0.45.0-1) UNRELEASED; urgency=low * [NEW] - Single Dialog for Upload/Download Online Services (issue #448) * [NEW] - Added option to swap paddles to Winkey settings (issue #676) * [NEW] - Added native support for the FLRig interface (issue #679) * [NEW] - Added New Version Notification - only for MacOS and Windows (PR #669 @aa5sh) * [NEW] - QRZ Upload - Added support for multiple API Keys * [NEW] - Logbook - Added highlighting to the filter button when a filter is active * [NEW] - WSJTX - Filtered label is shown when filter is enabled * [NEW] - DXC - Filtered label is shown when filter is enabled * [NEW] - Added support to Upload Cloudlog/Wavelog * [NEW] - Add JS8 to legacy_modes.json (issue #677) * [CHANGED] - Unification Settings storage * [CHANGED] - Calculate distances according to IARU rules * Fixed missing Wsjtx Spot values in the AlertWindow -- foldynl Fri, 11 Jul 2025 16:17:43 +0200 qlog (0.44.1-1) UNRELEASED; urgency=low * Fixed Rotator Widget Seg Fault for new users (issue #666) -- foldynl Sun, 11 May 2025 07:30:56 +0200 qlog (0.44.0-1) UNRELEASED; urgency=low * [NEW] - Activity Manager - Added SKCC, UKSMG and FISTS as Dynamic Fields * [NEW] - QSO - FISTS, SKCC, and UKSMG are auto-filled from MembershipQE (issue #628) * [NEW] - Rotator - Added QSO destination needle (issue #644) * [NEW] - QSO Detail - Adds grayline and short path (PR #653 @kyleboyle) * [CHANGED] - Rotator - Needle colors correspond to the online map (issue #644) * Fixed TCI cw_macros must contain RigID (issue #663) * Fixed TCI setKeySpeed sets keyer and macros speed (issue #663) * Fixed Data mode missing in rig control window - rigctld (issue #660 @aa5sh) * Improved DXC Mode detection * Updated Simplified Chinese translation -- foldynl Fri, 9 May 2025 17:05:38 +0200 qlog (0.43.1-1) UNRELEASED; urgency=low * Fixed Click on PHONE DX Spots sets wrong mode (issue #453) * Fixed No freq via Omnirig for IC7400 (issue #639) -- foldynl Sat, 19 Apr 2025 10:05:24 +0200 qlog (0.43.0-1) UNRELEASED; urgency=low * [NEW] - Added support to receive QSOs from CSN SAT Device (PR #610 @aa5sh) * [NEW] - Bandmap - Multiple independent bandmap windows (PR #593 @kyleboyle @foldynl) * [NEW] - Winkey Keyer driver currently supports v1 and v2 hardware * [NEW] - QSO Detail - QSLs QSLr Msg is editable * [NEW] - Activity Manager - Added new dynamic field - QSL Message Send * [CHANGED] - HamlibDrv - Enabled Power, RIT, XIT, Morse for RIG Netctl * [CHANGED] - QSO Detail - QSLMSG replaced by QSLMSG_RCVD (issue #633) * [CHANGED] - eQSL Upload - Added an option to choose a QSLMsg field * [CHANGED] - eQSL Download - eQSL QSLMSG is stored to QLog QSLMSG_RCVD * [CHANGED] - eQSL Download - Added QSLMSG_RCVD merging - eQSL message is appended * Fixed Speed pot doesn't seem to work with a WinKeyer; double chars (issue #618) * Fixed BandMap Spots colour change after a QSO (issue #632) * Fixed Incorrect Field Mapping for Received eQSL Messages (issue #633) -- foldynl Fri, 4 Apr 2025 14:19:18 +0200 qlog (0.42.2-1) UNRELEASED; urgency=low * Fixed Logbook country translations (issue #608) * Fixed Unexpected dialog when QSO after contest (issue #614) * Fixed Statistics Widget does not display NULL continents (@aa5sh) * Fixed Statistics Widget does not display NULL Band, Mode * Fixed Statistics Widget TOP10 does not display removed DXCCs * Fixed Statistics Widget TOP10 does not display translated country names * Fixed Awards Widget does not display removed DXCCs -- foldynl Fri, 7 Mar 2025 15:19:11 +0100 qlog (0.42.1-1) UNRELEASED; urgency=low * Fixed Unexpected timezone info (issue #600) * Fixed DXCC Statistics picks more entities (issue #601) * Fixed a crash when no internet connection -- foldynl Sat, 22 Feb 2025 11:45:02 +0100 qlog (0.42.0-1) UNRELEASED; urgency=low * [NEW] - Awards - Added Slots - total over each band (issue #538) * [NEW] - Awards - Added Grid Award - 2/4/6 Chars grid (issue #564) * [NEW] - Settings - Added options to switch 12/24 time and date format (issue #573) * [NEW] - Activity Manager - Added new dynamic fields - Rig (issue #575) * [NEW] - LoTW Import - Fill missing information also for confirmed QSOs (@aa5sh) * [NEW] - Added CW macro QTH * [NEW] - DXC - Added 15min Trend * [CHANGED] - Changed IOTA LOV Source, the official web is used * [CHANGED] - CSV Export: Time and Date formats use ISO8601 format (issue #562) * [CHANGED] - Settings - Renamed Shortcuts to GUI tab * [CHANGED] - LOV - Improved LOV download performance (PR #582 @aa5sh) * Partial fix Windows State/Size does not save in case of fullscreen (issue #418) * Fixing TCI error when you change Rig (issue #526) * Fixed DXCC Award total worked confirmed counts included deleted entities (PR #588 @aa5sh) * Fixed Raspberry PI Flatpak - Import Select file dialog crashes (issue #589) * Suppressed the ability to edit Contest fields after the start (issue #590) -- foldynl Fri, 14 Feb 2025 18:45:25 +0100 qlog (0.41.1-1) UNRELEASED; urgency=low * Fixed compilation issue under Debian 12 (issue #571) * Fixed Incorrect GPL version in rpm/deb packages (issue #572) * Fixed MacOS floating utility window bug (PR #576 @kyleboyle) * Updated IT translation -- foldynl Tue, 21 Jan 2025 14:00:52 +0100 qlog (0.41.0-1) UNRELEASED; urgency=low * [NEW] - Logbook - Added a new context menu item - Update QSO from Callbook (issue #450 @aa5sh) * [NEW] - DIGI mode is used in case of DXC Digi Spots (issue #480) * [NEW] - DXC - Retrieve information about SOTA, POTA, IOTA and WWFF from comment (issue #482) * [NEW] - Alert - Added SOTA, POTA, IOTA and WWFF filter * [NEW] - Added the COM Port Completer for Windows platform (issue #490) * [NEW] - Settings - Added DXCC Confirmed By options (issue #508) * [NEW] - Added POTA Export Formatter (activator/hunter) (PR #514 @kyleboyle) * [NEW] - CW Console - CW Halt with the user-defined shortcut (issue #518) * [NEW] - Added an input parameter to save debug message to file * [NEW] - Logbook - Added sorting function to logbook table columns (PR #540 @kyleboyle) * [NEW] - Network Notification - Added Rig Status Notification * [NEW] - Implemented ADIF 3.1.5 * [NEW] - ADIF 3.1.5 - Added new submodes FSKH245 and FSKH105 for HELL * [NEW] - ADIF 3.1.5 - Added new contest IDs * [NEW] - ADIF 3.1.5 - Added new columns (Import/Export only) * [NEW] - ADIF 3.1.5 - Added My DARC DOK to Station Profile * [CHANGED] - Settings: disabled band and mode name modification * [CHANGED] - DX Stats contain all enabled bands (issue #538) * [CHANGED] - Removed Freq, TimeDate On/Off Data Type Indicators (issue #552) * [CHANGED] - ADIF 3.1.5 - VUCC and MY_VUCC can contain 6 or 4-chars locators * [CHANGED] - Stop exporting default value N for qsl_rcvd, qsl_sent, lotw/dcl/eslq_qsl_rcvd/sent * [CHANGED] - Extended QSL/Import Dupe matching rule to Callsign, Band, Mode, Time and Sat_Name (issue #563) * Fixed MacOS - keep floating windows visible on app unfocus (issue #530) * Fixed Contest Filter ignores the first QSO (issue #529) * Fixed It is not possible to quit Qlog with detached widgets Rot and Rig (issue #534) * Fixed ADX/CSV/JSON do not export non-ASCII chars (issue #542) * Fixed Checking the 60m checkbox in cluster filters allows 160m spots to appear (issue #543 @aa5sh) * Fixed Problems uploading to QRZ.com (issue #559 PR #561 @aa5sh) * Fixed DX Stat screen is jumping up/down * Fixed Omnirig drivers: Digi modes are not correclty recognized -- foldynl Sat, 11 Jan 2025 09:26:36 +0100 qlog (0.40.1-1) UNRELEASED; urgency=low * Fixed Bands - Added missing 8m band (issue #515) * Fixed CW Console - EXCSTR does not work properly (issue #517) * Fixed Activity Manager - Missing Propagation Mode None (issue #519) * Fixed QSO Filter - filter fields with random order (PR #525 @aa5sh) * Fixed TCI error when you change Rig (issue #526) * Fixed NewContact - satellite mode too wide (issue #527) -- foldynl Fri, 29 Nov 2024 12:07:56 +0100 qlog (0.40.0-1) UNRELEASED; urgency=low * [NEW] - Activity Manager - merged Layout Manager and profiles (issue #408) * [NEW] - Activity Manager - Added new dynamic fields - Contest fields, RX/TX Power * [NEW] - Added light support for contests (issue #345) * [NEW] - Added CW macros EXCHSTR, EXCHNR, EXCHNRN * [NEW] - Export Filter - Added user filter combo (original idea PR #476 @aa5sh) * [NEW] - New Contact - Added expand/collapse button to QSO field tab widget (PR #495 @kyleboyle) * [NEW] - Alert - Added CQZ and ITUZ filters * [NEW] - KSTChat - Added a new 40MHz room (PR #496 @kyleboyle) * [NEW] - Station Profile contains Operator Callsign (issue #441 @kyleboyle) * [NEW] - Station Profile contains County (issue #493 @kyleboyle) * [NEW] - Statistics - Adds time of day and better qso mapping (PR #501 @kyleboyle) * [NEW] - Bandmap - Tooltip shows a spotter callsign (PR #507 @Skittlebrau) * [CHANGED] - New Contact - Renamed DXCC Tab to DX Stats contains DXCC and Station Statistics (issue #477) * [CHANGED] - QSL Import dialog - Detail text is selectable by mouse and keyboard * [CHANGED] - Removed Main Menu Layout; Activity Manager is in the bottom-left corner * [CHANGED] - Removed Keep Options from the Equipment Menu - use Activity Manager for it * Fixed issue when CW is always selected after Settings exiting or connecting the Rig * Updated Timezone definition file - version 2024b -- foldynl Sun, 24 Nov 2024 09:12:01 +0100 qlog (0.39.0-1) UNRELEASED; urgency=low * [NEW] - DXC - Added Full-text search * [NEW] - Select S in RST Edit when focused (issue #454) * [NEW] - Alerts - Added Member Column * [CHANGED] - HamlibDrv Rig/Rot- Added multiplatform reliable sleep * [CHANGED] - Changed Backup policy * [CHANGED] - Logbook page size - improved performance * [CHANGED] - Logbook - CTRL-A (Select All) is disabled * [CHANGED] - Awards - Bands are displayed based on the Settings (issue #452) * [CHANGED] - WSJTX - More reliable detection of CQ stations (PR #471 @aa5sh) * [CHANGED] - WSJTX - SOTA/POTA/WWFF/SIG are being added to the logged QSO (PR #463 @aa5sh) * [CHANGED] - Stats - Add a confirmation dialog for displaying over 50k QSOs on the map * [CHANGED] - New Contact - Starting QSO Timer when Rig online and WSJTX Update Callsign Status is received * [CHANGED] - Added a postponed handling for Rig soft errors (issue #472) * Fixed WSJT-X does not emit band change if rig is disconnected (issue #447) * Fixed Wrong import of ADIF file of another log program (issue #455) * Fixed WSJTX log record is stored incorrectly if it contains non-ASCII chars(issue #458) * Fixed ADIF import does not import records with old DXCC Entities (issue #459) * Fixed ADIF import incorrectly uses Station Profile parameters (issue #461) * Fixed Logbook - QSO Table Column Width Does Not Stick (issue #464) * Fixed Alerts Window displays OOB Spots (issue #469) * Fixed Field values from past QSOs are used incorrectly in case of WSJTX QSOs (#issue 470) -- foldynl Sat, 5 Oct 2024 13:33:52 +0200 qlog (0.38.0-1) UNRELEASED; urgency=low * [NEW] - Logbook - Added Send DX Spot to the QSO Context Menu * [NEW] - DX Filter - Added Dedup Time/Freq difference setting (@aa5sh) * [NEW] - Rig Setting - Added RTS/DTR PTT Type support (issue #353) * [NEW] - Bandmap - Scrollbar position is saved per band (issue #415) * [NEW] - New Contact - Added a dynamic value completer for SIG field (issue #425) * [NEW] - Awards - Added SOTA/POTA/WWFF (@aa5sh issue #311) * [NEW] - Awards - Added Not-Worked Filter * [NEW] - New Contact - Added Long Path Azimuth info * [NEW] - POTA Fields allow a comma-delimited list of one or more POTA Refs * [NEW] - WSJTX tunes freq/mode like Rig if rig is disconnected * [CHANGED] - Alert Widget is a Dock Widget (issue #399) * [CHANGED] - QLog adds more information from callbook for WSJTX QSOs (issues #403 #405 #420) * [CHANGED] - File Open dialogs are not a native dialog under Linux (issue #427) * [CHANGED] - Profiles transferred to DB * [CHANGED] - LOV last_dates transferred to DB * [CHANGED] - DX Cluster - Login Callsign is always the base Callsign * [REMOVED] - Setting DXCC Date * Fix for MacOS Layout Geometry Restore (@aa5sh) * Fixed TQSL does not block GUI thread * Fixed MacOS build process (@aa5sh) -- foldynl Thu, 29 Aug 2024 11:11:09 +0200 qlog (0.37.2-1) UNRELEASED; urgency=low * Fixed Field QSL Send Via should be retained (issue #413) * Fixed Set rotator position fails if azimuth > 180 (issue #417) * Fixed Windows State/Size does not save in case of fullscreen (issue #418) * Fixed Significant rounding during azimuth calculation (issue #422) * Updated Simplified Chinese translation * Updated Spanish translaction * Added Italian translation (thx IK1VQY) -- foldynl Fri, 26 Jul 2024 13:54:11 +0200 qlog (0.37.1-1) UNRELEASED; urgency=low * Fixed QSO Table Callsign filter is not filled properly (issue #401) * Fixed DXC zero frequency for last QSO in case of FT8 QSOs (issue #404) * Fixed Callsign Context Menu does not work (issue #409) * Fixed QSO Detail Save and Edit buttons are not translated (issue #410) -- foldynl Wed, 10 Jul 2024 21:18:45 +0200 qlog (0.37.0-1) UNRELEASED; urgency=low * [NEW] - Added Shortcuts Editor (issue #293) * [NEW] - Added QO100 Bandplan to correctly categorize the DX Spots * [NEW] - Improveded detection of SH/DX SHF Spots * [NEW] - Online Map - Added WSJTX CQ Spots * [NEW] - WSJTX - Sortable View * [NEW] - Alerts - Sortable View * [NEW] - Added Spanish translation (thx LU1IDC) * [NEW[ - Added Search Callsign Clear Button (issue #396) * [CHANGED] - QRZ auth should be over POST with form data (issue #389) * [CHANGED] - Big CTY file is used * [CHANGED] - Callbook Country DXCC ID is used in case of difference from Big CTY * [CHANGED] - Removed ALT+W and CTRL+DEL shortcuts * [CHANGED] - Removed New Contact and Save Contact from Logbook Main Menu * Fixed Guantanamo KG4 Issue (issue #372) * Fixed QRZ Lookup Not Including Full Name - History (issue #388) * Fixed Spot Last QSO contains TX freq, should contain RX freq (issue #390) * Fixed Spot Last QSO must contain Freq in kHz (issue #391) * Fixed Bandmap select previous selected callsign issue (issue #394) * Fixed Malfunctioning tuning of WSJTX Alert spot * Fixed DXCC Status for FT4 Spots incorrectly identified in WSJTX -- foldynl Mon, 1 Jul 2024 16:01:04 +0200 qlog (0.36.0-1) UNRELEASED; urgency=low * [NEW] - WSJTX: Added support to received ADIF QSO Log record * [NEW] - Sat mode is derived from RX/TX Freq * [NEW] - Logbook filters change color when enabled * [NEW] - Frequency input boxes PageUp/Dn switches the band (issue #360) * [NEW] - CTRL + PgUp/Dn switch band on the connected rig - global shortcut (issue #360) * [NEW] - Added number of filtered QSOs (issue #374) * Fixed Callbook query does not work (issue #377) * Fixed Logbook columns are reordered after Delete (issue #383) * Fixed Missing Republic of Kosovo flag (issue #384) -- foldynl Fri, 7 Jun 2024 15:44:02 +0200 qlog (0.35.2-1) UNRELEASED; urgency=low * Improved delete performance; added delete progress bar (issue #351) * Fixed Password with plus is incorrectly sent to online services (issue #366) * Fixed Compilation issue under v4.6 (issue #368) * Fixed Network Rig configuration is not saved (issue #370) -- foldynl Tue, 21 May 2024 19:57:13 +0200 qlog (0.35.1-1) UNRELEASED; urgency=low * Fixed Free QRZ callbook - Name is not populating (issue #363) * Fixed Incorrect CW segment freqs (issue #365) -- foldynl Mon, 6 May 2024 19:28:13 +0200 qlog (0.35.0-1) UNRELEASED; urgency=low * [NEW] - Added Rot Interface PSTRotator Network * [NEW] - Added QSO/QSL Since option to eQSL Dialog * [NEW] - Bandmap - Current Mode segment visualisation * [NEW] - CW Console - Added Word/Whole mode switch * [NEW] - Added Callbook Profile Image Widget * [NEW] - ASCII conversion based on Text-Unidecode/iconv algorithm (issue #316 #350) * [NEW] - ITU/CQ Zones can be defined in Station Profile (issue #358) * [CHANGED] - Spacebar is used as a focus changer for fields where space is not allowed * [CHANGED] - Focus does not select text in the input fields * [CHANGED] - Force XCB under Linux Wayland * [CHANGED] - Bandmap - Added Callsign/Freq/Mode to tooltip (issue #355) * Fixed incorrect ADIF date format for clublog_qso_upload_date (issue #342) * Fixed The last name from Callbooks queries (issue #346) -- foldynl Fri, 3 May 2024 13:12:36 +0200 qlog (0.34.0-1) UNRELEASED; urgency=low * [NEW] - Rotator Widget - Azimuth by Clicking * [NEW] - Rotator Widget - QSO button provides Short/Long Path (issue #330) * [NEW] - Equipment Menu - Added Keep Options between application restart (issue #331) * Fixed TCI - Thetis Connection issue (issue #327) * Fixed TCI - Spots To Rig are not displayed (issue #328) * Fixed Bandmap unintentionally emits frequency labels (issue #333) * Fixed Failing to load grid square for G and EI SOTA summits (issue #336) * Fixed HRDLog On-Air message is not sent (issue #337) * Fixed Offline Map - Improved Path drawing -- foldynl Mon, 25 Mar 2024 13:54:20 +0100 qlog (0.33.1-1) UNRELEASED; urgency=critical * Fixed Rotator offline map is incorrectly centered (issue #324) * Fixed Hamlib integration not working (issue #325) * Fixed issue when Hamlib reopen rig caused Initialization Error * Fixed issue when Omnirig Drv did not emit rigIsReady signal -- foldynl Sat, 9 Mar 2024 13:37:01 +0100 qlog (0.33.0-1) UNRELEASED; urgency=low * [NEW] - Added Rig Interface TCI * [NEW] - Callbook search can be temporarily paused * Improved DXC Mode recognition * Fixed Modal dialog blinks - Windows platform (issue #315) * Fixed LoTW and eQSL download are only QSLs dowloads - button label changed (issue #318) * Fixed i18n: Country Names and Prop-modes are translated (issue #322) -- foldynl Fri, 8 Mar 2024 11:49:26 +0100 qlog (0.32.0-1) UNRELEASED; urgency=low * [NEW] - Added Rig Interface Omnirig v1 (Windows only) * [NEW] - Added Rig Interface Omnirig v2 (Windows only) * [NEW] - Clublog - Added Clear Clublog and reupload QSOs * [NEW] - Clublog - Added Real-time Insert/Update/Delete * [CHANGED] - Clublog - Upload callsign is derived from the Current Profile Callsign * Fixed clang linker failed issue (issue #301) * Fixed SAT Mode U/U missing (issue #308 PR #309 thanks ea5wa) * Fixed Multiple QSO selection. Callsigns modified by mistake (issue #310) * Fixed Callbook query cache is not properly cleared when Callbook settings change (issue #313) -- foldynl Sat, 10 Feb 2024 17:31:58 +0100 qlog (0.31.0-1) UNRELEASED; urgency=low * [NEW] - DXC - Improved Mode recognition * [NEW] - DXC - Switch Rig mode based on DXC Spot Mode (issue #217) * [NEW] - DXC - Added Spot Country Column (issue #273) * [NEW] - DXC - Added Menu for server management * [NEW] - DXC - Added Auto-connect to server * [NEW] - DXC - Added Keep QSOs Context Menu * [NEW] - DXC - Added Clear QSO Context Menu * [NEW] - DXC - Added support for SH/DX response parsing * [NEW] - DXC - Added support for username, password for connection * [CHANGED] - DXC - Commands Combo changed to function button with selectable function * [CHANGED] - DXC - DX Spot is prepared via DXC Command Line, Remark dialog was removed * [NEW] - Online Map - IBP station double-click tunes freq and switch Rig mode * [NEW] - Main Window - Current profile name is shown (issue #282) * [NEW] - Import - Details can be saved to file (issue #284) * [NEW] - Added Simplified Chinese translation (PR #285 thank BG7JAF) * [NEW] - New Contact - Enter saves QSO if QSO time is running (issue #293 - partial) * [NEW] - New Contact - Callsign Enter event saves QSO if no Callbook is active - Pileup Mode (issue #293) * [NEW] - RIG Widget - RIT/XIT are displayed with user-friendly units (issue #294) * [CHANGED] - SAT List download - Shortened download period for SAT list from 30 to 7 days * Fixed ADI Import is too slow (issue #270) * Improved Import/Export Performance * Fixed Missing Satellite Mode SX (issue #291) * Fixed QSO Detail - Issue when Sat-Name field was always disabled * Fixed RPM build - Installed (but unpackaged) metainfo file issue -- foldynl Fri, 5 Jan 2024 11:23:36 +0100 qlog (0.30.0-1) UNRELEASED; urgency=low * [NEW] - QSL Images are stored in the database * [NEW] - Added AppStream Metainfo File (PR #262 thanks AsciiWolf) * [NEW] - Added (WPX) prefix (issue #263) * [NEW] - Added WPX Award statistics * [NEW] - Added support for external translation files(issue #275) * [CHANGED] - Removed QSOID from Export dialog column setting (issue #258) * Fixed Date editor does not support NULL value in Logbook Direct Editor (issue #256) * Fixed duplicate entry in Windows Add or Remove - only Window platform (issue #260) * Fixed RST fields revert to 59 after changing them (issue #261) * Fixed Cannot change TQSL Path in Settings - flatpak (issue #271) -- foldynl Fri, 1 Dec 2023 16:23:18 +0100 qlog (0.29.2-1) UNRELEASED; urgency=low * Fixed QLog is already running error popup on MacOS (issue #257 thanks rjesson) -- foldynl Mon, 13 Nov 2023 14:46:57 +0100 qlog (0.29.1-1) UNRELEASED; urgency=low * Fixed QSL cards tooltip are not displayed under qt6.5 (issue #248) * Fixed Distance unit is not displayed in QSO Info under Windows (issue #250) * Fixed Editing STATION_CALLSIGN can cause unwanted change in QSO Detail (issue #251) * Fixed QSO Detail Operator Name containes an incorrect value (issue #252) * Fixed Calls with VE, VA are coding as Amsterdam & St Paul Islands instead of Canada (issue #253) * Fixed LoTW QSL import reports unmatched QSOs sometime (issue #254) -- foldynl Fri, 10 Nov 2023 08:50:15 +0100 qlog (0.29.0-1) UNRELEASED; urgency=low * [NEW] - Added user-defined layout for New QSO Detail widget * [NEW] - Main window State and Geometry can be saved to layout profile * [NEW] - Awards - Added WAS * [NEW] - Awards - WAZ/ITU/WAC show all possible values * [NEW] - Distance unit (km/miles) is controlled by OS Locale * [CHANGED] - Removed SAT Tab - field can be added via Layout Editor * Improved Import QSO performance * Fixed QLog crashes if POTA, SOTA or WWFF contain incorrect values (issue #245) * Fixed QSOs are not uploaded to QRZ and HRDLog if fields contain HTML delimiter strings (issue #247) -- foldynl Fri, 20 Oct 2023 15:58:36 +0200 qlog (0.28.0-1) UNRELEASED; urgency=low * [NEW] - Added ON4KST Chat Support * [NEW] - Added Az BeamWidth and Az Offset to Antenna Profile * [NEW] - Double-Clicking the IBP callsign in the online map tunes the frequency * Fixed Browse button should open an expecting folder (issue #241) * Fixed Reword QSL buttons and Settings in QSO Details and Settings (issue #242) -- foldynl Fri, 22 Sep 2023 16:26:02 +0200 qlog (0.27.0-1) UNRELEASED; urgency=low * [NEW] - Added HRDLog Support * Fixed Text field alignment (issue #233) * Fixed Rig/Rot Connection port type selection (issue #235) * Fixed Incorrect Distance Value in WSJTX Widget (issue #236) * Fixed Incorrect WSJTX locator target on the map (issue #237) -- foldynl Mon, 21 Aug 2023 19:32:02 +0200 qlog (0.26.0-1) UNRELEASED; urgency=low * [NEW] - Added user-defined layout for New QSO widget * [NEW] - Pressing Spacebar in Callsign field skips RST fields * [NEW] - Added user-defined URL for web lookup (issue #230) * Fixed WSJTX QSOs should have an Operator Name from Callbook (issue #223) * Fixed US call area suffixes not handled correctly (issue #226 thanks Florian) * Fixed QSO Filter Detail allows to save an empty Filter Name (issue #228) -- foldynl Sun, 30 Jul 2023 19:27:08 +0200 qlog (0.25.1-1) UNRELEASED; urgency=low * Fixed Unexpected mode change when Setting Dialog is saved (issue #222) * Fixed QSL_SENT field has an incorrect ADIF name (issue #225) -- foldynl Mon, 17 Jul 2023 13:49:01 +0200 qlog (0.25.0-1) UNRELEASED; urgency=low * [NEW] - Export - Added CSV Format * [NEW] - Export - Added Type of Export Generic/QSLs (issue #209) * [NEW] - Export - Added Exported Columns Setting * [NEW] - Export - All export formats use the ADIF field name convention * [CHANGED] - Export - JSON format contains a header - JSON format change * [CHANGED] - Default Statistics Interval is curr_date-1 and curr_day * Fixed Errors from Secure Storage are not shown (issue #216) * Fixed RX/TX Bands are uneditable when RX/TX freqs are missing (issue #220) -- foldynl Tue, 4 Jul 2023 15:05:16 +0200 qlog (0.24.0-1) UNRELEASED; urgency=low * Fixed Incorrect FT4 mode-submode (issue #212) * Fixed CONTESTIA mode should be CONTESTI (issue #213) * Fixed Context menu deselects NewContactEditLine (issue #215) * Fixed incorrect WSJTX Filter initialization (issue #218) -- foldynl Fri, 16 Jun 2023 12:20:54 +0200 qlog (0.23.0-1) UNRELEASED; urgency=low * [NEW] - Added CWDaemon Keyer Support * [NEW] - Added FLDigi Keyer Support * [NEW] - Online Map - based on locale, the map language is selected (Only EN, FR, GE supported - issue #180) * Fixed After entering longer QTH, the field content is not left-aligned (issue #157) * Fixed wrong QSO Time in case of JTDX (issue #204) * Fixed QSL Sent Date fields are not filled if QSL Sent Status fields are Y (issue #207) -- foldynl Fri, 9 Jun 2023 12:00:00 +0200 qlog (0.22.0-1) UNRELEASED; urgency=low * [NEW] - ADIF Import - My Profile is used to define default values * [NEW] - ADIF Import - Checking a minimal set of input fields (start_time, call, band, mode, station_callsign) * [NEW] - ADIF Import - Added Import Result Summary + Import Detail Info * [NEW] - Main Menu - Added Help -> Mailing List. * [NEW] - Export - Filter for the exported QSOs * [CHANGE] - Renamed Locator to Gridsquare * Fixed Some anomalies in the input and processing of QSLr Date (issue #192) * Fixed User unfriedly CW Keyer Error (issue #194) * Fixed ADIF import (issue #196) * Fixed Operator field is incorrectly used (issue #197) * Fixed Crash if an unknown POTA & SOTA/WWFF Setting is entered (issue #198) * Fixed FLDIGI cannot connect QLog (issue #199) * Fixed if ADIF record is missing band info, add this from freq field (thx DJ5CW) -- foldynl Sun, 7 May 2023 10:00:00 +0200 qlog (0.21.0-1) UNRELEASED; urgency=low * [NEW] - Rotator - Added Used-Defined Buttons * [NEW] - Rotator - Added Destination Azimuth Needle * [NEW] - Online Map - Added Antenna Beam Path * [NEW] - Rig - Combos are disbled when disconnected * [NEW] - Club Member Lists (issue #60) * [NEW] - Alert Table shows rule names * [CHANGED] - Alerts, DXC and WSJTX Network Notifications * Fixed Antenna Azimuth Negative Value (issue #191) * Fixed CTY file is not loaded when duplicate record (issue #193) -- foldynl Tue, 16 Apr 2023 10:00:00 +0200 qlog (0.20.0-1) UNRELEASED; urgency=low * [NEW] - Added MUF Layer to online map * [NEW] - Added International Beacon Project (IBP) Beacons to online map * [NEW] - Centering the map on the current profile at start (issue #185) * Fixed incorrect ADIF interpretation of _SENT fields (issue #176) * Fixed Awards Dialog, Table double click for ITU/CQZ/WAZ/IOTA shows incorrect QSOs (issue #177) * Fixed ADIF double-type fields when 0.0 is currently mapped to NULL (issue #178) * Fixed QSO Detail to save NULL instead of empty string (issue #179) * Fixed ADIF Import default _INTL values are now stored correctly (issue #183) * Fixed Maps show an incorrect path if from/to grids are the same (issue #186) * Fixed Online Maps incorrect Bounds if Bandmap callsign double-click (issue #188) * Updated German translation (thx DL2KI) -- foldynl Tue, 14 Mar 2023 20:00:00 +0100 qlog (0.19.0-1) UNRELEASED; urgency=low * [NEW] - Added Aurora Layer to online map * [NEW] - Logbook - filter options are saved and restored * [NEW] - Map Setting is saved and restored (issue #140) * [NEW] - QSO Duration (issue #158) * [NEW] - DX Cluster uses monospace font (issue #164) * [NEW] - Awards - if click on the Entity/band the logbook filter is set (issue #168) * [NEW] - WSJTX - Added Multicast support (issue #172) * Fixed WWFF LOV Download (issue #169) -- foldynl Fri, 17 Feb 2023 15:00:00 +0100 qlog (0.18.0-1) UNRELEASED; urgency=low * [NEW] - ADIF 3.1.4 updates * Added new modes FREEDV and M17 * Added new band (submm) * Adopted Altitude (for SOTA only) * Adopted POTA (includes POTA List) * Adopted Gridsquare_ext (only import/export) * Adopted Hamlogeu_* (only import/export) * Adopted HamQTH_* (only import/export) * [NEW] - Added new DXCC Status and color for it - Confirmed * [NEW] - New Contact - Tab selection is saved * [NEW] - Grid can contain 8-characters * [NEW] - User filter can contain NULL value * [NEW] - Compilation - added variables for external sources * [NEW] - My DXCC/CQZ/ITUZ/Country is filled * [NEW] - Alerts - Added Aging (issue #153) * [NEW] - Alerts - Added DXCC Status Color (issue #153) * [NEW] - DXC - Added Log Status to filter (issue #154) * [NEW] - DXC - Added Spot deduplication to filter (issue #154) * [NEW] - WSJTX - Added CQ-Spot Filter (issue #155) * [NEW] - QSO Detail contains DXCC Tab (issue #156) * [CHANGED] - New QSO DXCC Tab reworked (issue #144) * [CHANGED] - All DXCC Stats are computed based on My DXCC instead of My Callsign * [CHANGED] - Station Profile Setting layout -- foldynl Sun, 15 Jan 2023 15:00:00 +0100 qlog (0.17.0-1) UNRELEASED; urgency=low * [NEW] - NetPort and Polling interval can be defined for NET Rigs * [NEW] - NetPort can be defined for NET Rots * [NEW] - Added Saving Bandmap Zoom per band (issue #137) * [NEW] - CW speed synchronisation (issue #139) * Fixed Missing callbook data when callsign has prefix (issue #133) * Fixed Winkey2 echo chars are incorrectly displayed in CW Console (issue #141) * [CHANGED] - Online Map - Gray-Line is enabled by default * Update Timezone database -- foldynl Sun, 18 Dec 2022 10:00:00 +0100 qlog (0.16.0-1) UNRELEASED; urgency=low * [NEW] - SOTA/IOTA lists updated regularly * [NEW] - Added WWFF list, updated regularly * [NEW] - QTH/Grid are filled based on SOTA/WWFF * [NEW] - DXC/WSJTX columns are movable, added column visibility setting * [NEW] - DXC/WSJTX columns layout is saved * [NEW] - Added Wiki&Report Issue links to Help section * [NEW] - About dialog contains run-time information * [NEW] - Solar Info as a ToolTip * [NEW] - QSO Manual Entry Mode * Fixed Bandmap unlogical animation when band is changed (issue #128) * Fixed Bandmap marks are not displayed correctly when RIT/XI (issue #131) * Fixed Setting Dialog size * Update Timezone database -- foldynl Sun, 20 Nov 2022 10:00:00 +0200 qlog (0.15.0-1) UNRELEASED; urgency=low * Fixed Keeping the Bandmap RX mark always visible when centre RX is disabled (issue #115) * Fixed Equipment Menu: Swapped Connect Keyer and Rig (issue #122) * Fixed Callsign is deleted when clicking bandmap (issue #126) * Fixed typo in the Map layer menu (issue #127) * Fixed compilation issues & warning under QT6 - preparation for QT6 migration -- foldynl Sun, 16 Oct 2022 19:00:00 +0200 qlog (0.14.1-1) UNRELEASED; urgency=low * Fixed CW Console - HALT Button is not enabled under Ubuntu flavours (issue #124) -- foldynl Sun, 2 Oct 2022 10:00:00 +0200 qlog (0.14.0-1) UNRELEASED; urgency=low * [NEW] CW Console (Winkey2, Morse over CAT support) * [NEW] DX Cluster pre-defined commands (send last spot, get stats) * [NEW] Added DX Cluster Views (Spots, WCY, WWV, ToALL) * [NEW] Implemented DX Cluster Reconnection * [NEW] Remember last used DX Cluster * [CHANGED] - UI unifications - Rot/Rig/DXC * Fixed COM port validation for Windows platform * Fixed Reconnecting (DXC/Callbook) (issue #110) * Fixed DX Cluster crashes when DXC server is not connected and a command is sent (issue #111) * Fixed Bandmap callsign selection not fully works (issue #116) -- foldynl Thu, 29 Sep 2022 18:00:00 +0200 qlog (0.13.0-1) UNRELEASED; urgency=low * [NEW] QSY Contact Wiping (issue #100) * [NEW] Timeoff is highlighted when QSO timer is running (issue #100) * [NEW] Callsign whisperer * [NEW] Bandmap - Spot's color is recalculated when QSO is saved * [NEW] BandMap - CTRL + Wheel zooming * [NEW] BandMap - Zooming via buttons keeps a focus on centre freq * [NEW] BandMap - DX Spot's Comment as a tooltip * [CHANGED] BandMap - UI Layout * Fixed MacOS builds (PR #102) (thx gerbert) * Fixed templates under MacOS (PR #101) (thx gerbert) * Fixed WindowsOS Installer - Unable to upgrade version -- foldynl Sat, 6 Aug 2022 18:00:00 +0200 qlog (0.12.0-1) UNRELEASED; urgency=low * [NEW] Statistics - Show ODX on the map * [EXPERIMENTAL] Support for QT Styles (issue #88) * [CHANGED] - Removed F2 as a shortcut for QSO field editing * Next fixing of a high CPU load when DXC is processed (issue #52) * Fixed QSO fields from prev QSOs when Prefix - Callsign - Suffix (issue #90) * Fixed Chaotic QSO start time (issue #93) * Offline maps - Lighter colors, night sky removed, Sun position removed (issue #97) * Fixed incorrect A-Index colort (issue #98) * Fixed Stats Widget - percents - does not reflect date range (issue #99) * Fixed potential LogParam Cache issue * Import/Export polishing -- foldynl Fri, 15 Jul 2022 18:00:00 +0200 qlog (0.11.0-1) UNRELEASED; urgency=low * [NEW] QSO Detail/Edit Dialog * [NEW] Added mW power Support * [NEW] Implemented ADIF 3.1.3 * [NEW] Rigwidget saves last used freq for bands * Fixed Rig Combo size when Rig Profile name is long (issue #31) * Fixed CQZ, ITUZ do not validate whether their entered value is a number (issue #75) * Fixed vucc, myvucc must be uppercase - Edit mode (issue #76) * Fixed Greyline-Map is very dark (issue #78) * Fixed DX Country is not saved properly when name is between S-Z (issue #79) * Fixed Bandmap call selection - only left mouse button (issue #82) * Fixed My Notes Copy & Paste - Rich Text (issue #83) * Fixed Font appearance in the context menu (issue #84) -- foldynl Sun, 26 Jun 2022 8:00:00 +0200 qlog (0.10.0-1) UNRELEASED; urgency=low * [NEW] Bandmap shows XIT/RIT Freq * [NEW] Bandmap RX Mark Center (issue #69) * [NEW] Getting PTT State from RIG - only for CAT-controlled rigs * [NEW] PTT Shortchut - only for CAT-controlled rigs * Fixed Lost internet conneciton is not detected properly (issue #56) * Fixed Cannot manually edit QSO Date&Time (issue #66) * Fixed Field contents in capital letters (issue #67) * Fixed Band RX is not updated when RX Freq is edited (issue #72) * Fixed Stat Windget does not handle a date range correctly (issue #73) * Fixed eQSL card is incorreclty handled when a callsign contains special characters (issue #74) -- foldynl Sun, 5 Jun 2022 12:00:00 +0200 qlog (0.9.0-1) UNRELEASED; urgency=low * [NEW] User-defined Spot Alerts * [NEW] User filter contains a new operator "Starts with" * [NEW] a real local time is showed for the DX callsign (issue #45) * [NEW] Lotw/eQSL registration info is shown from callbooks * [NEW] Added shortcuts for menu and tabs * [NEW] Bandmap - Switching a band view via Bandmap context menu (issue #57) * [CHANGED] - Network Notification format * Fixed issue with My Notes multiple lines edit/show mode (issue 39) * Fixed issue when GUI froze when Rig disconnect was called (issue #50) * Partially fixed a high CPU load when DXC is processed (issue #52) * Fixed crashes under Debian "bullseye" - 32bit (issue #55) * Fixed Bandmap Callsign selection margin (issue #61) * Fixed issue when it was not possible to enter RX/TX freq manually -- foldynl Fri, 20 May 2022 08:00:00 +0200 qlog (0.8.0-1) UNRELEASED; urgency=low * RIT/XIT offset enable/disable detection (issue #26) * Fixed Rig Setting, Data Bits (issue #28) * Added default PWR for Rig profile (issue #30) * Fixed issue when GUI freezes during Rig connection (issue #32 & #33) * Fixed issue with an incorrect value of A-Index (issue #34) * Fixed ADI Import - incorrect _INTL fields import (issue #35) * Fixed isuue with an editing of bands in Setting dialog (#issue 36) * Fixed issue with hamlib when get_pwr crashes for a network rig (issue #37) * Improved new QSO fields are filled from prev QSO (issue #40) * Added mode for a network Rig (issue #41) * Fixed warning - processing a new request but the previous one hasn't been completed (issue #42) * Fixed Info widget when Country name is long (issue #43) * Reordered column visibility Tabs (issue #46) * Improved Rig tunning when XIT/RIT is enabled (issue #47) -- foldynl Fri, 22 Apr 2022 08:00:00 +0200 qlog (0.7.0-1) UNRELEASED; urgency=low * [NEW] - Ant/Rig/Rot Profiles * [NEW] - Rig widget shows additional information * [NEW] - Rig widget Band/Mode/Profile Changer * [NEW] - Rot profile Changer * [NEW] - AZ/EL are stored when Rot is connected * Fixed an issue with Statistic widget (issue #25) * Fixed Rot AZ current value (issue #22) -- foldynl Fri, 8 Apr 2022 12:00:00 +0200 qlog (0.6.5-1) UNRELEASED; urgency=low * Fixed missing modes in Setting Dialog (issue #11) * Fixed Station Profile text color in dark mode (issue #10) * Fixed DXCluster Server Combo (issue #12) * Fixed TAB focus on QSO Fields (issue #14) -- foldynl Thu, 10 Mar 2022 20:35:00 +0200 qlog (0.6.0-1) UNRELEASED; urgency=low * [NEW] QSL - added import a file with QSL - QSLr column * Fixed QLog start when Band is 3cm (too long start time due to the Bandmap drawing) (issue #6) * Fixed Rotator Widget Warning - map transformation issue (issue #8) * Changed Bandmap window narrow size (issue #3) * Changed User Filter Widget size * Removed Units from Logbook widget * Removed UTC string * Renamed RSTs, RSTr etc. (issue #4) * Renamed Main Menu Services->Service and Station->Equipment * Internal - reworked Service networking signal handling -- foldynl Sun, 5 Mar 2022 08:00:00 +0200 qlog (0.5.0-1) UNRELEASED; urgency=low * DB: Started to use *_INTL fields * DB: Added all ADIF-supported modes/submodes * GUI: Dark Mode * GUI: TIme format controlled by Locale * Import/Export: ADI do not export UTF-8 characters and *_INTL fields * Import/Export: ADX exports UTF-8 characters and *_INTL fields * Import/Export: Added Import of ADX file format * Logbook: Shows QSO summary as a Callsign's tooltip * Logbook: QSO time is shown with seconds; added timezone * New QSO: Added My notes - free text for your personal notes * Backup: Change backup format form ADI to ADX (ADX supports UTF-8) * Settings: WSJTX Port is changable -- foldynl Sat, 19 Feb 2022 12:00:00 +0200 log (0.4.0-1) UNRELEASED; urgency=low * Stats: Added Show on Map - QSOs and Worked&Confirmed Grids * Stats: Stats are refreshed after every QSO * WSJTX: Remove TRX/Monitoring Status * Added Split mode - RX/TX RIG Offset * Added export of selected QSOs * Fixed FLdigi interface * CPPChecks & Clazy cleanup -- foldynl Sun, 9 Jan 2022 12:00:00 +0200 qlog (0.3.0-1) UNRELEASED; urgency=low * Rework Station Profile - stored in DB, new fields * Added VUCC fields support * Added BandMap marks (CTRL+M) * Clublog is uploaded the same way as EQSL and LOTW (modified QSO are resent) * Clublog real-time upload is temporary disabled * Added QRZ suppor - upload QSO and Callsign query * Callbook cooperation - Primary&Secondary - Secondary used when Primary did not find -- foldynl Sun, 19 Dec 2021 12:00:00 +0200 qlog (0.2.0-1) UNRELEASED; urgency=low * Fork changes * Many GUI Changes * Added Online Map * Secure Storage for passwords * Improved Logging * CTY and SAT Name auto-update * Added Station Profile * Reworked Stats * Added Awards * Added Czech Translation * many other changes -- foldynl Sat, 27 Nov 2021 19:07:01 +0200 qlog (0.1.0-1) UNRELEASED; urgency=low * Initial release - non-public -- foldynl Thu, 26 Aug 2021 19:50:55 +0200 ================================================ FILE: debian/control ================================================ Source: qlog Section: hamradio Priority: optional Maintainer: Ladislav Foldyna Uploaders: Ladislav Foldyna Vcs-Browser: https://github.com/foldynl/QLog Vcs-Git: https://github.com/foldynl/QLog.git Build-Depends: debhelper (>= 10.0.0), debhelper-compat (= 13), qtbase5-dev, libsqlite3-dev, libhamlib-dev, libqt5charts5-dev, qtchooser, qttools5-dev-tools, libqt5keychain1, qt5keychain-dev, qtwebengine5-dev, libqt5serialport5-dev, pkg-config, libqt5websockets5-dev, zlib1g-dev, libssl-dev Standards-Version: 4.5.0 Package: qlog Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, gnome-keyring | kwalletmanager Description: Qt Logging program for hamradio operators QLog is an Amateur Radio logging application for Linux, Windows and Mac OS. It is based on the Qt 5 framework and uses SQLite as database backend. ================================================ FILE: debian/copyright ================================================ Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: qlog Upstream-Contact: Ladislav Foldyna Files: * Copyright: 2020, Thomas Gatzweiler 2021-2026, Ladislav Foldyna 2025, Michael Morgan AA5SH 2025, Kyle Boyle 2018-2019, Bertold Van den Bergh 2026, Stephane Fillod License: GPL-3+ Files: debian/* Copyright: 2021-2026, Ladislav Foldyna 2025-2026, Dawid Kulas License: GPL-3+ The package for qlog was put together first by Ladislav Foldyna Files: res/io.github.foldynl.QLog.metainfo.xml Copyright: 2023-2026, Ladislav Foldyna 2023-2025, AsciiWolf License: CC0-1.0 Files: core/zonedetect.* Copyright: 2018, Bertold Van den Bergh 2022-2023, Ladislav Foldyna License: BSD-3-Clause Files: devtools/timezones/* Copyright: 2018, Bertold Van den Bergh 2022-2024, Ladislav Foldyna License: ODbL-1.0 Files: ui/component/SwitchButton.h ui/component/SwitchButton.cpp ui/component/ButtonStyle.h Copyright: 2018-2020, Iman Ahmadvand 2025, Ladislav Foldyna License: GPL-3+ Files: core/csv.hpp Copyright: 2017-2024 Vincent Lau 2017-2019 Martin Moene (string-view lite, embedded) License: Expat Files: devtools/timezones/builder/builder.cpp Copyright: 2018, Bertold Van den Bergh 2022-2024, Ladislav Foldyna License: BSD-3-Clause License: GPL-3+ This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. . This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. . You should have received a copy of the GNU General Public License along with this program. If not, see . . Comment: On Debian systems, the full text of the GNU General Public License version 3 can be found in the file '/usr/share/common-licenses/GPL-3'. License: CC0-1.0 To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. This software is distributed without any warranty. . You should have received a copy of the CC0 Public Domain Dedication along with this software. If not, see . Comment: On Debian systems, the full text of the CC0 1.0 Universal license can be found in the file '/usr/share/common-licenses/CC0-1.0'. License: BSD-3-Clause Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: . * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. . * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. . * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. . THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. License: ODbL-1.0 ### Preamble . The Open Database License (ODbL) is a license agreement intended to allow users to freely share, modify, and use this Database while maintaining this same freedom for others. Many databases are covered by copyright, and therefore this document licenses these rights. Some jurisdictions, mainly in the European Union, have specific rights that cover databases, and so the ODbL addresses these rights, too. Finally, the ODbL is also an agreement in contract for users of this Database to act in certain ways in return for accessing this Database. . Databases can contain a wide variety of types of content (images, audiovisual material, and sounds all in the same database, for example), and so the ODbL only governs the rights over the Database, and not the contents of the Database individually. Licensors should use the ODbL together with another license for the contents, if the contents have a single set of rights that uniformly covers all of the contents. If the contents have multiple sets of different rights, Licensors should describe what rights govern what contents together in the individual record or in some other way that clarifies what rights apply. . Sometimes the contents of a database, or the database itself, can be covered by other rights not addressed here (such as private contracts, trade mark over the name, or privacy rights / data protection rights over information in the contents), and so you are advised that you may have to consult other documents or clear other rights before doing activities not covered by this License. . ------ . The Licensor (as defined below) . and . You (as defined below) . agree as follows: . ### 1.0 Definitions of Capitalised Words . "Collective Database" - Means this Database in unmodified form as part of a collection of independent databases in themselves that together are assembled into a collective whole. A work that constitutes a Collective Database will not be considered a Derivative Database. . "Convey" - As a verb, means Using the Database, a Derivative Database, or the Database as part of a Collective Database in any way that enables a Person to make or receive copies of the Database or a Derivative Database. Conveying does not include interaction with a user through a computer network, or creating and Using a Produced Work, where no transfer of a copy of the Database or a Derivative Database occurs. "Contents" - The contents of this Database, which includes the information, independent works, or other material collected into the Database. For example, the contents of the Database could be factual data or works such as images, audiovisual material, text, or sounds. . "Database" - A collection of material (the Contents) arranged in a systematic or methodical way and individually accessible by electronic or other means offered under the terms of this License. . "Database Directive" - Means Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended or succeeded. . "Database Right" - Means rights resulting from the Chapter III ("sui generis") rights in the Database Directive (as amended and as transposed by member states), which includes the Extraction and Re-utilisation of the whole or a Substantial part of the Contents, as well as any similar rights available in the relevant jurisdiction under Section 10.4. . "Derivative Database" - Means a database based upon the Database, and includes any translation, adaptation, arrangement, modification, or any other alteration of the Database or of a Substantial part of the Contents. This includes, but is not limited to, Extracting or Re-utilising the whole or a Substantial part of the Contents in a new Database. . "Extraction" - Means the permanent or temporary transfer of all or a Substantial part of the Contents to another medium by any means or in any form. . "License" - Means this license agreement and is both a license of rights such as copyright and Database Rights and an agreement in contract. . "Licensor" - Means the Person that offers the Database under the terms of this License. . "Person" - Means a natural or legal person or a body of persons corporate or incorporate. . "Produced Work" - a work (such as an image, audiovisual material, text, or sounds) resulting from using the whole or a Substantial part of the Contents (via a search or other query) from this Database, a Derivative Database, or this Database as part of a Collective Database. . "Publicly" - means to Persons other than You or under Your control by either more than 50% ownership or by the power to direct their activities (such as contracting with an independent consultant). . "Re-utilisation" - means any form of making available to the public all or a Substantial part of the Contents by the distribution of copies, by renting, by online or other forms of transmission. . "Substantial" - Means substantial in terms of quantity or quality or a combination of both. The repeated and systematic Extraction or Re-utilisation of insubstantial parts of the Contents may amount to the Extraction or Re-utilisation of a Substantial part of the Contents. . "Use" - As a verb, means doing any act that is restricted by copyright or Database Rights whether in the original medium or any other; and includes without limitation distributing, copying, publicly performing, publicly displaying, and preparing derivative works of the Database, as well as modifying the Database as may be technically necessary to use it in a different mode or format. . "You" - Means a Person exercising rights under this License who has not previously violated the terms of this License with respect to the Database, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation. . Words in the singular include the plural and vice versa. . ### 2.0 What this License covers . 2.1. Legal effect of this document. This License is: . a. A license of applicable copyright and neighbouring rights; . b. A license of the Database Right; and . c. An agreement in contract between You and the Licensor. . 2.2 Legal rights covered. This License covers the legal rights in the Database, including: . a. Copyright. Any copyright or neighbouring rights in the Database. The copyright licensed includes any individual elements of the Database, but does not cover the copyright over the Contents independent of this Database. See Section 2.4 for details. Copyright law varies between jurisdictions, but is likely to cover: the Database model or schema, which is the structure, arrangement, and organisation of the Database, and can also include the Database tables and table indexes; the data entry and output sheets; and the Field names of Contents stored in the Database; . b. Database Rights. Database Rights only extend to the Extraction and Re-utilisation of the whole or a Substantial part of the Contents. Database Rights can apply even when there is no copyright over the Database. Database Rights can also apply when the Contents are removed from the Database and are selected and arranged in a way that would not infringe any applicable copyright; and . c. Contract. This is an agreement between You and the Licensor for access to the Database. In return you agree to certain conditions of use on this access as outlined in this License. . 2.3 Rights not covered. . a. This License does not apply to computer programs used in the making or operation of the Database; . b. This License does not cover any patents over the Contents or the Database; and . c. This License does not cover any trademarks associated with the Database. . 2.4 Relationship to Contents in the Database. The individual items of the Contents contained in this Database may be covered by other rights, including copyright, patent, data protection, privacy, or personality rights, and this License does not cover any rights (other than Database Rights or in contract) in individual Contents contained in the Database. For example, if used on a Database of images (the Contents), this License would not apply to copyright over individual images, which could have their own separate licenses, or one single license covering all of the rights over the images. . ### 3.0 Rights granted . 3.1 Subject to the terms and conditions of this License, the Licensor grants to You a worldwide, royalty-free, non-exclusive, terminable (but only under Section 9) license to Use the Database for the duration of any applicable copyright and Database Rights. These rights explicitly include commercial use, and do not exclude any field of endeavour. To the extent possible in the relevant jurisdiction, these rights may be exercised in all media and formats whether now known or created in the future. . The rights granted cover, for example: . a. Extraction and Re-utilisation of the whole or a Substantial part of the Contents; . b. Creation of Derivative Databases; . c. Creation of Collective Databases; . d. Creation of temporary or permanent reproductions by any means and in any form, in whole or in part, including of any Derivative Databases or as a part of Collective Databases; and . e. Distribution, communication, display, lending, making available, or performance to the public by any means and in any form, in whole or in part, including of any Derivative Database or as a part of Collective Databases. . 3.2 Compulsory license schemes. For the avoidance of doubt: . a. Non-waivable compulsory license schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; . b. Waivable compulsory license schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor waives the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; and, . c. Voluntary license schemes. The Licensor waives the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License. . 3.3 The right to release the Database under different terms, or to stop distributing or making available the Database, is reserved. Note that this Database may be multiple-licensed, and so You may have the choice of using alternative licenses for this Database. Subject to Section 10.4, all other rights not expressly granted by Licensor are reserved. . ### 4.0 Conditions of Use . 4.1 The rights granted in Section 3 above are expressly made subject to Your complying with the following conditions of use. These are important conditions of this License, and if You fail to follow them, You will be in material breach of its terms. . 4.2 Notices. If You Publicly Convey this Database, any Derivative Database, or the Database as part of a Collective Database, then You must: . a. Do so only under the terms of this License or another license permitted under Section 4.4; . b. Include a copy of this License (or, as applicable, a license permitted under Section 4.4) or its Uniform Resource Identifier (URI) with the Database or Derivative Database, including both in the Database or Derivative Database and in any relevant documentation; and . c. Keep intact any copyright or Database Right notices and notices that refer to this License. . d. If it is not possible to put the required notices in a particular file due to its structure, then You must include the notices in a location (such as a relevant directory) where users would be likely to look for it. . 4.3 Notice for using output (Contents). Creating and Using a Produced Work does not require the notice in Section 4.2. However, if you Publicly Use a Produced Work, You must include a notice associated with the Produced Work reasonably calculated to make any Person that uses, views, accesses, interacts with, or is otherwise exposed to the Produced Work aware that Content was obtained from the Database, Derivative Database, or the Database as part of a Collective Database, and that it is available under this License. . a. Example notice. The following text will satisfy notice under Section 4.3: . Contains information from DATABASE NAME, which is made available here under the Open Database License (ODbL). . DATABASE NAME should be replaced with the name of the Database and a hyperlink to the URI of the Database. "Open Database License" should contain a hyperlink to the URI of the text of this License. If hyperlinks are not possible, You should include the plain text of the required URI's with the above notice. . 4.4 Share alike. . a. Any Derivative Database that You Publicly Use must be only under the terms of: . i. This License; . ii. A later version of this License similar in spirit to this License; or . iii. A compatible license. . If You license the Derivative Database under one of the licenses mentioned in (iii), You must comply with the terms of that license. . b. For the avoidance of doubt, Extraction or Re-utilisation of the whole or a Substantial part of the Contents into a new database is a Derivative Database and must comply with Section 4.4. . c. Derivative Databases and Produced Works. A Derivative Database is Publicly Used and so must comply with Section 4.4. if a Produced Work created from the Derivative Database is Publicly Used. . d. Share Alike and additional Contents. For the avoidance of doubt, You must not add Contents to Derivative Databases under Section 4.4 a that are incompatible with the rights granted under this License. . e. Compatible licenses. Licensors may authorise a proxy to determine compatible licenses under Section 4.4 a iii. If they do so, the authorised proxy's public statement of acceptance of a compatible license grants You permission to use the compatible license. . . 4.5 Limits of Share Alike. The requirements of Section 4.4 do not apply in the following: . a. For the avoidance of doubt, You are not required to license Collective Databases under this License if You incorporate this Database or a Derivative Database in the collection, but this License still applies to this Database or a Derivative Database as a part of the Collective Database; . b. Using this Database, a Derivative Database, or this Database as part of a Collective Database to create a Produced Work does not create a Derivative Database for purposes of Section 4.4; and . c. Use of a Derivative Database internally within an organisation is not to the public and therefore does not fall under the requirements of Section 4.4. . 4.6 Access to Derivative Databases. If You Publicly Use a Derivative Database or a Produced Work from a Derivative Database, You must also offer to recipients of the Derivative Database or Produced Work a copy in a machine readable form of: . a. The entire Derivative Database; or . b. A file containing all of the alterations made to the Database or the method of making the alterations to the Database (such as an algorithm), including any additional Contents, that make up all the differences between the Database and the Derivative Database. . The Derivative Database (under a.) or alteration file (under b.) must be available at no more than a reasonable production cost for physical distributions and free of charge if distributed over the internet. . 4.7 Technological measures and additional terms . a. This License does not allow You to impose (except subject to Section 4.7 b.) any terms or any technological measures on the Database, a Derivative Database, or the whole or a Substantial part of the Contents that alter or restrict the terms of this License, or any rights granted under it, or have the effect or intent of restricting the ability of any person to exercise those rights. . b. Parallel distribution. You may impose terms or technological measures on the Database, a Derivative Database, or the whole or a Substantial part of the Contents (a "Restricted Database") in contravention of Section 4.74 a. only if You also make a copy of the Database or a Derivative Database available to the recipient of the Restricted Database: . i. That is available without additional fee; . ii. That is available in a medium that does not alter or restrict the terms of this License, or any rights granted under it, or have the effect or intent of restricting the ability of any person to exercise those rights (an "Unrestricted Database"); and . iii. The Unrestricted Database is at least as accessible to the recipient as a practical matter as the Restricted Database. . c. For the avoidance of doubt, You may place this Database or a Derivative Database in an authenticated environment, behind a password, or within a similar access control scheme provided that You do not alter or restrict the terms of this License or any rights granted under it or have the effect or intent of restricting the ability of any person to exercise those rights. . 4.8 Licensing of others. You may not sublicense the Database. Each time You communicate the Database, the whole or Substantial part of the Contents, or any Derivative Database to anyone else in any way, the Licensor offers to the recipient a license to the Database on the same terms and conditions as this License. You are not responsible for enforcing compliance by third parties with this License, but You may enforce any rights that You have over a Derivative Database. You are solely responsible for any modifications of a Derivative Database made by You or another Person at Your direction. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. . ### 5.0 Moral rights . 5.1 Moral rights. This section covers moral rights, including any rights to be identified as the author of the Database or to object to treatment that would otherwise prejudice the author's honour and reputation, or any other derogatory treatment: . a. For jurisdictions allowing waiver of moral rights, Licensor waives all moral rights that Licensor may have in the Database to the fullest extent possible by the law of the relevant jurisdiction under Section 10.4; . b. If waiver of moral rights under Section 5.1 a in the relevant jurisdiction is not possible, Licensor agrees not to assert any moral rights over the Database and waives all claims in moral rights to the fullest extent possible by the law of the relevant jurisdiction under Section 10.4; and . c. For jurisdictions not allowing waiver or an agreement not to assert moral rights under Section 5.1 a and b, the author may retain their moral rights over certain aspects of the Database. . Please note that some jurisdictions do not allow for the waiver of moral rights, and so moral rights may still subsist over the Database in some jurisdictions. . ### 6.0 Fair dealing, Database exceptions, and other rights not affected . 6.1 This License does not affect any rights that You or anyone else may independently have under any applicable law to make any use of this Database, including without limitation: . a. Exceptions to the Database Right including: Extraction of Contents from non-electronic Databases for private purposes, Extraction for purposes of illustration for teaching or scientific research, and Extraction or Re-utilisation for public security or an administrative or judicial procedure. . b. Fair dealing, fair use, or any other legally recognised limitation or exception to infringement of copyright or other applicable laws. . 6.2 This License does not affect any rights of lawful users to Extract and Re-utilise insubstantial parts of the Contents, evaluated quantitatively or qualitatively, for any purposes whatsoever, including creating a Derivative Database (subject to other rights over the Contents, see Section 2.4). The repeated and systematic Extraction or Re-utilisation of insubstantial parts of the Contents may however amount to the Extraction or Re-utilisation of a Substantial part of the Contents. . ### 7.0 Warranties and Disclaimer . 7.1 The Database is licensed by the Licensor "as is" and without any warranty of any kind, either express, implied, or arising by statute, custom, course of dealing, or trade usage. Licensor specifically disclaims any and all implied warranties or conditions of title, non-infringement, accuracy or completeness, the presence or absence of errors, fitness for a particular purpose, merchantability, or otherwise. Some jurisdictions do not allow the exclusion of implied warranties, so this exclusion may not apply to You. . ### 8.0 Limitation of liability . 8.1 Subject to any liability that may not be excluded or limited by law, the Licensor is not liable for, and expressly excludes, all liability for loss or damage however and whenever caused to anyone by any use under this License, whether by You or by anyone else, and whether caused by any fault on the part of the Licensor or not. This exclusion of liability includes, but is not limited to, any special, incidental, consequential, punitive, or exemplary damages such as loss of revenue, data, anticipated profits, and lost business. This exclusion applies even if the Licensor has been advised of the possibility of such damages. . 8.2 If liability may not be excluded by law, it is limited to actual and direct financial loss to the extent it is caused by proved negligence on the part of the Licensor. . ### 9.0 Termination of Your rights under this License . 9.1 Any breach by You of the terms and conditions of this License automatically terminates this License with immediate effect and without notice to You. For the avoidance of doubt, Persons who have received the Database, the whole or a Substantial part of the Contents, Derivative Databases, or the Database as part of a Collective Database from You under this License will not have their licenses terminated provided their use is in full compliance with this License or a license granted under Section 4.8 of this License. Sections 1, 2, 7, 8, 9 and 10 will survive any termination of this License. . 9.2 If You are not in breach of the terms of this License, the Licensor will not terminate Your rights under it. . 9.3 Unless terminated under Section 9.1, this License is granted to You for the duration of applicable rights in the Database. . 9.4 Reinstatement of rights. If you cease any breach of the terms and conditions of this License, then your full rights under this License will be reinstated: . a. Provisionally and subject to permanent termination until the 60th day after cessation of breach; . b. Permanently on the 60th day after cessation of breach unless otherwise reasonably notified by the Licensor; or . c. Permanently if reasonably notified by the Licensor of the violation, this is the first time You have received notice of violation of this License from the Licensor, and You cure the violation prior to 30 days after your receipt of the notice. . Persons subject to permanent termination of rights are not eligible to be a recipient and receive a license under Section 4.8. . 9.5 Notwithstanding the above, Licensor reserves the right to release the Database under different license terms or to stop distributing or making available the Database. Releasing the Database under different license terms or stopping the distribution of the Database will not withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above. . ### 10.0 General . 10.1 If any provision of this License is held to be invalid or unenforceable, that must not affect the validity or enforceability of the remainder of the terms and conditions of this License and each remaining provision of this License shall be valid and enforced to the fullest extent permitted by law. . 10.2 This License is the entire agreement between the parties with respect to the rights granted here over the Database. It replaces any earlier understandings, agreements or representations with respect to the Database. . 10.3 If You are in breach of the terms of this License, You will not be entitled to rely on the terms of this License or to complain of any breach by the Licensor. . 10.4 Choice of law. This License takes effect in and will be governed by the laws of the relevant jurisdiction in which the License terms are sought to be enforced. If the standard suite of rights granted under applicable copyright law and Database Rights in the relevant jurisdiction includes additional rights not granted under this License, these additional rights are granted in this License in order to meet the terms of this License. License: Expat Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: . The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. . THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: debian/rules ================================================ #!/usr/bin/make -f %: dh $@ --parallel ================================================ FILE: debian/source/format ================================================ 3.0 (quilt) ================================================ FILE: debian/watch ================================================ Version: 5 Template: Github Owner: foldynl Project: QLog #Release-Only: yes ================================================ FILE: devtools/ADIF/all_ADIF_fields_example.adi ================================================ ### QLog ADIF Export 3.1.4 Gene 0.20.0 20230324 081527 123 Main St. 35 1900 10 45 20 S AL WAS WAS 20M 40M N1MM 599 A 20220101 Y New Haven Nice signal! SA N1CON NAQP-SSB United States 5 DXCC DXCC HX 500 291 john.doe@example.com K1ABC 20220101 20220101 Y Y 1234 5678 Y 14200 7050 FN31pr FN31 K2XYZ 20230101 Y 20230101 Y 20220101 Y EU-001 001 8 3 N041 30.000 N041 30.000 20220101 20220101 Y Y 3 SSB GEM 1900 GP AL New Haven JA Gun United States 5 291 1234 FN31pr AA EU-001 001 8 N041 30.000 N041 30.000 John Doe 06511 K-5033 Icom IC-7300 SIG INFO SOTA Connecticut Main St. CT-009 FN80 WWFF Jane Smith Had a great QSO! 5 10 K1ABC K1ABC W1AW K-5033 A SAT 20220101 Y Tnx QSO! 20220101 20220101 Y D Y B BURO Y 20220101 20220101 Y New Haven AI Icom IC-7300 59 59 100 FM SO-50 70 59 USB N 1234 W1/HA-001 7050 7.050 Connecticut K1ABC 599 5.99 LSB Y 12345 1200 1100 100 1234 CT-009 ON FN31 http://all.com WWFF-OK11 ALT 20240101 20240202 I Y TEST CPU ALT DOK MY TEST DP 20240303 Y QSLR MSG U ================================================ FILE: devtools/ADIF/min_ADIF_fields_example.adi ================================================ ### QLog ADIF Export 3.1.4 QLog 0.20.0 20230324 081527 SO1TEST 20230101 132300 40m CW ================================================ FILE: devtools/ADIF/test_intl.adi ================================================ ### QLog ADIF Export 3.1.4 QLog 0.20.0 20230324 081527 SO1TEST 20230501 132300 40m CW com Rig from GP GP_INTL SO1TESTT 20230501 132300 40m CW com com_intl SO1TESTS 20230501 132300 40m CW ================================================ FILE: devtools/TCISimulator/client.html ================================================ WebSocket Test

================================================ FILE: devtools/TCISimulator/server.py ================================================ #!/usr/bin/env python3 """ TCI WebSocket simulator This script implements a minimal TCI-like WebSocket server that accepts multiple client connections and broadcasts status updates to all connected clients. How it works - Starts a WebSocket server on TCP port 8000. - When a client connects, the server immediately sends a set of capability/status messages (limits, device info, supported modulations) and a READY message. - Incoming messages are parsed as simple "COMMAND:arg1,arg2,..." frames and mapped to handlers. Supported commands include: VFO, TRX, MODULATION, DRIVE, RIT_OFFSET, RIT_ENABLE, XIT_OFFSET, XIT_ENABLE, and CW_MACROS_SPEED. - The server keeps an internal state (frequency, TX/RX, modulation, drive, RIT/XIT settings, CW speed). When a command updates the state, the server broadcasts the corresponding response/status frame to all connected clients. Notes - This is a simulator: it does not control real radio hardware; it only maintains and publishes state. - Each connection creates its own SimpleTCI state instance; broadcasting still goes to all clients. - Messages are terminated with ';' when sent to clients. """ import asyncio import websockets clients = set() class SimpleTCI: def __init__(self): self.currFreq = 14000000 self.currTrx = False self.currModulation = "USB" self.currDrive = 20 self.currRITOffset = 0 self.currRITEnable = False self.currCWSpeed = 20 self.currXITOffset = 0 self.currXITEnable = False async def sendTCIMessage(self, msg: str): print(f"Sending msg: {msg}") await asyncio.sleep(0.1) dead = set() for ws in clients: try: await ws.send(msg + ";") except Exception: dead.add(ws) for ws in dead: clients.discard(ws) async def VFOCommand(self, args, getNum): if len(args) > getNum: self.currFreq = args[2] await self.sendTCIMessage(f"vfo:0,0,{self.currFreq}") async def TRXCommand(self, args, getNum): if len(args) > getNum: self.currTrx = (args[1].lower() == "true") await self.sendTCIMessage(f"trx:0,{self.currTrx}") async def MODULATIONCommand(self, args, getNum): if len(args) > getNum: self.currModulation = args[1].upper() await self.sendTCIMessage(f"modulation:0,{self.currModulation}") async def DRIVECommand(self, args, getNum): if len(args) > getNum: self.currDrive = args[1] await self.sendTCIMessage(f"drive:0,{self.currDrive}") async def RITOFFSETCommand(self, args, getNum): if len(args) > getNum: self.currRITOffset = args[1] await self.sendTCIMessage(f"rit_offset:0,{self.currRITOffset}") async def RITENABLECommand(self, args, getNum): if len(args) > getNum: self.currRITEnable = (args[1].lower() == "true") await self.sendTCIMessage(f"rit_enable:0,{self.currRITEnable}") async def XITOFFSETCommand(self, args, getNum): if len(args) > getNum: self.currXITOffset = args[1] await self.sendTCIMessage(f"xit_offset:0,{self.currXITOffset}") async def XITENABLECommand(self, args, getNum): if len(args) > getNum: self.currXITEnable = (args[1].lower() == "true") await self.sendTCIMessage(f"xit_enable:0,{self.currXITEnable}") async def CWMACROSSPEEDCommand(self, args, getNum): if len(args) > getNum: self.currCWSpeed = args[0] await self.sendTCIMessage(f"cw_macros_speed:0,{self.currCWSpeed}") def _processCommand(self, argString, getArgsNum): stripArgs = argString.replace(";", "") args = stripArgs.split(",") if len(args) < getArgsNum: return None return args async def handleMessage(self, data: str): commandString = data.split(":") print(f"Received: {commandString}") if len(commandString) < 2: return command = commandString[0].upper() payload = commandString[1] if command == "VFO": args = self._processCommand(payload, 2) if args: await self.VFOCommand(args, 2) elif command == "TRX": args = self._processCommand(payload, 1) if args: await self.TRXCommand(args, 1) elif command == "MODULATION": args = self._processCommand(payload, 1) if args: await self.MODULATIONCommand(args, 1) elif command == "DRIVE": args = self._processCommand(payload, 1) if args: await self.DRIVECommand(args, 1) elif command == "RIT_OFFSET": args = self._processCommand(payload, 1) if args: await self.RITOFFSETCommand(args, 1) elif command == "RIT_ENABLE": args = self._processCommand(payload, 1) if args: await self.RITENABLECommand(args, 1) elif command == "XIT_OFFSET": args = self._processCommand(payload, 1) if args: await self.XITOFFSETCommand(args, 1) elif command == "XIT_ENABLE": args = self._processCommand(payload, 1) if args: await self.XITENABLECommand(args, 1) elif command == "CW_MACROS_SPEED": args = self._processCommand(payload, 0) if args is not None: await self.CWMACROSSPEEDCommand(args, 0) async def handler(websocket, path=None): clients.add(websocket) tci = SimpleTCI() print(websocket.remote_address, "connected") await tci.sendTCIMessage("VFO_LIMITS:10000,30000000;TRX_COUNT:2") await tci.sendTCIMessage("DEVICE:SunSDR2DX;MODULATIONS_LIST:AM,SAM,LSB,USB,CW,NFM,WFM;PROTOCOL:ExpertSDR3,1.9") await tci.sendTCIMessage("VFO:0,0,14000000;TRX:0,false;MODULATION:0,USB;RIT_OFFSET:0,-50;RIT_ENABLE:0,false") await tci.sendTCIMessage("READY") try: async for message in websocket: await tci.handleMessage(message) finally: clients.discard(websocket) print(websocket.remote_address, "closed") async def main(): host = "0.0.0.0" # nebo "127.0.0.1" jen pro localhost port = 8000 print(f"TCI Server Simulator listening on ws://{host}:{port}") async with websockets.serve(handler, host, port): await asyncio.Future() # run forever if __name__ == "__main__": asyncio.run(main()) ================================================ FILE: devtools/cabrillo/generate_cabrillo_templates.py ================================================ #!/usr/bin/env python3 """ Generate Cabrillo template SQL for QLog from formats.dat. Source: https://codeberg.org/kq4mhe/adif2cabrillo/raw/branch/main/resources/formats.dat Usage: python3 generate_cabrillo_templates.py -- diff since last run python3 generate_cabrillo_templates.py --full -- all templates Downloads formats.dat automatically. SQL goes to stdout. State is kept in formats.dat.state next to this script. Field mapping: All field-name mappings live in FIELD_MAP below. Format: "name": (sent_db, rcvd_db, formatter [, sent_label, rcvd_label]) "name": None -- skip this field Examples: "freq": ("freq", "freq", "freq_khz"), -- common field "call": ("station_callsign", "callsign", "upper", -- with explicit labels "My Call", "Call Rcvd"), "exch": ("stx_string", "srx_string", ""), -- exchange field "t": None, -- ignored Fields not listed produce "-- REVIEW" in the SQL output. To fix a REVIEW, just add the field name to FIELD_MAP. """ import os import re import sys import urllib.request SOURCE_URL = ("https://codeberg.org/kq4mhe/adif2cabrillo" "/raw/branch/main/resources/formats.dat") SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) DAT_PATH = os.path.join(SCRIPT_DIR, "formats.dat") STATE_PATH = os.path.join(SCRIPT_DIR, "formats.dat.state") # --------------------------------------------------------------------------- # Built-in contest IDs — only these are emitted as builtin templates. # Top 10 most popular worldwide contests by participation. # --------------------------------------------------------------------------- BUILTIN_CONTEST_IDS = { "CQ-WW-CW", "CQ-WW-SSB", "CQ-WPX-CW", "CQ-WPX-SSB", "CQ-WW-RTTY", "ARRL-DX-CW", "ARRL-DX-SSB", "IARU-HF", "ARRL-VHF-JAN", "CQ-VHF", } # --------------------------------------------------------------------------- # Field mapping: "name" -> (sent_db, rcvd_db, formatter [, sent_lbl, rcvd_lbl]) # "name" -> None means skip # --------------------------------------------------------------------------- FIELD_MAP = { # Common (non-directional) "freq": ("freq", "freq", "freq_khz"), "mo": ("mode", "mode", "mode_cabrillo"), "date": ("start_time", "start_time", "date_yyyy_mm_dd"), "time": ("start_time", "start_time", "time_hhmm"), # Callsigns "call": ("station_callsign", "callsign", "upper", "My Call", "Call Rcvd"), "callsign": ("station_callsign", "callsign", "upper", "My Call", "Call Rcvd"), # RST "rst": ("rst_sent", "rst_rcvd", ""), # Gridsquare "grid": ("my_gridsquare", "gridsquare", "upper"), # Serial number fields (map to stx / srx) "nr": ("stx", "srx", ""), "number": ("stx", "srx", ""), "num": ("stx", "srx", ""), "ser": ("stx", "srx", ""), "stx": ("stx", "srx", ""), "srx": ("stx", "srx", ""), # Exchange fields (map to stx_string / srx_string) "exch": ("stx_string", "srx_string", ""), "exchange": ("stx_string", "srx_string", ""), "exc": ("stx_string", "srx_string", ""), "ex1": ("stx_string", "srx_string", ""), "ex2": ("stx_string", "srx_string", ""), "ex3": ("stx_string", "srx_string", ""), "sec": ("stx_string", "srx_string", ""), "p": ("stx_string", "srx_string", ""), "ck": ("stx_string", "srx_string", ""), "zn": ("stx_string", "srx_string", ""), "age": ("stx_string", "srx_string", ""), "name": ("stx_string", "srx_string", ""), "qth": ("stx_string", "srx_string", ""), # Transmitter ID (fixed value, not from DB) "t": ("", "", "transmitter_id", "t", "t"), } DEFAULT_FIELDS = ["freq", "mo", "date", "time", "call", "rst", "exch", "call", "rst", "exch", "t"] # --------------------------------------------------------------------------- # Mapping logic # --------------------------------------------------------------------------- def map_field(name, is_sent, is_common): """Map field name to (db_field, formatter, label, needs_review) or None to skip.""" low = name.lower() entry = FIELD_MAP.get(low) if entry is None and low in FIELD_MAP: return None # explicitly skipped if entry is None: # Unknown field -> default to exchange db = "stx_string" if is_sent else "srx_string" sfx = "Sent" if is_sent else "Rcvd" return (db, "", f"{name.title()} {sfx}", False) sent_db, rcvd_db, formatter = entry[0], entry[1], entry[2] db = sent_db if is_sent else rcvd_db if len(entry) >= 5: label = entry[3] if is_sent else entry[4] elif is_common: label = name.title() else: sfx = "Sent" if is_sent else "Rcvd" label = f"{name.title()} {sfx}" return (db, formatter, label, False) def guess_mode(name): u = name.upper() if u.endswith("-CW"): return "CW" if u.endswith("-SSB") or u.endswith("-PH"): return "SSB" if "RTTY" in u or u.endswith("-RY"): return "RTTY" if "PSK" in u or "DIGI" in u or "FT8" in u: return "DIGI" if u.endswith("-FM"): return "FM" return "MIXED" # --------------------------------------------------------------------------- # Parsing helpers # --------------------------------------------------------------------------- def parse_names(line): names = [] for part in line.strip().split(","): m = re.match(r"^\s*([\w-]+)\s*\(([\w-]+)\)\s*$", part) if m: names += [m.group(1), m.group(2)] else: clean = re.sub(r"\s*\(.*?\)", "", part).strip() if clean: names.append(clean) return names def is_name(line): s = line.strip() if not s or s.startswith(("QSO:", "****", "[x")): return False if s[0].isdigit() and s.replace(" ", "").isdigit(): return False return "info sent" not in s.lower() def is_format(line): s = line.strip() return (s.startswith(("QSO:", "****")) and ("***" in s or "yyyy" in s or "nnnn" in s)) def is_header(line): s = line.strip().lower() return (s.startswith("qso:") and not is_format(line) and ("freq" in s or "call" in s)) def tokens(line): t = line.split() return t[1:] if t and t[0] in ("QSO:", "****") else t def esc(s): return s.replace("'", "''") def make_sig(fld, fmt_tok): return " ".join(f"{f}:{len(t)}" for f, t in zip(fld, fmt_tok) if FIELD_MAP.get(f.lower()) is not None or f.lower() not in FIELD_MAP) def load_state(path): state = {} if not os.path.isfile(path): return state with open(path, "r") as f: for line in f: line = line.strip() if line: parts = line.split("\t", 1) state[parts[0]] = parts[1] if len(parts) > 1 else "" return state def save_state(path, state): with open(path, "w") as f: for cid in sorted(state): f.write(f"{cid}\t{state[cid]}\n") def download(): print(f"Downloading {SOURCE_URL} ...", file=sys.stderr) urllib.request.urlretrieve(SOURCE_URL, DAT_PATH) print(f"Saved to {DAT_PATH}", file=sys.stderr) # --------------------------------------------------------------------------- # Main # --------------------------------------------------------------------------- def main(): full = "--full" in sys.argv download() prev = {} if full else load_state(STATE_PATH) with open(DAT_PATH, "r", encoding="utf-8", errors="replace") as f: lines = f.readlines() new_state = {} new_count = changed_count = skip_count = 0 # Collect: (contest_name, display_name, mode, [(pos, db, width, fmt, lbl), ...]) emit_list = [] i = 0 while i < len(lines): line = lines[i].rstrip() if not is_name(line): i += 1 continue names = parse_names(line) if not names or names[0].startswith("Standard"): i += 1 continue hdr = fmt = None j = i + 1 while j < min(i + 10, len(lines)): l = lines[j].rstrip() if is_name(l): break if not hdr and is_header(l): hdr = l if not fmt and is_format(l): fmt = l j += 1 if not fmt: i = j continue fmt_tok = tokens(fmt) fld = tokens(hdr) if hdr else list(DEFAULT_FIELDS[:len(fmt_tok)]) while len(fld) < len(fmt_tok): fld.append("unknown") n = min(len(fld), len(fmt_tok)) fld = fld[:n] fmt_tok = fmt_tok[:n] sig = make_sig(fld, fmt_tok) # Only process ADIF contest IDs names = [n for n in names if n in BUILTIN_CONTEST_IDS] if not names: i = j continue for name in names: new_state[name] = sig # Classify: new / changed / unchanged emit_names = [] for name in names: if name not in prev: emit_names.append(name) new_count += 1 elif prev[name] != sig: emit_names.append(name) changed_count += 1 else: skip_count += 1 if not emit_names: i = j continue # Find sent/rcvd boundary at second call/callsign calls = [k for k in range(n) if fld[k].lower() in ("call", "callsign")] sent0 = calls[0] if calls else n rcvd0 = calls[1] if len(calls) >= 2 else n # Build columns cols = [] pos = 0 for k in range(n): is_common = k < sent0 is_sent = k < rcvd0 mapped = map_field(fld[k], is_sent, is_common) if mapped is None: continue pos += 1 db, fmtr, lbl, _ = mapped cols.append((pos, db, len(fmt_tok[k]), fmtr, lbl)) for name in emit_names: emit_list.append((name, name.replace("-", " "), guess_mode(name), cols)) i = j # -- Generate SQL ------------------------------------------------------- out = ["-- Generated by devtools/cabrillo/generate_cabrillo_templates.py", "-- Source: " + SOURCE_URL, ""] if not emit_list: out.append("-- Nothing to emit.") else: for cid, display, mode, cols in emit_list: e = esc(cid) ed = esc(display) out.append( f"DELETE FROM cabrillo_templates " f"WHERE name = '{ed}';") out.append( f"INSERT INTO cabrillo_templates " f"(name, contest_name_for_header, " f"default_category_mode) VALUES " f"('{ed}', '{e}', '{esc(mode)}');") for p, db, w, fmtr, lbl in cols: out.append( f"INSERT INTO cabrillo_template_columns " f"(template_id, position, db_field, width, formatter, label) " f"SELECT id, {p}, '{esc(db)}', {w}, '{esc(fmtr)}', " f"'{esc(lbl)}' FROM cabrillo_templates " f"WHERE name = '{ed}';") out.append("") save_state(STATE_PATH, new_state) print("\n".join(out)) parts = [] if full: parts.append(f"{new_count} templates [full]") else: parts.append(f"{new_count} new, {changed_count} changed, " f"{skip_count} unchanged") print(", ".join(parts), file=sys.stderr) if __name__ == "__main__": main() ================================================ FILE: devtools/deployment/finalize_release.sh ================================================ #!/usr/bin/env bash # The script finalizes the release and edits the changlog in deployment scripts. # # Usage: # ./devtools/deployment/finalize_release.sh # # Must be executed from the QLog root directory. # Changelog is the single source of truth. set -uo pipefail ROOTDIR=. CHANGELOG="${ROOTDIR}/Changelog" QLOG_PRO="${ROOTDIR}/QLog.pro" if [ ! -f "${QLOG_PRO}" ]; then echo "ERROR: QLog.pro not found. Run this script from the QLog root directory." exit 1 fi # --- Checks --- DEB_CHANGELOG="${ROOTDIR}/debian/changelog" RPM_SPEC="${ROOTDIR}/rpm_spec/qlog.spec" METAINFO="${ROOTDIR}/res/io.github.foldynl.QLog.metainfo.xml" INSTALLER_PKG="${ROOTDIR}/installer/packages/de.dl2ic.qlog/meta/package.xml" INSTALLER_CFG="${ROOTDIR}/installer/config/config.xml" # Cleanup temp files on exit TMPFILE=$(mktemp) trap 'rm -f "${TMPFILE}"' EXIT if ! head -n 1 "${CHANGELOG}" | grep -q '^TBC - '; then echo "ERROR: Changelog does not start with 'TBC'. Already finalized?" exit 1 fi # --- Extract data --- QLOG_VERSION=$(head -n 1 "${CHANGELOG}" | awk '{print $3}') RELEASE_DATE_ISO=$(date "+%Y-%m-%d") RELEASE_DATE_CHANGELOG=$(date "+%Y/%m/%d") RELEASE_DATE_DEB=$(date "+%a, %-d %b %Y %T %z") RELEASE_DATE_RPM=$(date "+%a %b %-d %Y") # Changelog entries: lines from 2nd line until first empty line ENTRIES=$(sed -n '2,/^$/{ /^$/d; p }' "${CHANGELOG}") echo "Preparing release ${QLOG_VERSION} (${RELEASE_DATE_ISO})" # --- Helper --- xml_escape() { sed 's/&/\&/g; s//\>/g' } # --- QLog.pro --- echo " QLog.pro" sed -i "s/VERSION = .*/VERSION = ${QLOG_VERSION}/" "${QLOG_PRO}" # --- Changelog --- echo " Changelog" sed -i "s#^TBC - #${RELEASE_DATE_CHANGELOG} - #" "${CHANGELOG}" # --- DEB Changelog --- echo " debian/changelog" { echo "qlog (${QLOG_VERSION}-1) UNRELEASED; urgency=low" echo "${ENTRIES}" | sed 's/^-/ */' echo "" echo " -- foldynl ${RELEASE_DATE_DEB}" echo "" cat "${DEB_CHANGELOG}" } > "${TMPFILE}" cp "${TMPFILE}" "${DEB_CHANGELOG}" # --- RPM Changelog --- echo " rpm_spec/qlog.spec" { echo "* ${RELEASE_DATE_RPM} Ladislav Foldyna - ${QLOG_VERSION}-1" echo "${ENTRIES}" echo "" } > "${TMPFILE}" sed -i -e "/%changelog/{r ${TMPFILE}" -e '}' "${RPM_SPEC}" # --- Appstream Metainfo --- echo " res/io.github.foldynl.QLog.metainfo.xml" { echo " " echo " " echo "
    " echo "${ENTRIES}" | sed 's/^- //' | xml_escape | sed 's/^/
  • /; s/$/<\/li>/' echo "
" echo "
" echo "
" } > "${TMPFILE}" sed -i -e "/ /{r ${TMPFILE}" -e '}' "${METAINFO}" appstreamcli validate "${METAINFO}" # --- Qt Installer files --- echo " installer/packages/.../package.xml" sed -i "s/.*<\/Version>/${QLOG_VERSION}-1<\/Version>/" "${INSTALLER_PKG}" sed -i "s/.*<\/ReleaseDate>/${RELEASE_DATE_ISO}<\/ReleaseDate>/" "${INSTALLER_PKG}" echo " installer/config/config.xml" sed -i "s/.*<\/Version>/${QLOG_VERSION}<\/Version>/" "${INSTALLER_CFG}" # --- Summary --- echo "" echo "Release ${QLOG_VERSION} finalized. Changed files:" echo " - ${QLOG_PRO}" echo " - ${CHANGELOG}" echo " - ${DEB_CHANGELOG}" echo " - ${RPM_SPEC}" echo " - ${METAINFO}" echo " - ${INSTALLER_PKG}" echo " - ${INSTALLER_CFG}" # --- Commit --- echo "" read -r -p "Commit changes? [y/N] " confirm if [[ ! "${confirm}" =~ ^[Yy]$ ]]; then echo "Aborted." exit 0 fi git commit -a -m "Preparation for release ${QLOG_VERSION}" echo "Changes committed." # --- Push, merge to master --- BRANCH=$(git rev-parse --abbrev-ref HEAD) echo "" echo "Pushing ${BRANCH}..." git push origin "${BRANCH}" echo "Switching to master and merging ${BRANCH}..." git checkout master git pull git merge "${BRANCH}" # --- Tag and push --- TAG="v${QLOG_VERSION}" echo "" echo "Wait for GitHub Actions to pass, then confirm." echo "" read -r -p "Tag and push ${TAG}? [y/N] " confirm if [[ ! "${confirm}" =~ ^[Yy]$ ]]; then echo "Aborted. To tag manually later:" echo " git tag -a ${TAG} -m \"Release ${QLOG_VERSION}\"" echo " git push --atomic origin master ${TAG}" exit 0 fi git tag -a "${TAG}" -m "Release ${QLOG_VERSION}" git push --atomic origin master "${TAG}" echo "" echo "Tag ${TAG} pushed. You can now create the GitHub release:" echo " ./devtools/deployment/create_github_release.sh" ================================================ FILE: devtools/timezones/README.md ================================================ # Database files Please download the database files [here](https://cdn.bertold.org/zonedetect/db/db.zip) or create them using the builder. To use the builder, first install [shapelib](https://github.com/OSGeo/shapelib), then: cd builder ./makedb.sh This will create database files in `out` and `out_v1`, as well as a `db.zip` containing both directories. The files in the folder out\_v1/ use a newer format and use less space to encode the same information. QLog uses out\_v1/timezone21.bin ================================================ FILE: devtools/timezones/builder/builder.cpp ================================================ /* * Copyright (c) 2018, Bertold Van den Bergh (vandenbergh@bertold.org) * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the author nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR DISTRIBUTOR BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include unsigned version = 1; const double Inf = std::numeric_limits::infinity(); std::unordered_map alpha2ToName; std::unordered_map tzidToAlpha2; void errorFatal(std::string what) { std::cerr<& output, int64_t valueIn, bool handleNeg = true) { uint64_t value = valueIn; if(handleNeg) { value = encodeSignedToUnsigned(valueIn); } int bytesUsed = 0; do { uint8_t byteOut = value & 0x7F; if(value >= 128) { byteOut |= 128; } output.push_back(byteOut); bytesUsed ++; value >>= 7; } while(value); return bytesUsed; } uint64_t encodePointTo64(int64_t lat, int64_t lon){ assert_(lat || lon, "Tried to encode 0,0. This is not allowed"); uint64_t latu=encodeSignedToUnsigned(lat); uint64_t lonu=encodeSignedToUnsigned(lon); assert_(latu < (uint64_t)1<<32, "Unsigned lat overflow"); assert_(lonu < (uint64_t)1<<32, "Unsigned lat overflow"); uint64_t point = 0; for(uint8_t i=31; i<=31; i--){ point <<= 2; if(latu & (1< pointMap_; struct Point { static Point* GetPoint(double dlat = 0, double dlon = 0, unsigned int precision = 32){ int64_t lat = doubleToFixedPoint(dlat, 90, precision); int64_t lon = doubleToFixedPoint(dlon, 180, precision); uint64_t key = encodePointTo64(lat, lon); if(pointMap_.count(key)){ return pointMap_[key]; } Point* p = new Point(lat, lon); p->key_ = key; pointMap_[key] = p; return p; } Point(int64_t lat = 0, int64_t lon = 0) { lat_ = lat; lon_ = lon; } std::tuple value() { return std::make_tuple(lat_, lon_); } int encodePointBinary(std::vector& output) { int bytesUsed = encodeVariableLength(output, lat_); bytesUsed += encodeVariableLength(output, lon_); return bytesUsed; } int64_t lat_; int64_t lon_; uint64_t key_; PolygonData* parent_ = nullptr; int index_ = 0; bool encoded_ = false; uint64_t encodedOffset_ = 0; }; struct PolygonData { Point boundingMin; Point boundingMax; std::vector points_; unsigned long fileIndex_ = 0; unsigned long metadataId_; Point* lastPoint_ = nullptr; void processPoint(Point* p) { if(p->lat_ < boundingMin.lat_) { boundingMin.lat_ = p->lat_; } if(p->lon_ < boundingMin.lon_) { boundingMin.lon_ = p->lon_; } if(p->lat_ > boundingMax.lat_) { boundingMax.lat_ = p->lat_; } if(p->lon_ > boundingMax.lon_) { boundingMax.lon_ = p->lon_; } /* Don't encode duplicate points */ if(lastPoint_ == p){ return; } lastPoint_ = p; points_.push_back(p); } PolygonData(unsigned long id): boundingMin(INT64_MAX, INT64_MAX), boundingMax(INT64_MIN, INT64_MIN), metadataId_(id) { } struct LineSegment { std::vector points_; Point* prevPoint_; PolygonData* parent_; bool sameDirection(int64_t x1, int64_t y1, int64_t x2, int64_t y2){ if(!x2 && !y2){ return false; } if((x1 > 0 && x2 < 0) || (x1 < 0 && x2 > 0)){ return false; } if((y1 > 0 && y2 < 0) || (y1 < 0 && y2 > 0)){ return false; } if(x1 == 0){ return x2 == 0; } return y2 == (y1*x2/x1); } unsigned int encodeDelta(std::vector& output, PolygonData* mark = nullptr, int start = 0, int end = -1){ unsigned int numPoints = 0; if(end < 0){ end = points_.size()-1; } int64_t accDiffLat = 0, accDiffLon = 0; int64_t prevDiffLat = 0, prevDiffLon = 0; int64_t prevLat, prevLon; Point* prevPoint = prevPoint_; if(start > 0){ prevPoint = points_[start-1]; } std::tie(prevLat, prevLon) = prevPoint->value(); auto encodePoint = [&](bool force = false){ /* Encode accumulator. * After this the position is equal to that of the previous point */ if(accDiffLat || accDiffLon || force){ if(version == 0){ encodeVariableLength(output, accDiffLat); encodeVariableLength(output, accDiffLon); }else{ encodeVariableLength(output, encodePointTo64(accDiffLat, accDiffLon), false); } numPoints++; } /* Mark points as encoded if we mark and we are the parent */ if(mark && prevPoint->parent_ == mark){ prevPoint->encoded_ = true; prevPoint->encodedOffset_ = output.size(); } /* Reset accumulator */ accDiffLat = 0; accDiffLon = 0; }; for(int i = start; i<=end; i++){ Point* point = points_[i]; int64_t lat, lon; std::tie(lat, lon) = point->value(); /* Calculate difference */ int64_t diffLat = lat - prevLat; int64_t diffLon = lon - prevLon; /* Encode delta */ if(!sameDirection(diffLat, diffLon, prevDiffLat, prevDiffLon)){ encodePoint(); } accDiffLat += diffLat; accDiffLon += diffLon; /* Store previous values */ prevDiffLat = diffLat; prevDiffLon = diffLon; prevLat = lat; prevLon = lon; prevPoint = point; } /* Encode remainder if needed */ encodePoint(version == 0); return numPoints; } bool encodeReference(std::vector& output){ /* Search for first marked point */ int end = -1, start = -1; for(int i=0; iencoded_){ start = i; break; } } for(int i=points_.size()-1; i>=0; i--){ if(points_[i]->encoded_){ end = i; break; } } if(end < 0 || start < 0){ /* Only unencoded points, then we can only delta encode it ourself */ return false; } /* Encode delta until where we can refer */ encodeDelta(output, nullptr, 0, start); /* Add reference marker if it is still needed */ if(start != end){ uint64_t startRef = points_[start]->encodedOffset_; uint64_t endRef = points_[end]->encodedOffset_; output.push_back(0); output.push_back(1); encodeVariableLength(output, startRef, false); int64_t diff = endRef - startRef; encodeVariableLength(output, diff, true); } /* Encode delta till the end of the segment */ encodeDelta(output, nullptr, end+1); return true; } }; unsigned int encodeBinaryData(std::vector& output) { std::vector lines_; PolygonData* currentParent = nullptr; LineSegment* segment = nullptr; /* Step 1: Encode first point */ Point* prevPoint = points_[0]; if(version == 0){ prevPoint->encodePointBinary(output); }else{ encodeVariableLength(output, prevPoint->key_, false); } int direction = 0; /* Step 2: Go through the list of points and check which ones already exist. * We skip the first and last one since the first one is already encoded * and the last one is identical to the first */ for(int i=1; iparent_){ point->parent_ = this; point->index_ = i; } bool newSegment = false; if(point->parent_ == currentParent){ if(direction == 0){ direction = point->index_ - prevPoint->index_; if(direction > 1 || direction < -1){ newSegment = true; } }else{ if(point->index_ != prevPoint->index_ + direction){ newSegment = true; } } } if(point->parent_ != currentParent || newSegment){ if(segment){ lines_.push_back(segment); } currentParent = point->parent_; segment = new LineSegment(); segment->prevPoint_ = prevPoint; segment->parent_ = currentParent; direction = 0; } segment->points_.push_back(point); prevPoint = point; } if(segment){ lines_.push_back(segment); } unsigned int v0Points = 1; /* Step 3: Encode segments */ for(LineSegment* segment: lines_){ if(segment->parent_ == this || version == 0){ /* If we are the parent of the segment we must encode and mark it */ v0Points += segment->encodeDelta(output, this); }else{ /* We are not the parent, we can encode it or refer to it, depending on * which takes less bytes. In any case we should not mark it. */ std::vector delta; segment->encodeDelta(delta); std::vector reference; bool possible = segment->encodeReference(reference); if(!possible || delta.size() <= reference.size()){ output.insert(std::end(output), std::begin(delta), std::end(delta)); }else{ output.insert(std::end(output), std::begin(reference), std::end(reference)); } } } if (version != 0){ /* Step 4: Write end marker */ output.push_back(0); output.push_back(0); } return v0Points; } }; void encodeStringToBinary(std::vector& output, std::string& input) { encodeVariableLength(output, input.size(), false); for(unsigned int i=0; i usedStrings_; struct MetaData { void encodeBinaryData(std::vector& output) { for(std::string& str: data_) { if(str.length() >= 256) { std::cout << "Metadata string is too long\n"; exit(1); } if(!usedStrings_.count(str)) { usedStrings_[str] = output.size(); encodeStringToBinary(output, str); } else { encodeVariableLength(output, usedStrings_[str] + 256, false); } } } std::vector data_; unsigned long fileIndex_; }; std::vector polygons_; std::vector metadata_; std::vector fieldNames_; unsigned int decodeVariableLength(uint8_t* buffer, int64_t* result, bool handleNeg = true) { int64_t value = 0; unsigned int i=0, shift = 0; do { value |= (buffer[i] & 0x7F) << shift; shift += 7; } while(buffer[i++] & 0x80); if(!handleNeg) { *result = value; } else { *result = (value & 1)?-(value/2):(value/2); } return i; } void readMetaDataTimezone(DBFHandle dataHandle) { /* Specify field names */ fieldNames_.push_back("TimezoneIdPrefix"); fieldNames_.push_back("TimezoneId"); fieldNames_.push_back("CountryAlpha2"); fieldNames_.push_back("CountryName"); /* Parse attribute names */ for(int i = 0; i < DBFGetRecordCount(dataHandle); i++) { metadata_[i].data_.resize(4); for(int j = 0; j < DBFGetFieldCount(dataHandle); j++) { char fieldTitle[12]; int fieldWidth, fieldDecimals; DBFFieldType eType = DBFGetFieldInfo(dataHandle, j, fieldTitle, &fieldWidth, &fieldDecimals); fieldTitle[11] = 0; std::string fieldTitleStr(fieldTitle); if( eType == FTString ) { if(fieldTitleStr == "tzid") { std::string data = DBFReadStringAttribute(dataHandle, i, j); size_t pos = data.find('/'); if (pos == std::string::npos) { metadata_[i].data_.at(0) = data; } else { metadata_[i].data_.at(0) = data.substr(0, pos) + "/"; metadata_[i].data_.at(1) = data.substr(pos + 1, std::string::npos); } if(tzidToAlpha2.count(data)) { metadata_[i].data_.at(2) = tzidToAlpha2[data]; if(alpha2ToName.count(metadata_[i].data_.at(2))) { metadata_[i].data_.at(3) = alpha2ToName[metadata_[i].data_.at(2)]; } else { std::cout< parseAlpha2ToName(DBFHandle dataHandle) { std::unordered_map result; for(int i = 0; i < DBFGetRecordCount(dataHandle); i++) { std::string alpha2, name; for(int j = 0; j < DBFGetFieldCount(dataHandle); j++) { char fieldTitle[12]; int fieldWidth, fieldDecimals; DBFFieldType eType = DBFGetFieldInfo(dataHandle, j, fieldTitle, &fieldWidth, &fieldDecimals); fieldTitle[11] = 0; std::string fieldTitleStr(fieldTitle); if( eType == FTString ) { if(fieldTitleStr == "ISO_A2" || fieldTitleStr == "WB_A2") { std::string tmp = DBFReadStringAttribute(dataHandle, i, j); if(tmp != "-99" && alpha2 == "") { alpha2 = tmp; } } else if(fieldTitleStr == "NAME_LONG") { name = DBFReadStringAttribute(dataHandle, i, j); } } } if(alpha2 != "") { result[alpha2]=name; } } result["GF"]="French Guiana"; result["GP"]="Guadeloupe"; result["BQ"]="Bonaire"; result["MQ"]="Martinique"; result["SJ"]="Svalbard and Jan Mayen Islands"; result["NO"]="Norway"; result["CX"]="Christmas Island"; result["CC"]="Cocos Islands"; result["YT"]="Mayotte"; result["RE"]="Réunion"; result["TK"]="Tokelau"; result["TW"]="Taiwan"; return result; } std::unordered_map parseTimezoneToAlpha2(std::string path) { std::unordered_map result; //TODO: Clean solution... #include "zoneToAlpha.h" return result; } int main(int argc, char ** argv ) { if(argc != 7) { std::cout << "Wrong number of parameters\n"; return 1; } tzidToAlpha2 = parseTimezoneToAlpha2("TODO"); char tableType = argv[1][0]; std::string path = argv[2]; std::string outPath = argv[3]; unsigned int precision = strtol(argv[4], NULL, 10); std::string notice = argv[5]; version = strtol(argv[6], NULL, 10); if(version > 1){ std::cout << "Unknown version\n"; return 1; } DBFHandle dataHandle = DBFOpen("naturalearth/ne_10m_admin_0_countries_lakes", "rb" ); alpha2ToName = parseAlpha2ToName(dataHandle); DBFClose(dataHandle); dataHandle = DBFOpen(path.c_str(), "rb" ); if( dataHandle == NULL ) { errorFatal("Could not open attribute file\n"); } metadata_.resize(DBFGetRecordCount(dataHandle)); std::cout << "Reading "<nSHPType != 3 && shapeObject->nSHPType != 5 && shapeObject->nSHPType != 13 && shapeObject->nSHPType != 15) { std::cout<<"Unsupported shape object ("<< SHPTypeName(shapeObject->nSHPType) <<")\n"; continue; } int partIndex = 0; PolygonData* polygonData = nullptr; for(int j = 0; j < shapeObject->nVertices; j++ ) { if(j == 0 || j == shapeObject->panPartStart[partIndex]) { totalPolygons++; if(polygonData) { /* Commit it */ polygons_.push_back(polygonData); } polygonData = new PolygonData(i); if(partIndex + 1 < shapeObject->nParts) { partIndex++; } } Point* p = Point::GetPoint(shapeObject->padfY[j], shapeObject->padfX[j], precision); polygonData->processPoint(p); } if(polygonData) { /* Commit it */ polygons_.push_back(polygonData); } SHPDestroyObject(shapeObject); } } SHPClose(shapeHandle); std::cout<<"Parsed "<boundingMin.lat_ < b->boundingMin.lat_; }); /* Encode data section and store pointers */ std::vector outputData; for(PolygonData* polygon: polygons_) { polygon->fileIndex_ = outputData.size(); if(version == 0){ std::vector tmpData; unsigned int numPoints = polygon->encodeBinaryData(tmpData); encodeVariableLength(outputData, numPoints, false); outputData.insert(std::end(outputData), std::begin(tmpData), std::end(tmpData)); }else{ polygon->encodeBinaryData(outputData); } } std::cout << "Encoded data section into "< outputMeta; for(MetaData& metadata: metadata_) { metadata.fileIndex_ = outputMeta.size(); metadata.encodeBinaryData(outputMeta); } std::cout << "Encoded metadata into "< outputBBox; int64_t prevFileIndex = 0; int64_t prevMetaIndex = 0; for(PolygonData* polygon: polygons_) { polygon->boundingMin.encodePointBinary(outputBBox); polygon->boundingMax.encodePointBinary(outputBBox); encodeVariableLength(outputBBox, metadata_.at(polygon->metadataId_).fileIndex_ - prevMetaIndex); prevMetaIndex = metadata_[polygon->metadataId_].fileIndex_; encodeVariableLength(outputBBox, polygon->fileIndex_ - prevFileIndex, false); prevFileIndex = polygon->fileIndex_; } std::cout << "Encoded bounding box section into "< outputHeader; outputHeader.push_back('P'); outputHeader.push_back('L'); outputHeader.push_back('B'); outputHeader.push_back(tableType); outputHeader.push_back(version); outputHeader.push_back(precision); outputHeader.push_back(fieldNames_.size()); for(unsigned int i=0; i release rem %~nx0 release -> release rem %~nx0 clean -> clean release rem %~nx0 rebuild -> clean + release rem %~nx0 deploy -> windeployqt + binarycreator rem %~nx0 all -> rebuild + deploy rem ============================================================ rem ============================================================ rem - The script is executed in cmd.exe on Windows. rem - The Visual Studio environment init batch (VS_VCVARS) must exist. rem - It must support the selected target (VS_ARCH), e.g. x86_amd64 for x64 builds. rem - The Qt MSVC kit root (QT_BASE) must exist and contain: rem - %QT_BASE%\bin\qmake.exe rem - %QT_BASE%\bin\windeployqt.exe rem - The jom executable (JOM) must exist (used for qmake_all and Makefile.Release). rem - The Qt Installer Framework bin directory (QTIFW_BIN) must exist and contain: rem - binarycreator.exe rem - The QMake project file (PRO) must exist and be readable. rem - The working/build directory must be writable (Makefile.Release will be generated). rem rem - Hamlib must be present under %DEVROOT%\hamlib-w64-%HAMLIBVERSION% derived from HAMLIBVERSION: rem - Expected headers: %HAMLIBROOT%\include\ rem - Expected libraries: %HAMLIBROOT%\lib\msvc\ rem rem - vcpkg packages root (VCPKG_PACKAGES) must exist and contain required packages: rem - %VCPKG_PACKAGES%\%QTKEYCHAIN_PKG%\include and ...\lib rem - %VCPKG_PACKAGES%\%PTHREAD_PKG%\include and ...\lib rem - %VCPKG_PACKAGES%\%ZLIB_PKG%\include and ...\lib rem rem ============================================================ set "ROOT=%~dp0" if "%ROOT:~-1%"=="\" set "ROOT=%ROOT:~0,-1%" for %%I in ("%ROOT%\..\..\..") do set "DEVROOT=%%~fI" rem === CONFIGURATION === rem -- VC Compiler Settings set "VS_VCVARS=C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" rem -- QT Settings set "QT_BASE=C:\Qt\6.10.2\msvc2022_64" set "QTIFW_BIN=C:\Qt\Tools\QtInstallerFramework\4.6\bin" set "JOM=C:\Qt\Tools\QtCreator\bin\jom\jom.exe" rem -- Project Settings set "PROJECT_BASE=%DEVROOT%\QLog" for /f "tokens=3" %%V in ('findstr /R /C:"^VERSION *= *" "%PROJECT_BASE%\QLog.pro"') do set "QLOG_VERSION=%%V" set "INSTALLER_BASE=%DEVROOT%\qlog_build\qlog-installer-%QLOG_VERSION%" set "INSTALLER_SEQ=0" if exist "%INSTALLER_BASE%.exe" ( :seq_loop set /a INSTALLER_SEQ+=1 if exist "%INSTALLER_BASE%-!INSTALLER_SEQ!.exe" goto :seq_loop ) if %INSTALLER_SEQ%==0 ( set "INSTALLER_OUT=%INSTALLER_BASE%.exe" ) else ( set "INSTALLER_OUT=%INSTALLER_BASE%-!INSTALLER_SEQ!.exe" ) rem -- Libs Settings set "VCPKG_PACKAGES=%DEVROOT%\vcpkg\packages" set "VCPKG_QTKEYCHAIN_PKG=qtkeychain-qt6_x64-windows" set "VCPKG_PTHREAD_PKG=pthreads_x64-windows" set "VCPKG_ZLIB_PKG=zlib_x64-windows" rem -- Hamlib Settings set "HAMLIBVERSION_MAJOR=4" set "HAMLIBVERSION_MINOR=7" set "HAMLIBVERSION_PATCH=0" rem === END OF CONFIGURATION === set "VS_ARCH=x86_amd64" set "QMAKE=%QT_BASE%\bin\qmake.exe" set "WINDEPLOYQT=%QT_BASE%\bin\windeployqt.exe" set "BINARYCREATOR=%QTIFW_BIN%\binarycreator.exe" set "PRO=%PROJECT_BASE%\QLog.pro" set "BUILDROOT=%PROJECT_BASE%\build\deployment" set "HAMLIBVERSION=%HAMLIBVERSION_MAJOR%.%HAMLIBVERSION_MINOR%.%HAMLIBVERSION_PATCH%" set "HAMLIBROOT=%DEVROOT%\hamlib-w64-%HAMLIBVERSION%" set "HAMLIBINCLUDEPATH=%HAMLIBROOT%\include" set "HAMLIBLIBPATH=%HAMLIBROOT%\lib\msvc" set "HAMLIBBINPATH=%HAMLIBROOT%\bin" set "QTKEYCHAININCLUDEPATH=%VCPKG_PACKAGES%\%VCPKG_QTKEYCHAIN_PKG%\include" set "QTKEYCHAINLIBPATH=%VCPKG_PACKAGES%\%VCPKG_QTKEYCHAIN_PKG%\lib" set "QTKEYCHAINBINPATH=%VCPKG_PACKAGES%\%VCPKG_QTKEYCHAIN_PKG%\bin" set "PTHREADINCLUDEPATH=%VCPKG_PACKAGES%\%VCPKG_PTHREAD_PKG%\include" set "PTHREADLIBPATH=%VCPKG_PACKAGES%\%VCPKG_PTHREAD_PKG%\lib" set "ZLIBINCLUDEPATH=%VCPKG_PACKAGES%\%VCPKG_ZLIB_PKG%\include" set "ZLIBLIBPATH=%VCPKG_PACKAGES%\%VCPKG_ZLIB_PKG%\lib" set "ZLIBBINPATH=%VCPKG_PACKAGES%\%VCPKG_ZLIB_PKG%\bin" set "DEPLOY_DIR=%BUILDROOT%\installer\packages\de.dl2ic.qlog\data" set "INSTALLER_CONFIG=%BUILDROOT%\installer\config\config.xml" set "INSTALLER_PACKAGES=%BUILDROOT%\installer\packages" set "OPENSSLROOT=%DEVROOT%\openssl-3.0\x64" rem === Action === set "ACTION=%~1" if /I "%ACTION%"=="" set "ACTION=release" if not exist "%BUILDROOT%" mkdir "%BUILDROOT%" pushd "%BUILDROOT%" >nul rem === Initialize MSVC environment === echo === Initializing MSVC environment (%VS_ARCH%) === call "%VS_VCVARS%" %VS_ARCH% if errorlevel 1 goto :fail if /I "%ACTION%"=="clean" goto :clean if /I "%ACTION%"=="rebuild" goto :rebuild if /I "%ACTION%"=="release" goto :release if /I "%ACTION%"=="deploy" goto :deploy if /I "%ACTION%"=="all" goto :all echo Unknown action: %ACTION% echo Usage: %~nx0 [release^|clean^|rebuild^|deploy^|all] goto :fail rem ============================================================ rem BUILD STEPS rem ============================================================ :qmake echo === Running qmake === "%QMAKE%" "%PRO%" -spec win32-msvc ^ "CONFIG+=qtquickcompiler" ^ "HAMLIBINCLUDEPATH=%HAMLIBINCLUDEPATH%" ^ "HAMLIBLIBPATH=%HAMLIBLIBPATH%" ^ "HAMLIBVERSION_MAJOR=%HAMLIBVERSION_MAJOR%" ^ "HAMLIBVERSION_MINOR=%HAMLIBVERSION_MINOR%" ^ "HAMLIBVERSION_PATCH=%HAMLIBVERSION_PATCH%" ^ "QTKEYCHAININCLUDEPATH=%QTKEYCHAININCLUDEPATH%" ^ "QTKEYCHAINLIBPATH=%QTKEYCHAINLIBPATH%" ^ "PTHREADINCLUDEPATH=%PTHREADINCLUDEPATH%" ^ "PTHREADLIBPATH=%PTHREADLIBPATH%" ^ "ZLIBINCLUDEPATH=%ZLIBINCLUDEPATH%" ^ "ZLIBLIBPATH=%ZLIBLIBPATH%" ^ "OPENSSLINCLUDEPATH=%OPENSSLROOT%/include" ^ "OPENSSLLIBPATH=%OPENSSLROOT%/lib" if errorlevel 1 exit /b 10 echo === jom qmake_all === "%JOM%" qmake_all if errorlevel 1 exit /b 11 exit /b 0 :clean call :qmake if errorlevel 1 goto :fail echo === Cleaning (Makefile.Release) === "%JOM%" -f Makefile.Release clean if errorlevel 1 goto :fail del /F /Q "%BUILDROOT%\release\qlog.exe" >nul rmdir /S /Q "%BUILDROOT%\installer" >nul goto :ok :release call :qmake if errorlevel 1 goto :fail echo === Building Release (Makefile.Release) === "%JOM%" -f Makefile.Release if errorlevel 1 goto :fail goto :ok :rebuild call :qmake if errorlevel 1 goto :fail echo === Cleaning (Makefile.Release) === "%JOM%" -f Makefile.Release clean if errorlevel 1 goto :fail echo === Building Release (Makefile.Release) === "%JOM%" -f Makefile.Release if errorlevel 1 goto :fail goto :ok rem ============================================================ rem DEPLOYMENT STEPS rem ============================================================ :deploy echo === Deployment === rem Ensure tools are reachable (only for this process) set "PATH=%QT_BASE%\bin;%QTIFW_BIN%;%PATH%" if not exist "%BUILDROOT%\release\qlog.exe" ( echo ERROR: "%BUILDROOT%\release\qlog.exe" not found. echo Check DEPLOY_DIR: "%DEPLOY_DIR%" goto :fail ) robocopy "%PROJECT_BASE%\installer" "%BUILDROOT%\installer" /MIR if errorlevel 8 ( echo ERROR: Cannot ROBOCOPY installer goto :fail ) mkdir "%DEPLOY_DIR%" rem ***************** rem Copy QLog Binary rem ***************** copy /Y "%BUILDROOT%\release\qlog.exe" "%DEPLOY_DIR%\qlog.exe" if errorlevel 1 ( echo ERROR: Cannot copy qlog.exe to "%DEPLOY_DIR%" goto :fail ) rem *********** rem Copy Hamlib rem *********** copy /Y "%HAMLIBBINPATH%\*.dll" "%DEPLOY_DIR%" if errorlevel 1 ( echo ERROR: Cannot copy Hamlib DLL to "%DEPLOY_DIR%" goto :fail ) copy /Y "%HAMLIBBINPATH%\rigctld.exe" "%DEPLOY_DIR%" if errorlevel 1 ( echo ERROR: Cannot copy rigctld.exe to "%DEPLOY_DIR%" goto :fail ) rem **************** rem Copy QtKeychain rem **************** copy /Y "%QTKEYCHAINBINPATH%\*.dll" "%DEPLOY_DIR%" if errorlevel 1 ( echo ERROR: Cannot copy QTKeychain DLL to "%DEPLOY_DIR%" goto :fail ) rem **************** rem Copy zlib rem **************** copy /Y "%ZLIBBINPATH%\*.dll" "%DEPLOY_DIR%" if errorlevel 1 ( echo ERROR: Cannot copy zlib DLL to "%DEPLOY_DIR%" goto :fail ) rem **************** rem Copy OpenSSL rem **************** copy /Y "%OPENSSLROOT%\bin\*.dll" "%DEPLOY_DIR%" if errorlevel 1 ( echo ERROR: Cannot copy OpenSSL DLL to "%DEPLOY_DIR%" goto :fail ) rem **************** rem Deploy rem **************** pushd "%DEPLOY_DIR%" >nul if errorlevel 1 ( echo ERROR: Cannot enter DEPLOY_DIR: "%DEPLOY_DIR%" goto :fail ) echo --- windeployqt (release) --- "%WINDEPLOYQT%" -release --openssl-root "%OPENSSLROOT%" --skip-plugin-types qmltooling,position,qml,qsqlpsql,qsqlodbc,qsqlmimer "qlog.exe" if errorlevel 1 ( popd >nul goto :fail ) popd >nul if not exist "%BINARYCREATOR%" ( echo ERROR: binarycreator.exe not found: "%BINARYCREATOR%" goto :fail ) echo --- binarycreator --- "%BINARYCREATOR%" -f -c "%INSTALLER_CONFIG%" -p "%INSTALLER_PACKAGES%" "%INSTALLER_OUT%" if errorlevel 1 goto :fail goto :ok :all call :rebuild if errorlevel 1 goto :fail call :deploy if errorlevel 1 goto :fail goto :ok rem ============================================================ rem END rem ============================================================ :ok popd >nul endlocal exit /b 0 :fail set "EC=%errorlevel%" if "%EC%"=="0" set "EC=1" echo. echo FAILED with errorlevel %EC% popd >nul endlocal exit /b %EC% ================================================ FILE: devtools/windowsMake/tests.bat ================================================ @echo off setlocal EnableExtensions EnableDelayedExpansion rem ============================================================ rem Build + Run all unit tests for QLog (MSVC + Qt) rem Usage: rem %~nx0 -> build (release) + run rem %~nx0 all -> build (release) + run rem %~nx0 build -> build only (release) rem %~nx0 run -> run existing binaries rem %~nx0 clean -> clean build directory rem %~nx0 rebuild -> clean + build + run rem ============================================================ rem Notes: rem - This script is executed in cmd.exe on Windows. rem - It expects MSVC environment init batch (VS_VCVARS) and Qt qmake. rem - QTKEYCHAIN/HAMLIB/ZLIB paths are passed through like in make.bat. rem ============================================================ set "ROOT=%~dp0" if "%ROOT:~-1%"=="\" set "ROOT=%ROOT:~0,-1%" for %%I in ("%ROOT%\..\..\..") do set "DEVROOT=%%~fI" rem === CONFIGURATION (keep in sync with make.bat) === rem -- VC Compiler Settings set "VS_VCVARS=C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" set "VS_ARCH=x86_amd64" rem -- QT Settings set "QT_BASE=C:\Qt\6.10.2\msvc2022_64" set "JOM=C:\Qt\Tools\QtCreator\bin\jom\jom.exe" rem -- Project Settings set "PROJECT_BASE=%DEVROOT%\QLog" set "PRO=%PROJECT_BASE%\tests\tests.pro" set "BUILDROOT=%PROJECT_BASE%\build\tests" rem -- Libs Settings set "VCPKG_PACKAGES=%DEVROOT%\vcpkg\packages" set "VCPKG_QTKEYCHAIN_PKG=qtkeychain-qt6_x64-windows" set "VCPKG_PTHREAD_PKG=pthreads_x64-windows" set "VCPKG_ZLIB_PKG=zlib_x64-windows" rem -- Hamlib Settings set "HAMLIBVERSION_MAJOR=4" set "HAMLIBVERSION_MINOR=7" set "HAMLIBVERSION_PATCH=0" rem -- OpenSSL (needed at runtime for some configurations) set "OPENSSLROOT=%DEVROOT%\openssl-3.0\x64" rem === END OF CONFIGURATION === set "QMAKE=%QT_BASE%\bin\qmake.exe" set "HAMLIBVERSION=%HAMLIBVERSION_MAJOR%.%HAMLIBVERSION_MINOR%.%HAMLIBVERSION_PATCH%" set "HAMLIBROOT=%DEVROOT%\hamlib-w64-%HAMLIBVERSION%" set "HAMLIBINCLUDEPATH=%HAMLIBROOT%\include" set "HAMLIBLIBPATH=%HAMLIBROOT%\lib\msvc" set "HAMLIBBINPATH=%HAMLIBROOT%\bin" set "QTKEYCHAININCLUDEPATH=%VCPKG_PACKAGES%\%VCPKG_QTKEYCHAIN_PKG%\include" set "QTKEYCHAINLIBPATH=%VCPKG_PACKAGES%\%VCPKG_QTKEYCHAIN_PKG%\lib" set "QTKEYCHAINBINPATH=%VCPKG_PACKAGES%\%VCPKG_QTKEYCHAIN_PKG%\bin" set "PTHREADINCLUDEPATH=%VCPKG_PACKAGES%\%VCPKG_PTHREAD_PKG%\include" set "PTHREADLIBPATH=%VCPKG_PACKAGES%\%VCPKG_PTHREAD_PKG%\lib" set "ZLIBINCLUDEPATH=%VCPKG_PACKAGES%\%VCPKG_ZLIB_PKG%\include" set "ZLIBLIBPATH=%VCPKG_PACKAGES%\%VCPKG_ZLIB_PKG%\lib" set "ZLIBBINPATH=%VCPKG_PACKAGES%\%VCPKG_ZLIB_PKG%\bin" set "ACTION=%~1" if /I "%ACTION%"=="" set "ACTION=all" if not exist "%PRO%" ( echo ERROR: tests project not found: "%PRO%" goto :fail ) if not exist "%BUILDROOT%" mkdir "%BUILDROOT%" pushd "%BUILDROOT%" >nul echo === Initializing MSVC environment (%VS_ARCH%) === call "%VS_VCVARS%" %VS_ARCH% if errorlevel 1 goto :fail if exist "%JOM%" ( set "MAKE=%JOM%" ) else ( set "MAKE=nmake" ) if /I "%ACTION%"=="clean" goto :action_clean if /I "%ACTION%"=="build" goto :action_build if /I "%ACTION%"=="run" goto :action_run if /I "%ACTION%"=="rebuild" goto :action_rebuild if /I "%ACTION%"=="all" goto :action_all echo Unknown action: %ACTION% echo Usage: %~nx0 [all^|build^|run^|clean^|rebuild] goto :fail :qmake echo === Running qmake (tests, release) === "%QMAKE%" "%PRO%" -spec win32-msvc ^ "CONFIG+=release" ^ "CONFIG-=debug_and_release" ^ "HAMLIBINCLUDEPATH=%HAMLIBINCLUDEPATH%" ^ "HAMLIBLIBPATH=%HAMLIBLIBPATH%" ^ "HAMLIBVERSION_MAJOR=%HAMLIBVERSION_MAJOR%" ^ "HAMLIBVERSION_MINOR=%HAMLIBVERSION_MINOR%" ^ "HAMLIBVERSION_PATCH=%HAMLIBVERSION_PATCH%" ^ "QTKEYCHAININCLUDEPATH=%QTKEYCHAININCLUDEPATH%" ^ "QTKEYCHAINLIBPATH=%QTKEYCHAINLIBPATH%" ^ "PTHREADINCLUDEPATH=%PTHREADINCLUDEPATH%" ^ "PTHREADLIBPATH=%PTHREADLIBPATH%" ^ "ZLIBINCLUDEPATH=%ZLIBINCLUDEPATH%" ^ "ZLIBLIBPATH=%ZLIBLIBPATH%" ^ "OPENSSLINCLUDEPATH=%OPENSSLROOT%/include" ^ "OPENSSLLIBPATH=%OPENSSLROOT%/lib" if errorlevel 1 exit /b 10 exit /b 0 :doBuild call :qmake if errorlevel 1 exit /b 11 echo === Building tests === "%MAKE%" if errorlevel 1 exit /b 12 exit /b 0 :doRun echo === Running tests === set "PATH=%QT_BASE%\bin;%QTKEYCHAINBINPATH%;%HAMLIBBINPATH%;%ZLIBBINPATH%;%OPENSSLROOT%\bin;%PATH%" set "FAILED=0" set "COUNT=0" for /r "%BUILDROOT%" %%F in (tst_*.exe) do ( set /a COUNT+=1 echo --- %%~nxF --- "%%F" if errorlevel 1 ( echo FAILED: %%~nxF set "FAILED=1" goto :run_done ) ) :run_done if "%COUNT%"=="0" ( echo ERROR: No test executables found under "%BUILDROOT%". exit /b 21 ) if "%FAILED%"=="1" exit /b 20 exit /b 0 :action_clean echo === Cleaning build directory === if exist "%BUILDROOT%\Makefile" ( "%MAKE%" clean ) popd >nul rmdir /S /Q "%BUILDROOT%" >nul 2>&1 endlocal exit /b 0 :action_build call :doBuild if errorlevel 1 goto :fail goto :ok :action_run call :doRun if errorlevel 1 goto :fail goto :ok :action_rebuild echo === Rebuild === if exist "%BUILDROOT%\Makefile" ( "%MAKE%" clean ) call :doBuild if errorlevel 1 goto :fail call :doRun if errorlevel 1 goto :fail goto :ok :action_all call :doBuild if errorlevel 1 goto :fail call :doRun if errorlevel 1 goto :fail goto :ok :ok popd >nul endlocal exit /b 0 :fail set "EC=%errorlevel%" if "%EC%"=="0" set "EC=1" echo. echo FAILED with errorlevel %EC% popd >nul endlocal exit /b %EC% ================================================ FILE: doc/UDP_Notifications.txt ================================================ see details https://github.com/foldynl/QLog/wiki/Notifications ================================================ FILE: entitlements.xml ================================================ com.apple.security.cs.allow-unsigned-executable-memory com.apple.security.cs.disable-library-validation com.apple.security.cs.allow-jit com.apple.security.cs.disable-executable-page-protection ================================================ FILE: i18n/datastrings.tri ================================================ QT_TRANSLATE_NOOP("Data", "Aircraft Scatter"); QT_TRANSLATE_NOOP("Data", "Aurora-E"); QT_TRANSLATE_NOOP("Data", "Aurora"); QT_TRANSLATE_NOOP("Data", "Back scatter"); QT_TRANSLATE_NOOP("Data", "EchoLink"); QT_TRANSLATE_NOOP("Data", "Earth-Moon-Earth"); QT_TRANSLATE_NOOP("Data", "Sporadic E"); QT_TRANSLATE_NOOP("Data", "F2 Reflection"); QT_TRANSLATE_NOOP("Data", "Field Aligned Irregularities"); QT_TRANSLATE_NOOP("Data", "Ground Wave"); QT_TRANSLATE_NOOP("Data", "Internet-assisted"); QT_TRANSLATE_NOOP("Data", "Ionoscatter"); QT_TRANSLATE_NOOP("Data", "IRLP"); QT_TRANSLATE_NOOP("Data", "Line of Sight"); QT_TRANSLATE_NOOP("Data", "Meteor scatter"); QT_TRANSLATE_NOOP("Data", "Terrestrial or atmospheric repeater or transponder"); QT_TRANSLATE_NOOP("Data", "Rain scatter"); QT_TRANSLATE_NOOP("Data", "Satellite"); QT_TRANSLATE_NOOP("Data", "Trans-equatorial"); QT_TRANSLATE_NOOP("Data", "Tropospheric ducting"); ================================================ FILE: i18n/dbstrings.tri ================================================ QT_TRANSLATE_NOOP("DBStrings", "PHONE"); QT_TRANSLATE_NOOP("DBStrings", "CW"); QT_TRANSLATE_NOOP("DBStrings", "DIGITAL"); QT_TRANSLATE_NOOP("DBStrings", "Afghanistan"); QT_TRANSLATE_NOOP("DBStrings", "Agalega & St. Brandon"); QT_TRANSLATE_NOOP("DBStrings", "Aland Islands"); QT_TRANSLATE_NOOP("DBStrings", "Alaska"); QT_TRANSLATE_NOOP("DBStrings", "Albania"); QT_TRANSLATE_NOOP("DBStrings", "Algeria"); QT_TRANSLATE_NOOP("DBStrings", "American Samoa"); QT_TRANSLATE_NOOP("DBStrings", "Amsterdam & St. Paul Is."); QT_TRANSLATE_NOOP("DBStrings", "Andaman & Nicobar Is."); QT_TRANSLATE_NOOP("DBStrings", "Andorra"); QT_TRANSLATE_NOOP("DBStrings", "Angola"); QT_TRANSLATE_NOOP("DBStrings", "Anguilla"); QT_TRANSLATE_NOOP("DBStrings", "Annobon Island"); QT_TRANSLATE_NOOP("DBStrings", "Antarctica"); QT_TRANSLATE_NOOP("DBStrings", "Antigua & Barbuda"); QT_TRANSLATE_NOOP("DBStrings", "Argentina"); QT_TRANSLATE_NOOP("DBStrings", "Armenia"); QT_TRANSLATE_NOOP("DBStrings", "Aruba"); QT_TRANSLATE_NOOP("DBStrings", "Ascension Island"); QT_TRANSLATE_NOOP("DBStrings", "Asiatic Russia"); QT_TRANSLATE_NOOP("DBStrings", "Asiatic Turkey"); QT_TRANSLATE_NOOP("DBStrings", "Austral Islands"); QT_TRANSLATE_NOOP("DBStrings", "Australia"); QT_TRANSLATE_NOOP("DBStrings", "Austria"); QT_TRANSLATE_NOOP("DBStrings", "Aves Island"); QT_TRANSLATE_NOOP("DBStrings", "Azerbaijan"); QT_TRANSLATE_NOOP("DBStrings", "Azores"); QT_TRANSLATE_NOOP("DBStrings", "Bahamas"); QT_TRANSLATE_NOOP("DBStrings", "Bahrain"); QT_TRANSLATE_NOOP("DBStrings", "Baker & Howland Islands"); QT_TRANSLATE_NOOP("DBStrings", "Balearic Islands"); QT_TRANSLATE_NOOP("DBStrings", "Banaba Island"); QT_TRANSLATE_NOOP("DBStrings", "Bangladesh"); QT_TRANSLATE_NOOP("DBStrings", "Barbados"); QT_TRANSLATE_NOOP("DBStrings", "Belarus"); QT_TRANSLATE_NOOP("DBStrings", "Belgium"); QT_TRANSLATE_NOOP("DBStrings", "Belize"); QT_TRANSLATE_NOOP("DBStrings", "Benin"); QT_TRANSLATE_NOOP("DBStrings", "Bermuda"); QT_TRANSLATE_NOOP("DBStrings", "Bhutan"); QT_TRANSLATE_NOOP("DBStrings", "Bolivia"); QT_TRANSLATE_NOOP("DBStrings", "Bonaire"); QT_TRANSLATE_NOOP("DBStrings", "Bosnia-Herzegovina"); QT_TRANSLATE_NOOP("DBStrings", "Botswana"); QT_TRANSLATE_NOOP("DBStrings", "Bouvet"); QT_TRANSLATE_NOOP("DBStrings", "Brazil"); QT_TRANSLATE_NOOP("DBStrings", "British Virgin Islands"); QT_TRANSLATE_NOOP("DBStrings", "Brunei Darussalam"); QT_TRANSLATE_NOOP("DBStrings", "Bulgaria"); QT_TRANSLATE_NOOP("DBStrings", "Burkina Faso"); QT_TRANSLATE_NOOP("DBStrings", "Burundi"); QT_TRANSLATE_NOOP("DBStrings", "Cambodia"); QT_TRANSLATE_NOOP("DBStrings", "Cameroon"); QT_TRANSLATE_NOOP("DBStrings", "Canada"); QT_TRANSLATE_NOOP("DBStrings", "Canary Islands"); QT_TRANSLATE_NOOP("DBStrings", "Cape Verde"); QT_TRANSLATE_NOOP("DBStrings", "Cayman Islands"); QT_TRANSLATE_NOOP("DBStrings", "Central African Republic"); QT_TRANSLATE_NOOP("DBStrings", "Central Kiribati"); QT_TRANSLATE_NOOP("DBStrings", "Ceuta & Melilla"); QT_TRANSLATE_NOOP("DBStrings", "Chad"); QT_TRANSLATE_NOOP("DBStrings", "Chagos Islands"); QT_TRANSLATE_NOOP("DBStrings", "Chatham Islands"); QT_TRANSLATE_NOOP("DBStrings", "Chesterfield Islands"); QT_TRANSLATE_NOOP("DBStrings", "Chile"); QT_TRANSLATE_NOOP("DBStrings", "China"); QT_TRANSLATE_NOOP("DBStrings", "Christmas Island"); QT_TRANSLATE_NOOP("DBStrings", "Clipperton Island"); QT_TRANSLATE_NOOP("DBStrings", "Cocos (Keeling) Islands"); QT_TRANSLATE_NOOP("DBStrings", "Cocos Island"); QT_TRANSLATE_NOOP("DBStrings", "Colombia"); QT_TRANSLATE_NOOP("DBStrings", "Comoros"); QT_TRANSLATE_NOOP("DBStrings", "Conway Reef"); QT_TRANSLATE_NOOP("DBStrings", "Corsica"); QT_TRANSLATE_NOOP("DBStrings", "Costa Rica"); QT_TRANSLATE_NOOP("DBStrings", "Cote d'Ivoire"); QT_TRANSLATE_NOOP("DBStrings", "Crete"); QT_TRANSLATE_NOOP("DBStrings", "Croatia"); QT_TRANSLATE_NOOP("DBStrings", "Crozet Island"); QT_TRANSLATE_NOOP("DBStrings", "Cuba"); QT_TRANSLATE_NOOP("DBStrings", "Curacao"); QT_TRANSLATE_NOOP("DBStrings", "Cyprus"); QT_TRANSLATE_NOOP("DBStrings", "Czech Republic"); QT_TRANSLATE_NOOP("DBStrings", "DPR of Korea"); QT_TRANSLATE_NOOP("DBStrings", "Dem. Rep. of the Congo"); QT_TRANSLATE_NOOP("DBStrings", "Denmark"); QT_TRANSLATE_NOOP("DBStrings", "Desecheo Island"); QT_TRANSLATE_NOOP("DBStrings", "Djibouti"); QT_TRANSLATE_NOOP("DBStrings", "Dodecanese"); QT_TRANSLATE_NOOP("DBStrings", "Dominica"); QT_TRANSLATE_NOOP("DBStrings", "Dominican Republic"); QT_TRANSLATE_NOOP("DBStrings", "Ducie Island"); QT_TRANSLATE_NOOP("DBStrings", "East Malaysia"); QT_TRANSLATE_NOOP("DBStrings", "Easter Island"); QT_TRANSLATE_NOOP("DBStrings", "Eastern Kiribati"); QT_TRANSLATE_NOOP("DBStrings", "Ecuador"); QT_TRANSLATE_NOOP("DBStrings", "Egypt"); QT_TRANSLATE_NOOP("DBStrings", "El Salvador"); QT_TRANSLATE_NOOP("DBStrings", "England"); QT_TRANSLATE_NOOP("DBStrings", "Equatorial Guinea"); QT_TRANSLATE_NOOP("DBStrings", "Eritrea"); QT_TRANSLATE_NOOP("DBStrings", "Estonia"); QT_TRANSLATE_NOOP("DBStrings", "Ethiopia"); QT_TRANSLATE_NOOP("DBStrings", "European Russia"); QT_TRANSLATE_NOOP("DBStrings", "Falkland Islands"); QT_TRANSLATE_NOOP("DBStrings", "Faroe Islands"); QT_TRANSLATE_NOOP("DBStrings", "Fed. Rep. of Germany"); QT_TRANSLATE_NOOP("DBStrings", "Fernando de Noronha"); QT_TRANSLATE_NOOP("DBStrings", "Fiji"); QT_TRANSLATE_NOOP("DBStrings", "Finland"); QT_TRANSLATE_NOOP("DBStrings", "France"); QT_TRANSLATE_NOOP("DBStrings", "Franz Josef Land"); QT_TRANSLATE_NOOP("DBStrings", "French Guiana"); QT_TRANSLATE_NOOP("DBStrings", "French Polynesia"); QT_TRANSLATE_NOOP("DBStrings", "Gabon"); QT_TRANSLATE_NOOP("DBStrings", "Galapagos Islands"); QT_TRANSLATE_NOOP("DBStrings", "Georgia"); QT_TRANSLATE_NOOP("DBStrings", "Ghana"); QT_TRANSLATE_NOOP("DBStrings", "Gibraltar"); QT_TRANSLATE_NOOP("DBStrings", "Glorioso Islands"); QT_TRANSLATE_NOOP("DBStrings", "Greece"); QT_TRANSLATE_NOOP("DBStrings", "Greenland"); QT_TRANSLATE_NOOP("DBStrings", "Grenada"); QT_TRANSLATE_NOOP("DBStrings", "Guadeloupe"); QT_TRANSLATE_NOOP("DBStrings", "Guam"); QT_TRANSLATE_NOOP("DBStrings", "Guantanamo Bay"); QT_TRANSLATE_NOOP("DBStrings", "Guatemala"); QT_TRANSLATE_NOOP("DBStrings", "Guernsey"); QT_TRANSLATE_NOOP("DBStrings", "Guinea"); QT_TRANSLATE_NOOP("DBStrings", "Guinea-Bissau"); QT_TRANSLATE_NOOP("DBStrings", "Guyana"); QT_TRANSLATE_NOOP("DBStrings", "Haiti"); QT_TRANSLATE_NOOP("DBStrings", "Hawaii"); QT_TRANSLATE_NOOP("DBStrings", "Heard Island"); QT_TRANSLATE_NOOP("DBStrings", "Honduras"); QT_TRANSLATE_NOOP("DBStrings", "Hong Kong"); QT_TRANSLATE_NOOP("DBStrings", "Hungary"); QT_TRANSLATE_NOOP("DBStrings", "ITU HQ"); QT_TRANSLATE_NOOP("DBStrings", "Iceland"); QT_TRANSLATE_NOOP("DBStrings", "India"); QT_TRANSLATE_NOOP("DBStrings", "Indonesia"); QT_TRANSLATE_NOOP("DBStrings", "Iran"); QT_TRANSLATE_NOOP("DBStrings", "Iraq"); QT_TRANSLATE_NOOP("DBStrings", "Ireland"); QT_TRANSLATE_NOOP("DBStrings", "Isle of Man"); QT_TRANSLATE_NOOP("DBStrings", "Israel"); QT_TRANSLATE_NOOP("DBStrings", "Italy"); QT_TRANSLATE_NOOP("DBStrings", "Jamaica"); QT_TRANSLATE_NOOP("DBStrings", "Jan Mayen"); QT_TRANSLATE_NOOP("DBStrings", "Japan"); QT_TRANSLATE_NOOP("DBStrings", "Jersey"); QT_TRANSLATE_NOOP("DBStrings", "Johnston Island"); QT_TRANSLATE_NOOP("DBStrings", "Jordan"); QT_TRANSLATE_NOOP("DBStrings", "Juan Fernandez Islands"); QT_TRANSLATE_NOOP("DBStrings", "Juan de Nova & Europa"); QT_TRANSLATE_NOOP("DBStrings", "Kaliningrad"); QT_TRANSLATE_NOOP("DBStrings", "Kazakhstan"); QT_TRANSLATE_NOOP("DBStrings", "Kenya"); QT_TRANSLATE_NOOP("DBStrings", "Kerguelen Islands"); QT_TRANSLATE_NOOP("DBStrings", "Kermadec Islands"); QT_TRANSLATE_NOOP("DBStrings", "Kingdom of Eswatini"); QT_TRANSLATE_NOOP("DBStrings", "Kure Island"); QT_TRANSLATE_NOOP("DBStrings", "Kuwait"); QT_TRANSLATE_NOOP("DBStrings", "Kyrgyzstan"); QT_TRANSLATE_NOOP("DBStrings", "Lakshadweep Islands"); QT_TRANSLATE_NOOP("DBStrings", "Laos"); QT_TRANSLATE_NOOP("DBStrings", "Latvia"); QT_TRANSLATE_NOOP("DBStrings", "Lebanon"); QT_TRANSLATE_NOOP("DBStrings", "Lesotho"); QT_TRANSLATE_NOOP("DBStrings", "Liberia"); QT_TRANSLATE_NOOP("DBStrings", "Libya"); QT_TRANSLATE_NOOP("DBStrings", "Liechtenstein"); QT_TRANSLATE_NOOP("DBStrings", "Lithuania"); QT_TRANSLATE_NOOP("DBStrings", "Lord Howe Island"); QT_TRANSLATE_NOOP("DBStrings", "Luxembourg"); QT_TRANSLATE_NOOP("DBStrings", "Macao"); QT_TRANSLATE_NOOP("DBStrings", "Macquarie Island"); QT_TRANSLATE_NOOP("DBStrings", "Madagascar"); QT_TRANSLATE_NOOP("DBStrings", "Madeira Islands"); QT_TRANSLATE_NOOP("DBStrings", "Malawi"); QT_TRANSLATE_NOOP("DBStrings", "Maldives"); QT_TRANSLATE_NOOP("DBStrings", "Mali"); QT_TRANSLATE_NOOP("DBStrings", "Malpelo Island"); QT_TRANSLATE_NOOP("DBStrings", "Malta"); QT_TRANSLATE_NOOP("DBStrings", "Mariana Islands"); QT_TRANSLATE_NOOP("DBStrings", "Market Reef"); QT_TRANSLATE_NOOP("DBStrings", "Marquesas Islands"); QT_TRANSLATE_NOOP("DBStrings", "Marshall Islands"); QT_TRANSLATE_NOOP("DBStrings", "Martinique"); QT_TRANSLATE_NOOP("DBStrings", "Mauritania"); QT_TRANSLATE_NOOP("DBStrings", "Mauritius"); QT_TRANSLATE_NOOP("DBStrings", "Mayotte"); QT_TRANSLATE_NOOP("DBStrings", "Mellish Reef"); QT_TRANSLATE_NOOP("DBStrings", "Mexico"); QT_TRANSLATE_NOOP("DBStrings", "Micronesia"); QT_TRANSLATE_NOOP("DBStrings", "Midway Island"); QT_TRANSLATE_NOOP("DBStrings", "Minami Torishima"); QT_TRANSLATE_NOOP("DBStrings", "Moldova"); QT_TRANSLATE_NOOP("DBStrings", "Monaco"); QT_TRANSLATE_NOOP("DBStrings", "Mongolia"); QT_TRANSLATE_NOOP("DBStrings", "Montenegro"); QT_TRANSLATE_NOOP("DBStrings", "Montserrat"); QT_TRANSLATE_NOOP("DBStrings", "Morocco"); QT_TRANSLATE_NOOP("DBStrings", "Mount Athos"); QT_TRANSLATE_NOOP("DBStrings", "Mozambique"); QT_TRANSLATE_NOOP("DBStrings", "Myanmar"); QT_TRANSLATE_NOOP("DBStrings", "N.Z. Subantarctic Is."); QT_TRANSLATE_NOOP("DBStrings", "Namibia"); QT_TRANSLATE_NOOP("DBStrings", "Nauru"); QT_TRANSLATE_NOOP("DBStrings", "Navassa Island"); QT_TRANSLATE_NOOP("DBStrings", "Nepal"); QT_TRANSLATE_NOOP("DBStrings", "Netherlands"); QT_TRANSLATE_NOOP("DBStrings", "New Caledonia"); QT_TRANSLATE_NOOP("DBStrings", "New Zealand"); QT_TRANSLATE_NOOP("DBStrings", "Nicaragua"); QT_TRANSLATE_NOOP("DBStrings", "Niger"); QT_TRANSLATE_NOOP("DBStrings", "Nigeria"); QT_TRANSLATE_NOOP("DBStrings", "Niue"); QT_TRANSLATE_NOOP("DBStrings", "Norfolk Island"); QT_TRANSLATE_NOOP("DBStrings", "North Cook Islands"); QT_TRANSLATE_NOOP("DBStrings", "North Macedonia"); QT_TRANSLATE_NOOP("DBStrings", "Northern Ireland"); QT_TRANSLATE_NOOP("DBStrings", "Norway"); QT_TRANSLATE_NOOP("DBStrings", "Ogasawara"); QT_TRANSLATE_NOOP("DBStrings", "Oman"); QT_TRANSLATE_NOOP("DBStrings", "Pakistan"); QT_TRANSLATE_NOOP("DBStrings", "Palau"); QT_TRANSLATE_NOOP("DBStrings", "Palestine"); QT_TRANSLATE_NOOP("DBStrings", "Palmyra & Jarvis Islands"); QT_TRANSLATE_NOOP("DBStrings", "Panama"); QT_TRANSLATE_NOOP("DBStrings", "Papua New Guinea"); QT_TRANSLATE_NOOP("DBStrings", "Paraguay"); QT_TRANSLATE_NOOP("DBStrings", "Peru"); QT_TRANSLATE_NOOP("DBStrings", "Peter 1 Island"); QT_TRANSLATE_NOOP("DBStrings", "Philippines"); QT_TRANSLATE_NOOP("DBStrings", "Pitcairn Island"); QT_TRANSLATE_NOOP("DBStrings", "Poland"); QT_TRANSLATE_NOOP("DBStrings", "Portugal"); QT_TRANSLATE_NOOP("DBStrings", "Pr. Edward & Marion Is."); QT_TRANSLATE_NOOP("DBStrings", "Pratas Island"); QT_TRANSLATE_NOOP("DBStrings", "Puerto Rico"); QT_TRANSLATE_NOOP("DBStrings", "Qatar"); QT_TRANSLATE_NOOP("DBStrings", "Republic of Korea"); QT_TRANSLATE_NOOP("DBStrings", "Republic of Kosovo"); QT_TRANSLATE_NOOP("DBStrings", "Republic of South Sudan"); QT_TRANSLATE_NOOP("DBStrings", "Republic of the Congo"); QT_TRANSLATE_NOOP("DBStrings", "Reunion Island"); QT_TRANSLATE_NOOP("DBStrings", "Revillagigedo"); QT_TRANSLATE_NOOP("DBStrings", "Rodriguez Island"); QT_TRANSLATE_NOOP("DBStrings", "Romania"); QT_TRANSLATE_NOOP("DBStrings", "Rotuma Island"); QT_TRANSLATE_NOOP("DBStrings", "Rwanda"); QT_TRANSLATE_NOOP("DBStrings", "Saba & St. Eustatius"); QT_TRANSLATE_NOOP("DBStrings", "Sable Island"); QT_TRANSLATE_NOOP("DBStrings", "Samoa"); QT_TRANSLATE_NOOP("DBStrings", "San Andres & Providencia"); QT_TRANSLATE_NOOP("DBStrings", "San Felix & San Ambrosio"); QT_TRANSLATE_NOOP("DBStrings", "San Marino"); QT_TRANSLATE_NOOP("DBStrings", "Sao Tome & Principe"); QT_TRANSLATE_NOOP("DBStrings", "Sardinia"); QT_TRANSLATE_NOOP("DBStrings", "Saudi Arabia"); QT_TRANSLATE_NOOP("DBStrings", "Scarborough Reef"); QT_TRANSLATE_NOOP("DBStrings", "Scotland"); QT_TRANSLATE_NOOP("DBStrings", "Senegal"); QT_TRANSLATE_NOOP("DBStrings", "Serbia"); QT_TRANSLATE_NOOP("DBStrings", "Seychelles"); QT_TRANSLATE_NOOP("DBStrings", "Sierra Leone"); QT_TRANSLATE_NOOP("DBStrings", "Singapore"); QT_TRANSLATE_NOOP("DBStrings", "Sint Maarten"); QT_TRANSLATE_NOOP("DBStrings", "Slovak Republic"); QT_TRANSLATE_NOOP("DBStrings", "Slovenia"); QT_TRANSLATE_NOOP("DBStrings", "Solomon Islands"); QT_TRANSLATE_NOOP("DBStrings", "Somalia"); QT_TRANSLATE_NOOP("DBStrings", "South Africa"); QT_TRANSLATE_NOOP("DBStrings", "South Cook Islands"); QT_TRANSLATE_NOOP("DBStrings", "South Georgia Island"); QT_TRANSLATE_NOOP("DBStrings", "South Orkney Islands"); QT_TRANSLATE_NOOP("DBStrings", "South Sandwich Islands"); QT_TRANSLATE_NOOP("DBStrings", "South Shetland Islands"); QT_TRANSLATE_NOOP("DBStrings", "Sov Mil Order of Malta"); QT_TRANSLATE_NOOP("DBStrings", "Spain"); QT_TRANSLATE_NOOP("DBStrings", "Spratly Islands"); QT_TRANSLATE_NOOP("DBStrings", "Sri Lanka"); QT_TRANSLATE_NOOP("DBStrings", "St. Barthelemy"); QT_TRANSLATE_NOOP("DBStrings", "St. Helena"); QT_TRANSLATE_NOOP("DBStrings", "St. Kitts & Nevis"); QT_TRANSLATE_NOOP("DBStrings", "St. Lucia"); QT_TRANSLATE_NOOP("DBStrings", "St. Martin"); QT_TRANSLATE_NOOP("DBStrings", "St. Paul Island"); QT_TRANSLATE_NOOP("DBStrings", "St. Peter & St. Paul"); QT_TRANSLATE_NOOP("DBStrings", "St. Pierre & Miquelon"); QT_TRANSLATE_NOOP("DBStrings", "St. Vincent"); QT_TRANSLATE_NOOP("DBStrings", "Sudan"); QT_TRANSLATE_NOOP("DBStrings", "Suriname"); QT_TRANSLATE_NOOP("DBStrings", "Svalbard"); QT_TRANSLATE_NOOP("DBStrings", "Swains Island"); QT_TRANSLATE_NOOP("DBStrings", "Sweden"); QT_TRANSLATE_NOOP("DBStrings", "Switzerland"); QT_TRANSLATE_NOOP("DBStrings", "Syria"); QT_TRANSLATE_NOOP("DBStrings", "Taiwan"); QT_TRANSLATE_NOOP("DBStrings", "Tajikistan"); QT_TRANSLATE_NOOP("DBStrings", "Tanzania"); QT_TRANSLATE_NOOP("DBStrings", "Temotu Province"); QT_TRANSLATE_NOOP("DBStrings", "Thailand"); QT_TRANSLATE_NOOP("DBStrings", "The Gambia"); QT_TRANSLATE_NOOP("DBStrings", "Timor - Leste"); QT_TRANSLATE_NOOP("DBStrings", "Togo"); QT_TRANSLATE_NOOP("DBStrings", "Tokelau Islands"); QT_TRANSLATE_NOOP("DBStrings", "Tonga"); QT_TRANSLATE_NOOP("DBStrings", "Trindade & Martim Vaz"); QT_TRANSLATE_NOOP("DBStrings", "Trinidad & Tobago"); QT_TRANSLATE_NOOP("DBStrings", "Tristan da Cunha & Gough Islands"); QT_TRANSLATE_NOOP("DBStrings", "Tromelin Island"); QT_TRANSLATE_NOOP("DBStrings", "Tunisia"); QT_TRANSLATE_NOOP("DBStrings", "Turkmenistan"); QT_TRANSLATE_NOOP("DBStrings", "Turks & Caicos Islands"); QT_TRANSLATE_NOOP("DBStrings", "Tuvalu"); QT_TRANSLATE_NOOP("DBStrings", "UK Base Areas on Cyprus"); QT_TRANSLATE_NOOP("DBStrings", "US Virgin Islands"); QT_TRANSLATE_NOOP("DBStrings", "Uganda"); QT_TRANSLATE_NOOP("DBStrings", "Ukraine"); QT_TRANSLATE_NOOP("DBStrings", "United Arab Emirates"); QT_TRANSLATE_NOOP("DBStrings", "United Nations HQ"); QT_TRANSLATE_NOOP("DBStrings", "United States"); QT_TRANSLATE_NOOP("DBStrings", "Uruguay"); QT_TRANSLATE_NOOP("DBStrings", "Uzbekistan"); QT_TRANSLATE_NOOP("DBStrings", "Vanuatu"); QT_TRANSLATE_NOOP("DBStrings", "Vatican City"); QT_TRANSLATE_NOOP("DBStrings", "Venezuela"); QT_TRANSLATE_NOOP("DBStrings", "Vietnam"); QT_TRANSLATE_NOOP("DBStrings", "Wake Island"); QT_TRANSLATE_NOOP("DBStrings", "Wales"); QT_TRANSLATE_NOOP("DBStrings", "Wallis & Futuna Islands"); QT_TRANSLATE_NOOP("DBStrings", "West Malaysia"); QT_TRANSLATE_NOOP("DBStrings", "Western Kiribati"); QT_TRANSLATE_NOOP("DBStrings", "Western Sahara"); QT_TRANSLATE_NOOP("DBStrings", "Willis Island"); QT_TRANSLATE_NOOP("DBStrings", "Yemen"); QT_TRANSLATE_NOOP("DBStrings", "Zambia"); QT_TRANSLATE_NOOP("DBStrings", "Zimbabwe"); ================================================ FILE: i18n/i18n.qrc ================================================ qlog_de.qm qlog_cs.qm qlog_zh_CN.qm datastrings.tri qlog_es.qm qlog_fr.qm qlog_it.qm ================================================ FILE: i18n/qlog_cs.ts ================================================ ActivityEditor Activity Editor Editor Aktivit Activity Name Jméno aktivity Layout Rozložení Window Arrangement: Uspořádání oken: Saved Uloženo Clear Vymazat Available Fields Dostupná pole List of fields that can be used Seznam polí, která mohou být použita Row A Řádka A Move selected fields from the Available Fields List to the Row A Přesunout vybraná pole ze seznamu dostupných polí na řádek A Remove selected field from the Row A Odstranit vybrané pole z řádku A List of fields in the first variable row Seznam polí na první řádce Change the order. Move selected field up Změnit pořadí. Přesunout vybrané pole nahoru Change the order. Move selected field down Změnit pořadí. Přesunout vybrané pole dolu Row B Řádka B Move selected fields from the Available Fields List to the Row B Přesunout vybraná pole ze seznamu dostupných polí na řádek B Remove selected field from the Row B Odstranit vybrané pole z řádku B List of fields in the second variable row Seznam polí na druhé řádce Column A Sloupec A List of fields in the first QSO Detail Column Seznam polí v prvním sloupci detailu QSO Column B Sloupec B List of fields in the second QSO Detail Column Seznam polí ve druhém sloupci detailu QSO Column C Sloupec C List of fields in the third QSO Detail Column Seznam polí ve třetím sloupci detailu QSO New QSO Rows Řádky QSO New QSO Detail Columns Sloupce QSO Detailu Values Hodnoty Profiles Profily If unchecked, the profile does not change Pokud není aktivován, profil se nezmění Station Stanice Antenna Anténa Rig Rig Connect automatically Automaticky připojit Connect Připojit Rotator Rotátor Fields Pole Must not be empty Nesmí být prázdné Unsaved Neuloženo AlertRuleDetail Alert Rule Detail Detail Pravidla Rule Name Jméno pravidla Enabled Aktivní Sources Zdroje DX Cluster DX Cluster WSJTX WSJTX DX DX Worked Pracováno New Slot Nový slot Confirmed Potvrzeno New Entity Nová země New Mode Nový druh provozu New Band Nové pásmo DX Callsign DX Značka Use Perl-like regular expression Použijte regulární výraz ve formatu Perl .* .* Country Země Log Status Status v logu Spot Comment Spot komentář ITU ITU CQZ CQZ IOTA IOTA SOTA SOTA POTA POTA WWFF WWFF Special Programs Activity Modes Druhy provozu FTx (FT8/FT4) FTx (FT8/FT4) Phone Fóne CW CW Digital Digi Bands Pásma Continent Kontinent North America Severní Amerika Africa Afrika Antarctica Antarktida South America Jižní Amerika Asia Asie Europe Evropa Oceania Oceánie Member Člen Spotter Spotter All Vše Must not be empty Nesmí být prázdné No Club List is enabled Není aktívní žádný Club List AlertSettingDialog Alerts Rules Upozornění Rules Pravidla Add Přidat Edit Upravit Remove Vymazat Name Jméno State Stav AlertTableModel Rule Name Pravidlo Callsign Značka Frequency Frekvence Mode Druh provozu Updated Aktualizací Last Update Poslední Last Comment Zpráva Member Člen AlertWidget Alerts Upozornění Clear older than Vymazat starší Never Nemazat min(s) min Edit Rules Upravit pravidla Column Visibility... Zobrazení sloupců... Clear Vymazat AwardsDialog Awards Diplomy Options Nastavení Award Diplom My DXCC Entity Má DXCC Země User Filter Uživatelský filtr Confirmed by Potvrzeno LoTW LoTW eQSL eQSL Paper QSL Mode Druh provozu CW CW Phone Fóne Digi Digi Not-Worked Only Nepracováno Not-Confirmed Only Nepotvrzeno Show Zobrazit DXCC DXCC ITU ITU WAC WAC WAZ WAZ WAS WAS WPX WPX IOTA IOTA POTA Hunter POTA Lovec POTA Activator POTA Aktivátor SOTA SOTA WWFF WWFF Gridsquare 2-Chars Lokátor 2 znaky Gridsquare 4-Chars Lokátor 4 znaky Gridsquare 6-Chars Lokátor 6 znaků Gridsquare %1-Chars Lokátor %1 znaků US Counties Okresy USA Russian Districts Ruské distrikty Japanese Cities/Ku/Guns Japonská města / ku / guny NZ Counties Okresy Nového Zélandu Spanish DMEs Španělské DME Ukrainian Districts Ukrajinské distrikty No User Filter Žádný uživatelský filtr North America Severní Amerika South America Jižní Amerika Europe Evropa Africa Afrika Asia Asie Antarctica Antarktida Oceania Oceánie DELETED Smazáno AwardsTableModel Slots: Slot: Confirmed Potvrzeno Worked Pracováno Still Waiting Stále čeká BandmapWidget Form Bandmap Create an additional Bandmap Window Vytvořit další okno Bandmap Clear older Vymazat starší Clear All Vše vymazat Clear the current band Vymazat aktuální pásmo Never Nemazat min(s) min Bandmap Bandmap Show Band Zobrazit pásmo Center RX Centrovat RX Show Emergency Frequencies Zobrazit nouzové frekvence SOS SOS CWCatKey No Rig is connected Rig není připojen Rig does not support Morse over CAT Rig nepodporuje Morse over CAT Keyer is not connected Klíč není připojen Cannot send Text to Rig Nelze odeslat Text do rádia Rig is not connected Rig není připojen Cannot set Keyer Speed Nelze nastavit rychlost klíče Cannot stop Text Sending Nelze zastavit odesílání textu CWConsoleWidget Form Speed Rychlost WPM WPM Immediately stop CW sending Okamžitě zastavit vysílání CW Halt Zastavit Clear Sent and Echo Console Vymazat Odeslané a Echo konzoli Clear Vymazat &CW &CW Text to send Text k odeslání F1 F1 F2 F2 F3 F3 Shortcuts profile Shortcut profil CW Keyer Profile Profil klíče N/A - Sent text Odeslaný text Echoed text Echo text Switch between sending <b>words</b> individually (separated by spaces)<br> and sending the entire text as a <b>whole</b> (separated by a new line). Přepnout mezi odesílám po <b>slovech</n> (oddělené mezerami) <br>a odesláním jako <b>celku</b> (oddělené koncem rádku). F4 F4 F5 F5 F6 F6 F7 F7 CW Console - Halt Sending CW Konzole - Zastavit Rig must be connected Rig musí být připojen Word Slovo Whole Celek CWDaemonKey Keyer is not connected Klíč není připojen Cannot send Text Nelze odeslat text Cannot set Keyer Speed Nelze nastavit rychlost klíče Cannot stop Text Sending Nelze zastavit odesílání textu CWFldigiKey Connected device is not FLDigi Připojené zařízení není FLDigi Keyer is not connected Klíč není připojen Cannot send Text to FLDigi Nelze odeslat text do FLDigi Cannot send the Clear command to FLDigi Nelze odeslat příkaz do FLDigi Cannot send the TX command to FLDigi Nelze odeslat příkaz do FLDigi Cannot send the Text command to FLDigi Nelze odeslat příkaz do FLDigi Cannot send the Stop command to FLDigi Nelze odeslat příkaz do FLDigi Cannot send the Abort command to FLDigi Nelze odeslat příkaz do FLDigi Cannot receive data from FLDigi Nelze přijmount data z FLDigi FLDigi connection timeout Vypršel časový limit připojení FLDigi FLDigi connection error Chyba FLDigi připojeni FLDigi command error Chyba odeslaného FLDigi příkazu CWKeyer No CW Keyer Profile selected Není vybrán žádný profil klíče Initialization Error Chyba inicializace Internal Error Interní Chyba Connection Error Chyba připojení Cannot open the Keyer connection Nelze přijipoji klíč CWWinKey Connected device is not WinKey Připojené zařízení není WinKey Cannot send Text to Rig Nelze odeslat Text do rádia Keyer is not connected Klíč není připojen Communication Error Chyba připojení Cannot set Keyer Speed Nelze nastavit rychlost klíče Cannot stop Text Sending Nelze zastavit odesílání textu 4000 Hz 4000 Hz 2000 Hz 2000 Hz 1333 Hz 1333 Hz 1000 Hz 1000 Hz 800 Hz 800 Hz 666 Hz 666 Hz 571 Hz 571 Hz 500 Hz 500 Hz 444 Hz 444 Hz 400 Hz 400 Hz CabrilloExportDialog Cabrillo Export Export Cabrillo File: Soubor: Browse Procházet Contest Contest Format Template: Šablona formátu: Manage Spravovat User Filter: Uživatelský filtr: Date/Time: Datum/čas: yyyy-MM-dd HH:mm - - UTC UTC Station & Categories Stanice a kategorie Callsign: Značka: Operators: Operátoři: Band: Pásmo: Mode: Druh provozu: Power: Výkon: Operator: Operátor: Assisted: Asistované: Station: Stanice: Transmitter: Vysílač: Time: Čas: Overlay: Transmitter ID: Additional Další Name: Jméno: Email: Email: Address: Adresa: Max 6 lines Max. 6 řádků Gridsquare Lokátor Location: Umístění: Club: Klub: Soapbox: Volná poznámka: Off-Time: yyyy-mm-dd hhmm yyyy-mm-dd hhmm &Export &Export Cabrillo Files (*.log);;CBR Files (*.cbr);;All Files (*) Soubory Cabrillo (*.log);;CBR soubory (*.cbr);;Všechny soubory (*) QSO(s): %1 QSO: %1 QSOs: ? QSOs: ? QLog Warning Upozornění QLog Please select a contest template. Vyberte prosím šablonu soutěže. Please select an output file. Vyberte prosím výstupní soubor. No callsign available. Check your filters. Žádná volací značka není k dispozici. Zkontrolujte filtry. QLog Error Chyba QLog Failed to prepare export query. Nepodařilo se připravit exportní dotaz. Failed to query QSOs for export. Nepodařilo se načíst QSO pro export. Cannot open file %1 for writing. Nelze otevřít soubor %1 pro zápis. Exporting Cabrillo... Exportuje se Cabrillo… QLog Information Informace QLog Exported %n QSO(s) to Cabrillo file. Exportováno %n QSO do souboru Cabrillo. CabrilloFormat All Bands Všechna pásma 2m (144 MHz) 2m (144 MHz) 1.25m (222 MHz) 1.25m (222 MHz) 70cm (432 MHz) 70cm (432 MHz) 33cm (902 MHz) 33cm (902 MHz) 23cm (1.2 GHz) 23cm (1.2 GHz) Light Světlo VHF 3-Band VHF 3 pásma VHF FM Only Pouze VHF FM Digital Digi Mixed Smíšený High High Low Low QRP Single Operator Jeden operátor Multi Operator Více operátorů Check Log Zkontrolovat log Non-Assisted Neasistovaný Assisted Asistovaný Fixed Pevný Mobile Mobilní Portable Portable Rover Rover Rover Limited Rover Limited Rover Unlimited Rover Unlimited Expedition Expedice HQ HQ School Škola Distributed Distribuovaný One Jeden Two Dva Limited Omezený Unlimited Neomezený SWL SWL 6 Hours 6 hodin 12 Hours 12 hodin 24 Hours 24 hodin Classic Klasické Rookie Rookie TB Wires TB Wires Novice/Tech Nováček/Technik Over 50 Nad 50 Text (left-aligned) Text (zarovnán vlevo) Frequency (kHz) Frekvence (kHz) Time (HHMM) Čas (HHMM) Date (YYYY-MM-DD) Datum (RRRR-MM-DD) RST Short (drop last digit) RST zkrácené (bez poslední číslice) Uppercase Velká písmena Mode (Cabrillo) Mód (Cabrillo) Zero-Padded Nr. Číslo s nulami Transmitter ID CabrilloTemplateDialog Cabrillo Template Manager Správce Cabrillo šablon Import template Importovat šablonu Import Import Export template Exportovat šablonu Export Export New template Nová šablona New Nový Copy existing template Kopírovat existující šablonu Copy Kopírovat Delete template Smazat šablonu Delete Vymazat Template Name: Název šablony: Contest Name: Název závodu: Default Mode: Výchozí mód: QSO Line Columns: Sloupce řádku QSO: Contest name as required by the rules. It is possible to enter a custom string if it is not included in the list. Název soutěže podle pravidel. Je možné zadat vlastní řetězec, pokud není v seznamu. Seq. Poř. QSO Field Pole QSO Formatter Formátovač Width Šířka Label Štítek Add line Přidat řádek Add Přidat Remove selected line Odstranit vybraný řádek Remove Vymazat New Template Nová šablona Copy - %1 Kopie – %1 Delete Template Smazat šablonu Delete template '%1'? Smazat šablonu „%1“? Import Template Importovat šablonu QLog Cabrillo Template (*.qct) QLog Cabrillo šablona (*.qct) Import Failed Import se nezdařil Export Template Exportovat šablonu Export Failed Export se nezdařil Failed to write file: %1 Nepodařilo se zapsat soubor: %1 File not found: %1 Soubor nebyl nalezen: %1 Cannot open file: %1 Nelze otevřít soubor: %1 Invalid template file: missing name Neplatný soubor šablony: chybí název QLog Error Chyba QLog Cannot start database transaction. Nelze zahájit databázovou transakci. QLog Warning Upozornění QLog Cannot save template '%1': %2 Nelze uložit šablonu „%1“: %2 CallbookManager <p>The secondary callbook will be used</p> <p>Bude použit sekundární</p> ChatWidget ON4KST Chat ON4KST Chat Chat Room Chat Connect Připojit New Nový QLog Warning Upozornění QLog ON4KST Chat is not configured properly.<p> Please, use <b>Settings</b> dialog to configure it.</p> ON4KST Chat není správně nastaven.<p> Prosím, použijte dialog <b>Nastavení</b> pro konfiguraci služby.</p> CheckBoxDelegate Enabled Zapnuto Disabled Vypnuto ClockWidget Form Hodiny Sunrise Východ Sunset Západ N/A - CloudlogUploader Invalid API Key Neplatný klíč API ClubLogUploader Clublog Operation for Callsign %1 failed.<br>%2 Clublog operace pro značku %1 se nezdrařila.<br>%2 ColumnSettingDialog Column Visibility Setting Nastavení zobrazených sloupců General Obecné My Info Moje Info QSL && Callbooks QSL && Callbooks Members Členství Conditionals Podmínky Contests Závody Others Jiné Done Dokončit ColumnSettingGenericDialog Unselect All Odznačit vše Select All Vybrat vše ColumnSettingSimpleDialog Column Visibility Setting Nastavení zobrazených sloupců Done Dokončit DBSchemaMigration DXCC Entities DXCC Země Sats Info Sat Info SOTA Summits SOTA Summit WWFF Records WWFF záznamy IOTA Records IOTA záznamy POTA Records POTA záznamy Membership Directory Records Klubové informace Clublog CTY.XML Clublog CTY.XML List of Values Seznam hodnot Updating Aktualizuji Update Failed Aktualizace selhala DBStrings PHONE Fóne CW CW DIGITAL Digi Afghanistan Afghánistán Agalega & St. Brandon Agalega a St. Brandon Aland Islands Alandské ostrovy Alaska Aljaška Albania Albánie Algeria Alžírsko American Samoa Americká Samoa Amsterdam & St. Paul Is. Amsterdam a St. Paul Is. Andaman & Nicobar Is. Andaman & Nicobar Is. Andorra Andorra Angola Angola Anguilla Anguilla Annobon Island Ostrov Annobon Antarctica Antarktida Antigua & Barbuda Antigua a Barbuda Argentina Argentina Armenia Arménie Aruba Aruba Ascension Island Ostrov Ascension Asiatic Russia Asijské Rusko Asiatic Turkey Asijské Turecko Austral Islands Australské ostrovy Australia Austrálie Austria Rakousko Aves Island Ostrov Aves Azerbaijan Ázerbajdžán Azores Azory Bahamas Bahamy Bahrain Bahrajn Baker & Howland Islands Bakerovy a Howlandovy ostrovy Balearic Islands Baleárské ostrovy Banaba Island Ostrov Banaba Bangladesh Bangladéš Barbados Barbados Belarus Bělorusko Belgium Belgie Belize Belize Benin Benin Bermuda Bermudy Bhutan Bhútán Bolivia Bolívie Bonaire Bonaire Bosnia-Herzegovina Bosna a Hercegovina Botswana Botswana Bouvet Bouvet Brazil Brazílie British Virgin Islands Britské Panenské ostrovy Brunei Darussalam Brunej Darussalam Bulgaria Bulharsko Burkina Faso Burkina Faso Burundi Burundi Cambodia Kambodža Cameroon Kamerun Canada Kanada Canary Islands Kanárské ostrovy Cape Verde Kapverdy Cayman Islands Kajmanské ostrovy Central African Republic Středoafrická republika Central Kiribati Střední Kiribati Ceuta & Melilla Ceuta a Melilla Chad Čad Chagos Islands ostrovy Chagos Chatham Islands Chathamské ostrovy Chesterfield Islands Chesterfieldovy ostrovy Chile Chile China Čína Christmas Island Vánoční ostrov Clipperton Island Ostrov Clipperton Cocos (Keeling) Islands Kokosové (Keelingovy) ostrovy Cocos Island Kokosový ostrov Colombia Kolumbie Comoros Komory Conway Reef Conwayský útes Corsica Korsika Costa Rica Kostarika Cote d'Ivoire Pobřeží slonoviny Crete Kréta Croatia Chorvatsko Crozet Island Ostrov Crozet Cuba Kuba Curacao Curacao Cyprus Kypr Czech Republic Česká Republika DPR of Korea Korejská lidová republika Dem. Rep. of the Congo Dem. Rep. Konga Denmark Dánsko Desecheo Island Ostrov Desecheo Djibouti Džibutsko Dodecanese Dodecanese Dominica Dominika Dominican Republic Dominikánská republika Ducie Island Ostrov Ducie East Malaysia Východní Malajsie Easter Island Velikonoční ostrov Eastern Kiribati východní Kiribati Ecuador Ekvádor Egypt Egypt El Salvador El Salvador England Anglie Equatorial Guinea Rovníková Guinea Eritrea Eritrea Estonia Estonsko Ethiopia Etiopie European Russia evropské Rusko Falkland Islands Falklandy Faroe Islands Faerské ostrovy Fed. Rep. of Germany Německo Fernando de Noronha Fernando de Noronha Fiji Fidži Finland Finsko France Francie Franz Josef Land Země Františka Josefa French Guiana Francouzská Guyana French Polynesia Francouzská Polynésie Gabon Gabon Galapagos Islands Galapágy Georgia Gruzie Ghana Ghana Gibraltar Gibraltar Glorioso Islands Ostrovy Glorioso Greece Řecko Greenland Grónsko Grenada Grenada Guadeloupe Guadeloupe Guam Guam Guantanamo Bay Guantánamo Guatemala Guatemala Guernsey Guernsey Guinea Guinea Guinea-Bissau Guinea-Bissau Guyana Guyana Haiti Haiti Hawaii Havaj Heard Island Heardův ostrov Honduras Honduras Hong Kong Hong Kong Hungary Maďarsko ITU HQ ITU HQ Iceland Island India Indie Indonesia Indonésie Iran Írán Iraq Irák Ireland Irsko Isle of Man Ostrov Man Israel Izrael Italy Itálie Jamaica Jamaica Jan Mayen Jan Mayen Japan Japonsko Jersey Jersey Johnston Island Ostrov Johnston Jordan Jordán Juan Fernandez Islands Ostrovy Juana Fernandeze Juan de Nova & Europa Juan de Nova a Evropa Kaliningrad Kaliningrad Kazakhstan Kazachstán Kenya Keňa Kerguelen Islands Kerguelenské ostrovy Kermadec Islands ostrovy Kermadec Kingdom of Eswatini Království Eswatini Kure Island Ostrov Kure Kuwait Kuvajt Kyrgyzstan Kyrgyzstán Lakshadweep Islands Ostrovy Lakshadweep Laos Laos Latvia Lotyšsko Lebanon Libanon Lesotho Lesotho Liberia Libérie Libya Libye Liechtenstein Lichtenštejnsko Lithuania Litva Lord Howe Island Ostrov lorda Howea Luxembourg Lucembursko Macao Macao Macquarie Island Ostrov Macquarie Madagascar Madagaskar Madeira Islands ostrovy Madeira Malawi Malawi Maldives Maledivy Mali Mali Malpelo Island Ostrov Malpelo Malta Malta Mariana Islands Mariánské ostrovy Market Reef Market Reef Marquesas Islands Markézské ostrovy Marshall Islands Marshallovy ostrovy Martinique Martinik Mauritania Mauritánie Mauritius Mauricius Mayotte Mayotte Mellish Reef Mellish Reef Mexico Mexiko Micronesia Mikronésie Midway Island Ostrov Midway Minami Torishima Minami Torishima Moldova Moldavsko Monaco Monako Mongolia Mongolsko Montenegro Černá Hora Montserrat Montserrat Morocco Maroko Mount Athos Hora Athos Mozambique Mosambik Myanmar Myanmar N.Z. Subantarctic Is. Namibia Namibie Nauru Nauru Navassa Island Ostrov Navassa Nepal Nepál Netherlands Holandsko New Caledonia Nová Kaledonie New Zealand Nový Zéland Nicaragua Nikaragua Niger Niger Nigeria Nigérie Niue Niue Norfolk Island Ostrov Norfolk North Cook Islands Severní Cookovy ostrovy North Macedonia Severní Makedonie Northern Ireland Severní Irsko Norway Norsko Ogasawara Ogasawara Oman Omán Pakistan Pákistán Palau Palau Palestine Palestina Palmyra & Jarvis Islands Palmýra a ostrovy Jarvis Panama Panama Papua New Guinea Papua-Nová Guinea Paraguay Paraguay Peru Peru Peter 1 Island Ostrov Petr 1 Philippines Filipíny Pitcairn Island Ostrov Pitcairn Poland Polsko Portugal Portugalsko Pr. Edward & Marion Is. Pratas Island Ostrov Pratas Puerto Rico Portoriko Qatar Katar Republic of Korea Korejská republika Republic of Kosovo Kosovská republika Republic of South Sudan Republika Jižní Súdán Republic of the Congo Republika Kongo Reunion Island Ostrov Réunion Revillagigedo Revillagigedo Rodriguez Island Rodriguezův ostrov Romania Rumunsko Rotuma Island Ostrov Rotuma Rwanda Rwanda Saba & St. Eustatius Saba & St. Eustatius Sable Island Sable Island Samoa Samoa San Andres & Providencia San Andres & Providencie San Felix & San Ambrosio San Felix a San Ambrosio San Marino San Marino Sao Tome & Principe Svatý Tomáš a Princův ostrov Sardinia Sardinie Saudi Arabia Saudská arábie Scarborough Reef Útes Scarborough Scotland Skotsko Senegal Senegal Serbia Srbsko Seychelles Seychely Sierra Leone Sierra Leone Singapore Singapur Sint Maarten Svatý Martin Slovak Republic Slovenská republika Slovenia Slovinsko Solomon Islands Solomonovy ostrovy Somalia Somálsko South Africa Jižní Afrika South Cook Islands Jižní Cookovy ostrovy South Georgia Island Ostrov Jižní Georgie South Orkney Islands Jižní Orknejské ostrovy South Sandwich Islands Jižní Sandwichovy ostrovy South Shetland Islands Jižní Shetlandské ostrovy Sov Mil Order of Malta Maltézský řád Sov Mil Spain Španělsko Spratly Islands Spratlyho ostrovy Sri Lanka Srí Lanka St. Barthelemy Svatý Bartoloměj St. Helena Svatá Helena St. Kitts & Nevis Svatý Kryštof a Nevis St. Lucia Svatá Lucie St. Martin Svatý Martin St. Paul Island Ostrov svatého Pavla St. Peter & St. Paul Svatý Petr a Svatý Pavel St. Pierre & Miquelon St. Pierre a Miquelon St. Vincent Svatý Vincent Sudan Súdán Suriname Surinam Svalbard Svalbard Swains Island Ostrov Swains Sweden Švédsko Switzerland Švýcarsko Syria Sýrie Taiwan Tchaj-wan Tajikistan Tádžikistán Tanzania Tanzanie Temotu Province Provincie Temotu Thailand Thajsko The Gambia Gambie Timor - Leste Timor – Východní Togo Togo Tokelau Islands Tokelauské ostrovy Tonga Tonga Trindade & Martim Vaz Trindade a Martim Vazovi Trinidad & Tobago Trinidad a Tobago Tristan da Cunha & Gough Islands Tristan da Cunha a Goughovy ostrovy Tromelin Island Ostrov Tromelin Tunisia Tunisko Turkmenistan Turkmenistán Turks & Caicos Islands Ostrovy Turks a Caicos Tuvalu Tuvalu UK Base Areas on Cyprus US Virgin Islands Americké Panenské ostrovy Uganda Uganda Ukraine Ukrajina United Arab Emirates Spojené arabské emiráty United Nations HQ Sídlo Organizace spojených národů United States Spojené státy Uruguay Uruguay Uzbekistan Uzbekistán Vanuatu Vanuatu Vatican City Vatikán Venezuela Venezuela Vietnam Vietnam Wake Island Ostrov Wake Wales Wales Wallis & Futuna Islands Ostrovy Wallis a Futuna West Malaysia Západní Malajsie Western Kiribati Západní Kiribati Western Sahara Západní Sahara Willis Island Ostrov Willis Yemen Jemen Zambia Zambie Zimbabwe Zimbabwe DXCCSubmissionDialog DXCC Submission List Seznam pro DXCC podání Options Nastavení My DXCC Entity Má DXCC Země User Filter Uživatelský filtr Category Kategorie Mixed Smíšené CW CW Phone Fóne Digital Digi Confirmed by Potvrzeno LoTW LoTW Paper QSL Show Zobrazit Not Yet Submitted Dosud nepředloženo Submitted (Not Granted) Předloženo (neuznáno) Already Granted Již uznáno Band Scope Rozsah pásem Select 5-Band DXCC preset bands (80/40/20/15/10m) Vybrat přednastavená DXCC pásma 5-Band (80/40/20/15/10 m) 5-Band DXCC 5pásmové DXCC Select all DXCC-eligible bands Vybrat všechna DXCC způsobilá pásma All DXCC Bands Všechna DXCC pásma Bands Pásma Select options above and the list will update automatically. Vyberte možnosti výše a seznam se automaticky aktualizuje. Export the contacts listed above to an ADIF file Exportovat kontakty uvedené výše do souboru ADIF Export Export No User Filter Žádný uživatelský filtr Any Band (Entity Level) Libovolné pásmo (úroveň entity) 5-Band DXCC (80/40/20/15/10m) 5pásmové DXCC (80/40/20/15/10 m) Custom Band Selection Vlastní výběr pásem Entity Entita Prefix Prefix Callsign Značka Band Pásmo Mode Druh provozu Date Datum Submitted Předloženo Granted Uznáno Export ADIF Export ADIF No contacts to export. Žádné kontakty k exportu. Failed to retrieve contact records. Nepodařilo se načíst záznamy kontaktů. Export DXCC Submission List as ADIF Exportovat seznam DXCC podání jako ADIF any band libovolné pásmo 5-band 5pásmový all bands všechna pásma %1 selected band(s) %1 vybraných pásem No contacts match the selected criteria. Žádné kontakty neodpovídají zadaným kritériím. %1 %2 %3 — DXCC %4 / %5 %1 %2 %3 — DXCC %4 / %5 band-slot pásmo-slots entity entita entry záznam entries záznamů Data New Entity Nová země New Band Nové pásmo New Mode Nový druh provozu New Band&Mode Nové pásmo&druh New Slot Nový slot Confirmed Potvrzeno Worked Pracováno Hz Hz kHz kHz GHz GHz MHz MHz Yes Ano No Ne Requested Vyžádáno Queued Ve frontě Invalid Chybné Bureau Bureau Direct Direct Electronic Elektronicky Blank Nevyplněno Modified Upraveno Grayline Other Jiné Short Path Long Path Not Heard Neslyšeno Uncertain Nejistý Straight Key Sideswiper Mechanical semi-automatic keyer or Bug Mechanical fully-automatic keyer or Bug Single Paddle Single Paddle Dual Paddle Computer Driven Confirmed (AG) Potvrzeno (AG) Confirmed (no AG) Potvrzeno (non-AG) Unknown Neurčeno Aircraft Scatter Aircraft Scatte Aurora-E Aurora Pol. záře Back scatter EchoLink Earth-Moon-Earth Sporadic E F2 Reflection Field Aligned Irregularities Ground Wave Internet-assisted Ionoscatter IRLP Line of Sight Meteor scatter Terrestrial or atmospheric repeater or transponder Rain scatter Satellite Trans-equatorial Tropospheric ducting DateFormatDelegate Blank Nevyplněno DevToolsDialog Developer Tools Vývojářské nástroje Debug Log Ladicí log Logging Rules: Pravidla logování: e.g. qlog.ui.*.runtime=true např. qlog.ui.*.runtime=true Apply Použít Generate Debug File Vygenerovat ladicí soubor Export File Exportovat soubor SQL Console SQL konzole Open SQL Otevřít SQL Save SQL Uložit SQL Run SQL Spustit SQL Export As Exportovat jako Enter SQL query here... Ctrl+Return = run Zadejte SQL dotaz... Ctrl+Enter = spustit TXT CSV CSV ADI Open SQL Query Otevřít SQL dotaz SQL Files (*.sql);;All Files (*) SQL soubory (*.sql);;Všechny soubory (*) Open Error Chyba při otevírání Cannot open file: %1 Nelze otevřít soubor: %1 Save SQL Query Uložit SQL dotaz Save Error Chyba ukládání Cannot save file: %1 Nelze uložit soubor: %1 Query saved to %1 Dotaz uložen do %1 Error: %1 Chyba: %1 %1 row(s) shown (truncated at %2) in %3 ms Zobrazeno %1 řádků (zkráceno na %2) za %3 ms %1 row(s) returned in %2 ms Vráceno %1 řádků za %2 ms Export Export No results to export. Žádné výsledky k exportu. Export Error Chyba exportu Cannot open file for writing: %1 Nelze otevřít soubor pro zápis: %1 Exported %1 row(s) to %2 Exportováno %1 řádků do %2 Text Files (*.txt);;All Files (*) Textové soubory (*.txt);;Všechny soubory (*) CSV Files (*.csv);;All Files (*) CSV soubory (*.csv);;Všechny soubory (*) ADIF Export Export ADIF ADIF export requires the query to include the contacts 'id' column. Example: SELECT id, callsign FROM contacts WHERE ... No valid contact IDs found in the result set. Ve výsledcích nebyla nalezena žádná platná ID kontaktů. Failed to retrieve contact records: %1 Nepodařilo se načíst záznamy kontaktů: %1 No matching contacts found in the database. V databázi nebyly nalezeny žádné odpovídající kontakty. Logging rules cleared (defaults) Pravidla logování vymazána (výchozí) Logging rules applied Pravidla logování použita Save Debug Log Uložit ladicí log No debug log file is currently being written Aktuálně se nezapisuje žádný ladicí log soubor Log Files (*.log);;All Files (*) Log soubory (*.log);;Všechny soubory (*) Debug log saved to %1 Ladicí log uložen do %1 Failed to copy the debug log file. Nepodařilo se zkopírovat ladicí log soubor. File logging is disabled Ukládání logů do souboru je zakázáno Log file: %1 Log soubor: %1 DownloadQSLDialog Download QSLs Stáhnout QSL eQSL eQSL eQSL QTH Profile eQSL QTH Profil QSLs Since QSL od QSOs Since QSO od LoTW LoTW My Callsign Moje značka &Download &Stáhnout LoTW is not configured properly.<p> Please, use <b>Settings</b> dialog to configure it.</p> LoTW není správně nastaven.<p> Prosím, použijte dialog <b>Nastavení</b> pro konfiguraci služby.</p> eQSL is not configured properly.<p> Please, use <b>Settings</b> dialog to configure it.</p> eQSL není správně nastaven.<p> Prosím, použijte dialog <b>Nastavení</b> pro konfiguraci služby.</p> Cancel Zrušit Downloading from %1 Stahování z %1 Processing %1 QSLs Zpracovávám %1 QSL QLog Error Chyba QLog %1 update failed: Aktualizace %1 selhala: QLog Information Informace QLog No service selected Není vybrána žádná služba DxFilterDialog DX Cluster Filters Filtry DX Cluster General Filters Obecné Bands Pásmo FTx (FT8/FT4) FTx (FT8/FT4) Continent Kontinent North America Severní Amerika Africa Afrika Antarctica Antarktida South America Jižní Amerika Asia Asie Europe Evropa Oceania Oceánie Modes Druh provozu Phone Fóne CW CW Digital Digi Log Status Status v logu New Mode Nový druh provozu New Entity Nová země New Band Nové pásmo New Slot Nový slot Worked Pracováno Confirmed Potvrzeno Extended Filters Pokročilé Spotter Continent Kontinent Spottera Do not show the following spots when they have the same Callsign and their time difference is lower than Time Diff and their frequency difference is lower than Freq Diff Nezobrazovat následující spoty pokud mají stejnou značku a jejich rozdíl frekvencí a času je menší než nastavené parametry Spots Dedup Deduplikace Spotů Time difference Rozdíl časů s s Frequency difference Rozdíl frekvencí kHz Member Člen No Club List is enabled Není aktívní žádný Club List DxTableModel Time Čas Callsign Značka Frequency Frekvence Mode Mode Spotter Spotter Comment Poznámka Continent Kontinent Spotter Continent Kontinent Spottera Band Pásmo Member Člen Country Země DxWidget Send DX Cluster Command Odeslat příkaz do DX Clusteru Connect Připojit Insert a <b>hostname:port</b> of DXC Server. Vložte <b>hostname:port</b> DXC Serveru. Filtered Filtrováno Spots Spoty WCY WCY WWV WWV To All To All Console Konzole 15-min Trend Full-text search Fulltextové vyhledávání Search Hledat Close Search Zavřít vyhledávání Filter... Filtr... Spot Last QSO Spotni poslední QSO Send last QSO spot Odeslat poslední QSO spot Show HF Stats Zobrazit KV Stats Show VHF Stats Zobrazit VKV Stats Show WCY Zobrazit WCY Show WWV Zobrazit WWV Column Visibility... Zobrazení sloupců... Connect on startup Připojit při spuštění Automatic connection after start Automatické připojení po startu aplikace Delete Server Vymazat server DXC - Delete Server DXC - Vymazat server Clear Password Vymazat heslo Keep Spots Zachovat Spoty Spots are not cleared when connecting to a new DX Cluster. Při přepojení serverů nejsou spoty smazány. Clear Vymazat Clear all data Vše vymazat Search... Hledat... DXC - Search DXC- Hledat Which columns should be displayed Který sloupec by měl být zobrazen Filter DXC Filtr DXC My Continent Můj Kontinent Auto Automaticky Connecting... Připojování... DX Cluster is temporarily unavailable DX Cluster je dočasně nedostupný DXC Server Error Chyba DXC Serveru An invalid callsign Nesprávná značka DX Cluster Password DX Cluster Heslo Security Notice Bezpečnostní upozornění The password can be sent via an unsecured channel Heslo může být posláno přes nezabezpečený kanál Server Server Username Uživatelské jméno Disconnect Odpojit DX Cluster Command Příkaz DX Clusteru DxccTableModel Worked Pracováno eQSL eQSL LoTW LoTW Paper QSL DxccTableWidget Mode Druh provozu EQSLQSLDownloader Incorrect Password or QTHProfile Id Chybné heslo nebo QTHProfile Id ADIF file not found in eQSL response ADIF nenalezen v odpovědi z eQSL Incorrect Username or password Chybé uživatelské jméno nebo heslo Unknown Error Neznámá chyba Cannot opet temporary file Nelze otevřít dočasný soubor Cannot save the image to file Nelze uložit obrázek do souboru EQSLUploader Unknown Reply from eQSL Neznámá odpověď z eQSL EditActivitiesDialog Edit Activities Editor Aktivit Activities Aktivity Add Přidat Edit Upravit Remove Vymazat ExportDialog Export Selected QSOs Exportovat vybraná QSO CSV CSV POTA POTA Export Type Typ Exportu Column set Sada Sloupců Select one of the pre-defined sets of columns or define your own set of columns Vyberte jednu z předdefinovaných sad sloupců nebo definujte vlastní sadu sloupců Edit current set of columns Upravit aktuální sadu sloupců Edit Upravit Mark exported QSOs As Sent Označit exportované QSO jako Odeslané Export only QSOs that match the active filters Exportovat pouze QSO, která splňují nastavené filtry Filters Filtry Export only QSOs that match the selected date range Exportovat pouze QSO, která splnují zadaný časový interval Date Range Časové období Export only QSOs that match the selected My Callsign Exportovat pouze QSO, která mají vybrabou Moji značku My Callsign Moje značka Export only QSOs that match the selected My Gridsquare Exportovat pouze QSO, která mají vybraný Můj lokátor My Gridsquare Můj lokátor Export only QSOs that match the selected QSL Send Via value Exportovat QSO, která odpovídají vybrané hodnotě QSL Send Via QSL Send via QSL odeslat přes Include unusual QSO Sent statuses Zahrnout QSO se statusem odeslání Include Sent Status Zahrnout Status Odeslání Under normal circumstances this status means <b>"Ignore/Invalid"</b>.<br/>However, it may sometimes be wanted to ignore this setting when sending a QSL. Za normálních okolností tento status znamená <b>"Ignorovat"</b>.<br/>Avšak, někdy může být požadováno toto nastavení ignorovat. "Ignore" Ignorovat Under normal circumstances this status means <b>"do not send"</b>.<br/>However, it may sometimes be wanted to ignore this setting when sending a QSL. Za normálních okolností tento status znamená <b>Neodesílat</b>.<br/>Avšak, někdy může být požadováno toto nastavení ignorovat. "No" Neodesílat Resend already sent QSOs Znovu odeslat již odeslaná QSO Already Sent Již odeslaná User Filter Uživatelský filtr Export QSOs that match the selected user QSO Filter Exportovat QSO, která vyhovují vybranému QSO Filteru Station Profile Profil stanice File Soubor Browse Procházet ADX ADX JSON JSON Export QSOs Export QSO &Export &Export Export only QSOs matching this station profile Exportovat pouze QSO odpovídající tomuto profilu stanice Exporting... Exportuje se… Cancel Zrušit QLog Error Chyba QLog Cannot mark exported QSOs as Sent Nelze označit exportovaná QSO jako Odeslaná Generic Obecný QSLs QSL QSL-specific QSL specifické All Vše Minimal Minimální Custom 1 Uživatelské 1 Custom 2 Uživatelské 2 Custom 3 Uživatelské 3 ExportPasswordDialog Pack Data & Settings Zabalit data a nastavení Enter a password to encrypt stored credentials. The password will be needed to restore them later. Zadejte heslo pro zašifrování uložených přihlašovacích údajů. Toto heslo bude později potřeba pro jejich obnovení. Password Heslo Random Náhodné Confirm Password Potvrzení hesla Delete passwords from Credential Store after export Po exportu odstranit hesla z úložiště přihlašovacích údajů FlrigRigDrv Timeout Timeout FLRig response timeout Vypršel časový limit odezvy FLRig Network Error Chyba sítě HRDLogUploader Response message malformed Chyba v odpovědi HamQTHCallbook HamQTH HamQTH HamlibRigDrv None Žádné CAT CAT DTR DTR RTS RTS Initialization Error Chyba inicializace Cannot set PTT Type Nelze nastavit PTT Typ Cannot set PTT Share Nelze nastavit PTT Sdílení Cannot set CIV Addr Nelze nastavit CI-V adresu Unsupported Rig Driver Nepodporovaný ovladač Cannot set auto_power_on Nelze nastavit auto_power_on Cannot set no_xchg to 1 Nelze nastavit no_xchg na 1 Rig Open Error Spojení nelze navázat Set TX Frequency Error Chyba nastavení TX frekvence Set Frequency Error Chyba v nastavení frekvence Set Split Error Chyba nastavení split Set Mode Error Chyba nastavení režimu Set Split Mode Error Chyba nastavení split režimu Set PTT Error Chyba v získaní stavu PTT Cannot sent Morse This cannot be displayed Cannot stop Morse This cannot be displayed Get PTT Error This cannot be displayed Get Frequency Error Chyba v získání frekvence Get Mode Error Chyba v získání druhu provozu Get VFO Error Chyba v získání VFO Get PWR Error This cannot be displayed Get PWR (power2mw) Error This cannot be displayed Get RIT Function Error This cannot be displayed Get RIT Error This cannot be displayed Get XIT Function Error This cannot be displayed Get XIT Error This cannot be displayed Get Split Error Get TX Frequency Error Get KeySpeed Error This cannot be displayed Set KeySpeed Error This cannot be displayed HamlibRotDrv Initialization Error Chyba inicializace Unsupported Rotator Driver Nepodporovaný ovladač Rot Open Error Spojení nelze navázat Set Possition Error Chyba v nastavení pozice Get Possition Error Chyba v získání pozice ImportDialog Import Import Import all or only QSOs from the given period Importovat vše nebo vybraná QSO z vybraného časového období File Soubor Browse Procházet Defaults Výchozí Comment Komentář If DXCC is missing in the imported record, it will be resolved from the callsign. Pokud v importovaném záznamu chybí DXCC, bude doplněno podle volacího znaku. Fill missing DXCC Entity Information Doplnit chybějící DXCC informace o entitě My Profile Můj profil My Rig Můj Rig ADX ADX Date Range Časové období All vše Options Nastavení &Import &Import Select File Vybrat soubor The value is used when an input record does not contain the ADIF value Hodnota je použita v případě, když importovaný záznam má příslušné ADIF pole prázdné The values below will be used when an input record does not contain the ADIF values Hodnoty jsou použity v případě, když importovaný záznam má příslušné ADIF pole prázdné <p><b>In-Log QSO:</b></p><p> <p><b>QSO v logu:</b></p><p> <p><b>Importing:</b></p><p> <p><b>Importováno:</b></p><p> Duplicate QSO Duplicitní QSO <p>Do you want to import duplicate QSO?</p>%1 %2 <p>Přejete si importovat toto duplicitní QSO?</p>%1 %2 Save to File Uložit do souboru QLog Import Summary QLog Shrnutí Importu Import date Datum Importu Imported file Soubor importu Imported: %n contact(s) Importovano: %n kontaktů Importovano: %n kontakt Importovano: %n kontaktů Warning(s): %n Upozornění: %n Upozornění: %n Upozornění: %n Error(s): %n Chyb: %n Chyb: %n Chyb: %n Details Detaily Import Result Výsledek Importu Save Details... Uložit detaily... InputPasswordDialog Dialog Neprekladat - nezobrazuje se The password will be stored in the Credential Store. Heslo bude uloženo do uložiště hesel. Remember Password Zapamatovat heslo KSTChat Unknown User Neznámý uživatel Invalid password Nesprávné heslo KSTChatWidget Chat messages Chat zprávy Valuable messages Zprávy stojící za pozornost Clear selected Callsign and Chat message entry. Vymazat vybranou značku a zprávu ze vstupu. Send chat message Odeslat chat zprávu Column Visibility Zobrazení sloupců Which columns should be displayed Který sloupec by měl být zobrazen Prepare QSO Připravit QSO Transfer Callsign and Gridsquare Information to the New QSO dialog Přenese značku a lokátor do okna pro nové QSO Show About Me Only Zobrazit pouze o mně Show only messages where my callsign is present Zobrazit pouze zprávy, kde je moje volací značka Suppress User To User Potlačit Uživatel-Uživatel Suppress private messages between two callsigns Potlačí soukromé zprávy mezi dvěma uživateli Highlight Zvýraznit Highlight messages based on the setting Zvýraznit zprávy na základě nastavení Highlight Rules Pravidla Zvýraznění Beam Směrovat Clear Messages Vymazat zprávy Clear all messages in the window Vymazat všechny zprávy v okně You Vy KSTHighlightRuleDetail Edit Rule Upravit pravidlo Rule Name Jméno pravidla Chat Room Chat Enabled Aktivní Highlight message which match Zvýraznit zprávy odpovídající All the following conditions všem následující podmínkám Any of the following conditions jakékoliv z následujících podmínek Add Condition Přidat podmínku All Vše Sender Odesílatel Message Zpráva Gridsquare Lokátor Contains Obsahuje Starts with Začíná Remove Vymazat Must not be empty Nesmí být prázdné KSTHighlighterSettingDialog Highlight Rules Pravidla Zvýraznění Rules Pravidla Add Přidat Edit Upravit Remove Vymazat Name Jméno State Stav KeySequenceEdit Clear Vymazat LoadDatabaseDialog Unpack Data & Settings Rozbalit data a nastavení Warning Upozornění <html><head/><body><p>⚠ <span style=" font-weight:700;">Current database will be DELETED!</span><br/>⚠ <span style=" font-weight:700;">All stored passwords will be DELETED!</span><br/>⚠ <span style=" font-weight:700;">The application will restart after loading</span>.</p><p>To preserve data, use Pack Data &amp; Settings or Export QSOs first.</p></body></html> <html><head/><body><p>⚠ <span style=" font-weight:700;">Aktuální databáze bude SMAZÁNA!</span><br/>⚠ <span style=" font-weight:700;">Všechna uložená hesla budou SMAZÁNA!</span><br/>⚠ <span style=" font-weight:700;">Aplikace se po načtení restartuje</span>.</p><p>Pro zachování dat nejprve použijte Pack Data &amp; Settings nebo Export QSOs.</p></body></html> Select Database Vybrat databázi File: Soubor: Browse Procházet Status: No file selected Stav: Nebyl vybrán žádný soubor Decrypt Credentials Dešifrovat přihlašovací údaje Enter password to decrypt credentials Zadejte heslo pro dešifrování přihlašovacích údajů Password: Heslo: Load && Restart Načíst a restartovat Invalid password Nesprávné heslo Select Database File Vybrat soubor databáze QLog Database Export (*.dbe);;All Files (*) QLog export databáze (*.dbe);;Všechny soubory (*) Cannot create temporary file Nelze vytvořit dočasný soubor Decompressing database... Rozbaluje se databáze… Cannot decompress database file Nelze rozbalit soubor databáze Cannot open database Nelze otevřít databázi Valid database Platná databáze Different platform Jiná platforma Password required to import credentials Pro import přihlašovacích údajů je vyžadováno heslo No encrypted credentials in database V databázi nejsou žádné zašifrované přihlašovací údaje LogFormat Cannot find My DXCC Entity Info Nelze dohledat Mé DXCC Informace A minimal set of fields not present (start_time, call, band, mode, station_callsign) Zaznam neobsahuje minimální počet polí (start_time, call, band, mode, station_callsign) Outside the selected Date Range Mimo vybraný rozsah datumů Duplicate Duplicitní DXCC Info is missing Chybí informace o DXCC no Station Callsign present Značka stanice není k dispozici Cannot insert to database Nelze vložit do databáze Imported Importováno DXCC State: Stav DXCC: Error Chyba Warning Upozornění LogbookModel QSO ID QSO ID Time on Čas od Time off Čas do Call Značka RST Sent RST Sent RST Rcvd RST Rcvd Frequency Frekvence RSTs RSTs RSTr RSTr Band Pásmo Mode Druh provozu Submode Druh provozu QSLr QSLr QSLr Date Datum QSLr QSLs QSLs QSLs Date Datum QSLs LoTWr LoTWr LoTWr Date Datum LoTWr LoTWs LoTWs LoTWs Date Datum LoTWs TX PWR TX Výkon Address (ASCII) Adresa (ASCII) Altitude Nadmořská výška Gridsquare Extended Lokator rozšíření DOK DOK Distance Vzdálenost eQSLr Date Datu eQSLr eQSLs Date Datum eQSLs eQSLr eQSLr eQSLs eQSLs HamlogEU Upload Date Datum Nahrání HamlogEU HamlogEU Upload Status Stav nahrání HamlogEU HamQTH Upload Date Datum Nahrání HamQTH HamQTH Upload Status Stav nahrání HamQTH CW Key Info CW Klíč CW Key Type Typ CW Klíče My Altitude Má nadmořská výška My City (ASCII) Moje město (ASCII) My Country (ASCII) Moje země (ASCII) My Gridsquare Extended Muj lokator rozšíření My IOTA Island ID Moje IOTA Island ID My CW Key Info Můj CW klíč My CW Key Type Můj typ CW klíče My Name (ASCII) Mé jméno (ASCII) My Postal Code (ASCII) Mé směrovací číslo (ASCII) My POTA Ref Má POTA My Rig (ASCII) Můj Rig (ASCII) My Special Interest Activity (ASCII) My Spec. Interes Activity Info (ASCII) My Spec. Interest Activity Info Name Jméno Notes (ASCII) Poznámky (ASCII) Operator Callsign Značka Operátora QSLr Via QSLr Via QSLs Via QSLs Via QTH QTH Gridsquare Lokátor DXCC DXCC Country Země Continent Kontinent Name (ASCII) Jméno (ASCII) QTH (ASCII) QTH (ASCII) Country (ASCII) Země (ASCII) CQZ CQZ ITU ITU Prefix Prefix State Stát County Okres IOTA IOTA QRZ Download Date Datum stažení z QRZ QRZ Download Status Stav stahování QRZ QSLs Message (ASCII) Odchozí QSL Zpráva (ASCII) QSLs Message Odchozí QSL Zpráva QSLr Message Příchozí QSL Zpráva RcvPWR SIG (ASCII) SIG SIG SIG Info (ASCII) SIG Info SIG Info RcvNr RcvNr RcvExch RcvExch SentNr SentExch VUCC VUCC Web Web QSL Sent QSL Sent Additional Fields Nezařazená Pole Address Adresa Age Věk A-Index A-Index Antenna Az Azimut Antény Antenna El Elevace Antény Signal Path Cesta Signálu ARRL Section ARRL Section Award Submitted Award Granted Band RX RX Pásmo Contest Check Class Třída ClubLog Upload Date Datum Nahrání Clublog ClubLog Upload State Stav nahrání Clublog Comment (ASCII) Komentář (ASCII) Comment Komentář Region Region Rig (ASCII) Rig (ASCII) My ARRL Section My WWFF Můj WWFF WWFF WWFF Paper QSL LoTW LoTW eQSL eQSL QSL Received QSL přijato County Alt Okres Alt Contacted Operator Kontaktovaný Operátor Contest ID ID Závodu Credit Submitted Credit Granted DCLr Date Datum DCLr DCLs Date Datum DCLs DCLr DCLr DCLs DCLs Email Email Owner Callsign Vlastní značka eQSL AG eQSL AG FISTS Number FISTS CC EME Init EME Init Frequency RX RX Frekvence Guest Operator Hostující Operátor HRDLog Upload Date Datum Nahrání HRDLog HRDLog Upload Status Stav nahrání HRDLog IOTA Island ID IOTA Island ID K-Index K-Index Latitude Zeměpisná šířka Longitude Zeměpisná délka Max Bursts MS Shower Name Jméno MS roje My Antenna (ASCII) Moje anténa (ASCII) My Antenna Moje anténa My City Moje město My County Můj okres My County Alt Můj Okres Alt My Country Moje země My CQZ Moje CQZ My DARC DOK Můj DARC DOK My DXCC Moje DXCC My FISTS Můj FISTS My Gridsquare Můj lokátor My IOTA Moje IOTA My ITU Moje ITU My Latitude Moje zeměpisná šířka My Longitude Moje zeměpisná délka My Name Mé jméno My Postal Code Mé směrovací číslo My Rig Můj Rig My Special Interest Activity Má SIG My SOTA Moje SOTA My State Můj Stát My Street Moje ulice My USA-CA Counties My VUCC Grids Mé VUCC lokátory Notes Poznámky #MS Bursts #MS Burst #MS Pings #MS Ping POTA POTA Contest Precedence Propagation Mode Podmínky šíření Public Encryption Key Veřejný šifrovací klíč QRZ Upload Date Datum Nahrání QRZ QRZ Upload Status Stav nahrání QRZ QSL Message QSL zpráva QSL Via QSL Via QSO Completed Úplné QSO QSO Random Random QSO Rig Rig SAT Mode SAT Mode SAT Name Jméno SAT Solar Flux Solar Flux Silent Key Silent Key SKCC Member SKCC Member SOTA SOTA Logging Station Callsign Značka logující stanice SWL SWL Ten-Ten Number Ten-Ten Číslo UKSMG Member UKSMG Člen USA-CA Counties VE Prov LogbookWidget Delete Vymazat Logbook - Delete QSO Logbook - Vymazat QSO Upload to Clublog Nahrát do Clublog Lookup on Web Vyhledat na Webu Update from Callbook Aktualizovat z Callbooku Add Missing Info Doplnit chybějící informace Mark QSL RCVD Označit QSL přijato Mark QSL Requested Označit QSL požadováno Filter Callsign Vyhledat značku Edit Value Upravit hodnotu Logbook - Edit Value Logbook - Upravit hodnotu Column Visibility Zobrazení sloupců Which columns should be displayed Který sloupec by měl být zobrazen Export Selected Export vybraných Export selected QSOs Export vybraných QSO Send DX Spot Odeslat DX Spot Logbook - Send DX Spot Logbook - Odeslat DX Spot Callsign Značka Gridsquare Lokátor POTA POTA SOTA SOTA WWFF WWFF SIG SIG IOTA IOTA Delete the selected contacts? Vymazat vybraný kontakt? Clublog's <b>Immediately Send</b> supports only one-by-one deletion<br><br>Do you want to continue despite the fact<br>that the DELETE operation will not be sent to Clublog? Clublog <b>Okamžité odeslání</b> podporuje pouze mazání po jednom záznamu<br><br>Chcete pokračovat navzdory skutečnosti,<br>že operace DELETE nebude odeslána do Clublogu? Deleting QSOs Mazání QSO Update Aktualizace By updating, all selected rows will be affected.<br>The value currently edited in the column will be applied to all selected rows.<br><br>Do you want to edit them? Aktualizací budou ovlivněny všechny vybrané řádky<br>Aktuálněupravená hodnota ve sloupci se použije na všechny vybrané řádky.<br>Chcete je upravit? Count: %n Anzahl: %n Počet: %n Počet: %n Počet: %n Downloading eQSL Image Stahování eQSL obrázku Cancel Zrušit All Bands Všechna pásma All Modes Všechny módy All Countries Všechny země No User Filter Žádný uživatelský filtr QLog Warning Upozornění QLog Each batch supports up to 100 QSOs. Každá dávka podporuje až 100 QSO. QSOs Update Progress Progres aktualizace QLog Error Chyba QLog Callbook login failed Selhalo přihlášení do Callbooku Callbook error: Chyba Callbooku: All Clubs Všechny kluby eQSL Download Image failed: Stažení eQSL obrázku selhalo: LotwQSLDownloader Cannot open temporary file Nelze otevřít dočasný soubor Incorrect login or password Nesprávné přihlašovací jméno nebo heslo LotwUploader Upload cancelled by user Nahrání přerušeno uživatelem Upload rejected by LoTW Nahrání odmítnuto LoTW Unexpected response from TQSL server Neočekávaná odpověď z TQSL TQSL utility error Chyba TQSL TQSLlib error Chyba TQSLLib Unable to open input file Nelze otevřít vstupní soubor Unable to open output file Nelze otevřít výstupní soubor All QSOs were duplicates or out of date range Všechna QSO byla duplicitní nebo mimo časový rozsah Some QSOs were duplicates or out of date range Nekterá QSO byla duplicitní nebo mimo časový rozsah Command syntax error Chybný příkaz LoTW Connection error (no network or LoTW is unreachable) Chyba připojení k LoTW Unexpected Error from TQSL Neočekávaná chyba TQSL TQSL not found Nenalezen TQSL TQSL crashed TQSL neočekávaně skončil MainWindow &File &Soubor &Logbook &Logbook &Equipment &Zařízení &Help &Nápověda &Window Z&obrazení Se&rvice Sl&užby Toolbar Toolbar Clock Hodiny Map Mapa DX Cluster DX Cluster WSJTX WSJTX Rotator Rotátor Bandmap Bandmap Rig Rig Online Map Online Mapa CW Console CW Konzole Chat Chat Profile Image Profilová fotka &Settings &Nastavení &Import &Import &Export &Export Mailing List... Mailing List... Edit Upravit Save Arrangement Uložit uspořádání Keep Options Zachovat nastavení Restore connection options after application restart Obnovit možnosti připojení po restartu aplikace Connect R&ig Připojit R&ig Alerts Upozornění Quit Ukončit Application - Quit Aplikace - Ukončit Pack Data && Settings Zabalit data a nastavení Unpack Data && Settings Rozbalit data a nastavení New QSO - Clear Nový kontakt - Vymazat &About &O aplikaci New QSO - Save Nový kontakt - Uložit S&tatistics S&tatistiky QSL &Gallery QSL &Galerie Developer Tools Vývojářské nástroje Run custom read-only SQL queries against the logbook database Spouštět vlastní SQL dotazy pouze pro čtení nad databází logu Print QSL &Labels &Tisk QSL štítků Connect R&otator Připojit R&otátor QSO &Filters &Filtry QSO &Awards &Diplomy DXCC &Submission List &Seznam pro DXCC podání Generate a list of contacts to submit for ARRL DXCC award credit Vygenerovat seznam spojení pro předložení k uznání ARRL DXCC Beep Pípnutí Connect &CW Keyer Připojit &CW Klíč &Wiki &Wiki Report &Bug... Nahlásít &Bug... &Manual Entry &Ruční zadání Switch New Contact dialog to the manually entry mode<br/>(time, freq, profiles etc. are not taken from their common sources) Přepnout QSO okno do režimu ručního zadávání<br/>(hodnoty času, frekvence, profilů atd. nejsou přebírány z jejich běžných zdrojů) Logbook - Search Callsign Logbook - Vyhledat značku New QSO - Add text from Callsign field to Bandmap Nový kontakt - Přenést text z pole Callsign do Bandmap Rig - Band Down Rig - Pásmo dolů Rig - Band Up Rig - Pásmo nahoru New QSO - Use Callsign from the Whisperer Nový kontakt - Použít volací značku z Našeptávače CW Console - Key Speed Up CW Konzole - Rychlost klíče zvýšit CW Console - Key Speed Down CW Konzole - Rychlost klíče snížit CW Console - Profile Up CW Konzole - O profil výš CW Console - Profile Down CW Konzole - O profil níž Rig - PTT On/Off Rig - PTT On/Off All Bands Všechna pásma Each Band Každé pásmo Each Band && Mode Každé pásmo a mod No Check Bez kontroly Single Jedna pro vše Per Band Pro pásmo Stop Zastavit Reset Vymazat None Žádné Upload Nahrát Service - Upload QSOs Služba – Nahrávání QSO Download QSLs Stáhnout QSL Service - Download QSLs Služba - Stáhnout QSL Theme: Native Téma: Native Theme: QLog Light Téma: QLog Light Theme: QLog Dark Téma: QLog Dark What's New Novinky Export Cabrillo Export Cabrillo Edit Rules Upravit pravidla Contest Contest Dupe Check Kontrola Duplicit Sequence Sekvence Linking Exchange With Spojit Exchange s Clear Vymazat Show Alerts Zobrazit upozornění About O aplikaci Wsjtx Wsjtx Color Theme Barevné téma Not enabled for non-Fusion style Není povoleno pro jiný styl než Fusion Press to tune the alert Stiskni pro naladění alertu Clublog Immediately Upload Error Chyba Okamžitého nahrávání do Clublog <b>Error Detail:</b> <b>Detail chyby:</b> op: op: A New Version Nová verze A new version %1 is available. Je k dispozici nová verze %1. Remind Me Later Připomenout později Download Stáhnout Failed to encrypt credentials. Nepodařilo se zašifrovat přihlašovací údaje. Database files (*.dbe);;All files (*) Soubory databáze (*.dbe);;Všechny soubory (*) Failed to create temporary file. Nepodařilo se vytvořit dočasný soubor. Failed to dump the database. Nepodařilo se exportovat databázi. Compressing database... Komprimuje se databáze… Database successfully dumped to %1 Databáze byla úspěšně exportována do %1 Failed to compress the database. Nepodařilo se komprimovat databázi. Failed to prepare database for import. Nepodařilo se připravit databázi pro import. Classic Klasické Do you want to remove the Contest filter %1? Prejete si odstranit Contest filtr %1? Contest: Contest: <h1>QLog %1</h1><p>&copy; 2019 Thomas Gatzweiler DL2IC<br/>&copy; 2021-2026 Ladislav Foldyna OK1MLG<br/>&copy; 2025-2026 Michael Morgan AA5SH<br/>&copy; 2025-2026 Kyle Boyle VE9KZ</p><p>Based on Qt %2<br/>%3<br/>%4<br/>%5</p><p>Icon by <a href='http://www.iconshock.com'>Icon Shock</a><br />Satellite images by <a href='http://www.nasa.gov'>NASA</a><br />ZoneDetect by <a href='https://github.com/BertoldVdb/ZoneDetect'>Bertold Van den Bergh</a><br />TimeZone Database by <a href='https://github.com/evansiroky/timezone-boundary-builder'>Evan Siroky</a> <h1>QLog %1</h1><p>&copy; 2019 Thomas Gatzweiler DL2IC<br/>&copy; 2021-2026 Ladislav Foldyna OK1MLG<br/>&copy; 2025-2026Michael Morgan AA5SH<br/>&copy; 2025-2026 Kyle Boyle VE9KZ</p><p>Záloženo na Qt %2<br/>%3<br/>%4<br/>%5</p><p>Ikony <a href='http://www.iconshock.com'>Icon Shock</a><br />Satelitní snímky <a href='http://www.nasa.gov'>NASA</a><br />ZoneDetect <a href='https://github.com/BertoldVdb/ZoneDetect'>Bertold Van den Bergh</a><br />TimeZone <a href='https://github.com/evansiroky/timezone-boundary-builder'>Evan Siroky</a> N/A - MapWebChannelHandler Grid Lokátor Gray-Line Noc a Den Beam Aurora Pol. záře MUF MUF IBP IBP Chat WSJTX - CQ WSJTX - CQ Path Cesta NewContactWidget 59 59 Mode Mode Frequency Frekvence Callsign Značka TX: RX: 80m 80m Date Datum Reset Vymazat Save Uložit RSTr RSTr <b>DUPE !!!</b> <b>DUPE !!!</b> RSTs RSTs Lookup the call on the web. The query URL can be changed in Settings -> Callbook Vyhledet informace o značce na webu. URL dotazu lze změnit v Nastavení -> Callbook Web Web Time On Čas od Info Info D&X Stats D&X Stats <b>DXCC Statistics</b> <b>DXCC Statistiky</b> <b>Station Statistics</b> <b>Statistiky stanice</b> Member: Člen: World Wide Flora & Fauna World Wide Flora & Fauna QSL Send Status QSL Send Status Paper QSL <b>Yes</b> - an outgoing QSL card has been sent; the QSO has been uploaded to, and accepted by, the online service<br/><b>No</b> - do not send an outgoing QSL card; do not upload the QSO to the online service<br/><b>Requested</b> - the contacted station has requested a QSL card; the contacted station has requested the QSO be uploaded to the online service<br/><b>Queued</b> - an outgoing QSL card has been selected to be sent; a QSO has been selected to be uploaded to the online service<br/> <b>Odesláno</b> -QSL byl odeslán; QSO bylo nahráno a akceptováno online službou<br/><b>Neodesílat</b> -QSL nemá být odesláno; neodesílat do online služby<br/><b>Vyžádano</b> -protistanice vyžádala QSL; protistanice vyžádala opětovné nahrání do online služby<br/><b>Ve frontě</b> -QSL bylo zařazeno do fronty k vyřízení; QSO je ve frontě k nahrání do online služby<br/> LoTW LoTW eQSL eQSL QSL Send via QSL odeslat přes Blank Nevyplněno My &Notes Mé &poznámky &Details &Detaily M&y Station Mo&je Stanice Station Stanice the contacted station's DARC DOK (District Location Code) (ex. A01) DARC DOK (kód uzemí) (např A01) Duration Délka QSL via QSL Via Propagation Mode Podmínky šíření Rig Rig W W Antenna Anténa MHz MHz No Neodesílat Yes Odesláno Requested Vyžádáno Queued Ve frontě Ignored Ignorovat Bureau Bureau Direct Direct Electronic Elektronicky QLog Error Chyba QLog Callbook login failed Selhalo přihlášení do Callbooku LP LP New Entity! Nová země! New Band! Nové pásmo! New Mode! Nový druh provozu! New Band & Mode! Nové pásmo & druh! New Slot! Nový slot! Worked Pracováno Confirmed Potvrzeno GE GE GM GM GA GA m m Callbook search is active Hledání v Callbooku je aktivní Contest ID must be filled in to activate Pro aktivaci je nutné vyplnit Contest ID It is not the name of the contest but it is an assigned<br>Contest ID (ex. CQ-WW-CW for CQ WW DX Contest (CW)) Není to jméno Contestu ale jeho přiřazené Contest ID (např. CQ-WW-CW pro CQ WW DX Contest (CW)) Description of the contacted station's equipment Popis vybavení kontaktované stanice Callbook search is inactive Hledání v Callbooku není aktivní Expand/Collapse Rozbalit/sbalit two or four adjacent Maidenhead grid locators, each four characters long, (ex. EN98,FM08,EM97,FM07) dva nebo čtyři sousední lokátory, každý o délce čtyř znaků (např. EN98,FM08,EM97,FM07) Special Activity Group Special Activity Group Information OmnirigRigDrv Rig 1 Rig 1 Rig 2 Rig 2 Initialization Error Chyba inicializace Rig status changed Změna stavu Rigu Rig is not connected Rig není připojen OmnirigV2RigDrv Rig 1 Rig 1 Rig 2 Rig 2 Rig 3 Rig 3 Rig 4 Rig 4 Initialization Error Chyba inicializace Rig status changed Změna stavu Rigu Rig is not connected Rig není připojen PSTRotDrv Rot 1 Cannot bind a port Port nelze použít Cannot get IP Address for Nelze získat IP adresa pro No IPv4 Address for Neexistuje IPv4 adresa pro Error Occurred Došlo k chybě Operation Timeout Timeout PaperQSLDialog Manage QSL Card Správa QSL Available QSLs Dostupné QSL Copy the input file from the source folder to the QLog Internal QSL Storage folder.<br/>The original file remains unchanged in the source folder Zkopiruje vstupní soubor ze zdrojové složky do složky QLog uložiště QSL.<br/>Původní soubor zůstane ve zdrojové složce nezměněn Import QSL Import QSL Add File Přidat Remove Vymazat Delete Vymazat Delete QSL? Vymazat QSL? Open Otevřít Toggle Favorite Oblíbené PlatformSettingsDialog Platform-specific Settings Nastavení specifické pro platformu The database was exported from a different platform. Please verify or update the following settings. You can leave fields empty and configure them later in Settings. Databáze byla exportována z jiné platformy. Ověřte nebo aktualizujte následující nastavení. Pole můžete nechat prázdná a nastavit je později v Nastavení. Setting Nastavení Value Hodnota Continue Pokračovat Select File Vybrat soubor All Files (*) Všechny soubory (*) ProfileImageWidget Profile Image Profilová fotka QCoreApplication QLog Help QLog Help QMessageBox QLog Warning Upozornění QLog Club List Update failed. Cannot remove old records Nepovedlo se aktualizovat Club List. Není možné odstranit staré záznamy Club List Update failed. Cannot plan new downloads Nepovedlo se aktualizovat Club List. Není možné naplánovat stažení listů QLog Critical Chyba QLog Cannot save a password for %1 to the Credential Store Do uložiště hesel nelze uložit heslo pro %1 Cannot get a password for %1 from the Credential Store Z uložiště hesel nelze získat heslo pro %1 Unexpected Club List download. Canceling next downloads Neočekávaná odpověď Club Listu. Ruším aktualizaci Unexpected Club List content for Neočekávaný obsah Club List pro Network error. Cannot download Club List for Síťová chyba. Nepovedlo se stáhnout Club List pro QLog Error Chyba QLog QLog is already running QLog již běží Failed to process pending database import. Nepodařilo se zpracovat čekající import databáze. The database was imported successfully, but the stored passwords could not be restored (decryption failed or the data is corrupted). All service passwords have been cleared and must be re-entered in Settings. Databáze byla úspěšně importována, ale uložená hesla nebylo možné obnovit (dešifrování selhalo nebo jsou data poškozena). Všechna hesla služeb byla vymazána a musí být znovu zadána v Nastavení. Could not connect to database. Nelze se připojit k databázi. Could not export a QLog database to ADIF as a backup.<p>Try to export your log to ADIF manually Nelze exportovat QLog databázi do ADIF jako backup.<p>Pokuste se ručně exportovat log do ADIF Database migration failed. Migrace databaze selhala. DXC Server Name Error Chyba jména DXC Serveru DXC Server address must be in format<p><b>[username@]hostname:port</b> (ex. hamqth.com:7300)</p> Adresa DXC Serveru musí být ve formátu <p><b>[uživatel@]hostname:port</b> (např. hamqth.com:7300)</p> DX Cluster Password DX Cluster heslo Invalid Password Nesprávné heslo DXC Server Connection Error Chyba připojení k DXC serveru The fields <b>%0</b> will not be saved because the <b>%1</b> is not filled. Pole <b>%0</b> nebudou uložena, protože <b>%1</b> není vyplněno. Your callsign is empty. Please, set your Station Profile Vaše značka není vyplněna. Prosím, nastavte Profil Stanice QLog Info QLog Info Activity name is already exists. Jméno aktivity již existuje. Rule name is already exists. Pravidlo s tímto jménem již existuje. Callsign Regular Expression is incorrect. Regulární výraz pro značku je chybný. Comment Regular Expression is incorrect. Regulární výraz pro komentář je chybný. Cannot Update Alert Rules Nelze aktualizovat Pravidla upozornění Filter name is already exists. Filtr s tímto jménem již existuje. Please, define at least one Station Locations Profile Prosím, definujte alespoň jeden Profil Stanice WSJTX Multicast is enabled but the Address is not a multicast address. WSJTX Multicast je aktivní ale adresa není multicast IP adresa. Loop detected. Raw UDP forward uses the same port as the WSJT-X receiving port. Zjištěna smyčka. Raw UDP přesměrování používá stejný port jako příjem WSJT-X. Rig port must be a valid COM port.<br>For Windows use COMxx, for unix-like OS use a path to device Port musí být platný COM port.<br>Použijte COMxx pro Windows, pro ostatní cestu k souboru zařízení Rig PTT port must be a valid COM port.<br>For Windows use COMxx, for unix-like OS use a path to device PTT Port musí být platný COM port.<br>Použijte COMxx pro Windows, pro ostatní cestu k souboru zařízení <b>TX Range</b>: Max Frequency must not be 0. <b>Rozsah TX</b>: Koncová frekvence nesmí být 0. <b>TX Range</b>: Max Frequency must not be under Min Frequency. <b>Rozsah TX</b>: Koncová frekvence nesmí být menší než počáteční. Rotator port must be a valid COM port.<br>For Windows use COMxx, for unix-like OS use a path to device Port musí být platný COM port.<br>Použijte COMxx pro Windows, pro ostatní cestu k souboru zařízení CW Keyer port must be a valid COM port.<br>For Windows use COMxx, for unix-like OS use a path to device Port musí být platný COM port.<br>Použijte COMxx pro Windows, pro ostatní cestu k souboru zařízení Cannot change the CW Keyer Model to <b>Morse over CAT</b><br>No Morse over CAT support for Rig(s) <b>%1</b> Nelze změnit Model klíče na <b>Morse over CAT</b><br>Nasledující zařízení nepodporuji Morse over CAT support <b>%1</b> Cannot delete the CW Keyer Profile<br>The CW Key Profile is used by Rig(s): <b>%1</b> Nelze vymazat Profil Klíče<br>Profil je pouzívám temito zařízeními:<b>%1</b> Operator Callsign has an invalid format Značka operátora ma chybý format Gridsquare has an invalid format Lokátor má chybný formát VUCC Grids have an invalid format (must be 2 or 4 Gridsquares separated by ',') VUCC lokátor má neplatný formát (musí být 2 nebo 4 lokátory oddělené ',') Country must not be empty Zěme nesmí být prázdná CQZ must not be empty CQZ nesmí být prázdné ITU must not be empty ITU nesmí být prázdné Callsign has an invalid format Značka má chybný formát Filename is empty Jméno souboru je zprázdné Cannot write to the file Nelze zapsat do souboru QLog Information Informace QLog Exported. Exportováno. Exported %n contact(s). Exportován %n kontakt. Exporováno %n kontaktů. Exporováno %n kontaktů. <b>Rig Error:</b> <b>Chyba Rig:</b> <b>Rotator Error:</b> <b>Chyba Rotátoru:</b> <b>CW Keyer Error:</b> <b>Chyba CW Klíče:</b> Chat Error: Chyba v Chatu: Cannot update QSO Filter Conditions Nelze aktualizovat QSO Filter QObject Cannot connect to DXC Server <p>Reason <b>: Nelse se připojit k DXC serveru <p>Důvod <b>: Connection Refused Spojení odmítnuto Host closed the connection Server uzavřel spojení Host not found Server nenalezen Timeout Timeout Network Error Chyba sítě Internal Error Interní Chyba Importing Database Importuji databázi Opening Database Načítání Databáze Backuping Database Záloha Databáze Migrating Database Migrace Databáze Starting Application Start aplikace My Rig Můj Rig Logging Station Callsign Značka logující stanice My Gridsquare Můj lokátor Operator Name Jméno operátora Operator Callsign Značka operátora My City Mé město My Country Moje země My County Můj okres My IOTA Moje IOTA My SOTA Má SOTA My Special Interest Activity Má SIG My Spec. Interes Activity Info Mé SIG Info My VUCC Grids Mé VUCC lokátory My WWFF Můj WWFF My POTA Ref Má POTA My DARC DOK Můj DARC DOK My ITU Moje ITU My CQZ Moje CQZ My DXCC Moje DXCC <b>Imported</b>: %n contact(s) <b>Importován</b>: %n kontakt <b>Importováno</b>: %n kontaktů <b>Importováno</b>: %n kontaktů <b>Warning(s)</b>: %n <b>Upozornění</b>: %n <b>Upozornění</b>: %n <b>Upozornění</b>: %n <b>Error(s)</b>: %n <b>Chyb</b>: %n <b>Chyb</b>: %n <b>Chyb</b>: %n km km miles mil Not a valid QLog database Neplatná databáze QLog Database version too new (requires newer QLog version) Verze databáze je příliš nová (vyžaduje novější verzi QLog) Database is not QLog Export file Databáze není soubor QLog Export TQSL Path Cesta k TQSL (Flatpak internal path) (Interní cesta Flatpak) Rig Rig Rig PTT Importazione database Rig rigctld Rig rigctld Rotator Rotátor CW Keyer CW Klíč TOTAL Worked Celkem Pracováno TOTAL Confirmed Celkem Potvrzeno Confirmed Potvrzeno Worked Pracováno QRZCallbook QRZ.com QRZ.com QRZUploader General Error Obecná chyba QSLGalleryDialog QSL Card Gallery Galerie QSL lístků Search by callsign... Hledat podle volací značky… Sort by: Seřadit podle: Export Filtered Exportovat filtrované Date (Newest) Datum (nejnovější) Date (Oldest) Datum (nejstarší) Callsign (A-Z) Volací značka (A-Z) Callsign (Z-A) Volací značka (Z-A) %n QSL card(s) %n QSL lístek %n QSL lísteky %n QSL lísteků All QSL Cards Všechny QSL lístky Favorites Oblíbené By Country Podle země By Date Podle data By Band Podle pásma By Mode Podle módu By Continent Podle kontinentu Remove from Favorites Odebrat z oblíbených Add to Favorites Přidat mezi oblíbené Open Otevřít Save... Uložit… Save QSL Card Uložit QSL lístek Export QSL Cards Exportovat QSL lístky Exported %1 of %2 cards Exportováno %1 z %2 lístků QSLImportStatDialog QSL Import Summary Shrnutí importu QSL Summary Shrnutí Downloaded: Stažených: Updated: Aktualizovaných: Unmatched: Nespárovaných: Errors: Chyb: Details Detaily New QSLs: Nové QSL: Updated QSOs: Aktualizované QSO: Unmatched QSLs: Nespárované QSL: QSLPrintLabelDialog Print QSL Labels Tisk QSL štítků Filter Filtr Date Range Časové období My Callsign Moje značka Station Profile Profil stanice QSL Sent QSL Sent User Filter Uživatelský filtr Label Template Šablona štítku Page Size: Velikost stránky: Columns: Sloupce: Rows: Řádka: Label Width: Šířka štítku: Label Height: Výška štítku: Left Margin: Levý okraj: Top Margin: Horní okraj: H Spacing: Vodorovná mezera: V Spacing: Svislá mezera: Label Appearance Vzhled štítku Print Label Borders Tisk okrajů štítků QSOs per Label: QSO na štítek: Footer Left Text: Levý text zápatí: Footer Right Text: Pravý text zápatí: Skip Label: Přeskočit štítek: Sans Font: Bezpatkové písmo: Mono Font: Neproporcionální písmo: Callsign Size: Velikost volací značky: "To Radio" Size: Velikost „To Radio“: "To Radio" Text: Text „To Radio“: Header Size: Velikost záhlaví: Data Size: Velikost dat: Date Header Text: Text záhlaví data: Date Format: Formát data: Time Header Text: Text záhlaví času: Band Header Text: Text záhlaví pásma: Mode Header Text: Text záhlaví módu: QSL Header Text: Text záhlaví QSL: Extra Column: Další sloupec: Extra Column Text Text dalšího sloupce (DB column name) (název sloupce DB) No matching QSOs found Nebyla nalezena žádná odpovídající QSO Page 0 of 0 Stránka 0 z 0 Labels: 0 (0 pages) Štítky: 0 (0 stránek) Print Tisk Export as PDF Exportovat jako PDF Custom Vlastní Empty Prázdné QSOs matching this station profile QSO odpovídající tomuto profilu stanice Labels: %1 (%2 pages) Štítky: %1 (%2 stránek) Page %1 of %2 Stránka %1 z %2 Export PDF Export PDF PDF Files (*.pdf) Soubory PDF (*.pdf) Mark as Sent Označit jako odeslané Mark printed/exported QSOs as sent? Označit vytištěná/exportovaná QSO jako odeslaná? QSODetailDialog Frequency Frekvence dd/MM/yyyy HH:mm:ss dd/MM/yyyy HH:mm:ss Time On Čas od Time Off Čas do TX: TX: MHz MHz RX: Blank Nevyplněno Callsign Značka Mode Druh provozu RSTs RSTs RSTr RSTr Band Pásmo QTH QTH Name Jméno Comment Komentář My Notes Mé poznámky about:blank &Details &Detaily Country Země Cont Kontinent ITU ITU CQ CQ State Stát County Okres Age Věk AF AF AN AN AS AS EU EU NA NA OC OC SA SA IOTA IOTA POTA POTA SOTA SOTA SIG SIG FISTS FISTS SKCC SKCC Ten-Ten Ten-Ten UKSMG UKSMG FISTS CC D&X Stats D&X Stats <b>DXCC Statistics</b> <b>DXCC Statistiky</b> <b>Station Statistics</b> <b>Statistiky stanice</b> Operator Callsign Značka operátora QSLr Message Příchozí QSL Zpráva QSLs Message Odchozí QSL Zpráva Contest ID ID Závodu Member: Člen: SIG Info SIG Info DOK DOK the contacted station's DARC DOK (District Location Code) (ex. A01) DARC DOK (kód uzemí) (např A01) VUCC VUCC Gridsquare Lokátor two or four adjacent Gridsquares, each four characters long, (ex. EN98,FM08,EM97,FM07) dva nebo čtyři sousední lokátory, každý o délce čtyř znaků (např. EN98,FM08,EM97,FM07) WWFF WWFF E-Mail E-Mail URL URL Propagation Mode Podmínky šíření Satellite Name Jméno Satelitu Satellite Mode Satelitní Mode M&y Station Mo&je Stanice Operator Name Jméno operátora Rig Rig Antenna Anténa Power Výkon W W Sig Info Sig Info &QSL - - Manage QSL Card Správa QSL <b>Yes</b> - an outgoing QSL card has been sent; the QSO has been uploaded to, and accepted by, the online service<br/><b>No</b> - do not send an outgoing QSL card; do not upload the QSO to the online service<br/><b>Requested</b> - the contacted station has requested a QSL card; the contacted station has requested the QSO be uploaded to the online service<br/><b>Queued</b> - an outgoing QSL card has been selected to be sent; a QSO has been selected to be uploaded to the online service<br/> <b>Odesláno</b> -QSL byl odeslán; QSO bylo nahráno a akceptováno online službou<br/><b>Neodesílat</b> -QSL nemá být odesláno; neodesílat do online služby<br/><b>Vyžádano</b> -protistanice vyžádala QSL; protistanice vyžádala opětovné nahrání do online služby<br/><b>Ve frontě</b> -QSL bylo zařazeno do fronty k vyřízení; QSO je ve frontě k nahrání do online služby<br/> QSL Sent via QSL odesláno via Received Přijato Paper QSL LoTW LoTW eQSL eQSL QSL via QSL Via Sent Odesláno Show QSL Card Zobrazit QSL <b>Yes</b> - an incoming QSL card has been received; the QSO has been confirmed by the online service<br/><b>No</b> - an incoming QSL card has not been received; the QSO has not been confirmed by the online service<br/><b>Requested</b> - the logging station has requested a QSL card; the logging station has requested the QSO be uploaded to the online service<br/> <b>Ano</b> - QSL byl přijat; QSO bylo potvrzeno online službou<br/><b>Ne</b> - QSL nebylo přijato; nebylo potvrzeno online službou<br/><b>Vyžádano</b> - QSL vyžádáno; vyžádáno opětovné nahrání do online služby<br/> &Contest &Závod RcvNr RcvNr RcvExch RcvExch SendNr SendNr SendExch SendExch &Reset &Reset &Lookup &Vyhledat No Neodesílat Yes Odesláno Requested Vyžádáno Queued Ve frontě Ignored Ignorovat Bureau Bureau Direct Direct Electronic Elektronicky Submit changes Uložit změny Really submit all changes? Opravdu uložit všechny změny? QLog Error Chyba QLog Cannot save all changes - internal error Nepovedlo se uložit všechny změny - interní chyba Cannot save all changes - try to reset all changes Nepovedlo se uložit všechny změny - zkuste reset všech změn QSO Detail QSO Detail Edit QSO Úprava QSO Downloading eQSL Image Stahování eQSL obrázku Cancel Zrušit eQSL Download Image failed: Stažení eQSL obrázku selhalo: DX Callsign must not be empty Značka nesmí být prazdná DX callsign has an incorrect format Značka má chybný formát TX Frequency or Band must be filled TX Frekvence nebo Pásmo musí být vyplněno DX Grid has an incorrect format Lokátor má chybný formát Based on callsign, DXCC Country is different from the entered value - expecting Na základě značky DXCC Zěme nemá správné ID - očekáváno Based on callsign, DXCC Continent is different from the entered value - expecting Na základě značky DXCC Kontinent nemá správné ID - očekáváno Based on callsign, DXCC ITU is different from the entered value - expecting Na základě značky ITU nemá správné ID - očekáváno Based on callsign, DXCC CQZ is different from the entered value - expecting Na základě značky CQZ nemá správné ID - očekáváno Based on Frequencies, Sat Mode should be Satelitní Mode se na základě frekvencí liší od zadané hodnoty - očekáváno blank Nevyplněno Sat name must not be empty Jméno Satelitu nesmí být prázdné Own VUCC Grids have an incorrect format Vlastní VUCC má špatný formát Based on own callsign, own DXCC ITU is different from the entered value - expecting Vlastní DXCC ITU se na základě vlastní volací značky liší od zadané hodnoty - očekáváno Based on own callsign, own DXCC CQZ is different from the entered value - expecting Vlastní DXCC CQZ se na základě vlastní volací značky liší od zadané hodnoty - očekáváno Based on own callsign, own DXCC Country is different from the entered value - expecting Vlastní DXCC Země se na základě vlastní volací značky liší od zadané hodnoty - očekáváno LoTW Sent Status to <b>No</b> does not make any sense if QSL Sent Date is set. Set Date to 1.1.1900 to leave the date field blank Pole LoTW Odesláno nastavené na <b>Neodesílat</b> nedává smysl pokud je nastaveno datum odeslání QSL. Nastavte datum na 1.1.1900, aby pole datum zůstalo prázdné Date should be present for LoTW Sent Status <b>Yes</b> Datum by měl být nastavenen v případě LoTW Sent Status <b>Odesláno</b> eQSL Sent Status to <b>No</b> does not make any sense if QSL Sent Date is set. Set Date to 1.1.1900 to leave the date field blank Pole eQSL Odesláno nastavené na <b>Neodesílat</b> nedává smysl pokud je nastaveno datum odeslání QSL. Nastavte datum na 1.1.1900, aby pole datum zůstalo prázdné Date should be present for eQSL Sent Status <b>Yes</b> Datum by měl být nastavenen v případě eQSL Sent Status <b>Odesláno</b> Paper Sent Status to <b>No</b> does not make any sense if QSL Sent Date is set. Set Date to 1.1.1900 to leave the date field blank Pole Odesláno nastavené na <b>Neodesílat</b> nedává smysl pokud je nastaveno datum odeslání QSL. Nastavte datum na 1.1.1900, aby pole datum zůstalo prázdné Date should be present for Paper Sent Status <b>Yes</b> Datum by měl být nastavenen v případě Sent Status <b>Odesláno</b> VUCC has an incorrect format VUCC má chybný formát TX Band should be TX Pásmo by mělo být RX Band should be RX Pásmo by mělo být Own Callsign must not be empty Vlastní značka nesmí být prázdná Own callsign has an incorrect format Vlastní značka má špatný formát Based on SOTA Summit, QTH does not match SOTA Summit Name - expecting Na základě SOTA Summit, QTH neodpovídat SOTA definici - očekáváno Based on SOTA Summit, Grid does not match SOTA Grid - expecting Na základě SOTA Summit, Lokátor neodpovídat SOTA definici - očekáváno Based on POTA record, QTH does not match POTA Name - expecting Na základě POTA Summit, QTH neodpovídat POTA definici - očekáváno Based on POTA record, Grid does not match POTA Grid - expecting Na základě POTA Summit, Lokátor neodpovídat POTA definici - očekáváno Based on SOTA Summit, my QTH does not match SOTA Summit Name - expecting Na základě SOTA Summit, Mé QTH neodpovídat SOTA definici - očekáváno Based on SOTA Summit, my Grid does not match SOTA Grid - expecting Na základě SOTA Summit, Můj Lokátor neodpovídat SOTA definici - očekáváno Based on POTA record, my QTH does not match POTA Name - expecting Na základě POTA Summit, Mé QTH neodpovídat POTA definici - očekáváno Based on POTA record, my Grid does not match POTA Grid - expecting Na základě POTA Summit, Můj Lokátor neodpovídat POTA definici - očekáváno Callbook error: Chyba Callbooku: <b>Warning: </b> <b>Upozornění: </b> Validation Kontrola Yellow marked fields are invalid.<p>Nevertheless, save the changes?</p> Žlutě vyznačená pole obsahují neplatné hodnoty<p>I přes to uložit změny?</p> &Save &Uložit &Edit U&pravit QSOFilterDetail QSO Filter Detail Detail Filtru QSO Filter Name: Jméno filtru: Find QSO which match Najít QSO, která odpovídají All the following conditions všem následující podmínkám Any of the following conditions jakékoliv z následujících podmínek Add Condition Přidat podmínku Equal Rovná se Not Equal Nerovná se Contains Obsahuje Not Contains Neobsahuje Greater Than Větší než Less Than Menší než Starts with Začíná RegExp RegExp Remove Vymazat Must not be empty Nesmí být prázdné QSOFilterDialog QSO Filters QSO Filtry Filters Filtry Add Přidat Edit Upravit Remove Vymazat Rig No Rig Profile selected Není vybrán žádný Rig profil Rigctld Error Chyba Rigctld Initialization Error Chyba inicializace Internal Error Interní Chyba Cannot open Rig Rig nelze připojit RigWidget Form Form RX RX Disconnected Odpojeno MHz MHz Disable Split Vypnout split RIT: 0.00000 MHz RIT: 0.00000 MHz XIT: 0.00000 MHz XIT: 0.00000 MHz PWR: %1W PWR: %1W RigctldAdvancedDialog Rigctld Advanced Settings Pokročilá nastavení Rigctld Rigctld Path: Cesta k rigctld: Leave empty for auto-detection Nechte prázdné pro automatickou detekci Browse Procházet Auto-detect Rigctld path Automaticky zjistit cestu k Rigctld Auto-Detect Automatická detekce Rigctld Version: Verze Rigctld: Additional Arguments: Další parametry: e.g. -v -v for verbose logging např. -v -v pro podrobné protokolování Cannot be changed Nelze změnit Auto Detect Automatická detekce rigctld was not found on this system. Please install Hamlib or specify the path manually. rigctld nebyl na tomto systému nalezen. Nainstalujte prosím Hamlib nebo zadejte cestu ručně. Executable (*.exe);;All files (*.*) Spustitelný soubor (*.exe);;Všechny soubory (*.*) All files (*) Všechny soubory (*) Select rigctld executable Vybrat spustitelný soubor rigctld Not found Nenalezeno RigctldManager rigctld executable not found in /app/bin/. This should not happen in Flatpak build. Spustitelný soubor rigctld nebyl nalezen v /app/bin/. V Flatpak verzi by se toto nemělo stát. rigctld executable not found. Please install Hamlib or specify the path in Advanced settings. Spustitelný soubor rigctld nebyl nalezen. Nainstalujte prosím Hamlib nebo zadejte cestu v Pokročilém nastavení. Hamlib major version mismatch: QLog was compiled with Hamlib %1 but rigctld reports version %2.%3.%4. Rig model IDs are incompatible between major versions. Nesoulad hlavní verze Hamlib: QLog byl kompilován s Hamlib %1, ale rigctld hlásí verzi %2.%3.%4. ID modelů transceiverů nejsou kompatibilní mezi hlavními verzemi. Port %1 is already in use. Another rigctld or application may be running on this port. Port %1 je již používán. Na tomto portu může běžet jiný rigctld nebo aplikace. rigctld started but not responding on port %1. rigctld byl spuštěn, ale na portu %1 neodpovídá. Failed to start rigctld: %1 %2 Nepodařilo se spustit rigctld: %1 %2 rigctld crashed. rigctld havaroval. rigctld timed out. rigctld vypršel časový limit. Write error with rigctld. Chyba zápisu do rigctld. Read error with rigctld. Chyba čtení z rigctld. Unknown rigctld error. Neznámá chyba rigctld. Rotator No Rotator Profile selected Není vybrán žádný Rot profil Initialization Error Chyba inicializace Internal Error Interní Chyba Cannot open Rotator Rotátor nelze připojit RotatorWidget Form Form Az: Az: ° ° Goto Goto Previous Button Profile Předchozí profil tlačítka Next Button Profile Následující profil tlačítka QSO LP QSO Long Path QSO SP QSO Short Path SettingsDialog Settings Nastavení Station Stanice Profile Name Jméno profilu Delete Vymazat QTH QTH Add Přidat Callsign Značka Rigs Rigs Antennas Antény Operator name (Optional parameter) Jméno operátora (nepovinný parametr) Station Gridsquare (Mandatory parameter) Lokátor Stanice (povinný parametr) Callsign (Mandatory parameter) Značka (povinný parametr) Gridsquare Lokátor List of all available Station Profiles Seznam všech dostupných profilů stanice SOTA (Optional parameter) SOTA (nepovinný parametr) SOTA SOTA SIG SIG IOTA IOTA IOTA (Optional parameter) IOTA (nepovinný parametr) SIG Information (Optional parameter) SIG Informace (nepovinný parametr) VUCC VUCC QTH Name (Optional parameter) Jméno QTH (nepovinný parametr) VUCC Grids (Optional parameter). Ex. EN98,FM08,EM97,FM07 VUCC lokátor (nepovinný perametr). Příklad EN98,FM08,EM97,FM07 List of all available Rigs Seznam všech dostupných Rig List of all available Antennas Seznam všech dostupných antén Model Model Port Port Baudrate Baudrate 1200 1200 2400 2400 4800 4800 9600 9600 19200 19200 38400 38400 57600 57600 RX Offset (RIT) RX Offset (RIT) MHz MHz TX Offset (XIT) TX Offset (XIT) Rotators Rotátory 115200 115200 Data Bits Data Bits Profiles Profily World Wide Flora & Fauna (Optional parameter) World Wide Flora & Fauna (nepovinný parametr) Operator Name Jméno operátora WWFF WWFF Equipment Zařízení Profiles Profily Description Popis Azimuth Beamwidth Šířka svazku Azimuth Offset Posun Azimutu Valid range value is 0° - 100° (0° Unspecified) Platný rozsah hodnot je 0° - 100° (0° nespecifikováno) Unspecified Nespecifikováno Default Speed Výchozí rychlost WPM WPM CW Shortcut Profiles CW ShortCut Profily F1 F1 Short Desciption of the Button (up to 7 chars) Kratký popisek tlačítka (max 7 znaků) F2 F2 F3 F3 F4 F4 F5 F5 F6 F6 F7 F7 Minimum and maximum TX frequencies. Specific ranges are derived from allowed Band in the Setting. Minimální a maximální TX frekvence. Specifické rozsahy např. ovládání Rigu, se odvozují z povolených pásem v záložce Pásma. TX Range Rozsah TX - - Enter manually RIT or Transverter Offset Vložte RIT nebo transverter offset Enter manually XIT or Transverter offset Vložte XIT nebo transverter offset Blank Nevyplněno CW Speed Sync Sync CW Rychlosti 5 5 6 6 7 7 8 8 Stop Bits Stop Bits 1 1 2 2 Flow Control Flow Control None None Hardware Hardware Software Software Parity Parity No No Even Even Odd Odd Space Space Mark Mark Poll Interval Poll Interval ms ms Host Name Název serveru HamLib does not support to change a destination port. HamLib nepodporuje změnu cílového portu. Default PWR Výchozí PWR Enter default PWR (ex. when Rig is disconnected) Vložte výchozí PWR (např. když je Rig offline) W W Mode Druh provozu Freq Frekvence PTT State Stav PTT POTA POTA Profile name that is used as the alias for the Callsign, Gridsquare, Operator name, and QTH (required parameter) Název profilu, který se používá jako alias pro volací značku, lokátor, jméno operátora a QTH (povinný parametr) SIG (Optional parameter). SIG (Optional parameter). SIG Info SIG Info CW Keyers CW Klíče Keyer Profiles Profil klíče List of all available CW Keyers Seznam všech dostupných klíčů Keyer Mode Režim klíče N/A - Assigned CW Keyer Přiřazený klíč CW Keyer Speed Rychlost CW Klíče Port Type Typ Portu Serial Serial User Buttons Profiles Uživatelská tlačítka Button 1 Tlačítko 1 Button 2 Tlačítko 2 Button 3 Tlačítko 3 Button 4 Tlačítko 4 ° ° ITU ITU CQZ CQZ Operator Callsign Značka operátora Country Země Station Callsign Značka logující stanice Callsign of operator (Optional parameter, if different from station callsign) Volací znak operátora (volitelný, pokud se liší od volacího znaku stanice) County Okres Station County Location (Optional parameter) Okres (nepovinný parametr) DOK DOK Swap Paddles Prohodit pádla Interface Interface Offsets Offset PTT Type Typ PTT PTT Port PTT Port DX Spots to Rig DX Spoty do Rigu Paddle Only Sidetone Sidetone Freq: Frek. sidetone: <DXCALL> = DX Callsign <NAME> = DX Operator Name <RST> = Report 599 <RSTN> = Report 5NN <GREETING> = Greenting GM/GA/GE <QTH> = QTH <MYCALL> = My Callsign <MYNAME> = My Name <MYQTH> = My QTH <MYLOCATOR> = My Gridsquare <MYGRID> = My Gridsquare <MYSIG> = My SIG <MYSIGINFO> = My SIG Information <MYIOTA> = My IOTA <MYSOTA> = MY SOTA <MYWWFT> = My WWFT <MYVUCC> = MY VUCC <MYPWR> = My Power in W <EXCHSTR> = Contest Exchange Message <EXCHNR> = Contest Exchange Serial Number <EXCHNRN> = Contest Exchange Serial Number (9→N, 0→T) <+> = Speed +5 WPM (<++> = +10 WPM, etc.) <-> = Speed -5 WPM (<--> = -10 WPM, etc.) <DXCALL> = DX značka <NAME> = DX Jméno operátora <NAME> = DX Jméno operátora <RST> = Report 599 <RSTN> = Report 5NN <GREETING> = Pozdrav GM/GA/GE <QTH> = QTH <MYCALL> = Má značka <MYNAME> = Mé jméno <MYQTH> = Mé QTH <MYLOCATOR> = Můj Lokátor <MYGRID> = Můj Lokátor <MYSIG> = Můj SIG <MYSIGINFO> = Mé SIG Info <MYIOTA> = Má IOTA <MYSOTA> = Má SOTA <MYWWFT> = Mé WWFT <MYVUCC> = Mé VUCC <MYPWR> = Můj Výkon ve W <EXCHSTR> = Contest Exchange <EXCHNR> = Contest Exchange Serial Number <EXCHNRN> = Contest Exchange Serial Number (9→N, 0→T) RST> = Report 599 <RSTN> = Report 5NN <GREETING> = Pozdrav GM/GA/GE <QTH> = QTH <MYCALL> = Má značka <MYNAME> = Mé jméno <MYQTH> = Mé QTH <MYLOCATOR> = Můj Lokátor <MYGRID> = Můj Lokátor <MYSIG> = Můj SIG <MYSIGINFO> = Mé SIG Info <MYIOTA> = Má IOTA <MYSOTA> = Má SOTA <MYWWFT> = Mé WWFT <MYVUCC> = Mé VUCC <MYPWR> = Můj Výkon ve W <EXCHSTR> = Contest Exchange <EXCHNR> = Contest Exchange Serial Number <EXCHNRN> = Contest Exchange Serial Number (9→N, 0→T) <+> = Rychlost +5 WPM (<++> = +10 WPM, atd.) <-> = Rychlost -5 WPM (<--> = -10 WPM, atd.) RX: RX: TX: TX: Split Start rigctld daemon to share rig with other applications (e.g. WSJT-X) Spustit démon rigctld pro sdílení transceiveru s jinými aplikacemi (např. WSJT-X) Share Rig via port Sdílet transceiver přes port Advanced... Pokročilé… Rig Port Port Rig CIV Addr CIV Addr Auto Automaticky RTS RTS DTR DTR Web Lookup Button Hledání na Webu URL URL Specify the URL to use for quick search. The <DXCALL> macro will be replaced by the current callsign Zadejte URL, kterou chcete použít pro rychlé vyhledávání. Makro <DXCALL> bude nahrazeno aktuální volací značkou Test URL with your Callsign Test URL s vlastní značkou Test Test Clubs Kluby Active Lists Seznam aktivních Immediately Upload Okamžítý Upload HRDLog HRDLog It is not a password. It is the upload code received via email after the registration to HRDLOG..net Toto není heslo. Toto je kód pro nahrávání, který jste obdrželi e-mailem po registraci na HRDLOG.net Upload Code Upload Code If it is enabled and Rig is connected then QLog periodically sends On-Air messages to HRDLog Pokud je povoleno a Rig je připojen, pak QLog pravidelně odesílá zprávy On-Air do HRDLog Send On-Air Odesílat On-Air Leave empty for auto-detection Nechte prázdné pro automatickou detekci Auto-detect TQSL path Automaticky zjistit cestu k TQSL Auto-Detect Automatická detekce TQSL Version Verze TQSL Default API Key Výchozí klíč API Callsign-specific API Keys API klíče specifické pro volací značku Wavelog Wavelog API Key Klíč API Danger Zone Nebezpečná zóna <b>⚠ This is a danger zone. Proceed with caution, as actions performed here cannot be undone and may have a significant impact on your log.</b> <b>⚠ Toto je nebezpečná zóna. Pokračujte opatrně, protože provedené akce nelze vrátit zpět a mohou mít zásadní dopad na váš log.</b> Delete All QSOs Smazat všechny QSO Delete All Passwords from the Secure Store Smazat všechna hesla z bezpečného úložiště Endpoint Koncový bod Others Jiné Status Confirmed By Potvrzeno Paper QSL Chat Chat <b>Security Notice:</b> QLog stores all passwords in the Secure Storage. Unfortunately, ON4KST uses a protocol where this password is sent over an unsecured channel as plaintext.</p><p>Please exercise caution when choosing your password for this service, as your password is sent over an unsecured channel in plaintext form.</p> <b>Oznámení o zabezpečení:</b> QLog ukládá všechna hesla do zabezpečeného úložiště. ON4KST bohužel používá protokol, kde je toto heslo odesíláno přes nezabezpečený kanál jako prostý text.</p><p>Při výběru hesla pro tuto službu buďte opatrní, protože vaše heslo je odesíláno přes nezabezpečený kanál v podobě prostého textu.< /p> The '>' character is interpreted as a marker for the initial cursor position in the Report column. <br/>Ex.: '5>9' means the cursor will be positioned on the second character Znak „>“ je interpretován jako značka pro počáteční pozici kurzoru ve sloupci Report.<br>Př.: „5>9“ znamená, že kurzor bude umístěn na druhém znaku Raw UDP Forward UDP Forward <p>List of IP addresses to which QLog forwards raw UDP WSJT-X packets.</p>The IP addresses are separated by a space and have the form IP:PORT <p>Seznam IP adres, na které QLog přeposílá nezměněné UDP WSJTX pakety. </p>Adresy jsou odděleny mezerou a mají formát IP:PORT Join Multicast Použít Multicast Enable/Disable Multicast option for WSJTX Povolit/zakázat možnost Multicast pro WSJTX Multicast Address Multicast Adresa Specify Multicast Address. <br>On some Linux systems it may be necessary to enable multicast on the loop-back network interface. Zadejte Multicast adresu. <br>Na některých Linux systémech může být nutné povolit Multicast pro Loopback interface. TTL TTL Time-To-Live determines the range<br> over which a multicast packet is propagated in your intranet. Time-To-Live určuje vzdálenost<br>, do které se paket Multicastu v síti šíří. Color CQ Spots Obarvit CQ spoty Enable/Disable sending color-coded status indicators back to WSJT-X for each callsign calling CQ Povolit / zakázat odesílání barevně kódovaných stavových indikátorů zpět do WSJT-X pro každou Notifications Notifikace DX Spots DX Spoty <p> List of IP addresses to which QLog sends UDP notification packets with DX Cluster Spots.</p>The IP addresses are separated by a space and have the form IP:PORT <p>Seznam IP adres, na které QLog přeposílá UDP notificate z DX Clusteru. </p>Adresy jsou odděleny mezerou a mají formát IP:PORT QSO Changes Změny QSO <p> List of IP addresses to which QLog sends UDP notification packets about a new/updated/deleted QSO in the log.</p>The IP addresses are separated by a space and have the form IP:PORT <p>Seznam IP adres, na které QLog přeposílá UDP notificate o novém/aktualizovaném/vymazaném QSO. </p>Adresy jsou odděleny mezerou a mají formát IP:PORT Wsjtx CQ Spots Wsjtx CQ Spoty <p> List of IP addresses to which QLog sends UDP notification packets with WSJTX CQ Spots.</p>The IP addresses are separated by a space and have the form IP:PORT <p>Seznam IP adres, na které QLog přeposílá UDP notificate s WSJTX CQ Spoty. </p>Adresy jsou odděleny mezerou a mají formát IP:PORT Rig Status Stav Rig <p> List of IP addresses to which QLog sends UDP notification packets when Rig State changes.</p>The IP addresses are separated by a space and have the form IP:PORT <p>Seznam IP adres, na které QLog přeposílá UDP notifikace Stavu Rigu. </p>Adresy jsou odděleny mezerou a mají formát IP:PORT GUI GUI Time Format Formát času 24-hour 24hodinový AM/PM AM/PM Unit System Jednotkový systém Metric Metrický Imperial Imperiální Date Format Formát data System Systémový Custom Vlastní <a href="https://doc.qt.io/qt-6/qdate.html#fromString-1">Time Format Documentation</a> <a href="https://doc.qt.io/qt-6/qdate.html#fromString-1">Dokumentace formátu času</a> Spot Alerts Upozornění na Spoty <p> List of IP addresses to which QLog sends UDP notification packets about user Spot Alerts.</p>The IP addresses are separated by a space and have the form IP:PORT <p>Seznam IP adres, na které QLog přeposílá UDP notifikace Upozornění na Spoty. </p>Adresy jsou odděleny mezerou a mají formát IP:PORT LogID LogID <p>Assigned LogID to the current log.</p>The LogID is sent in the Network Nofitication messages as a unique instance identified.<p> The ID is generated automatically and cannot be changed</> <p>LogID pro aktuální log</p>LogID se posílá ve všech UDP notifikacích jako unikátní identifikátor.<p> ID je generováno automaticky a nemůže být změněno.</> Callbook Callbook Query Order Pořadí dotazů Primary Primární Secondary Sekundární HamQTH HamQTH Username Uživatelské jméno Password Heslo QRZ.com QRZ.com Port where QLog listens an incoming traffic from WSJT-X Port, kde QLog poslouchá příchozí zprávy z WSJT-X ex. 192.168.1.1:1234 192.168.2.1:1234 např. 192.168.1.1:1234 192.168.2.1:1234 eQSL eQSL Browse Procházet LoTW LoTW TQSL Path Cesta k TQSL Network Síť Wsjtx Wsjtx Port ClubLog ClubLog E-Mail E-Mail Power Výkon VFO VFO Use COMxx for Window or path to COM port under Unix-like OS Použijte COMxx pro Windows or cestu ke COM portu pro Unix-like OS List of all available CW Shortcuts Profiles Seznam všech dostupných CW Shortcut Profilů Rig Features Vlastnosti QSY Wiping QSY Výmaz <b>Notice:</b> At least a QRZ XML Subscription is recommended to access detailed information. Without a subscription, you will obtain limited data from QRZ, such as missing grid and other fields. <b>Oznámení:</b> Pro přístup k podrobným informacím se doporučuje alespoň předplatné QRZ XML. Bez předplatného získáte od QRZ jen omezené údaje, například chybějící grid a další pole. Sync && QSL Sync && QSL QSOs are uploaded immediately QSO jsou okamžítě nahrána Using an internal TQSL instance Používám interní TQSL Bands Pásma Modes Druhy provozu DXCC DXCC Name Jméno Report Report State Stav Disabled Vypnuto WinKey WinKey Press <b>Modify</b> to confirm the profile changes or <b>Cancel</b>. Stiskněte <b>Upravit</b> pro potvrzení změny profilu nebo <b>Zrušit</b>. Must not be empty Nesmí být prázdné Modify Upravit Special - Omnirig Special - Omnirig Cannot be changed Nelze změnit Start (MHz) Pořátek (MHz) End (MHz) Konec (MHz) SAT Mode SAT Mode Dummy Dummy Morse Over CAT Morse Over CAT CWDaemon CWDaemon FLDigi FLDigi Single Paddle Single Paddle IAMBIC A IAMBIC A IAMBIC B IAMBIC B Ultimate Ultimate High High Low Low Select File Vybrat soubor Auto Detect Automatická detekce TQSL was not found on this system. Please install TQSL or specify the path manually. TQSL nebyl na tomto systému nalezen. Nainstalujte prosím TQSL nebo zadejte cestu ručně. Not found Nenalezeno Rig sharing is only available for Hamlib driver Sdílení transceiveru je k dispozici pouze pro ovladač Hamlib Rig sharing is not available for network connection Sdílení transceiveru není k dispozici pro síťové připojení Delete Passwords Smazat hesla All passwords have been deleted Všechna hesla byla smazána Deleting all QSOs... Mažu všechna QSO... Error Chyba Failed to delete all QSOs. Nepodařilo se smazat všechna QSO. members členů Required internet connection during application start Je vyžadováno připojení do internetu během startu aplikace ShortcutEditorModel Description Popis Shortcut Zkratky Conflict with a built-in shortcut Konflikt s vestavěnou klávesovou zkratkou Conflict with a user-defined shortcut Konflikt s klávesovou zkratkou ShowUploadDialog QSOs to Upload Vybraná QSO pro nahrání Selected QSO Vybraná QSO Upload Nahrát SmartSearchBox Search Hledat StatisticsWidget Statistics Statistiky My Callsign Moje značka Date Range Časové období Graph Type Typ grafu My Gridsquare Můj lokátor QSOs per QSO za User Filter Uživatelský filtr Confirmed by Potvrzeno Percents Procenta Top 10 Top 10 Histogram Histogram LoTW LoTW eQSL eQSL Paper QSL My Antenna Moje Anténa Show on Map Zobrazit na mapě My Rig Můj Rig to do Band Pásmo Year rok Month měsíc Day in Week den v týdnu Hour hodinu Mode druh provozu Continent kontinent Propagation Mode podmínky šíření Confirmed / Not Confirmed Potvrzeno / Nepotvrzeno Countries Země Big Gridsquares Velké čtverce Distance Vzdálenost QSOs QSO Confirmed/Worked Grids Potvrzené / Pracováno Lokátory ODX ODX Sun Ned Mon Pon Tue Út Wed Stř Thu Čtvr Fri Pát Sat Sob Not specified Neurčeno Confirmed Potvrzeno Not Confirmed Nepotvrzeno No User Filter Žádný uživatelský filtr Over 50000 QSOs. Display them? Přes 50000 QSO. Zobrazit je? Rendering QSOs... Vykreslování QSO… All Vše TCIRigDrv Rig 0 Rig 0 Rig 1 Rig 1 Rig 2 Rig 2 Rig 3 Rig 3 Error Occurred Došlo k chybě Rig status changed Změna stavu Rigu Rig is not connected Rig není připojen TimestampFormatDelegate Blank Nevyplněno ToAllTableModel Time Čas Spotter Spotter Message Zpráva UploadQSODialog Upload QSOs Nahrát QSO eQSL eQSL LoTW LoTW QRZ.com QRZ.com Clublog Clublog HRDLog HRDLog Wavelog Wavelog Filters Filtry Station Profile Profil stanice My Callsign Moje značka Gridsquare Lokátor Include QSOs Status Zahrnout QSO se statusem Under normal circumstances this status means <b>"do not send"</b>.<br/>However, it may sometimes be wanted to ignore this setting when sending a QSL. Za normálních okolností tento status znamená <b>Neodesílat</b>.<br/>Avšak, někdy může být požadováno toto nastavení ignorovat. No Ne Under normal circumstances this status means <b>"Ignore/Invalid"</b>.<br/>However, it may sometimes be wanted to ignore this setting when sending a QSL. Za normálních okolností tento status znamená <b>"Ignorovat"</b>.<br/>Avšak, někdy může být požadováno toto nastavení ignorovat. Ignore Ignorovat None Žádné QSLs Message Odchozí QSL Zpráva Comment Poznámka QSL Message Field Pole QSL zprávy QTH Profile QTH Profil This option deletes all QSOs in the Clublog<br>and based on filter, it uploads all QSOs regardless of their status. Tato volba smaže všechna QSO v Clublog<br>a na základě filtru nahraje všechna QSO bez ohledu na jejich stav. Clear Clublog and reupload QSOs Vymazat Clublog a znovu nahrát QSO LoTW / TQSL LoTW / TQSL TQSL Station Location Umístění stanice TQSL Station Profile ID Profil stanice Reupload All Nahrát vše znovu Show QSOs... Zobrazit QSO... &Upload &Nahrát Unspecified Nespecifikováno Hide QSOs... Skrýt QSO... Uploading to %1 Nahrávám na %1 Cancel Zrušit QLog Warning - %1 Upozornění QLog - %1 Cannot update QSO Status Nelze aktualizovat stav QSO Cannot upload the QSO(s): Není možné nahrát QSO: QLog Information Informace QLog No QSO found to upload. Nebyl nalezen žádný QSO k nahrání. QSO(s) were uploaded to the selected services QSO byly nahrány do vybraných služeb Time Čas Callsign Značka Mode Druh provozu Upload to Nahrát do The values below will be used when an input record does not contain the ADIF values Hodnoty jsou použity v případě, když importovaný záznam má příslušné ADIF pole prázdné Any Jakýkoliv Location callsign (%1) and grid (%2) do not match selected filters Volací značka (%1) a čtverec (%2) neodpovídají vybraným filtrům Location callsign (%1) does not match selected callsign (%2) Volací značka lokace (%1) neodpovídá vybrané volací značce (%2) Location grid (%1) does not match selected grid (%2) Čtverec lokace (%1) neodpovídá vybranému čtverci (%2) Unknown Neurčeno Service is not configured properly.<p> Please, use <b>Settings</b> dialog to configure it.</p> Služba není správně nastavena.<p> Prosím, použijte dialog <b>Nastavení</b> pro konfiguraci služby.</p> UserListModel Callsign Značka Gridsquare Lokátor Distance Vzdálenost Azimuth Azimut Comment Poznámka WCYTableModel Time Čas K K expK expK A A R R SFI SFI SA SA GMF GMF Au Au WWVTableModel Time Čas SFI SFI A A K K Info Info WsjtxFilterDialog WSJTX Filters WSJTX Filtry General Filters Obecné Log Status Status v logu New Band Nové pásmo New Mode Nový druh provozu New Entity Nová země New Slot Nový slot Worked Pracováno Confirmed Potvrzeno Continent Kontinent North America Severní Amerika Europe Evropa South America Jižní Amerika Africa Afrika Antarctica Antarktida Asia Asie Oceania Oceánie Distance more than Vzdálenost větší než SNR better than SNR lepší než dB Extended Filters Pokročilé Member Člen No Club List is enabled Není aktívní žádný Club List WsjtxTableModel Callsign Značka Gridsquare Lokátor Distance Vzdálenost SNR SNR Last Activity Aktivita Last Message Zpráva Member Člen WsjtxWidget Form Form Filtered Filtrováno Column Visibility... Zobrazení sloupců... Filter... Filtr... Which columns should be displayed Který sloupec by měl být zobrazen Filter Spots Filtr main Run with the specific namespace. Spustit ve specifickém jmeném prostoru. namespace namespace Translation file - absolute or relative path and QM file name. Soubor s překladem - absolutní nebo relativní cesta a jméno QM souboru. path/QM-filename path/QM-filename Set language. <code> example: 'en' or 'en_US'. Ignore environment setting. Nastavit jazyk. Příklad <code>: 'en' nebo 'en_US'. Ignoruje OS nastavení. code kód Writes debug messages to the debug file Zapsat ladící zprávy do souboru Process pending database import (internal use) Zpracovat čekající import databáze (pro interní použití) Force update of all value lists (DXCC, SATs, etc.) Vynutit aktualizaci všech seznamů hodnot (DXCC, SATy atd.) ================================================ FILE: i18n/qlog_de.ts ================================================ ActivityEditor Activity Editor Aktivitätseditor Activity Name Aktivitätsname Layout Layout Window Arrangement: Fensteranordnung: Saved Gespeichert Clear Löschen Available Fields Verfügbare Felder List of fields that can be used Liste der Felder, die verwendet werden können Row A Zeile A Move selected fields from the Available Fields List to the Row A Ausgewählte Felder aus der Liste der verfügbaren Felder in die Zeile A verschieben Remove selected field from the Row A Ausgewähltes Feld aus der Zeile A entfernen List of fields in the first variable row Liste der Felder in der ersten Variablenzeile Change the order. Move selected field up Ändern Sie die Reihenfolge. Ausgewähltes Feld nach oben verschieben Change the order. Move selected field down Ändern Sie die Reihenfolge. Ausgewähltes Feld nach unten verschieben Row B Zeile B Move selected fields from the Available Fields List to the Row B Ausgewählte Felder aus der Liste der verfügbaren Felder in die Zeile B verschieben Remove selected field from the Row B Ausgewähltes Feld aus der Zeile B entfernen List of fields in the second variable row Liste der Felder in der zweiten Variablenzeile Column A Spalte A List of fields in the first QSO Detail Column Liste der Felder in der ersten QSO-Detail Spalte Column B Spalte B List of fields in the second QSO Detail Column Liste der Felder in der zweiten QSO-Detail Spalte Column C Spalte C List of fields in the third QSO Detail Column Liste der Felder in der dritten QSO-Detail-Spalte New QSO Rows Neue QSO Zeilen New QSO Detail Columns Neue Spalten in den QSO Details Values Werte Profiles Profile If unchecked, the profile does not change Wenn nicht aktiviert, ändert sich das Profil nicht Station Station Antenna Antenne Rig Rig Connect automatically Automatisch verbinden Connect Verbinden Rotator Rotor Fields Felder Must not be empty Darf nicht leer sein Unsaved Ungespeichert AlertRuleDetail Alert Rule Detail Alarmregel Details Rule Name Regelname Enabled Aktiviert Sources Quellen DX Cluster WSJTX WSJTX DX Worked Gearbeitet New Slot Neuer Slot Confirmed Bestätigt New Entity Neuer Eintrag New Mode Neuer Mode New Band Neues Band DX Callsign DX Rufzeichen Use Perl-like regular expression Verwende Perl-ähnliche reguläre Ausdrücke .* Country Land Log Status Protokollstatus Spot Comment Spot-Kommentar ITU ITU CQZ CQZ IOTA IOTA SOTA SOTA POTA POTA WWFF WWFF Special Programs Sonderprogramme Modes Betriebsarten FTx (FT8/FT4) FTx (FT8/FT4) Phone Phonie CW CW Digital Digital Bands Bänder Continent Kontinent North America Nordamerika Africa Afrika Antarctica Antarktis South America Südamerika Asia Asien Europe Europa Oceania Ozeanien Member Mitglied Spotter All Alle Must not be empty Darf nicht leer sein No Club List is enabled Keine Clubliste aktiviert AlertSettingDialog Alerts Rules Alarmregeln Rules Regeln Add Hinzufügen Edit Bearbeiten Remove Entfernen Name Name State Status AlertTableModel Rule Name Regelname Callsign Rufzeichen Frequency Frequenz Mode Betriebsart Updated Aktualisiert Last Update Letzte Aktualisierung Last Comment Letzter Kommentar Member Mitglied AlertWidget Alerts Benachrichtigungen Clear older than Löschen älter als Never Nie min(s) Edit Rules Regeln bearbeiten Column Visibility... Spaltensichtbarkeit... Clear Löschen AwardsDialog Awards Auszeichnungen Options Optionen Award Auszeichnung My DXCC Entity Eigener DXCC Eintrag User Filter Benutzer-Filter Confirmed by Bestätigt durch LoTW eQSL Paper Papier Mode Betriebsart CW Phone Phonie Digi Not-Worked Only nur nicht gearbeit Not-Confirmed Only Nur unbestätigt Show Anzeigen DXCC ITU WAC WAZ WAS WPX IOTA POTA Hunter POTA Jaeger POTA Activator POTA Aktivierer SOTA SOTA WWFF Gridsquare 2-Chars Locator 2 Zeichen Gridsquare 4-Chars Locator 4 Zeichen Gridsquare 6-Chars Locator 6 Zeichen Gridsquare %1-Chars Locator %1 Zeichen US Counties Countys der USA Russian Districts Russische Distrikte Japanese Cities/Ku/Guns Japanische Städte / Ku / Gun NZ Counties Countys Neuseelands Spanish DMEs Spanische DME Ukrainian Districts Ukrainische Distrikte No User Filter Kein Benutzerfilter North America Nordamerika South America Südamerika Europe Europa Africa Afrika Asia Asien Antarctica Antarktis Oceania Ozeanien DELETED Gelöscht AwardsTableModel Slots: Slot: Confirmed Bestätigt Worked Gearbeitet Still Waiting Wartend BandmapWidget Form Create an additional Bandmap Window Ein zusätzliches Bandplan-Fenster erstellen Clear older Ältere löschen Never Nie Clear All Alle löschen Clear the current band Aktuelles Band löschen min(s) Bandmap Bandplan Show Band Band anzeigen Center RX RX zentrieren Show Emergency Frequencies Notruffrequenzen anzeigen SOS CWCatKey No Rig is connected Rig nicht verbunden Rig does not support Morse over CAT Rig unterstützt kein CW-über-CAT Keyer is not connected Keyer nicht verbunden Cannot send Text to Rig Kann Text nicht an Rig senden Rig is not connected Rig nicht verbunden Cannot set Keyer Speed Kann Keyer Geschwindigkeit nicht einstellen Cannot stop Text Sending Kann Textübertragung nicht beenden CWConsoleWidget Form Speed Geschwindigkeit Immediately stop CW sending CW-Sendung sofort stoppen Halt Halt Clear Sent and Echo Console Sendefenster und Echo-Konsole löschen Clear Löschen &CW Text to send Zu versendender Text F1 F2 F3 Shortcuts profile Funktionstasten Profile CW Keyer Profile CW-Keyer Profil N/A WPM Sent text Gesendeter Text Echoed text Wiederholter Text Switch between sending <b>words</b> individually (separated by spaces)<br> and sending the entire text as a <b>whole</b> (separated by a new line). Wechseln Sie zwischen dem Senden <b>Wort</b> für Wort (getrennt durch Leerzeichen) <br> und dem Senden als <b>Ganzes</b> (getrennt durch eine neue Zeile). F4 F5 F6 F7 CW Console - Halt Sending CW Console - Halt Rig must be connected Rig muss verbunden sein Word Wort Whole Ganze CWDaemonKey Keyer is not connected Keyer nicht verbunden Cannot send Text Kann Text nicht senden Cannot set Keyer Speed Kann Keyer-Geschwindigkeit nicht einstellen Cannot stop Text Sending Kann Textübertragung nicht beenden CWFldigiKey Connected device is not FLDigi Verbundenes Programm ist nicht FLDigi Keyer is not connected Keyer ist nicht verbunden Cannot send Text to FLDigi Kann Text nicht an FLDigi senden Cannot send the Clear command to FLDigi Kann das Kommando Clear nicht an FLDigi senden Cannot send the TX command to FLDigi Kann das Kommando TX nicht an FLDigi senden Cannot send the Text command to FLDigi Kann das Text-Kommando nicht an FLDigi senden Cannot send the Stop command to FLDigi Kann das Kommando Stop nicht an FLDigi senden Cannot send the Abort command to FLDigi Kann das Kommando Abort nicht an FLDigi senden Cannot receive data from FLDigi Kann keine Daten von FLDigi empfangen FLDigi connection timeout Zeitüberschreitung der FLDigi-Verbindung FLDigi connection error FLDigi Verbindungsfehler FLDigi command error FLDigi Kommandofehler CWKeyer No CW Keyer Profile selected Kein CW-Keyer Profil ausgewählt Initialization Error Initialisierungsfehler Internal Error Interner Fehler Connection Error Verbindungsfehler Cannot open the Keyer connection Kann die Keyer-Verbindung nicht herstellen CWWinKey Connected device is not WinKey Angeschlossenes Gerät ist kein WinKey Cannot send Text to Rig Kann Text nicht an Rig senden Keyer is not connected Keyer nicht verbunden Communication Error Kommunikationsfehler Cannot set Keyer Speed Kann Keyer-Geschwindigkeit nicht einstellen Cannot stop Text Sending Kann Textübertragung nicht beenden 4000 Hz 4000 Hz 2000 Hz 2000 Hz 1333 Hz 1333 Hz 1000 Hz 1000 Hz 800 Hz 800 Hz 666 Hz 666 Hz 571 Hz 571 Hz 500 Hz 500 Hz 444 Hz 444 Hz 400 Hz 400 Hz CabrilloExportDialog Cabrillo Export Cabrillo exportieren File: Datei: Browse Durchsuchen Contest Contest Format Template: Formatvorlage: Manage Verwalten User Filter: Benutzerfilter: Date/Time: Datum/Uhrzeit: yyyy-MM-dd HH:mm - - UTC Station & Categories Station & Kategorien Callsign: Rufzeichen: Operators: Operatoren: Band: Band: Mode: Betriebsart: Power: Leistung: Operator: Operator: Assisted: Unterstützt: Station: Station: Transmitter: Sender: Time: Zeit: Overlay: Transmitter ID: Additional Zusätzlich Name: Name: Email: Email: Address: Adresse: Max 6 lines Max. 6 Zeilen Gridsquare Gitterfeld Location: Standort: Club: Club: Soapbox: Freitext: Off-Time: yyyy-mm-dd hhmm yyyy-mm-dd hhmm &Export &Exportieren Cabrillo Files (*.log);;CBR Files (*.cbr);;All Files (*) Cabrillo-Dateien (*.log);;CBR-Dateien (*.cbr);;Alle Dateien (*) QSO(s): %1 QSO(s): %1 QSOs: ? QSOs: ? QLog Warning QLog Warnung Please select a contest template. Bitte wählen Sie eine Contest-Vorlage aus. Please select an output file. Bitte wählen Sie eine Ausgabedatei aus. No callsign available. Check your filters. Kein Rufzeichen verfügbar. Überprüfen Sie Ihre Filter. QLog Error QLog Fehler Failed to prepare export query. Exportabfrage konnte nicht vorbereitet werden. Failed to query QSOs for export. QSOs für den Export konnten nicht abgefragt werden. Cannot open file %1 for writing. Datei %1 kann nicht zum Schreiben geöffnet werden. Exporting Cabrillo... Cabrillo wird exportiert… QLog Information QLog Information Exported %n QSO(s) to Cabrillo file. %n QSOs in die Cabrillo-Datei exportiert. CabrilloFormat All Bands Alle Bänder 2m (144 MHz) 2m (144 MHz) 1.25m (222 MHz) 1.25m (222 MHz) 70cm (432 MHz) 70cm (432 MHz) 33cm (902 MHz) 33cm (902 MHz) 23cm (1.2 GHz) 23cm (1.2 GHz) Light Licht VHF 3-Band VHF 3-Band VHF FM Only Nur VHF FM Digital Digital Mixed Gemischt High High Low Low QRP Single Operator Einmannbetrieb Multi Operator Mehrmannbetrieb Check Log Log prüfen Non-Assisted Nicht assistiert Assisted Assistiert Fixed Fest Mobile Mobil Portable Rover Rover Rover Limited Rover Limited Rover Unlimited Expedition Expedition HQ School Schule Distributed Verteilt One Eins Two Zwei Limited Begrenzt Unlimited Unbegrenzt SWL SWL 6 Hours 6 Stunden 12 Hours 12 Stunden 24 Hours 24 Stunden Classic Klassisch Rookie TB Wires Novice/Tech Novize/Techniker Over 50 Über 50 Text (left-aligned) Text (linksbündig) Frequency (kHz) Frequenz (kHz) Time (HHMM) Zeit (HHMM) Date (YYYY-MM-DD) Datum (JJJJ-MM-TT) RST Short (drop last digit) RST kurz (letzte Ziffer weglassen) Uppercase Großbuchstaben Mode (Cabrillo) Modus (Cabrillo) Zero-Padded Nr. Nummer mit Nullen Transmitter ID CabrilloTemplateDialog Cabrillo Template Manager Cabrillo-Vorlagenverwaltung Import template Vorlage importieren Import Importieren Export template Vorlage exportieren Export Exportieren New template Neue Vorlage New Neu Copy existing template Vorhandene Vorlage kopieren Copy Kopieren Delete template Vorlage löschen Delete Löschen Template Name: Vorlagenname: Contest Name: Contest-Name: Default Mode: Standardmodus: QSO Line Columns: QSO-Zeilen-Spalten: Contest name as required by the rules. It is possible to enter a custom string if it is not included in the list. Contest-Name gemäß den Regeln. Es ist möglich, eine benutzerdefinierte Zeichenkette einzugeben, falls sie nicht in der Liste enthalten ist. Seq. Nr. QSO Field QSO-Feld Formatter Formatierer Width Breite Label Etikett Add line Zeile hinzufügen Add Hinzufügen Remove selected line Ausgewählte Zeile entfernen Remove Entfernen New Template Neue Vorlage Copy - %1 Kopie – %1 Delete Template Vorlage löschen Delete template '%1'? Vorlage „%1“ löschen? Import Template Vorlage importieren QLog Cabrillo Template (*.qct) QLog Cabrillo-Vorlage (*.qct) Import Failed Import fehlgeschlagen Export Template Vorlage exportieren Export Failed Export fehlgeschlagen Failed to write file: %1 Datei konnte nicht geschrieben werden: %1 File not found: %1 Datei nicht gefunden: %1 Cannot open file: %1 Datei kann nicht geöffnet werden: %1 Invalid template file: missing name Ungültige Vorlagendatei: Name fehlt QLog Error QLog Fehler Cannot start database transaction. Datenbanktransaktion kann nicht gestartet werden. QLog Warning QLog Warnung Cannot save template '%1': %2 Vorlage „%1“ kann nicht gespeichert werden: %2 CallbookManager <p>The secondary callbook will be used</p> <p>Das sekundäre Callbook wird verwendet</p> ChatWidget ON4KST Chat Chat Room Chatraum Connect Verbinden New Neu QLog Warning QLog Warnung ON4KST Chat is not configured properly.<p> Please, use <b>Settings</b> dialog to configure it.</p> ON4KST Chat ist nicht richtig konfiguriert.<p>Bitte verwenden Sie den <b>Einstellungen</b> Dialog um ihn zu konfigurieren.</p> CheckBoxDelegate Enabled Aktiv Disabled Inaktiv ClockWidget Form Sunrise SA Sunset SU N/A CloudlogUploader Invalid API Key Ungültiger API-Schlüssel ClubLogUploader Clublog Operation for Callsign %1 failed.<br>%2 Clublog-Anfrage für Rufzeichen %1 fehlgeschlagen.<br>%2 ColumnSettingDialog Column Visibility Setting Einstellung der Spaltensichtbarkeit General Allgemein My Info Eigene Info QSL && Callbooks Members Mitglieder Conditionals Bedingungen Contests Conteste Others Andere Done erledigt ColumnSettingGenericDialog Unselect All Alle abwählen Select All Alle auswählen ColumnSettingSimpleDialog Column Visibility Setting Spaltensichtbarkeit Done erledigt DBSchemaMigration DXCC Entities DXCC Einträge Sats Info SAT Info SOTA Summits SOTA Gipfel WWFF Records WWFF Datensätze IOTA Records IOTA Datensätze POTA Records POTA Datensätze Membership Directory Records Mitgliedschaftsverzeichnis Clublog CTY.XML Clublog CTY.XML List of Values Werteliste Updating Update Failed DBStrings PHONE Phonie CW CW DIGITAL Digital Afghanistan Afghanistan Agalega & St. Brandon Agalega & St. Brandon Aland Islands Aland-Inseln Alaska Alaska Albania Albanien Algeria Algerien American Samoa Amerikanischen Samoa-Inseln Amsterdam & St. Paul Is. Amsterdam & St. Paul Is. Andaman & Nicobar Is. Andamanen und Nikobaren. Andorra Andorra Angola Angola Anguilla Anguilla Annobon Island Annobon-Insel Antarctica Antarktis Antigua & Barbuda Antigua und Barbuda Argentina Argentinien Armenia Armenien Aruba Aruba Ascension Island Ascension-Insel Asiatic Russia Asiatisches Russland Asiatic Turkey Asiatische Türkei Austral Islands Austral-Inseln Australia Australien Austria Österreich Aves Island Aves-Insel Azerbaijan Aserbaidschan Azores Azoren Bahamas Bahamas Bahrain Bahrain Baker & Howland Islands Baker- und Howlandinseln Balearic Islands Balearen Banaba Island Banaba-Insel Bangladesh Bangladesch Barbados Barbados Belarus Weißrussland Belgium Belgien Belize Belize Benin Benin Bermuda Bermuda Bhutan Bhutan Bolivia Bolivien Bonaire Bonaire Bosnia-Herzegovina Bosnien-Herzegowina Botswana Botswana Bouvet Bouvet Brazil Brasilien British Virgin Islands Britische Jungferninseln Brunei Darussalam Brunei Darussalam Bulgaria Bulgarien Burkina Faso Burkina Faso Burundi Burundi Cambodia Kambodscha Cameroon Kamerun Canada Kanada Canary Islands Kanarische Inseln Cape Verde Kap Verde Cayman Islands Cayman Inseln Central African Republic Zentralafrikanische Republik Central Kiribati Zentralkiribati Ceuta & Melilla Ceuta und Melilla Chad Tschad Chagos Islands Chagos-Inseln Chatham Islands Chatham-Inseln Chesterfield Islands Chesterfield-Inseln Chile Chile China China Christmas Island Weihnachtsinsel Clipperton Island Clipperton-Insel Cocos (Keeling) Islands Kokosinseln (Keelinginseln) Cocos Island Kokosinsel Colombia Kolumbien Comoros Komoren Conway Reef Conway-Riff Corsica Korsika Costa Rica Costa Rica Cote d'Ivoire Elfenbeinküste Crete Kreta Croatia Kroatien Crozet Island Crozet-Insel Cuba Kuba Curacao Curacao Cyprus Zypern Czech Republic Tschechien DPR of Korea DVR Korea Dem. Rep. of the Congo Dem. Republik Kongo Denmark Dänemark Desecheo Island Insel Desecheo Djibouti Dschibuti Dodecanese Dodekanes Dominica Dominica Dominican Republic Dominikanische Republik Ducie Island Ducie-Insel East Malaysia Ost-Malaysia Easter Island Osterinsel Eastern Kiribati Ostkiribati Ecuador Ecuador Egypt Ägypten El Salvador El Salvador England England Equatorial Guinea Äquatorialguinea Eritrea Eritrea Estonia Estland Ethiopia Äthiopien European Russia Europäisches Russland Falkland Islands Falkland Inseln Faroe Islands Färöer Inseln Fed. Rep. of Germany Deutschland Fernando de Noronha Fernando de Noronha Fiji Fidschi Finland Finnland France Frankreich Franz Josef Land Franz-Josef-Land French Guiana Französisch-Guayana French Polynesia Französisch Polynesien Gabon Gabun Galapagos Islands Galapagos Inseln Georgia Georgia Ghana Ghana Gibraltar Gibraltar Glorioso Islands Glorioso-Inseln Greece Griechenland Greenland Grönland Grenada Grenada Guadeloupe Guadeloupe Guam Guam Guantanamo Bay Guantanamo Bay Guatemala Guatemala Guernsey Guernsey Guinea Guinea Guinea-Bissau Guinea-Bissau Guyana Guyana Haiti Haiti Hawaii Hawaii Heard Island Heard-Insel Honduras Honduras Hong Kong Hongkong Hungary Ungarn ITU HQ ITU HQ Iceland Island India Indien Indonesia Indonesien Iran Iran Iraq Irak Ireland Irland Isle of Man Isle of Man Israel Israel Italy Italien Jamaica Jamaika Jan Mayen Jan Mayen Japan Japan Jersey Jersey Johnston Island Johnston-Insel Jordan Jordanien Juan Fernandez Islands Juan-Fernandez-Inseln Juan de Nova & Europa Juan de Nova & Europa Kaliningrad Kaliningrad Kazakhstan Kasachstan Kenya Kenia Kerguelen Islands Kerguelen-Inseln Kermadec Islands Kermadec-Inseln Kingdom of Eswatini Königreich Eswatini Kure Island Kure-Insel Kuwait Kuwait Kyrgyzstan Kirgisistan Lakshadweep Islands Lakshadweep-Inseln Laos Laos Latvia Lettland Lebanon Libanon Lesotho Lesotho Liberia Liberia Libya Libyen Liechtenstein Liechtenstein Lithuania Litauen Lord Howe Island Lord-Howe-Insel Luxembourg Luxemburg Macao Macau Macquarie Island Macquarie-Insel Madagascar Madagaskar Madeira Islands Madeira-Inseln Malawi Malawi Maldives Malediven Mali Mali Malpelo Island Malpelo-Insel Malta Malta Mariana Islands Marianen Market Reef Marktriff Marquesas Islands Marquesas-Inseln Marshall Islands Marshallinseln Martinique Martinique Mauritania Mauretanien Mauritius Mauritius Mayotte Mayotte Mellish Reef Mellish Riff Mexico Mexiko Micronesia Mikronesien Midway Island Midway-Insel Minami Torishima Minami Torishima Moldova Moldawien Monaco Monaco Mongolia Mongolei Montenegro Montenegro Montserrat Montserrat Morocco Marokko Mount Athos Berg Athos Mozambique Mosambik Myanmar Myanmar N.Z. Subantarctic Is. Subantarktische Inseln Neuseelands Namibia Namibia Nauru Nauru Navassa Island Navassa-Insel Nepal Nepal Netherlands Niederlande New Caledonia Neu-Kaledonien New Zealand Neuseeland Nicaragua Nicaragua Niger Niger Nigeria Nigeria Niue Niue Norfolk Island Norfolkinsel North Cook Islands Nordcookinseln North Macedonia Nordmazedonien Northern Ireland Nordirland Norway Norwegen Ogasawara Ogasawara Oman Oman Pakistan Pakistan Palau Palau Palestine Palästina Palmyra & Jarvis Islands Palmyra und Jarvis-Inseln Panama Panama Papua New Guinea Papua Neu-Guinea Paraguay Paraguay Peru Peru Peter 1 Island Peter-1-Insel Philippines Philippinen Pitcairn Island Pitcairninsel Poland Polen Portugal Portugal Pr. Edward & Marion Is. Pratas Island Pratas-Insel Puerto Rico Puerto Rico Qatar Katar Republic of Korea Republik Korea Republic of Kosovo Republik Kosovo Republic of South Sudan Republik Südsudan Republic of the Congo Republik Kongo Reunion Island Insel La Réunion Revillagigedo Revillagigedo Rodriguez Island Rodriguez-Insel Romania Rumänien Rotuma Island Rotuma-Insel Rwanda Ruanda Saba & St. Eustatius Saba und St. Eustatius (Statia) Sable Island Sable-Insel Samoa Samoa San Andres & Providencia San Andrés und Providencia San Felix & San Ambrosio San Felix und San Ambrosio San Marino San Marino Sao Tome & Principe São Tomé und Príncipe Sardinia Sardinien Saudi Arabia Saudi-Arabien Scarborough Reef Scarborough-Riff Scotland Schottland Senegal Senegal Serbia Serbien Seychelles Seychellen Sierra Leone Sierra Leone Singapore Singapur Sint Maarten Sint Maarten Slovak Republic Slowakische Republik Slovenia Slowenien Solomon Islands Salomon-Inseln Somalia Somalia South Africa Südafrika South Cook Islands Südliche Cookinseln South Georgia Island Insel Südgeorgien South Orkney Islands Südliche Orkney-Inseln South Sandwich Islands Südliche Sandwichinseln South Shetland Islands Südshetlandinseln Sov Mil Order of Malta Souveräner Malteserorden Spain Spanien Spratly Islands Spratly-Inseln Sri Lanka Sri Lanka St. Barthelemy St. Barthelemy St. Helena St. Helena St. Kitts & Nevis St. Kitts und Nevis St. Lucia St. Lucia St. Martin St. Martin St. Paul Island St. Peter & St. Paul St. Peter und St. Paul St. Pierre & Miquelon St. Pierre und Miquelon St. Vincent St. Vincent Sudan Sudan Suriname Surinam Svalbard Spitzbergen Swains Island Swains-Insel Sweden Schweden Switzerland Schweiz Syria Syrien Taiwan Taiwan Tajikistan Tadschikistan Tanzania Tansania Temotu Province Provinz Temotu Thailand Thailand The Gambia Gambia Timor - Leste Timor - Leste Togo Togo Tokelau Islands Tokelau-Inseln Tonga Tonga Trindade & Martim Vaz Trindade und Martim Vaz Trinidad & Tobago Trinidad & Tobago Tristan da Cunha & Gough Islands Tristan da Cunha und Gough-Inseln Tromelin Island Tromelin-Insel Tunisia Tunesien Turkmenistan Turkmenistan Turks & Caicos Islands Turks- und Caicosinseln Tuvalu Tuvalu UK Base Areas on Cyprus Souveräne Militärbasen Akrotiri und Dekelia US Virgin Islands Amerikanische Jungferninseln Uganda Uganda Ukraine Ukraine United Arab Emirates Vereinigte Arabische Emirate United Nations HQ Vereinte Nationen HQ United States Uruguay Uruguay Uzbekistan Usbekistan Vanuatu Vanuatu Vatican City Vatikanstadt Venezuela Venezuela Vietnam Vietnam Wake Island Wake Island Wales Wales Wallis & Futuna Islands Wallis- und Futuna-Inseln West Malaysia West Malaysia Western Kiribati Westkiribati Western Sahara Westsahara Willis Island Willis Island Yemen Jemen Zambia Sambia Zimbabwe Zimbabwe DXCCSubmissionDialog DXCC Submission List DXCC-Einreichungsliste Options Optionen My DXCC Entity Eigener DXCC Eintrag User Filter Benutzer-Filter Category Kategorie Mixed Gemischt CW CW Phone Phonie Digital Digital Confirmed by Bestätigt durch LoTW LoTW Paper Papier Show Anzeigen Not Yet Submitted Noch nicht eingereicht Submitted (Not Granted) Eingereicht (nicht anerkannt) Already Granted Bereits anerkannt Band Scope Bandbereich Select 5-Band DXCC preset bands (80/40/20/15/10m) Vordefinierte 5-Band-DXCC-Bänder auswählen (80/40/20/15/10 m) 5-Band DXCC 5-Band-DXCC Select all DXCC-eligible bands Alle DXCC-berechtigten Bänder auswählen All DXCC Bands Alle DXCC-Bänder Bands Bänder Select options above and the list will update automatically. Wählen Sie die Optionen oben aus und die Liste wird automatisch aktualisiert. Export the contacts listed above to an ADIF file Die oben aufgeführten Kontakte in eine ADIF-Datei exportieren Export Exportieren No User Filter Kein Benutzerfilter Any Band (Entity Level) Beliebiges Band (Entity-Ebene) 5-Band DXCC (80/40/20/15/10m) 5-Band-DXCC (80/40/20/15/10 m) Custom Band Selection Benutzerdefinierte Bandauswahl Entity Entität Prefix Präfix Callsign Rufzeichen Band Band Mode Betriebsart Date Datum Submitted Eingereicht Granted Anerkannt Export ADIF ADIF exportieren No contacts to export. Keine Kontakte zum Exportieren. Failed to retrieve contact records. Kontaktaufzeichnungen konnten nicht abgerufen werden. Export DXCC Submission List as ADIF DXCC-Einreichungsliste als ADIF exportieren any band beliebiges Band 5-band 5-Band all bands alle Bänder %1 selected band(s) %1 ausgewählte Bänder No contacts match the selected criteria. Keine Kontakte entsprechen den ausgewählten Kriterien. %1 %2 %3 — DXCC %4 / %5 %1 %2 %3 — DXCC %4 / %5 band-slot Band-Slot entity Entität entry Eintrag entries Einträge Data New Entity Neuer Eintrag New Band Neues Band New Mode Neuer Mode New Band&Mode Neues Band&Mode New Slot Neuer Slot Confirmed Bestätigt Worked Gearbeitet Hz Hz kHz kHz GHz GHz MHz MHz Yes Ja No Nein Requested Angefordert Queued Wartend Invalid Ungültig Bureau Büro Direct Direkt Electronic Elektronisch Blank Leer Modified Geändert Grayline Grayline Other Andere Short Path Long Path Not Heard Nicht gehört Uncertain Unsicher Straight Key Straight Key Sideswiper Sideswiper Mechanical semi-automatic keyer or Bug Mechanical semi-automatic keyer or Bug Mechanical fully-automatic keyer or Bug Mechanical fully-automatic keyer or Bug Single Paddle Single Paddle Dual Paddle Dual Paddle Computer Driven Computergesteuert Confirmed (AG) Bestätigt (AG) Confirmed (no AG) Bestätigt (non-AG) Unknown Unbekannt Aircraft Scatter Aircraft Scatte Aurora-E Aurora-E Aurora Aurora Back scatter Back scatter EchoLink EchoLink Earth-Moon-Earth Erde-Mond-Erde Sporadic E Sporadic E F2 Reflection F2 Reflektion Field Aligned Irregularities Feldbedingte Unregelmäßigkeiten Ground Wave Bodenwelle Internet-assisted Internet-assisted Ionoscatter Ionoscatter IRLP IRLP Line of Sight Sichtlinie Meteor scatter Terrestrial or atmospheric repeater or transponder Rain scatter Satellite Trans-equatorial Tropospheric ducting DateFormatDelegate Blank Leer DevToolsDialog Developer Tools Entwickler-Tools Debug Log Debug-Log Logging Rules: Protokollierungsregeln: e.g. qlog.ui.*.runtime=true z. B. qlog.ui.*.runtime=true Apply Anwenden Generate Debug File Debug-Datei erzeugen Export File Datei exportieren SQL Console SQL-Konsole Open SQL SQL öffnen Save SQL SQL speichern Run SQL SQL ausführen Export As Exportieren als Enter SQL query here... Ctrl+Return = run SQL-Abfrage hier eingeben... Strg+Eingabe = ausführen TXT CSV ADI Open SQL Query SQL-Abfrage öffnen SQL Files (*.sql);;All Files (*) SQL-Dateien (*.sql);;Alle Dateien (*) Open Error Fehler beim Öffnen Cannot open file: %1 Datei kann nicht geöffnet werden: %1 Save SQL Query SQL-Abfrage speichern Save Error Fehler beim Speichern Cannot save file: %1 Datei kann nicht gespeichert werden: %1 Query saved to %1 Abfrage in %1 gespeichert Error: %1 Fehler: %1 %1 row(s) shown (truncated at %2) in %3 ms %1 Zeile(n) angezeigt (gekürzt auf %2) in %3 ms %1 row(s) returned in %2 ms %1 Zeile(n) in %2 ms zurückgegeben Export Exportieren No results to export. Keine Ergebnisse zum Exportieren. Export Error Exportfehler Cannot open file for writing: %1 Datei kann nicht zum Schreiben geöffnet werden: %1 Exported %1 row(s) to %2 %1 Zeile(n) nach %2 exportiert Text Files (*.txt);;All Files (*) Textdateien (*.txt);;Alle Dateien (*) CSV Files (*.csv);;All Files (*) CSV-Dateien (*.csv);;Alle Dateien (*) ADIF Export ADIF exportieren ADIF export requires the query to include the contacts 'id' column. Example: SELECT id, callsign FROM contacts WHERE ... No valid contact IDs found in the result set. Keine gültigen Kontakt-IDs im Ergebnissatz gefunden. Failed to retrieve contact records: %1 Kontaktaufzeichnungen konnten nicht abgerufen werden: %1 No matching contacts found in the database. Keine passenden Kontakte in der Datenbank gefunden. Logging rules cleared (defaults) Protokollierungsregeln zurückgesetzt (Standard) Logging rules applied Protokollierungsregeln angewendet Save Debug Log Debug-Log speichern No debug log file is currently being written Derzeit wird keine Debug-Logdatei geschrieben Log Files (*.log);;All Files (*) Logdateien (*.log);;Alle Dateien (*) Debug log saved to %1 Debug-Log in %1 gespeichert Failed to copy the debug log file. Die Debug-Logdatei konnte nicht kopiert werden. File logging is disabled Dateiprotokollierung ist deaktiviert Log file: %1 Logdatei: %1 DownloadQSLDialog Download QSLs QSLs herunterladen eQSL eQSL eQSL QTH Profile eQSL QTH Profil QSLs Since QSLs seit QSOs Since QSO seit LoTW LoTW My Callsign Eigenes Rufzeichen &Download &Herunterladen LoTW is not configured properly.<p> Please, use <b>Settings</b> dialog to configure it.</p> LoTW ist nicht richtig eingerichtet.<p> Bitte verwende den <b>Einstellungen</b> Dialog, um es einzurichten.</p> eQSL is not configured properly.<p> Please, use <b>Settings</b> dialog to configure it.</p> eQSL ist nicht richtig eingerichtet.<p> Bitte verwende den <b>Einstellungen</b> Dialog, um es einzurichten.</p> Cancel Abbrechen Downloading from %1 Herunterladen von %1 Processing %1 QSLs Verarbeite %1 QSLs QLog Error QLog Fehler %1 update failed: Aktualisierung von %1 fehlgeschlagen: QLog Information QLog Information No service selected Kein Dienst ausgewählt DxFilterDialog DX Cluster Filters DX Cluster Filter General Filters Allgemeine Filter Bands Bänder FTx (FT8/FT4) FTx (FT8/FT4) Continent Kontinent North America Nordamerika Africa Afrika Antarctica Antarktis South America Südamerika Asia Asien Europe Europa Oceania Ozeanien Modes Betriebsarten Phone Phonie CW CW Digital Digital Log Status Log Status New Mode Neuer Mode New Entity Neuer Eintrag New Band Neues Band New Slot Neuer Slot Worked Gearbeitet Confirmed Bestätigt Extended Filters Erweiterte Filter Spotter Continent Spotter Kontinent Do not show the following spots when they have the same Callsign and their time difference is lower than Time Diff and their frequency difference is lower than Freq Diff Folgende Spots nicht anzeigen, wenn sie dasselbe Rufzeichen haben und der Unterschied in Frequenz und Zeit kleiner als die eingestellten Parameter ist Spots Dedup Spot-Dubletten entfernen Time difference Zeitunterschied s s Frequency difference Frequenzunterschied kHz kHz Member Mitglied No Club List is enabled Keine Clubliste aktiviert DxTableModel Time Zeit Callsign Rufzeichen Frequency Frequenz Mode Betriebsart Spotter Comment Kommentar Continent Kontinent Spotter Continent Spotter Kontinent Band Band Member Mitglied Country Land DxWidget Send DX Cluster Command Sende DX-Cluster Kommando Spots Filtered Gefiltert WCY WCY WWV WWV To All An alle Console Konsole 15-min Trend Full-text search Volltextsuche Search Suchen Close Search Suche schließen Filter... Filter... Filter DXC Filter DXC Spot Last QSO Spot letztes QSO Send last QSO spot Letzten QSO-Spot senden Show HF Stats HF Statistik anzeigen Show VHF Stats VHF Statistik anzeigen Show WCY WCY anzeigen Show WWV WWV anzeigen Column Visibility... Spaltensichtbarkeit... Connect on startup Beim Start verbinden Automatic connection after start Automatische Verbindung nach dem Start Delete Server Löschen Server DXC - Delete Server DXC - Löschen Server Clear Password Passwort löschen Keep Spots Spots merken Spots are not cleared when connecting to a new DX Cluster. Spots werden nicht gelöscht, wenn eine Verbindung zu einem neuen DX-Cluster hergestellt wird. Clear Löschen Clear all data Alle Löschen Search... Suchen... DXC - Search DXC- Suchen Which columns should be displayed Welche Spalten sollen angezeigt werden Insert a <b>hostname:port</b> of DXC Server. Füge <b>hostname:port</b> eines DXC Servers ein. Connect Verbinden My Continent Mein Kontinent Auto Automatisch Connecting... Verbinden... DX Cluster is temporarily unavailable DX-Cluster vorübergehend nicht erreichbar DXC Server Error DXC-Serverfehler An invalid callsign Ungültiges Rufzeichen DX Cluster Password DX Cluster Passwort Security Notice Sicherheitshinweis The password can be sent via an unsecured channel Das Passwort kann über einen ungesicherten Kanal gesendet werden Server Server Username Benutzername Disconnect Trennen DX Cluster Command DX-Cluster Kommando DxccTableModel Worked Gearbeitet eQSL LoTW Paper Papier DxccTableWidget Mode Betriebsart EQSLQSLDownloader Incorrect Password or QTHProfile Id Falsches Passwort oder QTHProfil-Id ADIF file not found in eQSL response Keine ADIF-Datei in der eQSL-Antwort gefunden Incorrect Username or password Falscher Benutzername oder Passwort Unknown Error Unbekannter Fehler Cannot opet temporary file Kann temporäre Datei nicht öffnen Cannot save the image to file Kann das Bild nicht in die Datei speichern EQSLUploader Unknown Reply from eQSL Unbekannte Antwort von eQSL EditActivitiesDialog Edit Activities Aktivitätseditor Activities Aktivitäten Add Hinzufügen Edit Bearbeiten Remove Entfernen ExportDialog Export Selected QSOs Ausgewählte QSOs exportieren CSV POTA POTA Export Type Export-Typ Column set Export-Felder Select one of the pre-defined sets of columns or define your own set of columns Wählen Sie eine der vordefinierten Auswahl von ADIF-Feldern aus oder definieren Sie Ihre eigene Auswahl Edit current set of columns Bearbeiten Sie die aktuelle Auswahl von ADIF-Feldern Edit Bearbeiten Mark exported QSOs As Sent Markieren Sie exportierte QSOs als gesendet Export only QSOs that match the active filters Nur QSOs exportieren, die mit den aktiven Filtern übereinstimmen Filters Filter Export only QSOs that match the selected date range Nur QSOs exportieren, die dem ausgewählten Datumsbereich entsprechen Date Range Datumsbereich Export only QSOs that match the selected My Callsign Nur QSOs exportieren, die mit dem ausgewählten eigenen Rufzeichen übereinstimmen My Callsign Eigenes Rufzeichen Export only QSOs that match the selected My Gridsquare Nur QSOs exportieren, die mit dem ausgewählten eigenen Gitterfeld übereinstimmen My Gridsquare Eigenes Gitterfeld Export only QSOs that match the selected QSL Send Via value Exportieren Sie nur QSOs, die der ausgewählten QSL-Ausgang-Via entsprechen QSL Send via QSL Ausgang via Include unusual QSO Sent statuses Unüblichen QSO Sent-Status einbeziehen Include Sent Status Send-Status mit einbeziehen Under normal circumstances this status means <b>"Ignore/Invalid"</b>.<br/>However, it may sometimes be wanted to ignore this setting when sending a QSL. Unter normalen Umständen bedeutet dieser Status <b>"ignorieren/fehlerhaft"</b>.<br/>Manchmal kann es jedoch sinnvoll sein, diese Einstellung beim Senden einer QSL zu ignorieren. "Ignore" "Ignorieren" Under normal circumstances this status means <b>"do not send"</b>.<br/>However, it may sometimes be wanted to ignore this setting when sending a QSL. Unter normalen Umständen bedeutet dieser Status <b>"nicht senden"</b>.<br/>Manchmal kann es jedoch sinnvoll sein, diese Einstellung beim Senden einer QSL zu ignorieren. "No" "Nein" Resend already sent QSOs Bereits gesendete QSOs erneut senden Already Sent Bereits gesendet User Filter Benutzer-Filter Export QSOs that match the selected user QSO Filter QSOs exportieren, die dem ausgewählten Benutzer-QSO-Filter entsprechen Station Profile Stationsprofil File Datei Browse Durchsuchen ADX ADX JSON JSON Export QSOs QSOs exportieren &Export &Exportieren Export only QSOs matching this station profile Nur QSOs exportieren, die tomuto Stationsprofil entsprechen Exporting... Export wird ausgeführt… Cancel Abbrechen QLog Error QLog Fehler Cannot mark exported QSOs as Sent Exportierte QSOs können nicht als gesendet markiert werden Generic Allgemein QSLs QSL QSL-specific QSL-spezifisch All Alle Minimal Custom 1 Benutzerdefiniert 1 Custom 2 Benutzerdefiniert 2 Custom 3 Benutzerdefiniert 3 ExportPasswordDialog Pack Data & Settings Daten und Einstellungen packen Enter a password to encrypt stored credentials. The password will be needed to restore them later. Geben Sie ein Passwort ein, um die gespeicherten Zugangsdaten zu verschlüsseln. Dieses Passwort wird später benötigt, um sie wiederherzustellen. Password Passwort Random Zufällig Confirm Password Passwort bestätigen Delete passwords from Credential Store after export Passwörter nach dem Export aus dem Credential Store löschen FlrigRigDrv Timeout Zeitüberschreitung FLRig response timeout FLRig-Antwortzeitüberschreitung Network Error Netzwerkfehler HRDLogUploader Response message malformed Antwortnachricht fehlerhaft HamQTHCallbook HamQTH HamQTH HamlibRigDrv None Keine CAT CAT DTR DTR RTS RTS Initialization Error Initialisierungsfehler Cannot set PTT Type PTT-Typ kann nicht eingestellt werden Cannot set PTT Share PTT-Share kann nicht eingestellt werden Cannot set CIV Addr CI-V-Adresse kann nicht gesetzt werden Unsupported Rig Driver Nicht unterstützter Rig-Treiber Cannot set auto_power_on auto_power_on kann nicht gesetzt werden Cannot set no_xchg to 1 no_xchg kann nicht auf 1 gesetzt werden Rig Open Error Verbindung fehlgeschlagen Set TX Frequency Error Fehler beim Setzen der TX-Frequenz Set Frequency Error Fehler bei der Frequenzeinstellung Set Split Error Fehler beim Setzen von Split Set Mode Error Fehler bei der Moduseinstellung Set Split Mode Error Fehler beim Setzen des Split-Modus Set PTT Error Fehler beim Auslösen der PTT Cannot sent Morse This cannot be displayed Cannot stop Morse This cannot be displayed Get PTT Error This cannot be displayed Get Frequency Error Fehler bei der Frequenzabfrage Get Mode Error Fehler bei der Mode-Abfrage Get VFO Error Fehler beim Abrufen des VFO Get PWR Error This cannot be displayed Get PWR (power2mw) Error This cannot be displayed Get RIT Function Error This cannot be displayed Get RIT Error This cannot be displayed Get XIT Function Error This cannot be displayed Get XIT Error This cannot be displayed Get Split Error Get TX Frequency Error Get KeySpeed Error This cannot be displayed Set KeySpeed Error This cannot be displayed HamlibRotDrv Initialization Error Initialisierungsfehler Unsupported Rotator Driver Nicht unterstützter Rotor-Treiber Rot Open Error Verbindung fehlgeschlagen Set Possition Error Fehler beim Einstellen der Position Get Possition Error Fehler beim Abrufen der Position ImportDialog Import Importieren Defaults Voreinstellung Comment Kommentar My Rig Eigener Rig Import all or only QSOs from the given period Alle oder nur QSOs aus dem angegebenen Zeitraum importieren File Datei ADX ADX Browse Durchsuchen My Profile Eigenes Profil If DXCC is missing in the imported record, it will be resolved from the callsign. Wenn DXCC im importierten Datensatz fehlt, wird es aus dem Rufzeichen ermittelt. Fill missing DXCC Entity Information Fehlende DXCC-Entitätsinformationen ergänzen Date Range Zeitraum All Alle Options Optionen &Import &Importieren Select File Datei auswählen The value is used when an input record does not contain the ADIF value Der Wert wird verwendet, wenn ein importierter Datensatz den ADIF-Wert nicht enthält The values below will be used when an input record does not contain the ADIF values Die folgenden Werte werden verwendet, wenn ein importierter Datensatz die ADIF-Werte nicht enthält <p><b>In-Log QSO:</b></p><p> <p><b>Logbuch QSO:</b></p><p> <p><b>Importing:</b></p><p> <p><b>Importieren:</b></p><p> Duplicate QSO Doppeltes QSO <p>Do you want to import duplicate QSO?</p>%1 %2 <p>Doppelte QSOs importieren?</p>%1 %2 Save to File In Datei speichern QLog Import Summary Zusammenfassung QLog Import Import date Datum importieren Imported file Datei importieren Imported: %n contact(s) Importiert: %n Kontakt(e) Warning(s): %n Warnungen: %n Error(s): %n Fehler: %n Details Details Import Result Ergebnis des Imports Save Details... Details speichern... InputPasswordDialog Dialog Do not translate -not used The password will be stored in the Credential Store. Das Passwort wird im Credential Store gespeichert. Remember Password Passwort merken KSTChat Unknown User Unbekannter Benutzer Invalid password Falsches Passwort KSTChatWidget Chat messages Chat-Nachrichten Valuable messages Wertvolle Nachrichten Clear selected Callsign and Chat message entry. Ausgewählten Rufzeichen- und Chat-Nachrichteneintrag löschen. Send chat message Chat-Nachricht senden Column Visibility Spaltensichtbarkeit Which columns should be displayed Welche Spalten sollen angezeigt werden Prepare QSO QSO vorbereiten Transfer Callsign and Gridsquare Information to the New QSO dialog Übertragen Sie Rufzeichen und Gitterfeldinformationen in den Neues QSO Dialog Show About Me Only Nur Informationen über mich anzeigen Show only messages where my callsign is present Nur Nachrichten anzeigen, in denen mein Rufzeichen vorkommt Suppress User To User Unterdrückung von Benutzer zu Benutzer Suppress private messages between two callsigns Private Nachrichten zwischen zwei Rufzeichen unterdrücken Highlight Hervorheben Highlight messages based on the setting Hervorhebung von Meldungen je nach Einstellung Highlight Rules Regeln hervorheben Beam Clear Messages Nachrichten löschen Clear all messages in the window Alle Nachrichten im Fenster löschen You Du KSTHighlightRuleDetail Edit Rule Regel bearbeiten Rule Name Regelname Chat Room Nachrichtenraum Enabled Aktiv Highlight message which match Hervorhebung von Nachrichten, die übereinstimmen All the following conditions Alle folgenden Bedingungen Any of the following conditions Eine der folgenden Bedingungen Add Condition Bedingung hinzufügen All Alle Sender Message Nachricht Gridsquare Gitterfeld Contains enthält Starts with beginnt mit Remove Entfernen Must not be empty Darf nicht leer sein KSTHighlighterSettingDialog Highlight Rules Regeln hervorheben Rules Regeln Add Hinzufügen Edit Bearbeiten Remove Entfernen Name Name State Staat KeySequenceEdit Clear Löschen LoadDatabaseDialog Unpack Data & Settings Daten und Einstellungen entpacken Warning Warnung <html><head/><body><p>⚠ <span style=" font-weight:700;">Current database will be DELETED!</span><br/>⚠ <span style=" font-weight:700;">All stored passwords will be DELETED!</span><br/>⚠ <span style=" font-weight:700;">The application will restart after loading</span>.</p><p>To preserve data, use Pack Data &amp; Settings or Export QSOs first.</p></body></html> <html><head/><body><p>⚠ <span style=" font-weight:700;">Die aktuelle Datenbank wird GELÖSCHT!</span><br/>⚠ <span style=" font-weight:700;">Alle gespeicherten Passwörter werden GELÖSCHT!</span><br/>⚠ <span style=" font-weight:700;">Die Anwendung wird nach dem Laden neu gestartet</span>.</p><p>Um Daten zu sichern, verwenden Sie zuerst Pack Data &amp; Settings oder Export QSOs.</p></body></html> Select Database Datenbank auswählen File: Datei: Browse Durchsuchen Status: No file selected Status: Keine Datei ausgewählt Decrypt Credentials Zugangsdaten entschlüsseln Enter password to decrypt credentials Passwort eingeben, um die Zugangsdaten zu entschlüsseln Password: Passwort: Load && Restart Laden und neu starten Invalid password Falsches Passwort Select Database File Datenbankdatei auswählen QLog Database Export (*.dbe);;All Files (*) QLog-Datenbankexport (*.dbe);;Alle Dateien (*) Cannot create temporary file Temporäre Datei kann nicht erstellt werden Decompressing database... Datenbank wird dekomprimiert… Cannot decompress database file Datenbankdatei kann nicht dekomprimiert werden Cannot open database Datenbank kann nicht geöffnet werden Valid database Gültige Datenbank Different platform Andere Plattform Password required to import credentials Passwort zum Importieren der Zugangsdaten erforderlich No encrypted credentials in database Keine verschlüsselten Zugangsdaten in der Datenbank LogFormat Cannot find My DXCC Entity Info Meine DXCC-Informationen fehlen A minimal set of fields not present (start_time, call, band, mode, station_callsign) Ein minimaler Satz von Datenfeldern, die nicht vorhanden sind (start_time, call, band, mode, station_callsign) Outside the selected Date Range Außerhalb des gewählten Datumsbereichs Duplicate Duplikate DXCC Info is missing DXCC-Informationen fehlen no Station Callsign present Kein Stationsrufzeichen vorhanden Cannot insert to database Einfügen in die Datenbank nicht möglich Imported Importiert DXCC State: DXCC Status: Error Fehler Warning Warnung LogbookModel QSO ID Original descriptions may be more meaningful here? QSO ID Time on Startzeit Time off Endzeit Call Rufzeichen SIG (ASCII) SIG SIG Info (ASCII) SIG Info VUCC VUCC Web Web RST Sent RST Ausgang RST Rcvd RST Eingang Frequency Frequenz Band Band Mode Betriebsart Submode Unterart My City (ASCII) Eigene Stadt (ASCII) My Country (ASCII) Eigenes Land (ASCII) My Name (ASCII) Eigener Name (ASCII) My Postal Code (ASCII) Eigene Postleitzahl (ASCII) My Rig (ASCII) Eigener Rig (ASCII) My Special Interest Activity (ASCII) Eigene spezielle Interessenaktivität (ASCII) My Spec. Interes Activity Info (ASCII) Eigene spezielle Interessenaktivität Info (ASCII) My Spec. Interest Activity Info Eigene spezielle Interessenaktivität Info Name Name Notes (ASCII) Anmerkungen (ASCII) QTH Gridsquare Locator DXCC DXCC Country Land Continent Kontinent RSTs RSTa RSTr RSTe Name (ASCII) Name (ASCII) QTH (ASCII) QTH (ASCII) Country (ASCII) Land (ASCII) CQZ CQZ QSLr QSLe QSLr Date QSLe Datum QSLs QSLa QSLs Date QSLa Datum LoTWr LoTWe LoTWr Date LoTWe Datum LoTWs LoTWa LoTWs Date LoTWa Datum TX PWR TX Leistung Additional Fields Zusatzfelder Address Adresse Age Alter A-Index A-Index Antenna Az Antenne Az Antenna El Antenne El Signal Path Signalweg ARRL Section ARRL Sektion Award Submitted Award eingereicht Award Granted Award erteilt Band RX Band RX Contest Check Contest Püfung Class Klasse ClubLog Upload Date Clublog Uploaddatum ClubLog Upload State Clublog Uploadstatus Comment (ASCII) Kommentar (ASCII) Comment Kommentar County Alt Landkreis Alt Contacted Operator Operator kontaktiert Contest ID Contest ID Credit Submitted Beitrag eingereicht Credit Granted Beitrag bewilligt DCLr Date DCLe Datum DCLs Date DCLa Datum DCLr DCLe DCLs DCLa Email Email Owner Callsign Eigentümer Rufzeichen eQSL AG eQSL AG FISTS Number FISTS Nummer FISTS CC EME Init EME Initialisierung Frequency RX Frequenz RX Guest Operator Gastoperator HRDLog Upload Date HRDLog Uploaddatum HRDLog Upload Status HRDLog Uploadstatus IOTA Island ID IOTA Insel-ID K-Index K-Index Latitude Breitengrad Longitude Längengrad Max Bursts MS Shower Name My Antenna (ASCII) Eigene Antenne (ASCII) My Antenna Eigene Antenne My County Alt Eigener Landkreis Alt My DARC DOK Eigenes DARC DOK Operator Callsign Operator Rufzeichen POTA POTA QRZ Download Date QRZ Herunterladen Datum QRZ Download Status QRZ Herunterladen Status QSLs Message (ASCII) QSLs Message QSL-Nachricht QSLr Message QSLr Via QSLe via QSLs Via QSLa via Region Region Rig (ASCII) RcvPWR RcvNr Nr. erhalten RcvExch Info erhalten SentNr Nr. gesendet SentExch Info gesendet My ARRL Section Eigene ARRL Sektion My WWFF Eigener WWFF WWFF Paper Papier LoTW LoTW eQSL eQSL QSL Received QSL Eingang My City Eigene Stadt Address (ASCII) Adresse (ASCII) Altitude Höhe Gridsquare Extended Erweitertes Gitterfeld DOK DOK Distance Entfernung eQSLr Date eQSLe Datum eQSLs Date eQSLa Datum eQSLr eQSLe eQSLs eQSLa HamlogEU Upload Date HamlogEU Uploaddatum HamlogEU Upload Status HamlogEU Uploadstatus HamQTH Upload Date HamQTH Uploaddatum HamQTH Upload Status HamQTH Uploadstatus CW Key Info CW Key Information CW Key Type CW Key Typ My Altitude Eigene Höhe My County Eigener Landkreis My Country Eigenes Land My CQZ Eigene CQZ My DXCC Eigenes DXCC Land My FISTS Eigene FISTS Nummer My Gridsquare Eigener Locator My Gridsquare Extended Eigener erweiterter Locator My IOTA Eigene IOTA Nummer My IOTA Island ID Eigene IOTA Insel-ID My ITU Eigene ITU Zone My Latitude Eigener Breitengrad My Longitude Eigener Längengrad My CW Key Info Eiene CW Key My CW Key Type Eigener CW Key Typ My Name Eigener Name My Postal Code Eigene Postleitzahl My POTA Ref Eigene POTA Ref My Rig Eigener Rig My Special Interest Activity Eigene spezielle Interessenaktivität My SOTA Eigene SOTA Nummer My State Eigener Staat My Street Eigene Strasse My USA-CA Counties Eigene USA-CA Bezirke My VUCC Grids Eigenes VUCC Gitterfeld Notes Anmerkungen #MS Bursts #MS Pings Contest Precedence Contest Vorrang Propagation Mode Ausbreitungsmodus Public Encryption Key Öffentlicher Chiffrierschlüssel QRZ Upload Date QRZ Uploaddatum QRZ Upload Status QRZ Uploadstatus QSL Message QSL-Nachricht QSL Via QSO Completed QSO abgeschlossen QSO Random Rig SAT Mode SAT Betriebsart SAT Name SAT Name Solar Flux Silent Key Verstorben SKCC Member SKCC Mitglied SOTA SOTA Logging Station Callsign Logging Station Rufzeichen SWL SWL Ten-Ten Number Ten-Ten Nummer UKSMG Member UKSMG Mitglied USA-CA Counties USA-CA Bezirke VE Prov ITU ITU Prefix Präfix State Staat County Landkreis IOTA IOTA QSL Sent QSL Ausgang LogbookWidget Delete Löschen Logbook - Delete QSO Logbuch - Löschen QSO Upload to Clublog Nach Clublog hochladen Lookup on Web Nachschlagen im Web Update from Callbook Update vom Callbook Add Missing Info fehlende Angaben ergänzen Mark QSL RCVD QSL als empfangen markieren Mark QSL Requested QSL als angefordert markieren Filter Callsign Rufzeichen filtern Edit Value Wert Bearbeiten Logbook - Edit Value Logbuch - Wert Bearbeiten Column Visibility Which columns should be displayed Welche Spalten sollen angezeigt werden Export Selected Ausgewählte exportieren Export selected QSOs Ausgewählte QSOs exportieren Send DX Spot QSO-Spot senden Logbook - Send DX Spot Logbuch - QSO-Spot senden Callsign Rufzeichen Gridsquare Gitterfeld POTA POTA SOTA SOTA WWFF WWFF SIG SIG IOTA IOTA Delete the selected contacts? Die ausgewählten Kontakte löschen? Clublog's <b>Immediately Send</b> supports only one-by-one deletion<br><br>Do you want to continue despite the fact<br>that the DELETE operation will not be sent to Clublog? Clublog <b>Sofort Upload</b> unterstützt nur das Löschen eines Datensatzes nach dem anderen.<br><br>Möchten Sie fortfahren, obwohl<br>der DELETE-Vorgang nicht an Clublog gesendet wird? Deleting QSOs QSOs Löschen Update Aktualisieren By updating, all selected rows will be affected.<br>The value currently edited in the column will be applied to all selected rows.<br><br>Do you want to edit them? Durch die Aktualisierung werden alle ausgewählten Zeilen beeinflusst.<br>Der aktuell bearbeitete Wert in der Spalte wird auf alle ausgewählten Zeilen angewendet.<br>Möchten Sie fortfahren? Count: %n Anzahl: %n Anzahl: %n Downloading eQSL Image eQSL-Bild herunterladen Cancel Abbrechen All Bands Alle Bänder All Modes Alle Betriebsarten All Countries Alle Länder No User Filter Kein Benutzerfilter QLog Warning QLog Warnung Each batch supports up to 100 QSOs. Jeder Stapel unterstützt bis zu 100 QSOs. QSOs Update Progress QSOs Update Fortschritt QLog Error QLog Fehler Callbook login failed Callbook-Anmeldung fehlgeschlagen Callbook error: Callbook-Fehler: All Clubs Alle Clubs eQSL Download Image failed: eQSL-Bild download fehlgeschlagen: LotwQSLDownloader Cannot open temporary file Kann temporäre Datei nicht öffnen Incorrect login or password Falscher Benutzername oder falsches Passwort LotwUploader Upload cancelled by user Upload vom Benutzer abgebrochen Upload rejected by LoTW Upload durch LoTW abgelehnt Unexpected response from TQSL server Unerwartete Antwort vom TQSL-Server TQSL utility error TQSL-Utility Fehler TQSLlib error TQSLib Fehler Unable to open input file Kann Eingabedatei nicht öffnen Unable to open output file Kann Ausgabedatei nicht öffnen All QSOs were duplicates or out of date range Alle QSOs waren Duplikate oder außerhalb des Datumsbereichs Some QSOs were duplicates or out of date range Einige QSOs waren Duplikate oder außerhalb des Datumsbereichs Command syntax error Befehls-Syntaxfehler LoTW Connection error (no network or LoTW is unreachable) LoTW Verbindungsfehler (kein Netzwerk oder LoTW nicht erreichbar) Unexpected Error from TQSL Unerwarteter Fehler von TQSL TQSL not found TQSL nicht gefunden TQSL crashed TQSL abgestürzt MainWindow Rig Map Karte Toolbar Werkzeugleiste &File &Datei &Logbook &Logbuch &Equipment &Geräte &Help &Hilfe &Window Fe&nster Se&rvice Clock Uhr WSJTX WSJTX Rotator Rotor Bandmap Online Map Online Karte CW Console Chat Profile Image Profilfoto &Settings Einste&llungen &Import &Importieren &Export &Exportieren Mailing List... Edit Bearbeiten Keep Options Optionen behalten Restore connection options after application restart Verbindungsoptionen nach dem Neustart der Anwendung wiederherstellen Connect R&ig Verbinde R&ig Alerts Alerts Quit Beenden Application - Quit App - Beenden Pack Data && Settings Daten und Einstellungen packen Unpack Data && Settings Daten und Einstellungen entpacken New QSO - Clear Neuer Kontakt – Löschen &About &Über New QSO - Save Neuer Kontakt – Speichern S&tatistics S&tatistik QSL &Gallery QSL &Galerie Developer Tools Entwickler-Tools Run custom read-only SQL queries against the logbook database Benutzerdefinierte SQL-Abfragen (nur lesen) für die Logbuchdatenbank ausführen Print QSL &Labels QSL-Etiketten &drucken Connect R&otator Verbinde R&otor QSO &Filters QSO &Filter &Awards DXCC &Submission List DXCC-&Einreichungsliste Generate a list of contacts to submit for ARRL DXCC award credit Liste von Verbindungen zur Einreichung für ARRL DXCC-Anerkennung erstellen Beep Connect &CW Keyer Verbinde &CW Keyer &Wiki &Wiki Report &Bug... Fehler &melden... &Manual Entry &Manueller Eintrag Switch New Contact dialog to the manually entry mode<br/>(time, freq, profiles etc. are not taken from their common sources) Schalte den Dialog "Neuer Kontakt" in den manuellen Eingabemodus<br/>(Zeit, Frequenz, Profile etc. werden nicht aus ihren gemeinsamen Quellen übernommen) Save Arrangement Arrangement speichern Logbook - Search Callsign Logbuch - Suche New QSO - Add text from Callsign field to Bandmap Neuer Kontakt - Übertragen Sie den Text vom Rufzeichenfeld in die Bandmap Rig - Band Down Rig - Band (-) Rig - Band Up Rig - Band (+) New QSO - Use Callsign from the Whisperer Neuer Kontakt - Verwende Rufzeichen vom Whisperer CW Console - Key Speed Up CW Console - Geschwindigkeit (+) CW Console - Key Speed Down CW Console - Geschwindigkeit (-) CW Console - Profile Up CW Console - CW-Keyer Profil (+) CW Console - Profile Down CW Console - CW-Keyer Profil (-) Rig - PTT On/Off Rig - PTT On/Off All Bands Alle Bänder Each Band Jedes Band Each Band && Mode Jedes Band & Mode No Check Keine Kontrolle Single Eine für alles Per Band Band Stop Stoppen Reset Zurücksetzen None Keine Upload Hochladen Service - Upload QSOs Service – QSO hochladen Download QSLs QSLs herunterladen Service - Download QSLs Service - QSLs herunterladen Theme: Native Thema: Native Theme: QLog Light Thema: QLog Light Theme: QLog Dark Thema: QLog Dark What's New Was ist neu Export Cabrillo Cabrillo exportieren Wsjtx Wsjtx Contest Contest Dupe Check Dupe-Prüfung Sequence Sequenz Linking Exchange With Exchange mit verknüpfen Edit Rules Regeln bearbeiten Clear Löschen Show Alerts Alerts anzeigen About Über DX Cluster Color Theme Farbschema Not enabled for non-Fusion style Für keinen anderen Stil als Fusion zulässig Press to tune the alert Drücken zum Einstellen des Alarms Clublog Immediately Upload Error Clublog-Sofort-Upload-Fehler <b>Error Detail:</b> op: op: A New Version Eine neue Version A new version %1 is available. Eine neue Version %1 ist verfügbar. Remind Me Later Später erinnern Download Herunterladen Failed to encrypt credentials. Verschlüsseln der Zugangsdaten fehlgeschlagen. Database files (*.dbe);;All files (*) Datenbankdateien (*.dbe);;Alle Dateien (*) Failed to create temporary file. Temporäre Datei konnte nicht erstellt werden. Failed to dump the database. Datenbank-Dump fehlgeschlagen. Compressing database... Datenbank wird komprimiert… Database successfully dumped to %1 Datenbank erfolgreich exportiert nach %1 Failed to compress the database. Datenbank konnte nicht komprimiert werden. Failed to prepare database for import. Datenbank konnte nicht für den Import vorbereitet werden. Classic Klassisch Do you want to remove the Contest filter %1? Möchten Sie den Contest-Filter %1 entfernen? Contest: Contest: <h1>QLog %1</h1><p>&copy; 2019 Thomas Gatzweiler DL2IC<br/>&copy; 2021-2026 Ladislav Foldyna OK1MLG<br/>&copy; 2025-2026 Michael Morgan AA5SH<br/>&copy; 2025-2026 Kyle Boyle VE9KZ</p><p>Based on Qt %2<br/>%3<br/>%4<br/>%5</p><p>Icon by <a href='http://www.iconshock.com'>Icon Shock</a><br />Satellite images by <a href='http://www.nasa.gov'>NASA</a><br />ZoneDetect by <a href='https://github.com/BertoldVdb/ZoneDetect'>Bertold Van den Bergh</a><br />TimeZone Database by <a href='https://github.com/evansiroky/timezone-boundary-builder'>Evan Siroky</a> <h1>QLog %1</h1><p>&copy; 2019 Thomas Gatzweiler DL2IC<br/>&copy; 2021-2026 Ladislav Foldyna OK1MLG<br/>&copy; 2025-2026 Michael Morgan AA5SH<br/>&copy; 2025-2026 Kyle Boyle VE9KZ</p><p>Qt %2<br/>%3<br/>%4<br/>%5</p><p>Icon by <a href='http://www.iconshock.com'>Icon Shock</a><br />Satellite images by <a href='http://www.nasa.gov'>NASA</a><br />ZoneDetect by <a href='https://github.com/BertoldVdb/ZoneDetect'>Bertold Van den Bergh</a><br />TimeZone Database by <a href='https://github.com/evansiroky/timezone-boundary-builder'>Evan Siroky</a> N/A MapWebChannelHandler Grid Gitterfeld Gray-Line Beam Aurora MUF IBP Chat WSJTX - CQ WSJTX - CQ Path Weg NewContactWidget Callsign Rufzeichen Date Datum Time On Startzeit W Rig RSTr RSTe TX: RX: RSTs RSTa 80m &Details &Details the contacted station's DARC DOK (District Location Code) (ex. A01) DARC-DOK (Ortsverbandkenner) der kontaktierten Station (z.B. A01) Station Station Info <b>DUPE !!!</b> <b>DUPE !!!</b> Lookup the call on the web. The query URL can be changed in Settings -> Callbook Suchen Sie das Rufzeichen im Internet. Die Abfrage-URL kann unter Einstellungen -> Callbook geändert werden Web Duration Dauer World Wide Flora & Fauna QSL Send Status QSL Ausgangsstatus Paper Papier <b>Yes</b> - an outgoing QSL card has been sent; the QSO has been uploaded to, and accepted by, the online service<br/><b>No</b> - do not send an outgoing QSL card; do not upload the QSO to the online service<br/><b>Requested</b> - the contacted station has requested a QSL card; the contacted station has requested the QSO be uploaded to the online service<br/><b>Queued</b> - an outgoing QSL card has been selected to be sent; a QSO has been selected to be uploaded to the online service<br/> <b>Ja</b> - eine ausgehende QSL-Karte wurde gesendet; das QSO wurde in den Online-Dienst hochgeladen und von diesem akzeptiert.<br/><b>Nein</b> - keine ausgehende QSL-Karte senden; das QSO nicht in den Online-Dienst hochladen.<br/><b>Angefordert</b> - die kontaktierte Station hat eine QSL-Karte angefordert; die kontaktierte Station hat angefordert, dass das QSO in den Online-Dienst hochgeladen wird.<br/><b>Wartend</b> - eine ausgehende QSL-Karte wurde zum Senden ausgewählt; ein QSO wurde zum Hochladen in den Online-Dienst ausgewählt.<br/> LoTW eQSL QSL Send via QSL Ausgang via D&X Stats D&X Stats <b>DXCC Statistics</b> <b>DXCC-Statistik</b> <b>Station Statistics</b> <b>Stationsstatistik</b> Blank Leer My &Notes Eigene &Anmerkungen Member: Mitglied: No Nein Yes Ja Requested Angefordert Queued Wartend QSL via QSL via Propagation Mode Ausbreitungsmodus Bureau Büro Direct Direkt Antenna Antenne MHz M&y Station &Eigene Station Reset Zurücksetzen Save Speichern Frequency Frequenz 59 Mode Betriebsart Ignored Ignoriert Electronic Elektronisch QLog Error QLog Fehler Callbook login failed Callbook-Anmeldung fehlgeschlagen LP LP New Entity! Neuer Eintrag! New Band! Neues Band! New Mode! Neue Betriebsart! New Band & Mode! Neues Band & Betriebsart! New Slot! Neuer Slot! Worked Gearbeitet Confirmed Bestätigt GE GA GM GM GA GT m Callbook search is active Callbook-Suche ist aktiv Contest ID must be filled in to activate Zur Aktivierung muss die Contest-ID ausgefüllt werden It is not the name of the contest but it is an assigned<br>Contest ID (ex. CQ-WW-CW for CQ WW DX Contest (CW)) Description of the contacted station's equipment Beschreibung der Ausrüstung der kontaktierten Station Callbook search is inactive Callbook-Suche ist inaktiv Expand/Collapse Erweitern/Zusammenklappen two or four adjacent Maidenhead grid locators, each four characters long, (ex. EN98,FM08,EM97,FM07) zwei oder vier nebeneinander liegende, jeweils vier Zeichen lange Gitterfelder (z. B. EN98,FM08,EM97,FM07) Special Activity Group Spezielle Aktivitätsgruppe Special Activity Group Information OmnirigRigDrv Rig 1 Rig 1 Rig 2 Rig 2 Initialization Error Initialisierungsfehler Rig status changed Rig-Status geändert Rig is not connected Rig nicht verbunden OmnirigV2RigDrv Rig 1 Rig 1 Rig 2 Rig 2 Rig 3 Rig 3 Rig 4 Rig 4 Initialization Error Initialisierungsfehler Rig status changed Rig-Status geändert Rig is not connected Rig nicht verbunden PSTRotDrv Rot 1 Cannot bind a port Ein Port kann nicht gebunden werden Cannot get IP Address for IP-Adresse kann nicht abgerufen werden No IPv4 Address for Keine IPv4-Adresse für Error Occurred Fehler ist aufgetreten Operation Timeout Zeitüberschreitung PaperQSLDialog Manage QSL Card QSL-Karte verwalten Available QSLs Verfügbare QSL Copy the input file from the source folder to the QLog Internal QSL Storage folder.<br/>The original file remains unchanged in the source folder Kopieren Sie die Bilddatei aus dem Quellordner in den Ordner "QLog Internal QSL Storage".<br/>Die Originaldatei bleibt unverändert im Quellordner Import QSL QSL importieren Add File Datei hinzufügen Remove Entfernen Delete Löschen Delete QSL? Löschen QSL? Open Öffnen Toggle Favorite Favorit PlatformSettingsDialog Platform-specific Settings Plattform-spezifische Einstellungen The database was exported from a different platform. Please verify or update the following settings. You can leave fields empty and configure them later in Settings. Die Datenbank wurde von einer anderen Plattform exportiert. Bitte überprüfen oder aktualisieren Sie die folgenden Einstellungen. Sie können Felder leer lassen und später in den Einstellungen konfigurieren. Setting Einstellungen Value Wert Continue Fortsetzen Select File Datei auswählen All Files (*) Alle Dateien (*) ProfileImageWidget Profile Image Profilfoto QCoreApplication QLog Help QLog Hilfe QMessageBox QLog Error QLog Fehler QLog is already running QLog wird bereits ausgeführt Failed to process pending database import. Ausstehenden Datenbankimport konnte nicht verarbeitet werden. The database was imported successfully, but the stored passwords could not be restored (decryption failed or the data is corrupted). All service passwords have been cleared and must be re-entered in Settings. Die Datenbank wurde erfolgreich importiert, aber die gespeicherten Passwörter konnten nicht wiederhergestellt werden (Entschlüsselung fehlgeschlagen oder Daten sind beschädigt). Alle Service-Passwörter wurden gelöscht und müssen in den Einstellungen erneut eingegeben werden. Could not connect to database. Keine Verbindung zur Datenbank möglich. Could not export a QLog database to ADIF as a backup.<p>Try to export your log to ADIF manually Kann QLog-Datenbank nicht als Backup nach ADIF exportieren.<p>Versuchen Sie, Ihr Log manuell nach ADIF zu exportieren Database migration failed. Migration der Datenbank fehlgeschlagen. QLog Warning QLog Warnung Club List Update failed. Cannot remove old records Aktualisierung der Clubliste fehlgeschlagen. Alte Datensätze können nicht entfernt werden Club List Update failed. Cannot plan new downloads Aktualisierung der Clubliste fehlgeschlagen. Kann keine neuen Downloads durchführen QLog Critical QLog kritischer Fehler Cannot save a password for %1 to the Credential Store Das Passwort für %1 kann nicht im Passwort Speicher gespeichert werden Cannot get a password for %1 from the Credential Store Es kann kein Passwort für %1 aus dem Passwort Speicher abgerufen werden Unexpected Club List download. Canceling next downloads Unerwartete Clubliste herunterladen. Nächste Downloads beenden Unexpected Club List content for Unerwarteter Inhalt der Clubliste für Network error. Cannot download Club List for Netzwerkfehler. Clubliste kann nicht heruntergeladen werden für DXC Server Name Error DXC Server Namensfehler DXC Server address must be in format<p><b>[username@]hostname:port</b> (ex. hamqth.com:7300)</p> Format der DXC-Server-Adresse <p><b>[benutzername@]hostname:Port</b> (z. B. hamqth.com:7300)</p> DX Cluster Password DX Cluster Passwort Invalid Password Falsches Passwort DXC Server Connection Error DXC Server Verbindungsfehler The fields <b>%0</b> will not be saved because the <b>%1</b> is not filled. Die Felder <b>%0</b> werden nicht gespeichert, da <b>%1</b> nicht ausgefüllt ist. Your callsign is empty. Please, set your Station Profile Ihr Rufzeichen ist nicht angegeben. Bitte richten Sie Ihr Stationsprofil ein QLog Info QLog Info Activity name is already exists. Der Aktivitätsname existiert bereits. Rule name is already exists. Name der Regel ist bereits vorhanden. Callsign Regular Expression is incorrect. Der reguläre Ausdruck für das Rufzeichen ist fehlerhaft.. Comment Regular Expression is incorrect. Kommentar Regulärer Ausdruck ist fehlerhaft. Cannot Update Alert Rules Kann Alarmregeln nicht aktualisieren Filter name is already exists. Filtername ist bereits vorhanden. Please, define at least one Station Locations Profile Bitte definieren Sie mindestens ein Stationsstandortprofil WSJTX Multicast is enabled but the Address is not a multicast address. WSJTX Multicast ist aktiviert, aber die Adresse ist keine Multicast-Adresse. Loop detected. Raw UDP forward uses the same port as the WSJT-X receiving port. Schleife erkannt. Raw-UDP-Weiterleitung verwendet denselben Port wie der WSJT-X-Empfangsport. Rig port must be a valid COM port.<br>For Windows use COMxx, for unix-like OS use a path to device Rig port muss ein gültiger COM-Port sein.<br>Für Windows verwende COMxx, für Unix-ähnliche Betriebssysteme verwende einen Pfad zum Gerät Rig PTT port must be a valid COM port.<br>For Windows use COMxx, for unix-like OS use a path to device Rig-PTT-Port muss ein gültiger COM-Port sein. Für Windows verwenden Sie COMxx, für Unix-ähnliche Betriebssysteme verwenden Sie einen Pfad zum Gerät <b>TX Range</b>: Max Frequency must not be 0. <b>TX-Bereich</b>: Max Frequenz darf nicht 0 sein. <b>TX Range</b>: Max Frequency must not be under Min Frequency. <b>TX-Bereich</b>: Die Maximalfrequenz darf nicht niedriger als die Minimalfrequenz sein. Rotator port must be a valid COM port.<br>For Windows use COMxx, for unix-like OS use a path to device Rotor-Port muss ein gültiger COM-Port sein.<br>Für Windows verwenden Sie COMxx, für Unix-ähnliche Betriebssysteme verwenden Sie einen Pfad zum Gerät CW Keyer port must be a valid COM port.<br>For Windows use COMxx, for unix-like OS use a path to device CW Keyer Port muss ein gültiger COM-Port sein.<br>Für Windows verwenden Sie COMxx, für unix-ähnliche Betriebssysteme verwenden Sie einen Pfad zum Gerät Cannot change the CW Keyer Model to <b>Morse over CAT</b><br>No Morse over CAT support for Rig(s) <b>%1</b> Kann das CW-Keyer Modell nicht zu <b>CW-über-CAT</b> ändern.<br>Keine CW-über-CAT Unterstützung für Rig(s) <b>%1</b> Cannot delete the CW Keyer Profile<br>The CW Key Profile is used by Rig(s): <b>%1</b> Kann das CW-Keyer Profil nicht löschen.<br>Das Profil wird verwendet von Rig(s): <b>%1</b> Operator Callsign has an invalid format Das Operator-Rufzeichen hat ein ungültiges Format Gridsquare has an invalid format Gitterfeld hat ein falsches Format VUCC Grids have an invalid format (must be 2 or 4 Gridsquares separated by ',') VUCC Gitterfeld hat ein falsches Format (es müssen 2 oder 4 Gitterfelder sein, getrennt durch ',') Country must not be empty Das Land darf nicht leer sein CQZ must not be empty CQZ darf nicht leer sein ITU must not be empty ITU darf nicht leer sein Callsign has an invalid format Rufzeichen hat ein falsches Format Filename is empty Dateiname ist leer Cannot write to the file Kann nicht in die Datei schreiben QLog Information QLog Information Exported. Exportiert. Exported %n contact(s). %n Kontakt(e) exportiert. Chat Error: Chat Fehler: Cannot update QSO Filter Conditions QSO-Filter kann nicht aktualisiert werden <b>Rig Error:</b> <b>Rotator Error:</b> <b>CW Keyer Error:</b> QObject Cannot connect to DXC Server <p>Reason <b>: Kann keine Verbindung zum DXC Server herstellen <p>Ursache <b>: Connection Refused Verbindung abgelehnt Host closed the connection Der Host hat die Verbindung beendet Host not found Host nicht gefunden Timeout Zeitüberschreitung Network Error Netzwerkfehler Internal Error Interner Fehler Importing Database Datenbank wird importiert Opening Database Datenbank öffnen Backuping Database Datenbank sichern Migrating Database Datenbank migrieren Starting Application Anwendung starten My Rig Eigener Rig Logging Station Callsign Logging-Station Rufzeichen My Gridsquare Eigenes Gitterfeld Operator Name Operator Name Operator Callsign Operator Rufzeichen My City Eigene Stadt My Country Eigenes Land My County Eigener Landkreis My IOTA Eigene IOTA Nummer My SOTA Eigene SOTA Nummer My Special Interest Activity Eigene spezielle Interessenaktivität My Spec. Interes Activity Info Eigene spezielle Interessenaktivität Info My VUCC Grids Eigenes VUCC Gitterfeld My WWFF Eigener WWFF My POTA Ref Eigene POTA Ref My DARC DOK Eigenes DARC DOK My ITU Eigene ITU Zone My CQZ Eigene CQZ My DXCC Eigenes DXCC Land <b>Imported</b>: %n contact(s) <b>Importiert</b>: %n Kontakt(e) <b>Warning(s)</b>: %n <b>Warnungen</b>: %n <b>Error(s)</b>: %n <b>Fehler</b>: %n km km miles mil Not a valid QLog database Keine gültige QLog-Datenbank Database version too new (requires newer QLog version) Datenbankversion zu neu (erfordert neuere QLog-Version) Database is not QLog Export file Datenbank ist keine QLog-Exportdatei TQSL Path TQSL Pfad (Flatpak internal path) (Interner Flatpak-Pfad) Rig Rig Rig PTT Rig rigctld Rotator Rotor CW Keyer CW Key TOTAL Worked Summe Gearbeitet TOTAL Confirmed Summe Bestätigt Confirmed Bestätigt Worked Gearbeitet QRZCallbook QRZ.com QRZ.com QRZUploader General Error Allgemeiner Fehler QSLGalleryDialog QSL Card Gallery QSL-Karten-Galerie Search by callsign... Nach Rufzeichen suchen… Sort by: Sortieren nach: Export Filtered Gefilterte exportieren Date (Newest) Datum (neueste) Date (Oldest) Datum (älteste) Callsign (A-Z) Rufzeichen (A-Z) Callsign (Z-A) Rufzeichen (Z-A) %n QSL card(s) %n QSL-Karte(n) %n QSL-Karte(n) All QSL Cards Alle QSL-Karten Favorites Favoriten By Country Nach Land By Date Nach Datum By Band Nach Band By Mode Nach Modus By Continent Nach Kontinent Remove from Favorites Aus Favoriten entfernen Add to Favorites Zu Favoriten hinzufügen Open Öffnen Save... Speichern… Save QSL Card QSL-Karte speichern Export QSL Cards QSL-Karten exportieren Exported %1 of %2 cards %1 von %2 Karten exportiert QSLImportStatDialog QSL Import Summary Zusammenfassung QSL-Import Summary Zusammenfassung Downloaded: Heruntergeladen: Updated: Aktualisiert: Unmatched: Nicht gefunden: Errors: Fehler: Details Details New QSLs: Neue QSLs: Updated QSOs: Aktualisierte QSOs: Unmatched QSLs: Nicht gefundene QSLs: QSLPrintLabelDialog Print QSL Labels QSL-Etiketten drucken Filter Filter Date Range My Callsign Eigenes Rufzeichen Station Profile Stationsprofil QSL Sent QSL Ausgang User Filter Benutzer-Filter Label Template Etikettenvorlage Page Size: Seitengröße: Columns: Spalte: Rows: Zeile: Label Width: Etikettenbreite: Label Height: Etikettenhöhe: Left Margin: Linker Rand: Top Margin: Oberer Rand: H Spacing: Horizontaler Abstand: V Spacing: Vertikaler Abstand: Label Appearance Erscheinungsbild des Etiketts Print Label Borders Etikettenränder drucken QSOs per Label: QSOs pro Etikett: Footer Left Text: Linker Fußzeilentext: Footer Right Text: Rechter Fußzeilentext: Skip Label: Etikett überspringen: Sans Font: Serifenlose Schrift: Mono Font: Monospace-Schrift: Callsign Size: Größe des Rufzeichens: "To Radio" Size: Größe „To Radio“: "To Radio" Text: Text „To Radio“: Header Size: Kopfzeilen-Größe: Data Size: Datengröße: Date Header Text: Datum-Header-Text: Date Format: Datumsformat: Time Header Text: Zeit-Header-Text: Band Header Text: Band-Header-Text: Mode Header Text: Modus-Header-Text: QSL Header Text: QSL-Header-Text: Extra Column: Zusätzliche Spalte: Extra Column Text Text der zusätzlichen Spalte (DB column name) (Datenbank-Spaltenname) No matching QSOs found Keine passenden QSOs gefunden Page 0 of 0 Seite 0 von 0 Labels: 0 (0 pages) Etiketten: 0 (0 Seiten) Print Drucken Export as PDF Als PDF exportieren Custom Benutzerdefiniert Empty Leer QSOs matching this station profile QSOs, die diesem Stationsprofil entsprechen Labels: %1 (%2 pages) Etiketten: %1 (%2 Seiten) Page %1 of %2 Seite %1 von %2 Export PDF PDF exportieren PDF Files (*.pdf) PDF-Dateien (*.pdf) Mark as Sent Als gesendet markieren Mark printed/exported QSOs as sent? Gedruckte/exportierte QSOs als gesendet markieren? QSODetailDialog Frequency Frequenz dd/MM/yyyy HH:mm:ss Time On Startzeit Time Off Endzeit TX: MHz RX: Blank Leer Callsign Rufzeichen Mode Betriebsart RSTs RSTa RSTr RSTe Band QTH Name Name Comment Kommentar My Notes Eigene Anmerkungen about:blank &Details &Details Country Land Cont Kontinent ITU CQ State Staat County Landkreis Age Alter AF AN AS EU NA OC SA two or four adjacent Gridsquares, each four characters long, (ex. EN98,FM08,EM97,FM07) zwei oder vier nebeneinander liegende, jeweils vier Zeichen lange Gitterfelder (z. B. EN98,FM08,EM97,FM07) IOTA POTA SOTA SIG SIG Info FISTS FISTS SKCC SKCC Ten-Ten Ten-Ten UKSMG UKSMG FISTS CC D&X Stats D&X Stats <b>DXCC Statistics</b> <b>DXCC-Statistik</b> <b>Station Statistics</b> <b>Stationsstatistik</b> Operator Callsign Operator Rufzeichen QSLr Message QSLs Message QSL-Nachricht Contest ID Contest ID Member: Mitglied: DOK Gridsquare Gitterfeld the contacted station's DARC DOK (District Location Code) (ex. A01) DARC-DOK (Ortsverbandkenner) der kontaktierten Station (z.B. A01) VUCC WWFF E-Mail URL Propagation Mode Ausbreitungsmodus Satellite Name Satellit-Name Satellite Mode Satellit-Betriebsart M&y Station &Eigene Station Operator Name Operator Name Rig Antenna Antenne Power Leistung W Sig Info &QSL &QSL - Manage QSL Card QSL-Karte verwalten <b>Yes</b> - an outgoing QSL card has been sent; the QSO has been uploaded to, and accepted by, the online service<br/><b>No</b> - do not send an outgoing QSL card; do not upload the QSO to the online service<br/><b>Requested</b> - the contacted station has requested a QSL card; the contacted station has requested the QSO be uploaded to the online service<br/><b>Queued</b> - an outgoing QSL card has been selected to be sent; a QSO has been selected to be uploaded to the online service<br/> <b>Ja</b> - eine ausgehende QSL-Karte wurde gesendet; das QSO wurde in den Online-Dienst hochgeladen und von diesem akzeptiert.<br/><b>Nein</b> - keine ausgehende QSL-Karte senden; das QSO nicht in den Online-Dienst hochladen.<br/><b>Angefordert</b> - die kontaktierte Station hat eine QSL-Karte angefordert; die kontaktierte Station hat angefordert, dass das QSO in den Online-Dienst hochgeladen wird.<br/><b>Wartend</b> - eine ausgehende QSL-Karte wurde zum Senden ausgewählt; ein QSO wurde zum Hochladen in den Online-Dienst ausgewählt.<br/> QSL Sent via QSL Ausgang via Received Eingang Paper Papier LoTW LoTW eQSL eQSL QSL via Sent Ausgang Show QSL Card QSL-Karte ansehen <b>Yes</b> - an incoming QSL card has been received; the QSO has been confirmed by the online service<br/><b>No</b> - an incoming QSL card has not been received; the QSO has not been confirmed by the online service<br/><b>Requested</b> - the logging station has requested a QSL card; the logging station has requested the QSO be uploaded to the online service<br/> <b>Ja</b> - eine eingehende QSL-Karte wurde angenommen; das QSO wurde vom Online-Dienst bestätigt.<br/><b>Nein</b> - eine eingehende QSL-Karte wurde nicht angenommen; das QSO wurde vom Online-Dienst nicht bestätigt.<br/><b>Angefordert</b> - die loggende Station hat eine QSL-Karte angefordert; die loggende Station hat verlangt, dass das QSO in den Online-Dienst hochgeladen wird.<br/> &Contest RcvNr Nr. erhalten RcvExch Info erhalten SendNr Nr. gesendet SendExch Info gesendet &Reset &Zurücksetzen &Lookup &Suchen No Nein Yes Ja Requested Angefordert Queued Wartend Ignored Ignoriert Bureau Büro Direct Direkt Electronic Elektronisch Submit changes Änderungen übermitteln Really submit all changes? Wirklich alle Änderungen übermitteln? QLog Error QLog Fehler Cannot save all changes - internal error Es können nicht alle Änderungen gespeichert werden - interner Fehler Cannot save all changes - try to reset all changes Es können nicht alle Änderungen gespeichert werden - versuchen Sie, alle Änderungen zurückzusetzen QSO Detail QSO Detail Edit QSO QSO Bearbeiten Downloading eQSL Image eQSL-Bild herunterladen Cancel Abbrechen eQSL Download Image failed: eQSL-Bild download fehlgeschlagen: DX Callsign must not be empty DX-Rufzeichen darf nicht leer sein DX callsign has an incorrect format DX-Rufzeichen hat ein falsches Format TX Frequency or Band must be filled TX-Frequenz oder -Band muss ausgefüllt sein DX Grid has an incorrect format DX Gitterfeld hat ein fehlerhaftes Format Based on callsign, DXCC Country is different from the entered value - expecting Based on callsign, DXCC Continent is different from the entered value - expecting Based on callsign, DXCC ITU is different from the entered value - expecting Based on callsign, DXCC CQZ is different from the entered value - expecting Based on own callsign, own DXCC ITU is different from the entered value - expecting Based on own callsign, own DXCC CQZ is different from the entered value - expecting Based on own callsign, own DXCC Country is different from the entered value - expecting Based on SOTA Summit, Grid does not match SOTA Grid - expecting Based on POTA record, QTH does not match POTA Name - expecting Based on POTA record, Grid does not match POTA Grid - expecting Based on SOTA Summit, my QTH does not match SOTA Summit Name - expecting Based on SOTA Summit, my Grid does not match SOTA Grid - expecting Based on POTA record, my QTH does not match POTA Name - expecting Based on POTA record, my Grid does not match POTA Grid - expecting VUCC has an incorrect format VUCC hat ein fehlerhaftes Format blank Leer Based on Frequencies, Sat Mode should be Sat name must not be empty Der Satellitenname darf nicht leer sein Own Callsign must not be empty Eigenes Rufzeichen darf nicht leer sein Own callsign has an incorrect format Eigenes Rufzeichen hat ein fehlerhaftes Format Own VUCC Grids have an incorrect format Eigenes VUCC Gitterfeld hat ein fehlerhaftes Format LoTW Sent Status to <b>No</b> does not make any sense if QSL Sent Date is set. Set Date to 1.1.1900 to leave the date field blank LoTW Sent Status <b>Nein</b> macht keinen Sinn, wenn ein QSL Sent Datum gesetzt ist. Setzen Sie das Datum auf 1.1.1900, um das Datumsfeld leer zu lassen Date should be present for LoTW Sent Status <b>Yes</b> Ein Datum sollte für LoTW Sent Status <b>Ja</b> angegeben werden eQSL Sent Status to <b>No</b> does not make any sense if QSL Sent Date is set. Set Date to 1.1.1900 to leave the date field blank eQSL Sent Status <b>Nein</b> macht keinen Sinn, wenn ein QSL Sent Datum eingestellt ist. Setzen Sie Datum auf 1.1.1900, um das Datumsfeld leer zu lassen Date should be present for eQSL Sent Status <b>Yes</b> Ein Datum sollte für eQSL Sent Status <b>Ja</b> angegeben werden Paper Sent Status to <b>No</b> does not make any sense if QSL Sent Date is set. Set Date to 1.1.1900 to leave the date field blank Papier Sent Status <b>Nein</b> macht keinen Sinn, wenn ein QSL Sent Datum gesetzt ist. Setzen Sie das Datum auf 1.1.1900, um das Datumsfeld leer zu lassen Date should be present for Paper Sent Status <b>Yes</b> Ein Datum sollte für Papier Sent Status <b>Ja</b> angegeben werden Based on SOTA Summit, QTH does not match SOTA Summit Name - expecting Auf Grundlage der SOTA-Daten stimmt das QTH nicht mit dem SOTA Gipfelname überein - erwartend TX Band should be TX-Band sollte sein RX Band should be RX-Band sollte sein Callbook error: Callbook-Fehler: <b>Warning: </b> <b>Warnung: </b> Validation Überprüfung Yellow marked fields are invalid.<p>Nevertheless, save the changes?</p> Gelb markierte Felder sind ungültig.<p>Dennoch die Änderungen speichern?</p> &Save &Speichern &Edit B&earbeiten QSOFilterDetail QSO Filter Detail QSO Filter Details Filter Name: Filter Name: Find QSO which match QSO mit Suchfilter finden All the following conditions Alle folgenden Bedingungen Any of the following conditions Eine der folgenden Bedingungen Add Condition Bedingung hinzufügen Equal gleich Not Equal nicht gleich Contains enthält Not Contains enthält nicht Greater Than größer als Less Than kleiner als Starts with beginnt mit RegExp Remove Entfernen Must not be empty Darf nicht leer sein QSOFilterDialog QSO Filters QSO-Filter Filters Filter Add Hinzufügen Edit Bearbeiten Remove Entfernen Rig No Rig Profile selected Kein Rig-Profil ausgewählt Rigctld Error Rigctld-Fehler Initialization Error Initialisierungsfehler Internal Error Interner Fehler Cannot open Rig Kann Rig nicht öffnen RigWidget Form RX Disconnected Nicht verbunden MHz MHz Disable Split Split deaktivieren RIT: 0.00000 MHz XIT: 0.00000 MHz PWR: %1W RigctldAdvancedDialog Rigctld Advanced Settings Erweiterte Rigctld-Einstellungen Rigctld Path: Rigctld-Pfad: Leave empty for auto-detection Leer lassen für automatische Erkennung Browse Durchsuchen Auto-detect Rigctld path Rigctld-Pfad automatisch erkennen Auto-Detect Automatische Erkennung Rigctld Version: Rigctld-Version: Additional Arguments: Zusätzliche Argumente: e.g. -v -v for verbose logging z. B. -v -v für ausführliche Protokollierung Cannot be changed Kann nicht geändert werden Auto Detect Automatische Erkennung rigctld was not found on this system. Please install Hamlib or specify the path manually. rigctld wurde auf diesem System nicht gefunden. Bitte installieren Sie Hamlib oder geben Sie den Pfad manuell an. Executable (*.exe);;All files (*.*) Ausführbare Datei (*.exe);;Alle Dateien (*.*) All files (*) Alle Dateien (*) Select rigctld executable Rigctld ausführbare Datei auswählen Not found Nicht gefunden RigctldManager rigctld executable not found in /app/bin/. This should not happen in Flatpak build. Rigctld-Ausführbare Datei wurde in /app/bin/ nicht gefunden. Dies sollte im Flatpak-Build nicht passieren. rigctld executable not found. Please install Hamlib or specify the path in Advanced settings. Rigctld-Ausführbare Datei wurde nicht gefunden. Bitte installieren Sie Hamlib oder geben Sie den Pfad in den erweiterten Einstellungen an. Hamlib major version mismatch: QLog was compiled with Hamlib %1 but rigctld reports version %2.%3.%4. Rig model IDs are incompatible between major versions. Hamlib-Hauptversionskonflikt: QLog wurde mit Hamlib %1 kompiliert, aber rigctld meldet Version %2.%3.%4. Rig-Modell-IDs sind zwischen Hauptversionen nicht kompatibel. Port %1 is already in use. Another rigctld or application may be running on this port. Port %1 wird bereits verwendet. Ein anderer rigctld oder eine Anwendung läuft möglicherweise auf diesem Port. rigctld started but not responding on port %1. Rigctld wurde gestartet, reagiert aber nicht auf Port %1. Failed to start rigctld: %1 %2 Rigctld konnte nicht gestartet werden: %1 %2 rigctld crashed. rigctld ist abgestürzt. rigctld timed out. rigctld hat die Zeitüberschreitung erreicht. Write error with rigctld. Schreibfehler bei rigctld. Read error with rigctld. Lese Fehler bei rigctld. Unknown rigctld error. Unbekannter rigctld-Fehler. Rotator No Rotator Profile selected Kein Rotor-Profil ausgewählt Initialization Error Initialisierungsfehler Internal Error Interner Fehler Cannot open Rotator Kann Rotor nicht öffnen RotatorWidget Form Az: ° Goto Previous Button Profile Vorheriges Tastenprofil Next Button Profile Nächstes Tastenprofil QSO LP QSO Long Path QSO SP QSO Short Path SettingsDialog Settings Einstellungen Station Station Callsign Rufzeichen Rigs Delete Löschen Add Hinzufügen Model Modell SOTA (Optional parameter) SOTA (optional) SOTA SIG IOTA Profile Name Profilname IOTA (Optional parameter) IOTA (optional) SIG Information (Optional parameter) SIG Information (optional) VUCC QTH VUCC Grids (Optional parameter). Ex. EN98,FM08,EM97,FM07 VUCC Gitterfelder (optional). Beisp. EN98,FM08,EM97,FM07 Baudrate Baudrate Default PWR Standard-Ausgangsleistung Enter default PWR (ex. when Rig is disconnected) Standard-Ausgangsleistung eingeben (z.B. wenn der Rig nicht verbunden ist) W 1200 1200 2400 2400 4800 4800 9600 9600 19200 19200 38400 38400 57600 57600 115200 115200 Data Bits Daten Bit Profiles Profile Equipment Geräte Antennas Antennen Profiles Profile Description Beschreibung Use COMxx for Window or path to COM port under Unix-like OS Verwende COMxx für Windows oder den Pfad zum COM-Anschluss unter Unix-ähnlichen Betriebssystemen List of all available CW Shortcuts Profiles Liste der verfügbaren CW Funktionstastenprofile Minimum and maximum TX frequencies. Specific ranges are derived from allowed Band in the Setting. Niedrigste und höchste Sendefrequenzen. Die spezifischen Bereiche ergeben sich aus dem zulässigen Band in der Einstellung. TX Range TX Frequenzbereich - - Enter manually RIT or Transverter Offset Eingabe RIT oder Transverter Offset Enter manually XIT or Transverter offset Eingabe XIT oder Transverter Offset 5 6 6 7 7 8 8 Stop Bits 1 2 2 Flow Control None Hardware Software Parity No Even Odd Space Mark Poll Interval ms Host Name HamLib does not support to change a destination port. HamLib unterstützt keine Änderung eines Zielports. Power Leistung VFO RX Offset (RIT) MHz World Wide Flora & Fauna (Optional parameter) World Wide Flora & Fauna (optional) WWFF Mode Betriebsart Freq Frequenz TX Offset (XIT) PTT State PTT Status Default Speed Standard WPM WPM CW Shortcut Profiles CW Funktionstasten Profile F1 F1 Short Desciption of the Button (up to 7 chars) Kurzbeschreibung der Schaltfläche (bis zu 7 Zeichen) F2 F2 F3 F3 F4 F4 F5 F5 F6 F6 F7 F7 Blank Leer Rig Features Rig Merkmale QSY Wiping POTA SIG (Optional parameter). SIG (optional). SIG Info CW Speed Sync WPM-Sync Rotators Rotoren Query Order Reihenfolge der Abfrage Primary Primär Secondary Sekundär QRZ.com <b>Notice:</b> At least a QRZ XML Subscription is recommended to access detailed information. Without a subscription, you will obtain limited data from QRZ, such as missing grid and other fields. <b>Notiz:</b> Es wird empfohlen, mindestens ein QRZ-XML-Abonnement abzuschließen, um detaillierte Informationen zu erhalten. Ohne ein Abonnement erhalten Sie nur begrenzte Daten von QRZ, z. B. Gitterfeld und andere Felder. QSOs are uploaded immediately QSOs werden sofort hochgeladen Using an internal TQSL instance Verwendung einer internen TQSL Port where QLog listens an incoming traffic from WSJT-X Port, an dem QLog auf eingehenden Datenverkehr von WSJT-X wartet Raw UDP Forward UDP Rohdaten-Weiterleitung Profile name that is used as the alias for the Callsign, Gridsquare, Operator name, and QTH (required parameter) Profilname, der als Alias für Rufzeichen, QTH-Locator, Operator-Name und QTH verwendet wird (erforderlich) CW Keyers CW-Keyer Keyer Profiles Keyer Profile List of all available CW Keyers Liste der verfügbaren CW Keyer Keyer Mode Keyer Modus N/A Assigned CW Keyer Zugeordneter CW-Keyer CW Keyer Speed CW-Keyer WPM Port Type Port Typ Serial Serial User Buttons Profiles Funktionstasten Profile Button 1 Taste 1 Button 2 Taste 2 Button 3 Taste 3 Button 4 Taste 4 ° Operator Name Operator Name ITU ITU CQZ CQZ Operator Callsign Operator Rufzeichen Country Land Station Callsign Logging Station Rufzeichen Callsign of operator (Optional parameter, if different from station callsign) Rufzeichen des Operators (Optionale Angabe, falls es vom Stationsrufzeichen abweicht) County Landkreis Station County Location (Optional parameter) DOK DOK Azimuth Beamwidth Azimuth Offset Valid range value is 0° - 100° (0° Unspecified) Gültiger Wertebereich: 0° - 100° (0° unbestimmt) Unspecified Unbestimmt Swap Paddles Paddles vertauschen Interface Interface Offsets Offset PTT Type PTT Typ PTT Port PTT Port DX Spots to Rig DX-Spots zum Rig Paddle Only Sidetone Sidetone Freq: Sidetone-Frequenz: <DXCALL> = DX Callsign <NAME> = DX Operator Name <RST> = Report 599 <RSTN> = Report 5NN <GREETING> = Greenting GM/GA/GE <QTH> = QTH <MYCALL> = My Callsign <MYNAME> = My Name <MYQTH> = My QTH <MYLOCATOR> = My Gridsquare <MYGRID> = My Gridsquare <MYSIG> = My SIG <MYSIGINFO> = My SIG Information <MYIOTA> = My IOTA <MYSOTA> = MY SOTA <MYWWFT> = My WWFT <MYVUCC> = MY VUCC <MYPWR> = My Power in W <EXCHSTR> = Contest Exchange Message <EXCHNR> = Contest Exchange Serial Number <EXCHNRN> = Contest Exchange Serial Number (9→N, 0→T) <+> = Speed +5 WPM (<++> = +10 WPM, etc.) <-> = Speed -5 WPM (<--> = -10 WPM, etc.) <DXCALL> = DX Rufzeichen <NAME> = DX Operator Name <RST> = Report 599 <RSTN> = Report 5NN <GREETING> = Grussformel GM/GA/GE <QTH> = QTH <MYCALL> = Eigenes Rufzeichen <MYNAME> = Eigener Name <MYQTH> = Eigenes QTH <MYLOCATOR> = Eigenes Gitterfeld <MYSIG> = Eigenes SIG <MYSIGINFO> = Eigene SIG Information <MYIOTA> = Eigenes IOTA <MYSOTA> = Eigenes SOTA <MYWWFT> = Eigenes WWFT <MYVUCC> = Eigenes VUCC <MYPWR> = Eigene Leistung in W <EXCHSTR> = Contest Exchange <EXCHNR> = Contest Exchange Serial Number <EXCHNRN> = Contest Exchange Serial Number (9→N, 0→T) <+> = Geschwindigkeit +5 WPM (<++> = +10 WPM, usw.) <-> = Geschwindigkeit -5 WPM (<--> = -10 WPM, usw.) RX: TX: Split Start rigctld daemon to share rig with other applications (e.g. WSJT-X) Rigctld-Daemon starten, um das Rig mit anderen Anwendungen zu teilen (z. B. WSJT-X) Share Rig via port Rig über Port freigeben Advanced... Erweitert… Rig Port Rig-Port CIV Addr CIV Addr Auto Automatisch RTS RTS DTR DTR Web Lookup Button Schaltfläche Web Suche URL Specify the URL to use for quick search. The <DXCALL> macro will be replaced by the current callsign Geben Sie die URL an, die für die Schnellsuche verwendet werden soll. Das Makro <DXCALL> wird durch das aktuelle Rufzeichen ersetzt Test URL with your Callsign Prüfen Sie die URL mit Ihrem Rufzeichen Test Clubs Active Lists Aktive Listen Immediately Upload Sofort-Upload HRDLog HRDLog It is not a password. It is the upload code received via email after the registration to HRDLOG..net Das ist kein Passwort. Es handelt sich um den Upload-Code, den sie nach der Registrierung bei HRDLOG.net per E-Mail erhalten haben Upload Code Do not translate. Upload Code If it is enabled and Rig is connected then QLog periodically sends On-Air messages to HRDLog Wenn es aktiviert ist und Rig angeschlossen ist, sendet QLog regelmäßig On-Air-Nachrichten an HRDLog Send On-Air On-Air senden Leave empty for auto-detection Leer lassen für automatische Erkennung Auto-detect TQSL path TQSL-Pfad automatisch erkennen Auto-Detect Automatische Erkennung TQSL Version TQSL-Version Default API Key Standard-API-Schlüssel Callsign-specific API Keys API-Schlüssel, die auf das Rufzeichen zugeschnitten sind Wavelog Wavelog API Key API-Schlüssel Danger Zone Gefahrenzone <b>⚠ This is a danger zone. Proceed with caution, as actions performed here cannot be undone and may have a significant impact on your log.</b> <b>⚠ Dies ist eine Gefahrenzone. Gehen Sie vorsichtig vor, da die ausgeführten Aktionen nicht rückgängig gemacht werden können und erhebliche Auswirkungen auf Ihr Log haben können.</b> Delete All QSOs Alle QSOs löschen Delete All Passwords from the Secure Store Alle Passwörter aus dem sicheren Speicher löschen Endpoint Endpunkt Others Andere Status Confirmed By Bestätigt durch Paper Papier Chat <b>Security Notice:</b> QLog stores all passwords in the Secure Storage. Unfortunately, ON4KST uses a protocol where this password is sent over an unsecured channel as plaintext.</p><p>Please exercise caution when choosing your password for this service, as your password is sent over an unsecured channel in plaintext form.</p> <b>Sicherheitshinweis:</b> QLog speichert alle Passwörter im Secure Storage. Leider verwendet ON4KST ein Protokoll, bei dem dieses Passwort über einen ungesicherten Kanal im Klartext gesendet wird.</p><p>Bitte seien Sie vorsichtig, wenn Sie Ihr Passwort für diesen Dienst wählen, da Ihr Passwort über einen ungesicherten Kanal im Klartext gesendet wird.</p> The '>' character is interpreted as a marker for the initial cursor position in the Report column. <br/>Ex.: '5>9' means the cursor will be positioned on the second character Das Zeichen „>“ wird als Markierung für die anfängliche Cursorposition in der Spalte „Report“ interpretiert.<br/>Beispiel: „5>9“ bedeutet, dass sich der Cursor auf dem zweiten Zeichen befindet <p>List of IP addresses to which QLog forwards raw UDP WSJT-X packets.</p>The IP addresses are separated by a space and have the form IP:PORT <p>Liste der IP-Adressen, an die QLog unbearbeitete UDP WSJT-X-Pakete weiterleitet.</p>Die IP-Adressen werden durch ein Leerzeichen getrennt und haben die Form IP:PORT Join Multicast Multicast verbinden Enable/Disable Multicast option for WSJTX Aktivieren/Deaktivieren der Multicast-Option für WSJTX Multicast Address Multicast Adresse Specify Multicast Address. <br>On some Linux systems it may be necessary to enable multicast on the loop-back network interface. Geben Sie die Multicast-Adresse an. <br>Auf einigen Linux-Systemen kann es erforderlich sein, Multicast auf der Loopback-Netzwerkschnittstelle zu aktivieren. TTL Time-To-Live determines the range<br> over which a multicast packet is propagated in your intranet. Time-To-Live bestimmt den Bereich<br>, über den ein Multicast-Paket in Ihrem Intranet verbreitet wird. Color CQ Spots CQ-Spots einfärben Enable/Disable sending color-coded status indicators back to WSJT-X for each callsign calling CQ Senden farbcodierter Statusindikatoren für jede CQ-rufende Station zurück an WSJT-X aktivieren/deaktivieren Notifications Benachrichtigungen DX Spots <p> List of IP addresses to which QLog sends UDP notification packets with DX Cluster Spots.</p>The IP addresses are separated by a space and have the form IP:PORT <p>Liste der IP-Adressen, an die QLog UDP-Notification-Pakete mit DX Cluster Spots sendet.</p>Die IP-Adressen sind durch ein Leerzeichen getrennt und haben das Format IP:PORT QSO Changes QSO Änderungen <p> List of IP addresses to which QLog sends UDP notification packets about a new/updated/deleted QSO in the log.</p>The IP addresses are separated by a space and have the form IP:PORT <p>Liste der IP-Adressen, an die QLog UDP-Benachrichtigungspakete über ein neues/aktualisiertes/gelöschtes QSO im Log sendet.</p>Die IP-Adressen sind durch ein Leerzeichen getrennt und haben das Format IP:PORT Wsjtx CQ Spots <p> List of IP addresses to which QLog sends UDP notification packets with WSJTX CQ Spots.</p>The IP addresses are separated by a space and have the form IP:PORT <p>Liste der IP-Adressen, an die QLog UDP-Benachrichtigungspakete mit WSJTX CQ Spots sendet.</p>Die IP-Adressen sind durch ein Leerzeichen getrennt und haben das Format IP:PORT Rig Status Rig-Status <p> List of IP addresses to which QLog sends UDP notification packets when Rig State changes.</p>The IP addresses are separated by a space and have the form IP:PORT <p>Liste der IP-Adressen, an die QLog UDP-Benachrichtigungspakete mit Rig-Status sendet.</p>Die IP-Adressen sind durch ein Leerzeichen getrennt und haben das Format IP:PORT GUI GUI Time Format Zeitformat 24-hour 24-Stunden AM/PM AM/PM Unit System Einheitensystem Metric Metrisch Imperial Imperial Date Format Datumsformat System System Custom Benutzerdefiniert <a href="https://doc.qt.io/qt-6/qdate.html#fromString-1">Time Format Documentation</a> <a href="https://doc.qt.io/qt-6/qdate.html#fromString-1">Dokumentation des Zeitformats</a> Spot Alerts <p> List of IP addresses to which QLog sends UDP notification packets about user Spot Alerts.</p>The IP addresses are separated by a space and have the form IP:PORT <p>Liste der IP-Adressen, an die QLog UDP-Benachrichtigungspakete über User Spot Alerts sendet.</p>Die IP-Adressen sind durch ein Leerzeichen getrennt und haben das Format IP:PORT LogID <p>Assigned LogID to the current log.</p>The LogID is sent in the Network Nofitication messages as a unique instance identified.<p> The ID is generated automatically and cannot be changed</> <p>Zugeordnete LogID für das aktuelle Protokoll.</p>Die LogID wird in den Netzwerk-Notifizierungsmeldungen als eindeutige Instanzkennung gesendet.<p>Die ID wird automatisch generiert und kann nicht geändert werden</> ex. 192.168.1.1:1234 192.168.2.1:1234 eQSL Browse Durchsuchen LoTW LoTW TQSL Path TQSL Pfad Network Netzwerk Wsjtx Wsjtx Port ClubLog E-Mail E-Mail Sync && QSL Bands Bänder Modes Betriebsarten DXCC Port Operator name (Optional parameter) Operator-Name (optional) Station Gridsquare (Mandatory parameter) Stations-Gitterfeld (erforderlich) Callsign (Mandatory parameter) Rufzeichen (erforderlich) Gridsquare Gitterfeld List of all available Station Profiles Liste der verfügbaren Stations-Profile QTH Name (Optional parameter) QTH Name (optional) List of all available Rigs Liste der verfügbaren Rigs List of all available Antennas Liste der verfügbaren Antennen Callbook HamQTH Username Benutzername Password Passwort Name Name Report Bericht State Staat Start (MHz) Beginn (MHz) End (MHz) Ende (MHz) SAT Mode SAT Betriebsart Disabled Inaktiv Dummy Morse Over CAT CW-über-CAT CWDaemon FLDigi Single Paddle IAMBIC A IAMBIC B Ultimate High High Low Low Press <b>Modify</b> to confirm the profile changes or <b>Cancel</b>. Drücken Sie <b>Ändern</b>, um die Profiländerungen zu bestätigen oder <b>Abbrechen</b>. Must not be empty Darf nicht leer sein Auto Detect Automatische Erkennung TQSL was not found on this system. Please install TQSL or specify the path manually. TQSL wurde auf diesem System nicht gefunden. Bitte installieren Sie TQSL oder geben Sie den Pfad manuell an. Not found Nicht gefunden Rig sharing is only available for Hamlib driver Rig-Freigabe ist nur für den Hamlib-Treiber verfügbar Rig sharing is not available for network connection Rig-Freigabe ist für Netzwerkverbindungen nicht verfügbar Delete Passwords Passwörter löschen All passwords have been deleted Alle Passwörter wurden gelöscht Deleting all QSOs... Alle QSOs werden gelöscht... Error Fehler Failed to delete all QSOs. Alle QSOs konnten nicht gelöscht werden. members Mitglieder Required internet connection during application start Erfordert Internetverbindung beim Start der Anwendung Modify Ändern Special - Omnirig Special - Omnirig Cannot be changed Kann nicht geändert werden WinKey WinKey Select File Datei auswählen ShortcutEditorModel Description Beschreibung Shortcut Tastenkombination Conflict with a built-in shortcut Konflikt mit einer eingebauten Tastenkombination Conflict with a user-defined shortcut Konflikt mit einer benutzerdefinierten Tastenkombination ShowUploadDialog QSOs to Upload QSOs zum Hochladen Selected QSO Ausgewählte QSOs Upload Hochladen SmartSearchBox Search Suchen StatisticsWidget Statistics Statistik My Gridsquare Eigenes Gitterfeld Show on Map Auf Karte ansehen User Filter Benutzer-Filter Confirmed by Bestätigt durch My Callsign Eigenes Rufzeichen Date Range Datumsbereich LoTW LoTW eQSL eQSL Paper Papier Graph Type Diagramm-Typ QSOs per QSOs pro Percents Prozent Top 10 Histogram Histogramm My Antenna Eigene Antenne My Rig Eigener Rig to bis Band Band Confirmed Not Confirmed Year Jahr Month Monat Day in Week Wochentag Hour Stunde Mode Betriebsart Continent Kontinent Propagation Mode Ausbreitungsmodus Confirmed / Not Confirmed Bestätigt / nicht Bestätigt Countries Länder Big Gridsquares Grossfelder Distance Entfernung QSOs Confirmed/Worked Grids Bestätigte / gearbeitete Gitterfelder ODX ODX Sun Son Mon Mon Tue Die Wed Mit Thu Don Fri Fre Sat Sa Not specified Nicht angegeben No User Filter Kein Benutzerfilter Over 50000 QSOs. Display them? Über 50000 QSOs. Anzeigen? Rendering QSOs... Rendern der QSOs… All Alle TCIRigDrv Rig 0 Rig 0 Rig 1 Rig 1 Rig 2 Rig 2 Rig 3 Rig 3 Error Occurred Fehler aufgetreten Rig status changed Rig-Status geändert Rig is not connected Rig nicht verbunden TimestampFormatDelegate Blank Leer ToAllTableModel Time Zeit Spotter Message Nachricht UploadQSODialog Upload QSOs QSOs hochladen eQSL eQSL LoTW LoTW QRZ.com QRZ.com Clublog Clublog HRDLog HRDLog Wavelog Wavelog Filters Filter Station Profile Stationsprofil My Callsign Eigenes Rufzeichen Gridsquare Gitterfeld Include QSOs Status QSOs mit Sendestatus Under normal circumstances this status means <b>"do not send"</b>.<br/>However, it may sometimes be wanted to ignore this setting when sending a QSL. Unter normalen Umständen bedeutet dieser Status <b>"nicht senden"</b>.<br/>Manchmal kann es jedoch sinnvoll sein, diese Einstellung beim Senden einer QSL zu ignorieren. No Nein Under normal circumstances this status means <b>"Ignore/Invalid"</b>.<br/>However, it may sometimes be wanted to ignore this setting when sending a QSL. Unter normalen Umständen bedeutet dieser Status <b>"ignorieren/fehlerhaft"</b>.<br/>Manchmal kann es jedoch sinnvoll sein, diese Einstellung beim Senden einer QSL zu ignorieren. Ignore Ignorieren None Keine QSLs Message QSL-Nachricht Comment Kommentar QSL Message Field QSL-Nachrichtenfeld QTH Profile QTH Profil This option deletes all QSOs in the Clublog<br>and based on filter, it uploads all QSOs regardless of their status. Diese Option löscht alle QSOs im Clublog<br>und lädt basierend auf dem Filter alle QSOs hoch, unabhängig von ihrem Status. Clear Clublog and reupload QSOs Clublog löschen und QSOs erneut hochladen LoTW / TQSL LoTW / TQSL TQSL Station Location TQSL-Stationstandort Station Profile ID Stationsprofil Reupload All Alles erneut hochladen Show QSOs... QSOs anzeigen... &Upload &Hochladen Unspecified Unbestimmt Hide QSOs... QSOs ausblenden... Uploading to %1 Lade hoch zu %1 Cancel Abbrechen QLog Warning - %1 QLog Warnung - %1 Cannot update QSO Status QSO-Status kann nicht aktualisiert werden Cannot upload the QSO(s): Kann QSO(s) nicht hochladen: QLog Information QLog Information No QSO found to upload. Keine QSO zum Hochladen gefunden. QSO(s) were uploaded to the selected services QSOs wurden zu den ausgewählten Diensten hochgeladen Time Zeit Callsign Rufzeichen Mode Betriebsart Upload to Hochladen zu The values below will be used when an input record does not contain the ADIF values Die folgenden Werte werden verwendet, wenn ein importierter Datensatz die ADIF-Werte nicht enthält Any Irgendein Location callsign (%1) and grid (%2) do not match selected filters Standortrufzeichen (%1) und Grid (%2) stimmen nicht mit den ausgewählten Filtern überein Location callsign (%1) does not match selected callsign (%2) Standortrufzeichen (%1) stimmt nicht mit dem ausgewählten Rufzeichen (%2) überein Location grid (%1) does not match selected grid (%2) Standort-Grid (%1) stimmt nicht mit dem ausgewählten Grid (%2) überein Unknown Unbekannt Service is not configured properly.<p> Please, use <b>Settings</b> dialog to configure it.</p> Service ist nicht richtig eingerichtet.<p> Bitte verwende den <b>Einstellungen</b> Dialog, um es einzurichten.</p> UserListModel Callsign Rufzeichen Gridsquare Gitterfeld Distance Entfernung Azimuth Comment Kommentar WCYTableModel Time Zeit K expK A R SFI SA GMF Au WWVTableModel Time Zeit SFI A K Info WsjtxFilterDialog WSJTX Filters WSJTX-Filter General Filters Allgemeine Filter Log Status Log Status New Band Neues Band New Mode Neuer Mode New Entity Neuer Eintrag New Slot Neuer Slot Worked Gearbeitet Confirmed Bestätigt Continent Kontinent North America Nordamerika Europe Europa South America Südamerika Africa Afrika Antarctica Antarktis Asia Asien Oceania Ozeanien Distance more than Entfernung größer als SNR better than SNR besser als dB dB Extended Filters Erweiterte Filter Member Mitglied No Club List is enabled Keine Clubliste aktiviert WsjtxTableModel SNR SNR Callsign Rufzeichen Gridsquare Gitterfeld Distance Entfernung Last Activity Letzte Aktivität Last Message Letzte Nachricht Member Mitglied WsjtxWidget Form Filtered Gefiltert Column Visibility... Spaltensichtbarkeit... Filter... Filter... Which columns should be displayed Welche Spalten sollen angezeigt werden Filter Spots Filter Spots main Run with the specific namespace. Im spezifischen Namensraum ausführen. namespace Namensraum Translation file - absolute or relative path and QM file name. Übersetzungsdatei - Absoluter oder relativer Pfad und Name der QM-Datei. path/QM-filename path/QM-filename Set language. <code> example: 'en' or 'en_US'. Ignore environment setting. Sprache einstellen. Beispiel <code>: 'en' oder 'en_US'. Ignoriert Betriebssystemeinstellungen. code code Writes debug messages to the debug file Schreibt Debugmeldungen in die Debugdatei Process pending database import (internal use) Ausstehenden Datenbankimport verarbeiten (interne Verwendung) Force update of all value lists (DXCC, SATs, etc.) Aktualisierung aller Wertelisten erzwingen (DXCC, SATs usw.) ================================================ FILE: i18n/qlog_es.ts ================================================ ActivityEditor Activity Editor Editor de actividades Activity Name Nombre de la actividad Layout Diseño Window Arrangement: Disposición de las ventanas: Saved Guardado Clear Limpiar Available Fields Campos disponibles List of fields that can be used Lista de campos que pueden utilizarse Row A Fila A Move selected fields from the Available Fields List to the Row A Mover los campos seleccionados de la lista de campos disponibles a la fila A Remove selected field from the Row A Eliminar el campo seleccionado de la fila A List of fields in the first variable row Lista de campos de la primera fila de variables Change the order. Move selected field up Cambiar el orden. Mover el campo seleccionado hacia arriba Change the order. Move selected field down Cambiar el orden. Desplazar hacia abajo el campo seleccionado Row B Fila B Move selected fields from the Available Fields List to the Row B Mover los campos seleccionados de la lista de campos disponibles a la fila B Remove selected field from the Row B Eliminar el campo seleccionado de la fila B List of fields in the second variable row Lista de campos de la segunda fila de variables Column A Columna A List of fields in the first QSO Detail Column Lista de campos en la primera columna de detalles del QSO Column B Columna B List of fields in the second QSO Detail Column Lista de campos de la segunda columna de detalles del QSO Column C Columna C List of fields in the third QSO Detail Column Lista de campos de la tercera columna de detalles del QSO New QSO Rows Nuevas filas del QSO New QSO Detail Columns Nuevas columnas de detalles del QSO Values Valores Profiles Perfiles If unchecked, the profile does not change Si no está marcada, el perfil no cambia Station Estación Antenna Antena Rig Equipo Connect automatically Conectar automáticamente Connect Conectar Rotator Rotor Fields Campos Must not be empty No debe estar vacío Unsaved Sin guardar AlertRuleDetail Alert Rule Detail Detalle de la Regla de Alerta Rule Name Nombre de la Regla Enabled Habilitar Sources Fuentes DX Cluster Cluster DX WSJTX WSJTX DX DX Worked Trabajado New Slot Nuevo Slot Confirmed Confirmado New Entity Nueva Entidad New Mode Nuevo Modo New Band Nueva Banda DX Callsign Indicativo DX Use Perl-like regular expression Utilice expresiones regulares similares a Perl .* .* Country País Log Status Estado del Registro Spot Comment Comentario Anunciado ITU ITU CQZ CQZ IOTA IOTA SOTA SOTA POTA POTA WWFF WWFF Special Programs Especial Modes Modos FTx (FT8/FT4) FTx (FT8/FT4) Phone Fonía CW CW Digital Digital Bands Bandas Continent Continente North America Norteamérica Africa África Antarctica Antártida South America Sudamérica Asia Asia Europe Europa Oceania Oceanía Member Miembro Spotter Anunciante All Todos Must not be empty No debe estar vacío No Club List is enabled No hay ninguna lista de clubes habilitada AlertSettingDialog Alerts Rules Reglas de Alerta Rules Reglas Add Agregar Edit Editar Remove Quitar Name Nombre State Estado AlertTableModel Rule Name Nombre Regla Callsign Indicativo Frequency Frecuencia Mode Modo Updated Actualizado Last Update Última Actualización Last Comment Último Comentario Member Miembro AlertWidget Alerts Alertas Clear older than Borrar anteriores a Never Nunca min(s) min(s) Edit Rules Editar Reglas Editar Reglas Column Visibility... Visibilidad de Columnas Visibilidad de Columnas... Clear Limpiar Limpiar AwardsDialog Awards Diplomas Diplomas Options Opciones Opciones Award Diploma Diploma My DXCC Entity Mi entidad DXCC Mi Entidad DXCC User Filter Filtro de usuario Confirmed by Confirmado por Confirmado por LoTW LoTW eQSL eQSL Paper Papel Papel Mode Modo Modo CW CW Phone Fonía Digi Digi Not-Worked Only Sólo no trabajado Sólo no trabajado Not-Confirmed Only No confirmado Show Mostrar Mostrar DXCC DXCC ITU ITU WAC WAC WAZ WAZ WAS WAS WPX WPX IOTA IOTA POTA Hunter Cazador de POTA Cazador de POTA POTA Activator Activador de POTA Activador de POTA SOTA Cumbres en el Aire SOTA WWFF Flora y Fauna Mundial WWFF Gridsquare 2-Chars Gridsquare 4-Chars Gridsquare 6-Chars Gridsquare %1-Chars US Counties Condados de EE. UU Russian Districts Distritos de Rusia Japanese Cities/Ku/Guns Ciudades japonesas / Ku / Gun NZ Counties Condados de Nueva Zelanda Spanish DMEs DMEs de España Ukrainian Districts Distritos de Ucrania No User Filter Sin filtro de usuario DELETED Eliminado North America América del Norte América del Norte South America América del Sur América del Sur Europe Europa Europa Africa África África Oceania Oceanía Oceanía Asia Asia Antarctica Antártida Antártida AwardsTableModel Slots: Slot: Confirmed Confirmado Worked Trabajado Still Waiting Esperando BandmapWidget Form Mapa de Banda Create an additional Bandmap Window Crear una ventana adicional de Bandmap Clear older Borrar anteriores a Clear All Borrar Todo Clear the current band Borrar la banda actual Never Nunca min(s) min(s) Bandmap Mapa de Banda Show Band Mostrar Banda Center RX Centrar RX Show Emergency Frequencies Mostrar frecuencias de emergencia SOS CWCatKey No Rig is connected Ninguna Radio está conectada Rig does not support Morse over CAT La Radio no soprte Morse sobre CAT Cannot send Text to Rig No se puede enviar el Texto a la Radio Keyer is not connected El manipulador no está conectado Rig is not connected Radio no conectada Cannot set Keyer Speed No se puede establecer la velocidad del manipulador Cannot stop Text Sending No se puede detener el envío del Texto CWConsoleWidget Form CW Keyer Profile Perfil del Manipulador CW Shortcuts profile Perfil de atajos Speed Velocidad N/A S/D WPM PPM Sent text Enviar Texto Echoed text Texto repetido &CW &CW Text to send Texto a Enviar Switch between sending <b>words</b> individually (separated by spaces)<br> and sending the entire text as a <b>whole</b> (separated by a new line). Cambiar entre enviar <b>palabras</b> individualmente (separadas por espacios)<br> y enviar el texto completo como un <b>todo</b> (separado por una nueva línea). F1 F2 F3 F4 F5 F6 F7 Halt Detener CW Console - Halt Sending Consola CW - Detener Clear Sent and Echo Console Borrar enviados y consola Eco Immediately stop CW sending Detener inmediatamente la transmisión CW Clear Borrar Rig must be connected La radio debe estar conectada Word Palabra Whole Todo CWDaemonKey Keyer is not connected El manipulador no está conectado Cannot send Text No se puede enviar el Texto Cannot set Keyer Speed No se puede establecer la velocidad del manipulador Cannot stop Text Sending No se puede detener el envío del Texto CWFldigiKey Connected device is not FLDigi El dispositivo conectado no es FLDigi Keyer is not connected El manipulador no está conectado Cannot send Text to FLDigi No se puede enviar el Texto a FLDigi Cannot send the Clear command to FLDigi No se puede enviar Borrar a FLDigi Cannot send the TX command to FLDigi No se puede enviar TX a FLDigi Cannot send the Text command to FLDigi No se puede enviar Texto a FLDigi Cannot send the Stop command to FLDigi No se puede enviar Parar a FLDigi Cannot send the Abort command to FLDigi No se puede enviar Abortar a FLDigi Cannot receive data from FLDigi No se reciben datos de FLDigi FLDigi connection timeout Terminó el tiempo de espera de conexión FLDigi FLDigi connection error Error de conexión FLDigi FLDigi command error Error de comando FLDigi CWKeyer No CW Keyer Profile selected No Hay Perfil de Manipulador Seleccionado Initialization Error Error al Inicializar Internal Error Error Interno Connection Error Error de Conexión Cannot open the Keyer connection No se puede conectar al Manipulador CWWinKey Connected device is not WinKey El dispositivo conectado no es Winkey Cannot send Text to Rig No se puede enviar el Texto a la Radio Keyer is not connected El manipulador no está conectado Communication Error Error de Comunicación Cannot set Keyer Speed No se puede establecer la velocidad del manipulador Cannot stop Text Sending No se puede detener el envío del Texto 4000 Hz 4000 Hz 2000 Hz 4000 Hz {2000 ?} 1333 Hz 4000 Hz {1333 ?} 1000 Hz 4000 Hz {1000 ?} 800 Hz 4000 Hz {800 ?} 666 Hz 4000 Hz {666 ?} 571 Hz 4000 Hz {571 ?} 500 Hz 4000 Hz {500 ?} 444 Hz 4000 Hz {444 ?} 400 Hz 4000 Hz {400 ?} CabrilloExportDialog Cabrillo Export Exportar Cabrillo File: Archivo: Browse Navegar Contest Concurso Format Template: Plantilla de formato: Manage Administrar User Filter: Filtro de usuario: Date/Time: Fecha/Hora: yyyy-MM-dd HH:mm - - UTC Station & Categories Estación y categorías Callsign: Indicativo: Operators: Operadores: Band: Banda: Mode: Modo: Power: Potencia: Operator: Operador: Assisted: Asistido: Station: Estación: Transmitter: Transmisor: Time: Hora: Overlay: Transmitter ID: Additional Adicional Name: Nombre: Email: EMail: Address: Dirección: Max 6 lines Máx. 6 líneas Gridsquare Locator Location: Ubicación: Club: Club: Soapbox: Comentario libre: Off-Time: yyyy-mm-dd hhmm yyyy-mm-dd hhmm &Export &Exportar Cabrillo Files (*.log);;CBR Files (*.cbr);;All Files (*) Archivos Cabrillo (*.log);;Archivos CBR (*.cbr);;Todos los archivos (*) QSO(s): %1 QSO(s): %1 QSOs: ? QSOs: ? QLog Warning Alerta de QLog Please select a contest template. Seleccione una plantilla de concurso. Please select an output file. Seleccione un archivo de salida. No callsign available. Check your filters. No hay indicativos disponibles. Compruebe sus filtros. QLog Error Error de QLog Failed to prepare export query. No se pudo preparar la consulta de exportación. Failed to query QSOs for export. No se pudieron consultar los QSOs para la exportación. Cannot open file %1 for writing. No se puede abrir el archivo %1 para escritura. Exporting Cabrillo... Exportando Cabrillo… QLog Information Información de QLog Exported %n QSO(s) to Cabrillo file. Exportados %n QSOs al archivo Cabrillo. CabrilloFormat All Bands Todas las bandas 2m (144 MHz) 2m (144 MHz) 1.25m (222 MHz) 1.25m (222 MHz) 70cm (432 MHz) 70cm (432 MHz) 33cm (902 MHz) 33cm (902 MHz) 23cm (1.2 GHz) 23cm (1.2 GHz) Light Luz VHF 3-Band VHF de 3 bandas VHF FM Only Solo VHF FM Digital Digital Mixed Mixto High High Low Low QRP Single Operator Operador único Multi Operator Multioperador Check Log Revisar log Non-Assisted No asistido Assisted Asistido Fixed Fijo Mobile Móvil Portable Rover Rover Limited Rover Unlimited Expedition Expedición HQ School Escuela Distributed Distribuido One Uno Two Dos Limited Limitado Unlimited Ilimitado SWL SWL 6 Hours 6 horas 12 Hours 12 horas 24 Hours 24 horas Classic Clásico Rookie TB Wires Novice/Tech Novato/Técnico Over 50 Más de 50 Text (left-aligned) Texto (alineado a la izquierda) Frequency (kHz) Frecuencia (kHz) Time (HHMM) Hora (HHMM) Date (YYYY-MM-DD) Fecha (AAAA-MM-DD) RST Short (drop last digit) RST corto (omitir el último dígito) Uppercase Mayúsculas Mode (Cabrillo) Modo (Cabrillo) Zero-Padded Nr. Número con ceros Transmitter ID CabrilloTemplateDialog Cabrillo Template Manager Gestor de plantillas Cabrillo Import template Importar plantilla Import Importar Export template Exportar plantilla Export Exportar New template Nueva plantilla New Nuevo Copy existing template Copiar plantilla existente Copy Copiar Delete template Eliminar plantilla Delete Template Name: Nombre de la plantilla: Contest Name: Nombre del concurso: Default Mode: Modo predeterminado: QSO Line Columns: Columnas de línea QSO: Contest name as required by the rules. It is possible to enter a custom string if it is not included in the list. Nombre del concurso según las reglas. Es posible introducir una cadena personalizada si no está incluida en la lista. Seq. N.º QSO Field Campo QSO Formatter Formateador Width Ancho Label Etiqueta Add line Añadir línea Add Remove selected line Eliminar línea seleccionada Remove New Template Nueva plantilla Copy - %1 Copia – %1 Delete Template Eliminar plantilla Delete template '%1'? ¿Eliminar la plantilla «%1»? Import Template Importar plantilla QLog Cabrillo Template (*.qct) Plantilla Cabrillo de QLog (*.qct) Import Failed Importación fallida Export Template Exportar plantilla Export Failed Exportación fallida Failed to write file: %1 No se pudo escribir el archivo: %1 File not found: %1 Archivo no encontrado: %1 Cannot open file: %1 No se puede abrir el archivo: %1 Invalid template file: missing name Archivo de plantilla inválido: falta el nombre QLog Error Error de QLog Cannot start database transaction. No se puede iniciar la transacción de base de datos. QLog Warning Alerta de QLog Cannot save template '%1': %2 No se puede guardar la plantilla «%1»: %2 CallbookManager <p>The secondary callbook will be used</p> <p>Se usará el libro de guaria secundario</p> ChatWidget ON4KST Chat Chat ON4KST Chat Room Sala de Chat Connect Conectar New Nuevo QLog Warning Alerta de QLog ON4KST Chat is not configured properly.<p> Please, use <b>Settings</b> dialog to configure it.</p> Chat ON4KST no está configurado apropiadamente.<p> Por favor usar <b>Ajustes</b> para configurarlo.</p> CheckBoxDelegate Enabled Activado Disabled Desactivado ClockWidget Form Reloj Sunrise Amanecer Sunset Ocaso N/A S/D CloudlogUploader Invalid API Key Clave API no válida ClubLogUploader Clublog Operation for Callsign %1 failed.<br>%2 La Operación de Clublog para el indicativo %1 ha fallado.<br>%2 ColumnSettingDialog Column Visibility Setting Ajustes de Visibilidad de Columnas General General My Info Mi Información QSL && Callbooks QSL y Libros de Guardia Members Miembros Conditionals Condiciones Contests Concursos Others Otros Done Hecho ColumnSettingGenericDialog Unselect All Seleccionar Nada Select All Seleccionar Todo ColumnSettingSimpleDialog Column Visibility Setting Ajustes de Visibilidad de Columnas Done Hecho DBSchemaMigration DXCC Entities Entidades DXCC Sats Info Informació de Satélites SOTA Summits Cumbres SOTA WWFF Records Registros WWFF IOTA Records Registros IOTA POTA Records Registros POTA Membership Directory Records Registros del Directorio de Miembros Clublog CTY.XML Clublog CTY.XML List of Values Lista de Valores Updating Actualizando Update Failed Actualización Fallida DBStrings PHONE Fonía CW CW DIGITAL Digital Afghanistan Afganistán Agalega & St. Brandon Is. Agalega y San Brandon Aland Islands Is. Aland Alaska Alaska Albania Albania Algeria Argelia American Samoa Samoa Americana Amsterdam & St. Paul Is. Is. San Pablo y Ámsterdam Andaman & Nicobar Is. Is. Andamán y Nicobar Andorra Andorra Angola Angola Anguilla Anguilla Annobon Island Is. Annobón Antarctica Antártida Antigua & Barbuda Antigua y Barbuda Argentina Argentina Armenia Armenia Aruba Aruba Ascension Island Is. Ascensión Asiatic Russia Rusia Asiática Asiatic Turkey Turquía Asiática Austral Islands Is. Australes Australia Australia Austria Austria Aves Island Is. de Aves Azerbaijan Azerbaiyán Azores Azores Bahamas Bahamas Bahrain Baréin Baker & Howland Islands Is. Baker y Howland Balearic Islands Is. Baleares Banaba Island Is. Banana Bangladesh Bangladesh Barbados Barbados Belarus Bielorrusia Belgium Bélgica Belize Belice Benin Benín Bermuda Bermudas Bhutan Bután Bolivia Bolivia Bonaire Bonaire Bosnia-Herzegovina Bosnia y Herzegovina Botswana Botsuana Bouvet Bouvet Brazil Brasil British Virgin Islands Is. Vírgenes Británicas Brunei Darussalam Brunéi Bulgaria Bulgaria Burkina Faso Burkina Faso Burundi Burundi Cambodia Camboya Cameroon Camerún Canada Canadá Canary Islands Is. Canarias Cape Verde Cabo Verde Cayman Islands Is. Caimán Central African Republic República Centroafricana Central Kiribati Kiribati Central Ceuta & Melilla Ceuta y Melilla Chad Chad Chagos Islands Is. Chagos Chatham Islands Is. Chatham Chesterfield Islands Is. Chesterfield Chile Chile China China Christmas Island Is. de Navidad Clipperton Island Is. Clipperton Cocos (Keeling) Islands Is. Cocos (Keeling) Cocos Island Is. del Coco Colombia Colombia Comoros Islas Comoras Conway Reef Conway Reef Corsica Córcega Costa Rica Costa Rica Cote d'Ivoire Costa de Marfíl Crete Creta Croatia Croacia Crozet Island Is. Crozet Cuba Cuba Curacao Curazao Cyprus Chipre Czech Republic Rep. Checa (Chequia) DPR of Korea Corea del Norte Dem. Rep. of the Congo República Democrática del Congo Denmark Dinamarca Desecheo Island Is. Desecheo Djibouti Yibuti Dodecanese Dodecaneso Dominica Dominica Dominican Republic Rep. Dominicana Ducie Island Is. Ducie East Malaysia Malasia Oriental Easter Island Is. de Pascua Eastern Kiribati Kiribati Oriental Ecuador Ecuador Egypt Egipto El Salvador El Salvador England Inglaterra Equatorial Guinea Guinea Ecuatorial Eritrea Eritrea Estonia Estonia Ethiopia Etiopía European Russia Rusia Europea Falkland Islands Is. Falkland (Malvinas) Faroe Islands Is. Feroe Fed. Rep. of Germany Alemania Fernando de Noronha Fernando de Noronha Fiji Fiyi Finland Finlandia France Francia Franz Josef Land Tierra Franciso José French Guiana Guayana Francesa French Polynesia Polinesia Francesa Gabon Gabón Galapagos Islands Is. Galápagos Georgia Georgia Ghana Ghana Gibraltar Gibraltar Glorioso Islands Is. Glorioso Greece Grecia Greenland Groenlandia Grenada Granada Guadeloupe Guadalupe Guam Guam Guantanamo Bay Bahía de Guantánamo Guatemala Guatemala Guernsey Guernsey Guinea Guinea Guinea-Bissau Guinea-Bisáu Guyana Guyana Haiti Haití Hawaii Hawái Heard Island Is. Heard Honduras Honduras Hong Kong Hong Kong Hungary Hungría ITU HQ ITU Ginebra Iceland Islandia India India Indonesia Indonesia Iran Irán Iraq Iraq Ireland Irlanda Isle of Man Isla de Man Israel Israel Italy Italia Jamaica Jamaica Jan Mayen Jan Mayen Japan Japón Jersey Jersey Johnston Island Is. Johnston Jordan Jordania Juan Fernandez Islands Is. Juan Fernández Juan de Nova & Europa Juan de Nova, Europa Kaliningrad Kaliningrado Kazakhstan Kazajistán Kenya Kenia Kerguelen Islands Is. Kerguelen Kermadec Islands Is. Kermadec Kingdom of Eswatini Reino de Esuatini Kure Island Is. Kure Kuwait Kuwait Kyrgyzstan Kirguistán Lakshadweep Islands Is. Laquedivas Laos Laos Latvia Letonia Lebanon Líbano Lesotho Lesoto Liberia Liberia Libya Libia Liechtenstein Liechtenstein Lithuania Lituania Lord Howe Island Is. Lord Howe Luxembourg Luxemburgo Macao Macao Macquarie Island Is. Madquarie Madagascar Madagascar Madeira Islands Is. Madeira Malawi Malawi Maldives Maldivas Mali Malí Malpelo Island Is. Malpelo Malta Malta Mariana Islands Is. Mariana Market Reef Market Reef Marquesas Islands Is. Marquesas Marshall Islands Is. Marshall Martinique Martinica Mauritania Mauritania Mauritius Mauricio Mayotte Mayotte Mellish Reef Arrecife Mellish Mexico México Micronesia Micronesia Midway Island Is. Midway Minami Torishima Minami Torishima Moldova Moldavia Monaco Mónaco Mongolia Mongolia Montenegro Montenegro Montserrat Monserrat Morocco Marruecos Mount Athos Monte Athos Mozambique Mozambique Myanmar Myanmar (Birmania) N.Z. Subantarctic Is. Islas Subantárticas de N.Z. Namibia Namibia Nauru Nauru Navassa Island Is. Navassa Nepal Nepal Netherlands Países Bajos New Caledonia Nueva Caledonia New Zealand Nueva Zelanda Nicaragua Nicaragua Niger Niger Nigeria Nigeria Niue Niue Norfolk Island Is. Norfolk North Cook Islands Is. Cook del Norte North Macedonia Macedonia del Norte Northern Ireland Irlanda del Norte Norway Noruega Ogasawara Ogasawara Oman Omán Pakistan Pakistán Palau Palau Palestine Palestina Palmyra & Jarvis Islands Is. Palmyra y Jarvis Panama Panamá Papua New Guinea Papúa Nueva Guinea Paraguay Paraguay Peru Perú Peter 1 Island Is. Pedro I Philippines Filipinas Pitcairn Island Is.Pitcairn Poland Polonia Portugal Portugal Pr. Edward & Marion Is. Is. Príncipe Eduardo y Marion Pratas Island Is. Pratas Puerto Rico Puerto Rico Qatar Qatar Republic of Korea Corea del Sur Republic of Kosovo República de Kosovo Republic of South Sudan Sudán del Sur Republic of the Congo República del Congo Reunion Island Is. Reunión Revillagigedo Islas Revillagigedo Rodriguez Island Is. Rodrigues Romania Rumania Rotuma Island Is. Rotuma Rwanda Ruanda Saba & St. Eustatius Saba y San Eustaquio Sable Island Is. Sable Samoa Samoa San Andres & Providencia San Andrés y Providencia San Felix & San Ambrosio San Félix y San Ambrosio San Marino San Marino Sao Tome & Principe Santo Tomé y Príncipe Sardinia Cerdeña Saudi Arabia Arabia Saudita Scarborough Reef Arrecife Scarborough Scotland Escocia Senegal Senegal Serbia Serbia Seychelles Seychelles Sierra Leone Sierra Leona Singapore Singapur Sint Maarten Sint Maarten Slovak Republic Rep. Eslovaquia Slovenia Eslovenia Solomon Islands Is. Salomón Somalia Somalia South Africa Sudáfrica South Cook Islands Is. Cook del Sur South Georgia Island Is. Georgias del Sur South Orkney Islands Is. Orcadas del Sur South Sandwich Islands Is. Sandwich del Sur South Shetland Islands Is. Shetland del Sur Sov Mil Order of Malta Orden de Malta Spain España Spratly Islands Is. Spratly Sri Lanka Sri Lanka St. Barthelemy Isla San Bartolomé St. Helena Santa Helena St. Kitts & Nevis San Cristóbal y Nieves St. Lucia Isla de Santa Lucía St. Martin Isla de San Martín St. Paul Island Is. de San Pablo St. Peter & St. Paul San Pedro y San Pablo St. Pierre & Miquelon San Pedro y Miquelón St. Vincent Isla San Vicente Sudan Sudán Suriname Surinam Svalbard Svalbard Swains Island Is. Swains Sweden Suecia Switzerland Suiza Syria Siria Taiwan Taiwán Tajikistan Tajikistan Tanzania Tanzania Temotu Province Provincia Temotu Thailand Tailandia The Gambia Gambia Timor - Leste Timor Oriental Togo Togo Tokelau Islands Is. Tokelau Tonga Tonga Trindade & Martim Vaz Islas Trinidad y Martín Vaz Trinidad & Tobago Trinidad y Tobago Tristan da Cunha & Gough Islands Islas Tristán de Acuña y Gough Tromelin Island Is. Tromelin Tunisia Túnez Turkmenistan Turkmenistán Turks & Caicos Islands Is. Turcas y Caicos Tuvalu Tuvalu UK Base Areas on Cyprus Área Soberana Inglesa en Chipre US Virgin Islands Is. Vírgenes Americanas Uganda Uganda Ukraine Ucrania United Arab Emirates Emiratos Árabes Unidos United Nations HQ ONU Nueva York United States Estados Unidos Uruguay Uruguay Uzbekistan Uzbekistan Vanuatu Vanuatu Vatican City Ciudad del Vaticano Venezuela Venezuela Vietnam Vietnam Wake Island Wake Is. Wales Gales Wallis & Futuna Islands Wallis y Futuna Is. West Malaysia Malasia Occidental (Peninsular) Western Kiribati Kiribati Occidental Western Sahara Sahara Occidental Willis Island Willis Is. Yemen Yemen Zambia Zambia Zimbabwe Zimbabue DXCCSubmissionDialog DXCC Submission List Lista de envío DXCC Options Opciones My DXCC Entity Mi Entidad DXCC User Filter Filtro de usuario Category Categoría Mixed Mixto CW CW Phone Fonía Digital Digital Confirmed by Confirmado por LoTW LoTW Paper Papel Show Mostrar Not Yet Submitted Aún no enviado Submitted (Not Granted) Enviado (no concedido) Already Granted Ya concedido Band Scope Alcance de banda Select 5-Band DXCC preset bands (80/40/20/15/10m) Seleccionar bandas predefinidas DXCC de 5 bandas (80/40/20/15/10 m) 5-Band DXCC DXCC de 5 bandas Select all DXCC-eligible bands Seleccionar todas las bandas elegibles para DXCC All DXCC Bands Todas las bandas DXCC Bands Bandas Select options above and the list will update automatically. Seleccione las opciones anteriores y la lista se actualizará automáticamente. Export the contacts listed above to an ADIF file Exportar los contactos listados arriba a un archivo ADIF Export Exportar No User Filter Sin filtro de usuario Any Band (Entity Level) Cualquier banda (nivel de entidad) 5-Band DXCC (80/40/20/15/10m) DXCC de 5 bandas (80/40/20/15/10 m) Custom Band Selection Selección personalizada de bandas Entity Entidad Prefix Prefijo Callsign Indicativo Band Banda Mode Modo Date Fecha Submitted Enviado Granted Concedido Export ADIF Exportar ADIF No contacts to export. No hay contactos para exportar. Failed to retrieve contact records. No se pudieron obtener los registros de contactos. Export DXCC Submission List as ADIF Exportar la lista de envío DXCC como ADIF any band cualquier banda 5-band 5 bandas all bands todas las bandas %1 selected band(s) %1 banda(s) seleccionada(s) No contacts match the selected criteria. Ningún contacto coincide con los criterios seleccionados. %1 %2 %3 — DXCC %4 / %5 %1 %2 %3 — DXCC %4 / %5 band-slot ranura de banda entity entidad entry entrada entries entradas Data New Entity Nueva Entidad New Band Nueva Banda New Mode Nuevo Modo New Band&Mode Nueva Banda y Modo New Slot Nuevo Slot Confirmed Confirmado Worked Trabajado Hz Hz kHz kHz GHz GHz MHz MHz Yes No No Requested Solicitado Queued En Cola Invalid Inválido Bureau Bureau Direct Directa Electronic Electrónica Blank Vacío Modified Modificado Grayline Línea Gris Other Otro Short Path Paso Corto Long Path Paso Largo Not Heard No Escuchado Uncertain Dudoso Straight Key Sideswiper Mechanical semi-automatic keyer or Bug Mechanical fully-automatic keyer or Bug Single Paddle Pala Simple Dual Paddle Computer Driven Confirmed (AG) Confirmado (AG) Confirmed (no AG) Confirmado (non-AG) Unknown Desconocido Aircraft Scatter Reflexión por Aviones Aurora-E Aurora-E Aurora Aurora Back scatter Retrodispersión EchoLink EchoLink Earth-Moon-Earth Tierra-Luna-Tierra Sporadic E Esporádica E F2 Reflection Reflexión F2 Field Aligned Irregularities Irregularidades FAI Ground Wave Ondas de Tierra Internet-assisted Asistido por Internet Ionoscatter Dispersión Ionosférica IRLP Line of Sight Línea de Visión Meteor scatter Dispersión Meteórica Terrestrial or atmospheric repeater or transponder Repetidor o transpondedor terrestre o atmosférico Rain scatter Dispersión por Lluvia Satellite Satélite Trans-equatorial Trans-ecuatorial Tropospheric ducting Conductos Troposféricos DateFormatDelegate Blank Vacío DevToolsDialog Developer Tools Herramientas dev Debug Log Registro de depuración Logging Rules: Reglas de registro: e.g. qlog.ui.*.runtime=true p. ej. qlog.ui.*.runtime=true Apply Aplicar Generate Debug File Generar archivo de depuración Export File Exportar archivo SQL Console Consola SQL Open SQL Abrir SQL Save SQL Guardar SQL Run SQL Ejecutar SQL Export As Exportar como Enter SQL query here... Ctrl+Return = run Introduzca la consulta SQL aquí... Ctrl+Enter = ejecutar TXT CSV ADI Open SQL Query Abrir consulta SQL SQL Files (*.sql);;All Files (*) Archivos SQL (*.sql);;Todos los archivos (*) Open Error Error al abrir Cannot open file: %1 No se puede abrir el archivo: %1 Save SQL Query Guardar consulta SQL Save Error Error de guardado Cannot save file: %1 No se puede guardar el archivo: %1 Query saved to %1 Consulta guardada en %1 Error: %1 Error: %1 %1 row(s) shown (truncated at %2) in %3 ms %1 fila(s) mostrada(s) (recortado a %2) en %3 ms %1 row(s) returned in %2 ms %1 fila(s) devuelta(s) en %2 ms Export Exportar No results to export. No hay resultados para exportar. Export Error Error de exportación Cannot open file for writing: %1 No se puede abrir el archivo para escritura: %1 Exported %1 row(s) to %2 Exportadas %1 fila(s) a %2 Text Files (*.txt);;All Files (*) Archivos de texto (*.txt);;Todos los archivos (*) CSV Files (*.csv);;All Files (*) Archivos CSV (*.csv);;Todos los archivos (*) ADIF Export Exportar ADIF ADIF export requires the query to include the contacts 'id' column. Example: SELECT id, callsign FROM contacts WHERE ... No valid contact IDs found in the result set. No se encontraron IDs de contacto válidos en el conjunto de resultados. Failed to retrieve contact records: %1 No se pudieron obtener los registros de contactos: %1 No matching contacts found in the database. No se encontraron contactos coincidentes en la base de datos. Logging rules cleared (defaults) Reglas de registro restablecidas (predeterminadas) Logging rules applied Reglas de registro aplicadas Save Debug Log Guardar registro de depuración No debug log file is currently being written Actualmente no se está escribiendo ningún archivo de registro de depuración Log Files (*.log);;All Files (*) Archivos de registro (*.log);;Todos los archivos (*) Debug log saved to %1 Registro de depuración guardado en %1 Failed to copy the debug log file. No se pudo copiar el archivo de registro de depuración. File logging is disabled El registro en archivo está desactivado Log file: %1 Archivo de registro: %1 DownloadQSLDialog Download QSLs Descargar QSLs eQSL eQSL eQSL QTH Profile Perfil de QTH en eQSL QSLs Since QSLs desde QSOs Since QSO desde LoTW LoTW My Callsign Mi Indicativo &Download &Descargar LoTW is not configured properly.<p> Please, use <b>Settings</b> dialog to configure it.</p> LoTW no está configurado apropiadamente.<p> Por favor usar <b>Ajustes</b> para configurarlo.</p> eQSL is not configured properly.<p> Please, use <b>Settings</b> dialog to configure it.</p> eQSL no está configurado apropiadamente.<p> Por favor usar <b>Ajustes</b> para configurarlo.</p> Cancel Cancelar Downloading from %1 Descargando desde %1 Processing %1 QSLs Procesando %1 QSLs QLog Error Error de QLog %1 update failed: Error al actualizar %1: QLog Information Información de QLog No service selected No se ha seleccionado ningún servicio DxFilterDialog DX Cluster Filters Filtros del DX Cluster General Filters Filtro Generales Bands Bandas Modes Modos Phone Fonía CW CW Digital Digital Continent Continente North America Norteamérica Africa África Antarctica Antártida South America Sudamérica Asia Asia Europe Europa FTx (FT8/FT4) FTx (FT8/FT4) Oceania Oceanía Log Status Estado del Registro New Mode Nuevo Modo New Entity Nueva Entidad New Band Nueva Banda New Slot Nuevo Slot Worked Trabajado Confirmed Confirmado Extended Filters Filtros Extendidos Spotter Continent Continente del Anunciante Do not show the following spots when they have the same Callsign and their time difference is lower than Time Diff and their frequency difference is lower than Freq Diff No mostrar los siguientes spots cuando tengan el mismo indicativo y su diferencia horaria sea inferior a Time Diff y su diferencia de frecuencia sea inferior a Freq Diff Spots Dedup Deduplicación de Anuncios Time difference Diferencia Horaria Diferencia Horaria s s Frequency difference Diferencia de Frecuencia Diferencia de Frecuencia kHz Khz Member Miembro No Club List is enabled No hay ninguna lista de clubes habilitada DxTableModel Time Hora Callsign Indicativo Frequency Frecuencia Mode Modo Spotter Anunciante Comment Comentario Continent Continente Spotter Continent Continente del Anunciante Band Banda Member Miembro Country País DxWidget Insert a <b>hostname:port</b> of DXC Server. Inserte <b>nombre de host:puerto</b> del servidor del Cluster DX. Connect Conectar Filtered Filtrado Spots Anuncios WCY WCY WWV WWV To All A Todo Console Consola 15-min Trend Full-text search Búsqueda de texto completo Search Búsqueda Close Search Cerrar búsqueda Send DX Cluster Command Enviar comando de Cluster DX Filter... Filtrar... Filter DXC Filtrar Cluster DX Spot Last QSO Anun. Últ. QSO Send last QSO spot Anunciar el último QSO realizado Show HF Stats Mostrar Estadísticas de HF Show VHF Stats Mostrar Estadísticas de VHF Show WCY Mostrar WCY Show WWV Mostrar WWV Column Visibility... Visibilidad de Columnas... Which columns should be displayed Qué columnas deben mostrarse Connect on startup Conectar al inicio Automatic connection after start Conectar automáticamente después de iniciar Delete Server Borrar Servidor DXC - Delete Server Cluster DX - Eliminar Servidor Clear Password Borrar Contraseña Keep Spots Mantener Anuncios Spots are not cleared when connecting to a new DX Cluster. Los anuncios no se borran al conectarse a un nuevo clúster DX. Clear Borrar Clear all data Borrar todos los datos Search... Búsqueda... DXC - Search DXC - Búsqueda My Continent Mi continente Auto Automático Connecting... Conectando... DX Cluster is temporarily unavailable El Cluster DX no está disponible temporalmente DXC Server Error Error del Servidor del Cluster DX An invalid callsign Un indicativo inválido DX Cluster Password Contraseña del Cluster DX Security Notice Aviso de Seguridad The password can be sent via an unsecured channel La contraseña se puede enviar a través de un canal no seguro Server Servidor Username Nombre de Usuario Disconnect Desconectar DX Cluster Command Comando de DX Cluster DxccTableModel Worked Trabajado eQSL eQSL LoTW LoTW Paper Papel DxccTableWidget Mode Modo EQSLQSLDownloader Incorrect Password or QTHProfile Id Contraseña o PerfilQTH Id incorrectos ADIF file not found in eQSL response Incorrect Username or password Nombre de usuario o contraseña incorrectos Unknown Error Error Desconocido Cannot opet temporary file No se puede abrir el archivo temporal Cannot save the image to file No se puede guardar la imagen en el archivo EQSLUploader Unknown Reply from eQSL Respuesta Desconocida de eQSL EditActivitiesDialog Edit Activities Editar actividades Activities Actividades Add Añadir Edit Editar Remove Eliminar ExportDialog Export Selected QSOs Exportar los QSOs Seleccionados File Archivo Browse Navegar ADX CSV JSON POTA POTA Export Type Tipo de Exportación Column set Conjunto de columnas Select one of the pre-defined sets of columns or define your own set of columns Select one of the pre-defined sets of columns or define your own set of columns Edit current set of columns Editar el conjunto actual de columnas Edit Editar Mark exported QSOs As Sent Marcar QSOs exportados como enviados Export only QSOs that match the active filters Exporta solo QSOs que coincidan con los filtros activos. Filters Filtros Export only QSOs that match the selected date range Exporte solo QSOs que coincidan con el rango de fechas seleccionado Date Range Rango de Fechas Export only QSOs that match the selected My Callsign Exporta solo QSOs que coincidan con Mi Indicativo seleccionado My Callsign Mi Indicativo Export only QSOs that match the selected My Gridsquare Exporta solo QSOs que coincidan con Mi Locator seleccionado My Gridsquare Mi Locator Export only QSOs that match the selected QSL Send Via value Exporte solo los QSO que coincidan con el Valor de Enviar QSL Vía seleccionado QSL Send via Enviar QSL vía Include unusual QSO Sent statuses Incluir estados inusuales de QSO enviados Include Sent Status Incluir Estado de Envío Under normal circumstances this status means <b>"Ignore/Invalid"</b>.<br/>However, it may sometimes be wanted to ignore this setting when sending a QSL. En circunstancias normales, este estado significa <b>"Ignorado/Inválido"</b>.<br/>Sin embargo, a veces es posible que desee ignorar esta configuración al enviar una QSL. "Ignore" "Ignorar" Under normal circumstances this status means <b>"do not send"</b>.<br/>However, it may sometimes be wanted to ignore this setting when sending a QSL. En circunstancias normales, este estado significa <b>"no enviar"</b>.<br/>Sin embargo, a veces es posible que desee ignorar esta configuración al enviar una QSL. "No" "No" Resend already sent QSOs Reenviar QSOs ya enviados Already Sent Ya está Enviado User Filter Filtro de usuario Export QSOs that match the selected user QSO Filter Exportar QSO que coincidan con el filtro de QSO de usuario seleccionado Station Profile Perfil de estación Export QSOs Exportar QSOs &Export &Exportar Export only QSOs matching this station profile Exportar solo los QSO que coincidan con este perfil de estación Exporting... Exportando… Cancel Cancelar QLog Error Error de QLog Cannot mark exported QSOs as Sent No se pueden marcar los QSOs exportados como enviados Generic Genérico QSLs QSLs All Todas Minimal Mínimo QSL-specific Específico de QSL Custom 1 Personalizado 1 Custom 2 Personalizado 2 Custom 3 Personalizado 3 ExportPasswordDialog Pack Data & Settings Empaquetar datos y configuración Enter a password to encrypt stored credentials. The password will be needed to restore them later. Introduzca una contraseña para cifrar las credenciales almacenadas. Esta contraseña será necesaria más adelante para restaurarlas. Password Contraseña Random Aleatorio Confirm Password Confirmar contraseña Delete passwords from Credential Store after export Eliminar las contraseñas del almacén de credenciales después de la exportación FlrigRigDrv Timeout Se agotó el tiempo de espera FLRig response timeout Tiempo de espera agotado para la respuesta de FLRig Network Error Error de red HRDLogUploader Response message malformed Mensaje de respuesta con formato incorrecto HamQTHCallbook HamQTH HamQTH HamlibRigDrv None Ninguno Ninguno CAT Sintonización asistida por ordenador CAT DTR Terminal de datos listo (DTR) es una señal de control en comunicaciones serie RS-232, transmitida desde un equipo terminal de datos (DTE), como una computadora, a un equipo de comunicaciones de datos (DCE), por ejemplo un interface, para indicar que el terminal está listo para comunicaciones y el interface puede iniciar un canal de comunicaciones. DTR RTS Control de Señal del Puerto Serie RTS Initialization Error Error al Inicializar Cannot set PTT Type No se puede configurar el tipo de PTT No se puede configurar el tipo de PTT Cannot set PTT Share No se puede configurar la acción del PTT No se puede configurar la acción del PTT Cannot set CIV Addr No se puede establecer la dirección CI-V Unsupported Rig Driver Controlador de Radio No Soportado Cannot set auto_power_on No se puede establecer auto_power_on Cannot set no_xchg to 1 No se puede establecer no_xchg en 1 Rig Open Error Conexión fallida Set TX Frequency Error Error al establecer la frecuencia TX Set Frequency Error Error al Establecer Frecuencia Set Split Error Error al establecer split Set Mode Error Error al establecer el modo Set Split Mode Error Error al establecer el modo split Set PTT Error Error al Establecer PTT Cannot sent Morse This cannot be displayed No se puede enviar Morse Cannot stop Morse This cannot be displayed No se puede detener Morse Get PTT Error No se puede visualizar Error al obtener PTT Get Frequency Error Error al Obtener Frecuencia Get Mode Error Error al Obtener Modo Get VFO Error Error al obtener el VFO Get PWR Error No se puede visualizar Error al obtener PWR Get PWR (power2mw) Error No se puede visualizar Error al obtener PWR (power2mw) Get RIT Function Error No se puede visualizar Error al obtenerfunción de RIT Get RIT Error No se puede visualizar Error al obtener RIT Get XIT Function Error No se puede visualizar Error al obtener función XIT Get XIT Error No se puede visualizar Error al obtener XIT Get Split Error Get TX Frequency Error Get KeySpeed Error No se puede visualizar Error al obtener KeySpeed Set KeySpeed Error No se puede visualizar Configurar error de KeySpeed HamlibRotDrv Initialization Error Error al Inicializar Unsupported Rotator Driver Controlador de Rotor No Soportado Rot Open Error Conexión fallida Set Possition Error Error al Establecer Posición Get Possition Error Error al Obtener Posición ImportDialog Import Importar Date Range Rango de Fechas Import all or only QSOs from the given period Importar todos o sólo los QSOs del período determinado All Todos File Archivo ADX Browse Navegar Options Opciones The value is used when an input record does not contain the ADIF value El valor se utiliza cuando un registro de entrada no contiene el valor ADIF Defaults Valores predeterminados My Profile Mi Perfil My Rig Mi Radio Comment Comentario If DXCC is missing in the imported record, it will be resolved from the callsign. Si falta el DXCC en el registro importado, se resolverá a partir del indicativo. Fill missing DXCC Entity Information Completar la información de entidad DXCC faltante &Import &Importar Select File Seleccionar Archivo The values below will be used when an input record does not contain the ADIF values Los valores siguientes se utilizarán cuando un registro de entrada no contenga los valores ADIF <p><b>In-Log QSO:</b></p><p> <p><b>QSO en Log:</b></p><p> <p><b>Importing:</b></p><p> <p><b>Importando:</b></p><p> Duplicate QSO QSO Duplicado <p>Do you want to import duplicate QSO?</p>%1 %2 <p>¿Quiere importar QSO duplicado?</p>%1 %2 Save to File Guardar al Archivo QLog Import Summary Resumen de Importación de QLog Import date Fecha de Importación Imported file Archivo Importado Imported: %n contact(s) Importado: %n contacto/s Warning(s): %n Alerta/s: %n Error(s): %n Error/s: %n Details Detalles Import Result Resultado de Importación Save Details... Guardar Detalles... InputPasswordDialog Dialog The password will be stored in the Credential Store. La contraseña se guardará en el Almacén de Credenciales. Remember Password Recordar Contraseña KSTChat Unknown User Usuario desconocido Invalid password Contraseña incorrecta KSTChatWidget Chat messages Mensajes de Chat Valuable messages Mensajes valiosos Clear selected Callsign and Chat message entry. Borrar el indicativo seleccionado y la entrada de mensaje Send chat message Enviar mensaje de chat Column Visibility Visibilidad de Columnas Which columns should be displayed Qué columnas deben mostrarse Prepare QSO Preparar QSO Transfer Callsign and Gridsquare Information to the New QSO dialog Transferir información de Indicativo y Locator a Nuevo QSO Show About Me Only Mostrar Acerca de Mi Únicamente Show only messages where my callsign is present Mostrar solo mensajes que contengan mi indicativo Suppress User To User Suprimir Usuario a Usuario Suppress private messages between two callsigns Suprimir mensajes privados entre dos indicativos Highlight Resaltar Highlight messages based on the setting Resaltar mensajes basados en los ajustes Highlight Rules Reglas de Resaltado Beam Haz Antena Clear Messages Borrar Mensajes Clear all messages in the window Borrar todos los mensajes en la ventana You Usted KSTHighlightRuleDetail Edit Rule Editar Regla Rule Name Nombre de la Regla Chat Room Sala de Chat Enabled Habilitar Highlight message which match Resaltar mensaje que coincida con All the following conditions Todas las siguientes condiciones Any of the following conditions Cualquiera de las siguientes condiciones Add Condition Agregar Condición All Todos Sender Remitente Message Mensaje Gridsquare Locator Contains Contiene Starts with Comience con Remove Quitar Must not be empty No debe estar vacío KSTHighlighterSettingDialog Highlight Rules Reglas de Resaltado de Mensajes Rules Reglas Add Agregar Edit Editar Remove Quitar Name Nombre State Estado KeySequenceEdit Clear Borrar LoadDatabaseDialog Unpack Data & Settings Desempaquetar datos y configuración Warning Alerta <html><head/><body><p>⚠ <span style=" font-weight:700;">Current database will be DELETED!</span><br/>⚠ <span style=" font-weight:700;">All stored passwords will be DELETED!</span><br/>⚠ <span style=" font-weight:700;">The application will restart after loading</span>.</p><p>To preserve data, use Pack Data &amp; Settings or Export QSOs first.</p></body></html> <html><head/><body><p>⚠ <span style=" font-weight:700;">¡La base de datos actual será ELIMINADA!</span><br/>⚠ <span style=" font-weight:700;">¡Todas las contraseñas almacenadas serán ELIMINADAS!</span><br/>⚠ <span style=" font-weight:700;">La aplicación se reiniciará después de la carga</span>.</p><p>Para conservar los datos, utilice primero Pack Data &amp; Settings o Export QSOs.</p></body></html> Select Database Seleccionar base de datos File: Archivo: Browse Navegar Status: No file selected Estado: Ningún archivo seleccionado Decrypt Credentials Descifrar credenciales Enter password to decrypt credentials Introduzca la contraseña para descifrar las credenciales Password: Contraseña: Load && Restart Cargar y reiniciar Invalid password Contraseña incorrecta Select Database File Seleccionar archivo de base de datos QLog Database Export (*.dbe);;All Files (*) Exportación de base de datos QLog (*.dbe);;Todos los archivos (*) Cannot create temporary file No se puede crear el archivo temporal Decompressing database... Descomprimiendo base de datos… Cannot decompress database file No se puede descomprimir el archivo de la base de datos Cannot open database No se puede abrir la base de datos Valid database Base de datos válida Different platform Plataforma diferente Password required to import credentials Se requiere contraseña para importar credenciales No encrypted credentials in database No hay credenciales cifradas en la base de datos LogFormat Cannot find My DXCC Entity Info No se puede encontrar la información de mi entidad DXCC A minimal set of fields not present (start_time, call, band, mode, station_callsign) Un conjunto mínimo de campos no presentes (hora_inicio, indicativo, banda, modo, indicativo de estación) Outside the selected Date Range Fuera del rango de fechas seleccionado Duplicate Duplicado DXCC Info is missing La información DXCC falta no Station Callsign present no hay Indicativo de Estación presente Cannot insert to database No se puede insertar en la base de datos Imported Importado DXCC State: Estado DXCC: Error Error Warning Alerta LogbookModel QSO ID ID QSO Time on Hora inicio Time off Hora fin Call Indicativo RSTs RSTe RSTr RSTr Frequency Frecuencia Band Banda Mode Modo Submode Submodo Name (ASCII) Nombre (ASCII) QTH (ASCII) QTH (ASCII) Gridsquare Locator DXCC DXCC Country (ASCII) País (ASCII) Continent Continente CQZ CQZ ITU ITU Prefix Prefijo State Estado County Condado IOTA IOTA QSLr QSLr QSLr Date Fecha QSLr QSLs QSLe QSLs Date Fecha QSLe LoTWr LoTWr LoTWr Date Fecha LoTWr LoTWs LoTWe LoTWs Date Fecha LoTWe TX PWR Potencia TX Additional Fields Campos Adicionales Address (ASCII) Dirección (ASCII) Address Dirección Age Edad Altitude Altitud A-Index A-Index Antenna Az Antena Az Antenna El Antena El Signal Path Paso de Antena ARRL Section Sección ARRL Award Submitted Diploma Presentado Award Granted Diploma Otorgado Band RX Banda RX Gridsquare Extended Locator Extendido Contest Check Prueba de Concurso Class Categoría ClubLog Upload Date Fecha de carga en ClubLog ClubLog Upload State Estado de carga en ClubLog Comment (ASCII) Comentario (ASCII) Comment Comentario Contacted Operator Operador Contactado Contest ID ID del Concurso Country País County Alt Condado Alt Credit Submitted Crédito Recibido Credit Granted Crédito Otorgado DOK DOK DCLr Date Fecha DCLr DCLs Date Fecha DCLe DCLr DCLr DCLs DCLe Distance Distancia Email EMail Owner Callsign Indicativo del Propietario eQSL AG eQSL AG eQSLr Date Fecha eQSLr eQSLs Date Fecha eQSLe eQSLr eQSLr eQSLs eQSLe FISTS Number Número FISTS FISTS CC CC FISTS EME Init Inicio EME Frequency RX Frecuencia RX Guest Operator Operador Invitado HamlogEU Upload Date Fecha de carga en HamlogEU HamlogEU Upload Status Estado de carga en HamlogEU HamQTH Upload Date Fecha de carga en HamQTH HamQTH Upload Status Estado de carga en HamQTH HRDLog Upload Date Fecha de carga en HRDLog HRDLog Upload Status Estado de carga en HRDLog IOTA Island ID ID IOTA de la Isla K-Index K-Index Latitude Latitud Longitude Longitud Max Bursts Ráfagas Máximas MS Shower Name Nombre Lluvia Meteroros My Altitude Mi Altitud My Antenna (ASCII) Mi Antena (ASCII) My Antenna Mi Antena My City (ASCII) Mi Ciudad (ASCII) My City Mi Ciudad My County Mi Condado My County Alt Mi Condado Alt My Country (ASCII) Mi Condado (ASCII) My Country Mi País My CQZ Mi Zona CQ My DARC DOK Mi DARC DOK My DXCC Mi DXCC My FISTS Mi FISTS My Gridsquare Mi Locator My Gridsquare Extended Mi Locator Extendido My IOTA Mi IOTA My IOTA Island ID Mi ID IOTA de la isla My ITU MI ITU My Latitude Mi Latitud My Longitude Mi Longitud My Name (ASCII) Mi Nombre (ASCII) My Name Mi Nombre My Postal Code (ASCII) Mi Código Postal (ASCII) My Postal Code Mi Código Postal My POTA Ref Mi Ref POTA My Rig (ASCII) Mi Radio (ASCII) My Rig Mi Radio My Special Interest Activity (ASCII) Mi Actividad de Interés Especial (ASCII) My Special Interest Activity Mi Actividad de Interés Especial My Spec. Interes Activity Info (ASCII) Mi Info de Actividad de Interés Especial (ASCII) My Spec. Interest Activity Info Mi Info de Actividad de Interés Especial My SOTA Mi SOTA My State Mi Estado My Street Mi Calle My USA-CA Counties Mi Condado USA-CA My VUCC Grids Mi Locator VUCC Name Nombre Notes (ASCII) Notas (ASCII) QRZ Download Date Fecha de descarga de QRZ QRZ Download Status Estado de la descarga QRZ QSLs Message (ASCII) QSLs Message Mensaje del QSL QSLr Message RcvPWR Potencia recibida RcvNr Número recibido RcvExch Intercambio recibido SentNr Número enviado SentExch Intercambio enviado Notes Notas #MS Bursts #MS Pings POTA POTA Contest Precedence Precedencia del Concurso Propagation Mode Modo de Propagación Public Encryption Key Clave de Cifrado Pública QRZ Upload Date Fecha de carga en QRZ QRZ Upload Status Estado de carga en QRZ QSL Message Mensaje del QSL CW Key Info CW Key Type My CW Key Info My CW Key Type Operator Callsign Indicativo del operador QSLr Via QSLr Vía QSLs Via QSLe Vía QSL Via QSL vía QSO Completed QSO Completado QSO Random QSO Aleatorio QTH QTH Region Región Rig (ASCII) Radio (ASCII) Rig Equipo SAT Mode Modo del Satélite SAT Name Nombre del Satélite Solar Flux Flujo Solar SIG (ASCII) SIG SIG Info (ASCII) SIG Info Silent Key SKCC Member Miembro SKCC SOTA SOTA Logging Station Callsign Indicativo de la Estación SWL SWL Ten-Ten Number Número Ten-Ten UKSMG Member Miembro UKSMG USA-CA Counties Condado USA-CA VE Prov Provincia VE VUCC VUCC Web Web My ARRL Section Mi Sección ARRL My WWFF Mi WWFF WWFF WWFF RST Sent RST Enviadas RST Rcvd RST Recibidas Paper Papel LoTW LoTW eQSL eQSL QSL Received QSL Recibida QSL Sent QSL Enviada LogbookWidget Delete Eliminar Logbook - Delete QSO Libro de Guardia - Eliminar QSO Update from Callbook Actualización del Callbook Logbook - Edit Value Libro de Guardia - Editar Valor Send DX Spot Envía un Spot de DX Enviar Spot de DX Logbook - Send DX Spot El libro envia Spot de DX Upload to Clublog Subir a Clublog Lookup on Web Buscar en la Web Add Missing Info Añadir la información que falta Mark QSL RCVD Marcar QSL como recibida Mark QSL Requested Marcar QSL solicitada Filter Callsign Filtrar Indicativo Edit Value Editar Valor Column Visibility Visibilidad de Columnas Which columns should be displayed Qué columnas deben mostrarse Export Selected Exportar Seleccionados Export selected QSOs Exportar los QSOs seleccionados Callsign Indicativo Gridsquare POTA POTA SOTA SOTA WWFF WWFF SIG SIG IOTA IOTA Delete the selected contacts? ¿Eliminar los conactos seleccionados? Clublog's <b>Immediately Send</b> supports only one-by-one deletion<br><br>Do you want to continue despite the fact<br>that the DELETE operation will not be sent to Clublog? Deleting QSOs Borrando QSOs Update Actualizar By updating, all selected rows will be affected.<br>The value currently edited in the column will be applied to all selected rows.<br><br>Do you want to edit them? Al actualizar, todas las filas seleccionadas se verán afectadas.<br>El valor actualmente editado en la columna se aplicará a todas las filas seleccionadas.<br><br>¿Quieres editarlas? Count: %n QSO: %n QSOs: %n Downloading eQSL Image Descargando Imágen de eQSL Cancel Cancelar All Bands Todas las bandas All Modes Todos los modos All Countries Todos los países No User Filter Sin filtro de usuario QLog Warning Alerta de QLog Each batch supports up to 100 QSOs. Cada lote admite hasta 100 QSO. QSOs Update Progress Actualización de QSOs Progreso QLog Error Error de QLog Callbook login failed Error al iniciar sesión en el Callbook Callbook error: Error del Libro de Guardia: All Clubs Todos los clubes eQSL Download Image failed: La descarga de imágen de eQSL ha fallado: LotwQSLDownloader Cannot open temporary file No se puede abrir el archivo temporal Incorrect login or password Usuario o contraseña incorrectos LotwUploader Upload cancelled by user Carga cancelada por el usuario Upload rejected by LoTW Carga rechazada por LoTW Unexpected response from TQSL server Respuesta inesperada del servidor TQSL TQSL utility error Error de TQSL TQSLlib error Error de TQSLlib Unable to open input file No se puede abrir el archivo de entrante Unable to open output file No se puede abrir el archivo saliente All QSOs were duplicates or out of date range Todos los QSOs estaban duplicados o fuera de rango Some QSOs were duplicates or out of date range Algunos QSOs estaban duplicados o fuera de rango Command syntax error Error de sintaxis LoTW Connection error (no network or LoTW is unreachable) Error de conexión a LoTW (no hay red o LoTW es inaccesible) Unexpected Error from TQSL Error inesperado de TQSL TQSL not found TQSL no encontrado TQSL crashed TQSL falló MainWindow &File &Archivo &Logbook &Libro de Guardia &Equipment E&quipo &Help A&yuda &Window &Ventana Se&rvice &Servicios Toolbar Barra de herramientas Clock Reloj Map Mapa DX Cluster Cluster DX WSJTX WSJTX Rotator Rotor Bandmap Mapa de Banda Rig Radio Online Map Mapa en línea CW Console Consola CW Chat Chat Profile Image Imágen de Perfil Alerts Alertas &Settings &Ajustes &Import &Importar &Export &Exportar Connect R&ig Conectar &Radio &About &Acerca de Upload Subir Service - Upload QSOs Servicios - Subir QSOs Download QSLs Descargar QSLs Service - Download QSLs Servicios - Descargar QSLs Quit Salir Application - Quit Aplicación - Salir New QSO - Clear Nuevo QSO - Limpiar New QSO - Save Nuevo QSO - Guardar S&tatistics Es&tadísticas Wsjtx Wsjtx Connect R&otator Conectar R&otor QSO &Filters &Filtrar QSO &Awards &Diplomas Edit Rules Editar Reglas Clear Limpiar Show Alerts Mostrar Alertas Beep Campana Contest Concurso Dupe Check Comprobación de duplicados Sequence Secuencia Linking Exchange With Intercambio de enlaces con Pack Data && Settings Empaquetar datos y configuración Unpack Data && Settings Desempaquetar datos y configuración QSL &Gallery &Galería QSL Developer Tools Herramientas dev Run custom read-only SQL queries against the logbook database Ejecutar consultas SQL personalizadas de solo lectura sobre la base de datos del log Print QSL &Labels &Imprimir etiquetas QSL DXCC &Submission List &Lista de envío DXCC Generate a list of contacts to submit for ARRL DXCC award credit Generar una lista de contactos para presentar para el crédito del premio ARRL DXCC Connect &CW Keyer Conectar &Manipulador &Wiki &Wiki Report &Bug... Reportar &Bug... &Manual Entry Entrada &Manual Switch New Contact dialog to the manually entry mode<br/>(time, freq, profiles etc. are not taken from their common sources) Cambia la pantalla de Nuevo Contacto a Entrada Manual<br/>(hora, frec, perfiles etc. no se tomarán de las fuentes habituales) Mailing List... Lista de Correo... Edit Editar Save Arrangement Guardar Disposición Keep Options Mantener Opciones Restore connection options after application restart Restaurar las opciones de conexión después de reiniciar Logbook - Search Callsign Libro de Guardia - Buscar Indicativo New QSO - Add text from Callsign field to Bandmap Nuevo QSO - Agregar texto del campo Indicativo al Mapa de Banda Rig - Band Down Radio - Bajar Banda Rig - Band Up Radio - Subir Banda New QSO - Use Callsign from the Whisperer Nuevo QSO - Usar el indicativo del Whisperer CW Console - Key Speed Up Consola CW - Subir Velocidad CW Console - Key Speed Down Consola CW - Bajar Velocidad CW Console - Profile Up Consola CW - Subir Perfil CW Console - Profile Down Consola CW - Bajar Perfil Rig - PTT On/Off Radio - PTT Enc./Apag All Bands Todas las bandas Each Band Cada banda Each Band && Mode Cada banda y modo No Check Sin control Single Único Per Band Por banda Stop Parar Reset Restablecer None Ninguno Theme: Native Tema: Native Theme: QLog Light Tema: QLog Light Theme: QLog Dark Tema: QLog Dark What's New Novedades Export Cabrillo Exportar Cabrillo Color Theme Tema de color Not enabled for non-Fusion style No habilitado para estilos que no sean Fusion Press to tune the alert Presione para sintonizar la alerta Clublog Immediately Upload Error Error de carga inmediata de Clublog <b>Error Detail:</b> <b>Detalle del Error:</b> op: Op: A New Version Una nueva versión A new version %1 is available. Una nueva versión %1 está disponible. Remind Me Later Recordármelo más tarde Download Descargar Failed to encrypt credentials. No se pudo cifrar las credenciales. Database files (*.dbe);;All files (*) Archivos de base de datos (*.dbe);;Todos los archivos (*) Failed to create temporary file. No se pudo crear el archivo temporal. Failed to dump the database. No se pudo volcar la base de datos. Compressing database... Comprimiendo base de datos… Database successfully dumped to %1 Base de datos exportada con éxito a %1 Failed to compress the database. No se pudo comprimir la base de datos. Failed to prepare database for import. No se pudo preparar la base de datos para la importación. Classic Clásico Do you want to remove the Contest filter %1? ¿Desea eliminar el filtro Concurso %1? Contest: Concurso: <h1>QLog %1</h1><p>&copy; 2019 Thomas Gatzweiler DL2IC<br/>&copy; 2021-2026 Ladislav Foldyna OK1MLG<br/>&copy; 2025-2026 Michael Morgan AA5SH<br/>&copy; 2025-2026 Kyle Boyle VE9KZ</p><p>Based on Qt %2<br/>%3<br/>%4<br/>%5</p><p>Icon by <a href='http://www.iconshock.com'>Icon Shock</a><br />Satellite images by <a href='http://www.nasa.gov'>NASA</a><br />ZoneDetect by <a href='https://github.com/BertoldVdb/ZoneDetect'>Bertold Van den Bergh</a><br />TimeZone Database by <a href='https://github.com/evansiroky/timezone-boundary-builder'>Evan Siroky</a> <h1>QLog %1</h1><p>&copy; 2019 Thomas Gatzweiler DL2IC<br/>&copy; 2021-2026 Ladislav Foldyna OK1MLG<br/>&copy; 2025-2026 Michael Morgan AA5SH<br/>&copy; 2025-2026 Kyle Boyle VE9KZ</p><p>Basado en Qt %2<br/>%3<br/>%4<br/>%5</p><p>Iconos por <a href='http://www.iconshock.com'>Icon Shock</a><br />Imágenes satelitales por <a href='http://www.nasa.gov'>NASA</a><br />Detección de zonas por <a href='https://github.com/BertoldVdb/ZoneDetect'>Bertold Van den Bergh</a><br />Base de datos de zonas horarias por <a href='https://github.com/evansiroky/timezone-boundary-builder'>Evan Siroky</a> About Acerca de N/A S/D MapWebChannelHandler Grid Locator Gray-Line Línea Gris Beam Haz Antena Aurora Aurora MUF IBP Chat Chat WSJTX - CQ WSJTX - CQ Path Rutas NewContactWidget Frequency Frecuencia Callsign Indicativo <b>DUPE !!!</b> Contacto duplicado. <b>DUPL !!!</b> RX: RX: TX: TX: MHz 80m RSTs RSTe RSTr RSTr 59 Mode Modo Save Guardar Lookup the call on the web. The query URL can be changed in Settings -> Callbook Consulta el indicativo en la web. La URL de consulta se puede cambiar en Ajustes ->Callbook Web Web Time On Hora Inicio Reset Reiniciar Date Fecha Duration Duración Info Información &Details &Detalles QSL Send Status Estado de envío de QSL Paper Papel <b>Yes</b> - an outgoing QSL card has been sent; the QSO has been uploaded to, and accepted by, the online service<br/><b>No</b> - do not send an outgoing QSL card; do not upload the QSO to the online service<br/><b>Requested</b> - the contacted station has requested a QSL card; the contacted station has requested the QSO be uploaded to the online service<br/><b>Queued</b> - an outgoing QSL card has been selected to be sent; a QSO has been selected to be uploaded to the online service<br/> <b>Sí</b>: se ha enviado una tarjeta QSL; el QSO ha sido cargado y aceptado por el servicio en línea<br/><b>No</b> - no envié una tarjeta QSL; no cargué el QSO al servicio en línea<br/><b>Solicitado</b> - la estación contactada ha solicitado una tarjeta QSL; la estación contactada ha solicitado que el QSO se cargue en el servicio en línea<br/><b>En cola</b> - se ha seleccionado una tarjeta QSL para enviar; Se ha seleccionado un QSO para subir al servicio online<br/> LoTW LoTW eQSL eQSL QSL Send via Enviar QSL vía QSL via QSL vía Propagation Mode Modo de Propagación D&X Stats Estadísticas D&X <b>DXCC Statistics</b> <b>Estadísticas DXCC</b> <b>Station Statistics</b> <b>Estadísticas de la estación</b> M&y Station &Mi Estación Station Estación Rig Equipo Antenna Antena Blank Vacío W W My &Notes Mis N&otas Member: Miembro: Expand/Collapse Expandir/Contraer No No Yes Requested Solicitado Queued En Cola Ignored Ignorado Bureau Bureau Direct Directa Electronic Electrónica QLog Error Error de QLog Callbook login failed Error al iniciar sesión en el Callbook LP LP New Entity! Nueva Entidad! New Band! Nueva Banda! New Mode! Nuevo Modo! New Band & Mode! Nueva Banda y Modo! New Slot! Nuevo Slot! Worked Trabajado Confirmed Confirmado GE GE GM GM GA GA m Callbook search is inactive Búsqueda en Callbook desactivada Callbook search is active Búsqueda en Callbook activada Contest ID must be filled in to activate Debe rellenarse el ID del concurso para activarlo two or four adjacent Maidenhead grid locators, each four characters long, (ex. EN98,FM08,EM97,FM07) dos o cuatro locators adyacentes, cada uno de cuatro caracteres (ej. EN98,FM08,EM97,FM07) the contacted station's DARC DOK (District Location Code) (ex. A01) el DARC DOK (código de ubicación de distrito) de la estación contactada (ej. A01) World Wide Flora & Fauna World Wide Flora & Fauna Special Activity Group Grupo de Actividades Especiales Special Activity Group Information Información Grupo de Actividades Especiales It is not the name of the contest but it is an assigned<br>Contest ID (ex. CQ-WW-CW for CQ WW DX Contest (CW)) No es el nombre del concurso, sino un ID de concurso asignado (ej. CQ-WW-CW para CQ WW DX Contest (CW)) Description of the contacted station's equipment Descripción del equipo de la estación contactada OmnirigRigDrv Rig 1 Equipo 1 Rig 2 Equipo 2 Initialization Error Error al Inicializar Rig status changed El estado del equipo ha cambiado Rig is not connected El equipo no está conectado OmnirigV2RigDrv Rig 1 Equipo 1 Rig 2 Equipo 2 Rig 3 Equipo 3 Rig 4 Equipo 4 Initialization Error Error al Inicializar Rig status changed El estado del equipo ha cambiado Rig is not connected El equipo no está conectado PSTRotDrv Rot 1 Rot 1 Cannot bind a port No se puede vincular un puerto Cannot get IP Address for No se puede obtener la dirección IP para No IPv4 Address for Sin dirección IPv4 para Error Occurred Se produjo un error Operation Timeout Se ha agotado el tiempo de espera de la operación PaperQSLDialog Manage QSL Card Administrar QSL Available QSLs QSLs Disponibles Copy the input file from the source folder to the QLog Internal QSL Storage folder.<br/>The original file remains unchanged in the source folder Copie el archivo de la carpeta de origen a la carpeta de almacenamiento interno de QSL de QLog.<br/>El archivo original permanece sin cambios en la carpeta de origen Import QSL Importar QSL Add File Agregar Archivo Remove Quitar Delete Borrar Delete QSL? ¿Borrar QSL? Open Abrir Toggle Favorite Favorito PlatformSettingsDialog Platform-specific Settings Configuración específica de la plataforma The database was exported from a different platform. Please verify or update the following settings. You can leave fields empty and configure them later in Settings. La base de datos se exportó desde una plataforma diferente. Verifique o actualice la siguiente configuración. Puede dejar los campos vacíos y configurarlos más tarde en Configuración. Setting Ajustes Value Valor Continue Continuar Select File Seleccionar Archivo All Files (*) Todos los archivos (*) ProfileImageWidget Profile Image Imágen de Perfil QCoreApplication QLog Help Ayuda de QLog QMessageBox QLog Critical QLog Crítico Cannot save a password for %1 to the Credential Store No se puede guardar una contraseña para %1 en el almacén de credenciales Cannot get a password for %1 from the Credential Store No se puede obtener una contraseña para %1 del almacén de credenciales QLog Warning Alerta de QLog Club List Update failed. Cannot remove old records Error al actualizar la lista de clubes. No se pueden eliminar registros antiguos Club List Update failed. Cannot plan new downloads Error al actualizar la lista de clubes. No se pueden planificar nuevas descargas Unexpected Club List download. Canceling next downloads Descarga inesperada de la lista de clubes. Cancelar próximas descargas Unexpected Club List content for Contenido inesperado de la lista de clubes para Network error. Cannot download Club List for Error de red. No se puede descargar la lista de clubes para QLog Error Error de QLog QLog is already running QLog ya se está ejecutando Failed to process pending database import. No se pudo procesar la importación pendiente de la base de datos. The database was imported successfully, but the stored passwords could not be restored (decryption failed or the data is corrupted). All service passwords have been cleared and must be re-entered in Settings. La base de datos se importó correctamente, pero no se pudieron restaurar las contraseñas almacenadas (falló la descifrado o los datos están corruptos). Todas las contraseñas de los servicios se han borrado y deben volver a introducirse en Configuración. Could not connect to database. No se pudo conectar a la base de datos. Could not export a QLog database to ADIF as a backup.<p>Try to export your log to ADIF manually No se pudo exportar una base de datos QLog a ADIF como copia de seguridad.<p>Intente exportar su registro a ADIF manualmente Database migration failed. Error en la migración de la base de datos. QLog Info Información de Qlog Activity name is already exists. El nombre de la actividad ya existe. Rule name is already exists. El nombre de la regla ya existe. Callsign Regular Expression is incorrect. La expresión regular del Indicativo es incorrecta. Comment Regular Expression is incorrect. La expresión regular del Comentario de llamada es incorrecta. Cannot Update Alert Rules No se pueden actualizar las reglas de alerta DXC Server Name Error Error en el nombre del servidor del Cluster DX DXC Server address must be in format<p><b>[username@]hostname:port</b> (ex. hamqth.com:7300)</p> La dirección del servidor del Cluster DX debe tener el formato<p><b>[nombre de usuario@]nombre de host:puerto</b> (ej. hamqth.com:7300)</p> DX Cluster Password Contraseña del Cluster DX Invalid Password Contraseña incorrecta DXC Server Connection Error Error en la conexión del servidor del Cluster DX Filename is empty El nombre del archivo está vacío Cannot write to the file No se puede escribir el archivo QLog Information Información de QLog Exported. Exportado. Exported %n contact(s). Exportado: %n contacto/s. Chat Error: Error del Chat: Filter name is already exists. El nombre del filtro ya existe. <b>Rig Error:</b> <b>Error del Equipo:</b> <b>Rotator Error:</b> <b>Error del Rotor:</b> <b>CW Keyer Error:</b> <b>Error del Manipulador:</b> The fields <b>%0</b> will not be saved because the <b>%1</b> is not filled. Los campos <b>%0</b> no se guardarán porque <b>%1</b> no está completado. Your callsign is empty. Please, set your Station Profile Tu indicativo está vacío. Por favor, configure su Perfil de Estación Please, define at least one Station Locations Profile Por favor, defina al menos un Perfil de Ubicaciones de Estaciones WSJTX Multicast is enabled but the Address is not a multicast address. WSJTX Multicast está habilitada pero la dirección no es una dirección de multidifusión. Loop detected. Raw UDP forward uses the same port as the WSJT-X receiving port. Bucle detectado. El reenvío UDP en bruto usa el mismo puerto que el puerto de recepción de WSJT-X. Rig port must be a valid COM port.<br>For Windows use COMxx, for unix-like OS use a path to device El puerto de la Radio debe ser un puerto COM válido.<br>Para Windows use COMxx, para sistemas operativos tipo Unix use una ruta al dispositivo Rig PTT port must be a valid COM port.<br>For Windows use COMxx, for unix-like OS use a path to device El puerto de control del PTT debe ser un puerto COM válido.<br>Para Windows utilice COMxx, para SO tipo unix utilice una ruta al dispositivo <b>TX Range</b>: Max Frequency must not be 0. <b>Rango TX</b>: la frecuencia máxima no debe ser 0. <b>TX Range</b>: Max Frequency must not be under Min Frequency. <b>Rango TX</b>: la frecuencia máxima no debe estar por debajo de la frecuencia mínima. Rotator port must be a valid COM port.<br>For Windows use COMxx, for unix-like OS use a path to device El puerto del Rotor debe ser un puerto COM válido.<br>Para Windows use COMxx, para sistemas operativos tipo Unix use una ruta al dispositivo CW Keyer port must be a valid COM port.<br>For Windows use COMxx, for unix-like OS use a path to device El puerto del Manipulador debe ser un puerto COM válido.<br>Para Windows use COMxx, para sistemas operativos tipo Unix use una ruta al dispositivo Cannot change the CW Keyer Model to <b>Morse over CAT</b><br>No Morse over CAT support for Rig(s) <b>%1</b> No se puede cambiar el modelo de Manipulador a <b>Morse sobre CAT</b><br>No se admite Morse sobre CAT para la/s Radio/s <b>%1</b> Cannot delete the CW Keyer Profile<br>The CW Key Profile is used by Rig(s): <b>%1</b> No se puede eliminar el Perfil del Manipulador<br>El Perfil del Manipulador es utilizado por la/s radio/s: <b>%1</b> Callsign has an invalid format El Indicativo tiene un formato no válido Operator Callsign has an invalid format El indicativo del operador tiene un formato no válido Gridsquare has an invalid format La cuadrícula no es válida en ese formato. El Locator tiene un formato no válido VUCC Grids have an invalid format (must be 2 or 4 Gridsquares separated by ',') El Locator VUCC tiene un formato no válido (debe se 2 o 4 dígitos separadas por ',') Country must not be empty El País no debe estar vacío CQZ must not be empty La Zona CQ no debe estar vacía ITU must not be empty ITU no debe estar vacío Cannot update QSO Filter Conditions No se pueden actualizar las condiciones del filtro QSO QObject km km miles millas Connection Refused Conexión Denegada Host closed the connection El host cerró la conexión Host not found Host no encontrado Timeout Se agotó el tiempo de espera Network Error Error de red Internal Error Error Interno Importing Database Importando base de datos Opening Database Abriendo Base de Datos Backuping Database Respaldando Base de Datos Migrating Database Migrando Base de Datos Starting Application Iniciando Aplicación My Rig Mi Radio Logging Station Callsign Indicativo de la Estación My Gridsquare Mi Locator Operator Name Nombre del operador Operator Callsign Indicativo del operador My City Mi Ciudad My Country Mi País My County Mi Condado My IOTA Mi IOTA My SOTA Mi SOTA My Special Interest Activity Mi Actividad de Interés Especial My Spec. Interes Activity Info Mi Info de Actividad de Interés Especial My VUCC Grids Mi Locator VUCC My WWFF Mi WWFF My POTA Ref Mi Ref POTA My DARC DOK Mi DARC DOK My ITU MI ITU My CQZ Mi Zona CQ My DXCC Mi DXCC Cannot connect to DXC Server <p>Reason <b>: No se puede conectar al servidor del Cluster DX <p>Razón <b>: <b>Imported</b>: %n contact(s) <b>Importado</b>: %n contacto/s <b>Warning(s)</b>: %n <b>Alerta/s</b>: %n <b>Error(s)</b>: %n <b>Error/es</b>: %n Not a valid QLog database No es una base de datos QLog válida Database version too new (requires newer QLog version) La versión de la base de datos es demasiado nueva (requiere una versión más reciente de QLog) Database is not QLog Export file La base de datos no es un archivo de exportación de QLog TQSL Path Ruta de TQSL (Flatpak internal path) (Ruta interna de Flatpak) Rig Rig PTT Rig rigctld Rotator Rotor CW Keyer Generador CW TOTAL Worked TOTAL Trabajado TOTAL Confirmed TOTAL Confirmado Confirmed Confirmado Worked Trabajado QRZCallbook QRZ.com QRZ.com QRZUploader General Error Error General QSLGalleryDialog QSL Card Gallery Galería de tarjetas QSL Search by callsign... Buscar por indicativo… Sort by: Ordenar por: Export Filtered Exportar filtrados Date (Newest) Fecha (más reciente) Date (Oldest) Fecha (más antigua) Callsign (A-Z) Indicativo (A-Z) Callsign (Z-A) Indicativo (Z-A) %n QSL card(s) %n tarjeta(s) QSL %n tarjeta(s) QSL All QSL Cards Todas las tarjetas QSL Favorites Favoritos By Country Por país By Date Por fecha By Band Por banda By Mode Por modo By Continent Por continente Remove from Favorites Quitar de favoritos Add to Favorites Añadir a favoritos Open Abrir Save... Guardar… Save QSL Card Guardar tarjeta QSL Export QSL Cards Exportar tarjetas QSL Exported %1 of %2 cards Exportadas %1 de %2 tarjetas QSLImportStatDialog QSL Import Summary Resumen de Importación de QSL Summary Resumen Downloaded: Descargadas: Updated: Actualizadas: Unmatched: Incomparables: Errors: Errores: Details Detalles New QSLs: QSLs Nuevas: Updated QSOs: QSOs actualizados: Unmatched QSLs: QSLs Incomparables: QSLPrintLabelDialog Print QSL Labels Imprimir etiquetas QSL Filter Filtrar Date Range Rango de Fechas My Callsign Mi Indicativo Station Profile Perfil de estación QSL Sent QSL Enviada User Filter Filtro de usuario Label Template Plantilla de etiqueta Page Size: Tamaño de página: Columns: Columna: Rows: Fila: Label Width: Ancho de la etiqueta: Label Height: Altura de la etiqueta: Left Margin: Margen izquierdo: Top Margin: Margen superior: H Spacing: Espaciado horizontal: V Spacing: Espaciado vertical: Label Appearance Apariencia de la etiqueta Print Label Borders Imprimir bordes de etiquetas QSOs per Label: QSOs por etiqueta: Footer Left Text: Texto de pie de página izquierdo: Footer Right Text: Texto de pie de página derecho: Skip Label: Omitir etiqueta: Sans Font: Fuente sans-serif: Mono Font: Fuente monoespaciada: Callsign Size: Tamaño del indicativo: "To Radio" Size: Tamaño de «To Radio»: "To Radio" Text: Texto «To Radio»: Header Size: Tamaño del encabezado: Data Size: Tamaño de datos: Date Header Text: Texto del encabezado de fecha: Date Format: Formato de fecha: Time Header Text: Texto del encabezado de hora: Band Header Text: Texto del encabezado de banda: Mode Header Text: Texto del encabezado de modo: QSL Header Text: Texto del encabezado QSL: Extra Column: Columna adicional: Extra Column Text Texto de la columna adicional (DB column name) (nombre de columna de BD) No matching QSOs found No se encontraron QSOs coincidentes Page 0 of 0 Página 0 de 0 Labels: 0 (0 pages) Etiquetas: 0 (0 páginas) Print Imprimir Export as PDF Exportar como PDF Custom Personalizado Empty Vacío QSOs matching this station profile QSOs que coinciden con este perfil de estación Labels: %1 (%2 pages) Etiquetas: %1 (%2 páginas) Page %1 of %2 Página %1 de %2 Export PDF Exportar PDF PDF Files (*.pdf) Archivos PDF (*.pdf) Mark as Sent Marcar como enviado Mark printed/exported QSOs as sent? ¿Marcar QSOs impresos/exportados como enviados? QSODetailDialog dd/MM/yyyy HH:mm:ss RSTs RSTe RSTr RSTr Mode Modo Time On Comienzo Time Off Término Callsign Indicativo TX: TX: Blank Vacío MHz MHz RX: RX: Frequency Frecuencia Band Banda QTH QTH Name Nombre Gridsquare Locator Comment Comentario My Notes Mis Notas about:blank &Details &Detalles Country País Cont Cont ITU ITU CQ CQ State Estado County Condado Age Edad AF AF AN AN AS AS EU EU NA NA OC OC SA SA VUCC VUCC two or four adjacent Gridsquares, each four characters long, (ex. EN98,FM08,EM97,FM07) Locator VUCC (Opcional). Ej. EN98,FM08,EM97,FM07 FISTS FISTS SKCC SKCC Ten-Ten Ten-Ten UKSMG UKSMG DOK DOK the contacted station's DARC DOK (District Location Code) (ex. A01) la estación DARC DOK (District Location Code) contactada (ej. A01) IOTA IOTA POTA POTA SOTA SOTA WWFF WWFF SIG SIG Info FISTS CC CC FISTS E-Mail E-Mail URL URL Propagation Mode Modo de Propagación Satellite Name Nombre del Satélite Satellite Mode Modo del Satélite M&y Station &Mi Estación Operator Name Nombre Operador Operator Callsign Indicativo del operador Rig Radio Antenna Antena Power Potencia W W QSLr Message QSLs Message Mensaje del QSL Contest ID ID del Concurso Sig Info D&X Stats Estadísticas D&X <b>DXCC Statistics</b> <b>Estadísticas DXCC</b> <b>Station Statistics</b> <b>Estadísticas de la estación</b> &QSL &QSL Sent Enviada - Manage QSL Card Administrar QSL QSL Sent via Enviar QSL vía <b>Yes</b> - an outgoing QSL card has been sent; the QSO has been uploaded to, and accepted by, the online service<br/><b>No</b> - do not send an outgoing QSL card; do not upload the QSO to the online service<br/><b>Requested</b> - the contacted station has requested a QSL card; the contacted station has requested the QSO be uploaded to the online service<br/><b>Queued</b> - an outgoing QSL card has been selected to be sent; a QSO has been selected to be uploaded to the online service<br/> <b>Sí</b>: se ha enviado una tarjeta QSL; el QSO ha sido cargado y aceptado por el servicio en línea<br/><b>No</b> - no envié una tarjeta QSL; no cargué el QSO al servicio en línea<br/><b>Solicitado</b> - la estación contactada ha solicitado una tarjeta QSL; la estación contactada ha solicitado que el QSO se cargue en el servicio en línea<br/><b>En cola</b> - se ha seleccionado una tarjeta QSL para enviar; Se ha seleccionado un QSO para subir al servicio online<br/> Paper Papel Show QSL Card Mostrar QSL <b>Yes</b> - an incoming QSL card has been received; the QSO has been confirmed by the online service<br/><b>No</b> - an incoming QSL card has not been received; the QSO has not been confirmed by the online service<br/><b>Requested</b> - the logging station has requested a QSL card; the logging station has requested the QSO be uploaded to the online service<br/> <b>Sí</b>: se ha recibido una tarjeta QSL; el QSO ha sido confirmado por el servicio en línea<br/><b>No</b> - no se ha recibido una tarjeta QSL; el QSO no ha sido confirmado por el servicio en línea<br/><b>Solicitado</b> - la estación de registro ha solicitado una tarjeta QSL; la estación de registro ha solicitado que el QSO se cargue en el servicio online<br/> Received Recibida QSL via QSL vía LoTW LoTW eQSL eQSL &Contest &Concurso RcvNr Número Recibido RcvExch Intercambio Recibido SendNr Número Enviado SendExch Intercambio Enviado Member: Miembro: &Reset &Reiniciar &Lookup &Buscar No No Yes Requested Solicitado Queued En Cola Ignored Ignorado Bureau Bureau Direct Directa Electronic Electrónica Submit changes Grabar cambios Really submit all changes? Realmente desea grabar todos los cambios? QLog Error Error de QLog Cannot save all changes - internal error No se pueden guardar todos los cambios - Error interno Cannot save all changes - try to reset all changes No se pueden guardar todos los cambios - intente reestablecer todos los cambios QSO Detail Detalle del QSO Edit QSO Editar QSO Downloading eQSL Image Descargando Imágen de eQSL Cancel Cancelar eQSL Download Image failed: La descarga de imágen de eQSL ha fallado: DX Callsign must not be empty El indicativo del DX no puede estar vacío DX callsign has an incorrect format El indicativo del DX tiene formato incorrecto TX Frequency or Band must be filled La Frecuencia o Bande de TX debe estar completada TX Band should be La Banda TX debe ser RX Band should be La Banda RX debe ser DX Grid has an incorrect format El Locator del DX tiene formato incorrecto Based on callsign, DXCC Country is different from the entered value - expecting Según el indicativo, el País es diferente del valor ingresado; se esperaba Based on callsign, DXCC Continent is different from the entered value - expecting Según el indicativo, el Continente es diferente del valor ingresado; se esperaba Based on callsign, DXCC ITU is different from the entered value - expecting Según el indicativo, la Zona ITU es diferente del valor ingresado; se esperaba Based on callsign, DXCC CQZ is different from the entered value - expecting Según el indicativo, la Zona CQ es diferente del valor ingresado; se esperaba VUCC has an incorrect format VUCC tiene formato incorrecto Based on Frequencies, Sat Mode should be Según las frecuencias, el modo Satélite debe ser blank vacío Sat name must not be empty El nombre del Satélite no debe estar vacío Own Callsign must not be empty El Indicativo propio no puede estar vacío Own callsign has an incorrect format El Indicativo propio tiene formato incorrecto Own VUCC Grids have an incorrect format El Locator VUCC propio tiene formato incorrecto Based on own callsign, own DXCC ITU is different from the entered value - expecting Según el indicativo propio, la Zona ITU propia es diferente del valor ingresado; se esperaba Based on own callsign, own DXCC CQZ is different from the entered value - expecting Según el indicativo propio, la Zona CQ propia es diferente del valor ingresado; se esperaba Based on own callsign, own DXCC Country is different from the entered value - expecting Según el indicativo propio, el País propio es diferente del valor ingresado; se esperaba Based on SOTA Summit, QTH does not match SOTA Summit Name - expecting Según la Cumbre SOTA, el QTH no coincide con el Nombre de la Cumbre SOTA - se esperaba Based on SOTA Summit, Grid does not match SOTA Grid - expecting Según la Cumbre SOTA, el Locator no coincide con el Locator SOTA - se esperaba Based on POTA record, QTH does not match POTA Name - expecting Según los registros POTA, el QTH no coincide con el Nombre POTA - se esperaba Based on POTA record, Grid does not match POTA Grid - expecting Según los registros POTA, el Locator no coincide con el Locator POTA - se esperaba Based on SOTA Summit, my QTH does not match SOTA Summit Name - expecting Según la Cumbre SOTA, mi QTH no coincide con el Nombre de la Cumbre SOTA - se esperaba Based on SOTA Summit, my Grid does not match SOTA Grid - expecting Según la Cumbre SOTA, mi Locator no coincide con el Locator SOTA - se esperaba Based on POTA record, my QTH does not match POTA Name - expecting Según los registros POTA, mi QTH no coincide con el Nombre POTA - se esperaba Based on POTA record, my Grid does not match POTA Grid - expecting Según los registros POTA, mi Locator no coincide con el Locator POTA - se esperaba LoTW Sent Status to <b>No</b> does not make any sense if QSL Sent Date is set. Set Date to 1.1.1900 to leave the date field blank El estado de envío de LoTW en <b>No</b> no tiene ningún sentido si se establece la fecha de envío de QSL. Establezca la fecha en 1.1.1900 para dejar el campo de fecha en blanco Date should be present for LoTW Sent Status <b>Yes</b> La fecha debe estar presente para el estado de envío de LotW <b>Sí</b> eQSL Sent Status to <b>No</b> does not make any sense if QSL Sent Date is set. Set Date to 1.1.1900 to leave the date field blank El estado de envío de eQSL en <b>No</b> no tiene ningún sentido si se establece la fecha de envío de QSL. Establezca la fecha en 1.1.1900 para dejar el campo de fecha en blanco Date should be present for eQSL Sent Status <b>Yes</b> La fecha debe estar presente para el estado de envío de eQSL <b>Sí</b> Paper Sent Status to <b>No</b> does not make any sense if QSL Sent Date is set. Set Date to 1.1.1900 to leave the date field blank El estado de envío de QSL Papel en <b>No</b> no tiene ningún sentido si se establece la fecha de envío de QSL. Establezca la fecha en 1.1.1900 para dejar el campo de fecha en blanco Date should be present for Paper Sent Status <b>Yes</b> La fecha debe estar presente para el estado de envío de Papel <b>Sí</b> Callbook error: Error del Libro de Guardia: <b>Warning: </b> <b>Alerta: </b> Validation Validación Yellow marked fields are invalid.<p>Nevertheless, save the changes?</p> Los campos marcados en amarillo no son válidos.<p>¿Guardar los cambios igualmente? &Save &Guardar &Edit &Editar QSOFilterDetail QSO Filter Detail Detalle de Filtros de QSO Filter Name: Nombre del Filtro Find QSO which match Buscar QSO que coincida con All the following conditions Todas las siguientes condiciones Any of the following conditions Cualquiera de las siguientes condiciones Add Condition Agregar Condición Equal Igual Not Equal No es Igual Contains Contiene Not Contains No Contiene Greater Than Mayor que Less Than Menor que Starts with Comience con RegExp Remove Quitar Must not be empty No debe estar vacío QSOFilterDialog QSO Filters Filtros de QSO Filters Filtros Add Agregar Edit Editar Remove Quitar Rig No Rig Profile selected No Hay Perfil de Radio Seleccionado Rigctld Error Error de Rigctld Initialization Error Error al Inicializar Internal Error Error Interno Cannot open Rig No se puede abrir la Radio RigWidget Form Radio RX Disconnected Desconectado MHz MHz Disable Split Desactivar split RIT: 0.00000 MHz XIT: 0.00000 MHz PWR: %1W RigctldAdvancedDialog Rigctld Advanced Settings Configuración avanzada de Rigctld Rigctld Path: Ruta de Rigctld: Leave empty for auto-detection Dejar vacío para detección automática Browse Navegar Auto-detect Rigctld path Detectar automáticamente la ruta de Rigctld Auto-Detect Detección automática Rigctld Version: Versión de Rigctld: Additional Arguments: Argumentos adicionales: e.g. -v -v for verbose logging p. ej. -v -v para registro detallado Cannot be changed No se puede cambiar Auto Detect Detección automática rigctld was not found on this system. Please install Hamlib or specify the path manually. rigctld no se encontró en este sistema. Instale Hamlib o especifique la ruta manualmente. Executable (*.exe);;All files (*.*) Ejecutable (*.exe);;Todos los archivos (*.*) All files (*) Todos los archivos (*) Select rigctld executable Seleccionar ejecutable de rigctld Not found No encontrado RigctldManager rigctld executable not found in /app/bin/. This should not happen in Flatpak build. No se encontró el ejecutable de rigctld en /app/bin/. Esto no debería ocurrir en la versión Flatpak. rigctld executable not found. Please install Hamlib or specify the path in Advanced settings. No se encontró el ejecutable de rigctld. Instale Hamlib o especifique la ruta en la configuración avanzada. Hamlib major version mismatch: QLog was compiled with Hamlib %1 but rigctld reports version %2.%3.%4. Rig model IDs are incompatible between major versions. Incompatibilidad de versión principal de Hamlib: QLog se compiló con Hamlib %1, pero rigctld informa la versión %2.%3.%4. Los ID de modelo de transceptor no son compatibles entre versiones principales. Port %1 is already in use. Another rigctld or application may be running on this port. El puerto %1 ya está en uso. Otro rigctld o aplicación puede estar ejecutándose en este puerto. rigctld started but not responding on port %1. rigctld se inició pero no responde en el puerto %1. Failed to start rigctld: %1 %2 No se pudo iniciar rigctld: %1 %2 rigctld crashed. rigctld se bloqueó. rigctld timed out. rigctld agotó el tiempo de espera. Write error with rigctld. Error de escritura con rigctld. Read error with rigctld. Error de lectura con rigctld. Unknown rigctld error. Error desconocido de rigctld. Rotator No Rotator Profile selected No Hay Perfil de Rotor Seleccionado Initialization Error Error al Inicializar Internal Error Error Interno Cannot open Rotator No se puede abrir el Rotor RotatorWidget Form Az: Az: ° Goto Ir a Previous Button Profile Perfil de botón anterior Next Button Profile Siguiente perfil de botón QSO LP QSO PL QSO Long Path QSO Paso Largo QSO SP QSO PC QSO Short Path QSO Paso Corto SettingsDialog Settings Ajustes Station Estación Profiles Perfiles World Wide Flora & Fauna (Optional parameter) World Wide Flora & Fauna (Opcional) POTA POTA SIG SIG Station Gridsquare (Mandatory parameter) Locator de la Estación (Obligatorio) Operator Name Nombre del Operador SOTA SOTA QTH QTH SIG Information (Optional parameter) Información de Grupo de Actividades Especiales (Opcional) WWFF WWFF IOTA (Optional parameter) IOTA (Opcional) Callsign Indicativo SOTA (Optional parameter) SOTA (Opcional) Profile name that is used as the alias for the Callsign, Gridsquare, Operator name, and QTH (required parameter) Nombre de perfil que se utiliza como alias para Indicativo, Locator, Nombre del operador y QTH (obligatorio) List of all available Station Profiles Listado de los Perfiles de Estación disponibles Gridsquare Locator IOTA IOTA Profile Name Nombre del Perfil VUCC VUCC SIG (Optional parameter). Grupo de Actividades Especiales (Opcional) VUCC Grids (Optional parameter). Ex. EN98,FM08,EM97,FM07 Locator VUCC (Opcional). Ej. EN98,FM08,EM97,FM07 SIG Info SIG Info Add Agregar Delete Eliminar QTH Name (Optional parameter) Nombre de QTH (Opcional) Operator name (Optional parameter) Nombre del operador (Opcional) Callsign (Mandatory parameter) Indicativo (Obligatorio) ITU ITU CQZ Zona CQ Operator Callsign Indicativo del operador Country País Station Callsign Indicativo de la estación Callsign of operator (Optional parameter, if different from station callsign) Indicativo del operador (parámetro opcional, si es diferente del indicativo de la estación) County Condado Station County Location (Optional parameter) Ubicación del condado de la estación (parámetro opcional) DOK DOK Equipment Equipo Antennas Antenas Profiles Perfiles Description Descripción Azimuth Beamwidth Apertura de Haz en Azimut Azimuth Offset Compensación de Azimut Valid range value is 0° - 100° (0° Unspecified) El valor de rango válido es 0° - 100° (0° sin definir) Unspecified Sin definir ° List of all available Antennas Lista de Antenas disponibles CW Keyers Manipuladores Keyer Profiles Perfiles de Manipulador List of all available CW Keyers Lista de Manipuladores disponibles Model Modelo Keyer Mode Modo del Manipulador Swap Paddles Intercambiar paletas Paddle Only Sidetone Sidetone Freq: Frecuencia del tono de retorno: Default Speed Velocidad por Defecto N/A S/D WPM PPM Port Puerto Use COMxx for Window or path to COM port under Unix-like OS Utilice COMxx para Windows o la ruta al puerto COM en un sistema operativo tipo Unix Baudrate Velocidad de Baudios 115200 57600 38400 19200 9600 4800 2400 1200 Host Name Nombre del Host HamLib does not support to change a destination port. HamLib no admite cambiar un puerto de destino. CW Shortcut Profiles Perfiles de Atajos de CW List of all available CW Shortcuts Profiles Listado de los Perfiles de Atajos disponibles F1 Short Desciption of the Button (up to 7 chars) Breve descripción del Botón (hasta 7 caracteres) F2 F3 F4 F5 F6 F7 Rigs Radios List of all available Rigs Lista de Equipos disponibles Interface Interface Minimum and maximum TX frequencies. Specific ranges are derived from allowed Band in the Setting. Frecuencias de TX mínima y máxima. Los rangos específicos se derivan de la Banda permitida en Ajustes. TX Range Rango TX - - Offsets Compensaciones Enter manually RIT or Transverter Offset Ingrese manualmente RIT o Transverter Offset MHz MHz Enter manually XIT or Transverter offset Ingrese manualmente XIT o Transverter Offset TX: TX: Default PWR Potencia por Defecto Enter default PWR (ex. when Rig is disconnected) Ingresar la potencia por defecto (ej. cuando la radio está desconectada) Blank Vacío W W Assigned CW Keyer Manipulador Asignado Rig Features Características de la Radio Mode Modo VFO VFO Freq Frecuencia QSY Wiping Limpieza de QSY Power Potencia PTT State Estado del PTT TX Offset (XIT) Comp. TX (XIT) RX Offset (RIT) Comp. RX (RIT) CW Keyer Speed Vel. Manipulador CW Speed Sync Sinc. de Velocidad CW Start rigctld daemon to share rig with other applications (e.g. WSJT-X) Iniciar el demonio rigctld para compartir el transceptor con otras aplicaciones (p. ej., WSJT-X) Share Rig via port Compartir transceptor mediante puerto Advanced... Avanzado… Poll Interval Tiempo de Consulta ms ms Port Type Tipo de Puerto Data Bits Bits de Datos Flow Control Control de Flujo Parity Paridad 8 7 6 5 Stop Bits Bits de Parada 1 2 PTT Type Tipo de PTT Tipo de PTT PTT Port Puerto para el PTT PTT Puerto DX Spots to Rig Anunciar DX a Radio Split CIV Addr CIV Addr Auto Automático RTS RTS DTR DTR Rotators Rotores Serial Serie Network Red User Buttons Profiles Perfiles de Botones de Usuario Button 1 Botón 1 Button 2 Botón 2 Button 3 Botón 3 Button 4 Botón 4 Callbook Callbook Query Order Órden de Consulta Primary Primario Secondary Secundario HamQTH Username Nombre de Usuario Password Contraseña QRZ.com Web Lookup Button Botón de búsqueda en la Web URL Specify the URL to use for quick search. The <DXCALL> macro will be replaced by the current callsign Especifique la URL que se utilizará para la búsqueda rápida. La macro <DXCALL> será reemplazada por el indicativo actual Test URL with your Callsign Probar la URL con su Indicativo Test Probar Clubs Clubs Active Lists Listas Activas Sync && QSL Sincronización y QSL ClubLog E-Mail QSOs are uploaded immediately Los QSOs serán subidos inmediatamente Danger Zone Zona de peligro <b>⚠ This is a danger zone. Proceed with caution, as actions performed here cannot be undone and may have a significant impact on your log.</b> <b>⚠ Esta es una zona de peligro. Proceda con precaución, ya que las acciones realizadas no se pueden deshacer y pueden tener un impacto significativo en su log.</b> Delete All QSOs Eliminar todos los QSOs Delete All Passwords from the Secure Store Eliminar todas las contraseñas del almacén seguro Immediately Upload Subir Inmediatamente eQSL HRDLog It is not a password. It is the upload code received via email after the registration to HRDLOG..net No es la contraseña. Es el código de carga recibido por correo electrónico después del registro en HRDLOG.net Upload Code Código de Carga If it is enabled and Rig is connected then QLog periodically sends On-Air messages to HRDLog Si está habilitado y la radio está conectada, QLog envía periódicamente mensajes al aire a HRDLog Send On-Air Enviar Al Aire LoTW TQSL Path Ruta de TQSL Browse Navegar Auto-detect TQSL path Detectar automáticamente la ruta de TQSL Auto-Detect Detección automática TQSL Version Versión de TQSL Using an internal TQSL instance Usando una instancia interna de TQSL Others Otros Status Confirmed By Confirmado por Paper Papel Chat Chat <b>Security Notice:</b> QLog stores all passwords in the Secure Storage. Unfortunately, ON4KST uses a protocol where this password is sent over an unsecured channel as plaintext.</p><p>Please exercise caution when choosing your password for this service, as your password is sent over an unsecured channel in plaintext form.</p> <b>Aviso de seguridad:</b> QLog almacena todas las contraseñas en el almacenamiento seguro. Desafortunadamente, ON4KST utiliza un protocolo en el que esta contraseña se envía a través de un canal no seguro como texto sin formato.</p><p>Tenga cuidado al elegir su contraseña para este servicio, ya que su contraseña se envía a través de un canal no seguro en formato de texto sin formato.< /p> Bands Bandas Modes Modos The '>' character is interpreted as a marker for the initial cursor position in the Report column. <br/>Ex.: '5>9' means the cursor will be positioned on the second character El carácter ">" se interpreta como un marcador de la posición inicial del cursor en la columna Report.<br/>Ej.: "5>9" significa que el cursor se colocará en el segundo carácter Color CQ Spots Colorear los spots CQ Enable/Disable sending color-coded status indicators back to WSJT-X for each callsign calling CQ Habilitar/deshabilitar el envío de indicadores de estado codificados por color de vuelta a WSJT-X para cada indicativo que llama CQ Rig Status Radio GUI GUI Time Format Formato de tiempo 24-hour 24 horas AM/PM AM/PM Unit System Sistema de unidades Metric Métrico Imperial Imperial Date Format Formato de fecha System Sistema Custom Personalizado <a href="https://doc.qt.io/qt-6/qdate.html#fromString-1">Time Format Documentation</a> <a href="https://doc.qt.io/qt-6/qdate.html#fromString-1">Documentación del formato de hora</a> DXCC <b>Notice:</b> At least a QRZ XML Subscription is recommended to access detailed information. Without a subscription, you will obtain limited data from QRZ, such as missing grid and other fields. <b>Aviso:</b> Se recomienda al menos una Suscripción QRZ XML para acceder a información detallada. Sin una suscripción, obtendrás datos limitados de QRZ. Default API Key Clave API predeterminada Callsign-specific API Keys Claves API específicas para indicativo Wavelog Wavelog API Key Clave API Endpoint Punto final Wsjtx Raw UDP Forward Reenvío UDP sin procesar <p>List of IP addresses to which QLog forwards raw UDP WSJT-X packets.</p>The IP addresses are separated by a space and have the form IP:PORT <p>Lista de direcciones IP a las que QLog reenvía paquetes UDP WSJT-X sin procesar.</p>Las direcciones IP están separadas por un espacio y tienen el formato IP:PUERTO ex. 192.168.1.1:1234 192.168.2.1:1234 ej. 192.168.1.1:1234 192.168.2.1:1234 Port Puerto Port where QLog listens an incoming traffic from WSJT-X Puerto donde QLog escucha el tráfico entrante de WSJT-X Join Multicast Unirse a Multidifusión Enable/Disable Multicast option for WSJTX Activar/desactivar la opción de multidifusión para WSJTX Multicast Address Dirección de multidifusión <DXCALL> = DX Callsign <NAME> = DX Operator Name <RST> = Report 599 <RSTN> = Report 5NN <GREETING> = Greenting GM/GA/GE <QTH> = QTH <MYCALL> = My Callsign <MYNAME> = My Name <MYQTH> = My QTH <MYLOCATOR> = My Gridsquare <MYGRID> = My Gridsquare <MYSIG> = My SIG <MYSIGINFO> = My SIG Information <MYIOTA> = My IOTA <MYSOTA> = MY SOTA <MYWWFT> = My WWFT <MYVUCC> = MY VUCC <MYPWR> = My Power in W <EXCHSTR> = Contest Exchange Message <EXCHNR> = Contest Exchange Serial Number <EXCHNRN> = Contest Exchange Serial Number (9→N, 0→T) <+> = Speed +5 WPM (<++> = +10 WPM, etc.) <-> = Speed -5 WPM (<--> = -10 WPM, etc.) <DXCALL> = Indicativo DX <NAME> = Nombre del operador DX <RST> = Reporte 599 <RSTN> = Reporte 5NN <GREETING> = Saludo GM/GA/GE <QTH> = QTH <MYCALL> = Mi Indicativo <MYNAME> = Mi Nombre <MYQTH> = Mi QTH <MYLOCATOR> = Mi Locator <MYGRID> = Mi Cuadrícula <MYSIG> = Mi Señal <MYSIGINFO> = Mi Información de la señal <MYIOTA> = Mi IOTA <MYSOTA> = Mi SOTA <MYWWFT> = Mi WWFT <MYVUCC> = Mi VUCC <MYPWR> = Mi Potencia en W <EXCHSTR> = Mensaje de intercambio del Concurso <EXCHNR> = Número de serie de intercambio del Concurso <EXCHNRN> = Número de serie de intercambio de Concursos (9→N, 0→T) <+> = Velocidad +5 WPM (<++> = +10 WPM, etc.) <-> = Velocidad -5 WPM (<--> = -10 WPM, etc.) RX: RX: Rig Port Leave empty for auto-detection Dejar vacío para detección automática Specify Multicast Address. <br>On some Linux systems it may be necessary to enable multicast on the loop-back network interface. Especifique la dirección de multidifusión. <br>En algunos sistemas Linux, puede ser necesario habilitar la multidifusión en la interfaz de red de bucle invertido. TTL TTL Time-To-Live determines the range<br> over which a multicast packet is propagated in your intranet. Time-To-Live determina el rango<br> sobre el cual se propaga un paquete de multidifusión en su intranet. Notifications Notificaciones LogID <p>Assigned LogID to the current log.</p>The LogID is sent in the Network Nofitication messages as a unique instance identified.<p> The ID is generated automatically and cannot be changed</> <p>LogID asignado al registro actual.</p>El LogID se envía en los mensajes de notificación de red como una instancia única identificada.<p>El ID se genera automáticamente y no se puede cambiar</> DX Spots Anuncios DX <p> List of IP addresses to which QLog sends UDP notification packets with DX Cluster Spots.</p>The IP addresses are separated by a space and have the form IP:PORT <p> Lista de direcciones IP a las que QLog envía paquetes de notificación UDP con anuncios de Cluster DX.</p> Las direcciones IP están separadas por un espacio y tienen la forma IP:PUERTO Spot Alerts Alertas de Anuncios <p> List of IP addresses to which QLog sends UDP notification packets about user Spot Alerts.</p>The IP addresses are separated by a space and have the form IP:PORT <p> Lista de direcciones IP a las que QLog envía paquetes de notificación UDP sobre Alertas de Anuncios de usuario.</p> Las direcciones IP están separadas por un espacio y tienen el formato IP:PUERTO QSO Changes Cambios de QSO <p> List of IP addresses to which QLog sends UDP notification packets about a new/updated/deleted QSO in the log.</p>The IP addresses are separated by a space and have the form IP:PORT <p> Lista de direcciones IP a las que QLog envía paquetes de notificación UDP sobre un QSO nuevo/actualizado/eliminado en el registro.</p>Las direcciones IP están separadas por un espacio y tienen el formato IP:PUERTO Wsjtx CQ Spots Anuncios CQ de Wsjtx <p> List of IP addresses to which QLog sends UDP notification packets with WSJTX CQ Spots.</p>The IP addresses are separated by a space and have the form IP:PORT <p> Lista de direcciones IP a las que QLog envía paquetes de notificación UDP con Anuncios de CQ de WSJTX.</p>Las direcciones IP están separadas por un espacio y tienen el formato IP:PUERTO <p> List of IP addresses to which QLog sends UDP notification packets when Rig State changes.</p>The IP addresses are separated by a space and have the form IP:PORT Special - Omnirig Especial - Omnirig Cannot be changed No se puede cambiar Name Nombre Report Informe State Estado Start (MHz) Inicio (MHz) End (MHz) Fin (MHz) SAT Mode Modo del Satélite Disabled Desactivado None Ninguno Hardware Hardware Software Software No No Even Par Odd Impar Mark Marca Space Espacio Dummy Morse Over CAT Morse Sobre CAT WinKey WinKey CWDaemon FLDigi Single Paddle Pala Simple IAMBIC A IAMBIC B Ultimate High High Low Low Press <b>Modify</b> to confirm the profile changes or <b>Cancel</b>. Presione <b>Modificar</b> para confirmar los cambios de perfil o <b>Cancelar</b>. Modify Modificar Must not be empty No debe estar vacío Select File Seleccionar Archivo Auto Detect Detección automática TQSL was not found on this system. Please install TQSL or specify the path manually. No se encontró TQSL en este sistema. Instale TQSL o especifique la ruta manualmente. Not found No encontrado Rig sharing is only available for Hamlib driver Compartir el transceptor solo está disponible para el controlador Hamlib Rig sharing is not available for network connection Compartir el transceptor no está disponible para conexiones de red Delete Passwords Eliminar contraseñas All passwords have been deleted Todas las contraseñas han sido eliminadas Deleting all QSOs... Eliminando todos los QSOs... Error Error Failed to delete all QSOs. No se pudieron eliminar todos los QSOs. members Miembros Required internet connection during application start Required internet connection during application start ShortcutEditorModel Description Descripción Shortcut Atajo Conflict with a built-in shortcut Conflicto con un atajo integrado Conflict with a user-defined shortcut Conflicto con un atajo definido por el usuario ShowUploadDialog QSOs to Upload QSOs a Subir Selected QSO QSO Seleccionados Upload Subir SmartSearchBox Search Búsqueda StatisticsWidget Statistics Estadísticas My Antenna Mi Antena My Gridsquare Mi Locator User Filter Filtro de usuario Confirmed by Confirmado por QSOs per QSOs por Percents Porcentajes Top 10 Top 10 Histogram Histrograma Show on Map Mostrar en el Mapa Graph Type Tipo de Gráfico to a My Rig Mi Radio My Callsign Mi Indicativo Band Banda Date Range Rango de Fechas LoTW LoTW eQSL eQSL Paper Papel Year Año Month Mes Day in Week Día de la Semana Hour Hora Mode Modo Continent Continente Propagation Mode Modo de Propagación Confirmed / Not Confirmed Confirmado / No Confirmado Countries Paises Big Gridsquares Grandes cuadrículas Distance Distancia QSOs QSOs Confirmed/Worked Grids Locators Confirmados/Trabajados ODX ODX Sun Dom Mon Lun Tue Mar Wed Mie Thu Jue Fri Vie Sat Sáb Not specified No Especificado Confirmed Confirmado Not Confirmed No Confirmado No User Filter Sin filtro de usuario Over 50000 QSOs. Display them? Más de 50.000 QSO. ¿Mostrarlo? Rendering QSOs... Renderizando los QSO… All Todos TCIRigDrv Rig 0 Radio 0 Rig 1 Radio 1 Rig 2 Radio 2 Rig 3 Radio 3 Error Occurred Se produjo un error Rig status changed El estado de la radio ha cambiado Rig is not connected Radio no conectada TimestampFormatDelegate Blank Vacío ToAllTableModel Time Hora Spotter Anunciante Message Mensaje UploadQSODialog Upload QSOs Subir QSOs eQSL eQSL LoTW LoTW QRZ.com QRZ.com Clublog Clublog HRDLog HRDLog Wavelog Wavelog Filters Filtros Station Profile Perfil de estación My Callsign Mi Indicativo Gridsquare Locator Include QSOs Status Incluir estado de QSOs Under normal circumstances this status means <b>"do not send"</b>.<br/>However, it may sometimes be wanted to ignore this setting when sending a QSL. En circunstancias normales, este estado significa <b>"no enviar"</b>.<br/>Sin embargo, a veces es posible que desee ignorar esta configuración al enviar una QSL. No No Under normal circumstances this status means <b>"Ignore/Invalid"</b>.<br/>However, it may sometimes be wanted to ignore this setting when sending a QSL. En circunstancias normales, este estado significa <b>"Ignorado/Inválido"</b>.<br/>Sin embargo, a veces es posible que desee ignorar esta configuración al enviar una QSL. Ignore Ignorar None Ninguno QSLs Message Mensaje del QSL Comment Comentario QSL Message Field Campo de mensaje QSL QTH Profile Perfil de QTH This option deletes all QSOs in the Clublog<br>and based on filter, it uploads all QSOs regardless of their status. Esta opción elimina todos los QSO en Clublog<br>y, según el filtro, carga todos los QSO independientemente de su estado. Clear Clublog and reupload QSOs Borrar Clublog y resubir los QSOs LoTW / TQSL LoTW / TQSL TQSL Station Location Ubicación de la estación TQSL Station Profile ID Perfil de estación Reupload All Volver a subir todo Show QSOs... Mostrar QSOs... &Upload &Subir Unspecified Sin definir Hide QSOs... Ocultar QSOs... Uploading to %1 Subiendo a %1 Cancel Cancelar QLog Warning - %1 Alerta de QLog - %1 Cannot update QSO Status No se puede actualizar el estado del QSO Cannot upload the QSO(s): No se puede subir el/los QSO/s: QLog Information Información de QLog No QSO found to upload. No se encontraron QSOs para subir. QSO(s) were uploaded to the selected services Los QSOs se cargaron en los servicios seleccionados Time Hora Callsign Indicativo Mode Modo Upload to Subir a The values below will be used when an input record does not contain the ADIF values Los valores siguientes se utilizarán cuando un registro de entrada no contenga los valores ADIF Any Cualquier Location callsign (%1) and grid (%2) do not match selected filters El indicativo de la ubicación (%1) y la cuadrícula (%2) no coinciden con los filtros seleccionados Location callsign (%1) does not match selected callsign (%2) El indicativo de la ubicación (%1) no coincide con el indicativo seleccionado (%2) Location grid (%1) does not match selected grid (%2) La cuadrícula de la ubicación (%1) no coincide con la cuadrícula seleccionada (%2) Unknown Desconocido Service is not configured properly.<p> Please, use <b>Settings</b> dialog to configure it.</p> UserListModel Callsign Indicativo Gridsquare Cuadrícula Distance Distancia Azimuth Azimut Comment Comentario WCYTableModel Time Hora K expK A R SFI SA GMF Au WWVTableModel Time Hora SFI A K Info Info WsjtxFilterDialog WSJTX Filters Filtros WSJTX General Filters Filtro Generales Log Status Estado del Registro New Band Nueva Banda New Mode Nuevo Modo New Entity Nueva Entidad New Slot Nuevo Slot Worked Trabajado Confirmed Confirmado Continent Continente North America Norteamérica Europe Europa South America Sudamérica Africa África Antarctica Antártida Asia Asia Oceania Oceanía Distance more than Distancia mayor a SNR better than SNR mejor a dB Extended Filters Filtros Extendidos Member Miembro No Club List is enabled No hay ninguna lista de clubes habilitada WsjtxTableModel Callsign Indicativo Gridsquare Cuadrícula Distance Distancia SNR Last Activity Última Actividad Last Message Último Mensaje Member Miembro WsjtxWidget Form Filtered Filtrado Column Visibility... Visibilidad de Columnas... Filter... Filtrar... Which columns should be displayed Qué columnas deben mostrarse Filter Spots Filtrar Anuncios main Run with the specific namespace. namespace Translation file - absolute or relative path and QM file name. path/QM-filename Set language. <code> example: 'en' or 'en_US'. Ignore environment setting. code Writes debug messages to the debug file Escribe mensajes de depuración en el archivo de depuración Process pending database import (internal use) Procesar importación pendiente de la base de datos (uso interno) Force update of all value lists (DXCC, SATs, etc.) Forzar actualización de todas las listas de valores (DXCC, SATs, etc.) ================================================ FILE: i18n/qlog_fr.ts ================================================ ActivityEditor Activity Editor Éditeur d'activité Activity Name Nom de l'activité Layout Mise en page Window Arrangement: Agencement des fenêtres : Saved Enregistré Clear Effacer Available Fields Champs disponibles List of fields that can be used Liste des champs utilisables Row A Ligne A Move selected fields from the Available Fields List to the Row A Déplacer les champs sélectionnés vers la Ligne A Remove selected field from the Row A Retirer le champ sélectionné de la Ligne A List of fields in the first variable row Liste des champs de la première ligne variable Change the order. Move selected field up Changer l'ordre. Monter le champ sélectionné Change the order. Move selected field down Changer l'ordre. Descendre le champ sélectionné Row B Ligne B Move selected fields from the Available Fields List to the Row B Déplacer les champs sélectionnés vers la Ligne B Remove selected field from the Row B Retirer le champ sélectionné de la Ligne B List of fields in the second variable row Liste des champs de la seconde ligne variable Column A Colonne A List of fields in the first QSO Detail Column Liste des champs de la première colonne de détail QSO Column B Colonne B List of fields in the second QSO Detail Column Liste des champs de la seconde colonne de détail QSO Column C Colonne C List of fields in the third QSO Detail Column Liste des champs de la troisième colonne de détail QSO New QSO Rows Lignes du nouveau QSO New QSO Detail Columns Colonnes de détail du nouveau QSO Values Valeurs Profiles Profils If unchecked, the profile does not change Si décoché, le profil ne change pas Station Station Antenna Antenne Rig Poste/Transceiver Connect automatically Connexion automatique Connect Connecter Rotator Rotor/Pointeur Fields Champs Must not be empty Ne doit pas être vide Unsaved Non enregistré AlertRuleDetail Alert Rule Detail Détail de la règle d'alerte Rule Name Nom de la règle Enabled Activé Sources Sources DX Cluster Cluster DX WSJTX WSJT-X DX DX Log Status Statut du Log Worked Contacté New Slot Nouveau créneau (Slot) Confirmed Confirmé New Entity Nouvelle Entité (DXCC) New Mode Nouveau Mode New Band Nouvelle Bande All Tous DX Callsign Indicatif DX Use Perl-like regular expression Utiliser une expression régulière (Perl) .* .* Country Pays ITU Zone ITU CQZ Zone CQ Spot Comment Commentaire du Spot Special Programs Diplômes & Programmes IOTA IOTA SOTA SOTA POTA POTA WWFF WWFF Modes Modes FTx (FT8/FT4) FTx (FT8/FT4) Digital Numérique Phone Phonie CW CW Bands Bandes Continent Continent South America Amérique du Sud Antarctica Antarctique North America Amérique du Nord Oceania Océanie Asia Asie Europe Europe Africa Afrique Member Membre de club Spotter Spotter (celui qui annonce) Must not be empty Ne doit pas être vide No Club List is enabled Aucune liste de club n'est activée AlertSettingDialog Alerts Rules Règles d'alertes Rules Règles Add Ajouter Edit Modifier Remove Supprimer Name Nom State État AlertTableModel Rule Name Nom de la règle Callsign Indicatif Frequency Fréquence Mode Mode Updated Mis à jour Last Update Dernière MAJ Last Comment Dernier commentaire Member Membre AlertWidget Alerts Alertes Clear older than Effacer plus ancien que Never Jamais min(s) min(s) Edit Rules Modifier les règles Column Visibility... Visibilité des colonnes... Clear Effacer AwardsDialog Awards Diplômes Options Options Award Diplôme My DXCC Entity Mon entité DXCC User Filter Filtre utilisateur Confirmed by Confirmé par LoTW LoTW eQSL eQSL Paper Papier (QSL) Mode Mode CW CW Phone Phonie Digi Numérique Show Afficher Not-Worked Only Non contactés seulement Not-Confirmed Only Non confirmés seulement DXCC DXCC ITU Zones ITU WAC WAC WAZ WAZ WAS WAS WPX WPX IOTA IOTA POTA Hunter Chasseur POTA POTA Activator Activateur POTA SOTA SOTA WWFF WWFF Gridsquare 2-Chars Locator (2 car.) Gridsquare 4-Chars Locator (4 car.) Gridsquare 6-Chars Locator (6 car.) Gridsquare %1-Chars Locator (%1 car.) US Counties Comtés US Russian Districts Districts russes (RDA) Japanese Cities/Ku/Guns Villes/Ku/Guns japonais (JCC/JCG) NZ Counties Comtés NZ Spanish DMEs DME espagnols Ukrainian Districts Districts ukrainiens (URDA) No User Filter Aucun filtre utilisateur DELETED SUPPRIMÉ North America Amérique du Nord South America Amérique du Sud Europe Europe Africa Afrique Oceania Océanie Asia Asie Antarctica Antarctique AwardsTableModel Slots: Slots : Confirmed Confirmé Worked Contacté Still Waiting En attente BandmapWidget Form Formulaire Create an additional Bandmap Window Créer une fenêtre Bandmap supplémentaire Clear older Effacer plus anciens Never Jamais min(s) min(s) Clear All Tout effacer Clear the current band Effacer la bande actuelle Bandmap Bandmap Show Band Afficher la bande Center RX Centrer sur RX Show Emergency Frequencies Montrer les fréquences d'urgence SOS SOS CWCatKey No Rig is connected Aucun poste connecté Rig does not support Morse over CAT Le poste ne supporte pas la CW via CAT Cannot send Text to Rig Impossible d'envoyer du texte au poste Keyer is not connected Le manipulateur (keyer) n'est pas connecté Rig is not connected Le poste n'est pas connecté Cannot set Keyer Speed Impossible de régler la vitesse du manipulateur Cannot stop Text Sending Impossible d'arrêter l'envoi du texte CWConsoleWidget Form Formulaire CW Keyer Profile Profil de manipulateur CW Shortcuts profile Profil de raccourcis Speed Vitesse N/A N/D WPM WPM (mots/min) Sent text Texte envoyé Echoed text Texte en écho &CW &CW Text to send Texte à envoyer Switch between sending <b>words</b> individually (separated by spaces)<br> and sending the entire text as a <b>whole</b> (separated by a new line). Alterner entre l'envoi par <b>mots</b> individuels (séparés par des espaces)<br> et l'envoi du texte <b>complet</b> (séparé par un saut de ligne). F1 F1 F2 F2 F3 F3 F4 F4 F5 F5 F6 F6 F7 F7 Immediately stop CW sending Arrêt immédiat de l'émission CW Halt Stop Clear Sent and Echo Console Effacer la console d'envoi et d'écho Clear Effacer CW Console - Halt Sending Console CW - Arrêt d'émission Rig must be connected Le poste doit être connecté Word Mot Whole Complet CWDaemonKey Keyer is not connected Le manipulateur n'est pas connecté Cannot send Text Impossible d'envoyer le texte Cannot set Keyer Speed Impossible de régler la vitesse Cannot stop Text Sending Impossible d'arrêter l'émission CWFldigiKey Connected device is not FLDigi L'appareil connecté n'est pas FLDigi Keyer is not connected Le manipulateur n'est pas connecté Cannot send Text to FLDigi Impossible d'envoyer le texte à FLDigi Cannot send the Clear command to FLDigi Impossible d'envoyer la commande Clear à FLDigi Cannot send the TX command to FLDigi Impossible d'envoyer la commande TX à FLDigi Cannot send the Text command to FLDigi Impossible d'envoyer la commande Text à FLDigi Cannot send the Stop command to FLDigi Impossible d'envoyer la commande Stop à FLDigi Cannot send the Abort command to FLDigi Impossible d'envoyer la commande Abort à FLDigi Cannot receive data from FLDigi Impossible de recevoir des données de FLDigi FLDigi connection timeout Délai de connexion à FLDigi dépassé FLDigi connection error Erreur de connexion à FLDigi FLDigi command error Erreur de commande FLDigi CWKeyer No CW Keyer Profile selected Aucun profil de manipulateur CW sélectionné Initialization Error Erreur d'initialisation Internal Error Erreur interne Connection Error Erreur de connexion Cannot open the Keyer connection Impossible d'ouvrir la connexion au manipulateur CWWinKey Connected device is not WinKey L'appareil connecté n'est pas un WinKey Cannot send Text to Rig Impossible d'envoyer le texte au poste Keyer is not connected Le manipulateur n'est pas connecté Communication Error Erreur de communication Cannot set Keyer Speed Impossible de régler la vitesse Cannot stop Text Sending Impossible d'arrêter l'émission 4000 Hz 4000 Hz 2000 Hz 2000 Hz 1333 Hz 1333 Hz 1000 Hz 1000 Hz 800 Hz 800 Hz 666 Hz 666 Hz 571 Hz 571 Hz 500 Hz 500 Hz 444 Hz 444 Hz 400 Hz 400 Hz CabrilloExportDialog Cabrillo Export Exporter Cabrillo File: Fichier : Browse Parcourir Contest Concours (Contest) Format Template: Modèle de format : Manage Gérer User Filter: Filtre utilisateur : Date/Time: Date/Heure: yyyy-MM-dd HH:mm - - UTC Station & Categories Station et catégories Callsign: Indicatif: Operators: Opérateurs : Band: Bande: Mode: Mode: Power: Puissance: Operator: Opérateur: Assisted: Assisté : Station: Station: Transmitter: Émetteur: Time: Heure: Overlay: Transmitter ID: Additional Supplémentaire Name: Nom: Email: Email: Address: Adresse: Max 6 lines Max. 6 lignes Gridsquare Locator Location: Emplacement: Club: Club: Soapbox: Commentaire libre: Off-Time: yyyy-mm-dd hhmm yyyy-mm-dd hhmm &Export &Exporter Cabrillo Files (*.log);;CBR Files (*.cbr);;All Files (*) Fichiers Cabrillo (*.log);;Fichiers CBR (*.cbr);;Tous les fichiers (*) QSO(s): %1 QSO(s) : %1 QSOs: ? QSOs: ? QLog Warning Avertissement QLog Please select a contest template. Veuillez sélectionner un modèle de concours. Please select an output file. Veuillez sélectionner un fichier de sortie. No callsign available. Check your filters. Aucun indicatif disponible. Vérifiez vos filtres. QLog Error Erreur QLog Failed to prepare export query. Impossible de préparer la requête d’exportation. Failed to query QSOs for export. Impossible de récupérer les QSO pour l’exportation. Cannot open file %1 for writing. Impossible d’ouvrir le fichier %1 en écriture. Exporting Cabrillo... Exportation Cabrillo… QLog Information Information QLog Exported %n QSO(s) to Cabrillo file. %n QSO exportés dans le fichier Cabrillo. CabrilloFormat All Bands 2m (144 MHz) 2m (144 MHz) 1.25m (222 MHz) 70cm (432 MHz) 70cm (432 MHz) 33cm (902 MHz) 33cm (902 MHz) 23cm (1.2 GHz) 23cm (1.2 GHz) Light Lumière VHF 3-Band VHF 3 bandes VHF FM Only VHF FM uniquement Digital Numérique Mixed Mixte High Haut (High) Low Bas (Low) QRP Single Operator Opérateur unique Multi Operator Multi-opérateurs Check Log Vérifier le log Non-Assisted Non assisté Assisted Assisté Fixed Fixe Mobile Mobile Portable Rover Rover Limited Rover Unlimited Expedition Expédition HQ School école Distributed Distribué One Un Two Deux Limited Limité Unlimited Illimité SWL SWL 6 Hours 6 heures 12 Hours 12 heures 24 Hours 24 heures Classic Classique Rookie TB Wires Novice/Tech Novice/Technicien Over 50 Plus de 50 Text (left-aligned) Texte (aligné à gauche) Frequency (kHz) Fréquence (kHz) Time (HHMM) Heure (HHMM) Date (YYYY-MM-DD) Date (AAAA-MM-JJ) RST Short (drop last digit) RST court (sans le dernier chiffre) Uppercase Majuscules Mode (Cabrillo) Mode (Cabrillo) Zero-Padded Nr. Numéro avec zéros Transmitter ID CabrilloTemplateDialog Cabrillo Template Manager Gestionnaire de modèles Cabrillo Import template Importer un modèle Import Importation Export template Exporter un modèle Export Exporter New template Nouveau modèle New Nouveau Copy existing template Copier un modèle existant Copy Copier Delete template Supprimer le modèle Delete Supprimer Template Name: Nom du modèle: Contest Name: Nom du concours: Default Mode: Mode par défaut: QSO Line Columns: Colonnes de ligne QSO: Contest name as required by the rules. It is possible to enter a custom string if it is not included in the list. Nom du concours selon les règles. Il est possible de saisir une chaîne personnalisée si elle ne figure pas dans la liste. Seq. QSO Field Champ QSO Formatter Formateur Width Largeur Label Étiquette Add line Ajouter une ligne Add Ajouter Remove selected line Supprimer la ligne sélectionnée Remove New Template Nouveau modèle Copy - %1 Copie – %1 Delete Template Supprimer le modèle Delete template '%1'? Supprimer le modèle «%1» ? Import Template Importer un modèle QLog Cabrillo Template (*.qct) Modèle Cabrillo QLog (*.qct) Import Failed Import échoué Export Template Exporter un modèle Export Failed Export échoué Failed to write file: %1 Impossible d’écrire le fichier : %1 File not found: %1 Fichier introuvable : %1 Cannot open file: %1 Impossible d’ouvrir le fichier : %1 Invalid template file: missing name Fichier de modèle invalide : nom manquant QLog Error Erreur QLog Cannot start database transaction. Impossible de démarrer la transaction de base de données. QLog Warning Avertissement QLog Cannot save template '%1': %2 Impossible d’enregistrer le modèle «%1» : %2 CallbookManager <p>The secondary callbook will be used</p> <p>La nomenclature (callbook) secondaire sera utilisée</p> ChatWidget ON4KST Chat Chat ON4KST Chat Room Salon de discussion Connect Connecter New Nouveau QLog Warning Avertissement QLog ON4KST Chat is not configured properly.<p> Please, use <b>Settings</b> dialog to configure it.</p> Le chat ON4KST n'est pas configuré correctement.<p> Veuillez utiliser le menu <b>Paramètres</b> pour le configurer.</p> CheckBoxDelegate Enabled Activé Disabled Désactivé ClockWidget Form Formulaire Sunrise Lever soleil Sunset Coucher soleil N/A N/D CloudlogUploader Invalid API Key Clé API invalide ClubLogUploader Clublog Operation for Callsign %1 failed.<br>%2 L'opération Clublog pour l'indicatif %1 a échoué.<br>%2 ColumnSettingDialog Column Visibility Setting Réglage de visibilité des colonnes General Général My Info Mes infos QSL && Callbooks QSL && Nomenclature Members Membres Conditionals Conditionnels Contests Concours (Contests) Others Autres Done Terminé ColumnSettingGenericDialog Unselect All Tout désélectionner Select All Tout sélectionner ColumnSettingSimpleDialog Column Visibility Setting Réglage de visibilité des colonnes Done Terminé DBSchemaMigration DXCC Entities Entités DXCC Sats Info Infos Satellites SOTA Summits Sommets SOTA WWFF Records Références WWFF IOTA Records Références IOTA POTA Records Références POTA Membership Directory Records Registres des listes de membres Clublog CTY.XML Fichier Clublog CTY.XML List of Values Liste de valeurs Updating Mise à jour de Update Failed Échec de la mise à jour DBStrings PHONE Phonie CW CW DIGITAL Numérique Afghanistan Afghanistan Agalega & St. Brandon Agalega & St. Brandon Aland Islands Îles d'Åland Alaska Alaska Albania Albanie Algeria Algérie American Samoa Samoa américaines Amsterdam & St. Paul Is. Is. Amsterdam & St. Paul Andaman & Nicobar Is. Is. Andaman & Nicobar Andorra Andorre Angola Angola Anguilla Anguilla Annobon Island Île d'Annobon Antarctica Antarctique Antigua & Barbuda Antigua & Barbuda Argentina Argentine Armenia Arménie Aruba Aruba Ascension Island Île de l'Ascension Asiatic Russia Russie asiatique Asiatic Turkey Turquie asiatique Austral Islands Îles Australes Australia Australie Austria Autriche Aves Island Île d'Aves Azerbaijan Azerbaïdjan Azores Açores Bahamas Bahamas Bahrain Bahreïn Baker & Howland Islands Îles Baker & Howland Balearic Islands Îles Baléares Banaba Island Île de Banaba Bangladesh Bangladesh Barbados Barbade Belarus Biélorussie Belgium Belgique Belize Belize Benin Bénin Bermuda Bermudes Bhutan Bhoutan Bolivia Bolivie Bonaire Bonaire Bosnia-Herzegovina Bosnie-Herzégovine Botswana Botswana Bouvet Bouvet Brazil Brésil British Virgin Islands Îles Vierges britanniques Brunei Darussalam Brunei Bulgaria Bulgarie Burkina Faso Burkina Faso Burundi Burundi Cambodia Cambodge Cameroon Cameroun Canada Canada Canary Islands Îles Canaries Cape Verde Cap-Vert Cayman Islands Îles Caïmans Central African Republic République centrafricaine Central Kiribati Kiribati Central Ceuta & Melilla Ceuta & Melilla Chad Tchad Chagos Islands Archipel des Chagos Chatham Islands Îles Chatham Chesterfield Islands Îles Chesterfield Chile Chili China Chine Christmas Island Île Christmas Clipperton Island Île de Clipperton Cocos (Keeling) Islands Îles Cocos (Keeling) Cocos Island Île de Cocos Colombia Colombie Comoros Comores Conway Reef Récif Conway Corsica Corse Costa Rica Costa Rica Cote d'Ivoire Côte d'Ivoire Crete Crète Croatia Croatie Crozet Island Île Crozet Cuba Cuba Curacao Curaçao Cyprus Chypre Czech Republic République tchèque DPR of Korea Corée du Nord Dem. Rep. of the Congo Rép. dém. du Congo Denmark Danemark Desecheo Island Île Desecheo Djibouti Djibouti Dodecanese Dodécanèse Dominica Dominique Dominican Republic République dominicaine Ducie Island Île Ducie East Malaysia Malaisie orientale Easter Island Île de Pâques Eastern Kiribati Kiribati oriental Ecuador Équateur Egypt Égypte El Salvador Salvador England Angleterre Equatorial Guinea Guinée équatoriale Eritrea Érythrée Estonia Estonie Ethiopia Éthiopie European Russia Russie européenne Falkland Islands Îles Falkland (Malouines) Faroe Islands Îles Féroé Fed. Rep. of Germany Rép. féd. d'Allemagne Fernando de Noronha Fernando de Noronha Fiji Fidji Finland Finlande France France Franz Josef Land Terre de François-Joseph French Guiana Guyane française French Polynesia Polynésie française Gabon Gabon Galapagos Islands Îles Galapagos Georgia Géorgie Ghana Ghana Gibraltar Gibraltar Glorioso Islands Îles Glorieuses Greece Grèce Greenland Groenland Grenada Grenade Guadeloupe Guadeloupe Guam Guam Guantanamo Bay Guantanamo Bay Guatemala Guatemala Guernsey Guernesey Guinea Guinée Guinea-Bissau Guinée-Bissau Guyana Guyana Haiti Haïti Hawaii Hawaï Heard Island Île Heard Honduras Honduras Hong Kong Hong Kong Hungary Hongrie ITU HQ Siège de l'UIT Iceland Islande India Inde Indonesia Indonésie Iran Iran Iraq Irak Ireland Irlande Isle of Man Île de Man Israel Israël Italy Italie Jamaica Jamaïque Jan Mayen Jan Mayen Japan Japon Jersey Jersey Johnston Island Île Johnston Jordan Jordanie Juan Fernandez Islands Îles Juan Fernández Juan de Nova & Europa Juan de Nova & Europa Kaliningrad Kaliningrad Kazakhstan Kazakhstan Kenya Kenya Kerguelen Islands Îles Kerguelen Kermadec Islands Îles Kermadec Kingdom of Eswatini Royaume d'Eswatini Kure Island Île Kure Kuwait Koweït Kyrgyzstan Kirghizistan Lakshadweep Islands Îles Lakshadweep Laos Laos Latvia Lettonie Lebanon Liban Lesotho Lesotho Liberia Liberia Libya Libye Liechtenstein Liechtenstein Lithuania Lituanie Lord Howe Island Île Lord Howe Luxembourg Luxembourg Macao Macao Macquarie Island Île Macquarie Madagascar Madagascar Madeira Islands Archipel de Madère Malawi Malawi Maldives Maldives Mali Mali Malpelo Island Île de Malpelo Malta Malte Mariana Islands Îles Mariannes Market Reef Récif Market Marquesas Islands Îles Marquises Marshall Islands Îles Marshall Martinique Martinique Mauritania Mauritanie Mauritius Maurice Mayotte Mayotte Mellish Reef Récif Mellish Mexico Mexique Micronesia Micronésie Midway Island Île Midway Minami Torishima Minami Torishima Moldova Moldavie Monaco Monaco Mongolia Mongolie Montenegro Monténégro Montserrat Montserrat Morocco Maroc Mount Athos Mont Athos Mozambique Mozambique Myanmar Myanmar (Birmanie) N.Z. Subantarctic Is. Is. subantarctiques de N.Z. Namibia Namibie Nauru Nauru Navassa Island Île de la Navasse Nepal Népal Netherlands Pays-Bas New Caledonia Nouvelle-Calédonie New Zealand Nouvelle-Zélande Nicaragua Nicaragua Niger Niger Nigeria Nigeria Niue Niue Norfolk Island Île Norfolk North Cook Islands Îles Cook du Nord North Macedonia Macédoine du Nord Northern Ireland Irlande du Nord Norway Norvège Ogasawara Ogasawara Oman Oman Pakistan Pakistan Palau Palaos Palestine Palestine Palmyra & Jarvis Islands Îles Palmyra & Jarvis Panama Panama Papua New Guinea Papouasie-Nouvelle-Guinée Paraguay Paraguay Peru Pérou Peter 1 Island Île Pierre 1er Philippines Philippines Pitcairn Island Île Pitcairn Poland Pologne Portugal Portugal Pr. Edward & Marion Is. Is. du Prince-Édouard & Marion Pratas Island Île Pratas Puerto Rico Porto Rico Qatar Qatar Republic of Korea République de Corée (Sud) Republic of Kosovo République du Kosovo Republic of South Sudan République du Soudan du Sud Republic of the Congo République du Congo Reunion Island Île de la Réunion Revillagigedo Revillagigedo Rodriguez Island Île Rodrigues Romania Roumanie Rotuma Island Île Rotuma Rwanda Rwanda Saba & St. Eustatius Saba & St. Eustache Sable Island Île de Sable Samoa Samoa San Andres & Providencia San Andrés & Providencia San Felix & San Ambrosio San Félix & San Ambrosio San Marino Saint-Marin Sao Tome & Principe Sao Tomé-et-Principe Sardinia Sardaigne Saudi Arabia Arabie saoudite Scarborough Reef Récif de Scarborough Scotland Écosse Senegal Sénégal Serbia Serbie Seychelles Seychelles Sierra Leone Sierra Leone Singapore Singapour Sint Maarten Saint-Martin (partie néerlandaise) Slovak Republic Slovaquie Slovenia Slovénie Solomon Islands Îles Salomon Somalia Somalie South Africa Afrique du Sud South Cook Islands Îles Cook du Sud South Georgia Island Géorgie du Sud South Orkney Islands Orcades du Sud South Sandwich Islands Îles Sandwich du Sud South Shetland Islands Shetland du Sud Sov Mil Order of Malta Ordre souverain de Malte Spain Espagne Spratly Islands Îles Spratleys Sri Lanka Sri Lanka St. Barthelemy Saint-Barthélemy St. Helena Sainte-Hélène St. Kitts & Nevis Saint-Kitts-et-Nevis St. Lucia Sainte-Lucie St. Martin Saint-Martin (partie française) St. Paul Island Île Saint-Paul (Alaska) St. Peter & St. Paul Rochers Saint-Pierre et Saint-Paul St. Pierre & Miquelon Saint-Pierre-et-Miquelon St. Vincent Saint-Vincent-et-les-Grenadines Sudan Soudan Suriname Suriname Svalbard Svalbard Swains Island Île Swains Sweden Suède Switzerland Suisse Syria Syrie Taiwan Taïwan Tajikistan Tadjikistan Tanzania Tanzanie Temotu Province Province de Temotu Thailand Thaïlande The Gambia Gambie Timor - Leste Timor oriental Togo Togo Tokelau Islands Îles Tokelau Tonga Tonga Trindade & Martim Vaz Trindade & Martim Vaz Trinidad & Tobago Trinité-et-Tobago Tristan da Cunha & Gough Islands Tristan da Cunha & Îles Gough Tromelin Island Île Tromelin Tunisia Tunisie Turkmenistan Turkménistan Turks & Caicos Islands Îles Turques-et-Caïques Tuvalu Tuvalu UK Base Areas on Cyprus Bases souveraines UK à Chypre US Virgin Islands Îles Vierges américaines Uganda Ouganda Ukraine Ukraine United Arab Emirates Émirats arabes unis United Nations HQ Siège des Nations unies United States États-Unis Uruguay Uruguay Uzbekistan Ouzbékistan Vanuatu Vanuatu Vatican City Cité du Vatican Venezuela Venezuela Vietnam Vietnam Wake Island Île Wake Wales Pays de Galles Wallis & Futuna Islands Îles Wallis-et-Futuna West Malaysia Malaisie occidentale Western Kiribati Kiribati occidental Western Sahara Sahara occidental Willis Island Île Willis Yemen Yémen Zambia Zambie Zimbabwe Zimbabwe DXCCSubmissionDialog DXCC Submission List Liste de soumission DXCC Options Options My DXCC Entity Mon entité DXCC User Filter Filtre utilisateur Category Catégorie Mixed Mixte CW CW Phone Phonie Digital Numérique Confirmed by Confirmé par LoTW LoTW Paper Papier Show Afficher Not Yet Submitted Pas encore soumis Submitted (Not Granted) Soumis (non accordé) Already Granted Déjà accordé Band Scope Plage de bande Select 5-Band DXCC preset bands (80/40/20/15/10m) Sélectionner les bandes prédéfinies DXCC 5 bandes (80/40/20/15/10 m) 5-Band DXCC DXCC 5 bandes Select all DXCC-eligible bands Sélectionner toutes les bandes éligibles DXCC All DXCC Bands Toutes les bandes DXCC Bands Bandes Select options above and the list will update automatically. Sélectionnez les options ci-dessus et la liste se mettra à jour automatiquement. Export the contacts listed above to an ADIF file Exporter les contacts listés ci-dessus dans un fichier ADIF Export Exporter No User Filter Aucun filtre utilisateur Any Band (Entity Level) Toute bande (niveau entité) 5-Band DXCC (80/40/20/15/10m) DXCC 5 bandes (80/40/20/15/10 m) Custom Band Selection Sélection personnalisée des bandes Entity Entité Prefix Préfixe Callsign Indicatif Band Bande Mode Mode Date Date Submitted Soumis Granted Accordé Export ADIF Exporter ADIF No contacts to export. Aucun contact à exporter. Failed to retrieve contact records. Impossible de récupérer les enregistrements de contacts. Export DXCC Submission List as ADIF Exporter la liste de soumission DXCC en ADIF any band toute bande 5-band 5 bandes all bands toutes les bandes %1 selected band(s) %1 bande(s) sélectionnée(s) No contacts match the selected criteria. Aucun contact ne correspond aux critères sélectionnés. %1 %2 %3 — DXCC %4 / %5 %1 %2 %3 — DXCC %4 / %5 band-slot emplacement de bande entity entité entry entrée entries entrées Data New Entity Nouvelle entité New Band Nouvelle bande New Mode Nouveau mode New Band&Mode Nouvelle bande & mode New Slot Nouveau créneau Confirmed Confirmé Worked Contacté Hz Hz kHz kHz GHz GHz MHz MHz Yes Oui No Non Requested Demandé Queued En attente Invalid Invalide Bureau Bureau Direct Direct Electronic Électronique Blank Vide Modified Modifié Grayline Ligne de gris Other Autre Short Path Petit chemin Long Path Grand chemin Not Heard Non entendu Uncertain Incertain Straight Key Pioche (Clé droite) Sideswiper Sideswiper (Cootie) Mechanical semi-automatic keyer or Bug Manipulateur semi-automatique (Bug) Mechanical fully-automatic keyer or Bug Manipulateur entièrement automatique Single Paddle Simple levier Dual Paddle Double levier (Iambique) Computer Driven Piloté par ordinateur Confirmed (AG) Confirmé (AG) Confirmed (no AG) Confirmé (sans AG) Unknown Inconnu Aircraft Scatter Diffusion par avion Aurora-E Aurore-E Aurora Aurore Back scatter Rétrodiffusion EchoLink EchoLink Earth-Moon-Earth Terre-Lune-Terre (EME) Sporadic E E sporadique F2 Reflection Réflexion F2 Field Aligned Irregularities Irrégularités alignées sur le champ Ground Wave Onde de sol Internet-assisted Assisté par Internet Ionoscatter Diffusion ionosphérique IRLP IRLP Line of Sight Ligne de vue Meteor scatter Météor-scatter Terrestrial or atmospheric repeater or transponder Relais ou transpondeur terrestre/atmosphérique Rain scatter Diffusion par la pluie Satellite Satellite Trans-equatorial Trans-équatorial Tropospheric ducting Guidage troposphérique DateFormatDelegate Blank Vide DevToolsDialog Developer Tools Outils dev Debug Log Journal de débogage Logging Rules: Règles de journalisation: e.g. qlog.ui.*.runtime=true p. ex. qlog.ui.*.runtime=true Apply Appliquer Generate Debug File Générer un fichier de débogage Export File Exporter le fichier SQL Console Console SQL Open SQL Ouvrir SQL Save SQL Enregistrer SQL Run SQL Exécuter SQL Export As Exporter en tant que Enter SQL query here... Ctrl+Return = run Saisissez la requête SQL ici... Ctrl+Entrée = exécuter TXT CSV CSV ADI Open SQL Query Ouvrir la requête SQL SQL Files (*.sql);;All Files (*) Fichiers SQL (*.sql);;Tous les fichiers (*) Open Error Erreur d’ouverture Cannot open file: %1 Impossible d’ouvrir le fichier: %1 Save SQL Query Enregistrer la requête SQL Save Error Erreur d’enregistrement Cannot save file: %1 Impossible d’enregistrer le fichier: %1 Query saved to %1 Requête enregistrée dans %1 Error: %1 Erreur : %1 %1 row(s) shown (truncated at %2) in %3 ms %1 ligne(s) affichée(s) (tronqué à %2) en %3 ms %1 row(s) returned in %2 ms %1 ligne(s) retournée(s) en %2 ms Export Exporter No results to export. Aucun résultat à exporter. Export Error Erreur d’exportation Cannot open file for writing: %1 Impossible d’ouvrir le fichier en écriture: %1 Exported %1 row(s) to %2 %1 ligne(s) exportée(s) vers %2 Text Files (*.txt);;All Files (*) Fichiers texte (*.txt);;Tous les fichiers (*) CSV Files (*.csv);;All Files (*) Fichiers CSV (*.csv);;Tous les fichiers (*) ADIF Export Exporter ADIF ADIF export requires the query to include the contacts 'id' column. Example: SELECT id, callsign FROM contacts WHERE ... No valid contact IDs found in the result set. Aucun identifiant de contact valide trouvé dans le jeu de résultats. Failed to retrieve contact records: %1 Impossible de récupérer les enregistrements de contacts: %1 No matching contacts found in the database. Aucun contact correspondant trouvé dans la base de données. Logging rules cleared (defaults) Règles de journalisation réinitialisées (par défaut) Logging rules applied Règles de journalisation appliquées Save Debug Log Enregistrer le journal de débogage No debug log file is currently being written Aucun fichier de journal de débogage n’est actuellement en cours d’écriture Log Files (*.log);;All Files (*) Fichiers journaux (*.log);;Tous les fichiers (*) Debug log saved to %1 Journal de débogage enregistré dans %1 Failed to copy the debug log file. Impossible de copier le fichier de journal de débogage. File logging is disabled La journalisation dans un fichier est désactivée Log file: %1 Fichier de journal : %1 DownloadQSLDialog Download QSLs Télécharger les QSL eQSL eQSL eQSL QTH Profile Profil QTH eQSL QSLs Since QSL depuis le QSOs Since QSO depuis le LoTW LoTW My Callsign Mon indicatif &Download &Télécharger LoTW is not configured properly.<p> Please, use <b>Settings</b> dialog to configure it.</p> LoTW n'est pas configuré correctement.<p> Veuillez utiliser la fenêtre des <b>Paramètres</b> pour le configurer.</p> eQSL is not configured properly.<p> Please, use <b>Settings</b> dialog to configure it.</p> eQSL n'est pas configuré correctement.<p> Veuillez utiliser la fenêtre des <b>Paramètres</b> pour le configurer.</p> Cancel Annuler Downloading from %1 Téléchargement depuis %1 Processing %1 QSLs Traitement de %1 QSL QLog Error Erreur QLog %1 update failed: La mise à jour de %1 a échoué : QLog Information Information QLog No service selected Aucun service sélectionné DxFilterDialog DX Cluster Filters Filtres DX Cluster General Filters Filtres généraux Bands Bandes Modes Modes Phone Phonie CW CW Digital Numérique FTx (FT8/FT4) FTx (FT8/FT4) Continent Continent North America Amérique du Nord South America Amérique du Sud Oceania Océanie Antarctica Antarctique Europe Europe Africa Afrique Asia Asie Log Status Statut du log New Mode Nouveau mode New Entity Nouvelle entité New Band Nouvelle bande New Slot Nouveau créneau Worked Contacté Confirmed Confirmé Extended Filters Filtres étendus Spotter Continent Continent du spotteur Do not show the following spots when they have the same Callsign and their time difference is lower than Time Diff and their frequency difference is lower than Freq Diff Ne pas afficher les spots suivants s'ils ont le même indicatif et que leur différence de temps est inférieure à Time Diff et leur différence de fréquence inférieure à Freq Diff Spots Dedup Déduplication des spots Time difference Différence de temps s s Frequency difference Différence de fréquence kHz kHz Member Membre No Club List is enabled Aucune liste de club n'est activée DxTableModel Time Heure Callsign Indicatif Frequency Fréquence Mode Mode Spotter Spotteur Comment Commentaire Continent Continent Spotter Continent Continent du spotteur Band Bande Member Membre Country Pays DxWidget Insert a <b>hostname:port</b> of DXC Server. Entrez un <b>hôte:port</b> de serveur DXC. Connect Connecter Filtered Filtré Spots Spots WCY WCY WWV WWV To All À tous Console Console 15-min Trend Tendance 15 min Full-text search Recherche plein texte Search Rechercher Close Search Fermer la recherche Send DX Cluster Command Envoyer une commande DX Cluster Filter... Filtrer... Filter DXC Filtrer DXC Spot Last QSO Spotter le dernier QSO Send last QSO spot Envoyer le spot du dernier QSO Show HF Stats Afficher stats HF Show VHF Stats Afficher stats VHF Show WCY Afficher WCY Show WWV Afficher WWV Column Visibility... Visibilité des colonnes... Which columns should be displayed Quelles colonnes doivent être affichées Connect on startup Connecter au démarrage Automatic connection after start Connexion automatique après le démarrage Delete Server Supprimer le serveur DXC - Delete Server DXC - Supprimer le serveur Clear Password Effacer le mot de passe Keep Spots Garder les spots Spots are not cleared when connecting to a new DX Cluster. Les spots ne sont pas effacés lors de la connexion à un nouveau DX Cluster. Clear Effacer Clear all data Effacer toutes les données Search... Rechercher... DXC - Search DXC - Rechercher My Continent Mon continent Auto Auto Connecting... Connexion... DX Cluster is temporarily unavailable Le DX Cluster est temporairement indisponible DXC Server Error Erreur serveur DXC An invalid callsign Un indicatif invalide DX Cluster Password Mot de passe DX Cluster Security Notice Avis de sécurité The password can be sent via an unsecured channel Le mot de passe peut être envoyé via un canal non sécurisé Server Serveur Username Utilisateur Disconnect Déconnecter DX Cluster Command Commande DX Cluster DxccTableModel Worked Contacté eQSL eQSL LoTW LoTW Paper Papier DxccTableWidget Mode Mode EQSLQSLDownloader Incorrect Password or QTHProfile Id Mot de passe ou ID de profil QTH incorrect ADIF file not found in eQSL response Fichier ADIF non trouvé dans la réponse eQSL Incorrect Username or password Identifiant ou mot de passe incorrect Unknown Error Erreur inconnue Cannot opet temporary file Impossible d'ouvrir le fichier temporaire Cannot save the image to file Impossible de sauvegarder l'image dans le fichier EQSLUploader Unknown Reply from eQSL Réponse inconnue de eQSL EditActivitiesDialog Edit Activities Modifier les activités Activities Activités Add Ajouter Edit Modifier Remove Supprimer ExportDialog Export Selected QSOs Exporter les QSO sélectionnés File Fichier Browse Parcourir ADX ADX CSV CSV JSON JSON POTA POTA Export Type Type d'exportation Column set Jeu de colonnes Select one of the pre-defined sets of columns or define your own set of columns Sélectionnez un des jeux de colonnes prédéfinis ou définissez le vôtre Edit current set of columns Modifier le jeu de colonnes actuel Edit Modifier Mark exported QSOs As Sent Marquer les QSO exportés comme envoyés Export only QSOs that match the active filters Exporter uniquement les QSO correspondant aux filtres actifs Filters Filtres Export only QSOs that match the selected date range Exporter uniquement les QSO de la période sélectionnée Date Range Plage de dates Export only QSOs that match the selected My Callsign Exporter uniquement les QSO correspondant à "Mon Indicatif" My Callsign Mon indicatif Export only QSOs that match the selected My Gridsquare Exporter uniquement les QSO correspondant à mon Locator (Gridsquare) My Gridsquare Mon Locator Export only QSOs that match the selected QSL Send Via value Exporter uniquement les QSO avec ce mode d'envoi de QSL QSL Send via Envoyer QSL via Include unusual QSO Sent statuses Inclure les statuts d'envoi de QSO inhabituels Include Sent Status Inclure le statut d'envoi Under normal circumstances this status means <b>"Ignore/Invalid"</b>.<br/>However, it may sometimes be wanted to ignore this setting when sending a QSL. En temps normal, ce statut signifie <b>"Ignorer/Invalide"</b>.<br/>Cependant, il peut être souhaitable d'ignorer ce paramètre lors de l'envoi d'une QSL. "Ignore" "Ignorer" Under normal circumstances this status means <b>"do not send"</b>.<br/>However, it may sometimes be wanted to ignore this setting when sending a QSL. En temps normal, ce statut signifie <b>"ne pas envoyer"</b>.<br/>Cependant, il peut être souhaitable d'ignorer ce paramètre lors de l'envoi d'une QSL. "No" "Non" Resend already sent QSOs Renvoyer les QSO déjà envoyés Already Sent Déjà envoyé User Filter Filtre utilisateur Export QSOs that match the selected user QSO Filter Exporter les QSO correspondant au filtre utilisateur sélectionné Station Profile Profil de station Export QSOs Exporter les QSO &Export &Exporter Export only QSOs matching this station profile Exporter uniquement les QSO correspondant à ce profil de station Exporting... Exportation en cours… Cancel Annuler QLog Error Erreur QLog Cannot mark exported QSOs as Sent Impossible de marquer les QSO exportés comme Envoyés Generic Générique QSLs QSL All Tous Minimal Minimal QSL-specific Spécifique aux QSL Custom 1 Personnalisé 1 Custom 2 Personnalisé 2 Custom 3 Personnalisé 3 ExportPasswordDialog Pack Data & Settings Archiver les données et paramètres Enter a password to encrypt stored credentials. The password will be needed to restore them later. Entrez un mot de passe pour chiffrer vos identifiants enregistrés. Ce mot de passe sera nécessaire pour les restaurer ultérieurement. Password Mot de passe Random Aléatoire Confirm Password Confirmer le mot de passe Delete passwords from Credential Store after export Supprimer les mots de passe du gestionnaire d'identifiants après export FlrigRigDrv Timeout Délai expiré FLRig response timeout Délai de réponse de FLRig expiré Network Error Erreur réseau HRDLogUploader Response message malformed Message de réponse mal formé HamQTHCallbook HamQTH HamQTH HamlibRigDrv None Aucun CAT CAT DTR DTR RTS RTS Initialization Error Erreur d'initialisation Cannot set PTT Type Impossible de définir le type de PTT Cannot set PTT Share Impossible de définir le partage du PTT Cannot set CIV Addr Impossible de définir l'adresse CI-V Unsupported Rig Driver Pilote de transceiver non supporté Cannot set auto_power_on Impossible de définir auto_power_on Cannot set no_xchg to 1 Impossible de définir no_xchg à 1 Rig Open Error Erreur d'ouverture du transceiver Set TX Frequency Error Erreur lors du réglage de la fréquence TX Set Frequency Error Erreur lors du réglage de la fréquence Set Split Error Erreur lors du réglage du split Set Mode Error Erreur lors du réglage du mode Set Split Mode Error Erreur lors du réglage du mode split Set PTT Error Erreur lors du réglage du PTT Cannot sent Morse Impossible d'envoyer du Morse Cannot stop Morse Impossible d'arrêter le Morse Get PTT Error Erreur lors de la lecture du PTT Get Frequency Error Erreur lors de la lecture de la fréquence Get Mode Error Erreur lors de la lecture du mode Get VFO Error Erreur lors de la lecture du VFO Get PWR Error Erreur lors de la lecture de la puissance Get PWR (power2mw) Error Erreur lors de la lecture de puissance (mW) Get RIT Function Error Erreur lors de la lecture de la fonction RIT Get RIT Error Erreur lors de la lecture du RIT Get XIT Function Error Erreur lors de la lecture de la fonction XIT Get XIT Error Erreur lors de la lecture du XIT Get Split Error Get TX Frequency Error Get KeySpeed Error Erreur lors de la lecture de la vitesse de manipulation Set KeySpeed Error Erreur lors du réglage de la vitesse de manipulation HamlibRotDrv Initialization Error Erreur d'initialisation Unsupported Rotator Driver Pilote de rotor non supporté Rot Open Error Erreur d'ouverture du rotor Set Possition Error Erreur lors du réglage de la position Get Possition Error Erreur lors de la lecture de la position ImportDialog Import Importation Date Range Plage de dates Import all or only QSOs from the given period Importer tous les QSO ou seulement ceux d'une période donnée All Tous File Fichier ADX ADX Browse Parcourir Options Options The value is used when an input record does not contain the ADIF value La valeur est utilisée lorsqu'un enregistrement d'entrée ne contient pas la valeur ADIF Defaults Par défaut My Profile Mon profil My Rig Mon transceiver Comment Commentaire If DXCC is missing in the imported record, it will be resolved from the callsign. Si le DXCC manque dans l’enregistrement importé, il sera déterminé à partir de l’indicatif. Fill missing DXCC Entity Information Compléter les informations d’entité DXCC manquantes &Import &Importer Select File Sélectionner un fichier The values below will be used when an input record does not contain the ADIF values Les valeurs ci-dessous seront utilisées lorsqu'un enregistrement d'entrée ne contient pas les valeurs ADIF <p><b>In-Log QSO:</b></p><p> <p><b>QSO dans le log :</b></p><p> <p><b>Importing:</b></p><p> <p><b>Importation :</b></p><p> Duplicate QSO QSO en double <p>Do you want to import duplicate QSO?</p>%1 %2 <p>Voulez-vous importer le QSO en double ?</p>%1 %2 Save to File Sauvegarder dans un fichier QLog Import Summary Résumé de l'importation QLog Import date Date d'importation Imported file Fichier importé Imported: %n contact(s) Importé : %n contact Importés : %n contacts Warning(s): %n Avertissement : %n Avertissements : %n Error(s): %n Erreur : %n Erreurs : %n Details Détails Import Result Résultat de l'importation Save Details... Enregistrer les détails... InputPasswordDialog Dialog Fenêtre The password will be stored in the Credential Store. Le mot de passe sera enregistré dans le gestionnaire d'identifiants. Remember Password Retenir le mot de passe KSTChat Unknown User Utilisateur inconnu Invalid password Mot de passe invalide KSTChatWidget Chat messages Messages de discussion Valuable messages Messages importants Clear selected Callsign and Chat message entry. Effacer l'indicatif sélectionné et le message de discussion saisi. Send chat message Envoyer le message Column Visibility Visibilité des colonnes Which columns should be displayed Quelles colonnes doivent être affichées Prepare QSO Préparer le QSO Transfer Callsign and Gridsquare Information to the New QSO dialog Transférer l'indicatif et le Locator vers la fenêtre de nouveau QSO Show About Me Only M'afficher uniquement Show only messages where my callsign is present Afficher uniquement les messages mentionnant mon indicatif Suppress User To User Supprimer "Utilisateur à Utilisateur" Suppress private messages between two callsigns Supprimer les messages privés entre deux indicatifs Highlight Mise en évidence Highlight messages based on the setting Mettre en évidence les messages selon les réglages Highlight Rules Règles de mise en évidence Beam Azimut (Beam) Clear Messages Effacer les messages Clear all messages in the window Effacer tous les messages de la fenêtre You Vous KSTHighlightRuleDetail Edit Rule Modifier la règle Rule Name Nom de la règle Chat Room Salon de discussion Enabled Activé Highlight message which match Mettre en évidence les messages correspondant à All the following conditions Toutes les conditions suivantes Any of the following conditions L'une des conditions suivantes Add Condition Ajouter une condition All Tous Sender Expéditeur Message Message Gridsquare Locator Contains Contient Starts with Commence par Remove Supprimer Must not be empty Ne doit pas être vide KSTHighlighterSettingDialog Highlight Rules Règles de mise en évidence Rules Règles Add Ajouter Edit Modifier Remove Supprimer Name Nom State État KeySequenceEdit Clear Effacer LoadDatabaseDialog Unpack Data & Settings Déballer les données et les paramètres Warning Avertissement <html><head/><body><p>⚠ <span style=" font-weight:700;">Current database will be DELETED!</span><br/>⚠ <span style=" font-weight:700;">All stored passwords will be DELETED!</span><br/>⚠ <span style=" font-weight:700;">The application will restart after loading</span>.</p><p>To preserve data, use Pack Data &amp; Settings or Export QSOs first.</p></body></html> <html><head/><body><p>⚠ <span style=" font-weight:700;">La base de données actuelle sera SUPPRIMÉE !</span><br/>⚠ <span style=" font-weight:700;">Tous les mots de passe enregistrés seront SUPPRIMÉS !</span><br/>⚠ <span style=" font-weight:700;">L'application redémarrera après le chargement</span>.</p><p>Pour préserver les données, utilisez d'abord Pack Data &amp; Settings ou Export QSOs.</p></body></html> Select Database Sélectionner la base de données File: Fichier : Browse Parcourir Status: No file selected Statut : Aucun fichier sélectionné Decrypt Credentials Déchiffrer les identifiants Enter password to decrypt credentials Entrez le mot de passe pour déchiffrer les identifiants Password: Mot de passe : Load && Restart Charger && Redémarrer Invalid password Mot de passe invalide Select Database File Sélectionner le fichier de base de données QLog Database Export (*.dbe);;All Files (*) Export de base de données QLog (*.dbe);;Tous les fichiers (*) Cannot create temporary file Impossible de créer le fichier temporaire Decompressing database... Décompression de la base de données... Cannot decompress database file Impossible de décompresser le fichier de base de données Cannot open database Impossible d'ouvrir la base de données Valid database Base de données valide Different platform Plateforme différente Password required to import credentials Mot de passe requis pour importer les identifiants No encrypted credentials in database Aucun identifiant chiffré dans la base de données LogFormat Cannot find My DXCC Entity Info Impossible de trouver mes informations d'entité DXCC A minimal set of fields not present (start_time, call, band, mode, station_callsign) Un ensemble minimal de champs est manquant (start_time, call, band, mode, station_callsign) Outside the selected Date Range En dehors de la plage de dates sélectionnée Duplicate Doublon DXCC Info is missing Les informations DXCC sont manquantes no Station Callsign present aucun indicatif de station présent Cannot insert to database Impossible d'insérer dans la base de données Imported Importé DXCC State: État DXCC : Error Erreur Warning Avertissement LogbookModel Country Pays Band Bande Mode Mode RST Sent RST Envoyé RST Rcvd RST Reçu Gridsquare Locator QSL Message Message QSL Comment Commentaire Notes Notes Paper Papier LoTW LoTW eQSL eQSL QSL Received QSL Reçue QSL Sent QSL Envoyée QSO ID ID QSO Time on Heure début Time off Heure fin Call Indicatif RSTs RSTe RSTr RSTr Frequency Fréquence Submode Sous-mode Name (ASCII) Nom (ASCII) QTH (ASCII) QTH (ASCII) DXCC DXCC Country (ASCII) Pays (ASCII) Continent Continent CQZ CQZ ITU ITU Prefix Préfixe State État County Comté County Alt Comté Alt IOTA IOTA QSLr QSLr QSLr Date Date QSLr QSLs QSLs QSLs Date Date QSLs LoTWr LoTWr LoTWr Date Date LoTWr LoTWs LoTWs LoTWs Date Date LoTWs TX PWR TX PWR Additional Fields Champs additionnels Address (ASCII) Adresse (ASCII) Address Adresse Age Âge Altitude Altitude A-Index Indice A Antenna Az Azimut Antenne Antenna El Élévation Antenne Signal Path Chemin du Signal ARRL Section Section ARRL Award Submitted Récompense soumise Award Granted Récompense accordée Band RX Bande RX Gridsquare Extended Locator étendu Contest Check Vérification Concours Class Classe ClubLog Upload Date Date d'envoi ClubLog ClubLog Upload State État d'envoi ClubLog Comment (ASCII) Commentaire (ASCII) Contacted Operator Opérateur contacté Contest ID ID Concours Credit Submitted Crédit soumis Credit Granted Crédit accordé DOK DOK DCLr Date Date DCLr DCLs Date Date DCLs DCLr DCLr DCLs DCLs Distance Distance Email Email Owner Callsign Indicatif du propriétaire eQSL AG eQSL AG eQSLr Date Date eQSLr eQSLs Date Date eQSLs eQSLr eQSLr eQSLs eQSLs FISTS Number Numéro FISTS FISTS CC FISTS CC EME Init Init EME Frequency RX Fréquence RX Guest Operator Opérateur invité HamlogEU Upload Date Date d'envoi HamlogEU HamlogEU Upload Status Statut d'envoi HamlogEU HamQTH Upload Date Date d'envoi HamQTH HamQTH Upload Status Statut d'envoi HamQTH HRDLog Upload Date Date d'envoi HRDLog HRDLog Upload Status Statut d'envoi HRDLog IOTA Island ID ID Île IOTA K-Index Indice K Latitude Latitude Longitude Longitude Max Bursts Rafales max (Bursts) CW Key Info Infos clé CW CW Key Type Type de clé CW MS Shower Name Essaim MS (Météor Scatter) My Altitude Mon altitude My Antenna (ASCII) Mon antenne (ASCII) My Antenna Mon antenne My City (ASCII) Ma ville (ASCII) My City Ma ville My County Mon département/comté My County Alt Mon département (Alt) My Country (ASCII) Mon pays (ASCII) My Country Mon pays My CQZ Ma zone CQ My DARC DOK Mon DOK (DARC) My DXCC Mon DXCC My FISTS Mon n° FISTS My Gridsquare Mon Locator My Gridsquare Extended Mon Locator (étendu) My IOTA Mon IOTA My IOTA Island ID Mon ID d'île IOTA My ITU Ma zone ITU My Latitude Ma latitude My Longitude Ma longitude My CW Key Info Infos sur ma clé CW My CW Key Type Mon type de clé CW My Name (ASCII) Mon nom (ASCII) My Name Mon nom My Postal Code (ASCII) Mon code postal (ASCII) My Postal Code Mon code postal My POTA Ref Ma réf. POTA My Rig (ASCII) Ma station/TRX (ASCII) My Rig Ma station/TRX My Special Interest Activity (ASCII) Mon activité spéciale (ASCII) My Special Interest Activity Mon activité spéciale My Spec. Interes Activity Info (ASCII) Infos activité spéc. (ASCII) My Spec. Interest Activity Info Infos activité spéc. My SOTA Mon SOTA My State Mon État/Province My Street Ma rue My USA-CA Counties Mes comtés USA-CA My VUCC Grids Mes locators VUCC Name Nom Notes (ASCII) Notes (ASCII) #MS Bursts Nb rafales MS #MS Pings Nb pings MS Operator Callsign Indicatif de l'opérateur POTA POTA Contest Precedence Precedence (Contest) Propagation Mode Mode de propagation Public Encryption Key Clé de chiffrement publique QRZ Download Date Date de téléchargement QRZ QRZ Download Status Statut téléchargement QRZ QRZ Upload Date Date d'envoi vers QRZ QRZ Upload Status Statut envoi vers QRZ QSLs Message (ASCII) Message QSL envoyée (ASCII) QSLs Message Message QSL envoyée QSLr Message Message QSL reçue QSLr Via QSL reçue via QSLs Via QSL envoyée via QSL Via QSL via QSO Completed QSO terminé QSO Random QSO aléatoire QTH QTH Region Région Rig (ASCII) TRX (ASCII) Rig TRX RcvPWR Puissance reçue SAT Mode Mode Satellite SAT Name Nom du Satellite Solar Flux Flux solaire SIG (ASCII) SIG (ASCII) SIG SIG SIG Info (ASCII) Infos SIG (ASCII) SIG Info Infos SIG Silent Key Silent Key (SK) SKCC Member Membre SKCC SOTA SOTA RcvNr N° reçu RcvExch Échange reçu Logging Station Callsign Indicatif de la station de saisie SentNr N° envoyé SentExch Échange envoyé SWL SWL Ten-Ten Number Numéro Ten-Ten UKSMG Member Membre UKSMG USA-CA Counties Comtés USA-CA VE Prov Province VE VUCC VUCC Web Web My ARRL Section Ma section ARRL My WWFF Mon WWFF WWFF WWFF LogbookWidget Delete Supprimer Logbook - Delete QSO Carnet de trafic - Supprimer QSO Upload to Clublog Envoyer vers Clublog Lookup on Web Rechercher sur le Web Update from Callbook Mise à jour via Callbook Add Missing Info Ajouter les infos manquantes Mark QSL RCVD Marquer QSL comme reçue Mark QSL Requested Marquer QSL demandée Filter Callsign Filtrer par indicatif Edit Value Modifier la valeur Logbook - Edit Value Carnet de trafic - Modifier la valeur Column Visibility Visibilité des colonnes Which columns should be displayed Quelles colonnes afficher Export Selected Exporter la sélection Export selected QSOs Exporter les QSO sélectionnés Send DX Spot Envoyer un Spot DX Logbook - Send DX Spot Carnet de trafic - Envoyer Spot DX Callsign Indicatif Gridsquare Locator POTA POTA SOTA SOTA WWFF WWFF SIG SIG IOTA IOTA All Bands Toutes les bandes All Modes Tous les modes All Countries Tous les pays No User Filter Aucun filtre utilisateur QLog Warning Avertissement QLog Each batch supports up to 100 QSOs. Chaque lot supporte jusqu'à 100 QSO. QSOs Update Progress Progression de la mise à jour des QSO Cancel Annuler QLog Error Erreur QLog Callbook login failed Échec de connexion au Callbook Callbook error: Erreur Callbook : All Clubs Tous les clubs Delete the selected contacts? Supprimer les contacts sélectionnés ? Clublog's <b>Immediately Send</b> supports only one-by-one deletion<br><br>Do you want to continue despite the fact<br>that the DELETE operation will not be sent to Clublog? L'option <b>Envoi immédiat</b> de Clublog ne supporte que la suppression unitaire.<br><br>Voulez-vous continuer sachant que l'opération de SUPPRESSION ne sera pas transmise à Clublog ? Deleting QSOs Suppression des QSO en cours Update Mettre à jour By updating, all selected rows will be affected.<br>The value currently edited in the column will be applied to all selected rows.<br><br>Do you want to edit them? La mise à jour affectera toutes les lignes sélectionnées.<br>La valeur éditée dans la colonne sera appliquée à toute la sélection.<br><br>Voulez-vous valider la modification ? Count: %n Nombre : %n Nombre : %n Downloading eQSL Image Téléchargement de l'image eQSL eQSL Download Image failed: Échec du téléchargement de l'image eQSL : LotwQSLDownloader Cannot open temporary file Impossible d'ouvrir le fichier temporaire Incorrect login or password Identifiant ou mot de passe incorrect LotwUploader Upload cancelled by user Envoi annulé par l'utilisateur Upload rejected by LoTW Envoi rejeté par LoTW Unexpected response from TQSL server Réponse inattendue du serveur TQSL TQSL utility error Erreur de l'utilitaire TQSL TQSLlib error Erreur TQSLlib Unable to open input file Impossible d'ouvrir le fichier d'entrée Unable to open output file Impossible d'ouvrir le fichier de sortie All QSOs were duplicates or out of date range Tous les QSO étaient des doublons ou hors plage de dates Some QSOs were duplicates or out of date range Certains QSO étaient des doublons ou hors plage de dates Command syntax error Erreur de syntaxe de commande LoTW Connection error (no network or LoTW is unreachable) Erreur de connexion LoTW (pas de réseau ou LoTW injoignable) Unexpected Error from TQSL Erreur inattendue de TQSL TQSL not found TQSL introuvable TQSL crashed TQSL a planté MainWindow &File &Fichier &Logbook &Carnet de trafic &Equipment &Équipement &Help &Aide &Window &Fenêtre Se&rvice Se&rvices Contest Concours (Contest) Dupe Check Vérif. Doublons Sequence Séquence Linking Exchange With Lier l'échange avec Toolbar Barre d'outils Clock Horloge Map Carte DX Cluster DX Cluster WSJTX WSJTX Rotator Rotor Bandmap Carte des bandes Rig TRX Online Map Carte en ligne CW Console Console CW Chat Chat Profile Image Image de profil Alerts Alertes Quit Quitter Application - Quit Quitter l'application &Settings &Réglages Pack Data && Settings Sauvegarder données && réglages Unpack Data && Settings Restaurer données && réglages New QSO - Clear Nouveau QSO - Effacer &Import &Importer &Export &Exporter Connect R&ig Connecter le T&RX &About À &propos New QSO - Save Nouveau QSO - Enregistrer S&tatistics S&tatistiques QSL &Gallery &Galerie QSL Developer Tools Outils dev Run custom read-only SQL queries against the logbook database Exécuter des requêtes SQL personnalisées en lecture seule sur la base de données du logbook Print QSL &Labels &Imprimer les étiquettes QSL Wsjtx WSJTX Connect R&otator Connecter le r&otor QSO &Filters &Filtres de QSO &Awards &Diplômes DXCC &Submission List &Liste de soumission DXCC Generate a list of contacts to submit for ARRL DXCC award credit Générer une liste de contacts à soumettre pour le crédit ARRL DXCC Edit Rules Modifier les règles Beep Bip Connect &CW Keyer Connecter le manipulateur &CW &Wiki &Wiki Report &Bug... Signaler un &bug... &Manual Entry Saisie &manuelle Switch New Contact dialog to the manually entry mode<br/>(time, freq, profiles etc. are not taken from their common sources) Passer la saisie de contact en mode manuel<br/>(l'heure, la fréquence et les profils ne sont plus synchronisés) Mailing List... Liste de diffusion... Edit Modifier Save Arrangement Enregistrer la disposition Keep Options Conserver les options Restore connection options after application restart Restaurer les options de connexion au redémarrage Logbook - Search Callsign Carnet de trafic - Chercher indicatif New QSO - Add text from Callsign field to Bandmap Nouveau QSO - Ajouter l'indicatif à la carte des bandes Rig - Band Down TRX - Bande inférieure Rig - Band Up TRX - Bande supérieure New QSO - Use Callsign from the Whisperer Nouveau QSO - Utiliser l'indicatif du Whisperer CW Console - Key Speed Up Console CW - Augmenter la vitesse CW Console - Key Speed Down Console CW - Diminuer la vitesse CW Console - Profile Up Console CW - Profil suivant CW Console - Profile Down Console CW - Profil précédent Rig - PTT On/Off TRX - PTT On/Off Clear Effacer Show Alerts Afficher les alertes All Bands Toutes bandes Each Band Par bande Each Band && Mode Par bande && mode No Check Pas de vérif. Single Simple Per Band Par bande Stop Arrêt Reset Réinitialiser None Aucun Upload Envoyer Service - Upload QSOs Service - Envoi des QSO Download QSLs Récupérer QSL Service - Download QSLs Service - Récupération des QSL Theme: Native Thème : Natif Theme: QLog Light Thème : QLog Clair Theme: QLog Dark Thème : QLog Sombre What's New Nouveautés Export Cabrillo Exporter Cabrillo Color Theme Thème de couleur Not enabled for non-Fusion style Non activé pour le style hors-Fusion Press to tune the alert Appuyez pour régler l'alerte Clublog Immediately Upload Error Erreur d'envoi immédiat Clublog <b>Error Detail:</b> <b>Détail de l'erreur :</b> op: op : A New Version Nouvelle version disponible A new version %1 is available. Une nouvelle version %1 est disponible. Remind Me Later Me le rappeler plus tard Download Télécharger Failed to encrypt credentials. Échec du chiffrement des identifiants. Database files (*.dbe);;All files (*) Fichiers base de données (*.dbe);;Tous les fichiers (*) Failed to create temporary file. Échec de création du fichier temporaire. Failed to dump the database. Échec de l'exportation de la base de données. Compressing database... Compression de la base... Database successfully dumped to %1 Base de données exportée avec succès vers %1 Failed to compress the database. Échec de la compression de la base de données. Failed to prepare database for import. Échec de la préparation de la base pour l'importation. Classic Classique Do you want to remove the Contest filter %1? Voulez-vous supprimer le filtre de concours %1 ? Contest: Concours : <h1>QLog %1</h1><p>&copy; 2019 Thomas Gatzweiler DL2IC<br/>&copy; 2021-2026 Ladislav Foldyna OK1MLG<br/>&copy; 2025-2026 Michael Morgan AA5SH<br/>&copy; 2025-2026 Kyle Boyle VE9KZ</p><p>Based on Qt %2<br/>%3<br/>%4<br/>%5</p><p>Icon by <a href='http://www.iconshock.com'>Icon Shock</a><br />Satellite images by <a href='http://www.nasa.gov'>NASA</a><br />ZoneDetect by <a href='https://github.com/BertoldVdb/ZoneDetect'>Bertold Van den Bergh</a><br />TimeZone Database by <a href='https://github.com/evansiroky/timezone-boundary-builder'>Evan Siroky</a> <h1>QLog %1</h1><p>&copy; 2019 Thomas Gatzweiler DL2IC<br/>&copy; 2021-2026 Ladislav Foldyna OK1MLG<br/>&copy; 2025-2026 Michael Morgan AA5SH<br/>&copy; 2025-2026 Kyle Boyle VE9KZ</p><p>Basé sur Qt %2<br/>%3<br/>%4<br/>%5</p><p>Icônes par <a href='http://www.iconshock.com'>Icon Shock</a><br />Images satellites par <a href='http://www.nasa.gov'>NASA</a><br />ZoneDetect par <a href='https://github.com/BertoldVdb/ZoneDetect'>Bertold Van den Bergh</a><br />Base de données TimeZone par <a href='https://github.com/evansiroky/timezone-boundary-builder'>Evan Siroky</a> About À propos N/A N/D MapWebChannelHandler Grid Grille/Locator Gray-Line Ligne de gris (Gray-Line) Beam Rayon (Beam) Aurora Aurore MUF MUF IBP IBP Chat Chat WSJTX - CQ WSJTX - Appels CQ Path Chemin/Trajet NewContactWidget Frequency Fréquence Callsign Indicatif <b>DUPE !!!</b> <b>DOUBLON !!!</b> RX: RX : TX: TX : MHz MHz 80m 80m RSTs RST envoyé RSTr RST reçu 59 59 Mode Mode Save Enregistrer Lookup the call on the web. The query URL can be changed in Settings -> Callbook Rechercher l'indicatif sur le web. L'URL peut être modifiée dans Réglages -> Callbook Web Web Time On Heure début Reset Réinit Date Date Duration Durée Info Infos &Details &Détails QSL Send Status Statut envoi QSL Paper Papier <b>Yes</b> - an outgoing QSL card has been sent; the QSO has been uploaded to, and accepted by, the online service<br/><b>No</b> - do not send an outgoing QSL card; do not upload the QSO to the online service<br/><b>Requested</b> - the contacted station has requested a QSL card; the contacted station has requested the QSO be uploaded to the online service<br/><b>Queued</b> - an outgoing QSL card has been selected to be sent; a QSO has been selected to be uploaded to the online service<br/> <b>Oui</b> - une carte QSL a été envoyée ; le QSO a été transmis et accepté par le service en ligne<br/><b>Non</b> - ne pas envoyer de QSL ; ne pas transmettre le QSO au service en ligne<br/><b>Demandé</b> - le correspondant a demandé une QSL ou un envoi vers un service en ligne<br/><b>En file d'attente</b> - la QSL ou l'envoi vers le service est programmé<br/> LoTW LoTW eQSL eQSL QSL Send via QSL envoyée via QSL via QSL via Propagation Mode Mode de propagation D&X Stats Stats D&X <b>DXCC Statistics</b> <b>Statistiques DXCC</b> <b>Station Statistics</b> <b>Statistiques de la station</b> M&y Station M&a Station Station Station Rig TRX Antenna Antenne My &Notes Mes &Notes Member: Membre : Expand/Collapse Développer/Réduire No Non Yes Oui Requested Demandé Queued En file d'attente Ignored Ignoré Bureau Bureau Direct Direct Electronic Électronique QLog Error Erreur QLog Callbook login failed Échec de connexion au Callbook LP LP (Long Path) New Entity! Nouvelle Entité ! New Band! Nouvelle Bande ! New Mode! Nouveau Mode ! New Band & Mode! Nouveau Bande & Mode ! New Slot! Nouveau Slot ! Worked Contacté (Worked) Confirmed Confirmé GE GE (Good Evening) GM GM (Good Morning) GA GA (Good Afternoon) m m Callbook search is inactive Recherche Callbook inactive Callbook search is active Recherche Callbook active Contest ID must be filled in to activate L'ID du Concours doit être renseigné pour l'activation two or four adjacent Maidenhead grid locators, each four characters long, (ex. EN98,FM08,EM97,FM07) deux ou quatre locators adjacents (Maidenhead), chacun de quatre caractères (ex. JN18,JN19...) the contacted station's DARC DOK (District Location Code) (ex. A01) le DOK (District Location Code) DARC de la station contactée (ex. A01) World Wide Flora & Fauna World Wide Flora & Fauna (WWFF) Special Activity Group Groupe d'Activité Spéciale Special Activity Group Information Informations du Groupe d'Activité Spéciale It is not the name of the contest but it is an assigned<br>Contest ID (ex. CQ-WW-CW for CQ WW DX Contest (CW)) Ce n'est pas le nom complet du concours mais l'ID attribué<br>(ex. CQ-WW-CW pour le CQ WW DX Contest CW) Blank Vide W W Description of the contacted station's equipment Description de l'équipement de la station contactée OmnirigRigDrv Rig 1 Transceiver 1 Rig 2 Transceiver 2 Initialization Error Erreur d'initialisation Rig status changed Le statut du poste a changé Rig is not connected Le transceiver n'est pas connecté OmnirigV2RigDrv Rig 1 Transceiver 1 Rig 2 Transceiver 2 Rig 3 Transceiver 3 Rig 4 Transceiver 4 Initialization Error Erreur d'initialisation Rig status changed Le statut du poste a changé Rig is not connected Le transceiver n'est pas connecté PSTRotDrv Rot 1 Rotor 1 Cannot bind a port Impossible de lier un port Cannot get IP Address for Impossible d'obtenir l'adresse IP pour No IPv4 Address for Pas d'adresse IPv4 pour Error Occurred Une erreur est survenue Operation Timeout Délai d'attente dépassé PaperQSLDialog Manage QSL Card Gérer la carte QSL Available QSLs QSL disponibles Copy the input file from the source folder to the QLog Internal QSL Storage folder.<br/>The original file remains unchanged in the source folder Copie le fichier d'entrée du dossier source vers le stockage QSL interne de QLog.<br/>Le fichier original reste inchangé dans le dossier source Import QSL Importer QSL Add File Ajouter un fichier Remove Retirer Delete Supprimer Delete QSL? Supprimer la QSL ? Open Ouvrir Toggle Favorite Ajouter/Retirer des favoris PlatformSettingsDialog Platform-specific Settings Paramètres spécifiques à la plateforme The database was exported from a different platform. Please verify or update the following settings. You can leave fields empty and configure them later in Settings. La base de données a été exportée depuis une plateforme différente. Veuillez vérifier ou mettre à jour les paramètres suivants. Vous pouvez laisser les champs vides et les configurer plus tard dans les Paramètres. Setting Paramètre Value Valeur Continue Continuer Select File Sélectionner le fichier All Files (*) Tous les fichiers (*) ProfileImageWidget Profile Image Image de profil QCoreApplication QLog Help Aide de QLog QMessageBox QLog Critical QLog Critique Cannot save a password for %1 to the Credential Store Impossible de sauvegarder un mot de passe pour %1 dans le gestionnaire d'identifiants Cannot get a password for %1 from the Credential Store Impossible de récupérer un mot de passe pour %1 depuis le gestionnaire d'identifiants QLog Warning Avertissement QLog Club List Update failed. Cannot remove old records Mise à jour de la liste des clubs échouée. Impossible de supprimer les anciens enregistrements Club List Update failed. Cannot plan new downloads Mise à jour de la liste des clubs échouée. Impossible de planifier les nouveaux téléchargements Unexpected Club List download. Canceling next downloads Téléchargement inattendu de la liste des clubs. Annulation des téléchargements suivants Unexpected Club List content for Contenu inattendu de la liste des clubs pour Network error. Cannot download Club List for Erreur réseau. Impossible de télécharger la liste des clubs pour QLog Error Erreur QLog QLog is already running QLog est déjà en cours d'exécution Failed to process pending database import. Échec du traitement de l'importation de la base de données en attente. The database was imported successfully, but the stored passwords could not be restored (decryption failed or the data is corrupted). All service passwords have been cleared and must be re-entered in Settings. La base de données a été importée avec succès, mais les mots de passe stockés n'ont pas pu être restaurés (échec du déchiffrement ou données corrompues). Tous les mots de passe des services ont été effacés et doivent être saisis à nouveau dans les Paramètres. Could not connect to database. Impossible de se connecter à la base de données. Could not export a QLog database to ADIF as a backup.<p>Try to export your log to ADIF manually Impossible d'exporter la base QLog vers ADIF pour sauvegarde.<p>Essayez d'exporter votre carnet de trafic en ADIF manuellement Database migration failed. La migration de la base de données a échoué. QLog Info Info QLog Activity name is already exists. Ce nom d'activité existe déjà. Rule name is already exists. Ce nom de règle existe déjà. Callsign Regular Expression is incorrect. L'expression régulière de l'indicatif est incorrecte. Comment Regular Expression is incorrect. L'expression régulière du commentaire est incorrecte. Cannot Update Alert Rules Impossible de mettre à jour les règles d'alerte DXC Server Name Error Erreur de nom de serveur DXC DXC Server address must be in format<p><b>[username@]hostname:port</b> (ex. hamqth.com:7300)</p> L'adresse du serveur DXC doit être au format<p><b>[utilisateur@]hôte:port</b> (ex. hamqth.com:7300)</p> DX Cluster Password Mot de passe DX Cluster Invalid Password Mot de passe invalide DXC Server Connection Error Erreur de connexion au serveur DXC Filename is empty Le nom de fichier est vide Cannot write to the file Impossible d'écrire dans le fichier QLog Information Information QLog Exported. Exporté. Exported %n contact(s). %n contact exporté. %n contacts exportés. Chat Error: Erreur de Chat : Filter name is already exists. Ce nom de filtre existe déjà. <b>Rig Error:</b> <b>Erreur Transceiver :</b> <b>Rotator Error:</b> <b>Erreur Rotor :</b> <b>CW Keyer Error:</b> <b>Erreur Keyer CW :</b> The fields <b>%0</b> will not be saved because the <b>%1</b> is not filled. Les champs <b>%0</b> ne seront pas sauvegardés car <b>%1</b> n'est pas renseigné. Your callsign is empty. Please, set your Station Profile Votre indicatif est vide. Veuillez configurer votre profil de station Cannot update QSO Filter Conditions Impossible de mettre à jour les conditions du filtre QSO Please, define at least one Station Locations Profile Veuillez définir au moins un profil d'emplacement de station WSJTX Multicast is enabled but the Address is not a multicast address. Le multicast WSJT-X est activé mais l'adresse n'est pas une adresse multicast. Loop detected. Raw UDP forward uses the same port as the WSJT-X receiving port. Boucle détectée. Le transfert UDP brut utilise le même port que le port de réception WSJT-X. Rig port must be a valid COM port.<br>For Windows use COMxx, for unix-like OS use a path to device Le port du transceiver doit être un port COM valide.<br>Pour Windows, utilisez COMxx, pour les OS de type Unix, utilisez un chemin vers le périphérique Rig PTT port must be a valid COM port.<br>For Windows use COMxx, for unix-like OS use a path to device Le port PTT doit être un port COM valide.<br>Pour Windows, utilisez COMxx, pour les OS de type Unix, utilisez un chemin vers le périphérique <b>TX Range</b>: Max Frequency must not be 0. <b>Plage TX</b> : La fréquence max ne doit pas être 0. <b>TX Range</b>: Max Frequency must not be under Min Frequency. <b>Plage TX</b> : La fréquence max ne doit pas être inférieure à la fréquence min. Rotator port must be a valid COM port.<br>For Windows use COMxx, for unix-like OS use a path to device Le port du rotor doit être un port COM valide.<br>Pour Windows, utilisez COMxx, pour les OS de type Unix, utilisez un chemin vers le périphérique CW Keyer port must be a valid COM port.<br>For Windows use COMxx, for unix-like OS use a path to device Le port du keyer CW doit être un port COM valide.<br>Pour Windows, utilisez COMxx, pour les OS de type Unix, utilisez un chemin vers le périphérique Cannot change the CW Keyer Model to <b>Morse over CAT</b><br>No Morse over CAT support for Rig(s) <b>%1</b> Impossible de changer le modèle de keyer CW en <b>Morse via CAT</b><br>Pas de support Morse via CAT pour le(s) poste(s) <b>%1</b> Cannot delete the CW Keyer Profile<br>The CW Key Profile is used by Rig(s): <b>%1</b> Impossible de supprimer le profil de keyer CW<br>Ce profil est utilisé par le(s) poste(s) : <b>%1</b> Callsign has an invalid format Le format de l'indicatif est invalide Operator Callsign has an invalid format L'indicatif de l'opérateur a un format invalide Gridsquare has an invalid format Le format du Locator est invalide VUCC Grids have an invalid format (must be 2 or 4 Gridsquares separated by ',') Les carrés VUCC ont un format invalide (doit être 2 ou 4 Locators séparés par des virgules) Country must not be empty Le pays ne doit pas être vide CQZ must not be empty La zone CQ ne doit pas être vide ITU must not be empty La zone ITU ne doit pas être vide QObject Importing Database Importation de la base de données Opening Database Ouverture de la base de données Backuping Database Sauvegarde de la base de données Migrating Database Migration de la base de données Starting Application Démarrage de l'application km km miles milles My Rig Mon transceiver Logging Station Callsign Indicatif de la station de log My Gridsquare Mon Locator Operator Name Nom de l'opérateur Operator Callsign Indicatif de l'opérateur My City Ma ville My Country Mon pays My County Mon département/comté My IOTA Mon IOTA My SOTA Mon SOTA My Special Interest Activity Mon activité spéciale My Spec. Interes Activity Info Info activité spéciale My VUCC Grids Mes carrés VUCC My WWFF Mon WWFF My POTA Ref Ma réf. POTA My DARC DOK Mon DOK (DARC) My ITU Ma zone ITU My CQZ Ma zone CQ My DXCC Mon DXCC Connection Refused Connexion refusée Host closed the connection L'hôte a fermé la connexion Host not found Hôte introuvable Timeout Délai d'attente dépassé Network Error Erreur Réseau Internal Error Erreur Interne Cannot connect to DXC Server <p>Reason <b>: Impossible de se connecter au serveur DXC <p>Raison <b> : <b>Imported</b>: %n contact(s) <b>Importé</b> : %n contact <b>Importés</b> : %n contacts <b>Warning(s)</b>: %n <b>Avertissement</b> : %n <b>Avertissements</b> : %n <b>Error(s)</b>: %n <b>Erreur</b> : %n <b>Erreurs</b> : %n Not a valid QLog database Base de données QLog invalide Database version too new (requires newer QLog version) Version de la base trop récente (nécessite une version plus récente de QLog) Database is not QLog Export file Le fichier n'est pas un export QLog TQSL Path Chemin TQSL (Flatpak internal path) (chemin interne Flatpak) Rig Poste Rig PTT PTT Poste Rig rigctld rigctld Poste Rotator Rotor CW Keyer Keyer CW TOTAL Worked TOTAL contactés TOTAL Confirmed TOTAL confirmés Confirmed Confirmé Worked QRZCallbook QRZ.com QRZ.com QRZUploader General Error Erreur générale QSLGalleryDialog QSL Card Gallery Galerie de cartes QSL Search by callsign... Chercher par indicatif... Sort by: Trier par : Export Filtered Exporter filtrés Date (Newest) Date (plus récent) Date (Oldest) Date (plus ancien) Callsign (A-Z) Indicatif (A-Z) Callsign (Z-A) Indicatif (Z-A) %n QSL card(s) %n carte QSL %n cartes QSL All QSL Cards Toutes les cartes QSL Favorites Favoris By Country Par Pays By Date Par Date By Band Par Bande By Mode Par Mode By Continent Par Continent Remove from Favorites Retirer des favoris Add to Favorites Ajouter aux favoris Open Ouvrir Save... Enregistrer... Save QSL Card Enregistrer la carte QSL Export QSL Cards Exporter les cartes QSL Exported %1 of %2 cards %1 cartes sur %2 exportées QSLImportStatDialog QSL Import Summary Résumé de l'importation QSL Summary Résumé Downloaded: Téléchargées : Updated: Mises à jour : Unmatched: Non appariées : Errors: Erreurs : Details Détails New QSLs: Nouvelles QSL : Updated QSOs: QSO mis à jour : Unmatched QSLs: QSL non appariées : QSLPrintLabelDialog Print QSL Labels Imprimer les étiquettes QSL Filter Filtrer Date Range Plage de dates My Callsign Mon indicatif Station Profile Profil de station QSL Sent QSL Envoyée User Filter Filtre utilisateur Label Template Modèle d’étiquette Page Size: Taille de page: Columns: Colonne: Rows: Ligne: Label Width: Largeur de l’étiquette : Label Height: Hauteur de l’étiquette: Left Margin: Marge gauche: Top Margin: Marge supérieure: H Spacing: Espacement horizontal: V Spacing: Espacement vertical: Label Appearance Apparence de l’étiquette Print Label Borders Imprimer les bordures des étiquettes QSOs per Label: QSO par étiquette: Footer Left Text: Texte de pied de page gauche: Footer Right Text: Texte de pied de page droit: Skip Label: Ignorer l’étiquette: Sans Font: Police sans-serif: Mono Font: Police monospace: Callsign Size: Taille de l’indicatif: "To Radio" Size: Taille « To Radio » : "To Radio" Text: Texte « To Radio » : Header Size: Taille de l’en-tête: Data Size: Taille des données: Date Header Text: Texte d’en-tête de date: Date Format: Format de date: Time Header Text: Texte d’en-tête de l’heure: Band Header Text: Texte d’en-tête de bande: Mode Header Text: Texte d’en-tête du mode: QSL Header Text: Texte d’en-tête QSL: Extra Column: Colonne supplémentaire: Extra Column Text Texte de la colonne supplémentaire (DB column name) (nom de colonne BD) No matching QSOs found Aucun QSO correspondant trouvé Page 0 of 0 Page 0 sur 0 Labels: 0 (0 pages) Étiquettes : 0 (0 pages) Print Imprimer Export as PDF Exporter en PDF Custom Personnalisé Empty Vide QSOs matching this station profile QSO correspondant à ce profil de station Labels: %1 (%2 pages) Étiquettes : %1 (%2 pages) Page %1 of %2 Page %1 sur %2 Export PDF Exporter en PDF PDF Files (*.pdf) Fichiers PDF (*.pdf) Mark as Sent Marquer comme envoyé Mark printed/exported QSOs as sent? Marquer les QSO imprimés/exportés comme envoyés ? QSODetailDialog dd/MM/yyyy HH:mm:ss dd/MM/yyyy HH:mm:ss RSTs RSTs RSTr RSTr Mode Mode Time On Heure début Time Off Heure fin Callsign Indicatif TX: TX : Blank Vide MHz MHz RX: RX : Frequency Fréquence Band Bande QTH QTH Name Nom Gridsquare Locator Comment Commentaire My Notes Mes notes about:blank about:blank &Details &Détails Country Pays Cont Cont. ITU ITU CQ CQ State État County Comté Age Âge AF AF AN AN AS AS EU EU NA NA OC OC SA SA VUCC VUCC two or four adjacent Gridsquares, each four characters long, (ex. EN98,FM08,EM97,FM07) deux ou quatre locators adjacents, de quatre caractères chacun (ex. JN18,JN19,JN28,JN29) IOTA IOTA POTA POTA SOTA SOTA WWFF WWFF SIG SIG SIG Info Info SIG FISTS FISTS SKCC SKCC Ten-Ten Ten-Ten UKSMG UKSMG DOK DOK the contacted station's DARC DOK (District Location Code) (ex. A01) le DOK (District Location Code) DARC de la station contactée (ex. A01) FISTS CC FISTS CC E-Mail E-mail URL URL Propagation Mode Mode de propagation Satellite Name Nom du satellite Satellite Mode Mode satellite D&X Stats Stats D&X <b>DXCC Statistics</b> <b>Statistiques DXCC</b> <b>Station Statistics</b> <b>Statistiques de la station</b> M&y Station M&a station Operator Name Nom de l'opérateur Operator Callsign Indicatif de l'opérateur Sig Info Info Sig Rig Équipement Antenna Antenne Power Puissance W W &QSL &QSL Show QSL Card Afficher la carte QSL <b>Yes</b> - an incoming QSL card has been received; the QSO has been confirmed by the online service<br/><b>No</b> - an incoming QSL card has not been received; the QSO has not been confirmed by the online service<br/><b>Requested</b> - the logging station has requested a QSL card; the logging station has requested the QSO be uploaded to the online service<br/> <b>Oui</b> - une carte QSL entrante a été reçue ; le QSO a été confirmé par le service en ligne<br/><b>Non</b> - aucune carte QSL n'a été reçue ; le QSO n'a pas été confirmé par le service en ligne<br/><b>Demandée</b> - la station a demandé une carte QSL ou le téléchargement du QSO vers le service en ligne<br/> - - QSL Sent via QSL envoyée via QSL via QSL via <b>Yes</b> - an outgoing QSL card has been sent; the QSO has been uploaded to, and accepted by, the online service<br/><b>No</b> - do not send an outgoing QSL card; do not upload the QSO to the online service<br/><b>Requested</b> - the contacted station has requested a QSL card; the contacted station has requested the QSO be uploaded to the online service<br/><b>Queued</b> - an outgoing QSL card has been selected to be sent; a QSO has been selected to be uploaded to the online service<br/> <b>Oui</b> - une carte QSL sortante a été envoyée ; le QSO a été accepté par le service en ligne<br/><b>Non</b> - ne pas envoyer de carte QSL ; ne pas envoyer le QSO au service en ligne<br/><b>Demandée</b> - la station contactée a demandé une carte QSL ou l'envoi vers le service en ligne<br/><b>En attente</b> - une carte QSL ou un envoi vers le service en ligne a été mis en file d'attente<br/> Received Reçue eQSL eQSL Manage QSL Card Gérer la carte QSL Paper Papier Sent Envoyée QSLr Message Message QSLr LoTW LoTW QSLs Message Message QSLs &Contest &Concours Contest ID ID du concours RcvNr N° Reçu RcvExch Échange Reçu SendNr N° Envoyé SendExch Échange Envoyé Member: Membre : &Reset &Réinitialiser &Lookup &Recherche No Non Yes Oui Requested Demandée Queued En attente Ignored Ignorée Bureau Bureau Direct Direct Electronic Électronique Submit changes Soumettre les modifications Really submit all changes? Voulez-vous vraiment soumettre toutes les modifications ? QLog Error Erreur QLog Cannot save all changes - internal error Impossible de sauvegarder les modifications - erreur interne Cannot save all changes - try to reset all changes Impossible de sauvegarder - essayez de réinitialiser les modifications QSO Detail Détail du QSO Edit QSO Modifier le QSO Downloading eQSL Image Téléchargement de l'image eQSL Cancel Annuler eQSL Download Image failed: Échec du téléchargement de l'image eQSL : DX Callsign must not be empty L'indicatif DX ne doit pas être vide DX callsign has an incorrect format Le format de l'indicatif DX est incorrect TX Frequency or Band must be filled La fréquence TX ou la bande doit être renseignée TX Band should be La bande TX devrait être RX Band should be La bande RX devrait être DX Grid has an incorrect format Le format du locator DX est incorrect Based on callsign, DXCC Country is different from the entered value - expecting D'après l'indicatif, le pays DXCC diffère de la valeur saisie - attendu : Based on callsign, DXCC Continent is different from the entered value - expecting D'après l'indicatif, le continent DXCC diffère de la valeur saisie - attendu : Based on callsign, DXCC ITU is different from the entered value - expecting D'après l'indicatif, la zone ITU diffère de la valeur saisie - attendu : Based on callsign, DXCC CQZ is different from the entered value - expecting D'après l'indicatif, la zone CQ diffère de la valeur saisie - attendu : VUCC has an incorrect format Le format VUCC est incorrect Based on Frequencies, Sat Mode should be D'après les fréquences, le mode Sat devrait être blank vide Sat name must not be empty Le nom du satellite ne doit pas être vide Own Callsign must not be empty Votre indicatif ne doit pas être vide Own callsign has an incorrect format Le format de votre indicatif est incorrect Own VUCC Grids have an incorrect format Le format de vos locators VUCC est incorrect Based on own callsign, own DXCC ITU is different from the entered value - expecting D'après votre indicatif, votre zone ITU diffère de la valeur saisie - attendu : Based on own callsign, own DXCC CQZ is different from the entered value - expecting D'après votre indicatif, votre zone CQ diffère de la valeur saisie - attendu : Based on own callsign, own DXCC Country is different from the entered value - expecting D'après votre indicatif, votre pays DXCC diffère de la valeur saisie - attendu : Based on SOTA Summit, QTH does not match SOTA Summit Name - expecting D'après le sommet SOTA, le QTH ne correspond pas au nom du sommet - attendu : Based on SOTA Summit, Grid does not match SOTA Grid - expecting D'après le sommet SOTA, le locator ne correspond pas au locator SOTA - attendu : Based on POTA record, QTH does not match POTA Name - expecting D'après la référence POTA, le QTH ne correspond pas au nom POTA - attendu : Based on POTA record, Grid does not match POTA Grid - expecting D'après la référence POTA, le locator ne correspond pas au locator POTA - attendu : Based on SOTA Summit, my QTH does not match SOTA Summit Name - expecting D'après le sommet SOTA, mon QTH ne correspond pas au nom du sommet - attendu : Based on SOTA Summit, my Grid does not match SOTA Grid - expecting D'après le sommet SOTA, mon locator ne correspond pas au locator SOTA - attendu : Based on POTA record, my QTH does not match POTA Name - expecting D'après la référence POTA, mon QTH ne correspond pas au nom POTA - attendu : Based on POTA record, my Grid does not match POTA Grid - expecting D'après la référence POTA, mon locator ne correspond pas au locator POTA - attendu : LoTW Sent Status to <b>No</b> does not make any sense if QSL Sent Date is set. Set Date to 1.1.1900 to leave the date field blank Le statut d'envoi LoTW sur <b>Non</b> est incohérent si une date d'envoi QSL est définie. Mettez la date au 01/01/1900 pour laisser le champ vide Date should be present for LoTW Sent Status <b>Yes</b> Une date doit être renseignée si le statut d'envoi LoTW est <b>Oui</b> eQSL Sent Status to <b>No</b> does not make any sense if QSL Sent Date is set. Set Date to 1.1.1900 to leave the date field blank Le statut d'envoi eQSL sur <b>Non</b> est incohérent si une date d'envoi QSL est définie. Mettez la date au 01/01/1900 pour laisser le champ vide Date should be present for eQSL Sent Status <b>Yes</b> Une date doit être renseignée si le statut d'envoi eQSL est <b>Oui</b> Paper Sent Status to <b>No</b> does not make any sense if QSL Sent Date is set. Set Date to 1.1.1900 to leave the date field blank Le statut d'envoi Papier sur <b>Non</b> est incohérent si une date d'envoi QSL est définie. Mettez la date au 01/01/1900 pour laisser le champ vide Date should be present for Paper Sent Status <b>Yes</b> Une date doit être renseignée si le statut d'envoi Papier est <b>Oui</b> Callbook error: Erreur du callbook : <b>Warning: </b> <b>Attention : </b> Validation Validation Yellow marked fields are invalid.<p>Nevertheless, save the changes?</p> Les champs marqués en jaune sont invalides.<p>Sauvegarder tout de même les modifications ?</p> &Save &Sauvegarder &Edit &Modifier QSOFilterDetail QSO Filter Detail Détail du filtre QSO Filter Name: Nom du filtre : Find QSO which match Trouver les QSO correspondant à All the following conditions Toutes les conditions suivantes Any of the following conditions L'une des conditions suivantes Add Condition Ajouter une condition Equal Égal à Not Equal Différent de Contains Contient Not Contains Ne contient pas Greater Than Supérieur à Less Than Inférieur à Starts with Commence par RegExp Exp. Régulière Remove Supprimer Must not be empty Ne doit pas être vide QSOFilterDialog QSO Filters Filtres de QSO Filters Filtres Add Ajouter Edit Modifier Remove Supprimer Rig No Rig Profile selected Aucun profil de déca (Rig) sélectionné Rigctld Error Erreur Rigctld Initialization Error Erreur d'initialisation Internal Error Erreur interne Cannot open Rig Impossible d'ouvrir le poste RigWidget Form Formulaire RX RX Disconnected Déconnecté MHz MHz Disable Split Désactiver le split RIT: 0.00000 MHz RIT : 0.00000 MHz XIT: 0.00000 MHz XIT : 0.00000 MHz PWR: %1W PWR : %1W RigctldAdvancedDialog Rigctld Advanced Settings Paramètres avancés Rigctld Rigctld Path: Chemin Rigctld : Leave empty for auto-detection Laisser vide pour une auto-détection Browse Parcourir Auto-detect Rigctld path Auto-détecter le chemin de Rigctld Auto-Detect Auto-détection Rigctld Version: Version de Rigctld : Additional Arguments: Arguments supplémentaires : e.g. -v -v for verbose logging ex: -v -v pour un log verbeux Cannot be changed Ne peut pas être modifié Auto Detect Auto-détection rigctld was not found on this system. Please install Hamlib or specify the path manually. rigctld n'a pas été trouvé sur ce système. Veuillez installer Hamlib ou spécifier le chemin manuellement. Executable (*.exe);;All files (*.*) Exécutable (*.exe);;Tous les fichiers (*.*) All files (*) Tous les fichiers (*) Select rigctld executable Sélectionner l'exécutable rigctld Not found Non trouvé RigctldManager rigctld executable not found in /app/bin/. This should not happen in Flatpak build. L'exécutable rigctld est introuvable dans /app/bin/. Cela ne devrait pas arriver avec un build Flatpak. rigctld executable not found. Please install Hamlib or specify the path in Advanced settings. L'exécutable rigctld est introuvable. Veuillez installer Hamlib ou spécifier le chemin dans les paramètres avancés. Hamlib major version mismatch: QLog was compiled with Hamlib %1 but rigctld reports version %2.%3.%4. Rig model IDs are incompatible between major versions. Discordance de version majeure de Hamlib : QLog a été compilé avec Hamlib %1 mais rigctld indique la version %2.%3.%4. Les ID de modèles de postes sont incompatibles entre versions majeures. Port %1 is already in use. Another rigctld or application may be running on this port. Le port %1 est déjà utilisé. Une autre instance de rigctld ou une autre application utilise probablement ce port. rigctld started but not responding on port %1. rigctld est démarré mais ne répond pas sur le port %1. Failed to start rigctld: %1 %2 Échec du démarrage de rigctld : %1 %2 rigctld crashed. rigctld a planté. rigctld timed out. Délai d'attente dépassé pour rigctld. Write error with rigctld. Erreur d'écriture avec rigctld. Read error with rigctld. Erreur de lecture avec rigctld. Unknown rigctld error. Erreur rigctld inconnue. Rotator No Rotator Profile selected Aucun profil de rotor sélectionné Initialization Error Erreur d'initialisation Internal Error Erreur interne Cannot open Rotator Impossible d'ouvrir le rotor RotatorWidget Form Formulaire Az: Az : ° ° Goto Aller à Previous Button Profile Profil de boutons précédent Next Button Profile Profil de boutons suivant QSO LP QSO LP QSO Long Path QSO Grand Chemin (LP) QSO SP QSO SP QSO Short Path QSO Petit Chemin (SP) SettingsDialog Settings Paramètres Station Station Profiles Profils SOTA SOTA IOTA IOTA SOTA (Optional parameter) SOTA (paramètre optionnel) SIG (Optional parameter). SIG (paramètre optionnel). ITU Zone ITU CQZ Zone CQ County Comté IOTA (Optional parameter) IOTA (paramètre optionnel) QTH Name (Optional parameter) Nom du QTH (paramètre optionnel) World Wide Flora & Fauna (Optional parameter) WWFF (paramètre optionnel) Operator name (Optional parameter) Nom de l'opérateur (paramètre optionnel) POTA POTA VUCC Grids (Optional parameter). Ex. EN98,FM08,EM97,FM07 Carrés VUCC (optionnel). Ex: JN18,JN19,IN98 Country Pays Station County Location (Optional parameter) Localisation du comté (paramètre optionnel) SIG Info Info SIG Station Gridsquare (Mandatory parameter) Localisateur de la station (obligatoire) QTH QTH Profile Name Nom du profil WWFF WWFF List of all available Station Profiles Liste de tous les profils de station disponibles SIG Information (Optional parameter) Information SIG (paramètre optionnel) Profile name that is used as the alias for the Callsign, Gridsquare, Operator name, and QTH (required parameter) Nom du profil utilisé comme alias pour l'indicatif, le localisateur, l'opérateur et le QTH (obligatoire) Callsign (Mandatory parameter) Indicatif (paramètre obligatoire) Callsign of operator (Optional parameter, if different from station callsign) Indicatif de l'opérateur (optionnel, si différent de celui de la station) Operator Name Nom de l'opérateur VUCC VUCC Station Callsign Indicatif de la station Gridsquare Locator SIG SIG Operator Callsign Indicatif de l'opérateur Add Ajouter Delete Supprimer DOK DOK Equipment Équipement Antennas Antennes Profiles Profils Description Description Azimuth Beamwidth Largeur de faisceau azimutal Azimuth Offset Offset azimutal Valid range value is 0° - 100° (0° Unspecified) La plage valide est 0° - 100° (0° = non spécifié) Unspecified Non spécifié ° ° List of all available Antennas Liste de toutes les antennes disponibles CW Keyers Clés télégraphiques (CW) Keyer Profiles Profils de manipulateur List of all available CW Keyers Liste de tous les manipulateurs CW disponibles Danger Zone Zone dangereuse <b>⚠ This is a danger zone. Proceed with caution, as actions performed here cannot be undone and may have a significant impact on your log.</b> <b>⚠ Ceci est une zone dangereuse. Procédez avec prudence, car les actions effectuées ne peuvent pas être annulées et peuvent avoir un impact important sur votre journal.</b> Delete All QSOs Supprimer tous les QSOs Delete All Passwords from the Secure Store Supprimer tous les mots de passe du coffre sécurisé Model Modèle Keyer Mode Mode du manipulateur Swap Paddles Inverser les palettes Paddle Only Sidetone Sidetone (palettes uniquement) Sidetone Freq: Fréq. sidetone : Default Speed Vitesse par défaut N/A N/D WPM MPM (WPM) Port Port Use COMxx for Window or path to COM port under Unix-like OS Utiliser COMxx pour Windows ou le chemin vers le port sous Linux/Unix Baudrate Vitesse (Bauds) 115200 115200 57600 57600 38400 38400 19200 19200 9600 9600 4800 4800 2400 2400 1200 1200 Host Name Nom d'hôte HamLib does not support to change a destination port. HamLib ne permet pas de changer le port de destination. CW Shortcut Profiles Profils de raccourcis CW List of all available CW Shortcuts Profiles Liste de tous les profils de raccourcis CW disponibles F1 F1 Short Desciption of the Button (up to 7 chars) Description courte du bouton (7 car. max) F2 F2 F3 F3 F4 F4 F5 F5 F6 F6 F7 F7 Rigs Transceivers Interface Interface Minimum and maximum TX frequencies. Specific ranges are derived from allowed Band in the Setting. Fréquences TX mini et maxi. Les plages spécifiques sont dérivées des bandes autorisées dans les paramètres. TX Range Plage TX - - Offsets Offsets Enter manually RIT or Transverter Offset Entrer manuellement l'offset RIT ou Transverter MHz MHz Enter manually XIT or Transverter offset Entrer manuellement l'offset XIT ou Transverter TX: TX : Default PWR Puissance par déf. Enter default PWR (ex. when Rig is disconnected) Entrer la puissance par défaut (ex: si poste déconnecté) Blank Vide W W Assigned CW Keyer Manipulateur CW assigné Rig Features Fonctionnalités du poste Mode Mode VFO VFO Freq Fréq. QSY Wiping Effacement sur QSY Power Puissance PTT State État du PTT TX Offset (XIT) Offset TX (XIT) RX Offset (RIT) Offset RX (RIT) Split CW Keyer Speed Vitesse manipulateur CW CW Speed Sync Synchro vitesse CW DX Spots to Rig Spots DX vers le poste Start rigctld daemon to share rig with other applications (e.g. WSJT-X) Lancer le démon rigctld pour partager le poste avec d'autres applis (ex: WSJT-X) Share Rig via port Partager le poste via le port Advanced... Avancé... Rig Port Port du poste CIV Addr Adresse CI-V Auto Auto Flow Control Contrôle de flux Parity Parité PTT Type Type de PTT PTT Port Port PTT RTS RTS DTR DTR Data Bits Bits de données 8 8 7 7 6 6 5 5 Stop Bits Bits d'arrêt 1 1 2 2 Port Type Type de port Poll Interval Intervalle d'interrogation ms ms List of all available Rigs Liste de tous les transceivers disponibles Rotators Rotors Serial Série Network Réseau User Buttons Profiles Profils de boutons utilisateur Button 1 Bouton 1 Button 2 Bouton 2 Button 3 Bouton 3 Button 4 Bouton 4 Callbook Nomenclature (Callbook) Query Order Ordre de requête Primary Primaire Secondary Secondaire HamQTH HamQTH Username Utilisateur Password Mot de passe QRZ.com QRZ.com <b>Notice:</b> At least a QRZ XML Subscription is recommended to access detailed information. Without a subscription, you will obtain limited data from QRZ, such as missing grid and other fields. <b>Note :</b> Un abonnement QRZ XML est recommandé pour accéder aux informations détaillées. Sans abonnement, les données seront limitées (localisateur ou autres champs manquants). Web Lookup Button Bouton de recherche Web URL URL Specify the URL to use for quick search. The <DXCALL> macro will be replaced by the current callsign Spécifier l'URL pour la recherche rapide. La macro <DXCALL> sera remplacée par l'indicatif courant Test URL with your Callsign Tester l'URL avec votre indicatif Test Test Clubs Clubs Active Lists Listes actives Sync && QSL Sync && QSL ClubLog ClubLog E-Mail E-Mail QSOs are uploaded immediately Les QSOs sont envoyés immédiatement Immediately Upload Envoi immédiat eQSL eQSL HRDLog HRDLog Callsign Indicatif It is not a password. It is the upload code received via email after the registration to HRDLOG..net Ce n'est pas un mot de passe. C'est le code d'envoi reçu par email après l'inscription sur HRDLOG.net Upload Code Code d'envoi If it is enabled and Rig is connected then QLog periodically sends On-Air messages to HRDLog Si activé et le poste connecté, QLog envoie périodiquement des messages "On-Air" à HRDLog Send On-Air Envoyer "On-Air" LoTW LoTW TQSL Path Chemin TQSL Browse Parcourir Using an internal TQSL instance Utiliser une instance TQSL interne Default API Key Clé API par défaut Callsign-specific API Keys Clés API par indicatif Wavelog Wavelog API Key Clé API Endpoint Point d'accès (Endpoint) Others Autres DXCC DXCC Status Confirmed By Statut confirmé par Paper Papier Chat Chat <b>Security Notice:</b> QLog stores all passwords in the Secure Storage. Unfortunately, ON4KST uses a protocol where this password is sent over an unsecured channel as plaintext.</p><p>Please exercise caution when choosing your password for this service, as your password is sent over an unsecured channel in plaintext form.</p> <b>Note de sécurité :</b> QLog stocke les mots de passe de manière sécurisée. Hélas, ON4KST utilise un protocole où le mot de passe transite en clair sur un canal non sécurisé.</p><p>Soyez prudent lors du choix de votre mot de passe pour ce service.</p> Bands Bandes Modes Modes The '>' character is interpreted as a marker for the initial cursor position in the Report column. <br/>Ex.: '5>9' means the cursor will be positioned on the second character Le caractère '>' sert de marqueur pour la position initiale du curseur dans la colonne Report. <br/>Ex: '5>9' positionnera le curseur sur le second caractère. Wsjtx WSJT-X Raw UDP Forward Redirection UDP brute <p>List of IP addresses to which QLog forwards raw UDP WSJT-X packets.</p>The IP addresses are separated by a space and have the form IP:PORT <p>Liste des adresses IP vers lesquelles QLog redirige les paquets UDP bruts de WSJT-X.</p>Les adresses sont séparées par des espaces (format IP:PORT). ex. 192.168.1.1:1234 192.168.2.1:1234 ex: 192.168.1.1:1234 192.168.2.1:1234 Port Port Port where QLog listens an incoming traffic from WSJT-X Port sur lequel QLog écoute le trafic provenant de WSJT-X Join Multicast Rejoindre le Multicast Enable/Disable Multicast option for WSJTX Activer/Désactiver l'option Multicast pour WSJT-X Multicast Address Adresse Multicast <DXCALL> = DX Callsign <NAME> = DX Operator Name <RST> = Report 599 <RSTN> = Report 5NN <GREETING> = Greenting GM/GA/GE <QTH> = QTH <MYCALL> = My Callsign <MYNAME> = My Name <MYQTH> = My QTH <MYLOCATOR> = My Gridsquare <MYGRID> = My Gridsquare <MYSIG> = My SIG <MYSIGINFO> = My SIG Information <MYIOTA> = My IOTA <MYSOTA> = MY SOTA <MYWWFT> = My WWFT <MYVUCC> = MY VUCC <MYPWR> = My Power in W <EXCHSTR> = Contest Exchange Message <EXCHNR> = Contest Exchange Serial Number <EXCHNRN> = Contest Exchange Serial Number (9→N, 0→T) <+> = Speed +5 WPM (<++> = +10 WPM, etc.) <-> = Speed -5 WPM (<--> = -10 WPM, etc.) <DXCALL> = Indicatif du correspondant <NAME> = Prénom du correspondant <RST> = Report 599 <RSTN> = Report 5NN <GREETING> = Salutations (GM/GA/GE) <QTH> = QTH distant <MYCALL> = Mon indicatif <MYNAME> = Mon prénom <MYQTH> = Mon QTH <MYLOCATOR> = Mon localisateur (Grid) <MYGRID> = Mon localisateur (Grid) <MYSIG> = Mon SIG <MYSIGINFO> = Mes infos SIG <MYIOTA> = Mon IOTA <MYSOTA> = Mon SOTA <MYWWFT> = Mon WWFF <MYVUCC> = Mon VUCC <MYPWR> = Ma puissance (W) <EXCHSTR> = Message d'échange concours <EXCHNR> = Numéro de série concours <EXCHNRN> = Numéro de série concours (9→N, 0→T) <+> = Vitesse +5 MPM (<++> = +10 MPM, etc.) <-> = Vitesse -5 MPM (<--> = -10 MPM, etc.) RX: RX : Leave empty for auto-detection Laisser vide pour l'auto-détection Auto-detect TQSL path Auto-détection du chemin TQSL Auto-Detect Auto-détection TQSL Version Version de TQSL Specify Multicast Address. <br>On some Linux systems it may be necessary to enable multicast on the loop-back network interface. Spécifier l'adresse Multicast. <br>Sur certains systèmes Linux, il peut être nécessaire d'activer le multicast sur l'interface de boucle locale (loop-back). TTL TTL Time-To-Live determines the range<br> over which a multicast packet is propagated in your intranet. Le TTL (Time-To-Live) détermine la portée de propagation<br> d'un paquet multicast sur votre intranet. Color CQ Spots Colorer les spots CQ Enable/Disable sending color-coded status indicators back to WSJT-X for each callsign calling CQ Activer/Désactiver l'envoi d'indicateurs d'état colorés à WSJT-X pour chaque indicatif appelant CQ Notifications Notifications LogID LogID <p>Assigned LogID to the current log.</p>The LogID is sent in the Network Nofitication messages as a unique instance identified.<p> The ID is generated automatically and cannot be changed</> <p>LogID assigné au carnet actuel.</p>Le LogID est envoyé dans les notifications réseau comme identifiant unique d'instance.<p>L'ID est généré automatiquement et ne peut être modifié.</p> DX Spots Spots DX <p> List of IP addresses to which QLog sends UDP notification packets with DX Cluster Spots.</p>The IP addresses are separated by a space and have the form IP:PORT <p>Liste des adresses IP vers lesquelles QLog envoie les notifications UDP des spots du cluster DX.</p>Les adresses sont séparées par des espaces (format IP:PORT). Spot Alerts Alertes de spots <p> List of IP addresses to which QLog sends UDP notification packets about user Spot Alerts.</p>The IP addresses are separated by a space and have the form IP:PORT <p>Liste des adresses IP vers lesquelles QLog envoie les notifications UDP pour les alertes de spots utilisateur.</p>Les adresses sont séparées par des espaces (format IP:PORT). QSO Changes Modifications de QSO <p> List of IP addresses to which QLog sends UDP notification packets about a new/updated/deleted QSO in the log.</p>The IP addresses are separated by a space and have the form IP:PORT <p>Liste des adresses IP vers lesquelles QLog envoie les notifications UDP lors de l'ajout/modification/suppression d'un QSO.</p>Les adresses sont séparées par des espaces (format IP:PORT). Wsjtx CQ Spots Spots CQ WSJT-X <p> List of IP addresses to which QLog sends UDP notification packets with WSJTX CQ Spots.</p>The IP addresses are separated by a space and have the form IP:PORT <p>Liste des adresses IP vers lesquelles QLog envoie les notifications UDP des spots CQ de WSJT-X.</p>Les adresses sont séparées par des espaces (format IP:PORT). Rig Status État du poste <p> List of IP addresses to which QLog sends UDP notification packets when Rig State changes.</p>The IP addresses are separated by a space and have the form IP:PORT <p>Liste des adresses IP vers lesquelles QLog envoie les notifications UDP lors d'un changement d'état du poste.</p>Les adresses sont séparées par des espaces (format IP:PORT). GUI Interface (GUI) Date Format Format de date System Système Custom Personnalisé <a href="https://doc.qt.io/qt-6/qdate.html#fromString-1">Time Format Documentation</a> <a href="https://doc.qt.io/qt-6/qdate.html#fromString-1">Documentation du format d'heure</a> Time Format Format d'heure 24-hour 24 heures AM/PM AM/PM Unit System Système d'unités Metric Métrique Imperial Impérial Special - Omnirig Spécial - Omnirig Cannot be changed Ne peut pas être modifié Name Nom Report Report State État / Province Start (MHz) Début (MHz) End (MHz) Fin (MHz) SAT Mode Mode SAT Disabled Désactivé None Aucun Hardware Matériel (Hard) Software Logiciel (Soft) No Non Even Pair Odd Impair Mark Mark Space Space Dummy Dummy (fictif) Morse Over CAT Morse via CAT WinKey WinKey CWDaemon CWDaemon FLDigi FLDigi Single Paddle Simple palette IAMBIC A IAMBIC A IAMBIC B IAMBIC B Ultimate Ultimatice High Haut (High) Low Bas (Low) Press <b>Modify</b> to confirm the profile changes or <b>Cancel</b>. Appuyez sur <b>Modifier</b> pour confirmer les changements de profil ou sur <b>Annuler</b>. Modify Modifier Must not be empty Ne doit pas être vide Select File Sélectionner un fichier Auto Detect Auto-détection TQSL was not found on this system. Please install TQSL or specify the path manually. TQSL n'a pas été trouvé sur ce système. Veuillez installer TQSL ou spécifier le chemin manuellement. Not found Non trouvé Rig sharing is only available for Hamlib driver Le partage du poste n'est disponible qu'avec le pilote Hamlib Rig sharing is not available for network connection Le partage du poste n'est pas disponible pour les connexions réseau Delete Passwords Supprimer les mots de passe All passwords have been deleted Tous les mots de passe ont été supprimés Deleting all QSOs... Suppression de tous les QSOs... Error Erreur Failed to delete all QSOs. Impossible de supprimer tous les QSOs. members membres Required internet connection during application start Connexion internet requise au démarrage de l'application ShortcutEditorModel Description Description Shortcut Raccourci Conflict with a built-in shortcut Conflit avec un raccourci intégré Conflict with a user-defined shortcut Conflit avec un raccourci utilisateur ShowUploadDialog QSOs to Upload QSO à envoyer Selected QSO QSO sélectionné Upload Envoyer SmartSearchBox Search Rechercher StatisticsWidget Statistics Statistiques Date Range Plage de dates to au Band Bande User Filter Filtre utilisateur Confirmed by Confirmé par LoTW LoTW eQSL eQSL Paper Papier Graph Type Type de graphique QSOs per QSO par Percents Pourcentages Top 10 Top 10 Histogram Histogramme Show on Map Afficher sur la carte My Callsign Mon indicatif My Gridsquare Mon locator My Rig Ma station My Antenna Mon antenne Sun Dim Mon Lun Tue Mar Wed Mer Thu Jeu Fri Ven Sat Sam Not specified Non spécifié Confirmed Confirmé Not Confirmed Non confirmé No User Filter Aucun filtre utilisateur Over 50000 QSOs. Display them? Plus de 50 000 QSO. Les afficher ? Rendering QSOs... Génération des QSO... Year Année Month Mois Day in Week Jour de la semaine Hour Heure Mode Mode Continent Continent Propagation Mode Mode de propagation Confirmed / Not Confirmed Confirmé / Non confirmé Countries Pays Big Gridsquares Grands carrés Locator Distance Distance QSOs QSO Confirmed/Worked Grids Locators confirmés/contactés ODX ODX All Tout TCIRigDrv Rig 0 Poste 0 Rig 1 Poste 1 Rig 2 Poste 2 Rig 3 Poste 3 Error Occurred Une erreur est survenue Rig status changed Le statut du poste a changé Rig is not connected Le poste n'est pas connecté TimestampFormatDelegate Blank Vide ToAllTableModel Time Heure Spotter Spotter Message Message UploadQSODialog Upload QSOs Envoyer les QSO eQSL eQSL LoTW LoTW QRZ.com QRZ.com Clublog Clublog HRDLog HRDLog Wavelog Wavelog Filters Filtres Station Profile Profil de station My Callsign Mon indicatif Gridsquare Locator Include QSOs Status Inclure le statut des QSO Under normal circumstances this status means <b>"do not send"</b>.<br/>However, it may sometimes be wanted to ignore this setting when sending a QSL. En temps normal, ce statut signifie <b>"ne pas envoyer"</b>.<br/>Toutefois, il peut être souhaitable d'ignorer ce réglage lors de l'envoi d'une QSL. No Non Under normal circumstances this status means <b>"Ignore/Invalid"</b>.<br/>However, it may sometimes be wanted to ignore this setting when sending a QSL. En temps normal, ce statut signifie <b>"Ignorer/Invalide"</b>.<br/>Toutefois, il peut être souhaitable d'ignorer ce réglage lors de l'envoi d'une QSL. Ignore Ignorer None Aucun QSLs Message Message des QSL Comment Commentaire QSL Message Field Champ de message QSL QTH Profile Profil QTH This option deletes all QSOs in the Clublog<br>and based on filter, it uploads all QSOs regardless of their status. Cette option supprime tous les QSO sur Clublog<br>et, selon le filtre, renvoie tous les QSO quel que soit leur statut. Clear Clublog and reupload QSOs Vider Clublog et renvoyer les QSO LoTW / TQSL LoTW / TQSL TQSL Station Location Emplacement de la station TQSL Station Profile ID ID du profil de station Reupload All Tout renvoyer Show QSOs... Afficher les QSO... &Upload &Envoyer Unspecified Non spécifié Hide QSOs... Masquer les QSO... Uploading to %1 Envoi vers %1 Cancel Annuler QLog Warning - %1 Avertissement QLog - %1 Cannot update QSO Status Impossible de mettre à jour le statut du QSO Cannot upload the QSO(s): Impossible d'envoyer le(s) QSO : QLog Information Information QLog No QSO found to upload. Aucun QSO trouvé pour l'envoi. QSO(s) were uploaded to the selected services Le(s) QSO a/ont été envoyé(s) aux services sélectionnés Time Heure Callsign Indicatif Mode Mode Upload to Envoyer vers The values below will be used when an input record does not contain the ADIF values Les valeurs ci-dessous seront utilisées lorsqu'un enregistrement ne contient pas les valeurs ADIF Any Tous Location callsign (%1) and grid (%2) do not match selected filters L'indicatif de l'emplacement (%1) et le locator (%2) ne correspondent pas aux filtres sélectionnés Location callsign (%1) does not match selected callsign (%2) L'indicatif de l'emplacement (%1) ne correspond pas à l'indicatif sélectionné (%2) Location grid (%1) does not match selected grid (%2) Le locator de l'emplacement (%1) ne correspond pas au locator sélectionné (%2) Unknown Inconnu Service is not configured properly.<p> Please, use <b>Settings</b> dialog to configure it.</p> Le service n'est pas configuré correctement.<p> Veuillez utiliser la boîte de dialogue <b>Paramètres</b> pour le configurer.</p> UserListModel Callsign Indicatif Gridsquare Locator Distance Distance Azimuth Azimut Comment Commentaire WCYTableModel Time Heure K K expK expK A A R R SFI SFI SA SA GMF GMF Au Au WWVTableModel Time Heure SFI SFI A A K K Info Info WsjtxFilterDialog WSJTX Filters Filtres WSJTX General Filters Filtres généraux Log Status Statut du log New Band Nouvelle bande New Mode Nouveau mode New Entity Nouvelle entité (DXCC) New Slot Nouveau slot (bande/mode) Worked Contacté Confirmed Confirmé Continent Continent North America Amérique du Nord Europe Europe South America Amérique du Sud Africa Afrique Antarctica Antarctique Asia Asie Oceania Océanie Distance more than Distance supérieure à SNR better than SNR meilleur que dB dB Extended Filters Filtres étendus Member Membre No Club List is enabled Aucune liste de club n'est activée WsjtxTableModel Callsign Indicatif Gridsquare Locator Distance Distance SNR SNR Last Activity Dernière activité Last Message Dernier message Member Membre WsjtxWidget Form Formulaire Filtered Filtré Column Visibility... Visibilité des colonnes... Which columns should be displayed Quelles colonnes doivent être affichées Filter... Filtrer... Filter Spots Filtrer les spots main Run with the specific namespace. Exécuter avec l'espace de noms (namespace) spécifique. namespace espace de noms Translation file - absolute or relative path and QM file name. Fichier de traduction - chemin absolu ou relatif et nom du fichier QM. path/QM-filename chemin/nom-du-fichier-QM Set language. <code> example: 'en' or 'en_US'. Ignore environment setting. Définir la langue. <code> exemple : 'en' ou 'en_US'. Ignore les paramètres d'environnement. code code Writes debug messages to the debug file Écrit les messages de débogage dans le fichier de debug Process pending database import (internal use) Traiter l'importation de base de données en attente (usage interne) Force update of all value lists (DXCC, SATs, etc.) Forcer la mise à jour de toutes les listes (DXCC, SATs, etc.) ================================================ FILE: i18n/qlog_it.ts ================================================ ActivityEditor Activity Editor Editor delle attività Activity Name Nome dell'attività Layout Layout Window Arrangement: Disposizione delle finestre: Saved Salvato Clear Cancella Available Fields Campi disponibili List of fields that can be used Elenco di campi che possono essere utilizzati Row A Riga A Move selected fields from the Available Fields List to the Row A Sposta i campi selezionati dall'elenco dei campi disponibili alla riga A Remove selected field from the Row A Elimina il campo selezionato dalla riga A List of fields in the first variable row Elenco dei campi nella prima riga modificabile Change the order. Move selected field up Cambia ordine. Muovi campo sesezionato in alto Change the order. Move selected field down Cambia ordine. Muovi campo sesezionato in basso Row B Riga B Move selected fields from the Available Fields List to the Row B Sposta i campi selezionati dall'elenco dei campi disponibili alla riga B Remove selected field from the Row B Elimina il campo selezionato dalla riga B List of fields in the second variable row Elenco dei campi nella seconda riga modificabile Column A Colonna A List of fields in the first QSO Detail Column Elenco dei campi nella prima colonna dei dettagli del QSO Column B Colonna B List of fields in the second QSO Detail Column Elenco dei campi nella seconda colonna dei dettagli del QSO Column C Colonna C List of fields in the third QSO Detail Column Elenco dei campi nella terza colonna dei dettagli del QSO New QSO Rows Righe del Nuovo QSO New QSO Detail Columns Colonne Dettagli del Nuovo QSO Values Valori Profiles Profili If unchecked, the profile does not change Se non selezionato, il profilo non cambia Station Stazione Antenna Antenna Rig Radio Connect automatically Connetti automaticamente Connect Connette Rotator Rotore Fields Campi Must not be empty Non deve essere vuoto Unsaved Non Salvato AlertRuleDetail Alert Rule Detail Dettaglio Regola dell'Allarme Rule Name Nome della Regola Enabled Abilitato Sources Sorgente DX Cluster DX Cluster WSJTX WSJTX DX DX Worked Lavorato New Slot Nuovo Slot Confirmed Confermato New Entity Nuova Entità New Mode Nuovo Modo New Band Nuova Banda DX Callsign DX Callsign Use Perl-like regular expression Utilizza espressioni regolari simili a Perl .* .* Country Nazione Log Status Stato del Log Spot Comment Spot Comment ITU ITU CQZ CQZone IOTA IOTA SOTA SOTA POTA POTA WWFF WWFF Special Programs Speciale Modes Modi FTx (FT8/FT4) FTx (FT8/FT4) Phone Fonia CW CW Digital Digital Bands Banda Continent Continente North America Nord America Africa Africa Antarctica Antartide South America Sud America Asia Asia Europe Europa Oceania Oceanía Member Membro Spotter Spotter All Tutti Must not be empty Non deve essere vuoto No Club List is enabled Nessuna Club List è abilitata AlertSettingDialog Alerts Rules Regole sugli avvisi Rules Regole Add Aggiungi Edit Modifica Remove Rimuovi Name Nome State Stato AlertTableModel Rule Name Nome della Regola Callsign Indicativo Frequency Frequenza Mode Modo Updated Aggiornato Last Update Último Aggiornamento Last Comment Último Commento Member Membro AlertWidget Alerts Avvisi Clear older than Cancella più vecchio di Never Mai min(s) min(s) Edit Rules Modifica Regole Column Visibility... Visibilità della colonna... Clear Cancella AwardsDialog Awards Diplomi Options Opzioni Award Diploma My DXCC Entity Mia Entità DXCC User Filter Filtro Utente Confirmed by Confermato da LoTW LoTW eQSL eQSL Paper Cartaceo Mode Modo CW CW Phone Fonia Digi Digi Not-Worked Only Solo non lavorato Not-Confirmed Only Non confermato Show Mostra DXCC DXCC ITU ITU WAC WAC WAZ WAZ WAS WAS WPX WPX IOTA IOTA POTA Hunter POTA Hunter POTA Activator POTA Activator SOTA SOTA WWFF WWFF Gridsquare 2-Chars Gridsquare 4-Chars Gridsquare 6-Chars Gridsquare %1-Chars US Counties Contee degli Stati Uniti Russian Districts Distretti della Russia Japanese Cities/Ku/Guns Città giapponesi / Ku / Gun NZ Counties Contee della Nuova Zelanda Spanish DMEs DME spagnoli Ukrainian Districts Distretti dell’Ucraina No User Filter Nessun filtro utente DELETED Eliminato North America Nord America South America South America Europe Europa Africa Africa Oceania Oceanía Asia Asia Antarctica Antartide AwardsTableModel Slots: Slot: Confirmed Confermato Worked Lavorato Still Waiting In attesa BandmapWidget Form Create an additional Bandmap Window Creare una finestra aggiuntiva di Bandmap Clear older Cancella più vecchio di Clear All Cancella Tutto Clear the current band Cancellare la banda attuale Never Mai min(s) min(s) Bandmap Bandmap Show Band Mostra Banda Center RX Centra RX Show Emergency Frequencies Mostra frequenze di emergenza SOS CWCatKey No Rig is connected Nessuna Radio connessa Rig does not support Morse over CAT La radio non supporta Morse over CAT Cannot send Text to Rig Impossibile inviare Testo alla Radio Keyer is not connected Keyer non connesso Rig is not connected Radio non connessa Cannot set Keyer Speed Impossibile impostare velocità Keyer Cannot stop Text Sending Impossibile fermare invio Testo CWConsoleWidget Form CW Keyer Profile Profili Keyer CW Shortcuts profile Profilo Scorciatoie Speed Velocità N/A N/A WPM WPM Sent text Testo inviato Echoed text Testo ripetuto &CW &CW Text to send Testo da inviare Switch between sending <b>words</b> individually (separated by spaces)<br> and sending the entire text as a <b>whole</b> (separated by a new line). Passa dall'invio di <b>parole</b> singolarmente (separate da spazi)<br> all'invio dell'intero testo come un'unica <b>parola</b> (separata da una nuova riga). F1 F2 F3 F4 F5 F6 F7 Halt Ferma CW Console - Halt Sending Console CW: Ferma Clear Sent and Echo Console Cancella Inviati ed Echo Console Immediately stop CW sending Interrompere immediatamente la trasmissione CW Clear Cancella Rig must be connected La radio deve essere connessa Word Parola Whole Tutto CWDaemonKey Keyer is not connected Keyer non connesso Cannot send Text Impossibile inviare il Testo Cannot set Keyer Speed Impossibile impostare velocità Keyer Cannot stop Text Sending Impossibile fermare invio Testo CWFldigiKey Connected device is not FLDigi Il dispositivo connesso non è FLDigi Keyer is not connected Keyer non connesso Cannot send Text to FLDigi Impossibile inviare Testo a FLDigi Cannot send the Clear command to FLDigi Impossibile inviare comando Clear a FLDigi Cannot send the TX command to FLDigi Impossibile inviare comando TX a FLDigi Cannot send the Text command to FLDigi Impossibile inviare comando Text a FLDigi Cannot send the Stop command to FLDigi Impossibile inviare comando Stop a FLDigi Cannot send the Abort command to FLDigi Impossibile inviare comando Abort a FLDigi Cannot receive data from FLDigi Impossibile ricevere dati da FLDigi FLDigi connection timeout Timeout connessione FLDigi FLDigi connection error Errore di connessione FLDigi FLDigi command error Errore comando FLDigi CWKeyer No CW Keyer Profile selected Nessun Profilo Keyer CW selezionato Initialization Error Errore di inizializzazione Internal Error Errore Interno Connection Error Errore di connessione Cannot open the Keyer connection Impossibile aprire la connessione Keyer CWWinKey Connected device is not WinKey Il dispositivo connesso non è Winkey Cannot send Text to Rig Impossibile inviare Testo alla Radio Keyer is not connected Keyer non connesso Communication Error Errore di comunicazione Cannot set Keyer Speed Impossibile impostare velocità Keyer Cannot stop Text Sending Impossibile fermare invio Testo 4000 Hz 4000 Hz 2000 Hz 4000 Hz {2000 ?} 1333 Hz 4000 Hz {1333 ?} 1000 Hz 4000 Hz {1000 ?} 800 Hz 4000 Hz {800 ?} 666 Hz 4000 Hz {666 ?} 571 Hz 4000 Hz {571 ?} 500 Hz 4000 Hz {500 ?} 444 Hz 4000 Hz {444 ?} 400 Hz 4000 Hz {400 ?} CabrilloExportDialog Cabrillo Export Esporta Cabrillo File: File: Browse Cerca Contest Contest Format Template: Modello di formato: Manage Gestisci User Filter: Filtro utente: Date/Time: Data/Ora: yyyy-MM-dd HH:mm - - UTC Station & Categories Stazione e categorie Callsign: Indicativo: Operators: Operatori: Band: Banda: Mode: Modo: Power: Potenza: Operator: Operatore: Assisted: Assistito: Station: Stazione: Transmitter: Trasmettitore: Time: Ora: Overlay: Transmitter ID: Additional Aggiuntivo Name: Nome: Email: Email: Address: Indirizzo: Max 6 lines Max 6 righe Gridsquare Griglia Location: Posizione: Club: Club: Soapbox: Commento libero: Off-Time: yyyy-mm-dd hhmm yyyy-mm-dd hhmm &Export &Esporta Cabrillo Files (*.log);;CBR Files (*.cbr);;All Files (*) File Cabrillo (*.log);;File CBR (*.cbr);;Tutti i file (*) QSO(s): %1 QSO(s): %1 QSOs: ? QSOs: ? QLog Warning Avviso QLog Please select a contest template. Selezionare un modello di contest. Please select an output file. Selezionare un file di output. No callsign available. Check your filters. Nessun nominativo disponibile. Controlla i filtri. QLog Error Errore di QLog Failed to prepare export query. Impossibile preparare la query di esportazione. Failed to query QSOs for export. Impossibile interrogare i QSO per l’esportazione. Cannot open file %1 for writing. Impossibile aprire il file %1 in scrittura. Exporting Cabrillo... Esportazione Cabrillo… QLog Information Informazioni QLog Exported %n QSO(s) to Cabrillo file. Esportati %n QSO nel file Cabrillo. CabrilloFormat All Bands Tutte le Bande 2m (144 MHz) 2m (144 MHz) 1.25m (222 MHz) 1.25m (222 MHz) 70cm (432 MHz) 70cm (432 MHz) 33cm (902 MHz) 33cm (902 MHz) 23cm (1.2 GHz) 23cm (1.2 GHz) Light Luce VHF 3-Band VHF a 3 bande VHF FM Only Solo VHF FM Digital Digital Mixed Misto High High Low Low QRP Single Operator Operatore singolo Multi Operator Multi operatore Check Log Controlla log Non-Assisted Non assistito Assisted Assistito Fixed Fisso Mobile Mobile Portable Rover Rover Limited Rover Unlimited Expedition Spedizione HQ School Scuola Distributed Distribuito One Uno Two Due Limited Limitato Unlimited Illimitato SWL SWL 6 Hours 6 ore 12 Hours 12 ore 24 Hours 24 ore Classic Classico Rookie TB Wires Novice/Tech Novizio/Tecnico Over 50 Oltre 50 Text (left-aligned) Testo (allineato a sinistra) Frequency (kHz) Frequenza (kHz) Time (HHMM) Ora (HHMM) Date (YYYY-MM-DD) Data (AAAA-MM-GG) RST Short (drop last digit) RST breve (senza l’ultima cifra) Uppercase Maiuscolo Mode (Cabrillo) Modalità (Cabrillo) Zero-Padded Nr. Numero con zeri Transmitter ID CabrilloTemplateDialog Cabrillo Template Manager Gestore modelli Cabrillo Import template Importa modello Import Importa Export template Esporta modello Export Esporta New template Nuovo modello New Nuovo Copy existing template Copia modello esistente Copy Copia Delete template Elimina modello Delete Template Name: Nome del modello: Contest Name: Nome del contest: Default Mode: Modalità predefinita: QSO Line Columns: Colonne riga QSO: Contest name as required by the rules. It is possible to enter a custom string if it is not included in the list. Nome del contest secondo le regole. È possibile inserire una stringa personalizzata se non è inclusa nell’elenco. Seq. N. QSO Field Campo QSO Formatter Formattatore Width Larghezza Label Etichetta Add line Aggiungi riga Add Aggiungi Remove selected line Rimuovi riga selezionata Remove Rimuovi New Template Nuovo modello Copy - %1 Copia – %1 Delete Template Elimina modello Delete template '%1'? Eliminare il modello «%1»? Import Template Importa modello QLog Cabrillo Template (*.qct) Modello Cabrillo QLog (*.qct) Import Failed Importazione fallita Export Template Esporta modello Export Failed Esportazione fallita Failed to write file: %1 Impossibile scrivere il file: %1 File not found: %1 File non trovato: %1 Cannot open file: %1 Impossibile aprire il file: %1 Invalid template file: missing name File modello non valido: nome mancante QLog Error Errore di QLog Cannot start database transaction. Impossibile avviare la transazione del database. QLog Warning Avviso QLog Cannot save template '%1': %2 Impossibile salvare il modello «%1»: %2 CallbookManager <p>The secondary callbook will be used</p> <p>Sarà usato il callbook secondario</p> ChatWidget ON4KST Chat ON4KST Chat Chat Room Chat Room Connect Connette New Nuovo QLog Warning Avviso QLog ON4KST Chat is not configured properly.<p> Please, use <b>Settings</b> dialog to configure it.</p> La chat ON4KST non è configurata correttamente.<p> Utilizza la finestra di dialogo <b>Impostazioni</b> per configurarla.</p> CheckBoxDelegate Enabled Abilitato Disabled Disabilitato ClockWidget Form Sunrise Alba Sunset Tramonto N/A N/A CloudlogUploader Invalid API Key Chiave API non valida ClubLogUploader Clublog Operation for Callsign %1 failed.<br>%2 Operazione Clublog per il nominativo %1 non riuscita.<br>%2 ColumnSettingDialog Column Visibility Setting Impostazione visibilità colonna General Generale My Info Mie informazioni QSL && Callbooks QSL && Callbooks Members Membri Conditionals Condizionali Contests Contests Others Altri Done Fatto ColumnSettingGenericDialog Unselect All Deseleziona tutto Select All Seleziona tutto ColumnSettingSimpleDialog Column Visibility Setting Impostazione visibilità colonna Done Fatto DBSchemaMigration DXCC Entities Entità DXCC Sats Info Info Satelliti SOTA Summits SOTA Summits WWFF Records WWFF Records IOTA Records IOTA Records POTA Records POTA Records Membership Directory Records Registro dell'elenco dei membri Clublog CTY.XML Clublog CTY.XML List of Values Lista di valori Updating In aggiornamento Update Failed Aggiornamento fallito DBStrings PHONE Fonia CW CW DIGITAL Digital Afghanistan Afghanistan Agalega & St. Brandon Agalega & St. Brandon Aland Islands Aland Islands Alaska Alaska Albania Albania Algeria Algeria American Samoa American Samoa Amsterdam & St. Paul Is. Amsterdam & St. Paul Is. Andaman & Nicobar Is. Andaman & Nicobar Is. Andorra Andorra Angola Angola Anguilla Anguilla Annobon Island Annobon Island Antarctica Antarctica Antigua & Barbuda Antigua y Barbuda Argentina Argentina Armenia Armenia Aruba Aruba Ascension Island Ascension Island Asiatic Russia Asiatic Russia Asiatic Turkey Asiatic Turkey Austral Islands Austral Islands Australia Australia Austria Austria Aves Island Aves Island Azerbaijan Azerbaiyán Azores Azores Bahamas Bahamas Bahrain Bahrain Baker & Howland Islands Baker & Howland Islands Balearic Islands Balearic Islands Banaba Island Banaba Island Bangladesh Bangladesh Barbados Barbados Belarus Belarus Belgium Belgium Belize Belice Benin Benín Bermuda Bermuda Bhutan Bhutan Bolivia Bolivia Bonaire Bonaire Bosnia-Herzegovina Bosnia-Herzegovina Botswana Botswana Bouvet Bouvet Brazil Brazil British Virgin Islands British Virgin Islands Brunei Darussalam Brunei Darussalam Bulgaria Bulgaria Burkina Faso Burkina Faso Burundi Burundi Cambodia Cambodia Cameroon Cameroon Canada Canada Canary Islands Canary Islands Cape Verde Cape Verde Cayman Islands Cayman Islands Central African Republic Central African Republic Central Kiribati Central Kiribati Ceuta & Melilla Ceuta & Melilla Chad Chad Chagos Islands Chagos Islands Chatham Islands Chatham Islands Chesterfield Islands Chesterfield Islands Chile Chile China China Christmas Island Christmas Island Clipperton Island Clipperton Island Cocos (Keeling) Islands Cocos (Keeling) Islands Cocos Island Cocos Island Colombia Colombia Comoros Comoros Conway Reef Conway Reef Corsica Corsica Costa Rica Costa Rica Cote d'Ivoire Cote d'Ivoire Crete Crete Croatia Croatia Crozet Island Crozet Island Cuba Cuba Curacao Curacao Cyprus Cyprus Czech Republic Czech Republic DPR of Korea DPR of Korea Dem. Rep. of the Congo Dem. Rep. of the Congo Denmark Denmark Desecheo Island Desecheo Island Djibouti Djibouti Dodecanese Dodecanese Dominica Dominica Dominican Republic Dominican Republic Ducie Island Ducie Island East Malaysia East Malaysia Easter Island Easter Island Eastern Kiribati Eastern Kiribati Ecuador Ecuador Egypt Egypt El Salvador El Salvador England England Equatorial Guinea Equatorial Guinea Eritrea Eritrea Estonia Estonia Ethiopia Ethiopia European Russia European Russia Falkland Islands Falkland Islands Faroe Islands Faroe Islands Fed. Rep. of Germany Fed. Rep. of Germany Fernando de Noronha Fernando de Noronha Fiji Fiji Finland Finland France France Franz Josef Land Franz Josef Land French Guiana French Guiana French Polynesia French Polynesia Gabon Gabon Galapagos Islands Galapagos Islands Georgia Georgia Ghana Ghana Gibraltar Gibraltar Glorioso Islands Glorioso Islands Greece Greece Greenland Greenland Grenada Grenada Guadeloupe Guadeloupe Guam Guam Guantanamo Bay Guantanamo Bay Guatemala Guatemala Guernsey Guernsey Guinea Guinea Guinea-Bissau Guinea-Bissau Guyana Guyana Haiti Haiti Hawaii Hawaii Heard Island Heard Island Honduras Honduras Hong Kong Hong Kong Hungary Hungary ITU HQ ITU HQ Iceland Iceland India India Indonesia Indonesia Iran Irán Iraq Iraq Ireland Ireland Isle of Man Isle of Man Israel Israel Italy Italy Jamaica Jamaica Jan Mayen Jan Mayen Japan Japan Jersey Jersey Johnston Island Johnston Island Jordan Jordan Juan Fernandez Islands Juan Fernandez Islands Juan de Nova & Europa Juan de Nova & Europa Kaliningrad Kaliningrad Kazakhstan Kazakhstan Kenya Kenya Kerguelen Islands Kerguelen Islands Kermadec Islands Kermadec Islands Kingdom of Eswatini Kingdom of Eswatini Kure Island Kure Island Kuwait Kuwait Kyrgyzstan Kyrgyzstan Lakshadweep Islands Lakshadweep Islands Laos Laos Latvia Latvia Lebanon Lebanon Lesotho Lesotho Liberia Liberia Libya Libya Liechtenstein Liechtenstein Lithuania Lithuania Lord Howe Island Lord Howe Island Luxembourg Luxembourg Macao Macao Macquarie Island Macquarie Island Madagascar Madagascar Madeira Islands Madeira Islands Malawi Malawi Maldives Maldivas Mali Malí Malpelo Island Malpelo Island Malta Malta Mariana Islands Mariana Islands Market Reef Market Reef Marquesas Islands Marquesas Islands Marshall Islands Marshall Islands Martinique Martinique Mauritania Mauritania Mauritius Mauritius Mayotte Mayotte Mellish Reef Mellish Reef Mexico Mexico Micronesia Micronesia Midway Island Midway Island Minami Torishima Minami Torishima Moldova Moldova Monaco Monaco Mongolia Mongolia Montenegro Montenegro Montserrat Montserrat Morocco Morocco Mount Athos Mount Athos Mozambique Mozambique Myanmar Myanmar N.Z. Subantarctic Is. N.Z. Subantarctic Is. Namibia Namibia Nauru Nauru Navassa Island Navassa Island Nepal Nepal Netherlands Netherlands New Caledonia New Caledonia New Zealand New Zealand Nicaragua Nicaragua Niger Niger Nigeria Nigeria Niue Niue Norfolk Island Norfolk Island North Cook Islands North Cook Islands North Macedonia North Macedonia Northern Ireland Northern Ireland Norway Norway Ogasawara Ogasawara Oman Oman Pakistan Pakistan Palau Palau Palestine Palestine Palmyra & Jarvis Islands Palmyra & Jarvis Islands Panama Panama Papua New Guinea Papua New Guinea Paraguay Paraguay Peru Peru Peter 1 Island Peter 1 Island Philippines Philippines Pitcairn Island Pitcairn Island Poland Poland Portugal Portugal Pr. Edward & Marion Is. Pr. Edward & Marion Is. Pratas Island Pratas Island Puerto Rico Puerto Rico Qatar Qatar Republic of Korea Republic of Korea Republic of Kosovo Republic of Kosovo Republic of South Sudan Republic of South Sudan Republic of the Congo Republic of the Congo Reunion Island Reunion Island Revillagigedo Revillagigedo Rodriguez Island Rodriguez Island Romania Romania Rotuma Island Rotuma Island Rwanda Rwanda Saba & St. Eustatius Saba & St. Eustatius Sable Island Sable Island Samoa Samoa San Andres & Providencia San Andres & Providencia San Felix & San Ambrosio San Felix & San Ambrosio San Marino San Marino Sao Tome & Principe Sao Tome & Principe Sardinia Sardinia Saudi Arabia Saudi Arabia Scarborough Reef Scarborough Reef Scotland Scotland Senegal Senegal Serbia Serbia Seychelles Seychelles Sierra Leone Sierra Leona Singapore Singapore Sint Maarten Sint Maarten Slovak Republic Slovak Republic Slovenia Slovenia Solomon Islands Solomon Islands Somalia Somalia South Africa South Africa South Cook Islands South Cook Islands South Georgia Island South Georgia Island South Orkney Islands South Orkney Islands South Sandwich Islands South Sandwich Islands South Shetland Islands South Shetland Islands Sov Mil Order of Malta Sov Mil Order of Malta Spain Spain Spratly Islands Spratly Islands Sri Lanka Sri Lanka St. Barthelemy St. Barthelemy St. Helena St. Helena St. Kitts & Nevis St. Kitts & Nevis St. Lucia St. Lucia St. Martin St. Martin St. Paul Island St. Paul Island St. Peter & St. Paul St. Peter & St. Paul St. Pierre & Miquelon St. Pierre & Miquelon St. Vincent St. Vincent Sudan Sudán Suriname Suriname Svalbard Svalbard Swains Island Swains Island Sweden Sweden Switzerland Switzerland Syria Syria Taiwan Taiwan Tajikistan Tajikistan Tanzania Tanzania Temotu Province Temotu Province Thailand Thailand The Gambia The Gambia Timor - Leste Timor - Leste Togo Togo Tokelau Islands Tokelau Islands Tonga Tonga Trindade & Martim Vaz Trindade & Martim Vaz Trinidad & Tobago Trinidad y Tobago Tristan da Cunha & Gough Islands Tristan da Cunha & Gough Islands Tromelin Island Tromelin Island Tunisia Tunisia Turkmenistan Turkmenistan Turks & Caicos Islands Turks & Caicos Islands Tuvalu Tuvalu UK Base Areas on Cyprus UK Base Areas on Cyprus US Virgin Islands US Virgin Islands Uganda Uganda Ukraine Ukraine United Arab Emirates United Arab Emirates United Nations HQ United Nations HQ United States United States Uruguay Uruguay Uzbekistan Uzbekistan Vanuatu Vanuatu Vatican City Vatican City Venezuela Venezuela Vietnam Vietnam Wake Island Wake Island Wales Wales Wallis & Futuna Islands Wallis & Futuna Islands West Malaysia West Malaysia Western Kiribati Western Kiribati Western Sahara Western Sahara Willis Island Willis Island Yemen Yemen Zambia Zambia Zimbabwe Zimbabwe DXCCSubmissionDialog DXCC Submission List Elenco di invio DXCC Options Opzioni My DXCC Entity Mia Entità DXCC User Filter Filtro Utente Category Categoria Mixed Misto CW CW Phone Fonia Digital Digital Confirmed by Confermato da LoTW LoTW Paper Cartaceo Show Mostra Not Yet Submitted Non ancora inviato Submitted (Not Granted) Inviato (non confermato) Already Granted Già confermato Band Scope Intervallo di banda Select 5-Band DXCC preset bands (80/40/20/15/10m) Seleziona bande predefinite DXCC a 5 bande (80/40/20/15/10 m) 5-Band DXCC DXCC a 5 bande Select all DXCC-eligible bands Seleziona tutte le bande idonee per DXCC All DXCC Bands Tutte le bande DXCC Bands Banda Select options above and the list will update automatically. Seleziona le opzioni sopra e l’elenco si aggiornerà automaticamente. Export the contacts listed above to an ADIF file Esporta i contatti elencati sopra in un file ADIF Export Esporta No User Filter Nessun filtro utente Any Band (Entity Level) Qualsiasi banda (livello entità) 5-Band DXCC (80/40/20/15/10m) DXCC a 5 bande (80/40/20/15/10 m) Custom Band Selection Selezione personalizzata delle bande Entity Entità Prefix Prefisso Callsign Indicativo Band Banda Mode Modo Date Data Submitted Inviato Granted Confermato Export ADIF Esporta ADIF No contacts to export. Nessun contatto da esportare. Failed to retrieve contact records. Impossibile recuperare i record dei contatti. Export DXCC Submission List as ADIF Esporta l’elenco di invio DXCC come ADIF any band qualsiasi banda 5-band 5 bande all bands tutte le bande %1 selected band(s) %1 banda/e selezionata/e No contacts match the selected criteria. Nessun contatto corrisponde ai criteri selezionati. %1 %2 %3 — DXCC %4 / %5 %1 %2 %3 — DXCC %4 / %5 band-slot slot di banda entity entità entry voce entries voci Data New Entity Nuova Entità New Band Nuova Banda New Mode Nuovo Modo New Band&Mode Nuovi Banda&Modo New Slot Nuovo Slot Confirmed Confermato Worked Lavorato Hz Hz kHz kHz GHz GHz MHz MHz Yes No No Requested Richiesto Queued In coda Invalid non valido Bureau Bureau Direct Diretto Electronic Elettronica Blank Vuoto Modified Modificato Grayline Grayline Other Altro Short Path Short Path Long Path Long Path Not Heard Non ascoltato Uncertain Incerto Straight Key Tasto Verticale Sideswiper Tasto a coltello Mechanical semi-automatic keyer or Bug Tasto semiautomatico o Bug Mechanical fully-automatic keyer or Bug Tasto meccanico automatico o Bug Single Paddle Tasto a singola paletta Dual Paddle Tasto a doppia paletta Computer Driven Comandato dal Computer Confirmed (AG) Confermato (AG) Confirmed (no AG) Confermato (non-AG) Unknown Sconosciuto Aircraft Scatter Aircraft Scatter Aurora-E Aurora-E Aurora Aurora Back scatter Back scatter EchoLink EchoLink Earth-Moon-Earth Earth-Moon-Earth Sporadic E E Sporadico F2 Reflection F2 Reflection Field Aligned Irregularities Field Aligned Irregularities Ground Wave Onda di Terra Internet-assisted Internet-assisted Ionoscatter Ionoscatter IRLP Line of Sight Linea di vista Meteor scatter Meteor scatter Terrestrial or atmospheric repeater or transponder Ripetitore o transponder terrestre o atmosferico Rain scatter Rain scatter Satellite Satellite Trans-equatorial Trans-equatoriale Tropospheric ducting Propagazione Troposferica DateFormatDelegate Blank Vuoto DevToolsDialog Developer Tools Strumenti dev Debug Log Log di debug Logging Rules: Regole di registrazione: e.g. qlog.ui.*.runtime=true es. qlog.ui.*.runtime=true Apply Applica Generate Debug File Genera file di debug Export File Esporta file SQL Console Console SQL Open SQL Apri SQL Save SQL Salva SQL Run SQL Esegui SQL Export As Esporta come Enter SQL query here... Ctrl+Return = run Inserisci la query SQL qui... Ctrl+Invio = esegui TXT CSV CSV ADI Open SQL Query Apri query SQL SQL Files (*.sql);;All Files (*) File SQL (*.sql);;Tutti i file (*) Open Error Errore di apertura Cannot open file: %1 Impossibile aprire il file: %1 Save SQL Query Salva query SQL Save Error Errore di salvataggio Cannot save file: %1 Impossibile salvare il file: %1 Query saved to %1 Query salvata in %1 Error: %1 Errore: %1 %1 row(s) shown (truncated at %2) in %3 ms %1 riga/righe mostrate (troncato a %2) in %3 ms %1 row(s) returned in %2 ms %1 riga/righe restituite in %2 ms Export Esporta No results to export. Nessun risultato da esportare. Export Error Errore di esportazione Cannot open file for writing: %1 Impossibile aprire il file in scrittura: %1 Exported %1 row(s) to %2 Esportate %1 righe in %2 Text Files (*.txt);;All Files (*) File di testo (*.txt);;Tutti i file (*) CSV Files (*.csv);;All Files (*) File CSV (*.csv);;Tutti i file (*) ADIF Export Esporta ADIF ADIF export requires the query to include the contacts 'id' column. Example: SELECT id, callsign FROM contacts WHERE ... No valid contact IDs found in the result set. Nessun ID contatto valido trovato nel set di risultati. Failed to retrieve contact records: %1 Impossibile recuperare i record dei contatti: %1 No matching contacts found in the database. Nessun contatto corrispondente trovato nel database. Logging rules cleared (defaults) Regole di logging ripristinate (predefinite) Logging rules applied Regole di logging applicate Save Debug Log Salva log di debug No debug log file is currently being written Attualmente non viene scritto alcun file di log di debug Log Files (*.log);;All Files (*) File di log (*.log);;Tutti i file (*) Debug log saved to %1 Log di debug salvato in %1 Failed to copy the debug log file. Impossibile copiare il file di log di debug. File logging is disabled La registrazione su file è disabilitata Log file: %1 File di log: %1 DownloadQSLDialog Download QSLs Scarica le QSL eQSL eQSL eQSL QTH Profile eQSL QTH Profile QSLs Since QSLs da QSOs Since QSO da LoTW LoTW My Callsign Mio Indicativo &Download &Scarica LoTW is not configured properly.<p> Please, use <b>Settings</b> dialog to configure it.</p> LoTW non è configurato correttamente.<p> Utilizza la finestra di dialogo <b>Impostazioni</b> per configurarlo.</p> eQSL is not configured properly.<p> Please, use <b>Settings</b> dialog to configure it.</p> eQSL non è configurato correttamente.<p> Utilizza la finestra di dialogo <b>Impostazioni</b> per configurarlo.</p> Cancel Cancella Downloading from %1 Download da %1 Processing %1 QSLs Elaborazione di %1 QSL QLog Error Errore di QLog %1 update failed: Aggiornamento di %1 non riuscito: QLog Information Informazioni QLog No service selected Nessun servizio selezionato DxFilterDialog DX Cluster Filters Filtri DX Cluster General Filters Filtri Generici Bands Bande Modes Modi Phone Fonia CW CW Digital Digital Continent Continente North America North America Africa Africa Antarctica Antarctica South America South America Asia Asia Europe Europa FTx (FT8/FT4) FTx (FT8/FT4) Oceania Oceanía Log Status Stato del Log New Mode Nuovo Modo New Entity Nuova Entità New Band Nuova Banda New Slot Nuovo Slot Worked Lavorato Confirmed Confermato Extended Filters Filtri Estesi Spotter Continent Continente Spotter Do not show the following spots when they have the same Callsign and their time difference is lower than Time Diff and their frequency difference is lower than Freq Diff Non mostrare i seguenti spot quando hanno lo stesso nominativo e la loro differenza di tempo è inferiore a Time Diff e la loro differenza di frequenza è inferiore a Freq Diff Spots Dedup Deduplica Spot Time difference Differenza oraria s s Frequency difference Differenza di Frequenza kHz kHz Member Membro No Club List is enabled Nessuna Club List è abilitata DxTableModel Time Ora Callsign Indicativo Frequency Frequenza Mode Modo Spotter Spotter Comment Commento Continent Continente Spotter Continent Continente Spotter Band Banda Member Membro Country Nazione DxWidget Insert a <b>hostname:port</b> of DXC Server. Inserisci un <b>nome host:porta</b> del Server DXC. Connect Connette Filtered Filtrato Spots Spots WCY WCY WWV WWV To All A tutti Console Console 15-min Trend Full-text search Ricerca a testo completo Search Ricerca Close Search Chiudi ricerca Send DX Cluster Command Invia comando DX Cluster Filter... Filtra... Filter DXC Filtro DXC Spot Last QSO Invia lo Spot dell'ultimo QSO salvato completabile con altre informazioni nella riga di invio comando al cluster Spot Ultimo QSO salvato Send last QSO spot Invia l'ultimo spot QSO Show HF Stats Mostra statistiche HF Show VHF Stats Mostra statistiche VHF Show WCY Mostra WCY Show WWV Mostra WWV Column Visibility... Visibilità della colonna... Which columns should be displayed Quali colonne devono essere visualizzate Connect on startup Connetti all'avvio Automatic connection after start Connessione automatica dopo l'avvio Delete Server Cancella Server DXC - Delete Server DXC - Elimina server Clear Password Cancella Password Keep Spots Mantieni Spots Spots are not cleared when connecting to a new DX Cluster. Gli spot non vengono cancellati quando ci si connette a un nuovo DX Cluster. Clear Cancella Clear all data Cancella Tutti i Dati Search... Ricerca... DXC - Search DXC - Ricerca My Continent Mio continente Auto Automatico Connecting... Sta Connettendo... DX Cluster is temporarily unavailable DX Cluster è temporaneamente non disponibile DXC Server Error DXC Errore del server An invalid callsign Indicativo non valido DX Cluster Password DX Cluster Password Security Notice Avviso di sicurezza The password can be sent via an unsecured channel La password può essere inviata tramite un canale non protetto Server Server Username Nome utente Disconnect Disconnette DX Cluster Command Comando del DX Cluster DxccTableModel Worked Lavorato eQSL eQSL LoTW LoTW Paper Cartaceo DxccTableWidget Mode Modo EQSLQSLDownloader Incorrect Password or QTHProfile Id Password o QTHProfile ID errati ADIF file not found in eQSL response File ADIF non trovato nella risposta eQSL Incorrect Username or password Nome utente o password errati Unknown Error Errore sconosciuto Cannot opet temporary file Impossibile aprire il file temporaneo Cannot save the image to file Impossibile salvare l'immagine su file EQSLUploader Unknown Reply from eQSL Risposta sconosciuta da eQSL EditActivitiesDialog Edit Activities Modifica Attività Activities Attività Add Aggiungi Edit Modifica Remove Rimuovi ExportDialog Export Selected QSOs Esporta QSO selezionati File File Browse Cerca ADX ADX CSV CSV JSON JSON POTA POTA Export Type Tipo di esportazione Column set Set di colonne Select one of the pre-defined sets of columns or define your own set of columns Seleziona uno dei set di colonne predefiniti o definisci il tuo set di colonne Edit current set of columns Modifica il set di colonne corrente Edit Modifica Mark exported QSOs As Sent Segna QSO esportati come inviati Export only QSOs that match the active filters Esporta solo i QSO che corrispondono ai filtri attivi Filters Filtri Export only QSOs that match the selected date range Esporta solo i QSO che corrispondono all'intervallo di date selezionato Date Range Intervallo di date Export only QSOs that match the selected My Callsign Esporta solo i QSO che corrispondono al mio nominativo selezionato My Callsign Mio Indicativo Export only QSOs that match the selected My Gridsquare Esporta solo i QSO che corrispondono alla Mia griglia selezionata My Gridsquare Mia Griglia Export only QSOs that match the selected QSL Send Via value Esporta solo i QSO che corrispondono al valore selezionato QSL Invia Via QSL Send via Invia QSL vía Include unusual QSO Sent statuses Includere stati insoliti dei QSO inviati Include Sent Status Includere lo stato Inviato Under normal circumstances this status means <b>"Ignore/Invalid"</b>.<br/>However, it may sometimes be wanted to ignore this setting when sending a QSL. In circostanze normali questo stato significa <b>"Ignora/Non valido"</b>.<br/>Tuttavia, a volte potrebbe essere necessario ignorare questa impostazione quando si invia una QSL. "Ignore" "ignora" Under normal circumstances this status means <b>"do not send"</b>.<br/>However, it may sometimes be wanted to ignore this setting when sending a QSL. In circostanze normali, questo stato significa <b>"non inviare".<br/>Tuttavia, a volte potresti voler ignorare questa impostazione quando invii una QSL. "No" "No" Resend already sent QSOs Reinviare i QSO già inviati Already Sent Già inviato User Filter Filtro Utente Export QSOs that match the selected user QSO Filter Esporta QSO che corrispondono al filtro QSO utente selezionato Station Profile Profilo della stazione Export QSOs Esporta QSOs &Export &Esporta Export only QSOs matching this station profile Esportare solo i QSO che corrispondono a questo profilo di stazione Exporting... Esportazione in corso… Cancel QLog Error Errore di QLog Cannot mark exported QSOs as Sent Impossibile Segnare QSO esportati come Inviati Generic Generico QSLs QSLs All Tutte Minimal Mínimo QSL-specific Specifico per QSL Custom 1 Personalizzato 1 Custom 2 Personalizzato 2 Custom 3 Personalizzato 3 ExportPasswordDialog Pack Data & Settings Impacchettare dati e impostazioni Enter a password to encrypt stored credentials. The password will be needed to restore them later. Inserire una password per cifrare le credenziali memorizzate. Questa password sarà necessaria in seguito per ripristinarle. Password Password Random Casuale Confirm Password Conferma password Delete passwords from Credential Store after export Eliminare le password dal Credential Store dopo l’esportazione FlrigRigDrv Timeout Timeout FLRig response timeout Timeout di risposta da FLRig Network Error Errore di rete HRDLogUploader Response message malformed Messaggio di risposta non valido HamQTHCallbook HamQTH HamQTH HamlibRigDrv None Nessuno CAT CAT DTR DTR RTS RTS Initialization Error Errore di inizializzazione Cannot set PTT Type Impossibile impostare Tipo PTT Cannot set PTT Share Impossibile impostare Condividi PTT Cannot set CIV Addr Impossibile impostare l’indirizzo CI-V Unsupported Rig Driver Driver della Radio non Supportato Cannot set auto_power_on Impossibile impostare auto_power_on Cannot set no_xchg to 1 Impossibile impostare no_xchg su 1 Rig Open Error Connessione Radio fallita Set TX Frequency Error Errore nell’impostazione della frequenza TX Set Frequency Error Errore impostazione fequenza Set Split Error Errore nell’impostazione dello split Set Mode Error Errore impostazione Modo Set Split Mode Error Errore nell’impostazione della modalità split Set PTT Error Errore impostazione PTT Cannot sent Morse This cannot be displayed impossibile inviare il codice Morse Cannot stop Morse This cannot be displayed impossibile fermare codice Morse Get PTT Error This cannot be displayed Errore PTT Get Frequency Error Errore della Frequenza Get Mode Error Errore del Modo Get VFO Error Errore VFO Get PWR Error This cannot be displayed Errore PWR Get PWR (power2mw) Error This cannot be displayed Errore PWR (power2mw) Get RIT Function Error This cannot be displayed Errore funzione RIT Get RIT Error This cannot be displayed Errore RIT Get XIT Function Error This cannot be displayed Errore funzione XIT Get XIT Error This cannot be displayed Errore XIT Get Split Error Get TX Frequency Error Get KeySpeed Error This cannot be displayed Errore di KeySpeed Set KeySpeed Error This cannot be displayed Errore impostazione KeySpeed HamlibRotDrv Initialization Error Errore di inizializzazione Unsupported Rotator Driver Driver del rotore non supportato Rot Open Error Connessione Rotore fallita Set Possition Error Errore di impostazione della posizione Get Possition Error Errore della posizione ImportDialog Import Importa Date Range Intervallo di date Import all or only QSOs from the given period Importa tutti o solo i QSO del periodo specificato All Tutti File File ADX ADX Browse Cerca Options Opzioni The value is used when an input record does not contain the ADIF value Il valore viene utilizzato quando una registrazione inserita non contiene il valore ADIF Defaults Valori di Defaults My Profile Mio Profilo My Rig Mia Radio Comment Commento If DXCC is missing in the imported record, it will be resolved from the callsign. Se il DXCC manca nel record importato, verrà determinato dal nominativo. Fill missing DXCC Entity Information Compilare le informazioni mancanti dell’entità DXCC &Import &Importa Select File Seleziona File The values below will be used when an input record does not contain the ADIF values I valori seguenti verranno utilizzati quando una registrazione inserita non contiene i valori ADIF <p><b>In-Log QSO:</b></p><p> <p><b>In-Log QSO:</b></p><p> <p><b>Importing:</b></p><p> <p><b>Importazione:</b></p><p> Duplicate QSO QSO duplicato <p>Do you want to import duplicate QSO?</p>%1 %2 <p>Vuoi importare QSO duplicati?</p>%1 %2 Save to File Salva sul file QLog Import Summary Riepilogo dell'importazione QLog Import date Data di importazione Imported file File importato Imported: %n contact(s) Importato: %n contatto Importati: %n contatti Warning(s): %n Allarme: %n Allarmi: %n Error(s): %n Errore: %n Errori: %n Details Dettagli Import Result Risultato importazione Save Details... Salva dettagli... InputPasswordDialog Dialog finestra di dialogo per l'immissione della password The password will be stored in the Credential Store. La password verrà archiviata nell'archivio credenziali. Remember Password Ricorda la password KSTChat Unknown User Utente sconosciuto Invalid password password non corretta KSTChatWidget Chat messages Messaggi della Chat Valuable messages Messaggi preziosi Clear selected Callsign and Chat message entry. Cancella Callsign e il messaggio di chat selezionati. Send chat message Invia messaggio della chat Column Visibility Visibilità della colonna Which columns should be displayed Quali colonne devono essere visualizzate Prepare QSO Prepara QSO Transfer Callsign and Gridsquare Information to the New QSO dialog Trasferisci le informazioni sul nominativo e sulla griglia nella finestra di dialogo Nuovo QSO Show About Me Only Mostra solo informazioni su di me Show only messages where my callsign is present Mostra solo i messaggi in cui è presente il mio nominativo Suppress User To User Sopprimi utente per utente Suppress private messages between two callsigns Sopprimi i messaggi privati ​​tra due nominativi Highlight Evidenzia Highlight messages based on the setting Evidenzia i messaggi in base all'impostazione Highlight Rules Evidenzia regole Beam Direzione TX Clear Messages Cancella Messaggi Clear all messages in the window Cancella tutti i messaggi nella finestra You Tu KSTHighlightRuleDetail Edit Rule Modifica Regola Rule Name Nome della Regola Chat Room Chat Room Enabled Abilitato Highlight message which match Evidenzia il messaggio che corrisponde All the following conditions Tutte le seguenti condizioni Any of the following conditions Una qualsiasi delle seguenti condizioni Add Condition Aggiungi condizione All Tutti Sender Mittente Message Messaggio Gridsquare Griglia Contains Contiene Starts with Inizia con Remove Rimuovi Must not be empty Non deve essere vuoto KSTHighlighterSettingDialog Highlight Rules Evidenzia regole Rules Regole Add Aggiungi Edit Modifica Remove Rimuovi Name Nome State Stato KeySequenceEdit Clear Cancella LoadDatabaseDialog Unpack Data & Settings Estrarre dati e impostazioni Warning Attenzione <html><head/><body><p>⚠ <span style=" font-weight:700;">Current database will be DELETED!</span><br/>⚠ <span style=" font-weight:700;">All stored passwords will be DELETED!</span><br/>⚠ <span style=" font-weight:700;">The application will restart after loading</span>.</p><p>To preserve data, use Pack Data &amp; Settings or Export QSOs first.</p></body></html> <html><head/><body><p>⚠ <span style=" font-weight:700;">Il database corrente sarà ELIMINATO!</span><br/>⚠ <span style=" font-weight:700;">Tutte le password memorizzate saranno ELIMINATE!</span><br/>⚠ <span style=" font-weight:700;">L'applicazione verrà riavviata dopo il caricamento</span>.</p><p>Per preservare i dati, utilizzare prima Pack Data &amp; Settings o Export QSOs.</p></body></html> Select Database Seleziona database File: File: Browse Cerca Status: No file selected Stato: Nessun file selezionato Decrypt Credentials Decrittare credenziali Enter password to decrypt credentials Inserire la password per decrittare le credenziali Password: Password: Load && Restart Caricare e riavviare Invalid password password non corretta Select Database File Seleziona file di database QLog Database Export (*.dbe);;All Files (*) Esportazione database QLog (*.dbe);;Tutti i file (*) Cannot create temporary file Impossibile creare il file temporaneo Decompressing database... Decompressione del database… Cannot decompress database file Impossibile decomprimere il file del database Cannot open database Impossibile aprire il database Valid database Database valida Different platform Piattaforma diversa Password required to import credentials Password richiesta per importare le credenziali No encrypted credentials in database Nessuna credenziale crittografata nel database LogFormat Cannot find My DXCC Entity Info Impossibile trovare le informazioni sulla mia entità DXCC A minimal set of fields not present (start_time, call, band, mode, station_callsign) Un minimo set di campi non presenti (start_time, call, band, mode, station_callsign) Outside the selected Date Range Otre il range di Data selezionata Duplicate Duplicato DXCC Info is missing Le informazioni DXCC mancano no Station Callsign present Nominativo di Stazione assente Cannot insert to database Impossibile inserire nel Database Imported Importato DXCC State: Stato DXCC: Error Errore Warning Attenzione LogbookModel QSO ID QSO N° Time on Inizio Time off Fine Call Indicativo RSTs RSTs RSTr RSTr Frequency Frequenza Band Banda Mode Modo Submode Submodo Name (ASCII) Nome (ASCII) QTH (ASCII) QTH (ASCII) Gridsquare Griglia DXCC DXCC Country (ASCII) Nazione (ASCII) Continent Continente CQZ CQZone ITU ITU Prefix Prefisso State Stato County Contea IOTA IOTA QSLr QSLr QSLr Date Data QSLr QSLs QSLs QSLs Date Data QSLs LoTWr LoTWr LoTWr Date Data LoTWr LoTWs LoTWs LoTWs Date Data LoTWs TX PWR TX PWR Additional Fields Campi aggiuntivi Address (ASCII) Indirizzo (ASCII) Address Indirizzo Age Età Altitude Altitudine A-Index A-Index Antenna Az Antenna Azim Antenna El Antenna Elev Signal Path Percorso del segnale ARRL Section Sezione ARRL Award Submitted Award inviato Award Granted Award concesso Band RX Banda RX Gridsquare Extended Griglia Estesa Contest Check Controllo Contest Class Categoria ClubLog Upload Date Data di caricamento su Clublog ClubLog Upload State Stato caricamento ClubLog Comment (ASCII) Commento (ASCII) Comment Commento Contacted Operator Operatore Contattato Contest ID Identificativo Contest Country Nazione County Alt Contea Alt Credit Submitted Credito inviato Credit Granted Credito concesso DOK DOK DCLr Date Data DCLr DCLs Date Data DCLs DCLr DCLr DCLs DCLs Distance Distanza Email Email Owner Callsign Callsign del Titolare eQSL AG eQSL AG eQSLr Date Data eQSLr eQSLs Date Data eQSLs eQSLr eQSLr eQSLs eQSLs FISTS Number Numero FISTS FISTS CC FISTS CC EME Init Inizio EME Frequency RX Frequenza RX Guest Operator Operatore ospite HamlogEU Upload Date Data di caricamento HamlogEU HamlogEU Upload Status Stato di caricamento HamlogEU HamQTH Upload Date Data di caricamento HamQTH HamQTH Upload Status Stato di caricamento HamQTH HRDLog Upload Date Data di caricamento di HRDLog HRDLog Upload Status Stato di caricamento di HRDLog IOTA Island ID IOTA Island ID K-Index K-Index Latitude Latitudine Longitude Longitudine Max Bursts Max Bursts MS Shower Name Nome della doccia MeteorScatter My Altitude Mia Altitudine My Antenna (ASCII) Mia Antenna (ASCII) My Antenna Mia Antenna My City (ASCII) Mia Città (ASCII) My City Mia Città My County Mia contea My County Alt Mia contea Alt My Country (ASCII) Mia nazione (ASCII) My Country Mia nazione My CQZ Mia CQ Zone My DARC DOK Mio DARC DOK My DXCC Mio DXCC My FISTS Mio FISTS My Gridsquare Mia Griglia My Gridsquare Extended Mia Griglia Estesa My IOTA Mio IOTA My IOTA Island ID Mio IOTA Island ID My ITU Mio ITU My Latitude Mia Latitudine My Longitude Mia Longitudine My Name (ASCII) Mio Nome (ASCII) My Name Mio nome My Postal Code (ASCII) Mio Codice Postale (ASCII) My Postal Code Mio Codice Postale My POTA Ref Mia Ref POTA My Rig (ASCII) Mia Radio (ASCII) My Rig Mia Radio My Special Interest Activity (ASCII) Mie attività di interesse specialel (ASCII) My Special Interest Activity Mie attività di interesse speciale My Spec. Interes Activity Info (ASCII) informazioni sulle mie attività di interesse speciale (ASCII) My Spec. Interest Activity Info Informazioni sulle mie attività di interesse speciale My SOTA Mio SOTA My State Mio Stato My Street Mia strada My USA-CA Counties Mio USA-CA Counties My VUCC Grids Mia Griglia VUCC Name Nome Notes (ASCII) Note (ASCII) QRZ Download Date Data di scaricamento QRZ QRZ Download Status Stato del download di QRZ QSLs Message (ASCII) QSLs Messaggio (ASCII) QSLs Message QSLs Messaggio QSLr Message QSLr Messaggio RcvPWR Potenza ricevuta RcvNr Numero Ricevuto RcvExch Scambio Ricevuto SentNr Numero Inviato SentExch Scambio Inviato Notes Note #MS Bursts #MS Bursts #MS Pings #MS Pings POTA POTA Contest Precedence Precedenza del Contest Propagation Mode Modo di Propagazione Public Encryption Key Chiave di crittografia pubblica QRZ Upload Date Data di caricamento QRZ QRZ Upload Status Stato caricamento QRZ QSL Message Messaggio della QSL CW Key Info Info sul Tasto CW CW Key Type Tipo di Tasto CW My CW Key Info Mie Info sul Tasto CW My CW Key Type Mio Tipo di Tasto CW Operator Callsign Call dell'operatore QSLr Via QSLr Vía QSLs Via QSLs Vía QSL Via QSL vía QSO Completed QSO Completato QSO Random QSO occasionale QTH QTH Region Regione Rig (ASCII) Radio (ASCII) Rig Radio SAT Mode SAT Mode SAT Name Nome del satellite Solar Flux Flusso solare SIG (ASCII) SIG (ASCII) SIG SIG SIG Info (ASCII) SIG Info (ASCII) SIG Info SIG Info Silent Key Silent Key SKCC Member Miembro SKCC SOTA SOTA Logging Station Callsign Indicativo della stazione SWL SWL Ten-Ten Number Ten-Ten Number UKSMG Member Miembro UKSMG USA-CA Counties USA-CA Counties VE Prov VE Prov VUCC VUCC Web Web My ARRL Section Mia Sezione ARRL My WWFF Mio WWFF WWFF WWFF RST Sent RST Inviato RST Rcvd RST Ricevuto Paper Cartaceo LoTW LoTW eQSL eQSL QSL Received QSL Ricevuta QSL Sent QSL inviata LogbookWidget Delete Elimina Logbook - Delete QSO Logbook - Elimina QSO Update from Callbook Aggiornamento dal Callbook Logbook - Edit Value Logbook - Modifica valore Send DX Spot Invia DX Spot Logbook - Send DX Spot Logbook - Invia DX Spot Upload to Clublog Carica su Clublog Lookup on Web Ricerca sul Web Add Missing Info Aggiungere le informazioni mancanti Mark QSL RCVD Contrassegna QSL come ricevuta Mark QSL Requested Contrassegnare QSL richiesta Filter Callsign Filtro Callsign Edit Value Modifica Valore Column Visibility Visibilità della colonna Which columns should be displayed Quali colonne devono essere visualizzate Export Selected Esporta selezionati Export selected QSOs Esporta QSO selezionati Callsign Indicativo Gridsquare Griglia POTA POTA SOTA SOTA WWFF WWFF SIG SIG IOTA IOTA Delete the selected contacts? Eliminare i contatti selezionati? Clublog's <b>Immediately Send</b> supports only one-by-one deletion<br><br>Do you want to continue despite the fact<br>that the DELETE operation will not be sent to Clublog? L'<b>Invio immediato</b> di Clublog supporta solo l'eliminazione uno per uno<br><br>Vuoi continuare nonostante il fatto<br>che l'operazione DELETE non verrà inviata a Clublog? Deleting QSOs Elimina QSOs Update Aggiornare By updating, all selected rows will be affected.<br>The value currently edited in the column will be applied to all selected rows.<br><br>Do you want to edit them? L'aggiornamento avrà effetto su tutte le righe selezionate.<br>Il valore attualmente modificato nella colonna verrà applicato a tutte le righe selezionate.<br><br>Vuoi modificarle? Count: %n Conteggio: %n Conteggio: %n Downloading eQSL Image Download immagine da eQSL Cancel Elimina All Bands Tutte le Bande All Modes Tutte le modalità All Countries Tutti i paesi No User Filter Nessun filtro utente QLog Warning Avviso QLog Each batch supports up to 100 QSOs. Ogni lotto supporta fino a 100 QSO. QSOs Update Progress Aggiornamento QSO Progressi QLog Error Errore di QLog Callbook login failed Accesso al Logbook non riuscito Callbook error: Errore del Callbook: All Clubs Tutti i club eQSL Download Image failed: Download immagine da eQSL fallito: LotwQSLDownloader Cannot open temporary file Impossibile aprire il file temporaneo Incorrect login or password Nome utente o password errati LotwUploader Upload cancelled by user Upload cancellato dall'utente Upload rejected by LoTW Upload rifiutato da LoTW Unexpected response from TQSL server Risposta inaspettata dal server TQSL TQSL utility error Errore di TQSL TQSLlib error Errore di TQSLlib Unable to open input file Impossibile aprire il file di input Unable to open output file Impossibile aprire il file di output All QSOs were duplicates or out of date range Tutti i QSO erano duplicati o non rientravano nell'intervallo di date Some QSOs were duplicates or out of date range Alcuni QSO erano duplicati o non rientravano nell'intervallo di date Command syntax error Errore di Sintassi LoTW Connection error (no network or LoTW is unreachable) Errore di connessione LoTW (nessuna rete o LoTW non è raggiungibile) Unexpected Error from TQSL Errore inaspettato da TQSL TQSL not found TQSL non trovato TQSL crashed TQSL si è bloccato MainWindow &File &File &Logbook &Logbook &Equipment &Apparati &Help &Aiuto &Window &Finestra Se&rvice &Servizi Toolbar Barra degli strumenti Clock Orologio Map Mappa DX Cluster DX Cluster WSJTX WSJTX Rotator Rotore Bandmap Bandmap Rig Radio Online Map Mappa Online CW Console Console CW Chat Chat Profile Image Foto su QRZ.com/HamQTH Alerts Avvisi &Settings &Impostazioni &Import &Importa &Export &Esporta Connect R&ig Connetti &Radio &About &A proposito di Upload Caricamento Service - Upload QSOs Servizio – Carica QSOs Download QSLs Scarica le QSL Service - Download QSLs Servizion - Scarica le QSL Quit Esci Application - Quit Chiudi applicazione New QSO - Clear Nuovo QSO - Pulisci New QSO - Save Nuovo QSO - Salva S&tatistics S&tatistiche Wsjtx Wsjtx Connect R&otator Connetti R&otore QSO &Filters &Filtri QSO &Awards &Diplomi Edit Rules Modifica Regole Clear Cancella Show Alerts Mostra Avvisi Beep Beep Contest Contest Dupe Check Controllo Duplicati Sequence Sequenza Linking Exchange With Collega scambio con Pack Data && Settings Impacchettare dati e impostazioni Unpack Data && Settings Estrarre dati e impostazioni QSL &Gallery &Galleria QSL Developer Tools Strumenti dev Run custom read-only SQL queries against the logbook database Eseguire query SQL personalizzate in sola lettura sul database del logbook Print QSL &Labels &Stampa etichette QSL DXCC &Submission List &Elenco di invio DXCC Generate a list of contacts to submit for ARRL DXCC award credit Generare un elenco di contatti da presentare per il credito del premio ARRL DXCC Connect &CW Keyer Connetti &CW Keyer &Wiki &Wiki Report &Bug... &segnala un errore... &Manual Entry &Inserimento Manuale Switch New Contact dialog to the manually entry mode<br/>(time, freq, profiles etc. are not taken from their common sources) Passa alla modalità di inserimento manuale nella finestra di dialogo Nuovo contatto<br/>(ora, frequenza, profili ecc. non vengono presi dalle loro fonti comuni) Mailing List... Mailing List... Edit Modifica Save Arrangement Salva disposizione Keep Options Mantieni opzioni Restore connection options after application restart Ripristina le opzioni di connessione dopo il riavvio dell'applicazione Logbook - Search Callsign Logbook - Cerca Callsign New QSO - Add text from Callsign field to Bandmap Nuovo QSO: aggiungi testo dal campo del nominativo alla Bandmap Rig - Band Down Radio - Banda Giù Rig - Band Up Radio - Banda Su New QSO - Use Callsign from the Whisperer Nuovo QSO: usa il nominativo del Whisperer CW Console - Key Speed Up Console CW: aumenta velocità CW Console - Key Speed Down Console CW: diminuisci velocità CW Console - Profile Up Console CW: carica profilo CW Console - Profile Down Console CW: scarica profilo Rig - PTT On/Off Radio - PTT On/Off All Bands Tutte le Bande Each Band Ogni Banda Each Band && Mode Ogni Banda && Modo No Check Nessun Controllo Single Singolo Per Band Per Banda Stop Stop Reset Reset None Nessuno Theme: Native Tema: Native Theme: QLog Light Tema: QLog Light Theme: QLog Dark Tema: QLog Dark What's New Novità Export Cabrillo Esporta Cabrillo Color Theme Tema colore Not enabled for non-Fusion style Non abilitato per stili diversi da Fusion Press to tune the alert Premi per sintonizzare l'allarme Clublog Immediately Upload Error Errore di caricamento immediato di Clublog <b>Error Detail:</b> <b>Dettagli errore:</b> op: op: A New Version Una nuova versione A new version %1 is available. Una nuova versione %1 è disponibile. Remind Me Later Ricordamelo più tardi Download Download Failed to encrypt credentials. Impossibile cifrare le credenziali. Database files (*.dbe);;All files (*) File di database (*.dbe);;Tutti i file (*) Failed to create temporary file. Impossibile creare il file temporaneo. Failed to dump the database. Impossibile esportare il database. Compressing database... Compressione del database… Database successfully dumped to %1 Database esportato con successo in %1 Failed to compress the database. Impossibile comprimere il database. Failed to prepare database for import. Impossibile preparare il database per l’importazione. Classic Classico Do you want to remove the Contest filter %1? Vuoi rimuovere il Contest filter %1? Contest: Contest: <h1>QLog %1</h1><p>&copy; 2019 Thomas Gatzweiler DL2IC<br/>&copy; 2021-2026 Ladislav Foldyna OK1MLG<br/>&copy; 2025-2026 Michael Morgan AA5SH<br/>&copy; 2025-2026 Kyle Boyle VE9KZ</p><p>Based on Qt %2<br/>%3<br/>%4<br/>%5</p><p>Icon by <a href='http://www.iconshock.com'>Icon Shock</a><br />Satellite images by <a href='http://www.nasa.gov'>NASA</a><br />ZoneDetect by <a href='https://github.com/BertoldVdb/ZoneDetect'>Bertold Van den Bergh</a><br />TimeZone Database by <a href='https://github.com/evansiroky/timezone-boundary-builder'>Evan Siroky</a> <h1>QLog %1</h1><p>&copy; 2019 Thomas Gatzweiler DL2IC<br/>&copy; 2021-2026 Ladislav Foldyna OK1MLG<br/>&copy; 2025-2026 Michael Morgan AA5SH<br/>&copy; 2025-2026 Kyle Boyle VE9KZ</p><p>Basato su Qt %2<br/>%3<br/>%4<br/>%5</p><p>Icona di <a href=' http://www.iconshock.com'>Icon Shock</a><br />Immagini satellitari di <a href='http://www.nasa.gov'>NASA</a><br />ZoneDetect di <a href='https://github.com/BertoldVdb/ZoneDetect'>Bertold Van den Bergh</a><br />Database TimeZone di <a href='https://github.com/evansiroky/timezone -boundary-builder'>Evan Siroky</a> About A proposito di N/A N/A MapWebChannelHandler Grid Griglia Gray-Line Gray-line Beam Direzione TX Aurora Aurora MUF MUF IBP IBP Chat Chat WSJTX - CQ WSJTX - CQ Path Direzioni NewContactWidget Frequency Frequenza Callsign Indicativo <b>DUPE !!!</b> <b>DUPE !!!</b> RX: RX: TX: TX: MHz 80m RSTs RSTs RSTr RSTr 59 Mode Modo Save Salva Lookup the call on the web. The query URL can be changed in Settings -> Callbook Cerca il call sul web. L'URL della query può essere modificato in Impostazioni -> Rubrica Web Web Time On Inizio Reset Reset Date Data Duration Durata Info Info &Details &Dettagli QSL Send Status Stato invio QSL Paper Cartaceo <b>Yes</b> - an outgoing QSL card has been sent; the QSO has been uploaded to, and accepted by, the online service<br/><b>No</b> - do not send an outgoing QSL card; do not upload the QSO to the online service<br/><b>Requested</b> - the contacted station has requested a QSL card; the contacted station has requested the QSO be uploaded to the online service<br/><b>Queued</b> - an outgoing QSL card has been selected to be sent; a QSO has been selected to be uploaded to the online service<br/> <b>Sì</b> - è stata inviata una QSL in uscita; il QSO è stato caricato e accettato dal servizio online<br/><b>No</b> - non inviare una cartolina QSL in uscita; non caricare il QSO sul servizio online<br/><b>Richiesto</b> - la stazione contattata ha richiesto una QSL; la stazione contattata ha richiesto il caricamento del QSO sul servizio online<br/><b>Queued</b> - è stata selezionata una QSL in uscita da inviare; è stato selezionato un QSO da caricare sul servizio online<br/> LoTW LoTW eQSL eQSL QSL Send via Invia QSL vía QSL via QSL vía Propagation Mode Modo di Propagazione D&X Stats D&X Statistiche <b>DXCC Statistics</b> <b>DXCC Statistics</b> <b>Station Statistics</b> <b>Station Statistics</b> M&y Station M&ia Stazione Station Stazione Rig Radio Antenna Antenna Blank Vuoto W W My &Notes Le mie N&ote Member: Membro: Expand/Collapse Espandi/Comprimi No No Yes Requested Richiesto Queued In coda Ignored Ignorato Bureau Bureau Direct Diretto Electronic Elettronica QLog Error Errore di QLog Callbook login failed Accesso al Logbook non riuscito LP LP New Entity! Nuova Entità! New Band! Nuova Banda! New Mode! Nuovo Modo! New Band & Mode! Nuova Banda e Modo! New Slot! Nuovo Slot! Worked Lavorato Confirmed Confermato GE GE GM GM GA GA m Callbook search is inactive Ricerca sul Callbook inattiva Callbook search is active Ricerca sul Callbook attiva Contest ID must be filled in to activate L'ID del contest deve essere compilato per attivare two or four adjacent Maidenhead grid locators, each four characters long, (ex. EN98,FM08,EM97,FM07) due o quattro localizzatori di griglia Maidenhead adiacenti, ciascuno lungo quattro caratteri, (es. EN98,FM08,EM97,FM07) the contacted station's DARC DOK (District Location Code) (ex. A01) il DARC DOK (District Location Code) della stazione contattata (ex. A01) World Wide Flora & Fauna World Wide Flora & Fauna Special Activity Group Gruppo di Attività Speciali Special Activity Group Information Informazioni sul gruppo di attività speciali It is not the name of the contest but it is an assigned<br>Contest ID (ex. CQ-WW-CW for CQ WW DX Contest (CW)) Non è il nome del Contest ma è un ID del Contest assegnato (ad esempio CQ-WW-CW per CQ WW DX Contest (CW)) Description of the contacted station's equipment Descrizione dell'attrezzatura della stazione contattata OmnirigRigDrv Rig 1 Radio 1 Rig 2 Radio 2 Initialization Error Errore di inizializzazione Rig status changed Stato della Radio cambiato Rig is not connected Radio non connessa OmnirigV2RigDrv Rig 1 Radio 1 Rig 2 Radio 2 Rig 3 Radio 3 Rig 4 Radio 4 Initialization Error Errore di inizializzazione Rig status changed Stato della Radio cambiato Rig is not connected Radio non connessa PSTRotDrv Rot 1 Rot 1 Cannot bind a port Impossibile associare una porta Cannot get IP Address for Impossibile ottenere l'indirizzo IP per No IPv4 Address for Nessun indirizzo IPv4 per Error Occurred Si è verificato un errore Operation Timeout Timeout operazione PaperQSLDialog Manage QSL Card Gestisci QSL Available QSLs QSLs Disponibili Copy the input file from the source folder to the QLog Internal QSL Storage folder.<br/>The original file remains unchanged in the source folder Copia il file di input dalla cartella di origine alla cartella QLog Internal QSL Storage.<br/>Il file originale rimane invariato nella cartella di origine Import QSL Importa QSL Add File Aggiungi File Remove Rimuovi Delete Cancella Delete QSL? Cancellare QSL? Open Apri Toggle Favorite Preferito PlatformSettingsDialog Platform-specific Settings Impostazioni specifiche della piattaforma The database was exported from a different platform. Please verify or update the following settings. You can leave fields empty and configure them later in Settings. Il database è stato esportato da una piattaforma diversa. Verificate o aggiornate le seguenti impostazioni. È possibile lasciare i campi vuoti e configurarli successivamente nelle Impostazioni. Setting Impostazioni Value Valore Continue Continua Select File Seleziona File All Files (*) Tutti i file (*) ProfileImageWidget Profile Image Foto su QRZ.com/HamQTH QCoreApplication QLog Help QLog Help QMessageBox QLog Critical QLog Crítico Cannot save a password for %1 to the Credential Store Impossibile salvare una password per %1 nell'archivio credenziali Cannot get a password for %1 from the Credential Store Impossibile ottenere una password per %1 dall'archivio credenziali QLog Warning Avviso QLog Club List Update failed. Cannot remove old records 50 / 5.000 Aggiornamento elenco club non riuscito. Impossibile rimuovere i vecchi record Club List Update failed. Cannot plan new downloads Aggiornamento elenco club non riuscito. Impossibile pianificare nuovi download Unexpected Club List download. Canceling next downloads Download imprevisto della Club-List. Annullamento dei download successivi Unexpected Club List content for Contenuti imprevisti della Club-List per Network error. Cannot download Club List for Errore di rete. Impossibile scaricare l'elenco dei club per QLog Error Errore di QLog QLog is already running QLog è già in esecuzione Failed to process pending database import. Impossibile elaborare l’importazione in sospeso del database. The database was imported successfully, but the stored passwords could not be restored (decryption failed or the data is corrupted). All service passwords have been cleared and must be re-entered in Settings. Il database è stato importato con successo, ma le password memorizzate non sono state ripristinate (decrittazione fallita o dati corrotti). Tutte le password dei servizi sono state cancellate e devono essere reinserite nelle Impostazioni. Could not connect to database. Impossibile collegare il Database. Could not export a QLog database to ADIF as a backup.<p>Try to export your log to ADIF manually Impossibile esportare un database QLog in ADIF come backup.<p>Prova a esportare manualmente il tuo Log in ADIF Database migration failed. Errore nella Migrazione del database. QLog Info QLog Info Activity name is already exists. Il Nome dell'Attività esiste già. Rule name is already exists. Il nome della regola esiste già. Callsign Regular Expression is incorrect. L'espressione regolare dell'indicativo non è corretta. Comment Regular Expression is incorrect. L'espressione regolare del commento non è corretta. Cannot Update Alert Rules Impossibile aggiornare le Regole di allarme DXC Server Name Error Errore nel nome del server DXC DXC Server address must be in format<p><b>[username@]hostname:port</b> (ex. hamqth.com:7300)</p> L'indirizzo del server DXC deve essere nel formato<p><b>[nomeutente@]nomehost:porta</b> (es. hamqth.com:7300)</p> DX Cluster Password DX Cluster Password Invalid Password Password non corretta DXC Server Connection Error Errore di connessione al server DXC Filename is empty Filename vuoto Cannot write to the file Impossibile scrivere nel file QLog Information Informazioni QLog Exported. Esportati. Exported %n contact(s). Esportato %n contatto. Esportati %n contatti. Chat Error: Errore Chat: Filter name is already exists. Il nome del filtro esiste già. <b>Rig Error:</b> <b>Errore della Radio:</b> <b>Rotator Error:</b> <b>Errore del rotore:</b> <b>CW Keyer Error:</b> <b>Errore Keyer CW:</b> The fields <b>%0</b> will not be saved because the <b>%1</b> is not filled. I campi <b>%0</b> non verranno salvati perché <b>%1</b> non è compilato. Your callsign is empty. Please, set your Station Profile Il tuo nominativo è vuoto. Per favore, imposta il Profilo della tua Stazione Please, define at least one Station Locations Profile Per favore, definisci almeno un profilo di posizioni della stazione WSJTX Multicast is enabled but the Address is not a multicast address. WSJTX Multicast è abilitato ma l'indirizzo non è un indirizzo multicast. Loop detected. Raw UDP forward uses the same port as the WSJT-X receiving port. Rilevato loop. L’inoltro UDP grezzo utilizza la stessa porta del porto di ricezione WSJT-X. Rig port must be a valid COM port.<br>For Windows use COMxx, for unix-like OS use a path to device La porta della Radio deve essere una porta COM valida.<br>Per Windows utilizzare COMxx, per sistemi operativi di tipo Unix utilizzare un percorso per il dispositivo Rig PTT port must be a valid COM port.<br>For Windows use COMxx, for unix-like OS use a path to device La porta PTT della Radio deve essere una porta COM valida.<br>Per Windows utilizzare COMxx, per sistemi operativi di tipo Unix utilizzare un percorso al dispositivo <b>TX Range</b>: Max Frequency must not be 0. <b>Intervallo TX</b>: la frequenza massima non deve essere 0. <b>TX Range</b>: Max Frequency must not be under Min Frequency. <b>Intervallo TX</b>: la frequenza massima non deve essere inferiore alla frequenza minima. Rotator port must be a valid COM port.<br>For Windows use COMxx, for unix-like OS use a path to device La porta del rotore deve essere una porta COM valida.<br>Per Windows utilizzare COMxx, per sistemi operativi di tipo Unix utilizzare un percorso per il dispositivo CW Keyer port must be a valid COM port.<br>For Windows use COMxx, for unix-like OS use a path to device La porta del keyer CW deve essere una porta COM valida.<br>Per Windows utilizzare COMxx, per sistemi operativi di tipo Unix utilizzare un percorso per il dispositivo Cannot change the CW Keyer Model to <b>Morse over CAT</b><br>No Morse over CAT support for Rig(s) <b>%1</b> Impossibile modificare il modello CW Keyer in <b>Morse su CAT</b><br>Nessun supporto Morse su CAT per a Radio <b>%1</b> Cannot delete the CW Keyer Profile<br>The CW Key Profile is used by Rig(s): <b>%1</b> Impossibile eliminare il profilo CW Keyer<br>Il profilo CW Keyer è utilizzato da Radio: <b>%1</b> Callsign has an invalid format L'indicativo ha un formato non valido Operator Callsign has an invalid format Il Call dell'Operatore ha un formato non valido Gridsquare has an invalid format La Griglia ha un formato non valido VUCC Grids have an invalid format (must be 2 or 4 Gridsquares separated by ',') Le griglie VUCC hanno un formato non valido (devono essere 2 o 4 Gridsquare separati da ',') Country must not be empty Nazione non deve essere vuoto CQZ must not be empty La CQ Zone non deve essere vuota ITU must not be empty ITU non deve essere vuoto Cannot update QSO Filter Conditions Impossibile aggiornare le Condizioni del Filtro QSO QObject km km miles miglia Connection Refused Connessione rifiutata Host closed the connection Host ha chiuso la connessione Host not found Host non trovato Timeout Timeout Network Error Errore di rete Internal Error Errore Interno Importing Database Importazione database Opening Database Apertura Database Backuping Database Backup del database Migrating Database Migrazione del database Starting Application Avvio applicazione My Rig Mia Radio Logging Station Callsign Indicativo della stazione My Gridsquare Mia Griglia Operator Name Nome dell'operatore Operator Callsign Call dell'Operatore My City Mia Città My Country Mia nazione My County Mia Contea My IOTA Mio IOTA My SOTA Mio SOTA My Special Interest Activity Mie attività di interesse speciale My Spec. Interes Activity Info Informazioni sulle mie attività di interesse speciale My VUCC Grids Mia Griglia VUCC My WWFF Mio WWFF My POTA Ref Mio Ref POTA My DARC DOK Mio DARC DOK My ITU Mio ITU My CQZ Mia CQ Zone My DXCC Mio DXCC Cannot connect to DXC Server <p>Reason <b>: Impossibile connettersi al server DXC <p>Motivo <b>: <b>Imported</b>: %n contact(s) <b>Importato</b>: %n contatto <b>Importati</b>: %n contatti <b>Warning(s)</b>: %n <b>Allarme</b>: %n <b>Allarmi</b>: %n <b>Error(s)</b>: %n <b>Errore</b>: %n <b>Errori</b>: %n Not a valid QLog database Database QLog non valida Database version too new (requires newer QLog version) La versione del database è troppo recente (richiede una versione più recente di QLog) Database is not QLog Export file Il database non è un file di esportazione QLog TQSL Path TQSL Path (Flatpak internal path) (Percorso interno Flatpak) Rig Radio Rig PTT Rig rigctld Rotator Rotore CW Keyer Chiavistello CW TOTAL Worked TOTALE Lavorato TOTAL Confirmed TOTALE Confermato Confirmed Confermato Worked Lavorato QRZCallbook QRZ.com QRZ.com QRZUploader General Error Errore generico QSLGalleryDialog QSL Card Gallery Galleria delle schede QSL Search by callsign... Cerca per nominativo… Sort by: Ordina per: Export Filtered Esporta filtrati Date (Newest) Data (più recente) Date (Oldest) Data (più vecchia) Callsign (A-Z) Nominativo (A-Z) Callsign (Z-A) Nominativo (Z-A) %n QSL card(s) %n scheda(e) QSL %n scheda(e) QSL All QSL Cards Tutte le schede QSL Favorites Preferiti By Country Per paese By Date Per data By Band Per banda By Mode Per modalità By Continent Per continente Remove from Favorites Rimuovi dai preferiti Add to Favorites Aggiungi ai preferiti Open Apri Save... Salva… Save QSL Card Salva scheda QSL Export QSL Cards Esporta schede QSL Exported %1 of %2 cards Esportate %1 di %2 schede QSLImportStatDialog QSL Import Summary Riepilogo importazione QSL Summary Sommario Downloaded: Scaricato: Updated: Aggiornato: Unmatched: Senza corrispondenza: Errors: Errori: Details Dettagli New QSLs: Nuove QSLs: Updated QSOs: QSO aggiornati: Unmatched QSLs: QSL senza corrispondenza: QSLPrintLabelDialog Print QSL Labels Stampa etichette QSL Filter Filtra Date Range Intervallo di date My Callsign Mio Indicativo Station Profile Profilo della stazione QSL Sent QSL inviata User Filter Filtro Utente Label Template Modello etichetta Page Size: Dimensione pagina: Columns: Colonna: Rows: Riga: Label Width: Larghezza etichetta: Label Height: Altezza etichetta: Left Margin: Margine sinistro: Top Margin: Margine superiore: H Spacing: Spaziatura orizzontale: V Spacing: Spaziatura verticale: Label Appearance Aspetto etichetta Print Label Borders Stampa bordi etichette QSOs per Label: QSO per etichetta: Footer Left Text: Testo piè di pagina sinistro: Footer Right Text: Testo piè di pagina destro: Skip Label: Salta etichetta: Sans Font: Carattere sans-serif: Mono Font: Carattere monospaziato: Callsign Size: Dimensione del nominativo: "To Radio" Size: Dimensione «To Radio»: "To Radio" Text: Testo «To Radio»: Header Size: Dimensione intestazione: Data Size: Dimensione dati: Date Header Text: Testo intestazione data: Date Format: Formato data: Time Header Text: Testo intestazione ora: Band Header Text: Testo intestazione banda: Mode Header Text: Testo intestazione modalità: QSL Header Text: Testo intestazione QSL: Extra Column: Colonna aggiuntiva: Extra Column Text Testo della colonna aggiuntiva (DB column name) (nome colonna DB) No matching QSOs found Nessun QSO corrispondente trovato Page 0 of 0 Pagina 0 di 0 Labels: 0 (0 pages) Etichette: 0 (0 pagine) Print Stampa Export as PDF Esporta come PDF Custom Personalizzato Empty Vuoto QSOs matching this station profile QSO che corrispondono a questo profilo di stazione Labels: %1 (%2 pages) Etichette: %1 (%2 pagine) Page %1 of %2 Pagina %1 di %2 Export PDF Esporta PDF PDF Files (*.pdf) File PDF (*.pdf) Mark as Sent Contrassegna come inviato Mark printed/exported QSOs as sent? Contrassegnare i QSO stampati/esportati come inviati? QSODetailDialog dd/MM/yyyy HH:mm:ss RSTs RSTs RSTr RSTr Mode Modo Time On Inizio Time Off Fine Callsign Indicativo TX: TX: Blank Vuoto MHz MHz RX: RX: Frequency Frequenza Band Banda QTH QTH Name Nome Gridsquare Griglia Comment Commento My Notes Le mie note about:blank &Details &Dettagli Country Nazione Cont Cont ITU ITU CQ CQ State Stato County Contea Age Età AF AF AN AN AS AS EU EU NA NA OC OC SA SA VUCC VUCC two or four adjacent Gridsquares, each four characters long, (ex. EN98,FM08,EM97,FM07) due o quattro Gridsquare adiacenti, ciascuno lungo quattro caratteri, (es. EN98,FM08,EM97,FM07) FISTS FISTS SKCC SKCC Ten-Ten Ten-Ten UKSMG UKSMG DOK DOK the contacted station's DARC DOK (District Location Code) (ex. A01) il DARC DOK (District Location Code) della stazione contattata (es. A01) IOTA IOTA POTA POTA SOTA SOTA WWFF WWFF SIG SIG Info FISTS CC FISTS CC E-Mail E-Mail URL URL Propagation Mode Modo di Propagazione Satellite Name Nome del Satellite Satellite Mode Modo del Satellite M&y Station M&ia Stazione Operator Name Nome dell'operatore Operator Callsign Call dell'Operatore Rig Radio Antenna Antenna Power Potenza W W QSLr Message QSLr Messaggio QSLs Message QSLs Messaggio Contest ID Contest ID Sig Info D&X Stats D&X Statistiche <b>DXCC Statistics</b> <b>DXCC Statistics</b> <b>Station Statistics</b> <b>Station Statistics</b> &QSL &QSL Sent Inviata - Manage QSL Card Gestisci la cartolina QSL QSL Sent via QSL inviata vía <b>Yes</b> - an outgoing QSL card has been sent; the QSO has been uploaded to, and accepted by, the online service<br/><b>No</b> - do not send an outgoing QSL card; do not upload the QSO to the online service<br/><b>Requested</b> - the contacted station has requested a QSL card; the contacted station has requested the QSO be uploaded to the online service<br/><b>Queued</b> - an outgoing QSL card has been selected to be sent; a QSO has been selected to be uploaded to the online service<br/> <b>Sì</b> - è stata inviata una QSL in uscita; il QSO è stato caricato e accettato dal servizio online<br/><b>No</b> - non inviare una cartolina QSL in uscita; non caricare il QSO sul servizio online<br/><b>Richiesto</b> - la stazione contattata ha richiesto una QSL; la stazione contattata ha richiesto il caricamento del QSO sul servizio online<br/><b>Queued</b> - è stata selezionata una QSL in uscita da inviare; è stato selezionato un QSO da caricare sul servizio online<br/> Paper Cartaceo Show QSL Card Mostra QSL <b>Yes</b> - an incoming QSL card has been received; the QSO has been confirmed by the online service<br/><b>No</b> - an incoming QSL card has not been received; the QSO has not been confirmed by the online service<br/><b>Requested</b> - the logging station has requested a QSL card; the logging station has requested the QSO be uploaded to the online service<br/> <b>Sì</b> - è stata ricevuta una QSL in arrivo; il QSO è stato confermato dal servizio online<br/><b>No</b> - non è stata ricevuta una QSL in arrivo; il QSO non è stato confermato dal servizio online<br/><b>Richiesto</b> - la stazione logging ha richiesto una QSL; la stazione logging ha richiesto che il QSO venga caricato sul servizio online<br/> Received Ricevuta QSL via QSL vía LoTW LoTW eQSL eQSL &Contest &Contest RcvNr Numero Ricevuto RcvExch Scambio Ricevuto SendNr Numero Inviato SendExch Scambio inviato Member: Membro: &Reset &Reset &Lookup &Cerca No No Yes Requested Richiesto Queued In coda Ignored Ignorato Bureau Bureau Direct Diretto Electronic Elettronica Submit changes Inviare le modifiche Really submit all changes? Inviare davvero tutte le modifiche? QLog Error Errore di QLog Cannot save all changes - internal error Impossibile salvare tutte le modifiche: errore interno Cannot save all changes - try to reset all changes Impossibile salvare tutte le modifiche: provare a ripristinare tutte le modifiche QSO Detail Dettagli del QSO Edit QSO Modifica QSO Downloading eQSL Image Download immagine da eQSL Cancel Cancella eQSL Download Image failed: Download immagine da eQSL fallito: DX Callsign must not be empty Il nominativo DX non deve essere vuoto DX callsign has an incorrect format l'indicativo DX ha un formato non corretto TX Frequency or Band must be filled La frequenza o la banda TX devono essere compilate TX Band should be La Banda TX dovrebbe essere RX Band should be La Banda RX dovrebbe essere DX Grid has an incorrect format La Griglia DX ha un formato non corretto Based on callsign, DXCC Country is different from the entered value - expecting In base al nominativo, il Paese DXCC è diverso dal valore inserito come previsto Based on callsign, DXCC Continent is different from the entered value - expecting In base al nominativo, il continente DXCC è diverso dal valore inserito come previsto Based on callsign, DXCC ITU is different from the entered value - expecting In base al nominativo, DXCC ITU è diverso dal valore inserito come previsto Based on callsign, DXCC CQZ is different from the entered value - expecting In base al nominativo, DXCC CQZ è diverso dal valore inserito come previsto VUCC has an incorrect format VUCC ha un formato non corretto Based on Frequencies, Sat Mode should be In base alle frequenze, la modalità satellitare dovrebbe essere blank vuoto Sat name must not be empty Il nome del Satellite non deve essere vuoto Own Callsign must not be empty Il proprio nominativo non deve essere vuoto Own callsign has an incorrect format Il proprio indicativo ha un formato non corretto Own VUCC Grids have an incorrect format La propria Griglia VUCC ha un formato non corretto Based on own callsign, own DXCC ITU is different from the entered value - expecting In base al proprio nominativo, il proprio DXCC ITU è diverso dal valore inserito previsto Based on own callsign, own DXCC CQZ is different from the entered value - expecting In base al proprio nominativo, il proprio DXCC CQZ è diverso dal valore inserito previsto Based on own callsign, own DXCC Country is different from the entered value - expecting In base al proprio nominativo, il proprio Paese DXCC è diverso dal valore inserito previsto Based on SOTA Summit, QTH does not match SOTA Summit Name - expecting Secondo il SOTA Summit, QTH non corrisponde al nome del SOTA Summit come previsto Based on SOTA Summit, Grid does not match SOTA Grid - expecting Secondo il SOTA Summit, la Griglia non corrisponde alla Griglia SOTA, come previsto Based on POTA record, QTH does not match POTA Name - expecting Secondo i record POTA, il QTH non corrisponde al nome POTA come previsto Based on POTA record, Grid does not match POTA Grid - expecting Secondo i record POTA, la griglia non corrisponde alla griglia POTA come previsto Based on SOTA Summit, my QTH does not match SOTA Summit Name - expecting Secondo il SOTA Summit, il mio QTH non corrisponde al nome SOTA Summit come previsto Based on SOTA Summit, my Grid does not match SOTA Grid - expecting Secondo il SOTA Summit, la mia griglia non corrisponde alla griglia SOTA, come previsto Based on POTA record, my QTH does not match POTA Name - expecting Secondo i record POTA, il mio QTH non corrisponde al nome POTA come previsto Based on POTA record, my Grid does not match POTA Grid - expecting Secondo i record POTA, la mia griglia non corrisponde alla griglia POTA, come previsto LoTW Sent Status to <b>No</b> does not make any sense if QSL Sent Date is set. Set Date to 1.1.1900 to leave the date field blank Lo stato di invio LoTW di <b>No</b> non ha alcun senso se è impostata la data di invio della QSL. Imposta la data su 1.1.1900 per lasciare vuoto il campo della data Date should be present for LoTW Sent Status <b>Yes</b> La data deve essere presente per lo stato inviato LoTW <b>Sì</b> eQSL Sent Status to <b>No</b> does not make any sense if QSL Sent Date is set. Set Date to 1.1.1900 to leave the date field blank Lo stato di invio eQSL <b>No</b> non ha alcun senso se è impostata la data di invio QSL. Imposta la data su 1.1.1900 per lasciare vuoto il campo della data Date should be present for eQSL Sent Status <b>Yes</b> La data deve essere presente per lo stato inviato eQSL <b>Sì</b> Paper Sent Status to <b>No</b> does not make any sense if QSL Sent Date is set. Set Date to 1.1.1900 to leave the date field blank Lo stato di invio QSL Cartacea pari a <b>No</b> non ha alcun senso se è impostata la data di invio QSL. Imposta la data su 1.1.1900 per lasciare vuoto il campo della data Date should be present for Paper Sent Status <b>Yes</b> La data deve essere presente per lo stato di invio cartaceo <b>Sì</b> Callbook error: Errore del Callbook: <b>Warning: </b> <b>Allarme: </b> Validation Validazione Yellow marked fields are invalid.<p>Nevertheless, save the changes?</p> I campi contrassegnati in giallo non sono validi.<p>Salvare comunque le modifiche?</p> &Save &Salva &Edit &Modifica QSOFilterDetail QSO Filter Detail Dettaglio filtro QSO Filter Name: Nome del filtro: Find QSO which match Trova il QSO che corrisponde All the following conditions Tutte le seguenti condizioni Any of the following conditions Una qualsiasi delle seguenti condizioni Add Condition Aggiungi condizione Equal Uguale Not Equal Non uguale Contains Contiene Not Contains Non Contiene Greater Than Maggiore di Less Than Minore di Starts with Inizia con RegExp Remove Rimuovi Must not be empty Non deve essere vuoto QSOFilterDialog QSO Filters Filtri QSO Filters Filtri Add Aggiungi Edit Modifica Remove Rimuovi Rig No Rig Profile selected Nessun Profilo Radio selezionato Rigctld Error Errore Rigctld Initialization Error Errore di inizializzazione Internal Error Errore Interno Cannot open Rig Impossibile aprire Radio RigWidget Form RX Disconnected Disconnesso MHz MHz Disable Split Disattiva split RIT: 0.00000 MHz XIT: 0.00000 MHz PWR: %1W RigctldAdvancedDialog Rigctld Advanced Settings Impostazioni avanzate Rigctld Rigctld Path: Percorso Rigctld: Leave empty for auto-detection Lasciare vuoto per il rilevamento automatico Browse Cerca Auto-detect Rigctld path Rileva automaticamente il percorso di Rigctld Auto-Detect Rilevamento automatico Rigctld Version: Versione Rigctld: Additional Arguments: Argomenti aggiuntivi: e.g. -v -v for verbose logging es. -v -v per registrazione dettagliata Cannot be changed Non può essere modificato Auto Detect Rilevamento automatico rigctld was not found on this system. Please install Hamlib or specify the path manually. rigctld non è stato trovato su questo sistema. Installare Hamlib o specificare manualmente il percorso. Executable (*.exe);;All files (*.*) Eseguibile (*.exe);;Tutti i file (*.*) All files (*) Tutti i file (*) Select rigctld executable Seleziona eseguibile rigctld Not found Non trovato RigctldManager rigctld executable not found in /app/bin/. This should not happen in Flatpak build. Eseguibile rigctld non trovato in /app/bin/. Questo non dovrebbe accadere nella build Flatpak. rigctld executable not found. Please install Hamlib or specify the path in Advanced settings. Eseguibile rigctld non trovato. Installare Hamlib o specificare il percorso nelle impostazioni avanzate. Hamlib major version mismatch: QLog was compiled with Hamlib %1 but rigctld reports version %2.%3.%4. Rig model IDs are incompatible between major versions. Incompatibilità della versione principale di Hamlib: QLog è stato compilato con Hamlib %1, ma rigctld riporta la versione %2.%3.%4. Gli ID dei modelli dei trasmettitori non sono compatibili tra le versioni principali. Port %1 is already in use. Another rigctld or application may be running on this port. La porta %1 è già in uso. Un altro rigctld o applicazione potrebbe essere in esecuzione su questa porta. rigctld started but not responding on port %1. rigctld è stato avviato ma non risponde sulla porta %1. Failed to start rigctld: %1 %2 Impossibile avviare rigctld: %1 %2 rigctld crashed. rigctld è crashato. rigctld timed out. rigctld ha superato il tempo limite. Write error with rigctld. Errore di scrittura con rigctld. Read error with rigctld. Errore di lettura con rigctld. Unknown rigctld error. Errore rigctld sconosciuto. Rotator No Rotator Profile selected Nessun Profilo Rotore selezionato Initialization Error Errore di inizializzazione Internal Error Errore Interno Cannot open Rotator Impossibile aprire Rotore RotatorWidget Form Az: Az: ° Goto Vai a Previous Button Profile Profilo pulsante precedente Next Button Profile Profilo pulsante successivo QSO LP QSO LP QSO Long Path QSO Long Path QSO SP QSO SP QSO Short Path QSO Short Path SettingsDialog Settings Impostazioni Station Stazione Profiles Profili World Wide Flora & Fauna (Optional parameter) World Wide Flora & Fauna (Opzionale) POTA SIG Station Gridsquare (Mandatory parameter) Stazione Gridsquare (Parametro obbligatorio) Operator Name Nome dell'operatore SOTA SOTA QTH QTH SIG Information (Optional parameter) SIG Information (Opzionale) WWFF WWFF IOTA (Optional parameter) IOTA (Opzionale) Callsign Indicativo SOTA (Optional parameter) SOTA (Opzionale) Profile name that is used as the alias for the Callsign, Gridsquare, Operator name, and QTH (required parameter) Nome del profilo utilizzato come alias per Callsign, Gridsquare, Nome operatore e QTH (parametro obbligatorio) List of all available Station Profiles Elenco di tutti i profili delle stazioni disponibili Gridsquare Griglia IOTA Profile Name Nome del Profilo VUCC VUCC SIG (Optional parameter). SIG (Opzionale). VUCC Grids (Optional parameter). Ex. EN98,FM08,EM97,FM07 Griglie VUCC (parametro opzionale). Es. EN98,FM08,EM97,FM07 SIG Info SIG Info Add Aggiungi Delete Eliminare QTH Name (Optional parameter) Nome del QTH (Opzionale) Operator name (Optional parameter) Nome dell'Operatore (Opzionale) Callsign (Mandatory parameter) Indicativo (Obligatorio) ITU CQZ CQZone Operator Callsign Call dell'Operatore Country Nazione Station Callsign Call della stazione Callsign of operator (Optional parameter, if different from station callsign) Call dell'operatore (Parametro Opzionale, se differente dal Call della stazione) County Contea Station County Location (Optional parameter) Posizione della Contea della Stazione (parametro facoltativo) DOK DOK Equipment Apparato Antennas Antenne Profiles Profili Description Descrizione Azimuth Beamwidth Larghezza del fascio azimutale Azimuth Offset Azimuth Offset Valid range value is 0° - 100° (0° Unspecified) Il valore dell'intervallo valido è 0° - 100° (0° non specificato) Unspecified Non definito ° List of all available Antennas Elenco di tutte le antenne disponibili CW Keyers CW Keyers Keyer Profiles Profili Keyer List of all available CW Keyers Elenco di tutti i manipolatori CW disponibili Model Modello Keyer Mode Keyer Mode Swap Paddles Inverti le palette Paddle Only Sidetone Sidetone Freq: Frequenza del sidetone: Default Speed Velocità di Default N/A N/A WPM WPM Port Porta Use COMxx for Window or path to COM port under Unix-like OS Utilizzare COMxx per Window o il percorso della porta COM in sistemi operativi simili a Unix Baudrate Baudrate 115200 57600 38400 19200 9600 4800 2400 1200 Host Name Nome Host HamLib does not support to change a destination port. HamLib non supporta la modifica della porta di destinazione. CW Shortcut Profiles Profili Scorciatoie CW List of all available CW Shortcuts Profiles Elenco di tutti i profili di Shortcuts CW disponibili F1 Short Desciption of the Button (up to 7 chars) Breve descrizione del Pulsante (fino a 7 caratteri) F2 F3 F4 F5 F6 F7 Rigs Radio List of all available Rigs Elenco di tutte le Radio disponibili Interface Interfaccia Minimum and maximum TX frequencies. Specific ranges are derived from allowed Band in the Setting. Frequenze minime e massime TX. Intervalli specifici derivano dalla banda consentita nell'impostazione. TX Range TX Range - - Offsets Offsets Enter manually RIT or Transverter Offset Immettere manualmente RIT o l'offset del transverter MHz Enter manually XIT or Transverter offset Immettere manualmente XIT o l'offset del transverter TX: TX: Default PWR Default PWR Enter default PWR (ex. when Rig is disconnected) Inserisci PWR predefinita (es. quando la Radio è disconnessa) Blank Vuoto W W Assigned CW Keyer CW Keyer assegnato Rig Features Caratteristiche della Radio Mode Modo VFO VFO Freq Frequenza QSY Wiping Cancella QSY Power Potenza PTT State Stato del PTT TX Offset (XIT) TX Offset (XIT) RX Offset (RIT) RX Offset (RIT) CW Keyer Speed Velocità CW Keyer CW Speed Sync Sincronizza velocità CW Start rigctld daemon to share rig with other applications (e.g. WSJT-X) Avviare il demone rigctld per condividere il trasmettitore con altre applicazioni (es. WSJT-X) Share Rig via port Condividi trasmettitore tramite porta Advanced... Avanzate… Poll Interval Polling ms Port Type Tipo di porta Data Bits Bits di dati Flow Control Controllo di flusso Parity Parità 8 7 6 5 Stop Bits Bits di stop 1 2 PTT Type Tipo PTT PTT Port Porta PTT DX Spots to Rig DX Spots alla radio Split CIV Addr CIV Addr Auto Automatico RTS RTS DTR DTR Rotators Rotori Serial Seriale Network Rete User Buttons Profiles Profili dei Tasti Utente Button 1 Pulsante 1 Button 2 Pulsante 2 Button 3 Pulsante 3 Button 4 Pulsante 4 Callbook Callbook Query Order Ordine delle interrogazioni Primary Primario Secondary Secondario HamQTH Username Nome utente Password Password QRZ.com Web Lookup Button Pulsante di ricerca Web URL Specify the URL to use for quick search. The <DXCALL> macro will be replaced by the current callsign Specificare l'URL da utilizzare per la ricerca rapida. La macro <DXCALL> verrà sostituita dal nominativo corrente Test URL with your Callsign Prova l'URL con il tuo nominativo Test Test Clubs Clubs Active Lists Elenchi attivi Sync && QSL Sincronizza && QSL ClubLog E-Mail QSOs are uploaded immediately I QSO vengono caricati immediatamente Danger Zone Zona pericolosa <b>⚠ This is a danger zone. Proceed with caution, as actions performed here cannot be undone and may have a significant impact on your log.</b> <b>⚠ Questa è una zona pericolosa. Procedere con cautela, poiché le azioni eseguite non possono essere annullate e possono avere un impatto significativo sul tuo log.</b> Delete All QSOs Elimina tutti i QSO Delete All Passwords from the Secure Store Elimina tutte le password dall’archivio sicuro Immediately Upload Carica immediatamente eQSL HRDLog It is not a password. It is the upload code received via email after the registration to HRDLOG..net Non è una password. È il codice di upload ricevuto via email dopo la registrazione a HRDLOG..net Upload Code Carica codice If it is enabled and Rig is connected then QLog periodically sends On-Air messages to HRDLog Se è abilitato e la Radio è connessa, QLog invia periodicamente messaggi On-Air a HRDLog Send On-Air Invia On-Air LoTW TQSL Path TQSL Path Browse Cerca Auto-detect TQSL path Rileva automaticamente il percorso di TQSL Auto-Detect Rilevamento automatico TQSL Version Versione di TQSL Using an internal TQSL instance In uso un'istanza TQSL interna Others Altri Status Confirmed By Confermato da Paper Cartaceo Chat Chat <b>Security Notice:</b> QLog stores all passwords in the Secure Storage. Unfortunately, ON4KST uses a protocol where this password is sent over an unsecured channel as plaintext.</p><p>Please exercise caution when choosing your password for this service, as your password is sent over an unsecured channel in plaintext form.</p> <b>Avviso di sicurezza:</b> QLog memorizza tutte le password nell'archivio sicuro. Sfortunatamente, ON4KST utilizza un protocollo in cui questa password viene inviata su un canale non protetto come testo normale.</p><p>Si prega di prestare attenzione quando si sceglie la password per questo servizio, poiché la password viene inviata su un canale non protetto in formato testo normale.< /p> Bands Bande Modes Modi The '>' character is interpreted as a marker for the initial cursor position in the Report column. <br/>Ex.: '5>9' means the cursor will be positioned on the second character Il carattere ">" è interpretato come un indicatore della posizione iniziale del cursore nella colonna Report.</br>Es.: "5>9" significa che il cursore sarà posizionato sul secondo carattere Color CQ Spots Colorare gli spot CQ Enable/Disable sending color-coded status indicators back to WSJT-X for each callsign calling CQ Abilitare/disabilitare l’invio di indicatori di stato codificati a colori verso WSJT-X per ogni nominativo che chiama CQ Rig Status Radio GUI GUI Time Format Formato orario 24-hour 24 ore AM/PM AM/PM Unit System Sistema di unità Metric Metrico Imperial Imperiale Date Format Formato data System Sistema Custom Personalizzato <a href="https://doc.qt.io/qt-6/qdate.html#fromString-1">Time Format Documentation</a> <a href="https://doc.qt.io/qt-6/qdate.html#fromString-1">Documentazione del formato orario</a> DXCC <b>Notice:</b> At least a QRZ XML Subscription is recommended to access detailed information. Without a subscription, you will obtain limited data from QRZ, such as missing grid and other fields. <b>Avviso:</b> Per accedere a informazioni dettagliate si raccomanda almeno un abbonamento QRZ XML. Senza un abbonamento, i dati ottenuti da QRZ sono limitati. Default API Key Chiave API predefinita Callsign-specific API Keys Chiavi API specifiche per nominativo Wavelog Wavelog API Key Chiave API Endpoint Endpoint Wsjtx Raw UDP Forward Inoltro UDP non elaborato <p>List of IP addresses to which QLog forwards raw UDP WSJT-X packets.</p>The IP addresses are separated by a space and have the form IP:PORT <p>Elenco di indirizzi IP a cui QLog inoltra i pacchetti UDP WSJT-X grezzi.</p>Gli indirizzi IP sono separati da uno spazio e hanno la forma IP:PORT ex. 192.168.1.1:1234 192.168.2.1:1234 Port Porta Port where QLog listens an incoming traffic from WSJT-X Porta dove QLog ascolta il traffico in entrata da WSJT-X Join Multicast Unisciti a Multicast Enable/Disable Multicast option for WSJTX Abilita/Disabilita opzione Multicast per WSJTX Multicast Address Indirizzo Multicast <DXCALL> = DX Callsign <NAME> = DX Operator Name <RST> = Report 599 <RSTN> = Report 5NN <GREETING> = Greenting GM/GA/GE <QTH> = QTH <MYCALL> = My Callsign <MYNAME> = My Name <MYQTH> = My QTH <MYLOCATOR> = My Gridsquare <MYGRID> = My Gridsquare <MYSIG> = My SIG <MYSIGINFO> = My SIG Information <MYIOTA> = My IOTA <MYSOTA> = MY SOTA <MYWWFT> = My WWFT <MYVUCC> = MY VUCC <MYPWR> = My Power in W <EXCHSTR> = Contest Exchange Message <EXCHNR> = Contest Exchange Serial Number <EXCHNRN> = Contest Exchange Serial Number (9→N, 0→T) <+> = Speed +5 WPM (<++> = +10 WPM, etc.) <-> = Speed -5 WPM (<--> = -10 WPM, etc.) RX: RX: Rig Port Leave empty for auto-detection Lasciare vuoto per il rilevamento automatico Specify Multicast Address. <br>On some Linux systems it may be necessary to enable multicast on the loop-back network interface. Specificare l'indirizzo multicast. <br>Su alcuni sistemi Linux potrebbe essere necessario abilitare il multicast sull'interfaccia di rete loopback. TTL TTL Time-To-Live determines the range<br> over which a multicast packet is propagated in your intranet. Time-To-Live determina l'intervallo<br> entro il quale un pacchetto multicast viene propagato nella tua Intranet. Notifications Notifiche LogID <p>Assigned LogID to the current log.</p>The LogID is sent in the Network Nofitication messages as a unique instance identified.<p> The ID is generated automatically and cannot be changed</> <p>LogID assegnato al registro corrente.</p>Il LogID viene inviato nei messaggi di notifica di rete come un'istanza univoca identificata.<p> L'ID viene generato automaticamente e non può essere modificato</> DX Spots DX Spots <p> List of IP addresses to which QLog sends UDP notification packets with DX Cluster Spots.</p>The IP addresses are separated by a space and have the form IP:PORT <p>Elenco degli indirizzi IP a cui QLog invia pacchetti di notifiche UDP con DX Cluster Spots.</p>Gli indirizzi IP sono separati da uno spazio e hanno la forma IP:PORT Spot Alerts Allarmi Spot <p> List of IP addresses to which QLog sends UDP notification packets about user Spot Alerts.</p>The IP addresses are separated by a space and have the form IP:PORT <p>Elenco degli indirizzi IP a cui QLog invia pacchetti di notifiche UDP sugli Spot Alerts dell'utente.</p>Gli indirizzi IP sono separati da uno spazio e hanno la forma IP:PORT QSO Changes Modifiche al QSO <p> List of IP addresses to which QLog sends UDP notification packets about a new/updated/deleted QSO in the log.</p>The IP addresses are separated by a space and have the form IP:PORT <p> Elenco degli indirizzi IP a cui QLog invia pacchetti di notifica UDP relativi a un QSO nuovo/aggiornato/eliminato nel log.</p>Gli indirizzi IP sono separati da uno spazio e hanno la forma IP:PORT Wsjtx CQ Spots Wsjtx CQ Spots <p> List of IP addresses to which QLog sends UDP notification packets with WSJTX CQ Spots.</p>The IP addresses are separated by a space and have the form IP:PORT <p>Elenco degli indirizzi IP a cui QLog invia pacchetti di notifiche UDP con WSJTX CQ Spots.</p>Gli indirizzi IP sono separati da uno spazio e hanno la forma IP:PORT <p> List of IP addresses to which QLog sends UDP notification packets when Rig State changes.</p>The IP addresses are separated by a space and have the form IP:PORT <p>Elenco di indirizzi IP a cui QLog invia pacchetti di notifica UDP quando cambia lo stato della Radio.</p>Gli indirizzi IP sono separati da uno spazio e hanno il formato IP:PORT Special - Omnirig Speciale - Omnirig Cannot be changed Non può essere modificato Name Nome Report Report State Stato Start (MHz) Inizio (MHz) End (MHz) Fine (MHz) SAT Mode SAT Mode Disabled Disabilitato None Nessuno Hardware Software Software No No Even Pari Odd Dispari Mark Segna Space Spazio Dummy Fittizio Morse Over CAT Morse Over CAT WinKey WinKey CWDaemon FLDigi Single Paddle Single Paddle IAMBIC A IAMBIC B Ultimate Definitivo High High Low Low Press <b>Modify</b> to confirm the profile changes or <b>Cancel</b>. Premi <b>Modifica</b> per confermare le modifiche al profilo o <b>Annulla</b>. Modify Modifica Must not be empty Non deve essere vuoto Select File Seleziona File Auto Detect Rilevamento automatico TQSL was not found on this system. Please install TQSL or specify the path manually. TQSL non è stato trovato su questo sistema. Installare TQSL o specificare manualmente il percorso. Not found Non trovato Rig sharing is only available for Hamlib driver La condivisione del trasmettitore è disponibile solo per il driver Hamlib Rig sharing is not available for network connection La condivisione del trasmettitore non è disponibile per connessioni di rete Delete Passwords Elimina password All passwords have been deleted Tutte le password sono state eliminate Deleting all QSOs... Eliminazione di tutti i QSO... Error Errore Failed to delete all QSOs. Impossibile eliminare tutti i QSO. members Membri Required internet connection during application start Connessione Internet richiesta durante l'avvio dell'applicazione ShortcutEditorModel Description Descrizione Shortcut Scorciatoia Conflict with a built-in shortcut Conflitto con una scorciatoia integrata Conflict with a user-defined shortcut Conflitto con una scorciatoia definita dall'utente ShowUploadDialog QSOs to Upload QSOs da Caricare Selected QSO QSO Selezionato Upload Caricamento SmartSearchBox Search Ricerca StatisticsWidget Statistics Statistiche My Antenna Mia Antenna My Gridsquare Mia Griglia User Filter Filtro Utente Confirmed by Confermato da QSOs per QSOs di Percents Percentuali Top 10 Top 10 Histogram Istogramma Show on Map Mostra sulla mappa Graph Type Tipo di grafico to a My Rig Mia Radio My Callsign Mio Indicativo Band Banda Date Range Intervallo di date LoTW LoTW eQSL eQSL Paper Cartaceo Year Anno Month Mese Day in Week Giorno della settimana Hour Ora Mode Modo Continent Continente Propagation Mode Modo di Propagazione Confirmed / Not Confirmed Confermato / Non Confermato Countries Nazioni Big Gridsquares Grandi quadrati della griglia Distance Distanza QSOs QSOs Confirmed/Worked Grids Griglia Confermata/Lavorata ODX ODX Sun Dom Mon Lun Tue Mar Wed Mer Thu Gio Fri Ven Sat Sab Not specified Non specificato Confirmed Confermato Not Confirmed Non confermato No User Filter Nessun filtro utente Over 50000 QSOs. Display them? Oltre 50000 QSO. Visualizzarli? Rendering QSOs... Rendering dei QSO… All Tutti TCIRigDrv Rig 0 Radio 0 Rig 1 Radio 1 Rig 2 Radio 2 Rig 3 Radio 3 Error Occurred Si è verificato un errore Rig status changed Stato della Radio cambiato Rig is not connected Radio non connessa TimestampFormatDelegate Blank Vuoto ToAllTableModel Time Ora Spotter Spotter Message Messaggio UploadQSODialog Upload QSOs Carica QSOs eQSL eQSL LoTW LoTW QRZ.com QRZ.com Clublog Clublog HRDLog HRDLog Wavelog Wavelog Filters Filtri Station Profile Profilo della stazione My Callsign Mio Indicativo Gridsquare Griglia Include QSOs Status Includi stato QSOs Under normal circumstances this status means <b>"do not send"</b>.<br/>However, it may sometimes be wanted to ignore this setting when sending a QSL. In circostanze normali, questo stato significa <b>"non inviare".<br/>Tuttavia, a volte potresti voler ignorare questa impostazione quando invii una QSL. No No Under normal circumstances this status means <b>"Ignore/Invalid"</b>.<br/>However, it may sometimes be wanted to ignore this setting when sending a QSL. In circostanze normali questo stato significa <b>"Ignora/Non valido"</b>.<br/>Tuttavia, a volte potrebbe essere necessario ignorare questa impostazione quando si invia una QSL. Ignore Ignora None Nessuno QSLs Message QSLs Messaggio Comment Commento QSL Message Field Campo messaggio QSL QTH Profile QTH Profile This option deletes all QSOs in the Clublog<br>and based on filter, it uploads all QSOs regardless of their status. Questa opzione elimina tutti i QSO nel Clublog<br>e, in base al filtro, carica tutti i QSO indipendentemente dal loro stato. Clear Clublog and reupload QSOs Cancella Clublog e ricarica i QSO LoTW / TQSL LoTW / TQSL TQSL Station Location Posizione stazione TQSL Station Profile ID Profilo della stazione Reupload All Ricarica tutto Show QSOs... Mostra QSOs... &Upload &Caricamento Unspecified Non definito Hide QSOs... Nascondi QSOs... Uploading to %1 Caricamento su %1 Cancel Cancella QLog Warning - %1 Avviso QLog - %1 Cannot update QSO Status Impossibile aggiornare lo stato del QSO Cannot upload the QSO(s): Impossibile caricare il/i QSO: QLog Information Informazioni QLog No QSO found to upload. Nessun QSO trovato da caricare. QSO(s) were uploaded to the selected services I QSOs sono stati caricati nei servizi selezionati Time Ora Callsign Indicativo Mode Modo Upload to Carica su The values below will be used when an input record does not contain the ADIF values I valori seguenti verranno utilizzati quando una registrazione inserita non contiene i valori ADIF Any Qualsiasi Location callsign (%1) and grid (%2) do not match selected filters Il nominativo della posizione (%1) e la griglia (%2) non corrispondono ai filtri selezionati Location callsign (%1) does not match selected callsign (%2) Il nominativo della posizione (%1) non corrisponde al nominativo selezionato (%2) Location grid (%1) does not match selected grid (%2) La griglia della posizione (%1) non corrisponde alla griglia selezionata (%2) Unknown Sconosciuto Service is not configured properly.<p> Please, use <b>Settings</b> dialog to configure it.</p> UserListModel Callsign Indicativo Gridsquare Griglia Distance Distanza Azimuth Azimuth Comment Commento WCYTableModel Time Ora K expK A R SFI SA GMF Au WWVTableModel Time Ora SFI A K Info Info WsjtxFilterDialog WSJTX Filters Filtri WSJTX General Filters Filtri Generici Log Status Stato del Log New Band Nuova Banda New Mode Nuovo Modo New Entity Nuova Entità New Slot Nuovo Slot Worked Lavorato Confirmed Confermato Continent Continente North America North America Europe Europe South America South America Africa Africa Antarctica Antarctica Asia Asia Oceania Oceanía Distance more than Distanza superiore a SNR better than SNR migliore di dB Extended Filters Filtri Estesi Member Membro No Club List is enabled Nessuna Club List è abilitata WsjtxTableModel Callsign Indicativo Gridsquare Griglia Distance Distanza SNR Last Activity Ultima Attività Last Message Ultimo messaggio Member Membro WsjtxWidget Form Filtered Filtrato Column Visibility... Visibilità della colonna... Filter... Filtra... Which columns should be displayed Quali colonne devono essere visualizzate Filter Spots Filtra Spots main Run with the specific namespace. Esegui con lo spazio dei nomi specifico. namespace spazio dei nomi Translation file - absolute or relative path and QM file name. File di traduzione: percorso assoluto o relativo e nome file QM. path/QM-filename percorso/nome file QM Set language. <code> example: 'en' or 'en_US'. Ignore environment setting. Imposta la lingua. <codice> esempio: 'en' o 'en_US'. Ignora l'impostazione dell'ambiente. code codice Writes debug messages to the debug file Scrive messaggi di debug nel file di debug Process pending database import (internal use) Elaborare l’importazione in sospeso del database (uso interno) Force update of all value lists (DXCC, SATs, etc.) Forza l’aggiornamento di tutte le liste di valori (DXCC, SAT, ecc.) ================================================ FILE: i18n/qlog_zh_CN.ts ================================================ ActivityEditor Activity Editor 竞赛/活动 编辑器 Activity Name 竞赛/活动 名称 Layout 布局 Window Arrangement: 窗口排列: Saved 已保存 Clear 清除 Available Fields 可用字段 List of fields that can be used 可用字段列表 Row A 行 A Move selected fields from the Available Fields List to the Row A 将选定的字段从“可用字段列表”移动到行A Remove selected field from the Row A 从行A中删除选定的字段 List of fields in the first variable row 第一可变行字段列表 Change the order. Move selected field up 改变顺序。上移所选字段 Change the order. Move selected field down 改变顺序。下移所选字段 Row B 行 B Move selected fields from the Available Fields List to the Row B 将选定的字段从“可用字段列表”移动到行B Remove selected field from the Row B 从行B中删除选定的字段 List of fields in the second variable row 第二可变行的字段列表 Column A 列 A List of fields in the first QSO Detail Column 第一列新通联详细列字段列表 Column B 列 B List of fields in the second QSO Detail Column 第二列新通联详细列字段列表 Column C 列 C List of fields in the third QSO Detail Column 第三列新通联详细列字段列表 New QSO Rows 新通联行 New QSO Detail Columns 新通联详情列 Values 配置 Profiles 配置文件 If unchecked, the profile does not change 如果不勾选,配置文件不会更改 Station 电台 Antenna 天线 Rig 设备 Connect automatically 自动连接 Connect 连接 Rotator 云台 Fields 字段 Must not be empty 不能为空 Unsaved 未保存 AlertRuleDetail Alert Rule Detail 提醒规则详情 Rule Name 规则名称 Enabled 启用 Sources DX Cluster DX 集群 WSJTX WSJTX DX DX Worked 已通联 New Slot 新组合 Confirmed 已确认 New Entity 新实体 New Mode 新模式 New Band 新波段 DX Callsign DX 呼号 Use Perl-like regular expression 使用 类Perl 正则表达式 .* .* Country 国家/地区 Log Status 日志状态 Spot Comment 报点备注 ITU ITU CQZ CQ分区 IOTA IOTA SOTA SOTA POTA POTA WWFF WWFF Special Programs 特别活动 Modes 模式 FTx (FT8/FT4) Phone 语音通话 CW CW Digital 数字模式 Bands 波段 Continent 大洲 North America 北美洲 Africa 非洲 Antarctica 南极洲 South America 南美洲 Asia 亚洲 Europe 欧洲 Oceania 大洋洲 Member 成员 Spotter 报点者 All 全部 Must not be empty 不能为空 No Club List is enabled 未启用俱乐部列表 AlertSettingDialog Alerts Rules 提醒规则 Rules 规则 Add 添加 Edit 编辑 Remove 删除 Name 名称 State 状态 AlertTableModel Rule Name 规则名 Callsign 呼号 Frequency 频率 Mode 模式 Updated 已更新 Last Update 最后更新 Last Comment 最后备注 Member 成员 AlertWidget Alerts 提醒 Clear older than 清除旧于 Never 从不 min(s) 分钟 Edit Rules 编辑规则 Column Visibility... 列可见性... Clear 清除 AwardsDialog Awards 奖项 Options 选项 Award 奖项 My DXCC Entity 我的 DXCC 实体 User Filter 用户过滤器 Confirmed by 确认自 LoTW LoTW eQSL eQSL Paper 纸质卡片 Mode 模式 CW CW Phone 语音通话 Digi 数字模式 Not-Worked Only 未通联 Not-Confirmed Only Show 显示 DXCC DXCC ITU ITU WAC WAC WAZ WAZ WAS WAS WPX WPX IOTA IOTA POTA Hunter POTA猎人 POTA Activator POTA激活者 SOTA SOTA WWFF WWFF Gridsquare 2-Chars 网格坐标 2字符 Gridsquare 4-Chars 网格坐标 4字符 Gridsquare 6-Chars 网格坐标 6字符 Gridsquare %1-Chars US Counties Russian Districts Japanese Cities/Ku/Guns NZ Counties Spanish DMEs Ukrainian Districts No User Filter 无用户筛选器 DELETED North America 北美 South America 南美 Europe 欧洲 Africa 非洲 Oceania 大洋洲 Asia 亚洲 Antarctica 南极洲 AwardsTableModel Slots: 组合: Confirmed 已确认 Worked 已通联 Still Waiting 等待中 BandmapWidget Form Form Create an additional Bandmap Window 创建额外的波段地图窗口 Clear older 清除旧于 Clear All 清除全部 Clear the current band 清除当前波段 Never 从不 min(s) 分钟 Bandmap 波段地图 Show Band 显示波段 Center RX 居中 RX Show Emergency Frequencies SOS CWCatKey No Rig is connected 尚未连接电台 Rig does not support Morse over CAT 电台不支持 Morse over CAT Cannot send Text to Rig 无法发送文字至电台 Keyer is not connected 电键未连接 Rig is not connected 电台未连接 Cannot set Keyer Speed 无法设置电键速度 Cannot stop Text Sending 无法停止发送文字 CWConsoleWidget Form Form CW Keyer Profile CW 电键配置文件 Shortcuts profile 快捷键配置文件 Speed 速度 N/A N/A WPM WPM Sent text 发送文字 Echoed text 回显文字 &CW &CW Text to send 待发送文字 Switch between sending <b>words</b> individually (separated by spaces)<br> and sending the entire text as a <b>whole</b> (separated by a new line). 切换「逐个发送单词」(空格分隔)与「整段发送文本」(换行分隔)模式。 F1 F1 F2 F2 F3 F3 F4 F4 F5 F5 F6 F6 F7 F7 Halt 终止 CW Console - Halt Sending CW控制台 - 终止发送 Clear Sent and Echo Console 清除发送及回显窗口 Immediately stop CW sending Clear 清除 Rig must be connected 必须先连接设备 Word 单词 Whole 整句 CWDaemonKey Keyer is not connected 电键未连接 Cannot send Text 无法发送文字 Cannot set Keyer Speed 无法设置电键速度 Cannot stop Text Sending 无法停止文字发送 CWFldigiKey Connected device is not FLDigi 已连接设备不是 FLDigi Keyer is not connected 电键未连接 Cannot send Text to FLDigi 无法发送文字至 FLDigi Cannot send the Clear command to FLDigi 无法发送清除命令至 FLDigi Cannot send the TX command to FLDigi 无法发送 TX 命令至 FLDigi Cannot send the Text command to FLDigi 无法发送文字命令至 FLDigi Cannot send the Stop command to FLDigi 无法发送停止命令至 FLDigi Cannot send the Abort command to FLDigi 无法发送中断命令至 FLDigi Cannot receive data from FLDigi 无法从 FLDigi 接收数据 FLDigi connection timeout FLDigi 连接超时 FLDigi connection error FLDigi 连接出错 FLDigi command error FLDigi 命令出错 CWKeyer No CW Keyer Profile selected 未选择 CW 电键配置文件 Initialization Error 初始化出错 Internal Error 内部错误 Connection Error 连接出错 Cannot open the Keyer connection 无法打开电键连接 CWWinKey Connected device is not WinKey 已连接的设备不是 WinKey Cannot send Text to Rig 无法发送文字至设备 Keyer is not connected 电键未连接 Communication Error 通讯出错 Cannot set Keyer Speed 无法设置电键速度 Cannot stop Text Sending 无法停止文字发送 4000 Hz 2000 Hz 1333 Hz 1000 Hz 800 Hz 666 Hz 571 Hz 500 Hz 444 Hz 400 Hz CabrilloExportDialog Cabrillo Export File: Browse 浏览 Contest 比赛 Format Template: Manage User Filter: Date/Time: yyyy-MM-dd HH:mm - UTC Station & Categories Callsign: Operators: Band: Mode: Power: Operator: Assisted: Station: Transmitter: Time: Overlay: Transmitter ID: Additional Name: Email: Address: Max 6 lines Gridsquare 网格坐标 Location: Club: Soapbox: Off-Time: yyyy-mm-dd hhmm yyyy-mm-dd hhmm &Export 导出(&E) Cabrillo Files (*.log);;CBR Files (*.cbr);;All Files (*) QSO(s): %1 QSOs: ? QLog Warning Please select a contest template. Please select an output file. No callsign available. Check your filters. QLog Error Failed to prepare export query. Failed to query QSOs for export. Cannot open file %1 for writing. Exporting Cabrillo... QLog Information QLog 信息 Exported %n QSO(s) to Cabrillo file. CabrilloFormat All Bands 2m (144 MHz) 1.25m (222 MHz) 70cm (432 MHz) 33cm (902 MHz) 23cm (1.2 GHz) Light VHF 3-Band VHF FM Only Digital 数字模式 Mixed High Low QRP Single Operator Multi Operator Check Log Non-Assisted Assisted Fixed Mobile Portable Rover Rover Limited Rover Unlimited Expedition HQ School Distributed One Two Limited Unlimited SWL SWL 6 Hours 12 Hours 24 Hours Classic 经典 Rookie TB Wires Novice/Tech Over 50 Text (left-aligned) Frequency (kHz) Time (HHMM) Date (YYYY-MM-DD) RST Short (drop last digit) Uppercase Mode (Cabrillo) Zero-Padded Nr. Transmitter ID CabrilloTemplateDialog Cabrillo Template Manager Import template Import 导入 Export template Export New template New 新建 Copy existing template Copy Delete template Delete 删除 Template Name: Contest Name: Default Mode: QSO Line Columns: Contest name as required by the rules. It is possible to enter a custom string if it is not included in the list. Seq. QSO Field Formatter Width Label Add line Add 添加 Remove selected line Remove New Template Copy - %1 Delete Template Delete template '%1'? Import Template QLog Cabrillo Template (*.qct) Import Failed Export Template Export Failed Failed to write file: %1 File not found: %1 Cannot open file: %1 Invalid template file: missing name QLog Error Cannot start database transaction. QLog Warning Cannot save template '%1': %2 CallbookManager <p>The secondary callbook will be used</p> <p>次选电台黄页将被使用</p> ChatWidget ON4KST Chat ON4KST 聊天室 Chat Room 聊天室 Connect 连接 New 新建 QLog Warning QLog 告警 ON4KST Chat is not configured properly.<p> Please, use <b>Settings</b> dialog to configure it.</p> ON4KST 聊天配置不正确。<p>请使用<b>设置</b>对话框进行配置。</p> CheckBoxDelegate Enabled 启用 Disabled 禁用 ClockWidget Form Form Sunrise 日出 Sunset 日落 N/A N/A CloudlogUploader Invalid API Key 无效的API密钥 ClubLogUploader Clublog Operation for Callsign %1 failed.<br>%2 呼号 %1 的 Clublog 操作失败。<br>%2 ColumnSettingDialog Column Visibility Setting 列可见性设置 General 常规 My Info 我的信息 QSL && Callbooks QSL && 电台黄页 Members 成员 Conditionals 条件 Contests 竞赛 Others 其他 Done 完成 ColumnSettingGenericDialog Unselect All 反选全部 Select All 选择全部 ColumnSettingSimpleDialog Column Visibility Setting 列可见性设置 Done 完成 DBSchemaMigration DXCC Entities DXCC 实体 Sats Info 卫星信息 SOTA Summits SOTA 山峰 WWFF Records WWFF 记录 IOTA Records IOTA 记录 POTA Records POTA 记录 Membership Directory Records 会员名录记录 Clublog CTY.XML List of Values 值列表 Updating 更新 Update Failed 更新失败 DBStrings PHONE CW CW DIGITAL Afghanistan 阿富汗 Agalega & St. Brandon 阿加莱加 & 圣布兰登群岛 Aland Islands 奥兰群岛 Alaska 阿拉斯加 Albania 阿尔巴尼亚 Algeria 阿尔及利亚 American Samoa 美属萨摩亚 Amsterdam & St. Paul Is. 阿姆斯特丹 & 圣保罗群岛. Andaman & Nicobar Is. 安达曼 & 尼科巴群岛. Andorra 安道尔 Angola 安哥拉 Anguilla 安圭拉岛 Annobon Island 安诺本岛 Antarctica 南极洲 Antigua & Barbuda 安提瓜和巴布达 Argentina 阿根廷 Armenia 亚美尼亚 Aruba 阿鲁巴 Ascension Island 阿森松岛 Asiatic Russia 亚洲俄罗斯 Asiatic Turkey 亚洲土耳其 Austral Islands 南岛 Australia 澳大利亚 Austria 奥地利 Aves Island 阿韦斯岛 Azerbaijan 阿塞拜疆 Azores 亚速尔群岛 Bahamas 巴哈马群岛 Bahrain 巴林 Baker & Howland Islands 豪兰和贝克群岛 Balearic Islands 巴利阿里群岛 Banaba Island 巴纳巴岛 Bangladesh 孟加拉 Barbados 巴巴多斯 Belarus 白俄罗斯 Belgium 比利时 Belize 伯利兹 Benin 贝宁 Bermuda 百慕大群岛 Bhutan 不丹 Bolivia 玻利维亚 Bonaire 博奈尔岛 Bosnia-Herzegovina 波斯尼亚和黑塞哥维那 Botswana 博茨瓦纳 Bouvet 布韦特 Brazil 巴西 British Virgin Islands 英属维京群岛 Brunei Darussalam 文莱达鲁萨兰国 Bulgaria 保加利亚 Burkina Faso 布基纳法索 Burundi 布隆迪 Cambodia 柬埔寨 Cameroon 喀麦隆 Canada 加拿大 Canary Islands 加纳利群岛 Cape Verde 佛得角 Cayman Islands 开曼群岛 Central African Republic 中非共和国 Central Kiribati 中基里巴斯 Ceuta & Melilla 休达 - 梅利利亚 Chad 乍得 Chagos Islands 查戈斯群岛 Chatham Islands 查塔姆群岛 Chesterfield Islands 切斯特菲尔德群岛 Chile 智利 China 中国 Christmas Island 圣诞岛 Clipperton Island 克利珀顿岛 Cocos (Keeling) Islands 科科斯 (基林) 群岛 Cocos Island 科科斯岛 Colombia 哥伦比亚 Comoros 科摩罗 Conway Reef 康韦礁 Corsica 科西嘉岛 Costa Rica 哥斯达黎加 Cote d'Ivoire 科特迪瓦 Crete 克里特岛 Croatia 克罗地亚 Crozet Island 克罗泽岛 Cuba 古巴 Curacao 库拉索岛 Cyprus 塞浦路斯 Czech Republic 捷克共和国 DPR of Korea 朝鲜 Dem. Rep. of the Congo 刚果民主共和国 Denmark 丹麦 Desecheo Island 德塞切奥岛 Djibouti 吉布提 Dodecanese 多德卡尼斯群岛 Dominica 多米尼克 Dominican Republic 多米尼加共和国 Ducie Island 迪西岛 East Malaysia 东马来西亚 Easter Island 复活节岛 Eastern Kiribati 东基里巴斯 Ecuador 厄瓜多尔 Egypt 埃及 El Salvador 萨尔瓦多 England 英格兰 Equatorial Guinea 赤道几内亚 Eritrea 厄立特里亚 Estonia 爱沙尼亚 Ethiopia 埃塞俄比亚 European Russia 欧洲俄罗斯 Falkland Islands 福克兰群岛 Faroe Islands 法罗群岛 Fed. Rep. of Germany 德国 Fernando de Noronha 费尔南多·迪诺罗尼亚群岛 Fiji 斐济 Finland 芬兰 France 法国 Franz Josef Land 法兰士约瑟夫地群岛 French Guiana 法属圭亚那 French Polynesia 法属波利尼西亚 Gabon 加蓬 Galapagos Islands 加拉帕戈斯群岛 Georgia 格鲁吉亚 Ghana 加纳 Gibraltar 直布罗陀 Glorioso Islands 格洛里厄斯群岛 Greece 希腊 Greenland 格陵兰 Grenada 格林纳达 Guadeloupe 瓜德罗普岛 Guam 关岛 Guantanamo Bay 关塔那摩湾 Guatemala 危地马拉 Guernsey 根西岛 Guinea 几内亚 Guinea-Bissau 几内亚比绍 Guyana 圭亚那 Haiti 海地 Hawaii 夏威夷 Heard Island 赫德岛 Honduras 洪都拉斯 Hong Kong 中国香港 Hungary 匈牙利 ITU HQ ITU HQ Iceland 冰岛 India 印度 Indonesia 印度尼西亚 Iran 伊朗 Iraq 伊拉克 Ireland 爱尔兰 Isle of Man 马恩岛 Israel 以色列 Italy 意大利 Jamaica 牙买加 Jan Mayen 扬马延岛 Japan 日本 Jersey 泽西岛 Johnston Island 约翰斯顿岛 Jordan 约旦 Juan Fernandez Islands 胡安费尔南德斯群岛 Juan de Nova & Europa 新胡安 - 欧罗巴岛 Kaliningrad 加里宁格勒 Kazakhstan 哈萨克斯坦 Kenya 肯尼亚 Kerguelen Islands 凯尔盖朗群岛 Kermadec Islands 克马德克群岛 Kingdom of Eswatini 斯威士兰王国 Kure Island 库雷环礁 Kuwait 科威特 Kyrgyzstan 吉尔吉斯斯坦 Lakshadweep Islands 拉克沙群岛 Laos 老挝 Latvia 拉脱维亚 Lebanon 黎巴嫩 Lesotho 莱索托 Liberia 利比里亚 Libya 利比亚 Liechtenstein 列支敦士登 Lithuania 立陶宛 Lord Howe Island 豪勋爵岛 Luxembourg 卢森堡 Macao 中国澳门 Macquarie Island 麦夸里岛 Madagascar 马达加斯加 Madeira Islands 马德拉群岛 Malawi 马拉维 Maldives 马尔代夫 Mali 马里 Malpelo Island 马尔佩洛岛 Malta 马耳他 Mariana Islands 马里亚纳群岛 Market Reef 马凯特礁 Marquesas Islands 马克萨斯群岛 Marshall Islands 马绍尔群岛 Martinique 马提尼克 Mauritania 毛里塔尼亚 Mauritius 毛里求斯 Mayotte 马约特岛 Mellish Reef 梅利什礁 Mexico 墨西哥 Micronesia 密克罗尼西亚 Midway Island 中途岛 Minami Torishima 南鸟岛 Moldova 摩尔多瓦 Monaco 摩纳哥 Mongolia 蒙古 Montenegro 黑山共和国 Montserrat 蒙特塞拉特岛 Morocco 摩洛哥 Mount Athos 阿索斯山 Mozambique 莫桑比克 Myanmar 缅甸 N.Z. Subantarctic Is. 新西兰亚南极群岛 Namibia 纳米比亚 Nauru 瑙鲁 Navassa Island 纳弗沙岛 Nepal 尼泊尔 Netherlands 荷兰 New Caledonia 新喀里多尼亚 New Zealand 新西兰 Nicaragua 尼加拉瓜 Niger 尼日尔 Nigeria 尼日利亚 Niue 纽埃岛 Norfolk Island 诺福克岛 North Cook Islands 北库克群岛 North Macedonia 北马其顿 Northern Ireland 北爱尔兰 Norway 挪威 Ogasawara 小笠原群岛 Oman 阿曼 Pakistan 巴基斯坦 Palau 帕劳 Palestine 巴勒斯坦 Palmyra & Jarvis Islands 巴尔米拉 - 贾维斯岛 Panama 巴拿马 Papua New Guinea 巴布亚新几內亚 Paraguay 巴拉圭 Peru 秘鲁 Peter 1 Island 彼得一世岛 Philippines 菲律宾 Pitcairn Island 皮特克恩岛 Poland 波兰 Portugal 葡萄牙 Pr. Edward & Marion Is. 爱德华王子群岛 Pratas Island 东沙群岛 Puerto Rico 波多黎各 Qatar 卡塔尔 Republic of Korea 韩国 Republic of Kosovo 科索沃共和国 Republic of South Sudan 南苏丹共和国 Republic of the Congo 刚果共和国 Reunion Island 留尼旺岛 Revillagigedo 雷维利亚希赫多群岛 Rodriguez Island 罗德里格斯岛 Romania 罗马尼亚 Rotuma Island 罗图马岛 Rwanda 卢旺达 Saba & St. Eustatius 萨巴 - 圣尤斯特歇斯岛 Sable Island 塞布尔岛 Samoa 萨摩亚 San Andres & Providencia 圣安德烈斯 - 普罗维登斯岛 San Felix & San Ambrosio 圣费利克斯 - 圣安布罗斯岛 San Marino 圣马力诺 Sao Tome & Principe 圣多美和普林西比民主共和国 Sardinia 撒丁岛 Saudi Arabia 沙特阿拉伯 Scarborough Reef 斯卡伯勒礁 Scotland 苏格兰 Senegal 塞内加尔 Serbia 塞尔维亚 Seychelles 塞舌尔群岛 Sierra Leone 塞拉利昂 Singapore 新加坡 Sint Maarten 圣马丁岛 Slovak Republic 斯洛伐克共和国 Slovenia 斯洛文尼亚 Solomon Islands 所罗门群岛 Somalia 索马里 South Africa 南非 South Cook Islands 南库克群岛 South Georgia Island 南乔治亚岛 South Orkney Islands 南奥克尼群岛 South Sandwich Islands 南桑威奇群岛 South Shetland Islands 南谢特兰群岛 Sov Mil Order of Malta 马耳他骑士团 Spain 西班牙 Spratly Islands 南沙群岛 Sri Lanka 斯里兰卡 St. Barthelemy 圣巴特岛 St. Helena 圣赫勒拿岛 St. Kitts & Nevis 圣基茨和尼维斯联邦 St. Lucia 圣卢西亚 St. Martin 圣马丁岛 St. Paul Island 圣保罗岛 St. Peter & St. Paul 圣彼得 - 圣保罗岛 St. Pierre & Miquelon 圣皮埃尔 - 密克隆群岛 St. Vincent 圣文森特岛 Sudan 苏丹 Suriname 苏里南 Svalbard 斯瓦尔巴特群岛 Swains Island 斯温斯岛 Sweden 瑞典 Switzerland 瑞士 Syria 叙利亚 Taiwan 中国台湾 Tajikistan 塔吉克斯坦 Tanzania 坦桑尼亚 Temotu Province 泰莫图省 Thailand 泰国 The Gambia 冈比亚 Timor - Leste 东帝汶 Togo 多哥 Tokelau Islands 托克劳群岛 Tonga 汤加 Trindade & Martim Vaz 特林达迪 - 马丁瓦斯群岛 Trinidad & Tobago 特立尼达和多巴哥 Tristan da Cunha & Gough Islands 特里斯坦-达库尼亚群岛 Tromelin Island 特罗姆兰岛 Tunisia 突尼斯 Turkmenistan 土库曼斯坦 Turks & Caicos Islands 特克斯和凯科斯群岛 Tuvalu 图瓦卢 UK Base Areas on Cyprus 塞浦路斯英属基地区 US Virgin Islands 美属维京群岛 Uganda 乌干达 Ukraine 乌克兰 United Arab Emirates 阿拉伯联合酋长国 United Nations HQ 联合国 HQ United States 美国 Uruguay 乌拉圭 Uzbekistan 乌兹别克斯坦 Vanuatu 瓦努阿图 Vatican City 梵蒂冈 Venezuela 委内瑞拉 Vietnam 越南 Wake Island 威克岛 Wales 威尔士 Wallis & Futuna Islands 瓦利斯和富图纳群岛 West Malaysia 西马来西亚 Western Kiribati 西基里巴斯 Western Sahara 西撒哈拉 Willis Island 威利斯岛 Yemen 也门 Zambia 赞比亚 Zimbabwe 津巴布韦 DXCCSubmissionDialog DXCC Submission List Options 选项 My DXCC Entity 我的 DXCC 实体 User Filter 用户过滤器 Category Mixed CW CW Phone 语音通话 Digital 数字模式 Confirmed by 确认自 LoTW LoTW Paper Show 显示 Not Yet Submitted Submitted (Not Granted) Already Granted Band Scope Select 5-Band DXCC preset bands (80/40/20/15/10m) 5-Band DXCC Select all DXCC-eligible bands All DXCC Bands Bands 波段 Select options above and the list will update automatically. Export the contacts listed above to an ADIF file Export No User Filter 无用户筛选器 Any Band (Entity Level) 5-Band DXCC (80/40/20/15/10m) Custom Band Selection Entity Prefix 前缀 Callsign 呼号 Band 波段 Mode 模式 Date 日期 Submitted Granted Export ADIF No contacts to export. Failed to retrieve contact records. Export DXCC Submission List as ADIF any band 5-band all bands %1 selected band(s) No contacts match the selected criteria. %1 %2 %3 — DXCC %4 / %5 band-slot entity entry entries Data New Entity 新实体 New Band 新波段 New Mode 新模式 New Band&Mode 新波段模式(&M) New Slot 新组合 Confirmed 已确认 Worked 已通联 Hz Hz kHz kHz GHz GHz MHz MHz Yes No Requested 已请求 Queued 排队中 Invalid 无效 Bureau 卡片局 Direct 直邮 Electronic 电子卡片 Blank 空白 Modified 已修改 Grayline 灰线 Other 其他 Short Path 短路径 Long Path 长路径 Not Heard 没听到过 Uncertain 不确定 Straight Key 直键 Sideswiper 扫拨键 Mechanical semi-automatic keyer or Bug 机械半自动或臭虫键 Mechanical fully-automatic keyer or Bug 机械全自动或臭虫键 Single Paddle 单桨键 Dual Paddle 双桨键 Computer Driven 电脑驱动 Confirmed (AG) Confirmed (no AG) Unknown 未知 Aircraft Scatter Aircraft Scatte 飞机反射 Aurora-E 极光-E Aurora 极光 Back scatter 反向散射 EchoLink EchoLink Earth-Moon-Earth 地球-月球-地球 Sporadic E 突发 E 层 F2 Reflection F2 反射 Field Aligned Irregularities 场向不规则体 Ground Wave 地波 Internet-assisted 互联网协助 Ionoscatter 电离层散射 IRLP IRLP Line of Sight 视线 Meteor scatter 流星余迹 Terrestrial or atmospheric repeater or transponder 陆地或大气中的中继器或转发器 Rain scatter 雨滴散射 Satellite 卫星 Trans-equatorial 跨赤道 Tropospheric ducting 对流层波导 DateFormatDelegate Blank 空白 DevToolsDialog Developer Tools Debug Log Logging Rules: e.g. qlog.ui.*.runtime=true Apply Generate Debug File Export File SQL Console Open SQL Save SQL Run SQL Export As Enter SQL query here... Ctrl+Return = run TXT CSV CSV ADI Open SQL Query SQL Files (*.sql);;All Files (*) Open Error Cannot open file: %1 Save SQL Query Save Error Cannot save file: %1 Query saved to %1 Error: %1 %1 row(s) shown (truncated at %2) in %3 ms %1 row(s) returned in %2 ms Export No results to export. Export Error Cannot open file for writing: %1 Exported %1 row(s) to %2 Text Files (*.txt);;All Files (*) CSV Files (*.csv);;All Files (*) ADIF Export ADIF export requires the query to include the contacts 'id' column. Example: SELECT id, callsign FROM contacts WHERE ... No valid contact IDs found in the result set. Failed to retrieve contact records: %1 No matching contacts found in the database. Logging rules cleared (defaults) Logging rules applied Save Debug Log No debug log file is currently being written Log Files (*.log);;All Files (*) Debug log saved to %1 Failed to copy the debug log file. File logging is disabled Log file: %1 DownloadQSLDialog Download QSLs 下载QSLs eQSL eQSL eQSL QTH Profile eQSL QTH昵称 QSLs Since QSLs自 QSOs Since QSOs自 LoTW LoTW My Callsign 我的呼号 &Download &下载 LoTW is not configured properly.<p> Please, use <b>Settings</b> dialog to configure it.</p> LoTW 配置不正确。<p>请使用<b>设置</b>对话框进行配置。</p> eQSL is not configured properly.<p> Please, use <b>Settings</b> dialog to configure it.</p> eQSL 配置不正确。<p>请使用<b>设置</b>对话框进行配置。</p> Cancel 取消 Downloading from %1 从%1下载 Processing %1 QSLs 正在处理 %1 QSLs QLog Error QLog错误 %1 update failed: %1 更新失败: QLog Information QLog 信息 No service selected 未选择服务 DxFilterDialog DX Cluster Filters DX动态过滤器 General Filters 通用过滤器 Bands 波段 Modes 模式 Phone 语音通话 CW CW Digital 数字模式 Continent 大洲 North America 北美洲 Africa 非洲 Antarctica 南极洲 South America 南美洲 Asia 亚洲 Europe 欧洲 FTx (FT8/FT4) Oceania 大洋洲 Log Status 日志状态 New Mode 新模式 New Entity 新实体 New Band 新波段 New Slot 新组合 Worked 已通联 Confirmed 已确认 Extended Filters 扩展过滤器 Spotter Continent 报点者大洲 Do not show the following spots when they have the same Callsign and their time difference is lower than Time Diff and their frequency difference is lower than Freq Diff 若以下报点满足:呼号相同、时间差小于【时间差阈值】且频率差小于【频率差阈值】,则自动过滤 Spots Dedup 报点去重 Time difference 时间偏差 s Frequency difference 频率偏差 kHz Member 成员 No Club List is enabled 未启用俱乐部列表 DxTableModel Time 时间 Callsign 呼号 Frequency 频率 Mode 模式 Spotter 报点者 Comment 备注 Continent 大洲 Spotter Continent 报点者大洲 Band 波段 Member 成员 Country 国家/地区 DxWidget Insert a <b>hostname:port</b> of DXC Server. 增加 DXC 服务器 <b>hostname:port</b>。 Connect 连接 Filtered 过滤的内容 Spots 报点 WCY WCY WWV WWV To All 对所有人 Console 控制台 15-min Trend 15分钟趋势 Full-text search 全文搜索 Search 搜索 Close Search 关闭搜索 Send DX Cluster Command 发送 DX 集群命令 Filter... 过滤器... Column Visibility... 列可见性... Connect on startup 启动时自动连接 Automatic connection after start 程序启动后自动连接服务器 Delete Server 删除服务器 DXC - Delete Server DXC - 删除服务器 Clear Password 清除密码 Keep Spots 保留报点 Spots are not cleared when connecting to a new DX Cluster. 连接至新的 DX 集群后,不清除旧的报点。 Clear 清除 Clear all data 清除所有数据 Search... 搜索... DXC - Search DXC - 搜索 Filter DXC DXC 过滤器 Spot Last QSO 最后通联报点 Send last QSO spot 发送最后的通联报点 Show HF Stats 显示 HF 状态 Show VHF Stats 显示 VHF 状态 Show WCY 显示 WCY Show WWV 显示 WWV Which columns should be displayed 应该显示哪些列 My Continent 我的大洲 Auto 自动 Connecting... 正在连接... DX Cluster is temporarily unavailable DX 集群暂时不可用 DXC Server Error DXC 服务器出错 An invalid callsign 无效的呼号 DX Cluster Password DX 集群密码 Security Notice 安全注意 The password can be sent via an unsecured channel 密码可能通过不安全的通道发送 Server 服务器 Username 用户名 Disconnect 断开连接 DX Cluster Command DX集群命令 DxccTableModel Worked 已通联 eQSL eQSL LoTW LoTW Paper 纸质卡片 DxccTableWidget Mode 模式 EQSLQSLDownloader Incorrect Password or QTHProfile Id 无效的密码或者 QTH 配置文件 ID ADIF file not found in eQSL response 在 eQSL 响应中找不到 ADIF 文件 Incorrect Username or password 用户名或密码不正确 Unknown Error 未知错误 Cannot opet temporary file 无法打开临时文件 Cannot save the image to file 无法保存图像至文件 EQSLUploader Unknown Reply from eQSL 来自 eQSL 的未知回复 EditActivitiesDialog Edit Activities 编辑活动 Activities 活动 Add 添加 Edit 编辑 Remove 删除 ExportDialog Export Selected QSOs 导出已选择的QSO File 文件 Browse 浏览 ADX ADX CSV CSV JSON JSON POTA POTA Export Type 导出类型 Column set 列设置 Select one of the pre-defined sets of columns or define your own set of columns 选择一个预定义的列集或定义您自己的列集 Edit current set of columns 编辑当前列集 Edit 编辑 Mark exported QSOs As Sent 将已导出的 QSOs 标记为已发送 Export only QSOs that match the active filters 只导出与活动过滤器匹配的 QSO Filters 过滤器 Export only QSOs that match the selected date range 只导出与所选日期范围匹配的 QSO Date Range 时间范围 Export only QSOs that match the selected My Callsign 只导出与所选“我的呼号”匹配的 QSO My Callsign 我的呼号 Export only QSOs that match the selected My Gridsquare 仅导出与所选"我的网格坐标"匹配的 QSO My Gridsquare 我的网格坐标 Export only QSOs that match the selected QSL Send Via value 仅导出与所选 QSL 发送经由 值匹配的 QSO QSL Send via QSL 发送经由 Include unusual QSO Sent statuses 包括不寻常的 QSO 发送状态 Include Sent Status 包含发送状态 Under normal circumstances this status means <b>"Ignore/Invalid"</b>.<br/>However, it may sometimes be wanted to ignore this setting when sending a QSL. 在正常情况下,此状态表示<b>“忽略/无效”</b>。<br/>然而,有时可能希望在发送 QSL 时忽略此设置。 "Ignore" “忽略” Under normal circumstances this status means <b>"do not send"</b>.<br/>However, it may sometimes be wanted to ignore this setting when sending a QSL. 在正常情况下,此状态表示<b>“不发送”</b>。<br/>然而,有时可能希望在发送 QSL 时忽略此设置。 "No" "否" Resend already sent QSOs 重新发送已发送过的 QSOs Already Sent 已经发送 User Filter 用户过滤器 Export QSOs that match the selected user QSO Filter 导出匹配已选择的用户QSO过滤器的通联记录 Station Profile 台站配置 Export QSOs 导出 QSO &Export 导出(&E) Export only QSOs matching this station profile Exporting... Cancel 取消 QLog Error QLog 错误 Cannot mark exported QSOs as Sent 无法标记已导出的 QSOs 为已发送 Generic 常规 QSLs QSL All 全部 Minimal 最小化 QSL-specific QSL-特定 Custom 1 自定义1 Custom 2 自定义2 Custom 3 自定义3 ExportPasswordDialog Pack Data & Settings Enter a password to encrypt stored credentials. The password will be needed to restore them later. Password 密码 Random Confirm Password Delete passwords from Credential Store after export FlrigRigDrv Timeout 超时 FLRig response timeout FLRig响应超时 Network Error 网络错误 HRDLogUploader Response message malformed 响应消息格式错误 HamQTHCallbook HamQTH HamQTH HamlibRigDrv None CAT DTR RTS Initialization Error 初始化出错 Cannot set PTT Type 无法设置PTT类型 Cannot set PTT Share 无法设置共享PTT Cannot set CIV Addr Unsupported Rig Driver 不支持的设备驱动 Cannot set auto_power_on 无法设置自动开机 Cannot set no_xchg to 1 Rig Open Error 打开电台出错 Set TX Frequency Error Set Frequency Error 设置频率出错 Set Split Error Set Mode Error 设置模式出错 Set Split Mode Error Set PTT Error 设置 PTT 出错 Cannot sent Morse 无法发送摩尔斯 Cannot stop Morse 无法停止摩尔斯 Get PTT Error 获取PTT出错 Get Frequency Error 获取频率出错 Get Mode Error 获取模式出错 Get VFO Error 获取VFO出错 Get PWR Error 获取功率出错 Get PWR (power2mw) Error 获取功率(power2mw)出错 Get RIT Function Error 获取RIT功能出错 Get RIT Error 获取RIT出错 Get XIT Function Error 获取XIT功能出错 Get XIT Error 获取XIT出错 Get Split Error Get TX Frequency Error Get KeySpeed Error 获取电键速度出错 Set KeySpeed Error 设置电键速度出错 HamlibRotDrv Initialization Error 初始化出错 Unsupported Rotator Driver 不支持的云台驱动 Rot Open Error 打开云台出错 Set Possition Error 设置方位出错 Get Possition Error 获取方位出错 ImportDialog Import 导入 Date Range 日期范围 Import all or only QSOs from the given period 导入所有或仅导入指定时间段的 QSO All 全部 File 文件 ADX ADX Browse 浏览 Options 选项 The value is used when an input record does not contain the ADIF value 当输入记录不包含ADIF值时使用该值 Defaults 默认 My Profile 我的配置文件 My Rig 我的设备 Comment 备注 If DXCC is missing in the imported record, it will be resolved from the callsign. Fill missing DXCC Entity Information &Import 导入(&I) Select File 选择文件 The values below will be used when an input record does not contain the ADIF values 当输入记录不包含ADIF值时,将使用下面的值 <p><b>In-Log QSO:</b></p><p> <p><b>在日志 QSO:</b></p><p> <p><b>Importing:</b></p><p> <p><b>导入中:</b></p><p> Duplicate QSO 重复的 QSO <p>Do you want to import duplicate QSO?</p>%1 %2 <p>是否要导入重复的QSO?</p> %1 %2 Save to File 保存至文件 QLog Import Summary QLog 导入摘要 Import date 导入日期 Imported file 导入的文件 Imported: %n contact(s) 已导入: %n 通联记录 Warning(s): %n 告警: %n Error(s): %n 错误: %n Details 详情 Import Result 导入结果 Save Details... 保存详情... InputPasswordDialog Dialog 对话框 The password will be stored in the Credential Store. 密码将存储在凭据存储库中。 Remember Password 记住密码 KSTChat Unknown User 未知用户 Invalid password 无效的密码 KSTChatWidget Chat messages 聊天消息 Valuable messages 有价值的消息 Clear selected Callsign and Chat message entry. 清除选定的呼号和聊天消息条目。 Send chat message 发送聊天消息 Column Visibility 列可见性 Which columns should be displayed 应该显示哪些列 Prepare QSO 准备 QSO Transfer Callsign and Gridsquare Information to the New QSO dialog 将呼号和网格信息转移到新 QSO 对话框 Show About Me Only 只显示跟我有关 Show only messages where my callsign is present 只显示带有我呼号的信息 Suppress User To User 禁止用户对用户 Suppress private messages between two callsigns 禁止两个呼号之间的私信 Highlight 高亮 Highlight messages based on the setting 根据设置突出显示消息 Highlight Rules 高亮规则 Beam 波束 Clear Messages 清除消息 Clear all messages in the window 清除窗口内的所有消息 You KSTHighlightRuleDetail Edit Rule 编辑规则 Rule Name 规则名称 Chat Room 聊天室 Enabled 启用 Highlight message which match 突出显示匹配的消息 All the following conditions 以下所有条件 Any of the following conditions 以下任一条件 Add Condition 添加条件 All 全部 Sender 发送者 Message 信息 Gridsquare 网格坐标 Contains 包含 Starts with 开始自 Remove 删除 Must not be empty 不能为空 KSTHighlighterSettingDialog Highlight Rules 高亮规则 Rules 规则 Add 添加 Edit 编辑 Remove 删除 Name State 状态 KeySequenceEdit Clear 清除 LoadDatabaseDialog Unpack Data & Settings Warning 告警 <html><head/><body><p>⚠ <span style=" font-weight:700;">Current database will be DELETED!</span><br/>⚠ <span style=" font-weight:700;">All stored passwords will be DELETED!</span><br/>⚠ <span style=" font-weight:700;">The application will restart after loading</span>.</p><p>To preserve data, use Pack Data &amp; Settings or Export QSOs first.</p></body></html> Select Database File: Browse 浏览 Status: No file selected Decrypt Credentials Enter password to decrypt credentials Password: Load && Restart Invalid password 无效的密码 Select Database File QLog Database Export (*.dbe);;All Files (*) Cannot create temporary file Decompressing database... Cannot decompress database file Cannot open database Valid database Different platform Password required to import credentials No encrypted credentials in database LogFormat Cannot find My DXCC Entity Info 无法找到我的DXCC实体信息 A minimal set of fields not present (start_time, call, band, mode, station_callsign) 需要提供最小字段集 (start_time, call, band, mode, station_callsign) Outside the selected Date Range 超出指定的日期范围 Duplicate 重复 DXCC Info is missing DXCC信息丢失 no Station Callsign present 未提供电台呼号 Cannot insert to database 无法插入到数据库中 Imported 已导入 DXCC State: DXCC状态: Error 错误 Warning 告警 LogbookModel QSO ID QSO ID Time on 开始时间 Time off 结束时间 Call 呼号 RSTs RST发 RSTr RST收 Frequency 频率 Band 波段 Mode 模式 Submode 子模式 Name (ASCII) 姓名(ASCII) QTH (ASCII) QTH (ASCII) Gridsquare 网格坐标 DXCC DXCC Country (ASCII) 国家(ASCII) Continent 大洲 CQZ CQ分区 ITU ITU Prefix 前缀 State County 县/郡 IOTA IOTA QSLr QSL收 QSLr Date QSL收日期 QSLs QSL发 QSLs Date QSL发日期 LoTWr LoTW收 LoTWr Date LoTW收日期 LoTWs LoTW发 LoTWs Date LoTW发日期 TX PWR 发送功率 Additional Fields 附加字段 Address (ASCII) 地址 (ASCII) Address 地址 Age 年龄 Altitude 海拔 A-Index A-指数 Antenna Az 天线方位 Antenna El 天线单元数 Signal Path 信号路径 ARRL Section ARRL 划分 Award Submitted 已提交奖项 Award Granted 已授予奖项 Band RX 波段 RX Gridsquare Extended 网格坐标扩展 Contest Check 竞赛检查 Class 等级 ClubLog Upload Date ClubLog 上传日期 ClubLog Upload State ClubLog 上传状态 Comment (ASCII) 备注 (ASCII) Comment 备注 Contacted Operator 联系过的操作员 Contest ID 竞赛标识 Country 国家/地区 County Alt 县/郡备用标识 Credit Submitted 积分已提交 Credit Granted 已授予的积分 DOK DOK DCLr Date DCLr 日期 DCLs Date DCLs 日期 DCLr DCLs Distance 距离 Email 电子邮件 Owner Callsign 主人呼号 eQSL AG eQSLr Date eQSL 接收日期 eQSLs Date eQSL 发送日期 eQSLr eQSL 接收 eQSLs eQSL 发送 FISTS Number FISTS 序号 FISTS CC FISTS CC EME Init EME Init Frequency RX 频率 RX Guest Operator 客座操作员 HamlogEU Upload Date HamlogEU 上传日期 HamlogEU Upload Status HamlogEU 上传状态 HamQTH Upload Date HamQTH 上传日期 HamQTH Upload Status HamQTH 上传状态 HRDLog Upload Date HRDLog 上传日期 HRDLog Upload Status HRDLog 上传状态 IOTA Island ID IOTA 岛屿 ID K-Index K-指数 Latitude 纬度 Longitude 经度 Max Bursts 最大流星散射爆发 MS Shower Name 流星雨名称 My Altitude 我的海拔 My Antenna (ASCII) 我的天线 (ASCII) My Antenna 我的天线 My City (ASCII) 我的城市 (ASCII) My City 我的城市 My County 我的县/郡 My County Alt 我的县/郡备用标识 My Country (ASCII) 我的国家/地区 (ASCII) My Country 我的国家/地区 My CQZ 我的 CQ 分区 My DARC DOK 我的DARC DOK My DXCC 我的 DXCC My FISTS 我的 FISTS My Gridsquare 我的网格坐标 My Gridsquare Extended 我的网格扩展 My IOTA 我的 IOTA My IOTA Island ID 我的 IOTA 海岛 ID My ITU 我的 ITU My Latitude 我的纬度 My Longitude 我的经度 My Name (ASCII) 我的姓名 (ASCII) My Name 我的姓名 My Postal Code (ASCII) 我的邮编 (ASCII) My Postal Code 我的邮编 My POTA Ref 我的 POTA 编号 My Rig (ASCII) 我的设备 (ASCII) My Rig 我的设备 My Special Interest Activity (ASCII) 我的特殊兴趣活动 (ASCII) My Special Interest Activity 我的特殊兴趣活动 My Spec. Interes Activity Info (ASCII) 我的特殊兴趣活动信息 (ASCII) My Spec. Interest Activity Info 我的特殊兴趣活动信息 My SOTA 我的 SOTA My State 我的省/州 My Street 我的街道 My USA-CA Counties 我的 USA-CA 县 My VUCC Grids 我的 VUCC 网格 Name Notes (ASCII) 笔记(ASCII) QRZ Download Date QRZ下载数据 QRZ Download Status QRZ下载状态 QSLs Message (ASCII) QSLs 信息 (ASCII) QSLs Message QSLs 信息 QSLr Message QSLr 信息 RcvPWR 接收功率 RcvNr 接收序号 RcvExch 接收交换信息 SentNr 发送序号 SentExch 发送交换信息 Notes 笔记 #MS Bursts 流星散射爆发数 #MS Pings 流星散射 ping 数 POTA POTA Contest Precedence 比赛优先 Propagation Mode 传播模式 Public Encryption Key 公钥加密算法 QRZ Upload Date QRZ 上传日期 QRZ Upload Status QRZ 上传状态 QSL Message QSL 信息 CW Key Info CW电键信息 CW Key Type CW电键类型 My CW Key Info 我的CW电键信息 My CW Key Type 我的CW电键类型 Operator Callsign 操作员呼号 QSLr Via QSL 接收经由 QSLs Via QSL 发送经由 QSL Via QSL 经由 QSO Completed 通联完成 QSO Random 随机通联 QTH QTH Region 地区 Rig (ASCII) 设备 (ASCII) Rig 设备 SAT Mode 卫星模式 SAT Name 卫星名称 Solar Flux 太阳通量 SIG (ASCII) 特别活动或兴趣团体名 (ASCII) SIG SIG SIG Info (ASCII) 特别活动或兴趣团体信息 (ASCII) SIG Info SIG 信息 Silent Key 静默电键 SKCC Member SKCC 成员 SOTA SOTA Logging Station Callsign 日志电台呼号 SWL SWL Ten-Ten Number Ten-Ten 序号 UKSMG Member UKSMG 成员 USA-CA Counties USA-CA 县 VE Prov VE 省 VUCC VUCC Web 网站 My ARRL Section 我的 ARRL 划分 My WWFF 我的 WWFF WWFF WWFF RST Sent RST 发 RST Rcvd RST 收 Paper 纸质卡片 LoTW LoTW eQSL eQSL QSL Received QSL 已接收 QSL Sent QSL 已发送 LogbookWidget Delete 删除 Logbook - Delete QSO 日志本 - 删除 QSO Upload to Clublog 上传至Clublog Lookup on Web 在网页中查看 Update from Callbook 从电台黄页更新 Add Missing Info 添加丢失的信息 Mark QSL RCVD Mark QSL Requested Filter Callsign 过滤呼号 Edit Value 编辑值 Logbook - Edit Value 日志本 - 编辑值 Column Visibility 列可见性 Which columns should be displayed 应该显示哪些列 Export Selected 导出已选择项 Export selected QSOs 导出已选择的QSOs Send DX Spot 发送DX报点 Logbook - Send DX Spot Logbook - 发送DX报点 Callsign 呼号 Gridsquare 网格坐标 POTA POTA SOTA SOTA WWFF WWFF SIG SIG IOTA IOTA Delete the selected contacts? 删除选中的联系人? Clublog's <b>Immediately Send</b> supports only one-by-one deletion<br><br>Do you want to continue despite the fact<br>that the DELETE operation will not be sent to Clublog? Clublog's <b>Immediately Send</b> supports only one-by-one deletion<br><br>Do you want to continue despite the fact<br>that the DELETE operation will not be sent to Clublog? Deleting QSOs 删除 QSO Update 更新 By updating, all selected rows will be affected.<br>The value currently edited in the column will be applied to all selected rows.<br><br>Do you want to edit them? 通过更新,所有选定的行都将受到影响。<br>当前在列中编辑的值将应用于所有选定的行。<br><br>您想要编辑他们吗? Count: %n 计数:%n Downloading eQSL Image 正在下载 eQSL 图片 Cancel 取消 All Bands 所有波段 All Modes 所有模式 All Countries 所有国家和地区 No User Filter 无用户筛选器 QLog Warning QLog警告 Each batch supports up to 100 QSOs. 每个批次最多支持 100 个 QSO。 QSOs Update Progress QSO更新进度 QLog Error QLog 错误 Callbook login failed 电台黄页登陆失败 Callbook error: 电台黄页错误: All Clubs 所有俱乐部 eQSL Download Image failed: eQSL 图片下载失败: LotwQSLDownloader Cannot open temporary file 无法打开临时文件 Incorrect login or password LotwUploader Upload cancelled by user 用户取消上传 Upload rejected by LoTW 上传被 LoTW 拒绝 Unexpected response from TQSL server 来自 TQSL 服务器的意外响应 TQSL utility error TQS L实用程序错误 TQSLlib error TQSLlib 错误 Unable to open input file 无法打开输入文件 Unable to open output file 无法打开输出文件 All QSOs were duplicates or out of date range 所有 QSO 都是重复的或超出了日期范围 Some QSOs were duplicates or out of date range 部分 QSO 是重复的或超出了日期范围 Command syntax error 命令语法错误 LoTW Connection error (no network or LoTW is unreachable) LoTW 连接错误 (没有网络或 LoTW 不可达) Unexpected Error from TQSL 来自 TQSL 的意外错误 TQSL not found 未找到 TQSL TQSL crashed TQSL 已崩溃 MainWindow &File 文件(&F) &Logbook 日志本(&L) &Equipment 设备(&E) &Help 帮助(&H) &Window 窗口(&W) Se&rvice 服务(&R) Toolbar 工具栏 Clock 时钟 Map 地图 DX Cluster DX动态 WSJTX WSJTX Rotator 云台 Bandmap 波段地图 Rig 设备 Online Map 在线地图 CW Console CW 控制台 Chat 聊天 Profile Image 资料图片 Alerts 提醒 &Settings 设置(&S) &Import 导入(&I) &Export 导出(&E) Connect R&ig 连接设备(&I) &About 关于(&A) Upload 上传 Service - Upload QSOs 服务-上传 QSOs Download QSLs 下载 QSLs Service - Download QSLs 服务-下载 QSLs Quit 退出 Application - Quit 程序 - 退出 New QSO - Clear 新 QSO - 清除 New QSO - Save 新 QSO - 保存 S&tatistics 统计(&T) Wsjtx Wsjtx Connect R&otator 连接云台(&O) QSO &Filters QSO过滤(&F) &Awards 奖项(&A) Edit Rules 编辑规则 Clear 清除 Show Alerts 显示提醒 Beep 鸣响 Contest 比赛 Dupe Check 查重 Sequence 序列 Linking Exchange With 链接交换信息至 Pack Data && Settings Unpack Data && Settings QSL &Gallery Developer Tools Run custom read-only SQL queries against the logbook database Print QSL &Labels DXCC &Submission List Generate a list of contacts to submit for ARRL DXCC award credit Connect &CW Keyer 连接CW键(&C) &Wiki &Wiki Report &Bug... 报告&Bug... &Manual Entry 手动输入(&M) Switch New Contact dialog to the manually entry mode<br/>(time, freq, profiles etc. are not taken from their common sources) 将 “新建联系人” 对话框切换到手动输入模式<br/>(时间、频率、配置文件等不是从它们的公共来源中获取) Mailing List... 邮件列表 ... Edit 编辑 Save Arrangement 保存布局 Keep Options 保持选项 Restore connection options after application restart 应用程序重新启动后恢复连接选项 Logbook - Search Callsign 日志本 - 查找呼号 New QSO - Add text from Callsign field to Bandmap 新 QSO - 将呼号字段文本添加到波段图上 Rig - Band Down 设备 - 波段向下 Rig - Band Up 设备 - 波段向上 New QSO - Use Callsign from the Whisperer 新 QSO - 使用来自 Whisperer 的呼号 CW Console - Key Speed Up CW 控制台 - 增加键速 CW Console - Key Speed Down CW 控制台 - 减慢键速 CW Console - Profile Up CW 控制台 - 上一个配置文件 CW Console - Profile Down CW 控制台 - 下一个配置文件 Rig - PTT On/Off 设备 - PTT ON/OFF All Bands 全部波段 Each Band 每个波段 Each Band && Mode 每个波段 && 模式 No Check 不做检查 Single 统一值 Per Band 各波段独立 Stop 停止 Reset 重置 None Theme: Native Theme: QLog Light Theme: QLog Dark What's New Export Cabrillo Color Theme Not enabled for non-Fusion style 非融合样式不允许使用 Press to tune the alert 按下调节提醒 Clublog Immediately Upload Error Clublog 立即上传出错 <b>Error Detail:</b> <b>错误详情:</b> op: 操作员: A New Version 新版本 A new version %1 is available. 新版本 %1 可用。 Remind Me Later 稍后提醒我 Download 下载 Failed to encrypt credentials. Database files (*.dbe);;All files (*) Failed to create temporary file. Failed to dump the database. Compressing database... Database successfully dumped to %1 Failed to compress the database. Failed to prepare database for import. Classic 经典 Do you want to remove the Contest filter %1? 你要删除比赛过滤器 %1 吗? Contest: 比赛: <h1>QLog %1</h1><p>&copy; 2019 Thomas Gatzweiler DL2IC<br/>&copy; 2021-2026 Ladislav Foldyna OK1MLG<br/>&copy; 2025-2026 Michael Morgan AA5SH<br/>&copy; 2025-2026 Kyle Boyle VE9KZ</p><p>Based on Qt %2<br/>%3<br/>%4<br/>%5</p><p>Icon by <a href='http://www.iconshock.com'>Icon Shock</a><br />Satellite images by <a href='http://www.nasa.gov'>NASA</a><br />ZoneDetect by <a href='https://github.com/BertoldVdb/ZoneDetect'>Bertold Van den Bergh</a><br />TimeZone Database by <a href='https://github.com/evansiroky/timezone-boundary-builder'>Evan Siroky</a> About 关于 N/A N/A MapWebChannelHandler Grid 网格 Gray-Line 灰线 Beam 波束 Aurora 极光 MUF MUF IBP IBP Chat 聊天 WSJTX - CQ WSJTX - CQ Path 路径 NewContactWidget Frequency 频率 Callsign 呼号 RX: 接收: TX: 发射: MHz MHz 80m 80m RSTs RST发 RSTr RST收 59 59 Mode 模式 Lookup the call on the web. The query URL can be changed in Settings -> Callbook 在网络上查找呼号。查询 URL 可以在 <b>设置 -> 电台黄页</b> 中更改 Web 网站 Time On 开始时间 Date 日期 D&X Stats D&X统计 <b>DXCC Statistics</b> <b>DXCC 统计</b> <b>Station Statistics</b> <b>电台统计</b> Save 保存 Duration 周期 Reset 重置 <b>DUPE !!!</b> <b>重复通联 !!!</b> Info 信息 &Details 详情(&D) QSL Send Status QSL 发送状态 Paper 卡片 <b>Yes</b> - an outgoing QSL card has been sent; the QSO has been uploaded to, and accepted by, the online service<br/><b>No</b> - do not send an outgoing QSL card; do not upload the QSO to the online service<br/><b>Requested</b> - the contacted station has requested a QSL card; the contacted station has requested the QSO be uploaded to the online service<br/><b>Queued</b> - an outgoing QSL card has been selected to be sent; a QSO has been selected to be uploaded to the online service<br/> <b>是</b> -已发出 QSL 卡; QSO 已上传至在线服务<br/><b>否</b> -不发送 QSL 卡;<br/><b>已请求</b> -被联系站点已请求一张 QSL 卡; 被联系站点已请求将 QSO 上传到在线服务<br/><b>排队</b> 已选择要发送的 QSL 卡; QSO 已上传至网上服务<br/> LoTW LoTW eQSL eQSL QSL Send via QSL 发送通过 QSL via QSL 通过 Propagation Mode 传播模式 M&y Station 我的电台(&Y) Station 电台 Rig 设备 Antenna 天线 Blank 空白 W W My &Notes 我的笔记(&N) Member: 成员: No Yes Requested 已请求 Queued 排队中 Ignored 已忽视 Bureau 卡片局 Direct 直邮 Electronic 电子卡片 QLog Error QLog错误 Callbook login failed 电台黄页登陆失败 LP 长路径 New Entity! 新实体! New Band! 新波段! New Mode! 新模式! New Band & Mode! 新波段与模式! New Slot! 新组合! Worked 已通联 Confirmed 已确认 GE GE GM GM GA GA m m Callbook search is active 电台黄页搜索可用 Contest ID must be filled in to activate 必须填写 竞赛标识 才能激活 It is not the name of the contest but it is an assigned<br>Contest ID (ex. CQ-WW-CW for CQ WW DX Contest (CW)) 这里要填的不是比赛名称,而是分配的竞赛标识<br>(例如CQ-WW-CW 代表CQ WW DX比赛(CW)) Description of the contacted station's equipment 已通联台站的设备描述 Callbook search is inactive 电台黄页搜索不可用 Expand/Collapse 展开/折叠 two or four adjacent Maidenhead grid locators, each four characters long, (ex. EN98,FM08,EM97,FM07) 两个或四个相邻的梅登黑德网格定位器,每个四个字符长,(例如 EN98, FM08, EM97, FM07) the contacted station's DARC DOK (District Location Code) (ex. A01) 联络电台的DARC DOK (地区位置代码) (例如 A01) World Wide Flora & Fauna 世界动植物 (可选参数) Special Activity Group 特别活动或兴趣团体名称 Special Activity Group Information 特别活动或兴趣团体信息 OmnirigRigDrv Rig 1 设备 1 Rig 2 设备 2 Initialization Error 初始化出错 Rig status changed 设备状态已更改 Rig is not connected 设备未连接 OmnirigV2RigDrv Rig 1 设备 1 Rig 2 设备 2 Rig 3 设备 3 Rig 4 设备 4 Initialization Error 初始化出错 Rig status changed 设备状态已更改 Rig is not connected 设备未连接 PSTRotDrv Rot 1 云台 1 Cannot bind a port 无法绑定端口 Cannot get IP Address for 无法获取 IP 地址给 No IPv4 Address for 无法获取 IPv4 地址给 Error Occurred Operation Timeout 操作超时 PaperQSLDialog Manage QSL Card 管理 QSL 卡片 Available QSLs 可用的 QSL Copy the input file from the source folder to the QLog Internal QSL Storage folder.<br/>The original file remains unchanged in the source folder 将输入文件从源文件夹复制到 “QLog 内部 QSL 存储” 文件夹中。<br/>原始文件在源文件夹中保持不变 Import QSL 导入 QSL Add File 添加文件 Remove 移除 Delete 删除 Delete QSL? 删除 QSL? Open 打开 Toggle Favorite PlatformSettingsDialog Platform-specific Settings The database was exported from a different platform. Please verify or update the following settings. You can leave fields empty and configure them later in Settings. Setting Value Continue Select File 选择文件 All Files (*) ProfileImageWidget Profile Image 资料图片 QCoreApplication QLog Help QLog 帮助 QMessageBox QLog Critical Qlog 临界值 Cannot save a password for %1 to the Credential Store 无法将 %1 的密码保存到凭据数据库 Cannot get a password for %1 from the Credential Store 无法从凭据数据库获取 %1 密码 QLog Warning QLog 告警 Club List Update failed. Cannot remove old records 俱乐部列表更新失败。无法删除旧记录 Club List Update failed. Cannot plan new downloads 俱乐部列表更新失败。无法规划新下载 Unexpected Club List download. Canceling next downloads 无法预料的俱乐部列表下载。取消下次下载 Unexpected Club List content for 未知的俱乐部列表内容 Network error. Cannot download Club List for 网络错误。无法下载俱乐部列表 QLog Error QLog 出错 QLog is already running 已经有 QLog 实例正在运行 Failed to process pending database import. The database was imported successfully, but the stored passwords could not be restored (decryption failed or the data is corrupted). All service passwords have been cleared and must be re-entered in Settings. Could not connect to database. 无法连接至数据库。 Could not export a QLog database to ADIF as a backup.<p>Try to export your log to ADIF manually 无法导出 QLog 数据库至 ADIF 备份。<p>请尝试手动导出您的日志至 ADIF Database migration failed. 数据库迁移失败。 QLog Info QLog 信息 Activity name is already exists. 活动名称已存在。 Rule name is already exists. 规则名已存在。 Callsign Regular Expression is incorrect. 呼号正则表达式不正确。 Comment Regular Expression is incorrect. 备注正则表达式不正确。 Cannot Update Alert Rules 无法更新提醒规则 DXC Server Name Error DXC 服务器名称错误 DXC Server address must be in format<p><b>[username@]hostname:port</b> (ex. hamqth.com:7300)</p> DXC 服务器地址格式必须为<p><b>[用户名@]主机名:端口</b> (例. hamqth.com:7300)</p> DX Cluster Password DX 集群密码 Invalid Password 无效的密码 DXC Server Connection Error DXC 服务器连接出错 Filename is empty 文件名为空 Cannot write to the file 无法写入文件 QLog Information QLog 信息 Exported. 已导出。 Exported %n contact(s). 已导出 %n 通联记录。 Chat Error: 聊天出错: Filter name is already exists. 过滤器名称已存在。 <b>Rig Error:</b> <b>设备出错:</b> <b>Rotator Error:</b> <b>旋转云台出错:</b> <b>CW Keyer Error:</b> <b>CW 键控器出错:</b> The fields <b>%0</b> will not be saved because the <b>%1</b> is not filled. 不会保存字段<b>%0</b>,因为<b>%1</b>未填充。 Your callsign is empty. Please, set your Station Profile 你的呼号为空。请先设置台站配置文件 Please, define at least one Station Locations Profile 请至少定义一个站点配置文件 WSJTX Multicast is enabled but the Address is not a multicast address. WSJTX 多播已启用,但地址不是多播地址。 Loop detected. Raw UDP forward uses the same port as the WSJT-X receiving port. Rig port must be a valid COM port.<br>For Windows use COMxx, for unix-like OS use a path to device 设备端口必须是一个有效的 COM 端口。<br>Windows 使用 COMxxx,对于unix-like OS 使用设备路径 Rig PTT port must be a valid COM port.<br>For Windows use COMxx, for unix-like OS use a path to device 电台PTT端口必须为可用的串口。<br>Windows是类似COMxx,类UNIX操作系统则为设备路径名 <b>TX Range</b>: Max Frequency must not be 0. <b>TX 范围</b>: 最大频率不能为 0。 <b>TX Range</b>: Max Frequency must not be under Min Frequency. <b>TX 范围</b>: 最大频率不能低于最小频率。 Rotator port must be a valid COM port.<br>For Windows use COMxx, for unix-like OS use a path to device 旋转云台端口必须是一个有效的 COM 端口。<br>对于Windows 使用 COMxxx,对于unix-like OS 使用设备路径 CW Keyer port must be a valid COM port.<br>For Windows use COMxx, for unix-like OS use a path to device CW 键控器端口必须是一个有效的 COM 端口。<br>对于Windows 使用 COMxxx,对于unix-like OS 使用设备路径 Cannot change the CW Keyer Model to <b>Morse over CAT</b><br>No Morse over CAT support for Rig(s) <b>%1</b> 无法将 CW 键控器模型改为 <b>Morse over CAT</b><br>设备 <b>%1</b>不支持Morse over CAT Cannot delete the CW Keyer Profile<br>The CW Key Profile is used by Rig(s): <b>%1</b> 无法删除 CW 键控器配置文件<br>本 CW 键控器配置文件正在被设备<b>%1</b>使用 Callsign has an invalid format 呼号格式无效 Operator Callsign has an invalid format 操作员呼号格式无效 Gridsquare has an invalid format 网格坐标格式无效 VUCC Grids have an invalid format (must be 2 or 4 Gridsquares separated by ',') VUCC 网格格式无效 (必须是 2 或 4 个被 ',' 分隔的网格坐标) Country must not be empty 国家/地区 不能为空 CQZ must not be empty CQ 分区不能为空 ITU must not be empty ITU 不能为空 Cannot update QSO Filter Conditions 无法更新QSO过滤器条件 QObject km 公里 miles 英里 Connection Refused 连接被拒绝 Host closed the connection 主机关闭连接 Host not found 找不到主机 Timeout 超时 Network Error 网络错误 Internal Error 内部错误 Importing Database Opening Database 打开数据库 Backuping Database 备份数据库 Migrating Database 迁移数据库 Starting Application 启动应用程序 My Rig 我的设备 Logging Station Callsign 记录电台呼号 My Gridsquare 我的网格坐标 Operator Name 操作员姓名 Operator Callsign 操作员呼号 My City 我的城市 My Country 我的国家/地区 My County 我的县/郡 My IOTA 我的 IOTA My SOTA 我的 SOTA My Special Interest Activity 我的特殊兴趣活动 My Spec. Interes Activity Info 我的特殊兴趣活动信息 My VUCC Grids 我的 VUCC 网格 My WWFF 我的 WWFF My POTA Ref 我的 POTA 编号 My DARC DOK 我的DARC DOK My ITU 我的 ITU My CQZ 我的 CQ 分区 My DXCC 我的 DXCC Cannot connect to DXC Server <p>Reason <b>: 无法连接至 DXC 服务器 <p>原因 <b>: <b>Imported</b>: %n contact(s) <b>已导入</b>: %n 通联 <b>Warning(s)</b>: %n <b>告警</b>: %n <b>Error(s)</b>: %n <b>错误</b>: %n Not a valid QLog database Database version too new (requires newer QLog version) Database is not QLog Export file TQSL Path TQSL路径 (Flatpak internal path) Rig 设备 Rig PTT Rig rigctld Rotator 云台 CW Keyer TOTAL Worked 全部已通联 TOTAL Confirmed 全部已确认 Confirmed 已确认 Worked 已通联 QRZCallbook QRZ.com QRZ.com QRZUploader General Error 常见错误 QSLGalleryDialog QSL Card Gallery Search by callsign... Sort by: Export Filtered Date (Newest) Date (Oldest) Callsign (A-Z) Callsign (Z-A) %n QSL card(s) All QSL Cards Favorites By Country By Date By Band By Mode By Continent Remove from Favorites Add to Favorites Open 打开 Save... Save QSL Card Export QSL Cards Exported %1 of %2 cards QSLImportStatDialog QSL Import Summary QSL 导入摘要 Summary 摘要 Downloaded: 已下载: Updated: 已更新: Unmatched: 未匹配: Errors: 出错: Details 详情 New QSLs: 新 QSLs: Updated QSOs: 更新 QSOs: Unmatched QSLs: 未匹配的 QSLs: 不匹配的 QSL: QSLPrintLabelDialog Print QSL Labels Filter Date Range My Callsign 我的呼号 Station Profile 台站配置 QSL Sent QSL 已发送 User Filter 用户过滤器 Label Template Page Size: Columns: Rows: Label Width: Label Height: Left Margin: Top Margin: H Spacing: V Spacing: Label Appearance Print Label Borders QSOs per Label: Footer Left Text: Footer Right Text: Skip Label: Sans Font: Mono Font: Callsign Size: "To Radio" Size: "To Radio" Text: Header Size: Data Size: Date Header Text: Date Format: Time Header Text: Band Header Text: Mode Header Text: QSL Header Text: Extra Column: Extra Column Text (DB column name) No matching QSOs found Page 0 of 0 Labels: 0 (0 pages) Print Export as PDF Custom 自定义 Empty QSOs matching this station profile Labels: %1 (%2 pages) Page %1 of %2 Export PDF PDF Files (*.pdf) Mark as Sent Mark printed/exported QSOs as sent? QSODetailDialog dd/MM/yyyy HH:mm:ss dd/MM/yyyy HH:mm:ss RSTs RST发 RSTr RST收 Mode 模式 Time On 起始时间 Time Off 结束时间 Callsign 呼号 TX: 发射: Blank 空白 MHz MHz RX: 接收: Frequency 频率 Band 波段 QTH QTH Name 姓名 Gridsquare 网格坐标 Comment 备注 My Notes 我的笔记 about:blank about:blank &Details 详情(&D) Country 国家/地区 Cont 大洲 ITU ITU CQ CQ State County 县/郡 Age 年龄 AF AF AN AN AS AS EU EU NA NA OC OC SA SA VUCC VUCC two or four adjacent Gridsquares, each four characters long, (ex. EN98,FM08,EM97,FM07) 两个或四个相邻的网格坐标,每个网格坐标长四个字符,(如 EN98, FM08, EM97, FM07) FISTS FISTS SKCC SKCC Ten-Ten Ten-Ten UKSMG UKSMG DOK DOK the contacted station's DARC DOK (District Location Code) (ex. A01) 联络电台的DARC DOK (地区位置代码) (例如 A01) IOTA IOTA POTA POTA SOTA SOTA WWFF WWFF SIG SIG SIG Info SIG信息 FISTS CC FISTS CC E-Mail 电子邮件 URL URL Propagation Mode 传播模式 Satellite Name 卫星名 Satellite Mode 卫星模式 M&y Station 我的电台(&Y) Operator Name 操作员姓名 Operator Callsign 操作员呼号 Rig 设备 Antenna 天线 Power 功率 W W QSLr Message QSLr 信息 QSLs Message QSLs 信息 Contest ID 竞赛标识 Sig Info SIG 信息 D&X Stats D&X 统计 <b>DXCC Statistics</b> <b>DXCC 统计</b> <b>Station Statistics</b> <b>电台统计</b> &QSL &QSL Sent 发出 - - Manage QSL Card 管理 QSL 卡片 QSL Sent via QSL 发送经由 <b>Yes</b> - an outgoing QSL card has been sent; the QSO has been uploaded to, and accepted by, the online service<br/><b>No</b> - do not send an outgoing QSL card; do not upload the QSO to the online service<br/><b>Requested</b> - the contacted station has requested a QSL card; the contacted station has requested the QSO be uploaded to the online service<br/><b>Queued</b> - an outgoing QSL card has been selected to be sent; a QSO has been selected to be uploaded to the online service<br/> <b>是</b> -已发出 QSL卡; QSO 已上传至在线服务<br/><b>否</b> -不发送 QSL卡;<br/><b>已请求</b> -被联系站点已请求 QSL卡;被联系站点已请求将 QSO 上传到在线服务<br/><b>排队</b>—已选择要发送的 QSL 卡;已选定一个 QSO 上载至在线服务<br/> Paper 纸质卡片 Show QSL Card 显示 QSL 卡片 <b>Yes</b> - an incoming QSL card has been received; the QSO has been confirmed by the online service<br/><b>No</b> - an incoming QSL card has not been received; the QSO has not been confirmed by the online service<br/><b>Requested</b> - the logging station has requested a QSL card; the logging station has requested the QSO be uploaded to the online service<br/> <b>是</b> -已收到 QSL 卡; QSO 已被在线服务确认<br/><b>否</b> -未收到 QSL 卡;在线服务请求的QSO未得到确认<br><b>已请求</b> -日志记录站已请求 QSL卡; 日志记录站要求将QSO上传到在线服务<br/> Received 收到 QSL via QSL 通过 LoTW LoTW eQSL eQSL &Contest 比赛(&C) RcvNr 接收序号 RcvExch 接收交换信息 SendNr 发送序号 SendExch 发送交换信息 Member: 成员: &Reset 重置(&R) &Lookup 查找(&L) No Yes Requested 已请求 Queued 排队中 Ignored 已忽视 Bureau 卡片局 Direct 直邮 Electronic 电子卡片 Submit changes 提交更改 Really submit all changes? 确认提交所有更改吗? QLog Error QLog 错误 Cannot save all changes - internal error 无法保存所有更改 - 内部错误 Cannot save all changes - try to reset all changes 无法保存所有更改 - 尝试重置所有更改 QSO Detail QSO 详情 Edit QSO 编辑 QSO Downloading eQSL Image 下载 eQSL 图片 Cancel 取消 eQSL Download Image failed: 下载 eQSL 图片失败: DX Callsign must not be empty DX 呼号不能为空 DX callsign has an incorrect format DX 呼号格式不正确 TX Frequency or Band must be filled 必须填入发射频率或波段 TX Band should be 发射波段应该为 RX Band should be 接收波段应该为 DX Grid has an incorrect format DX 网格格式错误 Based on callsign, DXCC Country is different from the entered value - expecting 根据呼号,DXCC 国家与输入值不同 - 期望 Based on callsign, DXCC Continent is different from the entered value - expecting 根据呼号,DXCC 大洲与输入值期望不同 - 期望 Based on callsign, DXCC ITU is different from the entered value - expecting 根据呼号,DXCC ITU 与输入值不同 - 期望 Based on callsign, DXCC CQZ is different from the entered value - expecting 根据呼号,DXCC CQZ 与输入值不同 - 期望 VUCC has an incorrect format VUCC 格式错误 Based on Frequencies, Sat Mode should be 基于频率,卫星模式应该为 blank 空白 Sat name must not be empty 卫星名不能为空 Own Callsign must not be empty 自己的呼号不能为空 Own callsign has an incorrect format 自己的呼号格式错误 Own VUCC Grids have an incorrect format 自己的 VUCC 网格格式错误 Based on own callsign, own DXCC ITU is different from the entered value - expecting 基于自己的呼号,自己的 DXCC ITU 与输入的值不同 - 期望 Based on own callsign, own DXCC CQZ is different from the entered value - expecting 基于自己的呼号,自己的 DXCC CQ分区 与输入的值不同 - 期望 Based on own callsign, own DXCC Country is different from the entered value - expecting 基于自己的呼号,自己的 DXCC 国家/地区与输入的值不同 - 期望 Based on SOTA Summit, QTH does not match SOTA Summit Name - expecting 基于 SOTA 山峰,QTH 不匹配 SOTA 山峰名称 - 期待 Based on SOTA Summit, Grid does not match SOTA Grid - expecting 基于 SOTA 山峰,网格坐标不匹配 SOTA 山峰名称 - 期待 Based on POTA record, QTH does not match POTA Name - expecting 基于 POTA 记录,QTH 不匹配 POTA 名称 - 期待 Based on POTA record, Grid does not match POTA Grid - expecting 基于 POTA 记录,网格坐标不匹配 POTA 名称 - 期待 Based on SOTA Summit, my QTH does not match SOTA Summit Name - expecting 基于 SOTA 山峰,我的 QTH 不匹配 SOTA 山峰名称 - 期待 Based on SOTA Summit, my Grid does not match SOTA Grid - expecting 基于 SOTA 山峰,我的网格坐标不匹配 SOTA 山峰名称 - 期待 Based on POTA record, my QTH does not match POTA Name - expecting 基于 POTA 记录,我的 QTH 不匹配 POTA 名称 - 期待 Based on POTA record, my Grid does not match POTA Grid - expecting 基于 POTA 记录,我的网格坐标不匹配 POTA 名称 - 期待 LoTW Sent Status to <b>No</b> does not make any sense if QSL Sent Date is set. Set Date to 1.1.1900 to leave the date field blank 如果已经设置了 QSL 发送日期, LoTW 发送状态设为<b>否</b> 没有任何意义。设置日期为 1.1.1900 以让日期字段为空 Date should be present for LoTW Sent Status <b>Yes</b> 需要为 LoTW 发送状态 提供日期。<b>是</b> eQSL Sent Status to <b>No</b> does not make any sense if QSL Sent Date is set. Set Date to 1.1.1900 to leave the date field blank 如果已经设置了 QSL 发送日期, eQSL 发送状态设为<b>否</b> 没有任何意义。设置日期为 1.1.1900 以让日期字段为空 Date should be present for eQSL Sent Status <b>Yes</b> 需要为 eQSL 发送状态提供日期 <b>是</b> Paper Sent Status to <b>No</b> does not make any sense if QSL Sent Date is set. Set Date to 1.1.1900 to leave the date field blank 如果已经设置了 QSL 发送日期, 纸质卡片发送状态设为<b>否</b> 没有任何意义。设置日期为 1.1.1900 以让日期字段为空 Date should be present for Paper Sent Status <b>Yes</b> 需要为纸质卡片发送状态提供日期 <b>是</b> Callbook error: 电台黄页错误: <b>Warning: </b> <b>告警</b>: %n Validation 验证 Yellow marked fields are invalid.<p>Nevertheless, save the changes?</p> 黄色标记的字段无效。<p>依然保存更改?</p> &Save 保存(&S) &Edit 编辑(&E) QSOFilterDetail QSO Filter Detail QSO过滤器详情 Filter Name: 过滤器名称: Find QSO which match 查找QSO符合 All the following conditions 所有以下条件 Any of the following conditions 任意以下条件 Add Condition 添加条件 Equal 等于 Not Equal 不等于 Contains 包含 Not Contains 不包含 Greater Than 大于 Less Than 小于 Starts with 起始自 RegExp Remove 删除 Must not be empty 不能为空 QSOFilterDialog QSO Filters QSO过滤器 Filters 过滤器 Add 添加 Edit 编辑 Remove 删除 Rig No Rig Profile selected 未选择设备配置文件 Rigctld Error Initialization Error 初始化出错 Internal Error 内部错误 Cannot open Rig 无法打开设备 RigWidget Form Form RX 接收 Disconnected MHz MHz Disable Split RIT: 0.00000 MHz RIT: 0.00000 MHz XIT: 0.00000 MHz XIT: 0.00000 MHz PWR: %1W 功率: %1W RigctldAdvancedDialog Rigctld Advanced Settings Rigctld Path: Leave empty for auto-detection Browse 浏览 Auto-detect Rigctld path Auto-Detect Rigctld Version: Additional Arguments: e.g. -v -v for verbose logging Cannot be changed Auto Detect rigctld was not found on this system. Please install Hamlib or specify the path manually. Executable (*.exe);;All files (*.*) All files (*) Select rigctld executable Not found RigctldManager rigctld executable not found in /app/bin/. This should not happen in Flatpak build. rigctld executable not found. Please install Hamlib or specify the path in Advanced settings. Hamlib major version mismatch: QLog was compiled with Hamlib %1 but rigctld reports version %2.%3.%4. Rig model IDs are incompatible between major versions. Port %1 is already in use. Another rigctld or application may be running on this port. rigctld started but not responding on port %1. Failed to start rigctld: %1 %2 rigctld crashed. rigctld timed out. Write error with rigctld. Read error with rigctld. Unknown rigctld error. Rotator No Rotator Profile selected 未选择旋转云台配置文件 Initialization Error 初始化出错 Internal Error 内部错误 Cannot open Rotator 无法打开云台 RotatorWidget Form Form Az: 方位: ° ° Goto 转到 Previous Button Profile Next Button Profile QSO LP QSO 长路径 QSO Long Path QSO 长路径 QSO SP QSO 短路经 QSO Short Path QSO 短路径 SettingsDialog Settings 设置 Station 电台 Profiles 配置文件 World Wide Flora & Fauna (Optional parameter) 世界动植物 (可选参数) POTA POTA SIG SIG Station Gridsquare (Mandatory parameter) 电台网格坐标 (梅登黑格网格) Operator Name 操作员姓名 SOTA SOTA QTH QTH SIG Information (Optional parameter) 特别活动或兴趣团体信息 (可选参数) WWFF WWFF IOTA (Optional parameter) IOTA (可选参数) Callsign 呼号 SOTA (Optional parameter) SOTA (可选参数) Profile name that is used as the alias for the Callsign, Gridsquare, Operator name, and QTH (required parameter) 配置文件名称,用作呼号、网格坐标、操作员名称和 QTH 的别名 (必需参数) List of all available Station Profiles 所有可用电台配置文件列表 Gridsquare 网格坐标 IOTA IOTA Profile Name 配置名 VUCC VUCC SIG (Optional parameter). 特别活动或兴趣团体名 (可选参数)。 VUCC Grids (Optional parameter). Ex. EN98,FM08,EM97,FM07 VUCC 网格 (可选参数)。例. EN98,FM08,EM97,FM07 SIG Info SIG 信息 Add 添加 Delete 删除 QTH Name (Optional parameter) QTH 名 (可选参数) Operator name (Optional parameter) 操作员姓名 (可选参数) Callsign (Mandatory parameter) 呼号 (必选参数) ITU ITU CQZ CQ分区 Country 国家/地区 DOK DOK Equipment 设备 Antennas 天线 Profiles 配置文件 Description 描述 Azimuth Beamwidth 方位波束宽度 Azimuth Offset 方位偏移 Valid range value is 0° - 100° (0° Unspecified) 有效值为 0° - 100° (0° 未指定) Unspecified 未指定 ° ° List of all available Antennas 所有可用天线列表 CW Keyers CW电键 Keyer Profiles 电键配置文件 List of all available CW Keyers 所有可用CW电键列表 Model 型号 Keyer Mode 电键模式 Swap Paddles 交换嘀/嗒 Paddle Only Sidetone Sidetone Freq: Default Speed 默认速度 N/A N/A WPM WPM Port 端口 Use COMxx for Window or path to COM port under Unix-like OS Windows 使用 COMxx,类unix操作系统使用路径 Baudrate 波特率 115200 115200 57600 57600 38400 38400 19200 19200 9600 9600 4800 4800 2400 2400 1200 1200 Host Name 主机名 HamLib does not support to change a destination port. HamLib 不支持修改目的端口。 CW Shortcut Profiles CW 快捷配置文件 List of all available CW Shortcuts Profiles 所有可用 CW 快捷配置文件列表 F1 F1 Short Desciption of the Button (up to 7 chars) 按钮的简短说明 (不超过7个字符) F2 F2 F3 F3 F4 F4 F5 F5 F6 F6 F7 F7 Rigs 电台 CIV Addr Auto 自动 RTS DTR List of all available Rigs 所有可用电台列表 Leave empty for auto-detection Auto-detect TQSL path Auto-Detect TQSL Version Color CQ Spots Enable/Disable sending color-coded status indicators back to WSJT-X for each callsign calling CQ Unit System Metric Imperial Interface 界面 Minimum and maximum TX frequencies. Specific ranges are derived from allowed Band in the Setting. 最小和最大 TX 频率。特定范围由设置中允许的波段导出。 TX Range 发射范围 - - Enter manually RIT or Transverter Offset 手动输入 RIT 或变频器偏移量 MHz MHz <DXCALL> = DX Callsign <NAME> = DX Operator Name <RST> = Report 599 <RSTN> = Report 5NN <GREETING> = Greenting GM/GA/GE <QTH> = QTH <MYCALL> = My Callsign <MYNAME> = My Name <MYQTH> = My QTH <MYLOCATOR> = My Gridsquare <MYGRID> = My Gridsquare <MYSIG> = My SIG <MYSIGINFO> = My SIG Information <MYIOTA> = My IOTA <MYSOTA> = MY SOTA <MYWWFT> = My WWFT <MYVUCC> = MY VUCC <MYPWR> = My Power in W <EXCHSTR> = Contest Exchange Message <EXCHNR> = Contest Exchange Serial Number <EXCHNRN> = Contest Exchange Serial Number (9→N, 0→T) <+> = Speed +5 WPM (<++> = +10 WPM, etc.) <-> = Speed -5 WPM (<--> = -10 WPM, etc.) RX: 接收: Enter manually XIT or Transverter offset 手动输入 XIT 或转换器偏移量 TX: 发射: Default PWR 默认功率 Enter default PWR (ex. when Rig is disconnected) 输入默认功率 (例如,当设备断开连接时) Blank 空白 W Assigned CW Keyer 指定CW电键 Rig Features 电台特性 Mode 模式 VFO VFO Freq 频率 QSY Wiping QSY擦除 Power 功率 PTT State PTT状态 TX Offset (XIT) 发射偏移(XIT) RX Offset (RIT) 接收偏移(RIT) CW Keyer Speed CW电键速度 CW Speed Sync CW速度同步 Start rigctld daemon to share rig with other applications (e.g. WSJT-X) Share Rig via port Advanced... Poll Interval 轮询间隔 ms 毫秒 Rig Port Data Bits 数据位 Flow Control 流控制 Parity 校验 8 8 7 7 6 6 5 5 Stop Bits 停止位 1 1 2 2 PTT Type PTT类型 PTT Port PTT端口 DX Spots to Rig DX 报点到电台 QSOs are uploaded immediately QSO 将立即上传 Default API Key 默认API密钥 Callsign-specific API Keys 特定呼号的API密匙 Wavelog Wavelog API Key API密钥 Endpoint 站点地址 Others 其他 Status Confirmed By 确认自 Paper 纸质卡片 The '>' character is interpreted as a marker for the initial cursor position in the Report column. <br/>Ex.: '5>9' means the cursor will be positioned on the second character “>” 字符被解读为 “报告” 列中初始光标位置的标记。 <br/>示例:“5>9” 表示光标将定位在第二个字符上 Rig Status 设备状态 <p> List of IP addresses to which QLog sends UDP notification packets when Rig State changes.</p>The IP addresses are separated by a space and have the form IP:PORT <p>当设备状态发生变化时,QLog向其发送 UDP 通知数据包的 IP 地址列表。</p>IP地址用空格分隔,形式为IP:PORT GUI 用户界面 Time Format 时间格式 24-hour 24小时 AM/PM Date Format 日期格式 System 系统 Custom 自定义 <a href="https://doc.qt.io/qt-6/qdate.html#fromString-1">Time Format Documentation</a> <a href="https://doc.qt.io/qt-6/qdate.html#fromString-1">时间格式文档</a> Port Type 端口类型 Serial 串口 Network 网络 Rotators 旋转云台 Operator Callsign 操作员呼号 Station Callsign 电台呼号 Callsign of operator (Optional parameter, if different from station callsign) 操作员的呼号(可选参数,如果操作员呼号与电台呼号不相同) County 县/郡 Station County Location (Optional parameter) 电台地址(可选参数) Offsets 偏移 Split User Buttons Profiles 用户按钮配置文件 Button 1 按钮1 Button 2 按钮2 Button 3 按钮3 Button 4 按钮4 Callbook 电台黄页 Query Order 查询顺序 Primary 首选 Secondary 次选 HamQTH HamQTH Username 用户名 Password 密码 QRZ.com QRZ.com <b>Notice:</b> At least a QRZ XML Subscription is recommended to access detailed information. Without a subscription, you will obtain limited data from QRZ, such as missing grid and other fields. <b>注意:</b>建议使用 QRZ XML 订阅来获取详细信息。如果没有订阅,您只能从 QRZ 获取有限的数据,例如缺少网格和其他字段。 Web Lookup Button Web 查找按钮 URL URL Specify the URL to use for quick search. The <DXCALL> macro will be replaced by the current callsign 指定要用于快速搜索的URL。<DXCALL> 宏将被当前的呼号替换 Test URL with your Callsign 使用您的呼号来测试 URL Test 测试 Clubs 俱乐部 Active Lists 活跃列表 Sync && QSL 同步 && QSL ClubLog Clublog E-Mail 电子邮件 Immediately Upload 立即上传 eQSL eQSL HRDLog HRDLog It is not a password. It is the upload code received via email after the registration to HRDLOG..net 它不是密码。这是注册 HRDLOG.net 后通过电子邮件收到的上传代码 Upload Code 上传代码 If it is enabled and Rig is connected then QLog periodically sends On-Air messages to HRDLog 如果它被启用并且设备已连接,那么 QLog 会定期向 HRDLog 发送 On-Air 消息 Send On-Air 发送 On-Air LoTW LoTW TQSL Path TQSL路径 Browse 浏览 Using an internal TQSL instance 使用内部 TQSL 实例 Chat 聊天 <b>Security Notice:</b> QLog stores all passwords in the Secure Storage. Unfortunately, ON4KST uses a protocol where this password is sent over an unsecured channel as plaintext.</p><p>Please exercise caution when choosing your password for this service, as your password is sent over an unsecured channel in plaintext form.</p> <b>安全通知:</b> QLog将所有密码存储在安全存储中。不幸的是,ON4KST使用一种协议,该协议将密码以明文形式通过不安全的通道发送。</p><p>请谨慎选择此服务的密码,因为您的密码以明文形式通过不安全的通道发送。</p> Bands 波段 Modes 模式 Danger Zone <b>⚠ This is a danger zone. Proceed with caution, as actions performed here cannot be undone and may have a significant impact on your log.</b> Delete All QSOs Delete All Passwords from the Secure Store DXCC DXCC Wsjtx Wsjtx Raw UDP Forward 原始UDP转发 <p>List of IP addresses to which QLog forwards raw UDP WSJT-X packets.</p>The IP addresses are separated by a space and have the form IP:PORT <p> QLog转发UDP WSJT-X原始报文的IP地址列表。</p> IP地址之间用空格分隔,格式为IP:PORT ex. 192.168.1.1:1234 192.168.2.1:1234 例. 192.168.1.1:1234 192.168.2.1:1234 Port 端口 Port where QLog listens an incoming traffic from WSJT-X QLog 监听来自 WSJT-X 的传入数据流的端口 Join Multicast 加入多播 Enable/Disable Multicast option for WSJTX 启用/禁用 WSJTX 的多播选项 Multicast Address 多播地址 Specify Multicast Address. <br>On some Linux systems it may be necessary to enable multicast on the loop-back network interface. 指定组播地址。<br>在某些Linux系统上,可能需要在环路网络接口上启用多播。 TTL TTL Time-To-Live determines the range<br> over which a multicast packet is propagated in your intranet. 生存时间 (Time-To-Live) 决定了组播数据包在内部网中传播的范围。 Notifications 通知 LogID 日志ID <p>Assigned LogID to the current log.</p>The LogID is sent in the Network Nofitication messages as a unique instance identified.<p> The ID is generated automatically and cannot be changed</> <p>为当前日志分配 LogID。</p> LogID作为唯一实例标识在网络通知消息中发送。<p>该ID为自动生成,不可手动修改</> DX Spots DX 报点 <p> List of IP addresses to which QLog sends UDP notification packets with DX Cluster Spots.</p>The IP addresses are separated by a space and have the form IP:PORT <p> QLog 发送 DX 集群报点 UDP 通知报文的 IP 地址列表。</p> IP 地址之间用空格分隔,格式为 IP:PORT Spot Alerts 实时报点提醒 <p> List of IP addresses to which QLog sends UDP notification packets about user Spot Alerts.</p>The IP addresses are separated by a space and have the form IP:PORT <p> QLog 向用户发送 实时报点提醒 UDP 报文的 IP 地址列表。</p> IP 地址之间用空格分隔,格式为 IP:PORT QSO Changes QSO 变更 <p> List of IP addresses to which QLog sends UDP notification packets about a new/updated/deleted QSO in the log.</p>The IP addresses are separated by a space and have the form IP:PORT <p>日志中 新增/更新/删 除QSO时,QLog 向其发送 UDP 通知报文的 IP 地址列表。</p> IP地址之间用空格分隔,格式为 IP:PORT Wsjtx CQ Spots WSJTX CQ 报点 <p> List of IP addresses to which QLog sends UDP notification packets with WSJTX CQ Spots.</p>The IP addresses are separated by a space and have the form IP:PORT <p> QLog 通过 WSJTX CQ 报点 发送 UDP 通知报文的 IP 地址列表。</p> IP地址之间用空格分隔,格式为 IP:PORT Special - Omnirig 特别的 - Ominirig Cannot be changed Name 姓名 Report 报告 State 状态 Start (MHz) 起始(MHz) End (MHz) 结束(MHz) SAT Mode 卫星模式 Disabled 禁用 None Hardware 硬件 Software 软件 No Even 偶校验 Odd 奇校验 Mark 1校验 Space 0校验 Dummy 虚拟 Morse Over CAT Morse Over CAT WinKey CWDaemon CWDaemon FLDigi FLDigi Single Paddle 单桨电键 IAMBIC A IAMBIC A IAMBIC B IAMBIC B Ultimate Ultimate High Low Press <b>Modify</b> to confirm the profile changes or <b>Cancel</b>. 按<b>修改</b>确认更改配置文件或按<b>取消</b>。 Modify 修改 Must not be empty 不能为空 Select File 选择文件 Auto Detect TQSL was not found on this system. Please install TQSL or specify the path manually. Not found Rig sharing is only available for Hamlib driver Rig sharing is not available for network connection Delete Passwords All passwords have been deleted Deleting all QSOs... Error 错误 Failed to delete all QSOs. members 成员 Required internet connection during application start 在应用程序启动时需要互联网连接 ShortcutEditorModel Description 描述 Shortcut 快捷键 Conflict with a built-in shortcut 与内置快捷键发生冲突 Conflict with a user-defined shortcut 与用户定义快捷键发生冲突 ShowUploadDialog QSOs to Upload 需要上传的QSOs Selected QSO 已选择的QSO Upload 上传 SmartSearchBox Search 搜索 StatisticsWidget Statistics 统计分析 My Antenna 我的天线 My Gridsquare 我的网格坐标 User Filter 用户过滤器 Confirmed by 确认自 QSOs per QSOs 每 Percents 百分比 Top 10 前 10 名 Histogram 直方图 Show on Map 在地图上显示 Graph Type 图表类型 to My Rig 我的设备 My Callsign 我的呼号 Band 波段 Date Range 日期范围 LoTW LoTW eQSL eQSL Paper 纸质卡片 Year Month Day in Week 星期几 Hour 小时 Mode 模式 Continent 大洲 Propagation Mode 传播模式 Confirmed / Not Confirmed 已确认/未确认 Countries 国家/地区 Big Gridsquares 大网格坐标 Distance 距离 QSOs QSO Confirmed/Worked Grids 确认/已通联 网格 ODX ODX Sun 星期日 Mon 星期一 Tue 星期二 Wed 星期三 Thu 星期四 Fri 星期五 Sat 星期六 Not specified 未指定 Confirmed 已确认 Not Confirmed 未确认 No User Filter 无用户筛选器 Over 50000 QSOs. Display them? 超过50000个QSO。显示全部吗? Rendering QSOs... All 全部 TCIRigDrv Rig 0 设备 0 Rig 1 设备 1 Rig 2 设备 2 Rig 3 设备 3 Error Occurred Rig status changed 设备状态已更改 Rig is not connected 设备未连接 TimestampFormatDelegate Blank 空白 ToAllTableModel Time 时间 Spotter 报点者 Message 消息 UploadQSODialog Upload QSOs 上传 QSOs eQSL eQSL LoTW LoTW QRZ.com QRZ.com Clublog Clublog HRDLog HRDLog Wavelog Filters 过滤器 Station Profile 台站配置 My Callsign 我的呼号 Gridsquare 网格坐标 Include QSOs Status 包括 QSOs 状态 Under normal circumstances this status means <b>"do not send"</b>.<br/>However, it may sometimes be wanted to ignore this setting when sending a QSL. 在正常情况下,此状态表示<b>“不发送”</b>。<br/>然而,有时可能希望在发送 QSL 时忽略此设置。 No Under normal circumstances this status means <b>"Ignore/Invalid"</b>.<br/>However, it may sometimes be wanted to ignore this setting when sending a QSL. 在正常情况下,此状态表示<b>“忽略/无效”</b>。<br/>然而,有时可能希望在发送 QSL 时忽略此设置。 Ignore 忽略 None QSLs Message QSLs 信息 Comment 备注 QSL Message Field QSL 确认信息字段 QTH Profile QTH 昵称 This option deletes all QSOs in the Clublog<br>and based on filter, it uploads all QSOs regardless of their status. 该选项删除 Clublog 中的所有 QSO,<br>并根据过滤器上传所有 QSO,而不管它们的状态如何。 Clear Clublog and reupload QSOs 清除 Clublog 并重新上传 QSO LoTW / TQSL TQSL Station Location Station Profile ID 台站配置ID Reupload All 点击后会触发 “重新上传全部已选定 / 历史上传过的内容” 的操作,常用于修复上传失败 重新上传全部 Show QSOs... 显示QSOs... &Upload &上传 Unspecified 未指定 Hide QSOs... 隐藏 QSOs... Uploading to %1 上传到 %1 Cancel 取消 QLog Warning - %1 QLog警告- %1 Cannot update QSO Status 无法更新 QSO 状态 Cannot upload the QSO(s): 无法上传该 QSO(s): QLog Information QLog 信息 No QSO found to upload. 未找到要上传的QSO。 QSO(s) were uploaded to the selected services QSO(s)已上传至选定的服务 Time 时间 Callsign 呼号 Mode 模式 Upload to 上传到 The values below will be used when an input record does not contain the ADIF values 当输入记录不包含ADIF值时,将使用下面的值 Any 任何 Location callsign (%1) and grid (%2) do not match selected filters Location callsign (%1) does not match selected callsign (%2) Location grid (%1) does not match selected grid (%2) Unknown 未知 Service is not configured properly.<p> Please, use <b>Settings</b> dialog to configure it.</p> 服务配置不正确。<p>请使用<b>设置</b>对话框进行配置。</p> UserListModel Callsign 呼号 Gridsquare 网格坐标 Distance 距离 Azimuth 方位 Comment 备注 WCYTableModel Time 时间 K K expK expK A A R R SFI SFI SA SA GMF GMF Au Au WWVTableModel Time 时间 SFI SFI A A K K Info 信息 WsjtxFilterDialog WSJTX Filters WSJTX 过滤器 General Filters 通用过滤器 Log Status Log 状态 New Band 新波段 New Mode 新模式 New Entity 新实体 New Slot 新组合 Worked 已通联 Confirmed 已确认 Continent 大洲 North America 北美洲 Europe 欧洲 South America 南美洲 Africa 非洲 Antarctica 南极洲 Asia 亚洲 Oceania 大洋洲 Distance more than 距离大于 SNR better than SNR 好于 dB dB Extended Filters 扩展过滤器 Member 成员 No Club List is enabled 未启用俱乐部列表 WsjtxTableModel Callsign 呼号 Gridsquare 网格坐标 Distance 距离 SNR SNR Last Activity 前次活跃 Last Message 前次消息 Member 成员 WsjtxWidget Form Form Filtered 过滤的内容 Column Visibility... 列可见性... Filter... 过滤器... Which columns should be displayed 应该显示哪些列 Filter Spots 报点过滤器 main Run with the specific namespace. 使用特定的命名空间运行。 namespace 命名空间 Translation file - absolute or relative path and QM file name. 翻译文件 - 绝对或相对路径及 QM 文件名。 path/QM-filename 路径/QM-文件名 Set language. <code> example: 'en' or 'en_US'. Ignore environment setting. 设置语言。<code> 示例: 'en' 或 'en_US'。忽略环境设置。 code 代码 Writes debug messages to the debug file 将调试信息写入debug文件 Process pending database import (internal use) Force update of all value lists (DXCC, SATs, etc.) ================================================ FILE: installer/config/config.xml ================================================ QLog 0.50.0 QLog OK1MLG QLog @ApplicationsDir@/QLog ================================================ FILE: installer/packages/de.dl2ic.qlog/meta/installscript.qs ================================================ var targetDirectoryPage = null; function Component() { installer.gainAdminRights(); component.loaded.connect(this, this.installerLoaded); } Component.prototype.createOperations = function() { component.createOperations(); if (installer.value("os") === "win") { component.addOperation("CreateShortcut", "@TargetDir@/qlog.exe", "@StartMenuDir@/QLog.lnk", "workingDirectory=@TargetDir@"); } } Component.prototype.installerLoaded = function() { installer.setDefaultPageVisible(QInstaller.TargetDirectory, false); installer.addWizardPage(component, "TargetWidget", QInstaller.TargetDirectory); targetDirectoryPage = gui.pageWidgetByObjectName("DynamicTargetWidget"); targetDirectoryPage.windowTitle = "Choose Installation Directory"; targetDirectoryPage.description.setText("Please select where the QLog will be installed:"); targetDirectoryPage.targetDirectory.textChanged.connect(this, this.targetDirectoryChanged); targetDirectoryPage.targetDirectory.setText(installer.value("TargetDir")); targetDirectoryPage.targetChooser.released.connect(this, this.targetChooserClicked); gui.pageById(QInstaller.ComponentSelection).entered.connect(this, this.componentSelectionPageEntered); } Component.prototype.targetChooserClicked = function() { var dir = QFileDialog.getExistingDirectory("", targetDirectoryPage.targetDirectory.text); targetDirectoryPage.targetDirectory.setText(dir); } Component.prototype.targetDirectoryChanged = function() { var dir = targetDirectoryPage.targetDirectory.text; if (installer.fileExists(dir) && installer.fileExists(dir + "/maintenancetool.exe")) { targetDirectoryPage.warning.setText("

Existing installation detected and will be overwritten.

"); } else if (installer.fileExists(dir)) { targetDirectoryPage.warning.setText("

Installing in existing directory. It will be wiped on uninstallation.

"); } else { targetDirectoryPage.warning.setText(""); } installer.setValue("TargetDir", dir); } Component.prototype.componentSelectionPageEntered = function() { var dir = installer.value("TargetDir"); if (installer.value("os") === "win") { if (installer.fileExists(dir) && installer.fileExists(dir + "/maintenancetool.exe")) { installer.execute(dir + "/maintenancetool.exe", ["purge", "-c"]); } } } ================================================ FILE: installer/packages/de.dl2ic.qlog/meta/package.xml ================================================ QLog The QLog main application. 0.50.0-1 2026-04-26 true true targetwidget.ui ================================================ FILE: installer/packages/de.dl2ic.qlog/meta/targetwidget.ui ================================================ TargetWidget 0 0 491 190 0 0 491 190 Form true 0 0 0 0 ... 0 true TextLabel Qt::Horizontal 40 20 Qt::Vertical 20 122 ================================================ FILE: logformat/AdiFormat.cpp ================================================ #include #include #include #include "data/Data.h" #include "AdiFormat.h" #include "core/debug.h" MODULE_IDENTIFICATION("qlog.logformat.adiformat"); #define ALWAYS_PRESENT true void AdiFormat::exportStart() { FCT_IDENTIFICATION; // ADIF Spec: // A Header begins with any character other than < and terminates // with a case-insensitive End-Of-Header tag // // Adding an extra space to be compliant with the ADIF specification. // It is the same as in the example in the ADIF resource file. stream << " "; writeField("ADIF_VER", ALWAYS_PRESENT, ADIF_VERSION_STRING); writeField("PROGRAMID", ALWAYS_PRESENT, PROGRAMID_STRING); writeField("PROGRAMVERSION", ALWAYS_PRESENT, VERSION); writeField("CREATED_TIMESTAMP", ALWAYS_PRESENT, QDateTime::currentDateTimeUtc().toString("yyyyMMdd hhmmss")); stream << "\n\n"; } void AdiFormat::exportContact(const QSqlRecord& record, QMap *applTags) { FCT_IDENTIFICATION; qCDebug(function_parameters)<\n\n"; } void AdiFormat::writeField(const QString &name, bool presenceCondition, const QString &value, const QString &type) { FCT_IDENTIFICATION; qCDebug(function_parameters)<< name << presenceCondition << value << type; if (!presenceCondition) return; /* ADIF does not support UTF-8 characterset therefore the Accents are remove */ QString accentless(Data::removeAccents(value)); qCDebug(runtime) << "Accentless: " << accentless; if ( value.isEmpty() || accentless.isEmpty() ) return; stream << "<" << name << ":" << accentless.size(); if (!type.isEmpty()) stream << ":" << type; stream << ">" << accentless << '\n'; } void AdiFormat::writeSQLRecord(const QSqlRecord &record, QMap *applTags) { FCT_IDENTIFICATION; for ( int i = 0; i <= record.count(); i++) { const QSqlField &tmpField = record.field(i); const ExportParams &ExportParams = DB2ADIFExportParams.value(tmpField.name()); if ( ExportParams.isValid ) writeField(ExportParams.ADIFName, tmpField.isValid(), formatOuput(ExportParams.formatter, tmpField.value()), ExportParams.outputType ); } const QVariant &startVariant = record.value("start_time"); if ( startVariant.isValid() ) { const QDateTime &time_start = startVariant.toDateTime().toTimeZone(QTimeZone::utc()); writeField("qso_date", startVariant.isValid(), formatOuput(OutputFieldFormatter::TODATE, time_start)); writeField("time_on", startVariant.isValid(), formatOuput(OutputFieldFormatter::TOTIME, time_start)); } const QVariant &endVariant = record.value("end_time"); if ( endVariant.isValid() ) { const QDateTime &time_end = record.value("end_time").toDateTime().toTimeZone(QTimeZone::utc()); writeField("qso_date_off", endVariant.isValid(), formatOuput(OutputFieldFormatter::TODATE, time_end)); writeField("time_off", endVariant.isValid(), formatOuput(OutputFieldFormatter::TOTIME, time_end)); } const QJsonObject &fields = QJsonDocument::fromJson(record.value("fields").toByteArray()).object(); const QStringList &keys = fields.keys(); for (const QString &key : keys) { writeField(key, ALWAYS_PRESENT, fields.value(key).toString()); } /* Add application-specific tags */ if ( applTags ) { const QStringList& appKeys = applTags->keys(); for (const QString &appkey : appKeys) { writeField(appkey, ALWAYS_PRESENT, applTags->value(appkey)); } } } void AdiFormat::readField(QString& field, QString& value) { FCT_IDENTIFICATION; //qCDebug(function_parameters)<> c; if (c == '<') { inHeader = false; state = KEY; field = ""; } else { inHeader = true; state = FIELD; } break; case FIELD: stream >> c; if (c == '<') { state = KEY; field = ""; } break; case KEY: stream >> c; if (c == ':') { state = SIZE; lengthString = ""; } else if (c == '>') { state = FIELD; if (inHeader && field.toLower() == "eoh") { inHeader = false; } else { value = ""; return; } } else { field.append(c); } break; case SIZE: stream >> c; if (c == ':') { if (!lengthString.isEmpty()) { length = lengthString.toInt(); } state = DATA_TYPE; typeString = ""; } else if (c == '>') { if (!lengthString.isEmpty()) { length = lengthString.toInt(); } if (length > 0) { state = VALUE; value = ""; } else { state = FIELD; if (!inHeader) { value = ""; return; } } } else { lengthString.append(c); } break; case DATA_TYPE: stream >> c; if (c == '>') { if (length > 0) { state = VALUE; value = ""; } else { state = FIELD; if (!inHeader) { value = ""; return; } } } else { typeString.append(c); } break; case VALUE: value = QString(stream.read(length)); state = FIELD; if (!inHeader) { return; } break; } } } void AdiFormat::mapContact2SQLRecord(QMap &contact, QSqlRecord &record) { FCT_IDENTIFICATION; preprocessINTLFields>(contact); /* Set default values if not present */ if ( defaults ) { const QStringList &keys = defaults->keys(); for ( const QString &key : keys ) { if ( contact.value(key).toString().isEmpty() ) { contact.insert(key, defaults->value(key)); } } // re-evaluate the fields preprocessINTLFields>(contact); } contactFields2SQLRecord(contact, record); /* If we have something unparsed then stored it as JSON to Field column */ if ( contact.count() > 0 ) { QJsonDocument doc = QJsonDocument::fromVariant(QVariant(contact)); record.setValue("fields", QString(doc.toJson())); } } void AdiFormat::contactFields2SQLRecord(QMap &contact, QSqlRecord &record) { FCT_IDENTIFICATION; record.setValue("callsign", contact.take("call").toString().toUpper()); record.setValue("rst_rcvd", contact.take("rst_rcvd")); record.setValue("rst_sent", contact.take("rst_sent")); record.setValue("gridsquare", contact.take("gridsquare").toString().toUpper()); record.setValue("cqz", contact.take("cqz")); record.setValue("ituz", contact.take("ituz")); record.setValue("freq", contact.take("freq")); record.setValue("band", contact.take("band").toString().toLower()); record.setValue("cont", contact.take("cont").toString().toUpper()); record.setValue("dxcc", contact.take("dxcc")); record.setValue("pfx", contact.take("pfx").toString().toUpper()); record.setValue("state", contact.take("state")); record.setValue("cnty", contact.take("cnty")); record.setValue("cnty_alt", contact.take("cnty_alt")); record.setValue("iota", contact.take("iota").toString().toUpper()); record.setValue("qsl_rcvd", parseQslRcvd(contact.take("qsl_rcvd").toString())); record.setValue("qsl_rdate", parseDate(contact.take("qslrdate").toString())); //TODO: DIFF MAPPING record.setValue("qsl_sent", parseQslSent(contact.take("qsl_sent").toString())); record.setValue("qsl_sdate", parseDate(contact.take("qslsdate").toString())); //TODO: DIFF MAPPING record.setValue("lotw_qsl_rcvd", parseQslRcvd(contact.take("lotw_qsl_rcvd").toString())); record.setValue("lotw_qslrdate", parseDate(contact.take("lotw_qslrdate").toString())); record.setValue("lotw_qsl_sent", parseQslSent(contact.take("lotw_qsl_sent").toString())); record.setValue("lotw_qslsdate", parseDate(contact.take("lotw_qslsdate").toString())); record.setValue("tx_pwr", contact.take("tx_pwr")); record.setValue("address", contact.take("address")); record.setValue("address_intl", contact.take("address_intl")); record.setValue("age", contact.take("age")); record.setValue("altitude", contact.take("altitude")); record.setValue("a_index", contact.take("a_index")); record.setValue("ant_az", contact.take("ant_az")); record.setValue("ant_el", contact.take("ant_el")); record.setValue("ant_path", contact.take("ant_path").toString().toUpper()); record.setValue("arrl_sect", contact.take("arrl_sect")); record.setValue("award_submitted",contact.take("award_submitted")); record.setValue("award_granted",contact.take("award_granted")); record.setValue("band_rx",contact.take("band_rx").toString().toLower()); record.setValue("check",contact.take("check")); record.setValue("class",contact.take("class")); record.setValue("clublog_qso_upload_date",parseDate(contact.take("clublog_qso_upload_date").toString())); record.setValue("clublog_qso_upload_status",parseUploadStatus(contact.take("clublog_qso_upload_status").toString())); record.setValue("contacted_op",contact.take("contacted_op")); record.setValue("comment",contact.take("comment")); record.setValue("comment_intl",contact.take("comment_intl")); record.setValue("contest_id",contact.take("contest_id")); record.setValue("country",contact.take("country")); record.setValue("country_intl",contact.take("country_intl")); record.setValue("credit_submitted",contact.take("credit_submitted")); record.setValue("credit_granted",contact.take("credit_granted")); record.setValue("darc_dok",contact.take("darc_dok").toString().toUpper()); record.setValue("dcl_qslrdate",parseDate(contact.take("dcl_qslrdate").toString())); record.setValue("dcl_qslsdate",parseDate(contact.take("dcl_qslsdate").toString())); record.setValue("dcl_qsl_rcvd",parseQslRcvd(contact.take("dcl_qsl_rcvd").toString())); record.setValue("dcl_qsl_sent",parseQslSent(contact.take("dcl_qsl_sent").toString())); record.setValue("distance",contact.take("distance")); record.setValue("email",contact.take("email")); record.setValue("eq_call",contact.take("eq_call")); record.setValue("eqsl_ag",parseEqslAg(contact.take("eqsl_ag").toString())); record.setValue("eqsl_qslrdate",parseDate(contact.take("eqsl_qslrdate").toString())); record.setValue("eqsl_qslsdate",parseDate(contact.take("eqsl_qslsdate").toString())); record.setValue("eqsl_qsl_rcvd",parseQslRcvd(contact.take("eqsl_qsl_rcvd").toString())); record.setValue("eqsl_qsl_sent",parseQslSent(contact.take("eqsl_qsl_sent").toString())); record.setValue("fists",contact.take("fists")); record.setValue("fists_cc",contact.take("fists_cc")); record.setValue("force_init",contact.take("force_init").toString().toUpper()); record.setValue("freq_rx",contact.take("freq_rx")); record.setValue("gridsquare_ext",contact.take("gridsquare_ext")); record.setValue("guest_op",contact.take("guest_op")); record.setValue("hamlogeu_qso_upload_date",parseDate(contact.take("hamlogeu_qso_upload_date").toString())); record.setValue("hamlogeu_qso_upload_status",parseUploadStatus(contact.take("hamlogeu_qso_upload_status").toString())); record.setValue("hamqth_qso_upload_date",parseDate(contact.take("hamqth_qso_upload_date").toString())); record.setValue("hamqth_qso_upload_status",parseUploadStatus(contact.take("hamqth_qso_upload_status").toString())); record.setValue("hrdlog_qso_upload_date",parseDate(contact.take("hrdlog_qso_upload_date").toString())); record.setValue("hrdlog_qso_upload_status",parseUploadStatus(contact.take("hrdlog_qso_upload_status").toString())); record.setValue("iota_island_id",contact.take("iota_island_id").toString().toUpper()); record.setValue("k_index",contact.take("k_index")); record.setValue("lat",contact.take("lat")); record.setValue("lon",contact.take("lon")); record.setValue("max_bursts",contact.take("max_bursts")); record.setValue("morse_key_info",contact.take("morse_key_info")); record.setValue("morse_key_type", parseMorseKeyType(contact.take("morse_key_type").toString())); record.setValue("ms_shower",contact.take("ms_shower")); record.setValue("my_antenna",contact.take("my_antenna")); record.setValue("my_antenna_intl",contact.take("my_antenna_intl")); record.setValue("my_altitude",contact.take("my_altitude")); record.setValue("my_arrl_sect",contact.take("my_arrl_sect")); record.setValue("my_city",contact.take("my_city")); record.setValue("my_city_intl",contact.take("my_city_intl")); record.setValue("my_country",contact.take("my_country")); record.setValue("my_country_intl",contact.take("my_country_intl")); record.setValue("my_cnty",contact.take("my_cnty")); record.setValue("my_cnty_alt",contact.take("my_cnty_alt")); record.setValue("my_cq_zone",contact.take("my_cq_zone")); record.setValue("my_darc_dok",contact.take("my_darc_dok").toString().toUpper()); record.setValue("my_dxcc",contact.take("my_dxcc")); record.setValue("my_fists",contact.take("my_fists")); record.setValue("my_gridsquare",contact.take("my_gridsquare").toString().toUpper()); record.setValue("my_gridsquare_ext",contact.take("my_gridsquare_ext").toString().toUpper()); record.setValue("my_iota",contact.take("my_iota").toString().toUpper()); record.setValue("my_iota_island_id",contact.take("my_iota_island_id").toString().toUpper()); record.setValue("my_itu_zone",contact.take("my_itu_zone")); record.setValue("my_lat",contact.take("my_lat")); record.setValue("my_lon",contact.take("my_lon")); record.setValue("my_morse_key_info",contact.take("my_morse_key_info")); record.setValue("my_morse_key_type", parseMorseKeyType(contact.take("my_morse_key_type").toString())); record.setValue("my_name",contact.take("my_name")); record.setValue("my_name_intl",contact.take("my_name_intl")); record.setValue("my_postal_code",contact.take("my_postal_code")); record.setValue("my_postal_code_intl",contact.take("my_postal_code_intl")); record.setValue("my_pota_ref",contact.take("my_pota_ref").toString().toUpper()); record.setValue("my_rig",contact.take("my_rig")); record.setValue("my_rig_intl",contact.take("my_rig_intl")); record.setValue("my_sig",contact.take("my_sig")); record.setValue("my_sig_intl",contact.take("my_sig_intl")); record.setValue("my_sig_info",contact.take("my_sig_info")); record.setValue("my_sig_info_intl",contact.take("my_sig_info_intl")); record.setValue("my_sota_ref",contact.take("my_sota_ref").toString().toUpper()); record.setValue("my_state",contact.take("my_state")); record.setValue("my_street",contact.take("my_street")); record.setValue("my_street_intl",contact.take("my_street_intl")); record.setValue("my_usaca_counties",contact.take("my_usaca_counties")); record.setValue("my_vucc_grids",contact.take("my_vucc_grids").toString().toUpper()); record.setValue("my_wwff_ref",contact.take("my_wwff_ref").toString().toUpper()); record.setValue("name",contact.take("name")); record.setValue("name_intl",contact.take("name_intl")); record.setValue("notes",contact.take("notes")); record.setValue("notes_intl",contact.take("notes_intl")); record.setValue("nr_bursts",contact.take("nr_bursts")); record.setValue("nr_pings",contact.take("nr_pings")); record.setValue("operator",contact.take("operator")); record.setValue("owner_callsign",contact.take("owner_callsign")); record.setValue("pota_ref",contact.take("pota_ref").toString().toUpper()); record.setValue("precedence",contact.take("precedence")); record.setValue("prop_mode",contact.take("prop_mode")); record.setValue("public_key",contact.take("public_key")); record.setValue("qrzcom_qso_download_date",parseDate(contact.take("qrzcom_qso_download_date").toString())); record.setValue("qrzcom_qso_download_status",parseDownloadStatus(contact.take("qrzcom_qso_download_status").toString())); record.setValue("qrzcom_qso_upload_date",parseDate(contact.take("qrzcom_qso_upload_date").toString())); record.setValue("qrzcom_qso_upload_status",parseUploadStatus(contact.take("qrzcom_qso_upload_status").toString())); record.setValue("qsl_rcvd_via",contact.take("qsl_rcvd_via").toString().toUpper()); record.setValue("qsl_sent_via",contact.take("qsl_sent_via").toString().toUpper()); record.setValue("qsl_via",contact.take("qsl_via")); record.setValue("qso_complete",contact.take("qso_complete").toString().toUpper()); record.setValue("qso_random",contact.take("qso_random").toString().toUpper()); record.setValue("qslmsg",contact.take("qslmsg")); record.setValue("qslmsg_intl",contact.take("qslmsg_intl")); record.setValue("qslmsg_rcvd",contact.take("qslmsg_rcvd")); record.setValue("qth",contact.take("qth")); record.setValue("qth_intl",contact.take("qth_intl")); record.setValue("region",contact.take("region")); record.setValue("rig",contact.take("rig")); record.setValue("rig_intl",contact.take("rig_intl")); record.setValue("rx_pwr",contact.take("rx_pwr")); record.setValue("sat_mode",contact.take("sat_mode")); record.setValue("sat_name",contact.take("sat_name")); record.setValue("sfi",contact.take("sfi")); record.setValue("sig",contact.take("sig")); record.setValue("sig_intl",contact.take("sig_intl")); record.setValue("sig_info",contact.take("sig_info")); record.setValue("sig_info_intl",contact.take("sig_info_intl")); record.setValue("silent_key",contact.take("silent_key").toString().toUpper()); record.setValue("skcc",contact.take("skcc")); record.setValue("sota_ref",contact.take("sota_ref").toString().toUpper()); record.setValue("srx",contact.take("srx")); record.setValue("srx_string",contact.take("srx_string")); record.setValue("station_callsign",contact.take("station_callsign").toString().toUpper()); record.setValue("stx",contact.take("stx")); record.setValue("stx_string",contact.take("stx_string")); record.setValue("swl",contact.take("swl").toString().toUpper()); record.setValue("ten_ten",contact.take("ten_ten")); record.setValue("uksmg",contact.take("uksmg")); record.setValue("usaca_counties",contact.take("usaca_counties")); record.setValue("ve_prov",contact.take("ve_prov")); record.setValue("vucc_grids",contact.take("vucc_grids").toString().toUpper()); record.setValue("web",contact.take("web")); record.setValue("wwff_ref",contact.take("wwff_ref").toString().toUpper()); QString mode = contact.take("mode").toString().toUpper(); QString submode = contact.take("submode").toString().toUpper(); const QPair& legacy = Data::instance()->legacyMode(mode); if ( !legacy.first.isEmpty() ) { mode = legacy.first; submode = legacy.second; } record.setValue("mode", mode); record.setValue("submode", submode); const QDate &date_on = parseDate(contact.take("qso_date").toString()); QDate date_off = parseDate(contact.take("qso_date_off").toString()); if ( date_off.isNull() || !date_off.isValid() ) { date_off = date_on; } QTime time_on = parseTime(contact.take("time_on").toString()); QTime time_off = parseTime(contact.take("time_off").toString()); if ( time_on.isValid() && time_off.isNull() ) { time_off = time_on; } if ( time_off.isValid() && time_on.isNull() ) { time_on = time_off; } QDateTime start_time(date_on, time_on, QTimeZone::utc()); QDateTime end_time(date_off, time_off, QTimeZone::utc()); if ( end_time < start_time ) { qCDebug(runtime) << "End time before start time!" << record; } record.setValue("start_time", start_time); record.setValue("end_time", end_time); } const QString AdiFormat::formatOuput(OutputFieldFormatter formatter, const QVariant &in) { QString ret; switch (formatter) { case TOLOWER: ret = toLower(in); break; case TOUPPER: ret = toUpper(in); break; case TODATE: ret = toDate(in); break; case TOTIME: ret = toTime(in); break; case REMOVEDEFAULTVALUEN: ret = removeDefaulValueN(in); break; case TOSTRING: default: ret = toString(in); } return ret; } const QString AdiFormat::toString(const QVariant &var) { return var.toString(); } const QString AdiFormat::toLower(const QVariant &var) { return var.toString().toLower(); } const QString AdiFormat::toUpper(const QVariant &var) { return var.toString().toUpper(); } const QString AdiFormat::toDate(const QVariant &var) { return var.toDate().toString("yyyyMMdd"); } const QString AdiFormat::toTime(const QVariant &var) { return var.toTime().toString("hhmmss"); } // There are fields with a default value of 'N'. // Therefore, it is not necessary to explicitly export them when // the local database also has the value 'N'. This approach can be // helpful, for example, in the case of new fields in ADIF, where // external systems are not yet prepared, and QLog would generate ADIF // with the default value 'N'. const QString AdiFormat::removeDefaulValueN(const QVariant &var) { const QString value = var.toString(); return (value == "N") ? QString() : value; } void AdiFormat::preprocessINTLField(const QString &fieldName, const QString &fieldIntlName, QMap &contact) { FCT_IDENTIFICATION; // NOTE: If modify this, modify also function below!!!! const QVariant &fld = contact.value(fieldName); const QVariant &fldIntl = contact.value(fieldIntlName); /* In general, it is a hack because ADI must not contain * _INTL fields. But some applications generate _INTL fields in ADI files * therefore it is needed to implement a logic how to convert INTL fields * to standard */ if ( !fld.toString().isEmpty() && !fldIntl.toString().isEmpty() ) { /* ascii and intl are present */ //no action } else if ( !fld.toString().isEmpty() && fldIntl.toString().isEmpty() ) { /* ascii is present but Intl is not present */ contact[fieldIntlName] = fld; } else if ( fld.toString().isEmpty() && !fldIntl.toString().isEmpty() ) { /* ascii is empty but Intl is present */ contact[fieldName] = Data::removeAccents(fldIntl.toString()); } else { /* both are empty */ /* do nothing */ } } void AdiFormat::preprocessINTLField(const QString &fieldName, const QString &fieldIntlName, QSqlRecord &contact) { FCT_IDENTIFICATION; // NOTE: If modify this, modify also function above!!!! const QVariant &fld = contact.value(fieldName); const QVariant &fldIntl = contact.value(fieldIntlName); /* In general, it is a hack because ADI must not contain * _INTL fields. But some applications generate _INTL fields in ADI files * therefore it is needed to implement a logic how to convert INTL fields * to standard */ if ( !fld.toString().isEmpty() && !fldIntl.toString().isEmpty() ) { /* ascii and intl are present */ //no action } else if ( !fld.toString().isEmpty() && fldIntl.toString().isEmpty() ) { /* ascii is present but Intl is not present */ contact.setValue(fieldIntlName, fld); } else if ( fld.toString().isEmpty() && !fldIntl.toString().isEmpty() ) { /* ascii is empty but Intl is present */ contact.setValue(fieldName, Data::removeAccents(fldIntl.toString())); } else { /* both are empty */ /* do nothing */ } } bool AdiFormat::readContact(QMap& contact) { FCT_IDENTIFICATION; while (!stream.atEnd()) { QString field; QString value; readField(field, value); field = field.toLower(); if (field == "eor") { return true; } if (!value.isEmpty()) { contact[field] = QVariant(value); } } return false; } AdiFormat::AdiFormat(QTextStream &stream) : LogFormat(stream) { FCT_IDENTIFICATION; #if QT_VERSION >= QT_VERSION_CHECK(6,0,0) stream.setEncoding(QStringConverter::Latin1); #else stream.setCodec("ISO 8859-1"); #endif } bool AdiFormat::importNext(QSqlRecord& record) { FCT_IDENTIFICATION; qCDebug(function_parameters)< contact; if ( !readContact(contact) ) { return false; } mapContact2SQLRecord(contact, record); return true; } QDate AdiFormat::parseDate(const QString &date) { FCT_IDENTIFICATION; qCDebug(function_parameters)< allowedValues({"SK", "SS", "BUG", "FAB", "SP", "DP", "CPU"}); const QString &inValue = value.toUpper(); return (allowedValues.contains(inValue)) ? inValue : QString(); } QString AdiFormat::parseEqslAg(const QString &value) { FCT_IDENTIFICATION; if ( value.isEmpty() ) return QString(); char firstChar = value.toUpper().at(0).toLatin1(); return ( firstChar == 'Y' || firstChar == 'N' || firstChar == 'U' ) ? QString(firstChar) : QString(); } QMap AdiFormat::fieldname2INTLNameMapping = { {"address", "address_intl"}, {"comment", "comment_intl"}, {"country", "country_intl"}, {"my_antenna", "my_antenna_intl"}, {"my_city", "my_city_intl"}, {"my_country", "my_country_intl"}, {"my_name", "my_name_intl"}, {"my_postal_code", "my_postal_code_intl"}, {"my_rig", "my_rig_intl"}, {"my_sig", "my_sig_intl"}, {"my_sig_info", "my_sig_info_intl"}, {"my_street", "my_street_intl"}, {"name", "name_intl"}, {"notes", "notes_intl"}, {"qslmsg", "qslmsg_intl"}, {"qth", "qth_intl"}, {"rig", "rig_intl"}, {"sig", "sig_intl"}, {"sig_info", "sig_info_intl"} }; QHash AdiFormat::DB2ADIFExportParams = { { "callsign", ExportParams("call")}, // { , ExportParams("qso_date")}, //SPECIAL // { , ExportParams("time_on")}, //SPECIAL // { , ExportParams("qso_date_off")}, //SPECIAL // { , ExportParams("time_off")}, //SPECIAL { "rst_rcvd", ExportParams("rst_rcvd")}, { "rst_sent", ExportParams("rst_sent")}, { "name", ExportParams("name")}, { "qth", ExportParams("qth")}, { "gridsquare", ExportParams("gridsquare")}, { "cqz", ExportParams("cqz")}, { "ituz", ExportParams("ituz")}, { "freq", ExportParams("freq", OutputFieldFormatter::TOSTRING)}, { "band", ExportParams("band", OutputFieldFormatter::TOLOWER)}, { "mode", ExportParams("mode")}, { "submode", ExportParams("submode")}, { "cont", ExportParams("cont")}, { "dxcc", ExportParams("dxcc")}, { "country", ExportParams("country")}, { "pfx", ExportParams("pfx")}, { "state", ExportParams("state")}, { "cnty", ExportParams("cnty")}, { "cnty_alt", ExportParams("cnty_alt")}, { "iota", ExportParams("iota", OutputFieldFormatter::TOUPPER)}, { "qsl_rcvd", ExportParams("qsl_rcvd", OutputFieldFormatter::REMOVEDEFAULTVALUEN)}, { "qsl_rdate", ExportParams("qslrdate", OutputFieldFormatter::TODATE)}, { "qsl_sent", ExportParams("qsl_sent", OutputFieldFormatter::REMOVEDEFAULTVALUEN)}, { "qsl_sdate", ExportParams("qslsdate", OutputFieldFormatter::TODATE)}, { "lotw_qsl_rcvd", ExportParams("lotw_qsl_rcvd", OutputFieldFormatter::REMOVEDEFAULTVALUEN)}, { "lotw_qslrdate", ExportParams("lotw_qslrdate", OutputFieldFormatter::TODATE)}, { "lotw_qsl_sent", ExportParams("lotw_qsl_sent", OutputFieldFormatter::REMOVEDEFAULTVALUEN)}, { "lotw_qslsdate", ExportParams("lotw_qslsdate", OutputFieldFormatter::TODATE)}, { "tx_pwr", ExportParams("tx_pwr")}, { "address", ExportParams("address")}, { "age", ExportParams("age")}, { "altitude", ExportParams("altitude")}, { "a_index", ExportParams("a_index")}, { "ant_az", ExportParams("ant_az")}, { "ant_el", ExportParams("ant_el")}, { "ant_path", ExportParams("ant_path")}, { "arrl_sect", ExportParams("arrl_sect")}, { "award_submitted", ExportParams("award_submitted")}, { "award_granted", ExportParams("award_granted")}, { "band_rx", ExportParams("band_rx", OutputFieldFormatter::TOLOWER)}, { "check", ExportParams("check")}, { "class", ExportParams("class")}, { "clublog_qso_upload_date", ExportParams("clublog_qso_upload_date", OutputFieldFormatter::TODATE)}, { "clublog_qso_upload_status", ExportParams("clublog_qso_upload_status")}, { "comment", ExportParams("comment")}, { "contacted_op", ExportParams("contacted_op")}, { "contest_id", ExportParams("contest_id")}, { "credit_submitted", ExportParams("credit_submitted")}, { "credit_granted", ExportParams("credit_granted")}, { "darc_dok", ExportParams("darc_dok")}, { "dcl_qslrdate", ExportParams("dcl_qslrdate", OutputFieldFormatter::TODATE)}, { "dcl_qslsdate", ExportParams("dcl_qslsdate", OutputFieldFormatter::TODATE)}, { "dcl_qsl_rcvd", ExportParams("dcl_qsl_rcvd", OutputFieldFormatter::REMOVEDEFAULTVALUEN)}, { "dcl_qsl_sent", ExportParams("dcl_qsl_sent", OutputFieldFormatter::REMOVEDEFAULTVALUEN)}, { "distance", ExportParams("distance")}, { "email", ExportParams("email")}, { "eq_call", ExportParams("eq_call")}, { "eqsl_ag", ExportParams("eqsl_ag", OutputFieldFormatter::TOUPPER)}, { "eqsl_qslrdate", ExportParams("eqsl_qslrdate", OutputFieldFormatter::TODATE)}, { "eqsl_qslsdate", ExportParams("eqsl_qslsdate", OutputFieldFormatter::TODATE)}, { "eqsl_qsl_rcvd", ExportParams("eqsl_qsl_rcvd", OutputFieldFormatter::REMOVEDEFAULTVALUEN)}, { "eqsl_qsl_sent", ExportParams("eqsl_qsl_sent", OutputFieldFormatter::REMOVEDEFAULTVALUEN)}, { "fists", ExportParams("fists")}, { "fists_cc", ExportParams("fists_cc")}, { "force_init", ExportParams("force_init")}, { "freq_rx", ExportParams("freq_rx")}, { "gridsquare_ext", ExportParams("gridsquare_ext")}, { "guest_op", ExportParams("guest_op")}, { "hamlogeu_qso_upload_date", ExportParams("hamlogeu_qso_upload_date", OutputFieldFormatter::TODATE)}, { "hamlogeu_qso_upload_status", ExportParams("hamlogeu_qso_upload_status")}, { "hamqth_qso_upload_date", ExportParams("hamqth_qso_upload_date", OutputFieldFormatter::TODATE)}, { "hamqth_qso_upload_status", ExportParams("hamqth_qso_upload_status")}, { "hrdlog_qso_upload_date", ExportParams("hrdlog_qso_upload_date", OutputFieldFormatter::TODATE)}, { "hrdlog_qso_upload_status", ExportParams("hrdlog_qso_upload_status")}, { "iota_island_id", ExportParams("iota_island_id", OutputFieldFormatter::TOUPPER)}, { "k_index", ExportParams("k_index")}, { "lat", ExportParams("lat")}, { "lon", ExportParams("lon")}, { "max_bursts", ExportParams("max_bursts")}, { "morse_key_info", ExportParams("morse_key_info")}, { "morse_key_type", ExportParams("morse_key_type", OutputFieldFormatter::TOUPPER)}, { "ms_shower", ExportParams("ms_shower")}, { "my_altitude", ExportParams("my_altitude")}, { "my_arrl_sect", ExportParams("my_arrl_sect")}, { "my_antenna", ExportParams("my_antenna")}, { "my_city", ExportParams("my_city")}, { "my_cnty", ExportParams("my_cnty")}, { "my_cnty_alt", ExportParams("my_cnty_alt")}, { "my_country", ExportParams("my_country")}, { "my_cq_zone", ExportParams("my_cq_zone")}, { "my_darc_dok", ExportParams("my_darc_dok")}, { "my_dxcc", ExportParams("my_dxcc")}, { "my_fists", ExportParams("my_fists")}, { "my_gridsquare", ExportParams("my_gridsquare")}, { "my_gridsquare_ext", ExportParams("my_gridsquare_ext")}, { "my_iota", ExportParams("my_iota", OutputFieldFormatter::TOUPPER)}, { "my_iota_island_id", ExportParams("my_iota_island_id", OutputFieldFormatter::TOUPPER)}, { "my_itu_zone", ExportParams("my_itu_zone")}, { "my_lat", ExportParams("my_lat")}, { "my_lon", ExportParams("my_lon")}, { "my_morse_key_info", ExportParams("my_morse_key_info")}, { "my_morse_key_type", ExportParams("my_morse_key_type", OutputFieldFormatter::TOUPPER)}, { "my_name", ExportParams("my_name")}, { "my_postal_code", ExportParams("my_postal_code")}, { "my_pota_ref", ExportParams("my_pota_ref", OutputFieldFormatter::TOUPPER)}, { "my_rig", ExportParams("my_rig")}, { "my_sig", ExportParams("my_sig")}, { "my_sig_info", ExportParams("my_sig_info")}, { "my_sota_ref", ExportParams("my_sota_ref", OutputFieldFormatter::TOUPPER)}, { "my_state", ExportParams("my_state")}, { "my_street", ExportParams("my_street")}, { "my_usaca_counties", ExportParams("my_usaca_counties")}, { "my_vucc_grids", ExportParams("my_vucc_grids", OutputFieldFormatter::TOUPPER)}, { "my_wwff_ref", ExportParams("my_wwff_ref", OutputFieldFormatter::TOUPPER)}, { "notes", ExportParams("notes")}, { "nr_bursts", ExportParams("nr_bursts")}, { "nr_pings", ExportParams("nr_pings")}, { "operator", ExportParams("operator")}, { "owner_callsign", ExportParams("owner_callsign")}, { "pota_ref", ExportParams("pota_ref", OutputFieldFormatter::TOUPPER)}, { "precedence", ExportParams("precedence")}, { "prop_mode", ExportParams("prop_mode")}, { "public_key", ExportParams("public_key")}, { "qrzcom_qso_download_date", ExportParams("qrzcom_qso_download_date", OutputFieldFormatter::TODATE)}, { "qrzcom_qso_download_status", ExportParams("qrzcom_qso_download_status")}, { "qrzcom_qso_upload_date", ExportParams("qrzcom_qso_upload_date", OutputFieldFormatter::TODATE)}, { "qrzcom_qso_upload_status", ExportParams("qrzcom_qso_upload_status")}, { "qslmsg", ExportParams("qslmsg")}, { "qslmsg_rcvd", ExportParams("qslmsg_rcvd")}, { "qsl_rcvd_via", ExportParams("qsl_rcvd_via")}, { "qsl_sent_via", ExportParams("qsl_sent_via")}, { "qsl_via", ExportParams("qsl_via")}, { "qso_complete", ExportParams("qso_complete")}, { "qso_random", ExportParams("qso_random")}, { "region", ExportParams("region")}, { "rig", ExportParams("rig")}, { "rx_pwr", ExportParams("rx_pwr")}, { "sat_mode", ExportParams("sat_mode")}, { "sat_name", ExportParams("sat_name")}, { "sfi", ExportParams("sfi")}, { "sig", ExportParams("sig")}, { "sig_info", ExportParams("sig_info")}, { "silent_key", ExportParams("silent_key")}, { "skcc", ExportParams("skcc")}, { "sota_ref", ExportParams("sota_ref", OutputFieldFormatter::TOUPPER)}, { "srx", ExportParams("srx")}, { "srx_string", ExportParams("srx_string")}, { "station_callsign", ExportParams("station_callsign")}, { "stx", ExportParams("stx")}, { "stx_string", ExportParams("stx_string")}, { "swl", ExportParams("swl")}, { "ten_ten", ExportParams("ten_ten")}, { "uksmg", ExportParams("uksmg")}, { "usaca_counties", ExportParams("usaca_counties")}, { "ve_prov", ExportParams("ve_prov")}, { "vucc_grids", ExportParams("vucc_grids", OutputFieldFormatter::TOUPPER)}, { "web", ExportParams("web")}, { "wwff_ref", ExportParams("wwff_ref", OutputFieldFormatter::TOUPPER)} }; #undef ALWAYS_PRESENT ================================================ FILE: logformat/AdiFormat.h ================================================ #ifndef QLOG_LOGFORMAT_ADIFORMAT_H #define QLOG_LOGFORMAT_ADIFORMAT_H #include "LogFormat.h" class AdiFormat : public LogFormat { public: explicit AdiFormat(QTextStream& stream); virtual bool importNext(QSqlRecord& ) override; virtual void exportContact(const QSqlRecord&, QMap *applTags = nullptr) override; virtual void exportStart() override; static QMap fieldname2INTLNameMapping; template static void preprocessINTLFields(T &contact) { { const QStringList &fieldMappingList = fieldname2INTLNameMapping.keys(); for ( const QString& fieldName : fieldMappingList ) preprocessINTLField(fieldName, fieldname2INTLNameMapping.value(fieldName), contact); } } protected: virtual void writeField(const QString &name, bool presenceCondition, const QString &value, const QString &type=""); virtual void writeSQLRecord(const QSqlRecord& record, QMap *applTags); virtual bool readContact(QVariantMap &); void mapContact2SQLRecord(QMap &contact, QSqlRecord &record); void contactFields2SQLRecord(QMap &contact, QSqlRecord &record); enum OutputFieldFormatter { TOSTRING, TOLOWER, TOUPPER, TODATE, TOTIME, REMOVEDEFAULTVALUEN }; const QString formatOuput(OutputFieldFormatter formatter, const QVariant &in); virtual const QString toString(const QVariant &); virtual const QString toLower(const QVariant &); virtual const QString toUpper(const QVariant &); virtual const QString toDate(const QVariant &); virtual const QString toTime(const QVariant &); virtual const QString removeDefaulValueN(const QVariant &); class ExportParams { public: ExportParams() : ADIFName(QString()), outputType(QString()), formatter(TOSTRING), isValid(false) {}; ExportParams(const QString &inADIFName, const OutputFieldFormatter formatter = OutputFieldFormatter::TOSTRING, const QString &inType = QString()) : ADIFName(inADIFName), outputType(inType), formatter(formatter), isValid(true) {}; QString ADIFName; QString outputType; OutputFieldFormatter formatter; bool isValid; }; static QHash DB2ADIFExportParams; const QString ADIF_VERSION_STRING = "3.1.7"; const QString PROGRAMID_STRING = "QLog"; private: void readField(QString& field, QString& value); QDate parseDate(const QString &date); QTime parseTime(const QString &time); QString parseQslRcvd(const QString &value); QString parseQslSent(const QString &value); QString parseUploadStatus(const QString &value); QString parseDownloadStatus(const QString &value); QString parseMorseKeyType(const QString &value); QString parseEqslAg(const QString &value); enum ParserState { START, FIELD, KEY, SIZE, DATA_TYPE, VALUE }; static void preprocessINTLField(const QString &fieldName, const QString &fieldIntlName, QMap &contact); static void preprocessINTLField(const QString &fieldName, const QString &fieldIntlName, QSqlRecord &contact); ParserState state = START; bool inHeader = false; }; #endif // QLOG_LOGFORMAT_ADIFORMAT_H ================================================ FILE: logformat/AdxFormat.cpp ================================================ #include #include #include "logformat/AdxFormat.h" #include "logformat/AdiFormat.h" #include "core/debug.h" MODULE_IDENTIFICATION("qlog.logformat.adxformat"); AdxFormat::AdxFormat(QTextStream &stream) : AdiFormat(stream), writer(nullptr), reader(nullptr) { FCT_IDENTIFICATION; #if QT_VERSION >= QT_VERSION_CHECK(6,0,0) stream.setEncoding(QStringConverter::Utf8); #else stream.setCodec("UTF-8"); #endif } void AdxFormat::importStart() { FCT_IDENTIFICATION; reader = new QXmlStreamReader(stream.device()); while ( reader->readNextStartElement() ) { qCDebug(runtime)<name(); if ( reader->name() == QString("ADX") ) { while ( reader->readNextStartElement() ) { qCDebug(runtime)<name(); if ( reader->name() == QString("HEADER") ) { reader->skipCurrentElement(); } else if ( reader->name() == QString("RECORDS") ) { qCDebug(runtime)<<"records found"; /* header is loaded, QLog is currently in Records sections which is loaded by importNext procedure */ return; } } } else { reader->skipCurrentElement(); } } } void AdxFormat::importEnd() { FCT_IDENTIFICATION; if ( reader ) { delete reader; reader = nullptr; } } void AdxFormat::exportStart() { FCT_IDENTIFICATION; QString date = QDateTime::currentDateTimeUtc().toString("yyyyMMdd hhmmss"); writer = new QXmlStreamWriter(stream.device()); writer->setAutoFormatting(true); writer->writeStartDocument(); writer->writeStartElement("ADX"); writer->writeStartElement("HEADER"); writer->writeTextElement("ADIF_VER", ADIF_VERSION_STRING); writer->writeTextElement("PROGRAMID", PROGRAMID_STRING); writer->writeTextElement("PROGRAMVERSION", VERSION); writer->writeTextElement("CREATED_TIMESTAMP", date); writer->writeEndElement(); writer->writeStartElement("RECORDS"); } void AdxFormat::exportEnd() { FCT_IDENTIFICATION; if ( writer ) { writer->writeEndDocument(); delete writer; writer = nullptr; } } void AdxFormat::exportContact(const QSqlRecord& record, QMap *applTags) { FCT_IDENTIFICATION; qCDebug(function_parameters)<writeStartElement("RECORD"); writeSQLRecord(record, applTags); writer->writeEndElement(); } void AdxFormat::writeField(const QString &name, bool presenceCondition, const QString &value, const QString &type) { FCT_IDENTIFICATION; qCDebug(function_parameters)<< name << presenceCondition << value << type; if (value.isEmpty() || !presenceCondition ) return; writer->writeTextElement(name.toUpper(), value); } void AdxFormat::writeSQLRecord(const QSqlRecord &record, QMap *applTags) { FCT_IDENTIFICATION; AdiFormat::writeSQLRecord(record, applTags); // Add _INTL fields const QStringList &fieldMappingList = fieldname2INTLNameMapping.values(); for ( const QString& value : fieldMappingList ) { const QVariant &tmp = record.value(value); writeField(value, tmp.isValid(), tmp.toString()); } } bool AdxFormat::readContact(QVariantMap & contact) { FCT_IDENTIFICATION; while ( !reader->atEnd() ) { reader->readNextStartElement(); qCDebug(runtime)<name(); if ( reader->name() == QString("RECORDS") && reader->isEndElement() ) { qCDebug(runtime)<<"End Records Element"; return false; } if ( reader->name() == QString("RECORD") ) { while (reader->readNextStartElement() ) { qCDebug(runtime)<<"adding element " << reader->name(); contact[reader->name().toLatin1().toLower()] = QVariant(reader->readElementText()); } return true; } else { reader->skipCurrentElement(); } } return false; } ================================================ FILE: logformat/AdxFormat.h ================================================ #ifndef QLOG_LOGFORMAT_ADXFORMAT_H #define QLOG_LOGFORMAT_ADXFORMAT_H #include #include "AdiFormat.h" class AdxFormat : public AdiFormat { public: explicit AdxFormat(QTextStream& stream); virtual void importStart() override; virtual void importEnd() override; virtual void exportContact(const QSqlRecord& record, QMap *applTags = nullptr) override; virtual void exportStart() override; virtual void exportEnd() override; protected: virtual void writeField(const QString &name, bool presenceCondition, const QString &value, const QString &type="") override; virtual void writeSQLRecord(const QSqlRecord& record, QMap *applTags) override; virtual bool readContact(QVariantMap& ) override; private: QXmlStreamWriter *writer; QXmlStreamReader *reader; }; #endif // QLOG_LOGFORMAT_ADXFORMAT_H ================================================ FILE: logformat/CSVFormat.cpp ================================================ #include #include "CSVFormat.h" #include "core/debug.h" MODULE_IDENTIFICATION("qlog.logformat.csvformat"); CSVFormat::CSVFormat(QTextStream &stream) : AdxFormat(stream), delimiter(',') { FCT_IDENTIFICATION; } void CSVFormat::exportStart() { FCT_IDENTIFICATION; } void CSVFormat::exportContact(const QSqlRecord &record, QMap *applTags) { FCT_IDENTIFICATION; qCDebug(function_parameters) << record; currectRecord.clear(); writeSQLRecord(record, applTags); exportedRecords << currectRecord; } void CSVFormat::exportEnd() { FCT_IDENTIFICATION; // Print Header - QMap sorts keys automatically const QList headerKeys(header.keys()); QStringList row; for ( const QString& headerField : headerKeys ) { row << headerField; } stream << row.join(delimiter) << "\n"; // Normalize and print exported records for ( const QHash &record : static_cast>&>(exportedRecords) ) { row.clear(); for ( const QString& headerField : headerKeys ) { row << record.value(headerField); } stream << row.join(delimiter) << "\n"; } } void CSVFormat::setDelimiter(const QChar &inDelimiter) { FCT_IDENTIFICATION; delimiter = inDelimiter; } void CSVFormat::writeField(const QString &name, bool presenceCondition, const QString &value, const QString &type) { FCT_IDENTIFICATION; qCDebug(function_parameters)<< name << presenceCondition << value << type; if ( !presenceCondition ) return; header[name] = 0; // using QMap only due to ordering and uniq, number is not used at this moment; currectRecord[name] = csvStringValue(value); } const QString CSVFormat::toDate(const QVariant &var) { return var.toDate().toString("yyyy-MM-dd"); } const QString CSVFormat::toTime(const QVariant &var) { return var.toTime().toString("hh:mm:ss"); } QString CSVFormat::csvStringValue(const QString &value) { FCT_IDENTIFICATION; return ((value.contains(delimiter))? "\"" + value + "\"" : value); } ================================================ FILE: logformat/CSVFormat.h ================================================ #ifndef QLOG_LOGFORMAT_CSVFORMAT_H #define QLOG_LOGFORMAT_CSVFORMAT_H #include "LogFormat.h" #include "logformat/AdxFormat.h" // derived from ADX because ADX format contains non-intl and intl fields class CSVFormat : public AdxFormat { public: explicit CSVFormat(QTextStream& stream); virtual void exportStart() override; virtual void exportContact(const QSqlRecord& record, QMap *) override; virtual void exportEnd() override; virtual void importStart() override {}; virtual void importEnd() override {}; void setDelimiter(const QChar&); protected: virtual void writeField(const QString &name, bool presenceCondition, const QString &value, const QString &type="") override; const QString toDate(const QVariant &) override; const QString toTime(const QVariant &) override; private: QMap header; QList> exportedRecords; QHash currectRecord; QString csvStringValue(const QString&); QChar delimiter; }; #endif // QLOG_LOGFORMAT_CSVFORMAT_H ================================================ FILE: logformat/CabrilloFormat.cpp ================================================ #include "CabrilloFormat.h" #include #include #include #include #include "core/debug.h" #include "data/BandPlan.h" MODULE_IDENTIFICATION("qlog.logformat.cabrilloformat"); const QString CabrilloFormat::FMT_NONE; const QString CabrilloFormat::FMT_FREQ_KHZ = "freq_khz"; const QString CabrilloFormat::FMT_TIME_HHMM = "time_hhmm"; const QString CabrilloFormat::FMT_DATE_YYYY_MM_DD = "date_yyyy_mm_dd"; const QString CabrilloFormat::FMT_RST_SHORT = "rst_short"; const QString CabrilloFormat::FMT_UPPER = "upper"; const QString CabrilloFormat::FMT_MODE_CABRILLO = "mode_cabrillo"; const QString CabrilloFormat::FMT_TRANSMITTER_ID = "transmitter_id"; const QString CabrilloFormat::FMT_PADDED_NR = "padded_nr"; const QString CabrilloFormat::BAND_ALL = "ALL"; const QString CabrilloFormat::BAND_160M = "160M"; const QString CabrilloFormat::BAND_80M = "80M"; const QString CabrilloFormat::BAND_40M = "40M"; const QString CabrilloFormat::BAND_20M = "20M"; const QString CabrilloFormat::BAND_15M = "15M"; const QString CabrilloFormat::BAND_10M = "10M"; const QString CabrilloFormat::BAND_6M = "6M"; const QString CabrilloFormat::BAND_4M = "4M"; const QString CabrilloFormat::BAND_2M = "2M"; const QString CabrilloFormat::BAND_222 = "222"; const QString CabrilloFormat::BAND_432 = "432"; const QString CabrilloFormat::BAND_902 = "902"; const QString CabrilloFormat::BAND_1_2G = "1.2G"; const QString CabrilloFormat::BAND_LIGHT = "LIGHT"; const QString CabrilloFormat::BAND_VHF_3_BAND = "VHF-3-BAND"; const QString CabrilloFormat::BAND_VHF_FM_ONLY = "VHF-FM-ONLY"; const QString CabrilloFormat::MODE_CW = "CW"; const QString CabrilloFormat::MODE_SSB = "SSB"; const QString CabrilloFormat::MODE_RTTY = "RTTY"; const QString CabrilloFormat::MODE_FM = "FM"; const QString CabrilloFormat::MODE_DIGI = "DIGI"; const QString CabrilloFormat::MODE_MIXED = "MIXED"; const QString CabrilloFormat::POWER_HIGH = "HIGH"; const QString CabrilloFormat::POWER_LOW = "LOW"; const QString CabrilloFormat::POWER_QRP = "QRP"; const QString CabrilloFormat::OPERATOR_SINGLE = "SINGLE-OP"; const QString CabrilloFormat::OPERATOR_MULTI = "MULTI-OP"; const QString CabrilloFormat::OPERATOR_CHECKLOG = "CHECKLOG"; const QString CabrilloFormat::ASSISTED_NO = "NON-ASSISTED"; const QString CabrilloFormat::ASSISTED_YES = "ASSISTED"; const QString CabrilloFormat::STATION_FIXED = "FIXED"; const QString CabrilloFormat::STATION_MOBILE = "MOBILE"; const QString CabrilloFormat::STATION_PORTABLE = "PORTABLE"; const QString CabrilloFormat::STATION_ROVER = "ROVER"; const QString CabrilloFormat::STATION_ROVER_LIMITED = "ROVER-LIMITED"; const QString CabrilloFormat::STATION_ROVER_UNLIMITED = "ROVER-UNLIMITED"; const QString CabrilloFormat::STATION_EXPEDITION = "EXPEDITION"; const QString CabrilloFormat::STATION_HQ = "HQ"; const QString CabrilloFormat::STATION_SCHOOL = "SCHOOL"; const QString CabrilloFormat::STATION_DISTRIBUTED = "DISTRIBUTED"; const QString CabrilloFormat::TRANSMITTER_ONE = "ONE"; const QString CabrilloFormat::TRANSMITTER_TWO = "TWO"; const QString CabrilloFormat::TRANSMITTER_LIMITED = "LIMITED"; const QString CabrilloFormat::TRANSMITTER_UNLIMITED = "UNLIMITED"; const QString CabrilloFormat::TRANSMITTER_SWL = "SWL"; const QString CabrilloFormat::TIME_6_HOURS = "6-HOURS"; const QString CabrilloFormat::TIME_12_HOURS = "12-HOURS"; const QString CabrilloFormat::TIME_24_HOURS = "24-HOURS"; const QString CabrilloFormat::OVERLAY_CLASSIC = "CLASSIC"; const QString CabrilloFormat::OVERLAY_ROOKIE = "ROOKIE"; const QString CabrilloFormat::OVERLAY_TB_WIRES = "TB-WIRES"; const QString CabrilloFormat::OVERLAY_NOVICE_TECH = "NOVICE-TECH"; const QString CabrilloFormat::OVERLAY_OVER_50 = "OVER-50"; CabrilloFormat::CabrilloFormat(QTextStream &stream) : LogFormat(stream), templateId(-1), transmitterId(0), multiOpEnabled(false) { FCT_IDENTIFICATION; } void CabrilloFormat::setTemplateId(int id) { FCT_IDENTIFICATION; qCDebug(function_parameters) << id; templateId = id; } void CabrilloFormat::setHeaderData(const HeaderData &data) { FCT_IDENTIFICATION; headerData = data; } void CabrilloFormat::setTransmitterId(int id) { FCT_IDENTIFICATION; qCDebug(function_parameters) << id; transmitterId = id; } QMap CabrilloFormat::buildHeaderFields() const { FCT_IDENTIFICATION; QMap fields; fields["CALLSIGN"] = headerData.callsign; auto addIfNotEmpty = [&](const QString& key, const QString& value) { if (!value.isEmpty()) fields[key] = value; }; addIfNotEmpty("NAME", headerData.operatorName); addIfNotEmpty("EMAIL", headerData.email); addIfNotEmpty("OPERATORS", headerData.operators); addIfNotEmpty("ADDRESS", headerData.address); addIfNotEmpty("GRID-LOCATOR", headerData.gridLocator); addIfNotEmpty("CATEGORY-OPERATOR", headerData.categoryOperator); addIfNotEmpty("CATEGORY-BAND", headerData.categoryBand); addIfNotEmpty("CATEGORY-POWER", headerData.categoryPower); addIfNotEmpty("CATEGORY-MODE", headerData.categoryMode); addIfNotEmpty("CATEGORY-ASSISTED", headerData.categoryAssisted); addIfNotEmpty("CATEGORY-STATION", headerData.categoryStation); addIfNotEmpty("CATEGORY-TRANSMITTER", headerData.categoryTransmitter); addIfNotEmpty("CATEGORY-TIME", headerData.categoryTime); addIfNotEmpty("CATEGORY-OVERLAY", headerData.categoryOverlay); addIfNotEmpty("LOCATION", headerData.location); addIfNotEmpty("CLUB", headerData.club); addIfNotEmpty("OFFTIME", headerData.offtime); addIfNotEmpty("SOAPBOX", headerData.soapbox); return fields; } QList CabrilloFormat::templateList() { FCT_IDENTIFICATION; QList result; QSqlQuery query("SELECT id, name, " "contest_name_for_header, default_category_mode " "FROM cabrillo_templates ORDER BY name"); while ( query.next() ) { TemplateInfo info; info.id = query.value(0).toInt(); info.name = query.value(1).toString(); info.contestName = query.value(2).toString(); info.defaultCategoryMode = query.value(3).toString(); result.append(info); } return result; } CabrilloFormat::TemplateInfo CabrilloFormat::templateInfo(int templateId) { FCT_IDENTIFICATION; qCDebug(function_parameters) << templateId; TemplateInfo info; info.id = -1; QSqlQuery query; if ( !query.prepare("SELECT id, name, " "contest_name_for_header, default_category_mode " "FROM cabrillo_templates WHERE id = :id") ) { qCWarning(runtime) << query.lastError().text(); return info; } query.bindValue(":id", templateId); if ( query.exec() && query.next() ) { info.id = query.value(0).toInt(); info.name = query.value(1).toString(); info.contestName = query.value(2).toString(); info.defaultCategoryMode = query.value(3).toString(); } else { qCWarning(runtime) << "Template not found:" << templateId; } return info; } QList CabrilloFormat::bandCategories() { return { { BAND_ALL, QCoreApplication::translate("CabrilloFormat","All Bands") }, { BAND_160M, "160m" }, { BAND_80M, "80m" }, { BAND_40M, "40m" }, { BAND_20M, "20m" }, { BAND_15M, "15m" }, { BAND_10M, "10m" }, { BAND_6M, "6m" }, { BAND_4M, "4m" }, { BAND_2M, QCoreApplication::translate("CabrilloFormat","2m (144 MHz)") }, { BAND_222, QCoreApplication::translate("CabrilloFormat","1.25m (222 MHz)") }, { BAND_432, QCoreApplication::translate("CabrilloFormat","70cm (432 MHz)") }, { BAND_902, QCoreApplication::translate("CabrilloFormat","33cm (902 MHz)") }, { BAND_1_2G, QCoreApplication::translate("CabrilloFormat","23cm (1.2 GHz)") }, { BAND_LIGHT, QCoreApplication::translate("CabrilloFormat","Light") }, { BAND_VHF_3_BAND, QCoreApplication::translate("CabrilloFormat","VHF 3-Band") }, { BAND_VHF_FM_ONLY, QCoreApplication::translate("CabrilloFormat","VHF FM Only") }, }; } QList CabrilloFormat::modeCategories() { return { { MODE_CW, "CW" }, { MODE_SSB, "SSB" }, { MODE_RTTY, "RTTY" }, { MODE_FM, "FM" }, { MODE_DIGI, QCoreApplication::translate("CabrilloFormat","Digital") }, { MODE_MIXED, QCoreApplication::translate("CabrilloFormat","Mixed") }, }; } QList CabrilloFormat::powerCategories() { return { { POWER_HIGH, QCoreApplication::translate("CabrilloFormat","High") }, { POWER_LOW, QCoreApplication::translate("CabrilloFormat","Low") }, { POWER_QRP, QCoreApplication::translate("CabrilloFormat","QRP") }, }; } QList CabrilloFormat::operatorCategories() { return { { OPERATOR_SINGLE, QCoreApplication::translate("CabrilloFormat", "Single Operator") }, { OPERATOR_MULTI, QCoreApplication::translate("CabrilloFormat","Multi Operator") }, { OPERATOR_CHECKLOG, QCoreApplication::translate("CabrilloFormat","Check Log") }, }; } QList CabrilloFormat::assistedCategories() { return { { ASSISTED_NO, QCoreApplication::translate("CabrilloFormat","Non-Assisted") }, { ASSISTED_YES, QCoreApplication::translate("CabrilloFormat","Assisted") }, }; } QList CabrilloFormat::stationCategories() { return { { STATION_FIXED, QCoreApplication::translate("CabrilloFormat","Fixed") }, { STATION_MOBILE, QCoreApplication::translate("CabrilloFormat","Mobile") }, { STATION_PORTABLE, QCoreApplication::translate("CabrilloFormat","Portable") }, { STATION_ROVER, QCoreApplication::translate("CabrilloFormat","Rover") }, { STATION_ROVER_LIMITED, QCoreApplication::translate("CabrilloFormat","Rover Limited") }, { STATION_ROVER_UNLIMITED, QCoreApplication::translate("CabrilloFormat","Rover Unlimited") }, { STATION_EXPEDITION, QCoreApplication::translate("CabrilloFormat","Expedition") }, { STATION_HQ, QCoreApplication::translate("CabrilloFormat","HQ") }, { STATION_SCHOOL, QCoreApplication::translate("CabrilloFormat","School") }, { STATION_DISTRIBUTED, QCoreApplication::translate("CabrilloFormat","Distributed") }, }; } QList CabrilloFormat::transmitterCategories() { return { { TRANSMITTER_ONE, QCoreApplication::translate("CabrilloFormat","One") }, { TRANSMITTER_TWO, QCoreApplication::translate("CabrilloFormat","Two") }, { TRANSMITTER_LIMITED, QCoreApplication::translate("CabrilloFormat","Limited") }, { TRANSMITTER_UNLIMITED, QCoreApplication::translate("CabrilloFormat","Unlimited") }, { TRANSMITTER_SWL, QCoreApplication::translate("CabrilloFormat","SWL") }, }; } QList CabrilloFormat::timeCategories() { return { { TIME_6_HOURS, QCoreApplication::translate("CabrilloFormat","6 Hours") }, { TIME_12_HOURS, QCoreApplication::translate("CabrilloFormat","12 Hours") }, { TIME_24_HOURS, QCoreApplication::translate("CabrilloFormat","24 Hours") }, }; } QList CabrilloFormat::overlayCategories() { return { { OVERLAY_CLASSIC, QCoreApplication::translate("CabrilloFormat","Classic") }, { OVERLAY_ROOKIE, QCoreApplication::translate("CabrilloFormat","Rookie") }, { OVERLAY_TB_WIRES, QCoreApplication::translate("CabrilloFormat","TB Wires") }, { OVERLAY_NOVICE_TECH, QCoreApplication::translate("CabrilloFormat","Novice/Tech") }, { OVERLAY_OVER_50, QCoreApplication::translate("CabrilloFormat","Over 50") }, }; } QList CabrilloFormat::formatterTypes() { return { { FMT_NONE, QCoreApplication::translate("CabrilloFormat","Text (left-aligned)") }, { FMT_FREQ_KHZ, QCoreApplication::translate("CabrilloFormat","Frequency (kHz)") }, { FMT_TIME_HHMM, QCoreApplication::translate("CabrilloFormat","Time (HHMM)") }, { FMT_DATE_YYYY_MM_DD, QCoreApplication::translate("CabrilloFormat","Date (YYYY-MM-DD)") }, { FMT_RST_SHORT, QCoreApplication::translate("CabrilloFormat","RST Short (drop last digit)") }, { FMT_UPPER, QCoreApplication::translate("CabrilloFormat","Uppercase") }, { FMT_MODE_CABRILLO, QCoreApplication::translate("CabrilloFormat","Mode (Cabrillo)") }, { FMT_PADDED_NR, QCoreApplication::translate("CabrilloFormat","Zero-Padded Nr.") }, { FMT_TRANSMITTER_ID, QCoreApplication::translate("CabrilloFormat","Transmitter ID") }, }; } void CabrilloFormat::loadTemplate() { FCT_IDENTIFICATION; columns.clear(); const TemplateInfo info = templateInfo(templateId); contestName = info.contestName; columns = loadTemplateColumns(templateId); } QList CabrilloFormat::loadTemplateColumns(int templateId) { FCT_IDENTIFICATION; QList result; QSqlQuery colQuery; if ( !colQuery.prepare("SELECT position, db_field, width, formatter, label " "FROM cabrillo_template_columns " "WHERE template_id = :tid ORDER BY position") ) { qCWarning(runtime) << colQuery.lastError().text(); return result; } colQuery.bindValue(":tid", templateId); if ( !colQuery.exec() ) { qCWarning(runtime) << colQuery.lastError().text(); return result; } while ( colQuery.next() ) { ColumnDef col; col.position = colQuery.value(0).toInt(); col.dbField = colQuery.value(1).toString(); col.width = colQuery.value(2).toInt(); col.formatter = colQuery.value(3).toString(); col.label = colQuery.value(4).toString(); result.append(col); } return result; } QString CabrilloFormat::formatField(const QString &value, const QString &formatter, int width) { FCT_IDENTIFICATION; qCDebug(function_parameters) << value << formatter << width; QString result = value; if ( !formatter.isEmpty() ) { if ( formatter == FMT_FREQ_KHZ ) { // MHz string -> kHz integer bool ok; double mhz = value.toDouble(&ok); if ( ok ) result = QString::number(static_cast(mhz * 1000)); } else if ( formatter == FMT_TIME_HHMM ) { QDateTime dt = QDateTime::fromString(value, Qt::ISODate); if ( dt.isValid() ) result = dt.toString("HHmm"); } else if ( formatter == FMT_DATE_YYYY_MM_DD ) { QDateTime dt = QDateTime::fromString(value, Qt::ISODate); if ( dt.isValid() ) result = dt.toString("yyyy-MM-dd"); } else if ( formatter == FMT_RST_SHORT ) { // 599 -> 59 (drop last digit) if ( result.length() >= 2 ) result = result.left(result.length() - 1); } else if ( formatter == FMT_UPPER ) { result = result.toUpper(); } else if ( formatter == FMT_MODE_CABRILLO ) { // ADIF mode -> Cabrillo mode: CW, PH, FM, RY, DG if ( value == "CW" ) result = "CW"; else if ( value == "FM" ) result = "FM"; else if ( value == "RTTY" ) result = "RY"; else { QString dxcc = BandPlan::modeToDXCCModeGroup(value); result = ( dxcc == "PHONE" ) ? "PH" : "DG"; } } else if ( formatter == FMT_PADDED_NR ) { bool ok; unsigned long nr = value.toLongLong(&ok); result = (ok) ? QString::number(nr).rightJustified(3, '0') : ""; } } // Pad or truncate to exactly 'width' characters (left-aligned) if ( width > 0 ) result = result.leftJustified(width, ' ', true); qCDebug(runtime) << result; return result; } void CabrilloFormat::writeMultiLineField(const QString &key, const QString &value, int maxLines) { FCT_IDENTIFICATION; const QString prefix = key + ": "; const int maxContent = MAX_LINE_LENGTH - prefix.length(); const QStringList inputLines = value.split('\n'); int lineCount = 0; for ( const QString &inputLine : inputLines ) { if ( maxLines > 0 && lineCount >= maxLines ) break; const QString trimmed = inputLine.trimmed(); if ( trimmed.isEmpty() ) continue; if ( trimmed.length() <= maxContent ) { stream << prefix << trimmed << '\n'; ++lineCount; continue; } // Word-wrap long lines const QStringList words = trimmed.split(' ', #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) Qt::SkipEmptyParts); #else QString::SkipEmptyParts); #endif QString line; line.reserve(maxContent); for ( const QString &word : words ) { if ( maxLines > 0 && lineCount >= maxLines ) break; const int extra = line.isEmpty() ? word.length() : word.length() + 1; if ( !line.isEmpty() && line.length() + extra > maxContent ) { stream << prefix << line << '\n'; ++lineCount; line.clear(); } if ( !line.isEmpty() ) line += ' '; line += word; } if ( !line.isEmpty() && (maxLines <= 0 || lineCount < maxLines) ) { stream << prefix << line << '\n'; ++lineCount; } } } void CabrilloFormat::exportStart() { FCT_IDENTIFICATION; loadTemplate(); const QMap headerFields = buildHeaderFields(); stream << "START-OF-LOG: " + FORMAT_VERSION_STRING << "\n"; stream << "CREATED-BY: " + PROGRAMID_STRING << "\n"; if ( !contestName.isEmpty() ) stream << "CONTEST: " << contestName << "\n"; for ( const QString &key : headerOrder ) { if ( !headerFields.contains(key) ) continue; const QString &val = headerFields.value(key); if ( key == "CATEGORY-OPERATOR" && val == OPERATOR_MULTI ) multiOpEnabled = true; if ( key == "ADDRESS" ) writeMultiLineField(key, val, MAX_ADDRESS_LINES); else if ( key == "SOAPBOX" ) writeMultiLineField(key, val); else if ( !val.isEmpty() ) stream << key << ": " << val << "\n"; } } void CabrilloFormat::exportContact(const QSqlRecord &record, QMap *) { FCT_IDENTIFICATION; QString line = "QSO:"; for ( const ColumnDef &col : static_cast&>(columns) ) { if ( col.formatter == FMT_TRANSMITTER_ID ) { line += " " + ((multiOpEnabled) ? QString::number(transmitterId).leftJustified(col.width, ' ', true) : " "); continue; } int fieldIndex = record.indexOf(col.dbField); QString value; if ( fieldIndex >= 0 ) value = record.value(fieldIndex).toString(); line += " " + formatField(value, col.formatter, col.width); } qCDebug(runtime) << line; stream << line << "\n"; } void CabrilloFormat::exportEnd() { FCT_IDENTIFICATION; stream << "END-OF-LOG:" << "\n"; } ================================================ FILE: logformat/CabrilloFormat.h ================================================ #ifndef QLOG_LOGFORMAT_CABRILLOFORMAT_H #define QLOG_LOGFORMAT_CABRILLOFORMAT_H #include "LogFormat.h" #include #include class CabrilloFormat : public LogFormat { public: explicit CabrilloFormat(QTextStream &stream); struct HeaderData { QString callsign; QString operatorName; QString email; QString operators; QString address; QString gridLocator; QString categoryOperator; QString categoryBand; QString categoryPower; QString categoryMode; QString categoryAssisted; QString categoryStation; QString categoryTransmitter; QString categoryTime; QString categoryOverlay; QString location; QString club; QString offtime; QString soapbox; }; void setTemplateId(int id); void setHeaderData(const HeaderData &data); void setTransmitterId(int id); void exportStart() override; void exportContact(const QSqlRecord &record, QMap *appliedMap = nullptr) override; void exportEnd() override; struct TemplateInfo { int id; QString name; QString contestName; QString defaultCategoryMode; }; struct ColumnDef { int position; QString dbField; int width; QString formatter; QString label; }; struct CategoryItem { QString value; QString label; }; static QList templateList(); static TemplateInfo templateInfo(int templateId); static QList loadTemplateColumns(int templateId); static QString formatField(const QString &value, const QString &formatter, int width); static QList bandCategories(); static QList modeCategories(); static QList powerCategories(); static QList operatorCategories(); static QList assistedCategories(); static QList stationCategories(); static QList transmitterCategories(); static QList timeCategories(); static QList overlayCategories(); static QList formatterTypes(); // Formatter type identifiers static const QString FMT_NONE; static const QString FMT_FREQ_KHZ; static const QString FMT_TIME_HHMM; static const QString FMT_DATE_YYYY_MM_DD; static const QString FMT_RST_SHORT; static const QString FMT_UPPER; static const QString FMT_MODE_CABRILLO; static const QString FMT_TRANSMITTER_ID; static const QString FMT_PADDED_NR; // Band category values static const QString BAND_ALL; static const QString BAND_160M; static const QString BAND_80M; static const QString BAND_40M; static const QString BAND_20M; static const QString BAND_15M; static const QString BAND_10M; static const QString BAND_6M; static const QString BAND_4M; static const QString BAND_2M; static const QString BAND_222; static const QString BAND_432; static const QString BAND_902; static const QString BAND_1_2G; static const QString BAND_LIGHT; static const QString BAND_VHF_3_BAND; static const QString BAND_VHF_FM_ONLY; // Mode category values static const QString MODE_CW; static const QString MODE_SSB; static const QString MODE_RTTY; static const QString MODE_FM; static const QString MODE_DIGI; static const QString MODE_MIXED; // Power category values static const QString POWER_HIGH; static const QString POWER_LOW; static const QString POWER_QRP; // Operator category values static const QString OPERATOR_SINGLE; static const QString OPERATOR_MULTI; static const QString OPERATOR_CHECKLOG; // Assisted category values static const QString ASSISTED_NO; static const QString ASSISTED_YES; // Station category values static const QString STATION_FIXED; static const QString STATION_MOBILE; static const QString STATION_PORTABLE; static const QString STATION_ROVER; static const QString STATION_ROVER_LIMITED; static const QString STATION_ROVER_UNLIMITED; static const QString STATION_EXPEDITION; static const QString STATION_HQ; static const QString STATION_SCHOOL; static const QString STATION_DISTRIBUTED; // Transmitter category values static const QString TRANSMITTER_ONE; static const QString TRANSMITTER_TWO; static const QString TRANSMITTER_LIMITED; static const QString TRANSMITTER_UNLIMITED; static const QString TRANSMITTER_SWL; // Time category values static const QString TIME_6_HOURS; static const QString TIME_12_HOURS; static const QString TIME_24_HOURS; // Overlay category values static const QString OVERLAY_CLASSIC; static const QString OVERLAY_ROOKIE; static const QString OVERLAY_TB_WIRES; static const QString OVERLAY_NOVICE_TECH; static const QString OVERLAY_OVER_50; const QString FORMAT_VERSION_STRING = "3.0"; const QString PROGRAMID_STRING = "QLog"; private: static const int MAX_LINE_LENGTH = 75; static const int MAX_ADDRESS_LINES = 6; void loadTemplate(); void writeMultiLineField(const QString &key, const QString &value, int maxLines = 0); QMap buildHeaderFields() const; int templateId; int transmitterId; HeaderData headerData; QList columns; QString contestName; bool multiOpEnabled; const QStringList headerOrder = { "CALLSIGN", "NAME", "EMAIL", "OPERATORS", "ADDRESS", "GRID-LOCATOR", "CATEGORY-OPERATOR", "CATEGORY-BAND", "CATEGORY-POWER", "CATEGORY-MODE", "CATEGORY-ASSISTED", "CATEGORY-STATION", "CATEGORY-TRANSMITTER", "CATEGORY-TIME", "CATEGORY-OVERLAY", "CLUB", "OFFTIME", "SOAPBOX" }; }; #endif // QLOG_LOGFORMAT_CABRILLOFORMAT_H ================================================ FILE: logformat/JsonFormat.cpp ================================================ #include #include #include "JsonFormat.h" #include "core/debug.h" MODULE_IDENTIFICATION("qlog.logformat.jsonformat"); void JsonFormat::exportStart() { FCT_IDENTIFICATION; data = QJsonArray(); } void JsonFormat::exportContact(const QSqlRecord& record, QMap*applTags) { FCT_IDENTIFICATION; qCDebug(function_parameters)< #include "AdxFormat.h" class JsonFormat : public AdxFormat { public: explicit JsonFormat(QTextStream& stream) : AdxFormat(stream) {} virtual bool importNext(QSqlRecord& contact) override; virtual void importStart() override {}; virtual void importEnd() override {}; virtual void exportStart() override; virtual void exportContact(const QSqlRecord& record, QMap *) override; virtual void exportEnd() override; protected: virtual void writeField(const QString &name, bool presenceCondition, const QString &value, const QString &type="") override; private: QJsonArray data; QJsonObject contact; }; #endif // QLOG_LOGFORMAT_JSONFORMAT_H ================================================ FILE: logformat/LogFormat.cpp ================================================ #include #include #include "LogFormat.h" #include "AdiFormat.h" #include "AdxFormat.h" #include "PotaAdiFormat.h" #include "JsonFormat.h" #include "CSVFormat.h" #include "CabrilloFormat.h" #include "data/Data.h" #include "core/debug.h" #include "data/Gridsquare.h" #include "data/Callsign.h" #include "data/BandPlan.h" #include "service/lotw/Lotw.h" #include "models/LogbookModel.h" #include "core/QSOFilterManager.h" MODULE_IDENTIFICATION("qlog.logformat.logformat"); LogFormat::LogFormat(QTextStream& stream) : QObject(nullptr), stream(stream), exportedFields("*"), duplicateQSOFunc(nullptr) { FCT_IDENTIFICATION; this->defaults = nullptr; } LogFormat::~LogFormat() { FCT_IDENTIFICATION; } LogFormat* LogFormat::open(QString type, QTextStream& stream) { FCT_IDENTIFICATION; qCDebug(function_parameters)<& defaults) { FCT_IDENTIFICATION; this->defaults = &defaults; } void LogFormat::setFilterDateRange(const QDate &start, const QDate &end) { FCT_IDENTIFICATION; qCDebug(function_parameters)<filterStartDate = start; this->filterEndDate = end; } void LogFormat::setFilterMyCallsign(const QString &myCallsing) { FCT_IDENTIFICATION; qCDebug(function_parameters) << myCallsing; this->filterMyCallsign = myCallsing; } void LogFormat::setFilterMyGridsquare(const QString &myGridsquare) { FCT_IDENTIFICATION; qCDebug(function_parameters) << myGridsquare; this->filterMyGridsquare = myGridsquare; } void LogFormat::setFilterSentPaperQSL(bool includeNo, bool includeIgnore, bool includeAlreadySent) { FCT_IDENTIFICATION; qCDebug(function_parameters) << includeNo << includeIgnore << includeAlreadySent; this->filterSentPaperQSL << "'R'" << "'Q'"; if ( includeNo ) { this->filterSentPaperQSL << "'N'"; } if ( includeIgnore ) { this->filterSentPaperQSL << "'I'"; } if ( includeAlreadySent ) { this->filterSentPaperQSL << "'Y'"; } } void LogFormat::setFilterSendVia(const QString &value) { FCT_IDENTIFICATION; qCDebug(function_parameters) << value; this->filterSendVia = value; } void LogFormat::setUserFilter(const QString &value) { FCT_IDENTIFICATION; userFilter = value; } void LogFormat::setFilterStationProfile(const StationProfile &profile) { FCT_IDENTIFICATION; filterStationProfile = profile; filterStationProfileSet = true; } void LogFormat::setPotaOnly(bool only) { FCT_IDENTIFICATION; filterPOTAOnly = only; } QString LogFormat::getWhereClause() { FCT_IDENTIFICATION; whereClause.clear(); whereClause << "1 = 1"; //generic filter if ( isDateRange() ) whereClause << "(datetime(start_time) BETWEEN datetime(:start_date) AND datetime(:end_date))"; if ( !filterMyCallsign.isEmpty() ) whereClause << "upper(station_callsign) = upper(:stationCallsign)"; if ( !filterMyGridsquare.isEmpty() ) whereClause << "upper(my_gridsquare) = upper(:myGridsquare)"; if ( !filterSentPaperQSL.isEmpty() ) whereClause << QString("upper(qsl_sent) in (%1)").arg(filterSentPaperQSL.join(", ")); if ( !filterSendVia.isEmpty() ) whereClause << ( ( filterSendVia == " " ) ? "qsl_sent_via is NULL" : "upper(qsl_sent_via) = upper(:qsl_sent_via)"); if ( filterPOTAOnly ) whereClause << QLatin1String("(my_pota_ref is not NULL OR pota_ref is not NULL OR lower(sig)='pota' OR lower(my_sig)='pota')"); if ( filterStationProfileSet ) { whereClause << QString("EXISTS (" "SELECT 1 FROM station_profiles" " WHERE station_profiles.profile_name = :stationProfileName" " AND %1" ")").arg(filterStationProfile.getContactInnerJoin()); } if ( !userFilter.isEmpty() ) whereClause << QSOFilterManager::getWhereClause(userFilter); return whereClause.join(" AND "); } void LogFormat::bindWhereClause(QSqlQuery &query) { FCT_IDENTIFICATION; if ( isDateRange() ) { #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) query.bindValue(":start_date", filterStartDate.startOfDay()); query.bindValue(":end_date", filterEndDate.endOfDay()); #else /* Due to ubuntu 20.04 where qt5.12 is present */ query.bindValue(":start_date", QDateTime(filterStartDate)); query.bindValue(":end_date", QDateTime(filterEndDate)); #endif } if ( !filterMyCallsign.isEmpty() ) { query.bindValue(":stationCallsign", filterMyCallsign); } if ( !filterMyGridsquare.isEmpty() ) { query.bindValue(":myGridsquare", filterMyGridsquare); } if ( !filterSendVia.isEmpty() ) { if ( filterSendVia != " " ) { query.bindValue(":qsl_sent_via", filterSendVia); } } if ( filterStationProfileSet ) { query.bindValue(":stationProfileName", filterStationProfile.profileName); } } void LogFormat::setExportedFields(const QStringList &fieldsList) { FCT_IDENTIFICATION; qCDebug(function_parameters) << fieldsList; exportedFields = fieldsList; } void LogFormat::setFillMissingDxcc(bool fillMissingDxcc) { FCT_IDENTIFICATION; qCDebug(function_parameters)<fillMissingDxcc = fillMissingDxcc; } void LogFormat::setDuplicateQSOCallback(duplicateQSOBehaviour (*func)(QSqlRecord *, QSqlRecord *)) { FCT_IDENTIFICATION; duplicateQSOFunc = func; } #define RECORDIDX(a) ( (a) - 1 ) unsigned long LogFormat::runImport(QTextStream& importLogStream, const StationProfile *defaultStationProfile, unsigned long *warnings, unsigned long *errors) { FCT_IDENTIFICATION; this->importStart(); unsigned long count = 0L; *errors = 0L; *warnings = 0L; unsigned long processedRec = 0; QSqlQuery dupQuery; QSqlQuery insertQuery; // It is important to use callsign index here if ( ! dupQuery.prepare("SELECT * FROM contacts " "WHERE callsign=upper(:callsign) " "AND upper(mode)=upper(:mode) " "AND upper(band)=upper(:band) " "AND COALESCE(sat_name, '') = COALESCE(:sat_name, '') " "AND ABS(JULIANDAY(start_time)-JULIANDAY(datetime(:startdate)))*24*60<30") ) { qWarning() << "cannot prepare Dup statement"; return 0; } QSqlDatabase::database().transaction(); QSqlTableModel model; model.setTable("contacts"); model.removeColumn(model.fieldIndex("id")); QSqlRecord record = model.record(); duplicateQSOBehaviour dupSetting = LogFormat::ASK_NEXT; auto setIfEmpty = [&](int column, const QString &value) { if ( record.value(RECORDIDX(column)).toString().isEmpty() && !value.isEmpty() ) record.setValue(RECORDIDX(column), value); }; auto setMyDefaultProfile = [&]() { setIfEmpty(LogbookModel::COLUMN_MY_DXCC, QString::number(defaultStationProfile->dxcc)); setIfEmpty(LogbookModel::COLUMN_STATION_CALLSIGN, defaultStationProfile->callsign); setIfEmpty(LogbookModel::COLUMN_MY_GRIDSQUARE, defaultStationProfile->locator); setIfEmpty(LogbookModel::COLUMN_MY_NAME, Data::removeAccents(defaultStationProfile->operatorName)); setIfEmpty(LogbookModel::COLUMN_MY_NAME_INTL, defaultStationProfile->operatorName); setIfEmpty(LogbookModel::COLUMN_OPERATOR, defaultStationProfile->operatorCallsign); setIfEmpty(LogbookModel::COLUMN_MY_CITY_INTL, defaultStationProfile->qthName); setIfEmpty(LogbookModel::COLUMN_MY_CITY, Data::removeAccents(defaultStationProfile->qthName)); setIfEmpty(LogbookModel::COLUMN_MY_IOTA, defaultStationProfile->iota); setIfEmpty(LogbookModel::COLUMN_MY_POTA_REF, defaultStationProfile->pota); setIfEmpty(LogbookModel::COLUMN_MY_SOTA_REF, defaultStationProfile->sota); setIfEmpty(LogbookModel::COLUMN_MY_SIG_INTL, defaultStationProfile->sig); setIfEmpty(LogbookModel::COLUMN_MY_SIG, Data::removeAccents(defaultStationProfile->sig)); setIfEmpty(LogbookModel::COLUMN_MY_SIG_INFO_INTL, defaultStationProfile->sigInfo); setIfEmpty(LogbookModel::COLUMN_MY_SIG_INFO, Data::removeAccents(defaultStationProfile->sigInfo)); setIfEmpty(LogbookModel::COLUMN_MY_VUCC_GRIDS, defaultStationProfile->vucc); setIfEmpty(LogbookModel::COLUMN_MY_ITU_ZONE, QString::number(defaultStationProfile->ituz)); setIfEmpty(LogbookModel::COLUMN_MY_CQ_ZONE, QString::number(defaultStationProfile->cqz)); setIfEmpty(LogbookModel::COLUMN_MY_COUNTRY_INTL, defaultStationProfile->country); setIfEmpty(LogbookModel::COLUMN_MY_COUNTRY, Data::removeAccents(defaultStationProfile->country)); setIfEmpty(LogbookModel::COLUMN_MY_CNTY, Data::removeAccents(defaultStationProfile->county)); setIfEmpty(LogbookModel::COLUMN_MY_DARC_DOK, Data::removeAccents(defaultStationProfile->darcDOK)); }; auto setMyEntity = [&](const DxccEntity &myEntity) { // force overwrite record.setValue(RECORDIDX(LogbookModel::COLUMN_MY_DXCC), myEntity.dxcc); record.setValue(RECORDIDX(LogbookModel::COLUMN_MY_COUNTRY), Data::removeAccents(myEntity.country)); record.setValue(RECORDIDX(LogbookModel::COLUMN_MY_COUNTRY_INTL), myEntity.country); // other DXCC related values ​​are not closely related to DXCC value and could have been filled // therefore check if it is present or not. setIfEmpty(LogbookModel::COLUMN_MY_ITU_ZONE, QString::number(myEntity.ituz)); setIfEmpty(LogbookModel::COLUMN_MY_CQ_ZONE, QString::number(myEntity.cqz)); }; auto lookupAndSetMyEntityByCallsign = [&] (const QString& recordMyDXCC) { const DxccEntity &myEntity = Data::instance()->lookupDxcc(recordMyDXCC); if ( myEntity.dxcc == 0 ) // My DXCC not found { writeImportLog(importLogStream, WARNING_SEVERITY, errors, warnings, processedRec, record, tr("Cannot find My DXCC Entity Info")); } else { setMyEntity(myEntity); } }; if ( !insertQuery.prepare( QSqlDatabase::database().driver()->sqlStatement(QSqlDriver::InsertStatement, "contacts", record, true)) ) { qWarning() << "cannot prepare Insert statement" << insertQuery.lastError(); return 0; } while (true) { record.clearValues(); if (!this->importNext(record)) break; processedRec++; /* Compute the Band if missing * Band is one of the mandatory fields */ if ( record.value(RECORDIDX(LogbookModel::COLUMN_BAND)).toString().isEmpty() && !record.value(RECORDIDX(LogbookModel::COLUMN_FREQUENCY)).toString().isEmpty() ) { double freq = record.value(RECORDIDX(LogbookModel::COLUMN_FREQUENCY)).toDouble(); record.setValue(RECORDIDX(LogbookModel::COLUMN_BAND), BandPlan::freq2Band(freq).name); } // needed later const QVariant &call = record.value(RECORDIDX(LogbookModel::COLUMN_CALL)); const QVariant &mycall = record.value(RECORDIDX(LogbookModel::COLUMN_STATION_CALLSIGN)); const QVariant &band = record.value(RECORDIDX(LogbookModel::COLUMN_BAND)); const QVariant &mode = record.value(RECORDIDX(LogbookModel::COLUMN_MODE)); const QDateTime &start_time = record.value(RECORDIDX(LogbookModel::COLUMN_TIME_ON)).toDateTime(); const QVariant &sota = record.value(RECORDIDX(LogbookModel::COLUMN_SOTA_REF)); const QVariant &mysota = record.value(RECORDIDX(LogbookModel::COLUMN_MY_SOTA_REF)); const QVariant &satName = record.value(RECORDIDX(LogbookModel::COLUMN_SAT_NAME)); /* checking matching fields if they are not empty */ if ( !start_time.isValid() || call.toString().isEmpty() || band.toString().isEmpty() || mode.toString().isEmpty()) { writeImportLog(importLogStream, ERROR_SEVERITY, errors, warnings, processedRec, record, tr("A minimal set of fields not present (start_time, call, band, mode, station_callsign)")); qWarning() << "Import does not contain minimal set of fields (start_time, call, band, mode, station_callsign)"; qCDebug(runtime) << record; continue; } if ( processedRec % 1000 == 0) { emit importPosition(stream.pos()); } if ( isDateRange() ) { if (!inDateRange(start_time.date())) { writeImportLog(importLogStream, WARNING_SEVERITY, errors, warnings, processedRec, record, tr("Outside the selected Date Range")); continue; } } if ( dupSetting != ACCEPT_ALL ) { dupQuery.bindValue(":callsign", call); dupQuery.bindValue(":mode", mode); dupQuery.bindValue(":band", band); dupQuery.bindValue(":startdate", start_time.toTimeZone(QTimeZone::utc()).toString("yyyy-MM-dd hh:mm:ss")); dupQuery.bindValue(":sat_name", satName); if ( !dupQuery.exec() ) { qWarning() << "Cannot exect DUP statement"; } if ( dupQuery.next() ) { if ( dupSetting == SKIP_ALL) { writeImportLog(importLogStream, WARNING_SEVERITY, errors, warnings, processedRec, record, tr("Duplicate")); continue; } /* Duplicate QSO found */ if ( duplicateQSOFunc ) { QSqlRecord dupRecord; dupRecord= dupQuery.record(); dupSetting = duplicateQSOFunc(&record, &dupRecord); } switch ( dupSetting ) { case ACCEPT_ALL: case ACCEPT_ONE: case ASK_NEXT: break; case SKIP_ONE: case SKIP_ALL: writeImportLog(importLogStream, WARNING_SEVERITY, errors, warnings, processedRec, record, tr("Duplicate")); continue; break; } } } /* Adding information which are important for QLog or QLog knows/compute them */ /************************/ /* Add DXCC Entity Info */ /************************/ const int recordDXCCId = record.value(RECORDIDX(LogbookModel::COLUMN_DXCC)).toInt(); // 0 = NaN or not present // otherwise = DXCC ID DxccEntity entity; if ( recordDXCCId != 0 ) { // get additional DXCC info entity = Data::instance()->lookupDxccIDAD1C(recordDXCCId); // if DXCC is not present on AD1C list, try Clublog, no CQZ and ITUZ info here if ( entity.dxcc == 0 ) entity = Data::instance()->lookupDxccIDClublog(recordDXCCId); // no record in clublog, only Warning, because DXCC number is present if ( entity.dxcc == 0 ) writeImportLog(importLogStream, WARNING_SEVERITY, errors, warnings, processedRec, record, tr("DXCC Info is missing")); } else if ( !fillMissingDxcc ) { // DXCC info is missing in the source and fillMissingDXCC is disabled - ERROR - QLog needs DXCC writeImportLog(importLogStream, ERROR_SEVERITY, errors, warnings, processedRec, record, tr("DXCC Info is missing")); continue; } else { // DXCC info is missing in the source and fillMissingDXCC is enabled, try to find on AD1C list entity = Data::instance()->lookupDxccAD1C(call.toString()); if ( entity.dxcc == 0 ) // if DXCC is not present on AD1C list, try Clublog, no CQZ and ITUZ info here entity = Data::instance()->lookupDxccClublog(call.toString(), start_time); if ( entity.dxcc == 0 ) { // no record in clublog - ERROR - QLog needs DXCC writeImportLog(importLogStream, ERROR_SEVERITY, errors, warnings, processedRec, record, tr("DXCC Info is missing")); continue; } } if ( entity.dxcc != 0 ) { record.setValue(RECORDIDX(LogbookModel::COLUMN_DXCC), entity.dxcc); record.setValue(RECORDIDX(LogbookModel::COLUMN_COUNTRY), Data::removeAccents(entity.country)); record.setValue(RECORDIDX(LogbookModel::COLUMN_COUNTRY_INTL), entity.country); // other DXCC related values ​​are not closely related to DXCC value and could have been filled // therefore check if it is present or not. setIfEmpty(LogbookModel::COLUMN_CONTINENT, entity.cont); setIfEmpty(LogbookModel::COLUMN_ITUZ, QString::number(entity.ituz)); setIfEmpty(LogbookModel::COLUMN_CQZ, QString::number(entity.cqz)); }; /************************/ /* Add My Station Info */ /************************/ int recordMyDXCCId = record.value(RECORDIDX(LogbookModel::COLUMN_MY_DXCC)).toInt(); // 0 = NAN or not present // otherwise = DXCC ID const QString &myCallString = mycall.toString(); if ( defaultStationProfile ) { // default is enabled // Case 1: Both recordMyDXCCId and myCallString are empty if ( recordMyDXCCId == 0 && myCallString.isEmpty() ) { setMyDefaultProfile(); } // Case 2: recordMyDXCCId is empty, myCallString is not else if ( recordMyDXCCId == 0 ) { if ( defaultStationProfile->callsign == myCallString ) setMyDefaultProfile(); else lookupAndSetMyEntityByCallsign(myCallString); } // Case 3: myCallString is empty, recordMyDXCCId is not else if ( myCallString.isEmpty() ) { if ( defaultStationProfile->dxcc == recordMyDXCCId ) setMyDefaultProfile(); else { // no Station Callsign = ERROR writeImportLog(importLogStream, ERROR_SEVERITY, errors, warnings, processedRec, record, tr("no Station Callsign present")); continue; } } // Case 4: Both recordMyDXCCId and myCallString are not empty else { if ( defaultStationProfile->callsign == myCallString ) { if ( defaultStationProfile->dxcc == recordMyDXCCId ) setMyDefaultProfile(); else { // no Station Callsign = ERROR writeImportLog(importLogStream, ERROR_SEVERITY, errors, warnings, processedRec, record, tr("no Station Callsign present")); continue; } } else lookupAndSetMyEntityByCallsign(myCallString); } } else { // default is disabled if ( myCallString.isEmpty() ) { // no Station Callsign = ERROR writeImportLog(importLogStream, ERROR_SEVERITY, errors, warnings, processedRec, record, tr("no Station Callsign present")); continue; } else { const DxccEntity &myEntity = ( recordMyDXCCId != 0 ) ? Data::instance()->lookupDxccID(recordMyDXCCId) : Data::instance()->lookupDxcc(myCallString); if ( myEntity.dxcc == 0 ) // My DXCC not found { writeImportLog(importLogStream, WARNING_SEVERITY, errors, warnings, processedRec, record, tr("Cannot find My DXCC Entity Info")); } else setMyEntity(myEntity); } } /***********/ /* Add PFX */ /***********/ if ( record.value(RECORDIDX(LogbookModel::COLUMN_PREFIX)).toString().isEmpty() ) { const QString &pfxRef = Callsign(call.toString()).getWPXPrefix(); if ( !pfxRef.isEmpty() ) { record.setValue(RECORDIDX(LogbookModel::COLUMN_PREFIX), pfxRef); } } /********************/ /* Compute Distance */ /********************/ const QString &gridsquare = record.value(RECORDIDX(LogbookModel::COLUMN_GRID)).toString(); const QString &my_gridsquare = record.value(RECORDIDX(LogbookModel::COLUMN_MY_GRIDSQUARE)).toString(); if ( !gridsquare.isEmpty() && !my_gridsquare.isEmpty() && record.value(RECORDIDX(LogbookModel::COLUMN_DISTANCE)).toString().isEmpty() ) { const Gridsquare grid(gridsquare); const Gridsquare my_grid(my_gridsquare); double distance; if ( my_grid.distanceTo(grid, distance) ) { record.setValue(RECORDIDX(LogbookModel::COLUMN_DISTANCE), distance); } } /*************************/ /* Compute Alt from SOTA */ /*************************/ if ( record.value(RECORDIDX(LogbookModel::COLUMN_ALTITUDE)).toString().isEmpty() && !sota.toString().isEmpty() ) { const SOTAEntity &sotaInfo = Data::instance()->lookupSOTA(sota.toString()); if ( sotaInfo.summitCode.compare(sota.toString(), Qt::CaseInsensitive) && !sotaInfo.summitName.isEmpty() ) { record.setValue(RECORDIDX(LogbookModel::COLUMN_ALTITUDE),sotaInfo.altm); } } /*******************************/ /* Compute My Alt from My SOTA */ /*******************************/ if ( record.value(RECORDIDX(LogbookModel::COLUMN_MY_ALTITUDE)).toString().isEmpty() && !mysota.toString().isEmpty() ) { const SOTAEntity &sotaInfo = Data::instance()->lookupSOTA(mysota.toString()); if ( sotaInfo.summitCode.compare(sota.toString(), Qt::CaseInsensitive) && !sotaInfo.summitName.isEmpty() ) { record.setValue(RECORDIDX(LogbookModel::COLUMN_MY_ALTITUDE),sotaInfo.altm); } } /******************/ /* PREPARE INSERT */ /******************/ // Bind all values for ( int i = 0; i < record.count(); i++ ) { insertQuery.bindValue(i, record.value(i)); } if ( ! insertQuery.exec() ) { writeImportLog(importLogStream, ERROR_SEVERITY, errors, warnings, processedRec, record, tr("Cannot insert to database") + " - " + insertQuery.lastError().text()); qWarning() << "Cannot insert a record to Contact Table - " << insertQuery.lastError(); qCDebug(runtime) << record; } else { writeImportLog(importLogStream, INFO_SEVERITY, errors, warnings, processedRec, record, tr("Imported")); count++; } } emit importPosition(stream.pos()); emit finished(count); QSqlDatabase::database().commit(); this->importEnd(); return count; } #undef RECORDIDX void LogFormat::runQSLImport(QSLFrom fromService) { FCT_IDENTIFICATION; auto reportFormatter = [&](const QDateTime &qsoTime, const QString &callsign, const QString &mode, const QStringList addInfo = QStringList()) { return QString("%0; %1; %2%3 %4").arg(qsoTime.isValid() ? qsoTime.toString(locale.formatDateShortWithYYYY()) : "-", callsign, mode, (addInfo.size() > 0 ) ? ";" : "", addInfo.join(", ")); }; static QRegularExpression reLeadingZero("^0+"); QSLMergeStat stats = {QStringList(), QStringList(), QStringList(), QStringList(), 0}; this->importStart(); QSqlTableModel model; model.setTable("contacts"); QSqlRecord QSLRecord = model.record(0); // Cache for mode to dxcc group lookups; avoids repeated DB queries for the same // mode value when processing large imports (LoTW fallback path only). QMap modeGroupCache; while ( true ) { QSLRecord.clearValues(); if ( !this->importNext(QSLRecord) ) break; stats.qsosDownloaded++; if ( stats.qsosDownloaded % 100 == 0 ) { emit importPosition(stream.pos()); } // needed later const QVariant &call = QSLRecord.value("callsign"); const QVariant &band = QSLRecord.value("band"); const QVariant &mode = QSLRecord.value("mode"); const QVariant &start_time = QSLRecord.value("start_time"); const QVariant &satName = QSLRecord.value("sat_name"); /* checking matching fields if they are not empty */ if ( !start_time.toDateTime().isValid() || call.toString().isEmpty() || band.toString().isEmpty() || mode.toString().isEmpty() ) { qWarning() << "Import does not contain field start_time or callsign or band or mode "; qCDebug(runtime) << QSLRecord; stats.errorQSLs.append(reportFormatter(start_time.toDateTime(), call.toString(), mode.toString())); continue; } const QString startTimeStr = start_time.toDateTime().toTimeZone(QTimeZone::utc()).toString("yyyy-MM-dd hh:mm:ss"); // Common filter conditions — shared by all match attempts const QString baseFilter = QString( "callsign=upper('%1') AND upper(band)=upper('%2') AND " "COALESCE(sat_name, '') = upper('%3') AND " "ABS(JULIANDAY(start_time)-JULIANDAY(datetime('%4')))*24*60<30" ).arg(call.toString(), band.toString(), satName.toString(), startTimeStr); // First attempt: exact mode match (used for eQSL; also the fast path for LoTW) model.setFilter(baseFilter + QString(" AND upper(mode)=upper('%1')").arg(mode.toString())); model.select(); // LoTW fallback: LoTW confirms QSOs when both sides specify modes in the same // group (CW / PHONE / DATA). // Two submitted descriptions of a QSO match if // https://lotw.arrl.org/lotw-help/frequently-asked-questions/#datamatch // your QSO description specifies a callsign that matches the Callsign Certificate specified by the Station Location your QSO partner used to digitally sign the QSO // your QSO partner's QSO description specifies a callsign that matches the Callsign Certificate specified by the Station Location you used to digitally sign the QSO // both QSO descriptions specify start times within 30 minutes of each other // both QSO descriptions specify the same band // both QSO descriptions specify the same mode (an exact mode match), or must specify modes belonging to the same Mode Group // for satellite QSOs, both QSO descriptions must specify the same satellite, and a propagation mode of SAT if ( model.rowCount() != 1 && fromService == LOTW ) { const QString modeStr = mode.toString(); QString dxccGroup = LotwBase::lotwGroupNameToDxcc(modeStr); if ( dxccGroup.isEmpty() ) { if ( !modeGroupCache.contains(modeStr) ) modeGroupCache.insert(modeStr, BandPlan::modeToDXCCModeGroup(modeStr)); dxccGroup = modeGroupCache.value(modeStr); } if ( !dxccGroup.isEmpty() ) { qCDebug(runtime) << "LoTW: mode group fallback" << mode << "->" << dxccGroup; model.setFilter(baseFilter + QString( " AND (SELECT dxcc FROM modes WHERE upper(name)=upper(mode) LIMIT 1) = '%1'" ).arg(dxccGroup)); model.select(); } } if ( model.rowCount() != 1 ) { stats.unmatchedQSLs.append(reportFormatter(start_time.toDateTime(), call.toString(), mode.toString())); continue; } /* we have one row for updating */ /* lets update it */ QSqlRecord originalRecord = model.record(0); switch ( fromService ) { case LOTW: { /* https://lotw.arrl.org/lotw-help/developer-query-qsos-qsls/?lang=en */ // always try to update contact from received QSL if ( QSLRecord.value("qsl_rcvd").toString() == 'Y' ) // qsl_rcvd is OK because LoTW sends lotw_qsl_rcvd value in qsl_rcvd { QStringList updatedFields; bool callUpdate = false; bool newlyReceived = (QSLRecord.value("qsl_rcvd").toString() != originalRecord.value("lotw_qsl_rcvd").toString()); qCDebug(runtime) << "Attempt to" << (newlyReceived ? "force " : "") << "update QSO" << call.toString() << band.toString() << start_time.toString(); auto conditionUpdate = [&](const QString &contactKey, const QString &qslKey, bool forceUpdate = false) { if ( !QSLRecord.value(qslKey).toString().isEmpty() && ( forceUpdate || originalRecord.value(contactKey).toString().isEmpty() ) ) { qCDebug(runtime) << "Updating:" << contactKey << "to" << QSLRecord.value(qslKey).toString() << (forceUpdate ? "force update" : ""); updatedFields.append(contactKey + "(" + QSLRecord.value(qslKey).toString() +")"); originalRecord.setValue(contactKey, QSLRecord.value(qslKey)); return true; } return false; }; auto conditionUpdateSpecial = [&](const QString &contactKey, const QString &qslKey, bool forceUpdate = false) { QString contactValue = originalRecord.value(contactKey).toString(); QString QSLValue = QSLRecord.value(qslKey).toString(); contactValue.remove(reLeadingZero); QSLValue.remove(reLeadingZero); if ( !QSLValue.isEmpty() && ( forceUpdate || contactValue != QSLValue ) ) { qCDebug(runtime) << "Updating:" << contactKey << "from" << originalRecord.value(contactKey).toString() << "to" << QSLValue << (forceUpdate ? "force update" : ""); updatedFields.append(contactKey + "(" + QSLRecord.value(qslKey).toString() +")"); originalRecord.setValue(contactKey, QSLValue); return true; } return false; }; callUpdate |= conditionUpdate("lotw_qsl_rcvd", "qsl_rcvd", newlyReceived); callUpdate |= conditionUpdate("lotw_qslrdate", "qsl_rdate", newlyReceived); callUpdate |= conditionUpdate("credit_granted", "credit_granted", newlyReceived); callUpdate |= conditionUpdate("credit_submitted", "credit_submitted", newlyReceived); callUpdate |= conditionUpdate("pfx", "pfx", newlyReceived); callUpdate |= conditionUpdate("iota", "iota", newlyReceived); callUpdate |= conditionUpdate("vucc_grids", "vucc_grids", newlyReceived); callUpdate |= conditionUpdate("state", "state", newlyReceived); callUpdate |= conditionUpdate("cnty", "cnty", newlyReceived); callUpdate |= conditionUpdateSpecial("ituz", "ituz", newlyReceived); callUpdate |= conditionUpdateSpecial("cqz", "cqz", newlyReceived); if ( originalRecord.value("qsl_rcvd_via").toString() != "E" ) { qCDebug(runtime) << "Updating: qsl_rcvd_via from" << originalRecord.value("qsl_rcvd_via").toString() << "to E"; originalRecord.setValue("qsl_rcvd_via", "E"); updatedFields.append("qsl_rcvd_via (E)"); callUpdate |= true; } const QString origGrig = originalRecord.value("gridsquare").toString(); const Gridsquare dxNewGrid(QSLRecord.value("gridsquare").toString()); if ( ( newlyReceived || origGrig.isEmpty() || ( origGrig.length() < QSLRecord.value("gridsquare").toString().length() && dxNewGrid.isValid() && dxNewGrid.getGrid().contains(origGrig) ) ) && !dxNewGrid.getGrid().isEmpty() ) { const Gridsquare myGrid(originalRecord.value("my_gridsquare").toString()); originalRecord.setValue("gridsquare", dxNewGrid.getGrid()); double distance; if ( myGrid.distanceTo(dxNewGrid, distance) ) { originalRecord.setValue("distance", QVariant(distance)); } qCDebug(runtime) << "Updating: grid from " << origGrig << "to" << dxNewGrid.getGrid(); updatedFields.append("gridsquare (" + dxNewGrid.getGrid() +")"); callUpdate |= true; } if ( callUpdate ) { qCDebug(runtime) << "Calling update for" << call << band << mode << start_time << satName; if ( !model.setRecord(0, originalRecord) ) { qWarning() << "Cannot update a Contact record - " << model.lastError(); qCDebug(runtime) << originalRecord; } if ( !model.submitAll() ) { qWarning() << "Cannot commit changes to Contact Table - " << model.lastError(); } if ( newlyReceived ) { const DxccStatus status = Data::instance()->dxccStatus(originalRecord.value("dxcc").toInt(), band.toString(), mode.toString()); stats.newQSLs.append(reportFormatter(start_time.toDateTime(), call.toString(), mode.toString(), {tr("DXCC State:") + " " + Data::statusToText(status)})); } else stats.updatedQSOs.append(reportFormatter(start_time.toDateTime(), call.toString(), mode.toString(), updatedFields)); } } break; } case EQSL: { /* http://www.eqsl.cc/qslcard/DownloadInBox.txt */ /* CALL QSO_DATE TIME_ON BAND MODE SUBMODE (tag only present if non-blank) PROP_MODE (tag only present if non-blank) RST_SENT (will be the sender's RST Sent, not yours) RST_RCVD (we do not capture this in uploads, so will normally be 0 length) QSL_SENT (always Y) QSL_SENT_VIA (always E) QSLMSG (if non-null and containing only valid printable ASCII characters) QSLMSG_INTL (if non-null and containing international characters - see ADIF V3 specs) APP_EQSL_SWL (tag only present if sender is SWL and then always Y) APP_EQSL_AG (tag only present if sender has Authenticity Guaranteed status and then always Y) GRIDSQUARE (tag only present if non-blank and at least 4 long) */ // LF: Since I consider this source unreliable, I will not update it here, as I do with LoTW // try to update contact from received QSL only in case when contact != Y if ( originalRecord.value("eqsl_qsl_rcvd").toString() != 'Y' ) { originalRecord.setValue("eqsl_qsl_rcvd", QSLRecord.value("qsl_sent")); originalRecord.setValue("eqsl_qslrdate", QDateTime::currentDateTimeUtc().date().toString("yyyy-MM-dd")); Gridsquare dxNewGrid(QSLRecord.value("gridsquare").toString()); if ( dxNewGrid.isValid() && ( originalRecord.value("gridsquare").toString().isEmpty() || dxNewGrid.getGrid().contains(originalRecord.value("gridsquare").toString())) ) { Gridsquare myGrid(originalRecord.value("my_gridsquare").toString()); originalRecord.setValue("gridsquare", dxNewGrid.getGrid()); double distance; if ( myGrid.distanceTo(dxNewGrid, distance) ) { originalRecord.setValue("distance", QVariant(distance)); } } const QString &prevValue = originalRecord.value("qslmsg_rcvd").toString(); const QString &newValue = QSLRecord.value("qslmsg").toString(); if ( !newValue.isEmpty() ) originalRecord.setValue("qslmsg_rcvd", prevValue.isEmpty() ? "eQSL: " + newValue : prevValue + "| eQSL: " + newValue); // temporary removed - ADIF 3.1.5 has no INTL equivalent for qslmsg_rcvd //originalRecord.setValue("qslmsg_int", QSLRecord.value("qslmsg_int")); originalRecord.setValue("qsl_rcvd_via", QSLRecord.value("qsl_sent_via")); /* * It appears that the life cycle of EQSL_AQ field is not fully understood * at the moment. Unfortunately, even on the ADIF forum there are differing opinions, * but I have gained the impression that the only authority that knows the correct * value of EQSL_AG is eQSL itself. Therefore, I believe that the value received from eQSL * should always be set here, even though the field’s value may vary at the time the QSL is received. */ originalRecord.setValue("eqsl_ag", QSLRecord.value("eqsl_ag")); if ( !model.setRecord(0, originalRecord) ) { qWarning() << "Cannot update a Contact record - " << model.lastError(); qCDebug(runtime) << originalRecord; } if ( !model.submitAll() ) { qWarning() << "Cannot commit changes to Contact Table - " << model.lastError(); } const DxccStatus status = Data::instance()->dxccStatus(originalRecord.value("dxcc").toInt(), band.toString(), mode.toString()); stats.newQSLs.append(reportFormatter(start_time.toDateTime(), call.toString(), mode.toString(), {tr("DXCC State:") + " " + Data::statusToText(status)})); } break; } default: qCDebug(runtime) << "Unknown QSL import"; } } emit importPosition(stream.pos()); this->importEnd(); emit QSLMergeFinished(stats); } long LogFormat::runExport() { FCT_IDENTIFICATION; this->exportStart(); QSqlQuery query; QString queryStmt = QString("SELECT %1 FROM contacts WHERE %2 ORDER BY start_time ASC").arg(exportedFields.join(", "), getWhereClause()); qCDebug(runtime) << queryStmt; if ( ! query.prepare(queryStmt) ) { qWarning() << "Cannot prepare select statement"; return 0; } bindWhereClause(query); if ( ! query.exec() ) { qWarning() << "Cannot execute select statement" << query.lastError(); return 0; } long count = 0L; /* following 3 lines are a workaround - SQLite does not * return a correct value for QSqlQuery.size */ int rows = (query.last()) ? query.at() + 1 : 0; query.first(); query.previous(); while (query.next()) { this->exportContact(query.record()); count++; if (count % 100 == 0) { emit exportProgress((int)(count * 100 / rows)); } } emit exportProgress(100); this->exportEnd(); return count; } long LogFormat::runExport(const QList &selectedQSOs) { FCT_IDENTIFICATION; this->exportStart(); long count = 0L; for (const QSqlRecord &qso: selectedQSOs) { QSqlRecord contactRecord; if ( exportedFields.first() != "*" ) { for ( const QString& fieldName : static_cast(exportedFields) ) { contactRecord.append(qso.field(fieldName)); } } else { contactRecord = qso; } this->exportContact(contactRecord); count++; if ( count % 10 == 0 ) { emit exportProgress((int)(count * 100 / selectedQSOs.size())); } } emit exportProgress(100); emit finished(count); this->exportEnd(); return count; } bool LogFormat::isDateRange() { FCT_IDENTIFICATION; return !filterStartDate.isNull() && !filterEndDate.isNull(); } bool LogFormat::inDateRange(QDate date) { FCT_IDENTIFICATION; qCDebug(function_parameters)<= filterStartDate && date <= filterEndDate; } QString LogFormat::importLogSeverityToString(ImportLogSeverity severity) { switch ( severity ) { case ERROR_SEVERITY: return tr("Error") + " - "; break; case WARNING_SEVERITY: return tr("Warning") + " - "; break; case INFO_SEVERITY: default: //NOTHING ; } return QString(); } void LogFormat::writeImportLog(QTextStream &errorLogStream, ImportLogSeverity severity, const QString &msg) { FCT_IDENTIFICATION; errorLogStream << importLogSeverityToString(severity) << msg << "\n"; } void LogFormat::writeImportLog(QTextStream& errorLogStream, ImportLogSeverity severity, unsigned long *error, unsigned long *warning, const unsigned long recordNo, const QSqlRecord &record, const QString &msg) { FCT_IDENTIFICATION; errorLogStream << QString("[QSO#%1]: ").arg(recordNo) << importLogSeverityToString(severity) << msg << QString(" (%1; %2; %3)").arg(record.value("start_time").toDateTime().toTimeZone(QTimeZone::utc()).toString(locale.formatDateShortWithYYYY()), record.value("callsign").toString(), record.value("mode").toString()) << "\n"; switch (severity) { case WARNING_SEVERITY: (*warning)++; break; case ERROR_SEVERITY: (*error)++; break; case INFO_SEVERITY: break; } } ================================================ FILE: logformat/LogFormat.h ================================================ #ifndef QLOG_LOGFORMAT_LOGFORMAT_H #define QLOG_LOGFORMAT_LOGFORMAT_H #include #include #include #include "core/LogLocale.h" #include "data/StationProfile.h" class QSqlRecord; struct QSLMergeStat { QStringList newQSLs; QStringList updatedQSOs; QStringList unmatchedQSLs; QStringList errorQSLs; int qsosDownloaded; }; class LogFormat : public QObject { Q_OBJECT public: enum Type { ADI, ADX, CABRILLO, JSON, CSV, POTA }; enum QSLFrom { LOTW, EQSL, UNKNOW }; enum duplicateQSOBehaviour { ASK_NEXT, SKIP_ONE, SKIP_ALL, ACCEPT_ONE, ACCEPT_ALL }; explicit LogFormat(QTextStream& stream); virtual ~LogFormat(); static LogFormat* open(QString type, QTextStream& stream); static LogFormat* open(Type type, QTextStream& stream); unsigned long runImport(QTextStream& importLogStream, const StationProfile *defaultStationProfile, unsigned long *warnings, unsigned long *errors); void runQSLImport(QSLFrom fromService); long runExport(); long runExport(const QList&); void setDefaults(QMap& defaults); void setFilterDateRange(const QDate &start, const QDate &end); void setFilterMyCallsign(const QString &myCallsing); void setFilterMyGridsquare(const QString &myGridsquare); void setFilterSentPaperQSL(bool includeNo, bool includeIgnore, bool includeAlreadySent); void setFilterSendVia(const QString &value); void setFilterStationProfile(const StationProfile &profile); void setUserFilter(const QString&value); void setPotaOnly(bool only); QString getWhereClause(); void bindWhereClause(QSqlQuery &); void setExportedFields(const QStringList& fieldsList); void setFillMissingDxcc(bool fillMissingDxcc); void setDuplicateQSOCallback(duplicateQSOBehaviour (*func)(QSqlRecord *, QSqlRecord *)); virtual void importStart() {} virtual void importEnd() {} virtual bool importNext(QSqlRecord&) { return false; } virtual void exportStart() {} virtual void exportEnd() {} virtual void exportContact(const QSqlRecord&, QMap * = nullptr) {} signals: void importPosition(qint64 value); void exportProgress(float value); void finished(int count); void QSLMergeFinished(QSLMergeStat stats); protected: QTextStream& stream; QMap* defaults; private: enum ImportLogSeverity { INFO_SEVERITY, WARNING_SEVERITY, ERROR_SEVERITY }; bool isDateRange(); bool inDateRange(QDate date); QString importLogSeverityToString(ImportLogSeverity); void writeImportLog(QTextStream& errorLogStream, ImportLogSeverity severity, const QString &msg); void writeImportLog(QTextStream& errorLogStream, ImportLogSeverity severity, unsigned long *error, unsigned long *warning, const unsigned long recordNo, const QSqlRecord &record, const QString &msg); QDate filterStartDate, filterEndDate; QString filterMyCallsign; QString filterMyGridsquare; QStringList filterSentPaperQSL; QString filterSendVia; StationProfile filterStationProfile; bool filterStationProfileSet = false; QStringList whereClause; QStringList exportedFields; QString userFilter; bool filterPOTAOnly = false; bool fillMissingDxcc = false; duplicateQSOBehaviour (*duplicateQSOFunc)(QSqlRecord *, QSqlRecord *); LogLocale locale; }; #endif // QLOG_LOGFORMAT_LOGFORMAT_H ================================================ FILE: logformat/PotaAdiFormat.cpp ================================================ #include "PotaAdiFormat.h" #include #include #include #include "core/debug.h" #include MODULE_IDENTIFICATION("qlog.logformat.potalogformat"); PotaAdiFormat::PotaAdiFormat(QTextStream &stream) : AdiFormat(stream), currentDate(QDateTime::currentDateTime()) { FCT_IDENTIFICATION; } void PotaAdiFormat::setExportDirectory(const QString &dir) { FCT_IDENTIFICATION; exportDir = dir; } void PotaAdiFormat::exportContact(const QSqlRecord &sourceRecord, QMap *applTags) { FCT_IDENTIFICATION; if ( exportDir.isEmpty() || !isValidPotaRecord(sourceRecord) ) return; QSqlRecord inputRecord(sourceRecord); preparePotaField(inputRecord, "my_pota_ref", "my_sig_info", "my_sig"); preparePotaField(inputRecord, "pota_ref", "sig_info", "sig"); QList expandedRecords({inputRecord}); // Expand records based on specific fields - one record for the specific POTA Ref expandParkRecord(expandedRecords, "my_sig_info"); expandParkRecord(expandedRecords, "sig_info"); for ( const QSqlRecord &record : static_cast&>(expandedRecords) ) { const QString &mySig = record.value("my_sig").toString().toLower(); // export Activator Log if ( mySig == "pota" ) { if ( AdiFormat *parkOut = this->getActivatorParkFormatter(record) ) { parkOut->exportContact(record, applTags); continue; } } // export Hunter Log else if ( record.value("sig").toString().toLower() == "pota" && mySig.isEmpty() ) AdiFormat::exportContact(record, applTags); } } AdiFormat *PotaAdiFormat::getActivatorParkFormatter(const QSqlRecord &record) { FCT_IDENTIFICATION; // https://docs.pota.app/docs/activator_reference/logging_made_easy.html#naming-your-files // station_callsign@park#-yyyymmdd const QString parkFileName = QString("%1@%2-%3.adif") .arg(record.value("station_callsign").toString(), record.value("my_sig_info").toString(), currentDate.toString("yyyyMMdd-hhmm")); if ( parkFormatters.contains(parkFileName) ) { qCDebug(runtime) << "Using park file " << parkFileName; return parkFormatters[parkFileName]->formatter; } ParkFormatter *parkFormatter = new ParkFormatter(); parkFormatter->file = new QFile(exportDir + QDir::separator() + parkFileName); if ( !parkFormatter->file->open(QFile::WriteOnly | QFile::Text) ) { qCWarning(runtime) << "Could not open POTA park file for writing " << exportDir + QDir::separator() + parkFileName; delete parkFormatter; return nullptr; } parkFormatter->stream = new QTextStream(parkFormatter->file); if ( !parkFormatter->stream ) { qCWarning(runtime) << "Cannot allocate QTextStream"; delete parkFormatter; return nullptr; } parkFormatter->formatter = new AdiFormat(*parkFormatter->stream); if ( !parkFormatter->formatter ) { qCWarning(runtime) << "Cannot allocate AdifFormatter"; delete parkFormatter; return nullptr; } parkFormatters[parkFileName] = parkFormatter; parkFormatter->formatter->exportStart(); return parkFormatter->formatter; } void PotaAdiFormat::expandParkRecord(QList &inputList, const QString &columnName) { FCT_IDENTIFICATION; QList expandedNewRecords; // can contain multiple parks as a csv: // K-0817,K-4566,K-4576,K-4573,K-4578@US-WY for ( auto it = inputList.cbegin(); it != inputList.cend(); it++ ) { QStringList activatedParks = it->value(columnName).toString().split(",", #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) Qt::SplitBehaviorFlags::SkipEmptyParts); #else QString::SkipEmptyParts); #endif for ( QString &str : activatedParks ) str = str.trimmed(); if ( activatedParks.size() <= 1 ) { expandedNewRecords.append(*it); continue; } for ( const QString &parkID : static_cast(activatedParks) ) { QSqlRecord newRecord(*it); newRecord.setValue(columnName, parkID); expandedNewRecords.append(newRecord); } } inputList.swap(expandedNewRecords); } void PotaAdiFormat::exportEnd() { FCT_IDENTIFICATION; const QList &formatters = parkFormatters.values(); for ( ParkFormatter *parkFormat : formatters) parkFormat->formatter->exportEnd(); } void PotaAdiFormat::moveFieldValue(QSqlRecord &record, const QString &fromFieldName, const QString &toFieldName) { FCT_IDENTIFICATION; QSqlField dupped(record.field(fromFieldName)); record.remove(record.indexOf(fromFieldName)); record.remove(record.indexOf(toFieldName)); dupped.setName(toFieldName); record.append(dupped); } bool PotaAdiFormat::isValidPotaRecord(const QSqlRecord &record) const { FCT_IDENTIFICATION; auto isPotaWithEmptyInfo = [](const QString &sig, const QString &info) { return sig == "pota" && info.isEmpty(); }; const QString &sigField = record.value("sig").toString().toLower(); const QString &mysigField = record.value("my_sig").toString().toLower(); return !record.value("my_pota_ref").toString().isEmpty() || !record.value("pota_ref").toString().isEmpty() || (sigField == "pota" && !isPotaWithEmptyInfo(sigField, record.value("sig_info").toString())) || (mysigField == "pota" && !isPotaWithEmptyInfo(mysigField, record.value("my_sig_info").toString())); } void PotaAdiFormat::preparePotaField(QSqlRecord &record, const QString &fromField, const QString &toField, const QString &toFieldSig) { FCT_IDENTIFICATION; if (record.value(fromField).toString().isEmpty()) return; moveFieldValue(record, fromField, toField); record.setValue(toFieldSig, "POTA"); } PotaAdiFormat::~PotaAdiFormat() { FCT_IDENTIFICATION; qDeleteAll(parkFormatters); parkFormatters.clear(); } ================================================ FILE: logformat/PotaAdiFormat.h ================================================ #ifndef QLOG_LOGFORMAT_POTALOGFORMAT_H #define QLOG_LOGFORMAT_POTALOGFORMAT_H #include "AdiFormat.h" /* * A specialized case of ADI export, where each activated park gets T'd into its * own file with some denormalization and values set to satisfy the pota.app * upload processes. */ class PotaAdiFormat : public AdiFormat { public: explicit PotaAdiFormat(QTextStream &stream); virtual void exportContact(const QSqlRecord &, QMap *applTags = nullptr) override; virtual void exportEnd() override; virtual bool importNext(QSqlRecord &) override { return false; } void setExportDirectory(const QString &dir); ~PotaAdiFormat(); private: QString exportDir; QDateTime currentDate; struct ParkFormatter { AdiFormat *formatter = nullptr; QFile *file = nullptr; QTextStream *stream = nullptr; ~ParkFormatter() { if ( formatter ) delete formatter; if ( stream ) delete stream; if ( file ) delete file; } }; QHash parkFormatters; AdiFormat *getActivatorParkFormatter(const QSqlRecord &record); void moveFieldValue(QSqlRecord &record, const QString &fromFieldName, const QString &toFieldName); bool isValidPotaRecord(const QSqlRecord &record) const; void preparePotaField(QSqlRecord &record, const QString &fromField, const QString &toField, const QString &toFieldSig); void expandParkRecord(QList &inputList, const QString &columnName); }; #endif // QLOG_LOGFORMAT_POTALOGFORMAT_H ================================================ FILE: models/AlertTableModel.cpp ================================================ #include #include #include "AlertTableModel.h" #include "data/Data.h" #include "rig/macros.h" //-+ FREQ_MATCH_TOLERANCE MHz is OK when QLog evaluates the same spot freq #define FREQ_MATCH_TOLERANCE 0.005 int AlertTableModel::rowCount(const QModelIndex&) const { return alertList.count(); } int AlertTableModel::columnCount(const QModelIndex&) const { return 8; } QVariant AlertTableModel::data(const QModelIndex& index, int role) const { const AlertTableRecord &selectedRecord = alertList.at(index.row()); if (role == Qt::DisplayRole) { switch ( index.column() ) { case COLUMN_RULENAME: return selectedRecord.ruleName.join(","); case COLUMN_CALLSIGN: return selectedRecord.alert.spot.callsign; case COLUMN_FREQ: return QSTRING_FREQ(selectedRecord.alert.spot.freq); case COLUMN_MODE: return selectedRecord.alert.spot.modeGroupString; case COLUMN_UPDATED: return selectedRecord.counter; case COLUMN_LAST_UPDATE: return locale.toString(selectedRecord.alert.spot.dateTime,locale.formatTimeLongWithoutTZ()); case COLUMN_LAST_COMMENT: return selectedRecord.alert.spot.comment; case COLUMN_MEMBER: return selectedRecord.alert.spot.memberList2StringList().join(","); default: return QVariant(); } } else if ( index.column() == COLUMN_CALLSIGN && role == Qt::BackgroundRole ) { return Data::statusToColor(selectedRecord.alert.spot.status, selectedRecord.alert.spot.dupeCount, QColor(Qt::transparent)); } else if ( role == Qt::UserRole ) { switch ( index.column() ) { case COLUMN_FREQ: return data(index, Qt::DisplayRole).toDouble(); break; case COLUMN_UPDATED: return data(index, Qt::DisplayRole).toULongLong(); break; case COLUMN_LAST_UPDATE: return selectedRecord.alert.spot.dateTime; break; default: return data(index, Qt::DisplayRole); } } return QVariant(); } QVariant AlertTableModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole || orientation != Qt::Horizontal) return QVariant(); switch (section) { case COLUMN_RULENAME: return tr("Rule Name"); case COLUMN_CALLSIGN: return tr("Callsign"); case COLUMN_FREQ: return tr("Frequency"); case COLUMN_MODE: return tr("Mode"); case COLUMN_UPDATED: return tr("Updated"); case COLUMN_LAST_UPDATE: return tr("Last Update"); case COLUMN_LAST_COMMENT: return tr("Last Comment"); case COLUMN_MEMBER: return tr("Member"); default: return QVariant(); } } void AlertTableModel::addAlert(const SpotAlert &entry) { AlertTableRecord newRecord(entry); QMutexLocker locker(&alertListMutex); int spotIndex = alertList.indexOf(newRecord); if ( spotIndex >= 0) { /* QLog already contains the spot, update it */ alertList[spotIndex].counter++; alertList[spotIndex].ruleName << entry.ruleNameList; alertList[spotIndex].ruleName.removeDuplicates(); alertList[spotIndex].ruleName.sort(); // QLog have WSJTX record and Spot from DXC is processed. // only change a limited number of fields to preserve the WSJTX Decode for an WSJTX application tuning if ( entry.source == SpotAlert::DXSPOT && alertList[spotIndex].alert.source == SpotAlert::WSJTXCQSPOT ) { alertList[spotIndex].alert.spot.comment = entry.spot.comment; alertList[spotIndex].alert.spot.spotter = entry.spot.spotter; alertList[spotIndex].alert.spot.dxcc_spotter = entry.spot.dxcc_spotter; } else { alertList[spotIndex].alert = entry; } emit dataChanged(createIndex(spotIndex,0), createIndex(spotIndex,5)); } else { /* New spot, insert it */ beginInsertRows(QModelIndex(), 0, 0); alertList.prepend(newRecord); endInsertRows(); } } void AlertTableModel::clear() { QMutexLocker locker(&alertListMutex); beginResetModel(); alertList.clear(); endResetModel(); } const AlertTableModel::AlertTableRecord AlertTableModel::getTableRecord(const QModelIndex &index) { QMutexLocker locker(&alertListMutex); return alertList.at(index.row()); } void AlertTableModel::aging(const int clear_interval_sec) { if ( clear_interval_sec <= 0 ) return; QMutexLocker locker(&alertListMutex); QMutableListIterator alertIterator(alertList); beginResetModel(); while ( alertIterator.hasNext() ) { alertIterator.next(); if ( alertIterator.value().alert.spot.dateTime.addSecs(clear_interval_sec) <= QDateTime::currentDateTimeUtc() ) { alertIterator.remove(); } } endResetModel(); } void AlertTableModel::resetDupe() { QMutexLocker locker(&alertListMutex); beginResetModel(); for ( AlertTableRecord &alert : alertList ) alert.alert.spot.dupeCount = 0; endResetModel(); } void AlertTableModel::recalculateDupe() { QMutexLocker locker(&alertListMutex); beginResetModel(); for ( AlertTableRecord &alertRecord : alertList ) { SpotAlert &alert = alertRecord.alert; alert.spot.dupeCount = Data::countDupe(alert.spot.callsign, alert.spot.band, alert.spot.modeGroupString); } endResetModel(); } void AlertTableModel::updateSpotsStatusWhenQSOAdded(const QSqlRecord &record) { qint32 dxcc = record.value("dxcc").toInt(); const QString &band = record.value("band").toString(); const QString &dxccModeGroup = BandPlan::modeToDXCCModeGroup(record.value("mode").toString()); const QString &callsign = record.value("callsign").toString(); QMutexLocker locker(&alertListMutex); beginResetModel(); for ( AlertTableRecord &alertRecord : alertList ) { SpotAlert &alert = alertRecord.alert; alert.spot.status = Data::dxccNewStatusWhenQSOAdded(alert.spot.status, alert.spot.dxcc.dxcc, alert.spot.band, ( ( alert.spot.modeGroupString == BandPlan::MODE_GROUP_STRING_FTx ) ? BandPlan::MODE_GROUP_STRING_DIGITAL : dxccModeGroup ), dxcc, band, dxccModeGroup); if ( alert.spot.callsign == callsign ) alert.spot.dupeCount = Data::dupeNewCountWhenQSOAdded(alert.spot.dupeCount, alert.spot.band, alert.spot.modeGroupString, band, dxccModeGroup); } endResetModel(); } void AlertTableModel::updateSpotsStatusWhenQSOUpdated(const QSqlRecord &) { QMutexLocker locker(&alertListMutex); // at this point, we don't know if callsign has been changed or other field. // TODO: DXCC status // TODO: Dupe status update // beginResetModel(); // for ( AlertTableRecord &alert : alertList ) // { // SpotAlert &spot = alert.alert; // spot.dupeCount = Data::countDupe(spot.callsign, spot.band, spot.modeGroupString); // } // endResetModel(); } void AlertTableModel::updateSpotsStatusWhenQSODeleted(const QSqlRecord &record) { // Pay attention: this method is called before the QSO is added to contacts const QString &callsign = record.value("callsign").toString(); const QString &band = record.value("band").toString(); const QString &dxccModeGroup = BandPlan::modeToDXCCModeGroup(record.value("mode").toString()); QMutexLocker locker(&alertListMutex); for ( AlertTableRecord &alertRecord : alertList ) { SpotAlert &alert = alertRecord.alert; if ( alert.spot.dupeCount && alert.spot.callsign == callsign ) alert.spot.dupeCount = Data::dupeNewCountWhenQSODelected(alert.spot.dupeCount, alert.spot.band, alert.spot.modeGroupString, band, dxccModeGroup); } } void AlertTableModel::updateSpotsDxccStatusWhenQSODeleted(const QSet &entities) { if ( entities.isEmpty() ) return; QMutexLocker locker(&alertListMutex); beginResetModel(); for ( AlertTableRecord &alertRecord : alertList ) { SpotAlert &alert = alertRecord.alert; if ( !entities.contains(alert.spot.dxcc.dxcc) ) continue; alert.spot.status = Data::instance()->dxccStatus(alert.spot.dxcc.dxcc, alert.spot.band, alert.spot.modeGroupString); } endResetModel(); } void AlertTableModel::recalculateDxccStatus() { QMutexLocker locker(&alertListMutex); beginResetModel(); for ( AlertTableRecord &alertRecord : alertList ) { SpotAlert &alert = alertRecord.alert; alert.spot.status = Data::instance()->dxccStatus(alert.spot.dxcc.dxcc, alert.spot.band, alert.spot.modeGroupString); } endResetModel(); } bool AlertTableModel::AlertTableRecord::operator==(const AlertTableRecord &spot) const { return ( (spot.alert.spot.callsign == this->alert.spot.callsign) && (spot.alert.spot.modeGroupString == this->alert.spot.modeGroupString) && (qAbs(this->alert.spot.freq - spot.alert.spot.freq) <= FREQ_MATCH_TOLERANCE) ); } AlertTableModel::AlertTableRecord::AlertTableRecord(const SpotAlert &spotAlert) : ruleName(spotAlert.ruleNameList), counter(0), alert(spotAlert) { } ================================================ FILE: models/AlertTableModel.h ================================================ #ifndef QLOG_MODELS_ALERTTABLEMODEL_H #define QLOG_MODELS_ALERTTABLEMODEL_H #include #include #include #include "core/LogLocale.h" class AlertTableModel : public QAbstractTableModel { Q_OBJECT public: enum column_id { COLUMN_RULENAME = 0, COLUMN_CALLSIGN = 1, COLUMN_FREQ = 2, COLUMN_MODE = 3, COLUMN_UPDATED = 4, COLUMN_LAST_UPDATE = 5, COLUMN_LAST_COMMENT = 6, COLUMN_MEMBER = 7, }; struct AlertTableRecord { QStringList ruleName; long long counter; SpotAlert alert; AlertTableRecord() : counter(0){}; bool operator==(const AlertTableRecord &) const; explicit AlertTableRecord(const SpotAlert&); }; AlertTableModel(QObject* parent = nullptr) : QAbstractTableModel(parent){}; int rowCount(const QModelIndex& parent = QModelIndex()) const; int columnCount(const QModelIndex& parent = QModelIndex()) const; QVariant data(const QModelIndex& index, int role) const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; void addAlert(const SpotAlert &entry); void clear(); const AlertTableRecord getTableRecord(const QModelIndex& index); void aging(const int clear_interval_sec); void resetDupe(); void recalculateDupe(); void updateSpotsStatusWhenQSOAdded(const QSqlRecord &record); void updateSpotsStatusWhenQSOUpdated(const QSqlRecord &); void updateSpotsStatusWhenQSODeleted(const QSqlRecord &record); void updateSpotsDxccStatusWhenQSODeleted(const QSet &entities); void recalculateDxccStatus(); private: LogLocale locale; QList alertList; QMutex alertListMutex; }; #endif // QLOG_MODELS_ALERTTABLEMODEL_H ================================================ FILE: models/AwardsTableModel.cpp ================================================ #include "AwardsTableModel.h" #include #include AwardsTableModel::AwardsTableModel(QObject* parent) : QSqlQueryModel(parent) { } QVariant AwardsTableModel::data(const QModelIndex &index, int role) const { /* using hiden column 0 to identify type of information */ /* 0 - Total Worked/Confirmed row * 1 - Confirmed row * 2 - Worked row * 3 - Per DXCC Entity row */ int originRowType = QSqlQueryModel::data(this->index(index.row(), 0), Qt::DisplayRole).toInt(); QVariant originCellValue = QSqlQueryModel::data(index, Qt::DisplayRole); int cellIntValue = originCellValue.toInt(); if ( role == Qt::DisplayRole && (originRowType == 1 || originRowType == 2) && index.column() == 2 ) { unsigned int count = 0; for ( int i = 3; i <= columnCount(); i++ ) count += QSqlQueryModel::data(this->index(index.row(), i), Qt::DisplayRole).toInt(); return tr("Slots: ") + QString::number(count) + " "; } if ( index.column() >= 3 ) { switch (role) { case Qt::BackgroundRole: if ( originRowType >= 3 ) { if ( cellIntValue > 1 ) return QColor(Qt::green); else if ( cellIntValue == 1 ) return QColor(255, 165, 0); } break; case Qt::ToolTipRole: if ( originRowType >= 3 ) { return ( cellIntValue > 1 ) ? tr("Confirmed") : (cellIntValue == 1) ? tr("Worked") : tr("Still Waiting"); } break; case Qt::DisplayRole: if ( originRowType >= 3 ) return QString(); break; case Qt::ForegroundRole: if ( originRowType >= 3 ) return QColor(Qt::transparent); break; } } else if ( role == Qt::FontRole && originRowType <= 2 ) { QFont font; font.setBold(true); return font; } return QSqlQueryModel::data(index, role); } ================================================ FILE: models/AwardsTableModel.h ================================================ #ifndef QLOG_MODELS_AWARDSTABLEMODEL_H #define QLOG_MODELS_AWARDSTABLEMODEL_H #include #include class AwardsTableModel : public QSqlQueryModel { Q_OBJECT public: explicit AwardsTableModel(QObject *parent = nullptr); QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; private: }; #endif // QLOG_MODELS_AWARDSTABLEMODEL_H ================================================ FILE: models/DxccTableModel.cpp ================================================ #include #include #include #include #include #include "DxccTableModel.h" #include "data/Data.h" #include "core/LogParam.h" DxccTableModel::DxccTableModel(QObject* parent) : QSqlQueryModel(parent) {} QVariant DxccTableModel::data(const QModelIndex &index, int role) const { if ( index.column() == 0 ) return QSqlQueryModel::data(index, role); switch ( role ) { case Qt::TextAlignmentRole: return int(Qt::AlignCenter | Qt::AlignVCenter); case Qt::BackgroundRole: { const QString &currData = data(index, Qt::DisplayRole).toString(); bool containsL = currData.contains('L'); bool containsP = currData.contains('P'); bool containsE = currData.contains('e'); if ( (LogParam::getDxccConfirmedByLotwState() && containsL) || (LogParam::getDxccConfirmedByPaperState() && containsP) || (LogParam::getDxccConfirmedByEqslState() && containsE) ) return Data::statusToColor(DxccStatus::NewMode, false, Qt::green); if ( containsL || containsP ||containsE || currData.contains("W") ) return Data::statusToColor(DxccStatus::Worked, false, Qt::transparent); } break; case Qt::DisplayRole: { const QString &currData = QSqlQueryModel::data(index, Qt::DisplayRole).toString(); if ( currData.isEmpty() || currData.size() < 3 ) return QString(); if ( currData == "111" ) return QString("W"); QString ret; if ( currData[0] == '2' ) ret.append("e"); if ( currData[1] == '2' ) ret.append("L"); if ( currData[2] == '2' ) ret.append("P"); return ret; } case Qt::ToolTipRole: { const QString &currData = data(index, Qt::DisplayRole).toString(); QStringList ret; if ( currData.contains("W") ) ret.append(tr("Worked")); if ( currData.contains("e") ) ret.append(tr("eQSL")); if ( currData.contains("L") ) ret.append(tr("LoTW")); if ( currData.contains("P") ) ret.append(tr("Paper")); return ret.join(", "); } } return QSqlQueryModel::data(index, role); } ================================================ FILE: models/DxccTableModel.h ================================================ #ifndef QLOG_MODELS_DXCCTABLEMODEL_H #define QLOG_MODELS_DXCCTABLEMODEL_H #include #include class DxccTableModel : public QSqlQueryModel { public: explicit DxccTableModel(QObject* parent = nullptr); QVariant data(const QModelIndex &, int role = Qt::DisplayRole) const; }; #endif // QLOG_MODELS_DXCCTABLEMODEL_H ================================================ FILE: models/LogbookModel.cpp ================================================ #include "LogbookModel.h" #include "data/Data.h" #include "data/Dxcc.h" #include "data/Gridsquare.h" #include "data/Callsign.h" #include "data/BandPlan.h" #include LogbookModel::LogbookModel(QObject* parent, QSqlDatabase db) : QSqlTableModel(parent, db) { setTable("contacts"); setEditStrategy(QSqlTableModel::OnFieldChange); setSort(COLUMN_TIME_ON, Qt::DescendingOrder); for (auto it = fieldNameTranslationMap.begin(); it != fieldNameTranslationMap.end(); ++it) setHeaderData(it.key(), Qt::Horizontal, getFieldNameTranslation(it.key())); } QVariant LogbookModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); if (role == Qt::DecorationRole && index.column() == COLUMN_CALL) { const QString &flag = Data::instance()->dxccFlag(QSqlTableModel::data(this->index(index.row(), COLUMN_DXCC), Qt::DisplayRole).toInt()); return ( !flag.isEmpty() ) ? QIcon(QString(":/flags/16/%1.png").arg(flag)) : QIcon(":/flags/16/unknown.png"); } if (role == Qt::DecorationRole && (index.column() == COLUMN_QSL_RCVD || index.column() == COLUMN_QSL_SENT || index.column() == COLUMN_LOTW_RCVD || index.column() == COLUMN_LOTW_SENT || index.column() == COLUMN_EQSL_QSL_RCVD || index.column() == COLUMN_EQSL_QSL_SENT || index.column() == COLUMN_DCL_QSL_RCVD || index.column() == COLUMN_DCL_QSL_SENT )) { QVariant value = QSqlTableModel::data(index, Qt::DisplayRole); if (value.toString() == "Y") { return QIcon(":/icons/done-24px.svg"); } // else { // return QIcon(":/icons/close-24px.svg"); // } } if ( role == Qt::ToolTipRole && index.column() == COLUMN_CALL ) { QString flag = Data::instance()->dxccFlag(QSqlTableModel::data(this->index(index.row(), COLUMN_DXCC), Qt::DisplayRole).toInt()); return QString("").arg(flag) + "

" + QSqlTableModel::data(index, Qt::DisplayRole).toString() + "

" + "" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + "
" + tr("Country") + ": " + QCoreApplication::translate("DBStrings", QSqlTableModel::data(this->index(index.row(), COLUMN_COUNTRY), Qt::DisplayRole).toString().toUtf8().constData()) + "
" + tr("Band") + ": " + QSqlTableModel::data(this->index(index.row(), COLUMN_BAND), Qt::DisplayRole).toString() + "
" + tr("Mode") + ": " + QSqlTableModel::data(this->index(index.row(), COLUMN_MODE), Qt::DisplayRole).toString() + "
" + tr("RST Sent") + ": " + QSqlTableModel::data(this->index(index.row(), COLUMN_RST_SENT), Qt::DisplayRole).toString() + "
" + tr("RST Rcvd") + ": " + QSqlTableModel::data(this->index(index.row(), COLUMN_RST_RCVD), Qt::DisplayRole).toString() + "
" + tr("Gridsquare") + ": " + QSqlTableModel::data(this->index(index.row(), COLUMN_GRID), Qt::DisplayRole).toString() + "
" + tr("QSL Message") + ": " + QSqlTableModel::data(this->index(index.row(), COLUMN_QSLMSG), Qt::DisplayRole).toString() + "
" + tr("Comment") + ": " + QSqlTableModel::data(this->index(index.row(), COLUMN_COMMENT_INTL), Qt::DisplayRole).toString() + "
" + tr("Notes") + ": " + QSqlTableModel::data(this->index(index.row(), COLUMN_NOTES_INTL), Qt::DisplayRole).toString() + "
" + "
" + "" + " " + " " + " " + " " + " " + QString(" ").arg((QSqlTableModel::data(this->index(index.row(), COLUMN_QSL_RCVD), Qt::DisplayRole).toString() == "Y") ? "done" : "close") + QString(" ").arg((QSqlTableModel::data(this->index(index.row(), COLUMN_LOTW_RCVD), Qt::DisplayRole).toString() == "Y") ? "done" : "close") + QString(" ").arg((QSqlTableModel::data(this->index(index.row(), COLUMN_EQSL_QSL_RCVD), Qt::DisplayRole).toString() == "Y") ? "done" : "close") + " " + " " + " " + QString(" ").arg((QSqlTableModel::data(this->index(index.row(), COLUMN_QSL_SENT), Qt::DisplayRole).toString() == "Y") ? "done" : "close") + QString(" ").arg((QSqlTableModel::data(this->index(index.row(), COLUMN_LOTW_SENT), Qt::DisplayRole).toString() == "Y") ? "done" : "close") + QString(" ").arg((QSqlTableModel::data(this->index(index.row(), COLUMN_EQSL_QSL_SENT), Qt::DisplayRole).toString() == "Y") ? "done" : "close") + " " + "
" + tr("Paper") + "" + tr("LoTW") +"" + tr("eQSL") +"
" + tr("QSL Received") + "
" + tr("QSL Sent") + "
"; } else if ( role == Qt::ToolTipRole && (index.column() == COLUMN_FIELDS || index.column() == COLUMN_NOTES || index.column() == COLUMN_NOTES_INTL) ) { return QSqlTableModel::data(index, Qt::DisplayRole); } else if ( role == Qt::DisplayRole && (index.column() == COLUMN_COUNTRY_INTL || index.column() == COLUMN_MY_COUNTRY_INTL) ) { return QCoreApplication::translate("DBStrings", QSqlTableModel::data(index, Qt::DisplayRole).toString().toUtf8().constData()); } return QSqlTableModel::data(index, role); } bool LogbookModel::setData(const QModelIndex &index, const QVariant &value, int role) { bool main_update_result = true; bool depend_update_result = true; if ( role == Qt::EditRole ) { switch ( index.column() ) { case COLUMN_TIME_ON: { QDateTime time_on = QSqlTableModel::data(this->index(index.row(), COLUMN_TIME_ON), Qt::DisplayRole).toDateTime(); QDateTime time_off = QSqlTableModel::data(this->index(index.row(), COLUMN_TIME_OFF), Qt::DisplayRole).toDateTime(); qint64 diff = time_on.secsTo(time_off); depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_TIME_OFF), QVariant(value.toDateTime().addSecs(diff)), role); break; } case COLUMN_TIME_OFF: { QDateTime time_on = QSqlTableModel::data(this->index(index.row(), COLUMN_TIME_ON), Qt::DisplayRole).toDateTime(); if ( value.toDateTime() < time_on ) { depend_update_result = false; } break; } case COLUMN_CALL: { QString new_callsign = value.toString(); DxccEntity dxccEntity = Data::instance()->lookupDxcc(new_callsign); if ( dxccEntity.dxcc ) { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_COUNTRY), QVariant(dxccEntity.country),role); depend_update_result = depend_update_result && QSqlTableModel::setData(this->index(index.row(), COLUMN_CQZ), QVariant(dxccEntity.cqz),role); depend_update_result = depend_update_result && QSqlTableModel::setData(this->index(index.row(), COLUMN_ITUZ), QVariant(dxccEntity.ituz),role); depend_update_result = depend_update_result && QSqlTableModel::setData(this->index(index.row(), COLUMN_DXCC), QVariant(dxccEntity.dxcc),role); depend_update_result = depend_update_result && QSqlTableModel::setData(this->index(index.row(), COLUMN_CONTINENT), QVariant(dxccEntity.cont),role); } else { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_COUNTRY), QVariant(QString()),role); depend_update_result = depend_update_result && QSqlTableModel::setData(this->index(index.row(), COLUMN_CQZ), QVariant(QString()),role); depend_update_result = depend_update_result && QSqlTableModel::setData(this->index(index.row(), COLUMN_ITUZ), QVariant(QString()),role); depend_update_result = depend_update_result && QSqlTableModel::setData(this->index(index.row(), COLUMN_DXCC), QVariant(QString()),role); depend_update_result = depend_update_result && QSqlTableModel::setData(this->index(index.row(), COLUMN_CONTINENT), QVariant(QString()),role); } const QString &pfxRef = Callsign(new_callsign).getWPXPrefix(); if ( !pfxRef.isEmpty() ) { depend_update_result = depend_update_result && QSqlTableModel::setData(this->index(index.row(), COLUMN_PREFIX), QVariant(pfxRef), role); } break; } case COLUMN_FREQUENCY: { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_BAND), QVariant(BandPlan::freq2Band(value.toDouble()).name), role ); break; } case COLUMN_BAND: { double freq = QSqlTableModel::data(this->index(index.row(), COLUMN_FREQUENCY), Qt::DisplayRole).toDouble(); depend_update_result = ( freq == 0.0 && !value.toString().isEmpty() ); break; } case COLUMN_FREQ_RX: { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_BAND_RX), QVariant(BandPlan::freq2Band(value.toDouble()).name), role ); break; } case COLUMN_BAND_RX: { double freq = QSqlTableModel::data(this->index(index.row(), COLUMN_FREQ_RX), Qt::DisplayRole).toDouble(); depend_update_result = ( freq == 0.0 && !value.toString().isEmpty() ); break; } case COLUMN_GRID: { if ( ! value.toString().isEmpty() ) { Gridsquare newgrid(value.toString()); if ( newgrid.isValid() ) { Gridsquare mygrid(QSqlTableModel::data(this->index(index.row(), COLUMN_MY_GRIDSQUARE), Qt::DisplayRole).toString()); double distance; if ( mygrid.distanceTo(newgrid, distance) ) { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_DISTANCE), QVariant(distance),role); } else { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_DISTANCE), QVariant(),role); } } else { /* do not update field with invalid Grid */ depend_update_result = false; } } else { /* empty grid is valid (when removing a value); need to remove also Distance */ depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_DISTANCE), QVariant(),role); } break; } case COLUMN_MY_GRIDSQUARE: { if ( ! value.toString().isEmpty() ) { Gridsquare mynewGrid(value.toString()); if ( mynewGrid.isValid() ) { Gridsquare dxgrid(QSqlTableModel::data(this->index(index.row(), COLUMN_GRID), Qt::DisplayRole).toString()); double distance; if ( mynewGrid.distanceTo(dxgrid, distance) ) { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_DISTANCE), QVariant(distance),role); } else { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_DISTANCE), QVariant(),role); } } else { /* do not update field with invalid Grid */ depend_update_result = false; } } else { /* empty grid is valid (when removing a value); need to remove also Distance */ depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_DISTANCE), QVariant(),role); } break; } case COLUMN_GRID_EXT: case COLUMN_MY_GRIDSQUARE_EXT: { if ( ! value.toString().isEmpty() ) { QRegularExpressionMatch match = Gridsquare::gridExtRegEx().match(value.toString()); if ( match.hasMatch() ) { depend_update_result = true; } else { /* grid has an incorrect format */ depend_update_result = false; } } else { /* empty grid is valid (when removing a value) */ depend_update_result = true; } break; } case COLUMN_SAT_MODE: case COLUMN_SAT_NAME: { if ( !value.toString().isEmpty() ) { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_PROP_MODE), "SAT"); } break; } case COLUMN_PROP_MODE: { QString sat_mode = QSqlTableModel::data(this->index(index.row(), COLUMN_SAT_MODE), Qt::DisplayRole).toString(); QString sat_name = QSqlTableModel::data(this->index(index.row(), COLUMN_SAT_NAME), Qt::DisplayRole).toString(); /* If sat name or mode is not empty then do not allow to change propmode from SAT to any */ if ( !sat_name.isEmpty() || !sat_mode.isEmpty() ) { depend_update_result = false; } break; } case COLUMN_ID: /* it is the primary key, do not update */ case COLUMN_COUNTRY: /* it is a computed value, do not update */ case COLUMN_DISTANCE: /* it is a computed value, do not update */ case COLUMN_MY_COUNTRY: { /* Do not allow to edit them */ depend_update_result = false; break; } case COLUMN_ADDRESS_INTL: { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_ADDRESS), Data::removeAccents(value.toString()),role); break; } case COLUMN_COMMENT_INTL: { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_COMMENT), Data::removeAccents(value.toString()),role); break; } case COLUMN_COUNTRY_INTL: { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_COUNTRY), Data::removeAccents(value.toString()),role); break; } case COLUMN_MY_ANTENNA_INTL: { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_MY_ANTENNA), Data::removeAccents(value.toString()),role); break; } case COLUMN_MY_CITY_INTL: { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_MY_CITY), Data::removeAccents(value.toString()),role); break; } case COLUMN_MY_COUNTRY_INTL: { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_MY_COUNTRY), Data::removeAccents(value.toString()),role); break; } case COLUMN_MY_NAME_INTL: { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_MY_NAME), Data::removeAccents(value.toString()),role); break; } case COLUMN_MY_POSTAL_CODE_INTL: { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_MY_POSTAL_CODE), Data::removeAccents(value.toString()),role); break; } case COLUMN_MY_RIG_INTL: { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_MY_RIG), Data::removeAccents(value.toString()),role); break; } case COLUMN_MY_SIG_INTL: { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_MY_SIG), Data::removeAccents(value.toString()),role); break; } case COLUMN_MY_SIG_INFO_INTL: { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_MY_SIG_INFO), Data::removeAccents(value.toString()),role); break; } case COLUMN_MY_STREET_INTL: { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_MY_STREET), Data::removeAccents(value.toString()),role); break; } case COLUMN_NAME_INTL: { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_NAME), Data::removeAccents(value.toString()),role); break; } case COLUMN_NOTES_INTL: { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_NOTES), Data::removeAccents(value.toString()),role); break; } case COLUMN_QSLMSG_INTL: { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_QSLMSG), Data::removeAccents(value.toString()),role); break; } case COLUMN_QTH_INTL: { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_QTH), Data::removeAccents(value.toString()),role); break; } case COLUMN_RIG_INTL: { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_RIG), Data::removeAccents(value.toString()),role); break; } case COLUMN_SIG_INTL: { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_SIG), Data::removeAccents(value.toString()),role); break; } case COLUMN_SIG_INFO_INTL: { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_SIG_INFO), Data::removeAccents(value.toString()),role); break; } case COLUMN_SOTA_REF: { SOTAEntity sotaInfo = Data::instance()->lookupSOTA(value.toString()); if ( sotaInfo.summitCode.toUpper() == value.toString().toUpper() && !sotaInfo.summitName.isEmpty() ) { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_ALTITUDE), sotaInfo.altm, role); // clazy:exclude=skipped-base-method } else { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_ALTITUDE), QVariant(), role); // clazy:exclude=skipped-base-method } break; } case COLUMN_MY_SOTA_REF: { SOTAEntity sotaInfo = Data::instance()->lookupSOTA(value.toString()); if ( sotaInfo.summitCode.toUpper() == value.toString().toUpper() && !sotaInfo.summitName.isEmpty() ) { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_MY_ALTITUDE), sotaInfo.altm, role); // clazy:exclude=skipped-base-method } else { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_MY_ALTITUDE), QVariant(), role); // clazy:exclude=skipped-base-method } break; } case COLUMN_STATION_CALLSIGN: { DxccEntity dxccEntity = Data::instance()->lookupDxcc(value.toString().toUpper()); if ( dxccEntity.dxcc ) { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_MY_DXCC), dxccEntity.dxcc, role); // clazy:exclude=skipped-base-method depend_update_result = depend_update_result && QSqlTableModel::setData(this->index(index.row(), COLUMN_MY_ITU_ZONE), dxccEntity.ituz, role); // clazy:exclude=skipped-base-method depend_update_result = depend_update_result && QSqlTableModel::setData(this->index(index.row(), COLUMN_MY_CQ_ZONE), dxccEntity.cqz, role); // clazy:exclude=skipped-base-method depend_update_result = depend_update_result && QSqlTableModel::setData(this->index(index.row(), COLUMN_MY_COUNTRY_INTL), dxccEntity.country, role); // clazy:exclude=skipped-base-method depend_update_result = depend_update_result && QSqlTableModel::setData(this->index(index.row(), COLUMN_MY_COUNTRY), Data::removeAccents(dxccEntity.country), role); // clazy:exclude=skipped-base-method } else { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_MY_COUNTRY), QVariant(QString()),role); // clazy:exclude=skipped-base-method depend_update_result = depend_update_result && QSqlTableModel::setData(this->index(index.row(), COLUMN_MY_CQ_ZONE), QVariant(QString()),role); // clazy:exclude=skipped-base-method depend_update_result = depend_update_result && QSqlTableModel::setData(this->index(index.row(), COLUMN_MY_ITU_ZONE), QVariant(QString()),role); // clazy:exclude=skipped-base-method depend_update_result = depend_update_result && QSqlTableModel::setData(this->index(index.row(), COLUMN_MY_DXCC), QVariant(QString()),role); // clazy:exclude=skipped-base-method depend_update_result = depend_update_result && QSqlTableModel::setData(this->index(index.row(), COLUMN_MY_COUNTRY_INTL), QVariant(QString()),role); // clazy:exclude=skipped-base-method } break; } } //updateExternalServicesUploadStatus(index, role, depend_update_result); if ( depend_update_result ) { switch ( index.column() ) { case COLUMN_FREQUENCY: case COLUMN_FREQ_RX: case COLUMN_TX_POWER: /* store NULL when 0.0MHz */ main_update_result = QSqlTableModel::setData(index, ( value.toDouble() == 0.0 ) ? QVariant() : value, role); // clazy:exclude=skipped-base-method break; case COLUMN_SOTA_REF: case COLUMN_MY_SOTA_REF: case COLUMN_IOTA: case COLUMN_MY_IOTA: case COLUMN_MY_GRIDSQUARE: case COLUMN_CALL: case COLUMN_GRID: case COLUMN_VUCC_GRIDS: case COLUMN_MY_VUCC_GRIDS: case COLUMN_MY_ARRL_SECT: case COLUMN_MY_WWFF_REF: case COLUMN_WWFF_REF: case COLUMN_MY_POTA_REF: case COLUMN_POTA_REF: case COLUMN_MY_GRIDSQUARE_EXT: case COLUMN_GRID_EXT: case COLUMN_STATION_CALLSIGN: main_update_result = QSqlTableModel::setData(index, ( !value.toString().isEmpty() ) ? value.toString().toUpper() : QVariant(), role); break; case COLUMN_ADDRESS_INTL: case COLUMN_COMMENT_INTL: case COLUMN_COUNTRY_INTL: case COLUMN_MY_ANTENNA_INTL: case COLUMN_MY_CITY_INTL: case COLUMN_MY_COUNTRY_INTL: case COLUMN_MY_NAME_INTL: case COLUMN_MY_POSTAL_CODE_INTL: case COLUMN_MY_RIG_INTL: case COLUMN_MY_SIG_INTL: case COLUMN_MY_SIG_INFO_INTL: case COLUMN_MY_STREET_INTL: case COLUMN_NAME_INTL: case COLUMN_NOTES_INTL: case COLUMN_QSLMSG_INTL: case COLUMN_QTH_INTL: case COLUMN_RIG_INTL: case COLUMN_SIG_INTL: case COLUMN_SIG_INFO_INTL: main_update_result = QSqlTableModel::setData(index, ( !value.toString().isEmpty() ) ? value : QVariant(), role); break; default: main_update_result = QSqlTableModel::setData(index, ( !value.toString().isEmpty() ) ? Data::removeAccents(value.toString()) : QVariant(), role); } } } return main_update_result && depend_update_result; } QMap LogbookModel::fieldNameTranslationMap = { {COLUMN_ID, QT_TR_NOOP("QSO ID")}, {COLUMN_TIME_ON, QT_TR_NOOP("Time on")}, {COLUMN_TIME_OFF, QT_TR_NOOP("Time off")}, {COLUMN_CALL, QT_TR_NOOP("Call")}, {COLUMN_RST_SENT, QT_TR_NOOP("RSTs")}, {COLUMN_RST_RCVD, QT_TR_NOOP("RSTr")}, {COLUMN_FREQUENCY, QT_TR_NOOP("Frequency")}, {COLUMN_BAND, QT_TR_NOOP("Band")}, {COLUMN_MODE, QT_TR_NOOP("Mode")}, {COLUMN_SUBMODE, QT_TR_NOOP("Submode")}, {COLUMN_NAME, QT_TR_NOOP("Name (ASCII)")}, {COLUMN_QTH, QT_TR_NOOP("QTH (ASCII)")}, {COLUMN_GRID, QT_TR_NOOP("Gridsquare")}, {COLUMN_DXCC, QT_TR_NOOP("DXCC")}, {COLUMN_COUNTRY, QT_TR_NOOP("Country (ASCII)")}, {COLUMN_CONTINENT, QT_TR_NOOP("Continent")}, {COLUMN_CQZ, QT_TR_NOOP("CQZ")}, {COLUMN_ITUZ, QT_TR_NOOP("ITU")}, {COLUMN_PREFIX, QT_TR_NOOP("Prefix")}, {COLUMN_STATE, QT_TR_NOOP("State")}, {COLUMN_COUNTY, QT_TR_NOOP("County")}, {COLUMN_CNTY_ALT, QT_TR_NOOP("County Alt")}, {COLUMN_IOTA, QT_TR_NOOP("IOTA")}, {COLUMN_QSL_RCVD, QT_TR_NOOP("QSLr")}, {COLUMN_QSL_RCVD_DATE, QT_TR_NOOP("QSLr Date")}, {COLUMN_QSL_SENT, QT_TR_NOOP("QSLs")}, {COLUMN_QSL_SENT_DATE, QT_TR_NOOP("QSLs Date")}, {COLUMN_LOTW_RCVD, QT_TR_NOOP("LoTWr")}, {COLUMN_LOTW_RCVD_DATE, QT_TR_NOOP("LoTWr Date")}, {COLUMN_LOTW_SENT, QT_TR_NOOP("LoTWs")}, {COLUMN_LOTW_SENT_DATE, QT_TR_NOOP("LoTWs Date")}, {COLUMN_TX_POWER, QT_TR_NOOP("TX PWR")}, {COLUMN_FIELDS, QT_TR_NOOP("Additional Fields")}, {COLUMN_ADDRESS, QT_TR_NOOP("Address (ASCII)")}, {COLUMN_ADDRESS_INTL, QT_TR_NOOP("Address")}, {COLUMN_AGE, QT_TR_NOOP("Age")}, {COLUMN_ALTITUDE, QT_TR_NOOP("Altitude")}, {COLUMN_A_INDEX, QT_TR_NOOP("A-Index")}, {COLUMN_ANT_AZ, QT_TR_NOOP("Antenna Az")}, {COLUMN_ANT_EL, QT_TR_NOOP("Antenna El")}, {COLUMN_ANT_PATH, QT_TR_NOOP("Signal Path")}, {COLUMN_ARRL_SECT, QT_TR_NOOP("ARRL Section")}, {COLUMN_AWARD_SUBMITTED, QT_TR_NOOP("Award Submitted")}, {COLUMN_AWARD_GRANTED, QT_TR_NOOP("Award Granted")}, {COLUMN_BAND_RX, QT_TR_NOOP("Band RX")}, {COLUMN_GRID_EXT, QT_TR_NOOP("Gridsquare Extended")}, {COLUMN_CHECK, QT_TR_NOOP("Contest Check")}, {COLUMN_CLASS, QT_TR_NOOP("Class")}, {COLUMN_CLUBLOG_QSO_UPLOAD_DATE, QT_TR_NOOP("ClubLog Upload Date")}, {COLUMN_CLUBLOG_QSO_UPLOAD_STATUS, QT_TR_NOOP("ClubLog Upload State")}, {COLUMN_COMMENT, QT_TR_NOOP("Comment (ASCII)")}, {COLUMN_COMMENT_INTL, QT_TR_NOOP("Comment")}, {COLUMN_CONTACTED_OP, QT_TR_NOOP("Contacted Operator")}, {COLUMN_CONTEST_ID, QT_TR_NOOP("Contest ID")}, {COLUMN_COUNTRY_INTL, QT_TR_NOOP("Country")}, {COLUMN_CREDIT_SUBMITTED, QT_TR_NOOP("Credit Submitted")}, {COLUMN_CREDIT_GRANTED, QT_TR_NOOP("Credit Granted")}, {COLUMN_DARC_DOK, QT_TR_NOOP("DOK")}, {COLUMN_DCL_QSLRDATE, QT_TR_NOOP("DCLr Date")}, {COLUMN_DCL_QSLSDATE, QT_TR_NOOP("DCLs Date")}, {COLUMN_DCL_QSL_RCVD, QT_TR_NOOP("DCLr")}, {COLUMN_DCL_QSL_SENT, QT_TR_NOOP("DCLs")}, {COLUMN_DISTANCE, QT_TR_NOOP("Distance")}, {COLUMN_EMAIL, QT_TR_NOOP("Email")}, {COLUMN_EQ_CALL, QT_TR_NOOP("Owner Callsign")}, {COLUMN_EQSL_AG, QT_TR_NOOP("eQSL AG")}, {COLUMN_EQSL_QSLRDATE, QT_TR_NOOP("eQSLr Date")}, {COLUMN_EQSL_QSLSDATE, QT_TR_NOOP("eQSLs Date")}, {COLUMN_EQSL_QSL_RCVD, QT_TR_NOOP("eQSLr")}, {COLUMN_EQSL_QSL_SENT, QT_TR_NOOP("eQSLs")}, {COLUMN_FISTS, QT_TR_NOOP("FISTS Number")}, {COLUMN_FISTS_CC, QT_TR_NOOP("FISTS CC")}, {COLUMN_FORCE_INIT, QT_TR_NOOP("EME Init")}, {COLUMN_FREQ_RX, QT_TR_NOOP("Frequency RX")}, {COLUMN_GUEST_OP, QT_TR_NOOP("Guest Operator")}, {COLUMN_HAMLOGEU_QSO_UPLOAD_DATE, QT_TR_NOOP("HamlogEU Upload Date")}, {COLUMN_HAMLOGEU_QSO_UPLOAD_STATUS, QT_TR_NOOP("HamlogEU Upload Status")}, {COLUMN_HAMQTH_QSO_UPLOAD_DATE, QT_TR_NOOP("HamQTH Upload Date")}, {COLUMN_HAMQTH_QSO_UPLOAD_STATUS, QT_TR_NOOP("HamQTH Upload Status")}, {COLUMN_HRDLOG_QSO_UPLOAD_DATE, QT_TR_NOOP("HRDLog Upload Date")}, {COLUMN_HRDLOG_QSO_UPLOAD_STATUS, QT_TR_NOOP("HRDLog Upload Status")}, {COLUMN_IOTA_ISLAND_ID, QT_TR_NOOP("IOTA Island ID")}, {COLUMN_K_INDEX, QT_TR_NOOP("K-Index")}, {COLUMN_LAT, QT_TR_NOOP("Latitude")}, {COLUMN_LON, QT_TR_NOOP("Longitude")}, {COLUMN_MAX_BURSTS, QT_TR_NOOP("Max Bursts")}, {COLUMN_MORSE_KEY_INFO, QT_TR_NOOP("CW Key Info")}, {COLUMN_MORSE_KEY_TYPE, QT_TR_NOOP("CW Key Type")}, {COLUMN_MS_SHOWER, QT_TR_NOOP("MS Shower Name")}, {COLUMN_MY_ALTITUDE, QT_TR_NOOP("My Altitude")}, {COLUMN_MY_ANTENNA, QT_TR_NOOP("My Antenna (ASCII)")}, {COLUMN_MY_ANTENNA_INTL, QT_TR_NOOP("My Antenna")}, {COLUMN_MY_CITY, QT_TR_NOOP("My City (ASCII)")}, {COLUMN_MY_CITY_INTL, QT_TR_NOOP("My City")}, {COLUMN_MY_CNTY, QT_TR_NOOP("My County")}, {COLUMN_MY_CNTY_ALT, QT_TR_NOOP("My County Alt")}, {COLUMN_MY_COUNTRY, QT_TR_NOOP("My Country (ASCII)")}, {COLUMN_MY_COUNTRY_INTL, QT_TR_NOOP("My Country")}, {COLUMN_MY_CQ_ZONE, QT_TR_NOOP("My CQZ")}, {COLUMN_MY_DARC_DOK, QT_TR_NOOP("My DARC DOK")}, {COLUMN_MY_DXCC, QT_TR_NOOP("My DXCC")}, {COLUMN_MY_FISTS, QT_TR_NOOP("My FISTS")}, {COLUMN_MY_GRIDSQUARE, QT_TR_NOOP("My Gridsquare")}, {COLUMN_MY_GRIDSQUARE_EXT, QT_TR_NOOP("My Gridsquare Extended")}, {COLUMN_MY_IOTA, QT_TR_NOOP("My IOTA")}, {COLUMN_MY_IOTA_ISLAND_ID, QT_TR_NOOP("My IOTA Island ID")}, {COLUMN_MY_ITU_ZONE, QT_TR_NOOP("My ITU")}, {COLUMN_MY_LAT, QT_TR_NOOP("My Latitude")}, {COLUMN_MY_LON, QT_TR_NOOP("My Longitude")}, {COLUMN_MY_MORSE_KEY_INFO, QT_TR_NOOP("My CW Key Info")}, {COLUMN_MY_MORSE_KEY_TYPE, QT_TR_NOOP("My CW Key Type")}, {COLUMN_MY_NAME, QT_TR_NOOP("My Name (ASCII)")}, {COLUMN_MY_NAME_INTL, QT_TR_NOOP("My Name")}, {COLUMN_MY_POSTAL_CODE, QT_TR_NOOP("My Postal Code (ASCII)")}, {COLUMN_MY_POSTAL_CODE_INTL, QT_TR_NOOP("My Postal Code")}, {COLUMN_MY_POTA_REF, QT_TR_NOOP("My POTA Ref")}, {COLUMN_MY_RIG, QT_TR_NOOP("My Rig (ASCII)")}, {COLUMN_MY_RIG_INTL, QT_TR_NOOP("My Rig")}, {COLUMN_MY_SIG, QT_TR_NOOP("My Special Interest Activity (ASCII)")}, {COLUMN_MY_SIG_INTL, QT_TR_NOOP("My Special Interest Activity")}, {COLUMN_MY_SIG_INFO, QT_TR_NOOP("My Spec. Interes Activity Info (ASCII)")}, {COLUMN_MY_SIG_INFO_INTL, QT_TR_NOOP("My Spec. Interest Activity Info")}, {COLUMN_MY_SOTA_REF, QT_TR_NOOP("My SOTA")}, {COLUMN_MY_STATE, QT_TR_NOOP("My State")}, {COLUMN_MY_STREET, QT_TR_NOOP("My Street")}, {COLUMN_MY_STREET_INTL, QT_TR_NOOP("My Street")}, {COLUMN_MY_USACA_COUNTIES, QT_TR_NOOP("My USA-CA Counties")}, {COLUMN_MY_VUCC_GRIDS, QT_TR_NOOP("My VUCC Grids")}, {COLUMN_NAME_INTL, QT_TR_NOOP("Name")}, {COLUMN_NOTES, QT_TR_NOOP("Notes (ASCII)")}, {COLUMN_NOTES_INTL, QT_TR_NOOP("Notes")}, {COLUMN_NR_BURSTS, QT_TR_NOOP("#MS Bursts")}, {COLUMN_NR_PINGS, QT_TR_NOOP("#MS Pings")}, {COLUMN_OPERATOR, QT_TR_NOOP("Operator Callsign")}, {COLUMN_OWNER_CALLSIGN, QT_TR_NOOP("Owner Callsign")}, {COLUMN_POTA_REF, QT_TR_NOOP("POTA")}, {COLUMN_PRECEDENCE, QT_TR_NOOP("Contest Precedence")}, {COLUMN_PROP_MODE, QT_TR_NOOP("Propagation Mode")}, {COLUMN_PUBLIC_KEY, QT_TR_NOOP("Public Encryption Key")}, {COLUMN_QRZCOM_QSO_DOWNLOAD_DATE, QT_TR_NOOP("QRZ Download Date")}, {COLUMN_QRZCOM_QSO_DOWNLOAD_STATUS, QT_TR_NOOP("QRZ Download Status")}, {COLUMN_QRZCOM_QSO_UPLOAD_DATE, QT_TR_NOOP("QRZ Upload Date")}, {COLUMN_QRZCOM_QSO_UPLOAD_STATUS, QT_TR_NOOP("QRZ Upload Status")}, {COLUMN_QSLMSG, QT_TR_NOOP("QSLs Message (ASCII)")}, {COLUMN_QSLMSG_INTL, QT_TR_NOOP("QSLs Message")}, {COLUMN_QSLMSG_RCVD, QT_TR_NOOP("QSLr Message")}, {COLUMN_QSL_RCVD_VIA, QT_TR_NOOP("QSLr Via")}, {COLUMN_QSL_SENT_VIA, QT_TR_NOOP("QSLs Via")}, {COLUMN_QSL_VIA, QT_TR_NOOP("QSL Via")}, {COLUMN_QSO_COMPLETE, QT_TR_NOOP("QSO Completed")}, {COLUMN_QSO_RANDOM, QT_TR_NOOP("QSO Random")}, {COLUMN_QTH_INTL, QT_TR_NOOP("QTH")}, {COLUMN_REGION, QT_TR_NOOP("Region")}, {COLUMN_RIG, QT_TR_NOOP("Rig (ASCII)")}, {COLUMN_RIG_INTL, QT_TR_NOOP("Rig")}, {COLUMN_RX_PWR, QT_TR_NOOP("RcvPWR")}, {COLUMN_SAT_MODE, QT_TR_NOOP("SAT Mode")}, {COLUMN_SAT_NAME, QT_TR_NOOP("SAT Name")}, {COLUMN_SFI, QT_TR_NOOP("Solar Flux")}, {COLUMN_SIG, QT_TR_NOOP("SIG (ASCII)")}, {COLUMN_SIG_INTL, QT_TR_NOOP("SIG")}, {COLUMN_SIG_INFO, QT_TR_NOOP("SIG Info (ASCII)")}, {COLUMN_SIG_INFO_INTL, QT_TR_NOOP("SIG Info")}, {COLUMN_SILENT_KEY, QT_TR_NOOP("Silent Key")}, {COLUMN_SKCC, QT_TR_NOOP("SKCC Member")}, {COLUMN_SOTA_REF, QT_TR_NOOP("SOTA")}, {COLUMN_SRX, QT_TR_NOOP("RcvNr")}, {COLUMN_SRX_STRING, QT_TR_NOOP("RcvExch")}, {COLUMN_STATION_CALLSIGN, QT_TR_NOOP("Logging Station Callsign")}, {COLUMN_STX, QT_TR_NOOP("SentNr")}, {COLUMN_STX_STRING, QT_TR_NOOP("SentExch")}, {COLUMN_SWL, QT_TR_NOOP("SWL")}, {COLUMN_TEN_TEN, QT_TR_NOOP("Ten-Ten Number")}, {COLUMN_UKSMG, QT_TR_NOOP("UKSMG Member")}, {COLUMN_USACA_COUNTIES, QT_TR_NOOP("USA-CA Counties")}, {COLUMN_VE_PROV, QT_TR_NOOP("VE Prov")}, {COLUMN_VUCC_GRIDS, QT_TR_NOOP("VUCC")}, {COLUMN_WEB, QT_TR_NOOP("Web")}, {COLUMN_MY_ARRL_SECT, QT_TR_NOOP("My ARRL Section")}, {COLUMN_MY_WWFF_REF, QT_TR_NOOP("My WWFF")}, {COLUMN_WWFF_REF, QT_TR_NOOP("WWFF")} }; #if 0 void LogbookModel::updateExternalServicesUploadStatus(const QModelIndex &index, int role, bool &updateResult) { switch (index.column() ) { case COLUMN_TIME_ON: case COLUMN_CALL: case COLUMN_FREQUENCY: case COLUMN_BAND: case COLUMN_PROP_MODE: case COLUMN_SAT_MODE: case COLUMN_SAT_NAME: case COLUMN_MODE: case COLUMN_SUBMODE: case COLUMN_STATION_CALLSIGN: case COLUMN_RST_RCVD: case COLUMN_RST_SENT: case COLUMN_QSL_RCVD: case COLUMN_QSL_SENT: case COLUMN_QSL_RCVD_DATE: case COLUMN_QSL_SENT_DATE: case COLUMN_DXCC: case COLUMN_CREDIT_GRANTED: case COLUMN_VUCC_GRIDS: case COLUMN_OPERATOR: case COLUMN_GRID: case COLUMN_NOTES: updateUploadToModified(index, role, COLUMN_CLUBLOG_QSO_UPLOAD_STATUS, updateResult); //updateUploadToModified(index, role, COLUMN_HRDLOG_QSO_UPLOAD_STATUS, updateResult); break; } /* QRZ consumes all ADIF Fields */ updateUploadToModified(index, role, COLUMN_QRZCOM_QSO_UPLOAD_STATUS, updateResult); /* HRDLOG consumes all ADIF Fields */ updateUploadToModified(index, role, COLUMN_HRDLOG_QSO_UPLOAD_STATUS, updateResult); } void LogbookModel::updateUploadToModified(const QModelIndex &index, int role, int column, bool &updateResult) { QString status = QSqlTableModel::data(this->index(index.row(), column), Qt::DisplayRole).toString(); if ( status == "Y" ) { updateResult = updateResult && QSqlTableModel::setData(this->index(index.row(), column), QVariant("M"), role); } } #endif ================================================ FILE: models/LogbookModel.h ================================================ #ifndef QLOG_MODELS_LOGBOOKMODEL_H #define QLOG_MODELS_LOGBOOKMODEL_H #include #include class LogbookModel : public QSqlTableModel { Q_OBJECT public: explicit LogbookModel(QObject* parent = nullptr, QSqlDatabase db = QSqlDatabase()); QVariant data(const QModelIndex &index, int role) const override; bool setData(const QModelIndex &index, const QVariant &value, int role) override; void updateExternalServicesUploadStatus( const QModelIndex &index, int role, bool &updateResult ); void updateUploadToModified( const QModelIndex &index, int role, int column, bool &updateResult ); enum ColumnID { COLUMN_INVALID = -1, COLUMN_ID = 0, COLUMN_TIME_ON = 1, COLUMN_TIME_OFF = 2, COLUMN_CALL = 3, COLUMN_RST_SENT = 4, COLUMN_RST_RCVD = 5, COLUMN_FREQUENCY = 6, COLUMN_BAND = 7, COLUMN_MODE = 8, COLUMN_SUBMODE = 9, COLUMN_NAME = 10, COLUMN_QTH = 11, COLUMN_GRID = 12, COLUMN_DXCC = 13, COLUMN_COUNTRY = 14, COLUMN_CONTINENT = 15, COLUMN_CQZ = 16, COLUMN_ITUZ = 17, COLUMN_PREFIX = 18, COLUMN_STATE = 19, COLUMN_COUNTY = 20, COLUMN_IOTA = 21, COLUMN_QSL_RCVD = 22, COLUMN_QSL_RCVD_DATE = 23, COLUMN_QSL_SENT = 24, COLUMN_QSL_SENT_DATE = 25, COLUMN_LOTW_RCVD = 26, COLUMN_LOTW_RCVD_DATE = 27, COLUMN_LOTW_SENT = 28, COLUMN_LOTW_SENT_DATE = 29, COLUMN_TX_POWER = 30, COLUMN_FIELDS = 31, COLUMN_ADDRESS = 32, COLUMN_ADDRESS_INTL = 33, COLUMN_AGE = 34, COLUMN_A_INDEX = 35, COLUMN_ANT_AZ = 36, COLUMN_ANT_EL = 37, COLUMN_ANT_PATH = 38, COLUMN_ARRL_SECT = 39, COLUMN_AWARD_SUBMITTED = 40, COLUMN_AWARD_GRANTED = 41, COLUMN_BAND_RX = 42, COLUMN_CHECK = 43, COLUMN_CLASS = 44, COLUMN_CLUBLOG_QSO_UPLOAD_DATE = 45, COLUMN_CLUBLOG_QSO_UPLOAD_STATUS = 46, COLUMN_COMMENT = 47, COLUMN_COMMENT_INTL = 48, COLUMN_CONTACTED_OP = 49, COLUMN_CONTEST_ID = 50, COLUMN_COUNTRY_INTL = 51, COLUMN_CREDIT_SUBMITTED = 52, COLUMN_CREDIT_GRANTED = 53, COLUMN_DARC_DOK = 54, COLUMN_DISTANCE = 55, COLUMN_EMAIL = 56, COLUMN_EQ_CALL = 57, COLUMN_EQSL_QSLRDATE = 58, COLUMN_EQSL_QSLSDATE = 59, COLUMN_EQSL_QSL_RCVD = 60, COLUMN_EQSL_QSL_SENT = 61, COLUMN_FISTS = 62, COLUMN_FISTS_CC = 63, COLUMN_FORCE_INIT = 64, COLUMN_FREQ_RX = 65, COLUMN_GUEST_OP = 66, COLUMN_HRDLOG_QSO_UPLOAD_DATE = 67, COLUMN_HRDLOG_QSO_UPLOAD_STATUS = 68, COLUMN_IOTA_ISLAND_ID = 69, COLUMN_K_INDEX = 70, COLUMN_LAT = 71, COLUMN_LON = 72, COLUMN_MAX_BURSTS = 73, COLUMN_MS_SHOWER = 74, COLUMN_MY_ANTENNA = 75, COLUMN_MY_ANTENNA_INTL = 76, COLUMN_MY_CITY = 77, COLUMN_MY_CITY_INTL = 78, COLUMN_MY_CNTY = 79, COLUMN_MY_COUNTRY = 80, COLUMN_MY_COUNTRY_INTL = 81, COLUMN_MY_CQ_ZONE = 82, COLUMN_MY_DXCC = 83, COLUMN_MY_FISTS = 84, COLUMN_MY_GRIDSQUARE = 85, COLUMN_MY_IOTA = 86, COLUMN_MY_IOTA_ISLAND_ID = 87, COLUMN_MY_ITU_ZONE = 88, COLUMN_MY_LAT = 89, COLUMN_MY_LON = 90, COLUMN_MY_NAME = 91, COLUMN_MY_NAME_INTL = 92, COLUMN_MY_POSTAL_CODE = 93, COLUMN_MY_POSTAL_CODE_INTL = 94, COLUMN_MY_RIG = 95, COLUMN_MY_RIG_INTL = 96, COLUMN_MY_SIG = 97, COLUMN_MY_SIG_INTL = 98, COLUMN_MY_SIG_INFO = 99, COLUMN_MY_SIG_INFO_INTL = 100, COLUMN_MY_SOTA_REF = 101, COLUMN_MY_STATE = 102, COLUMN_MY_STREET = 103, COLUMN_MY_STREET_INTL = 104, COLUMN_MY_USACA_COUNTIES = 105, COLUMN_MY_VUCC_GRIDS = 106, COLUMN_NAME_INTL = 107, COLUMN_NOTES = 108, COLUMN_NOTES_INTL = 109, COLUMN_NR_BURSTS = 110, COLUMN_NR_PINGS = 111, COLUMN_OPERATOR = 112, COLUMN_OWNER_CALLSIGN = 113, COLUMN_PRECEDENCE = 114, COLUMN_PROP_MODE = 115, COLUMN_PUBLIC_KEY = 116, COLUMN_QRZCOM_QSO_UPLOAD_DATE = 117, COLUMN_QRZCOM_QSO_UPLOAD_STATUS = 118, COLUMN_QSLMSG = 119, COLUMN_QSLMSG_INTL = 120, COLUMN_QSL_RCVD_VIA = 121, COLUMN_QSL_SENT_VIA = 122, COLUMN_QSL_VIA = 123, COLUMN_QSO_COMPLETE = 124, COLUMN_QSO_RANDOM = 125, COLUMN_QTH_INTL = 126, COLUMN_REGION = 127, COLUMN_RIG = 128, COLUMN_RIG_INTL = 129, COLUMN_RX_PWR = 130, COLUMN_SAT_MODE = 131, COLUMN_SAT_NAME = 132, COLUMN_SFI = 133, COLUMN_SIG = 134, COLUMN_SIG_INTL = 135, COLUMN_SIG_INFO = 136, COLUMN_SIG_INFO_INTL = 137, COLUMN_SILENT_KEY = 138, COLUMN_SKCC = 139, COLUMN_SOTA_REF = 140, COLUMN_SRX = 141, COLUMN_SRX_STRING = 142, COLUMN_STATION_CALLSIGN = 143, COLUMN_STX = 144, COLUMN_STX_STRING = 145, COLUMN_SWL = 146, COLUMN_TEN_TEN = 147, COLUMN_UKSMG = 148, COLUMN_USACA_COUNTIES = 149, COLUMN_VE_PROV = 150, COLUMN_VUCC_GRIDS = 151, COLUMN_WEB = 152, COLUMN_MY_ARRL_SECT = 153, COLUMN_MY_WWFF_REF = 154, COLUMN_WWFF_REF = 155, COLUMN_ALTITUDE = 156, COLUMN_GRID_EXT = 157, COLUMN_HAMLOGEU_QSO_UPLOAD_DATE = 158, COLUMN_HAMLOGEU_QSO_UPLOAD_STATUS = 159, COLUMN_HAMQTH_QSO_UPLOAD_DATE = 160, COLUMN_HAMQTH_QSO_UPLOAD_STATUS = 161, COLUMN_MY_ALTITUDE = 162, COLUMN_MY_GRIDSQUARE_EXT = 163, COLUMN_MY_POTA_REF = 164, COLUMN_POTA_REF = 165, COLUMN_CNTY_ALT = 166, COLUMN_DCL_QSLRDATE = 167, COLUMN_DCL_QSLSDATE = 168, COLUMN_DCL_QSL_RCVD = 169, COLUMN_DCL_QSL_SENT = 170, COLUMN_MORSE_KEY_INFO = 171, COLUMN_MORSE_KEY_TYPE = 172, COLUMN_MY_CNTY_ALT = 173, COLUMN_MY_DARC_DOK = 174, COLUMN_MY_MORSE_KEY_INFO = 175, COLUMN_MY_MORSE_KEY_TYPE = 176, COLUMN_QRZCOM_QSO_DOWNLOAD_DATE = 177, COLUMN_QRZCOM_QSO_DOWNLOAD_STATUS = 178, COLUMN_QSLMSG_RCVD = 179, COLUMN_EQSL_AG = 180, COLUMN_LAST_ELEMENT = 181 }; private: static QMap fieldNameTranslationMap; public: static const QString getFieldNameTranslation(const LogbookModel::ColumnID key) { const QString value = fieldNameTranslationMap.value(key); return value.isEmpty() ? QString () : tr(value.toStdString().c_str()); } }; #endif // QLOG_MODELS_LOGBOOKMODEL_H ================================================ FILE: models/RigTypeModel.cpp ================================================ #include "RigTypeModel.h" #include "rig/Rig.h" RigTypeModel::RigTypeModel(QObject* parent) : QAbstractListModel(parent) { } int RigTypeModel::rowCount(const QModelIndex&) const { return rigList.count(); } QVariant RigTypeModel::data(const QModelIndex& index, int role) const { if (role == Qt::DisplayRole) { return rigList.value(index.row()); } if (role == Qt::UserRole ) { return rigIds[rigList.value(index.row())]; } return QVariant(); } QModelIndex RigTypeModel::index(int row, int column, const QModelIndex& parent) const { if (!hasIndex(row, column, parent)) { return QModelIndex(); } int rigId = rigIds[rigList.value(row)]; if (rigId) return createIndex(row, column, rigId); else return QModelIndex(); } void RigTypeModel::select(int driverID) { beginResetModel(); rigIds.clear(); rigList.clear(); if ( driverID == 0 ) return; const QList> models = Rig::instance()->getModelList(static_cast(driverID)); for ( const QPair &model : models ) { const QString &name = model.second; rigIds[name] = model.first; rigList.append(name); } rigList.sort(); endResetModel(); } ================================================ FILE: models/RigTypeModel.h ================================================ #ifndef QLOG_MODELS_RIGTYPEMODEL_H #define QLOG_MODELS_RIGTYPEMODEL_H #include #include class RigTypeModel : public QAbstractListModel { Q_OBJECT public: RigTypeModel(QObject* parent = 0); int rowCount(const QModelIndex& parent = QModelIndex()) const; QVariant data(const QModelIndex& index, int role) const; QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const; void select(int driverID); private: QStringList rigList; QMap rigIds; }; #endif // QLOG_MODELS_RIGTYPEMODEL_H ================================================ FILE: models/RotTypeModel.cpp ================================================ #include "rotator/Rotator.h" #include "RotTypeModel.h" RotTypeModel::RotTypeModel(QObject* parent) : QAbstractListModel(parent) { } int RotTypeModel::rowCount(const QModelIndex&) const { return rotList.count(); } QVariant RotTypeModel::data(const QModelIndex& index, int role) const { if (role == Qt::DisplayRole) { return rotList.value(index.row()); } if (role == Qt::UserRole ) { return rotIds[rotList.value(index.row())]; } return QVariant(); } QModelIndex RotTypeModel::index(int row, int column, const QModelIndex& parent) const { if (!hasIndex(row, column, parent)) { return QModelIndex(); } int rotId = rotIds[rotList.value(row)]; if (rotId) return createIndex(row, column, rotId); else return QModelIndex(); } void RotTypeModel::select(int driverID) { beginResetModel(); rotIds.clear(); rotList.clear(); if ( driverID == 0 ) return; const QList> models = Rotator::instance()->getModelList(static_cast(driverID)); for ( const QPair &model : models ) { const QString &name = model.second; rotIds[name] = model.first; rotList.append(name); } rotList.sort(); endResetModel(); } ================================================ FILE: models/RotTypeModel.h ================================================ #ifndef QLOG_MODELS_ROTTYPEMODEL_H #define QLOG_MODELS_ROTTYPEMODEL_H #include #include class RotTypeModel : public QAbstractListModel { Q_OBJECT public: RotTypeModel(QObject* parent = 0); int rowCount(const QModelIndex& parent = QModelIndex()) const; QVariant data(const QModelIndex& index, int role) const; QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const; void select(int driverID); private: QStringList rotList; QMap rotIds; }; #endif // QLOG_MODELS_RIGTYPEMODEL_H ================================================ FILE: models/SearchFilterProxyModel.cpp ================================================ #include "SearchFilterProxyModel.h" SearchFilterProxyModel::SearchFilterProxyModel(QObject *parent) : QSortFilterProxyModel{parent} {} void SearchFilterProxyModel::setSearchString(const QString &searchString) { this->searchString = searchString; invalidateFilter(); } void SearchFilterProxyModel::setSearchSkippedCols(const QVector &columns) { searchSkippedCols = columns; invalidateFilter(); } bool SearchFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { // full-text search for ( int col = 0; col < sourceModel()->columnCount(); ++col ) { if (searchSkippedCols.contains(col) ) continue; QModelIndex index = sourceModel()->index(source_row, col, source_parent); QString data = index.data(Qt::DisplayRole).toString(); if ( data.contains(searchString, Qt::CaseInsensitive) ) return true; } return false; } ================================================ FILE: models/SearchFilterProxyModel.h ================================================ #ifndef QLOG_MODELS_SEARCHFILTERPROXYMODEL_H #define QLOG_MODELS_SEARCHFILTERPROXYMODEL_H #include class SearchFilterProxyModel : public QSortFilterProxyModel { Q_OBJECT public: SearchFilterProxyModel(QObject* parent = nullptr); void setSearchString(const QString& searchString); void setSearchSkippedCols(const QVector &columns); protected: bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override; private: QString searchString; QVector searchSkippedCols; }; #endif // QLOG_MODELS_SEARCHFILTERPROXYMODEL_H ================================================ FILE: models/ShortcutEditorModel.cpp ================================================ #include "ShortcutEditorModel.h" ShortcutEditorModel::ShortcutEditorModel(const QList &actions, const QStringList &builtInStaticActions, QObject *parent) : QAbstractTableModel{parent}, actionList(actions), builtInStaticActionList(builtInStaticActions) { std::sort(actionList.begin(), actionList.end(), [](const QAction *a, const QAction *b) { return a->toolTip().localeAwareCompare(b->toolTip()) < 0; }); } int ShortcutEditorModel::rowCount(const QModelIndex &) const { return actionList.count(); } int ShortcutEditorModel::columnCount(const QModelIndex &) const { return 2; } QVariant ShortcutEditorModel::headerData(int section, Qt::Orientation orientation, int role) const { if ( role != Qt::DisplayRole || orientation != Qt::Horizontal ) return QVariant(); switch ( section ) { case COLUMN_DESCRIPTION: return tr("Description"); case COLUMN_SHORTCUT: return tr("Shortcut"); default: return QVariant(); } } QVariant ShortcutEditorModel::data(const QModelIndex &index, int role) const { QAction *action = actionList.at(index.row()); if ( !action ) return QVariant(); if ( role == Qt::DisplayRole ) { switch ( index.column() ) { case COLUMN_DESCRIPTION: return action->toolTip(); case COLUMN_SHORTCUT: return action->shortcut().toString(QKeySequence::NativeText); default: return QVariant(); } } return QVariant(); } bool ShortcutEditorModel::setData(const QModelIndex &index, const QVariant &value, int role) { if ( role == Qt::EditRole && index.column() == COLUMN_SHORTCUT ) { QAction *action = actionList.at(index.row()); const QString &newShortcutString = value.toString(); if ( !action ) return false; if ( newShortcutString.isEmpty() ) { action->setShortcut(QKeySequence()); emit dataChanged(index, index); return true; } if ( builtInStaticActionList.contains(newShortcutString) ) { emit conflictDetected(tr("Conflict with a built-in shortcut")); return false; } if ( findShortcut(actionList, action, newShortcutString) ) { emit conflictDetected(tr("Conflict with a user-defined shortcut")); return false; } action->setShortcut(QKeySequence(newShortcutString)); emit dataChanged(index, index); return true; } return QAbstractItemModel::setData(index, value, role); } Qt::ItemFlags ShortcutEditorModel::flags(const QModelIndex &index) const { if ( !index.isValid() ) return Qt::NoItemFlags; Qt::ItemFlags modelFlags = QAbstractItemModel::flags(index); if ( index.column() == COLUMN_SHORTCUT ) modelFlags |= Qt::ItemIsEditable; return modelFlags; } const QAction *ShortcutEditorModel::findShortcut(const QList &list, const QAction *currAction, const QString &shortcut) const { for ( const QAction* action : list ) { if ( action->shortcut().toString(QKeySequence::NativeText) == shortcut && action != currAction ) return action; } return nullptr; } ================================================ FILE: models/ShortcutEditorModel.h ================================================ #ifndef QLOG_MODELS_SHORTCUTEDITORMODEL_H #define QLOG_MODELS_SHORTCUTEDITORMODEL_H #include #include class ShortcutEditorModel : public QAbstractTableModel { Q_OBJECT public: explicit ShortcutEditorModel(const QList &actions, const QStringList &builtInStaticActions, QObject *parent = nullptr); int rowCount(const QModelIndex& parent = QModelIndex()) const override; int columnCount(const QModelIndex& = QModelIndex()) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex& index, int role) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; Qt::ItemFlags flags(const QModelIndex &index) const override; enum column_id { COLUMN_DESCRIPTION = 0, COLUMN_SHORTCUT = 1, }; signals: void conflictDetected(const QString &); private: QList actionList; QStringList builtInStaticActionList; const QAction *findShortcut(const QList &list, const QAction *currAction, const QString&) const; }; #endif // QLOG_MODELS_SHORTCUTEDITORMODEL_H ================================================ FILE: models/SqlListModel.cpp ================================================ #include #include #include #include "SqlListModel.h" SqlListModel::SqlListModel(const QString &query, const QString &placeholder, QObject *parent) : QSqlQueryModel(parent), placeholder(placeholder), stmt(query) { this->setQuery(stmt); } QVariant SqlListModel::headerData(int section, Qt::Orientation orientation, int role) const { Q_UNUSED(section) Q_UNUSED(orientation) Q_UNUSED(role) return QVariant(); } int SqlListModel::rowCount(const QModelIndex &parent) const { // For list models only the root node (an invalid parent) should return the list's size. For all // other (valid) parents, rowCount() should return 0 so that it does not become a tree model. if (parent.isValid()) return 0; return QSqlQueryModel::rowCount(parent) + (!placeholder.isEmpty() ? 1 : 0); } QVariant SqlListModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); const int col = index.column(); const int row = index.row(); const bool hasPlaceholder = !placeholder.isEmpty(); const bool isTargetColumn = (col >= 0 && col <= 2); const int realRow = row - (hasPlaceholder ? 1 : 0); if (!isTargetColumn) return QVariant(); if ( role == Qt::DisplayRole ) { if ( hasPlaceholder && row == 0 ) return placeholder; const QModelIndex &sqlIndex = QSqlQueryModel::index(realRow, col); return QSqlQueryModel::data(sqlIndex, role); } // get row index; if (role == Qt::UserRole) return row - (hasPlaceholder ? 1 : 0); // get (role - UserRole) Column if (role >= Qt::UserRole + 1) { if (hasPlaceholder && row == 0) return QVariant(); const QModelIndex &sqlIndex = QSqlQueryModel::index(realRow, role - 1 - Qt::UserRole); return QSqlQueryModel::data(sqlIndex, Qt::DisplayRole); } return QVariant(); } void SqlListModel::refresh() { setQuery(stmt); } ================================================ FILE: models/SqlListModel.h ================================================ #ifndef QLOG_MODELS_SQLLISTMODEL_H #define QLOG_MODELS_SQLLISTMODEL_H #include class SqlListModel : public QSqlQueryModel { Q_OBJECT public: explicit SqlListModel(const QString &, const QString &, QObject *parent = nullptr); // Header: QVariant headerData(int, Qt::Orientation, int role = Qt::DisplayRole) const override; // Basic functionality: int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &, int role = Qt::DisplayRole) const override; void refresh(); private: QString placeholder; QString stmt; }; #endif // QLOG_MODELS_SQLLISTMODEL_H ================================================ FILE: models/WsjtxTableModel.cpp ================================================ #include #include "WsjtxTableModel.h" #include "data/Data.h" bool operator==(const WsjtxEntry& a, const WsjtxEntry& b) { return a.callsign == b.callsign; } int WsjtxTableModel::rowCount(const QModelIndex&) const { return wsjtxData.count(); } int WsjtxTableModel::columnCount(const QModelIndex&) const { return 7; } QVariant WsjtxTableModel::data(const QModelIndex& index, int role) const { WsjtxEntry entry = wsjtxData.at(index.row()); if (role == Qt::DisplayRole) { switch ( index.column() ) { case COLUMN_CALLSIGN: return entry.callsign; case COLUMN_GRID: return entry.grid; case COLUMN_DISTANCE: if ( entry.distance > 0.0 ) return entry.distance; else return QVariant(); case COLUMN_SNR: return QString::number(entry.decode.snr); case COLUMN_LAST_ACTIVITY: return entry.decode.time.toString(); case COLUMN_LAST_MESSAGE: return entry.comment; case COLUMN_MEMBER: return entry.memberList2StringList().join(", "); default: return QVariant(); } } else if (index.column() == COLUMN_CALLSIGN && role == Qt::BackgroundRole) { return Data::statusToColor(entry.status, entry.dupeCount, QColor(Qt::transparent)); } else if (index.column() > COLUMN_CALLSIGN && role == Qt::BackgroundRole) { if ( entry.receivedTime.secsTo(QDateTime::currentDateTimeUtc()) >= spotPeriod * 0.8) /* -20% time of period because WSTX sends messages in waves and not exactly in time period */ { return QColor(Qt::darkGray); } } else if (index.column() == COLUMN_CALLSIGN && role == Qt::ForegroundRole) { //return Data::statusToInverseColor(entry.status, QColor(Qt::black)); } else if (index.column() == COLUMN_CALLSIGN && role == Qt::ToolTipRole) { return QCoreApplication::translate("DBStrings", entry.dxcc.country.toUtf8().constData()) + " [" + Data::statusToText(entry.status) + "]"; } else if ( role == Qt::UserRole ) { switch ( index.column() ) { case COLUMN_DISTANCE: return data(index, Qt::DisplayRole).toDouble(); break; case COLUMN_SNR: return data(index, Qt::DisplayRole).toInt(); break; case COLUMN_LAST_ACTIVITY: return data(index, Qt::DisplayRole).toTime(); break; default: return data(index, Qt::DisplayRole); } } return QVariant(); } QVariant WsjtxTableModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole || orientation != Qt::Horizontal) return QVariant(); switch (section) { case COLUMN_CALLSIGN: return tr("Callsign"); case COLUMN_GRID: return tr("Gridsquare"); case COLUMN_DISTANCE: return tr("Distance"); case COLUMN_SNR: return tr("SNR"); case COLUMN_LAST_ACTIVITY: return tr("Last Activity"); case COLUMN_LAST_MESSAGE: return tr("Last Message"); case COLUMN_MEMBER: return tr("Member"); default: return QVariant(); } } void WsjtxTableModel::addOrReplaceEntry(const WsjtxEntry &entry) { int idx = wsjtxData.indexOf(entry); if ( idx >= 0 ) { if ( ! entry.grid.isEmpty() ) { wsjtxData[idx].grid = entry.grid; } wsjtxData[idx].status = entry.status; wsjtxData[idx].decode = entry.decode; wsjtxData[idx].receivedTime = entry.receivedTime; wsjtxData[idx].dupeCount = entry.dupeCount; // does not update club info emit dataChanged(createIndex(idx,0), createIndex(idx,4)); } else { beginInsertRows(QModelIndex(), wsjtxData.count(), wsjtxData.count()); wsjtxData.append(entry); endInsertRows(); } } void WsjtxTableModel::spotAging() { beginResetModel(); QMutableListIterator entry(wsjtxData); while ( entry.hasNext() ) { const WsjtxEntry ¤t = entry.next(); // keep the entry longer than the spotPeriod, because it is used for querying from the Map. if ( current.receivedTime.secsTo(QDateTime::currentDateTimeUtc()) > 3 * 60 ) entry.remove(); } endResetModel(); } bool WsjtxTableModel::callsignExists(const WsjtxEntry &call) { return wsjtxData.contains(call); } const WsjtxEntry WsjtxTableModel::getEntry(QModelIndex idx) const { return wsjtxData.at(idx.row()); } const WsjtxEntry WsjtxTableModel::getEntry(const QString &callsign) const { WsjtxEntry entry; entry.callsign = callsign; int index = wsjtxData.indexOf(entry); return (index < 0) ? WsjtxEntry() : wsjtxData.at(index); } void WsjtxTableModel::setCurrentSpotPeriod(float period) { spotPeriod = period; } void WsjtxTableModel::clear() { beginResetModel(); wsjtxData.clear(); endResetModel(); } void WsjtxTableModel::removeSpot(const QString &callsign) { beginResetModel(); QMutableListIterator entry(wsjtxData); while ( entry.hasNext() ) { if ( entry.next().callsign == callsign ) entry.remove(); } endResetModel(); } ================================================ FILE: models/WsjtxTableModel.h ================================================ #ifndef QLOG_MODELS_WSJTXTABLEMODEL_H #define QLOG_MODELS_WSJTXTABLEMODEL_H #include #include "data/WsjtxEntry.h" class WsjtxTableModel : public QAbstractTableModel { Q_OBJECT public: enum column_id { COLUMN_CALLSIGN = 0, COLUMN_GRID = 1, COLUMN_DISTANCE = 2, COLUMN_SNR = 3, COLUMN_LAST_ACTIVITY = 4, COLUMN_LAST_MESSAGE = 5, COLUMN_MEMBER = 6, }; WsjtxTableModel(QObject* parent = nullptr) : QAbstractTableModel(parent) {spotPeriod = 120;} int rowCount(const QModelIndex& parent = QModelIndex()) const; int columnCount(const QModelIndex& parent = QModelIndex()) const; QVariant data(const QModelIndex& index, int role) const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; void addOrReplaceEntry(const WsjtxEntry &entry); void spotAging(); bool callsignExists(const WsjtxEntry &); const WsjtxEntry getEntry(const QString &callsign) const; const WsjtxEntry getEntry(QModelIndex idx) const; void setCurrentSpotPeriod(float); void clear(); void removeSpot(const QString &callsign); private: QList wsjtxData; float spotPeriod; }; #endif // QLOG_MODELS_WSJTXTABLEMODEL_H ================================================ FILE: res/data/contests.json ================================================ [ {"id": "070-160M-SPRINT", "name": "PODXS Great Pumpkin Sprint"}, {"id": "070-3-DAY", "name": "PODXS Three Day Weekend"}, {"id": "070-31-FLAVORS", "name": "PODXS 31 Flavors"}, {"id": "070-40M-SPRINT", "name": "PODXS 40m Firecracker Sprint"}, {"id": "070-80M-SPRINT", "name": "PODXS 80m Jay Hudak Memorial Sprint"}, {"id": "070-PSKFEST", "name": "PODXS PSKFest"}, {"id": "070-ST-PATS-DAY", "name": "PODXS St. Patricks Day"}, {"id": "070-VALENTINE-SPRINT", "name": "PODXS Valentine Sprint"}, {"id": "10-RTTY", "name": "Ten-Meter RTTY Contest (2011 onwards)"}, {"id": "1010-OPEN-SEASON", "name": "Open Season Ten Meter QSO Party"}, {"id": "7QP", "name": "7th-Area QSO Party"}, {"id": "AL-QSO-PARTY", "name": "Alabama QSO Party"}, {"id": "ALL-ASIAN-DX-CW", "name": "JARL All Asian DX Contest (CW)"}, {"id": "ALL-ASIAN-DX-PHONE", "name": "JARL All Asian DX Contest (PHONE)"}, {"id": "ANARTS-RTTY", "name": "ANARTS WW RTTY"}, {"id": "ANATOLIAN-RTTY", "name": "Anatolian WW RTTY"}, {"id": "AP-SPRINT", "name": "Asia - Pacific Sprint"}, {"id": "AR-QSO-PARTY", "name": "Arkansas QSO Party"}, {"id": "ARI-DX", "name": "ARI DX Contest"}, {"id": "ARI-EME", "name": "ARI Italian EME Trophy"}, {"id": "ARI-IAC-13CM", "name": "ARI Italian Activity Contest (13cm+)"}, {"id": "ARI-IAC-23CM", "name": "ARI Italian Activity Contest (23cm)"}, {"id": "ARI-IAC-6M", "name": "ARI Italian Activity Contest (6m)"}, {"id": "ARI-IAC-UHF", "name": "ARI Italian Activity Contest (UHF)"}, {"id": "ARI-IAC-VHF", "name": "ARI Italian Activity Contest (VHF)"}, {"id": "ARRL-10", "name": "ARRL 10 Meter Contest"}, {"id": "ARRL-160", "name": "ARRL 160 Meter Contest"}, {"id": "ARRL-222", "name": "ARRL 222 MHz and Up Distance Contest"}, {"id": "ARRL-DX-CW", "name": "ARRL International DX Contest (CW)"}, {"id": "ARRL-DX-SSB", "name": "ARRL International DX Contest (Phone)"}, {"id": "ARRL-EME", "name": "ARRL EME contest"}, {"id": "ARRL-FIELD-DAY", "name": "ARRL Field Day"}, {"id": "ARRL-RR-CW", "name": "ARRL Rookie Roundup (CW)"}, {"id": "ARRL-RR-RTTY", "name": "ARRL Rookie Roundup (RTTY)"}, {"id": "ARRL-RR-SSB", "name": "ARRL Rookie Roundup (Phone)"}, {"id": "ARRL-RTTY", "name": "ARRL RTTY Round-Up"}, {"id": "ARRL-SCR", "name": "ARRL School Club Roundup"}, {"id": "ARRL-SS-CW", "name": "ARRL November Sweepstakes (CW)"}, {"id": "ARRL-SS-SSB", "name": "ARRL November Sweepstakes (Phone)"}, {"id": "ARRL-UHF-AUG", "name": "ARRL August UHF Contest"}, {"id": "ARRL-VHF-JAN", "name": "ARRL January VHF Sweepstakes"}, {"id": "ARRL-VHF-JUN", "name": "ARRL June VHF QSO Party"}, {"id": "ARRL-VHF-SEP", "name": "ARRL September VHF QSO Party"}, {"id": "AZ-QSO-PARTY", "name": "Arizona QSO Party"}, {"id": "BANGGAI-DX", "name": "ORARI Banggai DX Contest"}, {"id": "BARTG-RTTY", "name": "BARTG Spring RTTY Contest"}, {"id": "BARTG-SPRINT", "name": "BARTG Sprint Contest"}, {"id": "BC-QSO-PARTY", "name": "British Columbia QSO Party"}, {"id": "BEKASI-MERDEKA-CONTEST", "name": "ORARI Bekasi Merdeka Contest"}, {"id": "CA-QSO-PARTY", "name": "California QSO Party"}, {"id": "CIS-DX", "name": "CIS DX Contest"}, {"id": "CO-QSO-PARTY", "name": "Colorado QSO Party"}, {"id": "CQ-160-CW", "name": "CQ WW 160 Meter DX Contest (CW)"}, {"id": "CQ-160-SSB", "name": "CQ WW 160 Meter DX Contest (SSB)"}, {"id": "CQ-M", "name": "CQ-M International DX Contest"}, {"id": "CQ-VHF", "name": "CQ World-Wide VHF Contest"}, {"id": "CQ-WPX-CW", "name": "CQ WW WPX Contest (CW)"}, {"id": "CQ-WPX-RTTY", "name": "CQ/RJ WW RTTY WPX Contest"}, {"id": "CQ-WPX-SSB", "name": "CQ WW WPX Contest (SSB)"}, {"id": "CQ-WW-CW", "name": "CQ WW DX Contest (CW)"}, {"id": "CQ-WW-RTTY", "name": "CQ/RJ WW RTTY DX Contest"}, {"id": "CQ-WW-SSB", "name": "CQ WW DX Contest (SSB)"}, {"id": "CT-QSO-PARTY", "name": "Connecticut QSO Party"}, {"id": "CVA-DX-CW", "name": "Concurso Verde e Amarelo DX CW Contest"}, {"id": "CVA-DX-SSB", "name": "Concurso Verde e Amarelo DX CW Contest"}, {"id": "CWOPS-CW-OPEN", "name": "CWops CW Open Competition"}, {"id": "CWOPS-CWT", "name": "CWops Mini-CWT Test"}, {"id": "DARC-10", "name": "DARC 10m Contest"}, {"id": "DARC-CWA", "name": "DARC CW Trainee Contest"}, {"id": "DARC-FT4", "name": "DARC FT4 Contest"}, {"id": "DARC-HELL", "name": "DARC Hell Contest"}, {"id": "DARC-MICROWAVE", "name": "DARC Microwave Contest"}, {"id": "DARC-TRAINEE", "name": "DARC Trainee Contest"}, {"id": "DARC-UKW-SPRING", "name": "DARC UKW Spring Contest"}, {"id": "DARC-UKW-FIELD-DAY", "name": "DARC UKW Summer Contest"}, {"id": "DARC-VHF-UHF-MICROWAVE", "name": "DARC VHF-, UHF-, Microwave Contest (May)"}, {"id": "DARC-WAEDC-CW", "name": "WAE DX Contest (CW)"}, {"id": "DARC-WAEDC-RTTY", "name": "WAE DX Contest (RTTY)"}, {"id": "DARC-WAEDC-SSB", "name": "WAE DX Contest (SSB)"}, {"id": "DARC-WAG", "name": "DARC Worked All Germany"}, {"id": "DE-QSO-PARTY", "name": "Delaware QSO Party"}, {"id": "DL-DX-RTTY", "name": "DL-DX RTTY Contest"}, {"id": "DMC-RTTY", "name": "DMC RTTY Contest"}, {"id": "EA-CNCW", "name": "Concurso Nacional de Telegrafía"}, {"id": "EA-DME", "name": "Municipios Españoles"}, {"id": "EA-PSK63", "name": "EA PSK63"}, {"id": "EA-SMRE-CW", "name": "Su Majestad El Rey de España - CW"}, {"id": "EA-SMRE-SSB", "name": "Su Majestad El Rey de España - SSB"}, {"id": "EA-VHF-ATLANTIC", "name": "Atlántico V-UHF"}, {"id": "EA-VHF-COM", "name": "Combinado de V-UHF"}, {"id": "EA-VHF-COSTA-SOL", "name": "Costa del Sol V-UHF"}, {"id": "EA-VHF-EA", "name": "Nacional VHF"}, {"id": "EA-VHF-EA1RCS", "name": "Segovia EA1RCS V-UHF"}, {"id": "EA-VHF-QSL", "name": "QSL V-UHF & 50MHz"}, {"id": "EA-VHF-SADURNI", "name": "Sant Sadurni V-UHF"}, {"id": "EA-WW-RTTY", "name": "Unión de Radioaficionados Españoles RTTY Contest"}, {"id": "EASTER", "name": "DARC Easter Contest"}, {"id": "EPC-PSK63", "name": "PSK63 QSO Party"}, {"id": "EU Sprint", "name": "EU Sprint"}, {"id": "EU-HF", "name": "EU HF Championship"}, {"id": "EU-PSK-DX", "name": "EU PSK DX Contest"}, {"id": "EUCW160M", "name": "European CW Association 160m CW Party"}, {"id": "FALL SPRINT", "name": "FISTS Fall Sprint"}, {"id": "FL-QSO-PARTY", "name": "Florida QSO Party"}, {"id": "GA-QSO-PARTY", "name": "Georgia QSO Party"}, {"id": "HA-DX", "name": "Hungarian DX Contest"}, {"id": "HELVETIA", "name": "Helvetia Contest"}, {"id": "HI-QSO-PARTY", "name": "Hawaiian QSO Party"}, {"id": "HOLYLAND", "name": "IARC Holyland Contest"}, {"id": "IA-QSO-PARTY", "name": "Iowa QSO Party"}, {"id": "IARU-FIELD-DAY", "name": "DARC IARU Region 1 Field Day"}, {"id": "IARU-HF", "name": "IARU HF World Championship"}, {"id": "ID-QSO-PARTY", "name": "Idaho QSO Party"}, {"id": "IL QSO Party", "name": "Illinois QSO Party"}, {"id": "IN-QSO-PARTY", "name": "Indiana QSO Party"}, {"id": "JARTS-WW-RTTY", "name": "JARTS WW RTTY"}, {"id": "JIDX-CW", "name": "Japan International DX Contest (CW)"}, {"id": "JIDX-SSB", "name": "Japan International DX Contest (SSB)"}, {"id": "JT-DX-RTTY", "name": "Mongolian RTTY DX Contest"}, {"id": "K1USN-SSO", "name": "K1USN Slow Speed Open"}, {"id": "KS-QSO-PARTY", "name": "Kansas QSO Party"}, {"id": "KY-QSO-PARTY", "name": "Kentucky QSO Party"}, {"id": "LA-QSO-PARTY", "name": "Louisiana QSO Party"}, {"id": "LDC-RTTY", "name": "DRCG Long Distance Contest (RTTY)"}, {"id": "LZ DX", "name": "LZ DX Contest"}, {"id": "MAR-QSO-PARTY", "name": "Maritimes QSO Party"}, {"id": "MD-QSO-PARTY", "name": "Maryland QSO Party"}, {"id": "ME-QSO-PARTY", "name": "Maine QSO Party"}, {"id": "MI-QSO-PARTY", "name": "Michigan QSO Party"}, {"id": "MIDATLANTIC-QSO-PARTY", "name": "Mid-Atlantic QSO Party"}, {"id": "MN-QSO-PARTY", "name": "Minnesota QSO Party"}, {"id": "MO-QSO-PARTY", "name": "Missouri QSO Party"}, {"id": "MS-QSO-PARTY", "name": "Mississippi QSO Party"}, {"id": "MT-QSO-PARTY", "name": "Montana QSO Party"}, {"id": "NA-SPRINT-CW", "name": "North America Sprint (CW)"}, {"id": "NA-SPRINT-RTTY", "name": "North America Sprint (RTTY)"}, {"id": "NA-SPRINT-SSB", "name": "North America Sprint (Phone)"}, {"id": "NAQP-CW", "name": "North America QSO Party (CW)"}, {"id": "NAQP-RTTY", "name": "North America QSO Party (RTTY)"}, {"id": "NAQP-SSB", "name": "North America QSO Party (Phone)"}, {"id": "NAVAL", "name": "International Naval Contest (INC)"}, {"id": "NC-QSO-PARTY", "name": "North Carolina QSO Party"}, {"id": "ND-QSO-PARTY", "name": "North Dakota QSO Party"}, {"id": "NE-QSO-PARTY", "name": "Nebraska QSO Party"}, {"id": "NEQP", "name": "New England QSO Party"}, {"id": "NH-QSO-PARTY", "name": "New Hampshire QSO Party"}, {"id": "NJ-QSO-PARTY", "name": "New Jersey QSO Party"}, {"id": "NM-QSO-PARTY", "name": "New Mexico QSO Party"}, {"id": "NRAU-BALTIC-CW", "name": "NRAU-Baltic Contest (CW)"}, {"id": "NRAU-BALTIC-SSB", "name": "NRAU-Baltic Contest (SSB)"}, {"id": "NV-QSO-PARTY", "name": "Nevada QSO Party"}, {"id": "NY-QSO-PARTY", "name": "New York QSO Party"}, {"id": "OCEANIA-DX-CW", "name": "Oceania DX Contest (CW)"}, {"id": "OCEANIA-DX-SSB", "name": "Oceania DX Contest (SSB)"}, {"id": "OH-QSO-PARTY", "name": "Ohio QSO Party"}, {"id": "OK-DX-RTTY", "name": "Czech Radio Club OK DX Contest"}, {"id": "OK-OM-DX", "name": "Czech Radio Club OK-OM DX Contest"}, {"id": "OK-QSO-PARTY", "name": "Oklahoma QSO Party"}, {"id": "OMISS-QSO-PARTY", "name": "Old Man International Sideband Society QSO Party"}, {"id": "ON-QSO-PARTY", "name": "Ontario QSO Party"}, {"id": "OR-QSO-PARTY", "name": "Oregon QSO Party"}, {"id": "ORARI-DX", "name": "ORARI DX Contest"}, {"id": "PA-QSO-PARTY", "name": "Pennsylvania QSO Party"}, {"id": "PACC", "name": "Dutch PACC Contest"}, {"id": "PCC", "name": "PCC Pro CW Contest"}, {"id": "QC-QSO-PARTY", "name": "Quebec QSO Party"}, {"id": "RAC-CANADA-DAY", "name": "RAC Canada Day Contest"}, {"id": "RAC-CANADA-WINTER", "name": "RAC Canada Winter Contest"}, {"id": "RDAC", "name": "Russian District Award Contest"}, {"id": "RDXC", "name": "Russian DX Contest"}, {"id": "REF-160M", "name": "Reseau des Emetteurs Francais 160m Contest"}, {"id": "REF-CW", "name": "Reseau des Emetteurs Francais Contest (CW)"}, {"id": "REF-SSB", "name": "Reseau des Emetteurs Francais Contest (SSB)"}, {"id": "REP-PORTUGAL-DAY-HF", "name": "Rede dos Emissores Portugueses Portugal Day HF Contest"}, {"id": "RI-QSO-PARTY", "name": "Rhode Island QSO Party"}, {"id": "RSGB-160", "name": "1.8MHz Contest"}, {"id": "RSGB-21/28-CW", "name": "21/28 MHz Contest (CW)"}, {"id": "RSGB-21/28-SSB", "name": "21/28 MHz Contest (SSB)"}, {"id": "RSGB-80M-CC", "name": "80m Club Championships"}, {"id": "RSGB-AFS-CW", "name": "Affiliated Societies Team Contest (CW)"}, {"id": "RSGB-AFS-SSB", "name": "Affiliated Societies Team Contest (SSB)"}, {"id": "RSGB-CLUB-CALLS", "name": "Club Calls"}, {"id": "RSGB-COMMONWEALTH", "name": "Commonwealth Contest"}, {"id": "RSGB-IOTA", "name": "IOTA Contest"}, {"id": "RSGB-LOW-POWER", "name": "Low Power Field Day"}, {"id": "RSGB-NFD", "name": "National Field Day"}, {"id": "RSGB-ROPOCO", "name": "RoPoCo"}, {"id": "RSGB-SSB-FD", "name": "SSB Field Day"}, {"id": "RUSSIAN-RTTY", "name": "Russian Radio RTTY Worldwide Contest"}, {"id": "SAC-CW", "name": "Scandinavian Activity Contest (CW)"}, {"id": "SAC-SSB", "name": "Scandinavian Activity Contest (SSB)"}, {"id": "SARTG-RTTY", "name": "SARTG WW RTTY"}, {"id": "SC-QSO-PARTY", "name": "South Carolina QSO Party"}, {"id": "SCC-RTTY", "name": "SCC RTTY Championship"}, {"id": "SD-QSO-PARTY", "name": "South Dakota QSO Party"}, {"id": "ShortRY", "name": "DARC RTTY Short Contest"}, {"id": "SMP-AUG", "name": "SSA Portabeltest"}, {"id": "SMP-MAY", "name": "SSA Portabeltest"}, {"id": "SP-DX-RTTY", "name": "PRC SPDX Contest (RTTY)"}, {"id": "SPAR-WINTER-FD", "name": "SPAR Winter Field Day"}, {"id": "SPDXContest", "name": "SP DX Contest"}, {"id": "SPRING SPRINT", "name": "FISTS Spring Sprint"}, {"id": "SR-MARATHON", "name": "Scottish-Russian Marathon"}, {"id": "STEW-PERRY", "name": "Stew Perry Topband Distance Challenge"}, {"id": "SUMMER SPRINT", "name": "FISTS Summer Sprint"}, {"id": "TARA-GRID-DIP", "name": "TARA Grid Dip PSK-RTTY Shindig"}, {"id": "TARA-RTTY", "name": "TARA RTTY Mêlée"}, {"id": "TARA-RUMBLE", "name": "TARA Rumble PSK Contest"}, {"id": "TARA-SKIRMISH", "name": "TARA Skirmish Digital Prefix Contest"}, {"id": "TMC-RTTY", "name": "The Makrothen Contest"}, {"id": "TN-QSO-PARTY", "name": "Tennessee QSO Party"}, {"id": "TX-QSO-PARTY", "name": "Texas QSO Party"}, {"id": "UBA-DX-CW", "name": "UBA Contest (CW)"}, {"id": "UBA-DX-SSB", "name": "UBA Contest (SSB)"}, {"id": "UK-DX-BPSK63", "name": "European PSK Club BPSK63 Contest"}, {"id": "UK-DX-RTTY", "name": "UK DX RTTY Contest"}, {"id": "UKR-CHAMP-RTTY", "name": "Open Ukraine RTTY Championship"}, {"id": "UKRAINIAN DX", "name": "Ukrainian DX"}, {"id": "UKSMG-6M-MARATHON", "name": "UKSMG 6m Marathon"}, {"id": "UKSMG-SUMMER-ES", "name": "UKSMG Summer Es Contest"}, {"id": "US-COUNTIES-QSO", "name": "Mobile Amateur Awards Club"}, {"id": "UT-QSO-PARTY", "name": "Utah QSO Party"}, {"id": "VA-QSO-PARTY", "name": "Virginia QSO Party"}, {"id": "VENEZ-IND-DAY", "name": "RCV Venezuelan Independence Day Contest"}, {"id": "VIRGINIA QSO PARTY (import-only)", "name": "Virginia QSO Party"}, {"id": "VOLTA-RTTY", "name": "Alessandro Volta RTTY DX Contest"}, {"id": "WA-QSO-PARTY", "name": "Washington QSO Party"}, {"id": "WI-QSO-PARTY", "name": "Wisconsin QSO Party"}, {"id": "WIA-HARRY ANGEL", "name": "WIA Harry Angel Memorial 80m Sprint"}, {"id": "WIA-JMMFD", "name": "WIA John Moyle Memorial Field Day"}, {"id": "WIA-OCDX", "name": "WIA Oceania DX (OCDX) Contest"}, {"id": "WIA-REMEMBRANCE", "name": "WIA Remembrance Day"}, {"id": "WIA-ROSS HULL", "name": "WIA Ross Hull Memorial VHF/UHF Contest"}, {"id": "WIA-TRANS TASMAN", "name": "WIA Trans Tasman Low Bands Challenge"}, {"id": "WIA-VHF/UHF FD", "name": "WIA VHF UHF Field Days"}, {"id": "WIA-VK SHIRES", "name": "WIA VK Shires"}, {"id": "WINTER SPRINT", "name": "FISTS Winter Sprint"}, {"id": "WV-QSO-PARTY", "name": "West Virginia QSO Party"}, {"id": "WY-QSO-PARTY", "name": "Wyoming QSO Party"}, {"id": "XE-INTL-RTTY", "name": "Mexico International Contest (RTTY)"}, {"id": "YOHFDX", "name": "YODX HF contest"}, {"id": "YUDXC", "name": "YU DX Contest"} ] ================================================ FILE: res/data/dxcc.json ================================================ [ {"id":1,"name":"Canada","cqz":5,"ituz":9,"flag":"CA"}, {"id":2,"name":"Abu Ail Is.","cqz":21,"ituz":39,"flag":null}, {"id":3,"name":"Afghanistan","cqz":21,"ituz":40,"flag":"AF"}, {"id":4,"name":"Agalega & St. Brandon","cqz":39,"ituz":53,"flag":"MP"}, {"id":5,"name":"Aland Islands","cqz":15,"ituz":18,"flag":"AX"}, {"id":6,"name":"Alaska","cqz":1,"ituz":1,"flag":"US"}, {"id":7,"name":"Albania","cqz":15,"ituz":28,"flag":"AL"}, {"id":8,"name":"Aldabra","cqz":39,"ituz":53,"flag":"SC"}, {"id":9,"name":"American Samoa","cqz":32,"ituz":62,"flag":"AS"}, {"id":10,"name":"Amsterdam & St. Paul Is.","cqz":39,"ituz":68,"flag":"FR"}, {"id":11,"name":"Andaman & Nicobar Is.","cqz":26,"ituz":49,"flag":"IN"}, {"id":12,"name":"Anguilla","cqz":8,"ituz":11,"flag":"AI"}, {"id":13,"name":"Antarctica","cqz":13,"ituz":74,"flag":"AQ"}, {"id":14,"name":"Armenia","cqz":21,"ituz":29,"flag":"AM"}, {"id":15,"name":"Asiatic Russia","cqz":17,"ituz":30,"flag":"RU"}, {"id":16,"name":"N.Z. Subantarctic Is.","cqz":32,"ituz":60,"flag":"NZ"}, {"id":17,"name":"Aves Island","cqz":8,"ituz":11,"flag":"VE"}, {"id":18,"name":"Azerbaijan","cqz":21,"ituz":29,"flag":"AZ"}, {"id":19,"name":"Bajo Nuevo","cqz":8,"ituz":11,"flag":"CO"}, {"id":20,"name":"Baker & Howland Islands","cqz":31,"ituz":61,"flag":"US"}, {"id":21,"name":"Balearic Islands","cqz":14,"ituz":37,"flag":"ES"}, {"id":22,"name":"Palau","cqz":27,"ituz":64,"flag":"PW"}, {"id":23,"name":"Blenheim Reef","cqz":39,"ituz":41,"flag":"MU"}, {"id":24,"name":"Bouvet","cqz":38,"ituz":67,"flag":"NO"}, {"id":25,"name":"British North Borneo","cqz":28,"ituz":54,"flag":"GB"}, {"id":26,"name":"British Somaliland","cqz":37,"ituz":48,"flag":"GB"}, {"id":27,"name":"Belarus","cqz":16,"ituz":29,"flag":"BY"}, {"id":28,"name":"Canal Zone","cqz":7,"ituz":11,"flag":null}, {"id":29,"name":"Canary Islands","cqz":33,"ituz":36,"flag":"IC"}, {"id":30,"name":"Celebe & Molucca Is.","cqz":28,"ituz":54,"flag":"ID"}, {"id":31,"name":"Central Kiribati","cqz":31,"ituz":62,"flag":"KI"}, {"id":32,"name":"Ceuta & Melilla","cqz":33,"ituz":37,"flag":"ES"}, {"id":33,"name":"Chagos Islands","cqz":39,"ituz":41,"flag":"GB"}, {"id":34,"name":"Chatham Islands","cqz":32,"ituz":60,"flag":"NZ"}, {"id":35,"name":"Christmas Island","cqz":29,"ituz":54,"flag":"CX"}, {"id":36,"name":"Clipperton Island","cqz":7,"ituz":10,"flag":"FR"}, {"id":37,"name":"Cocos Island","cqz":7,"ituz":11,"flag":"CR"}, {"id":38,"name":"Cocos (Keeling) Islands","cqz":29,"ituz":54,"flag":"CC"}, {"id":39,"name":"Comoros","cqz":39,"ituz":53,"flag":null}, {"id":40,"name":"Crete","cqz":20,"ituz":28,"flag":"GR"}, {"id":41,"name":"Crozet Island","cqz":39,"ituz":68,"flag":"FR"}, {"id":42,"name":"Damao Diu","cqz":22,"ituz":41,"flag":null}, {"id":43,"name":"Desecheo Island","cqz":8,"ituz":11,"flag":"PR"}, {"id":44,"name":"Desroches","cqz":39,"ituz":53,"flag":null}, {"id":45,"name":"Dodecanese","cqz":20,"ituz":28,"flag":"GR"}, {"id":46,"name":"East Malaysia","cqz":28,"ituz":54,"flag":"MY"}, {"id":47,"name":"Easter Island","cqz":12,"ituz":63,"flag":"CL"}, {"id":48,"name":"Eastern Kiribati","cqz":31,"ituz":61,"flag":"KI"}, {"id":49,"name":"Equatorial Guinea","cqz":36,"ituz":47,"flag":"GQ"}, {"id":50,"name":"Mexico","cqz":6,"ituz":10,"flag":"MX"}, {"id":51,"name":"Eritrea","cqz":37,"ituz":48,"flag":"ER"}, {"id":52,"name":"Estonia","cqz":15,"ituz":29,"flag":"EE"}, {"id":53,"name":"Ethiopia","cqz":37,"ituz":48,"flag":"ET"}, {"id":54,"name":"European Russia","cqz":16,"ituz":29,"flag":"RU"}, {"id":55,"name":"Farquhar","cqz":39,"ituz":53,"flag":"SC"}, {"id":56,"name":"Fernando de Noronha","cqz":11,"ituz":13,"flag":"BR"}, {"id":57,"name":"Fr. Equatorial Africa","cqz":36,"ituz":47,"flag":"FR"}, {"id":58,"name":"French Indo-China","cqz":26,"ituz":49,"flag":"FR"}, {"id":59,"name":"French West Africa","cqz":35,"ituz":46,"flag":"FR"}, {"id":60,"name":"Bahamas","cqz":8,"ituz":11,"flag":"BS"}, {"id":61,"name":"Franz Josef Land","cqz":40,"ituz":75,"flag":"RU"}, {"id":62,"name":"Barbados","cqz":8,"ituz":11,"flag":"BB"}, {"id":63,"name":"French Guiana","cqz":9,"ituz":12,"flag":"FR"}, {"id":64,"name":"Bermuda","cqz":5,"ituz":11,"flag":"BM"}, {"id":65,"name":"British Virgin Islands","cqz":8,"ituz":11,"flag":"VG"}, {"id":66,"name":"Belize","cqz":7,"ituz":11,"flag":"BZ"}, {"id":67,"name":"French India","cqz":22,"ituz":41,"flag":"FR"}, {"id":68,"name":"Kuwait/Saudi Arabia Neut. Zone","cqz":21,"ituz":39,"flag":null}, {"id":69,"name":"Cayman Islands","cqz":8,"ituz":11,"flag":"KY"}, {"id":70,"name":"Cuba","cqz":8,"ituz":11,"flag":"CU"}, {"id":71,"name":"Galapagos Islands","cqz":10,"ituz":12,"flag":"EC"}, {"id":72,"name":"Dominican Republic","cqz":8,"ituz":11,"flag":"DO"}, {"id":74,"name":"El Salvador","cqz":7,"ituz":11,"flag":"SV"}, {"id":75,"name":"Georgia","cqz":21,"ituz":29,"flag":"GE"}, {"id":76,"name":"Guatemala","cqz":7,"ituz":11,"flag":"GT"}, {"id":77,"name":"Grenada","cqz":8,"ituz":11,"flag":"GD"}, {"id":78,"name":"Haiti","cqz":8,"ituz":11,"flag":"HT"}, {"id":79,"name":"Guadeloupe","cqz":8,"ituz":11,"flag":"FR"}, {"id":80,"name":"Honduras","cqz":7,"ituz":11,"flag":"HN"}, {"id":81,"name":"Germany","cqz":14,"ituz":28,"flag":"DE"}, {"id":82,"name":"Jamaica","cqz":8,"ituz":11,"flag":"JM"}, {"id":84,"name":"Martinique","cqz":8,"ituz":11,"flag":"MQ"}, {"id":85,"name":"Bonaire Curacao (Neth Antilles)","cqz":9,"ituz":11,"flag":null}, {"id":86,"name":"Nicaragua","cqz":7,"ituz":11,"flag":"NI"}, {"id":88,"name":"Panama","cqz":7,"ituz":11,"flag":"PA"}, {"id":89,"name":"Turks & Caicos Islands","cqz":8,"ituz":11,"flag":"TC"}, {"id":90,"name":"Trinidad & Tobago","cqz":9,"ituz":11,"flag":"TT"}, {"id":91,"name":"Aruba","cqz":9,"ituz":11,"flag":"AW"}, {"id":93,"name":"Geyser Reef","cqz":39,"ituz":53,"flag":null}, {"id":94,"name":"Antigua & Barbuda","cqz":8,"ituz":11,"flag":"AG"}, {"id":95,"name":"Dominica","cqz":8,"ituz":11,"flag":"DM"}, {"id":96,"name":"Montserrat","cqz":8,"ituz":11,"flag":"MS"}, {"id":97,"name":"St. Lucia","cqz":8,"ituz":11,"flag":"LC"}, {"id":98,"name":"St. Vincent","cqz":8,"ituz":11,"flag":"VC"}, {"id":99,"name":"Glorioso Islands","cqz":39,"ituz":53,"flag":"FR"}, {"id":100,"name":"Argentina","cqz":13,"ituz":14,"flag":"AR"}, {"id":101,"name":"Goa","cqz":22,"ituz":41,"flag":null}, {"id":102,"name":"Gold Coast Togoland","cqz":35,"ituz":46,"flag":null}, {"id":103,"name":"Guam","cqz":27,"ituz":64,"flag":"GU"}, {"id":104,"name":"Bolivia","cqz":10,"ituz":12,"flag":"BO"}, {"id":105,"name":"Guantanamo Bay","cqz":8,"ituz":11,"flag":"US"}, {"id":106,"name":"Guernsey","cqz":14,"ituz":27,"flag":"GG"}, {"id":107,"name":"Guinea","cqz":35,"ituz":46,"flag":"GN"}, {"id":108,"name":"Brazil","cqz":11,"ituz":15,"flag":"BR"}, {"id":109,"name":"Guinea-Bissau","cqz":35,"ituz":46,"flag":"GW"}, {"id":110,"name":"Hawaii","cqz":31,"ituz":61,"flag":"US"}, {"id":111,"name":"Heard Island","cqz":39,"ituz":68,"flag":"AU"}, {"id":112,"name":"Chile","cqz":12,"ituz":14,"flag":"CL"}, {"id":113,"name":"Ifni","cqz":33,"ituz":37,"flag":null}, {"id":114,"name":"Isle of Man","cqz":14,"ituz":27,"flag":"IM"}, {"id":115,"name":"Italian Somaliland","cqz":37,"ituz":48,"flag":"IT"}, {"id":116,"name":"Colombia","cqz":9,"ituz":12,"flag":"CO"}, {"id":117,"name":"ITU HQ","cqz":14,"ituz":28,"flag":"CH"}, {"id":118,"name":"Jan Mayen","cqz":40,"ituz":18,"flag":"NO"}, {"id":119,"name":"Java","cqz":28,"ituz":54,"flag":null}, {"id":120,"name":"Ecuador","cqz":10,"ituz":12,"flag":"EC"}, {"id":122,"name":"Jersey","cqz":14,"ituz":27,"flag":"JE"}, {"id":123,"name":"Johnston Island","cqz":31,"ituz":61,"flag":"US"}, {"id":124,"name":"Juan de Nova & Europa","cqz":39,"ituz":53,"flag":"FR"}, {"id":125,"name":"Juan Fernandez Islands","cqz":12,"ituz":14,"flag":"CL"}, {"id":126,"name":"Kaliningrad","cqz":15,"ituz":29,"flag":"RU"}, {"id":127,"name":"Kamaran Is.","cqz":21,"ituz":39,"flag":null}, {"id":128,"name":"Karelo-Finnish Rep.","cqz":16,"ituz":19,"flag":null}, {"id":129,"name":"Guyana","cqz":9,"ituz":12,"flag":"GY"}, {"id":130,"name":"Kazakhstan","cqz":17,"ituz":30,"flag":"KZ"}, {"id":131,"name":"Kerguelen Islands","cqz":39,"ituz":68,"flag":"FR"}, {"id":132,"name":"Paraguay","cqz":11,"ituz":14,"flag":"PY"}, {"id":133,"name":"Kermadec Islands","cqz":32,"ituz":60,"flag":"NZ"}, {"id":134,"name":"Kingman Reef","cqz":31,"ituz":61,"flag":"US"}, {"id":135,"name":"Kyrgyzstan","cqz":17,"ituz":30,"flag":"KG"}, {"id":136,"name":"Peru","cqz":10,"ituz":12,"flag":"PE"}, {"id":137,"name":"Republic of Korea","cqz":25,"ituz":44,"flag":"KR"}, {"id":138,"name":"Kure Island","cqz":31,"ituz":61,"flag":"US"}, {"id":139,"name":"Kuria Muria I.","cqz":21,"ituz":39,"flag":null}, {"id":140,"name":"Suriname","cqz":9,"ituz":12,"flag":"SR"}, {"id":141,"name":"Falkland Islands","cqz":13,"ituz":16,"flag":"FK"}, {"id":142,"name":"Lakshadweep Islands","cqz":22,"ituz":41,"flag":"IN"}, {"id":143,"name":"Laos","cqz":26,"ituz":49,"flag":"LA"}, {"id":144,"name":"Uruguay","cqz":13,"ituz":14,"flag":"UY"}, {"id":145,"name":"Latvia","cqz":15,"ituz":29,"flag":"LV"}, {"id":146,"name":"Lithuania","cqz":15,"ituz":29,"flag":"LT"}, {"id":147,"name":"Lord Howe Island","cqz":30,"ituz":60,"flag":"AU"}, {"id":148,"name":"Venezuela","cqz":9,"ituz":12,"flag":"VE"}, {"id":149,"name":"Azores","cqz":14,"ituz":36,"flag":"PT"}, {"id":150,"name":"Australia","cqz":30,"ituz":59,"flag":"AU"}, {"id":151,"name":"Malyj Vysotskij Island","cqz":16,"ituz":29,"flag":null}, {"id":152,"name":"Macao","cqz":24,"ituz":44,"flag":"MO"}, {"id":153,"name":"Macquarie Island","cqz":30,"ituz":60,"flag":"AU"}, {"id":154,"name":"Yemen Arab Rep.","cqz":21,"ituz":39,"flag":null}, {"id":155,"name":"Malaya","cqz":28,"ituz":54,"flag":null}, {"id":157,"name":"Nauru","cqz":31,"ituz":65,"flag":"NR"}, {"id":158,"name":"Vanuatu","cqz":32,"ituz":56,"flag":"VU"}, {"id":159,"name":"Maldives","cqz":22,"ituz":41,"flag":"MV"}, {"id":160,"name":"Tonga","cqz":32,"ituz":62,"flag":"TO"}, {"id":161,"name":"Malpelo Island","cqz":9,"ituz":12,"flag":"CO"}, {"id":162,"name":"New Caledonia","cqz":32,"ituz":56,"flag":"NC"}, {"id":163,"name":"Papua New Guinea","cqz":28,"ituz":51,"flag":"PG"}, {"id":164,"name":"Manchuria","cqz":24,"ituz":33,"flag":null}, {"id":165,"name":"Mauritius","cqz":39,"ituz":53,"flag":"MU"}, {"id":166,"name":"Mariana Islands","cqz":27,"ituz":64,"flag":"US"}, {"id":167,"name":"Market Reef","cqz":15,"ituz":18,"flag":"SE"}, {"id":168,"name":"Marshall Islands","cqz":31,"ituz":65,"flag":"MH"}, {"id":169,"name":"Mayotte","cqz":39,"ituz":53,"flag":"YT"}, {"id":170,"name":"New Zealand","cqz":32,"ituz":60,"flag":"NZ"}, {"id":171,"name":"Mellish Reef","cqz":30,"ituz":56,"flag":"AU"}, {"id":172,"name":"Pitcairn Island","cqz":32,"ituz":63,"flag":"PN"}, {"id":173,"name":"Micronesia","cqz":27,"ituz":65,"flag":"FM"}, {"id":174,"name":"Midway Island","cqz":31,"ituz":61,"flag":"US"}, {"id":175,"name":"French Polynesia","cqz":32,"ituz":63,"flag":"PF"}, {"id":176,"name":"Fiji","cqz":32,"ituz":56,"flag":"FJ"}, {"id":177,"name":"Minami Torishima","cqz":27,"ituz":90,"flag":"JP"}, {"id":178,"name":"Minerva Reef","cqz":32,"ituz":62,"flag":null}, {"id":179,"name":"Moldova","cqz":16,"ituz":29,"flag":"MD"}, {"id":180,"name":"Mount Athos","cqz":20,"ituz":28,"flag":"GR"}, {"id":181,"name":"Mozambique","cqz":37,"ituz":53,"flag":"MZ"}, {"id":182,"name":"Navassa Island","cqz":8,"ituz":11,"flag":"US"}, {"id":183,"name":"Netherlands Borneo","cqz":28,"ituz":54,"flag":null}, {"id":184,"name":"Netherlands N. Guinea","cqz":28,"ituz":51,"flag":null}, {"id":185,"name":"Solomon Islands","cqz":28,"ituz":51,"flag":"SB"}, {"id":186,"name":"Newfoundland Labrador","cqz":5,"ituz":9,"flag":null}, {"id":187,"name":"Niger","cqz":35,"ituz":46,"flag":"NE"}, {"id":188,"name":"Niue","cqz":32,"ituz":62,"flag":"NU"}, {"id":189,"name":"Norfolk Island","cqz":32,"ituz":60,"flag":"NF"}, {"id":190,"name":"Samoa","cqz":32,"ituz":62,"flag":"WS"}, {"id":191,"name":"North Cook Islands","cqz":32,"ituz":62,"flag":"NZ"}, {"id":192,"name":"Ogasawara","cqz":27,"ituz":45,"flag":"JP"}, {"id":193,"name":"Okinawa (Ryukyu Is.)","cqz":25,"ituz":45,"flag":null}, {"id":194,"name":"Okino Tori-shima","cqz":27,"ituz":45,"flag":null}, {"id":195,"name":"Annobon Island","cqz":36,"ituz":52,"flag":"GQ"}, {"id":196,"name":"Palestine","cqz":20,"ituz":39,"flag":null}, {"id":197,"name":"Palmyra & Jarvis Islands","cqz":31,"ituz":61,"flag":"US"}, {"id":198,"name":"Papua Territory","cqz":28,"ituz":51,"flag":null}, {"id":199,"name":"Peter 1 Island","cqz":12,"ituz":72,"flag":"NO"}, {"id":200,"name":"Portuguese Timor","cqz":28,"ituz":54,"flag":null}, {"id":201,"name":"Pr. Edward & Marion Is.","cqz":38,"ituz":57,"flag":"ZA"}, {"id":202,"name":"Puerto Rico","cqz":8,"ituz":11,"flag":"PR"}, {"id":203,"name":"Andorra","cqz":14,"ituz":27,"flag":"AD"}, {"id":204,"name":"Revillagigedo","cqz":6,"ituz":10,"flag":"MX"}, {"id":205,"name":"Ascension Island","cqz":36,"ituz":66,"flag":"GB"}, {"id":206,"name":"Austria","cqz":15,"ituz":28,"flag":"AT"}, {"id":207,"name":"Rodriguez Island","cqz":39,"ituz":53,"flag":"MU"}, {"id":208,"name":"Ruanda-Urundi","cqz":36,"ituz":52,"flag":null}, {"id":209,"name":"Belgium","cqz":14,"ituz":27,"flag":"BE"}, {"id":210,"name":"Saar","cqz":14,"ituz":28,"flag":null}, {"id":211,"name":"Sable Island","cqz":5,"ituz":9,"flag":"CA"}, {"id":212,"name":"Bulgaria","cqz":20,"ituz":28,"flag":"BG"}, {"id":213,"name":"St. Martin","cqz":8,"ituz":11,"flag":"FR"}, {"id":214,"name":"Corsica","cqz":15,"ituz":28,"flag":"FR"}, {"id":215,"name":"Cyprus","cqz":20,"ituz":39,"flag":"CY"}, {"id":216,"name":"San Andres & Providencia","cqz":7,"ituz":11,"flag":"NI"}, {"id":217,"name":"San Felix & San Ambrosio","cqz":12,"ituz":14,"flag":"CL"}, {"id":218,"name":"Czechoslovakia","cqz":15,"ituz":28,"flag":"CZ"}, {"id":219,"name":"Sao Tome & Principe","cqz":36,"ituz":47,"flag":"ST"}, {"id":220,"name":"Sarawak","cqz":28,"ituz":54,"flag":null}, {"id":221,"name":"Denmark","cqz":14,"ituz":18,"flag":"DK"}, {"id":222,"name":"Faroe Islands","cqz":14,"ituz":18,"flag":"FO"}, {"id":223,"name":"England","cqz":14,"ituz":27,"flag":"england"}, {"id":224,"name":"Finland","cqz":15,"ituz":18,"flag":"FI"}, {"id":225,"name":"Sardinia","cqz":15,"ituz":28,"flag":"IT"}, {"id":226,"name":"Saudi Arabia/Iraq Neut. Zone","cqz":21,"ituz":39,"flag":null}, {"id":227,"name":"France","cqz":14,"ituz":27,"flag":"FR"}, {"id":228,"name":"Serrana Bank & Roncador Cay","cqz":7,"ituz":11,"flag":null}, {"id":229,"name":"German Dem. Rep.","cqz":14,"ituz":28,"flag":"DE"}, {"id":230,"name":"Fed. Rep. of Germany","cqz":14,"ituz":28,"flag":"DE"}, {"id":231,"name":"Sikkim","cqz":22,"ituz":41,"flag":null}, {"id":232,"name":"Somalia","cqz":37,"ituz":48,"flag":"SO"}, {"id":233,"name":"Gibraltar","cqz":14,"ituz":37,"flag":"GI"}, {"id":234,"name":"South Cook Islands","cqz":32,"ituz":63,"flag":"GS"}, {"id":235,"name":"South Georgia Island","cqz":13,"ituz":73,"flag":"GS"}, {"id":236,"name":"Greece","cqz":20,"ituz":28,"flag":"GR"}, {"id":237,"name":"Greenland","cqz":40,"ituz":5,"flag":"GL"}, {"id":238,"name":"South Orkney Islands","cqz":13,"ituz":73,"flag":"GB"}, {"id":239,"name":"Hungary","cqz":15,"ituz":28,"flag":"HU"}, {"id":240,"name":"South Sandwich Islands","cqz":13,"ituz":73,"flag":"GS"}, {"id":241,"name":"South Shetland Islands","cqz":13,"ituz":73,"flag":"GB"}, {"id":242,"name":"Iceland","cqz":40,"ituz":17,"flag":"IS"}, {"id":243,"name":"People's Dem. Rep. of Yemen","cqz":21,"ituz":39,"flag":null}, {"id":244,"name":"Southern Sudan","cqz":34,"ituz":47,"flag":null}, {"id":245,"name":"Ireland","cqz":14,"ituz":27,"flag":"IE"}, {"id":246,"name":"Sov Mil Order of Malta","cqz":15,"ituz":28,"flag":"MT"}, {"id":247,"name":"Spratly Islands","cqz":26,"ituz":50,"flag":"PH"}, {"id":248,"name":"Italy","cqz":15,"ituz":28,"flag":"IT"}, {"id":249,"name":"St. Kitts & Nevis","cqz":8,"ituz":11,"flag":"KN"}, {"id":250,"name":"St. Helena","cqz":36,"ituz":66,"flag":"SH"}, {"id":251,"name":"Liechtenstein","cqz":14,"ituz":28,"flag":"LI"}, {"id":252,"name":"St. Paul Island","cqz":5,"ituz":9,"flag":"CA"}, {"id":253,"name":"St. Peter & St. Paul","cqz":11,"ituz":13,"flag":"BR"}, {"id":254,"name":"Luxembourg","cqz":14,"ituz":27,"flag":"LU"}, {"id":255,"name":"Sint Maarten Saba St Eustatius","cqz":8,"ituz":11,"flag":"NL"}, {"id":256,"name":"Madeira Islands","cqz":33,"ituz":36,"flag":"PT"}, {"id":257,"name":"Malta","cqz":15,"ituz":28,"flag":"MT"}, {"id":258,"name":"Sumatra","cqz":28,"ituz":54,"flag":null}, {"id":259,"name":"Svalbard","cqz":40,"ituz":18,"flag":"NO"}, {"id":260,"name":"Monaco","cqz":14,"ituz":27,"flag":"MC"}, {"id":261,"name":"Swan Is.","cqz":7,"ituz":11,"flag":null}, {"id":262,"name":"Tajikistan","cqz":17,"ituz":30,"flag":"TJ"}, {"id":263,"name":"Netherlands","cqz":14,"ituz":27,"flag":"NL"}, {"id":264,"name":"Tangier","cqz":33,"ituz":37,"flag":null}, {"id":265,"name":"Northern Ireland","cqz":14,"ituz":27,"flag":"GB"}, {"id":266,"name":"Norway","cqz":14,"ituz":18,"flag":"NO"}, {"id":267,"name":"Terr. New Guinea","cqz":28,"ituz":51,"flag":null}, {"id":268,"name":"Tibet","cqz":23,"ituz":41,"flag":null}, {"id":269,"name":"Poland","cqz":15,"ituz":28,"flag":"PL"}, {"id":270,"name":"Tokelau Islands","cqz":31,"ituz":62,"flag":"TK"}, {"id":271,"name":"Trieste","cqz":15,"ituz":28,"flag":null}, {"id":272,"name":"Portugal","cqz":14,"ituz":37,"flag":"PT"}, {"id":273,"name":"Trindade & Martim Vaz","cqz":11,"ituz":15,"flag":"BR"}, {"id":274,"name":"Tristan da Cunha & Gough Islands","cqz":38,"ituz":66,"flag":"GB"}, {"id":275,"name":"Romania","cqz":20,"ituz":28,"flag":"RO"}, {"id":276,"name":"Tromelin Island","cqz":39,"ituz":53,"flag":"FR"}, {"id":277,"name":"St. Pierre & Miquelon","cqz":5,"ituz":9,"flag":"CA"}, {"id":278,"name":"San Marino","cqz":15,"ituz":28,"flag":"SM"}, {"id":279,"name":"Scotland","cqz":14,"ituz":27,"flag":"scotland"}, {"id":280,"name":"Turkmenistan","cqz":17,"ituz":30,"flag":"TM"}, {"id":281,"name":"Spain","cqz":14,"ituz":37,"flag":"ES"}, {"id":282,"name":"Tuvalu","cqz":31,"ituz":65,"flag":"TV"}, {"id":283,"name":"UK Base Areas on Cyprus","cqz":20,"ituz":39,"flag":"CY"}, {"id":284,"name":"Sweden","cqz":14,"ituz":18,"flag":"SE"}, {"id":285,"name":"US Virgin Islands","cqz":8,"ituz":11,"flag":"VI"}, {"id":286,"name":"Uganda","cqz":37,"ituz":48,"flag":"UG"}, {"id":287,"name":"Switzerland","cqz":14,"ituz":28,"flag":"CH"}, {"id":288,"name":"Ukraine","cqz":16,"ituz":29,"flag":"UA"}, {"id":289,"name":"United Nations HQ","cqz":5,"ituz":8,"flag":"united-nations"}, {"id":291,"name":"United States","cqz":5,"ituz":8,"flag":"US"}, {"id":292,"name":"Uzbekistan","cqz":17,"ituz":30,"flag":"UZ"}, {"id":293,"name":"Vietnam","cqz":26,"ituz":49,"flag":"VN"}, {"id":294,"name":"Wales","cqz":14,"ituz":27,"flag":"wales"}, {"id":295,"name":"Vatican City","cqz":15,"ituz":28,"flag":"VA"}, {"id":296,"name":"Serbia","cqz":15,"ituz":28,"flag":"RS"}, {"id":297,"name":"Wake Island","cqz":31,"ituz":65,"flag":"US"}, {"id":298,"name":"Wallis & Futuna Islands","cqz":32,"ituz":62,"flag":"WF"}, {"id":299,"name":"West Malaysia","cqz":28,"ituz":54,"flag":"MY"}, {"id":301,"name":"Western Kiribati","cqz":31,"ituz":65,"flag":"KI"}, {"id":302,"name":"Western Sahara","cqz":33,"ituz":46,"flag":"EH"}, {"id":303,"name":"Willis Island","cqz":30,"ituz":55,"flag":"AU"}, {"id":304,"name":"Bahrain","cqz":21,"ituz":39,"flag":"BH"}, {"id":305,"name":"Bangladesh","cqz":22,"ituz":41,"flag":"BD"}, {"id":306,"name":"Bhutan","cqz":22,"ituz":41,"flag":"BT"}, {"id":307,"name":"Zanzibar","cqz":37,"ituz":53,"flag":null}, {"id":308,"name":"Costa Rica","cqz":7,"ituz":11,"flag":"CR"}, {"id":309,"name":"Myanmar","cqz":26,"ituz":49,"flag":"MM"}, {"id":312,"name":"Cambodia","cqz":26,"ituz":49,"flag":"KH"}, {"id":315,"name":"Sri Lanka","cqz":22,"ituz":41,"flag":"LK"}, {"id":318,"name":"China","cqz":24,"ituz":44,"flag":"CN"}, {"id":321,"name":"Hong Kong","cqz":24,"ituz":44,"flag":"HK"}, {"id":324,"name":"India","cqz":22,"ituz":41,"flag":"IN"}, {"id":327,"name":"Indonesia","cqz":28,"ituz":54,"flag":"ID"}, {"id":330,"name":"Iran","cqz":21,"ituz":40,"flag":"IR"}, {"id":333,"name":"Iraq","cqz":21,"ituz":39,"flag":"IQ"}, {"id":336,"name":"Israel","cqz":20,"ituz":39,"flag":"IL"}, {"id":339,"name":"Japan","cqz":25,"ituz":45,"flag":"JP"}, {"id":342,"name":"Jordan","cqz":20,"ituz":39,"flag":"JO"}, {"id":344,"name":"DPR of Korea","cqz":25,"ituz":44,"flag":"KP"}, {"id":345,"name":"Brunei Darussalam","cqz":28,"ituz":54,"flag":"BN"}, {"id":348,"name":"Kuwait","cqz":21,"ituz":39,"flag":"KW"}, {"id":354,"name":"Lebanon","cqz":20,"ituz":39,"flag":"LB"}, {"id":363,"name":"Mongolia","cqz":23,"ituz":32,"flag":"MN"}, {"id":369,"name":"Nepal","cqz":22,"ituz":42,"flag":"NP"}, {"id":370,"name":"Oman","cqz":21,"ituz":39,"flag":"OM"}, {"id":372,"name":"Pakistan","cqz":21,"ituz":41,"flag":"PK"}, {"id":375,"name":"Philippines","cqz":27,"ituz":50,"flag":"PH"}, {"id":376,"name":"Qatar","cqz":21,"ituz":39,"flag":"QA"}, {"id":378,"name":"Saudi Arabia","cqz":21,"ituz":39,"flag":"SA"}, {"id":379,"name":"Seychelles","cqz":39,"ituz":53,"flag":"SC"}, {"id":381,"name":"Singapore","cqz":28,"ituz":54,"flag":"SG"}, {"id":382,"name":"Djibouti","cqz":37,"ituz":48,"flag":"DJ"}, {"id":384,"name":"Syria","cqz":20,"ituz":39,"flag":"SY"}, {"id":386,"name":"Taiwan","cqz":24,"ituz":44,"flag":"TW"}, {"id":387,"name":"Thailand","cqz":26,"ituz":49,"flag":"TH"}, {"id":390,"name":"Asiatic Turkey","cqz":20,"ituz":39,"flag":"TR"}, {"id":391,"name":"United Arab Emirates","cqz":21,"ituz":39,"flag":"AE"}, {"id":400,"name":"Algeria","cqz":33,"ituz":37,"flag":"DZ"}, {"id":401,"name":"Angola","cqz":36,"ituz":52,"flag":"AO"}, {"id":402,"name":"Botswana","cqz":38,"ituz":57,"flag":"BW"}, {"id":404,"name":"Burundi","cqz":36,"ituz":52,"flag":"BI"}, {"id":406,"name":"Cameroon","cqz":36,"ituz":47,"flag":"CM"}, {"id":408,"name":"Central African Republic","cqz":36,"ituz":47,"flag":"CF"}, {"id":409,"name":"Cape Verde","cqz":35,"ituz":46,"flag":"CV"}, {"id":410,"name":"Chad","cqz":36,"ituz":47,"flag":"TD"}, {"id":411,"name":"Comoros","cqz":39,"ituz":53,"flag":"KM"}, {"id":412,"name":"Republic of the Congo","cqz":36,"ituz":52,"flag":"CG"}, {"id":414,"name":"Dem. Rep. of the Congo","cqz":36,"ituz":52,"flag":"CD"}, {"id":416,"name":"Benin","cqz":35,"ituz":46,"flag":"BJ"}, {"id":420,"name":"Gabon","cqz":36,"ituz":52,"flag":"GA"}, {"id":422,"name":"The Gambia","cqz":35,"ituz":46,"flag":"GM"}, {"id":424,"name":"Ghana","cqz":35,"ituz":46,"flag":"GH"}, {"id":428,"name":"Cote d'Ivoire","cqz":35,"ituz":46,"flag":"CI"}, {"id":430,"name":"Kenya","cqz":37,"ituz":48,"flag":"KE"}, {"id":432,"name":"Lesotho","cqz":38,"ituz":57,"flag":"LS"}, {"id":434,"name":"Liberia","cqz":35,"ituz":46,"flag":"LR"}, {"id":436,"name":"Libya","cqz":34,"ituz":38,"flag":"LY"}, {"id":438,"name":"Madagascar","cqz":39,"ituz":53,"flag":"MG"}, {"id":440,"name":"Malawi","cqz":37,"ituz":53,"flag":"MW"}, {"id":442,"name":"Mali","cqz":35,"ituz":46,"flag":"ML"}, {"id":444,"name":"Mauritania","cqz":35,"ituz":46,"flag":"MR"}, {"id":446,"name":"Morocco","cqz":33,"ituz":37,"flag":"MA"}, {"id":450,"name":"Nigeria","cqz":35,"ituz":46,"flag":"NG"}, {"id":452,"name":"Zimbabwe","cqz":38,"ituz":53,"flag":"ZW"}, {"id":453,"name":"Reunion Island","cqz":39,"ituz":53,"flag":"FR"}, {"id":454,"name":"Rwanda","cqz":36,"ituz":52,"flag":"RW"}, {"id":456,"name":"Senegal","cqz":35,"ituz":46,"flag":"SN"}, {"id":458,"name":"Sierra Leone","cqz":35,"ituz":46,"flag":"SL"}, {"id":460,"name":"Rotuma Island","cqz":32,"ituz":56,"flag":"FJ"}, {"id":462,"name":"South Africa","cqz":38,"ituz":57,"flag":"ZA"}, {"id":464,"name":"Namibia","cqz":38,"ituz":57,"flag":"NA"}, {"id":466,"name":"Sudan","cqz":34,"ituz":48,"flag":"SD"}, {"id":468,"name":"Kingdom of Eswatini","cqz":38,"ituz":57,"flag":"SZ"}, {"id":470,"name":"Tanzania","cqz":37,"ituz":53,"flag":"TZ"}, {"id":474,"name":"Tunisia","cqz":33,"ituz":37,"flag":"TN"}, {"id":478,"name":"Egypt","cqz":34,"ituz":38,"flag":"EG"}, {"id":480,"name":"Burkina Faso","cqz":35,"ituz":46,"flag":"BF"}, {"id":482,"name":"Zambia","cqz":36,"ituz":53,"flag":"ZM"}, {"id":483,"name":"Togo","cqz":35,"ituz":46,"flag":"TG"}, {"id":488,"name":"Walvis Bay","cqz":38,"ituz":57,"flag":null}, {"id":489,"name":"Conway Reef","cqz":32,"ituz":56,"flag":"FJ"}, {"id":490,"name":"Banaba Island","cqz":31,"ituz":65,"flag":"KI"}, {"id":492,"name":"Yemen","cqz":21,"ituz":39,"flag":"YE"}, {"id":493,"name":"Penguin Is.","cqz":38,"ituz":57,"flag":null}, {"id":497,"name":"Croatia","cqz":15,"ituz":28,"flag":"HR"}, {"id":499,"name":"Slovenia","cqz":15,"ituz":28,"flag":"SI"}, {"id":501,"name":"Bosnia-Herzegovina","cqz":15,"ituz":28,"flag":"BA"}, {"id":502,"name":"North Macedonia","cqz":15,"ituz":28,"flag":"MK"}, {"id":503,"name":"Czech Republic","cqz":15,"ituz":28,"flag":"CZ"}, {"id":504,"name":"Slovak Republic","cqz":15,"ituz":28,"flag":"SK"}, {"id":505,"name":"Pratas Island","cqz":24,"ituz":44,"flag":"TW"}, {"id":506,"name":"Scarborough Reef","cqz":27,"ituz":50,"flag":"PH"}, {"id":507,"name":"Temotu Province","cqz":32,"ituz":51,"flag":"SB"}, {"id":508,"name":"Austral Islands","cqz":32,"ituz":63,"flag":"PF"}, {"id":509,"name":"Marquesas Islands","cqz":31,"ituz":63,"flag":"FR"}, {"id":510,"name":"Palestine","cqz":20,"ituz":39,"flag":"PS"}, {"id":511,"name":"Timor - Leste","cqz":28,"ituz":54,"flag":"TL"}, {"id":512,"name":"Chesterfield Islands","cqz":30,"ituz":56,"flag":"GB"}, {"id":513,"name":"Ducie Island","cqz":32,"ituz":63,"flag":"PN"}, {"id":514,"name":"Montenegro","cqz":15,"ituz":28,"flag":"ME"}, {"id":515,"name":"Swains Island","cqz":32,"ituz":62,"flag":"US"}, {"id":516,"name":"St. Barthelemy","cqz":8,"ituz":11,"flag":"FR"}, {"id":517,"name":"Curacao","cqz":9,"ituz":11,"flag":"CW"}, {"id":518,"name":"Sint Maarten","cqz":8,"ituz":11,"flag":"NL"}, {"id":519,"name":"Saba & St. Eustatius","cqz":8,"ituz":11,"flag":"AN"}, {"id":520,"name":"Bonaire","cqz":9,"ituz":11,"flag":"NL"}, {"id":521,"name":"Republic of South Sudan","cqz":34,"ituz":48,"flag":"SS"}, {"id":522,"name":"Republic of Kosovo","cqz":15,"ituz":28,"flag":"XK"} ] ================================================ FILE: res/data/legacy_modes.json ================================================ { "AMTORFEC": { "mode": "TOR", "submode": "AMTORFEC" }, "ASCI": { "mode": "RTTY", "submode": "ASCI" }, "C4FM": { "mode": "DIGITALVOICE", "submode": "C4FM" }, "CHIP64": { "mode": "CHIP", "submode": "CHIP64" }, "CHIP128": { "mode": "CHIP", "submode": "CHIP128" }, "CONTESTI4/125": { "mode": "CONTESTI", "submode": "CT4/125" }, "CONTESTI4/250": { "mode": "CONTESTI", "submode": "CT4/250" }, "CONTESTI4/500": { "mode": "CONTESTI", "submode": "CT4/500" }, "CONTESTI4/1000": { "mode": "CONTESTI", "submode": "CT4/1K" }, "CONTESTI4/2000": { "mode": "CONTESTI", "submode": "CT4/2K" }, "CONTESTI8/125": { "mode": "CONTESTI", "submode": "CT8/125" }, "CONTESTI8/250": { "mode": "CONTESTI", "submode": "CT8/250" }, "CONTESTI8/500": { "mode": "CONTESTI", "submode": "CT8/500" }, "CONTESTI8/1000": { "mode": "CONTESTI", "submode": "CT8/1K" }, "CONTESTI8/2000": { "mode": "CONTESTI", "submode": "CT8/2K" }, "CONTESTI16/250": { "mode": "CONTESTI", "submode": "CT16/250" }, "CONTESTI16/500": { "mode": "CONTESTI", "submode": "CT16/500" }, "CONTESTI16/1000": { "mode": "CONTESTI", "submode": "CT16/1K" }, "CONTESTI16/2000": { "mode": "CONTESTI", "submode": "CT16/2K" }, "CONTESTI32/1000": { "mode": "CONTESTI", "submode": "CT32/1K" }, "CONTESTI32/2000": { "mode": "CONTESTI", "submode": "CT32/2K" }, "CONTESTI64/500": { "mode": "CONTESTI", "submode": "CT64/500" }, "CONTESTI64/1000": { "mode": "CONTESTI", "submode": "CT64/1K" }, "CONTESTI64/2000": { "mode": "CONTESTI", "submode": "CT64/2K" }, "DOMINOF": { "mode": "DOMINO", "submode": "DOMINOF" }, "DMR": { "mode": "DIGITALVOICE", "submode": "DMR" }, "DSTAR": { "mode": "DIGITALVOICE", "submode": "DSTAR" }, "FMHELL": { "mode": "HELL", "submode": "FMHELL" }, "FREEDV": { "mode": "DIGITALVOICE", "submode": "FREEDV" }, "FSKH245": { "mode": "HELL", "submode": "FSKH245" }, "FSKH105": { "mode": "HELL", "submode": "FSKH105" }, "FSK31": { "mode": "PSK", "submode": "FSK31" }, "GTOR": { "mode": "TOR", "submode": "GTOR" }, "HELL80": { "mode": "HELL", "submode": "HELL80" }, "HFSK": { "mode": "HELL", "submode": "HFSK" }, "JS8": { "mode": "MFSK", "submode": "JS8" }, "JT4A": { "mode": "JT4", "submode": "JT4A" }, "JT4B": { "mode": "JT4", "submode": "JT4B" }, "JT4C": { "mode": "JT4", "submode": "JT4C" }, "JT4D": { "mode": "JT4", "submode": "JT4D" }, "JT4E": { "mode": "JT4", "submode": "JT4E" }, "JT4F": { "mode": "JT4", "submode": "JT4F" }, "JT4G": { "mode": "JT4", "submode": "JT4G" }, "JT65A": { "mode": "JT65", "submode": "JT65A" }, "JT65B": { "mode": "JT65", "submode": "JT65B" }, "JT65C": { "mode": "JT65", "submode": "JT65C" }, "M17": { "mode": "DIGITALVOICE", "submode": "M17" }, "MFSK8": { "mode": "MFSK", "submode": "MFSK8" }, "MFSK16": { "mode": "MFSK", "submode": "MFSK16" }, "MFSK32": { "mode": "MFSK", "submode": "MFSK32" }, "MFSK4": { "mode": "MFSK", "submode": "MFSK4" }, "MFSK11": { "mode": "MFSK", "submode": "MFSK11" }, "MFSK22": { "mode": "MFSK", "submode": "MFSK22" }, "MFSK31": { "mode": "MFSK", "submode": "MFSK31" }, "MFSK64": { "mode": "MFSK", "submode": "MFSK64" }, "MFSK128": { "mode": "MFSK", "submode": "MFSK128" }, "MFSK64L": { "mode": "MFSK", "submode": "MFSK64" }, "MFSK128L": { "mode": "MFSK", "submode": "MFSK128" }, "MT63-500S": { "mode": "MT63", "submode": "MT63-500S" }, "MT63-500L": { "mode": "MT63", "submode": "MT63-500L" }, "MT63-1KS": { "mode": "MT63", "submode": "MT63" }, "MT63-1KL": { "mode": "MT63", "submode": "MT63" }, "MT63-2KS": { "mode": "MT63", "submode": "MT63" }, "MT63-2KL": { "mode": "MT63", "submode": "MT63" }, "SCAMP_OO": { "mode": "MTONE", "submode": "SCAMP_OO" }, "SCAMP_OO_SLW": { "mode": "MTONE", "submode": "SCAMP_OO_SLW" }, "OLIVIA 4/125": { "mode": "OLIVIA", "submode": "OLIVIA 4/125" }, "OLIVIA 4/250": { "mode": "OLIVIA", "submode": "OLIVIA 4/250" }, "OLIVIA 4/500": { "mode": "OLIVIA", "submode": "OLIVIA 4/500" }, "OLIVIA 4/1K": { "mode": "OLIVIA", "submode": "OLIVIA 4/1K" }, "OLIVIA 4/2K": { "mode": "OLIVIA", "submode": "OLIVIA 4/1K" }, "OLIVIA 8/125": { "mode": "OLIVIA", "submode": "OLIVIA 8/125" }, "OLIVIA 8/250": { "mode": "OLIVIA", "submode": "OLIVIA 8/250" }, "OLIVIA 8/500": { "mode": "OLIVIA", "submode": "OLIVIA 8/500" }, "OLIVIA 8/1K": { "mode": "OLIVIA", "submode": "OLIVIA 8/1K" }, "OLIVIA 8/2K": { "mode": "OLIVIA", "submode": "OLIVIA 8/2K" }, "OLIVIA 16/500": { "mode": "OLIVIA", "submode": "OLIVIA 16/500" }, "OLIVIA 16/1K": { "mode": "OLIVIA", "submode": "OLIVIA 16/1K" }, "OLIVIA 16/2K": { "mode": "OLIVIA", "submode": "OLIVIA 16/2K" }, "OLIVIA 32/1K": { "mode": "OLIVIA", "submode": "OLIVIA 32/1K" }, "OLIVIA 32/2K": { "mode": "OLIVIA", "submode": "OLIVIA 32/2K" }, "OLIVIA 64/500": { "mode": "OLIVIA", "submode": "OLIVIA 64/500" }, "OLIVIA 64/1K": { "mode": "OLIVIA", "submode": "OLIVIA 64/1K" }, "OLIVIA 64/2K": { "mode": "OLIVIA", "submode": "OLIVIA 64/2K" }, "PAC2": { "mode": "PAC", "submode": "PAC2" }, "PAC3": { "mode": "PAC", "submode": "PAC3" }, "PAX2": { "mode": "PAX", "submode": "PAX2" }, "PCW": { "mode": "CW", "submode": "PCW" }, "PSK10": { "mode": "PSK", "submode": "PSK10" }, "PSK31": { "mode": "PSK", "submode": "PSK31" }, "PSK63": { "mode": "PSK", "submode": "PSK63" }, "PSK63F": { "mode": "PSK", "submode": "PSK63F" }, "PSK125": { "mode": "PSK", "submode": "PSK125" }, "PSK125": { "mode": "PSK", "submode": "PSK125" }, "PSK250": { "mode": "PSK", "submode": "PSK250" }, "PSK500": { "mode": "PSK", "submode": "PSK500" }, "PSK1000": { "mode": "PSK", "submode": "PSK1000" }, "PSK125C12": { "mode": "PSK", "submode": "P125C12" }, "PSK250C6": { "mode": "PSK", "submode": "P2506" }, "PSK500C2": { "mode": "PSK", "submode": "2xP500" }, "PSK500C4": { "mode": "PSK", "submode": "4xP500" }, "PSK800C2": { "mode": "PSK", "submode": "P800RC2" }, "PSK1000C2": { "mode": "PSK", "submode": "P1KC2" }, "PSK125R": { "mode": "PSK", "submode": "P125R" }, "PSK250R": { "mode": "PSK", "submode": "P250R" }, "PSK500R": { "mode": "PSK", "submode": "P500R" }, "PSK1000R": { "mode": "PSK", "submode": "PSK1000R" }, "PSK63RC4": { "mode": "PSK", "submode": "P63R4" }, "PSK63RC5": { "mode": "PSK", "submode": "P63R5" }, "PSK63RC10": { "mode": "PSK", "submode": "P63R10" }, "PSK63RC20": { "mode": "PSK", "submode": "P63R20" }, "PSK63RC32": { "mode": "PSK", "submode": "P63R32" }, "PSK125RC4": { "mode": "PSK", "submode": "P125R4" }, "PSK125RC5": { "mode": "PSK", "submode": "P125R5" }, "PSK125RC10": { "mode": "PSK", "submode": "P125R10" }, "PSK125RC12": { "mode": "PSK", "submode": "P125R12" }, "PSK125RC16": { "mode": "PSK", "submode": "P125R16" }, "PSK250RC2": { "mode": "PSK", "submode": "P250R2" }, "PSK250RC3": { "mode": "PSK", "submode": "P250R3" }, "PSK250RC5": { "mode": "PSK", "submode": "P250R5" }, "PSK250RC6": { "mode": "PSK", "submode": "P250R6" }, "PSK250RC7": { "mode": "PSK", "submode": "P250R7" }, "PSK500RC2": { "mode": "PSK", "submode": "P500R2" }, "PSK500RC3": { "mode": "PSK", "submode": "P500R3" }, "PSK500RC4": { "mode": "PSK", "submode": "P500R4" }, "PSK800RC2": { "mode": "PSK", "submode": "P800RC2" }, "PSK1000RC2": { "mode": "PSK", "submode": "P1KRC2" }, "PSKAM10": { "mode": "PSK", "submode": "PSKAM10" }, "PSKAM31": { "mode": "PSK", "submode": "PSKAM31" }, "PSKAM50": { "mode": "PSK", "submode": "PSKAM50" }, "PSKFEC31": { "mode": "PSK", "submode": "PSKFEC31" }, "PSKHELL": { "mode": "HELL", "submode": "PSKHELL" }, "QPSK31": { "mode": "PSK", "submode": "QPSK31" }, "QPSK63": { "mode": "PSK", "submode": "QPSK63" }, "QPSK125": { "mode": "PSK", "submode": "QPSK125" }, "QPSK250": { "mode": "PSK", "submode": "QPSK250" }, "QPSK500": { "mode": "PSK", "submode": "QPSK500" }, "8PSK125": { "mode": "PSK", "submode": "8PSK125" }, "8PSK125FL": { "mode": "PSK", "submode": "8PSK125FL" }, "8PSK125F": { "mode": "PSK", "submode": "8PSK125F" }, "8PSK250": { "mode": "PSK", "submode": "8PSK250" }, "8PSK250F": { "mode": "PSK", "submode": "8PSK250FL" }, "8PSK250F": { "mode": "PSK", "submode": "8PSK250F" }, "8PSK500": { "mode": "PSK", "submode": "8PSK500" }, "8PSK500F": { "mode": "PSK", "submode": "8PSK500F" }, "8PSK1000": { "mode": "PSK", "submode": "8PSK1000" }, "8PSK1000F": { "mode": "PSK", "submode": "8PSK1000F" }, "8PSK1200F": { "mode": "PSK", "submode": "8PSK1200F" }, "SCAMP_FAST": { "mode": "FSK", "submode": "SCAMP_FAST" }, "SCAMP_SLOW": { "mode": "FSK", "submode": "SCAMP_SLOW" }, "SCAMP_VSLOW": { "mode": "FSK", "submode": "SCAMP_VSLOW" }, "THOR4": { "mode": "THOR", "submode": "THOR4" }, "THOR5": { "mode": "THOR", "submode": "THOR5" }, "THOR8": { "mode": "THOR", "submode": "THOR8" }, "THOR11": { "mode": "THOR", "submode": "THOR11" }, "THOR16": { "mode": "THOR", "submode": "THOR16" }, "THOR22": { "mode": "THOR", "submode": "THOR22" }, "THOR-25X4": { "mode": "THOR", "submode": "THOR25" }, "THOR-50X1": { "mode": "THOR", "submode": "THOR51" }, "THOR-50X2": { "mode": "THOR", "submode": "THOR52" }, "THOR-100": { "mode": "THOR", "submode": "THOR100" }, "THRB": { "mode": "THRB", "submode": "TB1" }, "THRBX": { "mode": "THRB", "submode": "THRBX" }, "TOR": { "mode": "NAVTEX", "submode": "NAVTEX" }, "TOR": { "mode": "SITORB", "submode": "SITORB" }, "USB": { "mode": "SSB", "submode": "USB" }, "LSB": { "mode": "SSB", "submode": "LSB" }, "FT4": { "mode": "MFSK", "submode": "FT4" }, "FST4": { "mode": "MFSK", "submode": "FST4"}, "FST4W": { "mode": "MFSK", "submode": "FST4W"}, "Q65": { "mode": "MFSK", "submode": "Q65"}, "FT2": { "mode": "MFSK", "submode": "FT2" }, "RIBBIT_PIX": { "mode": "OFDM", "submode": "RIBBIT_PIX" }, "RIBBIT_SMS": { "mode": "OFDM", "submode": "RIBBIT_SMS" }, "FREEDATA": { "mode": "DYNAMIC", "submode": "FREEDATA" } } ================================================ FILE: res/data/propagation_modes.json ================================================ [ {"id": "AS", "name": "Aircraft Scatter"}, {"id": "AUE", "name": "Aurora-E"}, {"id": "AUR", "name": "Aurora"}, {"id": "BS", "name": "Back scatter"}, {"id": "ECH", "name": "EchoLink"}, {"id": "EME", "name": "Earth-Moon-Earth"}, {"id": "ES", "name": "Sporadic E"}, {"id": "F2", "name": "F2 Reflection"}, {"id": "FAI", "name": "Field Aligned Irregularities"}, {"id": "GWAVE", "name": "Ground Wave"}, {"id": "INTERNET", "name": "Internet-assisted"}, {"id": "ION", "name": "Ionoscatter"}, {"id": "IRL", "name": "IRLP"}, {"id": "LOS", "name": "Line of Sight"}, {"id": "MS", "name": "Meteor scatter"}, {"id": "RPT", "name": "Terrestrial or atmospheric repeater or transponder"}, {"id": "RS", "name": "Rain scatter"}, {"id": "SAT", "name": "Satellite"}, {"id": "TEP", "name": "Trans-equatorial"}, {"id": "TR", "name": "Tropospheric ducting"} ] ================================================ FILE: res/data/sat_modes.json ================================================ [ {"id": "HU", "name": "H/U (21 / 435)"}, {"id": "AU", "name": "A/U (29 / 435)"}, {"id": "VV", "name": "V/V (145 / 145)"}, {"id": "VU", "name": "V/U (145 / 435)"}, {"id": "VS", "name": "V/S (145 / 2400)"}, {"id": "UV", "name": "U/V (435 / 145)"}, {"id": "UU", "name": "U/U (435 / 435)"}, {"id": "US", "name": "U/S (435 / 2400)"}, {"id": "LV", "name": "L/V (1268 / 145)"}, {"id": "LU", "name": "L/U (1268 / 435)"}, {"id": "LS", "name": "L/S (1268 / 2400)"}, {"id": "LX", "name": "L/X (1268 / 10450)"}, {"id": "SX", "name": "S/X (2400 / 10450)"} ] ================================================ FILE: res/flags/flags.qrc ================================================ 16/abkhazia.png 16/AD.png 16/AE.png 16/AF.png 16/AG.png 16/AI.png 16/AL.png 16/AM.png 16/AN.png 16/AO.png 16/AQ.png 16/AR.png 16/AS.png 16/AT.png 16/AU.png 16/AW.png 16/AX.png 16/AZ.png 16/BA.png 16/basque-country.png 16/BB.png 16/BD.png 16/BE.png 16/BF.png 16/BG.png 16/BH.png 16/BI.png 16/BJ.png 16/BL.png 16/BM.png 16/BN.png 16/BO.png 16/BR.png 16/british-antarctic-territory.png 16/BS.png 16/BT.png 16/BW.png 16/BY.png 16/BZ.png 16/CA.png 16/CC.png 16/CD.png 16/CF.png 16/CG.png 16/CH.png 16/CI.png 16/CK.png 16/CL.png 16/CM.png 16/CN.png 16/CO.png 16/commonwealth.png 16/CR.png 16/CU.png 16/CV.png 16/CW.png 16/CX.png 16/CY.png 16/CZ.png 16/DE.png 16/DJ.png 16/DK.png 16/DM.png 16/DO.png 16/DZ.png 16/EC.png 16/EE.png 16/EG.png 16/EH.png 16/england.png 16/ER.png 16/ES.png 16/ET.png 16/EU.png 16/FI.png 16/FJ.png 16/FK.png 16/FM.png 16/FO.png 16/FR.png 16/GA.png 16/GB.png 16/GD.png 16/GE.png 16/GG.png 16/GH.png 16/GI.png 16/GL.png 16/GM.png 16/GN.png 16/GQ.png 16/GR.png 16/GS.png 16/GT.png 16/GU.png 16/GW.png 16/GY.png 16/HK.png 16/HN.png 16/HR.png 16/HT.png 16/HU.png 16/IC.png 16/ID.png 16/IE.png 16/IL.png 16/IM.png 16/IN.png 16/IQ.png 16/IR.png 16/IS.png 16/IT.png 16/JE.png 16/JM.png 16/JO.png 16/JP.png 16/KE.png 16/KG.png 16/KH.png 16/KI.png 16/KM.png 16/KN.png 16/kosovo.png 16/KP.png 16/KR.png 16/KW.png 16/KY.png 16/KZ.png 16/LA.png 16/LB.png 16/LC.png 16/LI.png 16/LK.png 16/LR.png 16/LS.png 16/LT.png 16/LU.png 16/LV.png 16/LY.png 16/MA.png 16/mars.png 16/MC.png 16/MD.png 16/ME.png 16/MF.png 16/MG.png 16/MH.png 16/MK.png 16/ML.png 16/MM.png 16/MN.png 16/MO.png 16/MP.png 16/MQ.png 16/MR.png 16/MS.png 16/MT.png 16/MU.png 16/MV.png 16/MW.png 16/MX.png 16/MY.png 16/MZ.png 16/NA.png 16/nagorno-karabakh.png 16/nato.png 16/NC.png 16/NE.png 16/NF.png 16/NG.png 16/NI.png 16/NL.png 16/NO.png 16/northern-cyprus.png 16/NP.png 16/NR.png 16/NU.png 16/NZ.png 16/olympics.png 16/OM.png 16/PA.png 16/PE.png 16/PF.png 16/PG.png 16/PH.png 16/PK.png 16/PL.png 16/PN.png 16/PR.png 16/PS.png 16/PT.png 16/PW.png 16/PY.png 16/QA.png 16/red-cross.png 16/RO.png 16/RS.png 16/RU.png 16/RW.png 16/SA.png 16/SB.png 16/SC.png 16/scotland.png 16/SD.png 16/SE.png 16/SG.png 16/SH.png 16/SI.png 16/SK.png 16/SL.png 16/SM.png 16/SN.png 16/SO.png 16/somaliland.png 16/south-ossetia.png 16/SR.png 16/SS.png 16/ST.png 16/SV.png 16/SY.png 16/SZ.png 16/TC.png 16/TD.png 16/TF.png 16/TG.png 16/TH.png 16/TJ.png 16/TK.png 16/TL.png 16/TM.png 16/TN.png 16/TO.png 16/TR.png 16/TT.png 16/TV.png 16/TW.png 16/TZ.png 16/UA.png 16/UG.png 16/united-nations.png 16/unknown.png 16/US.png 16/UY.png 16/UZ.png 16/VA.png 16/VC.png 16/VE.png 16/VG.png 16/VI.png 16/VN.png 16/VU.png 16/wales.png 16/WF.png 16/WS.png 16/XK.png 16/YE.png 16/YT.png 16/ZA.png 16/ZM.png 16/ZW.png 24/abkhazia.png 24/AD.png 24/AE.png 24/AF.png 24/AG.png 24/AI.png 24/AL.png 24/AM.png 24/AN.png 24/AO.png 24/AQ.png 24/AR.png 24/AS.png 24/AT.png 24/AU.png 24/AW.png 24/AX.png 24/AZ.png 24/BA.png 24/basque-country.png 24/BB.png 24/BD.png 24/BE.png 24/BF.png 24/BG.png 24/BH.png 24/BI.png 24/BJ.png 24/BL.png 24/BM.png 24/BN.png 24/BO.png 24/BR.png 24/british-antarctic-territory.png 24/BS.png 24/BT.png 24/BW.png 24/BY.png 24/BZ.png 24/CA.png 24/CC.png 24/CD.png 24/CF.png 24/CG.png 24/CH.png 24/CI.png 24/CK.png 24/CL.png 24/CM.png 24/CN.png 24/CO.png 24/commonwealth.png 24/CR.png 24/CU.png 24/CV.png 24/CW.png 24/CX.png 24/CY.png 24/CZ.png 24/DE.png 24/DJ.png 24/DK.png 24/DM.png 24/DO.png 24/DZ.png 24/EC.png 24/EE.png 24/EG.png 24/EH.png 24/england.png 24/ER.png 24/ES.png 24/ET.png 24/EU.png 24/FI.png 24/FJ.png 24/FK.png 24/FM.png 24/FO.png 24/FR.png 24/GA.png 24/GB.png 24/GD.png 24/GE.png 24/GG.png 24/GH.png 24/GI.png 24/GL.png 24/GM.png 24/GN.png 24/GQ.png 24/GR.png 24/GS.png 24/GT.png 24/GU.png 24/GW.png 24/GY.png 24/HK.png 24/HN.png 24/HR.png 24/HT.png 24/HU.png 24/IC.png 24/ID.png 24/IE.png 24/IL.png 24/IM.png 24/IN.png 24/IQ.png 24/IR.png 24/IS.png 24/IT.png 24/JE.png 24/JM.png 24/JO.png 24/JP.png 24/KE.png 24/KG.png 24/KH.png 24/KI.png 24/KM.png 24/KN.png 24/kosovo.png 24/KP.png 24/KR.png 24/KW.png 24/KY.png 24/KZ.png 24/LA.png 24/LB.png 24/LC.png 24/LI.png 24/LK.png 24/LR.png 24/LS.png 24/LT.png 24/LU.png 24/LV.png 24/LY.png 24/MA.png 24/mars.png 24/MC.png 24/MD.png 24/ME.png 24/MF.png 24/MG.png 24/MH.png 24/MK.png 24/ML.png 24/MM.png 24/MN.png 24/MO.png 24/MP.png 24/MQ.png 24/MR.png 24/MS.png 24/MT.png 24/MU.png 24/MV.png 24/MW.png 24/MX.png 24/MY.png 24/MZ.png 24/NA.png 24/nagorno-karabakh.png 24/nato.png 24/NC.png 24/NE.png 24/NF.png 24/NG.png 24/NI.png 24/NL.png 24/NO.png 24/northern-cyprus.png 24/NP.png 24/NR.png 24/NU.png 24/NZ.png 24/olympics.png 24/OM.png 24/PA.png 24/PE.png 24/PF.png 24/PG.png 24/PH.png 24/PK.png 24/PL.png 24/PN.png 24/PR.png 24/PS.png 24/PT.png 24/PW.png 24/PY.png 24/QA.png 24/red-cross.png 24/RO.png 24/RS.png 24/RU.png 24/RW.png 24/SA.png 24/SB.png 24/SC.png 24/scotland.png 24/SD.png 24/SE.png 24/SG.png 24/SH.png 24/SI.png 24/SK.png 24/SL.png 24/SM.png 24/SN.png 24/SO.png 24/somaliland.png 24/south-ossetia.png 24/SR.png 24/SS.png 24/ST.png 24/SV.png 24/SY.png 24/SZ.png 24/TC.png 24/TD.png 24/TF.png 24/TG.png 24/TH.png 24/TJ.png 24/TK.png 24/TL.png 24/TM.png 24/TN.png 24/TO.png 24/TR.png 24/TT.png 24/TV.png 24/TW.png 24/TZ.png 24/UA.png 24/UG.png 24/united-nations.png 24/unknown.png 24/US.png 24/UY.png 24/UZ.png 24/VA.png 24/VC.png 24/VE.png 24/VG.png 24/VI.png 24/VN.png 24/VU.png 24/wales.png 24/WF.png 24/WS.png 24/XK.png 24/YE.png 24/YT.png 24/ZA.png 24/ZM.png 24/ZW.png 32/abkhazia.png 32/AD.png 32/AE.png 32/AF.png 32/AG.png 32/AI.png 32/AL.png 32/AM.png 32/AN.png 32/AO.png 32/AQ.png 32/AR.png 32/AS.png 32/AT.png 32/AU.png 32/AW.png 32/AX.png 32/AZ.png 32/BA.png 32/basque-country.png 32/BB.png 32/BD.png 32/BE.png 32/BF.png 32/BG.png 32/BH.png 32/BI.png 32/BJ.png 32/BL.png 32/BM.png 32/BN.png 32/BO.png 32/BR.png 32/british-antarctic-territory.png 32/BS.png 32/BT.png 32/BW.png 32/BY.png 32/BZ.png 32/CA.png 32/CC.png 32/CD.png 32/CF.png 32/CG.png 32/CH.png 32/CI.png 32/CK.png 32/CL.png 32/CM.png 32/CN.png 32/CO.png 32/commonwealth.png 32/CR.png 32/CU.png 32/CV.png 32/CW.png 32/CX.png 32/CY.png 32/CZ.png 32/DE.png 32/DJ.png 32/DK.png 32/DM.png 32/DO.png 32/DZ.png 32/EC.png 32/EE.png 32/EG.png 32/EH.png 32/england.png 32/ER.png 32/ES.png 32/ET.png 32/EU.png 32/FI.png 32/FJ.png 32/FK.png 32/FM.png 32/FO.png 32/FR.png 32/GA.png 32/GB.png 32/GD.png 32/GE.png 32/GG.png 32/GH.png 32/GI.png 32/GL.png 32/GM.png 32/GN.png 32/GQ.png 32/GR.png 32/GS.png 32/GT.png 32/GU.png 32/GW.png 32/GY.png 32/HK.png 32/HN.png 32/HR.png 32/HT.png 32/HU.png 32/IC.png 32/ID.png 32/IE.png 32/IL.png 32/IM.png 32/IN.png 32/IQ.png 32/IR.png 32/IS.png 32/IT.png 32/JE.png 32/JM.png 32/JO.png 32/JP.png 32/KE.png 32/KG.png 32/KH.png 32/KI.png 32/KM.png 32/KN.png 32/kosovo.png 32/KP.png 32/KR.png 32/KW.png 32/KY.png 32/KZ.png 32/LA.png 32/LB.png 32/LC.png 32/LI.png 32/LK.png 32/LR.png 32/LS.png 32/LT.png 32/LU.png 32/LV.png 32/LY.png 32/MA.png 32/mars.png 32/MC.png 32/MD.png 32/ME.png 32/MF.png 32/MG.png 32/MH.png 32/MK.png 32/ML.png 32/MM.png 32/MN.png 32/MO.png 32/MP.png 32/MQ.png 32/MR.png 32/MS.png 32/MT.png 32/MU.png 32/MV.png 32/MW.png 32/MX.png 32/MY.png 32/MZ.png 32/NA.png 32/nagorno-karabakh.png 32/nato.png 32/NC.png 32/NE.png 32/NF.png 32/NG.png 32/NI.png 32/NL.png 32/NO.png 32/northern-cyprus.png 32/NP.png 32/NR.png 32/NU.png 32/NZ.png 32/olympics.png 32/OM.png 32/PA.png 32/PE.png 32/PF.png 32/PG.png 32/PH.png 32/PK.png 32/PL.png 32/PN.png 32/PR.png 32/PS.png 32/PT.png 32/PW.png 32/PY.png 32/QA.png 32/red-cross.png 32/RO.png 32/RS.png 32/RU.png 32/RW.png 32/SA.png 32/SB.png 32/SC.png 32/scotland.png 32/SD.png 32/SE.png 32/SG.png 32/SH.png 32/SI.png 32/SK.png 32/SL.png 32/SM.png 32/SN.png 32/SO.png 32/somaliland.png 32/south-ossetia.png 32/SR.png 32/SS.png 32/ST.png 32/SV.png 32/SY.png 32/SZ.png 32/TC.png 32/TD.png 32/TF.png 32/TG.png 32/TH.png 32/TJ.png 32/TK.png 32/TL.png 32/TM.png 32/TN.png 32/TO.png 32/TR.png 32/TT.png 32/TV.png 32/TW.png 32/TZ.png 32/UA.png 32/UG.png 32/united-nations.png 32/unknown.png 32/US.png 32/UY.png 32/UZ.png 32/VA.png 32/VC.png 32/VE.png 32/VG.png 32/VI.png 32/VN.png 32/VU.png 32/wales.png 32/WF.png 32/WS.png 32/XK.png 32/YE.png 32/YT.png 32/ZA.png 32/ZM.png 32/ZW.png 64/abkhazia.png 64/AD.png 64/AE.png 64/AF.png 64/AG.png 64/AI.png 64/AL.png 64/AM.png 64/AN.png 64/AO.png 64/AQ.png 64/AR.png 64/AS.png 64/AT.png 64/AU.png 64/AW.png 64/AX.png 64/AZ.png 64/BA.png 64/basque-country.png 64/BB.png 64/BD.png 64/BE.png 64/BF.png 64/BG.png 64/BH.png 64/BI.png 64/BJ.png 64/BL.png 64/BM.png 64/BN.png 64/BO.png 64/BR.png 64/british-antarctic-territory.png 64/BS.png 64/BT.png 64/BW.png 64/BY.png 64/BZ.png 64/CA.png 64/CC.png 64/CD.png 64/CF.png 64/CG.png 64/CH.png 64/CI.png 64/CK.png 64/CL.png 64/CM.png 64/CN.png 64/CO.png 64/commonwealth.png 64/CR.png 64/CU.png 64/CV.png 64/CW.png 64/CX.png 64/CY.png 64/CZ.png 64/DE.png 64/DJ.png 64/DK.png 64/DM.png 64/DO.png 64/DZ.png 64/EC.png 64/EE.png 64/EG.png 64/EH.png 64/england.png 64/ER.png 64/ES.png 64/ET.png 64/EU.png 64/FI.png 64/FJ.png 64/FK.png 64/FM.png 64/FO.png 64/FR.png 64/GA.png 64/GB.png 64/GD.png 64/GE.png 64/GG.png 64/GH.png 64/GI.png 64/GL.png 64/GM.png 64/GN.png 64/GQ.png 64/GR.png 64/GS.png 64/GT.png 64/GU.png 64/GW.png 64/GY.png 64/HK.png 64/HN.png 64/HR.png 64/HT.png 64/HU.png 64/IC.png 64/ID.png 64/IE.png 64/IL.png 64/IM.png 64/IN.png 64/IQ.png 64/IR.png 64/IS.png 64/IT.png 64/JE.png 64/JM.png 64/JO.png 64/JP.png 64/KE.png 64/KG.png 64/KH.png 64/KI.png 64/KM.png 64/KN.png 64/kosovo.png 64/KP.png 64/KR.png 64/KW.png 64/KY.png 64/KZ.png 64/LA.png 64/LB.png 64/LC.png 64/LI.png 64/LK.png 64/LR.png 64/LS.png 64/LT.png 64/LU.png 64/LV.png 64/LY.png 64/MA.png 64/mars.png 64/MC.png 64/MD.png 64/ME.png 64/MF.png 64/MG.png 64/MH.png 64/MK.png 64/ML.png 64/MM.png 64/MN.png 64/MO.png 64/MP.png 64/MQ.png 64/MR.png 64/MS.png 64/MT.png 64/MU.png 64/MV.png 64/MW.png 64/MX.png 64/MY.png 64/MZ.png 64/NA.png 64/nagorno-karabakh.png 64/nato.png 64/NC.png 64/NE.png 64/NF.png 64/NG.png 64/NI.png 64/NL.png 64/NO.png 64/northern-cyprus.png 64/NP.png 64/NR.png 64/NU.png 64/NZ.png 64/olympics.png 64/OM.png 64/PA.png 64/PE.png 64/PF.png 64/PG.png 64/PH.png 64/PK.png 64/PL.png 64/PN.png 64/PR.png 64/PS.png 64/PT.png 64/PW.png 64/PY.png 64/QA.png 64/red-cross.png 64/RO.png 64/RS.png 64/RU.png 64/RW.png 64/SA.png 64/SB.png 64/SC.png 64/scotland.png 64/SD.png 64/SE.png 64/SG.png 64/SH.png 64/SI.png 64/SK.png 64/SL.png 64/SM.png 64/SN.png 64/SO.png 64/somaliland.png 64/south-ossetia.png 64/SR.png 64/SS.png 64/ST.png 64/SV.png 64/SY.png 64/SZ.png 64/TC.png 64/TD.png 64/TF.png 64/TG.png 64/TH.png 64/TJ.png 64/TK.png 64/TL.png 64/TM.png 64/TN.png 64/TO.png 64/TR.png 64/TT.png 64/TV.png 64/TW.png 64/TZ.png 64/UA.png 64/UG.png 64/united-nations.png 64/unknown.png 64/US.png 64/UY.png 64/UZ.png 64/VA.png 64/VC.png 64/VE.png 64/VG.png 64/VI.png 64/VN.png 64/VU.png 64/wales.png 64/WF.png 64/WS.png 64/XK.png 64/YE.png 64/YT.png 64/ZA.png 64/ZM.png 64/ZW.png ================================================ FILE: res/icons/icons.qrc ================================================ baseline-play_arrow-24px.svg baseline-pause-24px.svg baseline-search-24px.svg baseline-stop-24px.svg skip_next-24px.svg skip_previous-24px.svg cloud_upload-24px.svg cloud_download-24px.svg filter_list-24px.svg zoom_out-24px.svg zoom_in-24px.svg done-24px.svg check_circle-24px.svg help-24px.svg cancel-24px.svg info-24px.svg close-24px.svg alert.svg connect.svg disconnect.svg warning.svg loading.gif baseline-play_back-24px.svg baseline-play_down-24px.svg baseline-play_up-24px.svg password.png menu.svg search-globe.svg search-globe_red.svg search-globe_green.svg search-globe_orange.svg delete-button.svg new-window.svg clear-button.svg color-palette-light.svg color-palette-dark.svg ================================================ FILE: res/io.github.foldynl.QLog.metainfo.xml ================================================ io.github.foldynl.QLog qlog.desktop CC0-1.0 GPL-3.0-or-later QLog Ladislav Foldyna Ladislav Foldyna Amateur radio logbook

QLog is an Amateur Radio logging application for Linux, Windows. It is based on the Qt framework and uses SQLite as database backend. QLog aims to be as simple as possible, but to provide everything the operator expects from the log to be. This log is not currently focused on contests.

Features:

  • Customizable GUI
  • Rig control via Hamlib, TCI, FLRig
  • Rotator control via Hamlib
  • HamQTH and QRZ.com callbook integration
  • DX cluster integration
  • LoTW, eQSL, QRZ.com, Clublog, HRDLog.net, ON4KST Chat, Cloudlog/Wavelog integration (eQSL includes QSL pictures download)
  • Secure Password Storage for all services with password or security token
  • Online and Offline map
  • Club Member lookup
  • CW Keyer Support - CWDaemon, FLDigi (all supported modes), Morse Over CAT, WinKey v1 or v2
  • Bandmap
  • CW Console
  • WSJT-X integration
  • Station Location Profile support
  • Various station statistics
  • Basic Awards support
  • Custom QSO Filters
  • NO ads, NO user tracking, NO hidden telemetry - simply free and open-source
  • SQLite backend
The QLog main window https://foldynl.github.io/QLog/screens/qlog_main.png https://github.com/foldynl/QLog https://github.com/foldynl/QLog/issues https://github.com/foldynl/QLog/wiki https://github.com/foldynl https://github.com/foldynl/QLog https://github.com/foldynl/QLog/blob/master/CONTRIBUTING.md
  • [NEW] - Added Split detection
  • [NEW] - Added Developer and Support tools (PR #991 @aa5sh @foldynl)
  • [NEW] - Added a simple QSL label printing dialog (issue #562)
  • [NEW] - Added Cabrillo contest export
  • [NEW] - Added direct labeling of QSL Requested and QSL Received to the QSO context menu (PR #982 @aa5sh)
  • [NEW] - DXCC Submission Report (PR #967 @aa5sh)
  • [NEW] - DXC - Search pre-fills the callsign from New Contact if callsign is present
  • [NEW] - Settings - Added Danger Zone tab with Delete all passwords and QSOs
  • [CHANGED] - Mutex-free Omnirig1 and Omnirig2
  • [CHANGED] - POTA/SOTA/WWFF/SAT list are download from QLog LOV-repo
  • [CHANGED] - Used an external lib for CSV parsing for LOV Download
  • [CHANGED] - Import - ADIF import updates DXCC only if it is missing in the QSO (issue #983)
  • [REMOVED] - Import - ADIF import does not include the option to update DXCC during the import - no longer needed
  • Fixed Callsign disappears when calling a DX in a split via VFO-B (issue #799)
  • Fixing Rendering issue (issue #989)
  • Updated debian packaging (PR @979 thx @dawkagaming)
  • Hamlib rigctld switching between VFO A/B doesnt change the band mode - added workaround (issue #999)
  • Awards internal redesign
  • Removed AN from the WAC award (issue #1010)
  • Fixed Online Map OSM Access Blocked banner (issue #956)
  • Fixed Package build process issue - openssl-dev is missing (issue #957)
  • Fixed Missing dependence for QTKeychain (issue #964)
  • Added French Translation (PR #969 thx @fillods)
  • [NEW] - Added Pack and Unpack Data and Setting - Computer to Computer Migration (issue #535)
  • [NEW] - Added Rig Sharing via Rigctld (PR #736 issue #159 @aa5sh @foldynl)
  • [NEW] - Added QSL Gallery
  • [NEW] - QSO Filter - Added REGEXP operator
  • [NEW] - Settings - TQSL - Added Path auto-detect and validation
  • [NEW] - Upload QSO - Added LoTW Station Location Combo (PR #929 @TrgoSk @foldynl)
  • [NEW] - QSO Export - Added Station Profile Filter
  • [NEW] - BandMap shows emergency frequencies (issue #462)
  • [NEW] - Added Speed Up Down Macros - WinKey and CWDaemon (issue #491)
  • [NEW] - Settings Winkey v2 - Added PaddleOnly Sidetone (issue #739)
  • [NEW] - Settings Winkey v2 and CWDaemon - Added Sidetone Freq
  • [NEW] - All County fields contain Completer for Ukraine, US, Japan, NZ, Spanish, Russia (PR #785 @aa5sh @foldynl)
  • [NEW] - Added County Awards for Ukraine, US, Japan, NZ, Spanish, Russia (PR #785 @aa5sh @foldynl)
  • [NEW] - ADIF 3.1.7 - Added new modes FT2, FREEDATA, RIBBIT_PIX, RIBBIT_SMS (issue #934)
  • [CHANGED] - Generic FTx for FT8/FT4 (FT2) in Alert and DXC filter (issue #937)
  • [CHANGED] - LoTW QSL matching algorithm uses Mode and Mode Group matching (issue #942)
  • [CHANGED] - Reduced download period for DXCC Entities from 21 to 7 days
  • LogbookWidget: Delete Performance Optimization
  • Fixed Awards POTA Activator Filter
  • Fixed man page
  • Fixing SATmode Activity is blank (issue #948)
  • [NEW] - Rig Widget - tuning rig with mouse (issue #855)
  • [NEW] - Awards - Added User Filter Combo (issue #870)
  • [NEW] - Statistics - Added User Filter; new Dialog layout (issue #870)
  • [NEW] - Alerts - Added detailed Log Status settings (issue #817)
  • [NEW] - Settings - Enable/Disable sending color-coded status indicators back to WSJT-X (issue #885 @aa5sh @foldynl)
  • [CHANGED] - Omnirig Drivers: Removed QT AXContainers - pure Windows API code
  • [CHANGED] - Enabled Chekbox in Alerts Table (issue #867)
  • [CHANGED] - Serial Port Completer contains Horizontal ScrollBar
  • [CHANGED] - TCI - Settings - Default PWR defines the maximum output power
  • Fixed Omnirig drivers keep sending frequency change (issue #872)
  • Fixed ADIF import does not accept a default operator name (issue #884)
  • Fixed HamQTH URL from http to https (PR #886 @aa5sh)
  • Fixed Missing Band value when QSO from FLDigi (issue #892)
  • [CHANGED] - DXC - VE7CC-type Cluster is correctly detected (PR #851 @kyleboyle)
  • [CHANGED] - Chat - Changed ON4KST URL (issue #857)
  • Fixed missing clear button for Search Callsign (issue #753)
  • Fixed Updated QSOs are not sent to wavelog (issue #847)
  • Fixed Fixed Omnirig(v2) getFreq, getVFO, setFreq (issue #853)
  • Fixed ADI Header does not follow ADIF Spec (issue #859)
  • Fixed OmniRig settings are not saved (issue #862)
  • [NEW] - Adds theme options - native, light, dark (PR #718 @kyleboyle)
  • [NEW] - Implemented ADIF 3.1.6
  • [NEW] - ADIF 3.1.6 - Added new modes FSK and MTONE
  • [NEW] - ADIF 3.1.6 - Added new contest IDs
  • [NEW] - ADIF 3.1.6 - Added new column EQSL_AG (Import/Export only)
  • [NEW] - Statistics, QSODetail contain deleted DXCC Entities (PR #728 @aa5sh @foldynl)
  • [NEW] - Settings Hamlib - Added support to define CIV Addr for Icom and Ten-Tec (issue #747)
  • [NEW] - Settings Hamlib - Added RTS and DTR control (PR #809 @aa5sh)
  • [NEW] - Settings GUI - Added options to switch distance unit
  • [NEW] - Added Linux manpage (PR #791 @dawkagaming)
  • [NEW] - Added a link to the GitHub release notes under Help-Whats New
  • [NEW] - Added workaround for WriteLog - always call callbook lookups (PR #833 @sjwoodr)
  • [NEW] - Awards - Added Not-Confirmed filter (PR #836 @aa5sh)
  • [NEW] - Rotator Widget - Added buttons to change button profiles
  • [CHANGED] - Settings - Added network loop detection for WSJTX Forward (issue #815 @aa5sh @foldynl)
  • [CHANGED] - Rotator - Rotator timeout is set to 5s (PR #823 @aa5sh)
  • [CHANGED] - Double Spin Boxes - the decimal separator is forced to be a period
  • [DELETED] - WSJTX Widget - Removed Freq and Mode
  • Fixed QSODetail - Anti-meridian bug on map (issue #786)
  • Fixed Statistics: Center maps on QTH longitude (issue #824)
  • Fixed OmniRig disconnecting when its status changed unexpectedly (issue #832)
  • Fixed Spaces after QRZ.com name (issue #767)
  • Fixed SPID Rot1Prog Rotator more 360 deg (issue #775)
  • Fixed build instructions on Debian (PR #777 @leventelist)
  • Fixed Bandmap truncated output despite having space (issue #779)
  • Fixed Statistics - Anti-meridian bug on map (issue #786)
  • Fixed Binary file in the tarballs (issue #794)
  • Fixed LOTW and eQSL upload status updated even when nothing is uploaded (issue #807)
  • Fixed Imported QSOs contain incorrect My DXCC info (issue #812)
  • Fixed Statistics Dialog does not show confirmed grids
  • Fixed QSO filter incorrectly displays inserted date (issue #752)
  • Fixed Logbook Search Icon is not centered (issue #753)
  • Fixed Using CQRLog API Key for Clublog (issue #759)
  • Fixed Online Service Password leaking via debug files (issue #760)
  • Fixed Tabs are not showed properly (PR #762 @aa5sh)
  • Fixed inability to edit Power in QSO Detail (issue #763)
  • [NEW] - NewContact: POTA/SOTA/WWFF/IOTA info is taken from the nearest spot (@aa5sh @foldynl)
  • [NEW] - DXSpots are sent to Flex Radio (PR #694 @aa5sh)
  • [NEW] - CWConsole - Added support for automatic periodic sending of CW Macros (issue #708)
  • [NEW] - Added mapping of Winkey hardware buttons to CW macro keys F1–F4 (issue #711)
  • [NEW] - Added Search Types to the Logbook Search Editbox (issue #733)
  • [NEW] - Rig - Periodic Rig status reporting independent of its changes (PR #730 @aa5sh)
  • [NEW] - Settings - Serial Port Completer for MacOS and Linux (PR #737 @aa5sh)
  • [NEW] - Hamlib - Attempt to send Power On when connecting the Rig
  • [NEW] - POTA Spots Info is received from the API at api.pota.app to improve POTA detection
  • [CHANGED] - Logbook Search Filter Widget - Added Search Widget
  • [CHANGED] - QSO Detail Country boxes use the new Search Filter Widget
  • [CHANGED] - NewContact - RST field uses the Overwrite Mode
  • [CHANGED] - NewContact - Default cursor position in RST is at the first character
  • [CHANGED] - Setting Dialog - Modes - Default Report can define cursor position
  • [CHANGED] - HamlibRot - Changed Poll Interval from 500 to 2000ms
  • Fixed DUPE is not detected for the first Contest QSO (issue #699)
  • Fixed FREQ_RX/BAND_RX is present only when RX and TX freqs are different (issue #714)
  • Fixed Repeated log recording (issue #722)
  • Fixed QSO Filter contains untranslated QSO items (issue #732)
  • [NEW] - Single Dialog for Upload/Download Online Services (issue #448)
  • [NEW] - Added option to swap paddles to Winkey settings (issue #676)
  • [NEW] - Added native support for the FLRig interface (issue #679)
  • [NEW] - Added New Version Notification - only for MacOS and Windows (PR #669 @aa5sh)
  • [NEW] - QRZ Upload - Added support for multiple API Keys
  • [NEW] - Logbook - Added highlighting to the filter button when a filter is active
  • [NEW] - WSJTX - Filtered label is shown when filter is enabled
  • [NEW] - DXC - Filtered label is shown when filter is enabled
  • [NEW] - Added support to Upload Cloudlog/Wavelog
  • [NEW] - Add JS8 to legacy_modes.json (issue #677)
  • [CHANGED] - Unification Settings storage
  • [CHANGED] - Calculate distances according to IARU rules
  • Fixed missing Wsjtx Spot values in the AlertWindow
  • Fixed Rotator Widget Seg Fault for new users (issue #666)
  • [NEW] - Activity Manager - Added SKCC, UKSMG and FISTS as Dynamic Fields
  • [NEW] - QSO - FISTS, SKCC, and UKSMG are auto-filled from MembershipQE (issue #628)
  • [NEW] - Rotator - Added QSO destination needle (issue #644)
  • [NEW] - QSO Detail - Adds grayline and short path (PR #653 @kyleboyle)
  • [CHANGED] - Rotator - Needle colors correspond to the online map (issue #644)
  • Fixed TCI cw_macros must contain RigID (issue #663)
  • Fixed TCI setKeySpeed sets keyer and macros speed (issue #663)
  • Fixed Data mode missing in rig control window - rigctld (issue #660 @aa5sh)
  • Improved DXC Mode detection
  • Updated Simplified Chinese translation
  • Fixed Click on PHONE DX Spots sets wrong mode (issue #453)
  • Fixed No freq via Omnirig for IC7400 (issue #639)
  • [NEW] - Added support to receive QSOs from CSN SAT Device (PR #610 @aa5sh)
  • [NEW] - Bandmap - Multiple independent bandmap windows (PR #593 @kyleboyle @foldynl)
  • [NEW] - Winkey Keyer driver currently supports v1 and v2 hardware
  • [NEW] - QSO Detail - QSLs QSLr Msg is editable
  • [NEW] - Activity Manager - Added new dynamic field - QSL Message Send
  • [CHANGED] - HamlibDrv - Enabled Power, RIT, XIT, Morse for RIG Netctl
  • [CHANGED] - QSO Detail - QSLMSG replaced by QSLMSG_RCVD (issue #633)
  • [CHANGED] - eQSL Upload - Added an option to choose a QSLMsg field
  • [CHANGED] - eQSL Download - eQSL QSLMSG is stored to QLog QSLMSG_RCVD
  • [CHANGED] - eQSL Download - Added QSLMSG_RCVD merging - eQSL message is appended
  • Fixed Speed pot doesn't seem to work with a WinKeyer; double chars (issue #618)
  • Fixed BandMap Spots colour change after a QSO (issue #632)
  • Fixed Incorrect Field Mapping for Received eQSL Messages (issue #633)
  • Fixed Logbook country translations (issue #608)
  • Fixed Unexpected dialog when QSO after contest (issue #614)
  • Fixed Statistics Widget does not display NULL continents (@aa5sh)
  • Fixed Statistics Widget does not display NULL Band, Mode
  • Fixed Statistics Widget TOP10 does not display removed DXCCs
  • Fixed Statistics Widget TOP10 does not display translated country names
  • Fixed Awards Widget does not display removed DXCCs
  • Fixed Unexpected timezone info (issue #600)
  • Fixed DXCC Statistics picks more entities (issue #601)
  • Fixed a crash when no internet connection
  • [NEW] - Awards - Added Slots - total over each band (issue #538)
  • [NEW] - Awards - Added Grid Award - 2/4/6 Chars grid (issue #564)
  • [NEW] - Settings - Added options to switch 12/24 time and date format (issue #573)
  • [NEW] - Activity Manager - Added new dynamic fields - Rig (issue #575)
  • [NEW] - LoTW Import - Fill missing information also for confirmed QSOs (@aa5sh)
  • [NEW] - Added CW macro QTH
  • [NEW] - DXC - Added 15min Trend
  • [CHANGED] - Changed IOTA LOV Source, the official web is used
  • [CHANGED] - CSV Export: Time and Date formats use ISO8601 format (issue #562)
  • [CHANGED] - Settings - Renamed Shortcuts to GUI tab
  • [CHANGED] - LOV - Improved LOV download performance (PR #582 @aa5sh)
  • Partial fix Windows State/Size does not save in case of fullscreen (issue #418)
  • Fixing TCI error when you change Rig (issue #526)
  • Fixed DXCC Award total worked confirmed counts included deleted entities (PR #588 @aa5sh)
  • Fixed Raspberry PI Flatpak - Import Select file dialog crashes (issue #589)
  • Suppressed the ability to edit Contest fields after the start (issue #590)
  • Fixed compilation issue under Debian 12 (issue #571)
  • Fixed Incorrect GPL version in rpm/deb packages (issue #572)
  • Fixed MacOS floating utility window bug (PR #576 @kyleboyle)
  • Updated IT translation
  • [NEW] - Logbook - Added a new context menu item - Update QSO from Callbook (issue #450 @aa5sh)
  • [NEW] - DIGI mode is used in case of DXC Digi Spots (issue #480)
  • [NEW] - DXC - Retrieve information about SOTA, POTA, IOTA and WWFF from comment (issue #482)
  • [NEW] - Alert - Added SOTA, POTA, IOTA and WWFF filter
  • [NEW] - Added the COM Port Completer for Windows platform (issue #490)
  • [NEW] - Settings - Added DXCC Confirmed By options (issue #508)
  • [NEW] - Added POTA Export Formatter (activator/hunter) (PR #514 @kyleboyle)
  • [NEW] - CW Console - CW Halt with the user-defined shortcut (issue #518)
  • [NEW] - Added an input parameter to save debug message to file
  • [NEW] - Logbook - Added sorting function to logbook table columns (PR #540 @kyleboyle)
  • [NEW] - Network Notification - Added Rig Status Notification
  • [NEW] - Implemented ADIF 3.1.5
  • [NEW] - ADIF 3.1.5 - Added new submodes FSKH245 and FSKH105 for HELL
  • [NEW] - ADIF 3.1.5 - Added new contest IDs
  • [NEW] - ADIF 3.1.5 - Added new columns (Import/Export only)
  • [NEW] - ADIF 3.1.5 - Added My DARC DOK to Station Profile
  • [CHANGED] - Settings: disabled band and mode name modification
  • [CHANGED] - DX Stats contain all enabled bands (issue #538)
  • [CHANGED] - Removed Freq, TimeDate On/Off Data Type Indicators (issue #552)
  • [CHANGED] - ADIF 3.1.5 - VUCC and MY_VUCC can contain 6 or 4-chars locators
  • [CHANGED] - Stop exporting default value N for qsl_rcvd, qsl_sent, lotw/dcl/eslq_qsl_rcvd/sent
  • [CHANGED] - Extended QSL/Import Dupe matching rule to Callsign, Band, Mode, Time and Sat_Name (issue #563)
  • Fixed MacOS - keep floating windows visible on app unfocus (issue #530)
  • Fixed Contest Filter ignores the first QSO (issue #529)
  • Fixed It is not possible to quit Qlog with detached widgets Rot and Rig (issue #534)
  • Fixed ADX/CSV/JSON do not export non-ASCII chars (issue #542)
  • Fixed Checking the 60m checkbox in cluster filters allows 160m spots to appear (issue #543 @aa5sh)
  • Fixed Problems uploading to QRZ.com (issue #559 PR #561 @aa5sh)
  • Fixed DX Stat screen is jumping up/down
  • Fixed Omnirig drivers: Digi modes are not correclty recognized
  • Fixed Bands - Added missing 8m band (issue #515)
  • Fixed CW Console - EXCSTR does not work properly (issue #517)
  • Fixed Activity Manager - Missing Propagation Mode None (issue #519)
  • Fixed QSO Filter - filter fields with random order (PR #525 @aa5sh)
  • Fixed TCI error when you change Rig (issue #526)
  • Fixed NewContact - satellite mode too wide (issue #527)
  • [NEW] - Activity Manager - merged Layout Manager and profiles (issue #408)
  • [NEW] - Activity Manager - Added new dynamic fields - Contest fields, RX/TX Power
  • [NEW] - Added light support for contests (issue #345)
  • [NEW] - Added CW macros EXCHSTR, EXCHNR, EXCHNRN
  • [NEW] - Export Filter - Added user filter combo (original idea PR #476 @aa5sh)
  • [NEW] - New Contact - Added expand/collapse button to QSO field tab widget (PR #495 @kyleboyle)
  • [NEW] - Alert - Added CQZ and ITUZ filters
  • [NEW] - KSTChat - Added a new 40MHz room (PR #496 @kyleboyle)
  • [NEW] - Station Profile contains Operator Callsign (issue #441 @kyleboyle)
  • [NEW] - Station Profile contains County (issue #493 @kyleboyle)
  • [NEW] - Statistics - Adds time of day and better qso mapping (PR #501 @kyleboyle)
  • [NEW] - Bandmap - Tooltip shows a spotter callsign (PR #507 @Skittlebrau)
  • [CHANGED] - New Contact - Renamed DXCC Tab to DX Stats contains DXCC and Station Statistics (issue #477)
  • [CHANGED] - QSL Import dialog - Detail text is selectable by mouse and keyboard
  • [CHANGED] - Removed Main Menu Layout; Activity Manager is in the bottom-left corner
  • [CHANGED] - Removed Keep Options from the Equipment Menu - use Activity Manager for it
  • Fixed issue when CW is always selected after Settings exiting or connecting the Rig
  • Updated Timezone definition file - version 2024b
  • [NEW] - DXC - Added Full-text search
  • [NEW] - Select S in RST Edit when focused (issue #454)
  • [NEW] - Alerts - Added Member Column
  • [CHANGED] - HamlibDrv Rig/Rot- Added multiplatform reliable sleep
  • [CHANGED] - Changed Backup policy
  • [CHANGED] - Logbook page size - improved performance
  • [CHANGED] - Logbook - CTRL-A (Select All) is disabled
  • [CHANGED] - Awards - Bands are displayed based on the Settings (issue #452)
  • [CHANGED] - WSJTX - More reliable detection of CQ stations (PR #471 @aa5sh)
  • [CHANGED] - WSJTX - SOTA/POTA/WWFF/SIG are being added to the logged QSO (PR #463 @aa5sh)
  • [CHANGED] - Stats - Add a confirmation dialog for displaying over 50k QSOs on the map
  • [CHANGED] - New Contact - Starting QSO Timer when Rig online and WSJTX Update Callsign Status is received
  • [CHANGED] - Added a postponed handling for Rig soft errors (issue #472)
  • Fixed WSJT-X does not emit band change if rig is disconnected (issue #447)
  • Fixed Wrong import of ADIF file of another log program (issue #455)
  • Fixed WSJTX log record is stored incorrectly if it contains non-ASCII chars(issue #458)
  • Fixed ADIF import does not import records with old DXCC Entities (issue #459)
  • Fixed ADIF import incorrectly uses Station Profile parameters (issue #461)
  • Fixed Logbook - QSO Table Column Width Does Not Stick (issue #464)
  • Fixed Alerts Window displays OOB Spots (issue #469)
  • Fixed Field values from past QSOs are used incorrectly in case of WSJTX QSOs (#issue 470)
  • [NEW] - Logbook - Added Send DX Spot to the QSO Context Menu
  • [NEW] - DX Filter - Added Dedup Time/Freq difference setting (@aa5sh)
  • [NEW] - Rig Setting - Added RTS/DTR PTT Type support (issue #353)
  • [NEW] - Bandmap - Scrollbar position is saved per band (issue #415)
  • [NEW] - New Contact - Added a dynamic value completer for SIG field (issue #425)
  • [NEW] - Awards - Added SOTA/POTA/WWFF (@aa5sh issue #311)
  • [NEW] - Awards - Added Not-Worked Filter
  • [NEW] - New Contact - Added Long Path Azimuth info
  • [NEW] - POTA Fields allow a comma-delimited list of one or more POTA Refs
  • [NEW] - WSJTX tunes freq/mode like Rig if rig is disconnected
  • [CHANGED] - Alert Widget is a Dock Widget (issue #399)
  • [CHANGED] - QLog adds more information from callbook for WSJTX QSOs (issues #403 #405 #420)
  • [CHANGED] - File Open dialogs are not a native dialog under Linux (issue #427)
  • [CHANGED] - Profiles transferred to DB
  • [CHANGED] - LOV last_dates transferred to DB
  • [CHANGED] - DX Cluster - Login Callsign is always the base Callsign
  • [REMOVED] - Setting DXCC Date
  • Fix for MacOS Layout Geometry Restore (@aa5sh)
  • Fixed TQSL does not block GUI thread
  • Fixed MacOS build process (@aa5sh)
  • Fixed Field QSL Send Via should be retained (issue #413)
  • Fixed Set rotator position fails if azimuth > 180 (issue #417)
  • Fixed Windows State/Size does not save in case of fullscreen (issue #418)
  • Fixed Significant rounding during azimuth calculation (issue #422)
  • Updated Simplified Chinese translation
  • Updated Spanish translaction
  • Added Italian translation (thx IK1VQY)
  • Fixed QSO Table Callsign filter is not filled properly (issue #401)
  • Fixed DXC zero frequency for last QSO in case of FT8 QSOs (issue #404)
  • Fixed Callsign Context Menu does not work (issue #409)
  • Fixed QSO Detail Save and Edit buttons are not translated (issue #410)
  • [NEW] - Added Shortcuts Editor (issue #293)
  • [NEW] - Added QO100 Bandplan to correctly categorize the DX Spots
  • [NEW] - Improveded detection of SH/DX SHF Spots
  • [NEW] - Online Map - Added WSJTX CQ Spots
  • [NEW] - WSJTX - Sortable View
  • [NEW] - Alerts - Sortable View
  • [NEW] - Added Spanish translation (thx LU1IDC)
  • [NEW[ - Added Search Callsign Clear Button (issue #396)
  • [CHANGED] - QRZ auth should be over POST with form data (issue #389)
  • [CHANGED] - Big CTY file is used
  • [CHANGED] - Callbook Country DXCC ID is used in case of difference from Big CTY
  • [CHANGED] - Removed ALT+W and CTRL+DEL shortcuts
  • [CHANGED] - Removed New Contact and Save Contact from Logbook Main Menu
  • Fixed Guantanamo KG4 Issue (issue #372)
  • Fixed QRZ Lookup Not Including Full Name - History (issue #388)
  • Fixed Spot Last QSO contains TX freq, should contain RX freq (issue #390)
  • Fixed Spot Last QSO must contain Freq in kHz (issue #391)
  • Fixed Bandmap select previous selected callsign issue (issue #394)
  • Fixed Malfunctioning tuning of WSJTX Alert spot
  • Fixed DXCC Status for FT4 Spots incorrectly identified in WSJTX
================================================ FILE: res/map/onlinemap.html ================================================ Online Map
================================================ FILE: res/qlog.1 ================================================ .TH QLOG 1 "2026-03-13" "QLog" "User Commands" .SH NAME qlog \- Amateur Radio logging application .SH SYNOPSIS .B qlog .RI [ OPTIONS ] .SH DESCRIPTION QLog is a multiplatform Amateur Radio logging application. It is based on the Qt framework and uses SQLite as database backend. .PP QLog aims to be as simple as possible, but to provide everything the operator expects from the log. .SH OPTIONS .TP .BR \-d ", " \-\-debug Write debug messages to .I ~/.local/share/hamradio/QLog/qlog_debug_YYYYMMDDhhmmss.log .TP .BR \-f ", " \-\-force\-update Force update of all value lists (DXCC, SATs, etc.). .TP .BR \-l ", " \-\-language " " \fIcode\fR Set language (e.g.\& \fBen\fR or \fBen_US\fR). Ignores environment setting. .TP .BR \-t ", " \-\-translation " " \fIpath\fR Load a translation file (absolute or relative path to a QM file). .TP .BR \-h ", " \-\-help Display help message and exit. .TP .BR \-v ", " \-\-version Display version information and exit. .SH FILES .TP .I ~/.config/hamradio/QLog.conf Machine-specific settings. .TP .I ~/.local/share/hamradio/QLog/ Data directory. .SH BUGS Report bugs at . .SH SEE ALSO .UR https://github.com/foldynl/QLog/wiki QLog Wiki .UE .SH AUTHOR Ladislav Foldyna (OK1MLG) ================================================ FILE: res/qlog.desktop ================================================ [Desktop Entry] Version=1.0 Type=Application Name=QLog Comment=Amateur Radio Logbook Exec=qlog Icon=qlog Categories=Utility;HamRadio; Keywords=QLog;Ham;Radio;HamRadio;AmateurRadio;Log; ================================================ FILE: res/res.qrc ================================================ map/nasabluemarble.jpg map/nasaearthlights.jpg stylesheet.css qlog.png data/dxcc.json data/legacy_modes.json data/contests.json data/propagation_modes.json sql/migration_001.sql sql/contests.sql sql/migration_002.sql data/sat_modes.json qlog_screen.png map/onlinemap.html sql/migration_003.sql sql/migration_004.sql sql/migration_005.sql sql/migration_006.sql sql/migration_007.sql data/timezone21.bin sql/migration_008.sql sql/migration_009.sql sql/migration_010.sql sql/migration_011.sql sql/migration_012.sql sql/migration_013.sql sql/migration_014.sql sql/migration_015.sql sql/migration_016.sql sql/migration_017.sql sql/migration_018.sql sql/migration_019.sql sql/migration_020.sql sql/migration_021.sql sql/migration_022.sql sql/migration_023.sql sql/migration_024.sql sql/migration_025.sql sql/migration_026.sql sql/migration_027.sql sql/migration_028.sql sql/migration_029.sql sql/migration_030.sql sql/migration_031.sql sql/migration_032.sql sql/migration_033.sql sql/migration_034.sql sql/migration_035.sql sql/migration_036.sql sql/migration_037.sql sql/migration_038.sql ================================================ FILE: res/sql/contests.sql ================================================ INSERT INTO contests (contest_id, descrption) VALUES ('070-160M-SPRINT', 'PODXS Great Pumpkin Sprint'), ('070-3-DAY', 'PODXS Three Day Weekend'), ('070-31-FLAVORS', 'PODXS 31 Flavors'), ('070-40M-SPRINT', 'PODXS 40m Firecracker Sprint'), ('070-80M-SPRINT', 'PODXS 80m Jay Hudak Memorial Sprint'), ('070-PSKFEST', 'PODXS PSKFest'), ('070-ST-PATS-DAY', 'PODXS St. Patricks Day'), ('070-VALENTINE-SPRINT', 'PODXS Valentine Sprint'), ('10-RTTY', 'Ten-Meter RTTY Contest (2011 onwards)'), ('1010-OPEN-SEASON', 'Open Season Ten Meter QSO Party'), ('7QP', '7th-Area QSO Party'), ('AL-QSO-PARTY', 'Alabama QSO Party'), ('ALL-ASIAN-DX-CW', 'JARL All Asian DX Contest (CW)'), ('ALL-ASIAN-DX-PHONE', 'JARL All Asian DX Contest (PHONE)'), ('ANARTS-RTTY', 'ANARTS WW RTTY'), ('ANATOLIAN-RTTY', 'Anatolian WW RTTY'), ('AP-SPRINT', 'Asia - Pacific Sprint'), ('AR-QSO-PARTY', 'Arkansas QSO Party'), ('ARI-DX', 'ARI DX Contest'), ('ARRL-10', 'ARRL 10 Meter Contest'), ('ARRL-160', 'ARRL 160 Meter Contest'), ('ARRL-222', 'ARRL 222 MHz and Up Distance Contest'), ('ARRL-DX-CW', 'ARRL International DX Contest (CW)'), ('ARRL-DX-SSB', 'ARRL International DX Contest (Phone)'), ('ARRL-EME', 'ARRL EME contest'), ('ARRL-FIELD-DAY', 'ARRL Field Day'), ('ARRL-RR-CW', 'ARRL Rookie Roundup (CW)'), ('ARRL-RR-RTTY', 'ARRL Rookie Roundup (RTTY)'), ('ARRL-RR-SSB', 'ARRL Rookie Roundup (Phone)'), ('ARRL-RTTY', 'ARRL RTTY Round-Up'), ('ARRL-SCR', 'ARRL School Club Roundup'), ('ARRL-SS-CW', 'ARRL November Sweepstakes (CW)'), ('ARRL-SS-SSB', 'ARRL November Sweepstakes (Phone)'), ('ARRL-UHF-AUG', 'ARRL August UHF Contest'), ('ARRL-VHF-JAN', 'ARRL January VHF Sweepstakes'), ('ARRL-VHF-JUN', 'ARRL June VHF QSO Party'), ('ARRL-VHF-SEP', 'ARRL September VHF QSO Party'), ('AZ-QSO-PARTY', 'Arizona QSO Party'), ('BARTG-RTTY', 'BARTG Spring RTTY Contest'), ('BARTG-SPRINT', 'BARTG Sprint Contest'), ('BC-QSO-PARTY', 'British Columbia QSO Party'), ('CA-QSO-PARTY', 'California QSO Party'), ('CIS-DX', 'CIS DX Contest'), ('CO-QSO-PARTY', 'Colorado QSO Party'), ('CQ-160-CW', 'CQ WW 160 Meter DX Contest (CW)'), ('CQ-160-SSB', 'CQ WW 160 Meter DX Contest (SSB)'), ('CQ-M', 'CQ-M International DX Contest'), ('CQ-VHF', 'CQ World-Wide VHF Contest'), ('CQ-WPX-CW', 'CQ WW WPX Contest (CW)'), ('CQ-WPX-RTTY', 'CQ/RJ WW RTTY WPX Contest'), ('CQ-WPX-SSB', 'CQ WW WPX Contest (SSB)'), ('CQ-WW-CW', 'CQ WW DX Contest (CW)'), ('CQ-WW-RTTY', 'CQ/RJ WW RTTY DX Contest'), ('CQ-WW-SSB', 'CQ WW DX Contest (SSB)'), ('CT-QSO-PARTY', 'Connecticut QSO Party'), ('CVA-DX-CW', 'Concurso Verde e Amarelo DX CW Contest'), ('CVA-DX-SSB', 'Concurso Verde e Amarelo DX CW Contest'), ('CWOPS-CW-OPEN', 'CWops CW Open Competition'), ('CWOPS-CWT', 'CWops Mini-CWT Test'), ('DARC-WAEDC-CW', 'WAE DX Contest (CW)'), ('DARC-WAEDC-RTTY', 'WAE DX Contest (RTTY)'), ('DARC-WAEDC-SSB', 'WAE DX Contest (SSB)'), ('DARC-WAG', 'DARC Worked All Germany'), ('DE-QSO-PARTY', 'Delaware QSO Party'), ('DL-DX-RTTY', 'DL-DX RTTY Contest'), ('DMC-RTTY', 'DMC RTTY Contest'), ('EA-CNCW', 'Concurso Nacional de Telegrafía'), ('EA-DME', 'Municipios Españoles'), ('EA-PSK63', 'EA PSK63'), ('EA-SMRE-CW', 'Su Majestad El Rey de España - CW'), ('EA-SMRE-SSB', 'Su Majestad El Rey de España - SSB'), ('EA-VHF-ATLANTIC', 'Atlántico V-UHF'), ('EA-VHF-COM', 'Combinado de V-UHF'), ('EA-VHF-COSTA-SOL', 'Costa del Sol V-UHF'), ('EA-VHF-EA', 'Nacional VHF'), ('EA-VHF-EA1RCS', 'Segovia EA1RCS V-UHF'), ('EA-VHF-QSL', 'QSL V-UHF & 50MHz'), ('EA-VHF-SADURNI', 'Sant Sadurni V-UHF'), ('EA-WW-RTTY', 'Unión de Radioaficionados Españoles RTTY Contest'), ('EPC-PSK63', 'PSK63 QSO Party'), ('EU Sprint', 'EU Sprint'), ('EU-HF', 'EU HF Championship'), ('EU-PSK-DX', 'EU PSK DX Contest'), ('EUCW160M', 'European CW Association 160m CW Party'), ('FALL SPRINT', 'FISTS Fall Sprint'), ('FL-QSO-PARTY', 'Florida QSO Party'), ('GA-QSO-PARTY', 'Georgia QSO Party'), ('HA-DX', 'Hungarian DX Contest'), ('HELVETIA', 'Helvetia Contest'), ('HI-QSO-PARTY', 'Hawaiian QSO Party'), ('HOLYLAND', 'IARC Holyland Contest'), ('IA-QSO-PARTY', 'Iowa QSO Party'), ('IARU-FIELD-DAY', 'DARC IARU Region 1 Field Day'), ('IARU-HF', 'IARU HF World Championship'), ('ID-QSO-PARTY', 'Idaho QSO Party'), ('IL QSO Party', 'Illinois QSO Party'), ('IN-QSO-PARTY', 'Indiana QSO Party'), ('JARTS-WW-RTTY', 'JARTS WW RTTY'), ('JIDX-CW', 'Japan International DX Contest (CW)'), ('JIDX-SSB', 'Japan International DX Contest (SSB)'), ('JT-DX-RTTY', 'Mongolian RTTY DX Contest'), ('KS-QSO-PARTY', 'Kansas QSO Party'), ('KY-QSO-PARTY', 'Kentucky QSO Party'), ('LA-QSO-PARTY', 'Louisiana QSO Party'), ('LDC-RTTY', 'DRCG Long Distance Contest (RTTY)'), ('LZ DX', 'LZ DX Contest'), ('MAR-QSO-PARTY', 'Maritimes QSO Party'), ('MD-QSO-PARTY', 'Maryland QSO Party'), ('ME-QSO-PARTY', 'Maine QSO Party'), ('MI-QSO-PARTY', 'Michigan QSO Party'), ('MIDATLANTIC-QSO-PARTY', 'Mid-Atlantic QSO Party'), ('MN-QSO-PARTY', 'Minnesota QSO Party'), ('MO-QSO-PARTY', 'Missouri QSO Party'), ('MS-QSO-PARTY', 'Mississippi QSO Party'), ('MT-QSO-PARTY', 'Montana QSO Party'), ('NA-SPRINT-CW', 'North America Sprint (CW)'), ('NA-SPRINT-RTTY', 'North America Sprint (RTTY)'), ('NA-SPRINT-SSB', 'North America Sprint (Phone)'), ('NAQP-CW', 'North America QSO Party (CW)'), ('NAQP-RTTY', 'North America QSO Party (RTTY)'), ('NAQP-SSB', 'North America QSO Party (Phone)'), ('NC-QSO-PARTY', 'North Carolina QSO Party'), ('ND-QSO-PARTY', 'North Dakota QSO Party'), ('NE-QSO-PARTY', 'Nebraska QSO Party'), ('NEQP', 'New England QSO Party'), ('NH-QSO-PARTY', 'New Hampshire QSO Party'), ('NJ-QSO-PARTY', 'New Jersey QSO Party'), ('NM-QSO-PARTY', 'New Mexico QSO Party'), ('NRAU-BALTIC-CW', 'NRAU-Baltic Contest (CW)'), ('NRAU-BALTIC-SSB', 'NRAU-Baltic Contest (SSB)'), ('NV-QSO-PARTY', 'Nevada QSO Party'), ('NY-QSO-PARTY', 'New York QSO Party'), ('OCEANIA-DX-CW', 'Oceania DX Contest (CW)'), ('OCEANIA-DX-SSB', 'Oceania DX Contest (SSB)'), ('OH-QSO-PARTY', 'Ohio QSO Party'), ('OK-DX-RTTY', 'Czech Radio Club OK DX Contest'), ('OK-OM-DX', 'Czech Radio Club OK-OM DX Contest'), ('OK-QSO-PARTY', 'Oklahoma QSO Party'), ('OMISS-QSO-PARTY', 'Old Man International Sideband Society QSO Party'), ('ON-QSO-PARTY', 'Ontario QSO Party'), ('OR-QSO-PARTY', 'Oregon QSO Party'), ('PA-QSO-PARTY', 'Pennsylvania QSO Party'), ('PACC', 'Dutch PACC Contest'), ('QC-QSO-PARTY', 'Quebec QSO Party'), ('RAC-CANADA-DAY', 'RAC Canada Day Contest'), ('RAC-CANADA-WINTER', 'RAC Canada Winter Contest'), ('RDAC', 'Russian District Award Contest'), ('RDXC', 'Russian DX Contest'), ('REF-160M', 'Reseau des Emetteurs Francais 160m Contest'), ('REF-CW', 'Reseau des Emetteurs Francais Contest (CW)'), ('REF-SSB', 'Reseau des Emetteurs Francais Contest (SSB)'), ('REP-PORTUGAL-DAY-HF', 'Rede dos Emissores Portugueses Portugal Day HF Contest'), ('RI-QSO-PARTY', 'Rhode Island QSO Party'), ('RSGB-160', '1.8MHz Contest'), ('RSGB-21/28-CW', '21/28 MHz Contest (CW)'), ('RSGB-21/28-SSB', '21/28 MHz Contest (SSB)'), ('RSGB-80M-CC', '80m Club Championships'), ('RSGB-AFS-CW', 'Affiliated Societies Team Contest (CW)'), ('RSGB-AFS-SSB', 'Affiliated Societies Team Contest (SSB)'), ('RSGB-CLUB-CALLS', 'Club Calls'), ('RSGB-COMMONWEALTH', 'Commonwealth Contest'), ('RSGB-IOTA', 'IOTA Contest'), ('RSGB-LOW-POWER', 'Low Power Field Day'), ('RSGB-NFD', 'National Field Day'), ('RSGB-ROPOCO', 'RoPoCo'), ('RSGB-SSB-FD', 'SSB Field Day'), ('RUSSIAN-RTTY', 'Russian Radio RTTY Worldwide Contest'), ('SAC-CW', 'Scandinavian Activity Contest (CW)'), ('SAC-SSB', 'Scandinavian Activity Contest (SSB)'), ('SARTG-RTTY', 'SARTG WW RTTY'), ('SC-QSO-PARTY', 'South Carolina QSO Party'), ('SCC-RTTY', 'SCC RTTY Championship'), ('SD-QSO-PARTY', 'South Dakota QSO Party'), ('SMP-AUG', 'SSA Portabeltest'), ('SMP-MAY', 'SSA Portabeltest'), ('SP-DX-RTTY', 'PRC SPDX Contest (RTTY)'), ('SPAR-WINTER-FD', 'SPAR Winter Field Day'), ('SPDXContest', 'SP DX Contest'), ('SPRING SPRINT', 'FISTS Spring Sprint'), ('SR-MARATHON', 'Scottish-Russian Marathon'), ('STEW-PERRY', 'Stew Perry Topband Distance Challenge'), ('SUMMER SPRINT', 'FISTS Summer Sprint'), ('TARA-GRID-DIP', 'TARA Grid Dip PSK-RTTY Shindig'), ('TARA-RTTY', 'TARA RTTY Mêlée'), ('TARA-RUMBLE', 'TARA Rumble PSK Contest'), ('TARA-SKIRMISH', 'TARA Skirmish Digital Prefix Contest'), ('TMC-RTTY', 'The Makrothen Contest'), ('TN-QSO-PARTY', 'Tennessee QSO Party'), ('TX-QSO-PARTY', 'Texas QSO Party'), ('UBA-DX-CW', 'UBA Contest (CW)'), ('UBA-DX-SSB', 'UBA Contest (SSB)'), ('UK-DX-BPSK63', 'European PSK Club BPSK63 Contest'), ('UK-DX-RTTY', 'UK DX RTTY Contest'), ('UKR-CHAMP-RTTY', 'Open Ukraine RTTY Championship'), ('UKRAINIAN DX', 'Ukrainian DX'), ('UKSMG-6M-MARATHON', 'UKSMG 6m Marathon'), ('UKSMG-SUMMER-ES', 'UKSMG Summer Es Contest'), ('US-COUNTIES-QSO', 'Mobile Amateur Awards Club'), ('UT-QSO-PARTY', 'Utah QSO Party'), ('VA-QSO-PARTY', 'Virginia QSO Party'), ('VENEZ-IND-DAY', 'RCV Venezuelan Independence Day Contest'), ('VIRGINIA QSO PARTY (import-only)', 'Virginia QSO Party'), ('VOLTA-RTTY', 'Alessandro Volta RTTY DX Contest'), ('WA-QSO-PARTY', 'Washington QSO Party'), ('WI-QSO-PARTY', 'Wisconsin QSO Party'), ('WIA-HARRY ANGEL', 'WIA Harry Angel Memorial 80m Sprint'), ('WIA-JMMFD', 'WIA John Moyle Memorial Field Day'), ('WIA-OCDX', 'WIA Oceania DX (OCDX) Contest'), ('WIA-REMEMBRANCE', 'WIA Remembrance Day'), ('WIA-ROSS HULL', 'WIA Ross Hull Memorial VHF/UHF Contest'), ('WIA-TRANS TASMAN', 'WIA Trans Tasman Low Bands Challenge'), ('WIA-VHF/UHF FD', 'WIA VHF UHF Field Days'), ('WIA-VK SHIRES', 'WIA VK Shires'), ('WINTER SPRINT', 'FISTS Winter Sprint'), ('WV-QSO-PARTY', 'West Virginia QSO Party'), ('WY-QSO-PARTY', 'Wyoming QSO Party'), ('XE-INTL-RTTY', 'Mexico International Contest (RTTY)'), ('YOHFDX', 'YODX HF contest'), ('YUDXC', 'YU DX Contest'); ================================================ FILE: res/sql/migration_001.sql ================================================ CREATE TABLE IF NOT EXISTS schema_versions ( version INTEGER PRIMARY KEY, updated TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS contacts ( id INTEGER PRIMARY KEY, start_time TEXT, end_time TEXT, callsign TEXT NOT NULL, rst_sent TEXT, rst_rcvd TEXT, freq REAL, band TEXT, mode TEXT, submode TEXT, name TEXT, qth TEXT, gridsquare TEXY, dxcc INTEGER, country TEXT, cont TEXT, cqz INTEGER, ituz INTEGER, pfx TEXT, state TEXT, cnty TEXT, iota TEXT, qsl_rcvd CHECK(qsl_rcvd IN ('N', 'Y', 'R', 'I')) NOT NULL DEFAULT 'N', qsl_rdate TEXT, qsl_sent CHECK(qsl_sent IN ('N', 'Y', 'R', 'Q', 'I')) NOT NULL DEFAULT 'N', qsl_sdate TEXT, lotw_qsl_rcvd CHECK(qsl_rcvd IN ('N', 'Y', 'R', 'I')) NOT NULL DEFAULT 'N', lotw_qslrdate TEXT, lotw_qsl_sent CHECK(qsl_sent IN ('N', 'Y', 'R', 'Q', 'I')) NOT NULL DEFAULT 'N', lotw_qslsdate TEXT, tx_pwr REAL, fields JSON ); CREATE TABLE IF NOT EXISTS bands ( id INTEGER PRIMARY KEY, name TEXT UNIQUE NOT NULL, start_freq FLOAT, end_freq FLOAT, enabled BOOLEAN ); CREATE TABLE IF NOT EXISTS modes ( id INTEGER PRIMARY KEY, name TEXT UNIQUE NOT NULL, submodes JSON, rprt TEXT, dxcc TEXT CHECK(dxcc IN ('CW', 'PHONE', 'DIGITAL')) NOT NULL, enabled INTEGER ); CREATE TABLE IF NOT EXISTS dxcc_entities ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, prefix TEXT, cont TEXT, cqz INTEGER, ituz INTEGER, lat REAL, lon REAL, tz REAL ); CREATE TABLE IF NOT EXISTS dxcc_prefixes ( id INTEGER PRIMARY KEY, prefix TEXT UNIQUE NOT NULL, exact INTEGER, dxcc INTEGER REFERENCES dxcc_entities(id), cqz INTEGER, ituz INTEGER, cont TEXT, lat REAL, lon REAL ); CREATE INDEX prefix_idx ON dxcc_prefixes(prefix); CREATE INDEX callsign_idx ON contacts(callsign); INSERT INTO modes (name, rprt, dxcc, enabled, submodes) VALUES ('CW', '599', 'CW', true, NULL), ('SSB', '59', 'PHONE', true, '["LSB", "USB"]'), ('AM', '59', 'PHONE', true, NULL), ('FM', '59', 'PHONE', true, NULL), ('PSK', '599', 'DIGITAL', true, '[ "PSK31", "PSK63", "PSK63F", "PSK125", "PSK250", "PSK500", "PSK1000", "QPSK31", "QPSK63", "QPSK125", "QPSK250", "QPSK500" ]'), ('RTTY', '599', 'DIGITAL', true, NULL), ('MFSK', '599', 'DIGITAL', true, '[ "FT4", "MFSK4", "MFSK8", "MFSK11", "MFSK16", "MFSK22", "MFSK31", "MFSK32" ]'), ('OLIVIA', NULL, 'DIGITAL', true, '[ "OLIVIA 4/125", "OLIVIA 4/250", "OLIVIA 8/250", "OLIVIA 8/500", "OLIVIA 16/500", "OLIVIA 16/1000", "OLIVIA 32/1000" ]'), ('JT65', '-1', 'DIGITAL', true, '[ "JT65A", "JT65B", "JT65B2", "JT65C", "JT65C2" ]'), ('JT9', '-1', 'DIGITAL', true, '[ "JT9-1", "JT9-2", "JT9-5", "JT9-10", "JT9-30" ]'), ('FT8', '-1', 'DIGITAL', true, NULL), ('HELL', NULL, 'DIGITAL', true, '[ "FMHELL", "FSKHELL", "HELL80", "HFSK", "PSKHELL" ]'), ('CONTESTIA', NULL, 'DIGITAL', true, NULL), ('DOMINO', NULL, 'DIGITAL', true, '[ "DOMINOEX", "DOMINOF" ]'), ('MT63', NULL, 'DIGITAL', true, NULL), ('JT6M', '26', 'DIGITAL', true, NULL), ('JTMSK', '-1', 'DIGITAL', true, NULL), ('MSK144', '0', 'DIGITAL', true, NULL), ('FSK441', '26', 'DIGITAL', true, NULL), ('DIGITALVOICE', '59', 'PHONE', true, NULL), ('DSTAR', '59', 'PHONE', true, NULL), ('PKT', NULL, 'DIGITAL', true, NULL), ('ATV', NULL, 'DIGITAL', true, NULL), ('SSTV', NULL, 'DIGITAL', true, NULL); INSERT INTO bands (name, start_freq, end_freq, enabled) VALUES ('2190m', 0.136, 0.137, false), ('630m', 0.472, 0.479, false), ('560m', 0.501, 0.504, false), ('160m', 1.8, 2.0, true), ('80m', 3.5, 4.0, true), ('60m', 5.102, 5.4065, true), ('40m', 7.0, 7.3, true), ('30m', 10.1, 10.15, true), ('20m', 14.0, 14.35, true), ('17m', 18.068, 18.168, true), ('15m', 21.0, 21.45, true), ('12m', 24.89, 24.99, true), ('10m', 28.0, 29.7, true), ('6m', 50.0, 54.0, true), ('4m', 70.0, 71.0, true), ('2m', 144.0, 148.0, true), ('1.25m', 222.0, 225.0, false), ('70cm', 420.0, 450.0, true), ('33cm', 902.0, 928.0, false), ('23cm', 1240.0, 1300.0, false), ('13cm', 2300.0, 2450.0, false), ('9cm', 3300.0, 3500.0, false), ('6cm', 5650.0, 5925.0, false), ('3cm', 10000.0, 10500.0, false), ('1.25cm', 24000.0, 24250.0, false), ('6mm', 47000.0, 47200.0, false), ('4mm', 75500.0, 81000.0, false), ('2.5mm', 119980.0, 120020.0, false), ('2mm', 142000.0, 149000.0, false), ('1mm', 241000.0, 250000.0, false); ================================================ FILE: res/sql/migration_002.sql ================================================ ALTER TABLE contacts ADD address TEXT; ALTER TABLE contacts ADD address_intl TEXT; ALTER TABLE contacts ADD age INTEGER; ALTER TABLE contacts ADD a_index INTEGER; ALTER TABLE contacts ADD ant_az INTEGER; ALTER TABLE contacts ADD ant_el INTEGER; ALTER TABLE contacts ADD ant_path CHECK(ant_path IN ('G', 'O', 'S', 'L')); ALTER TABLE contacts ADD arrl_sect TEXT; ALTER TABLE contacts ADD award_submitted TEXT; ALTER TABLE contacts ADD award_granted TEXT; ALTER TABLE contacts ADD band_rx TEXT; ALTER TABLE contacts ADD "check" TEXT; ALTER TABLE contacts ADD "class" TEXT; ALTER TABLE contacts ADD clublog_qso_upload_date TEXT; ALTER TABLE contacts ADD clublog_qso_upload_status CHECK(clublog_qso_upload_status IN ('N', 'Y', 'M')) DEFAULT 'N'; ALTER TABLE contacts ADD "comment" TEXT; ALTER TABLE contacts ADD comment_intl TEXT; ALTER TABLE contacts ADD contacted_op TEXT; ALTER TABLE contacts ADD contest_id TEXT; ALTER TABLE contacts ADD country_intl TEXT; ALTER TABLE contacts ADD credit_submitted TEXT; ALTER TABLE contacts ADD credit_granted TEXT; ALTER TABLE contacts ADD darc_dok TEXT; ALTER TABLE contacts ADD distance REAL; ALTER TABLE contacts ADD email TEXT; ALTER TABLE contacts ADD eq_call TEXT; ALTER TABLE contacts ADD eqsl_qslrdate TEXT; ALTER TABLE contacts ADD eqsl_qslsdate TEXT; ALTER TABLE contacts ADD eqsl_qsl_rcvd CHECK(eqsl_qsl_rcvd IN ('N', 'Y', 'R', 'I')) DEFAULT 'N'; ALTER TABLE contacts ADD eqsl_qsl_sent CHECK(eqsl_qsl_sent IN ('N', 'Y', 'R', 'I')) DEFAULT 'N'; ALTER TABLE contacts ADD fists INTEGER; ALTER TABLE contacts ADD fists_cc INTEGER; ALTER TABLE contacts ADD force_init CHECK(force_init IN ('N', 'Y')); ALTER TABLE contacts ADD freq_rx REAL; ALTER TABLE contacts ADD guest_op TEXT; ALTER TABLE contacts ADD hrdlog_qso_upload_date TEXT; ALTER TABLE contacts ADD hrdlog_qso_upload_status CHECK(hrdlog_qso_upload_status IN ('N', 'Y', 'R', 'I')) DEFAULT 'N'; ALTER TABLE contacts ADD iota_island_id INTEGER; ALTER TABLE contacts ADD k_index INTEGER; ALTER TABLE contacts ADD lat TEXT; ALTER TABLE contacts ADD lon TEXT; ALTER TABLE contacts ADD max_bursts REAL; ALTER TABLE contacts ADD ms_shower TEXT; ALTER TABLE contacts ADD my_antenna TEXT; ALTER TABLE contacts ADD my_antenna_intl TEXT; ALTER TABLE contacts ADD my_city TEXT; ALTER TABLE contacts ADD my_city_intl TEXT; ALTER TABLE contacts ADD my_cnty TEXT; ALTER TABLE contacts ADD my_country TEXT; ALTER TABLE contacts ADD my_country_intl TEXT; ALTER TABLE contacts ADD my_cq_zone INTEGER; ALTER TABLE contacts ADD my_dxcc INTEGER; ALTER TABLE contacts ADD my_fists INTEGER; ALTER TABLE contacts ADD my_gridsquare TEXT; ALTER TABLE contacts ADD my_iota TEXT; ALTER TABLE contacts ADD my_iota_island_id INTEGER; ALTER TABLE contacts ADD my_itu_zone INTEGER; ALTER TABLE contacts ADD my_lat TEXT; ALTER TABLE contacts ADD my_lon TEXT; ALTER TABLE contacts ADD my_name TEXT; ALTER TABLE contacts ADD my_name_intl TEXT; ALTER TABLE contacts ADD my_postal_code TEXT; ALTER TABLE contacts ADD my_postal_code_intl TEXT; ALTER TABLE contacts ADD my_rig TEXT; ALTER TABLE contacts ADD my_rig_intl TEXT; ALTER TABLE contacts ADD my_sig TEXT; ALTER TABLE contacts ADD my_sig_intl TEXT; ALTER TABLE contacts ADD my_sig_info TEXT; ALTER TABLE contacts ADD my_sig_info_intl TEXT; ALTER TABLE contacts ADD my_sota_ref TEXT; ALTER TABLE contacts ADD my_state TEXT; ALTER TABLE contacts ADD my_street TEXT; ALTER TABLE contacts ADD my_street_intl TEXT; ALTER TABLE contacts ADD my_usaca_counties TEXT; ALTER TABLE contacts ADD my_vucc_grids TEXT; ALTER TABLE contacts ADD name_intl TEXT; ALTER TABLE contacts ADD notes TEXT; ALTER TABLE contacts ADD notes_intl TEXT; ALTER TABLE contacts ADD nr_bursts INTEGER; ALTER TABLE contacts ADD nr_pings INTEGER; ALTER TABLE contacts ADD "operator" TEXT; ALTER TABLE contacts ADD owner_callsign TEXT; ALTER TABLE contacts ADD precedence TEXT; ALTER TABLE contacts ADD prop_mode TEXT; ALTER TABLE contacts ADD public_key TEXT; ALTER TABLE contacts ADD qrzcom_qso_upload_date TEXT; ALTER TABLE contacts ADD qrzcom_qso_upload_status CHECK(qrzcom_qso_upload_status IN ('N', 'Y', 'M')) DEFAULT 'N'; ALTER TABLE contacts ADD qslmsg TEXT; ALTER TABLE contacts ADD qslmsg_intl TEXT; ALTER TABLE contacts ADD qsl_rcvd_via CHECK(qsl_rcvd_via IN ('B', 'D', 'E', 'M')); ALTER TABLE contacts ADD qsl_sent_via CHECK(qsl_sent_via IN ('B', 'D', 'E', 'M')); ALTER TABLE contacts ADD qsl_via TEXT; ALTER TABLE contacts ADD qso_complete CHECK(qso_complete IN ('Y', 'N', 'NIL', '?' )); ALTER TABLE contacts ADD qso_random CHECK(qso_random IN ('N', 'Y')); ALTER TABLE contacts ADD qth_intl TEXT; ALTER TABLE contacts ADD region TEXT; ALTER TABLE contacts ADD rig TEXT; ALTER TABLE contacts ADD rig_intl TEXT; ALTER TABLE contacts ADD rx_pwr REAL; ALTER TABLE contacts ADD sat_mode CHECK(sat_mode IN ('K', 'T', 'A', 'A', 'J', 'B', 'S', 'L', 'VU', 'UV', 'US', 'LU', 'LS', 'LX', 'VS')); ALTER TABLE contacts ADD sat_name TEXT; ALTER TABLE contacts ADD sfi INTEGER; ALTER TABLE contacts ADD sig TEXT; ALTER TABLE contacts ADD sig_intl TEXT; ALTER TABLE contacts ADD sig_info TEXT; ALTER TABLE contacts ADD sig_info_intl TEXT; ALTER TABLE contacts ADD silent_key CHECK(silent_key IN ('N', 'Y')); ALTER TABLE contacts ADD skcc TEXT; ALTER TABLE contacts ADD sota_ref TEXT; ALTER TABLE contacts ADD srx INTEGER; ALTER TABLE contacts ADD srx_string TEXT; ALTER TABLE contacts ADD station_callsign TEXT; ALTER TABLE contacts ADD stx INTEGER; ALTER TABLE contacts ADD stx_string TEXT; ALTER TABLE contacts ADD swl CHECK(swl IN ('N', 'Y')); ALTER TABLE contacts ADD ten_ten INTEGER; ALTER TABLE contacts ADD uksmg INTEGER; ALTER TABLE contacts ADD usaca_counties TEXT; ALTER TABLE contacts ADD ve_prov TEXT; ALTER TABLE contacts ADD vucc_grids TEXT; ALTER TABLE contacts ADD web TEXT; CREATE TABLE IF NOT EXISTS sat_info ( name TEXT NOT NULL, number INTEGER, uplink TEXT, downlink TEXT, beacon TEXT, "mode" TEXT, callsign TEXT, status TEXT ); CREATE TABLE IF NOT EXISTS qso_filter_matching_types ( matching_id INTEGER PRIMARY KEY, sql_operator TEXT NOT NULL ); INSERT INTO qso_filter_matching_types values (0,'AND'); INSERT INTO qso_filter_matching_types values (1,'OR'); CREATE TABLE IF NOT EXISTS qso_filter_operators ( operator_id INTEGER PRIMARY KEY, sql_operator TEXT NOT NULL ); INSERT INTO qso_filter_operators values (0,'='); INSERT INTO qso_filter_operators values (1,'<>'); INSERT INTO qso_filter_operators values (2,'like'); INSERT INTO qso_filter_operators values (3,'not like'); INSERT INTO qso_filter_operators values (4,'>'); INSERT INTO qso_filter_operators values (5,'<'); CREATE TABLE IF NOT EXISTS qso_filters ( filter_name text PRIMARY KEY, matching_type INTEGER REFERENCES qso_filter_matching_types(matching_id) ); CREATE TABLE IF NOT EXISTS qso_filter_rules ( filter_name TEXT REFERENCES qso_filters(filter_name) ON DELETE CASCADE, table_field_index INTEGER NOT NULL, operator_id INTEGER REFERENCES qso_filter_operators(operator_id) , "value" TEXT ); ================================================ FILE: res/sql/migration_003.sql ================================================ CREATE TABLE IF NOT EXISTS station_profiles ( profile_name TEXT PRIMARY KEY, callsign TEXT NOT NULL, locator TEXT NOT NULL, operator_name TEXT, qth_name TEXT, iota TEXT, sota TEXT, sig TEXT, sig_info TEXT, vucc TEXT ); ================================================ FILE: res/sql/migration_004.sql ================================================ INSERT INTO modes (name, rprt, dxcc, enabled, submodes) VALUES ('C4FM', '59', 'PHONE', true, NULL), ('CHIP', NULL, 'DIGITAL', true, '["CHIP64", "CHIP128"]'), ('CLO', NULL, 'DIGITAL', true, NULL), ('FAX', NULL, 'DIGITAL', true, NULL), ('ISCAT', NULL, 'DIGITAL', true, NULL), ('JT4', NULL, 'DIGITAL', true, '["JT4A", "JT4B", "JT4C", "JT4D", "JT4E", "JT4F", "JT4G"]'), ('PAX', NULL, 'DIGITAL', true, '["PAX2"]'), ('PSK2', NULL, 'DIGITAL', true, NULL), ('Q15', NULL, 'DIGITAL', true, NULL), ('QRA64', NULL, 'DIGITAL', true, '["QRA64A", "QRA64B", "QRA64C", "QRA64D", "QRA64E"]'), ('ROS', NULL, 'DIGITAL', true, '["ROS-EME", "ROS-HF", "ROS-MF"]'), ('RTTYM', NULL, 'DIGITAL', true, NULL), ('T10', NULL, 'DIGITAL', true, NULL), ('THOR', NULL, 'DIGITAL', true, '["THOR-M", "THOR4", "THOR5", "THOR8", "THOR11", "THOR16", "THOR22", "THOR25X4", "THOR50X1", "THOR50X2", "THOR100"]'), ('THRB', NULL, 'DIGITAL', true, '["THRBX", "THRBX1", "THRBX2", "THRBX4", "THROB1", "THROB2", "THROB4"]'), ('TOR', NULL, 'DIGITAL', true, '["TOR", "AMTORFEC", "GTOR", "NAVTEX", "SITORB"]'), ('V4', NULL, 'DIGITAL', true, NULL), ('VOI', NULL, 'DIGITAL', true, NULL), ('WINMOR', NULL, 'DIGITAL', true, NULL), ('WSPR', NULL, 'DIGITAL', true, NULL); UPDATE modes SET submodes='["DOM-M", "DOM4", "DOM5", "DOM8", "DOM11", "DOM16", "DOM22", "DOM44", "DOM88", "DOMINOEX", "DOMINOF"]' WHERE name = 'DOMINO'; UPDATE modes SET submodes='["FMHELL", "FSKHELL", "HELL80", "HELLX5", "HELLX9", "HFSK", "PSKHELL", "SLOWHELL"]' WHERE name = 'HELL'; UPDATE modes SET submodes='["FSQCALL", "FST4", "FST4W", "FT4", "JS8", "JTMS", "MFSK4", "MFSK8", "MFSK11", "MFSK16", "MFSK22", "MFSK31", "MFSK32", "MFSK64", "MFSK64L", "MFSK128", "MFSK128L", "Q65"]' WHERE name = 'MFSK'; UPDATE modes SET submodes='["8PSK125", "8PSK125F", "8PSK125FL", "8PSK250", "8PSK250F", "8PSK250FL", "8PSK500", "8PSK500F", "8PSK1000", "8PSK1000F", "8PSK1200F", "FSK31", "PSK10", "PSK31", "PSK63", "PSK63F", "PSK63RC4", "PSK63RC5", "PSK63RC10", "PSK63RC20", "PSK63RC32", "PSK125", "PSK125C12", "PSK125R", "PSK125RC10", "PSK125RC12", "PSK125RC16", "PSK125RC4", "PSK125RC5", "PSK250", "PSK250C6", "PSK250R", "PSK250RC2", "PSK250RC3", "PSK250RC5", "PSK250RC6", "PSK250RC7", "PSK500", "PSK500C2", "PSK500C4", "PSK500R", "PSK500RC2", "PSK500RC3", "PSK500RC4", "PSK800C2", "PSK800RC2", "PSK1000", "PSK1000C2", "PSK1000R", "PSK1000RC2", "PSKAM10", "PSKAM31", "PSKAM50", "PSKFEC31", "QPSK31", "QPSK63", "QPSK125", "QPSK250", "QPSK500", "SIM31"]' WHERE name = 'PSK'; UPDATE contacts SET name_intl = name WHERE name_intl IS NULL; UPDATE contacts SET qth_intl = qth WHERE qth_intl IS NULL; UPDATE contacts SET comment_intl = comment WHERE comment_intl IS NULL; UPDATE contacts SET my_antenna_intl = my_antenna WHERE my_antenna_intl IS NULL; UPDATE contacts SET my_city_intl = my_city WHERE my_city_intl IS NULL; UPDATE contacts SET my_rig_intl = my_rig WHERE my_rig_intl IS NULL; UPDATE contacts SET my_sig_intl = my_sig WHERE my_sig_intl IS NULL; UPDATE contacts SET my_sig_info_intl = my_sig_info WHERE my_sig_info_intl IS NULL; UPDATE contacts SET sig_intl = sig WHERE sig_intl IS NULL; UPDATE contacts SET sig_info_intl = sig_info WHERE sig_info_intl IS NULL; ================================================ FILE: res/sql/migration_005.sql ================================================ INSERT INTO modes (name, rprt, dxcc, enabled, submodes) VALUES ('CW', '599', 'CW', true, NULL), ('SSB', '59', 'PHONE', true, '["LSB", "USB"]'), ('AM', '59', 'PHONE', true, NULL), ('FM', '59', 'PHONE', true, NULL), ('PSK', '599', 'DIGITAL', true, '[ "PSK31", "PSK63", "PSK63F", "PSK125", "PSK250", "PSK500", "PSK1000", "QPSK31", "QPSK63", "QPSK125", "QPSK250", "QPSK500" ]'), ('RTTY', '599', 'DIGITAL', true, NULL), ('MFSK', '599', 'DIGITAL', true, '[ "FT4", "MFSK4", "MFSK8", "MFSK11", "MFSK16", "MFSK22", "MFSK31", "MFSK32" ]'), ('OLIVIA', NULL, 'DIGITAL', true, '[ "OLIVIA 4/125", "OLIVIA 4/250", "OLIVIA 8/250", "OLIVIA 8/500", "OLIVIA 16/500", "OLIVIA 16/1000", "OLIVIA 32/1000" ]'), ('JT65', '-1', 'DIGITAL', true, '[ "JT65A", "JT65B", "JT65B2", "JT65C", "JT65C2" ]'), ('JT9', '-1', 'DIGITAL', true, '[ "JT9-1", "JT9-2", "JT9-5", "JT9-10", "JT9-30" ]'), ('FT8', '-1', 'DIGITAL', true, NULL), ('HELL', NULL, 'DIGITAL', true, '[ "FMHELL", "FSKHELL", "HELL80", "HFSK", "PSKHELL" ]'), ('CONTESTIA', NULL, 'DIGITAL', true, NULL), ('DOMINO', NULL, 'DIGITAL', true, '[ "DOMINOEX", "DOMINOF" ]'), ('MT63', NULL, 'DIGITAL', true, NULL), ('JT6M', '26', 'DIGITAL', true, NULL), ('JTMSK', '-1', 'DIGITAL', true, NULL), ('MSK144', '0', 'DIGITAL', true, NULL), ('FSK441', '26', 'DIGITAL', true, NULL), ('DIGITALVOICE', '59', 'PHONE', true, NULL), ('DSTAR', '59', 'PHONE', true, NULL), ('PKT', NULL, 'DIGITAL', true, NULL), ('ATV', NULL, 'DIGITAL', true, NULL), ('SSTV', NULL, 'DIGITAL', true, NULL) ON CONFLICT DO NOTHING; ================================================ FILE: res/sql/migration_006.sql ================================================ CREATE TABLE IF NOT EXISTS log_param ( name TEXT PRIMARY KEY, value TEXT ); CREATE TABLE IF NOT EXISTS rig_profiles ( profile_name TEXT PRIMARY KEY, model NUMBER NOT NULL, port_pathname TEXT, hostname TEXT, netport NUMBER, baudrate NUMBER, databits NUMBER, stopbits REAL, flowcontrol TEXT, parity TEXT, pollinterval INTEGER, txfreq_start REAL, txfreq_end REAL, get_freq INTEGER, get_mode INTEGER, get_vfo INTEGER, get_pwr INTEGER, rit_offset REAL, xit_offset REAL, get_rit INTEGER, get_xit INTEGER ); CREATE TABLE IF NOT EXISTS rot_profiles ( profile_name TEXT PRIMARY KEY, model NUMBER NOT NULL, port_pathname TEXT, hostname TEXT, netport NUMBER, baudrate NUMBER, databits NUMBER, stopbits REAL, flowcontrol TEXT, parity TEXT ); CREATE TABLE IF NOT EXISTS ant_profiles ( profile_name TEXT PRIMARY KEY, desc TEXT ); ================================================ FILE: res/sql/migration_007.sql ================================================ UPDATE contacts SET address_intl = address WHERE address_intl IS NULL AND address IS NOT NULL; UPDATE contacts SET comment_intl = comment WHERE comment_intl IS NULL AND comment IS NOT NULL; UPDATE contacts SET country_intl = country WHERE country_intl IS NULL AND country IS NOT NULL; UPDATE contacts SET my_antenna_intl = my_antenna WHERE my_antenna_intl IS NULL AND my_antenna IS NOT NULL; UPDATE contacts SET my_city_intl = my_city WHERE my_city_intl IS NULL AND my_city IS NOT NULL; UPDATE contacts SET my_country_intl = my_country WHERE my_country_intl IS NULL AND my_country IS NOT NULL; UPDATE contacts SET my_name_intl = my_name WHERE my_name_intl IS NULL AND my_name IS NOT NULL; UPDATE contacts SET my_postal_code_intl = my_postal_code WHERE my_postal_code_intl IS NULL AND my_postal_code IS NOT NULL; UPDATE contacts SET my_rig_intl = my_rig WHERE my_rig_intl IS NULL AND my_rig IS NOT NULL; UPDATE contacts SET my_sig_intl = my_sig WHERE my_sig_intl IS NULL AND my_sig IS NOT NULL; UPDATE contacts SET my_sig_info_intl = my_sig_info WHERE my_sig_info_intl IS NULL AND my_sig_info IS NOT NULL; UPDATE contacts SET my_street_intl = my_street WHERE my_street_intl IS NULL AND my_street IS NOT NULL; UPDATE contacts SET name_intl = name WHERE name_intl IS NULL AND name IS NOT NULL; UPDATE contacts SET notes_intl = notes WHERE notes_intl IS NULL AND notes IS NOT NULL; UPDATE contacts SET qth_intl = qth WHERE qth_intl IS NULL AND qth IS NOT NULL; UPDATE contacts SET rig_intl = rig WHERE rig_intl IS NULL AND rig IS NOT NULL; UPDATE contacts SET sig_intl = sig WHERE sig_intl IS NULL AND sig IS NOT NULL; UPDATE contacts SET sig_info_intl = sig_info WHERE sig_info_intl IS NULL AND sig_info IS NOT NULL; UPDATE contacts SET clublog_qso_upload_status='N' WHERE clublog_qso_upload_status IS NULL; UPDATE contacts SET qrzcom_qso_upload_status='N' WHERE qrzcom_qso_upload_status IS NULL; UPDATE contacts SET hrdlog_qso_upload_status='N' WHERE hrdlog_qso_upload_status IS NULL; UPDATE contacts SET eqsl_qsl_rcvd='N' WHERE eqsl_qsl_rcvd IS NULL; UPDATE contacts SET eqsl_qsl_sent='N' WHERE eqsl_qsl_sent IS NULL; ALTER TABLE rig_profiles ADD default_pwr REAL DEFAULT 100.0; ================================================ FILE: res/sql/migration_008.sql ================================================ CREATE TABLE IF NOT EXISTS alert_rules ( rule_name text PRIMARY KEY, enabled INTEGER, source INTEGER, dx_callsign TEXT, dx_country INTEGER, dx_logstatus INTEGER, dx_continent TEXT, spot_comment TEXT, mode TEXT, band TEXT, spotter_country INTEGER, spotter_continent TEXT ); CREATE INDEX "dxcc_idx" ON "contacts" ( "dxcc" ); INSERT INTO qso_filter_operators VALUES(6, "starts with"); ================================================ FILE: res/sql/migration_009.sql ================================================ ALTER TABLE rig_profiles ADD get_ptt INTEGER DEFAULT 0; ================================================ FILE: res/sql/migration_010.sql ================================================ ALTER TABLE bands ADD last_seen_freq FLOAT; DELETE FROM modes WHERE name IN ('C4FM', 'DMR', 'DSTAR'); UPDATE modes SET submodes = '["C4FM", "DMR", "DSTAR"]' WHERE name = 'DIGITALVOICE'; INSERT INTO modes (name, rprt, dxcc, enabled, submodes) VALUES ('DYNAMIC', NULL, 'DIGITAL', false, '["VARA HF", "VARA SATELLITE", "VARA FM 1200", "VARA FM 9600"]') ON CONFLICT DO NOTHING; ALTER TABLE contacts ADD my_arrl_sect TEXT; ALTER TABLE contacts ADD my_wwff_ref TEXT; ALTER TABLE contacts ADD wwff_ref TEXT; ALTER TABLE station_profiles ADD wwff TEXT; ================================================ FILE: res/sql/migration_011.sql ================================================ ALTER TABLE rig_profiles ADD qsy_wiping INTEGER DEFAULT 0; UPDATE rig_profiles SET qsy_wiping = get_freq; ================================================ FILE: res/sql/migration_012.sql ================================================ CREATE TABLE IF NOT EXISTS cwkey_profiles( profile_name TEXT PRIMARY KEY, model NUMBER NOT NULL, default_speed NUMBER NOT NULL, key_mode NUMBER, port_pathname TEXT, baudrate NUMBER ); CREATE TABLE IF NOT EXISTS cwshortcut_profiles( profile_name TEXT PRIMARY KEY, f1_short TEXT, f1_macro TEXT, f2_short TEXT, f2_macro TEXT, f3_short TEXT, f3_macro TEXT, f4_short TEXT, f4_macro TEXT, f5_short TEXT, f5_macro TEXT, f6_short TEXT, f6_macro TEXT, f7_short TEXT, f7_macro TEXT ); INSERT INTO cwshortcut_profiles( profile_name, f1_short, f1_macro, f2_short, f2_macro, f3_short, f3_macro, f4_short, f4_macro, f5_short, f5_macro ) VALUES ( "Run", "CQ", "CQ CQ CQ DE K", "QRZ?", "QRZ?", "AGN?", "AGN?", "Text", " DE OM TNX FER CALL UR RST NAME QTH HW CPY? DE K", "End", " DE TNX FER QSO GL 73 DE " ); ALTER TABLE rig_profiles ADD get_key_speed INTEGER DEFAULT 1; ALTER TABLE rig_profiles ADD assigned_cw_key TEXT DEFAULT ' '; ================================================ FILE: res/sql/migration_013.sql ================================================ CREATE TABLE IF NOT EXISTS sota_summits( summit_code TEXT PRIMARY KEY, association_name TEXT, region_name TEXT, summit_name TEXT, altm INTEGER, altft INTEGER, gridref1 REAL, gridref2 REAL, longitude REAL, latitude REAL, points INTEGER, bonus_points INTEGER, valid_from TEXT, valid_to TEXT ); CREATE TABLE IF NOT EXISTS wwff_directory( reference TEXT PRIMARY KEY, status TEXT, name TEXT, program TEXT, dxcc TEXT, state TEXT, county TEXT, continent TEXT, iota TEXT, iaruLocator TEXT, latitude REAL, longitude REAL, iucncat TEXT, valid_from TEXT, valid_to TEXT ); CREATE TABLE IF NOT EXISTS iota( iotaid TEXT PRIMARY KEY, islandname TEXT ); ================================================ FILE: res/sql/migration_014.sql ================================================ ALTER TABLE rig_profiles ADD key_speed_sync INTEGER DEFAULT 0; ================================================ FILE: res/sql/migration_015.sql ================================================ UPDATE modes SET submodes = '["C4FM", "DMR", "DSTAR", "FREEDV", "M17" ]' WHERE name = 'DIGITALVOICE'; INSERT INTO bands (name, start_freq, end_freq, enabled) VALUES ('submm',300000.0, 7500000.0, 0); ALTER TABLE contacts ADD altitude REAL; ALTER TABLE contacts ADD gridsquare_ext TEXT; ALTER TABLE contacts ADD hamlogeu_qso_upload_date TEXT; ALTER TABLE contacts ADD hamlogeu_qso_upload_status CHECK(clublog_qso_upload_status IN ('N', 'Y', 'M')) DEFAULT 'N'; ALTER TABLE contacts ADD hamqth_qso_upload_date TEXT; ALTER TABLE contacts ADD hamqth_qso_upload_status CHECK(clublog_qso_upload_status IN ('N', 'Y', 'M')) DEFAULT 'N'; ALTER TABLE contacts ADD my_altitude REAL; ALTER TABLE contacts ADD my_gridsquare_ext TEXT; ALTER TABLE contacts ADD my_pota_ref TEXT; ALTER TABLE contacts ADD pota_ref TEXT; CREATE TABLE IF NOT EXISTS pota_directory( reference TEXT PRIMARY KEY, name TEXT, active INTEGER, entityID INTEGER, locationDesc TEXT, latitude REAL, longitude REAL, grid TEXT ); ALTER TABLE station_profiles ADD pota TEXT; ================================================ FILE: res/sql/migration_016.sql ================================================ ALTER TABLE contacts RENAME TO contacts_old; DROP INDEX callsign_idx; DROP INDEX dxcc_idx; CREATE TABLE IF NOT EXISTS adif_enum_qsl_rcvd ( "value" TEXT PRIMARY KEY); INSERT INTO adif_enum_qsl_rcvd VALUES ('Y'); INSERT INTO adif_enum_qsl_rcvd VALUES ('N'); INSERT INTO adif_enum_qsl_rcvd VALUES ('R'); INSERT INTO adif_enum_qsl_rcvd VALUES ('I'); INSERT INTO adif_enum_qsl_rcvd VALUES ('V'); CREATE TABLE IF NOT EXISTS adif_enum_qsl_sent ( "value" TEXT PRIMARY KEY); INSERT INTO adif_enum_qsl_sent VALUES ('Y'); INSERT INTO adif_enum_qsl_sent VALUES ('N'); INSERT INTO adif_enum_qsl_sent VALUES ('R'); INSERT INTO adif_enum_qsl_sent VALUES ('Q'); INSERT INTO adif_enum_qsl_sent VALUES ('I'); CREATE TABLE IF NOT EXISTS adif_enum_ant_path ( "value" TEXT PRIMARY KEY); INSERT INTO adif_enum_ant_path VALUES ('G'); INSERT INTO adif_enum_ant_path VALUES ('O'); INSERT INTO adif_enum_ant_path VALUES ('S'); INSERT INTO adif_enum_ant_path VALUES ('L'); CREATE TABLE IF NOT EXISTS adif_enum_qso_upload_status ( "value" TEXT PRIMARY KEY); INSERT INTO adif_enum_qso_upload_status VALUES ('Y'); INSERT INTO adif_enum_qso_upload_status VALUES ('N'); INSERT INTO adif_enum_qso_upload_status VALUES ('M'); CREATE TABLE IF NOT EXISTS adif_enum_qsl_via ( "value" TEXT PRIMARY KEY); INSERT INTO adif_enum_qsl_via VALUES ('B'); INSERT INTO adif_enum_qsl_via VALUES ('D'); INSERT INTO adif_enum_qsl_via VALUES ('E'); INSERT INTO adif_enum_qsl_via VALUES ('M'); CREATE TABLE IF NOT EXISTS adif_enum_qso_complete ( "value" TEXT PRIMARY KEY); INSERT INTO adif_enum_qso_complete VALUES ('Y'); INSERT INTO adif_enum_qso_complete VALUES ('N'); INSERT INTO adif_enum_qso_complete VALUES ('NIL'); INSERT INTO adif_enum_qso_complete VALUES ('?'); CREATE TABLE IF NOT EXISTS adif_enum_boolean ( "value" TEXT PRIMARY KEY); INSERT INTO adif_enum_boolean VALUES ('Y'); INSERT INTO adif_enum_boolean VALUES ('N'); CREATE TABLE "contacts" ( "id" INTEGER, "start_time" TEXT, "end_time" TEXT, "callsign" TEXT NOT NULL, "rst_sent" TEXT, "rst_rcvd" TEXT, "freq" REAL, "band" TEXT, "mode" TEXT, "submode" TEXT, "name" TEXT, "qth" TEXT, "gridsquare" TEXT, "dxcc" INTEGER, "country" TEXT, "cont" TEXT, "cqz" INTEGER, "ituz" INTEGER, "pfx" TEXT, "state" TEXT, "cnty" TEXT, "iota" TEXT, "qsl_rcvd" TEXT NOT NULL DEFAULT 'N' REFERENCES adif_enum_qsl_rcvd("value"), "qsl_rdate" TEXT, "qsl_sent" TEXT NOT NULL DEFAULT 'N' REFERENCES adif_enum_qsl_sent("value"), "qsl_sdate" TEXT, "lotw_qsl_rcvd" TEXT NOT NULL DEFAULT 'N' REFERENCES adif_enum_qsl_rcvd("value"), "lotw_qslrdate" TEXT, "lotw_qsl_sent" TEXT NOT NULL DEFAULT 'N' REFERENCES adif_enum_qsl_sent("value"), "lotw_qslsdate" TEXT, "tx_pwr" REAL, "fields" JSON, "address" TEXT, "address_intl" TEXT, "age" INTEGER, "a_index" INTEGER, "ant_az" INTEGER, "ant_el" INTEGER, "ant_path" TEXT REFERENCES adif_enum_ant_path("value"), "arrl_sect" TEXT, "award_submitted" TEXT, "award_granted" TEXT, "band_rx" TEXT, "check" TEXT, "class" TEXT, "clublog_qso_upload_date" TEXT, "clublog_qso_upload_status" TEXT REFERENCES adif_enum_qso_upload_status("value"), "comment" TEXT, "comment_intl" TEXT, "contacted_op" TEXT, "contest_id" TEXT, "country_intl" TEXT, "credit_submitted" TEXT, "credit_granted" TEXT, "darc_dok" TEXT, "distance" REAL, "email" TEXT, "eq_call" TEXT, "eqsl_qslrdate" TEXT, "eqsl_qslsdate" TEXT, "eqsl_qsl_rcvd" TEXT NOT NULL DEFAULT 'N' REFERENCES adif_enum_qsl_rcvd("value"), "eqsl_qsl_sent" TEXT NOT NULL DEFAULT 'N' REFERENCES adif_enum_qsl_sent("value"), "fists" INTEGER, "fists_cc" INTEGER, "force_init" TEXT REFERENCES adif_enum_boolean("value"), "freq_rx" REAL, "guest_op" TEXT, "hrdlog_qso_upload_date" TEXT, "hrdlog_qso_upload_status" TEXT REFERENCES adif_enum_qso_upload_status("value"), "iota_island_id" INTEGER, "k_index" INTEGER, "lat" TEXT, "lon" TEXT, "max_bursts" REAL, "ms_shower" TEXT, "my_antenna" TEXT, "my_antenna_intl" TEXT, "my_city" TEXT, "my_city_intl" TEXT, "my_cnty" TEXT, "my_country" TEXT, "my_country_intl" TEXT, "my_cq_zone" INTEGER, "my_dxcc" INTEGER, "my_fists" INTEGER, "my_gridsquare" TEXT, "my_iota" TEXT, "my_iota_island_id" INTEGER, "my_itu_zone" INTEGER, "my_lat" TEXT, "my_lon" TEXT, "my_name" TEXT, "my_name_intl" TEXT, "my_postal_code" TEXT, "my_postal_code_intl" TEXT, "my_rig" TEXT, "my_rig_intl" TEXT, "my_sig" TEXT, "my_sig_intl" TEXT, "my_sig_info" TEXT, "my_sig_info_intl" TEXT, "my_sota_ref" TEXT, "my_state" TEXT, "my_street" TEXT, "my_street_intl" TEXT, "my_usaca_counties" TEXT, "my_vucc_grids" TEXT, "name_intl" TEXT, "notes" TEXT, "notes_intl" TEXT, "nr_bursts" INTEGER, "nr_pings" INTEGER, "operator" TEXT, "owner_callsign" TEXT, "precedence" TEXT, "prop_mode" TEXT, "public_key" TEXT, "qrzcom_qso_upload_date" TEXT, "qrzcom_qso_upload_status" TEXT REFERENCES adif_enum_qso_upload_status("value"), "qslmsg" TEXT, "qslmsg_intl" TEXT, "qsl_rcvd_via" TEXT REFERENCES adif_enum_qsl_via("value"), "qsl_sent_via" TEXT REFERENCES adif_enum_qsl_via("value"), "qsl_via" TEXT, "qso_complete" TEXT REFERENCES adif_enum_qso_complete("value"), "qso_random" TEXT REFERENCES adif_enum_boolean("value"), "qth_intl" TEXT, "region" TEXT, "rig" TEXT, "rig_intl" TEXT, "rx_pwr" REAL, "sat_mode" TEXT, "sat_name" TEXT, "sfi" INTEGER, "sig" TEXT, "sig_intl" TEXT, "sig_info" TEXT, "sig_info_intl" TEXT, "silent_key" TEXT REFERENCES adif_enum_boolean("value"), "skcc" TEXT, "sota_ref" TEXT, "srx" INTEGER, "srx_string" TEXT, "station_callsign" TEXT, "stx" INTEGER, "stx_string" TEXT, "swl" TEXT REFERENCES adif_enum_boolean("value"), "ten_ten" INTEGER, "uksmg" INTEGER, "usaca_counties" TEXT, "ve_prov" TEXT, "vucc_grids" TEXT, "web" TEXT, "my_arrl_sect" TEXT, "my_wwff_ref" TEXT, "wwff_ref" TEXT, "altitude" REAL, "gridsquare_ext" TEXT, "hamlogeu_qso_upload_date" TEXT, "hamlogeu_qso_upload_status" TEXT REFERENCES adif_enum_qso_upload_status("value"), "hamqth_qso_upload_date" TEXT, "hamqth_qso_upload_status" TEXT REFERENCES adif_enum_qso_upload_status("value"), "my_altitude" REAL, "my_gridsquare_ext" TEXT, "my_pota_ref" TEXT, "pota_ref" TEXT, PRIMARY KEY("id") ); CREATE INDEX "callsign_idx" ON "contacts" ("callsign"); CREATE INDEX "dxcc_idx" ON "contacts" ("dxcc"); INSERT INTO contacts SELECT * FROM contacts_old; UPDATE contacts SET altitude = NULL where altitude = 0.0; UPDATE contacts SET my_altitude = NULL where my_altitude = 0.0; UPDATE contacts SET tx_pwr = NULL where tx_pwr = 0.0; ================================================ FILE: res/sql/migration_017.sql ================================================ CREATE TABLE IF NOT EXISTS rot_user_buttons_profiles( profile_name TEXT PRIMARY KEY, button1_short TEXT, button1_value REAL DEFAULT '-1', button2_short TEXT, button2_value REAL DEFAULT '-1', button3_short TEXT, button3_value REAL DEFAULT '-1', button4_short TEXT, button4_value REAL DEFAULT '-1' ); INSERT INTO rot_user_buttons_profiles( profile_name, button1_short, button1_value, button2_short, button2_value, button3_short, button3_value, button4_short, button4_value ) VALUES ( "Basic", "N", 0, "S", 180, "W", 270, "E", 90 ); UPDATE contacts SET ant_az = 360 + ant_az WHERE ant_az < 0; CREATE TABLE IF NOT EXISTS membership_directory( short_desc TEXT, long_desc TEXT, filename TEXT, last_update TEXT, num_records INTEGER ); CREATE TABLE IF NOT EXISTS membership ( callsign TEXT, member_id TEXT, valid_from TEXT, valid_to TEXT, clubid TEXT ); CREATE TABLE IF NOT EXISTS membership_versions ( clubid TEXT PRIMARY KEY, version INTEGER ); CREATE INDEX IF NOT EXISTS membership_callsign_idx ON membership(callsign); CREATE INDEX IF NOT EXISTS membership_clubid_idx ON membership(clubid); CREATE TABLE IF NOT EXISTS contacts_autovalue( contactid INTEGER PRIMARY KEY REFERENCES contacts("id") ON DELETE CASCADE, base_callsign TEXT ); CREATE INDEX IF NOT EXISTS contacts_autovalue_call_idx on contacts_autovalue(base_callsign); CREATE VIEW IF NOT EXISTS contact_clubs_view AS SELECT contactid, clubid FROM contacts_autovalue c, membership m WHERE c.base_callsign = m.callsign; INSERT INTO contacts_autovalue SELECT ID, ((WITH tokenizedCallsign(word, csv) AS ( SELECT '', callsign||'/' UNION ALL SELECT substr(csv, 0, instr(csv, '/')), substr(csv, instr(csv, '/') + 1) FROM tokenizedCallsign WHERE csv != '' ) SELECT word FROM tokenizedCallsign WHERE word != '' AND word REGEXP '^([A-Z][0-9]|[A-Z]{1,2}|[0-9][A-Z])([0-9]|[0-9]+)([A-Z]+)$' LIMIT 1) ) FROM contacts; ALTER TABLE alert_rules ADD dx_member TEXT DEFAULT '*'; ================================================ FILE: res/sql/migration_018.sql ================================================ ALTER TABLE cwkey_profiles ADD hostname TEXT; ALTER TABLE cwkey_profiles ADD netport NUMBER; ================================================ FILE: res/sql/migration_019.sql ================================================ UPDATE contacts SET clublog_qso_upload_status = 'M' WHERE mode IN ('FT4', 'FST4', 'FST4W', 'Q65') AND UPPER(clublog_qso_upload_status) = 'Y'; UPDATE contacts SET qrzcom_qso_upload_status = 'M' WHERE mode IN ('FT4', 'FST4', 'FST4W', 'Q65') AND UPPER(qrzcom_qso_upload_status) = 'Y'; UPDATE contacts SET mode = 'MFSK', submode = 'FT4' WHERE mode = 'FT4'; UPDATE contacts SET mode = 'MFSK', submode = 'FST4' WHERE mode = 'FST4'; UPDATE contacts SET mode = 'MFSK', submode = 'FST4W' WHERE mode = 'FST4W'; UPDATE contacts SET mode = 'MFSK', submode = 'Q65' WHERE mode = 'Q65'; UPDATE modes SET name = 'CONTESTI' WHERE name = 'CONTESTIA'; ================================================ FILE: res/sql/migration_020.sql ================================================ CREATE TABLE IF NOT EXISTS newcontact_layout_profiles( profile_name TEXT PRIMARY KEY, row_A TEXT, row_B TEXT ); ================================================ FILE: res/sql/migration_021.sql ================================================ CREATE TABLE IF NOT EXISTS chat_highlight_rules( rule_name TEXT PRIMARY KEY, room_id TEXT, enabled INTEGER, rule_definition TEXT ); ALTER TABLE ant_profiles ADD azimuth_beamwidth DOUBLE DEFAULT 0.0; ALTER TABLE ant_profiles ADD azimuth_offset DOUBLE DEFAULT 0.0; ================================================ FILE: res/sql/migration_022.sql ================================================ ALTER TABLE newcontact_layout_profiles RENAME TO main_layout_profiles; ALTER TABLE main_layout_profiles ADD detail_col_A TEXT; ALTER TABLE main_layout_profiles ADD detail_col_B TEXT; ALTER TABLE main_layout_profiles ADD detail_col_C TEXT; ALTER TABLE main_layout_profiles ADD main_geometry TEXT; ALTER TABLE main_layout_profiles ADD main_state TEXT; ALTER TABLE main_layout_profiles ADD dark_mode INTEGER; UPDATE main_layout_profiles SET detail_col_A = '15,17,16,19,20,34,151', detail_col_B = '54,21,165,140,155,135,137', detail_col_C = '56,152', dark_mode = 0 WHERE detail_col_A IS NULL; DROP TABLE IF EXISTS contacts_old; CREATE INDEX IF NOT EXISTS start_time_idx ON contacts(start_time); CREATE INDEX IF NOT EXISTS band_idx ON contacts(band); CREATE INDEX IF NOT EXISTS mode_idx ON contacts(mode); CREATE TABLE IF NOT EXISTS adif_enum_primary_subdivision( code TEXT, subdivision_name TEXT, dxcc INTEGER, PRIMARY KEY (code, dxcc) ); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('AK', 'Alaska', 6); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('HI', 'Hawaii', 110); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('CT', 'Connecticut', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('ME', 'Maine', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('MA', 'Massachusetts', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('NH', 'New Hampshire', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('RI', 'Rhode Island', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('VT', 'Vermont', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('NJ', 'New Jersey', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('NY', 'New York', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('DE', 'Delaware', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('DC', 'District of Columbia', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('MD', 'Maryland', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('PA', 'Pennsylvania', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('AL', 'Alabama', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('FL', 'Florida', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('GA', 'Georgia', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('KY', 'Kentucky', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('NC', 'North Carolina', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('SC', 'South Carolina', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('TN', 'Tennessee', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('VA', 'Virginia', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('AR', 'Arkansas', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('LA', 'Louisiana', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('MS', 'Mississippi', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('NM', 'New Mexico', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('OK', 'Oklahoma', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('TX', 'Texas', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('CA', 'California', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('AZ', 'Arizona', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('ID', 'Idaho', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('MT', 'Montana', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('NV', 'Nevada', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('OR', 'Oregon', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('UT', 'Utah', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('WA', 'Washington', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('WY', 'Wyoming', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('MI', 'Michigan', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('OH', 'Ohio', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('WV', 'West Virginia', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('IL', 'Illinois', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('IN', 'Indiana', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('WI', 'Wisconsin', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('CO', 'Colorado', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('IA', 'Iowa', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('KS', 'Kansas', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('MN', 'Minnesota', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('MO', 'Missouri', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('NE', 'Nebraska', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('ND', 'North Dakota', 291); INSERT INTO adif_enum_primary_subdivision(code, subdivision_name, dxcc) values ('SD', 'South Dakota', 291); ================================================ FILE: res/sql/migration_023.sql ================================================ CREATE TABLE contacts_qsl_cards ( contactid INTEGER REFERENCES contacts("id") ON DELETE CASCADE, "source" TEXT, name TEXT, "data" TEXT); CREATE UNIQUE INDEX IF NOT EXISTS contacts_qsl_id_idx ON contacts_qsl_cards(contactid, "source", name); ================================================ FILE: res/sql/migration_024.sql ================================================ ALTER TABLE rig_profiles ADD driver INTEGER DEFAULT 0; UPDATE rig_profiles SET driver = 1; ================================================ FILE: res/sql/migration_025.sql ================================================ ALTER TABLE rig_profiles ADD dxspot2rig INTEGER DEFAULT 0; ================================================ FILE: res/sql/migration_026.sql ================================================ ALTER TABLE rot_profiles ADD driver INTEGER DEFAULT 0; ALTER TABLE station_profiles ADD ituz INTEGER; ALTER TABLE station_profiles ADD cqz INTEGER; ALTER TABLE station_profiles ADD dxcc INTEGER; ALTER TABLE station_profiles ADD country TEXT; UPDATE rot_profiles SET driver = 1; ================================================ FILE: res/sql/migration_027.sql ================================================ ALTER TABLE bands ADD sat_designator TEXT; UPDATE bands SET sat_designator='H' WHERE lower(name) = '15m'; UPDATE bands SET sat_designator='A' WHERE lower(name) = '10m'; UPDATE bands SET sat_designator='V' WHERE lower(name) = '2m'; UPDATE bands SET sat_designator='U' WHERE lower(name) = '70cm'; UPDATE bands SET sat_designator='L' WHERE lower(name) = '23cm'; UPDATE bands SET sat_designator='S' WHERE lower(name) = '13cm'; UPDATE bands SET sat_designator='S2' WHERE lower(name) = '9cm'; UPDATE bands SET sat_designator='C' WHERE lower(name) = '6cm'; UPDATE bands SET sat_designator='X' WHERE lower(name) = '3cm'; UPDATE bands SET sat_designator='K' WHERE lower(name) = '1.25cm'; UPDATE bands SET sat_designator='R' WHERE lower(name) = '6mm'; ================================================ FILE: res/sql/migration_028.sql ================================================ DROP TABLE dxcc_prefixes; CREATE TABLE "dxcc_prefixes" ( "prefix" TEXT NOT NULL, "exact" INTEGER, "dxcc" INTEGER, "cqz" INTEGER, "ituz" INTEGER, "cont" TEXT, "lat" REAL, "lon" REAL, FOREIGN KEY("dxcc") REFERENCES "dxcc_entities"("id") ); CREATE INDEX prefix_idx ON dxcc_prefixes(prefix); DELETE FROM dxcc_entities; ================================================ FILE: res/sql/migration_029.sql ================================================ ALTER TABLE ant_profiles ADD selected INTEGER; ALTER TABLE cwkey_profiles ADD selected INTEGER; ALTER TABLE cwshortcut_profiles ADD selected INTEGER; ALTER TABLE rig_profiles ADD selected INTEGER; ALTER TABLE rot_profiles ADD selected INTEGER; ALTER TABLE rot_user_buttons_profiles ADD selected INTEGER; ALTER TABLE station_profiles ADD selected INTEGER; ALTER TABLE main_layout_profiles ADD selected INTEGER; CREATE INDEX IF NOT EXISTS contacts_pota_idx ON contacts (pota_ref); CREATE INDEX IF NOT EXISTS contacts_my_pota_idx ON contacts (my_pota_ref); CREATE INDEX IF NOT EXISTS contacts_sota_idx ON contacts (sota_ref); CREATE INDEX IF NOT EXISTS contacts_my_sota_idx ON contacts (my_sota_ref); CREATE INDEX IF NOT EXISTS contacts_wwff_ref_idx ON contacts (wwff_ref); CREATE INDEX IF NOT EXISTS contacts_my_wwff_ref_idx ON contacts (my_wwff_ref); CREATE INDEX IF NOT EXISTS contacts_iota_idx ON contacts (iota); CREATE INDEX IF NOT EXISTS contacts_sig_intl_idx ON contacts (sig_intl); DROP INDEX band_idx; CREATE INDEX IF NOT EXISTS contacts_band_idx ON contacts (band); DROP INDEX callsign_idx; CREATE INDEX IF NOT EXISTS contacts_callsign_idx ON contacts (callsign); DROP INDEX dxcc_idx; CREATE INDEX IF NOT EXISTS contacts_dxcc_idx ON contacts (dxcc); DROP INDEX mode_idx; CREATE INDEX IF NOT EXISTS contacts_mode_idx ON contacts (mode); DROP INDEX start_time_idx; CREATE INDEX IF NOT EXISTS contacts_start_time_idx ON contacts (start_time); DROP INDEX prefix_idx; CREATE INDEX IF NOT EXISTS dxcc_prefixes_prefix_idx ON dxcc_prefixes (prefix); ALTER TABLE rig_profiles ADD ptt_type TEXT; ALTER TABLE rig_profiles ADD ptt_port_pathname TEXT; UPDATE rig_profiles SET ptt_type = 'RIG' WHERE driver = 1; ================================================ FILE: res/sql/migration_030.sql ================================================ CREATE TABLE IF NOT EXISTS activity_profiles( profile_name TEXT PRIMARY KEY, config TEXT, selected INTEGER ); INSERT INTO activity_profiles SELECT profile_name, json_object( 'activityName', profile_name, 'profiles', json_array( json_object( 'profileType', 4, 'name', profile_name ) ) ) AS config, selected FROM main_layout_profiles; ALTER TABLE alert_rules ADD COLUMN ituz INTEGER; ALTER TABLE alert_rules ADD COLUMN cqz INTEGER; ALTER TABLE main_layout_profiles ADD tabsexpanded INTEGER DEFAULT 1; ALTER TABLE station_profiles ADD county TEXT; ALTER TABLE station_profiles ADD operator_callsign TEXT; ================================================ FILE: res/sql/migration_031.sql ================================================ INSERT INTO bands (name, start_freq, end_freq, enabled) VALUES ('8m',40.0, 45.0, 0); ================================================ FILE: res/sql/migration_032.sql ================================================ UPDATE modes SET submodes = '["FMHELL", "FSKHELL", "FSKH105", "FSKH245", "HELL80", "HELLX5", "HELLX9", "HFSK", "PSKHELL", "SLOWHELL"]' WHERE name = 'HELL'; ALTER TABLE contacts ADD cnty_alt TEXT; ALTER TABLE contacts ADD dcl_qslrdate TEXT; ALTER TABLE contacts ADD dcl_qslsdate TEXT; ALTER TABLE contacts ADD dcl_qsl_rcvd TEXT NOT NULL DEFAULT 'N'; ALTER TABLE contacts ADD dcl_qsl_sent TEXT NOT NULL DEFAULT 'N'; ALTER TABLE contacts ADD morse_key_info TEXT; ALTER TABLE contacts ADD morse_key_type TEXT; ALTER TABLE contacts ADD my_cnty_alt TEXT; ALTER TABLE contacts ADD my_darc_dok TEXT; ALTER TABLE contacts ADD my_morse_key_info TEXT; ALTER TABLE contacts ADD my_morse_key_type TEXT; ALTER TABLE contacts ADD qrzcom_qso_download_date TEXT; ALTER TABLE contacts ADD qrzcom_qso_download_status TEXT; ALTER TABLE contacts ADD qslmsg_rcvd TEXT; ALTER TABLE station_profiles ADD darc_dok TEXT; ALTER TABLE alert_rules ADD pota INTEGER DEFAULT 0; ALTER TABLE alert_rules ADD sota INTEGER DEFAULT 0; ALTER TABLE alert_rules ADD iota INTEGER DEFAULT 0; ALTER TABLE alert_rules ADD wwff INTEGER DEFAULT 0; ================================================ FILE: res/sql/migration_033.sql ================================================ ALTER TABLE main_layout_profiles ADD addlbandmaps TEXT; ================================================ FILE: res/sql/migration_034.sql ================================================ ALTER TABLE cwkey_profiles ADD paddle_swap INTEGER DEFAULT 0; ALTER TABLE contacts_autovalue ADD wavelog_qso_upload_status TEXT REFERENCES adif_enum_qso_upload_status("value"); ALTER TABLE contacts_autovalue ADD wavelog_qso_upload_date TEXT; ================================================ FILE: res/sql/migration_035.sql ================================================ INSERT INTO modes (name, submodes, rprt, dxcc, enabled) VALUES ('FSK', '["SCAMP_FAST", "SCAMP_SLOW", "SCAMP_VSLOW"]', NULL, 'DIGITAL', 0); INSERT INTO modes (name, submodes, rprt, dxcc, enabled) VALUES ('MTONE', '["SCAMP_OO", "SCAMP_OO_SLW"]', NULL, 'DIGITAL', 0); ALTER TABLE contacts ADD eqsl_ag TEXT; DROP TABLE IF EXISTS DXCC_PREFIXES; DROP TABLE IF EXISTS DXCC_ENTITIES; CREATE TABLE IF NOT EXISTS dxcc_entities_ad1c ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, prefix TEXT, cont TEXT, cqz INTEGER, ituz INTEGER, lat REAL, lon REAL, tz REAL ); CREATE TABLE "dxcc_prefixes_ad1c" ( prefix TEXT NOT NULL, exact INTEGER NOT NULL, dxcc INTEGER NOT NULL, cqz INTEGER, ituz INTEGER, cont TEXT, lat REAL, lon REAL ); CREATE INDEX dxcc_prefixes_ad1c_idx ON dxcc_prefixes_ad1c (prefix, exact); CREATE TABLE IF NOT EXISTS dxcc_entities_clublog ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, prefix TEXT, deleted INTEGER NOT NULL, cont TEXT, cqz INTEGER, ituz INTEGER, lat REAL, lon REAL, start TEXT, "end" TEXT ); CREATE TABLE "dxcc_prefixes_clublog" ( prefix TEXT NOT NULL, exact INTEGER NOT NULL, dxcc INTEGER NOT NULL, cqz INTEGER, ituz INTEGER, cont TEXT, lat REAL, lon REAL, start TEXT, "end" TEXT ); CREATE INDEX dxcc_prefixes_clublog_idx on dxcc_prefixes_clublog (prefix, exact); CREATE TABLE "dxcc_zone_exceptions_clublog" ( record INTEGER PRIMARY KEY, call TEXT NOT NULL, cqz INTEGER NOT NULL, start TEXT, "end" TEXT ); ALTER TABLE rig_profiles ADD COLUMN rts TEXT; ALTER TABLE rig_profiles ADD COLUMN dtr TEXT; ALTER TABLE rig_profiles ADD civaddr INTEGER; ================================================ FILE: res/sql/migration_036.sql ================================================ ALTER TABLE rig_profiles ADD COLUMN share_rigctld INTEGER DEFAULT 0; ALTER TABLE rig_profiles ADD COLUMN rigctld_port INTEGER DEFAULT 4532; ALTER TABLE rig_profiles ADD COLUMN rigctld_path TEXT; ALTER TABLE rig_profiles ADD COLUMN rigctld_args TEXT; ALTER TABLE contacts_qsl_cards ADD COLUMN favorite INTEGER DEFAULT 0; ALTER TABLE cwkey_profiles ADD paddle_only_sidetone INTEGER DEFAULT 0; ALTER TABLE cwkey_profiles ADD sidetone_frequency INTEGER DEFAULT 800; INSERT INTO qso_filter_operators VALUES(7, "regexp"); UPDATE log_param SET value = REPLACE(value, '|FT8', '|FTx') WHERE name = 'dxc/filter/dx/moderegexp' AND value LIKE '%|FT8%'; UPDATE alert_rules SET mode = REPLACE(mode, '|FT8', '|FTx') WHERE mode LIKE '%|FT8%'; CREATE TABLE IF NOT EXISTS adif_enum_secondary_subdivision ( dxcc INTEGER NOT NULL, code TEXT NOT NULL, subdivision_name TEXT NOT NULL, primary_code TEXT, aux TEXT, PRIMARY KEY (dxcc, code) ); CREATE INDEX IF NOT EXISTS contacts_dxcc_upper_cnty_idx ON contacts(dxcc, upper(cnty)) WHERE cnty IS NOT NULL AND cnty != ''; INSERT INTO adif_enum_secondary_subdivision VALUES (6,'AK,Haines','Haines','AK','Alaska First Judicial District'); INSERT INTO adif_enum_secondary_subdivision VALUES (6,'AK,Juneau','Juneau','AK','Alaska First Judicial District'); INSERT INTO adif_enum_secondary_subdivision VALUES (6,'AK,Ketchikan Gateway','Ketchikan Gateway','AK','Alaska First Judicial District'); INSERT INTO adif_enum_secondary_subdivision VALUES (6,'AK,Prince of Wales-Hyder','Prince of Wales-Hyder','AK','Alaska First Judicial District'); INSERT INTO adif_enum_secondary_subdivision VALUES (6,'AK,Sitka','Sitka','AK','Alaska First Judicial District'); INSERT INTO adif_enum_secondary_subdivision VALUES (6,'AK,Skagway','Skagway','AK','Alaska First Judicial District'); INSERT INTO adif_enum_secondary_subdivision VALUES (6,'AK,Yakutat','Yakutat','AK','Alaska First Judicial District'); INSERT INTO adif_enum_secondary_subdivision VALUES (6,'AK,Wrangell','Wrangell','AK','Alaska First Judicial District'); INSERT INTO adif_enum_secondary_subdivision VALUES (6,'AK,Hoonah-Angoon','Hoonah-Angoon','AK','Alaska First Judicial District'); INSERT INTO adif_enum_secondary_subdivision VALUES (6,'AK,Petersburg','Petersburg','AK','Alaska First Judicial District'); INSERT INTO adif_enum_secondary_subdivision VALUES (6,'AK,Wales-Hyder','Wales-Hyder','AK','Alaska First Judicial District'); INSERT INTO adif_enum_secondary_subdivision VALUES (6,'AK,Denali','Denali','AK','Alaska Fourth Judicial District'); INSERT INTO adif_enum_secondary_subdivision VALUES (6,'AK,Bethel','Bethel','AK','Alaska Fourth Judicial District'); INSERT INTO adif_enum_secondary_subdivision VALUES (6,'AK,Fairbanks North Star','Fairbanks North Star','AK','Alaska Fourth Judicial District'); INSERT INTO adif_enum_secondary_subdivision VALUES (6,'AK,Southeast Fairbanks','Southeast Fairbanks','AK','Alaska Fourth Judicial District'); INSERT INTO adif_enum_secondary_subdivision VALUES (6,'AK,Saint Matthew Island','Saint Matthew Island','AK','Alaska Fourth Judicial District'); INSERT INTO adif_enum_secondary_subdivision VALUES (6,'AK,Yukon-Koyukuk','Yukon-Koyukuk','AK','Alaska Fourth Judicial District'); INSERT INTO adif_enum_secondary_subdivision VALUES (6,'AK,Fairbanks','Fairbanks','AK','Alaska Fourth Judicial District'); INSERT INTO adif_enum_secondary_subdivision VALUES (6,'AK,Nome','Nome','AK','Alaska Second Judicial District'); INSERT INTO adif_enum_secondary_subdivision VALUES (6,'AK,North Slope','North Slope','AK','Alaska Second Judicial District'); INSERT INTO adif_enum_secondary_subdivision VALUES (6,'AK,Northwest Arctic','Northwest Arctic','AK','Alaska Second Judicial District'); INSERT INTO adif_enum_secondary_subdivision VALUES (6,'AK,Kusilvak','Kusilvak','AK','Alaska Second Judicial District'); INSERT INTO adif_enum_secondary_subdivision VALUES (6,'AK,Aleutians East','Aleutians East','AK','Alaska Third Judicial District'); INSERT INTO adif_enum_secondary_subdivision VALUES (6,'AK,Aleutians West','Aleutians West','AK','Alaska Third Judicial District'); INSERT INTO adif_enum_secondary_subdivision VALUES (6,'AK,Lake and Peninsula','Lake and Peninsula','AK','Alaska Third Judicial District'); INSERT INTO adif_enum_secondary_subdivision VALUES (6,'AK,Pribilof Islands','Pribilof Islands','AK','Alaska Third Judicial District'); INSERT INTO adif_enum_secondary_subdivision VALUES (6,'AK,Anchorage','Anchorage','AK','Alaska Third Judicial District'); INSERT INTO adif_enum_secondary_subdivision VALUES (6,'AK,Bristol Bay','Bristol Bay','AK','Alaska Third Judicial District'); INSERT INTO adif_enum_secondary_subdivision VALUES (6,'AK,Dillingham','Dillingham','AK','Alaska Third Judicial District'); INSERT INTO adif_enum_secondary_subdivision VALUES (6,'AK,Kenai Peninsula','Kenai Peninsula','AK','Alaska Third Judicial District'); INSERT INTO adif_enum_secondary_subdivision VALUES (6,'AK,Kodiak Island','Kodiak Island','AK','Alaska Third Judicial District'); INSERT INTO adif_enum_secondary_subdivision VALUES (6,'AK,Matanuska-Susitna','Matanuska-Susitna','AK','Alaska Third Judicial District'); INSERT INTO adif_enum_secondary_subdivision VALUES (6,'AK,Valdez-Cordova','Valdez-Cordova','AK','Alaska Third Judicial District'); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-01','Zheleznodorozhny district of Barnaul','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-02','Industrial district of Barnaul','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-03','Leninsky district of Barnaul','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-04','Oktyabrsky district of Barnaul','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-05','Centalny District of Barnaul','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-08','Rubtsovsk','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-09','Slavgorod','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-10','Yarovoye','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-11','Aleysky Area ','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-12','Altaisky Area ','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-13','Bayevsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-14','Biysky Area ','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-15','Blagoveshchensk Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-16','Burlinsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-17','Bystroistoksky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-18','Volchihinsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-19','Egoryevsky Area ','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-20','Eltsovsky Area ','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-21','Zavyalovsky Area ','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-22','Zalesovsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-23','Zarinsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-24','Zmeinogorsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-25','Zonalnyj Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-26','Kalmansky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-27','Kamensky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-28','Klyuchevsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-29','Kosihinsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-30','Krasnogorsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-31','Krasnoshchekovsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-32','Krutikhinsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-33','Kulundinsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-34','Kuryinsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-35','Kytmanovsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-36','Loktevsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-37','Mamontovsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-38','Mihajlovsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-39','Nemetsky National Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-40','Novichihinsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-41','Pavlovsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-42','Pankrushihinsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-43','Pervomajsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-44','Petropavlovsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-45','Pospelihinsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-46','Rebrihinsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-47','Rodinsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-48','Romanovsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-49','Rubtsovsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-50','Slavgorodsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-51','Smolensky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-52','Sovietsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-53','Soloneshensky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-54','Soltonsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-55','Suyetsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-56','Tabunsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-57','Talmensky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-58','Togulsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-59','Topchihinsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-60','Tretyakovsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-61','Troitsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-62','Tyumentsevsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-63','Uglovsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-64','Ust-Kalmanskiy Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-65','Ust-Pristanskiy Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-66','Habarsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-67','Tselinny Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-68','Charyshsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-69','Shelabolihinsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-70','Shipunovsky Area','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-71','Alejsk','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-72','Belokurikha','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-73','Biysk','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-74','Gornjak','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-75','Zarinsk','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-76','Zmeinogorsk','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-77','Kamen-Na-Obi','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-78','Novoaltaisk','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AL-79','Zato Sibirsky','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AM-01','Blagoveshchensk','AM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AM-02','Belogorsk','AM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AM-03','Zeya','AM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AM-04','Raychikhinsk','AM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AM-05','Svobodnyj','AM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AM-06','Tynda','AM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AM-07','Shimanovsk','AM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AM-08','Arharinsky Area','AM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AM-09','Belogorsky Area','AM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AM-10','Blagoveshchensky Area','AM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AM-11','Bureysky Area','AM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AM-12','Zavitinsky Area / incl. Zavitinsk','AM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AM-13','Zeysky Area','AM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AM-14','Ivanovsky Area','AM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AM-15','Konstantinovsky Area','AM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AM-16','Magdagachinsky Area','AM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AM-17','Mazanovsky Area','AM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AM-18','Mikhaylovsky Area','AM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AM-19','Oktyabrsky district','AM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AM-20','Romnensky Area','AM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AM-21','Svobodnensky Area','AM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AM-22','Selemdzhinsky Area','AM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AM-23','Seryshevsky Area','AM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AM-24','Skovorodinsky Area / incl. Skovorodino','AM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AM-25','Tambovsky Area','AM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AM-26','Tyndinsky Area','AM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AM-27','Shimanowski Area','AM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AM-28','Zato Tsiolkovsky','AM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'AM-29','Progress','AM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-01','Demsky District of Ufa','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-02','Kalininsky District of Ufa','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-03','Kirovsky District of Ufa','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-04','Leninsky District of Ufa','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-05','Oktyabrsky District of Ufa','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-06','Ordzhonikidzevsky District of Ufa','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-07','Sovetsky District of Ufa','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-08','Agidel','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-17','Kumertau','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-19','Neftekamsk','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-20','Oktjabrsky','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-21','Salavat','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-22','Sibaj','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-23','Sterlitamak','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-27','Zato Mezhgore','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-28','Abzelilovsky Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-29','Alsheevsky Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-30','Arkhangelsk Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-31','Askinsky Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-32','Aurgazinsky Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-33','Baymaksky Area / incl. Baymak','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-34','Bakalinsky Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-35','Baltachevsky Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-36','Belebeevsky Area / incl. Belebey','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-37','Belokatajsky Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-38','Beloretsky Area / incl. Beloreck','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-39','Bizhbulyaksky Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-40','Birsky Area / incl. Birsk','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-41','Blagovarsky Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-42','Blagoveshchensk Area / incl. Blagoveshchensk','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-43','Buzdjaksky Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-44','Buraevsky Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-45','Burzjansky Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-46','Gafurijsky Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-47','Davlekanovsky / incl. Davlekanovo','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-48','Duvansky Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-49','Dyurtyulinsky Area / incl. Dyurtyuli','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-50','Ermekeevsky Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-51','Zianchurinsky Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-52','Zilairsky Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-53','Iglinsky Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-54','Ilishevsky Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-55','Ishimbaysky Area / incl. Ishimbay','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-56','Kaltasinsky Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-57','Karaidelsky Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-58','Karmaskalinsky Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-59','Kiginsky Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-60','Krasnokamsky Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-61','Kugarchinsky Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-62','Kuyurgazinsky Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-63','Kushnarenkovsky Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-64','Meleuzovsky Area / incl. Meleuz','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-65','Mechetlinsky Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-66','Mishkinsky Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-67','Miyakinsky Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-68','Nurimanovsky Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-69','Salavatsky Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-70','Sterlibashevsky Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-71','Sterlitamaksky Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-72','Tatyshlinsky Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-73','Tuymazinsky Area / incl. Tuymazy','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-74','Ufimsky Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-75','Uchalinsky Area / incl. Uchaly','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-76','Fedorovsky Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-77','Haybullinsky Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-78','Chekmagushevsky Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-79','Chishminsky Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-80','Sharansky Area','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BA-81','Yanaulsky Area / incl. Yanaul','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BU-01','Zheleznodorozhny districd of Ulan-Ude','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BU-02','Oktyabrsky district of Ulan-Ude','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BU-03','Sovetsky district of Ulan-Ude','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BU-04','Severobaykalsk','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BU-05','Barguzinsky Area','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BU-06','Bauntovsky Area','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BU-07','Bichursky Area','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BU-08','Dzhidinsky Area','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BU-09','Eravninsky Area','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BU-10','Zaigrayevsky Area','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BU-11','Zakamensky Area / incl. Zakamensk','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BU-12','Ivolginsky Area','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BU-13','Kabansky Area / incl. Grandmother''s','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BU-14','Kizhinginsky Area','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BU-15','Kurumkansky Area','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BU-16','Kyakhtinsky district / incl. Kyakhta','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BU-17','Muysky Area','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BU-18','Okinsky Area','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BU-19','Pribaykalsky Area','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BU-20','Severo-Baikaljsky Area','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BU-21','Selenginsky Area / incl. Gusinoozersk','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BU-22','Tarbagatajsky Area','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BU-23','Tunkinsky Area','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BU-24','Horinsky Area','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'BU-25','Mukhorshibirsky Area','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-01','Kalininsky district of Chelyabinsk','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-02','Kurchatovsky district of Chelyabinsk','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-03','Leninsky district of Chelyabinsk','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-04','Metallurgichesky district of Chelyabinsk','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-05','Sovetsky district of Chelyabinsk','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-06','Traktorozavodsky district of Chelyabinsk','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-07','Centralny district of Chelyabinsk','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-08','Leninsky district of Magnitogorsk','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-09','Ordzhonikidzevsky district of Magnitogorsk','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-10','Pravoberezhny district of Magnitogorsk','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-12','Verhnij Ufalej','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-13','Emanzhelinsky Area / incl. Emanzhelinsk','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-14','Zlatoust','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-15','Karabash','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-19','Kopeysk','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-20','Korkinsky Area / incl. Korkino','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-21','Kyshtym','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-22','Miass','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-23','Plastovsky Area / incl. Plast','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-25','Troitsk','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-26','Ust-Katav','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-27','Tchebarkul','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-28','Yuzhnouralsk','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-29','Zato Snezhinsk','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-30','Zato Trehgorny','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-31','Zato Ozersk','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-32','Agapovsky Area','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-33','Argajashsky Area','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-34','Ashinsky Area / incl. Asha, Minyar, Sim','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-35','Bredinsky Area','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-36','Varnensky Area','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-37','Verkhneuralsky Area / incl. Verhneuralsk','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-38','Etkulsky Area','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-39','Kartalinsky Area / incl. Kartaly','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-40','Kaslinsky Area / incl. Kasli','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-41','Katav-Ivanovskiy Area / incl. Katav-Ivanovsk, Yuryuzan','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-42','Kizilsky Area','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-43','Krasnoarmeisky Area','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-44','Kunashaksky Area','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-45','Kusinsky Area / incl. Kusa','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-46','Nagajbaksky Area','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-47','Nyazepetrovsky Area / incl. Nyazepetrovsk','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-48','Oktyabrsky Area','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-49','Satkinsky Area / incl. Satka, Bakal','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-50','Sosnovsky Area','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-51','Troitsk Area','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-52','Uvelsky Area','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-53','Uysky Area','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-54','Chebarkulsky Area','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-55','Chesmensky Area','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CB-56','Locomotivny','CB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CK-01','Anadyr','CK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CK-02','Anadyrsky Area','CK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CK-04','Bilibinsky Area / incl. Bilibino','CK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CK-05','Iultinsky Area','CK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CK-06','Providensky Area','CK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CK-07','Chaunsky Area / incl. Pevek','CK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'CK-08','Chukotsky Area','CK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'EA-01','Birobidzhan','EA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'EA-02','Birobidzhansky Area','EA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'EA-03','Leninsky Area','EA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'EA-04','Obluchensky Area / incl. Obluche','EA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'EA-05','Oktyabrsky Area','EA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'EA-06','Smidovichsky Area','EA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'GA-01','Gorno-Altaysk','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'GA-02','Kosh-Agachskiy Area','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'GA-03','Mayminsky Area','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'GA-04','Ongudaysky Area','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'GA-05','Turochaksky Area','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'GA-06','Ulagansky Area','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'GA-07','Ust-Kansky Area','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'GA-08','Ust-Koksinskiy Area','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'GA-09','Chemalsky Area','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'GA-10','Choysky Area','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'GA-11','Shebalinsky Area','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HA-01','Abakan','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HA-02','Sayanogorsk','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HA-03','Chernogorsk','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HA-04','Altaysky Area','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HA-05','Askizsky Area','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HA-06','Biysky Area','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HA-07','Bogradsky Area','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HA-08','Ordzhonikidzevsky Area','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HA-09','Tashtypsky Area','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HA-10','Ust-Abakanskiy Area','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HA-11','Shirinsky Area','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HA-12','Abaza','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HA-13','Sorsk','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HK-01','Zheleznodorozhny district of Khabarovsk','HK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HK-02','Industrialny district of Khabarovsk','HK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HK-03','Kirovsky district of Khabarovsk','HK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HK-04','Krasnoflotsky district of Khabarovsk','HK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HK-05','Centralny district of Khabarovsk','HK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HK-06','Leninsky district of Komsomolsk-on-Amur','HK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HK-07','Centralny district of Komsomolsk-on-Amur','HK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HK-08','Amursk','HK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HK-09','Bikin','HK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HK-10','Nikolaevsk-na-Amure','HK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HK-11','Sovietskaya Gavan','HK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HK-12','Amursky Area','HK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HK-13','Ayano-Mayskiy Area','HK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HK-14','Bikinsky Area','HK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HK-15','Vaninsky Area','HK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HK-16','Verhnebureinsky Area','HK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HK-17','Vyazemsky Area / incl. Vyazemsky','HK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HK-18','Komsomolsky Area','HK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HK-19','Lazo Area','HK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HK-20','Nanajsky Area','HK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HK-21','Nikolaevsky Area','HK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HK-22','Okhotsky Area / incl. Okhotck','HK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HK-23','Poliny Osipenko Area','HK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HK-24','Sovetsko-Havansky Area','HK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HK-25','Solnechny Area','HK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HK-26','Tuguro-Chumikansky Area','HK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HK-27','Ulchsky Area','HK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HK-28','Khabarovsky Area','HK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HM-01','Hanty-Mansijsk','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HM-02','Beloyarsky','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HM-03','Kogalym','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HM-04','Langepas','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HM-05','Megion','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HM-06','Nefteyugansk','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HM-07','Nizhnevartovsk','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HM-08','Nyagan','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HM-09','Pokachi','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HM-10','Pyt-Yakh','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HM-11','Raduzhny','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HM-12','Surgut','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HM-13','Uraj','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HM-14','Yugorsk','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HM-15','Beloyarsky Area / incl. Beloyarsk','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HM-16','Berezovsky Area','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HM-17','Kondinsky Area','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HM-18','Nefteyugansky Area','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HM-19','Nizhnevartovsk Area','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HM-20','Oktyabrsky Area','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HM-21','Sovetsky Area  / incl. Sovietsky','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HM-22','Surgutsky Area / incl. Lyantor','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'HM-23','Khanty-Mansijsky Area','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-01','Leninsky District of Irkutsk','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-02','Oktiabrsky District of Irkutsk','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-03','Pravoberezhny District of Irkutsk','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-04','Sverdlovsky District of Irkutsk','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-05','Padunsky District of Bratsk','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-06','Centralny District of Bratsk','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-09','Zima','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-11','Sayansk','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-13','Tulun','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-14','Usolye-Sibirskoye','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-15','Ust-Ilimsk','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-17','Cheremhovo','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-19','Zalarinsky Area','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-20','Angarsky Area / incl. Angarsk','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-21','Balagansky Area','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-22','Bodaybinsky Area / incl. Bodaibo','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-23','Bratsky Area / incl. Vihorevka','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-24','Zhigalovsky Area','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-25','Ziminsky Area','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-26','Irkutsky Area','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-27','Kazachinsko-Lensky Area','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-28','Katangsky Area','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-29','Kachugsky Area','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-30','Kirensky Area / incl. Kirensk','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-31','Kuytunsky Area','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-32','Mamsko-Chujsky Area','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-33','Nizhneilimsky Area / incl. Zheleznogorsk-Ilimskiy','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-34','Nizhneudinsk Area / incl. Nizhneudinsk, Alzamay','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-35','Olkhonsky Area','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-36','Slyudyansky Area / incl. Slyudyanka, Baikalsk','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-37','Tayshetsky Area / incl. Tayshet, Biryusinsk','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-38','Tulunsky Area','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-39','Usolsky Area','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-40','Ust-Ilimsky Area','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-41','Ust-Kutskiy Area / incl. Ust-Kut','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-42','Ust-Udinskiy Area','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-43','Cheremhovsky Area','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-44','Chunsky Area','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-45','Shelekhovsky Area / incl. Shelehov','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-46','Pravoberezhny District of Bratsk','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-47','Svirsk','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-48','Alarsky Area','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-49','Bayandayevsky Area','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-50','Bokhansky Area','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-51','Nukutsky Area','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-52','Osinsky Area','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-53','Ehirit-Bulagatsky Area','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-54','Kirovsky Area','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'IR-55','Kujbyshevsky Area','IR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-01','Zavodskoj District of Kemerovo','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-02','Kirovsky District of Kemerovo','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-03','Leninsky District of Kemerovo','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-04','Rudnichny District of Kemerovo','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-05','Centralny District of Kemerovo','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-06','Zavodsky District of Novokuznetsk','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-07','Kuznetsky District of Novokuznetsk','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-08','Kuibyshevsky District of Novokuznetsk','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-09','Ordzhonikidzevsky District of Novokuznetsk','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-10','Centralny District of Novokuznetsk','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-11','Zenkovsky District of Prokopevsk','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-12','Rudnichny District of Prokopevsk','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-13','Centralny District of Prokopevsk','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-14','Anzhero-Sudzhensk','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-15','Belovo','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-16','Berezovsky','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-17','Gur`evsk','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-18','Kaltan','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-19','Kiselevsk','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-20','Leninsk-Kuznetskiy','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-21','Krasnobrodsky Okrug','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-22','Mezhdurechensky District','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-23','Myski','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-24','Osinniki','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-25','Taiga','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-26','Tashtagol','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-27','Polysaevo','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-28','Yurga','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-29','Belovsky Area','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-30','Guryevsky Area / incl. Gurevsk, Salair','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-31','Izhmorsky Area','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-32','Kemerovsky Area','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-33','Krapivinsky Area','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-34','Leninsk-Kuznetsky Area','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-35','Mariinsky Area','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-36','Mezhdurechensky Area','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-37','Novokuznetsky Area','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-38','Prokopyevsky Area','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-39','Promyshlennovsky Area','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-40','Tashtagolsky Area / incl. G.Tashtagol','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-41','Tisulsky Area','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-42','Topkinsky Area','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-43','Tjazhinsky Area','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-44','Chebulinsky Area','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-45','Yurginsky Area','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-46','Jajsky Area','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-47','Yashkinsky Area','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-48','Novoilyinsky District of Novokuznetsk','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-49','Lesnaja Polyana of Kemerovo','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-50','Kedrovka and Promyshlennovsky District of Kemerovo','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-51','Yagunovskiy and Pioner District Kemerovo','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-52','Mariinsk','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KE-53','Topki','KE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-01','Zheleznodorozhny District of Krasnoyarsk','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-02','Kirovsky District of Krasnoyarsk','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-03','Leninsky District of Krasnoyarsk','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-04','Oktyabrsky District of Krasnoyarsk','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-05','Sverdlovsky District of Krasnoyarsk','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-06','Sovetsky District of Krasnoyarsk','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-07','Centralny District of Krasnoyarsk','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-08','Achinsk','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-09','Bogotol','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-10','Borodino','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-11','Divnogorsk','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-12','Yeniseisk','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-15','Kansk','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-16','Lesosibirsk','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-17','Minusinsk','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-18','Nazarovo','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-19','Sosnovoborsk','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-20','Sharypovo','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-21','Zato Zheleznogorsk','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-22','Zato Zelenogorsk','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-23','Abansky Area','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-24','Achinsky Area','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-25','Balakhtinsky Area','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-26','Berezovsky Area','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-27','Biriljussky Area','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-28','Bogotolsky Area','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-29','Boguchansky Area','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-30','Bolshemurtinsky Area','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-31','Bolsheuluysky Area','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-32','Dzerzhinsky Area','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-33','Emelyanovsky Area','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-34','Yeniseisky Area','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-35','Yermakovsky Area','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-36','Idrinsky Area','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-37','Ilansky Area / incl. Ilanskiy','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-38','Irbeysky Area','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-39','Kazachinsky Area','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-40','Kansky Area','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-41','Karatuzsky Area','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-42','Kezhemsky Area / incl. Kodinskiy','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-43','Kozulsky Area','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-44','Krasnoturansky Area','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-45','Kuraginsky Area / incl. Artemivsk','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-46','Mansky Area','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-47','Minusinsky Area','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-48','Motyginsky Area','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-49','Nazarovsky Area','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-50','Nizhneingashsky Area','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-51','Novoselovsky Area','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-52','Partizansky Area','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-53','Pirovsky Area','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-54','Rybinsky Area / incl. Zaozyorny','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-55','Sayansky Area','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-56','Severo-Yeniseisky Area','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-57','Suhobuzimsky Area','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-58','Taseevsky Area','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-59','Turukhansky Area / incl. Igarka','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-60','Tyukhtetsky Area','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-61','Uzhursky Area / incl. Uzhur','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-62','Uyarsky Area / incl. Uyar','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-63','Sharypovsky Area','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-64','Shushensky Area','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-65','Tajmysky Area','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-66','Norilsk','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-67','Evenkijsky Area','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-68','Zato Solnechny','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-69','Kedrovy','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-70','Kajerkan Area','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KK-71','Talnakh Area','KK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KN-01','Kurgan','KN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KN-02','Shadrinsk','KN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KN-03','Almenevsky Area','KN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KN-04','Belozersky Area','KN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KN-05','Vargashinsky Area','KN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KN-06','Dalmatovsky Area / incl. Dalmatovo','KN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KN-07','Zverinogolovsky Area','KN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KN-08','Kargapol''Sky Area','KN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KN-09','Kataysky Area / incl. Kataysk','KN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KN-10','Ketovsky Area','KN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KN-11','Kurtamyshsky Area / incl. Kurtamysh','KN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KN-12','Lebyazhyevsky Area','KN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KN-13','Makushinsky Area / incl. Makushino','KN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KN-14','Mishkinsky Area','KN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KN-15','Mokrousovsky Area','KN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KN-16','Petukhovsky Area / incl. Petuhovo','KN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KN-17','Polovinsky Area','KN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KN-18','Pritobolny Area','KN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KN-19','Safakulevsky Area','KN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KN-20','Tselinny Area','KN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KN-21','Chastoozersky Area','KN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KN-22','Shatrovsky Area','KN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KN-23','Shumikhinsky Area / incl. Sensation','KN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KN-24','Shchuchansky Area / incl. Pike','KN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KN-25','Yurgamyshsky Area','KN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KN-26','Shadrinsky Area','KN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KO-01','Syktyvkar','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KO-02','Vorkuta','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KO-03','Vuktylsky Area / incl. Vuktyl','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KO-04','Inta','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KO-05','Pechorsky Area / incl. Pechora','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KO-06','Sosnogorsky Area / incl. Sosnogorsk and suburbs','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KO-07','Usinsk / incl. suburb','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KO-08','Ukhta','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KO-09','Izhemsky Area','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KO-10','Knyazhpogostsky Area / incl. Emba','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KO-11','Kojgorodsky Area','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KO-12','Kortkerossky Area','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KO-13','Priluzsky Area','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KO-14','Syktyvdinsky Area','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KO-15','Sysolsky Area','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KO-16','Troitsko-Pechorsky Area','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KO-17','Udorsky Area','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KO-18','Ust-Vymskiy Area / incl. Mikun','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KO-19','Ust-Kulomskiy Area','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KO-20','Ust-Tsilemskiy Area','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KO-21','Ezhvinsky Syktyvkar','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KT-01','Petropavlovsk-Kamchatsky','KT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KT-02','Elizovo','KT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KT-03','Zato Vilyuchinsk','KT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KT-04','Aleutsky Area','KT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KT-05','Bystrinsky Area','KT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KT-06','Elizovsky Area / incl. Elizovo','KT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KT-07','Milkovsky Area','KT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KT-08','Sobolevsky Area','KT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KT-09','Ust-Bolsheretskiy Area','KT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KT-10','Ust-Kamchatskiy Area / incl. Kljuchi','KT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KT-11','Karaginsky Area','KT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KT-12','Oljutorsky Area','KT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KT-13','Penzhinsky Area','KT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KT-14','Tigilsky Area','KT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'KT-15','Palana','KT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'MG-01','Magadan','MG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'MG-02','Olsky Area','MG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'MG-03','Omsukchansky Area','MG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'MG-04','Severo-Evensky Area','MG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'MG-05','Srednekansky Area','MG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'MG-06','Susuman Area / incl. Susuman','MG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'MG-07','Tenkinsky Area','MG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'MG-08','Hasynsky Area','MG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'MG-09','Yagodninsky Area','MG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-01','Dzerzhinsky District of Novosibirsk','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-02','Zheleznodorozhny District of Novosibirsk','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-03','Zayeltsovsky District of Novosibirsk','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-04','Kalininsky District of Novosibirsk','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-05','Kirovsky District of Novosibirsk','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-06','Leninsky District of Novosibirsk','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-07','Oktyabrsky District of Novosibirsk','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-08','Pervomajsky District of Novosibirsk','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-09','Sovetsky District of Novosibirsk','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-10','Centralny District of Novosibirsk','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-11','Bagansky Area','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-12','Barabinsk Area / incl. Barabinsk','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-13','Bolotninsky Area / incl. Bolotnoe','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-14','Vengerovsky Area','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-15','Dovolensky Area','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-16','Zdvinsky Area','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-17','Iskitimsky Area','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-18','Karasuksky Area / incl. Karasuk','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-19','Kargatsky Area / incl. Kargat','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-20','Kolyvansky Area','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-21','Kochenevsky Area','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-22','Kochkovsky Area','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-23','Krasnozersky Area','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-24','Kuibyshevsky Area / incl. Kuibyshev','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-25','Kupinsky Area Including Kupino','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-26','Kyshtovsky Area','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-27','Maslyaninsky Area','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-28','Moszkowski Area','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-29','Novosibirsky Area','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-30','Ordynsky Area','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-31','Severny Area','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-32','Suzunsky Area','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-33','Tatarsky Area / incl. Tatarsk','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-34','Toguchinsky Area / incl. Toguchin','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-35','Ubinsky Area','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-36','Ust-Tarkskiy Area','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-37','Chanovsky Area','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-38','Cherepanovsky Area / incl. Cherepanovo','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-39','Chistoozerny Area','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-40','Chulym Area / incl. Chulym','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-41','Barabinsk','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-42','Berdsk','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-43','Iskitim','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-44','Kujbyshev','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-45','Ob','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-46','Tatarsk','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'NS-47','Koltsovo','NS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-01','Dzerzhinsky District of Orenburg ','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-02','Leninsky District of Orenburg','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-03','Industrialny District of Orenburg','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-04','Centralny District of Orenburg','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-05','Leninsky District of Orsk','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-06','Oktyabrsky District of Orsk','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-07','Sovetsky District of Orsk','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-08','Abdulino','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-09','Buguruslan','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-10','Buzuluk','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-11','Guy','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-12','Kuvandyk','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-13','Mednogorsk','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-14','Novotroitsk','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-15','Sorochinsk','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-16','Yasny','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-17','Abdulinsky Area / incl. Abdulino','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-18','Adamovsky Area','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-19','Akbulaksky Area','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-20','Aleksandrovsky Area','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-21','Asekeevsky Area','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-22','Beljaevsky Area','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-23','Buguruslansky Area','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-24','Buzuluksky Area','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-25','Gaysky Area','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-26','Grachevsky Area','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-27','Dombarovsky Area','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-28','Ileksky Area','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-29','Kvarkensky Area','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-30','Krasnogvardeisky Area','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-31','Kuvandyksky Area / incl. Kuvandyk','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-32','Kurmanayevsky Area','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-33','Matveevsky Area','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-34','Novoorsky Area','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-35','Novosergievsky Area','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-36','Oktyabrsky Area','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-37','Orenburgsky Area','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-38','Pervomajsky Area','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-39','Perevolotsky Area','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-40','Ponomarevsky Area','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-41','Sakmarsky Area','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-42','Saraktashsky Area','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-43','Svetlinsky Area','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-44','Severny Area','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-45','Sol-Iletskiy Area / incl. Sol-Iletsk','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-46','Sorochinsky City District','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-47','Tashlinsky Area','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-48','Totsky Area','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-49','Tyulgansky Area','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-50','Sharlyksky Area','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-51','Yasnensky Area / incl. Yasny','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-52','Sol`-Iletsk','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OB-53','Zato Komarovskiy','OB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-01','Kirovsky District of Omsk','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-02','Leninsky District of Omsk','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-03','Oktiabrsky District of Omsk','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-04','Sovetsky District of Omsk','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-05','Centralny District of Omsk','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-06','Isil`Kul`','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-07','Kalachinsk','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-08','Nazyvaevsk','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-09','Tara','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-10','Tyukalinsk','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-11','Azovsky Nemetsky National Area','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-12','Bol''Sherechensky Area','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-13','Bolsheukovsky Area','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-14','Gor''Kovsky Area','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-15','Znamensky Area','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-16','Isilkulsky Area / incl. Isilkul','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-17','Kalachinsky Area / incl. Kalachinsk','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-18','Kolosovsky Area','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-19','Kormilovsky Area','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-20','Krutinsky Area','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-21','Ljubinsky Area','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-22','Mar''Janovsky Area','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-23','Moskalensky Area','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-24','Muromtsevsky Area','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-25','Nazyvayevsky Area / incl. Nazyvaevsk','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-26','Nizhneomsky Area','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-27','Novovarshavsky Area','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-28','Odessky Area','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-29','Okoneshnikovsky Area','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-30','Omsky Area','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-31','Pavlogradsky Area','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-32','Poltavsky Area','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-33','Russko-Polyansky Area','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-34','Sargatsky Area','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-35','Sedelnikovsky Area','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-36','Tavrichesky Area','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-37','Tarsky Area / incl. Tara ','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-38','Tevrizsky Area','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-39','Tyukalinsky Area / incl. Tyukalinsk','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-40','Ust-Ishimskiy Area','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-41','Cherlaksky Area','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'OM-42','Sherbakulsky Area','OM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PK-01','Leninsky District of Vladivostok','PK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PK-02','Pervomajsky District of Vladivostok','PK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PK-03','Pervorechensky District of Vladivostok','PK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PK-04','Sovetsky District of Vladivostok','PK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PK-05','Frunzensky District of Vladivostok','PK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PK-06','Arsenyev','PK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PK-07','Artem','PK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PK-08','Dalnegorsk','PK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PK-09','Dalnerechensk','PK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PK-10','Lesozavodsk','PK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PK-11','Nahodka','PK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PK-12','Partizansk','PK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PK-13','Spassk-Dalniy','PK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PK-14','Ussuriisk District','PK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PK-15','Bolshoy Kamen','PK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PK-16','Zato Fokino','PK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PK-17','Anuchinsky Area','PK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PK-18','Dalnerechensky Area','PK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PK-19','Kavalerovsky Area','PK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PK-20','Kirovsky Area','PK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PK-21','Krasnoarmeisky District','PK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PK-22','Lazovsky Area','PK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PK-24','Mikhaylovsky Area','PK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PK-25','Nadezhdinsky Area','PK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PK-26','Oktyabrsky Area','PK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PK-27','Olginsky Area','PK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PK-28','Partizansky Area','PK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PK-29','Pogranichny Area','PK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PK-30','Pozharsky Area','PK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PK-31','Spassky Area','PK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PK-32','Terneysky Area','PK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PK-34','Hankaysky Area','PK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PK-35','Hasansky Area','PK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PK-36','Horolsky Area','PK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PK-37','Chernigovsky Area','PK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PK-38','Chuguyevsky Area','PK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PK-39','Shkotovsky Area','PK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PK-40','Yakovlevsky Area','PK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-01','Dzerzhinsky District of Perm','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-02','Industrialny District of Perm','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-03','Kirovsky District of Perm','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-04','Leninsky District of Perm','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-05','Motovilikhinsky District of Perm','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-06','Ordzhonikidzevsky District of Perm','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-07','Sverdlovsky District of Perm','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-08','Aleksandrovsky Area / incl. Aleksandrovsk','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-09','Berezniki','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-10','Gremyachinsky Area / incl. Gremyachinsk','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-11','Gubakhinsky Area / incl. Gubakha','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-13','Kizelovsky Area / incl. Kizel','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-14','Krasnokamsky Area / incl. Krasnokamsk','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-15','Kungur','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-17','Solikamsk','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-20','Bardymsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-21','Berezovsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-22','Bolshesosnovsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-23','Vereshchaginsky Area / incl. Vereshchagino','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-24','Gornozavodsky Area / incl. Gornozavodsk','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-25','Elovsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-26','Ilyinsky Area / incl. Chermoz','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-27','Karagaysky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-28','Kishertsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-29','Krasnovishersky District / incl. Krasnovishersk','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-30','Kuedinsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-31','Kungursky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-32','Lysvensky Area / incl. Lysva','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-33','Nytvensky Area / incl. Nytva','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-34','Oktyabrsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-35','Ordinsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-36','Osinsky Area / incl. Osa','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-37','Ohansky Area Including Ohansk','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-38','Ochersky Area Including Ocher','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-39','Permsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-40','Sivinsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-41','Solikamsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-42','Suksunsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-43','Uinsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-44','Usolsky Area / incl. Usolye','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-45','Chastinsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-46','Cherdynsky Area / incl. Cherdyn','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-47','Chernushinsky Area / incl. Chernushka','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-48','Chusovskoy Area / incl. Chusovoy','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-49','Tchaikovsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-50','Dobryansky Area / incl. Dobryanka','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-51','Zato Zvezdny ','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-52','Kudymkar','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-53','Gaynsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-54','Kosinsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-55','Kochevsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-56','Kudymkarsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-57','Yurlinsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'PM-58','Yusvinsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SL-01','Yuzhno-Sakhalinsk','SL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SL-10','Aleksandrovsk-Sahalinskiy Area / incl. Aleksandrov-Sahalinskiy','SL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SL-11','Anivsky Area / incl. Aniva','SL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SL-12','Dolinsky Area / incl. Dolinsk','SL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SL-13','Korsakovsky Area / incl. Korsakov','SL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SL-14','Kurilsky Area / incl. Kurilsk','SL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SL-15','Makarovsky Area / incl. Makarov ','SL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SL-16','Nevelsky Area / incl. Nevelsk, Gornozavodsk','SL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SL-17','Nogliksky Area','SL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SL-18','Okhinsky Area / incl. Okha','SL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SL-19','Poronaysky Area / incl. Poronaysk, Vahrushev','SL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SL-20','Severo-Kurilsky Area / incl. Severo-Kurilsk','SL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SL-21','Tomarinsky Area / incl. Tomari, Krasnogorsk','SL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SL-22','Tymovsky Area','SL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SL-23','Uglegorsk Area / incl. Uglegorsk, Shahtersk','SL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SL-24','Holmsky Area / incl. Kholmsk, Chekhov','SL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SL-25','Juzhno-Kurilsky Area','SL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SL-26','Smirnykhovsky Area','SL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-01','Verkh-Isetsky District of Ekaterinburg','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-02','Zheleznodorozhny District of Ekaterinburg','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-03','Kirovsky District of Ekaterinburg','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-04','Leninsky District of Ekaterinburg','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-05','Oktyabrsky District of Ekaterinburg','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-06','Ordzhonikidzevsky District of Ekaterinburg','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-07','Chkalovsky District of Ekaterinburg','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-08','Krasnogorsk District of Kamensk-Uralsky','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-09','Sinarsky District of Kamensk-Uralsky','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-10','Dzerzhinsky District of Nizhni Tagil','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-11','Leninsky District of Nizhni Tagil','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-12','Tagilstroyevsky District of Nizhni Tagil','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-13','City of Alapaevsk','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-14','Artemovsky City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-15','Asbestovsky City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-16','Berezovsky City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-17','Bogdanovich City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-18','Verhnaya Pyshma City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-19','Verkhnesaldinsky City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-20','City District Zarechny','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-21','Ivdel City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-22','City of Irbit','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-23','Kamyshlovsky City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-24','Karpinsk City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-25','Kachkanar City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-26','Kirovgrad City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-27','Krasnoturyinsk City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-28','Krasnouralsk City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-29','Kushva City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-30','Nevyansk City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-31','Nizhnyaya Salda City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-32','Nizhnyaya Tura City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-33','Pervouralsk City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-34','Polevskoy City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-35','Revda City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-36','Rezhevsky City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-37','Severouralsk City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-38','Serov City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-39','Suhoj Log City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-40','Tavdinsky City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-41','Zato Lesnoj','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-42','Novouralsk City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-43','Alapaevsk City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-45','Artinsky City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-46','Achitsky City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-47','Bajkalovsky Area','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-48','Beloyarsk City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-51','Verkhotursky City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-52','Garinsky City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-53','Irbitsky Education','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-54','Kamensk City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-55','Kamyshlovsky Area','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-56','Krasnoufimsky District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-58','Nizhneserginsky Area','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-60','Gornouralsky City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-61','Pyshminsky City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-63','Servosky Area','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-64','Slobodo-Turinsky Area','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-66','Sysertsky City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-67','Taborinsky Area','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-69','Talitsky City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-70','Tugulymsky City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-71','Turinsky City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-72','Shalinsky City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-74','Krasnoufimsk City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-76','Novaya Lyalya City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-77','Zato Svobodny','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-78','Zato Ural`Sky','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-79','Aramilsky City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-80','Bisertsky City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-81','Verhnee Dubrovo City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-82','Verh-Neyvinskiy City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-83','Verhny Tagil City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-84','Verhnyaya Tura City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-85','Volchansky City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-86','Degtyarsk City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-87','Malyshevsky City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-88','Makhnevsky Municipality','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-89','Pelym City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-90','Reftinsky City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-91','Sosvinsky City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-92','Sredneuralsk City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-93','Staroutkinsk City District','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'SV-94','Academic District Of Ekaterinburg','SV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TN-01','Kalininsky District of Tyumen','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TN-02','Leninsky District of Tyumen','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TN-03','Centralny District of Tyumen','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TN-04','Zavodoukovsky City District','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TN-05','Ishim','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TN-06','Tobolsk','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TN-07','Yalutorovsk','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TN-08','Abatsky Area','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TN-09','Armizonsky Area','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TN-10','Aromashevsky Area','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TN-11','Berdjuzhsky Area','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TN-12','Vagaysky Area','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TN-13','Vikulovsky Area','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TN-14','Golyshmanovsky Area','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TN-15','Zavodoukovsky Area','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TN-16','Isetsky Area','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TN-17','Ishimsky Area','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TN-18','Kazansky Area','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TN-19','Nizhnetavdinsky Area','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TN-20','Omutinsky Area','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TN-21','Sladkovsky Area','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TN-22','Sorokinsky Area','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TN-23','Tobolsky Area','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TN-24','Tyumensky Area','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TN-25','Uvatsky Area','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TN-26','Uporovsky Area','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TN-27','Yurginsky Area','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TN-28','Yalutorovsky Area','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TN-29','Yarkovsky Area','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TN-30','East District of Tyumen','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TO-01','Kirovsky District of Tomsk','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TO-02','Leninsky District of Tomsk','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TO-03','Oktyabrsky District of Tomsk','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TO-04','Sovetsky District of Tomsk','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TO-07','Kedrovy','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TO-08','Strezhevoj','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TO-09','Seversk','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TO-10','Aleksandrovsky Area','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TO-11','Asinovsky Area / incl. Asino','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TO-12','Bakcharsky Area','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TO-13','Verhneketsky Area','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TO-14','Zyrjansky Area','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TO-15','Kargasoksky Area','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TO-16','Kozhevnikovsky Area','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TO-17','Kolpashevsky Area / incl. Kolpashevo','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TO-18','Krivosheinsky Area','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TO-19','Molchanovsky Area','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TO-20','Parabel''Sky Area','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TO-21','Pervomajsky Area','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TO-22','Tegul''Detsky Area','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TO-23','Tomsky Area','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TO-24','Chainsky Area','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TO-25','Shegarsky Area','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TU-01','Kyzyl','TU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TU-02','Ak-Dovurak','TU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TU-03','Bay-Tayginskiy Kozhuun','TU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TU-04','Barun-Hemchiksky Kozhuun','TU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TU-05','Dzun-Hemchiksky Kozhuun / incl. Chadan','TU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TU-06','Kaa-Hemsky Kozhuun','TU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TU-07','Kyzylsky Kozhuun','TU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TU-08','Mongun-Tayginsky Kozhuun','TU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TU-09','Ovyursky Kozhuun','TU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TU-10','Piy-Hemskiy Kozhuun / incl. Turan','TU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TU-11','Sut-Holskiy Kozhuun','TU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TU-12','Tandinsky Kozhuun','TU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TU-13','Tes-Hemsky Kozhuun','TU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TU-14','Todzhinsky Kozhuun','TU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TU-15','Ulug-Hemsky Area Including Shagonar','TU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TU-16','Ulug- Hemsky Kozhuun / incl. Shagonar','TU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TU-17','Chedi- Holsky Kozhuun','TU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TU-18','Erzinsky Kozhuun','TU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'TU-19','Tere - Holsky Kozhuun','TU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YA-01','Yakutsk','YA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YA-06','Abyjsky Area','YA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YA-07','Aldansky Area / incl. Aldan, Tommot','YA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YA-08','Allaihovsky Area','YA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YA-09','Amginsky Area','YA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YA-10','Anabarsky National Area','YA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YA-11','Bulunsky Area','YA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YA-12','Verhneviljujsky Area','YA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YA-13','Verhnekolymsky Area','YA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YA-14','Verkhoyansky Area / incl. Verkhoyansk','YA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YA-15','Vilyuysky Area / incl. Vilyuysk','YA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YA-16','Gorny Area','YA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YA-17','Zhigansky Area','YA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YA-18','Kobyaysky Area','YA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YA-19','Lensky Area / incl. Lensk','YA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YA-20','Megino-Kangalassky Area','YA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YA-21','Mirninsky Area / incl. Mirny, Udachny','YA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YA-22','Momsky National Area','YA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YA-23','Namsky Area','YA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YA-24','Nizhnekolymsky Area','YA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YA-25','Nyurbinsky Area / incl. Nyurba','YA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YA-26','Ojmjakonsky Area','YA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YA-27','Olekminsky Area / incl. Olekminsk','YA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YA-28','Oleneksky National Area','YA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YA-29','Srednekolymsky Area / incl. Srednekolymsk','YA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YA-30','Suntarsky Area','YA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YA-31','Tattinsky Area','YA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YA-32','Tomponsky Area','YA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YA-33','Ust''-Aldansky Area','YA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YA-34','Ust''-Majsky Area','YA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YA-35','Ust''-Jansky Area','YA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YA-36','Hangalassky Area / incl. Pokrovsk','YA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YA-37','Churapchinsky Area','YA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YA-38','Eveno-Bytantajsky National Area','YA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YA-39','Neryungrinsky Area / incl. Neryungri','YA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YA-40','Zhataj','YA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YN-01','Salekhard','YN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YN-02','Gubkinsky','YN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YN-03','Labytnangi','YN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YN-04','Muravlenko','YN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YN-05','Nadym','YN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YN-06','Novyj Urengoy','YN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YN-07','Noyabrsk','YN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YN-08','Krasnoselkupsky Area','YN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YN-09','Nadymsky Area / incl. Nadym ','YN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YN-10','Priural''Sky Area','YN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YN-11','Purovsky Area','YN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YN-12','Tazovsky District','YN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YN-13','Shuryshkarsky Area','YN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'YN-14','Yamalsky Area','YN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'ZK-01','Zheleznodorozhny District of Chita','ZK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'ZK-02','Ingodinsky District of Chita','ZK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'ZK-03','Centralny District of Chita','ZK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'ZK-04','Chernovsky District of Chita','ZK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'ZK-05','Balej','ZK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'ZK-06','Borzja','ZK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'ZK-07','Krasnokamensk','ZK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'ZK-08','Petrovsk-Zabajkalsky','ZK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'ZK-09','Akshinsky Area','ZK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'ZK-10','Aleksandrovo-Zavodsky Area','ZK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'ZK-11','Balejsky Area / incl. Baley','ZK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'ZK-12','Borzinsky Area / incl. Borzya','ZK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'ZK-13','Gazimuro-Zavodsky Area','ZK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'ZK-14','Transbaikalsky Area','ZK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'ZK-15','Kalarsky Area','ZK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'ZK-16','Kalgansky Area','ZK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'ZK-17','Karymsky Area','ZK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'ZK-18','Krasnokamensky Area / incl. Krasnokamensk','ZK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'ZK-19','Krasnochikoysky Area','ZK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'ZK-20','Kyrinsky Area','ZK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'ZK-21','Mogochinsky Area / incl. Mogocha','ZK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'ZK-22','Nerchinsky Area / incl. Nerchinsk ','ZK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'ZK-23','Nerchinsko-Zavodsky Area','ZK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'ZK-24','Olovyanninsky Area','ZK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'ZK-25','Ononsky Area','ZK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'ZK-26','Petrovsk-Zabaykalsky Area','ZK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'ZK-27','Priargunsky Area','ZK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'ZK-28','Sretensky Area / incl. Sretensk','ZK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'ZK-29','Tungiro-Olekminsky Area','ZK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'ZK-30','Uletovsky Area','ZK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'ZK-31','Hiloksky Area / incl. Hilok','ZK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'ZK-32','Chernyshevsky Area','ZK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'ZK-33','Chitinsky Area','ZK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'ZK-34','Shelopuginsky Area','ZK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'ZK-35','Shilkinsky Area / incl. Shilka','ZK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'ZK-36','Tungokochensky Area','ZK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'ZK-37','Aginsky Area','ZK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'ZK-38','Duldurginsky Area','ZK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'ZK-39','Mogojtujsky Area','ZK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'ZK-40','Aginskoe','ZK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (15,'ZK-41','Zato Poselok Gorny','ZK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07001','Alaró','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07002','Alaior','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07003','Alcúdia','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07004','Algaida','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07005','Andratx','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07006','Artà','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07007','Banyalbufar','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07008','Binissalem','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07009','Búger','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07010','Bunyola','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07011','Calvià','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07012','Campanet','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07013','Campos','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07014','Capdepera','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07015','Ciutadella de Menorca','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07016','Consell','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07017','Costitx','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07018','Deiá','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07019','Escorca','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07020','Esporles','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07021','Estellencs','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07022','Felanitx','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07023','Ferreries','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07024','Formentera','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07025','Fornalutx','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07026','Eivissa','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07027','Inca','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07028','Lloret de Vistalegre','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07029','Lloseta','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07030','Llubí','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07031','Llucmajor','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07032','Maó-Mahón','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07033','Manacor','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07034','Mancor de la Vall','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07035','Maria de la Salut','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07036','Marratxí','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07037','Mercadal (Es)','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07038','Montuïri','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07039','Muro','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07040','Palma','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07041','Petra','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07042','Pollença','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07043','Porreres','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07044','Pobla (Sa)','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07045','Puigpunyent','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07046','Sant Antoni de Portmany','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07047','Sencelles','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07048','Sant Josep de sa Talaia','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07049','Sant Joan','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07050','Sant Joan de Labritja','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07051','Sant Llorenç des Cardassar','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07052','Sant Lluís','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07053','Santa Eugènia','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07054','Santa Eulària des Riu','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07055','Santa Margalida','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07056','Santa María del Camí','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07057','Santanyí','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07058','Selva','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07059','Salines (Ses)','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07060','Sineu','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07061','Sóller','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07062','Son Servera','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07063','Valldemossa','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07064','Castell (Es)','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07065','Vilafranca de Bonany','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07901','Ariany','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (21,'07902','Migjorn Gran (Es)','IB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'35001','Agaete','GC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'35002','Agüimes','GC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'35003','Antigua','GC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'35004','Arrecife','GC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'35005','Artenara','GC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'35006','Arucas','GC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'35007','Betancuria','GC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'35008','Firgas','GC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'35009','Gáldar','GC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'35010','Haría','GC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'35011','Ingenio','GC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'35012','Mogán','GC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'35013','Moya','GC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'35014','Oliva (La)','GC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'35015','Pájara','GC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'35016','Palmas de Gran Canaria (Las)','GC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'35017','Puerto del Rosario','GC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'35018','San Bartolomé','GC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'35019','San Bartolomé de Tirajana','GC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'35020','Aldea de San Nicolás (La)','GC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'35021','Santa Brígida','GC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'35022','Santa Lucía de Tirajana','GC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'35023','Santa María de Guía de Gran Canaria','GC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'35024','Teguise','GC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'35025','Tejeda','GC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'35026','Telde','GC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'35027','Teror','GC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'35028','Tías','GC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'35029','Tinajo','GC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'35030','Tuineje','GC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'35031','Valsequillo de Gran Canaria','GC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'35032','Valleseco','GC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'35033','Vega de San Mateo','GC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'35034','Yaiza','GC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38001','Adeje','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38002','Agulo','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38003','Alajeró','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38004','Arafo','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38005','Arico','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38006','Arona','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38007','Barlovento','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38008','Breña Alta','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38009','Breña Baja','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38010','Buenavista del Norte','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38011','Candelaria','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38012','Fasnia','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38013','Frontera','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38014','Fuencaliente de la Palma','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38015','Garachico','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38016','Garafía','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38017','Granadilla de Abona','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38018','Guancha (La)','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38019','Guía de Isora','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38020','Güímar','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38021','Hermigua','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38022','Icod de los Vinos','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38023','San Cristóbal de La Laguna','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38024','Llanos de Aridane (Los)','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38025','Matanza de Acentejo (La)','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38026','Orotava (La)','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38027','Paso (El)','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38028','Puerto de la Cruz','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38029','Puntagorda','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38030','Puntallana','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38031','Realejos (Los)','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38032','Rosario (El)','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38033','San Andrés y Sauces','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38034','San Juan de la Rambla','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38035','San Miguel de Abona','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38036','San Sebastián de la Gomera','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38037','Santa Cruz de la Palma','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38038','Santa Cruz de Tenerife','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38039','Santa Úrsula','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38040','Santiago del Teide','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38041','Sauzal (El)','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38042','Silos (Los)','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38043','Tacoronte','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38044','Tanque (El)','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38045','Tazacorte','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38046','Tegueste','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38047','Tijarafe','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38048','Valverde','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38049','Valle Gran Rey','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38050','Vallehermoso','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38051','Victoria de Acentejo (La)','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38052','Vilaflor de Chasna','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38053','Villa de Mazo','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (29,'38901','Pinar de El Hierro (El)','TF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (32,'51001','Ceuta','CE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (32,'52001','Melilla','ML',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AD-01','Maikop','AD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AD-02','Adygeysk','AD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AD-03','Giaginsky Area','AD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AD-04','Koshekhablsky Area','AD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AD-05','Krasnogvardeisky district','AD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AD-06','Maikopsky Area','AD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AD-07','Takhtamukaysky Area','AD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AD-08','Teuchezhsky Area','AD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AD-09','Shovgenovsky Area','AD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AO-01','Kirovsky district of Astrakhan','AO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AO-02','Leninsky district of Astrakhan','AO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AO-03','Sovetsky district of Astrakhan','AO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AO-04','Trusovsky district of Astrakhan','AO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AO-06','Zato Znamensk','AO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AO-07','Akhtubinsky Area / incl. Akhtubinsk','AO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AO-08','Volodarsky Area','AO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AO-09','Enotayevsky Area','AO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AO-10','Ikryaninsky Area','AO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AO-11','Kamyzyaksky Area / incl. Kamyzyak','AO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AO-12','Krasnoyarsk Area','AO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AO-13','Limansky Area','AO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AO-14','Narimanovsky Area / incl. Narimanov','AO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AO-15','Privolzhsky Area','AO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AO-16','Harabalinsky Area / incl. Harabali','AO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AO-17','Chernoyarsky Area','AO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AR-01','Isakogorsky district of Arkhangelsk','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AR-02','Lomonosov District of Arkhangelsk','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AR-03','Oktiabrsky District of Arkhangels','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AR-04','Solombalsky District of Arkhangelsk','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AR-05','Koryazhma','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AR-06','Kotlas','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AR-07','Novodvinsk','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AR-08','Onega','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AR-09','Severodvinsk','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AR-10','Zato Mirny','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AR-11','Velsky Area / incl. Velsk','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AR-12','Verhnetoemsky Area','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AR-13','Vilegodsky Area','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AR-14','Vinogradovsky Area','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AR-15','Kargopolsky Area / incl. Kargopol','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AR-16','Konoshsky Area','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AR-17','Kotlassky Area / incl. Solvychegodsk','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AR-18','Krasnoborsky Area','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AR-19','Lensky Area','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AR-20','Leshukonsky Area','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AR-21','Mezensky Area / incl. Mezen','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AR-22','Nyandomsky Area / incl. Nyandoma','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AR-23','Onezhsky Area / incl. Onega','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AR-24','Pinezhsky Area','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AR-25','Plesetsky Area','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AR-26','Primorsky Area','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AR-27','Solovetsky Area','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AR-28','Ustyansky Area','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AR-29','Holmogorsky Area','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AR-30','Shenkursky Area / incl. Shenkursk','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AR-31','Novaya Zemlya','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AR-32','Franz Joseph Land','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AR-33','Varavino-Factoriya District of Arkhangelsk','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AR-34','Maymaksansky District of Arkhangelsk','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AR-35','Majskaya Gorka District of Arkhangelsk','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AR-36','Severny District of Arkhangelsk ','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'AR-37','Tsiglomensky District of Arkhangelsk ','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BO-01','Vostochny District of Belgorod','BO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BO-02','Zapadny District of Belgorod','BO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BO-05','Gubkinsky District','BO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BO-06','Stary Oskol District','BO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BO-08','Alexeyevsky District / incl. Alekseyevka','BO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BO-09','Belgorodsky Area','BO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BO-10','Borisovsky Area','BO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BO-11','Valuysky Area / incl. Valuyki','BO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BO-12','Veydelevsky Area','BO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BO-13','Volokonovsky Area','BO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BO-14','Grayvoronsky Area / incl. Grayvoron','BO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BO-16','Ivnyansky Area','BO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BO-17','Korochansky Area / incl. Korocha','BO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BO-18','Krasnensky Area','BO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BO-19','Krasnogvardeisky District','BO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BO-20','Krasnoyaruzhsky Area','BO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BO-21','Novooskolsky Area / incl. New Oskol','BO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BO-22','Prohorov''s Area','BO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BO-23','Rakityansky Area','BO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BO-24','Rovensky Area','BO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BO-26','Chernyansky Area','BO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BO-27','Shebekinsky Area / incl. Shebekino','BO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BO-28','Yakovlevsky Area','BO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BO-29','Alekseevka','BO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BO-30','Valuiki','BO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BO-31','Gubkin','BO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BO-32','Stary Oskol','BO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BR-01','Bezhitsky District of Bryansk','BR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BR-02','Volodarsky District of Bryansk','BR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BR-03','Sovetsky District of Bryansk','BR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BR-04','Fokinsky District of Bryansk','BR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BR-06','Klintsy','BR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BR-07','Novozybkov','BR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BR-08','Seltso','BR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BR-09','Brasovsky Area','BR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BR-10','Bryansky Area','BR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BR-11','Vygonichsky Area','BR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BR-12','Gordeevsky Area','BR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BR-13','Dubrovsky Area','BR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BR-14','Dyatkovsky Area / incl. Dyatkovo','BR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BR-15','Zhiryatinsky Area','BR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BR-16','Zhukovsky Area / incl. Zhukovka','BR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BR-17','Zlynkovsky Area / incl. Zlynka','BR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BR-18','Karachevsky Area / incl. Karachev','BR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BR-19','Kletnyansky Area','BR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BR-20','Klimovsky Area','BR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BR-21','Klintsovsky Area','BR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BR-22','Komarichsky Area','BR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BR-23','Krasnogorsky Area','BR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BR-24','Mglinsky Area / incl. Mglin','BR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BR-25','Navlinsky Area','BR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BR-26','Novozybkovsky Area','BR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BR-27','Pogarsky Area','BR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BR-28','Pochepsky Area / incl. Pochep','BR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BR-29','Rognedinsky Area','BR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BR-30','Sevsky Area / incl. Cebck','BR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BR-31','Starodubsky Area','BR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BR-32','Suzemsky Area','BR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BR-33','Surazhsky Area / incl. Surazh','BR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BR-34','Trubchevsky Area / incl. Trubchevsk','BR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BR-35','Unechsky Area / incl. Unecha','BR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BR-36','Starodub','BR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'BR-37','Fokino','BR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CN-01','Zavodskoy district of Grozny','CN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CN-02','Leninsky district of Grozny','CN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CN-03','Oktyabrsky district of Grozny','CN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CN-04','Staropromyslovsky Area of Grozny','CN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CN-06','Argun','CN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CN-07','Gudermes','CN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CN-10','Sunzhensky Area','CN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CN-11','Achhoy-Martanovskiy Area','CN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CN-12','Vedensky Area','CN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CN-13','Groznensky Area','CN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CN-14','Gudermessky Area / incl. Gudermes','CN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CN-15','Itum-Kalinskiy Area','CN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CN-16','Kurchaloyevsky Area','CN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CN-17','Nadterechny Area','CN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CN-18','Naursky Area','CN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CN-19','Nozhay-Yurtovskiy Area','CN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CN-20','Urus-Martanovskiy Area / incl. Urus-Martan','CN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CN-21','Shatoysky Area','CN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CN-22','Shalinsky Area / incl. Shawls','CN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CN-23','Sharoysky Area','CN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CN-24','Shchelkovskoj Area','CN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CU-01','Kalininsky district Cheboksary','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CU-02','Leninsky district Cheboksary','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CU-03','Moskovsky district Cheboksary','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CU-04','Alatyr','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CU-05','Kanash','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CU-06','Novocheboksarsk','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CU-07','Shumerlya','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CU-08','Alatyrsky Area','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CU-09','Alikovsky Area','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CU-10','Batyrevsky Area','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CU-11','Vurnarsky Area','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CU-12','Ibresinsky Area','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CU-13','Kanashsky Area','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CU-14','Kozlowski Area / incl. Kozlovka','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CU-15','Komsomolsky Area','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CU-16','Krasnoarmeisky district','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CU-17','Krasnochetaysky Area','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CU-18','Mariinsko-Posadsky Area / incl. Mariinsky Posad','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CU-19','Morgaushsky Area','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CU-20','Poretsky Area','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CU-21','Urmarsky Area','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CU-22','Tsivilsky Area / incl. Tsivilsk','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CU-23','Cheboksary Area','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CU-24','Shemurshinsky Area','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CU-25','Shumerlinsky Area','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CU-26','Yadrinsky Area / incl. Yadrin','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CU-27','Yalchiksky Area','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'CU-28','Yantikovsky Area','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-01','Kirovsky district of Makhachkala','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-02','Leninsky district of Makhachkala','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-03','Sovetsky district of Makhachkala','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-04','Buinaksk','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-05','Dagestanskie Ogni','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-06','Derbent','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-07','Izberbash','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-08','Kaspiysk','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-09','Kizilyurt','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-10','Kizlyar','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-11','Khasavyurt','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-12','Juzhno-Suhokumsk','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-13','Agulsky Area','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-14','Akushinsky Area','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-15','Akhvakhsky Area','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-16','Akhtynsky Area','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-17','Babayurtovsky Area','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-18','Botlikhsky Area','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-19','Buinaksky Area','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-20','Gergebilsky Area','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-21','Gumbetovsky Area','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-22','Gunibsky Area','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-23','Dakhadayevsky Area','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-24','Derbentsky Area','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-25','Dokuzparinsky Area','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-26','Kazbekovsky Area','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-27','Kaitagsky Area','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-28','Kayakentsky Area','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-29','Kizilyurtovsky Area','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-30','Kizlyarsky Area','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-31','Kulinsky Area','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-32','Kurakhsky Area','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-33','Laksky Area','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-34','Levashinsky Area','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-35','Karabudahkentsky Area','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-36','Kumtorkalinsky Area','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-37','Magaramkentsky Area','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-38','Novolaksky Area','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-39','Nogaisky Area','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-40','Rutulsky Area','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-41','Sergokalinsky Area','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-42','Soulieyman-Stalsky Area','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-43','Tabasaransky Area','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-44','Tarumovsky Area','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-45','Tlyaratinsky Area','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-46','Untsukulsky Area','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-47','Khasavyurtovsky Area','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-48','Hivsky Area','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-49','Hunzahsky Area','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-50','Tsumadinsky Area','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-51','Tsuntinsky Area','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-52','Charodinsky Area','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'DA-53','Shamilsky Area','DA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IN-01','Magas','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IN-02','Karabulak','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IN-03','Nazran','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IN-04','Malgobek','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IN-05','Dzhayrakhsky Area','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IN-06','Malgobeksky Area','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IN-07','Nazransky Area ','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IN-08','Sunzhensky Area','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IN-09','Sunzha','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IN-10','Altievsky District of the City of Nazran','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IN-11','Gamurzievsky District of the City of Nazran','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IN-12','Nasyr-Kortsky District of the City of Nazran','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IN-13','Central District of the City of Nazran','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IV-01','Leninsky District of Ivanovo','IV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IV-02','Oktyabrsky District of Ivanovo','IV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IV-03','Sovetsky District of Ivanovo','IV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IV-04','Frunzensky District of Ivanovo','IV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IV-05','Vichuga','IV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IV-06','Kineshma','IV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IV-07','Teykovo','IV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IV-10','Verhnelandehovsky Area','IV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IV-11','Vichugsky Area','IV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IV-12','Gavrilovo-Posadskiy Area / incl. Gavrilov Posad','IV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IV-13','Zavolzhye Area / incl. Zavolzhsk','IV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IV-14','Ivanovsky Area','IV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IV-15','Ilyinsky Area','IV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IV-16','Kineshma Area / incl. Navoloki','IV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IV-17','Komsomolsky / incl. Komsomolsk','IV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IV-18','Lezhnevsky Area','IV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IV-19','Lukhsky Area','IV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IV-20','Palehsky Area','IV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IV-21','Pestyakovsky Area','IV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IV-22','Privolzhsky Area / incl. Ples, Privolzhsk','IV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IV-23','Puchezhsky Area / incl. Puchezh','IV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IV-24','Rodnikovsky Area / incl. Rodniki','IV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IV-25','Savinsky Area','IV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IV-26','Teykovsky Area','IV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IV-27','Furmanovsky Area / incl. Furmanov ','IV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IV-28','Shuysky Area','IV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IV-29','Yuzhskiy Area / incl. Yuzha','IV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IV-30','Yuryevetsky Area / incl. Yuryevets','IV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'IV-31','Kohma','IV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KB-01','Nalchik','KB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KB-02','Baksansky Area','KB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KB-03','Zolsky Area','KB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KB-04','Majsky Area / incl. Majsky','KB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KB-05','Prohladnensky Area','KB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KB-06','Tersky Area / incl. Terek','KB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KB-07','Urvansky Area / incl. Nartkala','KB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KB-08','Chegemsky Area','KB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KB-09','Chereksky Area','KB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KB-10','Elbrussky Area / incl. Tyrnyauz','KB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KB-11','Leskemsky Area','KB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KB-12','Baksan','KB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KB-13','Prokhladny','KB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KC-01','Cherkessk','KC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KC-02','Karachaevsk / incl. Teberda','KC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KC-03','Adyge-Hablskiy Area','KC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KC-04','Zelenchuksky Area','KC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KC-05','Karachaevsky Area','KC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KC-06','Malokarachaevsky Area','KC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KC-07','Prikubansky Area','KC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KC-08','Urupsky Area','KC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KC-09','Ust-Dzhegutinskiy Area / incl. Ust-Dzheguta','KC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KC-10','Habezsky Area','KC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KC-11','Abazinsky Area','KC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KC-12','Nogaisky Area','KC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KG-01','Leninsky District of Kaluga','KG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KG-02','Moskovsky District of Kaluga','KG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KG-03','Oktyabrsky District of Kaluga','KG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KG-05','Lyudinovo','KG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KG-06','Obninsk','KG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KG-07','Babyninsky Area','KG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KG-08','Barjatinsky Area','KG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KG-09','Borovsky Area / incl. Borovsk','KG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KG-10','Dzerzhinsky District / incl. Kondrovo','KG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KG-11','Duminichsky Area','KG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KG-12','Zhizdrinsky Area / incl. Zhizdra','KG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KG-13','Zhukovsky Area / incl. Bugs','KG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KG-14','Iznoskovsky Area','KG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KG-15','City of Kirov and Kirovsky District','KG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KG-16','Kozelsky Area / incl. Kozelsk, Sosenskiy','KG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KG-17','Kuibyshevsky District','KG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KG-18','Lyudinovsky Area','KG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KG-19','Maloyaroslavetsky Area / incl. Maloyaroslavets','KG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KG-20','Medynsky Area / incl. Medyn','KG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KG-21','Meshchovsky Area / incl. Meshchovsk','KG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KG-22','Mosalsky Area / incl. Mosalsk','KG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KG-23','Peremyshlsky Area','KG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KG-24','Spas-Demenskiy Area / incl. Spas-Demensk','KG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KG-25','Sukhinichsky Area / incl. Suhinichi','KG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KG-26','Tarussky Area / incl. Tarusa','KG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KG-27','Ulyanovsky Area','KG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KG-28','Ferzikovsky Area','KG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KG-29','Hvastovichsky Area','KG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KG-30','Yukhnovsky Area / incl. Yuhnov','KG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KG-31','Balabanovo','KG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-01','Leninsky District of Kirov','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-02','Novovjatsky District of Kirov','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-03','Oktyabrsky District of Kirov','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-04','Pervomajsky District of Kirov','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-05','Vjatskie Polyany','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-06','Kirovo-Chepetsk','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-07','Kotelnich','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-08','Slobodskoj','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-09','Arbazhsky Area','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-10','Afanasyevsky Area','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-11','Belokholunitsky Area / incl. Belaja Kholunitsa','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-12','Bogorodsky Area','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-13','Verkhnekamsky Area / incl. Kirs','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-14','Verhoshizhemsky Area','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-15','Vyatskopolyansky Area / incl. Sosnovka','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-16','Darovsky Area','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-17','Zuyevsky Area / incl. Zuevka','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-18','Kiknursky Area','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-19','Kilmezsky Area','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-20','Kirovo-Chepetsky Area','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-21','Kotelnichsky Area','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-22','Kumensky Area','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-23','Lebjazhsky Area','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-24','Luzsky Area / incl. Luza','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-25','Malmyzhsky Area / incl. Malmyzh','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-26','Murashinsky Area / incl. Murashi','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-27','Nagorsky Area','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-28','Nemsky Area','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-29','Nolinsky Area / incl. Nolinsk','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-30','Omutninsky Area / incl. Omutninsk','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-31','Oparinsky Area','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-32','Orichevsky Area','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-33','Orlovsky Area / incl. Orlov','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-34','Pizhansky Area','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-35','Podosinovsky Area','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-36','Sanchursky Area','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-37','Svechinsky Area','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-38','Slobodskoj Area','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-39','Sovetsky Area / incl. Sovetsk','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-40','Sunsky Area','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-41','Tuzhinsky Area','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-42','Uninsky Area','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-43','Urzhumsky Area / incl. Urzhum','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-44','Falensky Area','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-45','Shabalinsky Area','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-46','Yuryansky Area','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-47','Yaransky Area / incl. Yaransk','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KI-48','Zato Pervomajsky','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KL-01','Petrozavodsk','KL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KL-04','Kostomuksha','KL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KL-07','Sortavalsky Area / incl. Sortavala','KL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KL-08','Belomorsky Area / incl. Belomorsk','KL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KL-10','Kalevalsky National Area','KL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KL-11','Kemsky Area / incl. Kem','KL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KL-12','Kondopozhsky Area / incl. Kondopoga','KL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KL-13','Lakhdenpokhsky Area / incl. Lahdenpohya','KL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KL-14','Louhsky Area','KL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KL-15','Medvezhyegorsky Area / incl. Medvezhyegorsk','KL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KL-16','Muyezersky Area','KL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KL-17','Olonetsky Area / incl. Olonets','KL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KL-18','Pitkyarantsky Area / incl. Pitkyaranta','KL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KL-19','Prionezhsky Area','KL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KL-20','Pryazhinsky Area','KL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KL-21','Pudozhsky Area / incl. Pudozh','KL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KL-22','Segezhsky Area / incl. Segezha','KL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KL-23','Suoyarvsky Area / incl. Suoyarvi','KL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KM-01','Elista','KM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KM-02','Gorodovikovsky Area / incl. Gorodovikovsk','KM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KM-03','Iki-Burulskiy Area','KM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KM-04','Ketchenerovsky Area','KM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KM-05','Lagansky Area / incl. Lagan','KM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KM-06','Maloderbetovsky Area','KM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KM-07','Oktyabrsky District','KM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KM-08','Priyutnensky Area','KM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KM-09','Sarpinsky Area','KM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KM-10','Tselinny Area','KM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KM-11','Chernozemelsky Area','KM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KM-12','Yustinsky Area','KM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KM-13','Yashaltinsky Area','KM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KM-14','YashkulskY Area','KM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KO-01','Syktyvkar','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KO-02','Vorkuta','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KO-03','Vuktylsky Area / incl. Vuktyl','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KO-04','Inta','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KO-05','Pechorsky Area / incl. Pechora','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KO-06','Sosnogorsky Area / incl. Sosnogorsk and suburbs','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KO-07','Usinsk / incl. suburb','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KO-08','Ukhta','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KO-09','Izhemsky Area','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KO-10','Knyazhpogostsky Area / incl. Emba','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KO-11','Koygorodsky Area','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KO-12','Kortkerossky Area','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KO-13','Priluzsky Area','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KO-14','Syktyvdinsky Area','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KO-15','Sysolsky Area','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KO-16','Troitsko-Pechorsky Area','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KO-17','Udorsky Area','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KO-18','Ust-Vymskiy Area / incl. Mikun','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KO-19','Ust-Kulomskiy Area','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KO-20','Ust-Tsilemskiy Area','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-01','Zapadny District of Krasnodar','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-02','Karasunsky District of Krasnodar','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-03','Prikubansky District of Krasnodar','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-04','Centralny District of Krasnodar','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-05','Vostochny District of Novorossiysk','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-06','Primorsky District of Novorossiysk','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-07','Centralny District of Novorossiysk','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-08','Adlersky District OF Sochi','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-09','Lazarevsky District of Sochi','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-10','Khostinsky District of Sochi','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-11','Centralny District of Sochi','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-12','Anapa','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-13','Armavir','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-14','Belorechensk','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-15','Gelendzhik','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-16','Goryachij Kljuch','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-17','Yeysk','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-18','Kropotkin','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-19','Krymsk','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-20','Labinsk','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-21','Slavyansk-On-Kuban','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-22','Tikhoretsk','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-23','Tuapse','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-24','Abinsky Area / incl. Abinsk','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-26','Apsheronsky Area / incl. Apsheronsk, Hadyzhensk','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-27','Beloglinsky Area','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-28','Belorechensky Area / incl. Belorechensk','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-29','Bryukhovetsky Area ','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-30','Vyselkovsky Area','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-31','Gulkevichsky Area / incl. Gulkevichi','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-32','Dinskoj Area','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-33','Yeysk Area / incl. Yeysk','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-34','Kavkazsky Area / incl. Kropotkin','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-35','Kalininsky District','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-36','Kanevsky Area','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-37','Korenovsky Area / incl. Kopehobck','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-38','Krasnoarmejsky Area','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-39','Krylovsky Area','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-40','Krymsky Area / incl. Krymsk','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-41','Kurganinsky Area / incl. Kurganinsk','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-42','Kushchevsky Area','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-43','Labinsky Area / incl. Labinsk','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-44','Leningradsky Area','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-45','Mostovsky Area','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-46','Novokubansky Area / incl. Novokubansk','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-47','Novopokrovsky Area','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-48','Otradnensky Area','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-49','Pavlovsky Area','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-50','Primorsko-Ahtarsky Area / incl. Primorsko-Ahtarsk','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-51','Seversky Area','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-52','Slavyansky Area / incl. Slavyansk-na-Kubani','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-53','Starominsky Area','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-54','Tbilissky Area','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-55','Temryuksky Area / incl. Temryuk','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-56','Timashevsky Area / incl. Timashevsk','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-57','Tikhoretsky Area / incl. Tihoretsk','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-58','Tuapsinsky Area / incl. Tuapse','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-59','Uspensky Area','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-60','Ust-Labinsky Area / incl. Ust-Labinsk','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-61','Shcherbinovsky Area','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-65','Novorossijsky District of Novorossiysk','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-66','Yuzhny District of Novorossiysk','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-67','Sirius Regional Center','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KR-68','Anapsky Area','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KS-01','Kostroma','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KS-02','Buj','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KS-03','Volgorechensk','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KS-04','Galich','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KS-05','Manturovo','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KS-06','Nerekhta','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KS-07','Neya','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KS-08','Sharja','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KS-09','Antropovsky Area','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KS-10','Buysky Area','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KS-11','Vohomsky Area','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KS-12','Galichsky Area','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KS-13','Kadyysky Area','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KS-14','Kologrivsky Area / incl. Kologriv','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KS-15','Kostromskoj Area','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KS-16','Krasnoselsky District','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KS-17','Makaryevsky Area / incl. Makarev','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KS-18','Manturovsky Area','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KS-19','Mezhevsky Area','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KS-20','Neysky Area','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KS-21','Nerehtsky Area','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KS-22','Oktyabrsky Area','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KS-23','Ostrovsky Area','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KS-24','Pavinsky Area','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KS-25','Parfenyevsky Area','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KS-26','Ponazyrevsky Area','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KS-27','Pyshchugsky Area','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KS-28','Soligalichsky Area / incl. Soligalich','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KS-29','Sudislavsky Area','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KS-30','Susaninsky Area','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KS-31','Chukhlomsky Area / incl. Chuhloma','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KS-32','Shar''Insky Area','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KU-01','Zheleznodorozhny District of Kursk','KU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KU-02','Seymsky District of Kursk','KU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KU-03','Centralny District of Kursk','KU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KU-04','Zheleznogorsk','KU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KU-05','Kurchatov','KU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KU-06','Lgov','KU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KU-07','Shchigry','KU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KU-08','Belovsky Area','KU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KU-09','Bolshesoldatsky Area','KU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KU-10','Glushkovsky Area','KU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KU-11','Gorshechensky Area','KU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KU-12','Dmitriyevsky Area / incl. Dmitriev-Lgovskiy','KU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KU-13','Zheleznogorsky Area','KU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KU-14','Zolotuhinsky Area','KU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KU-15','Kastorensky Area','KU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KU-16','Konyshevsky Area','KU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KU-17','Korenevsky Area','KU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KU-18','Kursky Area','KU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KU-19','Kurchatovsky Area','KU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KU-20','Lgovsky Area','KU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KU-21','Manturovsky Area','KU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KU-22','Medvensky Area','KU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KU-23','Oboyansky Area / incl. Oboyan','KU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KU-24','Oktyabrsky Area','KU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KU-25','Ponyrovsky Area','KU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KU-26','Pristensky Area','KU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KU-27','Rylsky Area / incl. Rylsk','KU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KU-28','Sovetsky Area','KU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KU-29','Solntsevsky Area','KU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KU-30','Sudzhansky Area / incl. Sudzha','KU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KU-31','Timsky Area','KU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KU-32','Fatezhsky Area / incl. Fatezh','KU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KU-33','Homutovsky Area','KU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KU-34','Cheremisinovsky Area','KU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'KU-35','Shchigrovsky Area','KU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'LO-16','Sosnovy Bor','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'LO-20','Boksitogorsky Area / incl. Boksitogorsk, Pikalyovo','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'LO-21','Volosovsky Area','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'LO-22','Volkhovsky Area / incl. Volkhov, Novaja Ladoga','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'LO-23','Vsevolozhsky Area / incl. Vsevolzhsk, Sertolovo','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'LO-24','Vyborgsky Area / incl. Vyborg, Vysotsk, Kamennogorsk','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'LO-25','Gatchina Area / incl. Gatchina, Communar','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'LO-26','Kingiseppsky Area / incl. Kingisepp, Ivangorod','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'LO-27','Kirishinsky Area / incl. Kirishi','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'LO-28','Kirovsky Area / incl. Kirovsk, Otradnoye','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'LO-29','Lodeynopolsky Area / incl. Lodeynoye Pole, Svirstroy','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'LO-30','Lomonosovsky Area','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'LO-31','Luzhsky Area / incl. Luga','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'LO-32','Podporozhsky Area / incl. Podporozhe','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'LO-33','Priozersky Area / incl. Priozersk','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'LO-34','Slantsevsky Area / incl. Slantsy','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'LO-35','Tikhvinsky Area / incl. Tikhvin','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'LO-36','Tosnensky Area / incl. Tosno, Lyuban, Nikolskoe','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'LP-01','Levoberezhny District of Lipetsk','LP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'LP-02','Oktiabrsky District of Lipetsk','LP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'LP-03','Pravoberezhny District of Lipetsk','LP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'LP-04','Sovetsky District of Lipetsk','LP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'LP-05','Yelets','LP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'LP-06','Volovsky Area','LP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'LP-07','Gryazinsky Area / incl. Grjazi','LP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'LP-08','Dankovsky Area / incl. Dankov','LP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'LP-09','Dobrinsky Area','LP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'LP-10','Dobrovsky Area','LP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'LP-11','Dolgorukovsky Area','LP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'LP-12','Yeletsky Area','LP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'LP-13','Zadonsky Area / incl. Zadonsk','LP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'LP-14','Izmalkovsky Area','LP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'LP-15','Krasninsky Area','LP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'LP-16','Lebedyansky Area / incl. Lebedyan','LP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'LP-17','Lev - Tolstovsky Area','LP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'LP-18','Lipetsky Area','LP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'LP-19','Stanovlyansky Area','LP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'LP-20','Terbunsky Area','LP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'LP-21','Usmansky Area / incl. Usman','LP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'LP-22','Hlevensky Area','LP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'LP-23','Chaplyginsky Area / incl. Chaplygin','LP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MA-01','Vostochny District of Moscow','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MA-02','Zapadny District of Moscow','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MA-03','City Of Zelenograd','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MA-04','Severny District of Moscow','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MA-05','Severovostochny District of Moscow','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MA-06','Severozapadny District of Moscow','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MA-07','Centralny District of Moscow','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MA-08','Jugovostochny District of Moscow','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MA-09','Jugozapadny District of Moscow','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MA-10','Juzhny District of Moscow','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MA-11','Novomoskovsky District of Moscow','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MA-12','Troitsky District of Moscow','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MD-01','Leninsky District of Saransk','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MD-02','Oktyabrsky District of Saransk','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MD-03','Proletarsky District of Saransk','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MD-04','Kovylkino','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MD-05','Ruzaevka','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MD-06','Ardatovsky Area / incl. Ardatov','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MD-07','Atyuryevsky Area','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MD-08','Atyashevsky Area','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MD-09','Bolshebereznikovsky Area','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MD-10','Bolsheignatovsky Area','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MD-11','Dubensky Area','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MD-12','Elnikovsky Area','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MD-13','Zubovo-Polyansky Area','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MD-14','Insarsky Area / incl. Insar','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MD-15','Ichalkovsky Area','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MD-16','Kadoshkinsky Area','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MD-17','Kovylkinsky Area / incl. Kovylkino','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MD-18','Kochkurovsky Area','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MD-19','Krasnoslobodsky Area / incl. Krasnoslobodsk','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MD-20','Lyambirsky Area','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MD-21','Romodanovsky Area','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MD-22','Ruzayevsky Area / incl. Ruzaevka ','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MD-23','Staroshajgovsky Area','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MD-24','Temnikovsky Area / incl. Temnikov','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MD-25','Tengushevsky Area','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MD-26','Torbeevsky Area','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MD-27','Chamzinsky Area','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-01','City District of Balashikha','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-02','Bronnitsy','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-03','Zato Vlasiha','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-04','Zato Voskhod','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-06','City District of Dzerzhinsk','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-08','Dolgoprudnyj','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-09','City District of Domodedovo','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-10','Dubna','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-13','Zhukovsky','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-14','Zato Zvezdny Gorodok','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-15','City District of Zvenigorod','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-16','City District of Ivanteevka','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-21','City District of Kolomna','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-22','City District of Korolev','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-23','City District of Kotelniki','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-24','City District of Lobnya','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-25','City District of Lytkarino','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-26','City District Losino-Petrovsky','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-27','Krasnoarmejsk','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-28','Zato Molodezhny','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-34','Orekhovo-Zuyevo','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-36','City District of Podolsk','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-37','Protvino','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-39','Pushchino','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-41','City District of Reutov','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-42','Roshal','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-44','Serpukhov','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-48','City District of Fryazino','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-49','City District of Khimki','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-50','City District of Chernogolovka','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-52','Elektrogorsk','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-54','City District of Electrostal','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-56','City District of Krasnoznamensk','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-58','Volokolamsk Area / incl. Volokolamsk','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-59','Voskresensky Area / incl. Voskresensk','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-60','Dmitrovsky Area / incl. Yakhroma, Dmitrov','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-62','Egoryevsky Area / incl. Egoryevsk','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-63','Zaraysky Area / incl. Zaraysk','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-64','Istrinsky Area / incl. Dedovsk, Istra','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-65','City District of Kashira','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-66','Klinsky Area / incl. Vysokovsk, Klin','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-67','Kolomensky Area','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-68','Krasnogorsky Area / incl. Krasnogorsk','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-69','Leninsky Area / incl. Vidnoe','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-70','Lotoshinsky Area','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-71','Lukhovitsky Area / incl. Lukhovitsy','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-72','Lyubertsy Area / incl. Lyubertsy','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-73','Mozhaisky Area / incl. Mozhaisk ','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-74','Mytishchi Area / incl. Mytischi ','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-75','Naro-Fominskiy Area / incl. Aprelevka, Naro-Fominsk','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-76','Noginsky Area / incl. Elektrougli, Noginsk','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-77','Odintsovsky Area / incl. Odintsovo','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-78','Ozyorsky Area / incl. Ozery','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-79','Orekhovo-Zuyevsky Area / incl. Drezna, Likino-Dulevo','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-80','Pavlovo-Posadsky Area / incl. Pavlovsk Posad','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-82','Pushkinsky Area / incl. Pushkino','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-83','Ramensky Area / incl. Ramenskoje','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-84','Ruzsky Area / incl. Ruza','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-85','Sergiyevo-Posadsky Area / incl. Krasnozavodsk','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-86','Serebrjano-Prudsky Area','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-87','Serpukhovsky Area','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-88','Solnechnogorsky Area / incl. Solnechnogorsk','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-89','Stupinsky Area / incl. Stupino','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-90','Taldomsky Area / incl. Taldom','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-92','Shatursky Area / incl. Shatura','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-93','Shakhovskoy Area','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-94','Shchelkovo Area / incl. Shchelkovo','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MO-95','Chekhovskt Area /incl.Chekhov','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MR-01','Yoshkar-Ola','MR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MR-02','Volzhsk','MR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MR-03','Kozmodemyansk','MR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MR-04','Volzhsky Area','MR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MR-05','Gornomariysky Area','MR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MR-06','Zvenigovsky Area / incl. Zvenigovo','MR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MR-07','Kilemarsky Area','MR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MR-08','Kuzhenersky Area','MR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MR-09','Mari-Turekskiy Area','MR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MR-10','Medvedevsky Area','MR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MR-11','Morkinsky Area','MR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MR-12','Novotor''yalskiy Area','MR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MR-13','Orshansky Area','MR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MR-14','Paranginsky Area','MR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MR-15','Sernursky Area','MR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MR-16','Sovetsky Area','MR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MR-17','Yurinsky Area','MR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MU-01','Leninsky District of Murmansk','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MU-02','Oktiabrsky District of Murmansk','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MU-03','Pervomajsky District of Murmansk','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MU-04','Apatity','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MU-05','Kandalaksha Area / incl. Kandalaksha','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MU-06','Kirovsk','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MU-07','Monchegorsk','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MU-08','Olenegorsk','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MU-09','Polyarnye Zori','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MU-10','Zato Zaozersk','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MU-11','Zato Ostrovnoj','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MU-13','Zato Severomorsk','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MU-16','Kovdorsky Area','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MU-17','Kolsky Area / incl. Kola','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MU-18','Lovozersky Area','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MU-19','Pechengsky Area / incl. Zapolyarny','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MU-20','Tersky Area','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MU-21','Zato Vidyayevo','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'MU-22','Zato Aleksandrovsk / incl. Gadzhievo','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-01','Avtozavodsky District of Nizhny Novgorod','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-02','Kanavinsky District of Nizhny Novgorod','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-03','Leninsky District of Nizhny Novgorod','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-04','Moskovsky District of Nizhny Novgorod','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-05','Nizhegorodsky District of Nizhny Novgorod','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-06','Prioksky District of Nizhny Novgorod','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-07','Sovetsky District of Nizhny Novgorod','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-08','Sormovsky District of Nizhny Novgorod','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-09','City District Arzamas','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-15','City District Dzerzhinsk','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-19','City District Sarov','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-20','Ardatovsky Area','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-21','Arzamas Area','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-22','Balakhna Area / incl. Balakhna','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-23','Bogorodsky Area / incl. Bogorodsk','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-24','Bolsheboldinsky Area','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-25','Bol''Shemurashkinsky Area','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-26','City District of Bor','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-27','Buturlinsky Area','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-28','Vadsky Area','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-29','Varnavinsky Area','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-30','Vachsky Area','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-31','Vetluzhsky Area / incl. Vetluga','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-32','Voznesensky Area','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-33','Volodarsky Area / incl. Volodarsk','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-34','Vorotynsky Area','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-35','Voskresensky Area','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-36','City District Vyksa','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-37','Gaginsky Area','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-38','Gorodetsky Area / incl. Gorodets, Zavolzhe','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-39','Dalnekonstantinovsky Area','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-40','Diveevsky Area','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-41','Knyagininsky Area / incl. Knyaginino','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-42','Koverninsky Area','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-43','Krasnobakovsky Area','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-44','Krasnooktyabrsky Area','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-45','Kstovsky Area / incl. Kstovo','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-46','City District of Kulebaki','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-47','Lukoyanovsky Area / incl. Lukoyanov','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-48','Lyskovsky Area / incl. Lyskovo','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-49','City District Navashinsky','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-50','Pavlovsk Area / incl. Pavlovo, Vorsma, Gorbatov','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-51','City District Pervomajsk','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-52','Perevozsky Area','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-53','Pilninsky Area','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-54','Pochinkovsky Area','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-55','City District Semenovsky','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-56','Sergachsky Area / incl. Sergach','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-57','Sechenovsky Area','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-58','City District Sokolsky','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-59','Sosnovsky Area','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-60','Spassky Area','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-61','Tonkinsky Area','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-62','Tonshaevsky Area','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-63','Urensky Area / incl. Uren','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-64','City District of Chkalovsk','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-65','Sharangsky Area','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-66','Shatkovsky Area','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NN-67','City District of Shakhunya','NN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NO-01','Naryan-Mar','NO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NO-02','Zapoljarny Area / incl. Iskatelej','NO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NO-03','Amderma','NO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NV-01','Novgorod','NV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NV-02','Borovichi','NV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NV-03','Staraja Russa','NV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NV-04','Batetsky Area','NV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NV-05','Borovichsky Area / incl. Borovichi','NV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NV-06','Valdaisky Area / incl. Valdai','NV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NV-07','Volotovsky Area','NV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NV-08','Demyansky Area','NV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NV-09','Krestetsky Area','NV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NV-10','Lyubytinsky Area','NV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NV-11','Malovishersky Area / incl. Malaya Vishera','NV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NV-12','Marevsky Area','NV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NV-13','Moshenskoy Area','NV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NV-14','Novgorodsky Area','NV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NV-15','Okulovsky Area / incl. Okulovka','NV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NV-16','Parfinsky Area','NV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NV-17','Pestovsky Area / incl. Pestovo','NV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NV-18','Poddorsky Area','NV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NV-19','Soletsky Area / incl. Soltsy','NV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NV-20','Starorussky Area / incl. Staraja Russa','NV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NV-21','Hvoyninsky Area','NV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NV-22','Holmsky Area / incl. Holm','NV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NV-23','Chudovsky Area / incl. Chudovo','NV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'NV-24','Shimsky Area','NV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'OR-01','Zheleznodorozhny District of Orel','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'OR-02','Zavodskoj Area of Orel','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'OR-03','Severny Area of Orel','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'OR-04','Sovetsky District of Orel','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'OR-05','Livny','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'OR-06','Mtsensk','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'OR-07','Bolkhovsky Area / incl. Bolhov','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'OR-08','Verhovsky Area','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'OR-09','Glazunovsky Area','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'OR-10','Dmitrovsky District / incl. Dmitrovsk-Orlovskiy','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'OR-11','Dolzhansky Area','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'OR-12','Zalegoshchensky Area','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'OR-13','Znamensky Area','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'OR-14','Kolpnjansky Area','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'OR-15','Korsakovsky Area','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'OR-16','Krasnozorensky Area','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'OR-17','Kromsky Area','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'OR-18','Livensky Area','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'OR-19','Maloarkhangel''sky Area / incl. Maloarhangelsk','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'OR-20','Mtsensky Area','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'OR-21','Novoderevenkovsky Area','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'OR-22','Novosilsky Area / incl. Novosil','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'OR-23','Orlovsky Area','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'OR-24','Pokrovsky Area','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'OR-25','Sverdlovsky Area','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'OR-26','Soskovsky Area','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'OR-27','Trosnyansky Area','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'OR-28','Uritsky Area','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'OR-29','Hotynetsky Area','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'OR-30','Shablykinsky Area','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PE-01','Zheleznodorozhny District of Penza','PE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PE-02','Leninsky District of Penza','PE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PE-03','Oktyabrsky District of Penza','PE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PE-04','Pervomajsky District of Penza','PE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PE-06','Kuznetsk','PE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PE-08','Zato Zarechny','PE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PE-09','Bashmakovsky Area','PE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PE-10','Bednodemyanovsky Area / incl. Bednodemyanovsk','PE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PE-11','Bekovsky Area','PE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PE-12','Belinsky Area / incl. Belinsky','PE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PE-13','Bessonovsky Area','PE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PE-14','Vadinsky Area','PE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PE-15','Gorodishchensky Area / incl. Gorodische, Sursk','PE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PE-16','Zemetchinsky Area','PE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PE-17','Issinsky Area','PE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PE-18','Kamensky Area / incl. Kamenka','PE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PE-19','Kameshkirsky Area','PE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PE-20','Kolyshleysky Area','PE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PE-22','Kuznetsky Area','PE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PE-23','Lopatinsky Area','PE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PE-24','Luninsky Area','PE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PE-25','Maloserdobinsky Area','PE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PE-26','Mokshansky Area','PE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PE-27','Narovchatsky Area','PE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PE-28','Neverkinsky Area','PE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PE-29','Nizhnelomovsky Area / incl. Nizhny Lomov','PE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PE-30','Nikolsky Area / incl. Nikolsk','PE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PE-31','Pachelmsky Area','PE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PE-32','Penzensky Area','PE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PE-33','Serdobsky Area / incl. Serdobsk','PE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PE-34','Sosnovoborsk Area','PE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PE-35','Tamalinsky Area','PE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PE-36','Shemysheysky Area','PE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-01','Dzerzhinsky District of Perm','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-02','Industrialny District of Perm','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-03','Kirovsky District of Perm','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-04','Leninsky District of Perm','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-05','Motovilikhinsky District of Perm','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-06','Ordzhonikidzevsky District of Perm','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-07','Sverdlovsky District of Perm','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-08','Aleksandrovsky Area / incl. Aleksandrovsk','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-09','Berezniki','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-10','Gremyachinsky Area / incl. Gremyachinsk','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-11','Gubakhinsky Area / incl. Gubakha','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-13','Kizelovsky Area / incl. Kizel','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-14','Krasnokamsky Area / incl. Krasnokamsk','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-15','Kungur','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-17','Solikamsk','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-20','Bardymsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-21','Berezovsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-22','Bolshesosnovsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-23','Vereshchaginsky Area / incl. Vereshchagino','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-24','Gornozavodsky Area / incl. Gornozavodsk','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-25','Elovsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-26','Ilyinsky Area / incl. Chermoz','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-27','Karagaysky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-28','Kishertsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-29','Krasnovishersky District / incl. Krasnovishersk','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-30','Kuyedinsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-31','Kungursky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-32','Lysvensky Area / incl. Lysva','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-33','Nytvensky Area / incl. Nytva','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-34','Oktyabrsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-35','Ordinsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-36','Osinsky Area / incl. Osa','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-37','Okhansky Area / incl. Okhansk','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-38','Ochersky Area / incl. Ocher','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-39','Permsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-40','Sivinsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-41','Solikamsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-42','Suksunsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-43','Uinsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-44','Usolsky Area / incl. Usolye','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-45','Chastinsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-46','Cherdynsky Area / incl. Cherdyn','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-47','Chernushinsky Area / incl. Chernushka','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-48','Chusovskoy Area / incl. Chusovoy','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-49','Tchaikovsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-50','Dobryansky Area / incl. Dobryanka','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-51','Zato Zvezdny ','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-52','Kudymkar','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-53','Gaynsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-54','Kosinsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-55','Kochevsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-56','Kudymkarsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-57','Yurlinsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PM-58','Yusvinsky Area','PM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PS-01','Pskov','PS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PS-02','Velikiye Luki','PS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PS-03','Bezhanitsky Area','PS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PS-04','Velikiye Luki Area','PS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PS-05','Gdovsky Area / incl. Gdov','PS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PS-06','Dedovichsky Area','PS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PS-07','Dnovsky Area / incl. Dno','PS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PS-08','Krasnogorodsky Area','PS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PS-09','Kunyinsky Area','PS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PS-10','Loknjansky Area','PS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PS-11','Nevelsky Area / incl. Nevel','PS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PS-12','Novorzhevsky Area / incl. Novorzhev','PS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PS-13','Novosokolnichesky Area / incl. Novosokolniki','PS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PS-14','Opochetsky Area / incl. Opochka','PS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PS-15','Ostrovsky Area / incl. Ostrov','PS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PS-16','Palkinsky Area','PS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PS-17','Pechorsky Area / incl. Pechora','PS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PS-18','Pljussky Area','PS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PS-19','Porkhovsky Area / incl. Porhov','PS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PS-20','Pskovsky Area','PS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PS-21','Pustoshkinsky Area / incl. Pustoshka','PS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PS-22','Pushkinogorsky Area','PS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PS-23','Pytalovsky Area / incl. Pytalovo','PS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PS-24','Sebezhsky Area / incl. Sebezh','PS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PS-25','Strugo-Krasnenskiy Area','PS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'PS-26','Usvyatsky Area','PS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RA-01','Zheleznodorozhny District of Ryazan','RA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RA-02','Moskovsky District of Ryazan','RA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RA-03','Oktiabrsky District of Ryazan','RA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RA-04','Sovetsky District of Ryazan','RA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RA-05','Kasimov','RA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RA-06','Sasovo','RA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RA-07','Skopin','RA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RA-08','Ermishinsky Area','RA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RA-09','Zaharovsky Area','RA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RA-10','Kadomsky Area','RA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RA-11','Kasimovsky Area','RA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RA-12','Klepikovsky Area / incl. Spas-Klepiki','RA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RA-13','Korablinsky Area / incl. Korablino','RA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RA-14','Miloslavsky Area','RA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RA-15','Mikhaylovsky Area / incl. Mikhaylov','RA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RA-16','Aleksandro-Nevskiy Area','RA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RA-17','Pitelinsky Area','RA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RA-18','Pronsky Area / incl. Novomichurinsk','RA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RA-19','Putjatinsky Area','RA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RA-20','Rybnovsky Area / incl. Rybnoe','RA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RA-21','Ryazhsky Area / incl. Ryazhsk','RA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RA-22','Ryazansky Area','RA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RA-23','Sapozhkovsky Area','RA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RA-24','Saraevsky Area','RA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RA-25','Sasovsky Area','RA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RA-26','Skopinsky Area','RA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RA-27','Spassky Area / incl. Spassk-Ryazanskiy','RA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RA-28','Starozhilovsky Area','RA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RA-29','Uholovsky Area','RA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RA-30','Chuchkovsky Area','RA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RA-31','Shatsky Area / incl. Shatsk','RA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RA-32','Shilovsky Area','RA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-01','Voroshilovsky District of Rostov-on-Don','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-02','Zheleznodorozhny District of Rostov-on-Don','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-03','Kirovsky District of Rostov-on-Don','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-04','Leninsky District of Rostov-on-Don','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-05','Oktyabrsky District of Rostov-on-Don','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-06','Pervomajsky District of Rostov-on-Don','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-07','Proletarsky District of Rostov-on-Don','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-08','Sovetsky District of Rostov-on-Don','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-09','Azov','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-10','Batajsk','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-12','Volgodonsk','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-13','Gukovo','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-14','Donetsk','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-15','Zverevo','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-16','Kamensk-Shahtinsky','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-19','Novocherkassk','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-20','Novoshakhtinsk','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-22','Taganrog','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-23','Shahty','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-24','Azovsky Area','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-25','Aksaysky Area / incl. Aksay','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-26','Bagaevsky Area','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-27','Belokalitvinsky Area / incl. Belaja Kalitva','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-28','Bokovsky Area','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-29','Verhnedonskoj Area','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-30','Veselovsky Area','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-31','Volgodonsky Area','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-32','Dubovsky Area','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-33','Egorlyksky Area','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-34','Zavetinsky Area','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-35','Zernogradsky Area / incl. Zernograd','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-36','Zimovnikovsky Area','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-37','Kagalnitsky Area','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-38','Kamensky Area','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-39','Kasharsky Area','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-40','Konstantinovsky Area / incl. Konstantinovsk','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-41','Krasnosulinsky Area / incl. Krasny Sulin','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-42','Kuibyshevsky Area','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-43','Martynovsky Area','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-44','Matveevo-Kurganskiy Area','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-45','Millerovsky Area / incl. Millerovo','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-46','Milyutinsky Area','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-47','Morozovsky Area / incl. Morozovsk','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-48','Myasnikovsky Area','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-49','Neklinovsky Area','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-50','Oblivsky Area','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-51','Oktyabrsky Area','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-52','Orlovsky Area','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-53','Peschanokopsky Area','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-54','Proletarsky Area / incl. Proletarsk','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-55','Remontnensky Area','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-56','Rodionovo-Nesvetayskiy Area','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-57','Salsky Area / incl. Salsk','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-58','Semikarakorsky Area / incl. Semikarakorsk','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-59','Sovetsky Area ','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-60','Tarasovsky Area','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-61','Tatsinsky Area','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-62','Ust-Donetskiy Area','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-63','Tselinsky Area','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-64','Tsimlyansk Area / incl. Tsimlyansk','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-65','Chertkovsky Area','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'RO-66','Sholohovsky Area','RO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-01','Volzhsky District of Saratov','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-02','Zavodskoj District of Saratov','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-03','Kirovsky District of Saratov','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-04','Leninsky District of Saratov','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-05','Oktyabrsky District of Saratov','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-06','Frunzensky District of Saratov','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-17','Shikhany','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-19','Aleksandrovo-Gayskiy Area','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-20','Arkadaksky Area / incl. Arkadak','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-21','Atkarsky Area','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-22','Bazarno-Karabulaksky Area','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-23','Balakovsky Area','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-24','Balashovsky Area','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-25','Baltaysky Area','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-26','Volsky Area','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-27','Voskresensky Area','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-28','Dergachevsky Area','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-29','Duhovnitsky Area','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-30','Ekaterinovsky Area','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-31','Ershovsky Area / incl. Ershov','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-32','Ivanteevsky Area','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-33','Kalininsky Area / incl. Kalininsk','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-34','Krasnoarmejsky Area','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-35','Krasnokutsky Area / incl. Krasny Kut','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-36','Krasnopartizansky Area','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-37','Lysogorsky Area','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-38','Markssky Area','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-39','Novoburassky Area','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-40','Novouzensky Area / incl. Novouzensk','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-41','Ozinsky Area','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-42','Perelyubsky Area','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-43','Petrovsky Area','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-44','Pitersky Area','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-45','Pugachevsky Area','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-46','Rovensky Area','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-47','Romanovsky Area','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-48','Rtishchevsky Area','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-49','Samoylovsky Area','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-50','Saratovsky Area','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-51','Sovetsky Area ','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-52','Tatishchevsky Area','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-53','Turkovsky Area','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-54','Fedorovsky Area','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-55','Hvalynsky Area','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-56','Engel''Ssky Area','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-57','Zato Mihaylovskiy','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SA-58','Zato Svetly','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SM-01','Zadneprovsky District of Smolensk','SM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SM-02','Leninsky District of Smolensk','SM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SM-03','Industrialny District of Smolensk','SM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SM-04','Desnogorsk','SM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SM-05','Velizhsky Area / incl. Velizh','SM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SM-06','Vyazemsky Area / incl. Vyazma','SM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SM-07','Gagarinsky Area / incl. Gagarin','SM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SM-08','Glinkovsky Area','SM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SM-09','Demidovsky Area / incl. Demidov','SM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SM-10','Dorogobuzhsky Area / incl. Dorogobuzh','SM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SM-11','Dukhovshchinsky Area / incl. Duhovshchina','SM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SM-12','Yelninsky Area / incl. Yelnya','SM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SM-13','Ershichsky Area','SM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SM-14','Kardymovsky Area','SM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SM-15','Krasninsky Area','SM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SM-16','Monastyrshchinsky Area','SM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SM-17','Novoduginsky Area','SM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SM-18','Pochinkovsky Area / incl. Pochinok','SM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SM-19','Roslavlsky Area / incl. Roslavl','SM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SM-20','Rudnyansky Area / incl. Rudnya','SM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SM-21','Safonovsky Area / incl. Safonovo','SM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SM-22','Smolensky Area','SM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SM-23','Sychevsky Area / incl. Sychevka','SM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SM-24','Temkinsky Area','SM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SM-25','Ugransky Area','SM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SM-26','Hislavichsky Area','SM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SM-27','Holm-Zhirkovskiy Area','SM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SM-28','Shumyachsky Area','SM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SM-29','Yartsevsky Area / incl. Yartsevo','SM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SO-01','Zaterechny District of Vladikavkaz','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SO-02','Iristonsky District of Vladikavkaz','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SO-03','Industrialny District of Vladikavkaz','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SO-04','Severozapadny District of Vladikavkaz','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SO-05','Alagirsky Area / incl. Alagir','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SO-06','Ardonsky Area / incl. Ardon','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SO-07','Digorsky Area / incl. Digora','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SO-08','Irafsky Area','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SO-09','Kirovsky Area','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SO-10','Mozdoksky Area / incl. Mozdok','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SO-11','Pravoberezhny Area / incl. Beslan','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SO-12','Prigorodny Area','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SP-01','Admiralteisky District St. Petersburg','SP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SP-02','Vasileostrovsky District St. Petersburg','SP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SP-03','Vyborgsky District St. Petersburg','SP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SP-04','Kalininsky District St. Petersburg','SP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SP-05','Kirovsky District St. Petersburg','SP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SP-06','Kolpino District St. Petersburg','SP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SP-07','Krasnogvardeisky District St. Petersburg','SP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SP-08','Krasnoselsky District St. Petersburg','SP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SP-09','Kronshtadt District St. Petersburg','SP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SP-10','Kurortny District St. Petersburg','SP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SP-12','Primorsky District St. Petersburg','SP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SP-13','Moskovsky District St. Petersburg','SP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SP-14','Nevsky District St. Petersburg','SP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SP-16','Petrogradsky District St. Petersburg','SP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SP-17','Petrodvorets District St. Petersburg','SP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SP-18','Pushkin District St. Petersburg','SP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SP-19','Frunzensky District St. Petersburg','SP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SP-20','Centralny District St. Petersburg','SP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-01','Zheleznodorozhny District of Samara','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-02','Kirovsky District of Samara','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-03','Krasnoglinsky District of Samara','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-04','Kuibyshevsky District of Samara','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-05','Leninsky District of Samara','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-06','Oktyabrsky District of Samara','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-07','Industrialny District of Samara','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-08','Samarsky District of Samara','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-09','Sovetsky District of Samara','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-10','Avtozavodsky District of Tolyatti','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-11','Komsomolsky District of Tolyatti','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-12','Centralny District of Tolyatti','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-13','Zhigulyovsk','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-14','Kinel','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-15','Novokujbyshevsk','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-16','Oktyabrsk','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-17','Otradny','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-18','Pokhvistnevo','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-19','Syzran','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-20','Chapaevsk','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-21','Alexeyevsky Area','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-22','Bezenchuksky Area','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-23','Bogatovsky Area','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-24','Bolsheglushitsky Area','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-25','Bolshechernigovsky Area','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-26','Borsky Area','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-27','Volzhsky Area','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-28','Elhovsky Area','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-29','Isaklinsky Area','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-30','Kamyshlinsky Area','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-31','Kinelsky Area','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-32','Kinel-Cherkasskiy Area','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-33','Klyavlinsky Area','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-34','Koshkinsky Area','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-35','Krasnoarmeisky Area','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-36','Krasnoyarsky Area','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-37','Neftegorsky Area / incl. Neftegorsk','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-38','Pestravsky Area','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-39','Pohvistnevsky Area','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-40','Privolzhsky Area','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-41','Sergiyevsky Area','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-42','Stavropolsky Area','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-43','Syzransky Area','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-44','Hvorostyansky Area','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-45','Chelno-Vershinsky Area','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-46','Shentalinsky Area','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'SR-47','Shigonsky Area','SR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'ST-01','Leninsky District of Stavropol','ST',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'ST-02','Oktyabrsky District of Stavropol','ST',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'ST-03','Industrialny Region of Stavropol','ST',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'ST-04','Budyonovsk','ST',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'ST-05','Georgievsk','ST',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'ST-06','Essentuki','ST',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'ST-07','Zheleznovodsk','ST',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'ST-08','Kislovodsk','ST',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'ST-09','Lermontov','ST',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'ST-10','Mineralnye Vody','ST',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'ST-11','Nevinnomyssk','ST',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'ST-12','Pyatigorsk','ST',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'ST-13','Aleksandrovsky Area','ST',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'ST-14','Andropovsky Area','ST',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'ST-15','Apanasenkovsky Area','ST',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'ST-16','Arzgirsky Area','ST',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'ST-17','Blagodarnensky Area / incl. Blagodarny','ST',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'ST-18','Budennovsky Area','ST',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'ST-19','Georgievsky Area','ST',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'ST-20','Grachevsky Area','ST',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'ST-21','Izobilnensky Area / incl. Izobilny','ST',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'ST-22','Ipatovsky Area / incl. Ipatovo','ST',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'ST-23','Kirovsky Area / incl. Novopavlovsk','ST',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'ST-24','Kochubeevsky Area','ST',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'ST-25','Krasnogvardejsky Area','ST',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'ST-26','Kursky Area','ST',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'ST-27','Levokumsky Area','ST',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'ST-28','Mineralnye Vody City District','ST',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'ST-29','Neftekumsky Area / incl. Neftekumsk','ST',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'ST-30','Novoaleksandrovsky Area / incl. Novoaleksandrovsk','ST',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'ST-31','Novoselitsky Area','ST',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'ST-32','Petrovsky Area / incl. Svetlograd','ST',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'ST-33','Predgorny Area','ST',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'ST-34','Sovetsky Area / incl. Zelenokumsk','ST',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'ST-35','Stepnovsky Area','ST',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'ST-36','Trunovsky Area','ST',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'ST-37','Turkmensky Area','ST',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'ST-38','Shpakovsky Area','ST',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-01','Aviastroitelny District of Kazan','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-02','Vakhitovsky District of Kazan','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-03','Kirovsky District of Kazan','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-04','Moskovsky District of Kazan','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-05','Novo-Savinovsky District of Kazan','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-06','Privolzhsky District of Kazan','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-07','Sovetsky District of Kazan','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-08','Aznakaevo','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-09','Almet`evsk','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-10','Bavly','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-11','Bugult`ma','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-12','Elabuga','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-13','Zainsk','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-14','Zelenodolsk','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-15','Leninogorsk','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-16','Naberezhny Chelny','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-17','Nizhnekamsk','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-18','Nurlat','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-19','Chistopol','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-20','Agryzsky Area / incl. Agryz','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-21','Aznakayevsky Area / incl. Aznakaevo','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-22','Aksubayevsky Area','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-23','Aktanyshsky Area','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-24','Alexeyevsky Area','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-25','Alkeevsky Area','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-26','Almetyevsk Area / incl. Almetyevsk','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-27','Apastovsky Area','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-28','Arsky Area','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-29','Atninsky Area','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-30','Bavlinsky Area / incl. Bavly','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-31','Baltasinsky Area','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-32','Bugul`minsky Area / incl. Bugulma','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-33','Buinsky Area / incl. Buinsk','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-34','Verhneuslonsky Area','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-35','Vysokogorsky Area','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-36','Drozhzhanovsky Area','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-37','Elabuzhsky Area / incl. Elabuga','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-38','Zainsky Area / incl. Zainsk','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-39','Zelenodolsky Area / incl. Zelenodolsk','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-40','Kaybitsky Area','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-41','Kamsko-Ustinskiy Area','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-42','Kukmorsky Area','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-43','Laishevsky Area','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-44','Leninogorsky Area / incl. Leninogorsk','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-45','Mamadyshsky Area / incl. Mamadysh','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-46','Mendeleevsky Area / incl. Mendeleevsk','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-47','Menzelinsky Area / incl. Menzelinsk','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-48','Musljumovsky Area','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-49','Nizhnekamsky Area / incl. Nizhnekamsk','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-50','Novosheshminsky Area','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-51','Nurlatsky Area / incl. Nurlat','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-52','Pestrechinsky Area','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-53','Rybno-Slobodsky Area','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-54','Sabinsky Area','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-55','Sarmanovsky Area','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-56','Spassky Area / incl. Bolgar','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-57','Tetyushsky Area / incl. Tetyushi','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-58','Tukayevsky Area','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-59','Tyulyachinsky Area','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-60','Cheremshansky Area','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-61','Chistopolsky Area / incl. Chistopol','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-62','Yutazinsky Area','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-63','Buinsk','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-64','Avtozavodskoj District of Naberezhnye Chelny','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-65','Komsomolsky District of Naberezhnye Chelny','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TA-66','Centralny District of Naberezhnye Chelny','TA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TB-01','Leninsky District of Tambov','TB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TB-02','Oktyabrsky District of Tambov','TB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TB-03','Sovetsky District of Tambov','TB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TB-04','Kirsanov','TB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TB-05','Kotovsk','TB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TB-06','Michurinsk','TB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TB-07','Morshansk','TB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TB-08','Rasskazovo','TB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TB-09','Uvarovo','TB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TB-10','Bondarsky Area','TB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TB-11','Gavrilovsky Area','TB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TB-12','Zherdevsky Area / incl. Zherdevka','TB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TB-13','Znamensky Area','TB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TB-14','Inzhavinsky Area','TB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TB-15','Kirsanovsky Area','TB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TB-16','Michurinsky Area','TB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TB-17','Mordovsky Area','TB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TB-18','Morshansky Area','TB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TB-19','Muchkapsky Area','TB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TB-20','Nikiforovsky Area','TB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TB-21','Pervomajsky Area','TB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TB-22','Petrovsky Area','TB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TB-23','Pichayevsky Area','TB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TB-24','Rasskazovsky Area','TB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TB-25','Rzhaksinsky Area','TB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TB-26','Sampursky Area','TB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TB-27','Sosnovsky Area','TB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TB-28','Staroyuryevsky Area','TB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TB-29','Tambovsky Area','TB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TB-30','Tokarevsky Area','TB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TB-31','Uvarovsky Area','TB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TB-32','Umetsky Area','TB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TL-01','Zarechensky District of Tula','TL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TL-02','Privokzal''ny District of Tula','TL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TL-03','Proletarsky District of Tula','TL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TL-04','Sovietsky District of Tula','TL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TL-05','Centralny District of Tula','TL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TL-08','Donskoy / incl. Severo-Zadonsk','TL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TL-11','City District Novomoskovsk','TL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TL-14','City Aleksin','TL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TL-15','Arsenyevsky Area','TL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TL-16','Belevsky Area / incl. Belev','TL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TL-17','Bogoroditsky Area / incl. Bogoroditsk','TL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TL-18','Venevsky Area / incl. Beheb','TL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TL-19','Volovsky Area','TL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TL-20','Dubenskii Area','TL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TL-21','City Efremov','TL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TL-22','Zaoksky Area','TL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TL-23','Kamensky Area','TL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TL-24','Kimovsky Area / incl. Kimovsk','TL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TL-25','Kireevsky Area / incl. Kireevsk, Bolohovo, Lipki','TL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TL-26','Kurkinsky Area','TL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TL-27','Leninsky Area','TL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TL-29','Odoevsky Area','TL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TL-30','Plavsky Area / incl. Plavsk','TL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TL-31','Suvorovsky Area / incl. Suvorov, Chekalin','TL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TL-32','Teplo - Ogarevsky Area','TL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TL-33','Uzlovsky Area / incl. Uzlovaya','TL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TL-34','Chernsky Area','TL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TL-35','Shchekinsky Area / incl. Shchekino, Sovetsk','TL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TL-36','Yasnogorsky Area / incl. Yasnogorsk','TL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TL-37','Novogurovsky','TL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TL-38','Slavny','TL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-01','Zavolzhsky District of Tver','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-02','Moscowsky District of Tver','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-03','Proletarsky District of Tver','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-04','Centralny District of Tver','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-07','Vyshny Volochek','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-09','Kimry','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-13','Rzhev','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-14','Torzhok','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-16','Andreapolsky Area / incl. Andreapol','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-17','Bezhetsky Area / incl. Bezhetsk','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-18','Belsky Area / incl. White','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-19','Bologovsky Area / incl. Bologoye','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-20','Vesyegonsky Area / incl. Vesegonsk','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-21','Vyshnevolotsky Area','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-22','Zharkovsky Area','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-23','Zapadnodvinsky Area / incl. Zapadnaja Dvina','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-24','Zubtsovsky Area / incl. Zubtsov','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-25','Kalininsky Area','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-26','Kalyazinsky Area / incl. Kalyazin ','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-27','Kashinsky Area / incl. Kashin','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-28','Kesovogorsky Area','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-29','Kimrsky Area','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-30','Konakovsky Area / incl. Konakovo','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-31','Krasnokholmsky Area / incl. Krasnyj Holm','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-32','Kuvshinovsky Area / incl. Kuvshinovo','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-33','Lesnoj Area','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-34','Likhoslavlsky Area / incl. Lihoslavl','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-35','Maksatihinsky Area','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-36','Molokovsky Area','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-37','Nelidovsky Area / incl. Nelidovo','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-38','Oleninsky Area','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-39','Ostashkovsky Area / incl. Ostashkov','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-40','Penovsky Area','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-41','Rameshkovsky Area','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-42','Rzhevsky Area','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-43','Sandovsky Area','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-44','Selizharovsky Area','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-45','Sonkovsky Area','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-46','Spirovsky Area','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-47','Staritsky Area / incl. Staritsa','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-48','Torzhoksky Area','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-49','Toropetsky Area / incl. Toropets','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-50','Udomlya Area / incl. Udomlya','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-51','Firovsky Area','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-52','Zato Solnechny','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'TV-53','Zato Ozerny','TV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UD-01','Industrialny District of Izhevsk','UD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UD-02','Leninsky District of Izhevsk','UD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UD-03','Oktyabrsky District of Izhevsk','UD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UD-04','Pervomajsky District of Izhevsk','UD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UD-05','Ustinovsky District of Izhevsk','UD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UD-06','Votkinsk','UD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UD-07','Glazov','UD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UD-08','Mozhga','UD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UD-09','Sarapul','UD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UD-10','Alnashsky Area','UD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UD-11','Balezinsky Area','UD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UD-12','Vavozhsky Area','UD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UD-13','Votkinsky Area','UD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UD-14','Glazovsky Area','UD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UD-15','Grakhovsky Area','UD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UD-16','Debessky Area','UD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UD-17','Zavyalovsky Area','UD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UD-18','Igrinsky Area','UD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UD-19','Kambarsky Area / incl. Kambarka','UD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UD-20','Kezsky Area','UD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UD-21','Kiznersky Area','UD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UD-22','Kiyasovsky Area','UD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UD-23','Krasnogorsky Area','UD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UD-24','Malopurginsky Area','UD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UD-25','Mozhginsky Area','UD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UD-26','Sarapulsky Area','UD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UD-27','Seltinsky Area','UD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UD-28','Syumsinsky Area','UD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UD-29','Uvinsky Area','UD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UD-30','Sharkansky Area','UD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UD-31','Yukamensky Area','UD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UD-32','Yakshur-Bodinskiy Area','UD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UD-33','Yarsky Area','UD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UD-34','Karakulinsky Area','UD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UL-01','Zheleznodorozhny District of Ulyanovsk','UL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UL-02','Zavolzhsky District of Ulyanovsk','UL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UL-03','Zasviyazhsky District of Ulyanovsk','UL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UL-04','Leninsky District of Ulyanovsk','UL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UL-06','Dimitrovgrad','UL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UL-07','Bazarnosyzgansky Area','UL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UL-08','Baryshsky Area / incl. Barysh','UL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UL-09','Veshkaymsky Area','UL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UL-10','Inzensky Area / incl. Inza','UL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UL-11','Karsunsky Area','UL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UL-12','Kuzovatovsky Area','UL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UL-13','Majnsky Area','UL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UL-14','Melekessky Area','UL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UL-15','Nikolaevsky Area','UL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UL-16','Novomalyklinsky Area','UL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UL-17','Novospassky Area','UL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UL-18','Pavlovsky Area','UL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UL-19','Radishchevsky Area','UL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UL-20','Sengileevsky Area / incl. Sengiley','UL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UL-21','Starokulatkinsky Area','UL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UL-22','Staromajnsky Area','UL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UL-23','Sursky Area','UL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UL-24','Terengulsky Area','UL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UL-25','Ulyanovsky Area','UL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UL-26','Tsilninsky Area','UL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UL-27','Cherdaklinsky Area','UL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'UL-28','Novoulyanovsk','UL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-01','Voroshilovsky District of Volgograd','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-02','Dzerzhinsky District of Volgograd','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-03','Kirovsky District of Volgograd','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-04','Krasnoarmeisky District of Volgograd','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-05','Krasnooktyabrsky District of Volgograd','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-06','Sovetsky District of Volgograd','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-07','Traktorozavodsky District of Volgograd','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-08','Centralny District of Volgograd','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-09','Volzhsky','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-10','Kamyshin','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-11','City District City OF Mikhailovka','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-12','Uryupinsk','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-13','Frolovo','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-14','Alexeyevsky Area','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-15','Bykovsky Area','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-16','Gorodishchensky Area','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-17','Danilovsky Area','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-18','Dubovsky Area / incl. Dubovka','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-19','Elansky Area','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-20','Zhirnovsky Area / incl. Zhirnovsk','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-21','Ilovlinsky Area','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-22','Kalachevsky Area / incl. Kalach-na-Donu','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-23','Kamyshinsky Area / incl. Petrov Val','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-24','Kikvidzensky Area','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-25','Kletsky Area','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-26','Kotelnikovsky Area / incl. Kotelnikovo','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-27','Kotovsky Area / incl. Kotovo','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-28','Kumylzhensky Area','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-29','Leninsky Area / incl. Leninsk','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-30','Mikhailovsky Area','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-31','Nekhayevsky Area','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-32','Nikolaevsky Area / incl. Nikolaevsk','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-33','Novoanninsky Area / incl. Novoanninskiy','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-34','Novonikolaevsky Area','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-35','Oktyabrsky Area','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-36','Olkhovsky Area','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-37','Pallasovsky Area / incl. Pallasovka','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-38','Rudnyansky Area','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-39','Svetlojarsky Area','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-40','Serafimovichsky Area / incl. Serafimovich','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-41','Sredneakhtubinsky Area / incl. Krasnoslobodsk','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-42','Staropoltavsky Area','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-43','Surovikinsky Area / incl. Surovikino','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-44','Uryupinsk Area','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-45','Frolovsky Area','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VG-46','Chernyshkovsky Area','VG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VL-01','Leninsky District of Vladimir ','VL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VL-02','Oktyabrsky District of Vladimir','VL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VL-03','Frunzensky District of Vladimir','VL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VL-06','Gus-Khrustalny','VL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VL-07','Kovrov','VL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VL-09','Murom','VL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VL-12','Raduzhny','VL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VL-13','Aleksandrovsky Area / incl. Alexandrov, Karabanovo, Strunino','VL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VL-14','Vyaznikovsky Area /incl. of Vyazniki','VL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VL-15','Gorokhovetsky Area / incl. Gorohovets','VL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VL-16','Gus-Khrustalny Area','VL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VL-17','Kameshkovsky Area / incl. Kameshkovo','VL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VL-18','Kirzhachsky Area / incl. Kirzhach','VL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VL-19','Kovrovsky Area','VL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VL-20','Kolchuginsky Area / incl. Kolchugino','VL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VL-21','Melenkovsky Area / incl. Melenki','VL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VL-22','Muromsky Area','VL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VL-23','Petushinsky Area / incl. Petushki, Kosterevo, Pokrov','VL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VL-24','Selivanovsky Area','VL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VL-25','Sobinsky Area / incl. Sobinka, Lakinsk','VL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VL-26','Sudogodsky Area / incl. Sudogda','VL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VL-27','Suzdalsky Area / incl. Suzdal','VL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VL-28','Yurev-Polskiy Area / incl. Yurev-Polskiy','VL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VO-01','Vologda','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VO-02','Velikiy Ustyug','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VO-03','Sokol','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VO-04','Cherepovets','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VO-05','Babayevsky Area / incl. Babayevo','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VO-06','Babushkinsky Area','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VO-07','Belozersky Area / incl. Belozersk','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VO-08','Vashkinsky Area','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VO-09','Velikoustyzhsky Area / incl. Veliky Ustyug, Krasavino','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VO-10','Verhovazhsky Area','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VO-11','Vozhegodsky Area','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VO-12','Vologodsky Area','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VO-13','Vytegorsky Area / incl. Vytegra','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VO-14','Gryazovetsky Area / incl. Gryazovets','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VO-15','Kadujsky Area','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VO-16','Kirillovsky Area / incl. Kirillov','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VO-17','Kichmengsko-Gorodetsky Area','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VO-18','Mezhdurechensky Area','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VO-19','Nikolsky Area / incl. Nikolsk','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VO-20','Njuksensky Area','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VO-21','Sokolsky Area / incl. Kadnikov','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VO-22','Syamzhensky Area','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VO-23','Tarnogsky Area','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VO-24','Totemsky Area / incl. Tot`Ma','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VO-25','Ust-Kubinskiy Area','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VO-26','Ustyuzhensky Area / incl. Ustyuzhna','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VO-27','Harovsky Area / incl. Harovsk','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VO-28','Chagodoshchensky Area','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VO-29','Cherepovetsky Area','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VO-30','Sheksinsky Area','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VR-01','Zheleznodorozhny District of Voronezh','VR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VR-02','Kominternovsky District of Voronezh','VR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VR-03','Levoberezhny District of Voronezh','VR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VR-04','Leninsky District of Voronezh','VR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VR-05','Sovetsky District of Voronezh','VR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VR-06','Centralny District of Voronezh','VR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VR-07','Borisoglebsk City District','VR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VR-09','Novovoronezh','VR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VR-13','Anninsky Area','VR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VR-14','Bobrovsky Area / incl. Bobrov','VR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VR-15','Bogucharsky Area / incl. Boguchar','VR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VR-17','Buturlinovsky Area / incl. Buturlinovka','VR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VR-18','Verhnemamonsky Area','VR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VR-19','Verhnehavsky Area','VR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VR-20','Vorobyevsky Area','VR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VR-21','Gribanovsky Area','VR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VR-22','Kalacheevsky Area / incl. Kalach','VR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VR-23','Kamensky Area','VR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VR-24','Kantemirovsky Area','VR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VR-25','Kashirsky Area','VR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VR-26','Liskinsky Area / incl. Liski','VR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VR-27','Nizhnedevitsky Area','VR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VR-28','Novousmansky Area','VR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VR-29','Novokhopersky Area / incl. Novohopersk','VR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VR-30','Olkhovatsky Area','VR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VR-31','Ostrogozhsky Area / incl. Ostrogozhsk','VR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VR-32','Pavlovsky Area / incl. Pavlovsk','VR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VR-33','Paninsky Area','VR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VR-34','Petropavlovsky Area','VR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VR-35','Povorinsky Area / incl. Povorino','VR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VR-36','Podgorensky Area','VR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VR-37','Ramonsky Area','VR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VR-38','Repyevsky Area','VR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VR-39','Rossoshansky Area / incl. Rossosh','VR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VR-40','Semiluksky Area / incl. Semiluki','VR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VR-41','Talovsky Area','VR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VR-42','Ternovsky Area','VR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VR-43','Hokholsky Area','VR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'VR-44','Ertilsky Area / incl. Ertil','VR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'YR-01','Dzerzhinsky District of Yaroslavl','YR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'YR-02','Zavolzhsky District of Yaroslavl','YR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'YR-03','Kirovsky District of Yaroslavl','YR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'YR-04','Krasnoperekopsky District of Yaroslavl','YR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'YR-05','Leninsky District of Yaroslavl','YR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'YR-06','Frunzensky District of Yaroslavl','YR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'YR-07','Pereslavl-Zalessky','YR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'YR-08','Rostov','YR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'YR-09','Rybinsk','YR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'YR-10','Tutaev','YR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'YR-11','Uglich','YR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'YR-12','Bolsheselsky Area','YR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'YR-13','Borisoglebsky Area','YR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'YR-14','Breytovsky Area','YR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'YR-15','Gavrilov-Yamsky Area / incl. Gavrilov-Yam','YR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'YR-16','Danilovsky Area / incl. Danilov','YR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'YR-17','Lyubimsky Area / incl. Lyubim ','YR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'YR-18','Myshkinsky Area / incl. Myshkin','YR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'YR-19','Nekouzsky Area','YR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'YR-20','Nekrasovsky Area','YR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'YR-21','Pervomajsky Area','YR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'YR-22','Pereslavsky Area','YR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'YR-23','Poshekhonsky Area / incl. Poshekhon`e','YR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'YR-24','Rostovsky Area / incl. Rostov ','YR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'YR-25','Rybinsk Area','YR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'YR-26','Tutayevsky Area / incl. Tutaev','YR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'YR-27','Uglichsky Area / incl. Uglich ','YR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (54,'YR-28','Yaroslavsky Area','YR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (61,'AR-32','Franz-Josef Land','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (110,'HI,Hawaii','Hawaii','HI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (110,'HI,Honolulu','Honolulu','HI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (110,'HI,Kalawao','Kalawao','HI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (110,'HI,Kauai','Kauai','HI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (110,'HI,Maui','Maui','HI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (126,'KA-01','Baltijsky (Deleted)','KA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (126,'KA-02','Leningradsky District of Kaliningrad','KA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (126,'KA-03','Moskovsky District of Kaliningrad','KA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (126,'KA-04','Oktjabr`Sky (Deleted)','KA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (126,'KA-05','Centralny District of Kaliningrad','KA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (126,'KA-06','Baltiysky Area / incl. Baltiysk, Primorsk','KA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (126,'KA-07','Pionersky City District','KA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (126,'KA-08','Svetlogorsk Area / incl. Svetlogorsk','KA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (126,'KA-09','Svetlovsky City District','KA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (126,'KA-10','Sovietsky City District','KA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (126,'KA-11','Bagrationovsky Area / incl. Bagrationovsk','KA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (126,'KA-12','Gvardeisky City District','KA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (126,'KA-13','Guryevsky City District','KA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (126,'KA-14','Gusevsky City District','KA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (126,'KA-15','Zelenogradsky Area / incl. Zelenogradsk','KA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (126,'KA-16','Krasnoznamensky Area / incl. Krasnoznamensk ','KA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (126,'KA-17','Nemansky Area / incl. Neman','KA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (126,'KA-18','Nesterovsky Area / incl. Nesterov','KA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (126,'KA-19','Ozyorsk City District','KA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (126,'KA-20','Polesye Area / incl. Polessk','KA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (126,'KA-21','Pravdinsky Area / incl. Pravdinsk','KA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (126,'KA-22','Slavsky Area / incl. Slavsk','KA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (126,'KA-24','Ladushkinsky City District','KA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (126,'KA-23','Chernyakhovsky Area / incl. Chernyakhovsk','KA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (126,'KA-25','Mamonovsky City District','KA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (126,'KA-26','Yantarny City District','KA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (151,'LO-24','Vyborgsky Area Including Vysotsk','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'090','Chatham Islands','CAN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'001','Mongonui','NTL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'002','Whangaroa ','NTL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'003','Hokianga ','NTL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'004','Bay of Islands ','NTL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'005','Whangarei ','NTL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'006','Hobson ','NTL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'007','Otamatea ','NTL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'008','Rodney ','AUK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'009','Great Barrier Island ','AUK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'010','Waitemata ','AUK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'011','Franklin','AUK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'012','Raglan ','WKO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'013','Waikato ','WKO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'014','Waipa ','WKO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'015','Otorohanga','WKO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'016','Waitomo ','WKO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'017','Taumaranui','MWT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'018','Coromandel ','WKO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'019','Thames ','WKO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'020','Hauraki Plains ','WKO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'021','Ohinemuri','WKO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'022','Piako ','WKO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'023','Matamata','WKO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'024','Tauranga ','BOP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'025','Rotorua ','BOP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'026','Taupo','WKO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'027','Whakatane ','BOP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'028','Opotiki ','BOP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'029','Waiapu ','GIS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'030','Waikohu','GIS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'031','Cook ','GIS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'032','Wairoa ','HKB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'033','Hawke''s Bay ','HKB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'034','Waipawa ','HKB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'035','Patangata','HKB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'036','Waipukurau ','HKB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'037','Dannevirke ','MWT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'038','Woodville ','MWT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'039','Clifton ','TKI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'040','Taranaki ','TKI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'041','Inglewood','TKI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'042','Stratford ','TKI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'043','Egmont ','TKI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'044','Eltham ','TKI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'045','Waimate West','TKI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'046','Hawera ','TKI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'047','Patea ','TKI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'048','Waimarino ','MWT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'049','Waitotara ','MWT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'050','Wanganui ','MWT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'051','Rangitikei ','MWT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'052','Kiwitea ','MWT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'053','Pohangina ','MWT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'054','Oroua ','MWT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'055','Manawatu','MWT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'056','Kairanga ','MWT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'057','Horowhenua ','MWT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'058','Hutt ','WGN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'059','Pahiatua','MWT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'060','Akitio ','MWT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'061','Eketahura ','MWT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'062','Masterton ','MWT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'063','Waiarapa South','WGN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'064','Featherston','WGN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'065','Marlborough ','MBH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'066','Awatere','MBH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'067','Kaikoura ','CAN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'068','Golden Bay','TAS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'069','Waimea ','TAS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'070','Buller ','WTC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'071','Inangahua ','WTC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'072','Grey ','WTC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'073','Westland ','WTC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'074','Amuri ','CAN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'075','Cheviot ','CAN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'076','Waipara','CAN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'077','Kowai ','CAN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'078','Ashley ','CAN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'079','Rangiora ','CAN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'080','Eyre ','CAN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'081','Oxford ','CAN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'082','Tawera ','CAN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'083','Malvern ','CAN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'084','Paparua ','CAN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'085','Waimari ','CAN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'086','Heathcote ','CAN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'087','Halswell ','CAN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'088','Mount Herbert ','CAN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'089','Akaroa ','CAN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'091','Wairewa ','CAN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'092','Ellesmere ','CAN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'093','Ashburton ','CAN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'094','Geraldine ','CAN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'095','Levels ','CAN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'096','Mackenzie ','CAN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'097','Waimate','CAN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'098','Waitaki ','CAN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'099','Waihemo ','OTA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'100','Waikouaiti ','OTA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'101','Peninsula ','OTA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'102','Tareri ','OTA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'103','Bruce ','OTA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'104','Clutha ','OTA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'105','Tuapeka ','OTA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'106','Maniototo ','OTA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'107','Vincent ','OTA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'108','Lake ','OTA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'109','Southland ','OTA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'110','Wallace ','STL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'111','Fiord ','STL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (170,'112','Stewart Island ','STL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01001','Alegría-Dulantzi','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01002','Amurrio','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01003','Aramaio','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01004','Artziniega','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01006','Armiñón','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01008','Arratzua-Ubarrundia','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01009','Asparrena','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01010','Ayala/Aiara','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01011','Baños de Ebro/Mañueta','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01013','Barrundia','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01014','Berantevilla','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01016','Bernedo','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01017','Campezo/Kanpezu','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01018','Zigoitia','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01019','Kripan','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01020','Kuartango','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01021','Elburgo/Burgelu','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01022','Elciego','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01023','Elvillar/Bilar','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01027','Iruraiz-Gauna','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01028','Labastida/Bastida','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01030','Lagrán','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01031','Laguardia','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01032','Lanciego/Lantziego','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01033','Lapuebla de Labarca','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01034','Leza','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01036','Laudio/Llodio','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01037','Arraia-Maeztu','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01039','Moreda de Álava/Moreda Araba','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01041','Navaridas','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01042','Okondo','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01043','Oyón-Oion','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01044','Peñacerrada-Urizaharra','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01046','Ribera Alta','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01047','Erriberabeitia','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01049','Añana','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01051','Agurain/Salvatierra','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01052','Samaniego','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01053','San Millán/Donemiliaga','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01054','Urkabustaiz','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01055','Valdegovía/Gaubea','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01056','Harana/Valle de Arana','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01057','Villabuena de Álava/Eskuernaga','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01058','Legutio','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01059','Vitoria-Gasteiz','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01060','Yécora/Iekora','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01061','Zalduondo','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01062','Zambrana','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01063','Zuia','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01901','Iruña Oka/Iruña de Oca','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'01902','Lantarón','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02001','Abengibre','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02002','Alatoz','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02003','Albacete','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02004','Albatana','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02005','Alborea','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02006','Alcadozo','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02007','Alcalá del Júcar','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02008','Alcaraz','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02009','Almansa','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02010','Alpera','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02011','Ayna','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02012','Balazote','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02013','Balsa de Ves','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02014','Ballestero (El)','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02015','Barrax','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02016','Bienservida','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02017','Bogarra','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02018','Bonete','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02019','Bonillo (El)','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02020','Carcelén','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02021','Casas de Juan Núñez','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02022','Casas de Lázaro','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02023','Casas de Ves','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02024','Casas-Ibáñez','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02025','Caudete','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02026','Cenizate','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02027','Corral-Rubio','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02028','Cotillas','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02029','Chinchilla de Monte-Aragón','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02030','Elche de la Sierra','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02031','Férez','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02032','Fuensanta','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02033','Fuente-Álamo','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02034','Fuentealbilla','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02035','Gineta (La)','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02036','Golosalvo','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02037','Hellín','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02038','Herrera (La)','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02039','Higueruela','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02040','Hoya-Gonzalo','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02041','Jorquera','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02042','Letur','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02043','Lezuza','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02044','Liétor','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02045','Madrigueras','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02046','Mahora','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02047','Masegoso','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02048','Minaya','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02049','Molinicos','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02050','Montalvos','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02051','Montealegre del Castillo','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02052','Motilleja','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02053','Munera','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02054','Navas de Jorquera','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02055','Nerpio','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02056','Ontur','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02057','Ossa de Montiel','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02058','Paterna del Madera','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02059','Peñascosa','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02060','Peñas de San Pedro','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02061','Pétrola','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02062','Povedilla','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02063','Pozohondo','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02064','Pozo-Lorente','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02065','Pozuelo','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02066','Recueja (La)','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02067','Riópar','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02068','Robledo','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02069','Roda (La)','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02070','Salobre','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02071','San Pedro','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02072','Socovos','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02073','Tarazona de la Mancha','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02074','Tobarra','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02075','Valdeganga','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02076','Vianos','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02077','Villa de Ves','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02078','Villalgordo del Júcar','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02079','Villamalea','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02080','Villapalacios','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02081','Villarrobledo','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02082','Villatoya','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02083','Villavaliente','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02084','Villaverde de Guadalimar','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02085','Viveros','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02086','Yeste','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'02901','Pozo Cañada','AB',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03001','Atzúbia, l''','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03002','Agost','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03003','Agres','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03004','Aigües','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03005','Albatera','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03006','Alcalalí','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03007','Alcocer de Planes','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03008','Alcoleja','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03009','Alcoy/Alcoi','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03010','Alfafara','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03011','Alfàs del Pi (l'')','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03012','Algorfa','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03013','Algueña','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03014','Alicante/Alacant','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03015','Almoradí','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03016','Almudaina','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03017','Alqueria d''Asnar (l'')','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03018','Altea','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03019','Aspe','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03020','Balones','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03021','Banyeres de Mariola','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03022','Benasau','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03023','Beneixama','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03024','Benejúzar','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03025','Benferri','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03026','Beniarbeig','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03027','Beniardá','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03028','Beniarrés','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03029','Benigembla','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03030','Benidoleig','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03031','Benidorm','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03032','Benifallim','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03033','Benifato','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03034','Benijófar','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03035','Benilloba','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03036','Benillup','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03037','Benimantell','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03038','Benimarfull','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03039','Benimassot','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03040','Benimeli','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03041','Benissa','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03042','Benitachell/Poble Nou de Benitatxell (el)','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03043','Biar','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03044','Bigastro','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03045','Bolulla','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03046','Busot','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03047','Calp','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03048','Callosa d''en Sarrià','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03049','Callosa de Segura','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03050','Campello (el)','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03051','Campo de Mirra/Camp de Mirra (el)','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03052','Cañada','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03053','Castalla','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03054','Castell de Castells','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03055','Catral','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03056','Cocentaina','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03057','Confrides','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03058','Cox','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03059','Crevillent','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03060','Quatretondeta','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03061','Daya Nueva','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03062','Daya Vieja','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03063','Dénia','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03064','Dolores','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03065','Elche/Elx','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03066','Elda','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03067','Facheca','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03068','Famorca','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03069','Finestrat','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03070','Formentera del Segura','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03071','Gata de Gorgos','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03072','Gaianes','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03073','Gorga','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03074','Granja de Rocamora','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03075','Castell de Guadalest (el)','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03076','Guardamar del Segura','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03077','Fondó de les Neus (el)/Hondón de las Nieves','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03078','Hondón de los Frailes','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03079','Ibi','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03080','Jacarilla','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03081','Xaló','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03082','Jávea/Xàbia','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03083','Jijona/Xixona','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03084','Lorcha/Orxa (l'')','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03085','Llíber','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03086','Millena','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03088','Monforte del Cid','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03089','Monóvar/Monòver','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03090','Mutxamel','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03091','Murla','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03092','Muro de Alcoy','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03093','Novelda','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03094','Nucia (la)','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03095','Ondara','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03096','Onil','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03097','Orba','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03098','Orxeta','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03099','Orihuela','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03100','Parcent','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03101','Pedreguer','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03102','Pego','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03103','Penàguila','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03104','Petrer','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03105','Pinós (el)/Pinoso','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03106','Planes','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03107','Polop','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03109','Rafal','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03110','Ràfol d''Almúnia (El)','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03111','Redován','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03112','Relleu','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03113','Rojales','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03114','Romana (la)','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03115','Sagra','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03116','Salinas','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03117','Sanet y Negrals','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03118','San Fulgencio','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03119','Sant Joan d''Alacant','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03120','San Miguel de Salinas','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03121','Santa Pola','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03122','San Vicente del Raspeig/Sant Vicent del Raspeig','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03123','Sax','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03124','Sella','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03125','Senija','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03127','Tàrbena','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03128','Teulada','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03129','Tibi','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03130','Tollos','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03131','Tormos','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03132','Torremanzanas/Torre de les Maçanes (la)','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03133','Torrevieja','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03134','Vall d''Alcalà (la)','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03135','Vall d''Ebo (la)','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03136','Vall de Gallinera','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03137','Vall de Laguar (la)','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03138','Verger (el)','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03139','Villajoyosa/Vila Joiosa (la)','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03140','Villena','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03901','Poblets (els)','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03902','Pilar de la Horadada','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03903','Montesinos (Los)','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'03904','San Isidro','A',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04001','Abla','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04002','Abrucena','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04003','Adra','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04004','Albanchez','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04005','Alboloduy','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04006','Albox','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04007','Alcolea','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04008','Alcóntar','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04009','Alcudia de Monteagud','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04010','Alhabia','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04011','Alhama de Almería','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04012','Alicún','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04013','Almería','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04014','Almócita','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04015','Alsodux','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04016','Antas','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04017','Arboleas','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04018','Armuña de Almanzora','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04019','Bacares','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04020','Bayárcal','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04021','Bayarque','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04022','Bédar','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04023','Beires','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04024','Benahadux','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04026','Benitagla','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04027','Benizalón','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04028','Bentarique','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04029','Berja','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04030','Canjáyar','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04031','Cantoria','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04032','Carboneras','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04033','Castro de Filabres','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04034','Cóbdar','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04035','Cuevas del Almanzora','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04036','Chercos','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04037','Chirivel','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04038','Dalías','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04041','Enix','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04043','Felix','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04044','Fines','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04045','Fiñana','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04046','Fondón','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04047','Gádor','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04048','Gallardos (Los)','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04049','Garrucha','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04050','Gérgal','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04051','Huécija','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04052','Huércal de Almería','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04053','Huércal-Overa','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04054','Íllar','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04055','Instinción','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04056','Laroya','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04057','Láujar de Andarax','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04058','Líjar','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04059','Lubrín','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04060','Lucainena de las Torres','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04061','Lúcar','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04062','Macael','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04063','María','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04064','Mojácar','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04065','Nacimiento','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04066','Níjar','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04067','Ohanes','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04068','Olula de Castro','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04069','Olula del Río','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04070','Oria','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04071','Padules','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04072','Partaloa','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04073','Paterna del Río','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04074','Pechina','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04075','Pulpí','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04076','Purchena','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04077','Rágol','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04078','Rioja','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04079','Roquetas de Mar','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04080','Santa Cruz de Marchena','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04081','Santa Fe de Mondújar','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04082','Senés','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04083','Serón','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04084','Sierro','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04085','Somontín','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04086','Sorbas','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04087','Suflí','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04088','Tabernas','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04089','Taberno','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04090','Tahal','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04091','Terque','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04092','Tíjola','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04093','Turre','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04094','Turrillas','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04095','Uleila del Campo','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04096','Urrácal','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04097','Velefique','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04098','Vélez-Blanco','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04099','Vélez-Rubio','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04100','Vera','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04101','Viator','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04102','Vícar','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04103','Zurgena','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04901','Tres Villas (Las)','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04902','Ejido (El)','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04903','Mojonera (La)','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'04904','Balanegra','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05001','Adanero','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05002','Adrada (La)','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05005','Albornos','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05007','Aldeanueva de Santa Cruz','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05008','Aldeaseca','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05010','Aldehuela (La)','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05012','Amavida','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05013','Arenal (El)','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05014','Arenas de San Pedro','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05015','Arevalillo','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05016','Arévalo','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05017','Aveinte','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05018','Avellaneda','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05019','Ávila','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05021','Barco de Ávila (El)','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05022','Barraco (El)','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05023','Barromán','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05024','Becedas','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05025','Becedillas','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05026','Bercial de Zapardiel','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05027','Berlanas (Las)','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05029','Bernuy-Zapardiel','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05030','Berrocalejo de Aragona','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05033','Blascomillán','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05034','Blasconuño de Matacabras','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05035','Blascosancho','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05036','Bohodón (El)','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05037','Bohoyo','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05038','Bonilla de la Sierra','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05039','Brabos','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05040','Bularros','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05041','Burgohondo','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05042','Cabezas de Alambre','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05043','Cabezas del Pozo','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05044','Cabezas del Villar','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05045','Cabizuela','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05046','Canales','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05047','Candeleda','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05048','Cantiveros','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05049','Cardeñosa','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05051','Carrera (La)','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05052','Casas del Puerto','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05053','Casasola','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05054','Casavieja','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05055','Casillas','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05056','Castellanos de Zapardiel','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05057','Cebreros','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05058','Cepeda la Mora','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05059','Cillán','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05060','Cisla','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05061','Colilla (La)','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05062','Collado de Contreras','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05063','Collado del Mirón','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05064','Constanzana','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05065','Crespos','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05066','Cuevas del Valle','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05067','Chamartín','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05069','Donjimeno','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05070','Donvidas','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05072','Espinosa de los Caballeros','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05073','Flores de Ávila','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05074','Fontiveros','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05075','Fresnedilla','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05076','Fresno (El)','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05077','Fuente el Saúz','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05078','Fuentes de Año','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05079','Gallegos de Altamiros','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05080','Gallegos de Sobrinos','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05081','Garganta del Villar','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05082','Gavilanes','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05083','Gemuño','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05084','Gilbuena','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05085','Gil García','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05086','Gimialcón','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05087','Gotarrendura','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05088','Grandes y San Martín','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05089','Guisando','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05090','Gutierre-Muñoz','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05092','Hernansancho','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05093','Herradón de Pinares','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05094','Herreros de Suso','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05095','Higuera de las Dueñas','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05096','Hija de Dios (La)','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05097','Horcajada (La)','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05099','Horcajo de las Torres','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05100','Hornillo (El)','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05101','Hoyocasero','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05102','Hoyo de Pinares (El)','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05103','Hoyorredondo','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05104','Hoyos del Collado','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05105','Hoyos del Espino','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05106','Hoyos de Miguel Muñoz','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05107','Hurtumpascual','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05108','Junciana','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05109','Langa','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05110','Lanzahíta','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05112','Losar del Barco (El)','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05113','Llanos de Tormes (Los)','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05114','Madrigal de las Altas Torres','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05115','Maello','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05116','Malpartida de Corneja','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05117','Mamblas','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05118','Mancera de Arriba','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05119','Manjabálago y Ortigosa de Rioalmar','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05120','Marlín','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05121','Martiherrero','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05122','Martínez','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05123','Mediana de Voltoya','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05124','Medinilla','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05125','Mengamuñoz','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05126','Mesegar de Corneja','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05127','Mijares','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05128','Mingorría','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05129','Mirón (El)','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05130','Mironcillo','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05131','Mirueña de los Infanzones','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05132','Mombeltrán','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05133','Monsalupe','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05134','Moraleja de Matacabras','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05135','Muñana','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05136','Muñico','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05138','Muñogalindo','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05139','Muñogrande','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05140','Muñomer del Peco','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05141','Muñopepe','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05142','Muñosancho','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05143','Muñotello','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05144','Narrillos del Álamo','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05145','Narrillos del Rebollar','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05147','Narros del Castillo','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05148','Narros del Puerto','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05149','Narros de Saldueña','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05151','Navacepedilla de Corneja','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05152','Nava de Arévalo','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05153','Nava del Barco','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05154','Navadijos','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05155','Navaescurial','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05156','Navahondilla','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05157','Navalacruz','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05158','Navalmoral','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05159','Navalonguilla','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05160','Navalosa','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05161','Navalperal de Pinares','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05162','Navalperal de Tormes','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05163','Navaluenga','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05164','Navaquesera','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05165','Navarredonda de Gredos','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05166','Navarredondilla','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05167','Navarrevisca','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05168','Navas del Marqués (Las)','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05169','Navatalgordo','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05170','Navatejares','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05171','Neila de San Miguel','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05172','Niharra','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05173','Ojos-Albos','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05174','Orbita','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05175','Oso (El)','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05176','Padiernos','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05177','Pajares de Adaja','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05178','Palacios de Goda','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05179','Papatrigo','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05180','Parral (El)','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05181','Pascualcobo','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05182','Pedro Bernardo','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05183','Pedro-Rodríguez','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05184','Peguerinos','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05185','Peñalba de Ávila','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05186','Piedrahíta','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05187','Piedralaves','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05188','Poveda','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05189','Poyales del Hoyo','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05190','Pozanco','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05191','Pradosegar','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05192','Puerto Castilla','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05193','Rasueros','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05194','Riocabado','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05195','Riofrío','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05196','Rivilla de Barajas','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05197','Salobral','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05198','Salvadiós','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05199','San Bartolomé de Béjar','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05200','San Bartolomé de Corneja','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05201','San Bartolomé de Pinares','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05204','Sanchidrián','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05205','Sanchorreja','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05206','San Esteban de los Patos','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05207','San Esteban del Valle','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05208','San Esteban de Zapardiel','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05209','San García de Ingelmos','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05210','San Juan de la Encinilla','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05211','San Juan de la Nava','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05212','San Juan del Molinillo','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05213','San Juan del Olmo','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05214','San Lorenzo de Tormes','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05215','San Martín de la Vega del Alberche','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05216','San Martín del Pimpollar','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05217','San Miguel de Corneja','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05218','San Miguel de Serrezuela','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05219','San Pascual','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05220','San Pedro del Arroyo','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05221','Santa Cruz del Valle','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05222','Santa Cruz de Pinares','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05224','Santa María del Arroyo','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05225','Santa María del Berrocal','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05226','Santa María de los Caballeros','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05227','Santa María del Tiétar','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05228','Santiago del Collado','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05229','Santo Domingo de las Posadas','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05230','Santo Tomé de Zabarcos','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05231','San Vicente de Arévalo','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05232','Serrada (La)','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05233','Serranillos','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05234','Sigeres','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05235','Sinlabajos','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05236','Solana de Ávila','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05237','Solana de Rioalmar','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05238','Solosancho','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05239','Sotalbo','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05240','Sotillo de la Adrada','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05241','Tiemblo (El)','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05242','Tiñosillos','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05243','Tolbaños','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05244','Tormellas','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05245','Tornadizos de Ávila','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05246','Tórtoles','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05247','Torre (La)','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05249','Umbrías','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05251','Vadillo de la Sierra','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05252','Valdecasa','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05253','Vega de Santa María','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05254','Velayos','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05256','Villaflor','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05257','Villafranca de la Sierra','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05258','Villanueva de Gómez','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05259','Villanueva del Aceral','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05260','Villanueva del Campillo','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05261','Villar de Corneja','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05262','Villarejo del Valle','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05263','Villatoro','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05264','Viñegra de Moraña','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05265','Vita','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05266','Zapardiel de la Cañada','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05267','Zapardiel de la Ribera','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05901','San Juan de Gredos','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05902','Santa María del Cubillo','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05903','Diego del Carpio','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05904','Santiago del Tormes','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'05905','Villanueva de Ávila','AV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06001','Acedera','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06002','Aceuchal','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06003','Ahillones','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06004','Alange','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06005','Albuera (La)','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06006','Alburquerque','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06007','Alconchel','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06008','Alconera','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06009','Aljucén','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06010','Almendral','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06011','Almendralejo','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06012','Arroyo de San Serván','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06013','Atalaya','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06014','Azuaga','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06015','Badajoz','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06016','Barcarrota','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06017','Baterno','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06018','Benquerencia de la Serena','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06019','Berlanga','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06020','Bienvenida','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06021','Bodonal de la Sierra','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06022','Burguillos del Cerro','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06023','Cabeza del Buey','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06024','Cabeza la Vaca','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06025','Calamonte','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06026','Calera de León','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06027','Calzadilla de los Barros','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06028','Campanario','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06029','Campillo de Llerena','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06030','Capilla','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06031','Carmonita','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06032','Carrascalejo (El)','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06033','Casas de Don Pedro','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06034','Casas de Reina','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06035','Castilblanco','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06036','Castuera','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06037','Codosera (La)','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06038','Cordobilla de Lácara','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06039','Coronada (La)','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06040','Corte de Peleas','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06041','Cristina','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06042','Cheles','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06043','Don Álvaro','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06044','Don Benito','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06045','Entrín Bajo','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06046','Esparragalejo','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06047','Esparragosa de la Serena','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06048','Esparragosa de Lares','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06049','Feria','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06050','Fregenal de la Sierra','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06051','Fuenlabrada de los Montes','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06052','Fuente de Cantos','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06053','Fuente del Arco','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06054','Fuente del Maestre','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06055','Fuentes de León','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06056','Garbayuela','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06057','Garlitos','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06058','Garrovilla (La)','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06059','Granja de Torrehermosa','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06060','Guareña','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06061','Haba (La)','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06062','Helechosa de los Montes','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06063','Herrera del Duque','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06064','Higuera de la Serena','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06065','Higuera de Llerena','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06066','Higuera de Vargas','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06067','Higuera la Real','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06068','Hinojosa del Valle','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06069','Hornachos','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06070','Jerez de los Caballeros','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06071','Lapa (La)','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06072','Lobón','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06073','Llera','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06074','Llerena','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06075','Magacela','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06076','Maguilla','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06077','Malcocinado','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06078','Malpartida de la Serena','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06079','Manchita','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06080','Medellín','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06081','Medina de las Torres','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06082','Mengabril','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06083','Mérida','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06084','Mirandilla','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06085','Monesterio','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06086','Montemolín','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06087','Monterrubio de la Serena','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06088','Montijo','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06089','Morera (La)','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06090','Nava de Santiago (La)','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06091','Navalvillar de Pela','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06092','Nogales','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06093','Oliva de la Frontera','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06094','Oliva de Mérida','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06095','Olivenza','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06096','Orellana de la Sierra','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06097','Orellana la Vieja','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06098','Palomas','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06099','Parra (La)','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06100','Peñalsordo','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06101','Peraleda del Zaucejo','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06102','Puebla de Alcocer','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06103','Puebla de la Calzada','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06104','Puebla de la Reina','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06105','Puebla del Maestre','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06106','Puebla del Prior','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06107','Puebla de Obando','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06108','Puebla de Sancho Pérez','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06109','Quintana de la Serena','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06110','Reina','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06111','Rena','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06112','Retamal de Llerena','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06113','Ribera del Fresno','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06114','Risco','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06115','Roca de la Sierra (La)','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06116','Salvaleón','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06117','Salvatierra de los Barros','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06118','Sancti-Spíritus','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06119','San Pedro de Mérida','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06120','Santa Amalia','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06121','Santa Marta','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06122','Santos de Maimona (Los)','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06123','San Vicente de Alcántara','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06124','Segura de León','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06125','Siruela','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06126','Solana de los Barros','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06127','Talarrubias','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06128','Talavera la Real','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06129','Táliga','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06130','Tamurejo','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06131','Torre de Miguel Sesmero','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06132','Torremayor','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06133','Torremejía','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06134','Trasierra','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06135','Trujillanos','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06136','Usagre','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06137','Valdecaballeros','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06138','Valdetorres','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06139','Valencia de las Torres','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06140','Valencia del Mombuey','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06141','Valencia del Ventoso','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06142','Valverde de Burguillos','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06143','Valverde de Leganés','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06144','Valverde de Llerena','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06145','Valverde de Mérida','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06146','Valle de la Serena','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06147','Valle de Matamoros','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06148','Valle de Santa Ana','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06149','Villafranca de los Barros','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06150','Villagarcía de la Torre','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06151','Villagonzalo','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06152','Villalba de los Barros','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06153','Villanueva de la Serena','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06154','Villanueva del Fresno','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06155','Villar del Rey','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06156','Villar de Rena','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06157','Villarta de los Montes','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06158','Zafra','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06159','Zahínos','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06160','Zalamea de la Serena','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06161','Zarza-Capilla','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06162','Zarza (La)','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06901','Valdelacalzada','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06902','Pueblonuevo del Guadiana','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'06903','Guadiana del Caudillo','BA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08001','Abrera','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08002','Aguilar de Segarra','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08003','Alella','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08004','Alpens','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08005','Ametlla del Vallès (L'')','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08006','Arenys de Mar','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08007','Arenys de Munt','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08008','Argençola','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08009','Argentona','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08010','Artés','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08011','Avià','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08012','Avinyó','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08013','Avinyonet del Penedès','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08014','Aiguafreda','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08015','Badalona','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08016','Bagà','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08017','Balenyà','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08018','Balsareny','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08019','Barcelona','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08020','Begues','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08021','Bellprat','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08022','Berga','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08023','Bigues i Riells','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08024','Borredà','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08025','Bruc (El)','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08026','Brull (El)','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08027','Cabanyes (Les)','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08028','Cabrera d''Anoia','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08029','Cabrera de Mar','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08030','Cabrils','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08031','Calaf','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08032','Caldes d''Estrac','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08033','Caldes de Montbui','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08034','Calders','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08035','Calella','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08036','Calonge de Segarra','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08037','Calldetenes','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08038','Callús','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08039','Campins','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08040','Canet de Mar','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08041','Canovelles','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08042','Cànoves i Samalús','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08043','Canyelles','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08044','Capellades','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08045','Capolat','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08046','Cardedeu','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08047','Cardona','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08048','Carme','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08049','Casserres','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08050','Castellar del Riu','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08051','Castellar del Vallès','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08052','Castellar de n''Hug','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08053','Castellbell i el Vilar','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08054','Castellbisbal','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08055','Castellcir','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08056','Castelldefels','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08057','Castell de l''Areny','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08058','Castellet i la Gornal','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08059','Castellfollit del Boix','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08060','Castellfollit de Riubregós','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08061','Castellgalí','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08062','Castellnou de Bages','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08063','Castellolí','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08064','Castellterçol','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08065','Castellví de la Marca','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08066','Castellví de Rosanes','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08067','Centelles','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08068','Cervelló','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08069','Collbató','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08070','Collsuspina','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08071','Copons','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08072','Corbera de Llobregat','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08073','Cornellà de Llobregat','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08074','Cubelles','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08075','Dosrius','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08076','Esparreguera','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08077','Esplugues de Llobregat','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08078','Espunyola (L'')','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08079','Estany (L'')','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08080','Fígols','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08081','Fogars de Montclús','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08082','Fogars de la Selva','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08083','Folgueroles','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08084','Fonollosa','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08085','Font-rubí','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08086','Franqueses del Vallès (Les)','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08087','Gallifa','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08088','Garriga (La)','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08089','Gavà','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08090','Gaià','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08091','Gelida','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08092','Gironella','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08093','Gisclareny','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08094','Granada (La)','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08095','Granera','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08096','Granollers','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08097','Gualba','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08098','Sant Salvador de Guardiola','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08099','Guardiola de Berguedà','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08100','Gurb','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08101','Hospitalet de Llobregat (L'')','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08102','Igualada','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08103','Jorba','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08104','Llacuna (La)','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08105','Llagosta (La)','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08106','Llinars del Vallès','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08107','Lliçà d''Amunt','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08108','Lliçà de Vall','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08109','Lluçà','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08110','Malgrat de Mar','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08111','Malla','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08112','Manlleu','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08113','Manresa','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08114','Martorell','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08115','Martorelles','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08116','Masies de Roda (Les)','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08117','Masies de Voltregà (Les)','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08118','Masnou (El)','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08119','Masquefa','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08120','Matadepera','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08121','Mataró','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08122','Mediona','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08123','Molins de Rei','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08124','Mollet del Vallès','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08125','Montcada i Reixac','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08126','Montgat','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08127','Monistrol de Montserrat','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08128','Monistrol de Calders','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08129','Muntanyola','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08130','Montclar','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08131','Montesquiu','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08132','Montmajor','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08133','Montmaneu','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08134','Figaró-Montmany','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08135','Montmeló','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08136','Montornès del Vallès','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08137','Montseny','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08138','Moià','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08139','Mura','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08140','Navarcles','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08141','Navàs','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08142','Nou de Berguedà (La)','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08143','Òdena','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08144','Olvan','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08145','Olèrdola','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08146','Olesa de Bonesvalls','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08147','Olesa de Montserrat','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08148','Olivella','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08149','Olost','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08150','Orís','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08151','Oristà','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08152','Orpí','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08153','Òrrius','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08154','Pacs del Penedès','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08155','Palafolls','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08156','Palau-solità i Plegamans','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08157','Pallejà','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08158','Papiol (El)','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08159','Parets del Vallès','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08160','Perafita','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08161','Piera','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08162','Hostalets de Pierola (Els)','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08163','Pineda de Mar','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08164','Pla del Penedès (El)','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08165','Pobla de Claramunt (La)','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08166','Pobla de Lillet (La)','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08167','Polinyà','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08168','Pontons','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08169','Prat de Llobregat (El)','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08170','Prats de Rei (Els)','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08171','Prats de Lluçanès','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08172','Premià de Mar','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08174','Puigdàlber','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08175','Puig-reig','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08176','Pujalt','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08177','Quar (La)','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08178','Rajadell','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08179','Rellinars','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08180','Ripollet','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08181','Roca del Vallès (La)','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08182','Pont de Vilomara i Rocafort (El)','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08183','Roda de Ter','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08184','Rubí','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08185','Rubió','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08187','Sabadell','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08188','Sagàs','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08189','Sant Pere Sallavinera','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08190','Saldes','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08191','Sallent','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08192','Santpedor','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08193','Sant Iscle de Vallalta','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08194','Sant Adrià de Besòs','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08195','Sant Agustí de Lluçanès','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08196','Sant Andreu de la Barca','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08197','Sant Andreu de Llavaneres','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08198','Sant Antoni de Vilamajor','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08199','Sant Bartomeu del Grau','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08200','Sant Boi de Llobregat','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08201','Sant Boi de Lluçanès','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08202','Sant Celoni','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08203','Sant Cebrià de Vallalta','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08204','Sant Climent de Llobregat','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08205','Sant Cugat del Vallès','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08206','Sant Cugat Sesgarrigues','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08207','Sant Esteve de Palautordera','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08208','Sant Esteve Sesrovires','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08209','Sant Fost de Campsentelles','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08210','Sant Feliu de Codines','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08211','Sant Feliu de Llobregat','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08212','Sant Feliu Sasserra','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08213','Sant Fruitós de Bages','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08214','Vilassar de Dalt','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08215','Sant Hipòlit de Voltregà','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08216','Sant Jaume de Frontanyà','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08217','Sant Joan Despí','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08218','Sant Joan de Vilatorrada','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08219','Vilassar de Mar','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08220','Sant Julià de Vilatorta','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08221','Sant Just Desvern','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08222','Sant Llorenç d''Hortons','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08223','Sant Llorenç Savall','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08224','Sant Martí de Centelles','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08225','Sant Martí d''Albars','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08226','Sant Martí de Tous','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08227','Sant Martí Sarroca','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08228','Sant Martí Sesgueioles','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08229','Sant Mateu de Bages','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08230','Premià de Dalt','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08231','Sant Pere de Ribes','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08232','Sant Pere de Riudebitlles','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08233','Sant Pere de Torelló','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08234','Sant Pere de Vilamajor','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08235','Sant Pol de Mar','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08236','Sant Quintí de Mediona','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08237','Sant Quirze de Besora','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08238','Sant Quirze del Vallès','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08239','Sant Quirze Safaja','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08240','Sant Sadurní d''Anoia','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08241','Sant Sadurní d''Osormort','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08242','Marganell','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08243','Santa Cecília de Voltregà','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08244','Santa Coloma de Cervelló','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08245','Santa Coloma de Gramenet','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08246','Santa Eugènia de Berga','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08247','Santa Eulàlia de Riuprimer','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08248','Santa Eulàlia de Ronçana','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08249','Santa Fe del Penedès','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08250','Santa Margarida de Montbui','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08251','Santa Margarida i els Monjos','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08252','Barberà del Vallès','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08253','Santa Maria de Besora','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08254','Esquirol, L''','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08255','Santa Maria de Merlès','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08256','Santa Maria de Martorelles','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08257','Santa Maria de Miralles','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08258','Santa Maria d''Oló','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08259','Santa Maria de Palautordera','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08260','Santa Perpètua de Mogoda','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08261','Santa Susanna','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08262','Sant Vicenç de Castellet','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08263','Sant Vicenç dels Horts','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08264','Sant Vicenç de Montalt','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08265','Sant Vicenç de Torelló','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08266','Cerdanyola del Vallès','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08267','Sentmenat','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08268','Cercs','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08269','Seva','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08270','Sitges','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08271','Sobremunt','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08272','Sora','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08273','Subirats','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08274','Súria','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08275','Tavèrnoles','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08276','Tagamanent','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08277','Talamanca','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08278','Taradell','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08279','Terrassa','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08280','Tavertet','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08281','Teià','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08282','Tiana','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08283','Tona','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08284','Tordera','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08285','Torelló','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08286','Torre de Claramunt (La)','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08287','Torrelavit','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08288','Torrelles de Foix','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08289','Torrelles de Llobregat','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08290','Ullastrell','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08291','Vacarisses','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08292','Vallbona d''Anoia','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08293','Vallcebre','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08294','Vallgorguina','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08295','Vallirana','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08296','Vallromanes','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08297','Veciana','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08298','Vic','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08299','Vilada','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08300','Viladecavalls','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08301','Viladecans','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08302','Vilanova del Camí','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08303','Vilanova de Sau','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08304','Vilobí del Penedès','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08305','Vilafranca del Penedès','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08306','Vilalba Sasserra','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08307','Vilanova i la Geltrú','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08308','Viver i Serrateix','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08901','Rupit i Pruit','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08902','Vilanova del Vallès','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08903','Sant Julià de Cerdanyola','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08904','Badia del Vallès','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'08905','Palma de Cervelló (La)','B',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09001','Abajas','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09003','Adrada de Haza','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09006','Aguas Cándidas','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09007','Aguilar de Bureba','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09009','Albillos','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09010','Alcocero de Mola','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09011','Alfoz de Bricia','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09012','Alfoz de Santa Gadea','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09013','Altable','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09014','Altos (Los)','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09016','Ameyugo','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09017','Anguix','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09018','Aranda de Duero','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09019','Arandilla','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09020','Arauzo de Miel','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09021','Arauzo de Salce','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09022','Arauzo de Torre','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09023','Arcos','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09024','Arenillas de Riopisuerga','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09025','Arija','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09026','Arlanzón','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09027','Arraya de Oca','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09029','Atapuerca','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09030','Ausines (Los)','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09032','Avellanosa de Muñó','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09033','Bahabón de Esgueva','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09034','Balbases (Los)','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09035','Baños de Valdearados','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09036','Bañuelos de Bureba','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09037','Barbadillo de Herreros','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09038','Barbadillo del Mercado','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09039','Barbadillo del Pez','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09041','Barrio de Muñó','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09043','Barrios de Bureba (Los)','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09044','Barrios de Colina','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09045','Basconcillos del Tozo','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09046','Bascuñana','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09047','Belbimbre','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09048','Belorado','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09050','Berberana','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09051','Berlangas de Roa','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09052','Berzosa de Bureba','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09054','Bozoó','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09055','Brazacorta','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09056','Briviesca','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09057','Bugedo','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09058','Buniel','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09059','Burgos','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09060','Busto de Bureba','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09061','Cabañes de Esgueva','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09062','Cabezón de la Sierra','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09063','Cavia','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09064','Caleruega','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09065','Campillo de Aranda','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09066','Campolara','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09067','Canicosa de la Sierra','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09068','Cantabrana','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09070','Carazo','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09071','Carcedo de Bureba','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09072','Carcedo de Burgos','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09073','Cardeñadijo','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09074','Cardeñajimeno','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09075','Cardeñuela Riopico','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09076','Carrias','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09077','Cascajares de Bureba','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09078','Cascajares de la Sierra','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09079','Castellanos de Castro','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09082','Castildelgado','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09083','Castil de Peones','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09084','Castrillo de la Reina','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09085','Castrillo de la Vega','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09086','Castrillo del Val','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09088','Castrillo de Riopisuerga','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09090','Castrillo Mota de Judíos','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09091','Castrojeriz','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09093','Cayuela','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09094','Cebrecos','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09095','Celada del Camino','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09098','Cerezo de Río Tirón','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09100','Cerratón de Juarros','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09101','Ciadoncha','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09102','Cillaperlata','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09103','Cilleruelo de Abajo','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09104','Cilleruelo de Arriba','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09105','Ciruelos de Cervera','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09108','Cogollos','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09109','Condado de Treviño','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09110','Contreras','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09112','Coruña del Conde','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09113','Covarrubias','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09114','Cubillo del Campo','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09115','Cubo de Bureba','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09117','Cueva de Roa (La)','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09119','Cuevas de San Clemente','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09120','Encío','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09122','Espinosa de Cervera','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09123','Espinosa del Camino','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09124','Espinosa de los Monteros','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09125','Estépar','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09127','Fontioso','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09128','Frandovínez','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09129','Fresneda de la Sierra Tirón','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09130','Fresneña','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09131','Fresnillo de las Dueñas','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09132','Fresno de Río Tirón','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09133','Fresno de Rodilla','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09134','Frías','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09135','Fuentebureba','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09136','Fuentecén','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09137','Fuentelcésped','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09138','Fuentelisendo','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09139','Fuentemolinos','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09140','Fuentenebro','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09141','Fuentespina','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09143','Galbarros','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09144','Gallega (La)','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09148','Grijalba','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09149','Grisaleña','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09151','Gumiel de Izán','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09152','Gumiel de Mercado','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09154','Hacinas','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09155','Haza','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09159','Hontanas','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09160','Hontangas','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09162','Hontoria de la Cantera','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09163','Hontoria del Pinar','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09164','Hontoria de Valdearados','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09166','Hormazas (Las)','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09167','Hornillos del Camino','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09168','Horra (La)','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09169','Hortigüela','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09170','Hoyales de Roa','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09172','Huérmeces','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09173','Huerta de Arriba','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09174','Huerta de Rey','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09175','Humada','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09176','Hurones','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09177','Ibeas de Juarros','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09178','Ibrillos','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09179','Iglesiarrubia','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09180','Iglesias','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09181','Isar','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09182','Itero del Castillo','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09183','Jaramillo de la Fuente','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09184','Jaramillo Quemado','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09189','Junta de Traslaloma','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09190','Junta de Villalba de Losa','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09191','Jurisdicción de Lara','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09192','Jurisdicción de San Zadornil','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09194','Lerma','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09195','Llano de Bureba','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09196','Madrigal del Monte','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09197','Madrigalejo del Monte','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09198','Mahamud','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09199','Mambrilla de Castrejón','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09200','Mambrillas de Lara','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09201','Mamolar','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09202','Manciles','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09206','Mazuela','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09208','Mecerreyes','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09209','Medina de Pomar','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09211','Melgar de Fernamental','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09213','Merindad de Cuesta-Urria','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09214','Merindad de Montija','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09215','Merindad de Sotoscueva','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09216','Merindad de Valdeporres','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09217','Merindad de Valdivielso','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09218','Milagros','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09219','Miranda de Ebro','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09220','Miraveche','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09221','Modúbar de la Emparedada','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09223','Monasterio de la Sierra','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09224','Monasterio de Rodilla','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09225','Moncalvillo','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09226','Monterrubio de la Demanda','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09227','Montorio','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09228','Moradillo de Roa','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09229','Nava de Roa','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09230','Navas de Bureba','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09231','Nebreda','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09232','Neila','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09235','Olmedillo de Roa','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09236','Olmillos de Muñó','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09238','Oña','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09239','Oquillas','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09241','Orbaneja Riopico','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09242','Padilla de Abajo','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09243','Padilla de Arriba','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09244','Padrones de Bureba','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09246','Palacios de la Sierra','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09247','Palacios de Riopisuerga','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09248','Palazuelos de la Sierra','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09249','Palazuelos de Muñó','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09250','Pampliega','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09251','Pancorbo','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09253','Pardilla','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09255','Partido de la Sierra en Tobalina','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09256','Pedrosa de Duero','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09257','Pedrosa del Páramo','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09258','Pedrosa del Príncipe','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09259','Pedrosa de Río Úrbel','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09261','Peñaranda de Duero','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09262','Peral de Arlanza','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09265','Piérnigas','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09266','Pineda de la Sierra','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09267','Pineda Trasmonte','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09268','Pinilla de los Barruecos','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09269','Pinilla de los Moros','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09270','Pinilla Trasmonte','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09272','Poza de la Sal','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09273','Prádanos de Bureba','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09274','Pradoluengo','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09275','Presencio','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09276','Puebla de Arganzón (La)','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09277','Puentedura','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09279','Quemada','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09280','Quintanabureba','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09281','Quintana del Pidio','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09283','Quintanaélez','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09287','Quintanaortuño','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09288','Quintanapalla','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09289','Quintanar de la Sierra','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09292','Quintanavides','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09294','Quintanilla de la Mata','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09295','Quintanilla del Coco','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09297','Quintanillas (Las)','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09298','Quintanilla San García','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09301','Quintanilla Vivar','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09302','Rabanera del Pinar','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09303','Rábanos','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09304','Rabé de las Calzadas','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09306','Rebolledo de la Torre','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09307','Redecilla del Camino','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09308','Redecilla del Campo','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09309','Regumiel de la Sierra','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09310','Reinoso','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09311','Retuerta','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09312','Revilla y Ahedo (La)','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09314','Revilla del Campo','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09315','Revillarruz','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09316','Revilla Vallejera','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09317','Rezmondo','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09318','Riocavado de la Sierra','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09321','Roa','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09323','Rojas','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09325','Royuela de Río Franco','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09326','Rubena','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09327','Rublacedo de Abajo','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09328','Rucandio','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09329','Salas de Bureba','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09330','Salas de los Infantes','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09332','Saldaña de Burgos','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09334','Salinillas de Bureba','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09335','San Adrián de Juarros','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09337','San Juan del Monte','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09338','San Mamés de Burgos','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09339','San Martín de Rubiales','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09340','San Millán de Lara','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09343','Santa Cecilia','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09345','Santa Cruz de la Salceda','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09346','Santa Cruz del Valle Urbión','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09347','Santa Gadea del Cid','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09348','Santa Inés','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09350','Santa María del Campo','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09351','Santa María del Invierno','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09352','Santa María del Mercadillo','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09353','Santa María Rivarredonda','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09354','Santa Olalla de Bureba','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09355','Santibáñez de Esgueva','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09356','Santibáñez del Val','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09358','Santo Domingo de Silos','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09360','San Vicente del Valle','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09361','Sargentes de la Lora','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09362','Sarracín','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09363','Sasamón','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09365','Sequera de Haza (La)','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09366','Solarana','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09368','Sordillos','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09369','Sotillo de la Ribera','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09372','Sotragero','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09373','Sotresgudo','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09374','Susinos del Páramo','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09375','Tamarón','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09377','Tardajos','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09378','Tejada','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09380','Terradillos de Esgueva','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09381','Tinieblas de la Sierra','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09382','Tobar','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09384','Tordómar','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09386','Torrecilla del Monte','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09387','Torregalindo','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09388','Torrelara','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09389','Torrepadre','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09390','Torresandino','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09391','Tórtoles de Esgueva','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09392','Tosantos','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09394','Trespaderne','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09395','Tubilla del Agua','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09396','Tubilla del Lago','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09398','Úrbel del Castillo','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09400','Vadocondes','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09403','Valdeande','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09405','Valdezate','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09406','Valdorros','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09407','Valmala','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09408','Vallarta de Bureba','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09409','Valle de Manzanedo','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09410','Valle de Mena','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09411','Valle de Oca','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09412','Valle de Tobalina','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09413','Valle de Valdebezana','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09414','Valle de Valdelaguna','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09415','Valle de Valdelucio','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09416','Valle de Zamanzas','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09417','Vallejera','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09418','Valles de Palenzuela','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09419','Valluércanes','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09421','Vid y Barrios (La)','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09422','Vid de Bureba (La)','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09423','Vileña','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09424','Viloria de Rioja','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09425','Vilviestre del Pinar','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09427','Villadiego','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09428','Villaescusa de Roa','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09429','Villaescusa la Sombría','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09430','Villaespasa','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09431','Villafranca Montes de Oca','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09432','Villafruela','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09433','Villagalijo','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09434','Villagonzalo Pedernales','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09437','Villahoz','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09438','Villalba de Duero','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09439','Villalbilla de Burgos','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09440','Villalbilla de Gumiel','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09441','Villaldemiro','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09442','Villalmanzo','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09443','Villamayor de los Montes','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09444','Villamayor de Treviño','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09445','Villambistia','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09446','Villamedianilla','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09447','Villamiel de la Sierra','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09448','Villangómez','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09449','Villanueva de Argaño','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09450','Villanueva de Carazo','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09451','Villanueva de Gumiel','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09454','Villanueva de Teba','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09455','Villaquirán de la Puebla','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09456','Villaquirán de los Infantes','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09458','Villariezo','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09460','Villasandino','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09463','Villasur de Herreros','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09464','Villatuelda','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09466','Villaverde del Monte','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09467','Villaverde-Mogina','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09471','Villayerno Morquillas','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09472','Villazopeque','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09473','Villegas','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09476','Villoruebo','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09478','Vizcaínos','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09480','Zael','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09482','Zarzosa de Río Pisuerga','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09483','Zazuar','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09485','Zuñeda','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09901','Quintanilla del Agua y Tordueles','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09902','Valle de Santibáñez','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09903','Villarcayo de Merindad de Castilla la Vieja','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09904','Valle de las Navas','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09905','Valle de Sedano','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09906','Merindad de Río Ubierna','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09907','Alfoz de Quintanadueñas','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'09908','Valle de Losa','BU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10001','Abadía','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10002','Abertura','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10003','Acebo','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10004','Acehúche','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10005','Aceituna','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10006','Ahigal','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10007','Albalá','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10008','Alcántara','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10009','Alcollarín','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10010','Alcuéscar','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10011','Aldeacentenera','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10012','Aldea del Cano','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10013','Aldea del Obispo (La)','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10014','Aldeanueva de la Vera','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10015','Aldeanueva del Camino','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10016','Aldehuela de Jerte','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10017','Alía','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10018','Aliseda','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10019','Almaraz','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10020','Almoharín','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10021','Arroyo de la Luz','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10022','Arroyomolinos de la Vera','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10023','Arroyomolinos','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10024','Baños de Montemayor','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10025','Barrado','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10026','Belvís de Monroy','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10027','Benquerencia','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10028','Berrocalejo','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10029','Berzocana','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10030','Bohonal de Ibor','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10031','Botija','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10032','Brozas','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10033','Cabañas del Castillo','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10034','Cabezabellosa','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10035','Cabezuela del Valle','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10036','Cabrero','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10037','Cáceres','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10038','Cachorrilla','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10039','Cadalso','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10040','Calzadilla','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10041','Caminomorisco','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10042','Campillo de Deleitosa','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10043','Campo Lugar','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10044','Cañamero','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10045','Cañaveral','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10046','Carbajo','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10047','Carcaboso','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10048','Carrascalejo','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10049','Casar de Cáceres','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10050','Casar de Palomero','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10051','Casares de las Hurdes','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10052','Casas de Don Antonio','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10053','Casas de Don Gómez','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10054','Casas del Castañar','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10055','Casas del Monte','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10056','Casas de Millán','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10057','Casas de Miravete','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10058','Casatejada','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10059','Casillas de Coria','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10060','Castañar de Ibor','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10061','Ceclavín','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10062','Cedillo','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10063','Cerezo','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10064','Cilleros','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10065','Collado de la Vera','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10066','Conquista de la Sierra','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10067','Coria','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10068','Cuacos de Yuste','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10069','Cumbre (La)','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10070','Deleitosa','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10071','Descargamaría','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10072','Eljas','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10073','Escurial','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10075','Fresnedoso de Ibor','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10076','Galisteo','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10077','Garciaz','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10078','Garganta (La)','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10079','Garganta la Olla','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10080','Gargantilla','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10081','Gargüera','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10082','Garrovillas de Alconétar','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10083','Garvín','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10084','Gata','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10085','Gordo (El)','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10086','Granja (La)','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10087','Guadalupe','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10088','Guijo de Coria','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10089','Guijo de Galisteo','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10090','Guijo de Granadilla','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10091','Guijo de Santa Bárbara','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10092','Herguijuela','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10093','Hernán-Pérez','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10094','Herrera de Alcántara','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10095','Herreruela','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10096','Hervás','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10097','Higuera','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10098','Hinojal','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10099','Holguera','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10100','Hoyos','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10101','Huélaga','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10102','Ibahernando','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10103','Jaraicejo','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10104','Jaraíz de la Vera','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10105','Jarandilla de la Vera','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10106','Jarilla','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10107','Jerte','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10108','Ladrillar','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10109','Logrosán','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10110','Losar de la Vera','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10111','Madrigal de la Vera','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10112','Madrigalejo','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10113','Madroñera','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10114','Majadas','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10115','Malpartida de Cáceres','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10116','Malpartida de Plasencia','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10117','Marchagaz','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10118','Mata de Alcántara','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10119','Membrío','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10120','Mesas de Ibor','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10121','Miajadas','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10122','Millanes','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10123','Mirabel','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10124','Mohedas de Granadilla','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10125','Monroy','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10126','Montánchez','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10127','Montehermoso','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10128','Moraleja','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10129','Morcillo','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10130','Navaconcejo','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10131','Navalmoral de la Mata','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10132','Navalvillar de Ibor','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10133','Navas del Madroño','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10134','Navezuelas','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10135','Nuñomoral','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10136','Oliva de Plasencia','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10137','Palomero','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10138','Pasarón de la Vera','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10139','Pedroso de Acim','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10140','Peraleda de la Mata','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10141','Peraleda de San Román','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10142','Perales del Puerto','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10143','Pescueza','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10144','Pesga (La)','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10145','Piedras Albas','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10146','Pinofranqueado','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10147','Piornal','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10148','Plasencia','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10149','Plasenzuela','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10150','Portaje','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10151','Portezuelo','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10152','Pozuelo de Zarzón','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10153','Puerto de Santa Cruz','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10154','Rebollar','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10155','Riolobos','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10156','Robledillo de Gata','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10157','Robledillo de la Vera','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10158','Robledillo de Trujillo','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10159','Robledollano','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10160','Romangordo','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10161','Ruanes','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10162','Salorino','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10163','Salvatierra de Santiago','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10164','San Martín de Trevejo','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10165','Santa Ana','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10166','Santa Cruz de la Sierra','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10167','Santa Cruz de Paniagua','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10168','Santa Marta de Magasca','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10169','Santiago de Alcántara','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10170','Santiago del Campo','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10171','Santibáñez el Alto','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10172','Santibáñez el Bajo','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10173','Saucedilla','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10174','Segura de Toro','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10175','Serradilla','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10176','Serrejón','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10177','Sierra de Fuentes','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10178','Talaván','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10179','Talaveruela de la Vera','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10180','Talayuela','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10181','Tejeda de Tiétar','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10182','Toril','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10183','Tornavacas','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10184','Torno (El)','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10185','Torrecilla de los Ángeles','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10186','Torrecillas de la Tiesa','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10187','Torre de Don Miguel','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10188','Torre de Santa María','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10189','Torrejoncillo','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10190','Torrejón el Rubio','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10191','Torremenga','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10192','Torremocha','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10193','Torreorgaz','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10194','Torrequemada','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10195','Trujillo','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10196','Valdastillas','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10197','Valdecañas de Tajo','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10198','Valdefuentes','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10199','Valdehúncar','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10200','Valdelacasa de Tajo','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10201','Valdemorales','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10202','Valdeobispo','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10203','Valencia de Alcántara','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10204','Valverde de la Vera','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10205','Valverde del Fresno','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10206','Viandar de la Vera','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10207','Villa del Campo','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10208','Villa del Rey','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10209','Villamesías','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10210','Villamiel','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10211','Villanueva de la Sierra','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10212','Villanueva de la Vera','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10213','Villar del Pedroso','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10214','Villar de Plasencia','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10215','Villasbuenas de Gata','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10216','Zarza de Granadilla','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10217','Zarza de Montánchez','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10218','Zarza la Mayor','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10219','Zorita','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10901','Rosalejo','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10902','Vegaviana','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10903','Alagón del Río','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10904','Tiétar','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'10905','Pueblonuevo de Miramontes','CC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11001','Alcalá de los Gazules','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11002','Alcalá del Valle','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11003','Algar','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11004','Algeciras','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11005','Algodonales','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11006','Arcos de la Frontera','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11007','Barbate','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11008','Barrios (Los)','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11009','Benaocaz','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11010','Bornos','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11011','Bosque (El)','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11012','Cádiz','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11013','Castellar de la Frontera','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11014','Conil de la Frontera','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11015','Chiclana de la Frontera','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11016','Chipiona','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11017','Espera','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11018','Gastor (El)','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11019','Grazalema','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11020','Jerez de la Frontera','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11021','Jimena de la Frontera','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11022','Línea de la Concepción (La)','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11023','Medina Sidonia','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11024','Olvera','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11025','Paterna de Rivera','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11026','Prado del Rey','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11027','Puerto de Santa María (El)','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11028','Puerto Real','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11029','Puerto Serrano','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11030','Rota','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11031','San Fernando','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11032','Sanlúcar de Barrameda','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11033','San Roque','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11034','Setenil de las Bodegas','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11035','Tarifa','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11036','Torre Alháquime','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11037','Trebujena','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11038','Ubrique','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11039','Vejer de la Frontera','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11040','Villaluenga del Rosario','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11041','Villamartín','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11042','Zahara','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11901','Benalup-Casas Viejas','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11902','San José del Valle','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'11903','San Martín del Tesorillo','CA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12001','Atzeneta del Maestrat','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12002','Aín','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12003','Albocàsser','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12004','Alcalà de Xivert','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12005','Alcora (l'')','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12006','Alcudia de Veo','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12007','Alfondeguilla','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12008','Algimia de Almonacid','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12009','Almassora','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12010','Almedíjar','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12011','Almenara','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12012','Altura','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12013','Arañuel','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12014','Ares del Maestrat','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12015','Argelita','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12016','Artana','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12017','Ayódar','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12018','Azuébar','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12020','Barracas','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12021','Betxí','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12022','Bejís','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12024','Benafer','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12025','Benafigos','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12026','Benassal','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12027','Benicarló','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12028','Benicasim/Benicàssim','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12029','Benlloc','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12031','Borriol','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12032','Burriana','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12033','Cabanes','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12034','Càlig','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12036','Canet lo Roig','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12037','Castell de Cabres','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12038','Castellfort','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12039','Castellnovo','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12040','Castellón de la Plana/Castelló de la Plana','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12041','Castillo de Villamalefa','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12042','Catí','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12043','Caudiel','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12044','Cervera del Maestre','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12045','Cinctorres','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12046','Cirat','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12048','Cortes de Arenoso','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12049','Costur','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12050','Coves de Vinromà (les)','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12051','Culla','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12052','Xert','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12053','Chilches/Xilxes','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12055','Chodos/Xodos','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12056','Chóvar','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12057','Eslida','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12058','Espadilla','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12059','Fanzara','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12060','Figueroles','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12061','Forcall','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12063','Fuente la Reina','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12064','Fuentes de Ayódar','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12065','Gaibiel','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12067','Geldo','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12068','Herbés','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12069','Higueras','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12070','Jana (la)','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12071','Jérica','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12072','Llucena/Lucena del Cid','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12073','Ludiente','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12074','Llosa (la)','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12075','Mata de Morella (la)','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12076','Matet','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12077','Moncofa','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12078','Montán','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12079','Montanejos','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12080','Morella','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12081','Navajas','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12082','Nules','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12083','Olocau del Rey','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12084','Onda','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12085','Oropesa del Mar/Orpesa','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12087','Palanques','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12088','Pavías','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12089','Peñíscola','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12090','Pina de Montalgrao','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12091','Portell de Morella','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12092','Puebla de Arenoso','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12093','Pobla de Benifassà (la)','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12094','Pobla Tornesa (la)','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12095','Ribesalbes','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12096','Rossell','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12097','Sacañet','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12098','Salzadella (la)','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12099','Sant Jordi/San Jorge','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12100','Sant Mateu','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12101','San Rafael del Río','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12102','Santa Magdalena de Pulpis','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12103','Sarratella, la','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12104','Segorbe','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12105','Sierra Engarcerán','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12106','Soneja','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12107','Sot de Ferrer','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12108','Sueras/Suera','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12109','Tales','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12110','Teresa','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12111','Tírig','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12112','Todolella','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12113','Toga','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12114','Torás','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12115','Toro (El)','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12116','Torralba del Pinar','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12117','Torreblanca','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12118','Torrechiva','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12119','Torre d''En Besora (la)','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12120','Torre Endoménech','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12121','Traiguera','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12122','Useras/Useres (les)','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12123','Vallat','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12124','Vall d''Alba','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12125','Vall de Almonacid','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12126','Vall d''Uixó (la)','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12127','Vallibona','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12128','Vilafamés','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12129','Vilafranca/Villafranca del Cid','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12130','Villahermosa del Río','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12131','Villamalur','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12132','Vilanova d''Alcolea','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12133','Villanueva de Viver','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12134','Vilar de Canes','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12135','Vila-real','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12136','Vilavella (la)','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12137','Villores','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12138','Vinaròs','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12139','Vistabella del Maestrat','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12140','Viver','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12141','Zorita del Maestrazgo','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12142','Zucaina','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12901','Alqueries, les/Alquerías del Niño Perdido','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'12902','Sant Joan de Moró','CS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13001','Abenójar','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13002','Agudo','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13003','Alamillo','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13004','Albaladejo','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13005','Alcázar de San Juan','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13006','Alcoba','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13007','Alcolea de Calatrava','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13008','Alcubillas','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13009','Aldea del Rey','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13010','Alhambra','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13011','Almadén','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13012','Almadenejos','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13013','Almagro','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13014','Almedina','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13015','Almodóvar del Campo','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13016','Almuradiel','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13017','Anchuras','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13018','Arenas de San Juan','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13019','Argamasilla de Alba','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13020','Argamasilla de Calatrava','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13021','Arroba de los Montes','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13022','Ballesteros de Calatrava','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13023','Bolaños de Calatrava','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13024','Brazatortas','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13025','Cabezarados','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13026','Cabezarrubias del Puerto','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13027','Calzada de Calatrava','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13028','Campo de Criptana','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13029','Cañada de Calatrava','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13030','Caracuel de Calatrava','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13031','Carrión de Calatrava','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13032','Carrizosa','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13033','Castellar de Santiago','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13034','Ciudad Real','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13035','Corral de Calatrava','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13036','Cortijos (Los)','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13037','Cózar','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13038','Chillón','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13039','Daimiel','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13040','Fernán Caballero','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13041','Fontanarejo','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13042','Fuencaliente','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13043','Fuenllana','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13044','Fuente el Fresno','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13045','Granátula de Calatrava','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13046','Guadalmez','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13047','Herencia','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13048','Hinojosas de Calatrava','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13049','Horcajo de los Montes','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13050','Labores (Las)','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13051','Luciana','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13052','Malagón','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13053','Manzanares','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13054','Membrilla','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13055','Mestanza','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13056','Miguelturra','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13057','Montiel','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13058','Moral de Calatrava','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13059','Navalpino','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13060','Navas de Estena','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13061','Pedro Muñoz','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13062','Picón','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13063','Piedrabuena','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13064','Poblete','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13065','Porzuna','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13066','Pozuelo de Calatrava','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13067','Pozuelos de Calatrava (Los)','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13068','Puebla de Don Rodrigo','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13069','Puebla del Príncipe','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13070','Puerto Lápice','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13071','Puertollano','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13072','Retuerta del Bullaque','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13073','Saceruela','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13074','San Carlos del Valle','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13075','San Lorenzo de Calatrava','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13076','Santa Cruz de los Cáñamos','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13077','Santa Cruz de Mudela','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13078','Socuéllamos','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13079','Solana (La)','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13080','Solana del Pino','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13081','Terrinches','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13082','Tomelloso','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13083','Torralba de Calatrava','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13084','Torre de Juan Abad','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13085','Torrenueva','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13086','Valdemanco del Esteras','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13087','Valdepeñas','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13088','Valenzuela de Calatrava','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13089','Villahermosa','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13090','Villamanrique','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13091','Villamayor de Calatrava','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13092','Villanueva de la Fuente','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13093','Villanueva de los Infantes','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13094','Villanueva de San Carlos','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13095','Villar del Pozo','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13096','Villarrubia de los Ojos','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13097','Villarta de San Juan','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13098','Viso del Marqués','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13901','Robledo (El)','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13902','Ruidera','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13903','Arenales de San Gregorio','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'13904','Llanos del Caudillo','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14001','Adamuz','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14002','Aguilar de la Frontera','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14003','Alcaracejos','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14004','Almedinilla','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14005','Almodóvar del Río','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14006','Añora','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14007','Baena','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14008','Belalcázar','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14009','Belmez','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14010','Benamejí','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14011','Blázquez (Los)','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14012','Bujalance','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14013','Cabra','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14014','Cañete de las Torres','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14015','Carcabuey','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14016','Cardeña','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14017','Carlota (La)','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14018','Carpio (El)','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14019','Castro del Río','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14020','Conquista','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14021','Córdoba','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14022','Doña Mencía','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14023','Dos Torres','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14024','Encinas Reales','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14025','Espejo','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14026','Espiel','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14027','Fernán-Núñez','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14028','Fuente la Lancha','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14029','Fuente Obejuna','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14030','Fuente Palmera','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14031','Fuente-Tójar','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14032','Granjuela (La)','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14033','Guadalcázar','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14034','Guijo (El)','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14035','Hinojosa del Duque','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14036','Hornachuelos','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14037','Iznájar','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14038','Lucena','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14039','Luque','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14040','Montalbán de Córdoba','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14041','Montemayor','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14042','Montilla','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14043','Montoro','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14044','Monturque','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14045','Moriles','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14046','Nueva Carteya','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14047','Obejo','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14048','Palenciana','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14049','Palma del Río','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14050','Pedro Abad','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14051','Pedroche','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14052','Peñarroya-Pueblonuevo','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14053','Posadas','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14054','Pozoblanco','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14055','Priego de Córdoba','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14056','Puente Genil','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14057','Rambla (La)','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14058','Rute','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14059','San Sebastián de los Ballesteros','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14060','Santaella','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14061','Santa Eufemia','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14062','Torrecampo','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14063','Valenzuela','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14064','Valsequillo','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14065','Victoria (La)','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14066','Villa del Río','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14067','Villafranca de Córdoba','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14068','Villaharta','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14069','Villanueva de Córdoba','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14070','Villanueva del Duque','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14071','Villanueva del Rey','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14072','Villaralto','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14073','Villaviciosa de Córdoba','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14074','Viso (El)','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14075','Zuheros','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14901','Fuente Carreteros','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'14902','Guijarrosa, La','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15001','Abegondo','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15002','Ames','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15003','Aranga','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15004','Ares','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15005','Arteixo','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15006','Arzúa','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15007','Baña (A)','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15008','Bergondo','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15009','Betanzos','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15010','Boimorto','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15011','Boiro','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15012','Boqueixón','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15013','Brión','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15014','Cabana de Bergantiños','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15015','Cabanas','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15016','Camariñas','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15017','Cambre','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15018','Capela (A)','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15019','Carballo','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15020','Carnota','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15021','Carral','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15022','Cedeira','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15023','Cee','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15024','Cerceda','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15025','Cerdido','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15027','Coirós','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15028','Corcubión','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15029','Coristanco','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15030','Coruña (A)','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15031','Culleredo','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15032','Curtis','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15033','Dodro','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15034','Dumbría','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15035','Fene','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15036','Ferrol','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15037','Fisterra','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15038','Frades','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15039','Irixoa','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15040','Laxe','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15041','Laracha (A)','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15042','Lousame','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15043','Malpica de Bergantiños','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15044','Mañón','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15045','Mazaricos','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15046','Melide','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15047','Mesía','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15048','Miño','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15049','Moeche','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15050','Monfero','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15051','Mugardos','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15052','Muxía','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15053','Muros','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15054','Narón','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15055','Neda','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15056','Negreira','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15057','Noia','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15058','Oleiros','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15059','Ordes','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15060','Oroso','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15061','Ortigueira','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15062','Outes','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15064','Paderne','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15065','Padrón','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15066','Pino (O)','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15067','Pobra do Caramiñal (A)','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15068','Ponteceso','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15069','Pontedeume','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15070','Pontes de García Rodríguez (As)','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15071','Porto do Son','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15072','Rianxo','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15073','Ribeira','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15074','Rois','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15075','Sada','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15076','San Sadurniño','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15077','Santa Comba','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15078','Santiago de Compostela','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15079','Santiso','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15080','Sobrado','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15081','Somozas (As)','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15082','Teo','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15083','Toques','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15084','Tordoia','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15085','Touro','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15086','Trazo','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15087','Valdoviño','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15088','Val do Dubra','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15089','Vedra','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15090','Vilasantar','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15091','Vilarmaior','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15092','Vimianzo','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15093','Zas','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15901','Cariño','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'15902','Oza-Cesuras','C',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16001','Abia de la Obispalía','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16002','Acebrón (El)','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16003','Alarcón','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16004','Albaladejo del Cuende','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16005','Albalate de las Nogueras','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16006','Albendea','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16007','Alberca de Záncara (La)','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16008','Alcalá de la Vega','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16009','Alcantud','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16010','Alcázar del Rey','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16011','Alcohujate','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16012','Alconchel de la Estrella','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16013','Algarra','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16014','Aliaguilla','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16015','Almarcha (La)','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16016','Almendros','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16017','Almodóvar del Pinar','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16018','Almonacid del Marquesado','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16019','Altarejos','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16020','Arandilla del Arroyo','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16022','Arcos de la Sierra','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16023','Chillarón de Cuenca','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16024','Arguisuelas','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16025','Arrancacepas','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16026','Atalaya del Cañavate','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16027','Barajas de Melo','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16029','Barchín del Hoyo','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16030','Bascuñana de San Pedro','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16031','Beamud','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16032','Belinchón','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16033','Belmonte','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16034','Belmontejo','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16035','Beteta','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16036','Boniches','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16038','Buciegas','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16039','Buenache de Alarcón','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16040','Buenache de la Sierra','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16041','Buendía','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16042','Campillo de Altobuey','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16043','Campillos-Paravientos','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16044','Campillos-Sierra','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16045','Canalejas del Arroyo','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16046','Cañada del Hoyo','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16047','Cañada Juncosa','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16048','Cañamares','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16049','Cañavate (El)','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16050','Cañaveras','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16051','Cañaveruelas','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16052','Cañete','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16053','Cañizares','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16055','Carboneras de Guadazaón','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16056','Cardenete','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16057','Carrascosa','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16058','Carrascosa de Haro','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16060','Casas de Benítez','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16061','Casas de Fernando Alonso','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16062','Casas de Garcimolina','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16063','Casas de Guijarro','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16064','Casas de Haro','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16065','Casas de los Pinos','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16066','Casasimarro','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16067','Castejón','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16068','Castillejo de Iniesta','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16070','Castillejo-Sierra','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16071','Castillo-Albaráñez','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16072','Castillo de Garcimuñoz','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16073','Cervera del Llano','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16074','Cierva (La)','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16078','Cuenca','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16079','Cueva del Hierro','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16081','Chumillas','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16082','Enguídanos','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16083','Fresneda de Altarejos','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16084','Fresneda de la Sierra','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16085','Frontera (La)','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16086','Fuente de Pedro Naharro','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16087','Fuentelespino de Haro','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16088','Fuentelespino de Moya','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16089','Fuentes','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16091','Fuertescusa','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16092','Gabaldón','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16093','Garaballa','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16094','Gascueña','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16095','Graja de Campalbo','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16096','Graja de Iniesta','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16097','Henarejos','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16098','Herrumblar (El)','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16099','Hinojosa (La)','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16100','Hinojosos (Los)','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16101','Hito (El)','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16102','Honrubia','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16103','Hontanaya','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16104','Hontecillas','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16106','Horcajo de Santiago','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16107','Huélamo','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16108','Huelves','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16109','Huérguina','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16110','Huerta de la Obispalía','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16111','Huerta del Marquesado','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16112','Huete','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16113','Iniesta','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16115','Laguna del Marquesado','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16116','Lagunaseca','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16117','Landete','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16118','Ledaña','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16119','Leganiel','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16121','Majadas (Las)','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16122','Mariana','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16123','Masegosa','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16124','Mesas (Las)','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16125','Minglanilla','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16126','Mira','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16128','Monreal del Llano','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16129','Montalbanejo','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16130','Montalbo','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16131','Monteagudo de las Salinas','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16132','Mota de Altarejos','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16133','Mota del Cuervo','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16134','Motilla del Palancar','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16135','Moya','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16137','Narboneta','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16139','Olivares de Júcar','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16140','Olmeda de la Cuesta','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16141','Olmeda del Rey','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16142','Olmedilla de Alarcón','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16143','Olmedilla de Eliz','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16145','Osa de la Vega','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16146','Pajarón','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16147','Pajaroncillo','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16148','Palomares del Campo','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16149','Palomera','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16150','Paracuellos','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16151','Paredes','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16152','Parra de las Vegas (La)','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16153','Pedernoso (El)','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16154','Pedroñeras (Las)','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16155','Peral (El)','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16156','Peraleja (La)','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16157','Pesquera (La)','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16158','Picazo (El)','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16159','Pinarejo','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16160','Pineda de Gigüela','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16161','Piqueras del Castillo','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16162','Portalrubio de Guadamejud','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16163','Portilla','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16165','Poyatos','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16166','Pozoamargo','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16167','Pozorrubio de Santiago','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16169','Pozuelo (El)','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16170','Priego','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16171','Provencio (El)','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16172','Puebla de Almenara','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16173','Valle de Altomira, El','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16174','Puebla del Salvador','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16175','Quintanar del Rey','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16176','Rada de Haro','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16177','Reíllo','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16181','Rozalén del Monte','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16185','Saceda-Trasierra','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16186','Saelices','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16187','Salinas del Manzano','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16188','Salmeroncillos','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16189','Salvacañete','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16190','San Clemente','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16191','San Lorenzo de la Parrilla','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16192','San Martín de Boniches','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16193','San Pedro Palmiches','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16194','Santa Cruz de Moya','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16195','Santa María del Campo Rus','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16196','Santa María de los Llanos','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16197','Santa María del Val','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16198','Sisante','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16199','Solera de Gabaldón','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16202','Talayuelas','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16203','Tarancón','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16204','Tébar','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16205','Tejadillos','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16206','Tinajas','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16209','Torralba','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16211','Torrejoncillo del Rey','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16212','Torrubia del Campo','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16213','Torrubia del Castillo','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16215','Tragacete','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16216','Tresjuncos','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16217','Tribaldos','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16218','Uclés','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16219','Uña','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16224','Valdemeca','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16225','Valdemorillo de la Sierra','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16227','Valdemoro-Sierra','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16228','Valdeolivas','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16231','Valhermoso de la Fuente','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16234','Valsalobre','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16236','Valverde de Júcar','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16237','Valverdejo','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16238','Vara de Rey','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16239','Vega del Codorno','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16240','Vellisca','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16242','Villaconejos de Trabaque','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16243','Villaescusa de Haro','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16244','Villagarcía del Llano','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16245','Villalba de la Sierra','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16246','Villalba del Rey','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16247','Villalgordo del Marquesado','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16248','Villalpardo','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16249','Villamayor de Santiago','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16250','Villanueva de Guadamejud','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16251','Villanueva de la Jara','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16253','Villar de Cañas','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16254','Villar de Domingo García','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16255','Villar de la Encina','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16258','Villar del Humo','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16259','Villar del Infantado','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16263','Villar de Olalla','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16264','Villarejo de Fuentes','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16265','Villarejo de la Peñuela','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16266','Villarejo-Periesteban','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16269','Villares del Saz','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16270','Villarrubio','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16271','Villarta','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16272','Villas de la Ventosa','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16273','Villaverde y Pasaconsol','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16274','Víllora','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16275','Vindel','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16276','Yémeda','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16277','Zafra de Záncara','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16278','Zafrilla','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16279','Zarza de Tajo','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16280','Zarzuela','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16901','Campos del Paraíso','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16902','Valdetórtola','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16903','Valeras (Las)','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16904','Fuentenava de Jábaga','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16905','Arcas','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16906','Valdecolmenas (Los)','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16908','Pozorrubielos de la Mancha','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16909','Sotorribas','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'16910','Villar y Velasco','CU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17001','Agullana','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17002','Aiguaviva','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17003','Albanyà','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17004','Albons','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17005','Far d''Empordà (El)','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17006','Alp','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17007','Amer','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17008','Anglès','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17009','Arbúcies','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17010','Argelaguer','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17011','Armentera (L'')','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17012','Avinyonet de Puigventós','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17013','Begur','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17014','Vajol (La)','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17015','Banyoles','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17016','Bàscara','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17018','Bellcaire d''Empordà','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17019','Besalú','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17020','Bescanó','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17021','Beuda','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17022','Bisbal d''Empordà (La)','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17023','Blanes','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17024','Bolvir','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17025','Bordils','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17026','Borrassà','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17027','Breda','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17028','Brunyola i Sant Martí Sapresa','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17029','Boadella i les Escaules','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17030','Cabanes','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17031','Cabanelles','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17032','Cadaqués','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17033','Caldes de Malavella','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17034','Calonge i Sant Antoni','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17035','Camós','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17036','Campdevànol','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17037','Campelles','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17038','Campllong','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17039','Camprodon','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17040','Canet d''Adri','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17041','Cantallops','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17042','Capmany','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17043','Queralbs','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17044','Cassà de la Selva','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17046','Castellfollit de la Roca','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17047','Castelló d''Empúries','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17048','Castell-Platja d''Aro','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17049','Celrà','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17050','Cervià de Ter','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17051','Cistella','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17052','Siurana','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17054','Colera','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17055','Colomers','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17056','Cornellà del Terri','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17057','Corçà','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17058','Crespià','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17060','Darnius','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17061','Das','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17062','Escala (L'')','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17063','Espinelves','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17064','Espolla','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17065','Esponellà','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17066','Figueres','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17067','Flaçà','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17068','Foixà','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17069','Fontanals de Cerdanya','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17070','Fontanilles','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17071','Fontcoberta','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17073','Fornells de la Selva','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17074','Fortià','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17075','Garrigàs','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17076','Garrigoles','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17077','Garriguella','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17078','Ger','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17079','Girona','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17080','Gombrèn','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17081','Gualta','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17082','Guils de Cerdanya','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17083','Hostalric','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17084','Isòvol','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17085','Jafre','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17086','Jonquera (La)','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17087','Juià','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17088','Lladó','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17089','Llagostera','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17090','Llambilles','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17091','Llanars','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17092','Llançà','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17093','Llers','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17094','Llívia','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17095','Lloret de Mar','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17096','Llosses (Les)','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17097','Madremanya','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17098','Maià de Montcal','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17099','Meranges','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17100','Masarac','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17101','Massanes','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17102','Maçanet de Cabrenys','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17103','Maçanet de la Selva','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17105','Mieres','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17106','Mollet de Peralada','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17107','Molló','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17109','Montagut i Oix','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17110','Mont-ras','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17111','Navata','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17112','Ogassa','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17114','Olot','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17115','Ordis','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17116','Osor','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17117','Palafrugell','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17118','Palamós','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17119','Palau de Santa Eulàlia','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17120','Palau-saverdera','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17121','Palau-sator','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17123','Palol de Revardit','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17124','Pals','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17125','Pardines','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17126','Parlavà','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17128','Pau','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17129','Pedret i Marzà','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17130','Pera (La)','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17132','Peralada','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17133','Planes d''Hostoles (Les)','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17134','Planoles','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17135','Pont de Molins','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17136','Pontós','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17137','Porqueres','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17138','Portbou','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17139','Preses (Les)','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17140','Port de la Selva (El)','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17141','Puigcerdà','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17142','Quart','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17143','Rabós','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17144','Regencós','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17145','Ribes de Freser','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17146','Riells i Viabrea','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17147','Ripoll','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17148','Riudarenes','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17149','Riudaura','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17150','Riudellots de la Selva','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17151','Riumors','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17152','Roses','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17153','Rupià','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17154','Sales de Llierca','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17155','Salt','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17157','Sant Andreu Salou','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17158','Sant Climent Sescebes','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17159','Sant Feliu de Buixalleu','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17160','Sant Feliu de Guíxols','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17161','Sant Feliu de Pallerols','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17162','Sant Ferriol','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17163','Sant Gregori','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17164','Sant Hilari Sacalm','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17165','Sant Jaume de Llierca','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17166','Sant Jordi Desvalls','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17167','Sant Joan de les Abadesses','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17168','Sant Joan de Mollet','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17169','Sant Julià de Ramis','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17170','Vallfogona de Ripollès','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17171','Sant Llorenç de la Muga','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17172','Sant Martí de Llémena','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17173','Sant Martí Vell','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17174','Sant Miquel de Campmajor','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17175','Sant Miquel de Fluvià','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17176','Sant Mori','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17177','Sant Pau de Segúries','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17178','Sant Pere Pescador','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17180','Santa Coloma de Farners','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17181','Santa Cristina d''Aro','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17182','Santa Llogaia d''Àlguema','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17183','Sant Aniol de Finestres','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17184','Santa Pau','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17185','Sant Joan les Fonts','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17186','Sarrià de Ter','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17187','Saus, Camallera i Llampaies','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17188','Selva de Mar (La)','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17189','Cellera de Ter (La)','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17190','Serinyà','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17191','Serra de Daró','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17192','Setcases','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17193','Sils','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17194','Susqueda','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17195','Tallada d''Empordà (La)','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17196','Terrades','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17197','Torrent','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17198','Torroella de Fluvià','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17199','Torroella de Montgrí','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17200','Tortellà','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17201','Toses','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17202','Tossa de Mar','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17203','Ultramort','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17204','Ullà','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17205','Ullastret','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17206','Urús','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17207','Vall d''en Bas (La)','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17208','Vall de Bianya (La)','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17209','Vall-llobrega','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17210','Ventalló','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17211','Verges','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17212','Vidrà','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17213','Vidreres','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17214','Vilabertran','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17215','Vilablareix','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17216','Viladasens','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17217','Viladamat','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17218','Vilademuls','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17220','Viladrau','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17221','Vilafant','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17222','Vilaür','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17223','Vilajuïga','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17224','Vilallonga de Ter','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17225','Vilamacolum','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17226','Vilamalla','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17227','Vilamaniscle','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17228','Vilanant','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17230','Vila-sacra','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17232','Vilopriu','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17233','Vilobí d''Onyar','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17234','Biure','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17901','Cruïlles, Monells i Sant Sadurní de l''Heura','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17902','Forallac','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'17903','Sant Julià del Llor i Bonmatí','GI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18001','Agrón','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18002','Alamedilla','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18003','Albolote','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18004','Albondón','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18005','Albuñán','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18006','Albuñol','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18007','Albuñuelas','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18010','Aldeire','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18011','Alfacar','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18012','Algarinejo','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18013','Alhama de Granada','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18014','Alhendín','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18015','Alicún de Ortega','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18016','Almegíjar','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18017','Almuñécar','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18018','Alquife','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18020','Arenas del Rey','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18021','Armilla','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18022','Atarfe','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18023','Baza','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18024','Beas de Granada','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18025','Beas de Guadix','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18027','Benalúa','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18028','Benalúa de las Villas','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18029','Benamaurel','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18030','Bérchules','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18032','Bubión','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18033','Busquístar','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18034','Cacín','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18035','Cádiar','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18036','Cájar','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18037','Calicasas','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18038','Campotéjar','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18039','Caniles','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18040','Cáñar','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18042','Capileira','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18043','Carataunas','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18044','Cástaras','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18045','Castilléjar','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18046','Castril','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18047','Cenes de la Vega','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18048','Cijuela','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18049','Cogollos de Guadix','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18050','Cogollos de la Vega','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18051','Colomera','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18053','Cortes de Baza','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18054','Cortes y Graena','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18056','Cúllar','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18057','Cúllar Vega','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18059','Chauchina','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18061','Chimeneas','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18062','Churriana de la Vega','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18063','Darro','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18064','Dehesas de Guadix','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18065','Dehesas Viejas','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18066','Deifontes','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18067','Diezma','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18068','Dílar','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18069','Dólar','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18070','Dúdar','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18071','Dúrcal','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18072','Escúzar','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18074','Ferreira','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18076','Fonelas','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18077','Fornes','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18078','Freila','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18079','Fuente Vaqueros','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18082','Galera','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18083','Gobernador','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18084','Gójar','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18085','Gor','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18086','Gorafe','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18087','Granada','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18088','Guadahortuna','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18089','Guadix','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18093','Gualchos','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18094','Güéjar Sierra','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18095','Güevéjar','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18096','Huélago','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18097','Huéneja','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18098','Huéscar','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18099','Huétor de Santillán','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18100','Huétor Tájar','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18101','Huétor Vega','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18102','Íllora','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18103','Ítrabo','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18105','Iznalloz','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18106','Játar','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18107','Jayena','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18108','Jérez del Marquesado','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18109','Jete','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18111','Jun','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18112','Juviles','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18114','Calahorra (La)','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18115','Láchar','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18116','Lanjarón','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18117','Lanteira','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18119','Lecrín','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18120','Lentegí','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18121','Lobras','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18122','Loja','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18123','Lugros','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18124','Lújar','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18126','Malahá (La)','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18127','Maracena','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18128','Marchal','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18132','Moclín','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18133','Molvízar','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18134','Monachil','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18135','Montefrío','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18136','Montejícar','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18137','Montillana','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18138','Moraleda de Zafayona','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18140','Motril','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18141','Murtas','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18143','Nigüelas','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18144','Nívar','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18145','Ogíjares','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18146','Orce','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18147','Órgiva','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18148','Otívar','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18149','Villa de Otura','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18150','Padul','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18151','Pampaneira','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18152','Pedro Martínez','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18153','Peligros','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18154','Peza (La)','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18157','Pinos Genil','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18158','Pinos Puente','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18159','Píñar','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18161','Polícar','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18162','Polopos','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18163','Pórtugos','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18164','Puebla de Don Fadrique','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18165','Pulianas','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18167','Purullena','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18168','Quéntar','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18170','Rubite','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18171','Salar','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18173','Salobreña','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18174','Santa Cruz del Comercio','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18175','Santa Fe','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18176','Soportújar','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18177','Sorvilán','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18178','Torre-Cardela','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18179','Torvizcón','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18180','Trevélez','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18181','Turón','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18182','Ugíjar','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18183','Válor','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18184','Vélez de Benaudalla','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18185','Ventas de Huelma','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18187','Villanueva de las Torres','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18188','Villanueva Mesía','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18189','Víznar','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18192','Zafarraya','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18193','Zubia (La)','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18194','Zújar','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18901','Taha (La)','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18902','Valle (El)','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18903','Nevada','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18904','Alpujarra de la Sierra','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18905','Gabias (Las)','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18906','Guájares, Los','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18907','Valle del Zalabí','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18908','Villamena','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18909','Morelábor','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18910','Pinar (El)','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18911','Vegas del Genil','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18912','Cuevas del Campo','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18913','Zagra','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18914','Valderrubio','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18915','Domingo Pérez de Granada','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'18916','Torrenueva Costa','GR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19001','Abánades','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19002','Ablanque','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19003','Adobes','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19004','Alaminos','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19005','Alarilla','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19006','Albalate de Zorita','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19007','Albares','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19008','Albendiego','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19009','Alcocer','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19010','Alcolea de las Peñas','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19011','Alcolea del Pinar','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19013','Alcoroches','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19015','Aldeanueva de Guadalajara','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19016','Algar de Mesa','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19017','Algora','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19018','Alhóndiga','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19019','Alique','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19020','Almadrones','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19021','Almoguera','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19022','Almonacid de Zorita','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19023','Alocén','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19024','Alovera','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19027','Alustante','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19031','Angón','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19032','Anguita','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19033','Anquela del Ducado','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19034','Anquela del Pedregal','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19036','Aranzueque','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19037','Arbancón','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19038','Arbeteta','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19039','Argecilla','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19040','Armallones','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19041','Armuña de Tajuña','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19042','Arroyo de las Fraguas','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19043','Atanzón','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19044','Atienza','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19045','Auñón','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19046','Azuqueca de Henares','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19047','Baides','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19048','Baños de Tajo','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19049','Bañuelos','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19050','Barriopedro','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19051','Berninches','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19052','Bodera (La)','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19053','Brihuega','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19054','Budia','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19055','Bujalaro','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19057','Bustares','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19058','Cabanillas del Campo','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19059','Campillo de Dueñas','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19060','Campillo de Ranas','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19061','Campisábalos','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19064','Canredondo','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19065','Cantalojas','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19066','Cañizar','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19067','Cardoso de la Sierra (El)','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19070','Casa de Uceda','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19071','Casar (El)','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19073','Casas de San Galindo','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19074','Caspueñas','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19075','Castejón de Henares','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19076','Castellar de la Muela','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19078','Castilforte','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19079','Castilnuevo','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19080','Cendejas de Enmedio','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19081','Cendejas de la Torre','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19082','Centenera','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19086','Cifuentes','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19087','Cincovillas','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19088','Ciruelas','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19089','Ciruelos del Pinar','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19090','Cobeta','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19091','Cogollor','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19092','Cogolludo','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19095','Condemios de Abajo','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19096','Condemios de Arriba','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19097','Congostrina','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19098','Copernal','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19099','Corduente','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19102','Cubillo de Uceda (El)','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19103','Checa','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19104','Chequilla','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19105','Chiloeches','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19106','Chillarón del Rey','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19107','Driebes','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19108','Durón','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19109','Embid','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19110','Escamilla','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19111','Escariche','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19112','Escopete','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19113','Espinosa de Henares','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19114','Esplegares','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19115','Establés','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19116','Estriégana','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19117','Fontanar','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19118','Fuembellida','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19119','Fuencemillán','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19120','Fuentelahiguera de Albatages','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19121','Fuentelencina','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19122','Fuentelsaz','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19123','Fuentelviejo','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19124','Fuentenovilla','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19125','Gajanejos','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19126','Galápagos','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19127','Galve de Sorbe','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19129','Gascueña de Bornova','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19130','Guadalajara','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19132','Henche','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19133','Heras de Ayuso','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19134','Herrería','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19135','Hiendelaencina','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19136','Hijes','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19138','Hita','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19139','Hombrados','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19142','Hontoba','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19143','Horche','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19145','Hortezuela de Océn','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19146','Huerce (La)','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19147','Huérmeces del Cerro','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19148','Huertahernando','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19150','Hueva','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19151','Humanes','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19152','Illana','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19153','Iniéstola','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19154','Inviernas (Las)','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19155','Irueste','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19156','Jadraque','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19157','Jirueque','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19159','Ledanca','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19160','Loranca de Tajuña','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19161','Lupiana','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19162','Luzaga','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19163','Luzón','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19165','Majaelrayo','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19166','Málaga del Fresno','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19167','Malaguilla','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19168','Mandayona','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19169','Mantiel','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19170','Maranchón','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19171','Marchamalo','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19172','Masegoso de Tajuña','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19173','Matarrubia','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19174','Matillas','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19175','Mazarete','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19176','Mazuecos','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19177','Medranda','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19178','Megina','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19179','Membrillera','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19181','Miedes de Atienza','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19182','Mierla (La)','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19183','Milmarcos','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19184','Millana','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19185','Miñosa (La)','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19186','Mirabueno','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19187','Miralrío','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19188','Mochales','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19189','Mohernando','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19190','Molina de Aragón','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19191','Monasterio','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19192','Mondéjar','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19193','Montarrón','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19194','Moratilla de los Meleros','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19195','Morenilla','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19196','Muduex','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19197','Navas de Jadraque (Las)','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19198','Negredo','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19199','Ocentejo','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19200','Olivar (El)','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19201','Olmeda de Cobeta','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19202','Olmeda de Jadraque (La)','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19203','Ordial (El)','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19204','Orea','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19208','Pálmaces de Jadraque','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19209','Pardos','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19210','Paredes de Sigüenza','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19211','Pareja','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19212','Pastrana','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19213','Pedregal (El)','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19214','Peñalén','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19215','Peñalver','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19216','Peralejos de las Truchas','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19217','Peralveche','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19218','Pinilla de Jadraque','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19219','Pinilla de Molina','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19220','Pioz','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19221','Piqueras','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19222','Pobo de Dueñas (El)','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19223','Poveda de la Sierra','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19224','Pozo de Almoguera','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19225','Pozo de Guadalajara','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19226','Prádena de Atienza','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19227','Prados Redondos','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19228','Puebla de Beleña','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19229','Puebla de Valles','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19230','Quer','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19231','Rebollosa de Jadraque','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19232','Recuenco (El)','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19233','Renera','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19234','Retiendas','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19235','Riba de Saelices','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19237','Rillo de Gallo','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19238','Riofrío del Llano','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19239','Robledillo de Mohernando','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19240','Robledo de Corpes','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19241','Romanillos de Atienza','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19242','Romanones','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19243','Rueda de la Sierra','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19244','Sacecorbo','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19245','Sacedón','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19246','Saelices de la Sal','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19247','Salmerón','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19248','San Andrés del Congosto','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19249','San Andrés del Rey','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19250','Santiuste','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19251','Saúca','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19252','Sayatón','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19254','Selas','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19255','Setiles','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19256','Sienes','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19257','Sigüenza','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19258','Solanillos del Extremo','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19259','Somolinos','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19260','Sotillo (El)','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19261','Sotodosos','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19262','Tamajón','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19263','Taragudo','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19264','Taravilla','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19265','Tartanedo','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19266','Tendilla','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19267','Terzaga','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19268','Tierzo','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19269','Toba (La)','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19270','Tordelrábano','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19271','Tordellego','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19272','Tordesilos','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19274','Torija','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19277','Torrecuadrada de Molina','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19278','Torrecuadradilla','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19279','Torre del Burgo','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19280','Torrejón del Rey','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19281','Torremocha de Jadraque','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19282','Torremocha del Campo','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19283','Torremocha del Pinar','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19284','Torremochuela','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19285','Torrubia','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19286','Tórtola de Henares','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19287','Tortuera','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19288','Tortuero','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19289','Traíd','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19290','Trijueque','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19291','Trillo','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19293','Uceda','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19294','Ujados','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19296','Utande','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19297','Valdarachas','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19298','Valdearenas','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19299','Valdeavellano','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19300','Valdeaveruelo','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19301','Valdeconcha','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19302','Valdegrudas','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19303','Valdelcubo','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19304','Valdenuño Fernández','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19305','Valdepeñas de la Sierra','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19306','Valderrebollo','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19307','Valdesotos','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19308','Valfermoso de Tajuña','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19309','Valhermoso','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19310','Valtablado del Río','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19311','Valverde de los Arroyos','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19314','Viana de Jadraque','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19317','Villanueva de Alcorón','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19318','Villanueva de Argecilla','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19319','Villanueva de la Torre','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19321','Villares de Jadraque','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19322','Villaseca de Henares','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19323','Villaseca de Uceda','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19324','Villel de Mesa','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19325','Viñuelas','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19326','Yebes','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19327','Yebra','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19329','Yélamos de Abajo','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19330','Yélamos de Arriba','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19331','Yunquera de Henares','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19332','Yunta (La)','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19333','Zaorejas','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19334','Zarzuela de Jadraque','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19335','Zorita de los Canes','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'19901','Semillas','GU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20001','Abaltzisketa','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20002','Aduna','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20003','Aizarnazabal','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20004','Albiztur','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20005','Alegia','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20006','Alkiza','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20007','Altzo','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20008','Amezketa','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20009','Andoain','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20010','Anoeta','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20011','Antzuola','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20012','Arama','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20013','Aretxabaleta','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20014','Asteasu','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20015','Ataun','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20016','Aia','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20017','Azkoitia','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20018','Azpeitia','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20019','Beasain','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20020','Beizama','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20021','Belauntza','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20022','Berastegi','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20023','Berrobi','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20024','Bidania-Goiatz','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20025','Zegama','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20026','Zerain','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20027','Zestoa','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20028','Zizurkil','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20029','Deba','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20030','Eibar','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20031','Elduain','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20032','Elgoibar','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20033','Elgeta','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20034','Eskoriatza','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20035','Ezkio-Itsaso','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20036','Hondarribia','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20037','Gaintza','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20038','Gabiria','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20039','Getaria','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20040','Hernani','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20041','Hernialde','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20042','Ibarra','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20043','Idiazabal','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20044','Ikaztegieta','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20045','Irun','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20046','Irura','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20047','Itsasondo','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20048','Larraul','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20049','Lazkao','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20050','Leaburu','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20051','Legazpi','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20052','Legorreta','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20053','Lezo','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20054','Lizartza','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20055','Arrasate/Mondragón','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20056','Mutriku','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20057','Mutiloa','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20058','Olaberria','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20059','Oñati','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20060','Orexa','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20061','Orio','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20062','Ormaiztegi','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20063','Oiartzun','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20064','Pasaia','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20065','Soraluze-Placencia de las Armas','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20066','Errezil','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20067','Errenteria','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20068','Leintz-Gatzaga','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20069','Donostia-San Sebastián','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20070','Segura','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20071','Tolosa','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20072','Urnieta','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20073','Usurbil','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20074','Bergara','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20075','Villabona','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20076','Ordizia','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20077','Urretxu','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20078','Zaldibia','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20079','Zarautz','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20080','Zumarraga','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20081','Zumaia','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20901','Mendaro','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20902','Lasarte-Oria','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20903','Astigarraga','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20904','Baliarrain','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20905','Orendain','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20906','Altzaga','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'20907','Gaztelu','SS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21001','Alájar','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21002','Aljaraque','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21003','Almendro (El)','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21004','Almonaster la Real','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21005','Almonte','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21006','Alosno','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21007','Aracena','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21008','Aroche','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21009','Arroyomolinos de León','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21010','Ayamonte','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21011','Beas','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21012','Berrocal','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21013','Bollullos Par del Condado','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21014','Bonares','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21015','Cabezas Rubias','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21016','Cala','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21017','Calañas','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21018','Campillo (El)','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21019','Campofrío','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21020','Cañaveral de León','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21021','Cartaya','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21022','Castaño del Robledo','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21023','Cerro de Andévalo (El)','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21024','Corteconcepción','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21025','Cortegana','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21026','Cortelazor','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21027','Cumbres de Enmedio','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21028','Cumbres de San Bartolomé','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21029','Cumbres Mayores','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21030','Chucena','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21031','Encinasola','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21032','Escacena del Campo','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21033','Fuenteheridos','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21034','Galaroza','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21035','Gibraleón','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21036','Granada de Río-Tinto (La)','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21037','Granado (El)','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21038','Higuera de la Sierra','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21039','Hinojales','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21040','Hinojos','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21041','Huelva','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21042','Isla Cristina','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21043','Jabugo','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21044','Lepe','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21045','Linares de la Sierra','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21046','Lucena del Puerto','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21047','Manzanilla','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21048','Marines (Los)','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21049','Minas de Riotinto','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21050','Moguer','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21051','Nava (La)','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21052','Nerva','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21053','Niebla','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21054','Palma del Condado (La)','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21055','Palos de la Frontera','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21056','Paterna del Campo','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21057','Paymogo','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21058','Puebla de Guzmán','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21059','Puerto Moral','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21060','Punta Umbría','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21061','Rociana del Condado','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21062','Rosal de la Frontera','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21063','San Bartolomé de la Torre','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21064','San Juan del Puerto','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21065','Sanlúcar de Guadiana','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21066','San Silvestre de Guzmán','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21067','Santa Ana la Real','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21068','Santa Bárbara de Casa','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21069','Santa Olalla del Cala','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21070','Trigueros','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21071','Valdelarco','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21072','Valverde del Camino','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21073','Villablanca','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21074','Villalba del Alcor','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21075','Villanueva de las Cruces','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21076','Villanueva de los Castillejos','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21077','Villarrasa','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21078','Zalamea la Real','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21079','Zufre','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'21902','Zarza-Perrunal, La','H',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22001','Abiego','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22002','Abizanda','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22003','Adahuesca','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22004','Agüero','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22006','Aisa','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22007','Albalate de Cinca','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22008','Albalatillo','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22009','Albelda','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22011','Albero Alto','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22012','Albero Bajo','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22013','Alberuela de Tubo','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22014','Alcalá de Gurrea','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22015','Alcalá del Obispo','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22016','Alcampell','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22017','Alcolea de Cinca','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22018','Alcubierre','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22019','Alerre','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22020','Alfántega','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22021','Almudévar','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22022','Almunia de San Juan','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22023','Almuniente','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22024','Alquézar','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22025','Altorricón','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22027','Angüés','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22028','Ansó','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22029','Antillón','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22032','Aragüés del Puerto','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22035','Arén','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22036','Argavieso','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22037','Arguis','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22039','Ayerbe','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22040','Azanuy-Alins','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22041','Azara','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22042','Azlor','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22043','Baélls','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22044','Bailo','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22045','Baldellou','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22046','Ballobar','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22047','Banastás','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22048','Barbastro','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22049','Barbués','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22050','Barbuñales','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22051','Bárcabo','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22052','Belver de Cinca','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22053','Benabarre','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22054','Benasque','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22055','Berbegal','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22057','Bielsa','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22058','Bierge','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22059','Biescas','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22060','Binaced','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22061','Binéfar','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22062','Bisaurri','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22063','Biscarrués','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22064','Blecua y Torres','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22066','Boltaña','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22067','Bonansa','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22068','Borau','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22069','Broto','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22072','Caldearenas','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22074','Campo','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22075','Camporrélls','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22076','Canal de Berdún','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22077','Candasnos','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22078','Canfranc','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22079','Capdesaso','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22080','Capella','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22081','Casbas de Huesca','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22082','Castejón del Puente','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22083','Castejón de Monegros','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22084','Castejón de Sos','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22085','Castelflorite','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22086','Castiello de Jaca','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22087','Castigaleu','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22088','Castillazuelo','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22089','Castillonroy','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22090','Colungo','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22094','Chalamera','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22095','Chía','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22096','Chimillas','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22099','Esplús','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22102','Estada','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22103','Estadilla','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22105','Estopiñán del Castillo','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22106','Fago','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22107','Fanlo','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22109','Fiscal','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22110','Fonz','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22111','Foradada del Toscar','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22112','Fraga','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22113','Fueva (La)','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22114','Gistaín','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22115','Grado (El)','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22116','Grañén','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22117','Graus','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22119','Gurrea de Gállego','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22122','Hoz de Jaca','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22124','Huerto','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22125','Huesca','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22126','Ibieca','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22127','Igriés','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22128','Ilche','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22129','Isábena','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22130','Jaca','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22131','Jasa','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22133','Labuerda','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22135','Laluenga','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22136','Lalueza','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22137','Lanaja','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22139','Laperdiguera','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22141','Lascellas-Ponzano','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22142','Lascuarre','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22143','Laspaúles','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22144','Laspuña','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22149','Loarre','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22150','Loporzano','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22151','Loscorrales','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22155','Monesma y Cajigar','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22156','Monflorite-Lascasas','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22157','Montanuy','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22158','Monzón','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22160','Naval','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22162','Novales','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22163','Nueno','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22164','Olvena','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22165','Ontiñena','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22167','Osso de Cinca','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22168','Palo','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22170','Panticosa','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22172','Peñalba','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22173','Peñas de Riglos (Las)','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22174','Peralta de Alcofea','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22175','Peralta de Calasanz','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22176','Peraltilla','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22177','Perarrúa','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22178','Pertusa','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22181','Piracés','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22182','Plan','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22184','Poleñino','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22186','Pozán de Vero','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22187','Puebla de Castro (La)','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22188','Puente de Montañana','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22189','Puértolas','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22190','Pueyo de Araguás (El)','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22193','Pueyo de Santa Cruz','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22195','Quicena','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22197','Robres','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22199','Sabiñánigo','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22200','Sahún','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22201','Salas Altas','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22202','Salas Bajas','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22203','Salillas','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22204','Sallent de Gállego','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22205','San Esteban de Litera','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22206','Sangarrén','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22207','San Juan de Plan','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22208','Santa Cilia','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22209','Santa Cruz de la Serós','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22212','Santaliestra y San Quílez','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22213','Sariñena','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22214','Secastilla','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22215','Seira','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22217','Sena','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22218','Senés de Alcubierre','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22220','Sesa','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22221','Sesué','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22222','Siétamo','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22223','Sopeira','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22225','Tamarite de Litera','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22226','Tardienta','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22227','Tella-Sin','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22228','Tierz','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22229','Tolva','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22230','Torla-Ordesa','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22232','Torralba de Aragón','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22233','Torre la Ribera','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22234','Torrente de Cinca','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22235','Torres de Alcanadre','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22236','Torres de Barbués','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22239','Tramaced','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22242','Valfarta','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22243','Valle de Bardají','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22244','Valle de Lierp','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22245','Velilla de Cinca','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22246','Beranuy','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22247','Viacamp y Litera','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22248','Vicién','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22249','Villanova','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22250','Villanúa','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22251','Villanueva de Sigena','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22252','Yebra de Basa','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22253','Yésero','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22254','Zaidín','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22901','Valle de Hecho','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22902','Puente la Reina de Jaca','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22903','San Miguel del Cinca','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22904','Sotonera (La)','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22905','Lupiñén-Ortilla','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22906','Santa María de Dulcis','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22907','Aínsa-Sobrarbe','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22908','Hoz y Costean','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'22909','Vencillón','HU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23001','Albanchez de Mágina','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23002','Alcalá la Real','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23003','Alcaudete','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23004','Aldeaquemada','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23005','Andújar','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23006','Arjona','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23007','Arjonilla','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23008','Arquillos','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23009','Baeza','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23010','Bailén','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23011','Baños de la Encina','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23012','Beas de Segura','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23014','Begíjar','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23015','Bélmez de la Moraleda','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23016','Benatae','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23017','Cabra del Santo Cristo','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23018','Cambil','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23019','Campillo de Arenas','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23020','Canena','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23021','Carboneros','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23024','Carolina (La)','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23025','Castellar','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23026','Castillo de Locubín','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23027','Cazalilla','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23028','Cazorla','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23029','Chiclana de Segura','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23030','Chilluévar','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23031','Escañuela','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23032','Espeluy','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23033','Frailes','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23034','Fuensanta de Martos','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23035','Fuerte del Rey','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23037','Génave','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23038','Guardia de Jaén (La)','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23039','Guarromán','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23040','Lahiguera','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23041','Higuera de Calatrava','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23042','Hinojares','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23043','Hornos','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23044','Huelma','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23045','Huesa','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23046','Ibros','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23047','Iruela (La)','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23048','Iznatoraf','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23049','Jabalquinto','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23050','Jaén','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23051','Jamilena','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23052','Jimena','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23053','Jódar','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23054','Larva','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23055','Linares','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23056','Lopera','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23057','Lupión','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23058','Mancha Real','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23059','Marmolejo','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23060','Martos','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23061','Mengíbar','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23062','Montizón','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23063','Navas de San Juan','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23064','Noalejo','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23065','Orcera','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23066','Peal de Becerro','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23067','Pegalajar','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23069','Porcuna','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23070','Pozo Alcón','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23071','Puente de Génave','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23072','Puerta de Segura (La)','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23073','Quesada','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23074','Rus','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23075','Sabiote','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23076','Santa Elena','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23077','Santiago de Calatrava','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23079','Santisteban del Puerto','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23080','Santo Tomé','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23081','Segura de la Sierra','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23082','Siles','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23084','Sorihuela del Guadalimar','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23085','Torreblascopedro','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23086','Torredelcampo','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23087','Torredonjimeno','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23088','Torreperogil','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23090','Torres','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23091','Torres de Albánchez','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23092','Úbeda','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23093','Valdepeñas de Jaén','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23094','Vilches','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23095','Villacarrillo','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23096','Villanueva de la Reina','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23097','Villanueva del Arzobispo','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23098','Villardompardo','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23099','Villares (Los)','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23101','Villarrodrigo','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23901','Cárcheles','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23902','Bedmar y Garcíez','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23903','Villatorres','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23904','Santiago-Pontones','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'23905','Arroyo del Ojanco','J',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24001','Acebedo','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24002','Algadefe','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24003','Alija del Infantado','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24004','Almanza','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24005','Antigua (La)','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24006','Ardón','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24007','Arganza','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24008','Astorga','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24009','Balboa','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24010','Bañeza (La)','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24011','Barjas','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24012','Barrios de Luna (Los)','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24014','Bembibre','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24015','Benavides','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24016','Benuza','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24017','Bercianos del Páramo','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24018','Bercianos del Real Camino','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24019','Berlanga del Bierzo','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24020','Boca de Huérgano','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24021','Boñar','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24022','Borrenes','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24023','Brazuelo','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24024','Burgo Ranero (El)','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24025','Burón','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24026','Bustillo del Páramo','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24027','Cabañas Raras','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24028','Cabreros del Río','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24029','Cabrillanes','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24030','Cacabelos','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24031','Calzada del Coto','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24032','Campazas','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24033','Campo de Villavidel','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24034','Camponaraya','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24036','Candín','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24037','Cármenes','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24038','Carracedelo','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24039','Carrizo','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24040','Carrocera','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24041','Carucedo','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24042','Castilfalé','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24043','Castrillo de Cabrera','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24044','Castrillo de la Valduerna','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24046','Castrocalbón','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24047','Castrocontrigo','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24049','Castropodame','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24050','Castrotierra de Valmadrigal','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24051','Cea','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24052','Cebanico','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24053','Cebrones del Río','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24054','Cimanes de la Vega','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24055','Cimanes del Tejar','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24056','Cistierna','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24057','Congosto','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24058','Corbillos de los Oteros','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24059','Corullón','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24060','Crémenes','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24061','Cuadros','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24062','Cubillas de los Oteros','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24063','Cubillas de Rueda','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24064','Cubillos del Sil','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24065','Chozas de Abajo','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24066','Destriana','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24067','Encinedo','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24068','Ercina (La)','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24069','Escobar de Campos','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24070','Fabero','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24071','Folgoso de la Ribera','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24073','Fresno de la Vega','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24074','Fuentes de Carbajal','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24076','Garrafe de Torío','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24077','Gordaliza del Pino','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24078','Gordoncillo','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24079','Gradefes','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24080','Grajal de Campos','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24081','Gusendos de los Oteros','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24082','Hospital de Órbigo','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24083','Igüeña','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24084','Izagre','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24086','Joarilla de las Matas','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24087','Laguna Dalga','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24088','Laguna de Negrillos','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24089','León','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24090','Lucillo','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24091','Luyego','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24092','Llamas de la Ribera','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24093','Magaz de Cepeda','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24094','Mansilla de las Mulas','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24095','Mansilla Mayor','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24096','Maraña','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24097','Matadeón de los Oteros','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24098','Matallana de Torío','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24099','Matanza','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24100','Molinaseca','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24101','Murias de Paredes','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24102','Noceda del Bierzo','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24103','Oencia','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24104','Omañas (Las)','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24105','Onzonilla','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24106','Oseja de Sajambre','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24107','Pajares de los Oteros','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24108','Palacios de la Valduerna','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24109','Palacios del Sil','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24110','Páramo del Sil','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24112','Peranzanes','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24113','Pobladura de Pelayo García','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24114','Pola de Gordón (La)','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24115','Ponferrada','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24116','Posada de Valdeón','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24117','Pozuelo del Páramo','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24118','Prado de la Guzpeña','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24119','Priaranza del Bierzo','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24120','Prioro','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24121','Puebla de Lillo','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24122','Puente de Domingo Flórez','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24123','Quintana del Castillo','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24124','Quintana del Marco','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24125','Quintana y Congosto','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24127','Regueras de Arriba','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24129','Reyero','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24130','Riaño','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24131','Riego de la Vega','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24132','Riello','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24133','Rioseco de Tapia','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24134','Robla (La)','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24136','Roperuelos del Páramo','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24137','Sabero','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24139','Sahagún','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24141','San Adrián del Valle','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24142','San Andrés del Rabanedo','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24143','Sancedo','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24144','San Cristóbal de la Polantera','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24145','San Emiliano','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24146','San Esteban de Nogales','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24148','San Justo de la Vega','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24149','San Millán de los Caballeros','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24150','San Pedro Bercianos','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24151','Santa Colomba de Curueño','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24152','Santa Colomba de Somoza','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24153','Santa Cristina de Valmadrigal','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24154','Santa Elena de Jamuz','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24155','Santa María de la Isla','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24156','Santa María del Monte de Cea','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24157','Santa María del Páramo','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24158','Santa María de Ordás','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24159','Santa Marina del Rey','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24160','Santas Martas','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24161','Santiago Millas','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24162','Santovenia de la Valdoncina','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24163','Sariegos','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24164','Sena de Luna','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24165','Sobrado','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24166','Soto de la Vega','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24167','Soto y Amío','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24168','Toral de los Guzmanes','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24169','Toreno','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24170','Torre del Bierzo','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24171','Trabadelo','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24172','Truchas','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24173','Turcia','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24174','Urdiales del Páramo','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24175','Valdefresno','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24176','Valdefuentes del Páramo','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24177','Valdelugueros','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24178','Valdemora','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24179','Valdepiélago','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24180','Valdepolo','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24181','Valderas','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24182','Valderrey','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24183','Valderrueda','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24184','Valdesamario','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24185','Val de San Lorenzo','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24187','Valdevimbre','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24188','Valencia de Don Juan','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24189','Valverde de la Virgen','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24190','Valverde-Enrique','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24191','Vallecillo','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24193','Vecilla (La)','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24194','Vegacervera','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24196','Vega de Espinareda','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24197','Vega de Infanzones','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24198','Vega de Valcarce','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24199','Vegaquemada','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24201','Vegas del Condado','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24202','Villablino','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24203','Villabraz','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24205','Villadangos del Páramo','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24206','Toral de los Vados','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24207','Villademor de la Vega','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24209','Villafranca del Bierzo','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24210','Villagatón','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24211','Villamandos','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24212','Villamañán','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24213','Villamartín de Don Sancho','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24214','Villamejil','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24215','Villamol','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24216','Villamontán de la Valduerna','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24217','Villamoratiel de las Matas','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24218','Villanueva de las Manzanas','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24219','Villaobispo de Otero','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24221','Villaquejida','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24222','Villaquilambre','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24223','Villarejo de Órbigo','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24224','Villares de Órbigo','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24225','Villasabariego','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24226','Villaselán','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24227','Villaturiel','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24228','Villazala','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24229','Villazanzo de Valderaduey','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24230','Zotes del Páramo','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24901','Villamanín','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'24902','Villaornate y Castro','LE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25001','Abella de la Conca','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25002','Àger','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25003','Agramunt','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25004','Alamús (Els)','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25005','Alàs i Cerc','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25006','Albagés (L'')','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25007','Albatàrrec','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25008','Albesa','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25009','Albi (L'')','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25010','Alcanó','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25011','Alcarràs','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25012','Alcoletge','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25013','Alfarràs','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25014','Alfés','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25015','Algerri','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25016','Alguaire','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25017','Alins','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25019','Almacelles','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25020','Almatret','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25021','Almenar','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25022','Alòs de Balaguer','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25023','Alpicat','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25024','Alt Àneu','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25025','Naut Aran','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25027','Anglesola','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25029','Arbeca','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25030','Pont de Bar (El)','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25031','Arres','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25032','Arsèguel','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25033','Artesa de Lleida','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25034','Artesa de Segre','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25035','Sentiu de Sió (La)','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25036','Aspa','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25037','Avellanes i Santa Linya (Les)','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25038','Aitona','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25039','Baix Pallars','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25040','Balaguer','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25041','Barbens','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25042','Baronia de Rialb (La)','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25043','Vall de Boí (La)','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25044','Bassella','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25045','Bausen','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25046','Belianes','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25047','Bellcaire d''Urgell','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25048','Bell-lloc d''Urgell','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25049','Bellmunt d''Urgell','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25050','Bellpuig','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25051','Bellver de Cerdanya','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25052','Bellvís','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25053','Benavent de Segrià','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25055','Biosca','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25056','Bovera','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25057','Bòrdes (Es)','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25058','Borges Blanques (Les)','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25059','Bossòst','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25060','Cabanabona','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25061','Cabó','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25062','Camarasa','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25063','Canejan','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25064','Castellar de la Ribera','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25067','Castelldans','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25068','Castellnou de Seana','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25069','Castelló de Farfanya','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25070','Castellserà','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25071','Cava','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25072','Cervera','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25073','Cervià de les Garrigues','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25074','Ciutadilla','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25075','Clariana de Cardener','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25076','Cogul (El)','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25077','Coll de Nargó','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25078','Corbins','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25079','Cubells','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25081','Espluga Calba (L'')','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25082','Espot','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25085','Estaràs','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25086','Esterri d''Àneu','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25087','Esterri de Cardós','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25088','Estamariu','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25089','Farrera','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25092','Floresta (La)','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25093','Fondarella','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25094','Foradada','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25096','Fuliola (La)','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25097','Fulleda','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25098','Gavet de la Conca','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25099','Golmés','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25100','Gósol','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25101','Granadella (La)','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25102','Granja d''Escarp (La)','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25103','Granyanella','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25104','Granyena de Segarra','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25105','Granyena de les Garrigues','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25109','Guimerà','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25110','Guissona','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25111','Guixers','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25112','Ivars de Noguera','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25113','Ivars d''Urgell','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25114','Ivorra','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25115','Isona i Conca Dellà','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25118','Juncosa','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25119','Juneda','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25120','Lleida','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25121','Les','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25122','Linyola','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25123','Lladorre','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25124','Lladurs','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25125','Llardecans','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25126','Llavorsí','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25127','Lles de Cerdanya','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25128','Llimiana','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25129','Llobera','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25130','Maldà','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25131','Massalcoreig','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25132','Massoteres','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25133','Maials','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25134','Menàrguens','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25135','Miralcamp','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25136','Molsosa (La)','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25137','Mollerussa','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25138','Montgai','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25139','Montellà i Martinet','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25140','Montferrer i Castellbò','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25141','Montoliu de Segarra','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25142','Montoliu de Lleida','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25143','Montornès de Segarra','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25145','Nalec','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25146','Navès','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25148','Odèn','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25149','Oliana','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25150','Oliola','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25151','Olius','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25152','Oluges (Les)','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25153','Omellons (Els)','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25154','Omells de na Gaia (Els)','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25155','Organyà','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25156','Os de Balaguer','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25157','Ossó de Sió','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25158','Palau d''Anglesola (El)','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25161','Conca de Dalt','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25163','Coma i la Pedra (La)','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25164','Penelles','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25165','Peramola','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25166','Pinell de Solsonès','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25167','Pinós','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25168','Poal (El)','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25169','Pobla de Cérvoles (La)','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25170','Bellaguarda','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25171','Pobla de Segur (La)','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25172','Ponts','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25173','Pont de Suert (El)','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25174','Portella (La)','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25175','Prats i Sansor','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25176','Preixana','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25177','Preixens','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25179','Prullans','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25180','Puiggròs','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25181','Puigverd d''Agramunt','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25182','Puigverd de Lleida','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25183','Rialp','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25185','Ribera d''Urgellet','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25186','Riner','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25189','Rosselló','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25190','Salàs de Pallars','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25191','Sanaüja','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25192','Sant Guim de Freixenet','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25193','Sant Llorenç de Morunys','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25194','Sant Ramon','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25196','Sant Esteve de la Sarga','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25197','Sant Guim de la Plana','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25200','Sarroca de Lleida','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25201','Sarroca de Bellera','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25202','Senterada','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25203','Seu d''Urgell (La)','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25204','Seròs','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25205','Sidamon','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25206','Soleràs (El)','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25207','Solsona','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25208','Soriguera','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25209','Sort','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25210','Soses','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25211','Sudanell','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25212','Sunyer','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25215','Talarn','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25216','Talavera','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25217','Tàrrega','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25218','Tarrés','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25219','Tarroja de Segarra','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25220','Térmens','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25221','Tírvia','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25222','Tiurana','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25223','Torà','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25224','Torms (Els)','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25225','Tornabous','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25226','Torrebesses','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25227','Torre de Cabdella (La)','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25228','Torrefarrera','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25230','Torregrossa','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25231','Torrelameu','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25232','Torres de Segre','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25233','Torre-serona','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25234','Tremp','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25238','Vallbona de les Monges','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25239','Valls de Valira (Les)','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25240','Vallfogona de Balaguer','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25242','Verdú','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25243','Vielha e Mijaran','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25244','Vilagrassa','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25245','Vilaller','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25247','Vilamòs','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25248','Vilanova de Bellpuig','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25249','Vilanova de l''Aguda','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25250','Vilanova de Meià','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25251','Vilanova de Segrià','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25252','Vila-sana','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25253','Vilosell (El)','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25254','Vilanova de la Barca','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25255','Vinaixa','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25901','Vall de Cardós','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25902','Sant Martí de Riucorb','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25903','Guingueta d''Àneu (La)','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25904','Castell de Mur','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25905','Ribera d''Ondara','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25906','Valls d''Aguilar (Les)','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25907','Torrefeta i Florejacs','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25908','Fígols i Alinyà','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25909','Vansa i Fórnols (La)','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25910','Josa i Tuixén','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25911','Plans de Sió (Els)','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25912','Gimenells i el Pla de la Font','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'25913','Riu de Cerdanya','L',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26001','Ábalos','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26002','Agoncillo','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26003','Aguilar del Río Alhama','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26004','Ajamil','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26005','Albelda de Iregua','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26006','Alberite','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26007','Alcanadre','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26008','Aldeanueva de Ebro','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26009','Alesanco','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26010','Alesón','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26011','Alfaro','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26012','Almarza de Cameros','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26013','Anguciana','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26014','Anguiano','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26015','Arenzana de Abajo','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26016','Arenzana de Arriba','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26017','Arnedillo','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26018','Arnedo','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26019','Arrúbal','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26020','Ausejo','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26021','Autol','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26022','Azofra','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26023','Badarán','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26024','Bañares','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26025','Baños de Rioja','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26026','Baños de Río Tobía','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26027','Berceo','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26028','Bergasa','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26029','Bergasillas Bajera','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26030','Bezares','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26031','Bobadilla','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26032','Brieva de Cameros','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26033','Briñas','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26034','Briones','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26035','Cabezón de Cameros','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26036','Calahorra','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26037','Camprovín','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26038','Canales de la Sierra','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26039','Canillas de Río Tuerto','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26040','Cañas','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26041','Cárdenas','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26042','Casalarreina','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26043','Castañares de Rioja','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26044','Castroviejo','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26045','Cellorigo','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26046','Cenicero','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26047','Cervera del Río Alhama','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26048','Cidamón','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26049','Cihuri','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26050','Cirueña','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26051','Clavijo','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26052','Cordovín','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26053','Corera','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26054','Cornago','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26055','Corporales','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26056','Cuzcurrita de Río Tirón','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26057','Daroca de Rioja','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26058','Enciso','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26059','Entrena','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26060','Estollo','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26061','Ezcaray','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26062','Foncea','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26063','Fonzaleche','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26064','Fuenmayor','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26065','Galbárruli','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26066','Galilea','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26067','Gallinero de Cameros','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26068','Gimileo','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26069','Grañón','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26070','Grávalos','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26071','Haro','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26072','Herce','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26073','Herramélluri','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26074','Hervías','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26075','Hormilla','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26076','Hormilleja','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26077','Hornillos de Cameros','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26078','Hornos de Moncalvillo','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26079','Huércanos','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26080','Igea','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26081','Jalón de Cameros','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26082','Laguna de Cameros','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26083','Lagunilla del Jubera','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26084','Lardero','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26086','Ledesma de la Cogolla','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26087','Leiva','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26088','Leza de Río Leza','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26089','Logroño','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26091','Lumbreras','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26092','Manjarrés','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26093','Mansilla de la Sierra','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26094','Manzanares de Rioja','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26095','Matute','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26096','Medrano','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26098','Munilla','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26099','Murillo de Río Leza','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26100','Muro de Aguas','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26101','Muro en Cameros','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26102','Nájera','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26103','Nalda','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26104','Navajún','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26105','Navarrete','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26106','Nestares','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26107','Nieva de Cameros','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26108','Ocón','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26109','Ochánduri','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26110','Ojacastro','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26111','Ollauri','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26112','Ortigosa de Cameros','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26113','Pazuengos','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26114','Pedroso','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26115','Pinillos','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26117','Pradejón','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26118','Pradillo','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26119','Préjano','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26120','Quel','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26121','Rabanera','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26122','Rasillo de Cameros (El)','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26123','Redal (El)','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26124','Ribafrecha','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26125','Rincón de Soto','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26126','Robres del Castillo','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26127','Rodezno','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26128','Sajazarra','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26129','San Asensio','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26130','San Millán de la Cogolla','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26131','San Millán de Yécora','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26132','San Román de Cameros','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26134','Santa Coloma','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26135','Santa Engracia del Jubera','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26136','Santa Eulalia Bajera','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26138','Santo Domingo de la Calzada','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26139','San Torcuato','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26140','Santurde de Rioja','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26141','Santurdejo','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26142','San Vicente de la Sonsierra','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26143','Sojuela','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26144','Sorzano','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26145','Sotés','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26146','Soto en Cameros','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26147','Terroba','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26148','Tirgo','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26149','Tobía','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26150','Tormantos','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26151','Torrecilla en Cameros','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26152','Torrecilla sobre Alesanco','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26153','Torre en Cameros','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26154','Torremontalbo','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26155','Treviana','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26157','Tricio','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26158','Tudelilla','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26160','Uruñuela','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26161','Valdemadera','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26162','Valgañón','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26163','Ventosa','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26164','Ventrosa','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26165','Viguera','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26166','Villalba de Rioja','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26167','Villalobar de Rioja','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26168','Villamediana de Iregua','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26169','Villanueva de Cameros','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26170','Villar de Arnedo (El)','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26171','Villar de Torre','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26172','Villarejo','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26173','Villarroya','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26174','Villarta-Quintana','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26175','Villavelayo','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26176','Villaverde de Rioja','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26177','Villoslada de Cameros','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26178','Viniegra de Abajo','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26179','Viniegra de Arriba','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26180','Zarratón','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26181','Zarzosa','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'26183','Zorraquín','LO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27001','Abadín','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27002','Alfoz','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27003','Antas de Ulla','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27004','Baleira','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27005','Barreiros','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27006','Becerreá','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27007','Begonte','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27008','Bóveda','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27009','Carballedo','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27010','Castro de Rei','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27011','Castroverde','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27012','Cervantes','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27013','Cervo','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27014','Corgo (O)','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27015','Cospeito','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27016','Chantada','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27017','Folgoso do Courel','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27018','Fonsagrada (A)','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27019','Foz','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27020','Friol','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27021','Xermade','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27022','Guitiriz','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27023','Guntín','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27024','Incio (O)','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27025','Xove','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27026','Láncara','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27027','Lourenzá','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27028','Lugo','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27029','Meira','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27030','Mondoñedo','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27031','Monforte de Lemos','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27032','Monterroso','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27033','Muras','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27034','Navia de Suarna','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27035','Negueira de Muñiz','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27037','Nogais (As)','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27038','Ourol','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27039','Outeiro de Rei','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27040','Palas de Rei','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27041','Pantón','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27042','Paradela','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27043','Páramo (O)','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27044','Pastoriza (A)','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27045','Pedrafita do Cebreiro','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27046','Pol','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27047','Pobra do Brollón (A)','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27048','Pontenova (A)','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27049','Portomarín','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27050','Quiroga','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27051','Ribadeo','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27052','Ribas de Sil','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27053','Ribeira de Piquín','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27054','Riotorto','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27055','Samos','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27056','Rábade','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27057','Sarria','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27058','Saviñao (O)','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27059','Sober','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27060','Taboada','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27061','Trabada','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27062','Triacastela','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27063','Valadouro (O)','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27064','Vicedo (O)','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27065','Vilalba','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27066','Viveiro','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27901','Baralla','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'27902','Burela','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28001','Acebeda (La)','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28002','Ajalvir','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28003','Alameda del Valle','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28004','Álamo (El)','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28005','Alcalá de Henares','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28006','Alcobendas','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28007','Alcorcón','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28008','Aldea del Fresno','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28009','Algete','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28010','Alpedrete','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28011','Ambite','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28012','Anchuelo','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28013','Aranjuez','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28014','Arganda del Rey','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28015','Arroyomolinos','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28016','Atazar (El)','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28017','Batres','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28018','Becerril de la Sierra','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28019','Belmonte de Tajo','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28020','Berzosa del Lozoya','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28021','Berrueco (El)','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28022','Boadilla del Monte','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28023','Boalo (El)','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28024','Braojos','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28025','Brea de Tajo','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28026','Brunete','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28027','Buitrago del Lozoya','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28028','Bustarviejo','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28029','Cabanillas de la Sierra','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28030','Cabrera (La)','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28031','Cadalso de los Vidrios','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28032','Camarma de Esteruelas','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28033','Campo Real','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28034','Canencia','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28035','Carabaña','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28036','Casarrubuelos','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28037','Cenicientos','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28038','Cercedilla','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28039','Cervera de Buitrago','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28040','Ciempozuelos','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28041','Cobeña','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28042','Colmenar del Arroyo','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28043','Colmenar de Oreja','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28044','Colmenarejo','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28045','Colmenar Viejo','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28046','Collado Mediano','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28047','Collado Villalba','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28048','Corpa','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28049','Coslada','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28050','Cubas de la Sagra','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28051','Chapinería','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28052','Chinchón','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28053','Daganzo de Arriba','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28054','Escorial (El)','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28055','Estremera','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28056','Fresnedillas de la Oliva','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28057','Fresno de Torote','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28058','Fuenlabrada','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28059','Fuente el Saz de Jarama','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28060','Fuentidueña de Tajo','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28061','Galapagar','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28062','Garganta de los Montes','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28063','Gargantilla del Lozoya y Pinilla de Buitrago','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28064','Gascones','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28065','Getafe','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28066','Griñón','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28067','Guadalix de la Sierra','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28068','Guadarrama','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28069','Hiruela (La)','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28070','Horcajo de la Sierra-Aoslos','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28071','Horcajuelo de la Sierra','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28072','Hoyo de Manzanares','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28073','Humanes de Madrid','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28074','Leganés','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28075','Loeches','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28076','Lozoya','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28078','Madarcos','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28079','Madrid','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28080','Majadahonda','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28082','Manzanares el Real','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28083','Meco','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28084','Mejorada del Campo','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28085','Miraflores de la Sierra','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28086','Molar (El)','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28087','Molinos (Los)','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28088','Montejo de la Sierra','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28089','Moraleja de Enmedio','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28090','Moralzarzal','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28091','Morata de Tajuña','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28092','Móstoles','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28093','Navacerrada','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28094','Navalafuente','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28095','Navalagamella','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28096','Navalcarnero','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28097','Navarredonda y San Mamés','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28099','Navas del Rey','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28100','Nuevo Baztán','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28101','Olmeda de las Fuentes','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28102','Orusco de Tajuña','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28104','Paracuellos de Jarama','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28106','Parla','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28107','Patones','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28108','Pedrezuela','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28109','Pelayos de la Presa','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28110','Perales de Tajuña','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28111','Pezuela de las Torres','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28112','Pinilla del Valle','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28113','Pinto','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28114','Piñuécar-Gandullas','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28115','Pozuelo de Alarcón','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28116','Pozuelo del Rey','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28117','Prádena del Rincón','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28118','Puebla de la Sierra','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28119','Quijorna','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28120','Rascafría','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28121','Redueña','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28122','Ribatejada','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28123','Rivas-Vaciamadrid','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28124','Robledillo de la Jara','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28125','Robledo de Chavela','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28126','Robregordo','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28127','Rozas de Madrid (Las)','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28128','Rozas de Puerto Real','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28129','San Agustín del Guadalix','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28130','San Fernando de Henares','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28131','San Lorenzo de El Escorial','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28132','San Martín de la Vega','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28133','San Martín de Valdeiglesias','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28134','San Sebastián de los Reyes','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28135','Santa María de la Alameda','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28136','Santorcaz','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28137','Santos de la Humosa (Los)','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28138','Serna del Monte (La)','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28140','Serranillos del Valle','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28141','Sevilla la Nueva','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28143','Somosierra','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28144','Soto del Real','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28145','Talamanca de Jarama','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28146','Tielmes','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28147','Titulcia','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28148','Torrejón de Ardoz','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28149','Torrejón de la Calzada','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28150','Torrejón de Velasco','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28151','Torrelaguna','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28152','Torrelodones','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28153','Torremocha de Jarama','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28154','Torres de la Alameda','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28155','Valdaracete','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28156','Valdeavero','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28157','Valdelaguna','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28158','Valdemanco','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28159','Valdemaqueda','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28160','Valdemorillo','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28161','Valdemoro','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28162','Valdeolmos-Alalpardo','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28163','Valdepiélagos','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28164','Valdetorres de Jarama','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28165','Valdilecha','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28166','Valverde de Alcalá','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28167','Velilla de San Antonio','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28168','Vellón (El)','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28169','Venturada','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28170','Villaconejos','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28171','Villa del Prado','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28172','Villalbilla','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28173','Villamanrique de Tajo','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28174','Villamanta','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28175','Villamantilla','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28176','Villanueva de la Cañada','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28177','Villanueva del Pardillo','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28178','Villanueva de Perales','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28179','Villar del Olmo','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28180','Villarejo de Salvanés','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28181','Villaviciosa de Odón','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28182','Villavieja del Lozoya','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28183','Zarzalejo','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28901','Lozoyuela-Navas-Sieteiglesias','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28902','Puentes Viejas','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'28903','Tres Cantos','M',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29001','Alameda','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29002','Alcaucín','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29003','Alfarnate','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29004','Alfarnatejo','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29005','Algarrobo','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29006','Algatocín','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29007','Alhaurín de la Torre','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29008','Alhaurín el Grande','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29009','Almáchar','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29010','Almargen','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29011','Almogía','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29012','Álora','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29013','Alozaina','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29014','Alpandeire','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29015','Antequera','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29016','Árchez','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29017','Archidona','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29018','Ardales','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29019','Arenas','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29020','Arriate','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29021','Atajate','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29022','Benadalid','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29023','Benahavís','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29024','Benalauría','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29025','Benalmádena','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29026','Benamargosa','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29027','Benamocarra','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29028','Benaoján','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29029','Benarrabá','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29030','Borge (El)','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29031','Burgo (El)','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29032','Campillos','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29033','Canillas de Aceituno','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29034','Canillas de Albaida','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29035','Cañete la Real','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29036','Carratraca','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29037','Cartajima','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29038','Cártama','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29039','Casabermeja','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29040','Casarabonela','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29041','Casares','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29042','Coín','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29043','Colmenar','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29044','Comares','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29045','Cómpeta','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29046','Cortes de la Frontera','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29047','Cuevas Bajas','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29048','Cuevas del Becerro','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29049','Cuevas de San Marcos','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29050','Cútar','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29051','Estepona','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29052','Faraján','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29053','Frigiliana','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29054','Fuengirola','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29055','Fuente de Piedra','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29056','Gaucín','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29057','Genalguacil','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29058','Guaro','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29059','Humilladero','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29060','Igualeja','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29061','Istán','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29062','Iznate','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29063','Jimera de Líbar','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29064','Jubrique','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29065','Júzcar','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29066','Macharaviaya','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29067','Málaga','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29068','Manilva','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29069','Marbella','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29070','Mijas','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29071','Moclinejo','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29072','Mollina','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29073','Monda','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29074','Montejaque','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29075','Nerja','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29076','Ojén','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29077','Parauta','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29079','Periana','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29080','Pizarra','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29081','Pujerra','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29082','Rincón de la Victoria','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29083','Riogordo','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29084','Ronda','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29085','Salares','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29086','Sayalonga','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29087','Sedella','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29088','Sierra de Yeguas','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29089','Teba','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29090','Tolox','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29091','Torrox','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29092','Totalán','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29093','Valle de Abdalajís','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29094','Vélez-Málaga','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29095','Villanueva de Algaidas','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29096','Villanueva del Rosario','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29097','Villanueva del Trabuco','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29098','Villanueva de Tapia','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29099','Viñuela','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29100','Yunquera','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29901','Torremolinos','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29902','Villanueva de la Concepción','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29903','Montecorto','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'29904','Serrato','MA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30001','Abanilla','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30002','Abarán','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30003','Águilas','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30004','Albudeite','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30005','Alcantarilla','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30006','Aledo','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30007','Alguazas','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30008','Alhama de Murcia','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30009','Archena','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30010','Beniel','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30011','Blanca','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30012','Bullas','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30013','Calasparra','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30014','Campos del Río','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30015','Caravaca de la Cruz','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30016','Cartagena','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30017','Cehegín','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30018','Ceutí','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30019','Cieza','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30020','Fortuna','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30021','Fuente Álamo de Murcia','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30022','Jumilla','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30023','Librilla','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30024','Lorca','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30025','Lorquí','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30026','Mazarrón','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30027','Molina de Segura','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30028','Moratalla','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30029','Mula','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30030','Murcia','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30031','Ojós','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30032','Pliego','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30033','Puerto Lumbreras','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30034','Ricote','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30035','San Javier','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30036','San Pedro del Pinatar','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30037','Torre-Pacheco','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30038','Torres de Cotillas (Las)','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30039','Totana','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30040','Ulea','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30041','Unión (La)','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30042','Villanueva del Río Segura','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30043','Yecla','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30901','Santomera','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'30902','Alcázares (Los)','MU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31001','Abáigar','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31002','Abárzuza/Abartzuza','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31003','Abaurregaina/Abaurrea Alta','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31004','Abaurrepea/Abaurrea Baja','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31005','Aberin','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31006','Ablitas','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31007','Adiós','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31008','Aguilar de Codés','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31009','Aibar/Oibar','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31010','Altsasu/Alsasua','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31011','Allín/Allin','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31012','Allo','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31013','Améscoa Baja','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31014','Ancín','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31015','Andosilla','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31016','Ansoáin/Antsoain','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31017','Anue','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31018','Añorbe','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31019','Aoiz/Agoitz','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31020','Araitz','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31021','Aranarache/Aranaratxe','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31022','Arantza','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31023','Aranguren','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31024','Arano','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31025','Arakil','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31026','Aras','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31027','Arbizu','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31028','Arce/Artzi','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31029','Arcos (Los)','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31030','Arellano','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31031','Areso','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31032','Arguedas','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31033','Aria','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31034','Aribe','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31035','Armañanzas','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31036','Arróniz','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31037','Arruazu','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31038','Artajona','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31039','Artazu','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31040','Atez/Atetz','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31041','Ayegui/Aiegi','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31042','Azagra','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31043','Azuelo','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31044','Bakaiku','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31045','Barásoain','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31046','Barbarin','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31047','Bargota','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31048','Barillas','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31049','Basaburua','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31050','Baztan','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31051','Beire','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31052','Belascoáin','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31053','Berbinzana','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31054','Bertizarana','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31055','Betelu','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31056','Biurrun-Olcoz','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31057','Buñuel','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31058','Auritz/Burguete','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31059','Burgui/Burgi','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31060','Burlada/Burlata','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31061','Busto (El)','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31062','Cabanillas','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31063','Cabredo','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31064','Cadreita','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31065','Caparroso','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31066','Cárcar','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31067','Carcastillo','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31068','Cascante','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31069','Cáseda','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31070','Castejón','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31071','Castillonuevo','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31072','Cintruénigo','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31073','Ziordia','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31074','Cirauqui/Zirauki','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31075','Ciriza/Ziritza','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31076','Cizur','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31077','Corella','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31078','Cortes','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31079','Desojo','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31080','Dicastillo','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31081','Donamaria','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31082','Etxalar','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31083','Echarri/Etxarri','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31084','Etxarri Aranatz','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31085','Etxauri','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31086','Valle de Egüés/Eguesibar','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31087','Elgorriaga','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31088','Noáin (Valle de Elorz)/Noain (Elortzibar)','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31089','Enériz/Eneritz','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31090','Eratsun','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31091','Ergoiena','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31092','Erro','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31093','Ezcároz/Ezkaroze','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31094','Eslava','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31095','Esparza de Salazar/Espartza Zaraitzu','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31096','Espronceda','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31097','Estella-Lizarra','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31098','Esteribar','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31099','Etayo','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31100','Eulate','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31101','Ezcabarte','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31102','Ezkurra','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31103','Ezprogui','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31104','Falces','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31105','Fitero','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31106','Fontellas','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31107','Funes','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31108','Fustiñana','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31109','Galar','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31110','Gallipienzo/Galipentzu','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31111','Gallués/Galoze','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31112','Garaioa','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31113','Garde','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31114','Garínoain','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31115','Garralda','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31116','Genevilla','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31117','Goizueta','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31118','Goñi','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31119','Güesa/Gorza','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31120','Guesálaz/Gesalatz','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31121','Guirguillano','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31122','Huarte/Uharte','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31123','Uharte Arakil','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31124','Ibargoiti','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31125','Igúzquiza','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31126','Imotz','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31127','Irañeta','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31128','Isaba/Izaba','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31129','Ituren','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31130','Iturmendi','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31131','Iza/Itza','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31132','Izagaondoa','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31133','Izalzu/Itzaltzu','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31134','Jaurrieta','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31135','Javier','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31136','Juslapeña','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31137','Beintza-Labaien','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31138','Lakuntza','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31139','Lana','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31140','Lantz','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31141','Lapoblación','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31142','Larraga','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31143','Larraona','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31144','Larraun','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31145','Lazagurría','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31146','Leache/Leatxe','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31147','Legarda','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31148','Legaria','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31149','Leitza','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31150','Leoz/Leotz','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31151','Lerga','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31152','Lerín','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31153','Lesaka','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31154','Lezaun','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31155','Liédena','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31156','Lizoáin-Arriasgoiti','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31157','Lodosa','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31158','Lónguida/Longida','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31159','Lumbier','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31160','Luquin','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31161','Mañeru','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31162','Marañón','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31163','Marcilla','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31164','Mélida','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31165','Mendavia','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31166','Mendaza','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31167','Mendigorría','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31168','Metauten','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31169','Milagro','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31170','Mirafuentes','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31171','Miranda de Arga','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31172','Monreal/Elo','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31173','Monteagudo','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31174','Morentin','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31175','Mues','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31176','Murchante','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31177','Murieta','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31178','Murillo el Cuende','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31179','Murillo el Fruto','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31180','Muruzábal','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31181','Navascués/Nabaskoze','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31182','Nazar','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31183','Obanos','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31184','Oco','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31185','Ochagavía/Otsagabia','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31186','Odieta','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31187','Oiz','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31188','Olaibar','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31189','Olazti/Olazagutía','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31190','Olejua','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31191','Olite/Erriberri','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31192','Olóriz/Oloritz','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31193','Cendea de Olza/Oltza Zendea','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31194','Valle de Ollo/Ollaran','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31195','Orbaizeta','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31196','Orbara','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31197','Orísoain','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31198','Oronz/Orontze','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31199','Oroz-Betelu/Orotz-Betelu','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31200','Oteiza','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31201','Pamplona/Iruña','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31202','Peralta/Azkoien','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31203','Petilla de Aragón','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31204','Piedramillera','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31205','Pitillas','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31206','Puente la Reina/Gares','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31207','Pueyo','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31208','Ribaforada','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31209','Romanzado','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31210','Roncal/Erronkari','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31211','Orreaga/Roncesvalles','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31212','Sada','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31213','Saldías','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31214','Salinas de Oro/Jaitz','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31215','San Adrián','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31216','Sangüesa/Zangoza','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31217','San Martín de Unx','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31219','Sansol','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31220','Santacara','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31221','Doneztebe/Santesteban','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31222','Sarriés/Sartze','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31223','Sartaguda','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31224','Sesma','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31225','Sorlada','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31226','Sunbilla','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31227','Tafalla','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31228','Tiebas-Muruarte de Reta','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31229','Tirapu','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31230','Torralba del Río','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31231','Torres del Río','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31232','Tudela','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31233','Tulebras','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31234','Ucar','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31235','Ujué/Uxue','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31236','Ultzama','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31237','Unciti','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31238','Unzué/Untzue','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31239','Urdazubi/Urdax','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31240','Urdiain','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31241','Urraul Alto','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31242','Urraul Bajo','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31243','Urroz-Villa','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31244','Urroz','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31245','Urzainqui/Urzainki','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31246','Uterga','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31247','Uztárroz/Uztarroze','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31248','Luzaide/Valcarlos','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31249','Valtierra','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31250','Bera','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31251','Viana','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31252','Vidángoz/Bidankoze','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31253','Bidaurreta','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31254','Villafranca','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31255','Villamayor de Monjardín','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31256','Hiriberri/Villanueva de Aezkoa','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31257','Villatuerta','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31258','Villava/Atarrabia','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31259','Igantzi','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31260','Valle de Yerri/Deierri','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31261','Yesa','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31262','Zabalza/Zabaltza','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31263','Zubieta','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31264','Zugarramurdi','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31265','Zúñiga','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31901','Barañáin/Barañain','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31902','Berrioplano/Berriobeiti','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31903','Berriozar','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31904','Irurtzun','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31905','Beriáin','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31906','Orkoien','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31907','Zizur Mayor/Zizur Nagusia','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'31908','Lekunberri','',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32001','Allariz','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32002','Amoeiro','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32003','Arnoia (A)','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32004','Avión','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32005','Baltar','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32006','Bande','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32007','Baños de Molgas','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32008','Barbadás','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32009','Barco de Valdeorras (O)','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32010','Beade','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32011','Beariz','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32012','Blancos (Os)','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32013','Boborás','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32014','Bola (A)','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32015','Bolo (O)','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32016','Calvos de Randín','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32017','Carballeda de Valdeorras','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32018','Carballeda de Avia','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32019','Carballiño (O)','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32020','Cartelle','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32021','Castrelo do Val','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32022','Castrelo de Miño','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32023','Castro Caldelas','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32024','Celanova','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32025','Cenlle','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32026','Coles','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32027','Cortegada','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32028','Cualedro','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32029','Chandrexa de Queixa','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32030','Entrimo','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32031','Esgos','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32032','Xinzo de Limia','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32033','Gomesende','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32034','Gudiña (A)','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32035','Irixo (O)','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32036','Xunqueira de Ambía','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32037','Xunqueira de Espadanedo','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32038','Larouco','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32039','Laza','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32040','Leiro','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32041','Lobeira','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32042','Lobios','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32043','Maceda','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32044','Manzaneda','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32045','Maside','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32046','Melón','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32047','Merca (A)','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32048','Mezquita (A)','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32049','Montederramo','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32050','Monterrei','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32051','Muíños','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32052','Nogueira de Ramuín','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32053','Oímbra','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32054','Ourense','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32055','Paderne de Allariz','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32056','Padrenda','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32057','Parada de Sil','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32058','Pereiro de Aguiar (O)','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32059','Peroxa (A)','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32060','Petín','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32061','Piñor','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32062','Porqueira','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32063','Pobra de Trives (A)','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32064','Pontedeva','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32065','Punxín','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32066','Quintela de Leirado','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32067','Rairiz de Veiga','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32068','Ramirás','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32069','Ribadavia','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32070','San Xoán de Río','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32071','Riós','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32072','Rúa (A)','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32073','Rubiá','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32074','San Amaro','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32075','San Cibrao das Viñas','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32076','San Cristovo de Cea','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32077','Sandiás','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32078','Sarreaus','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32079','Taboadela','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32080','Teixeira (A)','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32081','Toén','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32082','Trasmiras','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32083','Veiga (A)','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32084','Verea','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32085','Verín','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32086','Viana do Bolo','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32087','Vilamarín','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32088','Vilamartín de Valdeorras','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32089','Vilar de Barrio','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32090','Vilar de Santos','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32091','Vilardevós','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'32092','Vilariño de Conso','OU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33001','Allande','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33002','Aller','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33003','Amieva','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33004','Avilés','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33005','Belmonte de Miranda','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33006','Bimenes','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33007','Boal','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33008','Cabrales','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33009','Cabranes','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33010','Candamo','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33011','Cangas del Narcea','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33012','Cangas de Onís','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33013','Caravia','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33014','Carreño','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33015','Caso','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33016','Castrillón','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33017','Castropol','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33018','Coaña','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33019','Colunga','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33020','Corvera de Asturias','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33021','Cudillero','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33022','Degaña','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33023','Franco (El)','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33024','Gijón','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33025','Gozón','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33026','Grado','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33027','Grandas de Salime','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33028','Ibias','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33029','Illano','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33030','Illas','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33031','Langreo','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33032','Laviana','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33033','Lena','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33034','Valdés','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33035','Llanera','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33036','Llanes','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33037','Mieres','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33038','Morcín','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33039','Muros de Nalón','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33040','Nava','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33041','Navia','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33042','Noreña','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33043','Onís','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33044','Oviedo','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33045','Parres','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33046','Peñamellera Alta','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33047','Peñamellera Baja','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33048','Pesoz','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33049','Piloña','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33050','Ponga','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33051','Pravia','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33052','Proaza','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33053','Quirós','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33054','Regueras (Las)','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33055','Ribadedeva','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33056','Ribadesella','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33057','Ribera de Arriba','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33058','Riosa','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33059','Salas','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33060','San Martín del Rey Aurelio','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33061','San Martín de Oscos','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33062','Santa Eulalia de Oscos','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33063','San Tirso de Abres','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33064','Santo Adriano','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33065','Sariego','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33066','Siero','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33067','Sobrescobio','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33068','Somiedo','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33069','Soto del Barco','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33070','Tapia de Casariego','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33071','Taramundi','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33072','Teverga','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33073','Tineo','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33074','Vegadeo','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33075','Villanueva de Oscos','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33076','Villaviciosa','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33077','Villayón','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'33078','Yernes y Tameza','O',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34001','Abarca de Campos','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34003','Abia de las Torres','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34004','Aguilar de Campoo','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34005','Alar del Rey','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34006','Alba de Cerrato','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34009','Amayuelas de Arriba','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34010','Ampudia','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34011','Amusco','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34012','Antigüedad','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34015','Arconada','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34017','Astudillo','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34018','Autilla del Pino','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34019','Autillo de Campos','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34020','Ayuela','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34022','Baltanás','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34023','Venta de Baños','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34024','Baquerín de Campos','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34025','Bárcena de Campos','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34027','Barruelo de Santullán','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34028','Báscones de Ojeda','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34029','Becerril de Campos','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34031','Belmonte de Campos','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34032','Berzosilla','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34033','Boada de Campos','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34034','Boadilla del Camino','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34035','Boadilla de Rioseco','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34036','Brañosera','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34037','Buenavista de Valdavia','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34038','Bustillo de la Vega','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34039','Bustillo del Páramo de Carrión','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34041','Calahorra de Boedo','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34042','Calzada de los Molinos','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34045','Capillas','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34046','Cardeñosa de Volpejera','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34047','Carrión de los Condes','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34048','Castil de Vela','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34049','Castrejón de la Peña','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34050','Castrillo de Don Juan','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34051','Castrillo de Onielo','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34052','Castrillo de Villavega','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34053','Castromocho','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34055','Cervatos de la Cueza','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34056','Cervera de Pisuerga','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34057','Cevico de la Torre','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34058','Cevico Navero','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34059','Cisneros','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34060','Cobos de Cerrato','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34061','Collazos de Boedo','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34062','Congosto de Valdavia','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34063','Cordovilla la Real','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34066','Cubillas de Cerrato','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34067','Dehesa de Montejo','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34068','Dehesa de Romanos','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34069','Dueñas','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34070','Espinosa de Cerrato','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34071','Espinosa de Villagonzalo','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34072','Frechilla','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34073','Fresno del Río','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34074','Frómista','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34076','Fuentes de Nava','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34077','Fuentes de Valdepero','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34079','Grijota','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34080','Guardo','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34081','Guaza de Campos','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34082','Hérmedes de Cerrato','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34083','Herrera de Pisuerga','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34084','Herrera de Valdecañas','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34086','Hontoria de Cerrato','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34087','Hornillos de Cerrato','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34088','Husillos','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34089','Itero de la Vega','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34091','Lagartos','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34092','Lantadilla','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34093','Vid de Ojeda (La)','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34094','Ledigos','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34096','Lomas','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34098','Magaz de Pisuerga','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34099','Manquillos','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34100','Mantinos','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34101','Marcilla de Campos','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34102','Mazariegos','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34103','Mazuecos de Valdeginate','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34104','Melgar de Yuso','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34106','Meneses de Campos','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34107','Micieces de Ojeda','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34108','Monzón de Campos','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34109','Moratinos','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34110','Mudá','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34112','Nogal de las Huertas','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34113','Olea de Boedo','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34114','Olmos de Ojeda','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34116','Osornillo','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34120','Palencia','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34121','Palenzuela','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34122','Páramo de Boedo','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34123','Paredes de Nava','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34124','Payo de Ojeda','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34125','Pedraza de Campos','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34126','Pedrosa de la Vega','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34127','Perales','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34129','Pino del Río','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34130','Piña de Campos','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34131','Población de Arroyo','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34132','Población de Campos','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34133','Población de Cerrato','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34134','Polentinos','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34135','Pomar de Valdivia','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34136','Poza de la Vega','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34137','Pozo de Urama','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34139','Prádanos de Ojeda','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34140','Puebla de Valdavia (La)','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34141','Quintana del Puente','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34143','Quintanilla de Onsoña','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34146','Reinoso de Cerrato','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34147','Renedo de la Vega','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34149','Requena de Campos','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34151','Respenda de la Peña','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34152','Revenga de Campos','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34154','Revilla de Collazos','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34155','Ribas de Campos','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34156','Riberos de la Cueza','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34157','Saldaña','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34158','Salinas de Pisuerga','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34159','San Cebrián de Campos','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34160','San Cebrián de Mudá','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34161','San Cristóbal de Boedo','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34163','San Mamés de Campos','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34165','San Román de la Cuba','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34167','Santa Cecilia del Alcor','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34168','Santa Cruz de Boedo','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34169','Santervás de la Vega','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34170','Santibáñez de Ecla','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34171','Santibáñez de la Peña','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34174','Santoyo','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34175','Serna (La)','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34176','Sotobañado y Priorato','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34177','Soto de Cerrato','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34178','Tabanera de Cerrato','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34179','Tabanera de Valdavia','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34180','Támara de Campos','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34181','Tariego de Cerrato','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34182','Torquemada','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34184','Torremormojón','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34185','Triollo','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34186','Valbuena de Pisuerga','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34189','Valdeolmillos','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34190','Valderrábano','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34192','Valde-Ucieza','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34196','Valle de Cerrato','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34199','Velilla del Río Carrión','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34201','Vertavillo','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34202','Villabasta de Valdavia','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34204','Villacidaler','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34205','Villaconancio','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34206','Villada','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34208','Villaeles de Valdavia','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34210','Villahán','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34211','Villaherreros','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34213','Villalaco','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34214','Villalba de Guardo','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34215','Villalcázar de Sirga','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34216','Villalcón','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34217','Villalobón','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34218','Villaluenga de la Vega','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34220','Villamartín de Campos','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34221','Villamediana','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34222','Villameriel','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34223','Villamoronta','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34224','Villamuera de la Cueza','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34225','Villamuriel de Cerrato','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34227','Villanueva del Rebollar','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34228','Villanuño de Valdavia','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34229','Villaprovedo','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34230','Villarmentero de Campos','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34231','Villarrabé','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34232','Villarramiel','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34233','Villasarracino','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34234','Villasila de Valdavia','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34236','Villaturde','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34237','Villaumbrales','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34238','Villaviudas','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34240','Villerías de Campos','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34241','Villodre','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34242','Villodrigo','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34243','Villoldo','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34245','Villota del Páramo','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34246','Villovieco','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34901','Osorno la Mayor','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34902','Valle del Retortillo','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34903','Loma de Ucieza','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'34904','Pernía (La)','P',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36001','Arbo','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36002','Barro','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36003','Baiona','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36004','Bueu','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36005','Caldas de Reis','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36006','Cambados','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36007','Campo Lameiro','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36008','Cangas','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36009','Cañiza (A)','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36010','Catoira','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36013','Covelo','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36014','Crecente','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36015','Cuntis','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36016','Dozón','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36017','Estrada (A)','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36018','Forcarei','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36019','Fornelos de Montes','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36020','Agolada','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36021','Gondomar','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36022','Grove (O)','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36023','Guarda (A)','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36024','Lalín','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36025','Lama (A)','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36026','Marín','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36027','Meaño','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36028','Meis','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36029','Moaña','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36030','Mondariz','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36031','Mondariz-Balneario','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36032','Moraña','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36033','Mos','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36034','Neves (As)','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36035','Nigrán','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36036','Oia','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36037','Pazos de Borbén','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36038','Pontevedra','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36039','Porriño (O)','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36040','Portas','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36041','Poio','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36042','Ponteareas','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36043','Ponte Caldelas','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36044','Pontecesures','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36045','Redondela','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36046','Ribadumia','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36047','Rodeiro','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36048','Rosal (O)','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36049','Salceda de Caselas','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36050','Salvaterra de Miño','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36051','Sanxenxo','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36052','Silleda','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36053','Soutomaior','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36054','Tomiño','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36055','Tui','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36056','Valga','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36057','Vigo','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36058','Vilaboa','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36059','Vila de Cruces','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36060','Vilagarcía de Arousa','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36061','Vilanova de Arousa','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36901','Illa de Arousa (A)','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'36902','Cerdedo-Cotobade','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37001','Abusejo','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37002','Agallas','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37003','Ahigal de los Aceiteros','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37004','Ahigal de Villarino','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37005','Alameda de Gardón (La)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37006','Alamedilla (La)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37007','Alaraz','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37008','Alba de Tormes','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37009','Alba de Yeltes','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37010','Alberca (La)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37011','Alberguería de Argañán (La)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37012','Alconada','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37013','Aldeacipreste','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37014','Aldeadávila de la Ribera','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37015','Aldea del Obispo','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37016','Aldealengua','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37017','Aldeanueva de Figueroa','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37018','Aldeanueva de la Sierra','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37019','Aldearrodrigo','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37020','Aldearrubia','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37021','Aldeaseca de Alba','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37022','Aldeaseca de la Frontera','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37023','Aldeatejada','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37024','Aldeavieja de Tormes','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37025','Aldehuela de la Bóveda','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37026','Aldehuela de Yeltes','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37027','Almenara de Tormes','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37028','Almendra','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37029','Anaya de Alba','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37030','Añover de Tormes','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37031','Arabayona de Mógica','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37032','Arapiles','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37033','Arcediano','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37034','Arco (El)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37035','Armenteros','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37036','San Miguel del Robledo','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37037','Atalaya (La)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37038','Babilafuente','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37039','Bañobárez','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37040','Barbadillo','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37041','Barbalos','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37042','Barceo','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37044','Barruecopardo','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37045','Bastida (La)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37046','Béjar','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37047','Beleña','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37049','Bermellar','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37050','Berrocal de Huebra','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37051','Berrocal de Salvatierra','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37052','Boada','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37054','Bodón (El)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37055','Bogajo','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37056','Bouza (La)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37057','Bóveda del Río Almar','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37058','Brincones','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37059','Buenamadre','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37060','Buenavista','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37061','Cabaco (El)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37062','Cabezabellosa de la Calzada','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37063','Cabeza de Béjar (La)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37065','Cabeza del Caballo','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37067','Cabrerizos','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37068','Cabrillas','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37069','Calvarrasa de Abajo','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37070','Calvarrasa de Arriba','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37071','Calzada de Béjar (La)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37072','Calzada de Don Diego','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37073','Calzada de Valdunciel','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37074','Campillo de Azaba','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37077','Campo de Peñaranda (El)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37078','Candelario','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37079','Canillas de Abajo','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37080','Cantagallo','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37081','Cantalapiedra','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37082','Cantalpino','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37083','Cantaracillo','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37085','Carbajosa de la Sagrada','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37086','Carpio de Azaba','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37087','Carrascal de Barregas','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37088','Carrascal del Obispo','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37089','Casafranca','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37090','Casas del Conde (Las)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37091','Casillas de Flores','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37092','Castellanos de Moriscos','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37096','Castillejo de Martín Viejo','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37097','Castraz','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37098','Cepeda','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37099','Cereceda de la Sierra','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37100','Cerezal de Peñahorcada','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37101','Cerralbo','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37102','Cerro (El)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37103','Cespedosa de Tormes','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37104','Cilleros de la Bastida','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37106','Cipérez','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37107','Ciudad Rodrigo','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37108','Coca de Alba','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37109','Colmenar de Montemayor','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37110','Cordovilla','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37112','Cristóbal','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37113','Cubo de Don Sancho (El)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37114','Chagarcía Medianero','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37115','Dios le Guarde','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37116','Doñinos de Ledesma','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37117','Doñinos de Salamanca','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37118','Éjeme','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37119','Encina (La)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37120','Encina de San Silvestre','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37121','Encinas de Abajo','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37122','Encinas de Arriba','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37123','Encinasola de los Comendadores','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37124','Endrinal','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37125','Escurial de la Sierra','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37126','Espadaña','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37127','Espeja','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37128','Espino de la Orbada','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37129','Florida de Liébana','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37130','Forfoleda','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37131','Frades de la Sierra','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37132','Fregeneda (La)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37133','Fresnedoso','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37134','Fresno Alhándiga','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37135','Fuente de San Esteban (La)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37136','Fuenteguinaldo','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37137','Fuenteliante','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37138','Fuenterroble de Salvatierra','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37139','Fuentes de Béjar','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37140','Fuentes de Oñoro','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37141','Gajates','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37142','Galindo y Perahuy','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37143','Galinduste','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37144','Galisancho','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37145','Gallegos de Argañán','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37146','Gallegos de Solmirón','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37147','Garcibuey','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37148','Garcihernández','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37149','Garcirrey','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37150','Gejuelo del Barro','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37151','Golpejas','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37152','Gomecello','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37154','Guadramiro','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37155','Guijo de Ávila','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37156','Guijuelo','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37157','Herguijuela de Ciudad Rodrigo','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37158','Herguijuela de la Sierra','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37159','Herguijuela del Campo','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37160','Hinojosa de Duero','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37161','Horcajo de Montemayor','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37162','Horcajo Medianero','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37163','Hoya (La)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37164','Huerta','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37165','Iruelos','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37166','Ituero de Azaba','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37167','Juzbado','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37168','Lagunilla','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37169','Larrodrigo','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37170','Ledesma','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37171','Ledrada','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37172','Linares de Riofrío','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37173','Lumbrales','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37174','Macotera','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37175','Machacón','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37176','Madroñal','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37177','Maíllo (El)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37178','Malpartida','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37179','Mancera de Abajo','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37180','Manzano (El)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37181','Martiago','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37182','Martinamor','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37183','Martín de Yeltes','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37184','Masueco','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37185','Castellanos de Villiquera','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37186','Mata de Ledesma (La)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37187','Matilla de los Caños del Río','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37188','Maya (La)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37189','Membribe de la Sierra','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37190','Mieza','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37191','Milano (El)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37192','Miranda de Azán','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37193','Miranda del Castañar','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37194','Mogarraz','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37195','Molinillo','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37196','Monforte de la Sierra','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37197','Monleón','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37198','Monleras','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37199','Monsagro','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37200','Montejo','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37201','Montemayor del Río','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37202','Monterrubio de Armuña','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37203','Monterrubio de la Sierra','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37204','Morasverdes','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37205','Morille','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37206','Moríñigo','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37207','Moriscos','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37208','Moronta','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37209','Mozárbez','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37211','Narros de Matalayegua','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37212','Navacarros','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37213','Nava de Béjar','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37214','Nava de Francia','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37215','Nava de Sotrobal','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37216','Navales','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37217','Navalmoral de Béjar','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37218','Navamorales','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37219','Navarredonda de la Rinconada','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37221','Navasfrías','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37222','Negrilla de Palencia','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37223','Olmedo de Camaces','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37224','Orbada (La)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37225','Pajares de la Laguna','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37226','Palacios del Arzobispo','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37228','Palaciosrubios','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37229','Palencia de Negrilla','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37230','Parada de Arriba','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37231','Parada de Rubiales','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37232','Paradinas de San Juan','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37233','Pastores','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37234','Payo (El)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37235','Pedraza de Alba','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37236','Pedrosillo de Alba','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37237','Pedrosillo de los Aires','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37238','Pedrosillo el Ralo','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37239','Pedroso de la Armuña (El)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37240','Pelabravo','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37241','Pelarrodríguez','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37242','Pelayos','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37243','Peña (La)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37244','Peñacaballera','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37245','Peñaparda','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37246','Peñaranda de Bracamonte','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37247','Peñarandilla','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37248','Peralejos de Abajo','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37249','Peralejos de Arriba','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37250','Pereña de la Ribera','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37251','Peromingo','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37252','Pinedas','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37253','Pino de Tormes (El)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37254','Pitiegua','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37255','Pizarral','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37256','Poveda de las Cintas','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37257','Pozos de Hinojo','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37258','Puebla de Azaba','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37259','Puebla de San Medel','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37260','Puebla de Yeltes','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37261','Puente del Congosto','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37262','Puertas','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37263','Puerto de Béjar','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37264','Puerto Seguro','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37265','Rágama','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37266','Redonda (La)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37267','Retortillo','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37268','Rinconada de la Sierra (La)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37269','Robleda','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37270','Robliza de Cojos','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37271','Rollán','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37272','Saelices el Chico','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37273','Sagrada (La)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37274','Salamanca','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37275','Saldeana','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37276','Salmoral','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37277','Salvatierra de Tormes','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37278','San Cristóbal de la Cuesta','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37279','Sancti-Spíritus','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37280','Sanchón de la Ribera','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37281','Sanchón de la Sagrada','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37282','Sanchotello','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37283','Sando','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37284','San Esteban de la Sierra','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37285','San Felices de los Gallegos','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37286','San Martín del Castañar','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37287','San Miguel de Valero','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37288','San Morales','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37289','San Muñoz','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37290','San Pedro del Valle','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37291','San Pedro de Rozados','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37292','San Pelayo de Guareña','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37293','Santa María de Sando','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37294','Santa Marta de Tormes','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37296','Santiago de la Puebla','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37297','Santibáñez de Béjar','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37298','Santibáñez de la Sierra','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37299','Santiz','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37300','Santos (Los)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37301','Sardón de los Frailes','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37302','Saucelle','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37303','Sahugo (El)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37304','Sepulcro-Hilario','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37305','Sequeros','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37306','Serradilla del Arroyo','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37307','Serradilla del Llano','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37309','Sierpe (La)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37310','Sieteiglesias de Tormes','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37311','Sobradillo','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37312','Sorihuela','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37313','Sotoserrano','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37314','Tabera de Abajo','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37315','Tala (La)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37316','Tamames','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37317','Tarazona de Guareña','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37318','Tardáguila','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37319','Tejado (El)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37320','Tejeda y Segoyuela','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37321','Tenebrón','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37322','Terradillos','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37323','Topas','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37324','Tordillos','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37325','Tornadizo (El)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37327','Torresmenudas','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37328','Trabanca','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37329','Tremedal de Tormes','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37330','Valdecarros','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37331','Valdefuentes de Sangusín','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37332','Valdehijaderos','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37333','Valdelacasa','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37334','Valdelageve','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37335','Valdelosa','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37336','Valdemierque','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37337','Valderrodrigo','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37338','Valdunciel','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37339','Valero','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37340','Valsalabroso','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37341','Valverde de Valdelacasa','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37342','Valverdón','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37343','Vallejera de Riofrío','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37344','Vecinos','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37345','Vega de Tirados','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37346','Veguillas (Las)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37347','Vellés (La)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37348','Ventosa del Río Almar','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37349','Vídola (La)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37350','Vilvestre','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37351','Villaflores','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37352','Villagonzalo de Tormes','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37353','Villalba de los Llanos','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37354','Villamayor','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37355','Villanueva del Conde','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37356','Villar de Argañán','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37357','Villar de Ciervo','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37358','Villar de Gallimazo','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37359','Villar de la Yegua','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37360','Villar de Peralonso','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37361','Villar de Samaniego','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37362','Villares de la Reina','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37363','Villares de Yeltes','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37364','Villarino de los Aires','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37365','Villarmayor','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37366','Villarmuerto','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37367','Villasbuenas','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37368','Villasdardo','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37369','Villaseco de los Gamitos','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37370','Villaseco de los Reyes','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37371','Villasrubias','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37372','Villaverde de Guareña','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37373','Villavieja de Yeltes','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37374','Villoria','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37375','Villoruela','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37376','Vitigudino','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37377','Yecla de Yeltes','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37378','Zamarra','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37379','Zamayón','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37380','Zarapicos','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37381','Zarza de Pumareda (La)','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'37382','Zorita de la Frontera','SA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39001','Alfoz de Lloredo','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39002','Ampuero','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39003','Anievas','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39004','Arenas de Iguña','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39005','Argoños','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39006','Arnuero','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39007','Arredondo','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39008','Astillero (El)','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39009','Bárcena de Cicero','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39010','Bárcena de Pie de Concha','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39011','Bareyo','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39012','Cabezón de la Sal','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39013','Cabezón de Liébana','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39014','Cabuérniga','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39015','Camaleño','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39016','Camargo','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39017','Campoo de Yuso','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39018','Cartes','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39019','Castañeda','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39020','Castro-Urdiales','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39021','Cieza','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39022','Cillorigo de Liébana','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39023','Colindres','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39024','Comillas','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39025','Corrales de Buelna (Los)','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39026','Corvera de Toranzo','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39027','Campoo de Enmedio','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39028','Entrambasaguas','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39029','Escalante','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39030','Guriezo','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39031','Hazas de Cesto','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39032','Hermandad de Campoo de Suso','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39033','Herrerías','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39034','Lamasón','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39035','Laredo','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39036','Liendo','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39037','Liérganes','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39038','Limpias','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39039','Luena','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39040','Marina de Cudeyo','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39041','Mazcuerras','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39042','Medio Cudeyo','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39043','Meruelo','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39044','Miengo','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39045','Miera','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39046','Molledo','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39047','Noja','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39048','Penagos','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39049','Peñarrubia','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39050','Pesaguero','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39051','Pesquera','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39052','Piélagos','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39053','Polaciones','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39054','Polanco','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39055','Potes','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39056','Puente Viesgo','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39057','Ramales de la Victoria','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39058','Rasines','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39059','Reinosa','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39060','Reocín','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39061','Ribamontán al Mar','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39062','Ribamontán al Monte','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39063','Rionansa','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39064','Riotuerto','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39065','Rozas de Valdearroyo (Las)','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39066','Ruente','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39067','Ruesga','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39068','Ruiloba','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39069','San Felices de Buelna','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39070','San Miguel de Aguayo','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39071','San Pedro del Romeral','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39072','San Roque de Riomiera','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39073','Santa Cruz de Bezana','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39074','Santa María de Cayón','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39075','Santander','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39076','Santillana del Mar','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39077','Santiurde de Reinosa','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39078','Santiurde de Toranzo','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39079','Santoña','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39080','San Vicente de la Barquera','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39081','Saro','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39082','Selaya','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39083','Soba','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39084','Solórzano','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39085','Suances','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39086','Tojos (Los)','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39087','Torrelavega','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39088','Tresviso','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39089','Tudanca','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39090','Udías','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39091','Valdáliga','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39092','Valdeolea','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39093','Valdeprado del Río','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39094','Valderredible','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39095','Val de San Vicente','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39096','Vega de Liébana','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39097','Vega de Pas','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39098','Villacarriedo','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39099','Villaescusa','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39100','Villafufre','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39101','Valle de Villaverde','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'39102','Voto','S',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40001','Abades','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40002','Adrada de Pirón','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40003','Adrados','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40004','Aguilafuente','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40005','Alconada de Maderuelo','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40006','Aldealcorvo','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40007','Aldealengua de Pedraza','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40008','Aldealengua de Santa María','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40009','Aldeanueva de la Serrezuela','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40010','Aldeanueva del Codonal','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40012','Aldea Real','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40013','Aldeasoña','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40014','Aldehorno','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40015','Aldehuela del Codonal','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40016','Aldeonte','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40017','Anaya','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40018','Añe','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40019','Arahuetes','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40020','Arcones','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40021','Arevalillo de Cega','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40022','Armuña','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40024','Ayllón','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40025','Barbolla','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40026','Basardilla','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40028','Bercial','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40029','Bercimuel','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40030','Bernardos','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40031','Bernuy de Porreros','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40032','Boceguillas','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40033','Brieva','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40034','Caballar','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40035','Cabañas de Polendos','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40036','Cabezuela','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40037','Calabazas de Fuentidueña','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40039','Campo de San Pedro','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40040','Cantalejo','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40041','Cantimpalos','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40043','Carbonero el Mayor','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40044','Carrascal del Río','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40045','Casla','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40046','Castillejo de Mesleón','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40047','Castro de Fuentidueña','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40048','Castrojimeno','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40049','Castroserna de Abajo','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40051','Castroserracín','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40052','Cedillo de la Torre','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40053','Cerezo de Abajo','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40054','Cerezo de Arriba','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40055','Cilleruelo de San Mamés','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40056','Cobos de Fuentidueña','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40057','Coca','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40058','Codorniz','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40059','Collado Hermoso','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40060','Condado de Castilnovo','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40061','Corral de Ayllón','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40062','Cubillo','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40063','Cuéllar','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40065','Chañe','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40068','Domingo García','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40069','Donhierro','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40070','Duruelo','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40071','Encinas','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40072','Encinillas','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40073','Escalona del Prado','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40074','Escarabajosa de Cabezas','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40075','Escobar de Polendos','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40076','Espinar (El)','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40077','Espirdo','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40078','Fresneda de Cuéllar','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40079','Fresno de Cantespino','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40080','Fresno de la Fuente','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40081','Frumales','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40082','Fuente de Santa Cruz','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40083','Fuente el Olmo de Fuentidueña','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40084','Fuente el Olmo de Íscar','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40086','Fuentepelayo','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40087','Fuentepiñel','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40088','Fuenterrebollo','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40089','Fuentesaúco de Fuentidueña','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40091','Fuentesoto','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40092','Fuentidueña','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40093','Gallegos','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40094','Garcillán','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40095','Gomezserracín','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40097','Grajera','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40099','Honrubia de la Cuesta','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40100','Hontalbilla','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40101','Hontanares de Eresma','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40103','Huertos (Los)','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40104','Ituero y Lama','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40105','Juarros de Riomoros','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40106','Juarros de Voltoya','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40107','Labajos','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40108','Laguna de Contreras','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40109','Languilla','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40110','Lastras de Cuéllar','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40111','Lastras del Pozo','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40112','Lastrilla (La)','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40113','Losa (La)','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40115','Maderuelo','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40118','Marazuela','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40119','Martín Miguel','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40120','Martín Muñoz de la Dehesa','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40121','Martín Muñoz de las Posadas','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40122','Marugán','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40123','Matabuena','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40124','Mata de Cuéllar','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40125','Matilla (La)','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40126','Melque de Cercos','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40127','Membibre de la Hoz','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40128','Migueláñez','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40129','Montejo de Arévalo','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40130','Montejo de la Vega de la Serrezuela','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40131','Monterrubio','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40132','Moral de Hornuez','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40134','Mozoncillo','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40135','Muñopedro','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40136','Muñoveros','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40138','Nava de la Asunción','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40139','Navafría','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40140','Navalilla','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40141','Navalmanzano','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40142','Navares de Ayuso','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40143','Navares de Enmedio','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40144','Navares de las Cuevas','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40145','Navas de Oro','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40146','Navas de San Antonio','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40148','Nieva','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40149','Olombrada','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40150','Orejana','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40151','Ortigosa de Pestaño','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40152','Otero de Herreros','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40154','Pajarejos','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40155','Palazuelos de Eresma','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40156','Pedraza','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40157','Pelayos del Arroyo','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40158','Perosillo','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40159','Pinarejos','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40160','Pinarnegrillo','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40161','Carabias','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40162','Prádena','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40163','Puebla de Pedraza','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40164','Rapariegos','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40165','Rebollo','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40166','Remondo','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40168','Riaguas de San Bartolomé','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40170','Riaza','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40171','Ribota','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40172','Riofrío de Riaza','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40173','Roda de Eresma','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40174','Sacramenia','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40176','Samboal','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40177','San Cristóbal de Cuéllar','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40178','San Cristóbal de la Vega','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40179','Sanchonuño','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40180','Sangarcía','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40181','Real Sitio de San Ildefonso','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40182','San Martín y Mudrián','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40183','San Miguel de Bernuy','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40184','San Pedro de Gaíllos','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40185','Santa María la Real de Nieva','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40186','Santa Marta del Cerro','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40188','Santiuste de Pedraza','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40189','Santiuste de San Juan Bautista','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40190','Santo Domingo de Pirón','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40191','Santo Tomé del Puerto','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40192','Sauquillo de Cabezas','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40193','Sebúlcor','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40194','Segovia','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40195','Sepúlveda','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40196','Sequera de Fresno','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40198','Sotillo','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40199','Sotosalbos','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40200','Tabanera la Luenga','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40201','Tolocirio','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40202','Torreadrada','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40203','Torrecaballeros','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40204','Torrecilla del Pinar','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40205','Torreiglesias','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40206','Torre Val de San Pedro','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40207','Trescasas','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40208','Turégano','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40210','Urueñas','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40211','Valdeprados','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40212','Valdevacas de Montejo','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40213','Valdevacas y Guijar','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40214','Valseca','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40215','Valtiendas','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40216','Valverde del Majano','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40218','Valle de Tabladillo','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40219','Vallelado','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40220','Valleruela de Pedraza','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40221','Valleruela de Sepúlveda','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40222','Veganzones','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40223','Vegas de Matute','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40224','Ventosilla y Tejadilla','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40225','Villacastín','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40228','Villaverde de Íscar','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40229','Villaverde de Montejo','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40230','Villeguillo','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40231','Yanguas de Eresma','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40233','Zarzuela del Monte','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40234','Zarzuela del Pinar','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40901','Ortigosa del Monte','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40902','Cozuelos de Fuentidueña','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40903','Marazoleja','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40904','Navas de Riofrío','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40905','Cuevas de Provanco','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'40906','San Cristóbal de Segovia','SG',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41001','Aguadulce','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41002','Alanís','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41003','Albaida del Aljarafe','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41004','Alcalá de Guadaíra','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41005','Alcalá del Río','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41006','Alcolea del Río','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41007','Algaba (La)','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41008','Algámitas','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41009','Almadén de la Plata','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41010','Almensilla','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41011','Arahal','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41012','Aznalcázar','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41013','Aznalcóllar','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41014','Badolatosa','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41015','Benacazón','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41016','Bollullos de la Mitación','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41017','Bormujos','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41018','Brenes','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41019','Burguillos','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41020','Cabezas de San Juan (Las)','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41021','Camas','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41022','Campana (La)','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41023','Cantillana','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41024','Carmona','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41025','Carrión de los Céspedes','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41026','Casariche','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41027','Castilblanco de los Arroyos','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41028','Castilleja de Guzmán','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41029','Castilleja de la Cuesta','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41030','Castilleja del Campo','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41031','Castillo de las Guardas (El)','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41032','Cazalla de la Sierra','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41033','Constantina','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41034','Coria del Río','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41035','Coripe','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41036','Coronil (El)','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41037','Corrales (Los)','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41038','Dos Hermanas','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41039','Écija','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41040','Espartinas','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41041','Estepa','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41042','Fuentes de Andalucía','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41043','Garrobo (El)','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41044','Gelves','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41045','Gerena','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41046','Gilena','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41047','Gines','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41048','Guadalcanal','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41049','Guillena','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41050','Herrera','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41051','Huévar del Aljarafe','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41052','Lantejuela','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41053','Lebrija','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41054','Lora de Estepa','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41055','Lora del Río','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41056','Luisiana (La)','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41057','Madroño (El)','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41058','Mairena del Alcor','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41059','Mairena del Aljarafe','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41060','Marchena','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41061','Marinaleda','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41062','Martín de la Jara','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41063','Molares (Los)','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41064','Montellano','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41065','Morón de la Frontera','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41066','Navas de la Concepción (Las)','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41067','Olivares','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41068','Osuna','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41069','Palacios y Villafranca (Los)','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41070','Palomares del Río','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41071','Paradas','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41072','Pedrera','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41073','Pedroso (El)','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41074','Peñaflor','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41075','Pilas','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41076','Pruna','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41077','Puebla de Cazalla (La)','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41078','Puebla de los Infantes (La)','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41079','Puebla del Río (La)','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41080','Real de la Jara (El)','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41081','Rinconada (La)','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41082','Roda de Andalucía (La)','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41083','Ronquillo (El)','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41084','Rubio (El)','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41085','Salteras','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41086','San Juan de Aznalfarache','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41087','Sanlúcar la Mayor','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41088','San Nicolás del Puerto','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41089','Santiponce','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41090','Saucejo (El)','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41091','Sevilla','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41092','Tocina','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41093','Tomares','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41094','Umbrete','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41095','Utrera','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41096','Valencina de la Concepción','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41097','Villamanrique de la Condesa','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41098','Villanueva del Ariscal','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41099','Villanueva del Río y Minas','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41100','Villanueva de San Juan','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41101','Villaverde del Río','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41102','Viso del Alcor (El)','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41901','Cañada Rosal','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41902','Isla Mayor','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41903','Cuervo de Sevilla (El)','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'41904','Palmar de Troya, El','SE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42001','Abejar','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42003','Adradas','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42004','Ágreda','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42006','Alconaba','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42007','Alcubilla de Avellaneda','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42008','Alcubilla de las Peñas','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42009','Aldealafuente','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42010','Aldealices','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42011','Aldealpozo','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42012','Aldealseñor','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42013','Aldehuela de Periáñez','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42014','Aldehuelas (Las)','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42015','Alentisque','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42016','Aliud','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42017','Almajano','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42018','Almaluez','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42019','Almarza','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42020','Almazán','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42021','Almazul','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42022','Almenar de Soria','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42023','Alpanseque','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42024','Arancón','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42025','Arcos de Jalón','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42026','Arenillas','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42027','Arévalo de la Sierra','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42028','Ausejo de la Sierra','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42029','Baraona','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42030','Barca','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42031','Barcones','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42032','Bayubas de Abajo','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42033','Bayubas de Arriba','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42034','Beratón','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42035','Berlanga de Duero','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42036','Blacos','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42037','Bliecos','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42038','Borjabad','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42039','Borobia','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42041','Buberos','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42042','Buitrago','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42043','Burgo de Osma-Ciudad de Osma','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42044','Cabrejas del Campo','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42045','Cabrejas del Pinar','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42046','Calatañazor','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42048','Caltojar','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42049','Candilichera','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42050','Cañamaque','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42051','Carabantes','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42052','Caracena','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42053','Carrascosa de Abajo','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42054','Carrascosa de la Sierra','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42055','Casarejos','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42056','Castilfrío de la Sierra','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42057','Castilruiz','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42058','Castillejo de Robledo','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42059','Centenera de Andaluz','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42060','Cerbón','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42061','Cidones','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42062','Cigudosa','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42063','Cihuela','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42064','Ciria','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42065','Cirujales del Río','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42068','Coscurita','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42069','Covaleda','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42070','Cubilla','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42071','Cubo de la Solana','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42073','Cueva de Ágreda','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42075','Dévanos','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42076','Deza','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42078','Duruelo de la Sierra','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42079','Escobosa de Almazán','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42080','Espeja de San Marcelino','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42081','Espejón','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42082','Estepa de San Juan','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42083','Frechilla de Almazán','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42084','Fresno de Caracena','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42085','Fuentearmegil','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42086','Fuentecambrón','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42087','Fuentecantos','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42088','Fuentelmonge','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42089','Fuentelsaz de Soria','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42090','Fuentepinilla','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42092','Fuentes de Magaña','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42093','Fuentestrún','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42094','Garray','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42095','Golmayo','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42096','Gómara','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42097','Gormaz','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42098','Herrera de Soria','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42100','Hinojosa del Campo','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42103','Langa de Duero','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42105','Liceras','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42106','Losilla (La)','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42107','Magaña','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42108','Maján','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42110','Matalebreras','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42111','Matamala de Almazán','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42113','Medinaceli','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42115','Miño de Medinaceli','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42116','Miño de San Esteban','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42117','Molinos de Duero','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42118','Momblona','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42119','Monteagudo de las Vicarías','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42120','Montejo de Tiermes','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42121','Montenegro de Cameros','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42123','Morón de Almazán','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42124','Muriel de la Fuente','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42125','Muriel Viejo','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42127','Nafría de Ucero','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42128','Narros','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42129','Navaleno','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42130','Nepas','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42131','Nolay','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42132','Noviercas','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42134','Ólvega','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42135','Oncala','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42139','Pinilla del Campo','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42140','Portillo de Soria','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42141','Póveda de Soria (La)','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42142','Pozalmuro','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42144','Quintana Redonda','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42145','Quintanas de Gormaz','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42148','Quiñonería','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42149','Rábanos (Los)','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42151','Rebollar','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42152','Recuerda','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42153','Rello','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42154','Renieblas','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42155','Retortillo de Soria','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42156','Reznos','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42157','Riba de Escalote (La)','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42158','Rioseco de Soria','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42159','Rollamienta','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42160','Royo (El)','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42161','Salduero','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42162','San Esteban de Gormaz','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42163','San Felices','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42164','San Leonardo de Yagüe','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42165','San Pedro Manrique','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42166','Santa Cruz de Yanguas','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42167','Santa María de Huerta','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42168','Santa María de las Hoyas','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42171','Serón de Nágima','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42172','Soliedra','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42173','Soria','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42174','Sotillo del Rincón','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42175','Suellacabras','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42176','Tajahuerce','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42177','Tajueco','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42178','Talveila','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42181','Tardelcuende','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42182','Taroda','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42183','Tejado','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42184','Torlengua','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42185','Torreblacos','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42187','Torrubia de Soria','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42188','Trévago','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42189','Ucero','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42190','Vadillo','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42191','Valdeavellano de Tera','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42192','Valdegeña','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42193','Valdelagua del Cerro','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42194','Valdemaluque','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42195','Valdenebro','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42196','Valdeprado','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42197','Valderrodilla','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42198','Valtajeros','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42200','Velamazán','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42201','Velilla de la Sierra','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42202','Velilla de los Ajos','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42204','Viana de Duero','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42205','Villaciervos','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42206','Villanueva de Gormaz','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42207','Villar del Ala','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42208','Villar del Campo','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42209','Villar del Río','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42211','Villares de Soria (Los)','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42212','Villasayas','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42213','Villaseca de Arciel','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42215','Vinuesa','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42216','Vizmanos','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42217','Vozmediano','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42218','Yanguas','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'42219','Yelo','SO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43001','Aiguamúrcia','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43002','Albinyana','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43003','Albiol (L'')','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43004','Alcanar','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43005','Alcover','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43006','Aldover','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43007','Aleixar (L'')','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43008','Alfara de Carles','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43009','Alforja','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43010','Alió','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43011','Almoster','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43012','Altafulla','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43013','Ametlla de Mar (L'')','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43014','Amposta','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43015','Arbolí','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43016','Arboç (L'')','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43017','Argentera (L'')','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43018','Arnes','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43019','Ascó','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43020','Banyeres del Penedès','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43021','Barberà de la Conca','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43022','Batea','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43023','Bellmunt del Priorat','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43024','Bellvei','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43025','Benifallet','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43026','Benissanet','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43027','Bisbal de Falset (La)','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43028','Bisbal del Penedès (La)','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43029','Blancafort','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43030','Bonastre','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43031','Borges del Camp (Les)','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43032','Bot','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43033','Botarell','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43034','Bràfim','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43035','Cabacés','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43036','Cabra del Camp','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43037','Calafell','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43038','Cambrils','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43039','Capafonts','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43040','Capçanes','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43041','Caseres','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43042','Castellvell del Camp','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43043','Catllar (El)','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43044','Sénia (La)','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43045','Colldejou','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43046','Conesa','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43047','Constantí','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43048','Corbera d''Ebre','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43049','Cornudella de Montsant','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43050','Creixell','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43051','Cunit','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43052','Xerta','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43053','Duesaigües','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43054','Espluga de Francolí (L'')','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43055','Falset','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43056','Fatarella (La)','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43057','Febró (La)','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43058','Figuera (La)','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43059','Figuerola del Camp','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43060','Flix','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43061','Forès','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43062','Freginals','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43063','Galera (La)','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43064','Gandesa','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43065','Garcia','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43066','Garidells (Els)','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43067','Ginestar','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43068','Godall','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43069','Gratallops','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43070','Guiamets (Els)','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43071','Horta de Sant Joan','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43072','Lloar (El)','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43073','Llorac','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43074','Llorenç del Penedès','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43075','Margalef','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43076','Marçà','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43077','Mas de Barberans','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43078','Masdenverge','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43079','Masllorenç','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43080','Masó (La)','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43081','Maspujols','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43082','Masroig (El)','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43083','Milà (El)','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43084','Miravet','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43085','Molar (El)','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43086','Montblanc','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43088','Montbrió del Camp','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43089','Montferri','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43090','Montmell (El)','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43091','Mont-ral','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43092','Mont-roig del Camp','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43093','Móra d''Ebre','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43094','Móra la Nova','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43095','Morell (El)','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43096','Morera de Montsant (La)','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43097','Nou de Gaià (La)','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43098','Nulles','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43099','Palma d''Ebre (La)','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43100','Pallaresos (Els)','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43101','Passanant i Belltall','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43102','Paüls','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43103','Perafort','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43104','Perelló (El)','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43105','Piles (Les)','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43106','Pinell de Brai (El)','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43107','Pira','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43108','Pla de Santa Maria (El)','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43109','Pobla de Mafumet (La)','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43110','Pobla de Massaluca (La)','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43111','Pobla de Montornès (La)','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43112','Poboleda','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43113','Pont d''Armentera (El)','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43114','Porrera','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43115','Pradell de la Teixeta','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43116','Prades','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43117','Prat de Comte','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43118','Pratdip','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43119','Puigpelat','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43120','Querol','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43121','Rasquera','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43122','Renau','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43123','Reus','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43124','Riba (La)','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43125','Riba-roja d''Ebre','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43126','Riera de Gaià (La)','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43127','Riudecanyes','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43128','Riudecols','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43129','Riudoms','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43130','Rocafort de Queralt','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43131','Roda de Berà','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43132','Rodonyà','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43133','Roquetes','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43134','Rourell (El)','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43135','Salomó','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43136','Sant Carles de la Ràpita','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43137','Sant Jaume dels Domenys','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43138','Santa Bàrbara','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43139','Santa Coloma de Queralt','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43140','Santa Oliva','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43141','Pontils','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43142','Sarral','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43143','Savallà del Comtat','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43144','Secuita (La)','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43145','Selva del Camp (La)','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43146','Senan','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43147','Solivella','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43148','Tarragona','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43149','Tivenys','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43150','Tivissa','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43151','Torre de Fontaubella (La)','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43152','Torre de l''Espanyol (La)','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43153','Torredembarra','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43154','Torroja del Priorat','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43155','Tortosa','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43156','Ulldecona','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43157','Ulldemolins','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43158','Vallclara','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43159','Vallfogona de Riucorb','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43160','Vallmoll','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43161','Valls','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43162','Vandellòs i l''Hospitalet de l''Infant','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43163','Vendrell (El)','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43164','Vespella de Gaià','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43165','Vilabella','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43166','Vilallonga del Camp','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43167','Vilanova d''Escornalbou','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43168','Vilanova de Prades','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43169','Vilaplana','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43170','Vila-rodona','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43171','Vila-seca','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43172','Vilaverd','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43173','Vilella Alta (La)','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43174','Vilella Baixa (La)','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43175','Vilalba dels Arcs','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43176','Vimbodí i Poblet','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43177','Vinebre','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43178','Vinyols i els Arcs','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43901','Deltebre','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43902','Sant Jaume d''Enveja','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43903','Camarles','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43904','Aldea (L'')','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43905','Salou','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43906','Ampolla (L'')','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'43907','Canonja, La','T',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44001','Ababuj','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44002','Abejuela','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44003','Aguatón','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44004','Aguaviva','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44005','Aguilar del Alfambra','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44006','Alacón','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44007','Alba','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44008','Albalate del Arzobispo','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44009','Albarracín','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44010','Albentosa','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44011','Alcaine','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44012','Alcalá de la Selva','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44013','Alcañiz','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44014','Alcorisa','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44016','Alfambra','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44017','Aliaga','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44018','Almohaja','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44019','Alobras','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44020','Alpeñés','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44021','Allepuz','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44022','Alloza','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44023','Allueva','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44024','Anadón','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44025','Andorra','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44026','Arcos de las Salinas','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44027','Arens de Lledó','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44028','Argente','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44029','Ariño','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44031','Azaila','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44032','Bádenas','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44033','Báguena','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44034','Bañón','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44035','Barrachina','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44036','Bea','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44037','Beceite','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44038','Belmonte de San José','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44039','Bello','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44040','Berge','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44041','Bezas','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44042','Blancas','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44043','Blesa','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44044','Bordón','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44045','Bronchales','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44046','Bueña','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44047','Burbáguena','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44048','Cabra de Mora','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44049','Calaceite','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44050','Calamocha','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44051','Calanda','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44052','Calomarde','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44053','Camañas','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44054','Camarena de la Sierra','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44055','Camarillas','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44056','Caminreal','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44059','Cantavieja','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44060','Cañada de Benatanduz','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44061','Cañada de Verich (La)','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44062','Cañada Vellida','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44063','Cañizar del Olivar','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44064','Cascante del Río','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44065','Castejón de Tornos','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44066','Castel de Cabra','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44067','Castelnou','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44068','Castelserás','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44070','Castellar (El)','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44071','Castellote','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44074','Cedrillas','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44075','Celadas','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44076','Cella','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44077','Cerollera (La)','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44080','Codoñera (La)','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44082','Corbalán','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44084','Cortes de Aragón','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44085','Cosa','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44086','Cretas','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44087','Crivillén','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44088','Cuba (La)','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44089','Cubla','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44090','Cucalón','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44092','Cuervo (El)','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44093','Cuevas de Almudén','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44094','Cuevas Labradas','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44096','Ejulve','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44097','Escorihuela','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44099','Escucha','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44100','Estercuel','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44101','Ferreruela de Huerva','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44102','Fonfría','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44103','Formiche Alto','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44105','Fórnoles','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44106','Fortanete','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44107','Foz-Calanda','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44108','Fresneda (La)','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44109','Frías de Albarracín','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44110','Fuenferrada','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44111','Fuentes Calientes','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44112','Fuentes Claras','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44113','Fuentes de Rubielos','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44114','Fuentespalda','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44115','Galve','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44116','Gargallo','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44117','Gea de Albarracín','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44118','Ginebrosa (La)','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44119','Griegos','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44120','Guadalaviar','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44121','Gúdar','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44122','Híjar','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44123','Hinojosa de Jarque','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44124','Hoz de la Vieja (La)','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44125','Huesa del Común','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44126','Iglesuela del Cid (La)','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44127','Jabaloyas','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44128','Jarque de la Val','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44129','Jatiel','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44130','Jorcas','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44131','Josa','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44132','Lagueruela','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44133','Lanzuela','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44135','Libros','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44136','Lidón','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44137','Linares de Mora','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44138','Loscos','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44141','Lledó','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44142','Maicas','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44143','Manzanera','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44144','Martín del Río','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44145','Mas de las Matas','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44146','Mata de los Olmos (La)','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44147','Mazaleón','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44148','Mezquita de Jarque','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44149','Mirambel','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44150','Miravete de la Sierra','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44151','Molinos','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44152','Monforte de Moyuela','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44153','Monreal del Campo','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44154','Monroyo','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44155','Montalbán','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44156','Monteagudo del Castillo','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44157','Monterde de Albarracín','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44158','Mora de Rubielos','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44159','Moscardón','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44160','Mosqueruela','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44161','Muniesa','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44163','Noguera de Albarracín','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44164','Nogueras','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44165','Nogueruelas','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44167','Obón','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44168','Odón','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44169','Ojos Negros','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44171','Olba','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44172','Oliete','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44173','Olmos (Los)','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44174','Orihuela del Tremedal','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44175','Orrios','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44176','Palomar de Arroyos','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44177','Pancrudo','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44178','Parras de Castellote (Las)','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44179','Peñarroya de Tastavins','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44180','Peracense','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44181','Peralejos','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44182','Perales del Alfambra','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44183','Pitarque','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44184','Plou','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44185','Pobo (El)','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44187','Portellada (La)','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44189','Pozondón','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44190','Pozuel del Campo','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44191','Puebla de Híjar (La)','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44192','Puebla de Valverde (La)','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44193','Puertomingalvo','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44194','Ráfales','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44195','Rillo','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44196','Riodeva','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44197','Ródenas','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44198','Royuela','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44199','Rubiales','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44200','Rubielos de la Cérida','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44201','Rubielos de Mora','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44203','Salcedillo','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44204','Saldón','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44205','Samper de Calanda','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44206','San Agustín','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44207','San Martín del Río','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44208','Santa Cruz de Nogueras','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44209','Santa Eulalia','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44210','Sarrión','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44211','Segura de los Baños','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44212','Seno','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44213','Singra','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44215','Terriente','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44216','Teruel','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44217','Toril y Masegoso','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44218','Tormón','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44219','Tornos','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44220','Torralba de los Sisones','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44221','Torrecilla de Alcañiz','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44222','Torrecilla del Rebollar','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44223','Torre de Arcas','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44224','Torre de las Arcas','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44225','Torre del Compte','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44226','Torrelacárcel','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44227','Torre los Negros','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44228','Torremocha de Jiloca','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44229','Torres de Albarracín','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44230','Torrevelilla','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44231','Torrijas','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44232','Torrijo del Campo','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44234','Tramacastiel','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44235','Tramacastilla','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44236','Tronchón','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44237','Urrea de Gaén','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44238','Utrillas','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44239','Valacloche','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44240','Valbona','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44241','Valdealgorfa','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44243','Valdecuenca','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44244','Valdelinares','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44245','Valdeltormo','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44246','Valderrobres','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44247','Valjunquera','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44249','Vallecillo (El)','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44250','Veguillas de la Sierra','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44251','Villafranca del Campo','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44252','Villahermosa del Campo','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44256','Villanueva del Rebollar de la Sierra','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44257','Villar del Cobo','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44258','Villar del Salz','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44260','Villarluengo','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44261','Villarquemado','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44262','Villarroya de los Pinares','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44263','Villastar','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44264','Villel','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44265','Vinaceite','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44266','Visiedo','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44267','Vivel del Río Martín','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'44268','Zoma (La)','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45001','Ajofrín','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45002','Alameda de la Sagra','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45003','Albarreal de Tajo','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45004','Alcabón','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45005','Alcañizo','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45006','Alcaudete de la Jara','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45007','Alcolea de Tajo','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45008','Aldea en Cabo','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45009','Aldeanueva de Barbarroya','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45010','Aldeanueva de San Bartolomé','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45011','Almendral de la Cañada','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45012','Almonacid de Toledo','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45013','Almorox','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45014','Añover de Tajo','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45015','Arcicóllar','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45016','Argés','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45017','Azután','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45018','Barcience','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45019','Bargas','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45020','Belvís de la Jara','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45021','Borox','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45022','Buenaventura','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45023','Burguillos de Toledo','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45024','Burujón','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45025','Cabañas de la Sagra','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45026','Cabañas de Yepes','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45027','Cabezamesada','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45028','Calera y Chozas','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45029','Caleruela','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45030','Calzada de Oropesa','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45031','Camarena','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45032','Camarenilla','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45033','Campillo de la Jara (El)','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45034','Camuñas','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45035','Cardiel de los Montes','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45036','Carmena','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45037','Carpio de Tajo (El)','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45038','Carranque','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45039','Carriches','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45040','Casar de Escalona (El)','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45041','Casarrubios del Monte','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45042','Casasbuenas','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45043','Castillo de Bayuela','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45045','Cazalegas','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45046','Cebolla','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45047','Cedillo del Condado','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45048','Cerralbos (Los)','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45049','Cervera de los Montes','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45050','Ciruelos','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45051','Cobeja','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45052','Cobisa','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45053','Consuegra','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45054','Corral de Almaguer','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45055','Cuerva','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45056','Chozas de Canales','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45057','Chueca','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45058','Domingo Pérez','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45059','Dosbarrios','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45060','Erustes','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45061','Escalona','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45062','Escalonilla','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45063','Espinoso del Rey','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45064','Esquivias','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45065','Estrella (La)','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45066','Fuensalida','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45067','Gálvez','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45068','Garciotum','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45069','Gerindote','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45070','Guadamur','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45071','Guardia (La)','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45072','Herencias (Las)','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45073','Herreruela de Oropesa','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45074','Hinojosa de San Vicente','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45075','Hontanar','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45076','Hormigos','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45077','Huecas','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45078','Huerta de Valdecarábanos','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45079','Iglesuela del Tiétar, La','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45080','Illán de Vacas','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45081','Illescas','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45082','Lagartera','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45083','Layos','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45084','Lillo','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45085','Lominchar','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45086','Lucillos','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45087','Madridejos','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45088','Magán','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45089','Malpica de Tajo','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45090','Manzaneque','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45091','Maqueda','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45092','Marjaliza','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45093','Marrupe','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45094','Mascaraque','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45095','Mata (La)','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45096','Mazarambroz','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45097','Mejorada','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45098','Menasalbas','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45099','Méntrida','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45100','Mesegar de Tajo','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45101','Miguel Esteban','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45102','Mocejón','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45103','Mohedas de la Jara','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45104','Montearagón','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45105','Montesclaros','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45106','Mora','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45107','Nambroca','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45108','Nava de Ricomalillo (La)','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45109','Navahermosa','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45110','Navalcán','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45111','Navalmoralejo','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45112','Navalmorales (Los)','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45113','Navalucillos (Los)','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45114','Navamorcuende','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45115','Noblejas','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45116','Noez','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45117','Nombela','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45118','Novés','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45119','Numancia de la Sagra','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45120','Nuño Gómez','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45121','Ocaña','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45122','Olías del Rey','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45123','Ontígola','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45124','Orgaz','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45125','Oropesa','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45126','Otero','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45127','Palomeque','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45128','Pantoja','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45129','Paredes de Escalona','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45130','Parrillas','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45131','Pelahustán','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45132','Pepino','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45133','Polán','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45134','Portillo de Toledo','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45135','Puebla de Almoradiel (La)','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45136','Puebla de Montalbán (La)','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45137','Pueblanueva (La)','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45138','Puente del Arzobispo (El)','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45139','Puerto de San Vicente','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45140','Pulgar','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45141','Quero','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45142','Quintanar de la Orden','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45143','Quismondo','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45144','Real de San Vicente (El)','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45145','Recas','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45146','Retamoso de la Jara','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45147','Rielves','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45148','Robledo del Mazo','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45149','Romeral (El)','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45150','San Bartolomé de las Abiertas','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45151','San Martín de Montalbán','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45152','San Martín de Pusa','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45153','San Pablo de los Montes','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45154','San Román de los Montes','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45155','Santa Ana de Pusa','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45156','Santa Cruz de la Zarza','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45157','Santa Cruz del Retamar','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45158','Santa Olalla','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45159','Sartajada','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45160','Segurilla','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45161','Seseña','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45162','Sevilleja de la Jara','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45163','Sonseca','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45164','Sotillo de las Palomas','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45165','Talavera de la Reina','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45166','Tembleque','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45167','Toboso (El)','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45168','Toledo','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45169','Torralba de Oropesa','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45170','Torrecilla de la Jara','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45171','Torre de Esteban Hambrán (La)','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45172','Torrico','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45173','Torrijos','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45174','Totanés','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45175','Turleque','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45176','Ugena','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45177','Urda','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45179','Valdeverdeja','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45180','Valmojado','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45181','Velada','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45182','Ventas con Peña Aguilera (Las)','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45183','Ventas de Retamosa (Las)','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45184','Ventas de San Julián (Las)','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45185','Villacañas','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45186','Villa de Don Fadrique (La)','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45187','Villafranca de los Caballeros','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45188','Villaluenga de la Sagra','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45189','Villamiel de Toledo','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45190','Villaminaya','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45191','Villamuelas','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45192','Villanueva de Alcardete','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45193','Villanueva de Bogas','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45194','Villarejo de Montalbán','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45195','Villarrubia de Santiago','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45196','Villaseca de la Sagra','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45197','Villasequilla','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45198','Villatobas','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45199','Viso de San Juan (El)','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45200','Yébenes (Los)','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45201','Yeles','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45202','Yepes','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45203','Yuncler','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45204','Yunclillos','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45205','Yuncos','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'45901','Santo Domingo-Caudilla','TO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46001','Ademuz','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46002','Ador','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46003','Atzeneta d''Albaida','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46004','Agullent','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46005','Alaquàs','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46006','Albaida','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46007','Albal','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46008','Albalat de la Ribera','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46009','Albalat dels Sorells','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46010','Albalat dels Tarongers','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46011','Alberic','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46012','Alborache','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46013','Alboraia/Alboraya','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46014','Albuixech','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46015','Alcàsser','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46016','Alcàntera de Xúquer','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46017','Alzira','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46018','Alcublas','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46019','Alcúdia (l'')','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46020','Alcúdia de Crespins (l'')','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46021','Aldaia','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46022','Alfafar','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46023','Alfauir','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46024','Alfara de la Baronia','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46025','Alfara del Patriarca','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46026','Alfarp','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46027','Alfarrasí','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46028','Algar de Palancia','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46029','Algemesí','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46030','Algímia d’Alfara','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46031','Alginet','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46032','Almàssera','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46033','Almiserà','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46034','Almoines','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46035','Almussafes','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46036','Alpuente','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46037','Alqueria de la Comtessa (l'')','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46038','Andilla','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46039','Anna','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46040','Antella','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46041','Aras de los Olmos','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46042','Aielo de Malferit','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46043','Aielo de Rugat','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46044','Ayora','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46045','Barxeta','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46046','Barx','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46047','Bèlgida','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46048','Bellreguard','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46049','Bellús','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46050','Benagéber','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46051','Benaguasil','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46052','Benavites','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46053','Beneixida','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46054','Benetússer','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46055','Beniarjó','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46056','Beniatjar','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46057','Benicolet','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46058','Benifairó de les Valls','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46059','Benifairó de la Valldigna','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46060','Benifaió','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46061','Beniflá','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46062','Benigànim','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46063','Benimodo','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46064','Benimuslem','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46065','Beniparrell','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46066','Benirredrà','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46067','Benissanó','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46068','Benissoda','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46069','Benissuera','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46070','Bétera','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46071','Bicorp','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46072','Bocairent','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46073','Bolbaite','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46074','Bonrepòs i Mirambell','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46075','Bufali','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46076','Bugarra','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46077','Buñol','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46078','Burjassot','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46079','Calles','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46080','Camporrobles','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46081','Canals','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46082','Canet d''En Berenguer','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46083','Carcaixent','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46084','Càrcer','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46085','Carlet','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46086','Carrícola','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46087','Casas Altas','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46088','Casas Bajas','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46089','Casinos','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46090','Castelló de Rugat','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46091','Castellonet de la Conquesta','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46092','Castielfabib','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46093','Catadau','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46094','Catarroja','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46095','Caudete de las Fuentes','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46096','Cerdà','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46097','Cofrentes','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46098','Corbera','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46099','Cortes de Pallás','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46100','Cotes','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46101','Quart de les Valls','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46102','Quart de Poblet','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46103','Quartell','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46104','Quatretonda','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46105','Cullera','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46106','Chelva','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46107','Chella','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46108','Chera','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46109','Cheste','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46110','Xirivella','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46111','Chiva','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46112','Chulilla','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46113','Daimús','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46114','Domeño','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46115','Dos Aguas','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46116','Eliana (l'')','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46117','Emperador','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46118','Enguera','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46119','Ènova (l'')','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46120','Estivella','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46121','Estubeny','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46122','Faura','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46123','Favara','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46124','Fontanars dels Alforins','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46125','Fortaleny','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46126','Foios','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46127','Font d''En Carròs (la)','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46128','Font de la Figuera (la)','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46129','Fuenterrobles','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46130','Gavarda','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46131','Gandia','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46132','Genovés, el','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46133','Gestalgar','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46134','Gilet','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46135','Godella','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46136','Godelleta','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46137','Granja de la Costera (la)','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46138','Guadasequies/Guadasséquies','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46139','Guadassuar','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46140','Guardamar de la Safor','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46141','Higueruelas','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46142','Jalance','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46143','Xeraco','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46144','Jarafuel','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46145','Xàtiva','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46146','Xeresa','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46147','Llíria','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46148','Loriguilla','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46149','Losa del Obispo','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46150','Llutxent','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46151','Llocnou d''En Fenollet','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46152','Llocnou de la Corona','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46153','Llocnou de Sant Jeroni','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46154','Llanera de Ranes','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46155','Llaurí','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46156','Llombai','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46157','Llosa de Ranes (la)','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46158','Macastre','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46159','Manises','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46160','Manuel','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46161','Marines','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46162','Massalavés','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46163','Massalfassar','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46164','Massamagrell','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46165','Massanassa','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46166','Meliana','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46167','Millares','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46168','Miramar','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46169','Mislata','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46170','Mogente/Moixent','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46171','Moncada','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46172','Montserrat','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46173','Montaverner','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46174','Montesa','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46175','Montitxelvo/Montichelvo','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46176','Montroi/Montroy','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46177','Museros','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46178','Nàquera/Náquera','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46179','Navarrés','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46180','Novelé/Novetlè','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46181','Oliva','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46182','Olocau','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46183','Olleria (l'')','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46184','Ontinyent','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46185','Otos','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46186','Paiporta','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46187','Palma de Gandía','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46188','Palmera','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46189','Palomar (el)','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46190','Paterna','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46191','Pedralba','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46192','Petrés','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46193','Picanya','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46194','Picassent','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46195','Piles','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46196','Pinet','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46197','Polinyà de Xúquer','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46198','Potries','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46199','Pobla de Farnals (la)','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46200','Pobla del Duc (la)','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46201','Puebla de San Miguel','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46202','Pobla de Vallbona (la)','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46203','Pobla Llarga (la)','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46204','Puig de Santa María, El','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46205','Puçol','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46206','Quesa','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46207','Rafelbunyol','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46208','Rafelcofer','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46209','Rafelguaraf','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46210','Ráfol de Salem','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46211','Real de Gandia, el','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46212','Real','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46213','Requena','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46214','Riba-roja de Túria','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46215','Riola','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46216','Rocafort','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46217','Rotglà i Corberà','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46218','Rótova','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46219','Rugat','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46220','Sagunto/Sagunt','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46221','Salem','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46222','Sant Joanet','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46223','Sedaví','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46224','Segart','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46225','Sellent','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46226','Sempere','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46227','Senyera','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46228','Serra','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46229','Siete Aguas','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46230','Silla','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46231','Simat de la Valldigna','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46232','Sinarcas','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46233','Sollana','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46234','Sot de Chera','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46235','Sueca','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46236','Sumacàrcer','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46237','Tavernes Blanques','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46238','Tavernes de la Valldigna','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46239','Teresa de Cofrentes','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46240','Terrateig','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46241','Titaguas','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46242','Torrebaja','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46243','Torrella','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46244','Torrent','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46245','Torres Torres','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46246','Tous','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46247','Tuéjar','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46248','Turís','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46249','Utiel','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46250','València','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46251','Vallada','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46252','Vallanca','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46253','Vallés','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46254','Venta del Moro','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46255','Vilallonga/Villalonga','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46256','Vilamarxant','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46257','Villanueva de Castellón','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46258','Villar del Arzobispo','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46259','Villargordo del Cabriel','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46260','Vinalesa','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46261','Yátova','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46262','Yesa (La)','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46263','Zarra','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46902','Gátova','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46903','San Antonio de Benagéber','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'46904','Benicull de Xúquer','V',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47001','Adalia','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47002','Aguasal','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47003','Aguilar de Campos','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47004','Alaejos','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47005','Alcazarén','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47006','Aldea de San Miguel','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47007','Aldeamayor de San Martín','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47008','Almenara de Adaja','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47009','Amusquillo','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47010','Arroyo de la Encomienda','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47011','Ataquines','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47012','Bahabón','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47013','Barcial de la Loma','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47014','Barruelo del Valle','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47015','Becilla de Valderaduey','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47016','Benafarces','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47017','Bercero','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47018','Berceruelo','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47019','Berrueces','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47020','Bobadilla del Campo','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47021','Bocigas','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47022','Bocos de Duero','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47023','Boecillo','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47024','Bolaños de Campos','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47025','Brahojos de Medina','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47026','Bustillo de Chaves','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47027','Cabezón de Pisuerga','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47028','Cabezón de Valderaduey','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47029','Cabreros del Monte','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47030','Campaspero','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47031','Campillo (El)','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47032','Camporredondo','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47033','Canalejas de Peñafiel','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47034','Canillas de Esgueva','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47035','Carpio','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47036','Casasola de Arión','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47037','Castrejón de Trabancos','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47038','Castrillo de Duero','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47039','Castrillo-Tejeriego','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47040','Castrobol','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47041','Castrodeza','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47042','Castromembibre','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47043','Castromonte','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47044','Castronuevo de Esgueva','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47045','Castronuño','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47046','Castroponce','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47047','Castroverde de Cerrato','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47048','Ceinos de Campos','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47049','Cervillego de la Cruz','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47050','Cigales','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47051','Ciguñuela','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47052','Cistérniga','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47053','Cogeces de Íscar','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47054','Cogeces del Monte','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47055','Corcos','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47056','Corrales de Duero','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47057','Cubillas de Santa Marta','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47058','Cuenca de Campos','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47059','Curiel de Duero','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47060','Encinas de Esgueva','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47061','Esguevillas de Esgueva','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47062','Fombellida','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47063','Fompedraza','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47064','Fontihoyuelo','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47065','Fresno el Viejo','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47066','Fuensaldaña','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47067','Fuente el Sol','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47068','Fuente-Olmedo','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47069','Gallegos de Hornija','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47070','Gatón de Campos','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47071','Geria','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47073','Herrín de Campos','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47074','Hornillos de Eresma','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47075','Íscar','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47076','Laguna de Duero','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47077','Langayo','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47078','Lomoviejo','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47079','Llano de Olmedo','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47080','Manzanillo','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47081','Marzales','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47082','Matapozuelos','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47083','Matilla de los Caños','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47084','Mayorga','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47085','Medina del Campo','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47086','Medina de Rioseco','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47087','Megeces','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47088','Melgar de Abajo','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47089','Melgar de Arriba','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47090','Mojados','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47091','Monasterio de Vega','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47092','Montealegre de Campos','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47093','Montemayor de Pililla','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47094','Moral de la Reina','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47095','Moraleja de las Panaderas','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47096','Morales de Campos','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47097','Mota del Marqués','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47098','Mucientes','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47099','Mudarra (La)','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47100','Muriel','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47101','Nava del Rey','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47102','Nueva Villa de las Torres','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47103','Olivares de Duero','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47104','Olmedo','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47105','Olmos de Esgueva','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47106','Olmos de Peñafiel','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47109','Palazuelo de Vedija','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47110','Parrilla (La)','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47111','Pedraja de Portillo (La)','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47112','Pedrajas de San Esteban','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47113','Pedrosa del Rey','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47114','Peñafiel','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47115','Peñaflor de Hornija','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47116','Pesquera de Duero','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47117','Piña de Esgueva','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47118','Piñel de Abajo','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47119','Piñel de Arriba','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47121','Pollos','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47122','Portillo','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47123','Pozal de Gallinas','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47124','Pozaldez','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47125','Pozuelo de la Orden','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47126','Puras','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47127','Quintanilla de Arriba','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47128','Quintanilla del Molar','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47129','Quintanilla de Onésimo','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47130','Quintanilla de Trigueros','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47131','Rábano','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47132','Ramiro','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47133','Renedo de Esgueva','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47134','Roales de Campos','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47135','Robladillo','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47137','Roturas','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47138','Rubí de Bracamonte','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47139','Rueda','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47140','Saelices de Mayorga','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47141','Salvador de Zapardiel','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47142','San Cebrián de Mazote','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47143','San Llorente','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47144','San Martín de Valvení','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47145','San Miguel del Arroyo','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47146','San Miguel del Pino','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47147','San Pablo de la Moraleja','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47148','San Pedro de Latarce','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47149','San Pelayo','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47150','San Román de Hornija','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47151','San Salvador','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47152','Santa Eufemia del Arroyo','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47153','Santervás de Campos','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47154','Santibáñez de Valcorba','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47155','Santovenia de Pisuerga','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47156','San Vicente del Palacio','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47157','Sardón de Duero','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47158','Seca (La)','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47159','Serrada','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47160','Siete Iglesias de Trabancos','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47161','Simancas','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47162','Tamariz de Campos','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47163','Tiedra','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47164','Tordehumos','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47165','Tordesillas','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47166','Torrecilla de la Abadesa','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47167','Torrecilla de la Orden','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47168','Torrecilla de la Torre','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47169','Torre de Esgueva','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47170','Torre de Peñafiel','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47171','Torrelobatón','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47172','Torrescárcela','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47173','Traspinedo','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47174','Trigueros del Valle','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47175','Tudela de Duero','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47176','Unión de Campos (La)','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47177','Urones de Castroponce','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47178','Urueña','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47179','Valbuena de Duero','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47180','Valdearcos de la Vega','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47181','Valdenebro de los Valles','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47182','Valdestillas','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47183','Valdunquillo','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47184','Valoria la Buena','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47185','Valverde de Campos','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47186','Valladolid','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47187','Vega de Ruiponce','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47188','Vega de Valdetronco','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47189','Velascálvaro','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47190','Velilla','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47191','Velliza','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47192','Ventosa de la Cuesta','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47193','Viana de Cega','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47194','Viloria','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47195','Villabáñez','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47196','Villabaruz de Campos','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47197','Villabrágima','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47198','Villacarralón','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47199','Villacid de Campos','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47200','Villaco','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47203','Villafrades de Campos','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47204','Villafranca de Duero','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47205','Villafrechós','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47206','Villafuerte','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47207','Villagarcía de Campos','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47208','Villagómez la Nueva','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47209','Villalán de Campos','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47210','Villalar de los Comuneros','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47211','Villalba de la Loma','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47212','Villalba de los Alcores','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47213','Villalbarba','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47214','Villalón de Campos','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47215','Villamuriel de Campos','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47216','Villán de Tordesillas','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47217','Villanubla','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47218','Villanueva de Duero','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47219','Villanueva de la Condesa','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47220','Villanueva de los Caballeros','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47221','Villanueva de los Infantes','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47222','Villanueva de San Mancio','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47223','Villardefrades','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47224','Villarmentero de Esgueva','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47225','Villasexmir','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47226','Villavaquerín','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47227','Villavellid','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47228','Villaverde de Medina','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47229','Villavicencio de los Caballeros','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47230','Wamba','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47231','Zaratán','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'47232','Zarza (La)','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48001','Abadiño','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48002','Abanto y Ciérvana-Abanto Zierbena','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48003','Amorebieta-Etxano','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48004','Amoroto','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48005','Arakaldo','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48006','Arantzazu','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48007','Munitibar-Arbatzegi Gerrikaitz','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48008','Artzentales','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48009','Arrankudiaga','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48010','Arrieta','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48011','Arrigorriaga','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48012','Bakio','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48013','Barakaldo','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48014','Barrika','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48015','Basauri','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48016','Berango','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48017','Bermeo','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48018','Berriatua','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48019','Berriz','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48020','Bilbao','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48021','Busturia','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48022','Karrantza Harana/Valle de Carranza','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48023','Artea','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48024','Zeanuri','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48025','Zeberio','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48026','Dima','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48027','Durango','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48028','Ea','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48029','Etxebarri','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48030','Etxebarria','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48031','Elantxobe','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48032','Elorrio','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48033','Ereño','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48034','Ermua','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48035','Fruiz','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48036','Galdakao','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48037','Galdames','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48038','Gamiz-Fika','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48039','Garai','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48040','Gatika','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48041','Gautegiz Arteaga','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48042','Gordexola','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48043','Gorliz','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48044','Getxo','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48045','Güeñes','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48046','Gernika-Lumo','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48047','Gizaburuaga','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48048','Ibarrangelu','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48049','Ispaster','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48050','Izurtza','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48051','Lanestosa','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48052','Larrabetzu','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48053','Laukiz','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48054','Leioa','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48055','Lemoa','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48056','Lemoiz','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48057','Lekeitio','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48058','Mallabia','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48059','Mañaria','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48060','Markina-Xemein','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48061','Maruri-Jatabe','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48062','Mendata','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48063','Mendexa','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48064','Meñaka','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48065','Ugao-Miraballes','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48066','Morga','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48067','Muxika','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48068','Mundaka','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48069','Mungia','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48070','Aulesti','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48071','Muskiz','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48072','Otxandio','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48073','Ondarroa','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48074','Urduña-Orduña','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48075','Orozko','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48076','Sukarrieta','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48077','Plentzia','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48078','Portugalete','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48079','Errigoiti','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48080','Valle de Trápaga-Trapagaran','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48081','Lezama','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48082','Santurtzi','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48083','Ortuella','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48084','Sestao','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48085','Sopela','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48086','Sopuerta','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48087','Trucios-Turtzioz','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48088','Ubide','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48089','Urduliz','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48090','Balmaseda','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48091','Atxondo','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48092','Bedia','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48093','Areatza','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48094','Igorre','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48095','Zaldibar','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48096','Zalla','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48097','Zaratamo','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48901','Derio','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48902','Erandio','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48903','Loiu','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48904','Sondika','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48905','Zamudio','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48906','Forua','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48907','Kortezubi','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48908','Murueta','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48909','Nabarniz','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48910','Iurreta','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48911','Ajangiz','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48912','Alonsotegi','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48913','Zierbena','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48914','Arratzu','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'48915','Ziortza-Bolibar','BI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49002','Abezames','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49003','Alcañices','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49004','Alcubilla de Nogales','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49005','Alfaraz de Sayago','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49006','Algodre','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49007','Almaraz de Duero','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49008','Almeida de Sayago','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49009','Andavías','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49010','Arcenillas','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49011','Arcos de la Polvorosa','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49012','Argañín','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49013','Argujillo','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49014','Arquillinos','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49015','Arrabalde','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49016','Aspariegos','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49017','Asturianos','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49018','Ayoó de Vidriales','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49019','Barcial del Barco','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49020','Belver de los Montes','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49021','Benavente','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49022','Benegiles','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49023','Bermillo de Sayago','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49024','Bóveda de Toro (La)','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49025','Bretó','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49026','Bretocino','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49027','Brime de Sog','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49028','Brime de Urz','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49029','Burganes de Valverde','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49030','Bustillo del Oro','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49031','Cabañas de Sayago','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49032','Calzadilla de Tera','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49033','Camarzana de Tera','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49034','Cañizal','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49035','Cañizo','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49036','Carbajales de Alba','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49037','Carbellino','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49038','Casaseca de Campeán','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49039','Casaseca de las Chanas','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49040','Castrillo de la Guareña','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49041','Castrogonzalo','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49042','Castronuevo','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49043','Castroverde de Campos','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49044','Cazurra','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49046','Cerecinos de Campos','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49047','Cerecinos del Carrizal','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49048','Cernadilla','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49050','Cobreros','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49052','Coomonte','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49053','Coreses','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49054','Corrales del Vino','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49055','Cotanes del Monte','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49056','Cubillos','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49057','Cubo de Benavente','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49058','Cubo de Tierra del Vino (El)','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49059','Cuelgamures','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49061','Entrala','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49062','Espadañedo','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49063','Faramontanos de Tábara','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49064','Fariza','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49065','Fermoselle','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49066','Ferreras de Abajo','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49067','Ferreras de Arriba','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49068','Ferreruela','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49069','Figueruela de Arriba','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49071','Fonfría','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49075','Fresno de la Polvorosa','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49076','Fresno de la Ribera','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49077','Fresno de Sayago','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49078','Friera de Valverde','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49079','Fuente Encalada','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49080','Fuentelapeña','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49081','Fuentesaúco','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49082','Fuentes de Ropel','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49083','Fuentesecas','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49084','Fuentespreadas','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49085','Galende','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49086','Gallegos del Pan','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49087','Gallegos del Río','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49088','Gamones','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49090','Gema','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49091','Granja de Moreruela','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49092','Granucillo','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49093','Guarrate','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49094','Hermisende','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49095','Hiniesta (La)','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49096','Jambrina','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49097','Justel','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49098','Losacino','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49099','Losacio','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49100','Lubián','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49101','Luelmo','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49102','Maderal (El)','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49103','Madridanos','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49104','Mahide','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49105','Maire de Castroponce','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49107','Malva','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49108','Manganeses de la Lampreana','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49109','Manganeses de la Polvorosa','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49110','Manzanal de Arriba','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49111','Manzanal del Barco','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49112','Manzanal de los Infantes','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49113','Matilla de Arzón','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49114','Matilla la Seca','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49115','Mayalde','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49116','Melgar de Tera','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49117','Micereces de Tera','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49118','Milles de la Polvorosa','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49119','Molacillos','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49120','Molezuelas de la Carballeda','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49121','Mombuey','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49122','Monfarracinos','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49123','Montamarta','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49124','Moral de Sayago','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49125','Moraleja del Vino','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49126','Moraleja de Sayago','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49127','Morales del Vino','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49128','Morales de Rey','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49129','Morales de Toro','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49130','Morales de Valverde','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49131','Moralina','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49132','Moreruela de los Infanzones','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49133','Moreruela de Tábara','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49134','Muelas de los Caballeros','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49135','Muelas del Pan','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49136','Muga de Sayago','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49137','Navianos de Valverde','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49138','Olmillos de Castro','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49139','Otero de Bodas','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49141','Pajares de la Lampreana','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49142','Palacios del Pan','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49143','Palacios de Sanabria','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49145','Pedralba de la Pradería','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49146','Pego (El)','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49147','Peleagonzalo','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49148','Peleas de Abajo','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49149','Peñausende','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49150','Peque','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49151','Perdigón (El)','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49152','Pereruela','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49153','Perilla de Castro','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49154','Pías','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49155','Piedrahita de Castro','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49156','Pinilla de Toro','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49157','Pino del Oro','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49158','Piñero (El)','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49159','Pobladura del Valle','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49160','Pobladura de Valderaduey','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49162','Porto','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49163','Pozoantiguo','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49164','Pozuelo de Tábara','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49165','Prado','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49166','Puebla de Sanabria','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49167','Pueblica de Valverde','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49168','Quintanilla del Monte','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49169','Quintanilla del Olmo','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49170','Quintanilla de Urz','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49171','Quiruelas de Vidriales','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49172','Rabanales','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49173','Rábano de Aliste','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49174','Requejo','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49175','Revellinos','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49176','Riofrío de Aliste','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49177','Rionegro del Puente','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49178','Roales','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49179','Robleda-Cervantes','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49180','Roelos de Sayago','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49181','Rosinos de la Requejada','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49183','Salce','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49184','Samir de los Caños','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49185','San Agustín del Pozo','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49186','San Cebrián de Castro','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49187','San Cristóbal de Entreviñas','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49188','San Esteban del Molar','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49189','San Justo','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49190','San Martín de Valderaduey','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49191','San Miguel de la Ribera','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49192','San Miguel del Valle','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49193','San Pedro de Ceque','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49194','San Pedro de la Nave-Almendra','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49197','Santa Clara de Avedillo','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49199','Santa Colomba de las Monjas','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49200','Santa Cristina de la Polvorosa','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49201','Santa Croya de Tera','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49202','Santa Eufemia del Barco','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49203','Santa María de la Vega','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49204','Santa María de Valverde','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49205','Santibáñez de Tera','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49206','Santibáñez de Vidriales','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49207','Santovenia','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49208','San Vicente de la Cabeza','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49209','San Vitero','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49210','Sanzoles','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49214','Tábara','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49216','Tapioles','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49219','Toro','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49220','Torre del Valle (La)','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49221','Torregamones','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49222','Torres del Carrizal','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49223','Trabazos','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49224','Trefacio','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49225','Uña de Quintana','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49226','Vadillo de la Guareña','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49227','Valcabado','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49228','Valdefinjas','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49229','Valdescorriel','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49230','Vallesa de la Guareña','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49231','Vega de Tera','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49232','Vega de Villalobos','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49233','Vegalatrave','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49234','Venialbo','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49235','Vezdemarbán','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49236','Vidayanes','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49237','Videmala','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49238','Villabrázaro','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49239','Villabuena del Puente','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49240','Villadepera','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49241','Villaescusa','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49242','Villafáfila','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49243','Villaferrueña','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49244','Villageriz','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49245','Villalazán','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49246','Villalba de la Lampreana','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49247','Villalcampo','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49248','Villalobos','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49249','Villalonso','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49250','Villalpando','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49251','Villalube','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49252','Villamayor de Campos','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49255','Villamor de los Escuderos','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49256','Villanázar','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49257','Villanueva de Azoague','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49258','Villanueva de Campeán','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49259','Villanueva de las Peras','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49260','Villanueva del Campo','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49261','Villaralbo','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49262','Villardeciervos','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49263','Villar de Fallaves','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49264','Villar del Buey','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49265','Villardiegua de la Ribera','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49266','Villárdiga','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49267','Villardondiego','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49268','Villarrín de Campos','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49269','Villaseco del Pan','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49270','Villavendimio','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49271','Villaveza del Agua','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49272','Villaveza de Valverde','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49273','Viñas','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'49275','Zamora','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50001','Abanto','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50002','Acered','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50003','Agón','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50004','Aguarón','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50005','Aguilón','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50006','Ainzón','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50007','Aladrén','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50008','Alagón','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50009','Alarba','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50010','Alberite de San Juan','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50011','Albeta','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50012','Alborge','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50013','Alcalá de Ebro','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50014','Alcalá de Moncayo','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50015','Alconchel de Ariza','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50016','Aldehuela de Liestos','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50017','Alfajarín','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50018','Alfamén','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50019','Alforque','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50020','Alhama de Aragón','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50021','Almochuel','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50022','Almolda (La)','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50023','Almonacid de la Cuba','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50024','Almonacid de la Sierra','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50025','Almunia de Doña Godina (La)','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50026','Alpartir','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50027','Ambel','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50028','Anento','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50029','Aniñón','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50030','Añón de Moncayo','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50031','Aranda de Moncayo','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50032','Arándiga','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50033','Ardisa','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50034','Ariza','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50035','Artieda','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50036','Asín','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50037','Atea','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50038','Ateca','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50039','Azuara','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50040','Badules','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50041','Bagüés','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50042','Balconchán','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50043','Bárboles','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50044','Bardallur','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50045','Belchite','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50046','Belmonte de Gracián','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50047','Berdejo','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50048','Berrueco','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50050','Bijuesca','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50051','Biota','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50052','Bisimbre','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50053','Boquiñeni','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50054','Bordalba','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50055','Borja','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50056','Botorrita','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50057','Brea de Aragón','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50058','Bubierca','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50059','Bujaraloz','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50060','Bulbuente','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50061','Bureta','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50062','Burgo de Ebro (El)','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50063','Buste (El)','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50064','Cabañas de Ebro','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50065','Cabolafuente','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50066','Cadrete','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50067','Calatayud','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50068','Calatorao','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50069','Calcena','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50070','Calmarza','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50071','Campillo de Aragón','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50072','Carenas','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50073','Cariñena','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50074','Caspe','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50075','Castejón de Alarba','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50076','Castejón de las Armas','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50077','Castejón de Valdejasa','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50078','Castiliscar','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50079','Cervera de la Cañada','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50080','Cerveruela','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50081','Cetina','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50082','Cimballa','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50083','Cinco Olivas','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50084','Clarés de Ribota','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50085','Codo','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50086','Codos','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50087','Contamina','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50088','Cosuenda','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50089','Cuarte de Huerva','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50090','Cubel','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50091','Cuerlas (Las)','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50092','Chiprana','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50093','Chodes','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50094','Daroca','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50095','Ejea de los Caballeros','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50096','Embid de Ariza','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50098','Encinacorba','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50099','Épila','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50100','Erla','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50101','Escatrón','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50102','Fabara','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50104','Farlete','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50105','Fayón','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50106','Fayos (Los)','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50107','Figueruelas','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50108','Fombuena','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50109','Frago (El)','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50110','Frasno (El)','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50111','Fréscano','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50113','Fuendejalón','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50114','Fuendetodos','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50115','Fuentes de Ebro','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50116','Fuentes de Jiloca','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50117','Gallocanta','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50118','Gallur','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50119','Gelsa','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50120','Godojos','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50121','Gotor','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50122','Grisel','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50123','Grisén','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50124','Herrera de los Navarros','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50125','Ibdes','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50126','Illueca','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50128','Isuerre','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50129','Jaraba','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50130','Jarque de Moncayo','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50131','Jaulín','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50132','Joyosa (La)','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50133','Lagata','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50134','Langa del Castillo','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50135','Layana','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50136','Lécera','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50137','Leciñena','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50138','Lechón','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50139','Letux','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50140','Litago','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50141','Lituénigo','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50142','Lobera de Onsella','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50143','Longares','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50144','Longás','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50146','Lucena de Jalón','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50147','Luceni','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50148','Luesia','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50149','Luesma','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50150','Lumpiaque','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50151','Luna','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50152','Maella','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50153','Magallón','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50154','Mainar','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50155','Malanquilla','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50156','Maleján','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50157','Malón','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50159','Maluenda','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50160','Mallén','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50161','Manchones','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50162','Mara','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50163','María de Huerva','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50164','Mediana de Aragón','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50165','Mequinenza','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50166','Mesones de Isuela','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50167','Mezalocha','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50168','Mianos','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50169','Miedes de Aragón','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50170','Monegrillo','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50171','Moneva','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50172','Monreal de Ariza','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50173','Monterde','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50174','Montón','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50175','Morata de Jalón','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50176','Morata de Jiloca','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50177','Morés','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50178','Moros','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50179','Moyuela','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50180','Mozota','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50181','Muel','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50182','Muela (La)','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50183','Munébrega','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50184','Murero','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50185','Murillo de Gállego','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50186','Navardún','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50187','Nigüella','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50188','Nombrevilla','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50189','Nonaspe','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50190','Novallas','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50191','Novillas','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50192','Nuévalos','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50193','Nuez de Ebro','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50194','Olvés','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50195','Orcajo','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50196','Orera','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50197','Orés','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50198','Oseja','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50199','Osera de Ebro','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50200','Paniza','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50201','Paracuellos de Jiloca','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50202','Paracuellos de la Ribera','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50203','Pastriz','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50204','Pedrola','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50205','Pedrosas (Las)','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50206','Perdiguera','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50207','Piedratajada','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50208','Pina de Ebro','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50209','Pinseque','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50210','Pintanos (Los)','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50211','Plasencia de Jalón','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50212','Pleitas','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50213','Plenas','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50214','Pomer','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50215','Pozuel de Ariza','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50216','Pozuelo de Aragón','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50217','Pradilla de Ebro','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50218','Puebla de Albortón','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50219','Puebla de Alfindén (La)','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50220','Puendeluna','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50221','Purujosa','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50222','Quinto','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50223','Remolinos','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50224','Retascón','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50225','Ricla','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50227','Romanos','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50228','Rueda de Jalón','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50229','Ruesca','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50230','Sádaba','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50231','Salillas de Jalón','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50232','Salvatierra de Esca','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50233','Samper del Salz','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50234','San Martín de la Virgen de Moncayo','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50235','San Mateo de Gállego','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50236','Santa Cruz de Grío','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50237','Santa Cruz de Moncayo','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50238','Santa Eulalia de Gállego','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50239','Santed','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50240','Sástago','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50241','Sabiñán','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50242','Sediles','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50243','Sestrica','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50244','Sierra de Luna','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50245','Sigüés','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50246','Sisamón','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50247','Sobradiel','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50248','Sos del Rey Católico','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50249','Tabuenca','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50250','Talamantes','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50251','Tarazona','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50252','Tauste','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50253','Terrer','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50254','Tierga','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50255','Tobed','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50256','Torralba de los Frailes','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50257','Torralba de Ribota','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50258','Torralbilla','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50259','Torrehermosa','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50260','Torrelapaja','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50261','Torrellas','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50262','Torres de Berrellén','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50263','Torrijo de la Cañada','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50264','Tosos','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50265','Trasmoz','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50266','Trasobares','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50267','Uncastillo','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50268','Undués de Lerda','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50269','Urrea de Jalón','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50270','Urriés','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50271','Used','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50272','Utebo','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50273','Valdehorna','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50274','Val de San Martín','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50275','Valmadrid','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50276','Valpalmas','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50277','Valtorres','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50278','Velilla de Ebro','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50279','Velilla de Jiloca','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50280','Vera de Moncayo','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50281','Vierlas','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50282','Vilueña (La)','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50283','Villadoz','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50284','Villafeliche','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50285','Villafranca de Ebro','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50286','Villalba de Perejil','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50287','Villalengua','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50288','Villanueva de Gállego','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50289','Villanueva de Jiloca','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50290','Villanueva de Huerva','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50291','Villar de los Navarros','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50292','Villarreal de Huerva','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50293','Villarroya de la Sierra','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50294','Villarroya del Campo','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50295','Vistabella','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50296','Zaida (La)','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50297','Zaragoza','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50298','Zuera','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50901','Biel','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50902','Marracos','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (281,'50903','Villamayor de Gállego','Z',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CH-01','Cherkasy, Prydniprovs''kyi district','CH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CH-02','Cherkasy, Sosnivs''kyi distric','CH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CH-03','Vatutine','CH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CH-04','Horodyshche district','CH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CH-05','Drabiv district','CH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CH-06','Zhashkiv district','CH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CH-07','Zvenyhorodka district','CH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CH-08','Zolotonosha','CH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CH-09','Zolotonosha district','CH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CH-10','Kamianka district','CH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CH-11','Kaniv','CH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CH-12','Kaniv district','CH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CH-13','Katerynopil'' district','CH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CH-14','Korsun''-Shevchenkivs''kyi district','CH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CH-15','Lysianka district','CH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CH-16','Man''kivka district','CH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CH-17','Monastyryshche district','CH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CH-18','Smila','CH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CH-19','Smila district','CH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CH-20','Tal''ne district','CH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CH-21','Uman''','CH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CH-22','Uman'' district','CH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CH-23','Khrystynivka district','CH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CH-24','Cherkasy district','CH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CH-25','Chyhyryn district','CH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CH-26','Chornobai district','CH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CH-27','Shpola district','CH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CN-01','Chernivtsi, Shevchenkovs''kiy district','CN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CN-02','Chernivtsi, Pershotravnevyi distric','CN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CN-03','Chernivtsi, Sadhors''kyi distric','CN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CN-04','Vyzhnytsia district','CN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CN-05','Hertsa district','CN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CN-06','Hlyboka district','CN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CN-07','Zastavna district','CN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CN-08','Kel''mentsi district','CN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CN-09','Kitsman'' district','CN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CN-10','istrovs''k','CN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CN-11','Novoselytsia district','CN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CN-12','Pytyla district','CN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CN-13','Sokyriany district','CN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CN-14','Storozhynets'' district','CN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CN-15','Hotyn district','CN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CR-01','Chernihiv, Desnians''kyi district','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CR-02','Chernihiv, Novozavods''kyi distric','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CR-03','Bakhmach district','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CR-04','Bobrovyts''a district','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CR-05','Borzna district','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CR-06','Varva district','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CR-07','Horodnia district','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CR-08','Ichnia district','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CR-09','Kozelets'' district','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CR-10','Korop district','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CR-11','Koriukivka district','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CR-12','Kulykivka district','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CR-13','Mena district','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CR-14','Nizhyn','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CR-15','Nizhyn district','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CR-16','NovhorOD-Sivers''ky district','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CR-17','Nosivka district','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CR-18','Pryluky','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CR-19','Pryluky districkt','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CR-20','Ripky district','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CR-21','Semenivka district','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CR-22','Sosnytsia district','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CR-23','Sribne district','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CR-24','Talalaivka district','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CR-25','Chernihiv district','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CR-26','Shchors district','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'CR-27','Slavutych city','CR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-01','Dnipropetrovs''k, Amur-Nyzhn''odniprovs''kyi district','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-02','Dnipropetrovs''k, Babushkins''kyi distric','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-03','Dnipropetrovs''k, Zhovtnevyi distric','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-04','Dnipropetrovs''k, Idustrial''nyi distric','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-05','Dnipropetrovs''k, Kirovs''kyi distric','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-06','Dnipropetrovs''k, Chervonohvardijs''kyi distric','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-07','Dnipropetrovs''k, Lenins''kyi distric','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-08','Dnipropetrovs''k, Samars''kyi distric','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-09','Apostolove district','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-10','Vasyl''kivka district','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-11','Verkhn''odniprovs''k district','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-12','Vil''nohirs''k district','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-13','Dnipridzerzhyns''k , Bahlejs''kyi district','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-14','Dnipropetrovs''k, district','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-15','Zhovti Vody district','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-16','Kryvyi Rih, Dzerzhyns''kyi district','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-17','Kryvyi Rih district','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-18','Krynychky district','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-19','Mahdalynivka district','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-20','Marhanets''','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-21','Mezhova district','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-22','Nikopol''','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-23','Nikopol'' district','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-24','Novomoskovs''k','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-25','Novomoskovs''k district','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-26','Ordzhonikidze','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-27','Pavlohrad','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-28','Pavlohrad district','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-29','Pershotravens''k','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-30','Petrykivka district','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-31','Petropavlivka district','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-32','Pokrovs''ke district','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-33','Piatykhatky district','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-34','Synel''nykove','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-35','Synel''nykove district','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-36','Solone district','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-37','Sofiivka district','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-38','Ternivka','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-39','Ternivka district','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-40','Tomakivka district','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-41','Tsarychanka district','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-42','Shyroke district','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-43','Iurivka district','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-44','Dnipridzerzhyns''k, Zavodskyi district','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-45','Dnipridzerzhyns''k, Dniprovs''kyi district','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-46','Kryvyi Rih, Dolhintsevs''kyi district','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-47','Kryvyi Rih, Zhovtnevyi district','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-48','Kryvyi Rih, Inhulets''kyi district','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-49','Kryvyi Rih, Saksahans''kyi district','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-50','Kryvyi Rih, Ternivs''kyi district','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DN-51','Kryvyi Rih, Tsentral''no-mis''kyi district','DN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-01','Donets''k, Budjonivs''kyi district','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-02','Donets''k, Voroshylovs''kyi distric','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-03','Donets''k, Kalinins''kyi distric','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-04','Donets''k, Kyivs''kyi distric','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-05','Donets''k, Kirovs''kyi distric','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-06','Donets''k, Kuybyshevs''kyi distric','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-07','Donets''k, Lenins''kyi distric','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-08','Donets''k, Petrovs''kyi distric','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-09','Donets''k, Proletars''kyi distric','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-10','Avdiivka','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-11','Oleksandrivka district','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-12','Amvrosiivka district','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-13','Artemove','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-14','Artemivs''k district','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-15','Velyka Novosilka district','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-16','Volnovakha district','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-17','Volodars''k district','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-18','Horlivka, Tsentral''no-mis''kyi district','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-19','Debal''tseve','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-20','Dzerzhyns''k','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-21','Dymytrov','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-22','Dobropillia','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-23','Dobropillia district','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-24','Dokuchaievs''k','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-25','Druzhkivka','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-26','Ienakieve','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-27','Zhdanivk','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-28','Kirovs''ke','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-29','Kostiantynivka','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-30','Kostiantynivka district','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-31','Kramators''k','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-32','Krasny Lyman','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-33','Krasny Lyman district','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-34','Krasnoarmiis''k','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-35','Krasoarmiis''k district','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-36','Makiyivka, Tsentral''no-mis''kyi district','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-37','Manhush district','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-38','Maryinka district','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-39','Mariupol'', Zhovtnevyi district','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-40','Novoazovs''k district','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-41','Novohrodivka','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-42','Selydove','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-43','Slovians''k','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-44','Slovians''k district','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-45','Snizhne','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-46','Starobesheve district','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-47','Tel''manove district','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-48','Torez','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-49','Vuhledar district','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-50','Khartsyz''k','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-51','Shakhtars''k','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-52','Shakhtars''k district','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-53','Iasynuvata','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-54','Iasynuvata district','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-55','Horlivka , Kalinins''kyi district','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-56','Horlivka, Nikitovs''kyi district','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-57','Makiivka','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-58','Makiivka district','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-59','Mariupol'', Il''ichovskyi district','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-60','Mariupol'', Ordzhonikidzevs''kyi district','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-61','Mariupol'', Prymors''kyi district','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-62','Makiivka, Chervonogvardiis''kyi district','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'DO-63','akiivka, Kirovs''kyi district','DO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-01','Kharkiv, Dzerzhyns''kyi district','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-02','Kharkiv, Kyivs''kyi distric','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-03','Kharkiv, Kominternivs''kyi distric','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-04','Kharkiv, Lenins''kyi distric','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-05','Kharkiv, Moscows''kyi distric','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-06','Kharkiv, Zhovtnevyi distric','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-07','Kharkiv, Ordzhonikidzevs''kyi district','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-08','Kharkiv, Phrunzens''kyi distric','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-09','Kharkiv, Chervonozavods''kyi distric','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-10','Balakliia distric','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-11','Barvinkove district','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-12','Blyzniuky district','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-13','Bohodukhiv district','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-14','Borova district','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-15','Valky district','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-16','Velyky Burluk district','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-17','Vovchans''k district','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-18','Dvorichna district','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-19','Derhachi district','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-20','Zachepylivka district','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-21','Zmiyiv district','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-22','Zolochiv district','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-23','Izium','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-24','Izium district','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-25','Kehychivka district','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-26','Kolomak district','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-27','Krasnohrad district','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-28','Krasnokuts''k district','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-29','Kupians''k','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-30','Kupians''k district','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-31','Lozova','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-32','Lozova district','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-33','Liubotyn','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-34','Nova Vodolaha district','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-35','Pervomais''kyi','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-36','Pervomais''kyi district','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-37','Pechenihy district','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-38','Sakhnovshchyna district','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-39','Kharkiv district','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-40','Chuhuyiv','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-41','Chuhuyiv district','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HA-42','Shevchenkove district','HA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HE-01','Kherson, Dniprovs''kyi district','HE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HE-02','Kherson, Komsomol''s''kyi distric','HE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HE-03','Kherson, Suvorovs''kyi district','HE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HE-04','Beryslav district','HE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HE-05','Bilozerka district','HE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HE-06','Velyka Lepetykha district','HE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HE-07','Velyka Oleksandrivka district','HE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HE-08','Verkhniy Rohachyk district','HE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HE-09','Vysokopillia district','HE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HE-10','Heniches''k district','HE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HE-11','Hola Prystan'' district','HE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HE-12','Hornostayivka district','HE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HE-13','Ivanivka district','HE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HE-14','Kalanchak district','HE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HE-15','Kakhovka','HE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HE-16','Kakhovka district','HE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HE-17','Nyzhni Sirohozy district','HE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HE-18','Nova Kakhovka','HE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HE-19','Novovorontsovka district','HE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HE-20','Novotroyits''ke district','HE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HE-21','Skadovs''k district','HE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HE-22','Tsiurupyns''k district','HE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HE-23','Chaplynka district','HE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HM-01','Khmel''nyts''kyi','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HM-02','Bilohiria district','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HM-03','Vin''kivtsi district','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HM-04','Volochys''k district','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HM-05','Horodok district','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HM-06','Derazhnia district','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HM-07','Dunayivtsi district','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HM-08','Iziaslav district','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HM-09','Kamianets''-Podil''s''kyi','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HM-10','Kamianets''-Podil''s''kyi district','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HM-11','Krasyliv district','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HM-12','Letychiv district','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HM-13','Netishyn','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HM-14','Nova Ushytsia district','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HM-15','Polonne district','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HM-16','Slavuta','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HM-17','Slavuta district','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HM-18','Starokostiantyniv','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HM-19','Starokostiantyniv district','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HM-20','Stara Syniava district','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HM-21','Teofipol'' district','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HM-22','Khmel''nyts''kyi district','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HM-23','Chemerivtsi district','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HM-24','Shepetivka','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HM-25','Shepetivka district','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'HM-26','Iarmolyntsi district','HM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'IF-01','Ivano-Frankivs''k','IF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'IF-02','Bohorodchany district','IF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'IF-03','Bolekhi','IF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'IF-05','Verkhovyna district','IF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'IF-06','Halych district','IF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'IF-07','Horodenka district','IF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'IF-08','Dolyna district','IF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'IF-09','Kalush','IF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'IF-10','Lalysh district','IF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'IF-11','Kolomyia','IF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'IF-12','Kolomyia district','IF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'IF-13','Kosiv district','IF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'IF-14','Nadvirna district','IF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'IF-15','Rohatyn district','IF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'IF-16','Rozhniativ district','IF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'IF-17','Sniatyn district','IF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'IF-18','Tysmenytsia district','IF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'IF-19','Tlumach district','IF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'IF-20','Iaremcha','IF',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KI-01','Kirovohrad, Kirovs''kyi district','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KI-02','Kirovohrad, Lenins''kyi distric','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KI-03','Oleksandriia','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KI-04','Oleksandriia district','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KI-05','Oleksandrivka district','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KI-06','Bobrynets'' district','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KI-07','Haivoron district','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KI-08','Holovanivs''k district','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KI-09','Dobrovelychkivka district','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KI-10','Dolyns''ka district','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KI-11','Znamianka','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KI-12','Znamianka district','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KI-13','Kirovohrad district','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KI-14','Kompaniyivka district','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KI-15','Mala Vyska district','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KI-16','Novhorodka district','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KI-17','Novoarkhanhel''s''k district','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KI-18','Novomyrhorod district','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KI-19','Novoukrayinka district','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KI-20','Vil''shanka district','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KI-21','Onufriyivka district','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KI-22','Petrove district','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KI-23','Svitlovods''k','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KI-24','Svitlovods''k district','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KI-25','Ul''ianovka district','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KI-26','Ustyninka district','KI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KO-01','Bila Tserkva','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KO-02','Baryshivka district','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KO-03','Bila Tserkva district','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KO-04','Berzan'' district','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KO-05','Bohuslav district','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KO-06','Boryspil''','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KO-07','Boryspil'' district','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KO-08','Borodianka district','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KO-09','Brovary','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KO-10','Brovary district','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KO-11','Vasyl''kiv','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KO-12','Vasyl''kiv district','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KO-13','Vyshhorod district','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KO-14','Volodarka district','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KO-15','Zhurivka district','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KO-16','Ivankiv district','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KO-17','Irpin''','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KO-18','Kaharlyk district','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KO-19','KyiVO-Sviatoshyns''ky district','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KO-20','Makariv district','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KO-21','Myronivka district','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KO-22','Obukhiv district','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KO-23','Pereiaslav-Khmel''nyts''kyi','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KO-24','Pereiaslav-Khmel''nyts''kyi district','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KO-25','Polis''ke district','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KO-26','Chernobyls''kyi district (city Pripyiat'')','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KO-27','Rokytne district','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KO-28','Rzhyshchiv','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KO-29','Skvyra district','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KO-30','Stavyshche district','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KO-31','Tarashcha district','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KO-32','Tetiyiv district','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KO-33','Fastiv','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KO-34','Fastiv district','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KO-35','Iahotyn district','KO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KR-01','Simferopol'', Zaliznychnyi district','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KR-02','Simferopol'', Kyivs''kyi distric','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KR-03','Simferopol'', Tsentral''nyi distric','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KR-04','Alushta','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KR-05','Armians''k','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KR-06','Bakhchysarai district','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KR-07','Bilohirs''k district','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KR-08','Dzhankoi','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KR-09','Dzhankoi district','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KR-10','Ievpatoriia','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KR-11','Kerch','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KR-12','Kirovs''ke district','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KR-13','Krasnohvardiis''ke district','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KR-14','Krasnoperekops''k','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KR-15','Krasnoperekops''k district','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KR-16','Lenine district','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KR-17','Nyzhniohirs''kyi district','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KR-18','Pervomais''ke district','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KR-19','Rozdol''ne district','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KR-20','Saky','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KR-21','Saky district','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KR-22','Simferopol'' district','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KR-23','Soviets''kyi district','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KR-24','Sudak','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KR-25','Feodosiia','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KR-26','Chornomors''ke district','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KR-27','Yalta','KR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KV-01','Kyiv, Holosiivs''kyi distric','KV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KV-02','Kyiv, Darnyts''kyi distric','KV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KV-03','Kyiv. Desnians''kyi distric','KV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KV-04','Kyiv, Dniprovs''kyi distric','KV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KV-05','Kyiv, Obolons''kyi district','KV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KV-06','Kyiv, Pechers''kyi distric','KV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KV-07','Kyiv, Podols''kyi distric','KV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KV-08','Kyiv, Sviatoshyns''kyi distric','KV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KV-09','Kyiv, Solomens''kyi distric','KV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'KV-10','Kyiv, Shevchenkivs''kyi distric','KV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LU-01','Luhans''k, Artemivs''kyi district','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LU-02','Luhans''k, Zhovtnevyi distric','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LU-03','Luhans''k, Kamenobrods''kyi distric','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LU-04','Luhans''k, Lenins''kyi distric','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LU-05','Alchevs''k','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LU-06','Antratsyt','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LU-07','Antratsyt district','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LU-08','Bilovods''k district','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LU-09','Brianka','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LU-10','Bilikurakyne district','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LU-11','Kirovs''k','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LU-12','Krasnodon','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LU-13','Krasnodon district','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LU-14','Krasny Luch','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LU-15','Kreminna district','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LU-16','Lysychans''k','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LU-17','Lutuhyne district','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LU-18','Markivka district','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LU-19','Milove district','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LU-20','Novoajdar district','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LU-21','Novopskov district','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LU-22','Pervomais''k','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LU-23','Pereval''s''k district','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LU-24','Popasna district','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LU-25','Roven''ky','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LU-26','Rubizhne','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LU-27','Svatove district','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LU-28','Sverdlovs''k','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LU-29','Sverdlovs''k district','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LU-30','Sieverodonets''k','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LU-31','Slovianoserbs''k district','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LU-32','Stanychno-Luhans''ke district','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LU-33','Starobil''s''k district','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LU-34','Stakhanov','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LU-35','Troyits''ke district','LU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LV-01','L''viv, Halyts''kyi district','LV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LV-02','L''viv, Zaliznychnyi distric','LV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LV-03','L''viv, Lychakivs''kyi distric','LV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LV-04','L''viv, Sykhivs''kyi distric','LV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LV-05','L''viv, Frankivs''kyi distric','LV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LV-06','L''viv, Shevchenkivs''kyi distric','LV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LV-07','Boryslav','LV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LV-08','Brody district','LV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LV-09','Bus''k district','LV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LV-10','Horodok dostrict','LV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LV-11','Drohobych','LV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LV-12','Drohobych district','LV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LV-13','Zhydachiv district','LV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LV-14','Zhovkva district','LV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LV-15','Zolochiv district','LV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LV-16','Kamianka-Buz''ka district','LV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LV-17','Mykolayiv district','LV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LV-18','Mostys''ka district','LV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LV-19','Peremyshliany district','LV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LV-20','Pustomyty district','LV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LV-21','Radekhiv district','LV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LV-22','Sambir','LV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LV-23','Sambir district','LV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LV-24','Skole district','LV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LV-25','Sokal'' district','LV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LV-26','Staryi Sambir district','LV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LV-27','Stryi','LV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LV-28','Stryi district','LV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LV-29','Truskavets''','LV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LV-30','Turka district','LV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LV-31','Chervonohrad','LV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LV-32','Iavoriv district','LV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'LV-33','Morshyn','LV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'NI-01','Mykolayiv, Zavods''kyi district','NI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'NI-02','v, Korabel''nyi district','NI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'NI-03','v, Lenins''kyi district','NI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'NI-04','Mykolayiv, Tsentral''nyi distric','NI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'NI-05','Arbuzynka district','NI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'NI-06','Bashtanka district','NI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'NI-07','Berezanka district','NI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'NI-08','Bereznehuvate district','NI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'NI-09','Brats''ke district','NI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'NI-10','Veselynove district','NI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'NI-11','Voznesens''k','NI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'NI-12','Voznesens''k district','NI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'NI-13','Vradiyivka district','NI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'NI-14','Domanivka district','NI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'NI-15','Ielanets'' district','NI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'NI-16','Zhovtnevy district','NI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'NI-17','Kazanka district','NI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'NI-18','Kryve Ozero district','NI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'NI-19','Mykolayiv district','NI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'NI-20','Novyi Buh district','NI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'NI-21','Nova Odesa district','NI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'NI-22','Ochakiv','NI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'NI-23','Ochakiv district','NI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'NI-24','Pervomais''k','NI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'NI-25','Pervomais''k district','NI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'NI-26','Snihurivka district','NI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'NI-27','Iuzhnoukrayins''k','NI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'OD-01','Odesa','OD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'OD-02','Odesa, Illichivs''kyi distric','OD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'OD-03','Odesa, Kyivs''kyi distric','OD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'OD-04','Odesa, Lenins''kyi distric','OD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'OD-05','Odesa, Malinovs''kyi distric','OD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'OD-06','Odesa, Prymors''kyi distric','OD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'OD-07','Odesa, Suvorovs''kyi distric','OD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'OD-08','Odesa, Tsentral''nyi distric','OD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'OD-09','Anan''yiv district','OD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'OD-10','Artsyz district','OD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'OD-11','Balta district','OD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'OD-12','Berezivka district','OD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'OD-13','BilhorOD-Dnistrovs''kyi','OD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'OD-14','BilhorOD-Dnistrovs''kyi district','OD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'OD-15','Biliayivka district','OD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'OD-16','Bolhrad district','OD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'OD-17','Velyka Mykhailivka district','OD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'OD-18','Ivanivka district','OD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'OD-19','Izmayil','OD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'OD-20','Izmayil district','OD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'OD-21','Illichivs''k','OD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'OD-22','Kiliia district','OD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'OD-23','Kodyma district','OD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'OD-24','Kominternivs''ke district','OD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'OD-25','Kotovs''k','OD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'OD-26','Kotovs''k district','OD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'OD-27','Krasni Okny district','OD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'OD-28','Liubashivka district','OD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'OD-29','Mykolayivka district','OD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'OD-30','Ovidiopol'' district','OD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'OD-31','Reni district','OD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'OD-32','Rozdil''na district','OD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'OD-33','Savran'' district','OD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'OD-34','Sarata district','OD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'OD-35','Tarutyne district','OD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'OD-36','Tatarbunary district','OD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'OD-37','Teplodar','OD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'OD-38','Frunzivka district','OD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'OD-39','Shyriaieve district','OD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'OD-40','Iuzhne','OD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'PO-01','Poltava, Kyivs''kyi district','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'PO-02','Poltava, Lenins''kyi distric','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'PO-03','Poltava, Zhovtnevyi distric','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'PO-04','Velyka Bahachka district','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'PO-05','Hadiach district','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'PO-06','Hlobyne district','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'PO-07','Hrebinka district','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'PO-08','Dykan''ka district','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'PO-09','Zin''kiv district','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'PO-10','Karlivka district','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'PO-11','Kobeliaky district','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'PO-12','Kozel''shchyna district','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'PO-13','Komsomol''s''k','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'PO-14','Kotel''va district','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'PO-15','Kremenchuk, Avtozavods''ky distric','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'PO-16','Kremenchuk, Kryukovs''ky distric','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'PO-17','Kremenchuk district','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'PO-18','Lokhvytsia district','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'PO-19','Lubny','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'PO-20','Lubny district','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'PO-21','Mashivka district','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'PO-22','Myrhorod','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'PO-23','Myrhorod district','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'PO-24','Novi Sanzhary district','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'PO-25','Orzhytsia district','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'PO-26','Pyriatyn district','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'PO-27','Poltava district','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'PO-28','Reshetylivka district','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'PO-29','Semenivka district','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'PO-30','Khorol district','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'PO-31','Chornukhy district','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'PO-32','Chutove district','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'PO-33','Shyshaky district','PO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'RI-01','Rivne','RI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'RI-02','Berezne district','RI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'RI-03','Volodymyrets'' district','RI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'RI-04','Hoshcha district','RI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'RI-05','Demydivka district','RI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'RI-06','Dubno','RI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'RI-07','Dubno district','RI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'RI-08','Dubrovytsia district','RI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'RI-09','Zarichne district','RI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'RI-10','Zdolbuniv district','RI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'RI-11','Korets'' district','RI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'RI-12','Kostopil'' district','RI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'RI-13','Kuznetsovs''k','RI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'RI-14','Mlyniv district','RI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'RI-15','Ostroh','RI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'RI-16','Ostroh district','RI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'RI-17','Radyvyliv district','RI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'RI-18','Rivne district','RI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'RI-19','Rokytne district','RI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'RI-20','Sarny district','RI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'SL-01','Sevastopol'', Gagarins''kyi district','SL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'SL-02','Sevastopol'', Lenins''kyi distric','SL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'SL-03','Sevastopol'', Nakhimovs''kyi distric','SL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'SL-04','Sevastopol''','SL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'SU-01','Sumy, Zarichnyi district','SU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'SU-02','Sumy, Kovpakivs''kyi distric','SU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'SU-03','Okhtyrka','SU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'SU-04','Okhtyrka district','SU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'SU-05','Bilopillia district','SU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'SU-06','Buryn'' district','SU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'SU-07','Velyka Pysarivka district','SU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'SU-08','Hlukhiv','SU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'SU-09','Hlukhiv district','SU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'SU-10','Konotop','SU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'SU-11','Konotop district','SU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'SU-12','Krasnopillia district','SU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'SU-13','Krolevets'' district','SU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'SU-14','Lebedyn','SU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'SU-15','Lebedyn district','SU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'SU-16','Lypova Dolyna district','SU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'SU-17','Nedryhailiv district','SU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'SU-18','Putyvl'' district','SU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'SU-19','Romny','SU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'SU-20','Romny district','SU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'SU-21','Seredyna-Buda district','SU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'SU-22','Sumy district','SU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'SU-23','Trostianets'' district','SU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'SU-24','Shostka','SU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'SU-25','Shostka district','SU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'SU-26','Iampil'' district','SU',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'TE-01','Ternopil''','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'TE-02','Berezany district','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'TE-03','Borshchiv district','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'TE-04','Buchach district','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'TE-05','Husiatyn district','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'TE-06','Zalishchyky district','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'TE-07','Zbarazh district','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'TE-08','Zboriv district','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'TE-09','Kozova district','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'TE-10','Kremenets'' district','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'TE-11','Lanivtsi district','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'TE-12','Monastyrys''k district','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'TE-13','Pidvolochys''k district','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'TE-14','Pidhajtsi district','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'TE-15','Terebovlia district','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'TE-16','Ternopil'' district','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'TE-17','Chortkiv district','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'TE-18','Shums''k district','TE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VI-01','Vinnytsia, Zamostians''kyi district','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VI-02','Vinnytsia, Lenins''kyi distric','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VI-03','Vinnytsia, Starohorods''kyi distric','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VI-04','Bar district','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VI-05','Bershad'' district','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VI-06','Vinnytsia district','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VI-07','Haisyn district','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VI-08','Zhmerynka','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VI-09','Zhmerynka district','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VI-10','Illintsi district','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VI-11','Kalynivka district','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VI-12','Koziatyn','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VI-13','Koziatyn district','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VI-14','Kryzhopil'' district','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VI-15','Ladyzhyn district','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VI-16','Lypovets'' district','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VI-17','Lityn district','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VI-18','Mohyliv-Podil''s''kyi','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VI-19','Mohyliv-Podil''s''kyi district','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VI-20','Murovani Kurylivtsi district','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VI-21','Nemyriv district','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VI-22','Orativ district','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VI-23','Pishchanka district','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VI-24','Pohrebyshche district','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VI-25','Teplyk district','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VI-26','Tyvriv district','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VI-27','Tomashpil'' district','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VI-28','Trostianets'' district','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VI-29','Tul''chyn district','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VI-30','Khmil''nyk','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VI-31','Khmil''nyk district','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VI-32','Chernivtsi district','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VI-33','Chechel''nyk district','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VI-34','Sharhorod district','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VI-35','Iampil'' district','VI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VO-01','Luts''k','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VO-02','Volodymyr-Volyns''kyi','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VO-03','Volodymyr-Volyns''kyi district','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VO-04','Horokhiv district','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VO-05','Ivanychi district','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VO-06','Kamin''-Kashyrs''kyi district','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VO-07','Kivertsi district','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VO-08','Kovel''','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VO-09','Kovel'' district','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VO-10','Lokachi district','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VO-11','Luts''k district','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VO-12','Liubeshiv district','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VO-13','Liuboml'' district','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VO-14','Manevychi district','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VO-15','Novovolyns''k','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VO-16','Ratne district','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VO-17','Rozhyshche district','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VO-18','Stara Vyzhivka district','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VO-19','Turiis''k district','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'VO-20','Shats''k district','VO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZA-01','Uzhhorod','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZA-02','Berehove','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZA-03','Berehove district','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZA-04','Velykyi-Bereznyi district','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZA-05','Vynohradiv district','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZA-06','Volovets'' district','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZA-07','Irshava district','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZA-08','Mizhhiria district','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZA-09','Mukacheve','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZA-10','Mukacheve district','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZA-11','Perechyn district','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZA-12','Rakhiv district','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZA-13','Svaliava district','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZA-14','Tiachiv district','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZA-15','Uzhhorod','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZA-16','hust','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZA-17','Khust district','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZA-18','Chop','ZA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZH-01','Zhytomyr, Bohuns''kyi district','ZH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZH-02','Zhytomyr, Korolevs''kyi district','ZH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZH-03','Andrushivka district','ZH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZH-04','Baranivka district','ZH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZH-05','Berdychiv','ZH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZH-06','Berdychiv distict','ZH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZH-07','Brusyliv district','ZH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZH-08','Volodars''k-Volyns''kyi district','ZH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZH-09','Romanovs''ky district (Up 09.07.2003)','ZH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZH-10','Iemil''chyne district','ZH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZH-11','Zhytomyr district','ZH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZH-12','Korosten''','ZH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZH-13','Korosten'' district','ZH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZH-14','Korostyshiv district','ZH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZH-15','Luhyny district','ZH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZH-16','Liubar district','ZH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZH-17','Malyn district','ZH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZH-18','Narodychi district','ZH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZH-19','Novohrad-Volyns''kyi','ZH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZH-20','Novohrad-Volyns''kyi district','ZH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZH-21','Ovruch district','ZH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZH-22','Olevs''k district','ZH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZH-23','Popil''nia district','ZH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZH-24','Radomyshl'' district','ZH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZH-25','Ruzhyn district','ZH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZH-26','Chervonoarmiis''k district','ZH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZH-27','Cherniakhiv district','ZH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZH-28','Chudniv district','ZH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZP-01','Zaporizhzhia, Zhovtnevyi district','ZP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZP-02','Zaporizhzhia, Zavods''kyi distic','ZP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZP-03','Zaporizhzhia, Komunars''kyi distric','ZP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZP-04','Zaporizhzhia, Lenins''kyi distric','ZP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZP-05','Zaporizhzhia, Ordzhonikidzevs''kyi distric','ZP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZP-06','Zaporizhzhia, Khortyts''kyi distric','ZP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZP-07','Zaporizhzhia, Shevchenkivs''kyi distric','ZP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZP-08','Iakymivka district','ZP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZP-09','Berdians''k','ZP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZP-10','Berdians''k district','ZP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZP-11','Vasylivka district','ZP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZP-12','Velyka Bilozerka district','ZP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZP-13','Vesele district','ZP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZP-14','Vil''nians''k district','ZP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZP-15','Huliaipole district','ZP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZP-16','Zaporizhzhia district','ZP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZP-17','Kamianka-Dniprovs''ka district','ZP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZP-18','Kuibysheve district','ZP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZP-19','Melitopol''','ZP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZP-20','Melitopol'' district','ZP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZP-21','Mykhailivka district','ZP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZP-22','Novomykolayivka district','ZP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZP-23','Orikhiv district','ZP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZP-24','Polohy district','ZP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZP-25','Pryazovs''ke district','ZP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZP-26','Prymors''k district','ZP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZP-27','Rozivka district','ZP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZP-28','Tokmak','ZP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZP-29','Tokmak district','ZP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZP-30','Chernihivka district','ZP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (288,'ZP-31','Enerhodar','ZP',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Alameda','Alameda','CA','EB'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Contra Costa','Contra Costa','CA','EB'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Napa','Napa','CA','EB'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Solano','Solano','CA','EB'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MA,Barnstable','Barnstable','MA','EMA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MA,Bristol','Bristol','MA','EMA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MA,Dukes','Dukes','MA','EMA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MA,Essex','Essex','MA','EMA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MA,Middlesex','Middlesex','MA','EMA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MA,Nantucket','Nantucket','MA','EMA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MA,Norfolk','Norfolk','MA','EMA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MA,Plymouth','Plymouth','MA','EMA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MA,Suffolk','Suffolk','MA','EMA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Albany','Albany','NY','ENY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Columbia','Columbia','NY','ENY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Dutchess','Dutchess','NY','ENY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Greene','Greene','NY','ENY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Orange','Orange','NY','ENY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Putnam','Putnam','NY','ENY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Rensselaer','Rensselaer','NY','ENY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Rockland','Rockland','NY','ENY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Saratoga','Saratoga','NY','ENY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Schenectady','Schenectady','NY','ENY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Sullivan','Sullivan','NY','ENY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Ulster','Ulster','NY','ENY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Warren','Warren','NY','ENY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Washington','Washington','NY','ENY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Westchester','Westchester','NY','ENY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Adams','Adams','PA','EPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Berks','Berks','PA','EPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Bradford','Bradford','PA','EPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Bucks','Bucks','PA','EPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Carbon','Carbon','PA','EPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Chester','Chester','PA','EPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Columbia','Columbia','PA','EPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Cumberland','Cumberland','PA','EPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Dauphin','Dauphin','PA','EPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Delaware','Delaware','PA','EPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Juniata','Juniata','PA','EPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Lackawanna','Lackawanna','PA','EPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Lancaster','Lancaster','PA','EPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Lebanon','Lebanon','PA','EPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Lehigh','Lehigh','PA','EPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Luzerne','Luzerne','PA','EPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Lycoming','Lycoming','PA','EPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Monroe','Monroe','PA','EPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Montgomery','Montgomery','PA','EPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Montour','Montour','PA','EPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Northampton','Northampton','PA','EPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Northumberland','Northumberland','PA','EPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Perry','Perry','PA','EPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Philadelphia','Philadelphia','PA','EPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Pike','Pike','PA','EPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Schuylkill','Schuylkill','PA','EPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Snyder','Snyder','PA','EPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Sullivan','Sullivan','PA','EPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Susquehanna','Susquehanna','PA','EPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Tioga','Tioga','PA','EPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Union','Union','PA','EPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Wayne','Wayne','PA','EPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Wyoming','Wyoming','PA','EPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,York','York','PA','EPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WA,Adams','Adams','WA','EWA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WA,Asotin','Asotin','WA','EWA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WA,Benton','Benton','WA','EWA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WA,Chelan','Chelan','WA','EWA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WA,Columbia','Columbia','WA','EWA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WA,Douglas','Douglas','WA','EWA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WA,Ferry','Ferry','WA','EWA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WA,Franklin','Franklin','WA','EWA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WA,Garfield','Garfield','WA','EWA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WA,Grant','Grant','WA','EWA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WA,Kittitas','Kittitas','WA','EWA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WA,Klickitat','Klickitat','WA','EWA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WA,Lincoln','Lincoln','WA','EWA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WA,Okanogan','Okanogan','WA','EWA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WA,Pend Oreille','Pend Oreille','WA','EWA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WA,Spokane','Spokane','WA','EWA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WA,Stevens','Stevens','WA','EWA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WA,Walla Walla','Walla Walla','WA','EWA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WA,Whitman','Whitman','WA','EWA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WA,Yakima','Yakima','WA','EWA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Los Angeles','Los Angeles','CA','LAX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Alachua','Alachua','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Baker','Baker','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Bay','Bay','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Bradford','Bradford','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Calhoun','Calhoun','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Citrus','Citrus','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Clay','Clay','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Columbia','Columbia','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Dixie','Dixie','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Duval','Duval','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Escambia','Escambia','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Flagler','Flagler','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Franklin','Franklin','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Gadsden','Gadsden','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Gilchrist','Gilchrist','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Gulf','Gulf','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Hamilton','Hamilton','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Hernando','Hernando','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Holmes','Holmes','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Jackson','Jackson','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Jefferson','Jefferson','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Lafayette','Lafayette','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Lake','Lake','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Leon','Leon','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Levy','Levy','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Liberty','Liberty','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Madison','Madison','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Marion','Marion','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Nassau','Nassau','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Okaloosa','Okaloosa','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Orange','Orange','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Putnam','Putnam','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Santa Rosa','Santa Rosa','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Seminole','Seminole','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,St. Johns','St. Johns','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Sumter','Sumter','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Suwannee','Suwannee','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Taylor','Taylor','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Union','Union','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Volusia','Volusia','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Wakulla','Wakulla','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Walton','Walton','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Washington','Washington','FL','NFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Bronx','Bronx','NY','NLI'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Kings','Kings','NY','NLI'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Nassau','Nassau','NY','NLI'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,New York','New York','NY','NLI'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Queens','Queens','NY','NLI'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Richmond','Richmond','NY','NLI'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Suffolk','Suffolk','NY','NLI'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NJ,Bergen','Bergen','NJ','NNJ'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NJ,Essex','Essex','NJ','NNJ'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NJ,Hudson','Hudson','NJ','NNJ'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NJ,Hunterdon','Hunterdon','NJ','NNJ'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NJ,Middlesex','Middlesex','NJ','NNJ'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NJ,Monmouth','Monmouth','NJ','NNJ'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NJ,Morris','Morris','NJ','NNJ'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NJ,Passaic','Passaic','NJ','NNJ'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NJ,Somerset','Somerset','NJ','NNJ'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NJ,Sussex','Sussex','NJ','NNJ'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NJ,Union','Union','NJ','NNJ'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NJ,Warren','Warren','NJ','NNJ'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Clinton','Clinton','NY','NNY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Essex','Essex','NY','NNY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Franklin','Franklin','NY','NNY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Fulton','Fulton','NY','NNY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Hamilton','Hamilton','NY','NNY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Jefferson','Jefferson','NY','NNY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Lewis','Lewis','NY','NNY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Montgomery','Montgomery','NY','NNY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Schoharie','Schoharie','NY','NNY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,St. Lawrence','St. Lawrence','NY','NNY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Anderson','Anderson','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Archer','Archer','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Baylor','Baylor','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Bell','Bell','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Bosque','Bosque','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Bowie','Bowie','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Brown','Brown','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Camp','Camp','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Cass','Cass','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Cherokee','Cherokee','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Clay','Clay','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Collin','Collin','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Comanche','Comanche','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Cooke','Cooke','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Coryell','Coryell','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Dallas','Dallas','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Delta','Delta','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Denton','Denton','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Eastland','Eastland','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Ellis','Ellis','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Erath','Erath','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Falls','Falls','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Fannin','Fannin','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Franklin','Franklin','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Freestone','Freestone','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Grayson','Grayson','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Gregg','Gregg','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Hamilton','Hamilton','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Harrison','Harrison','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Henderson','Henderson','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Hill','Hill','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Hood','Hood','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Hopkins','Hopkins','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Hunt','Hunt','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Jack','Jack','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Johnson','Johnson','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Kaufman','Kaufman','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Lamar','Lamar','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Lampasas','Lampasas','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Limestone','Limestone','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Marion','Marion','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,McLennan','McLennan','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Mills','Mills','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Montague','Montague','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Morris','Morris','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Nacogdoches','Nacogdoches','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Navarro','Navarro','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Palo Pinto','Palo Pinto','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Panola','Panola','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Parker','Parker','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Rains','Rains','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Red River','Red River','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Rockwall','Rockwall','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Rusk','Rusk','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Shelby','Shelby','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Smith','Smith','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Somervell','Somervell','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Stephens','Stephens','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Tarrant','Tarrant','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Throckmorton','Throckmorton','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Titus','Titus','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Upshur','Upshur','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Van Zandt','Van Zandt','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Wichita','Wichita','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Wilbarger','Wilbarger','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Wise','Wise','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Wood','Wood','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Young','Young','TX','NTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Inyo','Inyo','CA','ORG'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Orange','Orange','CA','ORG'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Riverside','Riverside','CA','ORG'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,San Bernardino','San Bernardino','CA','ORG'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,San Luis Obispo','San Luis Obispo','CA','SB'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Santa Barbara','Santa Barbara','CA','SB'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Ventura','Ventura','CA','SB'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Monterey','Monterey','CA','SCV'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,San Benito','San Benito','CA','SCV'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,San Mateo','San Mateo','CA','SCV'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Santa Clara','Santa Clara','CA','SCV'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Santa Cruz','Santa Cruz','CA','SCV'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Imperial','Imperial','CA','SDG'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,San Diego','San Diego','CA','SDG'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Del Norte','Del Norte','CA','SF'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Humboldt','Humboldt','CA','SF'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Lake','Lake','CA','SF'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Marin','Marin','CA','SF'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Mendocino','Mendocino','CA','SF'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,San Francisco','San Francisco','CA','SF'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Sonoma','Sonoma','CA','SF'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Brevard','Brevard','FL','SFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Broward','Broward','FL','SFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Collier','Collier','FL','SFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Miami-Dade','Miami-Dade','FL','SFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Glades','Glades','FL','SFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Hendry','Hendry','FL','SFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Indian River','Indian River','FL','SFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Lee','Lee','FL','SFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Martin','Martin','FL','SFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Monroe','Monroe','FL','SFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Okeechobee','Okeechobee','FL','SFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Osceola','Osceola','FL','SFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Palm Beach','Palm Beach','FL','SFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,St. Lucie','St. Lucie','FL','SFL'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Calaveras','Calaveras','CA','SJV'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Fresno','Fresno','CA','SJV'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Kern','Kern','CA','SJV'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Kings','Kings','CA','SJV'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Madera','Madera','CA','SJV'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Mariposa','Mariposa','CA','SJV'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Merced','Merced','CA','SJV'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Mono','Mono','CA','SJV'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,San Joaquin','San Joaquin','CA','SJV'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Stanislaus','Stanislaus','CA','SJV'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Tulare','Tulare','CA','SJV'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Tuolumne','Tuolumne','CA','SJV'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NJ,Atlantic','Atlantic','NJ','SNJ'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NJ,Burlington','Burlington','NJ','SNJ'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NJ,Camden','Camden','NJ','SNJ'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NJ,Cape May','Cape May','NJ','SNJ'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NJ,Cumberland','Cumberland','NJ','SNJ'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NJ,Gloucester','Gloucester','NJ','SNJ'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NJ,Mercer','Mercer','NJ','SNJ'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NJ,Ocean','Ocean','NJ','SNJ'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NJ,Salem','Salem','NJ','SNJ'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Angelina','Angelina','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Aransas','Aransas','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Atascosa','Atascosa','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Austin','Austin','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Bandera','Bandera','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Bastrop','Bastrop','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Bee','Bee','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Bexar','Bexar','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Blanco','Blanco','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Brazoria','Brazoria','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Brazos','Brazos','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Brooks','Brooks','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Burleson','Burleson','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Burnet','Burnet','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Caldwell','Caldwell','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Calhoun','Calhoun','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Cameron','Cameron','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Chambers','Chambers','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Colorado','Colorado','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Comal','Comal','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Concho','Concho','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,DeWitt','DeWitt','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Dimmit','Dimmit','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Duval','Duval','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Edwards','Edwards','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Fayette','Fayette','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Fort Bend','Fort Bend','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Frio','Frio','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Galveston','Galveston','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Gillespie','Gillespie','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Goliad','Goliad','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Gonzales','Gonzales','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Grimes','Grimes','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Guadalupe','Guadalupe','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Hardin','Hardin','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Harris','Harris','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Hays','Hays','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Hidalgo','Hidalgo','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Houston','Houston','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Jackson','Jackson','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Jasper','Jasper','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Jefferson','Jefferson','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Jim Hogg','Jim Hogg','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Jim Wells','Jim Wells','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Karnes','Karnes','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Kendall','Kendall','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Kenedy','Kenedy','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Kerr','Kerr','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Kimble','Kimble','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Kinney','Kinney','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Kleberg','Kleberg','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,La Salle','La Salle','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Lavaca','Lavaca','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Lee','Lee','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Leon','Leon','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Liberty','Liberty','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Live Oak','Live Oak','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Llano','Llano','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Madison','Madison','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Mason','Mason','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Matagorda','Matagorda','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Maverick','Maverick','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,McCulloch','McCulloch','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,McMullen','McMullen','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Medina','Medina','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Menard','Menard','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Milam','Milam','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Montgomery','Montgomery','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Newton','Newton','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Nueces','Nueces','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Orange','Orange','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Polk','Polk','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Real','Real','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Refugio','Refugio','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Robertson','Robertson','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Sabine','Sabine','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,San Augustine','San Augustine','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,San Jacinto','San Jacinto','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,San Patricio','San Patricio','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,San Saba','San Saba','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Starr','Starr','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Travis','Travis','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Trinity','Trinity','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Tyler','Tyler','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Uvalde','Uvalde','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Val Verde','Val Verde','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Victoria','Victoria','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Walker','Walker','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Waller','Waller','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Washington','Washington','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Webb','Webb','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Wharton','Wharton','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Willacy','Willacy','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Williamson','Williamson','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Wilson','Wilson','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Zapata','Zapata','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Zavala','Zavala','TX','STX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Alpine','Alpine','CA','SV'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Amador','Amador','CA','SV'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Butte','Butte','CA','SV'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Colusa','Colusa','CA','SV'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,El Dorado','El Dorado','CA','SV'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Glenn','Glenn','CA','SV'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Lassen','Lassen','CA','SV'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Modoc','Modoc','CA','SV'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Nevada','Nevada','CA','SV'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Placer','Placer','CA','SV'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Plumas','Plumas','CA','SV'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Sacramento','Sacramento','CA','SV'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Shasta','Shasta','CA','SV'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Sierra','Sierra','CA','SV'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Siskiyou','Siskiyou','CA','SV'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Sutter','Sutter','CA','SV'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Tehama','Tehama','CA','SV'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Trinity','Trinity','CA','SV'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Yolo','Yolo','CA','SV'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CA,Yuba','Yuba','CA','SV'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Charlotte','Charlotte','FL','WCF'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,DeSoto','DeSoto','FL','WCF'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Hardee','Hardee','FL','WCF'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Highlands','Highlands','FL','WCF'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Hillsborough','Hillsborough','FL','WCF'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Manatee','Manatee','FL','WCF'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Pasco','Pasco','FL','WCF'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Pinellas','Pinellas','FL','WCF'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Polk','Polk','FL','WCF'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'FL,Sarasota','Sarasota','FL','WCF'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MA,Berkshire','Berkshire','MA','WMA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MA,Franklin','Franklin','MA','WMA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MA,Hampden','Hampden','MA','WMA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MA,Hampshire','Hampshire','MA','WMA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MA,Worcester','Worcester','MA','WMA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Allegany','Allegany','NY','WNY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Broome','Broome','NY','WNY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Cattaraugus','Cattaraugus','NY','WNY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Cayuga','Cayuga','NY','WNY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Chautauqua','Chautauqua','NY','WNY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Chemung','Chemung','NY','WNY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Chenango','Chenango','NY','WNY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Cortland','Cortland','NY','WNY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Delaware','Delaware','NY','WNY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Erie','Erie','NY','WNY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Genesee','Genesee','NY','WNY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Herkimer','Herkimer','NY','WNY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Livingston','Livingston','NY','WNY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Madison','Madison','NY','WNY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Monroe','Monroe','NY','WNY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Niagara','Niagara','NY','WNY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Oneida','Oneida','NY','WNY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Onondaga','Onondaga','NY','WNY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Ontario','Ontario','NY','WNY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Orleans','Orleans','NY','WNY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Oswego','Oswego','NY','WNY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Otsego','Otsego','NY','WNY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Schuyler','Schuyler','NY','WNY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Seneca','Seneca','NY','WNY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Steuben','Steuben','NY','WNY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Tioga','Tioga','NY','WNY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Tompkins','Tompkins','NY','WNY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Wayne','Wayne','NY','WNY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Wyoming','Wyoming','NY','WNY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NY,Yates','Yates','NY','WNY'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Allegheny','Allegheny','PA','WPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Armstrong','Armstrong','PA','WPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Beaver','Beaver','PA','WPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Bedford','Bedford','PA','WPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Blair','Blair','PA','WPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Butler','Butler','PA','WPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Cambria','Cambria','PA','WPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Cameron','Cameron','PA','WPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Centre','Centre','PA','WPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Clarion','Clarion','PA','WPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Clearfield','Clearfield','PA','WPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Clinton','Clinton','PA','WPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Crawford','Crawford','PA','WPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Elk','Elk','PA','WPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Erie','Erie','PA','WPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Fayette','Fayette','PA','WPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Forest','Forest','PA','WPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Franklin','Franklin','PA','WPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Fulton','Fulton','PA','WPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Greene','Greene','PA','WPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Huntingdon','Huntingdon','PA','WPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Indiana','Indiana','PA','WPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Jefferson','Jefferson','PA','WPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Lawrence','Lawrence','PA','WPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,McKean','McKean','PA','WPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Mercer','Mercer','PA','WPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Mifflin','Mifflin','PA','WPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Potter','Potter','PA','WPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Somerset','Somerset','PA','WPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Venango','Venango','PA','WPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Warren','Warren','PA','WPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Washington','Washington','PA','WPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'PA,Westmoreland','Westmoreland','PA','WPA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Andrews','Andrews','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Armstrong','Armstrong','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Bailey','Bailey','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Borden','Borden','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Brewster','Brewster','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Briscoe','Briscoe','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Callahan','Callahan','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Carson','Carson','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Castro','Castro','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Childress','Childress','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Cochran','Cochran','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Coke','Coke','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Coleman','Coleman','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Collingsworth','Collingsworth','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Cottle','Cottle','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Crane','Crane','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Crockett','Crockett','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Crosby','Crosby','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Culberson','Culberson','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Dallam','Dallam','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Dawson','Dawson','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Deaf Smith','Deaf Smith','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Dickens','Dickens','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Donley','Donley','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Ector','Ector','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,El Paso','El Paso','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Fisher','Fisher','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Floyd','Floyd','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Foard','Foard','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Gaines','Gaines','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Garza','Garza','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Glasscock','Glasscock','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Gray','Gray','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Hale','Hale','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Hall','Hall','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Hansford','Hansford','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Hardeman','Hardeman','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Hartley','Hartley','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Haskell','Haskell','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Hemphill','Hemphill','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Hockley','Hockley','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Howard','Howard','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Hudspeth','Hudspeth','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Hutchinson','Hutchinson','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Irion','Irion','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Jeff Davis','Jeff Davis','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Jones','Jones','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Kent','Kent','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,King','King','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Knox','Knox','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Lamb','Lamb','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Lipscomb','Lipscomb','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Loving','Loving','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Lubbock','Lubbock','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Lynn','Lynn','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Martin','Martin','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Midland','Midland','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Mitchell','Mitchell','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Moore','Moore','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Motley','Motley','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Nolan','Nolan','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Ochiltree','Ochiltree','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Oldham','Oldham','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Parmer','Parmer','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Pecos','Pecos','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Potter','Potter','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Presidio','Presidio','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Randall','Randall','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Reagan','Reagan','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Reeves','Reeves','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Roberts','Roberts','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Runnels','Runnels','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Schleicher','Schleicher','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Scurry','Scurry','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Shackelford','Shackelford','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Sherman','Sherman','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Sterling','Sterling','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Stonewall','Stonewall','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Sutton','Sutton','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Swisher','Swisher','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Taylor','Taylor','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Terrell','Terrell','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Terry','Terry','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Tom Green','Tom Green','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Upton','Upton','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Ward','Ward','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Wheeler','Wheeler','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Winkler','Winkler','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TX,Yoakum','Yoakum','TX','WTX'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WA,Clallam','Clallam','WA','WWA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WA,Clark','Clark','WA','WWA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WA,Cowlitz','Cowlitz','WA','WWA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WA,Grays Harbor','Grays Harbor','WA','WWA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WA,Island','Island','WA','WWA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WA,Jefferson','Jefferson','WA','WWA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WA,King','King','WA','WWA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WA,Kitsap','Kitsap','WA','WWA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WA,Lewis','Lewis','WA','WWA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WA,Mason','Mason','WA','WWA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WA,Pacific','Pacific','WA','WWA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WA,Pierce','Pierce','WA','WWA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WA,San Juan','San Juan','WA','WWA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WA,Skagit','Skagit','WA','WWA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WA,Skamania','Skamania','WA','WWA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WA,Snohomish','Snohomish','WA','WWA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WA,Thurston','Thurston','WA','WWA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WA,Wahkiakum','Wahkiakum','WA','WWA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WA,Whatcom','Whatcom','WA','WWA'); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Autauga','Autauga','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Baldwin','Baldwin','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Barbour','Barbour','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Bibb','Bibb','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Blount','Blount','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Bullock','Bullock','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Butler','Butler','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Calhoun','Calhoun','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Chambers','Chambers','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Cherokee','Cherokee','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Chilton','Chilton','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Choctaw','Choctaw','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Clarke','Clarke','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Clay','Clay','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Cleburne','Cleburne','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Coffee','Coffee','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Colbert','Colbert','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Conecuh','Conecuh','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Coosa','Coosa','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Covington','Covington','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Crenshaw','Crenshaw','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Cullman','Cullman','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Dale','Dale','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Dallas','Dallas','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,DeKalb','DeKalb','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Elmore','Elmore','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Escambia','Escambia','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Etowah','Etowah','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Fayette','Fayette','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Franklin','Franklin','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Geneva','Geneva','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Greene','Greene','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Hale','Hale','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Henry','Henry','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Houston','Houston','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Jackson','Jackson','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Jefferson','Jefferson','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Lamar','Lamar','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Lauderdale','Lauderdale','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Lawrence','Lawrence','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Lee','Lee','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Limestone','Limestone','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Lowndes','Lowndes','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Macon','Macon','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Madison','Madison','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Marengo','Marengo','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Marion','Marion','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Marshall','Marshall','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Mobile','Mobile','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Monroe','Monroe','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Montgomery','Montgomery','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Morgan','Morgan','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Perry','Perry','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Pickens','Pickens','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Pike','Pike','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Randolph','Randolph','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Russell','Russell','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Shelby','Shelby','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,St. Clair','St. Clair','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Sumter','Sumter','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Talladega','Talladega','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Tallapoosa','Tallapoosa','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Tuscaloosa','Tuscaloosa','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Walker','Walker','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Washington','Washington','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Wilcox','Wilcox','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AL,Winston','Winston','AL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Arkansas','Arkansas','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Ashley','Ashley','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Baxter','Baxter','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Benton','Benton','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Boone','Boone','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Bradley','Bradley','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Calhoun','Calhoun','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Carroll','Carroll','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Chicot','Chicot','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Clark','Clark','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Clay','Clay','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Cleburne','Cleburne','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Cleveland','Cleveland','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Columbia','Columbia','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Conway','Conway','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Craighead','Craighead','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Crawford','Crawford','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Crittenden','Crittenden','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Cross','Cross','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Dallas','Dallas','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Desha','Desha','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Drew','Drew','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Faulkner','Faulkner','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Franklin','Franklin','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Fulton','Fulton','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Garland','Garland','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Grant','Grant','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Greene','Greene','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Hempstead','Hempstead','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Hot Spring','Hot Spring','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Howard','Howard','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Independence','Independence','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Izard','Izard','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Jackson','Jackson','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Jefferson','Jefferson','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Johnson','Johnson','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Lafayette','Lafayette','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Lawrence','Lawrence','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Lee','Lee','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Lincoln','Lincoln','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Little River','Little River','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Logan','Logan','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Lonoke','Lonoke','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Madison','Madison','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Marion','Marion','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Miller','Miller','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Mississippi','Mississippi','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Monroe','Monroe','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Montgomery','Montgomery','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Nevada','Nevada','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Newton','Newton','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Ouachita','Ouachita','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Perry','Perry','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Phillips','Phillips','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Pike','Pike','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Poinsett','Poinsett','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Polk','Polk','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Pope','Pope','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Prairie','Prairie','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Pulaski','Pulaski','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Randolph','Randolph','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Saline','Saline','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Scott','Scott','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Searcy','Searcy','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Sebastian','Sebastian','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Sevier','Sevier','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Sharp','Sharp','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,St. Francis','St. Francis','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Stone','Stone','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Union','Union','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Van Buren','Van Buren','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Washington','Washington','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,White','White','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Woodruff','Woodruff','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AR,Yell','Yell','AR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AZ,La Paz','La Paz','AZ',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AZ,Apache','Apache','AZ',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AZ,Cochise','Cochise','AZ',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AZ,Coconino','Coconino','AZ',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AZ,Gila','Gila','AZ',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AZ,Graham','Graham','AZ',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AZ,Greenlee','Greenlee','AZ',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AZ,Maricopa','Maricopa','AZ',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AZ,Mohave','Mohave','AZ',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AZ,Navajo','Navajo','AZ',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AZ,Pima','Pima','AZ',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AZ,Pinal','Pinal','AZ',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AZ,Santa Cruz','Santa Cruz','AZ',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AZ,Yavapai','Yavapai','AZ',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'AZ,Yuma','Yuma','AZ',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Adams','Adams','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Alamosa','Alamosa','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Arapahoe','Arapahoe','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Archuleta','Archuleta','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Baca','Baca','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Bent','Bent','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Boulder','Boulder','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Broomfield','Broomfield','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Chaffee','Chaffee','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Cheyenne','Cheyenne','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Clear Creek','Clear Creek','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Conejos','Conejos','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Costilla','Costilla','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Crowley','Crowley','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Custer','Custer','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Delta','Delta','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Denver','Denver','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Dolores','Dolores','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Douglas','Douglas','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Eagle','Eagle','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,El Paso','El Paso','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Elbert','Elbert','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Fremont','Fremont','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Garfield','Garfield','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Gilpin','Gilpin','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Grand','Grand','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Gunnison','Gunnison','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Hinsdale','Hinsdale','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Huerfano','Huerfano','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Jackson','Jackson','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Jefferson','Jefferson','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Kiowa','Kiowa','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Kit Carson','Kit Carson','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,La Plata','La Plata','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Lake','Lake','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Larimer','Larimer','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Las Animas','Las Animas','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Lincoln','Lincoln','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Logan','Logan','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Mesa','Mesa','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Mineral','Mineral','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Moffat','Moffat','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Montezuma','Montezuma','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Montrose','Montrose','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Morgan','Morgan','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Otero','Otero','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Ouray','Ouray','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Park','Park','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Phillips','Phillips','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Pitkin','Pitkin','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Prowers','Prowers','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Pueblo','Pueblo','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Rio Blanco','Rio Blanco','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Rio Grande','Rio Grande','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Routt','Routt','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Saguache','Saguache','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,San Juan','San Juan','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,San Miguel','San Miguel','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Sedgwick','Sedgwick','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Summit','Summit','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Teller','Teller','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Washington','Washington','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Weld','Weld','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CO,Yuma','Yuma','CO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CT,Fairfield','Fairfield','CT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CT,Hartford','Hartford','CT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CT,Litchfield','Litchfield','CT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CT,Middlesex','Middlesex','CT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CT,New Haven','New Haven','CT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CT,New London','New London','CT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CT,Tolland','Tolland','CT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'CT,Windham','Windham','CT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'DC,Montgomery','Montgomery','DC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'DC,Prince George''s','Prince George''s','DC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'DE,Kent','Kent','DE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'DE,New Castle','New Castle','DE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'DE,Sussex','Sussex','DE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Appling','Appling','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Atkinson','Atkinson','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Bacon','Bacon','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Baker','Baker','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Baldwin','Baldwin','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Banks','Banks','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Barrow','Barrow','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Bartow','Bartow','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Ben Hill','Ben Hill','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Berrien','Berrien','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Bibb','Bibb','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Bleckley','Bleckley','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Brantley','Brantley','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Brooks','Brooks','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Bryan','Bryan','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Bulloch','Bulloch','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Burke','Burke','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Butts','Butts','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Calhoun','Calhoun','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Camden','Camden','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Candler','Candler','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Carroll','Carroll','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Catoosa','Catoosa','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Charlton','Charlton','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Chatham','Chatham','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Chattahoochee','Chattahoochee','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Chattooga','Chattooga','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Cherokee','Cherokee','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Clarke','Clarke','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Clay','Clay','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Clayton','Clayton','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Clinch','Clinch','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Cobb','Cobb','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Coffee','Coffee','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Colquitt','Colquitt','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Columbia','Columbia','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Cook','Cook','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Coweta','Coweta','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Crawford','Crawford','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Crisp','Crisp','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Dade','Dade','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Dawson','Dawson','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Decatur','Decatur','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,DeKalb','DeKalb','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Dodge','Dodge','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Dooly','Dooly','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Dougherty','Dougherty','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Douglas','Douglas','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Early','Early','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Echols','Echols','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Effingham','Effingham','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Elbert','Elbert','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Emanuel','Emanuel','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Evans','Evans','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Fannin','Fannin','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Fayette','Fayette','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Floyd','Floyd','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Forsyth','Forsyth','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Franklin','Franklin','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Fulton','Fulton','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Gilmer','Gilmer','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Glascock','Glascock','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Glynn','Glynn','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Gordon','Gordon','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Grady','Grady','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Greene','Greene','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Gwinnett','Gwinnett','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Habersham','Habersham','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Hall','Hall','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Hancock','Hancock','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Haralson','Haralson','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Harris','Harris','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Hart','Hart','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Heard','Heard','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Henry','Henry','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Houston','Houston','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Irwin','Irwin','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Jackson','Jackson','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Jasper','Jasper','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Jeff Davis','Jeff Davis','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Jefferson','Jefferson','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Jenkins','Jenkins','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Johnson','Johnson','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Jones','Jones','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Lamar','Lamar','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Lanier','Lanier','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Laurens','Laurens','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Lee','Lee','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Liberty','Liberty','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Lincoln','Lincoln','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Long','Long','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Lowndes','Lowndes','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Lumpkin','Lumpkin','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Macon','Macon','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Madison','Madison','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Marion','Marion','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,McDuffie','McDuffie','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,McIntosh','McIntosh','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Meriwether','Meriwether','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Miller','Miller','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Mitchell','Mitchell','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Monroe','Monroe','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Montgomery','Montgomery','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Morgan','Morgan','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Murray','Murray','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Muscogee','Muscogee','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Newton','Newton','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Oconee','Oconee','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Oglethorpe','Oglethorpe','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Paulding','Paulding','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Peach','Peach','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Pickens','Pickens','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Pierce','Pierce','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Pike','Pike','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Polk','Polk','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Pulaski','Pulaski','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Putnam','Putnam','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Quitman','Quitman','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Rabun','Rabun','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Randolph','Randolph','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Richmond','Richmond','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Rockdale','Rockdale','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Schley','Schley','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Screven','Screven','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Seminole','Seminole','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Spalding','Spalding','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Stephens','Stephens','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Stewart','Stewart','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Sumter','Sumter','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Talbot','Talbot','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Taliaferro','Taliaferro','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Tattnall','Tattnall','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Taylor','Taylor','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Telfair','Telfair','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Terrell','Terrell','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Thomas','Thomas','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Tift','Tift','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Toombs','Toombs','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Towns','Towns','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Treutlen','Treutlen','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Troup','Troup','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Turner','Turner','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Twiggs','Twiggs','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Union','Union','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Upson','Upson','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Walker','Walker','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Walton','Walton','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Ware','Ware','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Warren','Warren','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Washington','Washington','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Wayne','Wayne','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Webster','Webster','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Wheeler','Wheeler','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,White','White','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Whitfield','Whitfield','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Wilcox','Wilcox','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Wilkes','Wilkes','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Wilkinson','Wilkinson','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'GA,Worth','Worth','GA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Adair','Adair','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Adams','Adams','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Allamakee','Allamakee','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Appanoose','Appanoose','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Audubon','Audubon','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Benton','Benton','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Black Hawk','Black Hawk','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Boone','Boone','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Bremer','Bremer','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Buchanan','Buchanan','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Buena Vista','Buena Vista','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Butler','Butler','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Calhoun','Calhoun','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Carroll','Carroll','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Cass','Cass','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Cedar','Cedar','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Cerro Gordo','Cerro Gordo','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Cherokee','Cherokee','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Chickasaw','Chickasaw','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Clarke','Clarke','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Clay','Clay','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Clayton','Clayton','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Clinton','Clinton','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Crawford','Crawford','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Dallas','Dallas','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Davis','Davis','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Decatur','Decatur','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Delaware','Delaware','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Des Moines','Des Moines','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Dickinson','Dickinson','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Dubuque','Dubuque','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Emmet','Emmet','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Fayette','Fayette','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Floyd','Floyd','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Franklin','Franklin','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Fremont','Fremont','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Greene','Greene','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Grundy','Grundy','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Guthrie','Guthrie','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Hamilton','Hamilton','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Hancock','Hancock','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Hardin','Hardin','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Harrison','Harrison','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Henry','Henry','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Howard','Howard','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Humboldt','Humboldt','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Ida','Ida','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Iowa','Iowa','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Jackson','Jackson','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Jasper','Jasper','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Jefferson','Jefferson','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Johnson','Johnson','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Jones','Jones','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Keokuk','Keokuk','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Kossuth','Kossuth','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Lee','Lee','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Linn','Linn','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Louisa','Louisa','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Lucas','Lucas','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Lyon','Lyon','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Madison','Madison','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Mahaska','Mahaska','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Marion','Marion','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Marshall','Marshall','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Mills','Mills','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Mitchell','Mitchell','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Monona','Monona','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Monroe','Monroe','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Montgomery','Montgomery','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Muscatine','Muscatine','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,O''Brien','O''Brien','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Osceola','Osceola','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Page','Page','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Palo Alto','Palo Alto','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Plymouth','Plymouth','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Pocahontas','Pocahontas','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Polk','Polk','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Pottawattamie','Pottawattamie','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Poweshiek','Poweshiek','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Ringgold','Ringgold','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Sac','Sac','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Scott','Scott','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Shelby','Shelby','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Sioux','Sioux','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Story','Story','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Tama','Tama','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Taylor','Taylor','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Union','Union','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Van Buren','Van Buren','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Wapello','Wapello','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Warren','Warren','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Washington','Washington','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Wayne','Wayne','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Webster','Webster','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Winnebago','Winnebago','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Winneshiek','Winneshiek','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Woodbury','Woodbury','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Worth','Worth','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IA,Wright','Wright','IA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Ada','Ada','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Adams','Adams','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Bannock','Bannock','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Bear Lake','Bear Lake','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Benewah','Benewah','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Bingham','Bingham','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Blaine','Blaine','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Boise','Boise','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Bonner','Bonner','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Bonneville','Bonneville','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Boundary','Boundary','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Butte','Butte','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Camas','Camas','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Canyon','Canyon','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Caribou','Caribou','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Cassia','Cassia','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Clark','Clark','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Clearwater','Clearwater','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Custer','Custer','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Elmore','Elmore','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Franklin','Franklin','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Fremont','Fremont','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Gem','Gem','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Gooding','Gooding','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Idaho','Idaho','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Jefferson','Jefferson','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Jerome','Jerome','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Kootenai','Kootenai','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Latah','Latah','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Lemhi','Lemhi','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Lewis','Lewis','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Lincoln','Lincoln','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Madison','Madison','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Minidoka','Minidoka','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Nez Perce','Nez Perce','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Oneida','Oneida','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Owyhee','Owyhee','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Payette','Payette','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Power','Power','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Shoshone','Shoshone','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Teton','Teton','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Twin Falls','Twin Falls','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Valley','Valley','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ID,Washington','Washington','ID',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Adams','Adams','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Alexander','Alexander','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Bond','Bond','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Boone','Boone','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Brown','Brown','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Bureau','Bureau','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Calhoun','Calhoun','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Carroll','Carroll','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Cass','Cass','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Champaign','Champaign','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Christian','Christian','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Clark','Clark','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Clay','Clay','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Clinton','Clinton','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Coles','Coles','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Cook','Cook','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Crawford','Crawford','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Cumberland','Cumberland','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,DeWitt','DeWitt','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,DeKalb','DeKalb','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Douglas','Douglas','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,DuPage','DuPage','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Edgar','Edgar','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Edwards','Edwards','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Effingham','Effingham','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Fayette','Fayette','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Ford','Ford','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Franklin','Franklin','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Fulton','Fulton','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Gallatin','Gallatin','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Greene','Greene','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Grundy','Grundy','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Hamilton','Hamilton','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Hancock','Hancock','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Hardin','Hardin','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Henderson','Henderson','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Henry','Henry','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Iroquois','Iroquois','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Jackson','Jackson','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Jasper','Jasper','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Jefferson','Jefferson','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Jersey','Jersey','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Jo Daviess','Jo Daviess','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Johnson','Johnson','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Kane','Kane','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Kankakee','Kankakee','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Kendall','Kendall','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Knox','Knox','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,La Salle','La Salle','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Lake','Lake','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Lawrence','Lawrence','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Lee','Lee','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Livingston','Livingston','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Logan','Logan','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Macon','Macon','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Macoupin','Macoupin','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Madison','Madison','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Marion','Marion','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Marshall','Marshall','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Mason','Mason','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Massac','Massac','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,McDonough','McDonough','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,McHenry','McHenry','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,McLean','McLean','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Menard','Menard','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Mercer','Mercer','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Monroe','Monroe','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Montgomery','Montgomery','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Morgan','Morgan','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Moultrie','Moultrie','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Ogle','Ogle','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Peoria','Peoria','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Perry','Perry','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Piatt','Piatt','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Pike','Pike','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Pope','Pope','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Pulaski','Pulaski','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Putnam','Putnam','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Randolph','Randolph','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Richland','Richland','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Rock Island','Rock Island','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Saline','Saline','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Sangamon','Sangamon','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Schuyler','Schuyler','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Scott','Scott','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Shelby','Shelby','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,St. Clair','St. Clair','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Stark','Stark','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Stephenson','Stephenson','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Tazewell','Tazewell','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Union','Union','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Vermilion','Vermilion','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Wabash','Wabash','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Warren','Warren','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Washington','Washington','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Wayne','Wayne','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,White','White','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Whiteside','Whiteside','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Will','Will','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Williamson','Williamson','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Winnebago','Winnebago','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IL,Woodford','Woodford','IL',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Adams','Adams','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Allen','Allen','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Bartholomew','Bartholomew','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Benton','Benton','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Blackford','Blackford','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Boone','Boone','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Brown','Brown','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Carroll','Carroll','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Cass','Cass','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Clark','Clark','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Clay','Clay','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Clinton','Clinton','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Crawford','Crawford','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Daviess','Daviess','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,DeKalb','DeKalb','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Dearborn','Dearborn','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Decatur','Decatur','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Delaware','Delaware','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Dubois','Dubois','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Elkhart','Elkhart','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Fayette','Fayette','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Floyd','Floyd','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Fountain','Fountain','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Franklin','Franklin','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Fulton','Fulton','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Gibson','Gibson','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Grant','Grant','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Greene','Greene','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Hamilton','Hamilton','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Hancock','Hancock','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Harrison','Harrison','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Hendricks','Hendricks','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Henry','Henry','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Howard','Howard','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Huntington','Huntington','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Jackson','Jackson','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Jasper','Jasper','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Jay','Jay','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Jefferson','Jefferson','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Jennings','Jennings','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Johnson','Johnson','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Knox','Knox','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Kosciusko','Kosciusko','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,La Porte','La Porte','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Lagrange','Lagrange','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Lake','Lake','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Lawrence','Lawrence','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Madison','Madison','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Marion','Marion','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Marshall','Marshall','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Martin','Martin','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Miami','Miami','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Monroe','Monroe','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Montgomery','Montgomery','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Morgan','Morgan','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Newton','Newton','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Noble','Noble','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Ohio','Ohio','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Orange','Orange','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Owen','Owen','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Parke','Parke','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Perry','Perry','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Pike','Pike','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Porter','Porter','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Posey','Posey','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Pulaski','Pulaski','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Putnam','Putnam','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Randolph','Randolph','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Ripley','Ripley','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Rush','Rush','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Scott','Scott','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Shelby','Shelby','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Spencer','Spencer','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,St. Joseph','St. Joseph','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Starke','Starke','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Steuben','Steuben','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Sullivan','Sullivan','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Switzerland','Switzerland','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Tippecanoe','Tippecanoe','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Tipton','Tipton','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Union','Union','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Vanderburgh','Vanderburgh','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Vermillion','Vermillion','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Vigo','Vigo','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Wabash','Wabash','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Warren','Warren','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Warrick','Warrick','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Washington','Washington','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Wayne','Wayne','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Wells','Wells','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,White','White','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'IN,Whitley','Whitley','IN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Allen','Allen','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Anderson','Anderson','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Atchison','Atchison','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Barber','Barber','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Barton','Barton','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Bourbon','Bourbon','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Brown','Brown','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Butler','Butler','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Chase','Chase','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Chautauqua','Chautauqua','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Cherokee','Cherokee','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Cheyenne','Cheyenne','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Clark','Clark','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Clay','Clay','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Cloud','Cloud','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Coffey','Coffey','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Comanche','Comanche','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Cowley','Cowley','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Crawford','Crawford','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Decatur','Decatur','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Dickinson','Dickinson','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Doniphan','Doniphan','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Douglas','Douglas','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Edwards','Edwards','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Elk','Elk','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Ellis','Ellis','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Ellsworth','Ellsworth','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Finney','Finney','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Ford','Ford','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Franklin','Franklin','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Geary','Geary','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Gove','Gove','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Graham','Graham','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Grant','Grant','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Gray','Gray','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Greeley','Greeley','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Greenwood','Greenwood','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Hamilton','Hamilton','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Harper','Harper','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Harvey','Harvey','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Haskell','Haskell','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Hodgeman','Hodgeman','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Jackson','Jackson','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Jefferson','Jefferson','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Jewell','Jewell','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Johnson','Johnson','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Kearny','Kearny','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Kingman','Kingman','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Kiowa','Kiowa','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Labette','Labette','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Lane','Lane','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Leavenworth','Leavenworth','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Lincoln','Lincoln','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Linn','Linn','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Logan','Logan','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Lyon','Lyon','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Marion','Marion','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Marshall','Marshall','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,McPherson','McPherson','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Meade','Meade','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Miami','Miami','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Mitchell','Mitchell','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Montgomery','Montgomery','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Morris','Morris','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Morton','Morton','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Nemaha','Nemaha','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Neosho','Neosho','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Ness','Ness','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Norton','Norton','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Osage','Osage','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Osborne','Osborne','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Ottawa','Ottawa','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Pawnee','Pawnee','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Phillips','Phillips','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Pottawatomie','Pottawatomie','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Pratt','Pratt','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Rawlins','Rawlins','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Reno','Reno','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Republic','Republic','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Rice','Rice','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Riley','Riley','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Rooks','Rooks','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Rush','Rush','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Russell','Russell','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Saline','Saline','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Scott','Scott','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Sedgwick','Sedgwick','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Seward','Seward','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Shawnee','Shawnee','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Sheridan','Sheridan','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Sherman','Sherman','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Smith','Smith','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Stafford','Stafford','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Stanton','Stanton','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Stevens','Stevens','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Sumner','Sumner','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Thomas','Thomas','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Trego','Trego','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Wabaunsee','Wabaunsee','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Wallace','Wallace','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Washington','Washington','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Wichita','Wichita','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Wilson','Wilson','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Woodson','Woodson','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KS,Wyandotte','Wyandotte','KS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Adair','Adair','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Allen','Allen','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Anderson','Anderson','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Ballard','Ballard','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Barren','Barren','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Bath','Bath','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Bell','Bell','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Boone','Boone','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Bourbon','Bourbon','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Boyd','Boyd','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Boyle','Boyle','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Bracken','Bracken','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Breathitt','Breathitt','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Breckinridge','Breckinridge','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Bullitt','Bullitt','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Butler','Butler','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Caldwell','Caldwell','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Calloway','Calloway','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Campbell','Campbell','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Carlisle','Carlisle','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Carroll','Carroll','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Carter','Carter','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Casey','Casey','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Christian','Christian','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Clark','Clark','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Clay','Clay','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Clinton','Clinton','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Crittenden','Crittenden','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Cumberland','Cumberland','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Daviess','Daviess','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Edmonson','Edmonson','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Elliott','Elliott','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Estill','Estill','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Fayette','Fayette','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Fleming','Fleming','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Floyd','Floyd','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Franklin','Franklin','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Fulton','Fulton','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Gallatin','Gallatin','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Garrard','Garrard','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Grant','Grant','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Graves','Graves','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Grayson','Grayson','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Green','Green','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Greenup','Greenup','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Hancock','Hancock','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Hardin','Hardin','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Harlan','Harlan','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Harrison','Harrison','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Hart','Hart','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Henderson','Henderson','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Henry','Henry','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Hickman','Hickman','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Hopkins','Hopkins','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Jackson','Jackson','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Jefferson','Jefferson','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Jessamine','Jessamine','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Johnson','Johnson','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Kenton','Kenton','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Knott','Knott','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Knox','Knox','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Larue','Larue','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Laurel','Laurel','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Lawrence','Lawrence','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Lee','Lee','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Leslie','Leslie','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Letcher','Letcher','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Lewis','Lewis','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Lincoln','Lincoln','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Livingston','Livingston','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Logan','Logan','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Lyon','Lyon','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Madison','Madison','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Magoffin','Magoffin','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Marion','Marion','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Marshall','Marshall','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Martin','Martin','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Mason','Mason','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,McCracken','McCracken','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,McCreary','McCreary','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,McLean','McLean','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Meade','Meade','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Menifee','Menifee','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Mercer','Mercer','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Metcalfe','Metcalfe','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Monroe','Monroe','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Montgomery','Montgomery','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Morgan','Morgan','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Muhlenberg','Muhlenberg','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Nelson','Nelson','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Nicholas','Nicholas','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Ohio','Ohio','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Oldham','Oldham','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Owen','Owen','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Owsley','Owsley','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Pendleton','Pendleton','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Perry','Perry','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Pike','Pike','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Powell','Powell','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Pulaski','Pulaski','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Robertson','Robertson','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Rockcastle','Rockcastle','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Rowan','Rowan','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Russell','Russell','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Scott','Scott','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Shelby','Shelby','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Simpson','Simpson','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Spencer','Spencer','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Taylor','Taylor','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Todd','Todd','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Trigg','Trigg','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Trimble','Trimble','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Union','Union','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Warren','Warren','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Washington','Washington','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Wayne','Wayne','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Webster','Webster','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Whitley','Whitley','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Wolfe','Wolfe','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'KY,Woodford','Woodford','KY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Acadia','Acadia Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Allen','Allen Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Ascension','Ascension Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Assumption','Assumption Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Avoyelles','Avoyelles Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Beauregard','Beauregard Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Bienville','Bienville Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Bossier','Bossier Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Caddo','Caddo Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Calcasieu','Calcasieu Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Caldwell','Caldwell Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Cameron','Cameron Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Catahoula','Catahoula Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Claiborne','Claiborne Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Concordia','Concordia Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,DeSoto','DeSoto Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,E. Baton Rouge','East Baton Rouge Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,E. Carroll','East Carroll Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,E. Feliciana','East Feliciana Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Evangeline','Evangeline Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Franklin','Franklin Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Grant','Grant Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Iberia','Iberia Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Iberville','Iberville Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Jackson','Jackson Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Jefferson Davis','Jefferson Davis Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Jefferson','Jefferson Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,La Salle','La Salle Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Lafayette','Lafayette Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Lafourche','Lafourche Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Lincoln','Lincoln Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Livingston','Livingston Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Madison','Madison Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Morehouse','Morehouse Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Natchitoches','Natchitoches Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Orleans','Orleans Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Ouachita','Ouachita Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Plaquemines','Plaquemines Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Pointe Coupee','Pointe Coupee Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Rapides','Rapides Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Red River','Red River Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Richland','Richland Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Sabine','Sabine Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,St. Bernard','St. Bernard Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,St. Charles','St. Charles Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,St. Helena','St. Helena Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,St. James','St. James Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,St. John the Baptist','St. John the Baptist Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,St. Landry','St. Landry Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,St. Martin','St. Martin Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,St. Mary','St. Mary Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,St. Tammany','St. Tammany Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Tangipahoa','Tangipahoa Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Tensas','Tensas Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Terrebonne','Terrebonne Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Union','Union Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Vermilion','Vermilion Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Vernon','Vernon Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Washington','Washington Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Webster','Webster Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,W. Baton Rouge','West Baton Rouge Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,W. Carroll','West Carroll Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,W. Feliciana','West Feliciana Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'LA,Winn','Winn Parish','LA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MD,Allegany','Allegany','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MD,Anne Arundel','Anne Arundel','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MD,Baltimore City','Baltimore City','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MD,Baltimore','Baltimore','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MD,Calvert','Calvert','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MD,Caroline','Caroline','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MD,Carroll','Carroll','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MD,Cecil','Cecil','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MD,Charles','Charles','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MD,Dorchester','Dorchester','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MD,Frederick','Frederick','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MD,Garrett','Garrett','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MD,Harford','Harford','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MD,Howard','Howard','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MD,Kent','Kent','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MD,Montgomery','Montgomery','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MD,Prince George''s','Prince George''s','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MD,Queen Anne''s','Queen Anne''s','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MD,Somerset','Somerset','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MD,St. Mary''s','St. Mary''s','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MD,Talbot','Talbot','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MD,Washington','Washington','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MD,Wicomico','Wicomico','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MD,Worcester','Worcester','MD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ME,Androscoggin','Androscoggin','ME',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ME,Aroostook','Aroostook','ME',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ME,Cumberland','Cumberland','ME',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ME,Franklin','Franklin','ME',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ME,Hancock','Hancock','ME',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ME,Kennebec','Kennebec','ME',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ME,Knox','Knox','ME',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ME,Lincoln','Lincoln','ME',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ME,Oxford','Oxford','ME',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ME,Penobscot','Penobscot','ME',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ME,Piscataquis','Piscataquis','ME',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ME,Sagadahoc','Sagadahoc','ME',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ME,Somerset','Somerset','ME',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ME,Waldo','Waldo','ME',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ME,Washington','Washington','ME',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ME,York','York','ME',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Alcona','Alcona','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Alger','Alger','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Allegan','Allegan','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Alpena','Alpena','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Antrim','Antrim','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Arenac','Arenac','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Baraga','Baraga','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Barry','Barry','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Bay','Bay','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Benzie','Benzie','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Berrien','Berrien','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Branch','Branch','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Calhoun','Calhoun','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Cass','Cass','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Charlevoix','Charlevoix','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Cheboygan','Cheboygan','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Chippewa','Chippewa','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Clare','Clare','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Clinton','Clinton','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Crawford','Crawford','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Delta','Delta','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Dickinson','Dickinson','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Eaton','Eaton','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Emmet','Emmet','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Genesee','Genesee','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Gladwin','Gladwin','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Gogebic','Gogebic','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Grand Traverse','Grand Traverse','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Gratiot','Gratiot','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Hillsdale','Hillsdale','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Houghton','Houghton','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Huron','Huron','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Ingham','Ingham','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Ionia','Ionia','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Iosco','Iosco','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Iron','Iron','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Isabella','Isabella','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Jackson','Jackson','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Kalamazoo','Kalamazoo','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Kalkaska','Kalkaska','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Kent','Kent','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Keweenaw','Keweenaw','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Lake','Lake','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Lapeer','Lapeer','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Leelanau','Leelanau','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Lenawee','Lenawee','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Livingston','Livingston','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Luce','Luce','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Mackinac','Mackinac','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Macomb','Macomb','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Manistee','Manistee','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Marquette','Marquette','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Mason','Mason','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Mecosta','Mecosta','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Menominee','Menominee','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Midland','Midland','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Missaukee','Missaukee','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Monroe','Monroe','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Montcalm','Montcalm','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Montmorency','Montmorency','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Muskegon','Muskegon','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Newaygo','Newaygo','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Oakland','Oakland','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Oceana','Oceana','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Ogemaw','Ogemaw','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Ontonagon','Ontonagon','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Osceola','Osceola','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Oscoda','Oscoda','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Otsego','Otsego','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Ottawa','Ottawa','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Presque Isle','Presque Isle','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Roscommon','Roscommon','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Saginaw','Saginaw','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Sanilac','Sanilac','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Schoolcraft','Schoolcraft','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Shiawassee','Shiawassee','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,St. Clair','St. Clair','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,St. Joseph','St. Joseph','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Tuscola','Tuscola','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Van Buren','Van Buren','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Washtenaw','Washtenaw','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Wayne','Wayne','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MI,Wexford','Wexford','MI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Aitkin','Aitkin','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Anoka','Anoka','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Becker','Becker','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Beltrami','Beltrami','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Benton','Benton','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Big Stone','Big Stone','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Blue Earth','Blue Earth','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Brown','Brown','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Carlton','Carlton','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Carver','Carver','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Cass','Cass','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Chippewa','Chippewa','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Chisago','Chisago','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Clay','Clay','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Clearwater','Clearwater','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Cook','Cook','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Cottonwood','Cottonwood','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Crow Wing','Crow Wing','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Dakota','Dakota','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Dodge','Dodge','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Douglas','Douglas','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Faribault','Faribault','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Fillmore','Fillmore','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Freeborn','Freeborn','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Goodhue','Goodhue','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Grant','Grant','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Hennepin','Hennepin','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Houston','Houston','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Hubbard','Hubbard','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Isanti','Isanti','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Itasca','Itasca','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Jackson','Jackson','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Kanabec','Kanabec','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Kandiyohi','Kandiyohi','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Kittson','Kittson','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Koochiching','Koochiching','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Lac qui Parle','Lac qui Parle','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Lake of the Woods','Lake of the Woods','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Lake','Lake','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Le Sueur','Le Sueur','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Lincoln','Lincoln','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Lyon','Lyon','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Mahnomen','Mahnomen','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Marshall','Marshall','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Martin','Martin','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,McLeod','McLeod','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Meeker','Meeker','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Mille Lacs','Mille Lacs','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Morrison','Morrison','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Mower','Mower','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Murray','Murray','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Nicollet','Nicollet','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Nobles','Nobles','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Norman','Norman','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Olmsted','Olmsted','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Otter Tail','Otter Tail','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Pennington','Pennington','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Pine','Pine','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Pipestone','Pipestone','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Polk','Polk','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Pope','Pope','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Ramsey','Ramsey','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Red Lake','Red Lake','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Redwood','Redwood','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Renville','Renville','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Rice','Rice','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Rock','Rock','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Roseau','Roseau','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Scott','Scott','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Sherburne','Sherburne','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Sibley','Sibley','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,St. Louis','St. Louis','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Stearns','Stearns','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Steele','Steele','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Stevens','Stevens','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Swift','Swift','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Todd','Todd','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Traverse','Traverse','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Wabasha','Wabasha','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Wadena','Wadena','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Waseca','Waseca','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Washington','Washington','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Watonwan','Watonwan','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Wilkin','Wilkin','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Winona','Winona','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Wright','Wright','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MN,Yellow Medicine','Yellow Medicine','MN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Adair','Adair','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Andrew','Andrew','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Atchison','Atchison','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Audrain','Audrain','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Barry','Barry','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Barton','Barton','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Bates','Bates','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Benton','Benton','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Bollinger','Bollinger','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Boone','Boone','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Buchanan','Buchanan','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Butler','Butler','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Caldwell','Caldwell','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Callaway','Callaway','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Camden','Camden','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Cape Girardeau','Cape Girardeau','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Carroll','Carroll','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Carter','Carter','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Cass','Cass','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Cedar','Cedar','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Chariton','Chariton','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Christian','Christian','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Clark','Clark','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Clay','Clay','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Clinton','Clinton','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Cole','Cole','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Cooper','Cooper','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Crawford','Crawford','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Dade','Dade','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Dallas','Dallas','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Daviess','Daviess','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Dent','Dent','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,DeKalb','DeKalb','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Douglas','Douglas','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Dunklin','Dunklin','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Franklin','Franklin','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Gasconade','Gasconade','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Gentry','Gentry','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Greene','Greene','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Grundy','Grundy','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Harrison','Harrison','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Henry','Henry','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Hickory','Hickory','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Holt','Holt','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Howard','Howard','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Howell','Howell','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Iron','Iron','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Jackson','Jackson','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Jasper','Jasper','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Jefferson','Jefferson','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Johnson','Johnson','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Knox','Knox','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Laclede','Laclede','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Lafayette','Lafayette','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Lawrence','Lawrence','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Lewis','Lewis','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Lincoln','Lincoln','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Linn','Linn','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Livingston','Livingston','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Macon','Macon','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Madison','Madison','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Maries','Maries','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Marion','Marion','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,McDonald','McDonald','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Mercer','Mercer','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Miller','Miller','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Mississippi','Mississippi','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Moniteau','Moniteau','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Monroe','Monroe','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Montgomery','Montgomery','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Morgan','Morgan','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,New Madrid','New Madrid','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Newton','Newton','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Nodaway','Nodaway','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Oregon','Oregon','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Osage','Osage','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Ozark','Ozark','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Pemiscot','Pemiscot','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Perry','Perry','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Pettis','Pettis','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Phelps','Phelps','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Pike','Pike','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Platte','Platte','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Polk','Polk','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Pulaski','Pulaski','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Putnam','Putnam','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Ralls','Ralls','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Randolph','Randolph','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Ray','Ray','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Reynolds','Reynolds','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Ripley','Ripley','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Saline','Saline','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Schuyler','Schuyler','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Scotland','Scotland','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Scott','Scott','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Shannon','Shannon','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Shelby','Shelby','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,St. Charles','St. Charles','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,St. Clair','St. Clair','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,St. Francois','St. Francois','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,St. Louis City','St. Louis City','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,St. Louis','St. Louis','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Ste. Genevieve','Ste. Genevieve','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Stoddard','Stoddard','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Stone','Stone','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Sullivan','Sullivan','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Taney','Taney','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Texas','Texas','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Vernon','Vernon','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Warren','Warren','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Washington','Washington','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Wayne','Wayne','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Webster','Webster','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Worth','Worth','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MO,Wright','Wright','MO',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Adams','Adams','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Alcorn','Alcorn','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Amite','Amite','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Attala','Attala','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Benton','Benton','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Bolivar','Bolivar','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Calhoun','Calhoun','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Carroll','Carroll','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Chickasaw','Chickasaw','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Choctaw','Choctaw','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Claiborne','Claiborne','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Clarke','Clarke','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Clay','Clay','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Coahoma','Coahoma','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Copiah','Copiah','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Covington','Covington','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,DeSoto','DeSoto','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Forrest','Forrest','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Franklin','Franklin','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,George','George','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Greene','Greene','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Grenada','Grenada','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Hancock','Hancock','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Harrison','Harrison','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Hinds','Hinds','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Holmes','Holmes','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Humphreys','Humphreys','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Issaquena','Issaquena','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Itawamba','Itawamba','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Jackson','Jackson','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Jasper','Jasper','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Jefferson','Jefferson','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Jefferson Davis','Jefferson Davis','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Jones','Jones','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Kemper','Kemper','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Lafayette','Lafayette','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Lamar','Lamar','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Lauderdale','Lauderdale','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Lawrence','Lawrence','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Leake','Leake','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Lee','Lee','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Leflore','Leflore','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Lincoln','Lincoln','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Lowndes','Lowndes','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Madison','Madison','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Marion','Marion','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Marshall','Marshall','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Monroe','Monroe','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Montgomery','Montgomery','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Neshoba','Neshoba','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Newton','Newton','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Noxubee','Noxubee','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Oktibbeha','Oktibbeha','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Panola','Panola','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Pearl River','Pearl River','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Perry','Perry','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Pike','Pike','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Pontotoc','Pontotoc','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Prentiss','Prentiss','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Quitman','Quitman','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Rankin','Rankin','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Scott','Scott','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Sharkey','Sharkey','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Simpson','Simpson','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Smith','Smith','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Stone','Stone','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Sunflower','Sunflower','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Tallahatchie','Tallahatchie','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Tate','Tate','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Tippah','Tippah','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Tishomingo','Tishomingo','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Tunica','Tunica','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Union','Union','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Walthall','Walthall','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Warren','Warren','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Washington','Washington','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Wayne','Wayne','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Webster','Webster','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Wilkinson','Wilkinson','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Winston','Winston','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Yalobusha','Yalobusha','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MS,Yazoo','Yazoo','MS',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Beaverhead','Beaverhead','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Big Horn','Big Horn','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Blaine','Blaine','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Broadwater','Broadwater','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Carbon','Carbon','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Carter','Carter','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Cascade','Cascade','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Chouteau','Chouteau','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Custer','Custer','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Daniels','Daniels','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Dawson','Dawson','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Deer Lodge','Deer Lodge','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Fallon','Fallon','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Fergus','Fergus','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Flathead','Flathead','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Gallatin','Gallatin','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Garfield','Garfield','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Glacier','Glacier','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Golden Valley','Golden Valley','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Granite','Granite','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Hill','Hill','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Jefferson','Jefferson','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Judith Basin','Judith Basin','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Lake','Lake','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Lewis & Clark','Lewis & Clark','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Liberty','Liberty','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Lincoln','Lincoln','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Madison','Madison','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,McCone','McCone','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Meagher','Meagher','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Mineral','Mineral','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Missoula','Missoula','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Musselshell','Musselshell','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Park','Park','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Petroleum','Petroleum','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Phillips','Phillips','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Pondera','Pondera','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Powder River','Powder River','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Powell','Powell','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Prairie','Prairie','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Ravalli','Ravalli','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Richland','Richland','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Roosevelt','Roosevelt','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Rosebud','Rosebud','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Sanders','Sanders','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Sheridan','Sheridan','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Silver Bow','Silver Bow','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Stillwater','Stillwater','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Sweet Grass','Sweet Grass','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Teton','Teton','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Toole','Toole','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Treasure','Treasure','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Valley','Valley','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Wheatland','Wheatland','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Wibaux','Wibaux','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'MT,Yellowstone','Yellowstone','MT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Alamance','Alamance','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Alexander','Alexander','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Alleghany','Alleghany','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Anson','Anson','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Ashe','Ashe','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Avery','Avery','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Beaufort','Beaufort','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Bertie','Bertie','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Bladen','Bladen','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Brunswick','Brunswick','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Buncombe','Buncombe','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Burke','Burke','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Cabarrus','Cabarrus','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Caldwell','Caldwell','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Camden','Camden','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Carteret','Carteret','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Caswell','Caswell','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Catawba','Catawba','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Chatham','Chatham','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Cherokee','Cherokee','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Chowan','Chowan','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Clay','Clay','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Cleveland','Cleveland','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Columbus','Columbus','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Craven','Craven','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Cumberland','Cumberland','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Currituck','Currituck','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Dare','Dare','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Davidson','Davidson','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Davie','Davie','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Duplin','Duplin','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Durham','Durham','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Edgecombe','Edgecombe','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Forsyth','Forsyth','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Franklin','Franklin','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Gaston','Gaston','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Gates','Gates','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Graham','Graham','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Granville','Granville','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Greene','Greene','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Guilford','Guilford','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Halifax','Halifax','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Harnett','Harnett','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Haywood','Haywood','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Henderson','Henderson','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Hertford','Hertford','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Hoke','Hoke','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Hyde','Hyde','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Iredell','Iredell','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Jackson','Jackson','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Johnston','Johnston','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Jones','Jones','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Lee','Lee','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Lenoir','Lenoir','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Lincoln','Lincoln','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Macon','Macon','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Madison','Madison','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Martin','Martin','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,McDowell','McDowell','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Mecklenburg','Mecklenburg','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Mitchell','Mitchell','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Montgomery','Montgomery','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Moore','Moore','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Nash','Nash','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,New Hanover','New Hanover','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Northampton','Northampton','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Onslow','Onslow','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Orange','Orange','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Pamlico','Pamlico','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Pasquotank','Pasquotank','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Pender','Pender','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Perquimans','Perquimans','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Person','Person','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Pitt','Pitt','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Polk','Polk','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Randolph','Randolph','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Richmond','Richmond','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Robeson','Robeson','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Rockingham','Rockingham','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Rowan','Rowan','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Rutherford','Rutherford','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Sampson','Sampson','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Scotland','Scotland','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Stanly','Stanly','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Stokes','Stokes','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Surry','Surry','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Swain','Swain','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Transylvania','Transylvania','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Tyrrell','Tyrrell','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Union','Union','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Vance','Vance','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Wake','Wake','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Warren','Warren','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Washington','Washington','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Watauga','Watauga','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Wayne','Wayne','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Wilkes','Wilkes','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Wilson','Wilson','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Yadkin','Yadkin','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NC,Yancey','Yancey','NC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Adams','Adams','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Barnes','Barnes','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Benson','Benson','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Billings','Billings','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Bottineau','Bottineau','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Bowman','Bowman','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Burke','Burke','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Burleigh','Burleigh','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Cass','Cass','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Cavalier','Cavalier','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Dickey','Dickey','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Divide','Divide','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Dunn','Dunn','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Eddy','Eddy','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Emmons','Emmons','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Foster','Foster','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Golden Valley','Golden Valley','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Grand Forks','Grand Forks','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Grant','Grant','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Griggs','Griggs','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Hettinger','Hettinger','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Kidder','Kidder','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,LaMoure','LaMoure','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Logan','Logan','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,McHenry','McHenry','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,McIntosh','McIntosh','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,McKenzie','McKenzie','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,McLean','McLean','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Mercer','Mercer','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Morton','Morton','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Mountrail','Mountrail','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Nelson','Nelson','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Oliver','Oliver','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Pembina','Pembina','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Pierce','Pierce','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Ramsey','Ramsey','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Ransom','Ransom','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Renville','Renville','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Richland','Richland','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Rolette','Rolette','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Sargent','Sargent','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Sheridan','Sheridan','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Sioux','Sioux','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Slope','Slope','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Stark','Stark','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Steele','Steele','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Stutsman','Stutsman','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Towner','Towner','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Traill','Traill','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Walsh','Walsh','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Ward','Ward','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Wells','Wells','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'ND,Williams','Williams','ND',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Adams','Adams','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Antelope','Antelope','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Arthur','Arthur','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Banner','Banner','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Blaine','Blaine','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Boone','Boone','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Box Butte','Box Butte','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Boyd','Boyd','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Brown','Brown','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Buffalo','Buffalo','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Burt','Burt','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Butler','Butler','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Cass','Cass','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Cedar','Cedar','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Chase','Chase','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Cherry','Cherry','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Cheyenne','Cheyenne','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Clay','Clay','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Colfax','Colfax','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Cuming','Cuming','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Custer','Custer','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Dakota','Dakota','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Dawes','Dawes','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Dawson','Dawson','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Deuel','Deuel','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Dixon','Dixon','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Dodge','Dodge','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Douglas','Douglas','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Dundy','Dundy','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Fillmore','Fillmore','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Franklin','Franklin','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Frontier','Frontier','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Furnas','Furnas','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Gage','Gage','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Garden','Garden','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Garfield','Garfield','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Gosper','Gosper','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Grant','Grant','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Greeley','Greeley','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Hall','Hall','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Hamilton','Hamilton','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Harlan','Harlan','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Hayes','Hayes','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Hitchcock','Hitchcock','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Holt','Holt','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Hooker','Hooker','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Howard','Howard','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Jefferson','Jefferson','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Johnson','Johnson','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Kearney','Kearney','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Keith','Keith','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Keya Paha','Keya Paha','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Kimball','Kimball','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Knox','Knox','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Lancaster','Lancaster','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Lincoln','Lincoln','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Logan','Logan','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Loup','Loup','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Madison','Madison','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,McPherson','McPherson','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Merrick','Merrick','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Morrill','Morrill','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Nance','Nance','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Nemaha','Nemaha','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Nuckolls','Nuckolls','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Otoe','Otoe','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Pawnee','Pawnee','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Perkins','Perkins','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Phelps','Phelps','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Pierce','Pierce','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Platte','Platte','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Polk','Polk','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Red Willow','Red Willow','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Richardson','Richardson','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Rock','Rock','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Saline','Saline','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Sarpy','Sarpy','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Saunders','Saunders','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Scotts Bluff','Scotts Bluff','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Seward','Seward','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Sheridan','Sheridan','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Sherman','Sherman','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Sioux','Sioux','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Stanton','Stanton','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Thayer','Thayer','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Thomas','Thomas','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Thurston','Thurston','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Valley','Valley','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Washington','Washington','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Wayne','Wayne','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Webster','Webster','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,Wheeler','Wheeler','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NE,York','York','NE',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NH,Belknap','Belknap','NH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NH,Carroll','Carroll','NH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NH,Cheshire','Cheshire','NH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NH,Coos','Coos','NH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NH,Grafton','Grafton','NH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NH,Hillsborough','Hillsborough','NH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NH,Merrimack','Merrimack','NH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NH,Rockingham','Rockingham','NH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NH,Strafford','Strafford','NH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NH,Sullivan','Sullivan','NH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NM,Cibola','Cibola','NM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NM,Bernalillo','Bernalillo','NM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NM,Catron','Catron','NM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NM,Chaves','Chaves','NM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NM,Colfax','Colfax','NM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NM,Curry','Curry','NM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NM,De Baca','De Baca','NM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NM,Dona Ana','Dona Ana','NM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NM,Eddy','Eddy','NM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NM,Grant','Grant','NM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NM,Guadalupe','Guadalupe','NM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NM,Harding','Harding','NM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NM,Hidalgo','Hidalgo','NM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NM,Lea','Lea','NM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NM,Lincoln','Lincoln','NM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NM,Los Alamos','Los Alamos','NM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NM,Luna','Luna','NM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NM,McKinley','McKinley','NM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NM,Mora','Mora','NM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NM,Otero','Otero','NM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NM,Quay','Quay','NM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NM,Rio Arriba','Rio Arriba','NM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NM,Roosevelt','Roosevelt','NM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NM,San Juan','San Juan','NM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NM,San Miguel','San Miguel','NM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NM,Sandoval','Sandoval','NM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NM,Santa Fe','Santa Fe','NM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NM,Sierra','Sierra','NM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NM,Socorro','Socorro','NM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NM,Taos','Taos','NM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NM,Torrance','Torrance','NM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NM,Union','Union','NM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NM,Valencia','Valencia','NM',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NV,Churchill','Churchill','NV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NV,Clark','Clark','NV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NV,Douglas','Douglas','NV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NV,Elko','Elko','NV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NV,Esmeralda','Esmeralda','NV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NV,Eureka','Eureka','NV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NV,Humboldt','Humboldt','NV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NV,Lander','Lander','NV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NV,Lincoln','Lincoln','NV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NV,Lyon','Lyon','NV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NV,Mineral','Mineral','NV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NV,Nye','Nye','NV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NV,Pershing','Pershing','NV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NV,Storey','Storey','NV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NV,Washoe','Washoe','NV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'NV,White Pine','White Pine','NV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Adams','Adams','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Allen','Allen','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Ashland','Ashland','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Ashtabula','Ashtabula','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Athens','Athens','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Auglaize','Auglaize','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Belmont','Belmont','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Brown','Brown','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Butler','Butler','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Carroll','Carroll','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Champaign','Champaign','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Clark','Clark','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Clermont','Clermont','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Clinton','Clinton','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Columbiana','Columbiana','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Coshocton','Coshocton','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Crawford','Crawford','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Cuyahoga','Cuyahoga','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Darke','Darke','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Defiance','Defiance','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Delaware','Delaware','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Erie','Erie','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Fairfield','Fairfield','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Fayette','Fayette','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Franklin','Franklin','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Fulton','Fulton','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Gallia','Gallia','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Geauga','Geauga','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Greene','Greene','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Guernsey','Guernsey','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Hamilton','Hamilton','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Hancock','Hancock','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Hardin','Hardin','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Harrison','Harrison','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Henry','Henry','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Highland','Highland','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Hocking','Hocking','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Holmes','Holmes','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Huron','Huron','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Jackson','Jackson','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Jefferson','Jefferson','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Knox','Knox','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Lake','Lake','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Lawrence','Lawrence','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Licking','Licking','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Logan','Logan','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Lorain','Lorain','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Lucas','Lucas','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Madison','Madison','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Mahoning','Mahoning','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Marion','Marion','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Medina','Medina','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Meigs','Meigs','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Mercer','Mercer','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Miami','Miami','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Monroe','Monroe','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Montgomery','Montgomery','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Morgan','Morgan','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Morrow','Morrow','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Muskingum','Muskingum','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Noble','Noble','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Ottawa','Ottawa','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Paulding','Paulding','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Perry','Perry','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Pickaway','Pickaway','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Pike','Pike','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Portage','Portage','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Preble','Preble','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Putnam','Putnam','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Richland','Richland','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Ross','Ross','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Sandusky','Sandusky','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Scioto','Scioto','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Seneca','Seneca','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Shelby','Shelby','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Stark','Stark','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Summit','Summit','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Trumbull','Trumbull','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Tuscarawas','Tuscarawas','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Union','Union','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Van Wert','Van Wert','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Vinton','Vinton','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Warren','Warren','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Washington','Washington','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Wayne','Wayne','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Williams','Williams','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Wood','Wood','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OH,Wyandot','Wyandot','OH',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Adair','Adair','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Alfalfa','Alfalfa','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Atoka','Atoka','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Beaver','Beaver','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Beckham','Beckham','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Blaine','Blaine','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Bryan','Bryan','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Caddo','Caddo','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Canadian','Canadian','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Carter','Carter','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Cherokee','Cherokee','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Choctaw','Choctaw','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Cimarron','Cimarron','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Cleveland','Cleveland','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Coal','Coal','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Comanche','Comanche','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Cotton','Cotton','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Craig','Craig','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Creek','Creek','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Custer','Custer','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Delaware','Delaware','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Dewey','Dewey','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Ellis','Ellis','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Garfield','Garfield','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Garvin','Garvin','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Grady','Grady','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Grant','Grant','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Greer','Greer','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Harmon','Harmon','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Harper','Harper','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Haskell','Haskell','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Hughes','Hughes','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Jackson','Jackson','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Jefferson','Jefferson','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Johnston','Johnston','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Kay','Kay','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Kingfisher','Kingfisher','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Kiowa','Kiowa','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Latimer','Latimer','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Le Flore','Le Flore','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Lincoln','Lincoln','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Logan','Logan','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Love','Love','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Major','Major','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Marshall','Marshall','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Mayes','Mayes','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,McClain','McClain','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,McCurtain','McCurtain','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,McIntosh','McIntosh','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Murray','Murray','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Muskogee','Muskogee','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Noble','Noble','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Nowata','Nowata','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Okfuskee','Okfuskee','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Oklahoma','Oklahoma','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Okmulgee','Okmulgee','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Osage','Osage','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Ottawa','Ottawa','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Pawnee','Pawnee','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Payne','Payne','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Pittsburg','Pittsburg','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Pontotoc','Pontotoc','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Pottawatomie','Pottawatomie','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Pushmataha','Pushmataha','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Roger Mills','Roger Mills','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Rogers','Rogers','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Seminole','Seminole','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Sequoyah','Sequoyah','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Stephens','Stephens','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Texas','Texas','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Tillman','Tillman','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Tulsa','Tulsa','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Wagoner','Wagoner','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Washington','Washington','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Washita','Washita','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Woods','Woods','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OK,Woodward','Woodward','OK',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OR,Baker','Baker','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OR,Benton','Benton','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OR,Clackamas','Clackamas','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OR,Clatsop','Clatsop','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OR,Columbia','Columbia','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OR,Coos','Coos','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OR,Crook','Crook','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OR,Curry','Curry','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OR,Deschutes','Deschutes','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OR,Douglas','Douglas','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OR,Gilliam','Gilliam','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OR,Grant','Grant','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OR,Harney','Harney','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OR,Hood River','Hood River','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OR,Jackson','Jackson','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OR,Jefferson','Jefferson','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OR,Josephine','Josephine','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OR,Klamath','Klamath','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OR,Lake','Lake','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OR,Lane','Lane','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OR,Lincoln','Lincoln','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OR,Linn','Linn','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OR,Malheur','Malheur','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OR,Marion','Marion','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OR,Morrow','Morrow','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OR,Multnomah','Multnomah','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OR,Polk','Polk','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OR,Sherman','Sherman','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OR,Tillamook','Tillamook','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OR,Umatilla','Umatilla','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OR,Union','Union','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OR,Wallowa','Wallowa','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OR,Wasco','Wasco','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OR,Washington','Washington','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OR,Wheeler','Wheeler','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'OR,Yamhill','Yamhill','OR',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'RI,Bristol','Bristol','RI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'RI,Kent','Kent','RI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'RI,Newport','Newport','RI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'RI,Providence','Providence','RI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'RI,Washington','Washington','RI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Abbeville','Abbeville','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Aiken','Aiken','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Allendale','Allendale','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Anderson','Anderson','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Bamberg','Bamberg','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Barnwell','Barnwell','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Beaufort','Beaufort','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Berkeley','Berkeley','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Calhoun','Calhoun','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Charleston','Charleston','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Cherokee','Cherokee','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Chester','Chester','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Chesterfield','Chesterfield','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Clarendon','Clarendon','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Colleton','Colleton','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Darlington','Darlington','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Dillon','Dillon','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Dorchester','Dorchester','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Edgefield','Edgefield','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Fairfield','Fairfield','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Florence','Florence','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Georgetown','Georgetown','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Greenville','Greenville','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Greenwood','Greenwood','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Hampton','Hampton','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Horry','Horry','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Jasper','Jasper','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Kershaw','Kershaw','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Lancaster','Lancaster','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Laurens','Laurens','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Lee','Lee','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Lexington','Lexington','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Marion','Marion','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Marlboro','Marlboro','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,McCormick','McCormick','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Newberry','Newberry','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Oconee','Oconee','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Orangeburg','Orangeburg','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Pickens','Pickens','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Richland','Richland','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Saluda','Saluda','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Spartanburg','Spartanburg','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Sumter','Sumter','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Union','Union','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,Williamsburg','Williamsburg','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SC,York','York','SC',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Aurora','Aurora','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Beadle','Beadle','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Bennett','Bennett','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Bon Homme','Bon Homme','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Brookings','Brookings','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Brown','Brown','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Brule','Brule','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Buffalo','Buffalo','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Butte','Butte','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Campbell','Campbell','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Charles Mix','Charles Mix','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Clark','Clark','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Clay','Clay','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Codington','Codington','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Corson','Corson','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Custer','Custer','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Davison','Davison','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Day','Day','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Deuel','Deuel','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Dewey','Dewey','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Douglas','Douglas','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Edmunds','Edmunds','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Fall River','Fall River','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Faulk','Faulk','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Grant','Grant','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Gregory','Gregory','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Haakon','Haakon','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Hamlin','Hamlin','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Hand','Hand','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Hanson','Hanson','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Harding','Harding','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Hughes','Hughes','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Hutchinson','Hutchinson','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Hyde','Hyde','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Jackson','Jackson','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Jerauld','Jerauld','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Jones','Jones','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Kingsbury','Kingsbury','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Lake','Lake','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Lawrence','Lawrence','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Lincoln','Lincoln','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Lyman','Lyman','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Marshall','Marshall','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,McCook','McCook','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,McPherson','McPherson','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Meade','Meade','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Mellette','Mellette','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Miner','Miner','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Minnehaha','Minnehaha','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Moody','Moody','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Pennington','Pennington','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Perkins','Perkins','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Potter','Potter','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Roberts','Roberts','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Sanborn','Sanborn','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Spink','Spink','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Stanley','Stanley','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Sully','Sully','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Todd','Todd','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Tripp','Tripp','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Turner','Turner','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Union','Union','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Walworth','Walworth','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Yankton','Yankton','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Ziebach','Ziebach','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'SD,Oglala Lakota','Oglala Lakota','SD',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Anderson','Anderson','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Bedford','Bedford','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Benton','Benton','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Bledsoe','Bledsoe','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Blount','Blount','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Bradley','Bradley','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Campbell','Campbell','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Cannon','Cannon','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Carroll','Carroll','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Carter','Carter','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Cheatham','Cheatham','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Chester','Chester','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Claiborne','Claiborne','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Clay','Clay','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Cocke','Cocke','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Coffee','Coffee','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Crockett','Crockett','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Cumberland','Cumberland','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Davidson','Davidson','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Decatur','Decatur','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,DeKalb','DeKalb','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Dickson','Dickson','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Dyer','Dyer','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Fayette','Fayette','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Fentress','Fentress','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Franklin','Franklin','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Gibson','Gibson','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Giles','Giles','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Grainger','Grainger','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Greene','Greene','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Grundy','Grundy','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Hamblen','Hamblen','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Hamilton','Hamilton','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Hancock','Hancock','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Hardeman','Hardeman','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Hardin','Hardin','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Hawkins','Hawkins','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Haywood','Haywood','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Henderson','Henderson','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Henry','Henry','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Hickman','Hickman','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Houston','Houston','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Humphreys','Humphreys','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Jackson','Jackson','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Jefferson','Jefferson','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Johnson','Johnson','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Knox','Knox','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Lake','Lake','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Lauderdale','Lauderdale','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Lawrence','Lawrence','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Lewis','Lewis','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Lincoln','Lincoln','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Loudon','Loudon','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Macon','Macon','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Madison','Madison','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Marion','Marion','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Marshall','Marshall','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Maury','Maury','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,McMinn','McMinn','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,McNairy','McNairy','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Meigs','Meigs','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Monroe','Monroe','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Montgomery','Montgomery','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Moore','Moore','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Morgan','Morgan','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Obion','Obion','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Overton','Overton','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Perry','Perry','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Pickett','Pickett','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Polk','Polk','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Putnam','Putnam','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Rhea','Rhea','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Roane','Roane','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Robertson','Robertson','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Rutherford','Rutherford','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Scott','Scott','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Sequatchie','Sequatchie','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Sevier','Sevier','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Shelby','Shelby','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Smith','Smith','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Stewart','Stewart','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Sullivan','Sullivan','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Sumner','Sumner','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Tipton','Tipton','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Trousdale','Trousdale','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Unicoi','Unicoi','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Union','Union','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Van Buren','Van Buren','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Warren','Warren','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Washington','Washington','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Wayne','Wayne','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Weakley','Weakley','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,White','White','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Williamson','Williamson','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'TN,Wilson','Wilson','TN',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'UT,Beaver','Beaver','UT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'UT,Box Elder','Box Elder','UT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'UT,Cache','Cache','UT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'UT,Carbon','Carbon','UT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'UT,Daggett','Daggett','UT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'UT,Davis','Davis','UT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'UT,Duchesne','Duchesne','UT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'UT,Emery','Emery','UT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'UT,Garfield','Garfield','UT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'UT,Grand','Grand','UT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'UT,Iron','Iron','UT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'UT,Juab','Juab','UT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'UT,Kane','Kane','UT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'UT,Millard','Millard','UT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'UT,Morgan','Morgan','UT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'UT,Piute','Piute','UT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'UT,Rich','Rich','UT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'UT,Salt Lake','Salt Lake','UT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'UT,San Juan','San Juan','UT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'UT,Sanpete','Sanpete','UT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'UT,Sevier','Sevier','UT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'UT,Summit','Summit','UT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'UT,Tooele','Tooele','UT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'UT,Uintah','Uintah','UT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'UT,Utah','Utah','UT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'UT,Wasatch','Wasatch','UT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'UT,Washington','Washington','UT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'UT,Wayne','Wayne','UT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'UT,Weber','Weber','UT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Accomack','Accomack','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Albemarle','Albemarle','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Alleghany','Alleghany','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Amelia','Amelia','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Amherst','Amherst','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Appomattox','Appomattox','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Arlington','Arlington','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Augusta','Augusta','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Bath','Bath','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Bedford','Bedford','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Bland','Bland','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Botetourt','Botetourt','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Brunswick','Brunswick','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Buchanan','Buchanan','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Buckingham','Buckingham','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Campbell','Campbell','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Caroline','Caroline','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Carroll','Carroll','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Charles City','Charles City','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Charlotte','Charlotte','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Chesterfield','Chesterfield','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Clarke','Clarke','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Craig','Craig','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Culpeper','Culpeper','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Cumberland','Cumberland','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Dickenson','Dickenson','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Dinwiddie','Dinwiddie','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Essex','Essex','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Fairfax','Fairfax','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Fauquier','Fauquier','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Floyd','Floyd','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Fluvanna','Fluvanna','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Franklin','Franklin','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Frederick','Frederick','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Giles','Giles','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Gloucester','Gloucester','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Goochland','Goochland','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Grayson','Grayson','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Greene','Greene','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Greensville','Greensville','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Halifax','Halifax','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Hanover','Hanover','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Henrico','Henrico','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Henry','Henry','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Highland','Highland','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Isle of Wight','Isle of Wight','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,James City','James City','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,King and Queen','King and Queen','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,King George','King George','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,King William','King William','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Lancaster','Lancaster','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Lee','Lee','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Loudoun','Loudoun','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Louisa','Louisa','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Lunenburg','Lunenburg','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Madison','Madison','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Mathews','Mathews','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Mecklenburg','Mecklenburg','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Middlesex','Middlesex','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Montgomery','Montgomery','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Nelson','Nelson','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,New Kent','New Kent','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Northampton','Northampton','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Northumberland','Northumberland','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Nottoway','Nottoway','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Orange','Orange','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Page','Page','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Patrick','Patrick','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Pittsylvania','Pittsylvania','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Powhatan','Powhatan','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Prince Edward','Prince Edward','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Prince George','Prince George','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Prince William','Prince William','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Pulaski','Pulaski','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Rappahannock','Rappahannock','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Richmond','Richmond','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Roanoke','Roanoke','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Rockbridge','Rockbridge','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Rockingham','Rockingham','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Russell','Russell','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Scott','Scott','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Shenandoah','Shenandoah','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Smyth','Smyth','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Southampton','Southampton','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Spotsylvania','Spotsylvania','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Stafford','Stafford','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Surry','Surry','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Sussex','Sussex','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Tazewell','Tazewell','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Warren','Warren','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Washington','Washington','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Westmoreland','Westmoreland','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Wise','Wise','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,Wythe','Wythe','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VA,York','York','VA',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VT,Addison','Addison','VT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VT,Bennington','Bennington','VT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VT,Caledonia','Caledonia','VT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VT,Chittenden','Chittenden','VT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VT,Essex','Essex','VT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VT,Franklin','Franklin','VT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VT,Grand Isle','Grand Isle','VT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VT,Lamoille','Lamoille','VT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VT,Orange','Orange','VT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VT,Orleans','Orleans','VT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VT,Rutland','Rutland','VT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VT,Washington','Washington','VT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VT,Windham','Windham','VT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'VT,Windsor','Windsor','VT',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Adams','Adams','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Ashland','Ashland','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Barron','Barron','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Bayfield','Bayfield','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Brown','Brown','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Buffalo','Buffalo','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Burnett','Burnett','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Calumet','Calumet','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Chippewa','Chippewa','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Clark','Clark','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Columbia','Columbia','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Crawford','Crawford','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Dane','Dane','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Dodge','Dodge','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Door','Door','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Douglas','Douglas','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Dunn','Dunn','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Eau Claire','Eau Claire','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Florence','Florence','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Fond du Lac','Fond du Lac','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Forest','Forest','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Grant','Grant','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Green','Green','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Green Lake','Green Lake','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Iowa','Iowa','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Iron','Iron','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Jackson','Jackson','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Jefferson','Jefferson','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Juneau','Juneau','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Kenosha','Kenosha','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Kewaunee','Kewaunee','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,La Crosse','La Crosse','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Lafayette','Lafayette','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Langlade','Langlade','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Lincoln','Lincoln','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Manitowoc','Manitowoc','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Marathon','Marathon','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Marinette','Marinette','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Marquette','Marquette','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Menominee','Menominee','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Milwaukee','Milwaukee','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Monroe','Monroe','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Oconto','Oconto','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Oneida','Oneida','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Outagamie','Outagamie','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Ozaukee','Ozaukee','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Pepin','Pepin','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Pierce','Pierce','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Polk','Polk','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Portage','Portage','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Price','Price','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Racine','Racine','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Richland','Richland','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Rock','Rock','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Rusk','Rusk','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Sauk','Sauk','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Sawyer','Sawyer','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Shawano','Shawano','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Sheboygan','Sheboygan','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,St. Croix','St. Croix','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Taylor','Taylor','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Trempealeau','Trempealeau','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Vernon','Vernon','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Vilas','Vilas','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Walworth','Walworth','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Washburn','Washburn','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Washington','Washington','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Waukesha','Waukesha','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Waupaca','Waupaca','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Waushara','Waushara','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Winnebago','Winnebago','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WI,Wood','Wood','WI',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Barbour','Barbour','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Berkeley','Berkeley','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Boone','Boone','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Braxton','Braxton','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Brooke','Brooke','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Cabell','Cabell','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Calhoun','Calhoun','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Clay','Clay','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Doddridge','Doddridge','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Fayette','Fayette','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Gilmer','Gilmer','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Grant','Grant','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Greenbrier','Greenbrier','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Hampshire','Hampshire','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Hancock','Hancock','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Hardy','Hardy','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Harrison','Harrison','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Jackson','Jackson','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Jefferson','Jefferson','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Kanawha','Kanawha','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Lewis','Lewis','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Lincoln','Lincoln','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Logan','Logan','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Marion','Marion','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Marshall','Marshall','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Mason','Mason','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,McDowell','McDowell','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Mercer','Mercer','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Mineral','Mineral','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Mingo','Mingo','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Monongalia','Monongalia','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Monroe','Monroe','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Morgan','Morgan','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Nicholas','Nicholas','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Ohio','Ohio','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Pendleton','Pendleton','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Pleasants','Pleasants','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Pocahontas','Pocahontas','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Preston','Preston','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Putnam','Putnam','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Raleigh','Raleigh','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Randolph','Randolph','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Ritchie','Ritchie','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Roane','Roane','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Summers','Summers','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Taylor','Taylor','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Tucker','Tucker','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Tyler','Tyler','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Upshur','Upshur','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Wayne','Wayne','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Webster','Webster','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Wetzel','Wetzel','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Wirt','Wirt','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Wood','Wood','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WV,Wyoming','Wyoming','WV',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WY,Albany','Albany','WY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WY,Big Horn','Big Horn','WY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WY,Campbell','Campbell','WY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WY,Carbon','Carbon','WY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WY,Converse','Converse','WY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WY,Crook','Crook','WY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WY,Fremont','Fremont','WY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WY,Goshen','Goshen','WY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WY,Hot Springs','Hot Springs','WY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WY,Johnson','Johnson','WY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WY,Laramie','Laramie','WY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WY,Lincoln','Lincoln','WY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WY,Natrona','Natrona','WY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WY,Niobrara','Niobrara','WY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WY,Park','Park','WY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WY,Platte','Platte','WY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WY,Sheridan','Sheridan','WY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WY,Sublette','Sublette','WY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WY,Sweetwater','Sweetwater','WY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WY,Teton','Teton','WY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WY,Uinta','Uinta','WY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WY,Washakie','Washakie','WY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (291,'WY,Weston','Weston','WY',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01079','Futami','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0104','Hakodate (city)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0103','Otaru (city)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0102','Asahikawa (city)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0101','Sapporo (city)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'010106','Sapporo, Minami Ku','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01001','Akan','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01002','Ashoro','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01003','Atsukeshi','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01004','Atsuta (deleted)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01005','Abashiri','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01006','Abuta(Shiribeshi)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01007','Abuta(Iburi)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01008','Ishikari','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01064','Horoizumi','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01072','Yubari','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01065','Horobetsu (deleted)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01066','Mashike','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01067','Matsumae','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01068','Mitsuishi (deleted)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01069','Menashi','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'010103','Sapporo, Higashi Ku','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01071','Yamakoshi','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'010104','Sapporo, Shiroishi Ku','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01073','Yufutsu(Iburi)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01074','Yufutsu(Kamikawa)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01075','Yoichi','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01076','Rishiri','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01077','Rumoi','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0107','Obihiro (city)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01070','Mombetsu','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0130','Noboribetsu (city)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0105','Muroran (city)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0124','Chitose (city)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0125','Takikawa (city)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0126','Sunagawa (city)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0127','Utashinai (city)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0122','Mikasa (city)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0129','Furano (city)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0121','Nayoro (city)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0131','Eniwa (city)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0132','Kameda (city, deleted)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0133','Date (city)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0134','Kitahiroshima (city)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0135','Ishikari (city)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0136','Hokuto (city)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0128','Fukagawa (city)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0114','Wakkanai (city)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01078','Rebun','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0108','Kitami (city)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0109','Yubari (city)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0110','Iwamizawa (city)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0111','Abashiri (city)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0123','Nemuro (city)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0113','Tomakomai (city)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0106','Kushiro (city)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0115','Bibai (city)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0116','Ashibetsu (city)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0117','Ebetsu (city)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0118','Akabira (city)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0119','Mombetsu (city)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0120','Shibetsu (city)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0112','Rumoi (city)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01029','Sapporo (deleted)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01063','Furubira','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01021','Kamiiso','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01022','Kamikawa(Tokachi)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01023','Kamikawa(Kamikawa)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01024','Kameda','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01025','Kayabe','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01026','Kawakami','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01019','Kato','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01028','Kudo','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01018','Kasai','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01030','Samani','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01031','Saru','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01032','Shizunai (deleted)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01033','Shibetsu','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01034','Shimamaki','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01080','Hidaka','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01035','Shakotan','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01027','Kushiro','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'010101','Sapporo, Chuo Ku','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'010107','Sapporo, Nishi Ku','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'010108','Sapporo, Atsubetsu Ku','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'010109','Sapporo, Teine Ku','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'010105','Sapporo, Toyohira Ku','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'010110','Sapporo, Kiyota Ku','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01081','Uryu(Kamikawa)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01009','Isoya','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01020','Kabato','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'010102','Sapporo, Kita Ku','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01036','Shari','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01011','Usu','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01012','Utasutsu (deleted)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01013','Urakawa','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01014','Uryu(Sorachi)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01015','Esashi','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01016','Okushiri','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01017','Oshoro (deleted)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01010','Iwanai','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01056','Hanasaki (deleted)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01052','Niikappu','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01047','Tokachi','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01062','Furuu','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01061','Futoro (deleted)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01060','Hiroo','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01059','Hiyama','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01045','Teshio(Rumoi)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01057','Hamamasu (deleted)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01044','Chitose (deleted)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01055','Notsuke','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01054','Nemuro (deleted)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01053','Nishi','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01049','Tomamae','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01050','Nakagawa(Kamikawa)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01051','Nakagawa(Tokachi)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01058','Bikuni (deleted)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01037','Shiraoi','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01043','Sorachi(Kamikawa)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01042','Sorachi(Sorachi)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01041','Soya','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01040','Setana','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01039','Suttsu','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01038','Shiranuka','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01048','Tokoro','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'01046','Teshio(Soya)','#01',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0210','Hirakawa (city)','#02',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0209','Tsugaru (city)','#02',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0208','Mutsu (city)','#02',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0207','Misawa (city)','#02',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0206','Towada (city)','#02',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0205','Goshogawara (city)','#02',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0204','Kuroishi (city)','#02',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0202','Hirosaki (city)','#02',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0201','Aomori (city)','#02',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0203','Hachinohe (city)','#02',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'02008','Minamitsugaru','#02',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'02001','Kamikita','#02',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'02002','Kitatsugaru','#02',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'02003','Sannohe','#02',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'02004','Shimokita','#02',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'02005','Nakatsugaru','#02',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'02006','Nishitsugaru','#02',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'02007','Higashitsugaru','#02',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0304','Ichinoseki (city)','#03',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0313','Ninohe (city)','#03',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0312','Esashi (city, deleted)','#03',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0311','Rikuzentakata (city)','#03',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0310','Tono (city)','#03',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0309','Kuji (city)','#03',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0308','Kitakami (city)','#03',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0307','Hanamaki (city)','#03',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0303','Miyako (city)','#03',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0315','Oshu (city)','#03',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'03009','Nishiiwai','#03',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0314','Hachimantai (city)','#03',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0305','Ofunato (city)','#03',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0306','Mizusawa (city, deleted)','#03',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'03007','Shimohei','#03',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0302','Kamaishi (city)','#03',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'03010','Ninohe','#03',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'03012','Higashiiwai (deleted)','#03',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'03011','Hienuki (deleted)','#03',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'03008','Shiwa','#03',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'03006','Kesen','#03',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'03005','Kunohe','#03',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'03004','Kamihei','#03',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'03003','Esashi (deleted)','#03',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'03002','Iwate','#03',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'03001','Isawa','#03',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0301','Morioka (city)','#03',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'03013','Waga','#03',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0406','Oga (city)','#04',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0414','Nikaho (city)','#04',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0415','Senboku (city)','#04',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0413','Kitaakita (city)','#04',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0411','Katagami (city)','#04',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0410','Yurihonjo (city)','#04',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'04001','Ogachi','#04',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'04009','Yuri (deleted)','#04',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'04008','Yamamoto','#04',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'04007','Minamiakita','#04',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'04006','Hiraka (deleted)','#04',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'04005','Semboku','#04',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'04004','Kitaakita','#04',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0408','Omagari (city, deleted)','#04',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'04002','Kaduno','#04',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0409','Kazuno (city)','#04',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0401','Akita (city)','#04',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0402','Noshiro (city)','#04',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0403','Odate (city)','#04',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0404','Yokote (city)','#04',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0405','Honjo (city, deleted)','#04',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0407','Yuzawa (city)','#04',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'04003','Kawabe (deleted)','#04',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0412','Daisen (city)','#04',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0506','Sagae (city)','#05',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0501','Yamagata (city)','#05',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0513','Nan''yo (city)','#05',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0502','Yonezawa (city)','#05',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0503','Tsuruoka (city)','#05',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0505','Shinjo (city)','#05',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'05011','Mogami','#05',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'05010','Minamimurayama (deleted)','#05',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'05009','Minamiokitama (deleted)','#05',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'05008','Higashimurayama','#05',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'05007','Higashitagawa','#05',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0508','Murayama (city)','#05',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0511','Higashine (city)','#05',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0504','Sakata (city)','#05',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'05006','Higashiokitama','#05',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0509','Nagai (city)','#05',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0507','Kaminoyama (city)','#05',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0512','Obanazawa (city)','#05',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'05001','Akumi','#05',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'05002','Kitamurayama','#05',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'05003','Nishiokitama','#05',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'05004','Nishitagawa (deleted)','#05',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'05005','Nishimurayama','#05',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0510','Tendo (city)','#05',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0602','Ishinomaki (city)','#06',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0609','Tagajo (city)','#06',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0612','Tome (city)','#06',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0611','Iwanuma (city)','#06',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0610','Izumi (city, deleted)','#06',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0608','Kakuda (city)','#06',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0607','Natori (city)','#06',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0606','Shiroishi (city)','#06',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0605','Kesennuma (city)','#06',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0603','Shiogama (city)','#06',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0601','Sendai (city)','#06',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'06013','Miyagi','#06',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0604','Furukawa (city, deleted)','#06',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'06007','Shida (deleted)','#06',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0616','Tomiya (city)','#06',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'060101','Sendai, Aoba Ku','#06',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'060102','Sendai, Miyagino Ku','#06',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'060103','Sendai, Wakabayashi Ku','#06',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'060104','Sendai, Taihaku Ku','#06',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'060105','Sendai, Izumi Ku','#06',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'06001','Igu','#06',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'06002','Oshika','#06',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'06003','Katta','#06',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'06004','Kami','#06',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'06015','Monou (deleted)','#06',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'06006','Kurokawa','#06',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0613','Kuruhara (city)','#06',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'06008','Shibata','#06',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'06009','Tamadukuri (deleted)','#06',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'06010','Toda','#06',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'06011','Tome (deleted)','#06',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'06012','Natori (deleted)','#06',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'06014','Motoyoshi','#06',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'06016','Watari','#06',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0615','Osaki (city)','#06',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0614','Higashimatsushima (city)','#06',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'06005','Kurihara (deleted)','#06',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0716','Wakamatsu (city, deleted)','#07',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0711','Soma (city)','#07',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0719','Date (city)','#07',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0718','Minamisoma (city)','#07',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0717','Tamura (city)','#07',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0715','Iwaki (city)','#07',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0703','Koriyama (city)','#07',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0714','Nihonmatsu (city)','#07',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0712','Uchigo (city, deleted)','#07',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0704','Taira (city, deleted)','#07',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0710','Iwaki (city, deleted)','#07',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0709','Joban (city, deleted)','#07',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0708','Kitakata (city)','#07',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0707','Sukagawa (city)','#07',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0706','Haramachi (city, deleted)','#07',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0705','Shirakawa (city)','#07',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0713','Nakoso (city, deleted)','#07',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'07012','Tamura','#07',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'07001','Asaka (deleted)','#07',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'07002','Adachi','#07',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'07003','Ishikawa','#07',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'07005','Iwase','#07',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'07007','Kawanuma','#07',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'07008','Kitaaidu (deleted)','#07',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'07009','Shinobu (deleted)','#07',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'07017','Yama','#07',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'07011','Date','#07',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0702','Aizuwakamatsu (city)','#07',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'07013','Nishishirakawa','#07',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'07014','Higashishirakawa','#07',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'07015','Futaba','#07',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'07006','Onuma','#07',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'07016','Minamiaidu','#07',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0720','Motomiya (city)','#07',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0701','Fukushima (city)','#07',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'07010','Soma','#07',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'07004','Ishiki (deleted)','#07',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0802','Nagaoka (city)','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0810','Tokamachi (city)','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0809','Kamo (city)','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0808','Ojiya (city)','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0807','Niitsu (city), deleted','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0806','Shibata (city)','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0805','Kashiwazaki (city)','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'080108','Niigata, Nishikan Ku','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0803','Takada (city, deleted)','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0813','Tsubame (city)','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0801','Niigata (city)','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'080102','Niigata, Higashi Ku','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'080104','Niigata, Konan Ku','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'080105','Niigata, Akiha Ku','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'080106','Niigata, Minami Ku','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'080107','Niigata, Nishi Ku','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0804','Sanjo (city)','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0820','Shirone (city, deleted)','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0828','Tainai (city)','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0827','Myoko (city)','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0826','Minamiuonuma (city)','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0825','Uonuma (city)','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0824','Sado (city)','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0823','Agano (city)','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0811','Mitsuke (city)','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0821','Toyosaka (city, deleted)','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0812','Murakami (city)','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0819','Ryotsu (city, deleted)','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0818','Gosen (city, deleted)','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0817','Arai (city, deleted)','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0816','Itoigawa (city, deleted)','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0815','Tochio (city, deleted)','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0814','Naoetsu (city, deleted)','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'080103','Niigata, Chuo Ku','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0822','Joetsu (city)','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'08007','Santo','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'08015','Minamiuonuma','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'08014','Higashikubiki (deleted)','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'08012','Nishikubiki (deleted)','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'080101','Niigata, Kita Ku','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'08011','Nishikambara','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'08010','Nakakubiki (deleted)','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'08016','Minamikambara','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'08008','Nakauonuma','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'08013','Higashikambara','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'08006','Sado (deleted)','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'08005','Koshi (deleted)','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'08004','Kitakambara','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'08003','Kitauonuma (deleted)','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'08002','Kariwa','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'08001','Iwafune','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'08009','Nakakambara (deleted)','#08',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0909','Ina (city)','#09',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'09014','Hanishina','#09',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'09013','(Reserved)','#09',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'09006','Kitasaku','#09',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'09011','Suwa','#09',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'09009','Shimotakai','#09',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'09008','Shimoina','#09',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0901','Nagano (city)','#09',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0902','Matsumoto (city)','#09',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0903','Ueda (city)','#09',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0904','Okaya (city)','#09',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0905','Iida (city)','#09',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0906','Suwa (city)','#09',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'09015','Higashichikuma','#09',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0908','Komoro (city)','#09',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'09012','Chiisagata','#09',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0910','Komagane (city)','#09',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0911','Nakano (city)','#09',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0912','Omachi (city)','#09',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0913','Iiyama (city)','#09',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0914','Chino (city)','#09',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0915','Shiojiri (city)','#09',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0916','Shinonoi (city, deleted)','#09',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0917','Koshoku (city, deleted)','#09',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0918','Saku (city)','#09',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0919','Chikuma (city)','#09',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0920','Tomi (city)','#09',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0921','Azumino (city)','#09',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'0907','Suzaka (city)','#09',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'09005','Kitaazumi','#09',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'09016','Minamiazumi (deleted)','#09',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'09001','Kamiina','#09',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'09002','Kamitakai','#09',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'09003','Kamiminochi','#09',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'09010','Shimominochi','#09',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'09004','Kiso','#09',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'09007','Sarashina (deleted)','#09',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'09017','Minamisaku','#09',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1014','Higashimurayama (city)','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1015','Kokubunji (city)','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1019','Fussa (city)','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1013','Hino (city)','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1012','Kodaira (city)','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1011','Koganei (city)','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'10003','Minamitama (deleted)','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1010','Machida (city)','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1009','Chofu (city)','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1017','Hoya (city, deleted)','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1016','Kunitachi (city)','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1025','Tama (city)','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'10002','Nishitama','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'10004','Oshima','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'10005','Miyake','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1030','Nishitokyo (city)','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1029','Akiruno (city)','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1028','Hamura (city)','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'10007','Ogawasara','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1026','Inagi (city)','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1018','Tanashi (city, deleted)','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1024','Musashimurayama (city)','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1023','Higashikurume (city)','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1022','Kiyose (city)','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1021','Higashiyamato (city)','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1020','Komae (city)','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'10006','Hachijo','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1027','Akigawa (city, deleted)','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'100114','Tokyo, Nakano','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'100103','Tokyo, Minato','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'100104','Tokyo, Shinjuku','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'100106','Tokyo, Taito','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'100107','Tokyo, Sumida','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'100108','Tokyo, Koto','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'100109','Tokyo, Shinagawa','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'100110','Tokyo, Meguro','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'100111','Tokyo, Ota','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'100102','Tokyo, Chuo','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'100113','Tokyo, Shibuya','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'100105','Tokyo, Bunkyo','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'100115','Tokyo, Suginami','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'100116','Tokyo, Toshima','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'100117','Tokyo, Kita','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'100118','Tokyo, Arakawa','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'100119','Tokyo, Itabashi','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'100120','Tokyo, Nerima','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'100121','Tokyo, Adachi','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'100122','Tokyo, Katsushika','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'100123','Tokyo, Edogawa','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'100112','Tokyo, Setagaya','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1008','Akishima (city)','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1001','Tokyo 23-wards (city, deleted)','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1002','Hachioji (city)','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1003','Tachikawa (city)','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1004','Musashino (city)','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1005','Mitaka (city)','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1006','Ome (city)','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'10001','Kitatama (deleted)','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1007','Fuchu (city)','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'100101','Tokyo, Chiyoda','#10',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'110301','Kawasaki, Kawasaki Ku','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'110304','Kawasaki, Takatsu Ku','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'110306','Kawasaki, Miyamae Ku','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'110302','Kawasaki, Saiwai Ku','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'110118','Yokohama, Tsuzuki Ku','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'110117','Yokohama, Aoba Ku','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'110116','Yokohama, Izumi Ku','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'110115','Yokohama, Sakae Ku','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'110114','Yokohama, Seya Ku','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'110113','Yokohama, Midori Ku','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'110112','Yokohama, Asahi Ku','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'111001','Sagamihara, Midori Ku','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'110303','Kawasaki, Nakahara Ku','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'111002','Sagamihara, Chuou Ku','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1105','Kamakura (city)','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1104','Hiratsuka (city)','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1103','Kawasaki (city)','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1102','Yokosuka (city)','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1101','Yokohama (city)','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'110307','Kawasaki, Asao Ku','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1107','Odawara (city)','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1109','Zushi (city)','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1119','Ayase (city)','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1110','Sagamihara (city)','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1118','Minamiashigara (city)','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'110305','Kawasaki, Tama Ku','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1111','Miura (city)','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1112','Hadano (city)','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1113','Atsugi (city)','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1114','Yamato (city)','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1115','Isehara (city)','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1116','Ebina (city)','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1117','Zama (city)','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1106','Fujisawa (city)','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'11003','Ashigarashimo','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'110111','Yokohama, Konan Ku','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1108','Chigasaki (city)','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'11007','Miura','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'11006','Naka','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'11004','Koza','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'11002','Ahigarakami','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'11001','Aiko','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'110101','Yokohama, Tsurumi Ku','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'110102','Yokohama, Kanagawa Ku','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'110107','Yokohama, Isogo Ku','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'110104','Yokohama, Naka Ku','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'110105','Yokohama, Minami Ku','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'110109','Yokohama, Kohoku Ku','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'11005','Tsukui (deleted)','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'110106','Yokohama, Hodogaya Ku','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'110103','Yokohama, Nishi Ku','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'110110','Yokohama, Totsuka Ku','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'111003','Sagamihara, Minami Ku','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'110108','Yokohama, Kanazawa Ku','#11',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1221','Yachiyo (city)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1219','Ichihara (city)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1218','Katsuura (city)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1223','Kamogawa (city)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1216','Narashino (city)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1229','Sodegaura (city)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1215','Asahi (city)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1214','Yokaichiba (city, deleted)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1213','Togane (city)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1212','Sakura (city)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1211','Narita (city)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1210','Mobara (city)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1217','Kashiwa (city)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1230','Yachimata (city)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1237','Sanmu (city)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1236','Katori (city)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1235','Sosa (city)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1234','Minamiboso (city)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1233','Tomisato (city)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1227','Urayasu (city)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1231','Inzai (city)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1224','Kimitsu (city)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1220','Nagareyama (city)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1209','Sawara (city, deleted)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1238','Isumi (city)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1228','Yotsukaido (city)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1226','Futtu (city)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1225','Kamagaya (city)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1232','Shiroi (city)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'120103','Chiba, Inage Ku','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1222','Abiko (city)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1208','Noda (city)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'120102','Chiba, Hanamigawa Ku','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'120104','Chiba, Wakaba Ku','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'120105','Chiba, Midori Ku','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'120106','Chiba, Mihama Ku','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'12001','Awa','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'12002','Isumi','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'12003','Ichihara (deleted)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'12004','Inba','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'12005','Kaijo (deleted)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'12006','Katori','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'12007','Kimitsu (deleted)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1202','Choshi (city)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'120101','Chiba, Chuo Ku','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1206','Kisarazu (city)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1207','Matsudo (city)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'12008','Sambu','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1203','Ichikawa (city)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1205','Tateyama (city)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1201','Chiba (city)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1239','Oamishirasato (city)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'12012','Higashikatsushika (deleted)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'12011','Chosei','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'12010','Chiba (deleted)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'12009','Sosa (deleted)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1204','Funabashi (city)','#12',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1333','Kitamoto (city)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1332','Kuki (city)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1331','Okegawa (city)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1330','Niiza (city)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1329','Wako (city)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1328','Shiki (city)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1334','Yashio (city)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1326','Hatogaya (city, deleted)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1340','Satte (city)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1325','Iruma (city)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1327','Asaka (city)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1335','Kamifukuoka (city, deleted)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1336','Fujimi (city)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1337','Misato (city)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1339','Sakado (city)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1341','Tsurugashima (city)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1342','Hidaka (city)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1343','Yoshikawa (city)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1344','Saitama (city)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1324','Toda (city)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1306','Gyoda (city)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1338','Hasuda (city)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1311','Honjo (city)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1318','Fukaya (city)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1345','Fujimino (city)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1346','Shiraoka (city)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1301','Urawa (city, deleted)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1302','Kawagoe (city)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1303','Kumagaya (city)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1304','Kawaguchi (city)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1305','Omiya (city, deleted)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1307','Chichibu (city)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1308','Tokorozawa (city)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1310','Kazo (city)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1323','Warabi (city)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1312','Higashimatsuyama (city)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1313','Iwatsuki (city, deleted)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1314','Kasukabe (city)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1315','Sayama (city)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1317','Konosu (city)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1319','Ageo (city)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1320','Yono (city, deleted)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1321','Soka (city)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1322','Koshigaya (city)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1309','Hanno (city)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'13009','Minamisaitama','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'134403','Saitama, Omiya Ku','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'134404','Saitama, Minuma Ku','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'134402','Saitama, Kita Ku','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'134405','Saitama, Chuo Ku','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'134406','Saitama, Sakura Ku','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'134407','Saitama, Urawa Ku','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'134410','Saitama, Iwatsuki Ku','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'134408','Saitama, Minami Ku','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1316','Hanyu (city)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'13008','Hiki','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'13007','Chichibu','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'13006','Kodama','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'13005','Kitasaitama (deleted)','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'13004','Kitakatsushika','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'13003','Kitaadachi','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'13002','Osato','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'13001','Iruma','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'134401','Saitama, Nishi Ku','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'134409','Saitama, Midori Ku','#13',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'14006','Taga (deleted)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'14013','Makabe (deleted)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'14014','Yuki','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'14012','Higashiibaraki','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'14011','Nishiibaraki (deleted)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'14010','Niihari (deleted)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'14009','Namegata (deleted)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'14007','Tsukuba (deleted)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1435','Joso (city)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'14005','Sashima','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'14004','Kuji','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'14003','Kitasoma','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'14002','Kashima (deleted)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'14001','Inashiki','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'14008','Naka','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1419','Ushiku (city)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1437','Omitama (city)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1412','Hitachiota (city)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1413','Katsuta (city, deleted)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1414','Takahagi (city)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1415','Kitaibaraki (city)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1416','Kasama (city)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1410','Shimotsuma (city)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1418','Iwai (city, deleted)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1409','Nakaminato (city, deleted)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1420','Tsukuba (city)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1421','Hitachinaka (city)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1422','Kashima (city)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1423','Itako (city)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1424','Moriya (city)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1425','Hitachiomiya (city)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1426','Naka (city)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1417','Toride (city)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1401','Mito (city)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1427','Chikusei (city)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1434','Hokota (city)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1433','Namegata (city)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1432','Kamisu (city)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1431','Sakuragawa (city)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1430','Kasumigaura (city)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1411','Mitsukaido (city, deleted)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1428','Bandou (city)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1436','Tsukubamirai (city)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1402','Hitachi (city)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1403','Tsuchiura (city)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1404','Koga (city)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1405','Ishioka (city)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1406','Shimodate (city, deleted)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1407','Yuki (city)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1408','Ryugasaki (city)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1429','Inashiki (city)','#14',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'15001','Ashikaga (deleted)','#15',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'15002','Aso (deleted)','#15',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'15003','Kamitsuga (deleted)','#15',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'15004','Kawachi','#15',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'15005','Shioya','#15',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'15008','Haga','#15',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'15007','Nasu','#15',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'15006','Shimotsuga','#15',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1516','Shimotsuke (city)','#15',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1503','Tochigi (city)','#15',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1504','Sano (city)','#15',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1505','Kanuma (city)','#15',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1506','Nikko (city)','#15',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1507','Imaichi (city, deleted)','#15',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1508','Oyama (city)','#15',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1509','Mooka (city)','#15',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1510','Otawara (city)','#15',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1511','Yaita (city)','#15',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1512','Kuroiso (city, deleted)','#15',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1513','Nasushiobara (city)','#15',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1514','Sakura (city)','#15',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1501','Utsunomiya (city)','#15',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1515','Nasukarasuyama (city)','#15',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1502','Ashikaga (city)','#15',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'16003','Ora','#16',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1602','Takasaki (city)','#16',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1603','Kiryu (city)','#16',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1604','Isesaki (city)','#16',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1605','Ota (city)','#16',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1606','Numata (city)','#16',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1607','Tatebayashi (city)','#16',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1608','Shibukawa (city)','#16',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1609','Fujioka (city)','#16',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1610','Tomioka (city)','#16',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1611','Annaka (city)','#16',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1612','Midori (city)','#16',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1601','Maebashi (city)','#16',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'16010','Tone','#16',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'16001','Agatsuma (deleted)','#16',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'16011','Nitta (deleted)','#16',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'16009','Tano','#16',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'16008','Seta (deleted)','#16',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'16007','Sawa','#16',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'16006','Gumma (deleted)','#16',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'16005','Kitagumma','#16',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'16004','Kanra','#16',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'16002','Usui (deleted)','#16',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'16012','Yamada (deleted)','#16',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'17003','Nakakoma','#17',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1707','Nirasaki (city)','#17',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1701','Kofu (city)','#17',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1702','Fujiyoshida (city)','#17',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1703','Enzan (city, deleted)','#17',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1704','Tsuru (city)','#17',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1705','Yamanashi (city)','#17',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1706','Otsuki (city)','#17',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'17008','Minamitsuru','#17',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'17007','Minamikoma','#17',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'17006','Higashiyamanashi (deleted)','#17',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1709','Hokuto (city)','#17',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'17004','Nishiyatsushiro','#17',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1710','Kai (city)','#17',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'17002','Kitatsuru','#17',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'17001','Kitakoma (deleted)','#17',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'17005','Higashiyatsushiro (deleted)','#17',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1711','Fuehuki (city)','#17',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1713','Koshu (city)','#17',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1708','Minami-Alps (city)','#17',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1712','Uenohara (city)','#17',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1714','Chuo (city)','#17',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1808','Ito (city)','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'18004','Iwata (deleted)','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'18003','Ihara (deleted)','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'18002','Inasa (deleted)','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'18001','Abe (deleted)','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1809','Shimada (city)','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'18007','Shida (deleted)','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1807','Fujinomiya (city)','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1806','Mishima (city)','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1805','Atami (city)','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1804','Shimizu (city, deleted)','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1803','Numadu (city)','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1802','Hamamatsu (city)','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1810','Yoshiwara (city, deleted)','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'18012','Hamana (deleted)','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'180209','Hamamatsu, Hamana Ku','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'18013','Fuji (deleted)','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'180208','Hamamatsu, Chuo Ku','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1819','Hamakita (city, deleted)','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1818','Tenryu (city, deleted)','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1817','Fukuroi (city)','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1816','Gotemba (city)','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'18005','Ogasa (deleted)','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1815','Fujieda (city)','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'18006','Kamo','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'18011','Haibara','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'18010','Tagata','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1814','Kakegawa (city)','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1813','Fuji (city)','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1812','Yaidu (city)','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'18009','Sunto','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'18008','Shuchi','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1820','Shimoda (city)','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'180201','Hamamatsu, Naka Ku (deleted)','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1801','Shizuoka (city)','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1811','Iwata (city)','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1827','Makinohara (city)','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1826','Izunokuni (city)','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1825','Kikugawa (city)','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1824','Omaezaki (city)','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1823','Izu (city)','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1821','Susono (city)','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'180202','Hamamatsu, Higashi Ku (deleted)','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'180203','Hamamatsu, Nishi Ku (deleted)','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'180204','Hamamatsu, Minami Ku (deleted)','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'180205','Hamamatsu, Kita Ku (deleted)','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'180206','Hamamatsu, Hamakita Ku (deleted)','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'180101','Shizuoka, Aoi Ku','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'180103','Shizuoka, Shimizu Ku','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'180102','Shizuoka, Suruga Ku','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1822','Kosai (city)','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'180207','Hamamatsu, Tenryu Ku','#18',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'19014','Mugi (deleted)','#19',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'19018','Yoshiki (deleted)','#19',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1920','Gero (city)','#19',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'19017','Yoro','#19',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'19016','Yamagata (deleted)','#19',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1919','Gujo (city)','#19',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'19001','Ampachi','#19',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'19002','Inaba (deleted)','#19',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'19003','Ibi','#19',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'19004','Ena (deleted)','#19',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'19012','Fuwa','#19',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'19005','Ono','#19',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'19011','Hashima','#19',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1918','Motosu (city)','#19',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'19010','Toki (deleted)','#19',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1921','Kaizu (city)','#19',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'19009','Gujo (deleted)','#19',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'19008','Kamo','#19',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'19007','Kani','#19',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'19006','Kaizu (deleted)','#19',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'19013','Mashita (deleted)','#19',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1912','Toki (city)','#19',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1909','Hashima (city)','#19',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1908','Mizunami (city)','#19',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1907','Mino (city)','#19',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1906','Nakatsugawa (city)','#19',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1904','Tajimi (city)','#19',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1917','Hida (city)','#19',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1905','Seki (city)','#19',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1901','Gifu (city)','#19',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1915','Yamagata (city)','#19',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1911','Minokamo (city)','#19',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1916','Mizuho (city)','#19',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1913','Kakamigahara (city)','#19',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1903','Takayama (city)','#19',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1914','Kani (city)','#19',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'19015','Motosu','#19',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1902','Ogaki (city)','#19',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'1910','Ena (city)','#19',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'20016','Hoi (deleted)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'20017','Minamishitara (deleted)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'20018','Yana (deleted)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2038','Miyoshi (city)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2039','Ama (city)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2017','Tokoname (city)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2019','Konan (city)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2029','Owariasahi (city)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2020','Bisai (city, deleted)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2021','Komaki (city)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'200116','Nagoya, Tenpaku Ku','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'200115','Nagoya, Meito Ku','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'200114','Nagoya, Midori Ku','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2018','Moriyama (city, deleted)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2030','Iwakura (city)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'20015','Hekikai (deleted)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2025','Obu (city)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2026','Chita (city)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2027','Takahama (city)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2028','Chiryu (city)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2024','Tokai (city)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2031','Toyoake (city)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2022','Inazawa (city)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'200113','Nagoya, Moriyama Ku','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2032','Nissin (city)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2033','Tahara (city)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2034','Aisai (city)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2035','Kiyosu (city)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2036','Kitanagoya (city)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2037','Yatomi (city)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'200112','Nagoya, Minami Ku','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2023','Shinshiro (city)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2001','Nagoya (city)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'20014','Higashikamo (deleted)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2014','Nishio (city)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2002','Toyohashi (city)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2012','Toyota (city)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2011','Kariya (city)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2010','Hekinan (city)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2009','Tsushima (city)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2008','Toyokawa (city)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2007','Kasugai (city)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2006','Handa (city)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2005','Seto (city)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2016','Inuyama (city)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2003','Okazaki (city)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2015','Gamagori (city)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'200101','Nagoya, Chikusa Ku','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'200102','Nagoya, Higashi Ku','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'200103','Nagoya, Kita Ku','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'200104','Nagoya, Nishi Ku','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'200105','Nagoya, Nakamura Ku','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'200106','Nagoya, Naka Ku','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'200107','Nagoya, Showa Ku','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'200108','Nagoya, Mizuho Ku','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'200109','Nagoya, Atsuta Ku','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'200110','Nagoya, Nakagawa Ku','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'200111','Nagoya, Minato Ku','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2004','Ichinomiya (city)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'20001','Aichi','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'20006','Nakashima (deleted)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'20005','Chita','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'20007','Nishikasugai','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'20004','Kitashitara','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'20008','Nishikamo (deleted)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'20009','Niwa','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'20010','Nukata','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2013','Anjo (city)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'20002','Atsumi (deleted)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2040','Nagakute (city)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'20013','Higashikasugai (deleted)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'20012','Hazu (deleted)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'20011','Haguri (deleted)','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'20003','Ama','#20',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'21014','Mie','#21',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2116','Shima (city)','#21',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2117','Iga (city)','#21',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'21001','Age (deleted)','#21',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'21015','Minamimuro','#21',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'21002','Ano (deleted)','#21',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'21004','Iinan (deleted)','#21',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'21003','Ayama (deleted)','#21',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'21013','Naga (deleted)','#21',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'21012','Taki','#21',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2113','Hisai (city, deleted)','#21',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'21011','Suzuka (deleted)','#21',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'21016','Watarai','#21',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'21010','Shima (deleted)','#21',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2109','Owase (city)','#21',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2108','Nabari (city)','#21',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2107','Suzuka (city)','#21',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2106','Ueno (city, deleted)','#21',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2105','Kuwana (city)','#21',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2104','Matsusaka (city)','#21',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2103','Ise (city)','#21',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2102','Yokkaichi (city)','#21',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2101','Tsu (city)','#21',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2115','Inabe (city)','#21',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2112','Kumano (city)','#21',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2114','Ujiyamada (city, deleted)','#21',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'21009','Kuwana','#21',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'21008','Kitamuro','#21',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'21007','Kawage (deleted)','#21',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'21006','Inabe','#21',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'21005','Ichishi (deleted)','#21',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2110','Kameyama (city)','#21',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2111','Toba (city)','#21',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'22009','Takeno (deleted)','#22',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'22008','Soraku','#22',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'22010','Tsuduki','#22',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'22007','Kumano (deleted)','#22',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'22012','Funai','#22',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'22013','Minamikuwada (deleted)','#22',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'22011','Naka (deleted)','#22',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2201','Kyoto (city)','#22',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2202','Fukuchiyama (city)','#22',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2203','Maiduru (city)','#22',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2204','Ayabe (city)','#22',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2205','Uji (city)','#22',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'22014','Yoza','#22',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2207','Kameoka (city)','#22',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2206','Miyazu (city)','#22',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2209','Nagaokakyo (city)','#22',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2210','Muko (city)','#22',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2211','Yawata (city)','#22',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2212','Kyotanabe (city)','#22',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2213','Kyotango (city)','#22',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2214','Nantan (city)','#22',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2215','Kizugawa (city)','#22',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'220108','Kyoto, Ukyo Ku','#22',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'22006','Kuze','#22',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'220106','Kyoto, Shimogyo Ku','#22',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'22004','Kasa (deleted)','#22',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'22003','Otokuni','#22',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'22002','Ikaruga (deleted)','#22',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'22001','Amada (deleted)','#22',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'220109','Kyoto, Fushimi Ku','#22',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'220110','Kyoto, Yamashina Ku','#22',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'220111','Kyoto, Nishikyo Ku','#22',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'220101','Kyoto, Kita Ku','#22',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'220102','Kyoto, Kamigyo Ku','#22',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'220103','Kyoto, Sakyo Ku','#22',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2208','Joyo (city)','#22',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'220105','Kyoto, Higashiyama Ku','#22',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'220107','Kyoto, Minami Ku','#22',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'22005','Kitakuwada (deleted)','#22',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'220104','Kyoto, Nakagyo Ku','#22',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2301','Otsu (city)','#23',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2305','Yokaichi (city, deleted)','#23',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2306','Kusatsu (city)','#23',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2307','Moriyama (city)','#23',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2308','Ritto (city)','#23',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2309','Koka (city)','#23',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2310','Yasu (city)','#23',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2311','Konan (city)','#23',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2312','Takashima (city)','#23',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2313','Higashioumi (city)','#23',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2314','Maibara (city)','#23',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2304','Omihachiman (city)','#23',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'23004','Gamou','#23',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'23007','Koka (deleted)','#23',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2303','Nagahama (city)','#23',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2302','Hikone (city)','#23',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'23005','Kanzaki (deleted)','#23',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'23003','Echi','#23',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'23002','Inukamo','#23',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'23001','Ika (deleted)','#23',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'23009','Shiga (deleted)','#23',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'23010','Takashima (deleted)','#23',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'23011','Higashiazai (deleted)','#23',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'23012','Yasu (deleted)','#23',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'23008','Sakata (deleted)','#23',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'23006','Kurita (deleted)','#23',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2401','Nara (city)','#24',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2409','Ikoma (city)','#24',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'24002','Uda','#24',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2412','Uda (city)','#24',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2411','Katsuragi (city)','#24',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2410','Kashiba (city)','#24',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2408','Gose (city)','#24',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2407','Gojo (city)','#24',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2406','Sakurai (city)','#24',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2405','Kashihara (city)','#24',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2404','Tenri (city)','#24',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2402','Yamatotakada (city)','#24',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2403','Yamatokoriyama (city)','#24',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'24004','Kitakatsuragi','#24',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'24001','Ikoma','#24',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'24010','Yoshino','#24',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'24009','Yamabe','#24',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'24008','Minamikatsuragi (deleted)','#24',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'24003','Uchi (deleted)','#24',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'24005','Shiki','#24',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'24007','Takaichi','#24',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'24006','Soekami (deleted)','#24',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'250122','Osaka, Nishinari Ku','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'25007','Minamikawachi','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'250117','Osaka, Asahi Ku','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'250118','Osaka, Joto Ku','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'250119','Osaka, Abeno Ku','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'25006','Mishima','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'250121','Osaka, Higashisumiyoshi Ku','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'25001','Kitakawachi (deleted)','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'250101','Osaka, Kita Ku','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'250207','Sakai, Mihara Ku','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'250206','Sakai, Kita Ku','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'250205','Sakai, Minami Ku','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'250204','Sakai, Nishi Ku','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'250203','Sakai, Higashi Ku','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'250202','Sakai, Naka Ku','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'250120','Osaka, Sumiyoshi Ku','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'250106','Osaka, Nishi Ku','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'250115','Osaka, Higashinari Ku','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'250114','Osaka, Higashiyodogawa Ku','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'250113','Osaka, Nishiyodogawa Ku','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'250112','Osaka, Oyodo (deleted)','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'250111','Osaka, Naniwa Ku','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'250110','Osaka, Minami Ku (deleted)','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'250109','Osaka, Tennoji Ku','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'25003','Senboku','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'250107','Osaka, Minato Ku','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'25005','Nakakawachi (deleted)','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'250105','Osaka, Higashi Ku (deleted)','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'250104','Osaka, Konohana Ku','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'250103','Osaka, Fukushima Ku','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'250102','Osaka, Miyakojima Ku','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'25002','Sennan','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'250116','Osaka, Ikuno Ku','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'25004','Toyono','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'250108','Osaka, Taisho Ku','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2506','Ikeda (city)','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2516','Tondabayashi (city)','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2515','Izumisano (city)','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2514','Yao (city)','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2513','Ibaraki (city)','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2512','Hirakata (city)','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2511','Moriguchi (city)','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2510','Kaiduka (city)','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2509','Takatsuki (city)','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'250201','Sakai, Sakai Ku','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2507','Suita (city)','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2519','Hiraoka (city, deleted)','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2505','Fuse (city, deleted)','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2504','Toyonaka (city)','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2503','Kishiwada (city)','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2502','Sakai (city)','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2501','Osaka (city)','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'250125','Osaka, Suminoe Ku','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'250126','Osaka, Hirano Ku','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'250127','Osaka, Chuo Ku','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2508','Izumiotsu (city)','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2528','Settsu (city)','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'250124','Osaka, Tsurumi Ku','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2536','Hannan (city)','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2535','Osakasayama (city)','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2534','Katano (city)','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2533','Shijonawate (city)','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2532','Sennan (city)','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2531','Higashiosaka (city)','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2517','Neyagawa (city)','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2529','Fujiidera (city)','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2518','Kawachinagano (city)','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2527','Kadoma (city)','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2526','Habikino (city)','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2525','Kashiwara (city)','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2524','Mino (city)','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2523','Izumi (city)','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2522','Daito (city)','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2521','Matsubara (city)','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2520','Kawachi (city, deleted)','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'250123','Osaka, Yodogawa Ku','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2530','Takaishi (city)','#25',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2605','Gobo (city)','#26',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2602','Shingu (city)','#26',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'26007','Hidaka','#26',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'26006','Higashimuro','#26',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'26005','Nishimuro','#26',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2607','Arida (city)','#26',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2601','Wakayama (city)','#26',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'26001','Arida','#26',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2603','Kainan (city)','#26',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2604','Tanabe (city)','#26',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2606','Hashimoto (city)','#26',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2608','Kinokawa (city)','#26',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2609','Iwade (city)','#26',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'26003','Kaiso','#26',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'26002','Ito','#26',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'26004','Naga (deleted)','#26',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2724','Tanba (city)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2729','Kato (city)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2727','Awaji (city)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2726','Asago (city)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2725','Minamiawaji (city)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2730','Tatsuno (city)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'270108','Kobe, Chuo Ku','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'270109','Kobe, Nishi Ku','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'270110','Kobe, Fukiai Ku (deleted)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'270111','Kobe, Ikuta Ku (deleted)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2701','Kobe (city)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2702','Himeji (city)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2703','Amagasaki (city)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2704','Akashi (city)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2705','Nishinomiya (city)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2706','Sumoto (city)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2707','Ashiya (city)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2708','Itami (city)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2712','Tatsuno (city, deleted)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2710','Toyooka (city)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2723','Yabu (city)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2711','Kakogawa (city)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'27001','Akou','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2713','Ako (city)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2714','Nishiwaki (city)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2715','Takaraduka (city)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2716','Miki (city)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2717','Takasago (city)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2718','Kawanishi (city)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2719','Ono (city)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2720','Sanda (city)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2721','Kasai (city)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2722','Sasayama (city, deleted)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2709','Aioi (city)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'27018','Tsuna (deleted)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'27010','Kawabe','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'27011','Kanzaki','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'27012','Kinosaki (deleted)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'27013','Sayo','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2728','Shiso (city)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'27021','Mino (deleted)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'27002','Asago (deleted)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'27009','Kato (deleted)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'27017','Taki (deleted)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'27014','Shikama (deleted)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'27019','Hikami (deleted)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'27008','Kasai (deleted)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2731','Tanbasasayama (city)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'27020','Mikata','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'27024','Yabu (deleted)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'27023','Muko (deleted)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'27022','Mihara (deleted)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'27016','Taka','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'27005','Ibo','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'270105','Kobe, Suma Ku','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'270107','Kobe, Kita Ku','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'270104','Kobe, Nagata Ku','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'270103','Kobe, Hyogo Ku','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'270102','Kobe, Nada Ku','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'27003','Arima (deleted)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'270101','Kobe, Higashinada Ku','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'27004','Izushi (deleted)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'27007','Kako','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'27006','Innami (deleted)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'27015','Shiso (deleted)','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'270106','Kobe, Tarumi Ku','#27',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'28007','Himi (deleted)','#28',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'28006','Nei (deleted)','#28',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'28005','Nishitonami (deleted)','#28',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'28004','Nakaniikawa','#28',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'28003','Shimoniikawa','#28',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'28001','Imizu (deleted)','#28',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2801','Toyama (city)','#28',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'28002','Kaminiikawa (deleted)','#28',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'28008','Higashitonami (deleted)','#28',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2806','Namerikawa (city)','#28',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2805','Himi (city)','#28',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2802','Takaoka (city)','#28',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2803','Shinminato (city, deleted)','#28',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2804','Uodu (city)','#28',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2808','Tonami (city)','#28',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2809','Oyabe (city)','#28',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2810','Nanto (city)','#28',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2811','Imizu (city)','#28',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2807','Kurobe (city)','#28',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'29004','Ono (deleted)','#29',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2910','Sakai (city)','#29',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2901','Fukui (city)','#29',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2902','Tsuruga (city)','#29',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2903','Takefu (city, deleted)','#29',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2904','Obama (city)','#29',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2905','Ono (city)','#29',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2906','Katsuyama (city)','#29',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2907','Sabae (city)','#29',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'29002','Imadate','#29',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2909','Echizen (city)','#29',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'29001','Asuwa (deleted)','#29',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'29012','Mikatakaminaka','#29',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'29011','Yoshida','#29',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'29010','Mikata','#29',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'29008','Nanjo','#29',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'29006','Sakai (deleted)','#29',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'29005','Onyu (deleted)','#29',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'29007','Tsuruga (deleted)','#29',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'29003','Oi','#29',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'2908','Awara (city)','#29',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'29009','Nyuu','#29',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3004','Wajima (city)','#30',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'30001','Ishikawa (deleted)','#30',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'30002','Enuma (deleted)','#30',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'30004','Kahoku','#30',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'30005','Suzu (deleted)','#30',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'30008','Fugeshi (deleted)','#30',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'30009','Housu','#30',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3001','Kanazawa (city)','#30',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'30006','Nomi','#30',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3003','Komatsu (city)','#30',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'30007','Hakui','#30',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3005','Suzu (city)','#30',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3006','Kaga (city)','#30',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3007','Hakui (city)','#30',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3008','Matsuto (city, deleted)','#30',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3009','Kahoku (city)','#30',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3010','Hakusan (city)','#30',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3011','Nomi (city)','#30',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3002','Nanao (city)','#30',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3012','Nonoichi (city)','#30',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'30003','Kashima','#30',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3103','Tsuyama (city)','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3102','Kurashiki (city)','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3101','Okayama (city)','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'31006','Oda','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'31007','Katsuta','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'31001','Aida','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'31009','Kibi (deleted)','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3106','Tamashima (city, deleted)','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'31010','Kume','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'31011','Kojima (deleted)','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'31013','Jodo (deleted)','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'31014','Jobo (deleted)','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'31015','Tsukubo','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'31008','Kawakami (deleted)','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3105','Kojima (city, deleted)','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'31004','Atetsu (deleted)','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'31005','Oku (deleted)','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3118','Asakuchi (city)','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3117','Mimasaka (city)','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3116','Maniwa (city)','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3115','Akaiwa (city)','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3114','Setouchi (city)','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3113','Bizen (city)','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3112','Niimi (city)','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3111','Takahashi (city)','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3110','Soja (city)','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3109','Ibara (city)','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3108','saidaiji (city, deleted)','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3104','Tamano (city)','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'310104','Okayama, Minami Ku','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'31012','Shitsuki (deleted)','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'310101','Okayama, Kita Ku','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'31016','Tomada','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'31003','Asakuchi','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'31002','Akaiwa (deleted)','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'310102','Okayama, Naka Ku','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'310103','Okayama, Higashi Ku','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3107','Kasaoka (city)','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'31017','Maniwa','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'31018','Mitsu (deleted)','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'31019','Wake','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'31020','Kaga','#31',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3203','Izumo (city)','#32',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'32014','Nogi (deleted)','#32',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'32013','Nima','#32',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'32010','Chibu (deleted)','#32',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'32001','Ano (deleted)','#32',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'32015','Hikawa (deleted)','#32',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'32011','Naka (deleted)','#32',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'32012','Nita','#32',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3201','Matsue (city)','#32',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3202','Hamada (city)','#32',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'32002','Ama (deleted)','#32',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3204','Masuda (city)','#32',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3205','Oda (city)','#32',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3206','Yasugi (city)','#32',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3208','Hirata (city, deleted)','#32',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'32009','Suki (deleted)','#32',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'32017','Yatsuka (deleted)','#32',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'32003','Iishi','#32',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'32004','Ochi','#32',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'32005','Ohara (deleted)','#32',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'32006','Oki','#32',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'32007','Ochi (deleted)','#32',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'32008','Kanoashi','#32',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3209','Unnan (city)','#32',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'32016','Mino (deleted)','#32',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3207','Gotsu (city)','#32',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3314','Shinnan''yo (city, deleted)','#33',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'33010','Mine (deleted)','#33',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'33001','Asa (deleted)','#33',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3301','Yamaguchi (city)','#33',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'33002','Abu','#33',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3302','Shimonoseki (city)','#33',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'33003','Oshima','#33',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'33009','Toyoura (deleted)','#33',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'33004','Otsu (deleted)','#33',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'33005','Kuga','#33',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'33006','Kumage','#33',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'33007','Saba (deleted)','#33',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'33011','Yoshiki (deleted)','#33',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3315','Shunan (city)','#33',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3313','Mine (city)','#33',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3312','Yanai (city)','#33',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3311','Nagato (city)','#33',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3310','Hikari (city)','#33',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3309','Onoda (city, deleted)','#33',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3308','Iwakuni (city)','#33',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3307','Kudamatsu (city)','#33',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3306','Hofu (city)','#33',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3305','Tokuyama (city, deleted)','#33',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3304','Hagi (city)','#33',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3303','Ube (city)','#33',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'33008','Tsuno (deleted)','#33',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3316','San''yoonoda (city)','#33',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3403','Yonago (city)','#34',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'34001','Iwami','#34',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'34002','Ketaka (deleted)','#34',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'34003','Saihaku','#34',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'34004','Tohaku','#34',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3404','Sakaiminato (city)','#34',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3402','Kurayoshi (city)','#34',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3401','Tottori (city)','#34',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'34006','Yazu','#34',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'34005','Hino','#34',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'350103','Hiroshima, Minami Ku','#35',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'350108','Hiroshima, Saeki Ku','#35',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'35001','Aki (deleted)','#35',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'350107','Hiroshima, Aki Ku','#35',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'350106','Hiroshima, Asakita Ku','#35',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'350104','Hiroshima, Nishi Ku','#35',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'350102','Hiroshima, Higashi Ku','#35',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'350101','Hiroshima, Naka Ku','#35',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'350105','Hiroshima, Asaminami Ku','#35',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'35011','Numakuma (deleted)','#35',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'35003','Ashina (deleted)','#35',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3516','Etajima (city)','#35',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3515','Akitakata (city)','#35',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3514','Hatsukaichi (city)','#35',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3513','Higashihiroshima (city)','#35',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'35016','Yamagata (deleted)','#35',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'35015','Mitsugi (deleted)','#35',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'35014','Futami (deleted)','#35',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'35012','Hiba (deleted)','#35',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'35010','Toyota','#35',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'35009','Takata (deleted)','#35',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'35008','Sera','#35',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'35007','Jinseki','#35',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'35006','Saeki (deleted)','#35',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3507','Matsunaga (city, deleted)','#35',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3504','Mihara (city)','#35',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3505','Onomichi (city)','#35',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3502','Kure (city)','#35',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3501','Hiroshima (city)','#35',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'35013','Fukayasu (deleted)','#35',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'35005','Konu (deleted)','#35',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3503','Takehara (city)','#35',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3506','Innoshima (city, deleted)','#35',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3508','Fukuyama (city)','#35',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3509','Fuchu (city)','#35',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3510','Miyoshi (city)','#35',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3511','Syoubara (city)','#35',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3512','Otake (city)','#35',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'35002','Asa (deleted)','#35',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'35004','Kamo (deleted)','#35',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'36003','Kagawa','#36',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3606','Sanuki (city)','#36',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3604','Zentsuji (city)','#36',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3607','Higashikagawa (city)','#36',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3602','Marugame (city)','#36',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3605','Kan''onji (city)','#36',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'36007','Mitoyo (deleted)','#36',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'36006','Nakatado','#36',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'36004','Kita','#36',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'36002','Okawa (deleted)','#36',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'36001','Ayauta','#36',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3608','Mitoyo (city)','#36',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3603','Sakaide (city)','#36',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'36005','Syozu','#36',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3601','Takamatsu (city)','#36',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3704','Anan (city)','#37',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'37010','Miyoshi','#37',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3701','Tokushima (city)','#37',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3703','Komatsushima (city)','#37',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3705','Yoshinogawa (city)','#37',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3706','Awa (city)','#37',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3707','Mima (city)','#37',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3708','Miyoshi (city)','#37',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'37001','Awa (deleted)','#37',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'37008','Myodo','#37',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'37007','Myozai','#37',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'37006','Naka','#37',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'37005','Katsuura','#37',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'37004','Kaifu','#37',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'37003','Oe (deleted)','#37',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'37002','Itano','#37',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'37009','Mima','#37',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3702','Naruto (city)','#37',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'38011','Higashiuwa (deleted)','#38',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'38010','Nishiuwa','#38',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'38009','Nii','#38',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'38008','Shuso (deleted)','#38',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'38007','Kitauwa','#38',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'38006','Kita','#38',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'38005','Kamiukena','#38',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'38004','Onsen (deleted)','#38',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'38002','Uma (deleted)','#38',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'38012','Minamiuwa','#38',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'38003','Ochi','#38',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3810','Iyo (city)','#38',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3807','Ozu (city)','#38',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3801','Matsuyama (city)','#38',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3811','Hojo (city, deleted)','#38',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3812','Toyo (city, deleted)','#38',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3813','Shikokuchuo (city)','#38',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3814','Seiyo (city)','#38',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3815','Toon (city)','#38',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3803','Uwajima (city)','#38',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3804','Yawatahama (city)','#38',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3802','Imabari (city)','#38',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3806','Saijo (city)','#38',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3808','Iyomishima (city, deleted)','#38',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3809','Kawanoe (city, deleted)','#38',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'38001','Iyo','#38',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3805','Niihama (city)','#38',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3909','Nankoku (city)','#39',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3907','Sukumo (city)','#39',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3908','Tosashimizu (city)','#39',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'39006','Nagaoka','#39',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3910','Shimanto (city)','#39',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3912','Kami (city)','#39',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'39001','Agawa','#39',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3911','Konan (city)','#39',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'39004','Takaoka','#39',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'39002','Aki','#39',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'39005','Tosa','#39',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'39007','Hata','#39',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3906','Nakamura (city, deleted)','#39',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3905','Susaki (city)','#39',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3904','Tosa (city)','#39',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3903','Aki (city)','#39',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3902','Muroto (city)','#39',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'3901','Kochi (city)','#39',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'39003','Kami (deleted)','#39',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4021','Kitakyushu (city)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4016','Chikugo (city)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4017','Okawa (city)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4018','Yukuhashi (city)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4019','Buzen (city)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4020','Nakama (city)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4022','Ogoori (city)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4023','Kasuga (city)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4024','Chikushino (city)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4025','Onojo (city)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'40001','Asakura','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4027','Dazaifu (city)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4008','Omuta (city)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4026','Munakata (city)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4015','Yame (city)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4014','Yamada (city, deleted)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4013','Amagi (city, deleted)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4012','Yanagawa (city)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4011','Tagawa (city)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4009','Noogata (city)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4028','Maebaru (city, deleted)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4007','Kurume (city)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4006','Wakamatsu (city, deleted)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4005','Tobata (city, deleted)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4004','Yahata (city, deleted)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4003','Moji (city, deleted)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4002','Kokura (city, deleted)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4001','Fukuoka (city, deleted)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4010','Iizuka (city)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'40012','Mii','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'40005','Kasuya','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'40007','Kurate','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'40009','Tagawa','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'40010','Chikushi (deleted)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4037','Nakagawa (city)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4032','Miyawaka (city)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4033','Kama (city)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4034','Asakura (city)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'40004','Onga','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'40011','Chikujo','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'40006','Kaho','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'40013','Miike (deleted)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'40014','Mizuma','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'40015','Miyako','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'40016','Munakata (deleted)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'40017','Yamato (deleted)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'40018','Yame','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'40003','Ukiha (deleted)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'400101','Fukuoka, Higashi Ku','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4035','Miyama (city)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'402103','Kitakyushu, Tobata Ku','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'400102','Fukuoka, Hakata Ku','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'400103','Fukuoka, Chuo Ku','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'400104','Fukuoka, Minami Ku','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'400105','Fukuoka, Nishi Ku','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'400106','Fukuoka, Jonan Ku','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'400107','Fukuoka, Sawara Ku','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'40008','Sawara (deleted)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'402102','Kitakyushu, Wakamatsui Ku','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'40002','Itoshima (deleted)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'402104','Kitakyushu, Kokurakitai Ku','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4031','Ukiha (city)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'402106','Kitakyushu, Yahatahigashii Ku','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'402107','Kitakyushu, Yahatanishii Ku','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'402108','Kitakyushu, Yahata Ku (deleted)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'402109','Kitakyushu, Kokura Ku (deleted)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4029','Koga (city)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4030','Fukutsu (city)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'402105','Kitakyushu, Kokuraminamii Ku','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4036','Itoshima (city)','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'402101','Kitakyushu, Mojii Ku','#40',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'41006','Higashimatsuura','#41',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'41007','Fujitsu','#41',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'41004','Saga (deleted)','#41',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'41003','Kishima','#41',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'41002','Kanzaki','#41',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'41008','Miyaki','#41',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'41001','Ogi (deleted)','#41',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4108','Ogi (city)','#41',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4109','Ureshino (city)','#41',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4107','Kashima (city)','#41',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4106','Takeo (city)','#41',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4105','Imari (city)','#41',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4104','Taku (city)','#41',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4103','Tosu (city)','#41',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4102','Karatsu (city)','#41',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'41005','Nishimatsuura','#41',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4101','Saga (city)','#41',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4110','Kanzaki (city)','#41',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4202','Sasebo (city)','#42',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'42008','Minamitakaki (deleted)','#42',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'42007','Higashisonogi','#42',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'42006','Nishisonogi','#42',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'42005','Shimoagata (deleted)','#42',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'42004','Kitamatsuura','#42',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'42003','Kitatakaki (deleted)','#42',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'42002','Kamiagata (deleted)','#42',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'42001','Iki (deleted)','#42',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4206','Fukue (city, deleted)','#42',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4205','Omura (city)','#42',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4203','Shimabara (city)','#42',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4201','Nagasaki (city)','#42',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'42009','Minamimatsuura','#42',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4214','Minamishimabara (city)','#42',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4208','Matsuura (city)','#42',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4213','Unzen (city)','#42',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4212','Saikai (city)','#42',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4211','Goto (city)','#42',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4209','Tsushima (city)','#42',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4207','Hirado (city)','#42',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4204','Isahaya (city)','#42',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4210','Iki (city)','#42',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4307','Hondo (city, deleted)','#43',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'430104','Minami','#43',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'430103','Nishi','#43',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'430102','Higashi','#43',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'430101','Chuo','#43',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4301','Kumamoto (city)','#43',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4302','Yatsushiro (city)','#43',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4303','Hitoyoshi (city)','#43',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4304','Arao (city)','#43',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'430105','Kita','#43',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4306','Tamana (city)','#43',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4311','Uto (city)','#43',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4308','Yamaga (city, deleted)','#43',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4309','Ushibuka (city, deleted)','#43',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4310','Kikuchi (city)','#43',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4312','Kamiamakusa (city)','#43',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4314','Aso (city)','#43',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4315','Amakusa (city)','#43',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4316','Koshi (city)','#43',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4305','Minamata (city)','#43',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'43008','Kuma','#43',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'43012','Yatsushiro','#43',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'43011','Hotaku (deleted)','#43',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'43010','Tamana','#43',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'43001','Ashikita','#43',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'43009','Shimomashiki','#43',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'43007','Kikuchi','#43',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'43006','Kamoto (deleted)','#43',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'43005','Kamimashiki','#43',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'43004','Uto (deleted)','#43',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'43003','Amakusa','#43',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'43002','Aso','#43',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4313','Uki (city)','#43',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4401','Oita (city)','#44',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'44004','Kitaamabe (deleted)','#44',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'44001','Usa (deleted)','#44',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4408','Taketa (city)','#44',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4415','Kunisaki (city)','#44',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4414','Yufu (city)','#44',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4413','Bungoono (city)','#44',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4412','Usa (city)','#44',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4411','Kitsuki (city)','#44',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'44002','Oita (deleted)','#44',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4409','Tsurusaki (city, deleted)','#44',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'44003','Ono (deleted)','#44',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4406','Usuki (city)','#44',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'44010','Higashikunisaki','#44',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4404','Hita (city)','#44',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4403','Nakatsu (city)','#44',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4402','Beppu (city)','#44',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4405','Saiki (city)','#44',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4410','Bungotakada (city)','#44',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'44005','kusu','#44',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'44009','Hayami','#44',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'44008','Nishikunisaki (deleted)','#44',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'44006','Shimoge (deleted)','#44',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'44012','Minamiamabe (deleted)','#44',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'44011','Hita (deleted)','#44',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4407','Tsukumi (city)','#44',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'44007','Naoiri (deleted)','#44',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'45007','Minaminaka (deleted)','#45',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'45006','Higashimorokata','#45',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'45005','Higashiusuki','#45',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'45004','Nishimorokata','#45',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'45003','Nishiusuki','#45',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'45002','Koyu','#45',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4509','Ebino (city)','#45',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4503','Nobeoka (city)','#45',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'45008','Miyazaki (deleted)','#45',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4507','Kushima (city)','#45',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4506','Hyuga (city)','#45',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4505','Kobayashi (city)','#45',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4504','Nichinan (city)','#45',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4501','Miyazaki (city)','#45',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4502','Miyakonojo (city)','#45',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'45001','Kitamorokata','#45',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4508','Saito (city)','#45',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4614','Nishinoomote (city)','#46',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4604','Makurazaki (city)','#46',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4605','Kushikino (city, deleted)','#46',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4606','Akune (city)','#46',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4607','Izumi (city)','#46',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4608','Naze (city, deleted)','#46',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'46001','Aira (deleted)','#46',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4610','Ibusuki (city)','#46',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4603','Kanoya (city)','#46',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4613','Taniyama (city, deleted)','#46',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4609','Oguchi (city, deleted)','#46',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4615','Tarumizu (city)','#46',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4616','Satsumasendai (city)','#46',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4617','Hioki (city)','#46',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4618','Soo (city)','#46',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4619','Kirishima (city)','#46',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4620','Ichikikushikino (city)','#46',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4621','Minamisatsuma (city)','#46',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4622','Shibushi (city)','#46',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4623','Amami (city)','#46',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4612','Kokubu (city, deleted)','#46',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'46005','Oshima','#46',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'46002','Isa (deleted)','#46',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4611','Kaseda (city, deleted)','#46',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'46004','Ibusuki (deleted)','#46',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4602','Sendai (city, deleted)','#46',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'46006','Kagoshima','#46',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'46007','Kawanabe (deleted)','#46',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'46008','Kimotsuki','#46',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'46009','Kumage','#46',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4626','Aira (city)','#46',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4625','Isa (city)','#46',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4601','Kagoshima (city)','#46',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'46010','Satsuma','#46',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'46003','Izumi','#46',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'46011','Soo','#46',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'46012','Hioki (deleted)','#46',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4624','Minamikyushu (city)','#46',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4708','Nago (city)','#47',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4709','Urasoe (city)','#47',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4710','Itoman (city)','#47',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4707','Gushikawa (city, deleted)','#47',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4712','Tomigusuku (city)','#47',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4703','Hirara (city, deleted)','#47',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4713','Uruma (city)','#47',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4714','Miyakojima (city)','#47',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4715','Nanjo (city)','#47',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4711','Okinawa (city)','#47',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4706','Ginowan (city)','#47',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4704','Ishigaki (city)','#47',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4702','Ishikawa (city, deleted)','#47',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4701','Naha (city)','#47',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'47002','Shimajiri','#47',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'47003','Nakagami','#47',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'47004','Miyako','#47',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'47001','Kunigami','#47',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'47005','Yaeyama','#47',''); INSERT INTO adif_enum_secondary_subdivision VALUES (339,'4705','Koza (city, deleted)','#47',''); ================================================ FILE: res/sql/migration_037.sql ================================================ UPDATE modes SET submodes = '["FSQCALL", "FST4", "FST4W", "FT2", "FT4", "JS8", "JTMS", "MFSK4", "MFSK8", "MFSK11", "MFSK16", "MFSK22", "MFSK31", "MFSK32", "MFSK64", "MFSK64L", "MFSK128", "MFSK128L", "Q65"]' WHERE name = 'MFSK'; UPDATE modes SET submodes = '["VARA HF", "VARA SATELLITE", "VARA FM 1200", "VARA FM 9600", "FREEDATA"]' WHERE name = 'DYNAMIC'; INSERT INTO modes (name, submodes, rprt, dxcc, enabled) VALUES ('OFDM', '["RIBBIT_PIX", "RIBBIT_SMS"]', NULL, 'DIGITAL', 0); ================================================ FILE: res/sql/migration_038.sql ================================================ ALTER TABLE rig_profiles ADD get_split INTEGER DEFAULT 0; CREATE TABLE IF NOT EXISTS cabrillo_templates ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL UNIQUE, contest_name_for_header TEXT, default_category_mode TEXT ); CREATE TABLE IF NOT EXISTS cabrillo_template_columns ( id INTEGER PRIMARY KEY AUTOINCREMENT, template_id INTEGER NOT NULL REFERENCES cabrillo_templates(id) ON DELETE CASCADE, position INTEGER NOT NULL, db_field TEXT NOT NULL, width INTEGER NOT NULL, formatter TEXT, label TEXT ); DELETE FROM cabrillo_templates WHERE name = 'ARRL DX CW'; INSERT INTO cabrillo_templates (name, contest_name_for_header, default_category_mode) VALUES ('ARRL DX CW', 'ARRL-DX-CW', 'CW'); INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 1, 'freq', 5, 'freq_khz', 'Freq' FROM cabrillo_templates WHERE name = 'ARRL DX CW'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 2, 'mode', 2, 'mode_cabrillo', 'Mo' FROM cabrillo_templates WHERE name = 'ARRL DX CW'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 3, 'start_time', 10, 'date_yyyy_mm_dd', 'Date' FROM cabrillo_templates WHERE name = 'ARRL DX CW'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 4, 'start_time', 4, 'time_hhmm', 'Time' FROM cabrillo_templates WHERE name = 'ARRL DX CW'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 5, 'station_callsign', 13, 'upper', 'My Call' FROM cabrillo_templates WHERE name = 'ARRL DX CW'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 6, 'rst_sent', 3, '', 'Rst Sent' FROM cabrillo_templates WHERE name = 'ARRL DX CW'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 7, 'stx_string', 6, '', 'Exch Sent' FROM cabrillo_templates WHERE name = 'ARRL DX CW'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 8, 'callsign', 13, 'upper', 'Call Rcvd' FROM cabrillo_templates WHERE name = 'ARRL DX CW'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 9, 'rst_rcvd', 3, '', 'Rst Rcvd' FROM cabrillo_templates WHERE name = 'ARRL DX CW'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 10, 'srx_string', 6, '', 'Exch Rcvd' FROM cabrillo_templates WHERE name = 'ARRL DX CW'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 11, '', 1, 'transmitter_id', 't' FROM cabrillo_templates WHERE name = 'ARRL DX CW'; DELETE FROM cabrillo_templates WHERE name = 'ARRL DX SSB'; INSERT INTO cabrillo_templates (name, contest_name_for_header, default_category_mode) VALUES ('ARRL DX SSB', 'ARRL-DX-SSB', 'SSB'); INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 1, 'freq', 5, 'freq_khz', 'Freq' FROM cabrillo_templates WHERE name = 'ARRL DX SSB'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 2, 'mode', 2, 'mode_cabrillo', 'Mo' FROM cabrillo_templates WHERE name = 'ARRL DX SSB'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 3, 'start_time', 10, 'date_yyyy_mm_dd', 'Date' FROM cabrillo_templates WHERE name = 'ARRL DX SSB'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 4, 'start_time', 4, 'time_hhmm', 'Time' FROM cabrillo_templates WHERE name = 'ARRL DX SSB'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 5, 'station_callsign', 13, 'upper', 'My Call' FROM cabrillo_templates WHERE name = 'ARRL DX SSB'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 6, 'rst_sent', 3, '', 'Rst Sent' FROM cabrillo_templates WHERE name = 'ARRL DX SSB'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 7, 'stx_string', 6, '', 'Exch Sent' FROM cabrillo_templates WHERE name = 'ARRL DX SSB'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 8, 'callsign', 13, 'upper', 'Call Rcvd' FROM cabrillo_templates WHERE name = 'ARRL DX SSB'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 9, 'rst_rcvd', 3, '', 'Rst Rcvd' FROM cabrillo_templates WHERE name = 'ARRL DX SSB'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 10, 'srx_string', 6, '', 'Exch Rcvd' FROM cabrillo_templates WHERE name = 'ARRL DX SSB'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 11, '', 1, 'transmitter_id', 't' FROM cabrillo_templates WHERE name = 'ARRL DX SSB'; DELETE FROM cabrillo_templates WHERE name = 'ARRL VHF JAN'; INSERT INTO cabrillo_templates (name, contest_name_for_header, default_category_mode) VALUES ('ARRL VHF JAN', 'ARRL-VHF-JAN', 'MIXED'); INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 1, 'freq', 5, 'freq_khz', 'Freq' FROM cabrillo_templates WHERE name = 'ARRL VHF JAN'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 2, 'mode', 2, 'mode_cabrillo', 'Mo' FROM cabrillo_templates WHERE name = 'ARRL VHF JAN'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 3, 'start_time', 10, 'date_yyyy_mm_dd', 'Date' FROM cabrillo_templates WHERE name = 'ARRL VHF JAN'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 4, 'start_time', 4, 'time_hhmm', 'Time' FROM cabrillo_templates WHERE name = 'ARRL VHF JAN'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 5, 'station_callsign', 17, 'upper', 'My Call' FROM cabrillo_templates WHERE name = 'ARRL VHF JAN'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 6, 'my_gridsquare', 6, 'upper', 'Grid Sent' FROM cabrillo_templates WHERE name = 'ARRL VHF JAN'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 7, 'callsign', 17, 'upper', 'Call Rcvd' FROM cabrillo_templates WHERE name = 'ARRL VHF JAN'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 8, 'gridsquare', 6, 'upper', 'Grid Rcvd' FROM cabrillo_templates WHERE name = 'ARRL VHF JAN'; DELETE FROM cabrillo_templates WHERE name = 'CQ VHF'; INSERT INTO cabrillo_templates (name, contest_name_for_header, default_category_mode) VALUES ('CQ VHF', 'CQ-VHF', 'MIXED'); INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 1, 'freq', 5, 'freq_khz', 'Freq' FROM cabrillo_templates WHERE name = 'CQ VHF'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 2, 'mode', 2, 'mode_cabrillo', 'Mo' FROM cabrillo_templates WHERE name = 'CQ VHF'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 3, 'start_time', 10, 'date_yyyy_mm_dd', 'Date' FROM cabrillo_templates WHERE name = 'CQ VHF'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 4, 'start_time', 4, 'time_hhmm', 'Time' FROM cabrillo_templates WHERE name = 'CQ VHF'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 5, 'station_callsign', 17, 'upper', 'My Call' FROM cabrillo_templates WHERE name = 'CQ VHF'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 6, 'my_gridsquare', 6, 'upper', 'Grid Sent' FROM cabrillo_templates WHERE name = 'CQ VHF'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 7, 'callsign', 17, 'upper', 'Call Rcvd' FROM cabrillo_templates WHERE name = 'CQ VHF'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 8, 'gridsquare', 6, 'upper', 'Grid Rcvd' FROM cabrillo_templates WHERE name = 'CQ VHF'; DELETE FROM cabrillo_templates WHERE name = 'CQ WPX CW'; INSERT INTO cabrillo_templates (name, contest_name_for_header, default_category_mode) VALUES ('CQ WPX CW', 'CQ-WPX-CW', 'CW'); INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 1, 'freq', 5, 'freq_khz', 'Freq' FROM cabrillo_templates WHERE name = 'CQ WPX CW'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 2, 'mode', 2, 'mode_cabrillo', 'Mo' FROM cabrillo_templates WHERE name = 'CQ WPX CW'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 3, 'start_time', 10, 'date_yyyy_mm_dd', 'Date' FROM cabrillo_templates WHERE name = 'CQ WPX CW'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 4, 'start_time', 4, 'time_hhmm', 'Time' FROM cabrillo_templates WHERE name = 'CQ WPX CW'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 5, 'station_callsign', 13, 'upper', 'My Call' FROM cabrillo_templates WHERE name = 'CQ WPX CW'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 6, 'rst_sent', 3, '', 'Rst Sent' FROM cabrillo_templates WHERE name = 'CQ WPX CW'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 7, 'stx', 6, 'padded_nr', 'Exch Sent' FROM cabrillo_templates WHERE name = 'CQ WPX CW'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 8, 'callsign', 13, 'upper', 'Call Rcvd' FROM cabrillo_templates WHERE name = 'CQ WPX CW'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 9, 'rst_rcvd', 3, '', 'Rst Rcvd' FROM cabrillo_templates WHERE name = 'CQ WPX CW'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 10, 'srx', 6, 'padded_nr', 'Exch Rcvd' FROM cabrillo_templates WHERE name = 'CQ WPX CW'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 11, '', 1, 'transmitter_id', 't' FROM cabrillo_templates WHERE name = 'CQ WPX CW'; DELETE FROM cabrillo_templates WHERE name = 'CQ WPX SSB'; INSERT INTO cabrillo_templates (name, contest_name_for_header, default_category_mode) VALUES ('CQ WPX SSB', 'CQ-WPX-SSB', 'SSB'); INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 1, 'freq', 5, 'freq_khz', 'Freq' FROM cabrillo_templates WHERE name = 'CQ WPX SSB'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 2, 'mode', 2, 'mode_cabrillo', 'Mo' FROM cabrillo_templates WHERE name = 'CQ WPX SSB'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 3, 'start_time', 10, 'date_yyyy_mm_dd', 'Date' FROM cabrillo_templates WHERE name = 'CQ WPX SSB'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 4, 'start_time', 4, 'time_hhmm', 'Time' FROM cabrillo_templates WHERE name = 'CQ WPX SSB'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 5, 'station_callsign', 13, 'upper', 'My Call' FROM cabrillo_templates WHERE name = 'CQ WPX SSB'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 6, 'rst_sent', 3, '', 'Rst Sent' FROM cabrillo_templates WHERE name = 'CQ WPX SSB'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 7, 'stx', 6, 'padded_nr', 'Exch Sent' FROM cabrillo_templates WHERE name = 'CQ WPX SSB'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 8, 'callsign', 13, 'upper', 'Call Rcvd' FROM cabrillo_templates WHERE name = 'CQ WPX SSB'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 9, 'rst_rcvd', 3, '', 'Rst Rcvd' FROM cabrillo_templates WHERE name = 'CQ WPX SSB'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 10, 'srx', 6, 'padded_nr', 'Exch Rcvd' FROM cabrillo_templates WHERE name = 'CQ WPX SSB'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 11, '', 1, 'transmitter_id', 't' FROM cabrillo_templates WHERE name = 'CQ WPX SSB'; DELETE FROM cabrillo_templates WHERE name = 'CQ WW CW'; INSERT INTO cabrillo_templates (name, contest_name_for_header, default_category_mode) VALUES ('CQ WW CW', 'CQ-WW-CW', 'CW'); INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 1, 'freq', 5, 'freq_khz', 'Freq' FROM cabrillo_templates WHERE name = 'CQ WW CW'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 2, 'mode', 2, 'mode_cabrillo', 'Mo' FROM cabrillo_templates WHERE name = 'CQ WW CW'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 3, 'start_time', 10, 'date_yyyy_mm_dd', 'Date' FROM cabrillo_templates WHERE name = 'CQ WW CW'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 4, 'start_time', 4, 'time_hhmm', 'Time' FROM cabrillo_templates WHERE name = 'CQ WW CW'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 5, 'station_callsign', 13, 'upper', 'My Call' FROM cabrillo_templates WHERE name = 'CQ WW CW'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 6, 'rst_sent', 3, '', 'Rst Sent' FROM cabrillo_templates WHERE name = 'CQ WW CW'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 7, 'my_cq_zone', 6, '', 'Exch Sent' FROM cabrillo_templates WHERE name = 'CQ WW CW'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 8, 'callsign', 13, 'upper', 'Call Rcvd' FROM cabrillo_templates WHERE name = 'CQ WW CW'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 9, 'rst_rcvd', 3, '', 'Rst Rcvd' FROM cabrillo_templates WHERE name = 'CQ WW CW'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 10, 'cqz', 6, '', 'Exch Rcvd' FROM cabrillo_templates WHERE name = 'CQ WW CW'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 11, '', 1, 'transmitter_id', 't' FROM cabrillo_templates WHERE name = 'CQ WW CW'; DELETE FROM cabrillo_templates WHERE name = 'CQ WW SSB'; INSERT INTO cabrillo_templates (name, contest_name_for_header, default_category_mode) VALUES ('CQ WW SSB', 'CQ-WW-SSB', 'SSB'); INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 1, 'freq', 5, 'freq_khz', 'Freq' FROM cabrillo_templates WHERE name = 'CQ WW SSB'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 2, 'mode', 2, 'mode_cabrillo', 'Mo' FROM cabrillo_templates WHERE name = 'CQ WW SSB'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 3, 'start_time', 10, 'date_yyyy_mm_dd', 'Date' FROM cabrillo_templates WHERE name = 'CQ WW SSB'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 4, 'start_time', 4, 'time_hhmm', 'Time' FROM cabrillo_templates WHERE name = 'CQ WW SSB'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 5, 'station_callsign', 13, 'upper', 'My Call' FROM cabrillo_templates WHERE name = 'CQ WW SSB'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 6, 'rst_sent', 3, '', 'Rst Sent' FROM cabrillo_templates WHERE name = 'CQ WW SSB'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 7, 'my_cq_zone', 6, '', 'Exch Sent' FROM cabrillo_templates WHERE name = 'CQ WW SSB'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 8, 'callsign', 13, 'upper', 'Call Rcvd' FROM cabrillo_templates WHERE name = 'CQ WW SSB'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 9, 'rst_rcvd', 3, '', 'Rst Rcvd' FROM cabrillo_templates WHERE name = 'CQ WW SSB'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 10, 'cqz', 6, '', 'Exch Rcvd' FROM cabrillo_templates WHERE name = 'CQ WW SSB'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 11, '', 1, 'transmitter_id', 't' FROM cabrillo_templates WHERE name = 'CQ WW SSB'; DELETE FROM cabrillo_templates WHERE name = 'IARU HF'; INSERT INTO cabrillo_templates (name, contest_name_for_header, default_category_mode) VALUES ('IARU HF', 'IARU-HF', 'MIXED'); INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 1, 'freq', 5, 'freq_khz', 'Freq' FROM cabrillo_templates WHERE name = 'IARU HF'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 2, 'mode', 2, 'mode_cabrillo', 'Mo' FROM cabrillo_templates WHERE name = 'IARU HF'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 3, 'start_time', 10, 'date_yyyy_mm_dd', 'Date' FROM cabrillo_templates WHERE name = 'IARU HF'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 4, 'start_time', 4, 'time_hhmm', 'Time' FROM cabrillo_templates WHERE name = 'IARU HF'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 5, 'station_callsign', 13, 'upper', 'My Call' FROM cabrillo_templates WHERE name = 'IARU HF'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 6, 'rst_sent', 3, '', 'Rst Sent' FROM cabrillo_templates WHERE name = 'IARU HF'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 7, 'stx_string', 6, '', 'Exch Sent' FROM cabrillo_templates WHERE name = 'IARU HF'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 8, 'callsign', 13, 'upper', 'Call Rcvd' FROM cabrillo_templates WHERE name = 'IARU HF'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 9, 'rst_rcvd', 3, '', 'Rst Rcvd' FROM cabrillo_templates WHERE name = 'IARU HF'; INSERT INTO cabrillo_template_columns (template_id, position, db_field, width, formatter, label) SELECT id, 10, 'srx_string', 6, '', 'Exch Rcvd' FROM cabrillo_templates WHERE name = 'IARU HF'; ================================================ FILE: res/stylesheet.css ================================================ QLineEdit#callsignEdit { text-transform: uppercase; } QLineEdit#stationCallsignEdit{ text-transform: uppercase; } QLineEdit#stationOperatorCallsignEdit{ text-transform: uppercase; } QLineEdit#stationDarcDokEdit{ text-transform: uppercase; } QLineEdit#stationDarcDokEdit{ text-transform: uppercase; } QLineEdit#myDOKEdit{ text-transform: uppercase; } QLineEdit#dokEdit{ text-transform: uppercase; } QLineEdit#myCallsignEdit{ text-transform: uppercase; } QLineEdit#myOperatorCallsignEdit{ text-transform: uppercase; } QLineEdit#gridEdit { text-transform: uppercase; } QLabel#chatUnread { padding: 2px; min-height: 10px; border-radius: 10px; background: orange; } QLabel#chatValuableMsg { padding: 2px; min-height: 10px; border-radius: 10px; background: red; } QLabel.kindex-0, QLabel.kindex-1, QLabel.kindex-2, QLabel.kindex-3 { color: #0000ff; } QMenu::item:disabled{background-color:transparent; color:palette(disabled);} ================================================ FILE: rig/Rig.cpp ================================================ #include "Rig.h" #include "RigctldManager.h" #include "core/debug.h" #include "rig/drivers/HamlibRigDrv.h" #ifdef Q_OS_WIN #include "rig/drivers/OmnirigRigDrv.h" #include "rig/drivers/Omnirigv2RigDrv.h" #endif #include "rig/drivers/TCIRigDrv.h" #include "rig/drivers/FlrigRigDrv.h" MODULE_IDENTIFICATION("qlog.rig.rig"); #define MUTEXLOCKER qCDebug(runtime) << "Waiting for Rig mutex"; \ QMutexLocker locker(&rigLock); \ qCDebug(runtime) << "Using Rig" int Rig::DEFAULT_MODEL = HamlibRigDrv::DUMMY_MODEL; Rig::Rig(QObject *parent) : QObject{parent}, rigDriver(nullptr), connected(false), heartBeatTimer(new QTimer(this)) { FCT_IDENTIFICATION; drvMapping[HAMLIB_DRIVER] = DrvParams(HAMLIB_DRIVER, "Hamlib", &HamlibRigDrv::getModelList, &HamlibRigDrv::getCaps, &HamlibRigDrv::getPTTTypeList); #ifdef Q_OS_WIN drvMapping[OMNIRIG_DRIVER] = DrvParams(OMNIRIG_DRIVER, "Omnirig v1", &OmnirigRigDrv::getModelList, &OmnirigRigDrv::getCaps, nullptr); drvMapping[OMNIRIGV2_DRIVER] = DrvParams(OMNIRIGV2_DRIVER, "Omnirig v2", &OmnirigV2RigDrv::getModelList, &OmnirigV2RigDrv::getCaps, nullptr); #endif drvMapping[TCI_DRIVER] = DrvParams(TCI_DRIVER, "TCI", &TCIRigDrv::getModelList, &TCIRigDrv::getCaps, nullptr); drvMapping[FLRIG_DRIVER] = DrvParams(FLRIG_DRIVER, "FLRig", &FlrigRigDrv::getModelList, &FlrigRigDrv::getCaps, nullptr); connect(heartBeatTimer, &QTimer::timeout, this, &Rig::sendHeartBeat); } qint32 Rig::getNormalBandwidth(const QString &mode, const QString &) { FCT_IDENTIFICATION; if ( mode == "CW" ) return 1000; if ( mode == "SSB" || mode == "PSK" || mode == "MFSK" || mode == "FT8" || mode == "SSTV" ) return 2500; if ( mode == "AM" ) return 6000; if ( mode == "RTTY" ) return 2400; if ( mode == "FM" ) return 12500; return 6000; } const QList> Rig::getModelList(const DriverID &id) const { FCT_IDENTIFICATION; QList> ret; if ( drvMapping.contains(id) && drvMapping.value(id).getModeslListFunction != nullptr ) { ret = (drvMapping.value(id).getModeslListFunction)(); } return ret; } const QList > Rig::getPTTTypeList(const DriverID &id) const { FCT_IDENTIFICATION; QList> ret; if ( drvMapping.contains(id) && drvMapping.value(id).getPTTTypeListFunction != nullptr ) { ret = (drvMapping.value(id).getPTTTypeListFunction)(); } return ret; } const QList> Rig::getDriverList() const { FCT_IDENTIFICATION; QList> ret; const QList &keys = drvMapping.keys(); for ( const int &key : keys ) { ret << QPair(key, drvMapping[key].driverName); } return ret; } const RigCaps Rig::getRigCaps(const DriverID &id, int model) const { FCT_IDENTIFICATION; if ( drvMapping.contains(id) && drvMapping.value(id).getCapsFunction != nullptr) { return (drvMapping.value(id).getCapsFunction)(model); } return RigCaps(); } void Rig::stopTimer() { FCT_IDENTIFICATION; bool check = QMetaObject::invokeMethod(Rig::instance(), &Rig::stopTimerImplt, Qt::QueuedConnection); Q_ASSERT( check ); } void Rig::stopTimerImplt() { FCT_IDENTIFICATION; MUTEXLOCKER; heartBeatTimer->stop(); if ( rigDriver ) rigDriver->stopTimers(); } void Rig::start() { FCT_IDENTIFICATION; } void Rig::open() { FCT_IDENTIFICATION; QMetaObject::invokeMethod(this, &Rig::openImpl, Qt::QueuedConnection); } void Rig::openImpl() { FCT_IDENTIFICATION; MUTEXLOCKER; __openRig(); } void Rig::__openRig() { FCT_IDENTIFICATION; // if rig is active then close it __closeRig(); RigProfile newRigProfile = RigProfilesManager::instance()->getCurProfile1(); if ( newRigProfile == RigProfile() ) { emit rigErrorPresent(tr("No Rig Profile selected"), QString()); return; } qCDebug(runtime) << "Opening profile name: " << newRigProfile.profileName; // If rig sharing is enabled, start rigctld and modify profile to connect via network if ( newRigProfile.shareRigctld && newRigProfile.driver == HAMLIB_DRIVER && newRigProfile.getPortType() == RigProfile::SERIAL_ATTACHED) { qCDebug(runtime) << "Starting rigctld for rig sharing"; if ( !rigctldManager ) { rigctldManager = new RigctldManager(this); connect(rigctldManager, &RigctldManager::errorOccurred, this, [this](const QString &error) { emit rigErrorPresent(tr("Rigctld Error"), error); }); } if ( !rigctldManager->start(newRigProfile) ) return; // Modify profile to connect via network to rigctld newRigProfile.hostname = rigctldManager->getConnectHost(); newRigProfile.netport = rigctldManager->getConnectPort(); newRigProfile.portPath.clear(); // Clear serial port to force network connection newRigProfile.model = HamlibRigDrv::RIGCTLD_MODEL; qCDebug(runtime) << "Connecting to rigctld at" << newRigProfile.hostname << ":" << newRigProfile.netport; } rigDriver = getDriver(newRigProfile); if ( !rigDriver ) { // initialization failed emit rigErrorPresent(tr("Initialization Error"), tr("Internal Error")); return; } connect( rigDriver, &GenericRigDrv::frequencyChanged, this, [this](double a, double b, double c) { rigStatus.freq = a; emit frequencyChanged(VFO1, a, b, c); emitRigStatusChanged(); }); connect( rigDriver, &GenericRigDrv::txFrequencyChanged, this, [this](double txFreq) { rigStatus.txFreq = txFreq; emit frequencyChanged(VFO2, txFreq, txFreq, txFreq); emitRigStatusChanged(); }); connect( rigDriver, &GenericRigDrv::splitChanged, this, [this](bool enabled) { rigStatus.splitEnabled = enabled; emit splitChanged(VFO1, enabled); emitRigStatusChanged(); }); connect( rigDriver, &GenericRigDrv::pttChanged, this, [this](bool a) { rigStatus.ptt = (a) ? 1 : 0; emit pttChanged(VFO1, a); emitRigStatusChanged(); }); connect( rigDriver, &GenericRigDrv::modeChanged, this, [this](const QString &a, const QString &b, const QString &c, qint32 d) { rigStatus.rawmode = a; rigStatus.mode = b; rigStatus.submode = c; rigStatus.bandwidth = d; emit modeChanged(VFO1, a, b, c, d); emitRigStatusChanged(); }); connect( rigDriver, &GenericRigDrv::vfoChanged, this, [this](const QString &a) { rigStatus.vfo = a; emit vfoChanged(VFO1, a); emitRigStatusChanged(); }); connect( rigDriver, &GenericRigDrv::powerChanged, this, [this](double a) { rigStatus.power = a; emit powerChanged(VFO1, a); emitRigStatusChanged(); }); connect( rigDriver, &GenericRigDrv::ritChanged, this, [this](double a) { rigStatus.rit = a; emit ritChanged(VFO1, a); emitRigStatusChanged(); }); connect( rigDriver, &GenericRigDrv::xitChanged, this, [this](double a) { rigStatus.xit = a; emit xitChanged(VFO1, a); emitRigStatusChanged(); }); connect( rigDriver, &GenericRigDrv::keySpeedChanged, this, [this](unsigned int a) { rigStatus.keySpeed = a; emit keySpeedChanged(VFO1, a); emitRigStatusChanged(); }); connect( rigDriver, &GenericRigDrv::errorOccurred, this, [this](const QString &a, const QString &b) { close(); emit rigErrorPresent(a, b); }); connect( rigDriver, &GenericRigDrv::rigIsReady, this, [this, newRigProfile]() { connected = true; // Change Assigned CW Key if ( !newRigProfile.assignedCWKey.isEmpty() || newRigProfile.assignedCWKey != " ") { emit rigCWKeyOpenRequest(newRigProfile.assignedCWKey); } rigStatus.profile = newRigProfile.profileName; rigStatus.isConnected = true; heartBeatTimer->start(HEARTBEATPERIOD); emit rigConnected(); sendState(); }); if ( !rigDriver->open() ) { emit rigErrorPresent(tr("Cannot open Rig"), rigDriver->lastError()); qWarning() << rigDriver->lastError(); __closeRig(); return; } } void Rig::close() { FCT_IDENTIFICATION; QMetaObject::invokeMethod(this, &Rig::closeImpl, Qt::QueuedConnection); } void Rig::closeImpl() { FCT_IDENTIFICATION; MUTEXLOCKER; __closeRig(); } void Rig::__closeRig() { FCT_IDENTIFICATION; if ( !rigDriver ) { qCDebug(runtime) << "Driver is not active"; return; } const RigProfile &connectedRigProfile = rigDriver->getCurrRigProfile(); // Change Assigned CW Key if ( !connectedRigProfile.assignedCWKey.isEmpty() || connectedRigProfile.assignedCWKey != " ") { emit rigCWKeyCloseRequest(connectedRigProfile.assignedCWKey); } rigStatus.isConnected = false; heartBeatTimer->stop(); emitRigStatusChanged(); rigStatus.clear(); delete rigDriver; rigDriver = nullptr; connected = false; // Stop and cleanup rigctld if it was running if ( rigctldManager ) { qCDebug(runtime) << "Stopping rigctld"; rigctldManager->stop(); delete rigctldManager; rigctldManager = nullptr; } emit rigDisconnected(); } bool Rig::isRigConnected() { FCT_IDENTIFICATION; return connected; } bool Rig::isMorseOverCatSupported() { FCT_IDENTIFICATION; MUTEXLOCKER; if ( ! rigDriver ) return false; return rigDriver->isMorseOverCatSupported(); } void Rig::setFrequency(double newFreq) { FCT_IDENTIFICATION; qCDebug(function_parameters) << newFreq; setFrequency(VFO1, newFreq); } void Rig::setFrequency(VFOID vfoid, double newFreq) { FCT_IDENTIFICATION; qCDebug(function_parameters) << vfoid << newFreq; if ( newFreq > 0.0 ) { QMetaObject::invokeMethod(this, "setVFOFrequencyImpl", Qt::QueuedConnection, Q_ARG(VFOID, vfoid), Q_ARG(double, newFreq)); } } void Rig::setSplit(bool enabled) { FCT_IDENTIFICATION; qCDebug(function_parameters) << enabled; QMetaObject::invokeMethod(this, "setSplitImpl", Qt::QueuedConnection, Q_ARG(bool, enabled)); } void Rig::setVFOFrequencyImpl(VFOID vfoid, double newFreq) { FCT_IDENTIFICATION; qCDebug(function_parameters) << vfoid << newFreq; MUTEXLOCKER; if ( ! rigDriver ) return; rigDriver->setFrequency(vfoid, newFreq); } void Rig::setSplitImpl(bool enabled) { FCT_IDENTIFICATION; qCDebug(function_parameters) << enabled; MUTEXLOCKER; if ( ! rigDriver ) return; rigDriver->setSplit(enabled); } void Rig::setRawMode(const QString& rawMode) { FCT_IDENTIFICATION; qCDebug(function_parameters) << rawMode; if ( rawMode.isEmpty() ) return; QMetaObject::invokeMethod(this, "setRawModeImpl", Qt::QueuedConnection, Q_ARG(QString, rawMode)); } void Rig::setRawModeImpl(const QString &rawMode) { FCT_IDENTIFICATION; qCDebug(function_parameters) << rawMode; MUTEXLOCKER; if ( ! rigDriver ) return; rigDriver->setRawMode(rawMode); } void Rig::setMode(const QString &newMode, const QString &newSubMode, bool digiVariant) { FCT_IDENTIFICATION; qCDebug(function_parameters) << newMode << newSubMode << digiVariant; if ( newMode.isEmpty() && newSubMode.isEmpty() ) return; QMetaObject::invokeMethod(this, "setModeImpl", Qt::QueuedConnection, Q_ARG(QString, newMode), Q_ARG(QString, newSubMode), Q_ARG(bool, digiVariant)); } void Rig::setModeImpl(const QString &newMode, const QString &newSubMode, bool digiVariant) { FCT_IDENTIFICATION; qCDebug(function_parameters) << newMode << newSubMode << digiVariant; MUTEXLOCKER; if ( ! rigDriver ) return; rigDriver->setMode(newMode, newSubMode, digiVariant); } void Rig::setPTT(bool active) { FCT_IDENTIFICATION; QMetaObject::invokeMethod(this, "setPTTImpl", Qt::QueuedConnection, Q_ARG(bool, active)); } void Rig::setPTTImpl(bool active) { FCT_IDENTIFICATION; qCDebug(function_parameters) << active; MUTEXLOCKER; if ( ! rigDriver ) return; rigDriver->setPTT(active); } void Rig::setKeySpeed(qint16 wpm) { FCT_IDENTIFICATION; QMetaObject::invokeMethod(this, "setKeySpeedImpl", Qt::QueuedConnection, Q_ARG(qint16, wpm)); } void Rig::setKeySpeedImpl(qint16 wpm) { FCT_IDENTIFICATION; qCDebug(function_parameters) << wpm; MUTEXLOCKER; if ( ! rigDriver ) return; rigDriver->setKeySpeed(wpm); } void Rig::syncKeySpeed(qint16 wpm) { FCT_IDENTIFICATION; QMetaObject::invokeMethod(this, "syncKeySpeedImpl", Qt::QueuedConnection, Q_ARG(qint16, wpm)); } void Rig::syncKeySpeedImpl(qint16 wpm) { FCT_IDENTIFICATION; qCDebug(function_parameters) << wpm; MUTEXLOCKER; if ( ! rigDriver ) return; rigDriver->syncKeySpeed(wpm); } void Rig::sendMorse(const QString &text) { FCT_IDENTIFICATION; if ( text.isEmpty() ) return; QMetaObject::invokeMethod(this, "sendMorseImpl", Qt::QueuedConnection, Q_ARG(QString, text)); } void Rig::sendMorseImpl(const QString &text) { FCT_IDENTIFICATION; qCDebug(function_parameters) << text; MUTEXLOCKER; if ( ! rigDriver ) return; rigDriver->sendMorse(text); } void Rig::stopMorse() { FCT_IDENTIFICATION; QMetaObject::invokeMethod(this, "stopMorseImpl", Qt::QueuedConnection); } void Rig::stopMorseImpl() { FCT_IDENTIFICATION; MUTEXLOCKER; if ( ! rigDriver ) return; rigDriver->stopMorse(); } void Rig::sendState() { FCT_IDENTIFICATION; QMetaObject::invokeMethod(this, "sendStateImpl", Qt::QueuedConnection); } void Rig::sendStateImpl() { FCT_IDENTIFICATION; MUTEXLOCKER; if ( ! rigDriver ) return; rigDriver->sendState(); } void Rig::sendDXSpot(DxSpot spot) { FCT_IDENTIFICATION; QMetaObject::invokeMethod(this, "sendDXSpotImpl", Qt::QueuedConnection, Q_ARG(DxSpot, spot)); } void Rig::sendDXSpotImpl(const DxSpot &spot) { FCT_IDENTIFICATION; MUTEXLOCKER; if ( ! rigDriver ) return; rigDriver->sendDXSpot(spot); } GenericRigDrv *Rig::getDriver( const RigProfile &profile ) { FCT_IDENTIFICATION; //(if) for testing purpose #if 1 qCDebug(runtime) << profile.driver; switch ( profile.driver ) { case Rig::HAMLIB_DRIVER: return new HamlibRigDrv(profile, this); break; #ifdef Q_OS_WIN case Rig::OMNIRIG_DRIVER: return new OmnirigRigDrv(profile, this); break; case Rig::OMNIRIGV2_DRIVER: return new OmnirigV2RigDrv(profile, this); break; #endif case Rig::TCI_DRIVER: return new TCIRigDrv(profile, this); break; case Rig::FLRIG_DRIVER: return new FlrigRigDrv(profile, this); break; default: qWarning() << "Unsupported Rig Driver " << profile.driver; return nullptr; } #else #ifdef Q_OS_WIN return new OmnirigV2Drv(profile, this); #endif return new HamlibDrv(profile, this); #endif } void Rig::emitRigStatusChanged() { FCT_IDENTIFICATION; // it can happen especially with TCI that Rig received information, // emit signals but rig is not connected. if ( rigStatus.profile.isEmpty() ) return; emit rigStatusChanged(rigStatus); } const QStringList Rig::getAvailableRawModes() { FCT_IDENTIFICATION; MUTEXLOCKER; if ( ! rigDriver ) return QStringList(); return rigDriver->getAvailableModes();; } Rig::~Rig() { FCT_IDENTIFICATION; __closeRig(); } void Rig::sendHeartBeat() { FCT_IDENTIFICATION; // The functionality is similar to emitRigStatusChanged, but emits // a different signal to distinguish these two cases for future use. if ( !rigStatus.isConnected || rigStatus.profile.isEmpty() ) return; emit rigStatusHeartBeat(rigStatus); } #undef MUTEXLOCKER ================================================ FILE: rig/Rig.h ================================================ #ifndef RIG_RIG_H #define RIG_RIG_H #include #include #include #include #include "rig/drivers/GenericRigDrv.h" #include "RigCaps.h" #include "data/DxSpot.h" class RigctldManager; class Rig : public QObject { Q_OBJECT public: static int DEFAULT_MODEL; enum DriverID { UNDEF_DRIVER = 0, HAMLIB_DRIVER = 1, OMNIRIG_DRIVER = 2, OMNIRIGV2_DRIVER = 3, TCI_DRIVER = 4, FLRIG_DRIVER = 5, }; struct Status { QString profile; double freq = 0.0; QString mode; QString submode; QString rawmode; qint8 ptt = -1; double power = 0.0; quint16 keySpeed = 0; QString vfo = "Curr"; double rit = 0.0; double xit = 0.0; qint32 bandwidth = 0; bool isConnected = false; bool splitEnabled = false; double txFreq = 0.0; void clear() { profile.clear(); freq = 0.0; mode.clear(); submode.clear(); rawmode.clear(); ptt = -1; power = 0.0; keySpeed = 0; vfo="Curr"; rit = 0.0; xit = 0.0; bandwidth = 0; isConnected = false; splitEnabled = false; txFreq = 0.0; }; }; static Rig* instance() { static Rig instance; return &instance; }; static qint32 getNormalBandwidth(const QString &mode, const QString &subMode); explicit Rig(QObject *parent = nullptr); ~Rig(); bool isRigConnected(); bool isMorseOverCatSupported(); const QStringList getAvailableRawModes(); const QList> getModelList(const DriverID &id) const; const QList> getPTTTypeList(const DriverID &id) const; const QList> getDriverList() const; const RigCaps getRigCaps(const DriverID &, int) const; public slots: void start(); void open(); void close(); void stopTimer(); void setFrequency(double); void setFrequency(VFOID, double); void setSplit(bool); void setRawMode(const QString &rawMode); void setMode(const QString &, const QString &, bool = false); void setPTT(bool); void setKeySpeed(qint16 wpm); void syncKeySpeed(qint16 wpm); void sendMorse(const QString &text); void stopMorse(); void sendState(); void sendDXSpot(DxSpot spot); signals: void frequencyChanged(VFOID, double, double, double); void splitChanged(VFOID, bool); void modeChanged(VFOID, QString, QString, QString, qint32); void powerChanged(VFOID, double); void keySpeedChanged(VFOID, unsigned int); void vfoChanged(VFOID, QString); void ritChanged(VFOID, double); void xitChanged(VFOID, double); void pttChanged(VFOID, bool); void rigCWKeyOpenRequest(QString); void rigCWKeyCloseRequest(QString); void rigDisconnected(); void rigConnected(); void rigErrorPresent(QString, QString); void rigStatusChanged(Rig::Status); void rigStatusHeartBeat(Rig::Status); private slots: void stopTimerImplt(); void openImpl(); void closeImpl(); void setVFOFrequencyImpl(VFOID, double); void setSplitImpl(bool); void setRawModeImpl(const QString&); void setModeImpl(const QString &, const QString &, bool); void setPTTImpl(bool); void setKeySpeedImpl(qint16 wpm); void syncKeySpeedImpl(qint16 wpm); void sendMorseImpl(const QString &text); void stopMorseImpl(); void sendStateImpl(); void sendDXSpotImpl(const DxSpot &spot); void sendHeartBeat(); private: class DrvParams { public: DrvParams(const DriverID id, const QString &driverName, QList> (*getModelfct)(), RigCaps (*getCapsfct)(int), QList> (*getPTTTypefct)()) : driverID(id), driverName(driverName), getModeslListFunction(getModelfct), getCapsFunction(getCapsfct), getPTTTypeListFunction(getPTTTypefct){}; DrvParams() : driverID(UNDEF_DRIVER), getModeslListFunction(nullptr), getCapsFunction(nullptr), getPTTTypeListFunction(nullptr){}; DriverID driverID; QString driverName; QList> (*getModeslListFunction)(); RigCaps (*getCapsFunction)(int); QList> (*getPTTTypeListFunction)(); }; QMap drvMapping; void __closeRig(); void __openRig(); GenericRigDrv *getDriver(const RigProfile &profile); void emitRigStatusChanged(); private: GenericRigDrv *rigDriver; QMutex rigLock; Rig::Status rigStatus; bool connected; QTimer *heartBeatTimer; const quint16 HEARTBEATPERIOD = 1000; // in ms RigctldManager *rigctldManager = nullptr; }; Q_DECLARE_METATYPE(Rig::Status); #endif // RIG_RIG_H ================================================ FILE: rig/RigCaps.cpp ================================================ #include "RigCaps.h" RigCaps::RigCaps(bool canGetFreq, bool canGetMode, bool canGetVFO, bool canGetPWR, bool canGetRIT, bool canGetXIT, bool canGetPTT, bool canGetKeySpeed, bool canSendMorse, bool isNetworkOnly, bool needPolling, bool canProcessDXSpot, int serial_data_bits, int serial_stop_bits, bool isCIVAddrSupported, bool canGetSplit ) : canGetFreq(canGetFreq), canGetMode(canGetMode), canGetVFO(canGetVFO), canGetPWR(canGetPWR), canGetRIT(canGetRIT), canGetXIT(canGetXIT), canGetPTT(canGetPTT), canGetKeySpeed(canGetKeySpeed), canSendMorse(canSendMorse), serialDataBits(serial_data_bits), serialStopBits(serial_stop_bits), isNetworkOnly(isNetworkOnly), needPolling(needPolling), canProcessDXSpot(canProcessDXSpot), isCIVAddrSupported(isCIVAddrSupported), canGetSplit(canGetSplit) { } ================================================ FILE: rig/RigCaps.h ================================================ #ifndef RIG_RIGCAPS_H #define RIG_RIGCAPS_H class RigCaps { public: RigCaps(bool canGetFreq = false, bool canGetMode = false, bool canGetVFO = false, bool canGetPWR = false, bool canGetRIT = false, bool canGetXIT = false, bool canGetPTT = false, bool canGetKeySpeed = false, bool canSendMorse = false, bool isNetworkOnly = false, bool needPolling = false, bool canProcessDXSpot = false, int serial_data_bits = 8, int serial_stop_bits = 1, bool isCIVAddrSupported = false, bool canGetSplit = false ); bool canGetFreq; bool canGetMode; bool canGetVFO; bool canGetPWR; bool canGetRIT; bool canGetXIT; bool canGetPTT; bool canGetKeySpeed; bool canSendMorse; int serialDataBits; int serialStopBits; bool isNetworkOnly; bool needPolling; bool canProcessDXSpot; bool isCIVAddrSupported; bool canGetSplit; }; #endif // RIG_RIGCAPS_H ================================================ FILE: rig/RigctldManager.cpp ================================================ #include #include #include #include #include #include #include "RigctldManager.h" #include "core/debug.h" #include "data/SerialPort.h" MODULE_IDENTIFICATION("qlog.rig.rigctldmanager"); RigctldManager::RigctldManager(QObject *parent) : QObject(parent) { FCT_IDENTIFICATION; } RigctldManager::~RigctldManager() { FCT_IDENTIFICATION; stop(); } bool RigctldManager::start(const RigProfile &profile) { FCT_IDENTIFICATION; if ( rigctldProcess && rigctldProcess->state() != QProcess::NotRunning) { qCWarning(runtime) << "rigctld is already running"; return false; } // Find rigctld executable QString rigctldPath = profile.rigctldPath; if ( rigctldPath.isEmpty() ) { rigctldPath = findRigctldPath(); if ( rigctldPath.isEmpty() ) { qCWarning(runtime) << "rigctld executable not found"; #ifdef QLOG_FLATPAK emit errorOccurred(tr("rigctld executable not found in /app/bin/. This should not happen in Flatpak build.")); #else emit errorOccurred(tr("rigctld executable not found. Please install Hamlib or specify the path in Advanced settings.")); #endif return false; } } qCDebug(runtime) << "Using rigctld at:" << rigctldPath; // Check major version compatibility // Hamlib 3 vs 4 changed RIG model IDs and they are not compatible const RigctldVersion rigctldVersion = getVersion(rigctldPath); if ( rigctldVersion.isValid() && ((rigctldVersion.major >= 4 && HAMLIBVERSION_MAJOR <= 3) || (rigctldVersion.major <= 3 && HAMLIBVERSION_MAJOR >= 4))) { qCDebug(runtime) << "Hamlib major version mismatch: QLog compiled with" << HAMLIBVERSION_MAJOR << "but rigctld is" << rigctldVersion.major; emit errorOccurred(tr("Hamlib major version mismatch: QLog was compiled with Hamlib %1 " "but rigctld reports version %2.%3.%4. " "Rig model IDs are incompatible between major versions.") .arg(HAMLIBVERSION_MAJOR) .arg(rigctldVersion.major) .arg(rigctldVersion.minor) .arg(rigctldVersion.patch)); return false; } currentPort = profile.rigctldPort; // Check if port is already in use { QTcpSocket testSocket; testSocket.connectToHost("127.0.0.1", currentPort); if ( testSocket.waitForConnected(500) ) { testSocket.disconnectFromHost(); qCWarning(runtime) << "Port" << currentPort << "is already in use"; emit errorOccurred(tr("Port %1 is already in use. " "Another rigctld or application may be running on this port.") .arg(currentPort)); return false; } testSocket.abort(); } // Create process rigctldProcess = new QProcess(this); rigctldProcess->setProcessChannelMode(QProcess::SeparateChannels); connect(rigctldProcess, &QProcess::started, this, &RigctldManager::onProcessStarted); connect(rigctldProcess, QOverload::of(&QProcess::finished), this, &RigctldManager::onProcessFinished); connect(rigctldProcess, &QProcess::errorOccurred, this, &RigctldManager::onProcessError); connect(rigctldProcess, &QProcess::readyReadStandardOutput, this, &RigctldManager::onReadyReadStdout); connect(rigctldProcess, &QProcess::readyReadStandardError, this, &RigctldManager::onReadyReadStderr); // exec arguments const QStringList args = buildArguments(profile); qCDebug(runtime) << "Starting rigctld with args:" << args; rigctldProcess->start(rigctldPath, args); if ( !rigctldProcess->waitForStarted(5000) ) { qCDebug(runtime) << "Failed to start rigctld"; // onProcessError already emits errorOccurred via QProcess::errorOccurred signal delete rigctldProcess; rigctldProcess = nullptr; return false; } // Wait for rigctld to be ready (accepting connections) if ( !waitForRigctldReady() ) { qCDebug(runtime) << "rigctld not responding on port" << currentPort; stop(); emit errorOccurred(tr("rigctld started but not responding on port %1.").arg(currentPort)); return false; } qCDebug(runtime) << "rigctld started successfully on port" << currentPort; emit started(); return true; } void RigctldManager::stop() { FCT_IDENTIFICATION; if ( !rigctldProcess ) return; stoppingInProgress = true; if ( rigctldProcess->state() != QProcess::NotRunning ) { qCDebug(runtime) << "Stopping rigctld"; // Disconnect signals to prevent error notifications during controlled shutdown rigctldProcess->disconnect(); rigctldProcess->terminate(); if ( !rigctldProcess->waitForFinished(3000) ) { qCWarning(runtime) << "rigctld did not terminate gracefully, killing"; rigctldProcess->kill(); rigctldProcess->waitForFinished(1000); } } delete rigctldProcess; rigctldProcess = nullptr; stoppingInProgress = false; emit stopped(); } bool RigctldManager::isRunning() const { return rigctldProcess && rigctldProcess->state() == QProcess::Running; } QString RigctldManager::findRigctldPath() { FCT_IDENTIFICATION; // First priority: Check application directory (bundled rigctld) #ifdef Q_OS_WIN const QString appDirPath = QCoreApplication::applicationDirPath() + "/rigctld.exe"; #else const QString appDirPath = QCoreApplication::applicationDirPath() + "/rigctld"; #endif if ( QFile::exists(appDirPath) ) { qCDebug(runtime) << "Found bundled rigctld at:" << appDirPath; return appDirPath; } // Second priority: Platform-specific paths const QStringList platformPaths = { #ifdef Q_OS_WIN "C:/Program Files/Hamlib/bin/rigctld.exe", "C:/Program Files (x86)/Hamlib/bin/rigctld.exe", QDir::homePath() + "/AppData/Local/Hamlib/bin/rigctld.exe" #endif #ifdef Q_OS_MACOS "/opt/homebrew/bin/rigctld", "/usr/local/bin/rigctld", "/opt/local/bin/rigctld" #endif #ifdef QLOG_FLATPAK "/app/bin/rigctld", // Flatpak path #else "/usr/bin/rigctld", "/usr/local/bin/rigctld", "/opt/hamlib/bin/rigctld" #endif }; for ( const QString &p : platformPaths ) { if ( QFile::exists(p) ) { qCDebug(runtime) << "Found rigctld at:" << p; return p; } } // Last resort: Try $PATH const QString path = QStandardPaths::findExecutable("rigctld"); if ( !path.isEmpty() ) { qCDebug(runtime) << "Found rigctld in PATH:" << path; return path; } qCWarning(runtime) << "rigctld not found"; return QString(); } RigctldVersion RigctldManager::getVersion(const QString &rigctldPath) { FCT_IDENTIFICATION; qCDebug(function_parameters) << rigctldPath; RigctldVersion version; QString path = rigctldPath; if ( path.isEmpty() ) path = findRigctldPath(); if ( path.isEmpty() ) { qCDebug(runtime) << "rigctld not found"; return version; } QProcess process; process.start(path, QStringList("--version")); if ( !process.waitForFinished(2000) ) { qCDebug(runtime) << "rigctld --version timed out"; return version; } const QString output = QString::fromLocal8Bit(process.readAllStandardOutput()).trimmed(); qCDebug(runtime) << "rigctld version output:" << output; // "rigctld Hamlib 4.5.5" QRegularExpression re("rigct\\S*\\s+\\S+\\s+(\\d+)\\.(\\d+)\\.(\\d+)"); QRegularExpressionMatch match = re.match(output); // clazy:exclude=use-static-qregularexpression if ( match.hasMatch() ) { version.major = match.captured(1).toInt(); version.minor = match.captured(2).toInt(); version.patch = match.captured(3).toInt(); qCDebug(runtime) << "Parsed version:" << version.major << version.minor << version.patch; } else { qCDebug(runtime) << "Failed to parse version from output:" << output; } return version; } bool RigctldManager::waitForRigctldReady(int timeoutMs) { FCT_IDENTIFICATION; QTcpSocket testSocket; int elapsed = 0; const int checkInterval = 100; // test connection while ( elapsed < timeoutMs ) { testSocket.connectToHost("127.0.0.1", currentPort); if ( testSocket.waitForConnected(500) ) { testSocket.disconnectFromHost(); qCDebug(runtime) << "rigctld is ready after" << elapsed << "ms"; return true; } testSocket.abort(); // Check if process is still running if ( rigctldProcess && rigctldProcess->state() != QProcess::Running ) { qCWarning(runtime) << "rigctld process terminated unexpectedly"; return false; } QThread::msleep(checkInterval); elapsed += checkInterval; } return false; } QStringList RigctldManager::buildArguments(const RigProfile &profile) const { FCT_IDENTIFICATION; QStringList args; // Model args << "-m" << QString::number(profile.model); // Listen Port args << "-t" << QString::number(profile.rigctldPort); // Serial port settings if ( profile.getPortType() == RigProfile::SERIAL_ATTACHED ) { args << "-r" << profile.portPath; // BandRate if ( profile.baudrate > 0 ) args << "-s" << QString::number(profile.baudrate); // Data bits if ( profile.databits > 0 ) args << "-C" << QString("data_bits=%1").arg(profile.databits); // Stop bits if ( profile.stopbits > 0 ) args << "-C" << QString("stop_bits=%1").arg(static_cast(profile.stopbits)); // Parity if ( !profile.parity.isEmpty() && profile.parity.compare(SerialPort::SERIAL_PARITY_NO, Qt::CaseInsensitive) ) { QString parity = profile.parity.toLower(); if ( parity == SerialPort::SERIAL_PARITY_EVEN ) parity = "E"; else if ( parity == SerialPort::SERIAL_PARITY_ODD ) parity = "O"; else parity = "N"; args << "-C" << QString("serial_parity=%1").arg(parity); } // Flow control if ( !profile.flowcontrol.isEmpty() && profile.flowcontrol.compare(SerialPort::SERIAL_FLOWCONTROL_NONE, Qt::CaseInsensitive) ) { QString flow = profile.flowcontrol.toLower(); if ( flow == SerialPort::SERIAL_FLOWCONTROL_HARDWARE ) flow = "Hardware"; else if ( flow == SerialPort::SERIAL_FLOWCONTROL_SOFTWARE ) flow = "XONXOFF"; args << "-C" << QString("serial_handshake=%1").arg(flow); } // CIV address for Icom if (profile.civAddr >= 0) args << "-C" << QString("civaddr=%1").arg(profile.civAddr, 2, 16, QChar('0')); // DTR signal if ( !profile.dtr.isEmpty() && profile.dtr.compare(SerialPort::SERIAL_SIGNAL_NONE, Qt::CaseInsensitive) ) { QString dtr = profile.dtr.toLower(); if ( dtr == SerialPort::SERIAL_SIGNAL_HIGH ) dtr = "ON"; else if ( dtr == SerialPort::SERIAL_SIGNAL_LOW ) dtr = "OFF"; args << "-C" << QString("dtr_state=%1").arg(dtr); } // RTS signal if ( !profile.rts.isEmpty() && profile.rts.compare(SerialPort::SERIAL_SIGNAL_NONE, Qt::CaseInsensitive) ) { QString rts = profile.rts.toLower(); if ( rts == SerialPort::SERIAL_SIGNAL_HIGH ) rts = "ON"; else if ( rts == SerialPort::SERIAL_SIGNAL_LOW ) rts = "OFF"; args << "-C" << QString("rts_state=%1").arg(rts); } } // Additional user-specified arguments if ( !profile.rigctldArgs.isEmpty() ) { // Split by whitespace, respecting quotes QStringList extraArgs = profile.rigctldArgs.split(QRegularExpression("\\s+"), // clazy:exclude=use-static-qregularexpression #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) Qt::SkipEmptyParts); #else QString::SkipEmptyParts); #endif args << extraArgs; } qCDebug(runtime) << args; return args; } void RigctldManager::onProcessStarted() { FCT_IDENTIFICATION; qCDebug(runtime) << "rigctld process started"; } void RigctldManager::onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus) { FCT_IDENTIFICATION; qCDebug(runtime) << "rigctld process finished with exit code" << exitCode << "status" << exitStatus; if ( stoppingInProgress ) return; emit stopped(); } void RigctldManager::onProcessError(QProcess::ProcessError error) { FCT_IDENTIFICATION; QString errorStr; switch ( error ) { case QProcess::FailedToStart: { const QString program = rigctldProcess ? rigctldProcess->program() : QString(); const QStringList args = rigctldProcess ? rigctldProcess->arguments() : QStringList(); errorStr = tr("Failed to start rigctld: %1 %2").arg(program, args.join(" ")); break; } case QProcess::Crashed: errorStr = tr("rigctld crashed."); break; case QProcess::Timedout: errorStr = tr("rigctld timed out."); break; case QProcess::WriteError: errorStr = tr("Write error with rigctld."); break; case QProcess::ReadError: errorStr = tr("Read error with rigctld."); break; default: errorStr = tr("Unknown rigctld error."); break; } qCDebug(runtime) << "rigctld process error:" << errorStr; if ( !stoppingInProgress ) emit errorOccurred(errorStr); } void RigctldManager::onReadyReadStdout() { if (!rigctldProcess) return; const QByteArray data = rigctldProcess->readAllStandardOutput(); if ( !data.isEmpty() ) { // Split by lines and log each const QList lines = data.split('\n'); for ( const QByteArray &line : lines ) { if ( !line.trimmed().isEmpty() ) qCDebug(runtime) << "rigctld:" << line.trimmed(); } } } void RigctldManager::onReadyReadStderr() { if (!rigctldProcess) return; const QByteArray data = rigctldProcess->readAllStandardError(); if ( !data.isEmpty() ) { const QList lines = data.split('\n'); for ( const QByteArray &line : lines ) { if ( !line.trimmed().isEmpty() ) qCDebug(runtime) << "rigctld stderr:" << line.trimmed(); } } } ================================================ FILE: rig/RigctldManager.h ================================================ #ifndef RIG_RIGCTLDMANAGER_H #define RIG_RIGCTLDMANAGER_H #include #include #include "data/RigProfile.h" struct RigctldVersion { int major = -1; int minor = -1; int patch = -1; bool isValid() const { return major >= 0; } }; class RigctldManager : public QObject { Q_OBJECT public: explicit RigctldManager(QObject *parent = nullptr); ~RigctldManager(); bool start(const RigProfile &profile); void stop(); bool isRunning() const; QString getConnectHost() const { return "127.0.0.1"; } quint16 getConnectPort() const { return currentPort; } static QString findRigctldPath(); static RigctldVersion getVersion(const QString &rigctldPath = QString()); signals: void started(); void stopped(); void errorOccurred(const QString &error); private slots: void onProcessStarted(); void onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus); void onProcessError(QProcess::ProcessError error); void onReadyReadStdout(); void onReadyReadStderr(); private: bool waitForRigctldReady(int timeoutMs = 5000); QStringList buildArguments(const RigProfile &profile) const; QProcess *rigctldProcess = nullptr; quint16 currentPort = 4532; bool stoppingInProgress = false; }; #endif // RIG_RIGCTLDMANAGER_H ================================================ FILE: rig/drivers/FlrigRigDrv.cpp ================================================ #include "FlrigRigDrv.h" #include "core/debug.h" #include "rig/macros.h" MODULE_IDENTIFICATION("qlog.rig.driver.flrigrigdrv"); RigCaps FlrigRigDrv::getCaps(int) { FCT_IDENTIFICATION; RigCaps ret; ret.isNetworkOnly = true; ret.canGetFreq = true; ret.canGetMode = true; ret.canGetVFO = true; ret.canGetPTT = true; ret.canGetPWR = true; ret.canGetKeySpeed = true; ret.canSendMorse = true; ret.needPolling = true; ret.canGetSplit = true; return ret; } QList > FlrigRigDrv::getModelList() { FCT_IDENTIFICATION; return {{1, "FLRig"}}; } FlrigRigDrv::FlrigRigDrv(const RigProfile &profile, QObject *parent) : GenericRigDrv(profile, parent), networkManager(new QNetworkAccessManager(this)), rigReady(false), hostUrl(QString("http://%1:%2/").arg(profile.hostname).arg(profile.netport)) { FCT_IDENTIFICATION; buildModeMappingHash(); resetCurrStates(); } FlrigRigDrv::~FlrigRigDrv() { FCT_IDENTIFICATION; networkManager->deleteLater(); } bool FlrigRigDrv::open() { FCT_IDENTIFICATION; reqGET_MODES(); // Cannot retrieve mode from rig while Send/Pause is active. turn it off. reqCWIO_SEND(false); return true; } bool FlrigRigDrv::isMorseOverCatSupported() { FCT_IDENTIFICATION; return true; } QStringList FlrigRigDrv::getAvailableModes() { FCT_IDENTIFICATION; return rigAvailableModes.keys(); } void FlrigRigDrv::setFrequency(double newFreq) { FCT_IDENTIFICATION; qCDebug(function_parameters) << QSTRING_FREQ(newFreq); if ( !rigProfile.getFreqInfo || !rigReady ) return; if ( newFreq == currFreq ) { qCDebug(runtime) << "The same Freq - skip change" << currFreq << currFreq; return; } sendXmlRpcCommand("rig.set_vfo", { newFreq }); } void FlrigRigDrv::setFrequency(VFOID vfoid, double newFreq) { FCT_IDENTIFICATION; qCDebug(function_parameters) << vfoid << QSTRING_FREQ(newFreq); if ( !rigProfile.getFreqInfo || !rigReady ) return; if ( vfoid == VFO1 ) { setFrequency(newFreq); return; } // VFO2 — TX frequency sendXmlRpcCommand("rig.set_vfoB", { newFreq }); } void FlrigRigDrv::setSplit(bool enabled) { FCT_IDENTIFICATION; qCDebug(function_parameters) << enabled; if ( !rigProfile.getSplitInfo || !rigReady ) return; sendXmlRpcCommand("rig.set_split", { enabled ? 1 : 0 }); } void FlrigRigDrv::setRawMode(const QString &rawMode) { FCT_IDENTIFICATION; qCDebug(function_parameters) << rawMode; if ( !rigProfile.getModeInfo || rawMode.isEmpty() || !rigReady ) return; if ( rawMode == currRawMode ) { qCDebug(runtime) << "The same Mode - skip change" << rawMode << currRawMode; return; } sendXmlRpcCommand("rig.set_mode", { rawMode }); } void FlrigRigDrv::setMode(const QString &mode, const QString &subMode, bool digiVariant) { FCT_IDENTIFICATION; qCDebug(function_parameters) << mode << subMode << digiVariant; setRawMode(mode2RawMode(mode, subMode, digiVariant)); } void FlrigRigDrv::setPTT(bool ptt) { FCT_IDENTIFICATION; qCDebug(function_parameters) << ptt; if ( !rigProfile.getPTTInfo || !rigReady ) return; sendXmlRpcCommand("rig.set_ptt", { ptt }); } void FlrigRigDrv::setKeySpeed(qint16 wpm) { FCT_IDENTIFICATION; qCDebug(function_parameters) << wpm; if ( wpm <= 0 || !rigReady ) return; if ( !rigProfile.getKeySpeed ) return; sendXmlRpcCommand("rig.cwio_set_wpm", { wpm }); } void FlrigRigDrv::syncKeySpeed(qint16 wpm) { FCT_IDENTIFICATION; qCDebug(function_parameters) << wpm; if ( !rigProfile.keySpeedSync || !rigReady ) return; setKeySpeed(wpm); } void FlrigRigDrv::sendMorse(const QString &text) { FCT_IDENTIFICATION; qCDebug(function_parameters) << text; if ( text.isEmpty() || !rigReady ) return; QString innerText = text; if ( !sendTextFlag && !text.contains('[')) innerText.prepend("["); innerText.replace("\n", "]"); sendTextFlag = !innerText.contains("]"); sendXmlRpcCommand("rig.cwio_text", {innerText}); } void FlrigRigDrv::stopMorse() { FCT_IDENTIFICATION; if ( !rigReady ) return; reqCWIO_SEND(false); } void FlrigRigDrv::sendState() { FCT_IDENTIFICATION; resetCurrStates(); } void FlrigRigDrv::stopTimers() { FCT_IDENTIFICATION; for ( QTimer* timer : static_cast>(runningTimers) ) timer->stop(); runningTimers.clear(); // deallocation is performed during the class destruction - QTimer is a child } void FlrigRigDrv::sendDXSpot(const DxSpot &) { FCT_IDENTIFICATION; // not implemented } void FlrigRigDrv::startRigStatePoll() { FCT_IDENTIFICATION; reqGET_VFO(); reqGET_MODE(); reqGET_BW(); reqGET_POWER(); reqGET_AB(); reqGET_PTT(); reqGET_SPLIT(); reqCWIO_GET_WPM(); } void FlrigRigDrv::reqGET_MODES() { FCT_IDENTIFICATION; sendXmlRpcCommand("rig.get_modes"); } void FlrigRigDrv::reqCWIO_SEND(bool state) { FCT_IDENTIFICATION; qCDebug(function_parameters) << state; sendTextFlag = state; sendXmlRpcCommand("rig.cwio_send", {(state) ? 1 : 0}); } void FlrigRigDrv::rspGET_MODES(const QVariant &value) { FCT_IDENTIFICATION; /* * * LSBUSB * */ qCDebug(function_parameters) << value; const QStringList &receivedModes = value.toStringList(); rigAvailableModes.clear(); for ( const QString &mode : receivedModes) if ( raw2ADIFModeMapping.contains(mode) ) rigAvailableModes[mode] = raw2ADIFModeMapping.value(mode); qCDebug(runtime) << "Rig Modes" << rigAvailableModes.keys(); rigReady = true; emit rigIsReady(); startRigStatePoll(); } void FlrigRigDrv::reqGET_VFO() { FCT_IDENTIFICATION; if ( rigProfile.getFreqInfo ) sendXmlRpcCommand("rig.get_vfo"); } void FlrigRigDrv::rspGET_VFO(const QVariant &value) { FCT_IDENTIFICATION; /* * * 14070000 * */ qCDebug(function_parameters) << value; double vfoFreq = Hz2MHz(value.toLongLong()); qCDebug(runtime) << "Rig Freq: "<< QSTRING_FREQ(vfoFreq); qCDebug(runtime) << "Object Freq: "<< QSTRING_FREQ(currFreq); if ( vfoFreq != currFreq ) { currFreq = vfoFreq; qCDebug(runtime) << "emitting FREQ changed"; emit frequencyChanged(currFreq, currFreq, currFreq); } QTimer::singleShot(rigProfile.pollInterval, this, &FlrigRigDrv::reqGET_VFO); } void FlrigRigDrv::reqGET_MODE() { FCT_IDENTIFICATION; if ( rigProfile.getModeInfo ) sendXmlRpcCommand("rig.get_mode"); } void FlrigRigDrv::rspGET_MODE(const QVariant &value) { FCT_IDENTIFICATION; /* * * USB * */ qCDebug(function_parameters) << value; const QString &rawMode = value.toString(); qCDebug(runtime) << "Rig Mode: " << rawMode; qCDebug(runtime) << "Object Mode: "<< currRawMode; if ( rawMode != currRawMode ) { // mode change currRawMode = rawMode; QString submode; const QString &mode = getModeNormalizedText(rawMode, submode); qCDebug(runtime) << "emitting MODE changed" << rawMode << mode << submode; emit modeChanged(rawMode, mode, submode, 0); } QTimer::singleShot(rigProfile.pollInterval, this, &FlrigRigDrv::reqGET_MODE); } void FlrigRigDrv::reqGET_BW() { FCT_IDENTIFICATION; if ( rigProfile.getModeInfo ) sendXmlRpcCommand("rig.get_bw"); } void FlrigRigDrv::rspGET_BW(const QVariant &value) { FCT_IDENTIFICATION; /* * * NONE * */ qCDebug(function_parameters) << value; const QVariantList &values = value.toList(); if ( values.isEmpty() ) qCDebug(runtime) << "BW List is empty"; else { bool ok = false; int rigBW = values.at(0).toInt(&ok); if ( !ok ) qCDebug(runtime) << "Received BW is not a number"; qCDebug(runtime) << "Rig BW: "<< rigBW; qCDebug(runtime) << "Object BW: "<< currBW; if ( rigBW != currBW ) { currBW = rigBW; qCDebug(runtime) << "emitting BW changed" << currBW; QString submode; const QString &mode = getModeNormalizedText(currRawMode, submode); emit modeChanged(currRawMode, mode, submode, currBW); } } QTimer::singleShot(rigProfile.pollInterval, this, &FlrigRigDrv::reqGET_BW); } void FlrigRigDrv::reqGET_POWER() { FCT_IDENTIFICATION; if ( rigProfile.getPWRInfo ) sendXmlRpcCommand("rig.get_power"); } void FlrigRigDrv::rspGET_POWER(const QVariant &value) { FCT_IDENTIFICATION; /* * * 0 * */ qCDebug(function_parameters) << value; qlonglong pwr = value.toLongLong(); qCDebug(runtime) << "Rig PWR: "<< pwr; qCDebug(runtime) << "Object PWR: "<< currPWR; if ( pwr != currPWR ) { currPWR = pwr; qCDebug(runtime) << "emitting PWR changed " << pwr; emit powerChanged(pwr); } QTimer::singleShot(rigProfile.pollInterval, this, &FlrigRigDrv::reqGET_POWER); } void FlrigRigDrv::reqGET_AB() { FCT_IDENTIFICATION; if ( rigProfile.getVFOInfo ) sendXmlRpcCommand("rig.get_AB"); } void FlrigRigDrv::rspGET_AB(const QVariant &value) { FCT_IDENTIFICATION; /* * * A * */ qCDebug(function_parameters) << value; const QString &vfo = "VFO " + value.toString(); qCDebug(runtime) << "Rig AB: "<< vfo; qCDebug(runtime) << "Object AB: "<< currVFO; if ( vfo != currVFO ) { currVFO = vfo; qCDebug(runtime) << "emitting VFO changed" << currVFO; emit vfoChanged(currVFO); } QTimer::singleShot(rigProfile.pollInterval, this, &FlrigRigDrv::reqGET_AB); } void FlrigRigDrv::reqGET_PTT() { FCT_IDENTIFICATION; if ( rigProfile.getPTTInfo ) sendXmlRpcCommand("rig.get_ptt"); } void FlrigRigDrv::rspGET_PTT(const QVariant &value) { FCT_IDENTIFICATION; qCDebug(function_parameters) << value; bool ptt = value.toBool(); qCDebug(runtime) << "Rig PTT: "<< ptt; qCDebug(runtime) << "Object PTT: "<< QString::number(currPTT); if ( ptt != currPTT ) { currPTT = (ptt) ? 1 : 0; qCDebug(runtime) << "emitting PTT changed" << QString::number(currPTT); emit pttChanged(currPTT); } QTimer::singleShot(rigProfile.pollInterval, this, &FlrigRigDrv::reqGET_PTT); } void FlrigRigDrv::reqCWIO_GET_WPM() { FCT_IDENTIFICATION; if ( rigProfile.getKeySpeed ) sendXmlRpcCommand("rig.cwio_get_wpm"); } void FlrigRigDrv::rspCWIO_GET_WPM(const QVariant &value) { FCT_IDENTIFICATION; int keySpeed = value.toInt(); qCDebug(runtime) << "Rig Key Speed: "<< keySpeed; qCDebug(runtime) << "Object Key Speed: "<< QString::number(currKeySpeed); if ( keySpeed != currKeySpeed ) { currKeySpeed = keySpeed; qCDebug(runtime) << "emitting Key Speed changed" << QString::number(currKeySpeed); emit keySpeedChanged(currKeySpeed); } QTimer::singleShot(rigProfile.pollInterval, this, &FlrigRigDrv::reqCWIO_GET_WPM); } void FlrigRigDrv::reqGET_SPLIT() { FCT_IDENTIFICATION; if ( rigProfile.getSplitInfo ) sendXmlRpcCommand("rig.get_split", {}, false); } void FlrigRigDrv::rspGET_SPLIT(const QVariant &value) { FCT_IDENTIFICATION; qCDebug(function_parameters) << value; bool newSplitEnabled = value.toBool(); qCDebug(runtime) << "Rig Split:" << newSplitEnabled; qCDebug(runtime) << "Object Split:" << currSplitEnabled; if ( newSplitEnabled != currSplitEnabled ) { currSplitEnabled = newSplitEnabled; qCDebug(runtime) << "emitting SPLIT changed" << currSplitEnabled; emit splitChanged(currSplitEnabled); } if ( currSplitEnabled ) { // When split is on, also poll TX frequency reqGET_TX_FREQ(); } else if ( currTxFreq != 0.0 ) { currTxFreq = 0.0; } QTimer::singleShot(rigProfile.pollInterval, this, &FlrigRigDrv::reqGET_SPLIT); } void FlrigRigDrv::reqGET_TX_FREQ() { FCT_IDENTIFICATION; sendXmlRpcCommand("rig.get_vfoB", {}, false); } void FlrigRigDrv::rspGET_TX_FREQ(const QVariant &value) { FCT_IDENTIFICATION; qCDebug(function_parameters) << value; double txFreq = Hz2MHz(value.toLongLong()); qCDebug(runtime) << "Rig TX Freq:" << QSTRING_FREQ(txFreq); qCDebug(runtime) << "Object TX Freq:" << QSTRING_FREQ(currTxFreq); if ( txFreq != currTxFreq ) { currTxFreq = txFreq; qCDebug(runtime) << "emitting TX FREQ changed"; emit txFrequencyChanged(currTxFreq); } } void FlrigRigDrv::resetCurrStates() { // clear current values means emiting all params currFreq = 0.0; currTxFreq = 0.0; currSplitEnabled = false; currVFO.clear(); currRawMode.clear(); currPWR = -1; currBW = -1; currPTT = -1; currKeySpeed = -1; } void FlrigRigDrv::handleError(const QString &category, const QString &errorMsg) { FCT_IDENTIFICATION; qCDebug(function_parameters) << errorMsg; if ( !lastErrorText.isEmpty() ) return; // only one error message will be reported lastErrorText = errorMsg; emit errorOccurred(category, lastErrorText); } void FlrigRigDrv::sendXmlRpcCommand(const QString &method, const QList ¶ms, bool emitError) { FCT_IDENTIFICATION; qCDebug(function_parameters) << method << params << emitError; QByteArray data; QXmlStreamWriter writer(&data); writer.writeStartDocument(); // custom clientid header writer.writeDTD(""); writer.writeStartElement("methodCall"); writer.writeTextElement("methodName", method); if (!params.isEmpty()) { writer.writeStartElement("params"); for (const QVariant ¶m : params) { writer.writeStartElement("param"); writer.writeStartElement("value"); switch ( param.type() ) { case QVariant::Double: writer.writeTextElement("double", QString::number(param.toDouble())); break; case QVariant::Int: writer.writeTextElement("i4", QString::number(param.toInt())); break; default: writer.writeTextElement("string", param.toString()); } writer.writeEndElement(); // value writer.writeEndElement(); // param } writer.writeEndElement(); // params } writer.writeEndElement(); // methodCall writer.writeEndDocument(); QNetworkRequest request(hostUrl); request.setHeader(QNetworkRequest::ContentTypeHeader, "text/xml"); qCDebug(runtime) << data; QNetworkReply *reply = networkManager->post(request, data); QTimer *timeoutTimer = new QTimer(this); timeoutTimer->setSingleShot(true); timeoutTimer->start(RESPONSE_TIMEOUT); runningTimers << timeoutTimer; connect(timeoutTimer, &QTimer::timeout, this, [this, reply, timeoutTimer, emitError, method]() { qCDebug(runtime) << "Response timeout detected for" << method; if ( reply->isRunning() ) { reply->abort(); reply->deleteLater(); if (emitError) handleError(tr("Timeout"), tr("FLRig response timeout")); } timeoutTimer->deleteLater(); }); connect(reply, &QNetworkReply::finished, this, [this, reply, method, timeoutTimer]() { timeoutTimer->stop(); timeoutTimer->deleteLater(); handleXmlRpcResponse(reply, method); reply->deleteLater(); }); } QVariantList FlrigRigDrv::parseArray(QXmlStreamReader &reader) { FCT_IDENTIFICATION; QVariantList list; while (!reader.atEnd()) { reader.readNext(); if (reader.isStartElement() && reader.name().toString() == "value") { const QVariant &val = parseSingleValue(reader); list << val; } if (reader.isEndElement() && reader.name().toString() == "array") break; } return list; } QVariant FlrigRigDrv::parseSingleValue(QXmlStreamReader &reader) { FCT_IDENTIFICATION; while (!reader.atEnd()) { reader.readNext(); if ( reader.isStartElement()) { const QString &type = reader.name().toString(); if (type == "int" || type == "i4") return reader.readElementText().toInt(); else if (type == "double") return reader.readElementText().toDouble(); else if (type == "string") return reader.readElementText(); else if (type == "array") return parseArray(reader); else return reader.readElementText(); } if (reader.isCharacters()) return reader.text().toString(); // untyped if (reader.isEndElement() && reader.name().toString() == "value") break; } return {}; } QVariantMap FlrigRigDrv::parseStruct(QXmlStreamReader &reader) { FCT_IDENTIFICATION; QVariantMap map; QString currentName; while (!reader.atEnd()) { reader.readNext(); if (reader.isStartElement()) { if (reader.name().toString() == "name") currentName = reader.readElementText(); else if (reader.name().toString() == "value" && !currentName.isEmpty()) { map[currentName] = parseSingleValue(reader); currentName.clear(); } } if ( reader.isEndElement() && reader.name().toString() == "struct") break; } return map; } QVariant FlrigRigDrv::parseValueFromResponse(const QByteArray &xml, bool *ok, QString &errorMessage) { FCT_IDENTIFICATION; if (ok) *ok = false; errorMessage.clear(); QXmlStreamReader reader(xml); while (!reader.atEnd()) { reader.readNext(); if (reader.isStartElement() && reader.name().toString() == "value") { if (ok) *ok = true; return parseSingleValue(reader); } if (reader.isStartElement() && reader.name().toString() == "fault") { while (!reader.atEnd()) { reader.readNext(); if (reader.isStartElement() && reader.name().toString() == "value") { QVariantMap faultStruct = parseStruct(reader); if ( faultStruct.contains("faultString")) errorMessage = faultStruct.value("faultString").toString(); return faultStruct; } } } } if (reader.hasError()) errorMessage = reader.errorString(); return {}; } void FlrigRigDrv::buildModeMappingHash() { FCT_IDENTIFICATION; /* Following Modes are used in Flrig source code * git grep "modes_ =" | awk '{print $1" "$4 }' | while read file var; do fixed_var="v${var::-1}"; sed -n "/static const char \*${fixed_var}/,/};/p" "${file::-1}"; done | grep -oE '"[^"]+"' | tr -d '"' | sort -u | paste -sd, - * * A1A,AM,AM-2,AM2.4,AM6.0,AM-D,AM-D1,AM-D2,AM-D3,AM-N,AMN,AM-syn,AM(Sync),AMW,C4FM,CW,CW2.4,CW500,CW-L,CWL, * CW-LSB,CW-N,CWN,CW-NR,CW-R,CW-U,CWU,CW-USB,DATA,DATA2-LSB,DATA-FM,DATA-FMN,DATA-L,DATA-LSB,DATA-R,DATA-U, * DATA-USB,D-FM,DFM,DIG,DIGI,DIGL,DIGU,D-LSB,DRM,DSB,D-USB,DV,DV-R,F1B,FM,FM(1),FM1,FM(2),FM2,FM-D,FM-D1, * FM-D2,FM-D3,FM-M,FM-N,FMN,FM-W,FMW,FSK,FSK-R,H3E,LCW,LSB,LSB-D,LSB-D1,LSB-D2,LSB-D3,NFM,PKT,PKT(FM), * PKT-FM,PKT(L),PKT-L,PKT-U,PSK,PSK-R,RTTY,RTTY(L),RTTY-L,RTTY-R,RTTY(U),RTTY-U,SAH,SAL,SAM,SPEC,UCW,USB, * USB-D,USB-D1,USB-D2,USB-D3,USER-L,USER-U,W-FM,WFM */ // * Mapping from FLrig Mode to ADIF modes raw2ADIFModeMapping = { { "A1A", { "CW", "", false } }, { "AM", { "AM", "", false } }, { "AM-2", { "AM", "", false } }, { "AM2.4", { "AM", "", false } }, { "AM6.0", { "AM", "", false } }, { "AM-D", { "AM", "", true } }, { "AM-D1", { "AM", "", true } }, { "AM-D2", { "AM", "", true } }, { "AM-D3", { "AM", "", true } }, { "AM-N", { "AM", "", false } }, { "AMN", { "AM", "", false } }, { "AM-syn", { "AM", "", false } }, { "AM(Sync)", { "AM", "", false } }, { "AMW", { "AM", "", false } }, { "C4FM", { "DIGITALVOICE", "C4FM", true } }, { "CW", { "CW", "", false } }, { "CW2.4", { "CW", "", false } }, { "CW500", { "CW", "", false } }, { "CW-L", { "CW", "", false } }, { "CWL", { "CW", "", false } }, { "CW-LSB", { "CW", "", false } }, { "CW-N", { "CW", "", false } }, { "CWN", { "CW", "", false } }, { "CW-NR", { "CW", "", false } }, { "CW-R", { "CW", "", false } }, { "CW-U", { "CW", "", false } }, { "CWU", { "CW", "", false } }, { "CW-USB", { "CW", "", false } }, { "DATA", { "SSB", "USB", true } }, { "DATA2-LSB", { "SSB", "LSB", true } }, { "DATA-FM", { "FM", "", true } }, { "DATA-FMN", { "FM", "", true } }, { "DATA-L", { "SSB", "LSB", true } }, { "DATA-LSB", { "SSB", "LSB", true } }, { "DATA-R", { "SSB", "LSB", true } }, { "DATA-U", { "SSB", "USB", true } }, { "DATA-USB", { "SSB", "USB", true } }, { "D-FM", { "FM", "", true } }, { "DFM", { "FM", "", true } }, { "DIG", { "SSB", "USB", true } }, { "DIGI", { "SSB", "USB", true } }, { "DIGL", { "SSB", "LSB", true } }, { "DIGU", { "SSB", "USB", true } }, { "D-LSB", { "SSB", "LSB", true } }, //{ "DRM", { "DATA", "DRM", true } }, { "DSB", { "SSB", "", false } }, { "D-USB", { "SSB", "USB", true } }, { "DV", { "DIGITALVOICE", "DSTAR", true } }, { "DV-R", { "DIGITALVOICE", "DSTAR", true } }, { "F1B", { "RTTY", "", true } }, { "FM", { "FM", "", false } }, { "FM(1)", { "FM", "", false } }, { "FM1", { "FM", "", false } }, { "FM(2)", { "FM", "", false } }, { "FM2", { "FM", "", false } }, { "FM-D", { "FM", "", true } }, { "FM-D1", { "FM", "", true } }, { "FM-D2", { "FM", "", true } }, { "FM-D3", { "FM", "", true } }, { "FM-M", { "FM", "", false } }, { "FM-N", { "FM", "", false } }, { "FMN", { "FM", "", false } }, { "FM-W", { "FM", "", false } }, { "FMW", { "FM", "", false } }, { "FSK", { "RTTY", "", true } }, { "FSK-R", { "RTTY", "", true } }, { "H3E", { "AM", "", false } }, { "LCW", { "CW", "", false } }, { "LSB", { "SSB", "LSB", false } }, { "LSB-D", { "SSB", "LSB", true } }, { "LSB-D1", { "SSB", "LSB", true } }, { "LSB-D2", { "SSB", "LSB", true } }, { "LSB-D3", { "SSB", "LSB", true } }, { "NFM", { "FM", "", false } }, { "PKT", { "SSB", "USB", true } }, { "PKT(FM)", { "FM", "", true } }, { "PKT-FM", { "FM", "", true } }, { "PKT(L)", { "SSB", "LSB", true } }, { "PKT-L", { "SSB", "LSB", true } }, { "PKT-U", { "SSB", "USB", true } }, { "PSK", { "RTTY", "", true } }, { "PSK-R", { "RTTY", "", true } }, { "RTTY", { "RTTY", "", true } }, { "RTTY(L)", { "RTTY", "", true } }, { "RTTY-L", { "RTTY", "", true } }, { "RTTY-R", { "RTTY", "", true } }, { "RTTY(U)", { "RTTY", "", true } }, { "RTTY-U", { "RTTY", "", true } }, // { "SAH", { "DATA", "SAH", true } }, // { "SAL", { "DATA", "SAL", true } }, // { "SAM", { "DATA", "SAM", true } }, // { "SPEC", { "DATA", "SPEC", true } }, { "UCW", { "CW", "", false } }, { "USB", { "SSB", "USB", false } }, { "USB-D", { "SSB", "USB", true } }, { "USB-D1", { "SSB", "USB", true } }, { "USB-D2", { "SSB", "USB", true } }, { "USB-D3", { "SSB", "USB", true } }, { "USER-L", { "SSB", "LSB", true } }, { "USER-U", { "SSB", "USB", true } }, { "W-FM", { "FM", "", false } }, { "WFM", { "FM", "", false } } }; } void FlrigRigDrv::handleXmlRpcResponse(QNetworkReply *reply, const QString &method) { FCT_IDENTIFICATION; if (reply->error() != QNetworkReply::NoError) { handleError(tr("Network Error"), reply->errorString()); return; } bool ok = false; QString errorMessage; const QByteArray &rawResp = reply->readAll(); QVariant respValue = parseValueFromResponse(rawResp, &ok, errorMessage); qCDebug(runtime) << "Method:" << method << "Response:" << respValue << "Raw:" << rawResp; if ( !ok ) { handleError(tr("Network Error"), errorMessage); return; } FlrigRigDrv::responseHandler handler = responseHandlers.value(method); if ( !handler ) { qCDebug(runtime) << "Handler not defined for" << method; return; } (this->*(handler))(respValue); } const QString FlrigRigDrv::getModeNormalizedText(const QString &rawMode, QString &submode) const { FCT_IDENTIFICATION; qCDebug(function_parameters) << rawMode; const RigMode &modeInfo = raw2ADIFModeMapping.value(rawMode); qCDebug(runtime) << "Result" << modeInfo.mode << modeInfo.submode << modeInfo.digiMode; submode = modeInfo.submode; return modeInfo.mode; } const QString FlrigRigDrv::mode2RawMode(const QString &mode, const QString &submode, bool digiVariant) const { FCT_IDENTIFICATION; qCDebug(function_parameters) << mode << submode << digiVariant; // find a suitable mode from the connected Rig modes for ( auto it = rigAvailableModes.cbegin(); it != rigAvailableModes.cend(); it++ ) { qCDebug(runtime) << "Key:" << it.key() << "Values:" << it->mode << it->submode << it->digiMode; if ( it->mode == mode && it->submode == submode && it->digiMode == digiVariant ) return it.key(); } return QString(); } ================================================ FILE: rig/drivers/FlrigRigDrv.h ================================================ #ifndef RIG_DRIVERS_FLRIGRIGDRV_H #define RIG_DRIVERS_FLRIGRIGDRV_H #include "GenericRigDrv.h" #include "rig/RigCaps.h" class FlrigRigDrv : public GenericRigDrv { Q_OBJECT public: static RigCaps getCaps(int); static QList> getModelList(); explicit FlrigRigDrv(const RigProfile &profile, QObject *parent = nullptr); virtual ~FlrigRigDrv(); virtual bool open() override; virtual bool isMorseOverCatSupported() override; virtual QStringList getAvailableModes() override; virtual void setFrequency(double) override; virtual void setFrequency(VFOID, double) override; virtual void setSplit(bool) override; virtual void setRawMode(const QString &) override; virtual void setMode(const QString &, const QString &, bool) override; virtual void setPTT(bool) override; virtual void setKeySpeed(qint16 wpm) override; virtual void syncKeySpeed(qint16 wpm) override; virtual void sendMorse(const QString &) override; virtual void stopMorse() override; virtual void sendState() override; virtual void stopTimers() override; virtual void sendDXSpot(const DxSpot &spot) override; private slots: void startRigStatePoll(); void reqGET_MODES(); void reqCWIO_SEND(bool); void reqGET_VFO(); void reqGET_MODE(); void reqGET_BW(); void reqGET_POWER(); void reqGET_AB(); void reqGET_PTT(); void reqCWIO_GET_WPM(); void reqGET_SPLIT(); void reqGET_TX_FREQ(); private: struct RigMode { QString mode; QString submode; bool digiMode; }; QHash raw2ADIFModeMapping; QHash rigAvailableModes; typedef void (FlrigRigDrv::*responseHandler)(const QVariant&); void resetCurrStates(); void handleError(const QString &category, const QString &errorMsg); void sendXmlRpcCommand(const QString &method, const QList ¶ms = {}, bool emitError = true); QVariantList parseArray(QXmlStreamReader &reader); QVariant parseSingleValue(QXmlStreamReader &reader); QVariantMap parseStruct(QXmlStreamReader &reader); QVariant parseValueFromResponse(const QByteArray &xml, bool *ok, QString &errorMessage); void buildModeMappingHash(); void handleXmlRpcResponse(QNetworkReply *reply, const QString &method); const QString getModeNormalizedText(const QString& rawMode, QString &submode) const; const QString mode2RawMode(const QString &mode, const QString &submode, bool digiVariant) const; void rspGET_MODES(const QVariant &value); void rspGET_VFO(const QVariant &value); void rspGET_MODE(const QVariant &value); void rspGET_BW(const QVariant &value); void rspGET_POWER(const QVariant &value); void rspGET_AB(const QVariant &value); void rspGET_PTT(const QVariant &value); void rspCWIO_GET_WPM(const QVariant &value); void rspGET_SPLIT(const QVariant &value); void rspGET_TX_FREQ(const QVariant &value); QNetworkAccessManager *networkManager; double currFreq; double currTxFreq; bool currSplitEnabled; QString currVFO; QString currRawMode; qint32 currBW; qint32 currKeySpeed; qlonglong currPWR; char currPTT; bool rigReady; QUrl hostUrl; QList runningTimers; bool sendTextFlag; const QHash responseHandlers = { {"rig.get_modes", &FlrigRigDrv::rspGET_MODES}, {"rig.get_vfo", &FlrigRigDrv::rspGET_VFO}, {"rig.get_mode", &FlrigRigDrv::rspGET_MODE}, {"rig.get_bw", &FlrigRigDrv::rspGET_BW}, {"rig.get_power", &FlrigRigDrv::rspGET_POWER}, {"rig.get_AB", &FlrigRigDrv::rspGET_AB}, {"rig.get_ptt", &FlrigRigDrv::rspGET_PTT}, {"rig.cwio_get_wpm", &FlrigRigDrv::rspCWIO_GET_WPM}, {"rig.get_split", &FlrigRigDrv::rspGET_SPLIT}, {"rig.get_vfoB", &FlrigRigDrv::rspGET_TX_FREQ} }; const uint RESPONSE_TIMEOUT = 5000; // in secs }; #endif // RIG_DRIVERS_FLRIGRIGDRV_H ================================================ FILE: rig/drivers/GenericRigDrv.cpp ================================================ #include "GenericRigDrv.h" #include "core/debug.h" MODULE_IDENTIFICATION("qlog.core.rig.driver.genericrigdrv"); GenericRigDrv::GenericRigDrv(const RigProfile &profile, QObject *parent) : QObject{parent}, rigProfile(profile), opened(false) { FCT_IDENTIFICATION; } const RigProfile GenericRigDrv::getCurrRigProfile() const { FCT_IDENTIFICATION; return rigProfile; } const QString GenericRigDrv::lastError() const { FCT_IDENTIFICATION; return lastErrorText; } void GenericRigDrv::setFrequency(VFOID vfoid, double freq) { FCT_IDENTIFICATION; qCDebug(function_parameters) << vfoid << freq; // Default: VFO1 delegates to the single-frequency setter, VFO2 is ignored if ( vfoid == VFO1 ) setFrequency(freq); } void GenericRigDrv::setSplit(bool) { FCT_IDENTIFICATION; // Default: do nothing — driver does not support split } ================================================ FILE: rig/drivers/GenericRigDrv.h ================================================ #ifndef RIG_DRIVERS_GENERICRIGDRV_H #define RIG_DRIVERS_GENERICRIGDRV_H #include #include "data/RigProfile.h" #include "data/DxSpot.h" enum VFOID { VFO1 = 0, VFO2 = 1 }; Q_DECLARE_METATYPE(VFOID) class GenericRigDrv : public QObject { Q_OBJECT public: explicit GenericRigDrv(const RigProfile &profile, QObject *parent = nullptr); virtual ~GenericRigDrv() {}; const RigProfile getCurrRigProfile() const; const QString lastError() const; virtual bool open() = 0; virtual bool isMorseOverCatSupported() = 0; virtual QStringList getAvailableModes() = 0; virtual void setFrequency(double) = 0; virtual void setFrequency(VFOID, double); virtual void setSplit(bool); virtual void setRawMode(const QString &) = 0; virtual void setMode(const QString &, const QString &, bool) = 0; virtual void setPTT(bool) = 0; virtual void setKeySpeed(qint16 wpm) = 0; virtual void syncKeySpeed(qint16 wpm) = 0; virtual void sendMorse(const QString &) = 0; virtual void stopMorse() = 0; virtual void sendState() = 0; virtual void stopTimers() = 0; virtual void sendDXSpot(const DxSpot &spot) = 0; signals: // STATE Signals void rigIsReady(); void frequencyChanged(double, double, double); void txFrequencyChanged(double); void splitChanged(bool); void pttChanged(bool); void modeChanged(QString, QString, QString, qint32); void vfoChanged(QString); void powerChanged(double); void ritChanged(double); void xitChanged(double); void keySpeedChanged(unsigned int); // Error Signal void errorOccurred(QString, QString); protected: RigProfile rigProfile; QString lastErrorText; bool opened; }; #endif // RIG_DRIVERS_GENERICRIGDRV_H ================================================ FILE: rig/drivers/HamlibRigDrv.cpp ================================================ #include #include #include "HamlibRigDrv.h" #include "core/debug.h" #include "rig/macros.h" #include "data/SerialPort.h" #include "data/Data.h" #ifndef HAMLIB_FILPATHLEN #define HAMLIB_FILPATHLEN FILPATHLEN #endif #ifndef RIG_IS_SOFT_ERRCODE #define RIG_IS_SOFT_ERRCODE(errcode) (errcode == RIG_EINVAL || errcode == RIG_ENIMPL || errcode == RIG_ERJCTED \ || errcode == RIG_ETRUNC || errcode == RIG_ENAVAIL || errcode == RIG_ENTARGET \ || errcode == RIG_EVFO || errcode == RIG_EDOM) #endif // macro introduced hamlib 4.6 #ifndef PTTPORT #define PTTPORT(r) (&r->state.pttport) #endif int HamlibRigDrv::RIGCTLD_MODEL = RIG_MODEL_NETRIGCTL; int HamlibRigDrv::DUMMY_MODEL = RIG_MODEL_DUMMY; #define MUTEXLOCKER qCDebug(runtime) << "Waiting for Drv mutex"; \ QMutexLocker locker(&drvLock); \ qCDebug(runtime) << "Using Drv" MODULE_IDENTIFICATION("qlog.rig.driver.hamlibdrv"); QList> HamlibRigDrv::getModelList() { FCT_IDENTIFICATION; QList> ret; rig_load_all_backends(); #if ( HAMLIBVERSION_MAJOR >= 4 && HAMLIBVERSION_MINOR >= 2 ) rig_list_foreach_model(addRig, &ret); #else rig_list_foreach(addRig, &ret); #endif return ret; } QList > HamlibRigDrv::getPTTTypeList() { FCT_IDENTIFICATION; QList> ret; ret << QPair("None", tr("None")) << QPair("RIG", tr("CAT")) << QPair("DTR", tr("DTR")) << QPair("RTS", tr("RTS")); return ret; } #if ( HAMLIBVERSION_MAJOR >= 4 && HAMLIBVERSION_MINOR >= 2 ) int HamlibRigDrv::addRig (const rig_model_t rigModel, void *data) { QList> *list = static_cast>*>(data); QString name = QString("%1 %2 (%3)").arg(QString::fromLatin1(rig_get_caps_cptr(rigModel, RIG_CAPS_MFG_NAME_CPTR)).trimmed(), QString::fromLatin1(rig_get_caps_cptr(rigModel, RIG_CAPS_MODEL_NAME_CPTR)).trimmed(), QString::fromLatin1(rig_get_caps_cptr(rigModel, RIG_CAPS_VERSION_CPTR)).trimmed()); list->append(QPair(rigModel, name)); return -1; } #else int HamlibRigDrv::addRig(const rig_caps *caps, void* data) { QList> *list = static_cast>*>(data); QString name = QString("%1 %2 (%3)").arg(caps->mfg_name, caps->model_name, caps->version); list->append(QPair(caps->rig_model, name)); return -1; } #endif RigCaps HamlibRigDrv::getCaps(int model) { FCT_IDENTIFICATION; const struct rig_caps *caps = rig_get_caps(model); RigCaps ret; ret.isNetworkOnly = (model == RIG_MODEL_NETRIGCTL); ret.needPolling = true; if ( caps ) { ret.canGetFreq = ( caps->get_freq ); ret.canGetMode = ( caps->get_mode ); ret.canGetVFO = ( caps->get_vfo ); ret.canGetPWR = (((caps->has_get_level) & (RIG_LEVEL_RFPOWER)) && caps->power2mW ); ret.canGetRIT = ( caps->get_rit && ((caps->has_get_func) & (RIG_FUNC_RIT)) ); ret.canGetXIT = ( caps->get_xit && ((caps->has_get_func) & (RIG_FUNC_XIT)) ); ret.canGetKeySpeed = ( ((caps->has_get_level) & (RIG_LEVEL_KEYSPD)) ); ret.canGetPTT = ( caps->get_ptt ); ret.canSendMorse = ( caps->send_morse != nullptr ); ret.canProcessDXSpot = isSmartSDRSlice(caps); ret.isCIVAddrSupported = isCIVAddrRig(caps); ret.canGetSplit = ( caps->get_split_vfo && caps->get_split_freq ); if ( ret.isNetworkOnly ) { #if ( HAMLIBVERSION_MAJOR == 4 && ( HAMLIBVERSION_MINOR == 2 || HAMLIBVERSION_MINOR == 3 ) ) /* due to a hamlib issue #855 (https://github.com/Hamlib/Hamlib/issues/855) * the PWR will be disabled for 4.2.x and 4.3.x for NETRIG */ ret.canGetPWR = false; #else // this feature is known after connection to RIG what is too late for QLog, Let's try to enable it. ret.canGetPWR = true; ret.canGetRIT = true; ret.canGetXIT = true; ret.canGetKeySpeed = true; #endif } ret.serialDataBits = caps->serial_data_bits; ret.serialStopBits = caps->serial_stop_bits; } return ret; } HamlibRigDrv::HamlibRigDrv(const RigProfile &profile, QObject *parent) : GenericRigDrv(profile, parent), rig(nullptr), SmartSDRSpotCounter(0), forceSendState(false), currPTT(false), currFreq(Hz(0)), currPBWidth(Hz(0)), currModeId(RIG_MODE_NONE), currVFO(RIG_VFO_NONE), currPWR(0), currRIT(0.0), currXIT(0.0), keySpeed(0), morseOverCatSupported(false), currSplitEnabled(false), futureSplit(false), currTxFreq(Hz(0)) { FCT_IDENTIFICATION; rig_load_all_backends(); rig = rig_init(rigProfile.model); if ( !rig ) { // initialization failed qCDebug(runtime) << "Cannot allocate Rig structure"; lastErrorText = tr("Initialization Error"); } rig_set_debug(RIG_DEBUG_BUG); connect(&errorTimer, &QTimer::timeout, this, &HamlibRigDrv::checkErrorCounter); } HamlibRigDrv::~HamlibRigDrv() { FCT_IDENTIFICATION; if ( !drvLock.tryLock(200) ) { qCDebug(runtime) << "Waited too long"; // better to make a memory leak return; } if ( rig ) { rig_close(rig); rig_cleanup(rig); rig = nullptr; } drvLock.unlock(); } bool HamlibRigDrv::open() { FCT_IDENTIFICATION; MUTEXLOCKER; if ( !rig ) { // initialization failed lastErrorText = tr("Initialization Error"); qCDebug(runtime) << "Rig is not initialized"; return false; } RigProfile::rigPortType portType = rigProfile.getPortType(); if ( portType == RigProfile::NETWORK_ATTACHED ) { //handling Network Radio const QString portString = rigProfile.hostname + ":" + QString::number(rigProfile.netport); strncpy(rig->state.rigport.pathname, portString.toLocal8Bit().constData(), HAMLIB_FILPATHLEN - 1); } else if ( portType == RigProfile::SERIAL_ATTACHED ) { //handling Serial Port Radio strncpy(rig->state.rigport.pathname, rigProfile.portPath.toLocal8Bit().constData(), HAMLIB_FILPATHLEN - 1); rig->state.rigport.parm.serial.rate = rigProfile.baudrate; rig->state.rigport.parm.serial.data_bits = rigProfile.databits; rig->state.rigport.parm.serial.stop_bits = rigProfile.stopbits; rig->state.rigport.parm.serial.handshake = stringToHamlibFlowControl(rigProfile.flowcontrol); rig->state.rigport.parm.serial.parity = stringToHamlibParity(rigProfile.parity); rig->state.rigport.parm.serial.dtr_state = stringToHamlibSerialSignal(rigProfile.dtr); rig->state.rigport.parm.serial.rts_state = stringToHamlibSerialSignal(rigProfile.rts); qCDebug(runtime) << "Using PTT Type" << rigProfile.pttType.toLocal8Bit().constData() << "PTT Path" << rigProfile.pttPortPath; if ( !rigProfile.pttPortPath.isEmpty() ) { strncpy(PTTPORT(rig)->pathname, rigProfile.pttPortPath.toLocal8Bit().constData(), HAMLIB_FILPATHLEN - 1); } if ( rig_set_conf(rig, rig_token_lookup(rig, "ptt_type"), rigProfile.pttType.toLocal8Bit().constData()) != RIG_OK ) { lastErrorText = tr("Cannot set PTT Type"); qCDebug(runtime) << "Rig Open Error" << lastErrorText; return false; } if ( rig_set_conf(rig, rig_token_lookup(rig, "ptt_share"), "1") != RIG_OK ) { lastErrorText = tr("Cannot set PTT Share"); qCDebug(runtime) << "Rig Open Error" << lastErrorText; return false; } if ( rigProfile.civAddr >= 0 ) { const QString civAddrString = QString::number(rigProfile.civAddr); const QString civAddrHexString = QString::number(rigProfile.civAddr, 16); qCDebug(runtime) << "Setting CIV Addr to" << civAddrString << "0x" + civAddrHexString; if ( rig_set_conf(rig, rig_token_lookup(rig, "civaddr"), civAddrString.toUtf8().constData()) != RIG_OK ) { lastErrorText = tr("Cannot set CIV Addr") + " 0x" + civAddrHexString; qCDebug(runtime) << "Rig Open Error" << lastErrorText; return false; } } } else { lastErrorText = tr("Unsupported Rig Driver"); qCDebug(runtime) << "Rig Open Error" << lastErrorText; return false; } if ( rig_set_conf(rig, rig_token_lookup(rig, "auto_power_on"), "1") != RIG_OK ) { lastErrorText = tr("Cannot set auto_power_on"); qCDebug(runtime) << "Rig Open Error" << lastErrorText; // return false; ignore the error - no critical } #if 0 if ( rig_set_conf(rig, rig_token_lookup(rig, "no_xchg"), "1") != RIG_OK ) { lastErrorText = tr("Cannot set no_xchg to 1"); qCDebug(runtime) << "Rig Open Error" << lastErrorText; // return false; ignore the error - no critical } #endif int status = rig_open(rig); if ( !isRigRespOK(status, tr("Rig Open Error"), false) ) return false; qCDebug(runtime) << "Rig Open - OK"; opened = true; currRIT = MHz(rigProfile.ritOffset); currXIT = MHz(rigProfile.xitOffset); morseOverCatSupported = ( rig->caps->send_morse != nullptr ); rmode_t localRigModes = RIG_MODE_NONE; localRigModes = static_cast(rig->state.mode_list); // static_cast is due to the old Hamlib versions // where mode_list is defined as INT /* hamlib 3.x and 4.x are very different - workaround */ for ( unsigned char i = 0; i < (sizeof(rmode_t)*8)-1; i++ ) { /* hamlib 3.x and 4.x are very different - workaround */ const char *ms = rig_strrmode(static_cast(localRigModes & rig_idx2setting(i))); if (!ms || !ms[0]) continue; qCDebug(runtime) << "Supported Mode :" << ms; modeList.append(QString(ms)); } connect(&timer, &QTimer::timeout, this, &HamlibRigDrv::checkRigStateChange); timer.start(rigProfile.pollInterval); emit rigIsReady(); return true; } bool HamlibRigDrv::isMorseOverCatSupported() { FCT_IDENTIFICATION; MUTEXLOCKER; return morseOverCatSupported; } QStringList HamlibRigDrv::getAvailableModes() { FCT_IDENTIFICATION; MUTEXLOCKER; return modeList; } vfo_t HamlibRigDrv::getTxVfo() const { FCT_IDENTIFICATION; // TX VFO is always B/SUB — setSplit() forces RX to A/MAIN before // enabling split, so we always know where TX lives. vfo_t txVfo = (rig->state.vfo_list & RIG_VFO_B) ? RIG_VFO_B : RIG_VFO_SUB; qCDebug(runtime) << "txVfo:" << hamlibVFO2String(txVfo); return txVfo; } void HamlibRigDrv::setFrequency(double newFreq) { FCT_IDENTIFICATION; setFrequency(VFO1, newFreq); } void HamlibRigDrv::setFrequency(VFOID vfoid, double newFreq) { FCT_IDENTIFICATION; qCDebug(function_parameters) << vfoid << newFreq; if ( !rigProfile.getFreqInfo ) return; MUTEXLOCKER; if ( !rig ) { qCWarning(runtime) << "Rig is not active"; return; } if ( vfoid == VFO1 && newFreq == currFreq ) return; if ( vfoid == VFO2 && newFreq == currTxFreq ) return; if ( vfoid == VFO2 ) { int status = rig_set_freq(rig, RIG_VFO_TX, newFreq); isRigRespOK(status, tr("Set TX Frequency Error"), false); } else { int status = rig_set_freq(rig, RIG_VFO_CURR, newFreq); isRigRespOK(status, tr("Set Frequency Error"), false); } commandSleep(); } void HamlibRigDrv::setSplit(bool enabled) { FCT_IDENTIFICATION; qCDebug(function_parameters) << enabled; if ( !rigProfile.getSplitInfo ) return; MUTEXLOCKER; if ( !rig ) { qCWarning(runtime) << "Rig is not active"; return; } // Force RX to A/MAIN before enabling split — this guarantees // a known VFO state regardless of rig's get_vfo support. // Same strategy as WSJT-X (HamlibTransceiver::do_start). if ( enabled ) { vfo_t rxVfo = (rig->state.vfo_list & RIG_VFO_A) ? RIG_VFO_A : RIG_VFO_MAIN; int rcVfo = rig_set_vfo(rig, rxVfo); qCDebug(runtime) << "Forced RX VFO to" << hamlibVFO2String(rxVfo) << "result:" << rcVfo; commandSleep(); } vfo_t txVfo = getTxVfo(); split_t splitVal = enabled ? RIG_SPLIT_ON : RIG_SPLIT_OFF; int status = rig_set_split_vfo(rig, RIG_VFO_TX, splitVal, txVfo); isRigRespOK(status, tr("Set Split Error"), false); futureSplit = (status == RIG_OK && enabled); commandSleep(); } void HamlibRigDrv::setRawMode(const QString &rawMode) { FCT_IDENTIFICATION; qCDebug(function_parameters) << rawMode; if ( !rigProfile.getModeInfo ) return; MUTEXLOCKER; if ( !rig ) { qCWarning(runtime) << "Rig is not active"; return; } __setMode(rig_parse_mode(rawMode.toLatin1())); } void HamlibRigDrv::setMode(const QString &mode, const QString &subMode, bool digiVariant) { FCT_IDENTIFICATION; qCDebug(function_parameters) << mode << subMode << digiVariant; QString innerSubmode(subMode); if ( digiVariant && (innerSubmode == "USB" || innerSubmode == "LSB") && modeList.contains("PKT" + subMode) ) innerSubmode.prepend("PKT"); setRawMode((innerSubmode.isEmpty()) ? mode : innerSubmode); } void HamlibRigDrv::__setMode(rmode_t newModeID) { FCT_IDENTIFICATION; if ( newModeID != RIG_MODE_NONE && newModeID != currModeId ) { int status = rig_set_mode(rig, RIG_VFO_CURR, newModeID, RIG_PASSBAND_NOCHANGE); isRigRespOK(status, tr("Set Mode Error"), false); commandSleep(); } #if 0 // SPLIT MODE // sync Split VFO mode // The information about the split change may not // have arrived yet, but the VFO can now be set. // That's why futureSplit was used if ( futureSplit ) { // There muse be VFO_B otherwise it does not work on 4.5.x. RIG_VFO_TX does not work too. // But no problem because primary is switched to VFOA int status = rig_set_split_mode(rig, RIG_VFO_B, newModeID, RIG_PASSBAND_NOCHANGE); isRigRespOK(status, tr("Set Split Mode Error"), false); commandSleep(); } #endif } void HamlibRigDrv::setPTT(bool newPTTState) { FCT_IDENTIFICATION; qCDebug(function_parameters) << newPTTState; if ( !rigProfile.getPTTInfo || PTTPORT(rig)->type.ptt == RIG_PTT_NONE ) return; MUTEXLOCKER; if ( !rig ) { qCWarning(runtime) << "Rig is not active"; return; } int status = rig_set_ptt(rig, RIG_VFO_CURR, (newPTTState ? RIG_PTT_ON : RIG_PTT_OFF)); isRigRespOK(status, tr("Set PTT Error"), false); // wait a moment because Rigs are slow and they are not possible to set and get // mode so quickly commandSleep(); } void HamlibRigDrv::setKeySpeed(qint16 wpm) { FCT_IDENTIFICATION; qCDebug(function_parameters) << wpm; if ( !rigProfile.getKeySpeed ) return; MUTEXLOCKER; if ( !rig ) { qCWarning(runtime) << "Rig is not active"; return; } __setKeySpeed(wpm); } void HamlibRigDrv::syncKeySpeed(qint16 wpm) { FCT_IDENTIFICATION; qCDebug(function_parameters) << wpm; if ( !rigProfile.keySpeedSync ) return; MUTEXLOCKER; if ( !rig ) { qCWarning(runtime) << "Rig is not active"; return; } __setKeySpeed(wpm); } void HamlibRigDrv::sendMorse(const QString &text) { FCT_IDENTIFICATION; qCDebug(function_parameters) << text; QString chpString(text); chpString.remove("\n"); if ( chpString.isEmpty() ) return; MUTEXLOCKER; if ( !rig ) { qCWarning(runtime) << "Rig is not active"; return; } int status = rig_send_morse(rig, RIG_VFO_CURR, chpString.toLocal8Bit().constData()); isRigRespOK(status, tr("Cannot sent Morse"), false); commandSleep(); } void HamlibRigDrv::stopMorse() { FCT_IDENTIFICATION; MUTEXLOCKER; if ( !rig ) { qCWarning(runtime) << "Rig is not active"; return; } #if (HAMLIBVERSION_MAJOR >= 4) int status = rig_stop_morse(rig, RIG_VFO_CURR); isRigRespOK(status, tr("Cannot stop Morse"), false); #endif commandSleep(); } void HamlibRigDrv::sendState() { FCT_IDENTIFICATION; MUTEXLOCKER; forceSendState = true; } void HamlibRigDrv::stopTimers() { FCT_IDENTIFICATION; timer.stop(); errorTimer.stop(); } void HamlibRigDrv::sendDXSpot(const DxSpot &spot) { FCT_IDENTIFICATION; if ( currFreq == 0 ) return; // empty DXSpot; if ( !rig || !opened ) { qCWarning(runtime) << "Rig is not active"; return; } if ( isSmartSDRSlice(rig->caps) ) { #if (HAMLIBVERSION_MAJOR >= 4 && HAMLIBVERSION_MINOR >= 5 ) const QString freqStr = QString::number(spot.freq, 'f', 3); const QString call = spot.callsign.trimmed().toUpper(); const QColor spotColor = Data::statusToColor(spot.status, spot.dupeCount, QColor(187,194,195)); const QString command = QString("C%1|spot add rx_freq=%2 callsign=%3 color=%4 source=QLog timestamp=%5 lifetime_seconds=300 priority=4\n") .arg(++SmartSDRSpotCounter) .arg(freqStr) .arg(call) .arg(spotColor.name(QColor::HexArgb).toUpper()) .arg(spot.dateTime.toSecsSinceEpoch()); qCDebug(runtime) << "Sending DX Spot command:" << command; QByteArray cmdBytes = command.toUtf8(); unsigned char terminator = '\n'; const unsigned char* dataPtr = reinterpret_cast(cmdBytes.constData()); int status = rig_send_raw( rig, dataPtr, cmdBytes.length(), nullptr, 0, &terminator ); if (status != RIG_OK) { qCDebug(runtime) << "rig_send_raw failed:" << status; qCDebug(runtime) << hamlibErrorString(status); } #else qCWarning(runtime) << "Hamlib version does not support rig_send_raw. DX Spot not sent."; #endif } } void HamlibRigDrv::checkChanges() { FCT_IDENTIFICATION; if ( !checkFreqChange() ) return; checkPTTChange(); if ( !checkModeChange() ) return; checkVFOChange(); checkSplitChange(); checkPWRChange(); checkRITChange(); checkXITChange(); checkKeySpeedChange(); } void HamlibRigDrv::checkRigStateChange() { FCT_IDENTIFICATION; if ( !drvLock.tryLock(200) ) { qCDebug(runtime) << "Waited too long"; return; } qCDebug(runtime) << "Getting Rig state"; checkChanges(); forceSendState = false; // restart timer timer.start(rigProfile.pollInterval); drvLock.unlock(); } void HamlibRigDrv::checkErrorCounter() { FCT_IDENTIFICATION; if ( postponedErrors.isEmpty() ) return; qCDebug(runtime) << postponedErrors; // emit only one error auto it = postponedErrors.constBegin(); emit errorOccurred(it.key(), it.value()); postponedErrors.clear(); } void HamlibRigDrv::checkPTTChange() { FCT_IDENTIFICATION; if ( !rig ) { qCWarning(runtime) << "Rig is not active"; return; } if ( rigProfile.getPTTInfo && rig->caps->get_ptt && ( rig->caps->ptt_type == RIG_PTT_RIG || rig->caps->ptt_type == RIG_PTT_RIG_MICDATA) ) { ptt_t pttHamlib; int status = rig_get_ptt(rig, RIG_VFO_CURR, &pttHamlib); if ( isRigRespOK(status, tr("Get PTT Error"), false) ) { bool ptt = ( pttHamlib == RIG_PTT_OFF ) ? false : true; qCDebug(runtime) << "Rig PTT:"<< ptt; qCDebug(runtime) << "Object PTT:"<< currPTT; if ( ptt != currPTT || forceSendState ) { currPTT = ptt; qCDebug(runtime) << "emitting PTT changed" << currPTT; emit pttChanged(currPTT); } } } else qCDebug(runtime) << "Get PTT is disabled"; } bool HamlibRigDrv::checkFreqChange() { FCT_IDENTIFICATION; if ( !rig ) { qCWarning(runtime) << "Rig is not active"; return false; } if ( rigProfile.getFreqInfo && rig->caps->get_freq ) { freq_t vfo_freq; int status = rig_get_freq(rig, RIG_VFO_CURR, &vfo_freq); if ( isRigRespOK(status, tr("Get Frequency Error")) ) { qCDebug(runtime) << "Rig Freq: "<< QSTRING_FREQ(Hz2MHz(vfo_freq)); qCDebug(runtime) << "Object Freq: "<< QSTRING_FREQ(Hz2MHz(currFreq)); if ( vfo_freq != currFreq || forceSendState ) { currFreq = vfo_freq; qCDebug(runtime) << "emitting FREQ changed"; emit frequencyChanged(Hz2MHz(currFreq), Hz2MHz(getRITFreq()), Hz2MHz(getXITFreq())); } } else return false; } else qCDebug(runtime) << "Get Freq is disabled"; return true; } bool HamlibRigDrv::checkModeChange() { FCT_IDENTIFICATION; if ( !rig ) { qCWarning(runtime) << "Rig is not active"; return false; } if ( rigProfile.getModeInfo && rig->caps->get_mode ) { pbwidth_t pbwidth; rmode_t curr_modeId; /* * #999 * This is a workaround for Yaesu FTDx10 to work properly. */ vfo_t modeVFO = ( rigProfile.model == RIG_MODEL_FTDX10 ) ? RIG_VFO_NONE : RIG_VFO_CURR; int status = rig_get_mode(rig, modeVFO, &curr_modeId, &pbwidth); if ( isRigRespOK(status, tr("Get Mode Error")) ) { qCDebug(runtime) << "Rig Mode: "<< curr_modeId << "Rig Filter: "<< pbwidth; qCDebug(runtime) << "Object Mode: "<< currModeId << "Object Filter:" << currPBWidth; if ( curr_modeId != currModeId || ( pbwidth != RIG_PASSBAND_NOCHANGE && pbwidth != currPBWidth ) || forceSendState ) { // mode change currModeId = curr_modeId; currPBWidth = pbwidth; QString submode; const QString mode = getModeNormalizedText(currModeId, submode); const QString &rawModeText = hamlibMode2String(currModeId); qCDebug(runtime) << "emitting MODE changed" << rawModeText << mode << submode << currPBWidth; emit modeChanged(rawModeText, mode, submode, currPBWidth); } } else return false; } else qCDebug(runtime) << "Get Mode is disabled"; return true; } void HamlibRigDrv::checkVFOChange() { FCT_IDENTIFICATION; if ( !rig ) { qCWarning(runtime) << "Rig is not active"; return; } if ( rigProfile.getVFOInfo && rig->caps->get_vfo ) { vfo_t curr_vfo; int status = rig_get_vfo(rig, &curr_vfo); if ( isRigRespOK(status, tr("Get VFO Error"), false) ) { qCDebug(runtime) << "Rig VFO: "<< curr_vfo; qCDebug(runtime) << "Object VFO: "<< currVFO; if ( curr_vfo != currVFO || forceSendState ) { currVFO = curr_vfo; const QString rawVFOText = hamlibVFO2String(currVFO); qCDebug(runtime) << "emitting VFO changed" << rawVFOText; emit vfoChanged(rawVFOText); } } } else qCDebug(runtime) << "Get VFO is disabled"; } void HamlibRigDrv::checkPWRChange() { FCT_IDENTIFICATION; if ( !rig ) { qCWarning(runtime) << "Rig is not active"; return; } if ( rigProfile.getPWRInfo && rig_has_get_level(rig, RIG_LEVEL_RFPOWER) && rig->caps->power2mW ) { value_t rigPowerLevel; unsigned int rigPower; int status = rig_get_level(rig, RIG_VFO_CURR, RIG_LEVEL_RFPOWER, &rigPowerLevel); if ( isRigRespOK(status, tr("Get PWR Error"), false) ) { status = rig_power2mW(rig, &rigPower, rigPowerLevel.f, currFreq, currModeId); if ( isRigRespOK(status, tr("Get PWR (power2mw) Error"), false) ) { qCDebug(runtime) << "Rig PWR: "<< rigPower; qCDebug(runtime) << "Object PWR: "<< currPWR; if ( rigPower != currPWR || forceSendState ) { currPWR = rigPower; qCDebug(runtime) << "emitting PWR changed " << mW2W(currPWR); emit powerChanged(mW2W(currPWR)); } } } } else qCDebug(runtime) << "Get PWR is disabled"; } void HamlibRigDrv::checkRITChange() { FCT_IDENTIFICATION; if ( !rig ) { qCWarning(runtime) << "Rig is not active"; return; } if ( rigProfile.getRITInfo && rig->caps->get_rit && rig_has_get_func(rig, RIG_FUNC_RIT) ) { int ritStatus; shortfreq_t rit = s_Hz(0); int status = rig_get_func(rig, RIG_VFO_CURR, RIG_FUNC_RIT, &ritStatus); if ( isRigRespOK(status, tr("Get RIT Function Error"), false) ) { if ( ritStatus ) { /* RIT is on */ status = rig_get_rit(rig, RIG_VFO_CURR, &rit); if ( !isRigRespOK(status, tr("Get RIT Error"), false) ) rit = s_Hz(0); } else { /* RIT is off */ rit = s_Hz(0); } qCDebug(runtime) << "Rig RIT:"<< rit << "Rig RIT Status:" << ritStatus; qCDebug(runtime) << "Object RIT: "<< currRIT; if ( static_cast(rit) != currRIT || forceSendState ) { currRIT = static_cast(rit); qCDebug(runtime) << "emitting RIT changed" << QSTRING_FREQ(Hz2MHz(currRIT)); qCDebug(runtime) << "emitting FREQ changed " << QSTRING_FREQ(Hz2MHz(currFreq)) << QSTRING_FREQ(Hz2MHz(getRITFreq())) << QSTRING_FREQ(Hz2MHz(getXITFreq())); emit ritChanged(Hz2MHz(currRIT)); emit frequencyChanged(Hz2MHz(currFreq), Hz2MHz(getRITFreq()), Hz2MHz(getXITFreq())); } } } else qCDebug(runtime) << "Get RIT is disabled"; } void HamlibRigDrv::checkXITChange() { FCT_IDENTIFICATION; if ( !rig ) { qCWarning(runtime) << "Rig is not active"; return; } if ( rigProfile.getXITInfo && rig->caps->get_xit && rig_has_get_func(rig, RIG_FUNC_XIT) ) { int xitStatus; shortfreq_t xit = s_Hz(0); int status = rig_get_func(rig, RIG_VFO_CURR, RIG_FUNC_XIT, &xitStatus); if ( isRigRespOK(status, tr("Get XIT Function Error"), false) ) { if ( xitStatus ) { /* XIT is on */ status = rig_get_xit(rig, RIG_VFO_CURR, &xit); if ( !isRigRespOK(status, tr("Get XIT Error"), false) ) xit = s_Hz(0); } else { /* XIT is off */ xit = s_Hz(0); } qCDebug(runtime) << "RIG XIT: "<< xit << "Rig XIT Status:" << xitStatus;; qCDebug(runtime) << "Object XIT: "<< currXIT; if ( static_cast(xit) != currXIT || forceSendState ) { currXIT = static_cast(xit); qCDebug(runtime) << "emitting XIT changed" << QSTRING_FREQ(Hz2MHz(currXIT)); qCDebug(runtime) << "emitting FREQ changed " << QSTRING_FREQ(Hz2MHz(currFreq)) << QSTRING_FREQ(Hz2MHz(getRITFreq())) << QSTRING_FREQ(Hz2MHz(getXITFreq())); emit xitChanged(Hz2MHz(currXIT)); emit frequencyChanged(Hz2MHz(currFreq), Hz2MHz(getRITFreq()), Hz2MHz(getXITFreq())); } } } else qCDebug(runtime) << "Get XIT is disabled"; } void HamlibRigDrv::checkSplitChange() { FCT_IDENTIFICATION; if ( !rig ) { qCWarning(runtime) << "Rig is not active"; return; } if ( !rigProfile.getSplitInfo ) { qCDebug(runtime) << "Get SPLIT is disabled"; return; } split_t splitStatus = RIG_SPLIT_OFF; vfo_t splitVfo = RIG_VFO_NONE; int status = rig_get_split_vfo(rig, RIG_VFO_CURR, &splitStatus, &splitVfo); if ( !isRigRespOK(status, tr("Get Split Error"), false) ) { qCDebug(runtime) << "Get Split is not supported or failed"; return; } bool newSplitEnabled = (splitStatus == RIG_SPLIT_ON); qCDebug(runtime) << "Rig Split:" << newSplitEnabled << "Split VFO:" << splitVfo; qCDebug(runtime) << "Object Split:" << currSplitEnabled; if ( newSplitEnabled != currSplitEnabled || forceSendState ) { currSplitEnabled = newSplitEnabled; futureSplit = newSplitEnabled; qCDebug(runtime) << "emitting SPLIT changed" << currSplitEnabled; emit splitChanged(currSplitEnabled); } if ( currSplitEnabled ) { // Use rig_get_split_freq instead of rig_get_freq(RIG_VFO_TX). // On rigs without get_vfo (e.g. Icom), rig_get_split_vfo returns // a hardcoded splitVfo, so rig_get_freq(RIG_VFO_TX) // reads the wrong VFO // rig_get_split_freq uses the backend's CI-V "unselected VFO" read, // which correctly returns the other VFO's frequency. freq_t txFreq; status = rig_get_split_freq(rig, RIG_VFO_CURR, &txFreq); if ( isRigRespOK(status, tr("Get TX Frequency Error"), false) ) { qCDebug(runtime) << "Rig TX Freq:" << QSTRING_FREQ(Hz2MHz(txFreq)); qCDebug(runtime) << "Object TX Freq:" << QSTRING_FREQ(Hz2MHz(currTxFreq)); if ( txFreq != currTxFreq || forceSendState ) { currTxFreq = txFreq; qCDebug(runtime) << "emitting TX FREQ changed"; emit txFrequencyChanged(Hz2MHz(currTxFreq)); } } } else if ( currTxFreq != Hz(0) ) { // Split was turned off - reset TX freq currTxFreq = Hz(0); } } void HamlibRigDrv::checkKeySpeedChange() { FCT_IDENTIFICATION; if ( !rig ) { qCWarning(runtime) << "Rig is not active"; return; } if ( rigProfile.getKeySpeed && rig_has_get_level(rig, RIG_LEVEL_KEYSPD) ) { value_t rigKeySpeed; int status = rig_get_level(rig, RIG_VFO_CURR, RIG_LEVEL_KEYSPD, &rigKeySpeed); if ( isRigRespOK(status, tr("Get KeySpeed Error"), false) ) { qCDebug(runtime) << "RIG Key Speed: "<< rigKeySpeed.i; qCDebug(runtime) << "Object Key Speed: "<< keySpeed; if ( static_cast(rigKeySpeed.i) != keySpeed || forceSendState ) { keySpeed = static_cast(rigKeySpeed.i); emit keySpeedChanged(keySpeed); } } } else qCDebug(runtime) << "Get KeySpeed is disabled"; } double HamlibRigDrv::getRITFreq() { FCT_IDENTIFICATION; return currFreq + currRIT; } void HamlibRigDrv::setRITFreq(double rit) { currRIT = rit; } double HamlibRigDrv::getXITFreq() { return currFreq + currXIT; } void HamlibRigDrv::setXITFreq(double xit) { currXIT = xit; } void HamlibRigDrv::__setKeySpeed(qint16 wpm) { FCT_IDENTIFICATION; qCDebug(function_parameters) << wpm; if ( wpm < 0 ) { return; } value_t hamlibWPM; hamlibWPM.i = wpm; int status = rig_set_level(rig, RIG_VFO_CURR, RIG_LEVEL_KEYSPD, hamlibWPM); isRigRespOK(status, tr("Set KeySpeed Error"), false); commandSleep(); } void HamlibRigDrv::commandSleep() { FCT_IDENTIFICATION; QThread::msleep(200); } bool HamlibRigDrv::isRigRespOK(int errorStatus, const QString &errorName, bool emitError) { FCT_IDENTIFICATION; qCDebug(function_parameters) << errorStatus << errorName << emitError; if ( errorStatus == RIG_OK ) { if ( emitError ) postponedErrors.remove(errorName); return true; } lastErrorText = hamlibErrorString(errorStatus); if ( emitError ) { qCDebug(runtime) << "Emit Error detected"; if ( !RIG_IS_SOFT_ERRCODE(-errorStatus) ) { // hard error, emit error now qCDebug(runtime) << "Hard Error"; emit errorOccurred(errorName, lastErrorText); } else { // soft error postponedErrors[errorName] = lastErrorText; if ( !errorTimer.isActive() ) { qCDebug(runtime) << "Starting Error Timer"; errorTimer.start(15 * 1000); //15s } } } return false; } bool HamlibRigDrv::isSmartSDRSlice(const rig_caps *caps) { #if (HAMLIBVERSION_MAJOR >= 4 && HAMLIBVERSION_MINOR >= 6 ) // Hamlib 4.6 implements SmartSDR Slices. return QString::fromLatin1(caps->model_name).contains("SmartSDR Slice", Qt::CaseInsensitive); #else Q_UNUSED(caps) return false; #endif } bool HamlibRigDrv::isCIVAddrRig(const rig_caps *caps) { FCT_IDENTIFICATION; const QString &modelName = QString::fromLatin1(caps->mfg_name); return modelName.contains("Icom", Qt::CaseInsensitive) || modelName.contains("Ten-Tec", Qt::CaseInsensitive); /* rigctl: some Ten-Tec rigs so QLog will enable it for all Ten-Tec */ } const QString HamlibRigDrv::getModeNormalizedText(const rmode_t mode, QString &submode) const { submode = QString(); switch ( mode ) { case RIG_MODE_AM: return "AM"; case RIG_MODE_CW: return "CW"; case RIG_MODE_USB: {submode = "USB"; return "SSB";} case RIG_MODE_LSB: {submode = "LSB"; return "SSB";} case RIG_MODE_RTTY: return "RTTY"; case RIG_MODE_FM: return "FM"; case RIG_MODE_WFM: return "FM"; case RIG_MODE_CWR: return "CW"; case RIG_MODE_RTTYR: return "RTTY"; case RIG_MODE_AMS: return "AM"; case RIG_MODE_PKTLSB: {submode = "LSB"; return "SSB";} case RIG_MODE_PKTUSB: {submode = "USB"; return "SSB";} case RIG_MODE_PKTFM: return "FM"; case RIG_MODE_ECSSUSB: {submode = "USB"; return "SSB";} case RIG_MODE_ECSSLSB: {submode = "LSB"; return "SSB";} case RIG_MODE_FAX: return ""; case RIG_MODE_SAM: return ""; case RIG_MODE_SAL: return "AM"; case RIG_MODE_SAH: return "AM"; case RIG_MODE_DSB: return ""; case RIG_MODE_FMN: return "FM"; case RIG_MODE_PKTAM: return "AM"; default : return QString(); } } const QString HamlibRigDrv::hamlibMode2String(const rmode_t mode) const { const char *rawMode = rig_strrmode(mode); return QString(rawMode); } const QString HamlibRigDrv::hamlibVFO2String(const vfo_t vfo) const { const char *rawVFO = rig_strvfo(vfo); return QString(rawVFO); } serial_handshake_e HamlibRigDrv::stringToHamlibFlowControl(const QString &in_flowcontrol) { FCT_IDENTIFICATION; qCDebug(function_parameters) << in_flowcontrol; const QString &flowcontrol = in_flowcontrol.toLower(); if ( flowcontrol == SerialPort::SERIAL_FLOWCONTROL_SOFTWARE ) return RIG_HANDSHAKE_XONXOFF; if ( flowcontrol == SerialPort::SERIAL_FLOWCONTROL_HARDWARE ) return RIG_HANDSHAKE_HARDWARE; return RIG_HANDSHAKE_NONE; } serial_parity_e HamlibRigDrv::stringToHamlibParity(const QString &in_parity) { FCT_IDENTIFICATION; qCDebug(function_parameters) << in_parity; const QString &parity = in_parity.toLower(); if ( parity == SerialPort::SERIAL_PARITY_EVEN ) return RIG_PARITY_EVEN; if ( parity == SerialPort::SERIAL_PARITY_ODD ) return RIG_PARITY_ODD; if ( parity == SerialPort::SERIAL_PARITY_MARK ) return RIG_PARITY_MARK; if ( parity == SerialPort::SERIAL_PARITY_SPACE ) return RIG_PARITY_SPACE; return RIG_PARITY_NONE; } serial_control_state_e HamlibRigDrv::stringToHamlibSerialSignal(const QString &signalString) { FCT_IDENTIFICATION; qCDebug(function_parameters) << signalString; if ( signalString == SerialPort::SERIAL_SIGNAL_HIGH ) return RIG_SIGNAL_ON; if ( signalString == SerialPort::SERIAL_SIGNAL_LOW ) return RIG_SIGNAL_OFF; return RIG_SIGNAL_UNSET; } QString HamlibRigDrv::hamlibErrorString(int errorCode) { FCT_IDENTIFICATION; qCDebug(function_parameters) << errorCode; QString ret; QString detail(rigerror(errorCode)); #if ( HAMLIBVERSION_MAJOR >= 4 && HAMLIBVERSION_MINOR >= 5 ) // The rigerror has different behavior since 4.5. It contains the stack trace in the first part // Need to use rigerror2 ret = QString(rigerror2(errorCode)); #else static QRegularExpression re("[\r\n]"); QStringList errorList = detail.split(re); if ( errorList.size() >= 1 ) ret = errorList.at(0); #endif qWarning() << "Detail" << detail; qCWarning(runtime) << ret; return ret; } #undef HAMLIB_FILPATHLEN #undef RIG_IS_SOFT_ERRCODE #undef PTTPORT #undef MUTEXLOCKER ================================================ FILE: rig/drivers/HamlibRigDrv.h ================================================ #ifndef RIG_DRIVERS_HAMLIBRIGDRV_H #define RIG_DRIVERS_HAMLIBRIGDRV_H #include #include #include #include "GenericRigDrv.h" #include "rig/RigCaps.h" class HamlibRigDrv : public GenericRigDrv { public: static QList> getModelList(); static QList > getPTTTypeList(); static RigCaps getCaps(int model); static int RIGCTLD_MODEL; static int DUMMY_MODEL; static bool isSmartSDRSlice(const struct rig_caps *caps); static bool isCIVAddrRig(const struct rig_caps *caps); explicit HamlibRigDrv(const RigProfile &profile, QObject *parent = nullptr); virtual ~HamlibRigDrv(); virtual bool open() override; virtual bool isMorseOverCatSupported() override; virtual QStringList getAvailableModes() override; virtual void setFrequency(double) override; virtual void setFrequency(VFOID, double) override; virtual void setSplit(bool) override; virtual void setRawMode(const QString &) override; virtual void setMode(const QString &, const QString &, bool) override; virtual void setPTT(bool) override; virtual void setKeySpeed(qint16 wpm) override; virtual void syncKeySpeed(qint16 wpm) override; virtual void sendMorse(const QString &) override; virtual void stopMorse() override; virtual void sendState() override; virtual void stopTimers() override; virtual void sendDXSpot(const DxSpot &spot) override; private slots: void checkRigStateChange(); void checkErrorCounter(); private: // https://github.com/Hamlib/Hamlib/issues/1647 // use a newer HAMLIB API rig_list_foreach_model from 4.2 #if ( HAMLIBVERSION_MAJOR >= 4 && HAMLIBVERSION_MINOR >= 2 ) static int addRig (const rig_model_t rigModel, void *data); #else static int addRig(const rig_caps *caps, void* data); #endif void checkPTTChange(); bool checkFreqChange(); bool checkModeChange(); void checkVFOChange(); void checkSplitChange(); void checkPWRChange(); void checkRITChange(); void checkXITChange(); void checkKeySpeedChange(); void checkChanges(); double getRITFreq(); void setRITFreq(double); double getXITFreq(); void setXITFreq(double); void __setKeySpeed(qint16 wpm); void __setMode(rmode_t newModeID); void commandSleep(); bool isRigRespOK(int errorStatus, const QString &errorName, bool emitError = true); const QString getModeNormalizedText(const rmode_t mode, QString &submode) const; const QString hamlibMode2String(const rmode_t mode) const; const QString hamlibVFO2String(const vfo_t vfo) const; vfo_t getTxVfo() const; serial_handshake_e stringToHamlibFlowControl(const QString &in_flowcontrol); serial_parity_e stringToHamlibParity(const QString &in_parity); serial_control_state_e stringToHamlibSerialSignal(const QString &signalString); QString hamlibErrorString(int); RIG* rig; QTimer timer; QTimer errorTimer; quint32 SmartSDRSpotCounter; bool forceSendState; bool currPTT; double currFreq; pbwidth_t currPBWidth; rmode_t currModeId; vfo_t currVFO; unsigned int currPWR; double currRIT; double currXIT; unsigned int keySpeed; bool morseOverCatSupported; bool currSplitEnabled; bool futureSplit; double currTxFreq; QMutex drvLock; QHashpostponedErrors; QStringList modeList; }; #endif // RIG_DRIVERS_HAMLIBRIGDRV_H ================================================ FILE: rig/drivers/OmniRigEventSink.h ================================================ #ifndef OMNIRIGEVENTSINK_H #define OMNIRIGEVENTSINK_H #include #include // Generic IDispatch sink for OmniRig event interfaces. // Owner class must implement rigTypeChange(int), rigStatusChange(int) and rigParamsChange(int, int). template class OmniRigEventSink : public IDispatch { public: explicit OmniRigEventSink(Owner *owner) : refCount(1), owner(owner) {} HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) override { if ( !ppvObject ) return E_POINTER; if ( riid == IID_IUnknown || riid == IID_IDispatch || riid == __uuidof(EventInterface) ) { *ppvObject = static_cast(this); AddRef(); return S_OK; } *ppvObject = nullptr; return E_NOINTERFACE; } ULONG STDMETHODCALLTYPE AddRef() override { return InterlockedIncrement(&refCount); } ULONG STDMETHODCALLTYPE Release() override { ULONG r = InterlockedDecrement(&refCount); if ( r == 0 ) delete this; return r; } // IDispatch HRESULT STDMETHODCALLTYPE GetTypeInfoCount(UINT *pctinfo) override { if ( pctinfo ) *pctinfo = 0; return S_OK; } HRESULT STDMETHODCALLTYPE GetTypeInfo(UINT, LCID, ITypeInfo **) override { return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE GetIDsOfNames(REFIID, LPOLESTR *, UINT, LCID, DISPID *) override { return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE Invoke(DISPID dispIdMember, REFIID, LCID, WORD, DISPPARAMS *pDispParams, VARIANT *, EXCEPINFO *, UINT *) override { qCDebug(runtime) << "OmniRig event: dispId =" << dispIdMember << "argc =" << (pDispParams ? (int)pDispParams->cArgs : -1); if ( !owner ) return S_OK; // DispIDs from IDL: // 1 VisibleChange() // 2 RigTypeChange(long RigNumber) // 3 StatusChange(long RigNumber) // 4 ParamsChange(long RigNumber, long Params) // 5 CustomReply(long RigNumber, VARIANT Command, VARIANT Reply) switch (dispIdMember) { case 1: // VisibleChange // not-used break; case 2: // RigTypeChange if ( pDispParams && pDispParams->cArgs == 1 ) owner->rigTypeChange((int)pDispParams->rgvarg[0].lVal); break; case 3: // StatusChange if ( pDispParams && pDispParams->cArgs == 1 ) owner->rigStatusChange((int)pDispParams->rgvarg[0].lVal); break; case 4: // ParamsChange(RigNumber, Params) if ( pDispParams && pDispParams->cArgs == 2 ) { long params = pDispParams->rgvarg[0].lVal; long rig = pDispParams->rgvarg[1].lVal; owner->rigParamsChange((int)rig, (int)params); } break; case 5: // CustomReply // not-used break; default: break; } return S_OK; } private: LONG refCount; Owner *owner; }; #endif // OMNIRIGEVENTSINK_H ================================================ FILE: rig/drivers/OmnirigRigDrv.cpp ================================================ // This module is compiled only under Windows - therefore no ifdef related to Windows is needed #define NOMINMAX #include #include #include #include #include #include #include #include "OmnirigRigDrv.h" #include "core/debug.h" #include "rig/macros.h" #if 0 #define MUTEXLOCKER qCDebug(runtime) << "Waiting for Drv mutex"; \ QMutexLocker locker(&drvLock); \ qCDebug(runtime) << "Using Drv" #else #define MUTEXLOCKER qCDebug(runtime) << "Mutex-free"; #endif MODULE_IDENTIFICATION("qlog.rig.driver.omnirigdrv"); // import omnirigv1 #import "C:\\Program Files (x86)\\Afreet\\OmniRig\\OmniRig.exe" raw_interfaces_only named_guids rename_namespace("OmnirigV1") #include "OmniRigEventSink.h" static QString bstrToQString(BSTR b) { if ( !b ) return QString(); QString s = QString::fromWCharArray(b, SysStringLen(b)); SysFreeString(b); return s; } using OmniRigEventSinkV1 = OmniRigEventSink; QList > OmnirigRigDrv::getModelList() { FCT_IDENTIFICATION; QList> ret; ret << QPair(1, tr("Rig 1")) << QPair(2, tr("Rig 2")); return ret; } RigCaps OmnirigRigDrv::getCaps(int) { FCT_IDENTIFICATION; RigCaps ret; ret.isNetworkOnly = true; ret.canGetFreq = true; ret.canGetMode = true; ret.canGetVFO = true; //ret.canGetRIT = true; // temporary disabled because there is not rig with the implemented RitOffset //XIT is not supported by Omnirig lib now ret.canGetPTT = true; ret.canGetSplit = true; return ret; } OmnirigRigDrv::OmnirigRigDrv(const RigProfile &profile, QObject *parent) : GenericRigDrv(profile, parent), currFreq(0), currTxFreq(0), currRIT(0), currXIT(0), currPTT(false), currSplitEnabled(false), futureSplit(false), omniInterface(nullptr), rig(nullptr), eventSink(nullptr), connPoint(nullptr), readableParams(0), writableParams(0), FREQMASK(OmnirigV1::PM_FREQA | OmnirigV1::PM_FREQB | OmnirigV1::PM_FREQ), VFO_A_MASK(OmnirigV1::PM_VFOA | OmnirigV1::PM_VFOAA | OmnirigV1::PM_VFOAB), VFO_B_MASK(OmnirigV1::PM_VFOB | OmnirigV1::PM_VFOBA | OmnirigV1::PM_VFOBB), VFO_SPEC_MASK(OmnirigV1::PM_VFOEQUAL | OmnirigV1::PM_VFOSWAP), ALLVFOsMASK(VFO_A_MASK | VFO_B_MASK | VFO_SPEC_MASK), SPLIT_MASK(OmnirigV1::PM_SPLITON | OmnirigV1::PM_SPLITOFF) { FCT_IDENTIFICATION; HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); if ( FAILED(hr) ) qCWarning(runtime) << "CoInitializeEx failed, hr =" << QString::number(hr, 16); modeMap.insert(OmnirigV1::PM_CW_U, "CWR"); modeMap.insert(OmnirigV1::PM_CW_L, "CW"); modeMap.insert(OmnirigV1::PM_SSB_U, "USB"); modeMap.insert(OmnirigV1::PM_SSB_L, "LSB"); modeMap.insert(OmnirigV1::PM_DIG_U, "DIG_U"); modeMap.insert(OmnirigV1::PM_DIG_L, "DIG_L"); modeMap.insert(OmnirigV1::PM_AM, "AM"); modeMap.insert(OmnirigV1::PM_FM, "FM"); // Timer offlineTimer.setSingleShot(true); QObject::connect(&offlineTimer, &QTimer::timeout, this, [this]() { qCDebug(runtime) << "Offline timer exceeded"; emitDisconnect(); }); hr = CoCreateInstance(__uuidof(OmnirigV1::OmniRigX), nullptr, CLSCTX_LOCAL_SERVER, __uuidof(OmnirigV1::IOmniRigX), reinterpret_cast(&omniInterface)); if ( FAILED(hr) || !omniInterface ) { qCWarning(runtime) << "CoCreateInstance(OmniRig.OmniRigX) failed, hr =" << QString::number(hr, 16); lastErrorText = tr("Initialization Error"); omniInterface = nullptr; return; } // Event sink eventSink = new OmniRigEventSinkV1(this); IConnectionPointContainer *cpc = nullptr; hr = omniInterface->QueryInterface(IID_IConnectionPointContainer, reinterpret_cast(&cpc)); qCDebug(runtime) << "QI(IConnectionPointContainer) hr =" << QString::number(hr, 16); if ( SUCCEEDED(hr) && cpc ) { hr = cpc->FindConnectionPoint(__uuidof(OmnirigV1::IOmniRigXEvents), &connPoint); qCDebug(runtime) << "FindConnectionPoint(IOmniRigXEvents) hr =" << QString::number(hr, 16); if (SUCCEEDED(hr) && connPoint) { hr = connPoint->Advise(eventSink, &connCookie); qCDebug(runtime) << "Advise(IOmniRigXEvents) hr =" << QString::number(hr, 16) << "cookie =" << connCookie; if ( FAILED(hr) ) { qCWarning(runtime) << "IConnectionPoint::Advise failed"; connPoint->Release(); connPoint = nullptr; connCookie = 0; } } cpc->Release(); } } OmnirigRigDrv::~OmnirigRigDrv() { FCT_IDENTIFICATION; if ( !drvLock.tryLock(200) ) { qCDebug(runtime) << "Waited too long"; // better to make a memory leak CoUninitialize(); return; } if ( connPoint && connCookie ) { connPoint->Unadvise(connCookie); connCookie = 0; } if ( connPoint ) { connPoint->Release(); connPoint = nullptr; } if ( eventSink ) { eventSink->Release(); eventSink = nullptr; } if ( rig ) { rig->Release(); rig = nullptr; } if ( omniInterface ) { omniInterface->Release(); omniInterface = nullptr; } CoUninitialize(); drvLock.unlock(); } bool OmnirigRigDrv::open() { FCT_IDENTIFICATION; MUTEXLOCKER; if ( !omniInterface ) { lastErrorText = tr("Initialization Error"); qCDebug(runtime) << "Rig is not initialized"; return false; } long ifaceVer = 0; long swVer = 0; omniInterface->get_InterfaceVersion(&ifaceVer); omniInterface->get_SoftwareVersion(&swVer); quint16 swMajor = static_cast(swVer >> 16); quint16 swMinor = static_cast(swVer & 0xFFFF); quint16 ifMajor = static_cast(ifaceVer >> 8); quint16 ifMinor = static_cast(ifaceVer & 0xFF); qCDebug(runtime) << "Omnirig Version" << swMajor << "." << swMinor << "Interface Version" << ifMajor << "." << ifMinor; if ( rig ) { rig->Release(); rig = nullptr; } HRESULT hr = E_FAIL; switch (rigProfile.model) { case 1: hr = omniInterface->get_Rig1(&rig); break; case 2: hr = omniInterface->get_Rig2(&rig); break; default: hr = E_INVALIDARG; break; } if ( FAILED(hr) || !rig ) { lastErrorText = tr("Initialization Error"); qCDebug(runtime) << "Cannot get Rig Instance, hr =" << QString::number(hr, 16); return false; } __rigTypeChange(rigProfile.model); QTimer::singleShot(500, this, [this]() { this->rigStatusChange(rigProfile.model); }); return true; } bool OmnirigRigDrv::isMorseOverCatSupported() { FCT_IDENTIFICATION; return false; } QStringList OmnirigRigDrv::getAvailableModes() { FCT_IDENTIFICATION; QStringList ret; for ( auto it = modeMap.constBegin(); it != modeMap.constEnd(); ++it ) if ( it.key() & writableParams ) ret.append(it.value()); return ret; } void OmnirigRigDrv::setFrequency(double newFreq) { FCT_IDENTIFICATION; qCDebug(function_parameters) << QSTRING_FREQ(newFreq); if ( !rigProfile.getFreqInfo || !rig ) return; long internalFreq = static_cast(newFreq); qCDebug(runtime) << "Received freq" << internalFreq << "current" << currFreq; if ( internalFreq == currFreq ) return; MUTEXLOCKER; OmnirigV1::RigParamX vfo = OmnirigV1::PM_UNKNOWN; rig->get_Vfo(&vfo); if ( vfo & VFO_B_MASK ) { qCDebug(runtime) << "Setting VFO B Freq"; rig->put_FreqB(internalFreq); } else if ( writableParams & OmnirigV1::PM_FREQA ) { qCDebug(runtime) << "Setting VFO A Freq"; rig->put_FreqA(internalFreq); } else { qCDebug(runtime) << "Setting Generic VFO Freq"; rig->put_Freq(internalFreq); } commandSleep(); } void OmnirigRigDrv::setFrequency(VFOID vfoid, double newFreq) { FCT_IDENTIFICATION; qCDebug(function_parameters) << vfoid << QSTRING_FREQ(newFreq); if ( !rigProfile.getFreqInfo || !rig ) return; if ( vfoid == VFO1 ) { setFrequency(newFreq); return; } // VFO2 — TX frequency (set the non-active VFO) long internalFreq = static_cast(newFreq); MUTEXLOCKER; OmnirigV1::RigParamX currentVfo = OmnirigV1::PM_UNKNOWN; rig->get_Vfo(¤tVfo); const bool isBActive = (currentVfo & VFO_B_MASK); const bool targetIsA = isBActive; qCDebug(runtime) << "Active VFO is" << (isBActive ? "B" : "A") << ", setting TX freq to VFO" << (targetIsA ? "A" : "B"); const bool canDirect =targetIsA ? (writableParams & OmnirigV1::PM_FREQA) : (writableParams & OmnirigV1::PM_FREQB); if ( canDirect ) { if ( targetIsA ) { qCDebug(runtime) << "Direct FreqA"; rig->put_FreqA(internalFreq); } else { qCDebug(runtime) << "Direct FreqB"; rig->put_FreqB(internalFreq); } } else { qCDebug(runtime) << "Swap VFOs"; rig->put_Vfo(OmnirigV1::PM_VFOSWAP); rig->put_Freq(internalFreq); rig->put_Vfo(OmnirigV1::PM_VFOSWAP); } commandSleep(); } void OmnirigRigDrv::setSplit(bool enabled) { FCT_IDENTIFICATION; qCDebug(function_parameters) << enabled; if ( !rigProfile.getSplitInfo || !rig ) return; MUTEXLOCKER; rig->put_Split(enabled ? OmnirigV1::PM_SPLITON : OmnirigV1::PM_SPLITOFF); futureSplit = enabled; commandSleep(); } void OmnirigRigDrv::setRawMode(const QString &rawMode) { FCT_IDENTIFICATION; qCDebug(function_parameters) << rawMode; if ( !rigProfile.getModeInfo || !rig ) return; MUTEXLOCKER; const QList mappedMode = modeMap.keys(rawMode); if (!mappedMode.isEmpty()) { OmnirigV1::RigParamX m = static_cast(mappedMode.at(0)); qCDebug(runtime) << "Mode Found" << m; if ( !(m & writableParams) ) return; qCDebug(runtime) << "Setting Mode"; rig->put_Mode(m); #if 0 // SPLIT MODE // sync Split VFO mode // The information about the split change may not // have arrived yet, but the VFO can now be set. // That's why futureSplit was used if ( futureSplit ) { if (writableParams & (OmnirigV1::PM_VFOA | OmnirigV1::PM_VFOB)) { OmnirigV1::RigParamX currentVfo = OmnirigV1::PM_UNKNOWN; rig->get_Vfo(¤tVfo); const bool isA = (currentVfo & VFO_A_MASK); const auto targetVfo = isA ? OmnirigV1::PM_VFOB : OmnirigV1::PM_VFOA; const auto returnVfo = isA ? OmnirigV1::PM_VFOA : OmnirigV1::PM_VFOB; rig->put_Vfo(targetVfo); rig->put_Mode(m); rig->put_Vfo(returnVfo); } } #endif commandSleep(); } } void OmnirigRigDrv::setMode(const QString &mode, const QString &submode, bool digiVariant) { FCT_IDENTIFICATION; qCDebug(function_parameters) << mode << submode << digiVariant; QString innerSubmode(submode); if ( digiVariant ) { const QString digMode = QLatin1String("DIG_") + innerSubmode.at(0); if ( modeMap.key(digMode) & writableParams ) innerSubmode = digMode; } setRawMode((submode.isEmpty()) ? mode.toUpper() : innerSubmode.toUpper()); } void OmnirigRigDrv::setPTT(bool newPTTState) { FCT_IDENTIFICATION; qCDebug(function_parameters) << newPTTState; if ( !rigProfile.getPTTInfo || !rig ) return; MUTEXLOCKER; rig->put_Tx(newPTTState ? OmnirigV1::PM_TX : OmnirigV1::PM_RX); commandSleep(); } void OmnirigRigDrv::setKeySpeed(qint16) { FCT_IDENTIFICATION; //not implemented return; } void OmnirigRigDrv::syncKeySpeed(qint16) { FCT_IDENTIFICATION; //not implemented return; } void OmnirigRigDrv::sendMorse(const QString &) { FCT_IDENTIFICATION; //not implemented return; } void OmnirigRigDrv::stopMorse() { FCT_IDENTIFICATION; //not implemented return; } void OmnirigRigDrv::sendState() { FCT_IDENTIFICATION; MUTEXLOCKER; checkChanges(0, true); } void OmnirigRigDrv::stopTimers() { FCT_IDENTIFICATION; offlineTimer.stop(); } void OmnirigRigDrv::sendDXSpot(const DxSpot &) { FCT_IDENTIFICATION; //no action } void OmnirigRigDrv::__rigTypeChange(int rigID) { FCT_IDENTIFICATION; qCDebug(function_parameters) << "Rig ID" << rigID; if ( !rig ) { qCWarning(runtime) << "Rig is not active"; return; } if ( rigID != rigProfile.model ) return; long r = 0; long w = 0; rig->get_ReadableParams(&r); rig->get_WriteableParams(&w); readableParams = static_cast(r); writableParams = static_cast(w); qCDebug(runtime) << "R-params" << QString::number(readableParams, 16) << "W-params" << QString::number(writableParams, 16); } void OmnirigRigDrv::commandSleep() { QThread::msleep(200); } const QString OmnirigRigDrv::getModeNormalizedText(const QString &rawMode, QString &submode) { FCT_IDENTIFICATION; qCDebug(function_parameters) << rawMode; submode = QString(); if ( rawMode.contains("CW") ) return "CW"; if ( rawMode == "USB" ) { submode = "USB"; return "SSB"; } if ( rawMode == "LSB" ) { submode = "LSB"; return "SSB"; } if ( rawMode == "AM" ) return "AM"; if ( rawMode == "FM" ) return "FM"; // maybe bad maybe good if ( rawMode == "DIG_U" ) { submode = "USB"; return "SSB"; } // maybe bad maybe good if ( rawMode == "DIG_L" ) { submode = "LSB"; return "SSB"; } return QString(); } void OmnirigRigDrv::rigTypeChange(int rigID) { FCT_IDENTIFICATION; qCDebug(function_parameters) << "Rig ID" << rigID; if ( rigID != rigProfile.model ) return; MUTEXLOCKER; __rigTypeChange(rigID); } void OmnirigRigDrv::rigStatusChange(int rigID) { FCT_IDENTIFICATION; qCDebug(function_parameters) << "Rig ID" << rigID; if ( rigID != rigProfile.model ) return; MUTEXLOCKER; if ( !rig ) { qCWarning(runtime) << "Rig is not active"; return; } OmnirigV1::RigStatusX st = OmnirigV1::ST_NOTCONFIGURED; rig->get_Status(&st); BSTR statusStr = nullptr; rig->get_StatusStr(&statusStr); QString qStatusStr = bstrToQString(statusStr); qCDebug(runtime) << "Rig ID " << rigID; qCDebug(runtime) << "New Status" << st << qStatusStr; if ( OmnirigV1::ST_ONLINE != st ) { qCDebug(runtime) << "New status" << qStatusStr; if (!offlineTimer.isActive()) offlineTimer.start(OFFLINETIMER_TIME_MS); } else { offlineTimer.stop(); emit rigIsReady(); } } void OmnirigRigDrv::checkChanges(int params, bool force) { FCT_IDENTIFICATION; checkSplitChange(params, force); checkFreqChange(params, force); checkModeChange(params, force); checkPTTChange(params, force); checkVFOChange(params, force); checkRITChange(params, force); } void OmnirigRigDrv::rigParamsChange(int rigID, int params) { FCT_IDENTIFICATION; qCDebug(function_parameters) << rigID << params; if ( rigID != rigProfile.model ) return; MUTEXLOCKER; if ( !rig ) { qCWarning(runtime) << "Rig is not active"; return; } checkChanges(params); } bool OmnirigRigDrv::checkFreqChange(int params, bool force) { FCT_IDENTIFICATION; if ( !rig ) { qCWarning(runtime) << "Rig is not active"; return false; } if ( !rigProfile.getFreqInfo ) return true; if ( !force && !(params & (FREQMASK | ALLVFOsMASK) ) ) return true; unsigned int vfo_freq = 0; OmnirigV1::RigParamX vfo = OmnirigV1::PM_UNKNOWN; rig->get_Vfo(&vfo); const bool vfoIsB = (vfo & VFO_B_MASK); long tmp = 0L; if ( vfoIsB ) { qCDebug(runtime) << "Getting VFO B Freq"; rig->get_FreqB(&tmp); if ( !tmp ) { qCDebug(runtime) << "FreqB returned 0, falling back to Freq()"; rig->get_Freq(&tmp); } } else { qCDebug(runtime) << "Getting VFO A Freq"; rig->get_FreqA(&tmp); if ( !tmp ) { qCDebug(runtime) << "FreqA returned 0, falling back to Freq()"; rig->get_Freq(&tmp); } } vfo_freq = static_cast(tmp); qCDebug(runtime) << "Rig Freq: "<< vfo_freq; qCDebug(runtime) << "Object Freq: "<< currFreq; if ( vfo_freq != currFreq || force ) { currFreq = vfo_freq; qCDebug(runtime) << "emitting FREQ changed" << currFreq << Hz2MHz(currFreq); emit frequencyChanged(Hz2MHz(currFreq), Hz2MHz(getRITFreq()), Hz2MHz(getXITFreq())); } // TX frequency (the non-active VFO) when split is active if ( rigProfile.getSplitInfo && currSplitEnabled ) { long txTmp = 0L; if ( vfoIsB ) rig->get_FreqA(&txTmp); else rig->get_FreqB(&txTmp); unsigned int txFreq = static_cast(txTmp); if ( txFreq != currTxFreq || force ) { currTxFreq = txFreq; qCDebug(runtime) << "emitting TX FREQ changed" << currTxFreq << Hz2MHz(currTxFreq); emit txFrequencyChanged(Hz2MHz(currTxFreq)); } } return true; } bool OmnirigRigDrv::checkModeChange(int params, bool force) { FCT_IDENTIFICATION; if ( !rig ) { qCWarning(runtime) << "Rig is not active"; return false; } if ( rigProfile.getModeInfo ) { int inParams = params; if ( force ) { OmnirigV1::RigParamX m = OmnirigV1::PM_UNKNOWN; rig->get_Mode(&m); inParams = m; } QMap::const_iterator it; for ( it = modeMap.begin(); it != modeMap.end(); ++it ) { if ( inParams & it.key() ) { qCDebug(runtime) << "Rig Mode: "<< it.value(); qCDebug(runtime) << "Object Mode: "<< currModeID; if ( currModeID != it.value() || force) { currModeID = it.value(); QString submode; const QString mode = getModeNormalizedText(currModeID, submode); qCDebug(runtime) << "emitting MODE changed" << currModeID << mode << submode << 0; emit modeChanged(currModeID, mode, submode, 0); } break; } } } return true; } void OmnirigRigDrv::checkPTTChange(int params, bool force) { FCT_IDENTIFICATION; if ( !rig ) { qCWarning(runtime) << "Rig is not active"; return; } if ( rigProfile.getPTTInfo && (params & OmnirigV1::PM_RX || params & OmnirigV1::PM_TX || force) ) { int inParams = params; if ( force ) { OmnirigV1::RigParamX tx = OmnirigV1::PM_RX; rig->get_Tx(&tx); inParams = tx; } bool ptt = false; if ( inParams & OmnirigV1::PM_RX ) ptt = false; if ( inParams & OmnirigV1::PM_TX ) ptt = true; qCDebug(runtime) << "Rig PTT: "<< ptt; qCDebug(runtime) << "Object Mode: "<< currPTT; if ( ptt != currPTT || force ) { currPTT = ptt; qCDebug(runtime) << "emitting PTT changed" << currPTT; emit pttChanged(currPTT); } } } void OmnirigRigDrv::checkVFOChange(int params, bool force) { FCT_IDENTIFICATION; if ( !rig ) { qCWarning(runtime) << "Rig is not active"; return; } if ( !rigProfile.getVFOInfo ) return; if ( (params & ALLVFOsMASK) || force ) { int inParams; if (force || (params & VFO_SPEC_MASK)) { OmnirigV1::RigParamX v; rig->get_Vfo(&v); inParams = v; } else { inParams = params; } QString vfo; if ( inParams & VFO_A_MASK ) vfo = "VFOA"; if ( inParams & VFO_B_MASK ) vfo = "VFOB"; qCDebug(runtime) << "Rig VFO: "<< vfo; qCDebug(runtime) << "Object VFO: "<< currVFO; if ( vfo != currVFO || force ) { currVFO = vfo; qCDebug(runtime) << "emitting VFO changed" << currVFO; emit vfoChanged(currVFO); } } } void OmnirigRigDrv::checkRITChange(int params, bool force) { FCT_IDENTIFICATION; if ( !rig ) { qCWarning(runtime) << "Rig is not active"; return; } if (rigProfile.getRITInfo && (params & OmnirigV1::PM_RITON || params & OmnirigV1::PM_RITOFF || force)) { int inParams = params; if ( force ) { OmnirigV1::RigParamX r; rig->get_Rit(&r); inParams = r; } long off = 0; rig->get_RitOffset(&off); unsigned int rit = (inParams & OmnirigV1::PM_RITON) ? static_cast(off) : 0; qCDebug(runtime) << "Rig RIT: "<< rit; qCDebug(runtime) << "Object RIT: "<< currRIT; if ( rit != currRIT || force ) { currRIT = rit; qCDebug(runtime) << "emitting RIT changed" << QSTRING_FREQ(Hz2MHz(currRIT)); qCDebug(runtime) << "emitting FREQ changed " << QSTRING_FREQ(Hz2MHz(currFreq)) << QSTRING_FREQ(Hz2MHz(getRITFreq())) << QSTRING_FREQ(Hz2MHz(getXITFreq())); emit ritChanged(Hz2MHz(currRIT)); emit frequencyChanged(Hz2MHz(currFreq), Hz2MHz(getRITFreq()), Hz2MHz(getXITFreq())); } } } void OmnirigRigDrv::checkSplitChange(int params, bool force) { FCT_IDENTIFICATION; if ( !rig ) { qCWarning(runtime) << "Rig is not active"; return; } if ( !rigProfile.getSplitInfo ) { qCDebug(runtime) << "Get SPLIT is disabled"; return; } if ( (params & SPLIT_MASK) || force ) { int inParams = params; if ( force ) { OmnirigV1::RigParamX s; rig->get_Split(&s); inParams = s; } bool splitEnabled = ( inParams & OmnirigV1::PM_SPLITON ) != 0; qCDebug(runtime) << "Rig Split:" << splitEnabled; qCDebug(runtime) << "Object Split:" << currSplitEnabled; if ( splitEnabled != currSplitEnabled || force ) { currSplitEnabled = splitEnabled; futureSplit = splitEnabled; qCDebug(runtime) << "emitting SPLIT changed" << currSplitEnabled; emit splitChanged(currSplitEnabled); if ( !currSplitEnabled ) currTxFreq = 0; } } } double OmnirigRigDrv::getRITFreq() { FCT_IDENTIFICATION; return currFreq + currRIT; } void OmnirigRigDrv::setRITFreq(double rit) { currRIT = rit; } double OmnirigRigDrv::getXITFreq() { return currFreq + currXIT; } void OmnirigRigDrv::setXITFreq(double xit) { currXIT = xit; } void OmnirigRigDrv::emitDisconnect() { FCT_IDENTIFICATION; if ( !rig ) { qCWarning(runtime) << "Rig is not active"; return; } emit errorOccurred(tr("Rig status changed"), tr("Rig is not connected")); } #undef MUTEXLOCKER ================================================ FILE: rig/drivers/OmnirigRigDrv.h ================================================ #ifndef RIG_DRIVERS_OMNIRIGRIGDRV_H #define RIG_DRIVERS_OMNIRIGRIGDRV_H #include #include #include #include "GenericRigDrv.h" #include "rig/RigCaps.h" // Forward declaration from OmnirigV1 library (getting via #import in .cpp) namespace OmnirigV1 { struct IOmniRigX; struct IRigX; struct IOmniRigXEvents; enum RigParamX; enum RigStatusX; } struct IConnectionPoint; template class OmniRigEventSink; class OmnirigRigDrv : public GenericRigDrv { Q_OBJECT friend class OmniRigEventSink; public: static QList> getModelList(); static RigCaps getCaps(int model); explicit OmnirigRigDrv(const RigProfile &profile, QObject *parent = nullptr); virtual ~OmnirigRigDrv(); virtual bool open() override; virtual bool isMorseOverCatSupported() override; virtual QStringList getAvailableModes() override; virtual void setFrequency(double) override; virtual void setFrequency(VFOID, double) override; virtual void setSplit(bool) override; virtual void setRawMode(const QString &) override; virtual void setMode(const QString &, const QString &, bool) override; virtual void setPTT(bool newPTTState) override; virtual void setKeySpeed(qint16 wpm) override; virtual void syncKeySpeed(qint16 wpm) override; virtual void sendMorse(const QString &) override; virtual void stopMorse() override; virtual void sendState() override; virtual void stopTimers() override; virtual void sendDXSpot(const DxSpot &spot) override; private slots: void rigTypeChange(int); void rigStatusChange(int); void rigParamsChange(int rigID, int params); private: void __rigTypeChange(int); void commandSleep(); const QString getModeNormalizedText(const QString& rawMode, QString &submode); void checkChanges(int, bool force = false); bool checkFreqChange(int, bool); bool checkModeChange(int, bool); void checkPTTChange(int, bool); void checkVFOChange(int, bool); void checkRITChange(int, bool); void checkSplitChange(int, bool); double getRITFreq(); void setRITFreq(double); double getXITFreq(); void setXITFreq(double); void emitDisconnect(); private: unsigned int currFreq; unsigned int currTxFreq; QString currModeID; QString currVFO; unsigned int currRIT; unsigned int currXIT; bool currPTT; bool currSplitEnabled; bool futureSplit; // COM Objects OmnirigV1::IOmniRigX *omniInterface; // the main OmniRigX COM object OmnirigV1::IRigX *rig; // Event sink (IDispatch) defined in OmniRigEventSink.h OmniRigEventSink *eventSink; IConnectionPoint *connPoint; unsigned long connCookie; int readableParams; int writableParams; QMutex drvLock; QTimer offlineTimer; const int FREQMASK; const int VFO_A_MASK; const int VFO_B_MASK; const int VFO_SPEC_MASK; const int ALLVFOsMASK; const int SPLIT_MASK; static const uint OFFLINETIMER_TIME_MS = 10000; // Mode maps QMap modeMap; }; #endif // RIG_DRIVERS_OMNIRIGRIGDRV_H ================================================ FILE: rig/drivers/Omnirigv2RigDrv.cpp ================================================ // This module is compiled only under Windows - therefore no ifdef related to Windows is needed #define NOMINMAX #include #include #include #include #include #include #include #include "Omnirigv2RigDrv.h" #include "core/debug.h" #include "rig/macros.h" #if 0 #define MUTEXLOCKER qCDebug(runtime) << "Waiting for Drv mutex"; \ QMutexLocker locker(&drvLock); \ qCDebug(runtime) << "Using Drv" #else #define MUTEXLOCKER qCDebug(runtime) << "Mutex-free"; #endif MODULE_IDENTIFICATION("qlog.rig.driver.omnirigv2drv"); // import omnirigv2 #import "C:\\Program Files (x86)\\Omni-Rig V2\\omnirig2.exe" raw_interfaces_only named_guids rename_namespace("OmnirigV2") #include "OmniRigEventSink.h" static QString bstrToQString(BSTR b) { if ( !b ) return QString(); QString s = QString::fromWCharArray(b, SysStringLen(b)); SysFreeString(b); return s; } using OmniRigEventSinkV2 = OmniRigEventSink; QList > OmnirigV2RigDrv::getModelList() { FCT_IDENTIFICATION; QList> ret; ret << QPair(1, tr("Rig 1")) << QPair(2, tr("Rig 2")) << QPair(3, tr("Rig 3")) << QPair(4, tr("Rig 4")); return ret; } RigCaps OmnirigV2RigDrv::getCaps(int) { FCT_IDENTIFICATION; RigCaps ret; ret.isNetworkOnly = true; ret.canGetFreq = true; ret.canGetMode = true; ret.canGetVFO = true; //ret.canGetRIT = true; // temporary disabled because there is not rig with the implemented RitOffset //XIT is not supported by Omnirig lib now ret.canGetPTT = true; ret.canGetSplit = true; return ret; } OmnirigV2RigDrv::OmnirigV2RigDrv(const RigProfile &profile, QObject *parent) : GenericRigDrv(profile, parent), currFreq(0), currTxFreq(0), currRIT(0), currXIT(0), currPTT(false), currSplitEnabled(false), currModeID(), currVFO(), omniInterface(nullptr), rig(nullptr), eventSink(nullptr), connPoint(nullptr), connCookie(0), readableParams(0), writableParams(0), FREQMASK(OmnirigV2::PM_FREQA | OmnirigV2::PM_FREQB | OmnirigV2::PM_FREQ), VFO_A_MASK(OmnirigV2::PM_VFOA | OmnirigV2::PM_VFOAA | OmnirigV2::PM_VFOAB), VFO_B_MASK(OmnirigV2::PM_VFOB | OmnirigV2::PM_VFOBA | OmnirigV2::PM_VFOBB), VFO_SPEC_MASK(OmnirigV2::PM_VFOEQUAL | OmnirigV2::PM_VFOSWAP), ALLVFOsMASK(VFO_A_MASK | VFO_B_MASK | VFO_SPEC_MASK), SPLIT_MASK(OmnirigV2::PM_SPLITON | OmnirigV2::PM_SPLITOFF) { FCT_IDENTIFICATION; HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); if ( FAILED(hr) ) qCWarning(runtime) << "CoInitializeEx failed, hr =" << QString::number(hr, 16); modeMap.insert(OmnirigV2::PM_CW_U, "CWR"); modeMap.insert(OmnirigV2::PM_CW_L, "CW"); modeMap.insert(OmnirigV2::PM_SSB_U, "USB"); modeMap.insert(OmnirigV2::PM_SSB_L, "LSB"); modeMap.insert(OmnirigV2::PM_DIG_U, "DIG_U"); modeMap.insert(OmnirigV2::PM_DIG_L, "DIG_L"); modeMap.insert(OmnirigV2::PM_AM, "AM"); modeMap.insert(OmnirigV2::PM_FM, "FM"); // Timer offlineTimer.setSingleShot(true); QObject::connect(&offlineTimer, &QTimer::timeout, this, [this]() { qCDebug(runtime) << "Offline timer exceeded"; emitDisconnect(); }); hr = CoCreateInstance(__uuidof(OmnirigV2::OmniRigX), nullptr, CLSCTX_LOCAL_SERVER, __uuidof(OmnirigV2::IOmniRigX), reinterpret_cast(&omniInterface)); if ( FAILED(hr) || !omniInterface ) { qCWarning(runtime) << "CoCreateInstance(OmniRig.OmniRigX) failed, hr =" << QString::number(hr, 16); lastErrorText = tr("Initialization Error"); omniInterface = nullptr; return; } // Event sink eventSink = new OmniRigEventSinkV2(this); IConnectionPointContainer *cpc = nullptr; hr = omniInterface->QueryInterface(IID_IConnectionPointContainer, reinterpret_cast(&cpc)); qCDebug(runtime) << "QI(IConnectionPointContainer) hr =" << QString::number(hr, 16); if ( SUCCEEDED(hr) && cpc ) { hr = cpc->FindConnectionPoint(__uuidof(OmnirigV2::IOmniRigXEvents), &connPoint); qCDebug(runtime) << "FindConnectionPoint(IOmniRigXEvents) hr =" << QString::number(hr, 16); if (SUCCEEDED(hr) && connPoint) { hr = connPoint->Advise(eventSink, &connCookie); qCDebug(runtime) << "Advise(IOmniRigXEvents) hr =" << QString::number(hr, 16) << "cookie =" << connCookie; if ( FAILED(hr) ) { qCWarning(runtime) << "IConnectionPoint::Advise failed"; connPoint->Release(); connPoint = nullptr; connCookie = 0; } } cpc->Release(); } } OmnirigV2RigDrv::~OmnirigV2RigDrv() { FCT_IDENTIFICATION; if ( !drvLock.tryLock(200) ) { qCDebug(runtime) << "Waited too long"; // better to make a memory leak CoUninitialize(); return; } if ( connPoint && connCookie ) { connPoint->Unadvise(connCookie); connCookie = 0; } if ( connPoint ) { connPoint->Release(); connPoint = nullptr; } if ( eventSink ) { eventSink->Release(); eventSink = nullptr; } if ( rig ) { rig->Release(); rig = nullptr; } if ( omniInterface ) { omniInterface->Release(); omniInterface = nullptr; } CoUninitialize(); drvLock.unlock(); } bool OmnirigV2RigDrv::open() { FCT_IDENTIFICATION; MUTEXLOCKER; if ( !omniInterface ) { lastErrorText = tr("Initialization Error"); qCDebug(runtime) << "Rig is not initialized"; return false; } long ifaceVer = 0; long swVer = 0; omniInterface->get_InterfaceVersion(&ifaceVer); omniInterface->get_SoftwareVersion(&swVer); quint16 swMajor = static_cast(swVer >> 16); quint16 swMinor = static_cast(swVer & 0xFFFF); quint16 ifMajor = static_cast(ifaceVer >> 8); quint16 ifMinor = static_cast(ifaceVer & 0xFF); qCDebug(runtime) << "Omnirig Version" << swMajor << "." << swMinor << "Interface Version" << ifMajor << "." << ifMinor; if ( rig ) { rig->Release(); rig = nullptr; } HRESULT hr = E_FAIL; switch (rigProfile.model) { case 1: hr = omniInterface->get_Rig1(&rig); break; case 2: hr = omniInterface->get_Rig2(&rig); break; case 3: hr = omniInterface->get_Rig3(&rig); break; case 4: hr = omniInterface->get_Rig4(&rig); break; default: hr = E_INVALIDARG; break; } if ( FAILED(hr) || !rig ) { lastErrorText = tr("Initialization Error"); qCDebug(runtime) << "Cannot get Rig Instance, hr =" << QString::number(hr, 16); return false; } __rigTypeChange(rigProfile.model); QTimer::singleShot(500, this, [this]() { this->rigStatusChange(rigProfile.model); }); return true; } bool OmnirigV2RigDrv::isMorseOverCatSupported() { FCT_IDENTIFICATION; return false; } QStringList OmnirigV2RigDrv::getAvailableModes() { FCT_IDENTIFICATION; QStringList ret; for ( auto it = modeMap.constBegin(); it != modeMap.constEnd(); ++it ) if ( it.key() & writableParams ) ret.append(it.value()); return ret; } void OmnirigV2RigDrv::setFrequency(double newFreq) { FCT_IDENTIFICATION; qCDebug(function_parameters) << QSTRING_FREQ(newFreq); if ( !rigProfile.getFreqInfo || !rig ) return; long internalFreq = static_cast(newFreq); qCDebug(runtime) << "Received freq" << internalFreq << "current" << currFreq; if ( internalFreq == currFreq ) return; MUTEXLOCKER; OmnirigV2::RigParamX vfo = OmnirigV2::PM_UNKNOWN; rig->get_Vfo(&vfo); if ( vfo & VFO_B_MASK ) { qCDebug(runtime) << "Setting VFO B Freq"; rig->put_FreqB(internalFreq); } else if ( writableParams & OmnirigV2::PM_FREQA ) { qCDebug(runtime) << "Setting VFO A Freq"; rig->put_FreqA(internalFreq); } else { qCDebug(runtime) << "Setting Generic VFO Freq"; rig->put_Freq(internalFreq); } commandSleep(); } void OmnirigV2RigDrv::setFrequency(VFOID vfoid, double newFreq) { FCT_IDENTIFICATION; qCDebug(function_parameters) << vfoid << QSTRING_FREQ(newFreq); if ( !rigProfile.getFreqInfo || !rig ) return; if ( vfoid == VFO1 ) { setFrequency(newFreq); return; } // VFO2 — TX frequency (set the non-active VFO) long internalFreq = static_cast(newFreq); MUTEXLOCKER; OmnirigV2::RigParamX vfo = OmnirigV2::PM_UNKNOWN; rig->get_Vfo(&vfo); if ( vfo & VFO_B_MASK ) { qCDebug(runtime) << "Active VFO is B, setting TX freq to VFO A"; rig->put_FreqA(internalFreq); } else { qCDebug(runtime) << "Active VFO is A, setting TX freq to VFO B"; rig->put_FreqB(internalFreq); } commandSleep(); } void OmnirigV2RigDrv::setSplit(bool enabled) { FCT_IDENTIFICATION; qCDebug(function_parameters) << enabled; if ( !rigProfile.getSplitInfo || !rig ) return; MUTEXLOCKER; rig->put_Split(enabled ? OmnirigV2::PM_SPLITON : OmnirigV2::PM_SPLITOFF); commandSleep(); } void OmnirigV2RigDrv::setRawMode(const QString &rawMode) { FCT_IDENTIFICATION; qCDebug(function_parameters) << rawMode; if ( !rigProfile.getModeInfo || !rig ) return; MUTEXLOCKER; const QList mappedMode = modeMap.keys(rawMode); if (!mappedMode.isEmpty()) { OmnirigV2::RigParamX m = static_cast(mappedMode.at(0)); qCDebug(runtime) << "Mode Found" << m; if ( m & writableParams ) { qCDebug(runtime) << "Setting Mode"; rig->put_Mode(m); commandSleep(); } } } void OmnirigV2RigDrv::setMode(const QString &mode, const QString &submode, bool digiVariant) { FCT_IDENTIFICATION; qCDebug(function_parameters) << mode << submode << digiVariant; QString innerSubmode(submode); if ( digiVariant && !innerSubmode.isEmpty() ) { const QString digMode = QLatin1String("DIG_") + innerSubmode.at(0); if (modeMap.key(digMode, 0) & writableParams) innerSubmode = digMode; } setRawMode((submode.isEmpty()) ? mode.toUpper() : innerSubmode.toUpper()); } void OmnirigV2RigDrv::setPTT(bool newPTTState) { FCT_IDENTIFICATION; qCDebug(function_parameters) << newPTTState; if ( !rigProfile.getPTTInfo || !rig ) return; MUTEXLOCKER; rig->put_Tx(newPTTState ? OmnirigV2::PM_TX : OmnirigV2::PM_RX); commandSleep(); } void OmnirigV2RigDrv::setKeySpeed(qint16) { FCT_IDENTIFICATION; //not implemented } void OmnirigV2RigDrv::syncKeySpeed(qint16) { FCT_IDENTIFICATION; //not implemented } void OmnirigV2RigDrv::sendMorse(const QString &) { FCT_IDENTIFICATION; //not implemented } void OmnirigV2RigDrv::stopMorse() { FCT_IDENTIFICATION; //not implemented } void OmnirigV2RigDrv::sendState() { FCT_IDENTIFICATION; MUTEXLOCKER; checkChanges(0, true); } void OmnirigV2RigDrv::stopTimers() { FCT_IDENTIFICATION; offlineTimer.stop(); } void OmnirigV2RigDrv::sendDXSpot(const DxSpot &) { FCT_IDENTIFICATION; // no action } void OmnirigV2RigDrv::rigTypeChange(int rigID) { FCT_IDENTIFICATION; qCDebug(function_parameters) << "Rig ID" << rigID; if ( rigID != rigProfile.model ) return; MUTEXLOCKER; __rigTypeChange(rigID); } void OmnirigV2RigDrv::__rigTypeChange(int rigID) { FCT_IDENTIFICATION; qCDebug(function_parameters) << "Rig ID" << rigID; if ( !rig ) { qCWarning(runtime) << "Rig is not active"; return; } if ( rigID != rigProfile.model ) return; long r = 0; long w = 0; rig->get_ReadableParams(&r); rig->get_WriteableParams(&w); readableParams = static_cast(r); writableParams = static_cast(w); qCDebug(runtime) << "R-params" << QString::number(readableParams, 16) << "W-params" << QString::number(writableParams, 16); } void OmnirigV2RigDrv::rigStatusChange(int rigID) { FCT_IDENTIFICATION; qCDebug(function_parameters) << "Rig ID" << rigID; if ( rigID != rigProfile.model ) return; MUTEXLOCKER; if ( !rig ) { qCWarning(runtime) << "Rig is not active"; return; } OmnirigV2::RigStatusX st = OmnirigV2::ST_NOTCONFIGURED; rig->get_Status(&st); BSTR statusStr = nullptr; rig->get_StatusStr(&statusStr); QString qStatusStr = bstrToQString(statusStr); qCDebug(runtime) << "Rig ID " << rigID; qCDebug(runtime) << "New Status" << st << qStatusStr; if ( OmnirigV2::ST_ONLINE != st ) { qCDebug(runtime) << "New status" << qStatusStr; if (!offlineTimer.isActive()) offlineTimer.start(OFFLINETIMER_TIME_MS); } else { offlineTimer.stop(); emit rigIsReady(); } } void OmnirigV2RigDrv::rigParamsChange(int rigID, int params) { FCT_IDENTIFICATION; qCDebug(function_parameters) << rigID << params; if (rigID != rigProfile.model) return; MUTEXLOCKER; if ( !rig ) { qCWarning(runtime) << "Rig is not active"; return; } checkChanges(params); } void OmnirigV2RigDrv::checkChanges(int params, bool force) { FCT_IDENTIFICATION; checkSplitChange(params, force); checkFreqChange(params, force); checkModeChange(params, force); checkPTTChange(params, force); checkVFOChange(params, force); checkRITChange(params, force); } bool OmnirigV2RigDrv::checkFreqChange(int params, bool force) { FCT_IDENTIFICATION; if ( !rig ) { qCWarning(runtime) << "Rig is not active"; return false; } if ( !rigProfile.getFreqInfo ) return true; if ( !force && !( params & (FREQMASK | ALLVFOsMASK) ) ) return true; unsigned int vfo_freq = 0; OmnirigV2::RigParamX vfo = OmnirigV2::PM_UNKNOWN; rig->get_Vfo(&vfo); const bool vfoIsB = (vfo & VFO_B_MASK); long tmp = 0L; if ( vfoIsB ) { qCDebug(runtime) << "Getting VFO B Freq"; rig->get_FreqB(&tmp); if ( !tmp ) { qCDebug(runtime) << "FreqB returned 0, falling back to Freq()"; rig->get_Freq(&tmp); } } else { qCDebug(runtime) << "Getting VFO A Freq"; rig->get_FreqA(&tmp); if ( !tmp ) { qCDebug(runtime) << "FreqA returned 0, falling back to Freq()"; rig->get_Freq(&tmp); } } vfo_freq = static_cast(tmp); qCDebug(runtime) << "Rig Freq: "<< vfo_freq; qCDebug(runtime) << "Object Freq: "<< currFreq; if ( vfo_freq != currFreq || force ) { currFreq = vfo_freq; qCDebug(runtime) << "emitting FREQ changed" << currFreq << Hz2MHz(currFreq); emit frequencyChanged(Hz2MHz(currFreq), Hz2MHz(getRITFreq()), Hz2MHz(getXITFreq())); } // TX frequency (the non-active VFO) when split is active if ( rigProfile.getSplitInfo && currSplitEnabled ) { long txTmp = 0L; if ( vfoIsB ) rig->get_FreqA(&txTmp); else rig->get_FreqB(&txTmp); unsigned int txFreq = static_cast(txTmp); if ( txFreq != currTxFreq || force ) { currTxFreq = txFreq; qCDebug(runtime) << "emitting TX FREQ changed" << currTxFreq << Hz2MHz(currTxFreq); emit txFrequencyChanged(Hz2MHz(currTxFreq)); } } return true; } bool OmnirigV2RigDrv::checkModeChange(int params, bool force) { FCT_IDENTIFICATION; if ( !rig ) { qCWarning(runtime) << "Rig is not active"; return false; } if ( rigProfile.getModeInfo ) { int inParams = params; if ( force ) { OmnirigV2::RigParamX m = OmnirigV2::PM_UNKNOWN; rig->get_Mode(&m); inParams = m; } QMap::const_iterator it; for ( it = modeMap.begin(); it != modeMap.end(); ++it ) { if ( inParams & it.key() ) { qCDebug(runtime) << "Rig Mode: "<< it.value(); qCDebug(runtime) << "Object Mode: "<< currModeID; if ( currModeID != it.value() || force) { currModeID = it.value(); QString submode; const QString mode = getModeNormalizedText(currModeID, submode); qCDebug(runtime) << "emitting MODE changed" << currModeID << mode << submode << 0; emit modeChanged(currModeID, mode, submode, 0); } break; } } } return true; } void OmnirigV2RigDrv::checkPTTChange(int params, bool force) { FCT_IDENTIFICATION; if ( !rig ) { qCWarning(runtime) << "Rig is not active"; return; } if ( rigProfile.getPTTInfo && (params & OmnirigV2::PM_RX || params & OmnirigV2::PM_TX || force) ) { int inParams = params; if ( force ) { OmnirigV2::RigParamX tx = OmnirigV2::PM_RX; rig->get_Tx(&tx); inParams = tx; } bool ptt = false; if ( inParams & OmnirigV2::PM_RX ) ptt = false; if ( inParams & OmnirigV2::PM_TX ) ptt = true; qCDebug(runtime) << "Rig PTT: "<< ptt; qCDebug(runtime) << "Object Mode: "<< currPTT; if ( ptt != currPTT || force ) { currPTT = ptt; qCDebug(runtime) << "emitting PTT changed" << currPTT; emit pttChanged(currPTT); } } } void OmnirigV2RigDrv::checkVFOChange(int params, bool force) { FCT_IDENTIFICATION; if ( !rig ) { qCWarning(runtime) << "Rig is not active"; return; } if ( !rigProfile.getVFOInfo ) return; if ( (params & ALLVFOsMASK) || force ) { int inParams; if (force || ( params & VFO_SPEC_MASK ) ) { OmnirigV2::RigParamX v; rig->get_Vfo(&v); inParams = v; } else { inParams = params; } QString vfo; if ( inParams & VFO_A_MASK ) vfo = "VFOA"; if ( inParams & VFO_B_MASK ) vfo = "VFOB"; qCDebug(runtime) << "Rig VFO: "<< vfo; qCDebug(runtime) << "Object VFO: "<< currVFO; if ( vfo != currVFO || force ) { currVFO = vfo; qCDebug(runtime) << "emitting VFO changed" << currVFO; emit vfoChanged(currVFO); } } } void OmnirigV2RigDrv::checkRITChange(int params, bool force) { FCT_IDENTIFICATION; if ( !rig ) { qCWarning(runtime) << "Rig is not active"; return; } if (rigProfile.getRITInfo && (params & OmnirigV2::PM_RITON || params & OmnirigV2::PM_RITOFF || force)) { int inParams = params; if ( force ) { OmnirigV2::RigParamX r; rig->get_Rit(&r); inParams = r; } long off = 0; rig->get_RitOffset(&off); unsigned int rit = (inParams & OmnirigV2::PM_RITON) ? static_cast(off) : 0; qCDebug(runtime) << "Rig RIT: "<< rit; qCDebug(runtime) << "Object RIT: "<< currRIT; if ( rit != currRIT || force ) { currRIT = rit; qCDebug(runtime) << "emitting RIT changed" << QSTRING_FREQ(Hz2MHz(currRIT)); qCDebug(runtime) << "emitting FREQ changed " << QSTRING_FREQ(Hz2MHz(currFreq)) << QSTRING_FREQ(Hz2MHz(getRITFreq())) << QSTRING_FREQ(Hz2MHz(getXITFreq())); emit ritChanged(Hz2MHz(currRIT)); emit frequencyChanged(Hz2MHz(currFreq), Hz2MHz(getRITFreq()), Hz2MHz(getXITFreq())); } } } void OmnirigV2RigDrv::checkSplitChange(int params, bool force) { FCT_IDENTIFICATION; if ( !rig ) { qCWarning(runtime) << "Rig is not active"; return; } if ( !rigProfile.getSplitInfo ) { qCDebug(runtime) << "Get SPLIT is disabled"; return; } if ( (params & SPLIT_MASK) || force ) { int inParams = params; if ( force ) { OmnirigV2::RigParamX s; rig->get_Split(&s); inParams = s; } bool splitEnabled = ( inParams & OmnirigV2::PM_SPLITON ) != 0; qCDebug(runtime) << "Rig Split:" << splitEnabled; qCDebug(runtime) << "Object Split:" << currSplitEnabled; if ( splitEnabled != currSplitEnabled || force ) { currSplitEnabled = splitEnabled; qCDebug(runtime) << "emitting SPLIT changed" << currSplitEnabled; emit splitChanged(currSplitEnabled); if ( !currSplitEnabled ) currTxFreq = 0; } } } double OmnirigV2RigDrv::getRITFreq() { FCT_IDENTIFICATION; return currFreq + currRIT; } void OmnirigV2RigDrv::setRITFreq(double rit) { currRIT = static_cast(rit); } double OmnirigV2RigDrv::getXITFreq() { return currFreq + currXIT; } void OmnirigV2RigDrv::setXITFreq(double xit) { currXIT = static_cast(xit); } void OmnirigV2RigDrv::emitDisconnect() { FCT_IDENTIFICATION; if ( !rig ) { qCWarning(runtime) << "Rig is not active"; return; } emit errorOccurred(tr("Rig status changed"), tr("Rig is not connected")); } void OmnirigV2RigDrv::commandSleep() { QThread::msleep(200); } const QString OmnirigV2RigDrv::getModeNormalizedText(const QString &rawMode, QString &submode) { FCT_IDENTIFICATION; qCDebug(function_parameters) << rawMode; submode = QString(); if (rawMode.contains(QStringLiteral("CW"))) return QStringLiteral("CW"); if (rawMode == QStringLiteral("USB")) { submode = QStringLiteral("USB"); return QStringLiteral("SSB"); } if (rawMode == QStringLiteral("LSB")) { submode = QStringLiteral("LSB"); return QStringLiteral("SSB"); } if (rawMode == QStringLiteral("AM")) return QStringLiteral("AM"); if (rawMode == QStringLiteral("FM")) return QStringLiteral("FM"); // maybe bad maybe good if (rawMode == QStringLiteral("DIG_U")) { submode = QStringLiteral("USB"); return QStringLiteral("SSB"); } // maybe bad maybe good if (rawMode == QStringLiteral("DIG_L")) { submode = QStringLiteral("LSB"); return QStringLiteral("SSB"); } return QString(); } ================================================ FILE: rig/drivers/Omnirigv2RigDrv.h ================================================ #ifndef OMNIRIGV2RIGRIGDRV_H #define OMNIRIGV2RIGRIGDRV_H #include #include #include #include "GenericRigDrv.h" #include "rig/RigCaps.h" // Forward declaration from OmnirigV2 library (getting via #import in .cpp) namespace OmnirigV2 { struct IOmniRigX; struct IRigX; struct IOmniRigXEvents; enum RigParamX; enum RigStatusX; } struct IConnectionPoint; template class OmniRigEventSink; class OmnirigV2RigDrv : public GenericRigDrv { Q_OBJECT friend class OmniRigEventSink; public: static QList> getModelList(); static RigCaps getCaps(int); explicit OmnirigV2RigDrv(const RigProfile &profile, QObject *parent = nullptr); ~OmnirigV2RigDrv() override; bool open() override; bool isMorseOverCatSupported() override; QStringList getAvailableModes() override; void setFrequency(double) override; void setFrequency(VFOID, double) override; void setSplit(bool) override; void setRawMode(const QString &) override; void setMode(const QString &, const QString &, bool digiVariant) override; void setPTT(bool) override; void setKeySpeed(qint16 wpm) override; void syncKeySpeed(qint16 wpm) override; void sendMorse(const QString &) override; void stopMorse() override; void sendState() override; void stopTimers() override; void sendDXSpot(const DxSpot &spot) override; public slots: void rigTypeChange(int); void rigStatusChange(int); void rigParamsChange(int rigID, int params); private: void __rigTypeChange(int); void commandSleep(); const QString getModeNormalizedText(const QString &rawMode, QString &submode); void checkChanges(int params, bool force = false); bool checkFreqChange(int params, bool force); bool checkModeChange(int params, bool force); void checkPTTChange(int params, bool force); void checkVFOChange(int params, bool force); void checkRITChange(int params, bool force); void checkSplitChange(int params, bool force); double getRITFreq(); void setRITFreq(double); double getXITFreq(); void setXITFreq(double); void emitDisconnect(); private: unsigned int currFreq; unsigned int currTxFreq; unsigned int currRIT; unsigned int currXIT; bool currPTT; bool currSplitEnabled; QString currModeID; QString currVFO; // COM Objects OmnirigV2::IOmniRigX *omniInterface; // the main OmniRigX COM object OmnirigV2::IRigX *rig; // Event sink (IDispatch) defined in OmniRigEventSink.h OmniRigEventSink *eventSink; IConnectionPoint *connPoint; unsigned long connCookie; // Parameters from OmniRig (bitmap) int readableParams; int writableParams; QMutex drvLock; QTimer offlineTimer; const int FREQMASK; const int VFO_A_MASK; const int VFO_B_MASK; const int VFO_SPEC_MASK; const int ALLVFOsMASK; const int SPLIT_MASK; static const uint OFFLINETIMER_TIME_MS = 10000; // Mode maps QMap modeMap; }; #endif // OMNIRIGV2RIGRIGDRV_H ================================================ FILE: rig/drivers/TCIRigDrv.cpp ================================================ #include #include #include "TCIRigDrv.h" #include "rig/macros.h" #include "data/Data.h" #include "data/BandPlan.h" #include "core/debug.h" MODULE_IDENTIFICATION("qlog.rig.driver.tcidrv"); // https://github.com/ExpertSDR3/TCI/blob/main/TCI%20Protocol.pdf QList > TCIRigDrv::getModelList() { FCT_IDENTIFICATION; QList> ret; ret << QPair(1, tr("Rig 0")) // 0 cause a crash in setting dialog when RigModel combo is expacted, I don't know why << QPair(2, tr("Rig 1")) << QPair(3, tr("Rig 2")) << QPair(4, tr("Rig 3")); return ret; } RigCaps TCIRigDrv::getCaps(int) { FCT_IDENTIFICATION; RigCaps ret; ret.isNetworkOnly = true; ret.canGetFreq = true; ret.canGetMode = true; ret.canGetVFO = true; ret.canGetRIT = true; ret.canGetXIT = true; ret.canGetPTT = true; ret.canSendMorse = true; ret.canGetKeySpeed = true; ret.canGetPWR = true; ret.canProcessDXSpot = true; ret.canGetSplit = true; return ret; } TCIRigDrv::TCIRigDrv(const RigProfile &profile, QObject *parent) : GenericRigDrv(profile, parent), ready(false), receivedOnly(false), currFreq(0.0), currTxFreq(0.0), currRIT(0.0), currXIT(0.0), RITEnabled(false), XITEnabled(false), currSplitEnabled(false) { FCT_IDENTIFICATION; connect(&ws, &QWebSocket::connected, this, &TCIRigDrv::onConnected); #if (QT_VERSION < QT_VERSION_CHECK(6, 5, 0)) connect(&ws, QOverload::of(&QWebSocket::error), this, &TCIRigDrv::onSocketError); #else connect(&ws, &QWebSocket::errorOccurred, this, &TCIRigDrv::onSocketError); #endif } TCIRigDrv::~TCIRigDrv() { FCT_IDENTIFICATION; TCIRigDrv::stopTimers(); } bool TCIRigDrv::open() { FCT_IDENTIFICATION; QUrl url; url.setScheme("ws"); url.setHost(rigProfile.hostname); url.setPort(rigProfile.netport); ws.open(url); return true; } bool TCIRigDrv::isMorseOverCatSupported() { FCT_IDENTIFICATION; return true; } QStringList TCIRigDrv::getAvailableModes() { return modeList; } void TCIRigDrv::setFrequency(double newFreq) { FCT_IDENTIFICATION; qCDebug(function_parameters) << newFreq; if ( !rigProfile.getFreqInfo ) return; unsigned long long internalFreq = static_cast(newFreq); if ( internalFreq == currFreq ) { qCDebug(runtime) << "The same Freq - skip change" << internalFreq << currFreq; return; } QStringList args = {"0", //VFOID QString::number(internalFreq) }; sendCmd("vfo", true, args); } void TCIRigDrv::setFrequency(VFOID vfoid, double newFreq) { FCT_IDENTIFICATION; qCDebug(function_parameters) << vfoid << newFreq; if ( !rigProfile.getFreqInfo ) return; if ( vfoid == VFO1 ) { setFrequency(newFreq); return; } // VFO2 — TX frequency unsigned long long internalFreq = static_cast(newFreq); QStringList args = {"1", //VFO2 QString::number(internalFreq) }; sendCmd("vfo", true, args); } void TCIRigDrv::setSplit(bool enabled) { FCT_IDENTIFICATION; qCDebug(function_parameters) << enabled; if ( !rigProfile.getSplitInfo ) return; if ( receivedOnly ) { qCDebug(runtime) << "Only receiver - no action"; return; } QStringList args = {enabled ? "true" : "false"}; sendCmd("split_enable", true, args); } void TCIRigDrv::setRawMode(const QString &rawMode) { FCT_IDENTIFICATION; qCDebug(function_parameters) << rawMode; if ( !rigProfile.getModeInfo || rawMode.isEmpty() ) return; if ( rawMode == currMode ) { qCDebug(runtime) << "The same Mode - skip change" << rawMode << currMode; return; } QStringList args = {rawMode}; sendCmd("modulation", true, args); } void TCIRigDrv::setMode(const QString &mode, const QString &subMode, bool digiVariant) { FCT_IDENTIFICATION; qCDebug(function_parameters) << mode << subMode << digiVariant; setRawMode(mode2RawMode(mode, subMode, digiVariant)); } void TCIRigDrv::setPTT(bool newPTTState) { FCT_IDENTIFICATION; qCDebug(function_parameters) << newPTTState; if ( !rigProfile.getPTTInfo ) return; if ( receivedOnly ) { qCDebug(runtime) << "Only receiver - no action"; return; } QStringList args = {(newPTTState) ? "true" : "false"}; sendCmd("trx", true, args); } void TCIRigDrv::setKeySpeed(qint16 wpm) { FCT_IDENTIFICATION; qCDebug(function_parameters) << wpm; if ( !rigProfile.getKeySpeed ) return; if ( receivedOnly ) { qCDebug(runtime) << "Only receiver - no action"; return; } QStringList args = {QString::number(wpm)}; sendCmd("cw_keyer_speed", false, args); sendCmd("cw_macros_speed", false, args); } void TCIRigDrv::syncKeySpeed(qint16 wpm) { FCT_IDENTIFICATION; qCDebug(function_parameters) << wpm; if ( !rigProfile.keySpeedSync ) return; setKeySpeed(wpm); } void TCIRigDrv::sendMorse(const QString &text) { FCT_IDENTIFICATION; if ( text.isEmpty() ) return; if ( receivedOnly ) { qCDebug(runtime) << "Only receiver - no action"; return; } QStringList args {text}; sendCmd("cw_macros", true, args); } void TCIRigDrv::stopMorse() { FCT_IDENTIFICATION; if ( receivedOnly ) { qCDebug(runtime) << "Only receiver - no action"; return; } sendCmd("cw_macros_stop", false); } void TCIRigDrv::sendState() { FCT_IDENTIFICATION; // it seems that Rig sends its state at the begining of the session // but it has to be implmented due to "Exit From Manual Mode" sendCmd("vfo", true, QStringList("0")); //Freq // sendCmd("trx", true); // PTT // Disabled because if this command is received by Thetis // then Thetis unexpectedly closes the connection. // Expert SDR software does not do this. sendCmd("modulation", true); //Mode //sendCmd(); // Current VFO ???? sendCmd("drive", true); //PWR sendCmd("rit_offset", true); sendCmd("rit_enable", true); sendCmd("xit_offset", true); sendCmd("xit_enable", true); sendCmd("split_enable", true); sendCmd("cw_macros_speed", false); } void TCIRigDrv::stopTimers() { FCT_IDENTIFICATION; // no timer // send STOP command???? // close the WebSocket here becuase this method is called // via queue signals. What caused that there is not warning // that Websocket is destroyed from another thread ws.close(); return; } void TCIRigDrv::sendDXSpot(const DxSpot &spot) { FCT_IDENTIFICATION; if ( !rigProfile.dxSpot2Rig ) return; const QColor &spotColor = Data::statusToColor(spot.status, spot.dupeCount, QColor(187,194,195)); unsigned long long internalFreq = static_cast(MHz(spot.freq)); QString submode; const QString &mode = BandPlan::bandPlanMode2ExpectedMode(spot.bandPlanMode, submode); QStringList args = { spot.callsign, mode2RawMode(mode, submode, (BandPlan::isFTxBandMode(spot.bandPlanMode) || spot.bandPlanMode == BandPlan::BAND_MODE_DIGITAL)), QString::number(internalFreq), QString::number(spotColor.rgba()), spot.callsign }; sendCmd("spot", 0, args); } void TCIRigDrv::onConnected() { FCT_IDENTIFICATION; qCDebug(runtime) << "Rig is connected"; connect(&ws, &QWebSocket::textMessageReceived, this, &TCIRigDrv::onTextMessageReceived); // QLog has to wait for READY message to complete connection = to emit RigReady } void TCIRigDrv::onSocketError(QAbstractSocket::SocketError socker_error) { FCT_IDENTIFICATION; QString error_msg; qCDebug(runtime) << socker_error; switch (socker_error) { case QAbstractSocket::ConnectionRefusedError: error_msg.append(QObject::tr("Connection Refused")); break; case QAbstractSocket::RemoteHostClosedError: error_msg.append(QObject::tr("Host closed the connection")); //reconectRequested = true; break; case QAbstractSocket::HostNotFoundError: error_msg.append(QObject::tr("Host not found")); break; case QAbstractSocket::SocketTimeoutError: error_msg.append(QObject::tr("Timeout")); //reconectRequested = true; break; case QAbstractSocket::NetworkError: error_msg.append(QObject::tr("Network Error")); break; default: error_msg.append(QObject::tr("Internal Error")); } emit errorOccurred(tr("Error Occurred"), error_msg); } void TCIRigDrv::onTextMessageReceived(const QString &message) { FCT_IDENTIFICATION; qCDebug(function_parameters) << message; if ( message.isEmpty() ) return; #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) const QStringList &commands = message.split(";", Qt::SkipEmptyParts); #else /* Due to ubuntu 20.04 where qt5.12 is present */ const QStringList &commands = message.split(";", QString::SkipEmptyParts); #endif for ( const QString &command : commands ) { // message structure CMD1;CMD2;CMD3.... // CMD structure cmdName:arg0,arg1,arg2 qCDebug(runtime) << command; const QString &trimmedCommand = command.trimmed(); if ( trimmedCommand.isEmpty() ) continue; #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) const QStringList &cmdElemets = trimmedCommand.split(":", Qt::SkipEmptyParts); #else /* Due to ubuntu 20.04 where qt5.12 is present */ const QStringList &cmdElemets = trimmedCommand.split(":", QString::SkipEmptyParts); #endif const QString &cmdName = cmdElemets.at(0).toLower().trimmed(); QStringList cmdArgs; TCIRigDrv::parseFce parser = responseParsers.value(cmdName); if ( !parser ) { qCDebug(runtime) << "Parser function is not defined for" << cmdName; continue; } if ( cmdElemets.size() > 1 ) { #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) cmdArgs = cmdElemets.at(1).split(",", Qt::SkipEmptyParts); #else /* Due to ubuntu 20.04 where qt5.12 is present */ cmdArgs = cmdElemets.at(1).split(",", QString::SkipEmptyParts); #endif } // Trim argument - is a space possible? for( QString &arg : cmdArgs ) arg = arg.trimmed(); (this->*(parser))(cmdArgs); } } void TCIRigDrv::sendCmd(const QString &cmd, bool addRigID, const QStringList& args) { FCT_IDENTIFICATION; if ( !ready ) { qCDebug(runtime) << "Rig is not ready"; return; } QStringList modifiedArgs(args); if ( addRigID ) modifiedArgs.prepend(QString::number(rigProfile.model - 1)); //Mandatory part of the command QString cmdText = cmd.toUpper(); if ( modifiedArgs.size() > 0 ) cmdText.append(":" + modifiedArgs.join(",")); cmdText.append(";"); qCDebug(runtime) << "Sending command:" << cmdText; ws.sendTextMessage(cmdText); } const QString TCIRigDrv::getModeNormalizedText(const QString &rawMode, QString &submode) { FCT_IDENTIFICATION; qCDebug(function_parameters) << rawMode; submode = QString(); if ( rawMode.contains("CW") ) return "CW"; if ( rawMode == "USB" ) { submode = "USB"; return "SSB"; } if ( rawMode == "LSB" ) { submode = "LSB"; return "SSB"; } if ( rawMode == "AM" ) return "AM"; if ( rawMode == "NFM" ) return "FM"; if ( rawMode == "WFM" ) return "FM"; if ( rawMode == "DIGL" ) { submode = "LSB"; return "SSB"; } if ( rawMode == "DIGU" ) { submode = "USB"; return "SSB"; } return QString(); } const QString TCIRigDrv::mode2RawMode(const QString &mode, const QString &submode, bool digiVariant) { FCT_IDENTIFICATION; if (mode == "SSB") { QString innerSubmode = submode; if ( digiVariant ) { const QString digMode = QLatin1String("DIG") + submode.at(0); if ( modeList.contains(digMode) ) innerSubmode = digMode; } return innerSubmode; } if ( mode == "CW" ) return mode; if ( mode == "FM" ) return "NFM"; if ( mode == "AM" ) return mode; if ( mode == "FT8" ) { if ( modeList.contains("FT8") ) return "FT8"; if ( modeList.contains("DIGU") ) return "DIGU"; return "USB"; } if ( mode == "MFSK" ) { if ( submode == "FT4" && modeList.contains("FT4")) return "FT4"; if ( submode == "FT2" && modeList.contains("FT2")) return "FT2"; if ( modeList.contains("DIGU") ) return "DIGU"; return "USB"; } return QString(); } #define CHECK_PARAMS_COUNT( size, min ) \ if ( size < min ) \ { qCWarning(runtime) << "Incorrect number of arguments" << size << min; \ return; } void TCIRigDrv::rspPROTOCOL(const QStringList &cmdArgs) { FCT_IDENTIFICATION; // arg0 — program name. // аrg1 — version of the protocol. CHECK_PARAMS_COUNT(cmdArgs.size(), 2); qCDebug(runtime) << "Program name:" << cmdArgs.at(0) << "Procotol version:" << cmdArgs.at(1); } void TCIRigDrv::rspREADY(const QStringList &) { FCT_IDENTIFICATION; ready = true; emit rigIsReady(); } void TCIRigDrv::rspSTART(const QStringList &) { FCT_IDENTIFICATION; ready = true; } void TCIRigDrv::rspSTOP(const QStringList &) { FCT_IDENTIFICATION; ready = false; emit errorOccurred(tr("Rig status changed"), tr("Rig is not connected")); } void TCIRigDrv::rspRECEIVE_ONLY(const QStringList &cmdArgs) { FCT_IDENTIFICATION; // arg0 - received only (true), transceiver (false) CHECK_PARAMS_COUNT(cmdArgs.size(), 1); receivedOnly = ( cmdArgs.at(0).toLower() == "true" ); } void TCIRigDrv::rspMODULATIONS_LIST(const QStringList &cmdArgs) { FCT_IDENTIFICATION; // arg0 ... N for ( const QString &arg : cmdArgs ) modeList << arg.toUpper(); qCDebug(runtime) << modeList; } void TCIRigDrv::rspVFO(const QStringList &cmdArgs) { FCT_IDENTIFICATION; // arg0 - rigid // arg1 - VFO // arg2 - freq CHECK_PARAMS_COUNT(cmdArgs.size(), 3); if ( !rigProfile.getFreqInfo ) return; if ( cmdArgs.at(0) != QString::number(rigProfile.model - 1)) { qCDebug(runtime) << "Command is not for QLog"; return; } if ( cmdArgs.at(1) == "1" ) { // VFO 1 — TX frequency (relevant when split is active) if ( !rigProfile.getSplitInfo ) return; bool ok; double txFreq = cmdArgs.at(2).toDouble(&ok); if ( ok ) { double txFreqMHz = Hz2MHz(txFreq); qCDebug(runtime) << "Rig TX Freq" << QSTRING_FREQ(txFreqMHz); if ( txFreqMHz != currTxFreq ) { currTxFreq = txFreqMHz; qCDebug(runtime) << "emitting TX FREQ changed"; emit txFrequencyChanged(currTxFreq); } } else { qCDebug(runtime) << "Received TX Freq is not double" << cmdArgs.at(2); } return; } if ( cmdArgs.at(1) != "0" ) { qCDebug(runtime) << "Skipping info from VFO" << cmdArgs.at(1); return; } bool ok; currFreq = cmdArgs.at(2).toDouble(&ok); if ( ok ) { qCDebug(runtime) << "Rig Freq" << QSTRING_FREQ(Hz2MHz(currFreq)); qCDebug(runtime) << "emitting FREQ changed"; emit frequencyChanged(Hz2MHz(currFreq), Hz2MHz(getRITFreq()), Hz2MHz(getXITFreq())); } else { qCDebug(runtime) << "Received Freq is not double" << cmdArgs.at(2); } } void TCIRigDrv::rspTRX(const QStringList &cmdArgs) { FCT_IDENTIFICATION; // arg0 - rigid // arg1 - status // arg2 - signal source (optional) CHECK_PARAMS_COUNT(cmdArgs.size(), 2); if ( !rigProfile.getPTTInfo ) return; if ( cmdArgs.at(0) != QString::number(rigProfile.model - 1)) { qCDebug(runtime) << "Command is not for QLog"; return; } bool ptt = ( cmdArgs.at(1).compare("true", Qt::CaseInsensitive) == 0 ); qCDebug(runtime) << "Rig PTT:"<< ptt; qCDebug(runtime) << "emitting PTT changed" << ptt; emit pttChanged(ptt); } void TCIRigDrv::rspMODULATION(const QStringList &cmdArgs) { FCT_IDENTIFICATION; // arg0 - rigid // arg1 - mode CHECK_PARAMS_COUNT(cmdArgs.size(), 2); if ( !rigProfile.getModeInfo ) return; if ( cmdArgs.at(0) != QString::number(rigProfile.model - 1)) { qCDebug(runtime) << "Command is not for QLog"; return; } currMode = cmdArgs.at(1); QString submode; const QString mode = getModeNormalizedText(currMode, submode); qCDebug(runtime) << "emitting MODE changed" << currMode << mode << submode; emit modeChanged(currMode, mode, submode, 0); // TODO: bandwidth should also be emited // when RX_FILTER_BAND is processed } void TCIRigDrv::rspTUNE_DRIVE(const QStringList &cmdArgs) { FCT_IDENTIFICATION; // arg0 - rigid // arg1 - power output (in watts????) CHECK_PARAMS_COUNT(cmdArgs.size(), 2); // NO ACTION NOW } void TCIRigDrv::rspDRIVE(const QStringList &cmdArgs) { FCT_IDENTIFICATION; // arg0 - rigid // arg1 - power output (in % of max - 0 - 100%) CHECK_PARAMS_COUNT(cmdArgs.size(), 2); if ( !rigProfile.getPWRInfo ) return; if ( cmdArgs.at(0) != QString::number(rigProfile.model - 1)) { qCDebug(runtime) << "Command is not for QLog"; return; } bool ok; unsigned int powerLevel = cmdArgs.at(1).toUInt(&ok); if ( ok ) { // max power is set via Setting Rig Default PWR double maxPower = (rigProfile.defaultPWR > 0.0) ? rigProfile.defaultPWR : 100.0; const double rigPower = qRound(maxPower * powerLevel * 0.01 * 1000.0) / 1000.0; qCDebug(runtime) << "Rig PWR" << rigPower; qCDebug(runtime) << "emitting PWR changed"; emit powerChanged(rigPower); } else { qCDebug(runtime) << "Received PWR is not a number" << cmdArgs.at(1); } } void TCIRigDrv::rspRIT_OFFSET(const QStringList &cmdArgs) { FCT_IDENTIFICATION; // arg0 - rigid // arg1 - rit (+- in HZ) CHECK_PARAMS_COUNT(cmdArgs.size(), 2); if ( !rigProfile.getRITInfo ) return; if ( cmdArgs.at(0) != QString::number(rigProfile.model - 1)) { qCDebug(runtime) << "Command is not for QLog"; return; } bool ok; currRIT = cmdArgs.at(1).toDouble(&ok); if ( ok ) { qCDebug(runtime) << "Rig RIT" << RITEnabled << currRIT; qCDebug(runtime) << "emitting RIT changed" << QSTRING_FREQ(Hz2MHz(getRawRIT())); qCDebug(runtime) << "emitting FREQ changed " << QSTRING_FREQ(Hz2MHz(currFreq)) << QSTRING_FREQ(Hz2MHz(getRITFreq())) << QSTRING_FREQ(Hz2MHz(getXITFreq())); emit ritChanged(Hz2MHz(getRawRIT())); emit frequencyChanged(Hz2MHz(currFreq), Hz2MHz(getRITFreq()), Hz2MHz(getXITFreq())); } else { qCDebug(runtime) << "Received RIT is not a double" << cmdArgs.at(1); } } void TCIRigDrv::rspXIT_OFFSET(const QStringList &cmdArgs) { FCT_IDENTIFICATION; // arg0 - rigid // arg1 - rit (+- in HZ) CHECK_PARAMS_COUNT(cmdArgs.size(), 2); if ( !rigProfile.getXITInfo ) return; if ( cmdArgs.at(0) != QString::number(rigProfile.model - 1)) { qCDebug(runtime) << "Command is not for QLog"; return; } bool ok; currXIT = cmdArgs.at(1).toDouble(&ok); if ( ok ) { qCDebug(runtime) << "Rig XIT" << XITEnabled << currXIT; qCDebug(runtime) << "emitting XIT changed" << QSTRING_FREQ(Hz2MHz(getRawXIT())); qCDebug(runtime) << "emitting FREQ changed " << QSTRING_FREQ(Hz2MHz(currFreq)) << QSTRING_FREQ(Hz2MHz(getRITFreq())) << QSTRING_FREQ(Hz2MHz(getXITFreq())); emit xitChanged(Hz2MHz(getRawXIT())); emit frequencyChanged(Hz2MHz(currFreq), Hz2MHz(getRITFreq()), Hz2MHz(getXITFreq())); } else { qCDebug(runtime) << "Received XIT is not a double" << cmdArgs.at(1); } } void TCIRigDrv::rspCW_MACROS_SPEED(const QStringList &cmdArgs) { FCT_IDENTIFICATION; // arg0 - wpm CHECK_PARAMS_COUNT(cmdArgs.size(), 1); bool ok; unsigned int wpm = cmdArgs.at(0).toInt(&ok); if ( ok ) { qCDebug(runtime) << "Rig Key Speed:" << wpm; emit keySpeedChanged(wpm); } else { qCDebug(runtime) << "Received RIT is not a double" << cmdArgs.at(1); } } void TCIRigDrv::rspRIT_ENABLE(const QStringList &cmdArgs) { FCT_IDENTIFICATION; // arg0 - rigid // arg1 - status indicator. CHECK_PARAMS_COUNT(cmdArgs.size(), 2); if ( !rigProfile.getRITInfo ) return; if ( cmdArgs.at(0) != QString::number(rigProfile.model - 1)) { qCDebug(runtime) << "Command is not for QLog"; return; } RITEnabled = ( cmdArgs.at(1).toLower() == "true" ); qCDebug(runtime) << "Rig RIT status changed" << RITEnabled << currRIT; qCDebug(runtime) << "emitting RIT changed" << QSTRING_FREQ(Hz2MHz(getRawRIT())); emit ritChanged(Hz2MHz(getRawRIT())); emit frequencyChanged(Hz2MHz(currFreq), Hz2MHz(getRITFreq()), Hz2MHz(getXITFreq())); } void TCIRigDrv::rspXIT_ENABLE(const QStringList &cmdArgs) { FCT_IDENTIFICATION; // arg0 - rigid // arg1 - status indicator. CHECK_PARAMS_COUNT(cmdArgs.size(), 2); if ( !rigProfile.getXITInfo ) return; if ( cmdArgs.at(0) != QString::number(rigProfile.model - 1)) { qCDebug(runtime) << "Command is not for QLog"; return; } XITEnabled = ( cmdArgs.at(1).toLower() == "true" ); qCDebug(runtime) << "Rig XIT status changed" << XITEnabled << currXIT; qCDebug(runtime) << "emitting XIT changed" << QSTRING_FREQ(Hz2MHz(getRawXIT())); emit xitChanged(Hz2MHz(getRawXIT())); emit frequencyChanged(Hz2MHz(currFreq), Hz2MHz(getRITFreq()), Hz2MHz(getXITFreq())); } void TCIRigDrv::rspSPLIT_ENABLE(const QStringList &cmdArgs) { FCT_IDENTIFICATION; // arg0 - rigid // arg1 - status indicator. CHECK_PARAMS_COUNT(cmdArgs.size(), 2); if ( !rigProfile.getSplitInfo ) return; if ( cmdArgs.at(0) != QString::number(rigProfile.model - 1) ) { qCDebug(runtime) << "Command is not for QLog"; return; } bool newSplitEnabled = ( cmdArgs.at(1).toLower() == "true" ); qCDebug(runtime) << "Rig Split status changed" << newSplitEnabled; if ( newSplitEnabled != currSplitEnabled ) { currSplitEnabled = newSplitEnabled; qCDebug(runtime) << "emitting SPLIT changed" << currSplitEnabled; emit splitChanged(currSplitEnabled); } if ( currSplitEnabled ) { // request TX VFO frequency when split is active sendCmd("vfo", true, QStringList("1")); } else if ( currTxFreq != 0.0 ) { currTxFreq = 0.0; } } #undef CHECK_PARAMS_COUNT double TCIRigDrv::getRITFreq() { FCT_IDENTIFICATION; return currFreq + getRawRIT(); } void TCIRigDrv::setRITFreq(double rit) { currRIT = rit; } double TCIRigDrv::getXITFreq() { return currFreq + currXIT; } void TCIRigDrv::setXITFreq(double xit) { currXIT = xit; } double TCIRigDrv::getRawRIT() { return ( ( RITEnabled ) ? currRIT : 0.0 ); } double TCIRigDrv::getRawXIT() { return ( ( XITEnabled ) ? currXIT : 0.0 ); } ================================================ FILE: rig/drivers/TCIRigDrv.h ================================================ #ifndef RIG_DRIVERS_TCIRIGDRV_H #define RIG_DRIVERS_TCIRIGDRV_H #include #include #include "GenericRigDrv.h" #include "rig/RigCaps.h" class TCIRigDrv : public GenericRigDrv { Q_OBJECT public: static QList> getModelList(); static RigCaps getCaps(int); explicit TCIRigDrv(const RigProfile &profile, QObject *parent = nullptr); virtual ~TCIRigDrv(); virtual bool open() override; virtual bool isMorseOverCatSupported() override; virtual QStringList getAvailableModes() override; virtual void setFrequency(double) override; virtual void setFrequency(VFOID, double) override; virtual void setSplit(bool) override; virtual void setRawMode(const QString &) override; virtual void setMode(const QString &, const QString &, bool) override; virtual void setPTT(bool) override; virtual void setKeySpeed(qint16 wpm) override; virtual void syncKeySpeed(qint16 wpm) override; virtual void sendMorse(const QString &) override; virtual void stopMorse() override; virtual void sendState() override; virtual void stopTimers() override; virtual void sendDXSpot(const DxSpot &spot) override; private slots: void onConnected(); void onTextMessageReceived(const QString& message); void onSocketError(QAbstractSocket::SocketError socker_error); private: typedef void (TCIRigDrv::*parseFce)(const QStringList&); void sendCmd(const QString &cmd, bool addRigID, const QStringList &args = QStringList()); const QString getModeNormalizedText(const QString& rawMode, QString &submode); const QString mode2RawMode(const QString &mode, const QString &submode, bool digiVariant); // commands functions void rspPROTOCOL(const QStringList &); void rspREADY(const QStringList &); void rspSTART(const QStringList &); void rspSTOP(const QStringList &); void rspRECEIVE_ONLY(const QStringList &); void rspMODULATIONS_LIST(const QStringList &); void rspVFO(const QStringList &); void rspTRX(const QStringList &); void rspMODULATION(const QStringList &); void rspTUNE_DRIVE(const QStringList &); void rspDRIVE(const QStringList &); void rspRIT_OFFSET(const QStringList &); void rspXIT_OFFSET(const QStringList &); void rspCW_MACROS_SPEED(const QStringList &); void rspRIT_ENABLE(const QStringList &); void rspXIT_ENABLE(const QStringList &); void rspSPLIT_ENABLE(const QStringList &); double getRITFreq(); void setRITFreq(double); double getXITFreq(); void setXITFreq(double); double getRawRIT(); double getRawXIT(); QWebSocket ws; bool ready; bool receivedOnly; QStringList modeList; double currFreq; double currTxFreq; QString currMode; double currRIT; double currXIT; bool RITEnabled; bool XITEnabled; bool currSplitEnabled; const QHash responseParsers = { {"protocol", &TCIRigDrv::rspPROTOCOL}, {"ready", &TCIRigDrv::rspREADY}, {"start", &TCIRigDrv::rspSTART}, {"stop", &TCIRigDrv::rspSTOP}, {"receive_only", &TCIRigDrv::rspRECEIVE_ONLY}, {"modulations_list", &TCIRigDrv::rspMODULATIONS_LIST}, {"vfo", &TCIRigDrv::rspVFO}, {"trx", &TCIRigDrv::rspTRX}, {"modulation", &TCIRigDrv::rspMODULATION}, {"tune_drive", &TCIRigDrv::rspTUNE_DRIVE}, {"drive", &TCIRigDrv::rspDRIVE}, {"rit_offset", &TCIRigDrv::rspRIT_OFFSET}, {"xit_offset", &TCIRigDrv::rspXIT_OFFSET}, {"cw_macros_speed", &TCIRigDrv::rspCW_MACROS_SPEED}, {"rit_enable", &TCIRigDrv::rspRIT_ENABLE}, {"xit_enable", &TCIRigDrv::rspXIT_ENABLE}, {"split_enable", &TCIRigDrv::rspSPLIT_ENABLE}, }; }; #endif // RIG_DRIVERS_TCIRIGDRV_H ================================================ FILE: rig/macros.h ================================================ #ifndef RIG_MACROS_H #define RIG_MACROS_H #define BANDWIDTH_UNKNOWN 0 #define QSTRING_FREQ(f) (QString::number((f), 'f', 5)) #define Hz2MHz(f) ((double)((f)/1e6)) #define Hz2kHz(f) ((double)((f)/1e3)) #define mW2W(f) ((double)((f)/1000.0)) #ifndef MHz #define MHz(f) ((double)((f)*(double)1000000)) #endif #ifndef kHz #define kHz(f) ((double)((f)*(double)1000)) #endif #endif // RIG_MACROS_H ================================================ FILE: rotator/RotCaps.cpp ================================================ #include "RotCaps.h" RotCaps::RotCaps(bool isNetworkOnly, int serial_data_bits, int serial_stop_bits) : isNetworkOnly(isNetworkOnly), serialDataBits(serial_data_bits), serialStopBits(serial_stop_bits) { } ================================================ FILE: rotator/RotCaps.h ================================================ #ifndef QLOG_ROTATOR_ROTCAPS_H #define QLOG_ROTATOR_ROTCAPS_H class RotCaps { public: RotCaps(bool isNetworkOnly = false, int serial_data_bits = 8, int serial_stop_bits = 1); bool isNetworkOnly; int serialDataBits; int serialStopBits; }; #endif // QLOG_ROTATOR_ROTCAPS_H ================================================ FILE: rotator/Rotator.cpp ================================================ #include "rotator/Rotator.h" #include "core/debug.h" #include "rotator/drivers/HamlibRotDrv.h" #include "rotator/drivers/PSTRotDrv.h" MODULE_IDENTIFICATION("qlog.rotator.rotator"); #define MUTEXLOCKER qCDebug(runtime) << "Waiting for Rot mutex"; \ QMutexLocker locker(&rotLock); \ qCDebug(runtime) << "Using Rot" #define TIME_PERIOD 1000 Rotator::Rotator(QObject *parent) : QObject{parent}, rotDriver(nullptr), connected(false), cacheAzimuth(0.0), cacheElevation(0.0) { FCT_IDENTIFICATION; drvMapping[HAMLIB_DRIVER] = DrvParams(HAMLIB_DRIVER, "Hamlib", &HamlibRotDrv::getModelList, &HamlibRotDrv::getCaps); drvMapping[PSTROTATOR_DRIVER] = DrvParams(PSTROTATOR_DRIVER, "PSTRotator", &PSTRotDrv::getModelList, &PSTRotDrv::getCaps); } Rotator::~Rotator() { FCT_IDENTIFICATION; __closeRot(); } double Rotator::getAzimuth() { FCT_IDENTIFICATION; MUTEXLOCKER; return cacheAzimuth; } double Rotator::getElevation() { FCT_IDENTIFICATION; MUTEXLOCKER; return cacheElevation; } bool Rotator::isRotConnected() { FCT_IDENTIFICATION; return connected; } const QList > Rotator::getModelList(const DriverID &id) const { FCT_IDENTIFICATION; QList> ret; if ( drvMapping.contains(id) && drvMapping.value(id).getModeslListFunction != nullptr ) { ret = (drvMapping.value(id).getModeslListFunction)(); } return ret; } const QList > Rotator::getDriverList() const { FCT_IDENTIFICATION; QList> ret; const QList &keys = drvMapping.keys(); for ( const int &key : keys ) { ret << QPair(key, drvMapping[key].driverName); } return ret; } const RotCaps Rotator::getRotCaps(const DriverID &id, int model) const { FCT_IDENTIFICATION; if ( drvMapping.contains(id) && drvMapping.value(id).getCapsFunction != nullptr) { return (drvMapping.value(id).getCapsFunction)(model); } return RotCaps(); } void Rotator::stopTimer() { FCT_IDENTIFICATION; MUTEXLOCKER; bool check = QMetaObject::invokeMethod(Rotator::instance(), &Rotator::stopTimerImplt, Qt::QueuedConnection); Q_ASSERT( check ); } void Rotator::stopTimerImplt() { FCT_IDENTIFICATION; if ( rotDriver ) rotDriver->stopTimers(); } void Rotator::start() { FCT_IDENTIFICATION; } void Rotator::open() { FCT_IDENTIFICATION; QMetaObject::invokeMethod(this, &Rotator::openImpl, Qt::QueuedConnection); } void Rotator::openImpl() { FCT_IDENTIFICATION; MUTEXLOCKER; __openRot(); } void Rotator::sendState() { FCT_IDENTIFICATION; QMetaObject::invokeMethod(this, "sendStateImpl", Qt::QueuedConnection); } void Rotator::sendStateImpl() { FCT_IDENTIFICATION; MUTEXLOCKER; if ( ! rotDriver ) return; rotDriver->sendState(); } void Rotator::__openRot() { FCT_IDENTIFICATION; // if rot is active then close it __closeRot(); RotProfile newRotProfile = RotProfilesManager::instance()->getCurProfile1(); if ( newRotProfile == RotProfile() ) { emit rotErrorPresent(tr("No Rotator Profile selected"), QString()); return; } qCDebug(runtime) << "Opening profile name: " << newRotProfile.profileName; rotDriver = getDriver(newRotProfile); if ( !rotDriver ) { // initialization failed emit rotErrorPresent(tr("Initialization Error"), tr("Internal Error")); return; } connect(rotDriver, &GenericRotDrv::positioningChanged, this, [this](double a, double b) { cacheAzimuth = a; cacheElevation = b; emit positionChanged(a, b); }); connect(rotDriver, &GenericRotDrv::errorOccurred, this, [this](const QString &a, const QString &b) { close(); emit rotErrorPresent(a, b); }); connect(rotDriver, &GenericRotDrv::rotIsReady, this, [this, newRotProfile]() { connected = true; emit rotConnected(); sendState(); }); if ( !rotDriver->open() ) { emit rotErrorPresent(tr("Cannot open Rotator"), rotDriver->lastError()); qWarning() << rotDriver->lastError(); __closeRot(); return; } } GenericRotDrv *Rotator::getDriver(const RotProfile &profile) { FCT_IDENTIFICATION; qCDebug(runtime) << profile.driver; switch ( profile.driver ) { case Rotator::HAMLIB_DRIVER: return new HamlibRotDrv(profile, this); break; case Rotator::PSTROTATOR_DRIVER: return new PSTRotDrv(profile, this); break; default: qWarning() << "Unsupported Rotator Driver " << profile.driver; } return nullptr; } void Rotator::close() { FCT_IDENTIFICATION; QMetaObject::invokeMethod(this, &Rotator::closeImpl, Qt::QueuedConnection); } void Rotator::closeImpl() { FCT_IDENTIFICATION; MUTEXLOCKER; __closeRot(); } void Rotator::__closeRot() { FCT_IDENTIFICATION; if ( !rotDriver ) { qCDebug(runtime) << "Driver is not active"; return; } delete rotDriver; rotDriver = nullptr; connected = false; emit rotDisconnected(); } void Rotator::setPosition(double azimuth, double elevation) { FCT_IDENTIFICATION; QMetaObject::invokeMethod(this, "setPositionImpl", Qt::QueuedConnection, Q_ARG(double,azimuth), Q_ARG(double,elevation)); } void Rotator::setPositionImpl(double azimuth, double elevation) { FCT_IDENTIFICATION; qCDebug(function_parameters)<setPosition(azimuth, elevation); } #undef MUTEXLOCKER ================================================ FILE: rotator/Rotator.h ================================================ #ifndef QLOG_ROTATOR_ROTATOR_H #define QLOG_ROTATOR_ROTATOR_H #include #include "data/RotProfile.h" #include "rotator/drivers/GenericRotDrv.h" #include "RotCaps.h" class Rotator : public QObject { Q_OBJECT public: enum DriverID { UNDEF_DRIVER = 0, HAMLIB_DRIVER = 1, PSTROTATOR_DRIVER = 2 }; static Rotator* instance() { static Rotator instance; return &instance; }; double getAzimuth(); double getElevation(); bool isRotConnected(); const QList> getModelList(const DriverID &id) const; const QList> getDriverList() const; const RotCaps getRotCaps(const DriverID &, int) const; signals: void positionChanged(double azimuth, double elevation); void rotErrorPresent(QString, QString); void rotDisconnected(); void rotConnected(); public slots: void start(); void open(); void close(); void stopTimer(); void sendState(); void setPosition(double azimuth, double elevation); private slots: void setPositionImpl(double azimuth, double elevation); void stopTimerImplt(); void openImpl(); void closeImpl(); void sendStateImpl(); private: Rotator(QObject *parent = nullptr); ~Rotator(); class DrvParams { public: DrvParams(const DriverID id, const QString &driverName, QList> (*getModelfct)(), RotCaps (*getCapsfct)(int)) : driverID(id), driverName(driverName), getModeslListFunction(getModelfct), getCapsFunction(getCapsfct) {}; DrvParams() : driverID(UNDEF_DRIVER), getModeslListFunction(nullptr), getCapsFunction(nullptr) {}; DriverID driverID; QString driverName; QList> (*getModeslListFunction)(); RotCaps (*getCapsFunction)(int); }; QMap drvMapping; void __closeRot(); void __openRot(); GenericRotDrv *getDriver(const RotProfile &profile); private: GenericRotDrv *rotDriver; QMutex rotLock; bool connected; double cacheAzimuth; double cacheElevation; }; #endif // QLOG_ROTATOR_ROTATOR_H ================================================ FILE: rotator/drivers/GenericRotDrv.cpp ================================================ #include "GenericRotDrv.h" #include "core/debug.h" MODULE_IDENTIFICATION("qlog.core.rot.driver.genericrotdrv"); GenericRotDrv::GenericRotDrv(const RotProfile &profile, QObject *parent) : QObject{parent}, rotProfile(profile), opened(false), azimuth(0.0), elevation(0.0) { FCT_IDENTIFICATION; } const RotProfile GenericRotDrv::getCurrRotProfile() const { FCT_IDENTIFICATION; return rotProfile; } const QString GenericRotDrv::lastError() const { FCT_IDENTIFICATION; return lastErrorText; } double GenericRotDrv::normalizeAzimuth(double azimuth) const { FCT_IDENTIFICATION; /* This function takes any azimuth value (in degrees), including * negative values or values greater than 360, and returns an * equivalent angle normalized to the range [0, 360). */ double normalized = fmod(azimuth, 360.0); if ( normalized < 0.0 ) normalized += 360.0; qCDebug(runtime) << "Input azimuth:" << azimuth << "Normalized azimuth:" << normalized; return normalized; } ================================================ FILE: rotator/drivers/GenericRotDrv.h ================================================ #ifndef QLOG_ROTATOR_DRIVERS_GENERICROTDRV_H #define QLOG_ROTATOR_DRIVERS_GENERICROTDRV_H #include #include "data/RotProfile.h" class GenericRotDrv : public QObject { Q_OBJECT public: explicit GenericRotDrv(const RotProfile &profile, QObject *parent = nullptr); virtual ~GenericRotDrv() {}; const RotProfile getCurrRotProfile() const; const QString lastError() const; virtual bool open() = 0; virtual void sendState() = 0; virtual void setPosition(double azimuth, double elevation) = 0; virtual void stopTimers() = 0; double normalizeAzimuth(double azimuth) const; signals: void rotIsReady(); void positioningChanged(double azimuth, double elevation); // Error Signal void errorOccurred(QString, QString); protected: RotProfile rotProfile; QString lastErrorText; bool opened; double azimuth; double elevation; }; #endif // QLOG_ROTATOR_DRIVERS_GENERICROTDRV_H ================================================ FILE: rotator/drivers/HamlibRotDrv.cpp ================================================ #include #include #include "HamlibRotDrv.h" #include "core/debug.h" #include "data/SerialPort.h" #include "data/AntProfile.h" #define MUTEXLOCKER qCDebug(runtime) << "Waiting for Rot Drv mutex"; \ QMutexLocker locker(&drvLock); \ qCDebug(runtime) << "Using Rot Drv" #ifndef HAMLIB_FILPATHLEN #define HAMLIB_FILPATHLEN FILPATHLEN #endif #ifndef RIG_IS_SOFT_ERRCODE #define RIG_IS_SOFT_ERRCODE(errcode) (errcode == RIG_EINVAL || errcode == RIG_ENIMPL || errcode == RIG_ERJCTED \ || errcode == RIG_ETRUNC || errcode == RIG_ENAVAIL || errcode == RIG_ENTARGET \ || errcode == RIG_EVFO || errcode == RIG_EDOM) #endif MODULE_IDENTIFICATION("qlog.rotator.driver.hamlibdrv"); QList > HamlibRotDrv::getModelList() { FCT_IDENTIFICATION; QList> ret; rot_load_all_backends(); rot_list_foreach(addRig, &ret); return ret; } RotCaps HamlibRotDrv::getCaps(int model) { FCT_IDENTIFICATION; const struct rot_caps *caps = rot_get_caps(model); RotCaps ret; ret.isNetworkOnly = (model == RIG_MODEL_NETRIGCTL); ret.serialDataBits = caps->serial_data_bits; ret.serialStopBits = caps->serial_stop_bits; return ret; } int HamlibRotDrv::addRig(const rot_caps *caps, void *data) { FCT_IDENTIFICATION; QList> *list = static_cast>*>(data); QString name = QString("%1 %2 (%3)").arg(caps->mfg_name, caps->model_name, caps->version); list->append(QPair(caps->rot_model, name)); return -1; } HamlibRotDrv::HamlibRotDrv(const RotProfile &profile, QObject *parent) : GenericRotDrv{profile, parent}, rot(nullptr), forceSendState(false) { FCT_IDENTIFICATION; rot_load_all_backends(); rot = rot_init(rotProfile.model); if ( !rot ) { // initialization failed qCDebug(runtime) << "Cannot allocate Rotator structure"; lastErrorText = tr("Initialization Error"); } rig_set_debug(RIG_DEBUG_BUG); connect(&errorTimer, &QTimer::timeout, this, &HamlibRotDrv::checkErrorCounter); } HamlibRotDrv::~HamlibRotDrv() { FCT_IDENTIFICATION; if ( !drvLock.tryLock(200) ) { qCDebug(runtime) << "Waited too long"; // better to make a memory leak return; } if ( rot ) { rot_close(rot); rot_cleanup(rot); rot = nullptr; } drvLock.unlock(); } bool HamlibRotDrv::open() { FCT_IDENTIFICATION; MUTEXLOCKER; if ( !rot ) { // initialization failed lastErrorText = tr("Initialization Error"); qCDebug(runtime) << "Rot is not initialized"; return false; } RotProfile::rotPortType portType = rotProfile.getPortType(); if ( portType == RotProfile::NETWORK_ATTACHED ) { //handling Network Radio const QString portString = rotProfile.hostname + ":" + QString::number(rotProfile.netport); strncpy(rot->state.rotport.pathname, portString.toLocal8Bit().constData(), HAMLIB_FILPATHLEN - 1); } else if ( portType == RotProfile::SERIAL_ATTACHED ) { //handling Serial Port Radio strncpy(rot->state.rotport.pathname, rotProfile.portPath.toLocal8Bit().constData(), HAMLIB_FILPATHLEN - 1); rot->state.rotport.parm.serial.rate = rotProfile.baudrate; rot->state.rotport.parm.serial.data_bits = rotProfile.databits; rot->state.rotport.parm.serial.stop_bits = rotProfile.stopbits; rot->state.rotport.parm.serial.handshake = stringToHamlibFlowControl(rotProfile.flowcontrol); rot->state.rotport.parm.serial.parity = stringToHamlibParity(rotProfile.parity); } else { lastErrorText = tr("Unsupported Rotator Driver"); qCDebug(runtime) << "Rot Open Error" << lastErrorText; return false; } token_t timeout_token = rot_token_lookup(rot, "timeout"); if (timeout_token != -RIG_EINVAL) { int ret = rot_set_conf(rot, timeout_token, "5000"); if (ret == RIG_OK) { qCDebug(runtime) << "Rotator timeout set to 5000ms"; } else { qCDebug(runtime) << "rot_set_conf(timeout) failed:" << rigerror(ret); } } else { qCDebug(runtime) << "timeout token not found"; } int status = rot_open(rot); if ( !isRotRespOK(status, tr("Rot Open Error"), false) ) return false; qCDebug(runtime) << "Rot Open - OK"; opened = true; connect(&timer, &QTimer::timeout, this, &HamlibRotDrv::checkRotStateChange); timer.start(POLL_INTERVAL); emit rotIsReady(); return true; } void HamlibRotDrv::sendState() { FCT_IDENTIFICATION; MUTEXLOCKER; forceSendState = true; } // in_azimuth range is 0 - 360 // in_elevation, not used - always 0 void HamlibRotDrv::setPosition(double in_azimuth, double in_elevation) { FCT_IDENTIFICATION; qCDebug(function_parameters) << in_azimuth << in_elevation; MUTEXLOCKER; if ( !rot || !rot->caps ) { qCWarning(runtime) << "Rot is not active"; return; } if ( rot->caps->max_az == 0 ) { qCWarning(runtime) << "Rot does not support the Azimuth setting"; return; } double newElevation = in_elevation; double newAzimuth = in_azimuth - AntProfilesManager::instance()->getCurProfile1().azimuthOffset; // offset interval is -180 to 180 // azimuth input interval is 0 to 360 // min value is -180 // max valus is 540 newAzimuth = fmod(newAzimuth + 360, 360); qCDebug(runtime) << "Azimuth (with offset)" << newAzimuth; /**********************************/ /* Rotator specific modifications */ /**********************************/ qCDebug(runtime) << "Rotator limits: AZ" << rot->caps->min_az << "to" << rot->caps->max_az << "EL" << rot->caps->min_el << "to" << rot->caps->max_el; // recalculate azimuth to interval -180 to 180 for rotators that have this interval if ( rot->caps->max_az == 180.0 && rot->caps->min_az == -180.0 ) { if ( newAzimuth > 180.0 ) { qCDebug(runtime) << "Azimuth need to be recalculated to interval -180 to 180"; newAzimuth -= 360.0; } } else if ( rot->caps->max_az == 359.9f && newAzimuth > 359.9f ) { // exception for Green Heron RT-21 newAzimuth = 0; } /************************/ /* END of modifications */ /************************/ // the final value of the azimuth and elevation is known qCDebug(runtime) << "Set Az/El position" << static_cast(newAzimuth) << static_cast(newElevation); int status = rot_set_position(rot, static_cast(newAzimuth), static_cast(newElevation)); isRotRespOK(status, tr("Set Possition Error"), false); // wait a moment because Rotators are slow and they are not possible to set and get // mode so quickly (get mode is called in the main thread's update() function commandSleep(); } void HamlibRotDrv::stopTimers() { FCT_IDENTIFICATION; timer.stop(); errorTimer.stop(); } void HamlibRotDrv::checkRotStateChange() { FCT_IDENTIFICATION; if ( !drvLock.tryLock(200) ) { qCDebug(runtime) << "Waited too long"; return; } qCDebug(runtime) << "Getting Rot state"; checkChanges(); forceSendState = false; // restart timer timer.start(POLL_INTERVAL); drvLock.unlock(); } void HamlibRotDrv::checkErrorCounter() { FCT_IDENTIFICATION; if ( postponedErrors.isEmpty() ) return; qCDebug(runtime) << postponedErrors; // emit only one error auto it = postponedErrors.constBegin(); emit errorOccurred(it.key(), it.value()); postponedErrors.clear(); } void HamlibRotDrv::checkChanges() { FCT_IDENTIFICATION; checkAzEl(); } void HamlibRotDrv::checkAzEl() { FCT_IDENTIFICATION; if ( !rot ) { qCWarning(runtime) << "Rot is not active"; return; } if ( rot->caps->get_position ) { azimuth_t az; elevation_t el; int status = rot_get_position(rot, &az, &el); if ( isRotRespOK(status, tr("Get Possition Error")) ) { double newAzimuth = az; double newElevation = el; // Azimuth Normalization (-180,180) -> (0,360) - ADIF defined interval is 0-360 newAzimuth += AntProfilesManager::instance()->getCurProfile1().azimuthOffset; newAzimuth = normalizeAzimuth(newAzimuth); qCDebug(runtime) << "Rot Position: " << newAzimuth << newElevation; qCDebug(runtime) << "Object Position: "<< azimuth << elevation; if ( newAzimuth != azimuth || newElevation != elevation || forceSendState) { azimuth = newAzimuth; elevation = newElevation; qCDebug(runtime) << "emitting POSITIONING changed"; emit positioningChanged(azimuth, elevation); } } } else qCDebug(runtime) << "Get POSITION is disabled"; } bool HamlibRotDrv::isRotRespOK(int errorStatus, const QString errorName, bool emitError) { FCT_IDENTIFICATION; qCDebug(function_parameters) << errorStatus << errorName << emitError; if ( errorStatus == RIG_OK ) // there are no special codes for ROT, use RIG codes { if ( emitError ) postponedErrors.remove(errorName); return true; } lastErrorText = hamlibErrorString(errorStatus); if ( emitError ) { qCDebug(runtime) << "Emit Error detected"; if ( !RIG_IS_SOFT_ERRCODE(-errorStatus) ) { // hard error, emit error now qCDebug(runtime) << "Hard Error"; emit errorOccurred(errorName, lastErrorText); } else { // soft error postponedErrors[errorName] = lastErrorText; if ( !errorTimer.isActive() ) { qCDebug(runtime) << "Starting Error Timer"; errorTimer.start(15 * 1000); //15s } } } return false; } serial_handshake_e HamlibRotDrv::stringToHamlibFlowControl(const QString &in_flowcontrol) { FCT_IDENTIFICATION; qCDebug(function_parameters) << in_flowcontrol; const QString &flowcontrol = in_flowcontrol.toLower(); if ( flowcontrol == SerialPort::SERIAL_FLOWCONTROL_SOFTWARE ) return RIG_HANDSHAKE_XONXOFF; if ( flowcontrol == SerialPort::SERIAL_FLOWCONTROL_HARDWARE ) return RIG_HANDSHAKE_HARDWARE; return RIG_HANDSHAKE_NONE; } serial_parity_e HamlibRotDrv::stringToHamlibParity(const QString &in_parity) { FCT_IDENTIFICATION; qCDebug(function_parameters) << in_parity; const QString &parity = in_parity.toLower(); if ( parity == SerialPort::SERIAL_PARITY_EVEN ) return RIG_PARITY_EVEN; if ( parity == SerialPort::SERIAL_PARITY_ODD ) return RIG_PARITY_ODD; if ( parity == SerialPort::SERIAL_PARITY_MARK ) return RIG_PARITY_MARK; if ( parity == SerialPort::SERIAL_PARITY_SPACE ) return RIG_PARITY_SPACE; return RIG_PARITY_NONE; } QString HamlibRotDrv::hamlibErrorString(int errorCode) { FCT_IDENTIFICATION; qCDebug(function_parameters) << errorCode; QString ret; QString detail(rigerror(errorCode)); #if ( HAMLIBVERSION_MAJOR >= 4 && HAMLIBVERSION_MINOR >= 5 ) // The rigerror has different behavior since 4.5. It contains the stack trace in the first part // Need to use rigerror2 ret = QString(rigerror2(errorCode)); #else static QRegularExpression re("[\r\n]"); QStringList errorList = detail.split(re); if ( errorList.size() >= 1 ) ret = errorList.at(0); #endif qWarning() << "Detail" << detail; qCWarning(runtime) << ret; return ret; } void HamlibRotDrv::commandSleep() { FCT_IDENTIFICATION; QThread::msleep(100); } #undef MUTEXLOCKER ================================================ FILE: rotator/drivers/HamlibRotDrv.h ================================================ #ifndef QLOG_ROTATOR_DRIVERS_HAMLIBROTDRV_H #define QLOG_ROTATOR_DRIVERS_HAMLIBROTDRV_H #include #include #include "GenericRotDrv.h" #include "rotator/RotCaps.h" class HamlibRotDrv : public GenericRotDrv { public: static QList> getModelList(); static RotCaps getCaps(int model); explicit HamlibRotDrv(const RotProfile &profile, QObject *parent = nullptr); virtual ~HamlibRotDrv(); virtual bool open() override; virtual void sendState() override; virtual void setPosition(double in_azimuth, double in_elevation) override; virtual void stopTimers() override; private slots: void checkRotStateChange(); void checkErrorCounter(); private: static int addRig(const struct rot_caps* caps, void* data); void checkChanges(); void checkAzEl(); bool isRotRespOK(int errorStatus, const QString errorName, bool emitError = true); serial_handshake_e stringToHamlibFlowControl(const QString &in_flowcontrol); serial_parity_e stringToHamlibParity(const QString &in_parity); QString hamlibErrorString(int); void commandSleep(); ROT* rot; QTimer timer; QTimer errorTimer; QMutex drvLock; bool forceSendState; QHashpostponedErrors; const quint32 POLL_INTERVAL = 2000; }; #endif // QLOG_ROTATOR_DRIVERS_HAMLIBROTDRV_H ================================================ FILE: rotator/drivers/PSTRotDrv.cpp ================================================ #include #include #include "core/debug.h" #include "PSTRotDrv.h" #define MUTEXLOCKER qCDebug(runtime) << "Waiting for Rot Drv mutex"; \ QMutexLocker locker(&drvLock); \ qCDebug(runtime) << "Using Rot Drv" #define POOL_INTERVAL 1000 #define COMMAND_TIMEOUT POOL_INTERVAL * 0.7 MODULE_IDENTIFICATION("qlog.rotator.driver.pstrotdrv"); QList > PSTRotDrv::getModelList() { FCT_IDENTIFICATION; QList> ret; ret << QPair(1, tr("Rot 1")); return ret; } RotCaps PSTRotDrv::getCaps(int) { FCT_IDENTIFICATION; RotCaps ret; ret.isNetworkOnly = true; return ret; } PSTRotDrv::PSTRotDrv(const RotProfile &profile, QObject *parent) : GenericRotDrv{profile, parent}, forceSendState(false) { FCT_IDENTIFICATION; } PSTRotDrv::~PSTRotDrv() { FCT_IDENTIFICATION; PSTRotDrv::stopTimers(); } bool PSTRotDrv::open() { FCT_IDENTIFICATION; MUTEXLOCKER; bool rc = receiveSocket.bind(rotProfile.netport + 1); if ( !rc ) { lastErrorText = tr("Cannot bind a port") + " " + rotProfile.netport; qCDebug(runtime) << "Rot is not initialized - cannot bind port address" << rotProfile.netport; return false; } qCDebug(runtime) << "Listening port" << rotProfile.netport + 1; const QHostInfo &hostInfo = QHostInfo::fromName(rotProfile.hostname); if ( hostInfo.error() != QHostInfo::NoError ) { lastErrorText = tr("Cannot get IP Address for") + " " + rotProfile.hostname; qCWarning(runtime) << "Cannot get Rotator hostname" << hostInfo.errorString(); return false; } const QList addresses = hostInfo.addresses(); for ( const QHostAddress &address : addresses ) { // currently, it seems that PSTRotator supports only IPv4 Addresses // therefore it is necessary to filter out IPv6 adresses if ( address.protocol() == QAbstractSocket::IPv4Protocol ) { rotatorAddress = address; qCDebug(runtime) << "Found IPv4 address " << address; } } if ( rotatorAddress.isNull() ) { lastErrorText = tr("No IPv4 Address for") + " " + rotProfile.hostname; qCWarning(runtime) << "No IPv4 Address for " << rotProfile.hostname; return false; } qCDebug(runtime) << rotatorAddress; connect(&receiveSocket, &QUdpSocket::readyRead, this, &PSTRotDrv::readPendingDatagrams); connect(&refreshTimer, &QTimer::timeout, this, &PSTRotDrv::checkRotStateChange); connect(&timeoutTimer, &QTimer::timeout, this, [this]() { timeoutTimer.stop(); qCWarning(runtime) << "Operation Timeout"; emit errorOccurred(tr("Error Occurred"), tr("Operation Timeout")); }); refreshTimer.start(POOL_INTERVAL); emit rotIsReady(); opened = true; return rc; } void PSTRotDrv::sendState() { FCT_IDENTIFICATION; MUTEXLOCKER; forceSendState = true; } void PSTRotDrv::setPosition(double in_azimuth, double in_elevation) { FCT_IDENTIFICATION; qCDebug(function_parameters) << in_azimuth << in_elevation; MUTEXLOCKER; if ( !opened ) return; QString positionCommand = QString("" "0" "%1" "%2" "").arg(in_azimuth, 0, 'f', 1) .arg(in_elevation, 0, 'f', 1); sendCommand(positionCommand); } void PSTRotDrv::stopTimers() { FCT_IDENTIFICATION; receiveSocket.close(); refreshTimer.stop(); timeoutTimer.stop(); } void PSTRotDrv::checkRotStateChange() { FCT_IDENTIFICATION; MUTEXLOCKER; QUdpSocket sendSocket; QString azCommand = "AZ?"; QString elCommand = "EL?"; sendCommand(azCommand); sendCommand(elCommand); timeoutTimer.start(COMMAND_TIMEOUT); } void PSTRotDrv::commandSleep() { QThread::msleep(100); } void PSTRotDrv::sendCommand(const QString &cmd) { FCT_IDENTIFICATION; qCDebug(function_parameters) << cmd; QUdpSocket sendSocket; sendSocket.writeDatagram(cmd.toUtf8(), rotatorAddress, rotProfile.netport); commandSleep(); } void PSTRotDrv::readPendingDatagrams() { FCT_IDENTIFICATION; timeoutTimer.stop(); while ( receiveSocket.hasPendingDatagrams() ) { QNetworkDatagram datagram = receiveSocket.receiveDatagram(); QString data(datagram.data()); qCDebug(runtime) << "Received from" << datagram.senderAddress(); qCDebug(runtime) << data; // TODO: Check if sender has the IP Address from ROT Profile? // TODO: we assume that the entire response fits into one packet, // so there is no need to implement sequential loading of fragmented packets. // But we prefer to check whether the length is at least expected. if ( data.size() < 4 ) { qCWarning(runtime) << "Unexpected length of packet !!! - skipping"; continue; } double newAzimuth = azimuth; double newElevation = elevation; if ( data.startsWith("EL") ) newElevation = data.mid(3).toDouble(); else if ( data.startsWith("AZ") ) newAzimuth = data.mid(3).toDouble(); qCDebug(runtime) << "PSTRotator Positioning" << newAzimuth << newElevation; qCDebug(runtime) << "Object Positioning" << azimuth << elevation; if ( newAzimuth != azimuth || newElevation != elevation || forceSendState ) { forceSendState = false; azimuth = newAzimuth; elevation = newElevation; qCDebug(runtime) << "emitting POSITIONING changed" << azimuth << elevation; emit positioningChanged(azimuth, elevation); } } } #undef MUTEXLOCKER #undef POOL_INTERVAL ================================================ FILE: rotator/drivers/PSTRotDrv.h ================================================ #ifndef QLOG_ROTATOR_DRIVERS_PSTROTDRV_H #define QLOG_ROTATOR_DRIVERS_PSTROTDRV_H #include #include #include "GenericRotDrv.h" #include "rotator/RotCaps.h" class PSTRotDrv : public GenericRotDrv { Q_OBJECT public: static QList> getModelList(); static RotCaps getCaps(int); explicit PSTRotDrv(const RotProfile &profile, QObject *parent = nullptr); virtual ~PSTRotDrv(); virtual bool open() override; virtual void sendState() override; virtual void setPosition(double in_azimuth, double in_elevation) override; virtual void stopTimers() override; private slots: void checkRotStateChange(); private: void commandSleep(); void sendCommand(const QString& cmd); void readPendingDatagrams(); bool forceSendState; QTimer refreshTimer; QTimer timeoutTimer; QUdpSocket receiveSocket; QMutex drvLock; QHostAddress rotatorAddress; }; #endif // QLOG_ROTATOR_DRIVERS_PSTROTDRV_H ================================================ FILE: rpm_spec/qlog.spec ================================================ %define REPO_VERSION %(echo $REPO_VERSION) Summary: Qt Logging program for hamradio operators Name: QLog Version: %{REPO_VERSION} Release: 1%{?dist} License: GPLv3 Group: Productivity/Hamradio/Logging Source: https://github.com/foldynl/QLog/archive/refs/tags/v%{version}.tar.gz#/qlog-%{version}.tar.gz URL: https://github.com/foldynl/QLog/wiki Packager: Ladislav Foldyna BuildRequires: gcc-c++ BuildRequires: make BuildRequires: pkg-config BuildRequires: qt5-qtbase-devel BuildRequires: qt5-qtcharts-devel BuildRequires: qt5-qtwebengine-devel BuildRequires: qt5-qtserialport-devel BuildRequires: qt5-qtwebsockets-devel BuildRequires: hamlib-devel BuildRequires: libsqlite3x-devel BuildRequires: openssl-devel BuildRequires: qtkeychain-qt5-devel %description QLog is an Amateur Radio logging application for Linux, Windows and Mac OS. It is based on the Qt 5 framework and uses SQLite as database backend. %prep %global debug_package %{nil} %setup %build /usr/bin/qmake-qt5 PREFIX='/usr' %make_build %install INSTALL_ROOT=%{buildroot} make -f Makefile install %post %postun %files %{_bindir}/* %license LICENSE %doc README.md Changelog %{_datadir}/applications/qlog.desktop %{_datadir}//icons/hicolor/256x256/apps/qlog.png %{_metainfodir}/* %{_mandir}/man1/* %changelog * Sun Apr 26 2026 Ladislav Foldyna - 0.50.0-1 - [NEW] - Added Split detection - [NEW] - Added Developer and Support tools (PR #991 @aa5sh @foldynl) - [NEW] - Added a simple QSL label printing dialog (issue #562) - [NEW] - Added Cabrillo contest export - [NEW] - Added direct labeling of QSL Requested and QSL Received to the QSO context menu (PR #982 @aa5sh) - [NEW] - DXCC Submission Report (PR #967 @aa5sh) - [NEW] - DXC - Search pre-fills the callsign from New Contact if callsign is present - [NEW] - Settings - Added Danger Zone tab with Delete all passwords and QSOs - [CHANGED] - Mutex-free Omnirig1 and Omnirig2 - [CHANGED] - POTA/SOTA/WWFF/SAT list are download from QLog LOV-repo - [CHANGED] - Used an external lib for CSV parsing for LOV Download - [CHANGED] - Import - ADIF import updates DXCC only if it is missing in the QSO (issue #983) - [REMOVED] - Import - ADIF import does not include the option to update DXCC during the import - no longer needed - Fixed Callsign disappears when calling a DX in a split via VFO-B (issue #799) - Fixing Rendering issue (issue #989) - Updated debian packaging (PR @979 thx @dawkagaming) - Hamlib rigctld switching between VFO A/B doesnt change the band mode - added workaround (issue #999) - Awards internal redesign - Removed AN from the WAC award (issue #1010) * Thu Mar 19 2026 Ladislav Foldyna - 0.49.1-1 - Fixed Online Map OSM Access Blocked banner (issue #956) - Fixed Package build process issue - openssl-dev is missing (issue #957) - Fixed Missing dependence for QTKeychain (issue #964) - Added French Translation (PR #969 thx @fillods) * Fri Mar 13 2026 Ladislav Foldyna - 0.49.0-1 - [NEW] - Added Pack and Unpack Data and Setting - Computer to Computer Migration (issue #535) - [NEW] - Added Rig Sharing via Rigctld (PR #736 issue #159 @aa5sh @foldynl) - [NEW] - Added QSL Gallery - [NEW] - QSO Filter - Added REGEXP operator - [NEW] - Settings - TQSL - Added Path auto-detect and validation - [NEW] - Upload QSO - Added LoTW Station Location Combo (PR #929 @TrgoSk @foldynl) - [NEW] - QSO Export - Added Station Profile Filter - [NEW] - BandMap shows emergency frequencies (issue #462) - [NEW] - Added Speed Up Down Macros - WinKey and CWDaemon (issue #491) - [NEW] - Settings Winkey v2 - Added PaddleOnly Sidetone (issue #739) - [NEW] - Settings Winkey v2 and CWDaemon - Added Sidetone Freq - [NEW] - All County fields contain Completer for Ukraine, US, Japan, NZ, Spanish, Russia (PR #785 @aa5sh @foldynl) - [NEW] - Added County Awards for Ukraine, US, Japan, NZ, Spanish, Russia (PR #785 @aa5sh @foldynl) - [NEW] - ADIF 3.1.7 - Added new modes FT2, FREEDATA, RIBBIT_PIX, RIBBIT_SMS (issue #934) - [CHANGED] - Generic FTx for FT8/FT4 (FT2) in Alert and DXC filter (issue #937) - [CHANGED] - LoTW QSL matching algorithm uses Mode and Mode Group matching (issue #942) - [CHANGED] - Reduced download period for DXCC Entities from 21 to 7 days - LogbookWidget: Delete Performance Optimization - Fixed Awards POTA Activator Filter - Fixed man page - Fixing SATmode Activity is blank (issue #948) * Fri Jan 30 2026 Ladislav Foldyna - 0.48.0-1 - [NEW] - Rig Widget - tuning rig with mouse (issue #855) - [NEW] - Awards - Added User Filter Combo (issue #870) - [NEW] - Statistics - Added User Filter; new Dialog layout (issue #870) - [NEW] - Alerts - Added detailed Log Status settings (issue #817) - [NEW] - Settings - Enable/Disable sending color-coded status indicators back to WSJT-X (issue #885 @aa5sh @foldynl) - [CHANGED] - Omnirig Drivers: Removed QT AXContainers - pure Windows API code - [CHANGED] - Enabled Chekbox in Alerts Table (issue #867) - [CHANGED] - Serial Port Completer contains Horizontal ScrollBar - [CHANGED] - TCI - Settings - Default PWR defines the maximum output power - Fixed Omnirig drivers keep sending frequency change (issue #872) - Fixed ADIF import does not accept a default operator name (issue #884) - Fixed HamQTH URL from http to https (PR #886 @aa5sh) - Fixed Missing Band value when QSO from FLDigi (issue #892) * Fri Dec 19 2025 Ladislav Foldyna - 0.47.1-1 - [CHANGED] - DXC - VE7CC-type Cluster is correctly detected (PR #851 @kyleboyle) - [CHANGED] - Chat - Changed ON4KST URL (issue #857) - Fixed missing clear button for Search Callsign (issue #753) - Fixed Updated QSOs are not sent to wavelog (issue #847) - Fixed Fixed Omnirig(v2) getFreq, getVFO, setFreq (issue #853) - Fixed ADI Header does not follow ADIF Spec (issue #859) - Fixed OmniRig settings are not saved (issue #862) * Sat Dec 6 2025 Ladislav Foldyna - 0.47.0-1 - [NEW] - Adds theme options - native, light, dark (PR #718 @kyleboyle) - [NEW] - Implemented ADIF 3.1.6 - [NEW] - ADIF 3.1.6 - Added new modes FSK and MTONE - [NEW] - ADIF 3.1.6 - Added new contest IDs - [NEW] - ADIF 3.1.6 - Added new column EQSL_AG (Import/Export only) - [NEW] - Statistics, QSODetail contain deleted DXCC Entities (PR #728 @aa5sh @foldynl) - [NEW] - Settings Hamlib - Added support to define CIV Addr for Icom and Ten-Tec (issue #747) - [NEW] - Settings Hamlib - Added RTS and DTR control (PR #809 @aa5sh) - [NEW] - Settings GUI - Added options to switch distance unit - [NEW] - Added Linux manpage (PR #791 @dawkagaming) - [NEW] - Added a link to the GitHub release notes under Help-Whats New - [NEW] - Added workaround for WriteLog - always call callbook lookups (PR #833 @sjwoodr) - [NEW] - Awards - Added Not-Confirmed filter (PR #836 @aa5sh) - [NEW] - Rotator Widget - Added buttons to change button profiles - [CHANGED] - Settings - Added network loop detection for WSJTX Forward (issue #815 @aa5sh @foldynl) - [CHANGED] - Rotator - Rotator timeout is set to 5s (PR #823 @aa5sh) - [CHANGED] - Double Spin Boxes - the decimal separator is forced to be a period - [DELETED] - WSJTX Widget - Removed Freq and Mode - Fixed QSODetail - Anti-meridian bug on map (issue #786) - Fixed Statistics: Center maps on QTH longitude (issue #824) - Fixed OmniRig disconnecting when its status changed unexpectedly (issue #832) * Fri Oct 31 2025 Ladislav Foldyna - 0.46.2-1 - Fixed Spaces after QRZ.com name (issue #767) - Fixed SPID Rot1Prog Rotator more 360 deg (issue #775) - Fixed build instructions on Debian (PR #777 @leventelist) - Fixed Bandmap truncated output despite having space (issue #779) - Fixed Statistics - Anti-meridian bug on map (issue #786) - Fixed Binary file in the tarballs (issue #794) - Fixed LOTW and eQSL upload status updated even when nothing is uploaded (issue #807) - Fixed Imported QSOs contain incorrect My DXCC info (issue #812) - Fixed Statistics Dialog does not show confirmed grids * Fri Sep 26 2025 Ladislav Foldyna - 0.46.1-1 - Fixed QSO filter incorrectly displays inserted date (issue #752) - Fixed Logbook Search Icon is not centered (issue #753) - Fixed Using CQRLog API Key for Clublog (issue #759) - Fixed Online Service Password leaking via debug files (issue #760) - Fixed Tabs are not showed properly (PR #762 @aa5sh) - Fixed inability to edit Power in QSO Detail (issue #763) * Fri Sep 12 2025 Ladislav Foldyna - 0.46.0-1 - [NEW] - NewContact: POTA/SOTA/WWFF/IOTA info is taken from the nearest spot (@aa5sh @foldynl) - [NEW] - DXSpots are sent to Flex Radio (PR #694 @aa5sh) - [NEW] - CWConsole - Added support for automatic periodic sending of CW Macros (issue #708) - [NEW] - Added mapping of Winkey hardware buttons to CW macro keys F1–F4 (issue #711) - [NEW] - Added Search Types to the Logbook Search Editbox (issue #733) - [NEW] - Rig - Periodic Rig status reporting independent of its changes (PR #730 @aa5sh) - [NEW] - Settings - Serial Port Completer for MacOS and Linux (PR #737 @aa5sh) - [NEW] - Hamlib - Attempt to send Power On when connecting the Rig - [NEW] - POTA Spots Info is received from the API at api.pota.app to improve POTA detection - [CHANGED] - Logbook Search Filter Widget - Added Search Widget - [CHANGED] - QSO Detail Country boxes use the new Search Filter Widget - [CHANGED] - NewContact - RST field uses the Overwrite Mode - [CHANGED] - NewContact - Default cursor position in RST is at the first character - [CHANGED] - Setting Dialog - Modes - Default Report can define cursor position - [CHANGED] - HamlibRot - Changed Poll Interval from 500 to 2000ms - Fixed DUPE is not detected for the first Contest QSO (issue #699) - Fixed FREQ_RX/BAND_RX is present only when RX and TX freqs are different (issue #714) - Fixed Repeated log recording (issue #722) - Fixed QSO Filter contains untranslated QSO items (issue #732) * Fri Jul 11 2025 Ladislav Foldyna - 0.45.0-1 - [NEW] - Single Dialog for Upload/Download Online Services (issue #448) - [NEW] - Added option to swap paddles to Winkey settings (issue #676) - [NEW] - Added native support for the FLRig interface (issue #679) - [NEW] - Added New Version Notification - only for MacOS and Windows (PR #669 @aa5sh) - [NEW] - QRZ Upload - Added support for multiple API Keys - [NEW] - Logbook - Added highlighting to the filter button when a filter is active - [NEW] - WSJTX - Filtered label is shown when filter is enabled - [NEW] - DXC - Filtered label is shown when filter is enabled - [NEW] - Added support to Upload Cloudlog/Wavelog - [NEW] - Add JS8 to legacy_modes.json (issue #677) - [CHANGED] - Unification Settings storage - [CHANGED] - Calculate distances according to IARU rules - Fixed missing Wsjtx Spot values in the AlertWindow * Sun May 11 2025 Ladislav Foldyna - 0.44.1-1 - Fixed Rotator Widget Seg Fault for new users (issue #666) * Fri May 9 2025 Ladislav Foldyna - 0.44.0-1 - [NEW] - Activity Manager - Added SKCC, UKSMG and FISTS as Dynamic Fields - [NEW] - QSO - FISTS, SKCC, and UKSMG are auto-filled from MembershipQE (issue #628) - [NEW] - Rotator - Added QSO destination needle (issue #644) - [NEW] - QSO Detail - Adds grayline and short path (PR #653 @kyleboyle) - [CHANGED] - Rotator - Needle colors correspond to the online map (issue #644) - Fixed TCI cw_macros must contain RigID (issue #663) - Fixed TCI setKeySpeed sets keyer and macros speed (issue #663) - Fixed Data mode missing in rig control window - rigctld (issue #660 @aa5sh) - Improved DXC Mode detection - Updated Simplified Chinese translation * Sat Apr 19 2025 Ladislav Foldyna - 0.43.1-1 - Fixed Click on PHONE DX Spots sets wrong mode (issue #453) - Fixed No freq via Omnirig for IC7400 (issue #639) * Fri Apr 4 2025 Ladislav Foldyna - 0.43.0-1 - [NEW] - Added support to receive QSOs from CSN SAT Device (PR #610 @aa5sh) - [NEW] - Bandmap - Multiple independent bandmap windows (PR #593 @kyleboyle @foldynl) - [NEW] - Winkey Keyer driver currently supports v1 and v2 hardware - [NEW] - QSO Detail - QSLs QSLr Msg is editable - [NEW] - Activity Manager - Added new dynamic field - QSL Message Send - [CHANGED] - HamlibDrv - Enabled Power, RIT, XIT, Morse for RIG Netctl - [CHANGED] - QSO Detail - QSLMSG replaced by QSLMSG_RCVD (issue #633) - [CHANGED] - eQSL Upload - Added an option to choose a QSLMsg field - [CHANGED] - eQSL Download - eQSL QSLMSG is stored to QLog QSLMSG_RCVD - [CHANGED] - eQSL Download - Added QSLMSG_RCVD merging - eQSL message is appended - Fixed Speed pot doesn't seem to work with a WinKeyer; double chars (issue #618) - Fixed BandMap Spots colour change after a QSO (issue #632) - Fixed Incorrect Field Mapping for Received eQSL Messages (issue #633) * Fri Mar 7 2025 Ladislav Foldyna - 0.42.2-1 - Fixed Logbook country translations (issue #608) - Fixed Unexpected dialog when QSO after contest (issue #614) - Fixed Statistics Widget does not display NULL continents (@aa5sh) - Fixed Statistics Widget does not display NULL Band, Mode - Fixed Statistics Widget TOP10 does not display removed DXCCs - Fixed Statistics Widget TOP10 does not display translated country names - Fixed Awards Widget does not display removed DXCCs * Sat Feb 22 2025 Ladislav Foldyna - 0.42.1-1 - Fixed Unexpected timezone info (issue #600) - Fixed DXCC Statistics picks more entities (issue #601) - Fixed a crash when no internet connection * Fri Feb 14 2025 Ladislav Foldyna - 0.42.0-1 - [NEW] - Awards - Added Slots - total over each band (issue #538) - [NEW] - Awards - Added Grid Award - 2/4/6 Chars grid (issue #564) - [NEW] - Settings - Added options to switch 12/24 time and date format (issue #573) - [NEW] - Activity Manager - Added new dynamic fields - Rig (issue #575) - [NEW] - LoTW Import - Fill missing information also for confirmed QSOs (@aa5sh) - [NEW] - Added CW macro QTH - [NEW] - DXC - Added 15min Trend - [CHANGED] - Changed IOTA LOV Source, the official web is used - [CHANGED] - CSV Export: Time and Date formats use ISO8601 format (issue #562) - [CHANGED] - Settings - Renamed Shortcuts to GUI tab - [CHANGED] - LOV - Improved LOV download performance (PR #582 @aa5sh) - Partial fix Windows State/Size does not save in case of fullscreen (issue #418) - Fixing TCI error when you change Rig (issue #526) - Fixed DXCC Award total worked confirmed counts included deleted entities (PR #588 @aa5sh) - Fixed Raspberry PI Flatpak - Import Select file dialog crashes (issue #589) - Suppressed the ability to edit Contest fields after the start (issue #590) * Tue Jan 21 2025 Ladislav Foldyna - 0.41.1-1 - Fixed compilation issue under Debian 12 (issue #571) - Fixed Incorrect GPL version in rpm/deb packages (issue #572) - Fixed MacOS floating utility window bug (PR #576 @kyleboyle) - Updated IT translation * Sat Jan 11 2025 Ladislav Foldyna - 0.41.0-1 - [NEW] - Logbook - Added a new context menu item - Update QSO from Callbook (issue #450 @aa5sh) - [NEW] - DIGI mode is used in case of DXC Digi Spots (issue #480) - [NEW] - DXC - Retrieve information about SOTA, POTA, IOTA and WWFF from comment (issue #482) - [NEW] - Alert - Added SOTA, POTA, IOTA and WWFF filter - [NEW] - Added the COM Port Completer for Windows platform (issue #490) - [NEW] - Settings - Added DXCC Confirmed By options (issue #508) - [NEW] - Added POTA Export Formatter (activator/hunter) (PR #514 @kyleboyle) - [NEW] - CW Console - CW Halt with the user-defined shortcut (issue #518) - [NEW] - Added an input parameter to save debug message to file - [NEW] - Logbook - Added sorting function to logbook table columns (PR #540 @kyleboyle) - [NEW] - Network Notification - Added Rig Status Notification - [NEW] - Implemented ADIF 3.1.5 - [NEW] - ADIF 3.1.5 - Added new submodes FSKH245 and FSKH105 for HELL - [NEW] - ADIF 3.1.5 - Added new contest IDs - [NEW] - ADIF 3.1.5 - Added new columns (Import/Export only) - [NEW] - ADIF 3.1.5 - Added My DARC DOK to Station Profile - [CHANGED] - Settings: disabled band and mode name modification - [CHANGED] - DX Stats contain all enabled bands (issue #538) - [CHANGED] - Removed Freq, TimeDate On/Off Data Type Indicators (issue #552) - [CHANGED] - ADIF 3.1.5 - VUCC and MY_VUCC can contain 6 or 4-chars locators - [CHANGED] - Stop exporting default value N for qsl_rcvd, qsl_sent, lotw/dcl/eslq_qsl_rcvd/sent - [CHANGED] - Extended QSL/Import Dupe matching rule to Callsign, Band, Mode, Time and Sat_Name (issue #563) - Fixed MacOS - keep floating windows visible on app unfocus (issue #530) - Fixed Contest Filter ignores the first QSO (issue #529) - Fixed It is not possible to quit Qlog with detached widgets Rot and Rig (issue #534) - Fixed ADX/CSV/JSON do not export non-ASCII chars (issue #542) - Fixed Checking the 60m checkbox in cluster filters allows 160m spots to appear (issue #543 @aa5sh) - Fixed Problems uploading to QRZ.com (issue #559 PR #561 @aa5sh) - Fixed DX Stat screen is jumping up/down - Fixed Omnirig drivers: Digi modes are not correclty recognized * Fri Nov 29 2024 Ladislav Foldyna - 0.40.1-1 - Fixed Bands - Added missing 8m band (issue #515) - Fixed CW Console - EXCSTR does not work properly (issue #517) - Fixed Activity Manager - Missing Propagation Mode None (issue #519) - Fixed QSO Filter - filter fields with random order (PR #525 @aa5sh) - Fixed TCI error when you change Rig (issue #526) - Fixed NewContact - satellite mode too wide (issue #527) * Sun Nov 24 2024 Ladislav Foldyna - 0.40.0-1 - [NEW] - Activity Manager - merged Layout Manager and profiles (issue #408) - [NEW] - Activity Manager - Added new dynamic fields - Contest fields, RX/TX Power - [NEW] - Added light support for contests (issue #345) - [NEW] - Added CW macros EXCHSTR, EXCHNR, EXCHNRN - [NEW] - Export Filter - Added user filter combo (original idea PR #476 @aa5sh) - [NEW] - New Contact - Added expand/collapse button to QSO field tab widget (PR #495 @kyleboyle) - [NEW] - Alert - Added CQZ and ITUZ filters - [NEW] - KSTChat - Added a new 40MHz room (PR #496 @kyleboyle) - [NEW] - Station Profile contains Operator Callsign (issue #441 @kyleboyle) - [NEW] - Station Profile contains County (issue #493 @kyleboyle) - [NEW] - Statistics - Adds time of day and better qso mapping (PR #501 @kyleboyle) - [NEW] - Bandmap - Tooltip shows a spotter callsign (PR #507 @Skittlebrau) - [CHANGED] - New Contact - Renamed DXCC Tab to DX Stats contains DXCC and Station Statistics (issue #477) - [CHANGED] - QSL Import dialog - Detail text is selectable by mouse and keyboard - [CHANGED] - Removed Main Menu Layout; Activity Manager is in the bottom-left corner - [CHANGED] - Removed Keep Options from the Equipment Menu - use Activity Manager for it - Fixed issue when CW is always selected after Settings exiting or connecting the Rig - Updated Timezone definition file - version 2024b * Sat Oct 5 2024 Ladislav Foldyna - 0.39.0-1 - [NEW] - DXC - Added Full-text search - [NEW] - Select S in RST Edit when focused (issue #454) - [NEW] - Alerts - Added Member Column - [CHANGED] - HamlibDrv Rig/Rot- Added multiplatform reliable sleep - [CHANGED] - Changed Backup policy - [CHANGED] - Logbook page size - improved performance - [CHANGED] - Logbook - CTRL-A (Select All) is disabled - [CHANGED] - Awards - Bands are displayed based on the Settings (issue #452) - [CHANGED] - WSJTX - More reliable detection of CQ stations (PR #471 @aa5sh) - [CHANGED] - WSJTX - SOTA/POTA/WWFF/SIG are being added to the logged QSO (PR #463 @aa5sh) - [CHANGED] - Stats - Add a confirmation dialog for displaying over 50k QSOs on the map - [CHANGED] - New Contact - Starting QSO Timer when Rig online and WSJTX Update Callsign Status is received - [CHANGED] - Added a postponed handling for Rig soft errors (issue #472) - Fixed WSJT-X does not emit band change if rig is disconnected (issue #447) - Fixed Wrong import of ADIF file of another log program (issue #455) - Fixed WSJTX log record is stored incorrectly if it contains non-ASCII chars(issue #458) - Fixed ADIF import does not import records with old DXCC Entities (issue #459) - Fixed ADIF import incorrectly uses Station Profile parameters (issue #461) - Fixed Logbook - QSO Table Column Width Does Not Stick (issue #464) - Fixed Alerts Window displays OOB Spots (issue #469) - Fixed Field values from past QSOs are used incorrectly in case of WSJTX QSOs (#issue 470) * Thu Aug 29 2024 Ladislav Foldyna - 0.38.0-1 - [NEW] - Logbook - Added Send DX Spot to the QSO Context Menu - [NEW] - DX Filter - Added Dedup Time/Freq difference setting (@aa5sh) - [NEW] - Rig Setting - Added RTS/DTR PTT Type support (issue #353) - [NEW] - Bandmap - Scrollbar position is saved per band (issue #415) - [NEW] - New Contact - Added a dynamic value completer for SIG field (issue #425) - [NEW] - Awards - Added SOTA/POTA/WWFF (@aa5sh issue #311) - [NEW] - Awards - Added Not-Worked Filter - [NEW] - New Contact - Added Long Path Azimuth info - [NEW] - POTA Fields allow a comma-delimited list of one or more POTA Refs - [NEW] - WSJTX tunes freq/mode like Rig if rig is disconnected - [CHANGED] - Alert Widget is a Dock Widget (issue #399) - [CHANGED] - QLog adds more information from callbook for WSJTX QSOs (issues #403 #405 #420) - [CHANGED] - File Open dialogs are not a native dialog under Linux (issue #427) - [CHANGED] - Profiles transferred to DB - [CHANGED] - LOV last_dates transferred to DB - [CHANGED] - DX Cluster - Login Callsign is always the base Callsign - [REMOVED] - Setting DXCC Date - Fix for MacOS Layout Geometry Restore (@aa5sh) - Fixed TQSL does not block GUI thread - Fixed MacOS build process (@aa5sh) * Fri Jul 26 2024 Ladislav Foldyna - 0.37.2-1 - Fixed Field QSL Send Via should be retained (issue #413) - Fixed Set rotator position fails if azimuth > 180 (issue #417) - Fixed Windows State/Size does not save in case of fullscreen (issue #418) - Fixed Significant rounding during azimuth calculation (issue #422) - Updated Simplified Chinese translation - Updated Spanish translaction - Added Italian translation (thx IK1VQY) * Wed Jul 10 2024 Ladislav Foldyna - 0.37.1-1 - Fixed QSO Table Callsign filter is not filled properly (issue #401) - Fixed DXC zero frequency for last QSO in case of FT8 QSOs (issue #404) - Fixed Callsign Context Menu does not work (issue #409) - Fixed QSO Detail Save and Edit buttons are not translated (issue #410) * Mon Jul 1 2024 Ladislav Foldyna - 0.37.0-1 - [NEW] - Added Shortcuts Editor (issue #293) - [NEW] - Added QO100 Bandplan to correctly categorize the DX Spots - [NEW] - Improveded detection of SH/DX SHF Spots - [NEW] - Online Map - Added WSJTX CQ Spots - [NEW] - WSJTX - Sortable View - [NEW] - Alerts - Sortable View - [NEW] - Added Spanish translation (thx LU1IDC) - [NEW[ - Added Search Callsign Clear Button (issue #396) - [CHANGED] - QRZ auth should be over POST with form data (issue #389) - [CHANGED] - Big CTY file is used - [CHANGED] - Callbook Country DXCC ID is used in case of difference from Big CTY - [CHANGED] - Removed ALT+W and CTRL+DEL shortcuts - [CHANGED] - Removed New Contact and Save Contact from Logbook Main Menu - Fixed Guantanamo KG4 Issue (issue #372) - Fixed QRZ Lookup Not Including Full Name - History (issue #388) - Fixed Spot Last QSO contains TX freq, should contain RX freq (issue #390) - Fixed Spot Last QSO must contain Freq in kHz (issue #391) - Fixed Bandmap select previous selected callsign issue (issue #394) - Fixed Malfunctioning tuning of WSJTX Alert spot - Fixed DXCC Status for FT4 Spots incorrectly identified in WSJTX * Fri Jun 7 2024 Ladislav Foldyna - 0.36.0-1 - [NEW] - WSJTX: Added support to received ADIF QSO Log record - [NEW] - Sat mode is derived from RX/TX Freq - [NEW] - Logbook filters change color when enabled - [NEW] - Frequency input boxes PageUp/Dn switches the band (issue #360) - [NEW] - CTRL + PgUp/Dn switch band on the connected rig - global shortcut (issue #360) - [NEW] - Added number of filtered QSOs (issue #374) - Fixed Callbook query does not work (issue #377) - Fixed Logbook columns are reordered after Delete (issue #383) - Fixed Missing Republic of Kosovo flag (issue #384) * Tue May 21 2024 Ladislav Foldyna - 0.35.2-1 - Improved delete performance; added delete progress bar (issue #351) - Fixed Password with plus is incorrectly sent to online services (issue #366) - Fixed Compilation issue under v4.6 (issue #368) - Fixed Network Rig configuration is not saved (issue #370) * Mon May 6 2024 Ladislav Foldyna - 0.35.1-1 - Fixed Free QRZ callbook - Name is not populating (issue #363) - Fixed Incorrect CW segment freqs (issue #365) * Fri May 3 2024 Ladislav Foldyna - 0.35.0-1 - [NEW] - Added Rot Interface PSTRotator Network - [NEW] - Added QSO/QSL Since option to eQSL Dialog - [NEW] - Bandmap - Current Mode segment visualisation - [NEW] - CW Console - Added Word/Whole mode switch - [NEW] - Added Callbook Profile Image Widget - [NEW] - ASCII conversion based on Text-Unidecode/iconv algorithm (issue #316 #350) - [NEW] - ITU/CQ Zones can be defined in Station Profile (issue #358) - [CHANGED] - Spacebar is used as a focus changer for fields where space is not allowed - [CHANGED] - Focus does not select text in the input fields - [CHANGED] - Force XCB under Linux Wayland - [CHANGED] - Bandmap - Added Callsign/Freq/Mode to tooltip (issue #355) - Fixed incorrect ADIF date format for clublog_qso_upload_date (issue #342) - Fixed The last name from Callbooks queries (issue #346) * Mon Mar 25 2024 Ladislav Foldyna - 0.34.0-1 - [NEW] - Rotator Widget - Azimuth by Clicking - [NEW] - Rotator Widget - QSO button provides Short/Long Path (issue #330) - [NEW] - Equipment Menu - Added Keep Options between application restart (issue #331) - Fixed TCI - Thetis Connection issue (issue #327) - Fixed TCI - Spots To Rig are not displayed (issue #328) - Fixed Bandmap unintentionally emits frequency labels (issue #333) - Fixed Failing to load grid square for G and EI SOTA summits (issue #336) - Fixed HRDLog On-Air message is not sent (issue #337) - Fixed Offline Map - Improved Path drawing * Sat Mar 9 2024 Ladislav Foldyna - 0.33.1-1 - Fixed Rotator offline map is incorrectly centered (issue #324) - Fixed Hamlib integration not working (issue #325) - Fixed issue when Hamlib reopen rig caused Initialization Error - Fixed issue when Omnirig Drv did not emit rigIsReady signal * Fri Mar 8 2024 Ladislav Foldyna - 0.33.0-1 - [NEW] - Added Rig Interface TCI - [NEW] - Callbook search can be temporarily paused - Improved DXC Mode recognition - Fixed Modal dialog blinks - Windows platform (issue #315) - Fixed LoTW and eQSL download are only QSLs dowloads - button label changed (issue #318) - Fixed i18n: Country Names and Prop-modes are translated (issue #322) * Sat Feb 10 2024 Ladislav Foldyna - 0.32.0-1 - [NEW] - Added Rig Interface Omnirig v1 (Windows only) - [NEW] - Added Rig Interface Omnirig v2 (Windows only) - [NEW] - Clublog - Added Clear Clublog and reupload QSOs - [NEW] - Clublog - Added Real-time Insert/Update/Delete - [CHANGED] - Clublog - Upload callsign is derived from the Current Profile Callsign - Fixed clang linker failed issue (issue #301) - Fixed SAT Mode U/U missing (issue #308 PR #309 thanks ea5wa) - Fixed Multiple QSO selection. Callsigns modified by mistake (issue #310) - Fixed Callbook query cache is not properly cleared when Callbook settings change (issue #313) * Fri Jan 5 2024 Ladislav Foldyna - 0.31.0-1 - [NEW] - DXC - Improved Mode recognition - [NEW] - DXC - Switch Rig mode based on DXC Spot Mode (issue #217) - [NEW] - DXC - Added Spot Country Column (issue #273) - [NEW] - DXC - Added Menu for server management - [NEW] - DXC - Added Auto-connect to server - [NEW] - DXC - Added Keep QSOs Context Menu - [NEW] - DXC - Added Clear QSO Context Menu - [NEW] - DXC - Added support for SH/DX response parsing - [NEW] - DXC - Added support for username, password for connection - [CHANGED] - DXC - Commands Combo changed to function button with selectable function - [CHANGED] - DXC - DX Spot is prepared via DXC Command Line, Remark dialog was removed - [NEW] - Online Map - IBP station double-click tunes freq and switch Rig mode - [NEW] - Main Window - Current profile name is shown (issue #282) - [NEW] - Import - Details can be saved to file (issue #284) - [NEW] - Added Simplified Chinese translation (PR #285 thank BG7JAF) - [NEW] - New Contact - Enter saves QSO if QSO time is running (issue #293 - partial) - [NEW] - New Contact - Callsign Enter event saves QSO if no Callbook is active - Pileup Mode (issue #293) - [NEW] - RIG Widget - RIT/XIT are displayed with user-friendly units (issue #294) - [CHANGED] - SAT List download - Shortened download period for SAT list from 30 to 7 days - Fixed ADI Import is too slow (issue #270) - Improved Import/Export Performance - Fixed Missing Satellite Mode SX (issue #291) - Fixed QSO Detail - Issue when Sat-Name field was always disabled - Fixed RPM build - Installed (but unpackaged) metainfo file issue * Fri Dec 1 2023 Ladislav Foldyna - 0.30.0-1 - [NEW] - QSL Images are stored in the database - [NEW] - Added AppStream Metainfo File (PR #262 thanks AsciiWolf) - [NEW] - Added (WPX) prefix (issue #263) - [NEW] - Added WPX Award statistics - [NEW] - Added support for external translation files(issue #275) - [CHANGED] - Removed QSOID from Export dialog column setting (issue #258) - Fixed Date editor does not support NULL value in Logbook Direct Editor (issue #256) - Fixed duplicate entry in Windows Add or Remove - only Window platform (issue #260) - Fixed RST fields revert to 59 after changing them (issue #261) - Fixed Cannot change TQSL Path in Settings - flatpak (issue #271) * Mon Nov 13 2023 Ladislav Foldyna - 0.29.2-1 - Fixed QLog is already running error popup on MacOS (issue #257 thanks rjesson) * Fri Nov 10 2023 Ladislav Foldyna - 0.29.1-1 - Fixed QSL cards tooltip are not displayed under qt6.5 (issue #248) - Fixed Distance unit is not displayed in QSO Info under Windows (issue #250) - Fixed Editing STATION_CALLSIGN can cause unwanted change in QSO Detail (issue #251) - Fixed QSO Detail Operator Name containes an incorrect value (issue #252) - Fixed Calls with VE, VA are coding as Amsterdam & St Paul Islands instead of Canada (issue #253) - Fixed LoTW QSL import reports unmatched QSOs sometime (issue #254) * Fri Oct 20 2023 Ladislav Foldyna - 0.29.0-1 - [NEW] - Added user-defined layout for New QSO Detail widget - [NEW] - Main window State and Geometry can be saved to layout profile - [NEW] - Awards - Added WAS - [NEW] - Awards - WAZ/ITU/WAC show all possible values - [NEW] - Distance unit (km/miles) is controlled by OS Locale - [CHANGED] - Removed SAT Tab - field can be added via Layout Editor - Improved Import QSO performance - Fixed QLog crashes if POTA, SOTA or WWFF contain incorrect values (issue #245) - Fixed QSOs are not uploaded to QRZ and HRDLog if fields contain HTML delimiter strings (issue #247) * Fri Sep 22 2023 Ladislav Foldyna - 0.28.0-1 - [NEW] - Added ON4KST Chat Support - [NEW] - Added Az BeamWidth and Az Offset to Antenna Profile - [NEW] - Double-Clicking the IBP callsign in the online map tunes the frequency - Fixed Browse button should open an expecting folder (issue #241) - Fixed Reword QSL buttons and Settings in QSO Details and Settings (issue #242) * Mon Aug 21 2023 Ladislav Foldyna - 0.27.0-1 - [NEW] - Added HRDLog Support - Fixed Text field alignment (issue #233) - Fixed Rig/Rot Connection port type selection (issue #235) - Fixed Incorrect Distance Value in WSJTX Widget (issue #236) - Fixed Incorrect WSJTX locator target on the map (issue #237) * Sun Jul 30 2023 Ladislav Foldyna - 0.26.0-1 - [NEW] - Added user-defined layout for New QSO widget - [NEW] - Pressing Spacebar in Callsign field skips RST fields - [NEW] - Added user-defined URL for web lookup (issue #230) - Fixed WSJTX QSOs should have an Operator Name from Callbook (issue #223) - Fixed US call area suffixes not handled correctly (issue #226 thanks Florian) - Fixed QSO Filter Detail allows to save an empty Filter Name (issue #228) * Mon Jul 17 2023 Ladislav Foldyna 0.25.1-1 - Fixed Unexpected mode change when Setting Dialog is saved (issue #222) - Fixed QSL_SENT field has an incorrect ADIF name (issue #225) * Tue Jul 4 2023 Ladislav Foldyna - 0.25.0-1 - [NEW] - Export - Added CSV Format - [NEW] - Export - Added Type of Export Generic/QSLs (issue #209) - [NEW] - Export - Added Exported Columns Setting - [NEW] - Export - All export formats use the ADIF field name convention - [CHANGED] - Export - JSON format contains a header - JSON format change - [CHANGED] - Default Statistics Interval is curr_date-1 and curr_day - Fixed Errors from Secure Storage are not shown (issue #216) - Fixed RX/TX Bands are uneditable when RX/TX freqs are missing (issue #220) * Fri Jun 16 2023 Ladislav Foldyna - 0.24.0-1 - Fixed Incorrect FT4 mode-submode (issue #212) - Fixed CONTESTIA mode should be CONTESTI (issue #213) - Fixed Context menu deselects NewContactEditLine (issue #215) - Fixed incorrect WSJTX Filter initialization (issue #218) * Fri Jun 9 2023 Ladislav Foldyna - 0.23.0-1 - [NEW] - Added CWDaemon Keyer Support - [NEW] - Added FLDigi Keyer Support - [NEW] - Online Map - based on locale, the map language is selected (Only EN, FR, GE supported - issue #180) - Fixed After entering longer QTH, the field content is not left-aligned (issue #157) - Fixed wrong QSO Time in case of JTDX (issue #204) - Fixed QSL Sent Date fields are not filled if QSL Sent Status fields are Y (issue #207) * Sun May 7 2023 Ladislav Foldyna - 0.22.0-1 - [NEW] - ADIF Import - My Profile is used to define default values - [NEW] - ADIF Import - Checking a minimal set of input fields (start_time, call, band, mode, station_callsign) - [NEW] - ADIF Import - Added Import Result Summary + Import Detail Info - [NEW] - Main Menu - Added Help -> Mailing List. - [NEW] - Export - Filter for the exported QSOs - [CHANGE] - Renamed Locator to Gridsquare - Fixed Some anomalies in the input and processing of QSLr Date (issue #192) - Fixed User unfriedly CW Keyer Error (issue #194) - Fixed ADIF import (issue #196) - Fixed Operator field is incorrectly used (issue #197) - Fixed Crash if an unknown POTA & SOTA/WWFF Setting is entered (issue #198) - Fixed FLDIGI cannot connect QLog (issue #199) - Fixed if ADIF record is missing band info, add this from freq field (thx DJ5CW) * Sun Apr 16 2023 Ladislav Foldyna - 0.21.0-1 - [NEW] - Rotator - Added Used-Defined Buttons - [NEW] - Rotator - Added Destination Azimuth Needle - [NEW] - Online Map - Added Antenna Beam Path - [NEW] - Rig - Combos are disbled when disconnected - [NEW] - Club Member Lists (issue #60) - [NEW] - Alert Table shows rule names - [CHANGED] - Alerts, DXC and WSJTX Network Notifications - Fixed Antenna Azimuth Negative Value (issue #191) - Fixed CTY file is not loaded when duplicate record (issue #193) * Tue Mar 14 2023 Ladislav Foldyna - 0.20.0-1 - [NEW] - Added MUF Layer to online map - [NEW] - Added International Beacon Project (IBP) Beacons to online map - [NEW] - Centering the map on the current profile at start (issue #185) - Fixed incorrect ADIF interpretation of _SENT fields (issue #176) - Fixed Awards Dialog, Table double click for ITU/CQZ/WAZ/IOTA shows incorrect QSOs (issue #177) - Fixed ADIF double-type fields when 0.0 is currently mapped to NULL (issue #178) - Fixed QSO Detail to save NULL instead of empty string (issue #179) - Fixed ADIF Import default _INTL values are now stored correctly (issue #183) - Fixed Maps show an incorrect path if from/to grids are the same (issue #186) - Fixed Online Maps incorrect Bounds if Bandmap callsign double-click (issue #188) - Updated German translation (thx DL2KI) * Fri Feb 17 2023 Ladislav Foldyna - 0.19.0-1 - [NEW] - Added Aurora Layer to online map - [NEW] - Logbook - filter options are saved and restored - [NEW] - Map Setting is saved and restored (issue #140) - [NEW] - QSO Duration (issue #158) - [NEW] - DX Cluster uses monospace font (issue #164) - [NEW] - Awards - if click on the Entity/band the logbook filter is set (issue #168) - [NEW] - WSJTX - Added Multicast support (issue #172) - Fixed WWFF LOV Download (issue #169) * Sun Jan 15 2023 Ladislav Foldyna - 0.18.0-1 - [NEW] - ADIF 3.1.4 updates - Added new modes FREEDV and M17 - Added new band (submm) - Adopted Altitude (for SOTA only) - Adopted POTA (includes POTA List) - Adopted Gridsquare_ext (only import/export) - Adopted Hamlogeu_* (only import/export) - Adopted HamQTH_* (only import/export) - [NEW] - Added new DXCC Status and color for it - Confirmed - [NEW] - New Contact - Tab selection is saved - [NEW] - Grid can contain 8-characters - [NEW] - User filter can contain NULL value - [NEW] - Compilation - added variables for external sources - [NEW] - My DXCC/CQZ/ITUZ/Country is filled - [NEW] - Alerts - Added Aging (issue #153) - [NEW] - Alerts - Added DXCC Status Color (issue #153) - [NEW] - DXC - Added Log Status to filter (issue #154) - [NEW] - DXC - Added Spot deduplication to filter (issue #154) - [NEW] - WSJTX - Added CQ-Spot Filter (issue #155) - [NEW] - QSO Detail contains DXCC Tab (issue #156) - [CHANGED] - New QSO DXCC Tab reworked (issue #144) - [CHANGED] - All DXCC Stats are computed based on My DXCC instead of My Callsign - [CHANGED] - Station Profile Setting layout * Sun Dec 18 2022 Ladislav Foldyna - 0.17.0-1 - [NEW] - NetPort and Polling interval can be defined for NET Rigs - [NEW] - NetPort can be defined for NET Rots - [NEW] - Added Saving Bandmap Zoom per band (issue #137) - [NEW] - CW speed synchronisation (issue #139) - Fixed Missing callbook data when callsign has prefix (issue #133) - Fixed Winkey2 echo chars are incorrectly displayed in CW Console (issue #141) - [CHANGED] - Online Map - Gray-Line is enabled by default - Update Timezone database * Sun Nov 20 2022 Ladislav Foldyna - 0.16.0-1 - [NEW] - SOTA/IOTA lists updated regularly - [NEW] - Added WWFF list, updated regularly - [NEW] - QTH/Grid are filled based on SOTA/WWFF - [NEW] - DXC/WSJTX columns are movable, added column visibility setting - [NEW] - DXC/WSJTX columns layout is saved - [NEW] - Added Wiki&Report Issue links to Help section - [NEW] - About dialog contains run-time information - [NEW] - Solar Info as a ToolTip - [NEW] - QSO Manual Entry Mode - Fixed Bandmap unlogical animation when band is changed (issue #128) - Fixed Bandmap marks are not displayed correctly when RIT/XI (issue #131) - Fixed Setting Dialog size - Update Timezone database * Sun Oct 16 2022 Ladislav Foldyna - 0.15.0-1 - Fixed Keeping the Bandmap RX mark always visible when centre RX is disabled (issue #115) - Fixed Equipment Menu: Swapped Connect Keyer and Rig (issue #122) - Fixed Callsign is deleted when clicking bandmap (issue #126) - Fixed typo in the Map layer menu (issue #127) - Fixed compilation issues & warning under QT6 - preparation for QT6 migration * Sun Oct 2 2022 Ladislav Foldyna - 0.14.1-1 - Fixed CW Console - HALT Button is not enabled under Ubuntu flavours (issue #124) * Thu Sep 29 2022 Ladislav Foldyna - 0.14.0-1 - [NEW] CW Console (Winkey2, Morse over CAT support) - [NEW] DX Cluster pre-defined commands (send last spot, get stats) - [NEW] Added DX Cluster Views (Spots, WCY, WWV, ToALL) - [NEW] Implemented DX Cluster Reconnection - [NEW] Remember last used DX Cluster - [CHANGED] - UI unifications - Rot/Rig/DXC - Fixed COM port validation for Windows platform - Fixed Reconnecting (DXC/Callbook) (issue #110) - Fixed DX Cluster crashes when DXC server is not connected and a command is sent (issue #111) - Fixed Bandmap callsign selection not fully works (issue #116) * Sat Aug 6 2022 Ladislav Foldyna - 0.13.0-1 - [NEW] QSY Contact Wiping (issue #100) - [NEW] Timeoff is highlighted when QSO timer is running (issue #100) - [NEW] Callsign whisperer - [NEW] Bandmap - Spot's color is recalculated when QSO is saved - [NEW] BandMap - CTRL + Wheel zooming - [NEW] BandMap - Zooming via buttons keeps a focus on centre freq - [NEW] BandMap - DX Spot's Comment as a tooltip - [CHANGED] BandMap - UI Layout - Fixed MacOS builds (PR #102) (thx gerbert) - Fixed templates under MacOS (PR #101) (thx gerbert) - Fixed WindowsOS Installer - Unable to upgrade version * Fri Jul 15 2022 Ladislav Foldyna - 0.12.0-1 - [NEW] Statistics - Show ODX on the map - [EXPERIMENTAL] Support for QT Styles (issue #88) - [CHANGED] - Removed F2 as a shortcut for QSO field editing - Next fixing of a high CPU load when DXC is processed (issue #52) - Fixed QSO fields from prev QSOs when Prefix - Callsign - Suffix (issue #90) - Fixed Chaotic QSO start time (issue #93) - Offline maps - Lighter colors, night sky removed, Sun position removed (issue #97) - Fixed incorrect A-Index colort (issue #98) - Fixed Stats Widget - percents - does not reflect date range (issue #99) - Fixed potential LogParam Cache issue - Import/Export polishing * Sun Jun 26 2022 Ladislav Foldyna - 0.11.0-1 - [NEW] QSO Detail/Edit Dialog - [NEW] Added mW power Support - [NEW] Implemented ADIF 3.1.3 - [NEW] Rigwidget saves last used freq for bands - Fixed Rig Combo size when Rig Profile name is long (issue #31) - Fixed CQZ, ITUZ do not validate whether their entered value is a number (issue #75) - Fixed vucc, myvucc must be uppercase - Edit mode (issue #76) - Fixed Greyline-Map is very dark (issue #78) - Fixed DX Country is not saved properly when name is between S-Z (issue #79) - Fixed Bandmap call selection - only left mouse button (issue #82) - Fixed My Notes Copy & Paste - Rich Text (issue #83) - Fixed Font appearance in the context menu (issue #84) * Sun Jun 5 2022 Ladislav Foldyna - 0.10.0-1 - [NEW] Bandmap shows XIT/RIT Freq - [NEW] Bandmap RX Mark Center (issue #69) - [NEW] Getting PTT State from RIG - only for CAT-controlled rigs - [NEW] PTT Shortchut - only for CAT-controlled rigs - Fixed Lost internet conneciton is not detected properly (issue #56) - Fixed Cannot manually edit QSO Date&Time (issue #66) - Fixed Field contents in capital letters (issue #67) - Fixed Band RX is not updated when RX Freq is edited (issue #72) - Fixed Stat Windget does not handle a date range correctly (issue #73) - Fixed eQSL card is incorreclty handled when a callsign contains special characters (issue #74) * Fri May 20 2022 Ladislav Foldyna - 0.9.0-1 - [NEW] User-defined Spot Alerts - [NEW] User filter contains a new operator "Starts with" - [NEW] a real local time is shown for the DX callsign (issue #45) - [NEW] Lotw/eQSL registration info is showed from callbooks - [NEW] Added shortcuts for menu and tabs - [NEW] Bandmap - Switching a band view via Bandmap context menu (issue #57) - [CHANGED] - Network Notification format - Fixed issue with My Notes multiple lines edit/show mode (issue #39) - Fixed issue when GUI froze when Rig disconnect was called (issue #50) - Partially fixed a high CPU load when DXC is processed (issue #52) - Fixed crashes under Debian "bullseye" - 32bit (issue #55) - Fixed Bandmap Callsign selection margin (issue #61) - Fixed issue when it was not possible to enter RX/TX freq manually * Fri Apr 22 2022 Ladislav Foldyna - 0.8.0-1 - RIT/XIT offset enable/disable detection (issue #26) - Fixed Rig Setting, Data Bits (issue #28) - Added default PWR for Rig profile (issue #30) - Fixed issue when GUI freezes during Rig connection (issue #32 & #33) - Fixed issue with an incorrect value of A-Index (issue #34) - Fixed ADI Import - incorrect _INTL fields import (issue #35) - Fixed isuue with an editing of bands in Setting dialog (#issue 36) - Fixed issue with hamlib when get_pwr crashes for a network rig (issue #37) - Improved new QSO fields are filled from prev QSO (issue #40) - Added mode for a network Rig (issue #41) - Fixed warning - processing a new request but the previous one hasn't been completed (issue #42) - Fixed Info widget when Country name is long (issue #43) - Reordered column visibility Tabs (issue #46) - Improved Rig tunning when XIT/RIT is enabled (issue #47) * Fri Apr 8 2022 Ladislav Foldyna - 0.7.0-1 - [NEW] Ant/Rig/Rot Profiles - [NEW] Rig widget shows additional information - [NEW] Rig widget Band/Mode/Profile Changer - [NEW] Rot profile Changer - [NEW] AZ/EL are stored when Rot is connected - Fixed an issue with Statistic widget (issue #25) - Fixed Rot AZ current value (issue #22) * Thu Mar 10 2022 Ladislav Foldyna - 0.6.5-1 - Fixed missing modes in Setting Dialog (issue #11) - Fixed Station Profile text color in dark mode (issue #10) - Fixed DXCluster Server Combo (issue #12) - Fixed TAB focus on QSO Fields (issue #14) * Sun Mar 6 2022 Ladislav Foldyna - 0.6.0-1 - [NEW] QSL - added import a file with QSL - QSLr column - Fixed QLog start when Band is 3cm (too long start time due to the Bandmap drawing) (issue #6) - Fixed Rotator Widget Warning - map transformation issue (issue #8) - Changed Bandmap window narrow size (issue #3) - Changed User Filter Widget size - Removed Units from Logbook widget - Removed UTC string - Renamed RSTs, RSTr etc. (issue #4) - Renamed Main Menu Services->Service and Station->Equipment - Internal - reworked Service networking signal handling * Sat Feb 19 2022 Ladislav Foldyna - 0.5.0-1 - DB: Started to use *_INTL fields - DB: Added all ADIF-supported modes/submodes - GUI: Dark Mode - GUI: TIme format controlled by Locale - Import/Export: ADI do not export UTF-8 characters and *_INTL fields - Import/Export: ADX exports UTF-8 characters and *_INTL fields - Import/Export: Added Import of ADX file format - Logbook: Shows QSO summary as a Callsign's tooltip - Logbook: QSO time is shown with seconds; added timezone - New QSO: Added My notes - free text for your personal notes - Backup: Change backup format form ADI to ADX (ADX supports UTF-8) - Settings: WSJTX Port is changable * Sun Jan 9 2022 Ladislav Foldyna - 0.4.0-1 - Stats: Added Show on Map - QSOs and Worked&Confirmed Grids - Stats: Stats are refreshed after every QSO - WSJTX: Remove TRX/Monitoring Status - Added Split mode - RX/TX RIG Offset - Added export of selected QSOs - Fixed FLdigi interface - CPPChecks & Clazy cleanup * Sun Dec 19 2021 Ladislav Foldyna - 0.3.0-1 - Rework Station Profile - stored in DB, new fields - Added VUCC fields support - Added BandMap marks (CTRL+M) - Clublog is uploaded the same way as EQSL and LOTW (modified QSO are resent) - Clublog real-time upload is temporary disabled - Added QRZ suppor - upload QSO and Callsign query - Callbook cooperation - Primary&Secondary - Secondary used when Primary did not find * Sat Nov 27 2021 Ladislav Foldyna - 0.2.0-1 - Initial version of the package based on v0.2.0 ================================================ FILE: service/GenericCallbook.cpp ================================================ #include #include "GenericCallbook.h" #include "core/LogParam.h" const QString GenericCallbook::SECURE_STORAGE_KEY = "Callbook"; const QString GenericCallbook::CALLBOOK_NAME = "none"; GenericCallbook::GenericCallbook(QObject *parent) : QObject(parent) { nam = new QNetworkAccessManager(this); connect(nam, &QNetworkAccessManager::finished, this, &GenericCallbook::onNetworkReply); } const QString GenericCallbook::getWebLookupURL(const QString &callsign, const QString &URL, bool replaceMacro ) { QString url = ( URL.isEmpty() ) ? LogParam::getCallbookWebLookupURL("https://www.qrz.com/lookup/") : URL; if ( replaceMacro ) url.replace("", callsign); return url; } QString GenericCallbook::decodeHtmlEntities(const QString &text) { QTextDocument doc; doc.setHtml(text); return doc.toRawText().trimmed(); } void GenericCallbook::onNetworkReply(QNetworkReply *reply) { processReply(reply); } ================================================ FILE: service/GenericCallbook.h ================================================ #ifndef QLOG_SERVICE_GENERICCALLBOOK_H #define QLOG_SERVICE_GENERICCALLBOOK_H #include #include struct CallbookResponseData { QString call; QString nick; QString qth; QString gridsquare; QString qsl_via; QString cqz; QString ituz; QString dok; QString iota; QString email; QString dxcc; QString name_fmt; QString fname; QString lname; QString addr1; QString us_state; QString zipcode; QString country; QString latitude; QString longitude; QString county; QString lic_year; QString utc_offset; QString eqsl; QString pqsl; QString born; QString lotw; QString url; QString image_url; }; Q_DECLARE_METATYPE(CallbookResponseData) class GenericCallbook : public QObject { Q_OBJECT public: explicit GenericCallbook(QObject *parent = nullptr); virtual ~GenericCallbook() {nam->deleteLater();}; const static QString SECURE_STORAGE_KEY; const static QString CALLBOOK_NAME; static const QString getWebLookupURL(const QString &callsign, const QString &URL = QString(), bool replaceMacro = true); virtual QString getDisplayName() = 0; protected: QNetworkAccessManager* getNetworkAccessManager() {return nam;}; virtual void processReply(QNetworkReply *reply) = 0; QString decodeHtmlEntities(const QString &text); signals: void callsignResult(CallbookResponseData); void lookupError(const QString); void loginFailed(); void callsignNotFound(QString); public slots: virtual void queryCallsign(const QString &callsign) = 0; virtual void abortQuery() = 0; private: QNetworkAccessManager* nam; private slots: void onNetworkReply(QNetworkReply *reply); }; #endif // QLOG_SERVICE_GENERICCALLBOOK_H ================================================ FILE: service/GenericQSLDownloader.cpp ================================================ #include "GenericQSLDownloader.h" #include GenericQSLDownloader::GenericQSLDownloader(QObject *parent) : QObject{parent} { nam = new QNetworkAccessManager(this); connect(nam, &QNetworkAccessManager::finished, this, &GenericQSLDownloader::onNetworkReply); } void GenericQSLDownloader::onNetworkReply(QNetworkReply *reply) { processReply(reply); } ================================================ FILE: service/GenericQSLDownloader.h ================================================ #ifndef GENERICQSLDOWNLOADER_H #define GENERICQSLDOWNLOADER_H #include #include #include class GenericQSLDownloader : public QObject { Q_OBJECT public: explicit GenericQSLDownloader(QObject *parent = nullptr); virtual ~GenericQSLDownloader() {nam->deleteLater();}; virtual void receiveQSL(const QDate&, bool, const QString&) = 0; signals: void receiveQSLProgress(qulonglong value); void receiveQSLStarted(); void receiveQSLComplete(QSLMergeStat); void receiveQSLFailed(QString); public slots: virtual void abortDownload() = 0; QNetworkAccessManager* getNetworkAccessManager() {return nam;}; protected: virtual void processReply(QNetworkReply *reply) = 0; private slots: void onNetworkReply(QNetworkReply *reply); private: QNetworkAccessManager* nam; }; #endif // GENERICQSLDOWNLOADER_H ================================================ FILE: service/GenericQSOUploader.cpp ================================================ #include "GenericQSOUploader.h" #include #include #include "logformat/AdiFormat.h" GenericQSOUploader::GenericQSOUploader(const QStringList &uploadedFields, QObject *parent) : QObject{parent}, uploadedFields(uploadedFields) { nam = new QNetworkAccessManager(this); connect(nam, &QNetworkAccessManager::finished, this, &GenericQSOUploader::onNetworkReply); } const QByteArray GenericQSOUploader::generateADIF(const QList &qsos, QMap *applTags) { QByteArray data; QTextStream stream(&data, QIODevice::ReadWrite); AdiFormat adi(stream); adi.exportStart(); for ( const QSqlRecord &qso : qsos ) adi.exportContact(stripRecord(qso), applTags); adi.exportEnd(); stream.flush(); return data; } const QSqlRecord GenericQSOUploader::stripRecord(const QSqlRecord &inRecord) { if ( uploadedFields.isEmpty() ) return inRecord; QSqlRecord ret; for ( int i = 0; i < inRecord.count(); i++ ) { QSqlField curr = inRecord.field(i); if ( uploadedFields.contains(curr.name()) ) ret.append(curr); } return ret; } void GenericQSOUploader::onNetworkReply(QNetworkReply *reply) { processReply(reply); } ================================================ FILE: service/GenericQSOUploader.h ================================================ #ifndef QLOG_SERVICE_GENERICQSOUPLOADER_H #define QLOG_SERVICE_GENERICQSOUPLOADER_H #include #include #include class GenericQSOUploader : public QObject { Q_OBJECT public: explicit GenericQSOUploader(const QStringList &uploadedFields, QObject *parent = nullptr); virtual ~GenericQSOUploader() {nam->deleteLater();}; virtual void uploadQSOList(const QList& qsos, const QVariantMap &addlParams) = 0; signals: void uploadFinished(); void uploadError(QString); void uploadedQSO(qulonglong); public slots: virtual void abortRequest() = 0; protected: virtual const QByteArray generateADIF(const QList &qsos, QMap *applTags = nullptr); virtual const QSqlRecord stripRecord(const QSqlRecord &inRecord); virtual void processReply(QNetworkReply *reply) = 0; QNetworkAccessManager* getNetworkAccessManager() {return nam;}; private: QNetworkAccessManager* nam; QStringList uploadedFields; private slots: void onNetworkReply(QNetworkReply *reply); }; #endif // QLOG_SERVICE_GENERICQSOUPLOADER_H ================================================ FILE: service/cloudlog/Cloudlog.cpp ================================================ #include #include #include #include #include #include #include #include "Cloudlog.h" #include "core/debug.h" #include "core/LogParam.h" #include "logformat/AdiFormat.h" MODULE_IDENTIFICATION("qlog.core.cloudlog"); const QString CloudlogBase::SECURE_STORAGE_API_KEY = "Cloudlog"; const QString CloudlogBase::CONFIG_USERNAME_API_CONST = "logbookapi"; REGISTRATION_SECURE_SERVICE(CloudlogBase); QString CloudlogBase::getLogbookAPIKey() { FCT_IDENTIFICATION; return getPassword(SECURE_STORAGE_API_KEY, getUsername()); } void CloudlogBase::saveLogbookAPIKey(const QString &newKey) { FCT_IDENTIFICATION; deletePassword(CloudlogBase::SECURE_STORAGE_API_KEY, getUsername()); if ( newKey.isEmpty() ) return; savePassword(CloudlogBase::SECURE_STORAGE_API_KEY, getUsername(), newKey); } QString CloudlogBase::getAPIEndpoint() { FCT_IDENTIFICATION; return LogParam::getCloudlogAPIEndpoint(); } void CloudlogBase::setAPIEndpoint(const QString &endpoint) { FCT_IDENTIFICATION; LogParam::setCloudlogAPIEndpoint(endpoint); } void CloudlogBase::registerCredentials() { // both storage keys belong to the same logical service CredentialRegistry::instance().add(SECURE_STORAGE_API_KEY, []() { return QList { { SECURE_STORAGE_API_KEY, [](){ return getUsername(); } } }; }); } CloudlogUploader::CloudlogUploader(QObject *parent) : GenericQSOUploader(QStringList(), parent), CloudlogBase(), currentReply(nullptr), cancelUpload(false), stationID(0) { FCT_IDENTIFICATION; } CloudlogUploader::~CloudlogUploader() { FCT_IDENTIFICATION; if ( currentReply ) { currentReply->abort(); currentReply->deleteLater(); } } QVariantMap CloudlogUploader::generateUploadConfigMap(uint stationID) { FCT_IDENTIFICATION; return QVariantMap({{"stationID", stationID}}); } void CloudlogUploader::abortRequest() { FCT_IDENTIFICATION; cancelUpload = true; if ( currentReply ) { currentReply->abort(); currentReply = nullptr; } } void CloudlogUploader::sendStationInfoReq() { FCT_IDENTIFICATION; QUrl url(LogParam::getCloudlogAPIEndpoint() + "/api/station_info/" + getLogbookAPIKey()); QNetworkRequest request(url); //qCDebug(runtime) << url; QNetworkReply *reply = getNetworkAccessManager()->get(request); // do not use currentReply reply->setProperty("messageType", QVariant("getStationID")); } void CloudlogUploader::uploadContact(const QSqlRecord &record, uint stationID) { FCT_IDENTIFICATION; const QByteArray &data = generateADIF({record}); cancelUpload = false; uploadAdif(data, stationID); currentReply->setProperty("contactID", record.value("id")); } void CloudlogUploader::uploadAdif(const QByteArray &data, uint stationID) { FCT_IDENTIFICATION; QUrl url(LogParam::getCloudlogAPIEndpoint() + "/api/qso"); QNetworkRequest request(url); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); request.setRawHeader("Accept", "application/json"); QJsonObject json; json["key"] = getLogbookAPIKey(); json["station_profile_id"] = (qint64)stationID; json["type"] = "adif"; json["string"] = QString::fromUtf8(data); QJsonDocument doc(json); qCDebug(runtime) << "Request Json:" << doc.toJson(); currentReply = getNetworkAccessManager()->post(request, doc.toJson()); currentReply->setProperty("messageType", QVariant("uploadADIF")); } void CloudlogUploader::uploadQSOList(const QList &qsos, const QVariantMap &addlParams) { FCT_IDENTIFICATION; if ( qsos.isEmpty() ) { /* Nothing to do */ emit uploadFinished(); return; } /* Even though Cloudlog can upload batches, we will upload QSOs one by one. This has several advantages * - we don't have to verify the upload size limit for PHP * - we don't have to search for errors for individual QSOs * - we correctly display the upload progress dialog. */ stationID = addlParams["stationID"].toUInt(); cancelUpload = false; queuedContacts4Upload = qsos; uploadContact(queuedContacts4Upload.first(), stationID); queuedContacts4Upload.removeFirst(); } const QMap &CloudlogUploader::getAvailableStationIDs() const { return availableStationIDs; } void CloudlogUploader::processReply(QNetworkReply *reply) { FCT_IDENTIFICATION; /* always process one requests per class */ currentReply = nullptr; int replyStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); const QString &messageType = reply->property("messageType").toString(); const QByteArray &response = reply->readAll(); qCDebug(runtime) << "Received Message Type: " << messageType << "HTTP Code" << replyStatusCode << "Cloudlog URL" << reply->request().url().toString(); qCDebug(runtime) << "Response:" << response; /*************/ /* uploadQSO */ /*************/ if ( messageType == "uploadADIF" ) { switch (replyStatusCode) { case 201: // Created case 400: // Bad Request (e.g., duplicate) { const QVariantMap &resp = parseResponse(response); const QString &status = resp.value("status").toString(); const QStringList &messages = resp.value("messages").toStringList(); QString reason = messages.isEmpty() ? QString() : messages.first(); reason = (reason.isEmpty() && messages.size() >= 2) ? messages.at(1) : reason; /* The idea behind this was that QLog would submit all new and modified QSOs * to Wavelog even though Wavelog rejects duplicates. The goal was that once * Wavelog starts accepting QSO updates, QLog could remain unchanged. Unfortunately, * the issue is that Wavelog reports duplicates as errors, and worse, the message * text is translated into multiple languages. As a result, QLog cannot reliably * distinguish an actual error from a duplicate. Therefore, I am removing this check, * and it is necessary to ensure that QLog sends only new QSOs to Cloudlog/Wavelog. */ bool success = (status == "created"); //|| (status == "abort" && reason.contains("Duplicate for")); if (success) { emit uploadedQSO(reply->property("contactID").toULongLong()); if (queuedContacts4Upload.isEmpty()) { cancelUpload = false; emit uploadFinished(); } else if (!cancelUpload) { uploadContact(queuedContacts4Upload.first(), stationID); queuedContacts4Upload.removeFirst(); } } else { cancelUpload = false; queuedContacts4Upload.clear(); emit uploadError(reason.isEmpty() ? reply->errorString() : reason); } break; } case 401: // Unauthorized cancelUpload = false; queuedContacts4Upload.clear(); emit uploadError(tr("Invalid API Key")); break; default: qCWarning(runtime) << "Unexpected HTTP status code:" << replyStatusCode; cancelUpload = false; queuedContacts4Upload.clear(); emit uploadError(reply->errorString()); break; } } /*************/ /* uploadQSO */ /*************/ else if ( messageType == "getStationID" ) { if ( replyStatusCode == 200 ) { QJsonDocument doc = QJsonDocument::fromJson(response); if (doc.isArray()) { const QJsonArray &array = doc.array(); for ( const QJsonValue &value : array ) if (value.isObject()) { StationProfile profile = StationProfile::fromJson(value.toObject()); availableStationIDs.insert(profile.station_id, profile); } } emit stationIDsUpdated(); } } reply->deleteLater(); } const QByteArray CloudlogUploader::generateADIF(const QList &qsos, QMap *applTags) { FCT_IDENTIFICATION; QByteArray data; QTextStream stream(&data, QIODevice::ReadWrite); AdiFormat adi(stream); //adi.exportStart(); // don't add header for ( const QSqlRecord &qso : qsos ) adi.exportContact(stripRecord(qso), applTags); //adi.exportEnd(); // don't add footer stream.flush(); return data; } QVariantMap CloudlogUploader::parseResponse(const QByteArray &data) { FCT_IDENTIFICATION; QJsonParseError error; QJsonDocument doc = QJsonDocument::fromJson(data, &error); if ( error.error != QJsonParseError::NoError || !doc.isObject()) { qCWarning(runtime) << "JSON parse error:" << error.errorString(); return {}; } QJsonObject obj = doc.object(); return obj.toVariantMap(); } CloudlogUploader::StationProfile CloudlogUploader::StationProfile::fromJson(const QJsonObject &obj) { FCT_IDENTIFICATION; StationProfile profile; profile.station_id = obj.value("station_id").toString().toInt(); profile.station_profile_name = obj.value("station_profile_name").toString(); profile.station_gridsquare = obj.value("station_gridsquare").toString(); profile.station_callsign = obj.value("station_callsign").toString(); profile.station_active = obj.contains("station_active") && obj.value("station_active").toString() == "1"; return profile; } ================================================ FILE: service/cloudlog/Cloudlog.h ================================================ #ifndef QLOG_SERVICE_CLOUDLOG_CLOUDLOG_H #define QLOG_SERVICE_CLOUDLOG_CLOUDLOG_H #include #include #include "service/GenericQSOUploader.h" #include "core/CredentialStore.h" class QNetworkReply; class QNetworkAccessManager; class CloudlogBase : public SecureServiceBase { protected: const static QString SECURE_STORAGE_API_KEY; const static QString CONFIG_USERNAME_API_CONST; public: explicit CloudlogBase() {}; virtual ~CloudlogBase() {}; DECLARE_SECURE_SERVICE(CloudlogBase); static QString getUsername() {return CONFIG_USERNAME_API_CONST;} static QString getLogbookAPIKey(); static void saveLogbookAPIKey(const QString& newKey); static QString getAPIEndpoint(); static void setAPIEndpoint(const QString &endpoint); }; class CloudlogUploader : public GenericQSOUploader, private CloudlogBase { Q_OBJECT public: class StationProfile { public: int station_id; QString station_profile_name; QString station_gridsquare; QString station_callsign; bool station_active = false; static StationProfile fromJson(const QJsonObject &obj); }; explicit CloudlogUploader(QObject *parent = nullptr); virtual ~CloudlogUploader(); static QVariantMap generateUploadConfigMap(uint stationID); void uploadAdif(const QByteArray &data, uint stationID); virtual void uploadQSOList(const QList& qsos, const QVariantMap &addlParams) override; const QMap& getAvailableStationIDs() const; void sendStationInfoReq(); public slots: virtual void abortRequest() override; void uploadContact(const QSqlRecord &record, uint stationID); signals: void stationIDsUpdated(); protected: virtual void processReply(QNetworkReply* reply) override; virtual const QByteArray generateADIF(const QList &qsos, QMap *applTags = nullptr) override; private: QMap availableStationIDs; QNetworkReply *currentReply; QList queuedContacts4Upload; bool cancelUpload; uint stationID; QVariantMap parseResponse(const QByteArray &data); }; #endif // QLOG_SERVICE_CLOUDLOG_CLOUDLOG_H ================================================ FILE: service/clublog/ClubLog.cpp ================================================ #include #include #include #include #include #include #include #include #include #include "ClubLog.h" #include "core/debug.h" #include "core/CredentialStore.h" #include "core/LogParam.h" #include "data/Data.h" MODULE_IDENTIFICATION("qlog.core.clublog"); const QString ClubLogBase::SECURE_STORAGE_KEY = "Clublog"; const QString ClubLogBase::API_KEY = "7a45c2b20f932ca8908b975a60f0a78a7602f65a"; REGISTRATION_SECURE_SERVICE(ClubLogBase); // https://clublog.freshdesk.com/support/solutions/articles/53202-which-adif-fields-does-club-log-use- QStringList ClubLogUploader::uploadedFields = { "start_time", "qsl_rdate", "qsl_sdate", "callsign", "operator", "mode", "band", "band_rx", "freq", "qsl_rcvd", "lotw_qsl_rcvd", "qsl_sent", "dxcc", "prop_mode", "credit_granted", "rst_sent", "rst_rcvd", "notes", "gridsquare", "vucc_grids", "sat_name" }; const QString ClubLogBase::getEmail() { FCT_IDENTIFICATION; return LogParam::getClublogLogbookReqEmail(); } bool ClubLogBase::isUploadImmediatelyEnabled() { FCT_IDENTIFICATION; return LogParam::getClublogUploadImmediatelyEnabled(); } const QString ClubLogBase::getPasswd() { FCT_IDENTIFICATION; return getPassword(ClubLogBase::SECURE_STORAGE_KEY, getEmail()); } void ClubLogBase::saveUsernamePassword(const QString &newEmail, const QString &newPassword) { FCT_IDENTIFICATION; const QString &oldEmail = getEmail(); if ( oldEmail != newEmail ) { deletePassword(ClubLogBase::SECURE_STORAGE_KEY, oldEmail); } LogParam::setClublogLogbookReqEmail(newEmail); savePassword(ClubLogBase::SECURE_STORAGE_KEY, newEmail, newPassword); } void ClubLogBase::saveUploadImmediatelyConfig(bool value) { FCT_IDENTIFICATION; LogParam::setClublogUploadImmediatelyEnabled(value); } void ClubLogBase::registerCredentials() { // both storage keys belong to the same logical service CredentialRegistry::instance().add(SECURE_STORAGE_KEY, []() { return QList { { SECURE_STORAGE_KEY, [](){ return getEmail(); } } }; }); } ClubLogUploader::ClubLogUploader(QObject *parent) : GenericQSOUploader(uploadedFields, parent), ClubLogBase(), rx("[a-zA-Z]") { FCT_IDENTIFICATION; if ( !query_updateRT.prepare("UPDATE contacts " "SET clublog_qso_upload_status='Y', clublog_qso_upload_date = strftime('%Y-%m-%d',DATETIME('now', 'utc')) " "WHERE id = :id AND callsign = :callsign") ) qCWarning(runtime) << "Update statement is not prepared"; } ClubLogUploader::~ClubLogUploader() { FCT_IDENTIFICATION; if ( activeReplies.count() > 0 ) { QMutableListIterator i(activeReplies); while ( i.hasNext() ) { QNetworkReply* curr = i.next(); curr->abort(); curr->deleteLater(); i.remove(); } } } void ClubLogUploader::sendRealtimeRequest(const OnlineUploadCommand command, const QSqlRecord &record, const QString &uploadCallsign) { FCT_IDENTIFICATION; qCDebug(function_parameters) << command << uploadCallsign; if ( !isUploadImmediatelyEnabled() ) { qCDebug(runtime) << "Instant Upload is disabled, no action"; return; } const QString &email = getEmail(); const QString &password = getPasswd(); if ( email.isEmpty() || uploadCallsign.isEmpty() || password.isEmpty() ) return; QUrl url; QUrlQuery query; query.addQueryItem("email", email.toUtf8().toPercentEncoding()); query.addQueryItem("callsign", uploadCallsign.toUtf8().toPercentEncoding()); query.addQueryItem("password", password.toUtf8().toPercentEncoding()); query.addQueryItem("api", API_KEY); switch (command) { case ClubLogUploader::INSERT_QSO: { url.setUrl(API_LIVE_UPLOAD_URL); QByteArray data = generateADIF({record}); data.replace("\n", " "); query.addQueryItem("adif", data.trimmed().toPercentEncoding()); } break; case ClubLogUploader::UPDATE_QSO: case ClubLogUploader::DELETE_QSO: url.setUrl(API_LIVE_DELETE_URL); query.addQueryItem("dxcall", record.value("callsign").toByteArray()); query.addQueryItem("datetime", record.value("start_time").toDateTime().toTimeZone(QTimeZone::utc()).toString("yyyy-MM-dd hh:mm:ss").toUtf8()); query.addQueryItem("bandid", record.value("band").toString().replace(rx, "").toUtf8()); //clublog support non-ADIF bands enumaration, need remove m, cm, mm string break; default: qCWarning(runtime) << "Unsupported RT Command" << command; return; } qCDebug(runtime) << Data::safeQueryString(query); QNetworkRequest request(url); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); QNetworkReply *currentReply = getNetworkAccessManager()->post(request, query.query(QUrl::FullyEncoded).toUtf8()); QVariant messageType; switch ( command ) { case ClubLogUploader::INSERT_QSO: messageType = "realtimeInsert"; currentReply->setProperty("dxcall", record.value("callsign")); break; case ClubLogUploader::UPDATE_QSO: messageType = "realtimeUpdate"; RTupdatesInProgress.insert(record.value("id").toULongLong(), record); break; case ClubLogUploader::DELETE_QSO: messageType = "realtimeDelete"; break; } currentReply->setProperty("contactID", record.value("id")); currentReply->setProperty("messageType", messageType); currentReply->setProperty("uploadCallsign", uploadCallsign); activeReplies << currentReply; } void ClubLogUploader::uploadQSOList(const QList &qsos, const QVariantMap &addlParams) { FCT_IDENTIFICATION; uploadAdif(generateADIF(qsos), addlParams["uploadCallsign"].toString(), addlParams["clearFlag"].toBool()); } void ClubLogUploader::uploadAdif(const QByteArray& data, const QString &uploadCallsign, bool clearFlag) { FCT_IDENTIFICATION; qCDebug(function_parameters) << data; const QString &email = getEmail(); const QString &password = getPasswd(); if ( email.isEmpty() || uploadCallsign.isEmpty() || password.isEmpty() ) return; QUrl url(API_LOG_UPLOAD_URL); QHttpMultiPart* multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType); QHttpPart emailPart; emailPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"email\"")); emailPart.setBody(email.toUtf8()); QHttpPart callsignPart; callsignPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"callsign\"")); callsignPart.setBody(uploadCallsign.toUtf8()); QHttpPart passwordPart; passwordPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"password\"")); passwordPart.setBody(password.toUtf8()); QHttpPart clearPart; clearPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"clear\"")); clearPart.setBody( (clearFlag) ? "1" : "0"); QHttpPart apiPart; apiPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"api\"")); apiPart.setBody(API_KEY.toUtf8()); QHttpPart filePart; filePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream")); filePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"file\"; filename=\"clublog.adi\"")); filePart.setBody(data); multipart->append(emailPart); multipart->append(passwordPart); multipart->append(callsignPart); multipart->append(clearPart); multipart->append(apiPart); multipart->append(filePart); QNetworkRequest request(url); if ( activeReplies.count() > 0 ) qCWarning(runtime) << "processing a new request but the previous one hasn't been completed yet !!!"; QNetworkReply * currentReply = getNetworkAccessManager()->post(request, multipart); currentReply->setProperty("messageType", QVariant("uploadADIFFile")); currentReply->setProperty("uploadCallsign", uploadCallsign); multipart->setParent(currentReply); activeReplies << currentReply; } void ClubLogUploader::processReply(QNetworkReply* reply) { FCT_IDENTIFICATION; activeReplies.removeAll(reply); int replyStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); if ( reply->error() != QNetworkReply::NoError || replyStatusCode < 200 || replyStatusCode >= 300) { qCDebug(runtime) << "Clublog error URL " << reply->request().url().toString(); qCDebug(runtime) << "Clublog error" << reply->errorString(); qCDebug(runtime) << "HTTP Status Code" << replyStatusCode; if ( reply->error() != QNetworkReply::OperationCanceledError ) { emit uploadError(tr("Clublog Operation for Callsign %1 failed.
%2").arg(reply->property("uploadCallsign").toString(), reply->errorString())); reply->deleteLater(); } return; } const QString &messageType = reply->property("messageType").toString(); qCDebug(runtime) << "Received Message Type: " << messageType; /******************/ /* uploadADIFFile */ /******************/ if ( messageType == "uploadADIFFile" ) { emit uploadFinished(); } /******************/ /* realtimeInsert */ /******************/ else if ( messageType == "realtimeInsert" ) { query_updateRT.bindValue(":id", reply->property("contactID")); query_updateRT.bindValue(":callsign", reply->property("dxcall")); //to be sure that the QSO with the ID is still the same sa before sending if ( !query_updateRT.exec() ) qCWarning(runtime) << "RT Response: SQL Error" << query_updateRT.lastError(); else emit uploadedQSO(reply->property("contactID").toULongLong()); } /******************/ /* realtimeUpdate */ /******************/ else if ( messageType == "realtimeUpdate") { QSqlRecord insertRecord = RTupdatesInProgress.take(reply->property("contactID").toULongLong()); if ( insertRecord != QSqlRecord() ) { sendRealtimeRequest(ClubLogUploader::INSERT_QSO, insertRecord, reply->property("uploadCallsign").toString()); } else qWarning() << "Cannot find record for update in Update In-Progress Table"; } /******************/ /* realtimeDelete */ /******************/ else if ( messageType == "realtimeDelete") { //delete response - no action } /*************/ /* Otherwise */ /*************/ else qWarning() << "Unrecognized Clublog response" << reply->property("messageType").toString(); reply->deleteLater(); } void ClubLogUploader::abortRequest() { FCT_IDENTIFICATION; if ( activeReplies.count() > 0 ) { QMutableListIterator i(activeReplies); while ( i.hasNext() ) { QNetworkReply* curr = i.next(); curr->abort(); //curr->deleteLater(); // pointer is deleted later in processReply i.remove(); } } RTupdatesInProgress.clear(); } void ClubLogUploader::insertQSOImmediately(const QSqlRecord &record) { FCT_IDENTIFICATION; sendRealtimeRequest(ClubLogUploader::INSERT_QSO, record, generateUploadCallsign(record)); } void ClubLogUploader::updateQSOImmediately(const QSqlRecord &record) { FCT_IDENTIFICATION; const QString &uploadStatus = record.value("clublog_qso_upload_status").toString(); if ( uploadStatus.isEmpty() || uploadStatus == "N" ) { qCDebug(runtime) << "QSO would not be uploaded to Clublog - nothing to do"; return; } sendRealtimeRequest(ClubLogUploader::UPDATE_QSO, record, generateUploadCallsign(record)); } void ClubLogUploader::deleteQSOImmediately(const QSqlRecord &record) { FCT_IDENTIFICATION; const QString &uploadStatus = record.value("clublog_qso_upload_status").toString(); if ( uploadStatus.isEmpty() || uploadStatus == "N" ) { qCDebug(runtime) << "QSO would not be uploaded to Clublog - nothing to do"; return; } sendRealtimeRequest(ClubLogUploader::DELETE_QSO, record, generateUploadCallsign(record)); } const QString ClubLogUploader::generateUploadCallsign(const QSqlRecord &record) const { FCT_IDENTIFICATION; #if 0 //for cases when QSOs are uploaded to the Clublog log with QSO's station_callsign without the prefix Callsign uploadCallsign(record.value("station_callsign").toString()); if ( !uploadCallsign.isValid() ) qCWarning(runtime) << "Station callsign is not valid" << record.value("station_callsign").toString(); // QSOs are uploaded to the Clublog log with a name such // as QSO's station_callsign without the prefix return uploadCallsign.getHostPrefixWithDelimiter() + uploadCallsign.getBase(); #endif return record.value("station_callsign").toString(); } ================================================ FILE: service/clublog/ClubLog.h ================================================ #ifndef QLOG_SERVICE_CLUBLOG_CLUBLOG_H #define QLOG_SERVICE_CLUBLOG_CLUBLOG_H #include #include #include #include #include #include "service/GenericQSOUploader.h" #include "core/CredentialStore.h" class QNetworkReply; class QNetworkAccessManager; class ClubLogBase : public SecureServiceBase { protected: const static QString SECURE_STORAGE_KEY; const static QString API_KEY ; public: explicit ClubLogBase() {}; virtual ~ClubLogBase() {} ; DECLARE_SECURE_SERVICE(ClubLogBase); static const QString getEmail(); static bool isUploadImmediatelyEnabled(); static const QString getPasswd(); static void saveUsernamePassword(const QString &, const QString &); static void saveUploadImmediatelyConfig(bool value); static const QString getCTYUrl() {return QString("https://cdn.clublog.org/cty.php?api=%1").arg(API_KEY);}; }; class ClubLogUploader : public GenericQSOUploader, private ClubLogBase { Q_OBJECT public: static QStringList uploadedFields; static QVariantMap generateUploadConfigMap(const QString uploadCallsign, bool clearFlag) { return QVariantMap({{"uploadCallsign", uploadCallsign}, {"clearFlag", clearFlag}}); } enum OnlineUploadCommand { INSERT_QSO, UPDATE_QSO, DELETE_QSO }; explicit ClubLogUploader(QObject *parent = nullptr); virtual ~ClubLogUploader(); void uploadAdif(const QByteArray &data, const QString &uploadCallsign, bool clearFlag = false); void sendRealtimeRequest(const OnlineUploadCommand command, const QSqlRecord &record, const QString &uploadCallsign); virtual void uploadQSOList(const QList& qsos, const QVariantMap &addlParams) override; public slots: virtual void abortRequest() override; void insertQSOImmediately(const QSqlRecord &record); void updateQSOImmediately(const QSqlRecord &record); void deleteQSOImmediately(const QSqlRecord &record); protected: virtual void processReply(QNetworkReply *reply) override; private: const QString API_LIVE_UPLOAD_URL = "https://clublog.org/realtime.php"; const QString API_LIVE_DELETE_URL = "https://clublog.org/delete.php"; const QString API_LOG_UPLOAD_URL = "https://clublog.org/putlogs.php"; QList activeReplies; QSqlQuery query_updateRT; QHash RTupdatesInProgress; QRegularExpression rx; const QString generateUploadCallsign(const QSqlRecord &record) const; }; #endif // QLOG_SERVICE_CLUBLOG_CLUBLOG_H ================================================ FILE: service/eqsl/Eqsl.cpp ================================================ #include #include #include #include #include #include #include #include #include #include "Eqsl.h" #include "core/debug.h" #include "core/CredentialStore.h" #include "logformat/AdiFormat.h" #include "core/LogParam.h" #include "data/Data.h" MODULE_IDENTIFICATION("qlog.core.eqsl"); extern QTemporaryDir tempDir; /* http://www.eqsl.cc/qslcard/ADIFContentSpecs.cfm */ QStringList EQSLUploader::uploadedFields = { "start_time", "callsign", "mode", "freq", "band", "prop_mode", "rst_sent", "submode", "sat_mode", "sat_name", "my_cnty", "my_gridsquare", "qslmsg", "comment" }; const QString EQSLBase::SECURE_STORAGE_KEY = "eQSL"; REGISTRATION_SECURE_SERVICE(EQSLBase); const QString EQSLBase::getUsername() { FCT_IDENTIFICATION; return LogParam::getEQSLLogbookUsername(); } const QString EQSLBase::getPasswd() { FCT_IDENTIFICATION; return getPassword(EQSLBase::SECURE_STORAGE_KEY, getUsername()); } void EQSLBase::saveUsernamePassword(const QString &newUsername, const QString &newPassword) { FCT_IDENTIFICATION; const QString &oldUsername = getUsername(); if ( oldUsername != newUsername ) { deletePassword(EQSLBase::SECURE_STORAGE_KEY, oldUsername); } LogParam::setEQSLLogbookUsername(newUsername); savePassword(EQSLBase::SECURE_STORAGE_KEY, newUsername, newPassword); } void EQSLBase::registerCredentials() { // both storage keys belong to the same logical service CredentialRegistry::instance().add(SECURE_STORAGE_KEY, []() { return QList { { SECURE_STORAGE_KEY, [](){ return getUsername(); } } }; }); } EQSLUploader::EQSLUploader( QObject *parent ): GenericQSOUploader(uploadedFields, parent), currentReply(nullptr), commentAsQSLMSG(false), disableqslmsg(true) { FCT_IDENTIFICATION; } EQSLUploader::~EQSLUploader() { FCT_IDENTIFICATION; if ( currentReply ) { currentReply->abort(); currentReply->deleteLater(); } } void EQSLUploader::uploadAdif(const QByteArray &data) { FCT_IDENTIFICATION; const QString &username = getUsername(); const QString &password = getPassword(EQSLUploader::SECURE_STORAGE_KEY, username); /* http://www.eqsl.cc/qslcard/ImportADIF.txt */ QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType, this); /* UserName */ QHttpPart userPart; userPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"eqsl_user\"")); userPart.setBody(username.toUtf8()); /* Password */ QHttpPart passPart; passPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"eqsl_pswd\"")); passPart.setBody(password.toUtf8()); /* File */ QTemporaryFile file; file.open(); file.write(data); file.flush(); QHttpPart filePart; QString aux = QString("form-data; name=\"Filename\"; filename=\"%1\"").arg(file.fileName()); filePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream")); filePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant(aux)); filePart.setBody(data); multiPart->append(userPart); multiPart->append(passPart); multiPart->append(filePart); QUrl url(UPLOAD_ADIF_PAGE); QNetworkRequest request(url); if ( currentReply ) qCWarning(runtime) << "processing a new request but the previous one hasn't been completed yet !!!"; currentReply = getNetworkAccessManager()->post(request, multiPart); currentReply->setProperty("messageType", QVariant("uploadADIFFile")); } const QSqlRecord EQSLUploader::stripRecord(const QSqlRecord &record) { FCT_IDENTIFICATION; QSqlRecord newRecord(GenericQSOUploader::stripRecord(record)); if ( disableqslmsg ) newRecord.remove(newRecord.indexOf("qslmsg")); else if ( commentAsQSLMSG ) newRecord.setValue("qslmsg", record.value("comment")); int commentIndex = newRecord.indexOf("comment"); if (commentIndex != -1) newRecord.remove(commentIndex); return newRecord; } void EQSLUploader::uploadQSOList(const QList &qsos, const QVariantMap &addlParams) { FCT_IDENTIFICATION; QString qthProfile = addlParams["qthprofile"].toString(); commentAsQSLMSG = addlParams["commentasqslmsg"].toBool(); disableqslmsg = addlParams["disableqslmsg"].toBool(); QMap *applTags = nullptr; if ( !qthProfile.isEmpty() ) { applTags = new QMap; applTags->insert("app_eqsl_qth_nickname", qthProfile); } QByteArray data = generateADIF(qsos, applTags); if ( applTags ) delete applTags; uploadAdif(data); } void EQSLUploader::processReply(QNetworkReply* reply) { FCT_IDENTIFICATION; /* always process one requests per class */ currentReply = nullptr; int replyStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); if ( reply->error() != QNetworkReply::NoError || replyStatusCode < 200 || replyStatusCode >= 300) { qCDebug(runtime) << "eQSL error URL " << reply->request().url().toString(); qCDebug(runtime) << "eQSL error" << reply->errorString(); qCDebug(runtime) << "HTTP Status Code" << replyStatusCode; if ( reply->error() != QNetworkReply::OperationCanceledError ) { emit uploadError(reply->errorString()); reply->deleteLater(); } return; } const QString &messageType = reply->property("messageType").toString(); qCDebug(runtime) << "Received Message Type: " << messageType; /******************/ /* uploadADIFFile */ /******************/ if ( messageType == "uploadADIFFile" ) { const QString replyString(reply->readAll()); qCDebug(runtime) << replyString; static QRegularExpression rOK("Result: (.*)"); QRegularExpressionMatch matchOK = rOK.match(replyString); static QRegularExpression rError("Error: (.*)"); QRegularExpressionMatch matchError = rError.match(replyString); static QRegularExpression rWarning("Warning: (.*)"); QRegularExpressionMatch matchWarning = rWarning.match(replyString); static QRegularExpression rCaution("Caution: (.*)"); QRegularExpressionMatch matchCaution = rCaution.match(replyString); QString msg; if ( matchOK.hasMatch() ) { emit uploadFinished(); } else if (matchError.hasMatch() ) { msg = matchError.captured(1); emit uploadError(msg); } else if (matchWarning.hasMatch() ) { emit uploadFinished(); } else if (matchCaution.hasMatch() ) { msg = matchCaution.captured(1); emit uploadError(msg); } else { qCInfo(runtime) << "Unknown Reply "; qCInfo(runtime) << replyString; emit uploadError(tr("Unknown Reply from eQSL")); } } reply->deleteLater(); } void EQSLUploader::abortRequest() { FCT_IDENTIFICATION; if ( currentReply ) { currentReply->abort(); currentReply = nullptr; } } EQSLQSLDownloader::EQSLQSLDownloader(QObject *parent) : GenericQSLDownloader(parent), EQSLBase(), currentReply(nullptr), qslStorage(new QSLStorage(this)) { FCT_IDENTIFICATION; } EQSLQSLDownloader::~EQSLQSLDownloader() { FCT_IDENTIFICATION; if ( currentReply ) { currentReply->abort(); currentReply->deleteLater(); } qslStorage->deleteLater(); } void EQSLQSLDownloader::receiveQSL(const QDate &start_date, bool qso_since, const QString &qthNick) { FCT_IDENTIFICATION; qCDebug(function_parameters) << start_date << qso_since << qthNick; QList> params; if ( !qthNick.isEmpty() ) params.append(qMakePair(QString("QTHNickname"), qthNick)); if ( start_date.isValid() ) { if ( qso_since ) { const QString &start = start_date.toString("MM/dd/yyyy"); const QString &stop = QDate::currentDate().addDays(1).toString("MM/dd/yyyy"); params.append(qMakePair(QString("LimitDateLo"), start)); params.append(qMakePair(QString("LimitDateHi"), stop)); } else { //qsl_since const QString &start = start_date.toString("yyyyMMdd"); params.append(qMakePair(QString("RcvdSince"), start)); } } get(params); } void EQSLQSLDownloader::getQSLImage(const QSqlRecord &qso) { FCT_IDENTIFICATION; QString inCacheFilename; if ( isQSLImageInCache(qso, inCacheFilename) ) { emit QSLImageFound(inCacheFilename); return; } /* QSL image is not in Cache */ qCDebug(runtime) << "QSL is not in Cache"; const QString &username = getUsername(); const QString &password = getPasswd(); const QDateTime &time_start = qso.value("start_time").toDateTime().toTimeZone(QTimeZone::utc()); QUrlQuery query; query.addQueryItem("UserName", username.toUtf8().toPercentEncoding()); query.addQueryItem("Password", password.toUtf8().toPercentEncoding()); query.addQueryItem("CallsignFrom", qso.value("callsign").toString()); query.addQueryItem("QSOYear", time_start.toString("yyyy")); query.addQueryItem("QSOMonth", time_start.toString("MM")); query.addQueryItem("QSODay", time_start.toString("dd")); query.addQueryItem("QSOHour", time_start.toString("hh")); query.addQueryItem("QSOMinute", time_start.toString("mm")); query.addQueryItem("QSOBand", qso.value("band").toString()); query.addQueryItem("QSOMode", qso.value("mode").toString()); QUrl url(QSL_IMAGE_FILENAME_PAGE); url.setQuery(query); qCDebug(runtime) << Data::safeQueryString(query); if ( currentReply ) { qCWarning(runtime) << "processing a new request but the previous one hasn't been completed yet !!!"; } currentReply = getNetworkAccessManager()->get(QNetworkRequest(url)); currentReply->setProperty("messageType", QVariant("getQSLImageFileName")); currentReply->setProperty("onDiskFilename", QVariant(inCacheFilename)); currentReply->setProperty("QSORecordID", QVariant(qso.value("id"))); } void EQSLQSLDownloader::abortDownload() { FCT_IDENTIFICATION; if ( currentReply ) { currentReply->abort(); currentReply->deleteLater(); } } void EQSLQSLDownloader::processReply(QNetworkReply *reply) { FCT_IDENTIFICATION; /* always process one requests per class */ currentReply = nullptr; int replyStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); if ( reply->error() != QNetworkReply::NoError || replyStatusCode < 200 || replyStatusCode >= 300) { qCDebug(runtime) << "eQSL error URL " << reply->request().url().toString(); qCDebug(runtime) << "eQSL error" << reply->errorString(); qCDebug(runtime) << "HTTP Status Code" << replyStatusCode; if ( reply->error() != QNetworkReply::OperationCanceledError ) { emit receiveQSLFailed(reply->errorString()); emit QSLImageError(reply->errorString()); reply->deleteLater(); } return; } const QString &messageType = reply->property("messageType").toString(); qCDebug(runtime) << "Received Message Type: " << messageType; /*******************/ /* getADIFFileName */ /*******************/ if ( messageType == "getADIFFileName" ) { //getting the first page where a ADIF filename is present QString replyString(reply->readAll()); qCDebug(runtime) << replyString; if ( replyString.contains("No such Username/Password found") || replyString.contains("No such Callsign found") ) { qCDebug(runtime) << "Incorrect Password or QTHProfile Id"; emit receiveQSLFailed(tr("Incorrect Password or QTHProfile Id")); } else { static QRegularExpression re("/downloadedfiles/(.*.adi)\">.ADI file"); QRegularExpressionMatch match = re.match(replyString); if ( match.hasMatch() ) { const QString &filename = match.captured(1); downloadADIF(filename); } else if ( replyString.contains("You have no log entries")) { emit receiveQSLComplete(QSLMergeStat()); } else { qCInfo(runtime) << "File not found in HTTP reply "; emit receiveQSLFailed(tr("ADIF file not found in eQSL response")); } } } /***********************/ /* getQSLImageFileName */ /***********************/ else if ( messageType == "getQSLImageFileName" ) { //getting the first page where an Image filename is present QString replyString(reply->readAll()); qCDebug(runtime) << replyString; if ( replyString.contains("No such Username/Password found") ) emit QSLImageError(tr("Incorrect Username or password")); else { static QRegularExpression re("property("onDiskFilename").toString(); qulonglong qsoid = reply->property("QSORecordID").toULongLong(); downloadImage(filename, onDiskFilename, qsoid); } else { static QRegularExpression rError("Error: (.*)"); QRegularExpressionMatch matchError = rError.match(replyString); if (matchError.hasMatch() ) { QString msg = matchError.captured(1); emit QSLImageError(msg); } else { static QRegularExpression rWarning("Warning: (.*) --"); QRegularExpressionMatch matchWarning = rWarning.match(replyString); if ( matchWarning.hasMatch() ) { QString msg = matchWarning.captured(1); emit QSLImageError(msg); } else { qCInfo(runtime) << replyString; emit QSLImageError(tr("Unknown Error")); } } } } } /***********/ /* getADIF */ /***********/ else if ( messageType == "getADIF") { qint64 size = reply->size(); qCDebug(runtime) << "Reply size: " << size; /* Currently, QT returns an incorrect stream position value in Network stream * when the stream is used in QTextStream. Therefore * QLog downloads a response, saves it to a temp file and opens * the file as a stream */ QTemporaryFile tempFile; if ( ! tempFile.open() ) { qCDebug(runtime) << "Cannot open temp file"; emit receiveQSLFailed(tr("Cannot opet temporary file")); reply->deleteLater(); return; } const QByteArray &data = reply->readAll(); tempFile.write(data); tempFile.flush(); tempFile.seek(0); emit receiveQSLStarted(); /* see above why QLog uses a temp file */ QTextStream stream(&tempFile); AdiFormat adi(stream); connect(&adi, &AdiFormat::importPosition, this, [this, size](qint64 position) { if ( size > 0 ) { double progress = position * 100.0 / size; emit receiveQSLProgress(static_cast(progress)); } }); connect(&adi, &AdiFormat::QSLMergeFinished, this, [this](QSLMergeStat stats) { emit receiveQSLComplete(stats); }); adi.runQSLImport(adi.EQSL); tempFile.close(); } /********************/ /* downloadQSLImage */ /********************/ else if ( messageType == "downloadQSLImage") { qint64 size = reply->size(); qCDebug(runtime) << "Reply size: " << size; const QByteArray &data = reply->readAll(); const QString &onDiskFilename = reply->property("onDiskFilename").toString(); qulonglong qsoID = reply->property("QSORecordID").toULongLong(); QFile file(onDiskFilename); if ( !file.open(QIODevice::WriteOnly)) { emit QSLImageError(tr("Cannot save the image to file") + " " + onDiskFilename); return; } file.write(data); file.flush(); file.close(); if ( !qslStorage->add(QSLObject(qsoID, QSLObject::EQSL, QFileInfo(onDiskFilename).fileName(), data, QSLObject::RAWBYTES)) ) { qWarning() << "Cannot save the eQSL image to database cache"; // ??? database is only a cache for images. not needed to inform operator about this ???? } emit QSLImageFound(onDiskFilename); } reply->deleteLater(); } void EQSLQSLDownloader::get(const QList> ¶ms) { FCT_IDENTIFICATION; const QString &username = getUsername(); const QString &password = getPasswd(); QUrlQuery query; query.setQueryItems(params); query.addQueryItem("UserName", username.toUtf8().toPercentEncoding()); query.addQueryItem("Password", password.toUtf8().toPercentEncoding()); QUrl url(DOWNLOAD_1ST_PAGE); url.setQuery(query); qCDebug(runtime) << Data::safeQueryString(query); if ( currentReply ) qCWarning(runtime) << "processing a new request but the previous one hasn't been completed yet !!!"; currentReply = getNetworkAccessManager()->get(QNetworkRequest(url)); currentReply->setProperty("messageType", QVariant("getADIFFileName")); } void EQSLQSLDownloader::downloadADIF(const QString &filename) { FCT_IDENTIFICATION; qCDebug(function_parameters) << filename; QUrlQuery query; QUrl url(DOWNLOAD_2ND_PAGE + filename); url.setQuery(query); qCDebug(runtime) << url.toString(); if ( currentReply ) { qCWarning(runtime) << "processing a new request but the previous one hasn't been completed yet !!!"; } currentReply = getNetworkAccessManager()->get(QNetworkRequest(url)); currentReply->setProperty("messageType", QVariant("getADIF")); } void EQSLQSLDownloader::downloadImage(const QString &URLFilename, const QString &onDiskFilename, const qulonglong qsoid) { FCT_IDENTIFICATION; qCDebug(function_parameters) << URLFilename << onDiskFilename << qsoid; QUrlQuery query; QUrl url(QSL_IMAGE_DOWNLOAD_PAGE + URLFilename); url.setQuery(query); qCDebug(runtime) << url.toString(); currentReply = getNetworkAccessManager()->get(QNetworkRequest(url)); currentReply->setProperty("messageType", QVariant("downloadQSLImage")); currentReply->setProperty("onDiskFilename", QVariant(onDiskFilename)); currentReply->setProperty("QSORecordID", qsoid); } QString EQSLQSLDownloader::QSLImageFilename(const QSqlRecord &qso) { FCT_IDENTIFICATION; /* QSL Fileformat YYYYMMDD_ID_Call_eqsl.jpg */ const QDateTime &time_start = qso.value("start_time").toDateTime().toTimeZone(QTimeZone::utc()); const QString &ret = QString("%1_%2_%3_eqsl.jpg").arg(time_start.toString("yyyyMMdd"), qso.value("id").toString(), qso.value("callsign").toString().replace(QRegularExpression(QString::fromUtf8("[-`~!@#$%^&*()_—+=|:;<>«»,.?/{}\'\"]")),"_")); qCDebug(runtime) << "EQSL Image Filename: " << ret; return ret; } bool EQSLQSLDownloader::isQSLImageInCache(const QSqlRecord &qso, QString &fullPath) { FCT_IDENTIFICATION; bool isFileExists = false; const QString &expectingFilename = QSLImageFilename(qso); const QSLObject &eqsl = qslStorage->getQSL(qso, QSLObject::EQSL, expectingFilename); QFile f(tempDir.path() + QDir::separator() + eqsl.getQSLName()); qCDebug(runtime) << "Using temp file" << f.fileName(); fullPath = f.fileName(); if ( eqsl.getBLOB() != QByteArray() && f.open(QFile::WriteOnly) ) { f.write(eqsl.getBLOB()); f.flush(); f.close(); isFileExists = true; } qCDebug(runtime) << isFileExists << " " << fullPath; return isFileExists; } ================================================ FILE: service/eqsl/Eqsl.h ================================================ #ifndef QLOG_SERVICE_EQSL_EQSL_H #define QLOG_SERVICE_EQSL_EQSL_H #include #include #include "core/QSLStorage.h" #include "service/GenericQSOUploader.h" #include "service/GenericQSLDownloader.h" #include "core/CredentialStore.h" class QNetworkAccessManager; class QNetworkReply; class EQSLBase : public SecureServiceBase { protected: static const QString SECURE_STORAGE_KEY; public: explicit EQSLBase() {}; virtual ~EQSLBase() {}; DECLARE_SECURE_SERVICE(EQSLBase); static const QString getUsername(); static const QString getPasswd(); static void saveUsernamePassword(const QString&, const QString&); }; class EQSLUploader : public GenericQSOUploader, private EQSLBase { Q_OBJECT public: static QStringList uploadedFields; static QVariantMap generateUploadConfigMap(const QString qthProfile, bool disableQSLMSG, bool commentAsQSLMSG) { return QVariantMap({{"qthprofile", qthProfile}, {"disableqslmsg", disableQSLMSG}, {"commentasqslmsg", commentAsQSLMSG}}); } explicit EQSLUploader(QObject *parent = nullptr); virtual ~EQSLUploader(); void uploadAdif(const QByteArray&); virtual void uploadQSOList(const QList& qsos, const QVariantMap &addlParams) override; public slots: virtual void abortRequest() override; protected: virtual const QSqlRecord stripRecord(const QSqlRecord &record) override; private: const QString UPLOAD_ADIF_PAGE = "https://www.eQSL.cc/qslcard/ImportADIF.cfm"; QNetworkReply *currentReply; bool commentAsQSLMSG; bool disableqslmsg; virtual void processReply(QNetworkReply*) override; }; class EQSLQSLDownloader : public GenericQSLDownloader, private EQSLBase { Q_OBJECT public: explicit EQSLQSLDownloader(QObject *parent = nullptr); virtual ~EQSLQSLDownloader(); virtual void receiveQSL(const QDate &, bool, const QString &) override; void getQSLImage(const QSqlRecord&); signals: void QSLImageFound(QString); void QSLImageError(QString); public slots: virtual void abortDownload() override; private: QNetworkReply *currentReply; QSLStorage *qslStorage; const QString DOWNLOAD_1ST_PAGE = "https://www.eQSL.cc/qslcard/DownloadInBox.cfm"; const QString DOWNLOAD_2ND_PAGE = "https://www.eQSL.cc/downloadedfiles/"; const QString QSL_IMAGE_FILENAME_PAGE = "https://www.eQSL.cc/qslcard/GeteQSL.cfm"; const QString QSL_IMAGE_DOWNLOAD_PAGE = "https://www.eQSL.cc"; virtual void processReply(QNetworkReply* reply) override; void get(const QList > &); void downloadADIF(const QString &); void downloadImage(const QString &, const QString &, const qulonglong); QString QSLImageFilename(const QSqlRecord &); bool isQSLImageInCache(const QSqlRecord &, QString &); }; #endif // QLOG_SERVICE_EQSL_EQSL_H ================================================ FILE: service/hamqth/HamQTH.cpp ================================================ #include #include #include #include #include #include #include #include "HamQTH.h" #include "core/debug.h" #include "core/CredentialStore.h" #include "core/LogParam.h" MODULE_IDENTIFICATION("qlog.core.hamqth"); const QString HamQTHBase::SECURE_STORAGE_KEY = "HamQTH"; const QString HamQTHCallbook::CALLBOOK_NAME = "hamqth"; REGISTRATION_SECURE_SERVICE(HamQTHBase); const QString HamQTHBase::getUsername() { FCT_IDENTIFICATION; return LogParam::getHamQTHCallbookUsername(); } const QString HamQTHBase::getPasswd() { FCT_IDENTIFICATION; return getPassword(HamQTHBase::SECURE_STORAGE_KEY, getUsername()); } void HamQTHBase::saveUsernamePassword(const QString &newUsername, const QString &newPassword) { FCT_IDENTIFICATION; QString oldUsername = getUsername(); if ( oldUsername != newUsername ) { deletePassword(HamQTHBase::SECURE_STORAGE_KEY, oldUsername); } LogParam::setHamQTHCallbookUsername(newUsername); savePassword(HamQTHBase::SECURE_STORAGE_KEY, newUsername, newPassword); } void HamQTHBase::registerCredentials() { // both storage keys belong to the same logical service CredentialRegistry::instance().add(SECURE_STORAGE_KEY, []() { return QList { { SECURE_STORAGE_KEY, [](){ return getUsername(); } } }; }); } HamQTHCallbook::HamQTHCallbook(QObject* parent) : GenericCallbook(parent), HamQTHBase(), currentReply(nullptr) { FCT_IDENTIFICATION; incorrectLogin = false; } HamQTHCallbook::~HamQTHCallbook() { if ( currentReply ) currentReply->abort(); } QString HamQTHCallbook::getDisplayName() { FCT_IDENTIFICATION; return QString(tr("HamQTH")); } void HamQTHCallbook::queryCallsign(const QString &callsign) { FCT_IDENTIFICATION; qCDebug(function_parameters)<< callsign; if (sessionId.isEmpty()) { queuedCallsign = callsign; authenticate(); return; } QUrlQuery query; query.addQueryItem("id", sessionId); query.addQueryItem("callsign", callsign); query.addQueryItem("prg", "QLog"); QUrl url(API_URL); url.setQuery(query); if ( currentReply ) qCWarning(runtime) << "processing a new request but the previous one hasn't been completed yet !!!"; currentReply = getNetworkAccessManager()->get(QNetworkRequest(url)); currentReply->setProperty("queryCallsign", callsign); // Attention, variable callsign and queuedCallsign point to the same object // queuedCallsign must be cleared after the last use of the callsign variable queuedCallsign = QString(); } void HamQTHCallbook::abortQuery() { FCT_IDENTIFICATION; if ( currentReply ) { currentReply->abort(); //currentReply->deleteLater(); // pointer is deleted later in processReply currentReply = nullptr; } } void HamQTHCallbook::authenticate() { FCT_IDENTIFICATION; const QString &username = getUsername(); const QString &password = getPasswd(); if ( incorrectLogin && password == lastSeenPassword) { /* User already knows that login failed */ emit callsignNotFound(queuedCallsign); queuedCallsign = QString(); return; } if (!username.isEmpty() && !password.isEmpty()) { QUrlQuery query; query.addQueryItem("u", username.toUtf8().toPercentEncoding()); query.addQueryItem("p", password.toUtf8().toPercentEncoding()); QUrl url(API_URL); url.setQuery(query); if ( currentReply ) qCWarning(runtime) << "processing a new request but the previous one hasn't been completed yet !!!"; currentReply = getNetworkAccessManager()->get(QNetworkRequest(url)); lastSeenPassword = password; qCDebug(runtime) << "Sent Auth message"; } else { emit callsignNotFound(queuedCallsign); qCDebug(runtime) << "Empty username or password"; } } void HamQTHCallbook::processReply(QNetworkReply* reply) { FCT_IDENTIFICATION; /* always process one requests per class */ currentReply = nullptr; int replyStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); if ( reply->error() != QNetworkReply::NoError || replyStatusCode < 200 || replyStatusCode >= 300) { qCDebug(runtime) << "HamQTH error" << reply->errorString(); qCDebug(runtime) << "HTTP Status Code" << replyStatusCode; emit lookupError(reply->errorString()); reply->deleteLater(); return; } const QByteArray &response = reply->readAll(); qCDebug(runtime) << response; QXmlStreamReader xml(response); CallbookResponseData resposeData; while (!xml.atEnd() && !xml.hasError()) { QXmlStreamReader::TokenType token = xml.readNext(); if (token != QXmlStreamReader::StartElement) continue; const QString &elementName = xml.name().toString(); if ( elementName == "error" ) { queuedCallsign = QString(); QString errorString = xml.readElementText(); if ( errorString == "Wrong user name or password" ) { incorrectLogin = true; emit loginFailed(); } else if ( errorString == "Callsign not found" ) { incorrectLogin = false; emit callsignNotFound(reply->property("queryCallsign").toString()); return; } else qWarning() << "HamQTH Error - " << errorString; sessionId = QString(); emit lookupError(errorString); return; } else incorrectLogin = false; if (elementName == "session_id") sessionId = xml.readElementText(); else if (elementName == "callsign") resposeData.call = decodeHtmlEntities(xml.readElementText().toUpper()); else if (elementName == "nick") resposeData.nick = decodeHtmlEntities(xml.readElementText()); else if (elementName == "qth") resposeData.qth = decodeHtmlEntities(xml.readElementText()); else if (elementName == "grid") resposeData.gridsquare = decodeHtmlEntities(xml.readElementText().toUpper()); else if (elementName == "qsl_via") resposeData.qsl_via = decodeHtmlEntities(xml.readElementText().toUpper()); else if (elementName == "cq") resposeData.cqz = decodeHtmlEntities(xml.readElementText()); else if (elementName == "itu") resposeData.ituz = decodeHtmlEntities(xml.readElementText()); else if (elementName == "dok") resposeData.dok = decodeHtmlEntities(xml.readElementText().toUpper()); else if (elementName == "iota") resposeData.iota = decodeHtmlEntities(xml.readElementText().toUpper()); else if (elementName == "email") resposeData.email = decodeHtmlEntities(xml.readElementText()); else if (elementName == "adif") resposeData.dxcc = decodeHtmlEntities(xml.readElementText()); else if (elementName == "adr_name") resposeData.name_fmt = decodeHtmlEntities(xml.readElementText()); else if (elementName == "adr_street1") resposeData.addr1 = decodeHtmlEntities(xml.readElementText()); else if (elementName == "us_state") resposeData.us_state = decodeHtmlEntities(xml.readElementText()); else if (elementName == "adr_zip") resposeData.zipcode = decodeHtmlEntities(xml.readElementText()); else if (elementName == "country") resposeData.country = decodeHtmlEntities(xml.readElementText()); else if (elementName == "latitude") resposeData.latitude = decodeHtmlEntities(xml.readElementText()); else if (elementName == "longitude") resposeData.longitude = decodeHtmlEntities(xml.readElementText()); else if (elementName == "county") resposeData.county = decodeHtmlEntities(xml.readElementText()); else if (elementName == "lic_year") resposeData.lic_year = decodeHtmlEntities(xml.readElementText()); else if (elementName == "utc_offset") resposeData.utc_offset = decodeHtmlEntities(xml.readElementText()); else if (elementName == "eqsl") resposeData.eqsl = decodeHtmlEntities(xml.readElementText()); else if (elementName == "qsl") resposeData.pqsl = decodeHtmlEntities(xml.readElementText()); else if (elementName == "birth_year") resposeData.born = decodeHtmlEntities(xml.readElementText()); else if (elementName == "lotw") resposeData.lotw = decodeHtmlEntities(xml.readElementText()); else if (elementName == "web") resposeData.url = decodeHtmlEntities(xml.readElementText()); else if (elementName == "picture") resposeData.image_url = decodeHtmlEntities(xml.readElementText()); // HamQTH sends "http" URLs, which are redirected automatically // to their "https" variants. It's pointless to implement redirection // so let's replace http with https if ( !resposeData.image_url.contains("https")) resposeData.image_url.replace("http", "https"); } reply->deleteLater(); if ( !resposeData.call.isEmpty() ) emit callsignResult(resposeData); if (!queuedCallsign.isEmpty()) queryCallsign(queuedCallsign); } ================================================ FILE: service/hamqth/HamQTH.h ================================================ #ifndef QLOG_SERVICE_HAMQTH_HAMQTH_H #define QLOG_SERVICE_HAMQTH_HAMQTH_H #include #include #include "service/GenericCallbook.h" #include "core/CredentialStore.h" class QNetworkAccessManager; class QNetworkReply; class HamQTHBase : public SecureServiceBase { protected: const static QString SECURE_STORAGE_KEY; public: explicit HamQTHBase() {}; virtual ~HamQTHBase() {}; DECLARE_SECURE_SERVICE(HamQTHBase); static const QString getUsername(); static const QString getPasswd(); static void saveUsernamePassword(const QString&, const QString&); }; class HamQTHCallbook : public GenericCallbook, private HamQTHBase { Q_OBJECT public: const static QString CALLBOOK_NAME; explicit HamQTHCallbook(QObject *parent = nullptr); virtual ~HamQTHCallbook(); QString getDisplayName() override; public slots: virtual void queryCallsign(const QString &callsign) override; virtual void abortQuery() override; protected: void processReply(QNetworkReply* reply) override; private: const QString API_URL = "https://www.hamqth.com/xml.php"; QString sessionId; QString queuedCallsign; bool incorrectLogin; QString lastSeenPassword; QNetworkReply *currentReply; void authenticate(); }; #endif // QLOG_SERVICE_HAMQTH_HAMQTH_H ================================================ FILE: service/hrdlog/HRDLog.cpp ================================================ #include #include #include #include #include #include #include "HRDLog.h" #include "core/debug.h" #include "core/CredentialStore.h" #include "rig/macros.h" #include "core/LogParam.h" #include "data/Data.h" MODULE_IDENTIFICATION("qlog.core.hrdlog"); const QString HRDLogBase::SECURE_STORAGE_KEY = "HRDLog"; REGISTRATION_SECURE_SERVICE(HRDLogBase); // http://www.iw1qlh.net/projects/hrdlog/HRDLognet_4.pdf QStringList HRDLogUploader::uploadedFields = { "start_time", "freq", "band", "callsign", "mode", "submode", "rst_rcvd", "rst_sent", "qsl_sent", "qsl_rcvd", "qsl_via", "eqsl_qslrdate", "eqsl_qsl_rcvd", "eqsl_qslsdate", "eqsl_qsl_sent", "lotw_qsl_rcvd", "lotw_qslrdate", "lotw_qsl_sent", "lotw_qslsdate", "gridsquare", "dxcc", "qslmsg", "comment", "distance", "operator", "sat_mode", "sat_name", "prop_mode" }; const QString HRDLogBase::getRegisteredCallsign() { FCT_IDENTIFICATION; return LogParam::getHRDLogLogbookReqCallsign(); } const QString HRDLogBase::getUploadCode() { FCT_IDENTIFICATION; return getPassword(HRDLogBase::SECURE_STORAGE_KEY, getRegisteredCallsign()); } void HRDLogBase::saveUploadCode(const QString &newUsername, const QString &newPassword) { FCT_IDENTIFICATION; QString oldUsername = getRegisteredCallsign(); if ( oldUsername != newUsername ) { deletePassword(HRDLogBase::SECURE_STORAGE_KEY, oldUsername); } LogParam::setHRDLogLogbookReqCallsign(newUsername); savePassword(HRDLogBase::SECURE_STORAGE_KEY, newUsername, newPassword); } bool HRDLogBase::getOnAirEnabled() { FCT_IDENTIFICATION; return LogParam::getHRDLogOnAir(); } void HRDLogBase::saveOnAirEnabled(bool state) { FCT_IDENTIFICATION; LogParam::setHRDLogOnAir(state); } void HRDLogBase::registerCredentials() { // both storage keys belong to the same logical service CredentialRegistry::instance().add(SECURE_STORAGE_KEY, []() { return QList { { SECURE_STORAGE_KEY, [](){ return getRegisteredCallsign(); } } }; }); } // http://www.iw1qlh.net/projects/hrdlog/HRDLognet_4.pdf // It is not clear what QLog should send to HRDLog. Therefore it will // send all ADIF-fields HRDLogUploader::HRDLogUploader(QObject *parent) : GenericQSOUploader(uploadedFields, parent), HRDLogBase(), currentReply(nullptr), cancelUpload(false) { FCT_IDENTIFICATION; } HRDLogUploader::~HRDLogUploader() { FCT_IDENTIFICATION; if ( currentReply ) { currentReply->abort(); currentReply->deleteLater(); } } void HRDLogUploader::abortRequest() { FCT_IDENTIFICATION; cancelUpload = true; if ( currentReply ) { currentReply->abort(); //currentReply->deleteLater(); // pointer is deleted later in processReply currentReply = nullptr; } } void HRDLogUploader::uploadAdif(const QByteArray &data, const QVariant &contactID, bool update) { FCT_IDENTIFICATION; QUrlQuery params; params.addQueryItem("Callsign", getRegisteredCallsign().toUtf8().toPercentEncoding()); params.addQueryItem("Code", getUploadCode().toUtf8().toPercentEncoding()); params.addQueryItem("App", "QLog"); params.addQueryItem("ADIFData", data.trimmed().toPercentEncoding()); if ( update ) { params.addQueryItem("ADIFKey", data.trimmed().toPercentEncoding()); params.addQueryItem("Cmd", "UPDATE"); } QUrl url(API_LOG_UPLOAD_URL); QNetworkRequest request(url); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); qCDebug(runtime) << Data::safeQueryString(params); if ( currentReply ) { qCWarning(runtime) << "processing a new request but the previous one hasn't been completed yet !!!"; } currentReply = getNetworkAccessManager()->post(request, params.query(QUrl::FullyEncoded).toUtf8()); currentReply->setProperty("messageType", "uploadQSO"); currentReply->setProperty("ADIFData", data); currentReply->setProperty("contactID", contactID); } void HRDLogUploader::uploadContact(const QSqlRecord &record) { FCT_IDENTIFICATION; QByteArray data = generateADIF({record}); cancelUpload = false; uploadAdif(data.trimmed(), record.value("id"), (record.value("hrdlog_qso_upload_status").toString() == "M")); } void HRDLogUploader::uploadQSOList(const QList &qsos, const QVariantMap &) { FCT_IDENTIFICATION; /* always process one requests per class */ if ( qsos.isEmpty() ) { /* Nothing to do */ emit uploadFinished(); return; } cancelUpload = false; queuedContacts4Upload = qsos; uploadContact(queuedContacts4Upload.first()); queuedContacts4Upload.removeFirst(); } void HRDLogUploader::sendOnAir(double freq, const QString &mode) { FCT_IDENTIFICATION; qCDebug(function_parameters) << freq << mode; QUrlQuery params; params.addQueryItem("Callsign", getRegisteredCallsign().toUtf8().toPercentEncoding()); params.addQueryItem("Code", getUploadCode().toUtf8().toPercentEncoding()); params.addQueryItem("App", "QLog"); params.addQueryItem("Frequency", QString::number(static_cast(MHz(freq)))); params.addQueryItem("Mode", mode); params.addQueryItem("Radio", " "); QUrl url(API_ONAIR_URL); QNetworkRequest request(url); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); qCDebug(runtime) << Data::safeQueryString(params); if ( currentReply ) qCWarning(runtime) << "processing a new request but the previous one hasn't been completed yet !!!"; currentReply = getNetworkAccessManager()->post(request, params.query(QUrl::FullyEncoded).toUtf8()); currentReply->setProperty("messageType", QVariant("onAir")); } void HRDLogUploader::processReply(QNetworkReply *reply) { FCT_IDENTIFICATION; /* always process one requests per class */ currentReply = nullptr; int replyStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); if ( reply->error() != QNetworkReply::NoError || replyStatusCode < 200 || replyStatusCode >= 300) { qCDebug(runtime) << "HDRLog.com error URL " << reply->request().url().toString(); qCDebug(runtime) << "HDRLog.com error" << reply->errorString(); qCDebug(runtime) << "HTTP Status Code" << replyStatusCode; if ( reply->error() != QNetworkReply::OperationCanceledError ) { emit uploadError(reply->errorString()); reply->deleteLater(); } cancelUpload = true; return; } const QString &messageType = reply->property("messageType").toString(); qCDebug(runtime) << "Received Message Type: " << messageType; const QByteArray &response = reply->readAll(); qCDebug(runtime) << response; /*************/ /* uploadQSO */ /*************/ if ( messageType == "uploadQSO" ) { QDomDocument doc; if ( !doc.setContent(response) ) { qWarning() << "Failed to parse XML document from HRDLog"; emit uploadError(tr("Response message malformed")); cancelUpload = true; } else { QDomElement root = doc.documentElement(); QDomNodeList errorNodes = root.elementsByTagName("error"); if ( !errorNodes.isEmpty() ) { QDomElement errorElement = errorNodes.at(0).toElement(); QString errorText = errorElement.text(); qCDebug(runtime) << "XML contains an error element:" << errorText; if ( errorText.contains("Unable to find QSO") ) { // Try to resend it without UPDATE Flag uploadAdif(reply->property("ADIFData").toByteArray(), reply->property("contactID")); } else { emit uploadError(errorText); cancelUpload = true; } } else { qCDebug(runtime) << "Confirmed Upload for QSO Id " << reply->property("contactID").toInt(); emit uploadedQSO(reply->property("contactID").toULongLong()); if ( queuedContacts4Upload.isEmpty() ) { cancelUpload = false; emit uploadFinished(); } else if ( ! cancelUpload ) { uploadContact(queuedContacts4Upload.first()); queuedContacts4Upload.removeFirst(); } } } } else if ( messageType == "onAir" ) { // Do no handle onAir response - error handling is unclear from spec } reply->deleteLater(); } ================================================ FILE: service/hrdlog/HRDLog.h ================================================ #ifndef QLOG_SERVICE_HRDLOG_HRDLOG_H #define QLOG_SERVICE_HRDLOG_HRDLOG_H #include #include #include "service/GenericQSOUploader.h" #include "core/CredentialStore.h" class QNetworkReply; class QNetworkAccessManager; class HRDLogBase : public SecureServiceBase { protected: const static QString SECURE_STORAGE_KEY; public: explicit HRDLogBase() {}; virtual ~HRDLogBase() {}; DECLARE_SECURE_SERVICE(HRDLogBase); static const QString getRegisteredCallsign(); static const QString getUploadCode(); static bool getOnAirEnabled(); static void saveUploadCode(const QString &newUsername, const QString &newPassword); static void saveOnAirEnabled(bool state); }; class HRDLogUploader : public GenericQSOUploader, private HRDLogBase { Q_OBJECT public: static QStringList uploadedFields; explicit HRDLogUploader(QObject *parent = nullptr); virtual ~HRDLogUploader(); void uploadAdif(const QByteArray &data, const QVariant &contactID, bool update = false); void uploadContact(const QSqlRecord &record); virtual void uploadQSOList(const QList& qsos, const QVariantMap &addlParams) override; void sendOnAir(double freq, const QString &mode); public slots: virtual void abortRequest() override; protected: virtual void processReply(QNetworkReply* reply) override; private: QNetworkReply *currentReply; QList queuedContacts4Upload; bool cancelUpload; const QString API_LOG_UPLOAD_URL = "https://robot.hrdlog.net/NewEntry.aspx"; const QString API_ONAIR_URL = "https://robot.hrdlog.net/OnAir.aspx"; }; #endif // QLOG_SERVICE_HRDLOG_HRDLOG_H ================================================ FILE: service/kstchat/KSTChat.cpp ================================================ #include #include #include #include #ifdef Q_OS_WIN #include #include #include #else #include #include #include #endif #include "KSTChat.h" #include "core/debug.h" #include "data/Data.h" #include "data/StationProfile.h" #include "core/CredentialStore.h" #include "core/LogParam.h" MODULE_IDENTIFICATION("qlog.core.kstchat"); REGISTRATION_SECURE_SERVICE(KSTChat); KSTChat::KSTChat(int chatRoomIndex, const QString &username, const QString &password, const NewContactWidget *contact, QObject *parent) : QObject{parent}, chatRoomIdx(chatRoomIndex), userName(username), password(password), socket(nullptr), currCommand(NO_CMD), contact(contact) { FCT_IDENTIFICATION; #if 0 // Only for debug. It generate a testing message. QTimer *timer = new QTimer(this); connect(timer, &QTimer::timeout, this, [this]() { KSTChatMsg msg; msg.time = QDateTime::currentDateTimeUtc().toString("hhmm"); msg.sender = "OK1MLF"; msg.message = "Toto je pokusna zprava pro vsechny"; msg.grid = Gridsquare("JN79HK"); emit chatMsg(msg); }); timer->start(5000); #endif } KSTChat::~KSTChat() { FCT_IDENTIFICATION; disconnectChat(); } QList KSTChat::getUsersList() const { FCT_IDENTIFICATION; return userList; } KSTUsersInfo KSTChat::getUserInfo(const QString &username) const { FCT_IDENTIFICATION; qCDebug(function_parameters) << username; for ( const KSTUsersInfo &info : static_cast&>(userList) ) { if ( info.callsign == username ) return info; } return KSTUsersInfo(); } const QString KSTChat::getUsername() { FCT_IDENTIFICATION; return LogParam::getKSTChatUsername(); } const QString KSTChat::getPasswd() { FCT_IDENTIFICATION; return getPassword(KSTChat::SECURE_STORAGE_KEY, getUsername()); } void KSTChat::saveUsernamePassword(const QString &newUsername, const QString &newPassword) { FCT_IDENTIFICATION; const QString &oldUsername = getUsername(); if ( oldUsername != newUsername ) { deletePassword(KSTChat::SECURE_STORAGE_KEY, oldUsername); } LogParam::setKSTChatUsername(newUsername); savePassword(KSTChat::SECURE_STORAGE_KEY, newUsername, newPassword); } void KSTChat::registerCredentials() { // both storage keys belong to the same logical service CredentialRegistry::instance().add(SECURE_STORAGE_KEY, []() { return QList { { SECURE_STORAGE_KEY, [](){ return getUsername(); } } }; }); } void KSTChat::connectChat() { FCT_IDENTIFICATION; socket = new QTcpSocket(this); connect(socket, &QTcpSocket::readyRead, this, &KSTChat::receiveData); connect(socket, &QTcpSocket::connected, this, &KSTChat::socketConnected); #if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) connect(socket, QOverload::of(&QAbstractSocket::error), this, &KSTChat::socketError); #else connect(socket, &QTcpSocket::errorOccurred, this, &KSTChat::socketError); #endif socket->connectToHost(KST_HOSTNAME, KST_PORT); } void KSTChat::disconnectChat() { FCT_IDENTIFICATION; if ( socket ) { socket->disconnect(); socket->close(); socket->deleteLater(); socket = nullptr; } currCommand = NO_CMD; commandQueue.clear(); receiveBuffer.clear(); commandLineBuffer.clear(); emit chatDisconnected(); } void KSTChat::sendMessage(const QString &msg) { FCT_IDENTIFICATION; if ( msg.length() == 0 ) return; if ( msg.startsWith("/chat",Qt::CaseInsensitive) ) { emit chatError("Changing chat is not supported"); return; } sendCommand(( msg.startsWith("/") ) ? USER_CMD : NO_CMD, msg); } void KSTChat::reloadStationProfile() { FCT_IDENTIFICATION; sendSetGridCommand(); } void KSTChat::resetDupe() { FCT_IDENTIFICATION; for ( KSTUsersInfo &user: userList ) user.dupeCount = 0; emit usersListUpdated(); } void KSTChat::recalculateDupe() { FCT_IDENTIFICATION; if ( !contact ) return; const QString &modeGroupString = BandPlan::modeToDXCCModeGroup(contact->getMode()); for ( KSTUsersInfo &user: userList ) user.dupeCount = Data::countDupe(user.callsign, contact->getBand(), modeGroupString); emit usersListUpdated(); } void KSTChat::recalculateDxccStatus() { FCT_IDENTIFICATION; if ( !contact ) return; const QString &currBand = contact->getBand(); const QString &modeGroupString = BandPlan::modeToDXCCModeGroup(contact->getMode()); for ( KSTUsersInfo &user: userList ) user.status = Data::instance()->dxccStatus(user.dxcc.dxcc, currBand, modeGroupString); emit usersListUpdated(); } void KSTChat::updateSpotsStatusWhenQSOAdded(const QSqlRecord &record) { FCT_IDENTIFICATION; if ( !contact ) return; qint32 dxcc = record.value("dxcc").toInt(); const QString &band = record.value("band").toString(); const QString &dxccModeGroup = BandPlan::modeToDXCCModeGroup(record.value("mode").toString()); const QString &callsign = record.value("callsign").toString(); const QString &currBand = contact->getBand(); const QString &modeGroupString = BandPlan::modeToDXCCModeGroup(contact->getMode()); for ( KSTUsersInfo &user: userList ) { user.status = Data::dxccNewStatusWhenQSOAdded(user.status, user.dxcc.dxcc, currBand, modeGroupString, dxcc, currBand, dxccModeGroup); if ( user.callsign == callsign ) user.dupeCount = Data::dupeNewCountWhenQSOAdded(user.dupeCount, currBand, modeGroupString, band, dxccModeGroup); } emit usersListUpdated(); } void KSTChat::updateSpotsStatusWhenQSODeleted(const QSqlRecord &record) { FCT_IDENTIFICATION; if ( !contact ) return; const QString &band = record.value("band").toString(); const QString &dxccModeGroup = BandPlan::modeToDXCCModeGroup(record.value("mode").toString()); const QString &callsign = record.value("callsign").toString(); const QString &currBand = contact->getBand(); const QString &modeGroupString = BandPlan::modeToDXCCModeGroup(contact->getMode()); for ( KSTUsersInfo &user: userList ) { if ( user.dupeCount && user.callsign == callsign ) user.dupeCount = Data::dupeNewCountWhenQSODelected(user.dupeCount, currBand, modeGroupString, band, dxccModeGroup); } } void KSTChat::updateSpotsDxccStatusWhenQSODeleted(const QSet &entities) { FCT_IDENTIFICATION; // this method is called at the end of QSO Delete (after commit). if ( entities.isEmpty() || !contact) return; const QString &currBand = contact->getBand(); const QString &modeGroupString = BandPlan::modeToDXCCModeGroup(contact->getMode()); for ( KSTUsersInfo &user: userList ) { if ( !entities.contains(user.dxcc.dxcc) ) continue; user.status = Data::instance()->dxccStatus(user.dxcc.dxcc, currBand, modeGroupString); } emit usersListUpdated(); } void KSTChat::sendShowUsersCommand() { FCT_IDENTIFICATION; sendCommand(SHOW_USERS_CMD, "/sh us"); } void KSTChat::sendCommand(const Command &cmd, const QString &msg) { FCT_IDENTIFICATION; if ( currCommand != NO_CMD ) { qCDebug(runtime) << "Storing: " << msg; commandQueue << QPair(cmd, msg); return; } currCommand = cmd; if ( socket && socket->isOpen() ) { QByteArray data; data.append(msg.toLatin1()); data.append("\r\n"); qCDebug(runtime) << "Sending: " << msg; socket->write(data); } } void KSTChat::sendSetGridCommand() { FCT_IDENTIFICATION; const StationProfile &profile = StationProfilesManager::instance()->getCurProfile1(); sendCommand(SET_GRID_CMD, "/set qra " + profile.locator); } QStringList KSTChat::joinLines(const QByteArray &data) { FCT_IDENTIFICATION; qCDebug(function_parameters) << data; QByteArray fixedData(data); QStringList retList; fixedData.replace('\0', ""); receiveBuffer.append(QString::fromUtf8(fixedData)); int index = receiveBuffer.indexOf("\n"); while ( index != -1 ) { QString line = receiveBuffer.left(index + 1); //including "\n" char receiveBuffer = receiveBuffer.mid(index + 1); retList.append(line.trimmed()); index = receiveBuffer.indexOf('\n'); } return retList; } void KSTChat::receiveData() { FCT_IDENTIFICATION; static QRegularExpression chatLineRE("([0-9]{4})Z (.*)>(.*)"); QRegularExpressionMatch chatLineMatch; QString chatName(chatRooms.at(chatRoomIdx-1)); QRegularExpression chatCMDEndRE("([0-9]{4})Z " + userName.toUpper() + " " + QRegularExpression::escape(chatName) + " chat>(.*)"); QRegularExpressionMatch chatCMDEndMatch; const QStringList &lines = joinLines(socket->readAll()); for (const QString &line : lines ) { qCDebug(runtime) << "Processing Line" << line << "CMD" << currCommand; // Skip empty lines if ( line.length() == 0 ) { continue; } else if ( line.startsWith("Login:") ) { sendMessage(userName); return; } else if ( line.startsWith("Password:") ) { sendMessage(password); return; } else if ( line.startsWith("Your choice :") ) { sendCommand(LOGIN_CMD, QString::number(chatRoomIdx)); return; } else if ( line.startsWith("Unknown user") ) { emit chatError(tr("Unknown User")); disconnectChat(); return; } else if ( line.startsWith("Wrong password!") ) { emit chatError(tr("Invalid password")); disconnectChat(); return; } else { chatCMDEndMatch = chatCMDEndRE.match(line); if ( chatCMDEndMatch.hasMatch() ) { qCDebug(runtime) << "CMD" << currCommand << " - End Detected"; KSTChatMsg msg; msg.time = QDateTime::currentDateTimeUtc().toString("hhmm"); msg.sender = QString(); switch ( currCommand ) { case LOGIN_CMD: emit chatConnected(); msg.message = commandLineBuffer.join("\n"); emit chatMsg(msg); sendSetGridCommand(); sendShowUsersCommand(); break; case SET_GRID_CMD: msg.message = commandLineBuffer.join("\n"); emit chatMsg(msg); break; case SHOW_USERS_CMD: finalizeShowUsersCommand(commandLineBuffer); break; case USER_CMD: msg.message = commandLineBuffer.join("\n"); emit chatMsg(msg); break; default: {} } currCommand = NO_CMD; commandLineBuffer.clear(); if ( !commandQueue.isEmpty() ) { QPair cmd = commandQueue.takeFirst(); sendCommand(cmd.first, cmd.second); } } else { chatLineMatch = chatLineRE.match(line); if ( chatLineMatch.hasMatch() ) { KSTChatMsg msg; msg.time = QDateTime::currentDateTimeUtc().toString("hhmm");// own time to be better // chatLineMatch.captured(1); msg.sender = chatLineMatch.captured(2); msg.message = chatLineMatch.captured(3); msg.grid = getUserInfo(msg.sender).grid; emit chatMsg(msg); } else { if ( currCommand != NO_CMD) commandLineBuffer.append(line); } } } } } void KSTChat::socketConnected() { FCT_IDENTIFICATION; if ( !socket ) { qWarning() << "Socket is not opened"; return; } int fd = socket->socketDescriptor(); #ifdef Q_OS_WIN DWORD dwBytesRet = 0; struct tcp_keepalive alive; // your options for "keepalive" mode alive.onoff = TRUE; // turn it on alive.keepalivetime = 10000; // delay (ms) between requests, here is 10s, default is 2h (7200000) alive.keepaliveinterval = 5000; // delay between "emergency" ping requests, their number (6) is not configurable /* So with this config socket will send keepalive requests every 30 seconds after last data transaction when everything is ok. If there is no reply (wire plugged out) it'll send 6 requests with 5s delay between them and then close. As a result we will get disconnect after approximately 1 min timeout. */ if (WSAIoctl(fd, SIO_KEEPALIVE_VALS, &alive, sizeof(alive), NULL, 0, &dwBytesRet, NULL, NULL) == SOCKET_ERROR) { qWarning() << "WSAIotcl(SIO_KEEPALIVE_VALS) failed with err#" << WSAGetLastError(); } #else int enableKeepAlive = 1; int maxIdle = 10; int count = 3; int interval = 10; if ( setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &enableKeepAlive, sizeof(enableKeepAlive)) !=0 ) { qWarning() << "Cannot set keepalive for DXC"; } else { #ifndef Q_OS_MACOS if ( setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &maxIdle, sizeof(maxIdle)) != 0 ) #else if ( setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &maxIdle, sizeof(maxIdle)) != 0 ) #endif /* Q_OS_MACOS */ { qWarning() << "Cannot set keepalive idle for DXC"; } if ( setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &count, sizeof(count)) != 0 ) { qWarning() << "Cannot set keepalive counter for DXC"; } if ( setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &interval, sizeof(interval)) != 0 ) { qWarning() << "Cannot set keepalive interval for DXC"; } // TODO: setup TCP_USER_TIMEOUT???? } #endif receiveBuffer.clear(); commandLineBuffer.clear(); } void KSTChat::socketError(QAbstractSocket::SocketError socker_error) { FCT_IDENTIFICATION; QString error_msg; qCDebug(runtime) << socker_error; switch (socker_error) { case QAbstractSocket::ConnectionRefusedError: error_msg.append(QObject::tr("Connection Refused")); break; case QAbstractSocket::RemoteHostClosedError: error_msg.append(QObject::tr("Host closed the connection")); disconnectChat(); //reconectRequested = true; break; case QAbstractSocket::HostNotFoundError: error_msg.append(QObject::tr("Host not found")); break; case QAbstractSocket::SocketTimeoutError: error_msg.append(QObject::tr("Timeout")); disconnectChat(); //reconectRequested = true; break; case QAbstractSocket::NetworkError: error_msg.append(QObject::tr("Network Error")); disconnectChat(); break; default: error_msg.append(QObject::tr("Internal Error")); disconnectChat(); } emit chatError(error_msg); } void KSTChat::finalizeShowUsersCommand(const QStringList &buffer) { FCT_IDENTIFICATION; static QRegularExpression recordRE("^(\\S{3,})\\s{1,}(\\S+)\\s(.*)$"); userList.clear(); for ( const QString &record : static_cast&>(buffer) ) { QRegularExpressionMatch match = recordRE.match(record); if ( match.hasMatch() ) { KSTUsersInfo user; user.callsign = match.captured(1).remove('(').remove(')'); user.grid = Gridsquare(match.captured(2)); user.stationComment = match.captured(3); user.dxcc = Data::instance()->lookupDxcc(user.callsign); user.status = DxccStatus::UnknownStatus; if ( contact ) { const QString &modeGroup = BandPlan::modeToDXCCModeGroup(contact->getMode()); user.status = Data::instance()->dxccStatus(user.dxcc.dxcc, contact->getBand(), modeGroup); user.dupeCount = Data::countDupe(user.callsign, contact->getBand(), modeGroup); } userList << user; } else { qCDebug(runtime) << "Record does not match the pattern"; } } emit usersListUpdated(); QTimer::singleShot(1000 * KST_UPDATE_USERS_LIST, this, [this]() { qCDebug(runtime) << "Updating User List"; sendShowUsersCommand(); }); } const QStringList KSTChat::chatRooms = {"50/70 MHz", "144/432 MHz", "Microwave", "EME/JT65", "Low Band (160-80m)", "50 MHz IARU Region 3", "50 MHz IARU Region 2", "144/432 MHz IARU R 2", "144/432 MHz IARU R 3", "kHz (2000-630m)", "Warc (30,17,12m)", "28 MHz", "40 MHz"}; const QString KSTChat::SECURE_STORAGE_KEY = "KST"; chatHighlightEvaluator::chatHighlightEvaluator(const int roomIndex, QObject *parent) : QObject(parent), roomIndex(roomIndex) { FCT_IDENTIFICATION; loadRules(); } void chatHighlightEvaluator::clearRules() { FCT_IDENTIFICATION; qDeleteAll(ruleList); ruleList.clear(); } QStringList chatHighlightEvaluator::getAllRuleNames() { FCT_IDENTIFICATION; QStringList ret; QSqlQuery ruleStmt; if ( ! ruleStmt.prepare("SELECT rule_name FROM chat_highlight_rules ORDER BY rule_name") ) { qWarning() << "Cannot prepare select statement"; } else { if ( ruleStmt.exec() ) { while (ruleStmt.next()) { ret << ruleStmt.value(0).toString(); } } else { qWarning()<< "Cannot get rule names from DB" << ruleStmt.lastError();; } } return ret; } void chatHighlightEvaluator::loadRules() { FCT_IDENTIFICATION; if ( ruleList.size() > 0 ) { clearRules(); } QSqlQuery ruleStmt; if ( ! ruleStmt.prepare("SELECT rule_name FROM chat_highlight_rules " "WHERE (room_id = :room_id or room_id = 0) AND enabled = 1") ) { qWarning() << "Cannot prepare select statement"; } else { ruleStmt.bindValue(":room_id", roomIndex); if ( ruleStmt.exec() ) { while ( ruleStmt.next() ) { chatHighlightRule *rule; rule = new chatHighlightRule(); if ( rule ) { if ( rule->load(ruleStmt.value(0).toString()) ) ruleList.append(rule); else rule->deleteLater(); } } } else { qWarning()<< "Cannot get rule names from DB" << ruleStmt.lastError(); } } } bool chatHighlightEvaluator::shouldHighlight(const KSTChatMsg &msg, QStringList &matchedRules) { FCT_IDENTIFICATION; for ( const chatHighlightRule *rule : static_cast&>(ruleList) ) { qCDebug(runtime) << "Processing " << rule->ruleName; if ( rule->match(roomIndex, msg) ) { matchedRules << rule->ruleName; } } return ( matchedRules.size() > 0 ); } chatHighlightRule::chatHighlightRule(QObject *parent) : QObject(parent), enabled(false), ruleRoomIndex(-1), interConditionOperand(OPERAND_OR), ruleValid(false) { FCT_IDENTIFICATION; } bool chatHighlightRule::save() { FCT_IDENTIFICATION; if ( ruleName.isEmpty() ) { qCDebug(runtime) << "rule name is empty - do not save"; return false; } QSqlQuery insertUpdateStmt; if ( ! insertUpdateStmt.prepare("INSERT INTO chat_highlight_rules(rule_name, room_id, enabled, rule_definition) " " VALUES (:ruleName, :room_id, :enabled, :rule_definition) " " ON CONFLICT(rule_name) DO UPDATE SET rule_definition = :rule_definition, enabled = :enabled, room_id = :room_id " " WHERE rule_name = :ruleName")) { qWarning() << "Cannot prepare insert/update Alert Rule statement" << insertUpdateStmt.lastError(); return false; } insertUpdateStmt.bindValue(":ruleName", ruleName); insertUpdateStmt.bindValue(":enabled", enabled); insertUpdateStmt.bindValue(":room_id", ruleRoomIndex); insertUpdateStmt.bindValue(":rule_definition", toJson()); if ( ! insertUpdateStmt.exec() ) { qCDebug(runtime)<< "Cannot Update Alert Rules - " << insertUpdateStmt.lastError().text(); return false; } return true; } bool chatHighlightRule::load(const QString &ruleName) { FCT_IDENTIFICATION; qCDebug(function_parameters) << ruleName; QSqlQuery query; if ( ! query.prepare("SELECT rule_definition " "FROM chat_highlight_rules " "WHERE rule_name = :rule_name") ) { qWarning() << "Cannot prepare select statement"; return false; } query.bindValue(":rule_name", ruleName); if ( query.exec() && query.next() ) { fromJson(QJsonDocument::fromJson(query.value(0).toByteArray())); } else { qCDebug(runtime) << "SQL execution error: " << query.lastError().text(); return false; } return true; } QByteArray chatHighlightRule::toJson() { FCT_IDENTIFICATION; QJsonObject ruleJsonObject; ruleJsonObject["rulename"] = ruleName; ruleJsonObject["enabled"] = enabled; ruleJsonObject["roomId"] = ruleRoomIndex; ruleJsonObject["operand"] = interConditionOperand; QJsonArray conditionsArray; for ( const Condition &condition : static_cast&>(conditions) ) { QJsonObject conditionObject; conditionObject["source"] = condition.source; conditionObject["operatorid"] = condition.operatorID; conditionObject["value"] = condition.value; conditionsArray.push_back(conditionObject); } ruleJsonObject["conditions"] = conditionsArray; QJsonDocument doc(ruleJsonObject); return doc.toJson(); } void chatHighlightRule::fromJson(const QJsonDocument &ruleDefinition) { FCT_IDENTIFICATION; if ( ruleDefinition.isNull() ) return; /* * { * rulename = "xxxx", * enabled = true/false, * roomId = x, * operand = 1, * conditions = [ {source = 1, * operatorID = 1, * value = "xxxx'}, * ] * } */ ruleName = ruleDefinition["rulename"].toString(); enabled = ruleDefinition["enabled"].toBool(); ruleRoomIndex = ruleDefinition["roomId"].toInt(); interConditionOperand = static_cast(ruleDefinition["operand"].toInt()); const QJsonArray &conditionArray = ruleDefinition["conditions"].toArray(); for ( const QJsonValue &value : conditionArray ) { QJsonObject obj = value.toObject(); Condition condition; condition.source = static_cast(obj["source"].toInt()); condition.operatorID = static_cast(obj["operatorid"].toInt()); condition.value = obj["value"].toString(); conditions.append(condition); } ruleValid = true; } bool chatHighlightRule::match(const int inRoomIndex, const KSTChatMsg &msg) const { FCT_IDENTIFICATION; qCDebug(function_parameters) << inRoomIndex << ruleRoomIndex << msg.sender << msg.message << msg.grid.getGrid(); if ( !ruleValid ) { qCDebug(runtime) << "Rule is invalid"; return false; } if ( !enabled ) { qCDebug(runtime) << "Rule is disabled"; return false; } if ( inRoomIndex != ruleRoomIndex && ruleRoomIndex != 0 ) { qCDebug(runtime) << "Rule for different room ID"; return false; } bool result = false; bool isFirstCondition = true; for ( const Condition &condition : static_cast&>(conditions) ) { QString columnValue; switch ( condition.source ) { case SENDER: columnValue = msg.sender; qCDebug(runtime) << "Sender"; break; case MESSAGE: columnValue = msg.message; qCDebug(runtime) << "Message"; break; case GRIDSQUARE: columnValue = msg.grid.getGrid(); qCDebug(runtime) << "Grid"; break; } bool operatorResult = false; switch ( condition.operatorID ) { case OPERATOR_CONTAINS: qCDebug(runtime) << "Contains" << condition.value; operatorResult = columnValue.contains(condition.value, Qt::CaseInsensitive); break; case OPERATOR_STARTWITH: qCDebug(runtime) << "StartWith" << condition.value; operatorResult = columnValue.startsWith(condition.value, Qt::CaseInsensitive); break; } if ( isFirstCondition ) { result = operatorResult; } else { switch( interConditionOperand ) { case OPERAND_OR: qCDebug(runtime) << "OR"; result = result || operatorResult; break; case OPERAND_AND: qCDebug(runtime) << "AND"; result = result && operatorResult; } } qCDebug(runtime) << "matching sub-result" << result; isFirstCondition = false; } qCDebug(runtime) << "matching result"<< result; return result; } ================================================ FILE: service/kstchat/KSTChat.h ================================================ #ifndef QLOG_SERVICE_KSTCHAT_KSTCHAT_H #define QLOG_SERVICE_KSTCHAT_KSTCHAT_H #include #include #include "data/Gridsquare.h" #include "data/Dxcc.h" #include "ui/NewContactWidget.h" #include "core/CredentialStore.h" struct KSTChatMsg { QString time; QString sender; QString message; Gridsquare grid; QStringList matchedHighlightRules; }; Q_DECLARE_METATYPE(KSTChatMsg); struct KSTUsersInfo { QString callsign; // do not use Callsign class because KST users a free text here Gridsquare grid; QString stationComment; DxccEntity dxcc; DxccStatus status; qulonglong dupeCount = 0; }; Q_DECLARE_METATYPE(KSTUsersInfo); class chatHighlightRule : public QObject { Q_OBJECT public: enum InfoSource { SENDER = 0, MESSAGE = 1, GRIDSQUARE = 2 }; enum Operator { OPERATOR_CONTAINS = 0, OPERATOR_STARTWITH = 1 }; enum InterConditionOperand{ OPERAND_AND = 0, OPERAND_OR = 1 }; struct Condition { InfoSource source; Operator operatorID; QString value; Condition() : source(SENDER), operatorID(OPERATOR_CONTAINS) {}; }; public: explicit chatHighlightRule(QObject *parent = nullptr); ~chatHighlightRule(){}; bool save(); bool load(const QString &); bool match(const int inRoomIndex, const KSTChatMsg &msg) const; QString ruleName; bool enabled; int ruleRoomIndex; InterConditionOperand interConditionOperand; QList conditions; bool ruleValid; private: void fromJson(const QJsonDocument &ruleDefinition); QByteArray toJson(); }; class chatHighlightEvaluator : public QObject { Q_OBJECT public: explicit chatHighlightEvaluator(const int roomIndex, QObject *parent = nullptr); ~chatHighlightEvaluator() { clearRules();} void clearRules(); static QStringList getAllRuleNames(); public slots: void loadRules(); bool shouldHighlight(const KSTChatMsg &msg, QStringList &matchedRules); private: QListruleList; int roomIndex; }; class KSTChat : public QObject, public SecureServiceBase { Q_OBJECT protected: static const QString SECURE_STORAGE_KEY; public: const static QStringList chatRooms; DECLARE_SECURE_SERVICE(KSTChat); explicit KSTChat(int chatRoomIndex, const QString &username, const QString &password, const NewContactWidget *contact, QObject *parent = nullptr); ~KSTChat(); QList getUsersList() const; KSTUsersInfo getUserInfo(const QString& username) const; static const QString getUsername(); static const QString getPasswd(); static void saveUsernamePassword(const QString&, const QString&); public slots: void connectChat(); void disconnectChat(); void sendMessage(const QString&); void reloadStationProfile(); void resetDupe(); void recalculateDupe(); void recalculateDxccStatus(); void updateSpotsStatusWhenQSOAdded(const QSqlRecord &record); void updateSpotsStatusWhenQSODeleted(const QSqlRecord &record); void updateSpotsDxccStatusWhenQSODeleted(const QSet &entities); private slots: void receiveData(); void socketConnected(); void socketError(QAbstractSocket::SocketError socker_error); signals: void chatConnected(); void chatDisconnected(); void chatError(QString); void chatMsg(KSTChatMsg); void usersListUpdated(); private: enum Command { NO_CMD = 0, LOGIN_CMD = 1, USER_CMD = 2, SHOW_USERS_CMD = 3, SET_GRID_CMD = 4 }; int chatRoomIdx; QString userName; QString password; QTcpSocket* socket; Command currCommand; QString receiveBuffer; QStringList commandLineBuffer; void sendShowUsersCommand(); void sendCommand(const Command&, const QString&); void sendSetGridCommand(); void finalizeShowUsersCommand(const QStringList&); QStringList joinLines(const QByteArray &data); QList userList; QList> commandQueue; const NewContactWidget *contact; const QString KST_HOSTNAME = "www.on4kst.org"; const quint16 KST_PORT = 23000; const quint16 KST_UPDATE_USERS_LIST = 3 * 60;// update user list every 3*60 seconds }; #endif // QLOG_SERVICE_KSTCHAT_KSTCHAT_H ================================================ FILE: service/lotw/Lotw.cpp ================================================ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Lotw.h" #include "logformat/AdiFormat.h" #include "core/debug.h" #include "core/CredentialStore.h" #include "core/LogParam.h" #include "data/Data.h" MODULE_IDENTIFICATION("qlog.core.lotw"); QStringList LotwUploader::uploadedFields = { "callsign", "freq", "band", "freq_rx", "mode", "submode", "start_time", "prop_mode", "sat_name", "station_callsign", "operator", "rst_sent", "rst_rcvd", "my_state", "my_cnty", "my_vucc_grids" }; const QString LotwBase::SECURE_STORAGE_KEY = "LoTW"; REGISTRATION_SECURE_SERVICE(LotwBase); const QString LotwBase::getUsername() { FCT_IDENTIFICATION; return LogParam::getLoTWCallbookUsername(); } const QString LotwBase::getPasswd() { FCT_IDENTIFICATION; return getPassword(LotwBase::SECURE_STORAGE_KEY, getUsername()); } void LotwBase::saveUsernamePassword(const QString &newUsername, const QString &newPassword) { FCT_IDENTIFICATION; const QString &oldUsername = getUsername(); if ( oldUsername != newUsername ) { deletePassword(LotwBase::SECURE_STORAGE_KEY, oldUsername); } LogParam::setLoTWCallbookUsername(newUsername); savePassword(LotwBase::SECURE_STORAGE_KEY, newUsername, newPassword); } const QString LotwBase::getTQSLPath(const QString &defaultPath) { FCT_IDENTIFICATION; #ifdef QLOG_FLATPAK // flatpak package contain an internal tqsl that is always on the same path Q_UNUSED(defaultPath); return QString("/app/bin/tqsl"); #else return LogParam::getLoTWTQSLPath(defaultPath); #endif } void LotwBase::saveTQSLPath(const QString &newPath) { FCT_IDENTIFICATION; #ifdef QLOG_FLATPAK // do not save path for Flatpak version - an internal tqsl instance is present in the package Q_UNUSED(newPath); #else LogParam::setLoTWTQSLPath(newPath); #endif } QString LotwBase::findTQSLPath() { FCT_IDENTIFICATION; // Platform-specific well-known paths const QStringList platformPaths = { #ifdef Q_OS_WIN "C:/Program Files/ARRL/TQSL/tqsl.exe", "C:/Program Files (x86)/ARRL/TQSL/tqsl.exe", QDir::homePath() + "/AppData/Local/Programs/TQSL/tqsl.exe" #elif defined(Q_OS_MACOS) "/Applications/tqsl.app/Contents/MacOS/tqsl", "/Applications/TQSL.app/Contents/MacOS/tqsl" #else "/usr/bin/tqsl", "/usr/local/bin/tqsl", "/opt/tqsl/bin/tqsl" #endif }; for ( const QString &p : platformPaths ) { if ( QFile::exists(p) ) { qCDebug(runtime) << "Found TQSL at:" << p; return p; } } // Last resort: search in $PATH const QString path = QStandardPaths::findExecutable("tqsl"); if ( !path.isEmpty() ) { qCDebug(runtime) << "Found TQSL in PATH:" << path; return path; } qCWarning(runtime) << "TQSL not found"; return QString(); } TQSLVersion LotwBase::getTQSLVersion(const QString &tqslPath) { FCT_IDENTIFICATION; qCDebug(function_parameters) << tqslPath; TQSLVersion version; const QString path = tqslPath.trimmed().isEmpty() ? findTQSLPath() : tqslPath.trimmed(); if ( path.isEmpty() ) { qCDebug(runtime) << "TQSL not found"; return version; } QProcess process; process.setProcessChannelMode(QProcess::MergedChannels); process.start(path, QStringList("--version")); if ( !process.waitForFinished(2000) ) { qCDebug(runtime) << "tqsl --version timed out"; return version; } const QString output = QString::fromLocal8Bit(process.readAllStandardOutput()).trimmed(); qCDebug(runtime) << "tqsl version output:" << output; // "TQSL Version 2.7.2 [unknown]" QRegularExpression re("TQSL\\s+Version\\s+(\\d+)\\.(\\d+)\\.(\\d+)"); QRegularExpressionMatch match = re.match(output); // clazy:exclude=use-static-qregularexpression if ( match.hasMatch() ) { version.major = match.captured(1).toInt(); version.minor = match.captured(2).toInt(); version.patch = match.captured(3).toInt(); qCDebug(runtime) << "Parsed TQSL version:" << version.major << version.minor << version.patch; } else { qCDebug(runtime) << "Failed to parse TQSL version from output:" << output; } return version; } void LotwBase::registerCredentials() { // both storage keys belong to the same logical service CredentialRegistry::instance().add(SECURE_STORAGE_KEY, []() { return QList { { SECURE_STORAGE_KEY, [](){ return getUsername(); } } }; }); } QString LotwBase::getTQSLStationDataPath() { FCT_IDENTIFICATION; // QStandardPaths::GenericDataLocation is redirected by Flatpak to // ~/.var/app//data, where the bundled TQSL stores its data. // On a standard install TQSL uses the legacy ~/.tqsl/ directory. // On Windows, TQSL stores station_data in %APPDATA%\TrustedQSL\station_data. const QStringList candidates = { #ifdef Q_OS_WIN QDir::cleanPath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/../TrustedQSL/station_data"), QDir::homePath() + "/AppData/Roaming/TrustedQSL/station_data" #else QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/tqsl/station_data", QDir::homePath() + "/.tqsl/station_data" #endif }; for ( const QString &path : candidates ) { if ( QFile::exists(path) ) { qCDebug(runtime) << "Found TQSL station_data at:" << path; return path; } } qCDebug(runtime) << "TQSL station_data not found"; return {}; } QList LotwBase::getTQSLStationLocations() { FCT_IDENTIFICATION; const QString path = getTQSLStationDataPath(); if ( path.isEmpty() ) return {}; QFile file(path); if ( !file.open(QIODevice::ReadOnly) ) { qCDebug(runtime) << "Cannot open TQSL station_data:" << path; return {}; } QList locations; QXmlStreamReader xml(&file); TQSLStationLocation current; bool inStationData = false; while ( !xml.atEnd() && !xml.hasError() ) { const QXmlStreamReader::TokenType tokenType = xml.readNext(); if ( tokenType == QXmlStreamReader::StartElement ) { if ( xml.name().compare(QLatin1String("StationData"), Qt::CaseInsensitive) == 0 ) { current = TQSLStationLocation{}; current.name = xml.attributes().value("name").toString(); inStationData = true; } else if ( inStationData ) { if ( xml.name().compare(QLatin1String("CALL"), Qt::CaseInsensitive) == 0 ) current.callsign = xml.readElementText(); else if ( xml.name().compare(QLatin1String("GRIDSQUARE"), Qt::CaseInsensitive) == 0 ) current.grid = xml.readElementText(); } } else if ( tokenType == QXmlStreamReader::EndElement && xml.name().compare(QLatin1String("StationData"), Qt::CaseInsensitive) == 0 ) { if ( !current.name.isEmpty() ) locations << current; inStationData = false; } } qCDebug(runtime) << "TQSL locations count:" << locations.size(); return locations; } LotwUploader::LotwUploader(QObject *parent) : GenericQSOUploader(uploadedFields, parent), LotwBase() { FCT_IDENTIFICATION; } LotwUploader::~LotwUploader() { FCT_IDENTIFICATION; } void LotwUploader::uploadAdif(const QByteArray &data, const QString &location) { FCT_IDENTIFICATION; file.open(); file.write(data); file.flush(); QStringList args; args << "-d" << "-q" << "-u" << file.fileName(); // Pass -l only when the user explicitly selected a location if ( !location.trimmed().isEmpty() ) args << "-l" << location.trimmed(); QProcess *tqslProcess = new QProcess(); connect(tqslProcess, QOverload::of(&QProcess::finished), this, [this, tqslProcess](int exitCode, QProcess::ExitStatus exitStatus) { qCDebug(runtime) << "Process finished with exit code" << exitCode << "and exit status" << exitStatus; /* list of Error Codes: http://www.arrl.org/command-1 */ switch ( exitCode ) { case 0: // Success emit uploadFinished(); break; case 1: // Cancelled by user emit uploadError(tr("Upload cancelled by user")); break; case 2: // Rejected by LoTW emit uploadError(tr("Upload rejected by LoTW")); break; case 3: // Unexpected response from TQSL server emit uploadError(tr("Unexpected response from TQSL server")); break; case 4: // TQSL error emit uploadError(tr("TQSL utility error")); break; case 5: // TQSLlib error emit uploadError(tr("TQSLlib error")); break; case 6: // Unable to open input file emit uploadError(tr("Unable to open input file")); break; case 7: // Unable to open output file emit uploadError(tr("Unable to open output file")); break; case 8: // All QSOs were duplicates or out of date range emit uploadError(tr("All QSOs were duplicates or out of date range")); break; case 9: // Some QSOs were duplicates or out of date range emit uploadError(tr("Some QSOs were duplicates or out of date range")); break; case 10: // Command syntax error emit uploadError(tr("Command syntax error")); break; case 11: // LoTW Connection error (no network or LoTW is unreachable) emit uploadError(tr("LoTW Connection error (no network or LoTW is unreachable)")); break; default: emit uploadError(tr("Unexpected Error from TQSL")); } tqslProcess->deleteLater(); }); connect(tqslProcess, &QProcess::errorOccurred, this, [this, tqslProcess](QProcess::ProcessError error) { qDebug() << "Process error:" << error; switch ( error ) { case QProcess::FailedToStart: // Error code of QProcess::execute - Process cannot start emit uploadError(tr("TQSL not found")); break; case QProcess::Crashed: // Error code of QProcess::execute - Process crashed emit uploadError(tr("TQSL crashed")); break; default: emit uploadError(tr("Unexpected Error from TQSL")); } tqslProcess->deleteLater(); }); connect(tqslProcess, &QProcess::readyReadStandardOutput, this, [tqslProcess]() { qCDebug(runtime)<< "TQSL output: " << qPrintable(tqslProcess->readAllStandardOutput()); }); tqslProcess->setProcessChannelMode(QProcess::MergedChannels); tqslProcess->setReadChannel(QProcess::StandardOutput); qCDebug(runtime) << getTQSLPath("tqsl") << args; tqslProcess->start(getTQSLPath("tqsl"),args); } void LotwUploader::uploadQSOList(const QList &qsos, const QVariantMap &addlParams) { FCT_IDENTIFICATION; QByteArray data = generateADIF(qsos); const QString location = addlParams["tqsl_location"].toString(); uploadAdif(data, location); } LotwQSLDownloader::LotwQSLDownloader(QObject *parent) : GenericQSLDownloader(parent), LotwBase(), currentReply(nullptr) { FCT_IDENTIFICATION; } void LotwQSLDownloader::receiveQSL(const QDate &start_date, bool qso_since, const QString &station_callsign) { FCT_IDENTIFICATION; qCDebug(function_parameters) << start_date << " " << qso_since; QList> params; params.append(qMakePair(QString("qso_query"), QString("1"))); params.append(qMakePair(QString("qso_qsldetail"), QString("yes"))); params.append(qMakePair(QString("qso_owncall"), station_callsign)); const QString &start = start_date.toString("yyyy-MM-dd"); if (qso_since) { params.append(qMakePair(QString("qso_qsl"), QString("no"))); params.append(qMakePair(QString("qso_qsorxsince"), start)); } else { params.append(qMakePair(QString("qso_qsl"), QString("yes"))); params.append(qMakePair(QString("qso_qslsince"), start)); } get(params); } void LotwQSLDownloader::abortDownload() { FCT_IDENTIFICATION; if ( currentReply ) { currentReply->abort(); currentReply = nullptr; } } void LotwQSLDownloader::processReply(QNetworkReply *reply) { FCT_IDENTIFICATION; /* always process one requests per class */ currentReply = nullptr; int replyStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); if (reply->error() != QNetworkReply::NoError || replyStatusCode < 200 || replyStatusCode >= 300) { qCInfo(runtime) << "LotW error" << reply->errorString(); qCDebug(runtime) << "HTTP Status Code" << replyStatusCode; if ( reply->error() != QNetworkReply::OperationCanceledError ) { emit receiveQSLFailed(reply->errorString()); reply->deleteLater(); } return; } qint64 size = reply->size(); qCDebug(runtime) << "Reply received, size: " << size; /* Currently, QT returns an incorrect stream position value in Network stream * when the stream is used in QTextStream. Therefore * QLog downloads a response, saves it to a temp file and opens * the file as a stream */ QTemporaryFile tempFile; if ( ! tempFile.open() ) { qCDebug(runtime) << "Cannot open temp file"; emit receiveQSLFailed(tr("Cannot open temporary file")); return; } const QByteArray &data = reply->readAll(); qCDebug(runtime) << data; /* verify the Username/password incorrect only in case when message is short (10k). * otherwise, it is a long ADIF and it is not necessary to verify login status */ if ( size < 10000 && data.contains("Username/password incorrect") ) { emit receiveQSLFailed(tr("Incorrect login or password")); return; } tempFile.write(data); tempFile.flush(); tempFile.seek(0); emit receiveQSLStarted(); /* see above why QLog uses a temp file */ QTextStream stream(&tempFile); AdiFormat adi(stream); connect(&adi, &AdiFormat::importPosition, this, [this, size](qint64 position) { if ( size > 0 ) { double progress = position * 100.0 / size; emit receiveQSLProgress(static_cast(progress)); } }); connect(&adi, &AdiFormat::QSLMergeFinished, this, [this](QSLMergeStat stats) { emit receiveQSLComplete(stats); }); adi.runQSLImport(adi.LOTW); tempFile.close(); reply->deleteLater(); } void LotwQSLDownloader::get(QList> params) { FCT_IDENTIFICATION; const QString &username = getUsername(); const QString &password = getPasswd(); QUrlQuery query; query.setQueryItems(params); query.addQueryItem("login", username.toUtf8().toPercentEncoding()); query.addQueryItem("password", password.toUtf8().toPercentEncoding()); QUrl url(ADIF_API); url.setQuery(query); qCDebug(runtime) << Data::safeQueryString(query); if ( currentReply ) qCWarning(runtime) << "processing a new request but the previous one hasn't been completed yet !!!"; currentReply = getNetworkAccessManager()->get(QNetworkRequest(url)); } LotwQSLDownloader::~LotwQSLDownloader() { FCT_IDENTIFICATION; if ( currentReply ) { currentReply->abort(); currentReply->deleteLater(); } } ================================================ FILE: service/lotw/Lotw.h ================================================ #ifndef QLOG_SERVISE_LOTW_LOTW_H #define QLOG_SERVISE_LOTW_LOTW_H #include #include #include #include "service/GenericQSOUploader.h" #include "service/GenericQSLDownloader.h" #include "core/CredentialStore.h" class QNetworkAccessManager; struct TQSLVersion { int major = -1; int minor = -1; int patch = -1; bool isValid() const { return major >= 0; } }; struct TQSLStationLocation { QString name; QString callsign; QString grid; }; class LotwBase : public SecureServiceBase { protected: static const QString SECURE_STORAGE_KEY; public: explicit LotwBase() {}; virtual ~LotwBase() {}; DECLARE_SECURE_SERVICE(LotwBase); static const QString getUsername(); static const QString getPasswd(); static const QString getTQSLPath(const QString &defaultPath = QDir::rootPath()); static QString findTQSLPath(); static TQSLVersion getTQSLVersion(const QString &tqslPath = QString()); static QString getTQSLStationDataPath(); static QList getTQSLStationLocations(); static void saveUsernamePassword(const QString&, const QString&); static void saveTQSLPath(const QString&); // Returns the QLog dxcc group ("CW", "PHONE", "DIGITAL") for the given // LoTW generic mode group name, or an empty string for specific mode names. static QString lotwGroupNameToDxcc(const QString &lotwMode) { static const QMap map = { { "DATA", "DIGITAL" }, { "PHONE", "PHONE" }, { "CW", "CW" }, { "IMAGE", "DIGITAL" } }; return map.value(lotwMode.toUpper()); } }; class LotwUploader : public GenericQSOUploader, private LotwBase { Q_OBJECT public: static QStringList uploadedFields; static QVariantMap generateUploadConfigMap(const QString &location) { return QVariantMap({{"tqsl_location", location}}); } explicit LotwUploader(QObject *parent = nullptr); virtual ~LotwUploader(); void uploadAdif(const QByteArray &, const QString &location = QString()); virtual void uploadQSOList(const QList& qsos, const QVariantMap &addlParams) override; public slots: virtual void abortRequest() override {}; private: QTemporaryFile file; virtual void processReply(QNetworkReply*) override {}; }; class LotwQSLDownloader : public GenericQSLDownloader, private LotwBase { Q_OBJECT public: explicit LotwQSLDownloader(QObject *parent = nullptr); virtual ~LotwQSLDownloader(); virtual void receiveQSL(const QDate &, bool, const QString &) override; public slots: virtual void abortDownload() override; private: QNetworkReply *currentReply; const QString ADIF_API = "https://lotw.arrl.org/lotwuser/lotwreport.adi"; virtual void processReply(QNetworkReply* reply) override; void get(QList> params); }; #endif // QLOG_SERVISE_LOTW_LOTW_H ================================================ FILE: service/potaapp/PotaApp.cpp ================================================ #include #include #include "PotaApp.h" #include "core/debug.h" #include "data/Callsign.h" MODULE_IDENTIFICATION("qlog.service.potaapp.potaapp"); PotaAppActivatorDownloader::PotaAppActivatorDownloader(QObject *parent) : QObject(parent), nam(new QNetworkAccessManager(this)) { FCT_IDENTIFICATION; connect(nam, &QNetworkAccessManager::finished, this, &PotaAppActivatorDownloader::processReply); } PotaAppActivatorDownloader::~PotaAppActivatorDownloader() { FCT_IDENTIFICATION; if ( nam ) nam->deleteLater(); } void PotaAppActivatorDownloader::swapActivators(ActivatorStorage &a) { FCT_IDENTIFICATION; QMutexLocker locker(&activatorLock); a.swap(activators); } void PotaAppActivatorDownloader::updateActivators() { FCT_IDENTIFICATION; if ( currReply ) { qCWarning(runtime) << "request is still running"; currReply->abort(); currReply = nullptr; } const QUrl url(API_URL); QNetworkRequest req(url); req.setHeader(QNetworkRequest::UserAgentHeader, "QLog"); currReply = nam->get(req); } void PotaAppActivatorDownloader::processReply(QNetworkReply *reply) { FCT_IDENTIFICATION; currReply = nullptr; if (!reply) return; int replyStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); if ( reply->error() != QNetworkReply::NoError || replyStatusCode < 200 || replyStatusCode >= 300) { qCDebug(runtime) << "POTA API error" << reply->errorString(); qCDebug(runtime) << "HTTP Status Code" << replyStatusCode; reply->deleteLater(); return; } const QByteArray body = reply->readAll(); if ( body.isEmpty() ) return; // Parse JSON array of spots QJsonParseError jerr{}; const QJsonDocument doc = QJsonDocument::fromJson(body, &jerr); if (jerr.error != QJsonParseError::NoError || !doc.isArray()) { qCWarning(runtime) << "POTA JSON Error" << jerr.errorString(); return; } const QJsonArray arr = doc.array(); ActivatorStorage newActivators; for ( const QJsonValue &val : arr ) { if (!val.isObject()) continue; const QJsonObject o = val.toObject(); //qCDebug(runtime) << "Received Spot" << o; if ( o.value("comments").toString().toUpper().contains("QRT") ) { qCDebug(runtime) << "QRT - skipping"; continue; } POTASpot s; s.spotId = static_cast(o.value("spotId").toVariant().toULongLong()); s.activator = o.value("activator").toString().toUpper(); Callsign activator(s.activator); if (activator.isValid()) s.activatorBaseCallsign = activator.getBase(); s.frequency = o.value("frequency").toString().toDouble() / 1000; s.mode = o.value(("mode")).toString(); s.reference = o.value(("reference")).toString().toUpper(); s.parkName = o.value(("parkName")).toString(); s.spotter = o.value(("spotter")).toString().toUpper(); s.comments = o.value(("comments")).toString(); s.source = o.value(("source")).toString(); s.name = o.value(("name")).toString(); s.locationDesc = o.value(("locationDesc")).toString(); const QString st = o.value(("spotTime")).toString(); s.spotTime = QDateTime::fromString(st, Qt::ISODate); #if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) s.spotTime.setTimeZone(QTimeZone::UTC); #else s.spotTime.setTimeSpec(Qt::UTC); #endif if ( !s.activator.isEmpty() && s.frequency > 0.0 && !s.mode.isEmpty() ) { qCDebug(runtime) << "Adding" << s.activator << s.frequency << s.mode << s.reference << s.comments; newActivators.insert(s.activatorBaseCallsign, s); } } // atomic swap activatorLock.lock(); activators.swap(newActivators); activatorLock.unlock(); emit activatorsUpdated(); reply->deleteLater(); } ================================================ FILE: service/potaapp/PotaApp.h ================================================ #ifndef QLOG_SERVICE_POTAAPP_POTAAPP_H #define QLOG_SERVICE_POTAAPP_POTAAPP_H #include #include "data/POTASpot.h" class QNetworkAccessManager; class QNetworkReply; class PotaAppActivatorDownloader : public QObject { Q_OBJECT public: using ActivatorStorage = QMultiHash; explicit PotaAppActivatorDownloader(QObject *parent = nullptr); virtual ~PotaAppActivatorDownloader(); void swapActivators(ActivatorStorage &a); signals: void activatorsUpdated(); public slots: void updateActivators(); private slots: void processReply(QNetworkReply *reply); private: QNetworkAccessManager *nam = nullptr; QNetworkReply *currReply = nullptr; QMutex activatorLock; ActivatorStorage activators; const QString API_URL = "https://api.pota.app/spot/activator"; }; #endif // QLOG_SERVICE_POTAAPP_POTAAPP_H ================================================ FILE: service/qrzcom/QRZ.cpp ================================================ #include #include #include #include #include #include #include #include "QRZ.h" #include #include "core/debug.h" #include "core/CredentialStore.h" #include "data/Callsign.h" #include "core/LogParam.h" #include "data/Data.h" //https://www.qrz.com/docs/logbook/QRZLogbookAPI.html MODULE_IDENTIFICATION("qlog.core.qrz"); const QString QRZBase::SECURE_STORAGE_KEY = "QRZCOM"; const QString QRZBase::SECURE_STORAGE_API_KEY = "QRZCOMAPI"; const QString QRZBase::CONFIG_USERNAME_API_CONST = "logbookapi"; const QString QRZCallbook::CALLBOOK_NAME = "qrzcom"; REGISTRATION_SECURE_SERVICE(QRZBase); const QString QRZBase::getUsername() { FCT_IDENTIFICATION; return LogParam::getQRZCOMCallbookUsername(); } const QString QRZBase::getPasswd(const QString &username) { FCT_IDENTIFICATION; return getPassword(QRZBase::SECURE_STORAGE_KEY, username); } void QRZBase::saveUsernamePassword(const QString &newUsername, const QString &newPassword) { FCT_IDENTIFICATION; const QString &oldUsername = getUsername(); if ( oldUsername != newUsername ) { deletePassword(QRZBase::SECURE_STORAGE_KEY, oldUsername); } LogParam::setQRZCOMCallbookUsername(newUsername); savePassword(QRZBase::SECURE_STORAGE_KEY, newUsername, newPassword); } QString QRZBase::getInternalAPIUsername() { FCT_IDENTIFICATION; return QRZBase::CONFIG_USERNAME_API_CONST; } const QString QRZBase::getLogbookAPIKey(const QString &username) { FCT_IDENTIFICATION; return getPassword(QRZBase::SECURE_STORAGE_API_KEY, username); } void QRZBase::saveLogbookAPIKey(const QString &newKey, const QString &username) { FCT_IDENTIFICATION; deletePassword(QRZBase::SECURE_STORAGE_API_KEY, username); if ( ! newKey.isEmpty() ) savePassword(QRZBase::SECURE_STORAGE_API_KEY, username, newKey); } const QStringList QRZBase::getLogbookAPIAddlCallsigns() { FCT_IDENTIFICATION; return LogParam::getQRZCOMAPICallsignsList(); } void QRZBase::setLogbookAPIAddlCallsigns(const QStringList &list) { FCT_IDENTIFICATION; LogParam::setQRZCOMAPICallsignsList(list); } void QRZBase::registerCredentials() { // both storage keys belong to the same logical service CredentialRegistry::instance().add(SECURE_STORAGE_API_KEY, []() { QList ret; ret.append({ SECURE_STORAGE_KEY, [](){ return getUsername(); }}); ret.append({ SECURE_STORAGE_API_KEY, [](){ return getInternalAPIUsername(); }}); const QStringList &addCallsigns = QRZBase::getLogbookAPIAddlCallsigns(); for ( QString callsign : addCallsigns ) // do not use referece callsign here. ret.append({ SECURE_STORAGE_API_KEY, [callsign]() {return callsign;}}); return ret; }); } QRZCallbook::QRZCallbook(QObject* parent) : GenericCallbook(parent), QRZBase(), incorrectLogin(false), lastSeenPassword(QString()), currentReply(nullptr) { FCT_IDENTIFICATION; } QRZCallbook::~QRZCallbook() { if ( currentReply ) currentReply->abort(); } QString QRZCallbook::getDisplayName() { FCT_IDENTIFICATION; return QString(tr("QRZ.com")); } void QRZCallbook::queryCallsign(const QString &callsign) { FCT_IDENTIFICATION; qCDebug(function_parameters)<< callsign; if (sessionId.isEmpty()) { queuedCallsign = callsign; authenticate(); return; } QUrlQuery params; params.addQueryItem("s", sessionId); const Callsign qCall(callsign); // currently QRZ.com does not handle correctly prefixes and suffixes. // That's why it's better to give it away if possible params.addQueryItem("callsign", (qCall.isValid()) ? qCall.getBase() : callsign); QUrl url(API_URL); if ( currentReply ) qCWarning(runtime) << "processing a new request but the previous one hasn't been completed yet !!!"; QNetworkRequest request(url); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); qCDebug(runtime) << url; currentReply = getNetworkAccessManager()->post(request, params.query(QUrl::FullyEncoded).toUtf8()); currentReply->setProperty("queryCallsign", QVariant(callsign)); currentReply->setProperty("messageType", QVariant("callsignInfoQuery")); // Attention, variable callsign and queuedCallsign point to the same object // queuedCallsign must be cleared after the last use of the callsign variable queuedCallsign = QString(); } void QRZCallbook::abortQuery() { FCT_IDENTIFICATION; if ( currentReply ) { currentReply->abort(); //currentReply->deleteLater(); // pointer is deleted later in processReply currentReply = nullptr; } } void QRZCallbook::authenticate() { FCT_IDENTIFICATION; const QString &username = getUsername(); const QString &password = getPasswd(username); if ( incorrectLogin && password == lastSeenPassword) { /* User already knows that login failed */ emit callsignNotFound(queuedCallsign); queuedCallsign = QString(); return; } if ( !username.isEmpty() && !password.isEmpty() ) { QUrlQuery params; params.addQueryItem("username", username.toUtf8().toPercentEncoding()); params.addQueryItem("password", password.toUtf8().toPercentEncoding()); params.addQueryItem("agent", "QLog"); QUrl url(API_URL); if ( currentReply ) qCWarning(runtime) << "processing a new request but the previous one hasn't been completed yet !!!"; QNetworkRequest request(url); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); currentReply = getNetworkAccessManager()->post(request, params.query(QUrl::FullyEncoded).toUtf8()); currentReply->setProperty("messageType", QVariant("authenticate")); lastSeenPassword = password; } else { emit callsignNotFound(queuedCallsign); qCDebug(runtime) << "Empty username or password"; } } void QRZCallbook::processReply(QNetworkReply* reply) { FCT_IDENTIFICATION; /* always process one requests per class */ currentReply = nullptr; int replyStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); if ( reply->error() != QNetworkReply::NoError || replyStatusCode < 200 || replyStatusCode >= 300) { qCDebug(runtime) << "QRZ.com error URL " << reply->request().url().toString(); qCDebug(runtime) << "QRZ.com error" << reply->errorString(); qCDebug(runtime) << "HTTP Status Code" << replyStatusCode; if ( reply->error() != QNetworkReply::OperationCanceledError ) { emit lookupError(reply->errorString()); reply->deleteLater(); } return; } const QString &messageType = reply->property("messageType").toString(); qCDebug(runtime) << "Received Message Type: " << messageType; /*********************/ /* callsignInfoQuery */ /*********************/ if ( messageType == "callsignInfoQuery" || messageType == "authenticate" ) { const QByteArray &response = reply->readAll(); qCDebug(runtime) << response; QXmlStreamReader xml(response); CallbookResponseData resposeData; /* Reset Session Key */ /* Every response contains a valid key. If the key is not present */ /* then it is needed to request a new one */ sessionId = QString(); while ( !xml.atEnd() && !xml.hasError() ) { QXmlStreamReader::TokenType token = xml.readNext(); if (token != QXmlStreamReader::StartElement) continue; const QString elementName = xml.name().toString(); if ( elementName == "Error" ) { queuedCallsign = QString(); QString errorString = xml.readElementText(); if ( errorString.contains("Username/password incorrect")) { incorrectLogin = true; emit loginFailed(); emit lookupError(errorString); return; } else if ( errorString.contains("Not found:") ) { incorrectLogin = false; emit callsignNotFound(reply->property("queryCallsign").toString()); //return; } else { qInfo() << "QRZ Error - " << errorString; emit lookupError(errorString); } // do not call return here, we need to obtain Key from error message (if present) } else incorrectLogin = false; if (elementName == "Key") sessionId = xml.readElementText(); else if (elementName == "call") resposeData.call = decodeHtmlEntities(xml.readElementText().toUpper()); else if (elementName == "dxcc") resposeData.dxcc = decodeHtmlEntities(xml.readElementText()); else if (elementName == "fname") resposeData.fname = decodeHtmlEntities(xml.readElementText()); else if (elementName == "name") resposeData.lname = decodeHtmlEntities(xml.readElementText()); else if (elementName == "addr1") resposeData.addr1 = decodeHtmlEntities(xml.readElementText()); else if (elementName == "addr2") resposeData.qth = decodeHtmlEntities(xml.readElementText()); else if (elementName == "state") resposeData.us_state = decodeHtmlEntities(xml.readElementText()); else if (elementName == "zip") resposeData.zipcode = decodeHtmlEntities(xml.readElementText()); else if (elementName == "country") resposeData.country = decodeHtmlEntities(xml.readElementText()); else if (elementName == "lat") resposeData.latitude = decodeHtmlEntities(xml.readElementText()); else if (elementName == "lon") resposeData.longitude = decodeHtmlEntities(xml.readElementText()); else if (elementName == "county") resposeData.county = decodeHtmlEntities(xml.readElementText()); else if (elementName == "grid") resposeData.gridsquare = decodeHtmlEntities(xml.readElementText().toUpper()); else if (elementName == "efdate") resposeData.lic_year = decodeHtmlEntities(xml.readElementText()); else if (elementName == "qslmgr") resposeData.qsl_via = decodeHtmlEntities(xml.readElementText()); else if (elementName == "email") resposeData.email = decodeHtmlEntities(xml.readElementText()); else if (elementName == "GMTOffset") resposeData.utc_offset = decodeHtmlEntities(xml.readElementText()); else if (elementName == "eqsl") resposeData.eqsl = (xml.readElementText() == "1") ? "Y" : "N"; else if (elementName == "mqsl") resposeData.pqsl = decodeHtmlEntities(xml.readElementText()); else if (elementName == "cqzone") resposeData.cqz = decodeHtmlEntities(xml.readElementText()); else if (elementName == "ituzone") resposeData.ituz = decodeHtmlEntities(xml.readElementText()); else if (elementName == "born") resposeData.born = decodeHtmlEntities(xml.readElementText()); else if (elementName == "lotw") resposeData.lotw = (xml.readElementText() == "1") ? "Y" : "N"; else if (elementName == "iota") resposeData.iota = decodeHtmlEntities(xml.readElementText()); else if (elementName == "nickname") resposeData.nick = decodeHtmlEntities(xml.readElementText()); else if (elementName == "url") resposeData.url = decodeHtmlEntities(xml.readElementText()); else if (elementName == "name_fmt") resposeData.name_fmt = decodeHtmlEntities(xml.readElementText()); else if (elementName == "image") resposeData.image_url = decodeHtmlEntities(xml.readElementText()); } if (!resposeData.call.isEmpty()) emit callsignResult(resposeData); if (!queuedCallsign.isEmpty()) queryCallsign(queuedCallsign); } else reply->deleteLater(); } /* https://www.qrz.com/docs/logbook/QRZLogbookAPI.html */ /* ??? QRZ Support all ADIF Fields ??? */ QRZUploader::QRZUploader(QObject *parent) : GenericQSOUploader(QStringList(), parent), QRZBase(), currentReply(nullptr), cancelUpload(false) { FCT_IDENTIFICATION; } QRZUploader::~QRZUploader() { FCT_IDENTIFICATION; if ( currentReply ) { currentReply->abort(); currentReply->deleteLater(); } } void QRZUploader::uploadContact(const QSqlRecord &record) { FCT_IDENTIFICATION; //qCDebug(function_parameters) << record; QByteArray data = generateADIF({record}); cancelUpload = false; QString stationCallsign = record.value("station_callsign").toString(); if ( stationCallsign.isEmpty() ) stationCallsign = record.value("operator").toString(); qCDebug(runtime) << "Using station Callsign" << stationCallsign; const QString &logbookAPIKey = (addlCallsign.contains(stationCallsign)) ? getLogbookAPIKey(stationCallsign) : getLogbookAPIKey(getInternalAPIUsername()); actionInsert(logbookAPIKey, data, "REPLACE"); currentReply->setProperty("contactID", record.value("id")); } void QRZUploader::uploadQSOList(const QList& qsos, const QVariantMap &) { FCT_IDENTIFICATION; if ( qsos.isEmpty() ) { /* Nothing to do */ emit uploadFinished(); return; } cancelUpload = false; queuedContacts4Upload = qsos; addlCallsign.clear(); addlCallsign = QRZBase::getLogbookAPIAddlCallsigns(); uploadContact(queuedContacts4Upload.first()); queuedContacts4Upload.removeFirst(); } void QRZUploader::actionInsert(const QString &logbookAPIKey, QByteArray& data, const QString &insertPolicy) { FCT_IDENTIFICATION; QUrlQuery params; params.addQueryItem("KEY", logbookAPIKey); params.addQueryItem("ACTION", "INSERT"); params.addQueryItem("OPTION", insertPolicy); params.addQueryItem("ADIF", data.trimmed().toPercentEncoding()); QUrl url(API_LOGBOOK_URL); QNetworkRequest request(url); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); QString rheader = QString("QLog/%1").arg(VERSION); request.setRawHeader("User-Agent", rheader.toUtf8()); qCDebug(runtime) << Data::safeQueryString(params); if ( currentReply ) qCWarning(runtime) << "processing a new request but the previous one hasn't been completed yet !!!"; currentReply = getNetworkAccessManager()->post(request, params.query(QUrl::FullyEncoded).toUtf8()); currentReply->setProperty("messageType", QVariant("actionsInsert")); } void QRZUploader::abortRequest() { FCT_IDENTIFICATION; cancelUpload = true; if ( currentReply ) { currentReply->abort(); currentReply = nullptr; } } void QRZUploader::processReply(QNetworkReply *reply) { FCT_IDENTIFICATION; /* always process one requests per class */ currentReply = nullptr; int replyStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); if ( reply->error() != QNetworkReply::NoError || replyStatusCode < 200 || replyStatusCode >= 300) { qCDebug(runtime) << "QRZ.com error URL " << reply->request().url().toString(); qCDebug(runtime) << "QRZ.com error" << reply->errorString(); qCDebug(runtime) << "HTTP Status Code" << replyStatusCode; if ( reply->error() != QNetworkReply::OperationCanceledError ) { emit uploadError(reply->errorString()); reply->deleteLater(); } cancelUpload = true; return; } const QString &messageType = reply->property("messageType").toString(); qCDebug(runtime) << "Received Message Type: " << messageType; /*****************/ /* actionsInsert */ /*****************/ if ( messageType == "actionsInsert") { const QString replayString(reply->readAll()); qCDebug(runtime) << replayString; const QMap &data = parseActionResponse(replayString); const QString &status = data.value("RESULT", "FAILED"); if ( status == "OK" || status == "REPLACE" ) { qCDebug(runtime) << "Confirmed Upload for QSO Id " << reply->property("contactID").toULongLong(); emit uploadedQSO(reply->property("contactID").toULongLong()); if ( queuedContacts4Upload.isEmpty() ) { cancelUpload = false; emit uploadFinished(); } else { if ( ! cancelUpload ) { uploadContact(queuedContacts4Upload.first()); queuedContacts4Upload.removeFirst(); } } } else { emit uploadError(data.value("REASON", tr("General Error"))); cancelUpload = false; } } reply->deleteLater(); } QMap QRZUploader::parseActionResponse(const QString &responseString) const { FCT_IDENTIFICATION; qCDebug(function_parameters) << responseString; QMap data; const QStringList &parsedResponse = responseString.split("&"); for ( const QString ¶m : parsedResponse ) { QStringList parsedParams; parsedParams << param.split("="); if ( parsedParams.count() == 1 ) data[parsedParams.at(0)] = QString(); else if ( parsedParams.count() >= 2 ) data[parsedParams.at(0)] = parsedParams.at(1); } return data; } ================================================ FILE: service/qrzcom/QRZ.h ================================================ #ifndef QLOG_SERVICE_QRZ_QRZ_H #define QLOG_SERVICE_QRZ_QRZ_H #include #include #include #include "service/GenericCallbook.h" #include "service/GenericQSOUploader.h" #include "core/CredentialStore.h" class QNetworkAccessManager; class QNetworkReply; class QRZBase : public SecureServiceBase { protected: const static QString SECURE_STORAGE_KEY; const static QString SECURE_STORAGE_API_KEY; const static QString CONFIG_USERNAME_API_CONST; public: explicit QRZBase() {}; virtual ~QRZBase() {}; DECLARE_SECURE_SERVICE(QRZBase); static const QString getUsername(); static const QString getPasswd(const QString &username); static void saveUsernamePassword(const QString&, const QString&); static QString getInternalAPIUsername(); static const QString getLogbookAPIKey(const QString& username); static const QStringList getLogbookAPIAddlCallsigns(); static void setLogbookAPIAddlCallsigns(const QStringList &list); static void saveLogbookAPIKey(const QString& newKey, const QString &username); }; class QRZCallbook : public GenericCallbook, private QRZBase { Q_OBJECT public: const static QString CALLBOOK_NAME; explicit QRZCallbook(QObject *parent = nullptr); virtual ~QRZCallbook(); QString getDisplayName() override; public slots: virtual void queryCallsign(const QString &callsign) override; virtual void abortQuery() override; protected: virtual void processReply(QNetworkReply* reply) override; private: QString sessionId; QString queuedCallsign; bool incorrectLogin; QString lastSeenPassword; QNetworkReply *currentReply; const QString API_URL = "https://xmldata.qrz.com/xml/current/"; void authenticate(); }; class QRZUploader : public GenericQSOUploader, private QRZBase { Q_OBJECT public: explicit QRZUploader(QObject *parent = nullptr); virtual ~QRZUploader(); void uploadContact(const QSqlRecord &record); virtual void uploadQSOList(const QList& qsos, const QVariantMap &addlParams) override; public slots: virtual void abortRequest() override; protected: virtual void processReply(QNetworkReply* reply) override; private: QNetworkReply *currentReply; QList queuedContacts4Upload; QStringList addlCallsign; bool cancelUpload; const QString API_LOGBOOK_URL = "https://logbook.qrz.com/api"; void actionInsert(const QString &logbookAPIKey, QByteArray& data, const QString &insertPolicy); QMap parseActionResponse(const QString&) const; }; #endif // QLOG_SERVICE_QRZ_QRZ_H ================================================ FILE: tests/AlertEvaluatorTest/AlertEvaluatorTest.pro ================================================ QT += testlib core sql network widgets CONFIG += console testcase c++11 TEMPLATE = app TARGET = tst_alertevaluator INCLUDEPATH += $$PWD/../.. SOURCES += \ tst_alertevaluator.cpp \ ../../core/AlertEvaluator.cpp \ ../../data/BandPlan.cpp HEADERS += \ ../../core/AlertEvaluator.h \ ../../data/DxSpot.h \ ../../data/WsjtxEntry.h \ ../../data/SpotAlert.h \ ../../data/BandPlan.h ================================================ FILE: tests/AlertEvaluatorTest/tst_alertevaluator.cpp ================================================ #include #include #include #define private public #include "core/AlertEvaluator.h" #undef private #include "data/SpotAlert.h" #include "data/WsjtxEntry.h" #include "data/DxSpot.h" ClubInfo::ClubInfo(const QString &callsign, const QString &ID, const QDate &validFrom, const QDate &validTo, const QString &club) : callsign(callsign), id(ID), validFrom(validFrom), validTo(validTo), club(club) { } const QString& ClubInfo::getCallsign() const { return callsign; } const QString& ClubInfo::getID() const { return id; } const QDate& ClubInfo::getValidFrom() const { return validFrom; } const QDate& ClubInfo::getValidTo() const { return validTo; } const QString& ClubInfo::getClubInfo() const { return club; } struct RuleSpec { int source = SpotAlert::WSJTXCQSPOT; bool enabled = true; int dxLogStatusMap = DxccStatus::Worked; QString mode = QStringLiteral("*"); QString band = QStringLiteral("*"); int dxCountry = 0; int ituz = 0; int cqz = 0; bool pota = false; bool sota = false; bool iota = false; bool wwff = false; QString dxContinent = QStringLiteral("*"); int spotterCountry = 0; QString spotterContinent = QStringLiteral("*"); QStringList dxMember = {QStringLiteral("*")}; QString callsignRe = QStringLiteral(".*"); QString commentRe = QStringLiteral(".*"); }; struct WsjtxSpec { QString callsign = QStringLiteral("OK1TEST"); QString message = QStringLiteral("CQ TEST"); QString decodedMode = QStringLiteral("FT8"); QString band = QStringLiteral("20m"); int dxcc = 123; int ituz = 28; int cqz = 15; QString cont = QStringLiteral("EU"); int spotterDxcc = 45; QString spotterCont = QStringLiteral("EU"); int status = DxccStatus::Worked; bool containsPOTA = false; bool containsSOTA = false; bool containsIOTA = false; bool containsWWFF = false; QStringList members; }; struct DxSpotSpec { QString callsign = QStringLiteral("OK1TEST"); QString comment = QStringLiteral("CQ TEST"); QString modeGroupString = QStringLiteral("DIGITAL"); QString band = QStringLiteral("20m"); int dxcc = 123; int ituz = 28; int cqz = 15; QString cont = QStringLiteral("EU"); int spotterDxcc = 45; QString spotterCont = QStringLiteral("EU"); int status = DxccStatus::Worked; bool containsPOTA = false; bool containsSOTA = false; bool containsIOTA = false; bool containsWWFF = false; QStringList members; }; Q_DECLARE_METATYPE(RuleSpec) Q_DECLARE_METATYPE(WsjtxSpec) Q_DECLARE_METATYPE(DxSpotSpec) namespace { std::unique_ptr makeRule(const RuleSpec &spec, int source) { std::unique_ptr rule(new AlertRule()); rule->ruleName = QStringLiteral("rule"); rule->enabled = spec.enabled; rule->sourceMap = source; rule->dxCountry = spec.dxCountry; rule->dxLogStatusMap = spec.dxLogStatusMap; rule->dxContinent = spec.dxContinent; rule->dxComment = spec.commentRe; rule->dxMember = spec.dxMember; rule->dxMemberSet = QSet(spec.dxMember.begin(), spec.dxMember.end()); rule->mode = spec.mode; rule->band = spec.band; rule->spotterCountry = spec.spotterCountry; rule->spotterContinent = spec.spotterContinent; rule->ituz = spec.ituz; rule->cqz = spec.cqz; rule->pota = spec.pota; rule->sota = spec.sota; rule->iota = spec.iota; rule->wwff = spec.wwff; rule->ruleValid = true; rule->callsignRE.setPattern(spec.callsignRe); rule->callsignRE.setPatternOptions(QRegularExpression::CaseInsensitiveOption); rule->commentRE.setPattern(spec.commentRe); rule->commentRE.setPatternOptions(QRegularExpression::CaseInsensitiveOption); return rule; } WsjtxEntry makeWsjtxEntry(const WsjtxSpec &spec) { WsjtxEntry entry; entry.callsign = spec.callsign; entry.decode.message = spec.message; entry.decodedMode = spec.decodedMode; entry.band = spec.band; entry.dxcc.dxcc = spec.dxcc; entry.dxcc.ituz = spec.ituz; entry.dxcc.cqz = spec.cqz; entry.dxcc.cont = spec.cont; entry.dxcc_spotter.dxcc = spec.spotterDxcc; entry.dxcc_spotter.cont = spec.spotterCont; entry.status = static_cast(spec.status); entry.containsPOTA = spec.containsPOTA; entry.containsSOTA = spec.containsSOTA; entry.containsIOTA = spec.containsIOTA; entry.containsWWFF = spec.containsWWFF; for (const QString &member : spec.members) { entry.callsign_member.append(ClubInfo(entry.callsign, QString(), QDate(), QDate(), member)); } return entry; } DxSpot makeDxSpot(const DxSpotSpec &spec) { DxSpot spot; spot.callsign = spec.callsign; spot.comment = spec.comment; spot.modeGroupString = spec.modeGroupString; spot.band = spec.band; spot.dxcc.dxcc = spec.dxcc; spot.dxcc.ituz = spec.ituz; spot.dxcc.cqz = spec.cqz; spot.dxcc.cont = spec.cont; spot.dxcc_spotter.dxcc = spec.spotterDxcc; spot.dxcc_spotter.cont = spec.spotterCont; spot.status = static_cast(spec.status); spot.containsPOTA = spec.containsPOTA; spot.containsSOTA = spec.containsSOTA; spot.containsIOTA = spec.containsIOTA; spot.containsWWFF = spec.containsWWFF; for (const QString &member : spec.members) { spot.callsign_member.append(ClubInfo(spot.callsign, QString(), QDate(), QDate(), member)); } return spot; } } class AlertEvaluatorTest : public QObject { Q_OBJECT private slots: void initTestCase(); void match_wsjtx_data(); void match_wsjtx(); void match_dxspot_data(); void match_dxspot(); void cross_valid_data(); void cross_valid(); void cross_valid2_data(); void cross_valid2(); }; void AlertEvaluatorTest::initTestCase() { QLoggingCategory::setFilterRules(QStringLiteral("*.debug=false")); } void AlertEvaluatorTest::match_wsjtx_data() { QTest::addColumn("rule"); QTest::addColumn("input"); QTest::addColumn("expected"); /* Rule is disabled */ /* Test DXCC STATUS */ for (int bit = DxccStatus::NewEntity; bit <= DxccStatus::UnknownStatus; bit <<= 1) { RuleSpec rule; rule.enabled = false; rule.dxLogStatusMap = static_cast(bit); for (int bit2 = DxccStatus::NewEntity; bit2 <= DxccStatus::UnknownStatus; bit2 <<= 1) { WsjtxSpec input; input.status = static_cast(bit2); QTest::addRow("wsjtx_disabled_%d_%d", bit, bit2) << rule << input << false; } } /* Rule is enable */ /* Test DXCC STATUS */ for (int bit = DxccStatus::NewEntity; bit <= DxccStatus::UnknownStatus; bit <<= 1) { RuleSpec rule; rule.dxLogStatusMap = static_cast(bit); for (int bit2 = DxccStatus::NewEntity; bit2 <= DxccStatus::UnknownStatus; bit2 <<= 1) { WsjtxSpec input; input.status = static_cast(bit2); QTest::addRow("wsjtx_DXstatus_%d_%d", bit, bit2) << rule << input << (bit == bit2); } } /* Test Country */ for (int x = 0; x <= 10; x++) { RuleSpec rule; rule.dxCountry = x; for (int y = 0; y <= 10; y++) { WsjtxSpec input; input.dxcc = y; bool result = (x == 0) ? true : (x == y); // country == 0 ->no filter; QTest::addRow("wsjtx_DXCountry_%d_%d", x, y) << rule << input << result; } } /* Test Country */ for (int x = 0; x <= 10; x++) { RuleSpec rule; rule.spotterCountry = x; for (int y = 0; y <= 10; y++) { WsjtxSpec input; input.spotterDxcc = y; bool result = (x == 0) ? true : (x == y); // country == 0 ->no filter; QTest::addRow("wsjtx_SpotterCountry_%d_%d", x, y) << rule << input << result; } } /* Test ITU */ for (int x = 0; x <= 10; x++) { RuleSpec rule; rule.ituz = x; for (int y = 0; y <= 10; y++) { WsjtxSpec input; input.ituz = y; bool result = (x == 0) ? true : (x == y); // country == 0 ->no filter; QTest::addRow("wsjtx_ITU_%d_%d", x, y) << rule << input << result; } } /* Test ITU */ for (int x = 0; x <= 10; x++) { RuleSpec rule; rule.cqz = x; for (int y = 0; y <= 10; y++) { WsjtxSpec input; input.cqz = y; bool result = (x == 0) ? true : (x == y); // country == 0 ->no filter; QTest::addRow("wsjtx_CQZ_%d_%d", x, y) << rule << input << result; } } /* POTA/SOTA/IOTA/WWFF setting */ for (int ruleMask = 0; ruleMask < 16; ++ruleMask) { for (int inputMask = 0; inputMask < 16; ++inputMask) { RuleSpec rule; rule.pota = (ruleMask & 1) != 0; rule.sota = (ruleMask & 2) != 0; rule.iota = (ruleMask & 4) != 0; rule.wwff = (ruleMask & 8) != 0; WsjtxSpec in; in.containsPOTA = (inputMask & 1) != 0; in.containsSOTA = (inputMask & 2) != 0; in.containsIOTA = (inputMask & 4) != 0; in.containsWWFF = (inputMask & 8) != 0; const bool expected = (ruleMask == 0) ? true : ((inputMask & ruleMask) != 0); QTest::newRow( QString("wsjtx_ref_rule_0x%1_input_0x%2") .arg(ruleMask, 2, 16, QLatin1Char('0')) .arg(inputMask, 2, 16, QLatin1Char('0')) .toUtf8().constData() ) << rule << in << expected; } } /* Callsign match */ { RuleSpec rule; rule.callsignRe = "OK2T"; WsjtxSpec in; QTest::newRow(QString("wsjtx_callsign_%1_%2").arg(rule.callsignRe, in.callsign).toUtf8().constData() ) << rule << in << false; } { RuleSpec rule; rule.callsignRe = "OK1TE"; WsjtxSpec in; QTest::newRow(QString("wsjtx_callsign_%1_%2").arg(rule.callsignRe, in.callsign).toUtf8().constData() ) << rule << in << true; } { RuleSpec rule; rule.callsignRe = "OK2T.*"; WsjtxSpec in; QTest::newRow(QString("wsjtx_callsign_%1_%2").arg(rule.callsignRe, in.callsign).toUtf8().constData() ) << rule << in << false; } { RuleSpec rule; rule.callsignRe = "OK1TE.*"; WsjtxSpec in; QTest::newRow(QString("wsjtx_callsign_%1_%2").arg(rule.callsignRe, in.callsign).toUtf8().constData() ) << rule << in << true; } { RuleSpec rule; rule.callsignRe = "OK1TEST"; WsjtxSpec in; QTest::newRow(QString("wsjtx_callsign_%1_%2").arg(rule.callsignRe, in.callsign).toUtf8().constData() ) << rule << in << true; } { RuleSpec rule; rule.callsignRe = "OK1TEST"; WsjtxSpec in; in.callsign="OK1TEST/P"; QTest::newRow(QString("wsjtx_callsign_%1_%2").arg(rule.callsignRe, in.callsign).toUtf8().constData() ) << rule << in << true; } { RuleSpec rule; rule.callsignRe = "OK1TEST.*"; WsjtxSpec in; in.callsign="OK1TEST/P"; QTest::newRow(QString("wsjtx_callsign_%1_%2").arg(rule.callsignRe, in.callsign).toUtf8().constData() ) << rule << in << true; } { RuleSpec rule; rule.callsignRe = ".*TEST"; WsjtxSpec in; QTest::newRow(QString("wsjtx_callsign_%1_%2").arg(rule.callsignRe, in.callsign).toUtf8().constData() ) << rule << in << true; } /* DX Continent */ { QStringList conts = {"NA", "SA", "EU", "AF", "OC", "AS", "AN"}; for ( const QString &c : conts ) { RuleSpec rule; rule.dxContinent = "|" + c; for ( const QString &c1: conts ) { WsjtxSpec in; in.cont = c1; QTest::newRow(QString("wsjtx_continents_%1_%2").arg(rule.dxContinent, in.cont).toUtf8().constData() ) << rule << in << (rule.dxContinent.contains(in.cont)); } } } { RuleSpec rule; rule.dxContinent = "|EU|NA"; WsjtxSpec in; in.cont = "NA"; QTest::newRow(QString("wsjtx_continents_%1_%2").arg(rule.dxContinent, in.cont).toUtf8().constData() ) << rule << in << true; } { RuleSpec rule; rule.dxContinent = "|EU|NA"; WsjtxSpec in; in.cont = "OC"; QTest::newRow(QString("wsjtx_continents_%1_%2").arg(rule.dxContinent, in.cont).toUtf8().constData() ) << rule << in << false; } /* Spotter Continent */ { QStringList conts = {"NA", "SA", "EU", "AF", "OC", "AS", "AN"}; for ( const QString &c : conts ) { RuleSpec rule; rule.spotterContinent = "|" + c; for ( const QString &c1: conts ) { WsjtxSpec in; in.spotterCont = c1; QTest::newRow(QString("wsjtx_spottercontinents_%1_%2").arg(rule.spotterContinent, in.spotterCont).toUtf8().constData() ) << rule << in << (rule.spotterContinent.contains(in.spotterCont)); } } } { RuleSpec rule; rule.spotterContinent = "|EU|NA"; WsjtxSpec in; in.cont = "NA"; QTest::newRow(QString("wsjtx_spottercontinents_%1_%2").arg(rule.spotterContinent, in.spotterCont).toUtf8().constData() ) << rule << in << true; } { RuleSpec rule; rule.spotterContinent = "|EU|NA"; WsjtxSpec in; in.spotterCont = "OC"; QTest::newRow(QString("wsjtx_spottercontinents_%1_%2").arg(rule.spotterContinent, in.spotterCont).toUtf8().constData() ) << rule << in << false; } // message { RuleSpec rule; rule.commentRe = "TEST"; WsjtxSpec in; QTest::newRow(QString("wsjtx_comment_%1_%2").arg(rule.commentRe, in.message).toUtf8().constData() ) << rule << in << true; } { RuleSpec rule; rule.commentRe = "^TEST"; WsjtxSpec in; QTest::newRow(QString("wsjtx_comment_%1_%2").arg(rule.commentRe, in.message).toUtf8().constData() ) << rule << in << false; } { RuleSpec rule; rule.commentRe = ".*TEST.*"; WsjtxSpec in; QTest::newRow(QString("wsjtx_comment_%1_%2").arg(rule.commentRe, in.message).toUtf8().constData() ) << rule << in << true; } { RuleSpec rule; rule.commentRe = ".*TAST.*"; WsjtxSpec in; QTest::newRow(QString("wsjtx_comment_%1_%2").arg(rule.commentRe, in.message).toUtf8().constData() ) << rule << in << false; } { RuleSpec rule; rule.commentRe = "C.*TEST.*"; WsjtxSpec in; QTest::newRow(QString("wsjtx_comment_%1_%2").arg(rule.commentRe, in.message).toUtf8().constData() ) << rule << in << true; } // mode { RuleSpec rule; rule.mode = "|FTx"; WsjtxSpec in; QTest::newRow(QString("wsjtx_mode_%1_%2").arg(rule.mode, in.decodedMode).toUtf8().constData() ) << rule << in << true; } { RuleSpec rule; rule.mode = "|FTx"; WsjtxSpec in; in.decodedMode = "FT4"; QTest::newRow(QString("wsjtx_mode_%1_%2").arg(rule.mode, in.decodedMode).toUtf8().constData() ) << rule << in << true; } { RuleSpec rule; rule.mode = "|FTx"; WsjtxSpec in; in.decodedMode = "FT2"; QTest::newRow(QString("wsjtx_mode_%1_%2").arg(rule.mode, in.decodedMode).toUtf8().constData() ) << rule << in << true; } { RuleSpec rule; rule.mode = "|DIGITAL"; WsjtxSpec in; in.decodedMode = "FT8"; QTest::newRow(QString("wsjtx_mode_%1_%2").arg(rule.mode, in.decodedMode).toUtf8().constData() ) << rule << in << false; } { RuleSpec rule; rule.mode = "|DIGITAL"; WsjtxSpec in; in.decodedMode = "FT4"; QTest::newRow(QString("wsjtx_mode_%1_%2").arg(rule.mode, in.decodedMode).toUtf8().constData() ) << rule << in << false; } { RuleSpec rule; rule.mode = "|DIGITAL"; WsjtxSpec in; in.decodedMode = "FT2"; QTest::newRow(QString("wsjtx_mode_%1_%2").arg(rule.mode, in.decodedMode).toUtf8().constData() ) << rule << in << false; } { RuleSpec rule; rule.mode = "|DIGITAL"; WsjtxSpec in; in.decodedMode = "DIGITAL"; QTest::newRow(QString("wsjtx_mode_%1_%2").arg(rule.mode, in.decodedMode).toUtf8().constData() ) << rule << in << true; } // members { QStringList m1 = {"A1", "A2", "B1"}; QStringList m2 = {"B2"}; RuleSpec rule; rule.dxMember = m1; WsjtxSpec in; in.members = m2; QTest::newRow("wsjtx_members_notmatch") << rule << in << false; } { QStringList m1 = {"A1", "A2", "B1"}; QStringList m2 = {"B2", "B1"}; RuleSpec rule; rule.dxMember = m1; WsjtxSpec in; in.members = m2; QTest::newRow("wsjtx_members_match") << rule << in << true; } { QStringList m1 = {"B1"}; QStringList m2 = {"B1", "B2"}; RuleSpec rule; rule.dxMember = m1; WsjtxSpec in; in.members = m2; QTest::newRow("wsjtx_members_match2") << rule << in << true; } // band { RuleSpec rule; rule.band = "|20m|60m"; WsjtxSpec in; QTest::newRow(QString("wsjtx_band_%1_%2").arg(rule.band, in.band).toUtf8().constData()) << rule << in << true; } { RuleSpec rule; rule.band = "|60m"; WsjtxSpec in; QTest::newRow(QString("wsjtx_band_%1_%2").arg(rule.band, in.band).toUtf8().constData()) << rule << in << false; } { RuleSpec rule; rule.band = "|120m"; WsjtxSpec in; QTest::newRow(QString("wsjtx_band_%1_%2").arg(rule.band, in.band).toUtf8().constData()) << rule << in << false; } { RuleSpec rule; rule.band = "|10m|20m|60m|80m|"; WsjtxSpec in; QTest::newRow(QString("wsjtx_band_%1_%2").arg(rule.band, in.band).toUtf8().constData()) << rule << in << true; } } void AlertEvaluatorTest::match_wsjtx() { QFETCH(RuleSpec, rule); QFETCH(WsjtxSpec, input); QFETCH(bool, expected); auto alertRule = makeRule(rule, SpotAlert::WSJTXCQSPOT); WsjtxEntry entry = makeWsjtxEntry(input); QCOMPARE(alertRule->match(entry), expected); } void AlertEvaluatorTest::match_dxspot_data() { QTest::addColumn("rule"); QTest::addColumn("input"); QTest::addColumn("expected"); /* Rule is disabled */ /* Test DXCC STATUS */ for (int bit = DxccStatus::NewEntity; bit <= DxccStatus::UnknownStatus; bit <<= 1) { RuleSpec rule; rule.enabled = false; rule.dxLogStatusMap = static_cast(bit); for (int bit2 = DxccStatus::NewEntity; bit2 <= DxccStatus::UnknownStatus; bit2 <<= 1) { DxSpotSpec input; input.status = static_cast(bit2); QTest::addRow("dxspot_disabled_%d_%d", bit, bit2) << rule << input << false; } } /* Rule is enable */ /* Test DXCC STATUS */ for (int bit = DxccStatus::NewEntity; bit <= DxccStatus::UnknownStatus; bit <<= 1) { RuleSpec rule; rule.dxLogStatusMap = static_cast(bit); for (int bit2 = DxccStatus::NewEntity; bit2 <= DxccStatus::UnknownStatus; bit2 <<= 1) { DxSpotSpec input; input.status = static_cast(bit2); QTest::addRow("dxspot_DXstatus_%d_%d", bit, bit2) << rule << input << (bit == bit2); } } /* Test Country */ for (int x = 0; x <= 10; x++) { RuleSpec rule; rule.dxCountry = x; for (int y = 0; y <= 10; y++) { DxSpotSpec input; input.dxcc = y; bool result = (x == 0) ? true : (x == y); // country == 0 ->no filter; QTest::addRow("dxspot_DXCountry_%d_%d", x, y) << rule << input << result; } } /* Test Country */ for (int x = 0; x <= 10; x++) { RuleSpec rule; rule.spotterCountry = x; for (int y = 0; y <= 10; y++) { DxSpotSpec input; input.spotterDxcc = y; bool result = (x == 0) ? true : (x == y); // country == 0 ->no filter; QTest::addRow("dxspot_SpotterCountry_%d_%d", x, y) << rule << input << result; } } /* Test ITU */ for (int x = 0; x <= 10; x++) { RuleSpec rule; rule.ituz = x; for (int y = 0; y <= 10; y++) { DxSpotSpec input; input.ituz = y; bool result = (x == 0) ? true : (x == y); // country == 0 ->no filter; QTest::addRow("dxspot_ITU_%d_%d", x, y) << rule << input << result; } } /* Test ITU */ for (int x = 0; x <= 10; x++) { RuleSpec rule; rule.cqz = x; for (int y = 0; y <= 10; y++) { DxSpotSpec input; input.cqz = y; bool result = (x == 0) ? true : (x == y); // country == 0 ->no filter; QTest::addRow("dxspot_CQZ_%d_%d", x, y) << rule << input << result; } } /* POTA/SOTA/IOTA/WWFF setting */ for (int ruleMask = 0; ruleMask < 16; ++ruleMask) { for (int inputMask = 0; inputMask < 16; ++inputMask) { RuleSpec rule; rule.pota = (ruleMask & 1) != 0; rule.sota = (ruleMask & 2) != 0; rule.iota = (ruleMask & 4) != 0; rule.wwff = (ruleMask & 8) != 0; DxSpotSpec in; in.containsPOTA = (inputMask & 1) != 0; in.containsSOTA = (inputMask & 2) != 0; in.containsIOTA = (inputMask & 4) != 0; in.containsWWFF = (inputMask & 8) != 0; const bool expected = (ruleMask == 0) ? true : ((inputMask & ruleMask) != 0); QTest::newRow( QString("dxspot_ref_rule_0x%1_input_0x%2") .arg(ruleMask, 2, 16, QLatin1Char('0')) .arg(inputMask, 2, 16, QLatin1Char('0')) .toUtf8().constData() ) << rule << in << expected; } } /* Callsign match */ { RuleSpec rule; rule.callsignRe = "OK2T"; DxSpotSpec in; QTest::newRow(QString("dxspot_callsign_%1_%2").arg(rule.callsignRe, in.callsign).toUtf8().constData() ) << rule << in << false; } { RuleSpec rule; rule.callsignRe = "OK1TE"; DxSpotSpec in; QTest::newRow(QString("dxspot_callsign_%1_%2").arg(rule.callsignRe, in.callsign).toUtf8().constData() ) << rule << in << true; } { RuleSpec rule; rule.callsignRe = "OK2T.*"; DxSpotSpec in; QTest::newRow(QString("dxspot_callsign_%1_%2").arg(rule.callsignRe, in.callsign).toUtf8().constData() ) << rule << in << false; } { RuleSpec rule; rule.callsignRe = "OK1TE.*"; DxSpotSpec in; QTest::newRow(QString("dxspot_callsign_%1_%2").arg(rule.callsignRe, in.callsign).toUtf8().constData() ) << rule << in << true; } { RuleSpec rule; rule.callsignRe = "OK1TEST"; DxSpotSpec in; QTest::newRow(QString("dxspot_callsign_%1_%2").arg(rule.callsignRe, in.callsign).toUtf8().constData() ) << rule << in << true; } { RuleSpec rule; rule.callsignRe = "OK1TEST"; DxSpotSpec in; in.callsign="OK1TEST/P"; QTest::newRow(QString("dxspot_callsign_%1_%2").arg(rule.callsignRe, in.callsign).toUtf8().constData() ) << rule << in << true; } { RuleSpec rule; rule.callsignRe = "OK1TEST.*"; DxSpotSpec in; in.callsign="OK1TEST/P"; QTest::newRow(QString("dxspot_callsign_%1_%2").arg(rule.callsignRe, in.callsign).toUtf8().constData() ) << rule << in << true; } { RuleSpec rule; rule.callsignRe = ".*TEST"; DxSpotSpec in; QTest::newRow(QString("dxspot_callsign_%1_%2").arg(rule.callsignRe, in.callsign).toUtf8().constData() ) << rule << in << true; } /* DX Continent */ { QStringList conts = {"NA", "SA", "EU", "AF", "OC", "AS", "AN"}; for ( const QString &c : conts ) { RuleSpec rule; rule.dxContinent = "|" + c; for ( const QString &c1: conts ) { DxSpotSpec in; in.cont = c1; QTest::newRow(QString("dxspot_continents_%1_%2").arg(rule.dxContinent, in.cont).toUtf8().constData() ) << rule << in << (rule.dxContinent.contains(in.cont)); } } } { RuleSpec rule; rule.dxContinent = "|EU|NA"; DxSpotSpec in; in.cont = "NA"; QTest::newRow(QString("dxspot_continents_%1_%2").arg(rule.dxContinent, in.cont).toUtf8().constData() ) << rule << in << true; } { RuleSpec rule; rule.dxContinent = "|EU|NA"; DxSpotSpec in; in.cont = "OC"; QTest::newRow(QString("dxspot_continents_%1_%2").arg(rule.dxContinent, in.cont).toUtf8().constData() ) << rule << in << false; } /* Spotter Continent */ { QStringList conts = {"NA", "SA", "EU", "AF", "OC", "AS", "AN"}; for ( const QString &c : conts ) { RuleSpec rule; rule.spotterContinent = "|" + c; for ( const QString &c1: conts ) { DxSpotSpec in; in.spotterCont = c1; QTest::newRow(QString("dxspot_spottercontinents_%1_%2").arg(rule.spotterContinent, in.spotterCont).toUtf8().constData() ) << rule << in << (rule.spotterContinent.contains(in.spotterCont)); } } } { RuleSpec rule; rule.spotterContinent = "|EU|NA"; DxSpotSpec in; in.spotterCont = "NA"; QTest::newRow(QString("dxspot_spottercontinents_%1_%2").arg(rule.spotterContinent, in.spotterCont).toUtf8().constData() ) << rule << in << true; } { RuleSpec rule; rule.spotterContinent = "|EU|NA"; DxSpotSpec in; in.spotterCont = "OC"; QTest::newRow(QString("dxspot_spottercontinents_%1_%2").arg(rule.spotterContinent, in.spotterCont).toUtf8().constData() ) << rule << in << false; } // comment { RuleSpec rule; rule.commentRe = "TEST"; DxSpotSpec in; QTest::newRow(QString("dxspot_comment_%1_%2").arg(rule.commentRe, in.comment).toUtf8().constData() ) << rule << in << true; } { RuleSpec rule; rule.commentRe = "^TEST"; DxSpotSpec in; QTest::newRow(QString("dxspot_comment_%1_%2").arg(rule.commentRe, in.comment).toUtf8().constData() ) << rule << in << false; } { RuleSpec rule; rule.commentRe = ".*TEST.*"; DxSpotSpec in; QTest::newRow(QString("dxspot_comment_%1_%2").arg(rule.commentRe, in.comment).toUtf8().constData() ) << rule << in << true; } { RuleSpec rule; rule.commentRe = ".*TAST.*"; DxSpotSpec in; QTest::newRow(QString("dxspot_comment_%1_%2").arg(rule.commentRe, in.comment).toUtf8().constData() ) << rule << in << false; } { RuleSpec rule; rule.commentRe = "C.*TEST.*"; DxSpotSpec in; QTest::newRow(QString("dxspot_comment_%1_%2").arg(rule.commentRe, in.comment).toUtf8().constData() ) << rule << in << true; } // mode { RuleSpec rule; rule.mode = "|FTx"; DxSpotSpec in; in.modeGroupString = "FTx"; QTest::newRow(QString("dxspot_mode_%1_%2").arg(rule.mode, in.modeGroupString).toUtf8().constData() ) << rule << in << true; } { // FT4 spot: at a FT4 frequency the band plan assigns modeGroupString "FTx" RuleSpec rule; rule.mode = "|FTx"; DxSpotSpec in; in.modeGroupString = "FTx"; QTest::newRow("dxspot_mode_|FTx_FT4") << rule << in << true; } { // FT2 spot: at a FT2 frequency the band plan assigns modeGroupString "FTx" RuleSpec rule; rule.mode = "|FTx"; DxSpotSpec in; in.modeGroupString = "FTx"; QTest::newRow("dxspot_mode_|FTx_FT2") << rule << in << true; } { RuleSpec rule; rule.mode = "|DIGITAL"; DxSpotSpec in; in.modeGroupString = "FTx"; QTest::newRow(QString("dxspot_mode_%1_%2").arg(rule.mode, in.modeGroupString).toUtf8().constData() ) << rule << in << false; } { // FT4 spot (modeGroupString "FTx") does not match DIGITAL filter RuleSpec rule; rule.mode = "|DIGITAL"; DxSpotSpec in; in.modeGroupString = "FTx"; QTest::newRow("dxspot_mode_|DIGITAL_FT4") << rule << in << false; } { // FT2 spot (modeGroupString "FTx") does not match DIGITAL filter RuleSpec rule; rule.mode = "|DIGITAL"; DxSpotSpec in; in.modeGroupString = "FTx"; QTest::newRow("dxspot_mode_|DIGITAL_FT2") << rule << in << false; } { RuleSpec rule; rule.mode = "|DIGITAL"; DxSpotSpec in; in.modeGroupString = "DIGITAL"; QTest::newRow(QString("dxspot_mode_%1_%2").arg(rule.mode, in.modeGroupString).toUtf8().constData() ) << rule << in << true; } // members { QStringList m1 = {"A1", "A2", "B1"}; QStringList m2 = {"B2"}; RuleSpec rule; rule.dxMember = m1; DxSpotSpec in; in.members = m2; QTest::newRow("dxspot_members_notmatch") << rule << in << false; } { QStringList m1 = {"A1", "A2", "B1"}; QStringList m2 = {"B2", "B1"}; RuleSpec rule; rule.dxMember = m1; DxSpotSpec in; in.members = m2; QTest::newRow("dxspot_members_match") << rule << in << true; } { QStringList m1 = {"B1"}; QStringList m2 = {"B1", "B2"}; RuleSpec rule; rule.dxMember = m1; DxSpotSpec in; in.members = m2; QTest::newRow("dxspot_members_match2") << rule << in << true; } // band { RuleSpec rule; rule.band = "|20m|60m"; DxSpotSpec in; QTest::newRow(QString("dxspot_band_%1_%2").arg(rule.band, in.band).toUtf8().constData()) << rule << in << true; } { RuleSpec rule; rule.band = "|60m"; DxSpotSpec in; QTest::newRow(QString("dxspot_band_%1_%2").arg(rule.band, in.band).toUtf8().constData()) << rule << in << false; } { RuleSpec rule; rule.band = "|120m"; DxSpotSpec in; QTest::newRow(QString("dxspot_band_%1_%2").arg(rule.band, in.band).toUtf8().constData()) << rule << in << false; } { RuleSpec rule; rule.band = "|10m|20m|60m|80m|"; DxSpotSpec in; QTest::newRow(QString("dxspot_band_%1_%2").arg(rule.band, in.band).toUtf8().constData()) << rule << in << true; } } void AlertEvaluatorTest::match_dxspot() { QFETCH(RuleSpec, rule); QFETCH(DxSpotSpec, input); QFETCH(bool, expected); auto alertRule = makeRule(rule, SpotAlert::DXSPOT); DxSpot spot = makeDxSpot(input); QCOMPARE(alertRule->match(spot), expected); } void AlertEvaluatorTest::cross_valid_data() { QTest::addColumn("rule"); QTest::addColumn("input"); QTest::addColumn("expected"); { RuleSpec rule; DxSpotSpec in; QTest::newRow("Cross") << rule << in << false; } } void AlertEvaluatorTest::cross_valid() { QFETCH(RuleSpec, rule); QFETCH(DxSpotSpec, input); QFETCH(bool, expected); auto alertRule = makeRule(rule, SpotAlert::WSJTXCQSPOT); DxSpot spot = makeDxSpot(input); QCOMPARE(alertRule->match(spot), expected); } void AlertEvaluatorTest::cross_valid2_data() { QTest::addColumn("rule"); QTest::addColumn("input"); QTest::addColumn("expected"); { RuleSpec rule; WsjtxSpec in; QTest::newRow("Cross2") << rule << in << false; } } void AlertEvaluatorTest::cross_valid2() { QFETCH(RuleSpec, rule); QFETCH(WsjtxSpec, input); QFETCH(bool, expected); auto alertRule = makeRule(rule, SpotAlert::DXSPOT); WsjtxEntry spot = makeWsjtxEntry(input); QCOMPARE(alertRule->match(spot), expected); } QTEST_APPLESS_MAIN(AlertEvaluatorTest) #include "tst_alertevaluator.moc" ================================================ FILE: tests/BandPlanTest/BandPlanTest.pro ================================================ QT += testlib core sql CONFIG += console testcase c++11 TEMPLATE = app TARGET = tst_bandplan INCLUDEPATH += $$PWD/../.. SOURCES += \ tst_bandplan.cpp \ ../../data/BandPlan.cpp HEADERS += \ ../../data/BandPlan.h \ ../../data/Band.h ================================================ FILE: tests/BandPlanTest/tst_bandplan.cpp ================================================ #include #include #include #include #include "data/BandPlan.h" namespace { QString lastErrorString(const QSqlQuery &query) { return query.lastError().isValid() ? query.lastError().text() : QString(); } } class BandPlanTest : public QObject { Q_OBJECT private slots: void initTestCase(); void cleanupTestCase(); void freq2BandMode_data(); void freq2BandMode(); void freq2BandModeGroupString_data(); void freq2BandModeGroupString(); void freq2ExpectedMode_data(); void freq2ExpectedMode(); void freq2Band_data(); void freq2Band(); void bandsList_onlyDXCC(); void modeToDXCCModeGroup_data(); void modeToDXCCModeGroup(); void isFTxMode_data(); void isFTxMode(); void isFTxBandMode_data(); void isFTxBandMode(); }; void BandPlanTest::initTestCase() { QLoggingCategory::setFilterRules(QStringLiteral("*.debug=false")); qRegisterMetaType("BandPlan::BandPlanMode"); QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE"); db.setDatabaseName(":memory:"); QVERIFY(db.open()); QSqlQuery createBands; QVERIFY2(createBands.exec("CREATE TABLE bands (" "id INTEGER PRIMARY KEY AUTOINCREMENT," "name TEXT UNIQUE NOT NULL," "start_freq FLOAT," "end_freq FLOAT," "enabled BOOLEAN," "sat_designator TEXT)"), qPrintable(lastErrorString(createBands))); struct BandRow { const char *name; double start; double end; int enabled; }; const BandRow bandRows[] = { {"2190m", 0.134, 0.140, 1}, {"630m", 0.470, 0.490, 1}, {"160m", 1.800, 2.000, 1}, {"80m", 3.500, 4.000, 1}, {"60m", 5.350, 5.450, 1}, {"40m", 7.000, 7.300, 1}, {"30m", 10.100, 10.150, 1}, {"20m", 14.000, 14.350, 1}, {"17m", 18.068, 18.168, 1}, {"15m", 21.000, 21.450, 1}, {"12m", 24.890, 24.990, 1}, {"10m", 28.000, 29.700, 1}, {"6m", 50.000, 54.000, 1}, {"4m", 70.000, 71.000, 1}, {"2m", 144.000, 148.000, 1}, {"1.25m", 222.000, 225.000, 1}, {"70cm", 420.000, 450.000, 1}, {"33cm", 902.000, 928.000, 1}, {"23cm", 1240.000, 1300.000, 1}, {"13cm", 2300.000, 2450.000, 1}, {"3cm", 10000.000, 11000.000, 1} }; QSqlQuery insertBand; QVERIFY2(insertBand.prepare("INSERT INTO bands " "(name, start_freq, end_freq, enabled, sat_designator) " "VALUES (?, ?, ?, ?, ?)"), qPrintable(lastErrorString(insertBand))); for (const BandRow &row : bandRows) { insertBand.bindValue(0, QString::fromLatin1(row.name)); insertBand.bindValue(1, row.start); insertBand.bindValue(2, row.end); insertBand.bindValue(3, row.enabled); insertBand.bindValue(4, QString()); QVERIFY2(insertBand.exec(), qPrintable(lastErrorString(insertBand))); } QSqlQuery createModes; QVERIFY2(createModes.exec("CREATE TABLE modes (" "name TEXT PRIMARY KEY," "dxcc TEXT NOT NULL)"), qPrintable(lastErrorString(createModes))); struct ModeRow { const char *name; const char *dxcc; }; const ModeRow modeRows[] = { {"CW", "CW"}, {"SSB", "PHONE"}, {"FT8", "DIGITAL"}, {"FT4", "DIGITAL"}, {"FT2", "DIGITAL"}, {"RTTY", "DIGITAL"} }; QSqlQuery insertMode; QVERIFY2(insertMode.prepare("INSERT INTO modes (name, dxcc) VALUES (?, ?)"), qPrintable(lastErrorString(insertMode))); for (const ModeRow &row : modeRows) { insertMode.bindValue(0, QString::fromLatin1(row.name)); insertMode.bindValue(1, QString::fromLatin1(row.dxcc)); QVERIFY2(insertMode.exec(), qPrintable(lastErrorString(insertMode))); } } void BandPlanTest::cleanupTestCase() { const QString connectionName = QString::fromLatin1(QSqlDatabase::defaultConnection); { QSqlDatabase db = QSqlDatabase::database(); if (db.isValid()) { db.close(); } } if (QSqlDatabase::contains(connectionName)) { QSqlDatabase::removeDatabase(connectionName); } } void BandPlanTest::freq2BandMode_data() { QTest::addColumn("frequency"); QTest::addColumn("expectedMode"); QTest::newRow("cw") << 14.0 << BandPlan::BAND_MODE_CW; QTest::newRow("digital") << 14.071 << BandPlan::BAND_MODE_DIGITAL; QTest::newRow("ft8") << 14.074 << BandPlan::BAND_MODE_FT8; QTest::newRow("usb") << 14.200 << BandPlan::BAND_MODE_USB; QTest::newRow("out_of_band") << 1.0 << BandPlan::BAND_MODE_PHONE; QTest::newRow("negative") << -1.0 << BandPlan::BAND_MODE_PHONE; } void BandPlanTest::freq2BandMode() { QFETCH(double, frequency); QFETCH(BandPlan::BandPlanMode, expectedMode); QCOMPARE(BandPlan::freq2BandMode(frequency), expectedMode); } void BandPlanTest::freq2BandModeGroupString_data() { QTest::addColumn("frequency"); QTest::addColumn("expectedGroup"); QTest::newRow("cw") << 14.0 << QStringLiteral("CW"); QTest::newRow("digital") << 14.071 << QStringLiteral("DIGITAL"); QTest::newRow("ft8") << 14.074 << QStringLiteral("FTx"); QTest::newRow("phone") << 14.200 << QStringLiteral("PHONE"); QTest::newRow("out_of_band") << 1.0 << QStringLiteral("PHONE"); QTest::newRow("negative") << -1.0 << QStringLiteral("PHONE"); } void BandPlanTest::freq2BandModeGroupString() { QFETCH(double, frequency); QFETCH(QString, expectedGroup); QCOMPARE(BandPlan::freq2BandModeGroupString(frequency), expectedGroup); } void BandPlanTest::freq2ExpectedMode_data() { QTest::addColumn("frequency"); QTest::addColumn("expectedMode"); QTest::addColumn("expectedSubmode"); QTest::newRow("cw") << 14.0 << QStringLiteral("CW") << QString(); QTest::newRow("digital_usb") << 14.071 << QStringLiteral("SSB") << QStringLiteral("USB"); QTest::newRow("ft8") << 14.074 << QStringLiteral("FT8") << QString(); QTest::newRow("ft4") << 14.081 << QStringLiteral("MFSK") << QStringLiteral("FT4"); QTest::newRow("usb_voice") << 14.200 << QStringLiteral("SSB") << QStringLiteral("USB"); } void BandPlanTest::freq2ExpectedMode() { QFETCH(double, frequency); QFETCH(QString, expectedMode); QFETCH(QString, expectedSubmode); QString submode; QCOMPARE(BandPlan::freq2ExpectedMode(frequency, submode), expectedMode); QCOMPARE(submode, expectedSubmode); } void BandPlanTest::freq2Band_data() { QTest::addColumn("frequency"); QTest::addColumn("expectedBand"); const struct Case { double freq; const char *name; } cases[] = { {0.136, "2190m"}, {0.473, "630m"}, {1.9, "160m"}, {3.6, "80m"}, {5.36, "60m"}, {7.1, "40m"}, {10.11, "30m"}, {14.2, "20m"}, {18.1, "17m"}, {21.1, "15m"}, {24.9, "12m"}, {28.1, "10m"}, {50.1, "6m"}, {70.1, "4m"}, {144.1, "2m"}, {222.1, "1.25m"}, {430.1, "70cm"}, {902.1, "33cm"}, {1240.1, "23cm"} }; for (const Case &c : cases) { QTest::newRow(QByteArray::number(c.freq).constData()) << c.freq << QString::fromLatin1(c.name); } } void BandPlanTest::freq2Band() { QFETCH(double, frequency); QFETCH(QString, expectedBand); const Band band = BandPlan::freq2Band(frequency); QCOMPARE(band.name, expectedBand); } void BandPlanTest::bandsList_onlyDXCC() { const QList bands = BandPlan::bandsList(true); QStringList bandNames; for (const Band &band : bands) { bandNames << band.name; } const QStringList expected = { QStringLiteral("160m"), QStringLiteral("80m"), QStringLiteral("40m"), QStringLiteral("30m"), QStringLiteral("20m"), QStringLiteral("17m"), QStringLiteral("15m"), QStringLiteral("12m"), QStringLiteral("10m"), QStringLiteral("6m"), QStringLiteral("2m"), QStringLiteral("70cm"), QStringLiteral("23cm"), QStringLiteral("13cm"), QStringLiteral("3cm") }; QCOMPARE(bandNames, expected); } void BandPlanTest::modeToDXCCModeGroup_data() { QTest::addColumn("mode"); QTest::addColumn("expectedGroup"); QTest::newRow("ssb") << QStringLiteral("SSB") << QStringLiteral("PHONE"); QTest::newRow("cw") << QStringLiteral("CW") << QStringLiteral("CW"); QTest::newRow("ft8") << QStringLiteral("FT8") << QStringLiteral("DIGITAL"); QTest::newRow("ft4") << QStringLiteral("FT4") << QStringLiteral("DIGITAL"); QTest::newRow("ft2") << QStringLiteral("FT2") << QStringLiteral("DIGITAL"); QTest::newRow("rtty") << QStringLiteral("RTTY") << QStringLiteral("DIGITAL"); } void BandPlanTest::modeToDXCCModeGroup() { QFETCH(QString, mode); QFETCH(QString, expectedGroup); QCOMPARE(BandPlan::modeToDXCCModeGroup(mode), expectedGroup); } void BandPlanTest::isFTxMode_data() { QTest::addColumn("mode"); QTest::addColumn("expected"); QTest::newRow("ft8") << QStringLiteral("FT8") << true; QTest::newRow("ft4") << QStringLiteral("FT4") << true; QTest::newRow("ft2") << QStringLiteral("FT2") << true; QTest::newRow("cw") << QStringLiteral("CW") << false; QTest::newRow("rtty") << QStringLiteral("RTTY") << false; QTest::newRow("ft") << QStringLiteral("FT") << false; } void BandPlanTest::isFTxMode() { QFETCH(QString, mode); QFETCH(bool, expected); QCOMPARE(BandPlan::isFTxMode(mode), expected); } void BandPlanTest::isFTxBandMode_data() { QTest::addColumn("mode"); QTest::addColumn("expected"); QTest::newRow("ft8") << BandPlan::BAND_MODE_FT8 << true; QTest::newRow("ft4") << BandPlan::BAND_MODE_FT4 << true; QTest::newRow("ft2") << BandPlan::BAND_MODE_FT2 << true; QTest::newRow("cw") << BandPlan::BAND_MODE_CW << false; QTest::newRow("digital") << BandPlan::BAND_MODE_DIGITAL << false; QTest::newRow("phone") << BandPlan::BAND_MODE_PHONE << false; } void BandPlanTest::isFTxBandMode() { QFETCH(BandPlan::BandPlanMode, mode); QFETCH(bool, expected); QCOMPARE(BandPlan::isFTxBandMode(mode), expected); } QTEST_MAIN(BandPlanTest) #include "tst_bandplan.moc" ================================================ FILE: tests/CallsignTest/CallsignTest.pro ================================================ QT += testlib core CONFIG += console testcase c++11 TEMPLATE = app TARGET = tst_callsign INCLUDEPATH += $$PWD/../.. SOURCES += \ tst_callsign.cpp \ ../../data/Callsign.cpp HEADERS += \ ../../data/Callsign.h \ generated_cases.h ================================================ FILE: tests/CallsignTest/generated_cases.h ================================================ #pragma once struct ParseCase { const char *name; const char *callsign; bool valid; const char *normalized; const char *host; const char *hostDelim; const char *base; const char *basePrefix; const char *basePrefixNumber; const char *suffix; const char *suffixDelim; const char *wpx; }; static const ParseCase kParseCases[] = { {"Callsign empty", "", false, "", "", "", "", "", "", "", "", ""}, {"Callsign ABC", "ABC", false, "", "", "", "", "", "", "", "", ""}, {"Callsign ABC/1", "ABC/1", false, "", "", "", "", "", "", "", "", ""}, {"Callsign ABC/P", "ABC/P", false, "", "", "", "", "", "", "", "", ""}, {"Callsign 1/ABC", "1/ABC", false, "", "", "", "", "", "", "", "", ""}, {"Callsign P/ABC", "P/ABC", false, "", "", "", "", "", "", "", "", ""}, {"Callsign #ABC", "#ABC", false, "", "", "", "", "", "", "", "", ""}, {"Callsign ABC$", "ABC$", false, "", "", "", "", "", "", "", "", ""}, {"Callsign ABC AB", "ABC AB", false, "", "", "", "", "", "", "", "", ""}, {"Callsign 12345", "12345", false, "", "", "", "", "", "", "", "", ""}, {"Callsign INVALID", "INVALID", false, "", "", "", "", "", "", "", "", ""}, {"Callsign ABC@123.AA", "ABC@123.AA", false, "", "", "", "", "", "", "", "", ""}, {"Callsign ABC12AA", "ABC12AA", false, "", "", "", "", "", "", "", "", ""}, {"Callsign AB1234", "AB1234", false, "", "", "", "", "", "", "", "", ""}, {"Callsign OK1ABC", "OK1ABC", true, "OK1ABC", "", "", "OK1ABC", "OK", "1", "", "", "OK1"}, {"Callsign OK10ABC", "OK10ABC", true, "OK10ABC", "", "", "OK10ABC", "OK", "10", "", "", "OK10"}, {"Callsign OK100ABC", "OK100ABC", true, "OK100ABC", "", "", "OK100ABC", "OK", "100", "", "", "OK100"}, {"Callsign OK2ABC", "OK2ABC", true, "OK2ABC", "", "", "OK2ABC", "OK", "2", "", "", "OK2"}, {"Callsign 9A1ABC", "9A1ABC", true, "9A1ABC", "", "", "9A1ABC", "9A", "1", "", "", "9A1"}, {"Callsign 9A12ABC", "9A12ABC", true, "9A12ABC", "", "", "9A12ABC", "9A", "12", "", "", "9A12"}, {"Callsign A12ABC", "A12ABC", true, "A12ABC", "", "", "A12ABC", "A1", "2", "", "", "A12"}, {"Callsign OK1abc", "OK1abc", true, "OK1ABC", "", "", "OK1ABC", "OK", "1", "", "", "OK1"}, {"Callsign ok10ABC", "ok10ABC", true, "OK10ABC", "", "", "OK10ABC", "OK", "10", "", "", "OK10"}, {"Callsign ok100abc", "ok100abc", true, "OK100ABC", "", "", "OK100ABC", "OK", "100", "", "", "OK100"}, {"Callsign oK2ABc", "oK2ABc", true, "OK2ABC", "", "", "OK2ABC", "OK", "2", "", "", "OK2"}, {"Callsign 9a1ABC", "9a1ABC", true, "9A1ABC", "", "", "9A1ABC", "9A", "1", "", "", "9A1"}, {"Callsign 9A12AbC", "9A12AbC", true, "9A12ABC", "", "", "9A12ABC", "9A", "12", "", "", "9A12"}, {"Callsign A12ABc", "A12ABc", true, "A12ABC", "", "", "A12ABC", "A1", "2", "", "", "A12"}, {"Callsign OK1ABC/P", "OK1ABC/P", true, "OK1ABC/P", "", "", "OK1ABC", "OK", "1", "P", "/P", "OK1"}, {"Callsign OK1ABC/p", "OK1ABC/p", true, "OK1ABC/P", "", "", "OK1ABC", "OK", "1", "P", "/P", "OK1"}, {"Callsign OK1aBC/p", "OK1aBC/p", true, "OK1ABC/P", "", "", "OK1ABC", "OK", "1", "P", "/P", "OK1"}, {"Callsign OK10ABC/P", "OK10ABC/P", true, "OK10ABC/P", "", "", "OK10ABC", "OK", "10", "P", "/P", "OK10"}, {"Callsign OK100ABC/P", "OK100ABC/P", true, "OK100ABC/P", "", "", "OK100ABC", "OK", "100", "P", "/P", "OK100"}, {"Callsign OK2ABC/P", "OK2ABC/P", true, "OK2ABC/P", "", "", "OK2ABC", "OK", "2", "P", "/P", "OK2"}, {"Callsign 9A1ABC/P", "9A1ABC/P", true, "9A1ABC/P", "", "", "9A1ABC", "9A", "1", "P", "/P", "9A1"}, {"Callsign 9A12ABC/P", "9A12ABC/P", true, "9A12ABC/P", "", "", "9A12ABC", "9A", "12", "P", "/P", "9A12"}, {"Callsign A12ABC/P", "A12ABC/P", true, "A12ABC/P", "", "", "A12ABC", "A1", "2", "P", "/P", "A12"}, {"Callsign OK1ABC/A", "OK1ABC/A", true, "OK1ABC/A", "", "", "OK1ABC", "OK", "1", "A", "/A", "OK1"}, {"Callsign OK1ABC/a", "OK1ABC/a", true, "OK1ABC/A", "", "", "OK1ABC", "OK", "1", "A", "/A", "OK1"}, {"Callsign OK1aBC/a", "OK1aBC/a", true, "OK1ABC/A", "", "", "OK1ABC", "OK", "1", "A", "/A", "OK1"}, {"Callsign OK10ABC/A", "OK10ABC/A", true, "OK10ABC/A", "", "", "OK10ABC", "OK", "10", "A", "/A", "OK10"}, {"Callsign OK100ABC/A", "OK100ABC/A", true, "OK100ABC/A", "", "", "OK100ABC", "OK", "100", "A", "/A", "OK100"}, {"Callsign OK2ABC/A", "OK2ABC/A", true, "OK2ABC/A", "", "", "OK2ABC", "OK", "2", "A", "/A", "OK2"}, {"Callsign 9A1ABC/A", "9A1ABC/A", true, "9A1ABC/A", "", "", "9A1ABC", "9A", "1", "A", "/A", "9A1"}, {"Callsign 9A12ABC/A", "9A12ABC/A", true, "9A12ABC/A", "", "", "9A12ABC", "9A", "12", "A", "/A", "9A12"}, {"Callsign A12ABC/A", "A12ABC/A", true, "A12ABC/A", "", "", "A12ABC", "A1", "2", "A", "/A", "A12"}, {"Callsign OK1ABC/AM", "OK1ABC/AM", true, "OK1ABC/AM", "", "", "OK1ABC", "OK", "1", "AM", "/AM", "OK1"}, {"Callsign OK1ABC/AM", "OK1ABC/AM", true, "OK1ABC/AM", "", "", "OK1ABC", "OK", "1", "AM", "/AM", "OK1"}, {"Callsign OK1aBC/AM", "OK1aBC/AM", true, "OK1ABC/AM", "", "", "OK1ABC", "OK", "1", "AM", "/AM", "OK1"}, {"Callsign OK10ABC/AM", "OK10ABC/AM", true, "OK10ABC/AM", "", "", "OK10ABC", "OK", "10", "AM", "/AM", "OK10"}, {"Callsign OK100ABC/AM", "OK100ABC/AM", true, "OK100ABC/AM", "", "", "OK100ABC", "OK", "100", "AM", "/AM", "OK100"}, {"Callsign OK2ABC/AM", "OK2ABC/AM", true, "OK2ABC/AM", "", "", "OK2ABC", "OK", "2", "AM", "/AM", "OK2"}, {"Callsign 9A1ABC/AM", "9A1ABC/AM", true, "9A1ABC/AM", "", "", "9A1ABC", "9A", "1", "AM", "/AM", "9A1"}, {"Callsign 9A12ABC/AM", "9A12ABC/AM", true, "9A12ABC/AM", "", "", "9A12ABC", "9A", "12", "AM", "/AM", "9A12"}, {"Callsign A12ABC/AM", "A12ABC/AM", true, "A12ABC/AM", "", "", "A12ABC", "A1", "2", "AM", "/AM", "A12"}, {"Callsign OK1ABC/M", "OK1ABC/M", true, "OK1ABC/M", "", "", "OK1ABC", "OK", "1", "M", "/M", "OK1"}, {"Callsign OK1ABC/M", "OK1ABC/M", true, "OK1ABC/M", "", "", "OK1ABC", "OK", "1", "M", "/M", "OK1"}, {"Callsign OK1aBC/M", "OK1aBC/M", true, "OK1ABC/M", "", "", "OK1ABC", "OK", "1", "M", "/M", "OK1"}, {"Callsign OK10ABC/M", "OK10ABC/M", true, "OK10ABC/M", "", "", "OK10ABC", "OK", "10", "M", "/M", "OK10"}, {"Callsign OK100ABC/M", "OK100ABC/M", true, "OK100ABC/M", "", "", "OK100ABC", "OK", "100", "M", "/M", "OK100"}, {"Callsign OK2ABC/M", "OK2ABC/M", true, "OK2ABC/M", "", "", "OK2ABC", "OK", "2", "M", "/M", "OK2"}, {"Callsign 9A1ABC/M", "9A1ABC/M", true, "9A1ABC/M", "", "", "9A1ABC", "9A", "1", "M", "/M", "9A1"}, {"Callsign 9A12ABC/M", "9A12ABC/M", true, "9A12ABC/M", "", "", "9A12ABC", "9A", "12", "M", "/M", "9A12"}, {"Callsign A12ABC/M", "A12ABC/M", true, "A12ABC/M", "", "", "A12ABC", "A1", "2", "M", "/M", "A12"}, {"Callsign OK1ABC/MM", "OK1ABC/MM", true, "OK1ABC/MM", "", "", "OK1ABC", "OK", "1", "MM", "/MM", "OK1"}, {"Callsign OK1ABC/MM", "OK1ABC/MM", true, "OK1ABC/MM", "", "", "OK1ABC", "OK", "1", "MM", "/MM", "OK1"}, {"Callsign OK1aBC/MM", "OK1aBC/MM", true, "OK1ABC/MM", "", "", "OK1ABC", "OK", "1", "MM", "/MM", "OK1"}, {"Callsign OK10ABC/MM", "OK10ABC/MM", true, "OK10ABC/MM", "", "", "OK10ABC", "OK", "10", "MM", "/MM", "OK10"}, {"Callsign OK100ABC/MM", "OK100ABC/MM", true, "OK100ABC/MM", "", "", "OK100ABC", "OK", "100", "MM", "/MM", "OK100"}, {"Callsign OK2ABC/MM", "OK2ABC/MM", true, "OK2ABC/MM", "", "", "OK2ABC", "OK", "2", "MM", "/MM", "OK2"}, {"Callsign 9A1ABC/MM", "9A1ABC/MM", true, "9A1ABC/MM", "", "", "9A1ABC", "9A", "1", "MM", "/MM", "9A1"}, {"Callsign 9A12ABC/MM", "9A12ABC/MM", true, "9A12ABC/MM", "", "", "9A12ABC", "9A", "12", "MM", "/MM", "9A12"}, {"Callsign A12ABC/MM", "A12ABC/MM", true, "A12ABC/MM", "", "", "A12ABC", "A1", "2", "MM", "/MM", "A12"}, {"Callsign OK1ABC/P", "OK1ABC/P", true, "OK1ABC/P", "", "", "OK1ABC", "OK", "1", "P", "/P", "OK1"}, {"Callsign OK1ABC/P", "OK1ABC/P", true, "OK1ABC/P", "", "", "OK1ABC", "OK", "1", "P", "/P", "OK1"}, {"Callsign OK1aBC/P", "OK1aBC/P", true, "OK1ABC/P", "", "", "OK1ABC", "OK", "1", "P", "/P", "OK1"}, {"Callsign OK10ABC/P", "OK10ABC/P", true, "OK10ABC/P", "", "", "OK10ABC", "OK", "10", "P", "/P", "OK10"}, {"Callsign OK100ABC/P", "OK100ABC/P", true, "OK100ABC/P", "", "", "OK100ABC", "OK", "100", "P", "/P", "OK100"}, {"Callsign OK2ABC/P", "OK2ABC/P", true, "OK2ABC/P", "", "", "OK2ABC", "OK", "2", "P", "/P", "OK2"}, {"Callsign 9A1ABC/P", "9A1ABC/P", true, "9A1ABC/P", "", "", "9A1ABC", "9A", "1", "P", "/P", "9A1"}, {"Callsign 9A12ABC/P", "9A12ABC/P", true, "9A12ABC/P", "", "", "9A12ABC", "9A", "12", "P", "/P", "9A12"}, {"Callsign A12ABC/P", "A12ABC/P", true, "A12ABC/P", "", "", "A12ABC", "A1", "2", "P", "/P", "A12"}, {"Callsign OK1ABC/QRP", "OK1ABC/QRP", true, "OK1ABC/QRP", "", "", "OK1ABC", "OK", "1", "QRP", "/QRP", "OK1"}, {"Callsign OK1ABC/QRP", "OK1ABC/QRP", true, "OK1ABC/QRP", "", "", "OK1ABC", "OK", "1", "QRP", "/QRP", "OK1"}, {"Callsign OK1aBC/QRP", "OK1aBC/QRP", true, "OK1ABC/QRP", "", "", "OK1ABC", "OK", "1", "QRP", "/QRP", "OK1"}, {"Callsign OK10ABC/QRP", "OK10ABC/QRP", true, "OK10ABC/QRP", "", "", "OK10ABC", "OK", "10", "QRP", "/QRP", "OK10"}, {"Callsign OK100ABC/QRP", "OK100ABC/QRP", true, "OK100ABC/QRP", "", "", "OK100ABC", "OK", "100", "QRP", "/QRP", "OK100"}, {"Callsign OK2ABC/QRP", "OK2ABC/QRP", true, "OK2ABC/QRP", "", "", "OK2ABC", "OK", "2", "QRP", "/QRP", "OK2"}, {"Callsign 9A1ABC/QRP", "9A1ABC/QRP", true, "9A1ABC/QRP", "", "", "9A1ABC", "9A", "1", "QRP", "/QRP", "9A1"}, {"Callsign 9A12ABC/QRP", "9A12ABC/QRP", true, "9A12ABC/QRP", "", "", "9A12ABC", "9A", "12", "QRP", "/QRP", "9A12"}, {"Callsign A12ABC/QRP", "A12ABC/QRP", true, "A12ABC/QRP", "", "", "A12ABC", "A1", "2", "QRP", "/QRP", "A12"}, {"Callsign OK1ABC/R", "OK1ABC/R", true, "OK1ABC/R", "", "", "OK1ABC", "OK", "1", "R", "/R", "OK1"}, {"Callsign OK1ABC/R", "OK1ABC/R", true, "OK1ABC/R", "", "", "OK1ABC", "OK", "1", "R", "/R", "OK1"}, {"Callsign OK1aBC/R", "OK1aBC/R", true, "OK1ABC/R", "", "", "OK1ABC", "OK", "1", "R", "/R", "OK1"}, {"Callsign OK10ABC/R", "OK10ABC/R", true, "OK10ABC/R", "", "", "OK10ABC", "OK", "10", "R", "/R", "OK10"}, {"Callsign OK100ABC/R", "OK100ABC/R", true, "OK100ABC/R", "", "", "OK100ABC", "OK", "100", "R", "/R", "OK100"}, {"Callsign OK2ABC/R", "OK2ABC/R", true, "OK2ABC/R", "", "", "OK2ABC", "OK", "2", "R", "/R", "OK2"}, {"Callsign 9A1ABC/R", "9A1ABC/R", true, "9A1ABC/R", "", "", "9A1ABC", "9A", "1", "R", "/R", "9A1"}, {"Callsign 9A12ABC/R", "9A12ABC/R", true, "9A12ABC/R", "", "", "9A12ABC", "9A", "12", "R", "/R", "9A12"}, {"Callsign A12ABC/R", "A12ABC/R", true, "A12ABC/R", "", "", "A12ABC", "A1", "2", "R", "/R", "A12"}, {"Callsign OK1ABC/B", "OK1ABC/B", true, "OK1ABC/B", "", "", "OK1ABC", "OK", "1", "B", "/B", "OK1"}, {"Callsign OK1ABC/B", "OK1ABC/B", true, "OK1ABC/B", "", "", "OK1ABC", "OK", "1", "B", "/B", "OK1"}, {"Callsign OK1aBC/B", "OK1aBC/B", true, "OK1ABC/B", "", "", "OK1ABC", "OK", "1", "B", "/B", "OK1"}, {"Callsign OK10ABC/B", "OK10ABC/B", true, "OK10ABC/B", "", "", "OK10ABC", "OK", "10", "B", "/B", "OK10"}, {"Callsign OK100ABC/B", "OK100ABC/B", true, "OK100ABC/B", "", "", "OK100ABC", "OK", "100", "B", "/B", "OK100"}, {"Callsign OK2ABC/B", "OK2ABC/B", true, "OK2ABC/B", "", "", "OK2ABC", "OK", "2", "B", "/B", "OK2"}, {"Callsign 9A1ABC/B", "9A1ABC/B", true, "9A1ABC/B", "", "", "9A1ABC", "9A", "1", "B", "/B", "9A1"}, {"Callsign 9A12ABC/B", "9A12ABC/B", true, "9A12ABC/B", "", "", "9A12ABC", "9A", "12", "B", "/B", "9A12"}, {"Callsign A12ABC/B", "A12ABC/B", true, "A12ABC/B", "", "", "A12ABC", "A1", "2", "B", "/B", "A12"}, {"Callsign OK1ABC/LGT", "OK1ABC/LGT", true, "OK1ABC/LGT", "", "", "OK1ABC", "OK", "1", "LGT", "/LGT", "OK1"}, {"Callsign OK1ABC/LGT", "OK1ABC/LGT", true, "OK1ABC/LGT", "", "", "OK1ABC", "OK", "1", "LGT", "/LGT", "OK1"}, {"Callsign OK1aBC/LGT", "OK1aBC/LGT", true, "OK1ABC/LGT", "", "", "OK1ABC", "OK", "1", "LGT", "/LGT", "OK1"}, {"Callsign OK10ABC/LGT", "OK10ABC/LGT", true, "OK10ABC/LGT", "", "", "OK10ABC", "OK", "10", "LGT", "/LGT", "OK10"}, {"Callsign OK100ABC/LGT", "OK100ABC/LGT", true, "OK100ABC/LGT", "", "", "OK100ABC", "OK", "100", "LGT", "/LGT", "OK100"}, {"Callsign OK2ABC/LGT", "OK2ABC/LGT", true, "OK2ABC/LGT", "", "", "OK2ABC", "OK", "2", "LGT", "/LGT", "OK2"}, {"Callsign 9A1ABC/LGT", "9A1ABC/LGT", true, "9A1ABC/LGT", "", "", "9A1ABC", "9A", "1", "LGT", "/LGT", "9A1"}, {"Callsign 9A12ABC/LGT", "9A12ABC/LGT", true, "9A12ABC/LGT", "", "", "9A12ABC", "9A", "12", "LGT", "/LGT", "9A12"}, {"Callsign A12ABC/LGT", "A12ABC/LGT", true, "A12ABC/LGT", "", "", "A12ABC", "A1", "2", "LGT", "/LGT", "A12"}, {"Callsign OK1ABC/E", "OK1ABC/E", true, "OK1ABC/E", "", "", "OK1ABC", "OK", "1", "E", "/E", "OK1"}, {"Callsign OK1ABC-1", "OK1ABC-1", true, "OK1ABC-1", "", "", "OK1ABC", "OK", "1", "", "", "OK1"}, {"Callsign OE/OK1ABC", "OE/OK1ABC", true, "OE/OK1ABC", "OE", "OE/", "OK1ABC", "OK", "1", "", "", "OE0"}, {"Callsign Oe/OK1ABC", "Oe/OK1ABC", true, "OE/OK1ABC", "OE", "OE/", "OK1ABC", "OK", "1", "", "", "OE0"}, {"Callsign SP1/OK1ABC", "SP1/OK1ABC", true, "SP1/OK1ABC", "SP1", "SP1/", "OK1ABC", "OK", "1", "", "", "SP1"}, {"Callsign OE/OK1ABC/M", "OE/OK1ABC/M", true, "OE/OK1ABC/M", "OE", "OE/", "OK1ABC", "OK", "1", "M", "/M", "OE0"}, {"Callsign OE/OK1ABC/MM", "OE/OK1ABC/MM", true, "OE/OK1ABC/MM", "OE", "OE/", "OK1ABC", "OK", "1", "MM", "/MM", "OE0"}, {"Callsign OE/OK1ABC/AM", "OE/OK1ABC/AM", true, "OE/OK1ABC/AM", "OE", "OE/", "OK1ABC", "OK", "1", "AM", "/AM", "OE0"}, {"Callsign OE/OK1ABC/qrp", "OE/OK1ABC/qrp", true, "OE/OK1ABC/QRP", "OE", "OE/", "OK1ABC", "OK", "1", "QRP", "/QRP", "OE0"}, {"Callsign KH6XX/W0", "KH6XX/W0", true, "KH6XX/W0", "", "", "KH6XX", "KH", "6", "W0", "/W0", "W0"}, {"Callsign VE7ABC/2", "VE7ABC/2", true, "VE7ABC/2", "", "", "VE7ABC", "VE", "7", "2", "/2", "VE2"}, }; struct WPXCase { const char *name; const char *callsign; const char *wpx; }; static const WPXCase kWPXCases[] = { {"Callsign OK1AA", "OK1AA", "OK1"}, {"Callsign OK2AA", "OK2AA", "OK2"}, {"Callsign OL80ABC", "OL80ABC", "OL80"}, {"Callsign 9A1AA", "9A1AA", "9A1"}, {"Callsign 9A10AA", "9A10AA", "9A10"}, {"Callsign SP/OK1AA", "SP/OK1AA", "SP0"}, {"Callsign SP1/OK1AA", "SP1/OK1AA", "SP1"}, {"Callsign SP1/OK1AA/P", "SP1/OK1AA/P", "SP1"}, {"Callsign SP1/OK1AA/M", "SP1/OK1AA/M", "SP1"}, {"Callsign A1/OK1AA", "A1/OK1AA", "A1"}, {"Callsign VE7aBC/2", "VE7aBC/2", "VE2"}, {"Callsign OK1AA/P", "OK1AA/P", "OK1"}, {"Callsign OK1AA/E", "OK1AA/E", "OK1"}, {"Callsign OK1AA/AM", "OK1AA/AM", "OK1"}, {"Callsign OK1AA/QRP", "OK1AA/QRP", "OK1"}, {"Callsign N8ABC/KH9", "N8ABC/KH9", "KH9"}, {"Callsign OK1AA/22", "OK1AA/22", "OK1"}, {"Callsign OK1AA-22", "OK1AA-22", "OK1"}, {"Callsign OK1AA/KH", "OK1AA/KH", "KH0"}, }; ================================================ FILE: tests/CallsignTest/tst_callsign.cpp ================================================ #include #include "data/Callsign.h" #include "generated_cases.h" class CallsignTest : public QObject { Q_OBJECT private slots: void initTestCase(); void parse_data(); void parse(); void wpx_data(); void wpx(); }; void CallsignTest::initTestCase() { QLoggingCategory::setFilterRules(QStringLiteral("*.debug=false")); } void CallsignTest::parse_data() { QTest::addColumn("callsign"); QTest::addColumn("valid"); QTest::addColumn("normalized"); QTest::addColumn("hostPrefix"); QTest::addColumn("hostPrefixWithDelimiter"); QTest::addColumn("base"); QTest::addColumn("basePrefix"); QTest::addColumn("basePrefixNumber"); QTest::addColumn("suffix"); QTest::addColumn("suffixWithDelimiter"); QTest::addColumn("wpxPrefix"); int idx = 0; for (const ParseCase &parseCase : kParseCases) { const QString rowId = QStringLiteral("%1_%2").arg(parseCase.name).arg(idx++); const QByteArray rowBytes = rowId.toUtf8(); QTest::newRow(rowBytes.constData()) << QString::fromLatin1(parseCase.callsign) << parseCase.valid << QString::fromLatin1(parseCase.normalized) << QString::fromLatin1(parseCase.host) << QString::fromLatin1(parseCase.hostDelim) << QString::fromLatin1(parseCase.base) << QString::fromLatin1(parseCase.basePrefix) << QString::fromLatin1(parseCase.basePrefixNumber) << QString::fromLatin1(parseCase.suffix) << QString::fromLatin1(parseCase.suffixDelim) << QString::fromLatin1(parseCase.wpx); } } void CallsignTest::parse() { QFETCH(QString, callsign); QFETCH(bool, valid); QFETCH(QString, normalized); QFETCH(QString, hostPrefix); QFETCH(QString, hostPrefixWithDelimiter); QFETCH(QString, base); QFETCH(QString, basePrefix); QFETCH(QString, basePrefixNumber); QFETCH(QString, suffix); QFETCH(QString, suffixWithDelimiter); QFETCH(QString, wpxPrefix); Callsign cs(callsign); QCOMPARE(cs.isValid(), valid); if (!valid) { QVERIFY(cs.getCallsign().isEmpty()); QCOMPARE(cs.getWPXPrefix(), wpxPrefix); return; } QCOMPARE(cs.getCallsign(), normalized); QCOMPARE(cs.getHostPrefix(), hostPrefix); QCOMPARE(cs.getHostPrefixWithDelimiter(), hostPrefixWithDelimiter); QCOMPARE(cs.getBase(), base); QCOMPARE(cs.getBasePrefix(), basePrefix); QCOMPARE(cs.getBasePrefixNumber(), basePrefixNumber); QCOMPARE(cs.getSuffix(), suffix); QCOMPARE(cs.getSuffixWithDelimiter(), suffixWithDelimiter); QCOMPARE(cs.getWPXPrefix(), wpxPrefix); } void CallsignTest::wpx_data() { QTest::addColumn("callsign"); QTest::addColumn("expectedPrefix"); int idx = 0; for (const WPXCase &wpxCase : kWPXCases) { const QString rowId = QStringLiteral("%1_%2").arg(wpxCase.name).arg(idx++); const QByteArray rowBytes = rowId.toUtf8(); QTest::newRow(rowBytes.constData()) << QString::fromLatin1(wpxCase.callsign) << QString::fromLatin1(wpxCase.wpx); } } void CallsignTest::wpx() { QFETCH(QString, callsign); QFETCH(QString, expectedPrefix); Callsign cs(callsign); QCOMPARE(cs.getWPXPrefix(), expectedPrefix); } QTEST_APPLESS_MAIN(CallsignTest) #include "tst_callsign.moc" ================================================ FILE: tests/CredentialStoreTest/CredentialStoreTest.pro ================================================ QT += testlib core widgets sql CONFIG += console testcase c++11 TEMPLATE = app TARGET = tst_credentialstore INCLUDEPATH += $$PWD/../.. SOURCES += \ tst_credentialstore.cpp \ test_stubs.cpp \ ../../core/CredentialStore.cpp \ ../../core/PasswordCipher.cpp \ ../../core/LogParam.cpp HEADERS += \ ../../core/CredentialStore.h \ ../../core/PasswordCipher.h \ ../../core/LogParam.h # QtKeychain !isEmpty(QTKEYCHAININCLUDEPATH) { INCLUDEPATH += $$QTKEYCHAININCLUDEPATH } !isEmpty(QTKEYCHAINLIBPATH) { LIBS += -L$$QTKEYCHAINLIBPATH } equals(QT_MAJOR_VERSION, 6): LIBS += -lqt6keychain equals(QT_MAJOR_VERSION, 5): LIBS += -lqt5keychain # OpenSSL (for PasswordCipher) !isEmpty(OPENSSLINCLUDEPATH) { INCLUDEPATH += $$OPENSSLINCLUDEPATH } !isEmpty(OPENSSLLIBPATH) { LIBS += -L$$OPENSSLLIBPATH } win32 { LIBS += -llibcrypto } else { LIBS += -lcrypto } ================================================ FILE: tests/CredentialStoreTest/test_stubs.cpp ================================================ // Stubs for missing dependencies in CredentialStore tests // These provide minimal implementations to satisfy linker #include #include "core/LogDatabase.h" #include "data/BandPlan.h" // Stub for LogDatabase::currentPlatformId() QString LogDatabase::currentPlatformId() { return QStringLiteral("TestPlatform"); } // Stubs for BandPlan constants used in LogParam const QString BandPlan::MODE_GROUP_STRING_PHONE = QStringLiteral("PHONE"); const QString BandPlan::MODE_GROUP_STRING_CW = QStringLiteral("CW"); const QString BandPlan::MODE_GROUP_STRING_FTx = QStringLiteral("FTx"); const QString BandPlan::MODE_GROUP_STRING_DIGITAL = QStringLiteral("DIGITAL"); ================================================ FILE: tests/CredentialStoreTest/tst_credentialstore.cpp ================================================ #include #include #include #include #include #include #include #include #include #include "core/CredentialStore.h" #include "core/PasswordCipher.h" #include "core/LogParam.h" namespace { QtMessageHandler previousHandler = nullptr; void credentialStoreTestMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message) { if (type == QtWarningMsg) { if (message.contains(QStringLiteral("This plugin does not support propagateSizeHints()"))) return; if (message.contains(QStringLiteral("Cannot save a password. Error"))) return; if (message.contains(QStringLiteral("Cannot get a password"))) return; } if (previousHandler) previousHandler(type, context, message); } struct ScopedMessageHandler { ScopedMessageHandler() { previousHandler = qInstallMessageHandler(credentialStoreTestMessageHandler); } ~ScopedMessageHandler() { qInstallMessageHandler(previousHandler); previousHandler = nullptr; } }; } // anonymous namespace // Helper class to access protected CredentialStore methods via SecureServiceBase // Must be outside anonymous namespace for friend declaration to work class TestCredentialService : public SecureServiceBase { public: DECLARE_SECURE_SERVICE(TestCredentialService) // Expose protected methods from SecureServiceBase for testing static QString testGetPassword(const QString &key, const QString &user) { return getPassword(key, user); } static void testSavePassword(const QString &key, const QString &user, const QString &pass) { savePassword(key, user, pass); } static void testDeletePassword(const QString &key, const QString &user) { deletePassword(key, user); } // Test if save succeeded by trying to get the password back static bool testSaveAndVerify(const QString &key, const QString &user, const QString &pass) { savePassword(key, user, pass); return getPassword(key, user) == pass; } }; // Required registration for TestCredentialService void TestCredentialService::registerCredentials() { CredentialRegistry::instance().add("TestCredentialService", []() { return QList{}; }); } REGISTRATION_SECURE_SERVICE(TestCredentialService); class CredentialStoreTest : public QObject { Q_OBJECT private slots: void initTestCase(); void cleanupTestCase(); void instance_isStable(); void savePassword_emptyInputs_noEffect_data(); void savePassword_emptyInputs_noEffect(); void getPassword_emptyKeyOrUser_returnsEmpty(); void deletePassword_emptyKeyOrUser_noCrash(); void saveGetDelete_worksAndCleansUp(); void getPassword_benchmark_keychain(); // New tests for export/import functionality void exportImportPasswords_roundTrip(); void importPasswords_wrongPassword_fails(); void importPassphrase_saveGetDelete(); private: void setupMessageBoxCloser(); QTimer messageBoxCloser; QTemporaryDir *tempDir = nullptr; }; void CredentialStoreTest::initTestCase() { QLoggingCategory::setFilterRules(QStringLiteral("*.debug=false")); setupMessageBoxCloser(); // Create temporary database for LogParam tests tempDir = new QTemporaryDir(); QVERIFY(tempDir->isValid()); QString dbPath = tempDir->filePath("test.db"); // Create a minimal database with log_param table { QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", "testdb"); db.setDatabaseName(dbPath); QVERIFY(db.open()); QSqlQuery query(db); QVERIFY(query.exec("CREATE TABLE log_param (name TEXT PRIMARY KEY, value TEXT)")); db.close(); } QSqlDatabase::removeDatabase("testdb"); // Set as default database connection for LogParam QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE"); db.setDatabaseName(dbPath); QVERIFY(db.open()); } void CredentialStoreTest::cleanupTestCase() { QSqlDatabase::database().close(); QSqlDatabase::removeDatabase(QSqlDatabase::defaultConnection); delete tempDir; tempDir = nullptr; } void CredentialStoreTest::setupMessageBoxCloser() { messageBoxCloser.setInterval(25); messageBoxCloser.setSingleShot(false); connect(&messageBoxCloser, &QTimer::timeout, this, []() { const auto widgets = QApplication::topLevelWidgets(); for (QWidget *widget : widgets) { if (auto *box = qobject_cast(widget)) box->reject(); } }); messageBoxCloser.start(); } void CredentialStoreTest::instance_isStable() { CredentialStore *first = CredentialStore::instance(); CredentialStore *second = CredentialStore::instance(); QVERIFY(first != nullptr); QCOMPARE(first, second); } void CredentialStoreTest::savePassword_emptyInputs_noEffect_data() { QTest::addColumn("storageKey"); QTest::addColumn("user"); QTest::addColumn("pass"); QTest::newRow("empty_all") << QString() << QString() << QString(); QTest::newRow("empty_key") << QString() << QStringLiteral("u") << QStringLiteral("p"); QTest::newRow("empty_user") << QStringLiteral("k") << QString() << QStringLiteral("p"); QTest::newRow("empty_pass") << QStringLiteral("k") << QStringLiteral("u") << QString(); } void CredentialStoreTest::savePassword_emptyInputs_noEffect() { QFETCH(QString, storageKey); QFETCH(QString, user); QFETCH(QString, pass); // Empty inputs should have no effect (password should remain empty) TestCredentialService::testSavePassword(storageKey, user, pass); // If key or user is empty, getPassword should return empty if (storageKey.isEmpty() || user.isEmpty()) { QCOMPARE(TestCredentialService::testGetPassword(storageKey, user), QString()); } } void CredentialStoreTest::getPassword_emptyKeyOrUser_returnsEmpty() { QCOMPARE(TestCredentialService::testGetPassword(QString(), QStringLiteral("u")), QString()); QCOMPARE(TestCredentialService::testGetPassword(QStringLiteral("k"), QString()), QString()); QCOMPARE(TestCredentialService::testGetPassword(QString(), QString()), QString()); } void CredentialStoreTest::deletePassword_emptyKeyOrUser_noCrash() { TestCredentialService::testDeletePassword(QString(), QStringLiteral("u")); TestCredentialService::testDeletePassword(QStringLiteral("k"), QString()); TestCredentialService::testDeletePassword(QString(), QString()); QVERIFY(true); } void CredentialStoreTest::saveGetDelete_worksAndCleansUp() { const QString unique = QStringLiteral("%1-%2") .arg(QCoreApplication::applicationPid()) .arg(QDateTime::currentMSecsSinceEpoch()); const QString testKey = QStringLiteral("CredentialStoreTest-%1").arg(unique); const QString testUser = QStringLiteral("user-%1").arg(unique); const QString testPwd = QStringLiteral("pass-%1").arg(unique); struct Cleanup { QString key; QString user; ~Cleanup() { TestCredentialService::testDeletePassword(key, user); } } cleanup{testKey, testUser}; // Ensure clean state TestCredentialService::testDeletePassword(testKey, testUser); QCOMPARE(TestCredentialService::testGetPassword(testKey, testUser), QString()); // Save and verify if (!TestCredentialService::testSaveAndVerify(testKey, testUser, testPwd)) QSKIP("Credential store backend not available (save failed)."); QCOMPARE(TestCredentialService::testGetPassword(testKey, testUser), testPwd); // Delete and verify TestCredentialService::testDeletePassword(testKey, testUser); QCOMPARE(TestCredentialService::testGetPassword(testKey, testUser), QString()); } void CredentialStoreTest::getPassword_benchmark_keychain() { const ScopedMessageHandler handlerScope; const QString unique = QStringLiteral("%1-%2-bench") .arg(QCoreApplication::applicationPid()) .arg(QDateTime::currentMSecsSinceEpoch()); const QString testKey = QStringLiteral("CredentialStoreBench-%1").arg(unique); const QString testUser = QStringLiteral("user-%1").arg(unique); const QString testPwd = QStringLiteral("pass-%1").arg(unique); struct Cleanup { QString key; QString user; ~Cleanup() { TestCredentialService::testDeletePassword(key, user); } } cleanup{testKey, testUser}; TestCredentialService::testDeletePassword(testKey, testUser); QCOMPARE(TestCredentialService::testGetPassword(testKey, testUser), QString()); if (!TestCredentialService::testSaveAndVerify(testKey, testUser, testPwd)) QSKIP("Credential store backend not available (save failed)."); QCOMPARE(TestCredentialService::testGetPassword(testKey, testUser), testPwd); QString out; QBENCHMARK { out = TestCredentialService::testGetPassword(testKey, testUser); } QCOMPARE(out, testPwd); } void CredentialStoreTest::exportImportPasswords_roundTrip() { const ScopedMessageHandler handlerScope; const QString unique = QStringLiteral("%1-%2-export") .arg(QCoreApplication::applicationPid()) .arg(QDateTime::currentMSecsSinceEpoch()); const QString testKey = QStringLiteral("ExportTest-%1").arg(unique); const QString testUser = QStringLiteral("user-%1").arg(unique); const QString testPwd = QStringLiteral("secret-%1").arg(unique); const QString passphrase = QStringLiteral("test-passphrase-12345"); struct Cleanup { QString key; QString user; ~Cleanup() { TestCredentialService::testDeletePassword(key, user); LogParam::removeEncryptedPasswords(); LogParam::removeSourcePlatform(); } } cleanup{testKey, testUser}; // Save a test password if (!TestCredentialService::testSaveAndVerify(testKey, testUser, testPwd)) QSKIP("Credential store backend not available (save failed)."); // Register test credential for export CredentialRegistry::instance().add("ExportTest", [testKey, testUser]() { return QList{{testKey, [testUser]() { return testUser; }}}; }); // Export QVERIFY(CredentialStore::instance()->exportPasswords(passphrase)); // Verify encrypted blob was saved QByteArray blob = LogParam::getEncryptedPasswords(); QVERIFY(!blob.isEmpty()); // Delete the original password TestCredentialService::testDeletePassword(testKey, testUser); QCOMPARE(TestCredentialService::testGetPassword(testKey, testUser), QString()); // Import QVERIFY(CredentialStore::instance()->importPasswords(passphrase)); // Verify password was restored QCOMPARE(TestCredentialService::testGetPassword(testKey, testUser), testPwd); } void CredentialStoreTest::importPasswords_wrongPassword_fails() { const ScopedMessageHandler handlerScope; const QString unique = QStringLiteral("%1-%2-wrongpwd") .arg(QCoreApplication::applicationPid()) .arg(QDateTime::currentMSecsSinceEpoch()); const QString testKey = QStringLiteral("WrongPwdTest-%1").arg(unique); const QString testUser = QStringLiteral("user-%1").arg(unique); const QString testPwd = QStringLiteral("secret-%1").arg(unique); const QString correctPassphrase = QStringLiteral("correct-passphrase"); const QString wrongPassphrase = QStringLiteral("wrong-passphrase"); struct Cleanup { QString key; QString user; ~Cleanup() { TestCredentialService::testDeletePassword(key, user); LogParam::removeEncryptedPasswords(); LogParam::removeSourcePlatform(); } } cleanup{testKey, testUser}; // Save and export with correct passphrase if (!TestCredentialService::testSaveAndVerify(testKey, testUser, testPwd)) QSKIP("Credential store backend not available (save failed)."); CredentialRegistry::instance().add("WrongPwdTest", [testKey, testUser]() { return QList{{testKey, [testUser]() { return testUser; }}}; }); QVERIFY(CredentialStore::instance()->exportPasswords(correctPassphrase)); // Try to import with wrong passphrase QVERIFY(!CredentialStore::instance()->importPasswords(wrongPassphrase)); } void CredentialStoreTest::importPassphrase_saveGetDelete() { const ScopedMessageHandler handlerScope; const QString testPassphrase = QStringLiteral("import-test-passphrase-67890"); struct Cleanup { ~Cleanup() { CredentialStore::instance()->deleteImportPassphrase(); } } cleanup; // Initially should be empty CredentialStore::instance()->deleteImportPassphrase(); QCOMPARE(CredentialStore::instance()->getImportPassphrase(), QString()); // Save CredentialStore::instance()->saveImportPassphrase(testPassphrase); // Get QString retrieved = CredentialStore::instance()->getImportPassphrase(); if (retrieved.isEmpty()) QSKIP("Credential store backend not available (save failed)."); QCOMPARE(retrieved, testPassphrase); // Delete CredentialStore::instance()->deleteImportPassphrase(); QCOMPARE(CredentialStore::instance()->getImportPassphrase(), QString()); } int main(int argc, char **argv) { if (qEnvironmentVariableIsEmpty("QT_QPA_PLATFORM")) qputenv("QT_QPA_PLATFORM", "offscreen"); QApplication app(argc, argv); CredentialStoreTest tc; return QTest::qExec(&tc, argc, argv); } #include "tst_credentialstore.moc" ================================================ FILE: tests/DataTest/DataTest.pro ================================================ QT += testlib core sql CONFIG += console testcase c++11 TEMPLATE = app TARGET = tst_data INCLUDEPATH += $$PWD/../.. SOURCES += \ tst_data.cpp \ ../../data/Accents.cpp ================================================ FILE: tests/DataTest/tst_data.cpp ================================================ #include #include #include "data/Data.h" namespace { QString expectedExtendedChar(unsigned char value) { switch (value) { case 0xA0: return " "; case 0xA1: return "!"; case 0xA2: return "C/"; case 0xA3: return "PS"; case 0xA4: return "$?"; case 0xA5: return "Y="; case 0xA6: return "|"; case 0xA7: return "SS"; case 0xA8: return "\""; case 0xA9: return "(c)"; case 0xAA: return "a"; case 0xAB: return "<<"; case 0xAC: return "!"; case 0xAE: return "(r)"; case 0xAF: return "-"; case 0xB0: return "deg"; case 0xB1: return "+-"; case 0xB2: return "2"; case 0xB3: return "3"; case 0xB4: return "'"; case 0xB5: return "u"; case 0xB6: return "P"; case 0xB7: return "*"; case 0xB8: return ","; case 0xB9: return "1"; case 0xBA: return "o"; case 0xBB: return ">>"; case 0xBC: return " 1/4 "; case 0xBD: return " 1/2 "; case 0xBE: return " 3/4 "; case 0xC0: case 0xC1: case 0xC2: case 0xC3: case 0xC4: case 0xC5: return "A"; case 0xC6: return "AE"; case 0xC7: return "C"; case 0xC8: case 0xC9: case 0xCA: case 0xCB: return "E"; case 0xCC: case 0xCD: case 0xCE: case 0xCF: return "I"; case 0xD0: return "D"; case 0xD1: return "N"; case 0xD2: case 0xD3: case 0xD4: case 0xD5: case 0xD6: return "O"; case 0xD7: return "x"; case 0xD8: return "O"; case 0xD9: case 0xDA: case 0xDB: case 0xDC: return "U"; case 0xDD: return "Y"; case 0xDE: return "Th"; case 0xDF: return "ss"; case 0xE0: case 0xE1: case 0xE2: case 0xE3: case 0xE4: case 0xE5: return "a"; case 0xE6: return "ae"; case 0xE7: return "c"; case 0xE8: case 0xE9: case 0xEA: case 0xEB: return "e"; case 0xEC: case 0xED: case 0xEE: case 0xEF: return "i"; case 0xF0: return "d"; case 0xF1: return "n"; case 0xF2: case 0xF3: case 0xF4: case 0xF5: case 0xF6: return "o"; case 0xF7: return "/"; case 0xF8: return "o"; case 0xF9: case 0xFA: case 0xFB: case 0xFC: return "u"; case 0xFD: return "y"; case 0xFE: return "th"; default: return "?"; } } } class DataTest : public QObject { Q_OBJECT private slots: void initTestCase(); void removeAccents_basic(); void removeAccents_asciiPrintable(); void removeAccents_nonPrintable(); void removeAccents_limits(); void removeAccents_benchmark(); }; void DataTest::initTestCase() { QLoggingCategory::setFilterRules(QStringLiteral("*.debug=false")); } void DataTest::removeAccents_basic() { struct Case { const char *input; const char *expected; }; const Case cases[] = { {"ěščřžýáíé", "escrzyaie"}, {"ĚŠČŘŽÝÁÍÉ", "ESCRZYAIE"}, {"äöüß", "aouss"}, {"ÄÖÜẞ", "AOUSs"}, {"âêîôûàèùéëïü", "aeiouaeueeiu"}, {"鍖椾喊", "Chen Zhan Han "}, {"Питер. Лето. Любов", "Piter. Leto. Liubov"}, {"10°C", "10degC"}, {"⾦", "?"} }; for (const Case &c : cases) { QCOMPARE(Data::removeAccents(QString::fromUtf8(c.input)), QString::fromUtf8(c.expected)); } } void DataTest::removeAccents_asciiPrintable() { for (int i = 32; i <= 127; ++i) { const QString input{QChar(i)}; QCOMPARE(Data::removeAccents(input), input); } } void DataTest::removeAccents_nonPrintable() { for (int i = 128; i <= 254; ++i) { const QString input{QChar(i)}; const QString expected = expectedExtendedChar(static_cast(i)); QCOMPARE(Data::removeAccents(input), expected); } } void DataTest::removeAccents_limits() { const QString lastInput = QString::fromUtf8("\uFFFE"); QVERIFY(!lastInput.isEmpty()); QCOMPARE(Data::removeAccents(lastInput), QString("?")); const QString firstInput = QString::fromUtf8("\u0080"); QVERIFY(!firstInput.isEmpty()); QCOMPARE(Data::removeAccents(firstInput), QString("?")); } void DataTest::removeAccents_benchmark() { QBENCHMARK { Data::removeAccents(QStringLiteral("ĚŠČŘŽÝÁÍÉdkfj dfj dlkfj dslfkj dslfj dlfkjdfljdslfd lasdjkas jdlkasj dlkasj dlksajd lsajdlas jdls")); }; } QTEST_APPLESS_MAIN(DataTest) #include "tst_data.moc" ================================================ FILE: tests/DxServerStringTest/DxServerStringTest.pro ================================================ QT += testlib core CONFIG += console testcase c++11 TEMPLATE = app TARGET = tst_dxserverstring INCLUDEPATH += $$PWD/../.. SOURCES += \ tst_dxserverstring.cpp \ ../../data/DxServerString.cpp HEADERS += \ ../../data/DxServerString.h ================================================ FILE: tests/DxServerStringTest/tst_dxserverstring.cpp ================================================ #include #include "data/DxServerString.h" class DxServerStringTest : public QObject { Q_OBJECT private slots: void initTestCase(); void invalidServerStrings_data(); void invalidServerStrings(); void validServerStrings_data(); void validServerStrings(); }; void DxServerStringTest::initTestCase() { QLoggingCategory::setFilterRules(QStringLiteral("*.debug=false")); } void DxServerStringTest::invalidServerStrings_data() { QTest::addColumn("serverString"); const QStringList invalidStrings = { "", "1", "192.", "aa.", ".11", ".aa", ".11.11", ".aa.aa", "a1.11", "aa.cz:1234567", "aa.cz:-1234", "aa.cz:a234", "aa.cz#1234", "aa/aa@aa.cz:1234", "aa:1234567", "aa:-1234", "aa:a234", "aa#1234", "aa/aa@aa:1234", "192.168.2.1:1234567", "192.168.2.1:-1234", "192.168.2.1:a234", "192.168.2.1#1234", "aa/aa@192.168.2.1:1234", "a1@aaa@localhost:7000" }; for (const QString &value : invalidStrings) { QTest::newRow(value.toUtf8().constData()) << value; } } void DxServerStringTest::invalidServerStrings() { QFETCH(QString, serverString); const DxServerString server(serverString); QVERIFY(!DxServerString::isValidServerString(serverString)); QVERIFY(!server.isValid()); QCOMPARE(server.getUsername(), QString()); QCOMPARE(server.getHostname(), QString()); QCOMPARE(server.getPort(), 7300); QCOMPARE(server.getPasswordStorageKey(), QString()); } void DxServerStringTest::validServerStrings_data() { QTest::addColumn("serverString"); QTest::addColumn("expectedUsername"); QTest::addColumn("expectedHostname"); QTest::addColumn("expectedPort"); QTest::addColumn("expectedPasswordKey"); struct Case { const char *name; const char *server; const char *username; const char *hostname; int port; }; const Case cases[] = { {"localhost_default", "localhost:7300", "", "localhost", 7300}, {"localhost_user", "aa@localhost:7300", "aa", "localhost", 7300}, {"localhost_useralpha", "a1aaa@localhost:7300", "a1aaa", "localhost", 7300}, {"localhost_custom_port", "a1aaa@localhost:7000", "a1aaa", "localhost", 7000}, {"localhost_mixed_case", "a1Aaa@localHost:7000", "a1Aaa", "localHost", 7000}, {"loopback_default", "127.0.0.1:7300", "", "127.0.0.1", 7300}, {"loopback_user", "aa@127.0.0.1:7300", "aa", "127.0.0.1", 7300}, {"loopback_useralpha", "a1aaa@127.0.0.1:7300", "a1aaa", "127.0.0.1", 7300}, {"loopback_custom_port", "a1aaa@127.0.0.1:7000", "a1aaa", "127.0.0.1", 7000}, {"loopback_mixed_case", "a1Aaa@127.0.0.1:7000", "a1Aaa", "127.0.0.1", 7000}, {"domain_default", "aa.cz:7300", "", "aa.cz", 7300}, {"domain_user", "aa@aa.cz:7300", "aa", "aa.cz", 7300}, {"domain_useralpha", "a1aaa@aa.cz:7300", "a1aaa", "aa.cz", 7300}, {"domain_custom_port", "a1aaa@aa.cz:7000", "a1aaa", "aa.cz", 7000}, {"domain_mixed_case", "a1Aaa@aa.cZ:7000", "a1Aaa", "aa.cZ", 7000}, {"subdomain_default", "aa.bb.cz:7300", "", "aa.bb.cz", 7300}, {"subdomain_user", "aa@aa.bb.cz:7300", "aa", "aa.bb.cz", 7300}, {"subdomain_useralpha", "a1aaa@aa.bb.cz:7300", "a1aaa", "aa.bb.cz", 7300}, {"subdomain_custom_port", "a1aaa@aa.bb.cz:7000", "a1aaa", "aa.bb.cz", 7000}, {"subdomain_mixed_case", "a1Aaa@aa.bb.cZ:7000", "a1Aaa", "aa.bb.cZ", 7000} }; for (const Case &c : cases) { QTest::newRow(c.name) << QString::fromLatin1(c.server) << QString::fromLatin1(c.username) << QString::fromLatin1(c.hostname) << c.port << QStringLiteral("%1:%2").arg(QString::fromLatin1(c.hostname)).arg(c.port); } } void DxServerStringTest::validServerStrings() { QFETCH(QString, serverString); QFETCH(QString, expectedUsername); QFETCH(QString, expectedHostname); QFETCH(int, expectedPort); QFETCH(QString, expectedPasswordKey); const DxServerString server(serverString); QVERIFY(DxServerString::isValidServerString(serverString)); QVERIFY(server.isValid()); QCOMPARE(server.getUsername(), expectedUsername); QCOMPARE(server.getHostname(), expectedHostname); QCOMPARE(server.getPort(), expectedPort); QCOMPARE(server.getPasswordStorageKey(), expectedPasswordKey); } QTEST_APPLESS_MAIN(DxServerStringTest) #include "tst_dxserverstring.moc" ================================================ FILE: tests/FileCompressorTest/FileCompressorTest.pro ================================================ QT += testlib core widgets CONFIG += console testcase c++11 TEMPLATE = app TARGET = tst_filecompressor INCLUDEPATH += $$PWD/../.. SOURCES += \ tst_filecompressor.cpp \ ../../core/FileCompressor.cpp HEADERS += \ ../../core/FileCompressor.h # zlib !isEmpty(ZLIBINCLUDEPATH) { INCLUDEPATH += $$ZLIBINCLUDEPATH } !isEmpty(ZLIBLIBPATH) { LIBS += -L$$ZLIBLIBPATH } unix: LIBS += -lz win32: LIBS += -lzlib ================================================ FILE: tests/FileCompressorTest/tst_filecompressor.cpp ================================================ #include #include #include #include #include #include "core/FileCompressor.h" class FileCompressorTest : public QObject { Q_OBJECT private slots: void initTestCase(); void cleanupTestCase(); // In-memory tests void gzip_emptyData_returnsEmpty(); void gzip_smallData_compressesAndDecompresses(); void gzip_largeData_compressesAndDecompresses(); void gzip_binaryData_compressesAndDecompresses(); void gunzip_invalidData_returnsEmpty(); void gunzip_truncatedData_returnsEmpty(); // File-based tests void gzipFile_smallFile_compressesAndDecompresses(); void gzipFile_largeFile_compressesAndDecompresses(); void gzipFile_nonExistentSource_returnsFalse(); void gzipFile_invalidDestPath_returnsFalse(); void gzipFile_progressCallback_isCalled(); void gzipFile_progressCallbackCancel_stopsCompression(); // Benchmarks void gzip_benchmark_compression(); void gzip_benchmark_decompression(); private: QByteArray generateRandomData(int size); QByteArray generateCompressibleData(int size); QTemporaryDir *tempDir = nullptr; }; void FileCompressorTest::initTestCase() { tempDir = new QTemporaryDir(); QVERIFY(tempDir->isValid()); } void FileCompressorTest::cleanupTestCase() { delete tempDir; tempDir = nullptr; } QByteArray FileCompressorTest::generateRandomData(int size) { QByteArray data(size, '\0'); for (int i = 0; i < size; ++i) data[i] = static_cast(QRandomGenerator::global()->bounded(256)); return data; } QByteArray FileCompressorTest::generateCompressibleData(int size) { // Generate data with repeated patterns (highly compressible) QByteArray pattern = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; QByteArray data; data.reserve(size); while (data.size() < size) data.append(pattern); return data.left(size); } // ============================================================================ // In-memory tests // ============================================================================ void FileCompressorTest::gzip_emptyData_returnsEmpty() { QByteArray empty; QByteArray compressed = FileCompressor::gzip(empty); QVERIFY(compressed.isEmpty()); QByteArray decompressed = FileCompressor::gunzip(empty); QVERIFY(decompressed.isEmpty()); } void FileCompressorTest::gzip_smallData_compressesAndDecompresses() { QByteArray original = "Hello, World! This is a test message for compression."; QByteArray compressed = FileCompressor::gzip(original); QVERIFY(!compressed.isEmpty()); QVERIFY(compressed != original); QByteArray decompressed = FileCompressor::gunzip(compressed); QCOMPARE(decompressed, original); } void FileCompressorTest::gzip_largeData_compressesAndDecompresses() { // 1 MB of compressible data QByteArray original = generateCompressibleData(1024 * 1024); QByteArray compressed = FileCompressor::gzip(original); QVERIFY(!compressed.isEmpty()); // Compressible data should be significantly smaller QVERIFY(compressed.size() < original.size() / 2); QByteArray decompressed = FileCompressor::gunzip(compressed); QCOMPARE(decompressed, original); } void FileCompressorTest::gzip_binaryData_compressesAndDecompresses() { // Random binary data (less compressible) QByteArray original = generateRandomData(10000); QByteArray compressed = FileCompressor::gzip(original); QVERIFY(!compressed.isEmpty()); QByteArray decompressed = FileCompressor::gunzip(compressed); QCOMPARE(decompressed, original); } void FileCompressorTest::gunzip_invalidData_returnsEmpty() { QByteArray invalid = "This is not gzip compressed data"; QByteArray result = FileCompressor::gunzip(invalid); QVERIFY(result.isEmpty()); } void FileCompressorTest::gunzip_truncatedData_returnsEmpty() { QByteArray original = "Test data for truncation test"; QByteArray compressed = FileCompressor::gzip(original); QVERIFY(!compressed.isEmpty()); // Truncate the compressed data QByteArray truncated = compressed.left(compressed.size() / 2); QByteArray result = FileCompressor::gunzip(truncated); // Should return empty or partial data, but not crash QVERIFY(result != original); } // ============================================================================ // File-based tests // ============================================================================ void FileCompressorTest::gzipFile_smallFile_compressesAndDecompresses() { QString sourceFile = tempDir->filePath("small_source.txt"); QString compressedFile = tempDir->filePath("small_compressed.gz"); QString decompressedFile = tempDir->filePath("small_decompressed.txt"); // Create source file QByteArray original = "Small file content for compression test.\n"; { QFile file(sourceFile); QVERIFY(file.open(QIODevice::WriteOnly)); file.write(original); } // Compress QVERIFY(FileCompressor::gzipFile(sourceFile, compressedFile)); QVERIFY(QFile::exists(compressedFile)); // Decompress QVERIFY(FileCompressor::gunzipFile(compressedFile, decompressedFile)); QVERIFY(QFile::exists(decompressedFile)); // Verify content QFile resultFile(decompressedFile); QVERIFY(resultFile.open(QIODevice::ReadOnly)); QCOMPARE(resultFile.readAll(), original); } void FileCompressorTest::gzipFile_largeFile_compressesAndDecompresses() { QString sourceFile = tempDir->filePath("large_source.bin"); QString compressedFile = tempDir->filePath("large_compressed.gz"); QString decompressedFile = tempDir->filePath("large_decompressed.bin"); // Create 5 MB source file with compressible data QByteArray original = generateCompressibleData(5 * 1024 * 1024); { QFile file(sourceFile); QVERIFY(file.open(QIODevice::WriteOnly)); file.write(original); } // Compress QVERIFY(FileCompressor::gzipFile(sourceFile, compressedFile)); QVERIFY(QFile::exists(compressedFile)); // Compressed file should be smaller QFileInfo compressedInfo(compressedFile); QVERIFY(compressedInfo.size() < original.size() / 2); // Decompress QVERIFY(FileCompressor::gunzipFile(compressedFile, decompressedFile)); // Verify content QFile resultFile(decompressedFile); QVERIFY(resultFile.open(QIODevice::ReadOnly)); QCOMPARE(resultFile.readAll(), original); } void FileCompressorTest::gzipFile_nonExistentSource_returnsFalse() { QString sourceFile = tempDir->filePath("non_existent_file.txt"); QString compressedFile = tempDir->filePath("output.gz"); QVERIFY(!FileCompressor::gzipFile(sourceFile, compressedFile)); QVERIFY(!QFile::exists(compressedFile)); } void FileCompressorTest::gzipFile_invalidDestPath_returnsFalse() { QString sourceFile = tempDir->filePath("source_for_invalid_dest.txt"); QString invalidDest = "/nonexistent/path/output.gz"; // Create source file { QFile file(sourceFile); QVERIFY(file.open(QIODevice::WriteOnly)); file.write("test"); } QVERIFY(!FileCompressor::gzipFile(sourceFile, invalidDest)); } void FileCompressorTest::gzipFile_progressCallback_isCalled() { QString sourceFile = tempDir->filePath("progress_source.bin"); QString compressedFile = tempDir->filePath("progress_compressed.gz"); // Create 1 MB file QByteArray original = generateCompressibleData(1024 * 1024); { QFile file(sourceFile); QVERIFY(file.open(QIODevice::WriteOnly)); file.write(original); } int callCount = 0; qint64 lastProcessed = 0; bool progressValid = true; auto progressCallback = [&](qint64 processed, qint64 total) -> bool { ++callCount; if (processed < lastProcessed || total <= 0) progressValid = false; lastProcessed = processed; return true; // Continue }; QVERIFY(FileCompressor::gzipFile(sourceFile, compressedFile, progressCallback)); QVERIFY(callCount > 0); QVERIFY(progressValid); } void FileCompressorTest::gzipFile_progressCallbackCancel_stopsCompression() { QString sourceFile = tempDir->filePath("cancel_source.bin"); QString compressedFile = tempDir->filePath("cancel_compressed.gz"); // Create 2 MB file QByteArray original = generateCompressibleData(2 * 1024 * 1024); { QFile file(sourceFile); QVERIFY(file.open(QIODevice::WriteOnly)); file.write(original); } int callCount = 0; auto progressCallback = [&](qint64, qint64) -> bool { ++callCount; // Cancel after a few calls return callCount < 3; }; QVERIFY(!FileCompressor::gzipFile(sourceFile, compressedFile, progressCallback)); // Destination file should be cleaned up QVERIFY(!QFile::exists(compressedFile)); } // ============================================================================ // Benchmarks // ============================================================================ void FileCompressorTest::gzip_benchmark_compression() { QByteArray data = generateCompressibleData(1024 * 1024); // 1 MB QByteArray compressed; QBENCHMARK { compressed = FileCompressor::gzip(data); } QVERIFY(!compressed.isEmpty()); } void FileCompressorTest::gzip_benchmark_decompression() { QByteArray data = generateCompressibleData(1024 * 1024); // 1 MB QByteArray compressed = FileCompressor::gzip(data); QByteArray decompressed; QBENCHMARK { decompressed = FileCompressor::gunzip(compressed); } QCOMPARE(decompressed, data); } int main(int argc, char **argv) { QCoreApplication app(argc, argv); FileCompressorTest tc; return QTest::qExec(&tc, argc, argv); } #include "tst_filecompressor.moc" ================================================ FILE: tests/GridsquareTest/GridsquareTest.pro ================================================ QT += testlib core CONFIG += console testcase c++11 TEMPLATE = app TARGET = tst_gridsquare INCLUDEPATH += $$PWD/../.. SOURCES += \ tst_gridsquare.cpp \ ../../data/Gridsquare.cpp \ ../../core/LogLocale.cpp HEADERS += \ ../../data/Gridsquare.h \ ../../core/LogLocale.h ================================================ FILE: tests/GridsquareTest/tst_gridsquare.cpp ================================================ #include #include #include "data/Gridsquare.h" class GridsquareTest : public QObject { Q_OBJECT private slots: void initTest(); void invalidGridStrings_data(); void invalidGridStrings(); void validGridStrings_data(); void validGridStrings(); void invalidCoordinateCtor_data(); void invalidCoordinateCtor(); void validCoordinateCtor_data(); void validCoordinateCtor(); void gridToLatLon_data(); void gridToLatLon(); void gridToLatLon_invalid_data(); void gridToLatLon_invalid(); void latLonInput_data(); void latLonInput(); void getGrid_data(); void getGrid(); void distanceToCoordinates_data(); void distanceToCoordinates(); void distanceToGrids_data(); void distanceToGrids(); void distanceSymmetry(); void bearingToCoordinates_data(); void bearingToCoordinates(); void bearingToGrids_data(); void bearingToGrids(); void bearingSymmetry(); }; void GridsquareTest::initTest() { QLoggingCategory::setFilterRules(QStringLiteral("*.debug=false")); } void GridsquareTest::invalidGridStrings_data() { QTest::addColumn("grid"); const QStringList invalidGrids = { "aabbcc", "112233", "AA1122", "11AA22", "ZZ99AA", "AABBCCDD", "AABBCC11", "11223344", "AA112233", "11AA2233", "11AA22AA", "ZZ99AA00", "AA11AA00AA" }; for (const QString &grid : invalidGrids) { const QByteArray rowId = grid.toUtf8(); QTest::newRow(rowId.constData()) << grid; } } void GridsquareTest::invalidGridStrings() { QFETCH(QString, grid); const Gridsquare gs(grid); QVERIFY(!gs.isValid()); QCOMPARE(gs.getGrid(), QString()); QVERIFY(qIsNaN(gs.getLatitude())); QVERIFY(qIsNaN(gs.getLongitude())); } void GridsquareTest::validGridStrings_data() { QTest::addColumn("input"); QTest::addColumn("expectedGrid"); const struct Case { const char *input; const char *expected; } cases[] = { {"AA", "AA"}, {"AA11", "AA11"}, {"AA11AA", "AA11AA"}, {"AA11AA11", "AA11AA11"}, {"aa", "AA"}, {"aa11", "AA11"}, {"aa11aa", "AA11AA"}, {"aa11aa11", "AA11AA11"}, {"aA", "AA"}, {"aA11", "AA11"}, {"aA11Aa", "AA11AA"}, {"Aa11Aa11", "AA11AA11"} }; for (const Case &c : cases) { QTest::newRow(c.input) << QString::fromLatin1(c.input) << QString::fromLatin1(c.expected); } } void GridsquareTest::validGridStrings() { QFETCH(QString, input); QFETCH(QString, expectedGrid); const Gridsquare gs(input); QVERIFY(gs.isValid()); QCOMPARE(gs.getGrid(), expectedGrid); } void GridsquareTest::invalidCoordinateCtor_data() { QTest::addColumn("latitude"); QTest::addColumn("longitude"); const struct Case { double lat; double lon; } cases[] = { {250.0, 250.0}, {-250.0, 250.0}, {250.0, -250.0}, {-250.0, -250.0} }; const int casesCount = sizeof(cases) / sizeof(cases[0]); for (int i = 0; i < casesCount; ++i) { QTest::newRow(QString("invalid_coord_%1").arg(i).toUtf8().constData()) << cases[i].lat << cases[i].lon; } } void GridsquareTest::invalidCoordinateCtor() { QFETCH(double, latitude); QFETCH(double, longitude); const Gridsquare gs(latitude, longitude); QVERIFY(!gs.isValid()); QVERIFY(qIsNaN(gs.getLatitude())); QVERIFY(qIsNaN(gs.getLongitude())); QCOMPARE(gs.getGrid(), QString()); } void GridsquareTest::validCoordinateCtor_data() { QTest::addColumn("latitude"); QTest::addColumn("longitude"); const struct Case { double lat; double lon; } cases[] = { {40.0, -179.0}, {40.0, 179.0}, {-40.0, -179.0}, {-40.0, 179.0} }; const int casesCount = sizeof(cases) / sizeof(cases[0]); for (int i = 0; i < casesCount; ++i) { QTest::newRow(QString("valid_coord_%1").arg(i).toUtf8().constData()) << cases[i].lat << cases[i].lon; } } void GridsquareTest::validCoordinateCtor() { QFETCH(double, latitude); QFETCH(double, longitude); const Gridsquare gs(latitude, longitude); QVERIFY(gs.isValid()); QCOMPARE(gs.getLatitude(), latitude); QCOMPARE(gs.getLongitude(), longitude); } void GridsquareTest::gridToLatLon_data() { QTest::addColumn("grid"); QTest::addColumn("expectedLatitude"); QTest::addColumn("expectedLongitude"); const struct Case { const char *grid; int lat; int lon; } cases[] = { {"AA11", -88, -177}, {"AR11", 82, -177}, {"RA11", -88, 163}, {"RR11", 82, 163}, {"IJ11", 2, -17}, {"aa11", -88, -177}, {"ar11", 82, -177}, {"AA11AA", -89, -178}, {"AR11AA", 81, -178}, {"RA11AA", -89, 162}, {"RR11AA", 81, 162}, {"IJ11AA", 1, -18}, {"aa11aa", -89, -178}, {"ar11aa", 81, -178}, {"aa11AA", -89, -178}, {"ar11AA", 81, -178}, {"AA11aa", -89, -178}, {"AR11aa", 81, -178}, {"AA11AA11", -89, -178}, {"AR11AA11", 81, -178}, {"RA11AA11", -89, 162}, {"RR11AA11", 81, 162}, {"IJ11AA11", 1, -18}, {"aa11aa11", -89, -178}, {"ar11aa11", 81, -178}, {"aa11AA11", -89, -178}, {"ar11AA11", 81, -178}, {"AA11aa11", -89, -178}, {"AR11aa11", 81, -178} }; for (const Case &c : cases) { QTest::newRow(c.grid) << QString::fromLatin1(c.grid) << c.lat << c.lon; } } void GridsquareTest::gridToLatLon() { QFETCH(QString, grid); QFETCH(int, expectedLatitude); QFETCH(int, expectedLongitude); const Gridsquare gs(grid); QVERIFY(gs.isValid()); // Use floor(d + 0.5) instead of qRound() to get consistent "round half up" // behaviour across platforms. qRound() for negative half-integers (e.g. -88.5) // produces different results on MSVC vs GCC due to floating-point precision. QCOMPARE(static_cast(std::floor(gs.getLatitude() + 0.5)), expectedLatitude); QCOMPARE(static_cast(std::floor(gs.getLongitude() + 0.5)), expectedLongitude); } void GridsquareTest::gridToLatLon_invalid_data() { QTest::addColumn("grid"); const QStringList invalids = { "ZZ11", "ZZ11AA", "ZZ11AA11" }; for (const QString &grid : invalids) { QTest::newRow(grid.toUtf8().constData()) << grid; } } void GridsquareTest::gridToLatLon_invalid() { QFETCH(QString, grid); const Gridsquare gs(grid); QVERIFY(!gs.isValid()); QVERIFY(qIsNaN(gs.getLatitude())); QVERIFY(qIsNaN(gs.getLongitude())); } void GridsquareTest::latLonInput_data() { QTest::addColumn("latitude"); QTest::addColumn("longitude"); const struct Case { double lat; double lon; } cases[] = { {18.0, 17.9}, {-14.9, 179.0}, {14.9, -179.0}, {-14.9, -179.0} }; const int casesCount = sizeof(cases) / sizeof(cases[0]); for (int i = 0; i < casesCount; ++i) { QTest::newRow(QString("latlon_input_%1").arg(i).toUtf8().constData()) << cases[i].lat << cases[i].lon; } } void GridsquareTest::latLonInput() { QFETCH(double, latitude); QFETCH(double, longitude); const Gridsquare gs(latitude, longitude); QVERIFY(gs.isValid()); QCOMPARE(gs.getLatitude(), latitude); QCOMPARE(gs.getLongitude(), longitude); } void GridsquareTest::getGrid_data() { QTest::addColumn("fromCoordinates"); QTest::addColumn("gridInput"); QTest::addColumn("latitude"); QTest::addColumn("longitude"); QTest::addColumn("expectedGrid"); struct Case { const char *name; bool fromCoordinates; const char *gridInput; double lat; double lon; const char *expected; }; const Case cases[] = { {"invalid_string", false, "INVALID", 0.0, 0.0, ""}, {"from_coordinates", true, "", 18.0, 17.9, "JK88WA"}, {"two_chars", false, "AA", 0.0, 0.0, "AA"}, {"four_chars", false, "AA11", 0.0, 0.0, "AA11"}, {"six_chars", false, "AA11CD", 0.0, 0.0, "AA11CD"}, {"eight_chars", false, "AA11CD22", 0.0, 0.0, "AA11CD22"}, {"normalize", false, "AA11cd22", 0.0, 0.0, "AA11CD22"} }; for (const Case &c : cases) { QTest::newRow(c.name) << c.fromCoordinates << QString::fromLatin1(c.gridInput) << c.lat << c.lon << QString::fromLatin1(c.expected); } } void GridsquareTest::getGrid() { QFETCH(bool, fromCoordinates); QFETCH(QString, gridInput); QFETCH(double, latitude); QFETCH(double, longitude); QFETCH(QString, expectedGrid); const Gridsquare gs = fromCoordinates ? Gridsquare(latitude, longitude) : Gridsquare(gridInput); QCOMPARE(gs.getGrid(), expectedGrid); } void GridsquareTest::distanceToCoordinates_data() { QTest::addColumn("fromCoordinates"); QTest::addColumn("gridInput"); QTest::addColumn("sourceLat"); QTest::addColumn("sourceLon"); QTest::addColumn("targetLat"); QTest::addColumn("targetLon"); QTest::addColumn("expectedResult"); QTest::addColumn("expectedRoundedDistance"); struct Case { const char *name; bool fromCoordinates; const char *gridInput; double sourceLat; double sourceLon; double targetLat; double targetLon; bool expectedResult; int expectedRoundedDistance; }; const Case cases[] = { {"invalid_grid", false, "18123", 0.0, 0.0, 12.0, 17.9, false, 0}, {"from_latlon", true, "", 18.0, 17.9, 12.0, 17.9, true, 667}, {"grid_aa11", false, "AA11", 0.0, 0.0, 12.0, 17.9, true, 11504}, {"grid_aa11cd", false, "AA11CD", 0.0, 0.0, 12.0, 17.9, true, 11465}, {"grid_aa11cd22", false, "AA11CD22", 0.0, 0.0, 12.0, 17.9, true, 11464}, {"grid_aa11cd22_lower", false, "AA11cd22", 0.0, 0.0, 12.0, 17.9, true, 11464} }; for (const Case &c : cases) { QTest::newRow(c.name) << c.fromCoordinates << QString::fromLatin1(c.gridInput) << c.sourceLat << c.sourceLon << c.targetLat << c.targetLon << c.expectedResult << c.expectedRoundedDistance; } } void GridsquareTest::distanceToCoordinates() { QFETCH(bool, fromCoordinates); QFETCH(QString, gridInput); QFETCH(double, sourceLat); QFETCH(double, sourceLon); QFETCH(double, targetLat); QFETCH(double, targetLon); QFETCH(bool, expectedResult); QFETCH(int, expectedRoundedDistance); const Gridsquare source = fromCoordinates ? Gridsquare(sourceLat, sourceLon) : Gridsquare(gridInput); double distance = -1.0; QCOMPARE(source.distanceTo(targetLat, targetLon, distance), expectedResult); if (expectedResult) { QCOMPARE(qRound(distance), expectedRoundedDistance); } else { QCOMPARE(distance, 0.0); } } void GridsquareTest::distanceToGrids_data() { QTest::addColumn("sourceGrid"); QTest::addColumn("targetGrid"); QTest::addColumn("expectedRoundedDistance"); const struct Case { const char *name; const char *source; const char *target; int distance; } cases[] = { {"long_short_path", "JO80AA", "CD01AA", 18189}, {"long_short_path2", "JO80AA", "RE01AA", 17439}, {"dateline_shortest", "RB80AA", "AA01AA", 1001} }; for (const Case &c : cases) { QTest::newRow(c.name) << QString::fromLatin1(c.source) << QString::fromLatin1(c.target) << c.distance; } } void GridsquareTest::distanceToGrids() { QFETCH(QString, sourceGrid); QFETCH(QString, targetGrid); QFETCH(int, expectedRoundedDistance); const Gridsquare source(sourceGrid); const Gridsquare target(targetGrid); double distance = -1.0; QVERIFY(source.distanceTo(target, distance)); QCOMPARE(qRound(distance), expectedRoundedDistance); } void GridsquareTest::distanceSymmetry() { const Gridsquare gridA("RB80AA"); const Gridsquare gridB("AA01AA"); QVERIFY(gridA.isValid()); QVERIFY(gridB.isValid()); double distanceAB = -1.0; double distanceBA = -1.0; QVERIFY(gridA.distanceTo(gridB, distanceAB)); QVERIFY(gridB.distanceTo(gridA, distanceBA)); QCOMPARE(distanceAB, distanceBA); double sameDistance = -1.0; QVERIFY(gridA.distanceTo(gridA, sameDistance)); QCOMPARE(sameDistance, 0.0); } void GridsquareTest::bearingToCoordinates_data() { QTest::addColumn("fromCoordinates"); QTest::addColumn("gridInput"); QTest::addColumn("sourceLat"); QTest::addColumn("sourceLon"); QTest::addColumn("targetLat"); QTest::addColumn("targetLon"); QTest::addColumn("expectedResult"); QTest::addColumn("expectedRoundedBearing"); struct Case { const char *name; bool fromCoordinates; const char *gridInput; double sourceLat; double sourceLon; double targetLat; double targetLon; bool expectedResult; int expectedRoundedBearing; }; const Case cases[] = { {"invalid_grid", false, "18123", 0.0, 0.0, 12.0, 17.9, false, 0}, {"from_latlon", true, "", 18.0, 17.9, 12.0, 17.9, true, 180}, {"grid_aa11", false, "AA11", 0.0, 0.0, 12.0, 17.9, true, 195}, {"grid_aa11cd", false, "AA11CD", 0.0, 0.0, 12.0, 17.9, true, 196}, {"grid_aa11cd22", false, "AA11CD22", 0.0, 0.0, 12.0, 17.9, true, 196}, {"grid_aa11cd22_lower", false, "AA11cd22", 0.0, 0.0, 12.0, 17.9, true, 196} }; for (const Case &c : cases) { QTest::newRow(c.name) << c.fromCoordinates << QString::fromLatin1(c.gridInput) << c.sourceLat << c.sourceLon << c.targetLat << c.targetLon << c.expectedResult << c.expectedRoundedBearing; } } void GridsquareTest::bearingToCoordinates() { QFETCH(bool, fromCoordinates); QFETCH(QString, gridInput); QFETCH(double, sourceLat); QFETCH(double, sourceLon); QFETCH(double, targetLat); QFETCH(double, targetLon); QFETCH(bool, expectedResult); QFETCH(int, expectedRoundedBearing); const Gridsquare source = fromCoordinates ? Gridsquare(sourceLat, sourceLon) : Gridsquare(gridInput); double bearing = -1.0; QCOMPARE(source.bearingTo(targetLat, targetLon, bearing), expectedResult); if (expectedResult) { QCOMPARE(qRound(bearing), expectedRoundedBearing); } else { QCOMPARE(bearing, 0.0); } } void GridsquareTest::bearingToGrids_data() { QTest::addColumn("sourceGrid"); QTest::addColumn("targetGrid"); QTest::addColumn("expectedRoundedBearing"); const struct Case { const char *name; const char *source; const char *target; int bearing; } cases[] = { {"long_short_path", "JO80AA", "CD01AA", 228}, {"long_short_path2", "JO80AA", "RE01AA", 101}, {"dateline_shortest", "RB80AA", "AA01AA", 180} }; for (const Case &c : cases) { QTest::newRow(c.name) << QString::fromLatin1(c.source) << QString::fromLatin1(c.target) << c.bearing; } } void GridsquareTest::bearingToGrids() { QFETCH(QString, sourceGrid); QFETCH(QString, targetGrid); QFETCH(int, expectedRoundedBearing); const Gridsquare source(sourceGrid); const Gridsquare target(targetGrid); double bearing = -1.0; QVERIFY(source.bearingTo(target, bearing)); QCOMPARE(qRound(bearing), expectedRoundedBearing); } void GridsquareTest::bearingSymmetry() { const Gridsquare gridA("JO80AA"); const Gridsquare gridB("JO80AA"); QVERIFY(gridA.isValid()); QVERIFY(gridB.isValid()); double bearingAB = -1.0; double bearingBA = -1.0; QVERIFY(gridA.bearingTo(gridB, bearingAB)); QVERIFY(gridB.bearingTo(gridA, bearingBA)); QCOMPARE(bearingAB, bearingBA); QCOMPARE(bearingAB, 0.0); } QTEST_APPLESS_MAIN(GridsquareTest) #include "tst_gridsquare.moc" ================================================ FILE: tests/HostsPortStringTest/HostsPortStringTest.pro ================================================ QT += testlib core network CONFIG += console testcase c++11 TEMPLATE = app TARGET = tst_hostsportstring INCLUDEPATH += $$PWD/../.. SOURCES += \ tst_hostsportstring.cpp \ ../../data/HostsPortString.cpp HEADERS += \ ../../data/HostsPortString.h ================================================ FILE: tests/HostsPortStringTest/tst_hostsportstring.cpp ================================================ #include #include "data/HostsPortString.h" using AddressExpectation = QList>; Q_DECLARE_METATYPE(AddressExpectation) class HostsPortStringTest : public QObject { Q_OBJECT private slots: void initTestCase(); void regexValidation_data(); void regexValidation(); void parsing_data(); void parsing(); void hasLocalIPWithPort_data(); void hasLocalIPWithPort(); void hostPortAddress_basic(); void hostPortAddress_equality(); }; void HostsPortStringTest::initTestCase() { qRegisterMetaType>>("AddressExpectation"); QLoggingCategory::setFilterRules(QStringLiteral("*.debug=false")); } void HostsPortStringTest::regexValidation_data() { QTest::addColumn("input"); QTest::addColumn("expectedMatch"); struct Case { const char *name; const char *input; bool match; } cases[] = { {"single_address", "127.0.0.1:80", true}, {"multiple_addresses", "192.168.1.1:1 10.0.0.1:65535", true}, {"trailing_space", "10.10.10.10:12345 ", true}, {"empty_string", "", false}, {"missing_port", "127.0.0.1", false}, {"invalid_ip", "300.0.0.1:80", false}, {"hostname", "example.com:80", false} }; for (const Case &c : cases) { QTest::newRow(c.name) << QString::fromLatin1(c.input) << c.match; } } void HostsPortStringTest::regexValidation() { QFETCH(QString, input); QFETCH(bool, expectedMatch); const QRegularExpression regex = HostsPortString::hostsPortRegEx(); const bool actual = regex.match(input).hasMatch(); QCOMPARE(actual, expectedMatch); } void HostsPortStringTest::parsing_data() { QTest::addColumn("input"); QTest::addColumn("expected"); QTest::newRow("empty") << QString() << AddressExpectation{}; AddressExpectation basic; basic << qMakePair(QStringLiteral("127.0.0.1"), quint16(80)) << qMakePair(QStringLiteral("10.0.0.5"), quint16(123)); QTest::newRow("basic_valid") << QStringLiteral("127.0.0.1:80 10.0.0.5:123") << basic; AddressExpectation filtered; filtered << qMakePair(QStringLiteral("127.0.0.1"), quint16(80)) << qMakePair(QStringLiteral("1.1.1.1"), quint16(65535)); QTest::newRow("mixed_tokens") << QStringLiteral("127.0.0.1:80 invalid 192.168.0.1:-1 0.0.0.0:70000 1.1.1.1:65535") << filtered; } void HostsPortStringTest::parsing() { QFETCH(QString, input); QFETCH(AddressExpectation, expected); const HostsPortString hosts(input); const QList actual = hosts.getAddrList(); QCOMPARE(actual.size(), expected.size()); for (int i = 0; i < actual.size(); ++i) { QCOMPARE(actual.at(i).toString(), expected.at(i).first); QCOMPARE(actual.at(i).getPort(), expected.at(i).second); } } void HostsPortStringTest::hasLocalIPWithPort_data() { QTest::addColumn("input"); QTest::addColumn("port"); QTest::addColumn("expected"); QTest::newRow("loopback_match") << QStringLiteral("127.0.0.1:4321") << 4321 << true; QTest::newRow("loopback_wrong_port") << QStringLiteral("127.0.0.1:4321") << 1111 << false; QTest::newRow("wildcard_match") << QStringLiteral("0.0.0.0:5555") << 5555 << true; QTest::newRow("non_local") << QStringLiteral("192.0.2.1:6000") << 6000 << false; } void HostsPortStringTest::hasLocalIPWithPort() { QFETCH(QString, input); QFETCH(int, port); QFETCH(bool, expected); const HostsPortString hosts(input); QCOMPARE(hosts.hasLocalIPWithPort(port), expected); } void HostsPortStringTest::hostPortAddress_basic() { HostPortAddress addr(QStringLiteral("127.0.0.1"), 1234); QCOMPARE(addr.getPort(), quint16(1234)); addr.setPort(4321); QCOMPARE(addr.getPort(), quint16(4321)); QCOMPARE(addr.toString(), QStringLiteral("127.0.0.1")); } void HostsPortStringTest::hostPortAddress_equality() { const HostPortAddress addr1(QStringLiteral("127.0.0.1"), 80); const HostPortAddress addr2(QStringLiteral("127.0.0.1"), 80); const HostPortAddress addrDifferentPort(QStringLiteral("127.0.0.1"), 81); const HostPortAddress addrDifferentHost(QStringLiteral("10.0.0.1"), 80); QVERIFY(addr1 == addr2); QVERIFY(!(addr1 == addrDifferentPort)); QVERIFY(!(addr1 == addrDifferentHost)); } QTEST_APPLESS_MAIN(HostsPortStringTest) #include "tst_hostsportstring.moc" ================================================ FILE: tests/MigrationTest/MigrationTest.pro ================================================ QT += testlib sql core widgets network CONFIG += console testcase c++11 TEMPLATE = app TARGET = tst_migration INCLUDEPATH += $$PWD/../.. SOURCES += \ tst_migration.cpp RESOURCES += \ ../../res/res.qrc HEADERS += \ ../../core/Migration.h ================================================ FILE: tests/MigrationTest/tst_migration.cpp ================================================ #include #include #include #include #include #include #include #include "core/Migration.h" class MigrationSqlTest : public QObject { Q_OBJECT private slots: void initTestCase(); void cleanupTestCase(); void migrateVersion_data(); void migrateVersion(); private: QScopedPointer tempDir; QString dbPath; bool executeSqlFile(int version); int currentVersion() const; }; void MigrationSqlTest::initTestCase() { Q_INIT_RESOURCE(res); tempDir.reset(new QTemporaryDir); QVERIFY(tempDir->isValid()); dbPath = tempDir->filePath(QStringLiteral("migration_sql_test.sqlite")); QSqlDatabase db = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE")); db.setDatabaseName(dbPath); db.setConnectOptions("QSQLITE_ENABLE_REGEXP"); QVERIFY2(db.open(), qPrintable(db.lastError().text())); QSqlQuery query(QStringLiteral("CREATE TABLE schema_versions (version INTEGER PRIMARY KEY, updated TEXT NOT NULL)")); QVERIFY2(query.isActive(), qPrintable(query.lastError().text())); QSqlQuery insert(QStringLiteral("INSERT INTO schema_versions (version, updated) VALUES (0, datetime('now'))")); QVERIFY2(insert.isActive(), qPrintable(insert.lastError().text())); } void MigrationSqlTest::cleanupTestCase() { { QSqlDatabase db = QSqlDatabase::database(); if (db.isValid()) db.close(); } QSqlDatabase::removeDatabase(QSqlDatabase::defaultConnection); } bool MigrationSqlTest::executeSqlFile(int version) { const QString resourceName = QStringLiteral(":/res/sql/migration_%1.sql") .arg(version, 3, 10, QChar('0')); QFile sqlFile(resourceName); if (!sqlFile.open(QIODevice::ReadOnly | QIODevice::Text)) { QWARN(qPrintable(QStringLiteral("Cannot open %1").arg(resourceName))); return false; } const QString sqlContent = QTextStream(&sqlFile).readAll(); sqlFile.close(); const QStringList statements = sqlContent.split('\n').join(QStringLiteral(" ")).split(';'); QSqlDatabase db = QSqlDatabase::database(); QSqlQuery query(db); if (!db.transaction()) return false; for (const QString &statement : statements) { const QString trimmed = statement.trimmed(); if (trimmed.isEmpty()) continue; if (!query.exec(trimmed)) { qWarning() << "SQL execution failed for version" << version << ":" << trimmed << query.lastError(); db.rollback(); return false; } } QSqlQuery versionInsert(db); if (!versionInsert.prepare(QStringLiteral("INSERT INTO schema_versions (version, updated) VALUES (:version, datetime('now'))"))) { db.rollback(); return false; } versionInsert.bindValue(QStringLiteral(":version"), version); if (!versionInsert.exec()) { db.rollback(); return false; } return db.commit(); } int MigrationSqlTest::currentVersion() const { QSqlQuery query(QStringLiteral("SELECT MAX(version) FROM schema_versions")); if (!query.first()) return -1; return query.value(0).toInt(); } class MigrationSqlTest_FriendAccessor { public: static int latestVersion() { return DBSchemaMigration::latestVersion; } }; void MigrationSqlTest::migrateVersion_data() { QTest::addColumn("targetVersion"); const int latestVersion = MigrationSqlTest_FriendAccessor::latestVersion(); for (int version = 1; version <= latestVersion; ++version) { QTest::newRow(QStringLiteral("migration_%1").arg(version, 3, 10, QChar('0')).toUtf8().constData()) << version; } } void MigrationSqlTest::migrateVersion() { QFETCH(int, targetVersion); const int expectedCurrent = targetVersion - 1; QCOMPARE(currentVersion(), expectedCurrent); QVERIFY2(executeSqlFile(targetVersion), qPrintable(QStringLiteral("Migration SQL file %1 failed").arg(targetVersion, 3, 10, QChar('0')))); QCOMPARE(currentVersion(), targetVersion); } QTEST_MAIN(MigrationSqlTest) #include "tst_migration.moc" ================================================ FILE: tests/PasswordCipherTest/PasswordCipherTest.pro ================================================ QT += testlib core CONFIG += console testcase c++11 TEMPLATE = app TARGET = tst_passwordcipher INCLUDEPATH += $$PWD/../.. SOURCES += \ tst_passwordcipher.cpp \ ../../core/PasswordCipher.cpp HEADERS += \ ../../core/PasswordCipher.h # OpenSSL !isEmpty(OPENSSLINCLUDEPATH) { INCLUDEPATH += $$OPENSSLINCLUDEPATH } !isEmpty(OPENSSLLIBPATH) { LIBS += -L$$OPENSSLLIBPATH } win32 { LIBS += -llibcrypto } else { LIBS += -lcrypto } ================================================ FILE: tests/PasswordCipherTest/tst_passwordcipher.cpp ================================================ #include #include #include "core/PasswordCipher.h" class PasswordCipherTest : public QObject { Q_OBJECT private slots: // Basic functionality void encrypt_emptyPlaintext_succeeds(); void encrypt_smallData_encryptsAndDecrypts(); void encrypt_largeData_encryptsAndDecrypts(); void encrypt_binaryData_encryptsAndDecrypts(); void encrypt_unicodePassword_works(); void encrypt_longPassword_works(); // Decryption failures void decrypt_wrongPassword_fails(); void decrypt_invalidBase64_fails(); void decrypt_truncatedData_fails(); void decrypt_corruptedData_fails(); void decrypt_modifiedCiphertext_fails(); void decrypt_modifiedTag_fails(); // Edge cases void encrypt_emptyPassword_succeeds(); void encrypt_sameDataTwice_producesDifferentOutput(); // Security properties void encrypt_outputIsBase64(); void encrypt_outputContainsMagicHeader(); // Benchmarks void encrypt_benchmark(); void decrypt_benchmark(); private: QByteArray generateRandomData(int size); }; QByteArray PasswordCipherTest::generateRandomData(int size) { QByteArray data(size, '\0'); for (int i = 0; i < size; ++i) data[i] = static_cast(QRandomGenerator::global()->bounded(256)); return data; } // ============================================================================ // Basic functionality // ============================================================================ void PasswordCipherTest::encrypt_emptyPlaintext_succeeds() { // PasswordCipher intentionally rejects empty plaintext for security reasons QString password = "test-password"; QByteArray plaintext; // empty QByteArray encrypted; // Empty plaintext should be rejected QVERIFY(!PasswordCipher::encrypt(password, plaintext, encrypted)); QVERIFY(encrypted.isEmpty()); } void PasswordCipherTest::encrypt_smallData_encryptsAndDecrypts() { QString password = "my-secret-password-123"; QByteArray plaintext = "Hello, World! This is a secret message."; QByteArray encrypted; QVERIFY(PasswordCipher::encrypt(password, plaintext, encrypted)); QVERIFY(!encrypted.isEmpty()); QVERIFY(encrypted != plaintext); QByteArray decrypted; QVERIFY(PasswordCipher::decrypt(password, encrypted, decrypted)); QCOMPARE(decrypted, plaintext); } void PasswordCipherTest::encrypt_largeData_encryptsAndDecrypts() { QString password = "password-for-large-data"; QByteArray plaintext = generateRandomData(1024 * 1024); // 1 MB QByteArray encrypted; QVERIFY(PasswordCipher::encrypt(password, plaintext, encrypted)); QVERIFY(!encrypted.isEmpty()); QByteArray decrypted; QVERIFY(PasswordCipher::decrypt(password, encrypted, decrypted)); QCOMPARE(decrypted, plaintext); } void PasswordCipherTest::encrypt_binaryData_encryptsAndDecrypts() { QString password = "binary-data-password"; // Create binary data with all byte values including null bytes QByteArray plaintext; for (int i = 0; i < 256; ++i) plaintext.append(static_cast(i)); QByteArray encrypted; QVERIFY(PasswordCipher::encrypt(password, plaintext, encrypted)); QByteArray decrypted; QVERIFY(PasswordCipher::decrypt(password, encrypted, decrypted)); QCOMPARE(decrypted, plaintext); } void PasswordCipherTest::encrypt_unicodePassword_works() { QString password = QString::fromUtf8("密码测试🔐"); // Chinese + emoji QByteArray plaintext = "Secret data with unicode password"; QByteArray encrypted; QVERIFY(PasswordCipher::encrypt(password, plaintext, encrypted)); QByteArray decrypted; QVERIFY(PasswordCipher::decrypt(password, encrypted, decrypted)); QCOMPARE(decrypted, plaintext); } void PasswordCipherTest::encrypt_longPassword_works() { // Very long password (1000 characters) QString password = QString("a").repeated(1000); QByteArray plaintext = "Data encrypted with very long password"; QByteArray encrypted; QVERIFY(PasswordCipher::encrypt(password, plaintext, encrypted)); QByteArray decrypted; QVERIFY(PasswordCipher::decrypt(password, encrypted, decrypted)); QCOMPARE(decrypted, plaintext); } // ============================================================================ // Decryption failures // ============================================================================ void PasswordCipherTest::decrypt_wrongPassword_fails() { QString correctPassword = "correct-password"; QString wrongPassword = "wrong-password"; QByteArray plaintext = "Secret message"; QByteArray encrypted; QVERIFY(PasswordCipher::encrypt(correctPassword, plaintext, encrypted)); QByteArray decrypted; QVERIFY(!PasswordCipher::decrypt(wrongPassword, encrypted, decrypted)); } void PasswordCipherTest::decrypt_invalidBase64_fails() { QString password = "password"; QByteArray invalidBase64 = "This is not valid base64!!!@@@"; QByteArray decrypted; QVERIFY(!PasswordCipher::decrypt(password, invalidBase64, decrypted)); } void PasswordCipherTest::decrypt_truncatedData_fails() { QString password = "password"; QByteArray plaintext = "Some data to encrypt"; QByteArray encrypted; QVERIFY(PasswordCipher::encrypt(password, plaintext, encrypted)); // Truncate the encrypted data QByteArray truncated = encrypted.left(encrypted.size() / 2); QByteArray decrypted; QVERIFY(!PasswordCipher::decrypt(password, truncated, decrypted)); } void PasswordCipherTest::decrypt_corruptedData_fails() { QString password = "password"; QByteArray plaintext = "Data to be corrupted"; QByteArray encrypted; QVERIFY(PasswordCipher::encrypt(password, plaintext, encrypted)); // Decode, corrupt, re-encode QByteArray decoded = QByteArray::fromBase64(encrypted); if (decoded.size() > 20) decoded[20] = static_cast(decoded[20] ^ 0xFF); // Flip bits in the middle QByteArray corrupted = decoded.toBase64(); QByteArray decrypted; QVERIFY(!PasswordCipher::decrypt(password, corrupted, decrypted)); } void PasswordCipherTest::decrypt_modifiedCiphertext_fails() { QString password = "password"; QByteArray plaintext = "Original plaintext data"; QByteArray encrypted; QVERIFY(PasswordCipher::encrypt(password, plaintext, encrypted)); // Decode and modify ciphertext portion (after header) QByteArray decoded = QByteArray::fromBase64(encrypted); // Header is about 16 bytes + salt(16) + iv(12) = 44 bytes, then ciphertext starts if (decoded.size() > 50) decoded[50] = static_cast(decoded[50] ^ 0xFF); QByteArray modified = decoded.toBase64(); QByteArray decrypted; QVERIFY(!PasswordCipher::decrypt(password, modified, decrypted)); } void PasswordCipherTest::decrypt_modifiedTag_fails() { QString password = "password"; QByteArray plaintext = "Data with protected integrity"; QByteArray encrypted; QVERIFY(PasswordCipher::encrypt(password, plaintext, encrypted)); // Decode and modify the last byte (part of tag) QByteArray decoded = QByteArray::fromBase64(encrypted); if (!decoded.isEmpty()) decoded[decoded.size() - 1] = static_cast(decoded[decoded.size() - 1] ^ 0xFF); QByteArray modified = decoded.toBase64(); QByteArray decrypted; QVERIFY(!PasswordCipher::decrypt(password, modified, decrypted)); } // ============================================================================ // Edge cases // ============================================================================ void PasswordCipherTest::encrypt_emptyPassword_succeeds() { QString password; // empty QByteArray plaintext = "Data with empty password"; QByteArray encrypted; QVERIFY(PasswordCipher::encrypt(password, plaintext, encrypted)); QByteArray decrypted; QVERIFY(PasswordCipher::decrypt(password, encrypted, decrypted)); QCOMPARE(decrypted, plaintext); } void PasswordCipherTest::encrypt_sameDataTwice_producesDifferentOutput() { QString password = "same-password"; QByteArray plaintext = "Same plaintext data"; QByteArray encrypted1, encrypted2; QVERIFY(PasswordCipher::encrypt(password, plaintext, encrypted1)); QVERIFY(PasswordCipher::encrypt(password, plaintext, encrypted2)); // Due to random salt and IV, outputs should be different QVERIFY(encrypted1 != encrypted2); // But both should decrypt to the same plaintext QByteArray decrypted1, decrypted2; QVERIFY(PasswordCipher::decrypt(password, encrypted1, decrypted1)); QVERIFY(PasswordCipher::decrypt(password, encrypted2, decrypted2)); QCOMPARE(decrypted1, plaintext); QCOMPARE(decrypted2, plaintext); } // ============================================================================ // Security properties // ============================================================================ void PasswordCipherTest::encrypt_outputIsBase64() { QString password = "password"; QByteArray plaintext = "Test data"; QByteArray encrypted; QVERIFY(PasswordCipher::encrypt(password, plaintext, encrypted)); // Verify it's valid base64 by decoding QByteArray decoded = QByteArray::fromBase64(encrypted, QByteArray::Base64Encoding); QVERIFY(!decoded.isEmpty()); // Re-encode should match (possibly with padding differences) QByteArray reencoded = decoded.toBase64(); QCOMPARE(reencoded, encrypted); } void PasswordCipherTest::encrypt_outputContainsMagicHeader() { QString password = "password"; QByteArray plaintext = "Test data"; QByteArray encrypted; QVERIFY(PasswordCipher::encrypt(password, plaintext, encrypted)); // Decode and check magic header QByteArray decoded = QByteArray::fromBase64(encrypted); QVERIFY(decoded.size() >= 4); QCOMPARE(decoded.left(4), QByteArray("PMGR")); } // ============================================================================ // Benchmarks // ============================================================================ void PasswordCipherTest::encrypt_benchmark() { QString password = "benchmark-password"; QByteArray plaintext = generateRandomData(10000); // 10 KB QByteArray encrypted; QBENCHMARK { PasswordCipher::encrypt(password, plaintext, encrypted); } } void PasswordCipherTest::decrypt_benchmark() { QString password = "benchmark-password"; QByteArray plaintext = generateRandomData(10000); // 10 KB QByteArray encrypted; QVERIFY(PasswordCipher::encrypt(password, plaintext, encrypted)); QByteArray decrypted; QBENCHMARK { PasswordCipher::decrypt(password, encrypted, decrypted); } } int main(int argc, char **argv) { QCoreApplication app(argc, argv); PasswordCipherTest tc; return QTest::qExec(&tc, argc, argv); } #include "tst_passwordcipher.moc" ================================================ FILE: tests/QuadKeyCacheTest/QuadKeyCacheTest.pro ================================================ QT += testlib core CONFIG += console testcase c++11 TEMPLATE = app TARGET = tst_quadkeycache INCLUDEPATH += $$PWD/../.. SOURCES += \ tst_quadkeycache.cpp HEADERS += \ ../../core/QuadKeyCache.h ================================================ FILE: tests/QuadKeyCacheTest/tst_quadkeycache.cpp ================================================ #include #include "core/QuadKeyCache.h" namespace { struct DummyValue { DummyValue(int v = 0) : payload(v) {} int payload = 0; }; } class QuadKeyCacheTest : public QObject { Q_OBJECT private slots: void initTestCase(); void insertAndRetrieve(); void containsCheck(); void invalidateByPair(); void evictionFollowsCapacity(); }; void QuadKeyCacheTest::initTestCase() { QLoggingCategory::setFilterRules(QStringLiteral("*.debug=false")); } void QuadKeyCacheTest::insertAndRetrieve() { QuadKeyCache cache; auto value1 = new DummyValue{42}; auto value2 = new DummyValue{73}; cache.insert(1, 10, QStringLiteral("A"), QStringLiteral("alpha"), value1); cache.insert(2, 20, QStringLiteral("B"), QStringLiteral("beta"), value2); DummyValue *retrieved1 = cache.value(1, 10, QStringLiteral("A"), QStringLiteral("alpha")); DummyValue *retrieved2 = cache.value(2, 20, QStringLiteral("B"), QStringLiteral("beta")); QVERIFY(retrieved1 != nullptr); QVERIFY(retrieved2 != nullptr); QCOMPARE(retrieved1->payload, 42); QCOMPARE(retrieved2->payload, 73); QCOMPARE(cache.size(), 2); } void QuadKeyCacheTest::containsCheck() { QuadKeyCache cache; auto value = new DummyValue{5}; cache.insert(3, 30, QStringLiteral("C"), QStringLiteral("gamma"), value); QVERIFY(cache.contains(3, 30, QStringLiteral("C"), QStringLiteral("gamma"))); QVERIFY(!cache.contains(4, 40, QStringLiteral("D"), QStringLiteral("delta"))); } void QuadKeyCacheTest::invalidateByPair() { QuadKeyCache cache; cache.insert(1, 1, QStringLiteral("X"), QStringLiteral("x"), new DummyValue{1}); cache.insert(1, 1, QStringLiteral("Y"), QStringLiteral("y"), new DummyValue{2}); cache.insert(2, 2, QStringLiteral("X"), QStringLiteral("x"), new DummyValue{3}); QCOMPARE(cache.size(), 3); cache.invalidate(1, 1); QCOMPARE(cache.size(), 1); QVERIFY(cache.contains(2, 2, QStringLiteral("X"), QStringLiteral("x"))); QVERIFY(!cache.contains(1, 1, QStringLiteral("X"), QStringLiteral("x"))); QVERIFY(!cache.contains(1, 1, QStringLiteral("Y"), QStringLiteral("y"))); } void QuadKeyCacheTest::evictionFollowsCapacity() { QuadKeyCache cache; cache.setMaxCost(2); cache.insert(1, 1, QStringLiteral("A"), QStringLiteral("a"), new DummyValue{1}); cache.insert(2, 2, QStringLiteral("B"), QStringLiteral("b"), new DummyValue{2}); cache.insert(3, 3, QStringLiteral("C"), QStringLiteral("c"), new DummyValue{3}); QCOMPARE(cache.size(), 2); QVERIFY(cache.value(1, 1, QStringLiteral("A"), QStringLiteral("a")) == nullptr); QVERIFY(cache.value(2, 2, QStringLiteral("B"), QStringLiteral("b")) != nullptr); QVERIFY(cache.value(3, 3, QStringLiteral("C"), QStringLiteral("c")) != nullptr); } QTEST_APPLESS_MAIN(QuadKeyCacheTest) #include "tst_quadkeycache.moc" ================================================ FILE: tests/RigctldManagerTest/RigctldManagerTest.pro ================================================ QT += testlib core network CONFIG += console testcase c++11 TEMPLATE = app TARGET = tst_rigctldmanager # Local stubs first, then project includes INCLUDEPATH += $$PWD INCLUDEPATH += $$PWD/../.. SOURCES += \ tst_rigctldmanager.cpp \ ../../rig/RigctldManager.cpp \ ../../data/SerialPort.cpp \ test_stubs.cpp HEADERS += \ ../../rig/RigctldManager.h \ ../../data/SerialPort.h \ data/RigProfile.h \ core/debug.h # Hamlib !isEmpty(HAMLIBINCLUDEPATH) { INCLUDEPATH += $$HAMLIBINCLUDEPATH } isEmpty(HAMLIBVERSION_MAJOR): HAMLIBVERSION_MAJOR = 4 isEmpty(HAMLIBVERSION_MINOR): HAMLIBVERSION_MINOR = 0 isEmpty(HAMLIBVERSION_PATCH): HAMLIBVERSION_PATCH = 0 DEFINES += HAMLIBVERSION_MAJOR=$$HAMLIBVERSION_MAJOR DEFINES += HAMLIBVERSION_MINOR=$$HAMLIBVERSION_MINOR DEFINES += HAMLIBVERSION_PATCH=$$HAMLIBVERSION_PATCH # pthreads (needed by hamlib headers on Windows) !isEmpty(PTHREADINCLUDEPATH) { INCLUDEPATH += $$PTHREADINCLUDEPATH } !isEmpty(PTHREADLIBPATH) { LIBS += -L$$PTHREADLIBPATH } # Hamlib link !isEmpty(HAMLIBLIBPATH) { LIBS += -L$$HAMLIBLIBPATH } win32: LIBS += -lws2_32 -llibhamlib-4 unix: LIBS += -lhamlib ================================================ FILE: tests/RigctldManagerTest/core/debug.h ================================================ #ifndef TEST_CORE_DEBUG_H #define TEST_CORE_DEBUG_H #include // Declare logging categories Q_DECLARE_LOGGING_CATEGORY(runtime) Q_DECLARE_LOGGING_CATEGORY(function_parameters) // Empty macros for testing #define FCT_IDENTIFICATION #define MODULE_IDENTIFICATION(x) #endif // TEST_CORE_DEBUG_H ================================================ FILE: tests/RigctldManagerTest/data/RigProfile.h ================================================ #ifndef TEST_DATA_RIGPROFILE_H #define TEST_DATA_RIGPROFILE_H #include // Minimal RigProfile stub for testing RigctldManager class RigProfile { public: enum rigPortType { SERIAL_ATTACHED, NETWORK_ATTACHED, SPECIAL_OMNIRIG_ATTACHED }; RigProfile() { model = 1; netport = 0; baudrate = 0; databits = 0; stopbits = 0.0; shareRigctld = false; rigctldPort = 4532; civAddr = -1; } QString profileName; qint32 model; QString portPath; QString hostname; quint16 netport; quint32 baudrate; quint8 databits; float stopbits; QString flowcontrol; QString parity; QString dtr; QString rts; qint16 civAddr; bool shareRigctld; quint16 rigctldPort; QString rigctldPath; QString rigctldArgs; rigPortType getPortType() const { if (!hostname.isEmpty() && portPath.isEmpty()) return NETWORK_ATTACHED; return SERIAL_ATTACHED; } }; #endif // TEST_DATA_RIGPROFILE_H ================================================ FILE: tests/RigctldManagerTest/test_stubs.cpp ================================================ // Stub implementations for testing RigctldManager #include // Debug category definitions Q_LOGGING_CATEGORY(runtime, "qlog.runtime") Q_LOGGING_CATEGORY(function_parameters, "qlog.function_parameters") ================================================ FILE: tests/RigctldManagerTest/tst_rigctldmanager.cpp ================================================ #include #include #include #include #include #include #include "rig/RigctldManager.h" #include "data/RigProfile.h" class RigctldManagerTest : public QObject { Q_OBJECT private slots: void initTestCase(); void cleanupTestCase(); // findRigctldPath tests void findRigctldPath_returnsNonEmptyIfInstalled(); void findRigctldPath_prefersAppDirectory(); // buildArguments tests (via start with mock) void buildArguments_includesModel(); void buildArguments_includesPort(); void buildArguments_includesSerialSettings(); void buildArguments_includesAdditionalArgs(); // Lifecycle tests void isRunning_initiallyFalse(); void start_failsWithInvalidPath(); void start_failsWithInvalidProfile(); void getConnectHost_returnsLocalhost(); void getConnectPort_returnsConfiguredPort(); // getVersion tests void getVersion_returnsInvalidForNonexistentPath(); void getVersion_returnsValidIfInstalled(); void getVersion_autodetectsPath(); // Integration test (skipped if rigctld not available) void start_stop_integration(); private: QString findRigctld(); bool rigctldAvailable = false; QString rigctldPath; }; void RigctldManagerTest::initTestCase() { rigctldPath = RigctldManager::findRigctldPath(); rigctldAvailable = !rigctldPath.isEmpty(); if (!rigctldAvailable) { qWarning() << "rigctld not found - some tests will be skipped"; } else { qDebug() << "Found rigctld at:" << rigctldPath; } } void RigctldManagerTest::cleanupTestCase() { } QString RigctldManagerTest::findRigctld() { return RigctldManager::findRigctldPath(); } // ============================================================================ // findRigctldPath tests // ============================================================================ void RigctldManagerTest::findRigctldPath_returnsNonEmptyIfInstalled() { // This test passes if rigctld is installed, otherwise we just note it QString path = RigctldManager::findRigctldPath(); if (path.isEmpty()) { QSKIP("rigctld not installed on this system"); } QVERIFY(QFile::exists(path)); } void RigctldManagerTest::findRigctldPath_prefersAppDirectory() { // Create a mock rigctld in app directory QString appDir = QCoreApplication::applicationDirPath(); #ifdef Q_OS_WIN QString mockPath = appDir + "/rigctld.exe"; #else QString mockPath = appDir + "/rigctld"; #endif // Create empty file to simulate rigctld QFile mockFile(mockPath); bool created = mockFile.open(QIODevice::WriteOnly); if (!created) { QSKIP("Cannot create mock rigctld in app directory"); } mockFile.close(); // Now findRigctldPath should return the app directory path QString foundPath = RigctldManager::findRigctldPath(); QCOMPARE(foundPath, mockPath); // Cleanup QFile::remove(mockPath); } // ============================================================================ // buildArguments tests (indirect via profile inspection) // ============================================================================ void RigctldManagerTest::buildArguments_includesModel() { // We can't directly test buildArguments (private), but we can verify // that start() uses the correct model from profile // This is more of a documentation test RigProfile profile; profile.model = 1234; profile.rigctldPort = 14532; profile.portPath = "/dev/ttyUSB0"; // Verify profile is set correctly QCOMPARE(profile.model, 1234); } void RigctldManagerTest::buildArguments_includesPort() { RigProfile profile; profile.rigctldPort = 5000; QCOMPARE(profile.rigctldPort, static_cast(5000)); } void RigctldManagerTest::buildArguments_includesSerialSettings() { RigProfile profile; profile.baudrate = 9600; profile.databits = 8; profile.stopbits = 1; profile.parity = "None"; profile.flowcontrol = "None"; QCOMPARE(profile.baudrate, 9600u); QCOMPARE(profile.databits, static_cast(8)); } void RigctldManagerTest::buildArguments_includesAdditionalArgs() { RigProfile profile; profile.rigctldArgs = "-v -v --debug"; QCOMPARE(profile.rigctldArgs, QString("-v -v --debug")); } // ============================================================================ // Lifecycle tests // ============================================================================ void RigctldManagerTest::isRunning_initiallyFalse() { RigctldManager manager; QVERIFY(!manager.isRunning()); } void RigctldManagerTest::start_failsWithInvalidPath() { RigctldManager manager; QSignalSpy errorSpy(&manager, &RigctldManager::errorOccurred); RigProfile profile; profile.model = 1; profile.rigctldPort = 14532; profile.rigctldPath = "/nonexistent/path/to/rigctld"; profile.portPath = "/dev/ttyUSB0"; bool result = manager.start(profile); QVERIFY(!result); QVERIFY(!manager.isRunning()); } void RigctldManagerTest::start_failsWithInvalidProfile() { if (!rigctldAvailable) { QSKIP("rigctld not available"); } RigctldManager manager; RigProfile profile; profile.model = 1; // Dummy rig - should fail to open profile.rigctldPort = 14533; profile.portPath = "/dev/nonexistent_port"; // This should fail because the serial port doesn't exist bool result = manager.start(profile); // Even if rigctld starts, it should fail to connect to the rig // The behavior depends on how rigctld handles invalid ports // We just verify the manager handles this gracefully if (result) { manager.stop(); } QVERIFY(!manager.isRunning()); } void RigctldManagerTest::getConnectHost_returnsLocalhost() { RigctldManager manager; QCOMPARE(manager.getConnectHost(), QString("127.0.0.1")); } void RigctldManagerTest::getConnectPort_returnsConfiguredPort() { RigctldManager manager; // Default port should be 4532 QCOMPARE(manager.getConnectPort(), static_cast(4532)); } // ============================================================================ // getVersion tests // ============================================================================ void RigctldManagerTest::getVersion_returnsInvalidForNonexistentPath() { RigctldVersion version = RigctldManager::getVersion("/nonexistent/path/to/rigctld"); QVERIFY(!version.isValid()); QCOMPARE(version.major, -1); QCOMPARE(version.minor, -1); QCOMPARE(version.patch, -1); } void RigctldManagerTest::getVersion_returnsValidIfInstalled() { if ( !rigctldAvailable ) QSKIP("rigctld not available"); RigctldVersion version = RigctldManager::getVersion(rigctldPath); QVERIFY(version.isValid()); QVERIFY(version.major >= 0); QVERIFY(version.minor >= 0); QVERIFY(version.patch >= 0); qDebug() << "rigctld version:" << version.major << "." << version.minor << "." << version.patch; } void RigctldManagerTest::getVersion_autodetectsPath() { if ( !rigctldAvailable ) QSKIP("rigctld not available"); // Call without path - should autodetect RigctldVersion version = RigctldManager::getVersion(); QVERIFY(version.isValid()); QVERIFY(version.major >= 0); } // ============================================================================ // Integration test // ============================================================================ void RigctldManagerTest::start_stop_integration() { if (!rigctldAvailable) { QSKIP("rigctld not available for integration test"); } RigctldManager manager; QSignalSpy startedSpy(&manager, &RigctldManager::started); QSignalSpy stoppedSpy(&manager, &RigctldManager::stopped); QSignalSpy errorSpy(&manager, &RigctldManager::errorOccurred); RigProfile profile; profile.model = 1; // Dummy rig (Hamlib model 1 = Dummy) profile.rigctldPort = 14534; profile.rigctldPath = rigctldPath; // For dummy rig, we don't need a real port bool result = manager.start(profile); if (!result) { // If start failed, check why if (!errorSpy.isEmpty()) { qWarning() << "Start failed with error:" << errorSpy.first().first().toString(); } QSKIP("Could not start rigctld with dummy rig"); } QVERIFY(result); QVERIFY(manager.isRunning()); QCOMPARE(manager.getConnectPort(), static_cast(14534)); // Verify we can connect to the port QTcpSocket socket; socket.connectToHost("127.0.0.1", 14534); bool connected = socket.waitForConnected(2000); QVERIFY(connected); socket.disconnectFromHost(); // Stop manager.stop(); QVERIFY(!manager.isRunning()); // Verify port is no longer listening QTcpSocket socket2; socket2.connectToHost("127.0.0.1", 14534); bool stillConnected = socket2.waitForConnected(1000); QVERIFY(!stillConnected); } int main(int argc, char **argv) { QCoreApplication app(argc, argv); RigctldManagerTest tc; return QTest::qExec(&tc, argc, argv); } #include "tst_rigctldmanager.moc" ================================================ FILE: tests/tests.pro ================================================ TEMPLATE = subdirs CONFIG += ordered SUBDIRS += CallsignTest \ CredentialStoreTest \ DataTest \ FileCompressorTest \ GridsquareTest \ BandPlanTest \ AlertEvaluatorTest \ DxServerStringTest \ HostsPortStringTest \ MigrationTest \ PasswordCipherTest \ QuadKeyCacheTest \ RigctldManagerTest ================================================ FILE: ui/ActivityEditor.cpp ================================================ #include #include "ActivityEditor.h" #include "ui_ActivityEditor.h" #include "core/debug.h" #include "ui/NewContactWidget.h" #include "ui/MainWindow.h" #include "data/StationProfile.h" #include "data/AntProfile.h" #include "data/RigProfile.h" #include "data/RotProfile.h" #include "models/LogbookModel.h" #include "data/Data.h" #include "data/ActivityProfile.h" MODULE_IDENTIFICATION("qlog.ui.mainlayouteditor"); ActivityEditor::ActivityEditor(const QString &activityName, QWidget *parent) : QDialog(parent), ui(new Ui::ActivityEditor), availableFieldsModel(new StringListModel(this)), qsoRowAFieldsModel(new StringListModel(this)), qsoRowBFieldsModel(new StringListModel(this)), detailColAFieldsModel(new StringListModel(this)), detailColBFieldsModel(new StringListModel(this)), detailColCFieldsModel(new StringListModel(this)), dynamicWidgets(new NewContactDynamicWidgets(false, this)) { FCT_IDENTIFICATION; ui->setupUi(this); #if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) // disabled for QT 5.12 (ubuntu 20.04) due to issue in QT // moveRow does not work ui->qsoRowADownButton->setVisible(false); ui->qsoRowAUpButton->setVisible(false); ui->qsoRowBDownButton->setVisible(false); ui->qsoRowBUpButton->setVisible(false); ui->detailColADownButton->setVisible(false); ui->detailColAUpButton->setVisible(false); ui->detailColBDownButton->setVisible(false); ui->detailColBUpButton->setVisible(false); ui->detailColCDownButton->setVisible(false); ui->detailColCUpButton->setVisible(false); #endif availableFieldsModel->setStringList(dynamicWidgets->getAllFieldLabelNames()); availableFieldsModel->sort(0); ui->availableFieldsListView->setModel(availableFieldsModel); ui->qsoRowAFieldsListView->setModel(qsoRowAFieldsModel); ui->qsoRowBFieldsListView->setModel(qsoRowBFieldsModel); ui->detailColAFieldsListView->setModel(detailColAFieldsModel); ui->detailColBFieldsListView->setModel(detailColBFieldsModel); ui->detailColCFieldsListView->setModel(detailColCFieldsModel); connectQSORowButtons(); connectDetailColsButtons(); if ( ! activityName.isEmpty() ) { MainLayoutProfile profile = MainLayoutProfilesManager::instance()->getProfile(activityName); ui->activityNameEdit->setEnabled(false); ui->activityNameEdit->setText(profile.profileName); fillWidgets(profile); } else fillWidgets(MainLayoutProfile::getClassicLayout()); setupValuesTab(activityName); } ActivityEditor::~ActivityEditor() { delete ui; delete dynamicWidgets; } void ActivityEditor::save() { FCT_IDENTIFICATION; if ( ui->activityNameEdit->text().isEmpty() ) { ui->activityNameEdit->setPlaceholderText(tr("Must not be empty")); return; } if ( activityNameExists(ui->activityNameEdit->text()) ) { QMessageBox::warning(nullptr, QMessageBox::tr("QLog Info"), QMessageBox::tr("Activity name is already exists.")); return; } /********************** * Save Layout profile **********************/ MainLayoutProfile profile; profile.profileName = ui->activityNameEdit->text(); profile.rowA = getFieldIndexes(qsoRowAFieldsModel); profile.rowB = getFieldIndexes(qsoRowBFieldsModel); profile.detailColA = getFieldIndexes(detailColAFieldsModel); profile.detailColB = getFieldIndexes(detailColBFieldsModel); profile.detailColC = getFieldIndexes(detailColCFieldsModel); profile.mainGeometry = mainGeometry; profile.mainState = mainState; profile.addlBandmaps = addlBandmaps; profile.darkMode = darkMode; MainLayoutProfilesManager::instance()->addProfile(profile.profileName, profile); MainLayoutProfilesManager::instance()->save(); /*********************** * Save Activity profile ***********************/ ActivityProfile activity; activity.profileName = ui->activityNameEdit->text(); auto insertProfile = [&](ActivityProfile::ProfileType profileType, bool save, const QString &profileName, QCheckBox* connectCheckbox = nullptr) { if ( save ) { ActivityProfile::ProfileRecord rec; rec.name = profileName; if ( connectCheckbox ) rec.params[ActivityProfile::ProfileParamType::CONNECT] = connectCheckbox->isChecked(); activity.profiles.insert(profileType, rec); } }; auto insertParam = [&] (LogbookModel::ColumnID fieldID, QCheckBox* profileCheckbox, QVariant value) { if ( profileCheckbox->isChecked() ) activity.fieldValues.insert(fieldID, value); }; insertProfile(ActivityProfile::ProfileType::MAIN_LAYOUT_PROFILE, true, ui->activityNameEdit->text()); insertProfile(ActivityProfile::ProfileType::ANTENNA_PROFILE, ui->antennaProfileCheckbox->isChecked(), ui->antennaProfileCombo->currentText()); insertProfile(ActivityProfile::ProfileType::STATION_PROFILE, ui->stationProfileCheckbox->isChecked(), ui->stationProfileCombo->currentText()); insertProfile(ActivityProfile::ProfileType::RIG_PROFILE, ui->rigProfileCheckbox->isChecked(), ui->rigProfileCombo->currentText(), ui->rigAutoconnectCheckbox); insertProfile(ActivityProfile::ProfileType::ROT_PROFILE, ui->rotatorProfileCheckbox->isChecked(), ui->rotatorProfileCombo->currentText(), ui->rotatorAutoconnectCheckbox); insertParam(LogbookModel::COLUMN_CONTEST_ID, ui->contestIDCheckbox, ui->contestIDEdit->text()); insertParam(LogbookModel::COLUMN_PROP_MODE, ui->propagationModeCheckbox, ui->propagationModeCombo->currentText()); insertParam(LogbookModel::COLUMN_SAT_MODE, ui->satModeCheckbox, ui->satModeCombo->currentText()); insertParam(LogbookModel::COLUMN_SAT_NAME, ui->satNameCheckbox, ui->satNameEdit->text()); insertParam(LogbookModel::COLUMN_STX_STRING, ui->stxStringCheckbox, ui->stxStringEdit->text()); ActivityProfilesManager::instance()->addProfile(ui->activityNameEdit->text(), activity); ActivityProfilesManager::instance()->save(); accept(); } void ActivityEditor::profileNameChanged(const QString &profileName) { FCT_IDENTIFICATION; QPalette p; p.setColor(QPalette::Text, ( activityNameExists(profileName) ) ? Qt::red : qApp->palette().text().color()); ui->activityNameEdit->setPalette(p); } void ActivityEditor::clearMainLayoutClick() { FCT_IDENTIFICATION; mainGeometry = QByteArray(); mainState = QByteArray(); addlBandmaps = QList>(); darkMode = false; ui->mainLayoutStateLabel->setText(statusUnSavedText); ui->mainLayoutClearButton->setEnabled(false); } void ActivityEditor::setValueState() { FCT_IDENTIFICATION; ui->antennaProfileCombo->setEnabled(ui->antennaProfileCheckbox->isChecked()); ui->stationProfileCombo->setEnabled(ui->stationProfileCheckbox->isChecked()); ui->rigProfileCombo->setEnabled(ui->rigProfileCheckbox->isChecked()); ui->rigAutoconnectCheckbox->setEnabled(ui->rigProfileCheckbox->isChecked()); ui->rotatorProfileCombo->setEnabled(ui->rotatorProfileCheckbox->isChecked()); ui->rotatorAutoconnectCheckbox->setEnabled(ui->rotatorProfileCheckbox->isChecked()); bool isContestActive = !availableFieldsModel->stringList().contains(LogbookModel::getFieldNameTranslation(LogbookModel::COLUMN_CONTEST_ID)); bool isSTXStringActive = !availableFieldsModel->stringList().contains(LogbookModel::getFieldNameTranslation(LogbookModel::COLUMN_STX_STRING)); if ( !isContestActive ) ui->contestIDCheckbox->setChecked(false); ui->contestIDCheckbox->setVisible(isContestActive); ui->contestIDEdit->setVisible(isContestActive); ui->contestIDEdit->setEnabled(ui->contestIDCheckbox->isChecked()); if ( !isSTXStringActive ) ui->stxStringCheckbox->setChecked(false); ui->stxStringCheckbox->setVisible(isSTXStringActive); ui->stxStringEdit->setVisible(isSTXStringActive); ui->stxStringEdit->setEnabled(ui->stxStringCheckbox->isChecked()); ui->propagationModeCombo->setEnabled(ui->propagationModeCheckbox->isChecked()); bool isSatActive = ui->propagationModeCombo->currentText() == Data::instance()->propagationModeIDToText("SAT") && ui->propagationModeCheckbox->isChecked(); if ( !isSatActive ) { ui->satModeCheckbox->setChecked(false); ui->satNameCheckbox->setChecked(false); } ui->satModeCheckbox->setVisible(isSatActive); ui->satModeCombo->setVisible(isSatActive); ui->satModeCombo->setEnabled(ui->satModeCheckbox->isChecked()); ui->satNameCheckbox->setVisible(isSatActive); ui->satNameEdit->setVisible(isSatActive); ui->satNameEdit->setEnabled(ui->satNameCheckbox->isChecked()); } void ActivityEditor::moveField(StringListModel *source, StringListModel *destination, const QModelIndexList &sourceIndexList) { FCT_IDENTIFICATION; QModelIndexList selectedIndexes = sourceIndexList; if ( selectedIndexes.isEmpty() ) return; std::sort(selectedIndexes.begin(), selectedIndexes.end(), [](const QModelIndex &a, const QModelIndex &b) { return a.row() > b.row(); }); for ( const QModelIndex &index : sourceIndexList ) destination->append(source->data(index).toString()); /* Delete the sorted index list becuase without sorting * it deletes wrong records */ for ( const QModelIndex &index : selectedIndexes ) source->deleteItem(index); setValueState(); } void ActivityEditor::connectQSORowButtons() { FCT_IDENTIFICATION; // Connect buttons for QSO Row A connectMoveButtons(ui->qsoRowADownButton, ui->qsoRowAUpButton, ui->qsoRowAFieldsListView, qsoRowAFieldsModel); connectFieldButtons(ui->moveToQSORowAButton, ui->removeFromQSORowAButton, qsoRowAFieldsModel, ui->qsoRowAFieldsListView); // Connect buttons for QSO Row B connectMoveButtons(ui->qsoRowBDownButton, ui->qsoRowBUpButton, ui->qsoRowBFieldsListView, qsoRowBFieldsModel); connectFieldButtons(ui->moveToQSORowBButton, ui->removeFromQSORowBButton, qsoRowBFieldsModel, ui->qsoRowBFieldsListView); } void ActivityEditor::connectDetailColsButtons() { FCT_IDENTIFICATION; connectMoveButtons(ui->detailColADownButton, ui->detailColAUpButton, ui->detailColAFieldsListView, detailColAFieldsModel); connectFieldButtons(ui->moveToDetailColAButton, ui->removeFromDetailColAButton, detailColAFieldsModel, ui->detailColAFieldsListView); connectMoveButtons(ui->detailColBDownButton, ui->detailColBUpButton, ui->detailColBFieldsListView, detailColBFieldsModel); connectFieldButtons(ui->moveToDetailColBButton, ui->removeFromDetailColBButton, detailColBFieldsModel, ui->detailColBFieldsListView); connectMoveButtons(ui->detailColCDownButton, ui->detailColCUpButton, ui->detailColCFieldsListView, detailColCFieldsModel); connectFieldButtons(ui->moveToDetailColCButton, ui->removeFromDetailColCButton, detailColCFieldsModel, ui->detailColCFieldsListView); } void ActivityEditor::connectMoveButtons(QPushButton *downButton, QPushButton *upButton, QListView *listView, StringListModel *model) { FCT_IDENTIFICATION; connect(downButton, &QPushButton::clicked, this, [listView, model]() { const QModelIndexList &modelList = listView->selectionModel()->selectedRows(); if ( !modelList.isEmpty() ) model->moveDown(modelList.at(0)); }); connect(upButton, &QPushButton::clicked, this, [listView, model]() { const QModelIndexList &modelList = listView->selectionModel()->selectedRows(); if ( !modelList.isEmpty() ) model->moveUp(modelList.at(0)); }); } void ActivityEditor::connectFieldButtons(QPushButton *moveToButton, QPushButton *removeButton, StringListModel *targetModel, QListView *targetListView) { FCT_IDENTIFICATION; connect(moveToButton, &QPushButton::clicked, this, [this, targetModel]() { moveField(availableFieldsModel, targetModel, ui->availableFieldsListView->selectionModel()->selectedIndexes()); }); connect(removeButton, &QPushButton::clicked, this, [this, targetModel, targetListView]() { moveField(targetModel, availableFieldsModel, targetListView->selectionModel()->selectedRows()); }); } QList ActivityEditor::getFieldIndexes(StringListModel *model) { FCT_IDENTIFICATION; const QStringList &list = model->stringList(); QList ret; for ( const QString &fieldName : list ) { int index = dynamicWidgets->getIndex4FieldLabelName(fieldName); if ( index >= 0 ) ret << index; } return ret; } void ActivityEditor::setupValuesTab(const QString &activityName) { FCT_IDENTIFICATION; auto assignCompleter = [&] (QLineEdit *field, const QStringList &list) { QStringListModel *model = new QStringListModel(list); QCompleter *completer = new QCompleter(model, field); model->setParent(completer); completer->setCaseSensitivity(Qt::CaseInsensitive); completer->setFilterMode(Qt::MatchStartsWith); field->setCompleter(completer); }; auto assignTableCompleter = [&] (QLineEdit *field, const QString &tableName) { QSqlTableModel* model = new QSqlTableModel(); model->setTable(tableName); QCompleter *completer = new QCompleter(model, field); model->setParent(completer); completer->setCaseSensitivity(Qt::CaseInsensitive); completer->setFilterMode(Qt::MatchStartsWith); field->setCompleter(completer); model->select(); }; auto assignModel = [&] (QComboBox *field, const QStringList &list) { QStringListModel *model = new QStringListModel(list, field); field->setModel(model); }; auto setProfileVisible = [&] (QCheckBox *checkbox, QComboBox *field, QCheckBox *connectCheckbox = nullptr) { if ( field->count() == 0) { checkbox->setChecked(false); checkbox->setVisible(false); field->setVisible(false); if ( connectCheckbox ) connectCheckbox->setVisible(false); } else { checkbox->setVisible(true); field->setVisible(true); if ( connectCheckbox ) connectCheckbox->setVisible(true); } }; auto loadProfileValue = [&] (const ActivityProfile &activityProfile, ActivityProfile::ProfileType profileType, QCheckBox *profileCheckbox, QComboBox *profileCombo, QCheckBox *connectCheckbox = nullptr) { auto currProfile = activityProfile.profiles.value(profileType); int index = profileCombo->findText(currProfile.name); profileCheckbox->setChecked(index != -1); if ( index != -1 ) { profileCombo->setCurrentIndex(index); if ( connectCheckbox ) connectCheckbox->setChecked(activityProfile.getProfileParam(profileType, ActivityProfile::ProfileParamType::CONNECT).toBool()); } }; ui->stationProfileCombo->addItems(StationProfilesManager::instance()->profileNameList()); ui->antennaProfileCombo->addItems(AntProfilesManager::instance()->profileNameList()); ui->rigProfileCombo->addItems(RigProfilesManager::instance()->profileNameList()); ui->rotatorProfileCombo->addItems(RotProfilesManager::instance()->profileNameList()); setProfileVisible(ui->antennaProfileCheckbox, ui->antennaProfileCombo); setProfileVisible(ui->stationProfileCheckbox, ui->stationProfileCombo); setProfileVisible(ui->rigProfileCheckbox, ui->rigProfileCombo, ui->rigAutoconnectCheckbox); setProfileVisible(ui->rotatorProfileCheckbox, ui->rotatorProfileCombo, ui->rotatorAutoconnectCheckbox); ui->contestIDCheckbox->setText(LogbookModel::getFieldNameTranslation(LogbookModel::COLUMN_CONTEST_ID)); ui->propagationModeCheckbox->setText(LogbookModel::getFieldNameTranslation(LogbookModel::COLUMN_PROP_MODE)); ui->satModeCheckbox->setText(LogbookModel::getFieldNameTranslation(LogbookModel::COLUMN_SAT_MODE)); ui->satNameCheckbox->setText(LogbookModel::getFieldNameTranslation(LogbookModel::COLUMN_SAT_NAME)); ui->stxStringCheckbox->setText(LogbookModel::getFieldNameTranslation(LogbookModel::COLUMN_STX_STRING)); assignCompleter(ui->contestIDEdit, Data::instance()->contestList()); assignTableCompleter(ui->satNameEdit, "sat_info"); assignModel(ui->propagationModeCombo, Data::instance()->propagationModesList()); assignModel(ui->satModeCombo, Data::instance()->satModeList()); if ( !activityName.isEmpty() ) { const ActivityProfile &activity = ActivityProfilesManager::instance()->getProfile(activityName); loadProfileValue(activity, ActivityProfile::ProfileType::ANTENNA_PROFILE, ui->antennaProfileCheckbox, ui->antennaProfileCombo); loadProfileValue(activity, ActivityProfile::ProfileType::STATION_PROFILE, ui->stationProfileCheckbox, ui->stationProfileCombo); loadProfileValue(activity, ActivityProfile::ProfileType::RIG_PROFILE, ui->rigProfileCheckbox, ui->rigProfileCombo, ui->rigAutoconnectCheckbox); loadProfileValue(activity, ActivityProfile::ProfileType::ROT_PROFILE, ui->rotatorProfileCheckbox, ui->rotatorProfileCombo, ui->rotatorAutoconnectCheckbox); for ( auto i = activity.fieldValues.begin(); i != activity.fieldValues.end(); i++ ) { switch (i.key()) { case LogbookModel::COLUMN_CONTEST_ID: ui->contestIDCheckbox->setChecked(true); ui->contestIDEdit->setText(i.value().toString()); break; case LogbookModel::COLUMN_PROP_MODE: ui->propagationModeCheckbox->setChecked(true); ui->propagationModeCombo->setCurrentText(i.value().toString()); break; case LogbookModel::COLUMN_SAT_MODE: ui->satModeCheckbox->setChecked(true); ui->satModeCombo->setCurrentText(i.value().toString()); break; case LogbookModel::COLUMN_SAT_NAME: ui->satNameCheckbox->setChecked(true); ui->satNameEdit->setText(i.value().toString()); break; case LogbookModel::COLUMN_STX_STRING: ui->stxStringCheckbox->setChecked(true); ui->stxStringEdit->setText(i.value().toString()); break; default: qWarning() << "Unsupported Parameter" << i.key(); } } } setValueState(); } void ActivityEditor::fillWidgets(const MainLayoutProfile &profile) { FCT_IDENTIFICATION; auto processFields = [this](const QList& fieldIndices, StringListModel* targetModel) { for (int fieldIndex : fieldIndices) { const QString &fieldName = dynamicWidgets->getFieldLabelName4Index(fieldIndex); targetModel->append(fieldName); availableFieldsModel->deleteItem(fieldName); } }; // Process each row and detail column using the helper processFields(static_cast&>(profile.rowA), qsoRowAFieldsModel); processFields(static_cast&>(profile.rowB), qsoRowBFieldsModel); processFields(static_cast&>(profile.detailColA), detailColAFieldsModel); processFields(static_cast&>(profile.detailColB), detailColBFieldsModel); processFields(static_cast&>(profile.detailColC), detailColCFieldsModel); mainGeometry = profile.mainGeometry; mainState = profile.mainState; addlBandmaps = profile.addlBandmaps; darkMode = profile.darkMode; if ( mainGeometry == QByteArray() && mainState == QByteArray() ) { ui->mainLayoutStateLabel->setText(statusUnSavedText); ui->mainLayoutClearButton->setEnabled(false); } } bool ActivityEditor::activityNameExists(const QString &activityName) { FCT_IDENTIFICATION; qCDebug(function_parameters) << activityName; return ui->activityNameEdit->isEnabled() && MainLayoutProfilesManager::instance()->profileNameList().contains(activityName); } ================================================ FILE: ui/ActivityEditor.h ================================================ #ifndef QLOG_UI_ACTIVITYEDITOR_H #define QLOG_UI_ACTIVITYEDITOR_H #include #include #include #include "ui/NewContactWidget.h" #include "data/MainLayoutProfile.h" namespace Ui { class ActivityEditor; } class StringListModel : public QStringListModel { public: StringListModel(QObject *parent = nullptr) : QStringListModel(parent){}; void append (const QString& string) { insertRows(rowCount(), 1); setData(index(rowCount()-1), string); }; void deleteItem(const QModelIndex &index) { if (!index.isValid() || index.row() >= stringList().size()) return; removeRow(index.row()); } void deleteItem(const QString &value) { const QModelIndexList &itemIndexList = match(index(0,0), Qt::DisplayRole, value, 1, Qt::MatchExactly); for ( const QModelIndex & itemIndex: itemIndexList ) { deleteItem(itemIndex); } } void moveUp(const QModelIndex &index) { if ( index.row() - 1 < 0 ) return; moveRow(index.parent(), index.row(), index.parent(), index.row() - 1); } void moveDown(const QModelIndex &inIndex) { if ( inIndex.row() + 1 >= rowCount() ) return; moveUp(index(inIndex.row()+1)); } StringListModel& operator<<(const QString& string) { append(string); return *this; }; }; class ActivityEditor : public QDialog { Q_OBJECT public: explicit ActivityEditor(const QString &activityName = QString(), QWidget *parent = nullptr); ~ActivityEditor(); private: Ui::ActivityEditor *ui; StringListModel *availableFieldsModel; StringListModel *qsoRowAFieldsModel; StringListModel *qsoRowBFieldsModel; StringListModel *detailColAFieldsModel; StringListModel *detailColBFieldsModel; StringListModel *detailColCFieldsModel; QByteArray mainGeometry; QByteArray mainState; QList> addlBandmaps; bool darkMode; NewContactDynamicWidgets *dynamicWidgets; private slots: void save(); void profileNameChanged(const QString&); void clearMainLayoutClick(); void setValueState(); private: void fillWidgets(const MainLayoutProfile &profile); bool activityNameExists(const QString &activityName); void moveField(StringListModel *source, StringListModel *destination, const QModelIndexList &sourceIndexList); void connectQSORowButtons(); void connectDetailColsButtons(); void connectMoveButtons(QPushButton* downButton, QPushButton* upButton, QListView* listView, StringListModel* model); void connectFieldButtons(QPushButton* moveToButton, QPushButton* removeButton, StringListModel* targetModel, QListView* targetListView); QList getFieldIndexes(StringListModel *model); void setupValuesTab(const QString &activityName); const QString statusUnSavedText = tr("Unsaved"); }; #endif // QLOG_UI_ACTIVITYEDITOR_H ================================================ FILE: ui/ActivityEditor.ui ================================================ ActivityEditor 0 0 530 535 Activity Editor Activity Name Qt::Horizontal 40 20 0 true Layout 0 0 0 Window Arrangement: 0 0 Saved Qt::Horizontal QSizePolicy::Expanding 20 20 0 0 Clear Available Fields 4 4 4 4 List of fields that can be used QAbstractItemView::NoEditTriggers false QAbstractItemView::MultiSelection QAbstractItemView::ScrollPerPixel QAbstractItemView::ScrollPerPixel 0 0 0 4 0 4 4 Row A 4 4 4 4 Qt::Vertical 20 40 0 0 24 24 Move selected fields from the Available Fields List to the Row A :/icons/baseline-play_arrow-24px.svg:/icons/baseline-play_arrow-24px.svg 0 0 24 24 Remove selected field from the Row A :/icons/baseline-play_back-24px.svg:/icons/baseline-play_back-24px.svg Qt::Vertical 20 40 List of fields in the first variable row QAbstractItemView::NoEditTriggers false QAbstractItemView::ScrollPerPixel QAbstractItemView::ScrollPerPixel Qt::Vertical 20 40 0 0 24 24 Change the order. Move selected field up :/icons/baseline-play_up-24px.svg:/icons/baseline-play_up-24px.svg 0 0 24 24 Change the order. Move selected field down :/icons/baseline-play_down-24px.svg:/icons/baseline-play_down-24px.svg Qt::Vertical 20 40 Row B 6 4 4 4 4 Qt::Vertical 20 40 0 0 24 24 Move selected fields from the Available Fields List to the Row B :/icons/baseline-play_arrow-24px.svg:/icons/baseline-play_arrow-24px.svg 0 0 24 24 Remove selected field from the Row B :/icons/baseline-play_back-24px.svg:/icons/baseline-play_back-24px.svg Qt::Vertical 20 40 List of fields in the second variable row QAbstractItemView::NoEditTriggers false QAbstractItemView::ScrollPerPixel QAbstractItemView::ScrollPerPixel 0 Qt::Vertical 20 40 0 0 24 24 Change the order. Move selected field up :/icons/baseline-play_up-24px.svg:/icons/baseline-play_up-24px.svg 0 0 24 24 Change the order. Move selected field down :/icons/baseline-play_down-24px.svg:/icons/baseline-play_down-24px.svg Qt::Vertical 20 40 4 0 4 4 Column A 4 4 4 4 Qt::Vertical 20 40 0 0 24 24 Move selected fields from the Available Fields List to the Row A :/icons/baseline-play_arrow-24px.svg:/icons/baseline-play_arrow-24px.svg 0 0 24 24 Remove selected field from the Row A :/icons/baseline-play_back-24px.svg:/icons/baseline-play_back-24px.svg Qt::Vertical 20 40 List of fields in the first QSO Detail Column QAbstractItemView::NoEditTriggers false QAbstractItemView::ScrollPerPixel QAbstractItemView::ScrollPerPixel Qt::Vertical 20 40 0 0 24 24 Change the order. Move selected field up :/icons/baseline-play_up-24px.svg:/icons/baseline-play_up-24px.svg 0 0 24 24 Change the order. Move selected field down :/icons/baseline-play_down-24px.svg:/icons/baseline-play_down-24px.svg Qt::Vertical 20 40 Column B 4 4 4 4 Qt::Vertical 20 40 0 0 24 24 Move selected fields from the Available Fields List to the Row A :/icons/baseline-play_arrow-24px.svg:/icons/baseline-play_arrow-24px.svg 0 0 24 24 Remove selected field from the Row A :/icons/baseline-play_back-24px.svg:/icons/baseline-play_back-24px.svg Qt::Vertical 20 40 List of fields in the second QSO Detail Column QAbstractItemView::NoEditTriggers false QAbstractItemView::ScrollPerPixel QAbstractItemView::ScrollPerPixel Qt::Vertical 20 40 0 0 24 24 Change the order. Move selected field up :/icons/baseline-play_up-24px.svg:/icons/baseline-play_up-24px.svg 0 0 24 24 Change the order. Move selected field down :/icons/baseline-play_down-24px.svg:/icons/baseline-play_down-24px.svg Qt::Vertical 20 40 Column C 4 4 4 4 Qt::Vertical 20 40 0 0 24 24 Move selected fields from the Available Fields List to the Row A :/icons/baseline-play_arrow-24px.svg:/icons/baseline-play_arrow-24px.svg 0 0 24 24 Remove selected field from the Row A :/icons/baseline-play_back-24px.svg:/icons/baseline-play_back-24px.svg Qt::Vertical 20 40 List of fields in the third QSO Detail Column QAbstractItemView::NoEditTriggers false QAbstractItemView::ScrollPerPixel QAbstractItemView::ScrollPerPixel Qt::Vertical 20 40 0 0 24 24 Change the order. Move selected field up :/icons/baseline-play_up-24px.svg:/icons/baseline-play_up-24px.svg 0 0 24 24 Change the order. Move selected field down :/icons/baseline-play_down-24px.svg:/icons/baseline-play_down-24px.svg Qt::Vertical 20 40 New QSO Rows New QSO Detail Columns Values Profiles If unchecked, the profile does not change Station false If unchecked, the profile does not change Antenna false If unchecked, the profile does not change Rig false false 0 0 Connect automatically Connect true If unchecked, the profile does not change Rotator false false 0 0 Connect automatically Connect true Fields false false false false false Qt::Vertical 20 40 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok activityNameEdit tabWidget mainLayoutClearButton availableFieldsListView qsoRowAFieldsListView moveToQSORowAButton removeFromQSORowAButton qsoRowAUpButton qsoRowADownButton qsoRowBFieldsListView moveToQSORowBButton removeFromQSORowBButton qsoRowBUpButton qsoRowBDownButton layoutTypeComboBox detailColAFieldsListView moveToDetailColAButton removeFromDetailColAButton detailColAUpButton detailColADownButton detailColBFieldsListView moveToDetailColBButton removeFromDetailColBButton detailColBUpButton detailColBDownButton detailColCFieldsListView moveToDetailColCButton removeFromDetailColCButton detailColCUpButton detailColCDownButton stationProfileCheckbox stationProfileCombo antennaProfileCheckbox antennaProfileCombo rigProfileCheckbox rigProfileCombo rigAutoconnectCheckbox rotatorProfileCheckbox rotatorProfileCombo rotatorAutoconnectCheckbox contestIDCheckbox contestIDEdit stxStringCheckbox stxStringEdit propagationModeCheckbox propagationModeCombo satModeCheckbox satModeCombo satNameCheckbox satNameEdit buttonBox accepted() ActivityEditor save() 254 516 157 274 buttonBox rejected() ActivityEditor reject() 322 516 286 274 activityNameEdit textChanged(QString) ActivityEditor profileNameChanged(QString) 218 23 333 190 layoutTypeComboBox currentIndexChanged(int) stackedWidget setCurrentIndex(int) 512 475 405 258 mainLayoutClearButton clicked() ActivityEditor clearMainLayoutClick() 513 105 249 249 antennaProfileCheckbox stateChanged(int) ActivityEditor setValueState() 64 108 264 249 stationProfileCheckbox stateChanged(int) ActivityEditor setValueState() 53 90 264 249 rigProfileCheckbox stateChanged(int) ActivityEditor setValueState() 45 108 264 249 rotatorProfileCheckbox stateChanged(int) ActivityEditor setValueState() 61 108 264 249 contestIDCheckbox stateChanged(int) ActivityEditor setValueState() 114 114 264 261 propagationModeCheckbox stateChanged(int) ActivityEditor setValueState() 39 114 264 261 satModeCheckbox stateChanged(int) ActivityEditor setValueState() 114 114 264 261 satNameCheckbox stateChanged(int) ActivityEditor setValueState() 114 114 264 261 propagationModeCombo currentIndexChanged(int) ActivityEditor setValueState() 114 114 264 261 stxStringCheckbox stateChanged(int) ActivityEditor setValueState() 33 332 264 261 save() profileNameChanged(QString) clearMainLayoutClick() setValueState() ================================================ FILE: ui/AlertRuleDetail.cpp ================================================ #include #include #include #include #include #include "AlertRuleDetail.h" #include "ui_AlertRuleDetail.h" #include "core/debug.h" #include "../models/SqlListModel.h" #include "data/Data.h" #include "data/SpotAlert.h" #include "data/BandPlan.h" MODULE_IDENTIFICATION("qlog.ui.alerruledetail"); AlertRuleDetail::AlertRuleDetail(const QString &ruleName, QWidget *parent) : QDialog(parent), ui(new Ui::AlertRuleDetail), ruleName(ruleName) { FCT_IDENTIFICATION; ui->setupUi(this); ui->cqzEdit->setValidator(new QIntValidator(Data::getCQZMin(), Data::getCQZMax(), ui->cqzEdit)); ui->ituEdit->setValidator(new QIntValidator(Data::getITUZMin(), Data::getITUZMax(), ui->ituEdit)); /*************/ /* Get Bands */ /*************/ const QList bands = BandPlan::bandsList(false, true); int i = 0; for ( const Band &enabledBand : bands ) { const QString &bandName = enabledBand.name; QCheckBox *bandCheckbox = new QCheckBox(ui->band_group->parentWidget()); bandCheckbox->setText(bandName); bandCheckbox->setObjectName("band_" + bandName); int row = i / MAXCOLUMNS; int column = i % MAXCOLUMNS; ui->band_group->addWidget(bandCheckbox, row, column); i++; } /****************/ /* DX Countries */ /****************/ const QLatin1String countryStmt("SELECT id, translate_to_locale(name) " "FROM dxcc_entities_ad1c " "ORDER BY 2 COLLATE LOCALEAWARE ASC;"); SqlListModel *countryModel = new SqlListModel(countryStmt, tr("All"), ui->countryCombo); while (countryModel->canFetchMore()) countryModel->fetchMore(); ui->countryCombo->setModel(countryModel); ui->countryCombo->setModelColumn(1); ui->countryCombo->adjustMaxSize(); /********************/ /* Spotter Coutries */ /********************/ SqlListModel *countryModel2 = new SqlListModel(countryStmt, tr("All"), ui->spotterCountryCombo); while (countryModel2->canFetchMore()) countryModel2->fetchMore(); ui->spotterCountryCombo->setModel(countryModel2); ui->spotterCountryCombo->setModelColumn(1); ui->spotterCountryCombo->adjustMaxSize(); /**************************************/ /* Load or Prepare Rule Dialog Values */ /**************************************/ if ( ! ruleName.isEmpty() ) { loadRule(ruleName); } else { /* get Rule name from DB to checking whether a new filter name * will be unique */ QSqlQuery ruleStmt; if ( ! ruleStmt.prepare("SELECT rule_name FROM alert_rules ORDER BY rule_name") ) { qWarning() << "Cannot prepare select statement"; } else { if ( ruleStmt.exec() ) while (ruleStmt.next()) ruleNamesList << ruleStmt.value(0).toString(); else qWarning()<< "Cannot get filters names from DB" << ruleStmt.lastError(); } setDefaultValues(); generateMembershipCheckboxes(); } } AlertRuleDetail::~AlertRuleDetail() { FCT_IDENTIFICATION; delete ui; } void AlertRuleDetail::save() { FCT_IDENTIFICATION; if ( ui->ruleNameEdit->text().isEmpty() ) { ui->ruleNameEdit->setPlaceholderText(tr("Must not be empty")); return; } if ( ruleExists(ui->ruleNameEdit->text()) ) { QMessageBox::warning(nullptr, QMessageBox::tr("QLog Info"), QMessageBox::tr("Rule name is already exists.")); return; } QRegularExpression rxCall(ui->dxCallsignEdit->text()); QRegularExpression rxComm(ui->spotCommentEdit->text()); if ( !rxCall.isValid() ) { QMessageBox::warning(nullptr, QMessageBox::tr("QLog Info"), QMessageBox::tr("Callsign Regular Expression is incorrect.")); return; } if ( !rxComm.isValid() ) { QMessageBox::warning(nullptr, QMessageBox::tr("QLog Info"), QMessageBox::tr("Comment Regular Expression is incorrect.")); return; } AlertRule rule; /************* * Rule Name * *************/ rule.ruleName = ui->ruleNameEdit->text(); /*********** * Enabled * ***********/ rule.enabled = ui->ruleEnabledCheckBox->isChecked(); /********** * Source * **********/ int finalSource = 0; finalSource |= (ui->dxcCheckBox->isChecked() ? SpotAlert::DXSPOT : 0) | (ui->wsjtxCheckBox->isChecked() ? SpotAlert::WSJTXCQSPOT : 0); rule.sourceMap = finalSource; /*************** * DX Callsign * ***************/ rule.dxCallsign = ui->dxCallsignEdit->text(); /************** * DX Country * **************/ bool OK = false; int countryCode = ui->countryCombo->currentValue(1).toInt(&OK); rule.dxCountry = ( OK && countryCode > 0 ) ? countryCode : 0; // 0 = all /************* * DX Member * *************/ QStringList dxMember; if ( ui->memberGroupBox->isChecked() ) { for ( QCheckBox* item : static_cast&>(memberListCheckBoxes) ) if ( item->isChecked() ) dxMember.append(QString("%1").arg(item->text())); } else dxMember.append("*"); rule.dxMember = dxMember; /***************** * DX Log Status * *****************/ int status = 0; if ( ui->newEntityCheckbox->isChecked() ) status |= DxccStatus::NewEntity; if ( ui->newBandCheckbox->isChecked() ) status |= DxccStatus::NewBand; if ( ui->newModeCheckbox->isChecked() ) status |= DxccStatus::NewMode; if ( ui->newSlotCheckbox->isChecked() ) status |= DxccStatus::NewSlot; if ( ui->workedCheckbox->isChecked() ) status |= DxccStatus::Worked; if ( ui->confirmedCheckbox->isChecked() ) status |= DxccStatus::Confirmed; if ( ui->allCheckbox->isChecked() ) status = DxccStatus::All; rule.dxLogStatusMap = status; /**************** * DX Continent * ****************/ QString continentRE("*"); if ( ui->continent->isChecked() ) { continentRE = "NOTHING"; if ( ui->afcheckbox->isChecked() ) continentRE.append("|AF"); if ( ui->ancheckbox->isChecked() ) continentRE.append("|AN"); if ( ui->ascheckbox->isChecked() ) continentRE.append("|AS"); if ( ui->eucheckbox->isChecked() ) continentRE.append("|EU"); if ( ui->nacheckbox->isChecked() ) continentRE.append("|NA"); if ( ui->occheckbox->isChecked() ) continentRE.append("|OC"); if ( ui->sacheckbox->isChecked() ) continentRE.append("|SA"); } rule.dxContinent = continentRE; /******************* * Spotter Comment * *******************/ rule.dxComment = ui->spotCommentEdit->text(); /******** * Mode * ********/ QString modeRE("*"); if ( ui->modes->isChecked() ) { modeRE = "NOTHING"; if ( ui->cwcheckbox->isChecked() ) modeRE.append("|" + BandPlan::MODE_GROUP_STRING_CW); if ( ui->phonecheckbox->isChecked() ) modeRE.append("|" + BandPlan::MODE_GROUP_STRING_PHONE); if ( ui->digitalcheckbox->isChecked() ) modeRE.append("|" + BandPlan::MODE_GROUP_STRING_DIGITAL); if ( ui->ftxcheckbox->isChecked() ) modeRE.append("|" + BandPlan::MODE_GROUP_STRING_FTx); } rule.mode = modeRE; /******** * band * ********/ QString bandRE("*"); if ( ui->bands->isChecked() ) { bandRE = "NOTHING"; for ( int i = 0; i < ui->band_group->count(); i++) { QLayoutItem *item = ui->band_group->itemAt(i); if ( !item || !item->widget() ) continue; QCheckBox *bandcheckbox = qobject_cast(item->widget()); if ( bandcheckbox ) { if ( bandcheckbox->isChecked() ) { //NOTHING|20m|40m bandRE.append("|" + bandcheckbox->objectName().split("_").at(1)); } } } } rule.band = bandRE; /******************* * Spotter Country * *******************/ OK = false; int countryCodeSpotter = ui->spotterCountryCombo->currentValue(1).toInt(&OK); rule.spotterCountry = ( OK && countryCodeSpotter > 0 ) ? countryCodeSpotter : 0; // 0 = all /********************* * Spotter Continent * *********************/ QString spotterContinentRE("*"); if ( ui->continent_spotter->isChecked() ) { spotterContinentRE = "NOTHING" ; if ( ui->afcheckbox_spotter->isChecked() ) spotterContinentRE.append("|AF"); if ( ui->ancheckbox_spotter->isChecked() ) spotterContinentRE.append("|AN"); if ( ui->ascheckbox_spotter->isChecked() ) spotterContinentRE.append("|AS"); if ( ui->eucheckbox_spotter->isChecked() ) spotterContinentRE.append("|EU"); if ( ui->nacheckbox_spotter->isChecked() ) spotterContinentRE.append("|NA"); if ( ui->occheckbox_spotter->isChecked() ) spotterContinentRE.append("|OC"); if ( ui->sacheckbox_spotter->isChecked() ) spotterContinentRE.append("|SA"); } rule.spotterContinent = spotterContinentRE; /************ * CQ Zones ***********/ rule.cqz = (ui->cqzEdit->text().isEmpty() ? 0 : ui->cqzEdit->text().toInt()); /************ * ITU Zones ***********/ rule.ituz = (ui->ituEdit->text().isEmpty() ? 0 : ui->ituEdit->text().toInt()); /*********** * POTA **********/ rule.pota = ui->potaCheckbox->isChecked(); /*********** * SOTA **********/ rule.sota = ui->sotaCheckbox->isChecked(); /*********** * IOTA **********/ rule.iota = ui->iotaCheckbox->isChecked(); /*********** * WWFF **********/ rule.wwff = ui->wwffCheckbox->isChecked(); qCDebug(runtime) << rule; if ( ! rule.save() ) { QMessageBox::critical(nullptr, QMessageBox::tr("QLog Error"), QMessageBox::tr("Cannot Update Alert Rules")); return; } accept(); } void AlertRuleDetail::ruleNameChanged(const QString &newRuleName) { FCT_IDENTIFICATION; QPalette p; p.setColor(QPalette::Text, ( ruleExists(newRuleName) ) ? Qt::red : qApp->palette().text().color()); ui->ruleNameEdit->setPalette(p); } void AlertRuleDetail::callsignChanged(const QString &enteredRE) { FCT_IDENTIFICATION; QPalette p; QRegularExpression rx(enteredRE); p.setColor(QPalette::Text, ( !rx.isValid() ) ? Qt::red : qApp->palette().text().color()); ui->dxCallsignEdit->setPalette(p); } void AlertRuleDetail::spotCommentChanged(const QString &enteredRE) { FCT_IDENTIFICATION; QPalette p; QRegularExpression rx(enteredRE); p.setColor(QPalette::Text, ( !rx.isValid() ) ? Qt::red : qApp->palette().text().color()); ui->spotCommentEdit->setPalette(p); } void AlertRuleDetail::enabledLogStatusAll(bool enabled) { FCT_IDENTIFICATION; if ( enabled ) { ui->newEntityCheckbox->setChecked(enabled); ui->newBandCheckbox->setChecked(enabled); ui->newModeCheckbox->setChecked(enabled); ui->newSlotCheckbox->setChecked(enabled); ui->workedCheckbox->setChecked(enabled); ui->confirmedCheckbox->setChecked(enabled); } ui->newEntityCheckbox->setEnabled(!enabled); ui->newBandCheckbox->setEnabled(!enabled); ui->newModeCheckbox->setEnabled(!enabled); ui->newSlotCheckbox->setEnabled(!enabled); ui->workedCheckbox->setEnabled(!enabled); ui->confirmedCheckbox->setEnabled(!enabled); } void AlertRuleDetail::setDefaultValues() { FCT_IDENTIFICATION; ui->allCheckbox->setChecked(true); ui->countryCombo->setCurrentValue(ALLCOUNTRYIDX, 1); } bool AlertRuleDetail::ruleExists(const QString &ruleName) { FCT_IDENTIFICATION; qCDebug(function_parameters) << ruleName; return ruleNamesList.contains(ruleName); } void AlertRuleDetail::loadRule(const QString &ruleName) { FCT_IDENTIFICATION; ui->ruleNameEdit->setText(ruleName); ui->ruleNameEdit->setEnabled(false); AlertRule rule; if ( rule.load(ruleName) ) { /*********** * Enabled * ***********/ ui->ruleEnabledCheckBox->setChecked(rule.enabled); /********** * Source * **********/ ui->dxcCheckBox->setChecked((rule.sourceMap & SpotAlert::DXSPOT)); ui->wsjtxCheckBox->setChecked((rule.sourceMap & SpotAlert::WSJTXCQSPOT)); /*************** * DX Callsign * ***************/ ui->dxCallsignEdit->setText(rule.dxCallsign); /************** * DX Country * **************/ ui->countryCombo->setCurrentValue(rule.dxCountry, 1); /************* * DX Member * *************/ generateMembershipCheckboxes(&rule); const bool isDefaultAny = (rule.dxMember.size() == 1 && rule.dxMember.front() == "*"); ui->memberGroupBox->setChecked(!isDefaultAny); /***************** * DX Log Status * *****************/ uint statusSetting = rule.dxLogStatusMap; ui->allCheckbox->setChecked(statusSetting == DxccStatus::All); ui->newEntityCheckbox->setChecked(statusSetting & DxccStatus::NewEntity); ui->newBandCheckbox->setChecked(statusSetting & DxccStatus::NewBand); ui->newModeCheckbox->setChecked(statusSetting & DxccStatus::NewMode); ui->newSlotCheckbox->setChecked(statusSetting & DxccStatus::NewSlot); ui->workedCheckbox->setChecked(statusSetting & DxccStatus::Worked); ui->confirmedCheckbox->setChecked(statusSetting & DxccStatus::Confirmed); /************* * Continent * *************/ QString continentRE = rule.dxContinent; if ( continentRE == "*" ) { ui->continent->setChecked(false); } else { ui->continent->setChecked(true); ui->afcheckbox->setChecked(continentRE.contains("|AF")); ui->ancheckbox->setChecked(continentRE.contains("|AN")); ui->ascheckbox->setChecked(continentRE.contains("|AS")); ui->eucheckbox->setChecked(continentRE.contains("|EU")); ui->nacheckbox->setChecked(continentRE.contains("|NA")); ui->occheckbox->setChecked(continentRE.contains("|OC")); ui->sacheckbox->setChecked(continentRE.contains("|SA")); } /******************* * Spotter Comment * *******************/ ui->spotCommentEdit->setText(rule.dxComment); /******** * Mode * ********/ QString modeRE = rule.mode; if ( modeRE == "*" ) { ui->modes->setChecked(false); } else { ui->modes->setChecked(true); ui->cwcheckbox->setChecked(modeRE.contains("|" + BandPlan::MODE_GROUP_STRING_CW)); ui->phonecheckbox->setChecked(modeRE.contains("|" + BandPlan::MODE_GROUP_STRING_PHONE)); ui->digitalcheckbox->setChecked(modeRE.contains("|" + BandPlan::MODE_GROUP_STRING_DIGITAL)); ui->ftxcheckbox->setChecked(modeRE.contains("|" + BandPlan::MODE_GROUP_STRING_FTx)); } /******** * band * ********/ QString bandRE = rule.band; if ( bandRE == "*" ) { ui->bands->setChecked(false); } else { ui->bands->setChecked(true); for ( int i = 0; i < ui->band_group->count(); i++) { QLayoutItem *item = ui->band_group->itemAt(i); if ( !item || !item->widget() ) continue; QCheckBox *bandcheckbox = qobject_cast(item->widget()); if (bandcheckbox) { // object name: ex. band_20m // rule : NOTHING|20m|40m bandcheckbox->setChecked(bandRE.contains("|" + bandcheckbox->objectName().split("_").at(1))); } } } /******************* * Spotter Country * *******************/ ui->spotterCountryCombo->setCurrentValue(rule.spotterCountry, 1); /********************* * Spotter Continent * *********************/ QString spotterContinentRE = rule.spotterContinent; if ( spotterContinentRE == "*" ) { ui->continent_spotter->setChecked(false); } else { ui->continent_spotter->setChecked(true); ui->afcheckbox_spotter->setChecked(spotterContinentRE.contains("|AF")); ui->ancheckbox_spotter->setChecked(spotterContinentRE.contains("|AN")); ui->ascheckbox_spotter->setChecked(spotterContinentRE.contains("|AS")); ui->eucheckbox_spotter->setChecked(spotterContinentRE.contains("|EU")); ui->nacheckbox_spotter->setChecked(spotterContinentRE.contains("|NA")); ui->occheckbox_spotter->setChecked(spotterContinentRE.contains("|OC")); ui->sacheckbox_spotter->setChecked(spotterContinentRE.contains("|SA")); } /*********** * CQ Zones **********/ ui->cqzEdit->setText(( rule.cqz != 0) ? QString::number(rule.cqz) : QString()); /*********** * ITU Zones **********/ ui->ituEdit->setText(( rule.ituz != 0) ? QString::number(rule.ituz) : QString()); /*********** * POTA **********/ ui->potaCheckbox->setChecked(rule.pota); /*********** * SOTA **********/ ui->sotaCheckbox->setChecked(rule.sota); /*********** * IOTA **********/ ui->iotaCheckbox->setChecked(rule.iota); /*********** * WWFF **********/ ui->wwffCheckbox->setChecked(rule.wwff); } else qCDebug(runtime) << "Cannot load rule " << ruleName; } void AlertRuleDetail::generateMembershipCheckboxes(const AlertRule * rule) { FCT_IDENTIFICATION; const QStringList enabledLists = MembershipQE::getEnabledClubLists(); for ( const QString &enabledClub : enabledLists ) { QCheckBox *columnCheckbox = new QCheckBox(ui->dxMemberGrid->parentWidget()); columnCheckbox->setText(enabledClub); if ( rule ) columnCheckbox->setChecked(rule->dxMember.contains(enabledClub)); memberListCheckBoxes.append(columnCheckbox); } if ( memberListCheckBoxes.isEmpty() ) { ui->dxMemberGrid->addWidget(new QLabel(tr("No Club List is enabled"), this)); } else { int elementIndex = 0; for ( QCheckBox* item : static_cast&>(memberListCheckBoxes) ) { ui->dxMemberGrid->addWidget(item, elementIndex / MAXCOLUMNS, elementIndex % MAXCOLUMNS); elementIndex++; } } } ================================================ FILE: ui/AlertRuleDetail.h ================================================ #ifndef QLOG_UI_ALERTRULEDETAIL_H #define QLOG_UI_ALERTRULEDETAIL_H #include #include #include "core/AlertEvaluator.h" namespace Ui { class AlertRuleDetail; } class AlertRuleDetail : public QDialog { Q_OBJECT public: explicit AlertRuleDetail(const QString &ruleName, QWidget *parent); ~AlertRuleDetail(); public slots: void save(); void ruleNameChanged(const QString&); void callsignChanged(const QString&); void spotCommentChanged(const QString&); private: Ui::AlertRuleDetail *ui; QString ruleName; QStringList ruleNamesList; QList memberListCheckBoxes; private slots: void enabledLogStatusAll(bool enabled); private: void setDefaultValues(); bool ruleExists(const QString &ruleName); void loadRule(const QString &ruleName); void generateMembershipCheckboxes(const AlertRule * rule = nullptr); const quint8 MAXCOLUMNS = 8; const quint8 ALLCOUNTRYIDX = 0; }; #endif // QLOG_UI_ALERTRULEDETAIL_H ================================================ FILE: ui/AlertRuleDetail.ui ================================================ AlertRuleDetail 0 0 577 604 Alert Rule Detail 4 2 Rule Name 0 0 Qt::Horizontal QSizePolicy::Maximum 20 20 0 0 Enabled true Sources DX Cluster true WSJTX true Qt::Horizontal 40 20 Qt::Horizontal QSizePolicy::Preferred 40 20 0 false DX 12 2 Log Status 0 Worked New Slot Confirmed New Entity New Mode New Band All DX Callsign Use Perl-like regular expression .* Country 0 0 Qt::Horizontal QSizePolicy::Maximum 20 20 ITU Qt::Horizontal QSizePolicy::Maximum 10 20 0 0 40 16777215 2 Qt::Horizontal QSizePolicy::Maximum 10 20 CQZ Qt::Horizontal QSizePolicy::Maximum 10 20 0 0 40 16777215 2 Qt::Horizontal 40 20 Spot Comment Use Perl-like regular expression .* Special Programs 9 IOTA SOTA POTA WWFF 0 0 Modes true false 2 2 2 2 2 4 2 0 0 FTx (FT8/FT4) 0 0 Digital 0 0 Phone 0 0 CW Bands true false 2 2 2 2 2 4 2 Continent true false 2 2 2 2 2 4 2 South America Antarctica North America Oceania Asia Europe Africa Member true false 2 2 2 2 2 4 2 Spotter Country 0 0 Continent true false Antarctica false Asia false Europe false Africa false North America false Oceania false South America false Qt::Vertical 20 40 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok SmartSearchBox QComboBox
ui/component/SmartSearchBox.h
ruleNameEdit ruleEnabledCheckBox dxcCheckBox wsjtxCheckBox tabWidget allCheckbox newEntityCheckbox newBandCheckbox newModeCheckbox newSlotCheckbox workedCheckbox confirmedCheckbox dxCallsignEdit countryCombo ituEdit cqzEdit spotCommentEdit iotaCheckbox sotaCheckbox potaCheckbox wwffCheckbox modes cwcheckbox phonecheckbox digitalcheckbox ftxcheckbox bands continent afcheckbox ancheckbox nacheckbox sacheckbox ascheckbox eucheckbox occheckbox memberGroupBox spotterCountryCombo continent_spotter afcheckbox_spotter ancheckbox_spotter nacheckbox_spotter sacheckbox_spotter ascheckbox_spotter eucheckbox_spotter occheckbox_spotter buttonBox accepted() AlertRuleDetail save() 257 610 157 274 buttonBox rejected() AlertRuleDetail reject() 325 610 286 274 ruleNameEdit textChanged(QString) AlertRuleDetail ruleNameChanged(QString) 195 23 178 309 dxCallsignEdit textChanged(QString) AlertRuleDetail callsignChanged(QString) 232 149 178 309 spotCommentEdit textChanged(QString) AlertRuleDetail spotCommentChanged(QString) 232 242 178 309 allCheckbox toggled(bool) AlertRuleDetail enabledLogStatusAll(bool) 203 132 288 301 save() ruleNameChanged(QString) callsignChanged(QString) spotCommentChanged(QString) enabledLogStatusAll(bool)
================================================ FILE: ui/AlertSettingDialog.cpp ================================================ #include "AlertSettingDialog.h" #include "ui_AlertSettingDialog.h" #include "core/debug.h" #include "ui/AlertRuleDetail.h" #include "ui/component/StyleItemDelegate.h" MODULE_IDENTIFICATION("qlog.ui.alertsettingdialog"); AlertSettingDialog::AlertSettingDialog(QWidget *parent) : QDialog(parent), ui(new Ui::AlertSettingDialog) { ui->setupUi(this); rulesModel = new QSqlTableModel(this); rulesModel->setTable("alert_rules"); rulesModel->setHeaderData(0, Qt::Horizontal, tr("Name")); rulesModel->setHeaderData(1, Qt::Horizontal, tr("State")); rulesModel->setEditStrategy(QSqlTableModel::OnFieldChange); rulesModel->setSort(0, Qt::AscendingOrder); ui->rulesTableView->setModel(rulesModel); for ( int i = 0 ; i < rulesModel->columnCount(); i++ ) { ui->rulesTableView->hideColumn(i); } ui->rulesTableView->showColumn(0); ui->rulesTableView->showColumn(1); ui->rulesTableView->setColumnWidth(0,300); ui->rulesTableView->setItemDelegateForColumn(0, new ReadOnlyDelegate(ui->rulesTableView)); ui->rulesTableView->setItemDelegateForColumn(1,new CheckBoxDelegate(ui->rulesTableView)); rulesModel->select(); } AlertSettingDialog::~AlertSettingDialog() { delete ui; } void AlertSettingDialog::addRule() { FCT_IDENTIFICATION; AlertRuleDetail dialog(QString(), this); dialog.exec(); rulesModel->select(); } void AlertSettingDialog::removeRule() { FCT_IDENTIFICATION; rulesModel->removeRow(ui->rulesTableView->currentIndex().row()); ui->rulesTableView->clearSelection(); rulesModel->select(); } void AlertSettingDialog::editRule(QModelIndex idx) { FCT_IDENTIFICATION; if (idx.column() != 0) return; QModelIndex nameIdx = ui->rulesTableView->model()->index(idx.row(),0); QString ruleName = ui->rulesTableView->model()->data(nameIdx).toString(); AlertRuleDetail dialog(ruleName, this); dialog.exec(); rulesModel->select(); } void AlertSettingDialog::editRuleButton() { FCT_IDENTIFICATION; foreach (QModelIndex index, ui->rulesTableView->selectionModel()->selectedRows()) { editRule(index); } } ================================================ FILE: ui/AlertSettingDialog.h ================================================ #ifndef QLOG_UI_ALERTSETTINGDIALOG_H #define QLOG_UI_ALERTSETTINGDIALOG_H #include #include namespace Ui { class AlertSettingDialog; } class AlertSettingDialog : public QDialog { Q_OBJECT public: explicit AlertSettingDialog(QWidget *parent = nullptr); ~AlertSettingDialog(); private: Ui::AlertSettingDialog *ui; QSqlTableModel* rulesModel; public slots: void addRule(); void removeRule(); void editRule(QModelIndex); void editRuleButton(); }; #endif // QLOG_UI_ALERTSETTINGDIALOG_H ================================================ FILE: ui/AlertSettingDialog.ui ================================================ AlertSettingDialog 0 0 560 427 Alerts Rules Rules QAbstractItemView::DoubleClicked true QAbstractItemView::SingleSelection QAbstractItemView::SelectRows QAbstractItemView::ScrollPerPixel QAbstractItemView::ScrollPerPixel true 50 100 true false 20 20 false Qt::Vertical 20 40 Add Edit Remove Qt::Vertical 20 40 Qt::Horizontal QDialogButtonBox::Ok buttonBox accepted() AlertSettingDialog accept() 248 254 157 274 buttonBox rejected() AlertSettingDialog reject() 316 260 286 274 removeRuleButton clicked() AlertSettingDialog removeRule() 508 229 279 213 editRuleButton clicked() AlertSettingDialog editRuleButton() 508 198 279 213 addRuleButton clicked() AlertSettingDialog addRule() 508 167 279 213 rulesTableView doubleClicked(QModelIndex) AlertSettingDialog editRule(QModelIndex) 235 207 279 213 removeRule() editRule(QModelIndex) editRuleButton() addRule() ================================================ FILE: ui/AlertWidget.cpp ================================================ #include "AlertWidget.h" #include "ui_AlertWidget.h" #include "core/debug.h" #include "AlertSettingDialog.h" #include "ui/ColumnSettingDialog.h" #include "core/LogParam.h" MODULE_IDENTIFICATION("qlog.ui.alertwidget"); //Maximal Aging interval is 20s #define ALERT_AGING_CHECK_TIME 20000 AlertWidget::AlertWidget(QWidget *parent) : QWidget(parent), ui(new Ui::AlertWidget) { FCT_IDENTIFICATION; ui->setupUi(this); proxyModel = new QSortFilterProxyModel(this); alertTableModel = new AlertTableModel(proxyModel); proxyModel->setSourceModel(alertTableModel); proxyModel->setSortRole(Qt::UserRole); ui->alertTableView->setModel(proxyModel); ui->alertTableView->setSortingEnabled(true); ui->alertTableView->sortByColumn(AlertTableModel::COLUMN_UPDATED, Qt::DescendingOrder); ui->alertTableView->horizontalHeader()->setSectionsMovable(true); ui->alertTableView->addAction(ui->actionEditRules); ui->alertTableView->addAction(ui->actionColumnVisibility); ui->alertTableView->addAction(ui->actionClear); restoreTableHeaderState(); ui->clearAlertOlderSpinBox->setValue(LogParam::getAlertAging()); aging_timer = new QTimer; connect(aging_timer, &QTimer::timeout, this, &AlertWidget::alertAging); aging_timer->start(ALERT_AGING_CHECK_TIME); } AlertWidget::~AlertWidget() { FCT_IDENTIFICATION; if ( aging_timer ) { aging_timer->stop(); aging_timer->deleteLater(); } delete ui; } void AlertWidget::addAlert(const SpotAlert &alert) { FCT_IDENTIFICATION; alertTableModel->addAlert(alert); ui->alertTableView->repaint(); } void AlertWidget::clearAllAlerts() { FCT_IDENTIFICATION; alertTableModel->clear(); emit alertsCleared(); } void AlertWidget::entryDoubleClicked(QModelIndex index) { FCT_IDENTIFICATION; const QModelIndex &source_index = proxyModel->mapToSource(index); const AlertTableModel::AlertTableRecord &record = alertTableModel->getTableRecord(source_index); if ( record.alert.source == SpotAlert::WSJTXCQSPOT ) emit tuneWsjtx(record.alert.spot); else emit tuneDx(record.alert.getDxSpot()); } void AlertWidget::alertAgingChanged(int) { FCT_IDENTIFICATION; LogParam::setAlertAging(ui->clearAlertOlderSpinBox->value()); } void AlertWidget::showEditRules() { FCT_IDENTIFICATION; AlertSettingDialog dialog(this); dialog.exec(); emit rulesChanged(); } void AlertWidget::resetDupe() { FCT_IDENTIFICATION; alertTableModel->resetDupe(); } void AlertWidget::updateSpotsStatusWhenQSOAdded(const QSqlRecord &record) { FCT_IDENTIFICATION; alertTableModel->updateSpotsStatusWhenQSOAdded(record); } void AlertWidget::updateSpotsStatusWhenQSOUpdated(const QSqlRecord &record) { FCT_IDENTIFICATION; alertTableModel->updateSpotsStatusWhenQSOUpdated(record); } void AlertWidget::updateSpotsDupeWhenQSODeleted(const QSqlRecord &record) { FCT_IDENTIFICATION; // Pay attention: this method is called before the QSO is added to contacts alertTableModel->updateSpotsStatusWhenQSODeleted(record); } void AlertWidget::updateSpotsDxccStatusWhenQSODeleted(const QSet &entities) { alertTableModel->updateSpotsDxccStatusWhenQSODeleted(entities); } void AlertWidget::recalculateDupe() { FCT_IDENTIFICATION; alertTableModel->recalculateDupe(); } void AlertWidget::recalculateDxccStatus() { FCT_IDENTIFICATION; alertTableModel->recalculateDxccStatus(); } void AlertWidget::showColumnVisibility() { FCT_IDENTIFICATION; ColumnSettingSimpleDialog dialog(ui->alertTableView); dialog.exec(); saveTableHeaderState(); } void AlertWidget::alertAging() { FCT_IDENTIFICATION; alertTableModel->aging(ui->clearAlertOlderSpinBox->value() * 60); ui->alertTableView->repaint(); emit alertsCleared(); } void AlertWidget::saveTableHeaderState() { FCT_IDENTIFICATION; const QByteArray &state = ui->alertTableView->horizontalHeader()->saveState(); LogParam::setAlertWidgetState(state); } void AlertWidget::restoreTableHeaderState() { FCT_IDENTIFICATION; const QByteArray &state = LogParam::getAlertWidgetState(); if ( !state.isEmpty() ) ui->alertTableView->horizontalHeader()->restoreState(state); } int AlertWidget::alertCount() const { FCT_IDENTIFICATION; return alertTableModel->rowCount(); } void AlertWidget::finalizeBeforeAppExit() { FCT_IDENTIFICATION; saveTableHeaderState(); } ================================================ FILE: ui/AlertWidget.h ================================================ #ifndef QLOG_UI_ALERTWIDGET_H #define QLOG_UI_ALERTWIDGET_H #include #include "data/SpotAlert.h" #include "models/AlertTableModel.h" #include "data/DxSpot.h" #include "component/ShutdownAwareWidget.h" namespace Ui { class AlertWidget; } class AlertWidget : public QWidget, public ShutdownAwareWidget { Q_OBJECT public: explicit AlertWidget(QWidget *parent = nullptr); ~AlertWidget(); int alertCount() const; virtual void finalizeBeforeAppExit() override; public slots: void addAlert(const SpotAlert &alert); void clearAllAlerts(); void entryDoubleClicked(QModelIndex index); void alertAgingChanged(int); void showEditRules(); void resetDupe(); void updateSpotsStatusWhenQSOAdded(const QSqlRecord &record); void updateSpotsStatusWhenQSOUpdated(const QSqlRecord &record); void updateSpotsDupeWhenQSODeleted(const QSqlRecord &record); void updateSpotsDxccStatusWhenQSODeleted(const QSet &entities); void recalculateDupe(); void recalculateDxccStatus(); void saveTableHeaderState(); private slots: void showColumnVisibility(); signals: void alertsCleared(); void tuneDx(DxSpot); void tuneWsjtx(WsjtxEntry); void rulesChanged(); private: Ui::AlertWidget *ui; AlertTableModel* alertTableModel; QSortFilterProxyModel *proxyModel; QTimer *aging_timer; private slots: void alertAging(); void restoreTableHeaderState(); }; #endif // QLOG_UI_ALERTWIDGET_H ================================================ FILE: ui/AlertWidget.ui ================================================ AlertWidget 0 0 773 326 Alerts 0 0 0 0 Qt::ClickFocus Qt::ActionsContextMenu QAbstractItemView::NoEditTriggers false false false true QAbstractItemView::SingleSelection QAbstractItemView::SelectRows QAbstractItemView::ScrollPerPixel QAbstractItemView::ScrollPerPixel false true true 20 20 Clear older than Qt::ClickFocus Never min(s) 9999 Qt::Horizontal 40 20 Edit Rules Column Visibility... Clear alertTableView doubleClicked(QModelIndex) AlertWidget entryDoubleClicked(QModelIndex) 386 147 386 162 clearAlertOlderSpinBox valueChanged(int) AlertWidget alertAgingChanged(int) 178 302 386 162 actionEditRules triggered() AlertWidget showEditRules() -1 -1 386 162 actionColumnVisibility triggered() AlertWidget showColumnVisibility() -1 -1 386 162 actionClear triggered() AlertWidget clearAllAlerts() -1 -1 386 162 clearAllAlerts() entryDoubleClicked(QModelIndex) alertAgingChanged(int) showEditRules() showColumnVisibility() ================================================ FILE: ui/AwardsDialog.cpp ================================================ #include #include #include #include "AwardsDialog.h" #include "ui_AwardsDialog.h" #include "models/SqlListModel.h" #include "core/debug.h" #include "core/QSOFilterManager.h" #include "awards/AwardDXCC.h" #include "awards/AwardITU.h" #include "awards/AwardWAC.h" #include "awards/AwardWAZ.h" #include "awards/AwardWAS.h" #include "awards/AwardWPX.h" #include "awards/AwardIOTA.h" #include "awards/AwardPOTAHunter.h" #include "awards/AwardPOTAActivator.h" #include "awards/AwardSOTA.h" #include "awards/AwardWWFF.h" #include "awards/AwardGridsquare.h" #include "awards/AwardUSCounty.h" #include "awards/AwardRDA.h" #include "awards/AwardJapan.h" #include "awards/AwardNZ.h" #include "awards/AwardSpanishDME.h" #include "awards/AwardUKD.h" MODULE_IDENTIFICATION("qlog.ui.awardsdialog"); AwardsDialog::AwardsDialog(QWidget *parent) : QDialog(parent), ui(new Ui::AwardsDialog) { FCT_IDENTIFICATION; ui->setupUi(this); entityCallsignModel = new SqlListModel("SELECT my_dxcc, my_country_intl || ' (' || CASE WHEN LENGTH(GROUP_CONCAT(station_callsign, ', ')) > 50 " "THEN SUBSTR(GROUP_CONCAT(station_callsign, ', '), 0, 50) || '...' ELSE GROUP_CONCAT(station_callsign, ', ') END || ')' " "FROM(SELECT DISTINCT my_dxcc, my_country_intl, station_callsign FROM contacts) GROUP BY my_dxcc ORDER BY my_dxcc;", "", this); ui->myEntityComboBox->blockSignals(true); while (entityCallsignModel->canFetchMore()) { entityCallsignModel->fetchMore(); } ui->myEntityComboBox->setModel(entityCallsignModel); ui->myEntityComboBox->setModelColumn(1); ui->myEntityComboBox->blockSignals(false); m_awards = createAwards(); ui->awardComboBox->blockSignals(true); for ( const AwardDefinition *award : static_cast>(m_awards) ) ui->awardComboBox->addItem(award->displayName(), award->key()); ui->awardComboBox->blockSignals(false); ui->userFilterComboBox->blockSignals(true); ui->userFilterComboBox->setModel(QSOFilterManager::QSOFilterModel(tr("No User Filter"), ui->userFilterComboBox)); ui->userFilterComboBox->blockSignals(false); refreshTable(0); } AwardsDialog::~AwardsDialog() { FCT_IDENTIFICATION; qDeleteAll(m_awards); delete ui; } void AwardsDialog::refreshTable(int) { FCT_IDENTIFICATION; AwardDefinition *award = currentAward(); if ( !award ) return; setEntityInputEnabled(award->entityInputEnabled()); setNotWorkedEnabled(award->notWorkedEnabled()); if ( !award->widget() ) { QWidget *w = award->createWidget(ui->stackedWidget); ui->stackedWidget->addWidget(w); const QTableView *tableView = qobject_cast(w); if ( tableView ) { connect(tableView, &QTableView::doubleClicked, this, [this](const QModelIndex &idx) { AwardDefinition::ConditionResult result = currentAward()->getConditionSelected(idx); if ( result.valid ) emit awardConditionSelected(result.country, result.band, result.filter); }); } } ui->stackedWidget->setCurrentWidget(award->widget()); award->updateData(buildFilterParams()); } AwardDefinition* AwardsDialog::currentAward() const { FCT_IDENTIFICATION; int idx = ui->awardComboBox->currentIndex(); if ( idx >= 0 && idx < m_awards.size() ) return m_awards.at(idx); return nullptr; } AwardFilterParams AwardsDialog::buildFilterParams() const { FCT_IDENTIFICATION; AwardFilterParams params; params.entitySelected = getSelectedEntity(); params.modes << "'NONE'"; if ( ui->cwCheckBox->isChecked() ) params.modes << "'CW'"; if ( ui->phoneCheckBox->isChecked() ) params.modes << "'PHONE'"; if ( ui->digiCheckBox->isChecked() ) params.modes << "'DIGITAL'"; params.confirmedConditions << "1=2 "; if ( ui->eqslCheckBox->isChecked() ) params.confirmedConditions << " eqsl_qsl_rcvd = 'Y' "; if ( ui->lotwCheckBox->isChecked() ) params.confirmedConditions << " lotw_qsl_rcvd = 'Y' "; if ( ui->paperCheckBox->isChecked() ) params.confirmedConditions << " qsl_rcvd = 'Y' "; params.notWorkedOnly = ui->notWorkedCheckBox->isChecked(); params.notConfirmedOnly = ui->notConfirmedCheckBox->isChecked(); params.userFilterWhereClause = ( ui->userFilterComboBox->currentIndex() > 0 ) ? "AND " + QSOFilterManager::instance()->getWhereClause(ui->userFilterComboBox->currentText()) : ""; return params; } QString AwardsDialog::getSelectedEntity() const { FCT_IDENTIFICATION; int row = ui->myEntityComboBox->currentIndex(); const QModelIndex &idx = ui->myEntityComboBox->model()->index(row,0); const QString comboData = ui->myEntityComboBox->model()->data(idx).toString(); qCDebug(runtime) << comboData; return comboData; } void AwardsDialog::setEntityInputEnabled(bool enabled) { FCT_IDENTIFICATION; ui->myEntityComboBox->setVisible(enabled); ui->myEntityLabel->setVisible(enabled); } void AwardsDialog::setNotWorkedEnabled(bool enabled) { FCT_IDENTIFICATION; ui->notWorkedCheckBox->blockSignals(true); ui->notWorkedCheckBox->setVisible(enabled); ui->notWorkedLabel->setVisible(enabled); ui->notWorkedCheckBox->setChecked(enabled && ui->notWorkedCheckBox->isChecked()); ui->notWorkedCheckBox->blockSignals(false); ui->notConfirmedCheckBox->blockSignals(true); ui->notConfirmedCheckBox->setVisible(enabled); ui->notConfirmedCheckBox->setChecked(enabled && ui->notConfirmedCheckBox->isChecked()); ui->notConfirmedCheckBox->blockSignals(false); } QList AwardsDialog::createAwards() { return { new AwardDXCC(), new AwardITU(), new AwardWAC(), new AwardWAZ(), new AwardWAS(), new AwardWPX(), new AwardIOTA(), new AwardPOTAHunter(), new AwardPOTAActivator(), new AwardSOTA(), new AwardWWFF(), new AwardGridsquare(2), new AwardGridsquare(4), new AwardGridsquare(6), new AwardUSCounty(), new AwardRDA(), new AwardJapan(), new AwardNZ(), new AwardSpanishDME(), new AwardUKD(), }; } ================================================ FILE: ui/AwardsDialog.h ================================================ #ifndef QLOG_UI_AWARDSDIALOG_H #define QLOG_UI_AWARDSDIALOG_H #include #include #include "awards/AwardDefinition.h" #include "models/SqlListModel.h" namespace Ui { class AwardsDialog; } class AwardsDialog : public QDialog { Q_OBJECT public: explicit AwardsDialog(QWidget *parent = nullptr); ~AwardsDialog(); public slots: void refreshTable(int); signals: void awardConditionSelected(QString, QString, QString); private: Ui::AwardsDialog *ui; QList m_awards; SqlListModel* entityCallsignModel; AwardDefinition* currentAward() const; AwardFilterParams buildFilterParams() const; QString getSelectedEntity() const; void setEntityInputEnabled(bool); void setNotWorkedEnabled(bool); static QList createAwards(); }; #endif // QLOG_UI_AWARDSDIALOG_H ================================================ FILE: ui/AwardsDialog.ui ================================================ AwardsDialog 0 0 1016 702 Awards 4 Options 2 2 6 2 6 2 Award 0 0 Qt::Horizontal QSizePolicy::Maximum 40 20 My DXCC Entity Qt::Horizontal QSizePolicy::Minimum 20 20 0 0 Qt::Horizontal QSizePolicy::Maximum 40 20 User Filter Qt::Horizontal QSizePolicy::Maximum 20 20 0 0 Qt::Horizontal 40 20 Confirmed by LoTW true eQSL true Paper true Mode CW true Phone true Digi true Show Not-Worked Only Not-Confirmed Only Qt::Horizontal 40 20 Qt::Horizontal QDialogButtonBox::Close awardComboBox myEntityComboBox userFilterComboBox lotwCheckBox eqslCheckBox paperCheckBox cwCheckBox phoneCheckBox digiCheckBox notWorkedCheckBox notConfirmedCheckBox buttonBox accepted() AwardsDialog accept() 259 738 157 274 buttonBox rejected() AwardsDialog reject() 327 738 286 274 awardComboBox currentIndexChanged(int) AwardsDialog refreshTable(int) 409 69 345 295 myEntityComboBox currentIndexChanged(int) AwardsDialog refreshTable(int) 409 109 345 295 eqslCheckBox stateChanged(int) AwardsDialog refreshTable(int) 689 164 345 295 lotwCheckBox stateChanged(int) AwardsDialog refreshTable(int) 236 149 345 295 paperCheckBox stateChanged(int) AwardsDialog refreshTable(int) 960 164 345 295 cwCheckBox stateChanged(int) AwardsDialog refreshTable(int) 236 189 345 295 phoneCheckBox stateChanged(int) AwardsDialog refreshTable(int) 689 204 345 295 digiCheckBox stateChanged(int) AwardsDialog refreshTable(int) 960 204 345 295 notWorkedCheckBox stateChanged(int) AwardsDialog refreshTable(int) 545 96 507 350 notConfirmedCheckBox stateChanged(int) AwardsDialog refreshTable(int) 667 96 507 350 userFilterComboBox currentIndexChanged(int) AwardsDialog refreshTable(int) 616 135 503 350 refreshTable(int) ================================================ FILE: ui/BandmapWidget.cpp ================================================ #include #include #include #include #include #include #include #include #include #include #include #include "BandmapWidget.h" #include "ui_BandmapWidget.h" #include "data/Data.h" #include "data/BandPlan.h" #include "core/debug.h" #include "rig/macros.h" #include "core/LogParam.h" #include "core/EmergencyFrequency.h" MODULE_IDENTIFICATION("qlog.ui.bandmapwidget"); #define WIDGET_CENTER ( height()/2 - 50 ) QMap BandmapWidget::spots; QList BandmapWidget::nonVfoWidgets; double BandmapWidget::lastSeenVFOFreq = 0.0; BandmapWidget *BandmapWidget::vfoWidget = nullptr; BandmapWidget::BandmapWidget(const QString &widgetID, const Band &widgetBand, QWidget *parent) : QWidget(parent), ui(new Ui::BandmapWidget), rx_freq(0.0), tx_freq(0.0), bandmapScene(new GraphicsScene(this)), update_timer(new QTimer(this)), rxMark(nullptr), txMark(nullptr), keepRXCenter(true), showEmergencyMarkers(true), pendingSpots(0), lastStationUpdate(0), bandmapAnimation(true), isNonVfo(!widgetID.isEmpty()), isActive(false) { FCT_IDENTIFICATION; ui->setupUi(this); setObjectName((isNonVfo) ? widgetID : MAIN_WIDGET_OBJECT_NAME); double newContactFreq = (lastSeenVFOFreq == 0.0 ) ? LogParam::getNewContactFreq() : lastSeenVFOFreq; double ritFreq = newContactFreq + RigProfilesManager::instance()->getCurProfile1().ritOffset; double xitFreq = newContactFreq + RigProfilesManager::instance()->getCurProfile1().xitOffset; const QString &mode = LogParam::getNewContactMode(); const QString &submode = LogParam::getNewContactSubMode(); keepRXCenter = LogParam::getBandmapCenterRX(objectName()); showEmergencyMarkers = LogParam::getBandmapShowEmergency(objectName()); if ( isNonVfo ) { ui->bottomRow->setVisible(false); ui->clearAllButton->setVisible(false); nonVfoWidgets.append(this); } else { vfoWidget = this; ui->clearSpotOlderSpin->setValue(LogParam::getBandmapAging(objectName())); } setBand((widgetBand == Band()) ? BandPlan::freq2Band(ritFreq) : widgetBand, false); bandmapScene->setFocusOnTouch(false); connect(bandmapScene, &GraphicsScene::spotClicked, this, &BandmapWidget::spotClicked); connect(ui->scrollArea->verticalScrollBar(), &QScrollBar::rangeChanged, this, &BandmapWidget::focusZoomFreq); ui->graphicsView->setScene(bandmapScene); ui->graphicsView->installEventFilter(this); //ui->scrollArea->verticalScrollBar()->setSingleStep(5); connect(update_timer, &QTimer::timeout, this, &BandmapWidget::updateStationTimer); update_timer->start(BANDMAP_MAX_REFRESH_TIME); updateMode(VFO1, QString(), mode, submode, 0); updateTunedFrequency(VFO1, newContactFreq, ritFreq, xitFreq); ui->zoomSlider->setSliderPosition(zoom); } void BandmapWidget::update() { FCT_IDENTIFICATION; /**************** * Restart Time * ****************/ update_timer->setInterval(BANDMAP_MAX_REFRESH_TIME); /************* * Clear All * *************/ clearAllCallsignFromScene(); clearFreqMark(&rxMark); clearFreqMark(&txMark); bandmapScene->clear(); // do not show bandmap for submm bands if ( rx_freq > 250000.0 || currentBand.start >= 300000.0 ) return; /******************* * Determine Scale * *******************/ double step; int digits; determineStepDigits(step, digits); const int steps = static_cast(round((currentBand.end - currentBand.start) / step)); minHeight = steps * PIXELSPERSTEP + 30; ui->graphicsView->setFixedSize(270, minHeight); /****************/ /* Draw bandmap */ /****************/ const QPen gridPen(QColor(192, 192, 192)); const QBrush highlightBrush(QColor(102, 153, 255, 100)); for ( int i = 0; i <= steps; i++ ) { double plottedFreq = currentBand.start + step * i; const double y = i * PIXELSPERSTEP; // Add colored square if ( !currBandMode.isEmpty() && i < steps && currBandMode == BandPlan::freq2BandModeGroupString(plottedFreq) ) bandmapScene->addRect(0, y, 10, 10, QPen(Qt::NoPen), highlightBrush); const int lineLength = (i % 5 == 0) ? 15 : 10; bandmapScene->addLine(0, y, lineLength, y, gridPen); if ( i % 5 == 0 ) { QGraphicsTextItem* text = bandmapScene->addText(QString::number(plottedFreq, 'f', digits)); const QRectF rect = text->boundingRect(); text->setPos(-rect.width() - 5, y - (rect.height() / 2)); } } const QString &endFreqDigits= QString::number(currentBand.end + step*steps, 'f', digits); bandmapScene->setSceneRect(135 - (endFreqDigits.size() * PIXELSPERSTEP), 0, 0, steps * PIXELSPERSTEP + 10); /************************/ /* Draw TX and RX Marks */ /************************/ drawTXRXMarks(step); /*****************************/ /* Draw Emergency Freq Marks */ /*****************************/ drawEmergencyMarkers(step); /***************** * Draw Stations * *****************/ updateStations(); } void BandmapWidget::spotAging() { FCT_IDENTIFICATION; // only master Widget removes spots if ( isNonVfo ) { qCDebug(runtime) << "NonVFO widget - skipping"; return; } int clear_interval_sec = ui->clearSpotOlderSpin->value() * 60; qCDebug(function_parameters)< spotIterator(spots); while ( spotIterator.hasNext() ) { spotIterator.next(); //clear spots automatically if ( spotIterator.value().dateTime.addSecs(clear_interval_sec) <= QDateTime::currentDateTimeUtc() ) { spotIterator.remove(); } } } void BandmapWidget::updateStations() { FCT_IDENTIFICATION; /**************** * Restart Time * ****************/ update_timer->setInterval(BANDMAP_MAX_REFRESH_TIME); clearAllCallsignFromScene(); spotAging(); // do not show bandmap for submm bands if ( rx_freq > 250000.0 || currentBand.start >= 300000.0 )return; double step; int digits; double min_y = 0; const QColor lineColor(192,192,192); const QColor defaultTextColor = qApp->palette().color(QPalette::Text); const QString timeFormat = locale.formatTimeShort(); determineStepDigits(step, digits); QMap::iterator lower = spots.lowerBound(currentBand.start); QMap::iterator upper = spots.upperBound(currentBand.end); while ( lower != upper ) { DxSpot &spot = lower.value(); double freq_y = ((lower.key() - currentBand.start) / step) * PIXELSPERSTEP; double text_y = std::max(min_y + 5.0, freq_y); /************************* * Draw Line to Callsign * *************************/ lineItemList.append(bandmapScene->addLine(17, freq_y, 40, text_y, QPen(lineColor))); const QString &callsignTmp = spot.callsign; const QString &timeTmp = locale.toString(spot.dateTime, timeFormat); QGraphicsTextItem* text = bandmapScene->addText(callsignTmp + " @ " + timeTmp); text->document()->setDocumentMargin(0); qreal halfHeight = text->boundingRect().height() / 2; text->setPos(40, text_y - halfHeight); text->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsSelectable | text->flags()); text->setProperty("freq", lower.key()); text->setProperty("bandmode", static_cast(spot.bandPlanMode)); QString unit; unsigned char decP; double spotFreq = Data::MHz2UserFriendlyFreq(lower.key(), unit, decP); text->setToolTip(QString("%1 de %2
%3 %4; %5
%6").arg(callsignTmp, spot.spotter, QString::number(spotFreq, 'f', decP), unit, spot.modeGroupString, spot.comment)); min_y = text_y + halfHeight; text->setDefaultTextColor(Data::statusToColor(spot.status, spot.dupeCount, defaultTextColor)); textItemList.append(text); ++lower; } // Resize scene and view dynamically const QRectF &itemsRect = bandmapScene->itemsBoundingRect(); QRectF sceneRect = bandmapScene->sceneRect(); double resultHeight = qMax(itemsRect.bottom(), minHeight); sceneRect.setBottom(resultHeight + 10); ui->graphicsView->setFixedSize(270, resultHeight + 30); bandmapScene->setSceneRect(sceneRect); pendingSpots = 0; lastStationUpdate = QDateTime::currentMSecsSinceEpoch(); if ( !isNonVfo ) { // signal to the non-vfo bandmaps that the spots should be redrawn emit spotsUpdated(); } } void BandmapWidget::clearWidgetBand() { FCT_IDENTIFICATION; auto begin = spots.lowerBound(currentBand.start); auto end = spots.upperBound(currentBand.end); while (begin != end) begin = spots.erase(begin); if ( vfoWidget ) vfoWidget->updateStations(); // this causes that all bandmap will be updated updateNearestSpot(); } void BandmapWidget::finalizeBeforeAppExit() { FCT_IDENTIFICATION; saveState(); } void BandmapWidget::determineStepDigits(double &step, int &digits) const { FCT_IDENTIFICATION; switch (zoom) { case ZOOM_100HZ: step = 0.0001; digits = 4; break; case ZOOM_250HZ: step = 0.00025; digits = 4; break; case ZOOM_500HZ: step = 0.0005; digits = 4; break; case ZOOM_1KHZ: step = 0.001; digits = 3; break; case ZOOM_2K5HZ: step = 0.0025; digits = 3; break; case ZOOM_5KHZ: step = 0.005; digits = 3; break; case ZOOM_10KHZ: step = 0.01; digits = 2; break; } /* bands below are too wide for BandMap, therefore it is needed to short them */ if ( currentBand.start >= 28.0 && currentBand.start < 420.0 ) { step = step * 10; } if ( ( currentBand.start >= 420.0 && currentBand.start < 2300.0 ) || currentBand.start == 119980 ) { step = step * 100; } else if ( currentBand.start >= 2300.0 && currentBand.start < 75500.0 ) { step = step * 1000; } else if (currentBand.start == 75500.0 || currentBand.start >= 142000.0) { step = step * 10000; } } void BandmapWidget::clearAllCallsignFromScene() { FCT_IDENTIFICATION; QMutableListIterator lineIterator(lineItemList); while ( lineIterator.hasNext() ) { lineIterator.next(); bandmapScene->removeItem(lineIterator.value()); delete lineIterator.value(); } lineItemList.clear(); QMutableListIterator textIterator(textItemList); while ( textIterator.hasNext() ) { textIterator.next(); bandmapScene->removeItem(textIterator.value()); delete textIterator.value(); } textItemList.clear(); } void BandmapWidget::clearFreqMark(QGraphicsPolygonItem **currentPolygon) { FCT_IDENTIFICATION; if ( *currentPolygon != nullptr ) { bandmapScene->removeItem(*currentPolygon); delete *currentPolygon; *currentPolygon = nullptr; } } void BandmapWidget::drawFreqMark(const double freq, const double step, const QColor &color, QGraphicsPolygonItem **currentPolygon) { FCT_IDENTIFICATION; qCDebug(function_parameters) << freq << step << color; clearFreqMark(currentPolygon); /* do not show the freq mark if it is outside the bandmap */ if ( freq < currentBand.start || freq > currentBand.end ) { return; } int Yposition = Freq2ScenePos(freq).y(); QPolygonF poly; poly << QPointF(-1, Yposition) << QPointF(-7, Yposition - 7) << QPointF(-7, Yposition + 7); *currentPolygon = bandmapScene->addPolygon(poly, QPen(Qt::NoPen), QBrush(color, Qt::SolidPattern)); } void BandmapWidget::drawTXRXMarks(double step) { FCT_IDENTIFICATION; // do not show bandmap for submm bands if ( !isActive || rx_freq > 250000.0 || currentBand.start >= 300000.0 ) { return; } /**************************/ /* Draw RX frequency mark */ /**************************/ drawFreqMark(rx_freq, step, QColor(30, 180, 30), &rxMark); /**************************/ /* Draw TX frequency mark */ /**************************/ if ( tx_freq >= currentBand.start && tx_freq <= currentBand.end && tx_freq != rx_freq ) { drawFreqMark(tx_freq, step, QColor(255, 0, 0), &txMark); } else { clearFreqMark(&txMark); } } void BandmapWidget::removeDuplicates(DxSpot &spot) { FCT_IDENTIFICATION; QMap::iterator lower = spots.lowerBound(spot.freq - 0.005); QMap::iterator upper = spots.upperBound(spot.freq + 0.005); while ( lower != upper ) { if ( lower.value().callsign.compare(spot.callsign, Qt::CaseInsensitive) == 0 ) { lower = spots.erase(lower); } else { ++lower; } } } void BandmapWidget::addSpot(DxSpot spot) { FCT_IDENTIFICATION; // only master Widget adds spots if ( isNonVfo ) { qCDebug(runtime) << "NonVFO widget - skipping"; return; } qCDebug(function_parameters) << spot.freq << spot.callsign; this->removeDuplicates(spot); spots.insert(spot.freq, spot); if ( spot.band == currentBand.name ) { qint64 currTime = QDateTime::currentMSecsSinceEpoch(); /* if the spots are received slowly, this will guarantee * that Spots will be displayed as soon as they are received. * QLog does not have to wait for the timer to tick to update the stations. */ if ( currTime - BANDMAP_MAX_REFRESH_TIME >= lastStationUpdate ) updateStations(); else /* If the spot are received quickly then store them and wait for QTimer tick */ increasePendingSpots(); } //update NonVFO Pending spot counters for ( BandmapWidget *nonVfoWidget : static_cast>(nonVfoWidgets) ) if ( nonVfoWidget->getBand().name == spot.band ) nonVfoWidget->increasePendingSpots(); } void BandmapWidget::updateStationTimer() { FCT_IDENTIFICATION; /* This function handle QTime tick to update Stations */ qint64 currTime = QDateTime::currentMSecsSinceEpoch(); /* If there is (are) station(s) or Time to Aging occurred then update the bandmap */ if ( pendingSpots > 0 || currTime - BANDMAP_AGING_CHECK_TIME >= lastStationUpdate ) { updateStations(); } } DxSpot BandmapWidget::nearestSpot(const double freq) const { FCT_IDENTIFICATION; QMap::const_iterator it = spots.constFind(freq); if( it == spots.cend() ) { QMap::const_iterator lower = spots.lowerBound(freq - Hz2MHz(1000)); QMap::const_iterator upper = spots.upperBound(freq + Hz2MHz(1000)); it = std::min_element( lower, upper, [freq](const DxSpot &p1, const DxSpot &p2) { return qAbs(p1.freq - freq) < qAbs(p2.freq - freq); }); if ( it != upper ) { /* FOUND */ return it.value(); } else { /* Not found */ return DxSpot(); } } /* Exact Match */ return it.value(); } void BandmapWidget::updateNearestSpot(bool force) { FCT_IDENTIFICATION; // only master Widget updates spot if ( isNonVfo ) { qCDebug(runtime) << "NonVFO widget - skipping"; return; } DxSpot currNearestSpot; currNearestSpot = nearestSpot(rx_freq); if ( force || currNearestSpot.callsign != lastNearestSpot.callsign ) { emit nearestSpotFound(currNearestSpot); lastNearestSpot = currNearestSpot; } } void BandmapWidget::setBandmapAnimation(bool isEnable) { FCT_IDENTIFICATION; qCDebug(function_parameters) << isEnable; bandmapAnimation = isEnable; } void BandmapWidget::setBand(const Band &newBand, bool savePrevBandZoom) { FCT_IDENTIFICATION; if ( savePrevBandZoom ) saveState(); currentBand = newBand; zoom = getSavedZoom(newBand); zoomFreq = getSavedScrollFreq(newBand); zoomWidgetYOffset = WIDGET_CENTER; ui->zoomSlider->blockSignals(true); ui->zoomSlider->setSliderPosition(zoom); ui->zoomSlider->blockSignals(false); QWidget *dock = parentWidget(); if ( dock ) dock->setWindowTitle(tr("Bandmap") + " " + newBand.name); } void BandmapWidget::saveCurrentZoom() { FCT_IDENTIFICATION; LogParam::setBandmapZoom(objectName(), currentBand.name, zoom); } BandmapWidget::BandmapZoom BandmapWidget::getSavedZoom(const Band &band) { FCT_IDENTIFICATION; // if a zoom is not set, try to get it from the main Bandmap widget const QVariant &zoomVariantMain = LogParam::getBandmapZoom(MAIN_WIDGET_OBJECT_NAME, band.name, ZOOM_10KHZ); QVariant zoomVariant = LogParam::getBandmapZoom(objectName(), band.name, zoomVariantMain); return zoomVariant.value(); } void BandmapWidget::saveCurrentScrollFreq() { FCT_IDENTIFICATION; LogParam::setBandmapScrollFreq(objectName(), currentBand.name, visibleCentreFreq()); } double BandmapWidget::getSavedScrollFreq(const Band &band) { FCT_IDENTIFICATION; return LogParam::getBandmapScrollFreq(objectName(), band.name); } double BandmapWidget::visibleCentreFreq() const { FCT_IDENTIFICATION; QPoint point(0,ui->scrollArea->verticalScrollBar()->value() + WIDGET_CENTER); double ret = ScenePos2Freq(ui->graphicsView->mapToScene(point)); qCDebug(runtime) << "Centre freq" << ret; return ret; } bool BandmapWidget::isAlreadyOpened(const Band &band) const { FCT_IDENTIFICATION; for ( const BandmapWidget *widget : static_cast>(nonVfoWidgets)) { if ( widget && widget->isVisible() && widget->getBand() == band ) return true; } return false; } void BandmapWidget::saveState() { FCT_IDENTIFICATION; saveCurrentZoom(); saveCurrentScrollFreq(); } void BandmapWidget::spotAgingChanged(int) { FCT_IDENTIFICATION; LogParam::setBandmapAging(objectName(), ui->clearSpotOlderSpin->value()); } void BandmapWidget::clearSpots() { FCT_IDENTIFICATION; spots.clear(); updateStations(); updateNearestSpot(); } void BandmapWidget::setZoom(int zoomParam) { FCT_IDENTIFICATION; zoomWidgetYOffset = WIDGET_CENTER; zoomFreq = ( keepRXCenter ) ? rx_freq : visibleCentreFreq(); zoom = static_cast(static_cast(zoomParam)); setBandmapAnimation(false); update(); scrollToFreq(zoomFreq); setBandmapAnimation(true); } void BandmapWidget::updateSpotsStatusWhenQSOAdded(const QSqlRecord &record) { FCT_IDENTIFICATION; // only master Widget modifies spots if ( isNonVfo ) { qCDebug(runtime) << "NonVFO widget - skipping"; return; } qint32 dxcc = record.value("dxcc").toInt(); const QString &band = record.value("band").toString(); const QString &dxccModeGroup = BandPlan::modeToDXCCModeGroup(record.value("mode").toString()); const QString &callsign = record.value("callsign").toString(); for ( auto it = spots.begin(); it != spots.end(); ++it ) { DxSpot &spot = it.value(); spot.status = Data::dxccNewStatusWhenQSOAdded(spot.status, spot.dxcc.dxcc, spot.band, ( ( spot.modeGroupString == BandPlan::MODE_GROUP_STRING_FTx ) ? BandPlan::MODE_GROUP_STRING_DIGITAL : dxccModeGroup ), dxcc, band, dxccModeGroup); if ( spot.callsign == callsign ) spot.dupeCount = Data::dupeNewCountWhenQSOAdded(spot.dupeCount, spot.band, spot.modeGroupString, band, dxccModeGroup); } updateStations(); if ( callsign == lastNearestSpot.callsign ) updateNearestSpot(true); } void BandmapWidget::updateSpotsStatusWhenQSOUpdated(const QSqlRecord &) { FCT_IDENTIFICATION; // at this point, we don't know if callsign has been changed or other field. // TODO: DXCC status // TODO: Dupe status update // for ( auto it = spots.begin(); it != spots.end(); ++it ) // { // DxSpot &spot = it.value(); // spot.dupeCount = Data::countDupe(spot.callsign, spot.band, spot.modeGroupString); // } } void BandmapWidget::updateSpotsDupeWhenQSODeleted(const QSqlRecord &record) { FCT_IDENTIFICATION; // only master Widget updates spots if ( isNonVfo ) { qCDebug(runtime) << "NonVFO widget - skipping"; return; } // Pay attention: this method is called before the QSO is deleted from contacts const QString &callsign = record.value("callsign").toString(); const QString &band = record.value("band").toString(); const QString &dxccModeGroup = BandPlan::modeToDXCCModeGroup(record.value("mode").toString()); for ( auto it = spots.begin(); it != spots.end(); ++it ) { DxSpot &spot = it.value(); if ( spot.dupeCount && spot.callsign == callsign ) spot.dupeCount = Data::dupeNewCountWhenQSODelected(spot.dupeCount, spot.band, spot.modeGroupString, band, dxccModeGroup); } // do not call updateStation. it will be updated at the end of delete procedure // by updateSpotsDxccStatusWhenQSODeleted; } void BandmapWidget::updateSpotsDxccStatusWhenQSODeleted(const QSet &entities) { FCT_IDENTIFICATION; // only master Widget updates spots if ( isNonVfo ) { qCDebug(runtime) << "NonVFO widget - skipping"; return; } // this method is called at the end of QSO Delete (after commit). if ( entities.isEmpty() ) return; for ( auto it = spots.begin(); it != spots.end(); ++it ) { DxSpot &spot = it.value(); if ( !entities.contains(spot.dxcc.dxcc) ) continue; spot.status = Data::instance()->dxccStatus(spot.dxcc.dxcc, spot.band, spot.modeGroupString); } updateStations(); updateNearestSpot(true); } void BandmapWidget::recalculateDxccStatus() { FCT_IDENTIFICATION; // only master Widget updates spots if ( isNonVfo ) { qCDebug(runtime) << "NonVFO widget - skipping"; return; } for ( auto it = spots.begin(); it != spots.end(); ++it ) { DxSpot &spot = it.value(); spot.status = Data::instance()->dxccStatus(spot.dxcc.dxcc, spot.band, spot.modeGroupString); } updateStations(); updateNearestSpot(true); } void BandmapWidget::resetDupe() { FCT_IDENTIFICATION; // only master Widget updates spots if ( isNonVfo ) { qCDebug(runtime) << "NonVFO widget - skipping"; return; } for ( auto it = spots.begin(); it != spots.end(); ++it ) it.value().dupeCount = 0; updateStations(); } void BandmapWidget::recalculateDupe() { FCT_IDENTIFICATION; // only master Widget updates spots if ( isNonVfo ) { qCDebug(runtime) << "NonVFO widget - skipping"; return; } for ( auto it = spots.begin(); it != spots.end(); ++it ) { DxSpot &spot = it.value(); spot.dupeCount = Data::countDupe(spot.callsign, spot.band, spot.modeGroupString); } updateStations(); } void BandmapWidget::focusZoomFreq(int, int) { FCT_IDENTIFICATION; if ( zoomFreq > 0.0 ) { int newScrollValue = qMin(qMax(Freq2ScenePos(zoomFreq).y() - ( zoomWidgetYOffset ), 0.0), (double)ui->scrollArea->verticalScrollBar()->maximum()); ui->scrollArea->verticalScrollBar()->setValue(newScrollValue); zoomFreq = 0.0; } } void BandmapWidget::clickNewBandmapWindow() { FCT_IDENTIFICATION; const QString widgetID = QString("bandmap%1").arg(QDateTime::currentMSecsSinceEpoch()); saveCurrentZoom(); emit requestNewNonVfoBandmapWindow(widgetID, currentBand.name); } void BandmapWidget::spotClicked(const QString &call, double freq, BandPlan::BandPlanMode mode) { FCT_IDENTIFICATION; qCDebug(function_parameters) << call << freq << mode; qCDebug(runtime) << "Last Tuned DX" << lastTunedDX.callsign << lastTunedDX.freq; /* Do not emit the Spot two times - double click*/ if ( lastTunedDX.callsign == call && lastTunedDX.freq == freq ) return; emit tuneDx(spots.value(freq)); lastTunedDX.callsign = call; lastTunedDX.freq = freq; // allow to re-click the spot. // I don't want to use double click here because users expect to single-clicking on the map and double click // can emit tuneDX two times what causes an issue in Callbooks section and other parts of QLog. // However, to be able to click on the callsign again, it needs to be disabled for a short while. // That's why there is a delay. QTimer::singleShot(3000, this, [this]() { lastTunedDX.callsign.clear(); lastTunedDX.freq = 0.0; }); } void BandmapWidget::showContextMenu(const QPoint &point) { FCT_IDENTIFICATION; if ( ui->graphicsView->itemAt(point) ) { return; } QMenu contextMenu(this); QMenu bandsMenu(tr("Show Band"), &contextMenu); for ( const Band &enabledBand : BandPlan::bandsList(false, true)) { QAction* action = new QAction(enabledBand.name); connect(action, &QAction::triggered, this, [this, enabledBand]() { setBand(enabledBand); update(); }); if ( enabledBand == currentBand ) { action->setCheckable(true); action->setChecked(true); } bandsMenu.addAction(action); } QAction* centerRXAction = new QAction(tr("Center RX"), &contextMenu); centerRXAction->setCheckable(true); centerRXAction->setChecked(keepRXCenter); connect(centerRXAction, &QAction::triggered, this, &BandmapWidget::centerRXActionChecked); QAction* emergencyAction = new QAction(tr("Show Emergency Frequencies"), &contextMenu); emergencyAction->setCheckable(true); emergencyAction->setChecked(showEmergencyMarkers); connect(emergencyAction, &QAction::triggered, this, &BandmapWidget::emergencyMarkersActionChecked); contextMenu.addMenu(&bandsMenu); contextMenu.addAction(centerRXAction); contextMenu.addAction(emergencyAction); contextMenu.exec(ui->graphicsView->mapToGlobal(point)); } void BandmapWidget::updateTunedFrequency(VFOID vfoid, double vfoFreq, double ritFreq, double xitFreq) { FCT_IDENTIFICATION; qCDebug(function_parameters) << vfoid << vfoFreq << ritFreq << xitFreq; // Bandmap tracks the RX frequency only if ( vfoid == VFO2 ) return; lastSeenVFOFreq = vfoFreq; isActive = (ritFreq >= currentBand.start && ritFreq <= currentBand.end); if ( isNonVfo ) { if ( isActive ) { rx_freq = ritFreq; tx_freq = xitFreq; // bandmap is showing the tuned frequency, so show the marker drawMarkers(rx_freq); } else { rx_freq = (rx_freq == 0.0) ? getSavedScrollFreq(currentBand) : rx_freq; tx_freq = (tx_freq == 0.0) ? getSavedScrollFreq(currentBand) : tx_freq; } } else { if ( !isActive ) { /* Operator switched a band */ const Band& newBand = BandPlan::freq2Band(ritFreq); if ( !newBand.name.isEmpty() && !isAlreadyOpened(newBand) ) { rx_freq = ritFreq; tx_freq = xitFreq; isActive = true; setBand(newBand); /**********************/ /* Redraw all bandmap */ /**********************/ update(); } else { rx_freq = (rx_freq == 0.0) ? getSavedScrollFreq(currentBand) : rx_freq; tx_freq = (tx_freq == 0.0) ? getSavedScrollFreq(currentBand) : tx_freq; } } else { rx_freq = ritFreq; tx_freq = xitFreq; drawMarkers(rx_freq); } updateNearestSpot(); } if ( !isActive ) { clearFreqMark(&txMark); clearFreqMark(&rxMark); } } void BandmapWidget::drawEmergencyMarkers(double step) { FCT_IDENTIFICATION; if ( !showEmergencyMarkers ) return; QFont emergencyFont; emergencyFont.setPointSize(7); emergencyFont.setBold(true); const QColor lineColor(220, 40, 40); const QColor pillColor(185, 28, 28); const qreal pillX = 157.0; const qreal pillH = 14.0; const qreal tolerancePx = (EmergencyFrequency::TOLERANCE_MHZ / step) * PIXELSPERSTEP; const qreal glowH = qMin(tolerancePx, 15.0); const EmergencyFreqEntry *entry = EmergencyFrequency::inBand(currentBand.start, currentBand.end); if ( !entry ) return; const qreal y = ((entry->frequency - currentBand.start) / step) * PIXELSPERSTEP; // Gradient glow — height proportional to zoom, fades to transparent at edges QLinearGradient glow(0.0, y - glowH, 0.0, y + glowH); glow.setColorAt(0.0, QColor(220, 30, 30, 0)); glow.setColorAt(0.35, QColor(220, 30, 30, 70)); glow.setColorAt(0.5, QColor(220, 30, 30, 115)); glow.setColorAt(0.65, QColor(220, 30, 30, 70)); glow.setColorAt(1.0, QColor(220, 30, 30, 0)); bandmapScene->addRect(0, y - glowH, pillX, 2.0 * glowH, QPen(Qt::NoPen), QBrush(glow)); // Sharp centre line — runs from the scale up to the SOS pill bandmapScene->addLine(0, y, pillX, y, QPen(lineColor, 2)); // Pill label — sized dynamically around the text QGraphicsSimpleTextItem *textItem = bandmapScene->addSimpleText(tr("SOS"), emergencyFont); textItem->setBrush(QBrush(Qt::white)); const QRectF textRect = textItem->boundingRect(); const qreal pillW = textRect.width() + 10.0; const qreal pillY = y - pillH / 2.0; QPainterPath pillPath; pillPath.addRoundedRect(QRectF(pillX, pillY, pillW, pillH), 4, 4); QGraphicsPathItem *pillItem = bandmapScene->addPath(pillPath, QPen(Qt::NoPen), QBrush(pillColor)); textItem->setPos(pillX + (pillW - textRect.width()) / 2.0, pillY + (pillH - textRect.height()) / 2.0); pillItem->setZValue(1); textItem->setZValue(2); } void BandmapWidget::drawMarkers(double frequency) { FCT_IDENTIFICATION; double step; int digits; determineStepDigits(step, digits); /************************/ /* Draw TX and RX Marks */ /************************/ drawTXRXMarks(step); scrollToFreq(frequency); } void BandmapWidget::updateMode(VFOID, const QString &, const QString &mode, const QString &subMode, qint32 width) { FCT_IDENTIFICATION; qCDebug(function_parameters) << mode << subMode << width; const QString &newMode = BandPlan::modeToModeGroup(mode); if ( currBandMode != newMode ) { currBandMode = newMode; update(); } } BandmapWidget::~BandmapWidget() { FCT_IDENTIFICATION; if ( update_timer ) { update_timer->stop(); update_timer->deleteLater(); } nonVfoWidgets.removeAll(this); delete ui; } void BandmapWidget::resizeEvent(QResizeEvent *event) { FCT_IDENTIFICATION; QWidget::resizeEvent(event); scrollToFreq(rx_freq); } bool BandmapWidget::eventFilter(QObject *, QEvent *event) { FCT_IDENTIFICATION; if (event->type() == QEvent::Wheel) { QWheelEvent *wheelEvent = dynamic_cast(event); if ( wheelEvent ) { if ( QApplication::keyboardModifiers() == Qt::ControlModifier ) { /* * CRTL + Mouse Wheel * * Zoom In/Out */ QPoint zoomViewPoint = ui->graphicsView->mapFromGlobal(QCursor::pos()); zoomWidgetYOffset = zoomViewPoint.y() - ui->scrollArea->verticalScrollBar()->value(); zoomFreq = ScenePos2Freq(ui->graphicsView->mapToScene(zoomViewPoint)); QPoint wheelDelta(wheelEvent->angleDelta()); int delta = (wheelDelta.y() > 0) ? 1 : -1; ui->zoomSlider->setValue(ui->zoomSlider->value() + delta * ui->zoomSlider->singleStep()); /* * DO NOT focus zoomed Freq here because the scrollbar * is not resized yet and it is not possible to compute * a correct value for scrollbar value (scrollbar min/max * is recomputed later and it emits RangeChanged signal). * SO focus zoomed Freq in SLOT for RangeChanged signal. */ event->accept(); return true; } } } return false; } void BandmapWidget::scrollToFreq(double freq) { FCT_IDENTIFICATION; qreal freqScenePos = Freq2ScenePos(freq).y(); QPropertyAnimation *anim = new QPropertyAnimation(ui->scrollArea->verticalScrollBar(), "value", this); anim->setDuration((bandmapAnimation) ? 300 : 0); anim->setStartValue(ui->scrollArea->verticalScrollBar()->value()); if ( keepRXCenter ) { /* If RX freq should be center then center it */ anim->setEndValue(freqScenePos - (WIDGET_CENTER)); } else { /* If RX freq is out-of-scene then keep the RX mark visible - this is not centering !!! */ int sceneSize = height() - 60; int sliderSceneMin = ui->scrollArea->verticalScrollBar()->value(); int sliderSceneMax = ui->scrollArea->verticalScrollBar()->value() + sceneSize; if ( freqScenePos < sliderSceneMin ) { anim->setEndValue(sliderSceneMin - (sliderSceneMin - freqScenePos) - 40); } else if ( freqScenePos > sliderSceneMax - 20 ) //asymetric becuase possible slider below { anim->setEndValue(sliderSceneMin + (freqScenePos - sliderSceneMax) + 60); } else { anim->setEndValue(ui->scrollArea->verticalScrollBar()->value()); } } anim->start(QAbstractAnimation::DeleteWhenStopped); } QPointF BandmapWidget::Freq2ScenePos(const double freq) const { FCT_IDENTIFICATION; qCDebug(function_parameters) << freq; if ( freq < currentBand.start || freq > currentBand.end ) { return QPointF(); } double step; int digits; determineStepDigits(step, digits); QPointF ret(0, ((freq - currentBand.start) / step) * PIXELSPERSTEP); qCDebug(runtime) << ret; return ret; } double BandmapWidget::ScenePos2Freq(const QPointF &point) const { FCT_IDENTIFICATION; qCDebug(function_parameters) << point; double step; int digits; determineStepDigits(step, digits); double ret = currentBand.start + (point.y() / PIXELSPERSTEP) * step; if ( ret > currentBand.end ) { ret = currentBand.end; } if ( ret < currentBand.start ) { ret = currentBand.start; } qCDebug(runtime) << ret; return ret; } void BandmapWidget::centerRXActionChecked(bool state) { FCT_IDENTIFICATION; keepRXCenter = state; zoomFreq = 0.0; LogParam::setBandmapCenterRX(objectName(), keepRXCenter); if ( keepRXCenter ) scrollToFreq(rx_freq); } void BandmapWidget::emergencyMarkersActionChecked(bool state) { FCT_IDENTIFICATION; showEmergencyMarkers = state; LogParam::setBandmapShowEmergency(objectName(), showEmergencyMarkers); update(); } void GraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *evt) { FCT_IDENTIFICATION; if ( evt->button() & Qt::LeftButton ) { QGraphicsItem *item = itemAt(evt->scenePos(), QTransform()); QGraphicsTextItem *focusedSpot = dynamic_cast(item); if ( focusedSpot && focusedSpot->property("freq").isValid() ) emit spotClicked(focusedSpot->toPlainText().split(" ").first(), focusedSpot->property("freq").toDouble(), static_cast(focusedSpot->property("bandmode").toInt())); } evt->accept(); } void GraphicsScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *evt) { FCT_IDENTIFICATION; evt->accept(); } #undef WIDGET_CENTER ================================================ FILE: ui/BandmapWidget.h ================================================ #ifndef QLOG_UI_BANDMAPWIDGET_H #define QLOG_UI_BANDMAPWIDGET_H #include #include #include #include #include #include #include #include #include "data/DxSpot.h" #include "data/Band.h" #include "rig/Rig.h" #include "core/LogLocale.h" #include "component/ShutdownAwareWidget.h" namespace Ui { class BandmapWidget; } class QGraphicsScene; class GraphicsScene : public QGraphicsScene { Q_OBJECT; public: explicit GraphicsScene(QObject *parent = nullptr) : QGraphicsScene(parent){}; signals: void spotClicked(QString, double, BandPlan::BandPlanMode mode); protected: void mousePressEvent (QGraphicsSceneMouseEvent *evt) override; void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *evt) override; }; class BandmapWidget : public QWidget, public ShutdownAwareWidget { Q_OBJECT public: explicit BandmapWidget(const QString &widgetID = QString(), const Band &widgetBand = Band(), QWidget *parent = nullptr); ~BandmapWidget(); const Band& getBand() const {return currentBand;}; const QList getNonVfoWidgetList() {return nonVfoWidgets;}; enum BandmapZoom { ZOOM_100HZ = 6, ZOOM_250HZ = 5, ZOOM_500HZ = 4, ZOOM_1KHZ = 3, ZOOM_2K5HZ = 2, ZOOM_5KHZ = 1, ZOOM_10KHZ = 0 }; public slots: void update(); void updateTunedFrequency(VFOID, double, double, double); void updateMode(VFOID, const QString &, const QString &mode, const QString &subMode, qint32 width); void addSpot(DxSpot spot); void spotAgingChanged(int); void clearSpots(); void setZoom(int); void updateSpotsStatusWhenQSOAdded(const QSqlRecord &record); void updateSpotsStatusWhenQSOUpdated(const QSqlRecord &); void updateSpotsDupeWhenQSODeleted(const QSqlRecord &record); void updateSpotsDxccStatusWhenQSODeleted(const QSet &entities); void recalculateDxccStatus(); void resetDupe(); void recalculateDupe(); void updateStations(); void clearWidgetBand(); virtual void finalizeBeforeAppExit() override; void increasePendingSpots() {pendingSpots++;}; signals: void tuneDx(DxSpot); void nearestSpotFound(const DxSpot &); void spotsUpdated(); void requestNewNonVfoBandmapWindow(const QString &id, const QString &bandName); private: void removeDuplicates(DxSpot &spot); void spotAging(); void determineStepDigits(double &step, int &digits) const; void clearAllCallsignFromScene(); void clearFreqMark(QGraphicsPolygonItem **); void drawFreqMark(const double, const double, const QColor&, QGraphicsPolygonItem **); void drawTXRXMarks(double); void drawEmergencyMarkers(double step); void drawMarkers(double frequency); void resizeEvent(QResizeEvent * event) override; bool eventFilter(QObject *obj, QEvent *event) override; void scrollToFreq(double freq); QPointF Freq2ScenePos(const double) const; double ScenePos2Freq(const QPointF &point) const; DxSpot nearestSpot(const double) const; void updateNearestSpot(bool force = false); void setBandmapAnimation(bool); void setBand(const Band &newBand, bool savePrevBandZoom = true); void saveCurrentZoom(); BandmapWidget::BandmapZoom getSavedZoom(const Band &); void saveCurrentScrollFreq(); double getSavedScrollFreq(const Band &); double visibleCentreFreq() const; bool isAlreadyOpened(const Band &band) const; void saveState(); private slots: void centerRXActionChecked(bool); void emergencyMarkersActionChecked(bool); void spotClicked(const QString&, double, BandPlan::BandPlanMode); void showContextMenu(const QPoint&); void updateStationTimer(); void focusZoomFreq(int, int); void clickNewBandmapWindow(); private: Ui::BandmapWidget *ui; double rx_freq; double tx_freq; Band currentBand; BandmapZoom zoom; GraphicsScene* bandmapScene; static QMap spots; static QList nonVfoWidgets; static BandmapWidget* vfoWidget; static double lastSeenVFOFreq; QTimer *update_timer; QList lineItemList; QList textItemList; QGraphicsPolygonItem* rxMark; QGraphicsPolygonItem* txMark; bool keepRXCenter; bool showEmergencyMarkers; LogLocale locale; quint32 pendingSpots; qint64 lastStationUpdate; double zoomFreq; int zoomWidgetYOffset; bool bandmapAnimation; QString currBandMode; bool isNonVfo; bool isActive; struct LastTuneDx { QString callsign; double freq; }; LastTuneDx lastTunedDX; DxSpot lastNearestSpot; double minHeight; const QString MAIN_WIDGET_OBJECT_NAME = "bandmapWidget"; //Pixel between each step in BandMap const int PIXELSPERSTEP = 10; //Maximal Aging interval is 20s const int BANDMAP_AGING_CHECK_TIME = 20000; //Maximal refresh rate for bandmap is 1s const int BANDMAP_MAX_REFRESH_TIME = 1000; }; Q_DECLARE_METATYPE(BandmapWidget::BandmapZoom) #endif // QLOG_UI_BANDMAPWIDGET_H ================================================ FILE: ui/BandmapWidget.ui ================================================ BandmapWidget 0 0 283 662 250 0 Form 0 0 0 0 0 0 0 0 Qt::ClickFocus Create an additional Bandmap Window :/icons/new-window.svg:/icons/new-window.svg Qt::Horizontal 40 20 Clear older 0 0 Qt::ClickFocus Never min(s) 9999 Qt::ClickFocus Clear All Qt::ClickFocus QFrame::NoFrame QFrame::Plain -3 true 0 0 283 590 0 0 0 0 0 0 0 0 0 Qt::ClickFocus Qt::CustomContextMenu QWidget{background: transparent} QFrame::NoFrame Qt::ScrollBarAsNeeded Qt::ScrollBarAsNeeded QAbstractScrollArea::AdjustIgnored Qt::ClickFocus Clear the current band :/icons/clear-button.svg:/icons/clear-button.svg 24 24 Qt::Horizontal 40 20 0 0 Qt::ClickFocus 0 6 1 Qt::Horizontal false false QSlider::TicksAbove 0 0 :/icons/zoom_in-24px.svg Qt::NoTextInteraction graphicsView customContextMenuRequested(QPoint) BandmapWidget showContextMenu(QPoint) 225 297 225 330 clearAllButton clicked() BandmapWidget clearSpots() 149 617 155 281 newBandmapButton clicked() BandmapWidget clickNewBandmapWindow() 426 638 225 330 zoomSlider valueChanged(int) BandmapWidget setZoom(int) 377 612 225 330 clearBandButton clicked() BandmapWidget clearWidgetBand() 14 613 225 330 clearSpotOlderSpin valueChanged(int) BandmapWidget spotAgingChanged(int) 309 13 225 330 clearSpots() spotAgingChanged(int) showContextMenu(QPoint) clickNewBandmapWindow() setZoom(int) clearWidgetBand() ================================================ FILE: ui/CWConsoleWidget.cpp ================================================ #include #include #include "CWConsoleWidget.h" #include "ui_CWConsoleWidget.h" #include "core/debug.h" #include "data/CWKeyProfile.h" #include "cwkey/CWKeyer.h" #include "data/CWShortcutProfile.h" #include "core/LogParam.h" #include "component/RepeatButton.h" MODULE_IDENTIFICATION("qlog.ui.cwconsolewidget"); CWConsoleWidget::CWConsoleWidget(QWidget *parent) : QWidget(parent), ui(new Ui::CWConsoleWidget), cwKeyOnline(false), contact(nullptr), sendWord(false), macroButtonsConnected(false) { FCT_IDENTIFICATION; ui->setupUi(this); connect(ui->macroButtonGroup, QOverload::of(&QButtonGroup::buttonClicked), this, [=](const QAbstractButton *button) { int number = getMacroButtonID(button) - 1; qCDebug(runtime) << "Clicked number" << number; const CWShortcutProfile &profile = CWShortcutProfilesManager::instance()->getCurProfile1(); if ( number >= 0 && number < profile.macros.size()) sendCWText(profile.macros[number]); const QList ¯oButtonList = ui->macroButtonGroup->buttons(); for (QAbstractButton *btn : macroButtonList) { RepeatButton *rptButton = qobject_cast(btn); if ( rptButton && rptButton != button ) rptButton->stop(); } }); QStringListModel* keyModel = new QStringListModel(this); ui->cwKeyProfileCombo->setModel(keyModel); QStringListModel* shortcutModel = new QStringListModel(this); ui->cwShortcutProfileCombo->setModel(shortcutModel); refreshKeyProfileCombo(); refreshShortcutProfileCombo(); connect(ui->modeSwitch, &SwitchButton::stateChanged, this, &CWConsoleWidget::sendWordSwitched); sendWord = getSendWordConfig(); ui->modeSwitch->setChecked(sendWord); addAction(ui->actionHaltCW); cwKeyDisconnected(); } CWConsoleWidget::~CWConsoleWidget() { FCT_IDENTIFICATION; delete ui; } void CWConsoleWidget::registerContactWidget(const NewContactWidget * contactWidget) { FCT_IDENTIFICATION; contact = contactWidget; } void CWConsoleWidget::appendCWEchoText(QString text) { FCT_IDENTIFICATION; qCDebug(function_parameters) << text; if ( ui->cwEchoConsoleText->isEnabled() ) { ui->cwEchoConsoleText->moveCursor(QTextCursor::End); ui->cwEchoConsoleText->insertPlainText(text); } } void CWConsoleWidget::cwKeyProfileComboChanged(QString profileName) { FCT_IDENTIFICATION; qCDebug(function_parameters) << profileName; CWKeyProfilesManager::instance()->setCurProfile1(profileName); emit cwKeyProfileChanged(); } void CWConsoleWidget::cwShortcutProfileComboChanged(QString profileName) { FCT_IDENTIFICATION; qCDebug(function_parameters) << profileName; CWShortcutProfilesManager *shortcutManager = CWShortcutProfilesManager::instance(); shortcutManager->setCurProfile1(profileName); const CWShortcutProfile &profile = shortcutManager->getCurProfile1(); auto setupMacroButton = [&](RepeatButton *button, int index, int key) { button->setText(QString("F%1\n%2").arg(index+1).arg(profile.shortDescs[index])); button->setToolTip(profile.macros[index]); button->stop(); button->resetInterval(); if ( !macroButtonsConnected ) { connect(new QShortcut(QKeySequence(key), button), &QShortcut::activated, button, [button]{ button->handleClick(); }); connect(new QShortcut(QKeySequence(Qt::SHIFT | key), button), &QShortcut::activated, button, [button]{ button->repeatClick(); }); } }; setupMacroButton(ui->macroButton1, 0, Qt::Key_F1); setupMacroButton(ui->macroButton2, 1, Qt::Key_F2); setupMacroButton(ui->macroButton3, 2, Qt::Key_F3); setupMacroButton(ui->macroButton4, 3, Qt::Key_F4); setupMacroButton(ui->macroButton5, 4, Qt::Key_F5); setupMacroButton(ui->macroButton6, 5, Qt::Key_F6); setupMacroButton(ui->macroButton7, 6, Qt::Key_F7); macroButtonsConnected = true; emit cwShortcutProfileChanged(); } void CWConsoleWidget::cwShortcutProfileIncrease() { FCT_IDENTIFICATION; shortcutComboMove(1); } void CWConsoleWidget::cwShortcutProfileDecrease() { FCT_IDENTIFICATION; shortcutComboMove(-1); } void CWConsoleWidget::shortcutComboMove(int step) { FCT_IDENTIFICATION; qCDebug(function_parameters) << step; int count = ui->cwShortcutProfileCombo->count(); int currIndex = ui->cwShortcutProfileCombo->currentIndex(); if ( count > 0 && currIndex != -1 && step < count ) { int nextIndex = currIndex + step; nextIndex += (1 - nextIndex / count) * count; ui->cwShortcutProfileCombo->setCurrentIndex(nextIndex % count); } } void CWConsoleWidget::allowMorseSending(bool allow) { FCT_IDENTIFICATION; if ( allow ) { ui->cwEchoConsoleText->setEnabled(CWKeyer::instance()->canEchoChar()); ui->haltButton->setEnabled(CWKeyer::instance()->canStopSending()); ui->cwKeySpeedSpinBox->setEnabled(CWKeyer::instance()->canSetSpeed()); } else { ui->cwEchoConsoleText->setEnabled(allow); ui->haltButton->setEnabled(allow); ui->cwKeySpeedSpinBox->setEnabled(allow); } ui->clearButton->setEnabled(allow); ui->cwConsoleText->setEnabled(allow); ui->cwSendEdit->setEnabled(allow); ui->cwShortcutProfileCombo->setEnabled(allow); const QList ¯oButtonList = ui->macroButtonGroup->buttons(); for (QAbstractButton *btn : macroButtonList) { RepeatButton *rptButton = qobject_cast(btn); if ( rptButton ) rptButton->stop(); btn->setEnabled(allow); } ui->modeSwitch->setEnabled(allow); } void CWConsoleWidget::saveSendWordConfig(bool state) { FCT_IDENTIFICATION; LogParam::setCWConsoleSendWord(state); } bool CWConsoleWidget::getSendWordConfig() { FCT_IDENTIFICATION; return LogParam::getCWConsoleSendWord(); } int CWConsoleWidget::getMacroButtonID(const QAbstractButton *button) { FCT_IDENTIFICATION; QString name = button->objectName(); name.remove("macroButton"); return name.toInt(); } void CWConsoleWidget::refreshKeyProfileCombo() { FCT_IDENTIFICATION; ui->cwKeyProfileCombo->blockSignals(true); CWKeyProfilesManager *cwKeyManager = CWKeyProfilesManager::instance(); QStringList currProfiles = cwKeyManager->profileNameList(); QStringListModel* model = dynamic_cast(ui->cwKeyProfileCombo->model()); model->setStringList(currProfiles); if ( cwKeyManager->getCurProfile1().profileName.isEmpty() && currProfiles.count() > 0 ) { /* changing profile from empty to something */ ui->cwKeyProfileCombo->setCurrentText(currProfiles.first()); } else { /* no profile change, just refresh the combo and preserve current profile */ ui->cwKeyProfileCombo->setCurrentText(cwKeyManager->getCurProfile1().profileName); } cwKeyProfileComboChanged(ui->cwKeyProfileCombo->currentText()); ui->cwKeyProfileCombo->blockSignals(false); } void CWConsoleWidget::refreshShortcutProfileCombo() { FCT_IDENTIFICATION; ui->cwShortcutProfileCombo->blockSignals(true); CWShortcutProfilesManager *shortcutManager = CWShortcutProfilesManager::instance(); QStringList currProfiles = shortcutManager->profileNameList(); QStringListModel* model = dynamic_cast(ui->cwShortcutProfileCombo->model()); model->setStringList(currProfiles); if ( shortcutManager->getCurProfile1().profileName.isEmpty() && currProfiles.count() > 0 ) { /* changing profile from empty to something */ ui->cwShortcutProfileCombo->setCurrentText(currProfiles.first()); } else { /* no profile change, just refresh the combo and preserve current profile */ ui->cwShortcutProfileCombo->setCurrentText(shortcutManager->getCurProfile1().profileName); } cwShortcutProfileComboChanged(ui->cwShortcutProfileCombo->currentText()); ui->cwShortcutProfileCombo->blockSignals(false); } void CWConsoleWidget::reloadSettings() { FCT_IDENTIFICATION; refreshKeyProfileCombo(); refreshShortcutProfileCombo(); } void CWConsoleWidget::clearConsoles() { FCT_IDENTIFICATION; ui->cwConsoleText->clear(); ui->cwEchoConsoleText->clear(); } void CWConsoleWidget::cwKeyConnected(QString profile) { FCT_IDENTIFICATION; ui->cwKeyProfileCombo->setStyleSheet("QComboBox {color: green}"); if ( profile != ui->cwKeyProfileCombo->currentText() ) { ui->cwKeyProfileCombo->blockSignals(true); ui->cwKeyProfileCombo->setCurrentText(profile); ui->cwKeyProfileCombo->blockSignals(false); } allowMorseSending(true); ui->cwKeySpeedSpinBox->setValue(CWKeyProfilesManager::instance()->getCurProfile1().defaultSpeed); cwKeyOnline = true; ui->cwSendEdit->setPlaceholderText(QString()); } void CWConsoleWidget::cwKeyDisconnected() { FCT_IDENTIFICATION; ui->cwKeyProfileCombo->setStyleSheet("QComboBox {color: red}"); allowMorseSending(false); clearConsoles(); cwKeyOnline = false; } void CWConsoleWidget::cwKeySpeedChanged(int newWPM) { FCT_IDENTIFICATION; qCDebug(function_parameters) << newWPM; CWKeyer::instance()->setSpeed(newWPM); Rig::instance()->syncKeySpeed(newWPM); } void CWConsoleWidget::cwKeySpeedIncrease() { FCT_IDENTIFICATION; if ( !CWKeyer::instance()->canSetSpeed() ) return; ui->cwKeySpeedSpinBox->setValue(ui->cwKeySpeedSpinBox->value() + 2); } void CWConsoleWidget::cwKeySpeedDecrease() { FCT_IDENTIFICATION; if ( !CWKeyer::instance()->canSetSpeed() ) return; ui->cwKeySpeedSpinBox->setValue(ui->cwKeySpeedSpinBox->value() - 2); } void CWConsoleWidget::cwSendButtonPressed(bool insertNewLine) { FCT_IDENTIFICATION; if ( ui->cwSendEdit->text().isEmpty() ) { return; } sendCWText(ui->cwSendEdit->text(), insertNewLine); ui->cwSendEdit->clear(); } void CWConsoleWidget::rigDisconnectHandler() { FCT_IDENTIFICATION; if ( CWKeyer::instance()->rigMustConnected() ) { allowMorseSending(false); if ( cwKeyOnline ) { ui->cwSendEdit->setPlaceholderText(tr("Rig must be connected")); } } } void CWConsoleWidget::rigConnectHandler() { FCT_IDENTIFICATION; if ( cwKeyOnline ) { // if MorseOverCat Key if (CWKeyer::instance()->rigMustConnected() && Rig::instance()->isMorseOverCatSupported() ) { allowMorseSending(true); CWKeyer::instance()->setSpeed(ui->cwKeySpeedSpinBox->value()); ui->cwSendEdit->setPlaceholderText(QString()); } else { Rig::instance()->syncKeySpeed(ui->cwKeySpeedSpinBox->value()); } } } void CWConsoleWidget::haltButtonPressed() { FCT_IDENTIFICATION; ui->macroButton1->stop(); if ( !ui->haltButton->isEnabled() ) return; CWKeyer::instance()->immediatelyStop(); } void CWConsoleWidget::pressMacroButton(int buttonNumber) { FCT_IDENTIFICATION; qCDebug(function_parameters) << buttonNumber; const CWShortcutProfile &profile = CWShortcutProfilesManager::instance()->getCurProfile1(); int index = buttonNumber - 1; if ( index >= 0 && index < profile.macros.size()) sendCWText(profile.macros[index]); } void CWConsoleWidget::stopRepeateButtons() { const QList ¯oButtonList = ui->macroButtonGroup->buttons(); for (QAbstractButton *btn : macroButtonList) { RepeatButton *rptButton = qobject_cast(btn); if ( rptButton ) rptButton->stop(); } } void CWConsoleWidget::sendWordSwitched(int mode) { FCT_IDENTIFICATION; sendWord = (mode == Qt::Checked) ? true: false; ui->sendModeLabel->setText(sendWord ? tr("Word") : tr("Whole")); saveSendWordConfig(sendWord); } void CWConsoleWidget::cwTextChanged(QString text) { FCT_IDENTIFICATION; qCDebug(function_parameters) << text << text.endsWith(" ") << text.size() << sendWord; if ( sendWord && text.size() > 0 && text.endsWith(" ") ) { cwSendButtonPressed(false); } } void CWConsoleWidget::setWPM(qint32 wpm) { FCT_IDENTIFICATION; ui->cwKeySpeedSpinBox->blockSignals(true); ui->cwKeySpeedSpinBox->setValue(wpm); Rig::instance()->syncKeySpeed(wpm); ui->cwKeySpeedSpinBox->blockSignals(false); } void CWConsoleWidget::sendCWText(const QString &text, bool insertNewLine) { FCT_IDENTIFICATION; QString expandedText(text.toUpper()); expandMacros(expandedText); qCDebug(runtime) << "CW text" << expandedText; QString newLine = (insertNewLine ? QString("\n") : QString()); CWKeyer::instance()->sendText(expandedText + QString(" ") + newLine); //insert extra space do divide words in echo console newLine.replace("\n", QString::fromUtf8("\u23ce\n")); // UTF8 Return char ui->cwConsoleText->moveCursor(QTextCursor::End); ui->cwConsoleText->insertPlainText(expandedText + newLine); } void CWConsoleWidget::expandMacros(QString &text) { FCT_IDENTIFICATION; static QRegularExpression callRE(""); static QRegularExpression nameRE(""); static QRegularExpression rstRE(""); static QRegularExpression rstnRE(""); static QRegularExpression greetingRE(""); static QRegularExpression qthRE(""); static QRegularExpression myCallRE(""); static QRegularExpression myNameRE(""); static QRegularExpression myQTHRE(""); static QRegularExpression myLocatorRE(""); static QRegularExpression myGridRE(""); static QRegularExpression mySIGRE(""); static QRegularExpression mySIGInfoRE(""); static QRegularExpression myIOTARE(""); static QRegularExpression mySOTARE(""); static QRegularExpression myWWFTRE(""); static QRegularExpression myVUCCRE(""); static QRegularExpression myPWRRE(""); static QRegularExpression myEXCHSTRRE(""); static QRegularExpression myEXCHNRRE(""); static QRegularExpression myEXCHNRNRE(""); if ( contact ) { text.replace(callRE, contact->getCallsign().toUpper()); text.replace(nameRE, contact->getName().toUpper()); text.replace(rstRE, contact->getRST().toUpper()); text.replace(rstnRE, contact->getRST().replace('9', 'N')); text.replace(greetingRE, contact->getGreeting().toUpper()); text.replace(qthRE, contact->getQTH()); text.replace(myCallRE, contact->getMyCallsign().toUpper()); text.replace(myNameRE, contact->getMyName().toUpper()); text.replace(myQTHRE, contact->getMyQTH().toUpper()); text.replace(myLocatorRE, contact->getMyLocator().toUpper()); text.replace(myGridRE, contact->getMyLocator().toUpper()); text.replace(mySIGRE, contact->getMySIG().toUpper()); text.replace(mySIGInfoRE, contact->getMySIGInfo().toUpper()); text.replace(myIOTARE, contact->getMyIOTA().toUpper()); text.replace(mySOTARE, contact->getMySOTA().toUpper()); text.replace(myWWFTRE, contact->getMyWWFT().toUpper()); text.replace(myVUCCRE, contact->getMyVUCC().toUpper()); text.replace(myPWRRE, contact->getMyPWR().toUpper()); text.replace(myEXCHSTRRE, contact->getSentExch().toUpper()); text.replace(myEXCHNRRE, contact->getSentNr().rightJustified(3, '0')); text.replace(myEXCHNRNRE, contact->getSentNr().rightJustified(3, '0') .replace('9', 'N') .replace('0', 'T')); } } ================================================ FILE: ui/CWConsoleWidget.h ================================================ #ifndef QLOG_UI_CWCONSOLEWIDGET_H #define QLOG_UI_CWCONSOLEWIDGET_H #include #include "ui/NewContactWidget.h" namespace Ui { class CWConsoleWidget; } class CWConsoleWidget : public QWidget { Q_OBJECT public: explicit CWConsoleWidget(QWidget *parent = nullptr); ~CWConsoleWidget(); void registerContactWidget(const NewContactWidget*); signals: void cwKeyProfileChanged(); void cwShortcutProfileChanged(); public slots: void appendCWEchoText(QString); void reloadSettings(); void clearConsoles(); void setWPM(qint32); void cwKeySpeedIncrease(); void cwKeySpeedDecrease(); void cwShortcutProfileIncrease(); void cwShortcutProfileDecrease(); void rigDisconnectHandler(); void rigConnectHandler(); void cwKeyConnected(QString); void cwKeyDisconnected(); void haltButtonPressed(); void pressMacroButton(int); void stopRepeateButtons(); private slots: void cwKeyProfileComboChanged(QString); void cwShortcutProfileComboChanged(QString); void refreshKeyProfileCombo(); void refreshShortcutProfileCombo(); void cwKeySpeedChanged(int); void cwSendButtonPressed(bool insertNewLine = true); void sendWordSwitched(int); void cwTextChanged(QString); private: Ui::CWConsoleWidget *ui; bool cwKeyOnline; const NewContactWidget *contact; bool sendWord; bool macroButtonsConnected; void sendCWText(const QString &, bool insertNewLine = true); void expandMacros(QString &); void shortcutComboMove(int); void allowMorseSending(bool); void saveSendWordConfig(bool); bool getSendWordConfig(); int getMacroButtonID(const QAbstractButton* button); }; #endif // QLOG_UI_CWCONSOLEWIDGET_H ================================================ FILE: ui/CWConsoleWidget.ui ================================================ CWConsoleWidget 0 0 412 295 Form 0 0 0 0 0 Qt::ClickFocus CW Keyer Profile QComboBox::AdjustToContents 0 0 Qt::ClickFocus Qt::DefaultContextMenu Shortcuts profile QComboBox::AdjustToContents Qt::Horizontal 40 20 0 0 Qt::ClickFocus Speed N/A false false WPM 0 99 0 0 QFrame::Plain Qt::Vertical 4 Qt::NoFocus Sent text true Qt::TextSelectableByMouse Qt::NoFocus Echoed text true 0 &CW cwSendEdit Qt::ClickFocus Text to send true Switch between sending <b>words</b> individually (separated by spaces)<br> and sending the entire text as a <b>whole</b> (separated by a new line). Qt::Horizontal 40 20 50 16777215 Qt::ClickFocus F1 300 100 false macroButtonGroup 50 16777215 Qt::ClickFocus F2 false macroButtonGroup 50 16777215 Qt::ClickFocus F3 macroButtonGroup 50 16777215 Qt::ClickFocus F4 macroButtonGroup 50 16777215 Qt::ClickFocus F5 macroButtonGroup 50 16777215 Qt::ClickFocus F6 macroButtonGroup 50 16777215 Qt::ClickFocus F7 macroButtonGroup Qt::Horizontal 40 20 Qt::Horizontal 40 20 0 0 Qt::ClickFocus Immediately stop CW sending Halt Qt::Horizontal QSizePolicy::Maximum 40 20 Qt::ClickFocus Clear Sent and Echo Console Clear Qt::Horizontal 40 20 HaltCW HaltCW CW Console - Halt Sending Alt+T Qt::ApplicationShortcut true SwitchButton QWidget
ui/component/SwitchButton.h
1
RepeatButton QPushButton
ui/component/RepeatButton.h
cwKeySpeedSpinBox valueChanged(int) CWConsoleWidget cwKeySpeedChanged(int) 131 92 131 67 cwKeyProfileCombo currentTextChanged(QString) CWConsoleWidget cwKeyProfileComboChanged(QString) 131 41 131 67 cwSendEdit returnPressed() CWConsoleWidget cwSendButtonPressed() 114 134 157 111 haltButton clicked() actionHaltCW trigger() 262 200 -1 -1 cwShortcutProfileCombo currentTextChanged(QString) CWConsoleWidget cwShortcutProfileComboChanged(QString) 248 150 164 111 clearButton clicked() CWConsoleWidget clearConsoles() 279 22 164 147 cwSendEdit textChanged(QString) CWConsoleWidget cwTextChanged(QString) 220 215 205 147 actionHaltCW triggered() CWConsoleWidget haltButtonPressed() -1 -1 205 147 cwKeySpeedChanged(int) cwKeyProfileComboChanged(QString) cwSendButtonPressed() cwKeyMacroF1() cwKeyMacroF2() cwKeyMacroF3() cwKeyMacroF4() cwKeyMacroF5() cwKeyMacroF6() cwKeyMacroF7() haltButtonPressed() cwShortcutProfileComboChanged(QString) clearConsoles() cwTextChanged(QString)
================================================ FILE: ui/CabrilloExportDialog.cpp ================================================ #include "CabrilloExportDialog.h" #include "ui_CabrilloExportDialog.h" #include #include #include #include #include #include #include #include #include #include "core/debug.h" #include "core/QSOFilterManager.h" #include "data/BandPlan.h" #include "data/StationProfile.h" #include "logformat/CabrilloFormat.h" #include "ui/CabrilloTemplateDialog.h" MODULE_IDENTIFICATION("qlog.ui.cabrilloexportdialog"); CabrilloExportDialog::CabrilloExportDialog(QWidget *parent) : QDialog(parent), ui(new Ui::CabrilloExportDialog), bandStartFreq(0.0), bandEndFreq(0.0), bandFilterActive(false) { FCT_IDENTIFICATION; // Must be created before setupUi() because .ui signal connections // may trigger scheduleQsoCountUpdate() during initialization countDebounceTimer = new QTimer(this); countDebounceTimer->setSingleShot(true); countDebounceTimer->setInterval(300); connect(countDebounceTimer, &QTimer::timeout, this, &CabrilloExportDialog::updateQsoCount); ui->setupUi(this); ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("&Export")); for ( const CabrilloFormat::CategoryItem &item : static_cast>(CabrilloFormat::bandCategories()) ) ui->catBandCombo->addItem(item.label, item.value); for ( const CabrilloFormat::CategoryItem &item : static_cast>(CabrilloFormat::modeCategories()) ) ui->catModeCombo->addItem(item.label, item.value); for ( const CabrilloFormat::CategoryItem &item : static_cast>(CabrilloFormat::powerCategories()) ) ui->catPowerCombo->addItem(item.label, item.value); for ( const CabrilloFormat::CategoryItem &item : static_cast>(CabrilloFormat::operatorCategories()) ) ui->catOperatorCombo->addItem(item.label, item.value); for ( const CabrilloFormat::CategoryItem &item : static_cast>(CabrilloFormat::assistedCategories()) ) ui->catAssistedCombo->addItem(item.label, item.value); // Optional category combos — empty first item means "not included in output" ui->catStationCombo->addItem(QString(), QString()); for ( const CabrilloFormat::CategoryItem &item : static_cast>(CabrilloFormat::stationCategories()) ) ui->catStationCombo->addItem(item.label, item.value); ui->catTransmitterCombo->addItem(QString(), QString()); for ( const CabrilloFormat::CategoryItem &item : static_cast>(CabrilloFormat::transmitterCategories()) ) ui->catTransmitterCombo->addItem(item.label, item.value); ui->catTimeCombo->addItem(QString(), QString()); for ( const CabrilloFormat::CategoryItem &item : static_cast>(CabrilloFormat::timeCategories()) ) ui->catTimeCombo->addItem(item.label, item.value); ui->catOverlayCombo->addItem(QString(), QString()); for ( const CabrilloFormat::CategoryItem &item : static_cast>(CabrilloFormat::overlayCategories()) ) ui->catOverlayCombo->addItem(item.label, item.value); connect(ui->catOperatorCombo, QOverload::of(&QComboBox::currentIndexChanged), this, [this](int) { bool isMultiOp = (ui->catOperatorCombo->currentData().toString() == CabrilloFormat::OPERATOR_MULTI); ui->transmitterIdSpin->setEnabled(isMultiOp); if ( !isMultiOp ) ui->transmitterIdSpin->setValue(0); }); ui->startDateEdit->setDateTime(QDateTime(QDate::currentDate().addDays(-2), QTime(0, 0), Qt::UTC)); ui->endDateEdit->setDateTime(QDateTime(QDate::currentDate(), QTime(23, 59), Qt::UTC)); const QStringList filterList = QSOFilterManager::instance()->getFilterList(); for ( const QString &name : filterList ) ui->userFilterCombo->addItem(name); loadTemplates(); refreshCallsigns(); // select callsign from current station profile const StationProfile currentProfile = StationProfilesManager::instance()->getCurProfile1(); if ( !currentProfile.callsign.isEmpty() ) { int idx = ui->callsignCombo->findText(currentProfile.callsign.toUpper()); if ( idx >= 0 ) ui->callsignCombo->setCurrentIndex(idx); } } CabrilloExportDialog::~CabrilloExportDialog() { FCT_IDENTIFICATION; delete ui; } void CabrilloExportDialog::browseFile() { FCT_IDENTIFICATION; QSettings settings; const QString &lastPath = ( ui->fileEdit->text().isEmpty() ) ? settings.value("export/last_path", QDir::homePath()).toString() : ui->fileEdit->text(); const QString filename = QFileDialog::getSaveFileName(this, nullptr, lastPath, tr("Cabrillo Files (*.log);;CBR Files (*.cbr);;All Files (*)")); if ( !filename.isEmpty() ) { settings.setValue("export/last_path", QFileInfo(filename).path()); ui->fileEdit->setText(filename); } } void CabrilloExportDialog::loadTemplates() { FCT_IDENTIFICATION; ui->templateCombo->clear(); const QList templates = CabrilloFormat::templateList(); for ( const CabrilloFormat::TemplateInfo &tmpl : templates ) ui->templateCombo->addItem(tmpl.name, tmpl.id); } void CabrilloExportDialog::manageTemplates() { FCT_IDENTIFICATION; CabrilloTemplateDialog dialog(this); int currentId = ui->templateCombo->currentData().toInt(); dialog.selectTemplate(currentId); if ( dialog.exec() == QDialog::Accepted ) { loadTemplates(); int idx = ui->templateCombo->findData(currentId); if ( idx >= 0 ) ui->templateCombo->setCurrentIndex(idx); } } void CabrilloExportDialog::templateChanged(int index) { FCT_IDENTIFICATION; if ( index < 0 ) return; int tmplId = ui->templateCombo->itemData(index).toInt(); const CabrilloFormat::TemplateInfo info = CabrilloFormat::templateInfo(tmplId); if ( !info.defaultCategoryMode.isEmpty() ) { int modeIdx = ui->catModeCombo->findData(info.defaultCategoryMode); if ( modeIdx >= 0 ) ui->catModeCombo->setCurrentIndex(modeIdx); } } void CabrilloExportDialog::refreshCallsigns() { FCT_IDENTIFICATION; ui->callsignCombo->blockSignals(true); const QString previousCallsign = ui->callsignCombo->currentText(); ui->callsignCombo->clear(); QStringList conditions; conditions << "1 = 1"; if ( ui->userFilterCheckBox->isChecked() && !ui->userFilterCombo->currentText().isEmpty() ) { conditions << QSOFilterManager::getWhereClause(ui->userFilterCombo->currentText()); } if ( ui->dateRangeCheckBox->isChecked() ) { conditions << "(datetime(start_time) BETWEEN datetime(:start_date) AND datetime(:end_date))"; } QString sql = QString("SELECT UPPER(station_callsign) as cs, COUNT(*) as cnt " "FROM contacts WHERE %1 AND station_callsign IS NOT NULL " "AND station_callsign != '' " "GROUP BY cs ORDER BY cnt DESC") .arg(conditions.join(" AND ")); QSqlQuery query; if ( query.prepare(sql) ) { if ( ui->dateRangeCheckBox->isChecked() ) { query.bindValue(":start_date", ui->startDateEdit->dateTime().toUTC()); query.bindValue(":end_date", ui->endDateEdit->dateTime().toUTC()); } if ( query.exec() ) { while ( query.next() ) ui->callsignCombo->addItem(query.value(0).toString()); } else { qCWarning(runtime) << query.lastError().text(); } } if ( !previousCallsign.isEmpty() ) { int idx = ui->callsignCombo->findText(previousCallsign); if ( idx >= 0 ) ui->callsignCombo->setCurrentIndex(idx); } ui->callsignCombo->blockSignals(false); if ( ui->callsignCombo->count() > 0 ) callsignChanged(ui->callsignCombo->currentText()); scheduleQsoCountUpdate(); } void CabrilloExportDialog::callsignChanged(const QString &callsign) { FCT_IDENTIFICATION; qCDebug(function_parameters) << callsign; ui->operatorsEdit->setText(callsign); loadStationProfile(callsign); scheduleQsoCountUpdate(); } void CabrilloExportDialog::scheduleQsoCountUpdate() { FCT_IDENTIFICATION; countDebounceTimer->start(); } void CabrilloExportDialog::updateQsoCount() { FCT_IDENTIFICATION; if ( ui->callsignCombo->currentText().isEmpty() ) { ui->qsoCountLabel->setText(tr("QSO(s): %1").arg(0)); return; } resolveBandFilter(); const QString sql = QString("SELECT COUNT(*) FROM contacts WHERE %1").arg(buildWhereClause()); qCDebug(runtime) << sql; QSqlQuery query; if ( !query.prepare(sql) ) { ui->qsoCountLabel->setText(tr("QSOs: ?")); return; } bindWhereClause(query); if ( query.exec() && query.next() ) ui->qsoCountLabel->setText(tr("QSO(s): %1").arg(query.value(0).toInt())); else ui->qsoCountLabel->setText(tr("QSOs: ?")); } void CabrilloExportDialog::resolveBandFilter() { FCT_IDENTIFICATION; bandFilterActive = false; const QString selectedBand = ui->catBandCombo->currentData().toString(); if ( selectedBand == CabrilloFormat::BAND_ALL || selectedBand == CabrilloFormat::BAND_VHF_3_BAND || selectedBand == CabrilloFormat::BAND_VHF_FM_ONLY || selectedBand == CabrilloFormat::BAND_LIGHT ) { return; } // Cabrillo VHF/UHF designators use different naming than QLog bands table static const QMap cabrilloToBandName = { {CabrilloFormat::BAND_222, "1.25m"}, {CabrilloFormat::BAND_432, "70cm"}, {CabrilloFormat::BAND_902, "33cm"}, {CabrilloFormat::BAND_1_2G, "23cm"} }; const QString bandName = cabrilloToBandName.value(selectedBand, selectedBand); const Band band = BandPlan::bandName2Band(bandName); if ( !band.name.isEmpty() ) { bandStartFreq = band.start; bandEndFreq = band.end; bandFilterActive = true; } } void CabrilloExportDialog::loadStationProfile(const QString &callsign) { FCT_IDENTIFICATION; qCDebug(function_parameters) << callsign; ui->nameValue->clear(); ui->addressEdit->clear(); ui->gridLocatorEdit->clear(); if ( callsign.isEmpty() ) return; const StationProfile profile = StationProfilesManager::instance()->findByCallsign(callsign); if ( !profile.callsign.isEmpty() ) { ui->nameValue->setText(profile.operatorName); ui->addressEdit->setPlainText(profile.qthName); ui->gridLocatorEdit->setText(profile.locator); } } CabrilloFormat::HeaderData CabrilloExportDialog::buildHeaderData() const { FCT_IDENTIFICATION; CabrilloFormat::HeaderData data; data.callsign = ui->callsignCombo->currentText(); data.operatorName = ui->nameValue->text(); data.email = ui->emailEdit->text(); data.operators = ui->operatorsEdit->text(); data.address = ui->addressEdit->toPlainText().trimmed(); data.gridLocator = ui->gridLocatorEdit->text(); data.categoryOperator = ui->catOperatorCombo->currentData().toString(); data.categoryBand = ui->catBandCombo->currentData().toString(); data.categoryPower = ui->catPowerCombo->currentData().toString(); data.categoryMode = ui->catModeCombo->currentData().toString(); data.categoryAssisted = ui->catAssistedCombo->currentData().toString(); data.categoryStation = ui->catStationCombo->currentData().toString(); data.categoryTransmitter = ui->catTransmitterCombo->currentData().toString(); data.categoryTime = ui->catTimeCombo->currentData().toString(); data.categoryOverlay = ui->catOverlayCombo->currentData().toString(); data.location = ui->locationEdit->text(); data.club = ui->clubEdit->text(); data.offtime = ui->offtimeEdit->toPlainText().trimmed(); data.soapbox = ui->soapboxEdit->toPlainText().trimmed(); return data; } QString CabrilloExportDialog::buildWhereClause() const { FCT_IDENTIFICATION; QStringList conditions; conditions << "UPPER(station_callsign) = UPPER(:callsign)"; if ( ui->dateRangeCheckBox->isChecked() ) conditions << "(datetime(start_time) BETWEEN datetime(:export_start_date) AND datetime(:export_end_date))"; if ( ui->userFilterCheckBox->isChecked() && !ui->userFilterCombo->currentText().isEmpty() ) { conditions << QSOFilterManager::getWhereClause(ui->userFilterCombo->currentText()); } if ( bandFilterActive ) conditions << "freq BETWEEN :band_start_freq AND :band_end_freq"; const QString selectedMode = ui->catModeCombo->currentData().toString(); if ( selectedMode != CabrilloFormat::MODE_MIXED ) { if ( selectedMode == CabrilloFormat::MODE_DIGI ) conditions << "mode IN (SELECT name FROM modes WHERE dxcc = 'DIGITAL')"; else conditions << "UPPER(mode) = UPPER(:export_mode)"; } return conditions.join(" AND "); } void CabrilloExportDialog::bindWhereClause(QSqlQuery &query) const { FCT_IDENTIFICATION; query.bindValue(":callsign", ui->callsignCombo->currentText()); if ( ui->dateRangeCheckBox->isChecked() ) { query.bindValue(":export_start_date", ui->startDateEdit->dateTime().toUTC()); query.bindValue(":export_end_date", ui->endDateEdit->dateTime().toUTC()); } if ( bandFilterActive ) { query.bindValue(":band_start_freq", bandStartFreq); query.bindValue(":band_end_freq", bandEndFreq); } const QString selectedMode = ui->catModeCombo->currentData().toString(); if ( selectedMode != CabrilloFormat::MODE_MIXED && selectedMode != CabrilloFormat::MODE_DIGI ) query.bindValue(":export_mode", selectedMode); } void CabrilloExportDialog::accept() { FCT_IDENTIFICATION; if ( ui->templateCombo->currentIndex() < 0 ) { QMessageBox::warning(this, tr("QLog Warning"), tr("Please select a contest template.")); return; } if ( ui->fileEdit->text().isEmpty() ) { QMessageBox::warning(this, tr("QLog Warning"), tr("Please select an output file.")); return; } if ( ui->callsignCombo->currentText().isEmpty() ) { QMessageBox::warning(this, tr("QLog Warning"), tr("No callsign available. Check your filters.")); return; } resolveBandFilter(); const QString whereClause = buildWhereClause(); QString sql = QString("SELECT * FROM contacts WHERE %1 ORDER BY start_time ASC").arg(whereClause); qCDebug(runtime) << sql; QSqlQuery query; if ( !query.prepare(sql) ) { qCWarning(runtime) << "Failed to prepare export query:" << query.lastError().text(); QMessageBox::critical(this, tr("QLog Error"), tr("Failed to prepare export query.")); return; } bindWhereClause(query); if ( !query.exec() ) { qCWarning(runtime) << "Failed to execute export query:" << query.lastError().text(); QMessageBox::critical(this, tr("QLog Error"), tr("Failed to query QSOs for export.")); return; } QList records; while ( query.next() ) records.append(query.record()); QFile file(ui->fileEdit->text()); if ( !file.open(QIODevice::WriteOnly | QIODevice::Text) ) { QMessageBox::critical(this, tr("QLog Error"), tr("Cannot open file %1 for writing.").arg(ui->fileEdit->text())); return; } QTextStream stream(&file); CabrilloFormat format(stream); format.setTemplateId(ui->templateCombo->currentData().toInt()); format.setHeaderData(buildHeaderData()); format.setTransmitterId(ui->transmitterIdSpin->value()); QProgressDialog progress(tr("Exporting Cabrillo..."), QString(), 0, 100, this); progress.setWindowModality(Qt::WindowModal); progress.setMinimumDuration(0); connect(&format, &CabrilloFormat::exportProgress, this, [&progress](float pct) { progress.setValue(static_cast(pct)); }); long count = format.runExport(records); file.close(); progress.close(); QMessageBox::information(this, tr("QLog Information"), tr("Exported %n QSO(s) to Cabrillo file.", "", count)); QDialog::accept(); } ================================================ FILE: ui/CabrilloExportDialog.h ================================================ #ifndef QLOG_UI_CABRILLOEXPORTDIALOG_H #define QLOG_UI_CABRILLOEXPORTDIALOG_H #include #include "logformat/CabrilloFormat.h" class QSqlQuery; class QTimer; namespace Ui { class CabrilloExportDialog; } class CabrilloExportDialog : public QDialog { Q_OBJECT public: explicit CabrilloExportDialog(QWidget *parent = nullptr); ~CabrilloExportDialog(); private slots: void accept() override; void templateChanged(int index); void manageTemplates(); void browseFile(); void callsignChanged(const QString &callsign); void refreshCallsigns(); void updateQsoCount(); void scheduleQsoCountUpdate(); private: void loadTemplates(); void loadStationProfile(const QString &callsign); void resolveBandFilter(); CabrilloFormat::HeaderData buildHeaderData() const; QString buildWhereClause() const; void bindWhereClause(QSqlQuery &query) const; Ui::CabrilloExportDialog *ui; QTimer *countDebounceTimer; double bandStartFreq; double bandEndFreq; bool bandFilterActive; }; #endif // QLOG_UI_CABRILLOEXPORTDIALOG_H ================================================ FILE: ui/CabrilloExportDialog.ui ================================================ CabrilloExportDialog 0 0 700 691 Cabrillo Export true File: true Browse .. Contest Format Template: 0 0 0 0 0 Manage .. User Filter: false 0 0 Date/Time: false yyyy-MM-dd HH:mm true Qt::UTC - false yyyy-MM-dd HH:mm true Qt::UTC UTC Qt::Horizontal 0 0 Station & Categories Callsign: 0 0 Operators: Band: Mode: Power: Operator: Assisted: Station: Transmitter: Time: Overlay: Transmitter ID: false 0 1 0 Additional Name: Email: Address: 16777215 80 Max 6 lines Gridsquare Location: Club: Soapbox: 16777215 60 Off-Time: 16777215 60 yyyy-mm-dd hhmm yyyy-mm-dd hhmm yyyy-mm-dd hhmm yyyy-mm-dd hhmm true QSOs: --- Qt::Horizontal 0 0 QDialogButtonBox::Cancel|QDialogButtonBox::Ok fileEdit browseButton templateCombo manageTemplatesButton userFilterCheckBox userFilterCombo dateRangeCheckBox startDateEdit endDateEdit callsignCombo operatorsEdit catBandCombo catModeCombo catPowerCombo catOperatorCombo catAssistedCombo catStationCombo catTransmitterCombo catTimeCombo catOverlayCombo transmitterIdSpin nameValue emailEdit addressEdit gridLocatorEdit locationEdit clubEdit soapboxEdit offtimeEdit buttonBox rejected() CabrilloExportDialog reject() 290 500 290 260 buttonBox accepted() CabrilloExportDialog accept() 290 500 290 260 browseButton clicked() CabrilloExportDialog browseFile() 530 30 290 260 manageTemplatesButton clicked() CabrilloExportDialog manageTemplates() 450 90 290 260 templateCombo currentIndexChanged(int) CabrilloExportDialog templateChanged(int) 350 65 290 260 callsignCombo currentTextChanged(QString) CabrilloExportDialog callsignChanged(QString) 200 160 290 260 userFilterCheckBox toggled(bool) userFilterCombo setEnabled(bool) 80 120 350 120 userFilterCheckBox toggled(bool) CabrilloExportDialog refreshCallsigns() 80 120 290 260 userFilterCombo currentTextChanged(QString) CabrilloExportDialog refreshCallsigns() 350 120 290 260 dateRangeCheckBox toggled(bool) startDateEdit setEnabled(bool) 80 280 200 280 dateRangeCheckBox toggled(bool) endDateEdit setEnabled(bool) 80 280 400 280 dateRangeCheckBox toggled(bool) CabrilloExportDialog refreshCallsigns() 80 280 290 260 startDateEdit dateTimeChanged(QDateTime) CabrilloExportDialog refreshCallsigns() 200 280 290 260 endDateEdit dateTimeChanged(QDateTime) CabrilloExportDialog refreshCallsigns() 400 280 290 260 catBandCombo currentIndexChanged(int) CabrilloExportDialog scheduleQsoCountUpdate() 200 200 290 260 catModeCombo currentIndexChanged(int) CabrilloExportDialog scheduleQsoCountUpdate() 450 200 290 260 templateChanged(int) manageTemplates() browseFile() callsignChanged(QString) refreshCallsigns() scheduleQsoCountUpdate() ================================================ FILE: ui/CabrilloTemplateDialog.cpp ================================================ #include "CabrilloTemplateDialog.h" #include "ui_CabrilloTemplateDialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core/debug.h" #include "data/Data.h" #include "models/LogbookModel.h" MODULE_IDENTIFICATION("qlog.ui.cabrillotemplatedialog"); CabrilloTemplateDialog::CabrilloTemplateDialog(QWidget *parent) : QDialog(parent), ui(new Ui::CabrilloTemplateDialog), currentTemplateIndex(-1) { FCT_IDENTIFICATION; ui->setupUi(this); // Left panel (template list) stays compact, right panel (editor) expands ui->mainHLayout->setStretch(0, 0); ui->mainHLayout->setStretch(1, 1); // Columns table: auto-size columns to content QHeaderView *hdr = ui->columnsTable->horizontalHeader(); hdr->setSectionResizeMode(QHeaderView::ResizeToContents); // Populate available DB fields with translated names QSqlRecord contactsRecord = QSqlDatabase::database().record("contacts"); for ( int i = LogbookModel::ColumnID::COLUMN_ID; i < LogbookModel::ColumnID::COLUMN_LAST_ELEMENT; ++i ) { LogbookModel::ColumnID columnID = static_cast(i); const QString translation = LogbookModel::getFieldNameTranslation(columnID); if ( translation.isEmpty() ) continue; const QString dbField = contactsRecord.fieldName(i); if ( dbField.isEmpty() ) continue; dbFieldItems.append({dbField, translation}); } std::sort(dbFieldItems.begin(), dbFieldItems.end(), [](const CabrilloFormat::CategoryItem &a, const CabrilloFormat::CategoryItem &b) { return a.label.localeAwareCompare(b.label) < 0; }); formatterItems = CabrilloFormat::formatterTypes(); for ( const CabrilloFormat::CategoryItem &item : static_cast>(CabrilloFormat::modeCategories()) ) ui->defaultModeCombo->addItem(item.label, item.value); populateContestCombo(); connect(ui->nameEdit, &QLineEdit::textChanged, this, [this](const QString &text) { QListWidgetItem *item = ui->templateList->currentItem(); if ( item && currentTemplateIndex >= 0 && currentTemplateIndex < templates.size() ) item->setText(text); }); loadTemplatesFromDB(); populateTemplateList(); if ( !templates.isEmpty() ) ui->templateList->setCurrentRow(0); // Calculate dialog width so the columns table fits without horizontal scrollbar int tableWidth = hdr->length() + ui->columnsTable->frameWidth() * 2; int leftPanelWidth = ui->leftLayout->sizeHint().width(); QMargins m = ui->mainHLayout->contentsMargins(); int needed = m.left() + m.right() + ui->mainHLayout->spacing() + leftPanelWidth + tableWidth; resize(qMax(needed, width()), height()); // After sizing, let last column stretch when user resizes the dialog hdr->setSectionResizeMode(hdr->count() - 1, QHeaderView::Stretch); } CabrilloTemplateDialog::~CabrilloTemplateDialog() { FCT_IDENTIFICATION; delete ui; } void CabrilloTemplateDialog::installWheelGuard(QWidget *widget) { widget->setFocusPolicy(Qt::StrongFocus); widget->installEventFilter(this); } // Prevent mouse wheel from changing combo/spin values while scrolling the table; // wheel events are only handled when the widget has focus (after clicking on it) bool CabrilloTemplateDialog::eventFilter(QObject *obj, QEvent *event) { if ( event->type() == QEvent::Wheel ) { QWidget *widget = qobject_cast(obj); if ( widget && !widget->hasFocus() ) { event->ignore(); return true; } } return QDialog::eventFilter(obj, event); } void CabrilloTemplateDialog::selectTemplate(int templateId) { FCT_IDENTIFICATION; qCDebug(function_parameters) << templateId; for ( int i = 0; i < ui->templateList->count(); i++ ) { int idx = ui->templateList->item(i)->data(Qt::UserRole).toInt(); if ( idx >= 0 && idx < templates.size() && templates[idx].id == templateId ) { ui->templateList->setCurrentRow(i); return; } } } void CabrilloTemplateDialog::populateContestCombo() { FCT_IDENTIFICATION; const QStringList contests = Data::instance()->contestList(); for ( const QString &name : contests ) ui->contestCombo->addItem(name); } void CabrilloTemplateDialog::loadTemplatesFromDB() { FCT_IDENTIFICATION; templates.clear(); const QList infoList = CabrilloFormat::templateList(); for ( const CabrilloFormat::TemplateInfo &info : infoList ) { TemplateData tmpl; tmpl.id = info.id; tmpl.name = info.name; tmpl.contestName = info.contestName; tmpl.defaultMode = info.defaultCategoryMode; tmpl.columns = CabrilloFormat::loadTemplateColumns(info.id); tmpl.isNew = false; tmpl.isModified = false; tmpl.isDeleted = false; templates.append(tmpl); } } void CabrilloTemplateDialog::populateTemplateList() { FCT_IDENTIFICATION; ui->templateList->clear(); for ( int i = 0; i < templates.size(); i++ ) { const TemplateData &tmpl = templates[i]; if ( tmpl.isDeleted ) continue; QListWidgetItem *item = new QListWidgetItem(tmpl.name, ui->templateList); item->setData(Qt::UserRole, i); } } void CabrilloTemplateDialog::templateSelectionChanged() { FCT_IDENTIFICATION; // Save current template before switching if ( currentTemplateIndex >= 0 && currentTemplateIndex < templates.size() ) saveCurrentTemplate(); QListWidgetItem *item = ui->templateList->currentItem(); if ( !item ) { currentTemplateIndex = -1; return; } currentTemplateIndex = item->data(Qt::UserRole).toInt(); showTemplateEditor(currentTemplateIndex); } void CabrilloTemplateDialog::showTemplateEditor(int index) { FCT_IDENTIFICATION; if ( index < 0 || index >= templates.size() ) return; const TemplateData &tmpl = templates[index]; ui->nameEdit->setText(tmpl.name); int cidIdx = ui->contestCombo->findText(tmpl.contestName); if ( cidIdx >= 0 ) ui->contestCombo->setCurrentIndex(cidIdx); else ui->contestCombo->setEditText(tmpl.contestName); int modeIdx = ui->defaultModeCombo->findData(tmpl.defaultMode); if ( modeIdx >= 0 ) ui->defaultModeCombo->setCurrentIndex(modeIdx); populateColumnsTable(tmpl.columns); } void CabrilloTemplateDialog::populateColumnsTable(const QList &columns) { FCT_IDENTIFICATION; ui->columnsTable->setRowCount(0); ui->columnsTable->setRowCount(columns.size()); for ( int row = 0; row < columns.size(); row++ ) setupColumnRow(row, columns[row]); ui->columnsTable->resizeColumnsToContents(); } void CabrilloTemplateDialog::setupColumnRow(int row, const CabrilloFormat::ColumnDef &col) { FCT_IDENTIFICATION; // Position (read-only) QTableWidgetItem *posItem = new QTableWidgetItem(QString::number(col.position)); posItem->setFlags(posItem->flags() & ~Qt::ItemIsEditable); ui->columnsTable->setItem(row, 0, posItem); // DB Field (ComboBox with translated names) QComboBox *fieldCombo = new QComboBox(this); installWheelGuard(fieldCombo); fieldCombo->addItem(QString(), QLatin1String("")); // empty entry for non-DB fields for ( const CabrilloFormat::CategoryItem &item : static_cast&>(dbFieldItems) ) fieldCombo->addItem(item.label, item.value); int fieldIdx = fieldCombo->findData(col.dbField); if ( fieldIdx >= 0 ) fieldCombo->setCurrentIndex(fieldIdx); ui->columnsTable->setCellWidget(row, 1, fieldCombo); // Formatter (ComboBox) QComboBox *fmtCombo = new QComboBox(this); installWheelGuard(fmtCombo); for ( const CabrilloFormat::CategoryItem &fmt : static_cast&>(formatterItems) ) fmtCombo->addItem(fmt.label, fmt.value); int fmtIdx = fmtCombo->findData(col.formatter); if ( fmtIdx >= 0 ) fmtCombo->setCurrentIndex(fmtIdx); ui->columnsTable->setCellWidget(row, 2, fmtCombo); // Width (SpinBox) QSpinBox *widthSpin = new QSpinBox(this); installWheelGuard(widthSpin); widthSpin->setRange(1, 30); widthSpin->setValue(col.width); ui->columnsTable->setCellWidget(row, 3, widthSpin); // Label (QLineEdit) QLineEdit *labelEdit = new QLineEdit(col.label, this); ui->columnsTable->setCellWidget(row, 4, labelEdit); // Auto-adjust fields when Transmitter ID formatter is selected auto applyTransmitterIdDefaults = [fieldCombo, widthSpin, labelEdit](const QString &fmtValue) { bool isTxId = (fmtValue == CabrilloFormat::FMT_TRANSMITTER_ID); fieldCombo->setEnabled(!isTxId); if ( isTxId ) { fieldCombo->setCurrentIndex(0); // empty widthSpin->setValue(1); labelEdit->setText("t"); } }; connect(fmtCombo, QOverload::of(&QComboBox::currentIndexChanged), this, [fmtCombo, applyTransmitterIdDefaults](int) { applyTransmitterIdDefaults(fmtCombo->currentData().toString()); }); // Apply defaults for initially loaded transmitter_id columns applyTransmitterIdDefaults(col.formatter); } QList CabrilloTemplateDialog::readColumnsFromTable() const { FCT_IDENTIFICATION; QList columns; for ( int row = 0; row < ui->columnsTable->rowCount(); row++ ) { CabrilloFormat::ColumnDef col; col.position = row + 1; QComboBox *fieldCombo = qobject_cast(ui->columnsTable->cellWidget(row, 1)); col.dbField = fieldCombo ? fieldCombo->currentData().toString() : QLatin1String(""); QComboBox *fmtCombo = qobject_cast(ui->columnsTable->cellWidget(row, 2)); col.formatter = fmtCombo ? fmtCombo->currentData().toString() : ""; QSpinBox *widthSpin = qobject_cast(ui->columnsTable->cellWidget(row, 3)); col.width = widthSpin ? widthSpin->value() : 5; QLineEdit *labelEdit = qobject_cast(ui->columnsTable->cellWidget(row, 4)); col.label = labelEdit ? labelEdit->text() : ""; columns.append(col); } return columns; } void CabrilloTemplateDialog::saveCurrentTemplate() { FCT_IDENTIFICATION; if ( currentTemplateIndex < 0 || currentTemplateIndex >= templates.size() ) return; TemplateData &tmpl = templates[currentTemplateIndex]; tmpl.name = ui->nameEdit->text(); tmpl.contestName = ui->contestCombo->currentText(); tmpl.defaultMode = ui->defaultModeCombo->currentData().toString(); tmpl.columns = readColumnsFromTable(); tmpl.isModified = true; } void CabrilloTemplateDialog::newTemplate() { FCT_IDENTIFICATION; TemplateData tmpl; tmpl.id = -1; tmpl.name = tr("New Template"); tmpl.defaultMode = CabrilloFormat::MODE_CW; tmpl.isNew = true; tmpl.isModified = true; tmpl.isDeleted = false; // Default columns (common prefix) tmpl.columns = { {1, "freq", 5, CabrilloFormat::FMT_FREQ_KHZ, "Freq"}, {2, "mode", 2, CabrilloFormat::FMT_MODE_CABRILLO, "Mode"}, {3, "start_time", 10, CabrilloFormat::FMT_DATE_YYYY_MM_DD, "Date"}, {4, "start_time", 4, CabrilloFormat::FMT_TIME_HHMM, "Time"}, }; templates.append(tmpl); populateTemplateList(); ui->templateList->setCurrentRow(ui->templateList->count() - 1); } void CabrilloTemplateDialog::copyTemplate() { FCT_IDENTIFICATION; if ( currentTemplateIndex < 0 || currentTemplateIndex >= templates.size() ) return; // Save current state first saveCurrentTemplate(); const TemplateData &src = templates[currentTemplateIndex]; TemplateData tmpl; tmpl.id = -1; tmpl.name = tr("Copy - %1").arg(src.name); tmpl.contestName = src.contestName; tmpl.defaultMode = src.defaultMode; tmpl.columns = src.columns; tmpl.isNew = true; tmpl.isModified = true; tmpl.isDeleted = false; templates.append(tmpl); populateTemplateList(); ui->templateList->setCurrentRow(ui->templateList->count() - 1); } void CabrilloTemplateDialog::deleteTemplate() { FCT_IDENTIFICATION; if ( currentTemplateIndex < 0 || currentTemplateIndex >= templates.size() ) return; TemplateData &tmpl = templates[currentTemplateIndex]; int ret = QMessageBox::question(this, tr("Delete Template"), tr("Delete template '%1'?").arg(tmpl.name), QMessageBox::Yes | QMessageBox::No); if ( ret != QMessageBox::Yes ) return; tmpl.isDeleted = true; currentTemplateIndex = -1; populateTemplateList(); if ( ui->templateList->count() > 0 ) ui->templateList->setCurrentRow(0); } void CabrilloTemplateDialog::addColumn() { FCT_IDENTIFICATION; int row = ui->columnsTable->rowCount(); ui->columnsTable->setRowCount(row + 1); CabrilloFormat::ColumnDef col; col.position = row + 1; col.dbField = "callsign"; col.width = 13; setupColumnRow(row, col); ui->columnsTable->scrollToItem(ui->columnsTable->item(row, 0)); ui->columnsTable->setCurrentCell(row, 0); } void CabrilloTemplateDialog::removeColumn() { FCT_IDENTIFICATION; int row = ui->columnsTable->currentRow(); if ( row < 0 ) return; ui->columnsTable->removeRow(row); // Renumber positions for ( int i = 0; i < ui->columnsTable->rowCount(); i++ ) { QTableWidgetItem *posItem = ui->columnsTable->item(i, 0); if ( posItem ) posItem->setText(QString::number(i + 1)); } } void CabrilloTemplateDialog::moveColumnUp() { FCT_IDENTIFICATION; moveColumn(-1); } void CabrilloTemplateDialog::moveColumnDown() { FCT_IDENTIFICATION; moveColumn(1); } void CabrilloTemplateDialog::loadTemplate() { FCT_IDENTIFICATION; const QString filePath = QFileDialog::getOpenFileName( this, tr("Import Template"), QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), tr("QLog Cabrillo Template (*.qct)")); if ( filePath.isEmpty() ) return; QString error; TemplateData tmpl = readTemplateFromFile(filePath, &error); if ( tmpl.name.isEmpty() ) { QMessageBox::critical(this, tr("Import Failed"), error); return; } // Unique name const QString baseName = tmpl.name; int suffix = 1; bool collision = true; while ( collision ) { collision = false; for ( const TemplateData &existing : static_cast&>(templates) ) { if ( !existing.isDeleted && existing.name == tmpl.name ) { tmpl.name = QString("%1 (%2)").arg(baseName).arg(suffix++); collision = true; break; } } } templates.append(tmpl); populateTemplateList(); ui->templateList->setCurrentRow(ui->templateList->count() - 1); } void CabrilloTemplateDialog::exportTemplate() { FCT_IDENTIFICATION; if ( currentTemplateIndex < 0 || currentTemplateIndex >= templates.size() ) return; saveCurrentTemplate(); const TemplateData &tmpl = templates[currentTemplateIndex]; const QString defaultName = tmpl.name.simplified().replace(' ', '_') + ".qct"; const QString filePath = QFileDialog::getSaveFileName( this, tr("Export Template"), QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + "/" + defaultName, tr("QLog Cabrillo Template (*.qct)")); if ( filePath.isEmpty() ) return; QString error; if ( !writeTemplateToFile(tmpl, filePath, &error) ) QMessageBox::critical(this, tr("Export Failed"), error); } void CabrilloTemplateDialog::moveColumn(int direction) { FCT_IDENTIFICATION; int row = ui->columnsTable->currentRow(); int targetRow = row + direction; if ( row < 0 || targetRow < 0 || targetRow >= ui->columnsTable->rowCount() ) return; QList columns = readColumnsFromTable(); #if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)) columns.swapItemsAt(row, targetRow); #else columns.swap(row, targetRow); #endif for ( int i = 0; i < columns.size(); i++ ) columns[i].position = i + 1; populateColumnsTable(columns); ui->columnsTable->setCurrentCell(targetRow, 0); } bool CabrilloTemplateDialog::saveTemplateColumns(QSqlQuery &query, int templateId, const QList &columns) { FCT_IDENTIFICATION; // Delete existing columns if ( !query.prepare("DELETE FROM cabrillo_template_columns WHERE template_id = :tid") ) { qCWarning(runtime) << query.lastError().text(); return false; } query.bindValue(":tid", templateId); if ( !query.exec() ) { qCWarning(runtime) << query.lastError().text(); return false; } // Insert columns — prepare once, bind+exec in loop if ( !query.prepare("INSERT INTO cabrillo_template_columns " "(template_id, position, db_field, width, formatter, label) " "VALUES (:tid, :pos, :field, :width, :fmt, :label)") ) { qCWarning(runtime) << query.lastError().text(); return false; } for ( const CabrilloFormat::ColumnDef &col : columns ) { query.bindValue(":tid", templateId); query.bindValue(":pos", col.position); query.bindValue(":field", col.dbField); query.bindValue(":width", col.width); query.bindValue(":fmt", col.formatter); query.bindValue(":label", col.label); if ( !query.exec() ) { qCWarning(runtime) << query.lastError().text(); return false; } } return true; } bool CabrilloTemplateDialog::writeTemplateToFile(const TemplateData &tmpl, const QString &filePath, QString *error) const { FCT_IDENTIFICATION; QSettings s(filePath, QSettings::IniFormat); #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) s.setIniCodec("UTF-8"); #endif s.beginGroup("Template"); s.setValue("name", tmpl.name); s.setValue("contest", tmpl.contestName); s.setValue("mode", tmpl.defaultMode); s.setValue("version", 1); s.endGroup(); s.beginGroup("Columns"); s.setValue("count", tmpl.columns.size()); for ( int i = 0; i < tmpl.columns.size(); i++ ) { const CabrilloFormat::ColumnDef &col = tmpl.columns[i]; const QString prefix = QString("col%1").arg(i + 1); s.setValue(prefix + ".db_field", col.dbField); s.setValue(prefix + ".formatter", col.formatter); s.setValue(prefix + ".width", col.width); s.setValue(prefix + ".label", col.label); } s.endGroup(); s.sync(); if ( s.status() != QSettings::NoError ) { if ( error ) *error = tr("Failed to write file: %1").arg(filePath); return false; } return true; } CabrilloTemplateDialog::TemplateData CabrilloTemplateDialog::readTemplateFromFile(const QString &filePath, QString *error) { FCT_IDENTIFICATION; TemplateData tmpl{}; if ( !QFile::exists(filePath) ) { if ( error ) *error = tr("File not found: %1").arg(filePath); return tmpl; } QSettings s(filePath, QSettings::IniFormat); #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) s.setIniCodec("UTF-8"); #endif if ( s.status() != QSettings::NoError ) { if ( error ) *error = tr("Cannot open file: %1").arg(filePath); return tmpl; } s.beginGroup("Template"); tmpl.name = s.value("name").toString().trimmed(); tmpl.contestName = s.value("contest").toString().trimmed(); tmpl.defaultMode = s.value("mode", CabrilloFormat::MODE_CW).toString().trimmed(); s.endGroup(); if ( tmpl.name.isEmpty() ) { if ( error ) *error = tr("Invalid template file: missing name"); return TemplateData{}; } s.beginGroup("Columns"); const int count = s.value("count", 0).toInt(); for ( int i = 1; i <= count; i++ ) { const QString prefix = QString("col%1").arg(i); CabrilloFormat::ColumnDef col; col.position = i; col.dbField = s.value(prefix + ".db_field").toString(); col.formatter = s.value(prefix + ".formatter").toString(); col.width = s.value(prefix + ".width", 5).toInt(); col.label = s.value(prefix + ".label").toString(); tmpl.columns.append(col); } s.endGroup(); tmpl.id = -1; tmpl.isNew = true; tmpl.isModified = true; tmpl.isDeleted = false; return tmpl; } void CabrilloTemplateDialog::accept() { FCT_IDENTIFICATION; saveCurrentTemplate(); QSqlDatabase db = QSqlDatabase::database(); if ( !db.transaction() ) { qCWarning(runtime) << "Failed to start transaction"; QMessageBox::critical(this, tr("QLog Error"), tr("Cannot start database transaction.")); return; } QSqlQuery query; // Process deletions (ON DELETE CASCADE handles columns automatically) for ( const TemplateData &tmpl : static_cast&>(templates) ) { if ( !tmpl.isDeleted || tmpl.isNew ) continue; if ( !query.prepare("DELETE FROM cabrillo_templates WHERE id = :id") ) { qCWarning(runtime) << query.lastError().text(); Q_UNUSED(db.rollback()); return; } query.bindValue(":id", tmpl.id); if ( !query.exec() ) { qCWarning(runtime) << query.lastError().text(); Q_UNUSED(db.rollback()); return; } } // Process new and modified templates for ( const TemplateData &tmpl : static_cast&>(templates) ) { if ( tmpl.isDeleted ) continue; int targetId = tmpl.id; if ( tmpl.isNew ) { if ( !query.prepare("INSERT INTO cabrillo_templates " "(name, contest_name_for_header, default_category_mode) " "VALUES (:name, :contest, :mode)") ) { qCWarning(runtime) << query.lastError().text(); Q_UNUSED(db.rollback()); return; } query.bindValue(":name", tmpl.name); query.bindValue(":contest", tmpl.contestName); query.bindValue(":mode", tmpl.defaultMode); if ( !query.exec() ) { qCWarning(runtime) << query.lastError().text(); QMessageBox::warning(this, tr("QLog Warning"), tr("Cannot save template '%1': %2").arg(tmpl.name, query.lastError().text())); Q_UNUSED(db.rollback()); return; } targetId = query.lastInsertId().toInt(); } else if ( tmpl.isModified ) { if ( !query.prepare("UPDATE cabrillo_templates SET name = :name, " "contest_name_for_header = :contest, default_category_mode = :mode " "WHERE id = :id") ) { qCWarning(runtime) << query.lastError().text(); Q_UNUSED(db.rollback()); return; } query.bindValue(":name", tmpl.name); query.bindValue(":contest", tmpl.contestName); query.bindValue(":mode", tmpl.defaultMode); query.bindValue(":id", tmpl.id); if ( !query.exec() ) { qCWarning(runtime) << query.lastError().text(); Q_UNUSED(db.rollback()); return; } } else { continue; } if ( !saveTemplateColumns(query, targetId, tmpl.columns) ) { Q_UNUSED(db.rollback()); return; } } if ( !db.commit() ) { qCWarning(runtime) << "Failed to commit transaction"; Q_UNUSED(db.rollback()); return; } QDialog::accept(); } ================================================ FILE: ui/CabrilloTemplateDialog.h ================================================ #ifndef QLOG_UI_CABRILLOTEMPLATEDIALOG_H #define QLOG_UI_CABRILLOTEMPLATEDIALOG_H #include #include #include #include #include "logformat/CabrilloFormat.h" class QSqlQuery; namespace Ui { class CabrilloTemplateDialog; } class CabrilloTemplateDialog : public QDialog { Q_OBJECT public: explicit CabrilloTemplateDialog(QWidget *parent = nullptr); ~CabrilloTemplateDialog(); void selectTemplate(int templateId); protected: bool eventFilter(QObject *obj, QEvent *event) override; private slots: void accept() override; void templateSelectionChanged(); void newTemplate(); void copyTemplate(); void deleteTemplate(); void addColumn(); void removeColumn(); void moveColumnUp(); void moveColumnDown(); void loadTemplate(); void exportTemplate(); private: struct TemplateData { int id = -1; QString name; QString contestName; QString defaultMode; QList columns; bool isNew = true; bool isModified = true; bool isDeleted = false; }; void loadTemplatesFromDB(); void populateTemplateList(); void showTemplateEditor(int index); void saveCurrentTemplate(); void populateContestCombo(); void populateColumnsTable(const QList &columns); void setupColumnRow(int row, const CabrilloFormat::ColumnDef &col); void installWheelGuard(QWidget *widget); QList readColumnsFromTable() const; void moveColumn(int direction); bool saveTemplateColumns(QSqlQuery &query, int templateId, const QList &columns); bool writeTemplateToFile(const TemplateData &tmpl, const QString &filePath, QString *error = nullptr) const; TemplateData readTemplateFromFile(const QString &filePath, QString *error = nullptr); Ui::CabrilloTemplateDialog *ui; QList templates; int currentTemplateIndex; QList dbFieldItems; QList formatterItems; }; #endif // QLOG_UI_CABRILLOTEMPLATEDIALOG_H ================================================ FILE: ui/CabrilloTemplateDialog.ui ================================================ CabrilloTemplateDialog 0 0 850 550 Cabrillo Template Manager true Import template Import Export template Export 0 0 New template New Copy existing template Copy Delete template Delete Template Name: Contest Name: true Default Mode: 0 0 QComboBox::AdjustToContents QSO Line Columns: 0 Contest name as required by the rules. It is possible to enter a custom string if it is not included in the list. QAbstractItemView::SingleSelection QAbstractItemView::SelectRows QAbstractItemView::ScrollPerPixel QAbstractItemView::ScrollPerPixel 5 false Seq. QSO Field Formatter Width Label 0 0 Qt::Vertical 20 40 :/icons/baseline-play_up-24px.svg:/icons/baseline-play_up-24px.svg :/icons/baseline-play_down-24px.svg:/icons/baseline-play_down-24px.svg Qt::Vertical 20 40 Qt::Horizontal 0 0 0 0 Add line Add Remove selected line Remove Qt::Horizontal 40 20 QDialogButtonBox::Cancel|QDialogButtonBox::Ok nameEdit contestCombo defaultModeCombo newButton copyButton deleteButton addColumnButton removeColumnButton templateList buttonBox rejected() CabrilloTemplateDialog reject() 425 530 425 275 buttonBox accepted() CabrilloTemplateDialog accept() 425 530 425 275 templateList currentRowChanged(int) CabrilloTemplateDialog templateSelectionChanged() 100 200 425 275 newButton clicked() CabrilloTemplateDialog newTemplate() 60 480 425 275 copyButton clicked() CabrilloTemplateDialog copyTemplate() 120 480 425 275 deleteButton clicked() CabrilloTemplateDialog deleteTemplate() 160 480 425 275 addColumnButton clicked() CabrilloTemplateDialog addColumn() 300 400 425 275 removeColumnButton clicked() CabrilloTemplateDialog removeColumn() 400 400 425 275 moveUpButton clicked() CabrilloTemplateDialog moveColumnUp() 500 400 425 275 moveDownButton clicked() CabrilloTemplateDialog moveColumnDown() 600 400 425 275 loadTemplateButton clicked() CabrilloTemplateDialog loadTemplate() 72 24 424 274 exportTemplateButton clicked() CabrilloTemplateDialog exportTemplate() 202 24 424 274 templateSelectionChanged() newTemplate() copyTemplate() deleteTemplate() addColumn() removeColumn() moveColumnUp() moveColumnDown() loadTemplate() exportTemplate() ================================================ FILE: ui/ChatWidget.cpp ================================================ #include #include #include #include #include "ChatWidget.h" #include "ui_ChatWidget.h" #include "core/debug.h" #include "service/kstchat/KSTChat.h" #include "ui/KSTChatWidget.h" #include "core/LogParam.h" MODULE_IDENTIFICATION("qlog.ui.chatwidget"); ChatWidget::ChatWidget(QWidget *parent) : QWidget(parent), ui(new Ui::ChatWidget) { FCT_IDENTIFICATION; ui->setupUi(this); ui->chatTabWidget->tabBar()->tabButton(0, QTabBar::RightSide)->hide(); ui->chatRoomCombo->addItems(KSTChat::chatRooms); QWidget *w = ui->chatTabWidget->widget(0); w->setProperty("chatName", tr("New")); ui->chatTabWidget->setTabText(0, generateTabName(w)); ui->chatRoomCombo->setCurrentIndex(LogParam::getChatSelectedRoom()); } ChatWidget::~ChatWidget() { FCT_IDENTIFICATION; for (int i = 0; i < ui->chatTabWidget->count(); i++ ) { if ( qobject_cast(ui->chatTabWidget->widget(i)) ) ui->chatTabWidget->widget(i)->disconnect(SIGNAL(chatClosed())); } delete ui; } void ChatWidget::registerContactWidget(const NewContactWidget *contactWidget) { FCT_IDENTIFICATION; contact = contactWidget; } void ChatWidget::setChatCallsign(QString callsign) { FCT_IDENTIFICATION; qCDebug(function_parameters) << callsign; KSTChatWidget *kstWidget = qobject_cast(ui->chatTabWidget->currentWidget()); if ( kstWidget ) { kstWidget->setPrivateChatCallsign(callsign); } } void ChatWidget::reloadStationProfile() { FCT_IDENTIFICATION; for (int i = 0; i < ui->chatTabWidget->count(); i++ ) { KSTChatWidget *kstWidget = qobject_cast(ui->chatTabWidget->widget(i)); if ( kstWidget ) kstWidget->reloadStationProfile(); } } void ChatWidget::resetDupe() { FCT_IDENTIFICATION; for (int i = 0; i < ui->chatTabWidget->count(); i++ ) { KSTChatWidget *kstWidget = qobject_cast(ui->chatTabWidget->widget(i)); if ( kstWidget ) kstWidget->resetDupe(); } } void ChatWidget::recalculateDupe() { FCT_IDENTIFICATION; for (int i = 0; i < ui->chatTabWidget->count(); i++ ) { KSTChatWidget *kstWidget = qobject_cast(ui->chatTabWidget->widget(i)); if ( kstWidget ) kstWidget->recalculateDupe(); } } void ChatWidget::recalculateDxccStatus() { for (int i = 0; i < ui->chatTabWidget->count(); i++ ) { KSTChatWidget *kstWidget = qobject_cast(ui->chatTabWidget->widget(i)); if ( kstWidget ) kstWidget->recalculateDxccStatus(); } } void ChatWidget::updateSpotsStatusWhenQSOAdded(const QSqlRecord &record) { FCT_IDENTIFICATION; for (int i = 0; i < ui->chatTabWidget->count(); i++ ) { KSTChatWidget *kstWidget = qobject_cast(ui->chatTabWidget->widget(i)); if ( kstWidget ) kstWidget->updateSpotsStatusWhenQSOAdded(record); } } void ChatWidget::updateSpotsDupeWhenQSODeleted(const QSqlRecord &record) { FCT_IDENTIFICATION; for (int i = 0; i < ui->chatTabWidget->count(); i++ ) { KSTChatWidget *kstWidget = qobject_cast(ui->chatTabWidget->widget(i)); if ( kstWidget ) kstWidget->updateSpotsStatusWhenQSODeleted(record); } } void ChatWidget::updateSpotsDxccStatusWhenQSODeleted(const QSet &entities) { FCT_IDENTIFICATION; for (int i = 0; i < ui->chatTabWidget->count(); i++ ) { KSTChatWidget *kstWidget = qobject_cast(ui->chatTabWidget->widget(i)); if ( kstWidget ) kstWidget->updateSpotsDxccStatusWhenQSODeleted(entities); } } void ChatWidget::connectChat() { FCT_IDENTIFICATION; QString username = KSTChat::getUsername(); QString password = KSTChat::getPasswd(); if ( username.isEmpty() || password.isEmpty() ) { QMessageBox::warning(this, tr("QLog Warning"), tr("ON4KST Chat is not configured properly.

Please, use Settings dialog to configure it.

")); return; } KSTChatWidget *newWidget = new KSTChatWidget(ui->chatRoomCombo->currentIndex() + 1, username, password, contact, this); newWidget->setProperty("chatName", ui->chatRoomCombo->currentText()); newWidget->setProperty("unreadMsg", 0); newWidget->setProperty("valuableMsg", 0); ui->chatTabWidget->addTab(newWidget,generateTabName(newWidget)); ui->chatTabWidget->setCurrentWidget(newWidget); connect(newWidget, &KSTChatWidget::chatClosed, this, [this, newWidget]() { int i = this->findTabWidgetIndex(newWidget); if ( i >= 0 ) { this->closeTab(i); } }); connect(newWidget, &KSTChatWidget::chatUpdated, this, &ChatWidget::tabActive); connect(newWidget, &KSTChatWidget::valuableMessageUpdated, this, &ChatWidget::valuableMessageActive); connect(newWidget, &KSTChatWidget::prepareQSOInfo, this, &ChatWidget::processQSOInfo); connect(newWidget, &KSTChatWidget::userListUpdated, this, &ChatWidget::userListUpdate); connect(newWidget, &KSTChatWidget::beamingRequested, this, &ChatWidget::beamRequest); #if 0 // see comments in closeTab // Disable Chat Room in the Combobox QStandardItemModel *model = qobject_cast(ui->chatRoomCombo->model()); if ( model ) { QStandardItem *item = model->item(ui->chatRoomCombo->currentIndex()); item->setFlags(item->flags() & ~Qt::ItemIsEnabled); } #endif LogParam::setChatSelectedRoom(ui->chatRoomCombo->currentIndex()); } void ChatWidget::closeTab(int tabIndex) { FCT_IDENTIFICATION; qCDebug(function_parameters) << tabIndex; if ( tabIndex < 0 ) return; KSTChatWidget *kstWidget = qobject_cast(ui->chatTabWidget->widget(tabIndex)); ui->chatTabWidget->removeTab(tabIndex); #if 0 // It is not an user friednly to disable the item after connection // because QLog save the last selected item and this items remains selected - it is chaotic // Enable Chat Room in the Combobox QStandardItemModel *model = qobject_cast(ui->chatRoomCombo->model()); if ( model ) { QStandardItem *item = model->item(ui->chatRoomCombo->findText(kstWidget->property("chatName").toString())); item->setFlags(item->flags() | Qt::ItemIsEnabled); } #endif kstWidget->deleteLater(); ui->chatTabWidget->setCurrentIndex(tabIndex - 1); //emit userListUpdated(QList()); } void ChatWidget::tabActive(QWidget *w) { FCT_IDENTIFICATION; if ( w == ui->chatTabWidget->currentWidget() ) return; int unread = w->property("unreadMsg").toInt(); unread++; w->setProperty("unreadMsg", unread); int i = findTabWidgetIndex(w); if ( i >=0 ) { ui->chatTabWidget->setTabText(i, generateTabName(w)); setTabUnreadInfo(i, w); } } void ChatWidget::valuableMessageActive(QWidget *w) { FCT_IDENTIFICATION; if ( w == ui->chatTabWidget->currentWidget() ) return; int valuableMsgCounter = w->property("valuableMsg").toInt(); valuableMsgCounter++; w->setProperty("valuableMsg", valuableMsgCounter); int i = findTabWidgetIndex(w); if ( i >=0 ) { ui->chatTabWidget->setTabText(i, generateTabName(w)); setTabUnreadInfo(i, w); } } void ChatWidget::chatTabClicked(int tabIndex) { FCT_IDENTIFICATION; qCDebug(function_parameters) << tabIndex; QWidget *w = ui->chatTabWidget->widget(tabIndex); w->setProperty("unreadMsg", 0); w->setProperty("valuableMsg", 0); ui->chatTabWidget->setTabText(tabIndex, generateTabName(w)); setTabUnreadInfo(tabIndex, w); KSTChatWidget *kstWidget = qobject_cast(w); if ( kstWidget ) emit userListUpdated(kstWidget->getUserList()); else emit userListUpdated(QList()); } void ChatWidget::processQSOInfo(const QString &callsign, const QString &grid) { FCT_IDENTIFICATION; emit prepareQSOInfo(callsign, grid); } void ChatWidget::userListUpdate(QWidget *w) { FCT_IDENTIFICATION; if ( w != ui->chatTabWidget->currentWidget() ) return; KSTChatWidget *kstWidget = qobject_cast(w); if ( kstWidget ) emit userListUpdated(kstWidget->getUserList()); } void ChatWidget::beamRequest(double az) { FCT_IDENTIFICATION; emit beamingRequested(az); } int ChatWidget::findTabWidgetIndex(QWidget *w) { FCT_IDENTIFICATION; for (int i = 0 ; i < ui->chatTabWidget->count(); i++ ) { if ( ui->chatTabWidget->widget(i) == w ) return i; } return -1; } QString ChatWidget::generateTabName(QWidget *w) { FCT_IDENTIFICATION; return w->property("chatName").toString(); } QString ChatWidget::generateTabUnread(QWidget *w) { FCT_IDENTIFICATION; int unread = w->property("unreadMsg").toInt(); int valuableMsgCnt = w->property("valuableMsg").toInt(); return (( unread > 0 ) ? QString::number(valuableMsgCnt) + "/" + QString::number(unread) : ""); } void ChatWidget::setTabUnreadInfo(int tabIndex, QWidget *w) { FCT_IDENTIFICATION; if ( !w ) return; QWidget *tabButton = ui->chatTabWidget->tabBar()->tabButton(tabIndex, QTabBar::LeftSide); int unread = w->property("unreadMsg").toInt(); int valuableMsgCnt = w->property("valuableMsg").toInt(); if ( tabButton ) { tabButton->deleteLater(); ui->chatTabWidget->tabBar()->setTabButton(tabIndex, QTabBar::LeftSide, nullptr); tabButton = nullptr; } if ( unread == 0 && valuableMsgCnt == 0 ) { return; } else { // if I reuse the label then I have an issue with a geometry. Therefore I remove it and // create it again. tabButton = new QLabel(generateTabUnread(w)); tabButton->setObjectName((valuableMsgCnt > 0) ? "chatValuableMsg" : "chatUnread"); //based on this name, stylesheed is set from stylesheet.css ui->chatTabWidget->tabBar()->setTabButton(tabIndex, QTabBar::LeftSide, tabButton); } } ================================================ FILE: ui/ChatWidget.h ================================================ #ifndef QLOG_UI_CHATWIDGET_H #define QLOG_UI_CHATWIDGET_H #include #include "service/kstchat/KSTChat.h" #include "ui/NewContactWidget.h" namespace Ui { class ChatWidget; } class ChatWidget : public QWidget { Q_OBJECT public: explicit ChatWidget(QWidget *parent = nullptr); ~ChatWidget(); void registerContactWidget(const NewContactWidget *contactWidget); public slots: void setChatCallsign(QString); void reloadStationProfile(); void resetDupe(); void recalculateDupe(); void recalculateDxccStatus(); void updateSpotsStatusWhenQSOAdded(const QSqlRecord &record); void updateSpotsDupeWhenQSODeleted(const QSqlRecord &record); void updateSpotsDxccStatusWhenQSODeleted(const QSet &entities); private slots: void connectChat(); void closeTab(int); void tabActive(QWidget *); void valuableMessageActive(QWidget *); void chatTabClicked(int); void processQSOInfo(const QString&, const QString&); void userListUpdate(QWidget *w); void beamRequest(double); signals: void prepareQSOInfo(QString, QString); void userListUpdated(QList); void beamingRequested(double); private: Ui::ChatWidget *ui; const NewContactWidget *contact; int findTabWidgetIndex(QWidget *); QString generateTabName(QWidget *); QString generateTabUnread(QWidget *); void setTabUnreadInfo(int, QWidget *); }; #endif // QLOG_UI_CHATWIDGET_H ================================================ FILE: ui/ChatWidget.ui ================================================ ChatWidget 0 0 400 300 Form 0 0 0 0 Qt::ClickFocus QTabBar::tab {padding: 5px;} QTabWidget::North 0 5 5 true true Qt::Vertical 20 40 0 0 Qt::Horizontal 40 20 ON4KST Chat Qt::AlignCenter true Qt::ClickFocus Chat Room true Qt::ClickFocus Connect Qt::Horizontal 40 20 Qt::Vertical 20 40 chatTabWidget tabCloseRequested(int) ChatWidget closeTab(int) 199 149 199 149 chatConnectButton clicked() ChatWidget connectChat() 223 195 199 149 chatTabWidget currentChanged(int) ChatWidget chatTabClicked(int) 199 149 199 149 closeTab(int) connectChat() chatTypeChanged(QString) chatTabClicked(int) ================================================ FILE: ui/ClockWidget.cpp ================================================ #include #include #include #include "ClockWidget.h" #include "ui_ClockWidget.h" #include "core/debug.h" #include "data/Gridsquare.h" #include "data/StationProfile.h" #define EARTH_RADIUS 6371 #define EARTH_CIRCUM 40075 #define MSECS_PER_DAY (24.0 * 60.0 * 60.0 * 1000.0) MODULE_IDENTIFICATION("qlog.ui.clockwidget"); ClockWidget::ClockWidget(QWidget *parent) : QWidget(parent), ui(new Ui::ClockWidget), sunScene(new QGraphicsScene(this)), clockScene(new QGraphicsScene(this)), clockItem(new QGraphicsTextItem) { FCT_IDENTIFICATION; ui->setupUi(this); QTimer *timer = new QTimer(this); connect(timer, &QTimer::timeout, this, &ClockWidget::updateClock); timer->start(500); sunScene->setSceneRect(0, 0, 200, 10); ui->sunGraphicsView->setScene(sunScene.data()); QFont font = clockItem->font(); font.setPointSize(20); clockItem->setFont(font); clockScene->addItem(clockItem.data()); ui->clockGraphicsView->setScene(clockScene.data()); updateClock(); updateSun(); updateSunGraph(); } void ClockWidget::updateClock() { FCT_IDENTIFICATION; QDateTime now = QDateTime::currentDateTime().toTimeZone(QTimeZone::utc()); QColor textColor = qApp->palette().color(QPalette::Text); clockItem->setDefaultTextColor(textColor); clockItem->setPlainText(locale.toString(now, locale.formatTimeLongWithoutTZ())); if (now.time().second() == 0) { updateSun(); updateSunGraph(); } /* Use only in case when you want to debug which widget is focussed*/ //#define SHOW_FOCUS #ifdef SHOW_FOCUS QWidget * fw = qApp->focusWidget(); if ( fw ) qInfo() << fw->objectName() << fw->parent()->objectName(); #endif } void ClockWidget::updateSun() { FCT_IDENTIFICATION; Gridsquare myGrid (StationProfilesManager::instance()->getCurProfile1().locator); if ( myGrid.isValid() ) { double lat = myGrid.getLatitude(); double lon = myGrid.getLongitude(); qint64 julianDay = QDate::currentDate().toJulianDay(); double n = static_cast(julianDay) - 2451545.0 + 0.0008; double Js = n - lon / 360.0; double M = fmod(357.5291 + 0.98560028 * Js, 360.0); double C = 1.9148 * sin(M / 180.0 * M_PI) + 0.0200 * sin(2 * M / 180.0 * M_PI) + 0.0003 * sin(3 * M / 180.0 * M_PI); double L = fmod(M + C + 180 + 102.9372, 360.0); double Jt = 2451545.0 + Js + 0.0053 * sin(M / 180.0 * M_PI) - 0.0069 * sin(2 * L / 180.0 * M_PI); double sind = sin(L / 180.0 * M_PI) * sin(23.44 / 180.0 * M_PI); double cosd = cos(asin(sind)); double w = acos((sin(-0.83 / 180.0 * M_PI) - sin(lat / 180.0 * M_PI) * sind) / (cos(lat / 180.0 * M_PI) * cosd)); double Jrise = Jt - w / (2*M_PI) + 0.5; double Jset = Jt + w / (2*M_PI) + 0.5; sunrise = QTime::fromMSecsSinceStartOfDay(static_cast(fmod(Jrise, 1.0) * MSECS_PER_DAY)); sunset = QTime::fromMSecsSinceStartOfDay(static_cast(fmod(Jset, 1.0) * MSECS_PER_DAY)); ui->sunRiseLabel->setText(locale.toString(sunrise, locale.formatTimeShort())); ui->sunSetLabel->setText(locale.toString(sunset, locale.formatTimeShort())); } else { ui->sunRiseLabel->setText(tr("N/A")); ui->sunSetLabel->setText(tr("N/A")); } } void ClockWidget::updateSunGraph() { FCT_IDENTIFICATION; QColor dayColor(255, 253, 59); QColor nightColor(33, 150, 243); QColor currentColor(229, 57, 53); qreal width = sunScene->width(); double rise = sunrise.msecsSinceStartOfDay() / MSECS_PER_DAY * width; double set = sunset.msecsSinceStartOfDay() / MSECS_PER_DAY * width; double cur = QDateTime::currentDateTimeUtc().time().msecsSinceStartOfDay() / MSECS_PER_DAY * width; sunScene->clear(); if ( set > rise ) { sunScene->addRect(0, 0, rise, 10, QPen(Qt::NoPen), QBrush(nightColor, Qt::SolidPattern)); sunScene->addRect(rise, 0, set-rise, 10, QPen(Qt::NoPen), QBrush(dayColor, Qt::SolidPattern)); sunScene->addRect(set, 0, width-set, 10, QPen(Qt::NoPen), QBrush(nightColor, Qt::SolidPattern)); } else { sunScene->addRect(0, 0, set, 10, QPen(Qt::NoPen), QBrush(dayColor, Qt::SolidPattern)); sunScene->addRect(set, 0, rise-set, 10, QPen(Qt::NoPen), QBrush(nightColor, Qt::SolidPattern)); sunScene->addRect(rise, 0, width-rise, 10, QPen(Qt::NoPen), QBrush(dayColor, Qt::SolidPattern)); } QPen currentPen(currentColor); currentPen.setWidthF(2.0); sunScene->addLine(cur, 0, cur, 10, currentPen); } ClockWidget::~ClockWidget() { FCT_IDENTIFICATION; delete ui; } ================================================ FILE: ui/ClockWidget.h ================================================ #ifndef QLOG_UI_CLOCKWIDGET_H #define QLOG_UI_CLOCKWIDGET_H #include #include #include "core/LogLocale.h" #include namespace Ui { class ClockWidget; } class QGraphicsScene; class ClockWidget : public QWidget { Q_OBJECT public: explicit ClockWidget(QWidget *parent = nullptr); ~ClockWidget(); public slots: void updateClock(); void updateSun(); void updateSunGraph(); private: Ui::ClockWidget *ui; QScopedPointer sunScene; QScopedPointer clockScene; QScopedPointer clockItem; LogLocale locale; QTime sunrise; QTime sunset; }; #endif // QLOG_UI_CLOCKWIDGET_H ================================================ FILE: ui/ClockWidget.ui ================================================ ClockWidget 0 0 303 106 Form 0 0 0 0 0 0 0 16777215 40 Qt::NoFocus background-color: transparent; QFrame::NoFrame QFrame::Plain 0 Qt::ScrollBarAlwaysOff Qt::ScrollBarAlwaysOff false 16777215 20 Qt::NoFocus background-color: transparent; QFrame::NoFrame QFrame::Plain 0 Qt::ScrollBarAlwaysOff Qt::ScrollBarAlwaysOff false QPainter::TextAntialiasing Qt::Horizontal 40 0 0 0 50 0 8 Sunrise Qt::AlignCenter Qt::Horizontal QSizePolicy::Maximum 150 0 0 0 50 0 8 Sunset Qt::AlignCenter Qt::Horizontal 40 0 Qt::Horizontal 40 0 true 0 0 50 0 8 Sunrise Qt::AlignCenter Qt::Horizontal QSizePolicy::Maximum 150 0 0 0 50 0 8 Sunset Qt::AlignCenter Qt::Horizontal 40 0 Qt::Vertical 20 40 ================================================ FILE: ui/ColumnSettingDialog.cpp ================================================ #include #include #include "ColumnSettingDialog.h" #include "ui_ColumnSettingDialog.h" #include "ui_ColumnSettingSimpleDialog.h" #include "core/debug.h" #define CHECKBOXESPERROW 4 MODULE_IDENTIFICATION("qlog.ui.ColumnSettingDialog"); ColumnSettingDialog::ColumnSettingDialog(QTableView *table, QWidget *parent, const QList &columnIdExcludeFilter) : ColumnSettingGenericDialog(table->model(), parent), ui(new Ui::ColumnSettingDialog), table(table), columnIdExcludeFilter(columnIdExcludeFilter) { FCT_IDENTIFICATION; setupDialog(); } ColumnSettingDialog::ColumnSettingDialog(const QAbstractItemModel *model, const QSet &defaultStates, QWidget *parent, const QList &columnIdExcludeFilter) : ColumnSettingGenericDialog(model, parent), ui(new Ui::ColumnSettingDialog), table(nullptr), defaultColumnsState(defaultStates), columnIdExcludeFilter(columnIdExcludeFilter) { FCT_IDENTIFICATION; setupDialog(); } void ColumnSettingDialog::setupDialog() { FCT_IDENTIFICATION; ui->setupUi(this); ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Done")); ui->tabWidget->setCurrentIndex(0); QList generalCheckboxList; QList myInfoCheckboxList; QList qslInfoCheckboxList; QList membersInfoCheckboxList; QList otherInfoCheckboxList; QList conditionsCheckboxList; QList contestCheckboxList; int columnIndex = 0; while ( columnIndex < model->columnCount() ) { if ( columnIdExcludeFilter.contains(static_cast(columnIndex) ) ) { columnIndex++; continue; } QCheckBox *columnCheckbox = new QCheckBox(); QString columnNameString = model->headerData(columnIndex, Qt::Horizontal).toString(); columnCheckbox->setChecked(((!table) ? defaultColumnsState.contains(columnIndex) : !table->isColumnHidden(columnIndex))); columnCheckbox->setText(columnNameString); #if (QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)) connect(columnCheckbox, &QCheckBox::checkStateChanged, this, [columnIndex, this](Qt::CheckState state) #else connect(columnCheckbox, &QCheckBox::stateChanged, this, [columnIndex, this](int state) #endif { emit columnChanged(columnIndex, state); if ( table ) table->setColumnHidden(columnIndex, !table->isColumnHidden(columnIndex)); }); switch ( columnIndex ) { case LogbookModel::COLUMN_MY_ALTITUDE: case LogbookModel::COLUMN_MY_ARRL_SECT: case LogbookModel::COLUMN_MY_ANTENNA: case LogbookModel::COLUMN_MY_ANTENNA_INTL: case LogbookModel::COLUMN_MY_CITY: case LogbookModel::COLUMN_MY_CITY_INTL: case LogbookModel::COLUMN_MY_CNTY: case LogbookModel::COLUMN_MY_COUNTRY: case LogbookModel::COLUMN_MY_COUNTRY_INTL: case LogbookModel::COLUMN_MY_CQ_ZONE: case LogbookModel::COLUMN_MY_DXCC: case LogbookModel::COLUMN_MY_FISTS: case LogbookModel::COLUMN_MY_GRIDSQUARE: case LogbookModel::COLUMN_MY_GRIDSQUARE_EXT: case LogbookModel::COLUMN_MY_IOTA: case LogbookModel::COLUMN_MY_IOTA_ISLAND_ID: case LogbookModel::COLUMN_MY_ITU_ZONE: case LogbookModel::COLUMN_MY_LAT: case LogbookModel::COLUMN_MY_LON: case LogbookModel::COLUMN_MY_NAME: case LogbookModel::COLUMN_MY_NAME_INTL: case LogbookModel::COLUMN_MY_POSTAL_CODE: case LogbookModel::COLUMN_MY_POSTAL_CODE_INTL: case LogbookModel::COLUMN_MY_POTA_REF: case LogbookModel::COLUMN_MY_RIG: case LogbookModel::COLUMN_MY_RIG_INTL: case LogbookModel::COLUMN_MY_SIG: case LogbookModel::COLUMN_MY_SIG_INTL: case LogbookModel::COLUMN_MY_SIG_INFO: case LogbookModel::COLUMN_MY_SIG_INFO_INTL: case LogbookModel::COLUMN_MY_SOTA_REF: case LogbookModel::COLUMN_MY_STATE: case LogbookModel::COLUMN_MY_STREET: case LogbookModel::COLUMN_MY_STREET_INTL: case LogbookModel::COLUMN_MY_USACA_COUNTIES: case LogbookModel::COLUMN_MY_VUCC_GRIDS: case LogbookModel::COLUMN_MY_WWFF_REF: case LogbookModel::COLUMN_MY_CNTY_ALT: case LogbookModel::COLUMN_MY_DARC_DOK: case LogbookModel::COLUMN_MY_MORSE_KEY_INFO: case LogbookModel::COLUMN_MY_MORSE_KEY_TYPE: myInfoCheckboxList.append(columnCheckbox); break; case LogbookModel::COLUMN_QSL_RCVD: case LogbookModel::COLUMN_QSL_RCVD_DATE: case LogbookModel::COLUMN_QSL_SENT: case LogbookModel::COLUMN_QSL_SENT_DATE: case LogbookModel::COLUMN_EQSL_QSLRDATE: case LogbookModel::COLUMN_EQSL_QSLSDATE: case LogbookModel::COLUMN_EQSL_QSL_RCVD: case LogbookModel::COLUMN_EQSL_QSL_SENT: case LogbookModel::COLUMN_EQSL_AG: case LogbookModel::COLUMN_QSLMSG: case LogbookModel::COLUMN_QSLMSG_INTL: case LogbookModel::COLUMN_QSL_RCVD_VIA: case LogbookModel::COLUMN_QSL_SENT_VIA: case LogbookModel::COLUMN_QSL_VIA: case LogbookModel::COLUMN_LOTW_RCVD: case LogbookModel::COLUMN_LOTW_SENT: case LogbookModel::COLUMN_LOTW_RCVD_DATE: case LogbookModel::COLUMN_LOTW_SENT_DATE: case LogbookModel::COLUMN_QRZCOM_QSO_UPLOAD_DATE: case LogbookModel::COLUMN_QRZCOM_QSO_UPLOAD_STATUS: case LogbookModel::COLUMN_CLUBLOG_QSO_UPLOAD_DATE: case LogbookModel::COLUMN_CLUBLOG_QSO_UPLOAD_STATUS: case LogbookModel::COLUMN_HRDLOG_QSO_UPLOAD_DATE: case LogbookModel::COLUMN_HRDLOG_QSO_UPLOAD_STATUS: case LogbookModel::COLUMN_HAMLOGEU_QSO_UPLOAD_DATE: case LogbookModel::COLUMN_HAMLOGEU_QSO_UPLOAD_STATUS: case LogbookModel::COLUMN_HAMQTH_QSO_UPLOAD_DATE: case LogbookModel::COLUMN_HAMQTH_QSO_UPLOAD_STATUS: case LogbookModel::COLUMN_DCL_QSLRDATE: case LogbookModel::COLUMN_DCL_QSLSDATE: case LogbookModel::COLUMN_DCL_QSL_RCVD: case LogbookModel::COLUMN_DCL_QSL_SENT: case LogbookModel::COLUMN_QRZCOM_QSO_DOWNLOAD_DATE: case LogbookModel::COLUMN_QRZCOM_QSO_DOWNLOAD_STATUS: case LogbookModel::COLUMN_QSLMSG_RCVD: qslInfoCheckboxList.append(columnCheckbox); break; case LogbookModel::COLUMN_TEN_TEN: case LogbookModel::COLUMN_FISTS: case LogbookModel::COLUMN_FISTS_CC: case LogbookModel::COLUMN_SKCC: case LogbookModel::COLUMN_UKSMG: case LogbookModel::COLUMN_DARC_DOK: membersInfoCheckboxList.append(columnCheckbox); break; case LogbookModel::COLUMN_VE_PROV: case LogbookModel::COLUMN_USACA_COUNTIES: case LogbookModel::COLUMN_PUBLIC_KEY: case LogbookModel::COLUMN_FIELDS: case LogbookModel::COLUMN_AWARD_GRANTED: case LogbookModel::COLUMN_AWARD_SUBMITTED: case LogbookModel::COLUMN_CREDIT_GRANTED: case LogbookModel::COLUMN_CREDIT_SUBMITTED: case LogbookModel::COLUMN_CLASS: case LogbookModel::COLUMN_AGE: case LogbookModel::COLUMN_REGION: case LogbookModel::COLUMN_SILENT_KEY: case LogbookModel::COLUMN_WEB: case LogbookModel::COLUMN_GUEST_OP: case LogbookModel::COLUMN_FORCE_INIT: case LogbookModel::COLUMN_MAX_BURSTS: case LogbookModel::COLUMN_MS_SHOWER: case LogbookModel::COLUMN_NR_PINGS: case LogbookModel::COLUMN_NR_BURSTS: case LogbookModel::COLUMN_QSO_RANDOM: case LogbookModel::COLUMN_QSO_COMPLETE: case LogbookModel::COLUMN_LAT: case LogbookModel::COLUMN_LON: case LogbookModel::COLUMN_OWNER_CALLSIGN: case LogbookModel::COLUMN_CONTACTED_OP: case LogbookModel::COLUMN_OPERATOR: case LogbookModel::COLUMN_STATION_CALLSIGN: case LogbookModel::COLUMN_COMMENT: case LogbookModel::COLUMN_COUNTRY: case LogbookModel::COLUMN_IOTA_ISLAND_ID: case LogbookModel::COLUMN_NAME: case LogbookModel::COLUMN_NOTES: case LogbookModel::COLUMN_QTH: case LogbookModel::COLUMN_RIG: case LogbookModel::COLUMN_ADDRESS: case LogbookModel::COLUMN_RX_PWR: case LogbookModel::COLUMN_GRID_EXT: case LogbookModel::COLUMN_SIG: case LogbookModel::COLUMN_SIG_INFO: otherInfoCheckboxList.append(columnCheckbox); break; case LogbookModel::COLUMN_A_INDEX: case LogbookModel::COLUMN_K_INDEX: case LogbookModel::COLUMN_PROP_MODE: case LogbookModel::COLUMN_SFI: conditionsCheckboxList.append(columnCheckbox); break; case LogbookModel::COLUMN_PRECEDENCE: case LogbookModel::COLUMN_CONTEST_ID: case LogbookModel::COLUMN_SRX: case LogbookModel::COLUMN_SRX_STRING: case LogbookModel::COLUMN_STX: case LogbookModel::COLUMN_STX_STRING: case LogbookModel::COLUMN_CHECK: contestCheckboxList.append(columnCheckbox); break; default: generalCheckboxList.append(columnCheckbox); } columnIndex++; } addSortedCheckboxes(ui->generalInfoGrid, generalCheckboxList, CHECKBOXESPERROW); addSortedCheckboxes(ui->myInfoGrid, myInfoCheckboxList, CHECKBOXESPERROW); addSortedCheckboxes(ui->qslInfoGrid, qslInfoCheckboxList, CHECKBOXESPERROW); addSortedCheckboxes(ui->membersInfoGrig, membersInfoCheckboxList, CHECKBOXESPERROW); addSortedCheckboxes(ui->conditionsInfoGrid, conditionsCheckboxList, CHECKBOXESPERROW); addSortedCheckboxes(ui->contestsInfoGrid, contestCheckboxList, CHECKBOXESPERROW); addSortedCheckboxes(ui->otherInfoGrid, otherInfoCheckboxList, CHECKBOXESPERROW); addSelectUnselect(ui->generalInfoGrid, CHECKBOXESPERROW); addSelectUnselect(ui->myInfoGrid, CHECKBOXESPERROW); addSelectUnselect(ui->qslInfoGrid, CHECKBOXESPERROW); addSelectUnselect(ui->membersInfoGrig, CHECKBOXESPERROW); addSelectUnselect(ui->conditionsInfoGrid, CHECKBOXESPERROW); addSelectUnselect(ui->contestsInfoGrid, CHECKBOXESPERROW); addSelectUnselect(ui->otherInfoGrid, CHECKBOXESPERROW); } ColumnSettingDialog::~ColumnSettingDialog() { FCT_IDENTIFICATION; delete ui; } ColumnSettingGenericDialog::ColumnSettingGenericDialog(const QAbstractItemModel *model, QWidget *parent) : QDialog(parent), model(model) { FCT_IDENTIFICATION; } void ColumnSettingGenericDialog::addSortedCheckboxes(QGridLayout *grid, QList &checkboxlist, int elementsPerRow) { FCT_IDENTIFICATION; int elementIndex = 0; std::sort( checkboxlist.begin(), checkboxlist.end(), [](const QCheckBox* a, const QCheckBox *b)->bool { return a->text().localeAwareCompare(b->text()) < 0; }); for (auto item: checkboxlist) { grid->addWidget(item, elementIndex/elementsPerRow, elementIndex%elementsPerRow); elementIndex++; } } void ColumnSettingGenericDialog::addSelectUnselect(QGridLayout *grid, int elementsPerRow) { FCT_IDENTIFICATION; const QString unselectAllLabel = tr("Unselect All"); const QString selectAllLabel = tr("Select All"); QPushButton *buttonUnselectAll=new QPushButton(); QPushButton *buttonSelectAll=new QPushButton(); buttonUnselectAll->setText(unselectAllLabel); buttonSelectAll->setText(selectAllLabel); int current_rows = grid->rowCount(); grid->addWidget(buttonUnselectAll, current_rows + 1, 0); grid->addWidget(buttonSelectAll, current_rows + 1, elementsPerRow - 1); connect(buttonUnselectAll, &QPushButton::clicked, this, [grid]() { for(int idx = 0; idx < grid->count(); idx++) { QLayoutItem *item = grid->itemAt(idx); if ( !item || !item->widget() ) { continue; } QCheckBox *checkbox = qobject_cast(item->widget()); if ( checkbox ) { checkbox->setChecked(false); } } }); connect(buttonSelectAll, &QPushButton::clicked, this, [grid]() { for(int idx = 0; idx < grid->count(); idx++) { QLayoutItem *item = grid->itemAt(idx); if ( !item || !item->widget() ) { continue; } QCheckBox *checkbox = qobject_cast(item->widget()); if ( checkbox ) { checkbox->setChecked(true); } } }); } ColumnSettingSimpleDialog::ColumnSettingSimpleDialog(QTableView *table, QWidget *parent) : ColumnSettingGenericDialog(table->model(), parent), ui(new Ui::ColumnSettingSimpleDialog), table(table) { FCT_IDENTIFICATION; ui->setupUi(this); ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Done")); QList checkboxList; int columnIndex = 0; while ( columnIndex < model->columnCount() ) { QCheckBox *columnCheckbox=new QCheckBox(); QString columnNameString = model->headerData(columnIndex, Qt::Horizontal).toString(); columnCheckbox->setChecked(!table->isColumnHidden(columnIndex)); columnCheckbox->setText(columnNameString); #if (QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)) connect(columnCheckbox, &QCheckBox::checkStateChanged, this, [columnIndex, table, this](Qt::CheckState state) #else connect(columnCheckbox, &QCheckBox::stateChanged, this, [columnIndex, table, this](int state) #endif { emit columnChanged(columnIndex, state); if ( table) table->setColumnHidden(columnIndex, !table->isColumnHidden(columnIndex)); }); checkboxList.append(columnCheckbox); columnIndex++; } addSortedCheckboxes(ui->generalInfoGrid, checkboxList, CHECKBOXESPERROW); addSelectUnselect(ui->generalInfoGrid, CHECKBOXESPERROW); } ColumnSettingSimpleDialog::~ColumnSettingSimpleDialog() { FCT_IDENTIFICATION; delete ui; } ================================================ FILE: ui/ColumnSettingDialog.h ================================================ #ifndef QLOG_UI_COLUMNSETTINGDIALOG_H #define QLOG_UI_COLUMNSETTINGDIALOG_H #include #include #include #include #include #include #include #include "models/LogbookModel.h" namespace Ui { class ColumnSettingDialog; class ColumnSettingSimpleDialog; } class ColumnSettingGenericDialog : public QDialog { Q_OBJECT public: explicit ColumnSettingGenericDialog(const QAbstractItemModel *model, QWidget *parent = nullptr); ~ColumnSettingGenericDialog(){}; signals: void columnChanged(int index, bool state); protected: void addSortedCheckboxes(QGridLayout *grid, QList &checkboxlist, int elementsPerRow); void addSelectUnselect(QGridLayout *, int); const QAbstractItemModel *model; }; class ColumnSettingSimpleDialog : public ColumnSettingGenericDialog { Q_OBJECT public: explicit ColumnSettingSimpleDialog(QTableView *table, QWidget *parent = nullptr); ~ColumnSettingSimpleDialog(); private: Ui::ColumnSettingSimpleDialog *ui; QTableView *table; }; class ColumnSettingDialog : public ColumnSettingGenericDialog { Q_OBJECT public: explicit ColumnSettingDialog(QTableView *table, QWidget *parent = nullptr, const QList &columnIdExcludeFilter = QList()); explicit ColumnSettingDialog(const QAbstractItemModel *model, const QSet &defaultStates, QWidget *parent = nullptr, const QList &columnIdExcludeFilter = QList()); ~ColumnSettingDialog(); private: void setupDialog(); Ui::ColumnSettingDialog *ui; QTableView *table; QSet defaultColumnsState; QList columnIdExcludeFilter; }; #endif // QLOG_UI_COLUMNSETTINGDIALOG_H ================================================ FILE: ui/ColumnSettingDialog.ui ================================================ ColumnSettingDialog 0 0 680 444 Column Visibility Setting 0 General Qt::Vertical 20 40 My Info Qt::Vertical 20 40 QSL && Callbooks Qt::Vertical 20 40 Members Qt::Vertical 20 40 Conditionals Qt::Vertical 20 40 Contests Qt::Vertical 20 40 Others Qt::Vertical 20 40 Qt::Horizontal QDialogButtonBox::Ok false buttonBox accepted() ColumnSettingDialog accept() 248 254 157 274 buttonBox rejected() ColumnSettingDialog reject() 316 260 286 274 ================================================ FILE: ui/ColumnSettingSimpleDialog.ui ================================================ ColumnSettingSimpleDialog 0 0 158 69 Column Visibility Setting Qt::Horizontal QDialogButtonBox::Ok buttonBox accepted() ColumnSettingSimpleDialog accept() 248 254 157 274 buttonBox rejected() ColumnSettingSimpleDialog reject() 316 260 286 274 ================================================ FILE: ui/DXCCSubmissionDialog.cpp ================================================ #include #include #include #include #include #include "DXCCSubmissionDialog.h" #include "ui_DXCCSubmissionDialog.h" #include "models/SqlListModel.h" #include "core/debug.h" #include "core/QSOFilterManager.h" #include "data/BandPlan.h" #include "ui/ExportDialog.h" #include "ui/component/StyleItemDelegate.h" MODULE_IDENTIFICATION("qlog.ui.dxccsubmissiondialog"); // // ADIF credit_submitted / credit_granted are comma-delimited lists, e.g.: // "DXCC,DXCC_MODE,DXCC_BAND" // // Standard DXCC credit tokens: // DXCC — basic DXCC award (any mode, any band) // DXCC_MODE — DXCC mode endorsement (CW / Phone / Digital) // DXCC_BAND — DXCC band endorsement (per-band, incl. 5-Band DXCC) // // We use comma-padding so "DXCC" never accidentally matches "DXCC_MODE" or // "DXCC_BAND": INSTR(',' || field || ',', ',TOKEN,') > 0 // static QString creditHas(const QString &field, const QString &token) { return QString("INSTR(',' || COALESCE(%1,'') || ',', ',%2,') > 0") .arg(field, token); } DXCCSubmissionDialog::DXCCSubmissionDialog(QWidget *parent) : QDialog(parent) , ui(new Ui::DXCCSubmissionDialog) , tableModel(new QSqlQueryModel(this)) , entityCallsignModel(nullptr) { FCT_IDENTIFICATION; ui->setupUi(this); /********************/ /* My Entity Combo */ /********************/ entityCallsignModel = new SqlListModel( "SELECT my_dxcc, my_country_intl || ' (' || " "CASE WHEN LENGTH(GROUP_CONCAT(station_callsign, ', ')) > 50 " "THEN SUBSTR(GROUP_CONCAT(station_callsign, ', '), 0, 50) || '...' " "ELSE GROUP_CONCAT(station_callsign, ', ') END || ')' " "FROM(SELECT DISTINCT my_dxcc, my_country_intl, station_callsign FROM contacts) " "GROUP BY my_dxcc ORDER BY my_dxcc;", "", ui->myEntityComboBox); ui->myEntityComboBox->blockSignals(true); while (entityCallsignModel->canFetchMore()) entityCallsignModel->fetchMore(); ui->myEntityComboBox->setModel(entityCallsignModel); ui->myEntityComboBox->setModelColumn(1); ui->myEntityComboBox->blockSignals(false); /***************/ /* User Filter */ /***************/ ui->userFilterComboBox->blockSignals(true); ui->userFilterComboBox->setModel(QSOFilterManager::QSOFilterModel(tr("No User Filter"), ui->userFilterComboBox)); ui->userFilterComboBox->blockSignals(false); /*********/ /* Bands */ /*********/ ui->bandScopeComboBox->blockSignals(true); ui->bandScopeComboBox->addItem(tr("Any Band (Entity Level)"), QVariant(static_cast(DXCCBandScope::EntityLevel))); ui->bandScopeComboBox->addItem(tr("5-Band DXCC (80/40/20/15/10m)"), QVariant(static_cast(DXCCBandScope::FiveBand))); ui->bandScopeComboBox->addItem(tr("All DXCC Bands"), QVariant(static_cast(DXCCBandScope::AllDXCCBands))); ui->bandScopeComboBox->addItem(tr("Custom Band Selection"), QVariant(static_cast(DXCCBandScope::Custom))); ui->bandScopeComboBox->blockSignals(false); int i = 0; dxccBands = BandPlan::bandsList(false, true); for ( const Band &band : static_cast&>(dxccBands) ) { const QString &bandName = band.name; QCheckBox *cb = new QCheckBox(bandName, this); cb->setChecked(true); cb->setObjectName("bandCheckBox_" + bandName); int row = i / MAXCOLUMNS; int column = i % MAXCOLUMNS; ui->bandGroup->addWidget(cb, row, column); i++; bandCheckBoxes.append(cb); connect(cb, &QCheckBox::stateChanged, this, &DXCCSubmissionDialog::refreshTable); } // Band controls only visible when Custom scope is active setBandControlsVisible(false); ui->submissionTableView->setModel(tableModel); ui->submissionTableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); connect(ui->buttonGroup, QOverload::of(&QButtonGroup::buttonClicked), this, [this](QAbstractButton*) { refreshTable(); }); ui->exportADIFButton->setEnabled(false); // enabled once table has results ui->submissionTableView->setItemDelegateForColumn(6, new DateFormatDelegate(ui->submissionTableView)); refreshTable(); } DXCCSubmissionDialog::~DXCCSubmissionDialog() { FCT_IDENTIFICATION; delete ui; } void DXCCSubmissionDialog::onBandScopeChanged(int) { FCT_IDENTIFICATION; const DXCCBandScope scope = currentScope(); setBandControlsVisible(scope == DXCCBandScope::Custom); refreshTable(); } void DXCCSubmissionDialog::onFiveBandClicked() { FCT_IDENTIFICATION; ui->bandScopeComboBox->blockSignals(true); ui->bandScopeComboBox->setCurrentIndex( ui->bandScopeComboBox->findData(static_cast(DXCCBandScope::Custom))); ui->bandScopeComboBox->blockSignals(false); setBandControlsVisible(true); selectBandPreset(FIVE_BAND_DXCC); refreshTable(); } void DXCCSubmissionDialog::onAllBandsClicked() { FCT_IDENTIFICATION; for ( QCheckBox *cb : static_cast&>(bandCheckBoxes) ) { cb->blockSignals(true); cb->setChecked(true); cb->blockSignals(false); } } void DXCCSubmissionDialog::refreshTable() { FCT_IDENTIFICATION; if ( dxccBands.isEmpty() ) return; const DXCCBandScope scope = currentScope(); const bool perBand = (scope != DXCCBandScope::EntityLevel); const bool isMixed = ui->mixedRadioButton->isChecked(); const QStringList selectedBands = getSelectedBands(scope); const QString modeGroupFilter = buildModeGroupFilter(); /**********************/ /* Confirmation Level */ /**********************/ // ARRL DXCC accepts LoTW and paper/direct QSL cards only. QStringList confConds; if (ui->lotwCheckBox->isChecked()) confConds << "c.lotw_qsl_rcvd = 'Y'"; if (ui->paperCheckBox->isChecked()) confConds << "c.qsl_rcvd = 'Y'"; if ( confConds.isEmpty() ) { clearTable(); updateStatusLabel(0, selectedBands, scope); return; } const QString confFilter = confConds.join(" OR "); QString bandWhereClause; if ( perBand ) { if ( selectedBands.isEmpty() ) { clearTable(); updateStatusLabel(0, selectedBands, scope); return; } QStringList quoted; for ( const QString &b : selectedBands ) quoted << QString("'%1'").arg(b); bandWhereClause = QString("AND c.band IN (%1)").arg(quoted.join(",")); } const QString myEntity = getSelectedEntity(); const QString userFilter = (ui->userFilterComboBox->currentIndex() > 0) ? "AND " + QSOFilterManager::instance()->getWhereClause( ui->userFilterComboBox->currentText()) : ""; // ── ADIF credit token selection ──────────────────────────────────────── // // ARRL DXCC credit tokens stored in credit_submitted / credit_granted: // DXCC — basic DXCC (any mode, any band) // DXCC_MODE — mode endorsement (CW / Phone / Digital, entity-level) // DXCC_BAND — band endorsement (any mode per band, incl. 5-Band DXCC) // // For per-band scope, only DXCC_BAND matters — 5-Band DXCC (and all band // endorsements) require one contact per entity per band, ANY mode. There // is no separate "CW on 80m" credit token; the mode filter just controls // which contacts are eligible to show/submit, not the credit token itself. // // For entity-level: // Mixed → DXCC // CW/Phone/Digi → DXCC_MODE (filter ensures only that mode is shown) const QString creditToken = perBand ? "DXCC_BAND" : (isMixed ? "DXCC" : "DXCC_MODE"); // ── slot_credits CTE ─────────────────────────────────────────────────── // // Key fix: credit status is aggregated across ALL QSOs for the slot, not // just the single "best display" QSO selected by ROW_NUMBER(). // // Example failure case without this: a newer LoTW-matched Alaska-10m QSO // is ranked #1 for display but has no credit_granted yet; an older QSO // that was actually submitted already has credit_granted=DXCC_BAND. // Without slot_credits the Alaska-10m slot would wrongly appear as // "not yet submitted." // // slot_credits does NOT apply confFilter — credit_granted is authoritative // regardless of the current QSL confirmation state in QLog. // For entity-level mode awards it DOES apply modeGroupFilter so we don't // mistake a Phone credit for a CW-mode check. // For per-band it does NOT apply modeGroupFilter (any mode earns DXCC_BAND). const QString slotGroupBy = perBand ? "c.dxcc, c.band" : "c.dxcc"; const QString slotModeFilter = (perBand || isMixed) ? "" : "AND (" + modeGroupFilter + ") "; const QString slotCreditsCTE = "slot_credits AS ( " " SELECT " + slotGroupBy + ", " " MAX(CASE WHEN " + creditHas("c.credit_submitted", creditToken) + " THEN 1 ELSE 0 END) AS slot_submitted, " " MAX(CASE WHEN " + creditHas("c.credit_granted", creditToken) + " THEN 1 ELSE 0 END) AS slot_granted " " FROM contacts c " " INNER JOIN modes m ON c.mode = m.name " " WHERE c.my_dxcc = '" + myEntity + "' " " AND c.dxcc IS NOT NULL " " " + slotModeFilter + " " + bandWhereClause + " " " " + userFilter + " " " GROUP BY " + slotGroupBy + " " ") "; // ── ranked CTE ──────────────────────────────────────────────────────── // Picks the single best confirmed QSO per slot for display purposes. // Priority: LoTW confirmed > Paper confirmed > most recent. // Mode and confirmation filters are applied so we show the most relevant // contact matching the user's selection. const QString partitionClause = perBand ? "PARTITION BY c.dxcc, c.band" : "PARTITION BY c.dxcc"; const QString rankedCTE = "ranked AS ( " " SELECT " " c.id, c.callsign, c.band, c.mode, c.start_time, c.dxcc, " " c.lotw_qsl_rcvd, c.qsl_rcvd, " " ROW_NUMBER() OVER ( " " " + partitionClause + " " " ORDER BY " " CASE WHEN c.lotw_qsl_rcvd = 'Y' THEN 0 ELSE 1 END, " " CASE WHEN c.qsl_rcvd = 'Y' THEN 0 ELSE 1 END, " " c.start_time DESC " " ) AS rn " " FROM contacts c " " INNER JOIN modes m ON c.mode = m.name " " WHERE c.my_dxcc = '" + myEntity + "' " " AND c.dxcc IS NOT NULL " " AND (" + confFilter + ") " " AND (" + modeGroupFilter + ") " " " + bandWhereClause + " " " " + userFilter + " " ") "; // ── JOIN condition between ranked best-QSO and slot_credits ─────────── const QString slotJoinOn = perBand ? "sc.dxcc = bc.dxcc AND sc.band = bc.band" : "sc.dxcc = bc.dxcc"; // ── Status filter (uses aggregated slot_credits columns) ─────────────── QStringList statusConds; if (ui->showUnsubmittedCheckBox->isChecked()) statusConds << "(sc.slot_submitted = 0 AND sc.slot_granted = 0)"; if (ui->showSubmittedCheckBox->isChecked()) statusConds << "(sc.slot_submitted = 1 AND sc.slot_granted = 0)"; if (ui->showGrantedCheckBox->isChecked()) statusConds << "(sc.slot_granted = 1)"; if ( statusConds.isEmpty() ) { clearTable(); updateStatusLabel(0, selectedBands, scope); return; } const QString statusFilter = "(" + statusConds.join(" OR ") + ")"; // ── Final SQL ────────────────────────────────────────────────────────── // Column 0 is the contact id — hidden in the table view but used by // exportAsADIF() to fetch full contact records for the export. const QString sql = "WITH " + slotCreditsCTE + ", " + rankedCTE + "SELECT " " bc.id, " // col 0 (hidden) " translate_to_locale(e.name) AS '" + tr("Entity") + "', " // col 1 " e.prefix AS '" + tr("Prefix") + "', " // col 2 " bc.callsign AS '" + tr("Callsign") + "', " // col 3 " bc.band AS '" + tr("Band") + "', " // col 4 " bc.mode AS '" + tr("Mode") + "', " // col 5 " strftime('%Y-%m-%d', bc.start_time) AS '" + tr("Date") + "', " // col 6 " CASE WHEN bc.lotw_qsl_rcvd = 'Y' THEN 'Y' ELSE '' END AS '" + tr("LoTW") + "', " // col 7 " CASE WHEN bc.qsl_rcvd = 'Y' THEN 'Y' ELSE '' END AS '" + tr("Paper") + "', " // col 8 " CASE WHEN sc.slot_submitted = 1 THEN 'Y' ELSE '' END AS '" + tr("Submitted") + "', " // col 9 " CASE WHEN sc.slot_granted = 1 THEN 'Y' ELSE '' END AS '" + tr("Granted") + "' " // col 10 "FROM ranked bc " "INNER JOIN dxcc_entities_clublog e ON bc.dxcc = e.id " "INNER JOIN slot_credits sc ON " + slotJoinOn + " " "WHERE bc.rn = 1 " " AND " + statusFilter + " " "ORDER BY e.name COLLATE LOCALEAWARE ASC, bc.band ASC "; qCDebug(runtime) << "DXCC Submission SQL:" << sql; tableModel->setQuery(sql); if ( tableModel->lastError().isValid() ) qCWarning(runtime) << "DXCC Submission query error:" << tableModel->lastError().text(); while (tableModel->canFetchMore()) tableModel->fetchMore(); // Column 0 carries the contact id for export — keep it hidden from the user ui->submissionTableView->setColumnHidden(0, true); const int count = tableModel->rowCount(); ui->exportADIFButton->setEnabled(count > 0); updateStatusLabel(count, selectedBands, scope); } void DXCCSubmissionDialog::exportAsADIF() { FCT_IDENTIFICATION; // Collect contact IDs from hidden column 0 of the current result set QStringList ids; for ( int row = 0; row < tableModel->rowCount(); ++row ) { const QString id = tableModel->data(tableModel->index(row, 0)).toString(); if ( !id.isEmpty() ) ids << id; } if ( ids.isEmpty() ) { QMessageBox::information(this, tr("Export ADIF"), tr("No contacts to export.")); return; } // Fetch full contact records so ExportDialog has all ADIF fields available QSqlQuery query; if (!query.exec(QString("SELECT * FROM contacts WHERE id IN (%1) " "ORDER BY start_time ASC").arg(ids.join(",")))) { qCWarning(runtime) << "DXCC export fetch error:" << query.lastError().text(); QMessageBox::critical(this, tr("Export ADIF"), tr("Failed to retrieve contact records.")); return; } QList records; while ( query.next() ) records << query.record(); if ( records.isEmpty() ) return; ExportDialog dialog(records, this); dialog.setWindowTitle(tr("Export DXCC Submission List as ADIF")); dialog.exec(); } const QString DXCCSubmissionDialog::getSelectedEntity() const { FCT_IDENTIFICATION; const int row = ui->myEntityComboBox->currentIndex(); const QModelIndex idx = ui->myEntityComboBox->model()->index(row, 0); return ui->myEntityComboBox->model()->data(idx).toString(); } QStringList DXCCSubmissionDialog::getSelectedBands(DXCCBandScope scope) const { FCT_IDENTIFICATION; switch (scope) { case DXCCBandScope::FiveBand: return FIVE_BAND_DXCC; case DXCCBandScope::AllDXCCBands: { QStringList all; all.reserve(dxccBands.size()); for (const Band &b : dxccBands) all << b.name; return all; } case DXCCBandScope::Custom: { QStringList selected; for ( const QCheckBox *cb : static_cast&>(bandCheckBoxes) ) if (cb->isChecked()) selected << cb->text(); return selected; } default: return {}; } } QString DXCCSubmissionDialog::buildModeGroupFilter() const { FCT_IDENTIFICATION; if (ui->cwRadioButton->isChecked()) return "m.dxcc = 'CW'"; if (ui->phoneRadioButton->isChecked()) return "m.dxcc = 'PHONE'"; if (ui->digitalRadioButton->isChecked()) return "m.dxcc = 'DIGITAL'"; return "m.dxcc IN ('CW', 'PHONE', 'DIGITAL')"; // Mixed } void DXCCSubmissionDialog::selectBandPreset(const QStringList &bands) { FCT_IDENTIFICATION; for ( QCheckBox *cb : static_cast&>(bandCheckBoxes) ) { cb->blockSignals(true); cb->setChecked(bands.contains(cb->text(), Qt::CaseInsensitive)); cb->blockSignals(false); } } void DXCCSubmissionDialog::updateStatusLabel(int count, const QStringList &selectedBands, DXCCBandScope scope) { FCT_IDENTIFICATION; const bool perBand = ( scope != DXCCBandScope::EntityLevel) ; QString modeStr; if ( ui->cwRadioButton->isChecked() ) modeStr = tr("CW"); else if ( ui->phoneRadioButton->isChecked() ) modeStr = tr("Phone"); else if ( ui->digitalRadioButton->isChecked() ) modeStr = tr("Digital"); else modeStr = tr("Mixed"); QString scopeStr; switch ( scope ) { case DXCCBandScope::EntityLevel: scopeStr = tr("any band"); break; case DXCCBandScope::FiveBand: scopeStr = tr("5-band"); break; case DXCCBandScope::AllDXCCBands: scopeStr = tr("all bands"); break; case DXCCBandScope::Custom: scopeStr = tr("%1 selected band(s)").arg(selectedBands.size()); break; } if ( count == 0 ) ui->statusLabel->setText(tr("No contacts match the selected criteria.")); else ui->statusLabel->setText( tr("%1 %2 %3 — DXCC %4 / %5") .arg(count) .arg(perBand ? tr("band-slot") : tr("entity")) .arg(count == 1 ? tr("entry") : tr("entries")) .arg(modeStr) .arg(scopeStr)); } void DXCCSubmissionDialog::setBandControlsVisible(bool visible) { FCT_IDENTIFICATION; for (int i = 0; i < ui->bandGroup->count(); ++i) { QLayoutItem *item = ui->bandGroup->itemAt(i); if ( QWidget *w = item->widget() ) w->setVisible(visible); } ui->bandsLabel->setVisible(visible); ui->fiveBandButton->setVisible(visible); ui->allBandsButton->setVisible(visible); } DXCCSubmissionDialog::DXCCBandScope DXCCSubmissionDialog::currentScope() const { FCT_IDENTIFICATION; return static_cast(ui->bandScopeComboBox->currentData().toInt()); } void DXCCSubmissionDialog::clearTable() { tableModel->setQuery(QString()); } ================================================ FILE: ui/DXCCSubmissionDialog.h ================================================ #ifndef QLOG_UI_DXCCSUBMISSIONDIALOG_H #define QLOG_UI_DXCCSUBMISSIONDIALOG_H #include #include #include #include #include #include "models/SqlListModel.h" #include "data/Band.h" namespace Ui { class DXCCSubmissionDialog; } class DXCCSubmissionDialog : public QDialog { Q_OBJECT public: explicit DXCCSubmissionDialog(QWidget *parent = nullptr); ~DXCCSubmissionDialog(); // DXCC band scope presets enum class DXCCBandScope { EntityLevel, // Any band — one entry per entity (basic DXCC) FiveBand, // 80/40/20/15/10m preset AllDXCCBands, // All enabled DXCC bands, per band Custom // User-selected bands }; public slots: void refreshTable(); void onBandScopeChanged(int index); void onFiveBandClicked(); void onAllBandsClicked(); void exportAsADIF(); private: Ui::DXCCSubmissionDialog *ui; QSqlQueryModel *tableModel; SqlListModel *entityCallsignModel; // Band checkboxes added dynamically QList bandCheckBoxes; QList dxccBands; const quint8 MAXCOLUMNS = 14; // Helpers const QString getSelectedEntity() const; QStringList getSelectedBands(DXCCBandScope scope) const; QString buildModeGroupFilter() const; void selectBandPreset(const QStringList &bands); void updateStatusLabel(int count, const QStringList &selectedBands, DXCCBandScope scope); void setBandControlsVisible(bool visible); DXCCBandScope currentScope() const; void clearTable(); const QStringList FIVE_BAND_DXCC = { "80m", "40m", "20m", "15m", "10m" }; }; #endif // QLOG_UI_DXCCSUBMISSIONDIALOG_H ================================================ FILE: ui/DXCCSubmissionDialog.ui ================================================ DXCCSubmissionDialog 0 0 803 617 DXCC Submission List Options 4 3 6 2 6 4 My DXCC Entity 0 0 Qt::Horizontal QSizePolicy::Maximum 30 20 User Filter Qt::Horizontal QSizePolicy::Maximum 10 20 0 0 Qt::Horizontal 40 20 Category Mixed true buttonGroup CW buttonGroup Phone buttonGroup Digital buttonGroup Qt::Horizontal 40 20 Confirmed by 25 LoTW true Paper true Qt::Horizontal 40 20 Show 25 Not Yet Submitted true Submitted (Not Granted) true Already Granted false Qt::Horizontal 40 20 Band Scope 0 0 Qt::Horizontal QSizePolicy::Maximum 10 20 Select 5-Band DXCC preset bands (80/40/20/15/10m) 5-Band DXCC Qt::Horizontal QSizePolicy::Maximum 10 10 Select all DXCC-eligible bands All DXCC Bands Qt::Horizontal 40 20 Bands Select options above and the list will update automatically. Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter Qt::ClickFocus QAbstractItemView::NoEditTriggers true QAbstractItemView::NoSelection QAbstractItemView::ScrollPerPixel QAbstractItemView::ScrollPerPixel true true false 20 20 false Qt::Horizontal 40 20 Export the contacts listed above to an ADIF file Export .. Qt::Horizontal 40 20 0 0 Qt::Horizontal QDialogButtonBox::Close buttonBox rejected() DXCCSubmissionDialog reject() 259 738 157 274 myEntityComboBox currentIndexChanged(int) DXCCSubmissionDialog refreshTable() 177 50 549 374 userFilterComboBox currentIndexChanged(int) DXCCSubmissionDialog refreshTable() 402 50 549 374 bandScopeComboBox currentIndexChanged(int) DXCCSubmissionDialog onBandScopeChanged(int) 177 176 549 374 lotwCheckBox stateChanged(int) DXCCSubmissionDialog refreshTable() 168 113 549 374 paperCheckBox stateChanged(int) DXCCSubmissionDialog refreshTable() 241 113 549 374 showUnsubmittedCheckBox stateChanged(int) DXCCSubmissionDialog refreshTable() 214 144 549 374 showSubmittedCheckBox stateChanged(int) DXCCSubmissionDialog refreshTable() 401 144 549 374 showGrantedCheckBox stateChanged(int) DXCCSubmissionDialog refreshTable() 580 144 549 374 fiveBandButton clicked() DXCCSubmissionDialog onFiveBandClicked() 294 176 549 374 allBandsButton clicked() DXCCSubmissionDialog onAllBandsClicked() 426 176 549 374 exportADIFButton clicked() DXCCSubmissionDialog exportAsADIF() 76 725 549 374 refreshTable() onBandScopeChanged(int) onFiveBandClicked() onAllBandsClicked() exportAsADIF() ================================================ FILE: ui/DevToolsDialog.cpp ================================================ #include "DevToolsDialog.h" #include "ui_DevToolsDialog.h" #include "ui/component/SqlHighlighter.h" #include "ui/ExportDialog.h" #include "core/LogDatabase.h" #include "core/debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include MODULE_IDENTIFICATION("qlog.ui.devtoolsdialog"); const QString DevToolsDialog::READ_ONLY_CONNECTION("DevToolsDialog_readonly"); // --------------------------------------------------------------------------- // Construction / destruction // --------------------------------------------------------------------------- DevToolsDialog::DevToolsDialog(QWidget *parent) : QDialog(parent), ui(new Ui::DevToolsDialog), highlighter(nullptr), queryModel(new QSqlQueryModel(this)), sortProxy(new QSortFilterProxyModel(this)) { FCT_IDENTIFICATION; ui->setupUi(this); // Open a separate read-only connection to the database. // SQLite itself enforces read-only access - no keyword blocklist needed. { QSqlDatabase roDb = QSqlDatabase::addDatabase("QSQLITE", READ_ONLY_CONNECTION); roDb.setDatabaseName(LogDatabase::dbFilename()); roDb.setConnectOptions("QSQLITE_OPEN_READONLY;QSQLITE_ENABLE_REGEXP"); if ( !roDb.open() ) qCWarning(runtime) << "Cannot open read-only DB connection:" << roDb.lastError().text(); } // Restore geometry & splitter state QSettings settings; restoreGeometry(settings.value("devtools/geometry").toByteArray()); ui->splitter->restoreState(settings.value("devtools/splitter").toByteArray()); if ( ui->splitter->sizes().value(0, 0) == 0 ) { // First run - default split ui->splitter->setSizes({250, 450}); } // Monospace font for the editor QFont editorFont = QFontDatabase::systemFont(QFontDatabase::FixedFont); editorFont.setPointSize(10); ui->sqlEditor->setFont(editorFont); // Syntax highlighter highlighter = new SqlHighlighter(ui->sqlEditor->document()); // Results model + sortable proxy sortProxy->setSourceModel(queryModel); ui->resultsTable->setModel(sortProxy); ui->resultsTable->horizontalHeader()->setStretchLastSection(true); ui->resultsTable->verticalHeader()->setVisible(false); // Export drop-down menu QMenu *exportMenu = new QMenu(this); connect(exportMenu->addAction(tr("TXT")), &QAction::triggered, this, &DevToolsDialog::exportAsTxt); connect(exportMenu->addAction(tr("CSV")), &QAction::triggered, this, &DevToolsDialog::exportAsCsv); connect(exportMenu->addAction(tr("ADI")), &QAction::triggered, this, &DevToolsDialog::exportAsAdif); ui->exportButton->setMenu(exportMenu); ui->sqlEditor->installEventFilter(this); // Load db schema into highlighter loadSchema(); // Debug log controls ui->logToFileCheckBox->setChecked(isLogToFileEnabled()); updateDebugLogFileLabel(); } DevToolsDialog::~DevToolsDialog() { QSettings settings; settings.setValue("devtools/geometry", saveGeometry()); settings.setValue("devtools/splitter", ui->splitter->saveState()); delete ui; // Must be removed after all QSql* objects using it are destroyed QSqlDatabase::removeDatabase(READ_ONLY_CONNECTION); } // --------------------------------------------------------------------------- // Schema loading // --------------------------------------------------------------------------- void DevToolsDialog::loadSchema() { FCT_IDENTIFICATION; QStringList schemaIds; QSqlDatabase roDb = QSqlDatabase::database(READ_ONLY_CONNECTION); QSqlQuery q(roDb); if ( q.exec("SELECT name FROM sqlite_master WHERE type IN ('table','view') ORDER BY name") ) { while ( q.next() ) { const QString tableName = q.value(0).toString(); schemaIds.append(tableName); QSqlQuery colQ(roDb); if ( colQ.exec(QString("PRAGMA table_info(\"%1\")").arg(tableName)) ) { while ( colQ.next() ) { const QString col = colQ.value(1).toString(); if ( !schemaIds.contains(col, Qt::CaseInsensitive) ) schemaIds.append(col); } } else { qCWarning(runtime) << "PRAGMA table_info failed for" << tableName << colQ.lastError().text(); } } } else { qCWarning(runtime) << "Failed to query sqlite_master:" << q.lastError().text(); } // Highlight schema names (tables, columns) in a distinct color highlighter->setUserIdentifiers(schemaIds); } // --------------------------------------------------------------------------- // Event filter - keyboard shortcuts // --------------------------------------------------------------------------- bool DevToolsDialog::eventFilter(QObject *obj, QEvent *event) { FCT_IDENTIFICATION; if ( obj != ui->sqlEditor || event->type() != QEvent::KeyPress ) return QDialog::eventFilter(obj, event); QKeyEvent *ke = static_cast(event); // Ctrl+Return - run query if ( ke->key() == Qt::Key_Return && ( ke->modifiers() & Qt::ControlModifier ) ) { runQuery(); return true; } return false; } // --------------------------------------------------------------------------- // File operations // --------------------------------------------------------------------------- void DevToolsDialog::openQuery() { FCT_IDENTIFICATION; QSettings settings; const QString lastDir = settings.value("devtools/lastDir", QDir::homePath()).toString(); const QString filename = QFileDialog::getOpenFileName( this, tr("Open SQL Query"), lastDir, tr("SQL Files (*.sql);;All Files (*)")); if ( filename.isEmpty() ) return; QFile file(filename); if ( !file.open(QIODevice::ReadOnly | QIODevice::Text) ) { QMessageBox::warning(this, tr("Open Error"), tr("Cannot open file:\n%1").arg(file.errorString())); return; } QTextStream in(&file); ui->sqlEditor->setPlainText(in.readAll()); settings.setValue("devtools/lastDir", QFileInfo(filename).absolutePath()); } void DevToolsDialog::saveQuery() { FCT_IDENTIFICATION; QSettings settings; const QString lastDir = settings.value("devtools/lastDir", QDir::homePath()).toString(); QString filename = QFileDialog::getSaveFileName( this, tr("Save SQL Query"), lastDir, tr("SQL Files (*.sql);;All Files (*)")); if ( filename.isEmpty() ) return; if ( !filename.endsWith(".sql", Qt::CaseInsensitive) ) filename += ".sql"; QFile file(filename); if ( !file.open(QIODevice::WriteOnly | QIODevice::Text) ) { QMessageBox::warning(this, tr("Save Error"), tr("Cannot save file:\n%1").arg(file.errorString())); return; } QTextStream out(&file); out << ui->sqlEditor->toPlainText(); settings.setValue("devtools/lastDir", QFileInfo(filename).absolutePath()); ui->statusLabel->setText(tr("Query saved to %1").arg(QFileInfo(filename).fileName())); } // --------------------------------------------------------------------------- // Run query // --------------------------------------------------------------------------- void DevToolsDialog::runQuery() { FCT_IDENTIFICATION; const QString sql = ui->sqlEditor->toPlainText().trimmed(); if ( sql.isEmpty() ) return; QElapsedTimer timer; timer.start(); // Execute via the read-only connection; SQLite rejects any write attempt queryModel->setQuery(sql, QSqlDatabase::database(READ_ONLY_CONNECTION)); if ( queryModel->lastError().isValid() ) { ui->statusLabel->setText( tr("Error: %1").arg(queryModel->lastError().text())); ui->statusLabel->setStyleSheet("QLabel { color : red; }"); return; } ui->statusLabel->setStyleSheet(""); // Fetch rows in batches; cap at a reasonable limit to avoid blocking UI while ( queryModel->canFetchMore() && queryModel->rowCount() < MAX_FETCH_ROWS ) queryModel->fetchMore(); const qint64 elapsed = timer.elapsed(); const int rows = queryModel->rowCount(); const bool truncated = queryModel->canFetchMore(); ui->resultsTable->resizeColumnsToContents(); if ( truncated ) ui->statusLabel->setText( tr("%1 row(s) shown (truncated at %2) in %3 ms") .arg(rows).arg(MAX_FETCH_ROWS).arg(elapsed)); else ui->statusLabel->setText( tr("%1 row(s) returned in %2 ms").arg(rows).arg(elapsed)); } // --------------------------------------------------------------------------- // Export helpers // --------------------------------------------------------------------------- static bool openFileForWrite(QWidget *parent, const QString &caption, const QString &filter, const QString &defaultSuffix, QString &outPath) { QSettings settings; const QString lastDir = settings.value("devtools/lastDir", QDir::homePath()).toString(); QString path = QFileDialog::getSaveFileName(parent, caption, lastDir, filter); if ( path.isEmpty() ) return false; if ( !defaultSuffix.isEmpty() && !path.endsWith('.' + defaultSuffix, Qt::CaseInsensitive) ) path += '.' + defaultSuffix; settings.setValue("devtools/lastDir", QFileInfo(path).absolutePath()); outPath = path; return true; } void DevToolsDialog::exportModel( const QString &title, const QString &filter, const QString &defaultExt, const QString &separator, std::function formatter) { FCT_IDENTIFICATION; if ( queryModel->rowCount() == 0 ) { QMessageBox::information(this, tr("Export"), tr("No results to export.")); return; } QString filename; if ( !openFileForWrite(this, title, filter, defaultExt, filename) ) return; QFile file(filename); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { QMessageBox::warning(this, tr("Export Error"), tr("Cannot open file for writing:\n%1") .arg(file.errorString())); return; } QTextStream out(&file); const int rows = queryModel->rowCount(); const int cols = queryModel->columnCount(); std::function)> writeRow = [&](std::function dataProvider) { QStringList row; row.reserve(cols); for (int c = 0; c < cols; ++c) row << formatter(dataProvider(c)); out << row.join(separator) << "\n"; }; // header writeRow([&](int c) { return queryModel->headerData(c, Qt::Horizontal).toString(); }); // data for ( int r = 0; r < rows; ++r ) { writeRow([&](int c) { return queryModel->data(queryModel->index(r, c)).toString(); }); } ui->statusLabel->setText(tr("Exported %1 row(s) to %2").arg(rows) .arg(QFileInfo(filename).fileName())); } void DevToolsDialog::exportAsTxt() { FCT_IDENTIFICATION; exportModel(tr("TXT"), tr("Text Files (*.txt);;All Files (*)"), "txt", "\t", [](const QString &s) { return s; }); } void DevToolsDialog::exportAsCsv() { FCT_IDENTIFICATION; auto csvEscape = [](const QString &s) -> QString { if (s.contains(',') || s.contains('"') || s.contains('\n') || s.contains('\r')) return '"' + QString(s).replace('"', "\"\"") + '"'; return s; }; exportModel(tr("CSV"), tr("CSV Files (*.csv);;All Files (*)"), "csv", ",", csvEscape); } void DevToolsDialog::exportAsAdif() { FCT_IDENTIFICATION; if ( queryModel->rowCount() == 0 ) { QMessageBox::information(this, tr("Export"), tr("No results to export.")); return; } // Locate an 'id' column in the result set int idCol = -1; for ( int c = 0; c < queryModel->columnCount(); ++c ) { if ( queryModel->headerData(c, Qt::Horizontal) .toString().compare("id", Qt::CaseInsensitive) == 0 ) { idCol = c; break; } } if ( idCol < 0 ) { QMessageBox::warning(this, tr("ADIF Export"), tr("ADIF export requires the query to include the contacts 'id' column.\n\n" "Example:\n" " SELECT id, callsign FROM contacts WHERE ...")); return; } // Collect all contact IDs from the result (validated as numbers) QList ids; for ( int r = 0; r < queryModel->rowCount(); ++r ) { const QVariant id = queryModel->data(queryModel->index(r, idCol)); bool ok = false; const qlonglong numId = id.toLongLong(&ok); if ( ok ) ids << numId; } if ( ids.isEmpty() ) { QMessageBox::information(this, tr("ADIF Export"), tr("No valid contact IDs found in the result set.")); return; } // Build parameterized placeholders for the ID list QStringList placeholders; for ( int i = 0; i < ids.size(); ++i ) placeholders << "?"; QSqlQuery fetchQ; if ( ! fetchQ.prepare(QString("SELECT * FROM contacts WHERE id IN (%1) ORDER BY start_time ASC").arg(placeholders.join(',')))) { qWarning() << "Cannot prepare select statement for contacts table"; return; } for ( int i = 0; i < ids.size(); ++i ) fetchQ.addBindValue(ids.at(i)); if ( !fetchQ.exec() ) { QMessageBox::warning(this, tr("ADIF Export"), tr("Failed to retrieve contact records:\n%1") .arg(fetchQ.lastError().text())); return; } QList records; while ( fetchQ.next() ) records.append(fetchQ.record()); if ( records.isEmpty() ) { QMessageBox::information(this, tr("ADIF Export"), tr("No matching contacts found in the database.")); return; } // Hand off to the standard ExportDialog (same path as LogbookWidget right-click) ExportDialog dialog(records, this); dialog.exec(); } // --------------------------------------------------------------------------- // Debug log controls // --------------------------------------------------------------------------- void DevToolsDialog::logToFileToggled(bool checked) { FCT_IDENTIFICATION; qCDebug(function_parameters) << checked; if ( !checked ) { // Close the current log file so that re-enabling creates a new one setLogToFile(false); closeDebugLogFile(); } else { setLogToFile(true); // Force a log message so that the new log file gets created immediately qWarning() << "Debug file logging enabled by user"; } updateDebugLogFileLabel(); } void DevToolsDialog::applyLoggingRules() { FCT_IDENTIFICATION; const QString userRules = ui->loggingRulesEdit->text().trimmed(); if ( userRules.isEmpty() ) { // Empty rules - back to production defaults set_debug_level(LEVEL_PRODUCTION); ui->statusLabel->setText(tr("Logging rules cleared (defaults)")); } else { // Disable all debug first, then apply user rules on top const QString fullRules = "*.debug=false\n" + userRules; QLoggingCategory::setFilterRules(fullRules); ui->statusLabel->setText(tr("Logging rules applied")); } } void DevToolsDialog::saveDebugLog() { FCT_IDENTIFICATION; const QString logFilename = currentDebugLogFilename(); if ( logFilename.isEmpty() || !QFile::exists(logFilename) ) { QMessageBox::information(this, tr("Save Debug Log"), tr("No debug log file is currently being written")); return; } QSettings settings; const QString lastDir = settings.value("devtools/lastDir", QDir::homePath()).toString(); QString destFilename = QFileDialog::getSaveFileName( this, tr("Save Debug Log"), lastDir + "/" + QFileInfo(logFilename).fileName(), tr("Log Files (*.log);;All Files (*)")); if ( destFilename.isEmpty() ) return; if ( !destFilename.endsWith(".log", Qt::CaseInsensitive) ) destFilename += ".log"; if ( QFile::exists(destFilename) ) QFile::remove(destFilename); if ( QFile::copy(logFilename, destFilename) ) { settings.setValue("devtools/lastDir", QFileInfo(destFilename).absolutePath()); ui->statusLabel->setText( tr("Debug log saved to %1").arg(QFileInfo(destFilename).fileName())); } else { QMessageBox::warning(this, tr("Save Debug Log"), tr("Failed to copy the debug log file.")); } } void DevToolsDialog::updateDebugLogFileLabel() { FCT_IDENTIFICATION; const QString logFilename = currentDebugLogFilename(); if ( logFilename.isEmpty() || !isLogToFileEnabled() ) ui->debugLogFileLabel->setText(tr("File logging is disabled")); else ui->debugLogFileLabel->setText(tr("Log file: %1").arg(logFilename)); ui->saveDebugLogButton->setEnabled( !logFilename.isEmpty() && QFile::exists(logFilename)); } ================================================ FILE: ui/DevToolsDialog.h ================================================ #ifndef QLOG_UI_DEVTOOLSDIALOG_H #define QLOG_UI_DEVTOOLSDIALOG_H #include #include #include namespace Ui { class DevToolsDialog; } class SqlHighlighter; class DevToolsDialog : public QDialog { Q_OBJECT public: explicit DevToolsDialog(QWidget *parent = nullptr); ~DevToolsDialog(); protected: bool eventFilter(QObject *obj, QEvent *event) override; public slots: void openQuery(); void saveQuery(); void runQuery(); void exportAsTxt(); void exportAsCsv(); void exportAsAdif(); void logToFileToggled(bool checked); void applyLoggingRules(); void saveDebugLog(); private: static const QString READ_ONLY_CONNECTION; static const int MAX_FETCH_ROWS = 50000; Ui::DevToolsDialog *ui; SqlHighlighter *highlighter; QSqlQueryModel *queryModel; QSortFilterProxyModel *sortProxy; void loadSchema(); void updateDebugLogFileLabel(); void exportModel(const QString &title, const QString &filter, const QString &defaultExt, const QString &separator, std::function formatter); }; #endif // QLOG_UI_DEVTOOLSDIALOG_H ================================================ FILE: ui/DevToolsDialog.ui ================================================ DevToolsDialog 0 0 729 620 Developer Tools 0 0 Debug Log Logging Rules: e.g. qlog.ui.*.runtime=true true Apply Generate Debug File Export File .. Qt::Horizontal 40 20 SQL Console Open SQL Save SQL Run SQL Export As Qt::Vertical Enter SQL query here... Ctrl+Return = run Qt::Horizontal QDialogButtonBox::Close buttonBox rejected() DevToolsDialog reject() 316 260 286 274 openButton clicked() DevToolsDialog openQuery() 132 200 474 359 saveButton clicked() DevToolsDialog saveQuery() 360 200 474 359 runButton clicked() DevToolsDialog runQuery() 588 200 474 359 logToFileCheckBox toggled(bool) DevToolsDialog logToFileToggled(bool) 72 93 474 359 applyRulesButton clicked() DevToolsDialog applyLoggingRules() 887 57 474 359 saveDebugLogButton clicked() DevToolsDialog saveDebugLog() 182 93 474 359 openQuery() saveQuery() exportQuery() runQuery() logToFileToggled(bool) applyLoggingRules() saveDebugLog() ================================================ FILE: ui/DownloadQSLDialog.cpp ================================================ #include #include #include "DownloadQSLDialog.h" #include "qpushbutton.h" #include "ui_DownloadQSLDialog.h" #include "models/SqlListModel.h" #include "core/debug.h" #include "data/StationProfile.h" #include "service/lotw/Lotw.h" #include "service/eqsl/Eqsl.h" #include "ui/QSLImportStatDialog.h" #include "core/LogParam.h" MODULE_IDENTIFICATION("qlog.ui.downloadqsldialog"); DownloadQSLDialog::DownloadQSLDialog(QWidget *parent) : QDialog(parent), ui(new Ui::DownloadQSLDialog) { FCT_IDENTIFICATION; ui->setupUi(this); ui->lotwMyCallsignCombo->setModel(new SqlListModel("SELECT DISTINCT UPPER(station_callsign) " "FROM contacts ORDER BY station_callsign", "", ui->lotwMyCallsignCombo)); ui->lotwDateEdit->setDisplayFormat(locale.formatDateShortWithYYYY()); ui->eqslDateEdit->setDisplayFormat(locale.formatDateShortWithYYYY()); ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("&Download")); const StationProfile &profile = StationProfilesManager::instance()->getCurProfile1(); loadDialogState(); int index = ui->lotwMyCallsignCombo->findText(profile.callsign); if ( index >= 0 ) ui->lotwMyCallsignCombo->setCurrentIndex(index); else ui->lotwMyCallsignCombo->setCurrentText(LogParam::getDownloadQSLLoTWLastCall()); // Enable options based on the configuration if ( LotwBase::getUsername().isEmpty() ) { ui->lotwGroupBox->setChecked(false); ui->lotwGroupBox->setEnabled(false); ui->lotwGroupBox->setToolTip(tr("LoTW is not configured properly.

Please, use Settings dialog to configure it.

")); } if ( EQSLBase::getUsername().isEmpty() ) { ui->eqslGroupBox->setChecked(false); ui->eqslGroupBox->setEnabled(false); ui->eqslGroupBox->setToolTip(tr("eQSL is not configured properly.

Please, use Settings dialog to configure it.

")); } } DownloadQSLDialog::~DownloadQSLDialog() { FCT_IDENTIFICATION; delete ui; } void DownloadQSLDialog::startNextDownload() { FCT_IDENTIFICATION; if (!downloadQueue.isEmpty()) { auto next = downloadQueue.dequeue(); next(); // call Lambda for service; } else { QSLImportStatDialog statDialog(downloadStat); downloadStat.clear(); if ( statDialog.exec() == QDialog::Rejected ) done(QDialog::Accepted); } } void DownloadQSLDialog::loadDialogState() { FCT_IDENTIFICATION; /********/ /* LoTW */ /********/ ui->lotwGroupBox->setChecked(LogParam::getDownloadQSLServiceState("lotw")); ui->lotwDateEdit->setDate(LogParam::getDownloadQSLServiceLastDate("lotw")); ui->lotwDateTypeCombo->setCurrentIndex((LogParam::getDownloadQSLServiceLastQSOQSL("lotw")) ? 0 : 1); /********/ /* eQSL */ /********/ ui->eqslGroupBox->setChecked(LogParam::getDownloadQSLServiceState("eqsl")); ui->eqslDateEdit->setDate(LogParam::getDownloadQSLServiceLastDate("eqsl")); ui->eqslDateTypeCombo->setCurrentIndex((LogParam::getDownloadQSLServiceLastQSOQSL("eqsl")) ? 0 : 1); ui->eqslQTHProfileEdit->setText(LogParam::getDownloadQSLeQSLLastProfile()); } void DownloadQSLDialog::saveDialogState() { FCT_IDENTIFICATION; /********/ /* LoTW */ /********/ LogParam::setDownloadQSLServiceState("lotw", ui->lotwGroupBox->isChecked()); LogParam::setDownloadQSLServiceLastDate("lotw", QDateTime::currentDateTimeUtc().date()); LogParam::setDownloadQSLServiceLastQSOQSL("lotw", ui->lotwDateTypeCombo->currentIndex() == 0); /********/ /* eQSL */ /********/ LogParam::setDownloadQSLServiceState("eqsl", ui->eqslGroupBox->isChecked()); LogParam::setDownloadQSLServiceLastDate("eqsl", QDateTime::currentDateTimeUtc().date()); LogParam::setDownloadQSLServiceLastQSOQSL("eqsl", ui->eqslDateTypeCombo->currentIndex() == 0); LogParam::setDownloadQSLeQSLLastProfile(ui->eqslQTHProfileEdit->text()); } void DownloadQSLDialog::prepareDownload(GenericQSLDownloader *service, const QString &serviceName, bool qslSinceActive, const QString &settingString) { FCT_IDENTIFICATION; QProgressDialog* progressDialog = new QProgressDialog("", tr("Cancel"), 0, 0, this); progressDialog->setWindowModality(Qt::WindowModal); progressDialog->setRange(0, 0); progressDialog->setAttribute(Qt::WA_DeleteOnClose, true); progressDialog->show(); progressDialog->setLabelText(tr("Downloading from %1").arg(serviceName)); connect(service, &GenericQSLDownloader::receiveQSLProgress, progressDialog, &QProgressDialog::setValue); connect(service, &GenericQSLDownloader::receiveQSLStarted, this, [progressDialog, serviceName] { progressDialog->setLabelText(tr("Processing %1 QSLs").arg(serviceName)); progressDialog->setRange(0, 100); }); connect(service, &GenericQSLDownloader::receiveQSLComplete, this, [service, progressDialog, serviceName, qslSinceActive, settingString, this](QSLMergeStat stats) { qCDebug(runtime) << "Service:" << serviceName; qCDebug(runtime) << "New QSLs: " << stats.newQSLs; qCDebug(runtime) << "Unmatched QSLs: " << stats.unmatchedQSLs; if ( qslSinceActive ) LogParam::setDownloadQSLServiceLastDate(settingString, QDateTime::currentDateTimeUtc().date()); progressDialog->done(QDialog::Accepted); downloadStat[serviceName] = stats; service->deleteLater(); startNextDownload(); }); connect(service, &GenericQSLDownloader::receiveQSLFailed, this, [this, service, progressDialog, serviceName](const QString &error) { progressDialog->done(QDialog::Accepted); QMessageBox::critical(this, tr("QLog Error"), tr("%1 update failed: ").arg(serviceName) + error); service->deleteLater(); startNextDownload(); }); connect(progressDialog, &QProgressDialog::canceled, this, [this, service]() { qCDebug(runtime)<< "Operation canceled"; service->abortDownload(); service->deleteLater(); downloadQueue.clear(); }); } void DownloadQSLDialog::downloadQSLs() { FCT_IDENTIFICATION; saveDialogState(); downloadQueue.clear(); if ( ui->eqslGroupBox->isChecked() ) downloadQueue.enqueue([=]() { EQSLQSLDownloader* eqsl = new EQSLQSLDownloader(this); bool qslSinceActive = ui->eqslDateTypeCombo->currentIndex() == 0; prepareDownload(eqsl, "eQSL", qslSinceActive, "eqsl"); LogParam::setDownloadQSLeQSLLastProfile(ui->eqslQTHProfileEdit->text()); LogParam::setDownloadQSLServiceLastQSOQSL("eqsl", qslSinceActive); eqsl->receiveQSL(ui->eqslDateEdit->date(), !qslSinceActive, ui->eqslQTHProfileEdit->text()); }); if ( ui->lotwGroupBox->isChecked() ) downloadQueue.enqueue([=]() { LotwQSLDownloader* lotw = new LotwQSLDownloader(this); bool qslSinceActive = ui->lotwDateTypeCombo->currentIndex() == 0; prepareDownload(lotw, "LoTW", qslSinceActive, "lotw"); LogParam::setDownloadQSLLoTWLastCall(ui->lotwMyCallsignCombo->currentText()); LogParam::setDownloadQSLServiceLastQSOQSL("lotw", qslSinceActive); lotw->receiveQSL(ui->lotwDateEdit->date(), !qslSinceActive, ui->lotwMyCallsignCombo->currentText()); }); if ( downloadQueue.isEmpty() ) { QMessageBox::information(this, tr("QLog Information"), tr("No service selected")); return; } // Download Execution startNextDownload(); } ================================================ FILE: ui/DownloadQSLDialog.h ================================================ #ifndef DOWNLOADQSLDIALOG_H #define DOWNLOADQSLDIALOG_H #include #include "core/LogLocale.h" #include "logformat/LogFormat.h" #include "service/GenericQSLDownloader.h" namespace Ui { class DownloadQSLDialog; } class DownloadQSLDialog : public QDialog { Q_OBJECT public: explicit DownloadQSLDialog(QWidget *parent = nullptr); ~DownloadQSLDialog(); private: void prepareDownload(GenericQSLDownloader* service, const QString &serviceName, bool qslSinceActive, const QString &settingString); void startNextDownload(); void loadDialogState(); void saveDialogState(); Ui::DownloadQSLDialog *ui; LogLocale locale; QHash downloadStat; QQueue> downloadQueue; private slots: void downloadQSLs(); }; #endif // DOWNLOADQSLDIALOG_H ================================================ FILE: ui/DownloadQSLDialog.ui ================================================ DownloadQSLDialog Download QSLs true eQSL true eQSL QTH Profile 200 0 QSLs Since QSOs Since 0 0 LoTW true My Callsign QSLs Since QSOs Since 0 0 Qt::Horizontal QDialogButtonBox::Close|QDialogButtonBox::Ok eqslGroupBox eqslDateTypeCombo eqslDateEdit eqslQTHProfileEdit lotwGroupBox lotwDateTypeCombo lotwDateEdit lotwMyCallsignCombo buttonBox accepted() DownloadQSLDialog downloadQSLs() 248 254 157 274 buttonBox rejected() DownloadQSLDialog reject() 316 260 286 274 downloadQSLs() ================================================ FILE: ui/DxFilterDialog.cpp ================================================ #include #include #include #include #include #include "DxFilterDialog.h" #include "ui_DxFilterDialog.h" #include "core/debug.h" #include "data/Dxcc.h" #include "core/MembershipQE.h" #include "DxWidget.h" #include "core/LogParam.h" #include "data/BandPlan.h" MODULE_IDENTIFICATION("qlog.ui.dxfilterdialog"); DxFilterDialog::DxFilterDialog(QWidget *parent) : QDialog(parent), ui(new Ui::DxFilterDialog) { FCT_IDENTIFICATION; ui->setupUi(this); const QList &bands = BandPlan::bandsList(false, true); const QStringList &excludedBandFilter = LogParam::getDXCExcludedBands(); /********************/ /* Bands Checkboxes */ /********************/ int i = 0; for ( const Band &enabledBand : bands ) { const QString &bandName = enabledBand.name; QCheckBox *bandCheckbox = new QCheckBox(ui->band_group->parentWidget()); bandCheckbox->setText(bandName); bandCheckbox->setProperty("bandName", bandName); // just to be sure that Bandmap is not translated bandCheckbox->setObjectName("bandCheckBox_" + bandName); bandCheckbox->setChecked(!excludedBandFilter.contains(bandName)); int row = i / MAXCOLUMNS; int column = i % MAXCOLUMNS; ui->band_group->addWidget(bandCheckbox, row, column); i++; } /*********************/ /* Status Checkboxes */ /*********************/ uint statusSetting = LogParam::getDXCFilterDxccStatus(); ui->newEntitycheckbox->setChecked(statusSetting & DxccStatus::NewEntity); ui->newBandcheckbox->setChecked(statusSetting & DxccStatus::NewBand); ui->newModecheckbox->setChecked(statusSetting & DxccStatus::NewMode); ui->newSlotcheckbox->setChecked(statusSetting & DxccStatus::NewSlot); ui->workedcheckbox->setChecked(statusSetting & DxccStatus::Worked); ui->confirmedcheckbox->setChecked(statusSetting & DxccStatus::Confirmed); /*******************/ /* Mode Checkboxes */ /*******************/ const QString &moderegexp = LogParam::getDXCFilterModeRE(); ui->cwcheckbox->setChecked(moderegexp.contains("|" + BandPlan::MODE_GROUP_STRING_CW)); ui->phonecheckbox->setChecked(moderegexp.contains("|" + BandPlan::MODE_GROUP_STRING_PHONE)); ui->digitalcheckbox->setChecked(moderegexp.contains("|" + BandPlan::MODE_GROUP_STRING_DIGITAL)); ui->ftxcheckbox->setChecked(moderegexp.contains("|" + BandPlan::MODE_GROUP_STRING_FTx)); /************************/ /* Continent Checkboxes */ /************************/ const QString &contregexp = LogParam::getDXCFilterContRE(); ui->afcheckbox->setChecked(contregexp.contains("|AF")); ui->ancheckbox->setChecked(contregexp.contains("|AN")); ui->ascheckbox->setChecked(contregexp.contains("|AS")); ui->eucheckbox->setChecked(contregexp.contains("|EU")); ui->nacheckbox->setChecked(contregexp.contains("|NA")); ui->occheckbox->setChecked(contregexp.contains("|OC")); ui->sacheckbox->setChecked(contregexp.contains("|SA")); /********************************/ /* Spotter Continent Checkboxes */ /********************************/ const QString &contregexp_spotter = LogParam::getDXCFilterSpotterContRE(); ui->afcheckbox_spotter->setChecked(contregexp_spotter.contains("|AF")); ui->ancheckbox_spotter->setChecked(contregexp_spotter.contains("|AN")); ui->ascheckbox_spotter->setChecked(contregexp_spotter.contains("|AS")); ui->eucheckbox_spotter->setChecked(contregexp_spotter.contains("|EU")); ui->nacheckbox_spotter->setChecked(contregexp_spotter.contains("|NA")); ui->occheckbox_spotter->setChecked(contregexp_spotter.contains("|OC")); ui->sacheckbox_spotter->setChecked(contregexp_spotter.contains("|SA")); /*****************/ /* Deduplication */ /*****************/ ui->deduplicationGroupBox->setChecked(LogParam::getDXCFilterDedup()); ui->dedupTimeDiffSpinbox->setValue(LogParam::getDXCFilterDedupTime(DEDUPLICATION_TIME)); ui->dedupFreqDiffSpinbox->setValue(LogParam::getDXCFilterDedupFreq(DEDUPLICATION_FREQ_TOLERANCE)); /**********/ /* MEMBER */ /**********/ generateMembershipCheckboxes(); } void DxFilterDialog::accept() { FCT_IDENTIFICATION; /********************/ /* Bands Checkboxes */ /********************/ QStringList excludedBands; for ( int i = 0; i < ui->band_group->count(); i++) { QLayoutItem *item = ui->band_group->itemAt(i); if ( !item || !item->widget() ) continue; QCheckBox *bandcheckbox = qobject_cast(item->widget()); if ( bandcheckbox && !bandcheckbox->isChecked()) excludedBands << bandcheckbox->property("bandName").toString(); } LogParam::setDXCExcludedBands(excludedBands); /*********************/ /* Status Checkboxes */ /*********************/ uint status = 0; if ( ui->newEntitycheckbox->isChecked() ) status |= DxccStatus::NewEntity; if ( ui->newBandcheckbox->isChecked() ) status |= DxccStatus::NewBand; if ( ui->newModecheckbox->isChecked() ) status |= DxccStatus::NewMode; if ( ui->newSlotcheckbox->isChecked() ) status |= DxccStatus::NewSlot; if ( ui->workedcheckbox->isChecked() ) status |= DxccStatus::Worked; if ( ui->confirmedcheckbox->isChecked() ) status |= DxccStatus::Confirmed; LogParam::setDXCFilterDxccStatus(status); /*******************/ /* Mode Checkboxes */ /*******************/ QString moderegexp("NOTHING"); if ( ui->cwcheckbox->isChecked() ) moderegexp.append("|" + BandPlan::MODE_GROUP_STRING_CW); if ( ui->phonecheckbox->isChecked() ) moderegexp.append("|" + BandPlan::MODE_GROUP_STRING_PHONE); if ( ui->digitalcheckbox->isChecked() ) moderegexp.append("|" + BandPlan::MODE_GROUP_STRING_DIGITAL); if ( ui->ftxcheckbox->isChecked() ) moderegexp.append("|" + BandPlan::MODE_GROUP_STRING_FTx); LogParam::setDXCFilterModeRE(moderegexp); /************************/ /* Continent Checkboxes */ /************************/ QString contregexp = "NOTHING"; if ( ui->afcheckbox->isChecked() ) contregexp.append("|AF"); if ( ui->ancheckbox->isChecked() ) contregexp.append("|AN"); if ( ui->ascheckbox->isChecked() ) contregexp.append("|AS"); if ( ui->eucheckbox->isChecked() ) contregexp.append("|EU"); if ( ui->nacheckbox->isChecked() ) contregexp.append("|NA"); if ( ui->occheckbox->isChecked() ) contregexp.append("|OC"); if ( ui->sacheckbox->isChecked() ) contregexp.append("|SA"); LogParam::setDXCFilterContRE(contregexp); /********************************/ /* Spotter Continent Checkboxes */ /********************************/ QString contregexp_spotter = "NOTHING"; if ( ui->afcheckbox_spotter->isChecked() ) contregexp_spotter.append("|AF"); if ( ui->ancheckbox_spotter->isChecked() ) contregexp_spotter.append("|AN"); if ( ui->ascheckbox_spotter->isChecked() ) contregexp_spotter.append("|AS"); if ( ui->eucheckbox_spotter->isChecked() ) contregexp_spotter.append("|EU"); if ( ui->nacheckbox_spotter->isChecked() ) contregexp_spotter.append("|NA"); if ( ui->occheckbox_spotter->isChecked() ) contregexp_spotter.append("|OC"); if ( ui->sacheckbox_spotter->isChecked() ) contregexp_spotter.append("|SA"); LogParam::setDXCFilterSpotterContRE(contregexp_spotter); /*****************/ /* Deduplication */ /*****************/ LogParam::setDXCFilterDedup(ui->deduplicationGroupBox->isChecked()); LogParam::setDXCFilterDedupTime(ui->dedupTimeDiffSpinbox->value() ); LogParam::setDXCFilterDedupFreq(ui->dedupFreqDiffSpinbox->value()); /**********/ /* MEMBER */ /**********/ QStringList memberList; if ( ui->memberGroupBox->isChecked() ) { memberList.append("DUMMYCLUB"); for ( QCheckBox* item : static_cast&>(memberListCheckBoxes) ) if ( item->isChecked() ) memberList.append(item->text()); } LogParam::setDXCFilterMemberlists(memberList); done(QDialog::Accepted); } void DxFilterDialog::generateMembershipCheckboxes() { FCT_IDENTIFICATION; const QStringList ¤tFilter = LogParam::getDXCFilterMemberlists(); const QStringList &enabledLists = MembershipQE::getEnabledClubLists(); for ( const QString &enabledClub : enabledLists ) { QCheckBox *columnCheckbox = new QCheckBox(ui->memberGroupBox->parentWidget()); columnCheckbox->setText(enabledClub); columnCheckbox->setChecked(currentFilter.contains(enabledClub)); memberListCheckBoxes.append(columnCheckbox); } if ( memberListCheckBoxes.size() == 0 ) { ui->dxMemberGrid->addWidget(new QLabel(tr("No Club List is enabled"))); } else { int elementIndex = 0; for ( QCheckBox* item : static_cast&>(memberListCheckBoxes) ) { ui->dxMemberGrid->addWidget(item, elementIndex / MAXCOLUMNS, elementIndex % MAXCOLUMNS); elementIndex++; } } ui->memberGroupBox->setChecked(!currentFilter.isEmpty()); } DxFilterDialog::~DxFilterDialog() { FCT_IDENTIFICATION; delete ui; } ================================================ FILE: ui/DxFilterDialog.h ================================================ #ifndef QLOG_UI_DXFILTER_H #define QLOG_UI_DXFILTER_H #include #include namespace Ui { class DxFilterDialog; } class DxFilterDialog : public QDialog { Q_OBJECT public: explicit DxFilterDialog(QWidget *parent = nullptr); ~DxFilterDialog(); void accept() override; private: Ui::DxFilterDialog *ui; QList memberListCheckBoxes; const quint8 MAXCOLUMNS = 6; void generateMembershipCheckboxes(); }; #endif // QLOG_UI_DXFILTER_H ================================================ FILE: ui/DxFilterDialog.ui ================================================ DxFilterDialog 0 0 511 465 0 0 DX Cluster Filters true 0 0 0 General Filters Bands Qt::Vertical 20 40 0 0 701 356 Modes 7 0 0 Phone 0 0 CW 0 0 Digital 0 0 FTx (FT8/FT4) Continent North America South America Oceania Antarctica Europe Africa Asia Log Status New Mode New Entity New Band New Slot Worked Confirmed Extended Filters Spotter Continent South America Antarctica North America Asia Europe Africa Oceania Do not show the following spots when they have the same Callsign and their time difference is lower than Time Diff and their frequency difference is lower than Freq Diff Spots Dedup true 4 4 6 6 6 6 Time difference s 1 9999 Frequency difference kHz ± 1 100 Member true Qt::Vertical 20 40 0 0 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok tabWidget newEntitycheckbox newBandcheckbox newModecheckbox newSlotcheckbox workedcheckbox confirmedcheckbox cwcheckbox phonecheckbox digitalcheckbox ftxcheckbox afcheckbox ancheckbox nacheckbox sacheckbox ascheckbox eucheckbox occheckbox afcheckbox_spotter ancheckbox_spotter nacheckbox_spotter sacheckbox_spotter ascheckbox_spotter eucheckbox_spotter occheckbox_spotter deduplicationGroupBox dedupTimeDiffSpinbox dedupFreqDiffSpinbox memberGroupBox buttonBox accepted() DxFilterDialog accept() 248 254 157 274 buttonBox rejected() DxFilterDialog reject() 316 260 286 274 ================================================ FILE: ui/DxWidget.cpp ================================================ #include #include #include #include #include #ifdef Q_OS_WIN #include #include #include #else #include #include #include #endif #include #include #include #include "DxWidget.h" #include "ui_DxWidget.h" #include "data/Data.h" #include "DxFilterDialog.h" #include "core/debug.h" #include "data/StationProfile.h" #include "data/WCYSpot.h" #include "data/WWVSpot.h" #include "data/ToAllSpot.h" #include "ui/ColumnSettingDialog.h" #include "core/CredentialStore.h" #include "ui/InputPasswordDialog.h" #include "data/BandPlan.h" #include "rig/macros.h" #include "data/Callsign.h" #include "core/LogParam.h" #include "core/PotaQE.h" #define CONSOLE_VIEW 4 #define NUM_OF_RECONNECT_ATTEMPTS 3 #define RECONNECT_TIMEOUT 10000 MODULE_IDENTIFICATION("qlog.ui.dxwidget"); QString dxClusterDefaultUsername() { const StationProfile &profile = StationProfilesManager::instance()->getCurProfile1(); return profile.callsign.toLower(); } class DxClusterCredentials : public SecureServiceBase { public: DxClusterCredentials() = default; ~DxClusterCredentials() override = default; DECLARE_SECURE_SERVICE(DxClusterCredentials); static QString loadPasswd(const DxServerString &server) { if (!server.isValid()) return QString(); return SecureServiceBase::getPassword(server.getPasswordStorageKey(), server.getUsername()); } static void storePasswd(const DxServerString &server, const QString &password) { if (!server.isValid() || password.isEmpty()) return; SecureServiceBase::savePassword(server.getPasswordStorageKey(), server.getUsername(), password); } static void removePasswd(const DxServerString &server) { if (!server.isValid()) return; SecureServiceBase::deletePassword(server.getPasswordStorageKey(), server.getUsername()); } static void refreshDescriptorCache(const QStringList &serverStrings, const QString &defaultUsername); private: static QMutex cacheMutex; static QList descriptorCache; }; REGISTRATION_SECURE_SERVICE(DxClusterCredentials); QMutex DxClusterCredentials::cacheMutex; QList DxClusterCredentials::descriptorCache; void DxClusterCredentials::registerCredentials() { CredentialRegistry::instance().add(QStringLiteral("DXCluster"), []() { QMutexLocker locker(&cacheMutex); return descriptorCache; }); } void DxClusterCredentials::refreshDescriptorCache(const QStringList &serverStrings, const QString &defaultUsername) { QList descriptors; descriptors.reserve(serverStrings.size()); for (const QString &serverString : serverStrings) { DxServerString server(serverString, defaultUsername); if (!server.isValid()) continue; const QString storageKey = server.getPasswordStorageKey(); const QString username = server.getUsername(); if (storageKey.isEmpty() || username.isEmpty()) continue; const QString usernameCopy = username; descriptors.append({ storageKey, [usernameCopy]() { return usernameCopy; } }); } QMutexLocker locker(&cacheMutex); descriptorCache = descriptors; } int DxTableModel::rowCount(const QModelIndex&) const { return dxData.count(); } int DxTableModel::columnCount(const QModelIndex&) const { return 11; } QVariant DxTableModel::data(const QModelIndex& index, int role) const { const DxSpot &spot = dxData.at(index.row()); if ( role == Qt::DisplayRole ) { switch ( index.column() ) { case 0: return locale.toString(spot.dateTime, locale.formatTimeLongWithoutTZ()); case 1: return spot.callsign; case 2: return QString::number(spot.freq, 'f', 4); case 3: return spot.modeGroupString; case 4: return spot.spotter; case 5: return spot.comment; case 6: return spot.dxcc.cont; case 7: return spot.dxcc_spotter.cont; case 8: return spot.band; case 9: return spot.memberList2StringList().join(", "); case 10: return QCoreApplication::translate("DBStrings", spot.dxcc.country.toUtf8().constData()); default: return QVariant(); } } else if (index.column() == 1 && role == Qt::BackgroundRole) { return Data::statusToColor(spot.status, spot.dupeCount, QColor(Qt::transparent)); } else if (index.column() == 1 && role == Qt::ToolTipRole) { return QCoreApplication::translate("DBStrings", spot.dxcc.country.toUtf8().constData()) + " [" + Data::statusToText(spot.status) + "]"; } return QVariant(); } QVariant DxTableModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole || orientation != Qt::Horizontal) return QVariant(); switch ( section ) { case 0: return tr("Time"); case 1: return tr("Callsign"); case 2: return tr("Frequency"); case 3: return tr("Mode"); case 4: return tr("Spotter"); case 5: return tr("Comment"); case 6: return tr("Continent"); case 7: return tr("Spotter Continent"); case 8: return tr("Band"); case 9: return tr("Member"); case 10: return tr("Country"); default: return QVariant(); } } bool DxTableModel::addEntry(const DxSpot &entry, bool deduplicate, qint16 dedup_interval, double dedup_freq_tolerance) { bool shouldInsert = true; if ( deduplicate ) { for (const DxSpot &record : static_cast&>(dxData)) { if ( record.dateTime.secsTo(entry.dateTime) > dedup_interval ) break; if ( record.callsign == entry.callsign && qAbs(MHz(record.freq) - MHz(entry.freq)) < kHz(dedup_freq_tolerance) ) { qCDebug(runtime) << "Duplicate spot" << record.callsign << record.freq << entry.callsign << entry.freq; shouldInsert = false; break; } } } if ( shouldInsert ) { beginInsertRows(QModelIndex(), 0, 0); dxData.prepend(entry); endInsertRows(); } return shouldInsert; } void DxTableModel::clear() { beginResetModel(); dxData.clear(); endResetModel(); } int WCYTableModel::rowCount(const QModelIndex&) const { return wcyData.count(); } int WCYTableModel::columnCount(const QModelIndex&) const { return 9; } QVariant WCYTableModel::data(const QModelIndex& index, int role) const { if ( role == Qt::DisplayRole ) { const WCYSpot &spot = wcyData.at(index.row()); switch ( index.column() ) { case 0: return locale.toString(spot.time, locale.formatTimeLongWithoutTZ()); case 1: return spot.KIndex; case 2: return spot.expK; case 3: return spot.AIndex; case 4: return spot.RIndex; case 5: return spot.SFI; case 6: return spot.SA; case 7: return spot.GMF; case 8: return spot.Au; default: return QVariant(); } } return QVariant(); } QVariant WCYTableModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole || orientation != Qt::Horizontal) return QVariant(); switch ( section ) { case 0: return tr("Time"); case 1: return tr("K"); case 2: return tr("expK"); case 3: return tr("A"); case 4: return tr("R"); case 5: return tr("SFI"); case 6: return tr("SA"); case 7: return tr("GMF"); case 8: return tr("Au"); default: return QVariant(); } } void WCYTableModel::addEntry(const WCYSpot &entry) { beginInsertRows(QModelIndex(), 0, 0); wcyData.prepend(entry); endInsertRows(); } void WCYTableModel::clear() { beginResetModel(); wcyData.clear(); endResetModel(); } int WWVTableModel::rowCount(const QModelIndex&) const { return wwvData.count(); } int WWVTableModel::columnCount(const QModelIndex&) const { return 5; } QVariant WWVTableModel::data(const QModelIndex& index, int role) const { if ( role == Qt::DisplayRole ) { const WWVSpot &spot = wwvData.at(index.row()); switch ( index.column() ) { case 0: return locale.toString(spot.time, locale.formatTimeLongWithoutTZ()); case 1: return spot.SFI; case 2: return spot.AIndex; case 3: return spot.KIndex; case 4: return spot.info1 + " " + QChar(0x2192) + " " + spot.info2; default: return QVariant(); } } return QVariant(); } QVariant WWVTableModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole || orientation != Qt::Horizontal) return QVariant(); switch ( section ) { case 0: return tr("Time"); case 1: return tr("SFI"); case 2: return tr("A"); case 3: return tr("K"); case 4: return tr("Info"); default: return QVariant(); } } void WWVTableModel::addEntry(const WWVSpot &entry) { beginInsertRows(QModelIndex(), 0, 0); wwvData.prepend(entry); endInsertRows(); } void WWVTableModel::clear() { beginResetModel(); wwvData.clear(); endResetModel(); } int ToAllTableModel::rowCount(const QModelIndex&) const { return toAllData.count(); } int ToAllTableModel::columnCount(const QModelIndex&) const { return 3; } QVariant ToAllTableModel::data(const QModelIndex& index, int role) const { if ( role == Qt::DisplayRole ) { const ToAllSpot &spot = toAllData.at(index.row()); switch (index.column()) { case 0: return locale.toString(spot.time, locale.formatTimeLongWithoutTZ()); case 1: return spot.spotter; case 2: return spot.message; default: return QVariant(); } } return QVariant(); } QVariant ToAllTableModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole || orientation != Qt::Horizontal) return QVariant(); switch ( section ) { case 0: return tr("Time"); case 1: return tr("Spotter"); case 2: return tr("Message"); default: return QVariant(); } } void ToAllTableModel::addEntry(const ToAllSpot &entry) { beginInsertRows(QModelIndex(), 0, 0); toAllData.prepend(entry); endInsertRows(); } void ToAllTableModel::clear() { beginResetModel(); toAllData.clear(); endResetModel(); } bool DeleteHighlightedDXServerWhenDelPressedEventFilter::eventFilter(QObject *obj, QEvent *event) { if ( event->type() == QEvent::KeyPress ) { QKeyEvent *keyEvent = static_cast(event); if (keyEvent->key() == Qt::Key::Key_Delete && keyEvent->modifiers() == Qt::ControlModifier) { if ( dynamic_cast(obj) ) { emit deleteServerItem(); return true; } } } // standard event processing return QObject::eventFilter(obj, event); } /****************************************************/ DxWidget::DxWidget(QWidget *parent) : QWidget(parent), socket(nullptr), ui(new Ui::DxWidget), deduplicateSpots(false), commandsMenu(new QMenu(this)), reconnectAttempts(0), connectionState(DISCONNECTED), connectedServerString(nullptr), trendBandList({"6m", "10m", "12m", "15m", "17m", "20m", "30m", "40m", "60m", "80m", "160m"}), trendTableCornerLabel(nullptr), newContactWidget(nullptr) { FCT_IDENTIFICATION; ui->setupUi(this); setSearchClosed(); ui->serverSelect->setStyleSheet(QStringLiteral("QComboBox {color: red}")); wcyTableModel = new WCYTableModel(this); wwvTableModel = new WWVTableModel(this); toAllTableModel = new ToAllTableModel(this); dxTableProxyModel = new SearchFilterProxyModel(ui->dxTable); dxTableModel = new DxTableModel(dxTableProxyModel); dxTableProxyModel->setSourceModel(dxTableModel); QAction *separator = new QAction(this); separator->setSeparator(true); ui->dxTable->setModel(dxTableProxyModel); ui->dxTable->addAction(ui->actionFilter); ui->dxTable->addAction(ui->actionSearch); ui->dxTable->addAction(ui->actionDisplayedColumns); ui->dxTable->addAction(ui->actionClear); ui->dxTable->addAction(separator); ui->dxTable->addAction(ui->actionKeepSpots); ui->dxTable->hideColumn(6); //continent ui->dxTable->hideColumn(7); //spotter continen ui->dxTable->hideColumn(8); //band ui->dxTable->hideColumn(9); //Memberships ui->dxTable->hideColumn(10); //Country ui->dxTable->horizontalHeader()->setSectionsMovable(true); ui->wcyTable->setModel(wcyTableModel); ui->wcyTable->addAction(ui->actionDisplayedColumns); ui->wcyTable->addAction(ui->actionClear); ui->wcyTable->horizontalHeader()->setSectionsMovable(true); ui->wwvTable->setModel(wwvTableModel); ui->wwvTable->addAction(ui->actionDisplayedColumns); ui->wwvTable->addAction(ui->actionClear); ui->wwvTable->horizontalHeader()->setSectionsMovable(true); ui->toAllTable->setModel(toAllTableModel); ui->toAllTable->addAction(ui->actionDisplayedColumns); ui->toAllTable->addAction(ui->actionClear); ui->toAllTable->horizontalHeader()->setSectionsMovable(true); ui->trendTable->setRowCount(trendBandList.size()); ui->trendTable->setColumnCount(Data::getContinentList().size()); ui->trendTable->setHorizontalHeaderLabels(Data::getContinentList()); ui->trendTable->setVerticalHeaderLabels(trendBandList); ui->trendTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); ui->trendTable->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch); moderegexp.setPatternOptions(QRegularExpression::CaseInsensitiveOption); contregexp.setPatternOptions(QRegularExpression::CaseInsensitiveOption); spottercontregexp.setPatternOptions(QRegularExpression::CaseInsensitiveOption); bandregexp.setPatternOptions(QRegularExpression::CaseInsensitiveOption); reloadSetting(); serverComboSetup(); commandsMenu->addAction(ui->actionSpotQSO); commandsMenu->addSeparator(); commandsMenu->addAction(ui->actionShowWCY); commandsMenu->addAction(ui->actionShowWWV); ui->commandButton->setMenu(commandsMenu); ui->commandButton->setDefaultAction(ui->actionSpotQSO); ui->commandButton->setEnabled(false); QMenu *mainWidgetMenu = new QMenu(this); mainWidgetMenu->addAction(ui->actionDeleteServer); mainWidgetMenu->addAction(ui->actionForgetPassword); mainWidgetMenu->addSeparator(); mainWidgetMenu->addAction(ui->actionConnectOnStartup); QMenu *trendContinentMenu = new QMenu(tr("My Continent"), mainWidgetMenu); QActionGroup *continentMenuGroup = new QActionGroup(trendContinentMenu); continentMenuGroup->setExclusive(true); const QString &myContinent = LogParam::getDXCTrendContinent(QString()); QAction *actionAuto = new QAction(tr("Auto"), continentMenuGroup); actionAuto->setCheckable(true); actionAuto->setChecked(true); connect(actionAuto, &QAction::triggered, this, [this]() { LogParam::removeDXCTrendContinent(); recalculateTrend(); }); trendContinentMenu->addAction(actionAuto); trendContinentMenu->addSeparator(); for ( const QString &name : Data::getContinentList() ) { QAction* action = new QAction(name, continentMenuGroup); action->setCheckable(true); action->setChecked((myContinent == name)); connect(action, &QAction::triggered, this, [this, name]() { LogParam::setDXCTrendContinent(name); recalculateTrend(); }); trendContinentMenu->addAction(action); } mainWidgetMenu->addMenu(trendContinentMenu); ui->menuButton->setMenu(mainWidgetMenu); reconnectTimer.setInterval(RECONNECT_TIMEOUT); reconnectTimer.setSingleShot(true); connect(&reconnectTimer, &QTimer::timeout, this, &DxWidget::connectCluster); restoreWidgetSetting(); ui->actionConnectOnStartup->setChecked(getAutoconnectServer()); ui->actionKeepSpots->setChecked(getKeepQSOs()); dxTableProxyModel->setSearchSkippedCols(dxcListHiddenCols()); } void DxWidget::toggleConnect() { FCT_IDENTIFICATION; if ( (socket && socket->isOpen()) || reconnectAttempts ) { disconnectCluster(); } else { if ( !DxServerString::isValidServerString(ui->serverSelect->currentText()) ) { QMessageBox::warning(nullptr, QMessageBox::tr("DXC Server Name Error"), QMessageBox::tr("DXC Server address must be in format

[username@]hostname:port (ex. hamqth.com:7300)

")); return; } connectCluster(); } } void DxWidget::connectCluster() { FCT_IDENTIFICATION; const Callsign connectCallsign(StationProfilesManager::instance()->getCurProfile1().callsign); connectedServerString = new DxServerString(ui->serverSelect->currentText(), connectCallsign.isValid() ? connectCallsign.getBase().toLower() : QString()); if ( !connectedServerString ) { qWarning() << "Cannot allocate currServerString"; return; } if ( !connectedServerString->isValid() ) { qWarning() << "DX Server address is not valid"; return; } qCDebug(runtime) << "username:" << connectedServerString->getUsername() << "host:" << connectedServerString->getHostname() << "port:" << connectedServerString->getPort(); socket = new QTcpSocket(this); connect(socket, &QTcpSocket::readyRead, this, &DxWidget::receive, Qt::QueuedConnection); // QueuedConnection is needed because error is send together with disconnect // which causes creash because error destroid object during processing received signal connect(socket, &QTcpSocket::connected, this, &DxWidget::connected, Qt::QueuedConnection); #if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) connect(socket, QOverload::of(&QAbstractSocket::error), this, &DxWidget::socketError, Qt::QueuedConnection); #else connect(socket, &QTcpSocket::errorOccurred, this, &DxWidget::socketError, Qt::QueuedConnection); #endif ui->connectButton->setEnabled(false); ui->connectButton->setText(tr("Connecting...")); if ( reconnectAttempts == 0 ) { ui->log->clear(); if ( ! getKeepQSOs() ) { ui->dxTable->clearSelection(); dxTableModel->clear(); wcyTableModel->clear(); wwvTableModel->clear(); toAllTableModel->clear(); } ui->dxTable->repaint(); } socket->connectToHost(connectedServerString->getHostname(), connectedServerString->getPort()); connectionState = CONNECTING; } void DxWidget::disconnectCluster(bool tryReconnect) { FCT_IDENTIFICATION; reconnectTimer.stop(); ui->commandEdit->clear(); ui->commandEdit->setEnabled(false); ui->commandButton->setEnabled(false); ui->connectButton->setEnabled(true); if ( socket ) { socket->disconnect(); socket->close(); socket->deleteLater(); socket = nullptr; } if ( reconnectAttempts < NUM_OF_RECONNECT_ATTEMPTS && tryReconnect ) { reconnectAttempts++; reconnectTimer.start(); ui->commandEdit->setPlaceholderText(tr("DX Cluster is temporarily unavailable")); } else { reconnectAttempts = 0; ui->commandEdit->setPlaceholderText(""); ui->connectButton->setText(tr("Connect")); ui->serverSelect->setStyleSheet(QStringLiteral("QComboBox {color: red}")); } connectionState = DISCONNECTED; dxcType = UNKNOWN; if ( connectedServerString ) { delete connectedServerString; connectedServerString = nullptr; } clearAllPasswordIcons(); } void DxWidget::saveDXCServers() { FCT_IDENTIFICATION; QStringList serversItems = getDXCServerList(); const QString &curr_server = ui->serverSelect->currentText(); if ( DxServerString::isValidServerString(ui->serverSelect->currentText()) && !serversItems.contains(curr_server) && !curr_server.isEmpty() ) { ui->serverSelect->insertItem(0, ui->serverSelect->currentText()); serversItems.prepend(ui->serverSelect->currentText()); // insert policy is InsertAtTop ui->serverSelect->setCurrentIndex(0); } LogParam::setDXCServerlist(serversItems); LogParam::setDXCLastServer(ui->serverSelect->currentText()); DxClusterCredentials::refreshDescriptorCache(serversItems, dxClusterDefaultUsername()); } QString DxWidget::modeFilterRegExp() { FCT_IDENTIFICATION; return LogParam::getDXCFilterModeRE(); } QString DxWidget::contFilterRegExp() { FCT_IDENTIFICATION; return LogParam::getDXCFilterContRE(); } QString DxWidget::spotterContFilterRegExp() { FCT_IDENTIFICATION; return LogParam::getDXCFilterSpotterContRE(); } QString DxWidget::bandFilterRegExp() { FCT_IDENTIFICATION; QString regexp("NOTHING"); const QList &bands = BandPlan::bandsList(false, true); const QStringList exludedBandFilter = LogParam::getDXCExcludedBands(); for ( const Band &band : bands ) if ( !exludedBandFilter.contains(band.name) ) regexp.append("|^" + band.name); return regexp; } uint DxWidget::dxccStatusFilterValue() { FCT_IDENTIFICATION; return LogParam::getDXCFilterDxccStatus(); } int DxWidget::getDedupTimeValue() { FCT_IDENTIFICATION; return LogParam::getDXCFilterDedupTime(DEDUPLICATION_TIME); } int DxWidget::getDedupFreqValue() { FCT_IDENTIFICATION; return LogParam::getDXCFilterDedupFreq(DEDUPLICATION_FREQ_TOLERANCE); } bool DxWidget::spotDedupValue() { FCT_IDENTIFICATION; return LogParam::getDXCFilterDedup(); } QStringList DxWidget::dxMemberList() { FCT_IDENTIFICATION; return LogParam::getDXCFilterMemberlists(); } bool DxWidget::getAutoconnectServer() { FCT_IDENTIFICATION; return LogParam::getDXCAutoconnectServer(); } void DxWidget::saveAutoconnectServer(bool state) { FCT_IDENTIFICATION; LogParam::setDXCAutoconnectServer(state); } bool DxWidget::getKeepQSOs() { FCT_IDENTIFICATION; return LogParam::getDXCKeepQSOs(); } void DxWidget::saveKeepQSOs(bool state) { FCT_IDENTIFICATION; LogParam::setDXCKeepQSOs(state); } void DxWidget::sendCommand(const QString & command, bool switchToConsole) { FCT_IDENTIFICATION; if ( socket && socket->isOpen() ) socket->write((command + QLatin1String("\r\n")).toLatin1()); // switch to raw mode to see a response if ( switchToConsole ) { ui->viewModeCombo->setCurrentIndex(CONSOLE_VIEW); } } void DxWidget::saveWidgetSetting() { FCT_IDENTIFICATION; LogParam::setDXCDXTableState(ui->dxTable->horizontalHeader()->saveState()); LogParam::setDXCWCYTableState(ui->wcyTable->horizontalHeader()->saveState()); LogParam::setDXCWWVTableState(ui->wwvTable->horizontalHeader()->saveState()); LogParam::setDXCTOALLTableState(ui->toAllTable->horizontalHeader()->saveState()); LogParam::setDXCConsoleFontSize(ui->log->font().pointSize()); } void DxWidget::restoreWidgetSetting() { FCT_IDENTIFICATION; QByteArray state = LogParam::getDXCDXTableState(); if ( !state.isEmpty() ) ui->dxTable->horizontalHeader()->restoreState(state); state = LogParam::getDXCWCYTableState(); if ( !state.isEmpty() ) ui->wcyTable->horizontalHeader()->restoreState(state); state = LogParam::getDXCWWVTableState(); if ( !state.isEmpty() ) ui->wwvTable->horizontalHeader()->restoreState(state); state = LogParam::getDXCTOALLTableState(); if ( !state.isEmpty() ) ui->toAllTable->horizontalHeader()->restoreState(state); int fontsize = LogParam::getDXCConsoleFontSize(); QFont monospace(QFontDatabase::systemFont(QFontDatabase::FixedFont)); if ( fontsize > 0 ) monospace.setPointSize(fontsize); ui->log->setFont(monospace); } void DxWidget::send() { FCT_IDENTIFICATION; sendCommand(ui->commandEdit->text()); ui->commandEdit->clear(); } void DxWidget::receive() { FCT_IDENTIFICATION; static QRegularExpression dxSpotRE(QStringLiteral("^DX de ([a-zA-Z0-9\\/]+).*:\\s+([0-9|.]+)\\s+([a-zA-Z0-9\\/]+)[^\\s]*\\s+(.*)\\s+(\\d{4}Z)"), QRegularExpression::CaseInsensitiveOption); static QRegularExpression wcySpotRE(QStringLiteral("^(WCY de) +([A-Z0-9\\-#]*) +<(\\d{2})> *: +K=(\\d{1,3}) expK=(\\d{1,3}) A=(\\d{1,3}) R=(\\d{1,3}) SFI=(\\d{1,3}) SA=([a-zA-Z]{1,3}) GMF=([a-zA-Z]{1,3}) Au=([a-zA-Z]{2}) *$"), QRegularExpression::CaseInsensitiveOption); static QRegularExpression wwvSpotRE(QStringLiteral("^(WWV de) +([A-Z0-9\\-#]*) +<(\\d{2})Z?> *: *SFI=(\\d{1,3}), A=(\\d{1,3}), K=(\\d{1,3}), (.*\\b) *-> *(.*\\b) *$"), QRegularExpression::CaseInsensitiveOption); static QRegularExpression toAllSpotRE(QStringLiteral("^(To ALL de) +([A-Z0-9\\-#]*)\\s?(<(\\d{4})Z>)?[ :]+(.*)?$"), QRegularExpression::CaseInsensitiveOption); static QRegularExpression SHDXFormatRE(QStringLiteral("^\\s{0,8}([0-9|.]+)\\s+([a-zA-Z0-9\\/]+)[^\\s]*\\s+(.*)\\s+(\\d{4}Z) (.*)<([a-zA-Z0-9\\/]+)>$"), QRegularExpression::CaseInsensitiveOption); static QRegularExpression splitLineRE(QStringLiteral("(\a|\n|\r)+")); static QRegularExpression loginRE(QStringLiteral("enter your call(sign)?:")); static const QString loginStr = QStringLiteral("login"); static const QString ccClusterStr = QStringLiteral("Running CC Cluster software"); static const QString invalidCallsign = QStringLiteral("is an invalid callsign"); static const QString passwordStr = QStringLiteral("password"); static const QString sorryStr = QStringLiteral("sorry"); static const QString dxSpiderStr = QStringLiteral("dxspider"); static const QString dxPrefix = QStringLiteral("DX"); static const QString wcyPrefix = QStringLiteral("WCY de"); static const QString wwvPrefix = QStringLiteral("WWV de"); static const QString toAllPrefix = QStringLiteral("To ALL de"); reconnectAttempts = 0; if ( !socket || !connectedServerString ) { qCDebug(runtime) << "socket or connection string is null"; return; } const QByteArray rawData = socket->readAll(); if ( rawData.isEmpty() ) return; const QString data = QString::fromUtf8(rawData); #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) const QStringList lines = data.split(splitLineRE, Qt::SkipEmptyParts); #else /* Due to ubuntu 20.04 where qt5.12 is present */ const QStringList lines = data.split(splitLineRE, QString::SkipEmptyParts); #endif const auto sendLine = [this](const QString &s) { socket->write((s + QLatin1String("\r\n")).toUtf8()); }; for ( const QString &line : lines ) { qCDebug(runtime) << connectionState << line; // Skip empty lines if ( line.isEmpty() ) continue; if ( connectionState == CONNECTED ) { if ( line.startsWith(loginStr, Qt::CaseInsensitive) || line.contains(loginRE) ) { // username requested sendLine(connectedServerString->getUsername()); connectionState = LOGIN_SENT; qCDebug(runtime) << "Login sent"; continue; } if ( line.contains(ccClusterStr, Qt::CaseInsensitive) ) { dxcType = CCCluster; qCDebug(runtime) << "CC Cluster Detected"; updateCommandsMenu(); continue; } } if ( connectionState == LOGIN_SENT && line.contains(invalidCallsign) ) { // invalid login QMessageBox::warning(nullptr, tr("DXC Server Error"), tr("An invalid callsign")); continue; } if ( connectionState == LOGIN_SENT && line.startsWith(passwordStr, Qt::CaseInsensitive) ) { // password requested QString password = DxClusterCredentials::loadPasswd(*connectedServerString); if ( password.isEmpty() ) { InputPasswordDialog passwordDialog(tr("DX Cluster Password"), "" + tr("Security Notice") + ": " + tr("The password can be sent via an unsecured channel") + "

" + "" + tr("Server") + ": " + socket->peerName() + ":" + QString::number(socket->peerPort()) + "
" + "" + tr("Username") + ": " + connectedServerString->getUsername(), this); if ( passwordDialog.exec() == QDialog::Accepted ) { password = passwordDialog.getPassword(); if ( passwordDialog.getRememberPassword() && !password.isEmpty() ) { DxClusterCredentials::storePasswd(*connectedServerString, password); } } else { disconnectCluster(false); return; } } activateCurrPasswordIcon(); sendLine(password); connectionState = PASSWORD_SENT; qCDebug(runtime) << "Password sent"; continue; } if ( connectionState == PASSWORD_SENT && line.startsWith(sorryStr, Qt::CaseInsensitive ) ) { // invalid password DxClusterCredentials::removePasswd(*connectedServerString); QMessageBox::warning(nullptr, QMessageBox::tr("DX Cluster Password"), QMessageBox::tr("Invalid Password")); continue; } if ( connectionState == LOGIN_SENT || connectionState == PASSWORD_SENT ) { if ( line.contains(dxSpiderStr, Qt::CaseInsensitive) ) { connectionState = OPERATION; dxcType = DXSPIDER; qCDebug(runtime) << "dxspider Detected"; updateCommandsMenu(); ui->commandButton->setEnabled(true); continue; } // the difference compared to DXSpider is that CC Cluster type // is detected before login, but DXSpider type is detected after login if ( dxcType == CCCluster ) { connectionState = OPERATION; ui->commandButton->setEnabled(true); } } /********************/ /* Received DX SPOT */ /********************/ if ( line.startsWith(dxPrefix) ) { if ( connectionState == LOGIN_SENT || connectionState == PASSWORD_SENT ) connectionState = OPERATION; const QRegularExpressionMatch dxSpotMatch = dxSpotRE.match(line); if ( dxSpotMatch.hasMatch() ) { //DX de N9EN/4: 18077.0 OS5Z op. Marc; tnx QSO! 1359Z processDxSpot(dxSpotMatch.captured(1), //spotter dxSpotMatch.captured(2), //freq dxSpotMatch.captured(3), //call dxSpotMatch.captured(4)); //comment } } /************************/ /* Received WCY Info */ /************************/ else if ( line.startsWith(wcyPrefix) ) { const QRegularExpressionMatch wcySpotMatch = wcySpotRE.match(line); if ( wcySpotMatch.hasMatch() ) { WCYSpot spot; spot.time = QDateTime::currentDateTime().toTimeZone(QTimeZone::utc()); spot.KIndex = wcySpotMatch.captured(4).toUInt(); spot.expK = wcySpotMatch.captured(5).toUInt(); spot.AIndex = wcySpotMatch.captured(6).toUInt(); spot.RIndex = wcySpotMatch.captured(7).toUInt(); spot.SFI = wcySpotMatch.captured(8).toUInt(); spot.SA = wcySpotMatch.captured(9); spot.GMF = wcySpotMatch.captured(10); spot.Au = wcySpotMatch.captured(11); emit newWCYSpot(spot); wcyTableModel->addEntry(spot); } } /*********************/ /* Received WWV Info */ /*********************/ else if ( line.startsWith(wwvPrefix) ) { const QRegularExpressionMatch wwvSpotMatch = wwvSpotRE.match(line); if ( wwvSpotMatch.hasMatch() ) { WWVSpot spot; spot.time = QDateTime::currentDateTime().toTimeZone(QTimeZone::utc()); spot.SFI = wwvSpotMatch.captured(4).toUInt(); spot.AIndex = wwvSpotMatch.captured(5).toUInt(); spot.KIndex = wwvSpotMatch.captured(6).toUInt(); spot.info1 = wwvSpotMatch.captured(7); spot.info2 = wwvSpotMatch.captured(8); emit newWWVSpot(spot); wwvTableModel->addEntry(spot); } } /*************************/ /* Received Generic Info */ /*************************/ else if ( line.startsWith(toAllPrefix) ) { const QRegularExpressionMatch toAllSpotMatch = toAllSpotRE.match(line); if ( toAllSpotMatch.hasMatch() ) { ToAllSpot spot; spot.time = QDateTime::currentDateTime().toTimeZone(QTimeZone::utc()); spot.spotter = toAllSpotMatch.captured(2); DxccEntity spotter_info = Data::instance()->lookupDxcc(spot.spotter); spot.dxcc_spotter = spotter_info; spot.message = toAllSpotMatch.captured(5); emit newToAllSpot(spot); toAllTableModel->addEntry(spot); } } /****************/ /* SH/DX format */ /****************/ else if ( line.contains(SHDXFormatRE) ) { const QRegularExpressionMatch SHDXFormatMatch = SHDXFormatRE.match(line); if ( SHDXFormatMatch.hasMatch() ) { //14045.6 K5UV 6-Dec-2023 1359Z CWops CWT Contest const QDateTime &dateTime = QDateTime::fromString(SHDXFormatMatch.captured(3) + " " + SHDXFormatMatch.captured(4), "d-MMM-yyyy hhmmZ"); processDxSpot(SHDXFormatMatch.captured(6), //spotter SHDXFormatMatch.captured(1), //freq SHDXFormatMatch.captured(2), //call SHDXFormatMatch.captured(5), dateTime); //comment } } ui->log->appendPlainText(line); } } void DxWidget::socketError(QAbstractSocket::SocketError socker_error) { FCT_IDENTIFICATION; bool reconectRequested = (reconnectAttempts > 0 ) ? true : false; QString error_msg = QObject::tr("Cannot connect to DXC Server

Reason : "); qCDebug(runtime) << socker_error; switch (socker_error) { case QAbstractSocket::ConnectionRefusedError: error_msg.append(QObject::tr("Connection Refused")); break; case QAbstractSocket::RemoteHostClosedError: error_msg.append(QObject::tr("Host closed the connection")); reconectRequested = (connectionState != LOGIN_SENT && connectionState != PASSWORD_SENT); break; case QAbstractSocket::HostNotFoundError: error_msg.append(QObject::tr("Host not found")); break; case QAbstractSocket::SocketTimeoutError: error_msg.append(QObject::tr("Timeout")); reconectRequested = true; break; case QAbstractSocket::NetworkError: error_msg.append(QObject::tr("Network Error")); break; default: error_msg.append(QObject::tr("Internal Error")); } error_msg.append("

"); qInfo() << "Detailed Error: " << socker_error; if ( connectionState != LOGIN_SENT && connectionState != PASSWORD_SENT && (! reconectRequested || reconnectAttempts == NUM_OF_RECONNECT_ATTEMPTS)) { QMessageBox::warning(nullptr, QMessageBox::tr("DXC Server Connection Error"), error_msg); } disconnectCluster(reconectRequested); } void DxWidget::connected() { FCT_IDENTIFICATION; if ( !socket ) { qWarning() << "Socket is not opened"; return; } int fd = socket->socketDescriptor(); #ifdef Q_OS_WIN DWORD dwBytesRet = 0; struct tcp_keepalive alive; // your options for "keepalive" mode alive.onoff = TRUE; // turn it on alive.keepalivetime = 10000; // delay (ms) between requests, here is 10s, default is 2h (7200000) alive.keepaliveinterval = 5000; // delay between "emergency" ping requests, their number (6) is not configurable /* So with this config socket will send keepalive requests every 30 seconds after last data transaction when everything is ok. If there is no reply (wire plugged out) it'll send 6 requests with 5s delay between them and then close. As a result we will get disconnect after approximately 1 min timeout. */ if (WSAIoctl(fd, SIO_KEEPALIVE_VALS, &alive, sizeof(alive), NULL, 0, &dwBytesRet, NULL, NULL) == SOCKET_ERROR) { qWarning() << "WSAIotcl(SIO_KEEPALIVE_VALS) failed with err#" << WSAGetLastError(); } #else int enableKeepAlive = 1; int maxIdle = 10; int count = 3; int interval = 10; if ( setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &enableKeepAlive, sizeof(enableKeepAlive)) !=0 ) { qWarning() << "Cannot set keepalive for DXC"; } else { #ifndef Q_OS_MACOS if ( setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &maxIdle, sizeof(maxIdle)) != 0 ) #else if ( setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &maxIdle, sizeof(maxIdle)) != 0 ) #endif /* Q_OS_MACOS */ { qWarning() << "Cannot set keepalive idle for DXC"; } if ( setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &count, sizeof(count)) != 0 ) { qWarning() << "Cannot set keepalive counter for DXC"; } if ( setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &interval, sizeof(interval)) != 0 ) { qWarning() << "Cannot set keepalive interval for DXC"; } // TODO: setup TCP_USER_TIMEOUT???? } #endif ui->commandEdit->setEnabled(true); ui->connectButton->setEnabled(true); ui->connectButton->setText(tr("Disconnect")); ui->commandEdit->setPlaceholderText(tr("DX Cluster Command")); ui->serverSelect->setStyleSheet("QComboBox {color: green}"); connectionState = CONNECTED; saveDXCServers(); } void DxWidget::viewModeChanged(int index) { FCT_IDENTIFICATION; ui->stack->setCurrentIndex(index); } void DxWidget::entryDoubleClicked(QModelIndex index) { FCT_IDENTIFICATION; const QModelIndex &source_index = dxTableProxyModel->mapToSource(index); emit tuneDx(dxTableModel->getSpot(source_index)); } void DxWidget::actionFilter() { FCT_IDENTIFICATION; DxFilterDialog dialog; if (dialog.exec() == QDialog::Accepted) reloadSetting(); } void DxWidget::adjusteServerSelectSize(QString input) { FCT_IDENTIFICATION; qDebug(function_parameters)<< input << input.length(); QFont f; QFontMetrics met(f); ui->serverSelect->setMinimumWidth(met.boundingRect(input).width() + 35); ui->serverSelect->update(); ui->serverSelect->repaint(); } void DxWidget::serverSelectChanged(int index) { FCT_IDENTIFICATION; qDebug(function_parameters) << index; if ( (socket && socket->isOpen()) || reconnectAttempts ) { /* reconnect DXC Server */ if ( index >= 0 ) { disconnectCluster(); connectCluster(); } } } void DxWidget::setLastQSO(QSqlRecord qsoRecord) { FCT_IDENTIFICATION; lastQSO = qsoRecord; } void DxWidget::reloadSetting() { FCT_IDENTIFICATION; moderegexp.setPattern(modeFilterRegExp()); contregexp.setPattern(contFilterRegExp()); spottercontregexp.setPattern(spotterContFilterRegExp()); bandregexp.setPattern(bandFilterRegExp()); dxccStatusFilter = dxccStatusFilterValue(); deduplicateSpots = spotDedupValue(); deduplicatetime = getDedupTimeValue(); deduplicatefreq = getDedupFreqValue(); QStringList tmp = dxMemberList(); #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) dxMemberFilter = QSet(tmp.begin(), tmp.end()); #else /* Due to ubuntu 20.04 where qt5.12 is present */ dxMemberFilter = QSet(QSet::fromList(tmp)); #endif ui->filteredLabel->setHidden(!isFilterEnabled()); } void DxWidget::prepareQSOSpot(QSqlRecord qso) { FCT_IDENTIFICATION; qCDebug(runtime) << "QSO" << qso; if ( !ui->commandEdit->isEnabled() ) return; if ( qso.contains(QStringLiteral("start_time")) ) { //qso is valid record if ( qso.contains(QStringLiteral("freq")) && qso.contains(QStringLiteral("callsign")) ) { double spotFreq = ( qso.contains("freq_rx") && qso.value("freq_rx").toDouble() != 0.0 ) ? qso.value("freq_rx").toDouble() : qso.value("freq").toDouble(); // DX Spider allow to enter QSO freq in MHz but it is not reliable for SHF bands. // a more reliable way is to send a spot with kHz value ui->commandEdit->setText(QString("dx %1 %2 ").arg(QString::number(Hz2kHz(MHz(spotFreq)), 'f', 0), qso.value("callsign").toString())); ui->commandEdit->setFocus(); } } } void DxWidget::setSearch(const QString &text) { FCT_IDENTIFICATION; dxTableProxyModel->setSearchString(text); } void DxWidget::setSearchStatus(bool visible) { FCT_IDENTIFICATION; ui->searchEdit->setVisible(visible); ui->searchEdit->setFocus(); ui->searchCloseButton->setVisible(visible); ui->searchEdit->setText(visible && newContactWidget ? newContactWidget->getCallsign() : QString()); } void DxWidget::setSearchVisible() { FCT_IDENTIFICATION; setSearchStatus(!ui->searchEdit->isVisible()); } void DxWidget::setSearchClosed() { FCT_IDENTIFICATION; setSearchStatus(false); } void DxWidget::trendDoubleClicked(int row, int column) { FCT_IDENTIFICATION; qCDebug(function_parameters) << row << column; emit tuneBand(trendBandList[row]); } void DxWidget::setTunedFrequency(VFOID vfoid, double vfoFreq, double ritFreq, double xitFreq) { FCT_IDENTIFICATION; qCDebug(function_parameters) << vfoid << vfoFreq << ritFreq << xitFreq; // DX widget tracks the RX frequency only if ( vfoid == VFO2 ) return; const QString& newBand = BandPlan::freq2Band(xitFreq).name; const QBrush &defaultBrush = ui->trendTable->horizontalHeaderItem(0)->background(); for ( int i = 0; i < ui->trendTable->rowCount(); i++ ) { QTableWidgetItem *bandItem = ui->trendTable->verticalHeaderItem(i); if (!bandItem) continue; bandItem->setBackground(((bandItem->text() == newBand) ? QBrush(Qt::darkGray) : defaultBrush)); } } void DxWidget::registerContactWidget(const NewContactWidget *contactWidget) { FCT_IDENTIFICATION; newContactWidget = contactWidget; } void DxWidget::setDxTrend(QHash>> trend) { FCT_IDENTIFICATION; receivedTrendData = trend; recalculateTrend(); } QColor DxWidget::getHeatmapColor(int value, int maxValue) { if (maxValue == 0) return QColor(0,0,0,0); //double normalized = static_cast(value) / maxValue; double normalized = log(1 + value) / log(1 + maxValue); int g = 255; int r = static_cast(255 * normalized); int b = 0; int a = ( value == 0 ? 0 : 255); return QColor(r, g, b, a); } bool DxWidget::isFilterEnabled() const { FCT_IDENTIFICATION; return dxccStatusFilter != (DxccStatus::NewEntity | DxccStatus::NewBand | DxccStatus::NewMode | DxccStatus::NewSlot | DxccStatus::Worked | DxccStatus::Confirmed) || contregexp.pattern().count("|") != 7 || !dxMemberFilter.isEmpty() // || deduplicateSpots // deduplication does not mean filtring. || spottercontregexp.pattern().count("|") != 7 || moderegexp.pattern().count("|") != 4 || BandPlan::bandsList(false, true).size() != bandregexp.pattern().count("|"); } void DxWidget::recalculateTrend() { FCT_IDENTIFICATION; const DxccEntity &myDxccEntity = Data::instance()->lookupDxcc(StationProfilesManager::instance()->getCurProfile1().callsign); const QString &myContinent = LogParam::getDXCTrendContinent(myDxccEntity.cont); bool myContinentChanged = false; // Create Left Top Corner Label if ( ! trendTableCornerLabel ) { // this part must not be called in the class constructor. Geometry is not determined in QT trendTableCornerLabel = new QLabel(myContinent + " →", ui->trendTable); trendTableCornerLabel->setAlignment(Qt::AlignCenter); trendTableCornerLabel->setGeometry(0, 0, ui->trendTable->verticalHeader()->width(), ui->trendTable->horizontalHeader()->height()); trendTableCornerLabel->show(); } else { myContinentChanged = !trendTableCornerLabel->text().contains(myContinent); trendTableCornerLabel->setText(myContinent + " →"); } //Clear Table for (int row = 0; row < ui->trendTable->rowCount(); ++row) { for (int col = 0; col < ui->trendTable->columnCount(); ++col) { QTableWidgetItem *item = ui->trendTable->takeItem(row, col); if (item) delete item; } } ui->trendTable->clearContents(); // get my continent data trendDataForMyCont = receivedTrendData.value(myContinent); // update bidirections EU->OC and OC->EU for ( auto continentData = receivedTrendData.cbegin(); continentData != receivedTrendData.cend(); ++continentData ) { if (continentData.key() == myContinent ) continue; for ( auto band = continentData.value()[myContinent].cbegin(); band != continentData.value()[myContinent].cend(); ++band ) trendDataForMyCont[continentData.key()][band.key()] += band.value(); } int maxValue = 0; // find the max value for heatmap for ( const auto &outer : static_cast>&>(trendDataForMyCont) ) for ( const auto &inner : outer ) if ( inner > maxValue ) maxValue = inner; // fill the table for ( int row = 0; row < trendBandList.size(); ++row ) { const QString &band = trendBandList[row]; for ( int col = 0; col < Data::getContinentList().size(); ++col ) { const QString &toContinent = Data::getContinentList()[col]; int currentSpots = trendDataForMyCont.value(toContinent).value(band); int prevSpots = prevTrendDataForMyCont.value(toContinent).value(band); int diff = currentSpots - prevSpots; QString displayText = ( currentSpots == 0 ) ? "" : QString::number(currentSpots); if ( !prevTrendDataForMyCont.isEmpty() && !myContinentChanged ) { if (diff > 0) displayText += " (\u2197)"; else if (diff < 0) displayText += " (\u2198)"; } QTableWidgetItem *item = new QTableWidgetItem(displayText); item->setBackground(getHeatmapColor(currentSpots, maxValue)); item->setForeground(QColor(Qt::black)); item->setTextAlignment(Qt::AlignCenter); ui->trendTable->setItem(row, col, item); } } prevTrendDataForMyCont = trendDataForMyCont; } void DxWidget::actionCommandSpotQSO() { FCT_IDENTIFICATION; qCDebug(runtime) << "Last QSO" << lastQSO; prepareQSOSpot(lastQSO); ui->commandButton->setDefaultAction(ui->actionSpotQSO); } void DxWidget::actionCommandShowHFStats() { FCT_IDENTIFICATION; sendCommand(QStringLiteral("sh/hfstats"), true); ui->commandButton->setDefaultAction(ui->actionShowHFStats); } void DxWidget::actionCommandShowVHFStats() { FCT_IDENTIFICATION; sendCommand(QStringLiteral("sh/vhfstats"), true); ui->commandButton->setDefaultAction(ui->actionShowVHFStats); } void DxWidget::actionCommandShowWCY() { FCT_IDENTIFICATION; sendCommand(QStringLiteral("sh/wcy"), true); ui->commandButton->setDefaultAction(ui->actionShowWCY); } void DxWidget::actionCommandShowWWV() { FCT_IDENTIFICATION; sendCommand(QStringLiteral("sh/wwv"), true); ui->commandButton->setDefaultAction(ui->actionShowWWV); } void DxWidget::actionConnectOnStartup() { FCT_IDENTIFICATION; saveAutoconnectServer(ui->actionConnectOnStartup->isChecked()); if ( ui->actionConnectOnStartup->isChecked() && socket == nullptr) { // dxc is not connected, connnet it toggleConnect(); } } void DxWidget::actionDeleteServer() { FCT_IDENTIFICATION; actionForgetPassword(); ui->serverSelect->removeItem(ui->serverSelect->currentIndex()); ui->serverSelect->setMinimumWidth(0); saveDXCServers(); } void DxWidget::actionForgetPassword() { FCT_IDENTIFICATION; DxServerString serverName(ui->serverSelect->currentText(), StationProfilesManager::instance()->getCurProfile1().callsign.toLower()); if ( serverName.isValid() ) { DxClusterCredentials::removePasswd(serverName); } else { qCDebug(runtime) << "Cannot remove record from Secure Store, server name is not valid" << ui->serverSelect->currentText(); } ui->serverSelect->setItemIcon(ui->serverSelect->currentIndex(), QIcon()); } void DxWidget::actionKeepSpots() { FCT_IDENTIFICATION; saveKeepQSOs(ui->actionKeepSpots->isChecked()); } void DxWidget::actionClear() { FCT_IDENTIFICATION; QTableView *view = nullptr; switch ( ui->stack->currentIndex() ) { case 0: dxTableModel->clear(); view = ui->dxTable; break; case 1: wcyTableModel->clear(); view = ui->wcyTable; break; case 2: wwvTableModel->clear(); view = ui->wwvTable; break; case 3: toAllTableModel->clear(); view = ui->toAllTable; break; default: view = nullptr; } if ( view ) { view->repaint(); } } void DxWidget::displayedColumns() { FCT_IDENTIFICATION; QTableView *view = nullptr; switch ( ui->stack->currentIndex() ) { case 0: view = ui->dxTable; break; case 1: view = ui->wcyTable; break; case 2: view = ui->wwvTable; break; case 3: view = ui->toAllTable; break; default: view = nullptr; } if ( view ) { ColumnSettingSimpleDialog dialog(view); dialog.exec(); saveWidgetSetting(); if ( view == ui->dxTable ) dxTableProxyModel->setSearchSkippedCols(dxcListHiddenCols()); } } QStringList DxWidget::getDXCServerList() { FCT_IDENTIFICATION; QStringList ret; for ( int index = 0; index < ui->serverSelect->count(); index++ ) { ret << ui->serverSelect->itemText(index); } return ret; } void DxWidget::serverComboSetup() { FCT_IDENTIFICATION; DeleteHighlightedDXServerWhenDelPressedEventFilter *deleteHandled = new DeleteHighlightedDXServerWhenDelPressedEventFilter(this); ui->serverSelect->addItems(LogParam::getDXCServerlist()); ui->serverSelect->installEventFilter(deleteHandled); connect(deleteHandled, &DeleteHighlightedDXServerWhenDelPressedEventFilter::deleteServerItem, this, &DxWidget::actionDeleteServer); int index = ui->serverSelect->findText(LogParam::getDXCLastServer()); // if last server still exists then set it otherwise use the first one if ( index >= 0 ) { ui->serverSelect->setCurrentIndex(index); } DxClusterCredentials::refreshDescriptorCache(getDXCServerList(), dxClusterDefaultUsername()); } void DxWidget::clearAllPasswordIcons() { FCT_IDENTIFICATION; for (int i = 0; i < ui->serverSelect->count(); i++) { ui->serverSelect->setItemIcon(i, QIcon()); } } void DxWidget::activateCurrPasswordIcon() { FCT_IDENTIFICATION; ui->serverSelect->setItemIcon(ui->serverSelect->currentIndex(), QIcon(":/icons/password.png")); } void DxWidget::updateCommandsMenu() { FCT_IDENTIFICATION; switch (dxcType) { case CCCluster: if ( ui->commandButton->defaultAction() == ui->actionShowHFStats || ui->commandButton->defaultAction() == ui->actionShowVHFStats ) ui->commandButton->setDefaultAction(ui->actionSpotQSO); commandsMenu->removeAction(ui->actionShowHFStats); commandsMenu->removeAction(ui->actionShowVHFStats); break; case DXSPIDER: commandsMenu->addAction(ui->actionShowHFStats); commandsMenu->addAction(ui->actionShowVHFStats); break; default: qCDebug(runtime) << "no change"; } } void DxWidget::processDxSpot(const QString &spotter, const QString &freq, const QString &call, const QString &comment, const QDateTime &dateTime) { FCT_IDENTIFICATION; qCDebug(function_parameters) << spotter << freq << call << comment << dateTime << dateTime.isNull(); DxSpot spot; spot.dateTime = (!dateTime.isValid()) ? QDateTime::currentDateTime().toTimeZone(QTimeZone::utc()) : dateTime; spot.callsign = call; spot.freq = freq.toDouble() / 1000; spot.band = BandPlan::freq2Band(spot.freq).name; spot.spotter = spotter; spot.comment = comment.trimmed(); spot.bandPlanMode = modeGroupFromComment(spot.comment); if ( spot.bandPlanMode == BandPlan::BAND_MODE_UNKNOWN ) { spot.bandPlanMode = BandPlan::freq2BandMode(spot.freq); } if ( spot.bandPlanMode == BandPlan::BAND_MODE_PHONE ) { spot.bandPlanMode = (spot.freq < 10.0 ) ? BandPlan::BAND_MODE_LSB : BandPlan::BAND_MODE_USB; } spot.modeGroupString = BandPlan::bandMode2BandModeGroupString(spot.bandPlanMode); spot.dxcc = Data::instance()->lookupDxcc(call); spot.dxcc_spotter = Data::instance()->lookupDxcc(spotter); spot.status = Data::instance()->dxccStatus(spot.dxcc.dxcc, spot.band, spot.modeGroupString); spot.callsign_member = MembershipQE::instance()->query(spot.callsign); spot.dupeCount = Data::countDupe(spot.callsign, spot.band, spot.modeGroupString); wwffRefFromComment(spot); potaRefFromComment(spot); sotaRefFromComment(spot); iotaRefFromComment(spot); splitFreqFromComment(spot); #if 0 if ( !spot.sotaRef.isEmpty() ) qInfo() << "SOTA" << spot.sotaRef << spot.comment; if ( !spot.wwffRef.isEmpty() ) qInfo() << "WWFF" << spot.wwffRef << spot.comment; if ( !spot.potaRef.isEmpty() ) qInfo() << "POTA" << spot.potaRef << spot.comment; if ( !spot.iotaRef.isEmpty() ) qInfo() << "IOTA" << spot.iotaRef << spot.comment; #endif emit newSpot(spot); if ( spot.modeGroupString.contains(moderegexp) && spot.dxcc.cont.contains(contregexp) && spot.dxcc_spotter.cont.contains(spottercontregexp) && spot.band.contains(bandregexp) && ( spot.status & dxccStatusFilter) && ( dxMemberFilter.size() == 0 || (dxMemberFilter.size() && spot.memberList2Set().intersects(dxMemberFilter)) ) && spot.dupeCount == 0 ) { if ( dxTableModel->addEntry(spot, deduplicateSpots, deduplicatetime, deduplicatefreq) ) emit newFilteredSpot(spot); } } QVector DxWidget::dxcListHiddenCols() const { QVector ret; ret.reserve(dxTableModel->columnCount()); for ( int i = 0; i < dxTableModel->columnCount(); ++i ) { if (ui->dxTable->isColumnHidden(i)) ret.append(i); } return ret; } BandPlan::BandPlanMode DxWidget::modeGroupFromComment(const QString &comment) const { FCT_IDENTIFICATION; qCDebug(function_parameters) << comment; #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) const QStringList &tokenizedComment = comment.split(" ", Qt::SkipEmptyParts); #else /* Due to ubuntu 20.04 where qt5.12 is present */ const QStringList &tokenizedComment = comment.split(" ", QString::SkipEmptyParts); #endif if ( tokenizedComment.contains("CW", Qt::CaseInsensitive) || tokenizedComment.contains("", Qt::CaseInsensitive) ) return BandPlan::BAND_MODE_CW; if ( tokenizedComment.contains("FT8", Qt::CaseInsensitive) || tokenizedComment.contains("", Qt::CaseInsensitive) ) return BandPlan::BAND_MODE_FT8; if ( tokenizedComment.contains("FT4", Qt::CaseInsensitive) || tokenizedComment.contains("", Qt::CaseInsensitive) ) return BandPlan::BAND_MODE_FT4; if ( tokenizedComment.contains("FT2", Qt::CaseInsensitive) || tokenizedComment.contains("", Qt::CaseInsensitive ) ) return BandPlan::BAND_MODE_FT2; if ( tokenizedComment.contains("MSK144", Qt::CaseInsensitive) ) return BandPlan::BAND_MODE_DIGITAL; if ( tokenizedComment.contains("RTTY", Qt::CaseInsensitive) ) return BandPlan::BAND_MODE_DIGITAL; if ( tokenizedComment.contains("SSTV", Qt::CaseInsensitive) ) return BandPlan::BAND_MODE_DIGITAL; if ( tokenizedComment.contains("PACKET", Qt::CaseInsensitive) ) return BandPlan::BAND_MODE_DIGITAL; if ( tokenizedComment.contains("SSB", Qt::CaseInsensitive) || tokenizedComment.contains("", Qt::CaseInsensitive) ) return BandPlan::BAND_MODE_PHONE; if ( tokenizedComment.contains("USB", Qt::CaseInsensitive) ) return BandPlan::BAND_MODE_USB; if ( tokenizedComment.contains("LSB", Qt::CaseInsensitive) ) return BandPlan::BAND_MODE_LSB; if ( tokenizedComment.contains("", Qt::CaseInsensitive) ) return BandPlan::BAND_MODE_PHONE; return BandPlan::BAND_MODE_UNKNOWN; } QString DxWidget::refFromComment(const QString &comment, bool &flag, const QRegularExpression ®Ex, const QString &refType, int justified = 0) const { FCT_IDENTIFICATION; QRegularExpressionMatch stringMatch = regEx.match(comment); QString ref; if (stringMatch.hasMatch()) { flag = true; ref = stringMatch.captured(1).toUpper() + "-" + stringMatch.captured(2).rightJustified(justified, '0'); qCDebug(runtime) << refType << ":" << ref << "in comment:" << comment; } return ref; } void DxWidget::wwffRefFromComment(DxSpot &spot) const { FCT_IDENTIFICATION; static QRegularExpression wwffRegEx(QStringLiteral("(?:^|\\s)([A-Za-z0-9]{1,3}[Ff]{2})[- ]?(\\d{1,4})(?:\\s|$)"), QRegularExpression::CaseInsensitiveOption); spot.containsWWFF = spot.comment.contains("WWFF", Qt::CaseInsensitive); spot.wwffRef = refFromComment(spot.comment, spot.containsWWFF, wwffRegEx, QStringLiteral("WWFF"), 4); } void DxWidget::potaRefFromComment(DxSpot &spot) const { FCT_IDENTIFICATION; spot.containsPOTA = spot.comment.contains("POTA", Qt::CaseInsensitive); if ( spot.dxcc.dxcc == 0 ) return; QString flagA2Code = Data::instance()->dxccFlag(spot.dxcc.dxcc); if ( flagA2Code == "england" || flagA2Code == "scotland" || flagA2Code == "wales") flagA2Code = "GB"; QRegularExpression potaCountryRE(QString("(?:^|\\s)(%0)-(\\d{1,5})(?:\\s|@|$)").arg(flagA2Code), QRegularExpression::CaseInsensitiveOption); spot.potaRef = refFromComment(spot.comment, spot.containsPOTA, potaCountryRE, QStringLiteral("POTA_alternative"), 4); if ( !spot.containsPOTA ) { Callsign dxSpotCallsign(spot.callsign); // If POTA Info is not present in the comment, try to find it using POTAQE const QString &ref = PotaQE::instance()->findReferenceId(dxSpotCallsign, spot.freq).reference; if ( !ref.isEmpty() ) { spot.potaRef = ref; spot.containsPOTA = true; qCDebug(runtime) << "Found POTA" << spot.callsign << ref; spot.comment.append(" [+] POTA " + ref); } } } void DxWidget::sotaRefFromComment(DxSpot &spot) const { FCT_IDENTIFICATION; static QRegularExpression sotaRefRegEx(QStringLiteral("(?:^|\\s)([A-Za-z0-9]{1,3}/[A-Za-z]{2})-?(\\d{1,3})(?:\\s|$)"), QRegularExpression::CaseInsensitiveOption); spot.containsSOTA = spot.comment.contains("SOTA", Qt::CaseInsensitive); if ( spot.comment.contains("FT8", Qt::CaseInsensitive) // a false detection in case of TNX/FT8 comments || spot.comment.contains("FT4",Qt::CaseInsensitive) ) return; spot.sotaRef = refFromComment(spot.comment, spot.containsSOTA, sotaRefRegEx, QStringLiteral("SOTA"), 3); } void DxWidget::iotaRefFromComment(DxSpot &spot) const { FCT_IDENTIFICATION; spot.containsIOTA = spot.comment.contains("IOTA", Qt::CaseInsensitive); if ( spot.dxcc.cont.isEmpty() ) return; QRegularExpression iotaRegEx(QString("(?:^|\\s)(%0)[- ]?(\\d{1,3})(?:\\s|$)").arg(spot.dxcc.cont), QRegularExpression::CaseInsensitiveOption); spot.iotaRef = refFromComment(spot.comment, spot.containsIOTA, iotaRegEx, QStringLiteral("IOTA"), 3); } void DxWidget::splitFreqFromComment(DxSpot &spot) const { FCT_IDENTIFICATION; if ( spot.comment.isEmpty() || spot.freq <= 0.0 ) return; // Absolute TX frequency: "QSX 14250", "QSX 14.250", "LISTENING 28510", "LSN 28.510" static QRegularExpression absFreqRx(QStringLiteral("\\b(?:QSX|LISTENING|LSN)\\s+(\\d+\\.?\\d*)\\b"), QRegularExpression::CaseInsensitiveOption); QRegularExpressionMatch match = absFreqRx.match(spot.comment); if ( match.hasMatch() ) { double freq = match.captured(1).toDouble(); if ( freq > 1000.0 ) freq = freq / 1000.0; // kHz → MHz if ( freq > 0.0 ) { spot.freqTX = freq; qCDebug(runtime) << "Split absolute TX:" << spot.freqTX << "from:" << spot.comment; return; } } // Relative offset UP: "UP 5", "UP 5-10", "UP5" (kHz above spot freq) static QRegularExpression upNRx(QStringLiteral("\\bUP\\s*(\\d+(?:\\.\\d+)?)(?:\\s*[-/]\\s*\\d+(?:\\.\\d+)?)?\\b"), QRegularExpression::CaseInsensitiveOption); match = upNRx.match(spot.comment); if ( match.hasMatch() ) { double offsetKHz = match.captured(1).toDouble(); if ( offsetKHz > 0.0 ) { spot.freqTX = spot.freq + offsetKHz / 1000.0; qCDebug(runtime) << "Split UP" << offsetKHz << "kHz, TX:" << spot.freqTX; return; } } // Relative offset UP reversed: "1 UP", "5 UP", "5UP" static QRegularExpression nUpRx(QStringLiteral("\\b(\\d+(?:\\.\\d+)?)\\s*UP\\b"), QRegularExpression::CaseInsensitiveOption); match = nUpRx.match(spot.comment); if ( match.hasMatch() ) { double offsetKHz = match.captured(1).toDouble(); if ( offsetKHz > 0.0 ) { spot.freqTX = spot.freq + offsetKHz / 1000.0; qCDebug(runtime) << "Split" << offsetKHz << "UP kHz, TX:" << spot.freqTX; return; } } // Relative offset DOWN: "DN 5", "DOWN 5", "DWN 5" static QRegularExpression dnNRx(QStringLiteral("\\b(?:DN|DOWN|DWN)\\s*(\\d+(?:\\.\\d+)?)\\b"), QRegularExpression::CaseInsensitiveOption); match = dnNRx.match(spot.comment); if ( match.hasMatch() ) { double offsetKHz = match.captured(1).toDouble(); if ( offsetKHz > 0.0 ) { spot.freqTX = spot.freq - offsetKHz / 1000.0; qCDebug(runtime) << "Split DOWN" << offsetKHz << "kHz, TX:" << spot.freqTX; return; } } // Relative offset DOWN reversed: "5 DN", "5 DOWN" static QRegularExpression nDnRx(QStringLiteral("\\b(\\d+(?:\\.\\d+)?)\\s*(?:DN|DOWN|DWN)\\b"), QRegularExpression::CaseInsensitiveOption); match = nDnRx.match(spot.comment); if ( match.hasMatch() ) { double offsetKHz = match.captured(1).toDouble(); if ( offsetKHz > 0.0 ) { spot.freqTX = spot.freq - offsetKHz / 1000.0; qCDebug(runtime) << "Split" << offsetKHz << "DOWN kHz, TX:" << spot.freqTX; return; } } // Bare "UP" without number → 1 kHz default offset static QRegularExpression bareUpRx(QStringLiteral("\\bUP\\b"), QRegularExpression::CaseInsensitiveOption); if ( bareUpRx.match(spot.comment).hasMatch() ) { spot.freqTX = spot.freq + 1.0 / 1000.0; qCDebug(runtime) << "Split bare UP, TX:" << spot.freqTX; } } DxWidget::~DxWidget() { FCT_IDENTIFICATION; disconnectCluster(false); delete ui; } void DxWidget::finalizeBeforeAppExit() { FCT_IDENTIFICATION; saveWidgetSetting(); } ================================================ FILE: ui/DxWidget.h ================================================ #ifndef QLOG_UI_DXWIDGET_H #define QLOG_UI_DXWIDGET_H #include #include #include #include #include #include #include "data/DxSpot.h" #include "data/WCYSpot.h" #include "data/WWVSpot.h" #include "data/ToAllSpot.h" #include "core/LogLocale.h" #include "data/DxServerString.h" #include "models/SearchFilterProxyModel.h" #include "component/ShutdownAwareWidget.h" #include "NewContactWidget.h" // in sec #define DEDUPLICATION_TIME 3 // in kHz #define DEDUPLICATION_FREQ_TOLERANCE 5 namespace Ui { class DxWidget; } class DxTableModel : public QAbstractTableModel { Q_OBJECT public: DxTableModel(QObject* parent = 0) : QAbstractTableModel(parent) {} int rowCount(const QModelIndex& parent = QModelIndex()) const; int columnCount(const QModelIndex& parent = QModelIndex()) const; QVariant data(const QModelIndex& index, int role) const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; bool addEntry(const DxSpot &entry, bool deduplicate = false, qint16 dedup_interval = DEDUPLICATION_TIME, double dedup_freq_tolerance = DEDUPLICATION_FREQ_TOLERANCE); const DxSpot getSpot(const QModelIndex& index) const {return dxData.at(index.row());}; void clear(); private: QList dxData; LogLocale locale; }; class WCYTableModel : public QAbstractTableModel { Q_OBJECT public: WCYTableModel(QObject* parent = 0) : QAbstractTableModel(parent) {} int rowCount(const QModelIndex& parent = QModelIndex()) const; int columnCount(const QModelIndex& parent = QModelIndex()) const; QVariant data(const QModelIndex& index, int role) const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; void addEntry(const WCYSpot &entry); void clear(); private: QList wcyData; LogLocale locale; }; class WWVTableModel : public QAbstractTableModel { Q_OBJECT public: WWVTableModel(QObject* parent = 0) : QAbstractTableModel(parent) {} int rowCount(const QModelIndex& parent = QModelIndex()) const; int columnCount(const QModelIndex& parent = QModelIndex()) const; QVariant data(const QModelIndex& index, int role) const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; void addEntry(const WWVSpot &entry); void clear(); private: QList wwvData; LogLocale locale; }; class ToAllTableModel : public QAbstractTableModel { Q_OBJECT public: ToAllTableModel(QObject* parent = 0) : QAbstractTableModel(parent) {} int rowCount(const QModelIndex& parent = QModelIndex()) const; int columnCount(const QModelIndex& parent = QModelIndex()) const; QVariant data(const QModelIndex& index, int role) const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; void addEntry(const ToAllSpot &entry); void clear(); private: QList toAllData; LogLocale locale; }; class DeleteHighlightedDXServerWhenDelPressedEventFilter : public QObject { Q_OBJECT signals: void deleteServerItem(); public: DeleteHighlightedDXServerWhenDelPressedEventFilter(QObject *parent) : QObject(parent) {}; protected: bool eventFilter(QObject *obj, QEvent *event); }; class DxWidget : public QWidget, public ShutdownAwareWidget { Q_OBJECT public: explicit DxWidget(QWidget *parent = 0); ~DxWidget(); virtual void finalizeBeforeAppExit() override; void registerContactWidget(const NewContactWidget * contactWidget); public slots: void toggleConnect(); void receive(); void send(); void connected(); void socketError(QAbstractSocket::SocketError); void viewModeChanged(int); void entryDoubleClicked(QModelIndex); void actionFilter(); void adjusteServerSelectSize(QString); void serverSelectChanged(int); void setLastQSO(QSqlRecord); void reloadSetting(); void prepareQSOSpot(QSqlRecord); void setSearch(const QString &); void setSearchStatus(bool); void setSearchVisible(); void setSearchClosed(); void setDxTrend(QHash>>); void recalculateTrend(); void setTunedFrequency(VFOID vfoid, double vfoFreq, double ritFreq, double xitFreq); private slots: void actionCommandSpotQSO(); void actionCommandShowHFStats(); void actionCommandShowVHFStats(); void actionCommandShowWCY(); void actionCommandShowWWV(); void actionConnectOnStartup(); void actionDeleteServer(); void actionForgetPassword(); void actionKeepSpots(); void actionClear(); void displayedColumns(); void trendDoubleClicked(int row, int column); signals: void tuneDx(DxSpot); void tuneBand(QString); void newSpot(DxSpot); void newWCYSpot(WCYSpot); void newWWVSpot(WWVSpot); void newToAllSpot(ToAllSpot); void newFilteredSpot(DxSpot); private: enum DXCConnectionState { DISCONNECTED = 0, CONNECTING = 1, CONNECTED = 2, LOGIN_SENT = 3, PASSWORD_SENT = 4, OPERATION = 5 }; enum DXCType { UNKNOWN = 0, DXSPIDER = 1, CCCluster = 2 }; DxTableModel* dxTableModel; WCYTableModel* wcyTableModel; WWVTableModel* wwvTableModel; ToAllTableModel* toAllTableModel; SearchFilterProxyModel* dxTableProxyModel; QTcpSocket* socket; Ui::DxWidget *ui; QRegularExpression moderegexp; QRegularExpression contregexp; QRegularExpression spottercontregexp; QRegularExpression bandregexp; uint dxccStatusFilter; bool deduplicateSpots; int deduplicatetime; int deduplicatefreq; DXCType dxcType = UNKNOWN; QMenu *commandsMenu; QSet dxMemberFilter; QSqlRecord lastQSO; quint8 reconnectAttempts; QTimer reconnectTimer; DXCConnectionState connectionState; DxServerString *connectedServerString; QHash>> receivedTrendData; QHash> prevTrendDataForMyCont; QHash> trendDataForMyCont; QStringList trendBandList; QLabel *trendTableCornerLabel; const NewContactWidget *newContactWidget; void connectCluster(); void disconnectCluster(bool tryReconnect = false); void saveDXCServers(); QString modeFilterRegExp(); QString contFilterRegExp(); QString spotterContFilterRegExp(); QString bandFilterRegExp(); uint dxccStatusFilterValue(); bool spotDedupValue(); int getDedupTimeValue(); int getDedupFreqValue(); QStringList dxMemberList(); bool getAutoconnectServer(); void saveAutoconnectServer(bool); bool getKeepQSOs(); void saveKeepQSOs(bool); void sendCommand(const QString&, bool switchToConsole = false); void saveWidgetSetting(); void restoreWidgetSetting(); QStringList getDXCServerList(void); void serverComboSetup(); void clearAllPasswordIcons(); void activateCurrPasswordIcon(); void updateCommandsMenu(); void processDxSpot(const QString &spotter, const QString &freq, const QString &call, const QString &comment, const QDateTime &dateTime = QDateTime()); QVector dxcListHiddenCols() const; BandPlan::BandPlanMode modeGroupFromComment(const QString &comment) const; QString refFromComment(const QString &comment, bool &flag, const QRegularExpression ®Ex, const QString &refType, int justified) const; void wwffRefFromComment(DxSpot &spot) const; void potaRefFromComment(DxSpot &spot) const; void sotaRefFromComment(DxSpot &spot) const; void iotaRefFromComment(DxSpot &spot) const; void splitFreqFromComment(DxSpot &spot) const; QColor getHeatmapColor(int value, int maxValue); bool isFilterEnabled() const; }; #endif // QLOG_UI_DXWIDGET_H ================================================ FILE: ui/DxWidget.ui ================================================ DxWidget 0 0 427 511 Qt::NoFocus Form 0 0 0 0 0 0 0 0 Qt::ClickFocus Qt::ActionsContextMenu ::menu-indicator{ image: none; } :/icons/menu.svg:/icons/menu.svg Qt::ClickFocus Qt::DefaultContextMenu Insert a <b>hostname:port</b> of DXC Server. Qt::ImhNone true QComboBox::InsertAtTop QComboBox::AdjustToContents 0 0 Qt::ClickFocus Connect 0 0 color:red Filtered Qt::Horizontal 40 20 0 0 Qt::ClickFocus Spots WCY WWV To All Console 15-min Trend 0 0 0 0 0 Qt::ClickFocus Qt::ActionsContextMenu QAbstractItemView::AnyKeyPressed|QAbstractItemView::DoubleClicked false false false true QAbstractItemView::SingleSelection QAbstractItemView::SelectRows QAbstractItemView::ScrollPerPixel QAbstractItemView::ScrollPerPixel 24 80 false true false 20 20 0 Qt::ClickFocus Full-text search Search true Close Search :/icons/cancel-24px.svg:/icons/cancel-24px.svg true 0 0 0 0 Qt::ActionsContextMenu QAbstractItemView::NoEditTriggers false false true QAbstractItemView::SingleSelection QAbstractItemView::SelectRows QAbstractItemView::ScrollPerPixel QAbstractItemView::ScrollPerItem 40 70 true true false 20 20 0 0 0 0 Qt::ActionsContextMenu QAbstractItemView::NoEditTriggers false false false true QAbstractItemView::SingleSelection QAbstractItemView::SelectRows QAbstractItemView::ScrollPerPixel QAbstractItemView::ScrollPerPixel 70 true false 0 0 0 0 Qt::ActionsContextMenu QAbstractItemView::NoEditTriggers false false false true QAbstractItemView::SingleSelection QAbstractItemView::SelectRows QAbstractItemView::ScrollPerPixel QAbstractItemView::ScrollPerPixel 70 true false Qt::ActionsContextMenu 0 0 0 0 12 true Qt::ClickFocus true 0 0 0 0 Qt::NoFocus QAbstractItemView::NoEditTriggers false false false QAbstractItemView::NoSelection QAbstractItemView::ScrollPerPixel QAbstractItemView::ScrollPerPixel 0 false Qt::ClickFocus Send DX Cluster Command true false 0 0 Qt::NoFocus QToolButton::MenuButtonPopup Qt::ToolButtonTextOnly :/icons/filter_list-24px.svg:/icons/filter_list-24px.svg Filter... Filter DXC Spot Last QSO Send last QSO spot Show HF Stats Show VHF Stats Show WCY Show WWV Column Visibility... Which columns should be displayed true Connect on startup Automatic connection after start Delete Server DXC - Delete Server Clear Password true Keep Spots Spots are not cleared when connecting to a new DX Cluster. Clear Clear all data Search... DXC - Search Ctrl+D Qt::ApplicationShortcut true connectButton clicked() DxWidget toggleConnect() 172 25 367 149 commandEdit returnPressed() DxWidget send() 249 509 367 149 dxTable doubleClicked(QModelIndex) DxWidget entryDoubleClicked(QModelIndex) 340 272 456 255 actionFilter triggered() DxWidget actionFilter() -1 -1 456 255 serverSelect editTextChanged(QString) DxWidget adjusteServerSelectSize(QString) 44 25 456 255 serverSelect currentIndexChanged(int) DxWidget serverSelectChanged(int) 44 25 456 255 actionSpotQSO triggered() DxWidget actionCommandSpotQSO() -1 -1 170 255 actionShowHFStats triggered() DxWidget actionCommandShowHFStats() -1 -1 170 255 actionShowVHFStats triggered() DxWidget actionCommandShowVHFStats() -1 -1 170 255 actionShowWCY triggered() DxWidget actionCommandShowWCY() -1 -1 170 255 actionShowWWV triggered() DxWidget actionCommandShowWWV() -1 -1 170 255 viewModeCombo currentIndexChanged(int) DxWidget viewModeChanged(int) 339 25 170 255 actionDisplayedColumns triggered() DxWidget displayedColumns() -1 -1 170 255 actionConnectOnStartup changed() DxWidget actionConnectOnStartup() -1 -1 170 255 actionDeleteServer triggered() DxWidget actionDeleteServer() -1 -1 170 255 actionForgetPassword triggered() DxWidget actionForgetPassword() -1 -1 170 255 actionKeepSpots triggered() DxWidget actionKeepSpots() -1 -1 170 255 actionClear triggered() DxWidget actionClear() -1 -1 170 255 actionSearch triggered() DxWidget setSearchVisible() -1 -1 170 255 searchCloseButton clicked() DxWidget setSearchClosed() 325 462 170 255 searchEdit textChanged(QString) DxWidget setSearch(QString) 170 463 170 255 trendTable cellDoubleClicked(int,int) DxWidget trendDoubleClicked(int,int) 170 255 170 255 send() toggleConnect() viewModeChanged(int) entryDoubleClicked(QModelIndex) actionFilter() actionPhoneFilter() actionFT8Filter() actionPhoneFilter(bool) adjusteServerSelectSize(QString) serverSelectChanged(int) actionCommandSpotQSO() actionCommandShowHFStats() actionCommandShowVHFStats() actionCommandShowWCY() actionCommandShowWWV() displayedColumns() actionConnectOnStartup() actionDeleteServer() actionForgetPassword() actionKeepSpots() actionClear() setSearch(QString) setSearchVisible() setSearchClosed() trendDoubleClicked(int,int) ================================================ FILE: ui/DxccTableWidget.cpp ================================================ #include #include #include #include #include #include #include "models/DxccTableModel.h" #include "DxccTableWidget.h" #include "core/debug.h" #include "data/StationProfile.h" #include "data/BandPlan.h" MODULE_IDENTIFICATION("qlog.ui.dxcctablewidget"); DxccTableWidget::DxccTableWidget(QWidget *parent) : QTableView(parent) { FCT_IDENTIFICATION; dxccTableModel = new DxccTableModel(parent); this->setObjectName("dxccTableView"); this->setModel(dxccTableModel); this->verticalHeader()->setVisible(false); } void DxccTableWidget::clear() { FCT_IDENTIFICATION; dxccTableModel->clear(); dxccTableModel->setQuery(QString()); setHidden(true); } void DxccTableWidget::updateDxTable(const QString &condition, const QVariant &conditionValue, const Band &highlightedBand) { FCT_IDENTIFICATION; qCDebug(function_parameters) << condition << conditionValue; const QList& dxccBands = BandPlan::bandsList(false, true); if ( dxccBands.isEmpty() ) return; QString filter(QLatin1String("1 = 1")); StationProfile profile = StationProfilesManager::instance()->getCurProfile1(); QStringList stmt_band_part1; QStringList stmt_band_part2; if ( profile != StationProfile() ) filter.append(QString(" AND c.my_dxcc = %1").arg(profile.dxcc)); for ( const Band &band : dxccBands ) { stmt_band_part1 << QString(" MAX(CASE WHEN band = '%0' THEN CASE WHEN (eqsl_qsl_rcvd = 'Y') THEN 2 ELSE 1 END ELSE 0 END) as '%0_eqsl'," " MAX(CASE WHEN band = '%0' THEN CASE WHEN (lotw_qsl_rcvd = 'Y') THEN 2 ELSE 1 END ELSE 0 END) as '%0_lotw'," " MAX(CASE WHEN band = '%0' THEN CASE WHEN (qsl_rcvd = 'Y') THEN 2 ELSE 1 END ELSE 0 END) as '%0_paper' ") .arg(band.name); stmt_band_part2 << QString(" c.'%0_eqsl' || c.'%0_lotw'|| c.'%0_paper' as '%0'").arg(band.name); } QString stmt = QString("WITH dxcc_summary AS " " (" " SELECT " " m.dxcc , " " %1 " " FROM contacts c" " LEFT OUTER JOIN modes m on c.mode = m.name" " WHERE %2 AND %3 GROUP BY m.dxcc ) " " SELECT translate_to_locale(m.dxcc)," " %4 " " FROM (SELECT DISTINCT dxcc" " FROM modes) m" " LEFT OUTER JOIN dxcc_summary c ON c.dxcc = m.dxcc " " ORDER BY m.dxcc").arg(stmt_band_part1.join(","), filter, condition.arg(conditionValue.toString()), stmt_band_part2.join(",")); qCDebug(runtime) << stmt; dxccTableModel->setQuery(stmt); // get default Brush from Mode column - Mode Column has always the default color const QVariant &defaultBrush = dxccTableModel->headerData(0, Qt::Horizontal, Qt::BackgroundRole); dxccTableModel->setHeaderData(0, Qt::Horizontal, tr("Mode")); for ( int i = 0; i < dxccBands.size(); i++ ) { dxccTableModel->setHeaderData(i+1, Qt::Horizontal, ( highlightedBand == dxccBands.at(i) ) ? QBrush(Qt::darkGray) : defaultBrush, Qt::BackgroundRole); dxccTableModel->setHeaderData(i+1, Qt::Horizontal, dxccBands.at(i).name); } horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents); show(); } void DxccTableWidget::setDxCallsign(const QString &dxCallsign, const Band &band) { FCT_IDENTIFICATION; qCDebug(function_parameters) << dxCallsign; if (!dxCallsign.isEmpty()) updateDxTable("c.callsign = '%1'", dxCallsign.toUpper(), band); else clear(); } void DxccTableWidget::setDxcc(int dxcc, const Band &highlightedBand) { FCT_IDENTIFICATION; qCDebug(function_parameters) << dxcc; if ( dxcc ) updateDxTable("c.dxcc = %1", dxcc, highlightedBand); else clear(); } ================================================ FILE: ui/DxccTableWidget.h ================================================ #ifndef QLOG_UI_DXCCTABLEWIDGET_H #define QLOG_UI_DXCCTABLEWIDGET_H #include #include #include "data/Band.h" class DxccTableModel; class DxccTableWidget : public QTableView { Q_OBJECT public: explicit DxccTableWidget(QWidget *parent = nullptr); public slots: void clear(); void setDxcc(int dxcc, const Band &highlightedBand); void setDxCallsign(const QString &dxCallsign, const Band &band); private: void updateDxTable(const QString &condition, const QVariant &conditionValue, const Band &highlightedBand); DxccTableModel* dxccTableModel; }; #endif // QLOG_UI_DXCCTABLEWIDGET_H ================================================ FILE: ui/EditActivitiesDialog.cpp ================================================ #include "EditActivitiesDialog.h" #include "ui_EditActivitiesDialog.h" #include "core/debug.h" #include "ui/EditActivitiesDialog.h" #include "ui/ActivityEditor.h" #include "data/ActivityProfile.h" MODULE_IDENTIFICATION("qlog.ui.EditLayoutDialog"); EditActivitiesDialog::EditActivitiesDialog(QWidget *parent) : QDialog(parent), ui(new Ui::EditActivitiesDialog) { FCT_IDENTIFICATION; ui->setupUi(this); loadProfiles(); } EditActivitiesDialog::~EditActivitiesDialog() { FCT_IDENTIFICATION; delete ui; } void EditActivitiesDialog::loadProfiles() { FCT_IDENTIFICATION; ui->listView->setModel(new QStringListModel(ActivityProfilesManager::instance()->profileNameList(), this)); } void EditActivitiesDialog::addButton() { FCT_IDENTIFICATION; ActivityEditor dialog(QString(), this); dialog.exec(); loadProfiles(); } void EditActivitiesDialog::removeButton() { FCT_IDENTIFICATION; const QString &removeProfileName = ui->listView->currentIndex().data().toString(); ActivityProfilesManager::instance()->removeProfile(removeProfileName); ActivityProfilesManager::instance()->save(); MainLayoutProfilesManager::instance()->removeProfile(removeProfileName); MainLayoutProfilesManager::instance()->save(); loadProfiles(); } void EditActivitiesDialog::editEvent(const QModelIndex &idx) { FCT_IDENTIFICATION; ActivityEditor dialog(ui->listView->model()->data(idx).toString(), this); dialog.exec(); } void EditActivitiesDialog::editButton() { FCT_IDENTIFICATION; const QModelIndexList &selected = ui->listView->selectionModel()->selectedIndexes(); if (!selected.isEmpty()) editEvent(selected.first()); } ================================================ FILE: ui/EditActivitiesDialog.h ================================================ #ifndef QLOG_UI_EDITACTIVITIESDIALOG_H #define QLOG_UI_EDITACTIVITIESDIALOG_H #include #include namespace Ui { class EditActivitiesDialog; } class EditActivitiesDialog : public QDialog { Q_OBJECT public: explicit EditActivitiesDialog(QWidget *parent = nullptr); ~EditActivitiesDialog(); private: Ui::EditActivitiesDialog *ui; void loadProfiles(); public slots: void addButton(); void removeButton(); void editEvent(const QModelIndex &); void editButton(); }; #endif // QLOG_UI_EDITACTIVITIESDIALOG_H ================================================ FILE: ui/EditActivitiesDialog.ui ================================================ EditActivitiesDialog 0 0 357 258 Edit Activities Activities QAbstractItemView::NoEditTriggers Qt::Vertical 20 40 Add Edit Remove Qt::Vertical 20 40 Qt::Horizontal QDialogButtonBox::Ok buttonBox accepted() EditActivitiesDialog accept() 248 254 157 274 buttonBox rejected() EditActivitiesDialog reject() 316 260 286 274 addButton clicked() EditActivitiesDialog addButton() 485 29 269 208 removeButton clicked() EditActivitiesDialog removeButton() 485 70 269 208 listView doubleClicked(QModelIndex) EditActivitiesDialog editEvent(QModelIndex) 224 202 269 208 editButton clicked() EditActivitiesDialog editButton() 485 188 269 208 addButton() removeButton() editEvent(QModelIndex) editButton() ================================================ FILE: ui/ExportDialog.cpp ================================================ #include "ui/ExportDialog.h" #include #include #include #include #include #include "ui_ExportDialog.h" #include #include "core/debug.h" #include "models/SqlListModel.h" #include "data/StationProfile.h" #include "ui/ColumnSettingDialog.h" #include "data/Data.h" #include "core/LogParam.h" #include "core/QSOFilterManager.h" MODULE_IDENTIFICATION("qlog.ui.exportdialog"); ExportDialog::ExportDialog(QWidget *parent) : ExportDialog(QList(), parent) { FCT_IDENTIFICATION; this->setWindowTitle(tr("Export QSOs")); ui->myCallsignComboBox->setModel(new SqlListModel("SELECT DISTINCT UPPER(station_callsign) FROM contacts ORDER BY station_callsign", "", ui->myCallsignComboBox)); ui->myCallsignComboBox->setCurrentText(StationProfilesManager::instance()->getCurProfile1().callsign.toUpper()); ui->myGridComboBox->setModel(new SqlListModel("SELECT DISTINCT UPPER(my_gridsquare) FROM contacts WHERE station_callsign ='" + QString(ui->myCallsignComboBox->currentText()).replace("'", "''") + "' ORDER BY my_gridsquare", "", ui->myGridComboBox)); ui->startDateEdit->setDisplayFormat(locale.formatDateShortWithYYYY()); ui->startDateEdit->setDate(QDate::currentDate()); ui->endDateEdit->setDisplayFormat(locale.formatDateShortWithYYYY()); ui->endDateEdit->setDate(QDate::currentDate().addDays(1)); ui->userFilterComboBox->setModel(QSOFilterManager::QSOFilterModel("", ui->userFilterComboBox)); ui->userFilterCheckBox->setEnabled(ui->userFilterComboBox->count() > 0); const StationProfile &profile = StationProfilesManager::instance()->getCurProfile1(); ui->myStationProfileComboBox->setModel(new SqlListModel("SELECT DISTINCT profile_name FROM station_profiles ORDER BY profile_name", "", ui->myStationProfileComboBox)); int index = ui->myStationProfileComboBox->findText(profile.profileName); ui->myStationProfileComboBox->setCurrentIndex( ( index >= 0 ) ? index : -1); } ExportDialog::ExportDialog(const QList& qsos, QWidget *parent) : QDialog(parent), ui(new Ui::ExportDialog), qsos4export(qsos) { FCT_IDENTIFICATION; ui->setupUi(this); ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("&Export")); if ( qsos4export.size() > 0 ) { ui->filterGroup->setVisible(false); ui->addlSentStatusCheckbox->setVisible(false); ui->addlSentStatusICheckBox->setVisible(false); ui->addlSentStatusNCheckBox->setVisible(false); ui->addlSentStatusAlreadySentCheckBox->setVisible(false); adjustSize(); } fillQSLSendViaCombo(); fillExportTypeCombo(); fillExportedColumnsCombo(); // not posibility to define Exported Columns here } void ExportDialog::browse() { FCT_IDENTIFICATION; QSettings settings; //platform-dependent, must be present const QString &lastPath = ( ui->fileEdit->text().isEmpty() ) ? settings.value("export/last_path", QDir::homePath()).toString() : ui->fileEdit->text(); QString filename = QFileDialog::getSaveFileName(this, nullptr, lastPath); if ( !filename.isEmpty() ) { settings.setValue("export/last_path", QFileInfo(filename).path()); ui->fileEdit->setText(filename); } } void ExportDialog::toggleDateRange() { FCT_IDENTIFICATION; ui->startDateEdit->setEnabled(ui->dateRangeCheckBox->isChecked()); ui->endDateEdit->setEnabled(ui->dateRangeCheckBox->isChecked()); } void ExportDialog::toggleMyCallsign() { FCT_IDENTIFICATION; ui->myCallsignComboBox->setEnabled(ui->myCallsignCheckBox->isChecked()); if ( ui->myCallsignCheckBox->isChecked() ) ui->myStationProfileCheckbox->setChecked(false); } void ExportDialog::toggleMyGridsquare() { FCT_IDENTIFICATION; ui->myGridComboBox->setEnabled(ui->myGridCheckBox->isChecked()); if ( ui->myGridCheckBox->isChecked() ) ui->myStationProfileCheckbox->setChecked(false); } void ExportDialog::toggleQslSendVia() { FCT_IDENTIFICATION; ui->qslSendViaComboBox->setEnabled(ui->qslSendViaCheckbox->isChecked()); } void ExportDialog::toggleSentStatus() { FCT_IDENTIFICATION; ui->addlSentStatusICheckBox->setEnabled(ui->addlSentStatusCheckbox->isChecked()); ui->addlSentStatusNCheckBox->setEnabled(ui->addlSentStatusCheckbox->isChecked()); ui->addlSentStatusAlreadySentCheckBox->setEnabled(ui->addlSentStatusCheckbox->isChecked()); } void ExportDialog::toggleUserFilter() { FCT_IDENTIFICATION; ui->userFilterComboBox->setEnabled(ui->userFilterCheckBox->isChecked()); } void ExportDialog::toggleStationProfile() { FCT_IDENTIFICATION; bool isStationProfileActive = ui->myStationProfileCheckbox->isChecked(); ui->myStationProfileComboBox->setEnabled(isStationProfileActive); if ( isStationProfileActive ) { ui->myCallsignCheckBox->setChecked(false); ui->myGridCheckBox->setChecked(false); } onStationProfileChange(); } void ExportDialog::onStationProfileChange() { FCT_IDENTIFICATION; const StationProfile &selectedStationProfile = StationProfilesManager::instance()->getProfile(ui->myStationProfileComboBox->currentText()); if ( ui->myStationProfileCheckbox->isChecked() ) { QString toolTip = tr("Export only QSOs matching this station profile") + "
" + selectedStationProfile.toHTMLString(); ui->myStationProfileComboBox->setToolTip(toolTip); } else ui->myStationProfileComboBox->setToolTip({}); } void ExportDialog::runExport() { FCT_IDENTIFICATION; if ( ui->fileEdit->text().isEmpty() ) { QMessageBox::warning(nullptr, QMessageBox::tr("QLog Warning"), QMessageBox::tr("Filename is empty")); return; } QFile file(ui->fileEdit->text()); if ( ! file.open(QFile::WriteOnly | QFile::Text) ) { QMessageBox::critical(nullptr, QMessageBox::tr("QLog Error"), QMessageBox::tr("Cannot write to the file")); return; } QTextStream out(&file); LogFormat *format = LogFormat::open(ui->typeSelect->currentText(), out); if ( !format ) { qCritical() << "unknown log format"; return; } PotaAdiFormat *potaFormat = dynamic_cast(format); if ( potaFormat ) { potaFormat->setPotaOnly(true); potaFormat->setExportDirectory(QFileInfo(file).canonicalPath()); } if ( ui->dateRangeCheckBox->isChecked() ) format->setFilterDateRange(ui->startDateEdit->date(), ui->endDateEdit->date()); if ( ui->myStationProfileCheckbox->isChecked() ) { const StationProfile &profile = StationProfilesManager::instance()->getProfile( ui->myStationProfileComboBox->currentText()); format->setFilterStationProfile(profile); } if ( ui->myCallsignCheckBox->isChecked() ) format->setFilterMyCallsign(ui->myCallsignComboBox->currentText()); if ( ui->myGridCheckBox->isChecked() ) format->setFilterMyGridsquare(ui->myGridComboBox->currentText()); if ( ui->userFilterCheckBox->isChecked() ) format->setUserFilter(ui->userFilterComboBox->currentText()); if ( ui->exportTypeCombo->currentData() == "qsl" && ui->qslSendViaCheckbox->isChecked() ) format->setFilterSendVia(ui->qslSendViaComboBox->currentData().toString()); if ( ui->exportTypeCombo->currentData() == "qsl" ) { format->setFilterSentPaperQSL((ui->addlSentStatusCheckbox->isChecked()) ? ui->addlSentStatusNCheckBox->isChecked() : false, (ui->addlSentStatusCheckbox->isChecked()) ? ui->addlSentStatusICheckBox->isChecked() : false, (ui->addlSentStatusCheckbox->isChecked()) ? ui->addlSentStatusAlreadySentCheckBox->isChecked() : false); } if ( exportedColumns.count() > 0 ) { //translate column indexes to SQL column names QSetIterator i(exportedColumns); QSqlRecord record = logbookmodel.record(); QStringList fields; while ( i.hasNext() ) { fields << record.fieldName(i.next()); } format->setExportedFields(fields); } // Modal progress dialog QProgressDialog progressDialog(tr("Exporting..."), tr("Cancel"), 0, 100, this); progressDialog.setWindowModality(Qt::WindowModal); progressDialog.setMinimumDuration(0); connect(format, &LogFormat::exportProgress, &progressDialog, &QProgressDialog::setValue); long count = 0L; if ( qsos4export.size() > 0 ) { count = format->runExport(qsos4export); } else { count = format->runExport(); if ( count > 0 && ui->exportTypeCombo->currentData() == "qsl" && ui->markAsSentCheckbox->isChecked() ) { if ( ! markQSOAsSent(format) ) { QMessageBox::warning(this, tr("QLog Error"), tr("Cannot mark exported QSOs as Sent")); } } } progressDialog.close(); delete format; if ( potaFormat && qsos4export.size() > 0 ) // TODO: correctly calculate // the exported QSOs in case of POTA formatter and direct export dialog QMessageBox::information(nullptr, QMessageBox::tr("QLog Information"), QMessageBox::tr("Exported.")); else QMessageBox::information(nullptr, QMessageBox::tr("QLog Information"), QMessageBox::tr("Exported %n contact(s).", "", count)); accept(); } void ExportDialog::myCallsignChanged(const QString &myCallsign) { FCT_IDENTIFICATION; ui->myGridComboBox->setModel(new SqlListModel("SELECT DISTINCT UPPER(my_gridsquare) FROM contacts WHERE station_callsign ='" + QString(myCallsign).replace("'", "''") + "' ORDER BY my_gridsquare", "", ui->myGridComboBox)); } void ExportDialog::showColumnsSetting() { FCT_IDENTIFICATION; // don't want to export QSO ID because it is not a ADIF field QList excludeFilter({LogbookModel::COLUMN_ID}); ColumnSettingDialog dialog(&logbookmodel, exportedColumns, this, excludeFilter); connect(&dialog, &ColumnSettingDialog::columnChanged, this, &ExportDialog::exportedColumnStateChanged); dialog.exec(); } void ExportDialog::exportedColumnStateChanged(int index, bool state) { FCT_IDENTIFICATION; qCDebug(function_parameters) << index << state; if ( state ) exportedColumns.insert(index); else exportedColumns.remove(index); const QString &comboValue = ui->exportedColumnsCombo->itemData(ui->exportedColumnsCombo->currentIndex()).toString(); LogParam::setExportColumnSet(comboValue, exportedColumns); } void ExportDialog::fillExportTypeCombo() { FCT_IDENTIFICATION; ui->exportTypeCombo->addItem(tr("Generic"), "generic"); if ( qsos4export.size() == 0 ) { ui->exportTypeCombo->addItem(tr("QSLs"), "qsl"); } } void ExportDialog::exportTypeChanged(int index) { FCT_IDENTIFICATION; QString comboValue = ui->exportTypeCombo->itemData(index).toString(); if ( comboValue != "qsl" ) { ui->addlSentStatusCheckbox->setVisible(false); ui->addlSentStatusICheckBox->setVisible(false); ui->addlSentStatusNCheckBox->setVisible(false); ui->addlSentStatusAlreadySentCheckBox->setVisible(false); ui->qslSendViaCheckbox->setVisible(false); ui->qslSendViaComboBox->setVisible(false); ui->markAsSentCheckbox->setVisible(false); ui->exportedColumnsCombo->setCurrentIndex(ui->exportedColumnsCombo->findData("all")); } else { ui->addlSentStatusCheckbox->setVisible(true); ui->addlSentStatusICheckBox->setVisible(true); ui->addlSentStatusNCheckBox->setVisible(true); ui->addlSentStatusAlreadySentCheckBox->setVisible(true); ui->qslSendViaCheckbox->setVisible(true); ui->qslSendViaComboBox->setVisible(true); ui->markAsSentCheckbox->setVisible(true); ui->exportedColumnsCombo->setCurrentIndex(ui->exportedColumnsCombo->findData("qsl")); } adjustSize(); } void ExportDialog::fillExportedColumnsCombo() { FCT_IDENTIFICATION; ui->exportedColumnsCombo->addItem(tr("All"), "all"); ui->exportedColumnsCombo->addItem(tr("Minimal"), "min"); ui->exportedColumnsCombo->addItem(tr("POTA"), "pota"); ui->exportedColumnsCombo->addItem(tr("QSL-specific"), "qsl"); ui->exportedColumnsCombo->addItem(tr("Custom 1"), "c1"); ui->exportedColumnsCombo->addItem(tr("Custom 2"), "c2"); ui->exportedColumnsCombo->addItem(tr("Custom 3"), "c3"); } bool ExportDialog::markQSOAsSent(LogFormat *format) { FCT_IDENTIFICATION; if ( !format ) { return false; } QString updateQuery = "UPDATE contacts " "SET qsl_sent='Y', qsl_sdate = strftime('%Y-%m-%d',DATETIME('now', 'utc')) WHERE " + format->getWhereClause(); qCDebug(runtime) << updateQuery ; QSqlQuery query_update; if ( ! query_update.prepare(updateQuery) ) { qWarning() << "Cannot mark exported QSO as Sent - prepare error " << query_update.lastError().text(); return false; } format->bindWhereClause(query_update); if ( ! query_update.exec() ) { qWarning() << "Cannot mark exported QSO as Sent - execute error " << query_update.lastError().text(); return false; } return true; } void ExportDialog::exportedColumnsComboChanged(int index) { FCT_IDENTIFICATION; const QString &comboValue = ui->exportedColumnsCombo->itemData(index).toString(); //empty set means all values exported exportedColumns = QSet(); if ( comboValue == "all" ) { ui->exportedColumnsButton->setEnabled(false); } else { ui->exportedColumnsButton->setEnabled(true); if ( comboValue == "min" || comboValue == "c1" || comboValue == "c2" || comboValue == "c3" ) { exportedColumns = LogParam::getExportColumnSet(comboValue, minColumns); } else if ( comboValue == "qsl" ) { exportedColumns = LogParam::getExportColumnSet(comboValue, qslColumns); } else if ( comboValue == "pota" ) { exportedColumns = potaColumns; } } } void ExportDialog::fillQSLSendViaCombo() { FCT_IDENTIFICATION; QMapIterator iter(Data::instance()->qslSentViaEnum); while (iter.hasNext()) { iter.next(); ui->qslSendViaComboBox->addItem(iter.value(), iter.key()); } } void ExportDialog::exportFormatChanged(const QString &format) { FCT_IDENTIFICATION; if ( format == "POTA" ) { ui->exportedColumnsCombo->setCurrentIndex(ui->exportedColumnsCombo->findData("pota")); ui->exportTypeCombo->setEnabled(false); } else { ui->exportedColumnsCombo->setCurrentIndex(ui->exportedColumnsCombo->findData("all")); ui->exportTypeCombo->setEnabled(true); } } ExportDialog::~ExportDialog() { FCT_IDENTIFICATION; delete ui; } ================================================ FILE: ui/ExportDialog.h ================================================ #ifndef QLOG_UI_EXPORTDIALOG_H #define QLOG_UI_EXPORTDIALOG_H #include #include #include #include #include "core/LogLocale.h" #include "models/LogbookModel.h" #include "logformat/LogFormat.h" namespace Ui { class ExportDialog; } class ExportDialog : public QDialog { Q_OBJECT public: explicit ExportDialog(QWidget *parent = nullptr); explicit ExportDialog(const QList&, QWidget *parent = nullptr); ~ExportDialog(); public slots: void browse(); void toggleDateRange(); void toggleMyCallsign(); void toggleMyGridsquare(); void toggleQslSendVia(); void toggleSentStatus(); void toggleUserFilter(); void toggleStationProfile(); void onStationProfileChange(); void runExport(); void myCallsignChanged(const QString &myCallsign); void showColumnsSetting(); void exportedColumnStateChanged(int index, bool state); void exportTypeChanged(int index); void exportedColumnsComboChanged(int); void exportFormatChanged(const QString &format); private: Ui::ExportDialog *ui; LogLocale locale; QSet exportedColumns; const QSet minColumns{ LogbookModel::COLUMN_TIME_ON, LogbookModel::COLUMN_CALL, LogbookModel::COLUMN_FREQUENCY, LogbookModel::COLUMN_MODE, LogbookModel::COLUMN_SUBMODE }; const QSet qslColumns{ LogbookModel::COLUMN_TIME_ON, LogbookModel::COLUMN_CALL, LogbookModel::COLUMN_FREQUENCY, LogbookModel::COLUMN_MODE, LogbookModel::COLUMN_SUBMODE, LogbookModel::COLUMN_RST_SENT, LogbookModel::COLUMN_RST_RCVD }; const QSet potaColumns{ LogbookModel::COLUMN_TIME_ON, LogbookModel::COLUMN_CALL, LogbookModel::COLUMN_OPERATOR, LogbookModel::COLUMN_STATION_CALLSIGN, LogbookModel::COLUMN_FREQUENCY, LogbookModel::COLUMN_MODE, LogbookModel::COLUMN_SUBMODE, LogbookModel::COLUMN_MY_STATE, LogbookModel::COLUMN_MY_COUNTRY, LogbookModel::COLUMN_MY_POTA_REF, LogbookModel::COLUMN_POTA_REF, LogbookModel::COLUMN_MY_SIG, LogbookModel::COLUMN_MY_SIG_INFO, LogbookModel::COLUMN_SIG, LogbookModel::COLUMN_SIG_INFO }; LogbookModel logbookmodel; const QList qsos4export; void fillQSLSendViaCombo(); void fillExportTypeCombo(); void fillExportedColumnsCombo(); bool markQSOAsSent(LogFormat *format); }; #endif // QLOG_UI_EXPORTDIALOG_H ================================================ FILE: ui/ExportDialog.ui ================================================ ExportDialog 0 0 465 606 465 0 Export Selected QSOs true File Qt::ClickFocus true Browse .. 0 0 ADI ADX CSV JSON POTA 0 0 Export Type 0 0 Column set 6 0 0 Select one of the pre-defined sets of columns or define your own set of columns 0 0 Edit current set of columns Edit Mark exported QSOs As Sent true true Export only QSOs that match the active filters Filters 0 0 Export only QSOs that match the selected date range Date Range false 0 0 Qt::ClickFocus Export only QSOs that match the selected date range true 0 0 - Qt::AlignCenter false 0 0 Qt::ClickFocus Export only QSOs that match the selected date range true Qt::Horizontal 40 20 Export only QSOs that match the selected My Callsign My Callsign false Export only QSOs that match the selected My Callsign Export only QSOs that match the selected My Gridsquare My Gridsquare false Export only QSOs that match the selected My Gridsquare Export only QSOs that match the selected QSL Send Via value QSL Send via false Export only QSOs that match the selected QSL Send Via value Include unusual QSO Sent statuses Include Sent Status 0 0 0 10 false 0 0 Under normal circumstances this status means <b>"Ignore/Invalid"</b>.<br/>However, it may sometimes be wanted to ignore this setting when sending a QSL. "Ignore" true false 0 0 Under normal circumstances this status means <b>"do not send"</b>.<br/>However, it may sometimes be wanted to ignore this setting when sending a QSL. "No" true 0 0 false 0 0 Resend already sent QSOs Already Sent true User Filter false Export QSOs that match the selected user QSO Filter Station Profile false Qt::Horizontal QDialogButtonBox::Close|QDialogButtonBox::Ok browseButton typeSelect exportTypeCombo exportedColumnsCombo exportedColumnsButton markAsSentCheckbox dateRangeCheckBox myStationProfileCheckbox myStationProfileComboBox myCallsignCheckBox myCallsignComboBox myGridCheckBox myGridComboBox userFilterCheckBox userFilterComboBox qslSendViaCheckbox qslSendViaComboBox addlSentStatusCheckbox addlSentStatusICheckBox addlSentStatusNCheckBox addlSentStatusAlreadySentCheckBox buttonBox accepted() ExportDialog runExport() 266 536 157 274 buttonBox rejected() ExportDialog reject() 334 536 286 274 browseButton clicked() ExportDialog browse() 368 67 157 91 dateRangeCheckBox stateChanged(int) ExportDialog toggleDateRange() 124 258 157 91 myCallsignCheckBox stateChanged(int) ExportDialog toggleMyCallsign() 93 292 232 142 myGridCheckBox stateChanged(int) ExportDialog toggleMyGridsquare() 103 323 232 142 myCallsignComboBox currentTextChanged(QString) ExportDialog myCallsignChanged(QString) 443 302 232 142 exportedColumnsButton clicked() ExportDialog showColumnsSetting() 442 159 232 179 qslSendViaCheckbox stateChanged(int) ExportDialog toggleQslSendVia() 119 396 232 212 exportTypeCombo currentIndexChanged(int) ExportDialog exportTypeChanged(int) 443 126 232 212 exportedColumnsCombo currentIndexChanged(int) ExportDialog exportedColumnsComboChanged(int) 356 159 232 229 addlSentStatusCheckbox stateChanged(int) ExportDialog toggleSentStatus() 174 428 237 220 userFilterCheckBox stateChanged(int) ExportDialog toggleUserFilter() 70 352 232 272 typeSelect currentTextChanged(QString) ExportDialog exportFormatChanged(QString) 397 46 232 290 myStationProfileCheckbox stateChanged(int) ExportDialog toggleStationProfile() 85 300 232 302 myStationProfileComboBox currentIndexChanged(int) ExportDialog onStationProfileChange() 316 301 232 302 toggleDateRange() browse() runExport() toggleMyCallsign() toggleMyGridsquare() myCallsignChanged(QString) exportFormatChanged(QString) showColumnsSetting() toggleQslSendVia() exportTypeChanged(int) exportedColumnsComboChanged(int) toggleSentStatus() toggleUserFilter() toggleStationProfile() onStationProfileChange() ================================================ FILE: ui/ExportPasswordDialog.cpp ================================================ #include #include #include "ExportPasswordDialog.h" #include "ui_ExportPasswordDialog.h" #include "core/debug.h" MODULE_IDENTIFICATION("qlog.ui.exportpassworddialog"); ExportPasswordDialog::ExportPasswordDialog(QWidget *parent) : QDialog(parent), ui(new Ui::ExportPasswordDialog) { FCT_IDENTIFICATION; ui->setupUi(this); ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); } ExportPasswordDialog::~ExportPasswordDialog() { FCT_IDENTIFICATION; delete ui; } QString ExportPasswordDialog::getPassword() const { FCT_IDENTIFICATION; return ui->passwordEdit->text(); } bool ExportPasswordDialog::getDeletePasswords() const { FCT_IDENTIFICATION; return ui->deletePasswordsCheckBox->isChecked(); } void ExportPasswordDialog::validatePasswords() { FCT_IDENTIFICATION; const QString &pass = ui->passwordEdit->text(); const QString &confirm = ui->confirmEdit->text(); if ( pass.isEmpty() ) { ui->confirmEdit->clear(); ui->passwordEdit->setEchoMode(QLineEdit::PasswordEchoOnEdit); ui->confirmEdit->setEchoMode(QLineEdit::Password); } bool isEnabled = (!pass.isEmpty() && pass == confirm); ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(isEnabled); } void ExportPasswordDialog::generatePassword() { FCT_IDENTIFICATION; // removing similar chars 0 and Oo, 1 and lI static const char charset[] = "aBcDeFgHiJkLmNPqRsTuVwXyZ" "AbCdEfGhjKMnOpQrStUvWxYz" "@2$4&6!78#93%5*"; const int charsetLen = static_cast(sizeof(charset) - 1); const int passwordLen = 12; QRandomGenerator *rng = QRandomGenerator::system(); QString password; password.reserve(passwordLen); for ( int i = 0; i < passwordLen; ++i ) password.append(QChar(charset[rng->bounded(charsetLen)])); ui->passwordEdit->setText(password); ui->confirmEdit->setText(password); ui->passwordEdit->setEchoMode(QLineEdit::Normal); ui->confirmEdit->setEchoMode(QLineEdit::Normal); } ================================================ FILE: ui/ExportPasswordDialog.h ================================================ #ifndef QLOG_UI_EXPORTPASSWORDDIALOG_H #define QLOG_UI_EXPORTPASSWORDDIALOG_H #include namespace Ui { class ExportPasswordDialog; } class ExportPasswordDialog : public QDialog { Q_OBJECT public: explicit ExportPasswordDialog(QWidget *parent = nullptr); ~ExportPasswordDialog(); QString getPassword() const; bool getDeletePasswords() const; private slots: void validatePasswords(); void generatePassword(); private: Ui::ExportPasswordDialog *ui; }; #endif // QLOG_UI_EXPORTPASSWORDDIALOG_H ================================================ FILE: ui/ExportPasswordDialog.ui ================================================ ExportPasswordDialog 0 0 422 193 Pack Data & Settings Enter a password to encrypt stored credentials. The password will be needed to restore them later. true 0 QLineEdit::PasswordEchoOnEdit Password Qt::ClickFocus Random QLineEdit::Password Confirm Password Delete passwords from Credential Store after export false Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok passwordEdit confirmEdit deletePasswordsCheckBox generateButton buttonBox accepted() ExportPasswordDialog accept() 248 254 157 274 buttonBox rejected() ExportPasswordDialog reject() 316 260 286 274 passwordEdit textChanged(QString) ExportPasswordDialog validatePasswords() 167 69 210 96 confirmEdit textChanged(QString) ExportPasswordDialog validatePasswords() 210 104 210 96 generateButton clicked() ExportPasswordDialog generatePassword() 371 69 210 96 validatePasswords() generatePassword() ================================================ FILE: ui/ImportDialog.cpp ================================================ #include #include #include "ImportDialog.h" #include "ui_ImportDialog.h" #include "logformat/LogFormat.h" #include "core/debug.h" #include "data/StationProfile.h" #include "data/RigProfile.h" MODULE_IDENTIFICATION("qlog.ui.importdialog"); ImportDialog::ImportDialog(QWidget *parent) : QDialog(parent), ui(new Ui::ImportDialog) { FCT_IDENTIFICATION; ui->setupUi(this); ui->allCheckBox->setChecked(true); ui->startDateEdit->setDisplayFormat(locale.formatDateShortWithYYYY()); ui->startDateEdit->setDate(QDate::currentDate()); ui->endDateEdit->setDate(QDate::currentDate()); ui->endDateEdit->setDisplayFormat(locale.formatDateShortWithYYYY()); ui->progressBar->setValue(0); ui->progressBar->setMinimum(0); ui->progressBar->setMaximum(100); //ui->progressBar->setDisabled(true); QStringList rigs = RigProfilesManager::instance()->profileNameList(); QStringListModel* rigModel = new QStringListModel(rigs, this); ui->rigSelect->setModel(rigModel); if (!ui->rigSelect->currentText().isEmpty()) { ui->rigCheckBox->setChecked(true); ui->rigSelect->setCurrentText(RigProfilesManager::instance()->getCurProfile1().profileName); } QStringList profiles = StationProfilesManager::instance()->profileNameList(); QStringListModel* profileModel = new QStringListModel(profiles, this); ui->profileSelect->setModel(profileModel); if (!ui->profileSelect->currentText().isEmpty()) { ui->profileSelect->setCurrentText(StationProfilesManager::instance()->getCurProfile1().profileName); ui->profileCheckBox->setChecked(true); } ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("&Import")); // TODO: disabled #983 - If everything is OK, then delete it over time. ui->optionBox->setVisible(false); } void ImportDialog::browse() { FCT_IDENTIFICATION; QSettings settings; //platform-dependent, must be present const QString &lastPath = ( ui->fileEdit->text().isEmpty() ) ? settings.value("import/last_path", QDir::homePath()).toString() : ui->fileEdit->text(); QString filename = QFileDialog::getOpenFileName(this, tr("Select File"), lastPath, ui->typeSelect->currentText().toUpper() + "(*." + ui->typeSelect->currentText().toLower() + ")", nullptr, #if defined(Q_OS_LINUX) && !(defined(QLOG_FLATPAK) && defined(Q_PROCESSOR_ARM_64)) // Do not use the Native Dialog under Linux because the dialog is case-sensitive. // QT variant looks different but it is case-insensitive. // More information: // https://stackoverflow.com/questions/34858220/qt-how-to-set-a-case-insensitive-filter-on-qfiledialog // https://bugreports.qt.io/browse/QTBUG-51712 // But Raspberry pi crashes when DontUseNativeDialog is used therefore use the native for it. QFileDialog::DontUseNativeDialog #else QFileDialog::Options() #endif ); if ( !filename.isEmpty() ) { settings.setValue("import/last_path", QFileInfo(filename).path()); ui->fileEdit->setText(filename); } } void ImportDialog::toggleAll() { FCT_IDENTIFICATION; ui->startDateEdit->setEnabled(!ui->allCheckBox->isChecked()); ui->endDateEdit->setEnabled(!ui->allCheckBox->isChecked()); } void ImportDialog::toggleMyProfile() { FCT_IDENTIFICATION; ui->profileSelect->setEnabled(ui->profileCheckBox->isChecked()); } void ImportDialog::toggleMyRig() { FCT_IDENTIFICATION; ui->rigSelect->setEnabled(ui->rigCheckBox->isChecked()); } void ImportDialog::commentChanged(const QString &comment) { QString toolTip = tr("The value is used when an input record does not contain the ADIF value") + "
" + "" + tr("Comment") + ": " + comment + "
"; ui->commentEdit->setToolTip(toolTip); ui->commentCheckBox->setToolTip(toolTip); } void ImportDialog::toggleComment() { FCT_IDENTIFICATION; ui->commentEdit->setEnabled(ui->commentCheckBox->isChecked()); commentChanged(ui->commentEdit->text()); } void ImportDialog::computeProgress(qint64 position) { FCT_IDENTIFICATION; int progress = (int)(position * 100 / size); ui->progressBar->setValue(progress); QCoreApplication::processEvents(); } void ImportDialog::stationProfileTextChanged(const QString &newProfileName) { FCT_IDENTIFICATION; selectedStationProfile = StationProfilesManager::instance()->getProfile(newProfileName); QString toolTip = tr("The values below will be used when an input record does not contain the ADIF values") + "
" + selectedStationProfile.toHTMLString(); ui->profileSelect->setToolTip(toolTip); ui->profileCheckBox->setToolTip(toolTip); } void ImportDialog::rigProfileTextChanged(const QString &newProfileName) { FCT_IDENTIFICATION; QString toolTip = tr("The values below will be used when an input record does not contain the ADIF values") + "
" + RigProfilesManager::instance()->getProfile(newProfileName).toHTMLString(); ui->rigSelect->setToolTip(toolTip); ui->rigCheckBox->setToolTip(toolTip); } LogFormat::duplicateQSOBehaviour ImportDialog::showDuplicateDialog(QSqlRecord *imported, QSqlRecord *original) { FCT_IDENTIFICATION; qCDebug(function_parameters) << *imported << " " << *original; LogFormat::duplicateQSOBehaviour ret = LogFormat::ACCEPT_ONE; QMessageBox::StandardButton reply; QString inLogQSO = tr("

In-Log QSO:

") + original->value("start_time").toString() + " " + original->value("callsign").toString() + "

"; QString importedQSO = tr("

Importing:

") + imported->value("start_time").toString() + " " + imported->value("callsign").toString() + "

"; reply = QMessageBox::question(nullptr, tr("Duplicate QSO"), tr("

Do you want to import duplicate QSO?

%1 %2").arg(inLogQSO,importedQSO), QMessageBox::Yes|QMessageBox::No|QMessageBox::YesAll|QMessageBox::NoAll); switch ( reply ) { case QMessageBox::Yes: ret = LogFormat::ACCEPT_ONE; break; case QMessageBox::YesAll: ret = LogFormat::ACCEPT_ALL; break; case QMessageBox::No: ret = LogFormat::SKIP_ONE; break; case QMessageBox::NoAll: ret = LogFormat::SKIP_ALL; break; default: ret = LogFormat::ASK_NEXT; } qCDebug(runtime) << "ret: " << ret; return ret; } void ImportDialog::saveImportDetails(const QString &importDetail, const QString &filename, const int count, const unsigned long warnings, const unsigned long errors) { FCT_IDENTIFICATION; QSettings settings; //platform-dependent, must be present const QString &lastPath = settings.value("import/last_path_importdetails", QDir::homePath()).toString(); QString filePath = QFileDialog::getSaveFileName(this, tr("Save to File"), lastPath, "TXT (*.txt)"); if ( !filePath.isEmpty() ) { QFile file(filePath); if ( file.open(QIODevice::WriteOnly | QIODevice::Text) ) { const QDateTime &currTime = QDateTime::currentDateTimeUtc(); QTextStream out(&file); out << tr("QLog Import Summary") << "\n" << "\n" << tr("Import date") << ": " << currTime.toString(locale.formatDateShortWithYYYY()) << " " << locale.toString(currTime, locale.formatTimeLongWithoutTZ()) << " UTC\n" << tr("Imported file") << ": " << filename << "\n\n" << tr("Imported: %n contact(s)", "", count) << "\n" << tr("Warning(s): %n", "", warnings) << "\n" << tr("Error(s): %n", "", errors) << "\n" << "\n" << tr("Details") << ":\n" << importDetail; file.close(); settings.setValue("import/last_path_importdetails", QFileInfo(filePath).path()); } } } void ImportDialog::runImport() { FCT_IDENTIFICATION; if ( ui->fileEdit->text().isEmpty() ) { QMessageBox::warning(nullptr, QMessageBox::tr("QLog Warning"), QMessageBox::tr("Filename is empty")); return; } QFile file(ui->fileEdit->text()); file.open(QFile::ReadOnly | QFile::Text); QTextStream in(&file); size = file.size(); QMap defaults; if (ui->rigCheckBox->isChecked()) { defaults["my_rig_intl"] = ui->rigSelect->currentText(); } if (ui->commentCheckBox->isChecked()) { defaults["comment_intl"] = ui->commentEdit->text(); } LogFormat* format = LogFormat::open(ui->typeSelect->currentText(), in); if (!format) { qCritical() << "unknown log format"; return; } format->setDefaults(defaults); format->setFillMissingDxcc(ui->fillMissingDxccCheckBox->isChecked()); if (!ui->allCheckBox->isChecked()) { format->setFilterDateRange(ui->startDateEdit->date(), ui->endDateEdit->date()); } format->setDuplicateQSOCallback(showDuplicateDialog); connect(format, &LogFormat::importPosition, this, &ImportDialog::computeProgress); ui->buttonBox->setEnabled(false); ui->fileEdit->setEnabled(false); ui->typeSelect->setEnabled(false); ui->browseButton->setEnabled(false); ui->startDateEdit->setEnabled(false); ui->endDateEdit->setEnabled(false); ui->allCheckBox->setEnabled(false); ui->profileCheckBox->setEnabled(false); ui->profileSelect->setEnabled(false); ui->rigCheckBox->setEnabled(false); ui->rigSelect->setEnabled(false); ui->commentCheckBox->setEnabled(false); ui->commentEdit->setEnabled(false); ui->fillMissingDxccCheckBox->setEnabled(false); QString s; QTextStream out(&s); unsigned long errors = 0L; unsigned long warnings = 0L; int count = format->runImport(out, ( ui->profileCheckBox->isChecked() && selectedStationProfile != StationProfile() ) ?&selectedStationProfile: nullptr, &warnings, &errors); QString report = QObject::tr("Imported: %n contact(s)", "", count) + "
" + QObject::tr("Warning(s): %n", "", warnings) + "
" + QObject::tr("Error(s): %n", "", errors); QMessageBox msgBox; QAbstractButton* pButtonYes = nullptr; msgBox.setWindowTitle(tr("Import Result")); msgBox.setText(report); msgBox.setDetailedText(s); msgBox.setIcon(QMessageBox::Information); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setDefaultButton(QMessageBox::Ok); if ( !s.isEmpty() ) pButtonYes = msgBox.addButton(tr("Save Details..."), QMessageBox::ActionRole); QSpacerItem* horizontalSpacer = new QSpacerItem(500, 0, QSizePolicy::Minimum, QSizePolicy::Expanding); QGridLayout* layout = qobject_cast(msgBox.layout()); if ( !layout ) { qWarning() << "Layout is null"; delete horizontalSpacer; return; } layout->addItem(horizontalSpacer, layout->rowCount(), 0, 1, layout->columnCount()); msgBox.exec(); if ( pButtonYes && msgBox.clickedButton() == pButtonYes ) { saveImportDetails(s, ui->fileEdit->text(), count, warnings, errors); } delete format; qCDebug(runtime).noquote() << s; accept(); } ImportDialog::~ImportDialog() { FCT_IDENTIFICATION; delete ui; } ================================================ FILE: ui/ImportDialog.h ================================================ #ifndef QLOG_UI_IMPORTDIALOG_H #define QLOG_UI_IMPORTDIALOG_H #include #include #include #include "data/StationProfile.h" #include "core/LogLocale.h" namespace Ui { class ImportDialog; } class ImportDialog : public QDialog { Q_OBJECT public: explicit ImportDialog(QWidget *parent = 0); ~ImportDialog(); private slots: void browse(); void toggleAll(); void toggleComment(); void runImport(); void computeProgress(qint64 position); void stationProfileTextChanged(const QString&); void rigProfileTextChanged(const QString&); void toggleMyProfile(); void toggleMyRig(); void commentChanged(const QString&); private: Ui::ImportDialog *ui; qint64 size; StationProfile selectedStationProfile; LogLocale locale; static LogFormat::duplicateQSOBehaviour showDuplicateDialog(QSqlRecord *, QSqlRecord *); void saveImportDetails(const QString &importDetail, const QString &filename, const int count, const unsigned long warnings, const unsigned long errors); }; #endif // QLOG_UI_IMPORTDIALOG_H ================================================ FILE: ui/ImportDialog.ui ================================================ ImportDialog Import true File 0 0 ADI ADX Qt::ClickFocus true Browse .. Date Range 0 0 Import all or only QSOs from the given period All Qt::Horizontal 40 20 0 0 Qt::ClickFocus true - 0 0 Qt::ClickFocus true The value is used when an input record does not contain the ADIF value Defaults 0 0 My Profile false 0 0 My Rig false 0 0 0 0 Comment false 0 0 false true Options If DXCC is missing in the imported record, it will be resolved from the callsign. true Fill missing DXCC Entity Information true 24 QDialogButtonBox::Close|QDialogButtonBox::Ok false browseButton typeSelect allCheckBox profileCheckBox profileSelect rigCheckBox rigSelect commentCheckBox commentEdit fillMissingDxccCheckBox buttonBox accepted() ImportDialog runImport() 248 254 157 274 buttonBox rejected() ImportDialog reject() 316 260 286 274 allCheckBox stateChanged(int) ImportDialog toggleAll() 106 125 155 102 browseButton clicked() ImportDialog browse() 216 21 155 102 profileCheckBox stateChanged(int) ImportDialog toggleMyProfile() 59 303 239 279 rigSelect currentTextChanged(QString) ImportDialog rigProfileTextChanged(QString) 256 235 206 218 rigCheckBox stateChanged(int) ImportDialog toggleMyRig() 56 341 239 279 profileSelect currentTextChanged(QString) ImportDialog stationProfileTextChanged(QString) 256 204 206 218 commentCheckBox stateChanged(int) ImportDialog toggleComment() 66 379 239 279 commentEdit textChanged(QString) ImportDialog commentChanged(QString) 265 266 214 218 browse() toggleAll() runImport() toggleMyProfile() toggleMyRig() toggleComment() adjustLocatorTextColor() stationProfileTextChanged(QString) rigProfileTextChanged(QString) commentChanged(QString) ================================================ FILE: ui/InputPasswordDialog.cpp ================================================ #include "InputPasswordDialog.h" #include "ui_InputPasswordDialog.h" #include "core/debug.h" MODULE_IDENTIFICATION("qlog.ui.dxwidget"); InputPasswordDialog::InputPasswordDialog(QString dialogName, QString comment, QWidget *parent) : QDialog(parent), ui(new Ui::InputPasswordDialog) { FCT_IDENTIFICATION; ui->setupUi(this); setWindowTitle(dialogName); ui->comment->setText(comment); } InputPasswordDialog::~InputPasswordDialog() { FCT_IDENTIFICATION; delete ui; } QString InputPasswordDialog::getPassword() const { FCT_IDENTIFICATION; return ui->passwordLineEdit->text(); } bool InputPasswordDialog::getRememberPassword() const { FCT_IDENTIFICATION; return ui->rememberCheckBox->isChecked(); } ================================================ FILE: ui/InputPasswordDialog.h ================================================ #ifndef QLOG_UI_INPUTPASSWORDDIALOG_H #define QLOG_UI_INPUTPASSWORDDIALOG_H #include namespace Ui { class InputPasswordDialog; } class InputPasswordDialog : public QDialog { Q_OBJECT public: explicit InputPasswordDialog(QString dialogName, QString comment, QWidget *parent = nullptr); ~InputPasswordDialog(); QString getPassword() const; bool getRememberPassword() const; private: Ui::InputPasswordDialog *ui; }; #endif // QLOG_UI_INPUTPASSWORDDIALOG_H ================================================ FILE: ui/InputPasswordDialog.ui ================================================ InputPasswordDialog 0 0 400 126 Dialog QLineEdit::Password The password will be stored in the Credential Store. Remember Password true Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() InputPasswordDialog accept() 248 254 157 274 buttonBox rejected() InputPasswordDialog reject() 316 260 286 274 ================================================ FILE: ui/KSTChatWidget.cpp ================================================ #include #include #include #include "KSTChatWidget.h" #include "ui_KSTChatWidget.h" #include "core/debug.h" #include "data/Data.h" #include "data/StationProfile.h" #include "ui/ColumnSettingDialog.h" #include "ui/component/StyleItemDelegate.h" #include "KSTHighlighterSettingDialog.h" #include "rotator/Rotator.h" MODULE_IDENTIFICATION("qlog.ui.kstchatwidget"); KSTChatWidget::KSTChatWidget(int chatRoomIndex, const QString &username, const QString &password, const NewContactWidget *contact, QWidget *parent) : QWidget(parent), ui(new Ui::KSTChatWidget), messageModel(new ChatMessageModel(this)), valuableMessageModel(new ChatMessageModel(this)), chat(new KSTChat(chatRoomIndex, username, password, contact, this)), userListModel(new UserListModel(this)), highlightEvaluator(new chatHighlightEvaluator(chatRoomIndex, this)), userName(username) { FCT_IDENTIFICATION; ui->setupUi(this); ui->splitterMsgMsg->setSizes(QList({1, 0})); ui->splitterMsgUsr->setSizes(QList({3, 1})); ui->toLabel->setVisible(false); ui->resetButton->setVisible(false); setBeamActionVisible(Rotator::instance()->isRotConnected()); QCommonStyle style; ui->resetButton->setIcon(style.standardIcon(QStyle::SP_LineEditClearButton)); proxyModel = new QSortFilterProxyModel(this); proxyModel->setSourceModel(userListModel); proxyModel->setSortRole(Qt::UserRole); ui->usersTableView->setModel(proxyModel); ui->usersTableView->setSortingEnabled(true); ui->usersTableView->sortByColumn(0, Qt::AscendingOrder); ui->usersTableView->setItemDelegateForColumn(2, new DistanceFormatDelegate(1, 0.1, ui->usersTableView)); ui->usersTableView->setItemDelegateForColumn(3, new UnitFormatDelegate("°", 0, 0.1, ui->usersTableView)); ui->usersTableView->setItemDelegateForColumn(4, new HTMLDelegate(ui->usersTableView)); ui->usersTableView->horizontalHeader()->setSectionsMovable(true); ui->usersTableView->addAction(ui->actionPrefillQSO); ui->usersTableView->addAction(ui->actionBeam); ui->usersTableView->addAction(ui->actionDisplayedColumns); QAction *separator = new QAction(this); separator->setSeparator(true); ui->messageListView->setItemDelegate(new MessageDelegate(this)); ui->messageListView->setModel(messageModel); ui->messageListView->addAction(ui->actionShowAboutMeOnly); ui->messageListView->addAction(ui->actionSuppressUser2User); ui->messageListView->addAction(ui->actionHighlight); ui->messageListView->addAction(separator); ui->messageListView->addAction(ui->actionHighlightRules); ui->valuableMessageListView->setItemDelegate(new MessageDelegate(this)); ui->valuableMessageListView->setModel(valuableMessageModel); ui->valuableMessageListView->addAction(ui->actionClearValuableMessages); connect(chat.data(), &KSTChat::chatMsg, this, &KSTChatWidget::addChatMessage); connect(chat.data(), &KSTChat::chatError, this, &KSTChatWidget::showChatError); connect(chat.data(), &KSTChat::usersListUpdated, this, &KSTChatWidget::updateUserList); connect(chat.data(), &KSTChat::chatDisconnected, this, &KSTChatWidget::closeChat); connect(Rotator::instance(), &Rotator::rotConnected, this, [this]() { setBeamActionVisible(true); }); connect(Rotator::instance(), &Rotator::rotDisconnected, this, [this]() { setBeamActionVisible(false); }); chat->connectChat(); } KSTChatWidget::~KSTChatWidget() { FCT_IDENTIFICATION; delete ui; } QList KSTChatWidget::getUserList() { FCT_IDENTIFICATION; return (chat.data()) ? chat->getUsersList() : QList(); } void KSTChatWidget::addChatMessage(KSTChatMsg msg) { FCT_IDENTIFICATION; qCDebug(function_parameters) << msg.time << msg.sender << msg.message; if ( msg.message.isEmpty() ) return; bool isMyCallsignPresent = msg.message.contains(userName,Qt::CaseInsensitive); bool isSenderSelectedCallsign = ui->toLabel->isVisible() && !msg.sender.isEmpty() && msg.sender.startsWith(ui->toLabel->text()); bool isUser2User = msg.message.startsWith(" ("); //hack - how to easy to recognize Private Message bool shouldHighlight = ui->actionHighlight->isChecked() && isHighlightCandidate(msg); qCDebug(runtime) << "AboutMe" << isMyCallsignPresent; qCDebug(runtime) << "isUser2User" << isUser2User; qCDebug(runtime) << "shouldHighlight" << shouldHighlight; qCDebug(runtime) << "isSenderSelectedCallsign" << isSenderSelectedCallsign; // Filter incoming messages // Empty callsign means server response, do not suppress it if ( !msg.sender.isEmpty() ) { if ( ! shouldHighlight && ! isSenderSelectedCallsign ) { if ( ui->actionShowAboutMeOnly->isChecked() && !isMyCallsignPresent ) return; if ( ui->actionSuppressUser2User->isChecked() && isUser2User) return; } } ChatMessageModel::MessageDirection dir = ChatMessageModel::INCOMING; if ( shouldHighlight ) { dir = ChatMessageModel::INCOMING_HIGHLIGHT; } else if ( isMyCallsignPresent || isSenderSelectedCallsign ) { dir = ChatMessageModel::INCOMING_TOYOU; } if ( shouldHighlight || isMyCallsignPresent || isSenderSelectedCallsign ) { valuableMessageModel->addMessage(dir, msg); if ( ui->valuableMessageListView->verticalScrollBar()->value() == ui->valuableMessageListView->verticalScrollBar()->maximum() ) ui->valuableMessageListView->scrollToBottom(); emit valuableMessageUpdated(this); } messageModel->addMessage(dir, msg); if ( ui->messageListView->verticalScrollBar()->value() == ui->messageListView->verticalScrollBar()->maximum() ) ui->messageListView->scrollToBottom(); emit chatUpdated(this); } void KSTChatWidget::sendMessage() { FCT_IDENTIFICATION; KSTChatMsg chatMsg; QString command; chatMsg.sender = tr("You"); chatMsg.time = QDateTime::currentDateTimeUtc().toString("hhmm"); if ( ui->toLabel->text() != QString() ) { chatMsg.message = "(" + ui->toLabel->text() + ") " + ui->msgLineEdit->text(); command = "/cq " + ui->toLabel->text() + " " + ui->msgLineEdit->text(); } else { chatMsg.message = ui->msgLineEdit->text(); command = ui->msgLineEdit->text(); } messageModel->addMessage(ChatMessageModel::OUTGOING, chatMsg); valuableMessageModel->addMessage(ChatMessageModel::OUTGOING, chatMsg); ui->messageListView->scrollToBottom(); ui->valuableMessageListView->scrollToBottom(); chat->sendMessage(command); ui->msgLineEdit->blockSignals(true); ui->msgLineEdit->clear(); ui->msgLineEdit->blockSignals(false); } void KSTChatWidget::updateUserList() { FCT_IDENTIFICATION; userListModel->clear(); QList usersList = chat->getUsersList(); // refresh user List userListModel->updateList(usersList); setSelectedCallsignInUserList(ui->toLabel->text()); emit userListUpdated(this); } void KSTChatWidget::setPrivateChatCallsign(QString callsign) { FCT_IDENTIFICATION; qCDebug(function_parameters) << callsign; if ( callsign.isEmpty() ) { resetPressed(); return; } ui->toLabel->setVisible(true); ui->resetButton->setVisible(true); ui->toLabel->setText(callsign); setSelectedCallsignInUserList(callsign); } void KSTChatWidget::reloadStationProfile() { FCT_IDENTIFICATION; chat->reloadStationProfile(); } void KSTChatWidget::setBeamActionVisible(bool flag) { FCT_IDENTIFICATION; qCDebug(function_parameters) << flag; ui->actionBeam->setVisible(flag); } void KSTChatWidget::resetDupe() { FCT_IDENTIFICATION; chat->resetDupe(); } void KSTChatWidget::recalculateDupe() { FCT_IDENTIFICATION; chat->recalculateDupe(); } void KSTChatWidget::recalculateDxccStatus() { FCT_IDENTIFICATION; chat->recalculateDxccStatus(); } void KSTChatWidget::updateSpotsStatusWhenQSOAdded(const QSqlRecord &record) { FCT_IDENTIFICATION; chat->updateSpotsStatusWhenQSOAdded(record); } void KSTChatWidget::updateSpotsStatusWhenQSODeleted(const QSqlRecord &record) { FCT_IDENTIFICATION; chat->updateSpotsStatusWhenQSODeleted(record); } void KSTChatWidget::updateSpotsDxccStatusWhenQSODeleted(const QSet &entities) { FCT_IDENTIFICATION; chat->updateSpotsDxccStatusWhenQSODeleted(entities); } void KSTChatWidget::showChatError(const QString &error) { FCT_IDENTIFICATION; QMessageBox::warning(nullptr, QMessageBox::tr("QLog Error"), QMessageBox::tr("Chat Error: ") + " " + error); } void KSTChatWidget::closeChat() { FCT_IDENTIFICATION; emit chatClosed(); } void KSTChatWidget::displayedColumns() { FCT_IDENTIFICATION; ColumnSettingSimpleDialog dialog(ui->usersTableView); dialog.exec(); } void KSTChatWidget::userDoubleClicked(QModelIndex index) { FCT_IDENTIFICATION; const QModelIndex &sourceIindex = proxyModel->mapToSource(index); setPrivateChatCallsign(userListModel->getUserInfo(sourceIindex).callsign); } void KSTChatWidget::messageDoubleClicked(QModelIndex index) { FCT_IDENTIFICATION; const QStringList messageSenderElements = messageModel->getMessage(index).sender.split(" "); if ( messageSenderElements.size() > 0 ) setPrivateChatCallsign(messageSenderElements.at(0)); } void KSTChatWidget::prefillQSOAction() { FCT_IDENTIFICATION; const QModelIndex &sourceIndex = proxyModel->mapToSource(ui->usersTableView->currentIndex()); const KSTUsersInfo &info = userListModel->getUserInfo(sourceIndex); emit prepareQSOInfo(info.callsign, info.grid.getGrid()); } void KSTChatWidget::highlightPressed() { FCT_IDENTIFICATION; } bool KSTChatWidget::isHighlightCandidate(KSTChatMsg &msg) { FCT_IDENTIFICATION; return highlightEvaluator->shouldHighlight(msg, msg.matchedHighlightRules); } void KSTChatWidget::editHighlightRules() { FCT_IDENTIFICATION; KSTHighlighterSettingDialog dialog(this); dialog.exec(); highlightEvaluator->loadRules(); } void KSTChatWidget::resetPressed() { FCT_IDENTIFICATION; ui->msgLineEdit->clear(); ui->toLabel->clear(); ui->usersTableView->clearSelection(); ui->toLabel->setVisible(false); ui->resetButton->setVisible(false); } void KSTChatWidget::beamingRequest() { FCT_IDENTIFICATION; const QModelIndex &sourceIndex = proxyModel->mapToSource(ui->usersTableView->currentIndex()); const KSTUsersInfo &info = userListModel->getUserInfo(sourceIndex); const StationProfile &profile = StationProfilesManager::instance()->getCurProfile1(); if ( !profile.locator.isEmpty() ) { Gridsquare myGrid(profile.locator); double bearing; if ( myGrid.bearingTo(info.grid, bearing) ) { emit beamingRequested(bearing); } } } void KSTChatWidget::clearValuableMessages() { FCT_IDENTIFICATION; valuableMessageModel->clear(); } void KSTChatWidget::setSelectedCallsignInUserList(const QString &callsign) { FCT_IDENTIFICATION; if ( callsign.isEmpty() ) return; const QModelIndexList &nextMatches = proxyModel->match(proxyModel->index(0,0), Qt::DisplayRole, callsign, 1, Qt::MatchExactly); if ( nextMatches.size() >= 1 ) { ui->usersTableView->setCurrentIndex(nextMatches.at(0)); } } int ChatMessageModel::rowCount(const QModelIndex &) const { return messages.size(); } QVariant ChatMessageModel::data(const QModelIndex &index, int role) const { if ( role == Qt::UserRole ) { return messages.at(index.row()).first; } if ( role == Qt::DisplayRole ) { const KSTChatMsg &msg = messages.at(index.row()).second; QString text(msg.message); text.replace("\n", "
"); QString htmlText; if ( !text.isEmpty() ) { // %1 %2
%3 htmlText = QString("%1 %2 %3
%4").arg(msg.time, msg.sender.isEmpty() ? "Server" : msg.sender, !msg.matchedHighlightRules.isEmpty() ? "(" + msg.matchedHighlightRules.join(", ") + ")" : "", text); } return htmlText; } return QVariant(); } void ChatMessageModel::addMessage(MessageDirection direction, const KSTChatMsg &msg) { beginInsertRows(QModelIndex(), messages.size(), messages.size()+1); messages.append(QPair(direction, msg)); endInsertRows(); } void ChatMessageModel::clear() { FCT_IDENTIFICATION; beginResetModel(); messages.clear(); endResetModel(); } KSTChatMsg ChatMessageModel::getMessage(const QModelIndex &index) const { FCT_IDENTIFICATION; return messages.at(index.row()).second; } void MessageDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { //https://forum.qt.io/topic/91480/implementing-chat-type-listview-with-text-bubbles/6 QTextDocument bodydoc; QTextOption textOption(bodydoc.defaultTextOption()); QString bodytext(index.data(Qt::DisplayRole).toString()); bool outgoing = (index.data(Qt::UserRole).toInt() == ChatMessageModel::MessageDirection::OUTGOING); qreal contentswidth = option.rect.width() * d_widthfraction - d_horizontalmargin - d_pointerwidth - d_leftpadding - d_rightpadding; textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); bodydoc.setDefaultTextOption(textOption); bodydoc.setHtml(bodytext); bodydoc.setTextWidth(contentswidth); qreal bodyheight = bodydoc.size().height(); painter->save(); painter->setRenderHint(QPainter::Antialiasing); // uncomment to see the area provided to paint this item //painter->drawRect(option.rect); painter->translate(option.rect.left() + d_horizontalmargin, option.rect.top() + ((index.row() == 0) ? d_verticalmargin : 0)); // background color for chat bubble QColor bgcolor; switch ( index.data(Qt::UserRole).toInt() ) { case ChatMessageModel::MessageDirection::OUTGOING: bgcolor = #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) QColorConstants::LightGray; #else QColor(Qt::lightGray); #endif break; case ChatMessageModel::MessageDirection::INCOMING_TOYOU: bgcolor = #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) QColorConstants::Green; #else QColor(Qt::green); #endif break; case ChatMessageModel::MessageDirection::INCOMING_HIGHLIGHT: bgcolor = #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) QColorConstants::Red; #else QColor(Qt::red); #endif break; default: bgcolor = #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) QColorConstants::Cyan; #else QColor(Qt::cyan); #endif } // create chat bubble QPainterPath pointie; // left bottom pointie.moveTo(0, bodyheight + d_toppadding + d_bottompadding); // right bottom pointie.lineTo(0 + contentswidth + d_pointerwidth + d_leftpadding + d_rightpadding - d_radius, bodyheight + d_toppadding + d_bottompadding); pointie.arcTo(0 + contentswidth + d_pointerwidth + d_leftpadding + d_rightpadding - 2 * d_radius, bodyheight + d_toppadding + d_bottompadding - 2 * d_radius, 2 * d_radius, 2 * d_radius, 270, 90); // right top pointie.lineTo(0 + contentswidth + d_pointerwidth + d_leftpadding + d_rightpadding, 0 + d_radius); pointie.arcTo(0 + contentswidth + d_pointerwidth + d_leftpadding + d_rightpadding - 2 * d_radius, 0, 2 * d_radius, 2 * d_radius, 0, 90); // left top pointie.lineTo(0 + d_pointerwidth + d_radius, 0); pointie.arcTo(0 + d_pointerwidth, 0, 2 * d_radius, 2 * d_radius, 90, 90); // left bottom almost (here is the pointie) pointie.lineTo(0 + d_pointerwidth, bodyheight + d_toppadding + d_bottompadding - d_pointerheight); pointie.closeSubpath(); // rotate bubble for outgoing messages if (!outgoing) { painter->translate(option.rect.width() - pointie.boundingRect().width() - d_horizontalmargin - d_pointerwidth, 0); painter->translate(pointie.boundingRect().center()); painter->rotate(180); painter->translate(-pointie.boundingRect().center()); } painter->setPen(QPen(bgcolor)); painter->drawPath(pointie); painter->fillPath(pointie, QBrush(bgcolor)); // rotate back or painter is going to paint the text rotated if (!outgoing) { painter->translate(pointie.boundingRect().center()); painter->rotate(-180); painter->translate(-pointie.boundingRect().center()); } QAbstractTextDocumentLayout::PaintContext ctx; //if (outgoing) ctx.palette.setColor(QPalette::Text, QColor("black")); //else //ctx.palette.setColor(QPalette::Text, QColor("white")); painter->translate((!outgoing ? 0 : d_pointerwidth) + d_leftpadding, 0); bodydoc.documentLayout()->draw(painter, ctx); painter->restore(); } QSize MessageDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { QTextDocument bodydoc; QTextOption textOption(bodydoc.defaultTextOption()); QString bodytext(index.data(Qt::DisplayRole).toString()); textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); bodydoc.setDefaultTextOption(textOption); bodydoc.setHtml(bodytext); // the width of the contents are the (a fraction of the window width) minus (margins + padding + width of the bubble's tail) qreal contentswidth = option.rect.width() * d_widthfraction - d_horizontalmargin - d_pointerwidth - d_leftpadding - d_rightpadding; // set this available width on the text document bodydoc.setTextWidth(contentswidth); QSize size(bodydoc.idealWidth() + d_horizontalmargin + d_pointerwidth + d_leftpadding + d_rightpadding, bodydoc.size().height() + d_bottompadding + d_toppadding + d_verticalmargin + 1); if (index.row() == 0) // have extra margin at top of first item size += QSize(0, d_verticalmargin); return size; } int UserListModel::rowCount(const QModelIndex &) const { return userData.count(); } int UserListModel::columnCount(const QModelIndex &) const { return 5; } QVariant UserListModel::data(const QModelIndex &index, int role) const { const KSTUsersInfo &userInfo = userData.at(index.row()); if ( role == Qt::DisplayRole ) { switch ( index.column() ) { case 0: return userInfo.callsign; break; case 1: if ( userInfo.grid.isValid() ) return userInfo.grid.getGrid(); else return QString(); break; case 2: case 3: { const StationProfile &profile = StationProfilesManager::instance()->getCurProfile1(); if ( !profile.locator.isEmpty() ) { Gridsquare myGrid(profile.locator); if ( index.column() == 2 ) { double distance; if ( myGrid.distanceTo(userInfo.grid, distance) ) { return QString::number(distance, 'f', 1); } } if ( index.column() == 3 ) { double bearing; if ( myGrid.bearingTo(userInfo.grid, bearing) ) { return QString::number(bearing, 'f', 0); } } } return QString(); } break; case 4: return userInfo.stationComment; break; default: return QVariant(); } } else if ( index.column() == 0 && role == Qt::BackgroundRole) { return Data::statusToColor(userInfo.status, userInfo.dupeCount, QColor(Qt::transparent)); } else if (index.column() == 0 && role == Qt::ToolTipRole) { return QCoreApplication::translate("DBStrings", userInfo.dxcc.country.toUtf8().constData()) + " [" + Data::statusToText(userInfo.status) + "]"; } else if ( role == Qt::UserRole ) { switch ( index.column() ) { case 2: return data(index,Qt::DisplayRole).toFloat(); break; case 3: return data(index,Qt::DisplayRole).toInt(); break; default: return data(index, Qt::DisplayRole); } } return QVariant(); } QVariant UserListModel::headerData(int section, Qt::Orientation orientation, int role) const { if ( role != Qt::DisplayRole || orientation != Qt::Horizontal ) return QVariant(); switch (section) { case 0: return tr("Callsign"); case 1: return tr("Gridsquare"); case 2: return tr("Distance"); case 3: return tr("Azimuth"); case 4: return tr("Comment"); default: return QVariant(); } } void UserListModel::updateList(const QList &userList) { beginResetModel(); userData = userList; endResetModel(); } void UserListModel::clear() { beginResetModel(); userData.clear(); endResetModel(); } KSTUsersInfo UserListModel::getUserInfo(const QModelIndex &index) const { return userData.at(index.row()); } void HTMLDelegate::paint(QPainter* painter, const QStyleOptionViewItem & inOption, const QModelIndex &index) const { QStyleOptionViewItem option = inOption; initStyleOption(&option, index); QStyle *style = option.widget? option.widget->style() : QApplication::style(); QTextDocument doc; doc.setHtml(option.text); option.text = QString(); style->drawControl(QStyle::CE_ItemViewItem, &option, painter); QAbstractTextDocumentLayout::PaintContext ctx; // Highlighting text if item is selected if (option.state & QStyle::State_Selected) ctx.palette.setColor(QPalette::Text, option.palette.color(QPalette::Active, QPalette::HighlightedText)); QRect textRect = style->subElementRect(QStyle::SE_ItemViewItemText, &option); painter->save(); painter->translate(textRect.topLeft()); painter->setClipRect(textRect.translated(-textRect.topLeft())); doc.documentLayout()->draw(painter, ctx); painter->restore(); } QSize HTMLDelegate::sizeHint ( const QStyleOptionViewItem & inOption, const QModelIndex & index ) const { QStyleOptionViewItem option = inOption; initStyleOption(&option, index); QTextDocument doc; doc.setHtml(option.text); doc.setTextWidth(option.rect.width()); return QSize(doc.idealWidth(), doc.size().height()); } ================================================ FILE: ui/KSTChatWidget.h ================================================ #ifndef QLOG_UI_KSTCHATWIDGET_H #define QLOG_UI_KSTCHATWIDGET_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "service/kstchat/KSTChat.h" #include "ui/NewContactWidget.h" namespace Ui { class KSTChatWidget; } class ChatMessageModel : public QAbstractListModel { public: enum MessageDirection { OUTGOING = 0, INCOMING = 1, INCOMING_TOYOU = 2, INCOMING_HIGHLIGHT = 3 }; public: explicit ChatMessageModel(QObject* parent = nullptr) : QAbstractListModel(parent) {} int rowCount(const QModelIndex& parent = QModelIndex()) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; void addMessage(MessageDirection direction, const KSTChatMsg &msg); void clear(); KSTChatMsg getMessage(const QModelIndex &index) const; private: QList> messages; }; class HTMLDelegate : public QStyledItemDelegate { public: explicit HTMLDelegate(QObject* parent = nullptr) : QStyledItemDelegate(parent){}; protected: void paint ( QPainter * painter, const QStyleOptionViewItem & inOption, const QModelIndex & index ) const; QSize sizeHint ( const QStyleOptionViewItem & inOption, const QModelIndex & index ) const; }; class MessageDelegate : public QStyledItemDelegate { public: explicit MessageDelegate(QObject* parent = nullptr) : QStyledItemDelegate(parent), d_radius(10), d_toppadding(0), d_bottompadding(0), d_leftpadding(2), d_rightpadding(2), d_verticalmargin(2), d_horizontalmargin(2), d_pointerwidth(5), d_pointerheight(5), d_widthfraction(0.95) {} void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override; private: int d_radius; int d_toppadding; int d_bottompadding; int d_leftpadding; int d_rightpadding; int d_verticalmargin; int d_horizontalmargin; int d_pointerwidth; int d_pointerheight; float d_widthfraction; }; class UserListModel : public QAbstractTableModel { Q_OBJECT public: UserListModel(QObject* parent = 0) : QAbstractTableModel(parent) {} int rowCount(const QModelIndex& parent = QModelIndex()) const; int columnCount(const QModelIndex& parent = QModelIndex()) const; QVariant data(const QModelIndex& index, int role) const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; void updateList(const QList &userList); void clear(); KSTUsersInfo getUserInfo(const QModelIndex &index) const; private: QList userData; }; class KSTChatWidget : public QWidget { Q_OBJECT public: explicit KSTChatWidget(int chatRoomIndex, const QString& username, const QString& password, const NewContactWidget *, QWidget *parent = nullptr); ~KSTChatWidget(); QList getUserList(); signals: void chatClosed(); void chatUpdated(QWidget *); void valuableMessageUpdated(QWidget *); void prepareQSOInfo(QString, QString); void userListUpdated(QWidget *); void beamingRequested(double); public slots: void addChatMessage(KSTChatMsg); void sendMessage(); void updateUserList(); void setPrivateChatCallsign(QString); void reloadStationProfile(); void setBeamActionVisible(bool); void resetDupe(); void recalculateDupe(); void recalculateDxccStatus(); void updateSpotsStatusWhenQSOAdded(const QSqlRecord &record); void updateSpotsStatusWhenQSODeleted(const QSqlRecord &record); void updateSpotsDxccStatusWhenQSODeleted(const QSet &entities); private slots: void showChatError(const QString &); void closeChat(); void displayedColumns(); void userDoubleClicked(QModelIndex); void messageDoubleClicked(QModelIndex); void prefillQSOAction(); void highlightPressed(); bool isHighlightCandidate(KSTChatMsg &); void editHighlightRules(); void resetPressed(); void beamingRequest(); void clearValuableMessages(); private: Ui::KSTChatWidget *ui; QPointer messageModel; QPointer valuableMessageModel; QPointer chat; UserListModel* userListModel; QSortFilterProxyModel * proxyModel; QPointer highlightEvaluator; QString userName; void setSelectedCallsignInUserList(const QString &); }; #endif // QLOG_UI_KSTCHATWIDGET_H ================================================ FILE: ui/KSTChatWidget.ui ================================================ KSTChatWidget 0 0 374 197 Form Qt::Horizontal 4 Qt::Vertical 0 0 250 0 Qt::ClickFocus Qt::ActionsContextMenu Chat messages QAbstractScrollArea::AdjustToContents QAbstractItemView::NoEditTriggers false Qt::IgnoreAction QAbstractItemView::NoSelection QAbstractItemView::ScrollPerPixel QAbstractItemView::ScrollPerPixel QListView::Static false QListView::Adjust true Qt::ClickFocus Qt::ActionsContextMenu Valuable messages QAbstractItemView::ScrollPerPixel QAbstractItemView::ScrollPerPixel 0 0 100 0 Qt::ClickFocus Qt::ActionsContextMenu QAbstractItemView::NoEditTriggers true QAbstractItemView::ExtendedSelection QAbstractItemView::SelectRows QAbstractItemView::ScrollPerPixel QAbstractItemView::ScrollPerPixel true 0 0 0 Clear selected Callsign and Chat message entry. 16 16 true Qt::ClickFocus Send chat message true Column Visibility Which columns should be displayed Prepare QSO Transfer Callsign and Gridsquare Information to the New QSO dialog true Show About Me Only Show only messages where my callsign is present true Suppress User To User Suppress private messages between two callsigns true Highlight Highlight messages based on the setting Highlight Rules Beam Clear Messages Clear all messages in the window msgLineEdit returnPressed() KSTChatWidget sendMessage() 245 248 199 149 actionDisplayedColumns triggered() KSTChatWidget displayedColumns() -1 -1 200 120 usersTableView doubleClicked(QModelIndex) KSTChatWidget userDoubleClicked(QModelIndex) 327 104 200 120 actionPrefillQSO triggered() KSTChatWidget prefillQSOAction() -1 -1 200 120 actionHighlight triggered() KSTChatWidget highlightPressed() -1 -1 200 120 actionHighlightRules triggered() KSTChatWidget editHighlightRules() -1 -1 200 120 resetButton clicked() KSTChatWidget resetPressed() 109 218 200 120 messageListView doubleClicked(QModelIndex) KSTChatWidget messageDoubleClicked(QModelIndex) 133 104 200 120 actionBeam triggered() KSTChatWidget beamingRequest() -1 -1 200 120 actionClearValuableMessages triggered() KSTChatWidget clearValuableMessages() -1 -1 186 98 sendMessage() displayedColumns() userDoubleClicked(QModelIndex) prefillQSOAction() applyMessageFilter() toComboChanged(int) highlightPressed() editHighlightRules() resetPressed() messageDoubleClicked(QModelIndex) beamingRequest() clearValuableMessages() ================================================ FILE: ui/KSTHighlightRuleDetail.cpp ================================================ #include #include #include #include #include "KSTHighlightRuleDetail.h" #include "ui_KSTHighlightRuleDetail.h" #include "core/debug.h" #include "service/kstchat/KSTChat.h" MODULE_IDENTIFICATION("qlog.ui.ksthightlightruledetail"); KSTHighlightRuleDetail::KSTHighlightRuleDetail(const QString &ruleName, QWidget *parent) : QDialog(parent), ui(new Ui::KSTHighlightRuleDetail), condCount(0) { FCT_IDENTIFICATION; ui->setupUi(this); ui->roomCombo->addItem(tr("All")); ui->roomCombo->addItems(KSTChat::chatRooms); if ( ! ruleName.isEmpty() ) { loadRule(ruleName); } else { /* get Rules name from DB to checking whether a new rule name * will be unique */ ruleNamesList = chatHighlightEvaluator::getAllRuleNames(); } } KSTHighlightRuleDetail::~KSTHighlightRuleDetail() { FCT_IDENTIFICATION; delete ui; } void KSTHighlightRuleDetail::addCondition(int fieldIdx, int operatorId, QString value) { FCT_IDENTIFICATION; qCDebug(function_parameters) << "FieldIDX: " << fieldIdx << " Operator: " << operatorId << " Value: " << value; QHBoxLayout* conditionLayout = new QHBoxLayout(); conditionLayout->setObjectName(QString::fromUtf8("conditionLayout%1").arg(condCount)); /***************/ /* Field Combo */ /***************/ QComboBox* fieldNameCombo = new QComboBox(); fieldNameCombo->setObjectName(QString::fromUtf8("fieldNameCombo%1").arg(condCount)); QSizePolicy sizePolicy1(QSizePolicy::Maximum, QSizePolicy::Fixed); sizePolicy1.setHorizontalStretch(0); sizePolicy1.setVerticalStretch(0); sizePolicy1.setHeightForWidth(fieldNameCombo->sizePolicy().hasHeightForWidth()); fieldNameCombo->setSizePolicy(sizePolicy1); fieldNameCombo->addItem(tr("Sender")); fieldNameCombo->addItem(tr("Message")); fieldNameCombo->addItem(tr("Gridsquare")); if ( fieldIdx >= 0 ) { fieldNameCombo->setCurrentIndex(fieldIdx); } conditionLayout->addWidget(fieldNameCombo); /*******************/ /* Condition Combo */ /*******************/ QComboBox* conditionCombo = new QComboBox(); conditionCombo->addItem(QString(tr("Contains"))); conditionCombo->addItem(QString(tr("Starts with"))); conditionCombo->setObjectName(QString::fromUtf8("conditionCombo%1").arg(condCount)); if ( operatorId >= 0 ) { conditionCombo->setCurrentIndex(operatorId); } conditionLayout->addWidget(conditionCombo); /**************/ /* Value Edit */ /**************/ QLineEdit* valueEdit = new QLineEdit(); valueEdit->setObjectName(QString::fromUtf8("valueLineEdit%1").arg(condCount)); QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); sizePolicy.setHorizontalStretch(0); sizePolicy.setVerticalStretch(0); valueEdit->setSizePolicy(sizePolicy); valueEdit->setText(value); conditionLayout->addWidget(valueEdit); /*****************/ /* Remove Button */ /*****************/ QPushButton* removeButton = new QPushButton(tr("Remove")); removeButton->setObjectName(QString::fromUtf8("removeButton%1").arg(condCount)); QSizePolicy sizePolicy3(QSizePolicy::Maximum, QSizePolicy::Fixed); removeButton->setSizePolicy(sizePolicy3); conditionLayout->addWidget(removeButton); connect(removeButton, &QPushButton::clicked, this, [conditionLayout]() { QLayoutItem *item = NULL; while ((item = conditionLayout->takeAt(0)) != 0) { delete item->widget(); delete item; } conditionLayout->deleteLater(); }); /**************************/ /* Add to the main layout */ /**************************/ ui->conditionsLayout->addLayout(conditionLayout); condCount++; } void KSTHighlightRuleDetail::save() { FCT_IDENTIFICATION; if ( ui->ruleLineEdit->text().isEmpty() ) { ui->ruleLineEdit->setPlaceholderText(tr("Must not be empty")); return; } if ( ruleExists(ui->ruleLineEdit->text()) ) { QMessageBox::warning(nullptr, QMessageBox::tr("QLog Info"), QMessageBox::tr("Filter name is already exists.")); return; } chatHighlightRule rule; rule.ruleName = ui->ruleLineEdit->text(); rule.ruleRoomIndex = ui->roomCombo->currentIndex(); rule.enabled = ui->enabledCheckbox->isChecked(); rule.interConditionOperand = static_cast(ui->matchCombo->currentIndex()); const QList &conditionLayouts = ui->conditionsLayout->findChildren(); for (auto &conditionLine: conditionLayouts ) { chatHighlightRule::Condition condition; for ( int i = 0; i < 3; i++ ) { QString objectName = conditionLine->itemAt(i)->widget()->objectName(); if ( objectName.contains("fieldNameCom") ) { condition.source = static_cast(dynamic_cast(conditionLine->itemAt(i)->widget())->currentIndex()); } if ( objectName.contains("conditionCombo") ) { condition.operatorID = static_cast(dynamic_cast(conditionLine->itemAt(i)->widget())->currentIndex()); } if ( objectName.contains("valueLineEdit") ) { condition.value = dynamic_cast(conditionLine->itemAt(i)->widget())->text(); } } qCDebug(runtime)<< "Condition Values: " << ui->ruleLineEdit->text() << " " << condition.source << " " << condition.operatorID << " " << condition.value; rule.conditions << condition; } rule.save(); accept(); } void KSTHighlightRuleDetail::ruleNameChanged(const QString &newRuleName) { FCT_IDENTIFICATION; QPalette p; if ( ruleExists(newRuleName) ) { p.setColor(QPalette::Text,Qt::red); } else { p.setColor(QPalette::Text,qApp->palette().text().color()); } ui->ruleLineEdit->setPalette(p); } void KSTHighlightRuleDetail::loadRule(const QString &ruleName) { FCT_IDENTIFICATION; chatHighlightRule rule; if ( !rule.load(ruleName) ) { qWarning() << "Cannot load rule " << ruleName; } ui->ruleLineEdit->setText(ruleName); ui->ruleLineEdit->setEnabled(false); ui->roomCombo->setCurrentIndex(rule.ruleRoomIndex); ui->enabledCheckbox->setChecked(rule.enabled); ui->matchCombo->setCurrentIndex(rule.interConditionOperand); for ( const chatHighlightRule::Condition &condition : static_cast&>(rule.conditions)) { addCondition(condition.source, condition.operatorID, condition.value); } } bool KSTHighlightRuleDetail::ruleExists(const QString &ruleName) { FCT_IDENTIFICATION; qCDebug(function_parameters) << ruleName; return ruleNamesList.contains(ruleName); } ================================================ FILE: ui/KSTHighlightRuleDetail.h ================================================ #ifndef QLOG_UI_KSTHIGHLIGHTRULEDETAIL_H #define QLOG_UI_KSTHIGHLIGHTRULEDETAIL_H #include namespace Ui { class KSTHighlightRuleDetail; } class KSTHighlightRuleDetail : public QDialog { Q_OBJECT public: explicit KSTHighlightRuleDetail(const QString &ruleName = QString(), QWidget *parent = nullptr); ~KSTHighlightRuleDetail(); public slots: void addCondition(int fieldIdx = -1, int operatorId = -1, QString value = QString()); void save(); void ruleNameChanged(const QString&); private: Ui::KSTHighlightRuleDetail *ui; QStringList ruleNamesList; int condCount; void loadRule(const QString &ruleName); bool ruleExists(const QString &); }; #endif // QLOG_UI_KSTHIGHLIGHTRULEDETAIL_H ================================================ FILE: ui/KSTHighlightRuleDetail.ui ================================================ KSTHighlightRuleDetail 0 0 563 265 Edit Rule Rule Name Chat Room Enabled Qt::LeftToRight Highlight message which match 0 0 All the following conditions Any of the following conditions Qt::Vertical QSizePolicy::Preferred 10 20 0 0 Add Condition Qt::Horizontal 40 20 Qt::Vertical 20 40 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() KSTHighlightRuleDetail save() 248 254 157 274 buttonBox rejected() KSTHighlightRuleDetail reject() 316 260 286 274 ruleLineEdit textChanged(QString) KSTHighlightRuleDetail ruleNameChanged(QString) 368 23 281 132 addConditionButton clicked() KSTHighlightRuleDetail addCondition() 62 125 281 132 ruleNameChanged(QString) addCondition() save() ================================================ FILE: ui/KSTHighlighterSettingDialog.cpp ================================================ #include "KSTHighlighterSettingDialog.h" #include "ui_KSTHighlighterSettingDialog.h" #include "core/debug.h" #include "ui/component/StyleItemDelegate.h" #include "ui/KSTHighlightRuleDetail.h" MODULE_IDENTIFICATION("qlog.ui.ksthighlightersettingdialog"); KSTHighlighterSettingDialog::KSTHighlighterSettingDialog(QWidget *parent) : QDialog(parent), ui(new Ui::KSTHighlighterSettingDialog) { ui->setupUi(this); rulesModel = new QSqlTableModel(); rulesModel->setTable("chat_highlight_rules"); rulesModel->setHeaderData(rulesModel->fieldIndex("rule_name"), Qt::Horizontal, tr("Name")); rulesModel->setHeaderData(rulesModel->fieldIndex("enabled"), Qt::Horizontal, tr("State")); ui->rulesView->setModel(rulesModel); for ( int i = 0 ; i < rulesModel->columnCount(); i++ ) { ui->rulesView->hideColumn(i); } ui->rulesView->showColumn(rulesModel->fieldIndex("rule_name")); ui->rulesView->showColumn(rulesModel->fieldIndex("enabled")); ui->rulesView->setColumnWidth(0, 300); ui->rulesView->setItemDelegateForColumn(rulesModel->fieldIndex("enabled"), new CheckBoxDelegate(ui->rulesView)); rulesModel->select(); } KSTHighlighterSettingDialog::~KSTHighlighterSettingDialog() { delete ui; } void KSTHighlighterSettingDialog::addRule() { FCT_IDENTIFICATION; KSTHighlightRuleDetail dialog(QString(), this); dialog.exec(); rulesModel->select(); } void KSTHighlighterSettingDialog::removeRule() { FCT_IDENTIFICATION; rulesModel->removeRow(ui->rulesView->currentIndex().row()); ui->rulesView->clearSelection(); rulesModel->select(); } void KSTHighlighterSettingDialog::editRule(QModelIndex idx) { FCT_IDENTIFICATION; const QModelIndex &nameIdx = ui->rulesView->model()->index(idx.row(),0); const QString &ruleName = ui->rulesView->model()->data(nameIdx).toString(); KSTHighlightRuleDetail dialog(ruleName, this); dialog.exec(); rulesModel->select(); } void KSTHighlighterSettingDialog::editRuleButton() { FCT_IDENTIFICATION; foreach (QModelIndex index, ui->rulesView->selectionModel()->selectedRows()) { editRule(index); } } ================================================ FILE: ui/KSTHighlighterSettingDialog.h ================================================ #ifndef QLOG_UI_KSTHIGHLIGHTERSETTINGDIALOG_H #define QLOG_UI_KSTHIGHLIGHTERSETTINGDIALOG_H #include #include namespace Ui { class KSTHighlighterSettingDialog; } class KSTHighlighterSettingDialog : public QDialog { Q_OBJECT public: explicit KSTHighlighterSettingDialog(QWidget *parent = nullptr); ~KSTHighlighterSettingDialog(); QSqlTableModel* rulesModel; public slots: void addRule(); void removeRule(); void editRule(QModelIndex); void editRuleButton(); private: Ui::KSTHighlighterSettingDialog *ui; }; #endif // QLOG_UI_KSTHIGHLIGHTERSETTINGDIALOG_H ================================================ FILE: ui/KSTHighlighterSettingDialog.ui ================================================ KSTHighlighterSettingDialog 0 0 542 415 Highlight Rules Rules QAbstractItemView::NoEditTriggers true QAbstractItemView::SingleSelection QAbstractItemView::SelectRows QAbstractItemView::ScrollPerPixel QAbstractItemView::ScrollPerPixel true 50 100 true false false Qt::Vertical 20 40 Add Edit Remove Qt::Vertical 20 40 QDialogButtonBox::Ok rulesView doubleClicked(QModelIndex) KSTHighlighterSettingDialog editRule(QModelIndex) 220 201 264 207 removeRuleButton clicked() KSTHighlighterSettingDialog removeRule() 477 223 264 207 editRuleButton clicked() KSTHighlighterSettingDialog editRuleButton() 477 192 264 207 addRuleButton clicked() KSTHighlighterSettingDialog addRule() 477 161 264 207 buttonBox accepted() KSTHighlighterSettingDialog accept() 264 393 264 207 buttonBox rejected() KSTHighlighterSettingDialog reject() 264 393 264 207 editRule(QModelIndex) removeRule() editRuleButton() addRule() ================================================ FILE: ui/KSTWidget.cpp ================================================ ================================================ FILE: ui/KSTWidget.h ================================================ ================================================ FILE: ui/KSTWidget.ui ================================================ ================================================ FILE: ui/LoadDatabaseDialog.cpp ================================================ #include #include #include #include #include #include #include #include #include #include "LoadDatabaseDialog.h" #include "ui_LoadDatabaseDialog.h" #include "core/PasswordCipher.h" #include "core/FileCompressor.h" #include "core/debug.h" MODULE_IDENTIFICATION("qlog.ui.loaddatabasedialog"); const QString LoadDatabaseDialog::importConnectionName = QStringLiteral("LoadDatabaseDialogImport"); LoadDatabaseDialog::LoadDatabaseDialog(QWidget *parent) : QDialog(parent), ui(new Ui::LoadDatabaseDialog) { FCT_IDENTIFICATION; ui->setupUi(this); // Add custom Load & Restart button loadButton = ui->buttonBox->addButton(tr("Load && Restart"), QDialogButtonBox::AcceptRole); loadButton->setEnabled(false); // Connect buttonBox accepted signal to our accept() override connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &LoadDatabaseDialog::accept); // Initial state ui->decryptGroup->setEnabled(false); } LoadDatabaseDialog::~LoadDatabaseDialog() { closeImportDatabase(); delete ui; } QString LoadDatabaseDialog::getSelectedFile() const { return selectedFile; } QString LoadDatabaseDialog::getDecompressedFile() const { return tempDecompressedFile; } QString LoadDatabaseDialog::takeDecompressedFile() { // Close SQL connection first to release file lock if ( QSqlDatabase::contains(importConnectionName) ) { { QSqlDatabase db = QSqlDatabase::database(importConnectionName); db.close(); } QSqlDatabase::removeDatabase(importConnectionName); } QString path = tempDecompressedFile; tempDecompressedFile.clear(); // Transfer ownership - destructor won't delete return path; } QString LoadDatabaseDialog::getPassword() const { return ui->passwordEdit->text(); } bool LoadDatabaseDialog::isCrossPlatform() const { if ( !dbInfo.valid ) return false; return dbInfo.sourcePlatform != LogDatabase::currentPlatformId(); } bool LoadDatabaseDialog::requiresPassword() const { return !encryptedPasswordsBlob.isEmpty(); } void LoadDatabaseDialog::accept() { FCT_IDENTIFICATION; // Verify password before accepting if ( !verifyPassword() ) return; QDialog::accept(); } bool LoadDatabaseDialog::verifyPassword() { FCT_IDENTIFICATION; // No password needed if ( encryptedPasswordsBlob.isEmpty() ) return true; const QString password = ui->passwordEdit->text(); // Try to decrypt QByteArray json; if ( !PasswordCipher::decrypt(password, encryptedPasswordsBlob, json) ) { ui->passwordStatusLabel->setText( QString("✗ %1").arg(tr("Invalid password"))); ui->passwordEdit->clear(); ui->passwordEdit->setFocus(); return false; } // Verify JSON structure QJsonParseError parseError; QJsonDocument doc = QJsonDocument::fromJson(json, &parseError); if ( parseError.error != QJsonParseError::NoError ) { ui->passwordStatusLabel->setText( QString("✗ %1").arg(tr("Invalid password"))); ui->passwordEdit->clear(); ui->passwordEdit->setFocus(); return false; } return true; } void LoadDatabaseDialog::browseFile() { FCT_IDENTIFICATION; const QString documentsPath = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); QString filename = QFileDialog::getOpenFileName( this, tr("Select Database File"), documentsPath, tr("QLog Database Export (*.dbe);;All Files (*)") ); if ( filename.isEmpty() ) return; // Close previous import DB if any closeImportDatabase(); selectedFile = filename; ui->fileEdit->setText(filename); // Open and validate the database openImportDatabase(); } void LoadDatabaseDialog::openImportDatabase() { FCT_IDENTIFICATION; // Create temporary file for decompressed database QTemporaryFile tempFile; tempFile.setAutoRemove(false); // We manage deletion ourselves if ( !tempFile.open() ) { dbInfo.valid = false; dbInfo.errorMessage = tr("Cannot create temporary file"); ui->statusLabel->setText(QString("✗ %1") .arg(dbInfo.errorMessage)); ui->decryptGroup->setEnabled(false); ui->passwordEdit->clear(); encryptedPasswordsBlob.clear(); updateLoadButtonState(); return; } tempDecompressedFile = tempFile.fileName(); tempFile.close(); // Decompress the .dbe file (with progress dialog) if ( !FileCompressor::gunzipFileWithProgress(selectedFile, tempDecompressedFile, this, tr("Decompressing database...")) ) { dbInfo.valid = false; dbInfo.errorMessage = tr("Cannot decompress database file"); ui->statusLabel->setText(QString("✗ %1") .arg(dbInfo.errorMessage)); ui->decryptGroup->setEnabled(false); ui->passwordEdit->clear(); encryptedPasswordsBlob.clear(); QFile::remove(tempDecompressedFile); tempDecompressedFile.clear(); updateLoadButtonState(); return; } // Get basic info using LogDatabase::inspectDatabase on decompressed file dbInfo = LogDatabase::inspectDatabase(tempDecompressedFile); if ( !dbInfo.valid ) { ui->statusLabel->setText(QString("✗ %1") .arg(dbInfo.errorMessage)); ui->decryptGroup->setEnabled(false); ui->passwordEdit->clear(); encryptedPasswordsBlob.clear(); updateLoadButtonState(); return; } // Open the decompressed database and cache encrypted passwords blob { QSqlDatabase importDb = QSqlDatabase::addDatabase("QSQLITE", importConnectionName); importDb.setDatabaseName(tempDecompressedFile); if ( !importDb.open() ) { qWarning() << "Cannot open import database:" << importDb.lastError().text(); dbInfo.valid = false; dbInfo.errorMessage = tr("Cannot open database"); ui->statusLabel->setText(QString("✗ %1") .arg(dbInfo.errorMessage)); ui->decryptGroup->setEnabled(false); encryptedPasswordsBlob.clear(); updateLoadButtonState(); return; } // Read encrypted passwords blob QSqlQuery query(importDb); if ( query.exec("SELECT value FROM log_param WHERE name = 'security/encryptedpasswords'") ) { if ( query.first() && !query.value(0).toString().isEmpty() ) encryptedPasswordsBlob = QByteArray::fromBase64(query.value(0).toByteArray()); } importDb.close(); } QSqlDatabase::removeDatabase(importConnectionName); // Build status message QString status = QString("✓ %1") .arg(tr("Valid database")); if ( isCrossPlatform() ) { status += QString("
⚠ %1 (%2)") .arg(tr("Different platform"), dbInfo.sourcePlatform); } ui->statusLabel->setText(status); // Enable password group if database has encrypted passwords if ( !encryptedPasswordsBlob.isEmpty() ) { ui->decryptGroup->setEnabled(true); ui->passwordStatusLabel->setText(tr("Password required to import credentials")); } else { ui->decryptGroup->setEnabled(false); ui->passwordStatusLabel->setText(tr("No encrypted credentials in database")); } ui->passwordEdit->clear(); updateLoadButtonState(); } void LoadDatabaseDialog::closeImportDatabase() { FCT_IDENTIFICATION; if ( QSqlDatabase::contains(importConnectionName) ) { { QSqlDatabase db = QSqlDatabase::database(importConnectionName); db.close(); } QSqlDatabase::removeDatabase(importConnectionName); } // Remove temporary decompressed file if ( !tempDecompressedFile.isEmpty() ) { QFile::remove(tempDecompressedFile); tempDecompressedFile.clear(); } encryptedPasswordsBlob.clear(); } void LoadDatabaseDialog::updateLoadButtonState() { FCT_IDENTIFICATION; // Reset password status label to default text when user starts typing // (but not when password is cleared programmatically after invalid attempt) if ( !encryptedPasswordsBlob.isEmpty() && !ui->passwordEdit->text().isEmpty() ) ui->passwordStatusLabel->setText(tr("Password required to import credentials")); // Load & Restart is enabled when: // 1. Database is valid // 2. Either no password is needed OR password field is not empty bool passwordOk = encryptedPasswordsBlob.isEmpty() || !ui->passwordEdit->text().isEmpty(); loadButton->setEnabled(dbInfo.valid && passwordOk); } ================================================ FILE: ui/LoadDatabaseDialog.h ================================================ #ifndef QLOG_UI_LOADDATABASEDIALOG_H #define QLOG_UI_LOADDATABASEDIALOG_H #include #include #include #include "core/LogDatabase.h" namespace Ui { class LoadDatabaseDialog; } class LoadDatabaseDialog : public QDialog { Q_OBJECT public: explicit LoadDatabaseDialog(QWidget *parent = nullptr); ~LoadDatabaseDialog(); QString getSelectedFile() const; QString getDecompressedFile() const; QString takeDecompressedFile(); // Returns path and transfers ownership (caller must delete) QString getPassword() const; bool isCrossPlatform() const; bool requiresPassword() const; public slots: void accept() override; private slots: void browseFile(); void updateLoadButtonState(); private: void openImportDatabase(); void closeImportDatabase(); bool verifyPassword(); Ui::LoadDatabaseDialog *ui; QPushButton *loadButton; QString selectedFile; DatabaseInfo dbInfo; // Cached encrypted passwords blob from import DB QByteArray encryptedPasswordsBlob; // Temporary decompressed file path QString tempDecompressedFile; static const QString importConnectionName; }; #endif // QLOG_UI_LOADDATABASEDIALOG_H ================================================ FILE: ui/LoadDatabaseDialog.ui ================================================ LoadDatabaseDialog 0 0 500 411 Unpack Data & Settings true Warning <html><head/><body><p>⚠ <span style=" font-weight:700;">Current database will be DELETED!</span><br/>⚠ <span style=" font-weight:700;">All stored passwords will be DELETED!</span><br/>⚠ <span style=" font-weight:700;">The application will restart after loading</span>.</p><p>To preserve data, use Pack Data &amp; Settings or Export QSOs first.</p></body></html> Select Database File: true Browse Status: No file selected true Decrypt Credentials Enter password to decrypt credentials Password: QLineEdit::Password Qt::Vertical 20 40 Qt::Horizontal QDialogButtonBox::Cancel buttonBox rejected() LoadDatabaseDialog reject() 316 260 286 274 browseButton clicked() LoadDatabaseDialog browseFile() 437 201 249 205 passwordEdit textChanged(QString) LoadDatabaseDialog updateLoadButtonState() 289 335 249 205 browseFile() updateLoadButtonState() ================================================ FILE: ui/LogbookWidget.cpp ================================================ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "logformat/AdiFormat.h" #include "models/LogbookModel.h" #include "models/SqlListModel.h" #include "service/clublog/ClubLog.h" #include "LogbookWidget.h" #include "ui_LogbookWidget.h" #include "ui/component/StyleItemDelegate.h" #include "core/debug.h" #include "models/SqlListModel.h" #include "ui/ColumnSettingDialog.h" #include "data/Data.h" #include "ui/ExportDialog.h" #include "service/eqsl/Eqsl.h" #include "ui/PaperQSLDialog.h" #include "ui/QSODetailDialog.h" #include "core/MembershipQE.h" #include "service/GenericCallbook.h" #include "core/QSOFilterManager.h" #include "core/LogParam.h" MODULE_IDENTIFICATION("qlog.ui.logbookwidget"); LogbookWidget::LogbookWidget(QWidget *parent) : QWidget(parent), ui(new Ui::LogbookWidget), blockClublogSignals(false), lookupDialog(nullptr) { FCT_IDENTIFICATION; ui->setupUi(this); searchTypeList.insert(CALLSIGN_SEARCH, SearchDefinition(CALLSIGN_SEARCH, ui->actionSearchCallsign, "callsign")); searchTypeList.insert(GRIDSQUARE_SEARCH, SearchDefinition(GRIDSQUARE_SEARCH, ui->actionSearchGrid, "gridsquare")); searchTypeList.insert(POTA_SEARCH, SearchDefinition(POTA_SEARCH, ui->actionSearchPOTA, "pota_ref")); searchTypeList.insert(SOTA_SEARCH, SearchDefinition(SOTA_SEARCH, ui->actionSearchSOTA, "sota_ref")); searchTypeList.insert(WWFF_SEARCH, SearchDefinition(WWFF_SEARCH, ui->actionSearchWWFF, "wwff_ref")); searchTypeList.insert(SIG_SEARCH, SearchDefinition(SIG_SEARCH, ui->actionSearchSIG, "sig_intl")); searchTypeList.insert(IOTA_SEARCH, SearchDefinition(IOTA_SEARCH, ui->actionSearchIOTA, "iota")); setupSearchMenu(); connect(ui->countrySelectFilter, &SmartSearchBox::currentTextChanged, this, &LogbookWidget::countryFilterChanged); connect(ui->bandSelectFilter, &SmartSearchBox::currentTextChanged, this, &LogbookWidget::bandFilterChanged); connect(ui->modeSelectFilter, &SmartSearchBox::currentTextChanged, this, &LogbookWidget::modeFilterChanged); connect(ui->userSelectFilter, &SmartSearchBox::currentTextChanged, this, &LogbookWidget::userFilterChanged); connect(ui->clubSelectFilter, &SmartSearchBox::currentTextChanged, this, &LogbookWidget::clubFilterChanged); model = new LogbookModel(this); connect(model, &LogbookModel::beforeUpdate, this, &LogbookWidget::handleBeforeUpdate); connect(model, &LogbookModel::beforeDelete, this, &LogbookWidget::handleBeforeDelete); connect(ui->contactTable, &QTableQSOView::dataCommitted, this, [this](){emit logbookUpdated();}); /* Callbook Signals registration */ connect(&callbookManager, &CallbookManager::callsignResult, this, &LogbookWidget::callsignFound); connect(&callbookManager, &CallbookManager::callsignNotFound, this, &LogbookWidget::callsignNotFound); connect(&callbookManager, &CallbookManager::loginFailed, this, &LogbookWidget::callbookLoginFailed); connect(&callbookManager, &CallbookManager::lookupError, this, &LogbookWidget::callbookError); ui->contactTable->setModel(model); QAction *separator = new QAction(ui->contactTable); separator->setSeparator(true); QAction *separator1 = new QAction(ui->contactTable); separator1->setSeparator(true); QAction *separator2 = new QAction(ui->contactTable); separator2->setSeparator(true); QAction *separator3 = new QAction(ui->contactTable); separator3->setSeparator(true); ui->contactTable->addAction(ui->actionEditContact); ui->contactTable->addAction(ui->actionFilter); ui->contactTable->addAction(ui->actionLookup); ui->contactTable->addAction(ui->actionSendDXCSpot); ui->contactTable->addAction(separator); ui->contactTable->addAction(ui->actionExportAs); ui->contactTable->addAction(ui->actionCallbookLookup); ui->contactTable->addAction(separator1); ui->contactTable->addAction(ui->actionDisplayedColumns); ui->contactTable->addAction(separator2); ui->contactTable->addAction(ui->actionQSLRcvd); ui->contactTable->addAction(ui->actionQSLRequested); ui->contactTable->addAction(separator3); ui->contactTable->addAction(ui->actionDeleteContact); connect(ui->actionQSLRcvd, &QAction::triggered, this, &LogbookWidget::markQslReceived); connect(ui->actionQSLRequested, &QAction::triggered, this, &LogbookWidget::markQslRequested); ui->contactTable->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu); connect(ui->contactTable->horizontalHeader(), &QHeaderView::customContextMenuRequested, this, &LogbookWidget::showTableHeaderContextMenu); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_TIME_ON, new TimestampFormatDelegate(ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_TIME_OFF, new TimestampFormatDelegate(ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_CALL, new CallsignDelegate(ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_FREQUENCY, new UnitFormatDelegate("", 6, 0.001, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_BAND, new ComboFormatDelegate(new SqlListModel("SELECT name FROM bands ORDER BY start_freq", " ", ui->contactTable), ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_MODE, new ComboFormatDelegate(new SqlListModel("SELECT name FROM modes", " ", ui->contactTable), ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_CONTINENT, new ComboFormatDelegate(QStringList() << " " << Data::getContinentList(), ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_QSL_SENT, new ComboFormatDelegate(Data::instance()->qslSentEnum, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_QSL_SENT_VIA, new ComboFormatDelegate(Data::instance()->qslSentViaEnum, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_QSL_RCVD, new ComboFormatDelegate(Data::instance()->qslRcvdEnum, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_QSL_RCVD_VIA, new ComboFormatDelegate(Data::instance()->qslSentViaEnum, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_QSL_SENT_DATE, new DateFormatDelegate(ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_QSL_RCVD_DATE, new DateFormatDelegate(ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_LOTW_SENT, new ComboFormatDelegate(Data::instance()->qslSentEnum, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_LOTW_RCVD, new ComboFormatDelegate(Data::instance()->qslRcvdEnum, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_LOTW_RCVD_DATE, new DateFormatDelegate(ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_LOTW_SENT_DATE, new DateFormatDelegate(ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_TX_POWER, new UnitFormatDelegate("W", 3, 0.1, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_AGE, new UnitFormatDelegate("", 0, 1, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_ALTITUDE, new UnitFormatDelegate("m", 2, 0.1, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_A_INDEX, new UnitFormatDelegate("", 0, 1, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_ANT_AZ, new UnitFormatDelegate("", 1, 0.1, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_ANT_EL, new UnitFormatDelegate("", 1, 0.1, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_ANT_PATH, new ComboFormatDelegate(Data::instance()->antPathEnum, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_CLUBLOG_QSO_UPLOAD_DATE, new DateFormatDelegate(ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_CLUBLOG_QSO_UPLOAD_STATUS, new ComboFormatDelegate(Data::instance()->uploadStatusEnum, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_DCL_QSLRDATE, new DateFormatDelegate(ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_DCL_QSLSDATE, new DateFormatDelegate(ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_DCL_QSL_RCVD, new ComboFormatDelegate(Data::instance()->qslRcvdEnum, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_DCL_QSL_SENT, new ComboFormatDelegate(Data::instance()->qslSentEnum, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_DISTANCE, new DistanceFormatDelegate(1, 0.1, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_EQSL_AG, new ComboFormatDelegate(Data::instance()->eqslAgEnum, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_EQSL_QSLRDATE, new DateFormatDelegate(ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_EQSL_QSLSDATE, new DateFormatDelegate(ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_EQSL_QSL_RCVD, new ComboFormatDelegate(Data::instance()->qslRcvdEnum, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_EQSL_QSL_SENT, new ComboFormatDelegate(Data::instance()->qslSentEnum, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_FISTS, new UnitFormatDelegate("", 0, 1, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_FISTS_CC, new UnitFormatDelegate("", 0, 1, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_FORCE_INIT, new ComboFormatDelegate(Data::instance()->boolEnum, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_FREQ_RX, new UnitFormatDelegate("", 6, 0.001, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_BAND_RX, new ComboFormatDelegate(new SqlListModel("SELECT name FROM bands ORDER BY start_freq", " ", ui->contactTable), ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_HAMLOGEU_QSO_UPLOAD_DATE, new DateFormatDelegate(ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_HAMLOGEU_QSO_UPLOAD_STATUS, new ComboFormatDelegate(Data::instance()->uploadStatusEnum, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_HAMQTH_QSO_UPLOAD_DATE, new DateFormatDelegate(ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_HAMQTH_QSO_UPLOAD_STATUS, new ComboFormatDelegate(Data::instance()->uploadStatusEnum, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_HRDLOG_QSO_UPLOAD_DATE, new DateFormatDelegate(ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_HRDLOG_QSO_UPLOAD_STATUS, new ComboFormatDelegate(Data::instance()->uploadStatusEnum, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_IOTA_ISLAND_ID, new UnitFormatDelegate("", 0, 1, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_K_INDEX, new UnitFormatDelegate("", 0, 1, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_MORSE_KEY_TYPE, new ComboFormatDelegate(Data::instance()->morseKeyTypeEnum, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_MAX_BURSTS, new UnitFormatDelegate("", 0, 1, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_MY_ALTITUDE, new UnitFormatDelegate("m", 2, 0.1, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_MY_CQ_ZONE, new UnitFormatDelegate("", 0, 1, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_MY_DXCC, new UnitFormatDelegate("", 0, 1, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_MY_FISTS, new UnitFormatDelegate("", 0, 1, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_MY_IOTA_ISLAND_ID, new UnitFormatDelegate("", 0, 1, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_MY_ITU_ZONE, new UnitFormatDelegate("", 0, 1, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_MY_MORSE_KEY_TYPE, new ComboFormatDelegate(Data::instance()->morseKeyTypeEnum, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_NR_BURSTS, new UnitFormatDelegate("", 0, 1, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_NR_PINGS, new UnitFormatDelegate("", 0, 1, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_NOTES_INTL, new TextBoxDelegate(this)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_NOTES, new TextBoxDelegate(this)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_PROP_MODE, new ComboFormatDelegate(Data::instance()->propagationModesIDList(), ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_QRZCOM_QSO_UPLOAD_DATE, new DateFormatDelegate(ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_QRZCOM_QSO_UPLOAD_STATUS, new ComboFormatDelegate(Data::instance()->uploadStatusEnum, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_QRZCOM_QSO_DOWNLOAD_DATE, new DateFormatDelegate(ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_QRZCOM_QSO_DOWNLOAD_STATUS, new ComboFormatDelegate(Data::instance()->downloadStatusEnum, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_QSO_COMPLETE, new ComboFormatDelegate(Data::instance()->qsoCompleteEnum, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_QSO_RANDOM, new ComboFormatDelegate(Data::instance()->boolEnum, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_RX_PWR, new UnitFormatDelegate("W", 3, 0.1, ui->contactTable)); /*https://www.pe0sat.vgnet.nl/satellite/sat-information/modes/ */ /* use all possible values, do not use only modern modes in sat_modes.json */ ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_SAT_MODE, new ComboFormatDelegate(QStringList()<<" "<<"VU"<<"VV"<<"UV"<<"UU"<<"US"<<"LU"<<"LS"<<"LX"<<"VS"<<"SX"<<"K"<<"T"<<"A"<<"J"<<"B"<<"S"<<"L", ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_SFI, new UnitFormatDelegate("", 0, 1, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_SILENT_KEY, new ComboFormatDelegate(Data::instance()->boolEnum, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_SRX, new UnitFormatDelegate("", 0, 1, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_STX, new UnitFormatDelegate("", 0, 1, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_SWL, new ComboFormatDelegate(Data::instance()->boolEnum, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_TEN_TEN, new UnitFormatDelegate("", 0, 1, ui->contactTable)); ui->contactTable->setItemDelegateForColumn(LogbookModel::COLUMN_UKSMG, new UnitFormatDelegate("", 0, 1, ui->contactTable)); const QByteArray &logbookState = LogParam::getLogbookState(); if ( !logbookState.isEmpty() ) ui->contactTable->horizontalHeader()->restoreState(logbookState); else { /* Hide all */ for ( int i = 0; i < LogbookModel::COLUMN_LAST_ELEMENT; i++ ) { ui->contactTable->hideColumn(i); } /* Set a basic set of columns */ ui->contactTable->showColumn(LogbookModel::COLUMN_TIME_ON); ui->contactTable->showColumn(LogbookModel::COLUMN_CALL); ui->contactTable->showColumn(LogbookModel::COLUMN_RST_RCVD); ui->contactTable->showColumn(LogbookModel::COLUMN_RST_SENT); ui->contactTable->showColumn(LogbookModel::COLUMN_FREQUENCY); ui->contactTable->showColumn(LogbookModel::COLUMN_MODE); ui->contactTable->showColumn(LogbookModel::COLUMN_NAME_INTL); ui->contactTable->showColumn(LogbookModel::COLUMN_QTH_INTL); ui->contactTable->showColumn(LogbookModel::COLUMN_COMMENT_INTL); } ui->contactTable->setSortingEnabled(true); ui->contactTable->horizontalHeader()->setSectionsMovable(true); ui->contactTable->setStyle(new ProxyStyle(ui->contactTable->style())); ui->contactTable->installEventFilter(this); setDefaultSort(); ui->bandSelectFilter->blockSignals(true); ui->bandSelectFilter->setModel(new SqlListModel("SELECT name FROM bands ORDER BY start_freq", tr("All Bands"), ui->bandSelectFilter)); ui->bandSelectFilter->adjustMaxSize(); ui->bandSelectFilter->setHighlightWhenEnable(true); ui->bandSelectFilter->blockSignals(false); ui->modeSelectFilter->blockSignals(true); ui->modeSelectFilter->setModel(new SqlListModel("SELECT name FROM modes", tr("All Modes"), ui->modeSelectFilter)); ui->modeSelectFilter->adjustMaxSize(); ui->modeSelectFilter->setHighlightWhenEnable(true); ui->modeSelectFilter->blockSignals(false); ui->countrySelectFilter->blockSignals(true); ui->countrySelectFilter->setModel(new SqlListModel("SELECT id, translate_to_locale(name) " "FROM dxcc_entities_clublog WHERE id IN (SELECT DISTINCT dxcc FROM contacts) " "ORDER BY 2 COLLATE LOCALEAWARE ASC;", tr("All Countries"), ui->countrySelectFilter)); ui->countrySelectFilter->setModelColumn(1); ui->countrySelectFilter->adjustMaxSize(); ui->countrySelectFilter->setHighlightWhenEnable(true); ui->countrySelectFilter->blockSignals(false); ui->clubSelectFilter->setHighlightWhenEnable(true); refreshClubFilter(); ui->userSelectFilter->blockSignals(true); ui->userSelectFilter->setModel(QSOFilterManager::QSOFilterModel(tr("No User Filter"), ui->userSelectFilter)); ui->userSelectFilter->adjustMaxSize(); ui->userSelectFilter->setHighlightWhenEnable(true); ui->userSelectFilter->blockSignals(false); clublog = new ClubLogUploader(this); restoreFilters(); } void LogbookWidget::filterSelectedCallsign() { FCT_IDENTIFICATION; const QModelIndexList &modeList = ui->contactTable->selectionModel()->selectedRows(); if ( modeList.count() > 0 ) { const QSqlRecord &record = model->record(modeList.first().row()); filterCallsign(record.value("callsign").toString()); } } void LogbookWidget::filterCountryBand(const QString &countryName, const QString &band, const QString &addlFilter) { FCT_IDENTIFICATION; ui->countrySelectFilter->blockSignals(true); ui->bandSelectFilter->blockSignals(true); ui->userSelectFilter->blockSignals(true); ui->modeSelectFilter->blockSignals(true); ui->clubSelectFilter->blockSignals(true); ui->countrySelectFilter->setCurrentText(countryName); ui->bandSelectFilter->setCurrentText(band); //user wants to see only selected band and country ui->userSelectFilter->setCurrentText(""); //suppress user-defined filter ui->modeSelectFilter->setCurrentText(""); //suppress mode filter ui->clubSelectFilter->setCurrentText(""); //suppress club filter // set additional filter externalFilter = addlFilter; ui->clubSelectFilter->blockSignals(false); ui->userSelectFilter->blockSignals(false); ui->modeSelectFilter->blockSignals(false); ui->countrySelectFilter->blockSignals(false); ui->bandSelectFilter->blockSignals(false); filterTable(); } void LogbookWidget::lookupSelectedCallsign() { FCT_IDENTIFICATION; const QModelIndexList &modeList = ui->contactTable->selectionModel()->selectedRows(); if ( modeList.count() > 0) { const QSqlRecord &record = model->record(modeList.first().row()); QDesktopServices::openUrl(GenericCallbook::getWebLookupURL(record.value("callsign").toString())); } } void LogbookWidget::actionCallbookLookup() { FCT_IDENTIFICATION; callbookLookupBatch = ui->contactTable->selectionModel()->selectedRows(); ui->contactTable->clearSelection(); if ( callbookLookupBatch.count() > 100 ) { callbookLookupBatch.clear(); QMessageBox::warning(this, tr("QLog Warning"), tr("Each batch supports up to 100 QSOs.")); return; } lookupDialog = new QProgressDialog(tr("QSOs Update Progress"), tr("Cancel"), 0, callbookLookupBatch.count(), this); connect(lookupDialog, &QProgressDialog::canceled, this, [this]() { qCDebug(runtime)<< "Operation canceled"; callbookManager.abortQuery(); finishQSOLookupBatch(); }); lookupDialog->setValue(0); lookupDialog->setWindowModality(Qt::WindowModal); lookupDialog->show(); queryNextQSOLookupBatch(); } void LogbookWidget::queryNextQSOLookupBatch() { FCT_IDENTIFICATION; if ( callbookLookupBatch.isEmpty() ) { finishQSOLookupBatch(); return; } currLookupIndex = callbookLookupBatch.takeFirst(); callbookManager.queryCallsign(model->data(model->index(currLookupIndex.row(), LogbookModel::COLUMN_CALL), Qt::DisplayRole).toString()); } void LogbookWidget::finishQSOLookupBatch() { FCT_IDENTIFICATION; callbookLookupBatch.clear(); currLookupIndex = QModelIndex(); if ( lookupDialog ) { lookupDialog->done(QDialog::Accepted); lookupDialog->deleteLater(); lookupDialog = nullptr; } } void LogbookWidget::updateQSORecordFromCallbook(const CallbookResponseData& data) { FCT_IDENTIFICATION; auto getCurrIndexColumnValue = [&](const LogbookModel::ColumnID id) { return model->data(model->index(currLookupIndex.row(), id), Qt::EditRole).toString(); }; auto setModelData = [&](const LogbookModel::ColumnID id, const QVariant &value) { return model->setData(model->index(currLookupIndex.row(), id), value, Qt::EditRole); }; if ( getCurrIndexColumnValue(LogbookModel::COLUMN_CALL) != data.call) { qWarning() << "Callsigns don't match - skipping. QSO " << model->data(model->index(currLookupIndex.row(), LogbookModel::COLUMN_CALL), Qt::DisplayRole).toString() << "data " << data.call; return; } const QString fnamelname = QString("%1 %2").arg(data.fname, data.lname); const QString &nameValue = getCurrIndexColumnValue(LogbookModel::COLUMN_NAME_INTL); const LogbookModel::EditStrategy originEditStrategy = model->editStrategy(); model->setEditStrategy(QSqlTableModel::OnManualSubmit); if ( nameValue.isEmpty() || data.name_fmt.contains(nameValue) || fnamelname.contains(nameValue) || data.nick.contains(nameValue) ) { QString name = data.name_fmt; if ( name.isEmpty() ) name = ( data.fname.isEmpty() && data.lname.isEmpty() ) ? data.nick : fnamelname; setModelData(LogbookModel::COLUMN_NAME_INTL, name); } auto setIfEmpty = [&](const LogbookModel::ColumnID id, const QString &callbookValue, bool containsEnabled = false, bool forceReplace = false) { if ( callbookValue.isEmpty() ) return; const QString &columnValue = getCurrIndexColumnValue(id); if ( columnValue.isEmpty() || (forceReplace && callbookValue != columnValue) || (containsEnabled && callbookValue.contains(columnValue) && callbookValue != columnValue) ) { bool ret = setModelData(id, callbookValue); qCDebug(runtime) << "Changing" << LogbookModel::getFieldNameTranslation(id) << callbookValue << ret; } }; setIfEmpty(LogbookModel::COLUMN_GRID, data.gridsquare, true); setIfEmpty(LogbookModel::COLUMN_QTH_INTL, data.qth); setIfEmpty(LogbookModel::COLUMN_DARC_DOK, data.dok); setIfEmpty(LogbookModel::COLUMN_IOTA, data.iota); setIfEmpty(LogbookModel::COLUMN_EMAIL, data.email); setIfEmpty(LogbookModel::COLUMN_COUNTY, data.county); setIfEmpty(LogbookModel::COLUMN_QSL_VIA, data.qsl_via); setIfEmpty(LogbookModel::COLUMN_WEB, data.url); setIfEmpty(LogbookModel::COLUMN_STATE, data.us_state); setIfEmpty(LogbookModel::COLUMN_ITUZ, data.ituz, false, true); // always replace if different setIfEmpty(LogbookModel::COLUMN_CQZ, data.cqz, false, true); // always replace if different model->submitAll(); model->setEditStrategy(originEditStrategy); } void LogbookWidget::callsignFound(const CallbookResponseData &data) { FCT_IDENTIFICATION; updateQSORecordFromCallbook(data); currLookupIndex = QModelIndex(); if ( lookupDialog ) lookupDialog->setValue(lookupDialog->value() + 1); queryNextQSOLookupBatch(); } void LogbookWidget::callsignNotFound(const QString &call) { FCT_IDENTIFICATION; qCDebug(runtime) << call << "not found"; if ( lookupDialog ) lookupDialog->setValue(lookupDialog->value() + 1); queryNextQSOLookupBatch(); } void LogbookWidget::callbookLoginFailed(const QString&callbookString) { FCT_IDENTIFICATION; finishQSOLookupBatch(); QMessageBox::critical(this, tr("QLog Error"), callbookString + " " + tr("Callbook login failed")); } void LogbookWidget::callbookError(const QString &error) { FCT_IDENTIFICATION; finishQSOLookupBatch(); QMessageBox::critical(this, tr("QLog Error"), tr("Callbook error: ") + error); } void LogbookWidget::setCallsignSearch() { FCT_IDENTIFICATION; clearSearchText(); ui->searchTextFilter->setPlaceholderText(tr("Callsign")); if ( !callsignSearchValue.isEmpty() ) ui->searchTextFilter->setText(callsignSearchValue); } void LogbookWidget::setGridsquareSearch() { FCT_IDENTIFICATION; clearSearchText(); ui->searchTextFilter->setPlaceholderText(tr("Gridsquare")); } void LogbookWidget::setPotaSearch() { FCT_IDENTIFICATION; clearSearchText(); ui->searchTextFilter->setPlaceholderText(tr("POTA")); } void LogbookWidget::setSotaSearch() { FCT_IDENTIFICATION; clearSearchText(); ui->searchTextFilter->setPlaceholderText(tr("SOTA")); } void LogbookWidget::setWwffSearch() { FCT_IDENTIFICATION; clearSearchText(); ui->searchTextFilter->setPlaceholderText(tr("WWFF")); } void LogbookWidget::setSigSearch() { FCT_IDENTIFICATION; clearSearchText(); ui->searchTextFilter->setPlaceholderText(tr("SIG")); } void LogbookWidget::setIOTASearch() { FCT_IDENTIFICATION; clearSearchText(); ui->searchTextFilter->setPlaceholderText(tr("IOTA")); } void LogbookWidget::filterCallsign(const QString &call) { FCT_IDENTIFICATION; if ( call == callsignSearchValue ) return; callsignSearchValue = call; if ( ui->actionSearchCallsign->isChecked() ) { ui->searchTextFilter->blockSignals(true); ui->searchTextFilter->setText(call); ui->searchTextFilter->blockSignals(false); // clearbutton is shown only when signals are enabled. therefore // it is needed to force clear-button update ui->searchTextFilter->setClearButtonEnabled(false); ui->searchTextFilter->setClearButtonEnabled(true); } filterTable(); } void LogbookWidget::clearSearchText() { FCT_IDENTIFICATION; if ( ui->searchTextFilter->text().isEmpty() ) return; ui->searchTextFilter->clear(); } void LogbookWidget::setupSearchMenu() { FCT_IDENTIFICATION; QMenu *searchTypeMenu = new QMenu(ui->searchTypeButton); searchTypeGroup = new QActionGroup(this); searchTypeGroup->setExclusive(true); for ( auto it = searchTypeList.cbegin(); it != searchTypeList.cend(); ++it) { QAction *action = it.value().action; if ( action ) { action->setActionGroup(searchTypeGroup); searchTypeMenu->addAction(action); } } connect(searchTypeGroup, &QActionGroup::triggered, this, [this](QAction *action) { saveSearchTextFilter(action); }); ui->searchTypeButton->setMenu(searchTypeMenu); } void LogbookWidget::onSearchTextChanged() { FCT_IDENTIFICATION; filterTable(); } void LogbookWidget::bandFilterChanged() { FCT_IDENTIFICATION; saveBandFilter(); filterTable();; } void LogbookWidget::saveBandFilter() { FCT_IDENTIFICATION; LogParam::setLogbookFilterBand(ui->bandSelectFilter->currentText()); } void LogbookWidget::restoreBandFilter() { FCT_IDENTIFICATION; ui->bandSelectFilter->blockSignals(true); ui->bandSelectFilter->setCurrentText(LogParam::getLogbookFilterBand()); ui->bandSelectFilter->blockSignals(false); } void LogbookWidget::modeFilterChanged() { FCT_IDENTIFICATION; saveModeFilter(); filterTable(); } void LogbookWidget::saveModeFilter() { FCT_IDENTIFICATION; LogParam::setLogbookFilterMode(ui->modeSelectFilter->currentText()); } void LogbookWidget::restoreModeFilter() { FCT_IDENTIFICATION; ui->modeSelectFilter->blockSignals(true); ui->modeSelectFilter->setCurrentText(LogParam::getLogbookFilterMode()); ui->modeSelectFilter->blockSignals(false); } void LogbookWidget::countryFilterChanged() { FCT_IDENTIFICATION; saveCountryFilter(); filterTable(); } void LogbookWidget::saveCountryFilter() { FCT_IDENTIFICATION; LogParam::setLogbookFilterCountry(ui->countrySelectFilter->currentText()); } void LogbookWidget::restoreCountryFilter() { FCT_IDENTIFICATION; ui->countrySelectFilter->blockSignals(true); ui->countrySelectFilter->setCurrentText(LogParam::getLogbookFilterCountry()); ui->countrySelectFilter->blockSignals(false); } void LogbookWidget::userFilterChanged() { FCT_IDENTIFICATION; saveUserFilter(); filterTable(); } void LogbookWidget::setUserFilter(const QString &filterName) { FCT_IDENTIFICATION; qCDebug(function_parameters) << filterName; ui->userSelectFilter->setCurrentText(filterName); } void LogbookWidget::saveUserFilter() { FCT_IDENTIFICATION; LogParam::setLogbookFilterUserFilter(ui->userSelectFilter->currentText()); } void LogbookWidget::restoreUserFilter() { FCT_IDENTIFICATION; ui->userSelectFilter->blockSignals(true); ui->userSelectFilter->setCurrentText(LogParam::getLogbookFilterUserFilter()); ui->userSelectFilter->blockSignals(false); } void LogbookWidget::clubFilterChanged() { FCT_IDENTIFICATION; saveClubFilter(); filterTable(); } void LogbookWidget::refreshClubFilter() { FCT_IDENTIFICATION; ui->clubSelectFilter->blockSignals(true); const QString &member = ui->clubSelectFilter->currentText(); ui->clubSelectFilter->setModel(new QStringListModel(QStringList(tr("All Clubs")) << MembershipQE::instance()->getEnabledClubLists(),ui->clubSelectFilter)); ui->clubSelectFilter->adjustMaxSize(); ui->clubSelectFilter->setCurrentText(member); ui->clubSelectFilter->blockSignals(false); } void LogbookWidget::refreshUserFilter() { FCT_IDENTIFICATION; ui->userSelectFilter->refreshModel(); filterTable(); // TODO ??? is it needed } void LogbookWidget::saveClubFilter() { FCT_IDENTIFICATION; LogParam::setLogbookFilterClub(ui->clubSelectFilter->currentText()); } void LogbookWidget::restoreClubFilter() { FCT_IDENTIFICATION; ui->clubSelectFilter->blockSignals(true); ui->clubSelectFilter->setCurrentText(LogParam::getLogbookFilterClub()); ui->clubSelectFilter->blockSignals(false); } void LogbookWidget::saveSearchTextFilter(QAction *action) { FCT_IDENTIFICATION; LogParam::setLogbookFilterSearchType(action->data().toInt()); } void LogbookWidget::restoreSearchTextFilter() { FCT_IDENTIFICATION; int searchType = LogParam::getLogbookFilterSearchType(SearchType::CALLSIGN_SEARCH); const SearchDefinition &def = searchTypeList.value(static_cast(searchType)); if ( def.action ) { searchTypeGroup->blockSignals(true); def.action->setChecked(true); def.action->trigger(); searchTypeGroup->blockSignals(false); } } void LogbookWidget::restoreFilters() { FCT_IDENTIFICATION; restoreSearchTextFilter(); restoreModeFilter(); restoreBandFilter(); restoreCountryFilter(); restoreClubFilter(); restoreUserFilter(); externalFilter = QString(); clearSearchText(); filterTable(); } void LogbookWidget::uploadClublog() { FCT_IDENTIFICATION; QByteArray data; QTextStream stream(&data, QIODevice::ReadWrite); AdiFormat adi(stream); foreach (QModelIndex index, ui->contactTable->selectionModel()->selectedRows()) { QSqlRecord record = model->record(index.row()); adi.exportContact(record); } stream.flush(); //clublog->uploadAdif(data); } void LogbookWidget::deleteContact() { FCT_IDENTIFICATION; QMessageBox::StandardButton reply; reply = QMessageBox::question(this, tr("Delete"), tr("Delete the selected contacts?"), QMessageBox::Yes|QMessageBox::No); if ( reply != QMessageBox::Yes ) return; QModelIndexList deletedRowIndexes = ui->contactTable->selectionModel()->selectedRows(); // Since deletedRowIndexes contains indexes for columns that might be invisible, // and scrollToIndex needs an index with a visible column, // we obtain the column index from the first record in the table." int firstVisibleColumnIndex = ui->contactTable->indexAt(QPoint(0, 0)).column(); QModelIndex previousIndex = model->index(deletedRowIndexes.first().row()-1, firstVisibleColumnIndex); // Clublog does not accept batch DELETE operation // ask if an operator wants to continue if ( ClubLogBase::isUploadImmediatelyEnabled() && deletedRowIndexes.count() > 5 ) { reply = QMessageBox::question(this, tr("Delete"), tr("Clublog's Immediately Send supports only one-by-one deletion

" "Do you want to continue despite the fact
" "that the DELETE operation will not be sent to Clublog?"), QMessageBox::Yes|QMessageBox::No); if ( reply != QMessageBox::Yes ) return; else blockClublogSignals = true; } //It must be sorted in descending order to delete the correct rows. std::sort(deletedRowIndexes.begin(), deletedRowIndexes.end(), [](const QModelIndex &a, const QModelIndex &b) { return a.row() > b.row(); }); QProgressDialog *progress = new QProgressDialog(tr("Deleting QSOs"), tr("Cancel"), 0, deletedRowIndexes.size(), this); progress->setWindowModality(Qt::WindowModal); progress->setValue(0); progress->setAttribute(Qt::WA_DeleteOnClose, true); progress->setAutoClose(true); progress->show(); // disable Updates and current connection between model and QTableView // to improve performance // when reconnected model, the column are reordered. That's why we remember them // and restore them again after deletion. QByteArray state = ui->contactTable->horizontalHeader()->saveState(); ui->contactTable->setUpdatesEnabled(false); ui->contactTable->setModel(nullptr); QCoreApplication::processEvents(); quint32 cnt = 0; QSet removedEntities; removedEntities.reserve(deletedRowIndexes.size()); QSqlDatabase db = QSqlDatabase::database(); db.transaction(); for ( const QModelIndex &index : static_cast(deletedRowIndexes) ) { cnt++; removedEntities << model->data(model->index(index.row(), LogbookModel::COLUMN_DXCC), Qt::DisplayRole).toUInt(); model->removeRow(index.row()); if ( progress->wasCanceled() ) break; if ( cnt % 50 == 0 ) progress->setValue(cnt); } db.commit(); progress->setValue(deletedRowIndexes.size()); progress->done(QDialog::Accepted); // enable connection between model and QTableView ui->contactTable->setModel(model); ui->contactTable->setUpdatesEnabled(true); ui->contactTable->horizontalHeader()->restoreState(state); updateTable(); scrollToIndex(previousIndex); blockClublogSignals = false; emit deletedEntities(removedEntities); } void LogbookWidget::exportContact() { FCT_IDENTIFICATION; QListQSOs; const QModelIndexList &selectedIndexes = ui->contactTable->selectionModel()->selectedRows(); if ( selectedIndexes.count() < 1 ) return; for ( const QModelIndex &index : selectedIndexes ) QSOs << model->record(index.row()); ExportDialog dialog(QSOs); dialog.exec(); } void LogbookWidget::editContact() { FCT_IDENTIFICATION; if ( ui->contactTable->selectionModel()->selectedRows().size() > 1 ) { QMessageBox::StandardButton reply; reply = QMessageBox::question(this, tr("Update"), tr("By updating, all selected rows will be affected.
The value currently edited in the column will be applied to all selected rows.

Do you want to edit them?"), QMessageBox::Yes|QMessageBox::No); if (reply != QMessageBox::Yes) return; } ui->contactTable->edit(ui->contactTable->selectionModel()->currentIndex()); } void LogbookWidget::displayedColumns() { FCT_IDENTIFICATION; ColumnSettingDialog dialog(ui->contactTable); dialog.exec(); saveTableHeaderState(); } void LogbookWidget::reselectModel() { FCT_IDENTIFICATION; model->select(); // under normal conditions, only 256 records are loaded. // This will increase the value of the loaded records. while ( model->canFetchMore() && model->rowCount() < 5000 ) model->fetchMore(); // it is not possible to use mode->rowCount here because model contains only // the first 5000 records (or more) and rowCount has a value 5000 here. Therefore, it is needed // to run a QSL stateme with Count. Run it only in case when QTableview does not contain all // records from model int qsoCount = 0; if ( model->canFetchMore() ) { QString countRecordsStmt(QLatin1String("SELECT COUNT(1) FROM contacts")); if ( !model->filter().isEmpty() ) countRecordsStmt.append(QString(" WHERE %1").arg(model->filter())); QSqlQuery query(countRecordsStmt); qsoCount = query.first() ? query.value(0).toInt() : 0; } else qsoCount = model->rowCount(); ui->filteredQSOsLabel->setText(tr("Count: %n", "", qsoCount)); } void LogbookWidget::updateTable() { FCT_IDENTIFICATION; reselectModel(); // it is called when QSO is inserted/updated/deleted // therefore it is needed to refresh country select box ui->countrySelectFilter->refreshModel(); emit logbookUpdated(); } void LogbookWidget::saveTableHeaderState() { FCT_IDENTIFICATION; LogParam::setLogbookState(ui->contactTable->horizontalHeader()->saveState()); } void LogbookWidget::showTableHeaderContextMenu(const QPoint& point) { FCT_IDENTIFICATION; QMenu* contextMenu = new QMenu(this); for (int i = 0; i < model->columnCount(); i++) { const QString &name = model->headerData(i, Qt::Horizontal).toString(); QAction* action = new QAction(name, contextMenu); action->setCheckable(true); action->setChecked(!ui->contactTable->isColumnHidden(i)); connect(action, &QAction::triggered, this, [this, i]() { ui->contactTable->setColumnHidden(i, !ui->contactTable->isColumnHidden(i)); saveTableHeaderState(); }); contextMenu->addAction(action); } contextMenu->exec(point); } void LogbookWidget::updateSelectedRows(std::function updater) { FCT_IDENTIFICATION; const QModelIndexList selectedRows = ui->contactTable->selectionModel()->selectedRows(); if (selectedRows.isEmpty()) return; const LogbookModel::EditStrategy originEditStrategy = model->editStrategy(); model->setEditStrategy(QSqlTableModel::OnManualSubmit); for ( const QModelIndex &index : selectedRows ) updater(index.row()); model->submitAll(); model->setEditStrategy(originEditStrategy); updateTable(); } void LogbookWidget::markQslReceived() { FCT_IDENTIFICATION; updateSelectedRows([this](int row) { model->setData(model->index(row, LogbookModel::COLUMN_QSL_RCVD), "Y", Qt::EditRole); model->setData(model->index(row, LogbookModel::COLUMN_QSL_RCVD_DATE), QDate::currentDate(), Qt::EditRole); }); } void LogbookWidget::markQslRequested() { FCT_IDENTIFICATION; updateSelectedRows([this](int row) { model->setData(model->index(row, LogbookModel::COLUMN_QSL_SENT), "R", Qt::EditRole); }); } void LogbookWidget::doubleClickColumn(QModelIndex modelIndex) { FCT_IDENTIFICATION; /***********************/ /* show EQSL QSL Image */ /***********************/ if ( modelIndex.column() == LogbookModel::COLUMN_EQSL_QSL_RCVD && modelIndex.data().toString() == 'Y') { QProgressDialog* dialog = new QProgressDialog(tr("Downloading eQSL Image"), tr("Cancel"), 0, 0, this); dialog->setWindowModality(Qt::WindowModal); dialog->setRange(0, 0); dialog->setAutoClose(true); dialog->show(); EQSLQSLDownloader *eQSL = new EQSLQSLDownloader(dialog); connect(eQSL, &EQSLQSLDownloader::QSLImageFound, this, [dialog, eQSL](QString imgFile) { dialog->done(0); QDesktopServices::openUrl(QUrl::fromLocalFile(imgFile)); eQSL->deleteLater(); }); connect(eQSL, &EQSLQSLDownloader::QSLImageError, this, [this, dialog, eQSL](const QString &error) { dialog->done(1); QMessageBox::critical(this, tr("QLog Error"), tr("eQSL Download Image failed: ") + error); eQSL->deleteLater(); }); connect(dialog, &QProgressDialog::canceled, this, [eQSL]() { qCDebug(runtime)<< "Operation canceled"; eQSL->abortDownload(); eQSL->deleteLater(); }); eQSL->getQSLImage(model->record(modelIndex.row())); } /**************************/ /* show Paper QSL Manager */ /**************************/ else if ( modelIndex.column() == LogbookModel::COLUMN_QSL_RCVD && modelIndex.data().toString() == 'Y' ) { PaperQSLDialog dialog(model->record(modelIndex.row())); dialog.exec(); } /**************************************/ /* show generic QSO Show/Edit Dialog */ /**************************************/ else { QSODetailDialog dialog(model->record(modelIndex.row())); connect(&dialog, &QSODetailDialog::contactUpdated, this, [this](QSqlRecord& record) { emit contactUpdated(record); emit clublogContactUpdated(record); }); dialog.exec(); updateTable(); scrollToIndex(modelIndex); } } void LogbookWidget::handleBeforeUpdate(int row, QSqlRecord &record) { FCT_IDENTIFICATION; Q_UNUSED(row); emit contactUpdated(record); } void LogbookWidget::handleBeforeDelete(int row) { FCT_IDENTIFICATION; const QSqlRecord &oldRecord = model->record(row); emit contactDeleted(oldRecord); if ( !blockClublogSignals ) emit clublogContactDeleted(oldRecord); } void LogbookWidget::focusSearchCallsign() { FCT_IDENTIFICATION; ui->searchTextFilter->setFocus(); } void LogbookWidget::reloadSetting() { FCT_IDENTIFICATION; /* Refresh dynamic Club selection combobox */ refreshClubFilter(); callbookManager.initCallbooks(); updateTable(); } void LogbookWidget::sendDXCSpot() { FCT_IDENTIFICATION; const QModelIndexList &selectedIndexes = ui->contactTable->selectionModel()->selectedRows(); if ( selectedIndexes.count() < 1 ) return; emit sendDXSpotContactReq(model->record(selectedIndexes.at(0).row())); } void LogbookWidget::setDefaultSort() { FCT_IDENTIFICATION; ui->contactTable->sortByColumn(LogbookModel::COLUMN_TIME_ON, Qt::DescendingOrder); } void LogbookWidget::scrollToIndex(const QModelIndex &index, bool selectItem) { FCT_IDENTIFICATION; if ( index == QModelIndex() ) return; // is index visible ? if ( ui->contactTable->visualRect(index).isEmpty() ) { while ( model->canFetchMore() && ui->contactTable->visualRect(index).isEmpty() ) model->fetchMore(); if ( model->canFetchMore() ) model->fetchMore(); // one more fetch } ui->contactTable->scrollTo(index, QAbstractItemView::PositionAtCenter); if ( selectItem ) ui->contactTable->selectRow(index.row()); } void LogbookWidget::adjusteComboMinSize(QComboBox *combo) { FCT_IDENTIFICATION; if (combo->count() <= 0 ) return; QFontMetrics fontMetrics(combo->font()); combo->setMinimumWidth(fontMetrics.horizontalAdvance(combo->itemText(0)) + 35); } bool LogbookWidget::eventFilter(QObject *obj, QEvent *event) { //FCT_IDENTIFICATION; if ( event->type() == QEvent::KeyPress && obj == ui->contactTable ) { QKeyEvent *keyEvent = static_cast(event); // Block SelectAll if ( QKeySequence(keyEvent->modifiers() | keyEvent->key()) == QKeySequence::SelectAll ) return true; } return QObject::eventFilter(obj, event); } void LogbookWidget::colorsFilterWidget(QComboBox *widget) { FCT_IDENTIFICATION; widget->setStyleSheet( (widget->currentIndex() > 0) ? "QComboBox {border: 2px solid red; border-radius: 4px; padding: 2px;}" : ""); } void LogbookWidget::filterTable() { FCT_IDENTIFICATION; QStringList filterString; QString searchText = ui->searchTextFilter->text(); // an external request from Callsign search is always used (the request is sent by the NewContact Widget) if ( !ui->actionSearchCallsign->isChecked() && !callsignSearchValue.isEmpty() ) filterString.append(QString("callsign LIKE '%%1%'").arg(callsignSearchValue.toUpper())); for ( auto it = searchTypeList.cbegin(); it != searchTypeList.cend(); it++) { const SearchDefinition &def = it.value(); if ( !def.action ) continue; if ( def.action->isChecked() && !searchText.isEmpty() ) filterString.append(QString("%1 LIKE '%%2%'").arg(def.dbColumn, searchText.toUpper())); } const QString &bandFilterValue = ui->bandSelectFilter->currentText(); if ( ui->bandSelectFilter->currentIndex() != 0 && !bandFilterValue.isEmpty()) filterString.append(QString("band = '%1'").arg(bandFilterValue)); const QString &modeFilterValue = ui->modeSelectFilter->currentText(); if ( ui->modeSelectFilter->currentIndex() != 0 && !modeFilterValue.isEmpty() ) filterString.append(QString("mode = '%1'").arg(modeFilterValue)); bool OK = false; int countryCode = ui->countrySelectFilter->currentValue(1).toInt(&OK); if ( OK && countryCode > 0 ) filterString.append(QString("dxcc = '%1'").arg(countryCode)); if ( ui->clubSelectFilter->currentIndex() != 0 ) filterString.append(QString("id in (SELECT contactid FROM contact_clubs_view WHERE clubid = '%1')").arg(ui->clubSelectFilter->currentText())); if ( ui->userSelectFilter->currentIndex() != 0 ) filterString.append(QSOFilterManager::instance()->getWhereClause(ui->userSelectFilter->currentText())); if ( !externalFilter.isEmpty() ) filterString.append(QString("( ") + externalFilter + ")"); model->setFilter(filterString.join(" AND ")); qCDebug(runtime) << model->query().lastQuery(); reselectModel(); } LogbookWidget::~LogbookWidget() { FCT_IDENTIFICATION; if ( lookupDialog ) { callbookManager.abortQuery(); finishQSOLookupBatch(); } delete ui; } void LogbookWidget::finalizeBeforeAppExit() { FCT_IDENTIFICATION; saveTableHeaderState(); } ================================================ FILE: ui/LogbookWidget.h ================================================ #ifndef QLOG_UI_LOOKBOOKWIDGET_H #define QLOG_UI_LOOKBOOKWIDGET_H #include #include #include #include #include #include "core/CallbookManager.h" #include "component/ShutdownAwareWidget.h" namespace Ui { class LogbookWidget; } class ClubLogUploader; class LogbookModel; class QProgressDialog; class LogbookWidget : public QWidget, public ShutdownAwareWidget { Q_OBJECT public: explicit LogbookWidget(QWidget *parent = nullptr); ~LogbookWidget(); virtual void finalizeBeforeAppExit(); enum SearchType { UNKNOWN_SEARCH = 0, CALLSIGN_SEARCH = 1, GRIDSQUARE_SEARCH = 2, POTA_SEARCH = 3, SOTA_SEARCH = 4, WWFF_SEARCH = 5, IOTA_SEARCH = 6, SIG_SEARCH = 7 }; signals: void logbookUpdated(); void contactUpdated(QSqlRecord&); void contactDeleted(const QSqlRecord&); void deletedEntities(const QSet &entities); void sendDXSpotContactReq(const QSqlRecord&); // Clublog special signals // unfortunately, special rules are applied for uploading to Clublog. // The Clublog's RT Interface only accepts low-rate QSO uploading. // Therefore, it is necessary to send only selected QSO manipulations. // That is why these 2 special signals are emitted. // contactUpdated, contactDeleted signals are also emitted in these cases void clublogContactUpdated(QSqlRecord&); void clublogContactDeleted(const QSqlRecord&); public slots: void filterCallsign(const QString &call); void filterSelectedCallsign(); void filterCountryBand(const QString&, const QString&, const QString&); void lookupSelectedCallsign(); void onSearchTextChanged(); void bandFilterChanged(); void modeFilterChanged(); void countryFilterChanged(); void userFilterChanged(); void setUserFilter(const QString &filterName); void clubFilterChanged(); void refreshClubFilter(); void refreshUserFilter(); void restoreFilters(); void updateTable(); void uploadClublog(); void deleteContact(); void exportContact(); void editContact(); void displayedColumns(); void saveTableHeaderState(); void showTableHeaderContextMenu(const QPoint& point); void markQslReceived(); void markQslRequested(); void doubleClickColumn(QModelIndex); void handleBeforeUpdate(int, QSqlRecord&); void handleBeforeDelete(int); void focusSearchCallsign(); void reloadSetting(); void sendDXCSpot(); void setDefaultSort(); void actionCallbookLookup(); void callsignFound(const CallbookResponseData &data); void callsignNotFound(const QString&); void callbookLoginFailed(const QString&); void callbookError(const QString&); void setCallsignSearch(); void setGridsquareSearch(); void setPotaSearch(); void setSotaSearch(); void setWwffSearch(); void setSigSearch(); void setIOTASearch(); private: ClubLogUploader* clublog; LogbookModel* model; Ui::LogbookWidget *ui; QString externalFilter; bool blockClublogSignals; bool eventFilter(QObject *obj, QEvent *event); void colorsFilterWidget(QComboBox *widget); void filterTable(); void saveBandFilter(); void restoreBandFilter(); void saveModeFilter(); void restoreModeFilter(); void saveCountryFilter(); void restoreCountryFilter(); void saveUserFilter(); void restoreUserFilter(); void saveClubFilter(); void restoreClubFilter(); void saveSearchTextFilter(QAction *action); void restoreSearchTextFilter(); void reselectModel(); void scrollToIndex(const QModelIndex& index, bool selectItem = true); void adjusteComboMinSize(QComboBox * combo); void updateQSORecordFromCallbook(const CallbookResponseData &data); void queryNextQSOLookupBatch(); void finishQSOLookupBatch(); void clearSearchText(); void setupSearchMenu(); void updateSelectedRows(std::function updater); QModelIndexList callbookLookupBatch; QModelIndex currLookupIndex; CallbookManager callbookManager; QProgressDialog *lookupDialog; QString callsignSearchValue; QActionGroup *searchTypeGroup; class SearchDefinition { public: SearchDefinition(const SearchType searchType, QAction *action, const QString dbColumn) : searchType(searchType), action(action), dbColumn(dbColumn) {action->setData(searchType);}; SearchDefinition(): searchType(UNKNOWN_SEARCH), action(nullptr) {}; SearchType searchType; QAction *action; QString dbColumn; }; QMap searchTypeList; }; /* https://forum.qt.io/topic/90403/show-tooltip-immediatly/7/ */ class ProxyStyle : public QProxyStyle { public: using QProxyStyle::QProxyStyle; int styleHint(StyleHint hint, const QStyleOption* option = nullptr, const QWidget* widget = nullptr, QStyleHintReturn* returnData = nullptr) const override { if (hint == QStyle::SH_ToolTip_WakeUpDelay) return 0; // show tooltip immediately return QProxyStyle::styleHint(hint, option, widget, returnData); } }; #endif // QLOG_UI_LOGBOOKWIDGET_H ================================================ FILE: ui/LogbookWidget.ui ================================================ LogbookWidget 0 0 809 337 0 0 Form 0 0 0 0 0 0 0 0 24 16777215 Qt::ClickFocus Qt::NoContextMenu ::menu-indicator{ image: none; width: 0px;} :/icons/baseline-search-24px.svg:/icons/baseline-search-24px.svg 0 0 140 16777215 Qt::ClickFocus 20 true 0 0 Qt::ClickFocus Qt::ClickFocus 0 0 Qt::ClickFocus 0 0 Qt::ClickFocus 0 0 Qt::ClickFocus Qt::Horizontal 40 20 Qt::ClickFocus Qt::ActionsContextMenu QAbstractItemView::EditKeyPressed false true QAbstractItemView::SelectRows QAbstractItemView::ScrollPerPixel QAbstractItemView::ScrollPerPixel false false 25 25 .. Delete Logbook - Delete QSO Del true :/icons/cloud_upload-24px.svg:/icons/cloud_upload-24px.svg Upload to Clublog :/icons/baseline-search-24px.svg:/icons/baseline-search-24px.svg Lookup on Web Lookup on Web :/icons/cloud_download-24px.svg:/icons/cloud_download-24px.svg Update from Callbook Add Missing Info Mark QSL RCVD Mark QSL Requested :/icons/filter_list-24px.svg:/icons/filter_list-24px.svg Filter Callsign Edit Value Logbook - Edit Value Column Visibility Which columns should be displayed .. Export Selected Export selected QSOs Send DX Spot Logbook - Send DX Spot true Callsign true Gridsquare true POTA true SOTA true WWFF true SIG true IOTA QTableQSOView QTableView
ui/QTableQSOView.h
SmartSearchBox QWidget
ui/component/SmartSearchBox.h
1
actionDeleteContact triggered() LogbookWidget deleteContact() -1 -1 247 168 searchTextFilter textChanged(QString) LogbookWidget onSearchTextChanged() 129 16 404 168 actionUploadClublog triggered() LogbookWidget uploadClublog() -1 -1 404 168 actionFilter triggered() LogbookWidget filterSelectedCallsign() -1 -1 404 168 actionLookup triggered() LogbookWidget lookupSelectedCallsign() -1 -1 404 168 actionCallbookLookup triggered() LogbookWidget actionCallbookLookup() -1 -1 404 168 actionEditContact triggered() LogbookWidget editContact() -1 -1 404 168 actionDisplayedColumns triggered() LogbookWidget displayedColumns() -1 -1 404 168 contactTable doubleClicked(QModelIndex) LogbookWidget doubleClickColumn(QModelIndex) 404 189 404 168 actionExportAs triggered() LogbookWidget exportContact() -1 -1 404 168 actionSendDXCSpot triggered() LogbookWidget sendDXCSpot() -1 -1 404 168 actionSearchCallsign triggered() LogbookWidget setCallsignSearch() -1 -1 404 168 actionSearchGrid triggered() LogbookWidget setGridsquareSearch() -1 -1 404 168 actionSearchPOTA triggered() LogbookWidget setPotaSearch() -1 -1 404 168 actionSearchSOTA triggered() LogbookWidget setSotaSearch() -1 -1 404 168 actionSearchWWFF triggered() LogbookWidget setWwffSearch() -1 -1 404 168 actionSearchSIG triggered() LogbookWidget setSigSearch() -1 -1 404 168 actionSearchIOTA triggered() LogbookWidget setIOTASearch() -1 -1 404 168 deleteContact() onSearchTextChanged() bandFilterChanged() uploadClublog() filterSelectedCallsign() lookupSelectedCallsign() actionCallbookLookup() actionQSLRcvd() actionQSLRequested() modeFilterChanged() countryFilterChanged() editContact() displayedColumns() userFilterChanged() doubleClickColumn(QModelIndex) exportContact() clubFilterChanged() sendDXCSpot() setCallsignSearch() setGridsquareSearch() setPotaSearch() setSotaSearch() setWwffSearch() setSigSearch() setIOTASearch()
================================================ FILE: ui/MainWindow.cpp ================================================ #include #include #include #include #include #include #include #include "MainWindow.h" #include "ui_MainWindow.h" #include "ui/SettingsDialog.h" #include "ui/ImportDialog.h" #include "ui/ExportDialog.h" #include "ui/DevToolsDialog.h" #include "ui/CabrilloExportDialog.h" #include "core/FldigiTCPServer.h" #include "rig/Rig.h" #include "rotator/Rotator.h" #include "cwkey/CWKeyer.h" #include "core/WsjtxUDPReceiver.h" #include "core/debug.h" #include "ui/NewContactWidget.h" #include "ui/QSOFilterDialog.h" #include "ui/AwardsDialog.h" #include "ui/DXCCSubmissionDialog.h" #include "core/PropConditions.h" #include "data/MainLayoutProfile.h" #include "ui/EditActivitiesDialog.h" #include "ui/ProfileImageWidget.h" #include "core/LogParam.h" #include "core/QSOFilterManager.h" #include "data/Data.h" #include "data/ActivityProfile.h" #include "data/AntProfile.h" #include "data/RigProfile.h" #include "data/RotProfile.h" #include "ui/DownloadQSLDialog.h" #include "ui/UploadQSODialog.h" #include "core/LogParam.h" #include "core/PotaQE.h" #include "data/WsjtxEntry.h" #include "core/LogDatabase.h" #include "core/CredentialStore.h" #include "core/PlatformParameterManager.h" #include "core/FileCompressor.h" #include "ui/ExportPasswordDialog.h" #include "ui/LoadDatabaseDialog.h" #include "ui/PlatformSettingsDialog.h" #include "ui/QSLGalleryDialog.h" #include "ui/QSLPrintLabelDialog.h" #include #include #include #include MODULE_IDENTIFICATION("qlog.ui.mainwindow"); MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWindow), stats(new StatisticsWidget), clublogRT(new ClubLogUploader(this)) { FCT_IDENTIFICATION; ui->setupUi(this); restoreContestMenuSeqnoType(); restoreContestMenuDupeType(); restoreContestMenuLinkExchange(); PotaQE::instance(); themeButton = new QPushButton(this); themeButton->setToolTip(tr("Color Theme")); themeButton->setObjectName("themeButton"); themeButton->setFocusPolicy(Qt::ClickFocus); themeButton->setIcon(QIcon(QPixmap(":/icons/color-palette-dark.svg"))); themeButton->setFlat(true); QMenu *themeMenu = new QMenu(this); themeMenu->addAction(ui->actionThemeNative); themeMenu->addAction(ui->actionThemeLight); themeMenu->addAction(ui->actionThemeDark); QActionGroup *themeGroup = new QActionGroup(this); themeGroup->addAction(ui->actionThemeNative); themeGroup->addAction(ui->actionThemeLight); themeGroup->addAction(ui->actionThemeDark); themeButton->setMenu(themeMenu); /* Dark Mode is supported only in case of Fusion Style */ if ( QApplication::style()->objectName().compare("fusion", Qt::CaseSensitivity::CaseInsensitive) != 0) { isFusionStyle = false; themeButton->setEnabled(false); themeButton->setToolTip(tr("Not enabled for non-Fusion style")); } else { isFusionStyle = true; } /* the block below is present because the main window * becomes large after the instalation */ ui->wsjtxDockWidget->hide(); ui->rotatorDockWidget->hide(); ui->bandmapDockWidget->hide(); ui->mapDockWidget->hide(); ui->dxDockWidget->hide(); ui->rigDockWidget->hide(); ui->cwConsoleDockWidget->hide(); ui->chatDockWidget->hide(); ui->profileImageDockWidget->hide(); ui->alertDockWidget->hide(); ui->cwconsoleWidget->registerContactWidget(ui->newContactWidget); ui->rotatorWidget->registerContactWidget(ui->newContactWidget); ui->onlineMapWidget->registerContactWidget(ui->newContactWidget); ui->chatWidget->registerContactWidget(ui->newContactWidget); const QList dockWidgets = findChildren(); for (QDockWidget *dockWidget : dockWidgets) { if (!dockWidget) continue; if (dockWidget) dockWidget->setAttribute(Qt::WA_MacAlwaysShowToolWindow, true); if (auto tabs = dockWidget->findChild()) { if (tabs->tabBar()) tabs->tabBar()->show(); } } const StationProfile &profile = StationProfilesManager::instance()->getCurProfile1(); activityButton = new QPushButton("", ui->statusBar); activityButton->setFlat(true); activityButton->setFocusPolicy(Qt::NoFocus); activityButton->setObjectName("activityButton"); QMenu *activityMenu = new QMenu(activityButton); activityButton->setMenu(activityMenu); conditionsLabel = new QLabel("", ui->statusBar); conditionsLabel->setIndent(20); conditionsLabel->setToolTip(QString("").arg(PropConditions::solarSummaryFile())); profileLabel = new QLabel("" + profile.profileName + ":", ui->statusBar); profileLabel->setIndent(10); callsignLabel = new QLabel(stationCallsignStatus(profile), ui->statusBar); locatorLabel = new QLabel(profile.locator.toLower(), ui->statusBar); contestLabel = new QLabel(ui->statusBar); contestLabel->setIndent(20); alertButton = new QPushButton("0", ui->statusBar); alertButton->setIcon(QIcon(":/icons/alert.svg")); alertButton->setFlat(true); alertButton->setFocusPolicy(Qt::NoFocus); QMenu *menuAlert = new QMenu(this); menuAlert->addAction(ui->actionShowAlerts); menuAlert->addAction(ui->actionClearAlerts); menuAlert->addSeparator(); menuAlert->addAction(ui->actionEditAlertRules); menuAlert->addAction(ui->actionBeepSettingAlert); ui->actionBeepSettingAlert->setChecked(LogParam::getMainWindowAlertBeep()); alertButton->setMenu(menuAlert); alertTextButton = new QPushButton(" ", ui->statusBar); alertTextButton->setObjectName("alertTextButton"); alertTextButton->setFlat(true); alertTextButton->setFocusPolicy(Qt::NoFocus); alertTextButton->setToolTip(tr("Press to tune the alert")); ui->toolBar->hide(); ui->statusBar->addWidget(activityButton); ui->statusBar->addWidget(profileLabel); ui->statusBar->addWidget(callsignLabel); ui->statusBar->addWidget(locatorLabel); ui->statusBar->addWidget(contestLabel); ui->statusBar->addWidget(conditionsLabel); ui->statusBar->addPermanentWidget(alertTextButton); ui->statusBar->addPermanentWidget(alertButton); ui->statusBar->addPermanentWidget(themeButton); setContestMode(LogParam::getContestID()); connect(seqGroup, &QActionGroup::triggered, this, &MainWindow::saveContestMenuSeqnoType); connect(dupeGroup, &QActionGroup::triggered, this, &MainWindow::saveContestMenuDupeType); connect(linkExchangeGroup, &QActionGroup::triggered, this, &MainWindow::saveContestMenuLinkExchangeType); connect(ActivityProfilesManager::instance(), &ActivityProfilesManager::changeFinished, this, &MainWindow::handleActivityChange); connect(ActivityProfilesManager::instance(), &ActivityProfilesManager::changeFinished, ui->newContactWidget, &NewContactWidget::setValuesFromActivity); connect(AntProfilesManager::instance(), &AntProfilesManager::profileChanged, ui->newContactWidget, &NewContactWidget::refreshAntProfileCombo); connect(AntProfilesManager::instance(), &AntProfilesManager::profileChanged, ui->onlineMapWidget, &OnlineMapWidget::flyToMyQTH); connect(RotProfilesManager::instance(), &RotProfilesManager::profileChanged, ui->rotatorWidget, &RotatorWidget::refreshRotProfileCombo); connect(RigProfilesManager::instance(), &RigProfilesManager::profileChanged, ui->newContactWidget, &NewContactWidget::refreshRigProfileCombo); connect(RigProfilesManager::instance(), &RigProfilesManager::profileChanged, ui->rigWidget, &RigWidget::refreshRigProfileCombo); connect(MainLayoutProfilesManager::instance(), &MainLayoutProfilesManager::profileChanged, ui->newContactWidget, &NewContactWidget::setupCustomUi); connect(StationProfilesManager::instance(), &StationProfilesManager::profileChanged, this, &MainWindow::stationProfileChanged); connect(StationProfilesManager::instance(), &StationProfilesManager::profileChanged, ui->newContactWidget, &NewContactWidget::refreshStationProfileCombo); connect(StationProfilesManager::instance(), &StationProfilesManager::profileChanged, ui->rotatorWidget, &RotatorWidget::redrawMap); connect(StationProfilesManager::instance(), &StationProfilesManager::profileChanged, ui->onlineMapWidget, &OnlineMapWidget::flyToMyQTH); connect(StationProfilesManager::instance(), &StationProfilesManager::profileChanged, ui->chatWidget, &ChatWidget::reloadStationProfile); connect(StationProfilesManager::instance(), &StationProfilesManager::profileChanged, ui->clockWidget, &ClockWidget::updateSun); connect(StationProfilesManager::instance(), &StationProfilesManager::profileChanged, ui->dxWidget, &DxWidget::recalculateTrend); connect(this, &MainWindow::themeChanged, ui->bandmapWidget, &BandmapWidget::update); connect(this, &MainWindow::themeChanged, ui->clockWidget, &ClockWidget::updateClock); connect(this, &MainWindow::themeChanged, ui->onlineMapWidget, &OnlineMapWidget::changeTheme); connect(this, &MainWindow::themeChanged, ui->rotatorWidget, &RotatorWidget::redrawMap); connect(this, &MainWindow::themeChanged, stats, &StatisticsWidget::changeTheme); connect(ui->actionThemeNative, &QAction::triggered, this, [this]() { this->themeInit(0); }); connect(ui->actionThemeDark, &QAction::triggered, this, [this]() { this->themeInit(1); }); connect(ui->actionThemeLight, &QAction::triggered, this, [this]() { this->themeInit(2); }); connect(Rig::instance(), &Rig::rigErrorPresent, this, &MainWindow::rigErrorHandler); connect(Rig::instance(), &Rig::rigCWKeyOpenRequest, this, &MainWindow::cwKeyerConnectProfile); connect(Rig::instance(), &Rig::rigCWKeyCloseRequest, this, &MainWindow::cwKeyerDisconnectProfile); connect(Rig::instance(), &Rig::frequencyChanged, ui->onlineMapWidget, &OnlineMapWidget::setIBPBand); connect(Rig::instance(), &Rig::frequencyChanged, ui->bandmapWidget , &BandmapWidget::updateTunedFrequency); connect(Rig::instance(), &Rig::frequencyChanged, ui->newContactWidget, &NewContactWidget::changeFrequency); connect(Rig::instance(), &Rig::frequencyChanged, ui->rigWidget, &RigWidget::updateFrequency); connect(Rig::instance(), &Rig::frequencyChanged, ui->dxWidget , &DxWidget::setTunedFrequency); connect(Rig::instance(), &Rig::modeChanged, ui->bandmapWidget, &BandmapWidget::updateMode); connect(Rig::instance(), &Rig::modeChanged, ui->newContactWidget, &NewContactWidget::changeModefromRig); connect(Rig::instance(), &Rig::modeChanged, ui->rigWidget, &RigWidget::updateMode); connect(Rig::instance(), &Rig::powerChanged, ui->newContactWidget, &NewContactWidget::changePower); connect(Rig::instance(), &Rig::powerChanged, ui->rigWidget, &RigWidget::updatePWR); connect(Rig::instance(), &Rig::rigConnected, ui->newContactWidget, &NewContactWidget::rigConnected); connect(Rig::instance(), &Rig::rigConnected, ui->rigWidget, &RigWidget::rigConnected); connect(Rig::instance(), &Rig::rigConnected, ui->cwconsoleWidget, &CWConsoleWidget::rigConnectHandler); connect(Rig::instance(), &Rig::rigDisconnected, ui->cwconsoleWidget, &CWConsoleWidget::rigDisconnectHandler); connect(Rig::instance(), &Rig::rigDisconnected, ui->newContactWidget, &NewContactWidget::rigDisconnected); connect(Rig::instance(), &Rig::rigDisconnected, ui->rigWidget, &RigWidget::rigDisconnected); connect(Rig::instance(), &Rig::vfoChanged, ui->rigWidget, &RigWidget::updateVFO); connect(Rig::instance(), &Rig::xitChanged, ui->rigWidget, &RigWidget::updateXIT); connect(Rig::instance(), &Rig::ritChanged, ui->rigWidget, &RigWidget::updateRIT); connect(Rig::instance(), &Rig::pttChanged, ui->rigWidget, &RigWidget::updatePTT); connect(Rig::instance(), &Rig::splitChanged, ui->rigWidget, &RigWidget::updateSplit); connect(Rig::instance(), &Rig::splitChanged, ui->newContactWidget, &NewContactWidget::changeSplit); connect(Rig::instance(), &Rig::rigStatusChanged, &networknotification, &NetworkNotification::rigStatus); connect(Rig::instance(), &Rig::rigStatusHeartBeat, &networknotification, &NetworkNotification::rigStatus); connect(Rotator::instance(), &Rotator::rotErrorPresent, this, &MainWindow::rotErrorHandler); connect(Rotator::instance(), &Rotator::positionChanged, ui->onlineMapWidget, &OnlineMapWidget::antPositionChanged); connect(Rotator::instance(), &Rotator::rotConnected, ui->onlineMapWidget, &OnlineMapWidget::rotConnected); connect(Rotator::instance(), &Rotator::rotDisconnected, ui->onlineMapWidget, &OnlineMapWidget::rotDisconnected); connect(Rotator::instance(), &Rotator::positionChanged, ui->rotatorWidget, &RotatorWidget::positionChanged); connect(Rotator::instance(), &Rotator::rotConnected, ui->rotatorWidget, &RotatorWidget::rotConnected); connect(Rotator::instance(), &Rotator::rotDisconnected, ui->rotatorWidget, &RotatorWidget::rotDisconnected); connect(CWKeyer::instance(), &CWKeyer::cwKeyerError, this, &MainWindow::cwKeyerErrorHandler); connect(CWKeyer::instance(), &CWKeyer::cwKeyWPMChanged, ui->cwconsoleWidget, &CWConsoleWidget::setWPM); connect(CWKeyer::instance(), &CWKeyer::cwKeyEchoText, ui->cwconsoleWidget, &CWConsoleWidget::appendCWEchoText); connect(CWKeyer::instance(), &CWKeyer::cwKeyConnected, ui->cwconsoleWidget, &CWConsoleWidget::cwKeyConnected); connect(CWKeyer::instance(), &CWKeyer::cwKeyDisconnected, ui->cwconsoleWidget, &CWConsoleWidget::cwKeyDisconnected); connect(CWKeyer::instance(), &CWKeyer::cwKeyHWButton, ui->cwconsoleWidget, &CWConsoleWidget::pressMacroButton); connect(CWKeyer::instance(), &CWKeyer::cwKeyHWHaltPressed, ui->cwconsoleWidget, &CWConsoleWidget::haltButtonPressed); FldigiTCPServer* fldigi = new FldigiTCPServer(this); connect(fldigi, &FldigiTCPServer::addContact, ui->newContactWidget, &NewContactWidget::saveExternalContact); wsjtx = new WsjtxUDPReceiver(this); connect(wsjtx, &WsjtxUDPReceiver::statusReceived, ui->wsjtxWidget, &WsjtxWidget::statusReceived); connect(wsjtx, &WsjtxUDPReceiver::decodeReceived, ui->wsjtxWidget, &WsjtxWidget::decodeReceived); connect(wsjtx, &WsjtxUDPReceiver::addContact, ui->newContactWidget, &NewContactWidget::saveExternalContact); connect(ui->wsjtxWidget, &WsjtxWidget::CQSpot, &networknotification, &NetworkNotification::WSJTXCQSpot); connect(ui->wsjtxWidget, &WsjtxWidget::CQSpot, &alertEvaluator, &AlertEvaluator::WSJTXCQSpot); connect(ui->wsjtxWidget, &WsjtxWidget::filteredCQSpot, wsjtx, &WsjtxUDPReceiver::sendHighlightCallsign); connect(ui->wsjtxWidget, &WsjtxWidget::filteredCQSpot, ui->onlineMapWidget, &OnlineMapWidget::drawWSJTXSpot); connect(ui->wsjtxWidget, &WsjtxWidget::updatedCQSpot, wsjtx, &WsjtxUDPReceiver::sendClearHighlightCallsign); connect(ui->wsjtxWidget, &WsjtxWidget::spotsCleared, wsjtx, &WsjtxUDPReceiver::sendClearAllHighlightCallsign); connect(ui->wsjtxWidget, &WsjtxWidget::spotsCleared, ui->onlineMapWidget, &OnlineMapWidget::clearWSJTXSpots); connect(ui->wsjtxWidget, &WsjtxWidget::reply, wsjtx, &WsjtxUDPReceiver::sendReply); connect(ui->wsjtxWidget, &WsjtxWidget::frequencyChanged, ui->newContactWidget, &NewContactWidget::changeFrequency); connect(ui->wsjtxWidget, &WsjtxWidget::frequencyChanged, ui->onlineMapWidget, &OnlineMapWidget::setIBPBand); connect(ui->wsjtxWidget, &WsjtxWidget::frequencyChanged, ui->bandmapWidget , &BandmapWidget::updateTunedFrequency); connect(ui->wsjtxWidget, &WsjtxWidget::frequencyChanged, ui->dxWidget , &DxWidget::setTunedFrequency); connect(ui->wsjtxWidget, &WsjtxWidget::modeChanged, ui->newContactWidget, &NewContactWidget::changeModefromRig); connect(this, &MainWindow::settingsChanged, wsjtx, &WsjtxUDPReceiver::reloadSetting); connect(this, &MainWindow::settingsChanged, ui->rotatorWidget, &RotatorWidget::reloadSettings); connect(this, &MainWindow::settingsChanged, ui->rigWidget, &RigWidget::reloadSettings); connect(this, &MainWindow::settingsChanged, ui->cwconsoleWidget, &CWConsoleWidget::reloadSettings); connect(this, &MainWindow::settingsChanged, ui->rotatorWidget, &RotatorWidget::redrawMap); connect(this, &MainWindow::settingsChanged, ui->onlineMapWidget, &OnlineMapWidget::flyToMyQTH); connect(this, &MainWindow::settingsChanged, ui->logbookWidget, &LogbookWidget::reloadSetting); connect(this, &MainWindow::settingsChanged, ui->dxWidget, &DxWidget::reloadSetting); connect(this, &MainWindow::settingsChanged, ui->bandmapWidget, &BandmapWidget::recalculateDxccStatus); connect(this, &MainWindow::settingsChanged, ui->alertsWidget, &AlertWidget::recalculateDxccStatus); connect(this, &MainWindow::settingsChanged, ui->chatWidget, &ChatWidget::recalculateDxccStatus); connect(this, &MainWindow::settingsChanged, ui->newContactWidget, &NewContactWidget::readGlobalSettings); connect(this, &MainWindow::altBackslash, Rig::instance(), &Rig::setPTT); connect(this, &MainWindow::manualMode, ui->newContactWidget, &NewContactWidget::setManualMode); connect(this, &MainWindow::contestStopped, ui->newContactWidget, &NewContactWidget::stopContest); connect(this, &MainWindow::contestStopped, ui->bandmapWidget, &BandmapWidget::resetDupe); connect(this, &MainWindow::contestStopped, ui->alertsWidget, &AlertWidget::resetDupe); connect(this, &MainWindow::contestStopped, ui->chatWidget, &ChatWidget::resetDupe); connect(this, &MainWindow::dupeTypeChanged, ui->bandmapWidget, &BandmapWidget::recalculateDupe); connect(this, &MainWindow::dupeTypeChanged, ui->alertsWidget, &AlertWidget::recalculateDupe); connect(this, &MainWindow::dupeTypeChanged, ui->chatWidget, &ChatWidget::recalculateDupe); connect(this, &MainWindow::dupeTypeChanged, ui->newContactWidget, &NewContactWidget::refreshCallsignsColors); connect(ui->rigWidget, &RigWidget::rigProfileChanged, this, &MainWindow::rigConnect); connect(ui->rotatorWidget, &RotatorWidget::rotProfileChanged, this, &MainWindow::rotConnect); connect(ui->logbookWidget, &LogbookWidget::deletedEntities, Data::instance(), &Data::invalidateSetOfDXCCStatusCache); // must be the first delete signal connect(ui->logbookWidget, &LogbookWidget::logbookUpdated, stats, &StatisticsWidget::refreshWidget); connect(ui->logbookWidget, &LogbookWidget::contactUpdated, &networknotification, &NetworkNotification::QSOUpdated); connect(ui->logbookWidget, &LogbookWidget::clublogContactUpdated, clublogRT, &ClubLogUploader::updateQSOImmediately); connect(ui->logbookWidget, &LogbookWidget::contactDeleted, &networknotification, &NetworkNotification::QSODeleted); connect(ui->logbookWidget, &LogbookWidget::contactDeleted, ui->bandmapWidget, &BandmapWidget::updateSpotsDupeWhenQSODeleted); connect(ui->logbookWidget, &LogbookWidget::deletedEntities, ui->bandmapWidget, &BandmapWidget::updateSpotsDxccStatusWhenQSODeleted); connect(ui->logbookWidget, &LogbookWidget::contactDeleted, ui->alertsWidget, &AlertWidget::updateSpotsDupeWhenQSODeleted); connect(ui->logbookWidget, &LogbookWidget::deletedEntities, ui->alertsWidget, &AlertWidget::updateSpotsDxccStatusWhenQSODeleted); connect(ui->logbookWidget, &LogbookWidget::contactDeleted, ui->chatWidget, &ChatWidget::updateSpotsDupeWhenQSODeleted); connect(ui->logbookWidget, &LogbookWidget::deletedEntities, ui->chatWidget, &ChatWidget::updateSpotsDxccStatusWhenQSODeleted); connect(ui->logbookWidget, &LogbookWidget::deletedEntities, ui->newContactWidget, &NewContactWidget::refreshCallsignsColors); connect(ui->logbookWidget, &LogbookWidget::clublogContactDeleted, clublogRT, &ClubLogUploader::deleteQSOImmediately); connect(ui->logbookWidget, &LogbookWidget::sendDXSpotContactReq, ui->dxWidget, &DxWidget::prepareQSOSpot); connect(ui->newContactWidget, &NewContactWidget::contactAdded, Data::instance(), &Data::invalidateDXCCStatusCache); // must be the first delete signal connect(ui->newContactWidget, &NewContactWidget::contactAdded, ui->logbookWidget, &LogbookWidget::updateTable); connect(ui->newContactWidget, &NewContactWidget::contactAdded, ui->logbookWidget, &LogbookWidget::setDefaultSort); connect(ui->newContactWidget, &NewContactWidget::contactAdded, &networknotification, &NetworkNotification::QSOInserted); connect(ui->newContactWidget, &NewContactWidget::contactAdded, ui->bandmapWidget, &BandmapWidget::updateSpotsStatusWhenQSOAdded); connect(ui->newContactWidget, &NewContactWidget::contactAdded, ui->alertsWidget, &AlertWidget::updateSpotsStatusWhenQSOAdded); connect(ui->newContactWidget, &NewContactWidget::contactAdded, ui->chatWidget, &ChatWidget::updateSpotsStatusWhenQSOAdded); connect(ui->newContactWidget, &NewContactWidget::contactAdded, ui->wsjtxWidget, &WsjtxWidget::updateSpotsStatusWhenQSOAdded); connect(ui->newContactWidget, &NewContactWidget::contactAdded, ui->dxWidget, &DxWidget::setLastQSO); connect(ui->newContactWidget, &NewContactWidget::contactAdded, clublogRT, &ClubLogUploader::insertQSOImmediately); connect(ui->newContactWidget, &NewContactWidget::contestStarted, this, &MainWindow::startContest); connect(ui->newContactWidget, &NewContactWidget::newTarget, ui->mapWidget, &MapWidget::setTarget); connect(ui->newContactWidget, &NewContactWidget::newTarget, ui->onlineMapWidget, &OnlineMapWidget::setTarget); connect(ui->newContactWidget, &NewContactWidget::newTarget, ui->rotatorWidget, &RotatorWidget::setQSOBearing); connect(ui->newContactWidget, &NewContactWidget::filterCallsign, ui->logbookWidget, &LogbookWidget::filterCallsign); connect(ui->newContactWidget, &NewContactWidget::userFrequencyChanged, ui->bandmapWidget, &BandmapWidget::updateTunedFrequency); connect(ui->newContactWidget, &NewContactWidget::userFrequencyChanged, ui->onlineMapWidget, &OnlineMapWidget::setIBPBand); connect(ui->newContactWidget, &NewContactWidget::userFrequencyChanged, ui->dxWidget , &DxWidget::setTunedFrequency); connect(ui->newContactWidget, &NewContactWidget::userModeChanged, ui->bandmapWidget, &BandmapWidget::updateMode); connect(ui->newContactWidget, &NewContactWidget::markQSO, ui->bandmapWidget, &BandmapWidget::addSpot); connect(ui->newContactWidget, &NewContactWidget::callboolImageUrl, ui->profileImageWidget, &ProfileImageWidget::loadImageFromUrl); connect(ui->newContactWidget, &NewContactWidget::rigProfileChanged, this, &MainWindow::rigConnect); connect(ui->newContactWidget, &NewContactWidget::callsignChanged, ui->cwconsoleWidget, &CWConsoleWidget::stopRepeateButtons); connect(ui->newContactWidget, &NewContactWidget::contactReset, ui->cwconsoleWidget, &CWConsoleWidget::stopRepeateButtons); connect(ui->dxWidget, &DxWidget::newFilteredSpot, ui->bandmapWidget, &BandmapWidget::addSpot); connect(ui->dxWidget, &DxWidget::newFilteredSpot, Rig::instance(), &Rig::sendDXSpot); connect(ui->dxWidget, &DxWidget::newSpot, &networknotification, &NetworkNotification::dxSpot); connect(ui->dxWidget, &DxWidget::newSpot, &alertEvaluator, &AlertEvaluator::dxSpot); connect(ui->dxWidget, &DxWidget::newWCYSpot, &networknotification, &NetworkNotification::wcySpot); connect(ui->dxWidget, &DxWidget::newWWVSpot, &networknotification, &NetworkNotification::wwvSpot); connect(ui->dxWidget, &DxWidget::newToAllSpot, &networknotification, &NetworkNotification::toAllSpot); connect(ui->dxWidget, &DxWidget::tuneDx, ui->newContactWidget, &NewContactWidget::tuneDx); connect(ui->dxWidget, &DxWidget::tuneBand, ui->rigWidget, &RigWidget::setBand); ui->dxWidget->registerContactWidget(ui->newContactWidget); connect(&alertEvaluator, &AlertEvaluator::spotAlert, this, &MainWindow::processSpotAlert); connect(&alertEvaluator, &AlertEvaluator::spotAlert, &networknotification, &NetworkNotification::spotAlert); connect(ui->bandmapWidget, &BandmapWidget::tuneDx, ui->newContactWidget, &NewContactWidget::tuneDx); connect(ui->bandmapWidget, &BandmapWidget::nearestSpotFound, ui->newContactWidget, &NewContactWidget::setNearestSpot); connect(ui->bandmapWidget, &BandmapWidget::requestNewNonVfoBandmapWindow, this, &MainWindow::openNonVfoBandmap); connect(ui->wsjtxWidget, &WsjtxWidget::callsignSelected, ui->newContactWidget, &NewContactWidget::prepareWSJTXQSO); connect(ui->chatWidget, &ChatWidget::prepareQSOInfo, ui->newContactWidget, &NewContactWidget::fillCallsignGrid); connect(ui->chatWidget, &ChatWidget::userListUpdated, ui->onlineMapWidget, &OnlineMapWidget::drawChatUsers); connect(ui->chatWidget, &ChatWidget::beamingRequested, ui->rotatorWidget, &RotatorWidget::setBearing); connect(ui->onlineMapWidget, &OnlineMapWidget::chatCallsignPressed, ui->chatWidget, &ChatWidget::setChatCallsign); connect(ui->onlineMapWidget, &OnlineMapWidget::wsjtxCallsignPressed, ui->wsjtxWidget, &WsjtxWidget::callsignClicked); connect(ui->alertsWidget, &AlertWidget::rulesChanged, &alertEvaluator, &AlertEvaluator::loadRules); connect(ui->alertsWidget, &AlertWidget::alertsCleared, this, &MainWindow::clearAlertEvent); connect(ui->alertsWidget, &AlertWidget::tuneDx, ui->newContactWidget, &NewContactWidget::tuneDx); connect(ui->alertsWidget, &AlertWidget::tuneWsjtx, wsjtx, &WsjtxUDPReceiver::sendReply); conditions = new PropConditions(); connect(conditions, &PropConditions::conditionsUpdated, this, &MainWindow::conditionsUpdated); connect(conditions, &PropConditions::auroraMapUpdated, ui->onlineMapWidget, &OnlineMapWidget::auroraDataUpdate); connect(conditions, &PropConditions::mufMapUpdated, ui->onlineMapWidget, &OnlineMapWidget::mufDataUpdate); connect(conditions, &PropConditions::dxTrendFinalized, ui->dxWidget, &DxWidget::setDxTrend); ui->onlineMapWidget->assignPropConditions(conditions); ui->newContactWidget->assignPropConditions(conditions); connect(clublogRT, &ClubLogUploader::uploadError, this, [this](const QString &msg) { qCInfo(runtime) << "Clublog RT Upload Error: " << msg; QMessageBox::warning(this, tr("Clublog Immediately Upload Error"), msg); }); connect(clublogRT, &ClubLogUploader::uploadedQSO, ui->logbookWidget, &LogbookWidget::updateTable); if ( StationProfilesManager::instance()->profileNameList().isEmpty() ) firstRun = true; else MembershipQE::instance()->updateLists(); /********************/ /* GLOBAL SHORTCUTs */ /********************/ // Menu actions are defined in the MainWindow.ui file // The rest of global shortcuts are defined below connect(ui->actionSearchCallsign, &QAction::triggered, ui->logbookWidget, &LogbookWidget::focusSearchCallsign); connect(ui->actionAddBandmapMark, &QAction::triggered, ui->newContactWidget, &NewContactWidget::markContact); connect(ui->actionUseNearestCallsign, &QAction::triggered, ui->newContactWidget, &NewContactWidget::useNearestCallsign); connect(ui->actionBandSwitchUp, &QAction::triggered, ui->rigWidget, &RigWidget::bandUp); connect(ui->actionBandSwitchDown, &QAction::triggered, ui->rigWidget, &RigWidget::bandDown); connect(ui->actionCWSpeedUp, &QAction::triggered, ui->cwconsoleWidget, &CWConsoleWidget::cwKeySpeedIncrease); connect(ui->actionCWSpeedDown, &QAction::triggered, ui->cwconsoleWidget, &CWConsoleWidget::cwKeySpeedDecrease); connect(ui->actionCWProfileUp, &QAction::triggered, ui->cwconsoleWidget, &CWConsoleWidget::cwShortcutProfileIncrease); connect(ui->actionCWProfileDown, &QAction::triggered, ui->cwconsoleWidget, &CWConsoleWidget::cwShortcutProfileDecrease); // PTT Off is solved in the Event Handler because it is not possible to handle the Push/Release event for the shortcut. connect(ui->actionPTTOn, &QAction::triggered, this, &MainWindow::shortcutALTBackslash); // Register aditional action (global shortcuts) addAction(ui->actionSearchCallsign); addAction(ui->actionAddBandmapMark); addAction(ui->actionBandSwitchUp); addAction(ui->actionBandSwitchDown); addAction(ui->actionUseNearestCallsign); addAction(ui->actionCWSpeedUp); addAction(ui->actionCWSpeedDown); addAction(ui->actionCWProfileUp); addAction(ui->actionCWProfileDown); addAction(ui->actionPTTOn); addAction(ui->actionNewContact); addAction(ui->actionSaveContact); restoreUserDefinedShortcuts(); //restoreEquipmentConnOptions(); //restoreConnectionStates(); setupActivitiesMenu(); } void MainWindow::closeEvent(QCloseEvent* event) { FCT_IDENTIFICATION; const QString &currActivityProfile = ActivityProfilesManager::instance()->getCurProfile1().profileName; if ( currActivityProfile == QString() ) { // save dynamic Bandmap Widgets const QList> &bandmapList = getNonVfoBandmapsParams(); if ( bandmapList.isEmpty() ) LogParam::removeMainWindowBandmapWidgets(); else LogParam::setMainWindowBandmapWidgets(MainLayoutProfilesManager::toDBStringList(bandmapList)); } else LogParam::removeMainWindowBandmapWidgets(); // cleanup Bandmap config const QStringList configBandmapList = LogParam::bandmapsWidgets(); QSet configBandmapSet; #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) configBandmapSet = QSet(configBandmapList.begin(), configBandmapList.end()); #else configBandmapSet = QSet::fromList(configBandmapList); #endif MainLayoutProfilesManager *layoutManager = MainLayoutProfilesManager::instance(); QSet layoutBandmapSet; const QStringList &profiles = layoutManager->profileNameList(); for (const auto &addBandmapClassic : MainLayoutProfilesManager::toPairStringList(LogParam::getMainWindowBandmapWidgets())) layoutBandmapSet.insert(addBandmapClassic.first); for ( const QString &profile: profiles ) for ( const auto &addlProfileBandmap : layoutManager->getProfile(profile).addlBandmaps ) layoutBandmapSet.insert(addlProfileBandmap.first); QSet orphanConfigurations = configBandmapSet.subtract(layoutBandmapSet); orphanConfigurations.remove(ui->bandmapWidget->objectName()); // removed the main window name for ( const QString &orphanConfig : static_cast&>(orphanConfigurations) ) { qCDebug(runtime) << "Removing orphan configuration" << orphanConfig; LogParam::removeBandmapWidgetGroup(orphanConfig); } // Save unsaved widget states const auto allWidgets = findChildren(); for ( QWidget *w : allWidgets ) { ShutdownAwareWidget *widget = dynamic_cast(w); if ( widget ) widget->finalizeBeforeAppExit(); } // save the window geometry LogParam::setMainWindowGeometry(saveGeometry()); LogParam::setMainWindowState(saveState()); if ( stats ) { stats->close(); stats->deleteLater(); stats = nullptr; } const QList docks = findChildren(); for (QDockWidget* dock : docks) dock->close(); // Ensure they are closed QMainWindow::closeEvent(event); } /* It has to be controlled via global scope because keyReleaseEvent handles * only events from focused widget */ void MainWindow::keyReleaseEvent(QKeyEvent *event) { FCT_IDENTIFICATION; // shortcut has no Release signal therefore, it is necessary to handle it here. const QKeySequence &pttShortcut = ui->actionPTTOn->shortcut(); // QKeySequence can contain many combinations. check them for ( int pttShortcutIndex = 0; pttShortcutIndex < pttShortcut.count(); pttShortcutIndex++ ) { int key = pttShortcut[pttShortcutIndex] & ~Qt::KeyboardModifierMask; Qt::KeyboardModifiers modifiers = Qt::KeyboardModifiers(pttShortcut[pttShortcutIndex] & Qt::KeyboardModifierMask); if ( event->key() == key && event->modifiers() == modifiers && !event->isAutoRepeat() ) { emit altBackslash(false); } } QMainWindow::keyReleaseEvent(event); } QList MainWindow::getUserDefinedShortcutActionList() { QList ret; const QList &actions = findChildren(); for ( QAction* action : actions) { if ( !action->shortcut().toString(QKeySequence::NativeText).isEmpty() ) qCDebug(runtime) << action->text() << action->shortcut().toString(QKeySequence::NativeText); if ( action->property("changeableshortcut").toBool() ) { qCDebug(runtime) << "User-Defined shortcut" << action->shortcut().toString(QKeySequence::NativeText); ret << action; } } return ret; } QStringList MainWindow::getBuiltInStaticShortcutList() const { FCT_IDENTIFICATION; QStringList ret; const QList allShortcuts = findChildren(); for ( QShortcut* shortcut : allShortcuts) { if (!shortcut->key().toString().isEmpty()) { qCDebug(runtime) << "Built-In nonchangeble shortcut" << shortcut->key().toString(QKeySequence::NativeText); ret << shortcut->key().toString(QKeySequence::NativeText); } } const QList allButtons = findChildren(); for ( QPushButton* button : allButtons) { if ( !button->shortcut().toString().isEmpty()) { qCDebug(runtime) << "Built-In nonchangable shortcut - buttons" << button->shortcut().toString(QKeySequence::NativeText); ret << button->shortcut().toString(QKeySequence::NativeText); } } return ret; } void MainWindow::rigConnect() { FCT_IDENTIFICATION; //saveEquipmentConnOptions(); if ( ui->actionConnectRig->isChecked() ) Rig::instance()->open(); else Rig::instance()->close(); } void MainWindow::rigErrorHandler(const QString &error, const QString &errorDetail) { FCT_IDENTIFICATION; QMessageBox::warning(nullptr, QMessageBox::tr("QLog Warning"), QMessageBox::tr("Rig Error: ") + error + "

" + tr("Error Detail: ") + errorDetail + "

"); ui->actionConnectRig->setChecked(false); } void MainWindow::rotErrorHandler(const QString &error, const QString &errorDetail) { FCT_IDENTIFICATION; QMessageBox::warning(nullptr, QMessageBox::tr("QLog Warning"), QMessageBox::tr("Rotator Error: ") + error + "

" + tr("Error Detail: ") + errorDetail + "

"); ui->actionConnectRotator->setChecked(false); } void MainWindow::cwKeyerErrorHandler(const QString &error, const QString &errorDetail) { FCT_IDENTIFICATION; QMessageBox::warning(nullptr, QMessageBox::tr("QLog Warning"), QMessageBox::tr("CW Keyer Error: ") + error + "

" + tr("Error Detail: ") + errorDetail + "

"); ui->actionConnectCWKeyer->setChecked(false); } void MainWindow::stationProfileChanged() { FCT_IDENTIFICATION; const StationProfile &profile = StationProfilesManager::instance()->getCurProfile1(); qCDebug(runtime) << profile.callsign << " " << profile.locator << " " << profile.operatorName; profileLabel->setText("" + profile.profileName + ":"); callsignLabel->setText(stationCallsignStatus(profile)); locatorLabel->setText(profile.locator.toLower()); } QString MainWindow::stationCallsignStatus(const StationProfile &profile) const { FCT_IDENTIFICATION; if ( profile.operatorCallsign.isEmpty() || profile.callsign == profile.operatorCallsign ) return profile.callsign.toLower(); return profile.callsign.toLower() + " [" + tr("op: ") + profile.operatorCallsign.toLower() + "]"; } void MainWindow::openNonVfoBandmap(const QString &widgetID, const QString &bandName) { FCT_IDENTIFICATION; qCDebug(function_parameters) << widgetID << bandName; const Band &band = BandPlan::bandName2Band(bandName); QDockWidget *dock = nullptr; // bandmap docks stay open. Therefore it is necessary to decide whether // to reuse the dock or create a new one. dock = findChild(widgetID + "-dock"); if ( dock == nullptr ) { qCDebug(runtime) << "Creating a new Bandmap dock"; dock = new QDockWidget(this); dock->setAttribute(Qt::WA_MacAlwaysShowToolWindow, true); dock->setObjectName(widgetID + "-dock"); addDockWidget(Qt::RightDockWidgetArea, dock); dock->setFloating(true); const QRect &mainGeometry = geometry(); const QSize &dockSize = dock->sizeHint(); // middle int x = mainGeometry.x() + (mainGeometry.width() - dockSize.width()) / 2; int y = mainGeometry.y() + (mainGeometry.height() - dockSize.height()) / 2; dock->move(x, y); dock->resize(ui->bandmapDockWidget->size()); } BandmapWidget *bandmap = new BandmapWidget(widgetID, band, dock); dock->setWidget(bandmap); if ( !dock->isVisible() ) // show reused docks dock->show(); // the vfo bandmap takes care of managing the spot map, which is shared with the non vfo bandmaps. spotsUpdated // is triggered when spot map is dirty and the bandmaps should re-render. connect(ui->bandmapWidget, &BandmapWidget::spotsUpdated, bandmap, &BandmapWidget::updateStations); // connect selected signals as a common Bandmap widget connect(this, &MainWindow::themeChanged, bandmap, &BandmapWidget::update); connect(Rig::instance(), &Rig::frequencyChanged, bandmap, &BandmapWidget::updateTunedFrequency); connect(Rig::instance(), &Rig::modeChanged, bandmap, &BandmapWidget::updateMode); connect(ui->wsjtxWidget, &WsjtxWidget::frequencyChanged, bandmap, &BandmapWidget::updateTunedFrequency); connect(ui->newContactWidget, &NewContactWidget::userFrequencyChanged, bandmap, &BandmapWidget::updateTunedFrequency); connect(ui->newContactWidget, &NewContactWidget::userModeChanged, bandmap, &BandmapWidget::updateMode); connect(bandmap, &BandmapWidget::tuneDx, ui->newContactWidget, &NewContactWidget::tuneDx); } void MainWindow::openNonVfoBandmaps(const QList > &list) { FCT_IDENTIFICATION; // create additional bandmap widgets for ( const QPair &widget : list ) openNonVfoBandmap(widget.first, widget.second); } void MainWindow::clearNonVfoBandmaps() { FCT_IDENTIFICATION; const QList bandmapWidgets = ui->bandmapWidget->getNonVfoWidgetList(); BandmapWidget *widget = nullptr; QDockWidget *widgetDock = nullptr; for (auto it = bandmapWidgets.begin(); it != bandmapWidgets.end(); ++it) { widget = *it; if ( widget ) { widgetDock = findChild(widget->objectName() + "-dock"); widget->finalizeBeforeAppExit(); widget->setParent(nullptr); widget->close(); widget->deleteLater(); if ( widgetDock ) { widgetDock->close(); addDockWidget(Qt::RightDockWidgetArea, widgetDock); // without this, sometime is does not close the dock if it is floating //widgetDock->deleteLater(); // Do not delete the dock – Qlog will reuse it. This is a more reliable method when switching layouts. } } } } QList> MainWindow::getNonVfoBandmapsParams() const { FCT_IDENTIFICATION; QList> bandmapList; const QList bandmapWidgets = ui->bandmapWidget->getNonVfoWidgetList(); for ( BandmapWidget *widget : bandmapWidgets ) if ( widget && widget->isVisible() ) bandmapList << QPair(widget->objectName(), widget->getBand().name); qCDebug(runtime) << bandmapList; return bandmapList; } /* * This method is called only at startup and only on macOS and Windows. * For Linux, it makes no sense to call this, as the platform has its own update mechanisms. * And if it doesn’t, we still shouldn’t bother Linux users because * we are unable to distinguish whether the DEB/RPM package was installed directly from GitHub or through a distribution. * And if it’s from a distribution, the update dialog is unwanted. */ void MainWindow::checkNewVersion() { FCT_IDENTIFICATION; QNetworkAccessManager *manager = new QNetworkAccessManager(this); const QString repoName = #ifdef Q_OS_MAC "aa5sh/QLog"; #else "foldynl/QLog"; #endif const QUrl url("https://api.github.com/repos/" + repoName + "/releases/latest"); QNetworkRequest request(url); QNetworkReply *reply = manager->get(request); connect(reply, &QNetworkReply::finished, this, [this, reply, manager, repoName]() { if (reply->error()) { // do not show an error to user - It's not something the user should see. qWarning() << "Checking Version Error:" << reply->errorString(); } else { const QByteArray &response = reply->readAll(); const QJsonDocument &jsonDoc = QJsonDocument::fromJson(response); if ( jsonDoc.isObject() ) { QSettings settings; const QString &curVersion = QString("v%1").arg(VERSION); const QJsonObject &obj = jsonDoc.object(); const QString &newVersion = obj["tag_name"].toString(); const QString &seenVersion = settings.value("seenversion").toString(); qCDebug(runtime) << "Repo version" << newVersion << "currVersion" << curVersion << "setting string" << seenVersion; if( !newVersion.isEmpty() && curVersion != newVersion && seenVersion != newVersion) showUpdateDialog(newVersion, repoName); } else { // do not show an error to user - It's not something the user should see. qWarning() << "Checking Version Error: Invalid JSON: " << response; } } reply->deleteLater(); manager->deleteLater(); }); } void MainWindow::showUpdateDialog(const QString &newVersion, const QString &repoName) { FCT_IDENTIFICATION; QMessageBox msgBox; QSettings settings; msgBox.setWindowTitle(tr("A New Version")); msgBox.setText(tr("A new version %1 is available.").arg(newVersion)); msgBox.setIcon(QMessageBox::Information); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setDefaultButton(QMessageBox::Ok); msgBox.addButton(tr("Remind Me Later"), QMessageBox::ActionRole); QAbstractButton* pButtonDwn = msgBox.addButton(tr("Download"), QMessageBox::ActionRole); msgBox.exec(); QAbstractButton *clicked = msgBox.clickedButton(); if ( clicked == pButtonDwn ) QDesktopServices::openUrl(QUrl("https://www.github.com/" + repoName + "/releases/latest")); else if ( msgBox.standardButton(clicked) == QMessageBox::Ok) settings.setValue("seenversion", newVersion); // platform-depend parameter } void MainWindow::setDarkTheme() { FCT_IDENTIFICATION; QPalette darkPalette; QColor darkColor = QColor(45, 45, 45); QColor disabledColor = QColor(127, 127, 127); darkPalette.setColor(QPalette::Window, darkColor); darkPalette.setColor(QPalette::WindowText, Qt::white); darkPalette.setColor(QPalette::Base, QColor(18, 18, 18)); darkPalette.setColor(QPalette::Disabled, QPalette::Base, darkColor); darkPalette.setColor(QPalette::AlternateBase, darkColor); darkPalette.setColor(QPalette::Text, Qt::white); darkPalette.setColor(QPalette::Disabled, QPalette::Text, disabledColor); darkPalette.setColor(QPalette::Button, darkColor); darkPalette.setColor(QPalette::ButtonText, Qt::white); darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, disabledColor); darkPalette.setColor(QPalette::BrightText, Qt::red); darkPalette.setColor(QPalette::Link, QColor(42, 130, 218)); darkPalette.setColor(QPalette::Highlight, QColor(42, 130, 218)); darkPalette.setColor(QPalette::HighlightedText, Qt::black); darkPalette.setColor(QPalette::Disabled, QPalette::HighlightedText, disabledColor); qApp->setPalette(darkPalette); } void MainWindow::showEvent(QShowEvent *event) { FCT_IDENTIFICATION; QMainWindow::showEvent(event); if ( firstRun ) { firstRun = false; showSettings(); } } void MainWindow::setLightTheme() { FCT_IDENTIFICATION; QPalette lightPalette; QColor lightColor = QColor(239, 239, 239); QColor disabledColor = QColor(190, 190, 190); lightPalette.setColor(QPalette::Window, lightColor); lightPalette.setColor(QPalette::WindowText, Qt::black); lightPalette.setColor(QPalette::Base, Qt::white); lightPalette.setColor(QPalette::Disabled, QPalette::Base, lightColor); lightPalette.setColor(QPalette::AlternateBase, lightColor); lightPalette.setColor(QPalette::Text, Qt::black); lightPalette.setColor(QPalette::Disabled, QPalette::Text, disabledColor); lightPalette.setColor(QPalette::Button, lightColor); lightPalette.setColor(QPalette::ButtonText, Qt::black); lightPalette.setColor(QPalette::Disabled, QPalette::ButtonText, disabledColor); lightPalette.setColor(QPalette::BrightText, Qt::white); lightPalette.setColor(QPalette::Link, QColor(0, 0, 255)); lightPalette.setColor(QPalette::Highlight, QColor(48, 140, 198)); lightPalette.setColor(QPalette::HighlightedText, Qt::white); lightPalette.setColor(QPalette::Disabled, QPalette::HighlightedText, Qt::white); qApp->setPalette(lightPalette); } bool MainWindow::setNativeTheme() { FCT_IDENTIFICATION; qApp->setPalette(this->style()->standardPalette()); bool isDark = false; #if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) const auto scheme = QGuiApplication::styleHints()->colorScheme(); isDark = scheme == Qt::ColorScheme::Dark; #else const QPalette defaultPalette = this->style()->standardPalette(); const auto text = defaultPalette.color(QPalette::WindowText); const auto window = defaultPalette.color(QPalette::Window); isDark = text.lightness() > window.lightness(); #endif // QT_VERSION return isDark; } void MainWindow::themeInit(int mode) { FCT_IDENTIFICATION; LogParam::setMainWindowDarkMode(mode); bool isDark = false; switch (mode) { case 0: ui->actionThemeNative->setChecked(true); isDark = this->setNativeTheme(); break; case 1: ui->actionThemeDark->setChecked(true); this->setDarkTheme(); isDark = true; break; case 2: ui->actionThemeLight->setChecked(true); this->setLightTheme(); break; default: qWarning() << "Unsupported theme ID" << mode; } QFile style(":/res/stylesheet.css"); style.open(QFile::ReadOnly | QIODevice::Text); qApp->setStyleSheet(style.readAll()); style.close(); if (isDark) { themeButton->setIcon(QIcon(QPixmap(":/icons/color-palette-dark.svg"))); } else { themeButton->setIcon(QIcon(QPixmap(":/icons/color-palette-light.svg"))); } emit themeChanged(mode, isDark); } void MainWindow::changeEvent(QEvent *event) { if (event->type() == QEvent::ThemeChange) { if (ui->actionThemeNative->isChecked()) { this->themeInit(0); } } QMainWindow::changeEvent(event); } void MainWindow::processSpotAlert(SpotAlert alert) { FCT_IDENTIFICATION; ui->alertsWidget->addAlert(alert); alertButton->setText(QString::number(ui->alertsWidget->alertCount())); alertTextButton->setText(alert.ruleNameList.join(", ") + ": " + alert.spot.callsign + ", " + alert.spot.band + ", " + alert.spot.modeGroupString); if (alertTextButtonConn) QObject::disconnect(alertTextButtonConn); alertTextButtonConn = connect(alertTextButton, &QPushButton::clicked, this, [this, alert]() { if ( alert.source == SpotAlert::WSJTXCQSPOT ) wsjtx->sendReply(alert.spot); else ui->newContactWidget->tuneDx(alert.getDxSpot()); }); if ( ui->actionBeepSettingAlert->isChecked() ) QApplication::beep(); } void MainWindow::clearAlertEvent() { FCT_IDENTIFICATION; int newCount = ui->alertsWidget->alertCount(); alertButton->setText(QString::number(newCount)); if ( newCount == 0 ) { alertTextButton->setText(" "); if (alertTextButtonConn) QObject::disconnect(alertTextButtonConn); } } void MainWindow::beepSettingAlerts() { FCT_IDENTIFICATION; LogParam::setMainWindowAlertBeep(ui->actionBeepSettingAlert->isChecked()); if ( ui->actionBeepSettingAlert->isChecked() ) QApplication::beep(); } void MainWindow::shortcutALTBackslash() { FCT_IDENTIFICATION; emit altBackslash(true); } void MainWindow::setManualContact(bool isChecked) { FCT_IDENTIFICATION; emit manualMode(isChecked); } void MainWindow::showEditLayout() { FCT_IDENTIFICATION; EditActivitiesDialog dialog(this); dialog.exec(); setupActivitiesMenu(); } void MainWindow::showServiceUpload() { FCT_IDENTIFICATION; UploadQSODialog dialog(this); dialog.exec(); ui->logbookWidget->updateTable(); } void MainWindow::showServiceDownloadQSL() { FCT_IDENTIFICATION; DownloadQSLDialog dialog(this); dialog.exec(); ui->logbookWidget->updateTable(); } void MainWindow::showQSLGallery() { FCT_IDENTIFICATION; QSLGalleryDialog dialog(this); dialog.exec(); } void MainWindow::showDevTools() { FCT_IDENTIFICATION; DevToolsDialog dialog(this); dialog.exec(); } void MainWindow::printQslLabels() { FCT_IDENTIFICATION; QSLPrintLabelDialog dialog(this); dialog.exec(); } void MainWindow::showDumpDB() { FCT_IDENTIFICATION; ExportPasswordDialog passDialog(this); if ( passDialog.exec() != QDialog::Accepted ) return; const QString password = passDialog.getPassword(); const bool deletePasswords = passDialog.getDeletePasswords(); if ( !CredentialStore::instance()->exportPasswords(password) ) { QMessageBox::warning(this, tr("Pack Data && Settings"), tr("Failed to encrypt credentials.")); return; } const QString documentsPath = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); QString filename = QFileDialog::getSaveFileName(this, tr("Pack Data && Settings"), documentsPath, tr("Database files (*.dbe);;All files (*)")); if ( filename.isEmpty() ) { LogParam::removeEncryptedPasswords(); LogParam::removeSourcePlatform(); return; } // Add .dbe suffix if not present if ( !filename.endsWith(".dbe", Qt::CaseInsensitive) ) filename.append(".dbe"); // Create temporary file for uncompressed database QTemporaryFile tempFile; if ( !tempFile.open() ) { LogParam::removeEncryptedPasswords(); LogParam::removeSourcePlatform(); QMessageBox::warning(this, tr("Pack Data && Settings"), tr("Failed to create temporary file.")); return; } QString tempPath = tempFile.fileName(); tempFile.close(); // Copy database to temporary file bool ok = LogDatabase::instance()->atomicCopy(tempPath); LogParam::removeEncryptedPasswords(); LogParam::removeSourcePlatform(); if ( !ok ) { QFile::remove(tempPath); QMessageBox::warning(this, tr("Pack Data && Settings"), tr("Failed to dump the database.")); return; } // Compress temporary file to destination (with progress dialog) ok = FileCompressor::gzipFileWithProgress(tempPath, filename, this, tr("Compressing database...")); QFile::remove(tempPath); if ( ok ) { if ( deletePasswords ) CredentialStore::instance()->deleteAllPasswords(); QMessageBox::information(this, tr("Pack Data && Settings"), tr("Database successfully dumped to\n%1").arg(filename)); } else { QFile::remove(filename); QMessageBox::warning(this, tr("Pack Data && Settings"), tr("Failed to compress the database.")); } } void MainWindow::showLoadDB() { FCT_IDENTIFICATION; LoadDatabaseDialog loadDialog(this); if ( loadDialog.exec() != QDialog::Accepted ) return; const QString password = loadDialog.getPassword(); const bool crossPlatform = loadDialog.isCrossPlatform(); // Take ownership of decompressed file (we must delete it) const QString decompressedFile = loadDialog.takeDecompressedFile(); // Handle cross-platform settings if needed if ( crossPlatform ) { DatabaseInfo dbInfo = LogDatabase::inspectDatabase(decompressedFile); QList params = PlatformParameterManager::getParameters(decompressedFile, dbInfo.sourcePlatform); QList profileParams = PlatformParameterManager::getProfileParameters(decompressedFile, dbInfo.sourcePlatform); if ( !params.isEmpty() || !profileParams.isEmpty() ) { PlatformSettingsDialog settingsDialog(this); settingsDialog.setParameters(params, profileParams); if ( settingsDialog.exec() != QDialog::Accepted ) { QFile::remove(decompressedFile); return; } // Save modified parameters to a JSON file for later application QList modifiedParams = settingsDialog.getParameters(); QList modifiedProfileParams = settingsDialog.getProfilePortParameters(); PlatformParameterManager::saveParametersToFile(modifiedParams, modifiedProfileParams, PlatformParameterManager::pendingParametersPath()); } } // Save import passphrase to SecureStore if ( !password.isEmpty() ) CredentialStore::instance()->saveImportPassphrase(password); // Move decompressed file to pending import location const QString pendingPath = LogDatabase::pendingImportPath(); qCDebug(runtime) << "Decompressed file:" << decompressedFile << "exists:" << QFile::exists(decompressedFile); qCDebug(runtime) << "Pending path:" << pendingPath; // Remove existing pending file if any if ( QFile::exists(pendingPath) ) QFile::remove(pendingPath); if ( !QFile::rename(decompressedFile, pendingPath) ) { qCDebug(runtime) << "Rename failed, trying copy"; // rename failed, try copy + remove if ( !QFile::copy(decompressedFile, pendingPath) ) { qWarning() << "Copy also failed from" << decompressedFile << "to" << pendingPath; QFile::remove(PlatformParameterManager::pendingParametersPath()); QMessageBox::warning(this, tr("Unpack Data && Settings"), tr("Failed to prepare database for import.")); CredentialStore::instance()->deleteImportPassphrase(); QFile::remove(decompressedFile); return; } QFile::remove(decompressedFile); } // Restart the application restartApplication(); } void MainWindow::restartApplication() { FCT_IDENTIFICATION; qCDebug(runtime) << "Restarting application for database import"; // Wait a bit before starting new instance to ensure clean shutdown QThread::msleep(500); // Get original arguments (first one is app path) QStringList args = QCoreApplication::arguments(); args.removeFirst(); // Remove app path // Remove --import-pending if already present (avoid duplicates) args.removeAll("--import-pending"); // Add import-pending argument args << "--import-pending"; QProcess::startDetached(QCoreApplication::applicationFilePath(), args); // Quit current instance qApp->quit(); } void MainWindow::setLayoutGeometry() { FCT_IDENTIFICATION; // restore the window geometry and state const MainLayoutProfile &layoutProfile = MainLayoutProfilesManager::instance()->getCurProfile1(); QByteArray newGeometry; QByteArray newState; int darkMode = 0; const QList> bandmapWidgets = (layoutProfile.profileName.isEmpty()) ? MainLayoutProfilesManager::toPairStringList(LogParam::getMainWindowBandmapWidgets()) : layoutProfile.addlBandmaps; if ( layoutProfile.mainGeometry != QByteArray() || layoutProfile.mainState != QByteArray() ) { // layout from config newGeometry = layoutProfile.mainGeometry; newState = layoutProfile.mainState; darkMode = layoutProfile.darkMode; } else { // Classic Layout newGeometry = LogParam::getMainWindowGeometry(); newState = LogParam::getMainWindowState(); darkMode = LogParam::getMainWindowDarkMode(); } openNonVfoBandmaps(bandmapWidgets); #ifdef Q_OS_LINUX // workaround for QTBUG-46620 showNormal(); QApplication::processEvents(); #endif restoreGeometry(newGeometry); // workaround for QTBUG-46620 QTimer* nt = new QTimer(this); nt->setSingleShot(true); nt->setInterval(500); connect(nt, &QTimer::timeout, this, [this, darkMode, newState]() { restoreState(newState); this->themeInit(isFusionStyle ? darkMode : 0); connect(MainLayoutProfilesManager::instance(), &MainLayoutProfilesManager::profileChanged, this, &MainWindow::setSimplyLayoutGeometry); }); nt->connect(nt, &QTimer::timeout, nt, &QTimer::deleteLater); nt->start(); } void MainWindow::setSimplyLayoutGeometry() { //this method is a nextstep of the workaround for QTBUG-46620. // In the repeated setLayout, it is necessary to call only this. FCT_IDENTIFICATION; const MainLayoutProfile &layoutProfile = MainLayoutProfilesManager::instance()->getCurProfile1(); if ( !layoutProfile.profileName.isEmpty() ) clearNonVfoBandmaps(); openNonVfoBandmaps(layoutProfile.addlBandmaps); if ( layoutProfile.mainGeometry != QByteArray() || layoutProfile.mainState != QByteArray() ) { #ifdef Q_OS_LINUX // workaround for QTBUG-46620 showNormal(); QApplication::processEvents(); #endif restoreGeometry(layoutProfile.mainGeometry); QApplication::processEvents(); // workaround for QTBUG-46620 QTimer* nt = new QTimer(this); nt->setSingleShot(true); nt->setInterval(500); connect(nt, &QTimer::timeout, this, [this, layoutProfile]() { restoreState(layoutProfile.mainState); this->themeInit(isFusionStyle ? layoutProfile.darkMode : 0); }); nt->connect(nt, &QTimer::timeout, nt, &QTimer::deleteLater); nt->start(); } } void MainWindow::saveProfileLayoutGeometry() { FCT_IDENTIFICATION; MainLayoutProfile layoutProfile = MainLayoutProfilesManager::instance()->getCurProfile1(); if ( layoutProfile != MainLayoutProfile() ) { layoutProfile.addlBandmaps = getNonVfoBandmapsParams(); layoutProfile.mainGeometry = saveGeometry(); layoutProfile.mainState = saveState(); layoutProfile.darkMode = 0; if (ui->actionThemeDark->isChecked()) { layoutProfile.darkMode = 1; } else if (ui->actionThemeLight->isChecked()) { layoutProfile.darkMode = 2; } layoutProfile.tabsexpanded = ui->newContactWidget->getTabCollapseState(); MainLayoutProfilesManager::instance()->addProfile(layoutProfile.profileName, layoutProfile); MainLayoutProfilesManager::instance()->blockSignals(true); // prevent screen flashing MainLayoutProfilesManager::instance()->save(); MainLayoutProfilesManager::instance()->blockSignals(false); } } void MainWindow::setEquipmentKeepOptions(bool) { FCT_IDENTIFICATION; // this is obsolete, use activities instead. // Left only because of possible problems and for quick activation of the function. //saveEquipmentConnOptions(); } void MainWindow::setupActivitiesMenu() { FCT_IDENTIFICATION; QMenu *actionMenu = activityButton->menu(); actionMenu->clear(); const QString &currActivityProfile = ActivityProfilesManager::instance()->getCurProfile1().profileName; // The first position will be always the Classic Profile QAction *classicLayoutAction = new QAction(tr("Classic"), actionMenu); classicLayoutAction->setCheckable(true); if ( currActivityProfile == QString() ) { classicLayoutAction->setChecked(true); ui->actionSaveGeometry->setEnabled(false); setSimplyLayoutGeometry(); activityButton->setText(classicLayoutAction->text()); } connect(classicLayoutAction, &QAction::triggered, this, [this, classicLayoutAction]() { //save empty profile // Classic Action is only about Layout MainLayoutProfilesManager::instance()->setCurProfile1(""); ActivityProfilesManager::instance()->setCurProfile1(""); ui->actionSaveGeometry->setEnabled(false); activityButton->setText(classicLayoutAction->text()); } ); actionMenu->addAction(classicLayoutAction); QActionGroup *activitiMenuGroup = new QActionGroup(classicLayoutAction); activitiMenuGroup->addAction(classicLayoutAction); actionMenu->addSeparator(); // The rest of positions will be the Custom Activity Profiles const QStringList &activityProfileNames = ActivityProfilesManager::instance()->profileNameList(); for ( const QString &profileName : activityProfileNames ) { QAction *activityAction = new QAction(profileName, actionMenu); activityAction->setCheckable(true); if ( currActivityProfile == profileName ) { activityAction->setChecked(true); ui->actionSaveGeometry->setEnabled(true); ActivityProfilesManager::instance()->setAllProfiles(); activityButton->setText(activityAction->text()); } connect(activityAction, &QAction::triggered, this, [this, profileName, activityAction]() { ActivityProfilesManager::instance()->setCurProfile1(profileName); ui->actionSaveGeometry->setEnabled(true); activityButton->setText(activityAction->text()); } ); actionMenu->addAction(activityAction); activitiMenuGroup->addAction(activityAction); } actionMenu->addSeparator(); actionMenu->addAction(ui->actionSaveGeometry); actionMenu->addSeparator(); actionMenu->addAction(ui->actionActivitiesEdit); } void MainWindow::restoreUserDefinedShortcuts() { FCT_IDENTIFICATION; QSettings settings; //platform-dependent, must be present const QHash &state = settings.value("shortcuts").toHash(); if ( state.count() > 0) { const QList actions = getUserDefinedShortcutActionList(); for ( QAction *action : actions ) { const QVariant &value = state.value(action->objectName()); const QString ¤t = action->shortcut().toString(QKeySequence::NativeText); qCDebug(runtime) << "Object "<< action->objectName() << "current shortcut" << current << "requested" << value.toString(); if ( value != QVariant() && current != value.toString()) { action->setShortcut(value.toString()); qCDebug(runtime) << "Changed"; } } } } void MainWindow::saveUserDefinedShortcuts() { FCT_IDENTIFICATION; QSettings settings; //platform-dependent, must be present QHash state; const QList actions = getUserDefinedShortcutActionList(); for ( const QAction *action : actions ) { const QString & newShortcut = action->shortcut().toString(QKeySequence::NativeText); qCDebug(runtime) << "Object" << action->objectName() << "has a shortcut" << newShortcut; state[action->objectName()] = newShortcut; } settings.setValue("shortcuts", state); } void MainWindow::saveContestMenuSeqnoType(QAction *action) { FCT_IDENTIFICATION; LogParam::setContestSeqnoType(action->data()); // this function is called only if contest is not active // therefore it is not needed to somehow recalculate seq } void MainWindow::restoreContestMenuSeqnoType() { FCT_IDENTIFICATION; ui->actionSeqSingle->setData(Data::SeqType::SINGLE); ui->actionSeqPerBand->setData(Data::SeqType::PER_BAND); seqGroup = new QActionGroup(ui->menuSequence); seqGroup->addAction(ui->actionSeqSingle); seqGroup->addAction(ui->actionSeqPerBand); int seqnoType = LogParam::getContestSeqnoType(); const QList seqActions = seqGroup->actions(); for ( QAction *action : seqActions) { if ( action->data().toInt() == seqnoType ) { action->setChecked(true); break; } } } void MainWindow::saveContestMenuDupeType(QAction *action) { FCT_IDENTIFICATION; LogParam::setContestManuDupeType(action->data()); emit dupeTypeChanged(); } void MainWindow::saveContestMenuLinkExchangeType(QAction *action) { FCT_IDENTIFICATION; LogParam::setContestLinkExchange(action->data()); ui->newContactWidget->changeSRXStringLink(action->data().toInt()); } void MainWindow::restoreContestMenuDupeType() { FCT_IDENTIFICATION; dupeGroup = new QActionGroup(ui->menuDupeCheck); ui->actionDupeAllBands->setData(Data::DupeType::ALL_BANDS); ui->actionDupeEachBand->setData(Data::DupeType::EACH_BAND); ui->actionDupeEachBandMode->setData(Data::DupeType::EACH_BAND_MODE); ui->actionDupeNoCheck->setData(Data::DupeType::NO_CHECK); dupeGroup->addAction(ui->actionDupeAllBands); dupeGroup->addAction(ui->actionDupeEachBand); dupeGroup->addAction(ui->actionDupeEachBandMode); dupeGroup->addAction(ui->actionDupeNoCheck); int dupeType = LogParam::getContestDupeType(); const QList seqActions = dupeGroup->actions(); for ( QAction *action : seqActions) { if ( action->data().toInt() == dupeType ) { action->setChecked(true); break; } } } void MainWindow::restoreContestMenuLinkExchange() { FCT_IDENTIFICATION; linkExchangeGroup = new QActionGroup(ui->menuLinkExchange); int linkExchangeType = LogParam::getContestLinkExchange(); ui->actionLinkExchangeNone->setData(LogbookModel::COLUMN_INVALID); linkExchangeGroup->addAction(ui->actionLinkExchangeNone); ui->actionLinkExchangeNone->setChecked(linkExchangeType == LogbookModel::COLUMN_INVALID); QList actions; auto addActionToMenu = [&] (const LogbookModel::ColumnID columnID) { QAction *newAction = new QAction(ui->menuLinkExchange); newAction->setCheckable(true); newAction->setText(LogbookModel::getFieldNameTranslation(columnID)); newAction->setData(columnID); actions.append(newAction); }; addActionToMenu(LogbookModel::COLUMN_AGE); addActionToMenu(LogbookModel::COLUMN_CQZ); addActionToMenu(LogbookModel::COLUMN_ITUZ); addActionToMenu(LogbookModel::COLUMN_GRID); addActionToMenu(LogbookModel::COLUMN_NAME_INTL); addActionToMenu(LogbookModel::COLUMN_QTH_INTL); addActionToMenu(LogbookModel::COLUMN_RX_PWR); addActionToMenu(LogbookModel::COLUMN_STATE); std::sort(actions.begin(), actions.end(), [](const QAction *a, const QAction *b) { return a->text().localeAwareCompare(b->text()) < 0; }); for (QAction *action : actions) { ui->menuLinkExchange->addAction(action); linkExchangeGroup->addAction(action); if ( action->data().toInt() == linkExchangeType ) action->setChecked(true); } ui->newContactWidget->changeSRXStringLink(linkExchangeType); } void MainWindow::startContest(const QString contestID, const QDateTime dateTime) { FCT_IDENTIFICATION; // Contest's start signal is sent from NewContact const QSOFilter &contestFilter = QSOFilter::createFromDateContestFilter(contestID, dateTime); QSOFilterManager::instance()->save(contestFilter); ui->logbookWidget->refreshUserFilter(); ui->logbookWidget->setUserFilter(contestFilter.filterName); LogParam::setContestFilter(contestFilter.filterName); setContestMode(contestID); } void MainWindow::stopContest() { FCT_IDENTIFICATION; const QString &contestFilterName = LogParam::getContestFilter(); if ( !contestFilterName.isEmpty() ) { QMessageBox::StandardButton reply; reply = QMessageBox::question(this, tr("Contest"), tr("Do you want to remove the Contest filter %1?").arg(contestFilterName), QMessageBox::Yes|QMessageBox::No); if ( reply == QMessageBox::Yes ) { QSOFilterManager::instance()->remove(contestFilterName); ui->logbookWidget->refreshUserFilter(); } else { QSOFilter contestFilter = QSOFilterManager::instance()->getFilter(contestFilterName); contestFilter.addRule(QSOFilter::createToDateRule(QDateTime::currentDateTimeUtc())); QSOFilterManager::instance()->save(contestFilter); } } LogParam::setContestFilter(QString()); setContestMode(QString()); emit contestStopped(); } void MainWindow::setContestMode(const QString &contestID) { FCT_IDENTIFICATION; bool isActive = !contestID.isEmpty(); ui->actionContestStop->setEnabled(isActive); if ( seqGroup ) seqGroup->setEnabled(!isActive); contestLabel->setVisible(isActive); contestLabel->setText((isActive) ? "" + tr("Contest: ") + "" + contestID : QString()); } void MainWindow::handleActivityChange(const QString name) { FCT_IDENTIFICATION; qCDebug(function_parameters) << name; const ActivityProfile &profile = ActivityProfilesManager::instance()->getProfile(name); const QVariant &valueRig = profile.getProfileParam(ActivityProfile::ProfileType::RIG_PROFILE, ActivityProfile::ProfileParamType::CONNECT); if ( !valueRig.isNull() && RigProfilesManager::instance()->getCurProfile1().profileName == profile.profiles[ActivityProfile::ProfileType::RIG_PROFILE].name ) { if ( ui->actionConnectRig->isChecked() && valueRig.toBool() ) rigConnect(); else ui->actionConnectRig->setChecked(valueRig.toBool()); // rigConnect is called when the signal is processed } const QVariant &valueRot = profile.getProfileParam(ActivityProfile::ProfileType::ROT_PROFILE, ActivityProfile::ProfileParamType::CONNECT); if ( !valueRig.isNull() && RotProfilesManager::instance()->getCurProfile1().profileName == profile.profiles[ActivityProfile::ProfileType::ROT_PROFILE].name ) { if ( ui->actionConnectRotator->isChecked() && valueRig.toBool() ) rotConnect(); else ui->actionConnectRotator->setChecked(valueRot.toBool()); // rotConnect is called when the signal is processed } } void MainWindow::rotConnect() { FCT_IDENTIFICATION; //saveEquipmentConnOptions(); if ( ui->actionConnectRotator->isChecked() ) Rotator::instance()->open(); else Rotator::instance()->close(); } void MainWindow::cwKeyerConnect() { FCT_IDENTIFICATION; //saveEquipmentConnOptions();; if ( ui->actionConnectCWKeyer->isChecked() ) { CWKeyer::instance()->open(); } else { CWKeyer::instance()->close(); } } void MainWindow::cwKeyerConnectProfile(QString requestedProfile) { FCT_IDENTIFICATION; // it is a hack, maybe it will be improved in the future if ( requestedProfile == EMPTY_PROFILE_NAME || requestedProfile.isEmpty() ) { return; } CWKeyProfile testingProfile = CWKeyProfilesManager::instance()->getProfile(requestedProfile); if ( testingProfile == CWKeyProfile() ) //if requested profile does not exists then no change { qWarning() << "Rig request unknown CW Key Profile"; return; } ui->actionConnectCWKeyer->setChecked(true); CWKeyProfilesManager::instance()->setCurProfile1(requestedProfile); cwKeyerConnect(); } void MainWindow::cwKeyerDisconnectProfile(QString requestedProfile) { FCT_IDENTIFICATION; // it is a hack, maybe it will be improved in the future if ( requestedProfile == EMPTY_PROFILE_NAME || requestedProfile.isEmpty() ) { return; } CWKeyProfile testingProfile = CWKeyProfilesManager::instance()->getProfile(requestedProfile); if ( testingProfile == CWKeyProfile() ) //if requested profile does not exists then no change { qWarning() << "Rig requests an unknown CW Key Profile"; return; } /* checking whether the user has changed the assigned key during the work. If so, leave it connected. */ if ( testingProfile != CWKeyProfilesManager::instance()->getCurProfile1() ) { return; } ui->actionConnectCWKeyer->setChecked(false); cwKeyerConnect(); } void MainWindow::showSettings() { FCT_IDENTIFICATION; SettingsDialog sw(this); if ( sw.exec() == QDialog::Accepted ) { Data::instance()->clearDXCCStatusCache(); rigConnect(); rotConnect(); stationProfileChanged(); MembershipQE::instance()->updateLists(); saveUserDefinedShortcuts(); emit settingsChanged(); } else restoreUserDefinedShortcuts(); } void MainWindow::showStatistics() { FCT_IDENTIFICATION; if ( stats ) { stats->show(); } } void MainWindow::importLog() { FCT_IDENTIFICATION; ImportDialog dialog(this); dialog.exec(); ui->logbookWidget->updateTable(); } void MainWindow::exportLog() { FCT_IDENTIFICATION; ExportDialog dialog(this); dialog.exec(); ui->logbookWidget->updateTable(); } void MainWindow::exportCabrillo() { FCT_IDENTIFICATION; CabrilloExportDialog dialog(this); dialog.exec(); } void MainWindow::showAwards() { FCT_IDENTIFICATION; AwardsDialog* dialog = new AwardsDialog (this); connect(dialog, &AwardsDialog::awardConditionSelected, ui->logbookWidget, &LogbookWidget::filterCountryBand); connect(dialog, &AwardsDialog::finished, ui->logbookWidget, &LogbookWidget::restoreFilters); dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->show(); } void MainWindow::showDXCCSubmission() { FCT_IDENTIFICATION; DXCCSubmissionDialog dialog(this); dialog.exec(); } void MainWindow::showAbout() { FCT_IDENTIFICATION; QString aboutText = tr("

QLog %1

" "

© 2019 Thomas Gatzweiler DL2IC
" "© 2021-2026 Ladislav Foldyna OK1MLG
" "© 2025-2026 Michael Morgan AA5SH
" "© 2025-2026 Kyle Boyle VE9KZ

" "

Based on Qt %2
" "%3
" "%4
" "%5

" "
ui/NewContactWidget.h
1 contactAdded() newTarget(double,double) resetContact() saveContact() tuneDx(QString,double) readGlobalSettings() resetSTXSeq() LogbookWidget QWidget
ui/LogbookWidget.h
1 updateTable()
DxWidget QWidget
ui/DxWidget.h
1 tuneDx(QString,double)
WsjtxWidget QWidget
ui/WsjtxWidget.h
1
ClockWidget QWidget
ui/ClockWidget.h
1
MapWidget QWidget
ui/MapWidget.h
1
RotatorWidget QWidget
ui/RotatorWidget.h
1
BandmapWidget QWidget
ui/BandmapWidget.h
1
RigWidget QWidget
ui/RigWidget.h
1
OnlineMapWidget QWidget
ui/OnlineMapWidget.h
1
CWConsoleWidget QWidget
ui/CWConsoleWidget.h
1
ChatWidget QWidget
ui/ChatWidget.h
1
ProfileImageWidget QWidget
ui/ProfileImageWidget.h
1
AlertWidget QWidget
ui/AlertWidget.h
1
actionQuit triggered() MainWindow close() -1 -1 284 195 actionSettings triggered() MainWindow showSettings() -1 -1 317 231 actionDumpDB triggered() MainWindow showDumpDB() -1 -1 317 231 actionLoadDB triggered() MainWindow showLoadDB() -1 -1 317 231 actionExport triggered() MainWindow exportLog() -1 -1 399 325 actionImport triggered() MainWindow importLog() -1 -1 399 325 actionNewContact triggered() newContactWidget resetContact() -1 -1 408 622 actionSaveContact triggered() newContactWidget saveContact() -1 -1 408 622 actionAbout triggered() MainWindow showAbout() -1 -1 404 264 actionConnectRig toggled(bool) MainWindow rigConnect() -1 -1 446 264 actionStatistics triggered() MainWindow showStatistics() -1 -1 446 264 actionDXClusterWindow triggered() dxDockWidget show() -1 -1 845 340 actionMapWindow triggered() mapDockWidget show() -1 -1 845 227 actionWsjtx triggered() wsjtxDockWidget show() -1 -1 845 452 actionConnectRotator toggled(bool) MainWindow rotConnect() -1 -1 446 264 actionRotator triggered() rotatorDockWidget show() -1 -1 845 407 actionBandmap triggered() bandmapDockWidget show() -1 -1 845 482 actionRig triggered() rigDockWidget show() -1 -1 845 480 actionClock triggered() clockDockWidget show() -1 -1 845 93 actionQSOFilters triggered() MainWindow QSOFilterSetting() -1 -1 446 264 actionOnlineMap triggered() onlineMapDockWidget show() -1 -1 879 545 actionDXCCSubmission triggered() MainWindow showDXCCSubmission() -1 -1 456 298 actionAwards triggered() MainWindow showAwards() -1 -1 456 298 actionEditAlertRules triggered() MainWindow alertRuleSetting() -1 -1 456 298 actionBeepSettingAlert triggered() MainWindow beepSettingAlerts() -1 -1 456 298 actionConnectCWKeyer toggled(bool) MainWindow cwKeyerConnect() -1 -1 456 298 actionCWConsole triggered() cwConsoleDockWidget show() -1 -1 835 585 actionWikiHelp triggered() MainWindow showWikiHelp() -1 -1 456 312 actionReportBug triggered() MainWindow showReportBug() -1 -1 456 312 actionManualContact triggered(bool) MainWindow setManualContact(bool) -1 -1 456 312 actionMailingList triggered() MainWindow showMailingList() -1 -1 456 312 actionActivitiesEdit triggered() MainWindow showEditLayout() -1 -1 456 312 actionChat triggered() chatDockWidget show() -1 -1 771 585 actionSaveGeometry triggered() MainWindow saveProfileLayoutGeometry() -1 -1 456 256 actionProfileImage triggered() profileImageDockWidget show() -1 -1 883 518 actionAlerts triggered() MainWindow showAlerts() -1 -1 456 278 actionClearAlerts triggered() MainWindow clearAlerts() -1 -1 456 278 actionShowAlerts triggered() MainWindow showAlerts() -1 -1 456 278 actionContestStop triggered() MainWindow stopContest() -1 -1 456 278 actionSeqReset triggered() newContactWidget resetSTXSeq() -1 -1 426 529 actionServiceUpload triggered() MainWindow showServiceUpload() -1 -1 456 278 actionServiceDownload triggered() MainWindow showServiceDownloadQSL() -1 -1 456 278 actionWhatsNew triggered() MainWindow showWhatsNew() -1 -1 456 278 actionQSLGallery triggered() MainWindow showQSLGallery() -1 -1 456 278 actionDeveloperTools triggered() MainWindow showDevTools() -1 -1 456 298 actionPrintQslLabels triggered() MainWindow printQslLabels() -1 -1 456 278 actionExportCabrillo triggered() MainWindow exportCabrillo() -1 -1 456 312 settingsChanged() saveContact() resetContact() showSettings() importLog() exportLog() showAbout() rigConnect() showStatistics() rotConnect() QSOFilterSetting() showAwards() showDXCCSubmission() alertRuleSetting() showAlerts() clearAlerts() beepSettingAlerts() cwKeyerConnect() showWikiHelp() showReportBug() setManualContact(bool) showMailingList() showEditLayout() saveProfileLayoutGeometry() setEquipmentKeepOptions(bool) stopContest() showServiceDownloadQSL() showServiceUpload() showWhatsNew() showDumpDB() showLoadDB() showQSLGallery() showDevTools() printQslLabels() exportCabrillo() ================================================ FILE: ui/MapWebChannelHandler.cpp ================================================ #include #include "MapWebChannelHandler.h" #include "core/debug.h" #include "core/LogParam.h" MODULE_IDENTIFICATION("qlog.ui.maplayercontrolhandler"); MapWebChannelHandler::MapWebChannelHandler(const QString &configID, QObject *parent) : QObject(parent), configID(configID) { } void MapWebChannelHandler::connectWebChannel(QWebEnginePage *page) { FCT_IDENTIFICATION; QFile file(":/qtwebchannel/qwebchannel.js"); if (!file.open(QIODevice::ReadOnly)) { qCInfo(runtime) << "Cannot read qwebchannel.js"; return; } QTextStream stream(&file); QString js; js.append(stream.readAll()); js += " var webChannel = new QWebChannel(qt.webChannelTransport, function(channel) " "{ window.foo = channel.objects.layerControlHandler; });" " map.on('overlayadd', function(e){ " " switch (e.name) " " { " " case '" + tr("Grid") + "': " " foo.handleLayerSelectionChanged('maidenheadConfWorked', 'on'); " " break; " " case '" + tr("Gray-Line") + "': " " foo.handleLayerSelectionChanged('grayline', 'on'); " " break; " " case '" + tr("Beam") + "': " " foo.handleLayerSelectionChanged('antPathLayer', 'on'); " " break; " " case '" + tr("Aurora") + "': " " foo.handleLayerSelectionChanged('auroraLayer', 'on'); " " break; " " case '" + tr("MUF") + "': " " foo.handleLayerSelectionChanged('mufLayer', 'on'); " " break; " " case '" + tr("IBP") + "': " " foo.handleLayerSelectionChanged('IBPLayer', 'on'); " " break; " " case '" + tr("Chat") + "': " " foo.handleLayerSelectionChanged('chatStationsLayer', 'on'); " " break; " " case '" + tr("WSJTX - CQ") + "': " " foo.handleLayerSelectionChanged('wsjtxStationsLayer', 'on'); " " break; " " case '" + tr("Path") + "': " " foo.handleLayerSelectionChanged('pathLayer', 'on'); " " break; " " } " "});" "map.on('overlayremove', function(e){ " " switch (e.name) " " { " " case '" + tr("Grid") + "': " " foo.handleLayerSelectionChanged('maidenheadConfWorked', 'off'); " " break; " " case '" + tr("Gray-Line") + "': " " foo.handleLayerSelectionChanged('grayline', 'off'); " " break; " " case '" + tr("Beam") + "': " " foo.handleLayerSelectionChanged('antPathLayer', 'off'); " " break; " " case '" + tr("Aurora") + "': " " foo.handleLayerSelectionChanged('auroraLayer', 'off'); " " break; " " case '" + tr("MUF") + "': " " foo.handleLayerSelectionChanged('mufLayer', 'off'); " " break; " " case '" + tr("IBP") + "': " " foo.handleLayerSelectionChanged('IBPLayer', 'off'); " " break; " " case '" + tr("Chat") + "': " " foo.handleLayerSelectionChanged('chatStationsLayer', 'off'); " " break; " " case '" + tr("WSJTX - CQ") + "': " " foo.handleLayerSelectionChanged('wsjtxStationsLayer', 'off'); " " break; " " case '" + tr("Path") + "': " " foo.handleLayerSelectionChanged('pathLayer', 'off'); " " break; " " } " "});"; page->runJavaScript(js); } void MapWebChannelHandler::restoreLayerControlStates(QWebEnginePage *page) { FCT_IDENTIFICATION; QString js; const QStringList &keys = LogParam::getMapLayerStates(configID); for ( const QString &key : keys) { qCDebug(runtime) << "key:" << key << "value:" << LogParam::getMapLayerState(configID, key); js += ( LogParam::getMapLayerState(configID, key) ) ? QString("map.addLayer(%1);").arg(key) : QString("map.removeLayer(%1);").arg(key); } qCDebug(runtime) << js; page->runJavaScript(js); connectWebChannel(page); } QString MapWebChannelHandler::generateMapMenuJS(bool gridLayer, bool grayline, bool aurora, bool muf, bool ibp, bool antpath, bool chatStations, bool wsjtxStations, bool paths) { FCT_IDENTIFICATION; QStringList options; if ( aurora ) options << "\"" + tr("Aurora") + "\": auroraLayer"; if ( antpath ) options << "\"" + tr("Beam") + "\": antPathLayer"; if ( chatStations ) options << "\"" + tr("Chat") + "\": chatStationsLayer"; if ( gridLayer ) options << "\"" + tr("Grid") + "\": maidenheadConfWorked"; if ( grayline ) options << "\"" + tr("Gray-Line") + "\": grayline"; if ( ibp ) options << "\"" + tr("IBP") + "\": IBPLayer"; if ( muf ) options << "\"" + tr("MUF") + "\": mufLayer"; if ( wsjtxStations ) options << "\"" + tr("WSJTX - CQ") + "\": wsjtxStationsLayer"; if ( paths ) options << "\"" + tr("Path") + "\": pathLayer"; QString ret = QString("var layerControl = new L.Control.Layers(null," "{ %1 },{}).addTo(map);").arg(options.join(",")); qCDebug(runtime) << ret; return ret; } void MapWebChannelHandler::handleLayerSelectionChanged(const QVariant &data, const QVariant &state) { FCT_IDENTIFICATION; qCDebug(function_parameters) << data << state; LogParam::setMapLayerState(configID, data.toString(), (state.toString().toLower() == "on") ? true : false); } void MapWebChannelHandler::chatCallsignClicked(const QVariant &data) { FCT_IDENTIFICATION; emit chatCallsignPressed(data.toString()); } void MapWebChannelHandler::wsjtxCallsignClicked(const QVariant &data) { FCT_IDENTIFICATION; emit wsjtxCallsignPressed(data.toString()); } void MapWebChannelHandler::IBPCallsignClicked(const QVariant &callsign, const QVariant &freq) { FCT_IDENTIFICATION; emit IBPPressed(callsign.toString(), freq.toDouble()); } ================================================ FILE: ui/MapWebChannelHandler.h ================================================ #ifndef QLOG_UI_MAPWEBCHANNELHANDLER_H #define QLOG_UI_MAPWEBCHANNELHANDLER_H #include #include class MapWebChannelHandler : public QObject { Q_OBJECT public: explicit MapWebChannelHandler(const QString &configID, QObject *parent = nullptr); void restoreLayerControlStates(QWebEnginePage *page); QString generateMapMenuJS(bool gridLayer = true, bool grayline = false, bool aurora = false, bool muf = false, bool ibp = false, bool antpath = false, bool chatStations = false, bool wsjtxStations = false, bool paths = false); signals: void chatCallsignPressed(QString); void wsjtxCallsignPressed(QString); void IBPPressed(QString, double); public slots: void handleLayerSelectionChanged(const QVariant &data, const QVariant &state); void chatCallsignClicked(const QVariant &data); void wsjtxCallsignClicked(const QVariant &data); void IBPCallsignClicked(const QVariant &callsign, const QVariant &freq); private: QString configID; void connectWebChannel(QWebEnginePage *page); }; #endif // QLOG_UI_MAPWEBCHANNELHANDLER_H ================================================ FILE: ui/MapWidget.cpp ================================================ #include #include #include #include #include #include #include #include "MapWidget.h" #include "core/debug.h" #include "data/Gridsquare.h" #include "data/StationProfile.h" MODULE_IDENTIFICATION("qlog.ui.mapwidget"); MapWidget::MapWidget(QWidget *parent) : QGraphicsView(parent) { FCT_IDENTIFICATION; scene = new QGraphicsScene(this); this->setScene(scene); this->setStyleSheet("background-color: transparent;"); QPixmap pix(":/res/map/nasabluemarble.jpg"); scene->addPixmap(pix); scene->setSceneRect(pix.rect()); nightOverlay = new QGraphicsPixmapItem(); scene->addItem(nightOverlay); /*sunItem = scene->addEllipse(0, 0, sunSize, sunSize, QPen(QColor(235, 219, 52)), QBrush(QColor(235, 219, 52), Qt::SolidPattern)); */ terminatorItem = scene->addPath(QPainterPath(), QPen(QColor(100, 100, 100), 2), QBrush(QColor(0, 0, 0), Qt::SolidPattern)); this->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); redrawNightOverlay(); clear(); QTimer* timer = new QTimer(this); connect(timer, &QTimer::timeout, this, &MapWidget::redraw); timer->start(60000); } void MapWidget::clear() { FCT_IDENTIFICATION; QMutableListIterator i(items); while ( i.hasNext() ) { QGraphicsItem *item = i.next(); scene->removeItem(item); delete item; i.remove(); } Gridsquare myGrid(StationProfilesManager::instance()->getCurProfile1().locator); if ( myGrid.isValid() ) { double lat=0; double lon=0; lat = myGrid.getLatitude(); lon = myGrid.getLongitude(); drawPoint(coordToPoint(lat, lon)); } } void MapWidget::redraw() { FCT_IDENTIFICATION; redrawNightOverlay(); } void MapWidget::drawPoint(const QPoint &point) { FCT_IDENTIFICATION; qCDebug(function_parameters) << point; items << scene->addEllipse(point.x()-2, point.y()-2, 4, 4, QPen(QColor(255, 0, 0)), QBrush(QColor(255, 0, 0), Qt::SolidPattern)); } void MapWidget::drawLine(const QPoint &pointA, const QPoint &pointB) { FCT_IDENTIFICATION; qCDebug(function_parameters) << pointA << pointB; QPainterPath path; double latA, lonA, latB, lonB; double f = 0; double steps = 200.0; path.moveTo(pointA); pointToRad(pointA, latA, lonA); pointToRad(pointB, latB, lonB); double prevLon = lonA; double d = 2.0*asin(sqrt(pow(sin(latA-latB)/2, 2) + cos(latA)* cos(latB) * pow(sin((lonA-lonB)/2), 2))); for ( int i = 0; i < steps; i++ ) { double A = sin((1.0-f)*d)/sin(d); double B = sin(f*d)/sin(d); double x = A*cos(latA)*cos(lonA) + B*cos(latB)*cos(lonB); double y = A*cos(latA)*sin(lonA) + B*cos(latB)*sin(lonB); double z = A*sin(latA) + B*sin(latB); double lat = atan2(z, sqrt(x*x + y*y)); double lon = atan2(y, x); if ( qIsNaN(lat) || qIsNaN(lon)) continue; if ( qAbs(prevLon - lon) > M_PI ) { double endpoint = (prevLon > 0 ) ? M_PI : -M_PI; path.lineTo(radToPoint(lat,endpoint)); path.closeSubpath(); items << scene->addPath(QPainterPath(path), QPen(QColor(255, 0, 0)), QBrush(QColor(255, 0, 0), Qt::SolidPattern)); #if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)) path.clear(); #else /* Due to ubuntu 20.04 where qt5.12 is present */ path = QPainterPath(); #endif path.moveTo(radToPoint(lat, -1 * endpoint)); } const QPoint &p = radToPoint(lat, lon); path.lineTo(p); path.moveTo(p); prevLon = lon; f += 1.0 / steps; } path.lineTo(pointB); path.closeSubpath(); items << scene->addPath(path, QPen(QColor(255, 0, 0)), QBrush(QColor(255, 0, 0), Qt::SolidPattern)); } void MapWidget::redrawNightOverlay() { FCT_IDENTIFICATION; QDateTime current = QDateTime::currentDateTimeUtc(); int secondOfDay = (QTime(0, 0, 0).secsTo(current.time()) + 43200) % 86400; int dayOfYear = current.date().dayOfYear(); int daysInYear = QDate::isLeapYear(current.date().year()) ? 366 : 365; int longestDay = QDate(current.date().year(), 6, 21).dayOfYear(); double yearProgress = static_cast(dayOfYear-longestDay) / static_cast(daysInYear); double tilt = 23.5 * cos(2.0 * M_PI * yearProgress); double sunX = cos(2.0 * M_PI * (secondOfDay / 86400.0)); double sunY = -sin(2.0 * M_PI * (secondOfDay / 86400.0)); double sunZ = tan(2.0 * M_PI * (tilt / 360.0)); QVector3D sun(static_cast(sunX), static_cast(sunY), static_cast(sunZ)); sun.normalize(); // //double theta = acos(sunZ); //double phi = atan(sunY/sunX); //double sunLon = phi/M_PI * 180.0; //double sunLat = 90 - theta / M_PI * 180.0; //sunItem->setPos(coordToPoint(sunLat, sunLon) - QPoint(sunSize / 2, sunSize / 2)); // int maxX = static_cast(scene->width()); int maxY = static_cast(scene->height()); QImage overlay(maxX, maxY, QImage::Format_ARGB32); uchar* buffer = overlay.bits(); for ( int y = 0; y < maxY; y++ ) { double theta = M_PI * (static_cast(y) / (static_cast(maxY) - 1.0)); double posZ = cos(theta); double sinTheta = sin(theta); for ( int x = 0; x < maxX; x++ ) { double phi = 2.0 * M_PI * (static_cast(x) / (static_cast(maxX) - 1.0)) - M_PI; double posX = sinTheta * cos(phi); double posY = sinTheta * sin(phi); QVector3D pos(static_cast(posX), static_cast(posY), static_cast(posZ)); pos.normalize(); buffer[0] = 0; buffer[1] = 0; buffer[2] = 0; float ill = QVector3D::dotProduct(sun, pos); if ( ill <= -0.1f ) { buffer[3] = 255; } else if ( ill < 0.1f ) { double illd = 1.0 - (static_cast(ill) + 0.1) * 5.0; illd = pow(illd, 8); buffer[3] = static_cast(255.0 * illd); } else { buffer[3] = 0; } buffer += 4; } } QImage night(":/res/map/nasaearthlights.jpg"); QPainter painter; painter.begin(&overlay); painter.setCompositionMode(QPainter::CompositionMode_SourceAtop); painter.drawImage(0, 0, night); painter.end(); nightOverlay->setPixmap(QPixmap::fromImage(overlay)); } void MapWidget::pointToRad(const QPoint &point, double& lat, double& lon) { FCT_IDENTIFICATION; lat = M_PI / 2.0 - static_cast(point.y()) / (scene->height() - 1.0) * M_PI; lon = 2.0 * M_PI *(static_cast(point.x()) / (scene->width() - 1.0)) - M_PI; qCDebug(runtime) << point << lat << lon; } void MapWidget::pointToCoord(const QPoint &point, double& lat, double& lon) { FCT_IDENTIFICATION; qCDebug(runtime) << lat << lon; lat = 90.0 - (point.y() / scene->height()) * 180.0; lon = 360.0 * (point.x() / scene->width()) - 180.0; qCDebug(runtime) << point << lat << lon; } QPoint MapWidget::radToPoint(const double lat, const double lon) { FCT_IDENTIFICATION; if ( qIsNaN(lat) || qIsNaN(lon) ) return QPoint(); int x = static_cast((lon + M_PI) / (2.0 * M_PI) * scene->width()); int y = static_cast(scene->height() / 2.0 - (2.0 * lat / M_PI) * (scene->height() / 2.0)); qCDebug(runtime) << x << y << lat << lon; return QPoint(x, y); } QPoint MapWidget::coordToPoint(const double lat, const double lon) { FCT_IDENTIFICATION; int x = static_cast((lon + 180.0) / 360.0 * scene->width()); int y = static_cast(scene->height() / 2.0 - (lat / 90.0) * (scene->height() / 2.0)); qCDebug(runtime) << lat << lon << x << y; return QPoint(x, y); } void MapWidget::setTarget(double lat, double lon) { FCT_IDENTIFICATION; qCDebug(function_parameters) << lat << lon; clear(); if ( qIsNaN(lat) || qIsNaN(lon) ) return; Gridsquare myGrid(StationProfilesManager::instance()->getCurProfile1().locator); QPoint point = coordToPoint(lat, lon); drawPoint(point); if ( myGrid.isValid() ) { double qthLat = myGrid.getLatitude(); double qthLon = myGrid.getLongitude(); qCDebug(runtime) << "My QTH" << qthLat << qthLon; QPoint qth = coordToPoint(qthLat, qthLon); drawPoint(qth); drawLine(qth, point); } } void MapWidget::showEvent(QShowEvent* event) { FCT_IDENTIFICATION; this->fitInView(scene->sceneRect(), Qt::KeepAspectRatio); QWidget::showEvent(event); } void MapWidget::resizeEvent(QResizeEvent* event) { FCT_IDENTIFICATION; this->fitInView(scene->sceneRect(), Qt::KeepAspectRatio); QWidget::resizeEvent(event); } MapWidget::~MapWidget() { } ================================================ FILE: ui/MapWidget.h ================================================ #ifndef QLOG_UI_MAPWIDGET_H #define QLOG_UI_MAPWIDGET_H #include #include #include namespace Ui { class MapWidget; } class MapWidget : public QGraphicsView { Q_OBJECT public: explicit MapWidget(QWidget* parent = nullptr); ~MapWidget(); public slots: void setTarget(double lat, double lon); void clear(); void redraw(); protected: void showEvent(QShowEvent* event); void resizeEvent(QResizeEvent* event); private: void redrawNightOverlay(); void drawPoint(const QPoint &point); void drawLine(const QPoint &pointA, const QPoint &pointB); void pointToRad(const QPoint &point, double& lat, double& lon); void pointToCoord(const QPoint &point, double& lat, double& lon); QPoint radToPoint(const double lat, const double lon); QPoint coordToPoint(const double lat, const double lon); int sunSize = 20; QGraphicsPixmapItem* nightOverlay; QList items; QGraphicsEllipseItem* sunItem; QGraphicsPathItem* terminatorItem; QGraphicsScene* scene; }; #endif // QLOG_UI_MAPWIDGET_H ================================================ FILE: ui/ModeSelectionController.cpp ================================================ #include #include #include "ModeSelectionController.h" #include "core/debug.h" MODULE_IDENTIFICATION("qlog.ui.modeselectioncontroller"); ModeSelectionController::ModeSelectionController(QComboBox *modeCombo, QComboBox *submodeCombo, bool enabledOnly, bool selectFirstSubmode, bool hideSubmodeWhenEmpty, bool fallbackToFirstMode, QObject *parent) : QObject(parent), modeCombo(modeCombo), submodeCombo(submodeCombo), modeModel(nullptr), submodeModel(nullptr), enabledOnly(enabledOnly), selectFirstSubmode(selectFirstSubmode), hideSubmodeWhenEmpty(hideSubmodeWhenEmpty), fallbackToFirstMode(fallbackToFirstMode), nameColumn(-1) { FCT_IDENTIFICATION; if ( !modeCombo || !submodeCombo ) return; QSignalBlocker modeBlocker(modeCombo); QSignalBlocker submodeBlocker(submodeCombo); modeModel = new QSqlTableModel(this); modeModel->setTable("modes"); if ( enabledOnly ) modeModel->setFilter("enabled = true"); nameColumn = modeModel->fieldIndex("name"); modeModel->setSort(nameColumn, Qt::AscendingOrder); modeModel->select(); modeCombo->setModel(modeModel); modeCombo->setModelColumn(nameColumn); submodeModel = new QStringListModel(this); submodeCombo->setModel(submodeModel); } void ModeSelectionController::applyCurrentMode() { FCT_IDENTIFICATION; if ( !modeModel || !submodeModel || !modeCombo || !submodeCombo ) return; if ( modeCombo->currentIndex() < 0 ) { applySubmodes(QStringList()); emit defaultReportChanged(QString()); return; } const QSqlRecord record = currentRecord(); if ( record.isEmpty() ) { applySubmodes(QStringList()); emit defaultReportChanged(QString()); return; } const QString submodes = record.value("submodes").toString(); const QStringList submodeList = QJsonDocument::fromJson(submodes.toUtf8()).toVariant().toStringList(); applySubmodes(submodeList); emit defaultReportChanged(record.value("rprt").toString()); } void ModeSelectionController::reloadModel() { FCT_IDENTIFICATION; if ( !modeModel || !modeCombo || !submodeCombo ) return; const QString prevMode = modeCombo->currentText(); const QString prevSubmode = submodeCombo->currentText(); QSignalBlocker modeBlocker(modeCombo); QSignalBlocker submodeBlocker(submodeCombo); modeModel->select(); int modeIndex = modeCombo->findText(prevMode); if ( modeIndex >= 0 ) modeCombo->setCurrentIndex(modeIndex); else if ( fallbackToFirstMode && modeCombo->count() > 0 ) modeCombo->setCurrentIndex(0); else modeCombo->setCurrentIndex(-1); applyCurrentMode(); if ( !prevSubmode.isEmpty() ) { int submodeIndex = submodeCombo->findText(prevSubmode); if ( submodeIndex >= 0 ) submodeCombo->setCurrentIndex(submodeIndex); } } void ModeSelectionController::applySubmodes(const QStringList &submodeList) { FCT_IDENTIFICATION; QSignalBlocker blocker(submodeCombo); if ( submodeList.isEmpty() ) { submodeModel->setStringList(QStringList()); if ( hideSubmodeWhenEmpty ) submodeCombo->setVisible(false); else submodeCombo->setEnabled(false); if ( selectFirstSubmode ) submodeCombo->setCurrentIndex(-1); return; } QStringList list = submodeList; list.prepend(""); submodeModel->setStringList(list); if ( hideSubmodeWhenEmpty ) submodeCombo->setVisible(true); else submodeCombo->setEnabled(true); if ( selectFirstSubmode ) submodeCombo->setCurrentIndex(1); } QSqlRecord ModeSelectionController::currentRecord() const { FCT_IDENTIFICATION; if ( !modeModel ) return QSqlRecord(); return modeModel->record(modeCombo->currentIndex()); } ================================================ FILE: ui/ModeSelectionController.h ================================================ #ifndef QLOG_UI_MODESELECTIONCONTROLLER_H #define QLOG_UI_MODESELECTIONCONTROLLER_H #include #include #include #include #include class ModeSelectionController : public QObject { Q_OBJECT public: ModeSelectionController(QComboBox *modeCombo, QComboBox *submodeCombo, bool enabledOnly, bool selectFirstSubmode, bool hideSubmodeWhenEmpty, bool fallbackToFirstMode, QObject *parent = nullptr); void applyCurrentMode(); void reloadModel(); signals: void defaultReportChanged(const QString &report); private: void applySubmodes(const QStringList &submodeList); QSqlRecord currentRecord() const; QComboBox *modeCombo; QComboBox *submodeCombo; QSqlTableModel *modeModel; QStringListModel *submodeModel; bool enabledOnly; bool selectFirstSubmode; bool hideSubmodeWhenEmpty; bool fallbackToFirstMode; int nameColumn; }; #endif // QLOG_UI_MODESELECTIONCONTROLLER_H ================================================ FILE: ui/NewContactWidget.cpp ================================================ #include #include #include #include #include #include #include #include #include #include #include #include #include "rig/Rig.h" #include "rig/macros.h" #include "rotator/Rotator.h" #include "NewContactWidget.h" #include "ui_NewContactWidget.h" #include "core/debug.h" #include "data/Gridsquare.h" #include "data/StationProfile.h" #include "data/RigProfile.h" #include "data/AntProfile.h" #include "data/CWKeyProfile.h" #include "data/Data.h" #include "data/Callsign.h" #include "core/PropConditions.h" #include "core/MembershipQE.h" #include "logformat/AdiFormat.h" #include "data/MainLayoutProfile.h" #include "models/LogbookModel.h" #include "data/BandPlan.h" #include "ui/ModeSelectionController.h" #include "core/LogParam.h" #include "data/ActivityProfile.h" #include "core/LogParam.h" #include "core/PotaQE.h" #include "core/WsjtxUDPReceiver.h" MODULE_IDENTIFICATION("qlog.ui.newcontactwidget"); NewContactWidget::NewContactWidget(QWidget *parent) : QWidget(parent), rig(Rig::instance()), dxDistance(qQNaN()), contactTimer(new QTimer(this)), ui(new Ui::NewContactWidget), uiDynamic(new NewContactDynamicWidgets(true, this)), prop_cond(nullptr), countyCompleter(nullptr), QSOFreq(0.0), QSOTxFreq(0.0), prevQSOTxFreq(0.0), bandwidthFilter(BANDWIDTH_UNKNOWN), rigOnline(false), isManualEnterMode(false), rigSplitEnabled(false), callbookSearchPaused(false), modeController(nullptr) { FCT_IDENTIFICATION; ui->setupUi(this); // tab pane with QSO fields - expand & collapse tabCollapseBtn = new QToolButton(this); QIcon toggleIcon; toggleIcon.addPixmap(QPixmap(":/icons/baseline-play_down-24px.svg"), QIcon::Normal, QIcon::On); toggleIcon.addPixmap(QPixmap(":/icons/baseline-play_arrow-24px.svg"), QIcon::Normal, QIcon::Off); tabCollapseBtn->setIcon(toggleIcon); tabCollapseBtn->setCheckable(true); tabCollapseBtn->setToolTip(tr("Expand/Collapse")); tabCollapseBtn->setFocusPolicy(Qt::NoFocus); ui->qsoTabs->setCornerWidget(tabCollapseBtn, Qt::TopLeftCorner); connect(tabCollapseBtn, &QAbstractButton::toggled, this, &NewContactWidget::tabsExpandCollapse); connect(ui->qsoTabs, &QTabWidget::tabBarClicked, this, [this](const int) { // force expand if a tab is activated tabCollapseBtn->setChecked(true); }); ui->rstRcvdEdit->spaceForbidden(true); ui->rstSentEdit->spaceForbidden(true); ui->dupeLabel->setVisible(false); setupCustomUi(); uiDynamic->contestIDEdit->setText(LogParam::getContestID()); CWKeyProfilesManager::instance(); //TODO remove, make it better - workaround ui->dateEdit->setDisplayFormat(locale.formatDateShortWithYYYY()); ui->timeOnEdit->setDisplayFormat(locale.formatTimeLongWithoutTZ()); /**************************/ /* QSL Send Combo Content */ /**************************/ /* do no use Data::qslSentBox for it because * Data::qslSentBox has a different ordering. * Ordering below is optimized for a new Contact Widget only */ ui->qslSentBox->addItem(tr("No"), QVariant("N")); ui->qslSentBox->addItem(tr("Yes"), QVariant("Y")); ui->qslSentBox->addItem(tr("Requested"), QVariant("R")); ui->qslSentBox->addItem(tr("Queued"), QVariant("Q")); ui->qslSentBox->addItem(tr("Ignored"), QVariant("I")); /**************************/ /* QSL Send LoTW Combo Content */ /**************************/ /* do no use Data::eQSLSentBox for it because * Data::eQSLSentBox has a different ordering. * Ordering below is optimized for a new Contact Widget only */ ui->eQSLSentBox->addItem(tr("No"), QVariant("N")); ui->eQSLSentBox->addItem(tr("Yes"), QVariant("Y")); ui->eQSLSentBox->addItem(tr("Requested"), QVariant("R")); ui->eQSLSentBox->addItem(tr("Queued"), QVariant("Q")); ui->eQSLSentBox->addItem(tr("Ignored"), QVariant("I")); /**************************/ /* QSL Send eQSL Combo Content */ /**************************/ /* do no use Data::lotwQslSentBox for it because * Data::lotwQslSentBox has a different ordering. * Ordering below is optimized for a new Contact Widget only */ ui->lotwQslSentBox->addItem(tr("No"), QVariant("N")); ui->lotwQslSentBox->addItem(tr("Yes"), QVariant("Y")); ui->lotwQslSentBox->addItem(tr("Requested"), QVariant("R")); ui->lotwQslSentBox->addItem(tr("Queued"), QVariant("Q")); ui->lotwQslSentBox->addItem(tr("Ignored"), QVariant("I")); /*****************************/ /* QSL SendVia Combo Content */ /*****************************/ /* do no use Data::qslSentViaBox for it because * Data::qslSentViaBox has a different ordering. * Ordering below is optimized for a new Contact Widget only */ ui->qslSentViaBox->addItem("", QVariant("")); ui->qslSentViaBox->addItem(tr("Bureau"), QVariant("B")); ui->qslSentViaBox->addItem(tr("Direct"), QVariant("D")); ui->qslSentViaBox->addItem(tr("Electronic"), QVariant("E")); /****************/ /* Rig Profile */ /****************/ QStringListModel* rigModel = new QStringListModel(this); ui->rigEdit->setModel(rigModel); refreshRigProfileCombo(); /****************/ /* Ant Profile */ /****************/ QStringListModel* antModel = new QStringListModel(this); ui->antennaEdit->setModel(antModel); refreshAntProfileCombo(); /*******************/ /* Station Profile */ /*******************/ QStringListModel* stationProfilesModel = new QStringListModel(this); ui->stationProfileCombo->setModel(stationProfilesModel); refreshStationProfileCombo(); /******************/ /* Submodes Combo */ /******************/ modeController = new ModeSelectionController(ui->modeEdit, ui->submodeEdit, true, true, true, true, this); connect(modeController, &ModeSelectionController::defaultReportChanged, this, [this](const QString &report) { defaultReport = report; setDefaultReport(); }); modeController->applyCurrentMode(); /**********************/ /* Propagation Combo */ /**********************/ QStringListModel* propagationModeModel = new QStringListModel(Data::instance()->propagationModesList(), this); ui->propagationModeEdit->setModel(propagationModeModel); /***************/ /* Completers */ /***************/ wwffCompleter = new QCompleter(Data::instance()->wwffIDList(), this); wwffCompleter->setCaseSensitivity(Qt::CaseInsensitive); wwffCompleter->setFilterMode(Qt::MatchStartsWith); wwffCompleter->setModelSorting(QCompleter::CaseSensitivelySortedModel); uiDynamic->wwffEdit->setCompleter(nullptr); potaCompleter = new MultiselectCompleter(Data::instance()->potaIDList(), this); potaCompleter->setCaseSensitivity(Qt::CaseInsensitive); potaCompleter->setFilterMode(Qt::MatchStartsWith); potaCompleter->setModelSorting(QCompleter::CaseSensitivelySortedModel); uiDynamic->potaEdit->setCompleter(nullptr); sotaCompleter = new QCompleter(Data::instance()->sotaIDList(), this); sotaCompleter->setCaseSensitivity(Qt::CaseInsensitive); sotaCompleter->setFilterMode(Qt::MatchStartsWith); sotaCompleter->setModelSorting(QCompleter::CaseSensitivelySortedModel); uiDynamic->sotaEdit->setCompleter(nullptr); uiDynamic->countyEdit->setCompleter(nullptr); sigCompleter = new QCompleter(uiDynamic->sigEdit); sigCompleter->setCaseSensitivity(Qt::CaseInsensitive); sigCompleter->setFilterMode(Qt::MatchStartsWith); sigCompleter->setModelSorting(QCompleter::CaseSensitivelySortedModel); uiDynamic->sigEdit->setCompleter(sigCompleter); contestCompleter = new QCompleter(uiDynamic->contestIDEdit); contestCompleter->setCaseSensitivity(Qt::CaseInsensitive); contestCompleter->setFilterMode(Qt::MatchStartsWith); uiDynamic->contestIDEdit->setCompleter(contestCompleter); /**************/ /* CONNECTs */ /**************/ connect(&callbookManager, &CallbookManager::callsignResult, this, &NewContactWidget::setCallbookFields); connect(&callbookManager, &CallbookManager::loginFailed, this, [this](const QString &callbookString) { QMessageBox::critical(this, tr("QLog Error"), callbookString + " " + tr("Callbook login failed")); setCallbookStatusEnabled(callbookManager.isActive()); }); connect(contactTimer, &QTimer::timeout, this, &NewContactWidget::updateTimeOff); connect(MembershipQE::instance(), &MembershipQE::clubStatusResult, this, &NewContactWidget::setMembershipList); /******************************/ /* CONNECTs DYNAMIC WIDGETS */ /******************************/ connect(uiDynamic->gridEdit, &QLineEdit::textChanged, this, &NewContactWidget::gridChanged); connect(uiDynamic->potaEdit, &QLineEdit::editingFinished, this, &NewContactWidget::potaEditFinished); connect(uiDynamic->potaEdit, &QLineEdit::textChanged, this, &NewContactWidget::potaChanged); connect(uiDynamic->sotaEdit, &QLineEdit::editingFinished, this, &NewContactWidget::sotaEditFinished); connect(uiDynamic->sotaEdit, &QLineEdit::textChanged, this, &NewContactWidget::sotaChanged); connect(uiDynamic->wwffEdit, &QLineEdit::editingFinished, this, &NewContactWidget::wwffEditFinished); connect(uiDynamic->wwffEdit, &QLineEdit::textChanged, this, &NewContactWidget::wwffChanged); connect(uiDynamic->satNameEdit, &QLineEdit::textChanged, this, &NewContactWidget::satNameChanged); connect(uiDynamic->sigEdit, &NewContactEditLine::focusIn, this, &NewContactWidget::refreshSIGCompleter); connect(uiDynamic->contestIDEdit, &NewContactEditLine::focusIn, this, &NewContactWidget::refreshContestCompleter); connect(uiDynamic->contestIDEdit, &NewContactEditLine::textEdited, this, &NewContactWidget::setContestFieldsState); ui->rstSentEdit->installEventFilter(this); ui->rstRcvdEdit->installEventFilter(this); /********************/ /* Local SHORTCUTs */ /********************/ new QShortcut(QKeySequence(Qt::Key_Escape), this, SLOT(resetContact()), nullptr, Qt::ApplicationShortcut); new QShortcut(QKeySequence(Qt::Key_F10), this, SLOT(saveContact()), nullptr, Qt::ApplicationShortcut); new QShortcut(QKeySequence(Qt::Key_F9), this, SLOT(stopContactTimer()), nullptr, Qt::ApplicationShortcut); new QShortcut(QKeySequence(Qt::Key_F8), this, SLOT(startContactTimer()), nullptr, Qt::ApplicationShortcut); /****************************/ /* Set Visiable for widgets */ /****************************/ ui->freqTXEdit->setVisible(false); ui->bandTXLabel->setVisible(false); ui->freqRXLabel->setVisible(false); ui->freqTXLabel->setVisible(false); /***********************/ /* Read Widget Setting */ /***********************/ readWidgetSettings(); /*************************/ /* Reload Global Setting */ /*************************/ readGlobalSettings(); /**********************/ /* Clear Contact Form */ /**********************/ resetContact(); /************************/ /* Connect Field Change */ /* signals to determine */ /* whether form changed */ /************************/ connectFieldChanged(); // SQL query returns two QSOs. The first one is the last QSO with Base Callsign // and the second one is the last QSO for the Callsign from a portable QTH. isPrevQSOExactMatchQuery = prevQSOExactMatchQuery.prepare(QLatin1String("SELECT " " callsign, " " name_intl, " " qth_intl, " " gridsquare, " " notes_intl, " " email, " " web , " " darc_dok " "FROM contacts " "WHERE callsign = :exactCallsign " " AND gridsquare LIKE :grid " "ORDER BY start_time DESC " "LIMIT 1 ")); if ( !isPrevQSOExactMatchQuery) qWarning() << "Cannot prepare prevQSOExactMatchQuery statement"; isPrevQSOBaseCallMatchQuery = prevQSOBaseCallMatchQuery.prepare(QLatin1String("SELECT " " callsign, " " name_intl, " " qth_intl, " " gridsquare, " " notes_intl, " " email, " " web , " " darc_dok " "FROM contacts c " " INNER JOIN contacts_autovalue a ON c.id = a.contactid " "WHERE a.base_callsign = :partialCallsign " "ORDER BY start_time DESC " "LIMIT 1")); if ( !isPrevQSOBaseCallMatchQuery) qWarning() << "Cannot prepare prevQSOBaseCallMatchQuery statement"; setContestFieldsState(); } void NewContactWidget::setComboBaseData(QComboBox *combo, const QString &data) { FCT_IDENTIFICATION; int index = combo->findData(data); if ( index != -1 ) { combo->setCurrentIndex(index); } } void NewContactWidget::queryMemberList() { FCT_IDENTIFICATION; if ( callsign.size() >= 3 ) { MembershipQE::instance()->asyncQueryDetails(callsign, ui->bandRXLabel->text(), ui->modeEdit->currentText()); } } void NewContactWidget::readWidgetSettings() { FCT_IDENTIFICATION; realRigFreq = LogParam::getNewContactFreq(); ui->modeEdit->setCurrentText(LogParam::getNewContactMode()); ui->submodeEdit->setCurrentText(LogParam::getNewContactSubMode()); uiDynamic->powerEdit->setValue(LogParam::getNewContactPower()); ui->qsoTabs->setCurrentIndex(LogParam::getNewContactTabIndex()); setComboBaseData(ui->qslSentBox, LogParam::getNewContactQSLSent()); setComboBaseData(ui->lotwQslSentBox, LogParam::getNewContactLoTWQSLSent()); setComboBaseData(ui->eQSLSentBox, LogParam::getNewContactEQSLWQSLSent()); setComboBaseData(ui->qslSentViaBox, LogParam::getNewContactQSLVia()); ui->propagationModeEdit->setCurrentText(Data::instance()->propagationModeIDToText(LogParam::getNewContactPropMode())); tabCollapseBtn->setChecked(LogParam::getNewContactTabsExpanded()); tabsExpandCollapse(); } void NewContactWidget::writeWidgetSetting() { FCT_IDENTIFICATION; LogParam::setNewContactFreq(realRigFreq); LogParam::setNewContactMode(ui->modeEdit->currentText()); LogParam::setNewContactSubMode(ui->submodeEdit->currentText()); LogParam::setNewContactPower(uiDynamic->powerEdit->value()); LogParam::setNewContactTabIndex(ui->qsoTabs->currentIndex()); LogParam::setNewContactQSLSent(ui->qslSentBox->itemData(ui->qslSentBox->currentIndex()).toString()); LogParam::setNewContactLoTWQSLSent(ui->lotwQslSentBox->itemData(ui->lotwQslSentBox->currentIndex()).toString()); LogParam::setNewContactEQSLQSLSent(ui->eQSLSentBox->itemData(ui->eQSLSentBox->currentIndex()).toString()); LogParam::setNewContactQSLVia(ui->qslSentViaBox->itemData(ui->qslSentViaBox->currentIndex()).toString()); LogParam::setNewContactPropMode(Data::instance()->propagationModeTextToID(ui->propagationModeEdit->currentText())); LogParam::setNewContactTabsExpanded(tabCollapseBtn->isChecked()); } /* function read global setting, called when starting or when Setting is reloaded */ void NewContactWidget::readGlobalSettings() { FCT_IDENTIFICATION; /*************************/ /* Refresh mode combobox */ /*************************/ modeController->reloadModel(); /********************/ /* Reload Callbooks */ /********************/ callbookManager.initCallbooks(); setCallbookStatusEnabled(callbookManager.isActive()); /***********************************/ /* Refresh all Profiles Comboboxes */ /***********************************/ refreshStationProfileCombo(); refreshRigProfileCombo(); refreshAntProfileCombo(); // recalculate all stats setDxccInfo(ui->callsignEdit->text()); ui->freqRXEdit->loadBands(); ui->freqTXEdit->loadBands(); updatePartnerLocTime(); ui->dateEdit->setDisplayFormat(locale.formatDateShortWithYYYY()); ui->timeOnEdit->setDisplayFormat(locale.formatTimeLongWithoutTZ()); } /* function is called when an operator change Callsign Edit */ void NewContactWidget::handleCallsignFromUser() { FCT_IDENTIFICATION; QString newCallsign = ui->callsignEdit->text().toUpper(); qCDebug(runtime) << "newcallsign " << newCallsign << " old callsign " << callsign; // if operator presses a spacebar at the end of callsign then begins QSO and skips RST Fields if ( newCallsign.endsWith(" ")) { newCallsign.chop(1); ui->callsignEdit->blockSignals(true); ui->callsignEdit->setText(newCallsign); ui->callsignEdit->blockSignals(false); if ( newCallsign.isEmpty() ) return; QLineEdit *nextLineEdit = qobject_cast(ui->rstRcvdEdit->nextInFocusChain()); if ( nextLineEdit ) { nextLineEdit->setFocus(); startContactTimer(); } } if ( newCallsign == callsign ) return; callsign = newCallsign; clearCallbookQueryFields(); clearMemberQueryFields(); if ( callsign.isEmpty() ) { setDxccInfo(DxccEntity()); updateTime(); stopContactTimer(); } else { checkDupe(); useNearestSpotInfo(callsign); setDxccInfo(callsign); if ( callsign.length() >= 3 ) { queryPota(); useFieldsFromPrevQSO(callsign); } } emit callsignChanged(callsign); } /* function is called when Callsign Edit is finished - example pressed enter */ /* if callsign is entered then QLog call callbook query */ void NewContactWidget::finalizeCallsignEdit() { FCT_IDENTIFICATION; if ( callsign.size() >= 3 ) { queryMemberList(); if ( !callbookSearchPaused ) callbookManager.queryCallsign(callsign); } } void NewContactWidget::setDxccInfo(const DxccEntity &curr) { FCT_IDENTIFICATION; dxccEntity = curr; if ( dxccEntity.dxcc ) { ui->dxccInfo->setText(QCoreApplication::translate("DBStrings", dxccEntity.country.toUtf8().constData())); uiDynamic->cqzEdit->setText(QString::number(dxccEntity.cqz)); uiDynamic->ituEdit->setText(QString::number(dxccEntity.ituz)); updateCoordinates(dxccEntity.latlon[0], dxccEntity.latlon[1], COORD_DXCC); ui->dxccTableWidget->setDxcc(dxccEntity.dxcc, BandPlan::freq2Band(ui->freqTXEdit->value())); ui->stationTableWidget->setDxCallsign(ui->callsignEdit->text(), BandPlan::freq2Band(ui->freqTXEdit->value())); uiDynamic->contEdit->setCurrentText(dxccEntity.cont); ui->flagView->setPixmap((!dxccEntity.flag.isEmpty() ) ? QPixmap(QString(":/flags/64/%1.png").arg(dxccEntity.flag)) : QPixmap() ); updateDxccStatus(); updateCountyCompleter(dxccEntity.dxcc); } else { ui->dxccInfo->setText(" "); uiDynamic->cqzEdit->clear(); uiDynamic->ituEdit->clear(); clearCoordinates(); ui->dxccTableWidget->clear(); ui->stationTableWidget->clear(); uiDynamic->contEdit->setCurrentText(""); ui->flagView->setPixmap(QPixmap()); ui->dxccStatus->clear(); emit newTarget(qQNaN(), qQNaN()); updateCountyCompleter(0); } } void NewContactWidget::setDxccInfo(const QString &callsign) { FCT_IDENTIFICATION; qCDebug(function_parameters) << callsign; DxccEntity entity; if ( isManualEnterMode ) { const QDateTime &qsoDt = QDateTime(ui->dateEdit->date(), ui->timeOnEdit->time(), QTimeZone::utc()); entity = Data::instance()->lookupDxccClublog(callsign, qsoDt); } else entity = Data::instance()->lookupDxcc(callsign.toUpper()); setDxccInfo(entity); } void NewContactWidget::updateCountyCompleter(int dxcc) { FCT_IDENTIFICATION; uiDynamic->countyEdit->setCompleter(nullptr); delete countyCompleter; countyCompleter = ( dxcc != 0 ) ? Data::createCountyCompleter(dxcc, this) : nullptr; uiDynamic->countyEdit->setCompleter(countyCompleter); } void NewContactWidget::useFieldsFromPrevQSO(const QString &callsign, const QString &grid) { FCT_IDENTIFICATION; qCDebug(function_parameters) << callsign; if ( !isPrevQSOExactMatchQuery || !isPrevQSOBaseCallMatchQuery) return; const Callsign enteredCallsign(callsign); if ( !enteredCallsign.isValid() ) { emit filterCallsign(QString()); return; } const QString &baseCallsign = enteredCallsign.getBase(); // search the base_callsign prevQSOExactMatchQuery.bindValue(":exactCallsign", baseCallsign); prevQSOExactMatchQuery.bindValue(":grid", grid + "%"); if ( !prevQSOExactMatchQuery.exec() ) { qWarning() << "Cannot execute statement" << prevQSOExactMatchQuery.lastError(); emit filterCallsign(QString()); return; } if ( prevQSOExactMatchQuery.next() ) { // callsign match the base callsign - full info available if ( enteredCallsign.getHostPrefix().isEmpty() && enteredCallsign.getSuffix().isEmpty() ) { // entered callsign is base callsign - no portable QTH. Get all fields from // previous QSO uiDynamic->qthEdit->setText(prevQSOExactMatchQuery.value("qth_intl").toString()); uiDynamic->gridEdit->setText(prevQSOExactMatchQuery.value("gridsquare").toString()); uiDynamic->dokEdit->setText(prevQSOExactMatchQuery.value("darc_dok").toString()); } uiDynamic->nameEdit->setText(prevQSOExactMatchQuery.value("name_intl").toString()); ui->noteEdit->insertPlainText(prevQSOExactMatchQuery.value("notes_intl").toString()); uiDynamic->emailEdit->setText(prevQSOExactMatchQuery.value("email").toString()); uiDynamic->urlEdit->setText(prevQSOExactMatchQuery.value("web").toString()); emit filterCallsign(baseCallsign); } else { //exact match not found prevQSOBaseCallMatchQuery.bindValue(":partialCallsign", baseCallsign); if ( !prevQSOBaseCallMatchQuery.exec() ) { qWarning() << "Cannot execute statement2" << prevQSOBaseCallMatchQuery.lastError(); emit filterCallsign(QString()); return; } if ( prevQSOBaseCallMatchQuery.next() ) { // partial informaion available uiDynamic->nameEdit->setText(prevQSOBaseCallMatchQuery.value("name_intl").toString()); ui->noteEdit->insertPlainText(prevQSOBaseCallMatchQuery.value("notes_intl").toString()); uiDynamic->emailEdit->setText(prevQSOBaseCallMatchQuery.value("email").toString()); uiDynamic->urlEdit->setText(prevQSOBaseCallMatchQuery.value("web").toString()); emit filterCallsign(baseCallsign); } else { //callsign not found qCDebug(runtime) << "Callsign not match in the Logbook"; emit filterCallsign(QString()); } } } /* function handles a response from Callbook classes */ void NewContactWidget::setCallbookFields(const CallbookResponseData& data) { FCT_IDENTIFICATION; if ( data.call != callsign ) return; /* not filled or not fully filled then update it */ const QString fnamelname = QString("%1 %2").arg(data.fname, data.lname); if ( uiDynamic->nameEdit->text().isEmpty() || data.name_fmt.contains(uiDynamic->nameEdit->text()) || fnamelname.contains(uiDynamic->nameEdit->text()) || data.nick.contains(uiDynamic->nameEdit->text()) ) { QString name = data.name_fmt; if ( name.isEmpty() ) name = ( data.fname.isEmpty() && data.lname.isEmpty() ) ? data.nick : fnamelname; uiDynamic->nameEdit->setText(name); } if ( uiDynamic->gridEdit->text().isEmpty() || data.gridsquare.contains(uiDynamic->gridEdit->text()) ) uiDynamic->gridEdit->setText(data.gridsquare); if ( uiDynamic->qthEdit->text().isEmpty() || data.qth.contains(uiDynamic->qthEdit->text())) uiDynamic->qthEdit->setText(data.qth); if ( uiDynamic->dokEdit->text().isEmpty() ) uiDynamic->dokEdit->setText(data.dok); if ( uiDynamic->iotaEdit->text().isEmpty() ) uiDynamic->iotaEdit->setText(data.iota); if ( uiDynamic->emailEdit->text().isEmpty() ) uiDynamic->emailEdit->setText(data.email); if ( uiDynamic->countyEdit->text().isEmpty() ) uiDynamic->countyEdit->setText(data.county); if ( ui->qslViaEdit->text().isEmpty() ) ui->qslViaEdit->setText(data.qsl_via); if ( uiDynamic->urlEdit->text().isEmpty() ) uiDynamic->urlEdit->setText(data.url); if ( uiDynamic->stateEdit->text().isEmpty() ) uiDynamic->stateEdit->setText(data.us_state); if ( data.eqsl == "Y" ) ui->eqslLabel->setText("eQSL"); if ( data.lotw == "Y" ) ui->lotwLabel->setText("LoTW"); if ( !data.dxcc.isEmpty() ) { int callbookDXCC = data.dxcc.toInt(); if ( callbookDXCC != 0 && callbookDXCC != dxccEntity.dxcc ) { qCDebug(runtime) << "Received different DXCC Info" << data.dxcc << dxccEntity.dxcc; setDxccInfo(Data::instance()->lookupDxccID(callbookDXCC)); } } // always replace cqz/itu if ( !data.ituz.isEmpty() ) uiDynamic->ituEdit->setText(data.ituz); if ( !data.cqz.isEmpty() ) uiDynamic->cqzEdit->setText(data.cqz); emit callboolImageUrl(data.image_url); lastCallbookQueryData = data; } void NewContactWidget::setMembershipList(const QString &in_callsign, QMap data) { FCT_IDENTIFICATION; if ( in_callsign != callsign ) return; QString memberText; QMapIterator clubs(data); QPalette palette; while ( clubs.hasNext() ) { clubs.next(); const QColor &color = Data::statusToColor(static_cast(clubs.value().status), false, palette.color(QPalette::Text)); //"Hello World" memberText.append(QString("%2   ").arg(Data::colorToHTMLColor(color), clubs.key())); if ( clubs.key().toUpper() == "SKCC" && uiDynamic->skccEdit->text().isEmpty() && !clubs.value().membershipID.isEmpty() ) { uiDynamic->skccEdit->setText(clubs.value().membershipID); } // Currently UKSMG does not provide Member# - I leave it - maybe UKSMG will start providing MemberIDs. if ( clubs.key().toUpper() == "UKSMG" && uiDynamic->uksmgEdit->text().isEmpty() && !clubs.value().membershipID.isEmpty() ) { uiDynamic->uksmgEdit->setText(clubs.value().membershipID); } if ( clubs.key().toUpper() == "FISTS" && uiDynamic->fistsEdit->text().isEmpty() && !clubs.value().membershipID.isEmpty() ) { uiDynamic->fistsEdit->setText(clubs.value().membershipID); } } ui->memberListLabel->setText(memberText); } /* function just refresh Station Profile Combo */ void NewContactWidget::refreshStationProfileCombo() { FCT_IDENTIFICATION; ui->stationProfileCombo->blockSignals(true); QStringList currProfiles = StationProfilesManager::instance()->profileNameList(); QStringListModel* model = dynamic_cast(ui->stationProfileCombo->model()); model->setStringList(currProfiles); if ( StationProfilesManager::instance()->getCurProfile1().profileName.isEmpty() && currProfiles.count() > 0 ) { /* changing profile from empty to something */ ui->stationProfileCombo->setCurrentText(currProfiles.first()); stationProfileComboChanged(currProfiles.first()); } else { /* no profile change, just refresh the combo and preserve current profile */ ui->stationProfileCombo->setCurrentText(StationProfilesManager::instance()->getCurProfile1().profileName); } setDxccInfo(ui->callsignEdit->text()); updateDxccStatus(); ui->stationProfileCombo->blockSignals(false); } /* function just refresh Rig Profile Combo */ void NewContactWidget::refreshRigProfileCombo() { FCT_IDENTIFICATION; ui->rigEdit->blockSignals(true); QStringList currProfiles = RigProfilesManager::instance()->profileNameList(); QString currentText = ui->rigEdit->currentText(); QStringListModel* model = dynamic_cast(ui->rigEdit->model()); model->setStringList(currProfiles); if ( RigProfilesManager::instance()->getCurProfile1().profileName.isEmpty() && currProfiles.count() > 0 ) { /* changing profile from empty to something */ currentText = currProfiles.first(); ui->rigEdit->setCurrentText(currentText); rigProfileComboChanged(currProfiles.first()); } else { /* no profile change, just refresh the combo and preserve current profile */ currentText = (isManualEnterMode ) ? currentText : RigProfilesManager::instance()->getCurProfile1().profileName; ui->rigEdit->setCurrentText(currentText); //do not call rigProfileComboChanged because it did not change } ui->rigEdit->blockSignals(false); if ( isManualEnterMode ) return; __changeFrequency(VFO1, realRigFreq, realRigFreq + RigProfilesManager::instance()->getProfile(currentText).ritOffset, realRigFreq + RigProfilesManager::instance()->getProfile(currentText).xitOffset); uiDynamic->powerEdit->setValue(RigProfilesManager::instance()->getProfile(currentText).defaultPWR); } void NewContactWidget::refreshAntProfileCombo() { FCT_IDENTIFICATION; ui->antennaEdit->blockSignals(true); QStringList currProfiles = AntProfilesManager::instance()->profileNameList(); QStringListModel* model = dynamic_cast(ui->antennaEdit->model()); model->setStringList(currProfiles); if ( AntProfilesManager::instance()->getCurProfile1().profileName.isEmpty() && currProfiles.count() > 0 ) { /* changing profile from empty to something */ ui->antennaEdit->setCurrentText(currProfiles.first()); antProfileComboChanged(currProfiles.first()); } else { /* no profile change, just refresh the combo and preserve current profile */ ui->antennaEdit->setCurrentText(AntProfilesManager::instance()->getCurProfile1().profileName); } ui->antennaEdit->blockSignals(false); } void NewContactWidget::__modeChanged() { FCT_IDENTIFICATION; modeController->applyCurrentMode(); // Adjuste Combos Size int maxWidth = qMax(ui->modeEdit->sizeHint().width(), ui->submodeEdit->sizeHint().width()); ui->modeEdit->setFixedWidth(maxWidth); ui->submodeEdit->setFixedWidth(maxWidth); queryMemberList(); refreshCallsignsColors(); } /* Mode is changed from GUI */ void NewContactWidget::changeMode() { FCT_IDENTIFICATION; if ( !modeController ) return; __modeChanged(); // if manual mode is not enabled then change the mode if ( !isManualEnterMode ) { rig->setMode(ui->modeEdit->currentText(), ui->submodeEdit->currentText()); emit userModeChanged(VFO1, QString(), ui->modeEdit->currentText(), ui->submodeEdit->currentText(), bandwidthFilter); } } /* mode is changed from RIG */ /* Receveived from RIG */ void NewContactWidget::changeModefromRig(VFOID, const QString &, const QString &mode, const QString &subMode, qint32 width) { FCT_IDENTIFICATION; qCDebug(function_parameters) << mode << subMode << width; if ( isManualEnterMode ) { qCDebug(runtime) << "Manual mode enabled - ignore event"; return; } bandwidthFilter = width; changeModeWithoutSignals(mode, subMode); } void NewContactWidget::subModeChanged() { FCT_IDENTIFICATION; if ( isManualEnterMode ) { qCDebug(runtime) << "Manual mode enabled - ignore event"; return; } rig->setMode(ui->modeEdit->currentText(), ui->submodeEdit->currentText()); emit userModeChanged(VFO1, QString(), ui->modeEdit->currentText(), ui->submodeEdit->currentText(), bandwidthFilter); } void NewContactWidget::updateTXBand(double freq) { FCT_IDENTIFICATION; qCDebug(function_parameters)<bandTXLabel->setText("OOB!"); } else if (bandTX.name != ui->bandTXLabel->text()) { ui->bandTXLabel->setText(bandTX.name); } updateSatMode(); updateDxccStatus(); queryPota(); // It is not possible to call the potaquert everywhere when the freq changes, // call it when band is changed ui->dxccTableWidget->setDxcc(dxccEntity.dxcc, BandPlan::freq2Band(ui->freqTXEdit->value())); ui->stationTableWidget->setDxCallsign(ui->callsignEdit->text(), BandPlan::freq2Band(ui->freqTXEdit->value())); } void NewContactWidget::updateRXBand(double freq) { FCT_IDENTIFICATION; qCDebug(function_parameters)<bandRXLabel->text()) { setBandLabel(bandRX.name); } updateSatMode(); setSTXSeq(); refreshCallsignsColors(); } void NewContactWidget::gridChanged() { FCT_IDENTIFICATION; Gridsquare newGrid(uiDynamic->gridEdit->text()); if (!newGrid.isValid()) { coordPrec = COORD_NONE; setDxccInfo(ui->callsignEdit->text()); return; } updateCoordinates(newGrid.getLatitude(), newGrid.getLongitude(), COORD_GRID); } void NewContactWidget::clearCallbookQueryFields() { FCT_IDENTIFICATION; uiDynamic->nameEdit->clear(); uiDynamic->gridEdit->clear(); uiDynamic->qthEdit->clear(); uiDynamic->dokEdit->clear(); uiDynamic->iotaEdit->clear(); uiDynamic->emailEdit->clear(); uiDynamic->countyEdit->clear(); uiDynamic->urlEdit->clear(); uiDynamic->stateEdit->clear(); ui->qslViaEdit->clear(); ui->eqslLabel->setText(QString()); ui->lotwLabel->setText(QString()); emit callboolImageUrl(""); } void NewContactWidget::clearMemberQueryFields() { FCT_IDENTIFICATION; ui->memberListLabel->clear(); } void NewContactWidget::resetContact() { FCT_IDENTIFICATION; ui->callsignEdit->clear(); uiDynamic->commentEdit->clear(); ui->noteEdit->clear(); ui->dxccInfo->setText(" "); ui->distanceInfo->clear(); dxDistance = qQNaN(); ui->bearingInfo->clear(); ui->partnerLocTimeInfo->clear(); uiDynamic->cqzEdit->clear(); uiDynamic->ituEdit->clear(); uiDynamic->contEdit->setCurrentText(""); uiDynamic->sotaEdit->clear(); uiDynamic->potaEdit->clear(); uiDynamic->sigEdit->clear(); uiDynamic->sigInfoEdit->clear(); uiDynamic->vuccEdit->clear(); uiDynamic->wwffEdit->clear(); ui->dxccTableWidget->clear(); ui->stationTableWidget->clear(); ui->dxccStatus->clear(); ui->flagView->setPixmap(QPixmap()); uiDynamic->ageEdit->clear(); uiDynamic->srxStringEdit->clear(); uiDynamic->srxEdit->clear(); uiDynamic->rxPWREdit->clear(); uiDynamic->rigEdit->clear(); uiDynamic->qslMsgSEdit->clear(); uiDynamic->skccEdit->clear(); uiDynamic->uksmgEdit->clear(); uiDynamic->fistsEdit->clear(); uiDynamic->fistsCCEdit->clear(); ui->dupeLabel->setVisible(false); clearCallbookQueryFields(); clearMemberQueryFields(); ui->callsignEdit->setPalette(QPalette()); ui->callsignEdit->setFocus(); partnerTimeZone = QTimeZone(); updateTime(); stopContactTimer(); setDefaultReport(); callsign = QString(); dxccEntity = DxccEntity(); coordPrec = COORD_NONE; QSOFreq = 0.0; QSOTxFreq = 0.0; prevQSOTxFreq = 0.0; emit filterCallsign(QString()); emit newTarget(qQNaN(), qQNaN()); emit contactReset(); } void NewContactWidget::addAddlFields(QSqlRecord &record, const StationProfile &profile) { FCT_IDENTIFICATION; if ( record.value("pfx").toString().isEmpty() ) { const QString &pfxRef = Callsign(record.value("callsign").toString()).getWPXPrefix(); if ( !pfxRef.isEmpty() ) { record.setValue("pfx", pfxRef); } } if ( record.value("qsl_sent").toString().isEmpty() ) { QVariant sentState = ui->qslSentBox->itemData(ui->qslSentBox->currentIndex()); record.setValue("qsl_sent", sentState); if ( sentState == QVariant("Y") ) { record.setValue("qsl_sdate", QDate::currentDate()); } } if ( record.value("lotw_qsl_sent").toString().isEmpty() ) { QVariant sentState = ui->lotwQslSentBox->itemData(ui->lotwQslSentBox->currentIndex()); record.setValue("lotw_qsl_sent", sentState); if ( sentState == QVariant("Y") ) { record.setValue("lotw_qslsdate", QDate::currentDate()); } } if ( record.value("eqsl_qsl_sent").toString().isEmpty() ) { QVariant sentState = ui->eQSLSentBox->itemData(ui->eQSLSentBox->currentIndex()); record.setValue("eqsl_qsl_sent", sentState); if ( sentState == QVariant("Y") ) { record.setValue("eqsl_qslsdate", QDate::currentDate()); } } record.setValue("qsl_rcvd", "N"); record.setValue("lotw_qsl_rcvd", "N"); record.setValue("eqsl_qsl_rcvd", "N"); record.setValue("dcl_qsl_rcvd", "N"); record.setValue("dcl_qsl_sent", "N"); /* isNull is not necessary to use because NULL Text fields are empty */ if ( record.value("my_gridsquare").toString().isEmpty() && !profile.locator.isEmpty()) { record.setValue("my_gridsquare", profile.locator.toUpper()); } if ( ! record.value("gridsquare").toString().isEmpty() && ! record.value("my_gridsquare").toString().isEmpty() ) { Gridsquare myGrid(record.value("my_gridsquare").toString()); double distance; if ( myGrid.distanceTo(Gridsquare(record.value("gridsquare").toString()), distance) ) { record.setValue("distance", distance); } } if ( Rotator::instance()->isRotConnected() ) { record.setValue("ant_az", Rotator::instance()->getAzimuth()); record.setValue("ant_el", Rotator::instance()->getElevation()); } if ( prop_cond ) { if ( record.value("sfi").toString().isEmpty() && prop_cond->isFluxValid() ) { record.setValue("sfi", prop_cond->getFlux()); } if ( record.value("k_index").toString().isEmpty() && prop_cond->isKIndexValid() ) { record.setValue("k_index", prop_cond->getKIndex()); } if ( record.value("a_index").toString().isEmpty() && prop_cond->isAIndexValid() ) { record.setValue("a_index", prop_cond->getAIndex()); } } if ( (record.value("tx_pwr").toString().isEmpty() || record.value("tx_pwr") == 0.0 ) && uiDynamic->powerEdit->value() != 0.0) { record.setValue("tx_pwr", uiDynamic->powerEdit->value()); } if ( record.value("band").toString().isEmpty() && ! record.value("freq").toString().isEmpty() ) { record.setValue("band", BandPlan::freq2Band(record.value("freq").toDouble()).name); } if ( record.value("prop_mode").toString().isEmpty() && !ui->propagationModeEdit->currentText().isEmpty()) { record.setValue("prop_mode", Data::instance()->propagationModeTextToID(ui->propagationModeEdit->currentText())); } if ( record.value("prop_mode").toString() == "SAT" ) { // we only allow SAT_MODE information to be taken from the GUI // if the Log record TX Band/Freq matches the GUI's Band/Freq. Unfortunately, RX Freq will not be used, // because it might be missing from the log record. if ( record.value("sat_mode").toString().isEmpty() && !uiDynamic->satModeEdit->currentText().isEmpty() && record.value("band").toString() == ui->bandTXLabel->text()) { record.setValue("sat_mode", Data::instance()->satModeTextToID(uiDynamic->satModeEdit->currentText())); } if ( record.value("sat_name").toString().isEmpty() && !uiDynamic->satNameEdit->text().isEmpty() ) { record.setValue("sat_name", Data::removeAccents(uiDynamic->satNameEdit->text().toUpper())); } } if ( record.value("my_rig_intl").toString().isEmpty() && !ui->rigEdit->currentText().isEmpty() ) { record.setValue("my_rig_intl", ui->rigEdit->currentText()); } if ( record.value("my_antenna_intl").toString().isEmpty() && !ui->antennaEdit->currentText().isEmpty()) { record.setValue("my_antenna_intl", ui->antennaEdit->currentText()); } if ( record.value("my_city_intl").toString().isEmpty() && !profile.qthName.isEmpty()) { record.setValue("my_city_intl", profile.qthName); } if ( record.value("station_callsign").toString().isEmpty() && !profile.callsign.isEmpty() ) { record.setValue("station_callsign", profile.callsign.toUpper()); } if ( record.value("my_dxcc").toString().isEmpty() && profile.dxcc != 0 ) { record.setValue("my_dxcc", profile.dxcc); record.setValue("my_country_intl", profile.country); } if ( record.value("my_cnty").toString().isEmpty() && !profile.county.isEmpty() ) { record.setValue("my_cnty", profile.county); } if ( record.value("operator").toString().isEmpty() && !profile.operatorCallsign.isEmpty() ) { record.setValue("operator", profile.operatorCallsign.toUpper()); } else if ( record.value("operator").toString().isEmpty() && !profile.callsign.isEmpty() ) { record.setValue("operator", profile.callsign.toUpper()); } if ( record.value("my_itu_zone").toString().isEmpty() && profile.ituz != 0 ) { record.setValue("my_itu_zone", profile.ituz); } if ( record.value("my_cq_zone").toString().isEmpty() && profile.cqz != 0 ) { record.setValue("my_cq_zone", profile.cqz); } if ( record.value("my_darc_dok").toString().isEmpty() && !profile.darcDOK.isEmpty() ) { record.setValue("my_darc_dok", profile.darcDOK); } if ( record.value("my_name_intl").toString().isEmpty() && !profile.operatorName.isEmpty() ) { record.setValue("my_name_intl", profile.operatorName); } if ( record.value("my_iota").toString().isEmpty() && !profile.iota.isEmpty()) { record.setValue("my_iota", Data::removeAccents(profile.iota.toUpper())); } if ( record.value("my_sota_ref").toString().isEmpty() && !profile.sota.isEmpty()) { record.setValue("my_sota_ref", Data::removeAccents(profile.sota.toUpper())); } if ( ! record.value("my_sota_ref").toString().isEmpty() ) { SOTAEntity sotaInfo = Data::instance()->lookupSOTA(record.value("my_sota_ref").toString()); if ( sotaInfo.summitCode.toUpper() == record.value("my_sota_ref").toString().toUpper() && !sotaInfo.summitName.isEmpty() ) { record.setValue("my_altitude", sotaInfo.altm); } } if ( record.value("my_pota_ref").toString().isEmpty() && !profile.pota.isEmpty()) { record.setValue("my_pota_ref", Data::removeAccents(profile.pota.toUpper())); } if ( record.value("my_sig_intl").toString().isEmpty() && !profile.sig.isEmpty()) { record.setValue("my_sig_intl", profile.sig); } if ( record.value("my_sig_info_intl").toString().isEmpty() && !profile.sigInfo.isEmpty()) { record.setValue("my_sig_info_intl", profile.sigInfo); } if ( record.value("my_vucc_grids").toString().isEmpty() && !profile.vucc.isEmpty()) { record.setValue("my_vucc_grids", profile.vucc.toUpper()); } if ( record.value("my_wwff_ref").toString().isEmpty() && !profile.wwff.isEmpty()) { record.setValue("my_wwff_ref", profile.wwff.toUpper()); } // all contest fields are used only in case when // contestID field is visible - with this, the operator shows // that he/she wants to run a contest if ( uiDynamic->contestIDEdit->isVisible() && !uiDynamic->contestIDEdit->text().isEmpty() ) { uiDynamic->contestIDEdit->setText(Data::removeAccents(uiDynamic->contestIDEdit->text())); if ( shouldStartContest() ) startContest(record.value("start_time").toDateTime()); if ( record.value("contest_id").toString().isEmpty() ) { record.setValue("contest_id", uiDynamic->contestIDEdit->text()); } if ( record.value("srx_string").toString().isEmpty() && uiDynamic->srxStringEdit->isVisible() ) { record.setValue("srx_string", uiDynamic->srxStringEdit->styleSheet().contains(QLatin1String("uppercase")) ? uiDynamic->srxStringEdit->text().toUpper() :uiDynamic->srxStringEdit->text()); } if ( record.value("stx_string").toString().isEmpty() && uiDynamic->stxStringEdit->isVisible() ) { record.setValue("stx_string", uiDynamic->stxStringEdit->text()); } if ( record.value("srx").toString().isEmpty() && uiDynamic->srxEdit->isVisible() ) { record.setValue("srx", uiDynamic->srxEdit->text()); } if ( record.value("stx").toString().isEmpty() && uiDynamic->stxEdit->isVisible() ) { record.setValue("stx", uiDynamic->stxEdit->text()); // the field always contains a number - validator is enable for it // therefore it is possible to do this setSTXSeq(uiDynamic->stxEdit->text().toInt() + 1); } } else if ( (uiDynamic->srxStringEdit->isVisible() && !uiDynamic->srxStringEdit->text().isEmpty() ) || (uiDynamic->stxStringEdit->isVisible() && !uiDynamic->stxStringEdit->text().isEmpty() ) || uiDynamic->stxEdit->isVisible() || (uiDynamic->srxEdit->isVisible() && !uiDynamic->srxEdit->text().isEmpty() ) ) { QStringList fieldsTranslation ({ LogbookModel::getFieldNameTranslation(LogbookModel::COLUMN_SRX_STRING), LogbookModel::getFieldNameTranslation(LogbookModel::COLUMN_STX_STRING), LogbookModel::getFieldNameTranslation(LogbookModel::COLUMN_SRX), LogbookModel::getFieldNameTranslation(LogbookModel::COLUMN_STX), }); QMessageBox::warning(nullptr, QMessageBox::tr("QLog Warning"), QMessageBox::tr("The fields %0 will not be saved because the %1 is not filled.") .arg(fieldsTranslation.join(", "), LogbookModel::getFieldNameTranslation(LogbookModel::COLUMN_CONTEST_ID))); } if ( record.value("rx_pwr").toString().isEmpty() && uiDynamic->rxPWREdit->isVisible() && !uiDynamic->rxPWREdit->text().isEmpty() ) { record.setValue("rx_pwr", uiDynamic->rxPWREdit->text()); } } bool NewContactWidget::eventFilter(QObject *object, QEvent *event) { //FCT_IDENTIFICATION; if ( event->type() == QEvent::FocusIn && object == ui->rstSentEdit ) { if ( ui->callsignEdit->text().isEmpty() && ! ui->nearStationLabel->text().isEmpty() ) { changeCallsignManually(ui->nearStationLabel->text()); } if ( callsign.size() >= 3 && !contactTimer->isActive() ) { startContactTimer(); } } // Event handle to handle Enter press event for the Custom UI row. // The basic idea is: // - if enter is pressed on the Row A or Row B or RSTr(s) and QSO Timer is active then QSO is saved // - hadnler is registere automaticaly when fields are added to Row. // - But Callsign, POTA, SOTA, WWFF fields enter means "search" the value in callbook or xOTA directories // Therefore Enter event is blocked for these fields with one exception: // if Callbook is not active then Callsign enter causes "save QSO" event too. // The callsign exception was added to make it possible to quickly insert QSOs during pile-up. // Therefore there is no condition for an active QSO timer. if ( event->type() == QEvent::KeyPress && static_cast(event) && ( static_cast(event)->key() == Qt::Key_Return || static_cast(event)->key() == Qt::Key_Enter ) && ( ( object == ui->callsignEdit && (!callbookManager.isActive() || callbookSearchPaused) ) || ( object != ui->callsignEdit && object != uiDynamic->potaEdit && object != uiDynamic->sotaEdit && object != uiDynamic->wwffEdit && contactTimer->isActive() ) ) ) { saveContact(); } return false; } bool NewContactWidget::isQSOTimeStarted() { FCT_IDENTIFICATION; bool ret = ( contactTimer ) ? contactTimer->isActive() : false; qCDebug(runtime) << ret; return ret; } void NewContactWidget::QSYContactWiping(double newFreq) { FCT_IDENTIFICATION; qCDebug(function_parameters) << newFreq; qint32 QSYWipingWidth = bandwidthFilter; if ( isManualEnterMode ) { qCDebug(runtime) << "Manual mode enabled - ignore event"; return; } if ( QSYWipingWidth <= BANDWIDTH_UNKNOWN ) { QSYWipingWidth = Rig::getNormalBandwidth(ui->modeEdit->currentText(), ui->submodeEdit->currentText()); qCDebug(runtime) << "Passband is not defined - derived value " << QSYWipingWidth; } qCDebug(runtime) << "Rig online: " << rigOnline << " " << "QSO Freq: " << QSOFreq << " " << "QSO TX Freq: " << QSOTxFreq << " " << "QSO prev TX Freq: " << prevQSOTxFreq << " " << "QSO Time: " << isQSOTimeStarted() << " " << "Mode/submode: " << ui->modeEdit->currentText() << ui->submodeEdit->currentText() << "RIG Filter width: " << bandwidthFilter << "QSYWipingWidth: " << QSYWipingWidth << QSTRING_FREQ(Hz2MHz(QSYWipingWidth)) << "Diff RX: " << qAbs(QSOFreq - newFreq) << "Diff TX: " << qAbs(QSOTxFreq - newFreq) << "Diff prevTX: " << qAbs(prevQSOTxFreq - newFreq) << "Rig Profile QSO Wiping: " << RigProfilesManager::instance()->getCurProfile1().QSYWiping; double threshold = Hz2MHz(QSYWipingWidth) / 1.5; //1.5 is a magic constant - determined experimentally // Wipe only if the new freq is far from ALL known frequencies (RX, TX, and previous TX). // During VFO swap, VFO1/VFO2 updates arrive asynchronously in any order: // - VFO1 first: QSOTxFreq still holds the old TX freq -> matches new VFO1 → no wipe // - VFO2 first: QSOTxFreq is overwritten, but prevQSOTxFreq holds the old TX freq -> matches -> no wipe if ( RigProfilesManager::instance()->getCurProfile1().QSYWiping && rigOnline // only if Rig is connected && QSOFreq > 0.0 // it means that Form is "dirty" and contain freq when it got dirty && !isQSOTimeStarted() // operator is not in QSO && QSYWipingWidth != BANDWIDTH_UNKNOWN && qAbs(QSOFreq - newFreq) > threshold && ( QSOTxFreq <= 0.0 || qAbs(QSOTxFreq - newFreq) > threshold ) && ( prevQSOTxFreq <= 0.0 || qAbs(prevQSOTxFreq - newFreq) > threshold ) ) { resetContact(); } } void NewContactWidget::connectFieldChanged() { FCT_IDENTIFICATION; connect(ui->callsignEdit, &QLineEdit::textChanged, this, &NewContactWidget::formFieldChangedString); connect(ui->rstSentEdit, &QLineEdit::textChanged, this, &NewContactWidget::formFieldChangedString); connect(ui->rstRcvdEdit, &QLineEdit::textChanged, this, &NewContactWidget::formFieldChangedString); connect(uiDynamic->nameEdit, &QLineEdit::textChanged, this, &NewContactWidget::formFieldChangedString); connect(uiDynamic->qthEdit, &QLineEdit::textChanged, this, &NewContactWidget::formFieldChangedString); connect(uiDynamic->commentEdit, &QLineEdit::textChanged, this, &NewContactWidget::formFieldChangedString); connect(ui->noteEdit, &QTextEdit::textChanged, this, &NewContactWidget::formFieldChanged); connect(uiDynamic->contEdit, &QComboBox::currentTextChanged, this, &NewContactWidget::formFieldChangedString); connect(uiDynamic->ituEdit, &QLineEdit::textChanged, this, &NewContactWidget::formFieldChangedString); connect(uiDynamic->cqzEdit, &QLineEdit::textChanged, this, &NewContactWidget::formFieldChangedString); connect(uiDynamic->stateEdit, &QLineEdit::textChanged, this, &NewContactWidget::formFieldChangedString); connect(uiDynamic->countyEdit, &QLineEdit::textChanged, this, &NewContactWidget::formFieldChangedString); connect(uiDynamic->ageEdit, &QLineEdit::textChanged, this, &NewContactWidget::formFieldChangedString); connect(uiDynamic->iotaEdit, &QLineEdit::textChanged, this, &NewContactWidget::formFieldChangedString); connect(uiDynamic->sotaEdit, &QLineEdit::textChanged, this, &NewContactWidget::formFieldChangedString); connect(uiDynamic->potaEdit, &QLineEdit::textChanged, this, &NewContactWidget::formFieldChangedString); connect(uiDynamic->sigEdit, &QLineEdit::textChanged, this, &NewContactWidget::formFieldChangedString); connect(uiDynamic->sigInfoEdit, &QLineEdit::textChanged, this, &NewContactWidget::formFieldChangedString); connect(uiDynamic->dokEdit, &QLineEdit::textChanged, this, &NewContactWidget::formFieldChangedString); connect(uiDynamic->vuccEdit, &QLineEdit::textChanged, this, &NewContactWidget::formFieldChangedString); connect(uiDynamic->wwffEdit, &QLineEdit::textChanged, this, &NewContactWidget::formFieldChangedString); connect(ui->qslSentBox, &QComboBox::currentTextChanged, this, &NewContactWidget::formFieldChangedString); connect(ui->lotwQslSentBox, &QComboBox::currentTextChanged, this, &NewContactWidget::formFieldChangedString); connect(ui->eQSLSentBox, &QComboBox::currentTextChanged, this, &NewContactWidget::formFieldChangedString); connect(ui->qslSentViaBox, &QComboBox::currentTextChanged, this, &NewContactWidget::formFieldChangedString); connect(ui->qslViaEdit, &QLineEdit::textChanged, this, &NewContactWidget::formFieldChangedString); connect(uiDynamic->emailEdit, &QLineEdit::textChanged, this, &NewContactWidget::formFieldChangedString); connect(uiDynamic->urlEdit, &QLineEdit::textChanged, this, &NewContactWidget::formFieldChangedString); connect(uiDynamic->gridEdit, &QLineEdit::textChanged, this, &NewContactWidget::formFieldChangedString); connect(uiDynamic->contestIDEdit, &QLineEdit::textChanged, this, &NewContactWidget::formFieldChangedString); connect(uiDynamic->srxStringEdit, &QLineEdit::textChanged, this, &NewContactWidget::formFieldChangedString); connect(uiDynamic->stxStringEdit, &QLineEdit::textChanged, this, &NewContactWidget::formFieldChangedString); connect(uiDynamic->srxEdit, &QLineEdit::textChanged, this, &NewContactWidget::formFieldChangedString); connect(uiDynamic->rxPWREdit, &QLineEdit::textChanged, this, &NewContactWidget::formFieldChangedString); connect(uiDynamic->rigEdit, &QLineEdit::textChanged, this, &NewContactWidget::formFieldChangedString); connect(uiDynamic->qslMsgSEdit, &QLineEdit::textChanged, this, &NewContactWidget::formFieldChangedString); connect(uiDynamic->skccEdit, &QLineEdit::textChanged, this, &NewContactWidget::formFieldChangedString); connect(uiDynamic->uksmgEdit, &QLineEdit::textChanged, this, &NewContactWidget::formFieldChangedString); connect(uiDynamic->fistsEdit, &QLineEdit::textChanged, this, &NewContactWidget::formFieldChangedString); connect(uiDynamic->fistsCCEdit, &QLineEdit::textChanged, this, &NewContactWidget::formFieldChangedString); /* no other fields are currently considered * as an attempt to fill out the form */ } void NewContactWidget::saveContact() { FCT_IDENTIFICATION; const StationProfile &profile = StationProfilesManager::instance()->getProfile(ui->stationProfileCombo->currentText()); if ( profile.callsign.isEmpty() ) { QMessageBox::critical(nullptr, QMessageBox::tr("QLog Error"), QMessageBox::tr("Your callsign is empty. Please, set your Station Profile")); return; } if ( callsign.isEmpty() ) return; // if operator wants to save a QSO and QSO's Timer is not running, // then it is needed to update the QSO start time before saving if ( !isQSOTimeStarted() ) updateTime(); QDateTime start = QDateTime(ui->dateEdit->date(), ui->timeOnEdit->time(), QTimeZone::utc()); QDateTime end = ( isManualEnterMode ) ? start.addSecs(QTime(0,0).secsTo(ui->qsoDurationEdit->time())) : timeOff; QSqlTableModel model; model.setTable("contacts"); QSqlField idField = model.record().field(model.fieldIndex("id")); model.removeColumn(model.fieldIndex("id")); QSqlRecord record = model.record(0); record.setValue("start_time", start); record.setValue("end_time", end); record.setValue("callsign", callsign); if ( ! ui->rstSentEdit->text().isEmpty() ) { record.setValue("rst_sent", ui->rstSentEdit->text()); } if ( ! ui->rstRcvdEdit->text().isEmpty() ) { record.setValue("rst_rcvd", ui->rstRcvdEdit->text()); } if ( ! uiDynamic->nameEdit->text().isEmpty() ) { record.setValue("name_intl", uiDynamic->nameEdit->text()); } if ( ! uiDynamic->qthEdit->text().isEmpty() ) { record.setValue("qth_intl", uiDynamic->qthEdit->text()); } if ( ! uiDynamic->gridEdit->text().isEmpty() ) { record.setValue("gridsquare", uiDynamic->gridEdit->text().toUpper()); } record.setValue("freq", ui->freqTXEdit->value()); record.setValue("band", ui->bandTXLabel->text()); // Based on ADIF 3.1.x FREQ_RX is defined as "in a split frequency QSO" // Also here https://groups.io/g/adifdev/message/228 is mentioned: // "BAND_RX & FREQ_RX (Same than BAND and FREQ but for split/crossband/satellite QSOs)" if ( ui->freqRXEdit->value() != ui->freqTXEdit->value() ) { record.setValue("freq_rx", ui->freqRXEdit->value()); record.setValue("band_rx", ui->bandRXLabel->text()); } if ( ! ui->modeEdit->currentText().isEmpty() ) { record.setValue("mode", ui->modeEdit->currentText()); } if ( ! ui->submodeEdit->currentText().isEmpty() ) { record.setValue("submode", ui->submodeEdit->currentText()); } record.setValue("cqz", uiDynamic->cqzEdit->text().toInt()); record.setValue("ituz", uiDynamic->ituEdit->text().toInt()); if ( !dxccEntity.country.isEmpty() ) { record.setValue("dxcc", dxccEntity.dxcc); record.setValue("country", Data::removeAccents(dxccEntity.country)); record.setValue("country_intl", dxccEntity.country); } if ( ! uiDynamic->contEdit->currentText().isEmpty() ) { record.setValue("cont", uiDynamic->contEdit->currentText()); } if ( ! uiDynamic->countyEdit->text().isEmpty() ) { record.setValue("cnty", Data::removeAccents(uiDynamic->countyEdit->text())); } if ( ! uiDynamic->stateEdit->text().isEmpty() ) { record.setValue("state", Data::removeAccents(uiDynamic->stateEdit->text())); } if ( ! uiDynamic->iotaEdit->text().isEmpty() ) { record.setValue("iota", Data::removeAccents(uiDynamic->iotaEdit->text().toUpper())); } if ( ! uiDynamic->sigEdit->text().isEmpty() ) { record.setValue("sig_intl", uiDynamic->sigEdit->text()); } if ( ! uiDynamic->sigInfoEdit->text().isEmpty() ) { record.setValue("sig_info_intl", uiDynamic->sigInfoEdit->text()); } if ( ! ui->qslSentViaBox->currentText().isEmpty() ) { record.setValue("qsl_sent_via", Data::removeAccents(ui->qslSentViaBox->itemData(ui->qslSentViaBox->currentIndex()).toString())); } if ( coordPrec >= COORD_GRID && !qIsNaN(dxDistance)) { record.setValue("distance", dxDistance); } if ( !ui->AMLSInfo->text().isEmpty() ) { record.setValue("altitude", ui->AMLSInfo->text().split(" ")[0]); } if ( !uiDynamic->sotaEdit->text().isEmpty() ) { record.setValue("sota_ref", Data::removeAccents(uiDynamic->sotaEdit->text().toUpper())); } if ( !uiDynamic->potaEdit->text().isEmpty() ) { record.setValue("pota_ref", Data::removeAccents(uiDynamic->potaEdit->text().toUpper())); } if ( !uiDynamic->dokEdit->text().isEmpty() ) { record.setValue("darc_dok", Data::removeAccents(uiDynamic->dokEdit->text().toUpper())); } if ( !uiDynamic->vuccEdit->text().isEmpty() ) { record.setValue("vucc_grids", uiDynamic->vuccEdit->text().toUpper()); } if ( !uiDynamic->wwffEdit->text().isEmpty() ) { record.setValue("wwff_ref", uiDynamic->wwffEdit->text().toUpper()); } if (!uiDynamic->commentEdit->text().isEmpty()) { record.setValue("comment_intl", uiDynamic->commentEdit->text()); } if (!ui->noteEdit->toPlainText().isEmpty()) { record.setValue("notes_intl", ui->noteEdit->toPlainText()); } if (!ui->qslViaEdit->text().isEmpty()) { record.setValue("qsl_via", Data::removeAccents(ui->qslViaEdit->text().toUpper())); } if (!uiDynamic->ageEdit->text().isEmpty() && uiDynamic->ageEdit->text().toInt() > 0 ) { record.setValue("age", uiDynamic->ageEdit->text().toInt()); } if (!uiDynamic->emailEdit->text().isEmpty()) { record.setValue("email", Data::removeAccents(uiDynamic->emailEdit->text())); } if (!uiDynamic->urlEdit->text().isEmpty()) { record.setValue("web", Data::removeAccents(uiDynamic->urlEdit->text())); } if ( ! uiDynamic->rigEdit->text().isEmpty() ) { record.setValue("rig_intl", uiDynamic->rigEdit->text()); } if ( ! uiDynamic->qslMsgSEdit->text().isEmpty() ) { record.setValue("qslmsg_intl", uiDynamic->qslMsgSEdit->text()); } if ( ! uiDynamic->skccEdit->text().isEmpty() ) { record.setValue("skcc", uiDynamic->skccEdit->text()); } if ( ! uiDynamic->uksmgEdit->text().isEmpty() ) { record.setValue("uksmg", uiDynamic->uksmgEdit->text()); } if ( ! uiDynamic->fistsEdit->text().isEmpty() ) { record.setValue("fists", uiDynamic->fistsEdit->text()); } if ( ! uiDynamic->fistsCCEdit->text().isEmpty() ) { record.setValue("fists_cc", uiDynamic->fistsCCEdit->text()); } AdiFormat::preprocessINTLFields(record); addAddlFields(record, profile); AdiFormat::preprocessINTLFields(record); qCDebug(runtime) << record; if ( !model.insertRecord(-1, record) ) { qWarning() << "Cannot insert a record to Contact Table - " << model.lastError(); qCDebug(runtime) << record; return; } if ( !model.submitAll() ) { qWarning() << "Cannot commit changes to Contact Table - " << model.lastError(); return; } /* at this moment, there is no reliable way to get the last ID * therefore running SQL with MAX(id) does a good job */ QSqlQuery tmpQuery; if (tmpQuery.exec("SELECT MAX(id) FROM contacts")) { tmpQuery.next(); record.insert(0,idField); record.setValue("id", tmpQuery.value(0)); qDebug(runtime)<<"Last Inserted ID: " << tmpQuery.value(0); } updateNearestSpotDupe(); setNearestSpotColor(); resetContact(); emit contactAdded(record); } void NewContactWidget::saveExternalContact(QSqlRecord record) { FCT_IDENTIFICATION; const QString &savedCallsign = record.value("callsign").toString(); if ( savedCallsign.isEmpty() ) return; // Workaround - Issue #722 Repeated log recording - check dupe before insert // start_time, callsign, freq or band, mode // This workaround is primarily due to JTDX. In general, with WSJTX, QLog receives // two messages for a single QSO. One is in the QByteArray format and the other in ADIF. // Both of these messages are received by QLog, and if they arrive within 0.5 seconds of each other, // they are merged and sent as a single QSO. If this does not happen, unfortunately, two QSOs are sent. // Under some mysterious circumstances, JTDX sometimes generates the second message after several seconds, // sometimes even up to 3 minutes (see #722). This workaround is intended to prevent duplicate entries // from being stored in the log. // // In the future, however, this should be handled by merging the records. QSqlQuery checkQuery; if ( !checkQuery.prepare(QLatin1String("SELECT id FROM contacts " "WHERE strftime('%Y-%m-%d %H:%M:%S', start_time) = strftime('%Y-%m-%d %H:%M:%S', :starttime) " " AND callsign = :callsign " " AND (freq = :freq OR band = :band) " " AND mode = :mode " "LIMIT 1")) ) { qWarning() << "Cannot prepared select statement for the External Dupe Check"; return; } checkQuery.bindValue(":callsign", savedCallsign.toUpper()); checkQuery.bindValue(":freq", record.value("freq")); checkQuery.bindValue(":mode", record.value("mode")); checkQuery.bindValue(":band", record.value("band")); checkQuery.bindValue(":starttime", record.value("start_time")); if (!checkQuery.exec()) { qWarning() << "Duplicate check failed:" << checkQuery.lastError(); return; } if ( checkQuery.next() ) { qWarning() << "Contact with callsign" << savedCallsign << record.value("freq") << record.value("band") << record.value("mode") << record.value("start_time") << "already exists, skipping insert."; return; } // End of workaround #722 QSqlTableModel model; model.setTable("contacts"); QSqlField idField = model.record().field(model.fieldIndex("id")); model.removeColumn(model.fieldIndex("id")); // the Band value is mandatory for LoTW QSL matching algorithm if ( record.value("band").toString().isEmpty() && !record.value("freq").toString().isEmpty() ) { double freq = record.value("freq").toDouble(); record.setValue("band", BandPlan::freq2Band(freq).name); } // if DXCC field is present then it must be used as DXCC Entity int recordDXCCId = record.value("dxcc").toInt(); // 0 = NAN or not present // otherwise = DXCC ID const DxccEntity &dxcc = ( recordDXCCId ) ? Data::instance()->lookupDxccID(recordDXCCId) : Data::instance()->lookupDxcc(savedCallsign); if ( dxcc.dxcc != 0 ) { // force overwrite record.setValue("dxcc", dxcc.dxcc); record.setValue("country_intl", dxcc.country); if ( record.value("cqz").toString().isEmpty() ) record.setValue("cqz", dxcc.cqz); if ( record.value("ituz").toString().isEmpty() ) record.setValue("ituz", dxcc.ituz); if ( record.value("cont").toString().isEmpty() ) record.setValue("cont", dxcc.cont); } // add information from callbook if it is a known callsign // based on the poll #420, QLog adds more information from callbook if ( savedCallsign == ui->callsignEdit->text() ) { stopContactTimer(); updateTime(); // information independent of QTH if ( record.value("name_intl").toString().isEmpty() && record.value("name").toString().isEmpty() && !uiDynamic->nameEdit->text().isEmpty() ) record.setValue("name_intl", uiDynamic->nameEdit->text()); if ( record.value("email").toString().isEmpty() && !uiDynamic->emailEdit->text().isEmpty() ) record.setValue("email", uiDynamic->emailEdit->text()); if ( record.value("qsl_via").toString().isEmpty() && !ui->qslViaEdit->text().isEmpty() ) record.setValue("qsl_via", ui->qslViaEdit->text()); if ( record.value("web").toString().isEmpty() && !uiDynamic->urlEdit->text().isEmpty() ) record.setValue("web", uiDynamic->urlEdit->text()); if ( record.value("darc_dok").toString().isEmpty() && !uiDynamic->dokEdit->text().isEmpty() ) record.setValue("darc_dok", uiDynamic->dokEdit->text()); // information depending on QTH (Grid) const QString &savedGrid = record.value("gridsquare").toString(); if ( savedGrid.startsWith(uiDynamic->gridEdit->text(), Qt::CaseSensitivity::CaseInsensitive) || uiDynamic->gridEdit->text().startsWith(savedGrid, Qt::CaseSensitivity::CaseInsensitive ) ) { if ( uiDynamic->gridEdit->text().size() > savedGrid.size() ) record.setValue("gridsquare", uiDynamic->gridEdit->text()); if ( record.value("qth_intl").toString().isEmpty() && record.value("qth").toString().isEmpty() && !uiDynamic->qthEdit->text().isEmpty() ) record.setValue("qth_intl", uiDynamic->qthEdit->text()); if ( record.value("iota").toString().isEmpty() && !uiDynamic->iotaEdit->text().isEmpty() ) record.setValue("iota", uiDynamic->iotaEdit->text()); if ( record.value("cnty").toString().isEmpty() && !uiDynamic->countyEdit->text().isEmpty() ) record.setValue("cnty", uiDynamic->countyEdit->text()); if ( record.value("state").toString().isEmpty() && !uiDynamic->stateEdit->text().isEmpty() ) record.setValue("state", uiDynamic->stateEdit->text()); if ( record.value("pota_ref").toString().isEmpty() && !uiDynamic->potaEdit->text().isEmpty()) record.setValue("pota_ref", uiDynamic->potaEdit->text()); if ( record.value("sota_ref").toString().isEmpty() && !uiDynamic->sotaEdit->text().isEmpty()) record.setValue("sota_ref", uiDynamic->sotaEdit->text()); if ( record.value("sig_intl").toString().isEmpty() && !uiDynamic->sigEdit->text().isEmpty()) record.setValue("sig_intl", uiDynamic->sigEdit->text()); if ( record.value("sig_info_intl").toString().isEmpty() && !uiDynamic->sigInfoEdit->text().isEmpty()) record.setValue("sig_info_intl", uiDynamic->sigInfoEdit->text()); if ( record.value("wwff_ref").toString().isEmpty() && !uiDynamic->wwffEdit->text().isEmpty()) record.setValue("wwff_ref", uiDynamic->wwffEdit->text()); // fix ITUz and CQz from callbook, if necessary if ( record.value("ituz").toString() != uiDynamic->ituEdit->text() ) record.setValue("ituz", uiDynamic->ituEdit->text()); if ( record.value("cqz").toString() != uiDynamic->cqzEdit->text() ) record.setValue("cqz", uiDynamic->cqzEdit->text()); } } const StationProfile &profile = StationProfilesManager::instance()->getCurProfile1(); AdiFormat::preprocessINTLFields(record); addAddlFields(record, profile); AdiFormat::preprocessINTLFields(record); qCDebug(runtime) << record; if ( !model.insertRecord(-1, record) ) { qWarning() << "Cannot insert a record to Contact Table - " << model.lastError(); qCDebug(runtime) << record; return; } if ( !model.submitAll() ) { qWarning() << "Cannot commit changes to Contact Table - " << model.lastError(); return; } /* at this moment, there is no reliable way to get the last ID * therefore running SQL with MAX(id) does a good job */ QSqlQuery tmpQuery; if (tmpQuery.exec("SELECT MAX(id) FROM contacts")) { tmpQuery.next(); record.insert(0,idField); record.setValue("id", tmpQuery.value(0)); qDebug(runtime)<<"Last Inserted ID: " << tmpQuery.value(0); } updateNearestSpotDupe(); setNearestSpotColor(); emit contactAdded(record); } void NewContactWidget::startContactTimer() { FCT_IDENTIFICATION; if ( isManualEnterMode ) { qCDebug(runtime) << "Manual mode enabled - ignore event"; return; } updateTime(); if ( !isQSOTimeStarted() ) contactTimer->start(500); } void NewContactWidget::stopContactTimer() { FCT_IDENTIFICATION; if ( isManualEnterMode ) { qCDebug(runtime) << "Manual mode enabled - ignore event"; return; } if ( isQSOTimeStarted() ) contactTimer->stop(); updateTimeOff(); } void NewContactWidget::markContact() { FCT_IDENTIFICATION; if ( !ui->callsignEdit->text().isEmpty() ) { DxSpot spot; spot.dateTime = QDateTime::currentDateTime().toTimeZone(QTimeZone::utc()); spot.freq = ui->freqRXEdit->value(); spot.band = BandPlan::freq2Band(spot.freq).name; spot.callsign = ui->callsignEdit->text().toUpper(); emit markQSO(spot); } } void NewContactWidget::updateTime() { FCT_IDENTIFICATION; if ( isManualEnterMode ) { qCDebug(runtime) << "Manual mode enabled - ignore event"; return; } timeOff = QDateTime::currentDateTimeUtc(); ui->dateEdit->setDate(timeOff.date()); ui->timeOnEdit->setTime(timeOff.time()); ui->qsoDurationEdit->setTime(QTime(0,0,0)); } void NewContactWidget::updateTimeOff() { FCT_IDENTIFICATION; if ( isManualEnterMode ) { qCDebug(runtime) << "Manual mode enabled - ignore event"; return; } static bool shouldHighlighted = true; timeOff = QDateTime::currentDateTimeUtc(); qint64 seconds = ui->timeOnEdit->dateTime().secsTo(timeOff); QTime t = QTime(0,0).addSecs(seconds % 86400); ui->qsoDurationEdit->setTime(t); //QColor(76, 200, 80) ui->qsoDurationEdit->setStyleSheet( ( shouldHighlighted && isQSOTimeStarted() ) ? "background-color: #4CC850 ;" : ""); shouldHighlighted = ( isQSOTimeStarted() ) ? !shouldHighlighted : false; updatePartnerLocTime(); } void NewContactWidget::updateCoordinates(double lat, double lon, CoordPrecision prec) { FCT_IDENTIFICATION; if (prec < coordPrec) return; Gridsquare myGrid(StationProfilesManager::instance()->getProfile(ui->stationProfileCombo->currentText()).locator); double distance; double bearing; if ( myGrid.distanceTo(lat, lon, distance) && myGrid.bearingTo(lat, lon, bearing) ) { dxDistance = distance; QString unit; double showDistance = Gridsquare::distance2localeUnitDistance(dxDistance, unit, locale); double LPBearing = bearing - 180; if ( LPBearing < 0 ) LPBearing += 360; ui->distanceInfo->setText(QString::number(showDistance, '.', 1) + QString(" %1").arg(unit)); ui->bearingInfo->setText(QString("%1° (%2: %3°)").arg(QString::number(bearing, '.', 1), tr("LP"), QString::number(LPBearing, '.', 1))); QString partnerTimeZoneString = Data::instance()->getIANATimeZone(lat, lon); if ( !partnerTimeZoneString.isEmpty() ) { partnerTimeZone = QTimeZone(partnerTimeZoneString.toUtf8()); } else { partnerTimeZone = QTimeZone(); } coordPrec = prec; updatePartnerLocTime(); emit newTarget(lat, lon); } } void NewContactWidget::clearCoordinates() { FCT_IDENTIFICATION; ui->distanceInfo->clear(); dxDistance = qQNaN(); ui->bearingInfo->clear(); partnerTimeZone = QTimeZone(); ui->partnerLocTimeInfo->clear(); } void NewContactWidget::updateDxccStatus() { FCT_IDENTIFICATION; setNearestSpotColor(); if ( callsign.isEmpty() ) { ui->dxccStatus->clear(); ui->callsignEdit->setPalette(QPalette()); return; } DxccStatus status = Data::instance()->dxccStatus(dxccEntity.dxcc, ui->bandRXLabel->text(), ui->modeEdit->currentText()); switch (status) { case DxccStatus::NewEntity: ui->dxccStatus->setText(tr("New Entity!")); break; case DxccStatus::NewBand: ui->dxccStatus->setText(tr("New Band!")); break; case DxccStatus::NewMode: ui->dxccStatus->setText(tr("New Mode!")); break; case DxccStatus::NewBandMode: ui->dxccStatus->setText(tr("New Band & Mode!")); break; case DxccStatus::NewSlot: ui->dxccStatus->setText(tr("New Slot!")); break; case DxccStatus::Worked: ui->dxccStatus->setText(tr("Worked")); break; case DxccStatus::Confirmed: ui->dxccStatus->setText(tr("Confirmed")); break; default: ui->dxccStatus->clear(); } QPalette palette; palette.setColor(QPalette::Text, Data::statusToColor(status, ui->dupeLabel->isVisible(), palette.color(QPalette::Text))); ui->callsignEdit->setPalette(palette); } void NewContactWidget::updatePartnerLocTime() { FCT_IDENTIFICATION; if ( partnerTimeZone.isValid() ) { ui->partnerLocTimeInfo->setText(locale.toString(QDateTime::currentDateTime().toTimeZone(partnerTimeZone), locale.formatTimeLong()) + " (" + getGreeting() +")"); } } /* the function is called when a newcontact frequency spinbox is changed */ void NewContactWidget::frequencyTXChanged() { FCT_IDENTIFICATION; double xitFreq = ui->freqTXEdit->value(); if ( isManualEnterMode ) { qCDebug(runtime) << "Manual mode enabled - ignore freq change"; updateTXBand(xitFreq); /* Do not change RX */ return; } if ( rigSplitEnabled ) { // In split mode, TX frequency change goes to VFO2 updateTXBand(xitFreq); qCDebug(runtime) << "split TX freq: " << xitFreq; rig->setFrequency(VFO2, MHz(xitFreq)); return; } realRigFreq = xitFreq - RigProfilesManager::instance()->getCurProfile1().xitOffset; double ritFreq = (isManualEnterMode) ? ui->freqRXEdit->value() : realRigFreq + RigProfilesManager::instance()->getCurProfile1().ritOffset; __changeFrequency(VFO1, realRigFreq, ritFreq, xitFreq); // TODO: qlog should call queryMemberList but for saving time we will omit it. and callsign is usually // cleared // queryMemberList(); qCDebug(runtime) << "rig real freq: " << realRigFreq; rig->setFrequency(MHz(realRigFreq)); // set rig frequency emit userFrequencyChanged(VFO1, realRigFreq, ritFreq, xitFreq); } /* the function is called when a newcontact RX frequecy spinbox is changed */ void NewContactWidget::frequencyRXChanged() { FCT_IDENTIFICATION; double ritFreq = ui->freqRXEdit->value(); if ( isManualEnterMode ) { qCDebug(runtime) << "Manual mode enabled - ignore freq change"; updateRXBand(ritFreq); /* Change also TX freq on the oposite site, TX change does not change RX */ ui->freqTXEdit->setValue(ritFreq); return; } realRigFreq = ritFreq - RigProfilesManager::instance()->getCurProfile1().ritOffset; double xitFreq = realRigFreq + RigProfilesManager::instance()->getCurProfile1().xitOffset; __changeFrequency(VFO1, realRigFreq, ritFreq, xitFreq); qCDebug(runtime) << "rig real freq: " << realRigFreq; rig->setFrequency(MHz(realRigFreq)); // set rig frequency emit userFrequencyChanged(VFO1, realRigFreq, ritFreq, xitFreq); } /* the function is called when rig freq is changed */ /* Received from RIG */ void NewContactWidget::changeFrequency(VFOID vfoid, double vfoFreq, double ritFreq, double xitFreq) { FCT_IDENTIFICATION; qCDebug(function_parameters) << vfoid << vfoFreq << ritFreq << xitFreq; if ( vfoid == VFO2 ) { // TX VFO frequency update (split mode) // Keep previous TX freq — during VFO swap, VFO1/VFO2 updates // arrive asynchronously; prevQSOTxFreq prevents false QSY Wipe prevQSOTxFreq = QSOTxFreq; QSOTxFreq = vfoFreq; if ( isManualEnterMode ) return; ui->freqTXEdit->blockSignals(true); ui->freqTXEdit->setValue(vfoFreq); updateTXBand(vfoFreq); ui->freqTXEdit->blockSignals(false); return; } // VFO1 — RX frequency realFreqForManualExit = vfoFreq; if ( isManualEnterMode ) { qCDebug(runtime) << "Manual mode enabled - ignore event"; return; } __changeFrequency(vfoid, vfoFreq, ritFreq, xitFreq); } void NewContactWidget::changeSplit(VFOID, bool enabled) { FCT_IDENTIFICATION; qCDebug(function_parameters) << enabled; rigSplitEnabled = enabled; if ( !enabled ) { // Split turned off — sync TX freq back to VFO1 + XIT double xitFreq = realRigFreq + RigProfilesManager::instance()->getCurProfile1().xitOffset; ui->freqTXEdit->blockSignals(true); ui->freqTXEdit->setValue(xitFreq); updateTXBand(xitFreq); ui->freqTXEdit->blockSignals(false); } ui->freqTXEdit->setEnabled(!enabled); showRXTXFreqs(enabled || RigProfilesManager::instance()->getCurProfile1().ritOffset != 0.0 || RigProfilesManager::instance()->getCurProfile1().xitOffset != 0.0 || isManualEnterMode); } void NewContactWidget::changeModeWithoutSignals(const QString &mode, const QString &subMode) { FCT_IDENTIFICATION; qCDebug(function_parameters) << mode << subMode; if ( ui->modeEdit->currentText() == mode) { qCDebug(runtime) << "Mode did not change, changing submode"; ui->submodeEdit->blockSignals(true); ui->submodeEdit->setCurrentText(subMode); ui->submodeEdit->blockSignals(false); return; } qCDebug(runtime) << "Mode changed - updating submode list"; ui->modeEdit->blockSignals(true); ui->submodeEdit->blockSignals(true); ui->modeEdit->setCurrentText(mode); __modeChanged(); ui->submodeEdit->setCurrentText(subMode); ui->submodeEdit->blockSignals(false); ui->modeEdit->blockSignals(false); } void NewContactWidget::showRXTXFreqs(bool enable) { FCT_IDENTIFICATION; ui->freqTXEdit->setVisible(enable); ui->bandTXLabel->setVisible(enable); ui->freqRXLabel->setVisible(enable); ui->freqTXLabel->setVisible(enable); } /* Generic function to change freq */ void NewContactWidget::__changeFrequency(VFOID, double vfoFreq, double ritFreq, double xitFreq) { FCT_IDENTIFICATION; qCDebug(function_parameters) << vfoFreq << " " << ritFreq << " " << xitFreq; QSYContactWiping(ritFreq); realRigFreq = vfoFreq; // When split is active, TX freq comes via VFO2 — don't overwrite it here if ( !rigSplitEnabled ) { ui->freqTXEdit->blockSignals(true); ui->freqTXEdit->setValue(xitFreq); updateTXBand(xitFreq); ui->freqTXEdit->blockSignals(false); } ui->freqRXEdit->blockSignals(true); ui->freqRXEdit->setValue(ritFreq); updateRXBand(ritFreq); ui->freqRXEdit->blockSignals(false); showRXTXFreqs(( rigSplitEnabled || ritFreq != xitFreq || RigProfilesManager::instance()->getCurProfile1().ritOffset != 0.0 || RigProfilesManager::instance()->getCurProfile1().xitOffset != 0.0 || isManualEnterMode )); } /* Power is changed from RIG */ /* Received from RIG */ void NewContactWidget::changePower(VFOID, double power) { FCT_IDENTIFICATION; qCDebug(function_parameters) << power; if ( isManualEnterMode ) { qCDebug(runtime) << "Manual mode enabled - ignore event"; return; } uiDynamic->powerEdit->blockSignals(true); uiDynamic->powerEdit->setValue(power); uiDynamic->powerEdit->blockSignals(false); } /* connection slot */ /* received from RIG */ void NewContactWidget::rigConnected() { FCT_IDENTIFICATION; if ( isManualEnterMode ) { qCDebug(runtime) << "Manual mode enabled - ignore event"; return; } RigProfile currProfile = RigProfilesManager::instance()->getCurProfile1(); /* allow modify PWR only in case when Rig is not connected or user * does not want to get PWR from RIG */ if ( currProfile.getPWRInfo ) { uiDynamic->powerEdit->setEnabled(false); uiDynamic->powerEdit->setValue(0.0); } else { uiDynamic->powerEdit->setEnabled(true); uiDynamic->powerEdit->setValue(currProfile.defaultPWR); } rigOnline = true; } /* disconnection slot */ /* received from RIG */ void NewContactWidget::rigDisconnected() { FCT_IDENTIFICATION; if ( isManualEnterMode ) { qCDebug(runtime) << "Manual mode enabled - ignore event"; return; } changeSplit(VFO1, false); uiDynamic->powerEdit->setEnabled(true); uiDynamic->powerEdit->setValue(RigProfilesManager::instance()->getCurProfile1().defaultPWR); rigOnline = false; } void NewContactWidget::setNearestSpot(const DxSpot &spot) { FCT_IDENTIFICATION; nearestSpot = spot; setNearestSpotColor(); } void NewContactWidget::setNearestSpotColor() { FCT_IDENTIFICATION; if ( nearestSpot.callsign.isEmpty() ) { ui->nearStationLabel->clear(); return; } QPalette palette; const DxccEntity &spotEntity = Data::instance()->lookupDxcc(nearestSpot.callsign); const DxccStatus &status = Data::instance()->dxccStatus(spotEntity.dxcc, ui->bandRXLabel->text(), ui->modeEdit->currentText()); palette.setColor(QPalette::WindowText, Data::statusToColor(status, nearestSpot.dupeCount, palette.color(QPalette::Text))); ui->nearStationLabel->setPalette(palette); ui->nearStationLabel->setText(nearestSpot.callsign); } void NewContactWidget::setManualMode(bool isEnabled) { FCT_IDENTIFICATION; bool isExitManualMode = ! isEnabled && isManualEnterMode; if ( isEnabled && rigOnline ) { rigDisconnected(); } isManualEnterMode = isEnabled; if ( isExitManualMode ) { realRigFreq = realFreqForManualExit; exitManualMode(); showRXTXFreqs((RigProfilesManager::instance()->getCurProfile1().ritOffset != 0.0 || RigProfilesManager::instance()->getCurProfile1().xitOffset != 0.0)); } else { realFreqForManualExit = realRigFreq; resetContact(); showRXTXFreqs(true); ui->dateEdit->setReadOnly(false); ui->timeOnEdit->setReadOnly(false); ui->qsoDurationEdit->setReadOnly(false); ui->timeOnEdit->setFocusPolicy(Qt::StrongFocus); ui->dateEdit->setFocusPolicy(Qt::StrongFocus); ui->qsoDurationEdit->setFocusPolicy(Qt::StrongFocus); ui->qsoDurationEdit->setCurrentSection(QDateTimeEdit::MinuteSection); ui->thirdLineWidget->setTabOrder(ui->dateEdit, ui->timeOnEdit); ui->thirdLineWidget->setTabOrder(ui->timeOnEdit, ui->qsoDurationEdit); } QString styleString = (isManualEnterMode) ? "background-color: orange;" : ""; ui->modeLabel->setStyleSheet(styleString); ui->frequencyLabel->setStyleSheet(styleString); ui->dateLabel->setStyleSheet(styleString); ui->timeOnLabel->setStyleSheet(styleString); ui->qsoDurationLabel->setStyleSheet(styleString); ui->stationProfileLabel->setStyleSheet(styleString); ui->rigLabel->setStyleSheet(styleString); ui->antennaLabel->setStyleSheet(styleString); uiDynamic->powerLabel->setStyleSheet(styleString); } void NewContactWidget::exitManualMode() { FCT_IDENTIFICATION; // set date/time // clear form resetContact(); ui->dateEdit->setReadOnly(true); ui->timeOnEdit->setReadOnly(true); ui->qsoDurationEdit->setReadOnly(true); ui->timeOnEdit->setFocusPolicy(Qt::ClickFocus); ui->dateEdit->setFocusPolicy(Qt::ClickFocus); ui->qsoDurationEdit->setFocusPolicy(Qt::ClickFocus); //rig connected/disconnected if ( rig->isRigConnected() ) { rigConnected(); // set mode/submode // set frequency rig->sendState(); //resend rig state via signals } else { rigDisconnected(); } // reset my profiles refreshRigProfileCombo(); refreshAntProfileCombo(); refreshStationProfileCombo(); } void NewContactWidget::setupCustomUi() { FCT_IDENTIFICATION; // Clear Custom Lines const QList &customUiRows = ui->customLayout->findChildren(); for ( auto &rowLayout : customUiRows ) { qCDebug(runtime) << "Removing objects from " << rowLayout->objectName(); QLayoutItem *rowItem; while ( (rowItem = rowLayout->takeAt(0)) != nullptr ) { if ( rowItem->widget() != nullptr) { qCDebug(runtime) << "Removing widget" << rowItem->widget()->objectName(); rowItem->widget()->removeEventFilter(this); // only row fields has Special Event Filter (enter handling) rowItem->widget()->setHidden(true); } } } // Clear Detail Columns QList detailColumns; detailColumns << ui->detailColA << ui->detailColB << ui->detailColC; for ( QFormLayout * layout : static_cast&>(detailColumns) ) { qCDebug(runtime) << "Removing" << layout->rowCount() <<"object(s) from" << layout->objectName(); int rows = layout->rowCount(); for ( int i = 0 ; i < rows; i++ ) { if ( layout == ui->detailColC && i < 4 ) { qCDebug(runtime) << "Skipping row" << i << "because static content"; continue; } qCDebug(runtime) << "Deleting row" << i; QFormLayout::TakeRowResult result = layout->takeRow((layout == ui->detailColC) ? 4 : 0); if ( result.labelItem && result.fieldItem ) { qCDebug(runtime) << "Removing Widgets" << result.labelItem->widget()->objectName() << result.fieldItem->widget()->objectName(); result.labelItem->widget()->setHidden(true); result.fieldItem->widget()->setHidden(true); } else { qCDebug(runtime) << "Row is empty"; } } } MainLayoutProfile layoutProfile = MainLayoutProfilesManager::instance()->getCurProfile1(); QList addedWidgets; // Empty Profile means Classic Layout if ( layoutProfile == MainLayoutProfile() ) layoutProfile = MainLayoutProfile::getClassicLayout(); addedWidgets << setupCustomUiRow(ui->customRowALayout, layoutProfile.rowA); addedWidgets << setupCustomUiRow(ui->customRowBLayout, layoutProfile.rowB); setupCustomUiRowsTabOrder(addedWidgets); setupCustomDetailColumn(ui->detailColA, layoutProfile.detailColA); setupCustomDetailColumn(ui->detailColB, layoutProfile.detailColB); setupCustomDetailColumn(ui->detailColC, layoutProfile.detailColC); tabCollapseBtn->setChecked(layoutProfile.tabsexpanded); ui->qsoTabs->adjustSize(); update(); } QList NewContactWidget::setupCustomUiRow(QHBoxLayout *row, const QList& widgetsList) { FCT_IDENTIFICATION; qCDebug(function_parameters) << row->objectName() << widgetsList; QWidget *currCustomWidget = nullptr; QList ret; for ( int widgetID : widgetsList ) { currCustomWidget = uiDynamic->getRowWidget(widgetID); if ( !currCustomWidget ) { qWarning() << "Missing fieldIndex2WidgetMapping for index" << widgetID; continue; } currCustomWidget->installEventFilter(this); // only row fields have a special event filter (enter handling) qCDebug(runtime) << "Adding widget" << currCustomWidget->objectName(); row->addWidget(currCustomWidget); ret << currCustomWidget; } return ret; } QList NewContactWidget::setupCustomDetailColumn(QFormLayout *column, const QList &widgetsList) { FCT_IDENTIFICATION; qCDebug(function_parameters) << column->objectName() << widgetsList; QWidget *currCustomLabel = nullptr; QWidget *currCustomEditor = nullptr; QList ret; for ( int widgetID : widgetsList ) { currCustomLabel = uiDynamic->getLabel(widgetID); currCustomEditor = uiDynamic->getEditor(widgetID); if ( !currCustomLabel || !currCustomEditor ) { qWarning() << "Missing fieldIndex2WidgetMapping for index" << widgetID; continue; } qCDebug(runtime) << "Adding widget" << currCustomEditor->objectName(); column->addRow(currCustomLabel, currCustomEditor); ret << currCustomEditor; } return ret; } void NewContactWidget::setupCustomUiRowsTabOrder(const QList &customWidgets) { FCT_IDENTIFICATION; QWidget *prevCustomWidget = nullptr; for ( QWidget *currentWidget : customWidgets ) { if ( prevCustomWidget ) { QWidget *fromWidget = prevCustomWidget->findChild(); if ( !fromWidget ) fromWidget = prevCustomWidget->findChild(); QWidget *toWidget = currentWidget->findChild(); if ( !toWidget ) toWidget = currentWidget->findChild(); if ( fromWidget && toWidget ) { //ui->customLayoutWidget->setTabOrder(fromWidget, toWidget); setTabOrder(fromWidget, toWidget); } } else { QWidget *toWidget = currentWidget->findChild(); if ( !toWidget ) toWidget = currentWidget->findChild(); if ( toWidget ) { setTabOrder(ui->rstRcvdEdit, toWidget); } } prevCustomWidget = currentWidget; } setTabOrder(prevCustomWidget, ui->callsignEdit); } void NewContactWidget::setBandLabel(const QString &band) { FCT_IDENTIFICATION; ui->bandRXLabel->setText(band); } void NewContactWidget::updateSatMode() { FCT_IDENTIFICATION; if ( Data::instance()->propagationModeTextToID(ui->propagationModeEdit->currentText()) != "SAT") return; const QString satModeText = Data::instance()->satModeIDToText(( bandTX.satDesignator.isEmpty() || bandRX.satDesignator.isEmpty() ) ? "" : bandTX.satDesignator + bandRX.satDesignator); // Only update if a valid SAT mode was resolved. // When the rig reports only one VFO frequency, bandTX == bandRX which produces // an invalid designator combination (e.g. "SS") and satModeIDToText returns "". // In that case we preserve whatever SAT mode is currently set. if ( !satModeText.isEmpty() ) uiDynamic->satModeEdit->setCurrentText(satModeText); } void NewContactWidget::tuneDx(const DxSpot &spot) { FCT_IDENTIFICATION; qCDebug(function_parameters) << spot.callsign<< spot.freq << spot.bandPlanMode; if ( isManualEnterMode ) { qCDebug(runtime) << "Manual mode enabled - ignore event"; return; } const double frequency = (spot.freq > 0.0) ? spot.freq : ui->freqRXEdit->value(); #if 0 // DX SPLIT MODE // Enable split BEFORE setting RX frequency — setSplit(true) forces // VFO A as primary, so the subsequent RX freq goes to the correct VFO. // All rig commands are queued, so order of calls here = execution order. double txFreq = 0.0; // DX SPLIT MODE : Removed due to the inability of rig drivers to consistently manage split mode. // Example: the IC-7300 has an incorrectly implemented frequency setting when // enabling split on VFO B in Hamlib. It appears to be fixed in version 4.7, // but previous versions are broken. The same issue applies to Omnirig. // Because this would require a significant number of exceptions in the code and // would result in unreliable behavior, split configuration for DX is postponed // indefinitely for now. if ( spot.freqTX > 0.0 && rigOnline ) { txFreq = spot.freqTX; // For relative offsets (UP/DOWN), add random jitter within +- mode bandwidth // so that all QLog users don't call on the exact same frequency. // Absolute QSX frequencies (where freqTX differs significantly from freq) // are left unchanged — the spotter gave a precise frequency. if ( spot.freq > 0.0 && qAbs(spot.freqTX - spot.freq) < 0.1 ) { qint32 bw = Rig::getNormalBandwidth(ui->modeEdit->currentText(), ui->submodeEdit->currentText()); double jitterMHz = Hz2MHz(QRandomGenerator::global()->bounded(bw) - bw / 2); txFreq += jitterMHz; } qCDebug(runtime) << "Setting split from DX spot: TX" << txFreq; rig->setSplit(true); } #endif // Fix #453 // it is necessary to have the sequence of Set Freq and Set Mode. // Otherwise it may happen that the mode is not set correctly on the Rig ui->freqRXEdit->setValue(frequency); if ( frequency > 0.0 ) { #if 0 // SPLIT MODE // Set TX frequency after RX frequency — split is already enabled above if ( txFreq > 0.0 ) rig->setFrequency(VFO2, MHz(txFreq)); #endif QString subMode; QString mode = BandPlan::bandPlanMode2ExpectedMode(spot.bandPlanMode, subMode); if ( mode.isEmpty() ) { qCDebug(runtime) << "mode not found" << spot.bandPlanMode; mode = BandPlan::freq2ExpectedMode(frequency, subMode); } if ( !mode.isEmpty() ) { // in case of SSB, do not sent 2 mode changes to rig // therefore change Mode without signals and then set the // final mode changeModeWithoutSignals(mode, subMode); if (BandPlan::isFTxBandMode(spot.bandPlanMode) || spot.bandPlanMode == BandPlan::BAND_MODE_DIGITAL ) { // if rig is connected then FT8 mode is overwrotten by rig // but if the rig is not connected then mode contains a correct // mode rig->setMode("SSB", "USB", true); } else { rig->setMode(ui->modeEdit->currentText(), ui->submodeEdit->currentText()); } emit userModeChanged(VFO1, QString(), mode, subMode, bandwidthFilter); } } resetContact(); changeCallsignManually(spot.callsign, frequency); auto fillRef = [&](QLineEdit* edit, const QString& value, std::function finishSlot = nullptr) { if ( edit->text().isEmpty() && !value.isEmpty() ) { edit->setText(value); if (finishSlot) finishSlot(); } }; fillRef(uiDynamic->potaEdit, spot.potaRef, [this] { potaEditFinished(); }); fillRef(uiDynamic->sotaEdit, spot.sotaRef, [this] { sotaEditFinished(); }); fillRef(uiDynamic->wwffEdit, spot.wwffRef, [this] { wwffEditFinished(); }); fillRef(uiDynamic->iotaEdit, spot.iotaRef); } void NewContactWidget::fillCallsignGrid(const QString &callsign, const QString &grid) { FCT_IDENTIFICATION; qCDebug(function_parameters) << callsign<< grid; if ( isManualEnterMode ) { qCDebug(runtime) << "Manual mode enabled - ignore event"; return; } resetContact(); changeCallsignManually(callsign, ui->freqRXEdit->value()); uiDynamic->gridEdit->setText(grid); } void NewContactWidget::prepareWSJTXQSO(const QString &receivedCallsign, const QString &grid, const QString &id) { FCT_IDENTIFICATION; qCDebug(function_parameters) << receivedCallsign << grid << id; if ( isManualEnterMode ) { qCDebug(runtime) << "Manual mode enabled - ignore event"; return; } resetContact(); if ( receivedCallsign.isEmpty() ) return; QSOFreq = ui->freqRXEdit->value(); // Important !!! - to prevent QSY Contact Reset when the frequency is set // QSY Wipe disabling - It is possible to have a RIG connected and run WSJTX. // To prevent the QSY Wipe when WSJTX's Fake Split Mode is enabled, QLog starts the QSO Timer. if ( rigOnline ) startContactTimer(); callsign = receivedCallsign; ui->callsignEdit->setText(receivedCallsign); if ( !grid.isEmpty() ) { uiDynamic->gridEdit->setText(grid); } checkDupe(); setDxccInfo(receivedCallsign); queryPota(); // at the moment WSJTX sends several statuses about changing one callsign. // In order to avoid multiple searches, we will search only when we have a grid - it was usually the last // status message // the current status message sequence is // 1) prev Callsign empty grid // 2) new Callsign empty grid // 3) new Calllsign, new gris // WRITELOG workaround: Similar to JTDX, check application ID // If ID contains "WRITELOG", always trigger callbook lookup if ( !grid.isEmpty() || WsjtxUDPReceiver::isWriteLogId(id) ) { useFieldsFromPrevQSO(callsign, grid); finalizeCallsignEdit(); } } void NewContactWidget::setDefaultReport() { FCT_IDENTIFICATION; QString innerRpt(defaultReport); /* The > character indicates the position * where the focus should be. * If this is missing, it means the position of the first character. */ int position = innerRpt.indexOf(">"); if ( position >= 0 ) innerRpt.remove(position, 1); else position = 0; ui->rstRcvdEdit->setText(innerRpt); ui->rstRcvdEdit->setSelectionOffset(position); ui->rstSentEdit->setText(innerRpt); ui->rstSentEdit->setSelectionOffset(position); } void NewContactWidget::webLookup() { FCT_IDENTIFICATION; if ( !callsign.isEmpty() ) QDesktopServices::openUrl(GenericCallbook::getWebLookupURL(callsign)); } void NewContactWidget::refreshSIGCompleter() { FCT_IDENTIFICATION; QStringListModel *model = static_cast(sigCompleter->model()); if( !model ) model = new QStringListModel(); model->setStringList(Data::instance()->sigIDList()); sigCompleter->setModel(model); } void NewContactWidget::refreshContestCompleter() { FCT_IDENTIFICATION; QStringListModel *model = static_cast(contestCompleter->model()); if( !model ) model = new QStringListModel(); model->setStringList(Data::instance()->contestList()); contestCompleter->setModel(model); } QString NewContactWidget::getCallsign() const { FCT_IDENTIFICATION; return ui->callsignEdit->text().toUpper(); } QString NewContactWidget::getName() const { FCT_IDENTIFICATION; return uiDynamic->nameEdit->text(); } QString NewContactWidget::getRST() const { FCT_IDENTIFICATION; return ui->rstSentEdit->text(); } QString NewContactWidget::getQTH() const { FCT_IDENTIFICATION; return uiDynamic->qthEdit->text(); } QString NewContactWidget::getGreeting() const { FCT_IDENTIFICATION; QString greeting(tr("GE")); if ( partnerTimeZone.isValid() ) { QDateTime currPartnerTime = QDateTime::currentDateTime().toTimeZone(partnerTimeZone); if ( currPartnerTime.time().hour() >= 5 && currPartnerTime.time().hour() < 12 ) { greeting = tr("GM"); } else if ( currPartnerTime.time().hour() >=12 && currPartnerTime.time().hour() < 18 ) { greeting = tr("GA"); } } return greeting; } QString NewContactWidget::getMyCallsign() const { FCT_IDENTIFICATION; const StationProfile &profile = StationProfilesManager::instance()->getCurProfile1(); return profile.callsign; } QString NewContactWidget::getMyName() const { FCT_IDENTIFICATION; const StationProfile &profile = StationProfilesManager::instance()->getCurProfile1(); return profile.operatorName; } QString NewContactWidget::getMyQTH() const { FCT_IDENTIFICATION; const StationProfile &profile = StationProfilesManager::instance()->getCurProfile1(); return profile.qthName; } QString NewContactWidget::getMyLocator() const { FCT_IDENTIFICATION; const StationProfile &profile = StationProfilesManager::instance()->getCurProfile1(); return profile.locator; } QString NewContactWidget::getMySIG() const { FCT_IDENTIFICATION; const StationProfile &profile = StationProfilesManager::instance()->getCurProfile1(); return profile.sig; } QString NewContactWidget::getMySIGInfo() const { FCT_IDENTIFICATION; const StationProfile &profile = StationProfilesManager::instance()->getCurProfile1(); return profile.sigInfo; } QString NewContactWidget::getMyIOTA() const { FCT_IDENTIFICATION; const StationProfile &profile = StationProfilesManager::instance()->getCurProfile1(); return profile.iota; } QString NewContactWidget::getMySOTA() const { FCT_IDENTIFICATION; const StationProfile &profile = StationProfilesManager::instance()->getCurProfile1(); return profile.sota; } QString NewContactWidget::getMyPOTA() const { FCT_IDENTIFICATION; const StationProfile &profile = StationProfilesManager::instance()->getCurProfile1(); return profile.pota; } QString NewContactWidget::getMyWWFT() const { FCT_IDENTIFICATION; const StationProfile &profile = StationProfilesManager::instance()->getCurProfile1(); return profile.wwff; } QString NewContactWidget::getMyVUCC() const { FCT_IDENTIFICATION; const StationProfile &profile = StationProfilesManager::instance()->getCurProfile1(); return profile.vucc; } QString NewContactWidget::getMyPWR() const { FCT_IDENTIFICATION; return QString::number(uiDynamic->powerEdit->value(), 'f', ( uiDynamic->powerEdit->value() != 0.0 && uiDynamic->powerEdit->value() < 1 ) ? 1 : 0); } QString NewContactWidget::getBand() const { FCT_IDENTIFICATION; return ui->bandRXLabel->text(); } QString NewContactWidget::getMode() const { FCT_IDENTIFICATION; return ui->modeEdit->currentText(); } QString NewContactWidget::getSentNr() const { FCT_IDENTIFICATION; return (uiDynamic->stxEdit->isVisible()) ? uiDynamic->stxEdit->text() : QString(); } QString NewContactWidget::getSentExch() const { FCT_IDENTIFICATION; return (uiDynamic->stxStringEdit->isVisible()) ? uiDynamic->stxStringEdit->text() : QString(); } double NewContactWidget::getQSOBearing() const { FCT_IDENTIFICATION; const QString &bearingString = ui->bearingInfo->text(); return ( !bearingString.isEmpty() ? bearingString.mid(0,bearingString.indexOf("°")).toDouble() : qQNaN()); } double NewContactWidget::getQSODistance() const { FCT_IDENTIFICATION; return dxDistance; } bool NewContactWidget::getTabCollapseState() const { FCT_IDENTIFICATION; return tabCollapseBtn->isChecked(); } void NewContactWidget::finalizeBeforeAppExit() { FCT_IDENTIFICATION; writeWidgetSetting(); } void NewContactWidget::propModeChanged(const QString &propModeText) { FCT_IDENTIFICATION; qCDebug(runtime) << "propModeText: " << propModeText << " mode: "<< Data::instance()->propagationModeIDToText("SAT"); if ( propModeText == Data::instance()->propagationModeIDToText("SAT") ) { uiDynamic->satNameEdit->setText(LogParam::getNewContactSatName()); updateSatMode(); uiDynamic->satModeEdit->setEnabled(true); uiDynamic->satNameEdit->setEnabled(true); } else { uiDynamic->satModeEdit->setCurrentIndex(-1); uiDynamic->satNameEdit->clear(); uiDynamic->satModeEdit->setEnabled(false); uiDynamic->satNameEdit->setEnabled(false); } } void NewContactWidget::stationProfileComboChanged(const QString &profileName) { FCT_IDENTIFICATION; qCDebug(function_parameters) << profileName; // My Grid change gridChanged(); if ( isManualEnterMode ) { qCDebug(runtime) << "Manual mode enabled - ignore event"; return; } StationProfilesManager::instance()->setCurProfile1(profileName); // recalculate all stats setDxccInfo(ui->callsignEdit->text()); } void NewContactWidget::setValuesFromActivity(const QString &name) { FCT_IDENTIFICATION; const auto &variableHash = ActivityProfilesManager::instance()->getProfile(name).fieldValues; auto setFieldValue = [&](LogbookModel::ColumnID columnID, QLineEdit *edit) { const QVariant &value = variableHash.value(columnID); if ( !value.isNull() ) edit->setText(value.toString()); }; auto setFieldValueCombo = [&](LogbookModel::ColumnID columnID, QComboBox *combo) { const QVariant &value = variableHash.value(columnID); if ( !value.isNull() ) combo->setCurrentText(value.toString()); }; setFieldValue(LogbookModel::COLUMN_CONTEST_ID, uiDynamic->contestIDEdit); setFieldValue(LogbookModel::COLUMN_STX_STRING, uiDynamic->stxStringEdit); // propagation mode has to be changed before SAT MODE because SAT MODE combo is disabled // and it is not possible to set a value. setFieldValueCombo(LogbookModel::LogbookModel::COLUMN_PROP_MODE, ui->propagationModeEdit); setFieldValueCombo(LogbookModel::LogbookModel::COLUMN_SAT_MODE, uiDynamic->satModeEdit); setFieldValue(LogbookModel::COLUMN_SAT_NAME, uiDynamic->satNameEdit); setContestFieldsState(); } void NewContactWidget::rigProfileComboChanged(const QString &profileName) { FCT_IDENTIFICATION; qCDebug(function_parameters) << profileName; // set just power from the new profile uiDynamic->powerEdit->setValue(RigProfilesManager::instance()->getProfile(profileName).defaultPWR); if ( isManualEnterMode ) { qCDebug(runtime) << "Manual mode enabled - ignore event"; return; } RigProfilesManager::instance()->setCurProfile1(profileName); ui->freqRXEdit->setValue(realRigFreq + RigProfilesManager::instance()->getCurProfile1().ritOffset); ui->freqTXEdit->setValue(realRigFreq + RigProfilesManager::instance()->getCurProfile1().xitOffset); emit rigProfileChanged(); } void NewContactWidget::antProfileComboChanged(const QString &profileName) { FCT_IDENTIFICATION; qCDebug(function_parameters) << profileName; if ( isManualEnterMode ) { qCDebug(runtime) << "Manual mode enabled - ignore event"; return; } AntProfilesManager::instance()->setCurProfile1(profileName); } void NewContactWidget::sotaChanged(const QString &newSOTA) { FCT_IDENTIFICATION; uiDynamic->sotaEdit->setCompleter(( newSOTA.length() >= 3 ) ? sotaCompleter : nullptr); if ( uiDynamic->qthEdit->text() == lastSOTA.summitName ) uiDynamic->qthEdit->clear(); const Gridsquare SOTAGrid(lastSOTA.latitude, lastSOTA.longitude); if ( uiDynamic->gridEdit->text() == SOTAGrid.getGrid() ) uiDynamic->gridEdit->clear(); ui->AMLSInfo->clear(); } bool NewContactWidget::isSOTAValid(SOTAEntity *entity) { FCT_IDENTIFICATION; if ( uiDynamic->sotaEdit->text().isEmpty() ) return false; const SOTAEntity &sotaInfo = Data::instance()->lookupSOTA(uiDynamic->sotaEdit->text()); if ( entity ) *entity = sotaInfo; return ( sotaInfo.summitCode.toUpper() == uiDynamic->sotaEdit->text().toUpper() && !sotaInfo.summitName.isEmpty()); } void NewContactWidget::sotaEditFinished() { FCT_IDENTIFICATION; SOTAEntity sotaInfo; if ( isSOTAValid(&sotaInfo) ) { uiDynamic->qthEdit->setText(sotaInfo.summitName); const Gridsquare SOTAGrid(sotaInfo.latitude, sotaInfo.longitude); if ( SOTAGrid.isValid() ) uiDynamic->gridEdit->setText(SOTAGrid.getGrid()); ui->AMLSInfo->setText(QString::number(sotaInfo.altm) + tr(" m")); lastSOTA = sotaInfo; } else if ( isPOTAValid(nullptr) ) potaEditFinished(); else if ( isWWFFValid(nullptr) ) wwffEditFinished(); } void NewContactWidget::potaChanged(const QString &newPOTA) { FCT_IDENTIFICATION; uiDynamic->potaEdit->setCompleter( ( newPOTA.length() >= 3 ) ? potaCompleter : nullptr); if ( uiDynamic->qthEdit->text() == lastPOTA.name ) uiDynamic->qthEdit->clear(); const Gridsquare POTAGrid(lastPOTA.grid); if ( uiDynamic->gridEdit->text() == POTAGrid.getGrid() ) uiDynamic->gridEdit->clear(); } bool NewContactWidget::isPOTAValid(POTAEntity *entity) { FCT_IDENTIFICATION; if ( uiDynamic->potaEdit->text().isEmpty() ) return false; const QStringList &potaList = uiDynamic->potaEdit->text().split("@"); QString potaString; potaString = ( potaList.size() > 0 ) ? potaList[0] : uiDynamic->potaEdit->text(); const POTAEntity &potaInfo = Data::instance()->lookupPOTA(potaString); if ( entity ) *entity = potaInfo; return (potaInfo.reference.toUpper() == potaString.toUpper() && !potaInfo.name.isEmpty()); } void NewContactWidget::potaEditFinished() { FCT_IDENTIFICATION; POTAEntity potaInfo; if ( isPOTAValid(&potaInfo) ) { uiDynamic->qthEdit->setText(potaInfo.name); Gridsquare POTAGrid(potaInfo.grid); if ( POTAGrid.isValid() ) uiDynamic->gridEdit->setText(POTAGrid.getGrid()); lastPOTA = potaInfo; } else if ( isSOTAValid(nullptr) ) sotaEditFinished(); else if ( isWWFFValid(nullptr) ) wwffEditFinished(); } bool NewContactWidget::isWWFFValid(WWFFEntity *entity) { FCT_IDENTIFICATION; if ( uiDynamic->wwffEdit->text().isEmpty() ) return false; const WWFFEntity &wwffInfo = Data::instance()->lookupWWFF(uiDynamic->wwffEdit->text()); if ( entity ) *entity = wwffInfo; return (wwffInfo.reference.toUpper() == uiDynamic->wwffEdit->text().toUpper() && !wwffInfo.name.isEmpty()); } void NewContactWidget::useNearestSpotInfo(const QString &in_callsign) { FCT_IDENTIFICATION; if ( in_callsign.isEmpty() || nearestSpot.callsign.isEmpty() || in_callsign != ui->nearStationLabel->text() ) return; if ( nearestSpot.containsPOTA ) { uiDynamic->potaEdit->setText(nearestSpot.potaRef); potaEditFinished(); } if ( nearestSpot.containsSOTA ) { uiDynamic->sotaEdit->setText(nearestSpot.sotaRef); sotaEditFinished(); } if ( nearestSpot.containsIOTA ) { uiDynamic->iotaEdit->setText(nearestSpot.iotaRef); } if ( nearestSpot.containsWWFF ) { uiDynamic->wwffEdit->setText(nearestSpot.wwffRef); wwffEditFinished(); } } bool NewContactWidget::shouldStartContest() { FCT_IDENTIFICATION; const QString &prevContestID = LogParam::getContestID(); qCDebug(runtime) << "Prev Contest" << prevContestID << "Current" << uiDynamic->contestIDEdit->text(); return (uiDynamic->contestIDEdit->text() != prevContestID); } void NewContactWidget::startContest(const QDateTime &date) { FCT_IDENTIFICATION; resetSTXSeq(); LogParam::setContestID(uiDynamic->contestIDEdit->text()); LogParam::setContestDupeDate(date); emit contestStarted(uiDynamic->contestIDEdit->text(), date); } void NewContactWidget::setSTXSeq() { FCT_IDENTIFICATION; int seqnoType = LogParam::getContestSeqnoType(); int seq = LogParam::getContestSeqno(( seqnoType == Data::SeqType::SINGLE ) ? QString() : ui->bandTXLabel->text()); uiDynamic->stxEdit->setText(QString::number(seq).rightJustified(3, '0')); } void NewContactWidget::setSTXSeq(int newValue) { FCT_IDENTIFICATION; qCDebug(function_parameters) << newValue; int seqnoType = LogParam::getContestSeqnoType(); LogParam::setContestSeqno(newValue, (seqnoType == Data::SeqType::SINGLE) ? QString() : ui->bandTXLabel->text()); uiDynamic->stxEdit->setText(QString::number(newValue).rightJustified(3, '0')); } void NewContactWidget::updateNearestSpotDupe() { FCT_IDENTIFICATION; nearestSpot.dupeCount = Data::countDupe(nearestSpot.callsign, bandRX.name, ui->modeEdit->currentText()); } void NewContactWidget::resetSTXSeq() { FCT_IDENTIFICATION; LogParam::removeContestSeqno(); setSTXSeq(); } void NewContactWidget::stopContest() { FCT_IDENTIFICATION; LogParam::setContestID(QString()); LogParam::removeConetstDupeDate(); resetSTXSeq(); resetContact(); nearestSpot.dupeCount = false; setNearestSpotColor(); } void NewContactWidget::refreshCallsignsColors() { FCT_IDENTIFICATION; checkDupe(); updateNearestSpotDupe(); updateDxccStatus(); } void NewContactWidget::changeSRXStringLink(int linkType) { FCT_IDENTIFICATION; qCDebug(function_parameters) << linkType; static QMetaObject::Connection linkWidget2SRX; static QMetaObject::Connection linkSRX2Widget; if ( linkWidget2SRX ) disconnect(linkWidget2SRX); if ( linkSRX2Widget) disconnect(linkSRX2Widget); LogbookModel::ColumnID type = static_cast(linkType); const QValidator *newValidator = nullptr; NewContactEditLine *sourceWidget = nullptr; QString style; switch (type) { case LogbookModel::COLUMN_AGE: newValidator = uiDynamic->ageEdit->validator(); sourceWidget = uiDynamic->ageEdit; break; case LogbookModel::COLUMN_CQZ: newValidator = uiDynamic->cqzEdit->validator(); sourceWidget = uiDynamic->cqzEdit; break; case LogbookModel::COLUMN_ITUZ: newValidator = uiDynamic->ituEdit->validator(); sourceWidget = uiDynamic->ituEdit; break; case LogbookModel::COLUMN_GRID: newValidator = uiDynamic->gridEdit->validator(); sourceWidget = uiDynamic->gridEdit; style = "QLineEdit {text-transform: uppercase;}"; break; case LogbookModel::COLUMN_RX_PWR: newValidator = uiDynamic->rxPWREdit->validator(); sourceWidget = uiDynamic->rxPWREdit; break; case LogbookModel::COLUMN_NAME_INTL: sourceWidget = uiDynamic->nameEdit; break; case LogbookModel::COLUMN_QTH_INTL: sourceWidget = uiDynamic->qthEdit; break; case LogbookModel::COLUMN_STATE: sourceWidget = uiDynamic->stateEdit; break; default: newValidator = nullptr; sourceWidget = nullptr; } uiDynamic->srxStringEdit->setValidator(newValidator); uiDynamic->srxStringEdit->setStyleSheet(style); uiDynamic->srxStringEdit->setText((sourceWidget) ? sourceWidget->text() : QString()); if ( sourceWidget ) { linkWidget2SRX = connect(sourceWidget, &QLineEdit::textChanged, this, [this](const QString &text) { uiDynamic->srxStringEdit->blockSignals(true); uiDynamic->srxStringEdit->setText(text); uiDynamic->srxStringEdit->blockSignals(false); }); linkSRX2Widget = connect(uiDynamic->srxStringEdit, &QLineEdit::textChanged, this, [sourceWidget](const QString &text) { sourceWidget->blockSignals(true); sourceWidget->setText(text); sourceWidget->blockSignals(false); }); } } void NewContactWidget::checkDupe() { FCT_IDENTIFICATION; if ( callsign.isEmpty() ) return; ui->dupeLabel->setVisible(Data::countDupe(callsign, bandRX.name, ui->modeEdit->currentText())); } void NewContactWidget::wwffEditFinished() { FCT_IDENTIFICATION; WWFFEntity wwffInfo; if ( isWWFFValid(&wwffInfo) ) { uiDynamic->qthEdit->setText(wwffInfo.name); if ( ! wwffInfo.iota.isEmpty() && wwffInfo.iota != "-" ) { uiDynamic->iotaEdit->setText(wwffInfo.iota.toUpper()); } uiDynamic->gridEdit->setText(QString()); // WWFF's Grid is unrealiable information lastWWFF = wwffInfo; } else if ( isSOTAValid(nullptr) ) sotaEditFinished(); else if ( isPOTAValid(nullptr) ) potaEditFinished(); } void NewContactWidget::wwffChanged(const QString &newWWFF) { FCT_IDENTIFICATION; uiDynamic->wwffEdit->setCompleter( ( newWWFF.length() >= 3 ) ? wwffCompleter : nullptr); if ( uiDynamic->qthEdit->text() == lastWWFF.name ) { uiDynamic->qthEdit->clear(); uiDynamic->gridEdit->clear(); } } void NewContactWidget::formFieldChangedString(const QString &) { FCT_IDENTIFICATION; QSOFreq = ui->freqRXEdit->value(); // Initialize QSOTxFreq from display when form becomes dirty. // Rig drivers only emit VFO2 signals when TX freq CHANGES — // after resetContact() zeroes QSOTxFreq, the driver won't re-emit // an unchanged TX freq, leaving QSOTxFreq at 0. This causes false // QSY Wipe on the first VFO swap. Reading from the display provides // the correct TX freq even before the next VFO2 signal arrives. if ( QSOTxFreq <= 0.0 && rigSplitEnabled ) QSOTxFreq = ui->freqTXEdit->value(); } void NewContactWidget::formFieldChanged() { FCT_IDENTIFICATION; formFieldChangedString(QString()); } void NewContactWidget::useNearestCallsign() { FCT_IDENTIFICATION; updateTime(); changeCallsignManually(ui->nearStationLabel->text()); ui->callsignEdit->setFocus(); } void NewContactWidget::setCallbookStatusEnabled(bool callbookEnabled) { FCT_IDENTIFICATION; qCDebug(function_parameters) << callbookEnabled; if ( callbookEnabled ) { ui->callbookStatusButton->setIcon((callbookSearchPaused) ? QIcon(":/icons/search-globe_orange.svg") : QIcon(":/icons/search-globe_green.svg")); } else { callbookSearchPaused = false; ui->callbookStatusButton->setIcon(QIcon(":/icons/search-globe_red.svg")); } if ( !callbookEnabled || callbookSearchPaused ) { ui->callbookStatusButton->setToolTip(tr("Callbook search is inactive")); ui->callsignEdit->installEventFilter(this); } else { ui->callbookStatusButton->setToolTip(tr("Callbook search is active")); ui->callsignEdit->removeEventFilter(this); } } void NewContactWidget::changeCallbookSearchStatus() { FCT_IDENTIFICATION; callbookSearchPaused = !callbookSearchPaused; setCallbookStatusEnabled(callbookManager.isActive()); } void NewContactWidget::satNameChanged() { FCT_IDENTIFICATION; if ( Data::instance()->propagationModeTextToID(ui->propagationModeEdit->currentText()) == "SAT") LogParam::setNewContactSatName(uiDynamic->satNameEdit->text()); } NewContactWidget::~NewContactWidget() { FCT_IDENTIFICATION; delete ui; delete uiDynamic; } void NewContactWidget::assignPropConditions(PropConditions *cond) { FCT_IDENTIFICATION; prop_cond = cond; } void NewContactWidget::changeCallsignManually(const QString &callsign) { FCT_IDENTIFICATION; changeCallsignManually(callsign, ui->freqRXEdit->value()); } void NewContactWidget::changeCallsignManually(const QString &callsign, double freq) { FCT_IDENTIFICATION; QSOFreq = freq; // Important !!! - to prevent QSY Contact Reset when the frequency is set // Initialize TX freq for QSY Wipe protection — see formFieldChangedString comment if ( QSOTxFreq <= 0.0 && rigSplitEnabled ) QSOTxFreq = ui->freqTXEdit->value(); ui->callsignEdit->setText(callsign); ui->callsignEdit->end(false); handleCallsignFromUser(); finalizeCallsignEdit(); stopContactTimer(); } void NewContactWidget::tabsExpandCollapse() { FCT_IDENTIFICATION; QStackedWidget* stackedWidget = ui->qsoTabs->findChild(); stackedWidget->setVisible(tabCollapseBtn->isChecked()); int maxSize = 16777215; // default expand fully if(!tabCollapseBtn->isChecked()) { maxSize = ui->qsoTabs->tabBar()->sizeHint().height(); } ui->qsoTabs->setMaximumHeight(maxSize); } void NewContactWidget::setContestFieldsState() { FCT_IDENTIFICATION; bool enabled = !uiDynamic->contestIDEdit->text().isEmpty(); const QString &toolTip = (enabled) ? QString() : tr("Contest ID must be filled in to activate"); uiDynamic->srxEdit->setEnabled(enabled); uiDynamic->srxEdit->setToolTip(toolTip); uiDynamic->srxStringEdit->setEnabled(enabled); uiDynamic->srxStringEdit->setToolTip(toolTip); uiDynamic->stxEdit->setEnabled(enabled); uiDynamic->stxEdit->setToolTip(toolTip); uiDynamic->stxStringEdit->setEnabled(enabled); uiDynamic->stxStringEdit->setToolTip(toolTip); } void NewContactWidget::queryPota() { FCT_IDENTIFICATION; if ( callsign.size() >= 3 ) { // use copy constructor - POTA is updated from another thread and reference can be problem const QString ref = PotaQE::instance()->findReferenceId(Callsign(callsign), ui->freqRXEdit->value()).reference; uiDynamic->potaEdit->setText(ref); potaEditFinished(); } } void NewContactWidget::handleDateTimeChangeFromUser() { FCT_IDENTIFICATION; if ( !isManualEnterMode ) return; if ( callsign.isEmpty() ) setDxccInfo(DxccEntity()); else setDxccInfo(callsign); } NewContactDynamicWidgets::NewContactDynamicWidgets(bool allocateWidgets, QWidget *parent) : parent(parent), widgetsAllocated(allocateWidgets) { initializeWidgets(LogbookModel::COLUMN_NAME_INTL, "name", nameLabel, nameEdit); initializeWidgets(LogbookModel::COLUMN_QTH_INTL, "qth", qthLabel, qthEdit); initializeWidgets(LogbookModel::COLUMN_GRID, "grid", gridLabel, gridEdit); initializeWidgets(LogbookModel::COLUMN_COMMENT_INTL, "comment", commentLabel, commentEdit); initializeWidgets(LogbookModel::COLUMN_CONTINENT, "cont", contLabel, contEdit); initializeWidgets(LogbookModel::COLUMN_ITUZ, "itu", ituLabel, ituEdit); initializeWidgets(LogbookModel::COLUMN_CQZ, "cqz", cqzLabel, cqzEdit); initializeWidgets(LogbookModel::COLUMN_STATE, "state", stateLabel, stateEdit); initializeWidgets(LogbookModel::COLUMN_COUNTY, "county", countyLabel, countyEdit); initializeWidgets(LogbookModel::COLUMN_AGE, "age", ageLabel, ageEdit); initializeWidgets(LogbookModel::COLUMN_VUCC_GRIDS, "vucc", vuccLabel, vuccEdit); initializeWidgets(LogbookModel::COLUMN_DARC_DOK, "dok", dokLabel, dokEdit); initializeWidgets(LogbookModel::COLUMN_IOTA, "iota", iotaLabel, iotaEdit); initializeWidgets(LogbookModel::COLUMN_POTA_REF, "pota", potaLabel, potaEdit); initializeWidgets(LogbookModel::COLUMN_SOTA_REF, "sota", sotaLabel, sotaEdit); initializeWidgets(LogbookModel::COLUMN_WWFF_REF, "wwff", wwffLabel, wwffEdit); initializeWidgets(LogbookModel::COLUMN_SIG_INTL, "sig", sigLabel, sigEdit); initializeWidgets(LogbookModel::COLUMN_SIG_INFO_INTL, "sigInfo", sigInfoLabel, sigInfoEdit); initializeWidgets(LogbookModel::COLUMN_EMAIL, "email", emailLabel, emailEdit); initializeWidgets(LogbookModel::COLUMN_WEB, "url", urlLabel, urlEdit); initializeWidgets(LogbookModel::COLUMN_SAT_NAME, "satName", satNameLabel, satNameEdit); initializeWidgets(LogbookModel::COLUMN_SAT_MODE, "satMode", satModeLabel, satModeEdit); initializeWidgets(LogbookModel::COLUMN_CONTEST_ID, "contestID", contestIDLabel, contestIDEdit); initializeWidgets(LogbookModel::COLUMN_SRX_STRING, "srx_string", srxStringLabel, srxStringEdit); initializeWidgets(LogbookModel::COLUMN_STX_STRING, "stx_string", stxStringLabel, stxStringEdit); initializeWidgets(LogbookModel::COLUMN_SRX, "srx", srxLabel, srxEdit); initializeWidgets(LogbookModel::COLUMN_STX, "stx", stxLabel, stxEdit); initializeWidgets(LogbookModel::COLUMN_RX_PWR, "rx_pwr", rxPWRLabel, rxPWREdit); initializeWidgets(LogbookModel::COLUMN_TX_POWER, "power", powerLabel, powerEdit); initializeWidgets(LogbookModel::COLUMN_RIG_INTL, "rigDX", rigLabel, rigEdit); initializeWidgets(LogbookModel::COLUMN_QSLMSG_INTL, "qslMsgS", qslMsgSLabel, qslMsgSEdit); initializeWidgets(LogbookModel::COLUMN_SKCC, "skcc", skccLabel, skccEdit); initializeWidgets(LogbookModel::COLUMN_UKSMG, "uksmg", uksmgLabel, uksmgEdit); initializeWidgets(LogbookModel::COLUMN_FISTS, "fists", fistsLabel, fistsEdit); initializeWidgets(LogbookModel::COLUMN_FISTS_CC, "fistscc", fistsCCLabel, fistsCCEdit); if ( allocateWidgets ) { nameEdit->setMaxLength(50); qthEdit->setMaxLength(75); gridEdit->setMaximumSize(QSize(100, 16777215)); gridEdit->setMaxLength(10); gridEdit->setSizePolicy(QSizePolicy::Policy::Preferred, QSizePolicy::Policy::Maximum); gridEdit->setValidator(new QRegularExpressionValidator(Gridsquare::gridRegEx(), gridEdit)); gridEdit->spaceForbidden(true); contEdit->setMaximumSize(QSize(50, 16777215)); contEdit->setSizeAdjustPolicy(QComboBox::AdjustToContents); contEdit->addItem(QString()); for ( const QString &cont : Data::getContinentList() ) contEdit->addItem(cont); ituEdit->setMaximumSize(QSize(40, 16777215)); ituEdit->setSizePolicy(QSizePolicy::Policy::Preferred, QSizePolicy::Policy::Maximum); ituEdit->setMaxLength(2); ituEdit->setValidator(new QIntValidator(Data::getITUZMin(), Data::getITUZMax(), ituEdit)); ituEdit->spaceForbidden(true); cqzEdit->setMaximumSize(QSize(40, 16777215)); cqzEdit->setMaxLength(2); cqzEdit->setSizePolicy(QSizePolicy::Policy::Preferred, QSizePolicy::Policy::Maximum); cqzEdit->setValidator(new QIntValidator(Data::getCQZMin(), Data::getCQZMax(), cqzEdit)); cqzEdit->spaceForbidden(true); //stateEdit->setMaximumSize(QSize(200, 16777215)); //countyEdit->setMaximumSize(QSize(200, 16777215)); //ageEdit->setMaximumSize(QSize(50, 16777215)); ageEdit->setMaximumSize(QSize(40, 16777215)); ageEdit->setMaxLength(3); ageEdit->setValidator(new QIntValidator(0, 999, ageEdit)); ageEdit->spaceForbidden(true); //vuccEdit->setMaximumSize(QSize(200, 16777215)); vuccEdit->setValidator(new QRegularExpressionValidator(Gridsquare::gridVUCCRegEx(), vuccEdit)); vuccEdit->setToolTip(QCoreApplication::translate("NewContactWidget", "two or four adjacent Maidenhead grid locators, each four characters long, (ex. EN98,FM08,EM97,FM07)", nullptr)); //dokEdit->setMaximumSize(QSize(200, 16777215)); dokEdit->setToolTip(QCoreApplication::translate("NewContactWidget", "the contacted station's DARC DOK (District Location Code) (ex. A01)", nullptr)); //iotaEdit->setMaximumSize(QSize(200, 16777215)); QCompleter *iotaCompleter = new QCompleter(Data::instance()->iotaIDList(), iotaEdit); iotaCompleter->setCaseSensitivity(Qt::CaseInsensitive); iotaCompleter->setFilterMode(Qt::MatchContains); iotaCompleter->setModelSorting(QCompleter::CaseSensitivelySortedModel); iotaEdit->setCompleter(iotaCompleter); iotaEdit->spaceForbidden(true); //potaEdit->setMaximumSize(QSize(200, 16777215)); //potaEdit->setFocusPolicy(Qt::ClickFocus); //it has an external completer //sotaEdit->setMaximumSize(QSize(200, 16777215)); //sotaEdit->setFocusPolicy(Qt::ClickFocus); //it has an external completer //wwffEdit->setMaximumSize(QSize(200, 16777215)); //wwffEdit->setFocusPolicy(Qt::ClickFocus); wwffEdit->setToolTip(QCoreApplication::translate("NewContactWidget", "World Wide Flora & Fauna", nullptr)); //sigEdit->setMaximumSize(QSize(200, 16777215)); //sigEdit->setFocusPolicy(Qt::ClickFocus); sigEdit->setToolTip(QCoreApplication::translate("NewContactWidget", "Special Activity Group", nullptr)); //sigInfoEdit->setMaximumSize(QSize(200, 16777215)); //sigInfoEdit->setFocusPolicy(Qt::ClickFocus); sigInfoEdit->setToolTip(QCoreApplication::translate("NewContactWidget", "Special Activity Group Information", nullptr)); //emailEdit->setMaximumSize(QSize(200, 16777215)); //emailEdit->setFocusPolicy(Qt::ClickFocus); //urlEdit->setMaximumSize(QSize(200, 16777215)); //urlEdit->setFocusPolicy(Qt::ClickFocus); //satNameEdit->setMaximumSize(QSize(200, 16777215)); satNameEdit->setFocusPolicy(Qt::ClickFocus); satNameEdit->setEnabled(false); QSqlTableModel* satModel = new QSqlTableModel(satNameEdit); satModel->setTable("sat_info"); QCompleter *satCompleter = new QCompleter(satNameEdit); satCompleter->setModel(satModel); satCompleter->setCompletionColumn(satModel->fieldIndex("name")); satCompleter->setCaseSensitivity(Qt::CaseInsensitive); satNameEdit->setCompleter(satCompleter); satModel->select(); satModeEdit->setSizeAdjustPolicy(QComboBox::AdjustToContents); satModeEdit->setFocusPolicy(Qt::ClickFocus); satModeEdit->setEnabled(false); QStringList satModesList = Data::instance()->satModeList(); satModesList.prepend(""); QStringListModel* satModesModel = new QStringListModel(satModesList, satModeEdit); satModeEdit->setModel(satModesModel); contestIDEdit->setToolTip(QCoreApplication::translate("NewContactWidget", "It is not the name of the contest but it is an assigned
Contest ID (ex. CQ-WW-CW for CQ WW DX Contest (CW)) ", nullptr)); srxEdit->setValidator(new QIntValidator(0,INT_MAX, srxEdit)); stxEdit->setValidator(new QIntValidator(0,INT_MAX, stxEdit)); stxEdit->setText("001"); rxPWREdit->setValidator(new QDoubleValidator(0, 100000.0, 9, rxPWREdit)); powerEdit->setMaximum(1000000.0); powerEdit->setValue(0.0); powerEdit->setDecimals(3); powerEdit->setSpecialValueText(QCoreApplication::translate("NewContactWidget", "Blank")); powerEdit->setSuffix(QCoreApplication::translate("NewContactWidget", " W")); rigEdit->setToolTip(QCoreApplication::translate("NewContactWidget", "Description of the contacted station's equipment", nullptr)); uksmgEdit->setValidator(new QIntValidator(0, INT_MAX, uksmgEdit)); fistsEdit->setValidator(new QIntValidator(0, INT_MAX, fistsEdit)); fistsCCEdit->setValidator(new QIntValidator(0, INT_MAX, fistsCCEdit)); } } QWidget *NewContactDynamicWidgets::getRowWidget(int index) { FCT_IDENTIFICATION; if ( !widgetMapping.contains(index) ) return nullptr; widgetMapping.value(index).rowWidget->setHidden(false); widgetMapping.value(index).label->setHidden(false); widgetMapping.value(index).label->setFocusPolicy(Qt::NoFocus); widgetMapping.value(index).editor->setHidden(false); widgetMapping.value(index).editor->setFocusPolicy(Qt::StrongFocus); // recreate layout because getLabel destroy parent widgetMapping.value(index).rowWidget->layout()->addWidget(widgetMapping.value(index).label); widgetMapping.value(index).rowWidget->layout()->addWidget(widgetMapping.value(index).editor); return widgetMapping.value(index).rowWidget; } QWidget *NewContactDynamicWidgets::getLabel(int index) { FCT_IDENTIFICATION; if ( !widgetMapping.contains(index) ) return nullptr; widgetMapping.value(index).label->setHidden(false); widgetMapping.value(index).label->setFocusPolicy(Qt::NoFocus); return widgetMapping.value(index).label; } QWidget *NewContactDynamicWidgets::getEditor(int index) { FCT_IDENTIFICATION; if ( !widgetMapping.contains(index) ) return nullptr; widgetMapping.value(index).editor->setHidden(false); widgetMapping.value(index).editor->setFocusPolicy(Qt::ClickFocus); return widgetMapping.value(index).editor; } QStringList NewContactDynamicWidgets::getAllFieldLabelNames() const { FCT_IDENTIFICATION; QStringList ret; const QList &dynWidget = widgetMapping.values(); for (const DynamicWidget &widget : dynWidget) { ret << widget.fieldLabelName; } return ret; } int NewContactDynamicWidgets::getIndex4FieldLabelName(const QString &value) const { FCT_IDENTIFICATION; QHashIterator i(widgetMapping); while ( i.hasNext() ) { i.next(); if ( i.value().fieldLabelName == value ) return i.key(); } return -1; } QString NewContactDynamicWidgets::getFieldLabelName4Index(int i) const { FCT_IDENTIFICATION; if ( !widgetMapping.contains(i) ) return QString(); return widgetMapping.value(i).fieldLabelName; } template void NewContactDynamicWidgets::initializeWidgets(LogbookModel::ColumnID DBIndexMapping, const QString &objectName, QLabel *&retLabel, WidgetType *&retWidget) { FCT_IDENTIFICATION; DynamicWidget widget; widget.fieldLabelName = LogbookModel::getFieldNameTranslation(DBIndexMapping); widget.baseObjectName = objectName; widget.label = retLabel = nullptr; widget.editor = retLabel = nullptr; widget.rowWidget = retLabel = nullptr; if ( widgetsAllocated ) { QWidget *rowWidget = new QWidget(parent); rowWidget->setObjectName(objectName + "Widget"); QVBoxLayout *rowWidgetLayout = new QVBoxLayout(rowWidget); rowWidgetLayout->setSpacing(0); rowWidgetLayout->setObjectName(objectName + "Layout"); rowWidgetLayout->setContentsMargins(0,0,0,0); widget.label = retLabel = new QLabel(widget.fieldLabelName, rowWidget); retLabel->setObjectName(objectName + "Label"); widget.editor = retWidget = new WidgetType(rowWidget); retWidget->setObjectName(objectName + "Edit"); rowWidgetLayout->addWidget(retLabel); rowWidgetLayout->addWidget(retWidget); rowWidget->hide(); widget.rowWidget = rowWidget; } widgetMapping[DBIndexMapping] = widget; } ================================================ FILE: ui/NewContactWidget.h ================================================ #ifndef QLOG_UI_NEWCONTACTWIDGET_H #define QLOG_UI_NEWCONTACTWIDGET_H #include #include #include #include #include #include #include #include #include #include #include "data/DxSpot.h" #include "rig/Rig.h" #include "core/CallbookManager.h" #include "data/StationProfile.h" #include "core/PropConditions.h" #include "core/LogLocale.h" #include "models/LogbookModel.h" #include "ui/component/EditLine.h" #include "data/DxSpot.h" #include "ui/component/MultiselectCompleter.h" #include "data/POTAEntity.h" #include "data/SOTAEntity.h" #include "data/WWFFEntity.h" #include "component/ShutdownAwareWidget.h" #include "ui/component/BaseDoubleSpinBox.h" namespace Ui { class NewContactWidget; } class ModeSelectionController; enum CoordPrecision { COORD_NONE = 0, COORD_DXCC = 1, COORD_GRID = 2, COORD_FULL = 3 }; class NewContactDynamicWidgets { public: QLabel *nameLabel; NewContactEditLine *nameEdit; QLabel *qthLabel; NewContactEditLine *qthEdit; QLabel *gridLabel; NewContactEditLine *gridEdit; QLabel *commentLabel; NewContactEditLine *commentEdit; QLabel *contLabel; QComboBox *contEdit; QLabel *ituLabel; NewContactEditLine *ituEdit; QLabel *cqzLabel; NewContactEditLine *cqzEdit; QLabel *stateLabel; NewContactEditLine *stateEdit; QLabel *countyLabel; NewContactEditLine *countyEdit; QLabel *ageLabel; NewContactEditLine *ageEdit; QLabel *vuccLabel; NewContactEditLine *vuccEdit; QLabel *dokLabel; NewContactEditLine *dokEdit; QLabel *iotaLabel; NewContactEditLine *iotaEdit; QLabel *potaLabel; NewContactEditLine *potaEdit; QLabel *sotaLabel; NewContactEditLine *sotaEdit; QLabel *wwffLabel; NewContactEditLine *wwffEdit; QLabel *sigLabel; NewContactEditLine *sigEdit; QLabel *sigInfoLabel; NewContactEditLine *sigInfoEdit; QLabel *emailLabel; NewContactEditLine *emailEdit; QLabel *urlLabel; NewContactEditLine *urlEdit; QLabel *satNameLabel; NewContactEditLine *satNameEdit; QLabel *satModeLabel; QComboBox *satModeEdit; QLabel *contestIDLabel; NewContactEditLine *contestIDEdit; QLabel *srxStringLabel; NewContactEditLine *srxStringEdit; QLabel *stxStringLabel; NewContactEditLine *stxStringEdit; QLabel *srxLabel; NewContactEditLine *srxEdit; QLabel *stxLabel; NewContactEditLine *stxEdit; QLabel *rxPWRLabel; NewContactEditLine *rxPWREdit; QLabel *powerLabel; BaseDoubleSpinBox *powerEdit; QLabel *rigLabel; NewContactEditLine *rigEdit; QLabel *qslMsgSLabel; NewContactEditLine *qslMsgSEdit; QLabel *skccLabel; NewContactEditLine *skccEdit; QLabel *uksmgLabel; NewContactEditLine *uksmgEdit; QLabel *fistsLabel; NewContactEditLine *fistsEdit; QLabel *fistsCCLabel; NewContactEditLine *fistsCCEdit; explicit NewContactDynamicWidgets(bool allocateWidgets, QWidget *parent); QWidget* getRowWidget(int index); QWidget* getLabel(int index); QWidget* getEditor(int index); QStringList getAllFieldLabelNames() const; int getIndex4FieldLabelName(const QString&) const; QString getFieldLabelName4Index(int) const; private: struct DynamicWidget { QWidget* label; QWidget* editor; QWidget* rowWidget; QString baseObjectName; QString fieldLabelName; }; template void initializeWidgets(LogbookModel::ColumnID DBIndexMapping, const QString &objectName, QLabel *&retLabel, WidgetType *&retWidget); // Mapping from DB Index to QHash widgetMapping; QWidget *parent; bool widgetsAllocated; }; class NewContactWidget : public QWidget, public ShutdownAwareWidget { Q_OBJECT public: explicit NewContactWidget(QWidget *parent = nullptr); ~NewContactWidget(); void assignPropConditions(PropConditions *); QString getCallsign() const; QString getName() const; QString getRST() const; QString getGreeting() const; QString getQTH() const; QString getMyCallsign() const; QString getMyName() const; QString getMyQTH() const; QString getMyLocator() const; QString getMySIG() const; QString getMySIGInfo() const; QString getMyIOTA() const; QString getMySOTA() const; QString getMyPOTA() const; QString getMyWWFT() const; QString getMyVUCC() const; QString getMyPWR() const; QString getBand() const; QString getMode() const; QString getSentNr() const; QString getSentExch() const; double getQSOBearing() const; double getQSODistance() const; bool getTabCollapseState() const; virtual void finalizeBeforeAppExit() override; signals: void contactAdded(QSqlRecord record); void newTarget(double lat, double lon); void filterCallsign(QString call); void userFrequencyChanged(VFOID, double, double, double); void userModeChanged(VFOID, const QString &, const QString &mode, const QString &subMode, qint32 width); void markQSO(DxSpot spot); void callboolImageUrl(const QString&); void contestStarted(const QString contestID, const QDateTime date); void rigProfileChanged(); void callsignChanged(const QString& callsign); void contactReset(); public slots: void refreshRigProfileCombo(); void saveExternalContact(QSqlRecord record); void readGlobalSettings(); void tuneDx(const DxSpot &spot); void fillCallsignGrid(const QString &callsign, const QString& grid); void prepareWSJTXQSO(const QString &receivedCallsign, const QString &grid, const QString &id); void resetContact(); void saveContact(); // to receive RIG instructions void changeFrequency(VFOID, double, double, double); void changeSplit(VFOID, bool); void changeModeWithoutSignals(const QString &mode, const QString &subMode); void changeModefromRig(VFOID, const QString &rawMode, const QString &mode, const QString &subMode, qint32 width); void changePower(VFOID, double power); void rigConnected(); void rigDisconnected(); void setNearestSpot(const DxSpot &); void setNearestSpotColor(); void setManualMode(bool); void exitManualMode(); void refreshStationProfileCombo(); void refreshAntProfileCombo(); void stationProfileComboChanged(const QString&); void setValuesFromActivity(const QString &); void markContact(); void useNearestCallsign(); void setupCustomUi(); void resetSTXSeq(); void stopContest(); void refreshCallsignsColors(); void changeSRXStringLink(int); private slots: void handleCallsignFromUser(); void frequencyTXChanged(); void frequencyRXChanged(); void changeMode(); void subModeChanged(); void gridChanged(); void updateTime(); void updateTimeOff(); void startContactTimer(); void stopContactTimer(); void finalizeCallsignEdit(); void setMembershipList(const QString&, QMap); void setCallbookFields(const CallbookResponseData &data); void propModeChanged(const QString&); void sotaChanged(const QString&); void sotaEditFinished(); void potaChanged(const QString&); void potaEditFinished(); void wwffEditFinished(); void wwffChanged(const QString&); void formFieldChangedString(const QString&); void formFieldChanged(); void setCallbookStatusEnabled(bool); void changeCallbookSearchStatus(); void satNameChanged(); void rigProfileComboChanged(const QString&); void antProfileComboChanged(const QString&); void webLookup(); void refreshSIGCompleter(); void refreshContestCompleter(); void tabsExpandCollapse(); void setContestFieldsState(); void queryPota(); void handleDateTimeChangeFromUser(); private: void useFieldsFromPrevQSO(const QString &callsign, const QString &grid = QString()); void setDxccInfo(const DxccEntity &curr); void setDxccInfo(const QString &callsign); void clearCallbookQueryFields(); void clearMemberQueryFields(); void readWidgetSettings(); void writeWidgetSetting(); void __modeChanged(); void updateTXBand(double freq); void updateRXBand(double freq); void updateCoordinates(double lat, double lon, CoordPrecision prec); void clearCoordinates(); void updateDxccStatus(); void updatePartnerLocTime(); void setDefaultReport(); void addAddlFields(QSqlRecord &record, const StationProfile &profile); bool eventFilter(QObject *object, QEvent *event) override; bool isQSOTimeStarted(); void QSYContactWiping(double); void connectFieldChanged(); void changeCallsignManually(const QString &); void changeCallsignManually(const QString &, double); void __changeFrequency(VFOID, double vfoFreq, double ritFreq, double xitFreq); void showRXTXFreqs(bool); void setComboBaseData(QComboBox *, const QString &); void queryMemberList(); QList setupCustomUiRow(QHBoxLayout *row, const QList& widgetsList); QList setupCustomDetailColumn(QFormLayout *column, const QList& widgetsList); void setupCustomUiRowsTabOrder(const QList &customWidgets); void setBandLabel(const QString &); void updateSatMode(); bool isPOTAValid(POTAEntity *entity); bool isSOTAValid(SOTAEntity *entity); bool isWWFFValid(WWFFEntity *entity); void useNearestSpotInfo(const QString &in_callsign); void updateCountyCompleter(int dxcc); bool shouldStartContest(); void startContest(const QDateTime &date); void setSTXSeq(); void setSTXSeq(int newValue); void updateNearestSpotDupe(); void checkDupe(); private: Rig* rig; double realRigFreq; double realFreqForManualExit; QString callsign; double dxDistance; // QSO distance in km - used for ADIF and internal usage DxccEntity dxccEntity; QString defaultReport; CallbookManager callbookManager; QTimer* contactTimer; Ui::NewContactWidget *ui; NewContactDynamicWidgets *uiDynamic; CoordPrecision coordPrec; PropConditions *prop_cond; QCompleter *satCompleter; QCompleter *sotaCompleter; QCompleter *countyCompleter; MultiselectCompleter *potaCompleter; QCompleter *wwffCompleter; QCompleter *sigCompleter; QCompleter *contestCompleter; QTimeZone partnerTimeZone; double QSOFreq; double QSOTxFreq; double prevQSOTxFreq; qint32 bandwidthFilter; bool rigOnline; CallbookResponseData lastCallbookQueryData; SOTAEntity lastSOTA; POTAEntity lastPOTA; WWFFEntity lastWWFF; bool isManualEnterMode; bool rigSplitEnabled; LogLocale locale; QDateTime timeOff; bool callbookSearchPaused; Band bandTX; Band bandRX; QSqlQuery prevQSOExactMatchQuery; QSqlQuery prevQSOBaseCallMatchQuery; bool isPrevQSOExactMatchQuery; bool isPrevQSOBaseCallMatchQuery; DxSpot nearestSpot; QToolButton *tabCollapseBtn; ModeSelectionController *modeController; }; #endif // QLOG_UI_NEWCONTACTWIDGET_H ================================================ FILE: ui/NewContactWidget.ui ================================================ NewContactWidget 0 0 971 425 0 0 Qt::TabFocus Form 4 4 4 4 0 0 0 0 QLayout::SetMinimumSize 0 6 6 0 0 0 65 16777215 20 59 6 0 0 Frequency Qt::AlignCenter 4 0 0 Callsign 0 0 20 20 Qt::ClickFocus true Qt::AlignCenter Qt::NoTextInteraction QLabel { color : red; } <b>DUPE !!!</b> Qt::AlignCenter 1 2 0 0 RX: 0 0 Qt::ClickFocus false MHz 5 7500000.000000000000000 0.001000000000000 3.500000000000000 0 0 TX: true 0 0 Qt::ClickFocus false MHz 5 7500000.000000000000000 0.001000000000000 3.500000000000000 1 true 80m true 80m RSTs RSTr 0 0 150 0 300 16777215 20 true 25 0 0 65 16777215 20 59 6 1 0 0 Qt::ClickFocus QComboBox::AdjustToContents 0 0 Qt::ClickFocus QComboBox::AdjustToContents Mode Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter 6 0 0 0 0 6 3 3 0 0 0 0 0 true 0 0 Qt::NoFocus :/icons/baseline-stop-24px.svg:/icons/baseline-stop-24px.svg 12 16 0 0 Qt::NoFocus Save .. Qt::Horizontal 40 20 Qt::Horizontal QSizePolicy::Maximum 20 20 0 0 Qt::NoFocus Lookup the call on the web. The query URL can be changed in Settings -> Callbook Web :/icons/baseline-search-24px.svg:/icons/baseline-search-24px.svg Qt::Horizontal QSizePolicy::Maximum 20 20 0 0 Qt::ClickFocus true true Qt::UTC 0 0 Qt::ClickFocus true hh:mm:ss true Qt::UTC 0 0 Time On 0 0 Qt::NoFocus Reset .. 0 0 Qt::NoFocus :/icons/baseline-play_arrow-24px.svg:/icons/baseline-play_arrow-24px.svg 12 16 0 0 Date Duration 0 0 Qt::ClickFocus true 1999 12 31 1999 12 31 QDateTimeEdit::HourSection HH:mm:ss Qt::UTC 0 0 200 0 Info 0 0 0 0 0 0 0 0 0 13 true Qt::AlignCenter true -1 0 0 64 64 100000 64 Qt::AlignCenter 0 0 0 50 0 9 false Qt::AlignCenter 0 0 0 0 9 false Qt::AlignCenter 9 Qt::AlignCenter 9 Qt::AlignCenter 10 Qt::AlignCenter true Qt::Vertical QSizePolicy::Fixed 10 10 0 0 Qt::NoFocus 0 0 0 &Details Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop QSL Send Status 0 0 Paper 0 0 50 0 Qt::ClickFocus <b>Yes</b> - an outgoing QSL card has been sent; the QSO has been uploaded to, and accepted by, the online service<br/><b>No</b> - do not send an outgoing QSL card; do not upload the QSO to the online service<br/><b>Requested</b> - the contacted station has requested a QSL card; the contacted station has requested the QSO be uploaded to the online service<br/><b>Queued</b> - an outgoing QSL card has been selected to be sent; a QSO has been selected to be uploaded to the online service<br/> QComboBox::AdjustToContents 0 0 LoTW 0 0 50 0 Qt::ClickFocus <b>Yes</b> - an outgoing QSL card has been sent; the QSO has been uploaded to, and accepted by, the online service<br/><b>No</b> - do not send an outgoing QSL card; do not upload the QSO to the online service<br/><b>Requested</b> - the contacted station has requested a QSL card; the contacted station has requested the QSO be uploaded to the online service<br/><b>Queued</b> - an outgoing QSL card has been selected to be sent; a QSO has been selected to be uploaded to the online service<br/> QComboBox::AdjustToContents 0 0 eQSL 0 0 50 0 Qt::ClickFocus <b>Yes</b> - an outgoing QSL card has been sent; the QSO has been uploaded to, and accepted by, the online service<br/><b>No</b> - do not send an outgoing QSL card; do not upload the QSO to the online service<br/><b>Requested</b> - the contacted station has requested a QSL card; the contacted station has requested the QSO be uploaded to the online service<br/><b>Queued</b> - an outgoing QSL card has been selected to be sent; a QSO has been selected to be uploaded to the online service<br/> QComboBox::AdjustToContents Qt::Horizontal 0 20 QSL Send via 0 0 Paper 0 0 50 0 Qt::ClickFocus QComboBox::AdjustToContents Qt::Horizontal 40 20 QSL via 0 0 0 200 0 Qt::ClickFocus 25 Qt::Horizontal QSizePolicy::Minimum 20 10 true 9 Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 6 true 9 Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter 5 5 Qt::Horizontal 40 20 Propagation Mode 0 0 300 0 Qt::ClickFocus 0 0 D&X Stats 0 0 0 <b>DXCC Statistics</b> 0 0 Qt::NoFocus Qt::ScrollBarAlwaysOff Qt::ScrollBarAlwaysOff QAbstractScrollArea::AdjustToContents QAbstractItemView::NoEditTriggers true true QAbstractItemView::NoSelection QAbstractItemView::SelectItems Qt::SolidLine 20 45 false 23 23 <b>Station Statistics</b> 0 0 Qt::NoFocus Qt::ScrollBarAlwaysOff Qt::ScrollBarAlwaysOff QAbstractScrollArea::AdjustToContents QAbstractItemView::NoEditTriggers true QAbstractItemView::NoSelection 20 45 23 23 0 0 M&y Station Station Qt::ClickFocus QComboBox::AdjustToContents Rig 0 0 Qt::ClickFocus QComboBox::AdjustToContents Antenna 0 0 Qt::ClickFocus QComboBox::AdjustToContents Qt::Horizontal 40 20 0 0 My &Notes 0 0 Qt::ClickFocus QAbstractScrollArea::AdjustToContents false Qt::TextEditorInteraction 6 0 0 Member: 0 0 Qt::Horizontal 40 20 NewContactEditLine QLineEdit
ui/component/EditLine.h
DxccTableWidget QTableView
ui/DxccTableWidget.h
FreqQSpinBox QDoubleSpinBox
ui/component/FreqQSpinBox.h
NewContactRSTEditLine QLineEdit
ui/component/EditLine.h
callsignEdit rstSentEdit rstRcvdEdit noteEdit callsignEdit textChanged(QString) NewContactWidget handleCallsignFromUser() 177 54 389 176 freqRXEdit valueChanged(double) NewContactWidget frequencyRXChanged() 727 53 516 217 modeEdit currentTextChanged(QString) NewContactWidget changeMode() 427 69 516 263 callsignEdit returnPressed() NewContactWidget handleCallsignFromUser() 115 77 352 281 submodeEdit currentIndexChanged(int) NewContactWidget subModeChanged() 519 69 453 253 callsignEdit editingFinished() NewContactWidget finalizeCallsignEdit() 67 63 453 253 propagationModeEdit currentTextChanged(QString) NewContactWidget propModeChanged(QString) 863 392 562 290 stationProfileCombo currentTextChanged(QString) NewContactWidget stationProfileComboChanged(QString) 212 231 669 310 freqTXEdit valueChanged(double) NewContactWidget frequencyTXChanged() 727 87 669 310 rigEdit currentTextChanged(QString) NewContactWidget rigProfileComboChanged(QString) 212 262 736 398 antennaEdit currentTextChanged(QString) NewContactWidget antProfileComboChanged(QString) 212 293 736 398 webLookupButton clicked() NewContactWidget webLookup() 726 146 489 262 stopTimerButton clicked() NewContactWidget stopContactTimer() 380 146 489 262 saveButton clicked() NewContactWidget saveContact() 512 146 489 262 startTimerButton clicked() NewContactWidget startContactTimer() 246 146 489 262 resetButton clicked() NewContactWidget resetContact() 598 146 489 262 callbookStatusButton clicked() NewContactWidget changeCallbookSearchStatus() 75 17 485 185 dateEdit dateTimeChanged(QDateTime) NewContactWidget handleDateTimeChangeFromUser() 61 136 485 202 timeOnEdit dateTimeChanged(QDateTime) NewContactWidget handleDateTimeChangeFromUser() 168 136 485 202 saveContact() resetContact() handleCallsignFromUser() gridChanged() stopContactTimer() frequencyTXChanged() changeMode() updateTime() webLookup() startContactTimer() updateTimeStop() subModeChanged() propModeChanged(QString) finalizeCallsignEdit() freqTXOffsetChanged(double) stationProfileComboChanged(QString) sotaChanged(QString) freqRXOffsetChanged(double) frequencyRXChanged() rigProfileComboChanged(QString) antProfileComboChanged(QString) sotaEditFinished() wwffEditFinished() wwffChanged(QString) timeOnChanged() potaEditFinished() potaChanged(QString) changeCallbookSearchStatus() resetSTXCounters() handleDateTimeChangeFromUser()
================================================ FILE: ui/OnlineMapWidget.cpp ================================================ #include #include #include #include #include #include #include #include #include "OnlineMapWidget.h" #include "core/debug.h" #include "data/Gridsquare.h" #include "data/StationProfile.h" #include "data/AntProfile.h" #include "core/debug.h" #include "core/PropConditions.h" #include "data/Band.h" #include "data/Data.h" #include "rotator/Rotator.h" #include "rig/Rig.h" #include "data/BandPlan.h" #include "rig/macros.h" #include "core/LogParam.h" MODULE_IDENTIFICATION("qlog.ui.onlinemapwidget"); OnlineMapWidget::OnlineMapWidget(QWidget *parent): QWebEngineView(parent), main_page(new WebEnginePage(this)), isMainPageLoaded(false), webChannelHandler("onlinemap",parent), prop_cond(nullptr), contact(nullptr), lastSeenAzimuth(0.0), lastSeenElevation(0.0), isRotConnected(false) { FCT_IDENTIFICATION; main_page->setWebChannel(&channel); setPage(main_page); main_page->load(QUrl(QLatin1String("qrc:/res/map/onlinemap.html"))); connect(this, &OnlineMapWidget::loadFinished, this, &OnlineMapWidget::finishLoading); setFocusPolicy(Qt::ClickFocus); setContextMenuPolicy(Qt::NoContextMenu); channel.registerObject("layerControlHandler", &webChannelHandler); double freq = LogParam::getNewContactFreq(); freq += RigProfilesManager::instance()->getCurProfile1().ritOffset; setIBPBand(VFO1, 0.0, freq, 0.0); connect(&webChannelHandler, &MapWebChannelHandler::chatCallsignPressed, this, &OnlineMapWidget::chatCallsignTrigger); connect(&webChannelHandler, &MapWebChannelHandler::wsjtxCallsignPressed, this, &OnlineMapWidget::wsjtxCallsignTrigger); connect(&webChannelHandler, &MapWebChannelHandler::IBPPressed, this, &OnlineMapWidget::IBPCallsignTrigger); } void OnlineMapWidget::setTarget(double lat, double lon) { FCT_IDENTIFICATION; qCDebug(function_parameters) << lat << " " << lon; QString targetJavaScript; if ( ! qIsNaN(lat) && ! qIsNaN(lon) ) { /* Draw a new path */ Gridsquare myGrid(StationProfilesManager::instance()->getCurProfile1().locator); if ( myGrid.isValid() ) { targetJavaScript += QString("drawPath([{lat: %1, lng: %2}, {lat: %3, lng: %4}]);").arg(myGrid.getLatitude()) .arg(myGrid.getLongitude()) .arg(lat) .arg(lon); } } else targetJavaScript = QLatin1String("drawPath([]);"); runJavaScript(targetJavaScript); // redraw ant path because QSO distance can change antPositionChanged(lastSeenAzimuth, lastSeenElevation); } void OnlineMapWidget::changeTheme(int theme, bool isDark) { FCT_IDENTIFICATION; qCDebug(function_parameters) << theme << isDark; QString themeJavaScript; //theme == 1 dart themeJavaScript = (isDark == 1) ? QLatin1String( "map.getPanes().tilePane.style.webkitFilter=\"brightness(0.6) invert(1) " "contrast(3) hue-rotate(200deg) saturate(0.3) brightness(0.9)\";") : QLatin1String("map.getPanes().tilePane.style.webkitFilter=\"\";"); runJavaScript(themeJavaScript); } void OnlineMapWidget::auroraDataUpdate() { FCT_IDENTIFICATION; QStringList mapPoints; if ( !prop_cond ) return; if ( prop_cond->isAuroraMapValid() ) { const QList::MapPoint> &points = prop_cond->getAuroraPoints(); for ( const GenericValueMap::MapPoint &point : points ) { if ( point.value > 10 ) { mapPoints << QString("{lat: %1, lng: %2, count: %3}").arg(point.latitude) .arg(point.longitude) .arg(point.value) << QString("{lat: %1, lng: %2, count: %3}").arg(point.latitude) .arg(point.longitude - 360) .arg(point.value); } } } runJavaScript(QString(" auroraLayer.setData({max: 100, data:[%1]});").arg(mapPoints.join(","))); } void OnlineMapWidget::mufDataUpdate() { FCT_IDENTIFICATION; QStringList mapPoints; if ( !prop_cond ) return; if ( prop_cond->isMufMapValid() ) { const QList::MapPoint> &points = prop_cond->getMUFPoints(); for ( const GenericValueMap::MapPoint &point : points ) { mapPoints << QString("['%1', %2, %3]").arg(QString::number(point.value,'f',0)) .arg(point.latitude) .arg(point.longitude) << QString("['%1', %2, %3]").arg(QString::number(point.value,'f',0)) .arg(point.latitude) .arg(point.longitude - 360); } } runJavaScript(QString(" drawMuf([%1]);").arg(mapPoints.join(","))); } void OnlineMapWidget::setIBPBand(VFOID vfoid, double, double ritFreq, double) { FCT_IDENTIFICATION; // Online map tracks the RX frequency only if ( vfoid == VFO2 ) return; runJavaScript(QString("currentBand=\"%1\";").arg(BandPlan::freq2Band(ritFreq).name)); } void OnlineMapWidget::antPositionChanged(double in_azimuth, double in_elevation) { FCT_IDENTIFICATION; qCDebug(function_parameters) << in_azimuth << " " << in_elevation; if ( ! isRotConnected ) return; QString targetJavaScript; lastSeenAzimuth = in_azimuth; lastSeenElevation = in_elevation; /* Draw a new path */ Gridsquare myGrid(StationProfilesManager::instance()->getCurProfile1().locator); if ( myGrid.isValid() ) { double beamLen = 3000; // in km double azimuthBeamWidth = AntProfilesManager::instance()->getCurProfile1().azimuthBeamWidth; if ( contact ) { double newBeamLen = contact->getQSODistance(); if ( !qIsNaN(newBeamLen) ) { beamLen = newBeamLen; } } targetJavaScript += QString("drawAntPath({lat: %1, lng: %2}, %3, %4, %5);").arg(myGrid.getLatitude()) .arg(myGrid.getLongitude()) .arg(beamLen) .arg(in_azimuth) .arg(azimuthBeamWidth); } else { // clean paths targetJavaScript = QLatin1String("drawAntPath({}, 0, 0, 0);"); } runJavaScript(targetJavaScript); } void OnlineMapWidget::rotConnected() { FCT_IDENTIFICATION; isRotConnected = true; Rotator::instance()->sendState(); } void OnlineMapWidget::rotDisconnected() { FCT_IDENTIFICATION; isRotConnected = false; // clear the Ant Path runJavaScript(QLatin1String("drawAntPath({}, 0, 0, 0);")); } void OnlineMapWidget::finishLoading(bool) { FCT_IDENTIFICATION; if ( isMainPageLoaded ) return; isMainPageLoaded = true; /* which layers will be active */ postponedScripts += webChannelHandler.generateMapMenuJS(true, true, true, true, true, true, true, true); main_page->runJavaScript(postponedScripts); postponedScripts = QString(); webChannelHandler.restoreLayerControlStates(main_page); flyToMyQTH(); auroraDataUpdate(); } void OnlineMapWidget::chatCallsignTrigger(const QString &callsign) { FCT_IDENTIFICATION; qCDebug(function_parameters) << callsign; emit chatCallsignPressed(callsign); } void OnlineMapWidget::wsjtxCallsignTrigger(const QString &callsign) { FCT_IDENTIFICATION; qCDebug(function_parameters) << callsign; emit wsjtxCallsignPressed(callsign); } void OnlineMapWidget::IBPCallsignTrigger(const QString &callsign, double freq) { FCT_IDENTIFICATION; qCDebug(function_parameters) << callsign << freq; Rig::instance()->setFrequency(MHz(freq)); Rig::instance()->setMode("CW", QString()); } void OnlineMapWidget::runJavaScript(const QString &js) { FCT_IDENTIFICATION; qCDebug(function_parameters) << js; if ( !isMainPageLoaded ) postponedScripts.append(js); else main_page->runJavaScript(js); } void OnlineMapWidget::flyToMyQTH() { FCT_IDENTIFICATION; /* focus current location */ Gridsquare myGrid(StationProfilesManager::instance()->getCurProfile1().locator); if ( myGrid.isValid() ) { QString currentProfilePosition(QString("[\"\", %1, %2, yellowIcon]").arg(myGrid.getLatitude()) .arg(myGrid.getLongitude())); runJavaScript(QString("flyToPoint(%1, 4);").arg(currentProfilePosition)); } // redraw ant path because QSO distance can change antPositionChanged(lastSeenAzimuth, lastSeenElevation); } void OnlineMapWidget::drawChatUsers(const QList &list) { FCT_IDENTIFICATION; QList chatUsers; for ( const KSTUsersInfo &user : list ) { if ( user.grid.isValid() ) { chatUsers.append(QString("[\"%1\", %2, %3, %4]").arg(user.callsign) .arg(user.grid.getLatitude()) .arg(user.grid.getLongitude()) .arg("yellowIcon")); } } runJavaScript(QString("drawPointsGroup3([%1]);").arg(chatUsers.join(","))); } void OnlineMapWidget::drawWSJTXSpot(const WsjtxEntry &spot) { FCT_IDENTIFICATION; Gridsquare spotGrid(spot.grid); if ( spotGrid.isValid() ) { runJavaScript(QString("addWSJTXSpot(%1, %2, \"%3\", \"%4\");").arg(spotGrid.getLatitude()) .arg(spotGrid.getLongitude()) .arg(spot.callsign, Data::colorToHTMLColor(Data::statusToColor(spot.status, spot.dupeCount, QColor(Qt::white))))); } } void OnlineMapWidget::clearWSJTXSpots() { FCT_IDENTIFICATION; runJavaScript(QLatin1String("clearWSJTXSpots();")); } OnlineMapWidget::~OnlineMapWidget() { FCT_IDENTIFICATION; main_page->deleteLater(); } void OnlineMapWidget::assignPropConditions(PropConditions *conditions) { FCT_IDENTIFICATION; prop_cond = conditions; } void OnlineMapWidget::registerContactWidget(const NewContactWidget *contactWidget) { FCT_IDENTIFICATION; contact = contactWidget; } ================================================ FILE: ui/OnlineMapWidget.h ================================================ #ifndef QLOG_UI_ONLINEMAPWIDGET_H #define QLOG_UI_ONLINEMAPWIDGET_H #include #include #include #include "ui/MapWebChannelHandler.h" #include "core/PropConditions.h" #include "ui/WebEnginePage.h" #include "rig/Rig.h" #include "ui/NewContactWidget.h" #include "service/kstchat/KSTChat.h" #include "data/WsjtxEntry.h" namespace Ui { class OnlineMapWidget; } class OnlineMapWidget : public QWebEngineView { Q_OBJECT public: explicit OnlineMapWidget(QWidget* parent = nullptr); ~OnlineMapWidget(); void assignPropConditions(PropConditions *); void registerContactWidget(const NewContactWidget*); signals: void chatCallsignPressed(QString); void wsjtxCallsignPressed(QString); public slots: void setTarget(double lat, double lon); void changeTheme(int, bool isDark); void auroraDataUpdate(); void mufDataUpdate(); void setIBPBand(VFOID, double, double, double); void antPositionChanged(double in_azimuth, double in_elevation); void rotConnected(); void rotDisconnected(); void flyToMyQTH(); void drawChatUsers(const QList &list); void drawWSJTXSpot(const WsjtxEntry &spot); void clearWSJTXSpots(); protected slots: void finishLoading(bool); void chatCallsignTrigger(const QString&); void wsjtxCallsignTrigger(const QString&); void IBPCallsignTrigger(const QString&, double); private: WebEnginePage *main_page; bool isMainPageLoaded; QString postponedScripts; QWebChannel channel; MapWebChannelHandler webChannelHandler; PropConditions *prop_cond; const NewContactWidget *contact; double lastSeenAzimuth, lastSeenElevation; bool isRotConnected; void runJavaScript(const QString &); }; #endif // QLOG_UI_ONLINEMAPWIDGET_H ================================================ FILE: ui/PaperQSLDialog.cpp ================================================ #include #include #include #include #include #include #include #include #include "PaperQSLDialog.h" #include "ui_PaperQSLDialog.h" #include "core/debug.h" MODULE_IDENTIFICATION("qlog.ui.paperqsldialog"); extern QTemporaryDir tempDir; PaperQSLDialog::PaperQSLDialog(const QSqlRecord &qso, QWidget *parent) : QDialog(parent), ui(new Ui::PaperQSLDialog), qsl(new QSLStorage(this)), dialogQSORecord(qso), fileCount(0) { FCT_IDENTIFICATION; ui->setupUi(this); showAvailableFiles(); } PaperQSLDialog::~PaperQSLDialog() { FCT_IDENTIFICATION; delete qsl; delete ui; } void PaperQSLDialog::addFileClick() { FCT_IDENTIFICATION; QSettings settings; //platform-dependent, must be present const QString &lastPath = settings.value("paperqslimport/last_path", QDir::homePath()).toString(); QString filename = QFileDialog::getOpenFileName(this, tr("Add File"), lastPath, #if defined(Q_OS_WIN) "", #elif defined(Q_OS_MACOS) "", #else "", #endif nullptr, #if defined(Q_OS_LINUX) // Do not use the Native Dialog under Linux because the dialog is case-sensitive. // QT variant looks different but it is case-insensitive. // More information: // https://stackoverflow.com/questions/34858220/qt-how-to-set-a-case-insensitive-filter-on-qfiledialog // https://bugreports.qt.io/browse/QTBUG-51712 QFileDialog::DontUseNativeDialog #else QFileDialog::Options() #endif ); if ( !filename.isEmpty() ) { qCDebug(runtime) << "selected file " << filename; QFile file(filename); if ( !file.open(QIODevice::ReadOnly) ) return; settings.setValue("paperqslimport/last_path", QFileInfo(filename).path()); if ( qsl->add(QSLObject(dialogQSORecord, QSLObject::QSLFILE, QFileInfo(file).fileName(), file.readAll(), QSLObject::RAWBYTES)) ) { addFileToDialog(filename); } } } void PaperQSLDialog::showAvailableFiles() { FCT_IDENTIFICATION; const QStringList &files = qsl->getAvailableQSLNames(dialogQSORecord, QSLObject::QSLFILE); for ( const QString &file : files ) addFileToDialog(file); } void PaperQSLDialog::addFileToDialog(const QString &inFile) { FCT_IDENTIFICATION; QFileInfo file(inFile); qCDebug(function_parameters) << file.fileName(); // if file already exists, do not add it to dialog. if ( filenameList.contains(file.fileName() ) ) return; filenameList << file.fileName(); QHBoxLayout* fileLayout = new QHBoxLayout(); fileLayout->setObjectName(QString::fromUtf8("fileLayout%1").arg(fileCount)); /*****************/ /* Remove Button */ /*****************/ QPushButton* removeButton = new QPushButton(tr("Remove"), this); removeButton->setObjectName(QString::fromUtf8("removeButton%1").arg(fileCount)); fileLayout->addWidget(removeButton); connect(removeButton, &QPushButton::clicked, this, [this, fileLayout, file]() { QMessageBox::StandardButton reply; reply = QMessageBox::question(this, tr("Delete"), tr("Delete QSL?"), QMessageBox::Yes|QMessageBox::No); if (reply != QMessageBox::Yes) return; if ( qsl->remove(dialogQSORecord, QSLObject::QSLFILE, file.fileName()) ) { QLayoutItem *item = NULL; while ((item = fileLayout->takeAt(0)) != 0) { delete item->widget(); delete item; } fileLayout->deleteLater(); } }); /*****************/ /* Open Button */ /*****************/ QPushButton* openButton = new QPushButton(tr("Open"), this); openButton->setObjectName(QString::fromUtf8("openButton%1").arg(fileCount)); fileLayout->addWidget(openButton); /*******************/ /* Favorite Button */ /*******************/ const qulonglong contactId = dialogQSORecord.value("id").toULongLong(); const bool isFav = qsl->isFavorite(contactId, QSLObject::QSLFILE, file.fileName()); QPushButton *favButton = new QPushButton(isFav ? QStringLiteral("\u2605") : QStringLiteral("\u2606"), this); favButton->setObjectName(QString::fromUtf8("favButton%1").arg(fileCount)); favButton->setFixedWidth(30); favButton->setToolTip(tr("Toggle Favorite")); fileLayout->addWidget(favButton); connect(favButton, &QPushButton::clicked, this, [this, favButton, file]() { const qulonglong cId = dialogQSORecord.value("id").toULongLong(); const bool currentFav = (favButton->text() == QStringLiteral("\u2605")); if ( qsl->setFavorite(cId, QSLObject::QSLFILE, file.fileName(), !currentFav) ) favButton->setText(!currentFav ? QStringLiteral("\u2605") : QStringLiteral("\u2606")); }); connect(openButton, &QPushButton::clicked, this, [this, file]() { if ( !tempDir.isValid() ) { qCDebug(runtime) << "Temp directory" << tempDir.path()<< "for QSL is not valid"; return; } QFile f(tempDir.path() + QDir::separator() + file.fileName()); qCDebug(runtime) << "Using temp file" << f.fileName(); if ( f.open(QFile::WriteOnly) ) { f.write(qsl->getQSL(dialogQSORecord, QSLObject::QSLFILE, file.fileName()).getBLOB()); f.flush(); f.close(); } else { qWarning() << "Cannot open file for QSL"; } QDesktopServices::openUrl(QUrl::fromLocalFile(f.fileName())); }); /***************/ /* File label */ /***************/ QLabel *fileLabel = new QLabel(file.fileName().left(80), this); fileLabel->setObjectName(QString::fromUtf8("fileLabel%1").arg(fileCount)); fileLabel->setMaximumWidth(200); fileLabel->setMinimumWidth(200); QSizePolicy sizePolicy1(QSizePolicy::Fixed, QSizePolicy::Fixed); sizePolicy1.setHorizontalStretch(0); sizePolicy1.setVerticalStretch(0); sizePolicy1.setHeightForWidth(fileLabel->sizePolicy().hasHeightForWidth()); fileLabel->setSizePolicy(sizePolicy1); QMimeDatabase mimeDatabase; QMimeType mimeType; mimeType = mimeDatabase.mimeTypeForFile(file); if ( mimeType.name().contains("image", Qt::CaseInsensitive)) { // QByteArray->QString in arg is due to QT5.12 fileLabel->setToolTip(QString("").arg(mimeType.name(), QString(qsl->getQSL(dialogQSORecord, QSLObject::QSLFILE, file.fileName()).getBLOB(QSLObject::BASE64FORM)))); } fileLayout->addWidget(fileLabel); ui->filesListLayout->addLayout(fileLayout); fileCount++; } ================================================ FILE: ui/PaperQSLDialog.h ================================================ #ifndef QLOG_UI_PAPERQSLDIALOG_H #define QLOG_UI_PAPERQSLDIALOG_H #include #include #include "core/QSLStorage.h" namespace Ui { class PaperQSLDialog; } class PaperQSLDialog : public QDialog { Q_OBJECT public: explicit PaperQSLDialog(const QSqlRecord &qso, QWidget *parent = nullptr); ~PaperQSLDialog(); public slots: void addFileClick(); private: void showAvailableFiles(); void addFileToDialog(const QString &); void addNewFile(const QString &); Ui::PaperQSLDialog *ui; QSLStorage *qsl; QSqlRecord dialogQSORecord; unsigned int fileCount; QStringList filenameList; }; #endif // QLOG_UI_PAPERQSLDIALOG_H ================================================ FILE: ui/PaperQSLDialog.ui ================================================ PaperQSLDialog 0 0 366 134 Manage QSL Card true Available QSLs Copy the input file from the source folder to the QLog Internal QSL Storage folder.<br/>The original file remains unchanged in the source folder Import QSL .. Qt::Horizontal 40 20 Qt::Vertical 20 40 Qt::Horizontal QDialogButtonBox::Close buttonBox accepted() PaperQSLDialog accept() 248 254 157 274 buttonBox rejected() PaperQSLDialog reject() 316 260 286 274 addFileButton clicked() PaperQSLDialog addFileClick() 49 74 264 168 addFileClick() ================================================ FILE: ui/PlatformSettingsDialog.cpp ================================================ #include #include #include #include #include #include #include "PlatformSettingsDialog.h" #include "ui_PlatformSettingsDialog.h" #include "core/debug.h" #include "ui/component/EditLine.h" MODULE_IDENTIFICATION("qlog.ui.platformsettingsdialog"); PlatformSettingsDialog::PlatformSettingsDialog(QWidget *parent) : QDialog(parent), ui(new Ui::PlatformSettingsDialog), parameterCount(0) { FCT_IDENTIFICATION; ui->setupUi(this); // Set column resize modes ui->settingsTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents); ui->settingsTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch); // Add Continue button continueButton = ui->buttonBox->addButton(tr("Continue"), QDialogButtonBox::AcceptRole); connect(continueButton, &QPushButton::clicked, this, &QDialog::accept); } PlatformSettingsDialog::~PlatformSettingsDialog() { delete ui; } void PlatformSettingsDialog::setParameters(const QList ¶ms, const QList &profileParams) { FCT_IDENTIFICATION; parameters = params; profilePortParameters = profileParams; parameterCount = params.size(); int totalRows = params.size() + profileParams.size(); ui->settingsTable->setRowCount(totalRows); int row = 0; // Add PlatformParameter entries for ( const PlatformParameter &p : params ) { // Setting name QTableWidgetItem *nameItem = new QTableWidgetItem(p.displayName); ui->settingsTable->setItem(row, 0, nameItem); // Value - editable with browse button, imported value as placeholder QWidget *editWidget = new QWidget(this); QHBoxLayout *layout = new QHBoxLayout(editWidget); layout->setContentsMargins(2, 2, 2, 2); layout->setSpacing(2); QLineEdit *lineEdit = new QLineEdit(this); lineEdit->setObjectName(QString("lineEdit_%1").arg(row)); lineEdit->setPlaceholderText(p.currentValue); layout->addWidget(lineEdit); QPushButton *browseBtn = new QPushButton("...", this); browseBtn->setFixedWidth(30); connect(browseBtn, &QPushButton::clicked, this, [this, row]() { browseForPath(row); }); layout->addWidget(browseBtn); ui->settingsTable->setCellWidget(row, 1, editWidget); ++row; } // Add ProfilePortParameter entries for ( const ProfileParameter &p : profileParams ) { // Setting name (includes profile name) QTableWidgetItem *nameItem = new QTableWidgetItem(p.displayName); ui->settingsTable->setItem(row, 0, nameItem); // Value - editable with browse button, imported value as placeholder QWidget *editWidget = new QWidget(); QHBoxLayout *layout = new QHBoxLayout(editWidget); layout->setContentsMargins(2, 2, 2, 2); layout->setSpacing(2); if ( p.isExecutablePath ) { QLineEdit *lineEdit = new QLineEdit(this); lineEdit->setObjectName(QString("lineEdit_%1").arg(row)); lineEdit->setPlaceholderText(p.currentValue); layout->addWidget(lineEdit); QPushButton *browseBtn = new QPushButton("...",this); browseBtn->setFixedWidth(30); connect(browseBtn, &QPushButton::clicked, this, [this, row]() { browseForPath(row); }); layout->addWidget(browseBtn); } else { SerialPortEditLine *lineEdit = new SerialPortEditLine(this); lineEdit->setObjectName(QString("lineEdit_%1").arg(row)); lineEdit->setPlaceholderText(p.currentValue); layout->addWidget(lineEdit); } ui->settingsTable->setCellWidget(row, 1, editWidget); ++row; } } QList PlatformSettingsDialog::getParameters() const { FCT_IDENTIFICATION; QList result = parameters; for ( int row = 0; row < result.size(); ++row ) { QWidget *cellWidget = ui->settingsTable->cellWidget(row, 1); if ( cellWidget ) { QLineEdit *lineEdit = cellWidget->findChild(QString("lineEdit_%1").arg(row)); if ( lineEdit ) { qCDebug(runtime) << "new value" << lineEdit->text(); result[row].newValue = lineEdit->text(); } } } return result; } QList PlatformSettingsDialog::getProfilePortParameters() const { FCT_IDENTIFICATION; QList result = profilePortParameters; for ( int i = 0; i < result.size(); ++i ) { int row = parameterCount + i; QWidget *cellWidget = ui->settingsTable->cellWidget(row, 1); if ( cellWidget ) { QLineEdit *lineEdit = cellWidget->findChild(QString("lineEdit_%1").arg(row)); if ( lineEdit ) { qCDebug(runtime) << "new value" << lineEdit->text(); result[i].newValue = lineEdit->text(); } } } return result; } void PlatformSettingsDialog::browseForPath(int row) { FCT_IDENTIFICATION; qCDebug(function_parameters) << row; int totalRows = parameterCount + profilePortParameters.size(); if ( row < 0 || row >= totalRows ) return; QString filename = QFileDialog::getOpenFileName( this, tr("Select File"), QString(), tr("All Files (*)") ); if ( filename.isEmpty() ) return; QWidget *cellWidget = ui->settingsTable->cellWidget(row, 1); if ( cellWidget ) { QLineEdit *lineEdit = cellWidget->findChild(QString("lineEdit_%1").arg(row)); if ( lineEdit ) lineEdit->setText(filename); } } ================================================ FILE: ui/PlatformSettingsDialog.h ================================================ #ifndef QLOG_UI_PLATFORMSETTINGSDIALOG_H #define QLOG_UI_PLATFORMSETTINGSDIALOG_H #include #include #include "core/PlatformParameterManager.h" namespace Ui { class PlatformSettingsDialog; } class PlatformSettingsDialog : public QDialog { Q_OBJECT public: explicit PlatformSettingsDialog(QWidget *parent = nullptr); ~PlatformSettingsDialog(); void setParameters(const QList ¶ms, const QList &profileParams); QList getParameters() const; QList getProfilePortParameters() const; private slots: void browseForPath(int row); private: Ui::PlatformSettingsDialog *ui; QPushButton *continueButton; QList parameters; QList profilePortParameters; int parameterCount; // Number of PlatformParameter rows }; #endif // QLOG_UI_PLATFORMSETTINGSDIALOG_H ================================================ FILE: ui/PlatformSettingsDialog.ui ================================================ PlatformSettingsDialog 0 0 675 300 Platform-specific Settings true The database was exported from a different platform. Please verify or update the following settings. You can leave fields empty and configure them later in Settings. true QAbstractItemView::NoEditTriggers true QAbstractItemView::NoSelection 2 true false Setting Value Qt::Horizontal QDialogButtonBox::Cancel buttonBox rejected() PlatformSettingsDialog reject() 316 260 286 274 ================================================ FILE: ui/ProfileImageWidget.cpp ================================================ #include #include "ProfileImageWidget.h" #include "ui_ProfileImageWidget.h" #include "core/debug.h" MODULE_IDENTIFICATION("qlog.ui.profileimagewidget"); ProfileImageWidget::ProfileImageWidget(QWidget *parent) : QWidget(parent), ui(new Ui::ProfileImageWidget), imageLabel(new AspectRatioLabel(this)), nam(new QNetworkAccessManager(this)), currentReply(nullptr) { FCT_IDENTIFICATION; ui->setupUi(this); QVBoxLayout *layout = new QVBoxLayout(this); imageLabel->setAlignment(Qt::AlignCenter); imageLabel->setMinimumSize(sizeHint()); layout->addWidget(imageLabel); connect(nam, &QNetworkAccessManager::finished, this, &ProfileImageWidget::processReply); } ProfileImageWidget::~ProfileImageWidget() { FCT_IDENTIFICATION; if ( currentReply ) currentReply->abort(); nam->deleteLater(); imageLabel->deleteLater(); delete ui; } void ProfileImageWidget::loadImageFromUrl(const QString &urlAddress) { FCT_IDENTIFICATION; qCDebug(function_parameters) << urlAddress; if ( urlAddress.isEmpty() ) { imageLabel->clear(); imageLabel->setToolTip(""); return; } if ( currentReply ) { qCDebug(runtime) << "Previous request is still running"; currentReply->abort(); } QUrl url(urlAddress); currentReply = nam->get(QNetworkRequest(url)); } void ProfileImageWidget::processReply(QNetworkReply *reply) { FCT_IDENTIFICATION; /* always process one requests per class */ currentReply = nullptr; int replyStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); if ( reply->error() != QNetworkReply::NoError || replyStatusCode < 200 || replyStatusCode >= 300) { qCDebug(runtime) << "Download Image error: URL " << reply->request().url().toString(); qCDebug(runtime) << "Download Image error:" << reply->errorString(); qCDebug(runtime) << "HTTP Status Code" << replyStatusCode; if ( reply->error() != QNetworkReply::OperationCanceledError ) { reply->deleteLater(); } return; } QByteArray imageData = reply->readAll(); QPixmap pixmap; pixmap.loadFromData(imageData); imageLabel->setPixmap(pixmap); imageLabel->setScaledContents(true); mimeType = mimeDatabase.mimeTypeForData(imageData); if ( mimeType.name().contains("image", Qt::CaseInsensitive)) { // QByteArray->QString in arg is due to QT5.12 imageLabel->setToolTip(QString("").arg(mimeType.name(), QString(imageData.toBase64()))); } reply->deleteLater(); } AspectRatioLabel::AspectRatioLabel(QWidget* parent, Qt::WindowFlags f) : QLabel(parent, f) { } AspectRatioLabel::~AspectRatioLabel() { } void AspectRatioLabel::setPixmap(const QPixmap& pm) { pixmapWidth = pm.width(); pixmapHeight = pm.height(); updateMargins(); QLabel::setPixmap(pm); } void AspectRatioLabel::resizeEvent(QResizeEvent* event) { updateMargins(); QLabel::resizeEvent(event); } void AspectRatioLabel::updateMargins() { if ( pixmapWidth <= 0 || pixmapHeight <= 0 ) return; int w = width(); int h = height(); if ( w <= 0 || h <= 0 ) return; if ( w * pixmapHeight > h * pixmapWidth ) { int m = (w - (pixmapWidth * h / pixmapHeight)) / 2; setContentsMargins(m, 0, m, 0); } else { int m = (h - (pixmapHeight * w / pixmapWidth)) / 2; setContentsMargins(0, m, 0, m); } } ================================================ FILE: ui/ProfileImageWidget.h ================================================ #ifndef QLOG_UI_PROFILEIMAGEWIDGET_H #define QLOG_UI_PROFILEIMAGEWIDGET_H #include #include #include #include #include namespace Ui { class ProfileImageWidget; } class AspectRatioLabel : public QLabel { Q_OBJECT public: explicit AspectRatioLabel(QWidget* parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); ~AspectRatioLabel(); public slots: void setPixmap(const QPixmap& pm); protected: void resizeEvent(QResizeEvent* event) override; private: void updateMargins(); int pixmapWidth = 0; int pixmapHeight = 0; }; class ProfileImageWidget : public QWidget { Q_OBJECT public: explicit ProfileImageWidget(QWidget *parent = nullptr); ~ProfileImageWidget(); public slots: void loadImageFromUrl(const QString &urlAddress); private slots: void processReply(QNetworkReply* reply); private: Ui::ProfileImageWidget *ui; AspectRatioLabel *imageLabel; QNetworkAccessManager *nam; QNetworkReply *currentReply; QMimeDatabase mimeDatabase; QMimeType mimeType; }; #endif // QLOG_UI_PROFILEIMAGEWIDGET_H ================================================ FILE: ui/ProfileImageWidget.ui ================================================ ProfileImageWidget 0 0 400 300 Profile Image ================================================ FILE: ui/QSLGalleryDialog.cpp ================================================ #include #include #include #include #include #include #include #include #include #include #include #include #include "QSLGalleryDialog.h" #include "ui_QSLGalleryDialog.h" #include "core/debug.h" MODULE_IDENTIFICATION("qlog.ui.qslgallerydialog"); class QSLCardDelegate : public QStyledItemDelegate { public: using QStyledItemDelegate::QStyledItemDelegate; void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override { painter->save(); QStyleOptionViewItem opt = option; initStyleOption(&opt, index); opt.text.clear(); opt.icon = QIcon(); opt.decorationSize = QSize(); QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &opt, painter, opt.widget); const QRect rect = option.rect; const int iconW = option.decorationSize.width(); const int iconH = option.decorationSize.height(); const int textMargin = 4; // Draw icon centered horizontally const QIcon icon = index.data(Qt::DecorationRole).value(); const int iconX = rect.x() + (rect.width() - iconW) / 2; const int iconY = rect.y() + textMargin; icon.paint(painter, iconX, iconY, iconW, iconH); // Text area below icon const int textY = iconY + iconH + textMargin; const QRect textRect(rect.x() + textMargin, textY, rect.width() - 2 * textMargin, rect.bottom() - textY); // Bold callsign const QString callsign = index.data(QSLGalleryDialog::CallsignRole).toString(); QFont boldFont = option.font; boldFont.setBold(true); painter->setFont(boldFont); painter->setPen(option.palette.color(QPalette::Text)); const QFontMetrics boldFm(boldFont); const QString elidedCallsign = boldFm.elidedText(callsign, Qt::ElideRight, textRect.width()); painter->drawText(textRect, Qt::AlignHCenter | Qt::AlignTop, elidedCallsign); // Normal date below callsign const QString dateStr = index.data(QSLGalleryDialog::DateStringRole).toString(); QFont normalFont = option.font; painter->setFont(normalFont); const QRect dateRect(textRect.x(), textY + boldFm.height() + 1, textRect.width(), textRect.height() - boldFm.height()); const QFontMetrics normalFm(normalFont); const QString elidedDate = normalFm.elidedText(dateStr, Qt::ElideRight, dateRect.width()); painter->drawText(dateRect, Qt::AlignHCenter | Qt::AlignTop, elidedDate); // Draw favorite star in the top-right corner of the icon if ( index.data(QSLGalleryDialog::FavoriteRole).toBool() ) { QFont starFont = option.font; starFont.setPixelSize(14); painter->setFont(starFont); const QString star = QStringLiteral("\u2605"); const int starX = iconX + iconW - 14; const int starY = iconY + 14; // Black outline painter->setPen(Qt::black); painter->drawText(starX - 1, starY, star); painter->drawText(starX + 1, starY, star); painter->drawText(starX, starY - 1, star); painter->drawText(starX, starY + 1, star); // Yellow star painter->setPen(QColor(0xFF, 0xD7, 0x00)); painter->drawText(starX, starY, star); } painter->restore(); } QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &) const override { const int w = option.decorationSize.width() + 20; const int h = option.decorationSize.height() + QFontMetrics(option.font).height() * 2 + 16; return QSize(w, h); } }; QSLGalleryDialog::QSLGalleryDialog(QWidget *parent) : QDialog(parent), ui(new Ui::QSLGalleryDialog), scrollTimer(new QTimer(this)), tempDir(nullptr) { FCT_IDENTIFICATION; ui->setupUi(this); // tree 20%, gallery 80% ui->splitter->setStretchFactor(0, 2); ui->splitter->setStretchFactor(1, 8); ui->cardListWidget->setItemDelegate(new QSLCardDelegate(ui->cardListWidget)); connect(ui->filterTree, &QTreeWidget::currentItemChanged, this, [this]() { filterTreeSelectionChanged(); }); connect(ui->cardListWidget, &QListWidget::itemDoubleClicked, this, &QSLGalleryDialog::cardDoubleClicked); ui->cardListWidget->setContextMenuPolicy(Qt::CustomContextMenu); connect(ui->cardListWidget, &QWidget::customContextMenuRequested, this, &QSLGalleryDialog::showContextMenu); // Lazy loading scrollTimer->setSingleShot(true); scrollTimer->setInterval(150); connect(scrollTimer, &QTimer::timeout, this, &QSLGalleryDialog::loadVisibleThumbnails); connect(ui->cardListWidget->verticalScrollBar(), &QScrollBar::valueChanged, this, [this]() { scrollTimer->start(); }); connect(ui->exportFilteredButton, &QPushButton::clicked, this, &QSLGalleryDialog::exportFiltered); ui->sortCombo->addItem(tr("Date (Newest)"), SORT_DATE_DESC); ui->sortCombo->addItem(tr("Date (Oldest)"), SORT_DATE_ASC); ui->sortCombo->addItem(tr("Callsign (A-Z)"), SORT_CALLSIGN_ASC); ui->sortCombo->addItem(tr("Callsign (Z-A)"), SORT_CALLSIGN_DESC); connect(ui->sortCombo, QOverload::of(&QComboBox::currentIndexChanged), this, [this]() { loadGallery(); }); searchTimer = new QTimer(this); searchTimer->setSingleShot(true); searchTimer->setInterval(300); connect(searchTimer, &QTimer::timeout, this, [this]() { const QString text = ui->searchEdit->text().trimmed().toUpper(); int visibleCount = 0; for ( int i = 0; i < ui->cardListWidget->count(); ++i ) { QListWidgetItem *item = ui->cardListWidget->item(i); const bool match = text.isEmpty() || item->data(CallsignRole).toString().toUpper().contains(text); item->setHidden(!match); if ( match ) ++visibleCount; } ui->statusLabel->setText(tr("%n QSL card(s)", "", visibleCount)); ui->exportFilteredButton->setEnabled(visibleCount > 0); }); connect(ui->searchEdit, &QLineEdit::textChanged, this, [this]() { searchTimer->start(); }); buildFilterTree(); } QSLGalleryDialog::~QSLGalleryDialog() { FCT_IDENTIFICATION; delete tempDir; delete ui; } void QSLGalleryDialog::buildFilterTree() { FCT_IDENTIFICATION; ui->filterTree->clear(); // "All" root item QTreeWidgetItem *allItem = new QTreeWidgetItem(ui->filterTree); allItem->setText(0, tr("All QSL Cards")); allItem->setIcon(0, QApplication::style()->standardIcon(QStyle::SP_DirIcon)); allItem->setData(0, Qt::UserRole, FILTER_ALL); // "Favorites" item QTreeWidgetItem *favItem = new QTreeWidgetItem(ui->filterTree); favItem->setText(0, tr("Favorites")); favItem->setIcon(0, QApplication::style()->standardIcon(QStyle::SP_DirIcon)); favItem->setData(0, Qt::UserRole, FILTER_FAVORITE); const QSLStorage::FilterValues filters = qslStorage.getDistinctFilterValues(); // "By Country" branch QTreeWidgetItem *countryRoot = new QTreeWidgetItem(ui->filterTree); countryRoot->setText(0, tr("By Country")); countryRoot->setIcon(0, QApplication::style()->standardIcon(QStyle::SP_DirIcon)); countryRoot->setFlags(countryRoot->flags() & ~Qt::ItemIsSelectable); QList sortedCountries = filters.countries.keys(); std::sort(sortedCountries.begin(), sortedCountries.end(), [](const QString &a, const QString &b) { return a.localeAwareCompare(b) < 0; }); for ( const QString &country : static_cast&>(sortedCountries) ) { QTreeWidgetItem *item = new QTreeWidgetItem(countryRoot); item->setText(0, country); item->setData(0, Qt::UserRole, FILTER_COUNTRY); item->setData(0, Qt::UserRole + 1, filters.countries.value(country)); } // "By Date" branch (year -> months) QTreeWidgetItem *dateRoot = new QTreeWidgetItem(ui->filterTree); dateRoot->setText(0, tr("By Date")); dateRoot->setIcon(0, QApplication::style()->standardIcon(QStyle::SP_DirIcon)); dateRoot->setFlags(dateRoot->flags() & ~Qt::ItemIsSelectable); // Iterate years in descending order QStringList years = filters.yearMonths.keys(); std::sort(years.begin(), years.end(), std::greater()); for ( const QString &year : static_cast(years) ) { QTreeWidgetItem *yearItem = new QTreeWidgetItem(dateRoot); yearItem->setText(0, year); yearItem->setData(0, Qt::UserRole, FILTER_YEAR); yearItem->setData(0, Qt::UserRole + 1, year); const QStringList &months = filters.yearMonths.value(year); for ( const QString &month : months ) { QTreeWidgetItem *monthItem = new QTreeWidgetItem(yearItem); monthItem->setText(0, month); monthItem->setData(0, Qt::UserRole, FILTER_MONTH); monthItem->setData(0, Qt::UserRole + 1, year); monthItem->setData(0, Qt::UserRole + 2, month); } } // "By Band" branch QTreeWidgetItem *bandRoot = new QTreeWidgetItem(ui->filterTree); bandRoot->setText(0, tr("By Band")); bandRoot->setIcon(0, QApplication::style()->standardIcon(QStyle::SP_DirIcon)); bandRoot->setFlags(bandRoot->flags() & ~Qt::ItemIsSelectable); for ( const QString &band : filters.bands ) { QTreeWidgetItem *item = new QTreeWidgetItem(bandRoot); item->setText(0, band); item->setData(0, Qt::UserRole, FILTER_BAND); item->setData(0, Qt::UserRole + 1, band); } // "By Mode" branch QTreeWidgetItem *modeRoot = new QTreeWidgetItem(ui->filterTree); modeRoot->setText(0, tr("By Mode")); modeRoot->setIcon(0, QApplication::style()->standardIcon(QStyle::SP_DirIcon)); modeRoot->setFlags(modeRoot->flags() & ~Qt::ItemIsSelectable); for ( const QString &mode : filters.modes ) { QTreeWidgetItem *item = new QTreeWidgetItem(modeRoot); item->setText(0, mode); item->setData(0, Qt::UserRole, FILTER_MODE); item->setData(0, Qt::UserRole + 1, mode); } // "By Continent" branch QTreeWidgetItem *contRoot = new QTreeWidgetItem(ui->filterTree); contRoot->setText(0, tr("By Continent")); contRoot->setIcon(0, QApplication::style()->standardIcon(QStyle::SP_DirIcon)); contRoot->setFlags(contRoot->flags() & ~Qt::ItemIsSelectable); for ( const QString &cont : filters.continents ) { QTreeWidgetItem *item = new QTreeWidgetItem(contRoot); item->setText(0, cont); item->setData(0, Qt::UserRole, FILTER_CONTINENT); item->setData(0, Qt::UserRole + 1, cont); } // Select "All" by default ui->filterTree->setCurrentItem(allItem); } void QSLGalleryDialog::filterTreeSelectionChanged() { FCT_IDENTIFICATION; ui->searchEdit->blockSignals(true); ui->searchEdit->clear(); ui->searchEdit->blockSignals(false); loadGallery(); } void QSLGalleryDialog::loadGallery() { FCT_IDENTIFICATION; const QTreeWidgetItem *current = ui->filterTree->currentItem(); if ( !current ) return; const FilterType filter = static_cast(current->data(0, Qt::UserRole).toInt()); QList items; switch ( filter ) { case FILTER_ALL: items = qslStorage.getGalleryItems(); break; case FILTER_FAVORITE: items = qslStorage.getGalleryItemsFavorite(); break; case FILTER_COUNTRY: { const int dxcc = current->data(0, Qt::UserRole + 1).toInt(); items = qslStorage.getGalleryItemsByDxcc(dxcc); break; } case FILTER_YEAR: { const QString year = current->data(0, Qt::UserRole + 1).toString(); items = qslStorage.getGalleryItemsByYear(year); break; } case FILTER_MONTH: { const QString year = current->data(0, Qt::UserRole + 1).toString(); const QString month = current->data(0, Qt::UserRole + 2).toString(); items = qslStorage.getGalleryItemsByYearMonth(year, month); break; } case FILTER_BAND: { const QString band = current->data(0, Qt::UserRole + 1).toString(); items = qslStorage.getGalleryItemsByBand(band); break; } case FILTER_MODE: { const QString mode = current->data(0, Qt::UserRole + 1).toString(); items = qslStorage.getGalleryItemsByMode(mode); break; } case FILTER_CONTINENT: { const QString continent = current->data(0, Qt::UserRole + 1).toString(); items = qslStorage.getGalleryItemsByContinent(continent); break; } } populateItems(items); } void QSLGalleryDialog::populateItems(const QList &items) { FCT_IDENTIFICATION; ui->cardListWidget->clear(); QList sorted = items; const SortOrder order = static_cast(ui->sortCombo->currentData().toInt()); std::sort(sorted.begin(), sorted.end(), [order](const QSLGalleryItem &a, const QSLGalleryItem &b) { switch ( order ) { case SORT_DATE_ASC: return a.startTime < b.startTime; case SORT_CALLSIGN_ASC: return a.callsign.compare(b.callsign, Qt::CaseInsensitive) < 0; case SORT_CALLSIGN_DESC: return a.callsign.compare(b.callsign, Qt::CaseInsensitive) > 0; case SORT_DATE_DESC: default: return a.startTime > b.startTime; } }); // Create a placeholder pixmap QPixmap placeholder(150, 112); placeholder.fill(QColor(220, 220, 220)); for ( const QSLGalleryItem &item : sorted ) { const QString dateStr = item.startTime.toTimeZone(QTimeZone::utc()) .toString(locale.formatDateTimeShortWithYYYY()); QListWidgetItem *listItem = new QListWidgetItem(QIcon(placeholder), QString()); listItem->setData(ContactIdRole, static_cast(item.contactId)); listItem->setData(SourceRole, static_cast(item.source)); listItem->setData(NameRole, item.name); listItem->setData(ThumbnailLoadedRole, false); listItem->setData(CallsignRole, item.callsign); listItem->setData(DateStringRole, dateStr); listItem->setData(FavoriteRole, item.favorite); ui->cardListWidget->addItem(listItem); } ui->statusLabel->setText(tr("%n QSL card(s)", "", items.count())); ui->exportFilteredButton->setEnabled(!items.isEmpty()); // initial thumbnail load QTimer::singleShot(0, this, &QSLGalleryDialog::loadVisibleThumbnails); } void QSLGalleryDialog::loadVisibleThumbnails() { FCT_IDENTIFICATION; const QListWidget *lw = ui->cardListWidget; const int totalCount = lw->count(); if ( totalCount == 0 ) return; // Find visible range using viewport geometry const QRect viewportRect = lw->viewport()->rect(); const QModelIndex topLeft = lw->indexAt(viewportRect.topLeft()); const QModelIndex bottomRight = lw->indexAt(viewportRect.bottomRight()); int firstVisible = topLeft.isValid() ? topLeft.row() : 0; int lastVisible = bottomRight.isValid() ? bottomRight.row() : totalCount - 1; // If bottomRight is invalid, scan forward to find last visible item if ( !bottomRight.isValid() && topLeft.isValid() ) { for ( int i = firstVisible; i < totalCount; ++i ) { const QRect itemRect = lw->visualItemRect(lw->item(i)); if ( !itemRect.intersects(viewportRect) && i > firstVisible ) { lastVisible = i - 1; break; } lastVisible = i; } } // Add margin for smooth scrolling const int margin = 10; firstVisible = qMax(0, firstVisible - margin); lastVisible = qMin(totalCount - 1, lastVisible + margin); qCDebug(runtime) << "Loading thumbnails" << firstVisible << "-" << lastVisible << "of" << totalCount; for ( int i = firstVisible; i <= lastVisible; ++i ) { QListWidgetItem *item = lw->item(i); if ( item->data(ThumbnailLoadedRole).toBool() ) continue; const qulonglong contactId = item->data(ContactIdRole).toULongLong(); const int source = item->data(SourceRole).toInt(); const QString name = item->data(NameRole).toString(); const QByteArray data = qslStorage.getQSLData(contactId, source, name); if ( !data.isEmpty() ) { item->setIcon(QIcon(createThumbnail(data, name))); const QMimeType mimeType = mimeDb.mimeTypeForData(data); if ( mimeType.name().startsWith("image/") ) item->setToolTip(QString("") .arg(mimeType.name(), QString(data.toBase64()))); else item->setToolTip(QString("%1 (%2)").arg(name, mimeType.comment())); } item->setData(ThumbnailLoadedRole, true); } } QPixmap QSLGalleryDialog::createThumbnail(const QByteArray &data, const QString &name) const { FCT_IDENTIFICATION; const QMimeType mimeType = mimeDb.mimeTypeForData(data); if ( mimeType.name().startsWith("image/") ) { QPixmap pixmap; if ( pixmap.loadFromData(data) ) return pixmap.scaled(150, 112, Qt::KeepAspectRatio, Qt::SmoothTransformation); } // Non-image file: draw file type label over generic icon QPixmap pixmap(150, 112); pixmap.fill(Qt::transparent); QPainter p(&pixmap); const QIcon fileIcon = QApplication::style()->standardIcon(QStyle::SP_FileIcon); fileIcon.paint(&p, 0, 0, 150, 112); const QString suffix = QFileInfo(name).suffix().toUpper(); if ( !suffix.isEmpty() ) { QFont font = p.font(); font.setPixelSize(18); font.setBold(true); p.setFont(font); const QRect textRect(0, 70, 150, 30); p.setPen(Qt::white); p.setBrush(QColor(0, 0, 0, 160)); const QFontMetrics fm(font); const int textWidth = fm.horizontalAdvance(suffix) + 10; const int bgX = (150 - textWidth) / 2; p.drawRoundedRect(bgX, 72, textWidth, fm.height() + 4, 3, 3); p.setPen(Qt::white); p.drawText(textRect, Qt::AlignCenter, suffix); } p.end(); return pixmap; } void QSLGalleryDialog::cardDoubleClicked(QListWidgetItem *item) { FCT_IDENTIFICATION; openItem(item); } void QSLGalleryDialog::showContextMenu(const QPoint &pos) { FCT_IDENTIFICATION; QListWidgetItem *item = ui->cardListWidget->itemAt(pos); if ( !item ) return; QMenu menu(this); const bool isFav = item->data(FavoriteRole).toBool(); menu.addAction(isFav ? tr("Remove from Favorites") : tr("Add to Favorites"), this, [this, item] { toggleFavorite(item); }); menu.addSeparator(); menu.addAction(tr("Open"), this, [this, item] { openItem(item); }); menu.addAction(tr("Save..."), this, [this, item] { saveItem(item); }); menu.exec(ui->cardListWidget->viewport()->mapToGlobal(pos)); } void QSLGalleryDialog::openItem(const QListWidgetItem *item) { FCT_IDENTIFICATION; if ( !item ) return; const qulonglong contactId = item->data(ContactIdRole).toULongLong(); const int source = item->data(SourceRole).toInt(); const QString name = item->data(NameRole).toString(); const QByteArray data = qslStorage.getQSLData(contactId, source, name); if ( data.isEmpty() ) { qCWarning(runtime) << "No QSL data for" << contactId << source << name; return; } // Create temp dir if needed if ( !tempDir ) { tempDir = new QTemporaryDir(); if ( !tempDir->isValid() ) { qCWarning(runtime) << "Cannot create temporary directory"; return; } } const QString filePath = tempDir->path() + "/" + name; QFile file(filePath); if ( !file.open(QIODevice::WriteOnly) ) { qCWarning(runtime) << "Cannot write temporary file" << filePath; return; } file.write(data); file.close(); QDesktopServices::openUrl(QUrl::fromLocalFile(filePath)); } void QSLGalleryDialog::saveItem(const QListWidgetItem *item) { FCT_IDENTIFICATION; if ( !item ) return; const qulonglong contactId = item->data(ContactIdRole).toULongLong(); const int source = item->data(SourceRole).toInt(); const QString name = item->data(NameRole).toString(); const QByteArray data = qslStorage.getQSLData(contactId, source, name); if ( data.isEmpty() ) { qCDebug(runtime) << "No QSL data for" << contactId << source << name; return; } const QString savePath = QFileDialog::getSaveFileName(this, tr("Save QSL Card"), QDir::homePath() + "/" + name); if ( savePath.isEmpty() ) return; QFile file(savePath); if ( !file.open(QIODevice::WriteOnly) ) { qCDebug(runtime) << "Cannot write file" << savePath; return; } file.write(data); file.close(); qCDebug(runtime) << "QSL card saved to" << savePath; } void QSLGalleryDialog::toggleFavorite(QListWidgetItem *item) { FCT_IDENTIFICATION; if ( !item ) return; const qulonglong contactId = item->data(ContactIdRole).toULongLong(); const QSLObject::SourceType source = static_cast(item->data(SourceRole).toInt()); const QString name = item->data(NameRole).toString(); const bool currentFav = item->data(FavoriteRole).toBool(); if ( qslStorage.setFavorite(contactId, source, name, !currentFav) ) { item->setData(FavoriteRole, !currentFav); ui->cardListWidget->update(); } } void QSLGalleryDialog::exportFiltered() { FCT_IDENTIFICATION; const int count = ui->cardListWidget->count(); if ( count == 0 ) return; const QString dir = QFileDialog::getExistingDirectory(this, tr("Export QSL Cards"), QDir::homePath()); if ( dir.isEmpty() ) return; int saved = 0; int visible = 0; for ( int i = 0; i < count; ++i ) { const QListWidgetItem *item = ui->cardListWidget->item(i); if ( item->isHidden() ) continue; ++visible; const qulonglong contactId = item->data(ContactIdRole).toULongLong(); const int source = item->data(SourceRole).toInt(); const QString name = item->data(NameRole).toString(); const QByteArray data = qslStorage.getQSLData(contactId, source, name); if ( data.isEmpty() ) { qCDebug(runtime) << "No QSL data for" << contactId << source << name; continue; } const QString filePath = QDir(dir).filePath(name); QFile file(filePath); if ( !file.open(QIODevice::WriteOnly) ) { qCDebug(runtime) << "Cannot write file" << filePath; continue; } file.write(data); ++saved; } ui->statusLabel->setText(tr("Exported %1 of %2 cards").arg(saved).arg(visible)); qCDebug(runtime) << "Exported" << saved << "of" << visible << "QSL cards to" << dir; } ================================================ FILE: ui/QSLGalleryDialog.h ================================================ #ifndef QLOG_UI_QSLGALLERYDIALOG_H #define QLOG_UI_QSLGALLERYDIALOG_H #include #include #include #include #include #include "core/QSLStorage.h" #include "core/LogLocale.h" namespace Ui { class QSLGalleryDialog; } class QSLGalleryDialog : public QDialog { Q_OBJECT public: explicit QSLGalleryDialog(QWidget *parent = nullptr); ~QSLGalleryDialog(); // Exposed for delegate access enum ItemDataRole { ContactIdRole = Qt::UserRole, SourceRole = Qt::UserRole + 1, NameRole = Qt::UserRole + 2, ThumbnailLoadedRole = Qt::UserRole + 3, CallsignRole = Qt::UserRole + 4, DateStringRole = Qt::UserRole + 5, FavoriteRole = Qt::UserRole + 6 }; private slots: void filterTreeSelectionChanged(); void cardDoubleClicked(QListWidgetItem *item); void showContextMenu(const QPoint &pos); void loadVisibleThumbnails(); private: enum FilterType { FILTER_ALL = 0, FILTER_FAVORITE, FILTER_COUNTRY, FILTER_YEAR, FILTER_MONTH, FILTER_BAND, FILTER_MODE, FILTER_CONTINENT }; enum SortOrder { SORT_DATE_DESC = 0, SORT_DATE_ASC, SORT_CALLSIGN_ASC, SORT_CALLSIGN_DESC }; void buildFilterTree(); void loadGallery(); void populateItems(const QList &items); QPixmap createThumbnail(const QByteArray &data, const QString &name) const; void openItem(const QListWidgetItem *item); void saveItem(const QListWidgetItem *item); void toggleFavorite(QListWidgetItem *item); void exportFiltered(); Ui::QSLGalleryDialog *ui; QSLStorage qslStorage; QTimer *scrollTimer; QTimer *searchTimer; QTemporaryDir *tempDir; LogLocale locale; QMimeDatabase mimeDb; }; #endif // QLOG_UI_QSLGALLERYDIALOG_H ================================================ FILE: ui/QSLGalleryDialog.ui ================================================ QSLGalleryDialog 0 0 900 600 QSL Card Gallery Search by callsign... true Sort by: Qt::Horizontal 0 0 16 true 1 150 112 QAbstractItemView::ScrollPerPixel QAbstractItemView::ScrollPerPixel QListView::Static QListView::LeftToRight QListView::Adjust 10 QListView::IconMode true true Qt::Horizontal 40 20 Export Filtered .. Qt::Horizontal QDialogButtonBox::Close buttonBox rejected() QSLGalleryDialog reject() 400 580 400 300 ================================================ FILE: ui/QSLImportStatDialog.cpp ================================================ #include "QSLImportStatDialog.h" #include "ui_QSLImportStatDialog.h" #include "core/debug.h" MODULE_IDENTIFICATION("qlog.ui.QSLImportStatDialog"); QSLImportStatDialog::QSLImportStatDialog(const QSLMergeStat &stats, QWidget *parent) : QDialog(parent), ui(new Ui::QSLImportStatDialog) { FCT_IDENTIFICATION; ui->setupUi(this); showStat(stats.updatedQSOs.size() + stats.newQSLs.size(), stats.qsosDownloaded, stats.unmatchedQSLs.size(), stats.errorQSLs.size(), stats.newQSLs.join("\n"), stats.updatedQSOs.join("\n"), stats.unmatchedQSLs.join("\n")); } QSLImportStatDialog::QSLImportStatDialog(const QHash &stats, QWidget *parent) : QDialog(parent), ui(new Ui::QSLImportStatDialog) { FCT_IDENTIFICATION; ui->setupUi(this); quint64 updated = 0; quint64 downloaded = 0; quint64 unmatched = 0; quint64 errors = 0; QString newQSLText; QString updatedQSLText; QString unmatchedQSLText; for (auto i = stats.begin(), end = stats.end(); i != end; ++i) { updated += (i->updatedQSOs.size() + i->newQSLs.size()); downloaded += i->qsosDownloaded; unmatched += i->unmatchedQSLs.size(); errors += i->errorQSLs.size(); if ( !i.value().newQSLs.isEmpty() ) newQSLText.append("* " + i.key() + "\n" + i.value().newQSLs.join("\n") + "\n\n"); if ( !i.value().updatedQSOs.empty() ) updatedQSLText.append("* " + i.key() + "\n" + i.value().updatedQSOs.join("\n") + "\n\n"); if ( !i.value().unmatchedQSLs.isEmpty() ) unmatchedQSLText.append("* " + i.key() + "\n" + i.value().unmatchedQSLs.join("\n") + "\n\n"); } showStat(updated, downloaded, unmatched, errors, newQSLText, updatedQSLText, unmatchedQSLText); } void QSLImportStatDialog::showStat(const quint64 updated, const quint64 downloaded, const quint64 unmatched, const quint64 errors, const QString &newQSLText, const QString &updatedQSLText, const QString &unmatchedQSLText) { FCT_IDENTIFICATION; ui->updatedNumber->setText(QString::number(updated)); ui->downloadedNumber->setText(QString::number(downloaded)); ui->unmatchedNumber->setText(QString::number(unmatched)); ui->errorsNumber->setText(QString::number(errors)); if ( !newQSLText.isEmpty() ) { ui->detailsText->moveCursor(QTextCursor::End); ui->detailsText->insertPlainText("*** " + tr("New QSLs: ") + "\n"); ui->detailsText->moveCursor(QTextCursor::End); ui->detailsText->insertPlainText(newQSLText); ui->detailsText->moveCursor(QTextCursor::End); ui->detailsText->moveCursor(QTextCursor::End); } if ( !updatedQSLText.isEmpty() ) { ui->detailsText->insertPlainText("*** " + tr("Updated QSOs: ") + "\n"); ui->detailsText->moveCursor(QTextCursor::End); ui->detailsText->insertPlainText(updatedQSLText); ui->detailsText->moveCursor(QTextCursor::End); ui->detailsText->moveCursor(QTextCursor::End); } if ( !unmatchedQSLText.isEmpty() ) { ui->detailsText->insertPlainText("*** " + tr("Unmatched QSLs: ") + "\n"); ui->detailsText->moveCursor(QTextCursor::End); ui->detailsText->insertPlainText (unmatchedQSLText); ui->detailsText->moveCursor(QTextCursor::End); } } QSLImportStatDialog::~QSLImportStatDialog() { FCT_IDENTIFICATION; delete ui; } ================================================ FILE: ui/QSLImportStatDialog.h ================================================ #ifndef QLOG_UI_QSLIMPORTSTATDIALOG_H #define QLOG_UI_QSLIMPORTSTATDIALOG_H #include #include namespace Ui { class QSLImportStatDialog; } class QSLImportStatDialog : public QDialog { Q_OBJECT public: explicit QSLImportStatDialog(const QSLMergeStat &stats, QWidget *parent = nullptr); explicit QSLImportStatDialog(const QHash &stats, QWidget *parent = nullptr); ~QSLImportStatDialog(); private: Ui::QSLImportStatDialog *ui; void showStat(const quint64 updated, const quint64 downloaded, const quint64 unmatched, const quint64 errors, const QString &newQSLText, const QString &updatedQSLText, const QString &unmatchedQSLText); }; #endif // QLOG_UI_QSLIMPORTSTATDIALOG_H ================================================ FILE: ui/QSLImportStatDialog.ui ================================================ QSLImportStatDialog 0 0 481 562 QSL Import Summary Summary Downloaded: Updated: Unmatched: Errors: Details true Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse Qt::Horizontal QDialogButtonBox::Close buttonBox accepted() QSLImportStatDialog accept() 248 254 157 274 buttonBox rejected() QSLImportStatDialog reject() 316 260 286 274 ================================================ FILE: ui/QSLPrintLabelDialog.cpp ================================================ #include "ui/QSLPrintLabelDialog.h" #include "ui_QSLPrintLabelDialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core/debug.h" #include "core/LogParam.h" #include "core/QSOFilterManager.h" #include "data/StationProfile.h" #include "models/SqlListModel.h" #include "data/Data.h" MODULE_IDENTIFICATION("qlog.ui.qslprintlabeldialog"); QSLPrintLabelDialog::QSLPrintLabelDialog(QWidget *parent) : QDialog(parent), ui(new Ui::QSLPrintLabelDialog) { FCT_IDENTIFICATION; ui->setupUi(this); const QList widgets = findChildren(); for ( QWidget *w : widgets ) w->blockSignals(true); /* Sections start collapsed via maxHeight=0 instead of setVisible(false) * so that their width still contributes to the scroll area's sizeHint */ ui->templateSectionContent->setMaximumHeight(0); ui->printOptionsSectionContent->setMaximumHeight(0); /* Populate template combo: predefined + Custom */ const QList templates = QSLPrintLabelRenderer::predefinedTemplates(); for ( const LabelTemplate &tmpl : templates ) ui->templateComboBox->addItem(tmpl.name); ui->templateComboBox->addItem(tr("Custom")); /* Populate page size combo */ ui->pageSizeComboBox->addItem("A4", static_cast(QPageSize::A4)); ui->pageSizeComboBox->addItem("Letter", static_cast(QPageSize::Letter)); /* Populate callsign combo */ ui->myCallsignComboBox->setModel(new SqlListModel("SELECT DISTINCT UPPER(station_callsign) FROM contacts ORDER BY station_callsign", "", ui->myCallsignComboBox)); ui->myCallsignComboBox->setCurrentText(StationProfilesManager::instance()->getCurProfile1().callsign.toUpper()); /* Populate station profile combo */ ui->stationProfileComboBox->addItems(StationProfilesManager::instance()->profileNameList()); const StationProfile &profile = StationProfilesManager::instance()->getCurProfile1(); int profileIndex = ui->stationProfileComboBox->findText(profile.profileName); ui->stationProfileComboBox->setCurrentIndex( ( profileIndex >= 0 ) ? profileIndex : -1 ); /* Populate QSL sent combo */ populateQSLSentCombo(); /* Populate user filter combo */ ui->userFilterComboBox->setModel(QSOFilterManager::QSOFilterModel("", ui->userFilterComboBox)); ui->userFilterCheckBox->setEnabled(ui->userFilterComboBox->count() > 0); /* Mono font combo: show only monospaced fonts */ ui->monoFontComboBox->setFontFilters(QFontComboBox::MonospacedFonts); /* Populate extra column combobox from contacts table columns */ populateExtraColumnCombo(); /* Date edits */ ui->startDateEdit->setDisplayFormat(locale.formatDateShortWithYYYY()); ui->startDateEdit->setDate(QDate::currentDate().addYears(-1)); ui->endDateEdit->setDisplayFormat(locale.formatDateShortWithYYYY()); ui->endDateEdit->setDate(QDate::currentDate()); /* Section collapse/expand -- toggle maxHeight and arrow direction. * Using maxHeight instead of setVisible keeps the widget in layout * calculations so the scroll area's width is always correct. */ connect(ui->filterSectionButton, &QToolButton::toggled, this, [this](bool checked) { ui->filterSectionButton->setArrowType(checked ? Qt::DownArrow : Qt::RightArrow); ui->filterSectionContent->setMaximumHeight(checked ? QWIDGETSIZE_MAX : 0); }); connect(ui->templateSectionButton, &QToolButton::toggled, this, [this](bool checked) { ui->templateSectionButton->setArrowType(checked ? Qt::DownArrow : Qt::RightArrow); ui->templateSectionContent->setMaximumHeight(checked ? QWIDGETSIZE_MAX : 0); }); connect(ui->printOptionsSectionButton, &QToolButton::toggled, this, [this](bool checked) { ui->printOptionsSectionButton->setArrowType(checked ? Qt::DownArrow : Qt::RightArrow); ui->printOptionsSectionContent->setMaximumHeight(checked ? QWIDGETSIZE_MAX : 0); }); /* Load persisted settings and refresh data BEFORE connecting signals * to avoid triggering a refresh storm during initialization */ loadSettings(); for ( QWidget *w : widgets ) w->blockSignals(false); QTimer::singleShot(0, this, &QSLPrintLabelDialog::refreshData); QScroller::grabGesture(ui->previewScrollArea->viewport(), QScroller::LeftMouseButtonGesture); ui->previewScrollArea->viewport()->installEventFilter(this); } QSLPrintLabelDialog::~QSLPrintLabelDialog() { FCT_IDENTIFICATION; saveSettings(); delete ui; } void QSLPrintLabelDialog::loadSettings() { FCT_IDENTIFICATION; int templateIndex = LogParam::getQslLabelTemplate(); if ( templateIndex >= 0 && templateIndex < ui->templateComboBox->count() ) ui->templateComboBox->setCurrentIndex(templateIndex); ui->footerLeftEdit->setText(LogParam::getQslLabelFooterLeft()); ui->footerRightEdit->setText(LogParam::getQslLabelFooterRight()); ui->skipSpinBox->setValue(LogParam::getQslLabelSkip()); zoomPercent = LogParam::getQslLabelZoom(); ui->zoomSlider->setValue(zoomPercent); ui->zoomSpinBox->setValue(zoomPercent); ui->printBordersCheckBox->setChecked(LogParam::getQslLabelPrintBorders()); ui->dateFormatEdit->setText(LogParam::getQslLabelDateFormat()); const QString savedSansFont = LogParam::getQslLabelSansFont(); if ( !savedSansFont.isEmpty() ) ui->sansFontComboBox->setCurrentFont(QFont(savedSansFont)); const QString savedMonoFont = LogParam::getQslLabelMonoFont(); if ( !savedMonoFont.isEmpty() ) ui->monoFontComboBox->setCurrentFont(QFont(savedMonoFont)); /* extraColumnComboBox is populated in constructor before loadSettings() is called */ const QString savedExtraCol = LogParam::getQslLabelExtraColumn(); int extraIdx = ui->extraColumnComboBox->findData(savedExtraCol); ui->extraColumnComboBox->setCurrentIndex(extraIdx >= 0 ? extraIdx : 0); ui->columnHeaderEdit->setText(LogParam::getQslLabelExtraColumnHeader()); ui->toRadioTextEdit->setText(LogParam::getQslLabelToRadioText()); ui->hdrDateEdit->setText(LogParam::getQslLabelHdrDate()); ui->hdrTimeEdit->setText(LogParam::getQslLabelHdrTime()); ui->hdrBandEdit->setText(LogParam::getQslLabelHdrBand()); ui->hdrModeEdit->setText(LogParam::getQslLabelHdrMode()); ui->hdrQslEdit->setText(LogParam::getQslLabelHdrQsl()); ui->maxRowsSpinBox->setValue(LogParam::getQslLabelMaxRows()); ui->toRadioSizeSpinBox->setValue(LogParam::getQslLabelFontSizeToRadio()); ui->callsignSizeSpinBox->setValue(LogParam::getQslLabelFontSizeCallsign()); ui->headerSizeSpinBox->setValue(LogParam::getQslLabelFontSizeHeader()); ui->dataSizeSpinBox->setValue(LogParam::getQslLabelFontSizeData()); /* Load custom template fields from LogParam */ int pageSizeIdx = LogParam::getQslLabelCustomPageSize(); if ( pageSizeIdx >= 0 && pageSizeIdx < ui->pageSizeComboBox->count() ) ui->pageSizeComboBox->setCurrentIndex(pageSizeIdx); ui->colsSpinBox->setValue(LogParam::getQslLabelCustomCols()); ui->rowsSpinBox->setValue(LogParam::getQslLabelCustomRows()); ui->labelWidthSpinBox->setValue(LogParam::getQslLabelCustomLabelWidth()); ui->labelHeightSpinBox->setValue(LogParam::getQslLabelCustomLabelHeight()); ui->leftMarginSpinBox->setValue(LogParam::getQslLabelCustomLeftMargin()); ui->topMarginSpinBox->setValue(LogParam::getQslLabelCustomTopMargin()); ui->hSpacingSpinBox->setValue(LogParam::getQslLabelCustomHSpacing()); ui->vSpacingSpinBox->setValue(LogParam::getQslLabelCustomVSpacing()); /* Apply template fields state based on selected template */ const QList templates = QSLPrintLabelRenderer::predefinedTemplates(); if ( templateIndex >= 0 && templateIndex < templates.size() ) { populateTemplateFields(templates.at(templateIndex)); setTemplateFieldsEnabled(false); } else { setTemplateFieldsEnabled(true); } } void QSLPrintLabelDialog::saveSettings() { FCT_IDENTIFICATION; LogParam::setQslLabelTemplate(ui->templateComboBox->currentIndex()); LogParam::setQslLabelFooterLeft(ui->footerLeftEdit->text()); LogParam::setQslLabelFooterRight(ui->footerRightEdit->text()); LogParam::setQslLabelSkip(ui->skipSpinBox->value()); LogParam::setQslLabelZoom(zoomPercent); LogParam::setQslLabelPrintBorders(ui->printBordersCheckBox->isChecked()); LogParam::setQslLabelDateFormat(ui->dateFormatEdit->text()); LogParam::setQslLabelSansFont(ui->sansFontComboBox->currentFont().family()); LogParam::setQslLabelMonoFont(ui->monoFontComboBox->currentFont().family()); LogParam::setQslLabelExtraColumn(ui->extraColumnComboBox->currentData().toString()); LogParam::setQslLabelExtraColumnHeader(ui->columnHeaderEdit->text()); LogParam::setQslLabelToRadioText(ui->toRadioTextEdit->text()); LogParam::setQslLabelHdrDate(ui->hdrDateEdit->text()); LogParam::setQslLabelHdrTime(ui->hdrTimeEdit->text()); LogParam::setQslLabelHdrBand(ui->hdrBandEdit->text()); LogParam::setQslLabelHdrMode(ui->hdrModeEdit->text()); LogParam::setQslLabelHdrQsl(ui->hdrQslEdit->text()); LogParam::setQslLabelMaxRows(ui->maxRowsSpinBox->value()); LogParam::setQslLabelFontSizeToRadio(ui->toRadioSizeSpinBox->value()); LogParam::setQslLabelFontSizeCallsign(ui->callsignSizeSpinBox->value()); LogParam::setQslLabelFontSizeHeader(ui->headerSizeSpinBox->value()); LogParam::setQslLabelFontSizeData(ui->dataSizeSpinBox->value()); /* Save custom template fields */ LogParam::setQslLabelCustomPageSize(ui->pageSizeComboBox->currentIndex()); LogParam::setQslLabelCustomCols(ui->colsSpinBox->value()); LogParam::setQslLabelCustomRows(ui->rowsSpinBox->value()); LogParam::setQslLabelCustomLabelWidth(ui->labelWidthSpinBox->value()); LogParam::setQslLabelCustomLabelHeight(ui->labelHeightSpinBox->value()); LogParam::setQslLabelCustomLeftMargin(ui->leftMarginSpinBox->value()); LogParam::setQslLabelCustomTopMargin(ui->topMarginSpinBox->value()); LogParam::setQslLabelCustomHSpacing(ui->hSpacingSpinBox->value()); LogParam::setQslLabelCustomVSpacing(ui->vSpacingSpinBox->value()); } void QSLPrintLabelDialog::populateTemplateFields(const LabelTemplate &tmpl) { FCT_IDENTIFICATION; /* Block signals to avoid triggering customTemplateFieldChanged */ ui->pageSizeComboBox->blockSignals(true); ui->colsSpinBox->blockSignals(true); ui->rowsSpinBox->blockSignals(true); ui->labelWidthSpinBox->blockSignals(true); ui->labelHeightSpinBox->blockSignals(true); ui->leftMarginSpinBox->blockSignals(true); ui->topMarginSpinBox->blockSignals(true); ui->hSpacingSpinBox->blockSignals(true); ui->vSpacingSpinBox->blockSignals(true); int pageSizeIdx = ui->pageSizeComboBox->findData(static_cast(tmpl.pageSize)); if ( pageSizeIdx >= 0 ) ui->pageSizeComboBox->setCurrentIndex(pageSizeIdx); ui->colsSpinBox->setValue(tmpl.cols); ui->rowsSpinBox->setValue(tmpl.rows); ui->labelWidthSpinBox->setValue(tmpl.labelWidthMm); ui->labelHeightSpinBox->setValue(tmpl.labelHeightMm); ui->leftMarginSpinBox->setValue(tmpl.leftMarginMm); ui->topMarginSpinBox->setValue(tmpl.topMarginMm); ui->hSpacingSpinBox->setValue(tmpl.hSpacingMm); ui->vSpacingSpinBox->setValue(tmpl.vSpacingMm); ui->pageSizeComboBox->blockSignals(false); ui->colsSpinBox->blockSignals(false); ui->rowsSpinBox->blockSignals(false); ui->labelWidthSpinBox->blockSignals(false); ui->labelHeightSpinBox->blockSignals(false); ui->leftMarginSpinBox->blockSignals(false); ui->topMarginSpinBox->blockSignals(false); ui->hSpacingSpinBox->blockSignals(false); ui->vSpacingSpinBox->blockSignals(false); } void QSLPrintLabelDialog::setTemplateFieldsEnabled(bool enabled) { FCT_IDENTIFICATION; ui->pageSizeComboBox->setEnabled(enabled); ui->colsSpinBox->setEnabled(enabled); ui->rowsSpinBox->setEnabled(enabled); ui->labelWidthSpinBox->setEnabled(enabled); ui->labelHeightSpinBox->setEnabled(enabled); ui->leftMarginSpinBox->setEnabled(enabled); ui->topMarginSpinBox->setEnabled(enabled); ui->hSpacingSpinBox->setEnabled(enabled); ui->vSpacingSpinBox->setEnabled(enabled); } LabelTemplate QSLPrintLabelDialog::buildCustomTemplate() const { FCT_IDENTIFICATION; LabelTemplate tmpl; tmpl.name = tr("Custom"); tmpl.orientation = QPageLayout::Portrait; tmpl.pageSize = static_cast(ui->pageSizeComboBox->currentData().toInt()); tmpl.cols = ui->colsSpinBox->value(); tmpl.rows = ui->rowsSpinBox->value(); tmpl.labelWidthMm = ui->labelWidthSpinBox->value(); tmpl.labelHeightMm = ui->labelHeightSpinBox->value(); tmpl.leftMarginMm = ui->leftMarginSpinBox->value(); tmpl.topMarginMm = ui->topMarginSpinBox->value(); tmpl.hSpacingMm = ui->hSpacingSpinBox->value(); tmpl.vSpacingMm = ui->vSpacingSpinBox->value(); return tmpl; } void QSLPrintLabelDialog::populateExtraColumnCombo() { FCT_IDENTIFICATION; ui->extraColumnComboBox->addItem(tr("Empty"), QString()); QSqlRecord contactsRecord = QSqlDatabase::database().record("contacts"); QList> dbFieldItems; for ( int i = LogbookModel::ColumnID::COLUMN_ID; i < LogbookModel::ColumnID::COLUMN_LAST_ELEMENT; ++i ) { LogbookModel::ColumnID columnID = static_cast(i); const QString translation = LogbookModel::getFieldNameTranslation(columnID); if ( translation.isEmpty() ) continue; const QString dbField = contactsRecord.fieldName(i); if ( dbField.isEmpty() ) continue; dbFieldItems.append({translation, dbField}); } std::sort(dbFieldItems.begin(), dbFieldItems.end(), [](const QPair &a, const QPair &b) { return a.first.localeAwareCompare(b.first) < 0; }); for ( const QPair &item : static_cast>&>(dbFieldItems) ) ui->extraColumnComboBox->addItem(item.first, item.second); } void QSLPrintLabelDialog::populateQSLSentCombo() { FCT_IDENTIFICATION; QMapIterator iter(Data::instance()->qslSentEnum); int iter_index = 0; int value_index = 0; while ( iter.hasNext() ) { iter.next(); ui->qslSentComboBox->addItem(iter.value(), iter.key()); if ( iter.key() == "Q" ) // Queued by default value_index = iter_index; iter_index++; } ui->qslSentComboBox->setCurrentIndex(value_index); } void QSLPrintLabelDialog::toggleDateRange() { FCT_IDENTIFICATION; ui->startDateEdit->setEnabled(ui->dateRangeCheckBox->isChecked()); ui->endDateEdit->setEnabled(ui->dateRangeCheckBox->isChecked()); refreshData(); } void QSLPrintLabelDialog::toggleMyCallsign() { FCT_IDENTIFICATION; ui->myCallsignComboBox->setEnabled(ui->myCallsignCheckBox->isChecked()); if ( ui->myCallsignCheckBox->isChecked() ) ui->stationProfileCheckBox->setChecked(false); refreshData(); } void QSLPrintLabelDialog::toggleStationProfile() { FCT_IDENTIFICATION; ui->stationProfileComboBox->setEnabled(ui->stationProfileCheckBox->isChecked()); if ( ui->stationProfileCheckBox->isChecked() ) { const StationProfile &selectedStationProfile = StationProfilesManager::instance()->getProfile(ui->stationProfileComboBox->currentText()); const QString toolTip = tr("QSOs matching this station profile") + "
" + selectedStationProfile.toHTMLString(); ui->stationProfileComboBox->setToolTip(toolTip); ui->myCallsignCheckBox->setChecked(false); } else ui->stationProfileComboBox->setToolTip({}); refreshData(); } void QSLPrintLabelDialog::toggleQslSent() { FCT_IDENTIFICATION; ui->qslSentComboBox->setEnabled(ui->qslSentCheckBox->isChecked()); refreshData(); } void QSLPrintLabelDialog::toggleUserFilter() { FCT_IDENTIFICATION; ui->userFilterComboBox->setEnabled(ui->userFilterCheckBox->isChecked()); refreshData(); } void QSLPrintLabelDialog::templateChanged(int index) { FCT_IDENTIFICATION; qCDebug(function_parameters) << index; const QList templates = QSLPrintLabelRenderer::predefinedTemplates(); if ( index >= 0 && index < templates.size() ) { /* Predefined template selected - show values, disable fields */ populateTemplateFields(templates.at(index)); setTemplateFieldsEnabled(false); } else { /* Custom template - enable fields, load custom values from spinboxes */ setTemplateFieldsEnabled(true); } refreshData(); } void QSLPrintLabelDialog::skipChanged(int value) { FCT_IDENTIFICATION; qCDebug(function_parameters) << value; renderer.setSkipLabels(value); updatePreview(); } void QSLPrintLabelDialog::zoomChanged(int value) { FCT_IDENTIFICATION; qCDebug(function_parameters) << value; zoomPercent = value; updatePreview(); } void QSLPrintLabelDialog::customTemplateFieldChanged() { FCT_IDENTIFICATION; /* Only respond if Custom template is selected */ const QList templates = QSLPrintLabelRenderer::predefinedTemplates(); int index = ui->templateComboBox->currentIndex(); if ( index >= 0 && index < templates.size() ) return; refreshData(); } void QSLPrintLabelDialog::resizeEvent(QResizeEvent *event) { FCT_IDENTIFICATION; QWidget::resizeEvent(event); updatePreview(); } bool QSLPrintLabelDialog::eventFilter(QObject *obj, QEvent *event) { if ( obj == ui->previewScrollArea->viewport() && event->type() == QEvent::Wheel ) { QWheelEvent *wheelEvent = dynamic_cast(event); /* * CRTL + Mouse Wheel * * Zoom In/Out */ if ( wheelEvent && wheelEvent->modifiers() & Qt::ControlModifier ) { QPoint wheelDelta(wheelEvent->angleDelta()); int delta = ( wheelDelta.y() > 0 ) ? 1 : -1; int newZoom = zoomPercent + delta * 5; ui->zoomSpinBox->setValue(newZoom); return true; } } return QDialog::eventFilter(obj, event); } QString QSLPrintLabelDialog::buildWhereClause() const { FCT_IDENTIFICATION; QStringList where; where << "1 = 1"; if ( ui->dateRangeCheckBox->isChecked() ) where << "(datetime(start_time) BETWEEN datetime(:start_date) AND datetime(:end_date))"; if ( ui->myCallsignCheckBox->isChecked() ) where << "upper(station_callsign) = upper(:stationCallsign)"; if ( ui->stationProfileCheckBox->isChecked() ) { const StationProfile &stProfile = StationProfilesManager::instance()->getProfile( ui->stationProfileComboBox->currentText()); where << QString("EXISTS (" "SELECT 1 FROM station_profiles" " WHERE station_profiles.profile_name = :stationProfileName" " AND %1" ")").arg(stProfile.getContactInnerJoin()); } if ( ui->qslSentCheckBox->isChecked() ) where << "upper(qsl_sent) = upper(:qslSent)"; if ( ui->userFilterCheckBox->isChecked() ) { QString filterClause = QSOFilterManager::getWhereClause(ui->userFilterComboBox->currentText()); if ( !filterClause.isEmpty() ) where << filterClause; } return where.join(" AND "); } void QSLPrintLabelDialog::bindWhereClause(QSqlQuery &query) const { FCT_IDENTIFICATION; if ( ui->dateRangeCheckBox->isChecked() ) { QDate startDate = ui->startDateEdit->date(); QDate endDate = ui->endDateEdit->date(); #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) query.bindValue(":start_date", startDate.startOfDay()); query.bindValue(":end_date", endDate.endOfDay()); #else query.bindValue(":start_date", QDateTime(startDate)); query.bindValue(":end_date", QDateTime(endDate)); #endif } if ( ui->myCallsignCheckBox->isChecked() ) query.bindValue(":stationCallsign", ui->myCallsignComboBox->currentText()); if ( ui->stationProfileCheckBox->isChecked() ) query.bindValue(":stationProfileName", ui->stationProfileComboBox->currentText()); if ( ui->qslSentCheckBox->isChecked() ) query.bindValue(":qslSent", ui->qslSentComboBox->currentData().toString()); } void QSLPrintLabelDialog::buildLabels() { FCT_IDENTIFICATION; labelsData.clear(); const QString whereClause = buildWhereClause(); const QString dateFormat = ui->dateFormatEdit->text().trimmed(); const QString effectiveDateFormat = dateFormat.isEmpty() ? "yyyy-MM-dd" : dateFormat; const int maxRows = ui->maxRowsSpinBox->value(); const QString extraCol = ui->extraColumnComboBox->currentData().toString(); const QString extraColSelect = extraCol.isEmpty() ? QString() : QString(", \"%1\"").arg(extraCol); const QString sql = QString("SELECT start_time, callsign, band, mode, submode, " "qsl_rcvd, lotw_qsl_rcvd, eqsl_qsl_rcvd, dcl_qsl_rcvd" "%1 " "FROM contacts " "WHERE %2 " "ORDER BY callsign COLLATE NOCASE ASC, start_time ASC") .arg(extraColSelect, whereClause); QSqlQuery query; if ( !query.prepare(sql) ) { qCWarning(runtime) << "Cannot prepare QSL label query" << query.lastError().text(); return; } bindWhereClause(query); if ( !query.exec() ) { qCWarning(runtime) << "Cannot execute QSL label query" << query.lastError().text(); return; } QString currentCallsign; QList currentRows; while ( query.next() ) { const QDateTime startTime = query.value("start_time").toDateTime(); const QString callsign = query.value("callsign").toString().toUpper(); const QString band = query.value("band").toString().toUpper(); const QString mode = query.value("mode").toString().toUpper(); const QString submode = query.value("submode").toString().toUpper(); /* Mode normalization: * - If submode is present and non-empty, use submode * - SSB with USB/LSB submode stays SSB * - Otherwise use mode */ QString displayMode; if ( mode == "SSB" && ( submode == "USB" || submode == "LSB" ) ) { displayMode = "SSB"; } else if ( !submode.isEmpty() ) displayMode = submode.toUpper(); else displayMode = mode.toUpper(); /* QSL logic: TNX if any *_rcvd == 'Y', else PSE */ const QString qslRcvd = query.value("qsl_rcvd").toString().toUpper(); const QString lotwRcvd = query.value("lotw_qsl_rcvd").toString().toUpper(); const QString eqslRcvd = query.value("eqsl_qsl_rcvd").toString().toUpper(); const QString dclRcvd = query.value("dcl_qsl_rcvd").toString().toUpper(); const QString qslText = ( qslRcvd == "Y" || lotwRcvd == "Y" || eqslRcvd == "Y" || dclRcvd == "Y" ) ? "TNX" : "PSE"; QSLLabelData::QsoRow row; row.date = startTime.toUTC().toString(effectiveDateFormat); row.time = startTime.toUTC().toString("HH:mm"); row.band = band; row.mode = displayMode; row.qsl = qslText; row.extra = extraCol.isEmpty() ? QString() : query.value(extraCol).toString(); /* Group by callsign, split into chunks of 4 QSOs per label */ if ( callsign != currentCallsign ) { /* Flush previous callsign's rows */ while ( !currentRows.isEmpty() ) { QSLLabelData label; label.callsign = currentCallsign; int count = qMin(maxRows, currentRows.size()); for ( int i = 0; i < count; ++i ) label.qsos.append(currentRows.takeFirst()); labelsData.append(label); } currentCallsign = callsign; } currentRows.append(row); /* If we have maxRows rows, emit a label */ if ( currentRows.size() == maxRows ) { QSLLabelData label; label.callsign = currentCallsign; for ( int i = 0; i < maxRows; ++i ) label.qsos.append(currentRows.takeFirst()); labelsData.append(label); } } /* Flush remaining rows for the last callsign */ while ( !currentRows.isEmpty() ) { QSLLabelData label; label.callsign = currentCallsign; int count = qMin(maxRows, currentRows.size()); for ( int i = 0; i < count; ++i ) label.qsos.append(currentRows.takeFirst()); labelsData.append(label); } } void QSLPrintLabelDialog::refreshData() { FCT_IDENTIFICATION; buildLabels(); /* Set template on renderer */ const QList templates = QSLPrintLabelRenderer::predefinedTemplates(); int templateIndex = ui->templateComboBox->currentIndex(); if ( templateIndex >= 0 && templateIndex < templates.size() ) renderer.setTemplate(templates.at(templateIndex)); else renderer.setTemplate(buildCustomTemplate()); renderer.setLabels(labelsData); renderer.setSkipLabels(ui->skipSpinBox->value()); currentPage = 0; updatePreview(); } void QSLPrintLabelDialog::updatePreview() { FCT_IDENTIFICATION; renderer.setFooterLeft(ui->footerLeftEdit->text()); renderer.setFooterRight(ui->footerRightEdit->text()); renderer.setPrintBorders(ui->printBordersCheckBox->isChecked()); LabelStyleOptions styleOpts; styleOpts.sansFontFamily = ui->sansFontComboBox->currentFont().family(); styleOpts.monoFontFamily = ui->monoFontComboBox->currentFont().family(); styleOpts.toRadioFontSize = ui->toRadioSizeSpinBox->value(); styleOpts.callsignFontSize = ui->callsignSizeSpinBox->value(); styleOpts.headerFontSize = ui->headerSizeSpinBox->value(); styleOpts.dataFontSize = ui->dataSizeSpinBox->value(); const QString extraCol = ui->extraColumnComboBox->currentText(); const QString customHeader = ui->columnHeaderEdit->text().trimmed(); styleOpts.extraColumnHeader = (extraCol.isEmpty() || ui->extraColumnComboBox->currentIndex() == 0) ? QString() : (customHeader.isEmpty() ? extraCol : customHeader); styleOpts.maxQsoRows = ui->maxRowsSpinBox->value(); const QString toRadioText = ui->toRadioTextEdit->text().trimmed(); if ( !toRadioText.isEmpty() ) styleOpts.toRadioText = toRadioText; const QString hdrDate = ui->hdrDateEdit->text().trimmed(); if ( !hdrDate.isEmpty() ) styleOpts.hdrDate = hdrDate; const QString hdrTime = ui->hdrTimeEdit->text().trimmed(); if ( !hdrTime.isEmpty() ) styleOpts.hdrTime = hdrTime; const QString hdrBand = ui->hdrBandEdit->text().trimmed(); if ( !hdrBand.isEmpty() ) styleOpts.hdrBand = hdrBand; const QString hdrMode = ui->hdrModeEdit->text().trimmed(); if ( !hdrMode.isEmpty() ) styleOpts.hdrMode = hdrMode; const QString hdrQsl = ui->hdrQslEdit->text().trimmed(); if ( !hdrQsl.isEmpty() ) styleOpts.hdrQsl = hdrQsl; renderer.setStyleOptions(styleOpts); int totalPages = renderer.pageCount(); int totalLabels = renderer.labelCount(); bool hasLabels = ( totalLabels > 0 ); ui->printButton->setEnabled(hasLabels); ui->exportPdfButton->setEnabled(hasLabels); ui->statusLabel->setText(tr("Labels: %1 (%2 pages)") .arg(totalLabels) .arg(totalPages)); updatePageNavigation(); if ( totalPages > 0 && currentPage < totalPages ) { QImage pageImage = renderer.renderPage(currentPage); /* Calculate scaled width: fit-to-viewport * zoom% */ int fitWidth = ui->previewScrollArea->viewport()->width() - 10; if ( fitWidth > 0 ) { int scaledWidth = fitWidth * zoomPercent / 100; QPixmap pixmap = QPixmap::fromImage(pageImage); ui->previewLabel->setPixmap(pixmap.scaledToWidth(scaledWidth, Qt::SmoothTransformation)); } else { ui->previewLabel->setPixmap(QPixmap::fromImage(pageImage)); } ui->previewLabel->setAlignment(Qt::AlignCenter); } else { ui->previewLabel->clear(); ui->previewLabel->setText(tr("No matching QSOs found")); ui->previewLabel->setAlignment(Qt::AlignCenter); } } void QSLPrintLabelDialog::updatePageNavigation() { FCT_IDENTIFICATION; int totalPages = renderer.pageCount(); ui->prevPageButton->setEnabled(currentPage > 0); ui->nextPageButton->setEnabled(totalPages > 0 && currentPage < totalPages - 1); if ( totalPages > 0 ) ui->pageLabel->setText(tr("Page %1 of %2").arg(currentPage + 1).arg(totalPages)); else ui->pageLabel->setText(tr("Page 0 of 0")); } void QSLPrintLabelDialog::prevPage() { FCT_IDENTIFICATION; if ( currentPage > 0 ) { currentPage--; updatePreview(); } } void QSLPrintLabelDialog::nextPage() { FCT_IDENTIFICATION; int totalPages = renderer.pageCount(); if ( totalPages > 0 && currentPage < totalPages - 1 ) { currentPage++; updatePreview(); } } void QSLPrintLabelDialog::print() { FCT_IDENTIFICATION; QPrinter printer(QPrinter::HighResolution); QPrintDialog printDialog(&printer, this); if ( printDialog.exec() == QDialog::Accepted ) { renderer.printAll(&printer); askAndMarkQslSent(); } } void QSLPrintLabelDialog::exportPdf() { FCT_IDENTIFICATION; QSettings settings; const QString lastPath = settings.value("qsllabel/last_pdf_path", QDir::homePath()).toString(); QString filename = QFileDialog::getSaveFileName(this, tr("Export PDF"), lastPath, tr("PDF Files (*.pdf)")); if ( filename.isEmpty() ) return; settings.setValue("qsllabel/last_pdf_path", QFileInfo(filename).path()); QPrinter printer(QPrinter::HighResolution); printer.setOutputFormat(QPrinter::PdfFormat); printer.setOutputFileName(filename); renderer.printAll(&printer); askAndMarkQslSent(); } void QSLPrintLabelDialog::askAndMarkQslSent() { FCT_IDENTIFICATION; const QMessageBox::StandardButton answer = QMessageBox::question( this, tr("Mark as Sent"), tr("Mark printed/exported QSOs as sent?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if ( answer != QMessageBox::Yes ) return; const QString whereClause = buildWhereClause(); const QString sql = QString("UPDATE contacts " "SET qsl_sent = 'Y', " "qsl_sdate = strftime('%Y-%m-%d', DATETIME('now', 'utc')) " "WHERE %1").arg(whereClause); QSqlQuery query; if ( !query.prepare(sql) ) { qCWarning(runtime) << "Cannot prepare mark-sent query" << query.lastError().text(); return; } bindWhereClause(query); if ( !query.exec() ) qCWarning(runtime) << "Cannot execute mark-sent query" << query.lastError().text(); } ================================================ FILE: ui/QSLPrintLabelDialog.h ================================================ #ifndef QLOG_UI_QSLPRINTLABELDIALOG_H #define QLOG_UI_QSLPRINTLABELDIALOG_H #include #include "core/LogLocale.h" #include "core/QSLPrintLabelRenderer.h" class QSqlQuery; namespace Ui { class QSLPrintLabelDialog; } class QSLPrintLabelDialog : public QDialog { Q_OBJECT public: explicit QSLPrintLabelDialog(QWidget *parent = nullptr); ~QSLPrintLabelDialog(); private slots: void toggleDateRange(); void toggleMyCallsign(); void toggleStationProfile(); void toggleQslSent(); void toggleUserFilter(); void refreshData(); void updatePreview(); void prevPage(); void nextPage(); void print(); void exportPdf(); void templateChanged(int index); void skipChanged(int value); void zoomChanged(int value); void customTemplateFieldChanged(); protected: void resizeEvent(QResizeEvent* event) override; bool eventFilter(QObject *obj, QEvent *event) override; private: Ui::QSLPrintLabelDialog *ui; LogLocale locale; QSLPrintLabelRenderer renderer; QList labelsData; int currentPage = 0; int zoomPercent = 100; void buildLabels(); QString buildWhereClause() const; void bindWhereClause(QSqlQuery &query) const; void askAndMarkQslSent(); void updatePageNavigation(); void saveSettings(); void loadSettings(); void populateTemplateFields(const LabelTemplate &tmpl); void setTemplateFieldsEnabled(bool enabled); LabelTemplate buildCustomTemplate() const; void populateExtraColumnCombo(); void populateQSLSentCombo(); }; #endif // QLOG_UI_QSLPRINTLABELDIALOG_H ================================================ FILE: ui/QSLPrintLabelDialog.ui ================================================ QSLPrintLabelDialog 0 0 1108 700 Print QSL Labels true 0 0 QFrame::NoFrame Qt::ScrollBarAlwaysOn Qt::ScrollBarAlwaysOff QAbstractScrollArea::AdjustToContents true 0 0 393 1300 2 4 4 4 4 0 0 Filter true true Qt::ToolButtonTextBesideIcon Qt::DownArrow true 0 0 0 4 Date Range false 0 0 true false 0 0 true My Callsign true true QComboBox::AdjustToContents Station Profile false QSL Sent false User Filter false 0 0 Label Template true false Qt::ToolButtonTextBesideIcon Qt::RightArrow true 0 0 0 4 Page Size: false Columns: false 1 10 Rows: false 1 20 Label Width: false mm 1 10.000000000000000 300.000000000000000 0.500000000000000 Label Height: false mm 1 10.000000000000000 300.000000000000000 0.500000000000000 Left Margin: false mm 1 0.000000000000000 100.000000000000000 0.500000000000000 Top Margin: false mm 1 0.000000000000000 100.000000000000000 0.500000000000000 H Spacing: false mm 1 0.000000000000000 50.000000000000000 0.500000000000000 V Spacing: false mm 1 0.000000000000000 50.000000000000000 0.500000000000000 0 0 Label Appearance true false Qt::ToolButtonTextBesideIcon Qt::RightArrow true 6 0 0 0 0 Print Label Borders QSOs per Label: 1 10 4 Footer Left Text: Footer Right Text: Skip Label: 0 99 Sans Font: false Ubuntu Sans QComboBox::AdjustToMinimumContentsLengthWithIcon 16 Mono Font: false Andale Mono QComboBox::AdjustToMinimumContentsLengthWithIcon 16 QFontComboBox::MonospacedFonts Callsign Size: pt 1 4.000000000000000 72.000000000000000 0.500000000000000 14.000000000000000 "To Radio" Size: pt 1 4.000000000000000 72.000000000000000 0.500000000000000 7.500000000000000 "To Radio" Text: To Radio Header Size: pt 1 4.000000000000000 72.000000000000000 0.500000000000000 7.000000000000000 Data Size: pt 1 4.000000000000000 72.000000000000000 0.500000000000000 8.000000000000000 Date Header Text: Date Date Format: yyyy-MM-dd Time Header Text: Time Band Header Text: Band Mode Header Text: Mode QSL Header Text: QSL Extra Column: QComboBox::AdjustToContents Extra Column Text (DB column name) Qt::Vertical 20 40 true 0 0 671 568 No matching QSOs found Qt::AlignCenter Zoom: 25 200 99 Qt::Horizontal QSlider::TicksBelow 25 % 25 200 100 Qt::Horizontal 40 20 false 0 0 32 16777215 < Page 0 of 0 Qt::AlignCenter false 0 0 32 16777215 > Qt::Horizontal 40 20 Labels: 0 (0 pages) Qt::Horizontal 40 20 false Print .. Qt::Horizontal QSizePolicy::Maximum 40 20 false Export as PDF .. Qt::Horizontal 40 20 0 0 QDialogButtonBox::Close BaseDoubleSpinBox QDoubleSpinBox
ui/component/BaseDoubleSpinBox.h
zoomSlider valueChanged(int) zoomSpinBox setValue(int) 1017 611 1095 615 zoomSpinBox valueChanged(int) zoomSlider setValue(int) 1095 615 1017 611 buttonBox rejected() QSLPrintLabelDialog reject() 1057 675 553 349 dateRangeCheckBox toggled(bool) QSLPrintLabelDialog toggleDateRange() 71 63 553 349 myCallsignCheckBox toggled(bool) QSLPrintLabelDialog toggleMyCallsign() 69 101 553 349 stationProfileCheckBox toggled(bool) QSLPrintLabelDialog toggleStationProfile() 81 136 553 349 qslSentCheckBox toggled(bool) QSLPrintLabelDialog toggleQslSent() 62 171 553 349 userFilterCheckBox toggled(bool) QSLPrintLabelDialog toggleUserFilter() 66 206 553 349 startDateEdit dateChanged(QDate) QSLPrintLabelDialog refreshData() 212 65 553 349 endDateEdit dateChanged(QDate) QSLPrintLabelDialog refreshData() 334 65 553 349 myCallsignComboBox currentTextChanged(QString) QSLPrintLabelDialog refreshData() 273 102 553 349 stationProfileComboBox currentTextChanged(QString) QSLPrintLabelDialog refreshData() 273 137 553 349 qslSentComboBox currentTextChanged(QString) QSLPrintLabelDialog refreshData() 273 172 553 349 userFilterComboBox currentTextChanged(QString) QSLPrintLabelDialog refreshData() 273 207 553 349 templateComboBox currentIndexChanged(int) QSLPrintLabelDialog templateChanged(int) 206 281 553 349 skipSpinBox valueChanged(int) QSLPrintLabelDialog skipChanged(int) 290 819 553 349 footerLeftEdit textChanged(QString) QSLPrintLabelDialog updatePreview() 290 749 553 349 footerRightEdit textChanged(QString) QSLPrintLabelDialog updatePreview() 290 784 553 349 prevPageButton clicked() QSLPrintLabelDialog prevPage() 673 636 553 349 nextPageButton clicked() QSLPrintLabelDialog nextPage() 846 636 553 349 printButton clicked() QSLPrintLabelDialog print() 837 675 553 349 exportPdfButton clicked() QSLPrintLabelDialog exportPdf() 947 675 553 349 zoomSpinBox valueChanged(int) QSLPrintLabelDialog zoomChanged(int) 1059 598 553 349 printBordersCheckBox toggled(bool) QSLPrintLabelDialog updatePreview() 98 679 553 349 dateFormatEdit textChanged(QString) QSLPrintLabelDialog refreshData() 290 1139 553 349 columnHeaderEdit textChanged(QString) QSLPrintLabelDialog updatePreview() 290 1349 553 349 toRadioTextEdit textChanged(QString) QSLPrintLabelDialog updatePreview() 290 997 553 349 hdrDateEdit textChanged(QString) QSLPrintLabelDialog updatePreview() 290 1104 553 349 hdrTimeEdit textChanged(QString) QSLPrintLabelDialog updatePreview() 290 1174 553 349 hdrBandEdit textChanged(QString) QSLPrintLabelDialog updatePreview() 290 1209 553 349 hdrModeEdit textChanged(QString) QSLPrintLabelDialog updatePreview() 290 1244 553 349 hdrQslEdit textChanged(QString) QSLPrintLabelDialog updatePreview() 290 1279 553 349 maxRowsSpinBox valueChanged(int) QSLPrintLabelDialog refreshData() 290 713 553 349 extraColumnComboBox currentIndexChanged(int) QSLPrintLabelDialog refreshData() 290 1314 553 349 sansFontComboBox currentFontChanged(QFont) QSLPrintLabelDialog updatePreview() 290 855 553 349 monoFontComboBox currentFontChanged(QFont) QSLPrintLabelDialog updatePreview() 290 890 553 349 toRadioSizeSpinBox valueChanged(double) QSLPrintLabelDialog updatePreview() 290 961 553 349 callsignSizeSpinBox valueChanged(double) QSLPrintLabelDialog updatePreview() 290 925 553 349 headerSizeSpinBox valueChanged(double) QSLPrintLabelDialog updatePreview() 290 1032 553 349 dataSizeSpinBox valueChanged(double) QSLPrintLabelDialog updatePreview() 290 1068 553 349 pageSizeComboBox currentIndexChanged(int) QSLPrintLabelDialog customTemplateFieldChanged() 256 317 553 349 colsSpinBox valueChanged(int) QSLPrintLabelDialog customTemplateFieldChanged() 256 352 553 349 rowsSpinBox valueChanged(int) QSLPrintLabelDialog customTemplateFieldChanged() 256 388 553 349 labelWidthSpinBox valueChanged(double) QSLPrintLabelDialog customTemplateFieldChanged() 256 424 553 349 labelHeightSpinBox valueChanged(double) QSLPrintLabelDialog customTemplateFieldChanged() 256 460 553 349 leftMarginSpinBox valueChanged(double) QSLPrintLabelDialog customTemplateFieldChanged() 256 496 553 349 topMarginSpinBox valueChanged(double) QSLPrintLabelDialog customTemplateFieldChanged() 256 532 553 349 hSpacingSpinBox valueChanged(double) QSLPrintLabelDialog customTemplateFieldChanged() 256 568 553 349 vSpacingSpinBox valueChanged(double) QSLPrintLabelDialog customTemplateFieldChanged() 256 604 553 349 toggleDateRange() toggleMyCallsign() toggleStationProfile() toggleQslSent() toggleUserFilter() refreshData() templateChanged(int) skipChanged(int) updatePreview() prevPage() nextPage() print() exportPdf() zoomChanged(int) customTemplateFieldChanged()
================================================ FILE: ui/QSODetailDialog.cpp ================================================ #include #include #include #include #include #include #include #include #include "QSODetailDialog.h" #include "ui_QSODetailDialog.h" #include "core/debug.h" #include "data/Data.h" #include "PaperQSLDialog.h" #include "service/eqsl/Eqsl.h" #include "models/SqlListModel.h" #include "data/Gridsquare.h" #include "data/Callsign.h" #include "data/BandPlan.h" #include "core/LogParam.h" #include "component/SmartSearchBox.h" #include "ModeSelectionController.h" MODULE_IDENTIFICATION("qlog.ui.qsodetaildialog"); #define CHANGECSS "color: orange;" #define SAVE_BUTTON_TEXT 0 #define EDIT_BUTTON_TEXT 1 QSODetailDialog::QSODetailDialog(const QSqlRecord &qso, QWidget *parent) : QDialog(parent), ui(new Ui::QSODetailDialog), mapper(new QDataWidgetMapper(this)), model(new LogbookModelPrivate(this)), editedRecord(new QSqlRecord(qso)), isMainPageLoaded(false), main_page(new WebEnginePage(this)), layerControlHandler("qsodetail", parent) { FCT_IDENTIFICATION; ui->setupUi(this); /* model setting */ model->setFilter(QString("id = '%1'").arg(qso.value("id").toString())); model->select(); connect(model, &QSqlTableModel::beforeUpdate, this, &QSODetailDialog::handleBeforeUpdate); /* mapView setting */ main_page->setWebChannel(&channel); ui->mapView->setPage(main_page); main_page->load(QUrl(QStringLiteral("qrc:/res/map/onlinemap.html"))); ui->mapView->setFocusPolicy(Qt::ClickFocus); connect(ui->mapView, &QWebEngineView::loadFinished, this, &QSODetailDialog::mapLoaded); channel.registerObject("layerControlHandler", &layerControlHandler); /* Edit Button */ editButton = new QPushButton(getButtonText(EDIT_BUTTON_TEXT)); ui->buttonBox->addButton(editButton, QDialogButtonBox::ActionRole); connect(editButton, &QPushButton::clicked, this, &QSODetailDialog::editButtonPressed); /* Reset Button */ resetButton = new QPushButton(tr("&Reset")); ui->buttonBox->addButton(resetButton, QDialogButtonBox::ActionRole); connect(resetButton, &QPushButton::clicked, this, &QSODetailDialog::resetButtonPressed); /* Lookup Button */ lookupButton = new QPushButton(tr("&Lookup")); ui->buttonBox->addButton(lookupButton, QDialogButtonBox::ActionRole); connect(lookupButton, &QPushButton::clicked, this, &QSODetailDialog::lookupButtonPressed); lookupButtonMovie = new QMovie(this); lookupButtonMovie->setFileName(":/icons/loading.gif"); connect(lookupButtonMovie, &QMovie::frameChanged, this, [this] { this->lookupButton->setIcon(this->lookupButtonMovie->currentPixmap()); }); lookupButtonWaitingStyle(false); /* timeformat for DateTime */ ui->dateTimeOnEdit->setDisplayFormat(locale.formatDateShortWithYYYY() + " " + locale.formatTimeLongWithoutTZ()); ui->dateTimeOffEdit->setDisplayFormat(locale.formatDateShortWithYYYY() + " " + locale.formatTimeLongWithoutTZ()); ui->qslPaperReceiveDateEdit->setDisplayFormat(locale.formatDateShortWithYYYY()); ui->qslPaperSentDateEdit->setDisplayFormat(locale.formatDateShortWithYYYY()); ui->qslEqslSentDateEdit->setDisplayFormat(locale.formatDateShortWithYYYY()); ui->qslLotwSentDateEdit->setDisplayFormat(locale.formatDateShortWithYYYY()); /* Mapper setting */ mapper->setModel(model); QSOEditMapperDelegate *QSOitemDelegate = new QSOEditMapperDelegate(mapper); mapper->setItemDelegate(QSOitemDelegate); mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit); connect(QSOitemDelegate, &QSOEditMapperDelegate::keyEscapePressed, this, &QSODetailDialog::resetKeyPressed); /* Callbook Signals registration */ connect(&callbookManager, &CallbookManager::callsignResult, this, &QSODetailDialog::callsignFound); connect(&callbookManager, &CallbookManager::callsignNotFound, this, &QSODetailDialog::callsignNotFound); connect(&callbookManager, &CallbookManager::loginFailed, this, &QSODetailDialog::callbookLoginFailed); connect(&callbookManager, &CallbookManager::lookupError, this, &QSODetailDialog::callbookError); connect(MembershipQE::instance(), &MembershipQE::clubStatusResult, this, &QSODetailDialog::clubQueryResult); /*******************/ /* Main Screen GUI */ /*******************/ /* ITU Zones Validators */ ui->ituEdit->setValidator(new QIntValidator(Data::getITUZMin(), Data::getITUZMax(), this)); ui->myITUEdit->setValidator(new QIntValidator(Data::getITUZMin(), Data::getITUZMax(), this)); /* CQ Zones Validators */ ui->cqEdit->setValidator(new QIntValidator(Data::getCQZMin(), Data::getCQZMax(), this)); ui->myCQEdit->setValidator(new QIntValidator(Data::getCQZMin(), Data::getCQZMax(), this)); /* Submode mapping */ modeController.reset(new ModeSelectionController(ui->modeEdit, ui->submodeEdit, true, false, false, false, this)); modeController->applyCurrentMode(); /* IOTA Completer */ iotaCompleter.reset(new QCompleter(Data::instance()->iotaIDList(), this)); iotaCompleter->setCaseSensitivity(Qt::CaseInsensitive); iotaCompleter->setFilterMode(Qt::MatchContains); iotaCompleter->setModelSorting(QCompleter::CaseSensitivelySortedModel); ui->iotaEdit->setCompleter(iotaCompleter.data()); /* SOTA Completer */ sotaCompleter.reset(new QCompleter(Data::instance()->sotaIDList(), this)); sotaCompleter->setCaseSensitivity(Qt::CaseInsensitive); sotaCompleter->setFilterMode(Qt::MatchStartsWith); sotaCompleter->setModelSorting(QCompleter::CaseSensitivelySortedModel); ui->sotaEdit->setCompleter(nullptr); /* POTA Completer */ potaCompleter.reset(new MultiselectCompleter(Data::instance()->potaIDList(), this)); potaCompleter->setCaseSensitivity(Qt::CaseInsensitive); potaCompleter->setFilterMode(Qt::MatchStartsWith); potaCompleter->setModelSorting(QCompleter::CaseSensitivelySortedModel); ui->potaEdit->setCompleter(nullptr); /* WWFF Completer */ wwffCompleter.reset(new QCompleter(Data::instance()->wwffIDList(), this)); wwffCompleter->setCaseSensitivity(Qt::CaseInsensitive); wwffCompleter->setFilterMode(Qt::MatchStartsWith); wwffCompleter->setModelSorting(QCompleter::CaseSensitivelySortedModel); ui->wwffEdit->setCompleter(nullptr); /* MyIOTA Completer */ myIotaCompleter.reset(new QCompleter(Data::instance()->iotaIDList(), this)); myIotaCompleter->setCaseSensitivity(Qt::CaseInsensitive); myIotaCompleter->setFilterMode(Qt::MatchContains); myIotaCompleter->setModelSorting(QCompleter::CaseSensitivelySortedModel); ui->myIOTAEdit->setCompleter(myIotaCompleter.data()); /* MySOTA Completer */ mySotaCompleter.reset(new QCompleter(Data::instance()->sotaIDList(), this)); mySotaCompleter->setCaseSensitivity(Qt::CaseInsensitive); mySotaCompleter->setFilterMode(Qt::MatchStartsWith); mySotaCompleter->setModelSorting(QCompleter::CaseSensitivelySortedModel); ui->mySOTAEdit->setCompleter(nullptr); /* MyPOTA Completer */ myPotaCompleter.reset(new QCompleter(Data::instance()->potaIDList(), this)); myPotaCompleter->setCaseSensitivity(Qt::CaseInsensitive); myPotaCompleter->setFilterMode(Qt::MatchStartsWith); myPotaCompleter->setModelSorting(QCompleter::CaseSensitivelySortedModel); ui->myPOTAEdit->setCompleter(nullptr); /* MyWWFF Completer */ myWWFFCompleter.reset(new QCompleter(Data::instance()->wwffIDList(), this)); myWWFFCompleter->setCaseSensitivity(Qt::CaseInsensitive); myWWFFCompleter->setFilterMode(Qt::MatchStartsWith); myWWFFCompleter->setModelSorting(QCompleter::CaseSensitivelySortedModel); ui->myWWFFEdit->setCompleter(nullptr); /* SIF Completer */ sigCompleter.reset(new QCompleter(Data::instance()->sigIDList(), this)); sigCompleter->setCaseSensitivity(Qt::CaseInsensitive); sigCompleter->setFilterMode(Qt::MatchStartsWith); sigCompleter->setModelSorting(QCompleter::CaseSensitivelySortedModel); ui->sigEdit->setCompleter(sigCompleter.data()); /* Combo Mapping */ /* do no use Data::qslPaperSentStatusBox for it because * Data::qslPaperSentStatusBox has a different ordering. * Ordering below is optimized for a new Contact Widget only */ ui->qslPaperSentStatusBox->addItem(tr("No"), QVariant("N")); ui->qslPaperSentStatusBox->addItem(tr("Yes"), QVariant("Y")); ui->qslPaperSentStatusBox->addItem(tr("Requested"), QVariant("R")); ui->qslPaperSentStatusBox->addItem(tr("Queued"), QVariant("Q")); ui->qslPaperSentStatusBox->addItem(tr("Ignored"), QVariant("I")); /* Combo Mapping */ /* do no use Data::qslLotwSentStatusBox for it because * Data::qslLotwSentStatusBox has a different ordering. * Ordering below is optimized for a new Contact Widget only */ ui->qslLotwSentStatusBox->addItem(tr("No"), QVariant("N")); ui->qslLotwSentStatusBox->addItem(tr("Yes"), QVariant("Y")); ui->qslLotwSentStatusBox->addItem(tr("Requested"), QVariant("R")); ui->qslLotwSentStatusBox->addItem(tr("Queued"), QVariant("Q")); ui->qslLotwSentStatusBox->addItem(tr("Ignored"), QVariant("I")); /* Combo Mapping */ /* do no use Data::qslEqslSentStatusBox for it because * Data::qslEqslSentStatusBox has a different ordering. * Ordering below is optimized for a new Contact Widget only */ ui->qslEqslSentStatusBox->addItem(tr("No"), QVariant("N")); ui->qslEqslSentStatusBox->addItem(tr("Yes"), QVariant("Y")); ui->qslEqslSentStatusBox->addItem(tr("Requested"), QVariant("R")); ui->qslEqslSentStatusBox->addItem(tr("Queued"), QVariant("Q")); ui->qslEqslSentStatusBox->addItem(tr("Ignored"), QVariant("I")); QMapIterator iter(Data::instance()->qslRcvdEnum); while( iter.hasNext() ) { iter.next(); ui->qslPaperReceiveStatusBox->addItem(iter.value(), iter.key()); } /* do no use Data::qslSentViaBox for it because * Data::qslSentViaBox has a different ordering. * Ordering below is optimized for a new Contact Widget only */ ui->qslSentViaBox->addItem("", QVariant("")); ui->qslSentViaBox->addItem(tr("Bureau"), QVariant("B")); ui->qslSentViaBox->addItem(tr("Direct"), QVariant("D")); ui->qslSentViaBox->addItem(tr("Electronic"), QVariant("E")); /* Propagation */ QStringListModel* propagationModeModel = new QStringListModel(Data::instance()->propagationModesList(), this); ui->propagationModeEdit->setModel(propagationModeModel); /* Sat Modes & sat names */ QSqlTableModel* satModel = new QSqlTableModel(ui->satNameEdit); satModel->setTable("sat_info"); QCompleter *satCompleter = new QCompleter(ui->satNameEdit); satCompleter->setModel(satModel); satCompleter->setCompletionColumn(satModel->fieldIndex("name")); satCompleter->setCaseSensitivity(Qt::CaseInsensitive); ui->satNameEdit->setCompleter(satCompleter); satModel->select(); QStringList satModesList = Data::instance()->satModeList(); satModesList.prepend(""); QStringListModel* satModesModel = new QStringListModel(satModesList, this); ui->satModeEdit->setModel(satModesModel); /* Country */ ui->countryBox->setModel(new SqlListModel("SELECT id, translate_to_locale(name), name " "FROM dxcc_entities_clublog " "ORDER BY 2 COLLATE LOCALEAWARE ASC;", " ", ui->countryBox)); ui->countryBox->setModelColumn(1); ui->countryBox->adjustMaxSize(); /* My Country Combo */ ui->myCountryBox->setModel(new SqlListModel("SELECT id, translate_to_locale(name), name " "FROM dxcc_entities_clublog " "ORDER BY 2 COLLATE LOCALEAWARE ASC;", " ", ui->myCountryBox)); ui->myCountryBox->setModelColumn(1); ui->myCountryBox->adjustMaxSize(); /* Band Combos */ SqlListModel* bandModel = new SqlListModel("SELECT name FROM bands ORDER BY start_freq;", tr("Blank"), this); while ( bandModel->canFetchMore() ) { bandModel->fetchMore(); } ui->bandTXCombo->setModel(bandModel); ui->bandRXCombo->setModel(bandModel); /* Assign Validators */ ui->callsignEdit->setValidator(new QRegularExpressionValidator(Callsign::callsignRegEx(), this)); ui->myCallsignEdit->setValidator(new QRegularExpressionValidator(Callsign::callsignRegEx(), this)); ui->gridEdit->setValidator(new QRegularExpressionValidator(Gridsquare::gridRegEx(), this)); ui->myGridEdit->setValidator(new QRegularExpressionValidator(Gridsquare::gridRegEx(), this)); ui->vuccEdit->setValidator(new QRegularExpressionValidator(Gridsquare::gridVUCCRegEx(), this)); ui->myVUCCEdit->setValidator(new QRegularExpressionValidator(Gridsquare::gridVUCCRegEx(), this)); ui->fistsEdit->setValidator(new QIntValidator(0, INT_MAX, ui->fistsEdit)); ui->fistsCCEdit->setValidator(new QIntValidator(0, INT_MAX, ui->fistsCCEdit)); ui->tentenEdit->setValidator(new QIntValidator(0, INT_MAX, ui->tentenEdit)); ui->uksmgEdit->setValidator(new QIntValidator(0, INT_MAX, ui->uksmgEdit)); /***********/ /* Mapping */ /***********/ /* Detail */ mapper->addMapping(ui->dateTimeOnEdit, LogbookModel::COLUMN_TIME_ON); mapper->addMapping(ui->dateTimeOffEdit, LogbookModel::COLUMN_TIME_OFF); mapper->addMapping(ui->callsignEdit, LogbookModel::COLUMN_CALL); mapper->addMapping(ui->rstSentEdit, LogbookModel::COLUMN_RST_SENT); mapper->addMapping(ui->rstRcvdEdit, LogbookModel::COLUMN_RST_RCVD); mapper->addMapping(ui->modeEdit, LogbookModel::COLUMN_MODE, "currentText"); mapper->addMapping(ui->submodeEdit, LogbookModel::COLUMN_SUBMODE, "currentText"); mapper->addMapping(ui->freqRXEdit, LogbookModel::COLUMN_FREQ_RX); mapper->addMapping(ui->freqTXEdit, LogbookModel::COLUMN_FREQUENCY); mapper->addMapping(ui->bandRXCombo, LogbookModel::COLUMN_BAND_RX); mapper->addMapping(ui->bandTXCombo, LogbookModel::COLUMN_BAND); mapper->addMapping(ui->nameEdit, LogbookModel::COLUMN_NAME_INTL); mapper->addMapping(ui->qthEdit, LogbookModel::COLUMN_QTH_INTL); mapper->addMapping(ui->gridEdit, LogbookModel::COLUMN_GRID); mapper->addMapping(ui->commentEdit, LogbookModel::COLUMN_COMMENT_INTL); mapper->addMapping(ui->contEdit, LogbookModel::COLUMN_CONTINENT, "currentText"); mapper->addMapping(ui->ituEdit, LogbookModel::COLUMN_ITUZ); mapper->addMapping(ui->cqEdit, LogbookModel::COLUMN_CQZ); mapper->addMapping(ui->stateEdit, LogbookModel::COLUMN_STATE); mapper->addMapping(ui->countyEdit, LogbookModel::COLUMN_COUNTY); mapper->addMapping(ui->ageEdit, LogbookModel::COLUMN_AGE); mapper->addMapping(ui->iotaEdit, LogbookModel::COLUMN_IOTA); mapper->addMapping(ui->sotaEdit, LogbookModel::COLUMN_SOTA_REF); mapper->addMapping(ui->potaEdit, LogbookModel::COLUMN_POTA_REF); mapper->addMapping(ui->sigEdit, LogbookModel::COLUMN_SIG_INTL); mapper->addMapping(ui->sigInfoEdit, LogbookModel::COLUMN_SIG_INFO_INTL); mapper->addMapping(ui->dokEdit, LogbookModel::COLUMN_DARC_DOK); mapper->addMapping(ui->vuccEdit, LogbookModel::COLUMN_VUCC_GRIDS); mapper->addMapping(ui->wwffEdit, LogbookModel::COLUMN_WWFF_REF); mapper->addMapping(ui->countryBox, LogbookModel::COLUMN_DXCC); mapper->addMapping(ui->emailEdit, LogbookModel::COLUMN_EMAIL); mapper->addMapping(ui->urlEdit, LogbookModel::COLUMN_WEB); mapper->addMapping(ui->propagationModeEdit, LogbookModel::COLUMN_PROP_MODE); mapper->addMapping(ui->satNameEdit, LogbookModel::COLUMN_SAT_NAME); mapper->addMapping(ui->satModeEdit,LogbookModel::COLUMN_SAT_MODE); mapper->addMapping(ui->fistsEdit,LogbookModel::COLUMN_FISTS); mapper->addMapping(ui->fistsCCEdit,LogbookModel::COLUMN_FISTS_CC); mapper->addMapping(ui->skccEdit,LogbookModel::COLUMN_SKCC); mapper->addMapping(ui->tentenEdit,LogbookModel::COLUMN_TEN_TEN); mapper->addMapping(ui->uksmgEdit,LogbookModel::COLUMN_UKSMG); /* My Station */ mapper->addMapping(ui->myCallsignEdit, LogbookModel::COLUMN_STATION_CALLSIGN); mapper->addMapping(ui->myOperatorNameEdit, LogbookModel::COLUMN_MY_NAME_INTL); mapper->addMapping(ui->myCountryBox, LogbookModel::COLUMN_MY_DXCC); mapper->addMapping(ui->myITUEdit, LogbookModel::COLUMN_MY_ITU_ZONE); mapper->addMapping(ui->myCQEdit, LogbookModel::COLUMN_MY_CQ_ZONE); mapper->addMapping(ui->myQTHEdit, LogbookModel::COLUMN_MY_CITY_INTL); mapper->addMapping(ui->myGridEdit, LogbookModel::COLUMN_MY_GRIDSQUARE); mapper->addMapping(ui->mySOTAEdit, LogbookModel::COLUMN_MY_SOTA_REF); mapper->addMapping(ui->myPOTAEdit, LogbookModel::COLUMN_MY_POTA_REF); mapper->addMapping(ui->myIOTAEdit, LogbookModel::COLUMN_MY_IOTA); mapper->addMapping(ui->mySIGEdit, LogbookModel::COLUMN_MY_SIG); mapper->addMapping(ui->mySIGInfoEdit, LogbookModel::COLUMN_MY_SIG_INFO_INTL); mapper->addMapping(ui->myRigEdit, LogbookModel::COLUMN_MY_RIG_INTL); mapper->addMapping(ui->myAntEdit, LogbookModel::COLUMN_MY_ANTENNA_INTL); mapper->addMapping(ui->myVUCCEdit, LogbookModel::COLUMN_MY_VUCC_GRIDS); mapper->addMapping(ui->myWWFFEdit, LogbookModel::COLUMN_MY_WWFF_REF); mapper->addMapping(ui->powerEdit, LogbookModel::COLUMN_TX_POWER); mapper->addMapping(ui->myCountyEdit, LogbookModel::COLUMN_MY_CNTY); mapper->addMapping(ui->myOperatorCallsignEdit, LogbookModel::COLUMN_OPERATOR); mapper->addMapping(ui->myDOKEdit, LogbookModel::COLUMN_MY_DARC_DOK); /* Notes */ mapper->addMapping(ui->noteEdit, LogbookModel::COLUMN_NOTES_INTL); /* QSL */ mapper->addMapping(ui->qslPaperSentStatusBox, LogbookModel::COLUMN_QSL_SENT); mapper->addMapping(ui->qslPaperReceiveStatusBox, LogbookModel::COLUMN_QSL_RCVD); mapper->addMapping(ui->qslEqslReceiveDateLabel, LogbookModel::COLUMN_EQSL_QSLRDATE); mapper->addMapping(ui->qslEqslSentDateEdit, LogbookModel::COLUMN_EQSL_QSLSDATE); mapper->addMapping(ui->qslLotwReceiveDateLabel, LogbookModel::COLUMN_LOTW_RCVD_DATE); mapper->addMapping(ui->qslLotwSentDateEdit, LogbookModel::COLUMN_LOTW_SENT_DATE); mapper->addMapping(ui->qslEqslReceiveStatusLabel, LogbookModel::COLUMN_EQSL_QSL_RCVD); mapper->addMapping(ui->qslEqslSentStatusBox, LogbookModel::COLUMN_EQSL_QSL_SENT); mapper->addMapping(ui->qslLotwReceiveStatusLabel, LogbookModel::COLUMN_LOTW_RCVD); mapper->addMapping(ui->qslLotwSentStatusBox, LogbookModel::COLUMN_LOTW_SENT); mapper->addMapping(ui->qslReceivedMsgEdit, LogbookModel::COLUMN_QSLMSG_RCVD, "text"); mapper->addMapping(ui->qslSentMsgEdit, LogbookModel::COLUMN_QSLMSG_INTL, "text"); mapper->addMapping(ui->qslSentViaBox, LogbookModel::COLUMN_QSL_SENT_VIA); mapper->addMapping(ui->qslViaEdit, LogbookModel::COLUMN_QSL_VIA); mapper->addMapping(ui->qslPaperReceiveDateEdit, LogbookModel::COLUMN_QSL_RCVD_DATE); mapper->addMapping(ui->qslPaperSentDateEdit, LogbookModel::COLUMN_QSL_SENT_DATE); /* Contest */ mapper->addMapping(ui->contestIDEdit, LogbookModel::COLUMN_CONTEST_ID); mapper->addMapping(ui->srxEdit, LogbookModel::COLUMN_SRX); mapper->addMapping(ui->srxStringEdit, LogbookModel::COLUMN_SRX_STRING); mapper->addMapping(ui->stxEdit, LogbookModel::COLUMN_STX); mapper->addMapping(ui->stxStringEdit, LogbookModel::COLUMN_STX_STRING); /**************/ /* Get Record */ /**************/ //only one record is selected therefore calling toFirst is OK blockMappedWidgetSignals(true); mapper->toFirst(); blockMappedWidgetSignals(false); /* County completer — initialize for the loaded record's DXCC */ { const int dxcc = ui->countryBox->currentValue(1).toInt(); updateCountyCompleter(dxcc); } connect(ui->countryBox, &SmartSearchBox::currentTextChanged, this, [this](const QString &) { updateCountyCompleter(ui->countryBox->currentValue(1).toInt()); }); setReadOnlyMode(true); drawDXOnMap(ui->callsignEdit->text(), Gridsquare(ui->gridEdit->text())); drawMyQTHOnMap(ui->myCallsignEdit->text(), Gridsquare(ui->myGridEdit->text())); setStaticMapTime(ui->dateTimeOnEdit->dateTime()); refreshDXStatTabs(); queryMemberList(); enableWidgetChangeHandlers(); } void QSODetailDialog::accept() { FCT_IDENTIFICATION; if (editButton->text() == getButtonText(SAVE_BUTTON_TEXT) ) { QMessageBox::StandardButton reply; reply = QMessageBox::question(this, tr("Submit changes"), tr("Really submit all changes?"), QMessageBox::Yes|QMessageBox::No); if (reply == QMessageBox::Yes) { QSODetailDialog::SubmitError error = submitAllChanges(); if ( error == QSODetailDialog::SubmitMapperError || error == QSODetailDialog::SubmitModelError ) { QMessageBox::critical(this, tr("QLog Error"), tr("Cannot save all changes - internal error")); } } } callbookManager.abortQuery(); done(QDialog::Accepted); } void QSODetailDialog::keyPressEvent(QKeyEvent *evt) { FCT_IDENTIFICATION; /* suppress Enter press because it automatically save all changes */ if( evt->key() == Qt::Key_Enter || evt->key() == Qt::Key_Return ) { return; } QDialog::keyPressEvent(evt); } QSODetailDialog::~QSODetailDialog() { FCT_IDENTIFICATION; delete editedRecord; delete ui; } void QSODetailDialog::editButtonPressed() { FCT_IDENTIFICATION; if ( editButton->text() == getButtonText(SAVE_BUTTON_TEXT) ) { QSODetailDialog::SubmitError error = submitAllChanges(); if ( error == QSODetailDialog::SubmitCancelledByUser ) { /* an user wants to fix invalid fields, edit mode remains active */ return; } else if ( error == QSODetailDialog::SubmitMapperError || error == QSODetailDialog::SubmitModelError ) { QMessageBox::critical(this, tr("QLog Error"), tr("Cannot save all changes - try to reset all changes")); /* edit mode remains active to fix a possible problem */ return; } setReadOnlyMode(true); } else { setReadOnlyMode(false); /* Re-set completer — Qt loses the completer connection after setReadOnly(true/false) cycle */ updateCountyCompleter(ui->countryBox->currentValue(1).toInt()); timeLockDiff = ui->dateTimeOnEdit->dateTime().msecsTo(ui->dateTimeOffEdit->dateTime()); freqLockDiff = ui->freqTXEdit->value() - ui->freqRXEdit->value(); } } void QSODetailDialog::resetButtonPressed() { FCT_IDENTIFICATION; blockMappedWidgetSignals(true); mapper->revert(); blockMappedWidgetSignals(false); queryMemberList(); setReadOnlyMode(true); doValidation(); } void QSODetailDialog::lookupButtonPressed() { FCT_IDENTIFICATION; callbookLookupStart(); callbookManager.queryCallsign(ui->callsignEdit->text()); } /* called when qdatawidgetmapper reverts a widget value by pressing ESC */ void QSODetailDialog::resetKeyPressed(QObject *inObject) { FCT_IDENTIFICATION; QWidget *widget = qobject_cast(inObject); if ( widget ) { // datawidgetmapper resets a value when ESC is pressed // therefore it is needed to reset stylesheet when resetKey (ESC) is pressed widget->setStyleSheet(""); } } void QSODetailDialog::setReadOnlyMode(bool inReadOnly) { FCT_IDENTIFICATION; qCDebug(function_parameters) << inReadOnly; for ( int i = 0; i < LogbookModel::COLUMN_LAST_ELEMENT; i++) { QWidget *widget = mapper->mappedWidgetAt(i); if ( !widget ) continue; if ( QLineEdit *line = qobject_cast(widget) ) { line->setReadOnly(inReadOnly); if ( inReadOnly ) { line->setStyleSheet(""); } } else if ( QTextEdit *edit = qobject_cast(widget) ) { edit->setReadOnly(inReadOnly); if ( inReadOnly ) { edit->setStyleSheet(""); } } else if ( QDoubleSpinBox *spin = qobject_cast(widget) ) { spin->setReadOnly(inReadOnly); if ( inReadOnly ) { spin->setStyleSheet(""); } } else if ( QDateTimeEdit *datetime = qobject_cast(widget) ) { datetime->setReadOnly(inReadOnly); if ( inReadOnly ) { datetime->setStyleSheet(""); } } else if ( qobject_cast(widget) ) { //do nothing - naturally readonly } else if ( QComboBox *combo = qobject_cast(widget) ) { combo->setEnabled(!inReadOnly); if ( inReadOnly ) { combo->setStyleSheet(""); } } else if ( widget ) { widget->setEnabled(!inReadOnly); if ( inReadOnly ) { widget->setStyleSheet(""); } } } if ( ui->propagationModeEdit->currentText() != Data::instance()->propagationModeIDToText("SAT") ) { /* Do not enable sat fields when SAT prop is not selected */ ui->satModeEdit->setCurrentIndex(-1); ui->satNameEdit->clear(); ui->satModeEdit->setEnabled(false); ui->satNameEdit->setEnabled(false); } else { ui->satNameEdit->setEnabled(true); } editButton->setText((( inReadOnly) ? getButtonText(EDIT_BUTTON_TEXT) : getButtonText(SAVE_BUTTON_TEXT) )); ui->timeLockButton->setEnabled(!inReadOnly); ui->freqLockButton->setEnabled(!inReadOnly); resetButton->setEnabled(!inReadOnly); lookupButton->setEnabled(!inReadOnly && callbookManager.isActive()); if ( ui->qslEqslReceiveStatusLabel->property("originvalue").toString() != "Y" ) { ui->qslEqslPicButton->setEnabled(false); } setWindowTitle( (inReadOnly)? tr("QSO Detail") : tr("Edit QSO")); } void QSODetailDialog::modeChanged(QString) { FCT_IDENTIFICATION; if ( modeController.isNull() ) return; modeController->applyCurrentMode(); queryMemberList(); } void QSODetailDialog::showPaperButton() { FCT_IDENTIFICATION; PaperQSLDialog dialog(*editedRecord); dialog.exec(); } void QSODetailDialog::showEQSLButton() { FCT_IDENTIFICATION; QProgressDialog* dialog = new QProgressDialog(tr("Downloading eQSL Image"), tr("Cancel"), 0, 0, this); dialog->setWindowModality(Qt::WindowModal); dialog->setRange(0, 0); dialog->setAutoClose(true); dialog->show(); EQSLQSLDownloader *eQSL = new EQSLQSLDownloader(dialog); connect(eQSL, &EQSLQSLDownloader::QSLImageFound, this, [dialog, eQSL](QString imgFile) { dialog->done(0); QDesktopServices::openUrl(QUrl::fromLocalFile(imgFile)); eQSL->deleteLater(); }); connect(eQSL, &EQSLQSLDownloader::QSLImageError, this, [this, dialog, eQSL](const QString &error) { dialog->done(1); QMessageBox::critical(this, tr("QLog Error"), tr("eQSL Download Image failed: ") + error); eQSL->deleteLater(); }); connect(dialog, &QProgressDialog::canceled, this, [eQSL]() { qCDebug(runtime)<< "Operation canceled"; eQSL->abortDownload(); eQSL->deleteLater(); }); eQSL->getQSLImage(*editedRecord); } void QSODetailDialog::dateTimeOnChanged(const QDateTime &timeOn) { FCT_IDENTIFICATION; ui->dateTimeOffEdit->blockSignals(true); if ( ui->timeLockButton->isChecked() ) { ui->dateTimeOffEdit->setDateTime(timeOn.addMSecs(timeLockDiff)); ui->dateTimeOffEdit->setStyleSheet(CHANGECSS); //change handles are off, mark field as "changed" manually } else if ( ui->dateTimeOffEdit->dateTime() < timeOn ) { ui->dateTimeOffEdit->setDateTime(timeOn); ui->dateTimeOffEdit->setStyleSheet(CHANGECSS); //change handles are off, mark field as "changed" manually } ui->dateTimeOffEdit->blockSignals(false); } void QSODetailDialog::dateTimeOffChanged(const QDateTime &timeOff) { FCT_IDENTIFICATION; ui->dateTimeOnEdit->blockSignals(true); if ( ui->timeLockButton->isChecked() ) { ui->dateTimeOnEdit->setDateTime(timeOff.addMSecs(-timeLockDiff)); ui->dateTimeOnEdit->setStyleSheet(CHANGECSS); //change handles are off, mark field as "changed" manually } else if ( ui->dateTimeOnEdit->dateTime() > timeOff ) { ui->dateTimeOnEdit->setDateTime(timeOff); ui->dateTimeOnEdit->setStyleSheet(CHANGECSS); //change handles are off, mark field as "changed" manually } ui->dateTimeOnEdit->blockSignals(false); } void QSODetailDialog::freqTXChanged(double) { FCT_IDENTIFICATION; ui->freqRXEdit->blockSignals(true); if ( ui->freqLockButton->isChecked() && ui->freqRXEdit->value() != 0.0 ) { double shiftedRX = ui->freqTXEdit->value() - freqLockDiff; ui->freqRXEdit->setValue((shiftedRX < 0.0) ? 0.0 : shiftedRX); ui->freqRXEdit->setStyleSheet(CHANGECSS); //change handles are off, mark field as "changed" manually } ui->freqRXEdit->blockSignals(false); // TODO: qlog should call queryMemberList but for saving time we will omit it. // queryMemberList(); } void QSODetailDialog::freqRXChanged(double) { FCT_IDENTIFICATION; ui->freqTXEdit->blockSignals(true); if ( ui->freqLockButton->isChecked() ) { double shiftedTX = ui->freqRXEdit->value() + freqLockDiff; ui->freqTXEdit->setValue((shiftedTX < 0.0) ? 0.0 : shiftedTX); ui->freqTXEdit->setStyleSheet(CHANGECSS); //change handles are off, mark field as "changed" manually } ui->freqTXEdit->blockSignals(false); } void QSODetailDialog::timeLockToggled(bool toggled) { FCT_IDENTIFICATION; qCDebug(function_parameters) << toggled; if ( toggled ) { if ( ui->dateTimeOffEdit->dateTime() < ui->dateTimeOnEdit->dateTime() ) { ui->dateTimeOffEdit->blockSignals(true); ui->dateTimeOffEdit->setDateTime(ui->dateTimeOnEdit->dateTime()); ui->dateTimeOffEdit->blockSignals(false); timeLockDiff = 0; } else { timeLockDiff = ui->dateTimeOnEdit->dateTime().msecsTo(ui->dateTimeOffEdit->dateTime()); } } } void QSODetailDialog::freqLockToggled(bool toggled) { FCT_IDENTIFICATION; qCDebug(function_parameters) << toggled; if ( toggled ) { freqLockDiff = ui->freqTXEdit->value() - ui->freqRXEdit->value(); } } void QSODetailDialog::callsignChanged(const QString &) { FCT_IDENTIFICATION; refreshDXStatTabs(); } void QSODetailDialog::callsignEditFinished() { FCT_IDENTIFICATION; queryMemberList(); } void QSODetailDialog::queryMemberList() { FCT_IDENTIFICATION; if ( ui->callsignEdit->text().size() >= 3 ) { MembershipQE::instance()->asyncQueryDetails(ui->callsignEdit->text(), BandPlan::freq2Band(ui->freqTXEdit->value()).name, ui->modeEdit->currentText()); } } void QSODetailDialog::propagationModeChanged(const QString &propModeText) { FCT_IDENTIFICATION; qCDebug(function_parameters) << propModeText; if ( propModeText == Data::instance()->propagationModeIDToText("SAT") ) { ui->satModeEdit->setEnabled(true); ui->satNameEdit->setEnabled(true); } else { ui->satModeEdit->setCurrentIndex(-1); ui->satNameEdit->clear(); ui->satModeEdit->setEnabled(false); ui->satNameEdit->setEnabled(false); } } bool QSODetailDialog::doValidation() { FCT_IDENTIFICATION; bool allValid = true; const QList &list = findChildren(); for ( QLabel *label : list ) { if ( label ) label->setToolTip(QString()); } allValid &= highlightInvalid(ui->callsignLabel, ui->callsignEdit->text().isEmpty(), tr("DX Callsign must not be empty")); allValid &= highlightInvalid(ui->callsignLabel, !ui->callsignEdit->text().isEmpty() && !ui->callsignEdit->hasAcceptableInput(), tr("DX callsign has an incorrect format")); allValid &= highlightInvalid(ui->freqTXLabel, ui->freqTXEdit->value() == 0.0 && ui->bandTXCombo->currentIndex() == 0, tr("TX Frequency or Band must be filled")); const Band &bandTX = BandPlan::freq2Band(ui->freqTXEdit->value()); const QString &bandTXString = bandTX.name; allValid &= highlightInvalid(ui->bandLabel, ui->freqTXEdit->value() != 0.0 && ui->bandTXCombo->currentText() != bandTXString, tr("TX Band should be ") + "" + (bandTXString.isEmpty() ? "OOB" : bandTXString) + ""); allValid &= highlightInvalid(ui->bandLabel, ui->freqTXEdit->value() == 0.0 && ui->bandTXCombo->currentIndex() == 0, tr("TX Frequency or Band must be filled")); const Band &bandRX = BandPlan::freq2Band(ui->freqRXEdit->value()); const QString &bandRXString = bandRX.name; allValid &= highlightInvalid(ui->bandLabel, ui->freqRXEdit->value() != 0.0 && ui->bandRXCombo->currentText() != bandRXString, tr("RX Band should be ") + "" + (bandRXString.isEmpty() ? "OOB" : bandRXString) + ""); allValid &= highlightInvalid(ui->gridLabel, !ui->gridEdit->text().isEmpty() && !ui->gridEdit->hasAcceptableInput(), tr("DX Grid has an incorrect format")); const DxccEntity &dxccEntity = Data::instance()->lookupDxccClublog(ui->callsignEdit->text(), ui->dateTimeOnEdit->dateTime()); allValid &= highlightInvalid(ui->countryLabel, dxccEntity.dxcc && ui->countryBox->currentText() != QCoreApplication::translate("DBStrings", dxccEntity.country.toUtf8().constData()), tr("Based on callsign, DXCC Country is different from the entered value - expecting ") + " " + QCoreApplication::translate("DBStrings", dxccEntity.country.toUtf8().constData()) + ""); allValid &= highlightInvalid(ui->contLabel, dxccEntity.dxcc && ui->contEdit->currentText() != dxccEntity.cont, tr("Based on callsign, DXCC Continent is different from the entered value - expecting ") + " " + dxccEntity.cont + ""); allValid &= highlightInvalid(ui->ituLabel, dxccEntity.dxcc && ui->ituEdit->text() != QString::number(dxccEntity.ituz), tr("Based on callsign, DXCC ITU is different from the entered value - expecting ") + " " + QString::number(dxccEntity.ituz) + ""); allValid &= highlightInvalid(ui->cqLabel, dxccEntity.dxcc && ui->cqEdit->text() != QString::number(dxccEntity.cqz), tr("Based on callsign, DXCC CQZ is different from the entered value - expecting ") + " " + QString::number(dxccEntity.cqz) + ""); allValid &= highlightInvalid(ui->vuccLabel, !ui->vuccEdit->text().isEmpty() && !ui->vuccEdit->hasAcceptableInput(), tr("VUCC has an incorrect format")); const QString &expectedSatMode = (bandTX.satDesignator.isEmpty() || bandRX.satDesignator.isEmpty() ) ? "" : bandTX.satDesignator + bandRX.satDesignator; allValid &= highlightInvalid(ui->satModeLabel, ui->satModeEdit->currentIndex() > 0 && Data::instance()->satModeTextToID(ui->satModeEdit->currentText()) != expectedSatMode, tr("Based on Frequencies, Sat Mode should be ") + "" + ( (expectedSatMode.isEmpty()) ? tr("blank") : expectedSatMode) + ""); allValid &= highlightInvalid(ui->satNameLabel, Data::instance()->propagationModeTextToID(ui->propagationModeEdit->currentText()) == "SAT" && ui->satNameEdit->text().isEmpty(), tr("Sat name must not be empty")); const DxccEntity &myDxccEntity = Data::instance()->lookupDxccClublog(ui->myCallsignEdit->text(), ui->dateTimeOnEdit->dateTime()); allValid &= highlightInvalid(ui->myCallsignLabel, ui->myCallsignEdit->text().isEmpty(), tr("Own Callsign must not be empty")); allValid &= highlightInvalid(ui->myCallsignLabel, !ui->myCallsignEdit->text().isEmpty() && !ui->myCallsignEdit->hasAcceptableInput(), tr("Own callsign has an incorrect format")); allValid &= highlightInvalid(ui->myGridLabel, !ui->myGridEdit->text().isEmpty() && !ui->myGridEdit->hasAcceptableInput(), tr("DX Grid has an incorrect format")); allValid &= highlightInvalid(ui->myVUCCLabel, !ui->myVUCCEdit->text().isEmpty() && !ui->myVUCCEdit->hasAcceptableInput(), tr("Own VUCC Grids have an incorrect format")); allValid &= highlightInvalid(ui->myITULabel, myDxccEntity.dxcc && ui->myITUEdit->text() != QString::number(myDxccEntity.ituz), tr("Based on own callsign, own DXCC ITU is different from the entered value - expecting ") + " " + QString::number(myDxccEntity.ituz) + ""); allValid &= highlightInvalid(ui->myCQLabel, myDxccEntity.dxcc && ui->myCQEdit->text() != QString::number(myDxccEntity.cqz), tr("Based on own callsign, own DXCC CQZ is different from the entered value - expecting ") + " " + QString::number(myDxccEntity.cqz) + ""); allValid &= highlightInvalid(ui->myCountryLabel, myDxccEntity.dxcc && ui->myCountryBox->currentText() != QCoreApplication::translate("DBStrings", myDxccEntity.country.toUtf8().constData()), tr("Based on own callsign, own DXCC Country is different from the entered value - expecting ") + " " + QCoreApplication::translate("DBStrings", myDxccEntity.country.toUtf8().constData()) + ""); SOTAEntity sotaInfo; POTAEntity potaInfo; if ( !ui->sotaEdit->text().isEmpty() ) { sotaInfo = Data::instance()->lookupSOTA(ui->sotaEdit->text()); } if ( !ui->potaEdit->text().isEmpty() ) { potaInfo = Data::instance()->lookupPOTA(ui->potaEdit->text()); } allValid &= highlightInvalid(ui->qthLabel, sotaInfo.summitCode.toUpper() == ui->sotaEdit->text().toUpper() && !sotaInfo.summitName.isEmpty() && ui->qthEdit->text().toUpper() != sotaInfo.summitName.toUpper(), tr("Based on SOTA Summit, QTH does not match SOTA Summit Name - expecting ")+ " " + sotaInfo.summitName + ""); Gridsquare SOTAGrid(sotaInfo.latitude, sotaInfo.longitude); allValid &= highlightInvalid(ui->gridLabel, sotaInfo.summitCode.toUpper() == ui->sotaEdit->text().toUpper() && !sotaInfo.summitName.isEmpty() && SOTAGrid.isValid() && ui->gridEdit->text().toUpper() != SOTAGrid.getGrid().toUpper(), tr("Based on SOTA Summit, Grid does not match SOTA Grid - expecting ")+ " " + SOTAGrid.getGrid() + ""); allValid &= highlightInvalid(ui->qthLabel, potaInfo.reference.toUpper() == ui->potaEdit->text().toUpper() && !potaInfo.name.isEmpty() && ui->qthEdit->text().toUpper() != potaInfo.name.toUpper(), tr("Based on POTA record, QTH does not match POTA Name - expecting ")+ " " + potaInfo.name + ""); Gridsquare POTAGrid(potaInfo.grid); allValid &= highlightInvalid(ui->gridLabel, potaInfo.reference.toUpper() == ui->potaEdit->text().toUpper() && !potaInfo.name.isEmpty() && POTAGrid.isValid() && ui->gridEdit->text().toUpper() != POTAGrid.getGrid().toUpper(), tr("Based on POTA record, Grid does not match POTA Grid - expecting ")+ " " + POTAGrid.getGrid() + ""); SOTAEntity mySotaInfo; POTAEntity myPotaInfo; if ( !ui->mySOTAEdit->text().isEmpty() ) { mySotaInfo = Data::instance()->lookupSOTA(ui->mySOTAEdit->text()); } if ( !ui->myPOTAEdit->text().isEmpty() ) { myPotaInfo = Data::instance()->lookupPOTA(ui->myPOTAEdit->text()); } allValid &= highlightInvalid(ui->myQTHLabel, mySotaInfo.summitCode.toUpper() == ui->mySOTAEdit->text().toUpper() && !mySotaInfo.summitName.isEmpty() && ui->myQTHEdit->text().toUpper() != mySotaInfo.summitName.toUpper(), tr("Based on SOTA Summit, my QTH does not match SOTA Summit Name - expecting ")+ " " + mySotaInfo.summitName + ""); Gridsquare MySOTAGrid(mySotaInfo.latitude, mySotaInfo.longitude); allValid &= highlightInvalid(ui->myGridLabel, mySotaInfo.summitCode.toUpper() == ui->mySOTAEdit->text().toUpper() && !mySotaInfo.summitName.isEmpty() && MySOTAGrid.isValid() && ui->myGridEdit->text().toUpper() != MySOTAGrid.getGrid().toUpper(), tr("Based on SOTA Summit, my Grid does not match SOTA Grid - expecting ")+ " " + MySOTAGrid.getGrid() + ""); allValid &= highlightInvalid(ui->myQTHLabel, myPotaInfo.reference.toUpper() == ui->myPOTAEdit->text().toUpper() && !myPotaInfo.name.isEmpty() && ui->myQTHEdit->text().toUpper() != myPotaInfo.name.toUpper(), tr("Based on POTA record, my QTH does not match POTA Name - expecting ")+ " " + myPotaInfo.name + ""); Gridsquare myPOTAGrid(myPotaInfo.grid); allValid &= highlightInvalid(ui->myGridLabel, myPotaInfo.reference.toUpper() == ui->myPOTAEdit->text().toUpper() && !myPotaInfo.name.isEmpty() && myPOTAGrid.isValid() && ui->myGridEdit->text().toUpper() != myPOTAGrid.getGrid().toUpper(), tr("Based on POTA record, my Grid does not match POTA Grid - expecting ")+ " " + myPOTAGrid.getGrid() + ""); allValid &= highlightInvalid(ui->lotwHeaderLabel, ui->qslLotwSentDateEdit->date() != ui->qslLotwSentDateEdit->minimumDate() && ui->qslLotwSentStatusBox->currentData().toString() == "N", tr("LoTW Sent Status to No does not make any sense if QSL Sent Date is set. Set Date to 1.1.1900 to leave the date field blank")); allValid &= highlightInvalid(ui->lotwHeaderLabel, ui->qslLotwSentDateEdit->date() == ui->qslLotwSentDateEdit->minimumDate() && ( ui->qslLotwSentStatusBox->currentData().toString() == "Y" // || ui->qslLotwSentStatusBox->currentData().toString() == "Q" // QLog does not set date for Q state // || ui->qslLotwSentStatusBox->currentData().toString() == "I" // QLog does not set date for I state ), tr("Date should be present for LoTW Sent Status Yes")); allValid &= highlightInvalid(ui->eqslHeaderLabel, ui->qslEqslSentDateEdit->date() != ui->qslEqslSentDateEdit->minimumDate() && ui->qslEqslSentStatusBox->currentData().toString() == "N", tr("eQSL Sent Status to No does not make any sense if QSL Sent Date is set. Set Date to 1.1.1900 to leave the date field blank")); allValid &= highlightInvalid(ui->eqslHeaderLabel, ui->qslEqslSentDateEdit->date() == ui->qslEqslSentDateEdit->minimumDate() && ( ui->qslEqslSentStatusBox->currentData().toString() == "Y" // || ui->qslEqslSentStatusBox->currentData().toString() == "Q" // QLog does not set date for Q state // || ui->qslEqslSentStatusBox->currentData().toString() == "I" // QLog does not set date for I state ), tr("Date should be present for eQSL Sent Status Yes")); allValid &= highlightInvalid(ui->qslSentLabel, ui->qslPaperSentDateEdit->date() != ui->qslPaperSentDateEdit->minimumDate() && ui->qslPaperSentStatusBox->currentData().toString() == "N", tr("Paper Sent Status to No does not make any sense if QSL Sent Date is set. Set Date to 1.1.1900 to leave the date field blank")); allValid &= highlightInvalid(ui->qslSentLabel, ui->qslPaperSentDateEdit->date() == ui->qslPaperSentDateEdit->minimumDate() && ( ui->qslPaperSentStatusBox->currentData().toString() == "Y" // || ui->qslPaperSentStatusBox->currentData().toString() == "Q" // QLog does not set date for Q state // || ui->qslPaperSentStatusBox->currentData().toString() == "I" // QLog does not set date for I state ), tr("Date should be present for Paper Sent Status Yes")); qCDebug(runtime) << "Validation result: " << allValid; return allValid; } void QSODetailDialog::doValidationDateTime(const QDateTime &) { doValidation(); } void QSODetailDialog::doValidationDouble(double) { doValidation(); } void QSODetailDialog::mapLoaded(bool) { FCT_IDENTIFICATION; isMainPageLoaded = true; /* which layers will be active */ postponedScripts += layerControlHandler.generateMapMenuJS(true, true, false, false, false, false, false, false, true); main_page->runJavaScript(postponedScripts); const QPalette &defaultPalette = this->palette(); const QColor &text = defaultPalette.color(QPalette::WindowText); const QColor &window = defaultPalette.color(QPalette::Window); bool isDark = text.lightness() > window.lightness(); if ( isDark ) { QString themeJavaScript = "map.getPanes().tilePane.style.webkitFilter=\"brightness(0.6) invert(1) contrast(3) hue-rotate(200deg) saturate(0.3) brightness(0.9)\";"; main_page->runJavaScript(themeJavaScript); } layerControlHandler.restoreLayerControlStates(main_page); } void QSODetailDialog::myGridChanged(const QString &newGrid) { FCT_IDENTIFICATION; drawMyQTHOnMap(ui->myCallsignEdit->text(), Gridsquare(newGrid)); return; } void QSODetailDialog::DXGridChanged(const QString &newGrid) { FCT_IDENTIFICATION; drawDXOnMap(ui->callsignEdit->text(), Gridsquare(newGrid)); return; } void QSODetailDialog::callsignFound(const CallbookResponseData &data) { FCT_IDENTIFICATION; callbookLookupFinished(); /* blank or not fully filled then update it */ const QString fnamelname = QString("%1 %2").arg(data.fname, data.lname); if ( ui->nameEdit->text().isEmpty() || data.name_fmt.contains(ui->nameEdit->text()) || fnamelname.contains(ui->nameEdit->text()) || data.nick.contains(ui->nameEdit->text()) ) { QString name = data.name_fmt; if ( name.isEmpty() ) name = ( data.fname.isEmpty() && data.lname.isEmpty() ) ? data.nick : fnamelname; ui->nameEdit->setText(name); } if ( ui->gridEdit->text().isEmpty() || data.gridsquare.contains(ui->gridEdit->text()) ) ui->gridEdit->setText(data.gridsquare); if ( ui->qthEdit->text().isEmpty() || data.qth.contains(ui->qthEdit->text())) ui->qthEdit->setText(data.qth); if ( ui->dokEdit->text().isEmpty() ) ui->dokEdit->setText(data.dok); if ( ui->iotaEdit->text().isEmpty() ) ui->iotaEdit->setText(data.iota); if ( ui->emailEdit->text().isEmpty() ) ui->emailEdit->setText(data.email); if ( ui->countyEdit->text().isEmpty() ) ui->countyEdit->setText(data.county); if ( ui->qslViaEdit->text().isEmpty() ) ui->qslViaEdit->setText(data.qsl_via); if ( ui->urlEdit->text().isEmpty() ) ui->urlEdit->setText(data.url); if ( ui->stateEdit->text().isEmpty() ) ui->stateEdit->setText(data.us_state); if ( ui->ituEdit->text().isEmpty() ) ui->ituEdit->setText(data.ituz); if ( ui->cqEdit->text().isEmpty() ) ui->cqEdit->setText(data.cqz); } void QSODetailDialog::callsignNotFound(const QString &) { FCT_IDENTIFICATION; /* Do not show any info, not needed */ callbookLookupFinished(); } void QSODetailDialog::callbookLoginFailed(const QString&) { FCT_IDENTIFICATION; /* It is not needed to show an Error dialog because Login failed emits also callbookError signal */ /* QLog will show only callbookError */ //QMessageBox::critical(this, tr("QLog Error"), callbookString + " " + tr("Callbook login failed")); } void QSODetailDialog::callbookError(const QString &error) { FCT_IDENTIFICATION; QMessageBox::critical(this, tr("QLog Error"), tr("Callbook error: ") + error); } void QSODetailDialog::handleBeforeUpdate(int, QSqlRecord &record) { FCT_IDENTIFICATION; emit contactUpdated(record); } void QSODetailDialog::sotaChanged(const QString &newSOTA) { FCT_IDENTIFICATION; if ( newSOTA.length() >= 3 ) { ui->sotaEdit->setCompleter(sotaCompleter.data()); } else { ui->sotaEdit->setCompleter(nullptr); } } void QSODetailDialog::potaChanged(const QString &newPOTA) { FCT_IDENTIFICATION; if ( newPOTA.length() >= 3 ) { ui->potaEdit->setCompleter(potaCompleter.data()); } else { ui->potaEdit->setCompleter(nullptr); } } void QSODetailDialog::wwffChanged(const QString &newWWFF) { FCT_IDENTIFICATION; if ( newWWFF.length() >= 3 ) { ui->wwffEdit->setCompleter(wwffCompleter.data()); } else { ui->wwffEdit->setCompleter(nullptr); } } void QSODetailDialog::mySotaChanged(const QString &newSOTA) { FCT_IDENTIFICATION; if ( newSOTA.length() >= 3 ) { ui->mySOTAEdit->setCompleter(sotaCompleter.data()); } else { ui->mySOTAEdit->setCompleter(nullptr); } } void QSODetailDialog::myPOTAChanged(const QString &newPOTA) { FCT_IDENTIFICATION; if ( newPOTA.length() >= 3 ) { ui->myPOTAEdit->setCompleter(potaCompleter.data()); } else { ui->myPOTAEdit->setCompleter(nullptr); } } void QSODetailDialog::myWWFFChanged(const QString &newWWFF) { FCT_IDENTIFICATION; if ( newWWFF.length() >= 3 ) { ui->myWWFFEdit->setCompleter(wwffCompleter.data()); } else { ui->myWWFFEdit->setCompleter(nullptr); } } void QSODetailDialog::clubQueryResult(const QString &in_callsign, QMap data) { FCT_IDENTIFICATION; if ( in_callsign != ui->callsignEdit->text().toUpper() ) { // do not need this result return; } QString memberText; QMapIterator clubs(data); QPalette palette; //"Hello World" while ( clubs.hasNext() ) { clubs.next(); QColor color = Data::statusToColor(static_cast(clubs.value().status), false, palette.color(QPalette::Text)); memberText.append(QString("%2   ").arg(Data::colorToHTMLColor(color), clubs.key())); } ui->memberListLabel->setText(memberText); } bool QSODetailDialog::highlightInvalid(QLabel *labelWidget, bool cond, const QString &toolTip) { FCT_IDENTIFICATION; qCDebug(function_parameters) << labelWidget->objectName() << cond << toolTip; QString currToolTip(labelWidget->toolTip()); if ( cond ) { if ( ! currToolTip.contains(toolTip) ) { currToolTip.append(tr("Warning: ") + toolTip + "
"); } } else { currToolTip.remove(tr("Warning: ") + toolTip + "
"); } if ( currToolTip.isEmpty() ) { labelWidget->setStyleSheet(""); } else { labelWidget->setStyleSheet("color: black; border-radius: 5px; background: yellow;"); } labelWidget->setToolTip(currToolTip); return !cond; } void QSODetailDialog::blockMappedWidgetSignals(bool inBlocking) { FCT_IDENTIFICATION; qCDebug(function_parameters) << inBlocking; for ( int i = 0; i < LogbookModel::COLUMN_LAST_ELEMENT; i++) { QWidget *widget = mapper->mappedWidgetAt(i); if ( widget ) { widget->blockSignals(inBlocking); } } ui->modeEdit->blockSignals(false); //exception - submode must be filled based on Mode combobox } void QSODetailDialog::drawDXOnMap(const QString &label, const Gridsquare &dxGrid) { FCT_IDENTIFICATION; qCDebug(function_parameters) << label << dxGrid; if ( !dxGrid.isValid() ) { return; } QString stationString; QString popupString = label; Gridsquare myGrid = Gridsquare(ui->myGridEdit->text()); double distance = 0; if (dxGrid.distanceTo(myGrid, distance)) { QString unit; distance = Gridsquare::distance2localeUnitDistance(distance, unit, locale); popupString.append(QString("
%1 %2").arg(QString::number(distance, 'f', 0), unit)); } double lat = dxGrid.getLatitude(); double lon = dxGrid.getLongitude(); // do not wrap the points double delta = lon - myGrid.getLongitude(); if ( delta > 180 ) lon -= 360; if ( delta < -180 ) lon += 360; stationString.append(QString("[[\"%1\", %2, %3, yellowIcon]]").arg(popupString).arg(lat).arg(lon)); QString shortPath = QString("[%1, %2, %3, %4]") .arg(myGrid.getLatitude()) .arg(myGrid.getLongitude()) .arg(lat) .arg(lon); QString javaScript = QString("grids_confirmed = [];" "grids_worked = [];" "drawPoints(%1);" "drawShortPaths([%2]);" "maidenheadConfWorked.redraw();" "flyToPoint(%3[0], 6);") .arg(stationString, shortPath, stationString); qCDebug(runtime) << javaScript; if ( !isMainPageLoaded ) { postponedScripts.append(javaScript); } else { main_page->runJavaScript(javaScript); } } void QSODetailDialog::drawMyQTHOnMap(const QString &label, const Gridsquare &myGrid) { FCT_IDENTIFICATION; qCDebug(function_parameters) << label << myGrid; if ( ! myGrid.isValid() ) { return; } QString stationString; double lat = myGrid.getLatitude(); double lon = myGrid.getLongitude(); stationString.append(QString("[[\"%1\", %2, %3, homeIcon]]").arg(label).arg(lat).arg(lon)); QString javaScript = QString("grids_confirmed = [];" "grids_worked = [];" "drawPointsGroup2(%1);" "maidenheadConfWorked.redraw();").arg(stationString); qCDebug(runtime) << javaScript; if ( !isMainPageLoaded ) { postponedScripts.append(javaScript); } else { main_page->runJavaScript(javaScript); } } void QSODetailDialog::setStaticMapTime(const QDateTime &dateTime) { FCT_IDENTIFICATION; qCDebug(function_parameters) << dateTime; QString javaScript = QString("setStaticMapTime(new Date(%1));").arg(dateTime.toMSecsSinceEpoch()); qCDebug(runtime) << javaScript; if (!isMainPageLoaded) { postponedScripts.append(javaScript); } else { main_page->runJavaScript(javaScript); } } void QSODetailDialog::enableWidgetChangeHandlers() { FCT_IDENTIFICATION; for ( int i = 0; i < LogbookModel::COLUMN_LAST_ELEMENT; i++) { QWidget *widget = mapper->mappedWidgetAt(i); if ( !widget ) continue; if ( QLineEdit *line = qobject_cast(widget) ) { connect(line, &QLineEdit::textChanged, this, &QSODetailDialog::doValidation); connect(line, &QLineEdit::textChanged, this, [line]() { line->setStyleSheet(CHANGECSS); }); } else if ( QTextEdit *edit = qobject_cast(widget) ) { connect(edit, &QTextEdit::textChanged, this, &QSODetailDialog::doValidation); connect(edit, &QTextEdit::textChanged, this, [edit]() { edit->setStyleSheet(CHANGECSS); }); } else if ( QDoubleSpinBox *spin = qobject_cast(widget) ) { connect(spin, QOverload::of(&QDoubleSpinBox::valueChanged), this, &QSODetailDialog::doValidationDouble); connect(spin, QOverload::of(&QDoubleSpinBox::valueChanged), this, [spin](double) { spin->setStyleSheet(CHANGECSS); }); } else if ( QDateTimeEdit *datetime = qobject_cast(widget) ) { connect(datetime, &QDateTimeEdit::dateTimeChanged, this, &QSODetailDialog::doValidationDateTime); connect(datetime, &QDateTimeEdit::dateTimeChanged, this, [datetime](QDateTime) { datetime->setStyleSheet(CHANGECSS); }); } else if ( QComboBox *combo = qobject_cast(widget) ) { connect(combo, &QComboBox::currentTextChanged, this, &QSODetailDialog::doValidation); connect(combo, &QComboBox::currentTextChanged, this, [combo](QString) { combo->setStyleSheet(CHANGECSS); }); } else if ( SmartSearchBox *combo = qobject_cast(widget) ) { connect(combo, &SmartSearchBox::currentTextChanged, this, &QSODetailDialog::doValidation); connect(combo, &SmartSearchBox::currentTextChanged, this, [combo](QString) { combo->setStyleSheet(CHANGECSS); }); } } } void QSODetailDialog::updateCountyCompleter(int dxcc) { FCT_IDENTIFICATION; ui->countyEdit->setCompleter(nullptr); countyCompleter.reset(Data::createCountyCompleter(dxcc, this)); ui->countyEdit->setCompleter(countyCompleter.data()); } void QSODetailDialog::lookupButtonWaitingStyle(bool isWaiting) { FCT_IDENTIFICATION; if ( isWaiting ) { lookupButtonMovie->start(); } else { lookupButtonMovie->stop(); QIcon icon; icon.addFile(QString::fromUtf8(":/icons/baseline-search-24px.svg")); lookupButton->setIcon(icon); } } QSODetailDialog::SubmitError QSODetailDialog::submitAllChanges() { FCT_IDENTIFICATION; if ( ! doValidation() ) { QMessageBox::StandardButton reply; reply = QMessageBox::question(this, tr("Validation"), tr("Yellow marked fields are invalid.

Nevertheless, save the changes?

"), QMessageBox::Yes|QMessageBox::No); if (reply != QMessageBox::Yes) return QSODetailDialog::SubmitCancelledByUser; } if ( ! mapper->submit() ) { qWarning("Mapper was not submitted"); return QSODetailDialog::SubmitMapperError; } if ( ! model->submit() ) { qWarning("Model was not submitted"); return QSODetailDialog::SubmitModelError; } return QSODetailDialog::SubmitOK; } void QSODetailDialog::callbookLookupFinished() { FCT_IDENTIFICATION; lookupButton->setEnabled(true); resetButton->setEnabled(true); editButton->setEnabled(true); lookupButtonWaitingStyle(false); } void QSODetailDialog::callbookLookupStart() { FCT_IDENTIFICATION; lookupButton->setEnabled(false); resetButton->setEnabled(false); editButton->setEnabled(false); lookupButtonWaitingStyle(true); } void QSODetailDialog::refreshDXStatTabs() { FCT_IDENTIFICATION; const DxccEntity &dxccEntity = Data::instance()->lookupDxccIDClublog(editedRecord->field("dxcc").value().toInt()); const Band &currBand = BandPlan::freq2Band(ui->freqTXEdit->value()); ui->dxccTableWidget->setDxcc(dxccEntity.dxcc, currBand); ui->stationTableWidget->setDxCallsign(ui->callsignEdit->text(), currBand); } const QString QSODetailDialog::getButtonText(int index) const { FCT_IDENTIFICATION; static const char *buttonText[] = { QT_TR_NOOP("&Save"), QT_TR_NOOP("&Edit") }; return tr(buttonText[index]); } void QSOEditMapperDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { if ( editor->objectName() == "qslSentBox" || editor->objectName() == "qslSentViaBox" || editor->objectName() == "qslPaperSentStatusBox" || editor->objectName() == "qslPaperReceiveStatusBox" || editor->objectName() == "qslLotwSentStatusBox" || editor->objectName() == "qslEqslSentStatusBox" ) { QComboBox* combo = qobject_cast(editor); if ( combo ) { combo->setCurrentIndex(combo->findData(index.data())); } return; } else if ( editor->objectName() == "propagationModeEdit" ) { QComboBox* combo = qobject_cast(editor); if ( combo ) { combo->setCurrentText(Data::instance()->propagationModeIDToText(index.data().toString())); } return; } else if ( editor->objectName() == "satModeEdit" ) { QComboBox* combo = qobject_cast(editor); if ( combo ) { combo->setCurrentText(Data::instance()->satModeIDToText(index.data().toString())); } return; } else if ( editor->objectName() == "qslEqslReceiveDateLabel" || editor->objectName() == "qslLotwReceiveDateLabel" ) { QLabel* label = qobject_cast(editor); if ( label ) { if ( !index.data().toString().isEmpty() ) { label->setText(index.data().toDate().toString(locale.formatDateShortWithYYYY())); } } return; } else if ( editor->objectName() == "qslEqslReceiveStatusLabel" || editor->objectName() == "qslLotwReceiveStatusLabel" ) { QLabel* label = qobject_cast(editor); if ( label ) { QString statusIcon = QString("").arg((index.data().toString() == "Y") ? "done" : "close"); label->setText(statusIcon); label->setProperty("originvalue", index.data()); } return; } else if ( editor->objectName() == "countryBox" || editor->objectName() == "myCountryBox" ) { SmartSearchBox* combo = qobject_cast(editor); if ( combo ) combo->setCurrentValue(index.data(), 1); return; } else if ( editor->objectName() == "noteEdit" ) { QTextEdit *textEdit = static_cast(editor); if ( textEdit ) { QString value = index.data().toString(); textEdit->setPlainText(value); textEdit->setAcceptRichText(false); return; } } else if ( editor->objectName() == "freqRXEdit" ) { QDoubleSpinBox *spin = static_cast(editor); if ( spin ) { spin->setValue(index.data().toDouble()); return; } } else if ( editor->objectName() == "qslPaperReceiveDateEdit" || editor->objectName() == "qslPaperSentDateEdit" || editor->objectName() == "qslLotwSentDateEdit" || editor->objectName() == "qslEqslSentDateEdit" ) { QDateEdit *dateEdit = qobject_cast(editor); if ( dateEdit ) { if ( !index.data().toDate().isValid() ) { dateEdit->setDate(dateEdit->minimumDate()); return; } } } QItemDelegate::setEditorData(editor, index); // Hack: all NewContactEditLines should display // an initial part of the line. // Do not insert this functionality to NewContactEditLines because // this function is wanted only for the QSODetail Widget. NewContactEditLine* lineEdit = qobject_cast(editor); if ( lineEdit ) { lineEdit->home(false); } } void QSOEditMapperDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { /* ALL combos with Data */ if ( editor->objectName() == "qslSentBox" || editor->objectName() == "qslSentViaBox" || editor->objectName() == "qslPaperSentStatusBox" || editor->objectName() == "qslPaperReceiveStatusBox" || editor->objectName() == "qslLotwSentStatusBox" || editor->objectName() == "qslEqslSentStatusBox" ) { QComboBox* combo = static_cast(editor); if ( combo ) { model->setData(index, combo->currentData()); return; } } else if ( editor->objectName() == "propagationModeEdit" ) { QComboBox* combo = static_cast(editor); if ( combo ) { model->setData(index, Data::instance()->propagationModeTextToID(combo->currentText())); return; } } else if ( editor->objectName() == "satModeEdit" ) { QComboBox* combo = static_cast(editor); if ( combo ) { model->setData(index, Data::instance()->satModeTextToID(combo->currentText())); return; } } else if ( editor->objectName() == "noteEdit" ) { QTextEdit *textEdit = static_cast(editor); if ( textEdit ) { model->setData(index, textEdit->toPlainText()); return; } } else if ( editor->objectName() == "qslPaperReceiveDateEdit" || editor->objectName() == "qslPaperSentDateEdit" || editor->objectName() == "qslLotwSentDateEdit" || editor->objectName() == "qslEqslSentDateEdit" ) { QDateEdit *dateEdit = qobject_cast(editor); if ( dateEdit ) { if ( dateEdit->date() == dateEdit->minimumDate() ) { model->setData(index, QVariant()); } else { model->setData(index, dateEdit->date()); } return; } } else if ( editor->objectName() == "countryBox" || editor->objectName() == "myCountryBox") { SmartSearchBox *box = qobject_cast(editor); if ( box ) { int row = box->currentIndex(); QVariant dataDXCC; QVariant dataCountryEN; if ( row > 0 ) // the first line is an empty line { dataDXCC = box->currentValue(1); dataCountryEN = box->currentValue(3); } model->setData(index, dataDXCC); model->setData(model->index(index.row(), (editor->objectName() == "countryBox" ) ? LogbookModel::COLUMN_COUNTRY_INTL : LogbookModel::COLUMN_MY_COUNTRY_INTL), dataCountryEN); } return; } else if ( editor->objectName() == "qslEqslReceiveDateLabel" || editor->objectName() == "qslEqslReceiveStatusLabel" || editor->objectName() == "qslLotwReceiveDateLabel" || editor->objectName() == "qslLotwReceiveStatusLabel" ) { /* do not save */ return; } else if ( editor->objectName() == "bandRXCombo" || editor->objectName() == "bandTXCombo" ) { QComboBox* combo = static_cast(editor); if ( combo ) { if ( combo->currentIndex() == 0 ) { model->setData(index, QVariant()); } else { model->setData(index, combo->currentText()); } } return; } QItemDelegate::setModelData(editor, model, index); } bool QSOEditMapperDelegate::eventFilter(QObject *object, QEvent *event) { /* need to be eventFilter before Key Press IF because * this signal is used to reset Stylesheet for widget that is reset. And emit * has to be callled at the end of operation */ bool ret = QItemDelegate::eventFilter(object, event); if (event->type() == QEvent::KeyPress) { if (static_cast(event)->key() == Qt::Key_Escape) { emit keyEscapePressed(object); } } return ret; } QSODetailDialog::LogbookModelPrivate::LogbookModelPrivate(QObject *parent, QSqlDatabase db) : LogbookModel(parent, db) { setTable("contacts"); setEditStrategy(QSqlTableModel::OnRowChange); } QVariant QSODetailDialog::LogbookModelPrivate::data(const QModelIndex &index, int role) const { return QSqlTableModel::data(index, role); // clazy:exclude=skipped-base-method } bool QSODetailDialog::LogbookModelPrivate::setData(const QModelIndex &index, const QVariant &value, int role) { bool main_update_result = true; bool depend_update_result = true; if ( role == Qt::EditRole ) { switch ( index.column() ) { case COLUMN_CALL: { const QString &pfxRef = Callsign(value.toString()).getWPXPrefix(); if ( !pfxRef.isEmpty() ) { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_PREFIX), QVariant(pfxRef), role); // clazy:exclude=skipped-base-method } break; } case COLUMN_GRID: { if ( ! value.toString().isEmpty() ) { Gridsquare newgrid(value.toString()); if ( newgrid.isValid() ) { Gridsquare mygrid(QSqlTableModel::data(this->index(index.row(), COLUMN_MY_GRIDSQUARE), Qt::DisplayRole).toString()); // clazy:exclude=skipped-base-method double distance; if ( mygrid.distanceTo(newgrid, distance) ) { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_DISTANCE), QVariant(distance),role); // clazy:exclude=skipped-base-method } else { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_DISTANCE), QVariant(),role); // clazy:exclude=skipped-base-method } } else { /* do not update field with invalid Grid */ depend_update_result = false; } } else { /* empty grid is valid (when removing a value); need to remove also Distance */ depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_DISTANCE), QVariant(),role); // clazy:exclude=skipped-base-method } break; } case COLUMN_MY_GRIDSQUARE: { if ( ! value.toString().isEmpty() ) { Gridsquare mynewGrid(value.toString()); if ( mynewGrid.isValid() ) { Gridsquare dxgrid(QSqlTableModel::data(this->index(index.row(), COLUMN_GRID), Qt::DisplayRole).toString()); // clazy:exclude=skipped-base-method double distance; if ( mynewGrid.distanceTo(dxgrid, distance) ) { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_DISTANCE), QVariant(distance),role); // clazy:exclude=skipped-base-method } else { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_DISTANCE), QVariant(),role); // clazy:exclude=skipped-base-method } } else { /* do not update field with invalid Grid */ depend_update_result = false; } } else { /* empty grid is valid (when removing a value); need to remove also Distance */ depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_DISTANCE), QVariant(),role); // clazy:exclude=skipped-base-method } break; } case COLUMN_ADDRESS_INTL: { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_ADDRESS), Data::removeAccents(value.toString()),role); // clazy:exclude=skipped-base-method break; } case COLUMN_COMMENT_INTL: { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_COMMENT), Data::removeAccents(value.toString()),role); // clazy:exclude=skipped-base-method break; } case COLUMN_COUNTRY_INTL: { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_COUNTRY), Data::removeAccents(value.toString()),role); // clazy:exclude=skipped-base-method break; } case COLUMN_MY_ANTENNA_INTL: { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_MY_ANTENNA), Data::removeAccents(value.toString()),role); // clazy:exclude=skipped-base-method break; } case COLUMN_MY_CITY_INTL: { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_MY_CITY), Data::removeAccents(value.toString()),role); // clazy:exclude=skipped-base-method break; } case COLUMN_MY_COUNTRY_INTL: { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_MY_COUNTRY), Data::removeAccents(value.toString()),role); // clazy:exclude=skipped-base-method break; } case COLUMN_MY_NAME_INTL: { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_MY_NAME), Data::removeAccents(value.toString()),role); // clazy:exclude=skipped-base-method break; } case COLUMN_MY_POSTAL_CODE_INTL: { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_MY_POSTAL_CODE), Data::removeAccents(value.toString()),role); // clazy:exclude=skipped-base-method break; } case COLUMN_MY_RIG_INTL: { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_MY_RIG), Data::removeAccents(value.toString()),role); // clazy:exclude=skipped-base-method break; } case COLUMN_MY_SIG_INTL: { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_MY_SIG), Data::removeAccents(value.toString()),role); // clazy:exclude=skipped-base-method break; } case COLUMN_MY_SIG_INFO_INTL: { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_MY_SIG_INFO), Data::removeAccents(value.toString()),role); // clazy:exclude=skipped-base-method break; } case COLUMN_MY_STREET_INTL: { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_MY_STREET), Data::removeAccents(value.toString()),role); // clazy:exclude=skipped-base-method break; } case COLUMN_NAME_INTL: { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_NAME), Data::removeAccents(value.toString()),role); // clazy:exclude=skipped-base-method break; } case COLUMN_NOTES_INTL: { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_NOTES), Data::removeAccents(value.toString()),role); // clazy:exclude=skipped-base-method break; } case COLUMN_QSLMSG_INTL: { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_QSLMSG), Data::removeAccents(value.toString()),role); // clazy:exclude=skipped-base-method break; } case COLUMN_QTH_INTL: { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_QTH), Data::removeAccents(value.toString()),role); // clazy:exclude=skipped-base-method break; } case COLUMN_RIG_INTL: { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_RIG), Data::removeAccents(value.toString()),role); // clazy:exclude=skipped-base-method break; } case COLUMN_SIG_INTL: { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_SIG), Data::removeAccents(value.toString()),role); // clazy:exclude=skipped-base-method break; } case COLUMN_SIG_INFO_INTL: { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_SIG_INFO), Data::removeAccents(value.toString()),role); // clazy:exclude=skipped-base-method break; } case COLUMN_SOTA_REF: { SOTAEntity sotaInfo = Data::instance()->lookupSOTA(value.toString()); if ( sotaInfo.summitCode.toUpper() == value.toString().toUpper() && !sotaInfo.summitName.isEmpty() ) { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_ALTITUDE), sotaInfo.altm, role); // clazy:exclude=skipped-base-method } else { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_ALTITUDE), QVariant(), role); // clazy:exclude=skipped-base-method } break; } case COLUMN_MY_SOTA_REF: { SOTAEntity sotaInfo = Data::instance()->lookupSOTA(value.toString()); if ( sotaInfo.summitCode.toUpper() == value.toString().toUpper() && !sotaInfo.summitName.isEmpty() ) { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_MY_ALTITUDE), sotaInfo.altm, role); // clazy:exclude=skipped-base-method } else { depend_update_result = QSqlTableModel::setData(this->index(index.row(), COLUMN_MY_ALTITUDE), QVariant(), role); // clazy:exclude=skipped-base-method } break; } } //updateExternalServicesUploadStatus(index, role, depend_update_result); if ( depend_update_result ) { switch ( index.column() ) { case COLUMN_FREQUENCY: case COLUMN_FREQ_RX: case COLUMN_TX_POWER: /* store NULL when 0.0MHz */ main_update_result = QSqlTableModel::setData(index, ( value.toDouble() == 0.0 ) ? QVariant() // clazy:exclude=skipped-base-method : value, role); break; case COLUMN_SOTA_REF: case COLUMN_MY_SOTA_REF: case COLUMN_POTA_REF: case COLUMN_MY_POTA_REF: case COLUMN_IOTA: case COLUMN_MY_IOTA: case COLUMN_MY_GRIDSQUARE: case COLUMN_CALL: case COLUMN_GRID: case COLUMN_VUCC_GRIDS: case COLUMN_MY_VUCC_GRIDS: case COLUMN_MY_WWFF_REF: case COLUMN_WWFF_REF: case COLUMN_STATION_CALLSIGN: case COLUMN_OPERATOR: case COLUMN_DARC_DOK: case COLUMN_MY_DARC_DOK: main_update_result = QSqlTableModel::setData(index, ( !value.toString().isEmpty() ) ? value.toString().toUpper() // clazy:exclude=skipped-base-method : QVariant(), role); break; case COLUMN_ADDRESS_INTL: case COLUMN_COMMENT_INTL: case COLUMN_COUNTRY_INTL: case COLUMN_MY_ANTENNA_INTL: case COLUMN_MY_CITY_INTL: case COLUMN_MY_COUNTRY_INTL: case COLUMN_MY_NAME_INTL: case COLUMN_MY_POSTAL_CODE_INTL: case COLUMN_MY_RIG_INTL: case COLUMN_MY_SIG_INTL: case COLUMN_MY_SIG_INFO_INTL: case COLUMN_MY_STREET_INTL: case COLUMN_NAME_INTL: case COLUMN_NOTES_INTL: case COLUMN_QSLMSG_INTL: case COLUMN_QTH_INTL: case COLUMN_RIG_INTL: case COLUMN_SIG_INTL: case COLUMN_SIG_INFO_INTL: main_update_result = QSqlTableModel::setData(index, ( !value.toString().isEmpty() ) ? value // clazy:exclude=skipped-base-method : QVariant(), role); break; default: main_update_result = QSqlTableModel::setData(index, ( !value.toString().isEmpty() ) ? Data::removeAccents(value.toString()) // clazy:exclude=skipped-base-method : QVariant(), role); } } } return main_update_result && depend_update_result; } ================================================ FILE: ui/QSODetailDialog.h ================================================ #ifndef QLOG_UI_QSODETAILDIALOG_H #define QLOG_UI_QSODETAILDIALOG_H #include #include #include #include #include #include #include #include "models/LogbookModel.h" #include "data/Gridsquare.h" #include "core/CallbookManager.h" #include "ui/MapWebChannelHandler.h" #include "ui/WebEnginePage.h" #include "core/MembershipQE.h" #include "core/LogLocale.h" #include "ui/component/MultiselectCompleter.h" namespace Ui { class QSODetailDialog; } class ModeSelectionController; class QSOEditMapperDelegate : public QItemDelegate { Q_OBJECT public: QSOEditMapperDelegate(QObject *parent = 0) : QItemDelegate(parent) {}; void setEditorData(QWidget *editor, const QModelIndex &index) const override; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override; signals: void keyEscapePressed(QObject *); private: bool eventFilter(QObject *object, QEvent *event) override; LogLocale locale; }; class QSODetailDialog : public QDialog { Q_OBJECT public: explicit QSODetailDialog(const QSqlRecord &qso, QWidget *parent = nullptr); void accept() override; void keyPressEvent(QKeyEvent *evt) override; ~QSODetailDialog(); signals: void contactUpdated(QSqlRecord&); private slots: void editButtonPressed(); void resetButtonPressed(); void lookupButtonPressed(); void resetKeyPressed(QObject *); void setReadOnlyMode(bool); void modeChanged(QString); void showPaperButton(); void showEQSLButton(); void dateTimeOnChanged(const QDateTime &); void dateTimeOffChanged(const QDateTime &); void freqTXChanged(double); void freqRXChanged(double); void timeLockToggled(bool); void freqLockToggled(bool); void callsignChanged(const QString&); void callsignEditFinished(); void queryMemberList(); void propagationModeChanged(const QString &); bool doValidation(); void doValidationDateTime(const QDateTime&); void doValidationDouble(double); void mapLoaded(bool); void myGridChanged(const QString&); void DXGridChanged(const QString&); void callsignFound(const CallbookResponseData &data); void callsignNotFound(const QString&); void callbookLoginFailed(const QString&); void callbookError(const QString&); void handleBeforeUpdate(int, QSqlRecord&); void sotaChanged(const QString&); void potaChanged(const QString&); void wwffChanged(const QString&); void mySotaChanged(const QString&); void myPOTAChanged(const QString&); void myWWFFChanged(const QString&); void clubQueryResult(const QString &in_callsign, QMap data); void updateCountyCompleter(int dxcc); private: /* It is modified logbook model when only basic * validation are done. The extended validations * are done in Form itself */ class LogbookModelPrivate : public LogbookModel { public: explicit LogbookModelPrivate(QObject* parent = nullptr, QSqlDatabase db = QSqlDatabase()); QVariant data(const QModelIndex &index, int role) const override; bool setData(const QModelIndex &index, const QVariant &value, int role) override; }; enum SubmitError { SubmitOK = 0, SubmitCancelledByUser = 1, SubmitMapperError = 2, SubmitModelError = 3 }; bool highlightInvalid(QLabel *, bool, const QString&); void blockMappedWidgetSignals(bool); void drawDXOnMap(const QString &label, const Gridsquare &dxGrid); void drawMyQTHOnMap(const QString &label, const Gridsquare &myGrid); void setStaticMapTime(const QDateTime &dateTime); void enableWidgetChangeHandlers(); void lookupButtonWaitingStyle(bool); SubmitError submitAllChanges(); void callbookLookupFinished(); void callbookLookupStart(); void refreshDXStatTabs(); const QString getButtonText(int index) const; Ui::QSODetailDialog *ui; QPointer mapper; QPointer model; QSqlRecord *editedRecord; QPointer editButton; QPointer resetButton; QPointer lookupButton; QPointer lookupButtonMovie; qint64 timeLockDiff; double freqLockDiff; bool isMainPageLoaded; QPointer main_page; QString postponedScripts; CallbookManager callbookManager; QScopedPointer iotaCompleter; QScopedPointer myIotaCompleter; QScopedPointer sotaCompleter; QScopedPointer mySotaCompleter; QScopedPointer potaCompleter; QScopedPointer myPotaCompleter; QScopedPointer wwffCompleter; QScopedPointer myWWFFCompleter; QScopedPointer sigCompleter; QScopedPointer countyCompleter; QScopedPointer modeController; QWebChannel channel; MapWebChannelHandler layerControlHandler; LogLocale locale; }; #endif // QLOG_UI_QSODETAILDIALOG_H ================================================ FILE: ui/QSODetailDialog.ui ================================================ QSODetailDialog 0 0 16777215 764 4 6 0 0 0 Qt::StrongFocus QAbstractSpinBox::UpDownArrows dd/MM/yyyy HH:mm:ss Qt::UTC 0 0 Qt::StrongFocus dd/MM/yyyy HH:mm:ss Qt::UTC 0 0 24 16777215 true QPushButton { color:none; border:none; } QPushButton:checked{ background-color: none; border: none; } QPushButton:hover{ background-color: none; border: none; } :/icons/disconnect.svg :/icons/connect.svg:/icons/disconnect.svg 24 24 true true false true true 0 0 400 16777215 20 true 25 RSTs RSTr Mode Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter 0 0 Time On 0 0 Time Off 0 0 Qt::WheelFocus 0 0 Qt::WheelFocus 0 0 24 16777215 true QPushButton { color:none; border:none; } QPushButton:checked{ background-color: none; border: none; } QPushButton:hover{ background-color: none; border: none; } :/icons/disconnect.svg :/icons/connect.svg:/icons/disconnect.svg 24 24 true true true 0 0 65 16777215 11 25 0 0 65 16777215 11 25 Callsign 0 0 TX: true 0 0 Qt::StrongFocus Blank false MHz 5 7500000.000000000000000 0.001000000000000 0.000000000000000 0 0 RX: 0 0 Qt::StrongFocus Blank false MHz 5 7500000.000000000000000 0.001000000000000 0.000000000000000 0 0 Frequency Qt::AlignCenter Qt::Horizontal 40 20 Band 6 0 16777215 16777215 QTH Name Gridsquare 120 16777215 12 0 0 Comment 180 16777215 50 250 16777215 75 0 0 My Notes 0 0 0 0 400 200 false Qt::TextEditorInteraction 0 0 600 0 16777215 16777215 Qt::NoContextMenu about:blank 0 0 Qt::StrongFocus 0 &Details 4 Country Cont ITU 0 0 50 16777215 Qt::StrongFocus 2 CQ 0 0 50 16777215 Qt::StrongFocus 2 State 0 0 200 16777215 Qt::StrongFocus County 0 0 200 16777215 Qt::StrongFocus Age 0 0 50 16777215 Qt::StrongFocus true 0 0 Qt::WheelFocus QComboBox::AdjustToContents AF AN AS EU NA OC SA VUCC Qt::StrongFocus two or four adjacent Gridsquares, each four characters long, (ex. EN98,FM08,EM97,FM07) 0 0 Qt::StrongFocus 4 IOTA 0 0 Qt::StrongFocus POTA SOTA 0 0 Qt::StrongFocus WWFF SIG Qt::StrongFocus SIG Info Qt::StrongFocus 4 0 FISTS SKCC Ten-Ten UKSMG DOK Qt::StrongFocus the contacted station's DARC DOK (District Location Code) (ex. A01) FISTS CC 4 E-Mail 0 0 Qt::StrongFocus URL 0 0 Qt::StrongFocus Propagation Mode 0 0 300 0 Qt::WheelFocus Satellite Name false 0 0 Qt::StrongFocus Satellite Mode false 0 0 300 0 Qt::WheelFocus D&X Stats 0 0 <b>DXCC Statistics</b> 0 0 Qt::ScrollBarAlwaysOff Qt::ScrollBarAlwaysOff QAbstractScrollArea::AdjustToContentsOnFirstShow QAbstractItemView::NoEditTriggers true 20 45 23 0 0 <b>Station Statistics</b> 0 0 Qt::ScrollBarAlwaysOff Qt::ScrollBarAlwaysOff QAbstractScrollArea::AdjustToContents QAbstractItemView::NoEditTriggers true 20 45 23 M&y Station 4 Callsign Operator Name Country ITU 0 0 50 16777215 CQ 0 0 50 16777215 Qt::Horizontal 40 20 Gridsquare QTH County Operator Callsign 0 0 Qt::StrongFocus 4 IOTA POTA SOTA WWFF SIG Sig Info VUCC DOK 4 Rig Antenna Power 0 0 Qt::ClickFocus Blank W 3 1000000.000000000000000 0.000000000000000 &QSL 4 Qt::Horizontal QSizePolicy::Maximum 20 20 true Show QSL Card 0 0 Blank 0 0 0 1900 1 1 1900 1 1 0 0 105 0 <b>Yes</b> - an incoming QSL card has been received; the QSO has been confirmed by the online service<br/><b>No</b> - an incoming QSL card has not been received; the QSO has not been confirmed by the online service<br/><b>Requested</b> - the logging station has requested a QSL card; the logging station has requested the QSO be uploaded to the online service<br/> - - 0 0 105 0 Qt::WheelFocus QSL Sent via 0 0 0 0 Qt::StrongFocus 25 QSL via 0 0 Blank 1900 1 1 1900 1 1 <b>Yes</b> - an outgoing QSL card has been sent; the QSO has been uploaded to, and accepted by, the online service<br/><b>No</b> - do not send an outgoing QSL card; do not upload the QSO to the online service<br/><b>Requested</b> - the contacted station has requested a QSL card; the contacted station has requested the QSO be uploaded to the online service<br/><b>Queued</b> - an outgoing QSL card has been selected to be sent; a QSO has been selected to be uploaded to the online service<br/> 0 0 Blank 0 0 0 1900 1 1 1900 1 1 0 0 105 0 Qt::WheelFocus <b>Yes</b> - an outgoing QSL card has been sent; the QSO has been uploaded to, and accepted by, the online service<br/><b>No</b> - do not send an outgoing QSL card; do not upload the QSO to the online service<br/><b>Requested</b> - the contacted station has requested a QSL card; the contacted station has requested the QSO be uploaded to the online service<br/><b>Queued</b> - an outgoing QSL card has been selected to be sent; a QSO has been selected to be uploaded to the online service<br/> 0 0 Received 0 0 true eQSL true Manage QSL Card - - 0 0 true Paper 0 0 0 0 Sent 0 0 Blank 1900 1 1 1900 1 1 <b>Yes</b> - an outgoing QSL card has been sent; the QSO has been uploaded to, and accepted by, the online service<br/><b>No</b> - do not send an outgoing QSL card; do not upload the QSO to the online service<br/><b>Requested</b> - the contacted station has requested a QSL card; the contacted station has requested the QSO be uploaded to the online service<br/><b>Queued</b> - an outgoing QSL card has been selected to be sent; a QSO has been selected to be uploaded to the online service<br/> QSLr Message 0 0 true LoTW QSLs Message Qt::Vertical 20 40 &Contest Contest ID RcvNr true RcvExch true SendNr true SendExch true Qt::Horizontal QSizePolicy::Expanding 40 20 0 0 0 0 Member: 0 0 Qt::Horizontal 40 20 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok false QWebEngineView QWidget
QtWebEngineWidgets/QWebEngineView
NewContactEditLine QLineEdit
ui/component/EditLine.h
DxccTableWidget QTableView
ui/DxccTableWidget.h
FreqQSpinBox QDoubleSpinBox
ui/component/FreqQSpinBox.h
SmartSearchBox QWidget
ui/component/SmartSearchBox.h
1
BaseDoubleSpinBox QDoubleSpinBox
ui/component/BaseDoubleSpinBox.h
dateTimeOnEdit dateTimeOffEdit timeLockButton callsignEdit rstSentEdit rstRcvdEdit modeEdit submodeEdit freqTXEdit freqRXEdit bandTXCombo bandRXCombo freqLockButton nameEdit qthEdit gridEdit commentEdit noteEdit tabWidget countryBox contEdit ituEdit cqEdit stateEdit countyEdit ageEdit vuccEdit iotaEdit potaEdit sotaEdit wwffEdit sigEdit sigInfoEdit dokEdit fistsEdit fistsCCEdit skccEdit tentenEdit uksmgEdit emailEdit urlEdit propagationModeEdit satNameEdit satModeEdit dxccTableWidget myCallsignEdit myOperatorNameEdit myOperatorCallsignEdit myCountryBox myITUEdit myCQEdit myGridEdit myQTHEdit myCountyEdit myDOKEdit myIOTAEdit myPOTAEdit mySOTAEdit myWWFFEdit mySIGEdit mySIGInfoEdit myVUCCEdit myRigEdit myAntEdit qslPaperReceiveDateEdit qslPaperReceiveStatusBox qslPaperSentDateEdit qslPaperSentStatusBox qslLotwSentDateEdit qslLotwSentStatusBox qslEqslSentDateEdit qslEqslSentStatusBox qslSentViaBox qslViaEdit qslPaperPicButton qslEqslPicButton contestIDEdit srxEdit srxStringEdit stxEdit stxStringEdit stationTableWidget qslReceivedMsgEdit qslSentMsgEdit buttonBox rejected() QSODetailDialog reject() 864 751 565 354 buttonBox accepted() QSODetailDialog accept() 864 751 565 354 modeEdit currentTextChanged(QString) QSODetailDialog modeChanged(QString) 694 70 565 354 qslPaperPicButton clicked() QSODetailDialog showPaperButton() 317 662 565 309 qslEqslPicButton clicked() QSODetailDialog showEQSLButton() 661 662 565 309 dateTimeOnEdit dateTimeChanged(QDateTime) QSODetailDialog dateTimeOnChanged(QDateTime) 163 41 565 380 dateTimeOffEdit dateTimeChanged(QDateTime) QSODetailDialog dateTimeOffChanged(QDateTime) 163 74 565 380 timeLockButton toggled(bool) QSODetailDialog timeLockToggled(bool) 266 58 565 380 callsignEdit textChanged(QString) QSODetailDialog callsignChanged(QString) 424 58 565 380 propagationModeEdit currentTextChanged(QString) QSODetailDialog propagationModeChanged(QString) 738 496 565 380 gridEdit textChanged(QString) QSODetailDialog DXGridChanged(QString) 511 128 508 380 myGridEdit textChanged(QString) QSODetailDialog myGridChanged(QString) 237 496 508 380 freqLockButton toggled(bool) QSODetailDialog freqLockToggled(bool) 995 58 508 380 freqRXEdit valueChanged(double) QSODetailDialog freqRXChanged(double) 896 75 508 380 freqTXEdit valueChanged(double) QSODetailDialog freqTXChanged(double) 896 41 508 380 sotaEdit textChanged(QString) QSODetailDialog sotaChanged(QString) 362 467 513 344 mySOTAEdit textChanged(QString) QSODetailDialog mySotaChanged(QString) 539 467 513 344 myWWFFEdit textChanged(QString) QSODetailDialog myWWFFChanged(QString) 539 591 513 344 wwffEdit textChanged(QString) QSODetailDialog wwffChanged(QString) 362 622 513 344 potaEdit textChanged(QString) QSODetailDialog potaChanged(QString) 362 488 513 344 myPOTAEdit textChanged(QString) QSODetailDialog myPOTAChanged(QString) 539 457 513 344 callsignEdit editingFinished() QSODetailDialog callsignEditFinished() 410 53 513 356 modeChanged(QString) showPaperButton() showEQSLButton() dateTimeOnChanged(QDateTime) dateTimeOffChanged(QDateTime) timeLockToggled(bool) callsignChanged(QString) propagationModeChanged(QString) DXGridChanged(QString) myGridChanged(QString) freqLockToggled(bool) freqTXChanged(double) freqRXChanged(double) sotaChanged(QString) mySotaChanged(QString) wwffChanged(QString) myWWFFChanged(QString) potaChanged(QString) myPOTAChanged(QString) callsignEditFinished()
================================================ FILE: ui/QSOFilterDetail.cpp ================================================ #include #include #include #include #include "QSOFilterDetail.h" #include "ui_QSOFilterDetail.h" #include "core/debug.h" #include "data/Data.h" #include "core/QSOFilterManager.h" MODULE_IDENTIFICATION("qlog.ui.qsofilterdetail"); QSOFilterDetail::QSOFilterDetail(const QString &filterName, QWidget *parent) : QDialog(parent), ui(new Ui::QSOFilterDetail), filterName(filterName), condCount(0) { FCT_IDENTIFICATION; ui->setupUi(this); if ( ! filterName.isEmpty() ) loadFilter(filterName); else { /* get Filters name from DB to checking whether a new filter name * will be unique */ filterNamesList = QSOFilterManager::instance()->getFilterList(); } } QSOFilterDetail::~QSOFilterDetail() { FCT_IDENTIFICATION; delete ui; } void QSOFilterDetail::addCondition(int fieldIdx, int operatorId, QString value) { FCT_IDENTIFICATION; qCDebug(function_parameters) << "FieldIDX: " << fieldIdx << " Operator: " << operatorId << " Value: " << value; QHBoxLayout* conditionLayout = new QHBoxLayout(); conditionLayout->setObjectName(QString::fromUtf8("conditionLayout%1").arg(condCount)); /***************/ /* Field Combo */ /***************/ QComboBox* fieldNameCombo = new QComboBox(this); fieldNameCombo->setObjectName(QString::fromUtf8("fieldNameCombo%1").arg(condCount)); QSizePolicy sizePolicy1(QSizePolicy::Maximum, QSizePolicy::Fixed); sizePolicy1.setHorizontalStretch(0); sizePolicy1.setVerticalStretch(0); sizePolicy1.setHeightForWidth(fieldNameCombo->sizePolicy().hasHeightForWidth()); fieldNameCombo->setSizePolicy(sizePolicy1); QList> items; for ( int i = LogbookModel::ColumnID::COLUMN_ID; i < LogbookModel::ColumnID::COLUMN_LAST_ELEMENT; ++i ) { LogbookModel::ColumnID columnID = static_cast(i); items.append({columnID, LogbookModel::getFieldNameTranslation(columnID)}); } std::sort(items.begin(), items.end(), [](const QPair& a, const QPair& b) { return a.second.localeAwareCompare(b.second) < 0; }); for (const auto& item : items) fieldNameCombo->addItem(item.second, item.first); /* Do not set combo value here because we will connect signal Change later */ conditionLayout->addWidget(fieldNameCombo); /*******************/ /* Condition Combo */ /*******************/ QComboBox* conditionCombo = new QComboBox(this); conditionCombo->addItem(QString(tr("Equal"))); conditionCombo->addItem(QString(tr("Not Equal"))); conditionCombo->addItem(QString(tr("Contains"))); conditionCombo->addItem(QString(tr("Not Contains"))); conditionCombo->addItem(QString(tr("Greater Than"))); conditionCombo->addItem(QString(tr("Less Than"))); conditionCombo->addItem(QString(tr("Starts with"))); conditionCombo->addItem(QString(tr("RegExp"))); conditionCombo->setObjectName(QString::fromUtf8("conditionCombo%1").arg(condCount)); if ( operatorId >= 0 ) conditionCombo->setCurrentIndex(operatorId); conditionLayout->addWidget(conditionCombo); /**************/ /* Value Edit */ /**************/ QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); sizePolicy.setHorizontalStretch(0); sizePolicy.setVerticalStretch(0); // use stack to change Line and Date Edit - it will depend on column from combo selection QStackedWidget* stacked = new QStackedWidget(this); stacked->setObjectName(QString::fromUtf8("stackedValueEdit%1").arg(condCount)); stacked->setMaximumSize(QSize(16777215, 30)); stacked->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); stacked->setSizePolicy(sizePolicy); stacked->addWidget(createLineEdit(value, condCount, sizePolicy)); stacked->addWidget(createDateEdit(value, condCount, sizePolicy)); stacked->addWidget(createDateTimeEdit(value, condCount, sizePolicy)); stacked->addWidget(createComboBox(Data::instance()->qslSentEnum, value, condCount, sizePolicy)); stacked->addWidget(createComboBox(Data::instance()->qslSentViaEnum, value, condCount, sizePolicy)); stacked->addWidget(createComboBox(Data::instance()->qslRcvdEnum, value, condCount, sizePolicy)); stacked->addWidget(createComboBox(Data::instance()->uploadStatusEnum, value, condCount, sizePolicy)); stacked->addWidget(createComboBox(Data::instance()->antPathEnum, value, condCount, sizePolicy)); stacked->addWidget(createComboBox(Data::instance()->boolEnum, value, condCount, sizePolicy)); stacked->addWidget(createComboBox(Data::instance()->qsoCompleteEnum, value, condCount, sizePolicy)); stacked->addWidget(createComboBox(Data::instance()->downloadStatusEnum, value, condCount, sizePolicy)); stacked->addWidget(createComboBox(Data::instance()->morseKeyTypeEnum, value, condCount, sizePolicy)); stacked->addWidget(createComboBox(Data::instance()->eqslAgEnum, value, condCount, sizePolicy)); conditionLayout->addWidget(stacked); // connect field combo and stacked widged to switch correct Edit Widget connect(fieldNameCombo, QOverload::of(&QComboBox::currentIndexChanged), this, [this, stacked, value, fieldNameCombo](int) { /* Index is mapped the same way as LogbookModel columns Therefore, we can use Column aliases here */ int fieldIndex = fieldNameCombo->currentData().toInt(); if ( this->isDateField(fieldIndex) ) stacked->setCurrentIndex(1); //Date Edit else if ( this->isDateTimeField(fieldIndex) ) stacked->setCurrentIndex(2); //DateTime edit else if ( this->isQSLSentField(fieldIndex) ) stacked->setCurrentIndex(3); else if ( this->isQSLSentViaField(fieldIndex) ) stacked->setCurrentIndex(4); else if ( this->isQSLRcvdField(fieldIndex) ) stacked->setCurrentIndex(5); else if ( this->isUploadStatusField(fieldIndex) ) stacked->setCurrentIndex(6); else if ( this->isAntPathField(fieldIndex) ) stacked->setCurrentIndex(7); else if ( this->isBoolField(fieldIndex) ) stacked->setCurrentIndex(8); else if ( this->isQSOCompleteField(fieldIndex) ) stacked->setCurrentIndex(9); else if ( this->isDownloadStatusField(fieldIndex)) stacked->setCurrentIndex(10); else if ( this->isMorseKeyTypeField(fieldIndex)) stacked->setCurrentIndex(11); else if ( this->isEqslAgTypeField(fieldIndex)) stacked->setCurrentIndex(12); else stacked->setCurrentIndex(0); }); /* Set FieldNameCombo here to update Stacked Widget */ if ( fieldIdx >= 0 ) { int index = fieldNameCombo->findData(fieldIdx); if (index != -1) fieldNameCombo->setCurrentIndex(index); } /*****************/ /* Remove Button */ /*****************/ QPushButton* removeButton = new QPushButton(tr("Remove"), this); removeButton->setObjectName(QString::fromUtf8("removeButton%1").arg(condCount)); conditionLayout->addWidget(removeButton); connect(removeButton, &QPushButton::clicked, this, [conditionLayout]() { QLayoutItem *item = NULL; while ((item = conditionLayout->takeAt(0)) != 0) { delete item->widget(); delete item; } conditionLayout->deleteLater(); }); /**************************/ /* Add to the main layout */ /**************************/ ui->conditionsLayout->addLayout(conditionLayout); condCount++; } void QSOFilterDetail::loadFilter(const QString &filterName) { FCT_IDENTIFICATION; ui->filterLineEdit->setText(filterName); ui->filterLineEdit->setEnabled(false); const QSOFilter &filter = QSOFilterManager::instance()->getFilter(filterName); if ( filter.filterName == filterName ) { ui->matchingCombo->setCurrentIndex(filter.machingType); for ( const QSOFilterRule &rule : filter.rules ) addCondition(rule.tableFieldIndex, rule.operatorID, rule.value); } } bool QSOFilterDetail::filterExists(const QString &filterName) { FCT_IDENTIFICATION; qCDebug(function_parameters) << filterName; return filterNamesList.contains(filterName); } bool QSOFilterDetail::isDateField(int index) { FCT_IDENTIFICATION; bool ret = ( index == LogbookModel::COLUMN_QSL_RCVD_DATE || index == LogbookModel::COLUMN_QSL_SENT_DATE || index == LogbookModel::COLUMN_LOTW_RCVD_DATE || index == LogbookModel::COLUMN_LOTW_SENT_DATE || index == LogbookModel::COLUMN_CLUBLOG_QSO_UPLOAD_DATE || index == LogbookModel::COLUMN_EQSL_QSLRDATE || index == LogbookModel::COLUMN_EQSL_QSLSDATE || index == LogbookModel::COLUMN_HRDLOG_QSO_UPLOAD_DATE || index == LogbookModel::COLUMN_HAMLOGEU_QSO_UPLOAD_DATE || index == LogbookModel::COLUMN_HAMQTH_QSO_UPLOAD_DATE || index == LogbookModel::COLUMN_DCL_QSLRDATE || index == LogbookModel::COLUMN_DCL_QSLSDATE || index == LogbookModel::COLUMN_QRZCOM_QSO_DOWNLOAD_DATE); qCDebug(function_parameters) << index << " return " << ret; return ret; } bool QSOFilterDetail::isDateTimeField(int index) { FCT_IDENTIFICATION; bool ret = ( index == LogbookModel::COLUMN_TIME_ON || index == LogbookModel::COLUMN_TIME_OFF ); qCDebug(function_parameters) << index << " return " << ret; return ret; } bool QSOFilterDetail::isQSLSentField(int index) { FCT_IDENTIFICATION; bool ret = ( index == LogbookModel::COLUMN_QSL_SENT || index == LogbookModel::COLUMN_LOTW_SENT || index == LogbookModel::COLUMN_EQSL_QSL_SENT || index == LogbookModel::COLUMN_DCL_QSL_SENT); qCDebug(function_parameters) << index << " return " << ret; return ret; } bool QSOFilterDetail::isQSLSentViaField(int index) { FCT_IDENTIFICATION; bool ret = ( index == LogbookModel::COLUMN_QSL_SENT_VIA || index == LogbookModel::COLUMN_QSL_RCVD_VIA ); qCDebug(function_parameters) << index << " return " << ret; return ret; } bool QSOFilterDetail::isQSLRcvdField(int index) { FCT_IDENTIFICATION; bool ret = ( index == LogbookModel::COLUMN_QSL_RCVD || index == LogbookModel::COLUMN_LOTW_RCVD || index == LogbookModel::COLUMN_EQSL_QSL_RCVD || index == LogbookModel::COLUMN_DCL_QSL_RCVD); qCDebug(function_parameters) << index << " return " << ret; return ret; } bool QSOFilterDetail::isUploadStatusField(int index) { FCT_IDENTIFICATION; bool ret = ( index == LogbookModel::COLUMN_CLUBLOG_QSO_UPLOAD_STATUS || index == LogbookModel::COLUMN_HRDLOG_QSO_UPLOAD_STATUS || index == LogbookModel::COLUMN_QRZCOM_QSO_UPLOAD_STATUS || index == LogbookModel::COLUMN_HAMLOGEU_QSO_UPLOAD_STATUS || index == LogbookModel::COLUMN_HAMQTH_QSO_UPLOAD_STATUS); qCDebug(function_parameters) << index << " return " << ret; return ret; } bool QSOFilterDetail::isAntPathField(int index) { FCT_IDENTIFICATION; bool ret = ( index == LogbookModel::COLUMN_ANT_PATH ); qCDebug(function_parameters) << index << " return " << ret; return ret; } bool QSOFilterDetail::isBoolField(int index) { FCT_IDENTIFICATION; bool ret = ( index == LogbookModel::COLUMN_FORCE_INIT || index == LogbookModel::COLUMN_QSO_RANDOM || index == LogbookModel::COLUMN_SILENT_KEY || index == LogbookModel::COLUMN_SWL); qCDebug(function_parameters) << index << " return " << ret; return ret; } bool QSOFilterDetail::isQSOCompleteField(int index) { FCT_IDENTIFICATION; bool ret = ( index == LogbookModel::COLUMN_QSO_COMPLETE ); qCDebug(function_parameters) << index << " return " << ret; return ret; } bool QSOFilterDetail::isDownloadStatusField(int index) { FCT_IDENTIFICATION; bool ret = ( index == LogbookModel::COLUMN_QRZCOM_QSO_DOWNLOAD_STATUS ); qCDebug(function_parameters) << index << " return " << ret; return ret; } bool QSOFilterDetail::isMorseKeyTypeField(int index) { bool ret = ( index == LogbookModel::COLUMN_MORSE_KEY_TYPE || index == LogbookModel::COLUMN_MY_MORSE_KEY_TYPE ); qCDebug(function_parameters) << index << " return " << ret; return ret; } bool QSOFilterDetail::isEqslAgTypeField(int index) { bool ret = ( index == LogbookModel::COLUMN_EQSL_AG ); qCDebug(function_parameters) << index << " return " << ret; return ret; } QComboBox* QSOFilterDetail::createComboBox(const QMap &mapping, const QString &value, const int identifier, const QSizePolicy &sizepolicy) { FCT_IDENTIFICATION; QComboBox* combo = new QComboBox(); combo->setObjectName(QString::fromUtf8("valueCombo%1").arg(identifier)); combo->setFocusPolicy(Qt::ClickFocus); QMapIterator iter(mapping); int iter_index = 0; int value_index = 0; while ( iter.hasNext() ) { iter.next(); combo->addItem(iter.value(), iter.key()); if ( ! value.isEmpty() && iter.key() == value ) value_index = iter_index; iter_index++; } combo->setCurrentIndex(value_index); combo->setSizePolicy(sizepolicy); return combo; } QDateEdit *QSOFilterDetail::createDateEdit(const QString &value, const int identified, const QSizePolicy &sizepolicy) { FCT_IDENTIFICATION; QDateEdit* valueDate = new QDateEdit(); valueDate->setObjectName(QString::fromUtf8("valueDateEdit%1").arg(identified)); valueDate->setFocusPolicy(Qt::ClickFocus); valueDate->setCalendarPopup(true); #if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) valueDate->setTimeZone(QTimeZone::UTC); #else valueDate->setTimeSpec(Qt::UTC); #endif valueDate->setDisplayFormat(locale.formatDateShortWithYYYY()); valueDate->setSizePolicy(sizepolicy); if ( !value.isEmpty() ) valueDate->setDate(QDate::fromString(value, "yyyy-MM-dd")); return valueDate; } QDateTimeEdit *QSOFilterDetail::createDateTimeEdit(const QString &value, const int identified, const QSizePolicy &sizepolicy) { FCT_IDENTIFICATION; QDateTimeEdit* valueDateTime = new QDateTimeEdit(); valueDateTime->setObjectName(QString::fromUtf8("valueDateTimeEdit%1").arg(identified)); valueDateTime->setFocusPolicy(Qt::ClickFocus); #if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) valueDateTime->setTimeZone(QTimeZone::UTC); #else valueDateTime->setTimeSpec(Qt::UTC); #endif valueDateTime->setDisplayFormat(locale.formatDateShortWithYYYY() + " " + locale.formatTimeLongWithoutTZ()); valueDateTime->setSizePolicy(sizepolicy); if ( !value.isEmpty() ) { QDateTime dtValue = QDateTime::fromString(value, "yyyy-MM-ddTHH:mm:ss"); #if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) dtValue.setTimeZone(QTimeZone::UTC); #else dtValue.setTimeSpec(Qt::UTC); #endif valueDateTime->setDateTime(dtValue); } return valueDateTime; } QLineEdit *QSOFilterDetail::createLineEdit(const QString &value, const int identified, const QSizePolicy &sizepolicy) { FCT_IDENTIFICATION; QLineEdit* valueEdit = new QLineEdit(); valueEdit->setObjectName(QString::fromUtf8("valueLineEdit%1").arg(identified)); valueEdit->setSizePolicy(sizepolicy); valueEdit->setText(value); return valueEdit; } void QSOFilterDetail::save() { FCT_IDENTIFICATION; if ( ui->filterLineEdit->text().isEmpty() ) { ui->filterLineEdit->setPlaceholderText(tr("Must not be empty")); return; } if ( filterExists(ui->filterLineEdit->text()) ) { QMessageBox::warning(nullptr, QMessageBox::tr("QLog Info"), QMessageBox::tr("Filter name is already exists.")); return; } const QList &conditionLayouts = ui->conditionsLayout->findChildren(); QSOFilter filter; filter.filterName = ui->filterLineEdit->text(); filter.machingType = ui->matchingCombo->currentIndex(); for ( auto &condition: conditionLayouts ) { QSOFilterRule rule; for ( int i = 0; i < 3; i++ ) { QString objectName = condition->itemAt(i)->widget()->objectName(); if ( objectName.contains("fieldNameCombo") ) rule.tableFieldIndex = dynamic_cast(condition->itemAt(i)->widget())->currentData().toInt(); else if ( objectName.contains("conditionCombo") ) rule.operatorID = dynamic_cast(condition->itemAt(i)->widget())->currentIndex(); else if ( objectName.contains("stackedValueEdit") ) { QStackedWidget* editStack = dynamic_cast(condition->itemAt(i)->widget()); QWidget* stackedEdit = editStack->currentWidget(); if ( stackedEdit ) { QString stacketEditObjName = stackedEdit->objectName(); if ( stacketEditObjName.contains("valueLineEdit") ) { QLineEdit* editLine = dynamic_cast(stackedEdit); rule.value = editLine->text(); } else if ( stacketEditObjName.contains("valueDateEdit") ) { QDateEdit* dateTimeEdit = dynamic_cast(stackedEdit); rule.value = dateTimeEdit->date().toString(Qt::ISODate); } else if ( stacketEditObjName.contains("valueDateTimeEdit") ) { QDateTimeEdit* dateEdit = dynamic_cast(stackedEdit); rule.value = dateEdit->dateTime().toString("yyyy-MM-ddTHH:mm:ss"); } else if ( stacketEditObjName.contains("valueCombo") ) { QComboBox* combo = dynamic_cast(stackedEdit); rule.value = combo->currentData().toString(); if ( rule.value == " ") // empty value rule.value = QString(); } } else qCritical(runtime) << "Unexpected empty Stack - null pointer"; } else qWarning() << "Unknown object name" << objectName; } filter.addRule(rule); } if ( !QSOFilterManager::instance()->save(filter) ) { QMessageBox::critical(nullptr, QMessageBox::tr("QLog Error"), QMessageBox::tr("Cannot update QSO Filter Conditions")); return; } accept(); } void QSOFilterDetail::filterNameChanged(const QString &newFilterName) { FCT_IDENTIFICATION; QPalette p; p.setColor(QPalette::Text, (filterExists(newFilterName)) ? Qt::red : qApp->palette().text().color()); ui->filterLineEdit->setPalette(p); } ================================================ FILE: ui/QSOFilterDetail.h ================================================ #ifndef QLOG_UI_QSOFILTERDETAIL_H #define QLOG_UI_QSOFILTERDETAIL_H #include #include #include #include #include #include "core/LogLocale.h" namespace Ui { class QSOFilterDetail; } class QSOFilterDetail : public QDialog { Q_OBJECT public: explicit QSOFilterDetail(const QString &filterName = QString(), QWidget *parent = nullptr); ~QSOFilterDetail(); public slots: void addCondition(int fieldIdx = -1, int operatorId = -1, QString value = QString()); void save(); void filterNameChanged(const QString&); private: Ui::QSOFilterDetail *ui; QString filterName; int condCount; QStringList filterNamesList; private: void loadFilter(const QString &filterName); bool filterExists(const QString &filterName); bool isDateField(int index); bool isDateTimeField(int index); bool isQSLSentField(int index); bool isQSLSentViaField(int index); bool isQSLRcvdField(int index); bool isUploadStatusField(int index); bool isAntPathField(int index); bool isBoolField(int index); bool isQSOCompleteField(int index); bool isDownloadStatusField(int index); bool isMorseKeyTypeField(int index); bool isEqslAgTypeField(int index); QComboBox* createComboBox(const QMap&, const QString&, const int identifier, const QSizePolicy&); QDateEdit* createDateEdit(const QString&, const int, const QSizePolicy&); QDateTimeEdit* createDateTimeEdit(const QString&, const int, const QSizePolicy&); QLineEdit* createLineEdit(const QString&, const int, const QSizePolicy&); LogLocale locale; }; #endif // QLOG_UI_QSOFILTERDETAIL_H ================================================ FILE: ui/QSOFilterDetail.ui ================================================ QSOFilterDetail 0 0 648 173 QSO Filter Detail Filter Name: Find QSO which match 0 0 All the following conditions Any of the following conditions Qt::Horizontal QSizePolicy::Preferred 40 20 Qt::Vertical QSizePolicy::Preferred 10 20 0 0 Add Condition Qt::Horizontal 40 20 Qt::Vertical 20 40 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() QSOFilterDetail save() 248 254 157 274 buttonBox rejected() QSOFilterDetail reject() 316 260 286 274 addConditionButton clicked() QSOFilterDetail addCondition() 80 148 340 174 filterLineEdit textChanged(QString) QSOFilterDetail filterNameChanged(QString) 542 29 459 165 addCondition() save() filterNameChanged(QString) ================================================ FILE: ui/QSOFilterDialog.cpp ================================================ #include #include #include #include #include "QSOFilterDialog.h" #include "ui_QSOFilterDialog.h" #include "core/debug.h" #include "ui/QSOFilterDetail.h" MODULE_IDENTIFICATION("qlog.ui.qsofilterdialog"); QSOFilterDialog::QSOFilterDialog(QWidget *parent) : QDialog(parent), ui(new Ui::QSOFilterDialog) { FCT_IDENTIFICATION; ui->setupUi(this); filterModel = new QSqlTableModel(ui->filtersListView); filterModel->setTable("qso_filters"); ui->filtersListView->setModel(filterModel); ui->filtersListView->setModelColumn(filterModel->fieldIndex("filter_name")); ui->filtersListView->setSelectionMode(QAbstractItemView::SingleSelection); filterModel->select(); } QSOFilterDialog::~QSOFilterDialog() { FCT_IDENTIFICATION; delete ui; } void QSOFilterDialog::addFilter() { FCT_IDENTIFICATION; QSOFilterDetail dialog(QString(), this); dialog.exec(); filterModel->select(); } void QSOFilterDialog::removeFilter() { FCT_IDENTIFICATION; filterModel->removeRow(ui->filtersListView->currentIndex().row()); ui->filtersListView->clearSelection(); filterModel->select(); } void QSOFilterDialog::editFilter(QModelIndex idx) { FCT_IDENTIFICATION; QString filterName = ui->filtersListView->model()->data(idx).toString(); QSOFilterDetail dialog(filterName, this); dialog.exec(); } void QSOFilterDialog::editFilterButton() { FCT_IDENTIFICATION; const QModelIndexList &list = ui->filtersListView->selectionModel()->selectedIndexes(); if ( !list.empty() ) editFilter(list.first()); } ================================================ FILE: ui/QSOFilterDialog.h ================================================ #ifndef QLOG_UI_QSOFILTERDIALOG_H #define QLOG_UI_QSOFILTERDIALOG_H #include #include namespace Ui { class QSOFilterDialog; } class QSOFilterDialog : public QDialog { Q_OBJECT public: explicit QSOFilterDialog(QWidget *parent = nullptr); ~QSOFilterDialog(); private: Ui::QSOFilterDialog *ui; QSqlTableModel* filterModel; public slots: void addFilter(); void removeFilter(); void editFilter(QModelIndex); void editFilterButton(); }; #endif // QLOG_UI_QSOFILTERDIALOG_H ================================================ FILE: ui/QSOFilterDialog.ui ================================================ QSOFilterDialog 0 0 386 290 QSO Filters Filters QAbstractItemView::NoEditTriggers Qt::Vertical 20 40 Add Edit Remove Qt::Vertical 20 40 Qt::Horizontal QDialogButtonBox::Ok buttonBox accepted() QSOFilterDialog accept() 248 254 157 274 buttonBox rejected() QSOFilterDialog reject() 316 260 286 274 addFilterButton clicked() QSOFilterDialog addFilter() 485 29 269 208 removeFilterButton clicked() QSOFilterDialog removeFilter() 485 70 269 208 filtersListView doubleClicked(QModelIndex) QSOFilterDialog editFilter(QModelIndex) 224 202 269 208 editFilterButton clicked() QSOFilterDialog editFilterButton() 485 188 269 208 addFilter() removeFilter() editFilter(QModelIndex) editFilterButton() ================================================ FILE: ui/QTableQSOView.cpp ================================================ #include #include #include "QTableQSOView.h" #include "models/LogbookModel.h" QTableQSOView::QTableQSOView(QWidget *parent) : QTableView(parent) { } void QTableQSOView::commitData(QWidget *editor) { QTableView::commitData(editor); QAbstractItemModel *model = this->model(); QVariant value = model->data(this->currentIndex(), Qt::EditRole); int currRow = this->currentIndex().row(); int currCol = this->currentIndex().column(); /* Group Editing Support */ /* If rows are selected then update them*/ const QModelIndexList &selectedRows = this->selectionModel()->selectedRows(); for ( const QModelIndex &index : selectedRows ) { if ( index.row() != currRow // Do not update the same row again /* Protect selected columns against group editing */ && currCol != LogbookModel::COLUMN_CALL && currCol != LogbookModel::COLUMN_TIME_ON && currCol != LogbookModel::COLUMN_TIME_OFF ) { model->setData(model->index(index.row(),currCol), value, Qt::EditRole); } } emit dataCommitted(); } void QTableQSOView::keyPressEvent(QKeyEvent *event) { if ( event->key() == Qt::Key_F2 ) { return; } QTableView::keyPressEvent(event); }; ================================================ FILE: ui/QTableQSOView.h ================================================ #ifndef QLOG_UI_QTABLEQSOVIEW_H #define QLOG_UI_QTABLEQSOVIEW_H #include #include class QTableQSOView : public QTableView { Q_OBJECT signals: void dataCommitted(); public: explicit QTableQSOView(QWidget *parent = nullptr); void commitData(QWidget *editor) override; void keyPressEvent(QKeyEvent *event) override; }; #endif // QLOG_UI_QTABLEQSOVIEW_H ================================================ FILE: ui/RigWidget.cpp ================================================ #include "RigWidget.h" #include "ui_RigWidget.h" #include "rig/macros.h" #include "core/debug.h" #include "data/Data.h" #include "service/hrdlog/HRDLog.h" #include "data/BandPlan.h" MODULE_IDENTIFICATION("qlog.ui.rigwidget"); RigWidget::RigWidget(QWidget *parent) : QWidget(parent), lastSeenFreq(0.0), rigOnline(false), splitEnabled(false), ui(new Ui::RigWidget), hrdlog(new HRDLogUploader(this)) { FCT_IDENTIFICATION; ui->setupUi(this); ui->freqLabel->setSelectionModeEnabled(false); ui->freqLabel->setDebounceEnabled(true); ui->freqLabel->setDebounceIntervalMs(250); ui->freqLabel->setLocale(QLocale::c()); connect(ui->freqLabel, &FreqQSpinBox::debouncedValueChanged, this, &RigWidget::freqChanged); ui->txFreqLabel->setVisible(false); ui->txFreqLabel->setSelectionModeEnabled(false); ui->txFreqLabel->setDebounceEnabled(true); ui->txFreqLabel->setDebounceIntervalMs(250); ui->txFreqLabel->setLocale(QLocale::c()); connect(ui->txFreqLabel, &FreqQSpinBox::debouncedValueChanged, this, &RigWidget::txFreqChanged); ui->splitOffButton->setVisible(false); connect(ui->splitOffButton, &QToolButton::clicked, this, []() { Rig::instance()->setSplit(false); }); QStringListModel* rigModel = new QStringListModel(this); ui->rigProfilCombo->setModel(rigModel); ui->rigProfilCombo->setStyleSheet("QComboBox {color: red}"); QSqlTableModel* bandComboModel = new QSqlTableModel(this); bandComboModel->setTable("bands"); bandComboModel->setSort(bandComboModel->fieldIndex("start_freq"), Qt::AscendingOrder); ui->bandComboBox->setModel(bandComboModel); ui->bandComboBox->setModelColumn(bandComboModel->fieldIndex("name")); bandComboModel->select(); QStringListModel* modesModel = new QStringListModel(this); ui->modeComboBox->setModel(modesModel); refreshRigProfileCombo(); QTimer *onAirTimer = new QTimer(this); connect(onAirTimer, &QTimer::timeout, this, &RigWidget::sendOnAirState); onAirTimer->start(ONAIR_INTERVAL * 1000); resetRigInfo(); rigDisconnected(); } RigWidget::~RigWidget() { FCT_IDENTIFICATION; hrdlog->deleteLater(); delete ui; } void RigWidget::updateFrequency(VFOID vfoid, double vfoFreq, double ritFreq, double xitFreq) { FCT_IDENTIFICATION; qCDebug(function_parameters) << vfoid << vfoFreq << ritFreq << xitFreq; if ( vfoid == VFO2 ) { // TX VFO frequency update (only when split is active) ui->txFreqLabel->blockSignals(true); ui->txFreqLabel->setValue(vfoFreq); ui->txFreqLabel->blockSignals(false); ui->txFreqLabel->setReadOnly(true); return; } // VFO1 — RX frequency ui->freqLabel->blockSignals(true); ui->freqLabel->setValue(vfoFreq); ui->freqLabel->blockSignals(false); const QString &bandName = BandPlan::freq2Band(vfoFreq).name; if ( bandName != ui->bandComboBox->currentText() ) { ui->bandComboBox->blockSignals(true); saveLastSeenFreq(); ui->bandComboBox->setCurrentText(bandName); ui->bandComboBox->blockSignals(false); } lastSeenFreq = vfoFreq; } void RigWidget::updateSplit(VFOID, bool enabled) { FCT_IDENTIFICATION; qCDebug(function_parameters) << enabled; splitEnabled = enabled; ui->txFreqLabel->setVisible(enabled); ui->splitOffButton->setVisible(enabled); } void RigWidget::updateMode(VFOID vfoid, const QString &rawMode, const QString &mode, const QString &submode, qint32) { FCT_IDENTIFICATION; qCDebug(function_parameters)<modeLabel->setText(rawMode); if ( mode != ui->modeComboBox->currentText() ) { ui->modeComboBox->blockSignals(true); ui->modeComboBox->setCurrentText(rawMode); ui->modeComboBox->blockSignals(false); } lastSeenMode = mode; } void RigWidget::updatePWR(VFOID vfoid, double pwr) { FCT_IDENTIFICATION; qCDebug(function_parameters)<pwrLabel->setText(QString(tr("PWR: %1W")).arg(pwr)); } void RigWidget::updateVFO(VFOID vfoid, const QString &vfo) { FCT_IDENTIFICATION; qCDebug(function_parameters)<vfoLabel->setText(vfo); } void RigWidget::updateXIT(VFOID, double xit) { FCT_IDENTIFICATION; if ( xit != 0.0 ) { ui->xitOffset->setVisible(true); QString unit; unsigned char decP; double xitDisplay = Data::MHz2UserFriendlyFreq(xit, unit, decP); ui->xitOffset->setText(QString("XIT: %1 %2").arg(QString::number(xitDisplay, 'f', decP), unit)); } else { ui->xitOffset->setVisible(false); } } void RigWidget::updateRIT(VFOID, double rit) { FCT_IDENTIFICATION; if ( rit != 0.0 ) { ui->ritOffset->setVisible(true); QString unit; unsigned char decP; double ritDisplay = Data::MHz2UserFriendlyFreq(rit, unit, decP); ui->ritOffset->setText(QString("RIT: %1 %2").arg(QString::number(ritDisplay, 'f', decP), unit)); } else { ui->ritOffset->setVisible(false); } } void RigWidget::updatePTT(VFOID, bool ptt) { FCT_IDENTIFICATION; ui->pttLabel->setText(ptt ? "TX" : "RX"); if ( ptt ) ui->pttLabel->setStyleSheet("QLabel { font-weight: bold; color: white; border-radius: 8px; background-color: red; padding: 2px 4px; }"); else ui->pttLabel->setStyleSheet("QLabel { font-weight: bold; padding: 2px 4px; }"); } void RigWidget::bandComboChanged(const QString &newBand) { FCT_IDENTIFICATION; qCDebug(runtime) << "new Band:" << newBand; saveLastSeenFreq(); QSqlTableModel* bandComboModel = dynamic_cast(ui->bandComboBox->model()); QSqlRecord record = bandComboModel->record(ui->bandComboBox->currentIndex()); double newFreq = record.value("start_freq").toDouble(); if ( ! record.value("last_seen_freq").toString().isEmpty() ) { newFreq = record.value("last_seen_freq").toDouble(); } qCDebug(runtime) << "Tunning freq: " << newFreq; Rig::instance()->setFrequency(MHz(newFreq)); } void RigWidget::modeComboChanged(const QString &newMode) { FCT_IDENTIFICATION; Rig::instance()->setRawMode(newMode); } void RigWidget::rigProfileComboChanged(const QString &profileName) { FCT_IDENTIFICATION; qCDebug(function_parameters) << profileName; RigProfilesManager::instance()->setCurProfile1(profileName); refreshBandCombo(); refreshModeCombo(); resetRigInfo(); ui->pttLabel->setVisible(rigOnline && RigProfilesManager::instance()->getCurProfile1().getPTTInfo); emit rigProfileChanged(); } void RigWidget::refreshRigProfileCombo() { ui->rigProfilCombo->blockSignals(true); RigProfilesManager *rigManager = RigProfilesManager::instance(); QStringList currProfiles = rigManager->profileNameList(); QStringListModel* model = dynamic_cast(ui->rigProfilCombo->model()); model->setStringList(currProfiles); if ( rigManager->getCurProfile1().profileName.isEmpty() && currProfiles.count() > 0 ) { /* changing profile from empty to something */ ui->rigProfilCombo->setCurrentText(currProfiles.first()); rigProfileComboChanged(currProfiles.first()); } else { /* no profile change, just refresh the combo and preserve current profile */ ui->rigProfilCombo->setCurrentText(rigManager->getCurProfile1().profileName); } updateRIT(VFO1, rigManager->getCurProfile1().ritOffset); updateXIT(VFO1, rigManager->getCurProfile1().xitOffset); ui->pttLabel->setVisible(rigOnline && RigProfilesManager::instance()->getCurProfile1().getPTTInfo); ui->rigProfilCombo->blockSignals(false); refreshBandCombo(); refreshModeCombo(); } void RigWidget::refreshBandCombo() { FCT_IDENTIFICATION; QString currSelection = ui->bandComboBox->currentText(); const RigProfile &profile = RigProfilesManager::instance()->getCurProfile1(); ui->bandComboBox->blockSignals(true); QSqlTableModel *bandComboModel = dynamic_cast(ui->bandComboBox->model()); bandComboModel->setFilter(QString("enabled = 1 AND start_freq >= %1 AND end_freq <= %2").arg(profile.txFreqStart + profile.xitOffset) .arg(profile.txFreqEnd + profile.xitOffset)); bandComboModel->select(); ui->bandComboBox->setCurrentText(currSelection); ui->bandComboBox->blockSignals(false); } void RigWidget::refreshModeCombo() { FCT_IDENTIFICATION; QString currSelection = ui->modeComboBox->currentText(); ui->modeComboBox->blockSignals(true); QStringListModel* model = dynamic_cast(ui->modeComboBox->model()); model->setStringList(Rig::instance()->getAvailableRawModes()); ui->modeComboBox->setCurrentText(currSelection); ui->modeComboBox->blockSignals(false); } void RigWidget::reloadSettings() { FCT_IDENTIFICATION; refreshRigProfileCombo(); } void RigWidget::rigConnected() { FCT_IDENTIFICATION; ui->rigProfilCombo->setStyleSheet("QComboBox {color: green}"); rigOnline = true; ui->bandComboBox->blockSignals(true); ui->modeComboBox->blockSignals(true); ui->bandComboBox->setEnabled(true); ui->modeComboBox->setEnabled(true); ui->modeComboBox->blockSignals(false); ui->bandComboBox->blockSignals(false); ui->freqLabel->setReadOnly(false); ui->txFreqLabel->setReadOnly(false); ui->pttLabel->setVisible(RigProfilesManager::instance()->getCurProfile1().getPTTInfo); refreshModeCombo(); } void RigWidget::rigDisconnected() { FCT_IDENTIFICATION; ui->bandComboBox->blockSignals(true); ui->modeComboBox->blockSignals(true); saveLastSeenFreq(); ui->rigProfilCombo->setStyleSheet("QComboBox {color: red}"); rigOnline = false; resetRigInfo(); ui->bandComboBox->setEnabled(false); ui->modeComboBox->setEnabled(false); ui->modeComboBox->blockSignals(false); ui->bandComboBox->blockSignals(false); ui->freqLabel->setReadOnly(true); ui->txFreqLabel->setReadOnly(true); ui->pttLabel->setVisible(false); } void RigWidget::bandUp() { FCT_IDENTIFICATION; if ( !rigOnline ) return; int currentIndex = ui->bandComboBox->currentIndex(); if ( currentIndex < ui->bandComboBox->count() - 1) ui->bandComboBox->setCurrentIndex(currentIndex + 1); } void RigWidget::bandDown() { FCT_IDENTIFICATION; if ( !rigOnline ) return; int currentIndex = ui->bandComboBox->currentIndex(); if ( currentIndex > 0 ) ui->bandComboBox->setCurrentIndex(currentIndex - 1); } void RigWidget::setBand(const QString &band) { FCT_IDENTIFICATION; qCDebug(function_parameters) << band; if ( !rigOnline ) return; ui->bandComboBox->setCurrentText(band); } void RigWidget::sendOnAirState() { FCT_IDENTIFICATION; if ( rigOnline && HRDLogBase::getOnAirEnabled() ) { hrdlog->sendOnAir(lastSeenFreq, lastSeenMode); } } void RigWidget::freqChanged(double) { FCT_IDENTIFICATION; if ( !rigOnline ) return; Rig::instance()->setFrequency(MHz(ui->freqLabel->value())); } void RigWidget::txFreqChanged(double) { FCT_IDENTIFICATION; if ( !rigOnline || !splitEnabled ) return; Rig::instance()->setFrequency(VFO2, MHz(ui->txFreqLabel->value())); } void RigWidget::resetRigInfo() { QString empty; updateMode(VFO1, empty, empty, empty, 0); ui->pwrLabel->setText(QString("")); updateVFO(VFO1, empty); updateFrequency(VFO1, 0, 0, 0); updateSplit(VFO1, false); updateRIT(VFO1, RigProfilesManager::instance()->getCurProfile1().ritOffset); updateXIT(VFO1, RigProfilesManager::instance()->getCurProfile1().xitOffset); updatePTT(VFO1, false); } void RigWidget::saveLastSeenFreq() { FCT_IDENTIFICATION; if ( rigOnline && lastSeenFreq != 0.0 ) { qCDebug(runtime) << "Last Seen Freq" << lastSeenFreq; QSqlTableModel *bandComboModel = dynamic_cast(ui->bandComboBox->model()); QModelIndexList bandIndex = bandComboModel->match(bandComboModel->index(0,bandComboModel->fieldIndex("name")), Qt::DisplayRole, BandPlan::freq2Band(lastSeenFreq).name,1, Qt::MatchExactly); if ( bandIndex.size() > 0 ) { bandComboModel->setData(bandComboModel->index(bandIndex.at(0).row(), bandComboModel->fieldIndex("last_seen_freq")), lastSeenFreq); bandComboModel->submitAll(); } } } ================================================ FILE: ui/RigWidget.h ================================================ #ifndef QLOG_UI_RIGWIDGET_H #define QLOG_UI_RIGWIDGET_H #include #include "rig/Rig.h" #include "service/hrdlog/HRDLog.h" namespace Ui { class RigWidget; } class RigWidget : public QWidget { Q_OBJECT public: explicit RigWidget(QWidget *parent = nullptr); ~RigWidget(); signals: void rigProfileChanged(); public slots: void updateFrequency(VFOID, double, double, double); void updateSplit(VFOID, bool); void updateMode(VFOID, const QString&, const QString&, const QString&, qint32); void updatePWR(VFOID, double); void updateVFO(VFOID, const QString&); void updateXIT(VFOID, double); void updateRIT(VFOID, double); void updatePTT(VFOID, bool); void bandComboChanged(const QString&); void modeComboChanged(const QString&); void rigProfileComboChanged(const QString&); void refreshRigProfileCombo(); void refreshBandCombo(); void refreshModeCombo(); void reloadSettings(); void rigConnected(); void rigDisconnected(); void bandUp(); void bandDown(); void setBand(const QString &band); private slots: void sendOnAirState(); void freqChanged(double); void txFreqChanged(double); private: // On AIR pinging to HRDLog [in sec] const int ONAIR_INTERVAL = 60; void resetRigInfo(); void saveLastSeenFreq(); double lastSeenFreq; QString lastSeenMode; bool rigOnline; bool splitEnabled; Ui::RigWidget *ui; HRDLogUploader *hrdlog; }; #endif // QLOG_UI_RIGWIDGET_H ================================================ FILE: ui/RigWidget.ui ================================================ RigWidget 0 0 354 112 Form 2 0 0 0 0 0 0 0 Qt::ClickFocus QComboBox::AdjustToContents Qt::ClickFocus QComboBox::AdjustToContents Qt::ClickFocus QComboBox::AdjustToContents Qt::Horizontal 40 20 30 16777215 RX Qt::AlignCenter 16777215 16777215 60 16777215 Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 60 16777215 Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 Qt::Horizontal 40 20 0 0 20 Qt::NoFocus QDoubleSpinBox { background: transparent; border: none; padding: 0px; } QAbstractSpinBox::lineEdit { background: transparent; border: none; padding: 0px; margin: 0px; } false Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter true QAbstractSpinBox::NoButtons Disconnected false MHz 5 7500000.000000000000000 0.001000000000000 0 0 12 Qt::NoFocus QDoubleSpinBox { background: transparent; border: none; padding: 0px; color: #ef6c00; } QAbstractSpinBox::lineEdit { background: transparent; border: none; padding: 0px; margin: 0px; color: #ef6c00; } false Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter true QAbstractSpinBox::NoButtons false MHz 5 7500000.000000000000000 0.001000000000000 Qt::NoFocus Disable Split 12 12 true 0 8 RIT: 0.00000 MHz 8 XIT: 0.00000 MHz Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::Vertical 20 40 FreqQSpinBox QDoubleSpinBox
ui/component/FreqQSpinBox.h
rigProfilCombo currentTextChanged(QString) RigWidget rigProfileComboChanged(QString) 70 28 102 54 bandComboBox currentTextChanged(QString) RigWidget bandComboChanged(QString) 178 28 209 135 modeComboBox currentTextChanged(QString) RigWidget modeComboChanged(QString) 270 28 101 86 rigProfileComboChanged(QString) bandComboChanged(QString) modeComboChanged(QString) freqChanged(double)
================================================ FILE: ui/RigctldAdvancedDialog.cpp ================================================ #include #include #include #include "RigctldAdvancedDialog.h" #include "ui_RigctldAdvancedDialog.h" #include "rig/RigctldManager.h" #include "core/debug.h" MODULE_IDENTIFICATION("qlog.ui.rigctldadvanceddialog"); RigctldAdvancedDialog::RigctldAdvancedDialog(QWidget *parent) : QDialog(parent), ui(new Ui::RigctldAdvancedDialog), m_versionUpdateTimer(new QTimer(this)) { FCT_IDENTIFICATION; ui->setupUi(this); #ifdef QLOG_FLATPAK ui->pathEdit->setEnabled(false); ui->browseButton->setEnabled(false); ui->autoButton->setEnabled(false); ui->pathEdit->setPlaceholderText(tr("Cannot be changed")); #endif m_versionUpdateTimer->setSingleShot(true); m_versionUpdateTimer->setInterval(500); connect(m_versionUpdateTimer, &QTimer::timeout, this, &RigctldAdvancedDialog::updateVersionLabel); connect(ui->pathEdit, &QLineEdit::textChanged, this, [this]() { m_versionUpdateTimer->start(); }); updateVersionLabel(); } RigctldAdvancedDialog::~RigctldAdvancedDialog() { FCT_IDENTIFICATION; delete ui; } void RigctldAdvancedDialog::setPath(const QString &path) { ui->pathEdit->blockSignals(true); ui->pathEdit->setText(path); ui->pathEdit->blockSignals(false); updateVersionLabel(); } QString RigctldAdvancedDialog::getPath() const { return ui->pathEdit->text().trimmed(); } void RigctldAdvancedDialog::setArgs(const QString &args) { ui->argsEdit->setText(args); } QString RigctldAdvancedDialog::getArgs() const { return ui->argsEdit->text().trimmed(); } void RigctldAdvancedDialog::autoDetectPath() { FCT_IDENTIFICATION; const QString detectedPath = RigctldManager::findRigctldPath(); if ( detectedPath.isEmpty() ) { updateVersionLabel(); QMessageBox::warning(this, tr("Auto Detect"), tr("rigctld was not found on this system.\n" "Please install Hamlib or specify the path manually.")); } else setPath(detectedPath); } void RigctldAdvancedDialog::browsePath() { FCT_IDENTIFICATION; #ifdef Q_OS_WIN const QString filter = tr("Executable (*.exe);;All files (*.*)"); #else const QString filter = tr("All files (*)"); #endif const QString folder = (ui->pathEdit->text().isEmpty() ) ? QDir::rootPath() : ui->pathEdit->text(); const QString path = QFileDialog::getOpenFileName(this, tr("Select rigctld executable"), folder, filter); if ( !path.isEmpty() ) setPath(path); } void RigctldAdvancedDialog::updateVersionLabel() { FCT_IDENTIFICATION; const QString path = ui->pathEdit->text().trimmed(); const bool isAutoDetect = path.isEmpty(); // When autodetect, resolve the path to show it to the user const QString resolvedPath = isAutoDetect ? RigctldManager::findRigctldPath() : path; const QString NOTFOUND = QString("✗ %1").arg(tr("Not found")); if ( resolvedPath.isEmpty() ) { ui->versionValueLabel->setText(NOTFOUND); return; } const RigctldVersion version = RigctldManager::getVersion(resolvedPath); if ( !version.isValid() ) { ui->versionValueLabel->setText(NOTFOUND); return; } const QString versionStr = QString("✓ %1.%2.%3") .arg(version.major) .arg(version.minor) .arg(version.patch); if ( isAutoDetect ) ui->versionValueLabel->setText(QString("%1 (%2)").arg(versionStr, resolvedPath)); else ui->versionValueLabel->setText(versionStr); } ================================================ FILE: ui/RigctldAdvancedDialog.h ================================================ #ifndef QLOG_UI_RIGCTLDADVANCEDDIALOG_H #define QLOG_UI_RIGCTLDADVANCEDDIALOG_H #include #include namespace Ui { class RigctldAdvancedDialog; } class RigctldAdvancedDialog : public QDialog { Q_OBJECT public: explicit RigctldAdvancedDialog(QWidget *parent = nullptr); ~RigctldAdvancedDialog(); void setPath(const QString &path); QString getPath() const; void setArgs(const QString &args); QString getArgs() const; private slots: void autoDetectPath(); void browsePath(); void updateVersionLabel(); private: Ui::RigctldAdvancedDialog *ui; QTimer *m_versionUpdateTimer; }; #endif // QLOG_UI_RIGCTLDADVANCEDDIALOG_H ================================================ FILE: ui/RigctldAdvancedDialog.ui ================================================ RigctldAdvancedDialog 0 0 631 149 Rigctld Advanced Settings Rigctld Path: Leave empty for auto-detection true 0 0 Browse .. 0 0 Auto-detect Rigctld path Auto-Detect Rigctld Version: Additional Arguments: e.g. -v -v for verbose logging true Qt::Vertical 20 10 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() RigctldAdvancedDialog accept() 248 254 157 274 buttonBox rejected() RigctldAdvancedDialog reject() 316 260 286 274 autoButton clicked() RigctldAdvancedDialog autoDetectPath() 474 24 273 61 browseButton clicked() RigctldAdvancedDialog browsePath() 520 24 273 61 autoDetectPath() browsePath() ================================================ FILE: ui/RotatorWidget.cpp ================================================ #include #include #include #include #include "rotator/Rotator.h" #include "rotator/Rotator.h" #include "RotatorWidget.h" #include "ui_RotatorWidget.h" #include "core/debug.h" #include "data/Gridsquare.h" #include "data/StationProfile.h" #include "data/RotUsrButtonsProfile.h" MODULE_IDENTIFICATION("qlog.ui.rotatorwidget"); RotatorWidget::RotatorWidget(QWidget *parent) : QWidget(parent), antennaNeedle(nullptr), requestedAzimuthNeedle(nullptr), QSOAzimuthNeedle(nullptr), waitingFirstValue(true), compassScene(nullptr), ui(new Ui::RotatorWidget), antennaAzimuth(0.0), requestedAzimuth(qQNaN()), qsoAzimuth(qQNaN()), contact(nullptr) { FCT_IDENTIFICATION; ui->setupUi(this); redrawMap(); QStringListModel* rotModel = new QStringListModel(this); ui->rotProfileCombo->setModel(rotModel); ui->rotProfileCombo->setStyleSheet("QComboBox {color: red}"); refreshRotProfileCombo(); QStringListModel* userButtonModel = new QStringListModel(this); ui->userButtonsProfileCombo->setModel(userButtonModel); refreshRotUserButtonProfileCombo(); QMenu *qsoBearingMenu = new QMenu(this); qsoBearingMenu->addAction(ui->actionQSO_SP); qsoBearingMenu->addAction(ui->actionQSO_LP); ui->qsoBearingButton->setMenu(qsoBearingMenu); ui->qsoBearingButton->setDefaultAction(ui->actionQSO_SP); rotDisconnected(); } void RotatorWidget::gotoPosition() { FCT_IDENTIFICATION; setBearing(ui->gotoDoubleSpinBox->value()); } double RotatorWidget::getQSOBearing() { FCT_IDENTIFICATION; double qsoBearing = (contact) ? contact->getQSOBearing() : qQNaN(); qCDebug(runtime) << "QSO Bearing:" << qsoBearing; return qsoBearing; } void RotatorWidget::shortcutComboMove(int step) { FCT_IDENTIFICATION; qCDebug(function_parameters) << step; int count = ui->userButtonsProfileCombo->count(); int currIndex = ui->userButtonsProfileCombo->currentIndex(); if ( count > 0 && currIndex != -1 && step < count ) { int nextIndex = currIndex + step; nextIndex += (1 - nextIndex / count) * count; ui->userButtonsProfileCombo->setCurrentIndex(nextIndex % count); } } void RotatorWidget::qsoBearingLP() { FCT_IDENTIFICATION; ui->qsoBearingButton->setDefaultAction(ui->actionQSO_LP); double qsoBearing = getQSOBearing(); if ( qIsNaN(qsoBearing) ) return; qsoBearing -= 180; if ( qsoBearing < 0 ) qsoBearing += 360; setBearing(qsoBearing); } void RotatorWidget::qsoBearingSP() { FCT_IDENTIFICATION; ui->qsoBearingButton->setDefaultAction(ui->actionQSO_SP); setBearing(getQSOBearing()); } void RotatorWidget::setRequestedAz(double requestedAz) { FCT_IDENTIFICATION; qCDebug(function_parameters) << requestedAz; if ( qIsNaN(requestedAz) ) return; requestedAzimuth = requestedAz; if ( requestedAzimuthNeedle ) { requestedAzimuthNeedle->show(); requestedAzimuthNeedle->setRotation(requestedAz); } } void RotatorWidget::setBearing(double in_azimuth) { FCT_IDENTIFICATION; qCDebug(function_parameters) << in_azimuth; if ( qIsNaN(in_azimuth) || !ui->gotoDoubleSpinBox->isEnabled() ) return; setRequestedAz(in_azimuth); Rotator::instance()->setPosition(in_azimuth, 0); } void RotatorWidget::positionChanged(double in_azimuth, double in_elevation) { FCT_IDENTIFICATION; qCDebug(function_parameters) << in_azimuth <setRotation(antennaAzimuth); if ( waitingFirstValue ) { waitingFirstValue = false; if ( requestedAzimuthNeedle) requestedAzimuthNeedle->setRotation(in_azimuth); } ui->gotoDoubleSpinBox->blockSignals(true); ui->gotoDoubleSpinBox->setValue(antennaAzimuth); ui->gotoDoubleSpinBox->blockSignals(false); if ( qIsNaN(requestedAzimuth) || !qIsFinite(requestedAzimuth) ) { qWarning() << "Invalid value in RotatorWidget::positionChanged:" << requestedAzimuth; return; } if (qIsNaN(in_azimuth) || !qIsFinite(in_azimuth)) { qWarning() << "Invalid value in RotatorWidget::positionChanged:" << in_azimuth; return; } if ( qAbs(qRound(requestedAzimuth) - qRound(in_azimuth)) <= AZIMUTH_DEAD_BAND && requestedAzimuthNeedle ) requestedAzimuthNeedle->hide(); } void RotatorWidget::setQSOBearing(double , double ) { FCT_IDENTIFICATION; if ( !QSOAzimuthNeedle ) return; qsoAzimuth = getQSOBearing(); qCDebug(runtime) << qsoAzimuth; if ( !qIsNaN(qsoAzimuth) ) { QSOAzimuthNeedle->show(); QSOAzimuthNeedle->setRotation(qsoAzimuth); } else QSOAzimuthNeedle->hide(); } void RotatorWidget::shortcutProfileIncrease() { FCT_IDENTIFICATION; shortcutComboMove(1); } void RotatorWidget::shortcutProfileDecrease() { FCT_IDENTIFICATION; shortcutComboMove(-1); } void RotatorWidget::showEvent(QShowEvent* event) { FCT_IDENTIFICATION; ui->compassView->fitInView(compassScene->sceneRect(), Qt::KeepAspectRatio); QWidget::showEvent(event); } void RotatorWidget::resizeEvent(QResizeEvent* event) { FCT_IDENTIFICATION; ui->compassView->fitInView(compassScene->sceneRect(), Qt::KeepAspectRatio); QWidget::resizeEvent(event); } void RotatorWidget::mousePressEvent(QMouseEvent *event) { FCT_IDENTIFICATION; if( event->button() == Qt::LeftButton ) { QPointF clickPos = ui->compassView->mapToScene(ui->compassView->mapFromGlobal( #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) event->globalPosition().toPoint() #else event->globalPos() #endif )); qreal dx = clickPos.x(); qreal dy = -1 * clickPos.y(); if ( qSqrt(qPow(dx, 2) + qPow(dy, 2)) <= GLOBE_RADIUS ) // distance between click and center of the globe { double angle = qRadiansToDegrees(qAtan2(dx, dy)); if ( angle < 0 ) angle += 360; setBearing(std::round(angle)); } } QWidget::mousePressEvent(event); } void RotatorWidget::userButton1() { FCT_IDENTIFICATION; double bearing = RotUsrButtonsProfilesManager::instance()->getCurProfile1().bearings[0]; if ( bearing >= 0 ) setBearing(bearing); } void RotatorWidget::userButton2() { FCT_IDENTIFICATION; double bearing = RotUsrButtonsProfilesManager::instance()->getCurProfile1().bearings[1]; if ( bearing >= 0 ) setBearing(bearing); } void RotatorWidget::userButton3() { FCT_IDENTIFICATION; double bearing = RotUsrButtonsProfilesManager::instance()->getCurProfile1().bearings[2]; if ( bearing >= 0 ) setBearing(bearing); } void RotatorWidget::userButton4() { FCT_IDENTIFICATION; double bearing = RotUsrButtonsProfilesManager::instance()->getCurProfile1().bearings[3]; if ( bearing >= 0 ) setBearing(bearing); } void RotatorWidget::refreshRotProfileCombo() { FCT_IDENTIFICATION; ui->rotProfileCombo->blockSignals(true); QStringList currProfiles = RotProfilesManager::instance()->profileNameList(); QStringListModel* model = dynamic_cast(ui->rotProfileCombo->model()); model->setStringList(currProfiles); if ( RotProfilesManager::instance()->getCurProfile1().profileName.isEmpty() && currProfiles.count() > 0 ) { /* changing profile from empty to something */ ui->rotProfileCombo->setCurrentText(currProfiles.first()); rotProfileComboChanged(currProfiles.first()); } else { /* no profile change, just refresh the combo and preserve current profile */ ui->rotProfileCombo->setCurrentText(RotProfilesManager::instance()->getCurProfile1().profileName); } ui->rotProfileCombo->blockSignals(false); } void RotatorWidget::refreshRotUserButtonProfileCombo() { FCT_IDENTIFICATION; ui->userButtonsProfileCombo->blockSignals(true); RotUsrButtonsProfilesManager *buttonManager = RotUsrButtonsProfilesManager::instance(); QStringList currProfiles = buttonManager->profileNameList(); QStringListModel* model = dynamic_cast(ui->userButtonsProfileCombo->model()); model->setStringList(currProfiles); if ( buttonManager->getCurProfile1().profileName.isEmpty() && currProfiles.count() > 0 ) { /* changing profile from empty to something */ ui->userButtonsProfileCombo->setCurrentText(currProfiles.first()); } else { /* no profile change, just refresh the combo and preserve current profile */ ui->userButtonsProfileCombo->setCurrentText(buttonManager->getCurProfile1().profileName); } rotUserButtonProfileComboChanged(ui->userButtonsProfileCombo->currentText()); ui->userButtonsProfileCombo->blockSignals(false); } void RotatorWidget::refreshRotUserButtons() { FCT_IDENTIFICATION; RotUsrButtonsProfile profile = RotUsrButtonsProfilesManager::instance()->getCurProfile1(); setUserButtonDesc(ui->userButton_1, profile.shortDescs[0], profile.bearings[0]); setUserButtonDesc(ui->userButton_2, profile.shortDescs[1], profile.bearings[1]); setUserButtonDesc(ui->userButton_3, profile.shortDescs[2], profile.bearings[2]); setUserButtonDesc(ui->userButton_4, profile.shortDescs[3], profile.bearings[3]); } void RotatorWidget::setUserButtonDesc(QPushButton *button, const QString &desc, const double value) { FCT_IDENTIFICATION; if ( value >= 0 ) { button->setText(desc); button->setEnabled(true); } else button->setText(""); } void RotatorWidget::redrawMap() { FCT_IDENTIFICATION; if ( !ui ) return; if ( compassScene ) compassScene->deleteLater(); compassScene = new QGraphicsScene(this); ui->compassView->setScene(compassScene); ui->compassView->setStyleSheet("background-color: transparent;"); QImage source(":/res/map/nasabluemarble.jpg"); QImage map(MAP_RESOLUTION, MAP_RESOLUTION, QImage::Format_ARGB32); const Gridsquare myGrid(StationProfilesManager::instance()->getCurProfile1().locator); double lat = myGrid.getLatitude(); double lon = myGrid.getLongitude(); if ( qIsNaN(lat) || qIsNaN(lon) ) return; // transform image to azimuthal map const int mapWidth = map.width(); const int mapHeight = map.height(); const int srcWidth = source.width(); const int srcHeight = source.height(); const double lambda0 = (lon / 180.0) * M_PI; const double phi1 = - (lat / 90.0) * (0.5 * M_PI); const double sinPhi1 = sin(phi1); const double cosPhi1 = cos(phi1); const double twoPI = 2.0 * M_PI; for (int x = 0; x < mapWidth; x++) { double x2 = twoPI * (static_cast(x) / static_cast(mapWidth) - 0.5); for (int y = 0; y < mapHeight; y++) { double y2 = twoPI * (static_cast(y) / static_cast(mapHeight) - 0.5); double c = sqrt(x2 * x2 + y2 * y2); if ( c < M_PI ) { double phi = phi1; double lambda = lambda0; if ( c != 0.0 ) { double sinC = sin(c); double cosC = cos(c); phi = asin(cosC * sinPhi1 + (y2 * sinC * cosPhi1) / c); lambda = lambda0 + atan2(x2 * sinC, c * cosPhi1 * cosC - y2 * sinPhi1 * sinC); } double s = (lambda / twoPI) + 0.5; double t = (phi / M_PI) + 0.5; int x3 = static_cast(s * srcWidth); int y3 = static_cast(t * srcHeight); x3 = (x3 % srcWidth + srcWidth) % srcWidth; y3 = (y3 % srcHeight + srcHeight) % srcHeight; map.setPixelColor(x, y, source.pixelColor(x3, y3)); } else map.setPixelColor(x, y, QColor(0, 0, 0, 0)); } } // draw azimuthal map QGraphicsPixmapItem *pixMapItem = compassScene->addPixmap(QPixmap::fromImage(map)); pixMapItem->moveBy(-MAP_RESOLUTION/2, -MAP_RESOLUTION/2); pixMapItem->setTransformOriginPoint(MAP_RESOLUTION/2, MAP_RESOLUTION/2); pixMapItem->setScale(GLOBE_RADIUS * 2/MAP_RESOLUTION); // circle around the globe - globe "antialiasing" compassScene->addEllipse(-100, -100, GLOBE_RADIUS * 2, GLOBE_RADIUS * 2, QPen(QColor(100, 100, 100)), QBrush(QColor(0, 0, 0), Qt::NoBrush)); // point in the middle of globe compassScene->addEllipse(-1, -1, 2, 2, QPen(Qt::NoPen), QBrush(QColor(0, 0, 0), Qt::SolidPattern)); // draw needles QPainterPath path; path.lineTo(-2, 0); path.lineTo(0, -90); path.lineTo(2, 0); path.closeSubpath(); QPainterPath path2; path2.lineTo(-1, 0); path2.lineTo(0, -90); path2.lineTo(1, 0); path2.closeSubpath(); requestedAzimuthNeedle = compassScene->addPath(QPainterPath(path2), QPen(Qt::NoPen), QBrush(QColor(255,255,255), Qt::SolidPattern)); if ( !qIsNaN(requestedAzimuth) ) requestedAzimuthNeedle->setRotation(requestedAzimuth); else requestedAzimuthNeedle->hide(); antennaNeedle = compassScene->addPath(path, QPen(QColor(0, 0, 0, 150)), QBrush(QColor(255, 191, 0), Qt::SolidPattern)); antennaNeedle->setRotation(antennaAzimuth); if ( !ui->gotoButton->isEnabled() ) antennaNeedle->hide(); QSOAzimuthNeedle = compassScene->addPath(QPainterPath(path2), QPen(Qt::NoPen), QBrush(QColor(255,0,255), Qt::SolidPattern)); setQSOBearing(qQNaN(), qQNaN()); // only call the function; input parameters are ignored } void RotatorWidget::rotProfileComboChanged(QString profileName) { FCT_IDENTIFICATION; qCDebug(function_parameters) << profileName; RotProfilesManager::instance()->setCurProfile1(profileName); emit rotProfileChanged(); } void RotatorWidget::rotUserButtonProfileComboChanged(QString profileName) { FCT_IDENTIFICATION; qCDebug(function_parameters) << profileName; RotUsrButtonsProfilesManager::instance()->setCurProfile1(profileName); refreshRotUserButtons(); emit rotUserButtonChanged(); } void RotatorWidget::reloadSettings() { FCT_IDENTIFICATION; refreshRotProfileCombo(); refreshRotUserButtonProfileCombo(); } void RotatorWidget::rotConnected() { FCT_IDENTIFICATION; ui->rotProfileCombo->setStyleSheet("QComboBox {color: green}"); ui->gotoDoubleSpinBox->setEnabled(true); ui->gotoButton->setEnabled(true); ui->qsoBearingButton->setEnabled(true); ui->userButtonsProfileCombo->setEnabled(true); ui->leftButton->setVisible(ui->userButtonsProfileCombo->count()>1); ui->rightButton->setVisible(ui->userButtonsProfileCombo->count()>1); refreshRotUserButtons(); waitingFirstValue = true; if ( antennaNeedle ) antennaNeedle->show(); } void RotatorWidget::rotDisconnected() { FCT_IDENTIFICATION; ui->rotProfileCombo->setStyleSheet("QComboBox {color: red}"); ui->gotoDoubleSpinBox->setEnabled(false); ui->gotoButton->setEnabled(false); ui->qsoBearingButton->setEnabled(false); ui->userButtonsProfileCombo->setEnabled(false); ui->userButton_1->setEnabled(false); ui->userButton_2->setEnabled(false); ui->userButton_3->setEnabled(false); ui->userButton_4->setEnabled(false); ui->leftButton->setVisible(false); ui->rightButton->setVisible(false); if ( antennaNeedle ) antennaNeedle->hide(); if ( requestedAzimuthNeedle) requestedAzimuthNeedle->hide(); requestedAzimuth = qQNaN(); } RotatorWidget::~RotatorWidget() { delete ui; } void RotatorWidget::registerContactWidget(const NewContactWidget *contactWidget) { FCT_IDENTIFICATION; contact = contactWidget; } ================================================ FILE: ui/RotatorWidget.h ================================================ #ifndef QLOG_UI_ROTATORWIDGET_H #define QLOG_UI_ROTATORWIDGET_H #include #include #include #include "ui/NewContactWidget.h" namespace Ui { class RotatorWidget; } class QGraphicsScene; class QGraphicsPathItem; class RotatorWidget : public QWidget { Q_OBJECT public: explicit RotatorWidget(QWidget *parent = nullptr); ~RotatorWidget(); void registerContactWidget(const NewContactWidget*); signals: void rotProfileChanged(); void rotUserButtonChanged(); public slots: void setBearing(double); void positionChanged(double, double); void redrawMap(); void rotProfileComboChanged(QString); void rotUserButtonProfileComboChanged(QString); void reloadSettings(); void rotConnected(); void rotDisconnected(); void refreshRotProfileCombo(); void setQSOBearing(double, double); void shortcutProfileIncrease(); void shortcutProfileDecrease(); protected: void showEvent(QShowEvent* event); void resizeEvent(QResizeEvent* event); virtual void mousePressEvent(QMouseEvent *event); private slots: void userButton1(); void userButton2(); void userButton3(); void userButton4(); void gotoPosition(); void qsoBearingLP(); void qsoBearingSP(); void setRequestedAz(double); private: void refreshRotUserButtonProfileCombo(); void refreshRotUserButtons(); void setUserButtonDesc(QPushButton *button, const QString&, const double); double getQSOBearing(); void shortcutComboMove(int); QGraphicsPathItem* antennaNeedle; QGraphicsPathItem* requestedAzimuthNeedle; QGraphicsPathItem* QSOAzimuthNeedle; bool waitingFirstValue; QGraphicsScene* compassScene; Ui::RotatorWidget *ui; double antennaAzimuth; double requestedAzimuth; double qsoAzimuth; const NewContactWidget *contact; const int MAP_RESOLUTION = 1000; const float GLOBE_RADIUS = 100.0; const int AZIMUTH_DEAD_BAND = 2; }; #endif // QLOG_UI_ROTATORWIDGET_H ================================================ FILE: ui/RotatorWidget.ui ================================================ RotatorWidget 0 0 387 257 Form 0 0 0 0 Qt::ClickFocus QComboBox::AdjustToContents false Qt::Horizontal 40 20 Az: false 0 0 Qt::ClickFocus Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter ° 1 359.899999999999977 false Qt::ClickFocus Goto Qt::ClickFocus QFrame::NoFrame QFrame::Plain QPainter::Antialiasing|QPainter::SmoothPixmapTransform|QPainter::TextAntialiasing 0 0 Qt::Horizontal 40 20 false Qt::NoFocus QToolButton::MenuButtonPopup Qt::ToolButtonTextOnly 15 16777215 Previous Button Profile :/icons/baseline-play_back-24px.svg:/icons/baseline-play_back-24px.svg false 0 0 50 16777215 false 0 0 50 16777215 false 0 0 50 16777215 false 0 0 50 16777215 15 16777215 Next Button Profile :/icons/baseline-play_arrow-24px.svg:/icons/baseline-play_arrow-24px.svg Qt::Horizontal 40 20 QSO LP QSO Long Path QSO SP QSO Short Path BaseDoubleSpinBox QDoubleSpinBox
ui/component/BaseDoubleSpinBox.h
gotoButton clicked() RotatorWidget gotoPosition() 353 25 132 128 rotProfileCombo currentTextChanged(QString) RotatorWidget rotProfileComboChanged(QString) 61 25 132 128 userButton_1 clicked() RotatorWidget userButton1() 123 243 177 128 userButton_2 clicked() RotatorWidget userButton2() 177 243 177 128 userButton_3 clicked() RotatorWidget userButton3() 231 243 177 128 userButton_4 clicked() RotatorWidget userButton4() 285 243 177 128 userButtonsProfileCombo currentTextChanged(QString) RotatorWidget rotUserButtonProfileComboChanged(QString) 135 13 177 128 actionQSO_LP triggered() RotatorWidget qsoBearingLP() -1 -1 193 128 actionQSO_SP triggered() RotatorWidget qsoBearingSP() -1 -1 193 128 gotoDoubleSpinBox valueChanged(double) RotatorWidget setRequestedAz(double) 265 14 193 128 rightButton clicked() RotatorWidget shortcutProfileIncrease() 335 241 193 128 leftButton clicked() RotatorWidget shortcutProfileDecrease() 84 241 193 128 gotoPosition() rotProfileComboChanged(QString) userButton1() userButton2() userButton3() userButton4() rotUserButtonProfileComboChanged(QString) qsoBearingSP() qsoBearingLP() setRequestedAz(double) shortcutProfileIncrease() shortcutProfileDecrease()
================================================ FILE: ui/SettingsDialog.cpp ================================================ #include #include #include #include #include #include #include #include "SettingsDialog.h" #include "ui_SettingsDialog.h" #include "models/RigTypeModel.h" #include "models/RotTypeModel.h" #include "service/GenericCallbook.h" #include "service/qrzcom/QRZ.h" #include "service/hamqth/HamQTH.h" #include "service/lotw/Lotw.h" #include "service/clublog/ClubLog.h" #include "service/eqsl/Eqsl.h" #include "service/hrdlog/HRDLog.h" #include "ui/component/StyleItemDelegate.h" #include "core/debug.h" #include "data/StationProfile.h" #include "data/RigProfile.h" #include "data/AntProfile.h" #include "data/Data.h" #include "data/Gridsquare.h" #include "core/WsjtxUDPReceiver.h" #include "core/NetworkNotification.h" #include "rig/Rig.h" #include "rig/RigCaps.h" #include "rotator/Rotator.h" #include "rotator/RotCaps.h" #include "core/LogParam.h" #include "data/Callsign.h" #include "core/MembershipQE.h" #include "models/SqlListModel.h" #include "service/kstchat/KSTChat.h" #include "data/HostsPortString.h" #include "models/ShortcutEditorModel.h" #include "ui/component/StyleItemDelegate.h" #include "data/SerialPort.h" #include "service/cloudlog/Cloudlog.h" #include "ui/RigctldAdvancedDialog.h" #include "cwkey/drivers/CWWinKey.h" MODULE_IDENTIFICATION("qlog.ui.settingsdialog"); void SettingsDialog::refreshProfileView(QAbstractItemView *view, const QStringList &names) { QStringListModel *model = static_cast(view->model()); if ( model ) model->setStringList(names); } void SettingsDialog::disableCapCheckbox(QCheckBox *checkbox) { checkbox->setChecked(false); checkbox->setEnabled(false); } void SettingsDialog::initProfileListView(QAbstractItemView *view) { view->setModel(new QStringListModel(view)); } void SettingsDialog::populateFlowControlCombo(QComboBox *combo) { combo->addItem(tr("None"), SerialPort::SERIAL_FLOWCONTROL_NONE); combo->addItem(tr("Hardware"), SerialPort::SERIAL_FLOWCONTROL_HARDWARE); combo->addItem(tr("Software"), SerialPort::SERIAL_FLOWCONTROL_SOFTWARE); } void SettingsDialog::populateParityCombo(QComboBox *combo) { combo->addItem(tr("No"), SerialPort::SERIAL_PARITY_NO); combo->addItem(tr("Even"), SerialPort::SERIAL_PARITY_EVEN); combo->addItem(tr("Odd"), SerialPort::SERIAL_PARITY_ODD); combo->addItem(tr("Mark"), SerialPort::SERIAL_PARITY_MARK); combo->addItem(tr("Space"), SerialPort::SERIAL_PARITY_SPACE); } void SettingsDialog::populateSignalCombo(QComboBox *combo) { combo->addItem(tr("None"), SerialPort::SERIAL_SIGNAL_NONE); combo->addItem(tr("High"), SerialPort::SERIAL_SIGNAL_HIGH); combo->addItem(tr("Low"), SerialPort::SERIAL_SIGNAL_LOW); } void SettingsDialog::setComboByData(QComboBox *combo, const QVariant &data, int fallback) { const int idx = combo->findData(data); combo->setCurrentIndex((idx < 0) ? fallback : idx); } void SettingsDialog::updateOffsetSpinBox(QCheckBox *checkbox, QDoubleSpinBox *spinBox) { if ( checkbox->isChecked() ) { spinBox->setValue(0.0); spinBox->setEnabled(false); } else spinBox->setEnabled(true); } void SettingsDialog::deleteSelectedProfiles(QAbstractItemView *view, const std::function &removeProfile) { const QModelIndexList selectedRows = view->selectionModel()->selectedRows(); for ( const QModelIndex &index : selectedRows ) { removeProfile(view->model()->data(index).toString()); view->model()->removeRow(index.row()); } view->clearSelection(); } SettingsDialog::SettingsDialog(MainWindow *parent) : QDialog(parent), stationProfManager(StationProfilesManager::instance()), rigProfManager(RigProfilesManager::instance()), rotProfManager(RotProfilesManager::instance()), antProfManager(AntProfilesManager::instance()), cwKeyProfManager(CWKeyProfilesManager::instance()), cwShortcutProfManager(CWShortcutProfilesManager::instance()), rotUsrButtonsProfManager(RotUsrButtonsProfilesManager::instance()), countyCompleter(nullptr), ui(new Ui::SettingsDialog), sotaFallback(false), potaFallback(false), wwffFallback(false), tqslVersionTimer(new QTimer(this)) { FCT_IDENTIFICATION; ui->setupUi(this); ui->dateFormatResultLabel->setVisible(false); ui->dateFormatStringEdit->setVisible(false); ui->dateFormatDocLabel->setVisible(false); ui->rigPortTypeCombo->addItem(tr("Serial")); ui->rigPortTypeCombo->addItem(tr("Network")); ui->rigPortTypeCombo->addItem(tr("Special - Omnirig")); #ifdef QLOG_FLATPAK ui->lotwTextMessage->setVisible(true); ui->tqslPathEdit->setEnabled(false); ui->tqslPathEdit->setPlaceholderText(tr("Cannot be changed")); ui->tqslPathButton->setEnabled(false); ui->tqslAutoButton->setEnabled(false); #else ui->lotwTextMessage->setVisible(false); #endif tqslVersionTimer->setSingleShot(true); tqslVersionTimer->setInterval(500); connect(tqslVersionTimer, &QTimer::timeout, this, &SettingsDialog::updateTQSLVersionLabel); connect(ui->tqslPathEdit, &QLineEdit::textChanged, this, [this]() { tqslVersionTimer->start(); }); RotTypeModel* rotTypeModel = new RotTypeModel(ui->rotModelSelect); ui->rotModelSelect->setModel(rotTypeModel); initProfileListView(ui->rotProfilesListView); initProfileListView(ui->rotUsrButtonListView); initProfileListView(ui->antProfilesListView); initProfileListView(ui->cwProfilesListView); initProfileListView(ui->cwShortcutListView); initProfileListView(ui->stationProfilesListView); QStringListModel* cwKeysModel = new QStringListModel(ui->rigAssignedCWKeyCombo); ui->rigAssignedCWKeyCombo->setModel(cwKeysModel); /* Rig Models must be initialized after rigAssignedCWKeyCombo model !!!! */ /* becase rigChanged is called and it constain uninitialized * CW Model */ RigTypeModel* rigTypeModel = new RigTypeModel(ui->rigModelSelect); ui->rigModelSelect->setModel(rigTypeModel); for ( const QPair &driver : Rig::instance()->getDriverList() ) ui->rigInterfaceCombo->addItem(driver.second, driver.first); for ( const QPair &driver : Rotator::instance()->getDriverList() ) ui->rotInterfaceCombo->addItem(driver.second, driver.first); initProfileListView(ui->rigProfilesListView); /* Country Combo */ SqlListModel* countryModel = new SqlListModel("SELECT id, translate_to_locale(name), name " "FROM dxcc_entities_ad1c " "ORDER BY 2 COLLATE LOCALEAWARE ASC;", " ", ui->stationCountryCombo); while ( countryModel->canFetchMore() ) countryModel->fetchMore(); ui->stationCountryCombo->setModel(countryModel); ui->stationCountryCombo->setModelColumn(1); ShortcutEditorModel *shortcutModel = new ShortcutEditorModel( parent->getUserDefinedShortcutActionList(), parent->getBuiltInStaticShortcutList(), ui->shortcutsTableView); ui->shortcutsTableView->setModel(shortcutModel); ui->shortcutsTableView->horizontalHeader()->setSectionResizeMode(ShortcutEditorModel::COLUMN_DESCRIPTION, QHeaderView::Stretch); ui->shortcutsTableView->setItemDelegateForColumn(ShortcutEditorModel::COLUMN_SHORTCUT, new ShortcutDelegate(ui->shortcutsTableView)); connect(shortcutModel, &ShortcutEditorModel::conflictDetected, this, [this](const QString& text) { ui->shortcutInfoLabel->setText("" + text + ""); }); connect(shortcutModel, &ShortcutEditorModel::dataChanged, this, [this]() { ui->shortcutInfoLabel->clear(); }); ui->stationCQZEdit->setValidator(new QIntValidator(Data::getCQZMin(), Data::getCQZMax(), ui->stationCQZEdit)); ui->stationITUEdit->setValidator(new QIntValidator(Data::getITUZMin(), Data::getITUZMax(), ui->stationITUEdit)); modeTableModel = new QSqlTableModel(ui->modeTableView); modeTableModel->setTable("modes"); modeTableModel->setEditStrategy(QSqlTableModel::OnFieldChange); modeTableModel->setSort(1, Qt::AscendingOrder); modeTableModel->setHeaderData(1, Qt::Horizontal, tr("Name")); modeTableModel->setHeaderData(3, Qt::Horizontal, tr("Report")); modeTableModel->setHeaderData(4, Qt::Horizontal, tr("DXCC")); modeTableModel->setHeaderData(5, Qt::Horizontal, tr("State")); ui->modeTableView->setModel(modeTableModel); ui->modeTableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); ui->modeTableView->hideColumn(0); ui->modeTableView->hideColumn(2); ui->modeTableView->setItemDelegateForColumn(1, new ReadOnlyDelegate(ui->modeTableView)); ui->modeTableView->setItemDelegateForColumn(4, new ComboFormatDelegate(QStringList() << "CW"<< "PHONE" << "DIGITAL", ui->modeTableView)); ui->modeTableView->setItemDelegateForColumn(5, new CheckBoxDelegate(ui->modeTableView)); modeTableModel->select(); bandTableModel = new QSqlTableModel(ui->bandTableView); bandTableModel->setTable("bands"); bandTableModel->setEditStrategy(QSqlTableModel::OnFieldChange); bandTableModel->setSort(2, Qt::AscendingOrder); bandTableModel->setHeaderData(1, Qt::Horizontal, tr("Name")); bandTableModel->setHeaderData(2, Qt::Horizontal, tr("Start (MHz)")); bandTableModel->setHeaderData(3, Qt::Horizontal, tr("End (MHz)")); bandTableModel->setHeaderData(4, Qt::Horizontal, tr("State")); bandTableModel->setHeaderData(6, Qt::Horizontal, tr("SAT Mode")); ui->bandTableView->setModel(bandTableModel); ui->bandTableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); ui->bandTableView->horizontalHeader()->moveSection(6, 4); ui->bandTableView->hideColumn(0); // primary key ui->bandTableView->hideColumn(5); // last_seen_freq ui->bandTableView->setItemDelegateForColumn(1, new ReadOnlyDelegate(ui->bandTableView)); ui->bandTableView->setItemDelegateForColumn(2, new UnitFormatDelegate("", 6, 0.001, ui->bandTableView)); ui->bandTableView->setItemDelegateForColumn(3, new UnitFormatDelegate("", 6, 0.001, ui->bandTableView)); ui->bandTableView->setItemDelegateForColumn(4,new CheckBoxDelegate(ui->bandTableView)); bandTableModel->select(); ui->stationCallsignEdit->setValidator(new QRegularExpressionValidator(Callsign::callsignRegEx(), ui->stationCallsignEdit)); ui->stationOperatorCallsignEdit->setValidator(new QRegularExpressionValidator(Callsign::callsignRegEx(), ui->stationOperatorCallsignEdit)); ui->stationLocatorEdit->setValidator(new QRegularExpressionValidator(Gridsquare::gridRegEx(), ui->stationLocatorEdit)); ui->stationVUCCEdit->setValidator(new QRegularExpressionValidator(Gridsquare::gridVUCCRegEx(), ui->stationVUCCEdit)); /* https://stackoverflow.com/questions/13145397/regex-for-multicast-ip-address */ static QRegularExpression multicastAddress("^2(?:2[4-9]|3\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d?|0)){3}$"); ui->wsjtMulticastAddressEdit->setValidator(new QRegularExpressionValidator(multicastAddress, ui->wsjtMulticastAddressEdit)); const QRegularExpression hostsPortRe = HostsPortString::hostsPortRegEx(); QLineEdit * const hostsPortEdits[] = { ui->wsjtForwardEdit, ui->notifQSOEdit, ui->notifDXSpotsEdit, ui->notifWSJTXCQSpotsEdit, ui->notifSpotAlertEdit, ui->notifRigEdit }; for ( QLineEdit *edit : hostsPortEdits ) edit->setValidator(new QRegularExpressionValidator(hostsPortRe, edit)); iotaCompleter = new QCompleter(Data::instance()->iotaIDList(), ui->stationIOTAEdit); iotaCompleter->setCaseSensitivity(Qt::CaseInsensitive); iotaCompleter->setFilterMode(Qt::MatchContains); iotaCompleter->setModelSorting(QCompleter::CaseSensitivelySortedModel); ui->stationIOTAEdit->setCompleter(iotaCompleter); sotaCompleter = new QCompleter(Data::instance()->sotaIDList(), ui->stationSOTAEdit); sotaCompleter->setCaseSensitivity(Qt::CaseInsensitive); sotaCompleter->setFilterMode(Qt::MatchStartsWith); sotaCompleter->setModelSorting(QCompleter::CaseSensitivelySortedModel); ui->stationSOTAEdit->setCompleter(nullptr); wwffCompleter = new QCompleter(Data::instance()->wwffIDList(), ui->stationWWFFEdit); wwffCompleter->setCaseSensitivity(Qt::CaseInsensitive); wwffCompleter->setFilterMode(Qt::MatchStartsWith); wwffCompleter->setModelSorting(QCompleter::CaseSensitivelySortedModel); ui->stationWWFFEdit->setCompleter(nullptr); potaCompleter = new MultiselectCompleter(Data::instance()->potaIDList(), ui->stationPOTAEdit); potaCompleter->setCaseSensitivity(Qt::CaseInsensitive); potaCompleter->setFilterMode(Qt::MatchStartsWith); potaCompleter->setModelSorting(QCompleter::CaseSensitivelySortedModel); ui->stationPOTAEdit->setCompleter(nullptr); sigCompleter = new QCompleter(Data::instance()->sigIDList(), ui->stationSIGEdit); sigCompleter->setCaseSensitivity(Qt::CaseInsensitive); sigCompleter->setFilterMode(Qt::MatchStartsWith); sigCompleter->setModelSorting(QCompleter::CaseSensitivelySortedModel); ui->stationSIGEdit->setCompleter(sigCompleter); connect(ui->stationCountryCombo, QOverload::of(&QComboBox::currentIndexChanged), this, [this](int row) { const QModelIndex &idx = ui->stationCountryCombo->model()->index(row, 0); updateCountyCompleter(ui->stationCountryCombo->model()->data(idx).toInt()); }); ui->primaryCallbookCombo->addItem(tr("Disabled"), QVariant(GenericCallbook::CALLBOOK_NAME)); ui->primaryCallbookCombo->addItem(tr("HamQTH"), QVariant(HamQTHCallbook::CALLBOOK_NAME)); ui->primaryCallbookCombo->addItem(tr("QRZ.com"), QVariant(QRZCallbook::CALLBOOK_NAME)); populateFlowControlCombo(ui->rigFlowControlSelect); populateParityCombo(ui->rigParitySelect); populateFlowControlCombo(ui->rotFlowControlSelect); populateParityCombo(ui->rotParitySelect); ui->cwModelSelect->addItem(tr("Dummy"), CWKey::DUMMY_KEYER); ui->cwModelSelect->addItem(tr("Morse Over CAT"), CWKey::MORSEOVERCAT); ui->cwModelSelect->addItem(tr("WinKey"), CWKey::WINKEY_KEYER); ui->cwModelSelect->addItem(tr("CWDaemon"), CWKey::CWDAEMON_KEYER); ui->cwModelSelect->addItem(tr("FLDigi"), CWKey::FLDIGI_KEYER); ui->cwModelSelect->setCurrentIndex(ui->cwModelSelect->findData(DEFAULT_CWKEY_MODEL)); ui->cwKeyModeSelect->addItem(tr("Single Paddle"), CWKey::SINGLE_PADDLE); ui->cwKeyModeSelect->addItem(tr("IAMBIC A"), CWKey::IAMBIC_A); ui->cwKeyModeSelect->addItem(tr("IAMBIC B"), CWKey::IAMBIC_B); ui->cwKeyModeSelect->addItem(tr("Ultimate"), CWKey::ULTIMATE); ui->cwKeyModeSelect->setCurrentIndex(ui->cwKeyModeSelect->findData(CWKey::IAMBIC_B)); const QList> sideToneFreqsList = CWWinKey::sidetoneFrequencies(); for ( const QPair &f : sideToneFreqsList ) ui->cwSidetoneFreqCombo->addItem(f.first, f.second); ui->cwSidetoneFreqCombo->setCurrentIndex(ui->cwSidetoneFreqCombo->findData(CWWinKey::defaultSidetoneFrequency())); populateSignalCombo(ui->rigDTRCombo); populateSignalCombo(ui->rigRTSCombo); /* disable WSJTX Multicast by default */ joinMulticastChanged(false); generateMembershipCheckboxes(); readSettings(); } void SettingsDialog::save() { FCT_IDENTIFICATION; if ( stationProfManager->profileNameList().isEmpty() ) { QMessageBox::warning(nullptr, QMessageBox::tr("QLog Warning"), QMessageBox::tr("Please, define at least one Station Locations Profile")); return; } const QString pleaseModifyTXT = tr("Press Modify to confirm the profile changes or Cancel."); struct PendingModify { QPushButton *button; int tabIndex; int equipTabIndex; }; const PendingModify modifyChecks[] = { {ui->stationAddProfileButton, 0, -1}, {ui->antAddProfileButton, 1, 0}, {ui->cwAddProfileButton, 1, 1}, {ui->cwShortcutAddProfileButton, 1, 1}, {ui->rigAddProfileButton, 1, 2}, {ui->rotAddProfileButton, 1, 3}, {ui->rotUsrButtonAddProfileButton, 1, 3}, }; for ( const PendingModify &check : modifyChecks ) { if ( check.button->text() == tr("Modify") ) { ui->tabWidget->setCurrentIndex(check.tabIndex); if ( check.equipTabIndex >= 0 ) ui->equipmentTabWidget->setCurrentIndex(check.equipTabIndex); QMessageBox::warning(nullptr, QMessageBox::tr("QLog Warning"), pleaseModifyTXT); return; } } if ( ui->wsjtMulticastCheckbox->isChecked() && ! ui->wsjtMulticastAddressEdit->hasAcceptableInput()) { ui->tabWidget->setCurrentIndex(8); QMessageBox::warning(nullptr, QMessageBox::tr("QLog Warning"), QMessageBox::tr("WSJTX Multicast is enabled but the Address is not a multicast address.")); return; } if ( !ui->wsjtForwardEdit->text().isEmpty() ) { HostsPortString list(ui->wsjtForwardEdit->text()); if ( list.hasLocalIPWithPort(ui->wsjtPortSpin->value()) ) { ui->tabWidget->setCurrentIndex(8); QMessageBox::warning(nullptr, QMessageBox::tr("QLog Warning"), QMessageBox::tr("Loop detected. Raw UDP forward uses the same port as the WSJT-X receiving port.")); return; } } writeSettings(); accept(); } void SettingsDialog::addRigProfile() { FCT_IDENTIFICATION; if ( ui->rigProfileNameEdit->text().isEmpty() ) { ui->rigProfileNameEdit->setPlaceholderText(tr("Must not be empty")); return; } if ( ! ui->rigPortEdit->text().isEmpty() && ! ui->rigPortEdit->hasAcceptableInput()) { QMessageBox::warning(nullptr, QMessageBox::tr("QLog Warning"), QMessageBox::tr("Rig port must be a valid COM port.
For Windows use COMxx, for unix-like OS use a path to device")); return; } if ( ui->rigStackedWidget->currentIndex() == STACKED_WIDGET_SERIAL_SETTING ) { if ( ui->rigPTTTypeCombo->currentIndex() != PTT_TYPE_NONE_INDEX && ui->rigPTTTypeCombo->currentIndex() != PTT_TYPE_CAT_INDEX && ui->rigPTTPortEdit->text().isEmpty() ) { ui->rigPTTPortEdit->setPlaceholderText(tr("Must not be empty")); return; } if ( ! ui->rigPTTPortEdit->text().isEmpty() && ! ui->rigPTTPortEdit->hasAcceptableInput() ) { QMessageBox::warning(nullptr, QMessageBox::tr("QLog Warning"), QMessageBox::tr("Rig PTT port must be a valid COM port.
For Windows use COMxx, for unix-like OS use a path to device")); return; } } if ( ui->rigTXFreqMaxSpinBox->value() == 0.0 ) { QMessageBox::critical(nullptr, QMessageBox::tr("QLog Error"), QMessageBox::tr("TX Range: Max Frequency must not be 0.")); return; } if ( ui->rigTXFreqMaxSpinBox->value() <= ui->rigTXFreqMinSpinBox->value() ) { QMessageBox::critical(nullptr, QMessageBox::tr("QLog Error"), QMessageBox::tr("TX Range: Max Frequency must not be under Min Frequency.")); return; } if ( ui->rigAddProfileButton->text() == tr("Modify")) ui->rigAddProfileButton->setText(tr("Add")); RigProfile profile; profile.profileName = ui->rigProfileNameEdit->text(); profile.driver = ui->rigInterfaceCombo->currentData().toInt(); profile.model = ui->rigModelSelect->currentData().toInt(); profile.txFreqStart = ui->rigTXFreqMinSpinBox->value(); profile.txFreqEnd = ui->rigTXFreqMaxSpinBox->value(); if ( ui->rigStackedWidget->currentIndex() == STACKED_WIDGET_NETWORK_SETTING ) { profile.hostname = ui->rigHostNameEdit->text(); profile.netport = ui->rigNetPortSpin->value(); } if ( ui->rigStackedWidget->currentIndex() == STACKED_WIDGET_SERIAL_SETTING ) { profile.portPath = ui->rigPortEdit->text(); profile.baudrate = ui->rigBaudSelect->currentText().toInt(); profile.databits = ui->rigDataBitsSelect->currentText().toInt(); profile.stopbits = ui->rigStopBitsSelect->currentText().toFloat(); profile.flowcontrol = ui->rigFlowControlSelect->currentData().toString(); profile.parity = ui->rigParitySelect->currentData().toString(); profile.pttType = ui->rigPTTTypeCombo->currentData().toString(); profile.pttPortPath = ui->rigPTTPortEdit->text(); profile.rts = ui->rigRTSCombo->currentData().toString(); profile.dtr = ui->rigDTRCombo->currentData().toString(); profile.civAddr = ui->rigCIVAddrSpinBox->value(); } if ( ui->rigPollIntervalSpinBox->isEnabled() ) { profile.pollInterval = ui->rigPollIntervalSpinBox->value(); } profile.ritOffset = ui->rigRXOffsetSpinBox->value(); profile.xitOffset = ui->rigTXOffsetSpinBox->value(); profile.defaultPWR = ui->rigPWRDefaultSpinBox->value(); profile.assignedCWKey = ui->rigAssignedCWKeyCombo->currentText(); profile.getFreqInfo = ui->rigGetFreqCheckBox->isChecked(); profile.getModeInfo = ui->rigGetModeCheckBox->isChecked(); profile.getVFOInfo = ui->rigGetVFOCheckBox->isChecked(); profile.getPWRInfo = ui->rigGetPWRCheckBox->isChecked(); profile.getRITInfo = ui->rigGetRITCheckBox->isChecked(); profile.getXITInfo = ui->rigGetXITCheckBox->isChecked(); profile.getPTTInfo = ui->rigGetPTTStateCheckBox->isChecked(); profile.QSYWiping = ui->rigQSYWipingCheckBox->isChecked(); profile.getKeySpeed = ui->rigGetKeySpeedCheckBox->isChecked(); profile.keySpeedSync = ui->rigKeySpeedSyncCheckBox->isChecked(); profile.dxSpot2Rig = ui->rigDXSpots2RigCheckBox->isChecked(); profile.getSplitInfo = ui->rigGetSplitCheckBox->isChecked(); // Rigctld sharing settings profile.shareRigctld = ui->rigShareCheckBox->isChecked(); profile.rigctldPort = ui->rigSharePortSpinBox->value(); profile.rigctldPath = rigctldPath; profile.rigctldArgs = rigctldArgs; rigProfManager->addProfile(profile.profileName, profile); refreshRigProfilesView(); clearRigProfileForm(); } void SettingsDialog::delRigProfile() { FCT_IDENTIFICATION; deleteSelectedProfiles(ui->rigProfilesListView, [this](const QString &name) { rigProfManager->removeProfile(name); }); clearRigProfileForm(); } void SettingsDialog::doubleClickRigProfile(QModelIndex i) { FCT_IDENTIFICATION; const RigProfile &profile = rigProfManager->getProfile(ui->rigProfilesListView->model()->data(i).toString()); ui->rigProfileNameEdit->setText(profile.profileName); ui->rigInterfaceCombo->setCurrentIndex(ui->rigInterfaceCombo->findData(profile.driver)); ui->rigModelSelect->setCurrentIndex(ui->rigModelSelect->findData(profile.model)); int portIndex; switch ( profile.getPortType() ) { case RigProfile::SERIAL_ATTACHED: portIndex = RIGPORT_SERIAL_INDEX; break; case RigProfile::NETWORK_ATTACHED: portIndex = RIGPORT_NETWORK_INDEX; break; case RigProfile::SPECIAL_OMNIRIG_ATTACHED: portIndex = RIGPORT_SPECIAL_OMNIRIG_INDEX; break; default: qWarning() << "cannot set correct Rig Port - unsupported" << profile.getPortType(); portIndex = RIGPORT_SERIAL_INDEX; } ui->rigPortTypeCombo->setCurrentIndex(portIndex); ui->rigPortEdit->setText(profile.portPath); ui->rigHostNameEdit->setText(profile.hostname); ui->rigNetPortSpin->setValue(profile.netport); ui->rigBaudSelect->setCurrentText(QString::number(profile.baudrate)); ui->rigDataBitsSelect->setCurrentText(QString::number(profile.databits)); ui->rigStopBitsSelect->setCurrentText(QString::number(profile.stopbits)); ui->rigPollIntervalSpinBox->setValue(profile.pollInterval); ui->rigTXFreqMinSpinBox->setValue(profile.txFreqStart); ui->rigTXFreqMaxSpinBox->setValue(profile.txFreqEnd); ui->rigPWRDefaultSpinBox->setValue(profile.defaultPWR); ui->rigAssignedCWKeyCombo->setCurrentText(profile.assignedCWKey); ui->rigGetFreqCheckBox->setChecked(profile.getFreqInfo); ui->rigGetModeCheckBox->setChecked(profile.getModeInfo); ui->rigGetVFOCheckBox->setChecked(profile.getVFOInfo); ui->rigGetPWRCheckBox->setChecked(profile.getPWRInfo); ui->rigGetRITCheckBox->setChecked(profile.getRITInfo); ui->rigGetXITCheckBox->setChecked(profile.getXITInfo); ui->rigRXOffsetSpinBox->setValue(profile.ritOffset); ui->rigTXOffsetSpinBox->setValue(profile.xitOffset); ui->rigGetPTTStateCheckBox->setChecked(profile.getPTTInfo); ui->rigQSYWipingCheckBox->setChecked(profile.QSYWiping); ui->rigGetKeySpeedCheckBox->setChecked(profile.getKeySpeed); ui->rigKeySpeedSyncCheckBox->setChecked(profile.keySpeedSync); ui->rigDXSpots2RigCheckBox->setChecked(profile.dxSpot2Rig); ui->rigGetSplitCheckBox->setChecked(profile.getSplitInfo); setComboByData(ui->rigFlowControlSelect, profile.flowcontrol.toLower()); setComboByData(ui->rigParitySelect, profile.parity.toLower()); const RigCaps &caps = Rig::instance()->getRigCaps(static_cast(profile.driver), profile.model); setComboByData(ui->rigPTTTypeCombo, profile.pttType, PTT_TYPE_CAT_INDEX); ui->rigPTTPortEdit->setText(profile.pttPortPath); setComboByData(ui->rigRTSCombo, profile.rts, PTT_TYPE_CAT_INDEX); setComboByData(ui->rigDTRCombo, profile.dtr, PTT_TYPE_CAT_INDEX); ui->rigCIVAddrSpinBox->setValue(( profile.civAddr >= 0 ) ? profile.civAddr : CIVADDR_DISABLED_VALUE); // Rigctld sharing settings ui->rigShareCheckBox->setChecked(profile.shareRigctld); ui->rigSharePortSpinBox->setValue(profile.rigctldPort); rigctldPath = profile.rigctldPath; rigctldArgs = profile.rigctldArgs; setUIBasedOnRigCaps(caps); updateRigShareEnabled(); ui->rigAddProfileButton->setText(tr("Modify")); } void SettingsDialog::clearRigProfileForm() { FCT_IDENTIFICATION; ui->rigProfileNameEdit->setPlaceholderText(QString()); ui->rigPortEdit->setPlaceholderText(QString()); ui->rigHostNameEdit->setPlaceholderText(QString()); ui->rigPTTPortEdit->setPlaceholderText(QString()); ui->rigProfileNameEdit->clear(); ui->rigTXFreqMinSpinBox->setValue(0.0); ui->rigTXFreqMaxSpinBox->setValue(0.0); ui->rigPWRDefaultSpinBox->setValue(100.0); ui->rigAssignedCWKeyCombo->setCurrentIndex(0); ui->rigPollIntervalSpinBox->setValue(500.0); ui->rigPortEdit->clear(); ui->rigHostNameEdit->clear(); ui->rigNetPortSpin->setValue(RIG_NET_DEFAULT_PORT); ui->rigBaudSelect->setCurrentIndex(0); ui->rigDataBitsSelect->setCurrentIndex(0); ui->rigStopBitsSelect->setCurrentIndex(0); ui->rigFlowControlSelect->setCurrentIndex(0); ui->rigDTRCombo->setCurrentIndex(0); ui->rigRTSCombo->setCurrentIndex(0); ui->rigParitySelect->setCurrentIndex(0); ui->rigGetFreqCheckBox->setChecked(true); ui->rigGetModeCheckBox->setChecked(true); ui->rigGetVFOCheckBox->setChecked(true); ui->rigGetPWRCheckBox->setChecked(true); ui->rigGetRITCheckBox->setChecked(false); ui->rigGetXITCheckBox->setChecked(false); ui->rigRXOffsetSpinBox->setValue(0.0); ui->rigTXOffsetSpinBox->setValue(0.0); ui->rigGetPTTStateCheckBox->setChecked(false); ui->rigQSYWipingCheckBox->setChecked(true); ui->rigGetKeySpeedCheckBox->setChecked(true); ui->rigKeySpeedSyncCheckBox->setChecked(false); ui->rigDXSpots2RigCheckBox->setChecked(false); ui->rigAddProfileButton->setText(tr("Add")); ui->rigPTTPortEdit->clear(); ui->rigCIVAddrSpinBox->setValue(CIVADDR_DISABLED_VALUE); ui->rigGetSplitCheckBox->setChecked(false); // Rigctld sharing settings ui->rigShareCheckBox->setChecked(false); ui->rigSharePortSpinBox->setValue(4532); rigctldPath.clear(); rigctldArgs.clear(); rigChanged(ui->rigModelSelect->currentIndex()); updateRigShareEnabled(); } void SettingsDialog::rigRXOffsetChanged(int) { FCT_IDENTIFICATION; updateOffsetSpinBox(ui->rigGetRITCheckBox, ui->rigRXOffsetSpinBox); } void SettingsDialog::rigTXOffsetChanged(int) { FCT_IDENTIFICATION; updateOffsetSpinBox(ui->rigGetXITCheckBox, ui->rigTXOffsetSpinBox); } void SettingsDialog::rigGetFreqChanged(int) { FCT_IDENTIFICATION; ui->rigQSYWipingCheckBox->setChecked(ui->rigGetFreqCheckBox->isChecked()); ui->rigQSYWipingCheckBox->setEnabled(ui->rigGetFreqCheckBox->isChecked()); } void SettingsDialog::rigPortTypeChanged(int index) { FCT_IDENTIFICATION; qCDebug(function_parameters) << index; switch (index) { // Serial case RIGPORT_SERIAL_INDEX: { const RigCaps &caps = Rig::instance()->getRigCaps(static_cast(ui->rigInterfaceCombo->currentData().toInt()), ui->rigModelSelect->currentData().toInt()); ui->rigStackedWidget->setCurrentIndex(STACKED_WIDGET_SERIAL_SETTING); ui->rigDataBitsSelect->setCurrentText(QString::number(caps.serialDataBits)); ui->rigStopBitsSelect->setCurrentText(QString::number(caps.serialStopBits)); ui->rigHostNameEdit->clear(); } break; // Network case RIGPORT_NETWORK_INDEX: ui->rigStackedWidget->setCurrentIndex(STACKED_WIDGET_NETWORK_SETTING); ui->rigPortEdit->clear(); ui->rigNetPortSpin->setValue(RIG_NET_DEFAULT_PORT); break; // Omnirig Special case RIGPORT_SPECIAL_OMNIRIG_INDEX: ui->rigStackedWidget->setCurrentIndex(STACKED_WIDGET_SPECIAL_OMNIRIG_SETTING); ui->rigHostNameEdit->clear(); ui->rigPortEdit->clear(); break; default: qWarning() << "Unsupported Rig Port" << index; } } void SettingsDialog::rigInterfaceChanged(int) { FCT_IDENTIFICATION; RigTypeModel* rigTypeModel = qobject_cast (ui->rigModelSelect->model()); if ( !rigTypeModel ) return; ui->rigPortTypeCombo->removeItem(STACKED_WIDGET_SPECIAL_OMNIRIG_SETTING); Rig::DriverID driverID = static_cast(ui->rigInterfaceCombo->currentData().toInt()); if ( driverID == Rig::OMNIRIG_DRIVER || driverID == Rig::OMNIRIGV2_DRIVER ) { ui->rigPortTypeCombo->insertItem(STACKED_WIDGET_SPECIAL_OMNIRIG_SETTING, tr("Special - Omnirig")); } rigTypeModel->select(driverID); ui->rigModelSelect->setCurrentIndex(( driverID == Rig::HAMLIB_DRIVER ) ? ui->rigModelSelect->findData(Rig::DEFAULT_MODEL) : 0 ); ui->rigPTTTypeCombo->clear(); setComboByData(ui->rigRTSCombo, SerialPort::SERIAL_SIGNAL_NONE); setComboByData(ui->rigDTRCombo, SerialPort::SERIAL_SIGNAL_NONE); const QList> &pttTypes = Rig::instance()->getPTTTypeList(static_cast(driverID)); for ( const QPair &type : pttTypes ) ui->rigPTTTypeCombo->addItem(type.second, type.first); ui->rigRTSCombo->setVisible((driverID == Rig::HAMLIB_DRIVER)); ui->rigDTRCombo->setVisible((driverID == Rig::HAMLIB_DRIVER)); ui->rigPTTTypeCombo->setVisible(( driverID == Rig::HAMLIB_DRIVER )); ui->rigPTTTypeLabel->setVisible(( driverID == Rig::HAMLIB_DRIVER )); ui->rigPTTPortEdit->setVisible(( driverID == Rig::HAMLIB_DRIVER )); ui->rigPTTPortLabel->setVisible(( driverID == Rig::HAMLIB_DRIVER )); ui->rigPTTTypeCombo->setCurrentIndex(( driverID == Rig::HAMLIB_DRIVER ) ? PTT_TYPE_CAT_INDEX : 0); } void SettingsDialog::rigPTTTypeChanged(int index) { FCT_IDENTIFICATION; ui->rigPTTPortEdit->setVisible((index != PTT_TYPE_CAT_INDEX && index != PTT_TYPE_NONE_INDEX)); ui->rigPTTPortLabel->setVisible((index != PTT_TYPE_CAT_INDEX && index != PTT_TYPE_NONE_INDEX)); if ( index == PTT_TYPE_CAT_INDEX || index == PTT_TYPE_NONE_INDEX ) ui->rigPTTPortEdit->clear(); } void SettingsDialog::addRotProfile() { FCT_IDENTIFICATION; if ( ui->rotProfileNameEdit->text().isEmpty() ) { ui->rotProfileNameEdit->setPlaceholderText(tr("Must not be empty")); return; } if ( ! ui->rotPortEdit->text().isEmpty() && ! ui->rotPortEdit->hasAcceptableInput() ) { QMessageBox::warning(nullptr, QMessageBox::tr("QLog Warning"), QMessageBox::tr("Rotator port must be a valid COM port.
For Windows use COMxx, for unix-like OS use a path to device")); return; } if ( ui->rotAddProfileButton->text() == tr("Modify")) ui->rotAddProfileButton->setText(tr("Add")); RotProfile profile; profile.profileName = ui->rotProfileNameEdit->text(); profile.driver = ui->rotInterfaceCombo->currentData().toInt(); profile.model = ui->rotModelSelect->currentData().toInt(); if ( ui->rotStackedWidget->currentIndex() == STACKED_WIDGET_NETWORK_SETTING ) { profile.hostname = ui->rotHostNameEdit->text(); profile.netport = ui->rotNetPortSpin->value(); } if ( ui->rotStackedWidget->currentIndex() == STACKED_WIDGET_SERIAL_SETTING ) { profile.portPath = ui->rotPortEdit->text(); profile.baudrate = ui->rotBaudSelect->currentText().toInt(); profile.databits = ui->rotDataBitsSelect->currentText().toInt(); profile.stopbits = ui->rotStopBitsSelect->currentText().toFloat(); profile.flowcontrol = ui->rotFlowControlSelect->currentData().toString(); profile.parity = ui->rotParitySelect->currentData().toString(); } rotProfManager->addProfile(profile.profileName, profile); refreshRotProfilesView(); clearRotProfileForm(); } void SettingsDialog::delRotProfile() { FCT_IDENTIFICATION; deleteSelectedProfiles(ui->rotProfilesListView, [this](const QString &name) { rotProfManager->removeProfile(name); }); clearRotProfileForm(); } void SettingsDialog::refreshRotProfilesView() { FCT_IDENTIFICATION; refreshProfileView(ui->rotProfilesListView, rotProfManager->profileNameList()); } void SettingsDialog::doubleClickRotProfile(QModelIndex i) { FCT_IDENTIFICATION; const RotProfile &profile = rotProfManager->getProfile(ui->rotProfilesListView->model()->data(i).toString()); ui->rotProfileNameEdit->setText(profile.profileName); ui->rotInterfaceCombo->setCurrentIndex(ui->rotInterfaceCombo->findData(profile.driver)); ui->rotPortTypeCombo->setCurrentIndex( (profile.getPortType() == RotProfile::SERIAL_ATTACHED) ? ROTPORT_SERIAL_INDEX : ROTPORT_NETWORK_INDEX); ui->rotModelSelect->setCurrentIndex(ui->rotModelSelect->findData(profile.model)); ui->rotPortEdit->setText(profile.portPath); ui->rotHostNameEdit->setText(profile.hostname); ui->rotNetPortSpin->setValue(profile.netport); ui->rotBaudSelect->setCurrentText(QString::number(profile.baudrate)); ui->rotDataBitsSelect->setCurrentText(QString::number(profile.databits)); ui->rotStopBitsSelect->setCurrentText(QString::number(profile.stopbits)); setComboByData(ui->rotFlowControlSelect, profile.flowcontrol.toLower()); setComboByData(ui->rotParitySelect, profile.parity.toLower()); ui->rotAddProfileButton->setText(tr("Modify")); } void SettingsDialog::clearRotProfileForm() { FCT_IDENTIFICATION; ui->rotProfileNameEdit->setPlaceholderText(QString()); ui->rotPortEdit->setPlaceholderText(QString()); ui->rotHostNameEdit->setPlaceholderText(QString()); ui->rotProfileNameEdit->clear(); ui->rotPortEdit->clear(); ui->rotHostNameEdit->clear(); ui->rotBaudSelect->setCurrentIndex(0); ui->rotDataBitsSelect->setCurrentIndex(0); ui->rotStopBitsSelect->setCurrentIndex(0); ui->rotFlowControlSelect->setCurrentIndex(0); ui->rotParitySelect->setCurrentIndex(0); rotInterfaceChanged(ui->rotInterfaceCombo->currentIndex()); ui->rotAddProfileButton->setText(tr("Add")); } void SettingsDialog::rotPortTypeChanged(int index) { FCT_IDENTIFICATION; switch (index) { // Serial case ROTPORT_SERIAL_INDEX: { const RotCaps &caps = Rotator::instance()->getRotCaps(static_cast(ui->rotInterfaceCombo->currentData().toInt()), ui->rotModelSelect->currentData().toInt()); ui->rotStackedWidget->setCurrentIndex(STACKED_WIDGET_SERIAL_SETTING); ui->rotDataBitsSelect->setCurrentText(QString::number(caps.serialDataBits)); ui->rotStopBitsSelect->setCurrentText(QString::number(caps.serialStopBits)); ui->rotHostNameEdit->clear(); } break; // Network case RIGPORT_NETWORK_INDEX: ui->rotStackedWidget->setCurrentIndex(STACKED_WIDGET_NETWORK_SETTING); ui->rotPortEdit->clear(); ui->rotNetPortSpin->setValue(ROT_NET_DEFAULT_PORT); break; default: qWarning() << "Unsupported Rot Port" << index; } } void SettingsDialog::rotInterfaceChanged(int) { FCT_IDENTIFICATION; RotTypeModel* rotTypeModel = qobject_cast (ui->rotModelSelect->model()); if ( !rotTypeModel ) return; Rotator::DriverID driverID = static_cast(ui->rotInterfaceCombo->currentData().toInt()); rotTypeModel->select(driverID); if ( driverID == Rotator::HAMLIB_DRIVER ) { ui->rotModelSelect->setCurrentIndex(ui->rotModelSelect->findData(Rig::DEFAULT_MODEL)); ui->rotNetPortSpin->setValue(ROT_NET_DEFAULT_PORT); } else { ui->rotModelSelect->setCurrentIndex(0); ui->rotNetPortSpin->setValue(ROT_NET_DEFAULT_PSTROT); } } void SettingsDialog::addRotUsrButtonsProfile() { FCT_IDENTIFICATION; if ( ui->rotUsrButtonProfileNameEdit->text().isEmpty() ) { ui->rotUsrButtonProfileNameEdit->setPlaceholderText(tr("Must not be empty")); return; } if ( ui->rotUsrButtonAddProfileButton->text() == tr("Modify")) { ui->rotUsrButtonAddProfileButton->setText(tr("Add")); } RotUsrButtonsProfile profile; profile.profileName = ui->rotUsrButtonProfileNameEdit->text(); QLineEdit * const buttonEdits[] = { ui->rotUsrButton1Edit, ui->rotUsrButton2Edit, ui->rotUsrButton3Edit, ui->rotUsrButton4Edit }; QSpinBox * const spinBoxes[] = { ui->rotUsrButtonSpinBox1, ui->rotUsrButtonSpinBox2, ui->rotUsrButtonSpinBox3, ui->rotUsrButtonSpinBox4 }; for ( int i = 0; i < 4; ++i ) { profile.shortDescs[i] = buttonEdits[i]->text(); profile.bearings[i] = spinBoxes[i]->value(); } rotUsrButtonsProfManager->addProfile(profile.profileName, profile); refreshRotUsrButtonsProfilesView(); clearRotUsrButtonsProfileForm(); } void SettingsDialog::delRotUsrButtonsProfile() { FCT_IDENTIFICATION; deleteSelectedProfiles(ui->rotUsrButtonListView, [this](const QString &name) { rotUsrButtonsProfManager->removeProfile(name); }); clearRotUsrButtonsProfileForm(); } void SettingsDialog::refreshRotUsrButtonsProfilesView() { FCT_IDENTIFICATION; refreshProfileView(ui->rotUsrButtonListView, rotUsrButtonsProfManager->profileNameList()); } void SettingsDialog::doubleClickRotUsrButtonsProfile(QModelIndex i) { FCT_IDENTIFICATION; RotUsrButtonsProfile profile = rotUsrButtonsProfManager->getProfile(ui->rotUsrButtonListView->model()->data(i).toString()); ui->rotUsrButtonProfileNameEdit->setText(profile.profileName); QLineEdit * const buttonEdits[] = { ui->rotUsrButton1Edit, ui->rotUsrButton2Edit, ui->rotUsrButton3Edit, ui->rotUsrButton4Edit }; QSpinBox * const spinBoxes[] = { ui->rotUsrButtonSpinBox1, ui->rotUsrButtonSpinBox2, ui->rotUsrButtonSpinBox3, ui->rotUsrButtonSpinBox4 }; for ( int i = 0; i < 4; ++i ) { buttonEdits[i]->setText(profile.shortDescs[i]); spinBoxes[i]->setValue(profile.bearings[i]); } ui->rotUsrButtonAddProfileButton->setText(tr("Modify")); } void SettingsDialog::clearRotUsrButtonsProfileForm() { FCT_IDENTIFICATION; ui->rotUsrButtonProfileNameEdit->setPlaceholderText(QString()); ui->rotUsrButtonProfileNameEdit->clear(); QLineEdit * const buttonEdits[] = { ui->rotUsrButton1Edit, ui->rotUsrButton2Edit, ui->rotUsrButton3Edit, ui->rotUsrButton4Edit }; QSpinBox * const spinBoxes[] = { ui->rotUsrButtonSpinBox1, ui->rotUsrButtonSpinBox2, ui->rotUsrButtonSpinBox3, ui->rotUsrButtonSpinBox4 }; for ( int i = 0; i < 4; ++i ) { buttonEdits[i]->clear(); spinBoxes[i]->setValue(-1); } ui->rotUsrButtonAddProfileButton->setText(tr("Add")); } void SettingsDialog::addAntProfile() { FCT_IDENTIFICATION; if ( ui->antProfileNameEdit->text().isEmpty() ) { ui->antProfileNameEdit->setPlaceholderText(tr("Must not be empty")); return; } if ( ui->antAddProfileButton->text() == tr("Modify")) { ui->antAddProfileButton->setText(tr("Add")); } AntProfile profile; profile.profileName = ui->antProfileNameEdit->text(); profile.description = ui->antDescEdit->toPlainText(); profile.azimuthBeamWidth = ui->antAzBeamWidthSpinBox->value(); profile.azimuthOffset = ui->antAzOffsetSpinBox->value(); antProfManager->addProfile(profile.profileName, profile); refreshAntProfilesView(); clearAntProfileForm(); } void SettingsDialog::delAntProfile() { FCT_IDENTIFICATION; deleteSelectedProfiles(ui->antProfilesListView, [this](const QString &name) { antProfManager->removeProfile(name); }); clearAntProfileForm(); } void SettingsDialog::refreshAntProfilesView() { FCT_IDENTIFICATION; refreshProfileView(ui->antProfilesListView, antProfManager->profileNameList()); } void SettingsDialog::doubleClickAntProfile(QModelIndex i) { AntProfile profile = antProfManager->getProfile(ui->antProfilesListView->model()->data(i).toString()); ui->antProfileNameEdit->setText(profile.profileName); ui->antDescEdit->setPlainText(profile.description); ui->antAzBeamWidthSpinBox->setValue(profile.azimuthBeamWidth); ui->antAzOffsetSpinBox->setValue(profile.azimuthOffset); ui->antAddProfileButton->setText(tr("Modify")); } void SettingsDialog::clearAntProfileForm() { FCT_IDENTIFICATION; ui->antProfileNameEdit->setPlaceholderText(QString()); ui->antProfileNameEdit->clear(); ui->antDescEdit->clear(); ui->antAzBeamWidthSpinBox->setValue(0.0); ui->antAzOffsetSpinBox->setValue(0.0); ui->antAddProfileButton->setText(tr("Add")); } void SettingsDialog::addCWKeyProfile() { FCT_IDENTIFICATION; if ( ui->cwProfileNameEdit->text().isEmpty() ) { ui->cwProfileNameEdit->setPlaceholderText(tr("Must not be empty")); return; } if ( ! ui->cwPortEdit->text().isEmpty() && ! ui->cwPortEdit->hasAcceptableInput() ) { QMessageBox::warning(nullptr, QMessageBox::tr("QLog Warning"), QMessageBox::tr("CW Keyer port must be a valid COM port.
For Windows use COMxx, for unix-like OS use a path to device")); return; } CWKeyProfile cwKeyNewProfile; cwKeyNewProfile.model = CWKey::intToTypeID(ui->cwModelSelect->currentData().toInt()); cwKeyNewProfile.profileName = ui->cwProfileNameEdit->text(); cwKeyNewProfile.defaultSpeed = ui->cwDefaulSpeed->value(); cwKeyNewProfile.keyMode = CWKey::intToModeID(ui->cwKeyModeSelect->currentData().toInt()); cwKeyNewProfile.hostname = ( ui->cwStackedWidget->currentIndex() == STACKED_WIDGET_SERIAL_SETTING ) ? QString() : ui->cwHostNameEdit->text(); cwKeyNewProfile.netport = ( ui->cwStackedWidget->currentIndex() == STACKED_WIDGET_SERIAL_SETTING ) ? 0 : ui->cwNetPortSpin->value(); cwKeyNewProfile.portPath = ( ui->cwStackedWidget->currentIndex() == STACKED_WIDGET_SERIAL_SETTING ) ? ui->cwPortEdit->text() : QString(); cwKeyNewProfile.baudrate = ( ui->cwStackedWidget->currentIndex() == STACKED_WIDGET_SERIAL_SETTING ) ? ui->cwBaudSelect->currentText().toInt() : 0; QStringList noMorseCATSupportRigs; if ( cwKeyNewProfile.model == CWKey::MORSEOVERCAT ) { // changing Key to Morse Over CAT model // needed to verify if all rigs where the key is assigned, supports Morse over CAT const QStringList &availableRigProfileNames = rigProfManager->profileNameList(); for ( const QString &rigProfileName : availableRigProfileNames ) { qCDebug(runtime) << "Checking Rig Profile" << rigProfileName; const RigProfile &testedRig = rigProfManager->getProfile(rigProfileName); if ( testedRig.assignedCWKey == cwKeyNewProfile.profileName ) { if ( Rig::instance()->getRigCaps(static_cast(testedRig.driver), testedRig.model).canSendMorse ) { qCDebug(runtime) << testedRig.profileName << " has morse support - OK"; } else { qCDebug(runtime) << "no morse support for " << testedRig.profileName << " - must not change"; noMorseCATSupportRigs << testedRig.profileName; } } } } cwKeyNewProfile.paddleSwap = cwKeyNewProfile.model == CWKey::WINKEY_KEYER && cwKeyNewProfile.keyMode != CWKey::SINGLE_PADDLE && ui->cwSwapPaddlesCheckbox->isEnabled() && ui->cwSwapPaddlesCheckbox->isChecked(); cwKeyNewProfile.paddleOnlySidetone = cwKeyNewProfile.model == CWKey::WINKEY_KEYER && ui->cwPaddleOnlySidetoneCheckbox->isEnabled() && ui->cwPaddleOnlySidetoneCheckbox->isChecked(); cwKeyNewProfile.sidetoneFrequency = ( cwKeyNewProfile.model == CWKey::WINKEY_KEYER || cwKeyNewProfile.model == CWKey::CWDAEMON_KEYER ) ? ui->cwSidetoneFreqCombo->currentData().toInt() : CWWinKey::defaultSidetoneFrequency(); if ( ! noMorseCATSupportRigs.isEmpty() ) { QMessageBox::warning(nullptr, QMessageBox::tr("QLog Warning"), QMessageBox::tr("Cannot change the CW Keyer Model to Morse over CAT
No Morse over CAT support for Rig(s) %1").arg(noMorseCATSupportRigs.join(", "))); return; } if ( ui->cwAddProfileButton->text() == tr("Modify")) { ui->cwAddProfileButton->setText(tr("Add")); } cwKeyProfManager->addProfile(cwKeyNewProfile.profileName, cwKeyNewProfile); refreshCWKeyProfilesView(); clearCWKeyProfileForm(); refreshRigAssignedCWKeyCombo(); } void SettingsDialog::delCWKeyProfile() { FCT_IDENTIFICATION; const QModelIndexList cwSelectedRows = ui->cwProfilesListView->selectionModel()->selectedRows(); for ( const QModelIndex &index : cwSelectedRows ) { QStringList dependentRigs; QString removedCWProfile = ui->cwProfilesListView->model()->data(index).toString(); const QStringList &availableRigProfileNames = rigProfManager->profileNameList(); /* needed to verify whether removed Key is not used in Rig Profile as an assigned Key*/ for ( const QString &rigProfileName : availableRigProfileNames ) { qCDebug(runtime) << "Checking Rig Profile" << rigProfileName; RigProfile testedRig = rigProfManager->getProfile(rigProfileName); if ( testedRig.assignedCWKey == removedCWProfile ) dependentRigs << testedRig.profileName; } if ( dependentRigs.isEmpty() ) { cwKeyProfManager->removeProfile(removedCWProfile); ui->cwProfilesListView->model()->removeRow(index.row()); } else { QMessageBox::warning(nullptr, QMessageBox::tr("QLog Warning"), QMessageBox::tr("Cannot delete the CW Keyer Profile
The CW Key Profile is used by Rig(s): %1").arg(dependentRigs.join(", "))); } dependentRigs.clear(); } ui->cwProfilesListView->clearSelection(); clearCWKeyProfileForm(); refreshRigAssignedCWKeyCombo(); } void SettingsDialog::refreshCWKeyProfilesView() { FCT_IDENTIFICATION; refreshProfileView(ui->cwProfilesListView, cwKeyProfManager->profileNameList()); } void SettingsDialog::doubleClickCWKeyProfile(QModelIndex i) { FCT_IDENTIFICATION; CWKeyProfile profile = cwKeyProfManager->getProfile(ui->cwProfilesListView->model()->data(i).toString()); ui->cwProfileNameEdit->setText(profile.profileName); ui->cwModelSelect->setCurrentIndex(ui->cwModelSelect->findData(profile.model)); ui->cwDefaulSpeed->setValue(profile.defaultSpeed); ui->cwKeyModeSelect->setCurrentIndex(ui->cwKeyModeSelect->findData(profile.keyMode)); ui->cwPortEdit->setText(profile.portPath); ui->cwBaudSelect->setCurrentText(QString::number(profile.baudrate)); ui->cwHostNameEdit->setText(profile.hostname); ui->cwNetPortSpin->setValue(profile.netport); ui->cwSwapPaddlesCheckbox->setChecked(profile.paddleSwap); ui->cwPaddleOnlySidetoneCheckbox->setChecked(profile.paddleOnlySidetone); { int idx = ui->cwSidetoneFreqCombo->findData(profile.sidetoneFrequency); ui->cwSidetoneFreqCombo->setCurrentIndex(idx >= 0 ? idx : ui->cwSidetoneFreqCombo->findData(CWWinKey::defaultSidetoneFrequency())); } ui->cwAddProfileButton->setText(tr("Modify")); } void SettingsDialog::clearCWKeyProfileForm() { FCT_IDENTIFICATION; ui->cwProfileNameEdit->setPlaceholderText(QString()); ui->cwProfileNameEdit->clear(); ui->cwModelSelect->setCurrentIndex(ui->cwModelSelect->findData(DEFAULT_CWKEY_MODEL)); ui->cwKeyModeSelect->setCurrentIndex(ui->cwKeyModeSelect->findData(CWKey::IAMBIC_B)); ui->cwDefaulSpeed->setValue(CW_DEFAULT_KEY_SPEED); ui->cwPortEdit->clear(); ui->cwBaudSelect->setCurrentIndex(0); ui->cwHostNameEdit->clear(); ui->cwNetPortSpin->setValue(CW_NET_CWDAEMON_PORT); ui->cwSwapPaddlesCheckbox->setChecked(false); ui->cwPaddleOnlySidetoneCheckbox->setChecked(false); ui->cwSidetoneFreqCombo->setCurrentIndex(ui->cwSidetoneFreqCombo->findData(CWWinKey::defaultSidetoneFrequency())); ui->cwAddProfileButton->setText(tr("Add")); } void SettingsDialog::addCWShortcutProfile() { FCT_IDENTIFICATION; if ( ui->cwShortcutProfileNameEdit->text().isEmpty() ) { ui->cwShortcutProfileNameEdit->setPlaceholderText(tr("Must not be empty")); return; } if ( ui->cwShortcutAddProfileButton->text() == tr("Modify")) ui->cwShortcutAddProfileButton->setText(tr("Add")); CWShortcutProfile profile; profile.profileName = ui->cwShortcutProfileNameEdit->text(); QLineEdit * const shortEdits[] = { ui->cwShortcutF1ShortEdit, ui->cwShortcutF2ShortEdit, ui->cwShortcutF3ShortEdit, ui->cwShortcutF4ShortEdit, ui->cwShortcutF5ShortEdit, ui->cwShortcutF6ShortEdit, ui->cwShortcutF7ShortEdit }; QLineEdit * const macroEdits[] = { ui->cwShortcutF1MacroEdit, ui->cwShortcutF2MacroEdit, ui->cwShortcutF3MacroEdit, ui->cwShortcutF4MacroEdit, ui->cwShortcutF5MacroEdit, ui->cwShortcutF6MacroEdit, ui->cwShortcutF7MacroEdit }; for ( int i = 0; i < 7; ++i ) { profile.shortDescs[i] = shortEdits[i]->text(); profile.macros[i] = macroEdits[i]->text(); } cwShortcutProfManager->addProfile(profile.profileName, profile); refreshCWShortcutProfilesView(); clearCWShortcutProfileForm(); } void SettingsDialog::delCWShortcutProfile() { FCT_IDENTIFICATION; deleteSelectedProfiles(ui->cwShortcutListView, [this](const QString &name) { cwShortcutProfManager->removeProfile(name); }); clearCWShortcutProfileForm(); } void SettingsDialog::refreshCWShortcutProfilesView() { FCT_IDENTIFICATION; refreshProfileView(ui->cwShortcutListView, cwShortcutProfManager->profileNameList()); } void SettingsDialog::doubleClickCWShortcutProfile(QModelIndex i) { FCT_IDENTIFICATION; CWShortcutProfile profile = cwShortcutProfManager->getProfile(ui->cwShortcutListView->model()->data(i).toString()); ui->cwShortcutProfileNameEdit->setText(profile.profileName); QLineEdit * const shortEdits[] = { ui->cwShortcutF1ShortEdit, ui->cwShortcutF2ShortEdit, ui->cwShortcutF3ShortEdit, ui->cwShortcutF4ShortEdit, ui->cwShortcutF5ShortEdit, ui->cwShortcutF6ShortEdit, ui->cwShortcutF7ShortEdit }; QLineEdit * const macroEdits[] = { ui->cwShortcutF1MacroEdit, ui->cwShortcutF2MacroEdit, ui->cwShortcutF3MacroEdit, ui->cwShortcutF4MacroEdit, ui->cwShortcutF5MacroEdit, ui->cwShortcutF6MacroEdit, ui->cwShortcutF7MacroEdit }; for ( int i = 0; i < 7; ++i ) { shortEdits[i]->setText(profile.shortDescs[i]); macroEdits[i]->setText(profile.macros[i]); } ui->cwShortcutAddProfileButton->setText(tr("Modify")); } void SettingsDialog::clearCWShortcutProfileForm() { FCT_IDENTIFICATION; ui->cwShortcutProfileNameEdit->setPlaceholderText(QString()); ui->cwShortcutProfileNameEdit->clear(); QLineEdit * const shortEdits[] = { ui->cwShortcutF1ShortEdit, ui->cwShortcutF2ShortEdit, ui->cwShortcutF3ShortEdit, ui->cwShortcutF4ShortEdit, ui->cwShortcutF5ShortEdit, ui->cwShortcutF6ShortEdit, ui->cwShortcutF7ShortEdit }; QLineEdit * const macroEdits[] = { ui->cwShortcutF1MacroEdit, ui->cwShortcutF2MacroEdit, ui->cwShortcutF3MacroEdit, ui->cwShortcutF4MacroEdit, ui->cwShortcutF5MacroEdit, ui->cwShortcutF6MacroEdit, ui->cwShortcutF7MacroEdit }; for ( int i = 0; i < 7; ++i ) { shortEdits[i]->clear(); macroEdits[i]->clear(); } ui->cwShortcutAddProfileButton->setText(tr("Add")); } void SettingsDialog::refreshRigProfilesView() { FCT_IDENTIFICATION; refreshProfileView(ui->rigProfilesListView, rigProfManager->profileNameList()); } void SettingsDialog::refreshStationProfilesView() { FCT_IDENTIFICATION; refreshProfileView(ui->stationProfilesListView, stationProfManager->profileNameList()); } void SettingsDialog::addStationProfile() { FCT_IDENTIFICATION; if ( ui->stationProfileNameEdit->text().isEmpty() ) { ui->stationProfileNameEdit->setPlaceholderText(tr("Must not be empty")); return; } if ( ui->stationCallsignEdit->text().isEmpty() ) { ui->stationCallsignEdit->setPlaceholderText(tr("Must not be empty")); return; } if ( ui->stationLocatorEdit->text().isEmpty() ) { ui->stationLocatorEdit->setPlaceholderText(tr("Must not be empty")); return; } if ( ! ui->stationCallsignEdit->hasAcceptableInput() ) { QMessageBox::warning(nullptr, QMessageBox::tr("QLog Warning"), QMessageBox::tr("Callsign has an invalid format")); return; } if ( !ui->stationOperatorCallsignEdit->text().isEmpty() && ! ui->stationOperatorCallsignEdit->hasAcceptableInput() ) { QMessageBox::warning(nullptr, QMessageBox::tr("QLog Warning"), QMessageBox::tr("Operator Callsign has an invalid format")); return; } if ( ! ui->stationLocatorEdit->hasAcceptableInput() ) { QMessageBox::warning(nullptr, QMessageBox::tr("QLog Warning"), QMessageBox::tr("Gridsquare has an invalid format")); return; } if ( ! ui->stationVUCCEdit->text().isEmpty() ) { if ( ! ui->stationVUCCEdit->hasAcceptableInput() ) { QMessageBox::warning(nullptr, QMessageBox::tr("QLog Warning"), QMessageBox::tr("VUCC Grids have an invalid format (must be 2 or 4 Gridsquares separated by ',')")); return; } } if ( ui->stationCountryCombo->currentIndex() == 0 ) { QMessageBox::warning(nullptr, QMessageBox::tr("QLog Warning"), QMessageBox::tr("Country must not be empty")); return; } if ( ui->stationCQZEdit->text().isEmpty() ) { QMessageBox::warning(nullptr, QMessageBox::tr("QLog Warning"), QMessageBox::tr("CQZ must not be empty")); return; } if ( ui->stationITUEdit->text().isEmpty() ) { QMessageBox::warning(nullptr, QMessageBox::tr("QLog Warning"), QMessageBox::tr("ITU must not be empty")); return; } if ( ui->stationAddProfileButton->text() == tr("Modify")) { ui->stationAddProfileButton->setText(tr("Add")); } StationProfile profile; profile.profileName = ui->stationProfileNameEdit->text(); profile.callsign = ui->stationCallsignEdit->text().toUpper(); profile.locator = ui->stationLocatorEdit->text().toUpper(); profile.operatorName = ui->stationOperatorEdit->text(); profile.operatorCallsign = ui->stationOperatorCallsignEdit->text().toUpper(); profile.qthName = ui->stationQTHEdit->text(); profile.iota = ui->stationIOTAEdit->text().toUpper(); profile.sota = ui->stationSOTAEdit->text().toUpper(); profile.pota = ui->stationPOTAEdit->text().toUpper(); profile.sig = ui->stationSIGEdit->text(); profile.sigInfo = ui->stationSIGInfoEdit->text(); profile.vucc = ui->stationVUCCEdit->text().toUpper(); profile.wwff = ui->stationWWFFEdit->text().toUpper(); profile.cqz = ui->stationCQZEdit->text().toInt(); profile.ituz = ui->stationITUEdit->text().toInt(); profile.county = ui->stationCountyEdit->text(); profile.darcDOK = ui->stationDarcDokEdit->text().toUpper(); int row = ui->stationCountryCombo->currentIndex(); const QModelIndex &idxDXCC = ui->stationCountryCombo->model()->index(row,0); const QVariant &dataDXCC = ui->stationCountryCombo->model()->data(idxDXCC); const QModelIndex &idxCountryEN = ui->stationCountryCombo->model()->index(row,2); QVariant dataCountryEN = ui->stationCountryCombo->model()->data(idxCountryEN); profile.dxcc = dataDXCC.toInt(); profile.country = dataCountryEN.toString(); stationProfManager->addProfile(profile.profileName, profile); refreshStationProfilesView(); clearStationProfileForm(); } void SettingsDialog::deleteStationProfile() { FCT_IDENTIFICATION; deleteSelectedProfiles(ui->stationProfilesListView, [this](const QString &name) { stationProfManager->removeProfile(name); }); clearStationProfileForm(); } void SettingsDialog::doubleClickStationProfile(QModelIndex i) { FCT_IDENTIFICATION; StationProfile profile = stationProfManager->getProfile(ui->stationProfilesListView->model()->data(i).toString()); ui->stationProfileNameEdit->setText(profile.profileName); ui->stationCallsignEdit->blockSignals(true); ui->stationCallsignEdit->setText(profile.callsign); setValidationResultColor(ui->stationCallsignEdit); ui->stationCallsignEdit->blockSignals(false); ui->stationLocatorEdit->setText(profile.locator); ui->stationOperatorEdit->setText(profile.operatorName); ui->stationOperatorCallsignEdit->setText(profile.operatorCallsign); ui->stationQTHEdit->setText(profile.qthName); ui->stationIOTAEdit->setText(profile.iota); ui->stationSOTAEdit->blockSignals(true); ui->stationSOTAEdit->setText(profile.sota); ui->stationSOTAEdit->blockSignals(false); ui->stationPOTAEdit->blockSignals(true); ui->stationPOTAEdit->setText(profile.pota); ui->stationPOTAEdit->blockSignals(false); ui->stationSIGEdit->setText(profile.sig); ui->stationSIGInfoEdit->setText(profile.sigInfo); ui->stationVUCCEdit->setText(profile.vucc); ui->stationWWFFEdit->blockSignals(true); ui->stationWWFFEdit->setText(profile.wwff); ui->stationWWFFEdit->blockSignals(false); ui->stationCQZEdit->setText(QString::number(profile.cqz)); ui->stationITUEdit->setText(QString::number(profile.ituz)); ui->stationCountyEdit->setText(profile.county); ui->stationDarcDokEdit->setText(profile.darcDOK); const QModelIndexList &countryIndex = ui->stationCountryCombo->model()->match(ui->stationCountryCombo->model()->index(0,0), Qt::DisplayRole, profile.dxcc, 1, Qt::MatchExactly); if ( countryIndex.size() >= 1 ) ui->stationCountryCombo->setCurrentIndex(countryIndex.at(0).row()); updateCountyCompleter(profile.dxcc); ui->stationAddProfileButton->setText(tr("Modify")); } void SettingsDialog::clearStationProfileForm() { FCT_IDENTIFICATION; ui->stationProfileNameEdit->clear(); ui->stationProfileNameEdit->setPlaceholderText(QString()); ui->stationCallsignEdit->clear(); ui->stationCallsignEdit->setPlaceholderText(QString()); ui->stationLocatorEdit->clear(); ui->stationLocatorEdit->setPlaceholderText(QString()); ui->stationOperatorEdit->clear(); ui->stationOperatorCallsignEdit->clear(); ui->stationQTHEdit->clear(); ui->stationSOTAEdit->clear(); ui->stationPOTAEdit->clear(); ui->stationIOTAEdit->clear(); ui->stationSIGEdit->clear(); ui->stationSIGInfoEdit->clear(); ui->stationVUCCEdit->clear(); ui->stationWWFFEdit->clear(); ui->stationCQZEdit->clear(); ui->stationITUEdit->clear(); ui->stationCountryCombo->setCurrentIndex(0); ui->stationCountyEdit->clear(); ui->stationDarcDokEdit->clear(); ui->stationAddProfileButton->setText(tr("Add")); } /* This function is called when an user change Rig Combobox */ /* new rig entered */ void SettingsDialog::rigChanged(int index) { FCT_IDENTIFICATION; qCDebug(function_parameters)<rigModelSelect->currentData().toInt(); Rig::DriverID driverID = static_cast(ui->rigInterfaceCombo->currentData().toInt()); const RigCaps &caps = Rig::instance()->getRigCaps(driverID, rigID); if ( driverID == Rig::OMNIRIG_DRIVER || driverID == Rig::OMNIRIGV2_DRIVER ) { ui->rigPortTypeCombo->setCurrentIndex(RIGPORT_SPECIAL_OMNIRIG_INDEX); ui->rigPortTypeCombo->setEnabled(false); } else { ui->rigPortTypeCombo->setCurrentIndex(caps.isNetworkOnly ? RIGPORT_NETWORK_INDEX : RIGPORT_SERIAL_INDEX); ui->rigPortTypeCombo->setEnabled(!caps.isNetworkOnly); if ( !caps.isNetworkOnly ) { ui->rigDataBitsSelect->setCurrentText(QString::number(caps.serialDataBits)); ui->rigStopBitsSelect->setCurrentText(QString::number(caps.serialStopBits)); } } ui->rigPollIntervalSpinBox->setEnabled(caps.needPolling); /* Set default rig Caps */ ui->rigGetFreqCheckBox->setEnabled(true); ui->rigGetFreqCheckBox->setChecked(true); ui->rigGetModeCheckBox->setEnabled(true); ui->rigGetModeCheckBox->setChecked(true); ui->rigGetVFOCheckBox->setEnabled(true); ui->rigGetVFOCheckBox->setChecked(true); ui->rigGetPWRCheckBox->setEnabled(true); ui->rigGetPWRCheckBox->setChecked(true); ui->rigGetRITCheckBox->setEnabled(true); ui->rigGetRITCheckBox->setChecked(false); ui->rigGetXITCheckBox->setEnabled(true); ui->rigGetXITCheckBox->setChecked(false); ui->rigGetPTTStateCheckBox->setEnabled(true); if ( ui->rigPortTypeCombo->currentIndex() == RIGPORT_SPECIAL_OMNIRIG_INDEX || driverID == Rig::TCI_DRIVER ) ui->rigGetPTTStateCheckBox->setChecked(true); else ui->rigGetPTTStateCheckBox->setChecked(false); ui->rigQSYWipingCheckBox->setEnabled(true); ui->rigQSYWipingCheckBox->setChecked(true); ui->rigGetKeySpeedCheckBox->setEnabled(true); ui->rigGetKeySpeedCheckBox->setChecked(true); ui->rigKeySpeedSyncCheckBox->setEnabled(true); ui->rigKeySpeedSyncCheckBox->setChecked(false); ui->rigDXSpots2RigCheckBox->setEnabled(true); ui->rigDXSpots2RigCheckBox->setChecked(false); ui->rigGetSplitCheckBox->setEnabled(true); ui->rigGetSplitCheckBox->setChecked(true); setUIBasedOnRigCaps(caps); } void SettingsDialog::rotChanged(int index) { FCT_IDENTIFICATION; qCDebug(function_parameters) << index; int rotID = ui->rotModelSelect->currentData().toInt(); Rotator::DriverID driverID = static_cast(ui->rotInterfaceCombo->currentData().toInt()); const RotCaps &caps = Rotator::instance()->getRotCaps(driverID, rotID); if ( caps.isNetworkOnly ) { ui->rotPortTypeCombo->setCurrentIndex(ROTPORT_NETWORK_INDEX); ui->rotPortTypeCombo->setEnabled(false); } else ui->rotPortTypeCombo->setEnabled(true); if ( ui->rotPortTypeCombo->currentIndex() == RIGPORT_SERIAL_INDEX ) { ui->rotDataBitsSelect->setCurrentText(QString::number(caps.serialDataBits)); ui->rotStopBitsSelect->setCurrentText(QString::number(caps.serialStopBits)); } } void SettingsDialog::cwKeyChanged(int) { FCT_IDENTIFICATION; const CWKey::CWKeyTypeID currentType = CWKey::intToTypeID(ui->cwModelSelect->currentData().toInt()); ui->cwDefaulSpeed->setValue(CW_DEFAULT_KEY_SPEED); ui->cwStackedWidget->setCurrentIndex(( CWKey::isNetworkKey(currentType) ) ? STACKED_WIDGET_NETWORK_SETTING : STACKED_WIDGET_SERIAL_SETTING); if ( currentType == CWKey::MORSEOVERCAT || currentType == CWKey::CWDAEMON_KEYER || currentType == CWKey::FLDIGI_KEYER ) { ui->cwBaudSelect->setEnabled(false); ui->cwPortEdit->clear(); ui->cwPortEdit->setEnabled(false); ui->cwKeyModeSelect->setEnabled(false); ui->cwDefaulSpeed->setEnabled(true); ui->cwSwapPaddlesCheckbox->setEnabled(false); ui->cwPaddleOnlySidetoneCheckbox->setEnabled(false); ui->cwSidetoneFreqCombo->setEnabled(currentType == CWKey::CWDAEMON_KEYER); if ( currentType == CWKey::CWDAEMON_KEYER ) { ui->cwNetPortSpin->setValue(CW_NET_CWDAEMON_PORT); } else if ( currentType == CWKey::FLDIGI_KEYER ) { ui->cwDefaulSpeed->setEnabled(false); ui->cwNetPortSpin->setValue(CW_NET_FLDIGI_PORT); ui->cwDefaulSpeed->setValue(CW_KEY_SPEED_DISABLED); } return; } else // WINKEYS { ui->cwBaudSelect->setEnabled(true); ui->cwPortEdit->setEnabled(true); ui->cwKeyModeSelect->setEnabled(true); ui->cwDefaulSpeed->setEnabled(true); ui->cwSwapPaddlesCheckbox->setEnabled(true); ui->cwPaddleOnlySidetoneCheckbox->setEnabled(currentType == CWKey::WINKEY_KEYER); ui->cwSidetoneFreqCombo->setEnabled(currentType == CWKey::WINKEY_KEYER); } ui->cwBaudSelect->setCurrentText(( currentType == CWKey::WINKEY_KEYER ) ? "1200" : "115200"); } void SettingsDialog::cwModeChanged(int) { FCT_IDENTIFICATION; ui->cwSwapPaddlesCheckbox->setEnabled( CWKey::intToTypeID(ui->cwModelSelect->currentData().toInt()) == CWKey::WINKEY_KEYER && CWKey::intToModeID(ui->cwKeyModeSelect->currentData().toInt()) != CWKey::SINGLE_PADDLE ); } void SettingsDialog::rigStackWidgetChanged(int) { FCT_IDENTIFICATION; ui->rigPortEdit->clear(); ui->rigHostNameEdit->clear(); } void SettingsDialog::rotStackWidgetChanged(int) { FCT_IDENTIFICATION; ui->rotPortEdit->clear(); ui->rotHostNameEdit->clear(); } void SettingsDialog::cwKeyStackWidgetChanged(int) { FCT_IDENTIFICATION; ui->cwPortEdit->clear(); ui->cwHostNameEdit->clear(); } void SettingsDialog::tqslPathBrowse() { FCT_IDENTIFICATION; const QString &lastPath = ( ui->tqslPathEdit->text().isEmpty() ) ? QDir::rootPath() : ui->tqslPathEdit->text(); QString filename = QFileDialog::getOpenFileName(this, tr("Select File"), lastPath, #if defined(Q_OS_WIN) "TQSL (*.exe)", #elif defined(Q_OS_MACOS) "TQSL (*.app)", #else "TQSL (tqsl)", #endif nullptr, #if defined(Q_OS_LINUX) // Do not use the Native Dialog under Linux because the dialog is case-sensitive. // QT variant looks different but it is case-insensitive. // More information: // https://stackoverflow.com/questions/34858220/qt-how-to-set-a-case-insensitive-filter-on-qfiledialog // https://bugreports.qt.io/browse/QTBUG-51712 QFileDialog::DontUseNativeDialog #else QFileDialog::Options() #endif ); if ( !filename.isEmpty() ) { ui->tqslPathEdit->setText(filename); } } void SettingsDialog::tqslAutoDetect() { FCT_IDENTIFICATION; const QString detectedPath = LotwBase::findTQSLPath(); if ( detectedPath.isEmpty() ) { updateTQSLVersionLabel(); QMessageBox::warning(this, tr("Auto Detect"), tr("TQSL was not found on this system.\n" "Please install TQSL or specify the path manually.")); } else { ui->tqslPathEdit->setText(detectedPath); } } void SettingsDialog::updateTQSLVersionLabel() { FCT_IDENTIFICATION; const QString path = ui->tqslPathEdit->text().trimmed(); const bool isAutoDetect = path.isEmpty(); const QString resolvedPath = isAutoDetect ? LotwBase::findTQSLPath() : path; const QString NOTFOUND = QString("✗ %1").arg(tr("Not found")); if ( resolvedPath.isEmpty() ) { ui->tqslVersionValueLabel->setText(NOTFOUND); return; } const TQSLVersion version = LotwBase::getTQSLVersion(resolvedPath); if ( !version.isValid() ) { ui->tqslVersionValueLabel->setText(NOTFOUND); return; } const QString versionStr = QString("✓ %1.%2.%3") .arg(version.major) .arg(version.minor) .arg(version.patch); if ( isAutoDetect ) ui->tqslVersionValueLabel->setText(QString("%1 (%2)").arg(versionStr, resolvedPath)); else ui->tqslVersionValueLabel->setText(versionStr); } void SettingsDialog::stationCallsignChanged() { FCT_IDENTIFICATION; setValidationResultColor(ui->stationCallsignEdit); const QString &callsign = ui->stationCallsignEdit->text(); const DxccEntity &dxccEntity = Data::instance()->lookupDxcc(callsign); if ( dxccEntity.dxcc ) { ui->stationITUEdit->setText(QString::number(dxccEntity.ituz)); ui->stationCQZEdit->setText(QString::number(dxccEntity.cqz)); const QModelIndexList &countryIndex = ui->stationCountryCombo->model()->match(ui->stationCountryCombo->model()->index(0,0), Qt::DisplayRole, dxccEntity.dxcc, 1, Qt::MatchExactly); if ( countryIndex.size() >= 1 ) ui->stationCountryCombo->setCurrentIndex(countryIndex.at(0).row()); } else { ui->stationCountryCombo->setCurrentIndex(0); ui->stationCQZEdit->clear(); ui->stationITUEdit->clear(); } } void SettingsDialog::adjustLocatorTextColor() { FCT_IDENTIFICATION; setValidationResultColor(ui->stationLocatorEdit); } void SettingsDialog::adjustVUCCLocatorTextColor() { FCT_IDENTIFICATION; setValidationResultColor(ui->stationVUCCEdit); } void SettingsDialog::adjustRotCOMPortTextColor() { FCT_IDENTIFICATION; setValidationResultColor(ui->rotPortEdit); } void SettingsDialog::adjustRigCOMPortTextColor() { FCT_IDENTIFICATION; setValidationResultColor(ui->rigPortEdit); } void SettingsDialog::adjustCWKeyCOMPortTextColor() { FCT_IDENTIFICATION; setValidationResultColor(ui->cwPortEdit); } void SettingsDialog::adjustOperatorTextColor() { FCT_IDENTIFICATION; setValidationResultColor(ui->stationOperatorCallsignEdit); } void SettingsDialog::cancelled() { FCT_IDENTIFICATION; if ( stationProfManager->profileNameList().isEmpty() ) { QMessageBox::warning(nullptr, QMessageBox::tr("QLog Warning"), QMessageBox::tr("Please, define at least one Station Locations Profile")); return; } reject(); } void SettingsDialog::sotaChanged(const QString &newSOTA) { FCT_IDENTIFICATION; ui->stationSOTAEdit->setCompleter(( newSOTA.length() >= 3 ) ? sotaCompleter : nullptr); ui->stationQTHEdit->clear(); ui->stationLocatorEdit->clear(); } void SettingsDialog::sotaEditFinished() { FCT_IDENTIFICATION; SOTAEntity sotaInfo = Data::instance()->lookupSOTA(ui->stationSOTAEdit->text()); if ( sotaInfo.summitCode.toUpper() == ui->stationSOTAEdit->text().toUpper() && !sotaInfo.summitName.isEmpty() ) { sotaFallback = false; ui->stationQTHEdit->setText(sotaInfo.summitName); Gridsquare SOTAGrid(sotaInfo.latitude, sotaInfo.longitude); if ( SOTAGrid.isValid() ) ui->stationLocatorEdit->setText(SOTAGrid.getGrid()); } else if ( !ui->stationPOTAEdit->text().isEmpty() && !potaFallback ) { potaFallback = true; potaEditFinished(); } else if ( !ui->stationWWFFEdit->text().isEmpty() && !wwffFallback ) { wwffFallback = true; wwffEditFinished(); } } void SettingsDialog::potaChanged(const QString &newPOTA) { FCT_IDENTIFICATION; ui->stationPOTAEdit->setCompleter(( newPOTA.length() >= 3 ) ? potaCompleter : nullptr); ui->stationQTHEdit->clear(); ui->stationLocatorEdit->clear(); } void SettingsDialog::potaEditFinished() { FCT_IDENTIFICATION; QStringList potaList = ui->stationPOTAEdit->text().split("@"); QString potaString; if ( !potaList.isEmpty() ) potaString = potaList[0]; else potaString = ui->stationPOTAEdit->text(); POTAEntity potaInfo = Data::instance()->lookupPOTA(potaString); if ( potaInfo.reference.toUpper() == potaString.toUpper() && !potaInfo.name.isEmpty() ) { potaFallback = false; ui->stationQTHEdit->setText(potaInfo.name); Gridsquare POTAGrid(potaInfo.grid); if ( POTAGrid.isValid() ) ui->stationLocatorEdit->setText(POTAGrid.getGrid()); } else if ( !ui->stationSOTAEdit->text().isEmpty() && !sotaFallback ) { sotaEditFinished(); } else if ( !ui->stationWWFFEdit->text().isEmpty() && !wwffFallback ) { wwffEditFinished(); } } void SettingsDialog::wwffChanged(const QString &newWWFF) { FCT_IDENTIFICATION; ui->stationWWFFEdit->setCompleter(( newWWFF.length() >= 3 ) ? wwffCompleter : nullptr); if ( ui->stationSOTAEdit->text().isEmpty() ) { //do not clear IOTA - IOTA info seems to be not reliable from WWFF and IOTA //can be added manually by operator ui->stationQTHEdit->clear(); } } void SettingsDialog::wwffEditFinished() { FCT_IDENTIFICATION; WWFFEntity wwffInfo = Data::instance()->lookupWWFF(ui->stationWWFFEdit->text()); if ( wwffInfo.reference.toUpper() == ui->stationWWFFEdit->text().toUpper() && !wwffInfo.name.isEmpty() && ui->stationQTHEdit->text().isEmpty() ) { wwffFallback = false; ui->stationQTHEdit->setText(wwffInfo.name); if ( ! wwffInfo.iota.isEmpty() && wwffInfo.iota != "-" ) ui->stationIOTAEdit->setText(wwffInfo.iota.toUpper()); } else if ( !ui->stationSOTAEdit->text().isEmpty() && !sotaFallback ) { sotaEditFinished(); } else if ( !ui->stationPOTAEdit->text().isEmpty() && !potaFallback ) { potaEditFinished(); } } void SettingsDialog::primaryCallbookChanged(int index) { FCT_IDENTIFICATION; qCDebug(function_parameters) << index; QString primaryCallbookSelection = ui->primaryCallbookCombo->itemData(index).toString(); if ( primaryCallbookSelection == GenericCallbook::CALLBOOK_NAME ) { ui->secondaryCallbookCombo->clear(); ui->secondaryCallbookCombo->setEnabled(false); } else if ( primaryCallbookSelection == HamQTHCallbook::CALLBOOK_NAME ) { ui->secondaryCallbookCombo->setEnabled(true); ui->secondaryCallbookCombo->clear(); ui->secondaryCallbookCombo->addItem(tr("Disabled"), QVariant(GenericCallbook::CALLBOOK_NAME)); ui->secondaryCallbookCombo->addItem(tr("QRZ.com"), QVariant(QRZCallbook::CALLBOOK_NAME)); } else if ( primaryCallbookSelection == QRZCallbook::CALLBOOK_NAME ) { ui->secondaryCallbookCombo->setEnabled(true); ui->secondaryCallbookCombo->clear(); ui->secondaryCallbookCombo->addItem(tr("Disabled"), QVariant(GenericCallbook::CALLBOOK_NAME)); ui->secondaryCallbookCombo->addItem(tr("HamQTH"), QVariant(HamQTHCallbook::CALLBOOK_NAME)); } } void SettingsDialog::secondaryCallbookChanged(int index) { FCT_IDENTIFICATION; qCDebug(function_parameters) << index; } void SettingsDialog::assignedKeyChanged(int index) { FCT_IDENTIFICATION; qCDebug(function_parameters) << index; ui->rigKeySpeedSyncCheckBox->setEnabled(true); ui->rigKeySpeedSyncCheckBox->setChecked(false); const RigCaps &caps = Rig::instance()->getRigCaps(static_cast(ui->rigInterfaceCombo->currentData().toInt()), ui->rigModelSelect->currentData().toInt()); setUIBasedOnRigCaps(caps); } void SettingsDialog::testWebLookupURL() { FCT_IDENTIFICATION; QDesktopServices::openUrl(GenericCallbook::getWebLookupURL(stationProfManager->getCurProfile1().callsign, ui->webLookupURLEdit->text())); } void SettingsDialog::joinMulticastChanged(int state) { FCT_IDENTIFICATION; ui->wsjtMulticastAddressLabel->setVisible(state); ui->wsjtMulticastAddressEdit->setVisible(state); ui->wsjtMulticastTTLLabel->setVisible(state); ui->wsjtMulticastTTLSpin->setVisible(state); } void SettingsDialog::adjustWSJTXMulticastAddrTextColor() { FCT_IDENTIFICATION; setValidationResultColor(ui->wsjtMulticastAddressEdit); } void SettingsDialog::hrdlogSettingChanged() { FCT_IDENTIFICATION; ui->hrdlogOnAirCheckBox->setEnabled(!ui->hrdlogCallsignEdit->text().isEmpty() && !ui->hrdlogUploadCodeEdit->text().isEmpty()); if ( !ui->hrdlogOnAirCheckBox->isEnabled() ) ui->hrdlogOnAirCheckBox->setChecked(false); } void SettingsDialog::clublogSettingChanged() { FCT_IDENTIFICATION; ui->clublogUploadImmediatelyCheckbox->setEnabled(!ui->clublogEmailEdit->text().isEmpty() && !ui->clublogPasswordEdit->text().isEmpty()); if ( !ui->clublogUploadImmediatelyCheckbox->isEnabled() ) ui->clublogUploadImmediatelyCheckbox->setChecked(false); } void SettingsDialog::updateDateFormatResult() { FCT_IDENTIFICATION; ui->dateFormatResultLabel->setText(QDate::currentDate().toString(ui->dateFormatStringEdit->text())); } void SettingsDialog::rigFlowControlChanged(int) { FCT_IDENTIFICATION; // if HW handshake is enabled then RTS must be None bool isHWControlEnabled = (ui->rigFlowControlSelect->currentData().toString() == SerialPort::SERIAL_FLOWCONTROL_HARDWARE); if ( isHWControlEnabled ) setComboByData(ui->rigRTSCombo, SerialPort::SERIAL_SIGNAL_NONE); ui->rigRTSCombo->setEnabled(!isHWControlEnabled); } void SettingsDialog::showRigctldAdvanced() { FCT_IDENTIFICATION; RigctldAdvancedDialog dialog(this); dialog.setPath(rigctldPath); dialog.setArgs(rigctldArgs); if (dialog.exec() == QDialog::Accepted) { rigctldPath = dialog.getPath(); rigctldArgs = dialog.getArgs(); } } void SettingsDialog::rigShareChanged(int) { FCT_IDENTIFICATION; updateRigShareEnabled(); } void SettingsDialog::updateRigShareEnabled() { FCT_IDENTIFICATION; Rig::DriverID driverID = static_cast(ui->rigInterfaceCombo->currentData().toInt()); int portType = ui->rigPortTypeCombo->currentIndex(); // Share rigctld is only available for Hamlib driver with serial connection bool canShare = (driverID == Rig::HAMLIB_DRIVER) && (portType == RIGPORT_SERIAL_INDEX); ui->rigShareCheckBox->setEnabled(canShare); ui->rigSharePortSpinBox->setEnabled(canShare && ui->rigShareCheckBox->isChecked()); ui->rigShareAdvancedButton->setEnabled(canShare && ui->rigShareCheckBox->isChecked()); if ( !canShare ) { ui->rigShareCheckBox->setChecked(false); if (driverID != Rig::HAMLIB_DRIVER) ui->rigShareCheckBox->setToolTip(tr("Rig sharing is only available for Hamlib driver")); else ui->rigShareCheckBox->setToolTip(tr("Rig sharing is not available for network connection")); } else ui->rigShareCheckBox->setToolTip(tr("Start rigctld daemon to share rig with other applications (e.g. WSJT-X)")); } void SettingsDialog::qrzAddCallsignAPIKey() { FCT_IDENTIFICATION; QAbstractItemModel *model = ui->qrzCallsignApiKeyTableView->model(); int newRow = model->rowCount(); model->insertRow(newRow); QModelIndex index = model->index(newRow, 0); ui->qrzCallsignApiKeyTableView->setCurrentIndex(index); ui->qrzCallsignApiKeyTableView->edit(index); } void SettingsDialog::qrzDelCallsignAPIKey() { FCT_IDENTIFICATION; QModelIndex index = ui->qrzCallsignApiKeyTableView->currentIndex(); if ( !index.isValid() ) return; ui->qrzCallsignApiKeyTableView->model()->removeRow(index.row()); } void SettingsDialog::onDeleteAllPasswords() { FCT_IDENTIFICATION; CredentialStore::instance()->deleteAllPasswords(); QMessageBox::information(this, tr("Delete Passwords"), tr("All passwords have been deleted")); // It is necessary to close the dialog, because we would have to delete // all the passwords in the dialog. It would be safer to close it. reject(); } void SettingsDialog::onDeleteAllQSOs() { FCT_IDENTIFICATION; QProgressDialog *progress = new QProgressDialog(tr("Deleting all QSOs..."), QString(), 0, 0, this); progress->setWindowModality(Qt::ApplicationModal); progress->setMinimumDuration(0); progress->setAttribute(Qt::WA_DeleteOnClose, true); progress->show(); QTimer::singleShot(0, this, [this, progress]() { QSqlQuery query; bool ok = query.exec(QStringLiteral("DELETE FROM contacts")); if (!ok) qCDebug(runtime) << "Cannot delete QSOs:" << query.lastError().text(); progress->close(); if (!ok) QMessageBox::warning(this, tr("Error"), tr("Failed to delete all QSOs.")); accept(); }); } void SettingsDialog::readSettings() { FCT_IDENTIFICATION; refreshStationProfilesView(); refreshRigProfilesView(); refreshRotProfilesView(); refreshRotUsrButtonsProfilesView(); refreshAntProfilesView(); refreshCWKeyProfilesView(); refreshCWShortcutProfilesView(); refreshRigAssignedCWKeyCombo(); /************/ /* Callbook */ /************/ int primaryCallbookIndex = ui->primaryCallbookCombo->findData(LogParam::getPrimaryCallbook(GenericCallbook::CALLBOOK_NAME)); ui->primaryCallbookCombo->setCurrentIndex(primaryCallbookIndex); int secondaryCallbookIndex = ui->secondaryCallbookCombo->findData(LogParam::getSecondaryCallbook(GenericCallbook::CALLBOOK_NAME)); ui->secondaryCallbookCombo->setCurrentIndex(secondaryCallbookIndex); ui->hamQthUsernameEdit->setText(HamQTHBase::getUsername()); ui->hamQthPasswordEdit->setText(HamQTHBase::getPasswd()); ui->qrzUsernameEdit->setText(QRZBase::getUsername()); ui->qrzPasswordEdit->setText(QRZBase::getPasswd(QRZBase::getUsername())); ui->webLookupURLEdit->setText(GenericCallbook::getWebLookupURL("", QString(), false)); /********/ /* LoTW */ /********/ ui->lotwUsernameEdit->setText(LotwBase::getUsername()); ui->lotwPasswordEdit->setText(LotwBase::getPasswd()); ui->tqslPathEdit->setText(LotwBase::getTQSLPath()); updateTQSLVersionLabel(); /***********/ /* ClubLog */ /***********/ ui->clublogEmailEdit->setText(ClubLogBase::getEmail()); ui->clublogPasswordEdit->setText(ClubLogBase::getPasswd()); ui->clublogUploadImmediatelyCheckbox->setChecked(ClubLogBase::isUploadImmediatelyEnabled()); /********/ /* eQSL */ /********/ ui->eqslUsernameEdit->setText(EQSLBase::getUsername()); ui->eqslPasswordEdit->setText(EQSLBase::getPasswd()); /**********/ /* HRDLog */ /**********/ ui->hrdlogCallsignEdit->setText(HRDLogBase::getRegisteredCallsign()); ui->hrdlogUploadCodeEdit->setText(HRDLogBase::getUploadCode()); ui->hrdlogOnAirCheckBox->setChecked(HRDLogBase::getOnAirEnabled()); /***********/ /* QRZ.COM */ /***********/ ui->qrzApiKeyEdit->setText(QRZBase::getLogbookAPIKey(QRZBase::getInternalAPIUsername())); generateQRZAPICallsignTable(); /***********/ /* Wavelog */ /***********/ ui->wavelogAddQSOEndpointEdit->setText(CloudlogBase::getAPIEndpoint()); ui->wavelogApiKeyEdit->setText(CloudlogBase::getLogbookAPIKey()); /***********************/ /* Others - DXCC Group */ /***********************/ ui->dxccConfirmedByLotwCheckBox->setChecked(LogParam::getDxccConfirmedByLotwState()); ui->dxccConfirmedByPaperCheckBox->setChecked(LogParam::getDxccConfirmedByPaperState()); ui->dxccConfirmedByEqslCheckBox->setChecked(LogParam::getDxccConfirmedByEqslState()); /***************/ /* ON4KST Chat */ /***************/ ui->kstUsernameEdit->setText(KSTChat::getUsername()); ui->kstPasswordEdit->setText(KSTChat::getPasswd()); /***********/ /* MEMBERS */ /***********/ /***********/ /* NETWORK */ /***********/ ui->wsjtPortSpin->setValue(WsjtxUDPReceiver::getConfigPort()); ui->wsjtForwardEdit->setText(WsjtxUDPReceiver::getConfigForwardAddresses()); ui->wsjtMulticastCheckbox->setChecked(WsjtxUDPReceiver::getConfigMulticastJoin()); ui->wsjtMulticastAddressEdit->setText(WsjtxUDPReceiver::getConfigMulticastAddress()); ui->wsjtMulticastTTLSpin->setValue(WsjtxUDPReceiver::getConfigMulticastTTL()); ui->wsjtColorCqSpotsCheckbox->setChecked(WsjtxUDPReceiver::getConfigOutputColorCQSpot()); ui->notifLogIDEdit->setText(LogParam::getLogID()); ui->notifQSOEdit->setText(NetworkNotification::getNotifQSOAdiAddrs()); ui->notifDXSpotsEdit->setText(NetworkNotification::getNotifDXSpotAddrs()); ui->notifWSJTXCQSpotsEdit->setText(NetworkNotification::getNotifWSJTXCQSpotAddrs()); ui->notifSpotAlertEdit->setText(NetworkNotification::getNotifSpotAlertAddrs()); ui->notifRigEdit->setText(NetworkNotification::getNotifRigStateAddrs()); /*******/ /* GUI */ /*******/ bool timeformat24 = locale.getSettingUse24hformat(); ui->timeFormat24RadioButton->setChecked(timeformat24); ui->timeFormat12RadioButton->setChecked(!timeformat24); bool dateSystemFormat = locale.getSettingUseSystemDateFormat(); ui->dateFormatSystemRadioButton->setChecked(dateSystemFormat); ui->dateFormatCustomRadioButton->setChecked(!dateSystemFormat); ui->dateFormatStringEdit->setText(locale.getSettingDateFormat()); bool unitFormatMetric = locale.getSettingUseMetric(); ui->unitFormatMetricRadioButton->setChecked(unitFormatMetric); ui->unitFormatImperialRadioButton->setChecked(!unitFormatMetric); /******************/ /* END OF Reading */ /******************/ } void SettingsDialog::writeSettings() { FCT_IDENTIFICATION; stationProfManager->save(); rigProfManager->save(); rotProfManager->save(); rotUsrButtonsProfManager->save(); antProfManager->save(); cwKeyProfManager->save(); cwShortcutProfManager->save(); /************/ /* Callbook */ /************/ HamQTHBase::saveUsernamePassword(ui->hamQthUsernameEdit->text(), ui->hamQthPasswordEdit->text()); QRZBase::saveUsernamePassword(ui->qrzUsernameEdit->text(), ui->qrzPasswordEdit->text()); LogParam::setPrimaryCallbook(ui->primaryCallbookCombo->itemData(ui->primaryCallbookCombo->currentIndex()).toString()); LogParam::setSecondaryCallbook(ui->secondaryCallbookCombo->itemData(ui->secondaryCallbookCombo->currentIndex()).toString()); LogParam::setCallbookWebLookupURL(ui->webLookupURLEdit->text()); /********/ /* LoTW */ /********/ LotwBase::saveUsernamePassword(ui->lotwUsernameEdit->text(), ui->lotwPasswordEdit->text()); LotwBase::saveTQSLPath(ui->tqslPathEdit->text()); /***********/ /* ClubLog */ /***********/ ClubLogBase::saveUsernamePassword(ui->clublogEmailEdit->text(), ui->clublogPasswordEdit->text()); ClubLogBase::saveUploadImmediatelyConfig(ui->clublogUploadImmediatelyCheckbox->isChecked()); /********/ /* eQSL */ /********/ EQSLBase::saveUsernamePassword(ui->eqslUsernameEdit->text(), ui->eqslPasswordEdit->text()); /**********/ /* HRDLog */ /**********/ HRDLogBase::saveUploadCode(ui->hrdlogCallsignEdit->text(), ui->hrdlogUploadCodeEdit->text()); HRDLogBase::saveOnAirEnabled(ui->hrdlogOnAirCheckBox->isChecked()); /***********/ /* QRZ.COM */ /***********/ QRZBase::saveLogbookAPIKey(ui->qrzApiKeyEdit->text(), QRZBase::getInternalAPIUsername()); saveQRZAPICallsignTable(); /***********/ /* Wavelog */ /***********/ CloudlogBase::setAPIEndpoint(ui->wavelogAddQSOEndpointEdit->text()); CloudlogBase::saveLogbookAPIKey(ui->wavelogApiKeyEdit->text()); /***********************/ /* Others - DXCC Group */ /***********************/ LogParam::setDxccConfirmedByLotwState(ui->dxccConfirmedByLotwCheckBox->isChecked()); LogParam::setDxccConfirmedByPaperState(ui->dxccConfirmedByPaperCheckBox->isChecked()); LogParam::setDxccConfirmedByEqslState(ui->dxccConfirmedByEqslCheckBox->isChecked()); /***************/ /* ON4KST Chat */ /***************/ KSTChat::saveUsernamePassword(ui->kstUsernameEdit->text(), ui->kstPasswordEdit->text()); /***********/ /* MEMBERS */ /***********/ QStringList enabledLists; for ( QCheckBox* item: static_cast&>(memberListCheckBoxes) ) { if ( item->isChecked() ) { enabledLists << item->text(); } } MembershipQE::saveEnabledClubLists(enabledLists); /***********/ /* NETWORK */ /***********/ WsjtxUDPReceiver::saveConfigPort(ui->wsjtPortSpin->value()); WsjtxUDPReceiver::saveConfigForwardAddresses(ui->wsjtForwardEdit->text()); WsjtxUDPReceiver::saveConfigMulticastJoin(ui->wsjtMulticastCheckbox->isChecked()); WsjtxUDPReceiver::saveConfigMulticastAddress(ui->wsjtMulticastAddressEdit->text()); WsjtxUDPReceiver::saveConfigMulticastTTL(ui->wsjtMulticastTTLSpin->value()); WsjtxUDPReceiver::saveConfigOutputColorCQSpot(ui->wsjtColorCqSpotsCheckbox->isChecked()); NetworkNotification::saveNotifQSOAdiAddrs(ui->notifQSOEdit->text()); NetworkNotification::saveNotifDXSpotAddrs(ui->notifDXSpotsEdit->text()); NetworkNotification::saveNotifWSJTXCQSpotAddrs(ui->notifWSJTXCQSpotsEdit->text()); NetworkNotification::saveNotifSpotAlertAddrs(ui->notifSpotAlertEdit->text()); NetworkNotification::saveNotifRigStateAddrs(ui->notifRigEdit->text()); /*******/ /* GUI */ /*******/ locale.setSettingUse24hformat(ui->timeFormat24RadioButton->isChecked()); bool systemDateChecked = ui->dateFormatSystemRadioButton->isChecked(); locale.setSettingUseSystemDateFormat(systemDateChecked); if ( !systemDateChecked ) locale.setSettingDateFormat(ui->dateFormatStringEdit->text()); locale.setSettingUseMetric(ui->unitFormatMetricRadioButton->isChecked()); } /* this function is called when user modify rig progile * there may be situations where hamlib change the cap * for rig and it is necessary to change the settings of the rig. * This feature does it */ void SettingsDialog::setUIBasedOnRigCaps(const RigCaps &caps) { FCT_IDENTIFICATION; if ( !caps.canGetFreq ) disableCapCheckbox(ui->rigGetFreqCheckBox); if ( !caps.canGetMode ) disableCapCheckbox(ui->rigGetModeCheckBox); if ( !caps.canGetVFO ) disableCapCheckbox(ui->rigGetVFOCheckBox); if ( !caps.canGetPWR )disableCapCheckbox(ui->rigGetPWRCheckBox); if ( !caps.canGetRIT )disableCapCheckbox(ui->rigGetRITCheckBox); if ( !caps.canGetXIT )disableCapCheckbox(ui->rigGetXITCheckBox); if ( !caps.canGetPTT )disableCapCheckbox(ui->rigGetPTTStateCheckBox); if ( !ui->rigGetFreqCheckBox->isChecked() )disableCapCheckbox(ui->rigQSYWipingCheckBox); if ( !caps.canGetKeySpeed ) { disableCapCheckbox(ui->rigGetKeySpeedCheckBox); disableCapCheckbox(ui->rigKeySpeedSyncCheckBox); } if ( !caps.canProcessDXSpot )disableCapCheckbox(ui->rigDXSpots2RigCheckBox); if ( !caps.canGetSplit )disableCapCheckbox(ui->rigGetSplitCheckBox); if ( ui->rigAssignedCWKeyCombo->currentText() != EMPTY_CWKEY_PROFILE ) { CWKeyProfile selectedKeyProfile; selectedKeyProfile = cwKeyProfManager->getProfile(ui->rigAssignedCWKeyCombo->currentText()); if ( ! caps.canGetKeySpeed || (selectedKeyProfile.model == CWKey::MORSEOVERCAT) ) { ui->rigKeySpeedSyncCheckBox->setChecked(false); ui->rigKeySpeedSyncCheckBox->setEnabled(false); } } if ( !caps.isCIVAddrSupported )ui->rigCIVAddrSpinBox->setValue(CIVADDR_DISABLED_VALUE); ui->rigCIVAddrLabel->setVisible(caps.isCIVAddrSupported); ui->rigCIVAddrSpinBox->setVisible(caps.isCIVAddrSupported); } /* Based on selected Rig model, it is needed to prepare AssignedCWKeyCombo * content * The combo has to contain only supported keyes * If selected rig does not support MORSE over CAT then Combo must not contain * CW Key profiles where Morse Over CAT is used. */ void SettingsDialog::refreshRigAssignedCWKeyCombo() { FCT_IDENTIFICATION; const QString &cwKeyName = ui->rigAssignedCWKeyCombo->currentText(); const QStringList &availableCWProfileNames = cwKeyProfManager->profileNameList(); QStringList approvedCWProfiles; const RigCaps &caps = Rig::instance()->getRigCaps(static_cast(ui->rigInterfaceCombo->currentData().toInt()), ui->rigModelSelect->currentData().toInt()); approvedCWProfiles << EMPTY_CWKEY_PROFILE; // add empty profile (like NONE) if ( caps.canSendMorse ) { approvedCWProfiles << availableCWProfileNames; } else { // remove unsupported Morse Over CAT Profile Names for ( const QString &cwProfileName : availableCWProfileNames ) { const CWKeyProfile &testedKey = cwKeyProfManager->getProfile(cwProfileName); if ( testedKey.model != CWKey::MORSEOVERCAT ) { approvedCWProfiles << cwProfileName; } } } QStringListModel* model = static_cast(ui->rigAssignedCWKeyCombo->model()); if ( model ) model->setStringList(approvedCWProfiles); ui->rigAssignedCWKeyCombo->setCurrentText(cwKeyName); } void SettingsDialog::setValidationResultColor(QLineEdit *editBox) { FCT_IDENTIFICATION; QPalette p; if ( ! editBox ) return; p.setColor( QPalette::Text, ( ! editBox->hasAcceptableInput() ) ? Qt::red : qApp->palette().text().color()); editBox->setPalette(p); } void SettingsDialog::generateMembershipCheckboxes() { FCT_IDENTIFICATION; QSqlTableModel membershipDirectoryModel; membershipDirectoryModel.setTable("membership_directory"); membershipDirectoryModel.setSort(0, Qt::AscendingOrder); membershipDirectoryModel.select(); QStringList currentlyEnabledLists = MembershipQE::getEnabledClubLists(); for ( int i = 0 ; i < membershipDirectoryModel.rowCount(); i++ ) { QCheckBox *columnCheckbox = new QCheckBox(this); const QString shortDesc = membershipDirectoryModel.data(membershipDirectoryModel.index(i, membershipDirectoryModel.fieldIndex("short_desc"))).toString(); const QString longDesc = membershipDirectoryModel.data(membershipDirectoryModel.index(i, membershipDirectoryModel.fieldIndex("long_desc"))).toString(); const QString records = membershipDirectoryModel.data(membershipDirectoryModel.index(i, membershipDirectoryModel.fieldIndex("num_records"))).toString(); columnCheckbox->setText(shortDesc); columnCheckbox->setToolTip(longDesc + QString(" (" + tr("members") + ": %1)").arg(records)); columnCheckbox->setChecked(currentlyEnabledLists.contains(shortDesc)); memberListCheckBoxes.append(columnCheckbox); ui->clubListGrig->addWidget(columnCheckbox, i / 6, i % 6); } if ( memberListCheckBoxes.isEmpty() ) ui->clubListGrig->addWidget(new QLabel(tr("Required internet connection during application start"))); } void SettingsDialog::generateQRZAPICallsignTable() { FCT_IDENTIFICATION; QStandardItemModel* tableModel = new QStandardItemModel(ui->qrzCallsignApiKeyTableView); tableModel->setHorizontalHeaderLabels({tr("Callsign"), tr("API Key")}); const QStringList &addlCallsign = QRZBase::getLogbookAPIAddlCallsigns(); for ( const QString &callsign : addlCallsign ) { QList rowItems({new QStandardItem(callsign), new QStandardItem(QRZBase::getLogbookAPIKey(callsign))}); tableModel->appendRow(rowItems); } ui->qrzCallsignApiKeyTableView->setModel(tableModel); ui->qrzCallsignApiKeyTableView->resizeColumnsToContents(); ui->qrzCallsignApiKeyTableView->setItemDelegateForColumn(0, new UpperCaseUniqueDelegate(this)); ui->qrzCallsignApiKeyTableView->setItemDelegateForColumn(1, new PasswordDelegate(this)); } void SettingsDialog::saveQRZAPICallsignTable() { FCT_IDENTIFICATION; const QStringList &addlCallsignsAPIList = LogParam::getQRZCOMAPICallsignsList(); for ( const QString &callsign : addlCallsignsAPIList) { qCDebug(runtime) << "Deleting QRZ Callsign API" << callsign; QRZBase::saveLogbookAPIKey({}, callsign); // side-effect - an empty key causes deleting its old value in the secure store. } // delete original list of callsigns QRZBase::setLogbookAPIAddlCallsigns({}); QAbstractItemModel *model = ui->qrzCallsignApiKeyTableView->model(); QStringList newAddlCallsignsAPIList; for ( int row = 0; row < model->rowCount(); ++row ) { const QString &newCallsign = model->data(model->index(row, 0)).toString(); if ( !newCallsign.isEmpty() ) { qCDebug(runtime) << "Saving a new QRZ callsign API" << newCallsign; const QString &newPassword = model->data(model->index(row, 1)).toString(); if ( !newPassword.isEmpty() ) newAddlCallsignsAPIList.append(newCallsign); QRZBase::saveLogbookAPIKey(newPassword, newCallsign); } } if ( !newAddlCallsignsAPIList.isEmpty() ) QRZBase::setLogbookAPIAddlCallsigns(newAddlCallsignsAPIList); } void SettingsDialog::updateCountyCompleter(int dxcc) { FCT_IDENTIFICATION; ui->stationCountyEdit->setCompleter(nullptr); delete countyCompleter; countyCompleter = nullptr; if ( dxcc == 0 ) return; countyCompleter = Data::createCountyCompleter(dxcc, this); ui->stationCountyEdit->setCompleter(countyCompleter); } SettingsDialog::~SettingsDialog() { FCT_IDENTIFICATION; sotaCompleter->deleteLater(); iotaCompleter->deleteLater(); sigCompleter->deleteLater(); if ( countyCompleter ) countyCompleter->deleteLater(); delete ui; } ================================================ FILE: ui/SettingsDialog.h ================================================ #ifndef QLOG_UI_SETTINGSDIALOG_H #define QLOG_UI_SETTINGSDIALOG_H #include #include #include #include #include #include #include #include #include #include #include #include #include "data/StationProfile.h" #include "data/RigProfile.h" #include "data/RotProfile.h" #include "data/AntProfile.h" #include "data/CWKeyProfile.h" #include "data/CWShortcutProfile.h" #include "data/RotUsrButtonsProfile.h" #include "core/LogLocale.h" #include "ui/MainWindow.h" #include "ui/component/MultiselectCompleter.h" #include "rig/RigCaps.h" namespace Ui { class SettingsDialog; } class QSqlTableModel; class SettingsDialog : public QDialog { Q_OBJECT public: explicit SettingsDialog(MainWindow *parent = nullptr); ~SettingsDialog(); public slots: void save(); void addRigProfile(); void delRigProfile(); void refreshRigProfilesView(); void doubleClickRigProfile(QModelIndex); void clearRigProfileForm(); void rigRXOffsetChanged(int); void rigTXOffsetChanged(int); void rigGetFreqChanged(int); void rigPortTypeChanged(int); void rigInterfaceChanged(int); void rigPTTTypeChanged(int); void addRotProfile(); void delRotProfile(); void refreshRotProfilesView(); void doubleClickRotProfile(QModelIndex); void clearRotProfileForm(); void rotPortTypeChanged(int); void rotInterfaceChanged(int); void addRotUsrButtonsProfile(); void delRotUsrButtonsProfile(); void refreshRotUsrButtonsProfilesView(); void doubleClickRotUsrButtonsProfile(QModelIndex); void clearRotUsrButtonsProfileForm(); void addAntProfile(); void delAntProfile(); void refreshAntProfilesView(); void doubleClickAntProfile(QModelIndex); void clearAntProfileForm(); void addCWKeyProfile(); void delCWKeyProfile(); void refreshCWKeyProfilesView(); void doubleClickCWKeyProfile(QModelIndex); void clearCWKeyProfileForm(); void addCWShortcutProfile(); void delCWShortcutProfile(); void refreshCWShortcutProfilesView(); void doubleClickCWShortcutProfile(QModelIndex); void clearCWShortcutProfileForm(); void refreshStationProfilesView(); void addStationProfile(); void deleteStationProfile(); void doubleClickStationProfile(QModelIndex); void clearStationProfileForm(); void rigChanged(int); void rotChanged(int); void cwKeyChanged(int); void cwModeChanged(int); void rigStackWidgetChanged(int); void rotStackWidgetChanged(int); void cwKeyStackWidgetChanged(int); void tqslPathBrowse(); void tqslAutoDetect(); void updateTQSLVersionLabel(); void stationCallsignChanged(); void adjustLocatorTextColor(); void adjustVUCCLocatorTextColor(); void adjustRotCOMPortTextColor(); void adjustRigCOMPortTextColor(); void adjustCWKeyCOMPortTextColor(); void adjustOperatorTextColor(); void cancelled(); void sotaChanged(const QString&); void sotaEditFinished(); void potaChanged(const QString&); void potaEditFinished(); void wwffChanged(const QString&); void wwffEditFinished(); void primaryCallbookChanged(int); void secondaryCallbookChanged(int); void assignedKeyChanged(int); void testWebLookupURL(); void joinMulticastChanged(int); void adjustWSJTXMulticastAddrTextColor(); void hrdlogSettingChanged(); void clublogSettingChanged(); void updateDateFormatResult(); void rigFlowControlChanged(int); void showRigctldAdvanced(); void rigShareChanged(int); void qrzAddCallsignAPIKey(); void qrzDelCallsignAPIKey(); void onDeleteAllPasswords(); void onDeleteAllQSOs(); private: void readSettings(); void writeSettings(); void setUIBasedOnRigCaps(const RigCaps&); void refreshRigAssignedCWKeyCombo(); void updateRigShareEnabled(); void setValidationResultColor(QLineEdit *); void generateMembershipCheckboxes(); static void refreshProfileView(QAbstractItemView *view, const QStringList &names); static void disableCapCheckbox(QCheckBox *checkbox); static void initProfileListView(QAbstractItemView *view); static void populateFlowControlCombo(QComboBox *combo); static void populateParityCombo(QComboBox *combo); static void populateSignalCombo(QComboBox *combo); static void setComboByData(QComboBox *combo, const QVariant &data, int fallback = 0); static void updateOffsetSpinBox(QCheckBox *checkbox, QDoubleSpinBox *spinBox); static void deleteSelectedProfiles(QAbstractItemView *view, const std::function &removeProfile); void generateQRZAPICallsignTable(); void saveQRZAPICallsignTable(); void updateCountyCompleter(int dxcc); static constexpr int STACKED_WIDGET_SERIAL_SETTING = 0; static constexpr int STACKED_WIDGET_NETWORK_SETTING = 1; static constexpr int STACKED_WIDGET_SPECIAL_OMNIRIG_SETTING = 2; static constexpr int RIGPORT_SERIAL_INDEX = 0; static constexpr int RIGPORT_NETWORK_INDEX = 1; static constexpr int RIGPORT_SPECIAL_OMNIRIG_INDEX = 2; static constexpr int ROTPORT_SERIAL_INDEX = 0; static constexpr int ROTPORT_NETWORK_INDEX = 1; static constexpr int RIG_NET_DEFAULT_PORT = 4532; static constexpr int ROT_NET_DEFAULT_PORT = 4533; static constexpr int ROT_NET_DEFAULT_PSTROT = 12000; static constexpr int CW_NET_CWDAEMON_PORT = 6789; static constexpr int CW_NET_FLDIGI_PORT = 7362; static constexpr int CW_DEFAULT_KEY_SPEED = 20; static constexpr int CW_KEY_SPEED_DISABLED = 0; static constexpr int PTT_TYPE_NONE_INDEX = 0; static constexpr int PTT_TYPE_CAT_INDEX = 1; static constexpr int CIVADDR_DISABLED_VALUE = -1; static constexpr const char* EMPTY_CWKEY_PROFILE = " "; QSqlTableModel* modeTableModel; QSqlTableModel* bandTableModel; StationProfilesManager *stationProfManager; RigProfilesManager *rigProfManager; RotProfilesManager *rotProfManager; AntProfilesManager *antProfManager; CWKeyProfilesManager *cwKeyProfManager; CWShortcutProfilesManager *cwShortcutProfManager; RotUsrButtonsProfilesManager *rotUsrButtonsProfManager; QCompleter *sotaCompleter; QCompleter *iotaCompleter; QCompleter *wwffCompleter; MultiselectCompleter *potaCompleter; QCompleter *sigCompleter; QCompleter *countyCompleter; QList memberListCheckBoxes; Ui::SettingsDialog *ui; LogLocale locale; bool sotaFallback; bool potaFallback; bool wwffFallback; QString rigctldPath; QString rigctldArgs; QTimer *tqslVersionTimer; }; #endif // QLOG_UI_SETTINGSDIALOG_H ================================================ FILE: ui/SettingsDialog.ui ================================================ SettingsDialog 0 0 800 836 770 0 800 16777215 Settings true QDialogButtonBox::Cancel|QDialogButtonBox::Save 0 false Station Profiles 2 SOTA IOTA SOTA (Optional parameter) SIG (Optional parameter). 6 0 0 0 ITU 40 16777215 0 0 CQZ 40 16777215 Qt::Horizontal 5 5 County IOTA (Optional parameter) QTH Name (Optional parameter) World Wide Flora & Fauna (Optional parameter) Operator name (Optional parameter) POTA VUCC Grids (Optional parameter). Ex. EN98,FM08,EM97,FM07 Country Station County Location (Optional parameter) SIG Info Station Gridsquare (Mandatory parameter) 12 QTH Profile Name WWFF Qt::NoFocus Qt::NoContextMenu List of all available Station Profiles QAbstractItemView::NoEditTriggers SIG Information (Optional parameter) Profile name that is used as the alias for the Callsign, Gridsquare, Operator name, and QTH (required parameter) Callsign (Mandatory parameter) 25 Callsign of operator (Optional parameter, if different from station callsign) Operator Name VUCC Station Callsign Gridsquare SIG Operator Callsign Add Delete DOK Qt::Vertical 0 0 Equipment 0 true Antennas Profiles 400 0 Profile Name 25 Description false Azimuth Beamwidth Azimuth Offset 0 0 Valid range value is 0° - 100° (0° Unspecified) Unspecified ° 1 100.000000000000000 0 0 ° 1 -180.000000000000000 180.000000000000000 Add Delete Qt::NoFocus Qt::NoContextMenu List of all available Antennas QAbstractItemView::NoEditTriggers Qt::Vertical 0 0 CW Keyers Keyer Profiles 400 0 false 2 Profile Name 25 Model Keyer Mode 12 0 0 Swap Paddles 12 Paddle Only Sidetone Sidetone Freq: 0 0 Default Speed 0 0 N/A WPM 0 999 20 0 0 QFrame::NoFrame 0 0 0 0 0 Port Use COMxx for Window or path to COM port under Unix-like OS Baudrate 115200 57600 38400 19200 9600 4800 2400 1200 0 0 0 0 Host Name Port HamLib does not support to change a destination port. 1024 65535 6789 Add Qt::NoFocus Qt::NoContextMenu List of all available CW Keyers QAbstractItemView::NoEditTriggers Delete CW Shortcut Profiles Delete Add 400 0 2 Profile Name 25 0 0 F1 0 0 0 0 60 16777215 Short Desciption of the Button (up to 7 chars) 7 true <DXCALL> = DX Callsign <NAME> = DX Operator Name <RST> = Report 599 <RSTN> = Report 5NN <GREETING> = Greenting GM/GA/GE <QTH> = QTH <MYCALL> = My Callsign <MYNAME> = My Name <MYQTH> = My QTH <MYLOCATOR> = My Gridsquare <MYGRID> = My Gridsquare <MYSIG> = My SIG <MYSIGINFO> = My SIG Information <MYIOTA> = My IOTA <MYSOTA> = MY SOTA <MYWWFT> = My WWFT <MYVUCC> = MY VUCC <MYPWR> = My Power in W <EXCHSTR> = Contest Exchange Message <EXCHNR> = Contest Exchange Serial Number <EXCHNRN> = Contest Exchange Serial Number (9→N, 0→T) <+> = Speed +5 WPM (<++> = +10 WPM, etc.) <-> = Speed -5 WPM (<--> = -10 WPM, etc.) 0 0 F2 0 0 60 16777215 Short Desciption of the Button (up to 7 chars) 5 <DXCALL> = DX Callsign <NAME> = DX Operator Name <RST> = Report 599 <RSTN> = Report 5NN <GREETING> = Greenting GM/GA/GE <QTH> = QTH <MYCALL> = My Callsign <MYNAME> = My Name <MYQTH> = My QTH <MYLOCATOR> = My Gridsquare <MYGRID> = My Gridsquare <MYSIG> = My SIG <MYSIGINFO> = My SIG Information <MYIOTA> = My IOTA <MYSOTA> = MY SOTA <MYWWFT> = My WWFT <MYVUCC> = MY VUCC <MYPWR> = My Power in W <EXCHSTR> = Contest Exchange Message <EXCHNR> = Contest Exchange Serial Number <EXCHNRN> = Contest Exchange Serial Number (9→N, 0→T) <+> = Speed +5 WPM (<++> = +10 WPM, etc.) <-> = Speed -5 WPM (<--> = -10 WPM, etc.) <DXCALL> = DX Callsign <NAME> = DX Operator Name <RST> = Report 599 <RSTN> = Report 5NN <GREETING> = Greenting GM/GA/GE <QTH> = QTH <MYCALL> = My Callsign <MYNAME> = My Name <MYQTH> = My QTH <MYLOCATOR> = My Gridsquare <MYGRID> = My Gridsquare <MYSIG> = My SIG <MYSIGINFO> = My SIG Information <MYIOTA> = My IOTA <MYSOTA> = MY SOTA <MYWWFT> = My WWFT <MYVUCC> = MY VUCC <MYPWR> = My Power in W <EXCHSTR> = Contest Exchange Message <EXCHNR> = Contest Exchange Serial Number <EXCHNRN> = Contest Exchange Serial Number (9→N, 0→T) <+> = Speed +5 WPM (<++> = +10 WPM, etc.) <-> = Speed -5 WPM (<--> = -10 WPM, etc.) <DXCALL> = DX Callsign <NAME> = DX Operator Name <RST> = Report 599 <RSTN> = Report 5NN <GREETING> = Greenting GM/GA/GE <QTH> = QTH <MYCALL> = My Callsign <MYNAME> = My Name <MYQTH> = My QTH <MYLOCATOR> = My Gridsquare <MYGRID> = My Gridsquare <MYSIG> = My SIG <MYSIGINFO> = My SIG Information <MYIOTA> = My IOTA <MYSOTA> = MY SOTA <MYWWFT> = My WWFT <MYVUCC> = MY VUCC <MYPWR> = My Power in W <EXCHSTR> = Contest Exchange Message <EXCHNR> = Contest Exchange Serial Number <EXCHNRN> = Contest Exchange Serial Number (9→N, 0→T) <+> = Speed +5 WPM (<++> = +10 WPM, etc.) <-> = Speed -5 WPM (<--> = -10 WPM, etc.) <DXCALL> = DX Callsign <NAME> = DX Operator Name <RST> = Report 599 <RSTN> = Report 5NN <GREETING> = Greenting GM/GA/GE <QTH> = QTH <MYCALL> = My Callsign <MYNAME> = My Name <MYQTH> = My QTH <MYLOCATOR> = My Gridsquare <MYGRID> = My Gridsquare <MYSIG> = My SIG <MYSIGINFO> = My SIG Information <MYIOTA> = My IOTA <MYSOTA> = MY SOTA <MYWWFT> = My WWFT <MYVUCC> = MY VUCC <MYPWR> = My Power in W <EXCHSTR> = Contest Exchange Message <EXCHNR> = Contest Exchange Serial Number <EXCHNRN> = Contest Exchange Serial Number (9→N, 0→T) <+> = Speed +5 WPM (<++> = +10 WPM, etc.) <-> = Speed -5 WPM (<--> = -10 WPM, etc.) <DXCALL> = DX Callsign <NAME> = DX Operator Name <RST> = Report 599 <RSTN> = Report 5NN <GREETING> = Greenting GM/GA/GE <QTH> = QTH <MYCALL> = My Callsign <MYNAME> = My Name <MYQTH> = My QTH <MYLOCATOR> = My Gridsquare <MYGRID> = My Gridsquare <MYSIG> = My SIG <MYSIGINFO> = My SIG Information <MYIOTA> = My IOTA <MYSOTA> = MY SOTA <MYWWFT> = My WWFT <MYVUCC> = MY VUCC <MYPWR> = My Power in W <EXCHSTR> = Contest Exchange Message <EXCHNR> = Contest Exchange Serial Number <EXCHNRN> = Contest Exchange Serial Number (9→N, 0→T) <+> = Speed +5 WPM (<++> = +10 WPM, etc.) <-> = Speed -5 WPM (<--> = -10 WPM, etc.) <DXCALL> = DX Callsign <NAME> = DX Operator Name <RST> = Report 599 <RSTN> = Report 5NN <GREETING> = Greenting GM/GA/GE <QTH> = QTH <MYCALL> = My Callsign <MYNAME> = My Name <MYQTH> = My QTH <MYLOCATOR> = My Gridsquare <MYGRID> = My Gridsquare <MYSIG> = My SIG <MYSIGINFO> = My SIG Information <MYIOTA> = My IOTA <MYSOTA> = MY SOTA <MYWWFT> = My WWFT <MYVUCC> = MY VUCC <MYPWR> = My Power in W <EXCHSTR> = Contest Exchange Message <EXCHNR> = Contest Exchange Serial Number <EXCHNRN> = Contest Exchange Serial Number (9→N, 0→T) <+> = Speed +5 WPM (<++> = +10 WPM, etc.) <-> = Speed -5 WPM (<--> = -10 WPM, etc.) 0 0 F3 60 16777215 Short Desciption of the Button (up to 7 chars) 5 0 0 F4 60 16777215 Short Desciption of the Button (up to 7 chars) 5 0 0 F5 60 16777215 Short Desciption of the Button (up to 7 chars) 5 0 0 F6 60 16777215 Short Desciption of the Button (up to 7 chars) 5 0 0 F7 60 16777215 Short Desciption of the Button (up to 7 chars) 5 Qt::NoFocus Qt::NoContextMenu List of all available CW Shortcuts Profiles QAbstractItemView::NoEditTriggers Rigs 4 4 4 4 4 Profiles 4 4 4 4 4 Delete Add 0 0 410 0 false 2 2 4 4 4 4 Profile Name 25 Interface 0 0 Minimum and maximum TX frequencies. Specific ranges are derived from allowed Band in the Setting. TX Range Minimum and maximum TX frequencies. Specific ranges are derived from allowed Band in the Setting. MHz 250000.000000000000000 0 0 - Qt::AlignCenter Minimum and maximum TX frequencies. Specific ranges are derived from allowed Band in the Setting. MHz 250000.000000000000000 Offsets 0 0 Enter manually RIT or Transverter Offset RX: MHz 5 -9999999.999989999458194 9999999.999989999458194 0.000000000000000 Qt::Horizontal QSizePolicy::Expanding 40 20 0 0 Enter manually XIT or Transverter offset TX: MHz 5 -9999999.999989999458194 9999999.999989999458194 Default PWR Enter default PWR (ex. when Rig is disconnected) Blank W 3 1000000.000000000000000 100.000000000000000 Assigned CW Keyer Rig Features 0 0 Mode true VFO true Freq true 0 QSY Wiping Power true PTT State TX Offset (XIT) RX Offset (RIT) Split 0 CW Keyer Speed CW Speed Sync 0 DX Spots to Rig 0 Start rigctld daemon to share rig with other applications (e.g. WSJT-X) Share Rig via port 1024 65535 4532 Advanced... Qt::Horizontal 40 20 Port Type Qt::Horizontal 40 20 0 0 Poll Interval 0 0 ms 100 60000 500 0 0 QFrame::NoFrame 0 Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop 6 2 0 2 0 0 Rig Port 0 0 Use COMxx for Window or path to COM port under Unix-like OS 0 0 Baudrate 0 0 Qt::StrongFocus 115200 57600 38400 19200 9600 4800 2400 1200 CIV Addr Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 2 0 0 Auto 0x -1 255 16 0 0 0 0 Flow Control 0 0 0 0 Parity Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 2 0 0 PTT Type 6 PTT Port 0 0 0 0 RTS Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 2 0 0 0 0 DTR Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter Data Bits Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 8 7 6 5 Stop Bits Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 2 0 0 1 2 6 2 0 2 0 0 Host Name 10 Port 1 65535 4532 Qt::NoFocus Qt::NoContextMenu List of all available Rigs QAbstractItemView::NoEditTriggers Rotators Profiles Add Delete Qt::NoFocus Qt::NoContextMenu List of all available Rigs QAbstractItemView::NoEditTriggers 400 0 2 Profile Name 25 Interface 0 0 QComboBox::AdjustToContentsOnFirstShow Port Type Serial Network 0 0 QFrame::NoFrame 0 4 0 2 0 0 Port Use COMxx for Window or path to COM port under Unix-like OS Baudrate 115200 57600 38400 19200 9600 4800 2400 1200 Data Bits Flow Control 6 8 7 6 5 Stop Bits Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 1 2 6 Parity Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 2 0 0 0 0 Host Name 8 Port Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 1 65535 4533 User Buttons Profiles Qt::NoFocus Qt::NoContextMenu List of all available CW Shortcuts Profiles QAbstractItemView::NoEditTriggers Add Delete 400 0 2 Profile Name 25 0 0 Button 1 0 0 0 0 60 16777215 Short Desciption of the Button (up to 7 chars) 7 true 0 0 Button 2 0 0 60 16777215 Short Desciption of the Button (up to 7 chars) 5 0 0 Button 3 60 16777215 Short Desciption of the Button (up to 7 chars) 5 0 0 Button 4 60 16777215 Short Desciption of the Button (up to 7 chars) 5 0 0 Blank ° -1 359 -1 0 0 Blank ° -1 359 -1 0 0 Blank ° -1 359 -1 0 0 Blank ° -1 359 1 -1 Qt::Vertical 20 40 Callbook Query Order Primary Secondary HamQTH Username true Password true QLineEdit::Password QRZ.com Username true Password true QLineEdit::Password <b>Notice:</b> At least a QRZ XML Subscription is recommended to access detailed information. Without a subscription, you will obtain limited data from QRZ, such as missing grid and other fields. true Web Lookup Button URL Specify the URL to use for quick search. The <DXCALL> macro will be replaced by the current callsign Test URL with your Callsign Test Qt::Vertical 0 0 Clubs Active Lists Qt::Vertical 0 0 Sync && QSL Qt::Vertical 0 0 QTabWidget::Rounded 0 true ClubLog Qt::Vertical QSizePolicy::Fixed 20 20 E-Mail Password QLineEdit::Password Immediately Upload false QSOs are uploaded immediately eQSL Qt::Vertical QSizePolicy::Fixed 20 20 Username Password QLineEdit::Password HRDLog Qt::Vertical QSizePolicy::Fixed 20 20 Callsign It is not a password. It is the upload code received via email after the registration to HRDLOG..net Upload Code It is not a password. It is the upload code received via email after the registration to HRDLOG..net QLineEdit::Password If it is enabled and Rig is connected then QLog periodically sends On-Air messages to HRDLog Send On-Air false If it is enabled and Rig is connected then QLog periodically sends On-Air messages to HRDLog LoTW Qt::Vertical QSizePolicy::Fixed 20 20 Username Password QLineEdit::Password TQSL Path Leave empty for auto-detection true Browse .. Auto-detect TQSL path Auto-Detect TQSL Version 0 0 0 0 Using an internal TQSL instance QRZ.com Qt::Vertical QSizePolicy::Fixed 20 20 Default API Key QLineEdit::Password Qt::Vertical QSizePolicy::Maximum 20 20 Callsign-specific API Keys true QAbstractItemView::SingleSelection QAbstractItemView::ScrollPerPixel QAbstractItemView::ScrollPerPixel 300 true false 0 Add Qt::Horizontal QSizePolicy::Maximum 60 20 Delete Wavelog Qt::Vertical QSizePolicy::Fixed 20 20 Endpoint https://localhost/index.php/api/qso API Key QLineEdit::Password Others 0 0 DXCC Status Confirmed By 10 20 LoTW Paper eQSL Qt::Horizontal 0 0 Qt::Vertical 20 40 Chat 0 true ON4KST Qt::Vertical QSizePolicy::Fixed 20 20 Username Password QLineEdit::Password Qt::Vertical 0 0 <b>Security Notice:</b> QLog stores all passwords in the Secure Storage. Unfortunately, ON4KST uses a protocol where this password is sent over an unsecured channel as plaintext.</p><p>Please exercise caution when choosing your password for this service, as your password is sent over an unsecured channel in plaintext form.</p> true Qt::Vertical 0 0 Bands true QAbstractItemView::ScrollPerPixel QAbstractItemView::ScrollPerPixel true 20 20 Modes The '>' character is interpreted as a marker for the initial cursor position in the Report column. <br/>Ex.: '5>9' means the cursor will be positioned on the second character true true QAbstractItemView::ScrollPerPixel QAbstractItemView::ScrollPerPixel true 20 20 Network Wsjtx Raw UDP Forward <p>List of IP addresses to which QLog forwards raw UDP WSJT-X packets.</p>The IP addresses are separated by a space and have the form IP:PORT ex. 192.168.1.1:1234 192.168.2.1:1234 Port 0 0 Port where QLog listens an incoming traffic from WSJT-X 1025 65534 2237 Join Multicast 0 0 Specify Multicast Address. <br>On some Linux systems it may be necessary to enable multicast on the loop-back network interface. TTL 0 0 Time-To-Live determines the range<br> over which a multicast packet is propagated in your intranet. 1 255 1 Multicast Address 0 0 Enable/Disable Multicast option for WSJTX 0 0 Color CQ Spots 0 0 Enable/Disable sending color-coded status indicators back to WSJT-X for each callsign calling CQ Notifications LogID true <p>Assigned LogID to the current log.</p>The LogID is sent in the Network Nofitication messages as a unique instance identified.<p> The ID is generated automatically and cannot be changed</> true false DX Spots <p> List of IP addresses to which QLog sends UDP notification packets with DX Cluster Spots.</p>The IP addresses are separated by a space and have the form IP:PORT ex. 192.168.1.1:1234 192.168.2.1:1234 Spot Alerts <p> List of IP addresses to which QLog sends UDP notification packets about user Spot Alerts.</p>The IP addresses are separated by a space and have the form IP:PORT ex. 192.168.1.1:1234 192.168.2.1:1234 QSO Changes <p> List of IP addresses to which QLog sends UDP notification packets about a new/updated/deleted QSO in the log.</p>The IP addresses are separated by a space and have the form IP:PORT ex. 192.168.1.1:1234 192.168.2.1:1234 Wsjtx CQ Spots <p> List of IP addresses to which QLog sends UDP notification packets with WSJTX CQ Spots.</p>The IP addresses are separated by a space and have the form IP:PORT ex. 192.168.1.1:1234 192.168.2.1:1234 Rig Status <p> List of IP addresses to which QLog sends UDP notification packets when Rig State changes.</p>The IP addresses are separated by a space and have the form IP:PORT ex. 192.168.1.1:1234 192.168.2.1:1234 Qt::Vertical 0 0 GUI Date Format System buttonGroup_2 Custom buttonGroup_2 0 0 Qt::Horizontal QSizePolicy::Fixed 20 20 Qt::Horizontal QSizePolicy::Fixed 20 20 <a href="https://doc.qt.io/qt-6/qdate.html#fromString-1">Time Format Documentation</a> true Qt::Horizontal 40 20 Time Format 24-hour buttonGroup AM/PM buttonGroup Qt::Horizontal 40 20 Unit System Metric buttonGroup_3 Imperial buttonGroup_3 Qt::Horizontal 40 20 true QAbstractItemView::NoSelection QAbstractItemView::ScrollPerPixel QAbstractItemView::ScrollPerPixel 300 20 20 Danger Zone Qt::Vertical 20 40 <b>⚠ This is a danger zone. Proceed with caution, as actions performed here cannot be undone and may have a significant impact on your log.</b> Qt::AlignCenter true Qt::Vertical 20 40 0 0 QPushButton {background-color: red;} Delete All QSOs Qt::Vertical 20 40 0 0 QPushButton {background-color: red;} Delete All Passwords from the Secure Store Qt::Vertical 20 40 BaseDoubleSpinBox QDoubleSpinBox
ui/component/BaseDoubleSpinBox.h
SerialPortEditLine QLineEdit
ui/component/EditLine.h
stationProfileNameEdit stationCallsignEdit stationOperatorEdit stationOperatorCallsignEdit stationCountryCombo stationCountyEdit stationITUEdit stationCQZEdit stationLocatorEdit stationQTHEdit stationVUCCEdit stationSOTAEdit stationWWFFEdit stationPOTAEdit stationSIGEdit stationIOTAEdit stationSIGInfoEdit stationDarcDokEdit stationAddProfileButton stationDelProfileButton antProfileNameEdit antDescEdit antAzBeamWidthSpinBox antAzOffsetSpinBox antAddProfileButton antDelProfileButton cwProfileNameEdit cwModelSelect cwKeyModeSelect cwSwapPaddlesCheckbox cwPaddleOnlySidetoneCheckbox cwSidetoneFreqCombo cwDefaulSpeed cwPortEdit cwBaudSelect cwAddProfileButton cwDelProfileButton cwShortcutProfileNameEdit cwShortcutF1ShortEdit cwShortcutF1MacroEdit cwShortcutF2ShortEdit cwShortcutF2MacroEdit cwShortcutF3ShortEdit cwShortcutF3MacroEdit cwShortcutF4ShortEdit cwShortcutF4MacroEdit cwShortcutF5ShortEdit cwShortcutF5MacroEdit cwShortcutF6ShortEdit cwShortcutF6MacroEdit cwShortcutF7ShortEdit cwShortcutF7MacroEdit cwShortcutAddProfileButton cwShortcutDelProfileButton rigProfileNameEdit rigInterfaceCombo rigModelSelect rigTXFreqMinSpinBox rigTXFreqMaxSpinBox rigRXOffsetSpinBox rigTXOffsetSpinBox rigPWRDefaultSpinBox rigAssignedCWKeyCombo rigGetModeCheckBox rigGetVFOCheckBox rigGetFreqCheckBox rigQSYWipingCheckBox rigGetPWRCheckBox rigGetPTTStateCheckBox rigGetXITCheckBox rigGetRITCheckBox rigGetSplitCheckBox rigGetKeySpeedCheckBox rigKeySpeedSyncCheckBox rigDXSpots2RigCheckBox rigShareCheckBox rigSharePortSpinBox rigShareAdvancedButton rigPortTypeCombo rigPollIntervalSpinBox cwNetPortSpin rigPortEdit rigBaudSelect rigCIVAddrSpinBox rigDataBitsSelect rigStopBitsSelect rigFlowControlSelect rigParitySelect rigHostNameEdit rigDTRCombo rigRTSCombo rigPTTTypeCombo rigPTTPortEdit rigNetPortSpin rigAddProfileButton rigDelProfileButton rotProfileNameEdit rotInterfaceCombo rotModelSelect rotPortTypeCombo rotPortEdit rotBaudSelect rotDataBitsSelect rotStopBitsSelect rotFlowControlSelect rotParitySelect rotHostNameEdit rotNetPortSpin rotAddProfileButton rotDelProfileButton rotUsrButtonProfileNameEdit rotUsrButton1Edit rotUsrButtonSpinBox1 rotUsrButton2Edit rotUsrButtonSpinBox2 rotUsrButton3Edit rotUsrButtonSpinBox3 rotUsrButton4Edit rotUsrButtonSpinBox4 rotUsrButtonAddProfileButton rotUsrButtonDelProfileButton primaryCallbookCombo secondaryCallbookCombo hamQthUsernameEdit hamQthPasswordEdit qrzUsernameEdit qrzPasswordEdit webLookupURLEdit webLookupURLTestButton clublogEmailEdit clublogPasswordEdit clublogUploadImmediatelyCheckbox eqslUsernameEdit eqslPasswordEdit hrdlogCallsignEdit hrdlogUploadCodeEdit hrdlogOnAirCheckBox lotwUsernameEdit lotwPasswordEdit tqslPathEdit tqslPathButton tqslAutoButton qrzApiKeyEdit qrzCallsignApiKeyTableView qrzCallsignApiKeyAddButton qrzCallsignApiKeyDelButton wavelogAddQSOEndpointEdit wavelogApiKeyEdit dxccConfirmedByLotwCheckBox dxccConfirmedByPaperCheckBox dxccConfirmedByEqslCheckBox bandTableView modeTableView wsjtForwardEdit wsjtPortSpin wsjtColorCqSpotsCheckbox wsjtMulticastCheckbox wsjtMulticastAddressEdit wsjtMulticastTTLSpin notifLogIDEdit notifDXSpotsEdit notifSpotAlertEdit notifQSOEdit notifWSJTXCQSpotsEdit notifRigEdit tabWidget tabWidget_2 cwHostNameEdit equipmentTabWidget tabWidget_3 kstUsernameEdit kstPasswordEdit dateFormatSystemRadioButton dateFormatCustomRadioButton dateFormatStringEdit timeFormat24RadioButton timeFormat12RadioButton unitFormatMetricRadioButton unitFormatImperialRadioButton shortcutsTableView stationWWFFEdit editingFinished() SettingsDialog wwffEditFinished() 651 288 335 439 rigGetXITCheckBox stateChanged(int) SettingsDialog rigTXOffsetChanged(int) 333 117 347 399 antAddProfileButton clicked() SettingsDialog addAntProfile() 434 397 333 349 rotStackedWidget currentChanged(int) SettingsDialog rotStackWidgetChanged(int) 452 116 338 463 stationSOTAEdit editingFinished() SettingsDialog sotaEditFinished() 472 288 335 439 cwAddProfileButton clicked() SettingsDialog addCWKeyProfile() 79 97 338 408 rigPortEdit textChanged(QString) SettingsDialog adjustRigCOMPortTextColor() 462 116 338 463 stationLocatorEdit textChanged(QString) SettingsDialog adjustLocatorTextColor() 531 212 359 424 stationVUCCEdit textChanged(QString) SettingsDialog adjustVUCCLocatorTextColor() 531 240 377 424 secondaryCallbookCombo currentIndexChanged(int) SettingsDialog secondaryCallbookChanged(int) 473 135 377 424 cwModelSelect currentIndexChanged(int) SettingsDialog cwKeyChanged(int) 452 124 338 463 rigAddProfileButton clicked() SettingsDialog addRigProfile() 79 110 333 349 rigModelSelect currentIndexChanged(int) SettingsDialog rigChanged(int) 462 127 359 413 antDelProfileButton clicked() SettingsDialog delAntProfile() 637 397 333 349 rotModelSelect currentIndexChanged(int) SettingsDialog rotChanged(int) 452 120 333 349 rotProfilesListView doubleClicked(QModelIndex) SettingsDialog doubleClickRotProfile(QModelIndex) 58 116 333 349 cwShortcutDelProfileButton clicked() SettingsDialog delCWShortcutProfile() 100 116 338 463 stationSOTAEdit textChanged(QString) SettingsDialog sotaChanged(QString) 472 288 377 424 antProfilesListView doubleClicked(QModelIndex) SettingsDialog doubleClickAntProfile(QModelIndex) 153 184 333 349 rigGetFreqCheckBox stateChanged(int) SettingsDialog rigGetFreqChanged(int) 333 119 338 408 cwProfilesListView doubleClicked(QModelIndex) SettingsDialog doubleClickCWKeyProfile(QModelIndex) 58 116 338 408 rotAddProfileButton clicked() SettingsDialog addRotProfile() 79 106 333 349 rigStackedWidget currentChanged(int) SettingsDialog rigStackWidgetChanged(int) 462 116 338 463 primaryCallbookCombo currentIndexChanged(int) SettingsDialog primaryCallbookChanged(int) 154 135 377 424 cwShortcutAddProfileButton clicked() SettingsDialog addCWShortcutProfile() 87 116 338 463 rigDelProfileButton clicked() SettingsDialog delRigProfile() 100 110 333 349 cwShortcutListView doubleClicked(QModelIndex) SettingsDialog doubleClickCWShortcutProfile(QModelIndex) 73 125 338 463 stationCallsignEdit textChanged(QString) SettingsDialog stationCallsignChanged() 531 128 359 424 stationWWFFEdit textChanged(QString) SettingsDialog wwffChanged(QString) 651 288 335 439 rotDelProfileButton clicked() SettingsDialog delRotProfile() 100 106 333 349 cwDelProfileButton clicked() SettingsDialog delCWKeyProfile() 100 97 338 408 cwStackedWidget currentChanged(int) SettingsDialog cwKeyStackWidgetChanged(int) 452 116 338 463 rigGetRITCheckBox stateChanged(int) SettingsDialog rigRXOffsetChanged(int) 461 117 347 399 stationProfilesListView doubleClicked(QModelIndex) SettingsDialog doubleClickStationProfile(QModelIndex) 210 220 359 424 tqslPathButton clicked() SettingsDialog tqslPathBrowse() 711 546 359 424 tqslAutoButton clicked() SettingsDialog tqslAutoDetect() 780 546 359 424 stationAddProfileButton clicked() SettingsDialog addStationProfile() 454 393 359 424 cwPortEdit textChanged(QString) SettingsDialog adjustCWKeyCOMPortTextColor() 452 116 338 463 rotPortEdit textChanged(QString) SettingsDialog adjustRotCOMPortTextColor() 452 116 338 463 stationDelProfileButton clicked() SettingsDialog deleteStationProfile() 650 393 359 424 rigProfilesListView doubleClicked(QModelIndex) SettingsDialog doubleClickRigProfile(QModelIndex) 58 116 333 349 buttonBox accepted() SettingsDialog save() 344 758 297 188 buttonBox rejected() SettingsDialog cancelled() 344 758 297 188 rigAssignedCWKeyCombo currentIndexChanged(int) SettingsDialog assignedKeyChanged(int) 462 121 333 392 stationPOTAEdit editingFinished() SettingsDialog potaEditFinished() 472 316 348 392 stationPOTAEdit textChanged(QString) SettingsDialog potaChanged(QString) 472 316 348 392 wsjtMulticastCheckbox stateChanged(int) SettingsDialog joinMulticastChanged(int) 174 156 340 392 wsjtMulticastAddressEdit textChanged(QString) SettingsDialog adjustWSJTXMulticastAddrTextColor() 404 183 340 392 rotUsrButtonAddProfileButton clicked() SettingsDialog addRotUsrButtonsProfile() 79 112 340 392 rotUsrButtonDelProfileButton clicked() SettingsDialog delRotUsrButtonsProfile() 100 112 340 392 rotUsrButtonListView doubleClicked(QModelIndex) SettingsDialog doubleClickRotUsrButtonsProfile(QModelIndex) 58 122 340 392 webLookupURLTestButton clicked() SettingsDialog testWebLookupURL() 651 423 362 392 rigPortTypeCombo currentIndexChanged(int) SettingsDialog rigPortTypeChanged(int) 462 116 362 383 rotPortTypeCombo currentIndexChanged(int) SettingsDialog rotPortTypeChanged(int) 452 117 362 383 hrdlogCallsignEdit textChanged(QString) SettingsDialog hrdlogSettingChanged() 431 351 372 383 hrdlogUploadCodeEdit textChanged(QString) SettingsDialog hrdlogSettingChanged() 431 382 372 383 rigInterfaceCombo currentIndexChanged(int) SettingsDialog rigInterfaceChanged(int) 566 172 390 383 clublogPasswordEdit textChanged(QString) SettingsDialog clublogSettingChanged() 443 154 390 382 clublogEmailEdit textChanged(QString) SettingsDialog clublogSettingChanged() 443 123 390 382 rotInterfaceCombo currentIndexChanged(int) SettingsDialog rotInterfaceChanged(int) 587 173 390 396 rigPTTTypeCombo currentIndexChanged(int) SettingsDialog rigPTTTypeChanged(int) 407 661 384 396 stationOperatorCallsignEdit textChanged(QString) SettingsDialog adjustOperatorTextColor() 465 174 384 397 dateFormatCustomRadioButton toggled(bool) dateFormatStringEdit setVisible(bool) 241 91 358 91 dateFormatCustomRadioButton toggled(bool) dateFormatResultLabel setVisible(bool) 241 91 435 91 dateFormatStringEdit textChanged(QString) SettingsDialog updateDateFormatResult() 358 91 384 397 dateFormatCustomRadioButton toggled(bool) dateFormatDocLabel setVisible(bool) 241 91 609 91 qrzCallsignApiKeyAddButton clicked() SettingsDialog qrzAddCallsignAPIKey() 193 412 384 397 qrzCallsignApiKeyDelButton clicked() SettingsDialog qrzDelCallsignAPIKey() 575 412 384 397 cwKeyModeSelect currentIndexChanged(int) SettingsDialog cwModeChanged(int) 496 201 384 397 rigFlowControlSelect currentIndexChanged(int) SettingsDialog rigFlowControlChanged(int) 415 662 384 422 rigShareCheckBox stateChanged(int) SettingsDialog rigShareChanged(int) 455 527 384 424 rigShareAdvancedButton clicked() SettingsDialog showRigctldAdvanced() 688 527 384 424 rigInterfaceCombo currentIndexChanged(int) SettingsDialog rigShareChanged(int) 564 162 384 424 rigPortTypeCombo currentIndexChanged(int) SettingsDialog rigShareChanged(int) 437 559 384 424 deleteAllQSOsButton clicked() SettingsDialog onDeleteAllQSOs() 399 421 399 417 deleteAllPasswordsButton clicked() SettingsDialog onDeleteAllPasswords() 399 611 399 417 save() addRigProfile() delRigProfile() rigChanged(int) rotChanged(int) addAntProfile() delAntProfile() tqslPathBrowse() tqslAutoDetect() addStationProfile() deleteStationProfile() doubleClickStationProfile(QModelIndex) stationCallsignChanged() adjustLocatorTextColor() eqslDirBrowse() cancelled() adjustVUCCLocatorTextColor() sotaChanged(QString) primaryCallbookChanged(int) secondaryCallbookChanged(int) paperDirBrowse() doubleClickRigProfile(QModelIndex) doubleClickAntProfile(QModelIndex) addRotProfile() delRotProfile() doubleClickRotProfile(QModelIndex) rigRXOffsetChanged(int) rigTXOffsetChanged(int) rigGetFreqChanged(int) doubleClickCWKeyProfile(QModelIndex) addCWKeyProfile() delCWKeyProfile() doubleClickCWShortcutProfile(QModelIndex) addCWShortcutProfile() delCWShortcutProfile() cwKeyChanged(int) adjustCWKeyCOMPortTextColor() adjustRigCOMPortTextColor() adjustRotCOMPortTextColor() rigStackWidgetChanged(int) rotStackWidgetChanged(int) cwKeyStackWidgetChanged(int) sotaEditFinished() wwffEditFinished() wwffChanged(QString) assignedKeyChanged(int) potaEditFinished() potaChanged(QString) joinMulticastChanged(int) adjustWSJTXMulticastAddrTextColor() doubleClickRotUsrButtonsProfile(QModelIndex) delRotUsrButtonsProfile() addRotUsrButtonsProfile() testWebLookupURL() rigPortTypeChanged(int) rotPortTypeChanged(int) hrdlogSettingChanged() rigInterfaceChanged(int) clublogSettingChanged() rotInterfaceChanged(int) rigPTTTypeChanged(int) adjustOperatorTextColor() updateDateFormatResult() qrzAddCallsignAPIKey() qrzDelCallsignAPIKey() cwModeChanged(int) rigFlowControlChanged(int) rigShareChanged(int) showRigctldAdvanced() onDeleteAllQSOs() onDeleteAllPasswords()
================================================ FILE: ui/ShowUploadDialog.cpp ================================================ #include "ShowUploadDialog.h" #include "ui_ShowUploadDialog.h" #include ShowUploadDialog::ShowUploadDialog(const QString &qsoList, QWidget *parent) : QDialog(parent), ui(new Ui::ShowUploadDialog) { ui->setupUi(this); ui->qsoText->insertPlainText(qsoList); ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Upload")); } ShowUploadDialog::~ShowUploadDialog() { delete ui; } ================================================ FILE: ui/ShowUploadDialog.h ================================================ #ifndef QLOG_UI_SHOWUPLOADDIALOG_H #define QLOG_UI_SHOWUPLOADDIALOG_H #include namespace Ui { class ShowUploadDialog; } class ShowUploadDialog : public QDialog { Q_OBJECT public: explicit ShowUploadDialog(const QString &qsoList, QWidget *parent = nullptr); ~ShowUploadDialog(); private: Ui::ShowUploadDialog *ui; }; #endif // QLOG_UI_SHOWUPLOADDIALOG_H ================================================ FILE: ui/ShowUploadDialog.ui ================================================ ShowUploadDialog 0 0 558 386 QSOs to Upload Selected QSO false false true true Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() ShowUploadDialog accept() 248 254 157 274 buttonBox rejected() ShowUploadDialog reject() 316 260 286 274 ================================================ FILE: ui/SplashScreen.h ================================================ #ifndef QLOG_UI_SPLASHSCREEN_H #define QLOG_UI_SPLASHSCREEN_H #include #include #include class SplashScreen : public QSplashScreen { public: explicit SplashScreen(const QPixmap &pixmap = QPixmap()) : QSplashScreen(pixmap) {} private: bool painted=false; void paintEvent(QPaintEvent* e) override { QSplashScreen::paintEvent(e); painted=true; } public: void ensureFirstPaint() const { while(!painted) { QThread::usleep(1e3); qApp->processEvents(); } } }; #endif // QLOG_UI_SPLASHSCREEN_H ================================================ FILE: ui/StatisticsWidget.cpp ================================================ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "StatisticsWidget.h" #include "ui_StatisticsWidget.h" #include "core/debug.h" #include "models/SqlListModel.h" #include "data/Gridsquare.h" #include "core/QSOFilterManager.h" MODULE_IDENTIFICATION("qlog.ui.statisticswidget"); void StatisticsWidget::mainStatChanged(int idx) { FCT_IDENTIFICATION; qCDebug(function_parameters) << idx; setSubTypesCombo(idx); refreshGraph(); } void StatisticsWidget::refreshWidget() { FCT_IDENTIFICATION; if ( !isVisible() ) return; refreshCombos(); refreshGraph(); } void StatisticsWidget::refreshGraph() { FCT_IDENTIFICATION; if ( !isVisible() ) return; QStringList genericFilter; genericFilter << " 1 = 1 "; //just initialization - use only in case of empty Options if ( ui->myCallCombo->currentIndex() != 0 ) genericFilter << " (station_callsign = '" + ui->myCallCombo->currentText() + "') "; if ( ui->myGridCombo->currentIndex() != 0 ) { if ( ui->myGridCombo->currentText().isEmpty() ) genericFilter << " (my_gridsquare is NULL) "; else genericFilter << " (my_gridsquare = '" + ui->myGridCombo->currentText() + "') "; } if ( ui->myRigCombo->currentIndex() != 0 ) { if ( ui->myRigCombo->currentText().isEmpty() ) genericFilter << " (my_rig is NULL) "; else genericFilter << " (my_rig = '" + ui->myRigCombo->currentText() + "') "; } if ( ui->myAntennaCombo->currentIndex() != 0 ) { if ( ui->myAntennaCombo->currentText().isEmpty() ) genericFilter << " (my_antenna is NULL) "; else genericFilter << " (my_antenna = '" + ui->myAntennaCombo->currentText() + "') "; } if ( ui->bandCombo->currentIndex() != 0 && ! ui->bandCombo->currentText().isEmpty() ) genericFilter << " (band = '" + ui->bandCombo->currentText() + "') "; if ( ui->useDateRangeCheckBox->isChecked() ) genericFilter << " (datetime(start_time) BETWEEN datetime('" + ui->startDateEdit->dateTime().toString("yyyy-MM-dd HH:mm:ss") + "') AND datetime('" + ui->endDateEdit->dateTime().toString("yyyy-MM-dd HH:mm:ss") + "') ) "; if ( ui->userFilterCombo->currentIndex() > 0 ) genericFilter << QSOFilterManager::instance()->getWhereClause(ui->userFilterCombo->currentText()); qCDebug(runtime) << "main " << ui->statTypeMainCombo->currentIndex() << " secondary " << ui->statTypeSecCombo->currentIndex(); /****************/ /* QSO Per .... */ /****************/ if ( ui->statTypeMainCombo->currentIndex() == 0 ) { QString stmt; switch ( ui->statTypeSecCombo->currentIndex() ) { case 0: // Year case 1: // Month case 2: // Day in Week case 3: // Hour { QString startGenerator = "1"; QString endGenerator = "12"; QString formatGenerator = "%m"; QString XYMapping = "col1, SUM(cnt)"; if ( ui->statTypeSecCombo->currentIndex() == 0 ) { if ( ui->useDateRangeCheckBox->isChecked() ) { startGenerator = ui->startDateEdit->date().toString("yyyy"); endGenerator = ui->endDateEdit->date().toString("yyyy"); } else { startGenerator = "CAST(MIN(start_time) as INTEGER) from contacts"; endGenerator = " (select strftime('%Y', DATE()))"; } formatGenerator = "%Y"; } else if ( ui->statTypeSecCombo->currentIndex() == 1 ) { startGenerator = "1"; endGenerator = "12"; formatGenerator = "%m"; } else if ( ui->statTypeSecCombo->currentIndex() == 2 ) { startGenerator = "0"; endGenerator = "6"; formatGenerator = "%w"; XYMapping = "case col1 when 0 THEN '" + tr("Sun") + "' " "WHEN 1 THEN '" + tr("Mon") + "' " "WHEN 2 THEN '" + tr("Tue") + "' " "WHEN 3 THEN '" + tr("Wed") + "' " "WHEN 4 THEN '" + tr("Thu") + "' " "WHEN 5 THEN '" + tr("Fri") + "' " "ELSE '" + tr("Sat") + "' END, " "SUM(cnt) "; } else if ( ui->statTypeSecCombo->currentIndex() == 3 ) { startGenerator = "0"; endGenerator = "23"; formatGenerator = "%H"; } stmt = "WITH RECURSIVE cnt(incnt) AS ( " " SELECT " + startGenerator + " " " UNION ALL " " SELECT incnt + 1 " " FROM cnt " " WHERE incnt < " + endGenerator + " " " ) " " SELECT " + XYMapping + " " " FROM " " ( " " SELECT incnt as col1, 0 as cnt from cnt " " UNION ALL " " SELECT CAST(strftime('" + formatGenerator +"', start_time) as INTEGER) as col1, count(1) as cnt " " FROM contacts " " WHERE " + genericFilter.join(" AND ") + " " " GROUP BY col1 " " ) " " GROUP BY col1 " " ORDER BY col1"; } break; case 4: // Mode stmt = "SELECT IFNULL(mode, '" + tr("Not specified") + "'), COUNT(1) FROM contacts WHERE " + genericFilter.join(" AND ") + " GROUP BY mode ORDER BY mode"; break; case 5: // Band stmt = "SELECT IFNULL(band, '" + tr("Not specified") + "'), cnt " " FROM (SELECT c.band, b.start_freq, COUNT(1) AS cnt FROM contacts c LEFT JOIN bands b ON c.band = b.name" " WHERE " + genericFilter.join(" AND ") + " GROUP BY band, start_freq) ORDER BY start_freq"; break; case 6: // Continent stmt = "SELECT IFNULL(cont, '" + tr("Not specified") + "'), COUNT(1) FROM contacts WHERE " + genericFilter.join(" AND ") + " GROUP BY cont ORDER BY cont"; break; case 7: // Prop Mode stmt = "SELECT IFNULL(prop_mode, '" + tr("Not specified") + "'), COUNT(1) FROM contacts WHERE " + genericFilter.join(" AND ") + " GROUP BY prop_mode ORDER BY prop_mode"; break; } qCDebug(runtime) << stmt; QSqlQuery query(stmt); drawBarGraphs(ui->statTypeMainCombo->currentText() + " " + ui->statTypeSecCombo->currentText(), query); } /************/ /* Percents */ /************/ else if ( ui->statTypeMainCombo->currentIndex() == 1 ) { QString stmt; switch ( ui->statTypeSecCombo->currentIndex() ) { case 0: // Confirmed/Not Confirmed stmt = "SELECT (1.0 * COUNT(1)/(SELECT COUNT(1) AS total_cnt FROM contacts WHERE " + genericFilter.join(" AND ") +")) * 100 FROM contacts WHERE " + genericFilter.join(" AND ") + " AND (eqsl_qsl_rcvd = 'Y' OR lotw_qsl_rcvd = 'Y' OR qsl_rcvd = 'Y')"; break; } QSqlQuery query(stmt); qCDebug(runtime) << stmt; QPieSeries *series = new QPieSeries(); query.next(); float confirmed = query.value(0).toInt(); float notConfirmed = 100.0 - confirmed; series->append(tr("Confirmed ") + QString::number(confirmed) + "%", confirmed); series->append(tr("Not Confirmed ") + QString::number(notConfirmed) + "%", notConfirmed); series->setLabelsVisible(true); drawPieGraph(QString(), series); } /**********/ /* TOP 10 */ /**********/ else if ( ui->statTypeMainCombo->currentIndex() == 2 ) { QString stmt; switch ( ui->statTypeSecCombo->currentIndex() ) { case 0: // Countries stmt = "SELECT translate_to_locale(COALESCE(d.name, c.dxcc)) as dxcc_display, COUNT(1) AS cnt " "FROM ( SELECT * FROM contacts WHERE " + genericFilter.join(" AND ") + ") c LEFT JOIN dxcc_entities_clublog d ON c.dxcc = d.id " "GROUP BY dxcc_display ORDER BY cnt DESC LIMIT 10"; break; case 1: // Big squares stmt = "SELECT SUBSTR(gridsquare,1,4), COUNT(1) AS cnt FROM contacts WHERE gridsquare IS NOT NULL GROUP by SUBSTR(gridsquare,1,4) ORDER BY cnt DESC LIMIT 10"; break; } qCDebug(runtime) << stmt; QSqlQuery query(stmt); drawBarGraphs(ui->statTypeMainCombo->currentText() + " " + ui->statTypeSecCombo->currentText(), query); } /*************/ /* Histogram */ /*************/ else if ( ui->statTypeMainCombo->currentIndex() == 3 ) { QString stmt; switch ( ui->statTypeSecCombo->currentIndex() ) { case 0: // Distance QString distCoef = QString::number(Gridsquare::localeDistanceCoef(locale)); stmt = QString("WITH hist AS ( " " SELECT CAST((distance * %1)/500.00 AS INTEGER) * 500 as dist_floor, " " COUNT(1) AS count " " FROM contacts " " WHERE " + genericFilter.join(" AND ") + " AND distance IS NOT NULL " " GROUP BY 1 " " ORDER BY 1 " " ) " //" SELECT dist_floor || ' - ' || (dist_floor + 500) as dist_range, count " "SELECT dist_floor as dist_range, count " " FROM hist " " ORDER BY 1").arg(distCoef); break; } qCDebug(runtime) << stmt; QSqlQuery query(stmt); drawBarGraphs(ui->statTypeMainCombo->currentText() + " " + ui->statTypeSecCombo->currentText(), query); } /***************/ /* Show on Map */ /***************/ else if ( ui->statTypeMainCombo->currentIndex() == 4 ) { QStringList confirmed("1=2 "); if ( ui->eqslCheckBox->isChecked() ) confirmed << " eqsl_qsl_rcvd = 'Y' "; if ( ui->lotwCheckBox->isChecked() ) confirmed << " lotw_qsl_rcvd = 'Y' "; if ( ui->paperCheckBox->isChecked() ) confirmed << " qsl_rcvd = 'Y' "; QString innerCase = " CASE WHEN (" + confirmed.join("or") + ") THEN 1 ELSE 0 END "; QString stmtMyLocations = "SELECT DISTINCT my_gridsquare FROM contacts WHERE " + genericFilter.join(" AND "); QSqlQuery myLocations(stmtMyLocations); qCDebug(runtime) << stmtMyLocations; drawMyLocationsOnMap(myLocations); QString stmt; switch ( ui->statTypeSecCombo->currentIndex() ) { case 0: // QSOs case 1: // Confirmed & WorkedGrids stmt = "SELECT callsign, gridsquare, my_gridsquare, SUM(confirmed) FROM (SELECT callsign, gridsquare, my_gridsquare," + innerCase +" AS confirmed FROM contacts WHERE gridsquare is not NULL AND " + genericFilter.join(" AND ") +" ) GROUP BY callsign, gridsquare, my_gridsquare"; break; case 2: // ODX QString unit; Gridsquare::distance2localeUnitDistance(0, unit, locale); QString distCoef = QString::number(Gridsquare::localeDistanceCoef(locale)); QString sel = QString("SELECT callsign || '
' || CAST(ROUND(distance * %1,0) AS INT) || ' %2', gridsquare, my_gridsquare, ").arg(distCoef, unit); stmt = sel + innerCase + " AS confirmed FROM contacts WHERE " + genericFilter.join(" AND ") + " AND distance = (SELECT MAX(distance) FROM contacts WHERE " + genericFilter.join(" AND ") + ")"; break; } QSqlQuery query(stmt); qCDebug(runtime) << stmt; switch ( ui->statTypeSecCombo->currentIndex() ) { case 0: case 2: drawPointsOnMap(query); break; case 1: drawFilledGridsOnMap(query); break; } ui->stackedWidget->setCurrentIndex(1); } } void StatisticsWidget::dateRangeCheckBoxChanged(int) { FCT_IDENTIFICATION; if ( ui->useDateRangeCheckBox->isChecked() ) { ui->startDateEdit->setEnabled(true); ui->endDateEdit->setEnabled(true); } else { ui->startDateEdit->setEnabled(false); ui->endDateEdit->setEnabled(false); } refreshGraph(); } void StatisticsWidget::mapLoaded(bool) { FCT_IDENTIFICATION; isMainPageLoaded = true; /* which layers will be active */ postponedScripts += layerControlHandler.generateMapMenuJS(true, false, false, false, false, false, false, false, true); main_page->runJavaScript(postponedScripts); layerControlHandler.restoreLayerControlStates(main_page); } void StatisticsWidget::changeTheme(int theme, bool isDark) { FCT_IDENTIFICATION; qCDebug(function_parameters) << theme << isDark; QString themeJavaScript; if (isDark) /* dark mode */ themeJavaScript = "map.getPanes().tilePane.style.webkitFilter=\"brightness(0.6) invert(1) contrast(3) hue-rotate(200deg) saturate(0.3) brightness(0.9)\";"; else themeJavaScript = "map.getPanes().tilePane.style.webkitFilter=\"\";"; if ( !isMainPageLoaded ) postponedScripts.append(themeJavaScript); else main_page->runJavaScript(themeJavaScript); } StatisticsWidget::StatisticsWidget(QWidget *parent) : QWidget(parent), ui(new Ui::StatisticsWidget), main_page(new WebEnginePage(this)), isMainPageLoaded(false), layerControlHandler("statistics", parent) { FCT_IDENTIFICATION; ui->setupUi(this); ui->myCallCombo->setModel(new QStringListModel(this)); ui->myGridCombo->setModel(new QStringListModel(this)); ui->myRigCombo->setModel(new QStringListModel(this)); ui->myAntennaCombo->setModel(new QStringListModel(this)); ui->bandCombo->setModel(new QStringListModel(this)); ui->userFilterCombo->setModel(QSOFilterManager::QSOFilterModel(tr("No User Filter"), ui->userFilterCombo)); ui->startDateEdit->setDisplayFormat(locale.formatDateTimeShortWithYYYY()); ui->startDateEdit->setDate(QDate::currentDate().addDays(DEFAULT_STAT_RANGE)); ui->startDateEdit->setTime(QTime::fromMSecsSinceStartOfDay(0)); ui->endDateEdit->setDisplayFormat(locale.formatDateTimeShortWithYYYY()); ui->endDateEdit->setDate(QDate::currentDate()); ui->endDateEdit->setTime(QTime::fromMSecsSinceStartOfDay(86399999)); ui->graphView->setRenderHint(QPainter::Antialiasing); ui->graphView->setChart(new QChart()); main_page->setWebChannel(&channel); ui->mapView->setPage(main_page); connect(ui->mapView, &QWebEngineView::loadFinished, this, &StatisticsWidget::mapLoaded); main_page->load(QUrl(QStringLiteral("qrc:/res/map/onlinemap.html"))); ui->mapView->setFocusPolicy(Qt::ClickFocus); channel.registerObject("layerControlHandler", &layerControlHandler); } StatisticsWidget::~StatisticsWidget() { FCT_IDENTIFICATION; main_page->deleteLater(); delete ui; } bool StatisticsWidget::event(QEvent *event) { if (event->type() == QEvent::Show) { // We will not use refreshWidget here, even though at first glance it appears // to do the same thing. The difference is that we want class constructor to be as fast as possible. // Therefore, in the constructor, we do not populate the combo boxes. As a result, they are empty // when first displayed and need to be loaded and then combos for Rig, Ant, etc., can be set. refreshCombos(); ui->statTypeMainCombo->blockSignals(true); ui->statTypeMainCombo->setCurrentIndex(0); ui->statTypeMainCombo->blockSignals(false); setSubTypesCombo(ui->statTypeMainCombo->currentIndex()); ui->myRigCombo->blockSignals(true); ui->myRigCombo->setCurrentIndex(0); ui->myRigCombo->blockSignals(false); ui->myAntennaCombo->blockSignals(true); ui->myAntennaCombo->setCurrentIndex(0); ui->myAntennaCombo->blockSignals(false); ui->userFilterCombo->blockSignals(true); ui->userFilterCombo->setCurrentIndex(0); ui->userFilterCombo->blockSignals(false); refreshGraph(); } return QWidget::event(event); // Propagate the event further } void StatisticsWidget::drawBarGraphs(const QString &title, QSqlQuery &query) { FCT_IDENTIFICATION; if ( query.lastQuery().isEmpty() ) return; QChart *chart = ui->graphView->chart(); if ( chart != nullptr ) chart->deleteLater(); chart = new QChart(); QBarSet* set = new QBarSet(title, chart); QBarCategoryAxis* axisX = new QBarCategoryAxis(chart); QBarSeries* series = new QBarSeries(chart); QValueAxis *axisY = new QValueAxis(chart); while ( query.next() ) { axisX->append(query.value(0).toString()); *set << query.value(1).toInt(); } series->append(set); chart->addSeries(series); chart->addAxis(axisX, Qt::AlignBottom); series->attachAxis(axisX); axisY->setTickCount(10); chart->addAxis(axisY, Qt::AlignLeft); series->attachAxis(axisY); axisY->applyNiceNumbers(); axisY->setLabelFormat("%d"); series->setLabelsPosition(QAbstractBarSeries::LabelsInsideEnd); series->setLabelsVisible(true); chart->setTitle(title); chart->legend()->hide(); chart->setAnimationOptions(QChart::SeriesAnimations); chart->layout()->setContentsMargins(0, 0, 0, 0); chart->setBackgroundRoundness(0); ui->stackedWidget->setCurrentIndex(0); ui->graphView->setChart(chart); } void StatisticsWidget::drawPieGraph(const QString &title, QPieSeries *series) { FCT_IDENTIFICATION; QChart *chart = ui->graphView->chart(); if ( chart != nullptr ) chart->deleteLater(); chart = new QChart(); chart->setAnimationOptions(QChart::SeriesAnimations); chart->addSeries(series); chart->legend()->hide(); chart->setTitle(title); ui->stackedWidget->setCurrentIndex(0); ui->graphView->setChart(chart); } void StatisticsWidget::drawMyLocationsOnMap(QSqlQuery &query) { FCT_IDENTIFICATION; if ( query.lastQuery().isEmpty() ) return; QStringList locationIcons; QStringList rawLocationsPoint; while ( query.next() ) { const QString &loc = query.value(0).toString(); const Gridsquare stationGrid(loc); if ( stationGrid.isValid() ) { double lat = stationGrid.getLatitude(); double lon = stationGrid.getLongitude(); locationIcons.append(QString("[\"%1\", %2, %3, homeIcon]").arg(loc).arg(lat).arg(lon)); rawLocationsPoint.append(QString("[%1, %2]").arg(lat).arg(lon)); } } QString javaScript = QString("grids_confirmed = [];" "grids_worked = [];" "drawPointsGroup2([%1]);" "maidenheadConfWorked.redraw();" "map.panTo([0, L.latLngBounds([%2]).getCenter().lng]);").arg(locationIcons.join(","), rawLocationsPoint.join(",")); qCDebug(runtime) << javaScript; if ( !isMainPageLoaded ) postponedScripts.append(javaScript); else main_page->runJavaScript(javaScript); } void StatisticsWidget::drawPointsOnMap(QSqlQuery &query) { FCT_IDENTIFICATION; if ( query.lastQuery().isEmpty() ) return; QList stations; QList shortPaths; qulonglong count = 0; while ( query.next() ) { const Gridsquare stationGrid(query.value(1).toString()); const Gridsquare myStationGrid(query.value(2).toString()); if ( stationGrid.isValid() ) { count++; double lat = stationGrid.getLatitude(); double lon = stationGrid.getLongitude(); // do not wrap the points double delta = lon - myStationGrid.getLongitude(); if ( delta > 180 ) lon -= 360; if ( delta < -180 ) lon += 360; stations.append(QString("[\"%1\", %2, %3, %4]").arg(query.value(0).toString()) .arg(lat) .arg(lon) .arg((query.value(3).toInt()) > 0 ? "greenIconSmall" : "yellowIconSmall")); shortPaths.append(QString("[%1, %2, %3, %4]") .arg(myStationGrid.getLatitude()) .arg(myStationGrid.getLongitude()) .arg(lat) .arg(lon)); } } if ( count > 50000 ) { QMessageBox::StandardButton reply; reply = QMessageBox::question(this, tr("Statistics"), tr("Over 50000 QSOs. Display them?"), QMessageBox::Yes|QMessageBox::No); if ( reply != QMessageBox::Yes ) stations.clear(); } QString javaScript = QString("grids_confirmed = [];" "grids_worked = [];" "drawPointsBusy([%1], '%2');" "drawShortPathsBusy([%3], '%4');" "maidenheadConfWorked.redraw();").arg(stations.join(","), tr("Rendering QSOs..."), shortPaths.join(","), tr("Rendering QSOs...")); qCDebug(runtime) << javaScript; if ( !isMainPageLoaded ) postponedScripts.append(javaScript); else main_page->runJavaScript(javaScript); } void StatisticsWidget::drawFilledGridsOnMap(QSqlQuery &query) { FCT_IDENTIFICATION; if ( query.lastQuery().isEmpty() ) return; QList confirmedGrids; QList workedGrids; while ( query.next() ) { if ( query.value(3).toInt() > 0 && ! confirmedGrids.contains(query.value(1).toString()) ) confirmedGrids << QString("\"" + query.value(1).toString() + "\""); else workedGrids << QString("\"" + query.value(1).toString() + "\""); } QString javaScript = QString("grids_confirmed = [ %1 ]; " "grids_worked = [ %2 ];" "mylocations = [];" "drawPointsBusy([], '');" "drawShortPathsBusy([], '');" "maidenheadConfWorked.redraw();").arg(confirmedGrids.join(","), workedGrids.join(",")); qCDebug(runtime) << javaScript; if ( !isMainPageLoaded ) postponedScripts.append(javaScript); else main_page->runJavaScript(javaScript); } void StatisticsWidget::refreshCombos() { FCT_IDENTIFICATION; refreshCombo(ui->myCallCombo, QLatin1String("SELECT DISTINCT UPPER(station_callsign) FROM contacts ORDER BY station_callsign")); refreshCombo(ui->myRigCombo, QLatin1String("SELECT DISTINCT my_rig FROM contacts ORDER BY my_rig")); refreshCombo(ui->myAntennaCombo, QLatin1String("SELECT DISTINCT my_antenna FROM contacts ORDER BY my_antenna")); refreshCombo(ui->bandCombo, QLatin1String("SELECT DISTINCT band FROM contacts c, bands b WHERE c.band = b.name ORDER BY b.start_freq;")); refreshCombo(ui->myGridCombo, QLatin1String("SELECT DISTINCT UPPER(my_gridsquare) FROM contacts ORDER BY my_gridsquare")); SqlListModel *sqlModel = qobject_cast(ui->userFilterCombo->model()); if ( sqlModel ) sqlModel->refresh(); } void StatisticsWidget::setSubTypesCombo(int mainTypeIdx) { FCT_IDENTIFICATION; ui->statTypeSecCombo->blockSignals(true); ui->statTypeSecCombo->clear(); ui->lotwCheckBox->setEnabled(false); ui->eqslCheckBox->setEnabled(false); ui->paperCheckBox->setEnabled(false); switch ( mainTypeIdx ) { /* QSOs per */ case 0: { ui->statTypeSecCombo->addItem(tr("Year")); ui->statTypeSecCombo->addItem(tr("Month")); ui->statTypeSecCombo->addItem(tr("Day in Week")); ui->statTypeSecCombo->addItem(tr("Hour")); ui->statTypeSecCombo->addItem(tr("Mode")); ui->statTypeSecCombo->addItem(tr("Band")); ui->statTypeSecCombo->addItem(tr("Continent")); ui->statTypeSecCombo->addItem(tr("Propagation Mode")); } break; /* Percents */ case 1: { ui->statTypeSecCombo->addItem(tr("Confirmed / Not Confirmed")); } break; /* TOP 10 */ case 2: { ui->statTypeSecCombo->addItem(tr("Countries")); ui->statTypeSecCombo->addItem(tr("Big Gridsquares")); } break; /* Histogram */ case 3: { QString unit; Gridsquare::distance2localeUnitDistance(0, unit, locale); ui->statTypeSecCombo->addItem(tr("Distance") + QString(" [%1]").arg(unit)); } break; /* Show on Map */ case 4: { ui->statTypeSecCombo->addItem(tr("QSOs")); ui->statTypeSecCombo->addItem(tr("Confirmed/Worked Grids")); ui->statTypeSecCombo->addItem(tr("ODX")); ui->lotwCheckBox->setEnabled(true); ui->eqslCheckBox->setEnabled(true); ui->paperCheckBox->setEnabled(true); } break; } ui->statTypeSecCombo->blockSignals(false); } void StatisticsWidget::refreshCombo(QComboBox * combo, const QString &sqlQeury) { FCT_IDENTIFICATION; QString currSelection = combo->currentText(); combo->blockSignals(true); combo->setModel(new SqlListModel(sqlQeury,tr("All"), this)); combo->setCurrentText(currSelection); combo->blockSignals(false); } ================================================ FILE: ui/StatisticsWidget.h ================================================ #ifndef QLOG_UI_STATISTICSWIDGET_H #define QLOG_UI_STATISTICSWIDGET_H #include #include #include #include #include #include "ui/MapWebChannelHandler.h" #include "ui/WebEnginePage.h" #include "core/LogLocale.h" namespace Ui { class StatisticsWidget; } #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) using namespace QtCharts; #endif class StatisticsWidget : public QWidget { Q_OBJECT public slots: void mainStatChanged(int); void dateRangeCheckBoxChanged(int); void mapLoaded(bool); void changeTheme(int, bool isDark); void refreshWidget(); private slots: void refreshGraph(); public: explicit StatisticsWidget(QWidget *parent = nullptr); ~StatisticsWidget(); protected: bool event(QEvent *event) override; private: void drawBarGraphs(const QString &title, QSqlQuery &query); void drawPieGraph(const QString &title, QPieSeries* series); void drawMyLocationsOnMap(QSqlQuery &); void drawPointsOnMap(QSqlQuery&); void drawFilledGridsOnMap(QSqlQuery&); void refreshCombos(); void setSubTypesCombo(int mainTypeIdx); void refreshCombo(QComboBox * combo, const QString &sqlQeury); private: Ui::StatisticsWidget *ui; WebEnginePage *main_page; bool isMainPageLoaded; QString postponedScripts; QWebChannel channel; MapWebChannelHandler layerControlHandler; LogLocale locale; // default statistics interval [in days] const int DEFAULT_STAT_RANGE = -1; }; #endif // QLOG_UI_STATISTICSWIDGET_H ================================================ FILE: ui/StatisticsWidget.ui ================================================ StatisticsWidget 0 0 1058 730 Statistics 2 6 6 6 6 0 9 0 0 0 0 3 Date Range 0 0 false false 0 0 QAbstractSpinBox::UpDownArrows true false 1999 12 25 yyyy-MM-dd HH:mm true Qt::UTC 0 0 to false 0 0 false true true false yyyy-MM-dd HH:mm true Qt::UTC Qt::Horizontal 40 20 Band Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter User Filter Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter Confirmed by false LoTW false eQSL false Paper 0 0 0 0 0 0 Graph Type 0 0 Qt::LeftToRight QComboBox::NoInsert QComboBox::AdjustToContentsOnFirstShow QSOs per Percents Top 10 Histogram Show on Map 3 10 0 0 0 0 My Callsign 0 0 My Gridsquare 0 0 My Rig 0 0 0 0 My Antenna 0 0 1 2 2 2 2 2 2 2 2 2 2 Qt::NoContextMenu QWebEngineView QWidget
QtWebEngineWidgets/QWebEngineView
QChartView QGraphicsView
QtCharts
userFilterCombo bandCombo lotwCheckBox eqslCheckBox paperCheckBox useDateRangeCheckBox startDateEdit endDateEdit statTypeMainCombo statTypeSecCombo myCallCombo myGridCombo myRigCombo myAntennaCombo graphView statTypeMainCombo currentIndexChanged(int) StatisticsWidget mainStatChanged(int) 594 85 642 303 useDateRangeCheckBox stateChanged(int) StatisticsWidget dateRangeCheckBoxChanged(int) 253 185 642 303 statTypeSecCombo currentIndexChanged(int) StatisticsWidget refreshGraph() 818 85 745 604 myGridCombo currentIndexChanged(int) StatisticsWidget refreshGraph() 594 137 604 599 myCallCombo currentIndexChanged(int) StatisticsWidget refreshGraph() 234 137 387 602 myAntennaCombo currentIndexChanged(int) StatisticsWidget refreshGraph() 1042 137 1001 605 myRigCombo currentIndexChanged(int) StatisticsWidget refreshGraph() 818 137 1102 606 startDateEdit dateTimeChanged(QDateTime) StatisticsWidget refreshGraph() 412 191 234 602 endDateEdit dateTimeChanged(QDateTime) StatisticsWidget refreshGraph() 593 191 79 603 bandCombo currentIndexChanged(int) StatisticsWidget refreshGraph() 818 190 657 504 lotwCheckBox stateChanged(int) StatisticsWidget refreshGraph() 525 221 657 504 eqslCheckBox stateChanged(int) StatisticsWidget refreshGraph() 671 221 657 504 paperCheckBox stateChanged(int) StatisticsWidget refreshGraph() 817 221 657 504 userFilterCombo currentIndexChanged(int) StatisticsWidget refreshGraph() 216 27 528 364 mainStatChanged(int) refreshGraph() dateRangeCheckBoxChanged(int)
================================================ FILE: ui/UploadQSODialog.cpp ================================================ #include #include #include #include "UploadQSODialog.h" #include "ui_UploadQSODialog.h" #include "core/debug.h" #include "models/SqlListModel.h" #include "core/LogParam.h" #include "data/StationProfile.h" #include "service/clublog/ClubLog.h" #include "service/eqsl/Eqsl.h" #include "service/lotw/Lotw.h" #include "service/qrzcom/QRZ.h" #include "service/hrdlog/HRDLog.h" #include "service/cloudlog/Cloudlog.h" MODULE_IDENTIFICATION("qlog.ui.uploadqsodialog"); UploadQSODialog::UploadQSODialog(QWidget *parent) : QDialog(parent), ui(new Ui::UploadQSODialog), detailQSOsModel(new QStandardItemModel(this)), executeQueryEnabled(false) { FCT_IDENTIFICATION; ui->setupUi(this); onlineServices.insert(LOTWID, UploadTask(LOTWID, tr("LoTW"), new LotwUploader(this), "lotw_qsl_sent", "lotw_qslsdate", ui->lotwCheckbox, ui->lotwNumberLabel, !LotwBase::getUsername().isEmpty())); onlineServices.insert(EQSLID, UploadTask(EQSLID, tr("eQSL"), new EQSLUploader(this), "eqsl_qsl_sent", "eqsl_qslsdate", ui->eqslCheckbox, ui->eqslNumberLabel, !EQSLBase::getUsername().isEmpty())); onlineServices.insert(CLUBLOGID, UploadTask(CLUBLOGID, tr("Clublog"), new ClubLogUploader(this), "clublog_qso_upload_status", "clublog_qso_upload_date", ui->clublogCheckbox, ui->clublogNumberLabel, !ClubLogBase::getEmail().isEmpty())); onlineServices.insert(HRDLOGID, UploadTask(HRDLOGID, tr("HRDLog"), new HRDLogUploader(this), "hrdlog_qso_upload_status", "hrdlog_qso_upload_date", ui->hrdlogCheckbox, ui->hrdlogNumberLabel, !HRDLogBase::getRegisteredCallsign().isEmpty())); onlineServices.insert(QRZCOMID, UploadTask(QRZCOMID, tr("QRZ.com"), new QRZUploader(this), "qrzcom_qso_upload_status", "qrzcom_qso_upload_date", ui->qrzCheckbox, ui->qrzNumberLabel, !QRZBase::getLogbookAPIKey(QRZBase::getInternalAPIUsername()).isEmpty())); onlineServices.insert(WAVELOGID, UploadTask(WAVELOGID, tr("Wavelog"), new CloudlogUploader(this), "contacts_autovalue.wavelog_qso_upload_status", "contacts_autovalue.wavelog_qso_upload_date", ui->wavelogCheckbox, ui->wavelogNumberLabel, !CloudlogBase::getLogbookAPIKey().isEmpty())); ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("&Upload")); ui->myCallsignCombo->blockSignals(true); ui->myGridCombo->blockSignals(true); // based on LoTW source code and ADIF spec - priority STATION_CALLSIGN, OPERATOR ui->myCallsignCombo->setModel(new SqlListModel("SELECT DISTINCT UPPER(COALESCE(NULLIF(TRIM(station_callsign), ''), TRIM(operator))) " "FROM contacts " "WHERE station_callsign IS NOT NULL OR operator IS NOT NULL " "ORDER BY station_callsign", "", ui->myCallsignCombo)); ui->myStationProfileCombo->setModel(new SqlListModel("SELECT DISTINCT profile_name FROM station_profiles ORDER BY profile_name", "", ui->myStationProfileCombo)); setEQSLSettingVisible(false); setClublogSettingVisible(false); setLotwSettingVisible(false); setQSODetailVisible(false); setWavelogSettingVisible(false); // First entry is empty (no -l argument will be passed to TQSL). tqslLocations = LotwBase::getTQSLStationLocations(); ui->lotwLocationCombo->addItem(tr("Unspecified"), QString()); for ( const TQSLStationLocation &loc : static_cast>(tqslLocations) ) { QString displayText = loc.name; QStringList details; if ( !loc.callsign.isEmpty() ) details << loc.callsign; if ( !loc.grid.isEmpty() ) details << loc.grid.toUpper(); if ( !details.isEmpty() ) displayText += " (" + details.join(", ") + ")"; ui->lotwLocationCombo->addItem(displayText, loc.name); } connect(ui->lotwLocationCombo, QOverload::of(&QComboBox::currentIndexChanged), this, &UploadQSODialog::updateLotwLocationWarning); loadDialogState(); getWavelogStationID(); ui->myCallsignCombo->blockSignals(false); ui->myGridCombo->blockSignals(false); executeQueryEnabled = true; executeQuery(); } UploadQSODialog::~UploadQSODialog() { FCT_IDENTIFICATION; allSelectedQSOs.clear(); detailQSOsModel->deleteLater(); delete ui; } void UploadQSODialog::showQSODetails() { FCT_IDENTIFICATION; setQSODetailVisible(!ui->detailQSOView->isVisible()); } void UploadQSODialog::setEQSLSettingVisible(bool visible) { FCT_IDENTIFICATION; ui->eqslGroup->setVisible(visible); adjustSize(); } void UploadQSODialog::setClublogSettingVisible(bool visible) { FCT_IDENTIFICATION; ui->clublogGroup->setVisible(visible); adjustSize(); } void UploadQSODialog::setLotwSettingVisible(bool visible) { FCT_IDENTIFICATION; ui->lotwGroup->setVisible(visible); adjustSize(); } void UploadQSODialog::setWavelogSettingVisible(bool visible) { FCT_IDENTIFICATION; ui->wavelogGroup->setVisible(visible); adjustSize(); } void UploadQSODialog::setQSODetailVisible(bool visible) { FCT_IDENTIFICATION; ui->detailQSOView->setVisible(visible); ui->line->setVisible(visible); ui->showQSOButton->setText(visible ? tr("Hide QSOs...") : tr("Show QSOs...")); adjustSize(); } void UploadQSODialog::loadDialogState() { FCT_IDENTIFICATION; const StationProfile &profile = StationProfilesManager::instance()->getCurProfile1(); ui->clublogCheckbox->setChecked(ui->clublogCheckbox->isEnabled() && LogParam::getUploadServiceState("clublog")); ui->eqslCheckbox->setChecked(ui->eqslCheckbox->isEnabled() && LogParam::getUploadServiceState("eqsl")); ui->lotwCheckbox->setChecked(ui->lotwCheckbox->isEnabled() && LogParam::getUploadServiceState("lotw")); ui->hrdlogCheckbox->setChecked(ui->hrdlogCheckbox->isEnabled() && LogParam::getUploadServiceState("hrdlog")); ui->qrzCheckbox->setChecked(ui->qrzCheckbox->isEnabled() && LogParam::getUploadServiceState("qrzcom")); ui->wavelogCheckbox->setChecked(ui->wavelogCheckbox->isEnabled() && LogParam::getUploadServiceState("wavelog")); int index = ui->myCallsignCombo->findText(profile.callsign); if ( index >= 0 ) ui->myCallsignCombo->setCurrentIndex(index); else ui->myCallsignCombo->setCurrentText(LogParam::getUploadQSOLastCall()); handleCallsignChange(ui->myCallsignCombo->currentText()); ui->myGridCombo->setCurrentIndex(0); // ANY value index = ui->myStationProfileCombo->findText(profile.profileName); ui->myStationProfileCombo->setCurrentIndex( ( index >= 0 ) ? index : -1); ui->eqslQSLComment->setChecked(LogParam::getUploadeqslQSLComment()); ui->eqslQSLMessage->setChecked(LogParam::getUploadeqslQSLMessage()); ui->eqslQTHProfileEdit->setText(LogParam::getUploadeqslQTHProfile()); ui->myStationProfileCheckbox->setChecked(LogParam::getUploadQSOFilterType() == 1); ui->wavelogStationIDSpin->setValue(LogParam::getCloudlogStationID()); const int locIdx = ui->lotwLocationCombo->findData(LogParam::getUploadLoTWLocation()); ui->lotwLocationCombo->setCurrentIndex(locIdx >= 0 ? locIdx : 0); updateLotwLocationWarning(); } void UploadQSODialog::saveDialogState() { FCT_IDENTIFICATION; LogParam::setUploadServiceState("clublog", ui->clublogCheckbox->isChecked()); LogParam::setUploadServiceState("eqsl", ui->eqslCheckbox->isChecked()); LogParam::setUploadServiceState("lotw", ui->lotwCheckbox->isChecked()); LogParam::setUploadServiceState("hrdlog", ui->hrdlogCheckbox->isChecked()); LogParam::setUploadServiceState("qrzcom", ui->qrzCheckbox->isChecked()); LogParam::setUploadServiceState("wavelog", ui->wavelogCheckbox->isChecked()); LogParam::setUploadQSOLastCall(ui->myCallsignCombo->currentText()); LogParam::setUploadeqslQSLComment(ui->eqslQSLComment->isChecked()); LogParam::setUploadeqslQSLMessage(ui->eqslQSLMessage->isChecked()); LogParam::setUploadeqslQTHProfile(ui->eqslQTHProfileEdit->text()); LogParam::setUploadQSOFilterType(ui->myCallsignCheckbox->isChecked() ? 0 : 1); LogParam::setCloudlogStationID(ui->wavelogStationIDSpin->value()); LogParam::setUploadLoTWLocation(ui->lotwLocationCombo->currentData().toString()); } void UploadQSODialog::processNextUploader() { FCT_IDENTIFICATION; if ( uploadTaskQueue.isEmpty() ) { uploadFinished(); return; } currentTask = uploadTaskQueue.takeFirst(); GenericQSOUploader *uploader = currentTask.getUploader(); if ( !uploader ) { qWarning() << "Uploader is null - skipping"; uploadFinished(); return; } qCDebug(runtime) << "Uploading to" << currentTask.getServiceName(); QProgressDialog* dialog = new QProgressDialog(tr("Uploading to %1").arg(currentTask.getServiceName()), tr("Cancel"), 0, 0, this); dialog->setWindowModality(Qt::WindowModal); dialog->setValue(0); dialog->setAttribute(Qt::WA_DeleteOnClose, true); dialog->show(); connect(uploader, &GenericQSOUploader::uploadedQSO, this, [this, dialog, uploader](qulonglong qsoID) { // this part updates one QSO - it is use in case of services when a batch upload // is not supported qCDebug(runtime) << "signal uploadedQSO"; QString contactIDField("id"); QString tableName("contacts"); QString statusField = currentTask.getDBUploadStatusFieldName(); QString dateField = currentTask.getDBUploadDateFieldName(); if ( currentTask.getServiceID() == WAVELOGID ) { statusField.remove("contacts_autovalue."); // sqlite does not support aliases in the Update statement. dateField.remove("contacts_autovalue."); contactIDField = "contactid"; tableName = "contacts_autovalue"; } const QString &statement = QString("UPDATE %1 " "SET %2='Y', %3 = strftime('%Y-%m-%d',DATETIME('now', 'utc')) " "WHERE %4 = :qsoID").arg(tableName, statusField, dateField, contactIDField); qCDebug(runtime) << statement; // update in-DB record QSqlQuery updateQuery; updateQuery.prepare(statement); updateQuery.bindValue(":qsoID", qsoID); if ( ! updateQuery.exec() ) { const QString &taskName = currentTask.getServiceName(); qWarning() << "Cannot Update" << taskName << "Upload status for QSO number " << qsoID << updateQuery.lastError().text(); uploader->abortRequest(); dialog->done(QDialog::Accepted); QMessageBox::warning(this, tr("QLog Warning - %1").arg(taskName), tr("Cannot update QSO Status")); processNextUploader(); } // update in-memory record auto recordRef = allSelectedQSOs.value(qsoID); if ( !recordRef.isNull() ) { recordRef->setValue(statusField, "Y"); // I don't know but it seems that RETURNING clause does not work under QT. // So I'll set it to the current QT date. recordRef->setValue(dateField, QDateTime::currentDateTimeUtc().date().toString(Qt::ISODate)); } dialog->setValue(dialog->value() + 1); }); connect(uploader, &GenericQSOUploader::uploadFinished, this, [dialog, this]() { qCDebug(runtime) << "signal uploadFinished"; // this part updates all uploaded QSOs - it is use in case of services when a batch upload // is supported dialog->done(QDialog::Accepted); if ( currentTask.getServiceID() != HRDLOGID && currentTask.getServiceID() != QRZCOMID && currentTask.getServiceID() != WAVELOGID ) { const QString statusField = currentTask.getDBUploadStatusFieldName(); const QString dateField = currentTask.getDBUploadDateFieldName(); const QList idsToUpdate = currentTask.getQSOIDs(); QStringList idStrings; for ( qulonglong id : idsToUpdate ) idStrings.append(QString::number(id)); QString statement = QString("UPDATE contacts " "SET %1='Y', %2 = strftime('%Y-%m-%d',DATETIME('now', 'utc')) " "WHERE id IN (%3) ").arg(statusField, dateField, idStrings.join(",")); QSqlQuery updateQuery; updateQuery.prepare(statement); if ( !updateQuery.exec() ) qWarning() << "Cannot update" << currentTask.getServiceName() << "Upload status in DB" << updateQuery.lastError().text(); else currentTask.updateAllDBFieldValue(statusField, "Y", dateField, QDateTime::currentDateTimeUtc().date().toString(Qt::ISODate)); } processNextUploader(); }); connect(uploader, &GenericQSOUploader::uploadError, this, [this, dialog](const QString &msg) { qWarning() << currentTask.getServiceName() << "Upload Error: " << msg; dialog->done(QDialog::Accepted); QMessageBox::warning(this, tr("QLog Warning - %1").arg(currentTask.getServiceName()), tr("Cannot upload the QSO(s): ") + msg); processNextUploader(); }); connect(dialog, &QProgressDialog::canceled, this, [this, uploader]() { qCDebug(runtime)<< "Operation canceled"; uploader->abortRequest(); uploadFinished(); }); QVariantMap uploadConfig; // Special params for selected services switch ( currentTask.getServiceID() ) { case LOTWID: { // Pass the selected TQSL location as -l argument; index 0 = let TQSL choose if ( ui->lotwLocationCombo->currentIndex() > 0) uploadConfig = LotwUploader::generateUploadConfigMap(ui->lotwLocationCombo->currentData().toString()); break; } case EQSLID: uploadConfig = EQSLUploader::generateUploadConfigMap(ui->eqslQTHProfileEdit->text(), ui->eqslQSLNone->isChecked(), ui->eqslQSLComment->isChecked()); break; case CLUBLOGID: uploadConfig = ClubLogUploader::generateUploadConfigMap(ui->myCallsignCombo->currentText(), ui->clublogClearCheckbox->isChecked()); break; case WAVELOGID: uploadConfig = CloudlogUploader::generateUploadConfigMap(ui->wavelogStationIDSpin->value()); break; default: break; } const QList &list = currentTask.getQSOList(); // set progress bar range for services that do not support the batch upload if ( currentTask.getServiceID() == HRDLOGID || currentTask.getServiceID() == QRZCOMID || currentTask.getServiceID() == WAVELOGID ) dialog->setRange(0, list.size()); uploader->uploadQSOList(list, uploadConfig); } void UploadQSODialog::startUploadQueue() { FCT_IDENTIFICATION; saveDialogState(); for ( auto it = onlineServices.begin(); it != onlineServices.end(); ++it ) if ( !it.value().isQSOListEmpty() ) uploadTaskQueue.append(it.value()); if ( uploadTaskQueue.isEmpty() ) { QMessageBox::information(this, tr("QLog Information"), tr("No QSO found to upload.")); return; } processNextUploader(); } void UploadQSODialog::uploadFinished() { FCT_IDENTIFICATION; QMessageBox::information(this, tr("QLog Information"), tr("QSO(s) were uploaded to the selected services")); accept(); } void UploadQSODialog::updateQSONumbers() { FCT_IDENTIFICATION; for ( auto it = onlineServices.begin(); it != onlineServices.end(); ++it ) it->updateQSONumberLabel(); } void UploadQSODialog::getWavelogStationID() { FCT_IDENTIFICATION; CloudlogUploader *ptr = qobject_cast(onlineServices.value(WAVELOGID).getUploader()); if ( ptr ) { connect(ptr, &CloudlogUploader::stationIDsUpdated, this, [ptr, this]() { availableWavelogStationIDs = ptr->getAvailableStationIDs(); updateWavelogStationLabel(); }); ptr->sendStationInfoReq(); } } void UploadQSODialog::executeQuery() { FCT_IDENTIFICATION; if ( !executeQueryEnabled ) { qCDebug(runtime) << "query disabled"; return; } allSelectedQSOs.clear(); detailQSOsModel->clear(); detailQSOsModel->setHorizontalHeaderLabels({tr("Time"), tr("Callsign"), tr("Mode"), tr("Upload to")}); QStringList qslUploadStatuses = {"'M'", "'R'", "'Q'"}; // joined "Upload status" and "sent status" if ( ui->includeStatusNoCheckbox->isChecked() ) qslUploadStatuses << "'N'"; if ( ui->includeIgnoreCheckbox->isChecked() ) qslUploadStatuses << "'I'"; const QString whereTemplate("((UPPER(%1) in (%2) OR %1 IS NULL) %3)"); const QString statusColumnTemplate("CASE WHEN (%1) THEN 1 ELSE 0 END AS status_%2"); QStringList serviceSelectConditions; QStringList serviceStatusColumns; // prepare all params for building WHERE Clause for ( auto it = onlineServices.begin(); it != onlineServices.end(); ++it ) { UploadTask &task = it.value(); task.clearEnqueuedQSOs(); if ( !task.isChecked() ) continue; QStringList uploadStatuses(qslUploadStatuses); if ( it.key() == WAVELOGID ) { if ( ui->wavelogReuploadCheckbox->isChecked() ) uploadStatuses << "'Y'"; else // Wavelog reports duplicates as errors with translated error messages. // QLog cannot reliably distinguish between a duplicate and a real error. // Therefore, it is necessary to ensure that QLog sends only // new QSOs to Cloudlog/Wavelog. // Currently only two statues are use for Wavelog - Y/M - removing M (Modified) uploadStatuses.removeAll("'M'"); } if ( it.key() == CLUBLOGID && ui->clublogClearCheckbox->isChecked() ) { //reupload all QSOs (except N) uploadStatuses << "'Y'"; } QString addlCondition = ( it.key() == LOTWID && ui->lotwCheckbox->isChecked() ) ? "AND (upper(prop_mode) NOT IN ('INTERNET', 'RPT', 'ECH', 'IRL') OR prop_mode IS NULL)" : ""; const QString serviceSelectCondition = whereTemplate.arg(task.getDBUploadStatusFieldName(), uploadStatuses.join(","), addlCondition); serviceSelectConditions << serviceSelectCondition; serviceStatusColumns << statusColumnTemplate.arg(serviceSelectCondition, QString::number(task.getServiceID())); } const StationProfile &selectedStationProfile = StationProfilesManager::instance()->getProfile(ui->myStationProfileCombo->currentText()); if ( ui->myStationProfileCheckbox->isChecked() ) { QString toolTip = tr("The values below will be used when an input record does not contain the ADIF values") + "
" + selectedStationProfile.toHTMLString(); ui->myStationProfileCombo->setToolTip(toolTip); } if ( serviceSelectConditions.isEmpty() ) { updateQSONumbers(); qCDebug(runtime) << "No service checked"; ui->detailQSOView->setModel(nullptr); return; } QString selectStatement; QStringList whereParts; whereParts << "(" + serviceSelectConditions.join(" OR ") + ")"; // Prepare Upload QSO Query QSqlQuery uploadQSOQuery; if ( ui->myCallsignCheckbox->isChecked() ) { whereParts << QLatin1String("COALESCE(NULLIF(TRIM(station_callsign), ''), TRIM(operator)) = :station_callsign"); if ( !ui->myGridCombo->currentText().isEmpty() && ui->myGridCombo->currentIndex() > 0 ) whereParts << QLatin1String("my_gridsquare = :my_gridsquare"); selectStatement = QString("SELECT *, %1 FROM contacts INNER JOIN contacts_autovalue ON contacts.id = contacts_autovalue.contactid " "WHERE %2 ORDER BY start_time").arg(serviceStatusColumns.join(", "), whereParts.join(" AND ")); if ( !uploadQSOQuery.prepare(selectStatement) ) { qCDebug(runtime) << "Cannot prepare SQL 1" << selectStatement; return; } uploadQSOQuery.bindValue(":station_callsign", ui->myCallsignCombo->currentText().trimmed()); uploadQSOQuery.bindValue(":my_gridsquare", ui->myGridCombo->currentText().trimmed()); } else { whereParts << QLatin1String("station_profiles.profile_name = :profile_name"); selectStatement = QString("SELECT contacts.*, contacts_autovalue.*, %1 " "FROM contacts inner join station_profiles on %2 INNER JOIN contacts_autovalue ON contacts.id = contacts_autovalue.contactid " "WHERE %3 ORDER BY start_time").arg(serviceStatusColumns.join(", "), selectedStationProfile.getContactInnerJoin(), whereParts.join(" AND ") ); if ( !uploadQSOQuery.prepare(selectStatement) ) { qCDebug(runtime) << "Cannot prepare SQL 2" << selectStatement; return; } uploadQSOQuery.bindValue(":profile_name", selectedStationProfile.profileName); } qCDebug(runtime) << selectStatement << uploadQSOQuery.boundValues(); if ( !uploadQSOQuery.exec() ) { qCWarning(runtime) << "cannot execute statement" << uploadQSOQuery.lastError().text(); return; } QList rowItems; QStringList serviceNames; // update QSO Detail and QSO list while ( uploadQSOQuery.next() ) { rowItems.clear(); serviceNames.clear(); auto rec = QSharedPointer::create(uploadQSOQuery.record()); allSelectedQSOs.insert(rec->value("id").toULongLong(), rec); bool eqslUploadStatus = rec->value("status_" + QString::number(ServiceID::EQSLID)).toBool(); bool lotwUploadStatus = rec->value("status_" + QString::number(ServiceID::LOTWID)).toBool(); for ( auto it = onlineServices.begin(); it != onlineServices.end(); ++it ) { UploadTask &task = it.value(); if ( !task.isChecked() ) continue; ServiceID taskID = task.getServiceID(); bool forUploadMarked = false; // set forUploadMarked according to service dependency. switch (taskID) { case EQSLID: forUploadMarked = eqslUploadStatus; break; case LOTWID: forUploadMarked = lotwUploadStatus; break; case CLUBLOGID: forUploadMarked = lotwUploadStatus || rec->value("status_" + QString::number(taskID)).toBool(); break; // depends on the LoTW Receive field case HRDLOGID: forUploadMarked = eqslUploadStatus || lotwUploadStatus || rec->value("status_" + QString::number(taskID)).toBool(); break; //depends on EQSL and LoTW fields case QRZCOMID: forUploadMarked = (! serviceNames.empty()) || rec->value("status_" + QString::number(taskID)).toBool(); break; // all fields are sent case WAVELOGID: forUploadMarked = rec->value("status_" + QString::number(taskID)).toBool(); break; default: forUploadMarked = false; } if ( forUploadMarked ) { serviceNames << task.getServiceName(); task.addQSO(rec); } } rowItems << new QStandardItem(rec->value("start_time").toDateTime() .toTimeZone(QTimeZone::utc()) .toString(locale.formatDateTimeShortWithYYYY())); rowItems << new QStandardItem(rec->value("callsign").toString()); rowItems << new QStandardItem(rec->value("mode").toString()); rowItems << new QStandardItem(serviceNames.join(", ")); detailQSOsModel->appendRow(rowItems); } ui->detailQSOView->setModel(detailQSOsModel); ui->detailQSOView->resizeColumnsToContents(); updateQSONumbers(); updateLotwLocationWarning(); qCDebug(runtime) << "finiched"; } void UploadQSODialog::handleCallsignChange(const QString &myCallsign) { FCT_IDENTIFICATION; ui->myGridLabel->blockSignals(true); ui->myGridCombo->setModel(new SqlListModel("SELECT DISTINCT UPPER(my_gridsquare) " "FROM contacts " "WHERE COALESCE(NULLIF(TRIM(station_callsign), ''), TRIM(operator)) ='" + QString(myCallsign).replace("'", "''") + "' ORDER BY my_gridsquare", tr("Any"), ui->myGridCombo)); ui->myGridCombo->setCurrentIndex(0); executeQuery(); ui->myGridLabel->blockSignals(false); updateLotwLocationWarning(); } void UploadQSODialog::updateLotwLocationWarning() { FCT_IDENTIFICATION; // selected "Unspecified" – nothing to validate if ( ui->lotwLocationCombo->currentIndex() <= 0 ) { ui->lotwLocationWarningLabel->setVisible(false); return; } QString filterCallsign; QString filterGrid; bool checkGrid = false; if ( ui->myCallsignCheckbox->isChecked() ) { filterCallsign = ui->myCallsignCombo->currentText().toUpper().trimmed(); // myGridCombo index 0 means "Any" – skip the grid check in that case checkGrid = ui->myGridCombo->currentIndex() > 0; if ( checkGrid ) filterGrid = ui->myGridCombo->currentText().toUpper().trimmed(); } else { // Station Profile mode const StationProfile &profile = StationProfilesManager::instance()->getProfile(ui->myStationProfileCombo->currentText()); filterCallsign = profile.callsign.toUpper().trimmed(); checkGrid = !profile.locator.trimmed().isEmpty(); if ( checkGrid ) filterGrid = profile.locator.toUpper().trimmed(); } const QString selectedName = ui->lotwLocationCombo->currentData().toString(); for ( const TQSLStationLocation &loc : static_cast>(tqslLocations) ) { if ( loc.name != selectedName ) continue; const bool callsignMismatch = !loc.callsign.isEmpty() && !filterCallsign.isEmpty() && loc.callsign.toUpper() != filterCallsign; // Compare only the 4-character grid square prefix const bool gridMismatch = checkGrid && !loc.grid.isEmpty() && loc.grid.toUpper().left(4) != filterGrid.left(4); if ( callsignMismatch && gridMismatch ) { ui->lotwLocationWarningLabel->setText( QString("⚠ ") + tr("Location callsign (%1) and grid (%2) do not match selected filters") .arg(loc.callsign, loc.grid.toUpper())); } else if ( callsignMismatch ) { ui->lotwLocationWarningLabel->setText( QString("⚠ ") + tr("Location callsign (%1) does not match selected callsign (%2)") .arg(loc.callsign, filterCallsign)); } else if ( gridMismatch ) { ui->lotwLocationWarningLabel->setText( QString("⚠ ") + tr("Location grid (%1) does not match selected grid (%2)") .arg(loc.grid.toUpper(), filterGrid)); } ui->lotwLocationWarningLabel->setVisible(callsignMismatch || gridMismatch); return; } ui->lotwLocationWarningLabel->setVisible(false); } void UploadQSODialog::updateWavelogStationLabel() { FCT_IDENTIFICATION; const CloudlogUploader::StationProfile &currProfile = availableWavelogStationIDs.value(ui->wavelogStationIDSpin->value()); ui->wavelogStationIDProfileLabel->setText(( currProfile.station_id == ui->wavelogStationIDSpin->value()) ? currProfile.station_profile_name + " [" + currProfile.station_callsign + " / " + currProfile.station_gridsquare + "]" : tr("Unknown")); } ================================================ FILE: ui/UploadQSODialog.h ================================================ #ifndef UPLOADQSODIALOG_H #define UPLOADQSODIALOG_H #include #include #include #include #include #include #include #include "core/LogLocale.h" #include "service/GenericQSOUploader.h" #include "service/cloudlog/Cloudlog.h" #include "service/lotw/Lotw.h" namespace Ui { class UploadQSODialog; } class UploadQSODialog : public QDialog { Q_OBJECT public: explicit UploadQSODialog(QWidget *parent = nullptr); ~UploadQSODialog(); private slots: void showQSODetails(); void setEQSLSettingVisible(bool visible); void setClublogSettingVisible(bool visible); void setLotwSettingVisible(bool visible); void setWavelogSettingVisible(bool visible); void startUploadQueue(); void executeQuery(); void handleCallsignChange(const QString &); void updateWavelogStationLabel(); private: // The order is important here, because some services // send fields from other services. enum ServiceID { UNKNOWN = 0, LOTWID = 1, // independent EQSLID = 2, // independent CLUBLOGID = 3, // depends on the LoTW Receive field HRDLOGID = 4, // depends on EQSL and LoTW fields QRZCOMID = 5, // all fields are sent WAVELOGID = 6, // all fiedls are sent }; class UploadTask { public: UploadTask() : serviceID(UNKNOWN), uploader(nullptr), controElement(nullptr), qsoNumberLabel(nullptr) {}; UploadTask(ServiceID id, const QString &serviceName, GenericQSOUploader *uploader, const QString &dbUploadStatusFieldName, const QString &dbUploadDateFieldName, QCheckBox *enableWidget, QLabel *qsoCountLabel, bool isActivable): serviceID(id), serviceName(serviceName), uploader(uploader), controElement(enableWidget), dbUploadStatusFieldName(dbUploadStatusFieldName), dbUploadDateFieldName(dbUploadDateFieldName), qsoNumberLabel(qsoCountLabel) { enableWidget->setEnabled(isActivable); enableWidget->setToolTip((isActivable) ? "" : tr("Service is not configured properly.

Please, use Settings dialog to configure it.

")); }; ~UploadTask() {}; ServiceID getServiceID() const {return serviceID;}; void addQSO(QSharedPointer record) {qsoRefs.append(record); qsoIds.append(record->value("id").toULongLong());}; void clearEnqueuedQSOs() {qsoRefs.clear(); qsoIds.clear();}; const QList getQSOList() const { QList retList; const QList> localRefs = qsoRefs; for ( const QWeakPointer& weak : localRefs ) { QSharedPointer shared = weak.toStrongRef(); if (shared) retList.append(*shared); } return retList; }; const QList& getQSOIDs() const { return qsoIds; }; bool isChecked() {return (controElement) ? controElement->isChecked() : false;}; const QString& getDBUploadStatusFieldName() const {return dbUploadStatusFieldName;}; const QString& getDBUploadDateFieldName() const {return dbUploadDateFieldName;}; const QString& getServiceName() const {return serviceName;}; GenericQSOUploader *getUploader() const {return uploader;}; void updateQSONumberLabel() {qsoNumberLabel->setText((isChecked()) ? "(" + QString::number(qsoRefs.size()) + ")" : "");}; bool isQSOListEmpty() const {return qsoRefs.isEmpty(); }; void updateAllDBFieldValue(const QString &column1, const QVariant &value1, const QString &column2, const QVariant &value2) const { const QList> localRefs = qsoRefs; for ( const QWeakPointer& weak : localRefs ) { QSharedPointer shared = weak.toStrongRef(); if ( shared ) { shared->setValue(column1, value1); shared->setValue(column2, value2); } } }; private: ServiceID serviceID; QString serviceName; GenericQSOUploader *uploader; QList> qsoRefs; QList qsoIds; QCheckBox *controElement; QString dbUploadStatusFieldName; QString dbUploadDateFieldName; QLabel *qsoNumberLabel; }; QMap onlineServices; QHash> allSelectedQSOs; Ui::UploadQSODialog *ui; LogLocale locale; QList uploadTaskQueue; QStandardItemModel *detailQSOsModel; UploadTask currentTask; QMap availableWavelogStationIDs; QList tqslLocations; bool executeQueryEnabled; void updateLotwLocationWarning(); void setQSODetailVisible(bool visible); void loadDialogState(); void saveDialogState(); void processNextUploader(); void uploadFinished(); void updateQSONumbers(); void getWavelogStationID(); const uint WAVELOG_MAX_STATIONID = 99999; }; #endif // UPLOADQSODIALOG_H ================================================ FILE: ui/UploadQSODialog.ui ================================================ UploadQSODialog 0 0 597 819 0 0 Upload QSOs 0 10 0 10 eQSL uploadButtonGroup 10 LoTW uploadButtonGroup 0 0 5 QRZ.com uploadButtonGroup 5 Clublog uploadButtonGroup 0 0 5 HRDLog uploadButtonGroup 0 0 Wavelog uploadButtonGroup Filters Station Profile filterButtonGroup false My Callsign true filterButtonGroup 0 0 Gridsquare Include QSOs Status Under normal circumstances this status means <b>"do not send"</b>.<br/>However, it may sometimes be wanted to ignore this setting when sending a QSL. No Under normal circumstances this status means <b>"Ignore/Invalid"</b>.<br/>However, it may sometimes be wanted to ignore this setting when sending a QSL. Ignore Qt::Horizontal 40 20 true eQSL false false 2 4 4 4 4 None true QSLs Message Comment Qt::Horizontal QSizePolicy::MinimumExpanding 1 1 QSL Message Field QTH Profile Clublog false 4 2 6 4 4 4 0 0 This option deletes all QSOs in the Clublog<br>and based on filter, it uploads all QSOs regardless of their status. Clear Clublog and reupload QSOs LoTW / TQSL 4 4 4 4 TQSL Station Location false color: orange; true Wavelog Station Profile ID 0 0 1 99999 0 0 Qt::Horizontal QSizePolicy::Expanding 60 20 Reupload All true Qt::Horizontal Qt::NoFocus QAbstractItemView::NoEditTriggers false false true QAbstractItemView::NoSelection QAbstractItemView::ScrollPerPixel QAbstractItemView::ScrollPerPixel false false true true false true 0 Show QSOs... Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok eqslCheckbox qrzCheckbox hrdlogCheckbox lotwCheckbox clublogCheckbox wavelogCheckbox myStationProfileCheckbox myStationProfileCombo myCallsignCheckbox myCallsignCombo myGridCombo includeStatusNoCheckbox includeIgnoreCheckbox eqslQTHProfileEdit eqslQSLNone eqslQSLMessage eqslQSLComment clublogClearCheckbox lotwLocationCombo wavelogStationIDSpin wavelogReuploadCheckbox showQSOButton buttonBox accepted() UploadQSODialog startUploadQueue() 366 512 157 274 buttonBox rejected() UploadQSODialog reject() 434 512 286 274 showQSOButton clicked() UploadQSODialog showQSODetails() 61 512 285 244 uploadButtonGroup buttonToggled(QAbstractButton*,bool) UploadQSODialog executeQuery() -1 -1 285 244 eqslCheckbox toggled(bool) UploadQSODialog setEQSLSettingVisible(bool) 224 20 285 259 clublogCheckbox toggled(bool) UploadQSODialog setClublogSettingVisible(bool) 360 44 285 259 myCallsignCombo currentTextChanged(QString) UploadQSODialog handleCallsignChange(QString) 361 81 285 267 myGridCombo currentTextChanged(QString) UploadQSODialog executeQuery() 361 113 285 267 includeStatusNoCheckbox stateChanged(int) UploadQSODialog executeQuery() 184 145 285 267 includeIgnoreCheckbox stateChanged(int) UploadQSODialog executeQuery() 249 145 285 267 clublogClearCheckbox stateChanged(int) UploadQSODialog executeQuery() 408 269 285 267 myCallsignCheckbox toggled(bool) myCallsignCombo setEnabled(bool) 62 83 237 85 myCallsignCheckbox toggled(bool) myGridLabel setEnabled(bool) 62 83 359 85 myCallsignCheckbox toggled(bool) myGridCombo setEnabled(bool) 62 83 480 85 myStationProfileCheckbox toggled(bool) myStationProfileCombo setEnabled(bool) 74 117 358 118 filterButtonGroup buttonToggled(QAbstractButton*,bool) UploadQSODialog executeQuery() -1 -1 283 283 myStationProfileCombo currentTextChanged(QString) UploadQSODialog executeQuery() 373 125 298 310 lotwCheckbox toggled(bool) UploadQSODialog setLotwSettingVisible(bool) 224 44 285 259 wavelogCheckbox toggled(bool) UploadQSODialog setWavelogSettingVisible(bool) 442 49 298 310 wavelogReuploadCheckbox stateChanged(int) UploadQSODialog executeQuery() 453 423 298 310 wavelogStationIDSpin valueChanged(int) UploadQSODialog updateWavelogStationLabel() 187 424 298 350 showQSODetails() setEQSLSettingVisible(bool) setClublogSettingVisible(bool) executeQuery() handleCallsignChange(QString) startUploadQueue() setWavelogSettingVisible(bool) setLotwSettingVisible(bool) updateWavelogStationLabel() false ================================================ FILE: ui/WebEnginePage.cpp ================================================ #include #include #include #include "WebEnginePage.h" #include "core/debug.h" MODULE_IDENTIFICATION("qlog.ui.webenginepage"); WebEnginePage::WebEnginePage(QObject *parent) : QWebEnginePage{parent} { FCT_IDENTIFICATION; QString userAgent = QString("%1/%2 (+https://github.com/foldynl/QLog)") .arg(QCoreApplication::applicationName(), VERSION); profile()->setHttpUserAgent(userAgent); } bool WebEnginePage::acceptNavigationRequest(const QUrl &url, QWebEnginePage::NavigationType type, bool isMainFrame) { FCT_IDENTIFICATION; qCDebug(function_parameters) << url << type << isMainFrame; if ( isMainFrame && type == QWebEnginePage::NavigationTypeLinkClicked ) { QDesktopServices::openUrl(url); return false; } return QWebEnginePage::acceptNavigationRequest(url, type, isMainFrame); } void WebEnginePage::javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level, const QString &message, int lineNumber, const QString &sourceID) { FCT_IDENTIFICATION; Q_UNUSED(lineNumber); Q_UNUSED(sourceID); qCDebug(runtime)<<"level: " << level <<"; message: "< class WebEnginePage : public QWebEnginePage { public: explicit WebEnginePage(QObject *parent = nullptr); protected: bool acceptNavigationRequest(const QUrl &url, QWebEnginePage::NavigationType type, bool isMainFrame) override; void javaScriptConsoleMessage(QWebEnginePage::JavaScriptConsoleMessageLevel level, const QString &message, int lineNumber, const QString &sourceID) override; }; #endif // QLOG_UI_WEBENGINEPAGE_H ================================================ FILE: ui/WsjtxFilterDialog.cpp ================================================ #include "WsjtxFilterDialog.h" #include "ui_WsjtxFilterDialog.h" #include "core/debug.h" #include "data/Dxcc.h" #include "core/MembershipQE.h" #include "data/Gridsquare.h" #include "core/LogParam.h" MODULE_IDENTIFICATION("qlog.ui.wsjtxfilterdialog"); WsjtxFilterDialog::WsjtxFilterDialog(QWidget *parent) : QDialog(parent), ui(new Ui::WsjtxFilterDialog) { ui->setupUi(this); QString unit; Gridsquare::distance2localeUnitDistance(0, unit, locale); ui->distanceSpinBox->setSuffix(" " + unit); /*********************/ /* Status Checkboxes */ /*********************/ uint statusSetting = LogParam::getWsjtxFilterDxccStatus(); ui->newEntityCheckBox->setChecked(statusSetting & DxccStatus::NewEntity); ui->newBandCheckBox->setChecked(statusSetting & DxccStatus::NewBand); ui->newModeCheckBox->setChecked(statusSetting & DxccStatus::NewMode); ui->newSlotCheckBox->setChecked(statusSetting & DxccStatus::NewSlot); ui->workedCheckBox->setChecked(statusSetting & DxccStatus::Worked); ui->confirmedCheckBox->setChecked(statusSetting & DxccStatus::Confirmed); /************************/ /* Continent Checkboxes */ /************************/ const QString &contregexp = LogParam::getWsjtxFilterContRE(); ui->afCheckBox->setChecked(contregexp.contains("|AF")); ui->anCheckBox->setChecked(contregexp.contains("|AN")); ui->asCheckBox->setChecked(contregexp.contains("|AS")); ui->euCheckBox->setChecked(contregexp.contains("|EU")); ui->naCheckBox->setChecked(contregexp.contains("|NA")); ui->ocCheckBox->setChecked(contregexp.contains("|OC")); ui->saCheckBox->setChecked(contregexp.contains("|SA")); /*************/ /* Distance */ /*************/ ui->distanceSpinBox->setValue(LogParam::getWsjtxFilterDistance()); /********/ /* SNR */ /********/ ui->snrSpinBox->setValue(LogParam::getWsjtxFilterSNR()); /**********/ /* MEMBER */ /**********/ generateMembershipCheckboxes(); } void WsjtxFilterDialog::accept() { FCT_IDENTIFICATION; /*********************/ /* Status Checkboxes */ /*********************/ uint status = 0; if ( ui->newEntityCheckBox->isChecked() ) status |= DxccStatus::NewEntity; if ( ui->newBandCheckBox->isChecked() ) status |= DxccStatus::NewBand; if ( ui->newModeCheckBox->isChecked() ) status |= DxccStatus::NewMode; if ( ui->newSlotCheckBox->isChecked() ) status |= DxccStatus::NewSlot; if ( ui->workedCheckBox->isChecked() ) status |= DxccStatus::Worked; if ( ui->confirmedCheckBox->isChecked() ) status |= DxccStatus::Confirmed; LogParam::setWsjtxFilterDxccStatus(status); /************************/ /* Continent Checkboxes */ /************************/ QString contregexp = "NOTHING"; if ( ui->afCheckBox->isChecked() ) contregexp.append("|AF"); if ( ui->anCheckBox->isChecked() ) contregexp.append("|AN"); if ( ui->asCheckBox->isChecked() ) contregexp.append("|AS"); if ( ui->euCheckBox->isChecked() ) contregexp.append("|EU"); if ( ui->naCheckBox->isChecked() ) contregexp.append("|NA"); if ( ui->ocCheckBox->isChecked() ) contregexp.append("|OC"); if ( ui->saCheckBox->isChecked() ) contregexp.append("|SA"); LogParam::setWsjtxFilterContRE(contregexp); /*************/ /* Distance */ /*************/ LogParam::setWsjtxFilterDistance(ui->distanceSpinBox->value()); /********/ /* SNR */ /********/ LogParam::setWsjtxFilterSNR(ui->snrSpinBox->value()); /**********/ /* MEMBER */ /**********/ QStringList memberList; if ( ui->memberGroupBox->isChecked() ) { memberList.append("DUMMYCLUB"); for ( QCheckBox* item: static_cast&>(memberListCheckBoxes) ) if ( item->isChecked() ) memberList.append(item->text()); } LogParam::setWsjtxMemberlists(memberList); done(QDialog::Accepted); } void WsjtxFilterDialog::generateMembershipCheckboxes() { FCT_IDENTIFICATION; const QStringList ¤tFilter = LogParam::getWsjtxMemberlists(); const QStringList &enabledLists = MembershipQE::getEnabledClubLists(); for ( int i = 0 ; i < enabledLists.size(); i++) { QCheckBox *columnCheckbox = new QCheckBox(this); const QString &shortDesc = enabledLists.at(i); columnCheckbox->setText(shortDesc); columnCheckbox->setChecked(currentFilter.contains(shortDesc)); memberListCheckBoxes.append(columnCheckbox); } if ( memberListCheckBoxes.size() == 0 ) { ui->dxMemberGrid->addWidget(new QLabel(tr("No Club List is enabled"))); } else { int elementIndex = 0; for ( QCheckBox* item: static_cast&>(memberListCheckBoxes) ) { ui->dxMemberGrid->addWidget(item, elementIndex / 3, elementIndex % 3); elementIndex++; } } ui->memberGroupBox->setChecked((currentFilter.size() != 0)); } WsjtxFilterDialog::~WsjtxFilterDialog() { delete ui; } ================================================ FILE: ui/WsjtxFilterDialog.h ================================================ #ifndef QLOG_UI_WSJTXFILTERDIALOG_H #define QLOG_UI_WSJTXFILTERDIALOG_H #include #include #include #include "core/LogLocale.h" namespace Ui { class WsjtxFilterDialog; } class WsjtxFilterDialog : public QDialog { Q_OBJECT public: explicit WsjtxFilterDialog(QWidget *parent = nullptr); ~WsjtxFilterDialog(); void accept() override; private: Ui::WsjtxFilterDialog *ui; QList memberListCheckBoxes; QSet dxMemberFilter; LogLocale locale; void generateMembershipCheckboxes(); }; #endif // QLOG_UI_WSJTXFILTERDIALOG_H ================================================ FILE: ui/WsjtxFilterDialog.ui ================================================ WsjtxFilterDialog 0 0 373 434 WSJTX Filters 0 General Filters Log Status New Band New Mode New Entity New Slot Worked Confirmed Continent North America Europe South America Africa Antarctica Asia Oceania Distance more than 40075 0 SNR better than dB -41 -40 Qt::Vertical 20 40 Extended Filters Member true true Qt::Vertical 20 40 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() WsjtxFilterDialog accept() 248 254 157 274 buttonBox rejected() WsjtxFilterDialog reject() 316 260 286 274 ================================================ FILE: ui/WsjtxWidget.cpp ================================================ #include #include #include #include "WsjtxWidget.h" #include "ui_WsjtxWidget.h" #include "data/Data.h" #include "core/debug.h" #include "rig/Rig.h" #include "rig/macros.h" #include "data/StationProfile.h" #include "ui/ColumnSettingDialog.h" #include "ui/WsjtxFilterDialog.h" #include "data/Gridsquare.h" #include "ui/component/StyleItemDelegate.h" #include "data/BandPlan.h" #include "core/LogParam.h" #include "core/PotaQE.h" #include "core/WsjtxUDPReceiver.h" MODULE_IDENTIFICATION("qlog.ui.wsjtxswidget"); WsjtxWidget::WsjtxWidget(QWidget *parent) : QWidget(parent), ui(new Ui::WsjtxWidget), cqRE("(^(?:(?P(?:CQ|DE|QRZ)(?:\\s?DX|\\s(?:[A-Z]{1,4}|\\d{3}))|[A-Z0-9\\/]+|\\.{3})\\s)(?:(?P[A-Z0-9\\/]+)(?:\\s(?P[-+A-Z0-9]+)(?:\\s(?P(?:OOO|(?!RR73)[A-R]{2}[0-9]{2})))?)?)?)") { FCT_IDENTIFICATION; ui->setupUi(this); wsjtxTableModel = new WsjtxTableModel(this); proxyModel = new QSortFilterProxyModel(this); proxyModel->setSourceModel(wsjtxTableModel); proxyModel->setSortRole(Qt::UserRole); ui->tableView->setModel(proxyModel); ui->tableView->setSortingEnabled(true); ui->tableView->sortByColumn(WsjtxTableModel::COLUMN_LAST_ACTIVITY, Qt::DescendingOrder); ui->tableView->setItemDelegateForColumn(WsjtxTableModel::COLUMN_DISTANCE, new DistanceFormatDelegate(1, 0.1, ui->tableView)); ui->tableView->horizontalHeader()->setSectionsMovable(true); ui->tableView->addAction(ui->actionFilter); ui->tableView->addAction(ui->actionDisplayedColumns); restoreTableHeaderState(); reloadSetting(); } void WsjtxWidget::decodeReceived(WsjtxDecode decode) { FCT_IDENTIFICATION; qCDebug(function_parameters)<getCurProfile1(); if ( decode.message.startsWith("CQ") ) { QRegularExpressionMatch match = cqRE.match((decode.message)); if ( match.hasMatch() ) { WsjtxEntry entry; entry.dateTime = QDateTime::currentDateTime().toTimeZone(QTimeZone::utc()); entry.decode = decode; entry.callsign = match.captured(3); entry.grid = match.captured(4); entry.dxcc = Data::instance()->lookupDxcc(entry.callsign); entry.status = Data::instance()->dxccStatus(entry.dxcc.dxcc, currBand, BandPlan::MODE_GROUP_STRING_DIGITAL); entry.receivedTime = QDateTime::currentDateTimeUtc(); entry.freq = currFreq; entry.band = currBand; entry.decodedMode = status.mode; entry.bandPlanMode = ( status.mode == "FT8" ) ? BandPlan::BAND_MODE_FT8 : ( status.mode == "FT4" ) ? BandPlan::BAND_MODE_FT4 : ( status.mode == "FT2" ) ? BandPlan::BAND_MODE_FT2 : BandPlan::BAND_MODE_DIGITAL; entry.modeGroupString = BandPlan::bandMode2BandModeGroupString(entry.bandPlanMode); entry.spotter = profile.callsign.toUpper(); entry.comment = decode.message; entry.dxcc_spotter = Data::instance()->lookupDxcc(entry.spotter); // fix dxcc_spotter based on station prifile setting - cont is not calculated entry.dxcc_spotter.country = profile.country; entry.dxcc_spotter.cqz = profile.cqz; entry.dxcc_spotter.ituz = profile.ituz; entry.dxcc_spotter.dxcc = profile.dxcc; entry.distance = 0.0; entry.potaRef = PotaQE::instance()->findReferenceId(Callsign(entry.callsign), entry.freq).reference; if ( !entry.potaRef.isEmpty() ) { entry.containsPOTA = true; entry.comment.append(" [+] POTA " + entry.potaRef); } entry.callsign_member = MembershipQE::instance()->query(entry.callsign); entry.dupeCount = Data::countDupe(entry.callsign, entry.band, BandPlan::MODE_GROUP_STRING_DIGITAL); if ( !profile.locator.isEmpty() ) { Gridsquare myGrid(profile.locator); double distance; if ( myGrid.distanceTo(Gridsquare(entry.grid), distance) ) { entry.distance = round(distance); } } emit CQSpot(entry); QString unit; qCDebug(runtime) << "Continent" << entry.dxcc.cont.contains(contregexp) << "Continent RegExp" << contregexp << "DX Status" << (entry.status & dxccStatusFilter) << "Distance" << (entry.distance >= distanceFilter) << "Curr Distance" << entry.distance << distanceFilter << "SNR"<< (entry.decode.snr >= snrFilter ) << "Current SNR" << snrFilter << "Member" << ( dxMemberFilter.size() == 0 || (dxMemberFilter.size() && entry.memberList2Set().intersects(dxMemberFilter))); if ( entry.dxcc.cont.contains(contregexp) && ( entry.status & dxccStatusFilter ) && Gridsquare::distance2localeUnitDistance(entry.distance, unit, locale) >= distanceFilter && entry.decode.snr >= snrFilter && ( dxMemberFilter.size() == 0 || (dxMemberFilter.size() && entry.memberList2Set().intersects(dxMemberFilter))) && entry.dupeCount == 0 ) { wsjtxTableModel->addOrReplaceEntry(entry); emit filteredCQSpot(entry); } } } else { const QStringList &decodedElements = decode.message.split(" "); if ( decodedElements.count() > 1 ) { WsjtxEntry entry; entry.callsign = decodedElements.at(1); if ( wsjtxTableModel->callsignExists(entry) ) { entry.dateTime = QDateTime::currentDateTime().toTimeZone(QTimeZone::utc()); entry.dxcc = Data::instance()->lookupDxcc(entry.callsign); entry.status = Data::instance()->dxccStatus(entry.dxcc.dxcc, currBand, BandPlan::MODE_GROUP_STRING_DIGITAL); entry.decode = decode; entry.receivedTime = QDateTime::currentDateTimeUtc(); entry.freq = currFreq; entry.band = currBand; entry.decodedMode = status.mode; entry.comment = decode.message; entry.bandPlanMode = ( status.mode == "FT8" ) ? BandPlan::BAND_MODE_FT8 : ( status.mode == "FT4" ) ? BandPlan::BAND_MODE_FT4 : ( status.mode == "FT2" ) ? BandPlan::BAND_MODE_FT2 : BandPlan::BAND_MODE_DIGITAL; entry.modeGroupString = BandPlan::bandMode2BandModeGroupString(entry.bandPlanMode); entry.spotter = profile.callsign.toUpper(); entry.dxcc_spotter = Data::instance()->lookupDxcc(entry.spotter); // fix dxcc_spotter based on station prifile setting - cont is not calculated entry.dxcc_spotter.country = profile.country; entry.dxcc_spotter.cqz = profile.cqz; entry.dxcc_spotter.ituz = profile.ituz; entry.dxcc_spotter.dxcc = profile.dxcc; entry.distance = 0.0; entry.dupeCount = Data::countDupe(entry.callsign, entry.band, BandPlan::MODE_GROUP_STRING_DIGITAL); // it is not needed to update entry.callsign_clubs because addOrReplaceEntry does not // update it. Only CQ provides the club membeship info wsjtxTableModel->addOrReplaceEntry(entry); emit filteredCQSpot(entry); emit updatedCQSpot(entry); } } } wsjtxTableModel->spotAging(); ui->tableView->repaint(); } void WsjtxWidget::statusReceived(WsjtxStatus newStatus) { FCT_IDENTIFICATION; if ( this->status.dial_freq != newStatus.dial_freq ) { currFreq = Hz2MHz(newStatus.dial_freq); currBand = BandPlan::freq2Band(currFreq).name; clearTable(); } if ( this->status.dx_call != newStatus.dx_call || this->status.dx_grid != newStatus.dx_grid ) { emit callsignSelected(newStatus.dx_call, newStatus.dx_grid, newStatus.id); } if ( this->status.mode != newStatus.mode ) { wsjtxTableModel->setCurrentSpotPeriod(WsjtxUDPReceiver::modePeriodLength(newStatus.mode)); /*currently, only Status has a correct Mode in the message */ clearTable(); } if ( !Rig::instance()->isRigConnected() ) { const QPair& legacy = Data::instance()->legacyMode(newStatus.mode); QString mode = ( !legacy.first.isEmpty() ) ? legacy.first : newStatus.mode.toUpper(); QString submode = ( !legacy.first.isEmpty() ) ? legacy.second : QString(); emit modeChanged(VFO1, newStatus.mode, mode, submode, 0); emit frequencyChanged(VFO1, currFreq, currFreq, currFreq); } status = newStatus; wsjtxTableModel->spotAging(); ui->tableView->repaint(); } void WsjtxWidget::tableViewDoubleClicked(QModelIndex index) { FCT_IDENTIFICATION; const QModelIndex &source_index = proxyModel->mapToSource(index); const WsjtxEntry entry = wsjtxTableModel->getEntry(source_index); //emit callsignSelected(entry.callsign, entry.grid); // it is not needed to send this - Qlog receives the change via WSJTX emit reply(entry); } void WsjtxWidget::callsignClicked(QString callsign) { FCT_IDENTIFICATION; const WsjtxEntry entry = wsjtxTableModel->getEntry(callsign); if ( entry.callsign.isEmpty() ) return; emit callsignSelected(callsign, entry.grid, entry.decode.id); emit reply(entry); } void WsjtxWidget::tableViewClicked(QModelIndex) { FCT_IDENTIFICATION; //const QModelIndex &source_index = proxyModel->mapToSource(index); //lastSelectedCallsign = wsjtxTableModel->getEntry(source_index).callsign; } void WsjtxWidget::updateSpotsStatusWhenQSOAdded(const QSqlRecord &record) { FCT_IDENTIFICATION; wsjtxTableModel->removeSpot(record.value("callsign").toString()); } void WsjtxWidget::displayedColumns() { FCT_IDENTIFICATION; ColumnSettingSimpleDialog dialog(ui->tableView); dialog.exec(); saveTableHeaderState(); } void WsjtxWidget::actionFilter() { FCT_IDENTIFICATION; WsjtxFilterDialog dialog; if (dialog.exec() == QDialog::Accepted) { reloadSetting(); clearTable(); } } uint WsjtxWidget::dxccStatusFilterValue() const { FCT_IDENTIFICATION; return LogParam::getWsjtxFilterDxccStatus(); } QString WsjtxWidget::contFilterRegExp() const { FCT_IDENTIFICATION; return LogParam::getWsjtxFilterContRE(); } int WsjtxWidget::getDistanceFilterValue() const { FCT_IDENTIFICATION; return LogParam::getWsjtxFilterDistance(); } int WsjtxWidget::getSNRFilterValue() const { FCT_IDENTIFICATION; return LogParam::getWsjtxFilterSNR(); } QStringList WsjtxWidget::dxMemberList() const { FCT_IDENTIFICATION; return LogParam::getWsjtxMemberlists(); } void WsjtxWidget::reloadSetting() { FCT_IDENTIFICATION; contregexp.setPatternOptions(QRegularExpression::CaseInsensitiveOption); contregexp.setPattern(contFilterRegExp()); dxccStatusFilter = dxccStatusFilterValue(); distanceFilter = getDistanceFilterValue(); snrFilter = getSNRFilterValue(); QStringList tmp = dxMemberList(); #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) dxMemberFilter = QSet(tmp.begin(), tmp.end()); #else /* Due to ubuntu 20.04 where qt5.12 is present */ dxMemberFilter = QSet(QSet::fromList(tmp)); #endif ui->filteredLabel->setVisible(isFilterEnabled()); } void WsjtxWidget::clearTable() { FCT_IDENTIFICATION; wsjtxTableModel->clear(); emit spotsCleared(); } void WsjtxWidget::saveTableHeaderState() { FCT_IDENTIFICATION; LogParam::setWsjtxWidgetState(ui->tableView->horizontalHeader()->saveState()); } void WsjtxWidget::restoreTableHeaderState() { FCT_IDENTIFICATION; const QByteArray &state = LogParam::getWsjtxWidgetState(); if (!state.isEmpty()) { ui->tableView->horizontalHeader()->restoreState(state); } } bool WsjtxWidget::isFilterEnabled() { FCT_IDENTIFICATION; return dxccStatusFilter != (DxccStatus::NewEntity | DxccStatus::NewBand | DxccStatus::NewMode | DxccStatus::NewSlot | DxccStatus::Worked | DxccStatus::Confirmed) || contregexp.pattern().count("|") != 7 || distanceFilter > 0 || snrFilter > -41 || !dxMemberFilter.isEmpty(); } WsjtxWidget::~WsjtxWidget() { FCT_IDENTIFICATION; delete ui; } void WsjtxWidget::finalizeBeforeAppExit() { FCT_IDENTIFICATION; saveTableHeaderState(); } ================================================ FILE: ui/WsjtxWidget.h ================================================ #ifndef QLOG_UI_WSJTXWIDGET_H #define QLOG_UI_WSJTXWIDGET_H #include #include #include #include "data/WsjtxEntry.h" #include "models/WsjtxTableModel.h" #include "rig/Rig.h" #include "component/ShutdownAwareWidget.h" #include "core/LogLocale.h" #include "data/WsjtxStatus.h" namespace Ui { class WsjtxWidget; } class WsjtxWidget : public QWidget, public ShutdownAwareWidget { Q_OBJECT public: explicit WsjtxWidget(QWidget *parent = nullptr); ~WsjtxWidget(); virtual void finalizeBeforeAppExit() override; public slots: void decodeReceived(WsjtxDecode); void statusReceived(WsjtxStatus); void tableViewDoubleClicked(QModelIndex); void callsignClicked(QString); void tableViewClicked(QModelIndex); void updateSpotsStatusWhenQSOAdded(const QSqlRecord &record); private slots: void displayedColumns(); void actionFilter(); signals: void callsignSelected(QString callsign, QString grid, QString id); void reply(WsjtxEntry); void CQSpot(WsjtxEntry); void filteredCQSpot(WsjtxEntry); void updatedCQSpot(WsjtxEntry); void spotsCleared(); void frequencyChanged(VFOID, double, double, double); void modeChanged(VFOID, QString, QString, QString, qint32); private: uint dxccStatusFilterValue() const; QString contFilterRegExp() const; int getDistanceFilterValue() const; int getSNRFilterValue() const; QStringList dxMemberList() const; void reloadSetting(); void clearTable(); WsjtxTableModel* wsjtxTableModel; WsjtxStatus status; QString currBand; double currFreq; Ui::WsjtxWidget *ui; QSortFilterProxyModel *proxyModel; QRegularExpression contregexp; QRegularExpression cqRE; int distanceFilter; int snrFilter; uint dxccStatusFilter; QSet dxMemberFilter; LogLocale locale; void saveTableHeaderState(); void restoreTableHeaderState(); bool isFilterEnabled(); }; #endif // QLOG_UI_WSJTXWIDGET_H ================================================ FILE: ui/WsjtxWidget.ui ================================================ WsjtxWidget 0 0 400 300 Form 0 0 0 0 0 0 color: red Filtered Qt::AlignCenter Qt::NoTextInteraction Qt::ClickFocus Qt::ActionsContextMenu QAbstractScrollArea::AdjustToContents QAbstractItemView::NoEditTriggers false true QAbstractItemView::SingleSelection QAbstractItemView::SelectRows QAbstractItemView::ScrollPerPixel QAbstractItemView::ScrollPerPixel true 24 100 true true true true 20 20 false Column Visibility... Which columns should be displayed :/icons/filter_list-24px.svg:/icons/filter_list-24px.svg Filter... Filter Spots tableView doubleClicked(QModelIndex) WsjtxWidget tableViewDoubleClicked(QModelIndex) 199 181 199 149 tableView clicked(QModelIndex) WsjtxWidget tableViewClicked(QModelIndex) 199 162 199 149 actionDisplayedColumns triggered() WsjtxWidget displayedColumns() -1 -1 199 149 actionFilter triggered() WsjtxWidget actionFilter() -1 -1 199 149 tableViewDoubleClicked(QModelIndex) tableViewClicked(QModelIndex) displayedColumns() actionFilter() ================================================ FILE: ui/component/BaseDoubleSpinBox.cpp ================================================ #include #include "BaseDoubleSpinBox.h" BaseDoubleSpinBox::BaseDoubleSpinBox(QWidget *parent) : QDoubleSpinBox(parent) { setLocale(QLocale::C); } void BaseDoubleSpinBox::keyPressEvent(QKeyEvent *event) { if ( event->key() == ',' ) return; // supress QDoubleSpinBox::keyPressEvent(event); } ================================================ FILE: ui/component/BaseDoubleSpinBox.h ================================================ #ifndef BASEDOUBLESPINBOX_H #define BASEDOUBLESPINBOX_H #include class BaseDoubleSpinBox : public QDoubleSpinBox { public: BaseDoubleSpinBox(QWidget *parent = nullptr); virtual ~BaseDoubleSpinBox(){}; protected: virtual void keyPressEvent(QKeyEvent *event) override; }; #endif // BASEDOUBLESPINBOX_H ================================================ FILE: ui/component/ButtonStyle.h ================================================ /* * This is nearly complete Material design Switch widget implementation in qtwidgets module. * More info: https://material.io/design/components/selection-controls.html#switches * Copyright (C) 2018 Iman Ahmadvand * * This is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * It is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef QLOG_UI_COMPONENT_BUTTONSTYLE_H #define QLOG_UI_COMPONENT_BUTTONSTYLE_H #include #include #include #include #include #include Q_DECL_IMPORT void qt_blurImage(QPainter *p, QImage &blurImage, qreal radius, bool quality, bool alphaOnly, int transposed = 0); // src/widgets/effects/qpixmapfilter.cpp namespace Style { #define cyan500 QColor("#00bcd4") #define gray50 QColor("#fafafa") #define gray400 QColor("#bdbdbd") using Type = QEasingCurve::Type; struct Animation { Animation() = default; Animation(Type _easing, int _duration) :easing{ _easing }, duration{ _duration } { } Type easing; int duration; }; struct Switch { Switch() : height{ 24 }, //font{ QFont("Roboto medium", 13) }, indicatorMargin{ QMargins(8, 8, 8, 8) }, thumbOnBrush{ cyan500 }, thumbOnOpacity{ 1 }, trackOnBrush{ cyan500 }, trackOnOpacity{ 0.5 }, thumbOffBrush{ gray50 }, thumbOffOpacity{ 1 }, trackOffBrush{ Qt::black }, trackOffOpacity{ 0.38 }, thumbDisabled{ gray400 }, thumbDisabledOpacity{ 1 }, trackDisabled{ Qt::black }, trackDisabledOpacity{ 0.12 }, textColor{ Qt::black }, disabledTextOpacity{ 0.26 }, thumbBrushAnimation{ Animation(Type::Linear, 150) }, trackBrushAnimation{ Animation(Type::Linear, 150) }, thumbPosAniamtion{ Animation(Type::InOutQuad, 150) } { } int height; QFont font; QMargins indicatorMargin; QColor thumbOnBrush; double thumbOnOpacity; QColor trackOnBrush; double trackOnOpacity; QColor thumbOffBrush; double thumbOffOpacity; QColor trackOffBrush; double trackOffOpacity; QColor thumbDisabled; double thumbDisabledOpacity; QColor trackDisabled; double trackDisabledOpacity; QColor textColor; double disabledTextOpacity; Animation thumbBrushAnimation; Animation trackBrushAnimation; Animation thumbPosAniamtion; }; inline QPixmap drawShadowEllipse(qreal radius, qreal elevation, const QColor& color) { auto px = QPixmap(radius * 2, radius * 2); px.fill(Qt::transparent); { // draw ellipes QPainter p(&px); p.setBrush(color); p.setPen(Qt::NoPen); p.setRenderHint(QPainter::Antialiasing, true); p.drawEllipse(QRectF(0, 0, px.size().width(), px.size().height()).center(), radius - elevation, radius - elevation); } QImage tmp(px.size(), QImage::Format_ARGB32_Premultiplied); tmp.setDevicePixelRatio(px.devicePixelRatioF()); tmp.fill(0); QPainter tmpPainter(&tmp); tmpPainter.setCompositionMode(QPainter::CompositionMode_Source); tmpPainter.drawPixmap(QPointF(), px); tmpPainter.end(); // blur the alpha channel QImage blurred(tmp.size(), QImage::Format_ARGB32_Premultiplied); blurred.setDevicePixelRatio(px.devicePixelRatioF()); blurred.fill(0); { QPainter blurPainter(&blurred); qt_blurImage(&blurPainter, tmp, elevation * 4., true, false); } tmp = blurred; return QPixmap::fromImage(tmp); } } // namespace Style #endif // QLOG_UI_COMPONENT_BUTTONSTYLE_H ================================================ FILE: ui/component/EditLine.cpp ================================================ #include #include #include #include #include #include "EditLine.h" NewContactEditLine::NewContactEditLine(QWidget *parent) : QLineEdit(parent), spaceForbiddenFlag(false) { } void NewContactEditLine::focusInEvent(QFocusEvent *event) { QLineEdit::focusInEvent(event); Qt::FocusReason reason = event->reason(); if ( reason != Qt::ActiveWindowFocusReason && reason != Qt::PopupFocusReason ) { end(false); emit focusIn(); } //Deselect text when focus if ( hasSelectedText() && reason != Qt::PopupFocusReason ) { deselect(); } } void NewContactEditLine::focusOutEvent(QFocusEvent *event) { QLineEdit::focusOutEvent(event); Qt::FocusReason reason = event->reason(); if ( reason != Qt::ActiveWindowFocusReason && reason != Qt::PopupFocusReason ) { home(false); emit focusOut(); } } void NewContactEditLine::keyPressEvent(QKeyEvent *event) { if ( spaceForbiddenFlag && event->key() == Qt::Key_Space ) focusNextChild(); else QLineEdit::keyPressEvent(event); } void NewContactEditLine::setText(const QString &text) { QLineEdit::setText(text); home(false); } void NewContactEditLine::spaceForbidden(bool inSpaceForbidden) { spaceForbiddenFlag = inSpaceForbidden; } NewContactRSTEditLine::NewContactRSTEditLine(QWidget *parent) : NewContactEditLine(parent), focusInSelectionOffset(0) { setInputMask("xxxxxxx;"); } void NewContactRSTEditLine::setSelectionOffset(int offset) { focusInSelectionOffset = offset; } void NewContactRSTEditLine::setMaxLength(int len) { NewContactEditLine::setMaxLength(len); setInputMask(QString(len, 'x') + ';'); } void NewContactRSTEditLine::focusInEvent(QFocusEvent *event) { NewContactEditLine::focusInEvent(event); int position = 0; if ( event->reason() != Qt::PopupFocusReason && !text().isEmpty() && text().length() >= focusInSelectionOffset ) position = focusInSelectionOffset; setCursorPosition(position); } SerialPortEditLine::SerialPortEditLine(QWidget *parent) : QLineEdit(parent) { const QList &ports = QSerialPortInfo::availablePorts(); QStringList portNames; #if defined(Q_OS_WIN) for (const QSerialPortInfo &port : ports) portNames << port.portName(); #elif defined(Q_OS_MACOS) for (const QSerialPortInfo &port : ports) portNames << QString("/dev/%1").arg(port.portName()); #else // In the case of Linux, it is good to use /dev/serial/by-id // because it does not change over time. // obtain /dev/serial/by-id files QDir dir("/dev/serial/by-id"); const QStringList &symlinks = dir.entryList(QDir::System | QDir::Readable | QDir::NoDotAndDotDot); for ( const QSerialPortInfo &port : ports ) { QString dev = QString("/dev/%1").arg(port.portName()); QString niceName = dev; // try to find symlink for ( const QString &entry : symlinks ) { QString fullPath = dir.absoluteFilePath(entry); QFileInfo fi(fullPath); if ( fi.canonicalFilePath() == dev ) { niceName = fullPath; break; } } portNames << niceName; } #endif QCompleter *completer = new QCompleter(portNames, this); QAbstractItemView *popup = completer->popup(); popup->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); popup->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); setCompleter(completer); } void SerialPortEditLine::focusInEvent(QFocusEvent *event) { QLineEdit::focusInEvent(event); #if defined(Q_OS_WIN) completer()->setCompletionPrefix("COM"); completer()->complete(); #else completer()->setCompletionPrefix("/dev/"); completer()->complete(); #endif } ================================================ FILE: ui/component/EditLine.h ================================================ #ifndef QLOG_UI_COMPONENT_EDITLINE_H #define QLOG_UI_COMPONENT_EDITLINE_H #include #include class NewContactEditLine : public QLineEdit { Q_OBJECT public: explicit NewContactEditLine(QWidget *parent = nullptr); void setText(const QString & text); void spaceForbidden(bool); signals: void focusIn(); void focusOut(); protected: void focusInEvent(QFocusEvent* event) override; void focusOutEvent(QFocusEvent* event) override; void keyPressEvent(QKeyEvent *event) override; bool spaceForbiddenFlag; }; class NewContactRSTEditLine : public NewContactEditLine { Q_OBJECT public: explicit NewContactRSTEditLine(QWidget *parent = nullptr); void setSelectionOffset(int offset); void setMaxLength(int len); protected: void focusInEvent(QFocusEvent* event) override; int focusInSelectionOffset; }; class SerialPortEditLine : public QLineEdit { Q_OBJECT public: explicit SerialPortEditLine(QWidget *parent = nullptr); protected: void focusInEvent(QFocusEvent* event) override; }; #endif // QLOG_UI_COMPONENT_EDITLINE_H ================================================ FILE: ui/component/FreqQSpinBox.cpp ================================================ #include "ui/component/FreqQSpinBox.h" #include #include #include "data/BandPlan.h" FreqQSpinBox::FreqQSpinBox(QWidget *parent) : BaseDoubleSpinBox(parent), selectionModeEnabled(true) { loadBands(); debounceTimer.setSingleShot(true); connect(&debounceTimer, &QTimer::timeout, this, &FreqQSpinBox::flushDebounced); connect(this, QOverload::of(&QDoubleSpinBox::valueChanged), this, &FreqQSpinBox::onValueChangedImmediate); } void FreqQSpinBox::setSelectionModeEnabled(bool enabled) { selectionModeEnabled = enabled; if ( !enabled ) if (auto le = lineEdit()) le->deselect(); } void FreqQSpinBox::setDebounceEnabled(bool enabled) { if ( debounceEnabled == enabled ) return; debounceEnabled = enabled; if ( !debounceEnabled ) { if ( debounceTimer.isActive() )debounceTimer.stop(); flushDebounced(); } } void FreqQSpinBox::setDebounceIntervalMs(int ms) { debounceMs = qMax(0, ms); } void FreqQSpinBox::loadBands() { enabledBands = BandPlan::bandsList(false, true); } void FreqQSpinBox::keyPressEvent(QKeyEvent *event) { if ( isReadOnly() ) { BaseDoubleSpinBox::keyPressEvent(event); return; } if ( event->key() == Qt::Key_PageUp ) { increaseByBand(); event->accept(); return; } else if ( event->key() == Qt::Key_PageDown ) { decreaseByBand(); event->accept(); return; } BaseDoubleSpinBox::keyPressEvent(event); } void FreqQSpinBox::wheelEvent(QWheelEvent *event) { if ( isReadOnly() ) { BaseDoubleSpinBox::wheelEvent(event); return; } if ( event->modifiers() & Qt::ControlModifier ) { if ( event->angleDelta().y() > 0 ) increaseByBand(); else decreaseByBand(); event->accept(); return; } BaseDoubleSpinBox::wheelEvent(event); } void FreqQSpinBox::stepBy(int steps) { BaseDoubleSpinBox::stepBy(steps); if ( !selectionModeEnabled ) if (auto le = lineEdit()) le->deselect(); } void FreqQSpinBox::onValueChangedImmediate(double v) { if ( !debounceEnabled ) { emit debouncedValueChanged(v); return; } pendingValue = v; hasPending = true; if ( debounceMs == 0 ) { flushDebounced(); return; } debounceTimer.start(debounceMs); } void FreqQSpinBox::flushDebounced() { if ( !hasPending ) return; hasPending = false; emit debouncedValueChanged(pendingValue); } void FreqQSpinBox::increaseByBand() { if ( enabledBands.size() == 0 ) return; for ( const Band &band : static_cast&>(enabledBands) ) { if ( band.start > value() ) { setValue(band.start); maybeSelectAll(); return; } } } void FreqQSpinBox::decreaseByBand() { if ( enabledBands.size() == 0 ) return; double result = enabledBands.at(0).start; for ( const Band &band : static_cast&>(enabledBands) ) { if ( band.start < value() ) result = band.start; } setValue(result); maybeSelectAll(); } void FreqQSpinBox::maybeSelectAll() { if ( !selectionModeEnabled ) return; selectAll(); } ================================================ FILE: ui/component/FreqQSpinBox.h ================================================ #ifndef QLOG_UI_COMPONENT_FREQQSPINBOX_H #define QLOG_UI_COMPONENT_FREQQSPINBOX_H #include #include "ui/component/BaseDoubleSpinBox.h" class FreqQSpinBox : public BaseDoubleSpinBox { Q_OBJECT public: FreqQSpinBox(QWidget *parent = nullptr); virtual ~FreqQSpinBox() {}; void setSelectionModeEnabled(bool enabled); void setDebounceEnabled(bool enabled); void setDebounceIntervalMs(int ms); public slots: void loadBands(); signals: void debouncedValueChanged(double value); protected: virtual void keyPressEvent(QKeyEvent *event) override; virtual void wheelEvent(QWheelEvent *event) override; virtual void stepBy(int steps) override; private slots: void onValueChangedImmediate(double v); void flushDebounced(); private: void increaseByBand(); void decreaseByBand(); void maybeSelectAll(); QList enabledBands; bool selectionModeEnabled; QTimer debounceTimer; int debounceMs = 150; bool debounceEnabled = false; double pendingValue = 0.0; bool hasPending = false; }; #endif // QLOG_UI_COMPONENT_FREQQSPINBOX_H ================================================ FILE: ui/component/MultiselectCompleter.cpp ================================================ #include #include "ui/component/MultiselectCompleter.h" MultiselectCompleter::MultiselectCompleter(const QStringList& items, QObject* parent) : QCompleter(items, parent) { } QString MultiselectCompleter::pathFromIndex( const QModelIndex& index ) const { QString path = QCompleter::pathFromIndex(index); const QString &text = static_cast(widget())->text(); int pos = text.lastIndexOf(','); if ( pos >= 0 ) path = text.left(pos) + ", " + path; return path; } QStringList MultiselectCompleter::splitPath( const QString& path ) const { int pos = path.lastIndexOf(',') + 1; while ( pos < path.length() && path.at(pos) == QLatin1Char(' ') ) pos++; return QStringList(path.mid(pos)); } ================================================ FILE: ui/component/MultiselectCompleter.h ================================================ #ifndef QLOG_UI_COMPONENT_MULTISELECTCOMPLETER_H #define QLOG_UI_COMPONENT_MULTISELECTCOMPLETER_H #include class MultiselectCompleter : public QCompleter { Q_OBJECT public: explicit MultiselectCompleter(const QStringList &items, QObject *parent = nullptr); ~MultiselectCompleter() {}; public: QString pathFromIndex( const QModelIndex& index ) const; QStringList splitPath( const QString& path ) const; }; #endif // QLOG_UI_COMPONENT_MULTISELECTCOMPLETER_H ================================================ FILE: ui/component/RepeatButton.cpp ================================================ #include #include #include #include #include "RepeatButton.h" RepeatButton::RepeatButton(QWidget *parent) : QPushButton(parent), timer(new QTimer(this)), interval(0), progress(0) { connect(timer, &QTimer::timeout, this, &RepeatButton::onTimeout); connect(&updateTimer, &QTimer::timeout, this, &RepeatButton::updateProgress); QProgressBar bar; filledColor = bar.palette().color(QPalette::Highlight); filledColor.setAlpha(128); } void RepeatButton::stop() { timer->stop(); updateTimer.stop(); progress = 0; update(); } void RepeatButton::handleClick() { lastPressTime = QDateTime::currentDateTime(); if ( !timer->isActive() ) { interval = 0; QPushButton::click(); } else stop(); } void RepeatButton::resetInterval() { lastPressTime = QDateTime(); interval = 0; } void RepeatButton::repeatClick() { if ( timer->isActive() ) return; repeateEventDetected(); QPushButton::click(); } void RepeatButton::repeateEventDetected() { if ( interval == 0 ) { if ( lastPressTime.isNull() ) return; interval = lastPressTime.msecsTo(QDateTime::currentDateTime()); } startRepeating(); } void RepeatButton::startRepeating() { if ( interval > 0 ) { timer->start(interval); updateTimer.start(20); elapsed.start(); } } void RepeatButton::mousePressEvent(QMouseEvent *event) { if ( event->button() == Qt::LeftButton ) { if( QApplication::keyboardModifiers() & Qt::ShiftModifier ) repeateEventDetected(); else { handleClick(); return; } } QPushButton::mousePressEvent(event); } void RepeatButton::paintEvent(QPaintEvent *event) { QPushButton::paintEvent(event); if ( timer->isActive() ) { QPainter p(this); p.setRenderHint(QPainter::Antialiasing); p.setBrush(filledColor); p.setPen(Qt::NoPen); int fillWidth = width() * progress / 100; QRect rect(0, 0, fillWidth, height()); p.drawRect(rect); } } void RepeatButton::onTimeout() { QPushButton::click(); elapsed.restart(); } void RepeatButton::updateProgress() { if ( timer->isActive() ) { int i_progress = elapsed.elapsed() * 100 / interval; progress = qMin(i_progress, 100); update(); } } ================================================ FILE: ui/component/RepeatButton.h ================================================ #ifndef REPEATBUTTON_H #define REPEATBUTTON_H #include #include #include #include class RepeatButton : public QPushButton { Q_OBJECT public: RepeatButton(QWidget *parent = nullptr); public slots: void repeatClick(); void stop(); void handleClick(); void resetInterval(); protected: void mousePressEvent(QMouseEvent *event) override; void paintEvent(QPaintEvent *event) override; private slots: void onTimeout(); void updateProgress(); private: void startRepeating(); void repeateEventDetected(); QTimer *timer; QTimer updateTimer; QDateTime lastPressTime; QElapsedTimer elapsed; int interval = 0; int progress = 0; QColor filledColor; }; #endif // REPEATBUTTON_H ================================================ FILE: ui/component/ShutdownAwareWidget.h ================================================ #ifndef QLOG_UI_COMPONENT_SHUTDOWNAWAREWIDGET_H #define QLOG_UI_COMPONENT_SHUTDOWNAWAREWIDGET_H class ShutdownAwareWidget { public: virtual ~ShutdownAwareWidget() {}; virtual void finalizeBeforeAppExit() = 0; }; #endif // QLOG_UI_COMPONENT_SHUTDOWNAWAREWIDGET_H ================================================ FILE: ui/component/SmartSearchBox.cpp ================================================ #include "SmartSearchBox.h" #include #include #include #include #include #include #include #include #include "models/SqlListModel.h" SmartSearchBox::SmartSearchBox(QWidget *parent) : QWidget(parent), searchField(new QLineEdit(this)), listView(new QListView(this)), popup(new QDialog(this, Qt::Popup)), filterModel(new QSortFilterProxyModel(this)), selectedColumn(0), maxRowsInList(10), highlightWhenEnable(false) { filterModel->setSourceModel(nullptr); filterModel->setFilterCaseSensitivity(Qt::CaseInsensitive); filterModel->setFilterRole(Qt::DisplayRole); listView->setModel(filterModel); listView->setObjectName("SearchListView"); searchField->setClearButtonEnabled(true); searchField->setObjectName("SearchBox"); searchField->setPlaceholderText(tr("Search")); QVBoxLayout *popupLayout = new QVBoxLayout(popup); popupLayout->setContentsMargins(5, 5, 5, 5); popupLayout->addWidget(searchField); popupLayout->addWidget(listView); popup->setLayout(popupLayout); popup->setObjectName("SearchPopup"); connect(searchField, &QLineEdit::textChanged, this, &SmartSearchBox::onTextChanged); connect(listView, &QListView::clicked, this, &SmartSearchBox::onItemClicked); openButton = new QPushButton(this); connect(openButton, &QPushButton::clicked, this, [this]() { popup->move(mapToGlobal(QPoint(0, height()))); popup->show(); adjustPopupSize(); searchField->setFocus(); }); QVBoxLayout *mainLayout = new QVBoxLayout(this); mainLayout->setContentsMargins(0, 0, 0, 0); mainLayout->addWidget(openButton); setLayout(mainLayout); searchField->installEventFilter(this); listView->installEventFilter(this); popup->installEventFilter(this); connect(popup, &QDialog::finished, this, [this]() { searchField->clear(); }); setFocusProxy(openButton); } void SmartSearchBox::setModel(QAbstractItemModel *model) { if ( !model ) return ; SqlListModel *sqlModel = qobject_cast(model); if ( sqlModel ) while (sqlModel->canFetchMore()) sqlModel->fetchMore(); filterModel->setSourceModel(model); } void SmartSearchBox::onTextChanged(const QString &text) { filterModel->setFilterRegularExpression(QRegularExpression::escape(text)); if ( filterModel->rowCount() == 0 ) { listView->clearSelection(); return; } QModelIndex indexToSelect; if (text.isEmpty()) indexToSelect = currentValueSourceIndex.isValid() ? filterModel->mapFromSource(currentValueSourceIndex) : filterModel->index(0, selectedColumn); else indexToSelect = filterModel->index(0, selectedColumn); if ( indexToSelect.isValid() ) listView->setCurrentIndex(indexToSelect); } void SmartSearchBox::onItemClicked(const QModelIndex &index) { const QModelIndex &sourceIndex = filterModel->mapToSource(index); currentValueSourceIndex = sourceIndex; popup->hide(); searchField->clear(); changeButtonText(currentText()); } bool SmartSearchBox::eventFilter(QObject *obj, QEvent *event) { // This is a hack for pressing the ESC key for closing the Widget // It is defined as a Global Shortcut, but the problem is that the global shortcut consumes this event. // Therefore, the event never reaches this widget. // A suitable event was found for the ESC key press that is generated and does not negatively affect // the other widget functionalities. if ( event->type() == QEvent::KeyRelease && obj == popup && !this->hasFocus() && !popup->hasFocus() && !searchField->hasFocus() && ! listView->hasFocus()) { searchField->clear(); popup->hide(); return QWidget::eventFilter(obj, event); } if ( event->type() != QEvent::KeyPress ) return QWidget::eventFilter(obj, event); QKeyEvent *keyEvent = static_cast(event); int key = keyEvent->key(); if ( obj == searchField ) { if ( (key == Qt::Key_Down || key == Qt::Key_Up) && filterModel->rowCount() > 0 ) { listView->setFocus(); QModelIndex currIndex = listView->currentIndex(); int currentRow = currIndex.isValid() ? currIndex.row() : (key == Qt::Key_Up ? filterModel->rowCount() : -1); int nextRow = currentRow + (key == Qt::Key_Down ? 1 : -1); if ( nextRow < 0 ) nextRow = 0; else if ( nextRow >= filterModel->rowCount() ) nextRow = filterModel->rowCount() - 1; listView->setCurrentIndex(filterModel->index(nextRow, selectedColumn)); return true; } if ( key == Qt::Key_Return || key == Qt::Key_Enter ) { QModelIndex index = listView->currentIndex(); if ( index.isValid() ) onItemClicked(index); return true; } } else if ( obj == listView ) { if ( key == Qt::Key_Return || key == Qt::Key_Enter ) { QModelIndex index = listView->currentIndex(); if (index.isValid()) onItemClicked(index); return true; } if ( key == Qt::Key_Up && listView->currentIndex().row() == 0 ) { searchField->setFocus(); return true; } if ( !keyEvent->text().isEmpty() ) { // send keytext to search widget searchField->setFocus(); QKeyEvent forwardedEvent(QEvent::KeyPress, key, keyEvent->modifiers(), keyEvent->text()); QCoreApplication::sendEvent(searchField, &forwardedEvent); return true; } } return QWidget::eventFilter(obj, event); } void SmartSearchBox::adjustPopupSize() { if (!filterModel || filterModel->rowCount() == 0) return; int popupWidth = listView->sizeHintForColumn(selectedColumn) + 40; // padding listView->setMinimumWidth(popupWidth); int visibleRows = qMin(filterModel->rowCount(), maxRowsInList); int rowHeight = listView->sizeHintForRow(0); if (rowHeight <= 0) rowHeight = listView->fontMetrics().height(); int searchFieldHeight = searchField->sizeHint().height(); int popupHeight = searchFieldHeight + (visibleRows * rowHeight) + 20; // margin popup->resize(popupWidth + 10, popupHeight); } void SmartSearchBox::changeButtonText(const QString &text) { if ( openButton->text() != text ) { openButton->setText(text); adjustMaxSize(); listView->setCurrentIndex(filterModel->mapFromSource(currentValueSourceIndex)); emit currentTextChanged(text); } openButton->setStyleSheet( ( highlightWhenEnable && currentValueSourceIndex.row() > 0) ? "QPushButton " "{ border: 2px solid red; " " border-radius: 4px; " " padding: 2px;}" : ""); } void SmartSearchBox::setModelColumn(int column) { filterModel->setFilterKeyColumn(column); listView->setModelColumn(column); selectedColumn = column; } QVariant SmartSearchBox::currentValue(int column) { if ( !filterModel->sourceModel() ) return {}; return filterModel->sourceModel()->data(currentValueSourceIndex, Qt::UserRole + column); } QString SmartSearchBox::currentText() const { if ( !filterModel->sourceModel() ) return {}; return filterModel->sourceModel()->data(currentValueSourceIndex).toString(); } int SmartSearchBox::currentIndex() { if (!filterModel || !filterModel->sourceModel() || !currentValueSourceIndex.isValid() ) return 0; QModelIndex filteredIndex = filterModel->mapFromSource(currentValueSourceIndex); if (!filteredIndex.isValid()) return 0; return filteredIndex.row(); } void SmartSearchBox::setCurrentText(const QString &text) { if (!filterModel || !filterModel->sourceModel()) return; QAbstractItemModel *model = filterModel->sourceModel(); // default value is the first row value currentValueSourceIndex = model->index(0, selectedColumn); for ( int row = 0; row < model->rowCount(); ++row ) { const QModelIndex &index = model->index(row, selectedColumn); QString itemText = index.data(Qt::DisplayRole).toString(); if ( itemText.compare(text, Qt::CaseInsensitive) == 0 ) { currentValueSourceIndex = index; break; } } changeButtonText(currentText()); } void SmartSearchBox::setCurrentValue(const QVariant var, int column) { if (!filterModel || !filterModel->sourceModel()) return; QAbstractItemModel *model = filterModel->sourceModel(); // default value is the first row value currentValueSourceIndex = model->index(0, selectedColumn); for ( int row = 0; row < model->rowCount(); ++row ) { const QModelIndex &index = model->index(row, selectedColumn); QVariant userData = index.data(Qt::UserRole + column); if ( userData.toString() == var.toString() ) { currentValueSourceIndex = index; break; } } changeButtonText(currentText()); } void SmartSearchBox::adjustMaxSize() { if ( !filterModel || filterModel->sourceModel() || filterModel->sourceModel()->rowCount() == 0) return; const QModelIndex index = filterModel->sourceModel()->index(0, selectedColumn); if (!index.isValid()) return; const QString text = filterModel->sourceModel()->data(index).toString(); if (text.isEmpty()) return; const QFontMetrics metrics(font()); openButton->setMaximumWidth(metrics.horizontalAdvance(text) + 35); // + padding } void SmartSearchBox::refreshModel() { if ( !filterModel->sourceModel() ) return ; SqlListModel *sqlModel = qobject_cast(filterModel->sourceModel()); if ( sqlModel ) { QString text = currentText(); sqlModel->refresh(); setCurrentText(text); } } void SmartSearchBox::setHighlightWhenEnable(bool state) { highlightWhenEnable = state; } ================================================ FILE: ui/component/SmartSearchBox.h ================================================ #ifndef QLOG_UI_COMPONENT_SMARTSEARCHBOX_H #define QLOG_UI_COMPONENT_SMARTSEARCHBOX_H #include #include #include #include #include #include class SmartSearchBox : public QWidget { Q_OBJECT public: explicit SmartSearchBox(QWidget *parent = nullptr); void setModel(QAbstractItemModel *model); void setModelColumn(int column); QVariant currentValue(int column); QString currentText() const; int currentIndex(); void setCurrentText(const QString &text); void setCurrentValue(const QVariant var, int column); void adjustMaxSize(); void refreshModel(); void setHighlightWhenEnable(bool state); signals: void currentTextChanged(QString); private slots: void onTextChanged(const QString &text); void onItemClicked(const QModelIndex &index); protected: bool eventFilter(QObject *obj, QEvent *event) override; private: QLineEdit *searchField; QListView *listView; QDialog *popup; QSortFilterProxyModel *filterModel; QPushButton *openButton; int selectedColumn; int maxRowsInList; QModelIndex currentValueSourceIndex; bool highlightWhenEnable; void adjustPopupSize(); void changeButtonText(const QString &text); }; #endif // QLOG_UI_COMPONENT_SMARTSEARCHBOX_H ================================================ FILE: ui/component/SqlHighlighter.cpp ================================================ #include "SqlHighlighter.h" #include "core/debug.h" #include #include #include #include MODULE_IDENTIFICATION("qlog.ui.component.sqlhighlighter"); // Detect dark mode by checking whether the window background is darker than mid-gray. static bool detectDarkMode() { return QApplication::palette().window().color().lightness() < 128; } const QStringList &SqlHighlighter::sqlKeywords() { static const QStringList keywords = { "SELECT", "FROM", "WHERE", "AND", "OR", "NOT", "IN", "LIKE", "BETWEEN", "IS", "NULL", "ORDER", "BY", "GROUP", "HAVING", "LIMIT", "OFFSET", "DISTINCT", "ALL", "AS", "JOIN", "LEFT", "RIGHT", "INNER", "OUTER", "FULL", "CROSS", "ON", "UNION", "INTERSECT", "EXCEPT", "WITH", "RECURSIVE", "CASE", "WHEN", "THEN", "ELSE", "END", "EXISTS", "ASC", "DESC", "COLLATE", "CAST", "TRUE", "FALSE", "ROWID", "NOCASE", "BINARY" }; return keywords; } const QStringList &SqlHighlighter::sqlFunctions() { static const QStringList functions = { "COUNT", "SUM", "AVG", "MIN", "MAX", "ABS", "LENGTH", "LOWER", "UPPER", "SUBSTR", "TRIM", "LTRIM", "RTRIM", "REPLACE", "INSTR", "PRINTF", "ROUND", "COALESCE", "NULLIF", "IFNULL", "IIF", "TYPEOF", "DATE", "TIME", "DATETIME", "JULIANDAY", "STRFTIME", "RANDOM", "HEX", "QUOTE", "GROUP_CONCAT", "JSON_EXTRACT" }; return functions; } SqlHighlighter::SqlHighlighter(QTextDocument *parent) : QSyntaxHighlighter(parent), isDark(detectDarkMode()) { // ----------------------------------------------------------------------- // Color palette - two sets so both themes are readable. // // Dark colours lifted from VS Code "Dark+" defaults. // Light colours lifted from VS Code "Light+" defaults. // ----------------------------------------------------------------------- const QColor keywordColor = isDark ? QColor(86, 156, 214) : QColor( 0, 0, 187); const QColor functionColor = isDark ? QColor(220, 220, 170) : QColor(121, 94, 38); const QColor stringColor = isDark ? QColor(206, 145, 120) : QColor(163, 21, 21); const QColor numberColor = isDark ? QColor(181, 206, 168) : QColor( 9, 134, 88); const QColor commentColor = isDark ? QColor(106, 153, 85) : QColor( 10, 121, 10); // SQL keywords - bold QTextCharFormat keywordFormat; keywordFormat.setForeground(keywordColor); keywordFormat.setFontWeight(QFont::Bold); for ( const QString &kw : sqlKeywords() ) { Rule rule; rule.pattern = QRegularExpression( QString("\\b%1\\b").arg(kw), QRegularExpression::CaseInsensitiveOption); rule.format = keywordFormat; rules.append(rule); } // Built-in functions QTextCharFormat functionFormat; functionFormat.setForeground(functionColor); for ( const QString &fn : sqlFunctions() ) { Rule rule; rule.pattern = QRegularExpression( QString("\\b%1\\b").arg(fn), QRegularExpression::CaseInsensitiveOption); rule.format = functionFormat; rules.append(rule); } // Single-quoted strings QTextCharFormat stringFormat; stringFormat.setForeground(stringColor); Rule stringRule; stringRule.pattern = QRegularExpression("'[^']*'"); stringRule.format = stringFormat; rules.append(stringRule); // Numbers QTextCharFormat numberFormat; numberFormat.setForeground(numberColor); Rule numberRule; numberRule.pattern = QRegularExpression("\\b[0-9]+(\\.[0-9]+)?\\b"); numberRule.format = numberFormat; rules.append(numberRule); // Single-line comments (--) QTextCharFormat singleLineCommentFormat; singleLineCommentFormat.setForeground(commentColor); Rule slComment; slComment.pattern = QRegularExpression("--[^\n]*"); slComment.format = singleLineCommentFormat; rules.append(slComment); // Multi-line block comment format and delimiters multiLineCommentFormat.setForeground(commentColor); blockCommentStart = QRegularExpression("/\\*"); blockCommentEnd = QRegularExpression("\\*/"); // identifierColor is applied in setUserIdentifiers() using m_isDark } void SqlHighlighter::setUserIdentifiers(const QStringList &identifiers) { identifierRules.clear(); // Re-derive the identifier color from m_isDark so it stays in sync. const QColor identifierColor = isDark ? QColor(156, 220, 254) : QColor(0, 16, 128); QTextCharFormat identifierFormat; identifierFormat.setForeground(identifierColor); for (const QString &id : identifiers) { Rule rule; rule.pattern = QRegularExpression( QString("\\b%1\\b").arg(QRegularExpression::escape(id)), QRegularExpression::CaseInsensitiveOption); rule.format = identifierFormat; identifierRules.append(rule); } rehighlight(); } void SqlHighlighter::highlightBlock(const QString &text) { // Schema identifiers first (lowest priority - keywords override them below) for ( const Rule &rule : static_cast&>(identifierRules) ) { QRegularExpressionMatchIterator it = rule.pattern.globalMatch(text); while (it.hasNext()) { QRegularExpressionMatch m = it.next(); setFormat(m.capturedStart(), m.capturedLength(), rule.format); } } // Keywords, functions, strings, numbers, single-line comments for ( const Rule &rule : static_cast&>(rules) ) { QRegularExpressionMatchIterator it = rule.pattern.globalMatch(text); while (it.hasNext()) { QRegularExpressionMatch m = it.next(); setFormat(m.capturedStart(), m.capturedLength(), rule.format); } } // Multi-line block comments - tracked across blocks via block state setCurrentBlockState(0); int startIndex = (previousBlockState() == 1) ? 0 : text.indexOf(blockCommentStart); while (startIndex >= 0) { QRegularExpressionMatch endMatch = blockCommentEnd.match(text, startIndex); int endIndex = endMatch.capturedStart(); int len; if (endIndex == -1) { setCurrentBlockState(1); len = text.length() - startIndex; } else { len = endIndex - startIndex + endMatch.capturedLength(); } setFormat(startIndex, len, multiLineCommentFormat); startIndex = text.indexOf(blockCommentStart, startIndex + len); } } ================================================ FILE: ui/component/SqlHighlighter.h ================================================ #ifndef QLOG_UI_COMPONENT_SQLHIGHLIGHTER_H #define QLOG_UI_COMPONENT_SQLHIGHLIGHTER_H #include #include #include #include class SqlHighlighter : public QSyntaxHighlighter { Q_OBJECT public: explicit SqlHighlighter(QTextDocument *parent = nullptr); void setUserIdentifiers(const QStringList &identifiers); static const QStringList &sqlKeywords(); static const QStringList &sqlFunctions(); protected: void highlightBlock(const QString &text) override; private: struct Rule { QRegularExpression pattern; QTextCharFormat format; }; QVector rules; QVector identifierRules; QTextCharFormat multiLineCommentFormat; QRegularExpression blockCommentStart; QRegularExpression blockCommentEnd; bool isDark; }; #endif // QLOG_UI_COMPONENT_SQLHIGHLIGHTER_H ================================================ FILE: ui/component/StyleItemDelegate.h ================================================ #ifndef QLOG_UI_STYLEITEMDELEGATE_H #define QLOG_UI_STYLEITEMDELEGATE_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core/LogLocale.h" #include "data/Gridsquare.h" #include "ui/component/BaseDoubleSpinBox.h" class CallsignDelegate : public QStyledItemDelegate { public: CallsignDelegate(QObject* parent = 0) : QStyledItemDelegate(parent) { } void initStyleOption(QStyleOptionViewItem* option, const QModelIndex& index) const { QStyledItemDelegate::initStyleOption(option, index); option->font.setBold(true); } }; class DateFormatDelegate : public QStyledItemDelegate { private: LogLocale locale; public: DateFormatDelegate(QObject* parent = 0) : QStyledItemDelegate(parent) { } QString displayText(const QVariant& value, const QLocale&) const { return value.toDate().toString(locale.formatDateShortWithYYYY()); } QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem&, const QModelIndex&) const { QDateEdit* editor = new QDateEdit(parent); #if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) editor->setTimeZone(QTimeZone::UTC); #else editor->setTimeSpec(Qt::UTC); #endif editor->setMinimumDate(QDate(1900, 1, 1)); editor->setSpecialValueText(tr("Blank")); editor->setDisplayFormat(locale.formatDateShortWithYYYY()); return editor; } void updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex&) const { editor->setGeometry(option.rect); } void setEditorData(QWidget* editor, const QModelIndex& index) const { QDate value = index.model()->data(index, Qt::EditRole).toDate(); QDateEdit* timeEdit = static_cast(editor); timeEdit->setDate(value); } void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const { QDateEdit* dateEdit = static_cast(editor); dateEdit->interpretText(); QDateTime value = dateEdit->dateTime(); model->setData(index, ( dateEdit->date() == dateEdit->minimumDate() ) ? QVariant() : value, Qt::EditRole); } }; class TimestampFormatDelegate : public QStyledItemDelegate { private: LogLocale locale; public: TimestampFormatDelegate(QObject* parent = 0) : QStyledItemDelegate(parent) { } QString displayText(const QVariant& value, const QLocale&) const { return locale.toString(value.toDateTime().toTimeZone(QTimeZone::utc()), locale.formatDateShortWithYYYY() + " " + locale.formatTimeLongWithoutTZ()); } QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem&, const QModelIndex&) const { QDateTimeEdit* editor = new QDateTimeEdit(parent); #if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) editor->setTimeZone(QTimeZone::UTC); #else editor->setTimeSpec(Qt::UTC); #endif editor->setDateTime(QDateTime(QDate(1900, 1, 1), QTime(0, 0, 0))); editor->setSpecialValueText(tr("Blank")); editor->setDisplayFormat(locale.formatDateShortWithYYYY() + " " + locale.formatTimeLongWithoutTZ()); return editor; } void updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex&) const { editor->setGeometry(option.rect); } void setEditorData(QWidget* editor, const QModelIndex& index) const { QDateTime value = index.model()->data(index, Qt::EditRole).toDateTime(); QDateTimeEdit* timeEdit = static_cast(editor); timeEdit->setDateTime(value); } void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const { QDateTimeEdit* timeEdit = static_cast(editor); timeEdit->interpretText(); QDateTime value = timeEdit->dateTime(); model->setData(index, ( timeEdit->date() == timeEdit->minimumDate() ) ? QVariant() : value, Qt::EditRole); } }; class UnitFormatDelegate : public QStyledItemDelegate { public: UnitFormatDelegate(const QString &unit, int precision, double step, QObject* parent = 0) : QStyledItemDelegate(parent), unit(unit), precision(precision), step(step) { } void initStyleOption(QStyleOptionViewItem* option, const QModelIndex& index) const { QStyledItemDelegate::initStyleOption(option, index); option->displayAlignment = Qt::AlignVCenter | Qt::AlignLeft; } QString displayText(const QVariant& value, const QLocale&) const { return QString("%1 %2").arg(QString::number(value.toDouble(), 'f', precision), unit); } QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem&, const QModelIndex&) const { BaseDoubleSpinBox* editor = new BaseDoubleSpinBox(parent); editor->setDecimals(precision); editor->setRange(-1*step, 1e12); editor->setSingleStep(step); editor->setSpecialValueText("Empty"); return editor; } void updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex&) const { editor->setGeometry(option.rect); } void setEditorData(QWidget* editor, const QModelIndex& index) const { double value = index.model()->data(index, Qt::EditRole).toDouble(); BaseDoubleSpinBox* spinBox = static_cast(editor); spinBox->setValue(value); } void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const { BaseDoubleSpinBox* spinBox = static_cast(editor); if (spinBox->text() == "Empty" ) { model->setData(index, QVariant() , Qt::EditRole); return; } spinBox->interpretText(); double value = spinBox->value(); model->setData(index,value, Qt::EditRole); } private: QString unit; int precision; double step; }; class DistanceFormatDelegate : public QStyledItemDelegate { public: DistanceFormatDelegate(int precision, double step, QObject* parent = 0) : QStyledItemDelegate(parent), precision(precision), step(step) { } void initStyleOption(QStyleOptionViewItem* option, const QModelIndex& index) const { QStyledItemDelegate::initStyleOption(option, index); option->displayAlignment = Qt::AlignVCenter | Qt::AlignLeft; } QString displayText(const QVariant& value, const QLocale&) const { QString unit; double displayValue = Gridsquare::distance2localeUnitDistance(value.toDouble(), unit, locale); return QString("%1 %2").arg(QString::number(displayValue, 'f', precision), unit); } QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem&, const QModelIndex&) const { BaseDoubleSpinBox* editor = new BaseDoubleSpinBox(parent); editor->setDecimals(precision); editor->setRange(-1*step, 1e12); editor->setSingleStep(step); editor->setSpecialValueText("Empty"); return editor; } void updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex&) const { editor->setGeometry(option.rect); } void setEditorData(QWidget* editor, const QModelIndex& index) const { double value = index.model()->data(index, Qt::EditRole).toDouble(); BaseDoubleSpinBox* spinBox = static_cast(editor); spinBox->setValue(value); } void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const { BaseDoubleSpinBox* spinBox = static_cast(editor); if (spinBox->text() == "Empty" ) { model->setData(index, QVariant() , Qt::EditRole); return; } spinBox->interpretText(); double value = spinBox->value(); model->setData(index,value, Qt::EditRole); } private: int precision; double step; LogLocale locale; }; class ComboFormatDelegate : public QStyledItemDelegate { public: ComboFormatDelegate(QAbstractTableModel* in_model, QObject* parent = nullptr): QStyledItemDelegate(parent) { model = in_model; } ComboFormatDelegate(QStringList in_list, QObject* parent = nullptr): QStyledItemDelegate(parent) { model = nullptr; list = in_list; } ComboFormatDelegate(QMap in_map, QObject* parent = nullptr): QStyledItemDelegate(parent) { model = nullptr; map = in_map; } QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem&, const QModelIndex&) const { QComboBox *editor = new QComboBox(parent); return editor; } void updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex&) const { editor->setGeometry(option.rect); } void setEditorData(QWidget* editor, const QModelIndex& index) const { QComboBox* combo = static_cast(editor); combo->clear(); if ( model ) { combo->setModel(model); combo->setCurrentText(index.model()->data(index).toString()); } else if ( ! list.isEmpty() ) { combo->addItems(list); combo->setCurrentText(index.model()->data(index).toString()); } else if ( ! map.isEmpty() ) { QMapIterator iter(map); int iter_index = 0; int value_index = 0; while ( iter.hasNext() ) { iter.next(); combo->addItem(iter.value(), iter.key()); if ( iter.key() == index.model()->data(index).toString() ) { value_index = iter_index; } iter_index++; } combo->setCurrentIndex(value_index); } } void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const { QComboBox* combo = static_cast(editor); QString curr_value; if ( !map.isEmpty() ) { curr_value = combo->currentData().toString(); } else { curr_value = combo->currentText(); } if ( curr_value == " " ) { model->setData(index, QVariant(), Qt::EditRole); } else { model->setData(index, QVariant(curr_value), Qt::EditRole); } } private: QAbstractTableModel *model; QStringList list; QMap map; }; class CheckBoxDelegate: public QItemDelegate { Q_OBJECT public: CheckBoxDelegate(QObject *parent = nullptr ) : QItemDelegate(parent) {}; void paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const { bool is_enabled = index.model()->data(index, Qt::EditRole).toBool(); if ( !is_enabled) painter->fillRect(option.rect, option.palette.dark()); drawDisplay(painter,option,option.rect,is_enabled? QString(" ").append(tr("Enabled")) : QString(" ").append(tr("Disabled"))); drawFocus(painter,option,option.rect); }; QWidget *createEditor( QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &) const { QCheckBox *theCheckBox = new QCheckBox(parent); connect(theCheckBox, &QCheckBox::toggled, this, &CheckBoxDelegate::setData); return theCheckBox; }; void setEditorData( QWidget *editor, const QModelIndex &index ) const { bool val = index.model()->data( index, Qt::EditRole ).toBool(); QCheckBox *theCheckBox = static_cast(editor); theCheckBox->blockSignals(true); theCheckBox->setChecked(val); theCheckBox->blockSignals(false); } void setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const { model->setData(index, static_cast(editor)->isChecked(), Qt::EditRole); } void updateEditorGeometry( QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex & ) const { editor->setGeometry( option.rect ); } private slots: void setData(bool ) { QCheckBox *cb = qobject_cast(sender()); if (!cb) return; emit commitData(cb); emit closeEditor(cb, QAbstractItemDelegate::NoHint); } }; class TextBoxDelegate: public QItemDelegate { Q_OBJECT public: TextBoxDelegate(QObject *parent = 0 ) :QItemDelegate(parent){}; QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem&, const QModelIndex&) const { return new QTextEdit(parent); } void updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex&) const { editor->setGeometry(option.rect.x(),option.rect.y(),editor->sizeHint().width(),editor->sizeHint().height()); } void setEditorData(QWidget* editor, const QModelIndex& index) const { QString value = index.model()->data(index, Qt::EditRole).toString(); QTextEdit* textEditor = static_cast(editor); textEditor->setPlainText(value); textEditor->setAcceptRichText(false); } void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const { QTextEdit* textEditor = static_cast(editor); QString value = textEditor->toPlainText(); model->setData(index,value, Qt::EditRole); } void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { QString line = index.model()->data(index, Qt::EditRole).toString().simplified(); drawDisplay(painter,option,option.rect, line); drawFocus(painter, option, option.rect); } }; class KeySequenceEdit : public QWidget { Q_OBJECT public: KeySequenceEdit(QWidget* parent = nullptr) : QWidget(parent) { QHBoxLayout* layout = new QHBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); QPushButton* clearButton = new QPushButton(QLatin1String(), this); QIcon clearIcon = QApplication::style()->standardIcon(QStyle::SP_LineEditClearButton); clearButton->setIcon(clearIcon); clearButton->setToolTip(tr("Clear")); keySequenceEdit = new QKeySequenceEdit(this); layout->addWidget(keySequenceEdit); layout->addWidget(clearButton); connect(clearButton, &QPushButton::clicked, this, [=]() { keySequenceEdit->blockSignals(true); keySequenceEdit->clear(); keySequenceEdit->blockSignals(false); emit clearPress(); }); connect(keySequenceEdit, &QKeySequenceEdit::editingFinished, this, [=]() { emit editingFinished(); }); setFocusProxy(keySequenceEdit); } void setKeySequence(const QKeySequence &keySequence) { keySequenceEdit->setKeySequence(keySequence); } QKeySequence keySequence() const { return keySequenceEdit->keySequence(); } signals: void editingFinished(); void clearPress(); private: QKeySequenceEdit* keySequenceEdit; }; class ShortcutDelegate : public QStyledItemDelegate { Q_OBJECT public: ShortcutDelegate(QObject* parent = nullptr) : QStyledItemDelegate(parent) { } QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem&, const QModelIndex&) const { KeySequenceEdit *editor = new KeySequenceEdit(parent); connect(editor, &KeySequenceEdit::editingFinished, this, &ShortcutDelegate::commitAndCloseEditor); connect(editor, &KeySequenceEdit::clearPress, this, &ShortcutDelegate::commitAndCloseEditor); return editor; } void updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex&) const { editor->setGeometry(option.rect); } void setEditorData(QWidget* editor, const QModelIndex& index) const { KeySequenceEdit *keySequenceEdit = static_cast(editor); keySequenceEdit->blockSignals(true); keySequenceEdit->setKeySequence(index.model()->data(index, Qt::EditRole).toString()); keySequenceEdit->blockSignals(false); } void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const { const KeySequenceEdit *keySequenceEdit = static_cast(editor); model->setData(index, keySequenceEdit->keySequence().toString(QKeySequence::NativeText)); } private slots: void commitAndCloseEditor() { KeySequenceEdit *editor = static_cast(sender()); QAbstractItemView* view = qobject_cast(parent()); if ( view && view->indexWidget(view->currentIndex()) != editor ) { // if parent view is not active do nothing // otherwise it produces a warning QAbstractItemView::commitData called with an editor that does not belong to this view return; } emit commitData(editor); emit closeEditor(editor, QAbstractItemDelegate::NoHint); } }; class ReadOnlyDelegate : public QStyledItemDelegate { Q_OBJECT public: using QStyledItemDelegate::QStyledItemDelegate; QWidget *createEditor(QWidget *, const QStyleOptionViewItem &, const QModelIndex &) const override { return nullptr; } }; class UpperCaseDelegate : public QStyledItemDelegate { Q_OBJECT public: using QStyledItemDelegate::QStyledItemDelegate; QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &) const override { QLineEdit *editor = new QLineEdit(parent); return editor; } void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override { QLineEdit *lineEdit = qobject_cast(editor); if (lineEdit) { QString text = lineEdit->text().toUpper(); model->setData(index, text); } } }; class UpperCaseUniqueDelegate : public UpperCaseDelegate { Q_OBJECT public: using UpperCaseDelegate::UpperCaseDelegate; QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &) const override { QLineEdit *editor = new QLineEdit(parent); return editor; } void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override { QLineEdit *lineEdit = qobject_cast(editor); if (lineEdit) { QString text = lineEdit->text().toUpper(); for (int row = 0; row < model->rowCount(); ++row) { if (row == index.row()) continue; if (model->data(model->index(row, index.column()), Qt::EditRole).toString() == text) { QMessageBox::warning(nullptr, "Duplicate value", "The value must be unique."); return; } } model->setData(index, text); } } }; class PasswordDelegate : public QStyledItemDelegate { Q_OBJECT public: using QStyledItemDelegate::QStyledItemDelegate; QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &) const override { QLineEdit *editor = new QLineEdit(parent); editor->setEchoMode(QLineEdit::Password); return editor; } QString displayText(const QVariant &value, const QLocale &) const override { QLineEdit dummy; dummy.setEchoMode(QLineEdit::Password); dummy.setText(value.toString()); return dummy.displayText(); } }; #endif // QLOG_UI_STYLEITEMDELEGATE_H ================================================ FILE: ui/component/SwitchButton.cpp ================================================ /* * This is nearly complete Material design Switch widget implementation in qtwidgets module. * More info: https://material.io/design/components/selection-controls.html#switches * Copyright (C) 2018-2020 Iman Ahmadvand * * This is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * It is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include "SwitchButton.h" Animator::Animator(QObject* target, QObject* parent) : QVariantAnimation(parent) { setTargetObject(target); } Animator::~Animator() { stop(); } QObject* Animator::targetObject() const { return target.data(); } void Animator::setTargetObject(QObject* _target) { if (target.data() == _target) return; if (isRunning()) { qWarning("Animation::setTargetObject: you can't change the target of a running animation"); return; } target = _target; } void Animator::updateCurrentValue(const QVariant& value) { Q_UNUSED(value); if (!target.isNull()) { auto update = QEvent(QEvent::StyleAnimationUpdate); update.setAccepted(false); QCoreApplication::sendEvent(target.data(), &update); if (!update.isAccepted()) stop(); } } void Animator::updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState) { if (target.isNull() && oldState == Stopped) { qWarning("Animation::updateState: Changing state of an animation without target"); return; } QVariantAnimation::updateState(newState, oldState); if (!endValue().isValid() && direction() == Forward) { qWarning("Animation::updateState (%s): starting an animation without end value", targetObject()->metaObject()->className()); } } void Animator::setup(int duration, QEasingCurve easing) { setDuration(duration); setEasingCurve(easing); } void Animator::interpolate(const QVariant& _start, const QVariant& end) { setStartValue(_start); setEndValue(end); start(); } void Animator::setCurrentValue(const QVariant& value) { setStartValue(value); setEndValue(value); updateCurrentValue(currentValue()); } SelectionControl::SelectionControl(QWidget* parent) : QAbstractButton(parent) { setObjectName("SelectionControl"); setCheckable(true); } SelectionControl::~SelectionControl() { } #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) void SelectionControl::enterEvent(QEvent* e) #else void SelectionControl::enterEvent(QEnterEvent* e) #endif { setCursor(Qt::PointingHandCursor); QAbstractButton::enterEvent(e); } Qt::CheckState SelectionControl::checkState() const { return isChecked() ? Qt::Checked : Qt::Unchecked; } void SelectionControl::checkStateSet() { const auto state = checkState(); emit stateChanged(state); toggle(state); } void SelectionControl::nextCheckState() { QAbstractButton::nextCheckState(); SelectionControl::checkStateSet(); } void SwitchButton::init() { setFont(style.font); setObjectName("Switch"); setFocusPolicy(Qt::ClickFocus); /* setup animations */ thumbBrushAnimation = new Animator{ this, this }; trackBrushAnimation = new Animator{ this, this }; thumbPosAniamtion = new Animator{ this, this }; thumbPosAniamtion->setup(style.thumbPosAniamtion.duration, style.thumbPosAniamtion.easing); trackBrushAnimation->setup(style.trackBrushAnimation.duration, style.trackBrushAnimation.easing); thumbBrushAnimation->setup(style.thumbBrushAnimation.duration, style.thumbBrushAnimation.easing); /* set init values */ trackBrushAnimation->setStartValue(colorFromOpacity(style.trackOffBrush, style.trackOffOpacity)); trackBrushAnimation->setEndValue(colorFromOpacity(style.trackOffBrush, style.trackOffOpacity)); thumbBrushAnimation->setStartValue(colorFromOpacity(style.thumbOffBrush, style.thumbOffOpacity)); thumbBrushAnimation->setEndValue(colorFromOpacity(style.thumbOffBrush, style.thumbOffOpacity)); /* set standard palettes */ auto p = palette(); p.setColor(QPalette::Active, QPalette::ButtonText, style.textColor); p.setColor(QPalette::Disabled, QPalette::ButtonText, style.textColor); setPalette(p); setSizePolicy(QSizePolicy(QSizePolicy::Policy::Preferred, QSizePolicy::Policy::Fixed)); } QRect SwitchButton::indicatorRect() { const auto w = style.indicatorMargin.left() + style.height + style.indicatorMargin.right(); return ltr(this) ? QRect(0, 0, w, style.height) : QRect(width() - w, 0, w, style.height); } QRect SwitchButton::textRect() { const auto w = style.indicatorMargin.left() + style.height + style.indicatorMargin.right(); return ltr(this) ? rect().marginsRemoved(QMargins(w, 0, 0, 0)) : rect().marginsRemoved(QMargins(0, 0, w, 0)); } SwitchButton::SwitchButton(QWidget* parent) : SelectionControl(parent) { init(); } SwitchButton::SwitchButton(const QString& text, QWidget* parent) : SwitchButton(parent) { setText(text); } SwitchButton::SwitchButton(const QString& text, const QBrush& brush, QWidget* parent) : SwitchButton(text, parent) { style.thumbOnBrush = brush.color(); style.trackOnBrush = brush.color(); } SwitchButton::~SwitchButton() { } QSize SwitchButton::sizeHint() const { auto h = style.height; #if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) auto w = style.indicatorMargin.left() + style.height + style.indicatorMargin.right() + fontMetrics().horizontalAdvance(text()); #else auto w = style.indicatorMargin.left() + style.height + style.indicatorMargin.right() + fontMetrics().width(text()); #endif return QSize(w, h); } void SwitchButton::paintEvent(QPaintEvent*) { /* for desktop usage we do not need Radial reaction */ QPainter p(this); const auto _indicatorRect = indicatorRect(); const auto _textRect = textRect(); auto trackMargin = style.indicatorMargin; trackMargin.setTop(trackMargin.top() + 2); trackMargin.setBottom(trackMargin.bottom() + 2); QRectF trackRect = _indicatorRect.marginsRemoved(trackMargin); if (isEnabled()) { p.setOpacity(1.0); p.setPen(Qt::NoPen); /* draw track */ p.setBrush(trackBrushAnimation->currentValue().value()); p.setRenderHint(QPainter::Antialiasing, true); p.drawRoundedRect(trackRect, CORNER_RADIUS, CORNER_RADIUS); p.setRenderHint(QPainter::Antialiasing, false); /* draw thumb */ trackRect.setX(trackRect.x() - trackMargin.left() - trackMargin.right() - 2 + thumbPosAniamtion->currentValue().toInt()); auto thumbRect = trackRect; if (!shadowPixmap.isNull()) p.drawPixmap(thumbRect.center() - QPointF(THUMB_RADIUS, THUMB_RADIUS - 1.0), shadowPixmap); p.setBrush(thumbBrushAnimation->currentValue().value()); p.setRenderHint(QPainter::Antialiasing, true); // qDebug() << thumbRect << thumbPosAniamtion->currentValue(); p.drawEllipse(thumbRect.center(), THUMB_RADIUS - SHADOW_ELEVATION - 1.0, THUMB_RADIUS - SHADOW_ELEVATION - 1.0); p.setRenderHint(QPainter::Antialiasing, false); /* draw text */ if (text().isEmpty()) return; p.setOpacity(1.0); p.setPen(palette().color(QPalette::Active, QPalette::ButtonText)); p.setFont(font()); p.drawText(_textRect, Qt::AlignLeft | Qt::AlignVCenter, text()); } else { p.setOpacity(style.trackDisabledOpacity); p.setPen(Qt::NoPen); // draw track p.setBrush(style.trackDisabled); p.setRenderHint(QPainter::Antialiasing, true); p.drawRoundedRect(trackRect, CORNER_RADIUS, CORNER_RADIUS); p.setRenderHint(QPainter::Antialiasing, false); // draw thumb p.setOpacity(1.0); if (!isChecked()) trackRect.setX(trackRect.x() - trackMargin.left() - trackMargin.right() - 2); else trackRect.setX(trackRect.x() + trackMargin.left() + trackMargin.right() + 2); auto thumbRect = trackRect; if (!shadowPixmap.isNull()) p.drawPixmap(thumbRect.center() - QPointF(THUMB_RADIUS, THUMB_RADIUS - 1.0), shadowPixmap); p.setOpacity(1.0); p.setBrush(style.thumbDisabled); p.setRenderHint(QPainter::Antialiasing, true); p.drawEllipse(thumbRect.center(), THUMB_RADIUS - SHADOW_ELEVATION - 1.0, THUMB_RADIUS - SHADOW_ELEVATION - 1.0); /* draw text */ if (text().isEmpty()) return; p.setOpacity(style.disabledTextOpacity); p.setPen(palette().color(QPalette::Disabled, QPalette::ButtonText)); p.setFont(font()); p.drawText(_textRect, Qt::AlignLeft | Qt::AlignVCenter, text()); } } void SwitchButton::resizeEvent(QResizeEvent* e) { shadowPixmap = Style::drawShadowEllipse(THUMB_RADIUS, SHADOW_ELEVATION, QColor(0, 0, 0, 70)); SelectionControl::resizeEvent(e); } void SwitchButton::toggle(Qt::CheckState state) { if (state == Qt::Checked) { const QVariant posEnd = (style.indicatorMargin.left() + style.indicatorMargin.right() + 2) * 2; const QVariant thumbEnd = colorFromOpacity(style.thumbOnBrush, style.thumbOnOpacity); const QVariant trackEnd = colorFromOpacity(style.trackOnBrush, style.trackOnOpacity); if (!isVisible()) { thumbPosAniamtion->setCurrentValue(posEnd); thumbBrushAnimation->setCurrentValue(thumbEnd); trackBrushAnimation->setCurrentValue(trackEnd); } else { thumbPosAniamtion->interpolate(0, posEnd); thumbBrushAnimation->interpolate(colorFromOpacity(style.thumbOffBrush, style.thumbOffOpacity), thumbEnd); trackBrushAnimation->interpolate(colorFromOpacity(style.trackOffBrush, style.trackOffOpacity), trackEnd); } } else { // Qt::Unchecked const QVariant posEnd = 0; const QVariant thumbEnd = colorFromOpacity(style.thumbOffBrush, style.thumbOffOpacity); const QVariant trackEnd = colorFromOpacity(style.trackOffBrush, style.trackOffOpacity); if (!isVisible()) { thumbPosAniamtion->setCurrentValue(posEnd); thumbBrushAnimation->setCurrentValue(thumbEnd); trackBrushAnimation->setCurrentValue(trackEnd); } else { thumbPosAniamtion->interpolate(thumbPosAniamtion->currentValue().toInt(), posEnd); thumbBrushAnimation->interpolate(colorFromOpacity(style.thumbOnBrush, style.thumbOnOpacity), thumbEnd); trackBrushAnimation->interpolate(colorFromOpacity(style.trackOnBrush, style.trackOnOpacity), trackEnd); } } } ================================================ FILE: ui/component/SwitchButton.h ================================================ /* * This is nearly complete Material design Switch widget implementation in qtwidgets module. * More info: https://material.io/design/components/selection-controls.html#switches * Copyright (C) 2018-2020 Iman Ahmadvand * * This is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * It is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef QLOG_UI_COMPONENT_SWITCHBUTTON_H #define QLOG_UI_COMPONENT_SWITCHBUTTON_H #include #include "ui/component/ButtonStyle.h" class Animator final : public QVariantAnimation { Q_OBJECT Q_PROPERTY(QObject* targetObject READ targetObject WRITE setTargetObject) public: Animator(QObject* target, QObject* parent = nullptr); ~Animator() override; QObject* targetObject() const; void setTargetObject(QObject* _target); inline bool isRunning() const { return state() == Running; } public slots: void setup(int duration, QEasingCurve easing = QEasingCurve::Linear); void interpolate(const QVariant& _start, const QVariant& end); void setCurrentValue(const QVariant&); protected: void updateCurrentValue(const QVariant& value) override final; void updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState) override final; private: QPointer target; }; class SelectionControl : public QAbstractButton { Q_OBJECT public: explicit SelectionControl(QWidget* parent = nullptr); ~SelectionControl() override; Qt::CheckState checkState() const; Q_SIGNALS: void stateChanged(int); protected: #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) void enterEvent(QEvent*) override; #else void enterEvent(QEnterEvent*) override; #endif void checkStateSet() override; void nextCheckState() override; virtual void toggle(Qt::CheckState state) = 0; }; class SwitchButton final : public SelectionControl { Q_OBJECT static constexpr auto CORNER_RADIUS = 8.0; static constexpr auto THUMB_RADIUS = 8.0; static constexpr auto SHADOW_ELEVATION = 2.0; public: explicit SwitchButton(QWidget* parent = nullptr); explicit SwitchButton(const QString& text, QWidget* parent = nullptr); explicit SwitchButton(const QString& text, const QBrush&, QWidget* parent = nullptr); ~SwitchButton() override; QSize sizeHint() const override final; protected: void paintEvent(QPaintEvent*) override final; void resizeEvent(QResizeEvent*) override final; void toggle(Qt::CheckState) override final; void init(); QRect indicatorRect(); QRect textRect(); static inline QColor colorFromOpacity(const QColor& c, qreal opacity) { return QColor(c.red(), c.green(), c.blue(), qRound(opacity * 255.0)); } static inline bool ltr(QWidget* w) { if (nullptr != w) return w->layoutDirection() == Qt::LeftToRight; return false; } private: Style::Switch style; QPixmap shadowPixmap; QPointer thumbBrushAnimation; QPointer trackBrushAnimation; QPointer thumbPosAniamtion; }; #endif // QLOG_UI_COMPONENT_SWITCHBUTTON_H

Icon by Icon Shock
" "Satellite images by NASA
" "ZoneDetect by Bertold Van den Bergh
" "TimeZone Database by Evan Siroky"); QString version = QCoreApplication::applicationVersion(); QString hamlibVersion = #if defined(Q_OS_WIN) QString(rig_version()); #else QString(hamlib_version); #endif QString OSName = QString("%1 %2 (%3)").arg(QSysInfo::prettyProductName(), QSysInfo::currentCpuArchitecture(), QGuiApplication::platformName() ); #ifdef QLOG_FLATPAK OSName.append(" Flatpak"); #endif aboutText = aboutText.arg(version, qVersion(), hamlibVersion, QSslSocket::sslLibraryVersionString(), OSName); QMessageBox::about(this, tr("About"), aboutText); } void MainWindow::showWhatsNew() { FCT_IDENTIFICATION; QDesktopServices::openUrl(QString("https://github.com/foldynl/QLog/releases/tag/v%0").arg(VERSION)); } void MainWindow::showWikiHelp() { FCT_IDENTIFICATION; QDesktopServices::openUrl(QString("https://github.com/foldynl/QLog/wiki")); } void MainWindow::showMailingList() { FCT_IDENTIFICATION; QDesktopServices::openUrl(QString("https://groups.io/g/qlog")); } void MainWindow::showReportBug() { FCT_IDENTIFICATION; QDesktopServices::openUrl(QString("https://github.com/foldynl/QLog/blob/master/CONTRIBUTING.md#reporting-bugs")); } void MainWindow::showAlerts() { FCT_IDENTIFICATION; ui->alertDockWidget->show(); } void MainWindow::clearAlerts() { FCT_IDENTIFICATION; ui->alertsWidget->clearAllAlerts(); } void MainWindow::conditionsUpdated() { FCT_IDENTIFICATION; QString kcolor, fluxcolor, acolor; QString k_index_string, flux_string, a_index_string; k_index_string = flux_string = a_index_string = tr("N/A"); /* https://3fs.net.au/making-sense-of-solar-indices/ */ if ( conditions->isKIndexValid() ) { double k_index = conditions->getKIndex(); if (k_index < 3.5) { kcolor = "green"; } else if (k_index < 4.5) { kcolor = "orange"; } else { kcolor = "red"; } k_index_string = QString::number(k_index, 'g', 2); } if ( conditions->isFluxValid() ) { if ( conditions->getFlux() < 100 ) { fluxcolor = "red"; } else if ( conditions->getFlux() < 200 ) { fluxcolor = "orange"; } else { fluxcolor = "green"; } flux_string = QString::number(conditions->getFlux()); } if ( conditions->isAIndexValid() ) { if ( conditions->getAIndex() < 27 ) { acolor = "green"; } else if ( conditions->getAIndex() < 48 ) { acolor = "orange"; } else { acolor = "red"; } a_index_string = QString::number(conditions->getAIndex()); } conditionsLabel->setTextFormat(Qt::RichText); conditionsLabel->setText(QString("SFI %2 A %4 K %6").arg( fluxcolor, flux_string, acolor, a_index_string, kcolor, k_index_string )); } void MainWindow::QSOFilterSetting() { FCT_IDENTIFICATION; QSOFilterDialog dialog(this); dialog.exec(); ui->logbookWidget->refreshUserFilter(); stats->refreshWidget(); } void MainWindow::alertRuleSetting() { FCT_IDENTIFICATION; ui->alertsWidget->showEditRules(); } MainWindow::~MainWindow() { FCT_IDENTIFICATION; //saveEquipmentConnOptions(); Rig::instance()->close(); Rotator::instance()->close(); CWKeyer::instance()->close(); QThread::msleep(500); Rig::instance()->stopTimer(); Rotator::instance()->stopTimer(); CWKeyer::instance()->stopTimer(); conditions->deleteLater(); conditionsLabel->deleteLater(); profileLabel->deleteLater(); callsignLabel->deleteLater(); locatorLabel->deleteLater(); QSqlDatabase::database().close(); clublogRT->deleteLater(); if ( wsjtx ) wsjtx->deleteLater(); seqGroup->deleteLater(); dupeGroup->deleteLater(); delete ui; } ================================================ FILE: ui/MainWindow.h ================================================ #ifndef QLOG_UI_MAINWINDOW_H #define QLOG_UI_MAINWINDOW_H #include #include #include #include "ui/StatisticsWidget.h" #include "core/NetworkNotification.h" #include "core/AlertEvaluator.h" #include "core/PropConditions.h" #include "service/clublog/ClubLog.h" namespace Ui { class MainWindow; } class QLabel; class WsjtxUDPReceiver; class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget* parent = 0); ~MainWindow(); void closeEvent(QCloseEvent* event) override; void keyReleaseEvent(QKeyEvent *event) override; void changeEvent(QEvent *event) override; QList getUserDefinedShortcutActionList(); QStringList getBuiltInStaticShortcutList() const; signals: void settingsChanged(); void themeChanged(int themeMode, bool isDark); void altBackslash(bool active); void manualMode(bool); void contestStopped(); void dupeTypeChanged(); public slots: void rigErrorHandler(const QString &error, const QString &errorDetail); void rotErrorHandler(const QString &error, const QString &errorDetail); void cwKeyerErrorHandler(const QString &error, const QString &errorDetail); void stationProfileChanged(); void setLayoutGeometry(); void setSimplyLayoutGeometry(); void checkNewVersion(); private slots: void rigConnect(); void rotConnect(); void cwKeyerConnect(); void cwKeyerConnectProfile(QString); void cwKeyerDisconnectProfile(QString); void showSettings(); void showStatistics(); void importLog(); void exportLog(); void showAwards(); void showDXCCSubmission(); void showAbout(); void showWhatsNew(); void showWikiHelp(); void showMailingList(); void showReportBug(); void showAlerts(); void clearAlerts(); void conditionsUpdated(); void QSOFilterSetting(); void alertRuleSetting(); void processSpotAlert(SpotAlert alert); void clearAlertEvent(); void beepSettingAlerts(); void shortcutALTBackslash(); void setManualContact(bool); void showEditLayout(); void showServiceUpload(); void showServiceDownloadQSL(); void showDumpDB(); void showLoadDB(); void showQSLGallery(); void showDevTools(); void printQslLabels(); void saveProfileLayoutGeometry(); void setEquipmentKeepOptions(bool); void saveContestMenuSeqnoType(QAction *action); void saveContestMenuDupeType(QAction *action); void saveContestMenuLinkExchangeType(QAction *action); void startContest(const QString contestID, const QDateTime); void stopContest(); void exportCabrillo(); void setContestMode(const QString &contestID); void handleActivityChange(const QString name); void openNonVfoBandmap(const QString &widgetID, const QString& bandName); void showUpdateDialog(const QString &newVersion, const QString &repoName); private: Ui::MainWindow* ui; QLabel* conditionsLabel; QLabel* profileLabel; QLabel* callsignLabel; QLabel* locatorLabel; QLabel* contestLabel; QPushButton* alertButton; QPushButton* alertTextButton; QPushButton *themeButton; StatisticsWidget* stats; NetworkNotification networknotification; AlertEvaluator alertEvaluator; PropConditions *conditions; bool isFusionStyle; ClubLogUploader* clublogRT; WsjtxUDPReceiver* wsjtx; QActionGroup *seqGroup; QActionGroup *dupeGroup; QActionGroup *linkExchangeGroup; QPushButton *activityButton; QMetaObject::Connection alertTextButtonConn; bool firstRun = false; void setupActivitiesMenu(); void restoreUserDefinedShortcuts(); void saveUserDefinedShortcuts(); void restoreContestMenuSeqnoType(); void restoreContestMenuDupeType(); void restoreContestMenuLinkExchange(); QString stationCallsignStatus(const StationProfile &profile) const; void openNonVfoBandmaps(const QList> &list); void clearNonVfoBandmaps(); QList> getNonVfoBandmapsParams() const; void themeInit(int mode); bool setNativeTheme(); void setLightTheme(); void setDarkTheme(); void showEvent(QShowEvent *event) override; void restartApplication(); }; #endif // QLOG_UI_MAINWINDOW_H ================================================ FILE: ui/MainWindow.ui ================================================ MainWindow 0 0 913 625 QMainWindow::AllowNestedDocks|QMainWindow::AllowTabbedDocks|QMainWindow::AnimatedDocks|QMainWindow::GroupedDragging 0 0 0 0 0 Qt::ClickFocus 0 0 913 25 &File &Logbook &Equipment &Help &Window Se&rvice Contest Dupe Check Sequence Linking Exchange With true Toolbar false Qt::ToolButtonIconOnly false TopToolBarArea false Clock 2 Map 2 Qt::ClickFocus DX Cluster 2 WSJTX 2 Rotator 2 Bandmap 2 Rig 2 Online Map 2 CW Console 2 Chat 2 Profile Image 2 Alerts 2 .. Quit Application - Quit Ctrl+Q QAction::QuitRole true .. &Settings QAction::PreferencesRole Pack Data && Settings QAction::NoRole Unpack Data && Settings QAction::NoRole .. New QSO - Clear New QSO - Clear Ctrl+N true .. &Import QAction::NoRole .. &Export QAction::NoRole true Connect R&ig QAction::NoRole .. &About QAction::AboutRole .. New QSO - Save New QSO - Save Ctrl+S true :/res/icons/network-wireless.png:/res/icons/network-wireless.png DX Cluster QAction::NoRole :/res/icons/emblem-web.png:/res/icons/emblem-web.png Map QAction::NoRole .. S&tatistics QAction::NoRole QSL &Gallery QAction::NoRole .. Developer Tools Run custom read-only SQL queries against the logbook database QAction::NoRole Print QSL &Labels QAction::NoRole Wsjtx QAction::NoRole true Connect R&otator QAction::NoRole Rotator QAction::NoRole Bandmap QAction::NoRole Rig QAction::NoRole Clock QAction::NoRole :/icons/filter_list-24px.svg:/icons/filter_list-24px.svg QSO &Filters QAction::NoRole Online Map QAction::NoRole .. &Awards QAction::NoRole .. DXCC &Submission List Generate a list of contacts to submit for ARRL DXCC award credit QAction::NoRole Edit Rules QAction::NoRole true Beep true Connect &CW Keyer CW Console QAction::NoRole .. &Wiki QAction::NoRole Report &Bug... QAction::NoRole true &Manual Entry Switch New Contact dialog to the manually entry mode<br/>(time, freq, profiles etc. are not taken from their common sources) QAction::NoRole Mailing List... QAction::NoRole Edit Chat QAction::NoRole Save Arrangement Save Arrangement QAction::NoRole true Keep Options Restore connection options after application restart Profile Image QAction::NoRole true Logbook - Search Callsign Ctrl+F Qt::ApplicationShortcut QAction::NoRole true New QSO - Add text from Callsign field to Bandmap New QSO - Add text from Callsign field to Bandmap Ctrl+M Qt::ApplicationShortcut QAction::NoRole true Rig - Band Down Ctrl+PgDown Qt::ApplicationShortcut QAction::NoRole true Rig - Band Up Ctrl+PgUp Qt::ApplicationShortcut QAction::NoRole true New QSO - Use Callsign from the Whisperer Alt+Return Qt::ApplicationShortcut QAction::NoRole true CW Console - Key Speed Up Alt+Up Qt::ApplicationShortcut QAction::NoRole true CW Console - Key Speed Down Alt+Down Qt::ApplicationShortcut QAction::NoRole true CW Console - Profile Up Alt+Right Qt::ApplicationShortcut QAction::NoRole true CW Console - Profile Down Alt+Left Qt::ApplicationShortcut QAction::NoRole true Rig - PTT On/Off Alt+\ Qt::ApplicationShortcut false QAction::NoRole true Alerts QAction::NoRole Clear QAction::NoRole Show Alerts QAction::NoRole true All Bands QAction::NoRole true Each Band QAction::NoRole true Each Band && Mode QAction::NoRole true No Check QAction::NoRole true Single QAction::NoRole true Per Band QAction::NoRole false Stop QAction::NoRole Reset QAction::NoRole true None QAction::NoRole .. Upload Service - Upload QSOs Ctrl+Shift+I QAction::NoRole true Download QSLs Service - Download QSLs Ctrl+Shift+D QAction::NoRole true true Theme: Native QAction::NoRole true Theme: QLog Light QAction::NoRole true Theme: QLog Dark QAction::NoRole What's New QAction::NoRole Export Cabrillo QAction::NoRole NewContactWidget QWidget