Xwork's Blog

The lord is waiting to take your hand.

Feliz 2019

No hay comentarios.
Hola de nuevo a todos, el día de hoy solo escribo para desearles un feliz año 2019, cómo de costumbre, el año que viene publicaré mas y mas post que espero que sean de utilidad para el que los lee.

Sin nada mas que decir, FELIZ 2019!!!
Los leo luego.

No hay comentarios. :

Publicar un comentario

Vamos a programar #63 - Actualizando un gadget de windows 7 a windows 10 (parte 2).

No hay comentarios.
Hola de nuevo a todos, el dia de hoy vamos a terminar de modificar el gadget de reloj para usarlo en windows 10. Retomando el post anteror, el dia de hoy vamos a hacer modificaciones al gadget para hacerlo un poco mas a nuestro gusto.

Cambiando la localización.

Usualmente cuando nos referimos a localización, hablamos del idioma en el que estará el programa. Por default, el gadget viene en ingles, pero si queremos cambiarlo al idioma español (o al que sea), para hacer los cambios, debemos de editar el archivo "localization.js" y cambiar todo el contenido por el siguiente (si quieres que el gadget esté en español):


console.log('loaded: localization.js');

var m = ['ENERO','FEBRERO','MARZO','ABRIL','MAYO','JUNIO','JULIO','AGOSTO','SEPTIEMBRE','OCTUBRE','NOVIEMBRE','DICIEMBRE'];
var d = ['DOMINGO','LUNES','MARTES','MIERCOLES','JUEVES','VIERNES','SABADO'];

Básicamente lo que hacemos es escribir los nombres para cada día de la semana y para cada mes, esto no tiene realmente nada de difícil, salvo la parte de escribir los nombres de los días en otros idiomas.

De 12 a 24 horas

Otro de las partes que se puede modificar del gadget, es cómo se muestra la hora, por default el reloj viene en formato de 12 horas, pero además no se muestran lo segundos.

Antes de continuar, hay que resaltar que actualizar la hora a cada segundo, puede traer algunos problemas de rendimiento en algunos equipos, en lo personal, no lo he notado en un equipo medianamente viejo y mucho menos se siente en equipos mas modernos, por lo que si experimentas problemas de rendimiento, debes de cambiar el código para que funcione mejor (o quitar el gadget).
Una vez hecha la advertencia anterior, veamos el código.

console.log('loaded: gadget.js');

var font;
var color;
var opacity;
// var negativeVerticalMargin;
// var negativeHorizontalMargin;

var toTime;
var toDate;
var toMonth;
var toDay;

var targetWidth;
var targetHeight;

function init() {
	console.log('[init]');

	System.Gadget.settingsUI = 'settings.html';
	System.Gadget.onSettingsClosed = function() {
		readSettings();
		createTextObjects();
		refresh(false);
	};

	readSettings();
	createTextObjects();
	refresh(true);
}

function readSettings() {
	console.log('[readSettings]');

	font = System.Gadget.Settings.readString('font') || 'Arial';
	color = System.Gadget.Settings.readString('color') || 'white';
	opacity = System.Gadget.Settings.read('opacity') || 30;
	// negativeVerticalMargin = System.Gadget.Settings.read('negativeVerticalMargin') || 0;
	// negativeHorizontalMargin = System.Gadget.Settings.read('negativeHorizontalMargin') || 0;

	console.log('    font: '+font);
	console.log('    color: '+color);
	console.log('    opacity: '+opacity);
}

function createTextObjects() {
	console.log('[createTextObjects]');

	var bg = document.getElementById('bg');
	bg.removeObjects();

	// 0's are dummy chars for initial text object alignment
	toTime = bg.addTextObject('00:00:00', font, 105, color, 0, 0);
	toDate = bg.addTextObject('0', font, 80, color, toTime.left, 90); //(toTime.top+toTime.height)*0.69);
	toMonth = bg.addTextObject('0', font, 45, color, toDate.left+toDate.width, 95);
	toDay = bg.addTextObject('0', font, 30, color, toDate.left+toDate.width, 135);

	//we really don't want to be changing the width of the gadget because of clock time
	//because gadgets are anchored on the desktop by the left side, and the content is right-aligned

	targetWidth = document.getElementsByTagName('body')[0].style.width = toTime.width;
	targetHeight = document.getElementsByTagName('body')[0].style.height = toDay.top + toDay.height;
}

function refresh(autoreload) {
	console.log('[refresh]');

	console.log('    font: '+font);
	console.log('    color: '+color);
	console.log('    opacity: '+opacity);


	var currentTime = System.Time.getLocalTime(System.Time.currentTimeZone);
	var currentDate = new Date(Date.parse(currentTime));

	var hours = currentDate.getHours();

	var minutes = currentDate.getMinutes();
	minutes = ((minutes < 10) ? ':0' : ':') + minutes;
// Comentar las siguientes dos lineas para no mostrar los segundos
	var seconds = currentDate.getSeconds();
	seconds = ((seconds < 10) ? ':0' : ':') + seconds;
//Quitar los comentarios de la siguiente linea y comentar la siguiente para no mostar los segundos
	//toTime.value = hours + minutes + " HR";
	toTime.value = hours + minutes + seconds;
	toTime.font = font;
	toTime.color = color;
	toTime.opacity = opacity;

	toTime.left = targetWidth - toTime.width;
	toTime.top = 0;


	toDate.value = currentDate.getDate();
	toDate.font = font;
	toDate.color = color;
	toDate.opacity = opacity;

	toDate.left = toTime.left;
	toDate.top = 90; // - negativeVerticalMargin;


	toMonth.value = m[currentDate.getMonth()];
	toMonth.font = font;
	toMonth.color = color;
	toMonth.opacity = opacity;

	toMonth.left = toDate.left + toDate.width;
	// goofly little formula here to goose the month riiiigh up to but not touching the time, for most fonts
	toMonth.top = toDate.top + 4 + (toDate.top * 0.05) ; //95;


	toDay.value = d[currentDate.getDay()];
	toDay.font = font;
	toDay.color = color;
	toDay.opacity = opacity;

	toDay.left = toMonth.left;
	toDay.top = toMonth.top + 41; //135;


	console.log('    width: '+toTime.width+', height: '+toDay.top + toDay.height);
	console.log('    toTime: x='+toTime.left+', y='+toTime.top+', h='+toTime.height+', w='+toTime.width);
	

	if (autoreload) {
		//cuando mostramos los segundos hay que actualizar cada segundo, pero si queremos actualizar cada minutos, debemos hacerlo cada 60
		//en lugar de tener 1*1000, debemos cambiar a 60*1000
		setTimeout(function() { refresh(true); }, 1*1000);
	}
}

Este código es el que vamos a sustituir en el archivo "gadget.js" que es el principal. Con lo anterior, hemos cambiado la presentación de 12 a 24 horas, el código viene comentado para que hagas los cambios que creas necesarios, si te gusta más el formato de 12 horas, bastará con que revises el código fuente del original y el que aquí vimos.

El gadget empaquetado y que luce cómo el de la imagen, lo puedes descargar de mi dropbox y probarlo.

Los leo y luego y feliz 2019!!

No hay comentarios. :

Publicar un comentario

Vamos a programar #62 - Actualizando un gadget de windows 7 a windows 10.

No hay comentarios.
Hola de nuevo a todos, el día de hoy vamos a ver cómo es "posible" actualizar un gadget de Windows 7 para poder usarse en Windows 10.
Cómo muchos sabrán, hago programas para computadora, la mayoría de ellos están diseñados para ejecutarse en computadoras con Windows, pero muy específicamente en computadoras con Windows 7 para abajo. Gracias a que en la mayoria de los programas se usa .NET que se encarga de hacer el trabajo sucio por nosotros si es necesario que la aplicación se ejecute en un sistema operativo superior; llámese windows 10. Pero hace unos días, recibí la solicitud de un programa; mejor dicho; una "app" de windows 10 (y no voy a entrar en detalles por ahora) por lo que me vi en la necesidad de actualizar mi computadora.


La solicitud llegó unos días después, justo cuando había logrado personalizar mi escritorio a mi gusto.
Para hacer memoria, los gadgets son "mini-aplicaciones" basados en html, javascript y varios controles activex mas, estos aparecieron  por primera vez en windows vista y su tiempo de vida solo se prolongo hasta windows 7 (8?), debido a que se encontraron fallas críticas que permitían de manera potencial, permitir el acceso al equipo.

Es posible utilizar los gadgets en windows 10, pero para lograr una experiencia similar a windows 7, es necesario modificarlos, en el caso muy particular del gadget que vamos a modificar, el principal problema eran las fuentes. Solo estaban disponibles las basicas de windows y a pesar de haber instalado varias más, ninguna de éstas se mostraba.

