Conversión de binario a decimal

El uso de los números binarios es mas importante de lo que mucha gente cree. Se trata del lenguaje que el ordenador entiende, ya que este interpreta diferencias de voltaje como 1 (true o HI) y 0 (false o LO). Cada byte de información está compuesto por ocho dígitos binarios, y a cada cifra 0 o 1 se le llama bit. El número utilizado en el ejemplo, el 10110100, sería un byte, y cada una de sus ocho cifras, un bit. Imaginemos que tenemos el número binario 0. Al sumarle una unidad, este número binario cambiará a 1. Sin embargo, si volvemos a añadirle otra unidad, este número en formato binario será el 10 (aumenta la cifra a la izquierda, que era 0, y la anterior toma el valor mínimo). Sumemos ahora otra unidad y el aspecto del número será 11 (tres en decimal). Y podríamos seguir.

Binario Decimal
0000 0
0001 1
0010 2
0011 3
0100 4
0101 5
0110 6
0111 7
1000 8
1001 9
1010 10
1011 11
\vdots \vdots

 

Esto nos permite establecer un sistema bastante sencillo de conversión del binario al decimal. A partir de ahora, cuando escribamos un número binario, lo haremos con la notación usual, con una "b" al final del número (ej.: 10110100b)

Bien, pero ahora nos falta saber que hay que hacer para convertir un número binario a decimal. Para ello hemos de aplicar la siguiente notación matemática:

\displaystyle \sum_{i=0}^{n=bits-1} b_{i} \cdot 2^{n-i}

Donde b es el valor del bit binario en cada iteración, o sea, 0 (false) o 1 (true), y bits es el número de bits a convertir por ejemplo 10110100 es un byte por que tiene 8 bits, así que en este caso bits = 8. Así que, si queremos convertir el número binario 10110100 de 8 bits a decimal sería de la siguiente forma:

1 0 1 1 0 1 0 0
1_{0} \cdot 2^7 0_{1} \cdot 2^6  1_{2} \cdot 2^5 1_{3} \cdot 2^4 0_{4} \cdot 2^3 1_{5} \cdot 2^2 0_{6} \cdot 2^1 0_{7} \cdot 2^0

Sumando cada cálculo de bit quedaría:

1 \cdot 2^7+0+1 \cdot 2^5+1 \cdot 2^4+0+1 \cdot 2^2+0+0 \Rightarrow
\Rightarrow 128+32+16+4=180

Y finalmente se escribe:

10110100b=180

Contador de palabras de una web, en Python

Una vez me propusieron hacer el siguiente programa: "Un programa que cuente las palabras que aparecen en una web cualquiera y ordenarlas alfabéticamente". En ese momento me pregunté que para qué podría alguien querer algo así, supongo que para darle algún fin estadístico. En un principio pensé hacerlo en Perl, pero por alguna razón me vino a la cabeza BeutifulSoup para Python. Y parece que fue una buena idea. Aquí un pequeño ejemplo que funciona bien.

#!/usr/bin/python
 
import sys, re, string, urllib
# Se requiere el paquete BeautifulSoup
from bs4 import BeautifulSoup
 
# La URL es el primer argumento que recibe el programa.
url = sys.argv[1]
 
# Conversion de codigo HTML a texto plano.
texto = BeautifulSoup(urllib.urlopen(url)).get_text()

# Remplazo todos los caracteres ASCII de puntuacion por espacios e inicializa el Array
palabrasArray = re.sub('[%s]' % re.escape(string.punctuation), ' ', texto).split()
 
# Ordenacion sin repeticion de todos los elementos del Array
numero = 0
for i in sorted(set(palabrasArray)):
    # Imprime solo las palabras con caracteres alfabeticos.
    if (i.isalpha() == True):
        numero += 1
        print("%.3d %s" % (numero, i))

Un ejemplo de uso sería:

python contadorDePalabras.py http://www.gnu.org/

Y tiene una salida como esta:

001 About
002 Access
003 Action
004 Add
005 Allan
006 Also
007 America
008 April
009 Art
010 Attribution
011 BY
012 C
013 CC
014 Call
015 Can
016 Canada
017 Christopher
018 Chrome
019 Claim
020 Common
021 Commons
...

