And to our own way...

¡Nos actualizamos!!!!

Despues de un tiempo de ausencia, ¡¡¡regresamos!!!

Ahora hay mas descargas

No te olvides de echar un vistazo a la sección de descargas, ahora más software está disponible.

El reto de Xwork #2

El reto aun sigue, ¿Puedes resolverlo?

Dec To Any

Utilidad para convertir un número en base decimal a cualquiera.

Necesitas ocultar algo?

Encriptalo con la cálculadora de Vigenere.

domingo, 21 de enero de 2018

El video correcto #3 -FLAC Vs MP3

Hola de nuevo a todos, el día de hoy vamos a ver cual es la diferencia entre un archivo MP3 y un archivo FLAC


Hace poco, mi computadora murió y junto con ella, todos los archivos que se encontraban en el disco duro, una perdida considerable, pero no tan grave, puesto que tengo un respaldo de las cosas importantes en un disco duro externo. Cómo no tenia el presupuesto necesario para poder comprar un disco duro de 8TB, opté por comprar lo primero que encontrará, lo que conseguí, fue un disco duro de 80GB. Podría parecer bastante, pero suponiendo que todos los IDE's de desarrollo que uso, ocupan en conjunto 20 GB + 10GB del sistema operativo + los archivos que requiero utilizar, el espacio restante, fue realmente pequeño.
Actualmente no poseo mucha música, pero la poca que tengo, al estar en formato FLAC, ocupa un espacio considerable (en comparación al tradicional y compacto MP3).

Antes de seguir, el formato FLAC es el siguiente:
FLAC (son las siglas de Free Lossless Audio Codec) es un códec de audio que permite que el audio digital sea comprimido sin pérdidas de tal manera que el tamaño del archivo de audio se reduce sin que se pierda ningún tipo de información. El audio digital comprimido por el algoritmo de FLAC típicamente se puede reducir de 50 a 60% de su tamaño original,3​ y se descomprime en una copia idéntica de los datos de audio originales.
Por la calidad superior que estos poseen, es que los prefiero, pero ante la limitada capacidad del disco duro, decidí hacer una versión en MP3 de los archivos, para ver un ejemplo de cuantas es la diferencia entre uno y otro, veamos la información de la siguiente canción:

Archivo MP3 de la mejor calidad, 8 MB

Archivo FLAC con calidad buena (no superior) 26MB
Muchos no notan la diferencia entre un MP3 de máxima calidad y un FLAC promedio, pero por desgracia, yo si la notaba (antes porque ahora mi oído se ha gastado) y por eso trataba de "conseguir" la música en la mejor calidad posible.
En archivo MP3 se mutilan algunas frecuencias, que por lo general el oído no logra captar, pero en el archivo FLAC, todas las frecuencias se mantienen, y se comprimen, logrando que el sonido sea mucho más fiel al original.
Al deteriorarse mi oído, debo admitir que realmente, en el 60% de los casos ya no escucho ninguna diferencia entre un MP3 de alta calidad y un FLAC por lo que decidí crear una versión liviana. Lo importante de esto, es que podemos hacer una versión MP3 de un archivo FLAC; pero no el reves, crear un archivo FLAC de un MP3, es un desperdicio, cómo el MP3 literalmente fue "cortado", esas frecuencias faltantes ya no se pueden recuperar y estariamos haciendo un archivo pesado que realmente no valdría la pena.

Si quieres oír la diferencia entre FLAC y MP3, dejo una canción de muestra (que deberás de eliminar una vez que termines de comparar).

El archivo flac, lo puedes descargar de aqui.
El archivo mp3, lo puedes descargar de aqui.

Por ahora es todo, en el próximo post, veremos cómo crear un MP3 desde un archivo FLAC usando FFMPEG.

Los leo luego.

domingo, 14 de enero de 2018

Vamos a programar #46 - Código Bacon (ver Java android)

Hola de nuevo a todos, el día de hoy vamos a ver un poco mas sobre el código Bacon y cómo implementar una aplicación en android que haga todo el trabajo sucio por nosotros.
En post anteriores vimos cómo hacer la misma aplicación para C# (escritorio) y Javascript (Web), en esta ocasión, todo el post será solo para mostrar la app, puesto que las funciones que se emplean son exactamente las mismas que la versión de C#. El código completo es el siguiente:

package com.mdev.baconiancipher;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

public class MainActivity extends Activity {
	EditText ETInput, ETInput2, ETPreResult, ETResult;
	Button BtnPreCipher, BtnCipher;
	String[] Values = new String[]{"AAAAA", "AAAAB", "AAABA",
				"AAABB", "AABAA", "AABAB", "AABBA", "AABBB", "ABAAA", "ABAAA",
				"ABAAB", "ABABA", "ABABB", "ABBAA", "ABBAB", "ABBBA", "ABBBB",
				"BAAAA", "BAAAB", "BAABA", "BAABB", "BAABB", "BABAA", "BABAB",
				"BABBA", "BABBB"};


	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		ETInput = findViewById(R.id.ETInput);
		ETInput2 = findViewById(R.id.ETInput2);
		ETPreResult = findViewById(R.id.ETPreResult);
		ETResult = findViewById(R.id.ETResult);
		BtnPreCipher = findViewById(R.id.BtnPreCipher);
		BtnCipher = findViewById(R.id.BtnCipher);

		BtnPreCipher.setOnClickListener(new View.OnClickListener() {
				@Override
				public void onClick(View v) {
					ETPreResult.setText(CharToBaconianCode(ETInput.getText().toString()));
				}
			}
		);
		BtnCipher.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				ETResult.setText(FinalCipher(ETPreResult.getText().toString(),ETInput2.getText().toString()));
			}
		});
	}

	private String CharToBaconianCode(String Input)
	{
		Input = Input.toUpperCase();
		char[] Letras = Input.toCharArray();
		String OutValue = "";
		for (int X = 0; X < Letras.length; X++)
		{
			if ((byte)Letras[X] < 91 && (byte)Letras[X] > 64)
			{
				OutValue += Values[(byte)Letras[X] - 65];// + " ";
			}
		}
		return OutValue;
	}
	private boolean IsLower(byte Code)
	{
		if (Code > 96 && Code < 123)
			return true;
		else
			return false;
	}
	private String FinalCipher(String PreCipher, String Phrase)
	{
		if (PreCipher.length() != Phrase.length())
		{
			ShowDialog("Error","Las longitudes del pre-cifrado y la frase para insertarlo no coinciden");
			return "Error";
		}
		char[] PreCipherChars = PreCipher.toCharArray();
		char[] PhraseChars = Phrase.toCharArray();
		String FinalResult = "";
		for (int Y = 0; Y < PhraseChars.length; Y++)
		{
			if ((byte)PreCipherChars[Y] == 65)
			{
				if (IsLower((byte)PhraseChars[Y]))
					FinalResult += PhraseChars[Y];
				else
					FinalResult += (char)(PhraseChars[Y] + 32);
			}
			else
			{
				if (!IsLower((byte)PhraseChars[Y]))
					FinalResult += PhraseChars[Y];
				else
					FinalResult += (char)(PhraseChars[Y] - 32);
			}
		}
		return FinalResult;
	}
	//Mostrar dialog de manera sencilla
	private void ShowDialog(String Title, String Caption)
	{
		new AlertDialog.Builder(this)
				.setTitle(Title)
				.setMessage(Caption)
				.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
					public void onClick(DialogInterface dialog, int which) {
						// algo
					}
				})
				.setIcon(android.R.drawable.ic_dialog_alert)
				.show();
	}
}


Cómo notarás, no hay ningún cambio significativo (y no lo habrá en unos días debido a que mi PC murió y no cuento con el tiempo cómo para arreglarlo). Por ahora dejo el código fuente y el APK ya compilado para que los descargues de mi dropbox. Las actualizaciones se harán juntas para las 3 versiones en alguno de los próximos post

Por ahora es todo, los leo luego.

domingo, 7 de enero de 2018

Vamos a programar #45 - Código Bacon (ver. Javascript).

Hola de nuevo a todos, el día de hoy vamos a ver cómo crear una aplicación en javascript que sirva para aplicar el código Bacon en un texto.

En el post anterior hicimos una versión en C# para escritorio, el día de hoy vamos a realizar una versión para la Web.

El código.

El codigo en javascript que sirve para cifrar una frase usando el código Bacon es el siguiente:

