| /*  | 
 |  * QR Code generator library (C++) | 
 |  *  | 
 |  * Copyright (c) Project Nayuki. (MIT License) | 
 |  * https://www.nayuki.io/page/qr-code-generator-library | 
 |  *  | 
 |  * Permission is hereby granted, free of charge, to any person obtaining a copy of | 
 |  * this software and associated documentation files (the "Software"), to deal in | 
 |  * the Software without restriction, including without limitation the rights to | 
 |  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of | 
 |  * the Software, and to permit persons to whom the Software is furnished to do so, | 
 |  * subject to the following conditions: | 
 |  * - The above copyright notice and this permission notice shall be included in | 
 |  *   all copies or substantial portions of the Software. | 
 |  * - The Software is provided "as is", without warranty of any kind, express or | 
 |  *   implied, including but not limited to the warranties of merchantability, | 
 |  *   fitness for a particular purpose and noninfringement. In no event shall the | 
 |  *   authors or copyright holders be liable for any claim, damages or other | 
 |  *   liability, whether in an action of contract, tort or otherwise, arising from, | 
 |  *   out of or in connection with the Software or the use or other dealings in the | 
 |  *   Software. | 
 |  */ | 
 |  | 
 | #include <algorithm> | 
 | #include <climits> | 
 | #include <cstddef> | 
 | #include <cstdlib> | 
 | #include <sstream> | 
 | #include <utility> | 
 | #include "BitBuffer.hpp" | 
 | #include "QrCode.hpp" | 
 |  | 
 | #include "starboard/common/log.h" | 
 |  | 
 | #define throw SB_CHECK(false) << | 
 |  | 
 | using std::int8_t; | 
 | using std::uint8_t; | 
 | using std::size_t; | 
 | using std::vector; | 
 |  | 
 |  | 
 | namespace qrcodegen { | 
 |  | 
 | int QrCode::getFormatBits(Ecc ecl) { | 
 | 	switch (ecl) { | 
 | 		case Ecc::LOW     :  return 1; | 
 | 		case Ecc::MEDIUM  :  return 0; | 
 | 		case Ecc::QUARTILE:  return 3; | 
 | 		case Ecc::HIGH    :  return 2; | 
 | 		default:  throw "Assertion error"; | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 |  | 
 | QrCode QrCode::encodeText(const char *text, Ecc ecl, int minVersion) { | 
 | 	vector<QrSegment> segs = QrSegment::makeSegments(text); | 
 | 	return encodeSegments(segs, ecl, minVersion); | 
 | } | 
 |  | 
 |  | 
 | QrCode QrCode::encodeBinary(const vector<uint8_t> &data, Ecc ecl) { | 
 | 	vector<QrSegment> segs{QrSegment::makeBytes(data)}; | 
 | 	return encodeSegments(segs, ecl); | 
 | } | 
 |  | 
 |  | 
 | QrCode QrCode::encodeSegments(const vector<QrSegment> &segs, Ecc ecl, | 
 | 		int minVersion, int maxVersion, int mask, bool boostEcl) { | 
 | 	if (!(MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= MAX_VERSION) || mask < -1 || mask > 7) | 
 | 		throw "Invalid value"; | 
 | 	 | 
 | 	// Find the minimal version number to use | 
 | 	int version, dataUsedBits; | 
 | 	for (version = minVersion; ; version++) { | 
 | 		int dataCapacityBits = getNumDataCodewords(version, ecl) * 8;  // Number of data bits available | 
 | 		dataUsedBits = QrSegment::getTotalBits(segs, version); | 
 | 		if (dataUsedBits != -1 && dataUsedBits <= dataCapacityBits) | 
 | 			break;  // This version number is found to be suitable | 
 | 		if (version >= maxVersion)  // All versions in the range could not fit the given data | 
 | 			throw "Data too long"; | 
 | 	} | 
 | 	if (dataUsedBits == -1) | 
 | 		throw "Assertion error"; | 
 | 	 | 
 | 	// Increase the error correction level while the data still fits in the current version number | 
 | 	for (Ecc newEcl : vector<Ecc>{Ecc::MEDIUM, Ecc::QUARTILE, Ecc::HIGH}) { | 
 | 		if (boostEcl && dataUsedBits <= getNumDataCodewords(version, newEcl) * 8) | 
 | 			ecl = newEcl; | 
 | 	} | 
 | 	 | 
 | 	// Create the data bit string by concatenating all segments | 
 | 	size_t dataCapacityBits = getNumDataCodewords(version, ecl) * 8; | 
 | 	BitBuffer bb; | 
 | 	for (const QrSegment &seg : segs) { | 
 | 		bb.appendBits(seg.getMode().getModeBits(), 4); | 
 | 		bb.appendBits(seg.getNumChars(), seg.getMode().numCharCountBits(version)); | 
 | 		bb.insert(bb.end(), seg.getData().begin(), seg.getData().end()); | 
 | 	} | 
 | 	 | 
 | 	// Add terminator and pad up to a byte if applicable | 
 | 	bb.appendBits(0, std::min<size_t>(4, dataCapacityBits - bb.size())); | 
 | 	bb.appendBits(0, (8 - bb.size() % 8) % 8); | 
 | 	 | 
 | 	// Pad with alternate bytes until data capacity is reached | 
 | 	for (uint8_t padByte = 0xEC; bb.size() < dataCapacityBits; padByte ^= 0xEC ^ 0x11) | 
 | 		bb.appendBits(padByte, 8); | 
 | 	if (bb.size() % 8 != 0) | 
 | 		throw "Assertion error"; | 
 | 	 | 
 | 	// Create the QR Code symbol | 
 | 	return QrCode(version, ecl, bb.getBytes(), mask); | 
 | } | 
 |  | 
 |  | 
 | QrCode::QrCode(int ver, Ecc ecl, const vector<uint8_t> &dataCodewords, int mask) : | 
 | 		// Initialize fields | 
 | 		version(ver), | 
 | 		size(MIN_VERSION <= ver && ver <= MAX_VERSION ? ver * 4 + 17 : -1),  // Avoid signed overflow undefined behavior | 
 | 		errorCorrectionLevel(ecl), | 
 | 		modules(size, vector<bool>(size)),  // Entirely white grid | 
 | 		isFunction(size, vector<bool>(size)) { | 
 | 	 | 
 | 	// Check arguments | 
 | 	if (ver < MIN_VERSION || ver > MAX_VERSION || mask < -1 || mask > 7) | 
 | 		throw "Value out of range"; | 
 | 	 | 
 | 	// Draw function patterns, draw all codewords, do masking | 
 | 	drawFunctionPatterns(); | 
 | 	const vector<uint8_t> allCodewords = appendErrorCorrection(dataCodewords); | 
 | 	drawCodewords(allCodewords); | 
 | 	this->mask = handleConstructorMasking(mask); | 
 | } | 
 |  | 
 |  | 
 | int QrCode::getVersion() const { | 
 | 	return version; | 
 | } | 
 |  | 
 |  | 
 | int QrCode::getSize() const { | 
 | 	return size; | 
 | } | 
 |  | 
 |  | 
 | QrCode::Ecc QrCode::getErrorCorrectionLevel() const { | 
 | 	return errorCorrectionLevel; | 
 | } | 
 |  | 
 |  | 
 | int QrCode::getMask() const { | 
 | 	return mask; | 
 | } | 
 |  | 
 |  | 
 | bool QrCode::getModule(int x, int y) const { | 
 | 	return 0 <= x && x < size && 0 <= y && y < size && module(x, y); | 
 | } | 
 |  | 
 |  | 
 | std::string QrCode::toSvgString(int border) const { | 
 | 	if (border < 0) | 
 | 		throw "Border must be non-negative"; | 
 | 	if (border > INT_MAX / 2 || border * 2 > INT_MAX - size) | 
 | 		throw "Border too large"; | 
 | 	 | 
 | 	std::ostringstream sb; | 
 | 	sb << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; | 
 | 	sb << "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"; | 
 | 	sb << "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 "; | 
 | 	sb << (size + border * 2) << " " << (size + border * 2) << "\" stroke=\"none\">\n"; | 
 | 	sb << "\t<rect width=\"100%\" height=\"100%\" fill=\"#FFFFFF\"/>\n"; | 
 | 	sb << "\t<path d=\""; | 
 | 	bool head = true; | 
 | 	for (int y = -border; y < size + border; y++) { | 
 | 		for (int x = -border; x < size + border; x++) { | 
 | 			if (getModule(x, y)) { | 
 | 				if (head) | 
 | 					head = false; | 
 | 				else | 
 | 					sb << " "; | 
 | 				sb << "M" << (x + border) << "," << (y + border) << "h1v1h-1z"; | 
 | 			} | 
 | 		} | 
 | 	} | 
 | 	sb << "\" fill=\"#000000\"/>\n"; | 
 | 	sb << "</svg>\n"; | 
 | 	return sb.str(); | 
 | } | 
 |  | 
 |  | 
 | void QrCode::drawFunctionPatterns() { | 
 | 	// Draw horizontal and vertical timing patterns | 
 | 	for (int i = 0; i < size; i++) { | 
 | 		setFunctionModule(6, i, i % 2 == 0); | 
 | 		setFunctionModule(i, 6, i % 2 == 0); | 
 | 	} | 
 | 	 | 
 | 	// Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) | 
 | 	drawFinderPattern(3, 3); | 
 | 	drawFinderPattern(size - 4, 3); | 
 | 	drawFinderPattern(3, size - 4); | 
 | 	 | 
 | 	// Draw numerous alignment patterns | 
 | 	const vector<int> alignPatPos = getAlignmentPatternPositions(version); | 
 | 	int numAlign = alignPatPos.size(); | 
 | 	for (int i = 0; i < numAlign; i++) { | 
 | 		for (int j = 0; j < numAlign; j++) { | 
 | 			if ((i == 0 && j == 0) || (i == 0 && j == numAlign - 1) || (i == numAlign - 1 && j == 0)) | 
 | 				continue;  // Skip the three finder corners | 
 | 			else | 
 | 				drawAlignmentPattern(alignPatPos.at(i), alignPatPos.at(j)); | 
 | 		} | 
 | 	} | 
 | 	 | 
 | 	// Draw configuration data | 
 | 	drawFormatBits(0);  // Dummy mask value; overwritten later in the constructor | 
 | 	drawVersion(); | 
 | } | 
 |  | 
 |  | 
 | void QrCode::drawFormatBits(int mask) { | 
 | 	// Calculate error correction code and pack bits | 
 | 	int data = getFormatBits(errorCorrectionLevel) << 3 | mask;  // errCorrLvl is uint2, mask is uint3 | 
 | 	int rem = data; | 
 | 	for (int i = 0; i < 10; i++) | 
 | 		rem = (rem << 1) ^ ((rem >> 9) * 0x537); | 
 | 	data = data << 10 | rem; | 
 | 	data ^= 0x5412;  // uint15 | 
 | 	if (data >> 15 != 0) | 
 | 		throw "Assertion error"; | 
 | 	 | 
 | 	// Draw first copy | 
 | 	for (int i = 0; i <= 5; i++) | 
 | 		setFunctionModule(8, i, ((data >> i) & 1) != 0); | 
 | 	setFunctionModule(8, 7, ((data >> 6) & 1) != 0); | 
 | 	setFunctionModule(8, 8, ((data >> 7) & 1) != 0); | 
 | 	setFunctionModule(7, 8, ((data >> 8) & 1) != 0); | 
 | 	for (int i = 9; i < 15; i++) | 
 | 		setFunctionModule(14 - i, 8, ((data >> i) & 1) != 0); | 
 | 	 | 
 | 	// Draw second copy | 
 | 	for (int i = 0; i <= 7; i++) | 
 | 		setFunctionModule(size - 1 - i, 8, ((data >> i) & 1) != 0); | 
 | 	for (int i = 8; i < 15; i++) | 
 | 		setFunctionModule(8, size - 15 + i, ((data >> i) & 1) != 0); | 
 | 	setFunctionModule(8, size - 8, true); | 
 | } | 
 |  | 
 |  | 
 | void QrCode::drawVersion() { | 
 | 	if (version < 7) | 
 | 		return; | 
 | 	 | 
 | 	// Calculate error correction code and pack bits | 
 | 	int rem = version;  // version is uint6, in the range [7, 40] | 
 | 	for (int i = 0; i < 12; i++) | 
 | 		rem = (rem << 1) ^ ((rem >> 11) * 0x1F25); | 
 | 	long data = (long)version << 12 | rem;  // uint18 | 
 | 	if (data >> 18 != 0) | 
 | 		throw "Assertion error"; | 
 | 	 | 
 | 	// Draw two copies | 
 | 	for (int i = 0; i < 18; i++) { | 
 | 		bool bit = ((data >> i) & 1) != 0; | 
 | 		int a = size - 11 + i % 3, b = i / 3; | 
 | 		setFunctionModule(a, b, bit); | 
 | 		setFunctionModule(b, a, bit); | 
 | 	} | 
 | } | 
 |  | 
 |  | 
 | void QrCode::drawFinderPattern(int x, int y) { | 
 | 	for (int i = -4; i <= 4; i++) { | 
 | 		for (int j = -4; j <= 4; j++) { | 
 | 			int dist = std::max(std::abs(i), std::abs(j));  // Chebyshev/infinity norm | 
 | 			int xx = x + j, yy = y + i; | 
 | 			if (0 <= xx && xx < size && 0 <= yy && yy < size) | 
 | 				setFunctionModule(xx, yy, dist != 2 && dist != 4); | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 |  | 
 | void QrCode::drawAlignmentPattern(int x, int y) { | 
 | 	for (int i = -2; i <= 2; i++) { | 
 | 		for (int j = -2; j <= 2; j++) | 
 | 			setFunctionModule(x + j, y + i, std::max(std::abs(i), std::abs(j)) != 1); | 
 | 	} | 
 | } | 
 |  | 
 |  | 
 | void QrCode::setFunctionModule(int x, int y, bool isBlack) { | 
 | 	modules.at(y).at(x) = isBlack; | 
 | 	isFunction.at(y).at(x) = true; | 
 | } | 
 |  | 
 |  | 
 | bool QrCode::module(int x, int y) const { | 
 | 	return modules.at(y).at(x); | 
 | } | 
 |  | 
 |  | 
 | vector<uint8_t> QrCode::appendErrorCorrection(const vector<uint8_t> &data) const { | 
 | 	if (data.size() != static_cast<unsigned int>(getNumDataCodewords(version, errorCorrectionLevel))) | 
 | 		throw "Invalid argument"; | 
 | 	 | 
 | 	// Calculate parameter numbers | 
 | 	int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[static_cast<int>(errorCorrectionLevel)][version]; | 
 | 	int blockEccLen = ECC_CODEWORDS_PER_BLOCK[static_cast<int>(errorCorrectionLevel)][version]; | 
 | 	int rawCodewords = getNumRawDataModules(version) / 8; | 
 | 	int numShortBlocks = numBlocks - rawCodewords % numBlocks; | 
 | 	int shortBlockLen = rawCodewords / numBlocks; | 
 | 	 | 
 | 	// Split data into blocks and append ECC to each block | 
 | 	vector<vector<uint8_t> > blocks; | 
 | 	const ReedSolomonGenerator rs(blockEccLen); | 
 | 	for (int i = 0, k = 0; i < numBlocks; i++) { | 
 | 		vector<uint8_t> dat(data.cbegin() + k, data.cbegin() + (k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1))); | 
 | 		k += dat.size(); | 
 | 		const vector<uint8_t> ecc = rs.getRemainder(dat); | 
 | 		if (i < numShortBlocks) | 
 | 			dat.push_back(0); | 
 | 		dat.insert(dat.end(), ecc.cbegin(), ecc.cend()); | 
 | 		blocks.push_back(std::move(dat)); | 
 | 	} | 
 | 	 | 
 | 	// Interleave (not concatenate) the bytes from every block into a single sequence | 
 | 	vector<uint8_t> result; | 
 | 	for (int i = 0; static_cast<unsigned int>(i) < blocks.at(0).size(); i++) { | 
 | 		for (int j = 0; static_cast<unsigned int>(j) < blocks.size(); j++) { | 
 | 			// Skip the padding byte in short blocks | 
 | 			if (i != shortBlockLen - blockEccLen || j >= numShortBlocks) | 
 | 				result.push_back(blocks.at(j).at(i)); | 
 | 		} | 
 | 	} | 
 | 	if (result.size() != static_cast<unsigned int>(rawCodewords)) | 
 | 		throw "Assertion error"; | 
 | 	return result; | 
 | } | 
 |  | 
 |  | 
 | void QrCode::drawCodewords(const vector<uint8_t> &data) { | 
 | 	if (data.size() != static_cast<unsigned int>(getNumRawDataModules(version) / 8)) | 
 | 		throw "Invalid argument"; | 
 | 	 | 
 | 	size_t i = 0;  // Bit index into the data | 
 | 	// Do the funny zigzag scan | 
 | 	for (int right = size - 1; right >= 1; right -= 2) {  // Index of right column in each column pair | 
 | 		if (right == 6) | 
 | 			right = 5; | 
 | 		for (int vert = 0; vert < size; vert++) {  // Vertical counter | 
 | 			for (int j = 0; j < 2; j++) { | 
 | 				int x = right - j;  // Actual x coordinate | 
 | 				bool upward = ((right + 1) & 2) == 0; | 
 | 				int y = upward ? size - 1 - vert : vert;  // Actual y coordinate | 
 | 				if (!isFunction.at(y).at(x) && i < data.size() * 8) { | 
 | 					modules.at(y).at(x) = ((data.at(i >> 3) >> (7 - (i & 7))) & 1) != 0; | 
 | 					i++; | 
 | 				} | 
 | 				// If there are any remainder bits (0 to 7), they are already | 
 | 				// set to 0/false/white when the grid of modules was initialized | 
 | 			} | 
 | 		} | 
 | 	} | 
 | 	if (static_cast<unsigned int>(i) != data.size() * 8) | 
 | 		throw "Assertion error"; | 
 | } | 
 |  | 
 |  | 
 | void QrCode::applyMask(int mask) { | 
 | 	if (mask < 0 || mask > 7) | 
 | 		throw "Mask value out of range"; | 
 | 	for (int y = 0; y < size; y++) { | 
 | 		for (int x = 0; x < size; x++) { | 
 | 			bool invert; | 
 | 			switch (mask) { | 
 | 				case 0:  invert = (x + y) % 2 == 0;                    break; | 
 | 				case 1:  invert = y % 2 == 0;                          break; | 
 | 				case 2:  invert = x % 3 == 0;                          break; | 
 | 				case 3:  invert = (x + y) % 3 == 0;                    break; | 
 | 				case 4:  invert = (x / 3 + y / 2) % 2 == 0;            break; | 
 | 				case 5:  invert = x * y % 2 + x * y % 3 == 0;          break; | 
 | 				case 6:  invert = (x * y % 2 + x * y % 3) % 2 == 0;    break; | 
 | 				case 7:  invert = ((x + y) % 2 + x * y % 3) % 2 == 0;  break; | 
 | 				default:  throw "Assertion error"; | 
 | 			} | 
 | 			modules.at(y).at(x) = modules.at(y).at(x) ^ (invert & !isFunction.at(y).at(x)); | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 |  | 
 | int QrCode::handleConstructorMasking(int mask) { | 
 | 	if (mask == -1) {  // Automatically choose best mask | 
 | 		long minPenalty = LONG_MAX; | 
 | 		for (int i = 0; i < 8; i++) { | 
 | 			drawFormatBits(i); | 
 | 			applyMask(i); | 
 | 			long penalty = getPenaltyScore(); | 
 | 			if (penalty < minPenalty) { | 
 | 				mask = i; | 
 | 				minPenalty = penalty; | 
 | 			} | 
 | 			applyMask(i);  // Undoes the mask due to XOR | 
 | 		} | 
 | 	} | 
 | 	if (mask < 0 || mask > 7) | 
 | 		throw "Assertion error"; | 
 | 	drawFormatBits(mask);  // Overwrite old format bits | 
 | 	applyMask(mask);  // Apply the final choice of mask | 
 | 	return mask;  // The caller shall assign this value to the final-declared field | 
 | } | 
 |  | 
 |  | 
 | long QrCode::getPenaltyScore() const { | 
 | 	long result = 0; | 
 | 	 | 
 | 	// Adjacent modules in row having same color | 
 | 	for (int y = 0; y < size; y++) { | 
 | 		bool colorX = false; | 
 | 		for (int x = 0, runX = -1; x < size; x++) { | 
 | 			if (x == 0 || module(x, y) != colorX) { | 
 | 				colorX = module(x, y); | 
 | 				runX = 1; | 
 | 			} else { | 
 | 				runX++; | 
 | 				if (runX == 5) | 
 | 					result += PENALTY_N1; | 
 | 				else if (runX > 5) | 
 | 					result++; | 
 | 			} | 
 | 		} | 
 | 	} | 
 | 	// Adjacent modules in column having same color | 
 | 	for (int x = 0; x < size; x++) { | 
 | 		bool colorY = false; | 
 | 		for (int y = 0, runY = -1; y < size; y++) { | 
 | 			if (y == 0 || module(x, y) != colorY) { | 
 | 				colorY = module(x, y); | 
 | 				runY = 1; | 
 | 			} else { | 
 | 				runY++; | 
 | 				if (runY == 5) | 
 | 					result += PENALTY_N1; | 
 | 				else if (runY > 5) | 
 | 					result++; | 
 | 			} | 
 | 		} | 
 | 	} | 
 | 	 | 
 | 	// 2*2 blocks of modules having same color | 
 | 	for (int y = 0; y < size - 1; y++) { | 
 | 		for (int x = 0; x < size - 1; x++) { | 
 | 			bool  color = module(x, y); | 
 | 			if (  color == module(x + 1, y) && | 
 | 			      color == module(x, y + 1) && | 
 | 			      color == module(x + 1, y + 1)) | 
 | 				result += PENALTY_N2; | 
 | 		} | 
 | 	} | 
 | 	 | 
 | 	// Finder-like pattern in rows | 
 | 	for (int y = 0; y < size; y++) { | 
 | 		for (int x = 0, bits = 0; x < size; x++) { | 
 | 			bits = ((bits << 1) & 0x7FF) | (module(x, y) ? 1 : 0); | 
 | 			if (x >= 10 && (bits == 0x05D || bits == 0x5D0))  // Needs 11 bits accumulated | 
 | 				result += PENALTY_N3; | 
 | 		} | 
 | 	} | 
 | 	// Finder-like pattern in columns | 
 | 	for (int x = 0; x < size; x++) { | 
 | 		for (int y = 0, bits = 0; y < size; y++) { | 
 | 			bits = ((bits << 1) & 0x7FF) | (module(x, y) ? 1 : 0); | 
 | 			if (y >= 10 && (bits == 0x05D || bits == 0x5D0))  // Needs 11 bits accumulated | 
 | 				result += PENALTY_N3; | 
 | 		} | 
 | 	} | 
 | 	 | 
 | 	// Balance of black and white modules | 
 | 	int black = 0; | 
 | 	for (const vector<bool> &row : modules) { | 
 | 		for (bool color : row) { | 
 | 			if (color) | 
 | 				black++; | 
 | 		} | 
 | 	} | 
 | 	int total = size * size; | 
 | 	// Find smallest k such that (45-5k)% <= dark/total <= (55+5k)% | 
 | 	for (int k = 0; black*20L < (9L-k)*total || black*20L > (11L+k)*total; k++) | 
 | 		result += PENALTY_N4; | 
 | 	return result; | 
 | } | 
 |  | 
 |  | 
 | vector<int> QrCode::getAlignmentPatternPositions(int ver) { | 
 | 	if (ver < MIN_VERSION || ver > MAX_VERSION) { | 
 | 		throw "Version number out of range"; | 
 | 		return vector<int>(); | 
 | 	} else if (ver == 1) { | 
 | 		return vector<int>(); | 
 | 	} else { | 
 | 		int numAlign = ver / 7 + 2; | 
 | 		int step; | 
 | 		if (ver != 32) { | 
 | 			// ceil((size - 13) / (2*numAlign - 2)) * 2 | 
 | 			step = (ver * 4 + numAlign * 2 + 1) / (2 * numAlign - 2) * 2; | 
 | 		} else  // C-C-C-Combo breaker! | 
 | 			step = 26; | 
 | 		 | 
 | 		vector<int> result; | 
 | 		for (int i = 0, pos = ver * 4 + 10; i < numAlign - 1; i++, pos -= step) | 
 | 			result.insert(result.begin(), pos); | 
 | 		result.insert(result.begin(), 6); | 
 | 		return result; | 
 | 	} | 
 | } | 
 |  | 
 |  | 
 | int QrCode::getNumRawDataModules(int ver) { | 
 | 	if (ver < MIN_VERSION || ver > MAX_VERSION) | 
 | 		throw "Version number out of range"; | 
 | 	int result = (16 * ver + 128) * ver + 64; | 
 | 	if (ver >= 2) { | 
 | 		int numAlign = ver / 7 + 2; | 
 | 		result -= (25 * numAlign - 10) * numAlign - 55; | 
 | 		if (ver >= 7) | 
 | 			result -= 18 * 2;  // Subtract version information | 
 | 	} | 
 | 	return result; | 
 | } | 
 |  | 
 |  | 
 | int QrCode::getNumDataCodewords(int ver, Ecc ecl) { | 
 | 	if (ver < MIN_VERSION || ver > MAX_VERSION) | 
 | 		throw "Version number out of range"; | 
 | 	return getNumRawDataModules(ver) / 8 | 
 | 		- ECC_CODEWORDS_PER_BLOCK[static_cast<int>(ecl)][ver] | 
 | 		* NUM_ERROR_CORRECTION_BLOCKS[static_cast<int>(ecl)][ver]; | 
 | } | 
 |  | 
 |  | 
 | /*---- Tables of constants ----*/ | 
 |  | 
 | const int QrCode::PENALTY_N1 = 3; | 
 | const int QrCode::PENALTY_N2 = 3; | 
 | const int QrCode::PENALTY_N3 = 40; | 
 | const int QrCode::PENALTY_N4 = 10; | 
 |  | 
 |  | 
 | const int8_t QrCode::ECC_CODEWORDS_PER_BLOCK[4][41] = { | 
 | 	// Version: (note that index 0 is for padding, and is set to an illegal value) | 
 | 	//0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40    Error correction level | 
 | 	{-1,  7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30},  // Low | 
 | 	{-1, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28},  // Medium | 
 | 	{-1, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30},  // Quartile | 
 | 	{-1, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30},  // High | 
 | }; | 
 |  | 
 | const int8_t QrCode::NUM_ERROR_CORRECTION_BLOCKS[4][41] = { | 
 | 	// Version: (note that index 0 is for padding, and is set to an illegal value) | 
 | 	//0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40    Error correction level | 
 | 	{-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4,  4,  4,  4,  4,  6,  6,  6,  6,  7,  8,  8,  9,  9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25},  // Low | 
 | 	{-1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5,  5,  8,  9,  9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49},  // Medium | 
 | 	{-1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8,  8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68},  // Quartile | 
 | 	{-1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81},  // High | 
 | }; | 
 |  | 
 |  | 
 | QrCode::ReedSolomonGenerator::ReedSolomonGenerator(int degree) : | 
 | 		coefficients() { | 
 | 	if (degree < 1 || degree > 255) | 
 | 		throw "Degree out of range"; | 
 | 	 | 
 | 	// Start with the monomial x^0 | 
 | 	coefficients.resize(degree); | 
 | 	coefficients.at(degree - 1) = 1; | 
 | 	 | 
 | 	// Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), | 
 | 	// drop the highest term, and store the rest of the coefficients in order of descending powers. | 
 | 	// Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). | 
 | 	uint8_t root = 1; | 
 | 	for (int i = 0; i < degree; i++) { | 
 | 		// Multiply the current product by (x - r^i) | 
 | 		for (size_t j = 0; j < coefficients.size(); j++) { | 
 | 			coefficients.at(j) = multiply(coefficients.at(j), root); | 
 | 			if (j + 1 < coefficients.size()) | 
 | 				coefficients.at(j) ^= coefficients.at(j + 1); | 
 | 		} | 
 | 		root = multiply(root, 0x02); | 
 | 	} | 
 | } | 
 |  | 
 |  | 
 | vector<uint8_t> QrCode::ReedSolomonGenerator::getRemainder(const vector<uint8_t> &data) const { | 
 | 	// Compute the remainder by performing polynomial division | 
 | 	vector<uint8_t> result(coefficients.size()); | 
 | 	for (uint8_t b : data) { | 
 | 		uint8_t factor = b ^ result.at(0); | 
 | 		result.erase(result.begin()); | 
 | 		result.push_back(0); | 
 | 		for (size_t j = 0; j < result.size(); j++) | 
 | 			result.at(j) ^= multiply(coefficients.at(j), factor); | 
 | 	} | 
 | 	return result; | 
 | } | 
 |  | 
 |  | 
 | uint8_t QrCode::ReedSolomonGenerator::multiply(uint8_t x, uint8_t y) { | 
 | 	// Russian peasant multiplication | 
 | 	int z = 0; | 
 | 	for (int i = 7; i >= 0; i--) { | 
 | 		z = (z << 1) ^ ((z >> 7) * 0x11D); | 
 | 		z ^= ((y >> i) & 1) * x; | 
 | 	} | 
 | 	if (z >> 8 != 0) | 
 | 		throw "Assertion error"; | 
 | 	return static_cast<uint8_t>(z); | 
 | } | 
 |  | 
 | } |