Seguro que se puede hacer mejor y mas profesional, pero así de primeras cumple su función.

Reflexión sobre Bitcoin y su uso

Que levante la mano el que sea un experto en Bitcoin. Como bien dijo una vez un amigo "voy a dar mi opinión como experto de barra de bar" sobre el Bitcoin y su uso. Si has llegado hasta aquí y estás leyendo esto es por que no te pilla de nuevas, por lo tanto no voy a explicar lo que es desde el origen de los tiempos.

El otro día hablando con algunos compañeros, una persona mostró interés por el Bitcoin, decía algo así como "he leído por ahí que es una moneda en la que cualquiera puede invertir como en la bolsa, y que su precio fluctúa tanto que puede llegar a doblar su precio fácilmente, con lo cual te puedes forrar". Alguno ya estará sonriendo, ¿a que si?

Al final lo que cuentan son las intenciones, y parece que siempre hay alguien que está buscando alguna opción para sacar el mayor beneficio posible con la menor inversión posible. Y si puede ser, sin riesgos, por favor. Desde el punto de vista del que escribe, estas intenciones son perjudiciales para la comunidad, pero sobre todo para uno mismo. Los sistemas de producción capitalistas  funcionan (y terminan) siempre igual, cuidado. El Bitcoin no se creó para esto, no se ideó para ganar dinero, aunque algunos pocos hicieran mucho dinero en los tiempos de MtGox, a costa de otros pobres desgraciados que ante el pánico de perder todo malvendieron corriendo lo que pudieron. Ese no es el camino.

Las cosas hay que explicarlas sencillas, sin demasiados tecnicismos y sin necesidad de pavonearse con un exceso de intelecto tecnológico. Hay que hacerse entender y llegara todo el mundo. Cuántas veces habré hablado esto con mi amigo John Doe, "¿Te imaginas explicarle a mi madre cómo crear un monedero, cómo cifrarlo, cómo exportar la semilla encriptada a varios dispositivos offline, cómo importarlos en caso de pérdida y cómo cambiar la clave pública/privada cada cierto tiempo?", y eso sin contar con el entendimiento de lo que es una cadena de bloques y qué seguridad tiene o deja de tener. Señoras y señores, la finalidad no es otra que tener una independencia económica, descentralizada, fácil, rápida, barata y segura, y ya de paso escapar del control de gobiernos, bancos y otras siniestras entidades que no paran de imprimir billetes dándole a un botón. El Bitcoin aún tiene que madurar un poco más, se tiene que facilitar su adquisición y que cualquiera pueda tener un monedero de Bitcoin seguro.

¿Sería buena idea que los bancos gestionaran la apertura de un monedero y las operaciones de BTC como si de una cuenta corriente se tratara?

Sería mas fácil ir al banco y decir "Buenos días, quisiera tener un monedero Bitcoin y comprar 2,5 BTC" o "Por favor, quisiera transferir 0,27 BTC a esta dirección", y en menos de 30 segundos... Si algo sale mal quizás se harían responsables, y al fin y al cabo se encargarían de toda la logística "informática" para que el usuario sin conocimientos tecnológicos pueda acceder al Bitcoin. Suena bien, pero al final el banco terminaría de un modo u otro registrando todo en sus bases de datos y harían eso que tanto les gusta hacer. ¿Que hacemos entonces?

Lo bueno es que nadie podría darle al botón de imprimir más Bitcoins, práctica muy habitual en casi todos los países con una grandísima devaluación de su moneda.

Imaginemos una sociedad en la que todo el mundo entiende de criptografía, ciberseguridad y todo el mundo tiene habilidad con los ordenadores. Es mucho soñar a corto plazo. Estamos en ello, y parece que vamos por el buen camino. Lo vital es que la gente tenga confianza; más que en este sistema monetario, en su filosofía inicial, y no pensar en aprovecharse de las circunstancias para enriquecerse, por que eso no suele funcionar, y por que no es un buen camino para llegar al objetivo inicial de todo esto. Como todo, es cultural, es una cuestión de educación de cada uno.

Login en un programa hecho en C