<html>
	<head>
	<title>Bacon Cipher</title>
	<style type="text/css">
		.FormBacon{
			font: 95% Arial, Helvetica, sans-serif;
			max-width: 600px;
			margin: 10px auto;
			padding: 16px;
			background: #F9F9F9;
		}
		.FormBacon h1{
			font-size: 140%;
			text-align: center;
		}
		.h1c
		{
			font-size: 16px;
		}
		.textareaV
		{
			width: 100%;
			height: 150px;
			padding: 2px 5px;
			box-sizing: border-box;
			border: 2px solid #999;
			border-radius: 4px;
			resize: none;
			-webkit-transition-duration: 0.4s; /* Safari */
			transition-duration: 0.4s;
		}

		.textareaV:focus
		{
			width: 100%;
			height: 150px;
			padding: 2px 5px;
			box-sizing: border-box;
			border: 2px solid #26c6da;
			border-radius: 4px;
			resize: none;
		}

		.TextV
		{
			width: 100%;
			box-sizing: border-box;
			border: 2px solid #999;
			border-radius: 4px;
		}

		.ButtonV
		{
		width: 100%;
			background-color: #26c6da;
			border: none;
			color: white;
			padding: 15px 32px;
			text-align: center;
			text-decoration: none;
			display: inline-block;
			-webkit-transition-duration: 0.4s; /* Safari */
			transition-duration: 0.4s;
		}

		.ButtonV:hover
		{
		width: 100%;
			background-color: #30e0e0;
			border: none;
			color: white;
			padding: 15px 32px;
			text-align: center;
			text-decoration: none;
			display: inline-block;
		}
		</style>
		<script type="text/javascript">
		var Values =["AAAAA", "AAAAB", "AAABA",
			"AAABB", "AABAA", "AABAB", "AABBA", "AABBB", "ABAAA", "ABAAA",
			"ABAAB", "ABABA", "ABABB", "ABBAA", "ABBAB", "ABBBA", "ABBBB",
			"BAAAA", "BAAAB", "BAABA", "BAABB", "BAABB", "BABAA", "BABAB",
			"BABBA", "BABBB"];
		function PhraseToArray(Text){
			var Out = [];
			for(var i = 0; i < Text.length; i++){
				var CodeChar = Text.charCodeAt(i);
				Out.push(CodeChar);
			}
			return Out;
		}
		function CharToBaconianCode(){
		//debugger;
			Input = document.getElementById('InputText').value;
			Input = Input.toUpperCase();
			var Letras = PhraseToArray(Input);
			var OutValue = "";
			for (var X = 0; X < Letras.length; X++)
			{
				if (Letras[X] < 91 && Letras[X] > 64)
				{
					OutValue += Values[Letras[X] - 65];// + " ";
				}
			}
			document.getElementById('PreResult').value = OutValue;
			return OutValue;
		}
		function IsLower(Code)
		{
			if (Code > 96 && Code < 123)
				return true;
			else
				return false;
		}
		function FinalCipher()
		{
		//debugger;
			var PreCipher = document.getElementById('PreResult').value;
			var Phrase = document.getElementById('Phrase2').value;
			if (PreCipher.length != Phrase.length)
			{
				alert("Las longitudes del pre-cifrado y la frase para insertarlo no coinciden");
				return "Error";
			}
			var PreCipherChars = PhraseToArray(PreCipher);
			var PhraseChars = PhraseToArray(Phrase);
			var FinalResult = "";//string
			for (var Y = 0; Y < PhraseChars.length; Y++)
			{
				if (PreCipherChars[Y] == 65)
				{
					if (IsLower(PhraseChars[Y]))
						FinalResult += String.fromCharCode(PhraseChars[Y]);
					else
						FinalResult += String.fromCharCode(PhraseChars[Y] + 32);
				}
				else
				{
					if (!IsLower(PhraseChars[Y]))
						FinalResult += String.fromCharCode(PhraseChars[Y]);
					else
						FinalResult += String.fromCharCode(PhraseChars[Y] - 32);
				}
			}
			document.getElementById('Result').value = FinalResult;
		}
		</script>
	</head>
	<body>
		<div class="FormBacon">
			<br />
			<h1>Código Bacon</h1><br />
			<br />
			<form>
				<span class="h1c">Frase a encriptar:</span>
				<br />
				<input class="TextV" id="InputText" type="text"></input><br />
				<br />
				<input class="ButtonV" id="Encrypt" onclick="CharToBaconianCode()" type="button" value="Pre-cifrar" /><br />
				<br/>
				<span class"h1c">Resultado del Pre-cifrado</span><br/>
				<br/>
				<textarea class="textareaV" id="PreResult" rows="5"></textarea><br/>
				<br/>
				<span class="h1c">Frase para insertar</span><br /><br/>
				<input class="TextV" id="Phrase2" type="text" /><br />
				<br />
				<input class="ButtonV" id="UnEncrypt" onclick="FinalCipher()" type="button" value="Cifrar" /><br />
				<br />
				<span class="h1c">Resultado</span>
				<textarea class="textareaV" id="Result" rows="5"></textarea><br/><br/>
			</form>
		</div>
	</body>
</html>
 
La aplicación consiste de cinco partes; de las cuales cuatro son funciones y una más, es una variable que se usará para almacenar las constantes del cifrado
Primero definimos una nueva variable, será una matriz (que ya dije para que se usa). Luego creamos una funcion que reemplazará a la función de C# "string.toArray()", esta función es "PhraseToArray()"; recibe un parámetro que será el texto que queremos convertir a una matriz de números, hay que recordar que en el caso de la función en C#, cuando la llamamos, esta devuelve una matriz del tipo "char", en este caso, cómo vamos a usar el valor ASCII de esa letra, directamente guardamos en la matriz el valor resultante de llamar a la función "charCodeAt()", si hacemos memoria, está función, lo que hace es obtener el código ASCII de la letra (o símbolo) que se le pasa cómo argumento, entonces, solamente hacemos un ciclo "for" que recorra cada letra desde el inicio al final del texto que se ingreso cómo parámetro en la función "PhraseToArray()". Finalmente, está función devuelve una matriz con números que representa el código ASCII de cada letra.

La siguiente función es "CharToBaconianCode()", cómo su nombre lo dice, está función lo que hace, es convertir cada letra en su equivalente en el código Bacon. La única diferencia con la función de C#, es que esta no recibe parámetros, lo que hace para obtener el texto sobre el cual trabajara, es usar la función "document.getElementById('')" y obtener el valor de la caja de texto. El resto del proceso es similar, solamente que no hace conversiones de "char" a "byte", porque hay que recordar que los valores de la función "PhraseToArray()" ya están en un valor numérico.
La función "IsLower()" no sufre ningún cambio.
Finalmente la función  FinalCipher() al igual que la función "CharToBaconianCode()" no sufre mayor cambio con respecto a la función de C#.
Y bien, por ahora es todo, cómo te habrás dado cuenta, todo funciona perfecto cuando vamos al derecho, pero no al revés, por absurdo que parezca, olvide escribir el código para realizar el proceso contrario, pero no te preocupes, en breve agregaré las partes faltantes en esta versión y en la versión de C#, además, la siguiente semana, la versión para android vendrá con la función para hacer el proceso inverso. Cómo siempre, la descarga del código la puedes hacer desde mi dropbox.

Por ahora es todo, los leo luego.

domingo, 31 de diciembre de 2017

¡¡¡Feliz 2018!!!


Hola de nuevo a todos, este post es solo para agradecer a todos los que disfrutaron leyendo los post en el blog. Fue un 2017 muy agitado, con altos y bajos; pero finalmente hemos sobrevivido a otro año mas.
Espero que sigas siendo parte del blog de Xwork y que este 2018 sea un año aun mas productivo de lo que fue el anterior.

Nuevamente gracias; y nos leeremos luego.

Vamos a programar #44 - Código Bacon (ver. C#).

Hola de nuevo a todos, el día de hoy vamos a ver el código en C# que sirve para cifrar un mensaje usando el código Bacon.

El Código.

El código en c# que vamos a utilizar es el siguiente:

//El código Bacon sigue las siguientes reglas:
	// a- AAAAA
	// b- AAAAB
	// c- AAABA
	// d- AAABB
	// e- AABAA
	// f- AABAB
	// g- AABBA
	// h- AABBB
	// i j- ABAAA
	// k- ABAAB
	// l- ABABA
	// m- ABABB
	// n- ABBAA
	// o- ABBAB
	// p- ABBBA
	// q- ABBBB
	// r- BAAAA
	// s- BAAAB
	// t- BAABA
	// u v- BAABB
	// w- BABAA
	// x- BABAB
	// y- BABBA
	// z- BABBB
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Baconiancipher
{
	public partial class FrmMain : Form
	{
		private String[] Values = {"AAAAA", "AAAAB", "AAABA", 
		"AAABB", "AABAA", "AABAB", "AABBA", "AABBB", "ABAAA", "ABAAA",
		"ABAAB", "ABABA", "ABABB", "ABBAA", "ABBAB", "ABBBA", "ABBBB",
		"BAAAA", "BAAAB", "BAABA", "BAABB", "BAABB", "BABAA", "BABAB",
		"BABBA", "BABBB"};

		private string CharToBaconianCode(string Input)
		{
			Input = Input.ToUpper();
			char[] Letras = Input.ToCharArray();
			string OutValue = "";
			for (int X = 0; X < Letras.Length; X++)
			{
				if ((byte)Letras[X] < 91 && (byte)Letras[X] > 64)
				{
					OutValue += Values[(byte)Letras[X] - 65];// + " ";
				}
			}
			return OutValue;
		}
		private bool IsLower(byte Code)
		{
			if (Code > 96 && Code < 123)
				return true;
			else
				return false;
		}
		private string FinalCipher(string PreCipher, string Phrase)
		{
			if (PreCipher.Length != Phrase.Length)
			{
				MessageBox.Show("Las longitudes del pre-cifrado y la frase para insertarlo no coinciden", "Error",
					MessageBoxButtons.OK, MessageBoxIcon.Error);
				return "Error";
			}
			char[] PreCipherChars = PreCipher.ToCharArray();
			char[] PhraseChars = Phrase.ToCharArray();
			string FinalResult = "";
			for (int Y = 0; Y < PhraseChars.Length; Y++)
			{
				if ((byte)PreCipherChars[Y] == 65)
				{
					if (IsLower((byte)Phrase[Y]))
						FinalResult += PhraseChars[Y];
					else
						FinalResult += (char)(PhraseChars[Y] + 32);
				}
				else
				{
					if (!IsLower((byte)Phrase[Y]))
						FinalResult += PhraseChars[Y];
					else
						FinalResult += (char)(PhraseChars[Y] - 32);
				}
			}
			return FinalResult;
		}
		public FrmMain()
		{
			InitializeComponent();
		}

		private void BtnPreCipher_Click(object sender, EventArgs e)
		{
			TxtPrecipherResult.Text = CharToBaconianCode(TxtInput.Text);
			if (TxtPrecipherResult.TextLength == txtInput2.TextLength)
				BtnCipher.Enabled = true;
		}

		private void txtInput2_TextChanged(object sender, EventArgs e)
		{
			GBInput2.Text = "Frase de " + txtInput2.TextLength + " letras.";
			if (TxtPrecipherResult.TextLength == txtInput2.TextLength)
				BtnCipher.Enabled = true;
			else
				BtnCipher.Enabled = false;
		}

		private void BtnCipher_Click(object sender, EventArgs e)
		{
			TxtFinalResult.Text = FinalCipher(TxtPrecipherResult.Text, txtInput2.Text);
		}
	}
}

