#include <iostream>
#include <cmath>
#include <string>
#include <vector>
using namespace std;
int origAA[28];
const int ROWS = 25;
const int COLS = 25;
// 1. PEDIR LINK AL USUARIO:
string pedirLinkUsuario() {
string entrada;
cout << "Por favor, ingresa el link a codificar: ";
cin >> entrada;
return entrada;
}
// 2. LINK A BINARIO:
// Conversión Entero a binario
void decimalABinario(vector<int>& listaBits, int valorDecimal, int cantidadBits) {
// Se recorre desde el bit más significativo hacia abajo
for (int i = cantidadBits - 1; i >= 0; --i) {
int bit = (valorDecimal >> i) & 1;
listaBits.push_back(bit);
}
}
// Función Binario a Decimal (longitud)
int binarioADecimal(const vector<int>& bits) {
int valorDecimal = 0;
int potencia = 1; // Empezamos con 2^0 = 1
// Recorre el vector de atrás hacia adelante
for (int i = bits.size() - 1; i >= 0; --i) {
if (bits[i] == 1) {
valorDecimal += potencia;
}
potencia *= 2; // La potencia se duplica en cada paso (1, 2, 4, 8...)
}
return valorDecimal;
}
vector<int> procesarLink(string linkUsuario) {
vector<int> listaBits;
// A. MODO DE CODIFICACIÓN Binario (0100)
decimalABinario(listaBits, 4, 4);
// B. INDICADOR DE LONGITUD (8 bits)
int cantidadLetras = linkUsuario.length();
decimalABinario(listaBits, cantidadLetras, 8);
// C. TRADUCCIÓN DE DATOS (ASCII a Binario)
// Recorre el link letra por letra y convertimos su valor ASCII a 8 bits.
for (int i = 0; i < cantidadLetras; ++i) {
char letra = linkUsuario[i];
decimalABinario(listaBits, letra, 8);
}
// D. TERMINACIÓN Y ALINEACIÓN
// 224 bits (28 bytes).
int capacidadTotalBits = 224;
int bitsUsados = (int)listaBits.size();
int espacioLibre = capacidadTotalBits - bitsUsados;
// A) Terminación (0000)
if (espacioLibre >= 4) {
decimalABinario(listaBits, 0, 4);
} else if (espacioLibre > 0) {
decimalABinario(listaBits, 0, espacioLibre);
}
// B) Alineación a Byte (Total de bits = múltiplo de 8)
while (listaBits.size() % 8 != 0) {
listaBits.push_back(0);
}
// E. RELLENO (PADDING BYTES)
// Si sobran bytes vacíos, se rellenan alternando 236 (11101100) y 17 (00010001)
int byteRelleno1 = 236; // 11101100
int byteRelleno2 = 17; // 00010001
bool usarPrimerByte = true;
while (listaBits.size() < 224) {
if (usarPrimerByte) {
decimalABinario(listaBits, byteRelleno1, 8);
} else {
decimalABinario(listaBits, byteRelleno2, 8);
}
// Cambiamos el turno para el siguiente byte
usarPrimerByte = !usarPrimerByte;
}
return listaBits;
}
// 3. MOSTRAR VECTOR RESULTANTE
void mostrarVectorBits(const vector<int>& bits) {
cout << "\n---------------------------------------------------------------------" << endl;
cout << "----------------------------VECTOR DE BITS---------------------------" << endl;
cout << "---------------------------------------------------------------------\n"<< endl;
int contador = 0;
for(int i = 0; i < bits.size(); ++i) {
cout << bits[i];
// Formato visual: espacio cada 8 bits (1 byte)
contador++;
if (contador % 8 == 0) cout << " ";
if (contador % 64 == 0) cout << endl; // Salto de línea cada 64 bits
}
cout << endl << endl;
}
//Convertir un dato de binario a decimal
int binario_Decimal(string a){
int resultado = 0;
for (int i = 0; i < 8; i++) {
resultado = resultado * 2;
if (a[i] == '1')
resultado += 1;
}
return resultado;
}
//Convertir un dato de decimal a binario
string decimal_Binario(int a){
string bin = "00000000";
for (int i = 7; i >= 0; i--) {
bin[i] = (a % 2) + '0';
a /= 2;
}
return bin;
}
//Formar el vector en decimal para la correcion
void VectorParaCorreccion(vector<int>& vectorbits){
int indice = 0;
string cadena="";
string origBinario[28];
for (int i = 0; i < 224; i++){
cadena += to_string(vectorbits[i]);
}
for (size_t i = 0; i < cadena.size(); i += 8) {
origBinario[indice] = cadena.substr(i, 8);
origAA[indice]=binario_Decimal(origBinario[indice]);
indice++;
}
}
//Multiplicar dos datos en MG
int M_GF(int a, int b){
int multiplicacionAA;
int pol = 285;
int grande;
multiplicacionAA = 0;
for (int i = 0; i < 8; i++) {
if (b % 2 == 1){
multiplicacionAA = multiplicacionAA ^ a;
}
grande = (a >= 128);
a = a * 2;
if (grande){
a = a ^ pol;
}
a = a & 255;
b = b / 2;
}
return multiplicacionAA;
}
//Correcion y formar vector para acomodar
vector<string> RS(){
vector<string> VectorParaAcomodar(45);
int VectorRsD[16];
int multiplicacionAA;
int c, a, b, contador = 0, contador2 = 0;
int generador[17] = {1, 59, 13, 104, 189, 68, 209, 30, 8, 163, 65, 41, 229, 98, 50, 36, 59};
int junto[44];
for (int i = 0; i < 44; i++){
if (i < 28){
junto[i] = origAA[i];
}
if (i >= 28){
junto[i] = 0;
}
}
for (int i = 0; i < 28; i++) {
c = junto[i];
if (c != 0){
for (int j = 0; j < 17; j++) {
a = generador[j];
b = c;
multiplicacionAA = M_GF(a, b);
junto[i + j] = junto[i + j] ^ multiplicacionAA;
}
}
}
for (int i = 28; i < 44; i++){
VectorRsD[contador] = junto[i];
contador += 1;
}
for (int i = 0; i < 45; i++){
if (i<28){
VectorParaAcomodar[i] = decimal_Binario(origAA[i]);
}
if ((i >= 28) && (i < 44)){
VectorParaAcomodar[i] = decimal_Binario(VectorRsD[contador2]);
contador2 ++;
}
if (i==44){
VectorParaAcomodar[i] = "0000000";
}
}
return VectorParaAcomodar;
}
/*
Blanco = 2
Negro = 3
Amarillo = 4
Azul = 5
Vacío = 9
*/
void inicializarMatriz(int matriz[ROWS][COLS]) {
for (int i = 0; i < ROWS; ++i){
for (int j = 0; j < COLS; ++j){
matriz[i][j] = 9;
}
}
}
// Dibuja un cuadro grande de posicionamiento 7x7 con borde blanco de 1 celda
void dibujarFinder(int matriz[ROWS][COLS], int top, int left) {
const int size = 7;
// Perímetro blanco (2)
for (int dy = -1; dy <= size; ++dy) {
for (int dx = -1; dx <= size; ++dx) {
int y = top + dy;
int x = left + dx;
if (y >= 0 && y < ROWS && x >= 0 && x < COLS) {
matriz[y][x] = 2;
}
}
}
// 7x7 negro (3)
for (int dy = 0; dy < size; ++dy) {
for (int dx = 0; dx < size; ++dx) {
matriz[top + dy][left + dx] = 3;
}
}
// 5x5 blanco interior (2)
for (int dy = 1; dy < size - 1; ++dy) {
for (int dx = 1; dx < size - 1; ++dx) {
matriz[top + dy][left + dx] = 2;
}
}
// 3x3 central negro (3)
for (int dy = 2; dy < size - 2; ++dy) {
for (int dx = 2; dx < size - 2; ++dx) {
matriz[top + dy][left + dx] = 3;
}
}
}
// Cuadro pequeño de alineación 5x5
void dibujarAlignment(int matriz[ROWS][COLS], int top, int left) {
const int size = 3;
left += 1;
top += 1;
// Perímetro negro (3)
for (int dy = -1; dy <= size; ++dy) {
for (int dx = -1; dx <= size; ++dx) {
int y = top + dy;
int x = left + dx;
if (y >= 0 && y < ROWS && x >= 0 && x < COLS) {
matriz[y][x] = 3;
}
}
}
// Cuadro 3×3 blanco
for (int dy = 0; dy < size; ++dy) {
for (int dx = 0; dx < size; ++dx) {
matriz[top + dy][left + dx] = 2;
}
}
// Interior 1×1 negro
matriz[top + 1][left + 1] = 3;
}
// Coloca los cuadros grandes (UL, UR, LL)
void colocarFinders(int matriz[ROWS][COLS]) {
const int size = 7;
dibujarFinder(matriz, 0, 0); // Superior izquierda
dibujarFinder(matriz, 0, COLS - size); // Superior derecha
dibujarFinder(matriz, ROWS - size, 0); // Inferior izquierda
}
// dibuja las lineas de sincronizacion
void dibujarTimingPatterns(int matriz[ROWS][COLS]) {
int start = 8;
int end = COLS - 9;
// horizontal (linea 6)
int row = 6;
for (int j = start; j <= end; ++j) {
if ((j - start) % 2 == 0) {
matriz[row][j] = 3;
} else {
matriz[row][j] = 2;
}
}
// vertical (columna 6)
int col = 6;
for (int i = start; i <= end; ++i) {
if ((i - start) % 2 == 0) {
matriz[i][col] = 3;
} else {
matriz[i][col] = 2;
}
}
}
// reserva las zonas de formato para que nadie escriba encima
void reservarZonasFormato(int matriz[ROWS][COLS]) {
// zona primaria - columna 8 (filas 0-8 excepto 6)
for (int i = 0; i <= 8; ++i) {
if (i != 6) {
matriz[i][8] = 6;
}
}
// zona primaria - fila 8 (columnas 0-8 excepto 6)
for (int j = 0; j <= 8; ++j) {
if (j != 6) {
matriz[8][j] = 6;
}
}
// zona secundaria inferior - columna 8 (filas 17-24)
for (int i = 17; i <= 24; ++i) {
matriz[i][8] = 6;
}
// zona secundaria derecha - fila 8 (columnas 17-24)
for (int j = 17; j <= 24; ++j) {
matriz[8][j] = 6;
}
// dark module siempre negro
matriz[17][8] = 6;
}
// rellena las zonas de formato con los bits correctos
// version 2 m mask 0 -> 101010000010010
void colocarFormato(int matriz[ROWS][COLS]) {
// bits clonados en dos lugares
int formatoBits[15] = {1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0};
// ubicacion primaria (alrededor del finder superior izquierdo)
// primero: columna 8, filas 0-5, luego fila 7, luego fila 8
matriz[0][8] = formatoBits[0];
matriz[1][8] = formatoBits[1];
matriz[2][8] = formatoBits[2];
matriz[3][8] = formatoBits[3];
matriz[4][8] = formatoBits[4];
matriz[5][8] = formatoBits[5];
// saltar fila 6
matriz[7][8] = formatoBits[6];
matriz[8][8] = formatoBits[7];
// segundo: fila 8, columnas 7, 5, 4, 3, 2, 1, 0
matriz[8][7] = formatoBits[8];
// saltar columna 6
matriz[8][5] = formatoBits[9];
matriz[8][4] = formatoBits[10];
matriz[8][3] = formatoBits[11];
matriz[8][2] = formatoBits[12];
matriz[8][1] = formatoBits[13];
matriz[8][0] = formatoBits[14];
// ubicacion secundaria (lado contrario)
// columna 8, filas desde abajo (24..18)
matriz[24][8] = formatoBits[0];
matriz[23][8] = formatoBits[1];
matriz[22][8] = formatoBits[2];
matriz[21][8] = formatoBits[3];
matriz[20][8] = formatoBits[4];
matriz[19][8] = formatoBits[5];
matriz[18][8] = formatoBits[6];
// dark module (17,8) siempre negro
matriz[17][8] = 1;
// fila 8, columnas desde la derecha (17..24)
matriz[8][17] = formatoBits[7];
matriz[8][18] = formatoBits[8];
matriz[8][19] = formatoBits[9];
matriz[8][20] = formatoBits[10];
matriz[8][21] = formatoBits[11];
matriz[8][22] = formatoBits[12];
matriz[8][23] = formatoBits[13];
matriz[8][24] = formatoBits[14];
}
// Muestra el QR (símbolos distintos para poder distinguir en consola)
void mostrarMatriz(int matriz[ROWS][COLS]) {
// CARACTERES
for (int i = 0; i < ROWS; ++i) {
for (int j = 0; j < COLS; ++j) {
switch (matriz[i][j]) {
case 2: {
cout << char(219) << char(219);
break;
}
case 3: {
cout << " ";
break;
}
case 4: {
cout << "::";
break;
}
case 5: {
cout << "@@";
break;
}
case 9: {
cout << "##";
break;
}
default: {
cout << "??";
break;
}
}
}
cout << "\n";
}
cout << "\n\n";
// VALORES MATRIZ
for (int i = 0; i < ROWS; ++i) {
for (int j = 0; j < COLS; ++j) {
int valor = matriz[i][j];
// revisar si esta en zona fija
bool esZonaFija = false;
// timing patterns
if (i == 6 || j == 6) {
esZonaFija = true;
}
// finder patterns y separadores
if ((i <= 8 && j <= 8) || (i <= 8 && j >= 17) || (i >= 17 && j <= 8)) {
esZonaFija = true;
}
// alignment pattern (16-20, 16-20)
if (i >= 16 && i <= 20 && j >= 16 && j <= 20) {
esZonaFija = true;
}
// aplicar mascara 000 solo a datos
if (!esZonaFija && (valor == 0 || valor == 1) && ((i + j) % 2 == 0)) {
valor = valor ^ 1; // Invertir el bit
}
// mostrar cuadros blancos y negros
if (valor == 0 || valor == 2) {
cout << " "; // Blanco
} else if (valor == 1 || valor == 3) {
cout << char(219) << char(219); // Negro
} else {
// cualquier otro valor lo dejamos blanco
cout << " ";
}
}
cout << "\n";
}
}
// Une todos los bytes del vector en una sola cadena de bits
string unirBits(const vector<string>& VectorParaAcomodar) {
string bits;
for (const string& byteStr : VectorParaAcomodar) {
bits += byteStr;
}
return bits;
}
void acomodarEnMatriz(const string& bits, int matriz[ROWS][COLS]) {
int idx = 0; // indice del bit actual
int col = COLS - 1; // para empezar en la ultima columna
bool haciaArriba = true; // direccion inicial (de abajo hacia arriba)
while (col > 0 && idx < (int)bits.size()) {
if (col == 6) col--; // saltar columna de timing vertical
int c1 = col;
int c2 = col - 1;
if (haciaArriba) {
for (int fila = ROWS - 1; fila >= 0 && idx < (int)bits.size(); --fila) {
if (fila == 6) continue; // saltar linea de timing horizontal
for (int c : {c1, c2}) {
if (c < 0) continue;
if (c == 6) continue; // saltar columna de timing vertical
// solo colocar datos en celdas vacias (9)
// no tocar: finders (2,3), timing (2,3), alignment (2,3), formato (6)
if (matriz[fila][c] == 9) {
matriz[fila][c] = (bits[idx] == '1') ? 1 : 0;
idx++;
if (idx >= (int)bits.size()) break;
}
}
}
} else {
for (int fila = 0; fila < ROWS && idx < (int)bits.size(); ++fila) {
if (fila == 6) continue; // saltar linea de timing horizontal
for (int c : {c1, c2}) {
if (c < 0) continue;
if (c == 6) continue; // saltar columna de timing vertical
// solo colocar datos en celdas vacias (9)
// no tocar: finders (2,3), timing (2,3), alignment (2,3), formato (6)
if (matriz[fila][c] == 9) {
matriz[fila][c] = (bits[idx] == '1') ? 1 : 0;
idx++;
if (idx >= (int)bits.size()) break;
}
}
}
}
col -= 2;
haciaArriba = !haciaArriba;
}
}
int main() {
string linkUsuario = pedirLinkUsuario();
vector<int> bitsFinales = procesarLink(linkUsuario);
mostrarVectorBits(bitsFinales);
VectorParaCorreccion(bitsFinales);
vector<string> VectorParaAcomodar = RS();
static int matriz[ROWS][COLS];
inicializarMatriz(matriz);
colocarFinders(matriz);
dibujarAlignment(matriz, ROWS - 9, COLS - 9);
dibujarTimingPatterns(matriz); // dibujar timing antes de colocar datos
reservarZonasFormato(matriz); // reservar zonas de formato
string bitsAcomodar = unirBits(VectorParaAcomodar);
acomodarEnMatriz(bitsAcomodar, matriz);
// colocar formato al final (pisar los marcadores)
colocarFormato(matriz);
// volver a dibujar los timing por si alguien los piso
dibujarTimingPatterns(matriz);
mostrarMatriz(matriz);
return 0;
}