Imagina que tienes un programa hecho en C que sirve para desencriptar passwords. Ahora imagina que no quieres que lo utilice nadie mas que aquellos usuarios que sepan una password. Muchos pensarán que con asignar al binario unos permisos chmod y chown adecuados sería suficiente. Nunca es suficiente. Por ello, hoy voy a explicar una manera de añadir a nuestro programa un simple sistema de autenticación que funciona bien. Este es el código de mi archivo fuente softwareConLogin.c:

/* Compilacion:	gcc -std=gnu99 -Wall -c -MMD -MP -MF"softwareConLogin.d" -MT"softwareConLogin.d" -o "softwareConLogin.o" softwareConLogin.c
 * 		gcc -o softwareConLogin softwareConLogin.o -lcrypt
 */

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <crypt.h>
 
int main(void) {
	const char * const pass = "$1$Fw1PJ/2K$KhVip8FBuJMNXpZh.XqGX.";
	char *result;
	int ok;
 
	result = crypt(getpass("Password: "), pass);
	ok = strcmp(result, pass) == 0;
 
	puts(ok ? "Correcto!" : "Password erronea");
	return ok ? 0 : 1;
}

En este programa de ejemplo he utilizado una librería llamada crypt.h (cortesía de la FSF) por lo que tendremos que especificarlo a la hora de compilarlo con el flag -lcrypt.

Fijaos bien en la siguiente línea:

const char * const pass = "$1$Fw1PJ/2K$KhVip8FBuJMNXpZh.XqGX.";

Esa es nuestra password encriptada. ¿Podríamos ponerla en claro?, si pero cualquiera que tenga acceso al código fuente o a un buen decompilador podría ver cual es nuestra password, y eso no nos interesa. Lo que he hecho ha sido meter en el código fuente la password ya encriptada mediante un método de encriptación MD5/DES que ya expliqué otro el otro día. De este modo, al ejecutar nuestro software lo primero que nos va a pedir es una Password, y si se introduce correctamente el programa continuará, de lo contrario, dará un error de autenticación y finalizará. Se puede mejorar mucho, por ejemplo, añadiendo número de intentos, un usuario además de la password (también encriptado), etc. En unos días pondré una versión mejorada con todo esto y mas.

Encriptar cadenas con MD5/DES en C

Voy a explicar un simple ejemplo de programa en C para encriptar cadenas que se podrán usar a posteriori para securizar el uso de nuestros programas una vez compilados. Es muy sencillo y es una práctica que todos deberíamos ir adquiriendo, y en los tiempos que corren mas que nunca. Para ello he creado un archivo fuente encriptaCadena.c con el siguiente código:

/* Compilacion:	gcc -std=gnu99 -Wall -c -MMD -MP -MF"encriptaCadena.d" -MT"encriptaCadena.d" -o "encriptaCadena.o" encriptaCadena.c
 * 		gcc -o encriptaCadena encriptaCadena.o -lcrypt
 */

#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <crypt.h>
 
int main(void) {
	unsigned long seed[2];
	char salt[] = "$1$3xe26505";
	const char * const seedchars = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
	char *cadena;
	int i;
 
	seed[0] = time(NULL);
	seed[1] = getpid() ^ (seed[0] >> 14 & 0x30000);
 
	for (i = 0; i < 8; i++) {
		salt[3 + i] = seedchars[(seed[i / 5] >> (i % 5) * 6) & 0x3f];
	}
 
	cadena= crypt(getpass("Cadena: "), salt);
 
	puts(cadena);
	return 0;
}

He utilizado una librería llamada crypt.h (cortesía de la FSF) por lo que tendremos que especificarlo a la hora de compilarlo con el flag -lcrypt. Una vez compilado lo podemos usar de la siguiente manera (ejemplo en un sistema GNU/Linux o Unix):

./encriptaCadena HolaQueTal
$1$Fw1PJ/2K$KhVip8FBuJMNXpZh.XqGX.

Como se puede ver, la ejecución del programa devuelve una cadena encriptada $1$Fw1PJ/2K$KhVip8FBuJMNXpZh.XqGX. que podremos utilizar para validar una password de autenticación en otro programa que pida un Login. Esto es Free Software, compártelo.

Funciones lógicas elementales