El código consiste de 3 funciones, pero además hay una matriz del tipo "string" en el cual se almacena cada valor del código correspondiente a cada letra. En esta matriz verás que hay valores duplicados; pero si hacemos memoria, hay que recordar que el código para los pares de letras "i - j" y "u - v" son iguales, entonces, para fines prácticos, simplemente repetimos los valores de estas.

Antes de empezar con la explicación del código, hay que plantear lo que tenemos que hacer. Para poder trabajar más cómodamente, tenemos que buscar una forma de "entendernos" con la computadora. Si hacemos un poco más de memoria, en el post donde usábamos el cifrado de Vigenere lo que hacíamos realmente, era pasar la letra a su equivalente en el código ASCII. Al revisarlo, notaremos que las letras mayúsculas estaban comprendidas entre el 65 y 90, mientras que las minúsculas, están entre el 97 y 122 (si contar las letras ñ's), así que aprovechando eso y la matriz que creamos al inicio, podemos hacer mucho más fácil el proceso.

Ahora, el código en orden de aparición. Primero tenemos la función "CharToBaconianCode". Está función recibe un parámetro del tipo "string" y lo que hace, es crear el pre-cifrado aplicando a la frase que queremos ocultar las reglas del código, ya que para esta parte no necesitamos distinguir entre mayúsculas o minúsculas, lo primero que hacemos es convertir toda las frase a mayúsculas, así nos aseguraremos que todos los números (que representan a cada letra), estarán dentro del rango 65 ~ 90. Luego convertimos la frase en un arreglo del tipo "char", usando la función  "ToCharArray()", cuando separamos todo, tenemos que la letra "A" tiene el código 65, pero en la matriz que creamos al inicio en donde almacenamos el valor del código Bacon para cada letra, esta en la primer posicion (en la posición cero en realidad), entonces para ajustar cada valor y que coincida con loas valores de la matriz "Values", solo debemos de restar 65 y así obtendremos en automático el valor correcto desde la matriz (aquí lo importante de convertir todo a mayúsculas). Está función devuelve un valor del tipo "string" que representa el mensaje ingresado cómo parámetro pero con el pre-cifrado ya aplicado.

La siguiente función es: "IsLower". Cómo su nombre lo indica, está función solo sirve para identificar si un valor está dentro del rango de las letras minúsculas. Esta función recibe un parámetro del tipo "byte" que corresponde a la letra que queramos saber si es minúscula. Esta función devuelve un valor del tipo "bool" "true" si el parámetro ingresado están dentro del rango 97 ~ 122 (indicando que es una letra minúscula).

La última función es "FinalCipher". Esta función recibe dos parámetros, el primero de tipo "string" llamado "PreCipher", este debe de ser SIEMPRE, el resultado de la función "CharToBaconianCode" (o si lo hiciste a mano, el resultado del pre-cifrado). El segundo parámetro, también es del tipo "string" y se llama "Phrase", el cual debe de ser la frase en la cual queremos insertar el mensaje pre-cifrado. Para generar el resultado final, esta función separa los dos parámetros de entrada y los convierte en una matriz del tipo "char", la frase pre-cifrada es la que nos va a dictar cómo debe de estar escrito el mensaje, pero lo que primero nos aseguramos de solo tener "A" y "B", así podemos crear un par de condiciones para cada caso (Al encontrar "A" se escribirá una minúscula y al encontrar una "B", se usara una mayúscula). Dentro de cada condición, vamos a tener otra condición más. Puede ser que el mensaje de destino (o segundo parámetro) tenga ya una letra mayúscula o minúscula, si el pre-cifrado nos dice que debe de haber una minúscula, solamente mandamos a llamar a la función "IsLower" y si el resultado es "true", dejamos las cosas cómo están, pero si es el caso contrario, convertimos el valor de la letra actual a un valor del tipo "byte" y le restaremos 32 (recuerda que la letra "A" es 65 y la letra "a" es 97, al restar 32  a la "a" tenemos "A"), para el caso contrario, solamente bastará con sumarle 32 y obtendremos el mismo resultado.

A tomar en cuenta...

A pesar de que el programa funciona, aun está en fase "beta", en el caso de la frase que se quiere cifrar, solo bastará con escribirlo y después pulsar el botón "Pre-Cifrar", dentro del programa se toman las medidas necesarias para que el resultado sea el mensaje correctamente pre-cifrado; pero en el caso de la frase en la cual queremos insertarlo, debemos de asegurarnos de no usar símbolos o espacios, ya que se pueden producir resultados cómo en la imagen siguiente:
No deberían de ir los @
Por el momento lo dejare así. El cómo solucionarlo, lo puedes averiguar si revisas los post de cifrado que ya hay en el blog.

Cómo siempre, el código completo, lo puedes descargar de mi dropbox, para que lo revises y lo modifiques. La próxima semana, publicaré el código resuelto y además veremos cómo hacer lo mismo en Java (android) o Javascript.

Por ahora es todo, los leo luego

viernes, 22 de diciembre de 2017

Learning Machine #10 - Código Bacon.

Hola de nuevo a todos, despues de un graan tiempo de ausencia, finalmente regresamos.
El día de hoy vamos a ver otro método de cifrado, veremos cómo encriptar una cadena de texto y cómo des-encriptarla.


Código Bacon.

El Código Bacon o clave Baconiana (en inglés Baconian cipher), es un método esteganográfico desarrollado por Francis Bacon. El mensaje estaría oculto en la presentación del texto, más que en su contenido.
De acuerdo a Sir Francis Bacon, existen tres propiedades que debe de tener un cifrado
1) Debe de ser fácil de escribir y de leer
2) Que debe de ser confiable y que no pueda ser descifrado
3) Si es posible libre de sospecha.
Debido a que si un mensaje llegara a caer en manos incorrectas, este no pueda ser descifrado a pesar que sea examinado por expertos.1​
Esta última condición hace que el código Bacon suponga un híbrido entre los sistemas criptográficos y los esteganográficos compartiendo características de ambos. "Wikipedia/CodigoBacon"
El código Bacon consiste prácticamente en ocultar un mensaje dentro de otro, de la manera que el primero pase de forma desapercibida dentro del segundo, este, debe de ser un mensaje legible y coherente para no llamar la atención.

¿Cómo cifrar?

Para pode cifrar un texto usando el código Bacon, primero debemos de tener en cuenta la siguiente lista:

  • a - AAAAA
  • b - AAAAB
  • c - AAABA
  • d - AAABB
  • e - AABAA
  • f - AABAB
  • g - AABBA
  • h - AABBB
  • i-j - ABAAA
  • k - ABAAB
  • l - ABABA
  • m - ABABB
  • n - ABBAA
  • o - ABBAB
  • p - ABBBA
  • q - ABBBB
  • r - BAAAA
  • s - BAAAB
  • t - BAABA
  • u-v - BAABB
  • w - BABAA
  • x - BABAB
  • y - BABBA
  • z - BABBB
Al mensaje que queramos cifrar, debemos de cambiar cada letra del mensaje original, por la secuencia indicada para esa letra. Tomemos por ejemplo las siguiente frase:

XWorks Blog.

Empezaremos por reemplazar y obtendremos lo siguiente:

BABAB BABAA ABBAB BAAAA ABAAB BAAAB AAAAB ABABA ABBAB AABBA

Luego quitaremos los espacios y tendremos los sigueinte:

BABABBABAAABBABBAAAAABAABBAAABAAAABABABAABBABAABBA

Con eso quedaria lista la primer parte del cifrado. Para poder realizar la segunda parte, debemos de seleccionar un mensaje que tenga tantos caracteres cómo nuestro mensaje pre-cifrado, en el caso anterior tenemos 50 letras, entonces debemos de seleccionar uno con esa longitud. Yo usaré este:

Todos somos idiotas hasta que se demuestre lo contrarioooo

Si cuentas todas las letras, verás 50 (tuve que repetir al final para ajustar, pero si eres creativo puedes crear una frase que pase desapercibida).
Para crear la frase final, debemos de seguir las siguientes reglas.

Emparejaremos los mensajes, de tal modo que la primer y última letra coincidan, despues, en la segunda frase (que es legible), cambiaremos las letras entre mayúsculas y minúsculas de tal forma que cuando encontremos una letra "A" en el mensaje pre-cifrado usaremos una letra minúscula en el mensaje legible; y cuando encontremos  una letra "B" en el mensaje pre-cifrado, en el mensaje legible, pondremos una letra mayúscula. Usando las reglas anteriores, obtendremos esto:

  • Mensaje precifrado
    • BABABBABAAABBABBAAAAABAABBAAABAAAABABABAABBABAABBA
  • Mensaje para ocultar el pre-cifrado
    • Todos somos idiotas hasta que se demuestre lo contrarioooo
  • Resultado
    • ToDoSSoMosiDIoTAshastAquESedeMuestReLoConTRaRioOOo
Con eso habremos ocultado nuestro mensaje original "Xworks Blog" en el mensaje Todos somos idiotas hasta que se demuestre lo contrarioooo.

¿Cómo des-cifrar?

Para descifrar un mensaje, solo debemos de hacer el proceso inverso. Tomemos cómo ejemplo el siguiente mensaje:

HelLOWorlDxwORk

