15317bbafSopenharmony_ci/*
25317bbafSopenharmony_ci * Fast QR Code generator library
35317bbafSopenharmony_ci *
45317bbafSopenharmony_ci * Copyright (c) Project Nayuki. (MIT License)
55317bbafSopenharmony_ci * https://www.nayuki.io/page/fast-qr-code-generator-library
65317bbafSopenharmony_ci *
75317bbafSopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a copy of
85317bbafSopenharmony_ci * this software and associated documentation files (the "Software"), to deal in
95317bbafSopenharmony_ci * the Software without restriction, including without limitation the rights to
105317bbafSopenharmony_ci * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
115317bbafSopenharmony_ci * the Software, and to permit persons to whom the Software is furnished to do so,
125317bbafSopenharmony_ci * subject to the following conditions:
135317bbafSopenharmony_ci * - The above copyright notice and this permission notice shall be included in
145317bbafSopenharmony_ci *   all copies or substantial portions of the Software.
155317bbafSopenharmony_ci * - The Software is provided "as is", without warranty of any kind, express or
165317bbafSopenharmony_ci *   implied, including but not limited to the warranties of merchantability,
175317bbafSopenharmony_ci *   fitness for a particular purpose and noninfringement. In no event shall the
185317bbafSopenharmony_ci *   authors or copyright holders be liable for any claim, damages or other
195317bbafSopenharmony_ci *   liability, whether in an action of contract, tort or otherwise, arising from,
205317bbafSopenharmony_ci *   out of or in connection with the Software or the use or other dealings in the
215317bbafSopenharmony_ci *   Software.
225317bbafSopenharmony_ci */
235317bbafSopenharmony_ci
245317bbafSopenharmony_cipackage io.nayuki.fastqrcodegen;
255317bbafSopenharmony_ci
265317bbafSopenharmony_ciimport java.nio.charset.StandardCharsets;
275317bbafSopenharmony_ciimport java.util.ArrayList;
285317bbafSopenharmony_ciimport java.util.Arrays;
295317bbafSopenharmony_ciimport java.util.Base64;
305317bbafSopenharmony_ciimport java.util.List;
315317bbafSopenharmony_ciimport java.util.Objects;
325317bbafSopenharmony_ciimport io.nayuki.fastqrcodegen.QrSegment.Mode;
335317bbafSopenharmony_ci
345317bbafSopenharmony_ci
355317bbafSopenharmony_ci/**
365317bbafSopenharmony_ci * Splits text into optimal segments and encodes kanji segments.
375317bbafSopenharmony_ci * Provides static functions only; not instantiable.
385317bbafSopenharmony_ci * @see QrSegment
395317bbafSopenharmony_ci * @see QrCode
405317bbafSopenharmony_ci */
415317bbafSopenharmony_cipublic final class QrSegmentAdvanced {
425317bbafSopenharmony_ci
435317bbafSopenharmony_ci	/*---- Optimal list of segments encoder ----*/
445317bbafSopenharmony_ci
455317bbafSopenharmony_ci	/**
465317bbafSopenharmony_ci	 * Returns a list of zero or more segments to represent the specified Unicode text string.
475317bbafSopenharmony_ci	 * The resulting list optimally minimizes the total encoded bit length, subjected to the constraints
485317bbafSopenharmony_ci	 * in the specified {error correction level, minimum version number, maximum version number}.
495317bbafSopenharmony_ci	 * <p>This function can utilize all four text encoding modes: numeric, alphanumeric, byte (UTF-8),
505317bbafSopenharmony_ci	 * and kanji. This can be considered as a sophisticated but slower replacement for {@link
515317bbafSopenharmony_ci	 * QrSegment#makeSegments(String)}. This requires more input parameters because it searches a
525317bbafSopenharmony_ci	 * range of versions, like {@link QrCode#encodeSegments(List,QrCode.Ecc,int,int,int,boolean)}.</p>
535317bbafSopenharmony_ci	 * @param text the text to be encoded (not {@code null}), which can be any Unicode string
545317bbafSopenharmony_ci	 * @param ecl the error correction level to use (not {@code null})
555317bbafSopenharmony_ci	 * @param minVersion the minimum allowed version of the QR Code (at least 1)
565317bbafSopenharmony_ci	 * @param maxVersion the maximum allowed version of the QR Code (at most 40)
575317bbafSopenharmony_ci	 * @return a new mutable list (not {@code null}) of segments (not {@code null})
585317bbafSopenharmony_ci	 * containing the text, minimizing the bit length with respect to the constraints
595317bbafSopenharmony_ci	 * @throws NullPointerException if the text or error correction level is {@code null}
605317bbafSopenharmony_ci	 * @throws IllegalArgumentException if 1 &#x2264; minVersion &#x2264; maxVersion &#x2264; 40 is violated
615317bbafSopenharmony_ci	 * @throws DataTooLongException if the text fails to fit in the maxVersion QR Code at the ECL
625317bbafSopenharmony_ci	 */
635317bbafSopenharmony_ci	public static List<QrSegment> makeSegmentsOptimally(String text, QrCode.Ecc ecl, int minVersion, int maxVersion) {
645317bbafSopenharmony_ci		// Check arguments
655317bbafSopenharmony_ci		Objects.requireNonNull(text);
665317bbafSopenharmony_ci		Objects.requireNonNull(ecl);
675317bbafSopenharmony_ci		if (!(QrCode.MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= QrCode.MAX_VERSION))
685317bbafSopenharmony_ci			throw new IllegalArgumentException("Invalid value");
695317bbafSopenharmony_ci
705317bbafSopenharmony_ci		// Iterate through version numbers, and make tentative segments
715317bbafSopenharmony_ci		List<QrSegment> segs = null;
725317bbafSopenharmony_ci		int[] codePoints = toCodePoints(text);
735317bbafSopenharmony_ci		for (int version = minVersion; ; version++) {
745317bbafSopenharmony_ci			if (version == minVersion || version == 10 || version == 27)
755317bbafSopenharmony_ci				segs = makeSegmentsOptimally(codePoints, version);
765317bbafSopenharmony_ci			assert segs != null;
775317bbafSopenharmony_ci
785317bbafSopenharmony_ci			// Check if the segments fit
795317bbafSopenharmony_ci			int dataCapacityBits = QrCode.getNumDataCodewords(version, ecl) * 8;  // Number of data bits available
805317bbafSopenharmony_ci			int dataUsedBits = QrSegment.getTotalBits(segs, version);
815317bbafSopenharmony_ci			if (dataUsedBits != -1 && dataUsedBits <= dataCapacityBits)
825317bbafSopenharmony_ci				return segs;  // This version number is found to be suitable
835317bbafSopenharmony_ci			if (version >= maxVersion) {  // All versions in the range could not fit the given text
845317bbafSopenharmony_ci				String msg = "Segment too long";
855317bbafSopenharmony_ci				if (dataUsedBits != -1)
865317bbafSopenharmony_ci					msg = String.format("Data length = %d bits, Max capacity = %d bits", dataUsedBits, dataCapacityBits);
875317bbafSopenharmony_ci				throw new DataTooLongException(msg);
885317bbafSopenharmony_ci			}
895317bbafSopenharmony_ci		}
905317bbafSopenharmony_ci	}
915317bbafSopenharmony_ci
925317bbafSopenharmony_ci
935317bbafSopenharmony_ci	// Returns a new list of segments that is optimal for the given text at the given version number.
945317bbafSopenharmony_ci	private static List<QrSegment> makeSegmentsOptimally(int[] codePoints, int version) {
955317bbafSopenharmony_ci		if (codePoints.length == 0)
965317bbafSopenharmony_ci			return new ArrayList<>();
975317bbafSopenharmony_ci		Mode[] charModes = computeCharacterModes(codePoints, version);
985317bbafSopenharmony_ci		return splitIntoSegments(codePoints, charModes);
995317bbafSopenharmony_ci	}
1005317bbafSopenharmony_ci
1015317bbafSopenharmony_ci
1025317bbafSopenharmony_ci	// Returns a new array representing the optimal mode per code point based on the given text and version.
1035317bbafSopenharmony_ci	private static Mode[] computeCharacterModes(int[] codePoints, int version) {
1045317bbafSopenharmony_ci		if (codePoints.length == 0)
1055317bbafSopenharmony_ci			throw new IllegalArgumentException();
1065317bbafSopenharmony_ci		final Mode[] modeTypes = {Mode.BYTE, Mode.ALPHANUMERIC, Mode.NUMERIC, Mode.KANJI};  // Do not modify
1075317bbafSopenharmony_ci		final int numModes = modeTypes.length;
1085317bbafSopenharmony_ci
1095317bbafSopenharmony_ci		// Segment header sizes, measured in 1/6 bits
1105317bbafSopenharmony_ci		final int[] headCosts = new int[numModes];
1115317bbafSopenharmony_ci		for (int i = 0; i < numModes; i++)
1125317bbafSopenharmony_ci			headCosts[i] = (4 + modeTypes[i].numCharCountBits(version)) * 6;
1135317bbafSopenharmony_ci
1145317bbafSopenharmony_ci		// charModes[i][j] represents the mode to encode the code point at
1155317bbafSopenharmony_ci		// index i such that the final segment ends in modeTypes[j] and the
1165317bbafSopenharmony_ci		// total number of bits is minimized over all possible choices
1175317bbafSopenharmony_ci		Mode[][] charModes = new Mode[codePoints.length][numModes];
1185317bbafSopenharmony_ci
1195317bbafSopenharmony_ci		// At the beginning of each iteration of the loop below,
1205317bbafSopenharmony_ci		// prevCosts[j] is the exact minimum number of 1/6 bits needed to
1215317bbafSopenharmony_ci		// encode the entire string prefix of length i, and end in modeTypes[j]
1225317bbafSopenharmony_ci		int[] prevCosts = headCosts.clone();
1235317bbafSopenharmony_ci
1245317bbafSopenharmony_ci		// Calculate costs using dynamic programming
1255317bbafSopenharmony_ci		for (int i = 0; i < codePoints.length; i++) {
1265317bbafSopenharmony_ci			int c = codePoints[i];
1275317bbafSopenharmony_ci			int[] curCosts = new int[numModes];
1285317bbafSopenharmony_ci			{  // Always extend a byte mode segment
1295317bbafSopenharmony_ci				curCosts[0] = prevCosts[0] + countUtf8Bytes(c) * 8 * 6;
1305317bbafSopenharmony_ci				charModes[i][0] = modeTypes[0];
1315317bbafSopenharmony_ci			}
1325317bbafSopenharmony_ci			// Extend a segment if possible
1335317bbafSopenharmony_ci			if (QrSegment.ALPHANUMERIC_MAP[c] != -1) {  // Is alphanumeric
1345317bbafSopenharmony_ci				curCosts[1] = prevCosts[1] + 33;  // 5.5 bits per alphanumeric char
1355317bbafSopenharmony_ci				charModes[i][1] = modeTypes[1];
1365317bbafSopenharmony_ci			}
1375317bbafSopenharmony_ci			if ('0' <= c && c <= '9') {  // Is numeric
1385317bbafSopenharmony_ci				curCosts[2] = prevCosts[2] + 20;  // 3.33 bits per digit
1395317bbafSopenharmony_ci				charModes[i][2] = modeTypes[2];
1405317bbafSopenharmony_ci			}
1415317bbafSopenharmony_ci			if (isKanji(c)) {
1425317bbafSopenharmony_ci				curCosts[3] = prevCosts[3] + 78;  // 13 bits per Shift JIS char
1435317bbafSopenharmony_ci				charModes[i][3] = modeTypes[3];
1445317bbafSopenharmony_ci			}
1455317bbafSopenharmony_ci
1465317bbafSopenharmony_ci			// Start new segment at the end to switch modes
1475317bbafSopenharmony_ci			for (int j = 0; j < numModes; j++) {  // To mode
1485317bbafSopenharmony_ci				for (int k = 0; k < numModes; k++) {  // From mode
1495317bbafSopenharmony_ci					int newCost = (curCosts[k] + 5) / 6 * 6 + headCosts[j];
1505317bbafSopenharmony_ci					if (charModes[i][k] != null && (charModes[i][j] == null || newCost < curCosts[j])) {
1515317bbafSopenharmony_ci						curCosts[j] = newCost;
1525317bbafSopenharmony_ci						charModes[i][j] = modeTypes[k];
1535317bbafSopenharmony_ci					}
1545317bbafSopenharmony_ci				}
1555317bbafSopenharmony_ci			}
1565317bbafSopenharmony_ci
1575317bbafSopenharmony_ci			prevCosts = curCosts;
1585317bbafSopenharmony_ci		}
1595317bbafSopenharmony_ci
1605317bbafSopenharmony_ci		// Find optimal ending mode
1615317bbafSopenharmony_ci		Mode curMode = null;
1625317bbafSopenharmony_ci		for (int i = 0, minCost = 0; i < numModes; i++) {
1635317bbafSopenharmony_ci			if (curMode == null || prevCosts[i] < minCost) {
1645317bbafSopenharmony_ci				minCost = prevCosts[i];
1655317bbafSopenharmony_ci				curMode = modeTypes[i];
1665317bbafSopenharmony_ci			}
1675317bbafSopenharmony_ci		}
1685317bbafSopenharmony_ci
1695317bbafSopenharmony_ci		// Get optimal mode for each code point by tracing backwards
1705317bbafSopenharmony_ci		Mode[] result = new Mode[charModes.length];
1715317bbafSopenharmony_ci		for (int i = result.length - 1; i >= 0; i--) {
1725317bbafSopenharmony_ci			for (int j = 0; j < numModes; j++) {
1735317bbafSopenharmony_ci				if (modeTypes[j] == curMode) {
1745317bbafSopenharmony_ci					curMode = charModes[i][j];
1755317bbafSopenharmony_ci					result[i] = curMode;
1765317bbafSopenharmony_ci					break;
1775317bbafSopenharmony_ci				}
1785317bbafSopenharmony_ci			}
1795317bbafSopenharmony_ci		}
1805317bbafSopenharmony_ci		return result;
1815317bbafSopenharmony_ci	}
1825317bbafSopenharmony_ci
1835317bbafSopenharmony_ci
1845317bbafSopenharmony_ci	// Returns a new list of segments based on the given text and modes, such that
1855317bbafSopenharmony_ci	// consecutive code points in the same mode are put into the same segment.
1865317bbafSopenharmony_ci	private static List<QrSegment> splitIntoSegments(int[] codePoints, Mode[] charModes) {
1875317bbafSopenharmony_ci		if (codePoints.length == 0)
1885317bbafSopenharmony_ci			throw new IllegalArgumentException();
1895317bbafSopenharmony_ci		List<QrSegment> result = new ArrayList<>();
1905317bbafSopenharmony_ci
1915317bbafSopenharmony_ci		// Accumulate run of modes
1925317bbafSopenharmony_ci		Mode curMode = charModes[0];
1935317bbafSopenharmony_ci		int start = 0;
1945317bbafSopenharmony_ci		for (int i = 1; ; i++) {
1955317bbafSopenharmony_ci			if (i < codePoints.length && charModes[i] == curMode)
1965317bbafSopenharmony_ci				continue;
1975317bbafSopenharmony_ci			String s = new String(codePoints, start, i - start);
1985317bbafSopenharmony_ci			if (curMode == Mode.BYTE)
1995317bbafSopenharmony_ci				result.add(QrSegment.makeBytes(s.getBytes(StandardCharsets.UTF_8)));
2005317bbafSopenharmony_ci			else if (curMode == Mode.NUMERIC)
2015317bbafSopenharmony_ci				result.add(QrSegment.makeNumeric(s));
2025317bbafSopenharmony_ci			else if (curMode == Mode.ALPHANUMERIC)
2035317bbafSopenharmony_ci				result.add(QrSegment.makeAlphanumeric(s));
2045317bbafSopenharmony_ci			else if (curMode == Mode.KANJI)
2055317bbafSopenharmony_ci				result.add(makeKanji(s));
2065317bbafSopenharmony_ci			else
2075317bbafSopenharmony_ci				throw new AssertionError();
2085317bbafSopenharmony_ci			if (i >= codePoints.length)
2095317bbafSopenharmony_ci				return result;
2105317bbafSopenharmony_ci			curMode = charModes[i];
2115317bbafSopenharmony_ci			start = i;
2125317bbafSopenharmony_ci		}
2135317bbafSopenharmony_ci	}
2145317bbafSopenharmony_ci
2155317bbafSopenharmony_ci
2165317bbafSopenharmony_ci	// Returns a new array of Unicode code points (effectively
2175317bbafSopenharmony_ci	// UTF-32 / UCS-4) representing the given UTF-16 string.
2185317bbafSopenharmony_ci	private static int[] toCodePoints(String s) {
2195317bbafSopenharmony_ci		int[] result = s.codePoints().toArray();
2205317bbafSopenharmony_ci		for (int c : result) {
2215317bbafSopenharmony_ci			if (Character.isSurrogate((char)c))
2225317bbafSopenharmony_ci				throw new IllegalArgumentException("Invalid UTF-16 string");
2235317bbafSopenharmony_ci		}
2245317bbafSopenharmony_ci		return result;
2255317bbafSopenharmony_ci	}
2265317bbafSopenharmony_ci
2275317bbafSopenharmony_ci
2285317bbafSopenharmony_ci	// Returns the number of UTF-8 bytes needed to encode the given Unicode code point.
2295317bbafSopenharmony_ci	private static int countUtf8Bytes(int cp) {
2305317bbafSopenharmony_ci		if      (cp <        0) throw new IllegalArgumentException("Invalid code point");
2315317bbafSopenharmony_ci		else if (cp <     0x80) return 1;
2325317bbafSopenharmony_ci		else if (cp <    0x800) return 2;
2335317bbafSopenharmony_ci		else if (cp <  0x10000) return 3;
2345317bbafSopenharmony_ci		else if (cp < 0x110000) return 4;
2355317bbafSopenharmony_ci		else                    throw new IllegalArgumentException("Invalid code point");
2365317bbafSopenharmony_ci	}
2375317bbafSopenharmony_ci
2385317bbafSopenharmony_ci
2395317bbafSopenharmony_ci
2405317bbafSopenharmony_ci	/*---- Kanji mode segment encoder ----*/
2415317bbafSopenharmony_ci
2425317bbafSopenharmony_ci	/**
2435317bbafSopenharmony_ci	 * Returns a segment representing the specified text string encoded in kanji mode.
2445317bbafSopenharmony_ci	 * Broadly speaking, the set of encodable characters are {kanji used in Japan,
2455317bbafSopenharmony_ci	 * hiragana, katakana, East Asian punctuation, full-width ASCII, Greek, Cyrillic}.
2465317bbafSopenharmony_ci	 * Examples of non-encodable characters include {ordinary ASCII, half-width katakana,
2475317bbafSopenharmony_ci	 * more extensive Chinese hanzi}.
2485317bbafSopenharmony_ci	 * @param text the text (not {@code null}), with only certain characters allowed
2495317bbafSopenharmony_ci	 * @return a segment (not {@code null}) containing the text
2505317bbafSopenharmony_ci	 * @throws NullPointerException if the string is {@code null}
2515317bbafSopenharmony_ci	 * @throws IllegalArgumentException if the string contains non-encodable characters
2525317bbafSopenharmony_ci	 * @see #isEncodableAsKanji(String)
2535317bbafSopenharmony_ci	 */
2545317bbafSopenharmony_ci	public static QrSegment makeKanji(String text) {
2555317bbafSopenharmony_ci		Objects.requireNonNull(text);
2565317bbafSopenharmony_ci		BitBuffer bb = new BitBuffer();
2575317bbafSopenharmony_ci		text.chars().forEachOrdered(c -> {
2585317bbafSopenharmony_ci			int val = UNICODE_TO_QR_KANJI[c];
2595317bbafSopenharmony_ci			if (val == -1)
2605317bbafSopenharmony_ci				throw new IllegalArgumentException("String contains non-kanji-mode characters");
2615317bbafSopenharmony_ci			bb.appendBits(val, 13);
2625317bbafSopenharmony_ci		});
2635317bbafSopenharmony_ci		return new QrSegment(Mode.KANJI, text.length(), bb.data, bb.bitLength);
2645317bbafSopenharmony_ci	}
2655317bbafSopenharmony_ci
2665317bbafSopenharmony_ci
2675317bbafSopenharmony_ci	/**
2685317bbafSopenharmony_ci	 * Tests whether the specified string can be encoded as a segment in kanji mode.
2695317bbafSopenharmony_ci	 * Broadly speaking, the set of encodable characters are {kanji used in Japan,
2705317bbafSopenharmony_ci	 * hiragana, katakana, East Asian punctuation, full-width ASCII, Greek, Cyrillic}.
2715317bbafSopenharmony_ci	 * Examples of non-encodable characters include {ordinary ASCII, half-width katakana,
2725317bbafSopenharmony_ci	 * more extensive Chinese hanzi}.
2735317bbafSopenharmony_ci	 * @param text the string to test for encodability (not {@code null})
2745317bbafSopenharmony_ci	 * @return {@code true} iff each character is in the kanji mode character set
2755317bbafSopenharmony_ci	 * @throws NullPointerException if the string is {@code null}
2765317bbafSopenharmony_ci	 * @see #makeKanji(String)
2775317bbafSopenharmony_ci	 */
2785317bbafSopenharmony_ci	public static boolean isEncodableAsKanji(String text) {
2795317bbafSopenharmony_ci		Objects.requireNonNull(text);
2805317bbafSopenharmony_ci		return text.chars().allMatch(
2815317bbafSopenharmony_ci			c -> isKanji((char)c));
2825317bbafSopenharmony_ci	}
2835317bbafSopenharmony_ci
2845317bbafSopenharmony_ci
2855317bbafSopenharmony_ci	private static boolean isKanji(int c) {
2865317bbafSopenharmony_ci		return c < UNICODE_TO_QR_KANJI.length && UNICODE_TO_QR_KANJI[c] != -1;
2875317bbafSopenharmony_ci	}
2885317bbafSopenharmony_ci
2895317bbafSopenharmony_ci
2905317bbafSopenharmony_ci	// Data derived from ftp://ftp.unicode.org/Public/MAPPINGS/OBSOLETE/EASTASIA/JIS/SHIFTJIS.TXT
2915317bbafSopenharmony_ci	private static final String PACKED_QR_KANJI_TO_UNICODE =
2925317bbafSopenharmony_ci		"MAAwATAC/wz/DjD7/xr/G/8f/wEwmzCcALT/QACo/z7/4/8/MP0w/jCdMJ4wA07dMAUwBjAHMPwgFSAQ/w8AXDAcIBb/XCAmICUgGCAZIBwgHf8I/wkwFDAV/zv/Pf9b/10wCDAJMAowCzAMMA0wDjAPMBAwEf8LIhIAsQDX//8A9/8dImD/HP8eImYiZyIeIjQmQiZA" +
2935317bbafSopenharmony_ci		"ALAgMiAzIQP/5f8EAKIAo/8F/wP/Bv8K/yAApyYGJgUlyyXPJc4lxyXGJaEloCWzJbIlvSW8IDswEiGSIZAhkSGTMBP/////////////////////////////IggiCyKGIocigiKDIioiKf////////////////////8iJyIoAKwh0iHUIgAiA///////////////////" +
2945317bbafSopenharmony_ci		"//////////8iICKlIxIiAiIHImEiUiJqImsiGiI9Ih0iNSIrIiz//////////////////yErIDAmbyZtJmogICAhALb//////////yXv/////////////////////////////////////////////////xD/Ef8S/xP/FP8V/xb/F/8Y/xn///////////////////8h" +
2955317bbafSopenharmony_ci		"/yL/I/8k/yX/Jv8n/yj/Kf8q/yv/LP8t/y7/L/8w/zH/Mv8z/zT/Nf82/zf/OP85/zr///////////////////9B/0L/Q/9E/0X/Rv9H/0j/Sf9K/0v/TP9N/07/T/9Q/1H/Uv9T/1T/Vf9W/1f/WP9Z/1r//////////zBBMEIwQzBEMEUwRjBHMEgwSTBKMEswTDBN" +
2965317bbafSopenharmony_ci		"ME4wTzBQMFEwUjBTMFQwVTBWMFcwWDBZMFowWzBcMF0wXjBfMGAwYTBiMGMwZDBlMGYwZzBoMGkwajBrMGwwbTBuMG8wcDBxMHIwczB0MHUwdjB3MHgweTB6MHswfDB9MH4wfzCAMIEwgjCDMIQwhTCGMIcwiDCJMIowizCMMI0wjjCPMJAwkTCSMJP/////////////" +
2975317bbafSopenharmony_ci		"////////////////////////MKEwojCjMKQwpTCmMKcwqDCpMKowqzCsMK0wrjCvMLAwsTCyMLMwtDC1MLYwtzC4MLkwujC7MLwwvTC+ML8wwDDBMMIwwzDEMMUwxjDHMMgwyTDKMMswzDDNMM4wzzDQMNEw0jDTMNQw1TDWMNcw2DDZMNow2zDcMN0w3jDf//8w4DDh" +
2985317bbafSopenharmony_ci		"MOIw4zDkMOUw5jDnMOgw6TDqMOsw7DDtMO4w7zDwMPEw8jDzMPQw9TD2/////////////////////wORA5IDkwOUA5UDlgOXA5gDmQOaA5sDnAOdA54DnwOgA6EDowOkA6UDpgOnA6gDqf////////////////////8DsQOyA7MDtAO1A7YDtwO4A7kDugO7A7wDvQO+" +
2995317bbafSopenharmony_ci		"A78DwAPBA8MDxAPFA8YDxwPIA8n/////////////////////////////////////////////////////////////////////////////////////////////////////////////BBAEEQQSBBMEFAQVBAEEFgQXBBgEGQQaBBsEHAQdBB4EHwQgBCEEIgQjBCQEJQQm" +
3005317bbafSopenharmony_ci		"BCcEKAQpBCoEKwQsBC0ELgQv////////////////////////////////////////BDAEMQQyBDMENAQ1BFEENgQ3BDgEOQQ6BDsEPAQ9//8EPgQ/BEAEQQRCBEMERARFBEYERwRIBEkESgRLBEwETQROBE///////////////////////////////////yUAJQIlDCUQ" +
3015317bbafSopenharmony_ci		"JRglFCUcJSwlJCU0JTwlASUDJQ8lEyUbJRclIyUzJSslOyVLJSAlLyUoJTclPyUdJTAlJSU4JUL/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" +
3025317bbafSopenharmony_ci		"////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" +
3035317bbafSopenharmony_ci		"////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" +
3045317bbafSopenharmony_ci		"////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" +
3055317bbafSopenharmony_ci		"////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" +
3065317bbafSopenharmony_ci		"////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" +
3075317bbafSopenharmony_ci		"////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" +
3085317bbafSopenharmony_ci		"////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" +
3095317bbafSopenharmony_ci		"////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" +
3105317bbafSopenharmony_ci		"////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" +
3115317bbafSopenharmony_ci		"/////////////////////////////////////06cVRZaA5Y/VMBhG2MoWfaQIoR1gxx6UGCqY+FuJWXthGaCppv1aJNXJ2WhYnFbm1nQhnuY9H1ifb6bjmIWfJ+It1uJXrVjCWaXaEiVx5eNZ09O5U8KT01PnVBJVvJZN1nUWgFcCWDfYQ9hcGYTaQVwunVPdXB5+32t" +
3125317bbafSopenharmony_ci		"fe+Aw4QOiGOLApBVkHpTO06VTqVX34CykMF4704AWPFuopA4ejKDKIKLnC9RQVNwVL1U4VbgWftfFZjybeuA5IUt////////lmKWcJagl/tUC1PzW4dwz3+9j8KW6FNvnVx6uk4ReJOB/G4mVhhVBGsdhRqcO1nlU6ltZnTclY9WQk6RkEuW8oNPmQxT4VW2WzBfcWYg" +
3135317bbafSopenharmony_ci		"ZvNoBGw4bPNtKXRbdsh6Tpg0gvGIW4pgku1tsnWrdsqZxWCmiwGNipWyaY5TrVGG//9XElgwWURbtF72YChjqWP0bL9vFHCOcRRxWXHVcz9+AYJ2gtGFl5BgkludG1hpZbxsWnUlUflZLlllX4Bf3GK8ZfpqKmsna7Rzi3/BiVadLJ0OnsRcoWyWg3tRBFxLYbaBxmh2" +
3145317bbafSopenharmony_ci		"cmFOWU/6U3hgaW4pek+X804LUxZO7k9VTz1PoU9zUqBT71YJWQ9awVu2W+F50WaHZ5xntmtMbLNwa3PCeY15vno8e4eCsYLbgwSDd4Pvg9OHZoqyVimMqI/mkE6XHoaKT8Rc6GIRcll1O4Hlgr2G/ozAlsWZE5nVTstPGonjVt5YSljKXvtf62AqYJRgYmHQYhJi0GU5" +
3155317bbafSopenharmony_ci		"////////m0FmZmiwbXdwcHVMdoZ9dYKlh/mVi5aOjJ1R8VK+WRZUs1uzXRZhaGmCba94jYTLiFeKcpOnmrhtbJmohtlXo2f/hs6SDlKDVodUBF7TYuFkuWg8aDhru3NyeLp6a4maidKNa48DkO2Vo5aUl2lbZlyzaX2YTZhOY5t7IGor//9qf2i2nA1vX1JyVZ1gcGLs" +
3165317bbafSopenharmony_ci		"bTtuB27RhFuJEI9EThScOVP2aRtqOpeEaCpRXHrDhLKR3JOMVludKGgigwWEMXylUgiCxXTmTn5Pg1GgW9JSClLYUudd+1WaWCpZ5luMW5hb215yXnlgo2EfYWNhvmPbZWJn0WhTaPprPmtTbFdvIm+Xb0V0sHUYduN3C3r/e6F8IX3pfzZ/8ICdgmaDnomzisyMq5CE" +
3175317bbafSopenharmony_ci		"lFGVk5WRlaKWZZfTmSiCGE44VCtcuF3Mc6l2THc8XKl/640LlsGYEZhUmFhPAU8OU3FVnFZoV/pZR1sJW8RckF4MXn5fzGPuZzpl12XiZx9oy2jE////////al9eMGvFbBdsfXV/eUhbY3oAfQBfvYmPihiMtI13jsyPHZjimg6bPE6AUH1RAFmTW5xiL2KAZOxrOnKg" +
3185317bbafSopenharmony_ci		"dZF5R3+ph/uKvItwY6yDypegVAlUA1WraFRqWIpweCdndZ7NU3RbooEahlCQBk4YTkVOx08RU8pUOFuuXxNgJWVR//9nPWxCbHJs43B4dAN6dnquewh9Gnz+fWZl53JbU7tcRV3oYtJi4GMZbiCGWooxjd2S+G8BeaabWk6oTqtOrE+bT6BQ0VFHevZRcVH2U1RTIVN/" +
3195317bbafSopenharmony_ci		"U+tVrFiDXOFfN19KYC9gUGBtYx9lWWpLbMFywnLtd++A+IEFggiFTpD3k+GX/5lXmlpO8FHdXC1mgWltXEBm8ml1c4loUHyBUMVS5FdHXf6TJmWkayNrPXQ0eYF5vXtLfcqCuYPMiH+JX4s5j9GR0VQfkoBOXVA2U+VTOnLXc5Z36YLmjq+ZxpnImdJRd2Eahl5VsHp6" +
3205317bbafSopenharmony_ci		"UHZb05BHloVOMmrbkedcUVxI////////Y5h6n2yTl3SPYXqqcYqWiHyCaBd+cGhRk2xS8lQbhauKE3+kjs2Q4VNmiIh5QU/CUL5SEVFEVVNXLXPqV4tZUV9iX4RgdWF2YWdhqWOyZDplbGZvaEJuE3Vmej18+31MfZl+S39rgw6DSobNigiKY4tmjv2YGp2PgriPzpvo" +
3215317bbafSopenharmony_ci		"//9Sh2IfZINvwJaZaEFQkWsgbHpvVHp0fVCIQIojZwhO9lA5UCZQZVF8UjhSY1WnVw9YBVrMXvphsmH4YvNjcmkcailyfXKscy54FHhvfXl3DICpiYuLGYzijtKQY5N1lnqYVZoTnnhRQ1OfU7Nee18mbhtukHOEc/59Q4I3igCK+pZQTk5QC1PkVHxW+lnRW2Rd8V6r" +
3225317bbafSopenharmony_ci		"XydiOGVFZ69uVnLQfMqItIChgOGD8IZOioeN6JI3lseYZ58TTpROkk8NU0hUSVQ+Wi9fjF+hYJ9op2qOdFp4gYqeiqSLd5GQTl6byU6kT3xPr1AZUBZRSVFsUp9SuVL+U5pT41QR////////VA5ViVdRV6JZfVtUW11bj13lXedd9154XoNeml63XxhgUmFMYpdi2GOn" +
3235317bbafSopenharmony_ci		"ZTtmAmZDZvRnbWghaJdpy2xfbSptaW4vbp11MnaHeGx6P3zgfQV9GH1efbGAFYADgK+AsYFUgY+CKoNSiEyIYYsbjKKM/JDKkXWScXg/kvyVpJZN//+YBZmZmtidO1JbUqtT91QIWNVi92/gjGqPX565UUtSO1RKVv16QJF3nWCe0nNEbwmBcHURX/1g2pqoctuPvGtk" +
3245317bbafSopenharmony_ci		"mANOylbwV2RYvlpaYGhhx2YPZgZoOWixbfd11X06gm6bQk6bT1BTyVUGXW9d5l3uZ/tsmXRzeAKKUJOWiN9XUF6nYytQtVCsUY1nAFTJWF5Zu1uwX2liTWOhaD1rc24IcH2Rx3KAeBV4JnltZY59MIPciMGPCZabUmRXKGdQf2qMoVG0V0KWKlg6aYqAtFSyXQ5X/HiV" +
3255317bbafSopenharmony_ci		"nfpPXFJKVItkPmYoZxRn9XqEe1Z9IpMvaFybrXs5UxlRilI3////////W99i9mSuZOZnLWu6hamW0XaQm9ZjTJMGm6t2v2ZSTglQmFPCXHFg6GSSZWNoX3Hmc8p1I3uXfoKGlYuDjNuReJkQZaxmq2uLTtVO1E86T39SOlP4U/JV41bbWOtZy1nJWf9bUFxNXgJeK1/X" +
3265317bbafSopenharmony_ci		"YB1jB2UvW1xlr2W9ZehnnWti//9re2wPc0V5SXnBfPh9GX0rgKKBAoHziZaKXoppimaKjIrujMeM3JbMmPxrb06LTzxPjVFQW1db+mFIYwFmQmshbstsu3I+dL111HjBeTqADIAzgeqElI+ebFCef18Pi1idK3r6jvhbjZbrTgNT8Vf3WTFayVukYIluf28Gdb6M6luf" +
3275317bbafSopenharmony_ci		"hQB74FByZ/SCnVxhhUp+HoIOUZlcBGNojWZlnHFueT59F4AFix2OypBuhseQqlAfUvpcOmdTcHxyNZFMkciTK4LlW8JfMWD5TjtT1luIYktnMWuKculz4HougWuNo5FSmZZRElPXVGpb/2OIajl9rJcAVtpTzlRo////////W5dcMV3eT+5hAWL+bTJ5wHnLfUJ+TX/S" +
3285317bbafSopenharmony_ci		"ge2CH4SQiEaJcouQjnSPL5AxkUuRbJbGkZxOwE9PUUVTQV+TYg5n1GxBbgtzY34mkc2Sg1PUWRlbv23ReV1+LnybWH5xn1H6iFOP8E/KXPtmJXeseuOCHJn/UcZfqmXsaW9riW3z//9ulm9kdv59FF3hkHWRh5gGUeZSHWJAZpFm2W4aXrZ90n9yZviFr4X3ivhSqVPZ" +
3295317bbafSopenharmony_ci		"WXNej1+QYFWS5JZkULdRH1LdUyBTR1PsVOhVRlUxVhdZaFm+WjxbtVwGXA9cEVwaXoReil7gX3Bif2KEYttjjGN3ZgdmDGYtZnZnfmiiah9qNWy8bYhuCW5YcTxxJnFndcd3AXhdeQF5ZXnweuB7EXynfTmAloPWhIuFSYhdiPOKH4o8ilSKc4xhjN6RpJJmk36UGJac" +
3305317bbafSopenharmony_ci		"l5hOCk4ITh5OV1GXUnBXzlg0WMxbIl44YMVk/mdhZ1ZtRHK2dXN6Y4S4i3KRuJMgVjFX9Jj+////////Yu1pDWuWce1+VIB3gnKJ5pjfh1WPsVw7TzhP4U+1VQdaIFvdW+lfw2FOYy9lsGZLaO5pm214bfF1M3W5dx95XnnmfTOB44KvhaqJqoo6jquPm5Aykd2XB066" +
3315317bbafSopenharmony_ci		"TsFSA1h1WOxcC3UaXD2BTooKj8WWY5dteyWKz5gIkWJW81Oo//+QF1Q5V4JeJWOobDRwindhfIt/4IhwkEKRVJMQkxiWj3RemsRdB11pZXBnoo2olttjbmdJaRmDxZgXlsCI/m+EZHpb+E4WcCx1XWYvUcRSNlLiWdNfgWAnYhBlP2V0Zh9mdGjyaBZrY24FcnJ1H3bb" +
3325317bbafSopenharmony_ci		"fL6AVljwiP2Jf4qgipOKy5AdkZKXUpdZZYl6DoEGlrteLWDcYhplpWYUZ5B383pNfE1+PoEKjKyNZI3hjl94qVIHYtljpWRCYpiKLXqDe8CKrJbqfXaCDIdJTtlRSFNDU2Bbo1wCXBZd3WImYkdksGgTaDRsyW1FbRdn029ccU5xfWXLen97rX3a////////fkp/qIF6" +
3335317bbafSopenharmony_ci		"ghuCOYWmim6Mzo31kHiQd5KtkpGVg5uuUk1VhG84cTZRaHmFflWBs3zOVkxYUVyoY6pm/mb9aVpy2XWPdY55DnlWed98l30gfUSGB4o0ljuQYZ8gUOdSdVPMU+JQCVWqWO5ZT3I9W4tcZFMdYONg82NcY4NjP2O7//9kzWXpZvld42nNaf1vFXHlTol16Xb4epN8333P" +
3345317bbafSopenharmony_ci		"fZyAYYNJg1iEbIS8hfuIxY1wkAGQbZOXlxyaElDPWJdhjoHThTWNCJAgT8NQdFJHU3Ngb2NJZ19uLI2zkB9P11xejMplz32aU1KIllF2Y8NbWFtrXApkDWdRkFxO1lkaWSpscIpRVT5YFVmlYPBiU2fBgjVpVZZAmcSaKE9TWAZb/oAQXLFeL1+FYCBhS2I0Zv9s8G7e" +
3355317bbafSopenharmony_ci		"gM6Bf4LUiIuMuJAAkC6Wip7bm9tO41PwWSd7LJGNmEyd+W7dcCdTU1VEW4ViWGKeYtNsom/vdCKKF5Q4b8GK/oM4UeeG+FPq////////U+lPRpBUj7BZaoExXf166o+/aNqMN3L4nEhqPYqwTjlTWFYGV2ZixWOiZeZrTm3hbltwrXfteu97qn27gD2AxobLipWTW1bj" +
3365317bbafSopenharmony_ci		"WMdfPmWtZpZqgGu1dTeKx1Akd+VXMF8bYGVmemxgdfR6Gn9ugfSHGJBFmbN7yXVcevl7UYTE//+QEHnpepKDNlrhd0BOLU7yW5lf4GK9Zjxn8WzohmuId4o7kU6S85nQahdwJnMqgueEV4yvTgFRRlHLVYtb9V4WXjNegV8UXzVfa1+0YfJjEWaiZx1vbnJSdTp3OoB0" +
3375317bbafSopenharmony_ci		"gTmBeId2ir+K3I2FjfOSmpV3mAKc5VLFY1d29GcVbIhzzYzDk66Wc20lWJxpDmnMj/2TmnXbkBpYWmgCY7Rp+09Dbyxn2I+7hSZ9tJNUaT9vcFdqWPdbLH0scipUCpHjnbROrU9OUFxQdVJDjJ5USFgkW5peHV6VXq1e918fYIxitWM6Y9Bor2xAeId5jnoLfeCCR4oC" +
3385317bbafSopenharmony_ci		"iuaORJAT////////kLiRLZHYnw5s5WRYZOJldW70doR7G5Bpk9FuulTyX7lkpI9Nj+2SRFF4WGtZKVxVXpdt+36PdRyMvI7imFtwuU8da79vsXUwlvtRTlQQWDVYV1msXGBfkmWXZ1xuIXZ7g9+M7ZAUkP2TTXgleDpSql6mVx9ZdGASUBJRWlGs//9RzVIAVRBYVFhY" +
3395317bbafSopenharmony_ci		"WVdblVz2XYtgvGKVZC1ncWhDaLxo33bXbdhub22bcG9xyF9Tddh5d3tJe1R7UnzWfXFSMIRjhWmF5IoOiwSMRo4PkAOQD5QZlnaYLZowldhQzVLVVAxYAlwOYadknm0ed7N65YD0hASQU5KFXOCdB1M/X5dfs22ccnl3Y3m/e+Rr0nLsiq1oA2phUfh6gWk0XEqc9oLr" +
3405317bbafSopenharmony_ci		"W8WRSXAeVnhcb2DHZWZsjIxakEGYE1RRZseSDVlIkKNRhU5NUeqFmYsOcFhjepNLaWKZtH4EdXdTV2lgjt+W42xdToxcPF8Qj+lTAozRgImGeV7/ZeVOc1Fl////////WYJcP5fuTvtZil/Nio1v4XmweWJb54RxcytxsV50X/Vje2SaccN8mE5DXvxOS1fcVqJgqW/D" +
3415317bbafSopenharmony_ci		"fQ2A/YEzgb+PsomXhqRd9GKKZK2Jh2d3bOJtPnQ2eDRaRn91gq2ZrE/zXsNi3WOSZVdnb3bDckyAzIC6jymRTVANV/lakmiF//9pc3Fkcv2Mt1jyjOCWapAZh3955HfnhClPL1JlU1pizWfPbMp2fXuUfJWCNoWEj+tm3W8gcgZ+G4OrmcGeplH9e7F4cnu4gId7SGro" +
3425317bbafSopenharmony_ci		"XmGAjHVRdWBRa5Jibox2epGXmupPEH9wYpx7T5WlnOlWelhZhuSWvE80UiRTSlPNU9teBmQsZZFnf2w+bE5ySHKvc+11VH5BgiyF6Yype8SRxnFpmBKY72M9Zml1anbkeNCFQ4buUypTUVQmWYNeh198YLJiSWJ5YqtlkGvUbMx1snaueJF52H3Lf3eApYirirmMu5B/" +
3435317bbafSopenharmony_ci		"l16Y22oLfDhQmVw+X65nh2vYdDV3CX+O////////nztnynoXUzl1i5rtX2aBnYPxgJhfPF/FdWJ7RpA8aGdZ61qbfRB2fossT/VfamoZbDdvAnTieWiIaIpVjHle32PPdcV50oLXkyiS8oSchu2cLVTBX2xljG1ccBWMp4zTmDtlT3T2Tg1O2FfgWStaZlvMUaheA16c" +
3445317bbafSopenharmony_ci		"YBZidmV3//9lp2ZubW5yNnsmgVCBmoKZi1yMoIzmjXSWHJZET65kq2tmgh6EYYVqkOhcAWlTmKiEeoVXTw9Sb1+pXkVnDXmPgXmJB4mGbfVfF2JVbLhOz3Jpm5JSBlQ7VnRYs2GkYm5xGllufIl83n0blvBlh4BeThlPdVF1WEBeY15zXwpnxE4mhT2ViZZbfHOYAVD7" +
3455317bbafSopenharmony_ci		"WMF2VninUiV3pYURe4ZQT1kJckd7x33oj7qP1JBNT79SyVopXwGXrU/dgheS6lcDY1VraXUriNyPFHpCUt9Yk2FVYgpmrmvNfD+D6VAjT/hTBVRGWDFZSVudXPBc710pXpZisWNnZT5luWcL////////bNVs4XD5eDJ+K4DegrOEDITshwKJEooqjEqQppLSmP2c851s" +
3465317bbafSopenharmony_ci		"Tk9OoVCNUlZXSlmoXj1f2F/ZYj9mtGcbZ9Bo0lGSfSGAqoGoiwCMjIy/kn6WMlQgmCxTF1DVU1xYqGSyZzRyZ3dmekaR5lLDbKFrhlgAXkxZVGcsf/tR4XbG//9kaXjom1Seu1fLWblmJ2eaa85U6WnZXlWBnGeVm6pn/pxSaF1Opk/jU8hiuWcrbKuPxE+tfm2ev04H" +
3475317bbafSopenharmony_ci		"YWJugG8rhRNUc2cqm0Vd83uVXKxbxoccbkqE0XoUgQhZmXyNbBF3IFLZWSJxIXJfd9uXJ51haQtaf1oYUaVUDVR9Zg5234/3kpic9Fnqcl1uxVFNaMl9v33sl2KeumR4aiGDAlmEW19r23MbdvJ9soAXhJlRMmcontl27mdiUv+ZBVwkYjt8foywVU9gtn0LlYBTAU5f" +
3485317bbafSopenharmony_ci		"UbZZHHI6gDaRzl8ld+JThF95fQSFrIozjo2XVmfzha6UU2EJYQhsuXZS////////iu2POFUvT1FRKlLHU8tbpV59YKBhgmPWZwln2m5nbYxzNnM3dTF5UIjVipiQSpCRkPWWxIeNWRVOiE9ZTg6KiY8/mBBQrV58WZZbuV64Y9pj+mTBZtxpSmnYbQtutnGUdSh6r3+K" +
3495317bbafSopenharmony_ci		"gACESYTJiYGLIY4KkGWWfZkKYX5ikWsy//9sg210f8x//G3Af4WHuoj4Z2WDsZg8lvdtG31hhD2Rak5xU3VdUGsEb+uFzYYtiadSKVQPXGVnTmiodAZ0g3XiiM+I4ZHMluKWeF+Lc4d6y4ROY6B1ZVKJbUFunHQJdVl4a3ySloZ63J+NT7ZhbmXFhlxOhk6uUNpOIVHM" +
3505317bbafSopenharmony_ci		"W+5lmWiBbbxzH3ZCd616HHzngm+K0pB8kc+WdZgYUpt90VArU5hnl23LcdB0M4HojyqWo5xXnp90YFhBbZl9L5heTuRPNk+LUbdSsV26YBxzsnk8gtOSNJa3lvaXCp6Xn2Jmpmt0UhdSo3DIiMJeyWBLYZBvI3FJfD599IBv////////hO6QI5MsVEKbb2rTcImMwo3v" +
3515317bbafSopenharmony_ci		"lzJStFpBXspfBGcXaXxplG1qbw9yYnL8e+2AAYB+h0uQzlFtnpN5hICLkzKK1lAtVIyKcWtqjMSBB2DRZ6Cd8k6ZTpicEIprhcGFaGkAbn54l4FV////////////////////////////////////////////////////////////////////////////////////////" +
3525317bbafSopenharmony_ci		"/////////////////////////////18MThBOFU4qTjFONk48Tj9OQk5WTlhOgk6FjGtOioISXw1Ojk6eTp9OoE6iTrBOs062Ts5OzU7ETsZOwk7XTt5O7U7fTvdPCU9aTzBPW09dT1dPR092T4hPj0+YT3tPaU9wT5FPb0+GT5ZRGE/UT99Pzk/YT9tP0U/aT9BP5E/l" +
3535317bbafSopenharmony_ci		"UBpQKFAUUCpQJVAFTxxP9lAhUClQLE/+T+9QEVAGUENQR2cDUFVQUFBIUFpQVlBsUHhQgFCaUIVQtFCy////////UMlQylCzUMJQ1lDeUOVQ7VDjUO5Q+VD1UQlRAVECURZRFVEUURpRIVE6UTdRPFE7UT9RQFFSUUxRVFFievhRaVFqUW5RgFGCVthRjFGJUY9RkVGT" +
3545317bbafSopenharmony_ci		"UZVRllGkUaZRolGpUapRq1GzUbFRslGwUbVRvVHFUclR21HghlVR6VHt//9R8FH1Uf5SBFILUhRSDlInUipSLlIzUjlST1JEUktSTFJeUlRSalJ0UmlSc1J/Un1SjVKUUpJScVKIUpGPqI+nUqxSrVK8UrVSwVLNUtdS3lLjUuaY7VLgUvNS9VL4UvlTBlMIdThTDVMQ" +
3555317bbafSopenharmony_ci		"Uw9TFVMaUyNTL1MxUzNTOFNAU0ZTRU4XU0lTTVHWU15TaVNuWRhTe1N3U4JTllOgU6ZTpVOuU7BTtlPDfBKW2VPfZvxx7lPuU+hT7VP6VAFUPVRAVCxULVQ8VC5UNlQpVB1UTlSPVHVUjlRfVHFUd1RwVJJUe1SAVHZUhFSQVIZUx1SiVLhUpVSsVMRUyFSo////////" +
3565317bbafSopenharmony_ci		"VKtUwlSkVL5UvFTYVOVU5lUPVRRU/VTuVO1U+lTiVTlVQFVjVUxVLlVcVUVVVlVXVThVM1VdVZlVgFSvVYpVn1V7VX5VmFWeVa5VfFWDValVh1WoVdpVxVXfVcRV3FXkVdRWFFX3VhZV/lX9VhtV+VZOVlBx31Y0VjZWMlY4//9Wa1ZkVi9WbFZqVoZWgFaKVqBWlFaP" +
3575317bbafSopenharmony_ci		"VqVWrla2VrRWwla8VsFWw1bAVshWzlbRVtNW11buVvlXAFb/VwRXCVcIVwtXDVcTVxhXFlXHVxxXJlc3VzhXTlc7V0BXT1dpV8BXiFdhV39XiVeTV6BXs1ekV6pXsFfDV8ZX1FfSV9NYClfWV+NYC1gZWB1YclghWGJYS1hwa8BYUlg9WHlYhVi5WJ9Yq1i6WN5Yu1i4" +
3585317bbafSopenharmony_ci		"WK5YxVjTWNFY11jZWNhY5VjcWORY31jvWPpY+Vj7WPxY/VkCWQpZEFkbaKZZJVksWS1ZMlk4WT560llVWVBZTllaWVhZYllgWWdZbFlp////////WXhZgVmdT15Pq1mjWbJZxlnoWdxZjVnZWdpaJVofWhFaHFoJWhpaQFpsWklaNVo2WmJaalqaWrxavlrLWsJavVrj" +
3595317bbafSopenharmony_ci		"Wtda5lrpWtZa+lr7WwxbC1sWWzJa0FsqWzZbPltDW0VbQFtRW1VbWltbW2VbaVtwW3NbdVt4ZYhbeluA//9bg1umW7hbw1vHW8lb1FvQW+Rb5lviW95b5VvrW/Bb9lvzXAVcB1wIXA1cE1wgXCJcKFw4XDlcQVxGXE5cU1xQXE9bcVxsXG5OYlx2XHlcjFyRXJRZm1yr" +
3605317bbafSopenharmony_ci		"XLtctly8XLdcxVy+XMdc2VzpXP1c+lztXYxc6l0LXRVdF11cXR9dG10RXRRdIl0aXRldGF1MXVJdTl1LXWxdc112XYddhF2CXaJdnV2sXa5dvV2QXbddvF3JXc1d013SXdZd213rXfJd9V4LXhpeGV4RXhteNl43XkReQ15AXk5eV15UXl9eYl5kXkdedV52XnqevF5/" +
3615317bbafSopenharmony_ci		"XqBewV7CXshe0F7P////////XtZe417dXtpe217iXuFe6F7pXuxe8V7zXvBe9F74Xv5fA18JX11fXF8LXxFfFl8pXy1fOF9BX0hfTF9OXy9fUV9WX1dfWV9hX21fc193X4Nfgl9/X4pfiF+RX4dfnl+ZX5hfoF+oX61fvF/WX/tf5F/4X/Ff3WCzX/9gIWBg//9gGWAQ" +
3625317bbafSopenharmony_ci		"YClgDmAxYBtgFWArYCZgD2A6YFpgQWBqYHdgX2BKYEZgTWBjYENgZGBCYGxga2BZYIFgjWDnYINgmmCEYJtglmCXYJJgp2CLYOFguGDgYNNgtF/wYL1gxmC1YNhhTWEVYQZg9mD3YQBg9GD6YQNhIWD7YPFhDWEOYUdhPmEoYSdhSmE/YTxhLGE0YT1hQmFEYXNhd2FY" +
3635317bbafSopenharmony_ci		"YVlhWmFrYXRhb2FlYXFhX2FdYVNhdWGZYZZhh2GsYZRhmmGKYZFhq2GuYcxhymHJYfdhyGHDYcZhumHLf3lhzWHmYeNh9mH6YfRh/2H9Yfxh/mIAYghiCWINYgxiFGIb////////Yh5iIWIqYi5iMGIyYjNiQWJOYl5iY2JbYmBiaGJ8YoJiiWJ+YpJik2KWYtRig2KU" +
3645317bbafSopenharmony_ci		"Ytdi0WK7Ys9i/2LGZNRiyGLcYsxiymLCYsdim2LJYwxi7mLxYydjAmMIYu9i9WNQYz5jTWQcY09jlmOOY4Bjq2N2Y6Njj2OJY59jtWNr//9jaWO+Y+ljwGPGY+NjyWPSY/ZjxGQWZDRkBmQTZCZkNmUdZBdkKGQPZGdkb2R2ZE5lKmSVZJNkpWSpZIhkvGTaZNJkxWTH" +
3655317bbafSopenharmony_ci		"ZLtk2GTCZPFk54IJZOBk4WKsZONk72UsZPZk9GTyZPplAGT9ZRhlHGUFZSRlI2UrZTRlNWU3ZTZlOHVLZUhlVmVVZU1lWGVeZV1lcmV4ZYJlg4uKZZtln2WrZbdlw2XGZcFlxGXMZdJl22XZZeBl4WXxZ3JmCmYDZftnc2Y1ZjZmNGYcZk9mRGZJZkFmXmZdZmRmZ2Zo" +
3665317bbafSopenharmony_ci		"Zl9mYmZwZoNmiGaOZolmhGaYZp1mwWa5Zslmvma8////////ZsRmuGbWZtpm4GY/ZuZm6WbwZvVm92cPZxZnHmcmZyeXOGcuZz9nNmdBZzhnN2dGZ15nYGdZZ2NnZGeJZ3BnqWd8Z2pnjGeLZ6ZnoWeFZ7dn72e0Z+xns2fpZ7hn5GfeZ91n4mfuZ7lnzmfGZ+dqnGge" +
3675317bbafSopenharmony_ci		"aEZoKWhAaE1oMmhO//9os2graFloY2h3aH9on2iPaK1olGidaJtog2quaLlodGi1aKBoumkPaI1ofmkBaMppCGjYaSJpJmjhaQxozWjUaOdo1Wk2aRJpBGjXaONpJWj5aOBo72koaSppGmkjaSFoxml5aXdpXGl4aWtpVGl+aW5pOWl0aT1pWWkwaWFpXmldaYFpammy" +
3685317bbafSopenharmony_ci		"aa5p0Gm/acFp02m+ac5b6GnKad1pu2nDaadqLmmRaaBpnGmVabRp3mnoagJqG2n/awpp+WnyaedqBWmxah5p7WoUaetqCmoSasFqI2oTakRqDGpyajZqeGpHamJqWWpmakhqOGoiapBqjWqgaoRqomqj////////apeGF2q7asNqwmq4arNqrGreatFq32qqatpq6mr7" +
3695317bbafSopenharmony_ci		"awWGFmr6axJrFpsxax9rOGs3dtxrOZjua0drQ2tJa1BrWWtUa1trX2tha3hreWt/a4BrhGuDa41rmGuVa55rpGuqa6trr2uya7Frs2u3a7xrxmvLa9Nr32vsa+tr82vv//+evmwIbBNsFGwbbCRsI2xebFVsYmxqbIJsjWyabIFsm2x+bGhsc2ySbJBsxGzxbNNsvWzX" +
3705317bbafSopenharmony_ci		"bMVs3WyubLFsvmy6bNts72zZbOptH4hNbTZtK209bThtGW01bTNtEm0MbWNtk21kbVpteW1ZbY5tlW/kbYVt+W4VbgpttW3HbeZtuG3Gbext3m3Mbeht0m3Fbfpt2W3kbdVt6m3ubi1ubm4ubhlucm5fbj5uI25rbitudm5Nbh9uQ246bk5uJG7/bh1uOG6CbqpumG7J" +
3715317bbafSopenharmony_ci		"brdu0269bq9uxG6ybtRu1W6PbqVuwm6fb0FvEXBMbuxu+G7+bz9u8m8xbu9vMm7M////////bz5vE273b4Zvem94b4FvgG9vb1tv829tb4JvfG9Yb45vkW/Cb2Zvs2+jb6FvpG+5b8Zvqm/fb9Vv7G/Ub9hv8W/ub9twCXALb/pwEXABcA9v/nAbcBpvdHAdcBhwH3Aw" +
3725317bbafSopenharmony_ci		"cD5wMnBRcGNwmXCScK9w8XCscLhws3CucN9wy3Dd//9w2XEJcP1xHHEZcWVxVXGIcWZxYnFMcVZxbHGPcftxhHGVcahxrHHXcblxvnHScclx1HHOceBx7HHncfVx/HH5cf9yDXIQchtyKHItcixyMHIycjtyPHI/ckByRnJLclhydHJ+coJygXKHcpJylnKicqdyuXKy" +
3735317bbafSopenharmony_ci		"csNyxnLEcs5y0nLicuBy4XL5cvdQD3MXcwpzHHMWcx1zNHMvcylzJXM+c05zT57Yc1dzanNoc3BzeHN1c3tzenPIc7NzznO7c8Bz5XPuc950onQFdG90JXP4dDJ0OnRVdD90X3RZdEF0XHRpdHB0Y3RqdHZ0fnSLdJ50p3TKdM901HPx////////dOB043TndOl07nTy" +
3745317bbafSopenharmony_ci		"dPB08XT4dPd1BHUDdQV1DHUOdQ11FXUTdR51JnUsdTx1RHVNdUp1SXVbdUZ1WnVpdWR1Z3VrdW11eHV2dYZ1h3V0dYp1iXWCdZR1mnWddaV1o3XCdbN1w3W1db11uHW8dbF1zXXKddJ12XXjdd51/nX///91/HYBdfB1+nXydfN2C3YNdgl2H3YndiB2IXYidiR2NHYw" +
3755317bbafSopenharmony_ci		"djt2R3ZIdkZ2XHZYdmF2YnZodml2anZndmx2cHZydnZ2eHZ8doB2g3aIdot2jnaWdpN2mXaadrB2tHa4drl2unbCds121nbSdt524Xbldud26oYvdvt3CHcHdwR3KXckdx53JXcmdxt3N3c4d0d3Wndod2t3W3dld393fnd5d453i3eRd6B3nnewd7Z3uXe/d7x3vXe7" +
3765317bbafSopenharmony_ci		"d8d3zXfXd9p33Hfjd+53/HgMeBJ5JnggeSp4RXiOeHR4hnh8eJp4jHijeLV4qniveNF4xnjLeNR4vni8eMV4ynjs////////eOd42nj9ePR5B3kSeRF5GXkseSt5QHlgeVd5X3laeVV5U3l6eX95inmdeaefS3mqea55s3m5ebp5yXnVeed57HnheeN6CHoNehh6GXog" +
3775317bbafSopenharmony_ci		"eh95gHoxejt6Pno3ekN6V3pJemF6Ynppn516cHp5en16iHqXepV6mHqWeql6yHqw//96tnrFesR6v5CDesd6ynrNes961XrTetl62nrdeuF64nrmeu168HsCew97CnsGezN7GHsZex57NXsoezZ7UHt6ewR7TXsLe0x7RXt1e2V7dHtne3B7cXtse257nXuYe597jXuc" +
3785317bbafSopenharmony_ci		"e5p7i3uSe497XXuZe8t7wXvMe897tHvGe9176XwRfBR75nvlfGB8AHwHfBN783v3fBd8DXv2fCN8J3wqfB98N3wrfD18THxDfFR8T3xAfFB8WHxffGR8VnxlfGx8dXyDfJB8pHytfKJ8q3yhfKh8s3yyfLF8rny5fL18wHzFfMJ82HzSfNx84ps7fO988nz0fPZ8+n0G" +
3795317bbafSopenharmony_ci		"////////fQJ9HH0VfQp9RX1LfS59Mn0/fTV9Rn1zfVZ9Tn1yfWh9bn1PfWN9k32JfVt9j319fZt9un2ufaN9tX3Hfb19q349faJ9r33cfbh9n32wfdh93X3kfd59+33yfeF+BX4KfiN+IX4SfjF+H34Jfgt+In5GfmZ+O341fjl+Q343//9+Mn46fmd+XX5Wfl5+WX5a" +
3805317bbafSopenharmony_ci		"fnl+an5pfnx+e36DfdV+fY+ufn9+iH6Jfox+kn6QfpN+lH6Wfo5+m36cfzh/On9Ff0x/TX9Of1B/UX9Vf1R/WH9ff2B/aH9pf2d/eH+Cf4Z/g3+If4d/jH+Uf55/nX+af6N/r3+yf7l/rn+2f7iLcX/Ff8Z/yn/Vf9R/4X/mf+l/83/5mNyABoAEgAuAEoAYgBmAHIAh" +
3815317bbafSopenharmony_ci		"gCiAP4A7gEqARoBSgFiAWoBfgGKAaIBzgHKAcIB2gHmAfYB/gISAhoCFgJuAk4CagK1RkICsgNuA5YDZgN2AxIDagNaBCYDvgPGBG4EpgSOBL4FL////////louBRoE+gVOBUYD8gXGBboFlgWaBdIGDgYiBioGAgYKBoIGVgaSBo4FfgZOBqYGwgbWBvoG4gb2BwIHC" +
3825317bbafSopenharmony_ci		"gbqByYHNgdGB2YHYgciB2oHfgeCB54H6gfuB/oIBggKCBYIHggqCDYIQghaCKYIrgjiCM4JAglmCWIJdglqCX4Jk//+CYoJogmqCa4IugnGCd4J4gn6CjYKSgquCn4K7gqyC4YLjgt+C0oL0gvOC+oOTgwOC+4L5gt6DBoLcgwmC2YM1gzSDFoMygzGDQIM5g1CDRYMv" +
3835317bbafSopenharmony_ci		"gyuDF4MYg4WDmoOqg5+DooOWgyODjoOHg4qDfIO1g3ODdYOgg4mDqIP0hBOD64POg/2EA4PYhAuDwYP3hAeD4IPyhA2EIoQgg72EOIUGg/uEbYQqhDyFWoSEhHeEa4SthG6EgoRphEaELIRvhHmENYTKhGKEuYS/hJ+E2YTNhLuE2oTQhMGExoTWhKGFIYT/hPSFF4UY" +
3845317bbafSopenharmony_ci		"hSyFH4UVhRSE/IVAhWOFWIVI////////hUGGAoVLhVWFgIWkhYiFkYWKhaiFbYWUhZuF6oWHhZyFd4V+hZCFyYW6hc+FuYXQhdWF3YXlhdyF+YYKhhOGC4X+hfqGBoYihhqGMIY/hk1OVYZUhl+GZ4ZxhpOGo4aphqqGi4aMhraGr4bEhsaGsIbJiCOGq4bUht6G6Ybs" +
3855317bbafSopenharmony_ci		"//+G34bbhu+HEocGhwiHAIcDhvuHEYcJhw2G+YcKhzSHP4c3hzuHJYcphxqHYIdfh3iHTIdOh3SHV4doh26HWYdTh2OHaogFh6KHn4eCh6+Hy4e9h8CH0JbWh6uHxIezh8eHxoe7h++H8ofgiA+IDYf+h/aH94gOh9KIEYgWiBWIIoghiDGINog5iCeIO4hEiEKIUohZ" +
3865317bbafSopenharmony_ci		"iF6IYohriIGIfoieiHWIfYi1iHKIgoiXiJKIroiZiKKIjYikiLCIv4ixiMOIxIjUiNiI2YjdiPmJAoj8iPSI6IjyiQSJDIkKiROJQ4keiSWJKokriUGJRIk7iTaJOIlMiR2JYIle////////iWaJZIltiWqJb4l0iXeJfomDiYiJiomTiZiJoYmpiaaJrImvibKJuom9" +
3875317bbafSopenharmony_ci		"ib+JwInaidyJ3YnnifSJ+IoDihaKEIoMihuKHYolijaKQYpbilKKRopIinyKbYpsimKKhYqCioSKqIqhipGKpYqmipqKo4rEis2KworaiuuK84rn//+K5IrxixSK4IriiveK3orbiwyLB4saiuGLFosQixeLIIszl6uLJosriz6LKItBi0yLT4tOi0mLVotbi1qLa4tf" +
3885317bbafSopenharmony_ci		"i2yLb4t0i32LgIuMi46LkouTi5aLmYuajDqMQYw/jEiMTIxOjFCMVYxijGyMeIx6jIKMiYyFjIqMjYyOjJSMfIyYYh2MrYyqjL2MsoyzjK6MtozIjMGM5IzjjNqM/Yz6jPuNBI0FjQqNB40PjQ2NEJ9OjROMzY0UjRaNZ41tjXGNc42BjZmNwo2+jbqNz43ajdaNzI3b" +
3895317bbafSopenharmony_ci		"jcuN6o3rjd+N4438jgiOCY3/jh2OHo4Qjh+OQo41jjCONI5K////////jkeOSY5MjlCOSI5ZjmSOYI4qjmOOVY52jnKOfI6BjoeOhY6EjouOio6TjpGOlI6ZjqqOoY6sjrCOxo6xjr6OxY7IjsuO247jjvyO+47rjv6PCo8FjxWPEo8ZjxOPHI8fjxuPDI8mjzOPO485" +
3905317bbafSopenharmony_ci		"j0WPQo8+j0yPSY9Gj06PV49c//+PYo9jj2SPnI+fj6OPrY+vj7eP2o/lj+KP6o/vkIeP9JAFj/mP+pARkBWQIZANkB6QFpALkCeQNpA1kDmP+JBPkFCQUZBSkA6QSZA+kFaQWJBekGiQb5B2lqiQcpCCkH2QgZCAkIqQiZCPkKiQr5CxkLWQ4pDkYkiQ25ECkRKRGZEy" +
3915317bbafSopenharmony_ci		"kTCRSpFWkViRY5FlkWmRc5FykYuRiZGCkaKRq5GvkaqRtZG0kbqRwJHBkcmRy5HQkdaR35HhkduR/JH1kfaSHpH/khSSLJIVkhGSXpJXkkWSSZJkkkiSlZI/kkuSUJKckpaSk5KbklqSz5K5kreS6ZMPkvqTRJMu////////kxmTIpMakyOTOpM1kzuTXJNgk3yTbpNW" +
3925317bbafSopenharmony_ci		"k7CTrJOtk5STuZPWk9eT6JPlk9iTw5Pdk9CTyJPklBqUFJQTlAOUB5QQlDaUK5Q1lCGUOpRBlFKURJRblGCUYpRelGqSKZRwlHWUd5R9lFqUfJR+lIGUf5WClYeVipWUlZaVmJWZ//+VoJWolaeVrZW8lbuVuZW+lcpv9pXDlc2VzJXVldSV1pXcleGV5ZXiliGWKJYu" +
3935317bbafSopenharmony_ci		"li+WQpZMlk+WS5Z3llyWXpZdll+WZpZylmyWjZaYlpWWl5aqlqeWsZaylrCWtJa2lriWuZbOlsuWyZbNiU2W3JcNltWW+ZcElwaXCJcTlw6XEZcPlxaXGZcklyqXMJc5lz2XPpdEl0aXSJdCl0mXXJdgl2SXZpdoUtKXa5dxl3mXhZd8l4GXepeGl4uXj5eQl5yXqJem" +
3945317bbafSopenharmony_ci		"l6OXs5e0l8OXxpfIl8uX3Jftn0+X8nrfl/aX9ZgPmAyYOJgkmCGYN5g9mEaYT5hLmGuYb5hw////////mHGYdJhzmKqYr5ixmLaYxJjDmMaY6ZjrmQOZCZkSmRSZGJkhmR2ZHpkkmSCZLJkumT2ZPplCmUmZRZlQmUuZUZlSmUyZVZmXmZiZpZmtma6ZvJnfmduZ3ZnY" +
3955317bbafSopenharmony_ci		"mdGZ7ZnumfGZ8pn7mfiaAZoPmgWZ4poZmiuaN5pFmkKaQJpD//+aPppVmk2aW5pXml+aYpplmmSaaZprmmqarZqwmryawJrPmtGa05rUmt6a35rimuOa5prvmuua7pr0mvGa95r7mwabGJsamx+bIpsjmyWbJ5somymbKpsumy+bMptEm0ObT5tNm06bUZtYm3Sbk5uD" +
3965317bbafSopenharmony_ci		"m5GblpuXm5+boJuom7SbwJvKm7mbxpvPm9Gb0pvjm+Kb5JvUm+GcOpvym/Gb8JwVnBScCZwTnAycBpwInBKcCpwEnC6cG5wlnCScIZwwnEecMpxGnD6cWpxgnGecdpx4nOec7JzwnQmdCJzrnQOdBp0qnSadr50jnR+dRJ0VnRKdQZ0/nT6dRp1I////////nV2dXp1k" +
3975317bbafSopenharmony_ci		"nVGdUJ1ZnXKdiZ2Hnaudb516nZqdpJ2pnbKdxJ3BnbuduJ26ncadz53Cndmd0534nead7Z3vnf2eGp4bnh6edZ55nn2egZ6InouejJ6SnpWekZ6dnqWeqZ64nqqerZdhnsyezp7PntCe1J7cnt6e3Z7gnuWe6J7v//+e9J72nvee+Z77nvye/Z8Hnwh2t58VnyGfLJ8+" +
3985317bbafSopenharmony_ci		"n0qfUp9Un2OfX59gn2GfZp9nn2yfap93n3Kfdp+Vn5yfoFgvaceQWXRkUdxxmf//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" +
3995317bbafSopenharmony_ci		"////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" +
4005317bbafSopenharmony_ci		"////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" +
4015317bbafSopenharmony_ci		"/////////////////////////////////////////////w==";
4025317bbafSopenharmony_ci
4035317bbafSopenharmony_ci
4045317bbafSopenharmony_ci	private static short[] UNICODE_TO_QR_KANJI = new short[1 << 16];
4055317bbafSopenharmony_ci
4065317bbafSopenharmony_ci	static {  // Unpack the Shift JIS table into a more computation-friendly form
4075317bbafSopenharmony_ci		Arrays.fill(UNICODE_TO_QR_KANJI, (short)-1);
4085317bbafSopenharmony_ci		byte[] bytes = Base64.getDecoder().decode(PACKED_QR_KANJI_TO_UNICODE);
4095317bbafSopenharmony_ci		for (int i = 0; i < bytes.length; i += 2) {
4105317bbafSopenharmony_ci			char c = (char)(((bytes[i] & 0xFF) << 8) | (bytes[i + 1] & 0xFF));
4115317bbafSopenharmony_ci			if (c == 0xFFFF)
4125317bbafSopenharmony_ci				continue;
4135317bbafSopenharmony_ci			assert UNICODE_TO_QR_KANJI[c] == -1;
4145317bbafSopenharmony_ci			UNICODE_TO_QR_KANJI[c] = (short)(i / 2);
4155317bbafSopenharmony_ci		}
4165317bbafSopenharmony_ci	}
4175317bbafSopenharmony_ci
4185317bbafSopenharmony_ci
4195317bbafSopenharmony_ci
4205317bbafSopenharmony_ci	/*---- Miscellaneous ----*/
4215317bbafSopenharmony_ci
4225317bbafSopenharmony_ci	private QrSegmentAdvanced() {}  // Not instantiable
4235317bbafSopenharmony_ci
4245317bbafSopenharmony_ci}
425