En ingeniería electrónica o en sistemas digitales de la informática las funciones lógicas se utilizan en el ámbito mas bajo, en componentes electrónicos, en un mundo binario donde hay o no hay electricidad, en realidad no son otra cosa mas que operaciones con bits (1 y 0) o subidas y bajadas de tensión (HI y LO). Estas operaciones son la suma, la multiplicación y la inversión, y con la combinación de estas tres podemos formar las siguientes instrucciones mnemotécnicas llamadas funciones:

OR

La función OR representa la suma y tiene la siguiente ecuación lógica:

Q = A + B

Su simbolo es este:

Símbolo de la función lógica OR
Código 7432

Y su tabla de verdad es:

A B Q
0 0 0
0 1 1
1 0 1
1 1 1

Esta puerta lógica tiene salida alta (HI) siempre que al menos una de las entradas sea alta.

AND

La función AND representa la multiplicación y tiene la siguiente ecuación lógica:

Q = A \cdot B

Su simbolo es este:

Símbolo de la función lógica AND
Código 7408

Y su tabla de verdad es:

A B Q
0 0 0
0 1 0
1 0 o
1 1 1

Esta puerta lógica tiene salida alta (HI) si todas las entradas son altas.

NOT o INVER

La función NOT o INVER representa el valor inverso de entrada, es decir, si la entrada es HI (1), la salida es LO (0) y viceversa. Tiene la siguiente ecuación lógica:

A \rightarrow \overline{A}

Su simbolo es este:

Símbolo de la función lógica NOT
Código 7404

En realidad el símbolo que he puesto arriba es el que se utiliza como buffer en un cable conductor, pero el símbolo NOT o INVER que se utiliza convencionalmente en otras funciones lógicas es el circulito pequeño, lo veremos en las siguientes funciones.

Su tabla de verdad es:

A  \overline{A}
0 1
1 0

Cambia el estado de una entrada HI a LO o el estado de una entrada LO a HI.

NOR

La función NOR representa la inversión de la suma o el producto de las variables invertidas (Ley de Morgan) y tiene las siguientes ecuaciones lógicas:

Q = \overline{A + B}

Q = \overline{A} \cdot \overline{B}

Su simbolo es este:

Símbolo de la función lógica NOR
Código 7402

Y su tabla de verdad es:

A B Q
0 0 1
0 1 0
1 0 o
1 1 0

Esta puerta lógica tiene salida alta (HI) si no hay ninguna entrada baja (LO).

NAND

La función NAND representa la inversión del producto o la suma de las variables invertidas (Ley de Morgan) y tiene las siguientes ecuaciones lógicas:

Q = \overline{A \cdot B}

Q = \overline{A} \displaystyle{ + } \overline{B}

Su simbolo es este:

Símbolo de la función lógica NAND
Código 7400

Y su tabla de verdad es:

A B Q
0 0 1
0 1 1
1 0 1
1 1 0

Esta puerta lógica tiene salida baja (LO) si todas las entradas son altas (HI).

XOR

La función XOR representa una suma exclusiva o la suma del producto de las variables de entrada donde sólo una está invertida. Tiene las siguientes ecuaciones lógicas:

Q = A \oplus B

Q = A \cdot \overline{B} \displaystyle{ + } \overline{A} \cdot B

Su simbolo es este:

Símbolo de la función lógica XOR
Código 7486

Y su tabla de verdad es:

A B Q
0 0 0
0 1 1
1 0 1
1 1 0

Esta puerta lógica tiene salida baja (LO) si todas las entradas son iguales.

XNOR

La función XNOR representa la inversión de una suma exclusiva o la suma del producto de las variables de entrada cuando estas son iguales (Delta de Kronecker). Tiene las siguientes ecuaciones lógicas:

Q = \overline{A \oplus B}

Q =\overline{A} \cdot \overline{B} + A \cdot B

Su simbolo es este:

Símbolo de la función lógica XNOR
Código XOR + NOT

Y su tabla de verdad es:

A B Q
0 0 1
0 1 0
1 0 0
1 1 1

Esta puerta lógica tiene salida alta (HI) si todas las entradas son iguales.

Curioso número 9