Antes de continuar, si quieres usar el gadget en windows 10, debes de buscar algún programa que permita hacerlo, por cuestiones de seguridad no lo voy a poner aqui, ya que cómo mencioné antes, hay vulnerabilidades que podrían ser explotadas por un tercero. Si aun así quieres continuar, puedes buscar en internet "windows 10 gadgets", luego debes de descargar la version original del gadget desde su sitio web , descargarlo e instalarlo (solo para darle el debido crédito a sus autores, ya que en teoría vamos a reemplazar muchos de los archivos originales, aunque según la licencia, podemos modificarlo a nuestro gusto si no es que todo).

Ahora que ya estan dichas todas las advertencias, procederemos a abrir el gadget con Winrar.
Y extraeremos todo el contenido en una carpeta, una vez hecho buscaremos la carpeta llamada "js" y con un bloc de notas (en mi caso notepad++), abriremos el archivo "font-picker.js" y reemplazaremos su contenido con el siguiente codigo:

/*
Based on Jonathan Abbott's Flip Calendar gadget
http://vistagadgets.spaces.live.com/
*/

console.log('loaded: font-picker.js');

var fontPicker = {
	supportedFontTypes: ' (truetype) ',
	fontsToSkip: ':Bookshelf Symbol 7:'+':MS Outlook:'+':MS Reference Specialty:'+':MT Extra:'+':Symbol:'+':Webdings:'+':Wingdings:'+':Wingdings 2:'+':Wingdings 3:',

	fillYield: 50,	// Yield back to IE every X ms

	fontSizer: null,
	fonttypeIndex: 0,
	currentFont: System.Gadget.document.parentWindow.font,
	selectingFont: -1,

	init: function() {
		console.log('[init]');

		// System.Gadget.onSettingsClosing = this.settingsClosing;

		divFonttype.style.fontFamily = divFonttype.innerText = this.currentFont;
		divFonttype.style.fontSize = '16px';

		this.fontSizer = document.createElement('<g:background />').addTextObject(this.currentFont, this.currentFont, 16, 'black', 0, 0);

		//remove focus from the 1st checkbox
		document.body.focus();
		
		//read the fonts from the registry and build the table
		this.buildList(this.readFonts());

		// setFont(currentFont, 0);
		// console.log(tdFonts);
		// console.log(this.fonttypeIndex);
		tdFonts.rows(this.fonttypeIndex).className = 'menuItemSelected';
	},

	settingsClosing: function(event) {
		console.log('[settingsClosing]');
		
		if (event.closeAction == event.Action.commit) {
			System.Gadget.Settings.writeString('font', divFonttype.innerText);
		}
		// event.cancel = false;
	},

	readFonts: function() {
		console.log('[readFonts]');

		var HKLM = 2147483649 ;
		var rPath = 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts';
	

		// connect to the registry
		var oSwbem = new ActiveXObject('WbemScripting.SwbemLocator');
		var oSvc = oSwbem.ConnectServer(null, 'root\\default');
		var oReg = oSvc.Get('StdRegProv');

		// enumerate the values 
		var oMethod = oReg.Methods_.Item('EnumValues');
		var oInParam = oMethod.InParameters.SpawnInstance_();
		oInParam.hDefKey = HKLM;
		oInParam.sSubKeyName = rPath;
		var oOutParam = oReg.ExecMethod_(oMethod.Name, oInParam);

		// get the values into an array
		var sNames = oOutParam.sNames.toArray();

		// sort it
		sNames.sort();

		return sNames;
	},

	buildList: function(listItems) {
		console.log('[buildList]');

		// try{
			var counter = new Date();

			while (listItems.length > 0 && (new Date())-counter<this.fillYield) {
				// is the font one we can use
				var fontType = ' ' + listItems[0].substring(listItems[0].lastIndexOf('(')).toLowerCase() + ' ';
				var fontName = listItems[0].substring(0, listItems[0].length - fontType.length + 1);

				if(this.supportedFontTypes.indexOf(fontType) > -1 && this.fontsToSkip.indexOf(':' + fontName + ':') == -1) {
					//check the height, to see if it's a font we can use
					this.fontSizer.font = this.fontSizer.value = fontName;
					this.fontSizer.height = 0;

					if (this.fontSizer.height != 0) {
						// console.log('Using font: '+fontName);

						var tRow = tdFonts.insertRow(tdFonts.rows.length);
						tRow.className = 'menuItem';
						
						var tCell = tRow.insertCell(0);
						tCell.innerText = fontName;
						tCell.style.fontFamily = fontName;
						tCell.style.fontSize = divFonttype.style.fontSize;

						if (fontName == this.currentFont) {
							this.fonttypeIndex = tdFonts.rows.length - 1;
							// tRow.className = 'menuItemSelected';
						}

					} else {
						// console.log('Cant use font: '+fontName);
					}
				}
				listItems.shift();
			}

			if (listItems.length > 0) {
				var context = this;
				setTimeout(function() { context.buildList(listItems); }, 20);
			}
		// } catch(err) {
			// console.log('buildList: '+err.name+' - '+err.message);
		// }
	},

	showFontMenu: function(id, x, y) {
		console.log('[showFontMenu]');

		if (id == -1) {
			if (fontMenu.style.display != 'none') {
				id = this.selectingFont;
			} else {
				return;
			}
		}
			
		fontMenu.style.display = (fontMenu.style.display == 'none') ? 'inline' : 'none';

		if (fontMenu.style.display == 'none') {
			console.log('[showFontMenu] hidden');

			switch (this.selectingFont) {
				case 0:
					fontDropPNG.src = 'images/dropmenu.png';
					break;
			}
			this.selectingFont = -1;
		} else {
			console.log('[showFontMenu] displayed');

			fontMenu.style.top = y;
			fontMenu.style.left = x;
			this.selectingFont = id;
			
			// console.log(tdFonts.rows(fonttypeIndex).className);
			// console.log(id);

			switch (id) {
				case 0:
					fontMenu.scrollTop = tdFonts.rows(this.fonttypeIndex).offsetTop - 65;
					tdFonts.rows(this.fonttypeIndex).className = 'menuItemSelected';
					break;
			}

			// console.log(tdFonts.rows(fonttypeIndex).className);
		}
	},

	setFont: function(index, viaKey) {
		console.log('[setFont] '+index+', '+viaKey);

		var sFont = tdFonts.rows(index).cells(0).innerText;

		var selecting = (viaKey != null) ? viaKey : this.selectingFont;
		switch (selecting) {
			case 0:
				divFonttype.style.fontFamily = sFont;
				divFonttype.innerText = sFont;
				tdFonts.rows(this.fonttypeIndex).className = 'menuItem';
				this.fonttypeIndex = index;
				tdFonts.rows(this.fonttypeIndex).className = 'menuItemSelected';
				break;
		}

		// adjust the scrollbar if needed
		if (fontMenu.scrollTop > tdFonts.rows(index).offsetTop) {
			fontMenu.scrollTop = tdFonts.rows(index).offsetTop;
		}

		if (fontMenu.scrollTop < tdFonts.rows(index).offsetTop+tdFonts.rows(index).offsetHeight-150) {
			fontMenu.scrollTop = tdFonts.rows(index).offsetTop+tdFonts.rows(index).offsetHeight-150;
		}
	},

	setFontStyle: function(i) {
		console.log('[setFontStyle]');

		switch (i) {
			case 0:
				divFonttype.style.fontWeight = divFonttypeBold.checked ? 'bold' : '';
				divFonttype.style.fontStyle = divFonttypeItalic.checked ? 'italic' : '';
				break;
		}
	},

	keyPress: function(i) {
		console.log('[keyPress]');

		switch (event.keyCode)
		{
			case 40:	//down arrow
				if (i == 0) {
					if (this.fonttypeIndex < tdFonts.rows.length-1) {
						this.setFont(this.fonttypeIndex+1, i);
					}
				}
				break;
			case 38:	//up arrow
				if (i == 0) {
					if (this.fonttypeIndex > 0) {
						this.setFont(this.fonttypeIndex-1, i);
					}
				}
				break;
			case 36:	//home
				this.setFont(0, i);
				break;
			case 35:	//end
				this.setFont(tdFonts.rows.length - 1, i);
				break;
		}
	},

	mouseOver: function(mouseEntered, id) {
		// console.log('[mouseOver]');

		var selected = (this.selectingFont == 0) ? this.fonttypeIndex : 0;

		if (mouseEntered) {
			id.className = (id.rowIndex == selected) ? 'menuItemHoverSelected' : 'menuItemHover';
		} else {
			id.className = (id.rowIndex == selected) ? 'menuItemSelected' : 'menuItem';
		}
	}
}