Seguiremos las mismas reglas y cuando encontremos una letra mayúscula, anotaremos una letra "B", cuando sea minúscula, anotaremos una letra "A" y obtendremos lo siguiente:

BAABB BAAAB AABBA

Luego separaremos de cinco en cinco y compararemos con la tabla. En este caso obtendremos lo siguiente:

BAABB BAAAB AABBA

EL resultado es: USG , solo bastará un poco de acomodo para darle el sentido correcto, pero en este caso solo es USG la frase cifrada.

Y bien, por ahora es todo, la semana que viene veremos cómo hacer una aplicación de windows (en c#) que haga el trabajo sucio por nosotros.
Los leo luego.

domingo, 12 de noviembre de 2017

Learning Machine #9 - Conociendo a los archivos GPX.

Hola a todos, el día de hoy vamos a ver cómo es que funcionan los archivos GPX.
Hace poco tiempo, cansado de no hacer nada por la mañana, decidí que sería buena idea volver a hacer un poco de ejercicio. Correr no es una opcion por ahora, estoy tan fuera de forma que probablemente muera de un infarto al primer kilometro, pero no dejare que eso sea un impedimento, así que opte por hacer un recorrido en bicicleta.
Yo soy de esas persona a las que le gusta tener cuantificado todo, asi que si por ejemplo, decidiera correr, necesitaria algo para medir los pasos y en el caso de la bicicleta, algo para medir la distancia.
Una de las maneras más sencillas para hacerlo, es usar mi telefono, al disponer de tantos sensores, debia de haber uno que me ayudara en lo que queria hacer. El GPS que tiene integrado, ayuda a obtener las coordenadas del trayecto.
Actualmente existen muchas aplicaciones que sirven para medir la distancia, velocidad y altitud de un trayecto, la mayoria de las aplicaciones que probé, guardaban toda la informacion en un archivo de extension *.GPX.

Los archivos GPX.

GPX, o GPS eXchange Format (Formato de Intercambio GPS) es un esquema XML pensado para transferir datos GPS entre aplicaciones. Se puede usar para describir puntos (waypoints), recorridos (tracks), y rutas (routes). (Wikipedia/GPX)
En resumidas cuentas, un archivo GPX es solamente un archivo XML en el cual se guardan los datos que se obtienen del GPS (u otras fuentes), pero ¿cómo saber que etiquetas se deben de usar? Bueno, las mas importantes (y que usaremos en futuros proyectos) son las siguientes:

  • <gpx> Indica el inicio del archivo, aqui se definen los esquemas a utilizar
    • <metadata> Indica el inicio de los meta datos.
      • <createtime> Fecha de creación.
      • <starttime> Fecha y hora de inicio del registro.
      •  <endtime> Fecha y hora del final del registro.
      • <distance> Distancia (en metros).
      • <duration> Duración del registro.
      • <maxspeed> Velocidad máxima (Km/h).
      • <avgspeed> Velocidad promedio (Km/h).
      • <vehicle> Tipo de vehículo.
      • <description> Descripción.
    • <trk> Indica el inicio de una pista.
    • <trkseg> Indica el inicio de un segmento de la pista.
    • <trkpt lat="00.0000000" lon="-00.0000000"> Un punto de la pista, cómo atributos se usa "lat" para almacenar la latitud y "lon" para almacenar la longitud.
    • <ele> Indica la elevación en este punto.
    • <speed> Indica la velocidad en este punto.
    • <currentdistance> Indica la distancia total actual.
    • <timeelapsed> Indica el tiempo transcurrido en este punto.
    • <time> Fecha y hora en la que se registro este punto.
Con lo anterior, entonces podemos crear un archivo cómo el que sigue:

<?xml version="1.0" encoding="UTF-8"?>
<gpx xmlns="http://www.topografix.com/GPX/1/1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.1" creator="Speed View GPS" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">
   <metadata>
	  <createtime>2017-10-13T09:07:03Z</createtime>
	  <starttime>2017-10-13 08:32:23</starttime>
	  <endtime>2017-10-13 09:07:02</endtime>
	  <distance>7081.146</distance>
	  <duration>00:34:38</duration>
	  <maxspeed>7.91</maxspeed>
	  <avgspeed>3.406369</avgspeed>
	  <vehicle>car</vehicle>
	  <description>oct. 13, 2017 8:32:23</description>
   </metadata>
   <trk>
	  <trkseg>
		 <trkpt lat="19.2873547" lon="-98.9054489">
			<ele>2229.5</ele>
			<speed>4.17</speed>
			<currentdistance>4890.514</currentdistance>
			<timeelapsed>00:17:36</timeelapsed>
			<time>2017-10-13T08:49:59Z</time>
		 </trkpt>
		 <trkpt lat="19.2873004" lon="-98.9052929">
			<ele>2229.6</ele>
			<speed>4.68</speed>
			<currentdistance>4908.041</currentdistance>
			<timeelapsed>00:17:40</timeelapsed>
			<time>2017-10-13T08:50:03Z</time>
		 </trkpt>
		 <trkpt lat="19.2872414" lon="-98.9051528">
			<ele>2229.2</ele>
			<speed>5.6099997</speed>
			<currentdistance>4924.208</currentdistance>
			<timeelapsed>00:17:43</timeelapsed>
			<time>2017-10-13T08:50:06Z</time>
		 </trkpt>
		 <trkpt lat="19.2871614" lon="-98.9049907">
			<ele>2228.5</ele>
			<speed>6.16</speed>
			<currentdistance>4943.435</currentdistance>
			<timeelapsed>00:17:46</timeelapsed>
			<time>2017-10-13T08:50:09Z</time>
		 </trkpt>
		 <trkpt lat="19.2871065" lon="-98.9048134">
			<ele>2227.8</ele>
			<speed>6.6099997</speed>
			<currentdistance>4963.037</currentdistance>
			<timeelapsed>00:17:49</timeelapsed>
			<time>2017-10-13T08:50:12Z</time>
		 </trkpt>
		 <trkpt lat="19.2870293" lon="-98.9046317">
			<ele>2228.4</ele>
			<speed>6.8599997</speed>
			<currentdistance>4983.9917</currentdistance>
			<timeelapsed>00:17:52</timeelapsed>
			<time>2017-10-13T08:50:15Z</time>
		 </trkpt>
		 <trkpt lat="19.2869668" lon="-98.9044324">
			<ele>2229.5</ele>
			<speed>7.0699997</speed>
			<currentdistance>5006.054</currentdistance>
			<timeelapsed>00:17:55</timeelapsed>
			<time>2017-10-13T08:50:18Z</time>
		 </trkpt>
		 <trkpt lat="19.2868996" lon="-98.9042383">
			<ele>2229.0</ele>
			<speed>6.22</speed>
			<currentdistance>5027.7915</currentdistance>
			<timeelapsed>00:17:58</timeelapsed>
			<time>2017-10-13T08:50:21Z</time>
		 </trkpt>
		 <trkpt lat="19.2868314" lon="-98.9040744">
			<ele>2229.4</ele>
			<speed>5.42</speed>
			<currentdistance>5046.6025</currentdistance>
			<timeelapsed>00:18:01</timeelapsed>
			<time>2017-10-13T08:50:24Z</time>
		 </trkpt>
		 <trkpt lat="19.2867868" lon="-98.9039388">
			<ele>2229.3</ele>
			<speed>5.17</speed>
			<currentdistance>5061.6934</currentdistance>
			<timeelapsed>00:18:04</timeelapsed>
			<time>2017-10-13T08:50:27Z</time>
		 </trkpt>
		 <trkpt lat="19.2867203" lon="-98.9037992">
			<ele>2229.0</ele>
			<speed>5.42</speed>
			<currentdistance>5078.158</currentdistance>
			<timeelapsed>00:18:07</timeelapsed>
			<time>2017-10-13T08:50:30Z</time>
		 </trkpt>
		 <trkpt lat="19.2866701" lon="-98.9036398">
			<ele>2229.2</ele>
			<speed>5.75</speed>
			<currentdistance>5095.8345</currentdistance>
			<timeelapsed>00:18:10</timeelapsed>
			<time>2017-10-13T08:50:33Z</time>
		 </trkpt>
		 <trkpt lat="19.2866279" lon="-98.9035024">
			<ele>2229.4</ele>
			<speed>4.94</speed>
			<currentdistance>5111.036</currentdistance>
			<timeelapsed>00:18:13</timeelapsed>
			<time>2017-10-13T08:50:36Z</time>
		 </trkpt>
		 <trkpt lat="19.2865573" lon="-98.903338">
			<ele>2230.8</ele>
			<speed>5.35</speed>
			<currentdistance>5130.0107</currentdistance>
			<timeelapsed>00:18:17</timeelapsed>
			<time>2017-10-13T08:50:40Z</time>
		 </trkpt>
		 <trkpt lat="19.2865119" lon="-98.9031719">
			<ele>2232.1</ele>
			<speed>5.7</speed>
			<currentdistance>5148.191</currentdistance>
			<timeelapsed>00:18:20</timeelapsed>
			<time>2017-10-13T08:50:43Z</time>
		 </trkpt>
		 <trkpt lat="19.2864684" lon="-98.9030063">
			<ele>2231.6</ele>
			<speed>5.2999997</speed>
			<currentdistance>5166.3203</currentdistance>
			<timeelapsed>00:18:23</timeelapsed>
			<time>2017-10-13T08:50:46Z</time>
		 </trkpt>
		 <trkpt lat="19.2864122" lon="-98.9028658">
			<ele>2231.3</ele>
			<speed>4.96</speed>
			<currentdistance>5182.3594</currentdistance>
			<timeelapsed>00:18:26</timeelapsed>
			<time>2017-10-13T08:50:49Z</time>
		 </trkpt>
		 <trkpt lat="19.2863534" lon="-98.9026856">
			<ele>2231.1</ele>
			<speed>5.0899997</speed>
			<currentdistance>5202.404</currentdistance>
			<timeelapsed>00:18:30</timeelapsed>
			<time>2017-10-13T08:50:53Z</time>
		 </trkpt>
		 <trkpt lat="19.286309" lon="-98.9025323">
			<ele>2230.1</ele>
			<speed>5.73</speed>
			<currentdistance>5219.2666</currentdistance>
			<timeelapsed>00:18:33</timeelapsed>
			<time>2017-10-13T08:50:56Z</time>
		 </trkpt>
		 <trkpt lat="19.2862376" lon="-98.9023771">
			<ele>2230.8</ele>
			<speed>5.62</speed>
			<currentdistance>5237.3994</currentdistance>
			<timeelapsed>00:18:36</timeelapsed>
			<time>2017-10-13T08:50:59Z</time>
		 </trkpt>
		 <trkpt lat="19.286167" lon="-98.902229">
			<ele>2230.1</ele>
			<speed>5.7999997</speed>
			<currentdistance>5254.8184</currentdistance>
			<timeelapsed>00:18:39</timeelapsed>
			<time>2017-10-13T08:51:02Z</time>
		 </trkpt>
		 <trkpt lat="19.2861174" lon="-98.9020688">
			<ele>2229.7</ele>
			<speed>5.19</speed>
			<currentdistance>5272.635</currentdistance>
			<timeelapsed>00:18:42</timeelapsed>
			<time>2017-10-13T08:51:05Z</time>
		 </trkpt>
		 <trkpt lat="19.2860754" lon="-98.9019254">
			<ele>2229.5</ele>
			<speed>5.14</speed>
			<currentdistance>5288.4106</currentdistance>
			<timeelapsed>00:18:45</timeelapsed>
			<time>2017-10-13T08:51:08Z</time>
		 </trkpt>
		 <trkpt lat="19.2860339" lon="-98.9017654">
			<ele>2228.9</ele>
			<speed>4.89</speed>
			<currentdistance>5306.137</currentdistance>
			<timeelapsed>00:18:48</timeelapsed>
			<time>2017-10-13T08:51:11Z</time>
		 </trkpt>
		 <trkpt lat="19.2859513" lon="-98.9016034">
			<ele>2227.7</ele>
			<speed>4.52</speed>
			<currentdistance>5325.536</currentdistance>
			<timeelapsed>00:18:52</timeelapsed>
			<time>2017-10-13T08:51:15Z</time>
		 </trkpt>
		 <trkpt lat="19.285881" lon="-98.901449">
			<ele>2227.4</ele>
			<speed>4.81</speed>
			<currentdistance>5343.5986</currentdistance>
			<timeelapsed>00:18:56</timeelapsed>
			<time>2017-10-13T08:51:19Z</time>
		 </trkpt>
		 <trkpt lat="19.2858317" lon="-98.9013101">
			<ele>2228.0</ele>
			<speed>4.54</speed>
			<currentdistance>5359.328</currentdistance>
			<timeelapsed>00:18:59</timeelapsed>
			<time>2017-10-13T08:51:22Z</time>
		 </trkpt>
		 <trkpt lat="19.2857715" lon="-98.9011595">
			<ele>2228.3</ele>
			<speed>4.48</speed>
			<currentdistance>5376.5923</currentdistance>
			<timeelapsed>00:19:03</timeelapsed>
			<time>2017-10-13T08:51:26Z</time>
		 </trkpt>
		 <trkpt lat="19.2857089" lon="-98.9010114">
			<ele>2228.3</ele>
			<speed>4.35</speed>
			<currentdistance>5393.6396</currentdistance>
			<timeelapsed>00:19:07</timeelapsed>
			<time>2017-10-13T08:51:30Z</time>
		 </trkpt>
		 <trkpt lat="19.2856641" lon="-98.9008596">
			<ele>2228.6</ele>
			<speed>3.6799998</speed>
			<currentdistance>5410.394</currentdistance>
			<timeelapsed>00:19:11</timeelapsed>
			<time>2017-10-13T08:51:34Z</time>
		 </trkpt>
		 <trkpt lat="19.2855949" lon="-98.9006918">
			<ele>2228.4</ele>
			<speed>4.63</speed>
			<currentdistance>5429.6284</currentdistance>
			<timeelapsed>00:19:15</timeelapsed>
			<time>2017-10-13T08:51:38Z</time>
		 </trkpt>
		 <trkpt lat="19.2855428" lon="-98.9005373">
			<ele>2228.0</ele>
			<speed>4.48</speed>
			<currentdistance>5446.902</currentdistance>
			<timeelapsed>00:19:19</timeelapsed>
			<time>2017-10-13T08:51:42Z</time>
		 </trkpt>
		 <trkpt lat="19.2855065" lon="-98.9003938">
			<ele>2227.8</ele>
			<speed>5.7</speed>
			<currentdistance>5462.5405</currentdistance>
			<timeelapsed>00:19:22</timeelapsed>
			<time>2017-10-13T08:51:45Z</time>
		 </trkpt>
		 <trkpt lat="19.2854442" lon="-98.9002132">
			<ele>2227.5</ele>
			<speed>6.69</speed>
			<currentdistance>5482.7666</currentdistance>
			<timeelapsed>00:19:25</timeelapsed>
			<time>2017-10-13T08:51:48Z</time>
		 </trkpt>
		 <trkpt lat="19.2853641" lon="-98.9000407">
			<ele>2227.6</ele>
			<speed>6.8399997</speed>
			<currentdistance>5502.969</currentdistance>
			<timeelapsed>00:19:28</timeelapsed>
			<time>2017-10-13T08:51:51Z</time>
		 </trkpt>
		 <trkpt lat="19.2853049" lon="-98.8998464">
			<ele>2228.1</ele>
			<speed>6.7999997</speed>
			<currentdistance>5524.433</currentdistance>
			<timeelapsed>00:19:31</timeelapsed>
			<time>2017-10-13T08:51:54Z</time>
		 </trkpt>
		 <trkpt lat="19.2852443" lon="-98.8996762">
			<ele>2228.1</ele>
			<speed>6.25</speed>
			<currentdistance>5543.546</currentdistance>
			<timeelapsed>00:19:34</timeelapsed>
			<time>2017-10-13T08:51:57Z</time>
		 </trkpt>
		 <trkpt lat="19.2851887" lon="-98.8994991">
			<ele>2229.2</ele>
			<speed>6.6</speed>
			<currentdistance>5563.195</currentdistance>
			<timeelapsed>00:19:37</timeelapsed>
			<time>2017-10-13T08:52:00Z</time>
		 </trkpt>
		 <trkpt lat="19.2851245" lon="-98.8993265">
			<ele>2229.4</ele>
			<speed>6.25</speed>
			<currentdistance>5582.688</currentdistance>
			<timeelapsed>00:19:40</timeelapsed>
			<time>2017-10-13T08:52:03Z</time>
		 </trkpt>
		 <trkpt lat="19.2850556" lon="-98.8991673">
			<ele>2230.2</ele>
			<speed>6.44</speed>
			<currentdistance>5601.079</currentdistance>
			<timeelapsed>00:19:43</timeelapsed>
			<time>2017-10-13T08:52:06Z</time>
		 </trkpt>
		 <trkpt lat="19.2850039" lon="-98.8989882">
			<ele>2231.2</ele>
			<speed>6.43</speed>
			<currentdistance>5620.7734</currentdistance>
			<timeelapsed>00:19:46</timeelapsed>
			<time>2017-10-13T08:52:09Z</time>
		 </trkpt>
		 <trkpt lat="19.284942" lon="-98.8988195">
			<ele>2230.0</ele>
			<speed>6.42</speed>
			<currentdistance>5639.808</currentdistance>
			<timeelapsed>00:19:49</timeelapsed>
			<time>2017-10-13T08:52:12Z</time>
		 </trkpt>
		 <trkpt lat="19.2848768" lon="-98.8986502">
			<ele>2229.3</ele>
			<speed>6.33</speed>
			<currentdistance>5659.0356</currentdistance>
			<timeelapsed>00:19:52</timeelapsed>
			<time>2017-10-13T08:52:15Z</time>
		 </trkpt>
		 <trkpt lat="19.2848074" lon="-98.8984819">
			<ele>2228.3</ele>
			<speed>6.2999997</speed>
			<currentdistance>5678.3735</currentdistance>
			<timeelapsed>00:19:55</timeelapsed>
			<time>2017-10-13T08:52:18Z</time>
		 </trkpt>
		 <trkpt lat="19.2847355" lon="-98.8983052">
			<ele>2227.7</ele>
			<speed>6.24</speed>
			<currentdistance>5698.695</currentdistance>
			<timeelapsed>00:19:58</timeelapsed>
			<time>2017-10-13T08:52:21Z</time>
		 </trkpt>
		 <trkpt lat="19.2846779" lon="-98.8981337">
			<ele>2227.4</ele>
			<speed>6.24</speed>
			<currentdistance>5717.8203</currentdistance>
			<timeelapsed>00:20:01</timeelapsed>
			<time>2017-10-13T08:52:24Z</time>
		 </trkpt>
		 <trkpt lat="19.2846228" lon="-98.8979652">
			<ele>2227.3</ele>
			<speed>6.0899997</speed>
			<currentdistance>5736.634</currentdistance>
			<timeelapsed>00:20:04</timeelapsed>
			<time>2017-10-13T08:52:27Z</time>
		 </trkpt>
		 <trkpt lat="19.2845703" lon="-98.8978222">
			<ele>2228.1</ele>
			<speed>5.62</speed>
			<currentdistance>5752.7866</currentdistance>
			<timeelapsed>00:20:07</timeelapsed>
			<time>2017-10-13T08:52:30Z</time>
		 </trkpt>
		 <trkpt lat="19.2845295" lon="-98.8976665">
			<ele>2228.6</ele>
			<speed>5.0299997</speed>
			<currentdistance>5769.9194</currentdistance>
			<timeelapsed>00:20:10</timeelapsed>
			<time>2017-10-13T08:52:33Z</time>
		 </trkpt>
		 <trkpt lat="19.2844665" lon="-98.8974973">
			<ele>2228.4</ele>
			<speed>5.3199997</speed>
			<currentdistance>5789.093</currentdistance>
			<timeelapsed>00:20:14</timeelapsed>
			<time>2017-10-13T08:52:37Z</time>
		 </trkpt>
		 <trkpt lat="19.2844181" lon="-98.8973579">
			<ele>2228.4</ele>
			<speed>5.3199997</speed>
			<currentdistance>5804.718</currentdistance>
			<timeelapsed>00:20:17</timeelapsed>
			<time>2017-10-13T08:52:40Z</time>
		 </trkpt>
		 <trkpt lat="19.284345" lon="-98.8971953">
			<ele>2228.7</ele>
			<speed>5.46</speed>
			<currentdistance>5823.6953</currentdistance>
			<timeelapsed>00:20:20</timeelapsed>
			<time>2017-10-13T08:52:43Z</time>
		 </trkpt>
		 <trkpt lat="19.2843003" lon="-98.8970488">
			<ele>2228.7</ele>
			<speed>5.6099997</speed>
			<currentdistance>5839.8765</currentdistance>
			<timeelapsed>00:20:23</timeelapsed>
			<time>2017-10-13T08:52:46Z</time>
		 </trkpt>
		 <trkpt lat="19.2842265" lon="-98.8968865">
			<ele>2230.9</ele>
			<speed>5.7599998</speed>
			<currentdistance>5858.798</currentdistance>
			<timeelapsed>00:20:26</timeelapsed>
			<time>2017-10-13T08:52:49Z</time>
		 </trkpt>
		 <trkpt lat="19.2841685" lon="-98.8967161">
			<ele>2229.8</ele>
			<speed>5.7599998</speed>
			<currentdistance>5877.8633</currentdistance>
			<timeelapsed>00:20:29</timeelapsed>
			<time>2017-10-13T08:52:52Z</time>
		 </trkpt>
		 <trkpt lat="19.2840928" lon="-98.8965554">
			<ele>2230.3</ele>
			<speed>5.52</speed>
			<currentdistance>5896.795</currentdistance>
			<timeelapsed>00:20:32</timeelapsed>
			<time>2017-10-13T08:52:55Z</time>
		 </trkpt>
		 <trkpt lat="19.2840414" lon="-98.8964041">
			<ele>2230.1</ele>
			<speed>5.83</speed>
			<currentdistance>5913.686</currentdistance>
			<timeelapsed>00:20:35</timeelapsed>
			<time>2017-10-13T08:52:58Z</time>
		 </trkpt>
		 <trkpt lat="19.2839835" lon="-98.8962396">
			<ele>2230.4</ele>
			<speed>6.31</speed>
			<currentdistance>5932.1357</currentdistance>
			<timeelapsed>00:20:38</timeelapsed>
			<time>2017-10-13T08:53:01Z</time>
		 </trkpt>
		 <trkpt lat="19.2839417" lon="-98.8960511">
			<ele>2230.1</ele>
			<speed>5.81</speed>
			<currentdistance>5952.5576</currentdistance>
			<timeelapsed>00:20:41</timeelapsed>
			<time>2017-10-13T08:53:04Z</time>
		 </trkpt>
		 <trkpt lat="19.2838761" lon="-98.8958997">
			<ele>2230.2</ele>
			<speed>5.79</speed>
			<currentdistance>5970.072</currentdistance>
			<timeelapsed>00:20:44</timeelapsed>
			<time>2017-10-13T08:53:07Z</time>
		 </trkpt>
		 <trkpt lat="19.283805" lon="-98.8957721">
			<ele>2229.7</ele>
			<speed>5.31</speed>
			<currentdistance>5985.7344</currentdistance>
			<timeelapsed>00:20:47</timeelapsed>
			<time>2017-10-13T08:53:10Z</time>
		 </trkpt>
		 <trkpt lat="19.2837329" lon="-98.8956155">
			<ele>2228.8</ele>
			<speed>4.69</speed>
			<currentdistance>6004.08</currentdistance>
			<timeelapsed>00:20:51</timeelapsed>
			<time>2017-10-13T08:53:14Z</time>
		 </trkpt>
		 <trkpt lat="19.2836277" lon="-98.8955173">
			<ele>2229.0</ele>
			<speed>5.2599998</speed>
			<currentdistance>6019.7734</currentdistance>
			<timeelapsed>00:20:54</timeelapsed>
			<time>2017-10-13T08:53:17Z</time>
		 </trkpt>
		 <trkpt lat="19.2835321" lon="-98.8953833">
			<ele>2229.3</ele>
			<speed>5.47</speed>
			<currentdistance>6037.611</currentdistance>
			<timeelapsed>00:20:57</timeelapsed>
			<time>2017-10-13T08:53:20Z</time>
		 </trkpt>
		 <trkpt lat="19.2834933" lon="-98.8952368">
			<ele>2229.5</ele>
			<speed>5.35</speed>
			<currentdistance>6053.6636</currentdistance>
			<timeelapsed>00:21:00</timeelapsed>
			<time>2017-10-13T08:53:23Z</time>
		 </trkpt>
		 <trkpt lat="19.2834481" lon="-98.8950919">
			<ele>2228.7</ele>
			<speed>5.22</speed>
			<currentdistance>6069.696</currentdistance>
			<timeelapsed>00:21:03</timeelapsed>
			<time>2017-10-13T08:53:26Z</time>
		 </trkpt>
		 <trkpt lat="19.283385" lon="-98.8949165">
			<ele>2228.7</ele>
			<speed>4.7799997</speed>
			<currentdistance>6089.425</currentdistance>
			<timeelapsed>00:21:07</timeelapsed>
			<time>2017-10-13T08:53:30Z</time>
		 </trkpt>
		 <trkpt lat="19.2833551" lon="-98.8947334">
			<ele>2229.2</ele>
			<speed>4.8399997</speed>
			<currentdistance>6108.9653</currentdistance>
			<timeelapsed>00:21:11</timeelapsed>
			<time>2017-10-13T08:53:34Z</time>
		 </trkpt>
	  </trkseg>
   </trk>
</gpx>

Que tendrá cómo resultado algo cómo lo de la imagen siguiente:
Puedes ver el archivo GPX en http://maplorer.com/
No todos las etiquetas son necesarias para que el archivo funcione, pero por default la aplicación que use crea todas las etiquetas anteriores. Cómo eso se me hace un desperdicio, en el siguiente post, vamos ver cómo hacer uso del GPS en un dispositivo android (con GPS, obviamente) y crear nuestros propios archivos GPX para poder llevar un registro de los recorridos en bicicleta que haga.

Por ahora es todo, los leo luego.

domingo, 5 de noviembre de 2017

Vamos a programar #43 - Actualizando clock view.

Hola de nuevo a todos, el día de hoy vamos a ver una actualizacion del código de clock view.
Cómo recordarás, hace un buen rato dije que iba a actualizar el código, pero por varias razones lo pospuse, hasta que finalmente casí lo olvide. No se le hicieron muchos cambios, solo optimizaron algunas partes y se saco un poco más de provecho a las partes que ya se usaban y se agregaron unas cuantas más. Sí previamente ya habías hecho el proyecto, solo bastará con cargar el código al arduino, de cualquier forma, el código estará disponible para su descarga.

El código.

El código actualizado del Clock View es el siguiente:
//Clockview 2.0
#include <DS1302.h>
#include "LedControl.h"
#include <SD.h>
/*
	Para la conexión del modulo SD se siguen los siguientes:
	** MOSI - pin 11
	** MISO - pin 12
	** CLK - pin 13
	** CS - pin 10 - Este es el que se puede cambiar
*/
//Pin SD
int CS_PIN = 10;
//Constantes para los pines usados en la matriz
const int MaxDIn = 9;
const int MaxCS = 8;
const int MaxCLK = 7;
int MaxNDevices = 3;
bool IsConnected = false;
//Constantes para los pines usados en el reloj
const int kCePin = 6;  // RST
const int kIoPin = 5;  // Dat
const int kSclkPin = 4;  // Serial Clock
//Inicializacion de la matriz
//DIN,CLK,CS
LedControl lc = LedControl(MaxDIn, MaxCLK, MaxCS, MaxNDevices);
//Inicializacion del reloj
DS1302 rtc(kCePin, kIoPin, kSclkPin);
//Algunas variables
File Archivo;
char Texto[24];
int MatrixB = 10;
bool H24 = true;
bool HalfSecond = true;
bool IsScreenEnable = true;
bool ShowSeconds = true;
bool SDCardReady = false;
bool EditMode = false;
bool ShowDate = false;
bool Demo = false;
//Matriz con los "Numeros"
const unsigned char Numbers[] = {
	B11111110, B10000010, B11111110, //0
	B10000100, B11111110, B10000000, //1
	B11110010, B10010010, B10011110, //2
	B10000010, B10010010, B11111110, //3
	B00011110, B00010000, B11111110, //4
	B10011110, B10010010, B11110010, //5
	B11111110, B10010010, B11110010, //6
	B00000010, B00000010, B11111110, //7
	B11111110, B10010010, B11111110, //8
	B00011110, B00010010, B11111110, //9
	B11111110, B00010010, B11111110, //A - 10
	B11111110, B00011100, B11111110, //M - 11
	B11111110, B00010010, B00011110, //P - 12
	B11111110, B00010000, B11111110, //H - 13
	B11111110 ,B00110010, B11011110, //R - 14
};
//Definimos simbolos
const unsigned char Symbols[] = {
	B01000100, B00101000, B00010000, B11111110, B01010100, B00101000, //BT 0
	B00111100, B01000110, B01011010, B01001010, B01001010, B00111100, //Clock Adjust 1
	B11010110, B10111010, B01000100, B01000100, B10111010, B11010110//Brightness 2
};
//Imprimir simbolos
void PrintSymbol(byte Index,byte SymDevice){
	for (int Y = 1; Y < 7; Y++){
		lc.setRow(SymDevice, Y, Symbols[Index * 5 + Y - 1]);
	}
}
//Funcion para escribir los numero en la matriz.
void PrintNumber(byte NumberOne, byte NumberTwo, byte Device){
	for (int X = 1; X < 8; X++){
		if (X < 4){
			lc.setRow(Device, X, Numbers[NumberOne * 3 + X - 1]);
		}
		if (X == 4){
			lc.setRow(Device, 4, 0);
		}
		if (X > 4){
			lc.setRow(Device, X, Numbers[NumberTwo * 3 + X - 5]);
		}
	}
}
void printDate(){
	Time t = rtc.time();
	const String day = dayAsString(t.day);
	char buf[50];
	snprintf(buf, sizeof(buf), "%s %04d-%02d-%02d %02d:%02d:%02d",
	day.c_str(), t.yr, t.mon, t.date, t.hr, t.min, t.sec);
	int ShortYear = t.yr % 100;
	PrintNumber(t.date / 10, t.date % 10, 0);
	PrintNumber(t.mon / 10, t.mon % 10, 1);
	PrintNumber(ShortYear / 10, ShortYear % 10, 2);
}
//Imprimir el tiempo en las matrices y en el monitor serie.
void printTime(){
	Time t = rtc.time();
	const String day = dayAsString(t.day);
	char buf[50];
	snprintf(buf, sizeof(buf), "%s %04d-%02d-%02d %02d:%02d:%02d",
		day.c_str(), t.yr, t.mon, t.date, t.hr, t.min, t.sec);
	PrintNumber(AdjustTime(t.hr, H24) / 10,AdjustTime(t.hr, H24) % 10, 0);
	PrintNumber(t.min / 10, t.min % 10, 1);
	if (ShowSeconds){
		PrintNumber(t.sec / 10, t.sec % 10, 2);	
	}else{
		if(H24)
			PrintNumber(13, 14, 2);
		if(!H24 && t.hr < 13)
			PrintNumber(10, 11, 2);
		if(!H24 && t.hr > 12)
			PrintNumber(12, 11, 2);
	}
	//PrintSymbol(0, 2);
	if (IsScreenEnable == true)
	{
		if (HalfSecond == true){
			digitalWrite(3, HIGH);
			HalfSecond = false;
		}else{
			digitalWrite(3, LOW);
			HalfSecond = true;
		}
	}
	Serial.println(buf);
}
//Demo directo del ejemplo de la libreria
void ScreenDemo(){
	int devices=lc.getDeviceCount();
	for(int row=0;row<8;row++) {
		for(int col=0;col<8;col++) {
			for(int address=0;address<devices;address++) {
				int DemoButton = analogRead(A5);
				if(DemoButton < 200){
					Demo = false;
					break;
				}
				delay(40);
				lc.setLed(address,row,col,true);
				delay(40);
				lc.setLed(address,row,col,false);
			}
		}
	}
}
//Ajustar la hora
int AdjustTime(int Hour, bool In24Hformat){
	if(In24Hformat == true){
		return Hour;
	}
	if(In24Hformat == false && Hour > 12){
		Hour = Hour - 12;
		return Hour;
	}else{
		return Hour;
	}
}
//Guardar los ajustes
void SaveSettings(){
	Archivo = SD.open("Settings.clv", O_WRITE | O_CREAT);
	char SaveBuf[8];
	snprintf(SaveBuf, sizeof(SaveBuf), "%s%02d",">SETB", MatrixB);
	Serial.println(SaveBuf);
	if (Archivo){
		Archivo.seek(0);
		Archivo.println(SaveBuf);
		if(H24)
			Archivo.println(">SETF");
		else
			Archivo.println("XSETF");
		if (ShowSeconds)
			Archivo.println(">DISS");
		else
			Archivo.println("XDISS");
			Archivo.flush();
			Archivo.close();
		Serial.println("Guardado");
	} else {
		Serial.println("error writing test.txt");
	}
}
//Enviar un resumen de las configuraciones actuales
void SendResume(){
	
}
//Cargar los ajustes
void LoadSettings(){
	Archivo = SD.open("Settings.clv", FILE_READ);
	int B = 0;
	byte CurRead;
	char SettBuf[22];
	if (Archivo) {
		while (Archivo.available() > 0){
			CurRead = Archivo.read();
			if	(CurRead != 10 && CurRead != 13){
				SettBuf[B] = CurRead;
				B++;
				Serial.print(B);
			}else{
				CheckPetition(SettBuf, false);
				Serial.print(B);
				B = 0;
				for (int BF = 0; BF < 22; BF++)
					SettBuf[BF]=0;
			}
		}
		Archivo.close();
	}else{
		Serial.println("error opening file");
	}	
}
//Comprobar si hay algun comando
void CheckPetition(char DATA[], bool Save){
	int i = 0;
	int j = 0;
	String Texto(DATA);
	//>SETH2016122119001004
	if (Texto.startsWith(">DISS")){
		ShowSeconds = !ShowSeconds;
		if (Save)
			SaveSettings();
		Serial.print("No/Se muestran los segundos");
		for (int CurrentDevice = 0; CurrentDevice < MaxNDevices; CurrentDevice++){
			lc.shutdown(CurrentDevice, !IsScreenEnable);
		}
		for (j = 0; j < 11; j++) {
			DATA[j] = 0;
		}
		i = 0;
	}
	if (Texto.startsWith(">SCRA")){
		IsScreenEnable = !IsScreenEnable;
		Serial.print("Las matrices se apagaron/encendieron");
		for (int CurrentDevice = 0; CurrentDevice < MaxNDevices; CurrentDevice++){
			lc.shutdown(CurrentDevice, !IsScreenEnable);
		}
		for (j = 0; j < 11; j++) {
			DATA[j] = 0;
		}
		i = 0;
	}	
	if (Texto.startsWith(">RESET")){
		SD.remove("Settings.clv");
		for (int CurrentDevice = 0; CurrentDevice < MaxNDevices; CurrentDevice++){
			lc.shutdown(CurrentDevice, !IsScreenEnable);
		}
		for (j = 0; j < 11; j++) {
			DATA[j] = 0;
		}
		i = 0;
	}	
	if (Texto.startsWith(">SETF")){
		H24 = !H24;
		if (Save)
			SaveSettings();
		Serial.print("EL reloj cambio de formato 12H-24H");
		for (j = 0; j < 11; j++) {
			DATA[j] = 0;
		}
		i = 0;
	}
	if (Texto.startsWith(">SETH")){
		AdjustTime(Texto.substring(9, 5).toInt(),Texto.substring(11,9).toInt(),Texto.substring(13, 11).toInt(),
		Texto.substring(15, 13).toInt(), Texto.substring(17, 15).toInt(), Texto.substring(19, 17).toInt(),
		Texto.substring(21, 19).toInt());
		for (j = 0; j < 11; j++) {
			DATA[j] = 0;
		}
		i = 0;
	}
	if (Texto.startsWith(">SETB")){
		MatrixB = Texto.substring(5).toInt();
		if (Save)
			SaveSettings();
		Serial.println("El brillo se cambio");
		for (int CurrentDevice = 0; CurrentDevice < MaxNDevices; CurrentDevice++){
			lc.setIntensity(CurrentDevice, MatrixB);
		}
		for (j = 0; j < 11; j++) {
			DATA[j] = 0;
		}
		i = 0;
	}
	else {
		for (j = 0; j < 11; j++) {
			DATA[j] = 0;
		}
		i = 0;
	}
}
//Convertir los dias
String dayAsString(const Time::Day day){
	switch (day){
		case Time::kSunday: return "DOM";
		case Time::kMonday: return "LUN";
		case Time::kTuesday: return "MAR";
		case Time::kWednesday: return "MIE";
		case Time::kThursday: return "JUE";
		case Time::kFriday: return "VIE";
		case Time::kSaturday: return "SAB";
	}
	return "(unknown day)";
}
//Ajustar la hora
void AdjustTime(int Year,int Month, int Day, int Hour, int Minute, int Second, int DayOfWeek){
	Time::Day CurrentDay;
	switch (DayOfWeek){
		case 1:
			CurrentDay = Time::kSunday;
			break;
		case 2:
			CurrentDay = Time::kMonday;
			break;
		case 3:
			CurrentDay = Time::kTuesday;
			break;
		case 4:
			CurrentDay = Time::kWednesday;
			break;
		case 5:
			CurrentDay = Time::kThursday;
			break;
		case 6:
			CurrentDay = Time::kFriday;
			break;
		case 7:
			CurrentDay = Time::kSaturday;
			break;
	}
	//Esta parte se usa para actualizar la hora.
	rtc.writeProtect(false);
	rtc.halt(false);
	Time t(Year, Month, Day, Hour, Minute, Second, CurrentDay);
	rtc.time(t);	
}
//Inicializar la tarjeta SD para usarla
void InitializeSD(){
	pinMode(CS_PIN, OUTPUT);
	if (SD.begin()){
		SDCardReady = true;
		Serial.println("La tarjeta SD esta lista");
	}else{
		SDCardReady = false;
		Serial.println("La tarjeta SD no esta lista");
		return;
	}
}
//Setup
void setup(){
	Serial.begin(9600);
	for (int CurrentDevice = 0; CurrentDevice < MaxNDevices; CurrentDevice++){
		lc.shutdown(CurrentDevice, false);
		lc.setIntensity(CurrentDevice, MatrixB);
		lc.clearDisplay(CurrentDevice);
	}
	InitializeSD();
	pinMode(A2, INPUT_PULLUP);
	pinMode(A3, INPUT_PULLUP);
	pinMode(A4, INPUT_PULLUP);
	pinMode(A5, INPUT_PULLUP);
	pinMode(3, OUTPUT);
	if(SDCardReady){
		LoadSettings();
	}else{
		Serial.println("La tarjeta no está lista");
	}
}
//Loop
void loop(){
	int SetEditButton = analogRead(A2);
	int ShowDateButton = analogRead(A3);
	int TurnOffMatrixButton = analogRead(A4);
	int DemoButton = analogRead(A5);
	
	while (Demo == true){
		ScreenDemo();
	}
	if(DemoButton < 200){
		Demo = !Demo;
	}
	Serial.println(SetEditButton);	
	if(SetEditButton < 200){
		EditMode = !EditMode;
	}
	if(ShowDateButton < 200){
		ShowDate = true;
	}
	if(TurnOffMatrixButton < 200){
		IsScreenEnable = !IsScreenEnable;
		for (int CurrentDevice = 0; CurrentDevice < MaxNDevices; CurrentDevice++){
			lc.shutdown(CurrentDevice, IsScreenEnable);
		}
	}	
	if(ShowDate){
		printDate();
		delay(3000);
		ShowDate = false;
	}
	if(EditMode){
		int PotB = analogRead(A0);
		// print out the value you read:
		int MatrixB = map(PotB, 0, 1000, 1, 15);
		char SaveBuf[8];
		snprintf(SaveBuf, sizeof(SaveBuf), "%s%02d",">SETB", MatrixB);
		CheckPetition(SaveBuf, false);
		PrintNumber(MatrixB / 10,MatrixB % 10, 2);
		PrintSymbol(2,1);
		delay(100);
	}else{
		int i = 0;
		printTime();
		delay(100);
		if (Serial.available()) {
			while (Serial.available() > 0) {
				Texto[i] = Serial.read();
				i++;
			}
			Texto[i] = '\0';
		}
		CheckPetition(Texto, true);	
	}
}



El código es funcional, pero demostrativo. Uno de los cambios más notorios que se puede observar, es que los número son más grandes.
Los números eran de 3x5 LEDs, en su lugar, ahora son de 7x3
Además se hace uso de pulsadores para que tambien sea manipulable, en este caso, el código hace uso de 4, pero tal vez en un futuro, se usen solo 3, he visto muchos relojes que solo hacen el uso de ese número.
El código se actualizará nuevamente, pero por ahora lo público así para que te sientas libre de probar con tus propios ajustes. Si no dispones de pulsadores, no te preocupes, aun es totalmente utilizable solo por medio de bluetooth. El código anterior lo puedes descargar de la seccion de descargas y si quieres hacer uso de este, basta con copiarlo; todas las conexiones vienen en el código.
Por ahora es todo, los leo luego.

viernes, 20 de octubre de 2017

Vamos a programar #41 - Números palindrómicos (ver. Java for android)

Hola de nuevo a todos, el día de hoy y despúes de unas largas y mal merecidas "vacaciones", vamos a continuar con más temas de programación.
La última vez que vimos un post sobre programación, vimos la conjetura de los números palíndrómicos e hicimos un programa en C# para windows. Hoy y cómo en ocasiones anteriores, vamos a ver cómo hacer una versión para android.

El código.

El código en java que hace funcionar las cosas es el siguiente:

package com.mdev.numerospalindromos;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import java.lang.String;
import java.math.BigInteger;
import android.widget.ArrayAdapter;
import java.util.ArrayList;
public class MainActivity extends Activity {
	Button BtnCalculate;
	TextView TxtNumberIn;
	TextView TxtIterations;
	ListView LVResultados;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		BtnCalculate = (Button)findViewById(R.id.BtnCalculate);
		TxtNumberIn = (TextView)findViewById(R.id.TXTNumberIn);
		TxtIterations = (TextView)findViewById(R.id.TxtIterations);
		LVResultados = (ListView)findViewById(R.id.LVResults);
		BtnCalculate.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				GetNumber(TxtNumberIn.getText().toString(), Integer.parseInt(TxtIterations.getText().toString()));
			}
		});
	}
	private boolean IsPalindrome(String Number)
	{
		String Value = ReverseNumber(Number);
		if (Number.equals(Value))
			return true;
		else
			return false;
	}
	private String ReverseNumber(String NumberIn)
	{
		char[] charArray = NumberIn.toCharArray();
		char[] OutArray = new char[charArray.length];
		for (int i = charArray.length-1; i >= 0 ; i--){
			OutArray[charArray.length - 1 - i] = charArray[i];
		}
		return new String(OutArray);
	}
	private void GetNumber(String Number1, Integer MaxIterations)
	{
		ArrayList Items = new ArrayList();
		int Iterator = 1;
		if (IsPalindrome(Number1))
		{
			Items.add("0 iteraciones " + Number1 + " cumple la conjetura");
		}
		else
		{
			BigInteger Result = new BigInteger(Number1);

			while (!IsPalindrome(Result.toString()))
			{
				BigInteger NewNumber = new BigInteger(ReverseNumber(Result.toString()));
				Result = Result.add(NewNumber);
				if (Iterator >= MaxIterations)
				{
					break;
				}
				Iterator += 1;
				Items.add(Iterator + " iteraciones " + Result);
			}

			if (IsPalindrome(Result.toString()))
				Items.add("La conjetura se cumple");
			else
				Items.add("La conjetura no se cumple.");
		}
		final ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1,Items);
		LVResultados.setAdapter(adapter);
	}
}