Esto es un poco freak, aun así... con la siguiente lógica de "la raíz digital de un número de partida" se puede demostrar que con cualquier número entero positivo mayor que 9 (\{x \in \mathbb{Z} \ | \ x>9\}) se puede obtener el número 9, probadlo:

#!/usr/bin/bash
 
clear
 
printf "\nIntroduce número cualquiera mayor que 9, sin espacios: "
read f
 
r=`for ((i=0;i<${#f};i++));do echo "$RANDOM ${f:$i:1}";done | sort -u | awk '{print $2}' | egrep -v '0' | awk '{printf "%s",$0}';printf "\n"`
 
if (( "$f" > "$r" ))
then
	let q=$f-$r
else
	let q=$r-$f
fi
 
n=0
for ((i=0;i<${#q};i++))
do
	((n=$n+${q:$i:1}))
done
 
x=0
for ((z=0;z<${#n};z++))
do
	((x=$x+${n:$z:1}))
done
 
printf "%-19.19s%s\n" "NUMERO:" "$f"
printf "%-19.19s%s\n" "ORDENACION RANDOM:" "$r"
printf "%-19.19s%s\n" "RESTO:" "$q"
printf "%-19.19s%s\n" "SUMA CIFRAS RESTO:" "$n"
printf "%-19.19s%s\n" "SUMA:" "$x"
  1. Introducimos un número cualquiera, sin espacios.
  2. Generamos un segundo número con una alteración aleatoria del orden de las cifras del primer número.
  3. Obtenemos el resto entre el mayor y el menor de los dos primeros números.
  4. Sumamos las cifras de ese resto y dan como resultado un tercer número.
  5. Finalmente sumamos las cifras de este tercer número y... siempre se obtiene el número 9.

Ejemplo:

#Introduce número cualquiera mayor que 9, sin espacios: 239487978234

NUMERO:            239487978234
ORDENACION RANDOM: 822798334497
RESTO:             583310356263
SUMA CIFRAS RESTO: 45
SUMA:              9
  • \{ x \in \mathbb{Z} \ | \ x<9\}
  • x=239487978234 \Rightarrow x^\prime=822798334497
  • x^\prime - x=583310356263
  • 5+8+3+3+1+0+3+5+6+2+6+3=45
  • 4+5=9

Calendario anual en C

A continuación el código C que he creado para imprimir un calendario con formato y bastante presentable. Es bastante sencillo, la clave está en utilizar la congruencia de Zeller (algoritmo para calcular cualquier día de la semana de cualquier año) y una función simple para obtener los días que tiene un mes, incluyendo los años bisiestos. El resto de funciones son para darle un formato mas presentable:

/*
 * squareCalendar v0.01
 * Copyleft - 2011  Javier Dominguez Gomez
 * Written by Javier Dominguez Gomez <jdg@member.fsf.org>
 * GnuPG Key: 6ECD1616
 * Madrid, Spain
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Compilation:   gcc -Wall -c -MMD -MP -MF"squareCalendar.d" -MT"squareCalendar.d" -o "squareCalendar.o" squareCalendar.c
 *                gcc -o squareCalendar squareCalendar.o
 *
 * Usage:         ./squareCalendar 2011
 *
 */

#include <stdio.h>
#include <stdlib.h>

void puntos(int p,int max){
	if(p == max){
		fprintf(stdout,"|  . ");
	} else {
		fprintf(stdout," . ");
	}
}

int zeller(int ano, int mes){
	int a = (14-mes)/12;
	int y = ano-a;
	int m = mes+12*a-2;
	int dia = 1, d;
	d = (dia+y+y/4-y/100+y/400+(31*m)/12)%7;
	return d;
}

int calcularDiasMes(int ano, int mes){
	int dias;
	if(mes == 1 || mes == 3 || mes == 5 || mes == 7 || mes == 8 || mes == 10 || mes == 12){
		dias=31;
	} else if(mes == 2){
		if((ano % 4 == 0) && (ano % 100 != 0 || ano % 400 == 0)){
			dias=29;
		} else {
			dias=28;
		}
	} else {
		dias=30;
	}
	return dias;
}

void cabecera(int ano,int min,int max){
	int mes;
	char *nombreMes, *linea = "======================", *diasSemana = "LU MA MI JU VI | SA DO";

	fprintf(stdout,"\n");
	for(mes = min; mes <= max; mes++){
		switch(mes){
			case 1: nombreMes = "Enero";break;
			case 2: nombreMes = "Febrero";break;
			case 3: nombreMes = "Marzo";break;
			case 4: nombreMes = "Abril";break;
			case 5: nombreMes = "Mayo";break;
			case 6: nombreMes = "Junio";break;
			case 7: nombreMes = "Julio";break;
			case 8: nombreMes = "Agosto";break;
			case 9: nombreMes = "Septiembre";break;
			case 10: nombreMes = "Octubre";break;
			case 11: nombreMes = "Nomviembre";break;
			case 12: nombreMes = "Diciembre";break;
		}
		fprintf(stdout,"%-11s%11d   ",nombreMes,ano);

		if(mes % 3 == 0){
			fprintf(stdout,"\n");
		}
	}
	fprintf(stdout,"%s   %s   %s\n",linea,linea,linea);
	fprintf(stdout,"%s   %s   %s\n",diasSemana,diasSemana,diasSemana);
	fprintf(stdout,"%s   %s   %s\n",linea,linea,linea);
}

void semana(int numSemana,int min,int max,int ano,int mesMin,int mesMax){
	int p, z, mes, dia;

	for(mes = mesMin; mes <= mesMax; mes++){
		if(zeller(ano,mes) == 0){
			z = 6;
		} else {
			z = zeller(ano,mes)-1;
		}

		if(numSemana == 1){
			for(int p = 0;p<z;p++){
				puntos(p,5);
			}
		}

		int diasMes = calcularDiasMes(ano,mes);

		for(dia = 1;dia <=diasMes;dia++){
			z++;
			if(z <= max && z >= min){
				if(z == 6 || z == 13 || z == 20 || z == 27 || z == 34){
					fprintf(stdout,"| %2d ",dia);
				} else if(z == 36 && (mes == 2 || mes == 5 || mes == 8 || mes == 11)){
					fprintf(stdout,"%-23s%2d "," ",dia);
				} else if(z == 36 && (mes == 3 || mes == 6 || mes == 9 || mes == 12)){
					fprintf(stdout,"%-46s%2d "," ",dia);
				} else {
					fprintf(stdout,"%2d ",dia);
				}
			}
		}

		if((z == 29 || z == 30 || z == 31 || z == 32 || z == 33 || z == 34) && numSemana == 5){
			for(p=z;p<=34;p++){
				puntos(p,33);
			}
		} else if((z == 36 || z == 37) && numSemana == 6){
			for(p=z;p<=41;p++){
				puntos(p,40);
			}
		}

		if(mes % 3 == 0){
			fprintf(stdout,"\n");
		} else {
			fprintf(stdout,"  ");
		}
	}
}

void trimestre(int ano,int mesMin,int mesMax){
	cabecera(ano,mesMin,mesMax);
	semana(1,1,7,ano,mesMin,mesMax);
	semana(2,8,14,ano,mesMin,mesMax);
	semana(3,15,21,ano,mesMin,mesMax);
	semana(4,22,28,ano,mesMin,mesMax);
	semana(5,29,35,ano,mesMin,mesMax);
	semana(6,36,42,ano,mesMin,mesMax);
}

int main(int argc, char **argv) {
	char *p;
	long ano = strtol(argv[1], &p, 10);

	system("clear");

	if(ano >= 1){
		trimestre(ano,1,3);
		trimestre(ano,4,6);
		trimestre(ano,7,9);
		trimestre(ano,10,12);
	} else {
		fprintf(stderr,"Error: El primer argumento ha de ser mayor que 0.\n");
	}
}

La congruencia de Zeller tiene la siguiente notación matemática:

\left(q + \left\lfloor\frac{(m+1)26}{10}\right\rfloor + K + \left\lfloor\frac{K}{4}\right\rfloor + \left\lfloor\frac{J}{4}\right\rfloor - 2J\right) \mod 7

Una vez compilado el código solo queda ejecutarlo pasándole como argumento el año del que se quiere obtener el calendario, por ejemplo:

./squareCalendar 2011

Y el resultado es el siguiente:

calendario_C
Ejemplo de salida