La modificaciones que hicimos en este archivo sirven para detectar todas las fuentes disponibles en Windows. Anteriormente, las fuentes se guardaban por default en la ruta "\Windows\fonts" y el "catalogo" de fuentes se guardaba en el registro en la siguiente ruta: "HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts\\", pero para Windows 10 (y no se si para el 8 tambien), se cambio la ubicacion real y la del catalogo; ahora en lugar de estar en "HKLM" (HKey Local Machine), paso a almacenarse en "HKCU" (HKey Current User). Para hacer uso de cada una de las llaves, debemos de acceder a ellas por su valor hexadecimal, tomemos cómo referencia la siguiente lista:
  • HKEY_CLASSES_ROOT = 2147483648 (0x80000000)
  • HKEY_CURRENT_USER = 2147483649 (0x80000001)
  • HKEY_LOCAL_MACHINE = 2147483650 (0x80000002)
  • HKEY_USERS = 2147483651 (0x80000003)
  • HKEY_CURRENT_CONFIG = 2147483653 (0x80000005)
  • HKEY_DYN_DATA = 2147483654 (0x80000006)
Si revisas detenidamente, verás que en el código javascript hay una variable llamada "HKLM" cuyo valor corresponde a la llave "HKey Local Machine" que su vez es el número 2147483650, entonces la manera fácil de cambiar la llave a la que vamos a acceder es simplemente cambiando el valor de ésta a la del valor de la llave de destino (en este caso 2147483649).

Una vez cambiado este valor, para volver a empaquetar el gadget, debemos de usar winrar y elegir en formato "zip" y en método de compresión "No comprimir". Al archivo resultante de la compresión, simplemente le cambiamos el nombre de tal modo que la extensión en lugar de ".zip", sea ".gadget"

Después para instalarlo, simplemente hacemos doble clic en el archivo y se instalará al repositorio de gadgets.

Y bien, por ahora es todo, el siguiente post veremos como crear una versión de acuerdo al idioma que elijas y cómo agregar un par de personalizaciones para que se adapte un poco a tu gusto.

No hay comentarios. :

Publicar un comentario

Vamos a programar #61 - Calculadora de Vigenere Online 2.1

No hay comentarios.
Hola de nuevo a todos, hace poco mientras revisaba los comentarios, alguien mencionó: "tu calculadora es una porquería, no hace bien los cálculos", incluso pegó una prueba de cómo era que su web hacia bien las cosas y la mía no.
Solo para ver que no estuviera equivocado, revise su página y tan solo al ver su código fuente me di cuenta de porque las cosas no resultaban iguales en ambos casos.

La respuesta era simple, la del comentario no contaba la "Ñ", prácticamente uso código que encontró en la web pero de alguien que habla inglés por eso la disparidad.

Ya que realmente no había un error y creo que solo era spam, eliminé el comentario y me dio la idea para actualizar la calculadora pero en está ocasión agregué el soporte para el idioma inglés.

Antes que nada te recomiendo leer el primer post sobre el cifrado de Vigenere, y después tomes en consideración lo siguiente:

  1. En el idioma ingles no se usa la "Ñ"
  2. Se le asigna un valor de A = 1, B = 2, ..., Z = 26.
Ahora veamos SOLO el código javascript, puede parecer redundante, pero simplemente lo hice para dejar claro (y bastante obvio) cómo es que funciona.

function LangSelected(){
var radios = document.getElementsByName('Lang');
	for (var i = 0, length = radios.length; i < length; i++) {
		if (radios[i].checked) {
			return Number(radios[i].value)
		}
	}
}

function AsciiToA1(Char1, ForSpanish){
Char1 = Char1;
	if (IsEnie(Char1) == true)
		return 15;
	if (IsLower(Char1, ForSpanish))
		Char1 -= 32;
	if(ForSpanish == 1){
		if(Char1-64 < 15){
			return(Char1-64);
		}
		else if(Char1-64 >= 15 && Char1-64 < 28){
			return(Char1-63);
		}
	}
	if(ForSpanish != 1)
			return(Char1-64);
}
function IsLetter(c, ForSpanish){
	return IsUpper(c, ForSpanish) || IsLower(c, ForSpanish);
}

function IsEnie(Char2){
	if(Char2 == 209 || Char2 == 241){
		return true;
	}else{
		return false;
	}

}

function IsUpper(Char3, ForSpanish){
	if (ForSpanish == 1){
		 if (IsEnie(Char3)){
			return true;
		}else if(Char3 >= 65 && Char3 <= 90){
			return true;
		}
	}else if (ForSpanish != 1){
	if(Char3 >= 65 && Char3 <= 90)
		return true;
	else
		return false;
	}
}

function IsLower(Char4,ForSpanish){
	if (ForSpanish == 1){
		 if (IsEnie(Char4)){
			return true;
		}else if(Char4 >= 97 && Char4 <= 122){
			return true;
		}
	}else if (ForSpanish != 1){
	if(Char4 >= 97 && Char4 <= 122) 
		return true;
	else
		return false;
	}
}

function IsA1Char(Char5, ForSpanish){
	if (ForSpanish == 1)
	{
		if (Char5 > 27){
			return false;
		}else{
			return true;
		}
	}else if (Char5 > 26 && ForSpanish != 1){
		return false;
	}else{
		return true;
	}
}

function Encriptar(ModeEnc,ForSpanish)
{
	var 	ForSpanish =  LangSelected();
	var GetPhrase = document.getElementById('InputText').value;
	var GetPass = document.getElementById('PassWord').value;
	var Codes = [];
	var TotalChars;
	if(ForSpanish == 1)
		TotalChars = 27;
	else
		TotalChars = 26;
	if(GetPhrase.length < 1 || GetPass.length < 1)
	{
		alert('La frase/contraseña no puede estar en blanco')
		return;
	}
	var PassData = PhraseToArray(GetPass, ForSpanish);
	var PhraseData = PhraseToArray(GetPhrase, ForSpanish);
	var SpaceCount = 0;

	if (ModeEnc == true){
		for(var i = 0; i < PhraseData.length; i++){
			if(IsA1Char(PhraseData[i], ForSpanish) == false){
				Codes.push(PhraseData[i]);
				SpaceCount += 1;
			}else if(IsA1Char(PhraseData[i], ForSpanish) == true){
				Codes.push((PassData[(i - SpaceCount) % PassData.length] + PhraseData[i]) % TotalChars);
			}
		}
	}else{
		for(var i = 0; i < PhraseData.length; i++)	{
			if(IsA1Char(PhraseData[i], ForSpanish) == false){
				Codes.push(PhraseData[i]);
				SpaceCount += 1;
			}else{
				var Value = PhraseData[i] - PassData[(i - SpaceCount) % PassData.length];
				if (Value < 1){
					Value += TotalChars;
				}
			Codes.push(Value % TotalChars);
			}
		}
	}
	document.getElementById('Result').value = Codes;
	return Codes;
}
function RebuildString(Codigos)
{
	var ForSpanish =  LangSelected();
	var Salida = ""
	for(var i = 0; i < Codigos.length; i++)
	{
		if (IsA1Char(Codigos[i], ForSpanish) == false){
			Salida += String.fromCharCode(Codigos[i]);
		}else if (ForSpanish == 1){
			if (Codigos[i] == 15 )
				Salida += String.fromCharCode(209);
			if (Codigos[i] == 0)
				Salida += String.fromCharCode(90);
			if(Codigos[i] < 15 && Codigos [i] > 0)
				Salida += String.fromCharCode(Codigos[i]+64);
			if(Codigos[i] > 15 && Codigos[i] < 28)
				Salida += String.fromCharCode(Codigos[i]+63);
		}else if (ForSpanish != 1){
			if (Codigos[i] == 0){
				Salida += String.fromCharCode(90);
			}else{
				Salida += String.fromCharCode(Codigos[i]+64);
			}
		}	
	}
	document.getElementById('Result').value = Salida;
}

function PhraseToArray(Text, ForSpanish){
	var Out = [];
	for(var i = 0; i < Text.length; i++){
		var CodeChar = Text.charCodeAt(i);
		if(IsLetter(CodeChar, ForSpanish) == true){
			Out.push(AsciiToA1(CodeChar, ForSpanish));
		}else{
			Out.push(CodeChar);
		}
	}
	return Out;
}

Primero que nada se agregó la función "LangSelected" , esta simplemente se usa para indicar en que idioma se va a realizar el cifrado, por ahora solo hay inglés y español. Para determinar cual es el idioma a usar, buscará en el formulario principal cual de los controles del tipo "radio" es el que esta seleccionado, si es el primer radio, la función devuelve 1 si es el segundo; devuelve 2.

Luego a casi todas funciones se le agrego un segundo parámetro llamado "ForSpanish" y éste cómo su nombre lo indica, sirve para determinar si estamos "hablando" o no en español.

El código es similar al anterior, solo que está hay un apartado para cuando es español y para cuando no.

Cómo de costumbre el código lo puedes descargar de mi dropbox, en breve se actualizara la calculadora online.

No hay comentarios. :

Publicar un comentario

Vamos a programar #60 - Los números de Fibonacci (Ver. C)

No hay comentarios.
Hola de nuevo a todos, el día de hoy vamos a ver un poco acerca de la sucesión de Fibonacci. Hace poco alguien me comentó que en la universidad, le dejaron de tarea hacer un programa en C que calculara el valor "n" de un número en la sucesión de Fibonacci.