El código consta de 3 funciones. La primera de ellas en orden de aparición, es: IsPalindrome y aunque su nombre lo diga o lo haga parecer, esta función no es la encargada de hacer los cálculos. La función simplemente comprueba que el valor que se pasa cómo parámetro, cumpla con la condición: "es igual si se lee al derecho que al revés", usando la función "ReverseNumber". Si la condición se cumple, la función regresa un valor booleano "true" en caso contrario regresa "false".

La función "ReverseNumber", sirve para invertir el orden de los dígitos de un número, es decir; el primer número pasa a ser el último y viceversa. hay que recordar que la conjetura nos dice que se debe de obtener un palíndromo si se suma un número que sea el mismo número pero invertido. Esta función recibe un parámetro del tipo "String" que representa el número que queremos invertir. La función usa dos "Arrays" que contienen los dígitos almacenados como caracteres, por lo que "2" es algo totalmente diferente a 2, el parámetro de entrada, es el primero en separarse, con eso obtenemos el tamaño que tendrá el otro "Array" y lo asignamos al momento de crearlo. Para formar el valor de salida, simplemente recorremos en orden inverso el "Array" que contiene los digitos del valor de entrada. La función regresa un valor del tipo "String" que representa el parámetro  de entrada en orden inverso.

Finalmente el procedimiento que se encarga de comprobar la conjetura es "GetNumber". Este procedimiento hace todos los pasos.  El procedimiento recibe dos parámetros, el primero del tipo "String", es el número al cual le queremos aplica la conjetura, el segundo, es in valor del tipo "Integer" que contiene el valor máximo de la iteraciones. Antes de si quiera empezar con los cálculos, comprobamos que el valor que se ingresa  cumpla la conjetura, si lo hace, simplemente informamos que el  número es palíndromo y terminamos el procedimiento. En  caso contrario, empezamos convirtiendo el valor de "String " a "BigInteger". Luego iniciamos un bucle "while" en el cual haremos uso de la función "IsPalindrome" mientras está sea falsa  o mientras el valor de la variable "Iterator" sea menor que  el parámetro "MaxIterations". Cuando cualquiera de las condiciones es verdadera, salimos del bucle; si salimos debido a que la función "IsPalindrome" resulta verdadera,  mostramos un mensaje indicando que el número cumplió con la conjetura; pero si la salida del bucle se debe a que el número de iteraciones alcanzo el máximo indicado por el usuario, mostramos un  mensaje que la conjetura no se cumplió. En este caso, que un número no cumpla la conjetura en X numero de iteraciones, no quieres decir que por ello este exento, algunas veces bastará con aumentar el número de iteraciones.

