From c8763dd65eca9e6990255e88591ac33f63c26b77 Mon Sep 17 00:00:00 2001 From: ppc Date: Sun, 12 Jan 2025 12:48:48 +0000 Subject: [PATCH 01/38] add ICCard scanning support --- amnet-native/app.json | 7 +- amnet-native/lib/iccard-crypto.ts | 49 ++++++++++++ amnet-native/lib/iccard.ts | 119 ++++++++++++++++++++++++++++++ amnet-native/package-lock.json | 31 ++++++++ amnet-native/package.json | 2 + 5 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 amnet-native/lib/iccard-crypto.ts create mode 100644 amnet-native/lib/iccard.ts diff --git a/amnet-native/app.json b/amnet-native/app.json index 8a1c884..1aad049 100644 --- a/amnet-native/app.json +++ b/amnet-native/app.json @@ -12,7 +12,12 @@ "applinks:card.ppc.moe" ], "infoPlist": { - "UIViewControllerBasedStatusBarAppearance": "NO" + "UIViewControllerBasedStatusBarAppearance": "NO", + "NFCReaderUsageDescription": "AMNet reads IC cards' contents to use with the program", + "com.apple.developer.nfc.readersession.felica.systemcodes": ["0003", "88b4"] + }, + "entitlements": { + "com.apple.developer.nfc.readersession.formats": ["TAG", "NDEF"] }, "config": { "usesNonExemptEncryption": false diff --git a/amnet-native/lib/iccard-crypto.ts b/amnet-native/lib/iccard-crypto.ts new file mode 100644 index 0000000..d474aec --- /dev/null +++ b/amnet-native/lib/iccard-crypto.ts @@ -0,0 +1,49 @@ +import {Buffer} from "buffer"; + +const s_box_inv = [ + "243cba36e385a4d0934373b9706ec9f1100e9b2c97e70b636c2920fe86f3e1f5f69fb616047b8fabb1392a1beb5ca8ac3811125f893e7dcaec53db6d1ed281789646fff9541c287a4fd3c0dcc16af2bccb57fd4ae4f0b2c79540625241e2ad49a6b51f02c8da92e5b0c564764821de0f45584cdfa784cfd5154e27806b7ffc44714722dd300ca13be037a03523903274bf8dc2ead650bbd9c483b43168558b5df47218b7eff7982d010361cc0a8e1300e814af095175a52f1d0d658acd66073d05d84be99d997c91d1b819c32b426988c679173a4d5ba25afb253fbdf8edcee687a326a92ebe94087e67608c9c5e6fb39e06fa82aeee5977d71a9a34d456aa33", + "f9817c00b93037d590516ef0b206fbcd39145ff81bc74a82708c921deac34ec812e6b610d32f95842542a572e58f55ef86a253aeed262047de786828e445cc35bc3ebb8ec0be34afa60964017b44f5f3b13fa4b33280c94f6f40bf214c74e1115eeb3d712e9d6275d41648771367ad6b5d0787f7a89a59768b9b1ed8eeaae9992dc597f1a783fccadcbab84be88917050ba06523d1ce3603d7e0f291df0e9c2c0d66d673585cb40a4dc23bd2b7e2ac33db6027bd56432404028d7d7ff6b5f4fddd5bec79c4221fa18854e71998947e31a329e35acb6ab0cf6d936c7a08a93c1cd06350c68538c14149ab61522a9fd9181a57462b69dafaff0f15963a8afe9e0c", + "11a29ac394a051015bf7d130ee1f06d57767d3c16ee76a1aa48e9f6566d60c2dc6e469b7565a5760fc791ee11652f007d0cdca78c98bb38827f6e0c084cf2fa304008040a7fa759d4b89462228373413ff20b4da08266c8ff25d7b735c4c9071418a9ebb12e8556d2a254aafbd9519a531baadd9c5a66b83c4b6a9ccf9a1b17adcc7db2e7e7f8d4e620f0339fdb8d418c2ec6102531c3b7dbc1d59f58c98f16f9385f82c74d85e4d97ab87820a21425f0d5833443adde9fb5047c8d7819be51b1754862beff429320e7c2315ebe63c35e296683f0b43cede9cbe4f457276b510e3df92d2a84891b9acbf49b2996470eaf33e6314aeedaa24fe3dcbb009383605", + "c4f02849554af5fd75af2069c843866bc9a8c6544cdd025be89b597734d7c0515ff37ed4f19081ce198a7833cd978cd66a4b8fa480dc1a1714c5078766ad9d85b2f88d98df3e1f3f645840ac6c5c085d53baffd2a92c25abe432389af9cbc29167d0e322292d9248b40e9905a36f0a15efd5104fd8f600e6462b31578394ae03eb8e1324a11e5ed126d9a7ca366e7137eeb1fa7c7606b823be21bc9ec1da7a3b62277bb0e56318827f0feda02a9cbf11bde0880d3a795256f7b6d309161b70b342602f1ca29f72124547bbe10b018b1ddeec0c4ee9cfcc9574f4a593c7db4dfb3565fee739b5ea96c304414484fc6d30e2f268b7897d733db95a50a63c61aa2e", + "1a599cadc8e41154ed370f3ae65f3c4bb81589b1e8da697791568bdb0624cf18f8b087df8c35cb86539da4664a7a710a4838ffdc8320ce9832f6d7af705e738a14721ec129790708e94346d02fde2a4f3d2c50b375fc0b64ae317b61a530a093d2bec255ba6cf36268fdac3b95491f6bb485f7a603ec6e9a81090c6aee9e4ef0ab2d7ea1e1bbc9bc41f2fa2e1bdd2734d36004b501d640f9d50247d9d8e08f2bfbb7c5eb5716e5788df400cd8239c696bdbfe336450e261d63e7843eb69b22a3f58e236dc3997d9097102580d44ce274ccb25c33c7ea05123f51a8fef11c42cad176286f92a767a919a2445baaef587f21c08865b95d4d0d5ac413527c9f9417", + "c72b82615bd09684d34a70a19b59339fc02014532917c50bc97b97020d3a1e7c3f6b52e8753df6e40c8a4fc35f2665733123284874aaa73609b1e291045122fc08a605a4f1121c19eb4037c2a0411dd4dc07438f47afd12e98ab01baf06668acf9e769b6cb8d78871503d5dfa31a9d6aea2f944e9e42d7b83892d8bbdedd9abcb04c79f4583ee98381ffe355fd5db2ef9c6d549960da3becfa11d6c42aed4bae13bfb9068be01f7f5aad90390ff3bd466c2cf2f8fed9e6720e89be5ec6a8cff5577e8cb7e1887ac81b18db6f35d216326463a58ef7b47695867de5a95c8500c1934dfb30a2b5253410773c67712d4445560a50b3cd6280ce214924caee276ecc", + "a5b06db251551fbf3e8ddd1992ea1bbd326529a48967b1906800da0da65954f210142f45e03b23fad750ac93e643010a5c78709846a97bf395072ad374b33fa36082394e3448c01cf6c291644d3cf89d359a94e37af0f96eb912b8042d02281385728087db2bf14f26a2e1497e9ccca7b6a0d09b3677ad8b6b4a031d058a064bafe531b5d4c60c66ba83fd090fae71b76fdc41e8178e407f6230ffa88425fb16ce3744ab991eeb183a47f75f81cfed582c6cfe9e57539720ca791a5a88f5699fe7d90ebe42df56e44c22aa730b15c5eefcc7d6cbcd8ce27621e9d1ecc87dd88f617c2ebcdeb475d2c4633da15e5d6a0824c927bbef33865bd5385211f4c396c1", + "345adb2c5957122b30c2a092bfedbc45de279b96d3e6c5ebd8244ba421cca8d6ce3cbab109e0d732664a831d19ca89670f420771e9bb445e8517c4ae9c3f6b78e76d02a7fe33e3d90a7deaf118877b6203795223003e25b9dd16683b1f90a62ac629918a9def1e8476ee4f39fb111b0b93ad49b705e14dcbe838d055f2f70d8b65cdfad49ec9814e14c826b6f9aaf580f86a98ab58f3b88eb5974372a2da64c1405c13b0e5bd089f1c7e8f06be6e501a2f37a1fc102e705304206348e2fd6f7a7561b3353acf5b888251d247a5a9746c31947c9ac0ec150e28012dc3f0b4e48ddcac3d5f86c79522f4b2a35446d1778c0cffaf565d36697f994cd56073df41f6", + "04da79631ebdeae30b652501cda992ed187557308903096adcc798a450911fb10c778566ca121c67c2e01783599dd5228256a89fc66048b311fca328fb06dd5dba297a4fe8fee910cbf3937b6c6954e744a2841d8dcefffa1a879074a1f814aabcc0cf31b4d9bfd85e262dd0e43f19d68c2fab3958726edf3be63cb66288de40b28f9b7c9543d19eac9cd423e10e4b5333462013341b97f7f1c3614a6f5a217f702e554105c5d776e52715ec425c4d78358befd2eeb5beae023ad35fc424f0f9514eeb000ffdaf3ec19a528681807ef62bccb97d68f2ad99a7072c7338b06bb78e71a0f43da60d37db0a473616966d322a5be24594f5a54cc88a4964bb08c9b8" +]; + +function rotate_right(data: Uint8Array, nBytes: number, nBits: number) { + let prior = data[nBytes - 1]; + + for (let i = 0; i < nBytes; i++) { + let current = data[i]; + + data[i] = (current >> nBits) | ((prior & ((1 << nBits) - 1)) << (8 - nBits)); + prior = current; + } +} + +const S_BOX_INV = s_box_inv.map(x => new Uint8Array(Buffer.from(x, 'hex'))); +const N_TABLES = S_BOX_INV.length - 1; +const ITER_ADD = 5; + +export function performDecrypt(data: Uint8Array) { + const spad = data.map(x => S_BOX_INV[N_TABLES][x]); + + let count = (spad[15] >> 4) + 7; + let table = spad[15] + ITER_ADD * count; + + for (let z = 0; z < count; z++) { + table -= ITER_ADD; + rotate_right(spad, 15, 5); + + for (let i = 0; i < 15; i++) { + spad[i] = S_BOX_INV[table % N_TABLES][spad[i]]; + } + } + + return { + version: spad[0], + accessCodeBytes: spad.slice(6) // access code is a binary-coded decimal + }; +} diff --git a/amnet-native/lib/iccard.ts b/amnet-native/lib/iccard.ts new file mode 100644 index 0000000..88288de --- /dev/null +++ b/amnet-native/lib/iccard.ts @@ -0,0 +1,119 @@ +import {Buffer} from "buffer"; +import NfcManager, {NfcTech} from "react-native-nfc-manager"; + +import {performDecrypt} from "./iccard-crypto"; + +export enum AmusementICVendor { + Sega = 0x0078, + Konami = 0x0068, + BandaiNamcoEntertainment = 0x002A, + BandaiNamcoGames = 0x003A, + Nesica = 0x0079, + Unknown = 0x0000 +} + +type AmusementICCard = { + type: "amusement", + + idm: string, + accessCode: string, + vendor: AmusementICVendor +} + +type TransportICCard = { + type: "transport", + + idm: string +} + +export type ICCard = AmusementICCard | TransportICCard; + +/** + * Converts an IC card IDm to the 20-digit "0008" number used by some networks + * @param idm The IDm to convert + */ +export function icCard0008Number(idm: string): string { + // 0008 is the numerical representation of the idm padded with zeros until it is 20 digits + return BigInt(`0x${idm}`).toString().padStart(20, '0'); +} + +/** + * Reads a FeliCa card, returning access code information. + */ +export async function felicaCardReadAsync(): Promise { + try { + await NfcManager.requestTechnology(NfcTech.FelicaIOS, { + isReaderModeEnabled: true, + invalidateAfterFirstRead: false, + alertMessage: "Scan an Amusement or Transport card to get started" + }); + + const tag = await NfcManager.getTag(); + const idm = tag['idm'] as string; + + switch (tag['systemCode'] as string) { + case "0003": + return { + type: "transport", + idm + }; + + case "88B4": + return await readAmusementICCardContents(idm); + + default: + return null; + } + } catch (ex) { + console.warn(ex); + return null; + } finally { + await NfcManager.cancelTechnologyRequest(); + } +} + +async function readAmusementICCardContents(idm: string): Promise { + const idmBytes = Uint8Array.from(Buffer.from(idm, 'hex')); + const readCommand = [ + 0x06, // read + ...idmBytes, + 0x01, // service count (fixed) + 0x0B, 0x00, // service code (000b, le) + 0x02, // block count + 0x80, 0x00, // S_PAD0 block + 0x80, 0x82 // ID block + ]; + + let response = await NfcManager.sendFelicaCommandIOS(readCommand); + const offset = response.indexOf(0x07); // look for 0x07 as for some reason the response may not start at response[0] + + // response must have 44 bytes + if (offset === -1 || response.length - offset !== 44) { + return null; + } else if (offset !== 0) { + response = response.slice(offset); + } + + // response format: + // 0 - read response (0x07) + // 1-8 - IDm + // 9 - status flag 1 + // 10 - status flag 2 + // 11 - number of blocks (0x02) + // 12-27 - S_PAD0 block + // 28-43 - ID block (DFC is 35-36) + + if (response[9] !== 0x00 || response[11] !== 0x02) { + return null; + } + + const accessCode = performDecrypt(new Uint8Array(response.slice(12, 28))); + const dfcValue = response[37] | (response[36] << 8); + + return { + type: "amusement", + accessCode: [...accessCode.accessCodeBytes].map(x => x.toString(16).padStart(2, '0')).join(''), + vendor: dfcValue in AmusementICVendor ? dfcValue : AmusementICVendor.Unknown, + idm + }; +} diff --git a/amnet-native/package-lock.json b/amnet-native/package-lock.json index 4dd72ff..7fba1f4 100644 --- a/amnet-native/package-lock.json +++ b/amnet-native/package-lock.json @@ -28,11 +28,13 @@ "expo-system-ui": "~4.0.4", "lucide-react-native": "^0.427.0", "patch-package": "^8.0.0", + "pusher-js": "^8.2.0", "react": "18.3.1", "react-native": "0.76.5", "react-native-context-menu-view": "^1.16.0", "react-native-gesture-handler": "~2.20.2", "react-native-get-random-values": "^1.11.0", + "react-native-nfc-manager": "^3.16.1", "react-native-paper": "^5.12.5", "react-native-reanimated": "~3.16.1", "react-native-safe-area-context": "4.12.0", @@ -9733,6 +9735,15 @@ "node": ">=6" } }, + "node_modules/pusher-js": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/pusher-js/-/pusher-js-8.3.0.tgz", + "integrity": "sha512-6GohP06WlVeomAQQe9qWh1IDzd3+InluWt+ZUOcecVK1SEQkg6a8uYVsvxSJm7cbccfmHhE0jDkmhKIhue8vmA==", + "license": "MIT", + "dependencies": { + "tweetnacl": "^1.0.3" + } + }, "node_modules/qrcode-terminal": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/qrcode-terminal/-/qrcode-terminal-0.11.0.tgz", @@ -9972,6 +9983,20 @@ "react-native": ">=0.56" } }, + "node_modules/react-native-nfc-manager": { + "version": "3.16.1", + "resolved": "https://registry.npmjs.org/react-native-nfc-manager/-/react-native-nfc-manager-3.16.1.tgz", + "integrity": "sha512-rYhJSThAxAT4AVpZzZJc3X1jiTnZQlmrzk9DeeWLstUTXCoECt/75brLgn+EiXK+AfONnMnFdsSzIyruVWyIdw==", + "license": "MIT", + "peerDependencies": { + "@expo/config-plugins": "8 || 9" + }, + "peerDependenciesMeta": { + "@expo/config-plugins": { + "optional": true + } + } + }, "node_modules/react-native-paper": { "version": "5.12.5", "resolved": "https://registry.npmjs.org/react-native-paper/-/react-native-paper-5.12.5.tgz", @@ -11812,6 +11837,12 @@ "node": "*" } }, + "node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", + "license": "Unlicense" + }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", diff --git a/amnet-native/package.json b/amnet-native/package.json index a7f3e7b..481af55 100644 --- a/amnet-native/package.json +++ b/amnet-native/package.json @@ -28,11 +28,13 @@ "expo-system-ui": "~4.0.4", "lucide-react-native": "^0.427.0", "patch-package": "^8.0.0", + "pusher-js": "^8.2.0", "react": "18.3.1", "react-native": "0.76.5", "react-native-context-menu-view": "^1.16.0", "react-native-gesture-handler": "~2.20.2", "react-native-get-random-values": "^1.11.0", + "react-native-nfc-manager": "^3.16.1", "react-native-paper": "^5.12.5", "react-native-reanimated": "~3.16.1", "react-native-safe-area-context": "4.12.0", From adb60dd928b92c151ff235a1518d5d8346b6f1e7 Mon Sep 17 00:00:00 2001 From: ppc Date: Sun, 12 Jan 2025 19:44:06 +0000 Subject: [PATCH 02/38] update realm models --- amnet-native/lib/iccard.ts | 15 ++++++++---- amnet-native/models/AMCard.ts | 37 +++++++++++++++++++++++++++-- amnet-native/models/AMServer.ts | 4 ++-- amnet-native/models/RealmContext.ts | 6 ++--- 4 files changed, 50 insertions(+), 12 deletions(-) diff --git a/amnet-native/lib/iccard.ts b/amnet-native/lib/iccard.ts index 88288de..662ae55 100644 --- a/amnet-native/lib/iccard.ts +++ b/amnet-native/lib/iccard.ts @@ -3,6 +3,11 @@ import NfcManager, {NfcTech} from "react-native-nfc-manager"; import {performDecrypt} from "./iccard-crypto"; +export enum ICType { + Amusement = 1, + Transport = 2 +} + export enum AmusementICVendor { Sega = 0x0078, Konami = 0x0068, @@ -13,7 +18,7 @@ export enum AmusementICVendor { } type AmusementICCard = { - type: "amusement", + type: ICType.Amusement, idm: string, accessCode: string, @@ -21,7 +26,7 @@ type AmusementICCard = { } type TransportICCard = { - type: "transport", + type: ICType.Transport, idm: string } @@ -45,7 +50,7 @@ export async function felicaCardReadAsync(): Promise { await NfcManager.requestTechnology(NfcTech.FelicaIOS, { isReaderModeEnabled: true, invalidateAfterFirstRead: false, - alertMessage: "Scan an Amusement or Transport card to get started" + alertMessage: "Place an Amusement or Transport IC card on your device." }); const tag = await NfcManager.getTag(); @@ -54,7 +59,7 @@ export async function felicaCardReadAsync(): Promise { switch (tag['systemCode'] as string) { case "0003": return { - type: "transport", + type: ICType.Transport, idm }; @@ -111,7 +116,7 @@ async function readAmusementICCardContents(idm: string): Promise x.toString(16).padStart(2, '0')).join(''), vendor: dfcValue in AmusementICVendor ? dfcValue : AmusementICVendor.Unknown, idm diff --git a/amnet-native/models/AMCard.ts b/amnet-native/models/AMCard.ts index 978faba..c46412a 100644 --- a/amnet-native/models/AMCard.ts +++ b/amnet-native/models/AMCard.ts @@ -1,4 +1,5 @@ import {Realm} from "realm"; +import {AmusementICVendor, ICType} from "../lib/iccard"; export interface AMCardData { code: string; @@ -6,6 +7,7 @@ export interface AMCardData { added: Date; lastUsed?: Date | null; + physicalCardData?: AMPhysicalCardData | null; } export class AMCard extends Realm.Object implements AMCardData { @@ -18,6 +20,8 @@ export class AMCard extends Realm.Object implements AMCardData { lastUsed?: Date; lastUsedGameId?: string | null; + physicalCardData?: AMPhysicalCardData | null = null; + static generate(code: string, name: string) { return { _id: new Realm.BSON.ObjectId(), @@ -27,6 +31,16 @@ export class AMCard extends Realm.Object implements AMCardData { }; } + static delete(realm: Realm, card: AMCard) { + realm.write(() => { + if (card.physicalCardData) { + realm.delete(card.physicalCardData); + } + + realm.delete(card); + }); + } + static schema: Realm.ObjectSchema = { name: "card", primaryKey: "_id", @@ -36,8 +50,27 @@ export class AMCard extends Realm.Object implements AMCardData { code: "string", name: {type: "string", default: "New Card"}, added: {type: "date", default: new Date()}, - lastUsed: {type: "date", optional: true}, - lastUsedGameId: {type: "string", optional: true} + lastUsed: {type: "date", optional: true, indexed: true}, + lastUsedGameId: {type: "string", optional: true, indexed: true}, + physicalCardData: {type: "object", objectType: "physicalCard", optional: true} + } + }; +} + +export class AMPhysicalCardData extends Realm.Object { + + cardIDm: string; + cardType: ICType; + cardVendor: AmusementICVendor | null; + + static schema: Realm.ObjectSchema = { + name: "physicalCard", + primaryKey: "cardIDm", + + properties: { + cardIDm: "string", + cardType: "int", + cardVendor: {type: "int", optional: true} } }; } \ No newline at end of file diff --git a/amnet-native/models/AMServer.ts b/amnet-native/models/AMServer.ts index 08e5f33..b7b227d 100644 --- a/amnet-native/models/AMServer.ts +++ b/amnet-native/models/AMServer.ts @@ -40,9 +40,9 @@ export class AMServer extends Realm.Object implements AMServerConnecti apiVersion: {type: "int", default: 0}, gameId: {type: "string", optional: true}, serverAddress: "string", - serverName: {type: "string", default: "New Server"}, + serverName: {type: "string", default: "New Server", indexed: "full-text"}, createdAt: {type: "date", default: new Date()}, - lastUsed: {type: "date", optional: true} + lastUsed: {type: "date", optional: true, indexed: true} } }; } \ No newline at end of file diff --git a/amnet-native/models/RealmContext.ts b/amnet-native/models/RealmContext.ts index 5bd5ecf..6ecd846 100644 --- a/amnet-native/models/RealmContext.ts +++ b/amnet-native/models/RealmContext.ts @@ -1,10 +1,10 @@ import {createRealmContext} from "@realm/react"; -import {AMCard} from "./AMCard"; +import {AMCard, AMPhysicalCardData} from "./AMCard"; import {AMServer} from "./AMServer"; // @ts-ignore export default createRealmContext({ - schemaVersion: 3, - schema: [AMCard, AMServer], + schemaVersion: 4, + schema: [AMPhysicalCardData, AMCard, AMServer], deleteRealmIfMigrationNeeded: false }); \ No newline at end of file From 3e3755471d750769ef11be76f69a65a2499e531e Mon Sep 17 00:00:00 2001 From: ppc Date: Sun, 12 Jan 2025 19:44:45 +0000 Subject: [PATCH 03/38] refactor aimecard to iccard --- amnet-native/app/CardManager.tsx | 11 ++-- amnet-native/app/ServerCardIn.tsx | 21 ++++--- amnet-native/components/AimeCard.tsx | 36 ------------ amnet-native/components/ICCard.tsx | 83 ++++++++++++++++++++++++++++ 4 files changed, 99 insertions(+), 52 deletions(-) delete mode 100644 amnet-native/components/AimeCard.tsx create mode 100644 amnet-native/components/ICCard.tsx diff --git a/amnet-native/app/CardManager.tsx b/amnet-native/app/CardManager.tsx index c6f9b7e..3d06aef 100644 --- a/amnet-native/app/CardManager.tsx +++ b/amnet-native/app/CardManager.tsx @@ -5,7 +5,7 @@ import {Button, useTheme} from "react-native-paper"; import {CreditCard, Plus} from "lucide-react-native"; import ContextMenu from "react-native-context-menu-view"; -import {AimeCard} from "../components/AimeCard"; +import {ICCard} from "../components/ICCard"; import {EmptyStatePlaceholder} from "../components/EmptyStatePlaceholder"; import {AMCard} from "../models/AMCard"; @@ -21,19 +21,20 @@ function CardEntry(props: { card: AMCard, navigation: any }) { } switch (indexPath.toString()) { - // copy card + // copy card access code case "0": + case "0,0": await Clipboard.setStringAsync(props.card.code); return; // edit card case "1,0": - props.navigation.navigate('add-card', {cardId: props.card.code, cardName: props.card.name}); + props.navigation.navigate('edit-card', {cardId: props.card._id.toHexString()}); return; // remove card case "1,1": - realm.write(() => realm.delete(props.card)); + AMCard.delete(realm, props.card); return; } }, [realm, props.card, props.navigation]); @@ -50,7 +51,7 @@ function CardEntry(props: { card: AMCard, navigation: any }) { {title: "Edit Card", systemIcon: "square.and.pencil"}, {title: "Delete Card", destructive: true, systemIcon: "trash"}] }]}> - + ); } diff --git a/amnet-native/app/ServerCardIn.tsx b/amnet-native/app/ServerCardIn.tsx index f91be2a..f0b7325 100644 --- a/amnet-native/app/ServerCardIn.tsx +++ b/amnet-native/app/ServerCardIn.tsx @@ -2,7 +2,6 @@ import {BSON} from "realm"; import React, {useCallback, useEffect, useLayoutEffect, useMemo, useState} from "react"; import {ActivityIndicator, Button, Paragraph, Title, useTheme} from "react-native-paper"; import { - Alert, Platform, RefreshControl, ScrollView, @@ -28,7 +27,7 @@ import { import {EmptyStatePlaceholder} from "../components/EmptyStatePlaceholder"; import {AMUIStatusAlert} from "../components/AMUIStatusAlert"; import {AMUICardDetail} from "../components/AMUICard"; -import {AimeCard} from "../components/AimeCard"; +import {ICCard} from "../components/ICCard"; import RealmContext from "../models/RealmContext"; import {AMServer} from "../models/AMServer"; @@ -193,11 +192,11 @@ export default function ServerCardIn({route, navigation}) { {server.gameId ? `Play ${getGameName(server.gameId)}` : "Selected Card"} {selectedCard ? <> - }> + }> {serverDefaultCard?.code == selectedCard.code && } - + - - )} - ) -} diff --git a/amnet-native/app/card/CardAddModal.tsx b/amnet-native/app/card/CardAddModal.tsx new file mode 100644 index 0000000..eed0cfa --- /dev/null +++ b/amnet-native/app/card/CardAddModal.tsx @@ -0,0 +1,95 @@ +import React from "react"; +import {Platform, TouchableOpacity, View} from "react-native"; + +import {useNavigation} from "@react-navigation/native"; +import {createNativeStackNavigator} from "@react-navigation/native-stack"; + +import {Card, Paragraph, Text, Title, useTheme} from "react-native-paper"; +import {SmartphoneNfc, SquareAsterisk, SquarePlus} from "lucide-react-native"; + +import ManualCardEntry from "./ManualCardEntry"; +import PhysicalCardScan from "./PhyscialCardScan"; + +const Stack = createNativeStackNavigator(); + +function CardAddModal() { + return ( + + + + + + + ) +} + +function CardAddMethodSelector() { + const theme = useTheme(); + const navigation: any = useNavigation(); + + return ( + + + Add new Card + AMNet supports scanning physical cards, entering details + manually or generating new ones. + + + } + title="Scan Physical Card" + description="Amusement IC or Transport cards supported" + onPress={() => navigation.navigate('Scan')}/> + + + + } + title="Enter Details Manually" + //description="Manually enter card details (or generate a new one)" + onPress={() => navigation.navigate('Manual')}/> + + } + title="Generate New Card" + //description="Create a card from scratch" + onPress={() => navigation.navigate('Manual', {autoGenerate: true})}/> + + ) +} + +function MethodCard(props: { + title: string, + description?: string, + color: string, + icon: React.ReactElement, + onPress: () => void +}) { + const theme = useTheme(); + const large = props.description?.length > 0; + + return ( + props.onPress()}> + + + + {React.cloneElement(props.icon, {size: large ? 24 : 16, color: theme.colors.surfaceVariant})} + + + {props.title} + {props.description && ({props.description})} + + + + + ) +} + +export default Platform.OS === 'ios' ? CardAddModal : ManualCardEntry; diff --git a/amnet-native/app/card/CardEditorModal.tsx b/amnet-native/app/card/CardEditorModal.tsx new file mode 100644 index 0000000..19efa92 --- /dev/null +++ b/amnet-native/app/card/CardEditorModal.tsx @@ -0,0 +1,97 @@ +import {BSON} from "realm"; +import {Card, Divider, Paragraph, useTheme} from "react-native-paper"; +import {useCallback, useEffect, useLayoutEffect, useState} from "react"; +import {Platform, TextInput, TouchableOpacity, View} from "react-native"; + +import {useModalStyles} from "../../utils/ModalStyles"; + +import {AMCard} from "../../models/AMCard"; +import RealmContext from "../../models/RealmContext"; + +const {useObject, useRealm} = RealmContext; + +export default function CardEditorModal({navigation, route}) { + if (!route.params || !route.params.cardId) { + navigation.goBack(); + return; + } + + const theme = useTheme(); + const modalStyles = useModalStyles(); + + const realm = useRealm(); + const card = useObject(AMCard, BSON.ObjectId.createFromHexString(route.params.cardId)); + + const [cardName, setCardName] = useState(''); + + const updateCardCallback = useCallback((newName: string) => { + realm.write(() => { + card.name = newName; + }); + + navigation.goBack(); + }, [realm, navigation]); + + useEffect(() => { + if (card?.isValid() !== true) { + navigation.goBack(); + return; + } + + if (!cardName.length) { + setCardName(card.name); + } + }, [card]); + + // header effects + if (Platform.OS === 'ios') { + useLayoutEffect(() => { + navigation.setOptions({ + headerLeft: () => navigation.goBack()}> + Cancel + , + }); + }, [navigation, modalStyles]); + } + + useLayoutEffect(() => { + navigation.setOptions({ + headerRight: () => updateCardCallback(cardName)} disabled={!cardName.length}> + Save + + }); + }, [cardName]); + + return ( + + + + + + Card Name + + + + + + + + + Access Code + + + + + + + ) +} \ No newline at end of file diff --git a/amnet-native/app/card/ManualCardEntry.tsx b/amnet-native/app/card/ManualCardEntry.tsx new file mode 100644 index 0000000..ffdf8cb --- /dev/null +++ b/amnet-native/app/card/ManualCardEntry.tsx @@ -0,0 +1,108 @@ +import {generateCardNumber} from "amnet-shared"; +import {useCallback, useEffect, useLayoutEffect, useState} from "react"; +import {Platform, TextInput, TouchableOpacity, View} from "react-native"; +import {Button, Card, Divider, Paragraph, useTheme} from "react-native-paper"; + +import {AMCard} from "../../models/AMCard"; +import RealmContext from "../../models/RealmContext"; +import {useModalStyles} from "../../utils/ModalStyles"; + +const {useRealm} = RealmContext; + +export default function CardAddModal({navigation, route}) { + const realm = useRealm(); + const theme = useTheme(); + const modalStyles = useModalStyles(); + + // state values + const [cardName, setCardName] = useState(''); + const [cardNumber, setCardNumber] = useState(''); + + const cardAddCallback = useCallback((cardId: string, cardName: string) => { + const duplicateCard = realm.objects(AMCard.schema.name).find(x => x.code === cardId); + + if (duplicateCard) { + realm.write(() => { + duplicateCard.name = cardName; + }); + } else { + realm.write(() => { + realm.create(AMCard.schema.name, AMCard.generate(cardId, cardName)); + }); + } + + if (Platform.OS === 'ios') { + navigation.getParent()?.goBack(); + } else { + navigation.goBack(); + } + }, [realm]); + + // header effects + if (Platform.OS === 'ios') { + useLayoutEffect(() => { + navigation.setOptions({ + headerLeft: () => navigation.goBack()}> + Back + , + }); + }, [navigation, modalStyles]); + } + + useLayoutEffect(() => { + const canSave = cardName.length && cardNumber.length === 20; + const themeOptions = canSave ? modalStyles.headerText : {color: theme.colors.outline}; + + navigation.setOptions({ + headerRight: () => cardAddCallback(cardNumber, cardName)}> + Save + + }) + }, [navigation, modalStyles, cardName, cardNumber]); + + // check params for autoGenerate request + useEffect(() => { + if (route.params?.autoGenerate) { + setCardNumber(generateCardNumber()); + } + }, []); + + return ( + + + + + + Card Name + + + + + + + + + Access Code + + setCardNumber(val?.replaceAll(/\D/g, '').substring(0, 20) ?? '')} + style={{...modalStyles.formItemInput, color: theme.colors.onSurface}}/> + + + + + {/* only show card entry info when in create mode */} + + + ) +} diff --git a/amnet-native/app/card/PhyscialCardScan.tsx b/amnet-native/app/card/PhyscialCardScan.tsx new file mode 100644 index 0000000..19d235b --- /dev/null +++ b/amnet-native/app/card/PhyscialCardScan.tsx @@ -0,0 +1,90 @@ +import {useNavigation} from "@react-navigation/native"; +import {Card, Paragraph, useTheme} from "react-native-paper"; +import {useCallback, useLayoutEffect, useState} from "react"; +import {TextInput, TouchableOpacity, View} from "react-native"; + +import {useModalStyles} from "../../utils/ModalStyles"; + +import RealmContext from "../../models/RealmContext"; +import {AMCard, AMPhysicalCardData} from "../../models/AMCard"; +import {felicaCardReadAsync, icCard0008Number, ICType} from "../../lib/iccard"; + +const {useRealm} = RealmContext; + +export default function PhysicalCardScan() { + const realm = useRealm(); + const theme = useTheme(); + const navigation = useNavigation(); + const modalStyles = useModalStyles(); + + const [cardName, setCardName] = useState(''); + + const handleScan = useCallback(async () => { + const cardInfo = await felicaCardReadAsync(); + + if (!cardInfo) { + return; + } + + const accessCode = cardInfo.type === ICType.Amusement ? cardInfo.accessCode : icCard0008Number(cardInfo.idm); + + realm.write(() => { + let existingCard = realm.objects(AMCard.schema.name).find(x => x.code === accessCode); + const physicalCardInfo = realm.create(AMPhysicalCardData.schema.name, { + cardIDm: cardInfo.idm, + cardType: cardInfo.type, + cardVendor: cardInfo.type === ICType.Amusement ? cardInfo.vendor : null + }); + + if (!existingCard) { + existingCard = realm.create(AMCard.schema.name, AMCard.generate(accessCode, cardName)); + } else { + existingCard.name = cardName; + } + + // set physical card props + existingCard.added = new Date(); + existingCard.physicalCardData = physicalCardInfo; + }); + + // dismiss the parent screen (this screen can only be accessed from modal -> stack) + navigation.getParent()?.goBack(); + }, [realm, navigation, cardName]); + + useLayoutEffect(() => { + const canSave = cardName.length; + const themeOptions = canSave ? modalStyles.headerText : {color: theme.colors.outline}; + + navigation.setOptions({ + headerRight: () => handleScan()}> + Scan + + }) + }, [navigation, modalStyles, cardName]); + + return ( + + + + + + Card Name + + + + + + + + Ensure you have either an Amusement IC or Transport card ready to scan. + After the scan has completed, the card will be saved to your device. + + + ) +} \ No newline at end of file diff --git a/amnet-native/utils/ModalStyles.ts b/amnet-native/utils/ModalStyles.ts index 8b6ba2c..b3a0ac6 100644 --- a/amnet-native/utils/ModalStyles.ts +++ b/amnet-native/utils/ModalStyles.ts @@ -13,7 +13,14 @@ const modalStyles = StyleSheet.create({ }, formItemDivider: { marginVertical: 15 - } + }, + rootLayout: { + display: "flex", + gap: 10, + justifyContent: "center", + paddingHorizontal: 15, + paddingTop: 25 + }, }); export function useModalStyles() { From d52757f134dc76011c441c4a8b3c9e0abd0f812f Mon Sep 17 00:00:00 2001 From: ppc Date: Sun, 12 Jan 2025 20:06:55 +0000 Subject: [PATCH 05/38] make modal navigation headers the same --- amnet-native/app/card/ManualCardEntry.tsx | 11 ----------- amnet-native/lib/iccard.ts | 2 +- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/amnet-native/app/card/ManualCardEntry.tsx b/amnet-native/app/card/ManualCardEntry.tsx index ffdf8cb..92ad58d 100644 --- a/amnet-native/app/card/ManualCardEntry.tsx +++ b/amnet-native/app/card/ManualCardEntry.tsx @@ -38,17 +38,6 @@ export default function CardAddModal({navigation, route}) { } }, [realm]); - // header effects - if (Platform.OS === 'ios') { - useLayoutEffect(() => { - navigation.setOptions({ - headerLeft: () => navigation.goBack()}> - Back - , - }); - }, [navigation, modalStyles]); - } - useLayoutEffect(() => { const canSave = cardName.length && cardNumber.length === 20; const themeOptions = canSave ? modalStyles.headerText : {color: theme.colors.outline}; diff --git a/amnet-native/lib/iccard.ts b/amnet-native/lib/iccard.ts index 662ae55..a701b6d 100644 --- a/amnet-native/lib/iccard.ts +++ b/amnet-native/lib/iccard.ts @@ -50,7 +50,7 @@ export async function felicaCardReadAsync(): Promise { await NfcManager.requestTechnology(NfcTech.FelicaIOS, { isReaderModeEnabled: true, invalidateAfterFirstRead: false, - alertMessage: "Place an Amusement or Transport IC card on your device." + alertMessage: "Place an Amusement IC or Transport card on your device." }); const tag = await NfcManager.getTag(); From 297ad685a7110cc2816268b9a5f32afb09eb559e Mon Sep 17 00:00:00 2001 From: ppc Date: Sun, 12 Jan 2025 20:07:21 +0000 Subject: [PATCH 06/38] add comment --- amnet-native/app/card/CardAddModal.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/amnet-native/app/card/CardAddModal.tsx b/amnet-native/app/card/CardAddModal.tsx index eed0cfa..5cfaf4c 100644 --- a/amnet-native/app/card/CardAddModal.tsx +++ b/amnet-native/app/card/CardAddModal.tsx @@ -92,4 +92,5 @@ function MethodCard(props: { ) } +// the nfc library doesn't offer the same support for felica cards on android compared to ios... export default Platform.OS === 'ios' ? CardAddModal : ManualCardEntry; From 42cb2414877817a20b496e55547e0078b1b70034 Mon Sep 17 00:00:00 2001 From: ppc Date: Sun, 12 Jan 2025 20:19:46 +0000 Subject: [PATCH 07/38] add idm/access code preference for physical cards (server) --- AMNet.Server/Config.cs | 13 ++++++++++++- AMNet.Server/WebServer.cs | 6 ++++-- amnet-shared/dist/index.d.mts | 7 +++++-- amnet-shared/dist/index.d.ts | 7 +++++-- amnet-shared/dist/index.js.map | 2 +- amnet-shared/dist/index.mjs.map | 2 +- amnet-shared/src/api.ts | 6 +++++- 7 files changed, 33 insertions(+), 10 deletions(-) diff --git a/AMNet.Server/Config.cs b/AMNet.Server/Config.cs index 954fd87..506cf2c 100644 --- a/AMNet.Server/Config.cs +++ b/AMNet.Server/Config.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using Windows.Win32; using Windows.Win32.Foundation; @@ -6,21 +7,31 @@ namespace AMNet.Server; internal static class Config { + internal const string IOSection = "aimeio"; private const string ConfigFileName = @".\segatools.ini"; - internal const string IOSection = "aimeio"; + /// + /// Valid physical card identification methods. + /// IDm - uses the idm of the card to generate a 20-digit identifier (also known as the 0008 number) + /// Access - prefer the real access code of the card. if not available (i.e. Suica card), fallback to the IDm. + /// + private static readonly string[] ValidPhysicalCardIdentifiers = ["idm", "access"]; static Config() { GameId = ReadKey(IOSection, "gameId", 4); ServerName = ReadKey(IOSection, "serverName", 26, Environment.MachineName); + var physicalCardIdentifierPreference = ReadKey(IOSection, "physicalCardIdentifier", 10); + PhysicalCardIdentifierPreference = ValidPhysicalCardIdentifiers.Contains(physicalCardIdentifierPreference) ? physicalCardIdentifierPreference : ValidPhysicalCardIdentifiers[^1]; + EnableAimeTxt = PInvoke.GetPrivateProfileInt(IOSection, "enableKeyboardMode", 1, ConfigFileName) > 0; AimeTxtPath = ReadKey("aime", "aimePath", PInvoke.MAX_PATH, @"DEVICE\aime.txt"); } public static string GameId { get; } public static string ServerName { get; } + public static string PhysicalCardIdentifierPreference { get; } public static bool EnableAimeTxt { get; } public static string AimeTxtPath { get; } diff --git a/AMNet.Server/WebServer.cs b/AMNet.Server/WebServer.cs index e238f5b..18a0fc9 100644 --- a/AMNet.Server/WebServer.cs +++ b/AMNet.Server/WebServer.cs @@ -21,7 +21,8 @@ internal static partial class WebServer [property: JsonPropertyName("gameId")] string GameId, [property: JsonPropertyName("serverName")] string ServerName, [property: JsonPropertyName("sessionUptime")] long SessionUptime, - [property: JsonPropertyName("timeSinceLastPoll")] long? TimeSinceLastPoll); + [property: JsonPropertyName("timeSinceLastPoll")] long? TimeSinceLastPoll, + [property: JsonPropertyName("physicalCardIdentifierPreference")] string PhysicalCardIdentifierPreference); public record CardReadRequest( [property: JsonPropertyName("cardId")] string MatrixCode); @@ -82,7 +83,8 @@ internal static partial class WebServer Config.GameId, Config.ServerName, Environment.TickCount64 - startedAt, - lastPollAt < 0 ? null : Environment.TickCount64 - lastPollAt); + lastPollAt < 0 ? null : Environment.TickCount64 - lastPollAt, + Config.PhysicalCardIdentifierPreference); return Results.Ok(state); } diff --git a/amnet-shared/dist/index.d.mts b/amnet-shared/dist/index.d.mts index ad11302..167d216 100644 --- a/amnet-shared/dist/index.d.mts +++ b/amnet-shared/dist/index.d.mts @@ -30,10 +30,13 @@ interface AMServerInstanceMetrics { timeSinceLastPoll?: number; sessionUptime?: number; } +interface AMServerPhysicalCardPreferences { + preferredCardIdFormat?: "idm" | "access" | null; +} /** * The response from an AMNet server's info endpoint */ -type AMServerInfo = AMServerMetadata & AMServerConnectionInfo & AMServerInstanceMetrics; +type AMServerInfo = AMServerMetadata & AMServerConnectionInfo & AMServerInstanceMetrics & AMServerPhysicalCardPreferences; /** * Performs the card-in request to the specified server * @param server The server to perform the request against @@ -62,4 +65,4 @@ declare function validateCardId(id: string): boolean; */ declare function getGameName(id: string): string | null; -export { type AMCardInRequest, AMNET_API_VERSION, type AMServerConnectionInfo, type AMServerInfo, type AMServerInstanceMetrics, type AMServerMetadata, CARD_ID_REGEX, MIN_AMNET_API_VERSION, fetchServerInfo, generateCardNumber, getGameName, performCardIn, validateCardId }; +export { type AMCardInRequest, AMNET_API_VERSION, type AMServerConnectionInfo, type AMServerInfo, type AMServerInstanceMetrics, type AMServerMetadata, type AMServerPhysicalCardPreferences, CARD_ID_REGEX, MIN_AMNET_API_VERSION, fetchServerInfo, generateCardNumber, getGameName, performCardIn, validateCardId }; diff --git a/amnet-shared/dist/index.d.ts b/amnet-shared/dist/index.d.ts index ad11302..167d216 100644 --- a/amnet-shared/dist/index.d.ts +++ b/amnet-shared/dist/index.d.ts @@ -30,10 +30,13 @@ interface AMServerInstanceMetrics { timeSinceLastPoll?: number; sessionUptime?: number; } +interface AMServerPhysicalCardPreferences { + preferredCardIdFormat?: "idm" | "access" | null; +} /** * The response from an AMNet server's info endpoint */ -type AMServerInfo = AMServerMetadata & AMServerConnectionInfo & AMServerInstanceMetrics; +type AMServerInfo = AMServerMetadata & AMServerConnectionInfo & AMServerInstanceMetrics & AMServerPhysicalCardPreferences; /** * Performs the card-in request to the specified server * @param server The server to perform the request against @@ -62,4 +65,4 @@ declare function validateCardId(id: string): boolean; */ declare function getGameName(id: string): string | null; -export { type AMCardInRequest, AMNET_API_VERSION, type AMServerConnectionInfo, type AMServerInfo, type AMServerInstanceMetrics, type AMServerMetadata, CARD_ID_REGEX, MIN_AMNET_API_VERSION, fetchServerInfo, generateCardNumber, getGameName, performCardIn, validateCardId }; +export { type AMCardInRequest, AMNET_API_VERSION, type AMServerConnectionInfo, type AMServerInfo, type AMServerInstanceMetrics, type AMServerMetadata, type AMServerPhysicalCardPreferences, CARD_ID_REGEX, MIN_AMNET_API_VERSION, fetchServerInfo, generateCardNumber, getGameName, performCardIn, validateCardId }; diff --git a/amnet-shared/dist/index.js.map b/amnet-shared/dist/index.js.map index 8db7e48..9359f8f 100644 --- a/amnet-shared/dist/index.js.map +++ b/amnet-shared/dist/index.js.map @@ -1 +1 @@ -{"version":3,"sources":["../src/index.ts","../src/api.ts","../src/card.ts","../src/gameIds.ts"],"sourcesContent":["export * from './api';\nexport * from './card';\nexport * from './gameIds';","/**\n * The latest version of the AMNet API this library supports\n */\nexport const AMNET_API_VERSION = 1;\n\n/**\n * The minimum version of the AMNet API this library supports\n */\nexport const MIN_AMNET_API_VERSION = 1;\n\nexport interface AMCardInRequest {\n cardId: string;\n}\n\n/**\n * Server connection info (api version, address, etc.)\n */\nexport interface AMServerConnectionInfo {\n apiVersion: number;\n serverAddress: string;\n}\n\n/**\n * User-definable server metadata\n */\nexport interface AMServerMetadata {\n serverName: string;\n gameId?: string | null | undefined;\n}\n\n/**\n * Metrics about the current game session\n */\nexport interface AMServerInstanceMetrics {\n timeSinceLastPoll?: number;\n sessionUptime?: number;\n}\n\n/**\n * The response from an AMNet server's info endpoint\n */\nexport type AMServerInfo = AMServerMetadata & AMServerConnectionInfo & AMServerInstanceMetrics;\n\n/**\n * Performs the card-in request to the specified server\n * @param server The server to perform the request against\n * @param request The card-in request to send\n * @param signal The abort signal to use for the request\n */\nexport async function performCardIn(server: AMServerConnectionInfo, request: AMCardInRequest, signal: AbortSignal): Promise {\n if (server.apiVersion < MIN_AMNET_API_VERSION || server.apiVersion > AMNET_API_VERSION) {\n throw new Error(\"Unsupported server API version\");\n }\n \n try {\n const response = await fetch(`${new URL(server.serverAddress).origin}/amnet/signin`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\"\n },\n body: JSON.stringify(request),\n signal: signal\n });\n\n return response.ok;\n } catch {\n return false;\n }\n}\n\n/**\n * Attempts to connect to the specified server and fetch its info\n * @param serverAddress The origin address of the server to connect to\n * @param abort The abort signal to use for the request (for cancellation)\n */\nexport async function fetchServerInfo(serverAddress: string, abort: AbortSignal): Promise {\n try {\n const response = await fetch(`${new URL(serverAddress).origin}/amnet/info`, {\n signal: abort\n });\n \n if (!response.ok) {\n return null;\n }\n\n return {\n ...await response.json(),\n serverAddress: serverAddress\n } satisfies AMServerMetadata;\n } catch {\n return null;\n }\n}\n","export const CARD_ID_REGEX = /^(?:[0-9]{4} ?){5}$/i;\n\nexport function generateCardNumber(): string {\n let cardId = '';\n\n while (true) {\n // don't start with a 3\n const firstDigit = Math.floor(Math.random() * 10);\n\n if (firstDigit !== 3) {\n cardId = firstDigit.toString();\n break;\n }\n }\n\n for (let i = 0; i < 19; i++) {\n cardId += Math.floor(Math.random() * 10).toString();\n }\n\n return cardId;\n}\n\n/**\n * Validates the provided card id by known parameters\n * @param id The card id to validate\n */\nexport function validateCardId(id: string): boolean {\n return id[0] !== '3' && CARD_ID_REGEX.test(id);\n}\n","/**\n * Get the game name from the game identifier (4-letter code)\n * @param id\n */\nexport function getGameName(id: string): string | null {\n switch (id?.toUpperCase()) {\n case \"SDGT\":\n return \"Initial D The Arcade\";\n case \"SDHD\":\n case \"SDBT\":\n return \"CHUNITHM\";\n case \"SDDT\":\n return \"O.N.G.E.K.I.\";\n case \"SDEZ\":\n return \"maimai DX\";\n case \"SBZV\":\n return \"Project DIVA Future Tone\";\n \n default:\n return null;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACGO,IAAM,oBAAoB;AAK1B,IAAM,wBAAwB;AAyCrC,SAAsB,cAAc,QAAgC,SAA0B,QAAuC;AAAA;AACjI,QAAI,OAAO,aAAa,yBAAyB,OAAO,aAAa,mBAAmB;AACpF,YAAM,IAAI,MAAM,gCAAgC;AAAA,IACpD;AAEA,QAAI;AACA,YAAM,WAAW,MAAM,MAAM,GAAG,IAAI,IAAI,OAAO,aAAa,EAAE,MAAM,iBAAiB;AAAA,QACjF,QAAQ;AAAA,QACR,SAAS;AAAA,UACL,gBAAgB;AAAA,QACpB;AAAA,QACA,MAAM,KAAK,UAAU,OAAO;AAAA,QAC5B;AAAA,MACJ,CAAC;AAED,aAAO,SAAS;AAAA,IACpB,SAAQ;AACJ,aAAO;AAAA,IACX;AAAA,EACJ;AAAA;AAOA,SAAsB,gBAAgB,eAAuB,OAAkD;AAAA;AAC3G,QAAI;AACA,YAAM,WAAW,MAAM,MAAM,GAAG,IAAI,IAAI,aAAa,EAAE,MAAM,eAAe;AAAA,QACxE,QAAQ;AAAA,MACZ,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AACd,eAAO;AAAA,MACX;AAEA,aAAO,iCACA,MAAM,SAAS,KAAK,IADpB;AAAA,QAEH;AAAA,MACJ;AAAA,IACJ,SAAQ;AACJ,aAAO;AAAA,IACX;AAAA,EACJ;AAAA;;;AC5FO,IAAM,gBAAgB;AAEtB,SAAS,qBAA6B;AACzC,MAAI,SAAS;AAEb,SAAO,MAAM;AAET,UAAM,aAAa,KAAK,MAAM,KAAK,OAAO,IAAI,EAAE;AAEhD,QAAI,eAAe,GAAG;AAClB,eAAS,WAAW,SAAS;AAC7B;AAAA,IACJ;AAAA,EACJ;AAEA,WAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AACzB,cAAU,KAAK,MAAM,KAAK,OAAO,IAAI,EAAE,EAAE,SAAS;AAAA,EACtD;AAEA,SAAO;AACX;AAMO,SAAS,eAAe,IAAqB;AAChD,SAAO,GAAG,CAAC,MAAM,OAAO,cAAc,KAAK,EAAE;AACjD;;;ACxBO,SAAS,YAAY,IAA2B;AACnD,UAAQ,yBAAI,eAAe;AAAA,IACvB,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AAAA,IACL,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IAEX;AACI,aAAO;AAAA,EACf;AACJ;","names":[]} \ No newline at end of file +{"version":3,"sources":["../src/index.ts","../src/api.ts","../src/card.ts","../src/gameIds.ts"],"sourcesContent":["export * from './api';\nexport * from './card';\nexport * from './gameIds';","/**\n * The latest version of the AMNet API this library supports\n */\nexport const AMNET_API_VERSION = 1;\n\n/**\n * The minimum version of the AMNet API this library supports\n */\nexport const MIN_AMNET_API_VERSION = 1;\n\nexport interface AMCardInRequest {\n cardId: string;\n}\n\n/**\n * Server connection info (api version, address, etc.)\n */\nexport interface AMServerConnectionInfo {\n apiVersion: number;\n serverAddress: string;\n}\n\n/**\n * User-definable server metadata\n */\nexport interface AMServerMetadata {\n serverName: string;\n gameId?: string | null | undefined;\n}\n\n/**\n * Metrics about the current game session\n */\nexport interface AMServerInstanceMetrics {\n timeSinceLastPoll?: number;\n sessionUptime?: number;\n}\n\nexport interface AMServerPhysicalCardPreferences {\n preferredCardIdFormat?: \"idm\" | \"access\" | null;\n}\n\n/**\n * The response from an AMNet server's info endpoint\n */\nexport type AMServerInfo = AMServerMetadata & AMServerConnectionInfo & AMServerInstanceMetrics & AMServerPhysicalCardPreferences;\n\n/**\n * Performs the card-in request to the specified server\n * @param server The server to perform the request against\n * @param request The card-in request to send\n * @param signal The abort signal to use for the request\n */\nexport async function performCardIn(server: AMServerConnectionInfo, request: AMCardInRequest, signal: AbortSignal): Promise {\n if (server.apiVersion < MIN_AMNET_API_VERSION || server.apiVersion > AMNET_API_VERSION) {\n throw new Error(\"Unsupported server API version\");\n }\n \n try {\n const response = await fetch(`${new URL(server.serverAddress).origin}/amnet/signin`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\"\n },\n body: JSON.stringify(request),\n signal: signal\n });\n\n return response.ok;\n } catch {\n return false;\n }\n}\n\n/**\n * Attempts to connect to the specified server and fetch its info\n * @param serverAddress The origin address of the server to connect to\n * @param abort The abort signal to use for the request (for cancellation)\n */\nexport async function fetchServerInfo(serverAddress: string, abort: AbortSignal): Promise {\n try {\n const response = await fetch(`${new URL(serverAddress).origin}/amnet/info`, {\n signal: abort\n });\n \n if (!response.ok) {\n return null;\n }\n\n return {\n ...await response.json(),\n serverAddress: serverAddress\n } satisfies AMServerMetadata;\n } catch {\n return null;\n }\n}\n","export const CARD_ID_REGEX = /^(?:[0-9]{4} ?){5}$/i;\n\nexport function generateCardNumber(): string {\n let cardId = '';\n\n while (true) {\n // don't start with a 3\n const firstDigit = Math.floor(Math.random() * 10);\n\n if (firstDigit !== 3) {\n cardId = firstDigit.toString();\n break;\n }\n }\n\n for (let i = 0; i < 19; i++) {\n cardId += Math.floor(Math.random() * 10).toString();\n }\n\n return cardId;\n}\n\n/**\n * Validates the provided card id by known parameters\n * @param id The card id to validate\n */\nexport function validateCardId(id: string): boolean {\n return id[0] !== '3' && CARD_ID_REGEX.test(id);\n}\n","/**\n * Get the game name from the game identifier (4-letter code)\n * @param id\n */\nexport function getGameName(id: string): string | null {\n switch (id?.toUpperCase()) {\n case \"SDGT\":\n return \"Initial D The Arcade\";\n case \"SDHD\":\n case \"SDBT\":\n return \"CHUNITHM\";\n case \"SDDT\":\n return \"O.N.G.E.K.I.\";\n case \"SDEZ\":\n return \"maimai DX\";\n case \"SBZV\":\n return \"Project DIVA Future Tone\";\n \n default:\n return null;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACGO,IAAM,oBAAoB;AAK1B,IAAM,wBAAwB;AA6CrC,SAAsB,cAAc,QAAgC,SAA0B,QAAuC;AAAA;AACjI,QAAI,OAAO,aAAa,yBAAyB,OAAO,aAAa,mBAAmB;AACpF,YAAM,IAAI,MAAM,gCAAgC;AAAA,IACpD;AAEA,QAAI;AACA,YAAM,WAAW,MAAM,MAAM,GAAG,IAAI,IAAI,OAAO,aAAa,EAAE,MAAM,iBAAiB;AAAA,QACjF,QAAQ;AAAA,QACR,SAAS;AAAA,UACL,gBAAgB;AAAA,QACpB;AAAA,QACA,MAAM,KAAK,UAAU,OAAO;AAAA,QAC5B;AAAA,MACJ,CAAC;AAED,aAAO,SAAS;AAAA,IACpB,SAAQ;AACJ,aAAO;AAAA,IACX;AAAA,EACJ;AAAA;AAOA,SAAsB,gBAAgB,eAAuB,OAAkD;AAAA;AAC3G,QAAI;AACA,YAAM,WAAW,MAAM,MAAM,GAAG,IAAI,IAAI,aAAa,EAAE,MAAM,eAAe;AAAA,QACxE,QAAQ;AAAA,MACZ,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AACd,eAAO;AAAA,MACX;AAEA,aAAO,iCACA,MAAM,SAAS,KAAK,IADpB;AAAA,QAEH;AAAA,MACJ;AAAA,IACJ,SAAQ;AACJ,aAAO;AAAA,IACX;AAAA,EACJ;AAAA;;;AChGO,IAAM,gBAAgB;AAEtB,SAAS,qBAA6B;AACzC,MAAI,SAAS;AAEb,SAAO,MAAM;AAET,UAAM,aAAa,KAAK,MAAM,KAAK,OAAO,IAAI,EAAE;AAEhD,QAAI,eAAe,GAAG;AAClB,eAAS,WAAW,SAAS;AAC7B;AAAA,IACJ;AAAA,EACJ;AAEA,WAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AACzB,cAAU,KAAK,MAAM,KAAK,OAAO,IAAI,EAAE,EAAE,SAAS;AAAA,EACtD;AAEA,SAAO;AACX;AAMO,SAAS,eAAe,IAAqB;AAChD,SAAO,GAAG,CAAC,MAAM,OAAO,cAAc,KAAK,EAAE;AACjD;;;ACxBO,SAAS,YAAY,IAA2B;AACnD,UAAQ,yBAAI,eAAe;AAAA,IACvB,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AAAA,IACL,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IAEX;AACI,aAAO;AAAA,EACf;AACJ;","names":[]} \ No newline at end of file diff --git a/amnet-shared/dist/index.mjs.map b/amnet-shared/dist/index.mjs.map index be11df6..697ec06 100644 --- a/amnet-shared/dist/index.mjs.map +++ b/amnet-shared/dist/index.mjs.map @@ -1 +1 @@ -{"version":3,"sources":["../src/api.ts","../src/card.ts","../src/gameIds.ts"],"sourcesContent":["/**\n * The latest version of the AMNet API this library supports\n */\nexport const AMNET_API_VERSION = 1;\n\n/**\n * The minimum version of the AMNet API this library supports\n */\nexport const MIN_AMNET_API_VERSION = 1;\n\nexport interface AMCardInRequest {\n cardId: string;\n}\n\n/**\n * Server connection info (api version, address, etc.)\n */\nexport interface AMServerConnectionInfo {\n apiVersion: number;\n serverAddress: string;\n}\n\n/**\n * User-definable server metadata\n */\nexport interface AMServerMetadata {\n serverName: string;\n gameId?: string | null | undefined;\n}\n\n/**\n * Metrics about the current game session\n */\nexport interface AMServerInstanceMetrics {\n timeSinceLastPoll?: number;\n sessionUptime?: number;\n}\n\n/**\n * The response from an AMNet server's info endpoint\n */\nexport type AMServerInfo = AMServerMetadata & AMServerConnectionInfo & AMServerInstanceMetrics;\n\n/**\n * Performs the card-in request to the specified server\n * @param server The server to perform the request against\n * @param request The card-in request to send\n * @param signal The abort signal to use for the request\n */\nexport async function performCardIn(server: AMServerConnectionInfo, request: AMCardInRequest, signal: AbortSignal): Promise {\n if (server.apiVersion < MIN_AMNET_API_VERSION || server.apiVersion > AMNET_API_VERSION) {\n throw new Error(\"Unsupported server API version\");\n }\n \n try {\n const response = await fetch(`${new URL(server.serverAddress).origin}/amnet/signin`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\"\n },\n body: JSON.stringify(request),\n signal: signal\n });\n\n return response.ok;\n } catch {\n return false;\n }\n}\n\n/**\n * Attempts to connect to the specified server and fetch its info\n * @param serverAddress The origin address of the server to connect to\n * @param abort The abort signal to use for the request (for cancellation)\n */\nexport async function fetchServerInfo(serverAddress: string, abort: AbortSignal): Promise {\n try {\n const response = await fetch(`${new URL(serverAddress).origin}/amnet/info`, {\n signal: abort\n });\n \n if (!response.ok) {\n return null;\n }\n\n return {\n ...await response.json(),\n serverAddress: serverAddress\n } satisfies AMServerMetadata;\n } catch {\n return null;\n }\n}\n","export const CARD_ID_REGEX = /^(?:[0-9]{4} ?){5}$/i;\n\nexport function generateCardNumber(): string {\n let cardId = '';\n\n while (true) {\n // don't start with a 3\n const firstDigit = Math.floor(Math.random() * 10);\n\n if (firstDigit !== 3) {\n cardId = firstDigit.toString();\n break;\n }\n }\n\n for (let i = 0; i < 19; i++) {\n cardId += Math.floor(Math.random() * 10).toString();\n }\n\n return cardId;\n}\n\n/**\n * Validates the provided card id by known parameters\n * @param id The card id to validate\n */\nexport function validateCardId(id: string): boolean {\n return id[0] !== '3' && CARD_ID_REGEX.test(id);\n}\n","/**\n * Get the game name from the game identifier (4-letter code)\n * @param id\n */\nexport function getGameName(id: string): string | null {\n switch (id?.toUpperCase()) {\n case \"SDGT\":\n return \"Initial D The Arcade\";\n case \"SDHD\":\n case \"SDBT\":\n return \"CHUNITHM\";\n case \"SDDT\":\n return \"O.N.G.E.K.I.\";\n case \"SDEZ\":\n return \"maimai DX\";\n case \"SBZV\":\n return \"Project DIVA Future Tone\";\n \n default:\n return null;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGO,IAAM,oBAAoB;AAK1B,IAAM,wBAAwB;AAyCrC,SAAsB,cAAc,QAAgC,SAA0B,QAAuC;AAAA;AACjI,QAAI,OAAO,aAAa,yBAAyB,OAAO,aAAa,mBAAmB;AACpF,YAAM,IAAI,MAAM,gCAAgC;AAAA,IACpD;AAEA,QAAI;AACA,YAAM,WAAW,MAAM,MAAM,GAAG,IAAI,IAAI,OAAO,aAAa,EAAE,MAAM,iBAAiB;AAAA,QACjF,QAAQ;AAAA,QACR,SAAS;AAAA,UACL,gBAAgB;AAAA,QACpB;AAAA,QACA,MAAM,KAAK,UAAU,OAAO;AAAA,QAC5B;AAAA,MACJ,CAAC;AAED,aAAO,SAAS;AAAA,IACpB,SAAQ;AACJ,aAAO;AAAA,IACX;AAAA,EACJ;AAAA;AAOA,SAAsB,gBAAgB,eAAuB,OAAkD;AAAA;AAC3G,QAAI;AACA,YAAM,WAAW,MAAM,MAAM,GAAG,IAAI,IAAI,aAAa,EAAE,MAAM,eAAe;AAAA,QACxE,QAAQ;AAAA,MACZ,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AACd,eAAO;AAAA,MACX;AAEA,aAAO,iCACA,MAAM,SAAS,KAAK,IADpB;AAAA,QAEH;AAAA,MACJ;AAAA,IACJ,SAAQ;AACJ,aAAO;AAAA,IACX;AAAA,EACJ;AAAA;;;AC5FO,IAAM,gBAAgB;AAEtB,SAAS,qBAA6B;AACzC,MAAI,SAAS;AAEb,SAAO,MAAM;AAET,UAAM,aAAa,KAAK,MAAM,KAAK,OAAO,IAAI,EAAE;AAEhD,QAAI,eAAe,GAAG;AAClB,eAAS,WAAW,SAAS;AAC7B;AAAA,IACJ;AAAA,EACJ;AAEA,WAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AACzB,cAAU,KAAK,MAAM,KAAK,OAAO,IAAI,EAAE,EAAE,SAAS;AAAA,EACtD;AAEA,SAAO;AACX;AAMO,SAAS,eAAe,IAAqB;AAChD,SAAO,GAAG,CAAC,MAAM,OAAO,cAAc,KAAK,EAAE;AACjD;;;ACxBO,SAAS,YAAY,IAA2B;AACnD,UAAQ,yBAAI,eAAe;AAAA,IACvB,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AAAA,IACL,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IAEX;AACI,aAAO;AAAA,EACf;AACJ;","names":[]} \ No newline at end of file +{"version":3,"sources":["../src/api.ts","../src/card.ts","../src/gameIds.ts"],"sourcesContent":["/**\n * The latest version of the AMNet API this library supports\n */\nexport const AMNET_API_VERSION = 1;\n\n/**\n * The minimum version of the AMNet API this library supports\n */\nexport const MIN_AMNET_API_VERSION = 1;\n\nexport interface AMCardInRequest {\n cardId: string;\n}\n\n/**\n * Server connection info (api version, address, etc.)\n */\nexport interface AMServerConnectionInfo {\n apiVersion: number;\n serverAddress: string;\n}\n\n/**\n * User-definable server metadata\n */\nexport interface AMServerMetadata {\n serverName: string;\n gameId?: string | null | undefined;\n}\n\n/**\n * Metrics about the current game session\n */\nexport interface AMServerInstanceMetrics {\n timeSinceLastPoll?: number;\n sessionUptime?: number;\n}\n\nexport interface AMServerPhysicalCardPreferences {\n preferredCardIdFormat?: \"idm\" | \"access\" | null;\n}\n\n/**\n * The response from an AMNet server's info endpoint\n */\nexport type AMServerInfo = AMServerMetadata & AMServerConnectionInfo & AMServerInstanceMetrics & AMServerPhysicalCardPreferences;\n\n/**\n * Performs the card-in request to the specified server\n * @param server The server to perform the request against\n * @param request The card-in request to send\n * @param signal The abort signal to use for the request\n */\nexport async function performCardIn(server: AMServerConnectionInfo, request: AMCardInRequest, signal: AbortSignal): Promise {\n if (server.apiVersion < MIN_AMNET_API_VERSION || server.apiVersion > AMNET_API_VERSION) {\n throw new Error(\"Unsupported server API version\");\n }\n \n try {\n const response = await fetch(`${new URL(server.serverAddress).origin}/amnet/signin`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\"\n },\n body: JSON.stringify(request),\n signal: signal\n });\n\n return response.ok;\n } catch {\n return false;\n }\n}\n\n/**\n * Attempts to connect to the specified server and fetch its info\n * @param serverAddress The origin address of the server to connect to\n * @param abort The abort signal to use for the request (for cancellation)\n */\nexport async function fetchServerInfo(serverAddress: string, abort: AbortSignal): Promise {\n try {\n const response = await fetch(`${new URL(serverAddress).origin}/amnet/info`, {\n signal: abort\n });\n \n if (!response.ok) {\n return null;\n }\n\n return {\n ...await response.json(),\n serverAddress: serverAddress\n } satisfies AMServerMetadata;\n } catch {\n return null;\n }\n}\n","export const CARD_ID_REGEX = /^(?:[0-9]{4} ?){5}$/i;\n\nexport function generateCardNumber(): string {\n let cardId = '';\n\n while (true) {\n // don't start with a 3\n const firstDigit = Math.floor(Math.random() * 10);\n\n if (firstDigit !== 3) {\n cardId = firstDigit.toString();\n break;\n }\n }\n\n for (let i = 0; i < 19; i++) {\n cardId += Math.floor(Math.random() * 10).toString();\n }\n\n return cardId;\n}\n\n/**\n * Validates the provided card id by known parameters\n * @param id The card id to validate\n */\nexport function validateCardId(id: string): boolean {\n return id[0] !== '3' && CARD_ID_REGEX.test(id);\n}\n","/**\n * Get the game name from the game identifier (4-letter code)\n * @param id\n */\nexport function getGameName(id: string): string | null {\n switch (id?.toUpperCase()) {\n case \"SDGT\":\n return \"Initial D The Arcade\";\n case \"SDHD\":\n case \"SDBT\":\n return \"CHUNITHM\";\n case \"SDDT\":\n return \"O.N.G.E.K.I.\";\n case \"SDEZ\":\n return \"maimai DX\";\n case \"SBZV\":\n return \"Project DIVA Future Tone\";\n \n default:\n return null;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGO,IAAM,oBAAoB;AAK1B,IAAM,wBAAwB;AA6CrC,SAAsB,cAAc,QAAgC,SAA0B,QAAuC;AAAA;AACjI,QAAI,OAAO,aAAa,yBAAyB,OAAO,aAAa,mBAAmB;AACpF,YAAM,IAAI,MAAM,gCAAgC;AAAA,IACpD;AAEA,QAAI;AACA,YAAM,WAAW,MAAM,MAAM,GAAG,IAAI,IAAI,OAAO,aAAa,EAAE,MAAM,iBAAiB;AAAA,QACjF,QAAQ;AAAA,QACR,SAAS;AAAA,UACL,gBAAgB;AAAA,QACpB;AAAA,QACA,MAAM,KAAK,UAAU,OAAO;AAAA,QAC5B;AAAA,MACJ,CAAC;AAED,aAAO,SAAS;AAAA,IACpB,SAAQ;AACJ,aAAO;AAAA,IACX;AAAA,EACJ;AAAA;AAOA,SAAsB,gBAAgB,eAAuB,OAAkD;AAAA;AAC3G,QAAI;AACA,YAAM,WAAW,MAAM,MAAM,GAAG,IAAI,IAAI,aAAa,EAAE,MAAM,eAAe;AAAA,QACxE,QAAQ;AAAA,MACZ,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AACd,eAAO;AAAA,MACX;AAEA,aAAO,iCACA,MAAM,SAAS,KAAK,IADpB;AAAA,QAEH;AAAA,MACJ;AAAA,IACJ,SAAQ;AACJ,aAAO;AAAA,IACX;AAAA,EACJ;AAAA;;;AChGO,IAAM,gBAAgB;AAEtB,SAAS,qBAA6B;AACzC,MAAI,SAAS;AAEb,SAAO,MAAM;AAET,UAAM,aAAa,KAAK,MAAM,KAAK,OAAO,IAAI,EAAE;AAEhD,QAAI,eAAe,GAAG;AAClB,eAAS,WAAW,SAAS;AAC7B;AAAA,IACJ;AAAA,EACJ;AAEA,WAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AACzB,cAAU,KAAK,MAAM,KAAK,OAAO,IAAI,EAAE,EAAE,SAAS;AAAA,EACtD;AAEA,SAAO;AACX;AAMO,SAAS,eAAe,IAAqB;AAChD,SAAO,GAAG,CAAC,MAAM,OAAO,cAAc,KAAK,EAAE;AACjD;;;ACxBO,SAAS,YAAY,IAA2B;AACnD,UAAQ,yBAAI,eAAe;AAAA,IACvB,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AAAA,IACL,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IAEX;AACI,aAAO;AAAA,EACf;AACJ;","names":[]} \ No newline at end of file diff --git a/amnet-shared/src/api.ts b/amnet-shared/src/api.ts index 1b90f75..acfb88c 100644 --- a/amnet-shared/src/api.ts +++ b/amnet-shared/src/api.ts @@ -36,10 +36,14 @@ export interface AMServerInstanceMetrics { sessionUptime?: number; } +export interface AMServerPhysicalCardPreferences { + preferredCardIdFormat?: "idm" | "access" | null; +} + /** * The response from an AMNet server's info endpoint */ -export type AMServerInfo = AMServerMetadata & AMServerConnectionInfo & AMServerInstanceMetrics; +export type AMServerInfo = AMServerMetadata & AMServerConnectionInfo & AMServerInstanceMetrics & AMServerPhysicalCardPreferences; /** * Performs the card-in request to the specified server From 6c7479225c37c4a0df5b3a665ed3a3ffc458d3c5 Mon Sep 17 00:00:00 2001 From: ppc Date: Sun, 12 Jan 2025 20:42:46 +0000 Subject: [PATCH 08/38] update readme --- readme.md | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/readme.md b/readme.md index 736d994..1df1e18 100644 --- a/readme.md +++ b/readme.md @@ -9,12 +9,12 @@ An Aime Card Switcher for segatools-based games. ## Overview AMNet is designed to simulate the `DEVICE/aime.txt` file, allowing multiple users to log into a game using their phone. -This was originally intended for use with maimai and Chunithm, but should be compatible with any segatools-based game with Aime support. +This was originally intended for use with maimai and CHUNITHM, but should be compatible with any segatools-based game with Aime support. ## Setup 1. Download the latest `amnet.dll` from the releases page 2. Move `amnet.dll` to the game directory -3. Edit `segatools.ini` to use the new DLL: +3. Edit `segatools.ini`, replacing the existing [aimeio] section with this one (read all the comments and change settings as needed): ```ini [aimeio] ; To use a custom card reader IO DLL enter its path here. @@ -26,13 +26,22 @@ This was originally intended for use with maimai and Chunithm, but should be com ; serverName - the name of the server to show to users (defaults to the computer name if not set) ; serverAddress - the address to listen on, including the protocol (defaults to http://+:6070, where + is a wildcard) ; enableKeyboardMode - whether to enable hold-enter-to-login mode. Reads the file set at aimePath for card number (or DEVICE/aime.txt if not set). + ; physicalCardIdentifier - if the game is using a shared network (rinnet/aquadx.net/missless), set this to "idm" (otherwise "access"). It only affects the use of physical cards only. gameId=SDEZ ;serverName=CHUNI-PENGUIN ;serverAddress=http://+:6070 ;enableKeyboardMode=1 + ;physicalCardIdentifier=idm ``` 4. Launch the game, and check the amdaemon console for server startup messages -5. Access the using one of the interfaces (see below), add a new server `http://:6070`, add a card and start playing! +5. Access the using one of the interfaces (see below), add a new server `http://:6070`, import your existing card (or create a new one) and start playing! + +### Physical Cards +AMNet 1.3 adds support for scanning physical cards on iOS devices. If you own a physical Aime/Banapass/e-Amusement/Suica card, you can scan it using the app and use it in-game. +By changing the `physicalCardIdentifier` in segatools.ini, it is also possible to adjust the format of card numbers sent to the game: + +- `access` - This is the default and should be used if the game server is hosted locally. +- `idm` - If the game server is hosted on a shared network (rinnet/aquadx.net/missless) use this option instead (cards that have been used on a cabinet connected to the same network should load correctly). ## Interfaces AMNet can be used on most platforms, see below for options available for each platform: @@ -47,7 +56,7 @@ AMNet can be used on most platforms, see below for options available for each pl iOS TestFlight - + Aime/Banapass/e-Amusement/Suica IC cards supported Android From e0ba6c04f279e2400e8d9a322682db0a1dc7d4f9 Mon Sep 17 00:00:00 2001 From: ppc Date: Sun, 12 Jan 2025 20:53:35 +0000 Subject: [PATCH 09/38] add idm/access client-side handling --- amnet-native/app/ServerCardIn.tsx | 9 +++++++-- amnet-native/models/AMServer.ts | 8 +++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/amnet-native/app/ServerCardIn.tsx b/amnet-native/app/ServerCardIn.tsx index f0b7325..12ffead 100644 --- a/amnet-native/app/ServerCardIn.tsx +++ b/amnet-native/app/ServerCardIn.tsx @@ -32,7 +32,8 @@ import {ICCard} from "../components/ICCard"; import RealmContext from "../models/RealmContext"; import {AMServer} from "../models/AMServer"; import {AMCard} from "../models/AMCard"; -import ObjectId = BSON.ObjectId; + +import {icCard0008Number} from "../lib/iccard"; import { AMServerConnectionInfo, AMServerInstanceMetrics, @@ -41,6 +42,8 @@ import { performCardIn } from "amnet-shared"; +import ObjectId = BSON.ObjectId; + const {useRealm, useQuery, useObject} = RealmContext; export default function ServerCardIn({route, navigation}) { @@ -147,8 +150,10 @@ export default function ServerCardIn({route, navigation}) { const cardInCallback = useCallback(() => { setCardInCompleted(false); + const cardId = selectedCard.physicalCardData && server.preferredCardIdFormat === "idm" ? icCard0008Number(selectedCard.physicalCardData.cardIDm) : selectedCard.code; const abortController = new AbortController(); - performCardIn(server, {cardId: selectedCard.code}, abortController.signal).then(async r => { + + performCardIn(server, {cardId}, abortController.signal).then(async r => { const now = new Date(); realm.write(() => { server.lastUsed = now; diff --git a/amnet-native/models/AMServer.ts b/amnet-native/models/AMServer.ts index b7b227d..01d9b62 100644 --- a/amnet-native/models/AMServer.ts +++ b/amnet-native/models/AMServer.ts @@ -1,7 +1,7 @@ import {Realm} from 'realm'; -import {AMServerConnectionInfo, AMServerMetadata} from "amnet-shared"; +import {AMServerConnectionInfo, AMServerMetadata, AMServerPhysicalCardPreferences} from "amnet-shared"; -export class AMServer extends Realm.Object implements AMServerConnectionInfo, AMServerMetadata { +export class AMServer extends Realm.Object implements AMServerConnectionInfo, AMServerMetadata, AMServerPhysicalCardPreferences { _id!: Realm.BSON.ObjectId; apiVersion: number; @@ -12,7 +12,9 @@ export class AMServer extends Realm.Object implements AMServerConnecti lastUsed: Date; createdAt: Date; - + + preferredCardIdFormat?: "idm" | "access" | null; + get shareUrl() { return `http://card.ppc.moe/#/servers/add?address=${encodeURIComponent(this.serverAddress)}`; } From b2be6222713cfdedd8d00842b9724a3b1d961f9d Mon Sep 17 00:00:00 2001 From: ppc Date: Sun, 12 Jan 2025 20:53:47 +0000 Subject: [PATCH 10/38] version bump --- amnet-native/app.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amnet-native/app.json b/amnet-native/app.json index 1aad049..35377ae 100644 --- a/amnet-native/app.json +++ b/amnet-native/app.json @@ -1,7 +1,7 @@ { "name": "AMNet", "scheme": "amnet", - "version": "1.2.0", + "version": "1.3.0", "slug": "amnet-native", "icon": "./assets/icon.png", "userInterfaceStyle": "automatic", From f9b23268530726f38649502aae15e914e49c22f0 Mon Sep 17 00:00:00 2001 From: ppc Date: Sun, 12 Jan 2025 21:14:01 +0000 Subject: [PATCH 11/38] add physical card scan to server page --- amnet-native/app/ServerCardIn.tsx | 87 ++++++++++++++++---------- amnet-native/app/card/CardAddModal.tsx | 6 +- 2 files changed, 57 insertions(+), 36 deletions(-) diff --git a/amnet-native/app/ServerCardIn.tsx b/amnet-native/app/ServerCardIn.tsx index 12ffead..baa5e61 100644 --- a/amnet-native/app/ServerCardIn.tsx +++ b/amnet-native/app/ServerCardIn.tsx @@ -1,27 +1,19 @@ import {BSON} from "realm"; import React, {useCallback, useEffect, useLayoutEffect, useMemo, useState} from "react"; -import {ActivityIndicator, Button, Paragraph, Title, useTheme} from "react-native-paper"; -import { - Platform, - RefreshControl, - ScrollView, - Share, - StyleSheet, - Text, - TouchableOpacity, - View -} from "react-native"; +import {ActivityIndicator, Button, IconButton, Paragraph, Title, useTheme} from "react-native-paper"; +import {Platform, RefreshControl, ScrollView, Share, StyleSheet, Text, TouchableOpacity, View} from "react-native"; import { CircleCheck, CircleDot, CreditCard, - Nfc, Plus, SmartphoneNfcIcon, Share as ShareIcon, WifiOff, TvMinimalPlay, - Star + Star, + SmartphoneNfc, + SquareArrowOutUpRight } from "lucide-react-native"; import {EmptyStatePlaceholder} from "../components/EmptyStatePlaceholder"; @@ -33,14 +25,8 @@ import RealmContext from "../models/RealmContext"; import {AMServer} from "../models/AMServer"; import {AMCard} from "../models/AMCard"; -import {icCard0008Number} from "../lib/iccard"; -import { - AMServerConnectionInfo, - AMServerInstanceMetrics, - fetchServerInfo, - getGameName, - performCardIn -} from "amnet-shared"; +import {felicaCardReadAsync, icCard0008Number, ICType} from "../lib/iccard"; +import {AMServerConnectionInfo, AMServerInstanceMetrics, fetchServerInfo, getGameName, performCardIn} from "amnet-shared"; import ObjectId = BSON.ObjectId; @@ -153,7 +139,7 @@ export default function ServerCardIn({route, navigation}) { const cardId = selectedCard.physicalCardData && server.preferredCardIdFormat === "idm" ? icCard0008Number(selectedCard.physicalCardData.cardIDm) : selectedCard.code; const abortController = new AbortController(); - performCardIn(server, {cardId}, abortController.signal).then(async r => { + performCardIn(server, {cardId}, abortController.signal).then(r => { const now = new Date(); realm.write(() => { server.lastUsed = now; @@ -172,6 +158,30 @@ export default function ServerCardIn({route, navigation}) { return () => abortController.abort(); }, [server, selectedCard]); + const performPhysicalCardIn = useCallback(async () => { + const card = await felicaCardReadAsync(); + + if (!card) { + return; + } + + setCardInCompleted(false); + + const cardId = server.preferredCardIdFormat === "idm" || card.type !== ICType.Amusement ? icCard0008Number(card.idm) : card.accessCode; + const abortController = new AbortController(); + + performCardIn(server, {cardId}, abortController.signal).then(r => { + setCardInCompleted(r); + }).catch(() => { + setCardInCompleted(false); + }).finally(() => { + setTimeout(() => setCardInCompleted(null), 3000); + }); + + setTimeout(() => abortController.abort(), 5000); + return () => abortController.abort(); + }, [server]); + const performRefresh = () => { setServerRefreshing(true); setServerReloadCounter(serverReloadCounter + 1); @@ -191,7 +201,8 @@ export default function ServerCardIn({route, navigation}) { } return ( - }> + }> @@ -202,15 +213,27 @@ export default function ServerCardIn({route, navigation}) { actionIcon={}> {serverDefaultCard?.code == selectedCard.code && } - + + + {Platform.OS === 'ios' && ( + }/> + )} + : }/> diff --git a/amnet-native/app/card/CardAddModal.tsx b/amnet-native/app/card/CardAddModal.tsx index 5cfaf4c..7bdc2d7 100644 --- a/amnet-native/app/card/CardAddModal.tsx +++ b/amnet-native/app/card/CardAddModal.tsx @@ -35,7 +35,7 @@ function CardAddMethodSelector() { manually or generating new ones. - } title="Scan Physical Card" description="Amusement IC or Transport cards supported" @@ -46,13 +46,11 @@ function CardAddMethodSelector() { } title="Enter Details Manually" - //description="Manually enter card details (or generate a new one)" onPress={() => navigation.navigate('Manual')}/> - } title="Generate New Card" - //description="Create a card from scratch" onPress={() => navigation.navigate('Manual', {autoGenerate: true})}/> ) From 690a5b591c950c241aa4da831c53d590db96027b Mon Sep 17 00:00:00 2001 From: ppc Date: Sun, 12 Jan 2025 21:17:29 +0000 Subject: [PATCH 12/38] better NFCReaderUsageDescription --- amnet-native/app.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amnet-native/app.json b/amnet-native/app.json index 35377ae..4d50b2d 100644 --- a/amnet-native/app.json +++ b/amnet-native/app.json @@ -13,7 +13,7 @@ ], "infoPlist": { "UIViewControllerBasedStatusBarAppearance": "NO", - "NFCReaderUsageDescription": "AMNet reads IC cards' contents to use with the program", + "NFCReaderUsageDescription": "AMNet uses NFC to scan Physical IC Cards", "com.apple.developer.nfc.readersession.felica.systemcodes": ["0003", "88b4"] }, "entitlements": { From aae50396609cb4800d74a75e0e97a1fe6dbe9832 Mon Sep 17 00:00:00 2001 From: ppc Date: Sun, 12 Jan 2025 21:24:00 +0000 Subject: [PATCH 13/38] entitlement fix --- amnet-native/app.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amnet-native/app.json b/amnet-native/app.json index 4d50b2d..ea3cbe2 100644 --- a/amnet-native/app.json +++ b/amnet-native/app.json @@ -17,7 +17,7 @@ "com.apple.developer.nfc.readersession.felica.systemcodes": ["0003", "88b4"] }, "entitlements": { - "com.apple.developer.nfc.readersession.formats": ["TAG", "NDEF"] + "com.apple.developer.nfc.readersession.formats": ["TAG"] }, "config": { "usesNonExemptEncryption": false From 89338c794aee6f994c03d49108beaf44f0d1642c Mon Sep 17 00:00:00 2001 From: ppc Date: Mon, 13 Jan 2025 20:16:11 +0000 Subject: [PATCH 14/38] additional nfc support checks --- amnet-native/App.tsx | 2 +- amnet-native/app/ServerCardIn.tsx | 30 ++++++++++++----- amnet-native/app/card/CardAddModal.tsx | 46 +++++++++++++++++--------- 3 files changed, 53 insertions(+), 25 deletions(-) diff --git a/amnet-native/App.tsx b/amnet-native/App.tsx index e3a77ea..c5b16bc 100644 --- a/amnet-native/App.tsx +++ b/amnet-native/App.tsx @@ -121,7 +121,7 @@ export default function AppRoot() { - + diff --git a/amnet-native/app/ServerCardIn.tsx b/amnet-native/app/ServerCardIn.tsx index baa5e61..f60b69e 100644 --- a/amnet-native/app/ServerCardIn.tsx +++ b/amnet-native/app/ServerCardIn.tsx @@ -1,4 +1,5 @@ import {BSON} from "realm"; +import NfcManager from "react-native-nfc-manager"; import React, {useCallback, useEffect, useLayoutEffect, useMemo, useState} from "react"; import {ActivityIndicator, Button, IconButton, Paragraph, Title, useTheme} from "react-native-paper"; import {Platform, RefreshControl, ScrollView, Share, StyleSheet, Text, TouchableOpacity, View} from "react-native"; @@ -43,6 +44,16 @@ export default function ServerCardIn({route, navigation}) { query: q => q.sorted("lastUsed", true) }); + // nfc support + const [nfcSupported, setNfcSupported] = useState(Platform.OS === 'android' ? false : null); + useEffect(() => { + if (nfcSupported !== null) { + return; + } + + NfcManager.isSupported().then(setNfcSupported); + }, []); + // server state const [serverRefreshing, setServerRefreshing] = useState(false); const [serverReloadCounter, setServerReloadCounter] = useState(0); @@ -201,12 +212,12 @@ export default function ServerCardIn({route, navigation}) { } return ( - }> + }> - {server.gameId ? `Play ${getGameName(server.gameId)}` : "Selected Card"} + {server.gameId ? `Play ${getGameName(server.gameId)}` : "Selected Card"} {selectedCard ? <> } - - {Platform.OS === 'ios' && ( + {nfcSupported === true && ( } onPress={() => navigation.navigate('add-card')}> - Add card + Add Card - } + + } {cards.length > 0 && ( @@ -251,8 +263,8 @@ export default function ServerCardIn({route, navigation}) { diff --git a/amnet-native/app/card/CardAddModal.tsx b/amnet-native/app/card/CardAddModal.tsx index 7bdc2d7..4bef1e3 100644 --- a/amnet-native/app/card/CardAddModal.tsx +++ b/amnet-native/app/card/CardAddModal.tsx @@ -1,7 +1,6 @@ -import React from "react"; +import NfcManager from "react-native-nfc-manager"; import {Platform, TouchableOpacity, View} from "react-native"; - -import {useNavigation} from "@react-navigation/native"; +import React, {useEffect, useLayoutEffect, useState} from "react"; import {createNativeStackNavigator} from "@react-navigation/native-stack"; import {Card, Paragraph, Text, Title, useTheme} from "react-native-paper"; @@ -23,25 +22,41 @@ function CardAddModal() { ) } -function CardAddMethodSelector() { +function CardAddMethodSelector({navigation}) { const theme = useTheme(); - const navigation: any = useNavigation(); + const [nfcSupported, setNfcSupported] = useState(null); + + useLayoutEffect(() => { + navigation.setOptions({ + headerShown: false + }); + }, []); + + useEffect(() => { + NfcManager.isSupported().then(setNfcSupported); + }, []); return ( Add new Card - AMNet supports scanning physical cards, entering details - manually or generating new ones. + + AMNet supports scanning physical cards, entering details manually or generating new ones. + - } - title="Scan Physical Card" - description="Amusement IC or Transport cards supported" - onPress={() => navigation.navigate('Scan')}/> + {nfcSupported !== null && ( + <> + } + disabled={!nfcSupported} + title="Scan Physical Card" + description={nfcSupported ? "Amusement IC or Transport cards supported" : "NFC is not supported on this device"} + onPress={() => navigation.navigate('Scan')}/> - + + + )} } @@ -60,6 +75,7 @@ function MethodCard(props: { title: string, description?: string, color: string, + disabled?: boolean, icon: React.ReactElement, onPress: () => void }) { @@ -67,8 +83,8 @@ function MethodCard(props: { const large = props.description?.length > 0; return ( - props.onPress()}> - + !props.disabled && props.onPress()} disabled={props.disabled}> + Date: Mon, 13 Jan 2025 21:17:06 +0000 Subject: [PATCH 15/38] change transport ic icon to rail symbol --- amnet-native/components/ICCard.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/amnet-native/components/ICCard.tsx b/amnet-native/components/ICCard.tsx index 500ac81..32eb260 100644 --- a/amnet-native/components/ICCard.tsx +++ b/amnet-native/components/ICCard.tsx @@ -1,6 +1,6 @@ import {Text} from "react-native"; import {ReactNode, useMemo} from "react"; -import {CreditCard, History, Joystick, Keyboard, KeyRound, Tickets} from "lucide-react-native"; +import {CreditCard, History, Joystick, RailSymbol} from "lucide-react-native"; import TimeAgo from "./TimeAgo"; import {AMUICard, AMUICardActionProps, AMUICardDetail} from "./AMUICard"; @@ -44,7 +44,7 @@ function PhysicalICInfo(props: { physicalCard: AMPhysicalCardData | null }) { if (props.physicalCard?.cardType === ICType.Transport) { return ( - + Transport IC Card ) @@ -65,17 +65,17 @@ function PhysicalICInfo(props: { physicalCard: AMPhysicalCardData | null }) { function ICVendorName(vendor: AmusementICVendor): string { switch (vendor) { case AmusementICVendor.Sega: - return "Aime Card"; + return "AiMe Card"; case AmusementICVendor.Konami: return "e-Amusement Pass"; case AmusementICVendor.BandaiNamcoGames: case AmusementICVendor.BandaiNamcoEntertainment: - return "Bana Passport"; + return "Banapass"; case AmusementICVendor.Nesica: - return "Nesica Card"; + return "NESiCA Card"; default: return "Amusement IC Card"; From 0a80da6614dab2fbaea3b26cc87265fa76d17843 Mon Sep 17 00:00:00 2001 From: ppc Date: Mon, 13 Jan 2025 21:17:26 +0000 Subject: [PATCH 16/38] improve ux when scanning already imported physical card --- amnet-native/app/card/PhyscialCardScan.tsx | 24 ++++++++-------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/amnet-native/app/card/PhyscialCardScan.tsx b/amnet-native/app/card/PhyscialCardScan.tsx index 19d235b..67fadb8 100644 --- a/amnet-native/app/card/PhyscialCardScan.tsx +++ b/amnet-native/app/card/PhyscialCardScan.tsx @@ -26,25 +26,19 @@ export default function PhysicalCardScan() { return; } - const accessCode = cardInfo.type === ICType.Amusement ? cardInfo.accessCode : icCard0008Number(cardInfo.idm); - realm.write(() => { - let existingCard = realm.objects(AMCard.schema.name).find(x => x.code === accessCode); - const physicalCardInfo = realm.create(AMPhysicalCardData.schema.name, { + // check if the card has already been registered (convert a manually entered card to a physically scanned card) + const accessCode = cardInfo.type === ICType.Amusement ? cardInfo.accessCode : icCard0008Number(cardInfo.idm); + const cardEntity = realm.objects(AMCard.schema.name).find(x => x.code === accessCode) ?? realm.create(AMCard.schema.name, AMCard.generate(accessCode, cardName)); + + // set physical card props + cardEntity.name = cardName; + cardEntity.added = new Date(); + cardEntity.physicalCardData ??= realm.objectForPrimaryKey(AMPhysicalCardData.schema.name, cardInfo.idm) ?? realm.create(AMPhysicalCardData.schema.name, { cardIDm: cardInfo.idm, cardType: cardInfo.type, cardVendor: cardInfo.type === ICType.Amusement ? cardInfo.vendor : null }); - - if (!existingCard) { - existingCard = realm.create(AMCard.schema.name, AMCard.generate(accessCode, cardName)); - } else { - existingCard.name = cardName; - } - - // set physical card props - existingCard.added = new Date(); - existingCard.physicalCardData = physicalCardInfo; }); // dismiss the parent screen (this screen can only be accessed from modal -> stack) @@ -81,7 +75,7 @@ export default function PhysicalCardScan() { - + Ensure you have either an Amusement IC or Transport card ready to scan. After the scan has completed, the card will be saved to your device. From 9c8c4fd286de3b40d23a42d590868c47034c78fa Mon Sep 17 00:00:00 2001 From: ppc Date: Mon, 13 Jan 2025 21:17:38 +0000 Subject: [PATCH 17/38] improve HTTPS server warning --- amnet-native/app/ServerAddModal.tsx | 41 ++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/amnet-native/app/ServerAddModal.tsx b/amnet-native/app/ServerAddModal.tsx index f772c35..f15a3dc 100644 --- a/amnet-native/app/ServerAddModal.tsx +++ b/amnet-native/app/ServerAddModal.tsx @@ -1,7 +1,7 @@ import {useModalStyles} from "../utils/ModalStyles"; import {Card, Paragraph, useTheme} from "react-native-paper"; import {useCallback, useEffect, useLayoutEffect, useState} from "react"; -import {Platform, TextInput, TouchableOpacity, View} from "react-native"; +import {Alert, Platform, TextInput, TouchableOpacity, View} from "react-native"; import RealmContext from "../models/RealmContext"; import {AMServer} from "../models/AMServer"; @@ -34,25 +34,48 @@ export default function ServerAddModal({route, navigation}) { let urlValid = false; try { - new URL(serverAddress); - urlValid = true; + const protocol = new URL(serverAddress).protocol; + urlValid = protocol.localeCompare("http:", undefined, {sensitivity: "case"}) === 0 || protocol.localeCompare("https:", undefined, {sensitivity: "case"}) === 0; } catch { // do nothing } const themeOptions = urlValid ? modalStyles.headerText : {color: theme.colors.outline}; navigation.setOptions({ - headerRight: () => saveServer()}> + headerRight: () => saveServer(serverAddress)}> Save }) }, [navigation, modalStyles, serverAddress]); - const saveServer = useCallback(() => { - let serverObject = realm.objects("server").find(x => x.serverAddress == serverAddress); + const saveServer = useCallback(async (address: string) => { + if (address.startsWith("https:")) { + await new Promise(done => { + Alert.alert( + "HTTPS Warning", + "By default, AMNet doesn't support HTTPS without advanced configuration. It's highly likely you won't be able to connect.", + [ + { + text: "Keep HTTPS", + style: "destructive", + onPress: () => done() + }, + { + text: "Change to HTTP", + isPreferred: true, + onPress: () => { + address = address.replace(/^https:/i, "http:"); + done(); + } + }, + ]); + }) + } + + let serverObject = realm.objects("server").find(x => x.serverAddress == address); if (!serverObject) { realm.write(() => { - serverObject = realm.create("server", AMServer.generate(serverAddress)); + serverObject = realm.create("server", AMServer.generate(address)); }); } @@ -60,7 +83,7 @@ export default function ServerAddModal({route, navigation}) { setTimeout(() => navigation.navigate('server', { serverId: serverObject._id.toHexString() }), 500); - }, [serverAddress, realm]); + }, [realm]); return ( @@ -85,7 +108,7 @@ export default function ServerAddModal({route, navigation}) { - If you're using HTTPS, ensure a trusted certificate has been configured via HTTP.SYS + AMNet uses HTTP by default. Unless has been configured to work with HTTP.SYS, HTTPS will not work. ) } From b33670d849505e0ad1b8a5da76e6f7152694f154 Mon Sep 17 00:00:00 2001 From: ppc Date: Mon, 13 Jan 2025 21:17:51 +0000 Subject: [PATCH 18/38] move modal options to container --- amnet-native/App.tsx | 4 ++-- amnet-native/app/card/CardAddModal.tsx | 9 ++------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/amnet-native/App.tsx b/amnet-native/App.tsx index c5b16bc..df1e7b6 100644 --- a/amnet-native/App.tsx +++ b/amnet-native/App.tsx @@ -1,7 +1,7 @@ import React from "react"; import {StatusBar} from "expo-status-bar"; -import {useColorScheme, View} from "react-native"; import {Server, WalletCards} from "lucide-react-native"; +import {Platform, useColorScheme, View} from "react-native"; import {NavigationContainer} from "@react-navigation/native"; import {SafeAreaProvider} from "react-native-safe-area-context"; import {MD3Colors} from "react-native-paper/lib/typescript/types"; @@ -121,7 +121,7 @@ export default function AppRoot() { - + diff --git a/amnet-native/app/card/CardAddModal.tsx b/amnet-native/app/card/CardAddModal.tsx index 4bef1e3..482fb24 100644 --- a/amnet-native/app/card/CardAddModal.tsx +++ b/amnet-native/app/card/CardAddModal.tsx @@ -1,6 +1,7 @@ import NfcManager from "react-native-nfc-manager"; + +import React, {useEffect, useState} from "react"; import {Platform, TouchableOpacity, View} from "react-native"; -import React, {useEffect, useLayoutEffect, useState} from "react"; import {createNativeStackNavigator} from "@react-navigation/native-stack"; import {Card, Paragraph, Text, Title, useTheme} from "react-native-paper"; @@ -26,12 +27,6 @@ function CardAddMethodSelector({navigation}) { const theme = useTheme(); const [nfcSupported, setNfcSupported] = useState(null); - useLayoutEffect(() => { - navigation.setOptions({ - headerShown: false - }); - }, []); - useEffect(() => { NfcManager.isSupported().then(setNfcSupported); }, []); From 31c56d2449bc47732289bc713b8e0c9befcdeca4 Mon Sep 17 00:00:00 2001 From: ppc Date: Tue, 14 Jan 2025 15:46:35 +0000 Subject: [PATCH 19/38] update server to implement felica polling --- AMNet.Server/AMNet.Server.csproj | 1 - AMNet.Server/CardPresenter.cs | 27 ++++++++++++---- AMNet.Server/Config.cs | 29 ++++++++++------- AMNet.Server/DllMain.cs | 37 +++++++++++++-------- AMNet.Server/WebServer.cs | 55 +++++++++++++++++++------------- 5 files changed, 93 insertions(+), 56 deletions(-) diff --git a/AMNet.Server/AMNet.Server.csproj b/AMNet.Server/AMNet.Server.csproj index 8b6259d..1ee4c27 100644 --- a/AMNet.Server/AMNet.Server.csproj +++ b/AMNet.Server/AMNet.Server.csproj @@ -3,7 +3,6 @@ true Library - false net9.0-windows Size true diff --git a/AMNet.Server/CardPresenter.cs b/AMNet.Server/CardPresenter.cs index 2d053a4..b1c944d 100644 --- a/AMNet.Server/CardPresenter.cs +++ b/AMNet.Server/CardPresenter.cs @@ -13,11 +13,11 @@ internal class CardPresenter private readonly Lock _cardLock = new(); private StoredCard _currentCard; - public record StoredCard(byte[] Value, string OriginalValue, long ExpiresAt) + public record StoredCard(byte[] AccessCode, byte[] IDm, long ExpiresAt) { public bool Expired => Environment.TickCount64 >= ExpiresAt; - public override string ToString() => string.Join(" ", Enumerable.Range(0, 5).Select(x => OriginalValue.Substring(x * 4, 4))); + public override string ToString() => FormatHexCode(AccessCode); } /// @@ -31,10 +31,16 @@ internal class CardPresenter } } - public StoredCard TakeActiveCard() + public StoredCard TakeActiveCard(bool? requireIDm = null) { lock (_cardLock) { + // IDm and requireIDm are mutually exclusive + if (_currentCard == null || (requireIDm.HasValue && (_currentCard.IDm == null) ^ requireIDm.Value)) + { + return null; + } + var card = _currentCard; _currentCard = null; @@ -45,13 +51,14 @@ internal class CardPresenter /// /// Sets the card /// - /// The card id to set + /// The access code of the card to use + /// The FeliCa card IDm, if a physical card was used /// How long the card should be presented to the game for in milliseconds /// Whether the card was set successfully - public StoredCard SetCard(string cardId, long validFor = 5000) + public StoredCard SetCard(string accessCode, string idmHex, long validFor = 5000) { // ensure the matrix code is 20-digits long, otherwise pad with zeros - var matrixCode = cardId.Replace(" ", "").PadLeft(20, '0'); + var matrixCode = accessCode.Replace(" ", "").PadLeft(20, '0'); var bytes = new byte[10]; for (var i = 0; i < bytes.Length; i++) @@ -67,7 +74,8 @@ internal class CardPresenter } } - var card = new StoredCard(bytes, matrixCode, Environment.TickCount64 + validFor); + var idmBytes = !string.IsNullOrWhiteSpace(idmHex) && idmHex.Length <= 16 && idmHex.All(char.IsAsciiHexDigit) ? Convert.FromHexString(idmHex.PadLeft(16, '0')) : null; + var card = new StoredCard(bytes, idmBytes, Environment.TickCount64 + validFor); lock (_cardLock) { @@ -76,4 +84,9 @@ internal class CardPresenter return card; } + + /// + /// Formats a byte array as a hex string, inserting a space every 2 characters + /// + public static string FormatHexCode(byte[] code) => string.Join(" ", code.Chunk(2).Select(Convert.ToHexString)); } \ No newline at end of file diff --git a/AMNet.Server/Config.cs b/AMNet.Server/Config.cs index 506cf2c..8951085 100644 --- a/AMNet.Server/Config.cs +++ b/AMNet.Server/Config.cs @@ -1,5 +1,4 @@ using System; -using System.Linq; using Windows.Win32; using Windows.Win32.Foundation; @@ -10,30 +9,36 @@ internal static class Config internal const string IOSection = "aimeio"; private const string ConfigFileName = @".\segatools.ini"; - /// - /// Valid physical card identification methods. - /// IDm - uses the idm of the card to generate a 20-digit identifier (also known as the 0008 number) - /// Access - prefer the real access code of the card. if not available (i.e. Suica card), fallback to the IDm. - /// - private static readonly string[] ValidPhysicalCardIdentifiers = ["idm", "access"]; - static Config() { GameId = ReadKey(IOSection, "gameId", 4); ServerName = ReadKey(IOSection, "serverName", 26, Environment.MachineName); - - var physicalCardIdentifierPreference = ReadKey(IOSection, "physicalCardIdentifier", 10); - PhysicalCardIdentifierPreference = ValidPhysicalCardIdentifiers.Contains(physicalCardIdentifierPreference) ? physicalCardIdentifierPreference : ValidPhysicalCardIdentifiers[^1]; + PhysicalCardUseIDm = PInvoke.GetPrivateProfileInt(IOSection, "usePhysicalCardIdm", 0, ConfigFileName) > 0; EnableAimeTxt = PInvoke.GetPrivateProfileInt(IOSection, "enableKeyboardMode", 1, ConfigFileName) > 0; AimeTxtPath = ReadKey("aime", "aimePath", PInvoke.MAX_PATH, @"DEVICE\aime.txt"); } public static string GameId { get; } + + /// + /// The server name to show on devices. Defaults to the computer name. + /// public static string ServerName { get; } - public static string PhysicalCardIdentifierPreference { get; } + + /// + /// Whether physically scanned cards should have their IDm forwarded to the game instead of the access code. + /// + public static bool PhysicalCardUseIDm { get; } + /// + /// Whether holding the enter key should trigger a card scan using the code from the aime.txt file + /// public static bool EnableAimeTxt { get; } + + /// + /// Path to the aime.txt file. Defaults to .\DEVICE\aime.txt + /// public static string AimeTxtPath { get; } internal static unsafe string ReadKey(string section, string key, uint maxLength, string @default = null) diff --git a/AMNet.Server/DllMain.cs b/AMNet.Server/DllMain.cs index e951608..cda2438 100644 --- a/AMNet.Server/DllMain.cs +++ b/AMNet.Server/DllMain.cs @@ -36,7 +36,7 @@ public static class DllMain CancellationTokenRegistration? cancellationRegistration = null; cancellationRegistration = App.Lifetime.ApplicationStarted.Register(() => { - App.Logger.LogInformation("AMNet Server ({gameId}) started successfully.", Config.GameId); + App.Logger.LogInformation("AMNet Server ({gameId}) started successfully.", Config.GameId ?? "SXXX"); App.Logger.LogInformation("Visit {addr} from a mobile device on the same network to get started.", WebAddress); // ReSharper disable once AccessToModifiedClosure @@ -71,7 +71,7 @@ public static class DllMain using var aimeTxtReader = new StreamReader(File.OpenRead(Config.AimeTxtPath), Encoding.UTF8); var fileName = Path.GetFileName(Config.AimeTxtPath); - var cardResult = App.Services.GetRequiredService().SetCard(aimeTxtReader.ReadLine()); + var cardResult = App.Services.GetRequiredService().SetCard(aimeTxtReader.ReadLine(), null); if (cardResult == null) { @@ -98,25 +98,36 @@ public static class DllMain return 1; } - var card = App.Services.GetRequiredService().TakeActiveCard(); + // if physical cards are being directed to + var card = App.Services.GetRequiredService().TakeActiveCard(Config.PhysicalCardUseIDm ? false : null); if (card?.Expired != false) { return 1; } - try - { - Marshal.Copy(card.Value, 0, luid, (int)luidSize); - return 0; - } - finally - { - App.Logger.LogInformation("Card read in: {cardId}", card); - } + Marshal.Copy(card.AccessCode, 0, luid, (int)luidSize); + App.Logger.LogInformation("Access Code read: {cardId}", CardPresenter.FormatHexCode(card.AccessCode)); + return 0; } [UnmanagedCallersOnly(EntryPoint = "aime_io_nfc_get_felica_id")] - public static unsafe int GetFelicaId(byte unitNo, ulong* idm) => 1; // felica not supported + public static int GetFelicaId(byte unitNo, IntPtr idm) + { + if (!Config.PhysicalCardUseIDm || unitNo != 0) + { + return 1; + } + + var card = App.Services.GetRequiredService().TakeActiveCard(true); + if (card?.Expired != false) + { + return 1; + } + + Marshal.Copy(card.IDm, 0, idm, sizeof(ulong)); + App.Logger.LogInformation("FeliCa IDm read: {idm}", card.IDm); + return 0; + } [UnmanagedCallersOnly(EntryPoint = "aime_io_led_set_color")] public static void SetLedColour(byte unitNo, byte r, byte g, byte b) diff --git a/AMNet.Server/WebServer.cs b/AMNet.Server/WebServer.cs index 18a0fc9..0e3d127 100644 --- a/AMNet.Server/WebServer.cs +++ b/AMNet.Server/WebServer.cs @@ -1,5 +1,6 @@ using System; using System.Globalization; +using System.Linq; using System.Text.Json; using System.Text.Json.Serialization; using System.Threading; @@ -21,11 +22,14 @@ internal static partial class WebServer [property: JsonPropertyName("gameId")] string GameId, [property: JsonPropertyName("serverName")] string ServerName, [property: JsonPropertyName("sessionUptime")] long SessionUptime, - [property: JsonPropertyName("timeSinceLastPoll")] long? TimeSinceLastPoll, - [property: JsonPropertyName("physicalCardIdentifierPreference")] string PhysicalCardIdentifierPreference); + [property: JsonPropertyName("timeSinceLastPoll")] long? TimeSinceLastPoll); public record CardReadRequest( - [property: JsonPropertyName("cardId")] string MatrixCode); + [property: JsonPropertyName("cardId")] string AccessCode, + [property: JsonPropertyName("physicalCardIDm")] string PhysicalCardIDm); + + public record ServerErrorMessage( + [property: JsonPropertyName("message")] string Message); public static WebApplication BuildServer(params string[] listenAddresses) { @@ -83,8 +87,7 @@ internal static partial class WebServer Config.GameId, Config.ServerName, Environment.TickCount64 - startedAt, - lastPollAt < 0 ? null : Environment.TickCount64 - lastPollAt, - Config.PhysicalCardIdentifierPreference); + lastPollAt < 0 ? null : Environment.TickCount64 - lastPollAt); return Results.Ok(state); } @@ -99,23 +102,25 @@ internal static partial class WebServer } catch (JsonException e) { - ctx.Response.StatusCode = 400; - await ctx.Response.WriteAsync(e.Message); - + await WriteErrorResponse(ctx, StatusCodes.Status400BadRequest, e.Message); return; } - if (string.IsNullOrEmpty(request?.MatrixCode)) + if (string.IsNullOrEmpty(request?.AccessCode)) { - ctx.Response.StatusCode = 400; - await ctx.Response.WriteAsync("Card id not provided."); + await WriteErrorResponse(ctx, StatusCodes.Status400BadRequest, "Access Code not provided."); return; } - if (request.MatrixCode.Length is 0 or < 20) + if (request.AccessCode.Length is 0 or < 20 || !request.AccessCode.Any(char.IsAsciiDigit)) { - ctx.Response.StatusCode = 422; - await ctx.Response.WriteAsync("Invalid card id length."); + await WriteErrorResponse(ctx, StatusCodes.Status422UnprocessableEntity, "Invalid Access Code."); + return; + } + + if (request.AccessCode.All(x => x == '0')) + { + await WriteErrorResponse(ctx, StatusCodes.Status403Forbidden, "All-zero access codes are forbidden."); return; } @@ -124,26 +129,30 @@ internal static partial class WebServer if (currentCard?.Expired == false) { - var timeLeft = Math.Ceiling(TimeSpan.FromMilliseconds(currentCard.ExpiresAt - Environment.TickCount64).TotalSeconds); - - ctx.Response.StatusCode = 429; - ctx.Response.Headers.RetryAfter = timeLeft.ToString(CultureInfo.InvariantCulture); + ctx.Response.StatusCode = StatusCodes.Status429TooManyRequests; + ctx.Response.Headers.RetryAfter = Math.Ceiling(TimeSpan.FromMilliseconds(currentCard.ExpiresAt - Environment.TickCount64).TotalSeconds).ToString(CultureInfo.InvariantCulture); return; } - if (cardPresenter.SetCard(request.MatrixCode) != null) + if (cardPresenter.SetCard(request.AccessCode, request.PhysicalCardIDm) != null) { - ctx.Response.StatusCode = 202; + ctx.Response.StatusCode = StatusCodes.Status202Accepted; } else { - ctx.Response.StatusCode = 422; - await ctx.Response.WriteAsync("Invalid card id format."); + await WriteErrorResponse(ctx, StatusCodes.Status422UnprocessableEntity, "Invalid card id format."); } } - + + private static Task WriteErrorResponse(HttpContext ctx, int code, string message) + { + ctx.Response.StatusCode = code; + return ctx.Response.WriteAsJsonAsync(new ServerErrorMessage(message), SerializerContext.Default.ServerErrorMessage); + } + [JsonSerializable(typeof(SystemState))] [JsonSerializable(typeof(CardReadRequest))] + [JsonSerializable(typeof(ServerErrorMessage))] [JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.Never)] private partial class SerializerContext : JsonSerializerContext; } \ No newline at end of file From a6ad82ffdaa487161553e091f3008b04ab0d836c Mon Sep 17 00:00:00 2001 From: ppc Date: Tue, 14 Jan 2025 15:56:53 +0000 Subject: [PATCH 20/38] documentation fixes/config key simplification --- AMNet.Server/CardPresenter.cs | 2 +- AMNet.Server/Config.cs | 2 +- readme.md | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/AMNet.Server/CardPresenter.cs b/AMNet.Server/CardPresenter.cs index b1c944d..e4f248c 100644 --- a/AMNet.Server/CardPresenter.cs +++ b/AMNet.Server/CardPresenter.cs @@ -52,7 +52,7 @@ internal class CardPresenter /// Sets the card /// /// The access code of the card to use - /// The FeliCa card IDm, if a physical card was used + /// The FeliCa card IDm, if a physical card was used /// How long the card should be presented to the game for in milliseconds /// Whether the card was set successfully public StoredCard SetCard(string accessCode, string idmHex, long validFor = 5000) diff --git a/AMNet.Server/Config.cs b/AMNet.Server/Config.cs index 8951085..482c9bf 100644 --- a/AMNet.Server/Config.cs +++ b/AMNet.Server/Config.cs @@ -14,7 +14,7 @@ internal static class Config GameId = ReadKey(IOSection, "gameId", 4); ServerName = ReadKey(IOSection, "serverName", 26, Environment.MachineName); - PhysicalCardUseIDm = PInvoke.GetPrivateProfileInt(IOSection, "usePhysicalCardIdm", 0, ConfigFileName) > 0; + PhysicalCardUseIDm = PInvoke.GetPrivateProfileInt(IOSection, "useAimeDBForPhysicalCards", 0, ConfigFileName) > 0; EnableAimeTxt = PInvoke.GetPrivateProfileInt(IOSection, "enableKeyboardMode", 1, ConfigFileName) > 0; AimeTxtPath = ReadKey("aime", "aimePath", PInvoke.MAX_PATH, @"DEVICE\aime.txt"); } diff --git a/readme.md b/readme.md index 1df1e18..81d176f 100644 --- a/readme.md +++ b/readme.md @@ -26,22 +26,22 @@ This was originally intended for use with maimai and CHUNITHM, but should be com ; serverName - the name of the server to show to users (defaults to the computer name if not set) ; serverAddress - the address to listen on, including the protocol (defaults to http://+:6070, where + is a wildcard) ; enableKeyboardMode - whether to enable hold-enter-to-login mode. Reads the file set at aimePath for card number (or DEVICE/aime.txt if not set). - ; physicalCardIdentifier - if the game is using a shared network (rinnet/aquadx.net/missless), set this to "idm" (otherwise "access"). It only affects the use of physical cards only. + ; useAimeDBForPhysicalCards - Whether physical cards should use AiMeDB to retrieve access codes. If the game is using a hosted network, set this to 1 to load the same account data/profile as you would get on a physical cab. gameId=SDEZ ;serverName=CHUNI-PENGUIN ;serverAddress=http://+:6070 ;enableKeyboardMode=1 - ;physicalCardIdentifier=idm + ;useAimeDBForPhysicalCards=1 ``` 4. Launch the game, and check the amdaemon console for server startup messages 5. Access the using one of the interfaces (see below), add a new server `http://:6070`, import your existing card (or create a new one) and start playing! ### Physical Cards -AMNet 1.3 adds support for scanning physical cards on iOS devices. If you own a physical Aime/Banapass/e-Amusement/Suica card, you can scan it using the app and use it in-game. -By changing the `physicalCardIdentifier` in segatools.ini, it is also possible to adjust the format of card numbers sent to the game: +AMNet 1.3 adds support for scanning physical cards on iOS devices. If you own a physical AiMe/Banapass/e-Amusement/Suica card, you can scan it using the app and use it in-game. +By changing the `useAimeDBForPhysicalCards` in segatools.ini, it is also possible to adjust the format of card numbers sent to the game: -- `access` - This is the default and should be used if the game server is hosted locally. -- `idm` - If the game server is hosted on a shared network (rinnet/aquadx.net/missless) use this option instead (cards that have been used on a cabinet connected to the same network should load correctly). +- `0` - Default value, will send the 20-digit access code on the back of the card to the game and load the save data directly. +- `1` - Send the card's IDm data to the game, and it will use the connected AiMeDB to retrieve a 20 digit code assigned to the card. This option should be used on shared networks to enable the same account data/profile as you would get on a physical cab. ## Interfaces AMNet can be used on most platforms, see below for options available for each platform: From 0a42a0a912136c3441245d070cdf10ca07c38668 Mon Sep 17 00:00:00 2001 From: ppc Date: Tue, 14 Jan 2025 18:52:08 +0000 Subject: [PATCH 21/38] send idm with card in requests --- amnet-native/app/ServerCardIn.tsx | 7 +++---- amnet-shared/dist/index.d.mts | 8 +++----- amnet-shared/dist/index.d.ts | 8 +++----- amnet-shared/dist/index.js.map | 2 +- amnet-shared/dist/index.mjs.map | 2 +- amnet-shared/src/api.ts | 11 ++++------- 6 files changed, 15 insertions(+), 23 deletions(-) diff --git a/amnet-native/app/ServerCardIn.tsx b/amnet-native/app/ServerCardIn.tsx index f60b69e..9eebe84 100644 --- a/amnet-native/app/ServerCardIn.tsx +++ b/amnet-native/app/ServerCardIn.tsx @@ -147,10 +147,9 @@ export default function ServerCardIn({route, navigation}) { const cardInCallback = useCallback(() => { setCardInCompleted(false); - const cardId = selectedCard.physicalCardData && server.preferredCardIdFormat === "idm" ? icCard0008Number(selectedCard.physicalCardData.cardIDm) : selectedCard.code; const abortController = new AbortController(); - performCardIn(server, {cardId}, abortController.signal).then(r => { + performCardIn(server, {cardId: selectedCard.code, physicalCardIDm: selectedCard.physicalCardData?.cardIDm ?? undefined}, abortController.signal).then(r => { const now = new Date(); realm.write(() => { server.lastUsed = now; @@ -178,10 +177,10 @@ export default function ServerCardIn({route, navigation}) { setCardInCompleted(false); - const cardId = server.preferredCardIdFormat === "idm" || card.type !== ICType.Amusement ? icCard0008Number(card.idm) : card.accessCode; + const cardId = card.type !== ICType.Amusement ? icCard0008Number(card.idm) : card.accessCode; const abortController = new AbortController(); - performCardIn(server, {cardId}, abortController.signal).then(r => { + performCardIn(server, {cardId, physicalCardIDm: card.idm}, abortController.signal).then(r => { setCardInCompleted(r); }).catch(() => { setCardInCompleted(false); diff --git a/amnet-shared/dist/index.d.mts b/amnet-shared/dist/index.d.mts index 167d216..b6fea5e 100644 --- a/amnet-shared/dist/index.d.mts +++ b/amnet-shared/dist/index.d.mts @@ -8,6 +8,7 @@ declare const AMNET_API_VERSION = 1; declare const MIN_AMNET_API_VERSION = 1; interface AMCardInRequest { cardId: string; + physicalCardIDm?: string; } /** * Server connection info (api version, address, etc.) @@ -30,13 +31,10 @@ interface AMServerInstanceMetrics { timeSinceLastPoll?: number; sessionUptime?: number; } -interface AMServerPhysicalCardPreferences { - preferredCardIdFormat?: "idm" | "access" | null; -} /** * The response from an AMNet server's info endpoint */ -type AMServerInfo = AMServerMetadata & AMServerConnectionInfo & AMServerInstanceMetrics & AMServerPhysicalCardPreferences; +type AMServerInfo = AMServerMetadata & AMServerConnectionInfo & AMServerInstanceMetrics; /** * Performs the card-in request to the specified server * @param server The server to perform the request against @@ -65,4 +63,4 @@ declare function validateCardId(id: string): boolean; */ declare function getGameName(id: string): string | null; -export { type AMCardInRequest, AMNET_API_VERSION, type AMServerConnectionInfo, type AMServerInfo, type AMServerInstanceMetrics, type AMServerMetadata, type AMServerPhysicalCardPreferences, CARD_ID_REGEX, MIN_AMNET_API_VERSION, fetchServerInfo, generateCardNumber, getGameName, performCardIn, validateCardId }; +export { type AMCardInRequest, AMNET_API_VERSION, type AMServerConnectionInfo, type AMServerInfo, type AMServerInstanceMetrics, type AMServerMetadata, CARD_ID_REGEX, MIN_AMNET_API_VERSION, fetchServerInfo, generateCardNumber, getGameName, performCardIn, validateCardId }; diff --git a/amnet-shared/dist/index.d.ts b/amnet-shared/dist/index.d.ts index 167d216..b6fea5e 100644 --- a/amnet-shared/dist/index.d.ts +++ b/amnet-shared/dist/index.d.ts @@ -8,6 +8,7 @@ declare const AMNET_API_VERSION = 1; declare const MIN_AMNET_API_VERSION = 1; interface AMCardInRequest { cardId: string; + physicalCardIDm?: string; } /** * Server connection info (api version, address, etc.) @@ -30,13 +31,10 @@ interface AMServerInstanceMetrics { timeSinceLastPoll?: number; sessionUptime?: number; } -interface AMServerPhysicalCardPreferences { - preferredCardIdFormat?: "idm" | "access" | null; -} /** * The response from an AMNet server's info endpoint */ -type AMServerInfo = AMServerMetadata & AMServerConnectionInfo & AMServerInstanceMetrics & AMServerPhysicalCardPreferences; +type AMServerInfo = AMServerMetadata & AMServerConnectionInfo & AMServerInstanceMetrics; /** * Performs the card-in request to the specified server * @param server The server to perform the request against @@ -65,4 +63,4 @@ declare function validateCardId(id: string): boolean; */ declare function getGameName(id: string): string | null; -export { type AMCardInRequest, AMNET_API_VERSION, type AMServerConnectionInfo, type AMServerInfo, type AMServerInstanceMetrics, type AMServerMetadata, type AMServerPhysicalCardPreferences, CARD_ID_REGEX, MIN_AMNET_API_VERSION, fetchServerInfo, generateCardNumber, getGameName, performCardIn, validateCardId }; +export { type AMCardInRequest, AMNET_API_VERSION, type AMServerConnectionInfo, type AMServerInfo, type AMServerInstanceMetrics, type AMServerMetadata, CARD_ID_REGEX, MIN_AMNET_API_VERSION, fetchServerInfo, generateCardNumber, getGameName, performCardIn, validateCardId }; diff --git a/amnet-shared/dist/index.js.map b/amnet-shared/dist/index.js.map index 9359f8f..1827b1f 100644 --- a/amnet-shared/dist/index.js.map +++ b/amnet-shared/dist/index.js.map @@ -1 +1 @@ -{"version":3,"sources":["../src/index.ts","../src/api.ts","../src/card.ts","../src/gameIds.ts"],"sourcesContent":["export * from './api';\nexport * from './card';\nexport * from './gameIds';","/**\n * The latest version of the AMNet API this library supports\n */\nexport const AMNET_API_VERSION = 1;\n\n/**\n * The minimum version of the AMNet API this library supports\n */\nexport const MIN_AMNET_API_VERSION = 1;\n\nexport interface AMCardInRequest {\n cardId: string;\n}\n\n/**\n * Server connection info (api version, address, etc.)\n */\nexport interface AMServerConnectionInfo {\n apiVersion: number;\n serverAddress: string;\n}\n\n/**\n * User-definable server metadata\n */\nexport interface AMServerMetadata {\n serverName: string;\n gameId?: string | null | undefined;\n}\n\n/**\n * Metrics about the current game session\n */\nexport interface AMServerInstanceMetrics {\n timeSinceLastPoll?: number;\n sessionUptime?: number;\n}\n\nexport interface AMServerPhysicalCardPreferences {\n preferredCardIdFormat?: \"idm\" | \"access\" | null;\n}\n\n/**\n * The response from an AMNet server's info endpoint\n */\nexport type AMServerInfo = AMServerMetadata & AMServerConnectionInfo & AMServerInstanceMetrics & AMServerPhysicalCardPreferences;\n\n/**\n * Performs the card-in request to the specified server\n * @param server The server to perform the request against\n * @param request The card-in request to send\n * @param signal The abort signal to use for the request\n */\nexport async function performCardIn(server: AMServerConnectionInfo, request: AMCardInRequest, signal: AbortSignal): Promise {\n if (server.apiVersion < MIN_AMNET_API_VERSION || server.apiVersion > AMNET_API_VERSION) {\n throw new Error(\"Unsupported server API version\");\n }\n \n try {\n const response = await fetch(`${new URL(server.serverAddress).origin}/amnet/signin`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\"\n },\n body: JSON.stringify(request),\n signal: signal\n });\n\n return response.ok;\n } catch {\n return false;\n }\n}\n\n/**\n * Attempts to connect to the specified server and fetch its info\n * @param serverAddress The origin address of the server to connect to\n * @param abort The abort signal to use for the request (for cancellation)\n */\nexport async function fetchServerInfo(serverAddress: string, abort: AbortSignal): Promise {\n try {\n const response = await fetch(`${new URL(serverAddress).origin}/amnet/info`, {\n signal: abort\n });\n \n if (!response.ok) {\n return null;\n }\n\n return {\n ...await response.json(),\n serverAddress: serverAddress\n } satisfies AMServerMetadata;\n } catch {\n return null;\n }\n}\n","export const CARD_ID_REGEX = /^(?:[0-9]{4} ?){5}$/i;\n\nexport function generateCardNumber(): string {\n let cardId = '';\n\n while (true) {\n // don't start with a 3\n const firstDigit = Math.floor(Math.random() * 10);\n\n if (firstDigit !== 3) {\n cardId = firstDigit.toString();\n break;\n }\n }\n\n for (let i = 0; i < 19; i++) {\n cardId += Math.floor(Math.random() * 10).toString();\n }\n\n return cardId;\n}\n\n/**\n * Validates the provided card id by known parameters\n * @param id The card id to validate\n */\nexport function validateCardId(id: string): boolean {\n return id[0] !== '3' && CARD_ID_REGEX.test(id);\n}\n","/**\n * Get the game name from the game identifier (4-letter code)\n * @param id\n */\nexport function getGameName(id: string): string | null {\n switch (id?.toUpperCase()) {\n case \"SDGT\":\n return \"Initial D The Arcade\";\n case \"SDHD\":\n case \"SDBT\":\n return \"CHUNITHM\";\n case \"SDDT\":\n return \"O.N.G.E.K.I.\";\n case \"SDEZ\":\n return \"maimai DX\";\n case \"SBZV\":\n return \"Project DIVA Future Tone\";\n \n default:\n return null;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACGO,IAAM,oBAAoB;AAK1B,IAAM,wBAAwB;AA6CrC,SAAsB,cAAc,QAAgC,SAA0B,QAAuC;AAAA;AACjI,QAAI,OAAO,aAAa,yBAAyB,OAAO,aAAa,mBAAmB;AACpF,YAAM,IAAI,MAAM,gCAAgC;AAAA,IACpD;AAEA,QAAI;AACA,YAAM,WAAW,MAAM,MAAM,GAAG,IAAI,IAAI,OAAO,aAAa,EAAE,MAAM,iBAAiB;AAAA,QACjF,QAAQ;AAAA,QACR,SAAS;AAAA,UACL,gBAAgB;AAAA,QACpB;AAAA,QACA,MAAM,KAAK,UAAU,OAAO;AAAA,QAC5B;AAAA,MACJ,CAAC;AAED,aAAO,SAAS;AAAA,IACpB,SAAQ;AACJ,aAAO;AAAA,IACX;AAAA,EACJ;AAAA;AAOA,SAAsB,gBAAgB,eAAuB,OAAkD;AAAA;AAC3G,QAAI;AACA,YAAM,WAAW,MAAM,MAAM,GAAG,IAAI,IAAI,aAAa,EAAE,MAAM,eAAe;AAAA,QACxE,QAAQ;AAAA,MACZ,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AACd,eAAO;AAAA,MACX;AAEA,aAAO,iCACA,MAAM,SAAS,KAAK,IADpB;AAAA,QAEH;AAAA,MACJ;AAAA,IACJ,SAAQ;AACJ,aAAO;AAAA,IACX;AAAA,EACJ;AAAA;;;AChGO,IAAM,gBAAgB;AAEtB,SAAS,qBAA6B;AACzC,MAAI,SAAS;AAEb,SAAO,MAAM;AAET,UAAM,aAAa,KAAK,MAAM,KAAK,OAAO,IAAI,EAAE;AAEhD,QAAI,eAAe,GAAG;AAClB,eAAS,WAAW,SAAS;AAC7B;AAAA,IACJ;AAAA,EACJ;AAEA,WAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AACzB,cAAU,KAAK,MAAM,KAAK,OAAO,IAAI,EAAE,EAAE,SAAS;AAAA,EACtD;AAEA,SAAO;AACX;AAMO,SAAS,eAAe,IAAqB;AAChD,SAAO,GAAG,CAAC,MAAM,OAAO,cAAc,KAAK,EAAE;AACjD;;;ACxBO,SAAS,YAAY,IAA2B;AACnD,UAAQ,yBAAI,eAAe;AAAA,IACvB,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AAAA,IACL,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IAEX;AACI,aAAO;AAAA,EACf;AACJ;","names":[]} \ No newline at end of file +{"version":3,"sources":["../src/index.ts","../src/api.ts","../src/card.ts","../src/gameIds.ts"],"sourcesContent":["export * from './api';\r\nexport * from './card';\r\nexport * from './gameIds';","/**\r\n * The latest version of the AMNet API this library supports\r\n */\r\nexport const AMNET_API_VERSION = 1;\r\n\r\n/**\r\n * The minimum version of the AMNet API this library supports\r\n */\r\nexport const MIN_AMNET_API_VERSION = 1;\r\n\r\nexport interface AMCardInRequest {\r\n cardId: string;\r\n physicalCardIDm?: string;\r\n}\r\n\r\n/**\r\n * Server connection info (api version, address, etc.)\r\n */\r\nexport interface AMServerConnectionInfo {\r\n apiVersion: number;\r\n serverAddress: string;\r\n}\r\n\r\n/**\r\n * User-definable server metadata\r\n */\r\nexport interface AMServerMetadata {\r\n serverName: string;\r\n gameId?: string | null | undefined;\r\n}\r\n\r\n/**\r\n * Metrics about the current game session\r\n */\r\nexport interface AMServerInstanceMetrics {\r\n timeSinceLastPoll?: number;\r\n sessionUptime?: number;\r\n}\r\n\r\n/**\r\n * The response from an AMNet server's info endpoint\r\n */\r\nexport type AMServerInfo = AMServerMetadata & AMServerConnectionInfo & AMServerInstanceMetrics;\r\n\r\n/**\r\n * Performs the card-in request to the specified server\r\n * @param server The server to perform the request against\r\n * @param request The card-in request to send\r\n * @param signal The abort signal to use for the request\r\n */\r\nexport async function performCardIn(server: AMServerConnectionInfo, request: AMCardInRequest, signal: AbortSignal): Promise {\r\n if (server.apiVersion < MIN_AMNET_API_VERSION || server.apiVersion > AMNET_API_VERSION) {\r\n throw new Error(\"Unsupported server API version\");\r\n }\r\n\r\n try {\r\n const response = await fetch(`${new URL(server.serverAddress).origin}/amnet/signin`, {\r\n method: \"POST\",\r\n headers: {\r\n \"Content-Type\": \"application/json\"\r\n },\r\n body: JSON.stringify(request),\r\n signal: signal\r\n });\r\n\r\n return response.ok;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * Attempts to connect to the specified server and fetch its info\r\n * @param serverAddress The origin address of the server to connect to\r\n * @param abort The abort signal to use for the request (for cancellation)\r\n */\r\nexport async function fetchServerInfo(serverAddress: string, abort: AbortSignal): Promise {\r\n try {\r\n const response = await fetch(`${new URL(serverAddress).origin}/amnet/info`, {\r\n signal: abort\r\n });\r\n\r\n if (!response.ok) {\r\n return null;\r\n }\r\n\r\n return {\r\n ...await response.json(),\r\n serverAddress: serverAddress\r\n } satisfies AMServerMetadata;\r\n } catch {\r\n return null;\r\n }\r\n}\r\n","export const CARD_ID_REGEX = /^(?:[0-9]{4} ?){5}$/i;\r\n\r\nexport function generateCardNumber(): string {\r\n let cardId = '';\r\n\r\n while (true) {\r\n // don't start with a 3\r\n const firstDigit = Math.floor(Math.random() * 10);\r\n\r\n if (firstDigit !== 3) {\r\n cardId = firstDigit.toString();\r\n break;\r\n }\r\n }\r\n\r\n for (let i = 0; i < 19; i++) {\r\n cardId += Math.floor(Math.random() * 10).toString();\r\n }\r\n\r\n return cardId;\r\n}\r\n\r\n/**\r\n * Validates the provided card id by known parameters\r\n * @param id The card id to validate\r\n */\r\nexport function validateCardId(id: string): boolean {\r\n return id[0] !== '3' && CARD_ID_REGEX.test(id);\r\n}\r\n","/**\r\n * Get the game name from the game identifier (4-letter code)\r\n * @param id\r\n */\r\nexport function getGameName(id: string): string | null {\r\n switch (id?.toUpperCase()) {\r\n case \"SDGT\":\r\n return \"Initial D The Arcade\";\r\n case \"SDHD\":\r\n case \"SDBT\":\r\n return \"CHUNITHM\";\r\n case \"SDDT\":\r\n return \"O.N.G.E.K.I.\";\r\n case \"SDEZ\":\r\n return \"maimai DX\";\r\n case \"SBZV\":\r\n return \"Project DIVA Future Tone\";\r\n \r\n default:\r\n return null;\r\n }\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACGO,IAAM,oBAAoB;AAK1B,IAAM,wBAAwB;AA0CrC,SAAsB,cAAc,QAAgC,SAA0B,QAAuC;AAAA;AACjI,QAAI,OAAO,aAAa,yBAAyB,OAAO,aAAa,mBAAmB;AACpF,YAAM,IAAI,MAAM,gCAAgC;AAAA,IACpD;AAEA,QAAI;AACA,YAAM,WAAW,MAAM,MAAM,GAAG,IAAI,IAAI,OAAO,aAAa,EAAE,MAAM,iBAAiB;AAAA,QACjF,QAAQ;AAAA,QACR,SAAS;AAAA,UACL,gBAAgB;AAAA,QACpB;AAAA,QACA,MAAM,KAAK,UAAU,OAAO;AAAA,QAC5B;AAAA,MACJ,CAAC;AAED,aAAO,SAAS;AAAA,IACpB,SAAQ;AACJ,aAAO;AAAA,IACX;AAAA,EACJ;AAAA;AAOA,SAAsB,gBAAgB,eAAuB,OAAkD;AAAA;AAC3G,QAAI;AACA,YAAM,WAAW,MAAM,MAAM,GAAG,IAAI,IAAI,aAAa,EAAE,MAAM,eAAe;AAAA,QACxE,QAAQ;AAAA,MACZ,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AACd,eAAO;AAAA,MACX;AAEA,aAAO,iCACA,MAAM,SAAS,KAAK,IADpB;AAAA,QAEH;AAAA,MACJ;AAAA,IACJ,SAAQ;AACJ,aAAO;AAAA,IACX;AAAA,EACJ;AAAA;;;AC7FO,IAAM,gBAAgB;AAEtB,SAAS,qBAA6B;AACzC,MAAI,SAAS;AAEb,SAAO,MAAM;AAET,UAAM,aAAa,KAAK,MAAM,KAAK,OAAO,IAAI,EAAE;AAEhD,QAAI,eAAe,GAAG;AAClB,eAAS,WAAW,SAAS;AAC7B;AAAA,IACJ;AAAA,EACJ;AAEA,WAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AACzB,cAAU,KAAK,MAAM,KAAK,OAAO,IAAI,EAAE,EAAE,SAAS;AAAA,EACtD;AAEA,SAAO;AACX;AAMO,SAAS,eAAe,IAAqB;AAChD,SAAO,GAAG,CAAC,MAAM,OAAO,cAAc,KAAK,EAAE;AACjD;;;ACxBO,SAAS,YAAY,IAA2B;AACnD,UAAQ,yBAAI,eAAe;AAAA,IACvB,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AAAA,IACL,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IAEX;AACI,aAAO;AAAA,EACf;AACJ;","names":[]} \ No newline at end of file diff --git a/amnet-shared/dist/index.mjs.map b/amnet-shared/dist/index.mjs.map index 697ec06..a316ce2 100644 --- a/amnet-shared/dist/index.mjs.map +++ b/amnet-shared/dist/index.mjs.map @@ -1 +1 @@ -{"version":3,"sources":["../src/api.ts","../src/card.ts","../src/gameIds.ts"],"sourcesContent":["/**\n * The latest version of the AMNet API this library supports\n */\nexport const AMNET_API_VERSION = 1;\n\n/**\n * The minimum version of the AMNet API this library supports\n */\nexport const MIN_AMNET_API_VERSION = 1;\n\nexport interface AMCardInRequest {\n cardId: string;\n}\n\n/**\n * Server connection info (api version, address, etc.)\n */\nexport interface AMServerConnectionInfo {\n apiVersion: number;\n serverAddress: string;\n}\n\n/**\n * User-definable server metadata\n */\nexport interface AMServerMetadata {\n serverName: string;\n gameId?: string | null | undefined;\n}\n\n/**\n * Metrics about the current game session\n */\nexport interface AMServerInstanceMetrics {\n timeSinceLastPoll?: number;\n sessionUptime?: number;\n}\n\nexport interface AMServerPhysicalCardPreferences {\n preferredCardIdFormat?: \"idm\" | \"access\" | null;\n}\n\n/**\n * The response from an AMNet server's info endpoint\n */\nexport type AMServerInfo = AMServerMetadata & AMServerConnectionInfo & AMServerInstanceMetrics & AMServerPhysicalCardPreferences;\n\n/**\n * Performs the card-in request to the specified server\n * @param server The server to perform the request against\n * @param request The card-in request to send\n * @param signal The abort signal to use for the request\n */\nexport async function performCardIn(server: AMServerConnectionInfo, request: AMCardInRequest, signal: AbortSignal): Promise {\n if (server.apiVersion < MIN_AMNET_API_VERSION || server.apiVersion > AMNET_API_VERSION) {\n throw new Error(\"Unsupported server API version\");\n }\n \n try {\n const response = await fetch(`${new URL(server.serverAddress).origin}/amnet/signin`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\"\n },\n body: JSON.stringify(request),\n signal: signal\n });\n\n return response.ok;\n } catch {\n return false;\n }\n}\n\n/**\n * Attempts to connect to the specified server and fetch its info\n * @param serverAddress The origin address of the server to connect to\n * @param abort The abort signal to use for the request (for cancellation)\n */\nexport async function fetchServerInfo(serverAddress: string, abort: AbortSignal): Promise {\n try {\n const response = await fetch(`${new URL(serverAddress).origin}/amnet/info`, {\n signal: abort\n });\n \n if (!response.ok) {\n return null;\n }\n\n return {\n ...await response.json(),\n serverAddress: serverAddress\n } satisfies AMServerMetadata;\n } catch {\n return null;\n }\n}\n","export const CARD_ID_REGEX = /^(?:[0-9]{4} ?){5}$/i;\n\nexport function generateCardNumber(): string {\n let cardId = '';\n\n while (true) {\n // don't start with a 3\n const firstDigit = Math.floor(Math.random() * 10);\n\n if (firstDigit !== 3) {\n cardId = firstDigit.toString();\n break;\n }\n }\n\n for (let i = 0; i < 19; i++) {\n cardId += Math.floor(Math.random() * 10).toString();\n }\n\n return cardId;\n}\n\n/**\n * Validates the provided card id by known parameters\n * @param id The card id to validate\n */\nexport function validateCardId(id: string): boolean {\n return id[0] !== '3' && CARD_ID_REGEX.test(id);\n}\n","/**\n * Get the game name from the game identifier (4-letter code)\n * @param id\n */\nexport function getGameName(id: string): string | null {\n switch (id?.toUpperCase()) {\n case \"SDGT\":\n return \"Initial D The Arcade\";\n case \"SDHD\":\n case \"SDBT\":\n return \"CHUNITHM\";\n case \"SDDT\":\n return \"O.N.G.E.K.I.\";\n case \"SDEZ\":\n return \"maimai DX\";\n case \"SBZV\":\n return \"Project DIVA Future Tone\";\n \n default:\n return null;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGO,IAAM,oBAAoB;AAK1B,IAAM,wBAAwB;AA6CrC,SAAsB,cAAc,QAAgC,SAA0B,QAAuC;AAAA;AACjI,QAAI,OAAO,aAAa,yBAAyB,OAAO,aAAa,mBAAmB;AACpF,YAAM,IAAI,MAAM,gCAAgC;AAAA,IACpD;AAEA,QAAI;AACA,YAAM,WAAW,MAAM,MAAM,GAAG,IAAI,IAAI,OAAO,aAAa,EAAE,MAAM,iBAAiB;AAAA,QACjF,QAAQ;AAAA,QACR,SAAS;AAAA,UACL,gBAAgB;AAAA,QACpB;AAAA,QACA,MAAM,KAAK,UAAU,OAAO;AAAA,QAC5B;AAAA,MACJ,CAAC;AAED,aAAO,SAAS;AAAA,IACpB,SAAQ;AACJ,aAAO;AAAA,IACX;AAAA,EACJ;AAAA;AAOA,SAAsB,gBAAgB,eAAuB,OAAkD;AAAA;AAC3G,QAAI;AACA,YAAM,WAAW,MAAM,MAAM,GAAG,IAAI,IAAI,aAAa,EAAE,MAAM,eAAe;AAAA,QACxE,QAAQ;AAAA,MACZ,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AACd,eAAO;AAAA,MACX;AAEA,aAAO,iCACA,MAAM,SAAS,KAAK,IADpB;AAAA,QAEH;AAAA,MACJ;AAAA,IACJ,SAAQ;AACJ,aAAO;AAAA,IACX;AAAA,EACJ;AAAA;;;AChGO,IAAM,gBAAgB;AAEtB,SAAS,qBAA6B;AACzC,MAAI,SAAS;AAEb,SAAO,MAAM;AAET,UAAM,aAAa,KAAK,MAAM,KAAK,OAAO,IAAI,EAAE;AAEhD,QAAI,eAAe,GAAG;AAClB,eAAS,WAAW,SAAS;AAC7B;AAAA,IACJ;AAAA,EACJ;AAEA,WAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AACzB,cAAU,KAAK,MAAM,KAAK,OAAO,IAAI,EAAE,EAAE,SAAS;AAAA,EACtD;AAEA,SAAO;AACX;AAMO,SAAS,eAAe,IAAqB;AAChD,SAAO,GAAG,CAAC,MAAM,OAAO,cAAc,KAAK,EAAE;AACjD;;;ACxBO,SAAS,YAAY,IAA2B;AACnD,UAAQ,yBAAI,eAAe;AAAA,IACvB,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AAAA,IACL,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IAEX;AACI,aAAO;AAAA,EACf;AACJ;","names":[]} \ No newline at end of file +{"version":3,"sources":["../src/api.ts","../src/card.ts","../src/gameIds.ts"],"sourcesContent":["/**\r\n * The latest version of the AMNet API this library supports\r\n */\r\nexport const AMNET_API_VERSION = 1;\r\n\r\n/**\r\n * The minimum version of the AMNet API this library supports\r\n */\r\nexport const MIN_AMNET_API_VERSION = 1;\r\n\r\nexport interface AMCardInRequest {\r\n cardId: string;\r\n physicalCardIDm?: string;\r\n}\r\n\r\n/**\r\n * Server connection info (api version, address, etc.)\r\n */\r\nexport interface AMServerConnectionInfo {\r\n apiVersion: number;\r\n serverAddress: string;\r\n}\r\n\r\n/**\r\n * User-definable server metadata\r\n */\r\nexport interface AMServerMetadata {\r\n serverName: string;\r\n gameId?: string | null | undefined;\r\n}\r\n\r\n/**\r\n * Metrics about the current game session\r\n */\r\nexport interface AMServerInstanceMetrics {\r\n timeSinceLastPoll?: number;\r\n sessionUptime?: number;\r\n}\r\n\r\n/**\r\n * The response from an AMNet server's info endpoint\r\n */\r\nexport type AMServerInfo = AMServerMetadata & AMServerConnectionInfo & AMServerInstanceMetrics;\r\n\r\n/**\r\n * Performs the card-in request to the specified server\r\n * @param server The server to perform the request against\r\n * @param request The card-in request to send\r\n * @param signal The abort signal to use for the request\r\n */\r\nexport async function performCardIn(server: AMServerConnectionInfo, request: AMCardInRequest, signal: AbortSignal): Promise {\r\n if (server.apiVersion < MIN_AMNET_API_VERSION || server.apiVersion > AMNET_API_VERSION) {\r\n throw new Error(\"Unsupported server API version\");\r\n }\r\n\r\n try {\r\n const response = await fetch(`${new URL(server.serverAddress).origin}/amnet/signin`, {\r\n method: \"POST\",\r\n headers: {\r\n \"Content-Type\": \"application/json\"\r\n },\r\n body: JSON.stringify(request),\r\n signal: signal\r\n });\r\n\r\n return response.ok;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * Attempts to connect to the specified server and fetch its info\r\n * @param serverAddress The origin address of the server to connect to\r\n * @param abort The abort signal to use for the request (for cancellation)\r\n */\r\nexport async function fetchServerInfo(serverAddress: string, abort: AbortSignal): Promise {\r\n try {\r\n const response = await fetch(`${new URL(serverAddress).origin}/amnet/info`, {\r\n signal: abort\r\n });\r\n\r\n if (!response.ok) {\r\n return null;\r\n }\r\n\r\n return {\r\n ...await response.json(),\r\n serverAddress: serverAddress\r\n } satisfies AMServerMetadata;\r\n } catch {\r\n return null;\r\n }\r\n}\r\n","export const CARD_ID_REGEX = /^(?:[0-9]{4} ?){5}$/i;\r\n\r\nexport function generateCardNumber(): string {\r\n let cardId = '';\r\n\r\n while (true) {\r\n // don't start with a 3\r\n const firstDigit = Math.floor(Math.random() * 10);\r\n\r\n if (firstDigit !== 3) {\r\n cardId = firstDigit.toString();\r\n break;\r\n }\r\n }\r\n\r\n for (let i = 0; i < 19; i++) {\r\n cardId += Math.floor(Math.random() * 10).toString();\r\n }\r\n\r\n return cardId;\r\n}\r\n\r\n/**\r\n * Validates the provided card id by known parameters\r\n * @param id The card id to validate\r\n */\r\nexport function validateCardId(id: string): boolean {\r\n return id[0] !== '3' && CARD_ID_REGEX.test(id);\r\n}\r\n","/**\r\n * Get the game name from the game identifier (4-letter code)\r\n * @param id\r\n */\r\nexport function getGameName(id: string): string | null {\r\n switch (id?.toUpperCase()) {\r\n case \"SDGT\":\r\n return \"Initial D The Arcade\";\r\n case \"SDHD\":\r\n case \"SDBT\":\r\n return \"CHUNITHM\";\r\n case \"SDDT\":\r\n return \"O.N.G.E.K.I.\";\r\n case \"SDEZ\":\r\n return \"maimai DX\";\r\n case \"SBZV\":\r\n return \"Project DIVA Future Tone\";\r\n \r\n default:\r\n return null;\r\n }\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGO,IAAM,oBAAoB;AAK1B,IAAM,wBAAwB;AA0CrC,SAAsB,cAAc,QAAgC,SAA0B,QAAuC;AAAA;AACjI,QAAI,OAAO,aAAa,yBAAyB,OAAO,aAAa,mBAAmB;AACpF,YAAM,IAAI,MAAM,gCAAgC;AAAA,IACpD;AAEA,QAAI;AACA,YAAM,WAAW,MAAM,MAAM,GAAG,IAAI,IAAI,OAAO,aAAa,EAAE,MAAM,iBAAiB;AAAA,QACjF,QAAQ;AAAA,QACR,SAAS;AAAA,UACL,gBAAgB;AAAA,QACpB;AAAA,QACA,MAAM,KAAK,UAAU,OAAO;AAAA,QAC5B;AAAA,MACJ,CAAC;AAED,aAAO,SAAS;AAAA,IACpB,SAAQ;AACJ,aAAO;AAAA,IACX;AAAA,EACJ;AAAA;AAOA,SAAsB,gBAAgB,eAAuB,OAAkD;AAAA;AAC3G,QAAI;AACA,YAAM,WAAW,MAAM,MAAM,GAAG,IAAI,IAAI,aAAa,EAAE,MAAM,eAAe;AAAA,QACxE,QAAQ;AAAA,MACZ,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AACd,eAAO;AAAA,MACX;AAEA,aAAO,iCACA,MAAM,SAAS,KAAK,IADpB;AAAA,QAEH;AAAA,MACJ;AAAA,IACJ,SAAQ;AACJ,aAAO;AAAA,IACX;AAAA,EACJ;AAAA;;;AC7FO,IAAM,gBAAgB;AAEtB,SAAS,qBAA6B;AACzC,MAAI,SAAS;AAEb,SAAO,MAAM;AAET,UAAM,aAAa,KAAK,MAAM,KAAK,OAAO,IAAI,EAAE;AAEhD,QAAI,eAAe,GAAG;AAClB,eAAS,WAAW,SAAS;AAC7B;AAAA,IACJ;AAAA,EACJ;AAEA,WAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AACzB,cAAU,KAAK,MAAM,KAAK,OAAO,IAAI,EAAE,EAAE,SAAS;AAAA,EACtD;AAEA,SAAO;AACX;AAMO,SAAS,eAAe,IAAqB;AAChD,SAAO,GAAG,CAAC,MAAM,OAAO,cAAc,KAAK,EAAE;AACjD;;;ACxBO,SAAS,YAAY,IAA2B;AACnD,UAAQ,yBAAI,eAAe;AAAA,IACvB,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AAAA,IACL,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IAEX;AACI,aAAO;AAAA,EACf;AACJ;","names":[]} \ No newline at end of file diff --git a/amnet-shared/src/api.ts b/amnet-shared/src/api.ts index acfb88c..5a2a64e 100644 --- a/amnet-shared/src/api.ts +++ b/amnet-shared/src/api.ts @@ -10,6 +10,7 @@ export const MIN_AMNET_API_VERSION = 1; export interface AMCardInRequest { cardId: string; + physicalCardIDm?: string; } /** @@ -36,14 +37,10 @@ export interface AMServerInstanceMetrics { sessionUptime?: number; } -export interface AMServerPhysicalCardPreferences { - preferredCardIdFormat?: "idm" | "access" | null; -} - /** * The response from an AMNet server's info endpoint */ -export type AMServerInfo = AMServerMetadata & AMServerConnectionInfo & AMServerInstanceMetrics & AMServerPhysicalCardPreferences; +export type AMServerInfo = AMServerMetadata & AMServerConnectionInfo & AMServerInstanceMetrics; /** * Performs the card-in request to the specified server @@ -55,7 +52,7 @@ export async function performCardIn(server: AMServerConnectionInfo, request: AMC if (server.apiVersion < MIN_AMNET_API_VERSION || server.apiVersion > AMNET_API_VERSION) { throw new Error("Unsupported server API version"); } - + try { const response = await fetch(`${new URL(server.serverAddress).origin}/amnet/signin`, { method: "POST", @@ -82,7 +79,7 @@ export async function fetchServerInfo(serverAddress: string, abort: AbortSignal) const response = await fetch(`${new URL(serverAddress).origin}/amnet/info`, { signal: abort }); - + if (!response.ok) { return null; } From 99fd999c06769e8b9ca9872dd380a28542f5d58b Mon Sep 17 00:00:00 2001 From: ppc Date: Tue, 14 Jan 2025 22:17:48 +0000 Subject: [PATCH 22/38] fix server ordering (latest first) --- amnet-native/app/ServerManager.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amnet-native/app/ServerManager.tsx b/amnet-native/app/ServerManager.tsx index 9a36d98..356e63f 100644 --- a/amnet-native/app/ServerManager.tsx +++ b/amnet-native/app/ServerManager.tsx @@ -87,7 +87,7 @@ export default function ServerManager({navigation}) { q = q.filtered("serverName CONTAINS[c] $0 OR serverAddress CONTAINS[c] $0", searchQuery); } - return q.sorted("lastUsed"); + return q.sorted("lastUsed", true); }, [query, searchQuery]); // enable searchbar From d0c7616ed19bd44c2d83c6af67e9ae2ea05245c0 Mon Sep 17 00:00:00 2001 From: ppc Date: Tue, 14 Jan 2025 22:18:21 +0000 Subject: [PATCH 23/38] remove unused type --- amnet-native/models/AMServer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/amnet-native/models/AMServer.ts b/amnet-native/models/AMServer.ts index 01d9b62..4571dc9 100644 --- a/amnet-native/models/AMServer.ts +++ b/amnet-native/models/AMServer.ts @@ -1,7 +1,7 @@ import {Realm} from 'realm'; -import {AMServerConnectionInfo, AMServerMetadata, AMServerPhysicalCardPreferences} from "amnet-shared"; +import {AMServerConnectionInfo, AMServerMetadata} from "amnet-shared"; -export class AMServer extends Realm.Object implements AMServerConnectionInfo, AMServerMetadata, AMServerPhysicalCardPreferences { +export class AMServer extends Realm.Object implements AMServerConnectionInfo, AMServerMetadata { _id!: Realm.BSON.ObjectId; apiVersion: number; From 9324b30e7bc7b7062352cf720f974763a479cf68 Mon Sep 17 00:00:00 2001 From: ppc Date: Tue, 14 Jan 2025 22:20:53 +0000 Subject: [PATCH 24/38] check for existing entry to select when scanning a physical card --- amnet-native/app/ServerCardIn.tsx | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/amnet-native/app/ServerCardIn.tsx b/amnet-native/app/ServerCardIn.tsx index 9eebe84..cbfaad9 100644 --- a/amnet-native/app/ServerCardIn.tsx +++ b/amnet-native/app/ServerCardIn.tsx @@ -27,7 +27,13 @@ import {AMServer} from "../models/AMServer"; import {AMCard} from "../models/AMCard"; import {felicaCardReadAsync, icCard0008Number, ICType} from "../lib/iccard"; -import {AMServerConnectionInfo, AMServerInstanceMetrics, fetchServerInfo, getGameName, performCardIn} from "amnet-shared"; +import { + AMServerConnectionInfo, + AMServerInstanceMetrics, + fetchServerInfo, + getGameName, + performCardIn +} from "amnet-shared"; import ObjectId = BSON.ObjectId; @@ -149,7 +155,10 @@ export default function ServerCardIn({route, navigation}) { const abortController = new AbortController(); - performCardIn(server, {cardId: selectedCard.code, physicalCardIDm: selectedCard.physicalCardData?.cardIDm ?? undefined}, abortController.signal).then(r => { + performCardIn(server, { + cardId: selectedCard.code, + physicalCardIDm: selectedCard.physicalCardData?.cardIDm ?? undefined + }, abortController.signal).then(r => { const now = new Date(); realm.write(() => { server.lastUsed = now; @@ -175,6 +184,14 @@ export default function ServerCardIn({route, navigation}) { return; } + const savedCard = cards.find(x => x.physicalCardData?.cardIDm === card.idm); + + if (savedCard) { + setUserSelectedCard(savedCard); + cardInCallback(); + return; + } + setCardInCompleted(false); const cardId = card.type !== ICType.Amusement ? icCard0008Number(card.idm) : card.accessCode; @@ -211,7 +228,8 @@ export default function ServerCardIn({route, navigation}) { } return ( - }> + }> @@ -231,17 +249,18 @@ export default function ServerCardIn({route, navigation}) { contentStyle={{flexDirection: 'row-reverse'}} mode={serverConnectionSuccess ? "contained" : "outlined"} buttonColor={cardInCompleted === false ? "red" : "green"} - icon={() => }> + icon={() => }> {cardInCompleted === null ? "Card In" : cardInCompleted ? "Card In Success" : "Failed"} {nfcSupported === true && ( }/> + icon={() => }/> )} From e83287c7b7210b8181159322d81a0d4e56c72510 Mon Sep 17 00:00:00 2001 From: ppc Date: Tue, 14 Jan 2025 23:29:52 +0000 Subject: [PATCH 25/38] add amusement ic access code encode/decode --- amnet-shared/dist/index.d.mts | 10 +- amnet-shared/dist/index.d.ts | 10 +- amnet-shared/dist/index.js | 184 +++++++++++++++++++----------- amnet-shared/dist/index.js.map | 2 +- amnet-shared/dist/index.mjs | 171 ++++++++++++++++----------- amnet-shared/dist/index.mjs.map | 2 +- amnet-shared/package-lock.json | 27 +++++ amnet-shared/package.json | 5 + amnet-shared/src/card-encoding.ts | 94 +++++++++++++++ amnet-shared/src/card.ts | 23 +--- amnet-shared/src/index.ts | 4 +- amnet-shared/tsconfig.json | 13 ++- 12 files changed, 387 insertions(+), 158 deletions(-) create mode 100644 amnet-shared/src/card-encoding.ts diff --git a/amnet-shared/dist/index.d.mts b/amnet-shared/dist/index.d.mts index b6fea5e..ab3c7f9 100644 --- a/amnet-shared/dist/index.d.mts +++ b/amnet-shared/dist/index.d.mts @@ -50,6 +50,7 @@ declare function performCardIn(server: AMServerConnectionInfo, request: AMCardIn declare function fetchServerInfo(serverAddress: string, abort: AbortSignal): Promise; declare const CARD_ID_REGEX: RegExp; +declare const AMNET_COMPANY_ID = 597; declare function generateCardNumber(): string; /** * Validates the provided card id by known parameters @@ -63,4 +64,11 @@ declare function validateCardId(id: string): boolean; */ declare function getGameName(id: string): string | null; -export { type AMCardInRequest, AMNET_API_VERSION, type AMServerConnectionInfo, type AMServerInfo, type AMServerInstanceMetrics, type AMServerMetadata, CARD_ID_REGEX, MIN_AMNET_API_VERSION, fetchServerInfo, generateCardNumber, getGameName, performCardIn, validateCardId }; +type DecodedAccessCode = { + revision: number; + serial: number; +}; +declare function decodeAccessCode(code: string): DecodedAccessCode | null; +declare function encodeAccessCode(company: number, serial: number, rev?: number): string; + +export { type AMCardInRequest, AMNET_API_VERSION, AMNET_COMPANY_ID, type AMServerConnectionInfo, type AMServerInfo, type AMServerInstanceMetrics, type AMServerMetadata, CARD_ID_REGEX, decodeAccessCode as DecodeAMAccessCode, encodeAccessCode as EncodeAMAccessCode, MIN_AMNET_API_VERSION, fetchServerInfo, generateCardNumber, getGameName, performCardIn, validateCardId }; diff --git a/amnet-shared/dist/index.d.ts b/amnet-shared/dist/index.d.ts index b6fea5e..ab3c7f9 100644 --- a/amnet-shared/dist/index.d.ts +++ b/amnet-shared/dist/index.d.ts @@ -50,6 +50,7 @@ declare function performCardIn(server: AMServerConnectionInfo, request: AMCardIn declare function fetchServerInfo(serverAddress: string, abort: AbortSignal): Promise; declare const CARD_ID_REGEX: RegExp; +declare const AMNET_COMPANY_ID = 597; declare function generateCardNumber(): string; /** * Validates the provided card id by known parameters @@ -63,4 +64,11 @@ declare function validateCardId(id: string): boolean; */ declare function getGameName(id: string): string | null; -export { type AMCardInRequest, AMNET_API_VERSION, type AMServerConnectionInfo, type AMServerInfo, type AMServerInstanceMetrics, type AMServerMetadata, CARD_ID_REGEX, MIN_AMNET_API_VERSION, fetchServerInfo, generateCardNumber, getGameName, performCardIn, validateCardId }; +type DecodedAccessCode = { + revision: number; + serial: number; +}; +declare function decodeAccessCode(code: string): DecodedAccessCode | null; +declare function encodeAccessCode(company: number, serial: number, rev?: number): string; + +export { type AMCardInRequest, AMNET_API_VERSION, AMNET_COMPANY_ID, type AMServerConnectionInfo, type AMServerInfo, type AMServerInstanceMetrics, type AMServerMetadata, CARD_ID_REGEX, decodeAccessCode as DecodeAMAccessCode, encodeAccessCode as EncodeAMAccessCode, MIN_AMNET_API_VERSION, fetchServerInfo, generateCardNumber, getGameName, performCardIn, validateCardId }; diff --git a/amnet-shared/dist/index.js b/amnet-shared/dist/index.js index fb2c6ec..99261a3 100644 --- a/amnet-shared/dist/index.js +++ b/amnet-shared/dist/index.js @@ -1,10 +1,12 @@ "use strict"; +var __create = Object.create; var __defProp = Object.defineProperty; var __defProps = Object.defineProperties; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropDescs = Object.getOwnPropertyDescriptors; var __getOwnPropNames = Object.getOwnPropertyNames; var __getOwnPropSymbols = Object.getOwnPropertySymbols; +var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; @@ -32,33 +34,24 @@ var __copyProps = (to, from, except, desc) => { } return to; }; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); -var __async = (__this, __arguments, generator) => { - return new Promise((resolve, reject) => { - var fulfilled = (value) => { - try { - step(generator.next(value)); - } catch (e) { - reject(e); - } - }; - var rejected = (value) => { - try { - step(generator.throw(value)); - } catch (e) { - reject(e); - } - }; - var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); - step((generator = generator.apply(__this, __arguments)).next()); - }); -}; // src/index.ts var src_exports = {}; __export(src_exports, { AMNET_API_VERSION: () => AMNET_API_VERSION, + AMNET_COMPANY_ID: () => AMNET_COMPANY_ID, CARD_ID_REGEX: () => CARD_ID_REGEX, + DecodeAMAccessCode: () => decodeAccessCode, + EncodeAMAccessCode: () => encodeAccessCode, MIN_AMNET_API_VERSION: () => MIN_AMNET_API_VERSION, fetchServerInfo: () => fetchServerInfo, generateCardNumber: () => generateCardNumber, @@ -71,59 +64,117 @@ module.exports = __toCommonJS(src_exports); // src/api.ts var AMNET_API_VERSION = 1; var MIN_AMNET_API_VERSION = 1; -function performCardIn(server, request, signal) { - return __async(this, null, function* () { - if (server.apiVersion < MIN_AMNET_API_VERSION || server.apiVersion > AMNET_API_VERSION) { - throw new Error("Unsupported server API version"); - } - try { - const response = yield fetch(`${new URL(server.serverAddress).origin}/amnet/signin`, { - method: "POST", - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify(request), - signal - }); - return response.ok; - } catch (e) { - return false; - } - }); +async function performCardIn(server, request, signal) { + if (server.apiVersion < MIN_AMNET_API_VERSION || server.apiVersion > AMNET_API_VERSION) { + throw new Error("Unsupported server API version"); + } + try { + const response = await fetch(`${new URL(server.serverAddress).origin}/amnet/signin`, { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify(request), + signal + }); + return response.ok; + } catch (e) { + return false; + } } -function fetchServerInfo(serverAddress, abort) { - return __async(this, null, function* () { - try { - const response = yield fetch(`${new URL(serverAddress).origin}/amnet/info`, { - signal: abort - }); - if (!response.ok) { - return null; - } - return __spreadProps(__spreadValues({}, yield response.json()), { - serverAddress - }); - } catch (e) { +async function fetchServerInfo(serverAddress, abort) { + try { + const response = await fetch(`${new URL(serverAddress).origin}/amnet/info`, { + signal: abort + }); + if (!response.ok) { return null; } - }); + return __spreadProps(__spreadValues({}, await response.json()), { + serverAddress + }); + } catch (e) { + return null; + } +} + +// src/card-encoding.ts +var import_lodash = __toESM(require("lodash")); +var import_big_integer = __toESM(require("big-integer")); +var sbtable = [[[45, 50, 94, 2, 66, 55, 56, 49, 99, 36, 60, 0, 73, 30, 39, 37, 80, 74, 85, 7, 12, 33, 93, 83, 95, 91, 98, 92, 23, 24, 79, 21, 31, 35, 71, 70, 4, 43, 1, 5, 88, 90, 69, 58, 76, 20, 84, 62, 77, 44, 61, 11, 16, 53, 82, 34, 13, 47, 48, 87, 89, 15, 64, 3, 54, 28, 57, 67, 9, 75, 32, 65, 96, 17, 18, 29, 10, 14, 42, 6, 46, 26, 40, 59, 63, 78, 68, 8, 86, 51, 25, 72, 38, 97, 19, 81, 27, 41, 52, 22], [26, 32, 34, 79, 42, 45, 4, 75, 22, 73, 64, 98, 49, 19, 5, 24, 6, 55, 63, 25, 40, 77, 54, 12, 9, 44, 83, 71, 0, 90, 97, 96, 81, 80, 3, 31, 56, 92, 17, 86, 72, 28, 36, 78, 62, 30, 7, 46, 87, 20, 82, 50, 35, 61, 41, 94, 68, 11, 1, 10, 74, 67, 85, 43, 57, 15, 95, 84, 65, 53, 69, 52, 76, 88, 2, 14, 37, 58, 93, 16, 59, 51, 18, 99, 39, 47, 89, 8, 13, 91, 38, 33, 70, 48, 23, 29, 27, 66, 60, 21], [28, 53, 30, 85, 0, 95, 70, 47, 40, 51, 94, 68, 99, 15, 5, 57, 1, 16, 80, 93, 91, 52, 2, 32, 48, 77, 41, 75, 22, 39, 50, 81, 79, 73, 97, 76, 60, 4, 59, 25, 78, 38, 46, 58, 90, 84, 56, 6, 83, 62, 42, 10, 64, 31, 36, 61, 65, 63, 18, 8, 9, 96, 55, 26, 89, 67, 3, 98, 35, 21, 13, 12, 88, 71, 37, 29, 87, 49, 24, 86, 92, 20, 44, 74, 69, 17, 82, 54, 27, 7, 23, 66, 11, 19, 34, 45, 14, 72, 43, 33], [32, 44, 86, 79, 38, 61, 25, 8, 5, 85, 78, 48, 33, 87, 84, 35, 64, 41, 60, 21, 96, 75, 6, 69, 39, 94, 3, 92, 37, 77, 15, 13, 29, 59, 76, 91, 65, 56, 73, 83, 27, 62, 89, 50, 18, 98, 81, 17, 71, 28, 82, 11, 30, 43, 34, 20, 70, 22, 46, 63, 52, 68, 90, 53, 36, 10, 74, 58, 40, 88, 9, 2, 42, 26, 24, 4, 95, 57, 31, 66, 0, 14, 49, 16, 99, 19, 51, 23, 47, 12, 93, 1, 7, 80, 45, 97, 55, 67, 72, 54], [58, 35, 42, 38, 63, 36, 68, 30, 81, 4, 87, 80, 32, 69, 37, 67, 1, 86, 61, 94, 27, 11, 84, 26, 59, 73, 53, 66, 72, 71, 39, 33, 99, 8, 43, 88, 17, 22, 89, 5, 46, 93, 98, 48, 55, 83, 34, 3, 0, 16, 12, 20, 78, 9, 2, 76, 54, 51, 25, 24, 62, 57, 92, 10, 50, 28, 79, 23, 52, 95, 44, 60, 74, 90, 19, 77, 21, 31, 96, 49, 64, 6, 29, 85, 18, 13, 82, 47, 56, 45, 15, 7, 97, 75, 40, 41, 65, 14, 70, 91], [81, 82, 66, 72, 83, 4, 20, 94, 53, 48, 77, 41, 67, 64, 9, 24, 36, 55, 14, 1, 78, 84, 49, 0, 44, 58, 70, 25, 28, 73, 8, 68, 88, 54, 86, 87, 23, 52, 27, 5, 97, 37, 62, 50, 3, 89, 92, 47, 30, 10, 29, 80, 22, 26, 11, 13, 69, 60, 38, 90, 85, 57, 12, 75, 46, 39, 74, 33, 42, 93, 98, 91, 59, 99, 18, 6, 17, 16, 32, 40, 63, 7, 76, 15, 43, 71, 51, 35, 2, 65, 34, 96, 19, 56, 79, 21, 95, 45, 61, 31], [25, 88, 7, 57, 98, 63, 86, 13, 69, 84, 56, 20, 68, 90, 35, 6, 43, 93, 95, 3, 75, 4, 9, 28, 31, 66, 21, 44, 30, 76, 11, 22, 49, 2, 77, 40, 85, 19, 82, 16, 97, 92, 39, 96, 18, 32, 42, 94, 70, 71, 54, 79, 67, 78, 37, 12, 99, 53, 61, 41, 87, 17, 1, 14, 52, 10, 47, 58, 27, 46, 36, 55, 26, 15, 33, 50, 72, 51, 89, 23, 74, 59, 64, 8, 83, 65, 80, 29, 38, 0, 60, 24, 45, 81, 48, 91, 73, 5, 34, 62], [73, 85, 37, 11, 80, 46, 16, 47, 88, 65, 94, 64, 12, 38, 98, 70, 95, 48, 92, 18, 56, 75, 58, 7, 81, 15, 74, 13, 5, 44, 27, 32, 76, 90, 33, 55, 26, 96, 77, 24, 0, 89, 8, 4, 28, 39, 71, 3, 10, 41, 91, 53, 31, 68, 36, 17, 69, 60, 34, 54, 93, 82, 21, 51, 87, 59, 61, 6, 1, 23, 30, 66, 43, 25, 35, 42, 49, 78, 62, 14, 19, 20, 29, 22, 52, 83, 63, 45, 2, 99, 50, 79, 72, 40, 84, 97, 86, 57, 67, 9], [48, 94, 17, 55, 59, 24, 72, 75, 38, 85, 34, 98, 95, 36, 49, 63, 57, 54, 29, 6, 52, 47, 73, 1, 82, 9, 66, 83, 31, 61, 78, 60, 97, 14, 87, 15, 90, 70, 53, 10, 27, 8, 3, 64, 51, 50, 68, 26, 22, 19, 42, 96, 67, 7, 25, 39, 92, 30, 58, 93, 21, 74, 41, 18, 65, 44, 88, 32, 71, 69, 12, 77, 13, 11, 99, 79, 37, 89, 81, 35, 86, 56, 45, 76, 46, 80, 62, 43, 16, 84, 40, 5, 33, 91, 4, 20, 0, 23, 28, 2], [61, 45, 79, 13, 59, 52, 6, 77, 3, 31, 70, 80, 26, 33, 36, 4, 27, 32, 24, 74, 29, 94, 16, 12, 76, 7, 88, 57, 78, 42, 97, 96, 43, 83, 92, 21, 51, 47, 49, 58, 90, 41, 93, 98, 35, 63, 86, 37, 5, 30, 28, 72, 25, 22, 60, 19, 39, 34, 68, 50, 8, 64, 46, 99, 84, 87, 69, 56, 53, 55, 44, 54, 89, 65, 85, 0, 71, 15, 17, 66, 81, 82, 91, 95, 62, 48, 38, 75, 40, 14, 9, 10, 73, 67, 18, 11, 23, 2, 20, 1]], [[95, 90, 61, 38, 18, 7, 83, 39, 79, 14, 48, 21, 81, 44, 78, 50, 56, 91, 12, 86, 99, 98, 43, 63, 73, 87, 42, 72, 15, 68, 97, 23, 88, 69, 2, 51, 19, 58, 59, 22, 20, 29, 82, 11, 75, 70, 9, 71, 27, 30, 47, 89, 92, 60, 46, 76, 93, 54, 1, 96, 94, 57, 40, 62, 41, 3, 28, 64, 37, 74, 67, 16, 65, 53, 5, 36, 24, 45, 32, 33, 10, 85, 26, 4, 84, 77, 35, 13, 6, 8, 17, 80, 25, 55, 52, 66, 49, 34, 0, 31], [18, 14, 70, 43, 74, 45, 94, 36, 40, 6, 31, 46, 15, 47, 13, 19, 72, 61, 99, 21, 17, 92, 88, 78, 73, 90, 0, 96, 20, 28, 25, 51, 50, 77, 29, 16, 54, 12, 58, 38, 71, 23, 42, 83, 65, 60, 10, 33, 22, 76, 95, 32, 57, 5, 59, 67, 68, 3, 91, 85, 26, 62, 41, 1, 53, 82, 34, 24, 87, 27, 89, 75, 56, 30, 81, 7, 8, 4, 64, 66, 9, 63, 35, 52, 80, 93, 69, 84, 55, 86, 48, 98, 79, 97, 49, 44, 39, 11, 37, 2], [76, 16, 73, 29, 34, 14, 36, 90, 58, 70, 46, 2, 91, 59, 63, 32, 84, 40, 78, 38, 19, 37, 0, 31, 8, 44, 94, 39, 10, 66, 42, 1, 21, 88, 82, 97, 33, 4, 99, 74, 89, 62, 25, 87, 77, 43, 50, 95, 64, 23, 47, 93, 65, 27, 12, 71, 9, 72, 60, 18, 7, 22, 79, 24, 96, 45, 49, 98, 57, 11, 5, 86, 3, 80, 26, 51, 17, 85, 54, 67, 48, 35, 53, 41, 28, 13, 20, 92, 83, 69, 6, 68, 75, 52, 61, 30, 81, 56, 15, 55], [0, 62, 25, 61, 56, 87, 54, 27, 75, 49, 18, 69, 17, 71, 40, 6, 90, 80, 23, 26, 68, 24, 10, 21, 47, 88, 36, 55, 15, 22, 48, 12, 83, 99, 46, 67, 59, 29, 58, 73, 78, 97, 19, 33, 51, 98, 65, 91, 16, 4, 63, 41, 3, 70, 30, 8, 89, 34, 72, 60, 9, 95, 50, 2, 66, 53, 44, 85, 28, 5, 92, 43, 64, 96, 74, 77, 45, 52, 14, 42, 1, 32, 20, 76, 86, 82, 94, 7, 39, 13, 31, 11, 84, 93, 35, 37, 38, 57, 79, 81], [78, 8, 59, 25, 69, 15, 63, 3, 57, 5, 50, 96, 41, 66, 80, 81, 7, 45, 60, 74, 79, 32, 27, 58, 56, 82, 11, 47, 17, 85, 53, 40, 95, 84, 76, 92, 18, 71, 64, 51, 67, 91, 22, 34, 65, 31, 73, 93, 68, 75, 77, 37, 43, 12, 20, 99, 2, 1, 19, 35, 44, 55, 90, 9, 0, 6, 33, 89, 24, 14, 97, 10, 48, 61, 23, 54, 98, 86, 88, 94, 46, 70, 28, 36, 16, 30, 4, 39, 26, 38, 87, 52, 83, 49, 62, 42, 72, 29, 13, 21], [20, 88, 35, 5, 85, 98, 11, 78, 62, 83, 79, 80, 40, 14, 91, 8, 7, 69, 71, 61, 49, 38, 64, 82, 0, 47, 84, 95, 2, 50, 77, 41, 97, 36, 33, 81, 46, 21, 73, 48, 31, 92, 54, 89, 87, 60, 90, 10, 76, 45, 99, 52, 3, 44, 39, 43, 18, 16, 63, 12, 28, 70, 86, 6, 4, 24, 59, 30, 96, 65, 22, 56, 74, 23, 32, 75, 53, 93, 15, 67, 66, 17, 29, 25, 42, 37, 57, 9, 72, 68, 26, 34, 27, 51, 19, 58, 55, 94, 13, 1], [72, 53, 79, 61, 30, 33, 25, 75, 8, 55, 14, 6, 22, 93, 18, 77, 4, 74, 44, 36, 82, 94, 2, 5, 62, 54, 28, 3, 63, 97, 65, 7, 84, 73, 39, 92, 67, 91, 24, 45, 15, 81, 19, 78, 80, 64, 34, 43, 32, 87, 52, 95, 89, 37, 10, 23, 20, 68, 42, 47, 59, 58, 49, 71, 40, 85, 13, 31, 12, 57, 96, 90, 56, 41, 48, 9, 99, 35, 98, 26, 1, 46, 11, 69, 27, 83, 60, 50, 21, 70, 0, 88, 29, 86, 76, 16, 66, 38, 51, 17], [3, 68, 86, 94, 81, 90, 85, 32, 65, 52, 50, 42, 80, 78, 26, 93, 57, 58, 69, 82, 92, 27, 11, 67, 95, 41, 21, 88, 35, 17, 19, 15, 22, 62, 40, 76, 63, 59, 37, 55, 75, 9, 24, 89, 56, 53, 23, 60, 25, 6, 1, 71, 16, 7, 10, 44, 8, 12, 87, 64, 54, 72, 20, 33, 70, 79, 14, 97, 4, 99, 45, 39, 2, 77, 98, 74, 49, 30, 13, 61, 66, 73, 47, 43, 96, 83, 18, 91, 28, 48, 38, 34, 31, 36, 29, 0, 84, 46, 5, 51], [71, 84, 25, 70, 77, 56, 61, 47, 72, 27, 54, 89, 9, 46, 95, 59, 42, 98, 63, 69, 48, 16, 97, 14, 66, 83, 87, 68, 17, 2, 15, 79, 31, 1, 57, 12, 33, 36, 0, 40, 21, 50, 76, 41, 10, 13, 44, 5, 53, 4, 38, 35, 55, 96, 81, 91, 90, 64, 34, 51, 6, 28, 32, 39, 75, 18, 93, 24, 78, 7, 52, 43, 49, 37, 74, 8, 58, 65, 92, 67, 85, 20, 94, 88, 80, 86, 19, 60, 3, 11, 30, 45, 29, 22, 26, 73, 99, 23, 62, 82], [67, 91, 46, 33, 76, 3, 96, 23, 9, 68, 49, 63, 1, 92, 58, 62, 69, 77, 70, 75, 71, 94, 51, 55, 50, 52, 0, 88, 18, 16, 73, 6, 12, 87, 93, 79, 84, 57, 48, 41, 20, 47, 15, 42, 54, 81, 7, 30, 34, 56, 29, 8, 98, 5, 45, 64, 83, 74, 22, 82, 80, 95, 2, 72, 44, 31, 86, 78, 13, 10, 28, 89, 60, 35, 61, 19, 14, 85, 4, 43, 11, 40, 26, 99, 39, 32, 90, 17, 24, 37, 36, 21, 25, 53, 97, 59, 27, 38, 65, 66]], [[40, 77, 66, 65, 97, 96, 58, 64, 69, 30, 0, 3, 17, 33, 51, 23, 59, 10, 7, 20, 29, 49, 71, 13, 46, 4, 79, 92, 47, 18, 72, 57, 73, 80, 56, 28, 78, 67, 9, 87, 43, 31, 22, 62, 75, 99, 19, 63, 35, 93, 12, 98, 25, 26, 37, 89, 86, 15, 55, 50, 91, 38, 42, 81, 2, 94, 70, 68, 45, 88, 76, 74, 11, 24, 41, 60, 85, 21, 44, 32, 82, 61, 52, 83, 53, 36, 48, 1, 95, 27, 14, 84, 54, 6, 39, 34, 90, 5, 16, 8], [44, 69, 53, 28, 18, 11, 23, 65, 96, 25, 93, 42, 72, 58, 95, 56, 32, 21, 78, 77, 51, 34, 47, 7, 66, 13, 88, 67, 92, 98, 10, 50, 90, 45, 49, 9, 99, 94, 22, 24, 5, 85, 33, 3, 73, 35, 52, 63, 2, 76, 87, 43, 81, 83, 55, 61, 70, 75, 16, 14, 8, 71, 57, 40, 86, 39, 80, 79, 26, 48, 59, 4, 82, 0, 36, 12, 54, 38, 64, 17, 91, 27, 62, 84, 37, 74, 68, 1, 6, 19, 29, 46, 60, 30, 89, 15, 20, 97, 41, 31], [53, 22, 34, 16, 72, 71, 14, 94, 12, 59, 39, 6, 38, 68, 21, 93, 32, 46, 69, 55, 10, 8, 75, 60, 9, 13, 99, 90, 63, 49, 28, 92, 41, 7, 54, 83, 27, 91, 47, 43, 31, 74, 42, 40, 18, 81, 76, 84, 78, 52, 1, 26, 51, 2, 58, 11, 65, 67, 15, 23, 0, 79, 96, 4, 29, 62, 89, 66, 50, 98, 56, 61, 30, 88, 35, 25, 77, 70, 3, 44, 95, 80, 19, 64, 57, 37, 82, 85, 73, 48, 5, 86, 24, 36, 33, 97, 20, 45, 17, 87], [95, 36, 70, 82, 23, 55, 13, 51, 83, 38, 40, 18, 84, 74, 56, 85, 58, 14, 19, 69, 34, 59, 97, 89, 52, 77, 9, 91, 68, 16, 27, 88, 6, 73, 43, 5, 10, 66, 4, 61, 44, 46, 72, 50, 96, 21, 33, 15, 32, 0, 63, 92, 24, 39, 29, 49, 30, 64, 12, 26, 67, 53, 37, 2, 47, 65, 3, 20, 75, 78, 99, 87, 98, 1, 81, 90, 62, 54, 22, 35, 45, 25, 86, 48, 17, 60, 94, 8, 42, 31, 76, 28, 11, 57, 93, 41, 7, 71, 80, 79], [60, 24, 69, 92, 15, 36, 11, 98, 93, 27, 51, 35, 89, 74, 87, 4, 81, 94, 23, 84, 56, 3, 42, 18, 75, 73, 46, 86, 55, 99, 0, 88, 1, 26, 76, 70, 65, 67, 20, 13, 7, 45, 77, 17, 9, 78, 54, 48, 32, 40, 47, 31, 2, 6, 90, 10, 8, 50, 52, 38, 91, 14, 53, 12, 85, 44, 21, 80, 68, 33, 30, 57, 61, 63, 79, 41, 29, 39, 58, 5, 62, 96, 28, 71, 82, 34, 25, 43, 22, 95, 72, 37, 64, 16, 97, 59, 66, 83, 49, 19], [80, 0, 40, 36, 68, 18, 67, 14, 20, 21, 10, 17, 43, 3, 98, 81, 94, 55, 1, 39, 72, 51, 41, 66, 58, 2, 61, 78, 15, 99, 57, 52, 25, 87, 12, 65, 74, 64, 34, 37, 44, 30, 88, 79, 33, 27, 56, 35, 73, 42, 29, 54, 24, 5, 53, 16, 31, 4, 19, 84, 59, 50, 26, 63, 48, 28, 60, 45, 92, 82, 49, 77, 97, 76, 22, 86, 47, 62, 83, 70, 93, 38, 11, 13, 89, 75, 85, 69, 9, 46, 96, 90, 6, 91, 8, 7, 32, 71, 95, 23], [32, 14, 35, 84, 89, 11, 52, 34, 79, 65, 2, 88, 49, 29, 31, 94, 1, 74, 97, 51, 99, 37, 10, 47, 57, 81, 73, 76, 63, 58, 0, 38, 39, 27, 33, 8, 20, 21, 46, 66, 92, 36, 6, 30, 53, 17, 48, 64, 55, 28, 85, 50, 54, 12, 80, 41, 77, 69, 93, 9, 91, 13, 42, 75, 68, 86, 19, 3, 16, 78, 87, 45, 43, 56, 59, 70, 40, 24, 83, 98, 62, 5, 96, 4, 67, 60, 26, 23, 72, 95, 44, 82, 90, 25, 7, 18, 15, 22, 71, 61], [78, 53, 89, 93, 7, 84, 81, 95, 64, 50, 58, 35, 59, 63, 20, 76, 80, 8, 23, 28, 79, 52, 55, 48, 66, 17, 60, 65, 25, 29, 30, 9, 73, 39, 69, 3, 98, 22, 15, 87, 4, 37, 43, 5, 11, 83, 91, 40, 51, 33, 12, 92, 6, 67, 41, 94, 45, 88, 56, 36, 0, 68, 19, 54, 70, 26, 72, 77, 32, 34, 97, 71, 99, 75, 61, 31, 86, 82, 46, 2, 44, 96, 49, 74, 10, 62, 24, 16, 1, 38, 27, 14, 18, 85, 13, 57, 90, 21, 42, 47], [26, 30, 60, 78, 75, 48, 73, 52, 51, 57, 63, 90, 27, 36, 83, 81, 41, 44, 82, 37, 10, 64, 91, 4, 59, 95, 21, 24, 77, 56, 43, 38, 62, 55, 92, 0, 94, 20, 86, 66, 35, 80, 53, 25, 17, 18, 49, 76, 23, 89, 13, 15, 68, 8, 22, 46, 1, 42, 3, 34, 98, 88, 69, 31, 50, 65, 84, 32, 47, 61, 9, 19, 99, 85, 11, 79, 70, 2, 74, 14, 87, 7, 72, 5, 12, 58, 67, 39, 16, 71, 54, 6, 45, 93, 96, 28, 97, 29, 33, 40], [77, 66, 20, 30, 42, 23, 0, 49, 7, 64, 54, 27, 96, 97, 39, 84, 2, 51, 71, 17, 4, 92, 94, 95, 67, 69, 73, 75, 86, 37, 74, 50, 11, 79, 29, 15, 56, 43, 22, 83, 81, 61, 14, 3, 36, 44, 58, 10, 62, 19, 72, 80, 65, 24, 18, 5, 40, 90, 33, 9, 47, 76, 8, 59, 35, 91, 99, 13, 70, 88, 55, 16, 31, 28, 85, 45, 60, 25, 87, 82, 93, 12, 34, 68, 63, 26, 46, 48, 21, 52, 1, 78, 98, 38, 6, 32, 89, 53, 41, 57]], [[77, 99, 47, 64, 26, 63, 50, 81, 23, 45, 49, 36, 43, 56, 39, 12, 86, 62, 2, 98, 60, 94, 75, 9, 20, 74, 48, 28, 4, 84, 61, 31, 54, 15, 40, 7, 87, 38, 80, 6, 37, 46, 1, 55, 88, 73, 95, 33, 92, 18, 25, 66, 57, 27, 42, 35, 91, 51, 13, 90, 41, 83, 68, 53, 19, 3, 72, 89, 65, 70, 71, 8, 97, 59, 96, 52, 79, 22, 14, 69, 32, 93, 58, 29, 21, 76, 34, 5, 17, 67, 11, 78, 16, 0, 44, 30, 10, 24, 85, 82], [58, 62, 7, 39, 34, 69, 67, 73, 37, 59, 71, 66, 46, 57, 44, 51, 26, 85, 11, 28, 54, 47, 82, 21, 83, 76, 94, 16, 90, 95, 75, 55, 6, 22, 72, 74, 50, 40, 10, 19, 8, 53, 33, 98, 45, 87, 91, 13, 35, 70, 14, 38, 84, 99, 56, 30, 12, 89, 25, 97, 63, 65, 81, 92, 18, 29, 80, 61, 93, 43, 49, 52, 23, 31, 32, 4, 48, 3, 0, 78, 96, 64, 41, 88, 24, 5, 9, 15, 77, 86, 68, 1, 17, 79, 60, 36, 42, 2, 20, 27], [58, 62, 7, 39, 34, 69, 67, 73, 37, 59, 71, 66, 46, 57, 44, 51, 26, 85, 11, 28, 54, 47, 82, 21, 83, 76, 94, 16, 90, 95, 75, 55, 6, 22, 72, 74, 50, 40, 10, 19, 8, 53, 33, 98, 45, 87, 91, 13, 35, 70, 14, 38, 84, 99, 56, 30, 12, 89, 25, 97, 63, 65, 81, 92, 18, 29, 80, 61, 93, 43, 49, 52, 23, 31, 32, 4, 48, 3, 0, 78, 96, 64, 41, 88, 24, 5, 9, 15, 77, 86, 68, 1, 17, 79, 60, 36, 42, 2, 20, 27], [91, 43, 77, 69, 16, 96, 47, 34, 49, 15, 73, 84, 85, 22, 88, 95, 2, 80, 57, 33, 32, 41, 25, 14, 58, 9, 20, 65, 48, 97, 79, 89, 66, 63, 46, 83, 19, 92, 55, 18, 38, 0, 21, 50, 45, 62, 29, 26, 52, 28, 8, 6, 75, 86, 44, 81, 36, 90, 70, 30, 71, 39, 82, 87, 76, 1, 23, 5, 60, 98, 67, 53, 93, 72, 42, 74, 51, 4, 31, 59, 61, 7, 24, 56, 37, 54, 3, 11, 27, 40, 10, 64, 94, 35, 78, 68, 99, 17, 12, 13], [73, 70, 39, 9, 77, 61, 80, 10, 13, 92, 38, 4, 48, 57, 25, 72, 49, 51, 99, 97, 85, 36, 22, 15, 8, 34, 33, 76, 26, 45, 83, 59, 28, 96, 90, 47, 68, 88, 44, 6, 98, 12, 24, 46, 2, 54, 5, 58, 30, 63, 1, 18, 93, 67, 7, 50, 31, 16, 40, 56, 23, 86, 11, 52, 55, 75, 71, 27, 37, 87, 84, 65, 82, 29, 0, 14, 41, 35, 64, 21, 3, 95, 17, 60, 81, 74, 19, 91, 94, 32, 69, 53, 42, 89, 79, 43, 66, 20, 78, 62], [23, 78, 67, 41, 9, 77, 59, 72, 25, 82, 65, 89, 28, 37, 54, 70, 98, 33, 31, 44, 6, 17, 26, 73, 21, 36, 4, 2, 79, 95, 81, 50, 18, 34, 61, 0, 93, 24, 8, 66, 55, 62, 56, 30, 84, 58, 7, 88, 85, 64, 97, 39, 49, 3, 20, 42, 80, 96, 13, 63, 43, 60, 47, 90, 51, 14, 53, 1, 86, 57, 12, 19, 10, 45, 38, 68, 35, 87, 75, 5, 69, 27, 32, 91, 11, 99, 46, 29, 92, 22, 76, 15, 16, 48, 71, 74, 52, 94, 83, 40], [87, 89, 35, 98, 44, 21, 29, 83, 0, 77, 34, 60, 85, 64, 28, 58, 39, 36, 95, 22, 67, 30, 86, 57, 5, 81, 84, 26, 53, 62, 13, 90, 2, 88, 4, 46, 97, 73, 79, 43, 59, 72, 54, 32, 82, 10, 55, 38, 47, 12, 51, 1, 76, 48, 7, 14, 23, 20, 31, 18, 17, 15, 37, 42, 49, 68, 25, 93, 91, 40, 92, 50, 52, 70, 33, 80, 19, 96, 61, 75, 71, 99, 74, 9, 41, 66, 63, 8, 6, 24, 65, 56, 45, 69, 27, 16, 3, 78, 94, 11], [99, 6, 38, 11, 34, 28, 86, 57, 55, 78, 25, 95, 13, 63, 91, 14, 83, 1, 45, 17, 93, 76, 46, 79, 27, 85, 96, 58, 59, 44, 87, 65, 97, 22, 0, 51, 49, 52, 47, 67, 24, 88, 62, 50, 72, 3, 31, 82, 68, 42, 81, 23, 32, 98, 73, 60, 37, 54, 41, 80, 66, 18, 92, 4, 5, 94, 77, 39, 69, 16, 35, 7, 40, 30, 61, 26, 56, 64, 9, 71, 19, 21, 75, 84, 43, 53, 33, 29, 74, 36, 90, 2, 20, 15, 10, 48, 89, 70, 8, 12], [4, 28, 8, 72, 74, 31, 66, 0, 27, 54, 61, 23, 51, 73, 96, 32, 98, 41, 88, 95, 36, 22, 13, 38, 70, 84, 12, 7, 10, 60, 68, 33, 11, 35, 79, 42, 16, 62, 57, 24, 44, 94, 25, 48, 86, 97, 87, 49, 92, 71, 21, 1, 81, 5, 17, 85, 6, 75, 80, 34, 20, 30, 90, 69, 18, 89, 93, 56, 91, 40, 77, 52, 53, 14, 65, 45, 76, 2, 9, 29, 3, 26, 59, 55, 83, 46, 67, 47, 39, 50, 58, 63, 99, 15, 37, 43, 82, 64, 78, 19], [11, 94, 1, 93, 59, 91, 89, 51, 61, 81, 13, 29, 27, 58, 26, 3, 53, 69, 73, 21, 39, 12, 5, 18, 98, 74, 9, 96, 28, 65, 23, 4, 41, 72, 88, 34, 83, 54, 7, 15, 38, 84, 90, 52, 77, 48, 64, 60, 20, 79, 19, 66, 97, 30, 87, 45, 40, 36, 14, 86, 32, 33, 57, 16, 43, 42, 50, 10, 44, 76, 37, 6, 56, 31, 68, 99, 2, 17, 22, 35, 92, 85, 46, 75, 71, 67, 95, 78, 8, 63, 82, 25, 80, 24, 47, 49, 0, 70, 62, 55]], [[93, 99, 51, 49, 71, 64, 35, 42, 88, 17, 67, 92, 4, 12, 68, 13, 47, 7, 56, 14, 19, 53, 80, 85, 23, 5, 39, 6, 26, 89, 27, 54, 63, 73, 70, 33, 36, 3, 86, 58, 18, 2, 25, 21, 10, 34, 81, 94, 75, 98, 37, 46, 9, 95, 60, 74, 87, 78, 20, 24, 57, 0, 30, 83, 72, 61, 11, 32, 43, 82, 65, 69, 59, 62, 38, 76, 22, 16, 15, 45, 48, 79, 50, 90, 40, 1, 29, 77, 91, 31, 84, 96, 8, 52, 97, 41, 28, 44, 55, 66], [64, 71, 19, 27, 43, 95, 77, 16, 35, 81, 74, 29, 6, 86, 18, 37, 99, 46, 15, 87, 28, 63, 1, 65, 3, 79, 50, 12, 78, 84, 8, 88, 98, 69, 42, 0, 56, 32, 92, 83, 30, 59, 80, 9, 20, 89, 47, 23, 33, 34, 82, 24, 97, 60, 22, 90, 51, 76, 10, 94, 21, 91, 70, 49, 66, 14, 75, 2, 7, 13, 40, 93, 48, 36, 53, 72, 67, 62, 85, 17, 57, 58, 5, 25, 41, 31, 44, 73, 61, 26, 38, 54, 96, 39, 4, 68, 11, 52, 45, 55], [49, 88, 62, 73, 34, 15, 95, 68, 98, 5, 61, 35, 23, 45, 78, 64, 97, 70, 96, 65, 74, 16, 94, 37, 80, 56, 48, 28, 59, 1, 75, 4, 71, 47, 83, 93, 69, 21, 60, 38, 39, 19, 40, 82, 55, 7, 52, 18, 43, 54, 2, 77, 66, 90, 44, 32, 89, 3, 24, 6, 99, 17, 86, 29, 30, 22, 50, 81, 41, 42, 0, 9, 53, 58, 57, 25, 20, 12, 26, 51, 92, 87, 36, 31, 85, 63, 14, 8, 91, 11, 79, 27, 76, 84, 67, 33, 72, 13, 10, 46], [45, 15, 69, 88, 51, 28, 34, 80, 30, 42, 38, 90, 47, 17, 84, 49, 21, 55, 46, 96, 37, 97, 72, 85, 94, 29, 10, 66, 76, 82, 18, 86, 87, 25, 43, 92, 19, 89, 2, 93, 98, 40, 48, 53, 75, 99, 11, 62, 12, 60, 3, 24, 4, 6, 63, 81, 41, 20, 68, 9, 57, 52, 0, 44, 14, 16, 91, 27, 79, 73, 59, 61, 78, 7, 67, 1, 70, 83, 74, 64, 26, 95, 54, 65, 33, 39, 5, 50, 36, 35, 13, 23, 58, 71, 32, 77, 31, 22, 56, 8], [41, 43, 66, 69, 22, 23, 71, 2, 33, 38, 86, 51, 55, 59, 82, 96, 45, 97, 73, 34, 70, 21, 8, 58, 84, 90, 24, 25, 32, 78, 17, 27, 47, 68, 37, 72, 63, 77, 87, 89, 50, 16, 83, 54, 12, 56, 64, 62, 39, 92, 91, 53, 29, 76, 52, 94, 42, 81, 20, 98, 40, 79, 44, 10, 31, 0, 57, 19, 75, 67, 93, 6, 7, 11, 74, 26, 85, 5, 1, 49, 4, 61, 95, 3, 36, 9, 14, 13, 15, 48, 80, 46, 30, 65, 99, 18, 60, 88, 35, 28], [5, 56, 10, 81, 42, 35, 12, 94, 34, 13, 8, 0, 49, 17, 7, 69, 30, 1, 65, 14, 39, 2, 58, 76, 54, 31, 50, 46, 11, 27, 22, 61, 74, 64, 91, 18, 48, 63, 71, 57, 95, 87, 47, 67, 77, 53, 88, 23, 3, 83, 29, 86, 43, 60, 70, 78, 21, 99, 36, 66, 20, 62, 85, 4, 80, 98, 79, 82, 97, 15, 96, 52, 51, 6, 40, 93, 28, 41, 72, 45, 68, 16, 32, 75, 84, 89, 26, 55, 9, 59, 73, 24, 25, 33, 37, 19, 90, 44, 38, 92], [62, 28, 97, 69, 65, 46, 27, 96, 29, 73, 89, 42, 16, 36, 44, 63, 80, 60, 11, 54, 87, 92, 75, 81, 57, 26, 70, 49, 76, 10, 95, 84, 56, 22, 20, 9, 21, 61, 1, 47, 34, 19, 66, 0, 33, 91, 45, 3, 2, 90, 88, 43, 51, 12, 7, 18, 24, 85, 86, 8, 78, 38, 55, 30, 6, 35, 15, 93, 14, 99, 98, 82, 52, 74, 4, 50, 79, 37, 77, 17, 41, 71, 58, 53, 68, 39, 67, 25, 5, 83, 94, 13, 48, 32, 23, 59, 31, 64, 72, 40], [93, 20, 18, 22, 94, 27, 39, 59, 4, 11, 6, 75, 98, 62, 63, 81, 44, 53, 25, 95, 74, 57, 76, 5, 70, 60, 99, 12, 56, 85, 41, 67, 46, 19, 28, 1, 23, 80, 32, 68, 31, 51, 82, 55, 78, 77, 58, 69, 38, 87, 91, 79, 96, 52, 47, 73, 83, 89, 65, 3, 92, 14, 43, 66, 61, 37, 84, 54, 33, 88, 35, 71, 42, 90, 72, 50, 7, 40, 0, 24, 9, 29, 49, 45, 15, 21, 86, 64, 16, 36, 34, 17, 26, 10, 2, 13, 8, 97, 30, 48], [33, 3, 7, 64, 74, 23, 42, 25, 68, 85, 5, 8, 83, 17, 56, 79, 70, 73, 15, 6, 36, 18, 94, 97, 72, 22, 11, 96, 40, 58, 66, 49, 62, 86, 78, 67, 26, 98, 90, 48, 14, 2, 82, 91, 9, 76, 69, 44, 50, 47, 53, 71, 92, 12, 59, 37, 19, 60, 39, 13, 77, 16, 55, 41, 84, 31, 65, 27, 99, 51, 4, 45, 80, 46, 57, 21, 28, 81, 54, 29, 61, 88, 52, 43, 75, 20, 1, 10, 0, 35, 30, 32, 95, 38, 63, 87, 89, 24, 34, 93], [37, 25, 50, 66, 86, 23, 76, 27, 83, 97, 89, 35, 32, 41, 33, 87, 14, 59, 48, 61, 52, 3, 73, 67, 24, 56, 93, 22, 7, 70, 77, 28, 91, 30, 68, 29, 60, 8, 94, 13, 43, 17, 88, 57, 81, 34, 15, 95, 79, 96, 72, 84, 55, 46, 58, 10, 12, 38, 11, 99, 53, 42, 65, 44, 31, 21, 82, 6, 92, 26, 51, 5, 39, 63, 40, 9, 49, 18, 78, 80, 71, 74, 90, 2, 54, 47, 85, 75, 69, 45, 0, 16, 19, 1, 64, 98, 4, 62, 36, 20]]]; +var sbtable2 = [[3, 5, 8, 6, 0, 9, 7, 1, 2, 4], [1, 3, 9, 4, 2, 0, 8, 7, 5, 6], [0, 5, 1, 7, 9, 8, 6, 2, 3, 4], [6, 4, 7, 8, 3, 0, 9, 5, 2, 1], [2, 3, 8, 1, 9, 7, 6, 0, 4, 5], [3, 4, 1, 7, 2, 8, 6, 0, 9, 5], [6, 1, 8, 3, 0, 4, 2, 7, 5, 9], [9, 3, 7, 5, 2, 4, 0, 6, 1, 8], [6, 1, 5, 3, 0, 8, 7, 4, 2, 9], [9, 7, 4, 5, 1, 2, 3, 0, 8, 6]]; +var ibtable = [[[11, 38, 3, 63, 36, 39, 79, 19, 87, 68, 76, 51, 20, 56, 77, 61, 52, 73, 74, 94, 45, 31, 99, 28, 29, 90, 81, 96, 65, 75, 13, 32, 70, 21, 55, 33, 9, 15, 92, 14, 82, 97, 78, 37, 49, 0, 80, 57, 58, 7, 1, 89, 98, 53, 64, 5, 6, 66, 43, 83, 10, 50, 47, 84, 62, 71, 4, 67, 86, 42, 35, 34, 91, 12, 17, 69, 44, 48, 85, 30, 16, 95, 54, 23, 46, 18, 88, 59, 40, 60, 41, 25, 27, 22, 2, 24, 72, 93, 26, 8], [28, 58, 74, 34, 6, 14, 16, 46, 87, 24, 59, 57, 23, 88, 75, 65, 79, 38, 82, 13, 49, 99, 8, 94, 15, 19, 0, 96, 41, 95, 45, 35, 1, 91, 2, 52, 42, 76, 90, 84, 20, 54, 4, 63, 25, 5, 47, 85, 93, 12, 51, 81, 71, 69, 22, 17, 36, 64, 77, 80, 98, 53, 44, 18, 10, 68, 97, 61, 56, 70, 92, 27, 40, 9, 60, 7, 72, 21, 43, 3, 33, 32, 50, 26, 67, 62, 39, 48, 73, 86, 29, 89, 37, 78, 55, 66, 31, 30, 11, 83], [4, 16, 22, 66, 37, 14, 47, 89, 59, 60, 51, 92, 71, 70, 96, 13, 17, 85, 58, 93, 81, 69, 28, 90, 78, 39, 63, 88, 0, 75, 2, 53, 23, 99, 94, 68, 54, 74, 41, 29, 8, 26, 50, 98, 82, 95, 42, 7, 24, 77, 30, 9, 21, 1, 87, 62, 46, 15, 43, 38, 36, 55, 49, 57, 52, 56, 91, 65, 11, 84, 6, 73, 97, 33, 83, 27, 35, 25, 40, 32, 18, 31, 86, 48, 45, 3, 79, 76, 72, 64, 44, 20, 80, 19, 10, 5, 61, 34, 67, 12], [80, 91, 71, 26, 75, 8, 22, 92, 7, 70, 65, 51, 89, 31, 81, 30, 83, 47, 44, 85, 55, 19, 57, 87, 74, 6, 73, 40, 49, 32, 52, 78, 0, 12, 54, 15, 64, 28, 4, 24, 68, 17, 72, 53, 1, 94, 58, 88, 11, 82, 43, 86, 60, 63, 99, 96, 37, 77, 67, 33, 18, 5, 41, 59, 16, 36, 79, 97, 61, 23, 56, 48, 98, 38, 66, 21, 34, 29, 10, 3, 93, 46, 50, 39, 14, 9, 2, 13, 69, 42, 62, 35, 27, 90, 25, 76, 20, 95, 45, 84], [48, 16, 54, 47, 9, 39, 81, 91, 33, 53, 63, 21, 50, 85, 97, 90, 49, 36, 84, 74, 51, 76, 37, 67, 59, 58, 23, 20, 65, 82, 7, 77, 12, 31, 46, 1, 5, 14, 3, 30, 94, 95, 2, 34, 70, 89, 40, 87, 43, 79, 64, 57, 68, 26, 56, 44, 88, 61, 0, 24, 71, 18, 60, 4, 80, 96, 27, 15, 6, 13, 98, 29, 28, 25, 72, 93, 55, 75, 52, 66, 11, 8, 86, 45, 22, 83, 17, 10, 35, 38, 73, 99, 62, 41, 19, 69, 78, 92, 42, 32], [23, 19, 88, 44, 5, 39, 75, 81, 30, 14, 49, 54, 62, 55, 18, 83, 77, 76, 74, 92, 6, 95, 52, 36, 15, 27, 53, 38, 28, 50, 48, 99, 78, 67, 90, 87, 16, 41, 58, 65, 79, 11, 68, 84, 24, 97, 64, 47, 9, 22, 43, 86, 37, 8, 33, 17, 93, 61, 25, 72, 57, 98, 42, 80, 13, 89, 2, 12, 31, 56, 26, 85, 3, 29, 66, 63, 82, 10, 20, 94, 51, 0, 1, 4, 21, 60, 34, 35, 32, 45, 59, 71, 46, 69, 7, 96, 91, 40, 70, 73], [89, 62, 33, 19, 21, 97, 15, 2, 83, 22, 65, 30, 55, 7, 63, 73, 39, 61, 44, 37, 11, 26, 31, 79, 91, 0, 72, 68, 23, 87, 28, 24, 45, 74, 98, 14, 70, 54, 88, 42, 35, 59, 46, 16, 27, 92, 69, 66, 94, 32, 75, 77, 64, 57, 50, 71, 10, 3, 67, 81, 90, 58, 99, 5, 82, 85, 25, 52, 12, 8, 48, 49, 76, 96, 80, 20, 29, 34, 53, 51, 86, 93, 38, 84, 9, 36, 6, 60, 1, 78, 13, 95, 41, 17, 47, 18, 43, 40, 4, 56], [40, 68, 88, 47, 43, 28, 67, 23, 42, 99, 48, 3, 12, 27, 79, 25, 6, 55, 19, 80, 81, 62, 83, 69, 39, 73, 36, 30, 44, 82, 70, 52, 31, 34, 58, 74, 54, 2, 13, 45, 93, 49, 75, 72, 29, 87, 5, 7, 17, 76, 90, 63, 84, 51, 59, 35, 20, 97, 22, 65, 57, 66, 78, 86, 11, 9, 71, 98, 53, 56, 15, 46, 92, 0, 26, 21, 32, 38, 77, 91, 4, 24, 61, 85, 94, 1, 96, 64, 8, 41, 33, 50, 18, 60, 10, 16, 37, 95, 14, 89], [96, 23, 99, 42, 94, 91, 19, 53, 41, 25, 39, 73, 70, 72, 33, 35, 88, 2, 63, 49, 95, 60, 48, 97, 5, 54, 47, 40, 98, 18, 57, 28, 67, 92, 10, 79, 13, 76, 8, 55, 90, 62, 50, 87, 65, 82, 84, 21, 0, 14, 45, 44, 20, 38, 17, 3, 81, 16, 58, 4, 31, 29, 86, 15, 43, 64, 26, 52, 46, 69, 37, 68, 6, 22, 61, 7, 83, 71, 30, 75, 85, 78, 24, 27, 89, 9, 80, 34, 66, 77, 36, 93, 56, 59, 1, 12, 51, 32, 11, 74], [75, 99, 97, 8, 15, 48, 6, 25, 60, 90, 91, 95, 23, 3, 89, 77, 22, 78, 94, 55, 98, 35, 53, 96, 18, 52, 12, 16, 50, 20, 49, 9, 17, 13, 57, 44, 14, 47, 86, 56, 88, 41, 29, 32, 70, 1, 62, 37, 85, 38, 59, 36, 5, 68, 71, 69, 67, 27, 39, 4, 54, 0, 84, 45, 61, 73, 79, 93, 58, 66, 10, 76, 51, 92, 19, 87, 24, 7, 28, 2, 11, 80, 81, 33, 64, 74, 46, 65, 26, 72, 40, 82, 34, 42, 21, 83, 31, 30, 43, 63]], [[98, 58, 34, 65, 83, 74, 88, 5, 89, 46, 80, 43, 18, 87, 9, 28, 71, 90, 4, 36, 40, 11, 39, 31, 76, 92, 82, 48, 66, 41, 49, 99, 78, 79, 97, 86, 75, 68, 3, 7, 62, 64, 26, 22, 13, 77, 54, 50, 10, 96, 15, 35, 94, 73, 57, 93, 16, 61, 37, 38, 53, 2, 63, 23, 67, 72, 95, 70, 29, 33, 45, 47, 27, 24, 69, 44, 55, 85, 14, 8, 91, 12, 42, 6, 84, 81, 19, 25, 32, 51, 1, 17, 52, 56, 60, 0, 59, 30, 21, 20], [26, 63, 99, 57, 77, 53, 9, 75, 76, 80, 46, 97, 37, 14, 1, 12, 35, 20, 0, 15, 28, 19, 48, 41, 67, 30, 60, 69, 29, 34, 73, 10, 51, 47, 66, 82, 7, 98, 39, 96, 8, 62, 42, 3, 95, 5, 11, 13, 90, 94, 32, 31, 83, 64, 36, 88, 72, 52, 38, 54, 45, 17, 61, 81, 78, 44, 79, 55, 56, 86, 2, 40, 16, 24, 4, 71, 49, 33, 23, 92, 84, 74, 65, 43, 87, 59, 89, 68, 22, 70, 25, 58, 21, 85, 6, 50, 27, 93, 91, 18], [22, 31, 11, 72, 37, 70, 90, 60, 24, 56, 28, 69, 54, 85, 5, 98, 1, 76, 59, 20, 86, 32, 61, 49, 63, 42, 74, 53, 84, 3, 95, 23, 15, 36, 4, 81, 6, 21, 19, 27, 17, 83, 30, 45, 25, 65, 10, 50, 80, 66, 46, 75, 93, 82, 78, 99, 97, 68, 8, 13, 58, 94, 41, 14, 48, 52, 29, 79, 91, 89, 9, 55, 57, 2, 39, 92, 0, 44, 18, 62, 73, 96, 34, 88, 16, 77, 71, 43, 33, 40, 7, 12, 87, 51, 26, 47, 64, 35, 67, 38], [0, 80, 63, 52, 49, 69, 15, 87, 55, 60, 22, 91, 31, 89, 78, 28, 48, 12, 10, 42, 82, 23, 29, 18, 21, 2, 19, 7, 68, 37, 54, 90, 81, 43, 57, 94, 26, 95, 96, 88, 14, 51, 79, 71, 66, 76, 34, 24, 30, 9, 62, 44, 77, 65, 6, 27, 4, 97, 38, 36, 59, 3, 1, 50, 72, 46, 64, 35, 20, 11, 53, 13, 58, 39, 74, 8, 83, 75, 40, 98, 17, 99, 85, 32, 92, 67, 84, 5, 25, 56, 16, 47, 70, 93, 86, 61, 73, 41, 45, 33], [64, 57, 56, 7, 86, 9, 65, 16, 1, 63, 71, 26, 53, 98, 69, 5, 84, 28, 36, 58, 54, 99, 42, 74, 68, 3, 88, 22, 82, 97, 85, 45, 21, 66, 43, 59, 83, 51, 89, 87, 31, 12, 95, 52, 60, 17, 80, 27, 72, 93, 10, 39, 91, 30, 75, 61, 24, 8, 23, 2, 18, 73, 94, 6, 38, 44, 13, 40, 48, 4, 81, 37, 96, 46, 19, 49, 34, 50, 0, 20, 14, 15, 25, 92, 33, 29, 77, 90, 78, 67, 62, 41, 35, 47, 79, 32, 11, 70, 76, 55], [24, 99, 28, 52, 64, 3, 63, 16, 15, 87, 47, 6, 59, 98, 13, 78, 57, 81, 56, 94, 0, 37, 70, 73, 65, 83, 90, 92, 60, 82, 67, 40, 74, 34, 91, 2, 33, 85, 21, 54, 12, 31, 84, 55, 53, 49, 36, 25, 39, 20, 29, 93, 51, 76, 42, 96, 71, 86, 95, 66, 45, 19, 8, 58, 22, 69, 80, 79, 89, 17, 61, 18, 88, 38, 72, 75, 48, 30, 7, 10, 11, 35, 23, 9, 26, 4, 62, 44, 1, 43, 46, 14, 41, 77, 97, 27, 68, 32, 5, 50], [90, 80, 22, 27, 16, 23, 11, 31, 8, 75, 54, 82, 68, 66, 10, 40, 95, 99, 14, 42, 56, 88, 12, 55, 38, 6, 79, 84, 26, 92, 4, 67, 48, 5, 46, 77, 19, 53, 97, 34, 64, 73, 58, 47, 18, 39, 81, 59, 74, 62, 87, 98, 50, 1, 25, 9, 72, 69, 61, 60, 86, 3, 24, 28, 45, 30, 96, 36, 57, 83, 89, 63, 0, 33, 17, 7, 94, 15, 43, 2, 44, 41, 20, 85, 32, 65, 93, 49, 91, 52, 71, 37, 35, 13, 21, 51, 70, 29, 78, 76], [95, 50, 72, 0, 68, 98, 49, 53, 56, 41, 54, 22, 57, 78, 66, 31, 52, 29, 86, 30, 62, 26, 32, 46, 42, 48, 14, 21, 88, 94, 77, 92, 7, 63, 91, 28, 93, 38, 90, 71, 34, 25, 11, 83, 55, 70, 97, 82, 89, 76, 10, 99, 9, 45, 60, 39, 44, 16, 17, 37, 47, 79, 33, 36, 59, 8, 80, 23, 1, 18, 64, 51, 61, 81, 75, 40, 35, 73, 13, 65, 12, 4, 19, 85, 96, 6, 2, 58, 27, 43, 5, 87, 20, 15, 3, 24, 84, 67, 74, 69], [38, 33, 29, 88, 49, 47, 60, 69, 75, 12, 44, 89, 35, 45, 23, 30, 21, 28, 65, 86, 81, 40, 93, 97, 67, 2, 94, 9, 61, 92, 90, 32, 62, 36, 58, 51, 37, 73, 50, 63, 39, 43, 16, 71, 46, 91, 13, 7, 20, 72, 41, 59, 70, 48, 10, 52, 5, 34, 76, 15, 87, 6, 98, 18, 57, 77, 24, 79, 27, 19, 3, 0, 8, 95, 74, 64, 42, 4, 68, 31, 84, 54, 99, 25, 1, 80, 85, 26, 83, 11, 56, 55, 78, 66, 82, 14, 53, 22, 17, 96], [26, 12, 62, 5, 78, 53, 31, 46, 51, 8, 69, 80, 32, 68, 76, 42, 29, 87, 28, 75, 40, 91, 58, 7, 88, 92, 82, 96, 70, 50, 47, 65, 85, 3, 48, 73, 90, 89, 97, 84, 81, 39, 43, 79, 64, 54, 2, 41, 38, 10, 24, 22, 25, 93, 44, 23, 49, 37, 14, 95, 72, 74, 15, 11, 55, 98, 99, 0, 9, 16, 18, 20, 63, 30, 57, 19, 4, 17, 67, 35, 60, 45, 59, 56, 36, 77, 66, 33, 27, 71, 86, 1, 13, 34, 21, 61, 6, 94, 52, 83]], [[10, 87, 64, 11, 25, 97, 93, 18, 99, 38, 17, 72, 50, 23, 90, 57, 98, 12, 29, 46, 19, 77, 42, 15, 73, 52, 53, 89, 35, 20, 9, 41, 79, 13, 95, 48, 85, 54, 61, 94, 0, 74, 62, 40, 78, 68, 24, 28, 86, 21, 59, 14, 82, 84, 92, 58, 34, 31, 6, 16, 75, 81, 43, 47, 7, 3, 2, 37, 67, 8, 66, 22, 30, 32, 71, 44, 70, 1, 36, 26, 33, 63, 80, 83, 91, 76, 56, 39, 69, 55, 96, 60, 27, 49, 65, 88, 5, 4, 51, 45], [73, 87, 48, 43, 71, 40, 88, 23, 60, 35, 30, 5, 75, 25, 59, 95, 58, 79, 4, 89, 96, 17, 38, 6, 39, 9, 68, 81, 3, 90, 93, 99, 16, 42, 21, 45, 74, 84, 77, 65, 63, 98, 11, 51, 0, 33, 91, 22, 69, 34, 31, 20, 46, 2, 76, 54, 15, 62, 13, 70, 92, 55, 82, 47, 78, 7, 24, 27, 86, 1, 56, 61, 12, 44, 85, 57, 49, 19, 18, 67, 66, 52, 72, 53, 83, 41, 64, 50, 26, 94, 32, 80, 28, 10, 37, 14, 8, 97, 29, 36], [60, 50, 53, 78, 63, 90, 11, 33, 21, 24, 20, 55, 8, 25, 6, 58, 3, 98, 44, 82, 96, 14, 1, 59, 92, 75, 51, 36, 30, 64, 72, 40, 16, 94, 2, 74, 93, 85, 12, 10, 43, 32, 42, 39, 79, 97, 17, 38, 89, 29, 68, 52, 49, 0, 34, 19, 70, 84, 54, 9, 23, 71, 65, 28, 83, 56, 67, 57, 13, 18, 77, 5, 4, 88, 41, 22, 46, 76, 48, 61, 81, 45, 86, 35, 47, 87, 91, 99, 73, 66, 27, 37, 31, 15, 7, 80, 62, 95, 69, 26], [49, 73, 63, 66, 38, 35, 32, 96, 87, 26, 36, 92, 58, 6, 17, 47, 29, 84, 11, 18, 67, 45, 78, 4, 52, 81, 59, 30, 91, 54, 56, 89, 48, 46, 20, 79, 1, 62, 9, 53, 10, 95, 88, 34, 40, 80, 41, 64, 83, 55, 43, 7, 24, 61, 77, 5, 14, 93, 16, 21, 85, 39, 76, 50, 57, 65, 37, 60, 28, 19, 2, 97, 42, 33, 13, 68, 90, 25, 69, 99, 98, 74, 3, 8, 12, 15, 82, 71, 31, 23, 75, 27, 51, 94, 86, 0, 44, 22, 72, 70], [30, 32, 52, 21, 15, 79, 53, 40, 56, 44, 55, 6, 63, 39, 61, 4, 93, 43, 23, 99, 38, 66, 88, 18, 1, 86, 33, 9, 82, 76, 70, 51, 48, 69, 85, 11, 5, 91, 59, 77, 49, 75, 22, 87, 65, 41, 26, 50, 47, 98, 57, 10, 58, 62, 46, 28, 20, 71, 78, 95, 0, 72, 80, 73, 92, 36, 96, 37, 68, 2, 35, 83, 90, 25, 13, 24, 34, 42, 45, 74, 67, 16, 84, 97, 19, 64, 27, 14, 31, 12, 54, 60, 3, 8, 17, 89, 81, 94, 7, 29], [1, 18, 25, 13, 57, 53, 92, 95, 94, 88, 10, 82, 34, 83, 7, 28, 55, 11, 5, 58, 8, 9, 74, 99, 52, 32, 62, 45, 65, 50, 41, 56, 96, 44, 38, 47, 3, 39, 81, 19, 2, 22, 49, 12, 40, 67, 89, 76, 64, 70, 61, 21, 31, 54, 51, 17, 46, 30, 24, 60, 66, 26, 77, 63, 37, 35, 23, 6, 4, 87, 79, 97, 20, 48, 36, 85, 73, 71, 27, 43, 0, 15, 69, 78, 59, 86, 75, 33, 42, 84, 91, 93, 68, 80, 16, 98, 90, 72, 14, 29], [30, 16, 10, 67, 83, 81, 42, 94, 35, 59, 22, 5, 53, 61, 1, 96, 68, 45, 95, 66, 36, 37, 97, 87, 77, 93, 86, 33, 49, 13, 43, 14, 0, 34, 7, 2, 41, 21, 31, 32, 76, 55, 62, 72, 90, 71, 38, 23, 46, 12, 51, 19, 6, 44, 52, 48, 73, 24, 29, 74, 85, 99, 80, 28, 47, 9, 39, 84, 64, 57, 75, 98, 88, 26, 17, 63, 27, 56, 69, 8, 54, 25, 91, 78, 3, 50, 65, 70, 11, 4, 92, 60, 40, 58, 15, 89, 82, 18, 79, 20], [60, 88, 79, 35, 40, 43, 52, 4, 17, 31, 84, 44, 50, 94, 91, 38, 87, 25, 92, 62, 14, 97, 37, 18, 86, 28, 65, 90, 19, 29, 30, 75, 68, 49, 69, 11, 59, 41, 89, 33, 47, 54, 98, 42, 80, 56, 78, 99, 23, 82, 9, 48, 21, 1, 63, 22, 58, 95, 10, 12, 26, 74, 85, 13, 8, 27, 24, 53, 61, 34, 64, 71, 66, 32, 83, 73, 15, 67, 0, 20, 16, 6, 77, 45, 5, 93, 76, 39, 57, 2, 96, 46, 51, 3, 55, 7, 81, 70, 36, 72], [35, 56, 77, 58, 23, 83, 91, 81, 53, 70, 20, 74, 84, 50, 79, 51, 88, 44, 45, 71, 37, 26, 54, 48, 27, 43, 0, 12, 95, 97, 1, 63, 67, 98, 59, 40, 13, 19, 31, 87, 99, 16, 57, 30, 17, 92, 55, 68, 5, 46, 64, 8, 7, 42, 90, 33, 29, 9, 85, 24, 2, 69, 32, 10, 21, 65, 39, 86, 52, 62, 76, 89, 82, 6, 78, 4, 47, 28, 3, 75, 41, 15, 18, 14, 66, 73, 38, 80, 61, 49, 11, 22, 34, 93, 36, 25, 94, 96, 60, 72], [6, 90, 16, 43, 20, 55, 94, 8, 62, 59, 47, 32, 81, 67, 42, 35, 71, 19, 54, 49, 2, 88, 38, 5, 53, 77, 85, 11, 73, 34, 3, 72, 95, 58, 82, 64, 44, 29, 93, 14, 56, 98, 4, 37, 45, 75, 86, 60, 87, 7, 31, 17, 89, 97, 10, 70, 36, 99, 46, 63, 76, 41, 48, 84, 9, 52, 1, 24, 83, 25, 68, 18, 50, 26, 30, 27, 61, 0, 91, 33, 51, 40, 79, 39, 15, 74, 28, 78, 69, 96, 57, 65, 21, 80, 22, 23, 12, 13, 92, 66]], [[93, 42, 18, 65, 28, 87, 39, 35, 71, 23, 96, 90, 15, 58, 78, 33, 92, 88, 49, 64, 24, 84, 77, 8, 97, 50, 4, 53, 27, 83, 95, 31, 80, 47, 86, 55, 11, 40, 37, 14, 34, 60, 54, 12, 94, 9, 41, 2, 26, 10, 6, 57, 75, 63, 32, 43, 13, 52, 82, 73, 20, 30, 17, 5, 3, 68, 51, 89, 62, 79, 69, 70, 66, 45, 25, 22, 85, 0, 91, 76, 38, 7, 99, 61, 29, 98, 16, 36, 44, 67, 59, 56, 48, 81, 21, 46, 74, 72, 19, 1], [78, 91, 97, 77, 75, 85, 32, 2, 40, 86, 38, 18, 56, 47, 50, 87, 27, 92, 64, 39, 98, 23, 33, 72, 84, 58, 16, 99, 19, 65, 55, 73, 74, 42, 4, 48, 95, 8, 51, 3, 37, 82, 96, 69, 14, 44, 12, 21, 76, 70, 36, 15, 71, 41, 20, 31, 54, 13, 0, 9, 94, 67, 1, 60, 81, 61, 11, 6, 90, 5, 49, 10, 34, 7, 35, 30, 25, 88, 79, 93, 66, 62, 22, 24, 52, 17, 89, 45, 83, 57, 28, 46, 63, 68, 26, 29, 80, 59, 43, 53], [78, 91, 97, 77, 75, 85, 32, 2, 40, 86, 38, 18, 56, 47, 50, 87, 27, 92, 64, 39, 98, 23, 33, 72, 84, 58, 16, 99, 19, 65, 55, 73, 74, 42, 4, 48, 95, 8, 51, 3, 37, 82, 96, 69, 14, 44, 12, 21, 76, 70, 36, 15, 71, 41, 20, 31, 54, 13, 0, 9, 94, 67, 1, 60, 81, 61, 11, 6, 90, 5, 49, 10, 34, 7, 35, 30, 25, 88, 79, 93, 66, 62, 22, 24, 52, 17, 89, 45, 83, 57, 28, 46, 63, 68, 26, 29, 80, 59, 43, 53], [41, 65, 16, 86, 77, 67, 51, 81, 50, 25, 90, 87, 98, 99, 23, 9, 4, 97, 39, 36, 26, 42, 13, 66, 82, 22, 47, 88, 49, 46, 59, 78, 20, 19, 7, 93, 56, 84, 40, 61, 89, 21, 74, 1, 54, 44, 34, 6, 28, 8, 43, 76, 48, 71, 85, 38, 83, 18, 24, 79, 68, 80, 45, 33, 91, 27, 32, 70, 95, 3, 58, 60, 73, 10, 75, 52, 64, 2, 94, 30, 17, 55, 62, 35, 11, 12, 53, 63, 14, 31, 57, 0, 37, 72, 92, 15, 5, 29, 69, 96], [74, 50, 44, 80, 11, 46, 39, 54, 24, 3, 7, 62, 41, 8, 75, 23, 57, 82, 51, 86, 97, 79, 22, 60, 42, 14, 28, 67, 32, 73, 48, 56, 89, 26, 25, 77, 21, 68, 10, 2, 58, 76, 92, 95, 38, 29, 43, 35, 12, 16, 55, 17, 63, 91, 45, 64, 59, 13, 47, 31, 83, 5, 99, 49, 78, 71, 96, 53, 36, 90, 1, 66, 15, 0, 85, 65, 27, 4, 98, 94, 6, 84, 72, 30, 70, 20, 61, 69, 37, 93, 34, 87, 9, 52, 88, 81, 33, 19, 40, 18], [35, 67, 27, 53, 26, 79, 20, 46, 38, 4, 72, 84, 70, 58, 65, 91, 92, 21, 32, 71, 54, 24, 89, 0, 37, 8, 22, 81, 12, 87, 43, 18, 82, 17, 33, 76, 25, 13, 74, 51, 99, 3, 55, 60, 19, 73, 86, 62, 93, 52, 31, 64, 96, 66, 14, 40, 42, 69, 45, 6, 61, 34, 41, 59, 49, 10, 39, 2, 75, 80, 15, 94, 7, 23, 95, 78, 90, 5, 1, 28, 56, 30, 9, 98, 44, 48, 68, 77, 47, 11, 63, 83, 88, 36, 97, 29, 57, 50, 16, 85], [8, 51, 32, 96, 34, 24, 88, 54, 87, 83, 45, 99, 49, 30, 55, 61, 95, 60, 59, 76, 57, 5, 19, 56, 89, 66, 27, 94, 14, 6, 21, 58, 43, 74, 10, 2, 17, 62, 47, 16, 69, 84, 63, 39, 4, 92, 35, 48, 53, 64, 71, 50, 72, 28, 42, 46, 91, 23, 15, 40, 11, 78, 29, 86, 13, 90, 85, 20, 65, 93, 73, 80, 41, 37, 82, 79, 52, 9, 97, 38, 75, 25, 44, 7, 26, 12, 22, 0, 33, 1, 31, 68, 70, 67, 98, 18, 77, 36, 3, 81], [34, 17, 91, 45, 63, 64, 1, 71, 98, 78, 94, 3, 99, 12, 15, 93, 69, 19, 61, 80, 92, 81, 33, 51, 40, 10, 75, 24, 5, 87, 73, 46, 52, 86, 4, 70, 89, 56, 2, 67, 72, 58, 49, 84, 29, 18, 22, 38, 95, 36, 43, 35, 37, 85, 57, 8, 76, 7, 27, 28, 55, 74, 42, 13, 77, 31, 60, 39, 48, 68, 97, 79, 44, 54, 88, 82, 21, 66, 9, 23, 59, 50, 47, 16, 83, 25, 6, 30, 41, 96, 90, 14, 62, 20, 65, 11, 26, 32, 53, 0], [7, 51, 77, 80, 0, 53, 56, 27, 2, 78, 28, 32, 26, 22, 73, 93, 36, 54, 64, 99, 60, 50, 21, 11, 39, 42, 81, 8, 1, 79, 61, 5, 15, 31, 59, 33, 20, 94, 23, 88, 69, 17, 35, 95, 40, 75, 85, 87, 43, 47, 89, 12, 71, 72, 9, 83, 67, 38, 90, 82, 29, 10, 37, 91, 97, 74, 6, 86, 30, 63, 24, 49, 3, 13, 4, 57, 76, 70, 98, 34, 58, 52, 96, 84, 25, 55, 44, 46, 18, 65, 62, 68, 48, 66, 41, 19, 14, 45, 16, 92], [96, 2, 76, 15, 31, 22, 71, 38, 88, 26, 67, 0, 21, 10, 58, 39, 63, 77, 23, 50, 48, 19, 78, 30, 93, 91, 14, 12, 28, 11, 53, 73, 60, 61, 35, 79, 57, 70, 40, 20, 56, 32, 65, 64, 68, 55, 82, 94, 45, 95, 66, 7, 43, 16, 37, 99, 72, 62, 13, 4, 47, 8, 98, 89, 46, 29, 51, 85, 74, 17, 97, 84, 33, 18, 25, 83, 69, 44, 87, 49, 92, 9, 90, 36, 41, 81, 59, 54, 34, 6, 42, 5, 80, 3, 1, 86, 27, 52, 24, 75]], [[61, 85, 41, 37, 12, 25, 27, 17, 92, 52, 44, 66, 13, 15, 19, 78, 77, 9, 40, 20, 58, 43, 76, 24, 59, 42, 28, 30, 96, 86, 62, 89, 67, 35, 45, 6, 36, 50, 74, 26, 84, 95, 7, 68, 97, 79, 51, 16, 80, 3, 82, 2, 93, 21, 31, 98, 18, 60, 39, 72, 54, 65, 73, 32, 5, 70, 99, 10, 14, 71, 34, 4, 64, 33, 55, 48, 75, 87, 57, 81, 22, 46, 69, 63, 90, 23, 38, 56, 8, 29, 83, 88, 11, 0, 47, 53, 91, 94, 49, 1], [35, 22, 67, 24, 94, 82, 12, 68, 30, 43, 58, 96, 27, 69, 65, 18, 7, 79, 14, 2, 44, 60, 54, 47, 51, 83, 89, 3, 20, 11, 40, 85, 37, 48, 49, 8, 73, 15, 90, 93, 70, 84, 34, 4, 86, 98, 17, 46, 72, 63, 26, 56, 97, 74, 91, 99, 36, 80, 81, 41, 53, 88, 77, 21, 0, 23, 64, 76, 95, 33, 62, 1, 75, 87, 10, 66, 57, 6, 28, 25, 42, 9, 50, 39, 29, 78, 13, 19, 31, 45, 55, 61, 38, 71, 59, 5, 92, 52, 32, 16], [70, 29, 50, 57, 31, 9, 59, 45, 87, 71, 98, 89, 77, 97, 86, 5, 21, 61, 47, 41, 76, 37, 65, 12, 58, 75, 78, 91, 27, 63, 64, 83, 55, 95, 4, 11, 82, 23, 39, 40, 42, 68, 69, 48, 54, 13, 99, 33, 26, 0, 66, 79, 46, 72, 49, 44, 25, 74, 73, 28, 38, 10, 2, 85, 15, 19, 52, 94, 7, 36, 17, 32, 96, 3, 20, 30, 92, 51, 14, 90, 24, 67, 43, 34, 93, 84, 62, 81, 1, 56, 53, 88, 80, 35, 22, 6, 18, 16, 8, 60], [62, 75, 38, 50, 52, 86, 53, 73, 99, 59, 26, 46, 48, 90, 64, 1, 65, 13, 30, 36, 57, 16, 97, 91, 51, 33, 80, 67, 5, 25, 8, 96, 94, 84, 6, 89, 88, 20, 10, 85, 41, 56, 9, 34, 63, 0, 18, 12, 42, 15, 87, 4, 61, 43, 82, 17, 98, 60, 92, 70, 49, 71, 47, 54, 79, 83, 27, 74, 58, 2, 76, 93, 22, 69, 78, 44, 28, 95, 72, 68, 7, 55, 29, 77, 14, 23, 31, 32, 3, 37, 11, 66, 35, 39, 24, 81, 19, 21, 40, 45], [65, 78, 7, 83, 80, 77, 71, 72, 22, 85, 63, 73, 44, 87, 86, 88, 41, 30, 95, 67, 58, 21, 4, 5, 26, 27, 75, 31, 99, 52, 92, 64, 28, 8, 19, 98, 84, 34, 9, 48, 60, 0, 56, 1, 62, 16, 91, 32, 89, 79, 40, 11, 54, 51, 43, 12, 45, 66, 23, 13, 96, 81, 47, 36, 46, 93, 2, 69, 33, 3, 20, 6, 35, 18, 74, 68, 53, 37, 29, 61, 90, 57, 14, 42, 24, 76, 10, 38, 97, 39, 25, 50, 49, 70, 55, 82, 15, 17, 59, 94], [11, 17, 21, 48, 63, 0, 73, 14, 10, 88, 2, 28, 6, 9, 19, 69, 81, 13, 35, 95, 60, 56, 30, 47, 91, 92, 86, 29, 76, 50, 16, 25, 82, 93, 8, 5, 58, 94, 98, 20, 74, 77, 4, 52, 97, 79, 27, 42, 36, 12, 26, 72, 71, 45, 24, 87, 1, 39, 22, 89, 53, 31, 61, 37, 33, 18, 59, 43, 80, 15, 54, 38, 78, 90, 32, 83, 23, 44, 55, 66, 64, 3, 67, 49, 84, 62, 51, 41, 46, 85, 96, 34, 99, 75, 7, 40, 70, 68, 65, 57], [43, 38, 48, 47, 74, 88, 64, 54, 59, 35, 29, 18, 53, 91, 68, 66, 12, 79, 55, 41, 34, 36, 33, 94, 56, 87, 25, 6, 1, 8, 63, 96, 93, 44, 40, 65, 13, 77, 61, 85, 99, 80, 11, 51, 14, 46, 5, 39, 92, 27, 75, 52, 72, 83, 19, 62, 32, 24, 82, 95, 17, 37, 0, 15, 97, 4, 42, 86, 84, 3, 26, 81, 98, 9, 73, 22, 28, 78, 60, 76, 16, 23, 71, 89, 31, 57, 58, 20, 50, 10, 49, 45, 21, 67, 90, 30, 7, 2, 70, 69], [78, 35, 94, 59, 8, 23, 10, 76, 96, 80, 93, 9, 27, 95, 61, 84, 88, 91, 2, 33, 1, 85, 3, 36, 79, 18, 92, 5, 34, 81, 98, 40, 38, 68, 90, 70, 89, 65, 48, 6, 77, 30, 72, 62, 16, 83, 32, 54, 99, 82, 75, 41, 53, 17, 67, 43, 28, 21, 46, 7, 25, 64, 13, 14, 87, 58, 63, 31, 39, 47, 24, 71, 74, 55, 20, 11, 22, 45, 44, 51, 37, 15, 42, 56, 66, 29, 86, 49, 69, 57, 73, 50, 60, 0, 4, 19, 52, 97, 12, 26], [88, 86, 41, 1, 70, 10, 19, 2, 11, 44, 87, 26, 53, 59, 40, 18, 61, 13, 21, 56, 85, 75, 25, 5, 97, 7, 36, 67, 76, 79, 90, 65, 91, 0, 98, 89, 20, 55, 93, 58, 28, 63, 6, 83, 47, 71, 73, 49, 39, 31, 48, 69, 82, 50, 78, 62, 14, 74, 29, 54, 57, 80, 32, 94, 3, 66, 30, 35, 8, 46, 16, 51, 24, 17, 4, 84, 45, 60, 34, 15, 72, 77, 42, 12, 64, 9, 33, 95, 81, 96, 38, 43, 52, 99, 22, 92, 27, 23, 37, 68], [90, 93, 83, 21, 96, 71, 67, 28, 37, 75, 55, 58, 56, 39, 16, 46, 91, 41, 77, 92, 99, 65, 27, 5, 24, 1, 69, 7, 31, 35, 33, 64, 12, 14, 45, 11, 98, 0, 57, 72, 74, 13, 61, 40, 63, 89, 53, 85, 18, 76, 2, 70, 20, 60, 84, 52, 25, 43, 54, 17, 36, 19, 97, 73, 94, 62, 3, 23, 34, 88, 29, 80, 50, 22, 81, 87, 6, 30, 78, 48, 79, 44, 66, 8, 51, 86, 4, 15, 42, 10, 82, 32, 68, 26, 38, 47, 49, 9, 95, 59]]]; +var ibtable2 = [[4, 7, 8, 0, 9, 1, 3, 6, 2, 5], [5, 0, 4, 1, 3, 8, 9, 7, 6, 2], [0, 2, 7, 8, 9, 1, 6, 3, 5, 4], [5, 9, 8, 4, 1, 7, 0, 2, 3, 6], [7, 3, 0, 1, 8, 9, 6, 5, 2, 4], [7, 2, 4, 0, 1, 9, 6, 3, 5, 8], [4, 1, 6, 3, 5, 8, 0, 7, 2, 9], [6, 8, 4, 1, 5, 3, 7, 2, 9, 0], [4, 1, 8, 3, 7, 2, 0, 6, 5, 9], [7, 4, 5, 6, 2, 3, 9, 1, 8, 0]]; +function crc16(data, bits, initial = 65535, poly = 33800) { + let v = (0, import_big_integer.default)(initial); + let bigPoly = (0, import_big_integer.default)(poly); + for (let i = 0; i < bits; i++) { + v = v.shiftRight(1).xor(bigPoly.and(v.xor(data).and(import_big_integer.default.one).equals(import_big_integer.default.one) ? bigPoly : import_big_integer.default.zero)); + data = data.shiftRight(1); + } + return v.toJSNumber(); +} +function decodeAccessCode(code) { + code = code.replace(/\D/i, ""); + if (code.length !== 20) { + return null; + } + let body = import_lodash.default.range(3, 15, 2).map((i) => parseInt(code[i]) * 10 + parseInt(code[i + 1])); + let crc = import_lodash.default.range(15, 20).map((i) => parseInt(code[i])); + let boxes = [(crc[3] + crc[2]) % 10, crc[2], crc[3], crc[4], (crc[4] + crc[0]) % 10, crc[1]]; + for (const [n, i] of body.entries()) { + body[n] = ibtable[4][boxes[n]][i]; + } + const rv = body[0] >> 4; + boxes = [(crc[1] + crc[0]) % 10, crc[1], crc[2], crc[3], crc[4], (crc[4] + crc[1]) % 10]; + for (const [n, i] of body.entries()) { + if (n == 0) { + body[n] = body[n] & 240 | ibtable2[boxes[n]][body[n] & 15]; + } else { + body[n] = ibtable[rv][boxes[n]][i]; + } + } + const serial = body.map((i) => i.toString().padStart(2, "0")).join(""); + if (crc16((0, import_big_integer.default)(code.substring(0, 3) + serial, 16), 15 * 4) !== parseInt(code.substring(15, 20))) { + throw new Error(`CRC16 mismatch.`); + } + return { revision: rv, serial: parseInt(serial) }; +} +function encodeAccessCode(company, serial, rev = 0) { + if (company < 0 || company > 999) { + throw new Error(`Invalid company code`); + } + if (serial < 0 || serial > 99999999999) { + throw new Error(`Invalid serial number`); + } + if (rev < 0 || rev > sbtable.length) { + throw new Error(`Invalid revision`); + } + const bodyStr = `${rev}${serial.toString().padStart(11, "0")}`; + const crc_i = crc16((0, import_big_integer.default)(company.toString().padStart(3, "0") + bodyStr, 16), 15 * 4); + const crc = import_lodash.default.range(5).map((i) => Math.floor(crc_i / 10 ** (4 - i)) % 10); + const body = import_lodash.default.range(0, 12, 2).map((i) => parseInt(bodyStr[i]) * 10 + parseInt(bodyStr[i + 1])); + let boxes = [(crc[1] + crc[0]) % 10, crc[1], crc[2], crc[3], crc[4], (crc[4] + crc[1]) % 10]; + for (const [n, i] of body.entries()) { + if (n == 0) { + body[n] = body[n] & 240 | sbtable2[boxes[n]][body[n] & 15]; + } else { + body[n] = sbtable[rev][boxes[n]][i]; + } + } + boxes = [(crc[3] + crc[2]) % 10, crc[2], crc[3], crc[4], (crc[4] + crc[0]) % 10, crc[1]]; + for (const [n, i] of body.entries()) { + body[n] = sbtable[4][boxes[n]][i]; + } + return `${company.toString().padStart(3, "0")}${body.map((i) => i.toString().padStart(2, "0")).join("")}${crc_i.toString().padStart(5, "0")}`; } // src/card.ts var CARD_ID_REGEX = /^(?:[0-9]{4} ?){5}$/i; +var AMNET_COMPANY_ID = 597; function generateCardNumber() { - let cardId = ""; - while (true) { - const firstDigit = Math.floor(Math.random() * 10); - if (firstDigit !== 3) { - cardId = firstDigit.toString(); - break; - } - } - for (let i = 0; i < 19; i++) { - cardId += Math.floor(Math.random() * 10).toString(); - } - return cardId; + const serial = Math.floor(Math.random() * 1e11); + return encodeAccessCode(AMNET_COMPANY_ID, serial, 0); } function validateCardId(id) { return id[0] !== "3" && CARD_ID_REGEX.test(id); @@ -150,7 +201,10 @@ function getGameName(id) { // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { AMNET_API_VERSION, + AMNET_COMPANY_ID, CARD_ID_REGEX, + DecodeAMAccessCode, + EncodeAMAccessCode, MIN_AMNET_API_VERSION, fetchServerInfo, generateCardNumber, diff --git a/amnet-shared/dist/index.js.map b/amnet-shared/dist/index.js.map index 1827b1f..642da6b 100644 --- a/amnet-shared/dist/index.js.map +++ b/amnet-shared/dist/index.js.map @@ -1 +1 @@ -{"version":3,"sources":["../src/index.ts","../src/api.ts","../src/card.ts","../src/gameIds.ts"],"sourcesContent":["export * from './api';\r\nexport * from './card';\r\nexport * from './gameIds';","/**\r\n * The latest version of the AMNet API this library supports\r\n */\r\nexport const AMNET_API_VERSION = 1;\r\n\r\n/**\r\n * The minimum version of the AMNet API this library supports\r\n */\r\nexport const MIN_AMNET_API_VERSION = 1;\r\n\r\nexport interface AMCardInRequest {\r\n cardId: string;\r\n physicalCardIDm?: string;\r\n}\r\n\r\n/**\r\n * Server connection info (api version, address, etc.)\r\n */\r\nexport interface AMServerConnectionInfo {\r\n apiVersion: number;\r\n serverAddress: string;\r\n}\r\n\r\n/**\r\n * User-definable server metadata\r\n */\r\nexport interface AMServerMetadata {\r\n serverName: string;\r\n gameId?: string | null | undefined;\r\n}\r\n\r\n/**\r\n * Metrics about the current game session\r\n */\r\nexport interface AMServerInstanceMetrics {\r\n timeSinceLastPoll?: number;\r\n sessionUptime?: number;\r\n}\r\n\r\n/**\r\n * The response from an AMNet server's info endpoint\r\n */\r\nexport type AMServerInfo = AMServerMetadata & AMServerConnectionInfo & AMServerInstanceMetrics;\r\n\r\n/**\r\n * Performs the card-in request to the specified server\r\n * @param server The server to perform the request against\r\n * @param request The card-in request to send\r\n * @param signal The abort signal to use for the request\r\n */\r\nexport async function performCardIn(server: AMServerConnectionInfo, request: AMCardInRequest, signal: AbortSignal): Promise {\r\n if (server.apiVersion < MIN_AMNET_API_VERSION || server.apiVersion > AMNET_API_VERSION) {\r\n throw new Error(\"Unsupported server API version\");\r\n }\r\n\r\n try {\r\n const response = await fetch(`${new URL(server.serverAddress).origin}/amnet/signin`, {\r\n method: \"POST\",\r\n headers: {\r\n \"Content-Type\": \"application/json\"\r\n },\r\n body: JSON.stringify(request),\r\n signal: signal\r\n });\r\n\r\n return response.ok;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * Attempts to connect to the specified server and fetch its info\r\n * @param serverAddress The origin address of the server to connect to\r\n * @param abort The abort signal to use for the request (for cancellation)\r\n */\r\nexport async function fetchServerInfo(serverAddress: string, abort: AbortSignal): Promise {\r\n try {\r\n const response = await fetch(`${new URL(serverAddress).origin}/amnet/info`, {\r\n signal: abort\r\n });\r\n\r\n if (!response.ok) {\r\n return null;\r\n }\r\n\r\n return {\r\n ...await response.json(),\r\n serverAddress: serverAddress\r\n } satisfies AMServerMetadata;\r\n } catch {\r\n return null;\r\n }\r\n}\r\n","export const CARD_ID_REGEX = /^(?:[0-9]{4} ?){5}$/i;\r\n\r\nexport function generateCardNumber(): string {\r\n let cardId = '';\r\n\r\n while (true) {\r\n // don't start with a 3\r\n const firstDigit = Math.floor(Math.random() * 10);\r\n\r\n if (firstDigit !== 3) {\r\n cardId = firstDigit.toString();\r\n break;\r\n }\r\n }\r\n\r\n for (let i = 0; i < 19; i++) {\r\n cardId += Math.floor(Math.random() * 10).toString();\r\n }\r\n\r\n return cardId;\r\n}\r\n\r\n/**\r\n * Validates the provided card id by known parameters\r\n * @param id The card id to validate\r\n */\r\nexport function validateCardId(id: string): boolean {\r\n return id[0] !== '3' && CARD_ID_REGEX.test(id);\r\n}\r\n","/**\r\n * Get the game name from the game identifier (4-letter code)\r\n * @param id\r\n */\r\nexport function getGameName(id: string): string | null {\r\n switch (id?.toUpperCase()) {\r\n case \"SDGT\":\r\n return \"Initial D The Arcade\";\r\n case \"SDHD\":\r\n case \"SDBT\":\r\n return \"CHUNITHM\";\r\n case \"SDDT\":\r\n return \"O.N.G.E.K.I.\";\r\n case \"SDEZ\":\r\n return \"maimai DX\";\r\n case \"SBZV\":\r\n return \"Project DIVA Future Tone\";\r\n \r\n default:\r\n return null;\r\n }\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACGO,IAAM,oBAAoB;AAK1B,IAAM,wBAAwB;AA0CrC,SAAsB,cAAc,QAAgC,SAA0B,QAAuC;AAAA;AACjI,QAAI,OAAO,aAAa,yBAAyB,OAAO,aAAa,mBAAmB;AACpF,YAAM,IAAI,MAAM,gCAAgC;AAAA,IACpD;AAEA,QAAI;AACA,YAAM,WAAW,MAAM,MAAM,GAAG,IAAI,IAAI,OAAO,aAAa,EAAE,MAAM,iBAAiB;AAAA,QACjF,QAAQ;AAAA,QACR,SAAS;AAAA,UACL,gBAAgB;AAAA,QACpB;AAAA,QACA,MAAM,KAAK,UAAU,OAAO;AAAA,QAC5B;AAAA,MACJ,CAAC;AAED,aAAO,SAAS;AAAA,IACpB,SAAQ;AACJ,aAAO;AAAA,IACX;AAAA,EACJ;AAAA;AAOA,SAAsB,gBAAgB,eAAuB,OAAkD;AAAA;AAC3G,QAAI;AACA,YAAM,WAAW,MAAM,MAAM,GAAG,IAAI,IAAI,aAAa,EAAE,MAAM,eAAe;AAAA,QACxE,QAAQ;AAAA,MACZ,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AACd,eAAO;AAAA,MACX;AAEA,aAAO,iCACA,MAAM,SAAS,KAAK,IADpB;AAAA,QAEH;AAAA,MACJ;AAAA,IACJ,SAAQ;AACJ,aAAO;AAAA,IACX;AAAA,EACJ;AAAA;;;AC7FO,IAAM,gBAAgB;AAEtB,SAAS,qBAA6B;AACzC,MAAI,SAAS;AAEb,SAAO,MAAM;AAET,UAAM,aAAa,KAAK,MAAM,KAAK,OAAO,IAAI,EAAE;AAEhD,QAAI,eAAe,GAAG;AAClB,eAAS,WAAW,SAAS;AAC7B;AAAA,IACJ;AAAA,EACJ;AAEA,WAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AACzB,cAAU,KAAK,MAAM,KAAK,OAAO,IAAI,EAAE,EAAE,SAAS;AAAA,EACtD;AAEA,SAAO;AACX;AAMO,SAAS,eAAe,IAAqB;AAChD,SAAO,GAAG,CAAC,MAAM,OAAO,cAAc,KAAK,EAAE;AACjD;;;ACxBO,SAAS,YAAY,IAA2B;AACnD,UAAQ,yBAAI,eAAe;AAAA,IACvB,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AAAA,IACL,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IAEX;AACI,aAAO;AAAA,EACf;AACJ;","names":[]} \ No newline at end of file +{"version":3,"sources":["../src/index.ts","../src/api.ts","../src/card-encoding.ts","../src/card.ts","../src/gameIds.ts"],"sourcesContent":["export * from './api';\nexport * from './card';\nexport * from './gameIds';\n\nexport {decodeAccessCode as DecodeAMAccessCode, encodeAccessCode as EncodeAMAccessCode} from './card-encoding';","/**\n * The latest version of the AMNet API this library supports\n */\nexport const AMNET_API_VERSION = 1;\n\n/**\n * The minimum version of the AMNet API this library supports\n */\nexport const MIN_AMNET_API_VERSION = 1;\n\nexport interface AMCardInRequest {\n cardId: string;\n physicalCardIDm?: string;\n}\n\n/**\n * Server connection info (api version, address, etc.)\n */\nexport interface AMServerConnectionInfo {\n apiVersion: number;\n serverAddress: string;\n}\n\n/**\n * User-definable server metadata\n */\nexport interface AMServerMetadata {\n serverName: string;\n gameId?: string | null | undefined;\n}\n\n/**\n * Metrics about the current game session\n */\nexport interface AMServerInstanceMetrics {\n timeSinceLastPoll?: number;\n sessionUptime?: number;\n}\n\n/**\n * The response from an AMNet server's info endpoint\n */\nexport type AMServerInfo = AMServerMetadata & AMServerConnectionInfo & AMServerInstanceMetrics;\n\n/**\n * Performs the card-in request to the specified server\n * @param server The server to perform the request against\n * @param request The card-in request to send\n * @param signal The abort signal to use for the request\n */\nexport async function performCardIn(server: AMServerConnectionInfo, request: AMCardInRequest, signal: AbortSignal): Promise {\n if (server.apiVersion < MIN_AMNET_API_VERSION || server.apiVersion > AMNET_API_VERSION) {\n throw new Error(\"Unsupported server API version\");\n }\n\n try {\n const response = await fetch(`${new URL(server.serverAddress).origin}/amnet/signin`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\"\n },\n body: JSON.stringify(request),\n signal: signal\n });\n\n return response.ok;\n } catch {\n return false;\n }\n}\n\n/**\n * Attempts to connect to the specified server and fetch its info\n * @param serverAddress The origin address of the server to connect to\n * @param abort The abort signal to use for the request (for cancellation)\n */\nexport async function fetchServerInfo(serverAddress: string, abort: AbortSignal): Promise {\n try {\n const response = await fetch(`${new URL(serverAddress).origin}/amnet/info`, {\n signal: abort\n });\n\n if (!response.ok) {\n return null;\n }\n\n return {\n ...await response.json(),\n serverAddress: serverAddress\n } satisfies AMServerMetadata;\n } catch {\n return null;\n }\n}\n","import _ from \"lodash\";\nimport bigInt, {BigInteger} from \"big-integer\";\n\nconst sbtable = [[[45, 50, 94, 2, 66, 55, 56, 49, 99, 36, 60, 0, 73, 30, 39, 37, 80, 74, 85, 7, 12, 33, 93, 83, 95, 91, 98, 92, 23, 24, 79, 21, 31, 35, 71, 70, 4, 43, 1, 5, 88, 90, 69, 58, 76, 20, 84, 62, 77, 44, 61, 11, 16, 53, 82, 34, 13, 47, 48, 87, 89, 15, 64, 3, 54, 28, 57, 67, 9, 75, 32, 65, 96, 17, 18, 29, 10, 14, 42, 6, 46, 26, 40, 59, 63, 78, 68, 8, 86, 51, 25, 72, 38, 97, 19, 81, 27, 41, 52, 22], [26, 32, 34, 79, 42, 45, 4, 75, 22, 73, 64, 98, 49, 19, 5, 24, 6, 55, 63, 25, 40, 77, 54, 12, 9, 44, 83, 71, 0, 90, 97, 96, 81, 80, 3, 31, 56, 92, 17, 86, 72, 28, 36, 78, 62, 30, 7, 46, 87, 20, 82, 50, 35, 61, 41, 94, 68, 11, 1, 10, 74, 67, 85, 43, 57, 15, 95, 84, 65, 53, 69, 52, 76, 88, 2, 14, 37, 58, 93, 16, 59, 51, 18, 99, 39, 47, 89, 8, 13, 91, 38, 33, 70, 48, 23, 29, 27, 66, 60, 21], [28, 53, 30, 85, 0, 95, 70, 47, 40, 51, 94, 68, 99, 15, 5, 57, 1, 16, 80, 93, 91, 52, 2, 32, 48, 77, 41, 75, 22, 39, 50, 81, 79, 73, 97, 76, 60, 4, 59, 25, 78, 38, 46, 58, 90, 84, 56, 6, 83, 62, 42, 10, 64, 31, 36, 61, 65, 63, 18, 8, 9, 96, 55, 26, 89, 67, 3, 98, 35, 21, 13, 12, 88, 71, 37, 29, 87, 49, 24, 86, 92, 20, 44, 74, 69, 17, 82, 54, 27, 7, 23, 66, 11, 19, 34, 45, 14, 72, 43, 33], [32, 44, 86, 79, 38, 61, 25, 8, 5, 85, 78, 48, 33, 87, 84, 35, 64, 41, 60, 21, 96, 75, 6, 69, 39, 94, 3, 92, 37, 77, 15, 13, 29, 59, 76, 91, 65, 56, 73, 83, 27, 62, 89, 50, 18, 98, 81, 17, 71, 28, 82, 11, 30, 43, 34, 20, 70, 22, 46, 63, 52, 68, 90, 53, 36, 10, 74, 58, 40, 88, 9, 2, 42, 26, 24, 4, 95, 57, 31, 66, 0, 14, 49, 16, 99, 19, 51, 23, 47, 12, 93, 1, 7, 80, 45, 97, 55, 67, 72, 54], [58, 35, 42, 38, 63, 36, 68, 30, 81, 4, 87, 80, 32, 69, 37, 67, 1, 86, 61, 94, 27, 11, 84, 26, 59, 73, 53, 66, 72, 71, 39, 33, 99, 8, 43, 88, 17, 22, 89, 5, 46, 93, 98, 48, 55, 83, 34, 3, 0, 16, 12, 20, 78, 9, 2, 76, 54, 51, 25, 24, 62, 57, 92, 10, 50, 28, 79, 23, 52, 95, 44, 60, 74, 90, 19, 77, 21, 31, 96, 49, 64, 6, 29, 85, 18, 13, 82, 47, 56, 45, 15, 7, 97, 75, 40, 41, 65, 14, 70, 91], [81, 82, 66, 72, 83, 4, 20, 94, 53, 48, 77, 41, 67, 64, 9, 24, 36, 55, 14, 1, 78, 84, 49, 0, 44, 58, 70, 25, 28, 73, 8, 68, 88, 54, 86, 87, 23, 52, 27, 5, 97, 37, 62, 50, 3, 89, 92, 47, 30, 10, 29, 80, 22, 26, 11, 13, 69, 60, 38, 90, 85, 57, 12, 75, 46, 39, 74, 33, 42, 93, 98, 91, 59, 99, 18, 6, 17, 16, 32, 40, 63, 7, 76, 15, 43, 71, 51, 35, 2, 65, 34, 96, 19, 56, 79, 21, 95, 45, 61, 31], [25, 88, 7, 57, 98, 63, 86, 13, 69, 84, 56, 20, 68, 90, 35, 6, 43, 93, 95, 3, 75, 4, 9, 28, 31, 66, 21, 44, 30, 76, 11, 22, 49, 2, 77, 40, 85, 19, 82, 16, 97, 92, 39, 96, 18, 32, 42, 94, 70, 71, 54, 79, 67, 78, 37, 12, 99, 53, 61, 41, 87, 17, 1, 14, 52, 10, 47, 58, 27, 46, 36, 55, 26, 15, 33, 50, 72, 51, 89, 23, 74, 59, 64, 8, 83, 65, 80, 29, 38, 0, 60, 24, 45, 81, 48, 91, 73, 5, 34, 62], [73, 85, 37, 11, 80, 46, 16, 47, 88, 65, 94, 64, 12, 38, 98, 70, 95, 48, 92, 18, 56, 75, 58, 7, 81, 15, 74, 13, 5, 44, 27, 32, 76, 90, 33, 55, 26, 96, 77, 24, 0, 89, 8, 4, 28, 39, 71, 3, 10, 41, 91, 53, 31, 68, 36, 17, 69, 60, 34, 54, 93, 82, 21, 51, 87, 59, 61, 6, 1, 23, 30, 66, 43, 25, 35, 42, 49, 78, 62, 14, 19, 20, 29, 22, 52, 83, 63, 45, 2, 99, 50, 79, 72, 40, 84, 97, 86, 57, 67, 9], [48, 94, 17, 55, 59, 24, 72, 75, 38, 85, 34, 98, 95, 36, 49, 63, 57, 54, 29, 6, 52, 47, 73, 1, 82, 9, 66, 83, 31, 61, 78, 60, 97, 14, 87, 15, 90, 70, 53, 10, 27, 8, 3, 64, 51, 50, 68, 26, 22, 19, 42, 96, 67, 7, 25, 39, 92, 30, 58, 93, 21, 74, 41, 18, 65, 44, 88, 32, 71, 69, 12, 77, 13, 11, 99, 79, 37, 89, 81, 35, 86, 56, 45, 76, 46, 80, 62, 43, 16, 84, 40, 5, 33, 91, 4, 20, 0, 23, 28, 2], [61, 45, 79, 13, 59, 52, 6, 77, 3, 31, 70, 80, 26, 33, 36, 4, 27, 32, 24, 74, 29, 94, 16, 12, 76, 7, 88, 57, 78, 42, 97, 96, 43, 83, 92, 21, 51, 47, 49, 58, 90, 41, 93, 98, 35, 63, 86, 37, 5, 30, 28, 72, 25, 22, 60, 19, 39, 34, 68, 50, 8, 64, 46, 99, 84, 87, 69, 56, 53, 55, 44, 54, 89, 65, 85, 0, 71, 15, 17, 66, 81, 82, 91, 95, 62, 48, 38, 75, 40, 14, 9, 10, 73, 67, 18, 11, 23, 2, 20, 1]], [[95, 90, 61, 38, 18, 7, 83, 39, 79, 14, 48, 21, 81, 44, 78, 50, 56, 91, 12, 86, 99, 98, 43, 63, 73, 87, 42, 72, 15, 68, 97, 23, 88, 69, 2, 51, 19, 58, 59, 22, 20, 29, 82, 11, 75, 70, 9, 71, 27, 30, 47, 89, 92, 60, 46, 76, 93, 54, 1, 96, 94, 57, 40, 62, 41, 3, 28, 64, 37, 74, 67, 16, 65, 53, 5, 36, 24, 45, 32, 33, 10, 85, 26, 4, 84, 77, 35, 13, 6, 8, 17, 80, 25, 55, 52, 66, 49, 34, 0, 31], [18, 14, 70, 43, 74, 45, 94, 36, 40, 6, 31, 46, 15, 47, 13, 19, 72, 61, 99, 21, 17, 92, 88, 78, 73, 90, 0, 96, 20, 28, 25, 51, 50, 77, 29, 16, 54, 12, 58, 38, 71, 23, 42, 83, 65, 60, 10, 33, 22, 76, 95, 32, 57, 5, 59, 67, 68, 3, 91, 85, 26, 62, 41, 1, 53, 82, 34, 24, 87, 27, 89, 75, 56, 30, 81, 7, 8, 4, 64, 66, 9, 63, 35, 52, 80, 93, 69, 84, 55, 86, 48, 98, 79, 97, 49, 44, 39, 11, 37, 2], [76, 16, 73, 29, 34, 14, 36, 90, 58, 70, 46, 2, 91, 59, 63, 32, 84, 40, 78, 38, 19, 37, 0, 31, 8, 44, 94, 39, 10, 66, 42, 1, 21, 88, 82, 97, 33, 4, 99, 74, 89, 62, 25, 87, 77, 43, 50, 95, 64, 23, 47, 93, 65, 27, 12, 71, 9, 72, 60, 18, 7, 22, 79, 24, 96, 45, 49, 98, 57, 11, 5, 86, 3, 80, 26, 51, 17, 85, 54, 67, 48, 35, 53, 41, 28, 13, 20, 92, 83, 69, 6, 68, 75, 52, 61, 30, 81, 56, 15, 55], [0, 62, 25, 61, 56, 87, 54, 27, 75, 49, 18, 69, 17, 71, 40, 6, 90, 80, 23, 26, 68, 24, 10, 21, 47, 88, 36, 55, 15, 22, 48, 12, 83, 99, 46, 67, 59, 29, 58, 73, 78, 97, 19, 33, 51, 98, 65, 91, 16, 4, 63, 41, 3, 70, 30, 8, 89, 34, 72, 60, 9, 95, 50, 2, 66, 53, 44, 85, 28, 5, 92, 43, 64, 96, 74, 77, 45, 52, 14, 42, 1, 32, 20, 76, 86, 82, 94, 7, 39, 13, 31, 11, 84, 93, 35, 37, 38, 57, 79, 81], [78, 8, 59, 25, 69, 15, 63, 3, 57, 5, 50, 96, 41, 66, 80, 81, 7, 45, 60, 74, 79, 32, 27, 58, 56, 82, 11, 47, 17, 85, 53, 40, 95, 84, 76, 92, 18, 71, 64, 51, 67, 91, 22, 34, 65, 31, 73, 93, 68, 75, 77, 37, 43, 12, 20, 99, 2, 1, 19, 35, 44, 55, 90, 9, 0, 6, 33, 89, 24, 14, 97, 10, 48, 61, 23, 54, 98, 86, 88, 94, 46, 70, 28, 36, 16, 30, 4, 39, 26, 38, 87, 52, 83, 49, 62, 42, 72, 29, 13, 21], [20, 88, 35, 5, 85, 98, 11, 78, 62, 83, 79, 80, 40, 14, 91, 8, 7, 69, 71, 61, 49, 38, 64, 82, 0, 47, 84, 95, 2, 50, 77, 41, 97, 36, 33, 81, 46, 21, 73, 48, 31, 92, 54, 89, 87, 60, 90, 10, 76, 45, 99, 52, 3, 44, 39, 43, 18, 16, 63, 12, 28, 70, 86, 6, 4, 24, 59, 30, 96, 65, 22, 56, 74, 23, 32, 75, 53, 93, 15, 67, 66, 17, 29, 25, 42, 37, 57, 9, 72, 68, 26, 34, 27, 51, 19, 58, 55, 94, 13, 1], [72, 53, 79, 61, 30, 33, 25, 75, 8, 55, 14, 6, 22, 93, 18, 77, 4, 74, 44, 36, 82, 94, 2, 5, 62, 54, 28, 3, 63, 97, 65, 7, 84, 73, 39, 92, 67, 91, 24, 45, 15, 81, 19, 78, 80, 64, 34, 43, 32, 87, 52, 95, 89, 37, 10, 23, 20, 68, 42, 47, 59, 58, 49, 71, 40, 85, 13, 31, 12, 57, 96, 90, 56, 41, 48, 9, 99, 35, 98, 26, 1, 46, 11, 69, 27, 83, 60, 50, 21, 70, 0, 88, 29, 86, 76, 16, 66, 38, 51, 17], [3, 68, 86, 94, 81, 90, 85, 32, 65, 52, 50, 42, 80, 78, 26, 93, 57, 58, 69, 82, 92, 27, 11, 67, 95, 41, 21, 88, 35, 17, 19, 15, 22, 62, 40, 76, 63, 59, 37, 55, 75, 9, 24, 89, 56, 53, 23, 60, 25, 6, 1, 71, 16, 7, 10, 44, 8, 12, 87, 64, 54, 72, 20, 33, 70, 79, 14, 97, 4, 99, 45, 39, 2, 77, 98, 74, 49, 30, 13, 61, 66, 73, 47, 43, 96, 83, 18, 91, 28, 48, 38, 34, 31, 36, 29, 0, 84, 46, 5, 51], [71, 84, 25, 70, 77, 56, 61, 47, 72, 27, 54, 89, 9, 46, 95, 59, 42, 98, 63, 69, 48, 16, 97, 14, 66, 83, 87, 68, 17, 2, 15, 79, 31, 1, 57, 12, 33, 36, 0, 40, 21, 50, 76, 41, 10, 13, 44, 5, 53, 4, 38, 35, 55, 96, 81, 91, 90, 64, 34, 51, 6, 28, 32, 39, 75, 18, 93, 24, 78, 7, 52, 43, 49, 37, 74, 8, 58, 65, 92, 67, 85, 20, 94, 88, 80, 86, 19, 60, 3, 11, 30, 45, 29, 22, 26, 73, 99, 23, 62, 82], [67, 91, 46, 33, 76, 3, 96, 23, 9, 68, 49, 63, 1, 92, 58, 62, 69, 77, 70, 75, 71, 94, 51, 55, 50, 52, 0, 88, 18, 16, 73, 6, 12, 87, 93, 79, 84, 57, 48, 41, 20, 47, 15, 42, 54, 81, 7, 30, 34, 56, 29, 8, 98, 5, 45, 64, 83, 74, 22, 82, 80, 95, 2, 72, 44, 31, 86, 78, 13, 10, 28, 89, 60, 35, 61, 19, 14, 85, 4, 43, 11, 40, 26, 99, 39, 32, 90, 17, 24, 37, 36, 21, 25, 53, 97, 59, 27, 38, 65, 66]], [[40, 77, 66, 65, 97, 96, 58, 64, 69, 30, 0, 3, 17, 33, 51, 23, 59, 10, 7, 20, 29, 49, 71, 13, 46, 4, 79, 92, 47, 18, 72, 57, 73, 80, 56, 28, 78, 67, 9, 87, 43, 31, 22, 62, 75, 99, 19, 63, 35, 93, 12, 98, 25, 26, 37, 89, 86, 15, 55, 50, 91, 38, 42, 81, 2, 94, 70, 68, 45, 88, 76, 74, 11, 24, 41, 60, 85, 21, 44, 32, 82, 61, 52, 83, 53, 36, 48, 1, 95, 27, 14, 84, 54, 6, 39, 34, 90, 5, 16, 8], [44, 69, 53, 28, 18, 11, 23, 65, 96, 25, 93, 42, 72, 58, 95, 56, 32, 21, 78, 77, 51, 34, 47, 7, 66, 13, 88, 67, 92, 98, 10, 50, 90, 45, 49, 9, 99, 94, 22, 24, 5, 85, 33, 3, 73, 35, 52, 63, 2, 76, 87, 43, 81, 83, 55, 61, 70, 75, 16, 14, 8, 71, 57, 40, 86, 39, 80, 79, 26, 48, 59, 4, 82, 0, 36, 12, 54, 38, 64, 17, 91, 27, 62, 84, 37, 74, 68, 1, 6, 19, 29, 46, 60, 30, 89, 15, 20, 97, 41, 31], [53, 22, 34, 16, 72, 71, 14, 94, 12, 59, 39, 6, 38, 68, 21, 93, 32, 46, 69, 55, 10, 8, 75, 60, 9, 13, 99, 90, 63, 49, 28, 92, 41, 7, 54, 83, 27, 91, 47, 43, 31, 74, 42, 40, 18, 81, 76, 84, 78, 52, 1, 26, 51, 2, 58, 11, 65, 67, 15, 23, 0, 79, 96, 4, 29, 62, 89, 66, 50, 98, 56, 61, 30, 88, 35, 25, 77, 70, 3, 44, 95, 80, 19, 64, 57, 37, 82, 85, 73, 48, 5, 86, 24, 36, 33, 97, 20, 45, 17, 87], [95, 36, 70, 82, 23, 55, 13, 51, 83, 38, 40, 18, 84, 74, 56, 85, 58, 14, 19, 69, 34, 59, 97, 89, 52, 77, 9, 91, 68, 16, 27, 88, 6, 73, 43, 5, 10, 66, 4, 61, 44, 46, 72, 50, 96, 21, 33, 15, 32, 0, 63, 92, 24, 39, 29, 49, 30, 64, 12, 26, 67, 53, 37, 2, 47, 65, 3, 20, 75, 78, 99, 87, 98, 1, 81, 90, 62, 54, 22, 35, 45, 25, 86, 48, 17, 60, 94, 8, 42, 31, 76, 28, 11, 57, 93, 41, 7, 71, 80, 79], [60, 24, 69, 92, 15, 36, 11, 98, 93, 27, 51, 35, 89, 74, 87, 4, 81, 94, 23, 84, 56, 3, 42, 18, 75, 73, 46, 86, 55, 99, 0, 88, 1, 26, 76, 70, 65, 67, 20, 13, 7, 45, 77, 17, 9, 78, 54, 48, 32, 40, 47, 31, 2, 6, 90, 10, 8, 50, 52, 38, 91, 14, 53, 12, 85, 44, 21, 80, 68, 33, 30, 57, 61, 63, 79, 41, 29, 39, 58, 5, 62, 96, 28, 71, 82, 34, 25, 43, 22, 95, 72, 37, 64, 16, 97, 59, 66, 83, 49, 19], [80, 0, 40, 36, 68, 18, 67, 14, 20, 21, 10, 17, 43, 3, 98, 81, 94, 55, 1, 39, 72, 51, 41, 66, 58, 2, 61, 78, 15, 99, 57, 52, 25, 87, 12, 65, 74, 64, 34, 37, 44, 30, 88, 79, 33, 27, 56, 35, 73, 42, 29, 54, 24, 5, 53, 16, 31, 4, 19, 84, 59, 50, 26, 63, 48, 28, 60, 45, 92, 82, 49, 77, 97, 76, 22, 86, 47, 62, 83, 70, 93, 38, 11, 13, 89, 75, 85, 69, 9, 46, 96, 90, 6, 91, 8, 7, 32, 71, 95, 23], [32, 14, 35, 84, 89, 11, 52, 34, 79, 65, 2, 88, 49, 29, 31, 94, 1, 74, 97, 51, 99, 37, 10, 47, 57, 81, 73, 76, 63, 58, 0, 38, 39, 27, 33, 8, 20, 21, 46, 66, 92, 36, 6, 30, 53, 17, 48, 64, 55, 28, 85, 50, 54, 12, 80, 41, 77, 69, 93, 9, 91, 13, 42, 75, 68, 86, 19, 3, 16, 78, 87, 45, 43, 56, 59, 70, 40, 24, 83, 98, 62, 5, 96, 4, 67, 60, 26, 23, 72, 95, 44, 82, 90, 25, 7, 18, 15, 22, 71, 61], [78, 53, 89, 93, 7, 84, 81, 95, 64, 50, 58, 35, 59, 63, 20, 76, 80, 8, 23, 28, 79, 52, 55, 48, 66, 17, 60, 65, 25, 29, 30, 9, 73, 39, 69, 3, 98, 22, 15, 87, 4, 37, 43, 5, 11, 83, 91, 40, 51, 33, 12, 92, 6, 67, 41, 94, 45, 88, 56, 36, 0, 68, 19, 54, 70, 26, 72, 77, 32, 34, 97, 71, 99, 75, 61, 31, 86, 82, 46, 2, 44, 96, 49, 74, 10, 62, 24, 16, 1, 38, 27, 14, 18, 85, 13, 57, 90, 21, 42, 47], [26, 30, 60, 78, 75, 48, 73, 52, 51, 57, 63, 90, 27, 36, 83, 81, 41, 44, 82, 37, 10, 64, 91, 4, 59, 95, 21, 24, 77, 56, 43, 38, 62, 55, 92, 0, 94, 20, 86, 66, 35, 80, 53, 25, 17, 18, 49, 76, 23, 89, 13, 15, 68, 8, 22, 46, 1, 42, 3, 34, 98, 88, 69, 31, 50, 65, 84, 32, 47, 61, 9, 19, 99, 85, 11, 79, 70, 2, 74, 14, 87, 7, 72, 5, 12, 58, 67, 39, 16, 71, 54, 6, 45, 93, 96, 28, 97, 29, 33, 40], [77, 66, 20, 30, 42, 23, 0, 49, 7, 64, 54, 27, 96, 97, 39, 84, 2, 51, 71, 17, 4, 92, 94, 95, 67, 69, 73, 75, 86, 37, 74, 50, 11, 79, 29, 15, 56, 43, 22, 83, 81, 61, 14, 3, 36, 44, 58, 10, 62, 19, 72, 80, 65, 24, 18, 5, 40, 90, 33, 9, 47, 76, 8, 59, 35, 91, 99, 13, 70, 88, 55, 16, 31, 28, 85, 45, 60, 25, 87, 82, 93, 12, 34, 68, 63, 26, 46, 48, 21, 52, 1, 78, 98, 38, 6, 32, 89, 53, 41, 57]], [[77, 99, 47, 64, 26, 63, 50, 81, 23, 45, 49, 36, 43, 56, 39, 12, 86, 62, 2, 98, 60, 94, 75, 9, 20, 74, 48, 28, 4, 84, 61, 31, 54, 15, 40, 7, 87, 38, 80, 6, 37, 46, 1, 55, 88, 73, 95, 33, 92, 18, 25, 66, 57, 27, 42, 35, 91, 51, 13, 90, 41, 83, 68, 53, 19, 3, 72, 89, 65, 70, 71, 8, 97, 59, 96, 52, 79, 22, 14, 69, 32, 93, 58, 29, 21, 76, 34, 5, 17, 67, 11, 78, 16, 0, 44, 30, 10, 24, 85, 82], [58, 62, 7, 39, 34, 69, 67, 73, 37, 59, 71, 66, 46, 57, 44, 51, 26, 85, 11, 28, 54, 47, 82, 21, 83, 76, 94, 16, 90, 95, 75, 55, 6, 22, 72, 74, 50, 40, 10, 19, 8, 53, 33, 98, 45, 87, 91, 13, 35, 70, 14, 38, 84, 99, 56, 30, 12, 89, 25, 97, 63, 65, 81, 92, 18, 29, 80, 61, 93, 43, 49, 52, 23, 31, 32, 4, 48, 3, 0, 78, 96, 64, 41, 88, 24, 5, 9, 15, 77, 86, 68, 1, 17, 79, 60, 36, 42, 2, 20, 27], [58, 62, 7, 39, 34, 69, 67, 73, 37, 59, 71, 66, 46, 57, 44, 51, 26, 85, 11, 28, 54, 47, 82, 21, 83, 76, 94, 16, 90, 95, 75, 55, 6, 22, 72, 74, 50, 40, 10, 19, 8, 53, 33, 98, 45, 87, 91, 13, 35, 70, 14, 38, 84, 99, 56, 30, 12, 89, 25, 97, 63, 65, 81, 92, 18, 29, 80, 61, 93, 43, 49, 52, 23, 31, 32, 4, 48, 3, 0, 78, 96, 64, 41, 88, 24, 5, 9, 15, 77, 86, 68, 1, 17, 79, 60, 36, 42, 2, 20, 27], [91, 43, 77, 69, 16, 96, 47, 34, 49, 15, 73, 84, 85, 22, 88, 95, 2, 80, 57, 33, 32, 41, 25, 14, 58, 9, 20, 65, 48, 97, 79, 89, 66, 63, 46, 83, 19, 92, 55, 18, 38, 0, 21, 50, 45, 62, 29, 26, 52, 28, 8, 6, 75, 86, 44, 81, 36, 90, 70, 30, 71, 39, 82, 87, 76, 1, 23, 5, 60, 98, 67, 53, 93, 72, 42, 74, 51, 4, 31, 59, 61, 7, 24, 56, 37, 54, 3, 11, 27, 40, 10, 64, 94, 35, 78, 68, 99, 17, 12, 13], [73, 70, 39, 9, 77, 61, 80, 10, 13, 92, 38, 4, 48, 57, 25, 72, 49, 51, 99, 97, 85, 36, 22, 15, 8, 34, 33, 76, 26, 45, 83, 59, 28, 96, 90, 47, 68, 88, 44, 6, 98, 12, 24, 46, 2, 54, 5, 58, 30, 63, 1, 18, 93, 67, 7, 50, 31, 16, 40, 56, 23, 86, 11, 52, 55, 75, 71, 27, 37, 87, 84, 65, 82, 29, 0, 14, 41, 35, 64, 21, 3, 95, 17, 60, 81, 74, 19, 91, 94, 32, 69, 53, 42, 89, 79, 43, 66, 20, 78, 62], [23, 78, 67, 41, 9, 77, 59, 72, 25, 82, 65, 89, 28, 37, 54, 70, 98, 33, 31, 44, 6, 17, 26, 73, 21, 36, 4, 2, 79, 95, 81, 50, 18, 34, 61, 0, 93, 24, 8, 66, 55, 62, 56, 30, 84, 58, 7, 88, 85, 64, 97, 39, 49, 3, 20, 42, 80, 96, 13, 63, 43, 60, 47, 90, 51, 14, 53, 1, 86, 57, 12, 19, 10, 45, 38, 68, 35, 87, 75, 5, 69, 27, 32, 91, 11, 99, 46, 29, 92, 22, 76, 15, 16, 48, 71, 74, 52, 94, 83, 40], [87, 89, 35, 98, 44, 21, 29, 83, 0, 77, 34, 60, 85, 64, 28, 58, 39, 36, 95, 22, 67, 30, 86, 57, 5, 81, 84, 26, 53, 62, 13, 90, 2, 88, 4, 46, 97, 73, 79, 43, 59, 72, 54, 32, 82, 10, 55, 38, 47, 12, 51, 1, 76, 48, 7, 14, 23, 20, 31, 18, 17, 15, 37, 42, 49, 68, 25, 93, 91, 40, 92, 50, 52, 70, 33, 80, 19, 96, 61, 75, 71, 99, 74, 9, 41, 66, 63, 8, 6, 24, 65, 56, 45, 69, 27, 16, 3, 78, 94, 11], [99, 6, 38, 11, 34, 28, 86, 57, 55, 78, 25, 95, 13, 63, 91, 14, 83, 1, 45, 17, 93, 76, 46, 79, 27, 85, 96, 58, 59, 44, 87, 65, 97, 22, 0, 51, 49, 52, 47, 67, 24, 88, 62, 50, 72, 3, 31, 82, 68, 42, 81, 23, 32, 98, 73, 60, 37, 54, 41, 80, 66, 18, 92, 4, 5, 94, 77, 39, 69, 16, 35, 7, 40, 30, 61, 26, 56, 64, 9, 71, 19, 21, 75, 84, 43, 53, 33, 29, 74, 36, 90, 2, 20, 15, 10, 48, 89, 70, 8, 12], [4, 28, 8, 72, 74, 31, 66, 0, 27, 54, 61, 23, 51, 73, 96, 32, 98, 41, 88, 95, 36, 22, 13, 38, 70, 84, 12, 7, 10, 60, 68, 33, 11, 35, 79, 42, 16, 62, 57, 24, 44, 94, 25, 48, 86, 97, 87, 49, 92, 71, 21, 1, 81, 5, 17, 85, 6, 75, 80, 34, 20, 30, 90, 69, 18, 89, 93, 56, 91, 40, 77, 52, 53, 14, 65, 45, 76, 2, 9, 29, 3, 26, 59, 55, 83, 46, 67, 47, 39, 50, 58, 63, 99, 15, 37, 43, 82, 64, 78, 19], [11, 94, 1, 93, 59, 91, 89, 51, 61, 81, 13, 29, 27, 58, 26, 3, 53, 69, 73, 21, 39, 12, 5, 18, 98, 74, 9, 96, 28, 65, 23, 4, 41, 72, 88, 34, 83, 54, 7, 15, 38, 84, 90, 52, 77, 48, 64, 60, 20, 79, 19, 66, 97, 30, 87, 45, 40, 36, 14, 86, 32, 33, 57, 16, 43, 42, 50, 10, 44, 76, 37, 6, 56, 31, 68, 99, 2, 17, 22, 35, 92, 85, 46, 75, 71, 67, 95, 78, 8, 63, 82, 25, 80, 24, 47, 49, 0, 70, 62, 55]], [[93, 99, 51, 49, 71, 64, 35, 42, 88, 17, 67, 92, 4, 12, 68, 13, 47, 7, 56, 14, 19, 53, 80, 85, 23, 5, 39, 6, 26, 89, 27, 54, 63, 73, 70, 33, 36, 3, 86, 58, 18, 2, 25, 21, 10, 34, 81, 94, 75, 98, 37, 46, 9, 95, 60, 74, 87, 78, 20, 24, 57, 0, 30, 83, 72, 61, 11, 32, 43, 82, 65, 69, 59, 62, 38, 76, 22, 16, 15, 45, 48, 79, 50, 90, 40, 1, 29, 77, 91, 31, 84, 96, 8, 52, 97, 41, 28, 44, 55, 66], [64, 71, 19, 27, 43, 95, 77, 16, 35, 81, 74, 29, 6, 86, 18, 37, 99, 46, 15, 87, 28, 63, 1, 65, 3, 79, 50, 12, 78, 84, 8, 88, 98, 69, 42, 0, 56, 32, 92, 83, 30, 59, 80, 9, 20, 89, 47, 23, 33, 34, 82, 24, 97, 60, 22, 90, 51, 76, 10, 94, 21, 91, 70, 49, 66, 14, 75, 2, 7, 13, 40, 93, 48, 36, 53, 72, 67, 62, 85, 17, 57, 58, 5, 25, 41, 31, 44, 73, 61, 26, 38, 54, 96, 39, 4, 68, 11, 52, 45, 55], [49, 88, 62, 73, 34, 15, 95, 68, 98, 5, 61, 35, 23, 45, 78, 64, 97, 70, 96, 65, 74, 16, 94, 37, 80, 56, 48, 28, 59, 1, 75, 4, 71, 47, 83, 93, 69, 21, 60, 38, 39, 19, 40, 82, 55, 7, 52, 18, 43, 54, 2, 77, 66, 90, 44, 32, 89, 3, 24, 6, 99, 17, 86, 29, 30, 22, 50, 81, 41, 42, 0, 9, 53, 58, 57, 25, 20, 12, 26, 51, 92, 87, 36, 31, 85, 63, 14, 8, 91, 11, 79, 27, 76, 84, 67, 33, 72, 13, 10, 46], [45, 15, 69, 88, 51, 28, 34, 80, 30, 42, 38, 90, 47, 17, 84, 49, 21, 55, 46, 96, 37, 97, 72, 85, 94, 29, 10, 66, 76, 82, 18, 86, 87, 25, 43, 92, 19, 89, 2, 93, 98, 40, 48, 53, 75, 99, 11, 62, 12, 60, 3, 24, 4, 6, 63, 81, 41, 20, 68, 9, 57, 52, 0, 44, 14, 16, 91, 27, 79, 73, 59, 61, 78, 7, 67, 1, 70, 83, 74, 64, 26, 95, 54, 65, 33, 39, 5, 50, 36, 35, 13, 23, 58, 71, 32, 77, 31, 22, 56, 8], [41, 43, 66, 69, 22, 23, 71, 2, 33, 38, 86, 51, 55, 59, 82, 96, 45, 97, 73, 34, 70, 21, 8, 58, 84, 90, 24, 25, 32, 78, 17, 27, 47, 68, 37, 72, 63, 77, 87, 89, 50, 16, 83, 54, 12, 56, 64, 62, 39, 92, 91, 53, 29, 76, 52, 94, 42, 81, 20, 98, 40, 79, 44, 10, 31, 0, 57, 19, 75, 67, 93, 6, 7, 11, 74, 26, 85, 5, 1, 49, 4, 61, 95, 3, 36, 9, 14, 13, 15, 48, 80, 46, 30, 65, 99, 18, 60, 88, 35, 28], [5, 56, 10, 81, 42, 35, 12, 94, 34, 13, 8, 0, 49, 17, 7, 69, 30, 1, 65, 14, 39, 2, 58, 76, 54, 31, 50, 46, 11, 27, 22, 61, 74, 64, 91, 18, 48, 63, 71, 57, 95, 87, 47, 67, 77, 53, 88, 23, 3, 83, 29, 86, 43, 60, 70, 78, 21, 99, 36, 66, 20, 62, 85, 4, 80, 98, 79, 82, 97, 15, 96, 52, 51, 6, 40, 93, 28, 41, 72, 45, 68, 16, 32, 75, 84, 89, 26, 55, 9, 59, 73, 24, 25, 33, 37, 19, 90, 44, 38, 92], [62, 28, 97, 69, 65, 46, 27, 96, 29, 73, 89, 42, 16, 36, 44, 63, 80, 60, 11, 54, 87, 92, 75, 81, 57, 26, 70, 49, 76, 10, 95, 84, 56, 22, 20, 9, 21, 61, 1, 47, 34, 19, 66, 0, 33, 91, 45, 3, 2, 90, 88, 43, 51, 12, 7, 18, 24, 85, 86, 8, 78, 38, 55, 30, 6, 35, 15, 93, 14, 99, 98, 82, 52, 74, 4, 50, 79, 37, 77, 17, 41, 71, 58, 53, 68, 39, 67, 25, 5, 83, 94, 13, 48, 32, 23, 59, 31, 64, 72, 40], [93, 20, 18, 22, 94, 27, 39, 59, 4, 11, 6, 75, 98, 62, 63, 81, 44, 53, 25, 95, 74, 57, 76, 5, 70, 60, 99, 12, 56, 85, 41, 67, 46, 19, 28, 1, 23, 80, 32, 68, 31, 51, 82, 55, 78, 77, 58, 69, 38, 87, 91, 79, 96, 52, 47, 73, 83, 89, 65, 3, 92, 14, 43, 66, 61, 37, 84, 54, 33, 88, 35, 71, 42, 90, 72, 50, 7, 40, 0, 24, 9, 29, 49, 45, 15, 21, 86, 64, 16, 36, 34, 17, 26, 10, 2, 13, 8, 97, 30, 48], [33, 3, 7, 64, 74, 23, 42, 25, 68, 85, 5, 8, 83, 17, 56, 79, 70, 73, 15, 6, 36, 18, 94, 97, 72, 22, 11, 96, 40, 58, 66, 49, 62, 86, 78, 67, 26, 98, 90, 48, 14, 2, 82, 91, 9, 76, 69, 44, 50, 47, 53, 71, 92, 12, 59, 37, 19, 60, 39, 13, 77, 16, 55, 41, 84, 31, 65, 27, 99, 51, 4, 45, 80, 46, 57, 21, 28, 81, 54, 29, 61, 88, 52, 43, 75, 20, 1, 10, 0, 35, 30, 32, 95, 38, 63, 87, 89, 24, 34, 93], [37, 25, 50, 66, 86, 23, 76, 27, 83, 97, 89, 35, 32, 41, 33, 87, 14, 59, 48, 61, 52, 3, 73, 67, 24, 56, 93, 22, 7, 70, 77, 28, 91, 30, 68, 29, 60, 8, 94, 13, 43, 17, 88, 57, 81, 34, 15, 95, 79, 96, 72, 84, 55, 46, 58, 10, 12, 38, 11, 99, 53, 42, 65, 44, 31, 21, 82, 6, 92, 26, 51, 5, 39, 63, 40, 9, 49, 18, 78, 80, 71, 74, 90, 2, 54, 47, 85, 75, 69, 45, 0, 16, 19, 1, 64, 98, 4, 62, 36, 20]]];\nconst sbtable2 = [[3, 5, 8, 6, 0, 9, 7, 1, 2, 4], [1, 3, 9, 4, 2, 0, 8, 7, 5, 6], [0, 5, 1, 7, 9, 8, 6, 2, 3, 4], [6, 4, 7, 8, 3, 0, 9, 5, 2, 1], [2, 3, 8, 1, 9, 7, 6, 0, 4, 5], [3, 4, 1, 7, 2, 8, 6, 0, 9, 5], [6, 1, 8, 3, 0, 4, 2, 7, 5, 9], [9, 3, 7, 5, 2, 4, 0, 6, 1, 8], [6, 1, 5, 3, 0, 8, 7, 4, 2, 9], [9, 7, 4, 5, 1, 2, 3, 0, 8, 6]];\n\nconst ibtable = [[[11, 38, 3, 63, 36, 39, 79, 19, 87, 68, 76, 51, 20, 56, 77, 61, 52, 73, 74, 94, 45, 31, 99, 28, 29, 90, 81, 96, 65, 75, 13, 32, 70, 21, 55, 33, 9, 15, 92, 14, 82, 97, 78, 37, 49, 0, 80, 57, 58, 7, 1, 89, 98, 53, 64, 5, 6, 66, 43, 83, 10, 50, 47, 84, 62, 71, 4, 67, 86, 42, 35, 34, 91, 12, 17, 69, 44, 48, 85, 30, 16, 95, 54, 23, 46, 18, 88, 59, 40, 60, 41, 25, 27, 22, 2, 24, 72, 93, 26, 8], [28, 58, 74, 34, 6, 14, 16, 46, 87, 24, 59, 57, 23, 88, 75, 65, 79, 38, 82, 13, 49, 99, 8, 94, 15, 19, 0, 96, 41, 95, 45, 35, 1, 91, 2, 52, 42, 76, 90, 84, 20, 54, 4, 63, 25, 5, 47, 85, 93, 12, 51, 81, 71, 69, 22, 17, 36, 64, 77, 80, 98, 53, 44, 18, 10, 68, 97, 61, 56, 70, 92, 27, 40, 9, 60, 7, 72, 21, 43, 3, 33, 32, 50, 26, 67, 62, 39, 48, 73, 86, 29, 89, 37, 78, 55, 66, 31, 30, 11, 83], [4, 16, 22, 66, 37, 14, 47, 89, 59, 60, 51, 92, 71, 70, 96, 13, 17, 85, 58, 93, 81, 69, 28, 90, 78, 39, 63, 88, 0, 75, 2, 53, 23, 99, 94, 68, 54, 74, 41, 29, 8, 26, 50, 98, 82, 95, 42, 7, 24, 77, 30, 9, 21, 1, 87, 62, 46, 15, 43, 38, 36, 55, 49, 57, 52, 56, 91, 65, 11, 84, 6, 73, 97, 33, 83, 27, 35, 25, 40, 32, 18, 31, 86, 48, 45, 3, 79, 76, 72, 64, 44, 20, 80, 19, 10, 5, 61, 34, 67, 12], [80, 91, 71, 26, 75, 8, 22, 92, 7, 70, 65, 51, 89, 31, 81, 30, 83, 47, 44, 85, 55, 19, 57, 87, 74, 6, 73, 40, 49, 32, 52, 78, 0, 12, 54, 15, 64, 28, 4, 24, 68, 17, 72, 53, 1, 94, 58, 88, 11, 82, 43, 86, 60, 63, 99, 96, 37, 77, 67, 33, 18, 5, 41, 59, 16, 36, 79, 97, 61, 23, 56, 48, 98, 38, 66, 21, 34, 29, 10, 3, 93, 46, 50, 39, 14, 9, 2, 13, 69, 42, 62, 35, 27, 90, 25, 76, 20, 95, 45, 84], [48, 16, 54, 47, 9, 39, 81, 91, 33, 53, 63, 21, 50, 85, 97, 90, 49, 36, 84, 74, 51, 76, 37, 67, 59, 58, 23, 20, 65, 82, 7, 77, 12, 31, 46, 1, 5, 14, 3, 30, 94, 95, 2, 34, 70, 89, 40, 87, 43, 79, 64, 57, 68, 26, 56, 44, 88, 61, 0, 24, 71, 18, 60, 4, 80, 96, 27, 15, 6, 13, 98, 29, 28, 25, 72, 93, 55, 75, 52, 66, 11, 8, 86, 45, 22, 83, 17, 10, 35, 38, 73, 99, 62, 41, 19, 69, 78, 92, 42, 32], [23, 19, 88, 44, 5, 39, 75, 81, 30, 14, 49, 54, 62, 55, 18, 83, 77, 76, 74, 92, 6, 95, 52, 36, 15, 27, 53, 38, 28, 50, 48, 99, 78, 67, 90, 87, 16, 41, 58, 65, 79, 11, 68, 84, 24, 97, 64, 47, 9, 22, 43, 86, 37, 8, 33, 17, 93, 61, 25, 72, 57, 98, 42, 80, 13, 89, 2, 12, 31, 56, 26, 85, 3, 29, 66, 63, 82, 10, 20, 94, 51, 0, 1, 4, 21, 60, 34, 35, 32, 45, 59, 71, 46, 69, 7, 96, 91, 40, 70, 73], [89, 62, 33, 19, 21, 97, 15, 2, 83, 22, 65, 30, 55, 7, 63, 73, 39, 61, 44, 37, 11, 26, 31, 79, 91, 0, 72, 68, 23, 87, 28, 24, 45, 74, 98, 14, 70, 54, 88, 42, 35, 59, 46, 16, 27, 92, 69, 66, 94, 32, 75, 77, 64, 57, 50, 71, 10, 3, 67, 81, 90, 58, 99, 5, 82, 85, 25, 52, 12, 8, 48, 49, 76, 96, 80, 20, 29, 34, 53, 51, 86, 93, 38, 84, 9, 36, 6, 60, 1, 78, 13, 95, 41, 17, 47, 18, 43, 40, 4, 56], [40, 68, 88, 47, 43, 28, 67, 23, 42, 99, 48, 3, 12, 27, 79, 25, 6, 55, 19, 80, 81, 62, 83, 69, 39, 73, 36, 30, 44, 82, 70, 52, 31, 34, 58, 74, 54, 2, 13, 45, 93, 49, 75, 72, 29, 87, 5, 7, 17, 76, 90, 63, 84, 51, 59, 35, 20, 97, 22, 65, 57, 66, 78, 86, 11, 9, 71, 98, 53, 56, 15, 46, 92, 0, 26, 21, 32, 38, 77, 91, 4, 24, 61, 85, 94, 1, 96, 64, 8, 41, 33, 50, 18, 60, 10, 16, 37, 95, 14, 89], [96, 23, 99, 42, 94, 91, 19, 53, 41, 25, 39, 73, 70, 72, 33, 35, 88, 2, 63, 49, 95, 60, 48, 97, 5, 54, 47, 40, 98, 18, 57, 28, 67, 92, 10, 79, 13, 76, 8, 55, 90, 62, 50, 87, 65, 82, 84, 21, 0, 14, 45, 44, 20, 38, 17, 3, 81, 16, 58, 4, 31, 29, 86, 15, 43, 64, 26, 52, 46, 69, 37, 68, 6, 22, 61, 7, 83, 71, 30, 75, 85, 78, 24, 27, 89, 9, 80, 34, 66, 77, 36, 93, 56, 59, 1, 12, 51, 32, 11, 74], [75, 99, 97, 8, 15, 48, 6, 25, 60, 90, 91, 95, 23, 3, 89, 77, 22, 78, 94, 55, 98, 35, 53, 96, 18, 52, 12, 16, 50, 20, 49, 9, 17, 13, 57, 44, 14, 47, 86, 56, 88, 41, 29, 32, 70, 1, 62, 37, 85, 38, 59, 36, 5, 68, 71, 69, 67, 27, 39, 4, 54, 0, 84, 45, 61, 73, 79, 93, 58, 66, 10, 76, 51, 92, 19, 87, 24, 7, 28, 2, 11, 80, 81, 33, 64, 74, 46, 65, 26, 72, 40, 82, 34, 42, 21, 83, 31, 30, 43, 63]], [[98, 58, 34, 65, 83, 74, 88, 5, 89, 46, 80, 43, 18, 87, 9, 28, 71, 90, 4, 36, 40, 11, 39, 31, 76, 92, 82, 48, 66, 41, 49, 99, 78, 79, 97, 86, 75, 68, 3, 7, 62, 64, 26, 22, 13, 77, 54, 50, 10, 96, 15, 35, 94, 73, 57, 93, 16, 61, 37, 38, 53, 2, 63, 23, 67, 72, 95, 70, 29, 33, 45, 47, 27, 24, 69, 44, 55, 85, 14, 8, 91, 12, 42, 6, 84, 81, 19, 25, 32, 51, 1, 17, 52, 56, 60, 0, 59, 30, 21, 20], [26, 63, 99, 57, 77, 53, 9, 75, 76, 80, 46, 97, 37, 14, 1, 12, 35, 20, 0, 15, 28, 19, 48, 41, 67, 30, 60, 69, 29, 34, 73, 10, 51, 47, 66, 82, 7, 98, 39, 96, 8, 62, 42, 3, 95, 5, 11, 13, 90, 94, 32, 31, 83, 64, 36, 88, 72, 52, 38, 54, 45, 17, 61, 81, 78, 44, 79, 55, 56, 86, 2, 40, 16, 24, 4, 71, 49, 33, 23, 92, 84, 74, 65, 43, 87, 59, 89, 68, 22, 70, 25, 58, 21, 85, 6, 50, 27, 93, 91, 18], [22, 31, 11, 72, 37, 70, 90, 60, 24, 56, 28, 69, 54, 85, 5, 98, 1, 76, 59, 20, 86, 32, 61, 49, 63, 42, 74, 53, 84, 3, 95, 23, 15, 36, 4, 81, 6, 21, 19, 27, 17, 83, 30, 45, 25, 65, 10, 50, 80, 66, 46, 75, 93, 82, 78, 99, 97, 68, 8, 13, 58, 94, 41, 14, 48, 52, 29, 79, 91, 89, 9, 55, 57, 2, 39, 92, 0, 44, 18, 62, 73, 96, 34, 88, 16, 77, 71, 43, 33, 40, 7, 12, 87, 51, 26, 47, 64, 35, 67, 38], [0, 80, 63, 52, 49, 69, 15, 87, 55, 60, 22, 91, 31, 89, 78, 28, 48, 12, 10, 42, 82, 23, 29, 18, 21, 2, 19, 7, 68, 37, 54, 90, 81, 43, 57, 94, 26, 95, 96, 88, 14, 51, 79, 71, 66, 76, 34, 24, 30, 9, 62, 44, 77, 65, 6, 27, 4, 97, 38, 36, 59, 3, 1, 50, 72, 46, 64, 35, 20, 11, 53, 13, 58, 39, 74, 8, 83, 75, 40, 98, 17, 99, 85, 32, 92, 67, 84, 5, 25, 56, 16, 47, 70, 93, 86, 61, 73, 41, 45, 33], [64, 57, 56, 7, 86, 9, 65, 16, 1, 63, 71, 26, 53, 98, 69, 5, 84, 28, 36, 58, 54, 99, 42, 74, 68, 3, 88, 22, 82, 97, 85, 45, 21, 66, 43, 59, 83, 51, 89, 87, 31, 12, 95, 52, 60, 17, 80, 27, 72, 93, 10, 39, 91, 30, 75, 61, 24, 8, 23, 2, 18, 73, 94, 6, 38, 44, 13, 40, 48, 4, 81, 37, 96, 46, 19, 49, 34, 50, 0, 20, 14, 15, 25, 92, 33, 29, 77, 90, 78, 67, 62, 41, 35, 47, 79, 32, 11, 70, 76, 55], [24, 99, 28, 52, 64, 3, 63, 16, 15, 87, 47, 6, 59, 98, 13, 78, 57, 81, 56, 94, 0, 37, 70, 73, 65, 83, 90, 92, 60, 82, 67, 40, 74, 34, 91, 2, 33, 85, 21, 54, 12, 31, 84, 55, 53, 49, 36, 25, 39, 20, 29, 93, 51, 76, 42, 96, 71, 86, 95, 66, 45, 19, 8, 58, 22, 69, 80, 79, 89, 17, 61, 18, 88, 38, 72, 75, 48, 30, 7, 10, 11, 35, 23, 9, 26, 4, 62, 44, 1, 43, 46, 14, 41, 77, 97, 27, 68, 32, 5, 50], [90, 80, 22, 27, 16, 23, 11, 31, 8, 75, 54, 82, 68, 66, 10, 40, 95, 99, 14, 42, 56, 88, 12, 55, 38, 6, 79, 84, 26, 92, 4, 67, 48, 5, 46, 77, 19, 53, 97, 34, 64, 73, 58, 47, 18, 39, 81, 59, 74, 62, 87, 98, 50, 1, 25, 9, 72, 69, 61, 60, 86, 3, 24, 28, 45, 30, 96, 36, 57, 83, 89, 63, 0, 33, 17, 7, 94, 15, 43, 2, 44, 41, 20, 85, 32, 65, 93, 49, 91, 52, 71, 37, 35, 13, 21, 51, 70, 29, 78, 76], [95, 50, 72, 0, 68, 98, 49, 53, 56, 41, 54, 22, 57, 78, 66, 31, 52, 29, 86, 30, 62, 26, 32, 46, 42, 48, 14, 21, 88, 94, 77, 92, 7, 63, 91, 28, 93, 38, 90, 71, 34, 25, 11, 83, 55, 70, 97, 82, 89, 76, 10, 99, 9, 45, 60, 39, 44, 16, 17, 37, 47, 79, 33, 36, 59, 8, 80, 23, 1, 18, 64, 51, 61, 81, 75, 40, 35, 73, 13, 65, 12, 4, 19, 85, 96, 6, 2, 58, 27, 43, 5, 87, 20, 15, 3, 24, 84, 67, 74, 69], [38, 33, 29, 88, 49, 47, 60, 69, 75, 12, 44, 89, 35, 45, 23, 30, 21, 28, 65, 86, 81, 40, 93, 97, 67, 2, 94, 9, 61, 92, 90, 32, 62, 36, 58, 51, 37, 73, 50, 63, 39, 43, 16, 71, 46, 91, 13, 7, 20, 72, 41, 59, 70, 48, 10, 52, 5, 34, 76, 15, 87, 6, 98, 18, 57, 77, 24, 79, 27, 19, 3, 0, 8, 95, 74, 64, 42, 4, 68, 31, 84, 54, 99, 25, 1, 80, 85, 26, 83, 11, 56, 55, 78, 66, 82, 14, 53, 22, 17, 96], [26, 12, 62, 5, 78, 53, 31, 46, 51, 8, 69, 80, 32, 68, 76, 42, 29, 87, 28, 75, 40, 91, 58, 7, 88, 92, 82, 96, 70, 50, 47, 65, 85, 3, 48, 73, 90, 89, 97, 84, 81, 39, 43, 79, 64, 54, 2, 41, 38, 10, 24, 22, 25, 93, 44, 23, 49, 37, 14, 95, 72, 74, 15, 11, 55, 98, 99, 0, 9, 16, 18, 20, 63, 30, 57, 19, 4, 17, 67, 35, 60, 45, 59, 56, 36, 77, 66, 33, 27, 71, 86, 1, 13, 34, 21, 61, 6, 94, 52, 83]], [[10, 87, 64, 11, 25, 97, 93, 18, 99, 38, 17, 72, 50, 23, 90, 57, 98, 12, 29, 46, 19, 77, 42, 15, 73, 52, 53, 89, 35, 20, 9, 41, 79, 13, 95, 48, 85, 54, 61, 94, 0, 74, 62, 40, 78, 68, 24, 28, 86, 21, 59, 14, 82, 84, 92, 58, 34, 31, 6, 16, 75, 81, 43, 47, 7, 3, 2, 37, 67, 8, 66, 22, 30, 32, 71, 44, 70, 1, 36, 26, 33, 63, 80, 83, 91, 76, 56, 39, 69, 55, 96, 60, 27, 49, 65, 88, 5, 4, 51, 45], [73, 87, 48, 43, 71, 40, 88, 23, 60, 35, 30, 5, 75, 25, 59, 95, 58, 79, 4, 89, 96, 17, 38, 6, 39, 9, 68, 81, 3, 90, 93, 99, 16, 42, 21, 45, 74, 84, 77, 65, 63, 98, 11, 51, 0, 33, 91, 22, 69, 34, 31, 20, 46, 2, 76, 54, 15, 62, 13, 70, 92, 55, 82, 47, 78, 7, 24, 27, 86, 1, 56, 61, 12, 44, 85, 57, 49, 19, 18, 67, 66, 52, 72, 53, 83, 41, 64, 50, 26, 94, 32, 80, 28, 10, 37, 14, 8, 97, 29, 36], [60, 50, 53, 78, 63, 90, 11, 33, 21, 24, 20, 55, 8, 25, 6, 58, 3, 98, 44, 82, 96, 14, 1, 59, 92, 75, 51, 36, 30, 64, 72, 40, 16, 94, 2, 74, 93, 85, 12, 10, 43, 32, 42, 39, 79, 97, 17, 38, 89, 29, 68, 52, 49, 0, 34, 19, 70, 84, 54, 9, 23, 71, 65, 28, 83, 56, 67, 57, 13, 18, 77, 5, 4, 88, 41, 22, 46, 76, 48, 61, 81, 45, 86, 35, 47, 87, 91, 99, 73, 66, 27, 37, 31, 15, 7, 80, 62, 95, 69, 26], [49, 73, 63, 66, 38, 35, 32, 96, 87, 26, 36, 92, 58, 6, 17, 47, 29, 84, 11, 18, 67, 45, 78, 4, 52, 81, 59, 30, 91, 54, 56, 89, 48, 46, 20, 79, 1, 62, 9, 53, 10, 95, 88, 34, 40, 80, 41, 64, 83, 55, 43, 7, 24, 61, 77, 5, 14, 93, 16, 21, 85, 39, 76, 50, 57, 65, 37, 60, 28, 19, 2, 97, 42, 33, 13, 68, 90, 25, 69, 99, 98, 74, 3, 8, 12, 15, 82, 71, 31, 23, 75, 27, 51, 94, 86, 0, 44, 22, 72, 70], [30, 32, 52, 21, 15, 79, 53, 40, 56, 44, 55, 6, 63, 39, 61, 4, 93, 43, 23, 99, 38, 66, 88, 18, 1, 86, 33, 9, 82, 76, 70, 51, 48, 69, 85, 11, 5, 91, 59, 77, 49, 75, 22, 87, 65, 41, 26, 50, 47, 98, 57, 10, 58, 62, 46, 28, 20, 71, 78, 95, 0, 72, 80, 73, 92, 36, 96, 37, 68, 2, 35, 83, 90, 25, 13, 24, 34, 42, 45, 74, 67, 16, 84, 97, 19, 64, 27, 14, 31, 12, 54, 60, 3, 8, 17, 89, 81, 94, 7, 29], [1, 18, 25, 13, 57, 53, 92, 95, 94, 88, 10, 82, 34, 83, 7, 28, 55, 11, 5, 58, 8, 9, 74, 99, 52, 32, 62, 45, 65, 50, 41, 56, 96, 44, 38, 47, 3, 39, 81, 19, 2, 22, 49, 12, 40, 67, 89, 76, 64, 70, 61, 21, 31, 54, 51, 17, 46, 30, 24, 60, 66, 26, 77, 63, 37, 35, 23, 6, 4, 87, 79, 97, 20, 48, 36, 85, 73, 71, 27, 43, 0, 15, 69, 78, 59, 86, 75, 33, 42, 84, 91, 93, 68, 80, 16, 98, 90, 72, 14, 29], [30, 16, 10, 67, 83, 81, 42, 94, 35, 59, 22, 5, 53, 61, 1, 96, 68, 45, 95, 66, 36, 37, 97, 87, 77, 93, 86, 33, 49, 13, 43, 14, 0, 34, 7, 2, 41, 21, 31, 32, 76, 55, 62, 72, 90, 71, 38, 23, 46, 12, 51, 19, 6, 44, 52, 48, 73, 24, 29, 74, 85, 99, 80, 28, 47, 9, 39, 84, 64, 57, 75, 98, 88, 26, 17, 63, 27, 56, 69, 8, 54, 25, 91, 78, 3, 50, 65, 70, 11, 4, 92, 60, 40, 58, 15, 89, 82, 18, 79, 20], [60, 88, 79, 35, 40, 43, 52, 4, 17, 31, 84, 44, 50, 94, 91, 38, 87, 25, 92, 62, 14, 97, 37, 18, 86, 28, 65, 90, 19, 29, 30, 75, 68, 49, 69, 11, 59, 41, 89, 33, 47, 54, 98, 42, 80, 56, 78, 99, 23, 82, 9, 48, 21, 1, 63, 22, 58, 95, 10, 12, 26, 74, 85, 13, 8, 27, 24, 53, 61, 34, 64, 71, 66, 32, 83, 73, 15, 67, 0, 20, 16, 6, 77, 45, 5, 93, 76, 39, 57, 2, 96, 46, 51, 3, 55, 7, 81, 70, 36, 72], [35, 56, 77, 58, 23, 83, 91, 81, 53, 70, 20, 74, 84, 50, 79, 51, 88, 44, 45, 71, 37, 26, 54, 48, 27, 43, 0, 12, 95, 97, 1, 63, 67, 98, 59, 40, 13, 19, 31, 87, 99, 16, 57, 30, 17, 92, 55, 68, 5, 46, 64, 8, 7, 42, 90, 33, 29, 9, 85, 24, 2, 69, 32, 10, 21, 65, 39, 86, 52, 62, 76, 89, 82, 6, 78, 4, 47, 28, 3, 75, 41, 15, 18, 14, 66, 73, 38, 80, 61, 49, 11, 22, 34, 93, 36, 25, 94, 96, 60, 72], [6, 90, 16, 43, 20, 55, 94, 8, 62, 59, 47, 32, 81, 67, 42, 35, 71, 19, 54, 49, 2, 88, 38, 5, 53, 77, 85, 11, 73, 34, 3, 72, 95, 58, 82, 64, 44, 29, 93, 14, 56, 98, 4, 37, 45, 75, 86, 60, 87, 7, 31, 17, 89, 97, 10, 70, 36, 99, 46, 63, 76, 41, 48, 84, 9, 52, 1, 24, 83, 25, 68, 18, 50, 26, 30, 27, 61, 0, 91, 33, 51, 40, 79, 39, 15, 74, 28, 78, 69, 96, 57, 65, 21, 80, 22, 23, 12, 13, 92, 66]], [[93, 42, 18, 65, 28, 87, 39, 35, 71, 23, 96, 90, 15, 58, 78, 33, 92, 88, 49, 64, 24, 84, 77, 8, 97, 50, 4, 53, 27, 83, 95, 31, 80, 47, 86, 55, 11, 40, 37, 14, 34, 60, 54, 12, 94, 9, 41, 2, 26, 10, 6, 57, 75, 63, 32, 43, 13, 52, 82, 73, 20, 30, 17, 5, 3, 68, 51, 89, 62, 79, 69, 70, 66, 45, 25, 22, 85, 0, 91, 76, 38, 7, 99, 61, 29, 98, 16, 36, 44, 67, 59, 56, 48, 81, 21, 46, 74, 72, 19, 1], [78, 91, 97, 77, 75, 85, 32, 2, 40, 86, 38, 18, 56, 47, 50, 87, 27, 92, 64, 39, 98, 23, 33, 72, 84, 58, 16, 99, 19, 65, 55, 73, 74, 42, 4, 48, 95, 8, 51, 3, 37, 82, 96, 69, 14, 44, 12, 21, 76, 70, 36, 15, 71, 41, 20, 31, 54, 13, 0, 9, 94, 67, 1, 60, 81, 61, 11, 6, 90, 5, 49, 10, 34, 7, 35, 30, 25, 88, 79, 93, 66, 62, 22, 24, 52, 17, 89, 45, 83, 57, 28, 46, 63, 68, 26, 29, 80, 59, 43, 53], [78, 91, 97, 77, 75, 85, 32, 2, 40, 86, 38, 18, 56, 47, 50, 87, 27, 92, 64, 39, 98, 23, 33, 72, 84, 58, 16, 99, 19, 65, 55, 73, 74, 42, 4, 48, 95, 8, 51, 3, 37, 82, 96, 69, 14, 44, 12, 21, 76, 70, 36, 15, 71, 41, 20, 31, 54, 13, 0, 9, 94, 67, 1, 60, 81, 61, 11, 6, 90, 5, 49, 10, 34, 7, 35, 30, 25, 88, 79, 93, 66, 62, 22, 24, 52, 17, 89, 45, 83, 57, 28, 46, 63, 68, 26, 29, 80, 59, 43, 53], [41, 65, 16, 86, 77, 67, 51, 81, 50, 25, 90, 87, 98, 99, 23, 9, 4, 97, 39, 36, 26, 42, 13, 66, 82, 22, 47, 88, 49, 46, 59, 78, 20, 19, 7, 93, 56, 84, 40, 61, 89, 21, 74, 1, 54, 44, 34, 6, 28, 8, 43, 76, 48, 71, 85, 38, 83, 18, 24, 79, 68, 80, 45, 33, 91, 27, 32, 70, 95, 3, 58, 60, 73, 10, 75, 52, 64, 2, 94, 30, 17, 55, 62, 35, 11, 12, 53, 63, 14, 31, 57, 0, 37, 72, 92, 15, 5, 29, 69, 96], [74, 50, 44, 80, 11, 46, 39, 54, 24, 3, 7, 62, 41, 8, 75, 23, 57, 82, 51, 86, 97, 79, 22, 60, 42, 14, 28, 67, 32, 73, 48, 56, 89, 26, 25, 77, 21, 68, 10, 2, 58, 76, 92, 95, 38, 29, 43, 35, 12, 16, 55, 17, 63, 91, 45, 64, 59, 13, 47, 31, 83, 5, 99, 49, 78, 71, 96, 53, 36, 90, 1, 66, 15, 0, 85, 65, 27, 4, 98, 94, 6, 84, 72, 30, 70, 20, 61, 69, 37, 93, 34, 87, 9, 52, 88, 81, 33, 19, 40, 18], [35, 67, 27, 53, 26, 79, 20, 46, 38, 4, 72, 84, 70, 58, 65, 91, 92, 21, 32, 71, 54, 24, 89, 0, 37, 8, 22, 81, 12, 87, 43, 18, 82, 17, 33, 76, 25, 13, 74, 51, 99, 3, 55, 60, 19, 73, 86, 62, 93, 52, 31, 64, 96, 66, 14, 40, 42, 69, 45, 6, 61, 34, 41, 59, 49, 10, 39, 2, 75, 80, 15, 94, 7, 23, 95, 78, 90, 5, 1, 28, 56, 30, 9, 98, 44, 48, 68, 77, 47, 11, 63, 83, 88, 36, 97, 29, 57, 50, 16, 85], [8, 51, 32, 96, 34, 24, 88, 54, 87, 83, 45, 99, 49, 30, 55, 61, 95, 60, 59, 76, 57, 5, 19, 56, 89, 66, 27, 94, 14, 6, 21, 58, 43, 74, 10, 2, 17, 62, 47, 16, 69, 84, 63, 39, 4, 92, 35, 48, 53, 64, 71, 50, 72, 28, 42, 46, 91, 23, 15, 40, 11, 78, 29, 86, 13, 90, 85, 20, 65, 93, 73, 80, 41, 37, 82, 79, 52, 9, 97, 38, 75, 25, 44, 7, 26, 12, 22, 0, 33, 1, 31, 68, 70, 67, 98, 18, 77, 36, 3, 81], [34, 17, 91, 45, 63, 64, 1, 71, 98, 78, 94, 3, 99, 12, 15, 93, 69, 19, 61, 80, 92, 81, 33, 51, 40, 10, 75, 24, 5, 87, 73, 46, 52, 86, 4, 70, 89, 56, 2, 67, 72, 58, 49, 84, 29, 18, 22, 38, 95, 36, 43, 35, 37, 85, 57, 8, 76, 7, 27, 28, 55, 74, 42, 13, 77, 31, 60, 39, 48, 68, 97, 79, 44, 54, 88, 82, 21, 66, 9, 23, 59, 50, 47, 16, 83, 25, 6, 30, 41, 96, 90, 14, 62, 20, 65, 11, 26, 32, 53, 0], [7, 51, 77, 80, 0, 53, 56, 27, 2, 78, 28, 32, 26, 22, 73, 93, 36, 54, 64, 99, 60, 50, 21, 11, 39, 42, 81, 8, 1, 79, 61, 5, 15, 31, 59, 33, 20, 94, 23, 88, 69, 17, 35, 95, 40, 75, 85, 87, 43, 47, 89, 12, 71, 72, 9, 83, 67, 38, 90, 82, 29, 10, 37, 91, 97, 74, 6, 86, 30, 63, 24, 49, 3, 13, 4, 57, 76, 70, 98, 34, 58, 52, 96, 84, 25, 55, 44, 46, 18, 65, 62, 68, 48, 66, 41, 19, 14, 45, 16, 92], [96, 2, 76, 15, 31, 22, 71, 38, 88, 26, 67, 0, 21, 10, 58, 39, 63, 77, 23, 50, 48, 19, 78, 30, 93, 91, 14, 12, 28, 11, 53, 73, 60, 61, 35, 79, 57, 70, 40, 20, 56, 32, 65, 64, 68, 55, 82, 94, 45, 95, 66, 7, 43, 16, 37, 99, 72, 62, 13, 4, 47, 8, 98, 89, 46, 29, 51, 85, 74, 17, 97, 84, 33, 18, 25, 83, 69, 44, 87, 49, 92, 9, 90, 36, 41, 81, 59, 54, 34, 6, 42, 5, 80, 3, 1, 86, 27, 52, 24, 75]], [[61, 85, 41, 37, 12, 25, 27, 17, 92, 52, 44, 66, 13, 15, 19, 78, 77, 9, 40, 20, 58, 43, 76, 24, 59, 42, 28, 30, 96, 86, 62, 89, 67, 35, 45, 6, 36, 50, 74, 26, 84, 95, 7, 68, 97, 79, 51, 16, 80, 3, 82, 2, 93, 21, 31, 98, 18, 60, 39, 72, 54, 65, 73, 32, 5, 70, 99, 10, 14, 71, 34, 4, 64, 33, 55, 48, 75, 87, 57, 81, 22, 46, 69, 63, 90, 23, 38, 56, 8, 29, 83, 88, 11, 0, 47, 53, 91, 94, 49, 1], [35, 22, 67, 24, 94, 82, 12, 68, 30, 43, 58, 96, 27, 69, 65, 18, 7, 79, 14, 2, 44, 60, 54, 47, 51, 83, 89, 3, 20, 11, 40, 85, 37, 48, 49, 8, 73, 15, 90, 93, 70, 84, 34, 4, 86, 98, 17, 46, 72, 63, 26, 56, 97, 74, 91, 99, 36, 80, 81, 41, 53, 88, 77, 21, 0, 23, 64, 76, 95, 33, 62, 1, 75, 87, 10, 66, 57, 6, 28, 25, 42, 9, 50, 39, 29, 78, 13, 19, 31, 45, 55, 61, 38, 71, 59, 5, 92, 52, 32, 16], [70, 29, 50, 57, 31, 9, 59, 45, 87, 71, 98, 89, 77, 97, 86, 5, 21, 61, 47, 41, 76, 37, 65, 12, 58, 75, 78, 91, 27, 63, 64, 83, 55, 95, 4, 11, 82, 23, 39, 40, 42, 68, 69, 48, 54, 13, 99, 33, 26, 0, 66, 79, 46, 72, 49, 44, 25, 74, 73, 28, 38, 10, 2, 85, 15, 19, 52, 94, 7, 36, 17, 32, 96, 3, 20, 30, 92, 51, 14, 90, 24, 67, 43, 34, 93, 84, 62, 81, 1, 56, 53, 88, 80, 35, 22, 6, 18, 16, 8, 60], [62, 75, 38, 50, 52, 86, 53, 73, 99, 59, 26, 46, 48, 90, 64, 1, 65, 13, 30, 36, 57, 16, 97, 91, 51, 33, 80, 67, 5, 25, 8, 96, 94, 84, 6, 89, 88, 20, 10, 85, 41, 56, 9, 34, 63, 0, 18, 12, 42, 15, 87, 4, 61, 43, 82, 17, 98, 60, 92, 70, 49, 71, 47, 54, 79, 83, 27, 74, 58, 2, 76, 93, 22, 69, 78, 44, 28, 95, 72, 68, 7, 55, 29, 77, 14, 23, 31, 32, 3, 37, 11, 66, 35, 39, 24, 81, 19, 21, 40, 45], [65, 78, 7, 83, 80, 77, 71, 72, 22, 85, 63, 73, 44, 87, 86, 88, 41, 30, 95, 67, 58, 21, 4, 5, 26, 27, 75, 31, 99, 52, 92, 64, 28, 8, 19, 98, 84, 34, 9, 48, 60, 0, 56, 1, 62, 16, 91, 32, 89, 79, 40, 11, 54, 51, 43, 12, 45, 66, 23, 13, 96, 81, 47, 36, 46, 93, 2, 69, 33, 3, 20, 6, 35, 18, 74, 68, 53, 37, 29, 61, 90, 57, 14, 42, 24, 76, 10, 38, 97, 39, 25, 50, 49, 70, 55, 82, 15, 17, 59, 94], [11, 17, 21, 48, 63, 0, 73, 14, 10, 88, 2, 28, 6, 9, 19, 69, 81, 13, 35, 95, 60, 56, 30, 47, 91, 92, 86, 29, 76, 50, 16, 25, 82, 93, 8, 5, 58, 94, 98, 20, 74, 77, 4, 52, 97, 79, 27, 42, 36, 12, 26, 72, 71, 45, 24, 87, 1, 39, 22, 89, 53, 31, 61, 37, 33, 18, 59, 43, 80, 15, 54, 38, 78, 90, 32, 83, 23, 44, 55, 66, 64, 3, 67, 49, 84, 62, 51, 41, 46, 85, 96, 34, 99, 75, 7, 40, 70, 68, 65, 57], [43, 38, 48, 47, 74, 88, 64, 54, 59, 35, 29, 18, 53, 91, 68, 66, 12, 79, 55, 41, 34, 36, 33, 94, 56, 87, 25, 6, 1, 8, 63, 96, 93, 44, 40, 65, 13, 77, 61, 85, 99, 80, 11, 51, 14, 46, 5, 39, 92, 27, 75, 52, 72, 83, 19, 62, 32, 24, 82, 95, 17, 37, 0, 15, 97, 4, 42, 86, 84, 3, 26, 81, 98, 9, 73, 22, 28, 78, 60, 76, 16, 23, 71, 89, 31, 57, 58, 20, 50, 10, 49, 45, 21, 67, 90, 30, 7, 2, 70, 69], [78, 35, 94, 59, 8, 23, 10, 76, 96, 80, 93, 9, 27, 95, 61, 84, 88, 91, 2, 33, 1, 85, 3, 36, 79, 18, 92, 5, 34, 81, 98, 40, 38, 68, 90, 70, 89, 65, 48, 6, 77, 30, 72, 62, 16, 83, 32, 54, 99, 82, 75, 41, 53, 17, 67, 43, 28, 21, 46, 7, 25, 64, 13, 14, 87, 58, 63, 31, 39, 47, 24, 71, 74, 55, 20, 11, 22, 45, 44, 51, 37, 15, 42, 56, 66, 29, 86, 49, 69, 57, 73, 50, 60, 0, 4, 19, 52, 97, 12, 26], [88, 86, 41, 1, 70, 10, 19, 2, 11, 44, 87, 26, 53, 59, 40, 18, 61, 13, 21, 56, 85, 75, 25, 5, 97, 7, 36, 67, 76, 79, 90, 65, 91, 0, 98, 89, 20, 55, 93, 58, 28, 63, 6, 83, 47, 71, 73, 49, 39, 31, 48, 69, 82, 50, 78, 62, 14, 74, 29, 54, 57, 80, 32, 94, 3, 66, 30, 35, 8, 46, 16, 51, 24, 17, 4, 84, 45, 60, 34, 15, 72, 77, 42, 12, 64, 9, 33, 95, 81, 96, 38, 43, 52, 99, 22, 92, 27, 23, 37, 68], [90, 93, 83, 21, 96, 71, 67, 28, 37, 75, 55, 58, 56, 39, 16, 46, 91, 41, 77, 92, 99, 65, 27, 5, 24, 1, 69, 7, 31, 35, 33, 64, 12, 14, 45, 11, 98, 0, 57, 72, 74, 13, 61, 40, 63, 89, 53, 85, 18, 76, 2, 70, 20, 60, 84, 52, 25, 43, 54, 17, 36, 19, 97, 73, 94, 62, 3, 23, 34, 88, 29, 80, 50, 22, 81, 87, 6, 30, 78, 48, 79, 44, 66, 8, 51, 86, 4, 15, 42, 10, 82, 32, 68, 26, 38, 47, 49, 9, 95, 59]]];\nconst ibtable2 = [[4, 7, 8, 0, 9, 1, 3, 6, 2, 5], [5, 0, 4, 1, 3, 8, 9, 7, 6, 2], [0, 2, 7, 8, 9, 1, 6, 3, 5, 4], [5, 9, 8, 4, 1, 7, 0, 2, 3, 6], [7, 3, 0, 1, 8, 9, 6, 5, 2, 4], [7, 2, 4, 0, 1, 9, 6, 3, 5, 8], [4, 1, 6, 3, 5, 8, 0, 7, 2, 9], [6, 8, 4, 1, 5, 3, 7, 2, 9, 0], [4, 1, 8, 3, 7, 2, 0, 6, 5, 9], [7, 4, 5, 6, 2, 3, 9, 1, 8, 0]];\n\ntype DecodedAccessCode = { revision: number; serial: number };\n\nfunction crc16(data: BigInteger, bits: number, initial: number = 0xffff, poly: number = 0x8408): number {\n let v = bigInt(initial);\n let bigPoly = bigInt(poly);\n\n for (let i = 0; i < bits; i++) {\n v = v.shiftRight(1).xor(bigPoly.and(v.xor(data).and(bigInt.one).equals(bigInt.one) ? bigPoly : bigInt.zero));\n data = data.shiftRight(1);\n }\n\n return v.toJSNumber();\n}\n\nexport function decodeAccessCode(code: string): DecodedAccessCode | null {\n code = code.replace(/\\D/i, '');\n\n if (code.length !== 20) {\n return null;\n }\n\n let body = _.range(3, 15, 2).map(i => parseInt(code[i]) * 10 + parseInt(code[i + 1]));\n let crc = _.range(15, 20).map(i => parseInt(code[i]));\n\n let boxes = [(crc[3] + crc[2]) % 10, crc[2], crc[3], crc[4], (crc[4] + crc[0]) % 10, crc[1]];\n\n for (const [n, i] of body.entries()) {\n body[n] = ibtable[4][boxes[n]][i];\n }\n\n const rv = body[0] >> 4;\n\n boxes = [(crc[1] + crc[0]) % 10, crc[1], crc[2], crc[3], crc[4], (crc[4] + crc[1]) % 10]\n for (const [n, i] of body.entries()) {\n if (n == 0) {\n body[n] = (body[n] & 0xf0) | ibtable2[boxes[n]][body[n] & 0x0f];\n } else {\n body[n] = ibtable[rv][boxes[n]][i];\n }\n }\n\n const serial = body.map(i => i.toString().padStart(2, '0')).join('')\n\n if (crc16(bigInt(code.substring(0, 3) + serial, 16), 15 * 4) !== parseInt(code.substring(15, 20))) {\n throw new Error(`CRC16 mismatch.`);\n }\n\n return { revision: rv, serial: parseInt(serial) };\n}\n\nexport function encodeAccessCode(company: number, serial: number, rev: number = 0): string {\n if (company < 0 || company > 999) {\n throw new Error(`Invalid company code`);\n }\n\n if (serial < 0 || serial > 99999999999) {\n throw new Error(`Invalid serial number`);\n }\n\n if (rev < 0 || rev > sbtable.length) {\n throw new Error(`Invalid revision`);\n }\n\n const bodyStr = `${rev}${serial.toString().padStart(11, '0')}`;\n const crc_i = crc16(bigInt(company.toString().padStart(3, '0') + bodyStr, 16), 15 * 4);\n\n const crc = _.range(5).map(i => Math.floor(crc_i / (10 ** (4 - i))) % 10);\n const body = _.range(0, 12, 2).map(i => parseInt(bodyStr[i]) * 10 + parseInt(bodyStr[i + 1]));\n\n let boxes = [(crc[1] + crc[0]) % 10, crc[1], crc[2], crc[3], crc[4], (crc[4] + crc[1]) % 10];\n for (const [n, i] of body.entries()) {\n if (n == 0) {\n body[n] = (body[n] & 0xf0) | sbtable2[boxes[n]][body[n] & 0x0f];\n } else {\n body[n] = sbtable[rev][boxes[n]][i];\n }\n }\n\n boxes = [(crc[3] + crc[2]) % 10, crc[2], crc[3], crc[4], (crc[4] + crc[0]) % 10, crc[1]]\n for (const [n, i] of body.entries()) {\n body[n] = sbtable[4][boxes[n]][i];\n }\n\n return `${company.toString().padStart(3, '0')}${body.map(i => i.toString().padStart(2, '0')).join('')}${crc_i.toString().padStart(5, '0')}`;\n}\n","import {encodeAccessCode} from \"./card-encoding\";\n\nexport const CARD_ID_REGEX = /^(?:[0-9]{4} ?){5}$/i;\nexport const AMNET_COMPANY_ID = 597;\n\nexport function generateCardNumber(): string {\n // serial between 0 and 99999999999\n const serial = Math.floor(Math.random() * 100000000000);\n return encodeAccessCode(AMNET_COMPANY_ID, serial, 0);\n}\n\n/**\n * Validates the provided card id by known parameters\n * @param id The card id to validate\n */\nexport function validateCardId(id: string): boolean {\n return id[0] !== '3' && CARD_ID_REGEX.test(id);\n}\n","/**\n * Get the game name from the game identifier (4-letter code)\n * @param id\n */\nexport function getGameName(id: string): string | null {\n switch (id?.toUpperCase()) {\n case \"SDGT\":\n return \"Initial D The Arcade\";\n case \"SDHD\":\n case \"SDBT\":\n return \"CHUNITHM\";\n case \"SDDT\":\n return \"O.N.G.E.K.I.\";\n case \"SDEZ\":\n return \"maimai DX\";\n case \"SBZV\":\n return \"Project DIVA Future Tone\";\n \n default:\n return null;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACGO,IAAM,oBAAoB;AAK1B,IAAM,wBAAwB;AA0CrC,eAAsB,cAAc,QAAgC,SAA0B,QAAuC;AACjI,MAAI,OAAO,aAAa,yBAAyB,OAAO,aAAa,mBAAmB;AACpF,UAAM,IAAI,MAAM,gCAAgC;AAAA,EACpD;AAEA,MAAI;AACA,UAAM,WAAW,MAAM,MAAM,GAAG,IAAI,IAAI,OAAO,aAAa,EAAE,MAAM,iBAAiB;AAAA,MACjF,QAAQ;AAAA,MACR,SAAS;AAAA,QACL,gBAAgB;AAAA,MACpB;AAAA,MACA,MAAM,KAAK,UAAU,OAAO;AAAA,MAC5B;AAAA,IACJ,CAAC;AAED,WAAO,SAAS;AAAA,EACpB,SAAQ;AACJ,WAAO;AAAA,EACX;AACJ;AAOA,eAAsB,gBAAgB,eAAuB,OAAkD;AAC3G,MAAI;AACA,UAAM,WAAW,MAAM,MAAM,GAAG,IAAI,IAAI,aAAa,EAAE,MAAM,eAAe;AAAA,MACxE,QAAQ;AAAA,IACZ,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AACd,aAAO;AAAA,IACX;AAEA,WAAO,iCACA,MAAM,SAAS,KAAK,IADpB;AAAA,MAEH;AAAA,IACJ;AAAA,EACJ,SAAQ;AACJ,WAAO;AAAA,EACX;AACJ;;;AC7FA,oBAAc;AACd,yBAAiC;AAEjC,IAAM,UAAU,CAAC,CAAC,CAAC,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,EAAE,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC,CAAC;AACzqmB,IAAM,WAAW,CAAC,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC;AAEhV,IAAM,UAAU,CAAC,CAAC,CAAC,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,EAAE,GAAG,CAAC,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,EAAE,CAAC,CAAC;AACzqmB,IAAM,WAAW,CAAC,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC;AAIhV,SAAS,MAAM,MAAkB,MAAc,UAAkB,OAAQ,OAAe,OAAgB;AACpG,MAAI,QAAI,mBAAAA,SAAO,OAAO;AACtB,MAAI,cAAU,mBAAAA,SAAO,IAAI;AAEzB,WAAS,IAAI,GAAG,IAAI,MAAM,KAAK;AAC3B,QAAI,EAAE,WAAW,CAAC,EAAE,IAAI,QAAQ,IAAI,EAAE,IAAI,IAAI,EAAE,IAAI,mBAAAA,QAAO,GAAG,EAAE,OAAO,mBAAAA,QAAO,GAAG,IAAI,UAAU,mBAAAA,QAAO,IAAI,CAAC;AAC3G,WAAO,KAAK,WAAW,CAAC;AAAA,EAC5B;AAEA,SAAO,EAAE,WAAW;AACxB;AAEO,SAAS,iBAAiB,MAAwC;AACrE,SAAO,KAAK,QAAQ,OAAO,EAAE;AAE7B,MAAI,KAAK,WAAW,IAAI;AACpB,WAAO;AAAA,EACX;AAEA,MAAI,OAAO,cAAAC,QAAE,MAAM,GAAG,IAAI,CAAC,EAAE,IAAI,OAAK,SAAS,KAAK,CAAC,CAAC,IAAI,KAAK,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC;AACpF,MAAI,MAAM,cAAAA,QAAE,MAAM,IAAI,EAAE,EAAE,IAAI,OAAK,SAAS,KAAK,CAAC,CAAC,CAAC;AAEpD,MAAI,QAAQ,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC;AAE3F,aAAW,CAAC,GAAG,CAAC,KAAK,KAAK,QAAQ,GAAG;AACjC,SAAK,CAAC,IAAI,QAAQ,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC;AAAA,EACpC;AAEA,QAAM,KAAK,KAAK,CAAC,KAAK;AAEtB,UAAQ,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE;AACvF,aAAW,CAAC,GAAG,CAAC,KAAK,KAAK,QAAQ,GAAG;AACjC,QAAI,KAAK,GAAG;AACR,WAAK,CAAC,IAAK,KAAK,CAAC,IAAI,MAAQ,SAAS,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,EAAI;AAAA,IAClE,OAAO;AACH,WAAK,CAAC,IAAI,QAAQ,EAAE,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC;AAAA,IACrC;AAAA,EACJ;AAEA,QAAM,SAAS,KAAK,IAAI,OAAK,EAAE,SAAS,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE;AAEnE,MAAI,UAAM,mBAAAD,SAAO,KAAK,UAAU,GAAG,CAAC,IAAI,QAAQ,EAAE,GAAG,KAAK,CAAC,MAAM,SAAS,KAAK,UAAU,IAAI,EAAE,CAAC,GAAG;AAC/F,UAAM,IAAI,MAAM,iBAAiB;AAAA,EACrC;AAEA,SAAO,EAAE,UAAU,IAAI,QAAQ,SAAS,MAAM,EAAE;AACpD;AAEO,SAAS,iBAAiB,SAAiB,QAAgB,MAAc,GAAW;AACvF,MAAI,UAAU,KAAK,UAAU,KAAK;AAC9B,UAAM,IAAI,MAAM,sBAAsB;AAAA,EAC1C;AAEA,MAAI,SAAS,KAAK,SAAS,aAAa;AACpC,UAAM,IAAI,MAAM,uBAAuB;AAAA,EAC3C;AAEA,MAAI,MAAM,KAAK,MAAM,QAAQ,QAAQ;AACjC,UAAM,IAAI,MAAM,kBAAkB;AAAA,EACtC;AAEA,QAAM,UAAU,GAAG,GAAG,GAAG,OAAO,SAAS,EAAE,SAAS,IAAI,GAAG,CAAC;AAC5D,QAAM,QAAQ,UAAM,mBAAAA,SAAO,QAAQ,SAAS,EAAE,SAAS,GAAG,GAAG,IAAI,SAAS,EAAE,GAAG,KAAK,CAAC;AAErF,QAAM,MAAM,cAAAC,QAAE,MAAM,CAAC,EAAE,IAAI,OAAK,KAAK,MAAM,QAAS,OAAO,IAAI,EAAG,IAAI,EAAE;AACxE,QAAM,OAAO,cAAAA,QAAE,MAAM,GAAG,IAAI,CAAC,EAAE,IAAI,OAAK,SAAS,QAAQ,CAAC,CAAC,IAAI,KAAK,SAAS,QAAQ,IAAI,CAAC,CAAC,CAAC;AAE5F,MAAI,QAAQ,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE;AAC3F,aAAW,CAAC,GAAG,CAAC,KAAK,KAAK,QAAQ,GAAG;AACjC,QAAI,KAAK,GAAG;AACR,WAAK,CAAC,IAAK,KAAK,CAAC,IAAI,MAAQ,SAAS,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,EAAI;AAAA,IAClE,OAAO;AACH,WAAK,CAAC,IAAI,QAAQ,GAAG,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC;AAAA,IACtC;AAAA,EACJ;AAEA,UAAQ,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC;AACvF,aAAW,CAAC,GAAG,CAAC,KAAK,KAAK,QAAQ,GAAG;AACjC,SAAK,CAAC,IAAI,QAAQ,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC;AAAA,EACpC;AAEA,SAAO,GAAG,QAAQ,SAAS,EAAE,SAAS,GAAG,GAAG,CAAC,GAAG,KAAK,IAAI,OAAK,EAAE,SAAS,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,MAAM,SAAS,EAAE,SAAS,GAAG,GAAG,CAAC;AAC7I;;;AC3FO,IAAM,gBAAgB;AACtB,IAAM,mBAAmB;AAEzB,SAAS,qBAA6B;AAEzC,QAAM,SAAS,KAAK,MAAM,KAAK,OAAO,IAAI,IAAY;AACtD,SAAO,iBAAiB,kBAAkB,QAAQ,CAAC;AACvD;AAMO,SAAS,eAAe,IAAqB;AAChD,SAAO,GAAG,CAAC,MAAM,OAAO,cAAc,KAAK,EAAE;AACjD;;;ACbO,SAAS,YAAY,IAA2B;AACnD,UAAQ,yBAAI,eAAe;AAAA,IACvB,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AAAA,IACL,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IAEX;AACI,aAAO;AAAA,EACf;AACJ;","names":["bigInt","_"]} \ No newline at end of file diff --git a/amnet-shared/dist/index.mjs b/amnet-shared/dist/index.mjs index a88051e..fdefb01 100644 --- a/amnet-shared/dist/index.mjs +++ b/amnet-shared/dist/index.mjs @@ -17,83 +17,121 @@ var __spreadValues = (a, b) => { return a; }; var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); -var __async = (__this, __arguments, generator) => { - return new Promise((resolve, reject) => { - var fulfilled = (value) => { - try { - step(generator.next(value)); - } catch (e) { - reject(e); - } - }; - var rejected = (value) => { - try { - step(generator.throw(value)); - } catch (e) { - reject(e); - } - }; - var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); - step((generator = generator.apply(__this, __arguments)).next()); - }); -}; // src/api.ts var AMNET_API_VERSION = 1; var MIN_AMNET_API_VERSION = 1; -function performCardIn(server, request, signal) { - return __async(this, null, function* () { - if (server.apiVersion < MIN_AMNET_API_VERSION || server.apiVersion > AMNET_API_VERSION) { - throw new Error("Unsupported server API version"); - } - try { - const response = yield fetch(`${new URL(server.serverAddress).origin}/amnet/signin`, { - method: "POST", - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify(request), - signal - }); - return response.ok; - } catch (e) { - return false; - } - }); +async function performCardIn(server, request, signal) { + if (server.apiVersion < MIN_AMNET_API_VERSION || server.apiVersion > AMNET_API_VERSION) { + throw new Error("Unsupported server API version"); + } + try { + const response = await fetch(`${new URL(server.serverAddress).origin}/amnet/signin`, { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify(request), + signal + }); + return response.ok; + } catch (e) { + return false; + } } -function fetchServerInfo(serverAddress, abort) { - return __async(this, null, function* () { - try { - const response = yield fetch(`${new URL(serverAddress).origin}/amnet/info`, { - signal: abort - }); - if (!response.ok) { - return null; - } - return __spreadProps(__spreadValues({}, yield response.json()), { - serverAddress - }); - } catch (e) { +async function fetchServerInfo(serverAddress, abort) { + try { + const response = await fetch(`${new URL(serverAddress).origin}/amnet/info`, { + signal: abort + }); + if (!response.ok) { return null; } - }); + return __spreadProps(__spreadValues({}, await response.json()), { + serverAddress + }); + } catch (e) { + return null; + } +} + +// src/card-encoding.ts +import _ from "lodash"; +import bigInt from "big-integer"; +var sbtable = [[[45, 50, 94, 2, 66, 55, 56, 49, 99, 36, 60, 0, 73, 30, 39, 37, 80, 74, 85, 7, 12, 33, 93, 83, 95, 91, 98, 92, 23, 24, 79, 21, 31, 35, 71, 70, 4, 43, 1, 5, 88, 90, 69, 58, 76, 20, 84, 62, 77, 44, 61, 11, 16, 53, 82, 34, 13, 47, 48, 87, 89, 15, 64, 3, 54, 28, 57, 67, 9, 75, 32, 65, 96, 17, 18, 29, 10, 14, 42, 6, 46, 26, 40, 59, 63, 78, 68, 8, 86, 51, 25, 72, 38, 97, 19, 81, 27, 41, 52, 22], [26, 32, 34, 79, 42, 45, 4, 75, 22, 73, 64, 98, 49, 19, 5, 24, 6, 55, 63, 25, 40, 77, 54, 12, 9, 44, 83, 71, 0, 90, 97, 96, 81, 80, 3, 31, 56, 92, 17, 86, 72, 28, 36, 78, 62, 30, 7, 46, 87, 20, 82, 50, 35, 61, 41, 94, 68, 11, 1, 10, 74, 67, 85, 43, 57, 15, 95, 84, 65, 53, 69, 52, 76, 88, 2, 14, 37, 58, 93, 16, 59, 51, 18, 99, 39, 47, 89, 8, 13, 91, 38, 33, 70, 48, 23, 29, 27, 66, 60, 21], [28, 53, 30, 85, 0, 95, 70, 47, 40, 51, 94, 68, 99, 15, 5, 57, 1, 16, 80, 93, 91, 52, 2, 32, 48, 77, 41, 75, 22, 39, 50, 81, 79, 73, 97, 76, 60, 4, 59, 25, 78, 38, 46, 58, 90, 84, 56, 6, 83, 62, 42, 10, 64, 31, 36, 61, 65, 63, 18, 8, 9, 96, 55, 26, 89, 67, 3, 98, 35, 21, 13, 12, 88, 71, 37, 29, 87, 49, 24, 86, 92, 20, 44, 74, 69, 17, 82, 54, 27, 7, 23, 66, 11, 19, 34, 45, 14, 72, 43, 33], [32, 44, 86, 79, 38, 61, 25, 8, 5, 85, 78, 48, 33, 87, 84, 35, 64, 41, 60, 21, 96, 75, 6, 69, 39, 94, 3, 92, 37, 77, 15, 13, 29, 59, 76, 91, 65, 56, 73, 83, 27, 62, 89, 50, 18, 98, 81, 17, 71, 28, 82, 11, 30, 43, 34, 20, 70, 22, 46, 63, 52, 68, 90, 53, 36, 10, 74, 58, 40, 88, 9, 2, 42, 26, 24, 4, 95, 57, 31, 66, 0, 14, 49, 16, 99, 19, 51, 23, 47, 12, 93, 1, 7, 80, 45, 97, 55, 67, 72, 54], [58, 35, 42, 38, 63, 36, 68, 30, 81, 4, 87, 80, 32, 69, 37, 67, 1, 86, 61, 94, 27, 11, 84, 26, 59, 73, 53, 66, 72, 71, 39, 33, 99, 8, 43, 88, 17, 22, 89, 5, 46, 93, 98, 48, 55, 83, 34, 3, 0, 16, 12, 20, 78, 9, 2, 76, 54, 51, 25, 24, 62, 57, 92, 10, 50, 28, 79, 23, 52, 95, 44, 60, 74, 90, 19, 77, 21, 31, 96, 49, 64, 6, 29, 85, 18, 13, 82, 47, 56, 45, 15, 7, 97, 75, 40, 41, 65, 14, 70, 91], [81, 82, 66, 72, 83, 4, 20, 94, 53, 48, 77, 41, 67, 64, 9, 24, 36, 55, 14, 1, 78, 84, 49, 0, 44, 58, 70, 25, 28, 73, 8, 68, 88, 54, 86, 87, 23, 52, 27, 5, 97, 37, 62, 50, 3, 89, 92, 47, 30, 10, 29, 80, 22, 26, 11, 13, 69, 60, 38, 90, 85, 57, 12, 75, 46, 39, 74, 33, 42, 93, 98, 91, 59, 99, 18, 6, 17, 16, 32, 40, 63, 7, 76, 15, 43, 71, 51, 35, 2, 65, 34, 96, 19, 56, 79, 21, 95, 45, 61, 31], [25, 88, 7, 57, 98, 63, 86, 13, 69, 84, 56, 20, 68, 90, 35, 6, 43, 93, 95, 3, 75, 4, 9, 28, 31, 66, 21, 44, 30, 76, 11, 22, 49, 2, 77, 40, 85, 19, 82, 16, 97, 92, 39, 96, 18, 32, 42, 94, 70, 71, 54, 79, 67, 78, 37, 12, 99, 53, 61, 41, 87, 17, 1, 14, 52, 10, 47, 58, 27, 46, 36, 55, 26, 15, 33, 50, 72, 51, 89, 23, 74, 59, 64, 8, 83, 65, 80, 29, 38, 0, 60, 24, 45, 81, 48, 91, 73, 5, 34, 62], [73, 85, 37, 11, 80, 46, 16, 47, 88, 65, 94, 64, 12, 38, 98, 70, 95, 48, 92, 18, 56, 75, 58, 7, 81, 15, 74, 13, 5, 44, 27, 32, 76, 90, 33, 55, 26, 96, 77, 24, 0, 89, 8, 4, 28, 39, 71, 3, 10, 41, 91, 53, 31, 68, 36, 17, 69, 60, 34, 54, 93, 82, 21, 51, 87, 59, 61, 6, 1, 23, 30, 66, 43, 25, 35, 42, 49, 78, 62, 14, 19, 20, 29, 22, 52, 83, 63, 45, 2, 99, 50, 79, 72, 40, 84, 97, 86, 57, 67, 9], [48, 94, 17, 55, 59, 24, 72, 75, 38, 85, 34, 98, 95, 36, 49, 63, 57, 54, 29, 6, 52, 47, 73, 1, 82, 9, 66, 83, 31, 61, 78, 60, 97, 14, 87, 15, 90, 70, 53, 10, 27, 8, 3, 64, 51, 50, 68, 26, 22, 19, 42, 96, 67, 7, 25, 39, 92, 30, 58, 93, 21, 74, 41, 18, 65, 44, 88, 32, 71, 69, 12, 77, 13, 11, 99, 79, 37, 89, 81, 35, 86, 56, 45, 76, 46, 80, 62, 43, 16, 84, 40, 5, 33, 91, 4, 20, 0, 23, 28, 2], [61, 45, 79, 13, 59, 52, 6, 77, 3, 31, 70, 80, 26, 33, 36, 4, 27, 32, 24, 74, 29, 94, 16, 12, 76, 7, 88, 57, 78, 42, 97, 96, 43, 83, 92, 21, 51, 47, 49, 58, 90, 41, 93, 98, 35, 63, 86, 37, 5, 30, 28, 72, 25, 22, 60, 19, 39, 34, 68, 50, 8, 64, 46, 99, 84, 87, 69, 56, 53, 55, 44, 54, 89, 65, 85, 0, 71, 15, 17, 66, 81, 82, 91, 95, 62, 48, 38, 75, 40, 14, 9, 10, 73, 67, 18, 11, 23, 2, 20, 1]], [[95, 90, 61, 38, 18, 7, 83, 39, 79, 14, 48, 21, 81, 44, 78, 50, 56, 91, 12, 86, 99, 98, 43, 63, 73, 87, 42, 72, 15, 68, 97, 23, 88, 69, 2, 51, 19, 58, 59, 22, 20, 29, 82, 11, 75, 70, 9, 71, 27, 30, 47, 89, 92, 60, 46, 76, 93, 54, 1, 96, 94, 57, 40, 62, 41, 3, 28, 64, 37, 74, 67, 16, 65, 53, 5, 36, 24, 45, 32, 33, 10, 85, 26, 4, 84, 77, 35, 13, 6, 8, 17, 80, 25, 55, 52, 66, 49, 34, 0, 31], [18, 14, 70, 43, 74, 45, 94, 36, 40, 6, 31, 46, 15, 47, 13, 19, 72, 61, 99, 21, 17, 92, 88, 78, 73, 90, 0, 96, 20, 28, 25, 51, 50, 77, 29, 16, 54, 12, 58, 38, 71, 23, 42, 83, 65, 60, 10, 33, 22, 76, 95, 32, 57, 5, 59, 67, 68, 3, 91, 85, 26, 62, 41, 1, 53, 82, 34, 24, 87, 27, 89, 75, 56, 30, 81, 7, 8, 4, 64, 66, 9, 63, 35, 52, 80, 93, 69, 84, 55, 86, 48, 98, 79, 97, 49, 44, 39, 11, 37, 2], [76, 16, 73, 29, 34, 14, 36, 90, 58, 70, 46, 2, 91, 59, 63, 32, 84, 40, 78, 38, 19, 37, 0, 31, 8, 44, 94, 39, 10, 66, 42, 1, 21, 88, 82, 97, 33, 4, 99, 74, 89, 62, 25, 87, 77, 43, 50, 95, 64, 23, 47, 93, 65, 27, 12, 71, 9, 72, 60, 18, 7, 22, 79, 24, 96, 45, 49, 98, 57, 11, 5, 86, 3, 80, 26, 51, 17, 85, 54, 67, 48, 35, 53, 41, 28, 13, 20, 92, 83, 69, 6, 68, 75, 52, 61, 30, 81, 56, 15, 55], [0, 62, 25, 61, 56, 87, 54, 27, 75, 49, 18, 69, 17, 71, 40, 6, 90, 80, 23, 26, 68, 24, 10, 21, 47, 88, 36, 55, 15, 22, 48, 12, 83, 99, 46, 67, 59, 29, 58, 73, 78, 97, 19, 33, 51, 98, 65, 91, 16, 4, 63, 41, 3, 70, 30, 8, 89, 34, 72, 60, 9, 95, 50, 2, 66, 53, 44, 85, 28, 5, 92, 43, 64, 96, 74, 77, 45, 52, 14, 42, 1, 32, 20, 76, 86, 82, 94, 7, 39, 13, 31, 11, 84, 93, 35, 37, 38, 57, 79, 81], [78, 8, 59, 25, 69, 15, 63, 3, 57, 5, 50, 96, 41, 66, 80, 81, 7, 45, 60, 74, 79, 32, 27, 58, 56, 82, 11, 47, 17, 85, 53, 40, 95, 84, 76, 92, 18, 71, 64, 51, 67, 91, 22, 34, 65, 31, 73, 93, 68, 75, 77, 37, 43, 12, 20, 99, 2, 1, 19, 35, 44, 55, 90, 9, 0, 6, 33, 89, 24, 14, 97, 10, 48, 61, 23, 54, 98, 86, 88, 94, 46, 70, 28, 36, 16, 30, 4, 39, 26, 38, 87, 52, 83, 49, 62, 42, 72, 29, 13, 21], [20, 88, 35, 5, 85, 98, 11, 78, 62, 83, 79, 80, 40, 14, 91, 8, 7, 69, 71, 61, 49, 38, 64, 82, 0, 47, 84, 95, 2, 50, 77, 41, 97, 36, 33, 81, 46, 21, 73, 48, 31, 92, 54, 89, 87, 60, 90, 10, 76, 45, 99, 52, 3, 44, 39, 43, 18, 16, 63, 12, 28, 70, 86, 6, 4, 24, 59, 30, 96, 65, 22, 56, 74, 23, 32, 75, 53, 93, 15, 67, 66, 17, 29, 25, 42, 37, 57, 9, 72, 68, 26, 34, 27, 51, 19, 58, 55, 94, 13, 1], [72, 53, 79, 61, 30, 33, 25, 75, 8, 55, 14, 6, 22, 93, 18, 77, 4, 74, 44, 36, 82, 94, 2, 5, 62, 54, 28, 3, 63, 97, 65, 7, 84, 73, 39, 92, 67, 91, 24, 45, 15, 81, 19, 78, 80, 64, 34, 43, 32, 87, 52, 95, 89, 37, 10, 23, 20, 68, 42, 47, 59, 58, 49, 71, 40, 85, 13, 31, 12, 57, 96, 90, 56, 41, 48, 9, 99, 35, 98, 26, 1, 46, 11, 69, 27, 83, 60, 50, 21, 70, 0, 88, 29, 86, 76, 16, 66, 38, 51, 17], [3, 68, 86, 94, 81, 90, 85, 32, 65, 52, 50, 42, 80, 78, 26, 93, 57, 58, 69, 82, 92, 27, 11, 67, 95, 41, 21, 88, 35, 17, 19, 15, 22, 62, 40, 76, 63, 59, 37, 55, 75, 9, 24, 89, 56, 53, 23, 60, 25, 6, 1, 71, 16, 7, 10, 44, 8, 12, 87, 64, 54, 72, 20, 33, 70, 79, 14, 97, 4, 99, 45, 39, 2, 77, 98, 74, 49, 30, 13, 61, 66, 73, 47, 43, 96, 83, 18, 91, 28, 48, 38, 34, 31, 36, 29, 0, 84, 46, 5, 51], [71, 84, 25, 70, 77, 56, 61, 47, 72, 27, 54, 89, 9, 46, 95, 59, 42, 98, 63, 69, 48, 16, 97, 14, 66, 83, 87, 68, 17, 2, 15, 79, 31, 1, 57, 12, 33, 36, 0, 40, 21, 50, 76, 41, 10, 13, 44, 5, 53, 4, 38, 35, 55, 96, 81, 91, 90, 64, 34, 51, 6, 28, 32, 39, 75, 18, 93, 24, 78, 7, 52, 43, 49, 37, 74, 8, 58, 65, 92, 67, 85, 20, 94, 88, 80, 86, 19, 60, 3, 11, 30, 45, 29, 22, 26, 73, 99, 23, 62, 82], [67, 91, 46, 33, 76, 3, 96, 23, 9, 68, 49, 63, 1, 92, 58, 62, 69, 77, 70, 75, 71, 94, 51, 55, 50, 52, 0, 88, 18, 16, 73, 6, 12, 87, 93, 79, 84, 57, 48, 41, 20, 47, 15, 42, 54, 81, 7, 30, 34, 56, 29, 8, 98, 5, 45, 64, 83, 74, 22, 82, 80, 95, 2, 72, 44, 31, 86, 78, 13, 10, 28, 89, 60, 35, 61, 19, 14, 85, 4, 43, 11, 40, 26, 99, 39, 32, 90, 17, 24, 37, 36, 21, 25, 53, 97, 59, 27, 38, 65, 66]], [[40, 77, 66, 65, 97, 96, 58, 64, 69, 30, 0, 3, 17, 33, 51, 23, 59, 10, 7, 20, 29, 49, 71, 13, 46, 4, 79, 92, 47, 18, 72, 57, 73, 80, 56, 28, 78, 67, 9, 87, 43, 31, 22, 62, 75, 99, 19, 63, 35, 93, 12, 98, 25, 26, 37, 89, 86, 15, 55, 50, 91, 38, 42, 81, 2, 94, 70, 68, 45, 88, 76, 74, 11, 24, 41, 60, 85, 21, 44, 32, 82, 61, 52, 83, 53, 36, 48, 1, 95, 27, 14, 84, 54, 6, 39, 34, 90, 5, 16, 8], [44, 69, 53, 28, 18, 11, 23, 65, 96, 25, 93, 42, 72, 58, 95, 56, 32, 21, 78, 77, 51, 34, 47, 7, 66, 13, 88, 67, 92, 98, 10, 50, 90, 45, 49, 9, 99, 94, 22, 24, 5, 85, 33, 3, 73, 35, 52, 63, 2, 76, 87, 43, 81, 83, 55, 61, 70, 75, 16, 14, 8, 71, 57, 40, 86, 39, 80, 79, 26, 48, 59, 4, 82, 0, 36, 12, 54, 38, 64, 17, 91, 27, 62, 84, 37, 74, 68, 1, 6, 19, 29, 46, 60, 30, 89, 15, 20, 97, 41, 31], [53, 22, 34, 16, 72, 71, 14, 94, 12, 59, 39, 6, 38, 68, 21, 93, 32, 46, 69, 55, 10, 8, 75, 60, 9, 13, 99, 90, 63, 49, 28, 92, 41, 7, 54, 83, 27, 91, 47, 43, 31, 74, 42, 40, 18, 81, 76, 84, 78, 52, 1, 26, 51, 2, 58, 11, 65, 67, 15, 23, 0, 79, 96, 4, 29, 62, 89, 66, 50, 98, 56, 61, 30, 88, 35, 25, 77, 70, 3, 44, 95, 80, 19, 64, 57, 37, 82, 85, 73, 48, 5, 86, 24, 36, 33, 97, 20, 45, 17, 87], [95, 36, 70, 82, 23, 55, 13, 51, 83, 38, 40, 18, 84, 74, 56, 85, 58, 14, 19, 69, 34, 59, 97, 89, 52, 77, 9, 91, 68, 16, 27, 88, 6, 73, 43, 5, 10, 66, 4, 61, 44, 46, 72, 50, 96, 21, 33, 15, 32, 0, 63, 92, 24, 39, 29, 49, 30, 64, 12, 26, 67, 53, 37, 2, 47, 65, 3, 20, 75, 78, 99, 87, 98, 1, 81, 90, 62, 54, 22, 35, 45, 25, 86, 48, 17, 60, 94, 8, 42, 31, 76, 28, 11, 57, 93, 41, 7, 71, 80, 79], [60, 24, 69, 92, 15, 36, 11, 98, 93, 27, 51, 35, 89, 74, 87, 4, 81, 94, 23, 84, 56, 3, 42, 18, 75, 73, 46, 86, 55, 99, 0, 88, 1, 26, 76, 70, 65, 67, 20, 13, 7, 45, 77, 17, 9, 78, 54, 48, 32, 40, 47, 31, 2, 6, 90, 10, 8, 50, 52, 38, 91, 14, 53, 12, 85, 44, 21, 80, 68, 33, 30, 57, 61, 63, 79, 41, 29, 39, 58, 5, 62, 96, 28, 71, 82, 34, 25, 43, 22, 95, 72, 37, 64, 16, 97, 59, 66, 83, 49, 19], [80, 0, 40, 36, 68, 18, 67, 14, 20, 21, 10, 17, 43, 3, 98, 81, 94, 55, 1, 39, 72, 51, 41, 66, 58, 2, 61, 78, 15, 99, 57, 52, 25, 87, 12, 65, 74, 64, 34, 37, 44, 30, 88, 79, 33, 27, 56, 35, 73, 42, 29, 54, 24, 5, 53, 16, 31, 4, 19, 84, 59, 50, 26, 63, 48, 28, 60, 45, 92, 82, 49, 77, 97, 76, 22, 86, 47, 62, 83, 70, 93, 38, 11, 13, 89, 75, 85, 69, 9, 46, 96, 90, 6, 91, 8, 7, 32, 71, 95, 23], [32, 14, 35, 84, 89, 11, 52, 34, 79, 65, 2, 88, 49, 29, 31, 94, 1, 74, 97, 51, 99, 37, 10, 47, 57, 81, 73, 76, 63, 58, 0, 38, 39, 27, 33, 8, 20, 21, 46, 66, 92, 36, 6, 30, 53, 17, 48, 64, 55, 28, 85, 50, 54, 12, 80, 41, 77, 69, 93, 9, 91, 13, 42, 75, 68, 86, 19, 3, 16, 78, 87, 45, 43, 56, 59, 70, 40, 24, 83, 98, 62, 5, 96, 4, 67, 60, 26, 23, 72, 95, 44, 82, 90, 25, 7, 18, 15, 22, 71, 61], [78, 53, 89, 93, 7, 84, 81, 95, 64, 50, 58, 35, 59, 63, 20, 76, 80, 8, 23, 28, 79, 52, 55, 48, 66, 17, 60, 65, 25, 29, 30, 9, 73, 39, 69, 3, 98, 22, 15, 87, 4, 37, 43, 5, 11, 83, 91, 40, 51, 33, 12, 92, 6, 67, 41, 94, 45, 88, 56, 36, 0, 68, 19, 54, 70, 26, 72, 77, 32, 34, 97, 71, 99, 75, 61, 31, 86, 82, 46, 2, 44, 96, 49, 74, 10, 62, 24, 16, 1, 38, 27, 14, 18, 85, 13, 57, 90, 21, 42, 47], [26, 30, 60, 78, 75, 48, 73, 52, 51, 57, 63, 90, 27, 36, 83, 81, 41, 44, 82, 37, 10, 64, 91, 4, 59, 95, 21, 24, 77, 56, 43, 38, 62, 55, 92, 0, 94, 20, 86, 66, 35, 80, 53, 25, 17, 18, 49, 76, 23, 89, 13, 15, 68, 8, 22, 46, 1, 42, 3, 34, 98, 88, 69, 31, 50, 65, 84, 32, 47, 61, 9, 19, 99, 85, 11, 79, 70, 2, 74, 14, 87, 7, 72, 5, 12, 58, 67, 39, 16, 71, 54, 6, 45, 93, 96, 28, 97, 29, 33, 40], [77, 66, 20, 30, 42, 23, 0, 49, 7, 64, 54, 27, 96, 97, 39, 84, 2, 51, 71, 17, 4, 92, 94, 95, 67, 69, 73, 75, 86, 37, 74, 50, 11, 79, 29, 15, 56, 43, 22, 83, 81, 61, 14, 3, 36, 44, 58, 10, 62, 19, 72, 80, 65, 24, 18, 5, 40, 90, 33, 9, 47, 76, 8, 59, 35, 91, 99, 13, 70, 88, 55, 16, 31, 28, 85, 45, 60, 25, 87, 82, 93, 12, 34, 68, 63, 26, 46, 48, 21, 52, 1, 78, 98, 38, 6, 32, 89, 53, 41, 57]], [[77, 99, 47, 64, 26, 63, 50, 81, 23, 45, 49, 36, 43, 56, 39, 12, 86, 62, 2, 98, 60, 94, 75, 9, 20, 74, 48, 28, 4, 84, 61, 31, 54, 15, 40, 7, 87, 38, 80, 6, 37, 46, 1, 55, 88, 73, 95, 33, 92, 18, 25, 66, 57, 27, 42, 35, 91, 51, 13, 90, 41, 83, 68, 53, 19, 3, 72, 89, 65, 70, 71, 8, 97, 59, 96, 52, 79, 22, 14, 69, 32, 93, 58, 29, 21, 76, 34, 5, 17, 67, 11, 78, 16, 0, 44, 30, 10, 24, 85, 82], [58, 62, 7, 39, 34, 69, 67, 73, 37, 59, 71, 66, 46, 57, 44, 51, 26, 85, 11, 28, 54, 47, 82, 21, 83, 76, 94, 16, 90, 95, 75, 55, 6, 22, 72, 74, 50, 40, 10, 19, 8, 53, 33, 98, 45, 87, 91, 13, 35, 70, 14, 38, 84, 99, 56, 30, 12, 89, 25, 97, 63, 65, 81, 92, 18, 29, 80, 61, 93, 43, 49, 52, 23, 31, 32, 4, 48, 3, 0, 78, 96, 64, 41, 88, 24, 5, 9, 15, 77, 86, 68, 1, 17, 79, 60, 36, 42, 2, 20, 27], [58, 62, 7, 39, 34, 69, 67, 73, 37, 59, 71, 66, 46, 57, 44, 51, 26, 85, 11, 28, 54, 47, 82, 21, 83, 76, 94, 16, 90, 95, 75, 55, 6, 22, 72, 74, 50, 40, 10, 19, 8, 53, 33, 98, 45, 87, 91, 13, 35, 70, 14, 38, 84, 99, 56, 30, 12, 89, 25, 97, 63, 65, 81, 92, 18, 29, 80, 61, 93, 43, 49, 52, 23, 31, 32, 4, 48, 3, 0, 78, 96, 64, 41, 88, 24, 5, 9, 15, 77, 86, 68, 1, 17, 79, 60, 36, 42, 2, 20, 27], [91, 43, 77, 69, 16, 96, 47, 34, 49, 15, 73, 84, 85, 22, 88, 95, 2, 80, 57, 33, 32, 41, 25, 14, 58, 9, 20, 65, 48, 97, 79, 89, 66, 63, 46, 83, 19, 92, 55, 18, 38, 0, 21, 50, 45, 62, 29, 26, 52, 28, 8, 6, 75, 86, 44, 81, 36, 90, 70, 30, 71, 39, 82, 87, 76, 1, 23, 5, 60, 98, 67, 53, 93, 72, 42, 74, 51, 4, 31, 59, 61, 7, 24, 56, 37, 54, 3, 11, 27, 40, 10, 64, 94, 35, 78, 68, 99, 17, 12, 13], [73, 70, 39, 9, 77, 61, 80, 10, 13, 92, 38, 4, 48, 57, 25, 72, 49, 51, 99, 97, 85, 36, 22, 15, 8, 34, 33, 76, 26, 45, 83, 59, 28, 96, 90, 47, 68, 88, 44, 6, 98, 12, 24, 46, 2, 54, 5, 58, 30, 63, 1, 18, 93, 67, 7, 50, 31, 16, 40, 56, 23, 86, 11, 52, 55, 75, 71, 27, 37, 87, 84, 65, 82, 29, 0, 14, 41, 35, 64, 21, 3, 95, 17, 60, 81, 74, 19, 91, 94, 32, 69, 53, 42, 89, 79, 43, 66, 20, 78, 62], [23, 78, 67, 41, 9, 77, 59, 72, 25, 82, 65, 89, 28, 37, 54, 70, 98, 33, 31, 44, 6, 17, 26, 73, 21, 36, 4, 2, 79, 95, 81, 50, 18, 34, 61, 0, 93, 24, 8, 66, 55, 62, 56, 30, 84, 58, 7, 88, 85, 64, 97, 39, 49, 3, 20, 42, 80, 96, 13, 63, 43, 60, 47, 90, 51, 14, 53, 1, 86, 57, 12, 19, 10, 45, 38, 68, 35, 87, 75, 5, 69, 27, 32, 91, 11, 99, 46, 29, 92, 22, 76, 15, 16, 48, 71, 74, 52, 94, 83, 40], [87, 89, 35, 98, 44, 21, 29, 83, 0, 77, 34, 60, 85, 64, 28, 58, 39, 36, 95, 22, 67, 30, 86, 57, 5, 81, 84, 26, 53, 62, 13, 90, 2, 88, 4, 46, 97, 73, 79, 43, 59, 72, 54, 32, 82, 10, 55, 38, 47, 12, 51, 1, 76, 48, 7, 14, 23, 20, 31, 18, 17, 15, 37, 42, 49, 68, 25, 93, 91, 40, 92, 50, 52, 70, 33, 80, 19, 96, 61, 75, 71, 99, 74, 9, 41, 66, 63, 8, 6, 24, 65, 56, 45, 69, 27, 16, 3, 78, 94, 11], [99, 6, 38, 11, 34, 28, 86, 57, 55, 78, 25, 95, 13, 63, 91, 14, 83, 1, 45, 17, 93, 76, 46, 79, 27, 85, 96, 58, 59, 44, 87, 65, 97, 22, 0, 51, 49, 52, 47, 67, 24, 88, 62, 50, 72, 3, 31, 82, 68, 42, 81, 23, 32, 98, 73, 60, 37, 54, 41, 80, 66, 18, 92, 4, 5, 94, 77, 39, 69, 16, 35, 7, 40, 30, 61, 26, 56, 64, 9, 71, 19, 21, 75, 84, 43, 53, 33, 29, 74, 36, 90, 2, 20, 15, 10, 48, 89, 70, 8, 12], [4, 28, 8, 72, 74, 31, 66, 0, 27, 54, 61, 23, 51, 73, 96, 32, 98, 41, 88, 95, 36, 22, 13, 38, 70, 84, 12, 7, 10, 60, 68, 33, 11, 35, 79, 42, 16, 62, 57, 24, 44, 94, 25, 48, 86, 97, 87, 49, 92, 71, 21, 1, 81, 5, 17, 85, 6, 75, 80, 34, 20, 30, 90, 69, 18, 89, 93, 56, 91, 40, 77, 52, 53, 14, 65, 45, 76, 2, 9, 29, 3, 26, 59, 55, 83, 46, 67, 47, 39, 50, 58, 63, 99, 15, 37, 43, 82, 64, 78, 19], [11, 94, 1, 93, 59, 91, 89, 51, 61, 81, 13, 29, 27, 58, 26, 3, 53, 69, 73, 21, 39, 12, 5, 18, 98, 74, 9, 96, 28, 65, 23, 4, 41, 72, 88, 34, 83, 54, 7, 15, 38, 84, 90, 52, 77, 48, 64, 60, 20, 79, 19, 66, 97, 30, 87, 45, 40, 36, 14, 86, 32, 33, 57, 16, 43, 42, 50, 10, 44, 76, 37, 6, 56, 31, 68, 99, 2, 17, 22, 35, 92, 85, 46, 75, 71, 67, 95, 78, 8, 63, 82, 25, 80, 24, 47, 49, 0, 70, 62, 55]], [[93, 99, 51, 49, 71, 64, 35, 42, 88, 17, 67, 92, 4, 12, 68, 13, 47, 7, 56, 14, 19, 53, 80, 85, 23, 5, 39, 6, 26, 89, 27, 54, 63, 73, 70, 33, 36, 3, 86, 58, 18, 2, 25, 21, 10, 34, 81, 94, 75, 98, 37, 46, 9, 95, 60, 74, 87, 78, 20, 24, 57, 0, 30, 83, 72, 61, 11, 32, 43, 82, 65, 69, 59, 62, 38, 76, 22, 16, 15, 45, 48, 79, 50, 90, 40, 1, 29, 77, 91, 31, 84, 96, 8, 52, 97, 41, 28, 44, 55, 66], [64, 71, 19, 27, 43, 95, 77, 16, 35, 81, 74, 29, 6, 86, 18, 37, 99, 46, 15, 87, 28, 63, 1, 65, 3, 79, 50, 12, 78, 84, 8, 88, 98, 69, 42, 0, 56, 32, 92, 83, 30, 59, 80, 9, 20, 89, 47, 23, 33, 34, 82, 24, 97, 60, 22, 90, 51, 76, 10, 94, 21, 91, 70, 49, 66, 14, 75, 2, 7, 13, 40, 93, 48, 36, 53, 72, 67, 62, 85, 17, 57, 58, 5, 25, 41, 31, 44, 73, 61, 26, 38, 54, 96, 39, 4, 68, 11, 52, 45, 55], [49, 88, 62, 73, 34, 15, 95, 68, 98, 5, 61, 35, 23, 45, 78, 64, 97, 70, 96, 65, 74, 16, 94, 37, 80, 56, 48, 28, 59, 1, 75, 4, 71, 47, 83, 93, 69, 21, 60, 38, 39, 19, 40, 82, 55, 7, 52, 18, 43, 54, 2, 77, 66, 90, 44, 32, 89, 3, 24, 6, 99, 17, 86, 29, 30, 22, 50, 81, 41, 42, 0, 9, 53, 58, 57, 25, 20, 12, 26, 51, 92, 87, 36, 31, 85, 63, 14, 8, 91, 11, 79, 27, 76, 84, 67, 33, 72, 13, 10, 46], [45, 15, 69, 88, 51, 28, 34, 80, 30, 42, 38, 90, 47, 17, 84, 49, 21, 55, 46, 96, 37, 97, 72, 85, 94, 29, 10, 66, 76, 82, 18, 86, 87, 25, 43, 92, 19, 89, 2, 93, 98, 40, 48, 53, 75, 99, 11, 62, 12, 60, 3, 24, 4, 6, 63, 81, 41, 20, 68, 9, 57, 52, 0, 44, 14, 16, 91, 27, 79, 73, 59, 61, 78, 7, 67, 1, 70, 83, 74, 64, 26, 95, 54, 65, 33, 39, 5, 50, 36, 35, 13, 23, 58, 71, 32, 77, 31, 22, 56, 8], [41, 43, 66, 69, 22, 23, 71, 2, 33, 38, 86, 51, 55, 59, 82, 96, 45, 97, 73, 34, 70, 21, 8, 58, 84, 90, 24, 25, 32, 78, 17, 27, 47, 68, 37, 72, 63, 77, 87, 89, 50, 16, 83, 54, 12, 56, 64, 62, 39, 92, 91, 53, 29, 76, 52, 94, 42, 81, 20, 98, 40, 79, 44, 10, 31, 0, 57, 19, 75, 67, 93, 6, 7, 11, 74, 26, 85, 5, 1, 49, 4, 61, 95, 3, 36, 9, 14, 13, 15, 48, 80, 46, 30, 65, 99, 18, 60, 88, 35, 28], [5, 56, 10, 81, 42, 35, 12, 94, 34, 13, 8, 0, 49, 17, 7, 69, 30, 1, 65, 14, 39, 2, 58, 76, 54, 31, 50, 46, 11, 27, 22, 61, 74, 64, 91, 18, 48, 63, 71, 57, 95, 87, 47, 67, 77, 53, 88, 23, 3, 83, 29, 86, 43, 60, 70, 78, 21, 99, 36, 66, 20, 62, 85, 4, 80, 98, 79, 82, 97, 15, 96, 52, 51, 6, 40, 93, 28, 41, 72, 45, 68, 16, 32, 75, 84, 89, 26, 55, 9, 59, 73, 24, 25, 33, 37, 19, 90, 44, 38, 92], [62, 28, 97, 69, 65, 46, 27, 96, 29, 73, 89, 42, 16, 36, 44, 63, 80, 60, 11, 54, 87, 92, 75, 81, 57, 26, 70, 49, 76, 10, 95, 84, 56, 22, 20, 9, 21, 61, 1, 47, 34, 19, 66, 0, 33, 91, 45, 3, 2, 90, 88, 43, 51, 12, 7, 18, 24, 85, 86, 8, 78, 38, 55, 30, 6, 35, 15, 93, 14, 99, 98, 82, 52, 74, 4, 50, 79, 37, 77, 17, 41, 71, 58, 53, 68, 39, 67, 25, 5, 83, 94, 13, 48, 32, 23, 59, 31, 64, 72, 40], [93, 20, 18, 22, 94, 27, 39, 59, 4, 11, 6, 75, 98, 62, 63, 81, 44, 53, 25, 95, 74, 57, 76, 5, 70, 60, 99, 12, 56, 85, 41, 67, 46, 19, 28, 1, 23, 80, 32, 68, 31, 51, 82, 55, 78, 77, 58, 69, 38, 87, 91, 79, 96, 52, 47, 73, 83, 89, 65, 3, 92, 14, 43, 66, 61, 37, 84, 54, 33, 88, 35, 71, 42, 90, 72, 50, 7, 40, 0, 24, 9, 29, 49, 45, 15, 21, 86, 64, 16, 36, 34, 17, 26, 10, 2, 13, 8, 97, 30, 48], [33, 3, 7, 64, 74, 23, 42, 25, 68, 85, 5, 8, 83, 17, 56, 79, 70, 73, 15, 6, 36, 18, 94, 97, 72, 22, 11, 96, 40, 58, 66, 49, 62, 86, 78, 67, 26, 98, 90, 48, 14, 2, 82, 91, 9, 76, 69, 44, 50, 47, 53, 71, 92, 12, 59, 37, 19, 60, 39, 13, 77, 16, 55, 41, 84, 31, 65, 27, 99, 51, 4, 45, 80, 46, 57, 21, 28, 81, 54, 29, 61, 88, 52, 43, 75, 20, 1, 10, 0, 35, 30, 32, 95, 38, 63, 87, 89, 24, 34, 93], [37, 25, 50, 66, 86, 23, 76, 27, 83, 97, 89, 35, 32, 41, 33, 87, 14, 59, 48, 61, 52, 3, 73, 67, 24, 56, 93, 22, 7, 70, 77, 28, 91, 30, 68, 29, 60, 8, 94, 13, 43, 17, 88, 57, 81, 34, 15, 95, 79, 96, 72, 84, 55, 46, 58, 10, 12, 38, 11, 99, 53, 42, 65, 44, 31, 21, 82, 6, 92, 26, 51, 5, 39, 63, 40, 9, 49, 18, 78, 80, 71, 74, 90, 2, 54, 47, 85, 75, 69, 45, 0, 16, 19, 1, 64, 98, 4, 62, 36, 20]]]; +var sbtable2 = [[3, 5, 8, 6, 0, 9, 7, 1, 2, 4], [1, 3, 9, 4, 2, 0, 8, 7, 5, 6], [0, 5, 1, 7, 9, 8, 6, 2, 3, 4], [6, 4, 7, 8, 3, 0, 9, 5, 2, 1], [2, 3, 8, 1, 9, 7, 6, 0, 4, 5], [3, 4, 1, 7, 2, 8, 6, 0, 9, 5], [6, 1, 8, 3, 0, 4, 2, 7, 5, 9], [9, 3, 7, 5, 2, 4, 0, 6, 1, 8], [6, 1, 5, 3, 0, 8, 7, 4, 2, 9], [9, 7, 4, 5, 1, 2, 3, 0, 8, 6]]; +var ibtable = [[[11, 38, 3, 63, 36, 39, 79, 19, 87, 68, 76, 51, 20, 56, 77, 61, 52, 73, 74, 94, 45, 31, 99, 28, 29, 90, 81, 96, 65, 75, 13, 32, 70, 21, 55, 33, 9, 15, 92, 14, 82, 97, 78, 37, 49, 0, 80, 57, 58, 7, 1, 89, 98, 53, 64, 5, 6, 66, 43, 83, 10, 50, 47, 84, 62, 71, 4, 67, 86, 42, 35, 34, 91, 12, 17, 69, 44, 48, 85, 30, 16, 95, 54, 23, 46, 18, 88, 59, 40, 60, 41, 25, 27, 22, 2, 24, 72, 93, 26, 8], [28, 58, 74, 34, 6, 14, 16, 46, 87, 24, 59, 57, 23, 88, 75, 65, 79, 38, 82, 13, 49, 99, 8, 94, 15, 19, 0, 96, 41, 95, 45, 35, 1, 91, 2, 52, 42, 76, 90, 84, 20, 54, 4, 63, 25, 5, 47, 85, 93, 12, 51, 81, 71, 69, 22, 17, 36, 64, 77, 80, 98, 53, 44, 18, 10, 68, 97, 61, 56, 70, 92, 27, 40, 9, 60, 7, 72, 21, 43, 3, 33, 32, 50, 26, 67, 62, 39, 48, 73, 86, 29, 89, 37, 78, 55, 66, 31, 30, 11, 83], [4, 16, 22, 66, 37, 14, 47, 89, 59, 60, 51, 92, 71, 70, 96, 13, 17, 85, 58, 93, 81, 69, 28, 90, 78, 39, 63, 88, 0, 75, 2, 53, 23, 99, 94, 68, 54, 74, 41, 29, 8, 26, 50, 98, 82, 95, 42, 7, 24, 77, 30, 9, 21, 1, 87, 62, 46, 15, 43, 38, 36, 55, 49, 57, 52, 56, 91, 65, 11, 84, 6, 73, 97, 33, 83, 27, 35, 25, 40, 32, 18, 31, 86, 48, 45, 3, 79, 76, 72, 64, 44, 20, 80, 19, 10, 5, 61, 34, 67, 12], [80, 91, 71, 26, 75, 8, 22, 92, 7, 70, 65, 51, 89, 31, 81, 30, 83, 47, 44, 85, 55, 19, 57, 87, 74, 6, 73, 40, 49, 32, 52, 78, 0, 12, 54, 15, 64, 28, 4, 24, 68, 17, 72, 53, 1, 94, 58, 88, 11, 82, 43, 86, 60, 63, 99, 96, 37, 77, 67, 33, 18, 5, 41, 59, 16, 36, 79, 97, 61, 23, 56, 48, 98, 38, 66, 21, 34, 29, 10, 3, 93, 46, 50, 39, 14, 9, 2, 13, 69, 42, 62, 35, 27, 90, 25, 76, 20, 95, 45, 84], [48, 16, 54, 47, 9, 39, 81, 91, 33, 53, 63, 21, 50, 85, 97, 90, 49, 36, 84, 74, 51, 76, 37, 67, 59, 58, 23, 20, 65, 82, 7, 77, 12, 31, 46, 1, 5, 14, 3, 30, 94, 95, 2, 34, 70, 89, 40, 87, 43, 79, 64, 57, 68, 26, 56, 44, 88, 61, 0, 24, 71, 18, 60, 4, 80, 96, 27, 15, 6, 13, 98, 29, 28, 25, 72, 93, 55, 75, 52, 66, 11, 8, 86, 45, 22, 83, 17, 10, 35, 38, 73, 99, 62, 41, 19, 69, 78, 92, 42, 32], [23, 19, 88, 44, 5, 39, 75, 81, 30, 14, 49, 54, 62, 55, 18, 83, 77, 76, 74, 92, 6, 95, 52, 36, 15, 27, 53, 38, 28, 50, 48, 99, 78, 67, 90, 87, 16, 41, 58, 65, 79, 11, 68, 84, 24, 97, 64, 47, 9, 22, 43, 86, 37, 8, 33, 17, 93, 61, 25, 72, 57, 98, 42, 80, 13, 89, 2, 12, 31, 56, 26, 85, 3, 29, 66, 63, 82, 10, 20, 94, 51, 0, 1, 4, 21, 60, 34, 35, 32, 45, 59, 71, 46, 69, 7, 96, 91, 40, 70, 73], [89, 62, 33, 19, 21, 97, 15, 2, 83, 22, 65, 30, 55, 7, 63, 73, 39, 61, 44, 37, 11, 26, 31, 79, 91, 0, 72, 68, 23, 87, 28, 24, 45, 74, 98, 14, 70, 54, 88, 42, 35, 59, 46, 16, 27, 92, 69, 66, 94, 32, 75, 77, 64, 57, 50, 71, 10, 3, 67, 81, 90, 58, 99, 5, 82, 85, 25, 52, 12, 8, 48, 49, 76, 96, 80, 20, 29, 34, 53, 51, 86, 93, 38, 84, 9, 36, 6, 60, 1, 78, 13, 95, 41, 17, 47, 18, 43, 40, 4, 56], [40, 68, 88, 47, 43, 28, 67, 23, 42, 99, 48, 3, 12, 27, 79, 25, 6, 55, 19, 80, 81, 62, 83, 69, 39, 73, 36, 30, 44, 82, 70, 52, 31, 34, 58, 74, 54, 2, 13, 45, 93, 49, 75, 72, 29, 87, 5, 7, 17, 76, 90, 63, 84, 51, 59, 35, 20, 97, 22, 65, 57, 66, 78, 86, 11, 9, 71, 98, 53, 56, 15, 46, 92, 0, 26, 21, 32, 38, 77, 91, 4, 24, 61, 85, 94, 1, 96, 64, 8, 41, 33, 50, 18, 60, 10, 16, 37, 95, 14, 89], [96, 23, 99, 42, 94, 91, 19, 53, 41, 25, 39, 73, 70, 72, 33, 35, 88, 2, 63, 49, 95, 60, 48, 97, 5, 54, 47, 40, 98, 18, 57, 28, 67, 92, 10, 79, 13, 76, 8, 55, 90, 62, 50, 87, 65, 82, 84, 21, 0, 14, 45, 44, 20, 38, 17, 3, 81, 16, 58, 4, 31, 29, 86, 15, 43, 64, 26, 52, 46, 69, 37, 68, 6, 22, 61, 7, 83, 71, 30, 75, 85, 78, 24, 27, 89, 9, 80, 34, 66, 77, 36, 93, 56, 59, 1, 12, 51, 32, 11, 74], [75, 99, 97, 8, 15, 48, 6, 25, 60, 90, 91, 95, 23, 3, 89, 77, 22, 78, 94, 55, 98, 35, 53, 96, 18, 52, 12, 16, 50, 20, 49, 9, 17, 13, 57, 44, 14, 47, 86, 56, 88, 41, 29, 32, 70, 1, 62, 37, 85, 38, 59, 36, 5, 68, 71, 69, 67, 27, 39, 4, 54, 0, 84, 45, 61, 73, 79, 93, 58, 66, 10, 76, 51, 92, 19, 87, 24, 7, 28, 2, 11, 80, 81, 33, 64, 74, 46, 65, 26, 72, 40, 82, 34, 42, 21, 83, 31, 30, 43, 63]], [[98, 58, 34, 65, 83, 74, 88, 5, 89, 46, 80, 43, 18, 87, 9, 28, 71, 90, 4, 36, 40, 11, 39, 31, 76, 92, 82, 48, 66, 41, 49, 99, 78, 79, 97, 86, 75, 68, 3, 7, 62, 64, 26, 22, 13, 77, 54, 50, 10, 96, 15, 35, 94, 73, 57, 93, 16, 61, 37, 38, 53, 2, 63, 23, 67, 72, 95, 70, 29, 33, 45, 47, 27, 24, 69, 44, 55, 85, 14, 8, 91, 12, 42, 6, 84, 81, 19, 25, 32, 51, 1, 17, 52, 56, 60, 0, 59, 30, 21, 20], [26, 63, 99, 57, 77, 53, 9, 75, 76, 80, 46, 97, 37, 14, 1, 12, 35, 20, 0, 15, 28, 19, 48, 41, 67, 30, 60, 69, 29, 34, 73, 10, 51, 47, 66, 82, 7, 98, 39, 96, 8, 62, 42, 3, 95, 5, 11, 13, 90, 94, 32, 31, 83, 64, 36, 88, 72, 52, 38, 54, 45, 17, 61, 81, 78, 44, 79, 55, 56, 86, 2, 40, 16, 24, 4, 71, 49, 33, 23, 92, 84, 74, 65, 43, 87, 59, 89, 68, 22, 70, 25, 58, 21, 85, 6, 50, 27, 93, 91, 18], [22, 31, 11, 72, 37, 70, 90, 60, 24, 56, 28, 69, 54, 85, 5, 98, 1, 76, 59, 20, 86, 32, 61, 49, 63, 42, 74, 53, 84, 3, 95, 23, 15, 36, 4, 81, 6, 21, 19, 27, 17, 83, 30, 45, 25, 65, 10, 50, 80, 66, 46, 75, 93, 82, 78, 99, 97, 68, 8, 13, 58, 94, 41, 14, 48, 52, 29, 79, 91, 89, 9, 55, 57, 2, 39, 92, 0, 44, 18, 62, 73, 96, 34, 88, 16, 77, 71, 43, 33, 40, 7, 12, 87, 51, 26, 47, 64, 35, 67, 38], [0, 80, 63, 52, 49, 69, 15, 87, 55, 60, 22, 91, 31, 89, 78, 28, 48, 12, 10, 42, 82, 23, 29, 18, 21, 2, 19, 7, 68, 37, 54, 90, 81, 43, 57, 94, 26, 95, 96, 88, 14, 51, 79, 71, 66, 76, 34, 24, 30, 9, 62, 44, 77, 65, 6, 27, 4, 97, 38, 36, 59, 3, 1, 50, 72, 46, 64, 35, 20, 11, 53, 13, 58, 39, 74, 8, 83, 75, 40, 98, 17, 99, 85, 32, 92, 67, 84, 5, 25, 56, 16, 47, 70, 93, 86, 61, 73, 41, 45, 33], [64, 57, 56, 7, 86, 9, 65, 16, 1, 63, 71, 26, 53, 98, 69, 5, 84, 28, 36, 58, 54, 99, 42, 74, 68, 3, 88, 22, 82, 97, 85, 45, 21, 66, 43, 59, 83, 51, 89, 87, 31, 12, 95, 52, 60, 17, 80, 27, 72, 93, 10, 39, 91, 30, 75, 61, 24, 8, 23, 2, 18, 73, 94, 6, 38, 44, 13, 40, 48, 4, 81, 37, 96, 46, 19, 49, 34, 50, 0, 20, 14, 15, 25, 92, 33, 29, 77, 90, 78, 67, 62, 41, 35, 47, 79, 32, 11, 70, 76, 55], [24, 99, 28, 52, 64, 3, 63, 16, 15, 87, 47, 6, 59, 98, 13, 78, 57, 81, 56, 94, 0, 37, 70, 73, 65, 83, 90, 92, 60, 82, 67, 40, 74, 34, 91, 2, 33, 85, 21, 54, 12, 31, 84, 55, 53, 49, 36, 25, 39, 20, 29, 93, 51, 76, 42, 96, 71, 86, 95, 66, 45, 19, 8, 58, 22, 69, 80, 79, 89, 17, 61, 18, 88, 38, 72, 75, 48, 30, 7, 10, 11, 35, 23, 9, 26, 4, 62, 44, 1, 43, 46, 14, 41, 77, 97, 27, 68, 32, 5, 50], [90, 80, 22, 27, 16, 23, 11, 31, 8, 75, 54, 82, 68, 66, 10, 40, 95, 99, 14, 42, 56, 88, 12, 55, 38, 6, 79, 84, 26, 92, 4, 67, 48, 5, 46, 77, 19, 53, 97, 34, 64, 73, 58, 47, 18, 39, 81, 59, 74, 62, 87, 98, 50, 1, 25, 9, 72, 69, 61, 60, 86, 3, 24, 28, 45, 30, 96, 36, 57, 83, 89, 63, 0, 33, 17, 7, 94, 15, 43, 2, 44, 41, 20, 85, 32, 65, 93, 49, 91, 52, 71, 37, 35, 13, 21, 51, 70, 29, 78, 76], [95, 50, 72, 0, 68, 98, 49, 53, 56, 41, 54, 22, 57, 78, 66, 31, 52, 29, 86, 30, 62, 26, 32, 46, 42, 48, 14, 21, 88, 94, 77, 92, 7, 63, 91, 28, 93, 38, 90, 71, 34, 25, 11, 83, 55, 70, 97, 82, 89, 76, 10, 99, 9, 45, 60, 39, 44, 16, 17, 37, 47, 79, 33, 36, 59, 8, 80, 23, 1, 18, 64, 51, 61, 81, 75, 40, 35, 73, 13, 65, 12, 4, 19, 85, 96, 6, 2, 58, 27, 43, 5, 87, 20, 15, 3, 24, 84, 67, 74, 69], [38, 33, 29, 88, 49, 47, 60, 69, 75, 12, 44, 89, 35, 45, 23, 30, 21, 28, 65, 86, 81, 40, 93, 97, 67, 2, 94, 9, 61, 92, 90, 32, 62, 36, 58, 51, 37, 73, 50, 63, 39, 43, 16, 71, 46, 91, 13, 7, 20, 72, 41, 59, 70, 48, 10, 52, 5, 34, 76, 15, 87, 6, 98, 18, 57, 77, 24, 79, 27, 19, 3, 0, 8, 95, 74, 64, 42, 4, 68, 31, 84, 54, 99, 25, 1, 80, 85, 26, 83, 11, 56, 55, 78, 66, 82, 14, 53, 22, 17, 96], [26, 12, 62, 5, 78, 53, 31, 46, 51, 8, 69, 80, 32, 68, 76, 42, 29, 87, 28, 75, 40, 91, 58, 7, 88, 92, 82, 96, 70, 50, 47, 65, 85, 3, 48, 73, 90, 89, 97, 84, 81, 39, 43, 79, 64, 54, 2, 41, 38, 10, 24, 22, 25, 93, 44, 23, 49, 37, 14, 95, 72, 74, 15, 11, 55, 98, 99, 0, 9, 16, 18, 20, 63, 30, 57, 19, 4, 17, 67, 35, 60, 45, 59, 56, 36, 77, 66, 33, 27, 71, 86, 1, 13, 34, 21, 61, 6, 94, 52, 83]], [[10, 87, 64, 11, 25, 97, 93, 18, 99, 38, 17, 72, 50, 23, 90, 57, 98, 12, 29, 46, 19, 77, 42, 15, 73, 52, 53, 89, 35, 20, 9, 41, 79, 13, 95, 48, 85, 54, 61, 94, 0, 74, 62, 40, 78, 68, 24, 28, 86, 21, 59, 14, 82, 84, 92, 58, 34, 31, 6, 16, 75, 81, 43, 47, 7, 3, 2, 37, 67, 8, 66, 22, 30, 32, 71, 44, 70, 1, 36, 26, 33, 63, 80, 83, 91, 76, 56, 39, 69, 55, 96, 60, 27, 49, 65, 88, 5, 4, 51, 45], [73, 87, 48, 43, 71, 40, 88, 23, 60, 35, 30, 5, 75, 25, 59, 95, 58, 79, 4, 89, 96, 17, 38, 6, 39, 9, 68, 81, 3, 90, 93, 99, 16, 42, 21, 45, 74, 84, 77, 65, 63, 98, 11, 51, 0, 33, 91, 22, 69, 34, 31, 20, 46, 2, 76, 54, 15, 62, 13, 70, 92, 55, 82, 47, 78, 7, 24, 27, 86, 1, 56, 61, 12, 44, 85, 57, 49, 19, 18, 67, 66, 52, 72, 53, 83, 41, 64, 50, 26, 94, 32, 80, 28, 10, 37, 14, 8, 97, 29, 36], [60, 50, 53, 78, 63, 90, 11, 33, 21, 24, 20, 55, 8, 25, 6, 58, 3, 98, 44, 82, 96, 14, 1, 59, 92, 75, 51, 36, 30, 64, 72, 40, 16, 94, 2, 74, 93, 85, 12, 10, 43, 32, 42, 39, 79, 97, 17, 38, 89, 29, 68, 52, 49, 0, 34, 19, 70, 84, 54, 9, 23, 71, 65, 28, 83, 56, 67, 57, 13, 18, 77, 5, 4, 88, 41, 22, 46, 76, 48, 61, 81, 45, 86, 35, 47, 87, 91, 99, 73, 66, 27, 37, 31, 15, 7, 80, 62, 95, 69, 26], [49, 73, 63, 66, 38, 35, 32, 96, 87, 26, 36, 92, 58, 6, 17, 47, 29, 84, 11, 18, 67, 45, 78, 4, 52, 81, 59, 30, 91, 54, 56, 89, 48, 46, 20, 79, 1, 62, 9, 53, 10, 95, 88, 34, 40, 80, 41, 64, 83, 55, 43, 7, 24, 61, 77, 5, 14, 93, 16, 21, 85, 39, 76, 50, 57, 65, 37, 60, 28, 19, 2, 97, 42, 33, 13, 68, 90, 25, 69, 99, 98, 74, 3, 8, 12, 15, 82, 71, 31, 23, 75, 27, 51, 94, 86, 0, 44, 22, 72, 70], [30, 32, 52, 21, 15, 79, 53, 40, 56, 44, 55, 6, 63, 39, 61, 4, 93, 43, 23, 99, 38, 66, 88, 18, 1, 86, 33, 9, 82, 76, 70, 51, 48, 69, 85, 11, 5, 91, 59, 77, 49, 75, 22, 87, 65, 41, 26, 50, 47, 98, 57, 10, 58, 62, 46, 28, 20, 71, 78, 95, 0, 72, 80, 73, 92, 36, 96, 37, 68, 2, 35, 83, 90, 25, 13, 24, 34, 42, 45, 74, 67, 16, 84, 97, 19, 64, 27, 14, 31, 12, 54, 60, 3, 8, 17, 89, 81, 94, 7, 29], [1, 18, 25, 13, 57, 53, 92, 95, 94, 88, 10, 82, 34, 83, 7, 28, 55, 11, 5, 58, 8, 9, 74, 99, 52, 32, 62, 45, 65, 50, 41, 56, 96, 44, 38, 47, 3, 39, 81, 19, 2, 22, 49, 12, 40, 67, 89, 76, 64, 70, 61, 21, 31, 54, 51, 17, 46, 30, 24, 60, 66, 26, 77, 63, 37, 35, 23, 6, 4, 87, 79, 97, 20, 48, 36, 85, 73, 71, 27, 43, 0, 15, 69, 78, 59, 86, 75, 33, 42, 84, 91, 93, 68, 80, 16, 98, 90, 72, 14, 29], [30, 16, 10, 67, 83, 81, 42, 94, 35, 59, 22, 5, 53, 61, 1, 96, 68, 45, 95, 66, 36, 37, 97, 87, 77, 93, 86, 33, 49, 13, 43, 14, 0, 34, 7, 2, 41, 21, 31, 32, 76, 55, 62, 72, 90, 71, 38, 23, 46, 12, 51, 19, 6, 44, 52, 48, 73, 24, 29, 74, 85, 99, 80, 28, 47, 9, 39, 84, 64, 57, 75, 98, 88, 26, 17, 63, 27, 56, 69, 8, 54, 25, 91, 78, 3, 50, 65, 70, 11, 4, 92, 60, 40, 58, 15, 89, 82, 18, 79, 20], [60, 88, 79, 35, 40, 43, 52, 4, 17, 31, 84, 44, 50, 94, 91, 38, 87, 25, 92, 62, 14, 97, 37, 18, 86, 28, 65, 90, 19, 29, 30, 75, 68, 49, 69, 11, 59, 41, 89, 33, 47, 54, 98, 42, 80, 56, 78, 99, 23, 82, 9, 48, 21, 1, 63, 22, 58, 95, 10, 12, 26, 74, 85, 13, 8, 27, 24, 53, 61, 34, 64, 71, 66, 32, 83, 73, 15, 67, 0, 20, 16, 6, 77, 45, 5, 93, 76, 39, 57, 2, 96, 46, 51, 3, 55, 7, 81, 70, 36, 72], [35, 56, 77, 58, 23, 83, 91, 81, 53, 70, 20, 74, 84, 50, 79, 51, 88, 44, 45, 71, 37, 26, 54, 48, 27, 43, 0, 12, 95, 97, 1, 63, 67, 98, 59, 40, 13, 19, 31, 87, 99, 16, 57, 30, 17, 92, 55, 68, 5, 46, 64, 8, 7, 42, 90, 33, 29, 9, 85, 24, 2, 69, 32, 10, 21, 65, 39, 86, 52, 62, 76, 89, 82, 6, 78, 4, 47, 28, 3, 75, 41, 15, 18, 14, 66, 73, 38, 80, 61, 49, 11, 22, 34, 93, 36, 25, 94, 96, 60, 72], [6, 90, 16, 43, 20, 55, 94, 8, 62, 59, 47, 32, 81, 67, 42, 35, 71, 19, 54, 49, 2, 88, 38, 5, 53, 77, 85, 11, 73, 34, 3, 72, 95, 58, 82, 64, 44, 29, 93, 14, 56, 98, 4, 37, 45, 75, 86, 60, 87, 7, 31, 17, 89, 97, 10, 70, 36, 99, 46, 63, 76, 41, 48, 84, 9, 52, 1, 24, 83, 25, 68, 18, 50, 26, 30, 27, 61, 0, 91, 33, 51, 40, 79, 39, 15, 74, 28, 78, 69, 96, 57, 65, 21, 80, 22, 23, 12, 13, 92, 66]], [[93, 42, 18, 65, 28, 87, 39, 35, 71, 23, 96, 90, 15, 58, 78, 33, 92, 88, 49, 64, 24, 84, 77, 8, 97, 50, 4, 53, 27, 83, 95, 31, 80, 47, 86, 55, 11, 40, 37, 14, 34, 60, 54, 12, 94, 9, 41, 2, 26, 10, 6, 57, 75, 63, 32, 43, 13, 52, 82, 73, 20, 30, 17, 5, 3, 68, 51, 89, 62, 79, 69, 70, 66, 45, 25, 22, 85, 0, 91, 76, 38, 7, 99, 61, 29, 98, 16, 36, 44, 67, 59, 56, 48, 81, 21, 46, 74, 72, 19, 1], [78, 91, 97, 77, 75, 85, 32, 2, 40, 86, 38, 18, 56, 47, 50, 87, 27, 92, 64, 39, 98, 23, 33, 72, 84, 58, 16, 99, 19, 65, 55, 73, 74, 42, 4, 48, 95, 8, 51, 3, 37, 82, 96, 69, 14, 44, 12, 21, 76, 70, 36, 15, 71, 41, 20, 31, 54, 13, 0, 9, 94, 67, 1, 60, 81, 61, 11, 6, 90, 5, 49, 10, 34, 7, 35, 30, 25, 88, 79, 93, 66, 62, 22, 24, 52, 17, 89, 45, 83, 57, 28, 46, 63, 68, 26, 29, 80, 59, 43, 53], [78, 91, 97, 77, 75, 85, 32, 2, 40, 86, 38, 18, 56, 47, 50, 87, 27, 92, 64, 39, 98, 23, 33, 72, 84, 58, 16, 99, 19, 65, 55, 73, 74, 42, 4, 48, 95, 8, 51, 3, 37, 82, 96, 69, 14, 44, 12, 21, 76, 70, 36, 15, 71, 41, 20, 31, 54, 13, 0, 9, 94, 67, 1, 60, 81, 61, 11, 6, 90, 5, 49, 10, 34, 7, 35, 30, 25, 88, 79, 93, 66, 62, 22, 24, 52, 17, 89, 45, 83, 57, 28, 46, 63, 68, 26, 29, 80, 59, 43, 53], [41, 65, 16, 86, 77, 67, 51, 81, 50, 25, 90, 87, 98, 99, 23, 9, 4, 97, 39, 36, 26, 42, 13, 66, 82, 22, 47, 88, 49, 46, 59, 78, 20, 19, 7, 93, 56, 84, 40, 61, 89, 21, 74, 1, 54, 44, 34, 6, 28, 8, 43, 76, 48, 71, 85, 38, 83, 18, 24, 79, 68, 80, 45, 33, 91, 27, 32, 70, 95, 3, 58, 60, 73, 10, 75, 52, 64, 2, 94, 30, 17, 55, 62, 35, 11, 12, 53, 63, 14, 31, 57, 0, 37, 72, 92, 15, 5, 29, 69, 96], [74, 50, 44, 80, 11, 46, 39, 54, 24, 3, 7, 62, 41, 8, 75, 23, 57, 82, 51, 86, 97, 79, 22, 60, 42, 14, 28, 67, 32, 73, 48, 56, 89, 26, 25, 77, 21, 68, 10, 2, 58, 76, 92, 95, 38, 29, 43, 35, 12, 16, 55, 17, 63, 91, 45, 64, 59, 13, 47, 31, 83, 5, 99, 49, 78, 71, 96, 53, 36, 90, 1, 66, 15, 0, 85, 65, 27, 4, 98, 94, 6, 84, 72, 30, 70, 20, 61, 69, 37, 93, 34, 87, 9, 52, 88, 81, 33, 19, 40, 18], [35, 67, 27, 53, 26, 79, 20, 46, 38, 4, 72, 84, 70, 58, 65, 91, 92, 21, 32, 71, 54, 24, 89, 0, 37, 8, 22, 81, 12, 87, 43, 18, 82, 17, 33, 76, 25, 13, 74, 51, 99, 3, 55, 60, 19, 73, 86, 62, 93, 52, 31, 64, 96, 66, 14, 40, 42, 69, 45, 6, 61, 34, 41, 59, 49, 10, 39, 2, 75, 80, 15, 94, 7, 23, 95, 78, 90, 5, 1, 28, 56, 30, 9, 98, 44, 48, 68, 77, 47, 11, 63, 83, 88, 36, 97, 29, 57, 50, 16, 85], [8, 51, 32, 96, 34, 24, 88, 54, 87, 83, 45, 99, 49, 30, 55, 61, 95, 60, 59, 76, 57, 5, 19, 56, 89, 66, 27, 94, 14, 6, 21, 58, 43, 74, 10, 2, 17, 62, 47, 16, 69, 84, 63, 39, 4, 92, 35, 48, 53, 64, 71, 50, 72, 28, 42, 46, 91, 23, 15, 40, 11, 78, 29, 86, 13, 90, 85, 20, 65, 93, 73, 80, 41, 37, 82, 79, 52, 9, 97, 38, 75, 25, 44, 7, 26, 12, 22, 0, 33, 1, 31, 68, 70, 67, 98, 18, 77, 36, 3, 81], [34, 17, 91, 45, 63, 64, 1, 71, 98, 78, 94, 3, 99, 12, 15, 93, 69, 19, 61, 80, 92, 81, 33, 51, 40, 10, 75, 24, 5, 87, 73, 46, 52, 86, 4, 70, 89, 56, 2, 67, 72, 58, 49, 84, 29, 18, 22, 38, 95, 36, 43, 35, 37, 85, 57, 8, 76, 7, 27, 28, 55, 74, 42, 13, 77, 31, 60, 39, 48, 68, 97, 79, 44, 54, 88, 82, 21, 66, 9, 23, 59, 50, 47, 16, 83, 25, 6, 30, 41, 96, 90, 14, 62, 20, 65, 11, 26, 32, 53, 0], [7, 51, 77, 80, 0, 53, 56, 27, 2, 78, 28, 32, 26, 22, 73, 93, 36, 54, 64, 99, 60, 50, 21, 11, 39, 42, 81, 8, 1, 79, 61, 5, 15, 31, 59, 33, 20, 94, 23, 88, 69, 17, 35, 95, 40, 75, 85, 87, 43, 47, 89, 12, 71, 72, 9, 83, 67, 38, 90, 82, 29, 10, 37, 91, 97, 74, 6, 86, 30, 63, 24, 49, 3, 13, 4, 57, 76, 70, 98, 34, 58, 52, 96, 84, 25, 55, 44, 46, 18, 65, 62, 68, 48, 66, 41, 19, 14, 45, 16, 92], [96, 2, 76, 15, 31, 22, 71, 38, 88, 26, 67, 0, 21, 10, 58, 39, 63, 77, 23, 50, 48, 19, 78, 30, 93, 91, 14, 12, 28, 11, 53, 73, 60, 61, 35, 79, 57, 70, 40, 20, 56, 32, 65, 64, 68, 55, 82, 94, 45, 95, 66, 7, 43, 16, 37, 99, 72, 62, 13, 4, 47, 8, 98, 89, 46, 29, 51, 85, 74, 17, 97, 84, 33, 18, 25, 83, 69, 44, 87, 49, 92, 9, 90, 36, 41, 81, 59, 54, 34, 6, 42, 5, 80, 3, 1, 86, 27, 52, 24, 75]], [[61, 85, 41, 37, 12, 25, 27, 17, 92, 52, 44, 66, 13, 15, 19, 78, 77, 9, 40, 20, 58, 43, 76, 24, 59, 42, 28, 30, 96, 86, 62, 89, 67, 35, 45, 6, 36, 50, 74, 26, 84, 95, 7, 68, 97, 79, 51, 16, 80, 3, 82, 2, 93, 21, 31, 98, 18, 60, 39, 72, 54, 65, 73, 32, 5, 70, 99, 10, 14, 71, 34, 4, 64, 33, 55, 48, 75, 87, 57, 81, 22, 46, 69, 63, 90, 23, 38, 56, 8, 29, 83, 88, 11, 0, 47, 53, 91, 94, 49, 1], [35, 22, 67, 24, 94, 82, 12, 68, 30, 43, 58, 96, 27, 69, 65, 18, 7, 79, 14, 2, 44, 60, 54, 47, 51, 83, 89, 3, 20, 11, 40, 85, 37, 48, 49, 8, 73, 15, 90, 93, 70, 84, 34, 4, 86, 98, 17, 46, 72, 63, 26, 56, 97, 74, 91, 99, 36, 80, 81, 41, 53, 88, 77, 21, 0, 23, 64, 76, 95, 33, 62, 1, 75, 87, 10, 66, 57, 6, 28, 25, 42, 9, 50, 39, 29, 78, 13, 19, 31, 45, 55, 61, 38, 71, 59, 5, 92, 52, 32, 16], [70, 29, 50, 57, 31, 9, 59, 45, 87, 71, 98, 89, 77, 97, 86, 5, 21, 61, 47, 41, 76, 37, 65, 12, 58, 75, 78, 91, 27, 63, 64, 83, 55, 95, 4, 11, 82, 23, 39, 40, 42, 68, 69, 48, 54, 13, 99, 33, 26, 0, 66, 79, 46, 72, 49, 44, 25, 74, 73, 28, 38, 10, 2, 85, 15, 19, 52, 94, 7, 36, 17, 32, 96, 3, 20, 30, 92, 51, 14, 90, 24, 67, 43, 34, 93, 84, 62, 81, 1, 56, 53, 88, 80, 35, 22, 6, 18, 16, 8, 60], [62, 75, 38, 50, 52, 86, 53, 73, 99, 59, 26, 46, 48, 90, 64, 1, 65, 13, 30, 36, 57, 16, 97, 91, 51, 33, 80, 67, 5, 25, 8, 96, 94, 84, 6, 89, 88, 20, 10, 85, 41, 56, 9, 34, 63, 0, 18, 12, 42, 15, 87, 4, 61, 43, 82, 17, 98, 60, 92, 70, 49, 71, 47, 54, 79, 83, 27, 74, 58, 2, 76, 93, 22, 69, 78, 44, 28, 95, 72, 68, 7, 55, 29, 77, 14, 23, 31, 32, 3, 37, 11, 66, 35, 39, 24, 81, 19, 21, 40, 45], [65, 78, 7, 83, 80, 77, 71, 72, 22, 85, 63, 73, 44, 87, 86, 88, 41, 30, 95, 67, 58, 21, 4, 5, 26, 27, 75, 31, 99, 52, 92, 64, 28, 8, 19, 98, 84, 34, 9, 48, 60, 0, 56, 1, 62, 16, 91, 32, 89, 79, 40, 11, 54, 51, 43, 12, 45, 66, 23, 13, 96, 81, 47, 36, 46, 93, 2, 69, 33, 3, 20, 6, 35, 18, 74, 68, 53, 37, 29, 61, 90, 57, 14, 42, 24, 76, 10, 38, 97, 39, 25, 50, 49, 70, 55, 82, 15, 17, 59, 94], [11, 17, 21, 48, 63, 0, 73, 14, 10, 88, 2, 28, 6, 9, 19, 69, 81, 13, 35, 95, 60, 56, 30, 47, 91, 92, 86, 29, 76, 50, 16, 25, 82, 93, 8, 5, 58, 94, 98, 20, 74, 77, 4, 52, 97, 79, 27, 42, 36, 12, 26, 72, 71, 45, 24, 87, 1, 39, 22, 89, 53, 31, 61, 37, 33, 18, 59, 43, 80, 15, 54, 38, 78, 90, 32, 83, 23, 44, 55, 66, 64, 3, 67, 49, 84, 62, 51, 41, 46, 85, 96, 34, 99, 75, 7, 40, 70, 68, 65, 57], [43, 38, 48, 47, 74, 88, 64, 54, 59, 35, 29, 18, 53, 91, 68, 66, 12, 79, 55, 41, 34, 36, 33, 94, 56, 87, 25, 6, 1, 8, 63, 96, 93, 44, 40, 65, 13, 77, 61, 85, 99, 80, 11, 51, 14, 46, 5, 39, 92, 27, 75, 52, 72, 83, 19, 62, 32, 24, 82, 95, 17, 37, 0, 15, 97, 4, 42, 86, 84, 3, 26, 81, 98, 9, 73, 22, 28, 78, 60, 76, 16, 23, 71, 89, 31, 57, 58, 20, 50, 10, 49, 45, 21, 67, 90, 30, 7, 2, 70, 69], [78, 35, 94, 59, 8, 23, 10, 76, 96, 80, 93, 9, 27, 95, 61, 84, 88, 91, 2, 33, 1, 85, 3, 36, 79, 18, 92, 5, 34, 81, 98, 40, 38, 68, 90, 70, 89, 65, 48, 6, 77, 30, 72, 62, 16, 83, 32, 54, 99, 82, 75, 41, 53, 17, 67, 43, 28, 21, 46, 7, 25, 64, 13, 14, 87, 58, 63, 31, 39, 47, 24, 71, 74, 55, 20, 11, 22, 45, 44, 51, 37, 15, 42, 56, 66, 29, 86, 49, 69, 57, 73, 50, 60, 0, 4, 19, 52, 97, 12, 26], [88, 86, 41, 1, 70, 10, 19, 2, 11, 44, 87, 26, 53, 59, 40, 18, 61, 13, 21, 56, 85, 75, 25, 5, 97, 7, 36, 67, 76, 79, 90, 65, 91, 0, 98, 89, 20, 55, 93, 58, 28, 63, 6, 83, 47, 71, 73, 49, 39, 31, 48, 69, 82, 50, 78, 62, 14, 74, 29, 54, 57, 80, 32, 94, 3, 66, 30, 35, 8, 46, 16, 51, 24, 17, 4, 84, 45, 60, 34, 15, 72, 77, 42, 12, 64, 9, 33, 95, 81, 96, 38, 43, 52, 99, 22, 92, 27, 23, 37, 68], [90, 93, 83, 21, 96, 71, 67, 28, 37, 75, 55, 58, 56, 39, 16, 46, 91, 41, 77, 92, 99, 65, 27, 5, 24, 1, 69, 7, 31, 35, 33, 64, 12, 14, 45, 11, 98, 0, 57, 72, 74, 13, 61, 40, 63, 89, 53, 85, 18, 76, 2, 70, 20, 60, 84, 52, 25, 43, 54, 17, 36, 19, 97, 73, 94, 62, 3, 23, 34, 88, 29, 80, 50, 22, 81, 87, 6, 30, 78, 48, 79, 44, 66, 8, 51, 86, 4, 15, 42, 10, 82, 32, 68, 26, 38, 47, 49, 9, 95, 59]]]; +var ibtable2 = [[4, 7, 8, 0, 9, 1, 3, 6, 2, 5], [5, 0, 4, 1, 3, 8, 9, 7, 6, 2], [0, 2, 7, 8, 9, 1, 6, 3, 5, 4], [5, 9, 8, 4, 1, 7, 0, 2, 3, 6], [7, 3, 0, 1, 8, 9, 6, 5, 2, 4], [7, 2, 4, 0, 1, 9, 6, 3, 5, 8], [4, 1, 6, 3, 5, 8, 0, 7, 2, 9], [6, 8, 4, 1, 5, 3, 7, 2, 9, 0], [4, 1, 8, 3, 7, 2, 0, 6, 5, 9], [7, 4, 5, 6, 2, 3, 9, 1, 8, 0]]; +function crc16(data, bits, initial = 65535, poly = 33800) { + let v = bigInt(initial); + let bigPoly = bigInt(poly); + for (let i = 0; i < bits; i++) { + v = v.shiftRight(1).xor(bigPoly.and(v.xor(data).and(bigInt.one).equals(bigInt.one) ? bigPoly : bigInt.zero)); + data = data.shiftRight(1); + } + return v.toJSNumber(); +} +function decodeAccessCode(code) { + code = code.replace(/\D/i, ""); + if (code.length !== 20) { + return null; + } + let body = _.range(3, 15, 2).map((i) => parseInt(code[i]) * 10 + parseInt(code[i + 1])); + let crc = _.range(15, 20).map((i) => parseInt(code[i])); + let boxes = [(crc[3] + crc[2]) % 10, crc[2], crc[3], crc[4], (crc[4] + crc[0]) % 10, crc[1]]; + for (const [n, i] of body.entries()) { + body[n] = ibtable[4][boxes[n]][i]; + } + const rv = body[0] >> 4; + boxes = [(crc[1] + crc[0]) % 10, crc[1], crc[2], crc[3], crc[4], (crc[4] + crc[1]) % 10]; + for (const [n, i] of body.entries()) { + if (n == 0) { + body[n] = body[n] & 240 | ibtable2[boxes[n]][body[n] & 15]; + } else { + body[n] = ibtable[rv][boxes[n]][i]; + } + } + const serial = body.map((i) => i.toString().padStart(2, "0")).join(""); + if (crc16(bigInt(code.substring(0, 3) + serial, 16), 15 * 4) !== parseInt(code.substring(15, 20))) { + throw new Error(`CRC16 mismatch.`); + } + return { revision: rv, serial: parseInt(serial) }; +} +function encodeAccessCode(company, serial, rev = 0) { + if (company < 0 || company > 999) { + throw new Error(`Invalid company code`); + } + if (serial < 0 || serial > 99999999999) { + throw new Error(`Invalid serial number`); + } + if (rev < 0 || rev > sbtable.length) { + throw new Error(`Invalid revision`); + } + const bodyStr = `${rev}${serial.toString().padStart(11, "0")}`; + const crc_i = crc16(bigInt(company.toString().padStart(3, "0") + bodyStr, 16), 15 * 4); + const crc = _.range(5).map((i) => Math.floor(crc_i / 10 ** (4 - i)) % 10); + const body = _.range(0, 12, 2).map((i) => parseInt(bodyStr[i]) * 10 + parseInt(bodyStr[i + 1])); + let boxes = [(crc[1] + crc[0]) % 10, crc[1], crc[2], crc[3], crc[4], (crc[4] + crc[1]) % 10]; + for (const [n, i] of body.entries()) { + if (n == 0) { + body[n] = body[n] & 240 | sbtable2[boxes[n]][body[n] & 15]; + } else { + body[n] = sbtable[rev][boxes[n]][i]; + } + } + boxes = [(crc[3] + crc[2]) % 10, crc[2], crc[3], crc[4], (crc[4] + crc[0]) % 10, crc[1]]; + for (const [n, i] of body.entries()) { + body[n] = sbtable[4][boxes[n]][i]; + } + return `${company.toString().padStart(3, "0")}${body.map((i) => i.toString().padStart(2, "0")).join("")}${crc_i.toString().padStart(5, "0")}`; } // src/card.ts var CARD_ID_REGEX = /^(?:[0-9]{4} ?){5}$/i; +var AMNET_COMPANY_ID = 597; function generateCardNumber() { - let cardId = ""; - while (true) { - const firstDigit = Math.floor(Math.random() * 10); - if (firstDigit !== 3) { - cardId = firstDigit.toString(); - break; - } - } - for (let i = 0; i < 19; i++) { - cardId += Math.floor(Math.random() * 10).toString(); - } - return cardId; + const serial = Math.floor(Math.random() * 1e11); + return encodeAccessCode(AMNET_COMPANY_ID, serial, 0); } function validateCardId(id) { return id[0] !== "3" && CARD_ID_REGEX.test(id); @@ -119,7 +157,10 @@ function getGameName(id) { } export { AMNET_API_VERSION, + AMNET_COMPANY_ID, CARD_ID_REGEX, + decodeAccessCode as DecodeAMAccessCode, + encodeAccessCode as EncodeAMAccessCode, MIN_AMNET_API_VERSION, fetchServerInfo, generateCardNumber, diff --git a/amnet-shared/dist/index.mjs.map b/amnet-shared/dist/index.mjs.map index a316ce2..68c17b7 100644 --- a/amnet-shared/dist/index.mjs.map +++ b/amnet-shared/dist/index.mjs.map @@ -1 +1 @@ -{"version":3,"sources":["../src/api.ts","../src/card.ts","../src/gameIds.ts"],"sourcesContent":["/**\r\n * The latest version of the AMNet API this library supports\r\n */\r\nexport const AMNET_API_VERSION = 1;\r\n\r\n/**\r\n * The minimum version of the AMNet API this library supports\r\n */\r\nexport const MIN_AMNET_API_VERSION = 1;\r\n\r\nexport interface AMCardInRequest {\r\n cardId: string;\r\n physicalCardIDm?: string;\r\n}\r\n\r\n/**\r\n * Server connection info (api version, address, etc.)\r\n */\r\nexport interface AMServerConnectionInfo {\r\n apiVersion: number;\r\n serverAddress: string;\r\n}\r\n\r\n/**\r\n * User-definable server metadata\r\n */\r\nexport interface AMServerMetadata {\r\n serverName: string;\r\n gameId?: string | null | undefined;\r\n}\r\n\r\n/**\r\n * Metrics about the current game session\r\n */\r\nexport interface AMServerInstanceMetrics {\r\n timeSinceLastPoll?: number;\r\n sessionUptime?: number;\r\n}\r\n\r\n/**\r\n * The response from an AMNet server's info endpoint\r\n */\r\nexport type AMServerInfo = AMServerMetadata & AMServerConnectionInfo & AMServerInstanceMetrics;\r\n\r\n/**\r\n * Performs the card-in request to the specified server\r\n * @param server The server to perform the request against\r\n * @param request The card-in request to send\r\n * @param signal The abort signal to use for the request\r\n */\r\nexport async function performCardIn(server: AMServerConnectionInfo, request: AMCardInRequest, signal: AbortSignal): Promise {\r\n if (server.apiVersion < MIN_AMNET_API_VERSION || server.apiVersion > AMNET_API_VERSION) {\r\n throw new Error(\"Unsupported server API version\");\r\n }\r\n\r\n try {\r\n const response = await fetch(`${new URL(server.serverAddress).origin}/amnet/signin`, {\r\n method: \"POST\",\r\n headers: {\r\n \"Content-Type\": \"application/json\"\r\n },\r\n body: JSON.stringify(request),\r\n signal: signal\r\n });\r\n\r\n return response.ok;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * Attempts to connect to the specified server and fetch its info\r\n * @param serverAddress The origin address of the server to connect to\r\n * @param abort The abort signal to use for the request (for cancellation)\r\n */\r\nexport async function fetchServerInfo(serverAddress: string, abort: AbortSignal): Promise {\r\n try {\r\n const response = await fetch(`${new URL(serverAddress).origin}/amnet/info`, {\r\n signal: abort\r\n });\r\n\r\n if (!response.ok) {\r\n return null;\r\n }\r\n\r\n return {\r\n ...await response.json(),\r\n serverAddress: serverAddress\r\n } satisfies AMServerMetadata;\r\n } catch {\r\n return null;\r\n }\r\n}\r\n","export const CARD_ID_REGEX = /^(?:[0-9]{4} ?){5}$/i;\r\n\r\nexport function generateCardNumber(): string {\r\n let cardId = '';\r\n\r\n while (true) {\r\n // don't start with a 3\r\n const firstDigit = Math.floor(Math.random() * 10);\r\n\r\n if (firstDigit !== 3) {\r\n cardId = firstDigit.toString();\r\n break;\r\n }\r\n }\r\n\r\n for (let i = 0; i < 19; i++) {\r\n cardId += Math.floor(Math.random() * 10).toString();\r\n }\r\n\r\n return cardId;\r\n}\r\n\r\n/**\r\n * Validates the provided card id by known parameters\r\n * @param id The card id to validate\r\n */\r\nexport function validateCardId(id: string): boolean {\r\n return id[0] !== '3' && CARD_ID_REGEX.test(id);\r\n}\r\n","/**\r\n * Get the game name from the game identifier (4-letter code)\r\n * @param id\r\n */\r\nexport function getGameName(id: string): string | null {\r\n switch (id?.toUpperCase()) {\r\n case \"SDGT\":\r\n return \"Initial D The Arcade\";\r\n case \"SDHD\":\r\n case \"SDBT\":\r\n return \"CHUNITHM\";\r\n case \"SDDT\":\r\n return \"O.N.G.E.K.I.\";\r\n case \"SDEZ\":\r\n return \"maimai DX\";\r\n case \"SBZV\":\r\n return \"Project DIVA Future Tone\";\r\n \r\n default:\r\n return null;\r\n }\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGO,IAAM,oBAAoB;AAK1B,IAAM,wBAAwB;AA0CrC,SAAsB,cAAc,QAAgC,SAA0B,QAAuC;AAAA;AACjI,QAAI,OAAO,aAAa,yBAAyB,OAAO,aAAa,mBAAmB;AACpF,YAAM,IAAI,MAAM,gCAAgC;AAAA,IACpD;AAEA,QAAI;AACA,YAAM,WAAW,MAAM,MAAM,GAAG,IAAI,IAAI,OAAO,aAAa,EAAE,MAAM,iBAAiB;AAAA,QACjF,QAAQ;AAAA,QACR,SAAS;AAAA,UACL,gBAAgB;AAAA,QACpB;AAAA,QACA,MAAM,KAAK,UAAU,OAAO;AAAA,QAC5B;AAAA,MACJ,CAAC;AAED,aAAO,SAAS;AAAA,IACpB,SAAQ;AACJ,aAAO;AAAA,IACX;AAAA,EACJ;AAAA;AAOA,SAAsB,gBAAgB,eAAuB,OAAkD;AAAA;AAC3G,QAAI;AACA,YAAM,WAAW,MAAM,MAAM,GAAG,IAAI,IAAI,aAAa,EAAE,MAAM,eAAe;AAAA,QACxE,QAAQ;AAAA,MACZ,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AACd,eAAO;AAAA,MACX;AAEA,aAAO,iCACA,MAAM,SAAS,KAAK,IADpB;AAAA,QAEH;AAAA,MACJ;AAAA,IACJ,SAAQ;AACJ,aAAO;AAAA,IACX;AAAA,EACJ;AAAA;;;AC7FO,IAAM,gBAAgB;AAEtB,SAAS,qBAA6B;AACzC,MAAI,SAAS;AAEb,SAAO,MAAM;AAET,UAAM,aAAa,KAAK,MAAM,KAAK,OAAO,IAAI,EAAE;AAEhD,QAAI,eAAe,GAAG;AAClB,eAAS,WAAW,SAAS;AAC7B;AAAA,IACJ;AAAA,EACJ;AAEA,WAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AACzB,cAAU,KAAK,MAAM,KAAK,OAAO,IAAI,EAAE,EAAE,SAAS;AAAA,EACtD;AAEA,SAAO;AACX;AAMO,SAAS,eAAe,IAAqB;AAChD,SAAO,GAAG,CAAC,MAAM,OAAO,cAAc,KAAK,EAAE;AACjD;;;ACxBO,SAAS,YAAY,IAA2B;AACnD,UAAQ,yBAAI,eAAe;AAAA,IACvB,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AAAA,IACL,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IAEX;AACI,aAAO;AAAA,EACf;AACJ;","names":[]} \ No newline at end of file +{"version":3,"sources":["../src/api.ts","../src/card-encoding.ts","../src/card.ts","../src/gameIds.ts"],"sourcesContent":["/**\n * The latest version of the AMNet API this library supports\n */\nexport const AMNET_API_VERSION = 1;\n\n/**\n * The minimum version of the AMNet API this library supports\n */\nexport const MIN_AMNET_API_VERSION = 1;\n\nexport interface AMCardInRequest {\n cardId: string;\n physicalCardIDm?: string;\n}\n\n/**\n * Server connection info (api version, address, etc.)\n */\nexport interface AMServerConnectionInfo {\n apiVersion: number;\n serverAddress: string;\n}\n\n/**\n * User-definable server metadata\n */\nexport interface AMServerMetadata {\n serverName: string;\n gameId?: string | null | undefined;\n}\n\n/**\n * Metrics about the current game session\n */\nexport interface AMServerInstanceMetrics {\n timeSinceLastPoll?: number;\n sessionUptime?: number;\n}\n\n/**\n * The response from an AMNet server's info endpoint\n */\nexport type AMServerInfo = AMServerMetadata & AMServerConnectionInfo & AMServerInstanceMetrics;\n\n/**\n * Performs the card-in request to the specified server\n * @param server The server to perform the request against\n * @param request The card-in request to send\n * @param signal The abort signal to use for the request\n */\nexport async function performCardIn(server: AMServerConnectionInfo, request: AMCardInRequest, signal: AbortSignal): Promise {\n if (server.apiVersion < MIN_AMNET_API_VERSION || server.apiVersion > AMNET_API_VERSION) {\n throw new Error(\"Unsupported server API version\");\n }\n\n try {\n const response = await fetch(`${new URL(server.serverAddress).origin}/amnet/signin`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\"\n },\n body: JSON.stringify(request),\n signal: signal\n });\n\n return response.ok;\n } catch {\n return false;\n }\n}\n\n/**\n * Attempts to connect to the specified server and fetch its info\n * @param serverAddress The origin address of the server to connect to\n * @param abort The abort signal to use for the request (for cancellation)\n */\nexport async function fetchServerInfo(serverAddress: string, abort: AbortSignal): Promise {\n try {\n const response = await fetch(`${new URL(serverAddress).origin}/amnet/info`, {\n signal: abort\n });\n\n if (!response.ok) {\n return null;\n }\n\n return {\n ...await response.json(),\n serverAddress: serverAddress\n } satisfies AMServerMetadata;\n } catch {\n return null;\n }\n}\n","import _ from \"lodash\";\nimport bigInt, {BigInteger} from \"big-integer\";\n\nconst sbtable = [[[45, 50, 94, 2, 66, 55, 56, 49, 99, 36, 60, 0, 73, 30, 39, 37, 80, 74, 85, 7, 12, 33, 93, 83, 95, 91, 98, 92, 23, 24, 79, 21, 31, 35, 71, 70, 4, 43, 1, 5, 88, 90, 69, 58, 76, 20, 84, 62, 77, 44, 61, 11, 16, 53, 82, 34, 13, 47, 48, 87, 89, 15, 64, 3, 54, 28, 57, 67, 9, 75, 32, 65, 96, 17, 18, 29, 10, 14, 42, 6, 46, 26, 40, 59, 63, 78, 68, 8, 86, 51, 25, 72, 38, 97, 19, 81, 27, 41, 52, 22], [26, 32, 34, 79, 42, 45, 4, 75, 22, 73, 64, 98, 49, 19, 5, 24, 6, 55, 63, 25, 40, 77, 54, 12, 9, 44, 83, 71, 0, 90, 97, 96, 81, 80, 3, 31, 56, 92, 17, 86, 72, 28, 36, 78, 62, 30, 7, 46, 87, 20, 82, 50, 35, 61, 41, 94, 68, 11, 1, 10, 74, 67, 85, 43, 57, 15, 95, 84, 65, 53, 69, 52, 76, 88, 2, 14, 37, 58, 93, 16, 59, 51, 18, 99, 39, 47, 89, 8, 13, 91, 38, 33, 70, 48, 23, 29, 27, 66, 60, 21], [28, 53, 30, 85, 0, 95, 70, 47, 40, 51, 94, 68, 99, 15, 5, 57, 1, 16, 80, 93, 91, 52, 2, 32, 48, 77, 41, 75, 22, 39, 50, 81, 79, 73, 97, 76, 60, 4, 59, 25, 78, 38, 46, 58, 90, 84, 56, 6, 83, 62, 42, 10, 64, 31, 36, 61, 65, 63, 18, 8, 9, 96, 55, 26, 89, 67, 3, 98, 35, 21, 13, 12, 88, 71, 37, 29, 87, 49, 24, 86, 92, 20, 44, 74, 69, 17, 82, 54, 27, 7, 23, 66, 11, 19, 34, 45, 14, 72, 43, 33], [32, 44, 86, 79, 38, 61, 25, 8, 5, 85, 78, 48, 33, 87, 84, 35, 64, 41, 60, 21, 96, 75, 6, 69, 39, 94, 3, 92, 37, 77, 15, 13, 29, 59, 76, 91, 65, 56, 73, 83, 27, 62, 89, 50, 18, 98, 81, 17, 71, 28, 82, 11, 30, 43, 34, 20, 70, 22, 46, 63, 52, 68, 90, 53, 36, 10, 74, 58, 40, 88, 9, 2, 42, 26, 24, 4, 95, 57, 31, 66, 0, 14, 49, 16, 99, 19, 51, 23, 47, 12, 93, 1, 7, 80, 45, 97, 55, 67, 72, 54], [58, 35, 42, 38, 63, 36, 68, 30, 81, 4, 87, 80, 32, 69, 37, 67, 1, 86, 61, 94, 27, 11, 84, 26, 59, 73, 53, 66, 72, 71, 39, 33, 99, 8, 43, 88, 17, 22, 89, 5, 46, 93, 98, 48, 55, 83, 34, 3, 0, 16, 12, 20, 78, 9, 2, 76, 54, 51, 25, 24, 62, 57, 92, 10, 50, 28, 79, 23, 52, 95, 44, 60, 74, 90, 19, 77, 21, 31, 96, 49, 64, 6, 29, 85, 18, 13, 82, 47, 56, 45, 15, 7, 97, 75, 40, 41, 65, 14, 70, 91], [81, 82, 66, 72, 83, 4, 20, 94, 53, 48, 77, 41, 67, 64, 9, 24, 36, 55, 14, 1, 78, 84, 49, 0, 44, 58, 70, 25, 28, 73, 8, 68, 88, 54, 86, 87, 23, 52, 27, 5, 97, 37, 62, 50, 3, 89, 92, 47, 30, 10, 29, 80, 22, 26, 11, 13, 69, 60, 38, 90, 85, 57, 12, 75, 46, 39, 74, 33, 42, 93, 98, 91, 59, 99, 18, 6, 17, 16, 32, 40, 63, 7, 76, 15, 43, 71, 51, 35, 2, 65, 34, 96, 19, 56, 79, 21, 95, 45, 61, 31], [25, 88, 7, 57, 98, 63, 86, 13, 69, 84, 56, 20, 68, 90, 35, 6, 43, 93, 95, 3, 75, 4, 9, 28, 31, 66, 21, 44, 30, 76, 11, 22, 49, 2, 77, 40, 85, 19, 82, 16, 97, 92, 39, 96, 18, 32, 42, 94, 70, 71, 54, 79, 67, 78, 37, 12, 99, 53, 61, 41, 87, 17, 1, 14, 52, 10, 47, 58, 27, 46, 36, 55, 26, 15, 33, 50, 72, 51, 89, 23, 74, 59, 64, 8, 83, 65, 80, 29, 38, 0, 60, 24, 45, 81, 48, 91, 73, 5, 34, 62], [73, 85, 37, 11, 80, 46, 16, 47, 88, 65, 94, 64, 12, 38, 98, 70, 95, 48, 92, 18, 56, 75, 58, 7, 81, 15, 74, 13, 5, 44, 27, 32, 76, 90, 33, 55, 26, 96, 77, 24, 0, 89, 8, 4, 28, 39, 71, 3, 10, 41, 91, 53, 31, 68, 36, 17, 69, 60, 34, 54, 93, 82, 21, 51, 87, 59, 61, 6, 1, 23, 30, 66, 43, 25, 35, 42, 49, 78, 62, 14, 19, 20, 29, 22, 52, 83, 63, 45, 2, 99, 50, 79, 72, 40, 84, 97, 86, 57, 67, 9], [48, 94, 17, 55, 59, 24, 72, 75, 38, 85, 34, 98, 95, 36, 49, 63, 57, 54, 29, 6, 52, 47, 73, 1, 82, 9, 66, 83, 31, 61, 78, 60, 97, 14, 87, 15, 90, 70, 53, 10, 27, 8, 3, 64, 51, 50, 68, 26, 22, 19, 42, 96, 67, 7, 25, 39, 92, 30, 58, 93, 21, 74, 41, 18, 65, 44, 88, 32, 71, 69, 12, 77, 13, 11, 99, 79, 37, 89, 81, 35, 86, 56, 45, 76, 46, 80, 62, 43, 16, 84, 40, 5, 33, 91, 4, 20, 0, 23, 28, 2], [61, 45, 79, 13, 59, 52, 6, 77, 3, 31, 70, 80, 26, 33, 36, 4, 27, 32, 24, 74, 29, 94, 16, 12, 76, 7, 88, 57, 78, 42, 97, 96, 43, 83, 92, 21, 51, 47, 49, 58, 90, 41, 93, 98, 35, 63, 86, 37, 5, 30, 28, 72, 25, 22, 60, 19, 39, 34, 68, 50, 8, 64, 46, 99, 84, 87, 69, 56, 53, 55, 44, 54, 89, 65, 85, 0, 71, 15, 17, 66, 81, 82, 91, 95, 62, 48, 38, 75, 40, 14, 9, 10, 73, 67, 18, 11, 23, 2, 20, 1]], [[95, 90, 61, 38, 18, 7, 83, 39, 79, 14, 48, 21, 81, 44, 78, 50, 56, 91, 12, 86, 99, 98, 43, 63, 73, 87, 42, 72, 15, 68, 97, 23, 88, 69, 2, 51, 19, 58, 59, 22, 20, 29, 82, 11, 75, 70, 9, 71, 27, 30, 47, 89, 92, 60, 46, 76, 93, 54, 1, 96, 94, 57, 40, 62, 41, 3, 28, 64, 37, 74, 67, 16, 65, 53, 5, 36, 24, 45, 32, 33, 10, 85, 26, 4, 84, 77, 35, 13, 6, 8, 17, 80, 25, 55, 52, 66, 49, 34, 0, 31], [18, 14, 70, 43, 74, 45, 94, 36, 40, 6, 31, 46, 15, 47, 13, 19, 72, 61, 99, 21, 17, 92, 88, 78, 73, 90, 0, 96, 20, 28, 25, 51, 50, 77, 29, 16, 54, 12, 58, 38, 71, 23, 42, 83, 65, 60, 10, 33, 22, 76, 95, 32, 57, 5, 59, 67, 68, 3, 91, 85, 26, 62, 41, 1, 53, 82, 34, 24, 87, 27, 89, 75, 56, 30, 81, 7, 8, 4, 64, 66, 9, 63, 35, 52, 80, 93, 69, 84, 55, 86, 48, 98, 79, 97, 49, 44, 39, 11, 37, 2], [76, 16, 73, 29, 34, 14, 36, 90, 58, 70, 46, 2, 91, 59, 63, 32, 84, 40, 78, 38, 19, 37, 0, 31, 8, 44, 94, 39, 10, 66, 42, 1, 21, 88, 82, 97, 33, 4, 99, 74, 89, 62, 25, 87, 77, 43, 50, 95, 64, 23, 47, 93, 65, 27, 12, 71, 9, 72, 60, 18, 7, 22, 79, 24, 96, 45, 49, 98, 57, 11, 5, 86, 3, 80, 26, 51, 17, 85, 54, 67, 48, 35, 53, 41, 28, 13, 20, 92, 83, 69, 6, 68, 75, 52, 61, 30, 81, 56, 15, 55], [0, 62, 25, 61, 56, 87, 54, 27, 75, 49, 18, 69, 17, 71, 40, 6, 90, 80, 23, 26, 68, 24, 10, 21, 47, 88, 36, 55, 15, 22, 48, 12, 83, 99, 46, 67, 59, 29, 58, 73, 78, 97, 19, 33, 51, 98, 65, 91, 16, 4, 63, 41, 3, 70, 30, 8, 89, 34, 72, 60, 9, 95, 50, 2, 66, 53, 44, 85, 28, 5, 92, 43, 64, 96, 74, 77, 45, 52, 14, 42, 1, 32, 20, 76, 86, 82, 94, 7, 39, 13, 31, 11, 84, 93, 35, 37, 38, 57, 79, 81], [78, 8, 59, 25, 69, 15, 63, 3, 57, 5, 50, 96, 41, 66, 80, 81, 7, 45, 60, 74, 79, 32, 27, 58, 56, 82, 11, 47, 17, 85, 53, 40, 95, 84, 76, 92, 18, 71, 64, 51, 67, 91, 22, 34, 65, 31, 73, 93, 68, 75, 77, 37, 43, 12, 20, 99, 2, 1, 19, 35, 44, 55, 90, 9, 0, 6, 33, 89, 24, 14, 97, 10, 48, 61, 23, 54, 98, 86, 88, 94, 46, 70, 28, 36, 16, 30, 4, 39, 26, 38, 87, 52, 83, 49, 62, 42, 72, 29, 13, 21], [20, 88, 35, 5, 85, 98, 11, 78, 62, 83, 79, 80, 40, 14, 91, 8, 7, 69, 71, 61, 49, 38, 64, 82, 0, 47, 84, 95, 2, 50, 77, 41, 97, 36, 33, 81, 46, 21, 73, 48, 31, 92, 54, 89, 87, 60, 90, 10, 76, 45, 99, 52, 3, 44, 39, 43, 18, 16, 63, 12, 28, 70, 86, 6, 4, 24, 59, 30, 96, 65, 22, 56, 74, 23, 32, 75, 53, 93, 15, 67, 66, 17, 29, 25, 42, 37, 57, 9, 72, 68, 26, 34, 27, 51, 19, 58, 55, 94, 13, 1], [72, 53, 79, 61, 30, 33, 25, 75, 8, 55, 14, 6, 22, 93, 18, 77, 4, 74, 44, 36, 82, 94, 2, 5, 62, 54, 28, 3, 63, 97, 65, 7, 84, 73, 39, 92, 67, 91, 24, 45, 15, 81, 19, 78, 80, 64, 34, 43, 32, 87, 52, 95, 89, 37, 10, 23, 20, 68, 42, 47, 59, 58, 49, 71, 40, 85, 13, 31, 12, 57, 96, 90, 56, 41, 48, 9, 99, 35, 98, 26, 1, 46, 11, 69, 27, 83, 60, 50, 21, 70, 0, 88, 29, 86, 76, 16, 66, 38, 51, 17], [3, 68, 86, 94, 81, 90, 85, 32, 65, 52, 50, 42, 80, 78, 26, 93, 57, 58, 69, 82, 92, 27, 11, 67, 95, 41, 21, 88, 35, 17, 19, 15, 22, 62, 40, 76, 63, 59, 37, 55, 75, 9, 24, 89, 56, 53, 23, 60, 25, 6, 1, 71, 16, 7, 10, 44, 8, 12, 87, 64, 54, 72, 20, 33, 70, 79, 14, 97, 4, 99, 45, 39, 2, 77, 98, 74, 49, 30, 13, 61, 66, 73, 47, 43, 96, 83, 18, 91, 28, 48, 38, 34, 31, 36, 29, 0, 84, 46, 5, 51], [71, 84, 25, 70, 77, 56, 61, 47, 72, 27, 54, 89, 9, 46, 95, 59, 42, 98, 63, 69, 48, 16, 97, 14, 66, 83, 87, 68, 17, 2, 15, 79, 31, 1, 57, 12, 33, 36, 0, 40, 21, 50, 76, 41, 10, 13, 44, 5, 53, 4, 38, 35, 55, 96, 81, 91, 90, 64, 34, 51, 6, 28, 32, 39, 75, 18, 93, 24, 78, 7, 52, 43, 49, 37, 74, 8, 58, 65, 92, 67, 85, 20, 94, 88, 80, 86, 19, 60, 3, 11, 30, 45, 29, 22, 26, 73, 99, 23, 62, 82], [67, 91, 46, 33, 76, 3, 96, 23, 9, 68, 49, 63, 1, 92, 58, 62, 69, 77, 70, 75, 71, 94, 51, 55, 50, 52, 0, 88, 18, 16, 73, 6, 12, 87, 93, 79, 84, 57, 48, 41, 20, 47, 15, 42, 54, 81, 7, 30, 34, 56, 29, 8, 98, 5, 45, 64, 83, 74, 22, 82, 80, 95, 2, 72, 44, 31, 86, 78, 13, 10, 28, 89, 60, 35, 61, 19, 14, 85, 4, 43, 11, 40, 26, 99, 39, 32, 90, 17, 24, 37, 36, 21, 25, 53, 97, 59, 27, 38, 65, 66]], [[40, 77, 66, 65, 97, 96, 58, 64, 69, 30, 0, 3, 17, 33, 51, 23, 59, 10, 7, 20, 29, 49, 71, 13, 46, 4, 79, 92, 47, 18, 72, 57, 73, 80, 56, 28, 78, 67, 9, 87, 43, 31, 22, 62, 75, 99, 19, 63, 35, 93, 12, 98, 25, 26, 37, 89, 86, 15, 55, 50, 91, 38, 42, 81, 2, 94, 70, 68, 45, 88, 76, 74, 11, 24, 41, 60, 85, 21, 44, 32, 82, 61, 52, 83, 53, 36, 48, 1, 95, 27, 14, 84, 54, 6, 39, 34, 90, 5, 16, 8], [44, 69, 53, 28, 18, 11, 23, 65, 96, 25, 93, 42, 72, 58, 95, 56, 32, 21, 78, 77, 51, 34, 47, 7, 66, 13, 88, 67, 92, 98, 10, 50, 90, 45, 49, 9, 99, 94, 22, 24, 5, 85, 33, 3, 73, 35, 52, 63, 2, 76, 87, 43, 81, 83, 55, 61, 70, 75, 16, 14, 8, 71, 57, 40, 86, 39, 80, 79, 26, 48, 59, 4, 82, 0, 36, 12, 54, 38, 64, 17, 91, 27, 62, 84, 37, 74, 68, 1, 6, 19, 29, 46, 60, 30, 89, 15, 20, 97, 41, 31], [53, 22, 34, 16, 72, 71, 14, 94, 12, 59, 39, 6, 38, 68, 21, 93, 32, 46, 69, 55, 10, 8, 75, 60, 9, 13, 99, 90, 63, 49, 28, 92, 41, 7, 54, 83, 27, 91, 47, 43, 31, 74, 42, 40, 18, 81, 76, 84, 78, 52, 1, 26, 51, 2, 58, 11, 65, 67, 15, 23, 0, 79, 96, 4, 29, 62, 89, 66, 50, 98, 56, 61, 30, 88, 35, 25, 77, 70, 3, 44, 95, 80, 19, 64, 57, 37, 82, 85, 73, 48, 5, 86, 24, 36, 33, 97, 20, 45, 17, 87], [95, 36, 70, 82, 23, 55, 13, 51, 83, 38, 40, 18, 84, 74, 56, 85, 58, 14, 19, 69, 34, 59, 97, 89, 52, 77, 9, 91, 68, 16, 27, 88, 6, 73, 43, 5, 10, 66, 4, 61, 44, 46, 72, 50, 96, 21, 33, 15, 32, 0, 63, 92, 24, 39, 29, 49, 30, 64, 12, 26, 67, 53, 37, 2, 47, 65, 3, 20, 75, 78, 99, 87, 98, 1, 81, 90, 62, 54, 22, 35, 45, 25, 86, 48, 17, 60, 94, 8, 42, 31, 76, 28, 11, 57, 93, 41, 7, 71, 80, 79], [60, 24, 69, 92, 15, 36, 11, 98, 93, 27, 51, 35, 89, 74, 87, 4, 81, 94, 23, 84, 56, 3, 42, 18, 75, 73, 46, 86, 55, 99, 0, 88, 1, 26, 76, 70, 65, 67, 20, 13, 7, 45, 77, 17, 9, 78, 54, 48, 32, 40, 47, 31, 2, 6, 90, 10, 8, 50, 52, 38, 91, 14, 53, 12, 85, 44, 21, 80, 68, 33, 30, 57, 61, 63, 79, 41, 29, 39, 58, 5, 62, 96, 28, 71, 82, 34, 25, 43, 22, 95, 72, 37, 64, 16, 97, 59, 66, 83, 49, 19], [80, 0, 40, 36, 68, 18, 67, 14, 20, 21, 10, 17, 43, 3, 98, 81, 94, 55, 1, 39, 72, 51, 41, 66, 58, 2, 61, 78, 15, 99, 57, 52, 25, 87, 12, 65, 74, 64, 34, 37, 44, 30, 88, 79, 33, 27, 56, 35, 73, 42, 29, 54, 24, 5, 53, 16, 31, 4, 19, 84, 59, 50, 26, 63, 48, 28, 60, 45, 92, 82, 49, 77, 97, 76, 22, 86, 47, 62, 83, 70, 93, 38, 11, 13, 89, 75, 85, 69, 9, 46, 96, 90, 6, 91, 8, 7, 32, 71, 95, 23], [32, 14, 35, 84, 89, 11, 52, 34, 79, 65, 2, 88, 49, 29, 31, 94, 1, 74, 97, 51, 99, 37, 10, 47, 57, 81, 73, 76, 63, 58, 0, 38, 39, 27, 33, 8, 20, 21, 46, 66, 92, 36, 6, 30, 53, 17, 48, 64, 55, 28, 85, 50, 54, 12, 80, 41, 77, 69, 93, 9, 91, 13, 42, 75, 68, 86, 19, 3, 16, 78, 87, 45, 43, 56, 59, 70, 40, 24, 83, 98, 62, 5, 96, 4, 67, 60, 26, 23, 72, 95, 44, 82, 90, 25, 7, 18, 15, 22, 71, 61], [78, 53, 89, 93, 7, 84, 81, 95, 64, 50, 58, 35, 59, 63, 20, 76, 80, 8, 23, 28, 79, 52, 55, 48, 66, 17, 60, 65, 25, 29, 30, 9, 73, 39, 69, 3, 98, 22, 15, 87, 4, 37, 43, 5, 11, 83, 91, 40, 51, 33, 12, 92, 6, 67, 41, 94, 45, 88, 56, 36, 0, 68, 19, 54, 70, 26, 72, 77, 32, 34, 97, 71, 99, 75, 61, 31, 86, 82, 46, 2, 44, 96, 49, 74, 10, 62, 24, 16, 1, 38, 27, 14, 18, 85, 13, 57, 90, 21, 42, 47], [26, 30, 60, 78, 75, 48, 73, 52, 51, 57, 63, 90, 27, 36, 83, 81, 41, 44, 82, 37, 10, 64, 91, 4, 59, 95, 21, 24, 77, 56, 43, 38, 62, 55, 92, 0, 94, 20, 86, 66, 35, 80, 53, 25, 17, 18, 49, 76, 23, 89, 13, 15, 68, 8, 22, 46, 1, 42, 3, 34, 98, 88, 69, 31, 50, 65, 84, 32, 47, 61, 9, 19, 99, 85, 11, 79, 70, 2, 74, 14, 87, 7, 72, 5, 12, 58, 67, 39, 16, 71, 54, 6, 45, 93, 96, 28, 97, 29, 33, 40], [77, 66, 20, 30, 42, 23, 0, 49, 7, 64, 54, 27, 96, 97, 39, 84, 2, 51, 71, 17, 4, 92, 94, 95, 67, 69, 73, 75, 86, 37, 74, 50, 11, 79, 29, 15, 56, 43, 22, 83, 81, 61, 14, 3, 36, 44, 58, 10, 62, 19, 72, 80, 65, 24, 18, 5, 40, 90, 33, 9, 47, 76, 8, 59, 35, 91, 99, 13, 70, 88, 55, 16, 31, 28, 85, 45, 60, 25, 87, 82, 93, 12, 34, 68, 63, 26, 46, 48, 21, 52, 1, 78, 98, 38, 6, 32, 89, 53, 41, 57]], [[77, 99, 47, 64, 26, 63, 50, 81, 23, 45, 49, 36, 43, 56, 39, 12, 86, 62, 2, 98, 60, 94, 75, 9, 20, 74, 48, 28, 4, 84, 61, 31, 54, 15, 40, 7, 87, 38, 80, 6, 37, 46, 1, 55, 88, 73, 95, 33, 92, 18, 25, 66, 57, 27, 42, 35, 91, 51, 13, 90, 41, 83, 68, 53, 19, 3, 72, 89, 65, 70, 71, 8, 97, 59, 96, 52, 79, 22, 14, 69, 32, 93, 58, 29, 21, 76, 34, 5, 17, 67, 11, 78, 16, 0, 44, 30, 10, 24, 85, 82], [58, 62, 7, 39, 34, 69, 67, 73, 37, 59, 71, 66, 46, 57, 44, 51, 26, 85, 11, 28, 54, 47, 82, 21, 83, 76, 94, 16, 90, 95, 75, 55, 6, 22, 72, 74, 50, 40, 10, 19, 8, 53, 33, 98, 45, 87, 91, 13, 35, 70, 14, 38, 84, 99, 56, 30, 12, 89, 25, 97, 63, 65, 81, 92, 18, 29, 80, 61, 93, 43, 49, 52, 23, 31, 32, 4, 48, 3, 0, 78, 96, 64, 41, 88, 24, 5, 9, 15, 77, 86, 68, 1, 17, 79, 60, 36, 42, 2, 20, 27], [58, 62, 7, 39, 34, 69, 67, 73, 37, 59, 71, 66, 46, 57, 44, 51, 26, 85, 11, 28, 54, 47, 82, 21, 83, 76, 94, 16, 90, 95, 75, 55, 6, 22, 72, 74, 50, 40, 10, 19, 8, 53, 33, 98, 45, 87, 91, 13, 35, 70, 14, 38, 84, 99, 56, 30, 12, 89, 25, 97, 63, 65, 81, 92, 18, 29, 80, 61, 93, 43, 49, 52, 23, 31, 32, 4, 48, 3, 0, 78, 96, 64, 41, 88, 24, 5, 9, 15, 77, 86, 68, 1, 17, 79, 60, 36, 42, 2, 20, 27], [91, 43, 77, 69, 16, 96, 47, 34, 49, 15, 73, 84, 85, 22, 88, 95, 2, 80, 57, 33, 32, 41, 25, 14, 58, 9, 20, 65, 48, 97, 79, 89, 66, 63, 46, 83, 19, 92, 55, 18, 38, 0, 21, 50, 45, 62, 29, 26, 52, 28, 8, 6, 75, 86, 44, 81, 36, 90, 70, 30, 71, 39, 82, 87, 76, 1, 23, 5, 60, 98, 67, 53, 93, 72, 42, 74, 51, 4, 31, 59, 61, 7, 24, 56, 37, 54, 3, 11, 27, 40, 10, 64, 94, 35, 78, 68, 99, 17, 12, 13], [73, 70, 39, 9, 77, 61, 80, 10, 13, 92, 38, 4, 48, 57, 25, 72, 49, 51, 99, 97, 85, 36, 22, 15, 8, 34, 33, 76, 26, 45, 83, 59, 28, 96, 90, 47, 68, 88, 44, 6, 98, 12, 24, 46, 2, 54, 5, 58, 30, 63, 1, 18, 93, 67, 7, 50, 31, 16, 40, 56, 23, 86, 11, 52, 55, 75, 71, 27, 37, 87, 84, 65, 82, 29, 0, 14, 41, 35, 64, 21, 3, 95, 17, 60, 81, 74, 19, 91, 94, 32, 69, 53, 42, 89, 79, 43, 66, 20, 78, 62], [23, 78, 67, 41, 9, 77, 59, 72, 25, 82, 65, 89, 28, 37, 54, 70, 98, 33, 31, 44, 6, 17, 26, 73, 21, 36, 4, 2, 79, 95, 81, 50, 18, 34, 61, 0, 93, 24, 8, 66, 55, 62, 56, 30, 84, 58, 7, 88, 85, 64, 97, 39, 49, 3, 20, 42, 80, 96, 13, 63, 43, 60, 47, 90, 51, 14, 53, 1, 86, 57, 12, 19, 10, 45, 38, 68, 35, 87, 75, 5, 69, 27, 32, 91, 11, 99, 46, 29, 92, 22, 76, 15, 16, 48, 71, 74, 52, 94, 83, 40], [87, 89, 35, 98, 44, 21, 29, 83, 0, 77, 34, 60, 85, 64, 28, 58, 39, 36, 95, 22, 67, 30, 86, 57, 5, 81, 84, 26, 53, 62, 13, 90, 2, 88, 4, 46, 97, 73, 79, 43, 59, 72, 54, 32, 82, 10, 55, 38, 47, 12, 51, 1, 76, 48, 7, 14, 23, 20, 31, 18, 17, 15, 37, 42, 49, 68, 25, 93, 91, 40, 92, 50, 52, 70, 33, 80, 19, 96, 61, 75, 71, 99, 74, 9, 41, 66, 63, 8, 6, 24, 65, 56, 45, 69, 27, 16, 3, 78, 94, 11], [99, 6, 38, 11, 34, 28, 86, 57, 55, 78, 25, 95, 13, 63, 91, 14, 83, 1, 45, 17, 93, 76, 46, 79, 27, 85, 96, 58, 59, 44, 87, 65, 97, 22, 0, 51, 49, 52, 47, 67, 24, 88, 62, 50, 72, 3, 31, 82, 68, 42, 81, 23, 32, 98, 73, 60, 37, 54, 41, 80, 66, 18, 92, 4, 5, 94, 77, 39, 69, 16, 35, 7, 40, 30, 61, 26, 56, 64, 9, 71, 19, 21, 75, 84, 43, 53, 33, 29, 74, 36, 90, 2, 20, 15, 10, 48, 89, 70, 8, 12], [4, 28, 8, 72, 74, 31, 66, 0, 27, 54, 61, 23, 51, 73, 96, 32, 98, 41, 88, 95, 36, 22, 13, 38, 70, 84, 12, 7, 10, 60, 68, 33, 11, 35, 79, 42, 16, 62, 57, 24, 44, 94, 25, 48, 86, 97, 87, 49, 92, 71, 21, 1, 81, 5, 17, 85, 6, 75, 80, 34, 20, 30, 90, 69, 18, 89, 93, 56, 91, 40, 77, 52, 53, 14, 65, 45, 76, 2, 9, 29, 3, 26, 59, 55, 83, 46, 67, 47, 39, 50, 58, 63, 99, 15, 37, 43, 82, 64, 78, 19], [11, 94, 1, 93, 59, 91, 89, 51, 61, 81, 13, 29, 27, 58, 26, 3, 53, 69, 73, 21, 39, 12, 5, 18, 98, 74, 9, 96, 28, 65, 23, 4, 41, 72, 88, 34, 83, 54, 7, 15, 38, 84, 90, 52, 77, 48, 64, 60, 20, 79, 19, 66, 97, 30, 87, 45, 40, 36, 14, 86, 32, 33, 57, 16, 43, 42, 50, 10, 44, 76, 37, 6, 56, 31, 68, 99, 2, 17, 22, 35, 92, 85, 46, 75, 71, 67, 95, 78, 8, 63, 82, 25, 80, 24, 47, 49, 0, 70, 62, 55]], [[93, 99, 51, 49, 71, 64, 35, 42, 88, 17, 67, 92, 4, 12, 68, 13, 47, 7, 56, 14, 19, 53, 80, 85, 23, 5, 39, 6, 26, 89, 27, 54, 63, 73, 70, 33, 36, 3, 86, 58, 18, 2, 25, 21, 10, 34, 81, 94, 75, 98, 37, 46, 9, 95, 60, 74, 87, 78, 20, 24, 57, 0, 30, 83, 72, 61, 11, 32, 43, 82, 65, 69, 59, 62, 38, 76, 22, 16, 15, 45, 48, 79, 50, 90, 40, 1, 29, 77, 91, 31, 84, 96, 8, 52, 97, 41, 28, 44, 55, 66], [64, 71, 19, 27, 43, 95, 77, 16, 35, 81, 74, 29, 6, 86, 18, 37, 99, 46, 15, 87, 28, 63, 1, 65, 3, 79, 50, 12, 78, 84, 8, 88, 98, 69, 42, 0, 56, 32, 92, 83, 30, 59, 80, 9, 20, 89, 47, 23, 33, 34, 82, 24, 97, 60, 22, 90, 51, 76, 10, 94, 21, 91, 70, 49, 66, 14, 75, 2, 7, 13, 40, 93, 48, 36, 53, 72, 67, 62, 85, 17, 57, 58, 5, 25, 41, 31, 44, 73, 61, 26, 38, 54, 96, 39, 4, 68, 11, 52, 45, 55], [49, 88, 62, 73, 34, 15, 95, 68, 98, 5, 61, 35, 23, 45, 78, 64, 97, 70, 96, 65, 74, 16, 94, 37, 80, 56, 48, 28, 59, 1, 75, 4, 71, 47, 83, 93, 69, 21, 60, 38, 39, 19, 40, 82, 55, 7, 52, 18, 43, 54, 2, 77, 66, 90, 44, 32, 89, 3, 24, 6, 99, 17, 86, 29, 30, 22, 50, 81, 41, 42, 0, 9, 53, 58, 57, 25, 20, 12, 26, 51, 92, 87, 36, 31, 85, 63, 14, 8, 91, 11, 79, 27, 76, 84, 67, 33, 72, 13, 10, 46], [45, 15, 69, 88, 51, 28, 34, 80, 30, 42, 38, 90, 47, 17, 84, 49, 21, 55, 46, 96, 37, 97, 72, 85, 94, 29, 10, 66, 76, 82, 18, 86, 87, 25, 43, 92, 19, 89, 2, 93, 98, 40, 48, 53, 75, 99, 11, 62, 12, 60, 3, 24, 4, 6, 63, 81, 41, 20, 68, 9, 57, 52, 0, 44, 14, 16, 91, 27, 79, 73, 59, 61, 78, 7, 67, 1, 70, 83, 74, 64, 26, 95, 54, 65, 33, 39, 5, 50, 36, 35, 13, 23, 58, 71, 32, 77, 31, 22, 56, 8], [41, 43, 66, 69, 22, 23, 71, 2, 33, 38, 86, 51, 55, 59, 82, 96, 45, 97, 73, 34, 70, 21, 8, 58, 84, 90, 24, 25, 32, 78, 17, 27, 47, 68, 37, 72, 63, 77, 87, 89, 50, 16, 83, 54, 12, 56, 64, 62, 39, 92, 91, 53, 29, 76, 52, 94, 42, 81, 20, 98, 40, 79, 44, 10, 31, 0, 57, 19, 75, 67, 93, 6, 7, 11, 74, 26, 85, 5, 1, 49, 4, 61, 95, 3, 36, 9, 14, 13, 15, 48, 80, 46, 30, 65, 99, 18, 60, 88, 35, 28], [5, 56, 10, 81, 42, 35, 12, 94, 34, 13, 8, 0, 49, 17, 7, 69, 30, 1, 65, 14, 39, 2, 58, 76, 54, 31, 50, 46, 11, 27, 22, 61, 74, 64, 91, 18, 48, 63, 71, 57, 95, 87, 47, 67, 77, 53, 88, 23, 3, 83, 29, 86, 43, 60, 70, 78, 21, 99, 36, 66, 20, 62, 85, 4, 80, 98, 79, 82, 97, 15, 96, 52, 51, 6, 40, 93, 28, 41, 72, 45, 68, 16, 32, 75, 84, 89, 26, 55, 9, 59, 73, 24, 25, 33, 37, 19, 90, 44, 38, 92], [62, 28, 97, 69, 65, 46, 27, 96, 29, 73, 89, 42, 16, 36, 44, 63, 80, 60, 11, 54, 87, 92, 75, 81, 57, 26, 70, 49, 76, 10, 95, 84, 56, 22, 20, 9, 21, 61, 1, 47, 34, 19, 66, 0, 33, 91, 45, 3, 2, 90, 88, 43, 51, 12, 7, 18, 24, 85, 86, 8, 78, 38, 55, 30, 6, 35, 15, 93, 14, 99, 98, 82, 52, 74, 4, 50, 79, 37, 77, 17, 41, 71, 58, 53, 68, 39, 67, 25, 5, 83, 94, 13, 48, 32, 23, 59, 31, 64, 72, 40], [93, 20, 18, 22, 94, 27, 39, 59, 4, 11, 6, 75, 98, 62, 63, 81, 44, 53, 25, 95, 74, 57, 76, 5, 70, 60, 99, 12, 56, 85, 41, 67, 46, 19, 28, 1, 23, 80, 32, 68, 31, 51, 82, 55, 78, 77, 58, 69, 38, 87, 91, 79, 96, 52, 47, 73, 83, 89, 65, 3, 92, 14, 43, 66, 61, 37, 84, 54, 33, 88, 35, 71, 42, 90, 72, 50, 7, 40, 0, 24, 9, 29, 49, 45, 15, 21, 86, 64, 16, 36, 34, 17, 26, 10, 2, 13, 8, 97, 30, 48], [33, 3, 7, 64, 74, 23, 42, 25, 68, 85, 5, 8, 83, 17, 56, 79, 70, 73, 15, 6, 36, 18, 94, 97, 72, 22, 11, 96, 40, 58, 66, 49, 62, 86, 78, 67, 26, 98, 90, 48, 14, 2, 82, 91, 9, 76, 69, 44, 50, 47, 53, 71, 92, 12, 59, 37, 19, 60, 39, 13, 77, 16, 55, 41, 84, 31, 65, 27, 99, 51, 4, 45, 80, 46, 57, 21, 28, 81, 54, 29, 61, 88, 52, 43, 75, 20, 1, 10, 0, 35, 30, 32, 95, 38, 63, 87, 89, 24, 34, 93], [37, 25, 50, 66, 86, 23, 76, 27, 83, 97, 89, 35, 32, 41, 33, 87, 14, 59, 48, 61, 52, 3, 73, 67, 24, 56, 93, 22, 7, 70, 77, 28, 91, 30, 68, 29, 60, 8, 94, 13, 43, 17, 88, 57, 81, 34, 15, 95, 79, 96, 72, 84, 55, 46, 58, 10, 12, 38, 11, 99, 53, 42, 65, 44, 31, 21, 82, 6, 92, 26, 51, 5, 39, 63, 40, 9, 49, 18, 78, 80, 71, 74, 90, 2, 54, 47, 85, 75, 69, 45, 0, 16, 19, 1, 64, 98, 4, 62, 36, 20]]];\nconst sbtable2 = [[3, 5, 8, 6, 0, 9, 7, 1, 2, 4], [1, 3, 9, 4, 2, 0, 8, 7, 5, 6], [0, 5, 1, 7, 9, 8, 6, 2, 3, 4], [6, 4, 7, 8, 3, 0, 9, 5, 2, 1], [2, 3, 8, 1, 9, 7, 6, 0, 4, 5], [3, 4, 1, 7, 2, 8, 6, 0, 9, 5], [6, 1, 8, 3, 0, 4, 2, 7, 5, 9], [9, 3, 7, 5, 2, 4, 0, 6, 1, 8], [6, 1, 5, 3, 0, 8, 7, 4, 2, 9], [9, 7, 4, 5, 1, 2, 3, 0, 8, 6]];\n\nconst ibtable = [[[11, 38, 3, 63, 36, 39, 79, 19, 87, 68, 76, 51, 20, 56, 77, 61, 52, 73, 74, 94, 45, 31, 99, 28, 29, 90, 81, 96, 65, 75, 13, 32, 70, 21, 55, 33, 9, 15, 92, 14, 82, 97, 78, 37, 49, 0, 80, 57, 58, 7, 1, 89, 98, 53, 64, 5, 6, 66, 43, 83, 10, 50, 47, 84, 62, 71, 4, 67, 86, 42, 35, 34, 91, 12, 17, 69, 44, 48, 85, 30, 16, 95, 54, 23, 46, 18, 88, 59, 40, 60, 41, 25, 27, 22, 2, 24, 72, 93, 26, 8], [28, 58, 74, 34, 6, 14, 16, 46, 87, 24, 59, 57, 23, 88, 75, 65, 79, 38, 82, 13, 49, 99, 8, 94, 15, 19, 0, 96, 41, 95, 45, 35, 1, 91, 2, 52, 42, 76, 90, 84, 20, 54, 4, 63, 25, 5, 47, 85, 93, 12, 51, 81, 71, 69, 22, 17, 36, 64, 77, 80, 98, 53, 44, 18, 10, 68, 97, 61, 56, 70, 92, 27, 40, 9, 60, 7, 72, 21, 43, 3, 33, 32, 50, 26, 67, 62, 39, 48, 73, 86, 29, 89, 37, 78, 55, 66, 31, 30, 11, 83], [4, 16, 22, 66, 37, 14, 47, 89, 59, 60, 51, 92, 71, 70, 96, 13, 17, 85, 58, 93, 81, 69, 28, 90, 78, 39, 63, 88, 0, 75, 2, 53, 23, 99, 94, 68, 54, 74, 41, 29, 8, 26, 50, 98, 82, 95, 42, 7, 24, 77, 30, 9, 21, 1, 87, 62, 46, 15, 43, 38, 36, 55, 49, 57, 52, 56, 91, 65, 11, 84, 6, 73, 97, 33, 83, 27, 35, 25, 40, 32, 18, 31, 86, 48, 45, 3, 79, 76, 72, 64, 44, 20, 80, 19, 10, 5, 61, 34, 67, 12], [80, 91, 71, 26, 75, 8, 22, 92, 7, 70, 65, 51, 89, 31, 81, 30, 83, 47, 44, 85, 55, 19, 57, 87, 74, 6, 73, 40, 49, 32, 52, 78, 0, 12, 54, 15, 64, 28, 4, 24, 68, 17, 72, 53, 1, 94, 58, 88, 11, 82, 43, 86, 60, 63, 99, 96, 37, 77, 67, 33, 18, 5, 41, 59, 16, 36, 79, 97, 61, 23, 56, 48, 98, 38, 66, 21, 34, 29, 10, 3, 93, 46, 50, 39, 14, 9, 2, 13, 69, 42, 62, 35, 27, 90, 25, 76, 20, 95, 45, 84], [48, 16, 54, 47, 9, 39, 81, 91, 33, 53, 63, 21, 50, 85, 97, 90, 49, 36, 84, 74, 51, 76, 37, 67, 59, 58, 23, 20, 65, 82, 7, 77, 12, 31, 46, 1, 5, 14, 3, 30, 94, 95, 2, 34, 70, 89, 40, 87, 43, 79, 64, 57, 68, 26, 56, 44, 88, 61, 0, 24, 71, 18, 60, 4, 80, 96, 27, 15, 6, 13, 98, 29, 28, 25, 72, 93, 55, 75, 52, 66, 11, 8, 86, 45, 22, 83, 17, 10, 35, 38, 73, 99, 62, 41, 19, 69, 78, 92, 42, 32], [23, 19, 88, 44, 5, 39, 75, 81, 30, 14, 49, 54, 62, 55, 18, 83, 77, 76, 74, 92, 6, 95, 52, 36, 15, 27, 53, 38, 28, 50, 48, 99, 78, 67, 90, 87, 16, 41, 58, 65, 79, 11, 68, 84, 24, 97, 64, 47, 9, 22, 43, 86, 37, 8, 33, 17, 93, 61, 25, 72, 57, 98, 42, 80, 13, 89, 2, 12, 31, 56, 26, 85, 3, 29, 66, 63, 82, 10, 20, 94, 51, 0, 1, 4, 21, 60, 34, 35, 32, 45, 59, 71, 46, 69, 7, 96, 91, 40, 70, 73], [89, 62, 33, 19, 21, 97, 15, 2, 83, 22, 65, 30, 55, 7, 63, 73, 39, 61, 44, 37, 11, 26, 31, 79, 91, 0, 72, 68, 23, 87, 28, 24, 45, 74, 98, 14, 70, 54, 88, 42, 35, 59, 46, 16, 27, 92, 69, 66, 94, 32, 75, 77, 64, 57, 50, 71, 10, 3, 67, 81, 90, 58, 99, 5, 82, 85, 25, 52, 12, 8, 48, 49, 76, 96, 80, 20, 29, 34, 53, 51, 86, 93, 38, 84, 9, 36, 6, 60, 1, 78, 13, 95, 41, 17, 47, 18, 43, 40, 4, 56], [40, 68, 88, 47, 43, 28, 67, 23, 42, 99, 48, 3, 12, 27, 79, 25, 6, 55, 19, 80, 81, 62, 83, 69, 39, 73, 36, 30, 44, 82, 70, 52, 31, 34, 58, 74, 54, 2, 13, 45, 93, 49, 75, 72, 29, 87, 5, 7, 17, 76, 90, 63, 84, 51, 59, 35, 20, 97, 22, 65, 57, 66, 78, 86, 11, 9, 71, 98, 53, 56, 15, 46, 92, 0, 26, 21, 32, 38, 77, 91, 4, 24, 61, 85, 94, 1, 96, 64, 8, 41, 33, 50, 18, 60, 10, 16, 37, 95, 14, 89], [96, 23, 99, 42, 94, 91, 19, 53, 41, 25, 39, 73, 70, 72, 33, 35, 88, 2, 63, 49, 95, 60, 48, 97, 5, 54, 47, 40, 98, 18, 57, 28, 67, 92, 10, 79, 13, 76, 8, 55, 90, 62, 50, 87, 65, 82, 84, 21, 0, 14, 45, 44, 20, 38, 17, 3, 81, 16, 58, 4, 31, 29, 86, 15, 43, 64, 26, 52, 46, 69, 37, 68, 6, 22, 61, 7, 83, 71, 30, 75, 85, 78, 24, 27, 89, 9, 80, 34, 66, 77, 36, 93, 56, 59, 1, 12, 51, 32, 11, 74], [75, 99, 97, 8, 15, 48, 6, 25, 60, 90, 91, 95, 23, 3, 89, 77, 22, 78, 94, 55, 98, 35, 53, 96, 18, 52, 12, 16, 50, 20, 49, 9, 17, 13, 57, 44, 14, 47, 86, 56, 88, 41, 29, 32, 70, 1, 62, 37, 85, 38, 59, 36, 5, 68, 71, 69, 67, 27, 39, 4, 54, 0, 84, 45, 61, 73, 79, 93, 58, 66, 10, 76, 51, 92, 19, 87, 24, 7, 28, 2, 11, 80, 81, 33, 64, 74, 46, 65, 26, 72, 40, 82, 34, 42, 21, 83, 31, 30, 43, 63]], [[98, 58, 34, 65, 83, 74, 88, 5, 89, 46, 80, 43, 18, 87, 9, 28, 71, 90, 4, 36, 40, 11, 39, 31, 76, 92, 82, 48, 66, 41, 49, 99, 78, 79, 97, 86, 75, 68, 3, 7, 62, 64, 26, 22, 13, 77, 54, 50, 10, 96, 15, 35, 94, 73, 57, 93, 16, 61, 37, 38, 53, 2, 63, 23, 67, 72, 95, 70, 29, 33, 45, 47, 27, 24, 69, 44, 55, 85, 14, 8, 91, 12, 42, 6, 84, 81, 19, 25, 32, 51, 1, 17, 52, 56, 60, 0, 59, 30, 21, 20], [26, 63, 99, 57, 77, 53, 9, 75, 76, 80, 46, 97, 37, 14, 1, 12, 35, 20, 0, 15, 28, 19, 48, 41, 67, 30, 60, 69, 29, 34, 73, 10, 51, 47, 66, 82, 7, 98, 39, 96, 8, 62, 42, 3, 95, 5, 11, 13, 90, 94, 32, 31, 83, 64, 36, 88, 72, 52, 38, 54, 45, 17, 61, 81, 78, 44, 79, 55, 56, 86, 2, 40, 16, 24, 4, 71, 49, 33, 23, 92, 84, 74, 65, 43, 87, 59, 89, 68, 22, 70, 25, 58, 21, 85, 6, 50, 27, 93, 91, 18], [22, 31, 11, 72, 37, 70, 90, 60, 24, 56, 28, 69, 54, 85, 5, 98, 1, 76, 59, 20, 86, 32, 61, 49, 63, 42, 74, 53, 84, 3, 95, 23, 15, 36, 4, 81, 6, 21, 19, 27, 17, 83, 30, 45, 25, 65, 10, 50, 80, 66, 46, 75, 93, 82, 78, 99, 97, 68, 8, 13, 58, 94, 41, 14, 48, 52, 29, 79, 91, 89, 9, 55, 57, 2, 39, 92, 0, 44, 18, 62, 73, 96, 34, 88, 16, 77, 71, 43, 33, 40, 7, 12, 87, 51, 26, 47, 64, 35, 67, 38], [0, 80, 63, 52, 49, 69, 15, 87, 55, 60, 22, 91, 31, 89, 78, 28, 48, 12, 10, 42, 82, 23, 29, 18, 21, 2, 19, 7, 68, 37, 54, 90, 81, 43, 57, 94, 26, 95, 96, 88, 14, 51, 79, 71, 66, 76, 34, 24, 30, 9, 62, 44, 77, 65, 6, 27, 4, 97, 38, 36, 59, 3, 1, 50, 72, 46, 64, 35, 20, 11, 53, 13, 58, 39, 74, 8, 83, 75, 40, 98, 17, 99, 85, 32, 92, 67, 84, 5, 25, 56, 16, 47, 70, 93, 86, 61, 73, 41, 45, 33], [64, 57, 56, 7, 86, 9, 65, 16, 1, 63, 71, 26, 53, 98, 69, 5, 84, 28, 36, 58, 54, 99, 42, 74, 68, 3, 88, 22, 82, 97, 85, 45, 21, 66, 43, 59, 83, 51, 89, 87, 31, 12, 95, 52, 60, 17, 80, 27, 72, 93, 10, 39, 91, 30, 75, 61, 24, 8, 23, 2, 18, 73, 94, 6, 38, 44, 13, 40, 48, 4, 81, 37, 96, 46, 19, 49, 34, 50, 0, 20, 14, 15, 25, 92, 33, 29, 77, 90, 78, 67, 62, 41, 35, 47, 79, 32, 11, 70, 76, 55], [24, 99, 28, 52, 64, 3, 63, 16, 15, 87, 47, 6, 59, 98, 13, 78, 57, 81, 56, 94, 0, 37, 70, 73, 65, 83, 90, 92, 60, 82, 67, 40, 74, 34, 91, 2, 33, 85, 21, 54, 12, 31, 84, 55, 53, 49, 36, 25, 39, 20, 29, 93, 51, 76, 42, 96, 71, 86, 95, 66, 45, 19, 8, 58, 22, 69, 80, 79, 89, 17, 61, 18, 88, 38, 72, 75, 48, 30, 7, 10, 11, 35, 23, 9, 26, 4, 62, 44, 1, 43, 46, 14, 41, 77, 97, 27, 68, 32, 5, 50], [90, 80, 22, 27, 16, 23, 11, 31, 8, 75, 54, 82, 68, 66, 10, 40, 95, 99, 14, 42, 56, 88, 12, 55, 38, 6, 79, 84, 26, 92, 4, 67, 48, 5, 46, 77, 19, 53, 97, 34, 64, 73, 58, 47, 18, 39, 81, 59, 74, 62, 87, 98, 50, 1, 25, 9, 72, 69, 61, 60, 86, 3, 24, 28, 45, 30, 96, 36, 57, 83, 89, 63, 0, 33, 17, 7, 94, 15, 43, 2, 44, 41, 20, 85, 32, 65, 93, 49, 91, 52, 71, 37, 35, 13, 21, 51, 70, 29, 78, 76], [95, 50, 72, 0, 68, 98, 49, 53, 56, 41, 54, 22, 57, 78, 66, 31, 52, 29, 86, 30, 62, 26, 32, 46, 42, 48, 14, 21, 88, 94, 77, 92, 7, 63, 91, 28, 93, 38, 90, 71, 34, 25, 11, 83, 55, 70, 97, 82, 89, 76, 10, 99, 9, 45, 60, 39, 44, 16, 17, 37, 47, 79, 33, 36, 59, 8, 80, 23, 1, 18, 64, 51, 61, 81, 75, 40, 35, 73, 13, 65, 12, 4, 19, 85, 96, 6, 2, 58, 27, 43, 5, 87, 20, 15, 3, 24, 84, 67, 74, 69], [38, 33, 29, 88, 49, 47, 60, 69, 75, 12, 44, 89, 35, 45, 23, 30, 21, 28, 65, 86, 81, 40, 93, 97, 67, 2, 94, 9, 61, 92, 90, 32, 62, 36, 58, 51, 37, 73, 50, 63, 39, 43, 16, 71, 46, 91, 13, 7, 20, 72, 41, 59, 70, 48, 10, 52, 5, 34, 76, 15, 87, 6, 98, 18, 57, 77, 24, 79, 27, 19, 3, 0, 8, 95, 74, 64, 42, 4, 68, 31, 84, 54, 99, 25, 1, 80, 85, 26, 83, 11, 56, 55, 78, 66, 82, 14, 53, 22, 17, 96], [26, 12, 62, 5, 78, 53, 31, 46, 51, 8, 69, 80, 32, 68, 76, 42, 29, 87, 28, 75, 40, 91, 58, 7, 88, 92, 82, 96, 70, 50, 47, 65, 85, 3, 48, 73, 90, 89, 97, 84, 81, 39, 43, 79, 64, 54, 2, 41, 38, 10, 24, 22, 25, 93, 44, 23, 49, 37, 14, 95, 72, 74, 15, 11, 55, 98, 99, 0, 9, 16, 18, 20, 63, 30, 57, 19, 4, 17, 67, 35, 60, 45, 59, 56, 36, 77, 66, 33, 27, 71, 86, 1, 13, 34, 21, 61, 6, 94, 52, 83]], [[10, 87, 64, 11, 25, 97, 93, 18, 99, 38, 17, 72, 50, 23, 90, 57, 98, 12, 29, 46, 19, 77, 42, 15, 73, 52, 53, 89, 35, 20, 9, 41, 79, 13, 95, 48, 85, 54, 61, 94, 0, 74, 62, 40, 78, 68, 24, 28, 86, 21, 59, 14, 82, 84, 92, 58, 34, 31, 6, 16, 75, 81, 43, 47, 7, 3, 2, 37, 67, 8, 66, 22, 30, 32, 71, 44, 70, 1, 36, 26, 33, 63, 80, 83, 91, 76, 56, 39, 69, 55, 96, 60, 27, 49, 65, 88, 5, 4, 51, 45], [73, 87, 48, 43, 71, 40, 88, 23, 60, 35, 30, 5, 75, 25, 59, 95, 58, 79, 4, 89, 96, 17, 38, 6, 39, 9, 68, 81, 3, 90, 93, 99, 16, 42, 21, 45, 74, 84, 77, 65, 63, 98, 11, 51, 0, 33, 91, 22, 69, 34, 31, 20, 46, 2, 76, 54, 15, 62, 13, 70, 92, 55, 82, 47, 78, 7, 24, 27, 86, 1, 56, 61, 12, 44, 85, 57, 49, 19, 18, 67, 66, 52, 72, 53, 83, 41, 64, 50, 26, 94, 32, 80, 28, 10, 37, 14, 8, 97, 29, 36], [60, 50, 53, 78, 63, 90, 11, 33, 21, 24, 20, 55, 8, 25, 6, 58, 3, 98, 44, 82, 96, 14, 1, 59, 92, 75, 51, 36, 30, 64, 72, 40, 16, 94, 2, 74, 93, 85, 12, 10, 43, 32, 42, 39, 79, 97, 17, 38, 89, 29, 68, 52, 49, 0, 34, 19, 70, 84, 54, 9, 23, 71, 65, 28, 83, 56, 67, 57, 13, 18, 77, 5, 4, 88, 41, 22, 46, 76, 48, 61, 81, 45, 86, 35, 47, 87, 91, 99, 73, 66, 27, 37, 31, 15, 7, 80, 62, 95, 69, 26], [49, 73, 63, 66, 38, 35, 32, 96, 87, 26, 36, 92, 58, 6, 17, 47, 29, 84, 11, 18, 67, 45, 78, 4, 52, 81, 59, 30, 91, 54, 56, 89, 48, 46, 20, 79, 1, 62, 9, 53, 10, 95, 88, 34, 40, 80, 41, 64, 83, 55, 43, 7, 24, 61, 77, 5, 14, 93, 16, 21, 85, 39, 76, 50, 57, 65, 37, 60, 28, 19, 2, 97, 42, 33, 13, 68, 90, 25, 69, 99, 98, 74, 3, 8, 12, 15, 82, 71, 31, 23, 75, 27, 51, 94, 86, 0, 44, 22, 72, 70], [30, 32, 52, 21, 15, 79, 53, 40, 56, 44, 55, 6, 63, 39, 61, 4, 93, 43, 23, 99, 38, 66, 88, 18, 1, 86, 33, 9, 82, 76, 70, 51, 48, 69, 85, 11, 5, 91, 59, 77, 49, 75, 22, 87, 65, 41, 26, 50, 47, 98, 57, 10, 58, 62, 46, 28, 20, 71, 78, 95, 0, 72, 80, 73, 92, 36, 96, 37, 68, 2, 35, 83, 90, 25, 13, 24, 34, 42, 45, 74, 67, 16, 84, 97, 19, 64, 27, 14, 31, 12, 54, 60, 3, 8, 17, 89, 81, 94, 7, 29], [1, 18, 25, 13, 57, 53, 92, 95, 94, 88, 10, 82, 34, 83, 7, 28, 55, 11, 5, 58, 8, 9, 74, 99, 52, 32, 62, 45, 65, 50, 41, 56, 96, 44, 38, 47, 3, 39, 81, 19, 2, 22, 49, 12, 40, 67, 89, 76, 64, 70, 61, 21, 31, 54, 51, 17, 46, 30, 24, 60, 66, 26, 77, 63, 37, 35, 23, 6, 4, 87, 79, 97, 20, 48, 36, 85, 73, 71, 27, 43, 0, 15, 69, 78, 59, 86, 75, 33, 42, 84, 91, 93, 68, 80, 16, 98, 90, 72, 14, 29], [30, 16, 10, 67, 83, 81, 42, 94, 35, 59, 22, 5, 53, 61, 1, 96, 68, 45, 95, 66, 36, 37, 97, 87, 77, 93, 86, 33, 49, 13, 43, 14, 0, 34, 7, 2, 41, 21, 31, 32, 76, 55, 62, 72, 90, 71, 38, 23, 46, 12, 51, 19, 6, 44, 52, 48, 73, 24, 29, 74, 85, 99, 80, 28, 47, 9, 39, 84, 64, 57, 75, 98, 88, 26, 17, 63, 27, 56, 69, 8, 54, 25, 91, 78, 3, 50, 65, 70, 11, 4, 92, 60, 40, 58, 15, 89, 82, 18, 79, 20], [60, 88, 79, 35, 40, 43, 52, 4, 17, 31, 84, 44, 50, 94, 91, 38, 87, 25, 92, 62, 14, 97, 37, 18, 86, 28, 65, 90, 19, 29, 30, 75, 68, 49, 69, 11, 59, 41, 89, 33, 47, 54, 98, 42, 80, 56, 78, 99, 23, 82, 9, 48, 21, 1, 63, 22, 58, 95, 10, 12, 26, 74, 85, 13, 8, 27, 24, 53, 61, 34, 64, 71, 66, 32, 83, 73, 15, 67, 0, 20, 16, 6, 77, 45, 5, 93, 76, 39, 57, 2, 96, 46, 51, 3, 55, 7, 81, 70, 36, 72], [35, 56, 77, 58, 23, 83, 91, 81, 53, 70, 20, 74, 84, 50, 79, 51, 88, 44, 45, 71, 37, 26, 54, 48, 27, 43, 0, 12, 95, 97, 1, 63, 67, 98, 59, 40, 13, 19, 31, 87, 99, 16, 57, 30, 17, 92, 55, 68, 5, 46, 64, 8, 7, 42, 90, 33, 29, 9, 85, 24, 2, 69, 32, 10, 21, 65, 39, 86, 52, 62, 76, 89, 82, 6, 78, 4, 47, 28, 3, 75, 41, 15, 18, 14, 66, 73, 38, 80, 61, 49, 11, 22, 34, 93, 36, 25, 94, 96, 60, 72], [6, 90, 16, 43, 20, 55, 94, 8, 62, 59, 47, 32, 81, 67, 42, 35, 71, 19, 54, 49, 2, 88, 38, 5, 53, 77, 85, 11, 73, 34, 3, 72, 95, 58, 82, 64, 44, 29, 93, 14, 56, 98, 4, 37, 45, 75, 86, 60, 87, 7, 31, 17, 89, 97, 10, 70, 36, 99, 46, 63, 76, 41, 48, 84, 9, 52, 1, 24, 83, 25, 68, 18, 50, 26, 30, 27, 61, 0, 91, 33, 51, 40, 79, 39, 15, 74, 28, 78, 69, 96, 57, 65, 21, 80, 22, 23, 12, 13, 92, 66]], [[93, 42, 18, 65, 28, 87, 39, 35, 71, 23, 96, 90, 15, 58, 78, 33, 92, 88, 49, 64, 24, 84, 77, 8, 97, 50, 4, 53, 27, 83, 95, 31, 80, 47, 86, 55, 11, 40, 37, 14, 34, 60, 54, 12, 94, 9, 41, 2, 26, 10, 6, 57, 75, 63, 32, 43, 13, 52, 82, 73, 20, 30, 17, 5, 3, 68, 51, 89, 62, 79, 69, 70, 66, 45, 25, 22, 85, 0, 91, 76, 38, 7, 99, 61, 29, 98, 16, 36, 44, 67, 59, 56, 48, 81, 21, 46, 74, 72, 19, 1], [78, 91, 97, 77, 75, 85, 32, 2, 40, 86, 38, 18, 56, 47, 50, 87, 27, 92, 64, 39, 98, 23, 33, 72, 84, 58, 16, 99, 19, 65, 55, 73, 74, 42, 4, 48, 95, 8, 51, 3, 37, 82, 96, 69, 14, 44, 12, 21, 76, 70, 36, 15, 71, 41, 20, 31, 54, 13, 0, 9, 94, 67, 1, 60, 81, 61, 11, 6, 90, 5, 49, 10, 34, 7, 35, 30, 25, 88, 79, 93, 66, 62, 22, 24, 52, 17, 89, 45, 83, 57, 28, 46, 63, 68, 26, 29, 80, 59, 43, 53], [78, 91, 97, 77, 75, 85, 32, 2, 40, 86, 38, 18, 56, 47, 50, 87, 27, 92, 64, 39, 98, 23, 33, 72, 84, 58, 16, 99, 19, 65, 55, 73, 74, 42, 4, 48, 95, 8, 51, 3, 37, 82, 96, 69, 14, 44, 12, 21, 76, 70, 36, 15, 71, 41, 20, 31, 54, 13, 0, 9, 94, 67, 1, 60, 81, 61, 11, 6, 90, 5, 49, 10, 34, 7, 35, 30, 25, 88, 79, 93, 66, 62, 22, 24, 52, 17, 89, 45, 83, 57, 28, 46, 63, 68, 26, 29, 80, 59, 43, 53], [41, 65, 16, 86, 77, 67, 51, 81, 50, 25, 90, 87, 98, 99, 23, 9, 4, 97, 39, 36, 26, 42, 13, 66, 82, 22, 47, 88, 49, 46, 59, 78, 20, 19, 7, 93, 56, 84, 40, 61, 89, 21, 74, 1, 54, 44, 34, 6, 28, 8, 43, 76, 48, 71, 85, 38, 83, 18, 24, 79, 68, 80, 45, 33, 91, 27, 32, 70, 95, 3, 58, 60, 73, 10, 75, 52, 64, 2, 94, 30, 17, 55, 62, 35, 11, 12, 53, 63, 14, 31, 57, 0, 37, 72, 92, 15, 5, 29, 69, 96], [74, 50, 44, 80, 11, 46, 39, 54, 24, 3, 7, 62, 41, 8, 75, 23, 57, 82, 51, 86, 97, 79, 22, 60, 42, 14, 28, 67, 32, 73, 48, 56, 89, 26, 25, 77, 21, 68, 10, 2, 58, 76, 92, 95, 38, 29, 43, 35, 12, 16, 55, 17, 63, 91, 45, 64, 59, 13, 47, 31, 83, 5, 99, 49, 78, 71, 96, 53, 36, 90, 1, 66, 15, 0, 85, 65, 27, 4, 98, 94, 6, 84, 72, 30, 70, 20, 61, 69, 37, 93, 34, 87, 9, 52, 88, 81, 33, 19, 40, 18], [35, 67, 27, 53, 26, 79, 20, 46, 38, 4, 72, 84, 70, 58, 65, 91, 92, 21, 32, 71, 54, 24, 89, 0, 37, 8, 22, 81, 12, 87, 43, 18, 82, 17, 33, 76, 25, 13, 74, 51, 99, 3, 55, 60, 19, 73, 86, 62, 93, 52, 31, 64, 96, 66, 14, 40, 42, 69, 45, 6, 61, 34, 41, 59, 49, 10, 39, 2, 75, 80, 15, 94, 7, 23, 95, 78, 90, 5, 1, 28, 56, 30, 9, 98, 44, 48, 68, 77, 47, 11, 63, 83, 88, 36, 97, 29, 57, 50, 16, 85], [8, 51, 32, 96, 34, 24, 88, 54, 87, 83, 45, 99, 49, 30, 55, 61, 95, 60, 59, 76, 57, 5, 19, 56, 89, 66, 27, 94, 14, 6, 21, 58, 43, 74, 10, 2, 17, 62, 47, 16, 69, 84, 63, 39, 4, 92, 35, 48, 53, 64, 71, 50, 72, 28, 42, 46, 91, 23, 15, 40, 11, 78, 29, 86, 13, 90, 85, 20, 65, 93, 73, 80, 41, 37, 82, 79, 52, 9, 97, 38, 75, 25, 44, 7, 26, 12, 22, 0, 33, 1, 31, 68, 70, 67, 98, 18, 77, 36, 3, 81], [34, 17, 91, 45, 63, 64, 1, 71, 98, 78, 94, 3, 99, 12, 15, 93, 69, 19, 61, 80, 92, 81, 33, 51, 40, 10, 75, 24, 5, 87, 73, 46, 52, 86, 4, 70, 89, 56, 2, 67, 72, 58, 49, 84, 29, 18, 22, 38, 95, 36, 43, 35, 37, 85, 57, 8, 76, 7, 27, 28, 55, 74, 42, 13, 77, 31, 60, 39, 48, 68, 97, 79, 44, 54, 88, 82, 21, 66, 9, 23, 59, 50, 47, 16, 83, 25, 6, 30, 41, 96, 90, 14, 62, 20, 65, 11, 26, 32, 53, 0], [7, 51, 77, 80, 0, 53, 56, 27, 2, 78, 28, 32, 26, 22, 73, 93, 36, 54, 64, 99, 60, 50, 21, 11, 39, 42, 81, 8, 1, 79, 61, 5, 15, 31, 59, 33, 20, 94, 23, 88, 69, 17, 35, 95, 40, 75, 85, 87, 43, 47, 89, 12, 71, 72, 9, 83, 67, 38, 90, 82, 29, 10, 37, 91, 97, 74, 6, 86, 30, 63, 24, 49, 3, 13, 4, 57, 76, 70, 98, 34, 58, 52, 96, 84, 25, 55, 44, 46, 18, 65, 62, 68, 48, 66, 41, 19, 14, 45, 16, 92], [96, 2, 76, 15, 31, 22, 71, 38, 88, 26, 67, 0, 21, 10, 58, 39, 63, 77, 23, 50, 48, 19, 78, 30, 93, 91, 14, 12, 28, 11, 53, 73, 60, 61, 35, 79, 57, 70, 40, 20, 56, 32, 65, 64, 68, 55, 82, 94, 45, 95, 66, 7, 43, 16, 37, 99, 72, 62, 13, 4, 47, 8, 98, 89, 46, 29, 51, 85, 74, 17, 97, 84, 33, 18, 25, 83, 69, 44, 87, 49, 92, 9, 90, 36, 41, 81, 59, 54, 34, 6, 42, 5, 80, 3, 1, 86, 27, 52, 24, 75]], [[61, 85, 41, 37, 12, 25, 27, 17, 92, 52, 44, 66, 13, 15, 19, 78, 77, 9, 40, 20, 58, 43, 76, 24, 59, 42, 28, 30, 96, 86, 62, 89, 67, 35, 45, 6, 36, 50, 74, 26, 84, 95, 7, 68, 97, 79, 51, 16, 80, 3, 82, 2, 93, 21, 31, 98, 18, 60, 39, 72, 54, 65, 73, 32, 5, 70, 99, 10, 14, 71, 34, 4, 64, 33, 55, 48, 75, 87, 57, 81, 22, 46, 69, 63, 90, 23, 38, 56, 8, 29, 83, 88, 11, 0, 47, 53, 91, 94, 49, 1], [35, 22, 67, 24, 94, 82, 12, 68, 30, 43, 58, 96, 27, 69, 65, 18, 7, 79, 14, 2, 44, 60, 54, 47, 51, 83, 89, 3, 20, 11, 40, 85, 37, 48, 49, 8, 73, 15, 90, 93, 70, 84, 34, 4, 86, 98, 17, 46, 72, 63, 26, 56, 97, 74, 91, 99, 36, 80, 81, 41, 53, 88, 77, 21, 0, 23, 64, 76, 95, 33, 62, 1, 75, 87, 10, 66, 57, 6, 28, 25, 42, 9, 50, 39, 29, 78, 13, 19, 31, 45, 55, 61, 38, 71, 59, 5, 92, 52, 32, 16], [70, 29, 50, 57, 31, 9, 59, 45, 87, 71, 98, 89, 77, 97, 86, 5, 21, 61, 47, 41, 76, 37, 65, 12, 58, 75, 78, 91, 27, 63, 64, 83, 55, 95, 4, 11, 82, 23, 39, 40, 42, 68, 69, 48, 54, 13, 99, 33, 26, 0, 66, 79, 46, 72, 49, 44, 25, 74, 73, 28, 38, 10, 2, 85, 15, 19, 52, 94, 7, 36, 17, 32, 96, 3, 20, 30, 92, 51, 14, 90, 24, 67, 43, 34, 93, 84, 62, 81, 1, 56, 53, 88, 80, 35, 22, 6, 18, 16, 8, 60], [62, 75, 38, 50, 52, 86, 53, 73, 99, 59, 26, 46, 48, 90, 64, 1, 65, 13, 30, 36, 57, 16, 97, 91, 51, 33, 80, 67, 5, 25, 8, 96, 94, 84, 6, 89, 88, 20, 10, 85, 41, 56, 9, 34, 63, 0, 18, 12, 42, 15, 87, 4, 61, 43, 82, 17, 98, 60, 92, 70, 49, 71, 47, 54, 79, 83, 27, 74, 58, 2, 76, 93, 22, 69, 78, 44, 28, 95, 72, 68, 7, 55, 29, 77, 14, 23, 31, 32, 3, 37, 11, 66, 35, 39, 24, 81, 19, 21, 40, 45], [65, 78, 7, 83, 80, 77, 71, 72, 22, 85, 63, 73, 44, 87, 86, 88, 41, 30, 95, 67, 58, 21, 4, 5, 26, 27, 75, 31, 99, 52, 92, 64, 28, 8, 19, 98, 84, 34, 9, 48, 60, 0, 56, 1, 62, 16, 91, 32, 89, 79, 40, 11, 54, 51, 43, 12, 45, 66, 23, 13, 96, 81, 47, 36, 46, 93, 2, 69, 33, 3, 20, 6, 35, 18, 74, 68, 53, 37, 29, 61, 90, 57, 14, 42, 24, 76, 10, 38, 97, 39, 25, 50, 49, 70, 55, 82, 15, 17, 59, 94], [11, 17, 21, 48, 63, 0, 73, 14, 10, 88, 2, 28, 6, 9, 19, 69, 81, 13, 35, 95, 60, 56, 30, 47, 91, 92, 86, 29, 76, 50, 16, 25, 82, 93, 8, 5, 58, 94, 98, 20, 74, 77, 4, 52, 97, 79, 27, 42, 36, 12, 26, 72, 71, 45, 24, 87, 1, 39, 22, 89, 53, 31, 61, 37, 33, 18, 59, 43, 80, 15, 54, 38, 78, 90, 32, 83, 23, 44, 55, 66, 64, 3, 67, 49, 84, 62, 51, 41, 46, 85, 96, 34, 99, 75, 7, 40, 70, 68, 65, 57], [43, 38, 48, 47, 74, 88, 64, 54, 59, 35, 29, 18, 53, 91, 68, 66, 12, 79, 55, 41, 34, 36, 33, 94, 56, 87, 25, 6, 1, 8, 63, 96, 93, 44, 40, 65, 13, 77, 61, 85, 99, 80, 11, 51, 14, 46, 5, 39, 92, 27, 75, 52, 72, 83, 19, 62, 32, 24, 82, 95, 17, 37, 0, 15, 97, 4, 42, 86, 84, 3, 26, 81, 98, 9, 73, 22, 28, 78, 60, 76, 16, 23, 71, 89, 31, 57, 58, 20, 50, 10, 49, 45, 21, 67, 90, 30, 7, 2, 70, 69], [78, 35, 94, 59, 8, 23, 10, 76, 96, 80, 93, 9, 27, 95, 61, 84, 88, 91, 2, 33, 1, 85, 3, 36, 79, 18, 92, 5, 34, 81, 98, 40, 38, 68, 90, 70, 89, 65, 48, 6, 77, 30, 72, 62, 16, 83, 32, 54, 99, 82, 75, 41, 53, 17, 67, 43, 28, 21, 46, 7, 25, 64, 13, 14, 87, 58, 63, 31, 39, 47, 24, 71, 74, 55, 20, 11, 22, 45, 44, 51, 37, 15, 42, 56, 66, 29, 86, 49, 69, 57, 73, 50, 60, 0, 4, 19, 52, 97, 12, 26], [88, 86, 41, 1, 70, 10, 19, 2, 11, 44, 87, 26, 53, 59, 40, 18, 61, 13, 21, 56, 85, 75, 25, 5, 97, 7, 36, 67, 76, 79, 90, 65, 91, 0, 98, 89, 20, 55, 93, 58, 28, 63, 6, 83, 47, 71, 73, 49, 39, 31, 48, 69, 82, 50, 78, 62, 14, 74, 29, 54, 57, 80, 32, 94, 3, 66, 30, 35, 8, 46, 16, 51, 24, 17, 4, 84, 45, 60, 34, 15, 72, 77, 42, 12, 64, 9, 33, 95, 81, 96, 38, 43, 52, 99, 22, 92, 27, 23, 37, 68], [90, 93, 83, 21, 96, 71, 67, 28, 37, 75, 55, 58, 56, 39, 16, 46, 91, 41, 77, 92, 99, 65, 27, 5, 24, 1, 69, 7, 31, 35, 33, 64, 12, 14, 45, 11, 98, 0, 57, 72, 74, 13, 61, 40, 63, 89, 53, 85, 18, 76, 2, 70, 20, 60, 84, 52, 25, 43, 54, 17, 36, 19, 97, 73, 94, 62, 3, 23, 34, 88, 29, 80, 50, 22, 81, 87, 6, 30, 78, 48, 79, 44, 66, 8, 51, 86, 4, 15, 42, 10, 82, 32, 68, 26, 38, 47, 49, 9, 95, 59]]];\nconst ibtable2 = [[4, 7, 8, 0, 9, 1, 3, 6, 2, 5], [5, 0, 4, 1, 3, 8, 9, 7, 6, 2], [0, 2, 7, 8, 9, 1, 6, 3, 5, 4], [5, 9, 8, 4, 1, 7, 0, 2, 3, 6], [7, 3, 0, 1, 8, 9, 6, 5, 2, 4], [7, 2, 4, 0, 1, 9, 6, 3, 5, 8], [4, 1, 6, 3, 5, 8, 0, 7, 2, 9], [6, 8, 4, 1, 5, 3, 7, 2, 9, 0], [4, 1, 8, 3, 7, 2, 0, 6, 5, 9], [7, 4, 5, 6, 2, 3, 9, 1, 8, 0]];\n\ntype DecodedAccessCode = { revision: number; serial: number };\n\nfunction crc16(data: BigInteger, bits: number, initial: number = 0xffff, poly: number = 0x8408): number {\n let v = bigInt(initial);\n let bigPoly = bigInt(poly);\n\n for (let i = 0; i < bits; i++) {\n v = v.shiftRight(1).xor(bigPoly.and(v.xor(data).and(bigInt.one).equals(bigInt.one) ? bigPoly : bigInt.zero));\n data = data.shiftRight(1);\n }\n\n return v.toJSNumber();\n}\n\nexport function decodeAccessCode(code: string): DecodedAccessCode | null {\n code = code.replace(/\\D/i, '');\n\n if (code.length !== 20) {\n return null;\n }\n\n let body = _.range(3, 15, 2).map(i => parseInt(code[i]) * 10 + parseInt(code[i + 1]));\n let crc = _.range(15, 20).map(i => parseInt(code[i]));\n\n let boxes = [(crc[3] + crc[2]) % 10, crc[2], crc[3], crc[4], (crc[4] + crc[0]) % 10, crc[1]];\n\n for (const [n, i] of body.entries()) {\n body[n] = ibtable[4][boxes[n]][i];\n }\n\n const rv = body[0] >> 4;\n\n boxes = [(crc[1] + crc[0]) % 10, crc[1], crc[2], crc[3], crc[4], (crc[4] + crc[1]) % 10]\n for (const [n, i] of body.entries()) {\n if (n == 0) {\n body[n] = (body[n] & 0xf0) | ibtable2[boxes[n]][body[n] & 0x0f];\n } else {\n body[n] = ibtable[rv][boxes[n]][i];\n }\n }\n\n const serial = body.map(i => i.toString().padStart(2, '0')).join('')\n\n if (crc16(bigInt(code.substring(0, 3) + serial, 16), 15 * 4) !== parseInt(code.substring(15, 20))) {\n throw new Error(`CRC16 mismatch.`);\n }\n\n return { revision: rv, serial: parseInt(serial) };\n}\n\nexport function encodeAccessCode(company: number, serial: number, rev: number = 0): string {\n if (company < 0 || company > 999) {\n throw new Error(`Invalid company code`);\n }\n\n if (serial < 0 || serial > 99999999999) {\n throw new Error(`Invalid serial number`);\n }\n\n if (rev < 0 || rev > sbtable.length) {\n throw new Error(`Invalid revision`);\n }\n\n const bodyStr = `${rev}${serial.toString().padStart(11, '0')}`;\n const crc_i = crc16(bigInt(company.toString().padStart(3, '0') + bodyStr, 16), 15 * 4);\n\n const crc = _.range(5).map(i => Math.floor(crc_i / (10 ** (4 - i))) % 10);\n const body = _.range(0, 12, 2).map(i => parseInt(bodyStr[i]) * 10 + parseInt(bodyStr[i + 1]));\n\n let boxes = [(crc[1] + crc[0]) % 10, crc[1], crc[2], crc[3], crc[4], (crc[4] + crc[1]) % 10];\n for (const [n, i] of body.entries()) {\n if (n == 0) {\n body[n] = (body[n] & 0xf0) | sbtable2[boxes[n]][body[n] & 0x0f];\n } else {\n body[n] = sbtable[rev][boxes[n]][i];\n }\n }\n\n boxes = [(crc[3] + crc[2]) % 10, crc[2], crc[3], crc[4], (crc[4] + crc[0]) % 10, crc[1]]\n for (const [n, i] of body.entries()) {\n body[n] = sbtable[4][boxes[n]][i];\n }\n\n return `${company.toString().padStart(3, '0')}${body.map(i => i.toString().padStart(2, '0')).join('')}${crc_i.toString().padStart(5, '0')}`;\n}\n","import {encodeAccessCode} from \"./card-encoding\";\n\nexport const CARD_ID_REGEX = /^(?:[0-9]{4} ?){5}$/i;\nexport const AMNET_COMPANY_ID = 597;\n\nexport function generateCardNumber(): string {\n // serial between 0 and 99999999999\n const serial = Math.floor(Math.random() * 100000000000);\n return encodeAccessCode(AMNET_COMPANY_ID, serial, 0);\n}\n\n/**\n * Validates the provided card id by known parameters\n * @param id The card id to validate\n */\nexport function validateCardId(id: string): boolean {\n return id[0] !== '3' && CARD_ID_REGEX.test(id);\n}\n","/**\n * Get the game name from the game identifier (4-letter code)\n * @param id\n */\nexport function getGameName(id: string): string | null {\n switch (id?.toUpperCase()) {\n case \"SDGT\":\n return \"Initial D The Arcade\";\n case \"SDHD\":\n case \"SDBT\":\n return \"CHUNITHM\";\n case \"SDDT\":\n return \"O.N.G.E.K.I.\";\n case \"SDEZ\":\n return \"maimai DX\";\n case \"SBZV\":\n return \"Project DIVA Future Tone\";\n \n default:\n return null;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAGO,IAAM,oBAAoB;AAK1B,IAAM,wBAAwB;AA0CrC,eAAsB,cAAc,QAAgC,SAA0B,QAAuC;AACjI,MAAI,OAAO,aAAa,yBAAyB,OAAO,aAAa,mBAAmB;AACpF,UAAM,IAAI,MAAM,gCAAgC;AAAA,EACpD;AAEA,MAAI;AACA,UAAM,WAAW,MAAM,MAAM,GAAG,IAAI,IAAI,OAAO,aAAa,EAAE,MAAM,iBAAiB;AAAA,MACjF,QAAQ;AAAA,MACR,SAAS;AAAA,QACL,gBAAgB;AAAA,MACpB;AAAA,MACA,MAAM,KAAK,UAAU,OAAO;AAAA,MAC5B;AAAA,IACJ,CAAC;AAED,WAAO,SAAS;AAAA,EACpB,SAAQ;AACJ,WAAO;AAAA,EACX;AACJ;AAOA,eAAsB,gBAAgB,eAAuB,OAAkD;AAC3G,MAAI;AACA,UAAM,WAAW,MAAM,MAAM,GAAG,IAAI,IAAI,aAAa,EAAE,MAAM,eAAe;AAAA,MACxE,QAAQ;AAAA,IACZ,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AACd,aAAO;AAAA,IACX;AAEA,WAAO,iCACA,MAAM,SAAS,KAAK,IADpB;AAAA,MAEH;AAAA,IACJ;AAAA,EACJ,SAAQ;AACJ,WAAO;AAAA,EACX;AACJ;;;AC7FA,OAAO,OAAO;AACd,OAAO,YAA0B;AAEjC,IAAM,UAAU,CAAC,CAAC,CAAC,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,EAAE,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC,CAAC;AACzqmB,IAAM,WAAW,CAAC,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC;AAEhV,IAAM,UAAU,CAAC,CAAC,CAAC,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,EAAE,GAAG,CAAC,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,EAAE,CAAC,CAAC;AACzqmB,IAAM,WAAW,CAAC,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC;AAIhV,SAAS,MAAM,MAAkB,MAAc,UAAkB,OAAQ,OAAe,OAAgB;AACpG,MAAI,IAAI,OAAO,OAAO;AACtB,MAAI,UAAU,OAAO,IAAI;AAEzB,WAAS,IAAI,GAAG,IAAI,MAAM,KAAK;AAC3B,QAAI,EAAE,WAAW,CAAC,EAAE,IAAI,QAAQ,IAAI,EAAE,IAAI,IAAI,EAAE,IAAI,OAAO,GAAG,EAAE,OAAO,OAAO,GAAG,IAAI,UAAU,OAAO,IAAI,CAAC;AAC3G,WAAO,KAAK,WAAW,CAAC;AAAA,EAC5B;AAEA,SAAO,EAAE,WAAW;AACxB;AAEO,SAAS,iBAAiB,MAAwC;AACrE,SAAO,KAAK,QAAQ,OAAO,EAAE;AAE7B,MAAI,KAAK,WAAW,IAAI;AACpB,WAAO;AAAA,EACX;AAEA,MAAI,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC,EAAE,IAAI,OAAK,SAAS,KAAK,CAAC,CAAC,IAAI,KAAK,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC;AACpF,MAAI,MAAM,EAAE,MAAM,IAAI,EAAE,EAAE,IAAI,OAAK,SAAS,KAAK,CAAC,CAAC,CAAC;AAEpD,MAAI,QAAQ,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC;AAE3F,aAAW,CAAC,GAAG,CAAC,KAAK,KAAK,QAAQ,GAAG;AACjC,SAAK,CAAC,IAAI,QAAQ,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC;AAAA,EACpC;AAEA,QAAM,KAAK,KAAK,CAAC,KAAK;AAEtB,UAAQ,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE;AACvF,aAAW,CAAC,GAAG,CAAC,KAAK,KAAK,QAAQ,GAAG;AACjC,QAAI,KAAK,GAAG;AACR,WAAK,CAAC,IAAK,KAAK,CAAC,IAAI,MAAQ,SAAS,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,EAAI;AAAA,IAClE,OAAO;AACH,WAAK,CAAC,IAAI,QAAQ,EAAE,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC;AAAA,IACrC;AAAA,EACJ;AAEA,QAAM,SAAS,KAAK,IAAI,OAAK,EAAE,SAAS,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE;AAEnE,MAAI,MAAM,OAAO,KAAK,UAAU,GAAG,CAAC,IAAI,QAAQ,EAAE,GAAG,KAAK,CAAC,MAAM,SAAS,KAAK,UAAU,IAAI,EAAE,CAAC,GAAG;AAC/F,UAAM,IAAI,MAAM,iBAAiB;AAAA,EACrC;AAEA,SAAO,EAAE,UAAU,IAAI,QAAQ,SAAS,MAAM,EAAE;AACpD;AAEO,SAAS,iBAAiB,SAAiB,QAAgB,MAAc,GAAW;AACvF,MAAI,UAAU,KAAK,UAAU,KAAK;AAC9B,UAAM,IAAI,MAAM,sBAAsB;AAAA,EAC1C;AAEA,MAAI,SAAS,KAAK,SAAS,aAAa;AACpC,UAAM,IAAI,MAAM,uBAAuB;AAAA,EAC3C;AAEA,MAAI,MAAM,KAAK,MAAM,QAAQ,QAAQ;AACjC,UAAM,IAAI,MAAM,kBAAkB;AAAA,EACtC;AAEA,QAAM,UAAU,GAAG,GAAG,GAAG,OAAO,SAAS,EAAE,SAAS,IAAI,GAAG,CAAC;AAC5D,QAAM,QAAQ,MAAM,OAAO,QAAQ,SAAS,EAAE,SAAS,GAAG,GAAG,IAAI,SAAS,EAAE,GAAG,KAAK,CAAC;AAErF,QAAM,MAAM,EAAE,MAAM,CAAC,EAAE,IAAI,OAAK,KAAK,MAAM,QAAS,OAAO,IAAI,EAAG,IAAI,EAAE;AACxE,QAAM,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC,EAAE,IAAI,OAAK,SAAS,QAAQ,CAAC,CAAC,IAAI,KAAK,SAAS,QAAQ,IAAI,CAAC,CAAC,CAAC;AAE5F,MAAI,QAAQ,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE;AAC3F,aAAW,CAAC,GAAG,CAAC,KAAK,KAAK,QAAQ,GAAG;AACjC,QAAI,KAAK,GAAG;AACR,WAAK,CAAC,IAAK,KAAK,CAAC,IAAI,MAAQ,SAAS,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,EAAI;AAAA,IAClE,OAAO;AACH,WAAK,CAAC,IAAI,QAAQ,GAAG,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC;AAAA,IACtC;AAAA,EACJ;AAEA,UAAQ,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC;AACvF,aAAW,CAAC,GAAG,CAAC,KAAK,KAAK,QAAQ,GAAG;AACjC,SAAK,CAAC,IAAI,QAAQ,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC;AAAA,EACpC;AAEA,SAAO,GAAG,QAAQ,SAAS,EAAE,SAAS,GAAG,GAAG,CAAC,GAAG,KAAK,IAAI,OAAK,EAAE,SAAS,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,MAAM,SAAS,EAAE,SAAS,GAAG,GAAG,CAAC;AAC7I;;;AC3FO,IAAM,gBAAgB;AACtB,IAAM,mBAAmB;AAEzB,SAAS,qBAA6B;AAEzC,QAAM,SAAS,KAAK,MAAM,KAAK,OAAO,IAAI,IAAY;AACtD,SAAO,iBAAiB,kBAAkB,QAAQ,CAAC;AACvD;AAMO,SAAS,eAAe,IAAqB;AAChD,SAAO,GAAG,CAAC,MAAM,OAAO,cAAc,KAAK,EAAE;AACjD;;;ACbO,SAAS,YAAY,IAA2B;AACnD,UAAQ,yBAAI,eAAe;AAAA,IACvB,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AAAA,IACL,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IAEX;AACI,aAAO;AAAA,EACf;AACJ;","names":[]} \ No newline at end of file diff --git a/amnet-shared/package-lock.json b/amnet-shared/package-lock.json index 8e7e60e..8c546c6 100644 --- a/amnet-shared/package-lock.json +++ b/amnet-shared/package-lock.json @@ -7,7 +7,12 @@ "": { "name": "amnet-shared", "version": "1.0.0", + "dependencies": { + "big-integer": "^1.6.52", + "lodash": "^4.17.21" + }, "devDependencies": { + "@types/lodash": "^4.17.14", "tsup": "^8.2.4", "typescript": "^5.5.4" } @@ -771,6 +776,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/lodash": { + "version": "4.17.14", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.14.tgz", + "integrity": "sha512-jsxagdikDiDBeIRaPYtArcT8my4tN1og7MtMRquFT3XNA6axxyHDRUemqDz/taRDdOUn0GnGHRCuff4q48sW9A==", + "dev": true, + "license": "MIT" + }, "node_modules/ansi-regex": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", @@ -835,6 +847,15 @@ "dev": true, "license": "MIT" }, + "node_modules/big-integer": { + "version": "1.6.52", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", + "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", + "license": "Unlicense", + "engines": { + "node": ">=0.6" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -1391,6 +1412,12 @@ "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, "node_modules/lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", diff --git a/amnet-shared/package.json b/amnet-shared/package.json index a3b58fe..0b0bdcc 100644 --- a/amnet-shared/package.json +++ b/amnet-shared/package.json @@ -16,7 +16,12 @@ ".": "./dist/index.mjs" }, "devDependencies": { + "@types/lodash": "^4.17.14", "tsup": "^8.2.4", "typescript": "^5.5.4" + }, + "dependencies": { + "big-integer": "^1.6.52", + "lodash": "^4.17.21" } } diff --git a/amnet-shared/src/card-encoding.ts b/amnet-shared/src/card-encoding.ts new file mode 100644 index 0000000..2802109 --- /dev/null +++ b/amnet-shared/src/card-encoding.ts @@ -0,0 +1,94 @@ +import _ from "lodash"; +import bigInt, {BigInteger} from "big-integer"; + +const sbtable = [[[45, 50, 94, 2, 66, 55, 56, 49, 99, 36, 60, 0, 73, 30, 39, 37, 80, 74, 85, 7, 12, 33, 93, 83, 95, 91, 98, 92, 23, 24, 79, 21, 31, 35, 71, 70, 4, 43, 1, 5, 88, 90, 69, 58, 76, 20, 84, 62, 77, 44, 61, 11, 16, 53, 82, 34, 13, 47, 48, 87, 89, 15, 64, 3, 54, 28, 57, 67, 9, 75, 32, 65, 96, 17, 18, 29, 10, 14, 42, 6, 46, 26, 40, 59, 63, 78, 68, 8, 86, 51, 25, 72, 38, 97, 19, 81, 27, 41, 52, 22], [26, 32, 34, 79, 42, 45, 4, 75, 22, 73, 64, 98, 49, 19, 5, 24, 6, 55, 63, 25, 40, 77, 54, 12, 9, 44, 83, 71, 0, 90, 97, 96, 81, 80, 3, 31, 56, 92, 17, 86, 72, 28, 36, 78, 62, 30, 7, 46, 87, 20, 82, 50, 35, 61, 41, 94, 68, 11, 1, 10, 74, 67, 85, 43, 57, 15, 95, 84, 65, 53, 69, 52, 76, 88, 2, 14, 37, 58, 93, 16, 59, 51, 18, 99, 39, 47, 89, 8, 13, 91, 38, 33, 70, 48, 23, 29, 27, 66, 60, 21], [28, 53, 30, 85, 0, 95, 70, 47, 40, 51, 94, 68, 99, 15, 5, 57, 1, 16, 80, 93, 91, 52, 2, 32, 48, 77, 41, 75, 22, 39, 50, 81, 79, 73, 97, 76, 60, 4, 59, 25, 78, 38, 46, 58, 90, 84, 56, 6, 83, 62, 42, 10, 64, 31, 36, 61, 65, 63, 18, 8, 9, 96, 55, 26, 89, 67, 3, 98, 35, 21, 13, 12, 88, 71, 37, 29, 87, 49, 24, 86, 92, 20, 44, 74, 69, 17, 82, 54, 27, 7, 23, 66, 11, 19, 34, 45, 14, 72, 43, 33], [32, 44, 86, 79, 38, 61, 25, 8, 5, 85, 78, 48, 33, 87, 84, 35, 64, 41, 60, 21, 96, 75, 6, 69, 39, 94, 3, 92, 37, 77, 15, 13, 29, 59, 76, 91, 65, 56, 73, 83, 27, 62, 89, 50, 18, 98, 81, 17, 71, 28, 82, 11, 30, 43, 34, 20, 70, 22, 46, 63, 52, 68, 90, 53, 36, 10, 74, 58, 40, 88, 9, 2, 42, 26, 24, 4, 95, 57, 31, 66, 0, 14, 49, 16, 99, 19, 51, 23, 47, 12, 93, 1, 7, 80, 45, 97, 55, 67, 72, 54], [58, 35, 42, 38, 63, 36, 68, 30, 81, 4, 87, 80, 32, 69, 37, 67, 1, 86, 61, 94, 27, 11, 84, 26, 59, 73, 53, 66, 72, 71, 39, 33, 99, 8, 43, 88, 17, 22, 89, 5, 46, 93, 98, 48, 55, 83, 34, 3, 0, 16, 12, 20, 78, 9, 2, 76, 54, 51, 25, 24, 62, 57, 92, 10, 50, 28, 79, 23, 52, 95, 44, 60, 74, 90, 19, 77, 21, 31, 96, 49, 64, 6, 29, 85, 18, 13, 82, 47, 56, 45, 15, 7, 97, 75, 40, 41, 65, 14, 70, 91], [81, 82, 66, 72, 83, 4, 20, 94, 53, 48, 77, 41, 67, 64, 9, 24, 36, 55, 14, 1, 78, 84, 49, 0, 44, 58, 70, 25, 28, 73, 8, 68, 88, 54, 86, 87, 23, 52, 27, 5, 97, 37, 62, 50, 3, 89, 92, 47, 30, 10, 29, 80, 22, 26, 11, 13, 69, 60, 38, 90, 85, 57, 12, 75, 46, 39, 74, 33, 42, 93, 98, 91, 59, 99, 18, 6, 17, 16, 32, 40, 63, 7, 76, 15, 43, 71, 51, 35, 2, 65, 34, 96, 19, 56, 79, 21, 95, 45, 61, 31], [25, 88, 7, 57, 98, 63, 86, 13, 69, 84, 56, 20, 68, 90, 35, 6, 43, 93, 95, 3, 75, 4, 9, 28, 31, 66, 21, 44, 30, 76, 11, 22, 49, 2, 77, 40, 85, 19, 82, 16, 97, 92, 39, 96, 18, 32, 42, 94, 70, 71, 54, 79, 67, 78, 37, 12, 99, 53, 61, 41, 87, 17, 1, 14, 52, 10, 47, 58, 27, 46, 36, 55, 26, 15, 33, 50, 72, 51, 89, 23, 74, 59, 64, 8, 83, 65, 80, 29, 38, 0, 60, 24, 45, 81, 48, 91, 73, 5, 34, 62], [73, 85, 37, 11, 80, 46, 16, 47, 88, 65, 94, 64, 12, 38, 98, 70, 95, 48, 92, 18, 56, 75, 58, 7, 81, 15, 74, 13, 5, 44, 27, 32, 76, 90, 33, 55, 26, 96, 77, 24, 0, 89, 8, 4, 28, 39, 71, 3, 10, 41, 91, 53, 31, 68, 36, 17, 69, 60, 34, 54, 93, 82, 21, 51, 87, 59, 61, 6, 1, 23, 30, 66, 43, 25, 35, 42, 49, 78, 62, 14, 19, 20, 29, 22, 52, 83, 63, 45, 2, 99, 50, 79, 72, 40, 84, 97, 86, 57, 67, 9], [48, 94, 17, 55, 59, 24, 72, 75, 38, 85, 34, 98, 95, 36, 49, 63, 57, 54, 29, 6, 52, 47, 73, 1, 82, 9, 66, 83, 31, 61, 78, 60, 97, 14, 87, 15, 90, 70, 53, 10, 27, 8, 3, 64, 51, 50, 68, 26, 22, 19, 42, 96, 67, 7, 25, 39, 92, 30, 58, 93, 21, 74, 41, 18, 65, 44, 88, 32, 71, 69, 12, 77, 13, 11, 99, 79, 37, 89, 81, 35, 86, 56, 45, 76, 46, 80, 62, 43, 16, 84, 40, 5, 33, 91, 4, 20, 0, 23, 28, 2], [61, 45, 79, 13, 59, 52, 6, 77, 3, 31, 70, 80, 26, 33, 36, 4, 27, 32, 24, 74, 29, 94, 16, 12, 76, 7, 88, 57, 78, 42, 97, 96, 43, 83, 92, 21, 51, 47, 49, 58, 90, 41, 93, 98, 35, 63, 86, 37, 5, 30, 28, 72, 25, 22, 60, 19, 39, 34, 68, 50, 8, 64, 46, 99, 84, 87, 69, 56, 53, 55, 44, 54, 89, 65, 85, 0, 71, 15, 17, 66, 81, 82, 91, 95, 62, 48, 38, 75, 40, 14, 9, 10, 73, 67, 18, 11, 23, 2, 20, 1]], [[95, 90, 61, 38, 18, 7, 83, 39, 79, 14, 48, 21, 81, 44, 78, 50, 56, 91, 12, 86, 99, 98, 43, 63, 73, 87, 42, 72, 15, 68, 97, 23, 88, 69, 2, 51, 19, 58, 59, 22, 20, 29, 82, 11, 75, 70, 9, 71, 27, 30, 47, 89, 92, 60, 46, 76, 93, 54, 1, 96, 94, 57, 40, 62, 41, 3, 28, 64, 37, 74, 67, 16, 65, 53, 5, 36, 24, 45, 32, 33, 10, 85, 26, 4, 84, 77, 35, 13, 6, 8, 17, 80, 25, 55, 52, 66, 49, 34, 0, 31], [18, 14, 70, 43, 74, 45, 94, 36, 40, 6, 31, 46, 15, 47, 13, 19, 72, 61, 99, 21, 17, 92, 88, 78, 73, 90, 0, 96, 20, 28, 25, 51, 50, 77, 29, 16, 54, 12, 58, 38, 71, 23, 42, 83, 65, 60, 10, 33, 22, 76, 95, 32, 57, 5, 59, 67, 68, 3, 91, 85, 26, 62, 41, 1, 53, 82, 34, 24, 87, 27, 89, 75, 56, 30, 81, 7, 8, 4, 64, 66, 9, 63, 35, 52, 80, 93, 69, 84, 55, 86, 48, 98, 79, 97, 49, 44, 39, 11, 37, 2], [76, 16, 73, 29, 34, 14, 36, 90, 58, 70, 46, 2, 91, 59, 63, 32, 84, 40, 78, 38, 19, 37, 0, 31, 8, 44, 94, 39, 10, 66, 42, 1, 21, 88, 82, 97, 33, 4, 99, 74, 89, 62, 25, 87, 77, 43, 50, 95, 64, 23, 47, 93, 65, 27, 12, 71, 9, 72, 60, 18, 7, 22, 79, 24, 96, 45, 49, 98, 57, 11, 5, 86, 3, 80, 26, 51, 17, 85, 54, 67, 48, 35, 53, 41, 28, 13, 20, 92, 83, 69, 6, 68, 75, 52, 61, 30, 81, 56, 15, 55], [0, 62, 25, 61, 56, 87, 54, 27, 75, 49, 18, 69, 17, 71, 40, 6, 90, 80, 23, 26, 68, 24, 10, 21, 47, 88, 36, 55, 15, 22, 48, 12, 83, 99, 46, 67, 59, 29, 58, 73, 78, 97, 19, 33, 51, 98, 65, 91, 16, 4, 63, 41, 3, 70, 30, 8, 89, 34, 72, 60, 9, 95, 50, 2, 66, 53, 44, 85, 28, 5, 92, 43, 64, 96, 74, 77, 45, 52, 14, 42, 1, 32, 20, 76, 86, 82, 94, 7, 39, 13, 31, 11, 84, 93, 35, 37, 38, 57, 79, 81], [78, 8, 59, 25, 69, 15, 63, 3, 57, 5, 50, 96, 41, 66, 80, 81, 7, 45, 60, 74, 79, 32, 27, 58, 56, 82, 11, 47, 17, 85, 53, 40, 95, 84, 76, 92, 18, 71, 64, 51, 67, 91, 22, 34, 65, 31, 73, 93, 68, 75, 77, 37, 43, 12, 20, 99, 2, 1, 19, 35, 44, 55, 90, 9, 0, 6, 33, 89, 24, 14, 97, 10, 48, 61, 23, 54, 98, 86, 88, 94, 46, 70, 28, 36, 16, 30, 4, 39, 26, 38, 87, 52, 83, 49, 62, 42, 72, 29, 13, 21], [20, 88, 35, 5, 85, 98, 11, 78, 62, 83, 79, 80, 40, 14, 91, 8, 7, 69, 71, 61, 49, 38, 64, 82, 0, 47, 84, 95, 2, 50, 77, 41, 97, 36, 33, 81, 46, 21, 73, 48, 31, 92, 54, 89, 87, 60, 90, 10, 76, 45, 99, 52, 3, 44, 39, 43, 18, 16, 63, 12, 28, 70, 86, 6, 4, 24, 59, 30, 96, 65, 22, 56, 74, 23, 32, 75, 53, 93, 15, 67, 66, 17, 29, 25, 42, 37, 57, 9, 72, 68, 26, 34, 27, 51, 19, 58, 55, 94, 13, 1], [72, 53, 79, 61, 30, 33, 25, 75, 8, 55, 14, 6, 22, 93, 18, 77, 4, 74, 44, 36, 82, 94, 2, 5, 62, 54, 28, 3, 63, 97, 65, 7, 84, 73, 39, 92, 67, 91, 24, 45, 15, 81, 19, 78, 80, 64, 34, 43, 32, 87, 52, 95, 89, 37, 10, 23, 20, 68, 42, 47, 59, 58, 49, 71, 40, 85, 13, 31, 12, 57, 96, 90, 56, 41, 48, 9, 99, 35, 98, 26, 1, 46, 11, 69, 27, 83, 60, 50, 21, 70, 0, 88, 29, 86, 76, 16, 66, 38, 51, 17], [3, 68, 86, 94, 81, 90, 85, 32, 65, 52, 50, 42, 80, 78, 26, 93, 57, 58, 69, 82, 92, 27, 11, 67, 95, 41, 21, 88, 35, 17, 19, 15, 22, 62, 40, 76, 63, 59, 37, 55, 75, 9, 24, 89, 56, 53, 23, 60, 25, 6, 1, 71, 16, 7, 10, 44, 8, 12, 87, 64, 54, 72, 20, 33, 70, 79, 14, 97, 4, 99, 45, 39, 2, 77, 98, 74, 49, 30, 13, 61, 66, 73, 47, 43, 96, 83, 18, 91, 28, 48, 38, 34, 31, 36, 29, 0, 84, 46, 5, 51], [71, 84, 25, 70, 77, 56, 61, 47, 72, 27, 54, 89, 9, 46, 95, 59, 42, 98, 63, 69, 48, 16, 97, 14, 66, 83, 87, 68, 17, 2, 15, 79, 31, 1, 57, 12, 33, 36, 0, 40, 21, 50, 76, 41, 10, 13, 44, 5, 53, 4, 38, 35, 55, 96, 81, 91, 90, 64, 34, 51, 6, 28, 32, 39, 75, 18, 93, 24, 78, 7, 52, 43, 49, 37, 74, 8, 58, 65, 92, 67, 85, 20, 94, 88, 80, 86, 19, 60, 3, 11, 30, 45, 29, 22, 26, 73, 99, 23, 62, 82], [67, 91, 46, 33, 76, 3, 96, 23, 9, 68, 49, 63, 1, 92, 58, 62, 69, 77, 70, 75, 71, 94, 51, 55, 50, 52, 0, 88, 18, 16, 73, 6, 12, 87, 93, 79, 84, 57, 48, 41, 20, 47, 15, 42, 54, 81, 7, 30, 34, 56, 29, 8, 98, 5, 45, 64, 83, 74, 22, 82, 80, 95, 2, 72, 44, 31, 86, 78, 13, 10, 28, 89, 60, 35, 61, 19, 14, 85, 4, 43, 11, 40, 26, 99, 39, 32, 90, 17, 24, 37, 36, 21, 25, 53, 97, 59, 27, 38, 65, 66]], [[40, 77, 66, 65, 97, 96, 58, 64, 69, 30, 0, 3, 17, 33, 51, 23, 59, 10, 7, 20, 29, 49, 71, 13, 46, 4, 79, 92, 47, 18, 72, 57, 73, 80, 56, 28, 78, 67, 9, 87, 43, 31, 22, 62, 75, 99, 19, 63, 35, 93, 12, 98, 25, 26, 37, 89, 86, 15, 55, 50, 91, 38, 42, 81, 2, 94, 70, 68, 45, 88, 76, 74, 11, 24, 41, 60, 85, 21, 44, 32, 82, 61, 52, 83, 53, 36, 48, 1, 95, 27, 14, 84, 54, 6, 39, 34, 90, 5, 16, 8], [44, 69, 53, 28, 18, 11, 23, 65, 96, 25, 93, 42, 72, 58, 95, 56, 32, 21, 78, 77, 51, 34, 47, 7, 66, 13, 88, 67, 92, 98, 10, 50, 90, 45, 49, 9, 99, 94, 22, 24, 5, 85, 33, 3, 73, 35, 52, 63, 2, 76, 87, 43, 81, 83, 55, 61, 70, 75, 16, 14, 8, 71, 57, 40, 86, 39, 80, 79, 26, 48, 59, 4, 82, 0, 36, 12, 54, 38, 64, 17, 91, 27, 62, 84, 37, 74, 68, 1, 6, 19, 29, 46, 60, 30, 89, 15, 20, 97, 41, 31], [53, 22, 34, 16, 72, 71, 14, 94, 12, 59, 39, 6, 38, 68, 21, 93, 32, 46, 69, 55, 10, 8, 75, 60, 9, 13, 99, 90, 63, 49, 28, 92, 41, 7, 54, 83, 27, 91, 47, 43, 31, 74, 42, 40, 18, 81, 76, 84, 78, 52, 1, 26, 51, 2, 58, 11, 65, 67, 15, 23, 0, 79, 96, 4, 29, 62, 89, 66, 50, 98, 56, 61, 30, 88, 35, 25, 77, 70, 3, 44, 95, 80, 19, 64, 57, 37, 82, 85, 73, 48, 5, 86, 24, 36, 33, 97, 20, 45, 17, 87], [95, 36, 70, 82, 23, 55, 13, 51, 83, 38, 40, 18, 84, 74, 56, 85, 58, 14, 19, 69, 34, 59, 97, 89, 52, 77, 9, 91, 68, 16, 27, 88, 6, 73, 43, 5, 10, 66, 4, 61, 44, 46, 72, 50, 96, 21, 33, 15, 32, 0, 63, 92, 24, 39, 29, 49, 30, 64, 12, 26, 67, 53, 37, 2, 47, 65, 3, 20, 75, 78, 99, 87, 98, 1, 81, 90, 62, 54, 22, 35, 45, 25, 86, 48, 17, 60, 94, 8, 42, 31, 76, 28, 11, 57, 93, 41, 7, 71, 80, 79], [60, 24, 69, 92, 15, 36, 11, 98, 93, 27, 51, 35, 89, 74, 87, 4, 81, 94, 23, 84, 56, 3, 42, 18, 75, 73, 46, 86, 55, 99, 0, 88, 1, 26, 76, 70, 65, 67, 20, 13, 7, 45, 77, 17, 9, 78, 54, 48, 32, 40, 47, 31, 2, 6, 90, 10, 8, 50, 52, 38, 91, 14, 53, 12, 85, 44, 21, 80, 68, 33, 30, 57, 61, 63, 79, 41, 29, 39, 58, 5, 62, 96, 28, 71, 82, 34, 25, 43, 22, 95, 72, 37, 64, 16, 97, 59, 66, 83, 49, 19], [80, 0, 40, 36, 68, 18, 67, 14, 20, 21, 10, 17, 43, 3, 98, 81, 94, 55, 1, 39, 72, 51, 41, 66, 58, 2, 61, 78, 15, 99, 57, 52, 25, 87, 12, 65, 74, 64, 34, 37, 44, 30, 88, 79, 33, 27, 56, 35, 73, 42, 29, 54, 24, 5, 53, 16, 31, 4, 19, 84, 59, 50, 26, 63, 48, 28, 60, 45, 92, 82, 49, 77, 97, 76, 22, 86, 47, 62, 83, 70, 93, 38, 11, 13, 89, 75, 85, 69, 9, 46, 96, 90, 6, 91, 8, 7, 32, 71, 95, 23], [32, 14, 35, 84, 89, 11, 52, 34, 79, 65, 2, 88, 49, 29, 31, 94, 1, 74, 97, 51, 99, 37, 10, 47, 57, 81, 73, 76, 63, 58, 0, 38, 39, 27, 33, 8, 20, 21, 46, 66, 92, 36, 6, 30, 53, 17, 48, 64, 55, 28, 85, 50, 54, 12, 80, 41, 77, 69, 93, 9, 91, 13, 42, 75, 68, 86, 19, 3, 16, 78, 87, 45, 43, 56, 59, 70, 40, 24, 83, 98, 62, 5, 96, 4, 67, 60, 26, 23, 72, 95, 44, 82, 90, 25, 7, 18, 15, 22, 71, 61], [78, 53, 89, 93, 7, 84, 81, 95, 64, 50, 58, 35, 59, 63, 20, 76, 80, 8, 23, 28, 79, 52, 55, 48, 66, 17, 60, 65, 25, 29, 30, 9, 73, 39, 69, 3, 98, 22, 15, 87, 4, 37, 43, 5, 11, 83, 91, 40, 51, 33, 12, 92, 6, 67, 41, 94, 45, 88, 56, 36, 0, 68, 19, 54, 70, 26, 72, 77, 32, 34, 97, 71, 99, 75, 61, 31, 86, 82, 46, 2, 44, 96, 49, 74, 10, 62, 24, 16, 1, 38, 27, 14, 18, 85, 13, 57, 90, 21, 42, 47], [26, 30, 60, 78, 75, 48, 73, 52, 51, 57, 63, 90, 27, 36, 83, 81, 41, 44, 82, 37, 10, 64, 91, 4, 59, 95, 21, 24, 77, 56, 43, 38, 62, 55, 92, 0, 94, 20, 86, 66, 35, 80, 53, 25, 17, 18, 49, 76, 23, 89, 13, 15, 68, 8, 22, 46, 1, 42, 3, 34, 98, 88, 69, 31, 50, 65, 84, 32, 47, 61, 9, 19, 99, 85, 11, 79, 70, 2, 74, 14, 87, 7, 72, 5, 12, 58, 67, 39, 16, 71, 54, 6, 45, 93, 96, 28, 97, 29, 33, 40], [77, 66, 20, 30, 42, 23, 0, 49, 7, 64, 54, 27, 96, 97, 39, 84, 2, 51, 71, 17, 4, 92, 94, 95, 67, 69, 73, 75, 86, 37, 74, 50, 11, 79, 29, 15, 56, 43, 22, 83, 81, 61, 14, 3, 36, 44, 58, 10, 62, 19, 72, 80, 65, 24, 18, 5, 40, 90, 33, 9, 47, 76, 8, 59, 35, 91, 99, 13, 70, 88, 55, 16, 31, 28, 85, 45, 60, 25, 87, 82, 93, 12, 34, 68, 63, 26, 46, 48, 21, 52, 1, 78, 98, 38, 6, 32, 89, 53, 41, 57]], [[77, 99, 47, 64, 26, 63, 50, 81, 23, 45, 49, 36, 43, 56, 39, 12, 86, 62, 2, 98, 60, 94, 75, 9, 20, 74, 48, 28, 4, 84, 61, 31, 54, 15, 40, 7, 87, 38, 80, 6, 37, 46, 1, 55, 88, 73, 95, 33, 92, 18, 25, 66, 57, 27, 42, 35, 91, 51, 13, 90, 41, 83, 68, 53, 19, 3, 72, 89, 65, 70, 71, 8, 97, 59, 96, 52, 79, 22, 14, 69, 32, 93, 58, 29, 21, 76, 34, 5, 17, 67, 11, 78, 16, 0, 44, 30, 10, 24, 85, 82], [58, 62, 7, 39, 34, 69, 67, 73, 37, 59, 71, 66, 46, 57, 44, 51, 26, 85, 11, 28, 54, 47, 82, 21, 83, 76, 94, 16, 90, 95, 75, 55, 6, 22, 72, 74, 50, 40, 10, 19, 8, 53, 33, 98, 45, 87, 91, 13, 35, 70, 14, 38, 84, 99, 56, 30, 12, 89, 25, 97, 63, 65, 81, 92, 18, 29, 80, 61, 93, 43, 49, 52, 23, 31, 32, 4, 48, 3, 0, 78, 96, 64, 41, 88, 24, 5, 9, 15, 77, 86, 68, 1, 17, 79, 60, 36, 42, 2, 20, 27], [58, 62, 7, 39, 34, 69, 67, 73, 37, 59, 71, 66, 46, 57, 44, 51, 26, 85, 11, 28, 54, 47, 82, 21, 83, 76, 94, 16, 90, 95, 75, 55, 6, 22, 72, 74, 50, 40, 10, 19, 8, 53, 33, 98, 45, 87, 91, 13, 35, 70, 14, 38, 84, 99, 56, 30, 12, 89, 25, 97, 63, 65, 81, 92, 18, 29, 80, 61, 93, 43, 49, 52, 23, 31, 32, 4, 48, 3, 0, 78, 96, 64, 41, 88, 24, 5, 9, 15, 77, 86, 68, 1, 17, 79, 60, 36, 42, 2, 20, 27], [91, 43, 77, 69, 16, 96, 47, 34, 49, 15, 73, 84, 85, 22, 88, 95, 2, 80, 57, 33, 32, 41, 25, 14, 58, 9, 20, 65, 48, 97, 79, 89, 66, 63, 46, 83, 19, 92, 55, 18, 38, 0, 21, 50, 45, 62, 29, 26, 52, 28, 8, 6, 75, 86, 44, 81, 36, 90, 70, 30, 71, 39, 82, 87, 76, 1, 23, 5, 60, 98, 67, 53, 93, 72, 42, 74, 51, 4, 31, 59, 61, 7, 24, 56, 37, 54, 3, 11, 27, 40, 10, 64, 94, 35, 78, 68, 99, 17, 12, 13], [73, 70, 39, 9, 77, 61, 80, 10, 13, 92, 38, 4, 48, 57, 25, 72, 49, 51, 99, 97, 85, 36, 22, 15, 8, 34, 33, 76, 26, 45, 83, 59, 28, 96, 90, 47, 68, 88, 44, 6, 98, 12, 24, 46, 2, 54, 5, 58, 30, 63, 1, 18, 93, 67, 7, 50, 31, 16, 40, 56, 23, 86, 11, 52, 55, 75, 71, 27, 37, 87, 84, 65, 82, 29, 0, 14, 41, 35, 64, 21, 3, 95, 17, 60, 81, 74, 19, 91, 94, 32, 69, 53, 42, 89, 79, 43, 66, 20, 78, 62], [23, 78, 67, 41, 9, 77, 59, 72, 25, 82, 65, 89, 28, 37, 54, 70, 98, 33, 31, 44, 6, 17, 26, 73, 21, 36, 4, 2, 79, 95, 81, 50, 18, 34, 61, 0, 93, 24, 8, 66, 55, 62, 56, 30, 84, 58, 7, 88, 85, 64, 97, 39, 49, 3, 20, 42, 80, 96, 13, 63, 43, 60, 47, 90, 51, 14, 53, 1, 86, 57, 12, 19, 10, 45, 38, 68, 35, 87, 75, 5, 69, 27, 32, 91, 11, 99, 46, 29, 92, 22, 76, 15, 16, 48, 71, 74, 52, 94, 83, 40], [87, 89, 35, 98, 44, 21, 29, 83, 0, 77, 34, 60, 85, 64, 28, 58, 39, 36, 95, 22, 67, 30, 86, 57, 5, 81, 84, 26, 53, 62, 13, 90, 2, 88, 4, 46, 97, 73, 79, 43, 59, 72, 54, 32, 82, 10, 55, 38, 47, 12, 51, 1, 76, 48, 7, 14, 23, 20, 31, 18, 17, 15, 37, 42, 49, 68, 25, 93, 91, 40, 92, 50, 52, 70, 33, 80, 19, 96, 61, 75, 71, 99, 74, 9, 41, 66, 63, 8, 6, 24, 65, 56, 45, 69, 27, 16, 3, 78, 94, 11], [99, 6, 38, 11, 34, 28, 86, 57, 55, 78, 25, 95, 13, 63, 91, 14, 83, 1, 45, 17, 93, 76, 46, 79, 27, 85, 96, 58, 59, 44, 87, 65, 97, 22, 0, 51, 49, 52, 47, 67, 24, 88, 62, 50, 72, 3, 31, 82, 68, 42, 81, 23, 32, 98, 73, 60, 37, 54, 41, 80, 66, 18, 92, 4, 5, 94, 77, 39, 69, 16, 35, 7, 40, 30, 61, 26, 56, 64, 9, 71, 19, 21, 75, 84, 43, 53, 33, 29, 74, 36, 90, 2, 20, 15, 10, 48, 89, 70, 8, 12], [4, 28, 8, 72, 74, 31, 66, 0, 27, 54, 61, 23, 51, 73, 96, 32, 98, 41, 88, 95, 36, 22, 13, 38, 70, 84, 12, 7, 10, 60, 68, 33, 11, 35, 79, 42, 16, 62, 57, 24, 44, 94, 25, 48, 86, 97, 87, 49, 92, 71, 21, 1, 81, 5, 17, 85, 6, 75, 80, 34, 20, 30, 90, 69, 18, 89, 93, 56, 91, 40, 77, 52, 53, 14, 65, 45, 76, 2, 9, 29, 3, 26, 59, 55, 83, 46, 67, 47, 39, 50, 58, 63, 99, 15, 37, 43, 82, 64, 78, 19], [11, 94, 1, 93, 59, 91, 89, 51, 61, 81, 13, 29, 27, 58, 26, 3, 53, 69, 73, 21, 39, 12, 5, 18, 98, 74, 9, 96, 28, 65, 23, 4, 41, 72, 88, 34, 83, 54, 7, 15, 38, 84, 90, 52, 77, 48, 64, 60, 20, 79, 19, 66, 97, 30, 87, 45, 40, 36, 14, 86, 32, 33, 57, 16, 43, 42, 50, 10, 44, 76, 37, 6, 56, 31, 68, 99, 2, 17, 22, 35, 92, 85, 46, 75, 71, 67, 95, 78, 8, 63, 82, 25, 80, 24, 47, 49, 0, 70, 62, 55]], [[93, 99, 51, 49, 71, 64, 35, 42, 88, 17, 67, 92, 4, 12, 68, 13, 47, 7, 56, 14, 19, 53, 80, 85, 23, 5, 39, 6, 26, 89, 27, 54, 63, 73, 70, 33, 36, 3, 86, 58, 18, 2, 25, 21, 10, 34, 81, 94, 75, 98, 37, 46, 9, 95, 60, 74, 87, 78, 20, 24, 57, 0, 30, 83, 72, 61, 11, 32, 43, 82, 65, 69, 59, 62, 38, 76, 22, 16, 15, 45, 48, 79, 50, 90, 40, 1, 29, 77, 91, 31, 84, 96, 8, 52, 97, 41, 28, 44, 55, 66], [64, 71, 19, 27, 43, 95, 77, 16, 35, 81, 74, 29, 6, 86, 18, 37, 99, 46, 15, 87, 28, 63, 1, 65, 3, 79, 50, 12, 78, 84, 8, 88, 98, 69, 42, 0, 56, 32, 92, 83, 30, 59, 80, 9, 20, 89, 47, 23, 33, 34, 82, 24, 97, 60, 22, 90, 51, 76, 10, 94, 21, 91, 70, 49, 66, 14, 75, 2, 7, 13, 40, 93, 48, 36, 53, 72, 67, 62, 85, 17, 57, 58, 5, 25, 41, 31, 44, 73, 61, 26, 38, 54, 96, 39, 4, 68, 11, 52, 45, 55], [49, 88, 62, 73, 34, 15, 95, 68, 98, 5, 61, 35, 23, 45, 78, 64, 97, 70, 96, 65, 74, 16, 94, 37, 80, 56, 48, 28, 59, 1, 75, 4, 71, 47, 83, 93, 69, 21, 60, 38, 39, 19, 40, 82, 55, 7, 52, 18, 43, 54, 2, 77, 66, 90, 44, 32, 89, 3, 24, 6, 99, 17, 86, 29, 30, 22, 50, 81, 41, 42, 0, 9, 53, 58, 57, 25, 20, 12, 26, 51, 92, 87, 36, 31, 85, 63, 14, 8, 91, 11, 79, 27, 76, 84, 67, 33, 72, 13, 10, 46], [45, 15, 69, 88, 51, 28, 34, 80, 30, 42, 38, 90, 47, 17, 84, 49, 21, 55, 46, 96, 37, 97, 72, 85, 94, 29, 10, 66, 76, 82, 18, 86, 87, 25, 43, 92, 19, 89, 2, 93, 98, 40, 48, 53, 75, 99, 11, 62, 12, 60, 3, 24, 4, 6, 63, 81, 41, 20, 68, 9, 57, 52, 0, 44, 14, 16, 91, 27, 79, 73, 59, 61, 78, 7, 67, 1, 70, 83, 74, 64, 26, 95, 54, 65, 33, 39, 5, 50, 36, 35, 13, 23, 58, 71, 32, 77, 31, 22, 56, 8], [41, 43, 66, 69, 22, 23, 71, 2, 33, 38, 86, 51, 55, 59, 82, 96, 45, 97, 73, 34, 70, 21, 8, 58, 84, 90, 24, 25, 32, 78, 17, 27, 47, 68, 37, 72, 63, 77, 87, 89, 50, 16, 83, 54, 12, 56, 64, 62, 39, 92, 91, 53, 29, 76, 52, 94, 42, 81, 20, 98, 40, 79, 44, 10, 31, 0, 57, 19, 75, 67, 93, 6, 7, 11, 74, 26, 85, 5, 1, 49, 4, 61, 95, 3, 36, 9, 14, 13, 15, 48, 80, 46, 30, 65, 99, 18, 60, 88, 35, 28], [5, 56, 10, 81, 42, 35, 12, 94, 34, 13, 8, 0, 49, 17, 7, 69, 30, 1, 65, 14, 39, 2, 58, 76, 54, 31, 50, 46, 11, 27, 22, 61, 74, 64, 91, 18, 48, 63, 71, 57, 95, 87, 47, 67, 77, 53, 88, 23, 3, 83, 29, 86, 43, 60, 70, 78, 21, 99, 36, 66, 20, 62, 85, 4, 80, 98, 79, 82, 97, 15, 96, 52, 51, 6, 40, 93, 28, 41, 72, 45, 68, 16, 32, 75, 84, 89, 26, 55, 9, 59, 73, 24, 25, 33, 37, 19, 90, 44, 38, 92], [62, 28, 97, 69, 65, 46, 27, 96, 29, 73, 89, 42, 16, 36, 44, 63, 80, 60, 11, 54, 87, 92, 75, 81, 57, 26, 70, 49, 76, 10, 95, 84, 56, 22, 20, 9, 21, 61, 1, 47, 34, 19, 66, 0, 33, 91, 45, 3, 2, 90, 88, 43, 51, 12, 7, 18, 24, 85, 86, 8, 78, 38, 55, 30, 6, 35, 15, 93, 14, 99, 98, 82, 52, 74, 4, 50, 79, 37, 77, 17, 41, 71, 58, 53, 68, 39, 67, 25, 5, 83, 94, 13, 48, 32, 23, 59, 31, 64, 72, 40], [93, 20, 18, 22, 94, 27, 39, 59, 4, 11, 6, 75, 98, 62, 63, 81, 44, 53, 25, 95, 74, 57, 76, 5, 70, 60, 99, 12, 56, 85, 41, 67, 46, 19, 28, 1, 23, 80, 32, 68, 31, 51, 82, 55, 78, 77, 58, 69, 38, 87, 91, 79, 96, 52, 47, 73, 83, 89, 65, 3, 92, 14, 43, 66, 61, 37, 84, 54, 33, 88, 35, 71, 42, 90, 72, 50, 7, 40, 0, 24, 9, 29, 49, 45, 15, 21, 86, 64, 16, 36, 34, 17, 26, 10, 2, 13, 8, 97, 30, 48], [33, 3, 7, 64, 74, 23, 42, 25, 68, 85, 5, 8, 83, 17, 56, 79, 70, 73, 15, 6, 36, 18, 94, 97, 72, 22, 11, 96, 40, 58, 66, 49, 62, 86, 78, 67, 26, 98, 90, 48, 14, 2, 82, 91, 9, 76, 69, 44, 50, 47, 53, 71, 92, 12, 59, 37, 19, 60, 39, 13, 77, 16, 55, 41, 84, 31, 65, 27, 99, 51, 4, 45, 80, 46, 57, 21, 28, 81, 54, 29, 61, 88, 52, 43, 75, 20, 1, 10, 0, 35, 30, 32, 95, 38, 63, 87, 89, 24, 34, 93], [37, 25, 50, 66, 86, 23, 76, 27, 83, 97, 89, 35, 32, 41, 33, 87, 14, 59, 48, 61, 52, 3, 73, 67, 24, 56, 93, 22, 7, 70, 77, 28, 91, 30, 68, 29, 60, 8, 94, 13, 43, 17, 88, 57, 81, 34, 15, 95, 79, 96, 72, 84, 55, 46, 58, 10, 12, 38, 11, 99, 53, 42, 65, 44, 31, 21, 82, 6, 92, 26, 51, 5, 39, 63, 40, 9, 49, 18, 78, 80, 71, 74, 90, 2, 54, 47, 85, 75, 69, 45, 0, 16, 19, 1, 64, 98, 4, 62, 36, 20]]]; +const sbtable2 = [[3, 5, 8, 6, 0, 9, 7, 1, 2, 4], [1, 3, 9, 4, 2, 0, 8, 7, 5, 6], [0, 5, 1, 7, 9, 8, 6, 2, 3, 4], [6, 4, 7, 8, 3, 0, 9, 5, 2, 1], [2, 3, 8, 1, 9, 7, 6, 0, 4, 5], [3, 4, 1, 7, 2, 8, 6, 0, 9, 5], [6, 1, 8, 3, 0, 4, 2, 7, 5, 9], [9, 3, 7, 5, 2, 4, 0, 6, 1, 8], [6, 1, 5, 3, 0, 8, 7, 4, 2, 9], [9, 7, 4, 5, 1, 2, 3, 0, 8, 6]]; + +const ibtable = [[[11, 38, 3, 63, 36, 39, 79, 19, 87, 68, 76, 51, 20, 56, 77, 61, 52, 73, 74, 94, 45, 31, 99, 28, 29, 90, 81, 96, 65, 75, 13, 32, 70, 21, 55, 33, 9, 15, 92, 14, 82, 97, 78, 37, 49, 0, 80, 57, 58, 7, 1, 89, 98, 53, 64, 5, 6, 66, 43, 83, 10, 50, 47, 84, 62, 71, 4, 67, 86, 42, 35, 34, 91, 12, 17, 69, 44, 48, 85, 30, 16, 95, 54, 23, 46, 18, 88, 59, 40, 60, 41, 25, 27, 22, 2, 24, 72, 93, 26, 8], [28, 58, 74, 34, 6, 14, 16, 46, 87, 24, 59, 57, 23, 88, 75, 65, 79, 38, 82, 13, 49, 99, 8, 94, 15, 19, 0, 96, 41, 95, 45, 35, 1, 91, 2, 52, 42, 76, 90, 84, 20, 54, 4, 63, 25, 5, 47, 85, 93, 12, 51, 81, 71, 69, 22, 17, 36, 64, 77, 80, 98, 53, 44, 18, 10, 68, 97, 61, 56, 70, 92, 27, 40, 9, 60, 7, 72, 21, 43, 3, 33, 32, 50, 26, 67, 62, 39, 48, 73, 86, 29, 89, 37, 78, 55, 66, 31, 30, 11, 83], [4, 16, 22, 66, 37, 14, 47, 89, 59, 60, 51, 92, 71, 70, 96, 13, 17, 85, 58, 93, 81, 69, 28, 90, 78, 39, 63, 88, 0, 75, 2, 53, 23, 99, 94, 68, 54, 74, 41, 29, 8, 26, 50, 98, 82, 95, 42, 7, 24, 77, 30, 9, 21, 1, 87, 62, 46, 15, 43, 38, 36, 55, 49, 57, 52, 56, 91, 65, 11, 84, 6, 73, 97, 33, 83, 27, 35, 25, 40, 32, 18, 31, 86, 48, 45, 3, 79, 76, 72, 64, 44, 20, 80, 19, 10, 5, 61, 34, 67, 12], [80, 91, 71, 26, 75, 8, 22, 92, 7, 70, 65, 51, 89, 31, 81, 30, 83, 47, 44, 85, 55, 19, 57, 87, 74, 6, 73, 40, 49, 32, 52, 78, 0, 12, 54, 15, 64, 28, 4, 24, 68, 17, 72, 53, 1, 94, 58, 88, 11, 82, 43, 86, 60, 63, 99, 96, 37, 77, 67, 33, 18, 5, 41, 59, 16, 36, 79, 97, 61, 23, 56, 48, 98, 38, 66, 21, 34, 29, 10, 3, 93, 46, 50, 39, 14, 9, 2, 13, 69, 42, 62, 35, 27, 90, 25, 76, 20, 95, 45, 84], [48, 16, 54, 47, 9, 39, 81, 91, 33, 53, 63, 21, 50, 85, 97, 90, 49, 36, 84, 74, 51, 76, 37, 67, 59, 58, 23, 20, 65, 82, 7, 77, 12, 31, 46, 1, 5, 14, 3, 30, 94, 95, 2, 34, 70, 89, 40, 87, 43, 79, 64, 57, 68, 26, 56, 44, 88, 61, 0, 24, 71, 18, 60, 4, 80, 96, 27, 15, 6, 13, 98, 29, 28, 25, 72, 93, 55, 75, 52, 66, 11, 8, 86, 45, 22, 83, 17, 10, 35, 38, 73, 99, 62, 41, 19, 69, 78, 92, 42, 32], [23, 19, 88, 44, 5, 39, 75, 81, 30, 14, 49, 54, 62, 55, 18, 83, 77, 76, 74, 92, 6, 95, 52, 36, 15, 27, 53, 38, 28, 50, 48, 99, 78, 67, 90, 87, 16, 41, 58, 65, 79, 11, 68, 84, 24, 97, 64, 47, 9, 22, 43, 86, 37, 8, 33, 17, 93, 61, 25, 72, 57, 98, 42, 80, 13, 89, 2, 12, 31, 56, 26, 85, 3, 29, 66, 63, 82, 10, 20, 94, 51, 0, 1, 4, 21, 60, 34, 35, 32, 45, 59, 71, 46, 69, 7, 96, 91, 40, 70, 73], [89, 62, 33, 19, 21, 97, 15, 2, 83, 22, 65, 30, 55, 7, 63, 73, 39, 61, 44, 37, 11, 26, 31, 79, 91, 0, 72, 68, 23, 87, 28, 24, 45, 74, 98, 14, 70, 54, 88, 42, 35, 59, 46, 16, 27, 92, 69, 66, 94, 32, 75, 77, 64, 57, 50, 71, 10, 3, 67, 81, 90, 58, 99, 5, 82, 85, 25, 52, 12, 8, 48, 49, 76, 96, 80, 20, 29, 34, 53, 51, 86, 93, 38, 84, 9, 36, 6, 60, 1, 78, 13, 95, 41, 17, 47, 18, 43, 40, 4, 56], [40, 68, 88, 47, 43, 28, 67, 23, 42, 99, 48, 3, 12, 27, 79, 25, 6, 55, 19, 80, 81, 62, 83, 69, 39, 73, 36, 30, 44, 82, 70, 52, 31, 34, 58, 74, 54, 2, 13, 45, 93, 49, 75, 72, 29, 87, 5, 7, 17, 76, 90, 63, 84, 51, 59, 35, 20, 97, 22, 65, 57, 66, 78, 86, 11, 9, 71, 98, 53, 56, 15, 46, 92, 0, 26, 21, 32, 38, 77, 91, 4, 24, 61, 85, 94, 1, 96, 64, 8, 41, 33, 50, 18, 60, 10, 16, 37, 95, 14, 89], [96, 23, 99, 42, 94, 91, 19, 53, 41, 25, 39, 73, 70, 72, 33, 35, 88, 2, 63, 49, 95, 60, 48, 97, 5, 54, 47, 40, 98, 18, 57, 28, 67, 92, 10, 79, 13, 76, 8, 55, 90, 62, 50, 87, 65, 82, 84, 21, 0, 14, 45, 44, 20, 38, 17, 3, 81, 16, 58, 4, 31, 29, 86, 15, 43, 64, 26, 52, 46, 69, 37, 68, 6, 22, 61, 7, 83, 71, 30, 75, 85, 78, 24, 27, 89, 9, 80, 34, 66, 77, 36, 93, 56, 59, 1, 12, 51, 32, 11, 74], [75, 99, 97, 8, 15, 48, 6, 25, 60, 90, 91, 95, 23, 3, 89, 77, 22, 78, 94, 55, 98, 35, 53, 96, 18, 52, 12, 16, 50, 20, 49, 9, 17, 13, 57, 44, 14, 47, 86, 56, 88, 41, 29, 32, 70, 1, 62, 37, 85, 38, 59, 36, 5, 68, 71, 69, 67, 27, 39, 4, 54, 0, 84, 45, 61, 73, 79, 93, 58, 66, 10, 76, 51, 92, 19, 87, 24, 7, 28, 2, 11, 80, 81, 33, 64, 74, 46, 65, 26, 72, 40, 82, 34, 42, 21, 83, 31, 30, 43, 63]], [[98, 58, 34, 65, 83, 74, 88, 5, 89, 46, 80, 43, 18, 87, 9, 28, 71, 90, 4, 36, 40, 11, 39, 31, 76, 92, 82, 48, 66, 41, 49, 99, 78, 79, 97, 86, 75, 68, 3, 7, 62, 64, 26, 22, 13, 77, 54, 50, 10, 96, 15, 35, 94, 73, 57, 93, 16, 61, 37, 38, 53, 2, 63, 23, 67, 72, 95, 70, 29, 33, 45, 47, 27, 24, 69, 44, 55, 85, 14, 8, 91, 12, 42, 6, 84, 81, 19, 25, 32, 51, 1, 17, 52, 56, 60, 0, 59, 30, 21, 20], [26, 63, 99, 57, 77, 53, 9, 75, 76, 80, 46, 97, 37, 14, 1, 12, 35, 20, 0, 15, 28, 19, 48, 41, 67, 30, 60, 69, 29, 34, 73, 10, 51, 47, 66, 82, 7, 98, 39, 96, 8, 62, 42, 3, 95, 5, 11, 13, 90, 94, 32, 31, 83, 64, 36, 88, 72, 52, 38, 54, 45, 17, 61, 81, 78, 44, 79, 55, 56, 86, 2, 40, 16, 24, 4, 71, 49, 33, 23, 92, 84, 74, 65, 43, 87, 59, 89, 68, 22, 70, 25, 58, 21, 85, 6, 50, 27, 93, 91, 18], [22, 31, 11, 72, 37, 70, 90, 60, 24, 56, 28, 69, 54, 85, 5, 98, 1, 76, 59, 20, 86, 32, 61, 49, 63, 42, 74, 53, 84, 3, 95, 23, 15, 36, 4, 81, 6, 21, 19, 27, 17, 83, 30, 45, 25, 65, 10, 50, 80, 66, 46, 75, 93, 82, 78, 99, 97, 68, 8, 13, 58, 94, 41, 14, 48, 52, 29, 79, 91, 89, 9, 55, 57, 2, 39, 92, 0, 44, 18, 62, 73, 96, 34, 88, 16, 77, 71, 43, 33, 40, 7, 12, 87, 51, 26, 47, 64, 35, 67, 38], [0, 80, 63, 52, 49, 69, 15, 87, 55, 60, 22, 91, 31, 89, 78, 28, 48, 12, 10, 42, 82, 23, 29, 18, 21, 2, 19, 7, 68, 37, 54, 90, 81, 43, 57, 94, 26, 95, 96, 88, 14, 51, 79, 71, 66, 76, 34, 24, 30, 9, 62, 44, 77, 65, 6, 27, 4, 97, 38, 36, 59, 3, 1, 50, 72, 46, 64, 35, 20, 11, 53, 13, 58, 39, 74, 8, 83, 75, 40, 98, 17, 99, 85, 32, 92, 67, 84, 5, 25, 56, 16, 47, 70, 93, 86, 61, 73, 41, 45, 33], [64, 57, 56, 7, 86, 9, 65, 16, 1, 63, 71, 26, 53, 98, 69, 5, 84, 28, 36, 58, 54, 99, 42, 74, 68, 3, 88, 22, 82, 97, 85, 45, 21, 66, 43, 59, 83, 51, 89, 87, 31, 12, 95, 52, 60, 17, 80, 27, 72, 93, 10, 39, 91, 30, 75, 61, 24, 8, 23, 2, 18, 73, 94, 6, 38, 44, 13, 40, 48, 4, 81, 37, 96, 46, 19, 49, 34, 50, 0, 20, 14, 15, 25, 92, 33, 29, 77, 90, 78, 67, 62, 41, 35, 47, 79, 32, 11, 70, 76, 55], [24, 99, 28, 52, 64, 3, 63, 16, 15, 87, 47, 6, 59, 98, 13, 78, 57, 81, 56, 94, 0, 37, 70, 73, 65, 83, 90, 92, 60, 82, 67, 40, 74, 34, 91, 2, 33, 85, 21, 54, 12, 31, 84, 55, 53, 49, 36, 25, 39, 20, 29, 93, 51, 76, 42, 96, 71, 86, 95, 66, 45, 19, 8, 58, 22, 69, 80, 79, 89, 17, 61, 18, 88, 38, 72, 75, 48, 30, 7, 10, 11, 35, 23, 9, 26, 4, 62, 44, 1, 43, 46, 14, 41, 77, 97, 27, 68, 32, 5, 50], [90, 80, 22, 27, 16, 23, 11, 31, 8, 75, 54, 82, 68, 66, 10, 40, 95, 99, 14, 42, 56, 88, 12, 55, 38, 6, 79, 84, 26, 92, 4, 67, 48, 5, 46, 77, 19, 53, 97, 34, 64, 73, 58, 47, 18, 39, 81, 59, 74, 62, 87, 98, 50, 1, 25, 9, 72, 69, 61, 60, 86, 3, 24, 28, 45, 30, 96, 36, 57, 83, 89, 63, 0, 33, 17, 7, 94, 15, 43, 2, 44, 41, 20, 85, 32, 65, 93, 49, 91, 52, 71, 37, 35, 13, 21, 51, 70, 29, 78, 76], [95, 50, 72, 0, 68, 98, 49, 53, 56, 41, 54, 22, 57, 78, 66, 31, 52, 29, 86, 30, 62, 26, 32, 46, 42, 48, 14, 21, 88, 94, 77, 92, 7, 63, 91, 28, 93, 38, 90, 71, 34, 25, 11, 83, 55, 70, 97, 82, 89, 76, 10, 99, 9, 45, 60, 39, 44, 16, 17, 37, 47, 79, 33, 36, 59, 8, 80, 23, 1, 18, 64, 51, 61, 81, 75, 40, 35, 73, 13, 65, 12, 4, 19, 85, 96, 6, 2, 58, 27, 43, 5, 87, 20, 15, 3, 24, 84, 67, 74, 69], [38, 33, 29, 88, 49, 47, 60, 69, 75, 12, 44, 89, 35, 45, 23, 30, 21, 28, 65, 86, 81, 40, 93, 97, 67, 2, 94, 9, 61, 92, 90, 32, 62, 36, 58, 51, 37, 73, 50, 63, 39, 43, 16, 71, 46, 91, 13, 7, 20, 72, 41, 59, 70, 48, 10, 52, 5, 34, 76, 15, 87, 6, 98, 18, 57, 77, 24, 79, 27, 19, 3, 0, 8, 95, 74, 64, 42, 4, 68, 31, 84, 54, 99, 25, 1, 80, 85, 26, 83, 11, 56, 55, 78, 66, 82, 14, 53, 22, 17, 96], [26, 12, 62, 5, 78, 53, 31, 46, 51, 8, 69, 80, 32, 68, 76, 42, 29, 87, 28, 75, 40, 91, 58, 7, 88, 92, 82, 96, 70, 50, 47, 65, 85, 3, 48, 73, 90, 89, 97, 84, 81, 39, 43, 79, 64, 54, 2, 41, 38, 10, 24, 22, 25, 93, 44, 23, 49, 37, 14, 95, 72, 74, 15, 11, 55, 98, 99, 0, 9, 16, 18, 20, 63, 30, 57, 19, 4, 17, 67, 35, 60, 45, 59, 56, 36, 77, 66, 33, 27, 71, 86, 1, 13, 34, 21, 61, 6, 94, 52, 83]], [[10, 87, 64, 11, 25, 97, 93, 18, 99, 38, 17, 72, 50, 23, 90, 57, 98, 12, 29, 46, 19, 77, 42, 15, 73, 52, 53, 89, 35, 20, 9, 41, 79, 13, 95, 48, 85, 54, 61, 94, 0, 74, 62, 40, 78, 68, 24, 28, 86, 21, 59, 14, 82, 84, 92, 58, 34, 31, 6, 16, 75, 81, 43, 47, 7, 3, 2, 37, 67, 8, 66, 22, 30, 32, 71, 44, 70, 1, 36, 26, 33, 63, 80, 83, 91, 76, 56, 39, 69, 55, 96, 60, 27, 49, 65, 88, 5, 4, 51, 45], [73, 87, 48, 43, 71, 40, 88, 23, 60, 35, 30, 5, 75, 25, 59, 95, 58, 79, 4, 89, 96, 17, 38, 6, 39, 9, 68, 81, 3, 90, 93, 99, 16, 42, 21, 45, 74, 84, 77, 65, 63, 98, 11, 51, 0, 33, 91, 22, 69, 34, 31, 20, 46, 2, 76, 54, 15, 62, 13, 70, 92, 55, 82, 47, 78, 7, 24, 27, 86, 1, 56, 61, 12, 44, 85, 57, 49, 19, 18, 67, 66, 52, 72, 53, 83, 41, 64, 50, 26, 94, 32, 80, 28, 10, 37, 14, 8, 97, 29, 36], [60, 50, 53, 78, 63, 90, 11, 33, 21, 24, 20, 55, 8, 25, 6, 58, 3, 98, 44, 82, 96, 14, 1, 59, 92, 75, 51, 36, 30, 64, 72, 40, 16, 94, 2, 74, 93, 85, 12, 10, 43, 32, 42, 39, 79, 97, 17, 38, 89, 29, 68, 52, 49, 0, 34, 19, 70, 84, 54, 9, 23, 71, 65, 28, 83, 56, 67, 57, 13, 18, 77, 5, 4, 88, 41, 22, 46, 76, 48, 61, 81, 45, 86, 35, 47, 87, 91, 99, 73, 66, 27, 37, 31, 15, 7, 80, 62, 95, 69, 26], [49, 73, 63, 66, 38, 35, 32, 96, 87, 26, 36, 92, 58, 6, 17, 47, 29, 84, 11, 18, 67, 45, 78, 4, 52, 81, 59, 30, 91, 54, 56, 89, 48, 46, 20, 79, 1, 62, 9, 53, 10, 95, 88, 34, 40, 80, 41, 64, 83, 55, 43, 7, 24, 61, 77, 5, 14, 93, 16, 21, 85, 39, 76, 50, 57, 65, 37, 60, 28, 19, 2, 97, 42, 33, 13, 68, 90, 25, 69, 99, 98, 74, 3, 8, 12, 15, 82, 71, 31, 23, 75, 27, 51, 94, 86, 0, 44, 22, 72, 70], [30, 32, 52, 21, 15, 79, 53, 40, 56, 44, 55, 6, 63, 39, 61, 4, 93, 43, 23, 99, 38, 66, 88, 18, 1, 86, 33, 9, 82, 76, 70, 51, 48, 69, 85, 11, 5, 91, 59, 77, 49, 75, 22, 87, 65, 41, 26, 50, 47, 98, 57, 10, 58, 62, 46, 28, 20, 71, 78, 95, 0, 72, 80, 73, 92, 36, 96, 37, 68, 2, 35, 83, 90, 25, 13, 24, 34, 42, 45, 74, 67, 16, 84, 97, 19, 64, 27, 14, 31, 12, 54, 60, 3, 8, 17, 89, 81, 94, 7, 29], [1, 18, 25, 13, 57, 53, 92, 95, 94, 88, 10, 82, 34, 83, 7, 28, 55, 11, 5, 58, 8, 9, 74, 99, 52, 32, 62, 45, 65, 50, 41, 56, 96, 44, 38, 47, 3, 39, 81, 19, 2, 22, 49, 12, 40, 67, 89, 76, 64, 70, 61, 21, 31, 54, 51, 17, 46, 30, 24, 60, 66, 26, 77, 63, 37, 35, 23, 6, 4, 87, 79, 97, 20, 48, 36, 85, 73, 71, 27, 43, 0, 15, 69, 78, 59, 86, 75, 33, 42, 84, 91, 93, 68, 80, 16, 98, 90, 72, 14, 29], [30, 16, 10, 67, 83, 81, 42, 94, 35, 59, 22, 5, 53, 61, 1, 96, 68, 45, 95, 66, 36, 37, 97, 87, 77, 93, 86, 33, 49, 13, 43, 14, 0, 34, 7, 2, 41, 21, 31, 32, 76, 55, 62, 72, 90, 71, 38, 23, 46, 12, 51, 19, 6, 44, 52, 48, 73, 24, 29, 74, 85, 99, 80, 28, 47, 9, 39, 84, 64, 57, 75, 98, 88, 26, 17, 63, 27, 56, 69, 8, 54, 25, 91, 78, 3, 50, 65, 70, 11, 4, 92, 60, 40, 58, 15, 89, 82, 18, 79, 20], [60, 88, 79, 35, 40, 43, 52, 4, 17, 31, 84, 44, 50, 94, 91, 38, 87, 25, 92, 62, 14, 97, 37, 18, 86, 28, 65, 90, 19, 29, 30, 75, 68, 49, 69, 11, 59, 41, 89, 33, 47, 54, 98, 42, 80, 56, 78, 99, 23, 82, 9, 48, 21, 1, 63, 22, 58, 95, 10, 12, 26, 74, 85, 13, 8, 27, 24, 53, 61, 34, 64, 71, 66, 32, 83, 73, 15, 67, 0, 20, 16, 6, 77, 45, 5, 93, 76, 39, 57, 2, 96, 46, 51, 3, 55, 7, 81, 70, 36, 72], [35, 56, 77, 58, 23, 83, 91, 81, 53, 70, 20, 74, 84, 50, 79, 51, 88, 44, 45, 71, 37, 26, 54, 48, 27, 43, 0, 12, 95, 97, 1, 63, 67, 98, 59, 40, 13, 19, 31, 87, 99, 16, 57, 30, 17, 92, 55, 68, 5, 46, 64, 8, 7, 42, 90, 33, 29, 9, 85, 24, 2, 69, 32, 10, 21, 65, 39, 86, 52, 62, 76, 89, 82, 6, 78, 4, 47, 28, 3, 75, 41, 15, 18, 14, 66, 73, 38, 80, 61, 49, 11, 22, 34, 93, 36, 25, 94, 96, 60, 72], [6, 90, 16, 43, 20, 55, 94, 8, 62, 59, 47, 32, 81, 67, 42, 35, 71, 19, 54, 49, 2, 88, 38, 5, 53, 77, 85, 11, 73, 34, 3, 72, 95, 58, 82, 64, 44, 29, 93, 14, 56, 98, 4, 37, 45, 75, 86, 60, 87, 7, 31, 17, 89, 97, 10, 70, 36, 99, 46, 63, 76, 41, 48, 84, 9, 52, 1, 24, 83, 25, 68, 18, 50, 26, 30, 27, 61, 0, 91, 33, 51, 40, 79, 39, 15, 74, 28, 78, 69, 96, 57, 65, 21, 80, 22, 23, 12, 13, 92, 66]], [[93, 42, 18, 65, 28, 87, 39, 35, 71, 23, 96, 90, 15, 58, 78, 33, 92, 88, 49, 64, 24, 84, 77, 8, 97, 50, 4, 53, 27, 83, 95, 31, 80, 47, 86, 55, 11, 40, 37, 14, 34, 60, 54, 12, 94, 9, 41, 2, 26, 10, 6, 57, 75, 63, 32, 43, 13, 52, 82, 73, 20, 30, 17, 5, 3, 68, 51, 89, 62, 79, 69, 70, 66, 45, 25, 22, 85, 0, 91, 76, 38, 7, 99, 61, 29, 98, 16, 36, 44, 67, 59, 56, 48, 81, 21, 46, 74, 72, 19, 1], [78, 91, 97, 77, 75, 85, 32, 2, 40, 86, 38, 18, 56, 47, 50, 87, 27, 92, 64, 39, 98, 23, 33, 72, 84, 58, 16, 99, 19, 65, 55, 73, 74, 42, 4, 48, 95, 8, 51, 3, 37, 82, 96, 69, 14, 44, 12, 21, 76, 70, 36, 15, 71, 41, 20, 31, 54, 13, 0, 9, 94, 67, 1, 60, 81, 61, 11, 6, 90, 5, 49, 10, 34, 7, 35, 30, 25, 88, 79, 93, 66, 62, 22, 24, 52, 17, 89, 45, 83, 57, 28, 46, 63, 68, 26, 29, 80, 59, 43, 53], [78, 91, 97, 77, 75, 85, 32, 2, 40, 86, 38, 18, 56, 47, 50, 87, 27, 92, 64, 39, 98, 23, 33, 72, 84, 58, 16, 99, 19, 65, 55, 73, 74, 42, 4, 48, 95, 8, 51, 3, 37, 82, 96, 69, 14, 44, 12, 21, 76, 70, 36, 15, 71, 41, 20, 31, 54, 13, 0, 9, 94, 67, 1, 60, 81, 61, 11, 6, 90, 5, 49, 10, 34, 7, 35, 30, 25, 88, 79, 93, 66, 62, 22, 24, 52, 17, 89, 45, 83, 57, 28, 46, 63, 68, 26, 29, 80, 59, 43, 53], [41, 65, 16, 86, 77, 67, 51, 81, 50, 25, 90, 87, 98, 99, 23, 9, 4, 97, 39, 36, 26, 42, 13, 66, 82, 22, 47, 88, 49, 46, 59, 78, 20, 19, 7, 93, 56, 84, 40, 61, 89, 21, 74, 1, 54, 44, 34, 6, 28, 8, 43, 76, 48, 71, 85, 38, 83, 18, 24, 79, 68, 80, 45, 33, 91, 27, 32, 70, 95, 3, 58, 60, 73, 10, 75, 52, 64, 2, 94, 30, 17, 55, 62, 35, 11, 12, 53, 63, 14, 31, 57, 0, 37, 72, 92, 15, 5, 29, 69, 96], [74, 50, 44, 80, 11, 46, 39, 54, 24, 3, 7, 62, 41, 8, 75, 23, 57, 82, 51, 86, 97, 79, 22, 60, 42, 14, 28, 67, 32, 73, 48, 56, 89, 26, 25, 77, 21, 68, 10, 2, 58, 76, 92, 95, 38, 29, 43, 35, 12, 16, 55, 17, 63, 91, 45, 64, 59, 13, 47, 31, 83, 5, 99, 49, 78, 71, 96, 53, 36, 90, 1, 66, 15, 0, 85, 65, 27, 4, 98, 94, 6, 84, 72, 30, 70, 20, 61, 69, 37, 93, 34, 87, 9, 52, 88, 81, 33, 19, 40, 18], [35, 67, 27, 53, 26, 79, 20, 46, 38, 4, 72, 84, 70, 58, 65, 91, 92, 21, 32, 71, 54, 24, 89, 0, 37, 8, 22, 81, 12, 87, 43, 18, 82, 17, 33, 76, 25, 13, 74, 51, 99, 3, 55, 60, 19, 73, 86, 62, 93, 52, 31, 64, 96, 66, 14, 40, 42, 69, 45, 6, 61, 34, 41, 59, 49, 10, 39, 2, 75, 80, 15, 94, 7, 23, 95, 78, 90, 5, 1, 28, 56, 30, 9, 98, 44, 48, 68, 77, 47, 11, 63, 83, 88, 36, 97, 29, 57, 50, 16, 85], [8, 51, 32, 96, 34, 24, 88, 54, 87, 83, 45, 99, 49, 30, 55, 61, 95, 60, 59, 76, 57, 5, 19, 56, 89, 66, 27, 94, 14, 6, 21, 58, 43, 74, 10, 2, 17, 62, 47, 16, 69, 84, 63, 39, 4, 92, 35, 48, 53, 64, 71, 50, 72, 28, 42, 46, 91, 23, 15, 40, 11, 78, 29, 86, 13, 90, 85, 20, 65, 93, 73, 80, 41, 37, 82, 79, 52, 9, 97, 38, 75, 25, 44, 7, 26, 12, 22, 0, 33, 1, 31, 68, 70, 67, 98, 18, 77, 36, 3, 81], [34, 17, 91, 45, 63, 64, 1, 71, 98, 78, 94, 3, 99, 12, 15, 93, 69, 19, 61, 80, 92, 81, 33, 51, 40, 10, 75, 24, 5, 87, 73, 46, 52, 86, 4, 70, 89, 56, 2, 67, 72, 58, 49, 84, 29, 18, 22, 38, 95, 36, 43, 35, 37, 85, 57, 8, 76, 7, 27, 28, 55, 74, 42, 13, 77, 31, 60, 39, 48, 68, 97, 79, 44, 54, 88, 82, 21, 66, 9, 23, 59, 50, 47, 16, 83, 25, 6, 30, 41, 96, 90, 14, 62, 20, 65, 11, 26, 32, 53, 0], [7, 51, 77, 80, 0, 53, 56, 27, 2, 78, 28, 32, 26, 22, 73, 93, 36, 54, 64, 99, 60, 50, 21, 11, 39, 42, 81, 8, 1, 79, 61, 5, 15, 31, 59, 33, 20, 94, 23, 88, 69, 17, 35, 95, 40, 75, 85, 87, 43, 47, 89, 12, 71, 72, 9, 83, 67, 38, 90, 82, 29, 10, 37, 91, 97, 74, 6, 86, 30, 63, 24, 49, 3, 13, 4, 57, 76, 70, 98, 34, 58, 52, 96, 84, 25, 55, 44, 46, 18, 65, 62, 68, 48, 66, 41, 19, 14, 45, 16, 92], [96, 2, 76, 15, 31, 22, 71, 38, 88, 26, 67, 0, 21, 10, 58, 39, 63, 77, 23, 50, 48, 19, 78, 30, 93, 91, 14, 12, 28, 11, 53, 73, 60, 61, 35, 79, 57, 70, 40, 20, 56, 32, 65, 64, 68, 55, 82, 94, 45, 95, 66, 7, 43, 16, 37, 99, 72, 62, 13, 4, 47, 8, 98, 89, 46, 29, 51, 85, 74, 17, 97, 84, 33, 18, 25, 83, 69, 44, 87, 49, 92, 9, 90, 36, 41, 81, 59, 54, 34, 6, 42, 5, 80, 3, 1, 86, 27, 52, 24, 75]], [[61, 85, 41, 37, 12, 25, 27, 17, 92, 52, 44, 66, 13, 15, 19, 78, 77, 9, 40, 20, 58, 43, 76, 24, 59, 42, 28, 30, 96, 86, 62, 89, 67, 35, 45, 6, 36, 50, 74, 26, 84, 95, 7, 68, 97, 79, 51, 16, 80, 3, 82, 2, 93, 21, 31, 98, 18, 60, 39, 72, 54, 65, 73, 32, 5, 70, 99, 10, 14, 71, 34, 4, 64, 33, 55, 48, 75, 87, 57, 81, 22, 46, 69, 63, 90, 23, 38, 56, 8, 29, 83, 88, 11, 0, 47, 53, 91, 94, 49, 1], [35, 22, 67, 24, 94, 82, 12, 68, 30, 43, 58, 96, 27, 69, 65, 18, 7, 79, 14, 2, 44, 60, 54, 47, 51, 83, 89, 3, 20, 11, 40, 85, 37, 48, 49, 8, 73, 15, 90, 93, 70, 84, 34, 4, 86, 98, 17, 46, 72, 63, 26, 56, 97, 74, 91, 99, 36, 80, 81, 41, 53, 88, 77, 21, 0, 23, 64, 76, 95, 33, 62, 1, 75, 87, 10, 66, 57, 6, 28, 25, 42, 9, 50, 39, 29, 78, 13, 19, 31, 45, 55, 61, 38, 71, 59, 5, 92, 52, 32, 16], [70, 29, 50, 57, 31, 9, 59, 45, 87, 71, 98, 89, 77, 97, 86, 5, 21, 61, 47, 41, 76, 37, 65, 12, 58, 75, 78, 91, 27, 63, 64, 83, 55, 95, 4, 11, 82, 23, 39, 40, 42, 68, 69, 48, 54, 13, 99, 33, 26, 0, 66, 79, 46, 72, 49, 44, 25, 74, 73, 28, 38, 10, 2, 85, 15, 19, 52, 94, 7, 36, 17, 32, 96, 3, 20, 30, 92, 51, 14, 90, 24, 67, 43, 34, 93, 84, 62, 81, 1, 56, 53, 88, 80, 35, 22, 6, 18, 16, 8, 60], [62, 75, 38, 50, 52, 86, 53, 73, 99, 59, 26, 46, 48, 90, 64, 1, 65, 13, 30, 36, 57, 16, 97, 91, 51, 33, 80, 67, 5, 25, 8, 96, 94, 84, 6, 89, 88, 20, 10, 85, 41, 56, 9, 34, 63, 0, 18, 12, 42, 15, 87, 4, 61, 43, 82, 17, 98, 60, 92, 70, 49, 71, 47, 54, 79, 83, 27, 74, 58, 2, 76, 93, 22, 69, 78, 44, 28, 95, 72, 68, 7, 55, 29, 77, 14, 23, 31, 32, 3, 37, 11, 66, 35, 39, 24, 81, 19, 21, 40, 45], [65, 78, 7, 83, 80, 77, 71, 72, 22, 85, 63, 73, 44, 87, 86, 88, 41, 30, 95, 67, 58, 21, 4, 5, 26, 27, 75, 31, 99, 52, 92, 64, 28, 8, 19, 98, 84, 34, 9, 48, 60, 0, 56, 1, 62, 16, 91, 32, 89, 79, 40, 11, 54, 51, 43, 12, 45, 66, 23, 13, 96, 81, 47, 36, 46, 93, 2, 69, 33, 3, 20, 6, 35, 18, 74, 68, 53, 37, 29, 61, 90, 57, 14, 42, 24, 76, 10, 38, 97, 39, 25, 50, 49, 70, 55, 82, 15, 17, 59, 94], [11, 17, 21, 48, 63, 0, 73, 14, 10, 88, 2, 28, 6, 9, 19, 69, 81, 13, 35, 95, 60, 56, 30, 47, 91, 92, 86, 29, 76, 50, 16, 25, 82, 93, 8, 5, 58, 94, 98, 20, 74, 77, 4, 52, 97, 79, 27, 42, 36, 12, 26, 72, 71, 45, 24, 87, 1, 39, 22, 89, 53, 31, 61, 37, 33, 18, 59, 43, 80, 15, 54, 38, 78, 90, 32, 83, 23, 44, 55, 66, 64, 3, 67, 49, 84, 62, 51, 41, 46, 85, 96, 34, 99, 75, 7, 40, 70, 68, 65, 57], [43, 38, 48, 47, 74, 88, 64, 54, 59, 35, 29, 18, 53, 91, 68, 66, 12, 79, 55, 41, 34, 36, 33, 94, 56, 87, 25, 6, 1, 8, 63, 96, 93, 44, 40, 65, 13, 77, 61, 85, 99, 80, 11, 51, 14, 46, 5, 39, 92, 27, 75, 52, 72, 83, 19, 62, 32, 24, 82, 95, 17, 37, 0, 15, 97, 4, 42, 86, 84, 3, 26, 81, 98, 9, 73, 22, 28, 78, 60, 76, 16, 23, 71, 89, 31, 57, 58, 20, 50, 10, 49, 45, 21, 67, 90, 30, 7, 2, 70, 69], [78, 35, 94, 59, 8, 23, 10, 76, 96, 80, 93, 9, 27, 95, 61, 84, 88, 91, 2, 33, 1, 85, 3, 36, 79, 18, 92, 5, 34, 81, 98, 40, 38, 68, 90, 70, 89, 65, 48, 6, 77, 30, 72, 62, 16, 83, 32, 54, 99, 82, 75, 41, 53, 17, 67, 43, 28, 21, 46, 7, 25, 64, 13, 14, 87, 58, 63, 31, 39, 47, 24, 71, 74, 55, 20, 11, 22, 45, 44, 51, 37, 15, 42, 56, 66, 29, 86, 49, 69, 57, 73, 50, 60, 0, 4, 19, 52, 97, 12, 26], [88, 86, 41, 1, 70, 10, 19, 2, 11, 44, 87, 26, 53, 59, 40, 18, 61, 13, 21, 56, 85, 75, 25, 5, 97, 7, 36, 67, 76, 79, 90, 65, 91, 0, 98, 89, 20, 55, 93, 58, 28, 63, 6, 83, 47, 71, 73, 49, 39, 31, 48, 69, 82, 50, 78, 62, 14, 74, 29, 54, 57, 80, 32, 94, 3, 66, 30, 35, 8, 46, 16, 51, 24, 17, 4, 84, 45, 60, 34, 15, 72, 77, 42, 12, 64, 9, 33, 95, 81, 96, 38, 43, 52, 99, 22, 92, 27, 23, 37, 68], [90, 93, 83, 21, 96, 71, 67, 28, 37, 75, 55, 58, 56, 39, 16, 46, 91, 41, 77, 92, 99, 65, 27, 5, 24, 1, 69, 7, 31, 35, 33, 64, 12, 14, 45, 11, 98, 0, 57, 72, 74, 13, 61, 40, 63, 89, 53, 85, 18, 76, 2, 70, 20, 60, 84, 52, 25, 43, 54, 17, 36, 19, 97, 73, 94, 62, 3, 23, 34, 88, 29, 80, 50, 22, 81, 87, 6, 30, 78, 48, 79, 44, 66, 8, 51, 86, 4, 15, 42, 10, 82, 32, 68, 26, 38, 47, 49, 9, 95, 59]]]; +const ibtable2 = [[4, 7, 8, 0, 9, 1, 3, 6, 2, 5], [5, 0, 4, 1, 3, 8, 9, 7, 6, 2], [0, 2, 7, 8, 9, 1, 6, 3, 5, 4], [5, 9, 8, 4, 1, 7, 0, 2, 3, 6], [7, 3, 0, 1, 8, 9, 6, 5, 2, 4], [7, 2, 4, 0, 1, 9, 6, 3, 5, 8], [4, 1, 6, 3, 5, 8, 0, 7, 2, 9], [6, 8, 4, 1, 5, 3, 7, 2, 9, 0], [4, 1, 8, 3, 7, 2, 0, 6, 5, 9], [7, 4, 5, 6, 2, 3, 9, 1, 8, 0]]; + +type DecodedAccessCode = { revision: number; serial: number }; + +function crc16(data: BigInteger, bits: number, initial: number = 0xffff, poly: number = 0x8408): number { + let v = bigInt(initial); + let bigPoly = bigInt(poly); + + for (let i = 0; i < bits; i++) { + v = v.shiftRight(1).xor(bigPoly.and(v.xor(data).and(bigInt.one).equals(bigInt.one) ? bigPoly : bigInt.zero)); + data = data.shiftRight(1); + } + + return v.toJSNumber(); +} + +export function decodeAccessCode(code: string): DecodedAccessCode | null { + code = code.replace(/\D/i, ''); + + if (code.length !== 20) { + return null; + } + + let body = _.range(3, 15, 2).map(i => parseInt(code[i]) * 10 + parseInt(code[i + 1])); + let crc = _.range(15, 20).map(i => parseInt(code[i])); + + let boxes = [(crc[3] + crc[2]) % 10, crc[2], crc[3], crc[4], (crc[4] + crc[0]) % 10, crc[1]]; + + for (const [n, i] of body.entries()) { + body[n] = ibtable[4][boxes[n]][i]; + } + + const rv = body[0] >> 4; + + boxes = [(crc[1] + crc[0]) % 10, crc[1], crc[2], crc[3], crc[4], (crc[4] + crc[1]) % 10] + for (const [n, i] of body.entries()) { + if (n == 0) { + body[n] = (body[n] & 0xf0) | ibtable2[boxes[n]][body[n] & 0x0f]; + } else { + body[n] = ibtable[rv][boxes[n]][i]; + } + } + + const serial = body.map(i => i.toString().padStart(2, '0')).join('') + + if (crc16(bigInt(code.substring(0, 3) + serial, 16), 15 * 4) !== parseInt(code.substring(15, 20))) { + throw new Error(`CRC16 mismatch.`); + } + + return { revision: rv, serial: parseInt(serial) }; +} + +export function encodeAccessCode(company: number, serial: number, rev: number = 0): string { + if (company < 0 || company > 999) { + throw new Error(`Invalid company code`); + } + + if (serial < 0 || serial > 99999999999) { + throw new Error(`Invalid serial number`); + } + + if (rev < 0 || rev > sbtable.length) { + throw new Error(`Invalid revision`); + } + + const bodyStr = `${rev}${serial.toString().padStart(11, '0')}`; + const crc_i = crc16(bigInt(company.toString().padStart(3, '0') + bodyStr, 16), 15 * 4); + + const crc = _.range(5).map(i => Math.floor(crc_i / (10 ** (4 - i))) % 10); + const body = _.range(0, 12, 2).map(i => parseInt(bodyStr[i]) * 10 + parseInt(bodyStr[i + 1])); + + let boxes = [(crc[1] + crc[0]) % 10, crc[1], crc[2], crc[3], crc[4], (crc[4] + crc[1]) % 10]; + for (const [n, i] of body.entries()) { + if (n == 0) { + body[n] = (body[n] & 0xf0) | sbtable2[boxes[n]][body[n] & 0x0f]; + } else { + body[n] = sbtable[rev][boxes[n]][i]; + } + } + + boxes = [(crc[3] + crc[2]) % 10, crc[2], crc[3], crc[4], (crc[4] + crc[0]) % 10, crc[1]] + for (const [n, i] of body.entries()) { + body[n] = sbtable[4][boxes[n]][i]; + } + + return `${company.toString().padStart(3, '0')}${body.map(i => i.toString().padStart(2, '0')).join('')}${crc_i.toString().padStart(5, '0')}`; +} diff --git a/amnet-shared/src/card.ts b/amnet-shared/src/card.ts index 65956fb..8958321 100644 --- a/amnet-shared/src/card.ts +++ b/amnet-shared/src/card.ts @@ -1,23 +1,12 @@ +import {encodeAccessCode} from "./card-encoding"; + export const CARD_ID_REGEX = /^(?:[0-9]{4} ?){5}$/i; +export const AMNET_COMPANY_ID = 597; export function generateCardNumber(): string { - let cardId = ''; - - while (true) { - // don't start with a 3 - const firstDigit = Math.floor(Math.random() * 10); - - if (firstDigit !== 3) { - cardId = firstDigit.toString(); - break; - } - } - - for (let i = 0; i < 19; i++) { - cardId += Math.floor(Math.random() * 10).toString(); - } - - return cardId; + // serial between 5000 and 99999999999 + const serial = Math.floor(Math.random() * 99999994999) + 5000; + return encodeAccessCode(AMNET_COMPANY_ID, serial, 0); } /** diff --git a/amnet-shared/src/index.ts b/amnet-shared/src/index.ts index 66be498..35adf48 100644 --- a/amnet-shared/src/index.ts +++ b/amnet-shared/src/index.ts @@ -1,3 +1,5 @@ export * from './api'; export * from './card'; -export * from './gameIds'; \ No newline at end of file +export * from './gameIds'; + +export {decodeAccessCode as DecodeAMAccessCode, encodeAccessCode as EncodeAMAccessCode} from './card-encoding'; \ No newline at end of file diff --git a/amnet-shared/tsconfig.json b/amnet-shared/tsconfig.json index d543ba3..e29de38 100644 --- a/amnet-shared/tsconfig.json +++ b/amnet-shared/tsconfig.json @@ -1,14 +1,15 @@ { "compilerOptions": { - "target": "ES2016", + "baseUrl": ".", + "target": "ES2017", "module": "CommonJS", "noEmit": true, + "strict": true, + "skipLibCheck": true, "esModuleInterop": true, "forceConsistentCasingInFileNames": true, - "strict": true, - "noUncheckedIndexedAccess": true, - "skipLibCheck": true, - "baseUrl": "." }, - "include": ["src/**/*.ts"] + "include": [ + "src/**/*.ts" + ] } From 29b303cde8fef63061601c698033576a7c124b26 Mon Sep 17 00:00:00 2001 From: ppc Date: Tue, 14 Jan 2025 23:30:04 +0000 Subject: [PATCH 26/38] generate valid access codes on web --- amnet-web/package-lock.json | 5 +++++ amnet-web/src/components/card-creation-form.tsx | 5 ++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/amnet-web/package-lock.json b/amnet-web/package-lock.json index 6eb0eee..e3c5646 100644 --- a/amnet-web/package-lock.json +++ b/amnet-web/package-lock.json @@ -56,7 +56,12 @@ }, "../amnet-shared": { "version": "1.0.0", + "dependencies": { + "big-integer": "^1.6.52", + "lodash": "^4.17.21" + }, "devDependencies": { + "@types/lodash": "^4.17.14", "tsup": "^8.2.4", "typescript": "^5.5.4" } diff --git a/amnet-web/src/components/card-creation-form.tsx b/amnet-web/src/components/card-creation-form.tsx index 1d10b2a..11319d2 100644 --- a/amnet-web/src/components/card-creation-form.tsx +++ b/amnet-web/src/components/card-creation-form.tsx @@ -98,10 +98,9 @@ function AimeCardCreationForm(props: { callback?: () => void }) { - + If you've got a card, enter the matrix code here. If not, you can - form.setValue("id", generateCardNumber())}> + form.setValue("id", generateCardNumber())}> generate a new one. From 393965d2523e25c6ad25b5a0c96cbdbd0f2f1581 Mon Sep 17 00:00:00 2001 From: ppc Date: Tue, 14 Jan 2025 23:30:54 +0000 Subject: [PATCH 27/38] vulnerable package fix --- amnet-shared/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/amnet-shared/package-lock.json b/amnet-shared/package-lock.json index 8c546c6..525862c 100644 --- a/amnet-shared/package-lock.json +++ b/amnet-shared/package-lock.json @@ -984,9 +984,9 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "license": "MIT", "dependencies": { From 73d333cc232a3925f9220667e1a3fcc50a3cbbb2 Mon Sep 17 00:00:00 2001 From: ppc Date: Tue, 14 Jan 2025 23:57:36 +0000 Subject: [PATCH 28/38] validate cards when manually entering them --- amnet-native/app/card/ManualCardEntry.tsx | 40 ++++++++++++++++++++--- amnet-native/metro.config.js | 3 +- amnet-native/package-lock.json | 6 ++++ amnet-native/package.json | 1 + 4 files changed, 44 insertions(+), 6 deletions(-) diff --git a/amnet-native/app/card/ManualCardEntry.tsx b/amnet-native/app/card/ManualCardEntry.tsx index 92ad58d..ed8b34c 100644 --- a/amnet-native/app/card/ManualCardEntry.tsx +++ b/amnet-native/app/card/ManualCardEntry.tsx @@ -1,6 +1,6 @@ -import {generateCardNumber} from "amnet-shared"; +import {generateCardNumber, DecodeAMAccessCode} from "amnet-shared"; import {useCallback, useEffect, useLayoutEffect, useState} from "react"; -import {Platform, TextInput, TouchableOpacity, View} from "react-native"; +import {Alert, Platform, TextInput, TouchableOpacity, View} from "react-native"; import {Button, Card, Divider, Paragraph, useTheme} from "react-native-paper"; import {AMCard} from "../../models/AMCard"; @@ -18,7 +18,37 @@ export default function CardAddModal({navigation, route}) { const [cardName, setCardName] = useState(''); const [cardNumber, setCardNumber] = useState(''); - const cardAddCallback = useCallback((cardId: string, cardName: string) => { + const cardAddCallback = useCallback(async (cardId: string, cardName: string) => { + try { + // validate AM card numbers + cardId.startsWith('5') && DecodeAMAccessCode(cardId); + } catch { + const shouldContinue = await new Promise(done => { + Alert.alert( + "Invalid Access Code", + "It looks like the access code isn't from an Amusement IC card. Please double-check the code, and if it's correct, note that some networks might still reject it.", + [ + { + style: "destructive", + text: "Continue Anyway", + onPress: () => done(true) + }, + { + style: "cancel", + text: "Cancel", + onPress: () => done(false) + } + ], { + cancelable: true, + onDismiss: () => done(false) + }); + }) + + if (!shouldContinue) { + return; + } + } + const duplicateCard = realm.objects(AMCard.schema.name).find(x => x.code === cardId); if (duplicateCard) { @@ -43,7 +73,8 @@ export default function CardAddModal({navigation, route}) { const themeOptions = canSave ? modalStyles.headerText : {color: theme.colors.outline}; navigation.setOptions({ - headerRight: () => cardAddCallback(cardNumber, cardName)}> + headerRight: () => cardAddCallback(cardNumber, cardName)}> Save }) @@ -90,7 +121,6 @@ export default function CardAddModal({navigation, route}) { - {/* only show card entry info when in create mode */} ) diff --git a/amnet-native/metro.config.js b/amnet-native/metro.config.js index 492244c..06fa400 100644 --- a/amnet-native/metro.config.js +++ b/amnet-native/metro.config.js @@ -6,7 +6,8 @@ module.exports = (async () => { const config = await getDefaultConfig(__dirname); config.resolver.extraNodeModules = { - 'amnet-shared': resolve(__dirname, '../amnet-shared') + 'amnet-shared': resolve(__dirname, '../amnet-shared'), + '@babel/runtime': resolve(__dirname, 'node_modules/@babel/runtime') }; config.watchFolders = [ diff --git a/amnet-native/package-lock.json b/amnet-native/package-lock.json index 7fba1f4..df113e0 100644 --- a/amnet-native/package-lock.json +++ b/amnet-native/package-lock.json @@ -9,6 +9,7 @@ "version": "1.1.0", "hasInstallScript": true, "dependencies": { + "@babel/runtime": "^7.26.0", "@expo-google-fonts/jetbrains-mono": "^0.2.3", "@react-navigation/bottom-tabs": "^6.6.1", "@react-navigation/native": "^6.1.18", @@ -47,7 +48,12 @@ }, "../amnet-shared": { "version": "1.0.0", + "dependencies": { + "big-integer": "^1.6.52", + "lodash": "^4.17.21" + }, "devDependencies": { + "@types/lodash": "^4.17.14", "tsup": "^8.2.4", "typescript": "^5.5.4" } diff --git a/amnet-native/package.json b/amnet-native/package.json index 481af55..3ea5570 100644 --- a/amnet-native/package.json +++ b/amnet-native/package.json @@ -9,6 +9,7 @@ "postinstall": "patch-package" }, "dependencies": { + "@babel/runtime": "^7.26.0", "@expo-google-fonts/jetbrains-mono": "^0.2.3", "@react-navigation/bottom-tabs": "^6.6.1", "@react-navigation/native": "^6.1.18", From 16c0ff54c67aec7ecbe4773f368925be6c8de9b6 Mon Sep 17 00:00:00 2001 From: ppc Date: Wed, 15 Jan 2025 00:19:03 +0000 Subject: [PATCH 29/38] add message for bootleg ic cards --- amnet-native/components/ICCard.tsx | 4 +++- amnet-native/models/AMCard.ts | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/amnet-native/components/ICCard.tsx b/amnet-native/components/ICCard.tsx index 32eb260..4c5e46d 100644 --- a/amnet-native/components/ICCard.tsx +++ b/amnet-native/components/ICCard.tsx @@ -54,7 +54,9 @@ function PhysicalICInfo(props: { physicalCard: AMPhysicalCardData | null }) { return ( - {ICVendorName(props.physicalCard.cardVendor)} + + {props.physicalCard.isBootlegAmusementCard ? "Bootleg " : null}{ICVendorName(props.physicalCard.cardVendor)} + ); } diff --git a/amnet-native/models/AMCard.ts b/amnet-native/models/AMCard.ts index c46412a..430eeb1 100644 --- a/amnet-native/models/AMCard.ts +++ b/amnet-native/models/AMCard.ts @@ -63,6 +63,11 @@ export class AMPhysicalCardData extends Realm.Object { cardType: ICType; cardVendor: AmusementICVendor | null; + get isBootlegAmusementCard() { + // real cards have an IDm manufacturer code of 012E (first two bytes of the IDm or in this case, the first 4 characters) + return this.cardType === ICType.Amusement && !this.cardIDm.startsWith("012E"); + } + static schema: Realm.ObjectSchema = { name: "physicalCard", primaryKey: "cardIDm", From 874ba06e29f1337db7ac52bb0122f2ae203d4781 Mon Sep 17 00:00:00 2001 From: ppc Date: Wed, 15 Jan 2025 11:13:21 +0000 Subject: [PATCH 30/38] fix logic check --- AMNet.Server/CardPresenter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AMNet.Server/CardPresenter.cs b/AMNet.Server/CardPresenter.cs index e4f248c..b2d18fe 100644 --- a/AMNet.Server/CardPresenter.cs +++ b/AMNet.Server/CardPresenter.cs @@ -36,7 +36,7 @@ internal class CardPresenter lock (_cardLock) { // IDm and requireIDm are mutually exclusive - if (_currentCard == null || (requireIDm.HasValue && (_currentCard.IDm == null) ^ requireIDm.Value)) + if (_currentCard == null || (requireIDm.HasValue && (_currentCard.IDm != null) ^ requireIDm.Value)) { return null; } From 13cee9a4311388d7bace521fcfe34c7ce94c552c Mon Sep 17 00:00:00 2001 From: ppc Date: Wed, 15 Jan 2025 11:13:52 +0000 Subject: [PATCH 31/38] update logging output --- AMNet.Server/Config.cs | 3 +++ AMNet.Server/DllMain.cs | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/AMNet.Server/Config.cs b/AMNet.Server/Config.cs index 482c9bf..0018478 100644 --- a/AMNet.Server/Config.cs +++ b/AMNet.Server/Config.cs @@ -1,4 +1,5 @@ using System; +using System.Reflection; using Windows.Win32; using Windows.Win32.Foundation; @@ -41,6 +42,8 @@ internal static class Config /// public static string AimeTxtPath { get; } + public static Version ServerVersion { get; } = new(Assembly.GetExecutingAssembly().GetCustomAttribute()?.Version ?? "1.0"); + internal static unsafe string ReadKey(string section, string key, uint maxLength, string @default = null) { // +1 for null terminator diff --git a/AMNet.Server/DllMain.cs b/AMNet.Server/DllMain.cs index cda2438..248ded4 100644 --- a/AMNet.Server/DllMain.cs +++ b/AMNet.Server/DllMain.cs @@ -36,7 +36,7 @@ public static class DllMain CancellationTokenRegistration? cancellationRegistration = null; cancellationRegistration = App.Lifetime.ApplicationStarted.Register(() => { - App.Logger.LogInformation("AMNet Server ({gameId}) started successfully.", Config.GameId ?? "SXXX"); + App.Logger.LogInformation("AMNet Server v{version} ({gameId}) started.", Config.ServerVersion.ToString(Config.ServerVersion.Build > 0 ? 3 : 2), Config.GameId ?? "SXXX"); App.Logger.LogInformation("Visit {addr} from a mobile device on the same network to get started.", WebAddress); // ReSharper disable once AccessToModifiedClosure @@ -125,7 +125,7 @@ public static class DllMain } Marshal.Copy(card.IDm, 0, idm, sizeof(ulong)); - App.Logger.LogInformation("FeliCa IDm read: {idm}", card.IDm); + App.Logger.LogInformation("FeliCa IDm read: {idm}", CardPresenter.FormatHexCode(card.IDm)); return 0; } From bc48917fe838e7ebfde6eaa26eed718e08220a76 Mon Sep 17 00:00:00 2001 From: ppc Date: Wed, 15 Jan 2025 11:25:30 +0000 Subject: [PATCH 32/38] fix card scanning using previously selected card --- amnet-native/app/ServerCardIn.tsx | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/amnet-native/app/ServerCardIn.tsx b/amnet-native/app/ServerCardIn.tsx index cbfaad9..3305fd9 100644 --- a/amnet-native/app/ServerCardIn.tsx +++ b/amnet-native/app/ServerCardIn.tsx @@ -150,20 +150,21 @@ export default function ServerCardIn({route, navigation}) { } }, [serverId, serverReloadCounter, realm]); - const cardInCallback = useCallback(() => { + const cardInCallback = useCallback((card: AMCard) => { setCardInCompleted(false); const abortController = new AbortController(); performCardIn(server, { - cardId: selectedCard.code, - physicalCardIDm: selectedCard.physicalCardData?.cardIDm ?? undefined + cardId: card.code, + physicalCardIDm: card.physicalCardData?.cardIDm ?? undefined }, abortController.signal).then(r => { const now = new Date(); realm.write(() => { server.lastUsed = now; - selectedCard.lastUsed = now; - selectedCard.lastUsedGameId = server.gameId; + + card.lastUsed = now; + card.lastUsedGameId = server.gameId; }); setCardInCompleted(r); @@ -175,7 +176,7 @@ export default function ServerCardIn({route, navigation}) { setTimeout(() => abortController.abort(), 5000); return () => abortController.abort(); - }, [server, selectedCard]); + }, [server]); const performPhysicalCardIn = useCallback(async () => { const card = await felicaCardReadAsync(); @@ -188,7 +189,7 @@ export default function ServerCardIn({route, navigation}) { if (savedCard) { setUserSelectedCard(savedCard); - cardInCallback(); + cardInCallback(savedCard); return; } @@ -244,11 +245,11 @@ export default function ServerCardIn({route, navigation}) {