Antes de continuar, vamos a ver que es esta sucesion.

En matemáticas, la sucesión de Fibonacci es la siguiente sucesión infinita de números naturales: 0,1,1,2,3,5,8,13,21,34,55, … La sucesión comienza con los números 0 y 1,2​ y a partir de estos, «cada término es la suma de los dos anteriores», es la relación de recurrencia que la define. A los elementos de esta sucesión se les llama números de Fibonacci. Esta sucesión fue descrita en Europa por Leonardo de Pisa, matemático italiano del siglo XIII también conocido como Fibonacci. Tiene numerosas aplicaciones en ciencias de la computación, matemática y teoría de juegos. También aparece en configuraciones biológicas, como por ejemplo en las ramas de los árboles, en la disposición de las hojas en el tallo, en las flores de alcachofas y girasoles, en las inflorescencias del brécol romanesco y en la configuración de las piñas de las coníferas. De igual manera, se encuentra en la estructura espiral del caparazón de algunos moluscos, como el nautilus. Wikipedia/SucesionDeFibonacci.
Para ponerlo de manera sencilla, para calcular el valor de la sucesión, debemos de sumar los dos números previos, excepto cuando el valor de n sea 0 o 1, a partir de "n = 2", es cuando debemos de empezar a sumar por lo que tendríamos algo similar a lo siguiente:
El propósito del programa era encontrar una manera de hacerlo de forma secuencial y otra de forma re-cursiva. Partiendo de eso, podemos crear un programa cómo el que sigue:

#include <stdio.h>
long FibonacciNumbersRec(int n)
{
	if (n == 0 || n== 1 )
		return n;
	else
		return (FibonacciNumbersRec(n - 1) + FibonacciNumbersRec(n - 2));
}

long FibonacciNumbersIt(int n)
{
	long v1 = 0;
	long v2 = 1;
	long  v3, i;
	for(i = 1; i <= n; i++)
	{
		v3 = v1+v2;
		v1 = v2;
		v2 = v3;
	}
	return v1;
}
int main()
{
	printf("%4d\n", FibonacciNumbersIt(6));
	printf("%4d\n", FibonacciNumbersRec(6));
}

El programa consta de dos funciones, una de ellas es "FibonacciNumbersRec" (Rec es por Recursive) y la otra es "FibonacciNumbersIt" (It por Iterative). Para la función "FibonacciNumbersRec", primeramente comprobamos si el parámetro "n" es cualquiera de los números 1 o 0, si es el caso, la función devuelve "n" cómo resultado, si no es el caso anterior, la función se mandará a llamar a si misma con el parámetro "n = n - 1" y al resultado de esto, se le sumará el valor que resulta de llamarse a si misma con parámetro de "n = n -2"; el valor que regresa la función será el resultado de sumar los casos anteriores.

Para evitar la recursividad, podemos aprovechar que la sucesión depende de los dos valores anteriores inmediatos, por lo que podemos hacer todo en un ciclo "for" y usando las variables auxiliares "v1","v2 y "v3". Cómo la sucesión comienza con 1 y con 0, le asignamos esos valores a "v1" y a "v2", el resultado para ese paso dentro de la sucesión, sera la suma "v1" + "v2", los asignamos a "v3" y tomando en cuenta lo mencionado hace un momento, le asignamos a "v1" el valor que tiene "v2" y a "v2" le asignamos el valor de "v3". Todo esto lo hacemos hasta que el iterador llegue al valor del parámetro "n". Finalmente la función regresará cómo resultado el valor de "v1".

Y bien, con el código anterior hemos demostrado que hay más de una forma para llegar al mismo resultado. Muchas veces es más fácil hacer uso de la recursividad, pero si por alguna razón no debemos (o no disponemos, dependiendo del lenguaje) usarla, siempre hay formas de encontrar una solución.

El código, al igual que el del post anterior, puedes copiarlo y probarlo en el compilador online.

Y bien. por ahora es todo, los leo luego.

No hay comentarios. :

Publicar un comentario

Vamos a programar #59 - La función de Ackerman (ver. C)

No hay comentarios.
Hola de nuevo a todos, el día de hoy; y después de casi un mes de "vacaciones"; vamos a continuar con un poco de programación.
Hace algunos días mientras hablaba con un amigo, me dijo que en la universidad, le habían dejado cómo tarea hacer un programa en C que probara la función de Ackerman.

Pero primero veamos una pequeña definicion sobre lo que es la función de Ackerman:
En teoría de la computación, función de Ackermann es una función matemática recursiva encontrada en 1926 por Wilhelm Ackermann, tiene un crecimiento extremadamente rápido, de interés para la ciencia computacional teórica y la teoría de la computabilidad. Hoy en día, hay una serie de funciones que son llamadas funciones Ackermann. Todas ellas tienen una forma similar a la ley original la función de Ackermann y también tienen un comportamiento de crecimiento similar. Esta función toma dos números naturales como argumentos y devuelve un único número natural. Wikipedia/FuncionDeAckerman
Y sigue las siguientes reglas:

  •  n+1 Si m = 0.
  • A(m-1, 1) si m > 0 y n = 0.
  • A(m - 1, A(m, n - 1)) si m > y n > 0.
Cómo podrás ver, se trata de una función que es recursiva, es decir, ella misma se manda a llamar dentro de la misma función.

Sabiendo lo anterior, podemos crear un programa en C usando el siguiente código:


# include <stdio.h>
int AckermanFunction(int m, int n)
{
	if (m == 0)
		return (n+1);
	else
		if (n == 0)
			return (AckermanFunction(m - 1, 1));
		return(AckermanFunction(m - 1, AckermanFunction(m, n - 1)));
}
int main()
{
	int i,j;
	for (i = 0; i < 5; i++)
	{
		for (j = 0; j < 3; j++)
		{
			printf("%6d", AckermanFunction(i, j));
		}
		printf("\n");
	}
	return 0;
}

El código anterior consta de dos partes, la primera es la función llamada "AckermanFunction" que recibe dos parámetros del tipo "int". La función sigue las reglas de la función de Ackerman por lo que si al evaluar, el parámetro "m", este vale cero, la función regresará el valor de "n" mas uno, si no es el caso, la comprobación se hará para el valor de "n = 0", si es el caso, la función se mandará a llamar a si misma y se pasará cómo argumentos a "m" con valor de "m - 1" y "n" con el valor de "n = 1" y regresará el resultado de llamarse a si misma. Finalmente, si "m" y "n" son valores mayores a cero "m > 0", "n > 0", el valor que la función regresará, será el resultante de llamarse a si misma con los parámetros "m-1"  para "m" y el resultado de llamarse a si misma con los parámetros "m = m - 1" y "n = n - 1" para "n".
Para poder ver los resultados, puedes copiar el código y usarlo en un compilador de c en linea. y debería de salir un resultado similar al anterior. Puedes modificar el código para ver que pasa "mas allá" de la quinta fila, pero el resultado puede tomar mucho tiempo.

Y bien, por ahora es todo.

Los leo luego.

No hay comentarios. :

Publicar un comentario

Vamos a platicar #8 - Reseña del velocímetro.

No hay comentarios.
Hola de nuevo a todos, el día de hoy vamos a hablar sobre una de las cosas que prometí hace mucho: La reseña del velocímetro.


En abril del 2018 fue cuando empece con la construcción del velocímetro, y desde ese día lo he usado casi a diario, muy pocas le he modificado. Algunas de las cosas que tenía planeadas simplemente ya nos las implementé, si hacemos memoria, implementar las direccionales era algo que tenia planeado hacer (y no he hecho), pero a medida que lo usaba me di cuenta que con una luz intermitente tradicional y con la pantalla de 16x2 era más que suficiente; eso hasta cierto punto.

Normalmente lo uso en la mañana, pero me di cuenta de algunas cosas que podrían ser realmente útiles y que gracias al arduino se pueden implementar de manera un tanto sencilla. Un reloj en tiempo real, eso es algo que sin duda podría resultar útil. En algunas ocasiones he ido en bicicleta a lugares donde tengo compromisos y en ocasiones no dimensiono cuanto tiempo es el que he gastado en el camino. Puedo consultar mi teléfono y ver la hora (o comprar un G-Shock de Casio), pero a veces en medio del trafico hay que poner atención al camino, entonces ahi es cuando se dificulta un poco ver la hora. En cambio si se le agregara el modulo, bastaría con mirar un instante a la pantalla y ya sabría que hora es.