Y bien, por ahora es todo, al igual que las otra aplicaciones, el código fuente pronto la publicaré en la sección de descargas, el APK ya compilado, lo puedes descargar de mi dropbox.
Antes de terminar, debo aclarar que la aplicación aun no es a prueba de errores, y peor aun, no es a prueba de si misma. Todo es meramente ilustrativo, porque la aplicación va a hacer lo que el usuario le diga y por el momento no es recomendable pasar de las 1000 iteraciones; aunque la mayoría de los numero comprendidos entre 1 y 1000 la cumplen en unos cuantos pasos, puede pasar que haya un número que ni con 20000 iteraciones la cumpla, al hacer 20000 pasos la aplicación demandará memoria y capacidad de procesamiento por lo que las cosas se pueden volver inestables, además como todas las comprobaciones se hacen en el hilo principal, la aplicación se bloqueará hasta que termine (cualquiera de los dos casos, si se cumple o si se llego al limite establecido), en los siguiente post veremos cómo usar "threads" (C# y Java) y cómo manejarlas un poco, para; si por ejemplo; comenzamos un proceso largo, lo podamos detener en cualquier momento (si aun así quieres probar los alcances de tu dispositivo, puedes probar el número 196 con el límite que más gustes, pero quedas advertido que ni con 24000 iteraciones se cumple la conjetura).

Por ahora es todo. Los leo luego.