En la parte del software, he pensado en agregar algunas funciones, pero para poder desplegarlas todas, es necesario una pantalla más grande. Hace tres meses ordene una pantalla de 20x4 en ali-express, pero no ha llegado; en ese tiempo estaba pasando todos los componentes a una caja de acrilico que hice, entonces pensé que seria buena idea actualizar el hardware antes de sellar medianamente bien la caja, pero espere y la pantalla que tenia tiempo de espera estimado para su llegada de dos meses; aun no llega.
El motivo para usar una pantalla más grande es simple: así no es necesario agregar botones; si se agregaran botones para la operación del velocímetro, entonces sería lo mismo que sacar mi teléfono de mi mochila, presionar un botón, ver la hora y volverlo a guardar.

Por lo demás, lo que es el "Sistema básico" funciona de maravilla, a pesar de que el cálculo de la velocidad depende en buena medida de la llanta de enfrente, el margen de error está dentro del 5% por cada cien metros en la presentación; es decir, al momento de mostrar la distancia solo se usan dos decimales y por lo tanto se redondea, pero internamente el calculo se hace bien (dentro del margen de error) y las distancias que he medido con respecto a las mediciones que ya hay suelen estar bien (dentro de lo que cabe).

Y bien, por ahora es todo, si no has intentado construir tu propia versión del velocímetro, puedes hacerlo ahora siguiendo las instrucciones que hay en los post.

Los leo luego.

No hay comentarios. :

Publicar un comentario

Learning Machine #12 - Diseño de aplicaciones multi-hilo

No hay comentarios.
Hola de nuevo a todos. En ocasiones anteriores hemos hecho aplicaciones que están compuestas por varios procesos. Pero antes de avanzar, veremos que tipo de multi-hilo es el más conveniente dependiendo de que tarea queramos realizar.


En post anteriores hemos visto que hay tareas que requieren una tiempo de procesamiento largo; dependiendo del caso, hay situaciones en las que el usuario podrá seguir trabajando ya que no depende del resultado de la computación. En otros casos no es así, ya que se depende mucho de los datos resultantes.

Cuando nos encontramos en la primer situación, podemos informar o no que es lo que está sucediendo, pero cuando se presenta la segunda, por lo general es importante hacerlo. En alguna ocasión en un post, mostré cómo es que las barras de progreso suelen ser meros placebos ya que hay una serie de factores que afectan el tiempo en que cada calculo se hace (cuantas veces no hemos visto el famoso "Restan cero segundos" o "Progreso 100%" que dura más que todo lo anterior). Ahora pra entender un poco, veremos cada caso.

Caso uno: "no necesito los datos".

Ahora veremos un ejemplo, para eso modificaremos un poco el programa en C# que prueba la conjetura de Collatz. para empezar veamos el código.
using System;
using System.Windows.Forms;
using System.Numerics;
using System.Threading;

namespace ConjeturaDeCollatz
{
	public partial class FrmMain : Form
	{
		private delegate void UpdateListDelegate(string Value);

		private void UpdateList(string Value)
		{
			if (LBResults.InvokeRequired)
			{
				LBResults.Invoke(new UpdateListDelegate(UpdateList), Value);
			}
			else
			{
				LBResults.Items.Add(Value);
			}
		}
		private void CalculateCollatz(string Number)
		{
			BigInteger MyNumber;
			BigInteger.TryParse(Number, out MyNumber);
			while (MyNumber > 1)
			{
				Thread.Sleep(1500);
				if (MyNumber.IsEven == false)
				{
					MyNumber = MyNumber * 3 + 1;
					UpdateList(MyNumber.ToString());
				}
				else
				{
					MyNumber = MyNumber / 2;
					UpdateList(MyNumber.ToString());
				}
			}
		}
		private void RunWork(string Number)
		{
			Thread DoWork = new Thread(new ThreadStart(() => CalculateCollatz(Number)));
			DoWork.Start();
		}
		private void RunWorkWithNoThread(string Number)
		{
			CalculateCollatz(Number);
		}
		public FrmMain()
		{
			InitializeComponent();
		}

		private void BtnStart_Click(object sender, EventArgs e)
		{
			LBResults.Items.Clear();
			RunWork(TxtNumberIn.Text);

		}

		private void BtnStart2_Click(object sender, EventArgs e)
		{
			RunWorkWithNoThread(TxtNumberIn.Text);
		}
	}
}

Para poder hacer las pruebas, modificamos un poco el código que ya teníamos y además, agregamos un botón para que la acción se ejecute pero no en un hilo separado, si hace clic en este botón, verás que la aplicacion parecerá que se bloquea e incluso si presiona muchas veces el botón de cerrar, windows te preguntará si quieres cerrar la aplicación porque esta no responde, pero no es el caso; el proceso sigue corriendo de manera "normal", pero debido a que se encuentra la ejecución dentro del bucle "while" no puede responder a las peticiones del usuario. Por lo que simplemente podemos esperar (el usuario más bien) a que todo acabe, de cualquier forma los resultados no son necesarios.


Caso dos "necesito esos datos".

Cuando los datos son necesarios, debemos de hallar una forma de informar al usuario cuando hay datos listos para usarse, para eso vamos a crear un hilo secundario, imaginemos que queremos escalar 1000 imágenes y a cada una arreglarle el color, por lo que se debe de hacer de una por una a la vez; pero solo parte del trabajo humano porque el proceso de escalar las imágenes se puede hacer poniendo todas en una cola y escalarlas y solo cuando se ha procesado la primera, abrirla para empezar a trabajar con ella y en el fondo el escalado del resto de las imágenes puede seguir ya que nos va a tomar varios minutos en ajustar  nuestra imagen a lo que necesitamos.

En alguno de los post que hemos hecho, hicimos un programa en android que se conectaba al bluetooth del Clock View (puedes ver el post), hacemos uso de dos tipos de thread, en la primera, simplemente notificábamos al usuario que se está realizando la conexión entre el dispositivo y Clock view y cuando esta se llevaba a cabo, un "thread" diferente se se ejecutaba y su función era manejar todos los datos que se transmitían desde/hacia el dispositivo y el bluetooth y mientas no se pida que la conexión se cierre (o se cierre la aplicación), este hilo seguirá corriendo en el fondo.

Y bien, por ahora es todo, el código de la conjetura de Collatz lo puedes modificar para probar el ejemplo de este post. En los siguiente post continuaremos con más programación.
Los leo luego

No hay comentarios. :

Publicar un comentario

Vamos a programar #58 - AsyncTask en java.

No hay comentarios.
Hola de nuevo a todos, el día de hoy vamos a ver cómo usar la clase "AsyncTask" para proveer de un poco de multi-threading a una aplicación.



En el la aplicacion de los números de Muchhausen, vimos que empleamos una subclase llamada "UpdateList" que hereda de "AsyncTask", el nombre de la clase es bastante descriptivo, puesto que lo que hace es ejecutar una tarea de forma asíncrona.

la clase AsyncTask posee los siguientes metodos:

  • doInBackground(Params... params).
    • Este método se debe de sobrecargar para poder realizar una tarea de forma asíncrona.
  • onCancelled(Result result)
    • Se ejecuta en el hilo principal despúes de que cancel(boolean) es invocado o  doInBackground(Object[]) ha terminado.
  • onPostExecute(Result result).
    • Se ejecuta cuando doInBackground(Params...) ha terminado su ejecución.
  • OnPreExecute()
    • Se ejecuta en el hilo principal antes que doInBackground(Params...).
  • onProgressUpdate(Progress... values)
    • Este método se ejecuta despúes de que publishProgress(Progress...) is invoked.
  • publishProgress(Progress... values)
    • Este método puede ser invocado  desde doInBackground(Params...) para actualizar elementos en el hilo principal.

Para tener una idea de cómo funciona, retomemos el ejemplo de los números de Munchhausen. Para empezar, ¿qué es lo queremos hacer?, en este caso buscamos crear una lista de resultados en la que indiquemos si un número n cumple o no la conjetura. entonces para construir la clase AsyncTask de acuerdo a nuestros requerimientos, puede ser de la siguiente forma: private  class UpdateList extends AsyncTask<Integer,Void,ArrayList> y cada parametro corresponde a:

  • Integer, son los parametros que se usaran en doInBackground(), son del tipo integer porque son los números que vamos a usar cómo limites inferior y superior, y un tercero que se usará para indicar  si se deben mostrar o no todos los números.
  • Void, el segundo parametro cómo en eeste caso no mostramos ningún progreso en concreto, simplemente mostramos el dialogo de forma indeterminada, usualmente suele ser un valor Integer.
  • ArrayList,  debido a que todos los resultados de los calculos los guardamos en un "ArrayList", para poder usar esa lista cómo resultado de la ejecución, debemos de hacerlo del mismo tipo, si por alguna razón nuestro proceso debe de devolver un valor "boolean", "String" o etc. el tercer parametro debe de ser de ese tipo.
Para ejecutar el proceso, en el hilo principal debemos de llamar a "new UpdateList().execute(Min,Max,ShowAll);" los parámetros que usemos aqui se convertiran en un array y se asignaran a la variable que se use cuando definimos nuestro proceso.

Ahora veamos el siguiente código:

// Original de https://developer.android.com/reference/android/os/AsyncTask
 private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
	 protected Long doInBackground(URL... urls) {
		 int count = urls.length;
		 long totalSize = 0;
		 for (int i = 0; i < count; i++) {
			 totalSize += Downloader.downloadFile(urls[i]);
			 publishProgress((int) ((i / (float) count) * 100));
			 // Escape early if cancel() is called
			 if (isCancelled()) break;
		 }
		 return totalSize;
	 }

	 protected void onProgressUpdate(Integer... progress) {
		 setProgressPercent(progress[0]);
	 }

	 protected void onPostExecute(Long result) {
		 showDialog("Downloaded " + result + " bytes");
	 }
 }
 

Esta clase se construye de la siguiente forma: DownloadFilesTask extends AsyncTask<URL, Integer, Long>,  suponiendo que se llame a ejecutar con los siguientes parametros: "URL1", "URL2","URL3","URL4"..."URL10". El parámetro URL contendrá estos diez valores distintos entonces en "doInBackground(URL.. urls)" lo que se hace es primero asignar los parámetros a la variable "urls" y luego medir los elementos con "urls.length" y cada parámetro pasado al momento de iniciar la ejecución, estará en esta matriz. EL resultado de "doInBackground"  es del tipo "Long" que usaremos en "onPostExecute" para mostrar al usuario cuantos bytes se descargaron. Cada vez que se ejecute "publishProgress()" en "doInBackGround()", se ejecutará "onProgressUpdate()", en esta parte es donde debemos de cambiar cualquier indicador de progreso. finalmente en "onPostExecute()", mostraremos un dialogo con la información de cuantos bytes se descargaron.

En post anteriores ya hemos usado "AsyncTask"

Y bien, por ahora es todo, en el siguiente post añadiremos una barra de progreso y entenderemos mejor cómo y cuando debemos de hacer uso de "AsyncTask".

Los leo luego.

No hay comentarios. :

Publicar un comentario

Vamos a programar #57 - Los números de Munchhausen (ver. Java android)

No hay comentarios.
Hola de nuevo a todos, el día de hoy vamos a ver cómo probar los números de Munchhausen en java para usarse en dispositivos con android.

El código.

Cómo ya vimos cuales son las reglas de los números de Munchhausen en el primer post, ahora pasaremos directamente al código en java para poder usarlo. El código es el siguiente:

package com.mdev.munchhausen;

import android.app.Activity;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ListView;
import android.widget.TextView;

import java.util.ArrayList;

public class MainActivity extends Activity {

	Button BtnDowork;
	TextView TXTMin, TxtMax;
	CheckBox CHKShowAll;
	ListView ShowResults;
	private ProgressDialog progress;

	private boolean IsMunchhausenNumber(String Number)
	{
		int Value = 0;
		int CurrentNumber = 0;
		char[] Digits = Number.toCharArray();
		for (int i = 0; i < Digits.length; i++)
		{
			CurrentNumber = Digits[i] - 48;
			Value += (int)Math.pow(CurrentNumber,CurrentNumber);
		}
		String CurrentVal = String.valueOf(Value);
		if (Number.compareTo(CurrentVal) == 0)
			return true;
		else
			return false;
	}

	private ArrayList TestNumbers(int BeginNumber, int EndNumber, int ShowAll)
	{
		ArrayList TempList = new ArrayList();
		for (int i = BeginNumber; i <= EndNumber; i++)
		{
			if (IsMunchhausenNumber(String.valueOf(i)))
				TempList.add( "La conjetura se cumple para el número " + i);
			else if (ShowAll == 1)
				TempList.add( "El número " + i + " no cumple la conjetura");;
		}
		return TempList;
	}
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		BtnDowork = (Button)findViewById(R.id.BtnDoWork);
		TXTMin = (TextView)findViewById(R.id.TXTInMin);
		TxtMax = (TextView)findViewById(R.id.TXTInMax);
		CHKShowAll = (CheckBox)findViewById(R.id.CHKShowAll);
		ShowResults = (ListView) findViewById(R.id.LVResults);
		BtnDowork.setOnClickListener(new View.OnClickListener() {
										 @Override
										 public void onClick(View v) {
											 Integer ShowAll = 0;
											 Integer Min = Integer.valueOf(TXTMin.getText().toString());
											 Integer Max = Integer.valueOf(TxtMax.getText().toString());
											 if (CHKShowAll.isChecked())
												 ShowAll = 1;
											 else
												 ShowAll = 0;
												new UpdateList().execute(Min,Max,ShowAll);
										 }
									 }

		);
	}

	private  class UpdateList extends AsyncTask<Integer,Void,ArrayList>
	{
		@Override
		protected void onPreExecute()
		{
			progress = ProgressDialog.show(MainActivity.this, "Espera...", "Espera...!!!");
		}
		@Override
		protected ArrayList doInBackground(Integer... Limits)
		{
			ArrayList Lista = new ArrayList();
			Lista = TestNumbers(Limits[0],Limits[1],Limits[2]);
			return Lista;
		}
		@Override
		protected void onPostExecute(ArrayList result)
		{
			super.onPostExecute(result);
			final ArrayAdapter adapter = new ArrayAdapter(MainActivity.this,android.R.layout.simple_list_item_1, result);
			ShowResults.setAdapter(adapter);
			progress.dismiss();
		}
	}
}

El código ha cambiado un poco, en el caso de la función "IsMunchhuasenNumber()", todo se mantiene igual, es decir; se prueba un número y si este cumple la conjetura, la funcion devuelve "true", en caso contrario se devuelve "false".

La función "TestNumbers", es la que sufrido varios cambios. Para empezar, los parámetros que está recibe, los primeros dos se mantienen del tipo "int", pero el tercer parámetro pasa a ser del tipo "boolean" al tipo "int", esto solo hace por practicidad, un poquito más adelante veremos el por qué. Otro de los cambios que sufre, es que ahora pasa a ser una función en vez de un procedimiento. El valor que regresa es un valor del tipo "ArrayList", que es la lista en la cual se van almacenado los valores de salida. Cada vez que se prueba la conjetura para un número, se regresa una cadena de texto similar a "el número n no/cumple la conjetura", en la versión de C#, directamente se usaba el delegado y la función "UpdateList()" para hacerlo pero en este y debido a cómo está estructurado, pensé que era mejor hacerlo así.

A diferencia de los muchos otros códigos que he dicho que voy a "optimizar" (y que no he hecho), este ahora si usa un "Thread" diferente para hacer los cálculos y que no se bloquee la interfaz principal, para eso hacemos uso de la Subclase "UpdateList" que hereda de la clase "AsyncTask" (no se si sea en valido decir "hereda" en java, pero creo que si o si no se entiende). En el post de la semana que viene, veremos cómo usar esta clase para poder brindar un poco de "Multithreading", pero por ahora, al crear la instancia, pasamos tres parámetros que son "<Integer, Void, ArrayList>". Para poder usar la clase "AsyncTask", debemos de sobrecargar algunos de los metodos que la componen, en este caso solo son tres (pero hay más) y son: "onPreExecute()", "doInBackground()" y "onPostExecute()". Respectivamente cada uno hace lo siguiente: Es lo que se ejecutará antes de empezar, la parte principal y que mas tiempo va a consumir y la parte donde se procesaran los resultados.

Con el código anterior podemos probar los números de Munchhausen en un dispositivo con android, en el siguiente post veremos cómo es que funciona la clase "AsyncTask" y cómo nos podra ayudar con tareas en segundo plano. El código cómo de costumbre lo pongo en mi dropbox para que lo descargues, recuerda que a partir de este post, solo ira el código del layout principal y el código java. El APK ya compilado lo puedes descargar de mi dropbox directamente o puedes visitar la seccion de descargas del blog.

Y bien, por ahora es todo, los leo luego.

No hay comentarios. :

Publicar un comentario

Vamos a programar #56 - Los números de Munchhausen (ver. Javascript)

No hay comentarios.
Hola de nuevo a todos, el día de hoy vamos a continuar con la prueba de la conjetura de los números de Munchhausen.


En el post anterior vimos cuales son las reglas, por lo que ahora pasaremos directamente al código javscript para ver las diferencias entre la versión de C# y esta.

El código.

El código en javascript para probar los números de Munchhausen es el siguiente:


<html>
	<head>
		<style type="text/css">
		.h1M
		{
			font-size: 16px;
		}
		.formVigenere{
			font: 95% Arial, Helvetica, sans-serif;
			max-width: 400px;
			margin: 10px auto;
			padding: 16px;
			background: #F9F9F9;
		}
		.textareaM
		{
			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;
		}

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

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

		.ButtonM
		{
		width: 100%;
			background-color: #009900;
			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;
		}

		.ButtonM:hover
		{
		width: 100%;
			background-color: #4CAF50;
			border: none;
			color: white;
			padding: 15px 32px;
			text-align: center;
			text-decoration: none;
			display: inline-block;
		}
		</style>
		<script type="text/javascript">
		function NumberToDigits(Number){
			var Out = [];
			for(var i = 0; i < Number.length; i++){
				var CodeChar = Number.charCodeAt(i);
					Out.push(CodeChar - 48);
			}
			return Out;
		}

		function IsMunchhausen(Number){
			var Result = 0;
			var Digits = NumberToDigits(Number);
			for(var i = 0; i < Digits.length; i++){
				Result += Math.pow(Digits[i], Digits[i]);
			}
			if (Number == Result.toString()){
				return true;
			}
			else{
				return false;
			}
		}
		function TestNumbers(){
			document.getElementById('Result').value = "";
			BeginNumber = document.getElementById('MinNumber').value;
			EndNumber = document.getElementById('MaxNumber').value;
			SHowAll = document.getElementById('ShowAll').checked;
			for (var i = BeginNumber; i <= EndNumber; i++){
				if (IsMunchhausen(i.toString()))
					document.getElementById('Result').value += "La conjetura se cumple para el número " + i + "\n";
				else if (SHowAll)
					document.getElementById('Result').value += "El número " + i + " no cumple la conjetura\n";
			}
		}
		 </script>
	</head>
	<body>
	Utilidad para probar la conjetura de Munchhausen usando javascript.
	<form class="formVigenere">
		<span class="h1M">Numero de donde empezar:</span>
		<br />
		<input class="TextM" id="MinNumber" value = "1" type="text"></input><br />
		<span class="h1M">Numero hasta donde llegar</span><br /><br/>
		<input class="TextM" id="MaxNumber" value = "5000" type="text" /><br />
		<br />
		<input class="ButtonM" id="Calcula" onclick="TestNumbers()" type="button" value="Calcular" /><br />
		<br />
		<input type="checkbox" id = "ShowAll" value="Bike">Mostrar Todo<br><br>
		<span class="h1M">Resultado</span><br/>
		<textarea class="textareaM" id="Result" rows="5"></textarea><br/><br/>
	</form>
	</body>
<html>

El código anterior solo consta de tres funcione en esta ocasión en la función "NumberToDigits()" al convertir el número de entrada a los dígitos que lo forman, directamente hace la resta de 48 para poder usar el número directamente. Para poder recuperar los valores de cada control, hacemos uso de la función "document.getElementById".



Cómo de costumbre, el código lo dejo en mi dropbox para que lo pruebes, al ser el mismo "concepto" que la versión de C#, no requiere una explicación mayor. La semana que viene, veremos la versión para android.

Los leo luego.

No hay comentarios. :

Publicar un comentario

Vamos a programar #55 - Los números de Munchhausen (ver. C#)

No hay comentarios.
Hola de nuevo a todos, el día de hoy vamos a ver los números de Munchhausen y cómo probar la conjetura usando código en C#.

Antes que nada, veamos la definicion de los número de Munchhausen segun wikipedia:
El término fue acuñado por el ingeniero de software y matemático holandés Daan van Berkel en 2009.​ El nombre se debe a que cada dígito está "elevado" por sí mismo, esto evoca la historia de Barón Munchausen que se elevó a sí mismo hacia arriba jalando su propia coleta.​ Los números narcisistas siguen una regla similar, pero en el caso de los números narcisistas la potencia de los dígitos es fija, siendo elevados a la potencia del número de dígitos en el número.
En resumidas cuentas, se trata de tomar un número y separar cada dígito y este a su vez elevarlo a la potencia que será el mismo dígito. Antes de continuar, actualmente solo hay dos números conocidos que cumplen la conjetura. uno de ellos es el número 1 puesto que:

  • 1 = 1^1 = 1.
El otro número cónocido es 3435 puesto que:

  • 3435 = 3^3 + 4^4 + 3^3 + 5^5 = 27 + 256 + 27 + 3125 = 3435.
Aunque ya se conocen solo los número anteriores, igual vamos a ver el código en C# que prueba la conjetura.

El código.

El código en C# que sirve para probar la conjetura es el siguiente:

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;
using System.Threading;

namespace Munchhausen
{
	public partial class FrmMain : Form
	{
		private bool IsBusy = false;
		private delegate void UpdateListDelegate(string Value);
		private void UpdateList(string Value)
		{
			if (LBResults.InvokeRequired)
				LBResults.Invoke(new UpdateListDelegate(UpdateList), Value);
			else
				LBResults.Items.Add(Value);
		}
		private bool IsMunchhausenNumber(string Number)
		{
			int Value = 0;
			int CurrentNumber = 0;
			char[] Digits = Number.ToCharArray();
			for (int i = 0; i < Digits.Length; i++)
			{
				CurrentNumber = Digits[i] - 48;
				Value += (int)Math.Pow(CurrentNumber,CurrentNumber);
			}
			if (Number == Value.ToString())
				return true;
			else
				return false;
		}
		private void TestNumbers(int BeginNumber, int EndNumber, bool SHowAll)
		{
			for (int i = BeginNumber; i <= EndNumber; i++)
			{
				if (IsMunchhausenNumber(i.ToString()))
					UpdateList("La conjetura se cumple para el número " + i);
				if (SHowAll)
					UpdateList("El número " + i + " no cumple la conjetura");
			}
			IsBusy = false;
		}
		private void RunWork(int Minimum, int Maximun, bool ShowAll = false)
		{
			Thread DoWork = new Thread(new ThreadStart(() => TestNumbers(Minimum, Maximun,ShowAll)));
			IsBusy = true;
			DoWork.Start();
		}
		public FrmMain()
		{
			InitializeComponent();
		}

		private void BtnStart_Click(object sender, EventArgs e)
		{
			LBResults.Items.Clear();
			if (!IsBusy)
				RunWork(int.Parse(TxtMinNumber.Text), int.Parse(TxtMaxNumber.Text),CHKShowTrue.Checked);
			else
				MessageBox.Show("Hay una operación en proceso, espera","MDev",MessageBoxButtons.OK,MessageBoxIcon.Exclamation);
		}

		private void FrmMain_FormClosing(object sender, FormClosingEventArgs e)
		{
			if (IsBusy == true)
			{
				MessageBox.Show("No se puede salir ahora","MDev", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
				e.Cancel = true;
			}
		}
	}
}

El código anterior consta de cuatro funciones, pero solo la función "IsMunchhausenNumber()" es la que hace el trabajo, el resto solo son para poder usar la aplicación y que esta no se bloquee cuando se realice el proceso.

La función "IsMunchhausenNumber" recibe un parámetro del tipo "string" que será la cadena de texto que representa el número que se va a probar; decidí tomar directamente el parámetro cómo un "string" debido a que cuando recuperamos el número de la caja de texto, este sera de ese tipo, entonces convertirlo y luego hacer una conversión más para poder separarlo en los dígitos, significa doble trabajo.

Dentro de la función, convertimos el parámetro ingresado en un "array" del tipo "char", cada elemento representara un dígito del número ingresado. Luego para poder hacer los cálculos con cada dígito, a cada valor del tipo "char" le restamos 48 para obtener su valor real. Hay que recordar que el tipo "char" es esencialmente igual al tipo "byte" por lo que podemos restar (aunque habría que hacer la conversión implícita pero meh). La razón por la que se resta 48, es porque el número cero, esta en el lugar 48, el uno es 49 y así sucesivamente hasta el número nueve. Si intentamos convertir el valor del tipo "char" "0" a uno del tipo "int", este regresará 48, por lo que hacer la resta directamente resulta más fácil.

Después con los numero ya separados se hace la suma siguiendo las reglas de la conjetura y se compara el valor de la variable usada para hacer la suma con el número ingresado cómo parámetro; si estos son iguales entonces la función devuelve un valor del tipo "bool" "true", en caso contrario devuelve "false".

Para probar los número, se hace uso de la función "TestNumbers". Esta función recibe tres parámetros, el primero del tipo "int" que servirá cómo limite mínimo para empezar a probar los números. El segundo parámetro también del tipo "int" sirve para marcar el limite máximo hasta cual se van a probar los números, finalmente el tercer parámetro del tipo "bool", servirá par filtrar los resultados; solo cuando este sea "true", todos los resultados sin importar que cumplan la conjetura, se agregan a la lista de salida, en caso contrario, solo los que la cumplen son agregados.

Y bien, por ahora es todo en futuros post veremos que hacen el resto de las funciones (que de seguro habrás notado que aparecen en varios de los programas del blog) y probablemente al igual que algunos otros de los programas, crearemos versiones para android en java y web en javascript. Cómo de costumbre, puedes descargar el código fuente de mi dropbox para que lo pruebes.

Los leo luego

No hay comentarios. :

Publicar un comentario

Vamos a platicar #7 - Luchando contra la estupidez (última vez).

No hay comentarios.
Hola de nuevo a todos. En el post del día de hoy vamos a retomar un poco del trabajo de UNAM.
Parece que últimamente, cada vez que escribo  una sección de "Vamos a platicar", es sinónimo de "vamos a joder a alguien porque cree que los sismos son producto del HARP (sea lo que sea)" y no por las placas tectónicas; es decir; de forma natural.


Hace algunos días, de nueva cuenta se activó la alerta sísmica en la zona metropolitana de la Ciudad de México. Por fortuna el movimiento fue muy leve (por no decir imperceptible). Si has leido algunos post viejos de esta misma serie, sabrás que siempre trato de usar cómo referente la información proporcionada por el SSN.

Para no9 hacerle al feo a la costumbre, de inmediato salio gente que aseguraba que la magnitud estaba mal, "nos mienten", "se sintio pior" y cosas por el estilo. Cómo de costumbre hice lo minimo por tratar de persuadir a estas personas de que el SSN dice la verdad. Uno de ellos pregunto "¿y cómo sabes que no mete la mano el gobierno?". Yo sabía que la informacion practimente se publica en bruto (por eso despues los ajustes), pero realmente cómo es que se crea y se publica la información.

La solución, la UNAM creo un boletin en el cual lo explican.

Boletín UNAM-DGCS-440
Ciudad Universitaria.
11:00 hs. 21 de julio de 2018
GENERAN EN LA UNAM MAPAS DE INTENSIDADES SÍSMICAS Y DAÑOS EN TIEMPO REAL
Pueden contribuir a mejorar la toma de decisiones, dirigir la ayuda más rápido y con mayor eficiencia, e informar con prontitud a la población, explicó Mario Ordaz Schroeder, del Instituto de Ingeniería 
Para los mapas se utiliza información que proporcionan instituciones como el Servicio Sismológico Nacional de la UNAM 
Un movimiento telúrico tiene una sola magnitud, pero produce muchas intensidades, que son una medida instrumental y objetiva relacionada con los daños estructurales, aclaró
En los primeros minutos después de un sismo se produce un vacío de información en el que las autoridades no saben con precisión cuáles son los sitios más afectados o si se han producido daños en las estructuras de las edificaciones.
Por ello, la Coordinación de Ingeniería Sismológica del Instituto de Ingeniería de la UNAM genera mapas de intensidades y daños del territorio nacional en tiempo real, que pueden contribuir a mejorar la toma de decisiones, dirigir la ayuda más rápido y con mayor eficiencia, e informar con prontitud a la población si estamos o no ante una calamidad.
Mario Ordaz Schroeder explicó que un movimiento telúrico tiene una sola magnitud; por ejemplo, la del sismo del 19 de septiembre pasado fue de 7.1, pero las intensidades que se produjeron respecto al mismo fueron muchas.
“La intensidad sísmica es una medida local de la severidad del movimiento del suelo; está relacionada con la magnitud del temblor, pero depende de otros factores como la distancia desde el epicentro, el tipo de suelo o la ruptura de las placas tectónicas”.
A las construcciones no les “importa” si el temblor fue de magnitud 7 u 8, lo que las puede afectar es cuánto se acelera el suelo en su base. “Cuando tiembla, el suelo se mueve y esas aceleraciones se convierten en fuerzas de inercia sobre las estructuras”; por ello, la intensidad es una medida instrumental y objetiva relacionada con los daños estructurales, detalló el experto.
De ahí la importancia de crear mapas de intensidad sísmica muy poco tiempo después de que ocurrió un sismo. “Eso implica que existe la posibilidad de registrar la aceleración en el suelo, mediante instrumentos denominados acelerómetros, y llevar esos datos a un sitio central de recepción de manera automática, sin intervención humana, mediante infraestructura de comunicaciones; ya con los datos, un programa genera los llamados shake maps y los difunde”.
En México, para generar los mapas se utiliza información que proporcionan diferentes instituciones, entre ellas el Servicio Sismológico Nacional, que opera el Instituto de Geofísica.
Se cuenta con una cobertura nacional heterogénea, muy buena en Guerrero y Oaxaca, en donde hay una red que opera el Instituto de Ingeniería, y muy mala en regiones como la noroeste.
Cuando se presenta un temblor, expuso Mario Ordaz, se “dispara” automáticamente el proceso que toma los registros. El sistema identifica si se trata de un temblor o no, y de acuerdo con los valores de aceleración, determina hacer público un mapa.
En ellos se utiliza un código de color muy sencillo: rojo es “malo” y verde es “bueno”. En el sismo del 7 de septiembre se creó una imagen que el especialista calificó como “escalofriante”. Se observa un área de rojo intenso sobre Oaxaca y Chiapas, pero con tonos rojizos que llegaron hasta Veracruz y Puebla.
Para la Ciudad de México, que cuenta con una red de acelerómetros densa, de más de 100 instrumentos, se crean “mapas muy razonables de la intensidad”; así ocurrió con el sismo del 19 de septiembre y otros, finalizó Mario Ordaz.
El boletín anterior es una copia del original publicado por la UNAM.

Y bien por ahora es todo. La semana que viene continuaremos con más programación. Por último y por mas repetitivo que suene, no hay que descalificar al las fuentes de información oficiales. Muchas veces lo que sentimos no será lo que se mide, si no lo que pasa.

Los leo luego.

No hay comentarios. :

Publicar un comentario

Learning Machine #11 - Programando en Pascal - Ciclos for y repeat.

No hay comentarios.
Hola de nuevo a todos, el dia de hoy vamos a continuar con la serie "learning machine" para el lenguaje de programación Pascal. Anteriormente vimos algunas de las partes mas importanes del lenguaje, pero por alguna razon las parte número 9 no la publique o no lo hice bien. El punto es que vamos a ver una parte importante. Cómo usar los ciclos.

Muchas veces debemos hacer operaciones que requieren ser repetidas N número de veces, anteriormente vimos que podemos hacer que algumos procesos se repitan haciendo uso de while, pero que pasa si lo que queremos  hacer, ya lo tenemos bien definido, bien, para eso existe el ciclo for.

Ciclo for.

El ciclo for sirve para hacer N veces determinada tarea, en pascal la forma más básica de si uso es la siguiente:

{ ... }
{ ... }
	for {Desde} to {hasta} do
	begin
		{ haz algo }
	end;
{...}
{...}

Para hacer uso del ciclo, debemos de hacer uso de la palabra reservada "for" seguida del número desde cual se va empezar a contar, se debe de usar una variable que es la que usaremos a manera de contador. Seguido viene la palabra reservada "to" y despues viene lo que será el "tope" del ciclo, una vez indicado esto viene la palabra reservada "do" y dependiendo de cuanto código vamos a usar, podemos hacer uso de "begin" (para inidicar el inicio), todo el código contenido entre éste y su respectivo "end;", será lo que se ejecutará hasta que el contador llegue al limite indicado.

Ahora imaginemos que queremos hacer un programa que cuente del 1 al 100 y que además escriba "este mensaje se ha escrito N veces" en la consola. tomando en cuenta lo anterior, podemos hacer código cómo el siguiente:

Program Ciclofor;
uses crt, sysutils;
var
Contador : integer;
begin
	for Contador := 1 to 100 do
	begin
		writeln(concat('Este mensaje se ha escrito ', IntToStr(Contador) ,' veces usando for'));
	end;
	readln();
end.


Repeat until.

El bucle Repeat until, es muy similar al bucle while, es decir el ciclo se repetirá hasta que cierta condicion se cumpla.

Para hacerlo, primero debemos de hacer uso de la palabra reservada "repeat", luego escribiremos todo el código que se va a ejecutar y finalmente, pondremos la palabra reservada "until" y la condición que se debe de cumplir.

En la forma más simple, el bucle repeat tendrá la siguiente forma:

{ ... }
{ ... }
	repeat
		{ haz algo }
		{ haz algo }
		{ haz algo }		
		{ haz algo }
	until ({condicion});
{...}
{...}


Para tener una mejor idea de cómo se usa, vamos a hacer el mismo programa que usamos cómo ejemplo en el ciclo "for" solo que ahora lo adaptaremos a "repeat".
Program RepeatUntil;
uses crt, sysutils;
var
Contador : integer;
begin
	Contador := 100;
	repeat
		writeln(concat('Este mensaje se ha escrito ', IntToStr(Contador) ,' veces usando until'));
		Contador := Contador + 1;
	until (Contador > 100);
	readln();
end.

Algo importante que hay que resaltar, es que "repeat" a diferencia de "while", ejecutará al menos una vez el código dentro del bloque, esto debido a que la expresion se evalua hasta el final, cosa contraria a "while". Si quieres probar esto, puedes asignarle el valor de 100 a la variable "Contador" justo antes de "Repeat", la condicion ya se cumple, porque por obvio que parezca, 100 es mayor que 99.

Y bien, por ahora es todo, en post futuros veremos más aprendizaje de pascal (incluso la primer imagen te puede dar una idea de lo que viene). Puedes copiar los códigos anteriores para probarlos.

Los leo luego.

No hay comentarios. :

Publicar un comentario