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.

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.

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.

Vamos a platicar #3 - El sismo en México.




Tal y cómo lo mencioné la vez anterior, al encender el televisor, fue cómo logré apreciar la real magnitud de lo sucedido, la ciudad estaba totalmente estremecida, todos andaban con el rostro desencajado, los más jóvenes, por sufrir esa experiencia, los más viejos, ellos por volver a experimentar algo que se suponía que no debía de pasar de nuevo. ¿De que servia todo lo aprendido? supuestamente después de 1985, cosas cómo esas no deberían de pasar... Al final pasaron y tal cómo hace 32 años, la ciudad se fragmento una vez más.

Lo que pasó justo después es lo que se debe de rescatar. Todos aquellos que se encontraban cerca y vieron cómo se colapsaban los edificios, contrario a lo que uno pudiera pensar, no se alejaron para resguardarse, simplemente hicieron lo que debían de hacer, al estar lo más próximos a la tragedia, sería absurdo esperar a que viniera una ambulancia, bomberos o cualquier cosa que se supone debiera de ayudarlos. Simplemente usaron lo único que tenían disponible y comenzaron con la titánica tarea de rescatar a cualquiera que pudieran. Poco a poco se empezaron a crear cadenas de personas que sin saber quien era el que estaba al lado, comenzaron con el rescate de cualquiera que estuviera abajo, aun corriendo riesgos para su persona.

Cada vez más gente se unía cómo voluntaria, los que podían, levantaban piedras y mano a mano las alejaban del derrumbe, los que no podían, ayudaban distribuyendo víveres que las gente donaba. La ayuda fluía de manera impresionante. A diferencia del tembló anterior, cómo la tragedia ocurría en las calles, la ayuda era mucho más notoria, todos hacían lo que podían, incluso más.

Poco a poco me di cuenta que todo el tiempo había medido mal al país, siempre pensaba que si algo similar pasaba, cada quien haría lo que fuera necesario para resguardar sus bienes, eso fue lo que yo pensé primero yo y después los demás, al ver cómo a muchos no les interesaba me sentí mal conmigo mismo y por eso decidí ayudar en un centro de acopio. Fui un día completo a ayudar (no doy más detalles, se me hace innecesario).

El objetivo de estos post ha sido para rendir un pequeño homenaje a todos aquellos héroes anónimos, que me demostraron que aun hay muchas cosas buenas por las cuales abogar en este país, es cierto que algunas no funcionan cómo deberían, alguna simplemente no deberían de existir, pero por fortuna somos muchos más los que queremos un lugar mejor.

Sin mas que agregar solo me queda decir: Gracias a todos.

Los leo luego

Vamos a platicar #2 - El sismo en México

Hola de nuevo a todos, el día de hoy, les voy a contar una pequeña historia de lo que ha pasado en el centro de México.
Como muchos sabrán, el día  8 de septiembre se registró un temblor de magnitud 8.2 con epicentro en las costas de Chiapas. A pesar de haber sido un movimiento realmente potente, solo se registrarón daños en las zonas próximas al epicentro (sin ratar de minimizar la gravedad del asunto). La potencia que se registró fue superior a la del temblor más destructivo que se haya producido en la zona centro, incluso, supero al registrado en septiembre de 1985.
El temblor del 19 de septiembre de 1985, ha sido el más devastador que se había producido, destruyo mucha de la infraestructura de aquel momento, cobró la vida de al menos 5000 personas (según cifras no oficiales se habla de mas de 10000) y marcó un antes y un después en la vida de los ciudadanos de la Ciudad de México.
Al paso de los años, se registraron muchos sismos, algunos moderados y otro no tanto pero parecían poner a prueba a las edificaciones, que respondían soportando los movimientos.
Parte de las  cosas que nos dejó, fue la cultura de la prevención. Año tras año, se empezaron a realizar simulacros para que la población, supiera que hacer en caso de sismo.
El 19 de septiembre del 2017, se había llevado un simulacro como ya era costumbre, el día transcurría como se supone debía pasar, pero a las 13:14 horas de ese día, la tierra haría otro de sus movimientos. Un nuevo sismo de magnitud 7.1 con epicentro en puebla se desató y este a diferencia del que golpeo solo unos días antes, trajo consigo destrucción a zonas que no la habían experimentado desde hacia ya mucho y aumentando el que había en las zonas que apenas se les quitaba lo aturdido por el temblor previo mas reciente.
La zona donde yo vivo, está a una hora del  centro de la ciudad de México, pero aún así se registrarón derrumbes de algunos muros, caminos se destruyerón.
Justo cuando se registró el movimiento, al igual que mucha gente salí a la calle y a lo lejos logré ver como salia polvo de una de la construcciones más antiguas que hay en la ciudad. Me acerqué a ver cuales eran las proporciones del daño, a pesar que era un edificio dañado, me sorprendió bastante ver cómo una de ñas partes de la vieja casona, se había desplazado un buen tramo (según mis especulaciones, al menos 50 cm se movió, en cuanto lo averigüe lo agrego).
Era la primera vez que veía que ocurrían daños, por lo que decidí recorrer la ciudad y ver el espectáculo macabro que solo podía resultar de un temblor.
Poco a poco fui dimensionando que era lo que acababa de pasar, por mala suerte, aún no tenía noticias sobre la magnitud del movimiento, la energía eléctrica se había ido en toda la ciudad y las comunicaciones fallaron. Una persona cómo yo, que está acostumbrada a estar comunicado, pronto comencé a angustiarme, por no saber cual era la verdadera dimensión y por no saber si algo había pasado con las personas que me importaban. al seguir mi trayecto, vi cómo muchos transformadores eléctricos habían caído de sus postes, como muchas calles tenían fisuras, como se habían levantado trozo de concreto y de algunas casa que sufrieron daño considerable. Poco a poco se hozo la penumbra y una oscuridad como nunca recuerdo haber visto se hizo presente. Según reportes, al menos 2 millones de personas no tenían luz en las áreas afectadas, eso causo que una histeria (muy pequeña) se apoderara de todos, ante la posible falta de energía eléctrica que en su momento yo calculé que duraría días, micha gente salió a la calle a comprar tantas cosas cómo pudo y cosas que generalmente se acaban hasta el final del día, como por arte de magia, desaparecieron. Por fortuna, en varios sectores de la ciudad, en la noche, se restableció la energía eléctrica. A mi me toco el restablecimiento esa mis noche y lo primero que hice fue prender el televisor solo para apreciar la real magnitud de lo sucedido...

Vamos a platicar #1 - El sismo en México 7/9/2017

Hola de nuevo a todos, el día de hoy vamos a hacer una pequeña pausa de las cosas programacionales y vamos a hablar de algo que recientemente ocurrió en mi país.


Cómo muchos sabrán, el día  siete de septiempre de 2017 a las 23:49 GMT -6 (ST). Se registró un temblor con una magnitud de 8.2 grados. Pese a la gravedad de este, afortunadamente no fue tan catastrófico cómo el ocurrido el 19 de septiembre de 1985 en la ciudad de México.

Cuando el fenómeno termino, rápidamente empezaron a circular videos en el internet mostrando la magnitud que tuvo. Poco a poco empezaron a aparecer comentarios que explicaban algunos de los sucesos "extraños" que se apreciaban y otros más pronosticando futuros eventos sísmicos.



La explicación de las luces es realmente sencilla: son los cables de energía eléctrica que al moverse se tocan y causan un corto circuito, cómo el cielo estaba nublado, se magnificaba su efecto. Es cierto que existe un fenómeno llamado "luces de terremoto", pero estos son muy raros y además claramente se ve que en la mayoría de los casos (en el video) son producto de los cables de energía eléctrica.

Otro asunto es la predicción de los sismos. Actualmente y a pesar de todos los avances que se han hecho, aun no es posible predecir un sismo. Cuando ocurre un movimiento de magnitud considerable, es posible pronosticar que habrán replicas, inclusive nuevos sismos, pero bajo ninguna circunstancia se puede decir en que momento pasarán, me resulta sorprendente que haya gente diciendo "el día X habrá un mega-terremoto ..." pero me sorprende aun más que haya gente que les crea, en cada temblor reciente, hay gente que dijo que sabia que eso iba a ocurrir y peor aun, diciendo que iban a ocurrir.

Todos los datos relevante los puedes ver aquí:

Y bien por ahora es todo, en el siguiente post continuaremos con la programación.
Los leo luego.

Vamos a programar #40 - Números palindrómicos (ver C#)

Hola de nuevo a todos el día de hoy vamos a tratar de probar otra conjetura usando programación para esto.


Hace poco mientras charlaba con un amigo, por alguna razón empezamos a hablar sobre palíndromos. Para el que no lo sabe, un palíndromo es lo siguiente:

Un palíndromo (del griego palin dromein, volver a ir atrás), también llamado palindromo, palíndroma o palindroma, es una palabra, número o frase que se lee igual adelante que atrás. Si se trata de un numeral, usualmente en notación indoarábiga, se llama capicúa. Habitualmente, las frases palindrómicas se resienten en su significado cuanto más largas son. *Wikipedia/palíndromos*
 En resumidas cuentas, es una frase que se puede leer tanto de izquierda a derecha, cómo de derecha a izquierda.

Adivina ya te opina, ya ni miles origina, ya ni cetro me domina, ya ni monarcas, a repaso ni mulato carreta, caso nicotina, ya ni cita vecino, anima cocina, pedazo gallina, cedazo terso nos retoza de canilla goza, de pánico camina, ónice vaticina, ya ni tocino saca, a terracota luminosa pera, sacra nómina y ánimo de mortecina, ya ni giros elimina, ya ni poeta, ya ni vida. (de Ricardo Ochoa)
Hay que ser bastante creativos para lograr crear una oracion de mas de cinco palabras y que está tenga sentido.

Seguro te preguntarás, y eso que tiene que ver con la imagen de arriba? Mucho, pues al igual que las palabras crean palíndromos, los número son capaces de hacer lo mismo. hay algunos números que se pueden leer al derecho y al revés. Pero no solo eso, además existe una conjetura en la cual se dice que si se siguen ciertas reglas, es posible obtener un número palindrómico.
La conjetura es la siguiente:
"Tomese cualquier número entero postivo, a este hay que sumarle un número que será el mismo pero en orden inverso y se prosigue hasta conseguir el número palíndromo"
De acuerdo a la conjetura anterior, siguiendo ese método, deberíamos de ser capaces de obtener un número palíndromo a partir de cualquiera. Entonce el trabajo del día de hoy, consiste en crear una aplicación que haga los cálculos y no diga si un numero cumple la conjetura o no.

Supongamos que tenemos el numero 281, tendríamos lo siguiente:

  • 281 + 182 = 463
  • 463 + 364 = 827
  • 827 + 728 = 1555
  • 1555 + 5551 = 7106
  • 7106 + 6017 = 13123
  • 13123 + 32131 = 45254 Se cumple!!!
Con eso tenemos que al aplicar la conjetura al número 281, este la cumple en 6 pasos.

El Código

El código en c# que prueba la conjetura de los números palindrómicos 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.Numerics;

namespace NumerosPalindromicos
{
	public partial class FrmMain : Form
	{
		private bool IsPalindrome(string Number)
		{
			if (Number.ToString() == ReverseNumber(Number.ToString()))
				return true;
			else
				return false;

		}
		private string ReverseNumber(string NumberIn)
		{
			char[] charArray = NumberIn.ToCharArray();
			Array.Reverse(charArray);
			return new string(charArray);
		}

		private void GetNumber(string Number1, decimal MaxIterations)
		{
			int Iterator = 1;
			if (IsPalindrome(Number1) == true)
				LBResults.Items.Add("0 iteraciones "+ Number1 +" cumple la conjetura");
			else
			{
				BigInteger Result;
				BigInteger.TryParse(Number1,out Result);
				while (IsPalindrome(Result.ToString()) == false)
				{
					Result += BigInteger.Parse(ReverseNumber(Result.ToString()));
					LBResults.Items.Add(Iterator + " iteraciones "+ Result);
					if (Iterator >= MaxIterations)
					{
						break;
					}
					Iterator += 1;
				}

				if (IsPalindrome(Result.ToString()) == true)
					LBResults.Items.Add("La conjetura se cumple");
				else
					LBResults.Items.Add("La conjetura no se cumple.");
			}

		}
		public FrmMain()
		{
			InitializeComponent();
		}
		private void BtnDo_Click(object sender, EventArgs e)
		{
			LBResults.Items.Clear();
			GetNumber(TxtNumberIn.Text, NUDIterator.Value);
		}
	}
}

El código anterior consta de tres funciones, la primera de ellas (en orden de aparición) es "IsPalindrome". Está función del tipo "boolean" y recibe un parámetro del tipo "string"; se encarga de comprobar si el parámetro que se ingresa cumple con la condición que sea un palíndromo, para eso, simplemente invierte el orden de los número del parámetro de entrada usando la función "ReverseNumber".
La funcion "ReverseNumber, al igual que la funcion "IsPalindrome", recibe un parametro del tipo "string", este lo convierte en una matriz del tipo "char" y luego coloca el primer elemento al final y viceversa, con esto obtenemos el parametro de entrada invertido. La funcion regresa un valor del tipo "string".
Finalmente la función "GetNumber", es la que se encarga de hacer los cálculos. Esta función, recibe dos parámetros, el primero del tipo "string" que es la representación del número al cual queremos comprobar la conjetura. El segundo parámetro, es un valor del tipo "decimal" que se usará para determinar cuantas ciclos deben de hacerse antes de que se diga que la conjetura no se cumple.
En la función, lo primero que hacemos, es comprobar si el número no es ya un palíndromo, si lo es solamente agregamos en la lista de resultados que el número ya es palíndromo y que no se hizo ninguna iteración.
Si no es el caso, creamos una variable del tipo "BigInteger" en la que almacenaremos el resultado de la suma.
Luego creamos el bucle principal, la condición para que este siga activo, es que al llamar a la función "IsPalindrome" esta devuelva false.  Luego a la variable "Result" le sumará el resultado de la función "ReverseNumber", luego agregará el resultado a la lista de resultados  y después comprobará que la iteración actual, este dentro de los limites establecidos en el parámetro "MaxIterations"; si no es así, el bucle se interrumpirá y terminará la prueba de la conjetura declarando que no se cumple en el limite establecido (pero eso no significa que no tenga solución); en caso contrario, se incrementará el valor de la variable "Iterator" en uno y el bucle volvería al inicio.

Con el código anterior, podemos probar que el número 28,121,993 no cumple la conjetura, o no dentro de las primeras mil iteraciones (tampoco en 10,000).
Al código anterior, al igual que a varios de los otros proyectos, le faltan algunas optimizaciones (correrlo en un thread diferente para no bloquear la interfaz principal), pero por ahora sirve para dejar la idea en claro, cómo siempre, el código completo lo puedes descargar de mi dropbox para revisarlo.

Por ahora es todo, los leo luego.

Learning Machine #8 - Funciones y procedimientos.

Hola de nuevo a todos, el día de hoy vamos a continuar con el aprendizaje de Pascal. El tema de hoy corresponde a procedimientos y funciones.


Las funciones y procedimientos, son bloques de código que están separados del código que se ejecuta en la parte principal, además se distinguen porque estos se pueden ejecutar tantas veces mientras se les "llame", cuando lo hacemos, todo el código contenido en estos se ejecutará y al terminar, la ejecución continuará en la linea inmediata a la cual fue llamada la función/procedimiento.

Procedimientos.

Los procedimientos, son bloques de código que se ejecutan y no devuelven ningún tipo de resultado (o no que sea utilizable).
Veamos el siguiente código:

program Procedimiento;
uses crt;
var Number1, Number2 : integer;
procedure suma(N1 : integer; N2 : integer);
var Resultado : integer;
begin
	Resultado := N1 + N2;
	writeln('El Resultado es ', Resultado);
end;
begin
	clrscr;
	writeln('Ingresa el primer numero');
	readln(Number1);
	writeln('Ingresa el segundo numero');
	readln(Number2);
	suma(Number1,Number2);
	readln();
end.


Para definir un procedimiento, debemos de hacer uso de la palabra reservada "procedure", seguida del nombre que queramos darle al procedimiento. En el caso del código anterior, al procedimiento le dimos el nombre "suma", entre paréntesis, deben de ir los parámetros que el procedimiento vaya a usar, se pueden usar tantos cómo uno desee, pero cada uno debe de  ser definido con el tipo de datos que se usen y cada parámetro debe de ir separado por ";". Para abrir un procedimiento usamos "begin" y para cerrarlo usaremos "end;". Todo el código que se encuentre en medio de las palabras reservadas de apertura y cierre, sera el que se ejecute cada vez que se llame a la función; en este caso "suma". Para hacer uso de los parámetros, solamente debemos de llamarlos. para usar variables, debemos de declararlas en seguida de la creación del procedimiento, usando la palabra reservada "var", al igual cómo lo hacemos con las variables globales.

Funciones.

Las funciones son bastante similares a los procedimientos, es decir, van a ejecutar un bloque de código cada vez que se mande a llamar. La diferencia principal con los procedimientos, es que estas, devolverán un valor que se puede usar para otras cosas. Veamos el siguiente bloque de código:

program Funciones;
uses crt;
var Number1, Number2 : integer;
function suma(N1 : integer; N2 : integer) : integer;
var Resultado : integer;
begin
	Resultado := N1 + N2;
	{ suma:=Resultado; }
	exit(Resultado);
end;
begin
	clrscr;
	writeln('Ingresa el primer numero');
	readln(Number1);
	writeln('Ingresa el segundo numero');
	readln(Number2);
	writeln('La suma da ', suma(Number1,Number2));
	readln();
end.

Para definir una función, debemos de hacer uso de la palabra reservada "function", seguida del nombre del nombre que queramos darle a la función. Al igual que al procedimiento, en este caso le dimos el nombre de "suma", entre paréntesis deben de ir los parámetros y ademas, le debemos de dar un tipo, puede ser cualquiera de los soportados por Pascal (Boolean, integer, string, etc.).
Sabiendo que la función debe de devolver un dato que sea utilizable, debemos de ver la forma que este la regrese, para eso se hace uso de la palabra reservada "exit" y el valor que queramos regresar, deberá de ir entre paréntesis o podemos asignar el valor que queramos devolver a la función, en el caso del código anterior, es lo mismo "exit(Resultado)" que "suma:=Resultado", en ambos casos le estamos diciendo que el valor que la función regresará, será el que tenga la variable "Resultado".

Diferencias.

Al revisar el código del procedimiento y el de la función, verás que al momento de llamar al procedimiento, simplemente lo hacemos y cómo definimos en el procedimiento que escriba el resultado de sumar los parámetros, lo hará y se mostrará en pantalla, pero si no lo hacemos y queremos hacer uso del valor de la suma que se hace en el procedimiento, simplemente no podremos.
Ahora en el caso de la función, vemos que hacemos uso de ella cómo parámetro para "writeln" y está es la importancia de usar las funciones, todos los valores que estas generen se pueden usar cómo parámetros para otras funciones, para cambiar valores de variables, imaginemos que tenemos una función que hace un calculo grande y a ese valor lo queremos dividir a la mitad, simplemente definimos la función y directamente a la llamada de está dividirla.
Si quisiéramos hacer uso de un procedimiento y pasarlo cómo parámetro de un función (por ejemplo), el compilador nos marcaría un error como el de la imagen siguiente:
Si la tarea no es muy compleja se puede hacer uso de los procedimientos, en caso contrario, se recomiendan las funciones. En ambos casos, se recomienda su uso para llevar a cabo tareas que se repitan, siempre resulta más fácil llamar a una función/procedimiento en una linea que escribir diez lineas de código cada vez.

Y bien, por ahora es todo, todos los programas que se han hecho para Pascal, en breve, subire el código fuente para que lo revises.

Los leo luego.

Vamos a programar #39 - La conjetura de Collatz (Números maravillosos) Versión Android.

Hola de nuevo a todos, el día de hoy vamos a continuar con la conjetura de Collatz.
En el post anterior de "Vamos a programar", hicimos la versión de C#, cosa que resulto realmente fácil ya que en C# se puede hacer uso del tipo "BigInteger". En java para android, también existe el tipo "BigInteger" y en esta ocasión veremos un poco de cómo usarlo.

El código.

El código que hace funcionar la aplicación de la conjetura de Collatz en java para android es el siguiente:
package com.mdev.collatz;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;

import java.math.BigInteger;
import java.util.ArrayList;


import static android.R.attr.button;

public class MainActivity extends AppCompatActivity {

	Button BtnCalculate;
	TextView TxtNumberIn;
	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);
		LVResultados = (ListView) findViewById(R.id.LVResults);

		BtnCalculate.setOnClickListener(new View.OnClickListener()
		{
			@Override
			public void onClick(View view) {
				CalculateCollatz(TxtNumberIn.getText().toString());
			}
		});

	}
	private void CalculateCollatz(String Number)
	{
		ArrayList Items = new ArrayList();
		BigInteger MyNumber = new BigInteger(Number);
		while (MyNumber.compareTo(BigInteger.ONE) > 0)
		{
			if (IsEven(MyNumber)) {
				MyNumber = MyNumber.divide(BigInteger.valueOf(2));
				Items.add(MyNumber.toString());
			}else{
				MyNumber = MyNumber.multiply(BigInteger.valueOf(3));
				MyNumber = MyNumber.add(BigInteger.valueOf(1));
				Items.add(MyNumber.toString());
			}
		}
		final ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1,Items);
		LVResultados.setAdapter(adapter);
	}

	private boolean IsEven(BigInteger Number){
		BigInteger Result = Number.remainder(BigInteger.valueOf(2));
		int CompareResult = Result.compareTo(BigInteger.ZERO);
			if(CompareResult == 0)
			{
				return true;
			}else{
				return false;
			}
	}
}

Para empezar crearemos los controles que vamos a necesitar, está aplicación consta únicamente de 4 controles (5 en realidad, puedes revisarlo en el código completo).

  • 1 Textview.
  • 1 EditText
  • 1 Button
  • 1 Listview
Solo vamos a interactuar con los últimos tres pos lo que solo esos son los que crearemos tambien en el código.
Para tratar de simplificar un poco el código, hice lo traté de hacer lo mismo que en c#. Primero, iba a pedir un numero en formato de texto, luego crear una funcion que calcule la conjetura de Collatz y mostrar cada paso que se dio en un "ListView".
La cosa es sencilla, y podemos repetir lo que ya habíamos hecho, pero en java, a diferencia de c#, el tipo BigInteger no es tan simple de manejar.
En Java, el tipo BigInteger dispone de sus operaciones por separado, las comparaciones se deben de hacer desde los métodos contenidos en el tipo BigInteger. Eso quiere decir que si queremos comparar el número "1" con el valor BigInteger "1", no podemos realizarlo porque a diferencia de C#, aquí no se hace una conversión implícita; nosotros debemos de hacerla de forma explicita haciendo uso de los métodos contenidos en el tipo "BigInteger".

Retomando nuestro código, primero veremos la función "IsEven". la función "IsEven" recibe un parámetro del tipo "BigInteger", este, es el numero de cual queremos determinar si es par o no. Para hacerlo, hacemos uso de dos variables; la primera, llamada "Result" del tipo "BigInteger", se crea y se le asigna el valor que resulta del uso del método del parámetro de entrada "Number", el método que se usa es ".remainder()", este método lo que hace, es devolver el residuo de la división entre el numero "Number" y el parámetro que se le asigne; para este caso, debemos de dividir entre dos, pero cómo no se puede usar directamente "2", usamos otro procedimiento del tipo "BigInteger". el método que usamos es "BigInteger.valueOf()", este procedimiento lo que hace, es crear una versión del número que se pasa cómo parámetro en tipo "BigInteger".
Lo anterior se hace para determinar si un número es par o no (hay que recordar que los número pares, son aquellos que al ser divididos entre dos, dan un número entero). El resultado que obtenemos es 1 o 0 (cualquiera de los dos del tipo BigInteger).
Solo nos queda hacer la comparación, si el resultado es "0" entonces el número es par, para hacer la comparación, usamos otro procedimiento más del tipo "BigInteger". Hacemos uso de la función ".compareTo", lo hacemos en la variable resultado y le pasamos cómo parámetro una constante llamada "BigInteger.ZERO"; la constante vale 0. la función regresa un valor del tipo int con cualquiera de los 3 valores posibles:

  1. -1 si el valor es menor que el BigInteger desde el cual se llama.
  2. 0 si el valor es igual que el BigInteger desde el cual se llama.
  3. 1 si el valor es mayor que el BigInteger desde el cual se llama.
Para determinar si la función "IsEven" devuelve "true" o "false", creamos una variable del tipo "int" llamada "CompareResult" a la que le asignamos el valor de la función "Result.compareTo" con parámetro "BigInteger.ZERO".
Si el valor de la variable "result" es "0", la función IsEven devuelve "true", en caso contrario, "false".

El procedimiento "CalculateCollatz", es el encargado de hacer los cálculos, para eso, primero creamos la variable del tipo "BigIntger" llamada "MyNumber". Al usar el constructor del tipo "BigInteger", le podemos pasar un valor del tipo "String", en este caso le asignamos el parámetro "Number", con eso creamos un valor del tipo "BigInteger" con valor del "String" del parámetro.
Después creamos un bucle "while" con la condición "Number" sea mayor que "BigInteger.ONE". Con la función "IsEven", podemos replicar lo que hicimos en c# y dentro del bucle, verificamos si el número es par o no y dependiendo del caso, hacer los cálculos.
Para multiplicar un valor "BigInteger", debemos de hacer uso del método "BigInteger.multiply", este recibe cómo parámetro un "BigInteger" que será el multiplicador y el resultado, sera un valor BigInteger con el valor de la multiplicación. Para dividir un valor "BigInteger", debemos de hacer uso del método "BigInteger.divide", este recibe cómo parámetro un "BigInteger" que será el divisor y el resultado, sera un valor "BigInteger" con el valor de la división. lo mismo para la suma, debemos de hacer uso de "BigInteger.add".
Finalmente creamos un "ArrayAdapter" al que agregaremos todos los pasos que se hicieron y este se asina al control "ListView".

El resultado. Puedes descargar la APK con la aplicación ya compilada, el código completo lo subiré en breve, agregare threads para que la interfaz no se bloquee (aunque así como esta funciona bien), aunque con el código anterior, se cubre toda la parte lógica de la aplicación.

Bien, por ahora es todo.
Los leo luego

Learning Machine #7 - Select.

Hola a todos, el día de hoy vamos a continuar con más del aprendizaje del lenguaje de programación Pascal, continuaremos con las instrucción "Select"

Algunas veces debemos de elegir una opción entre un numero finito de ellas, cuando son reducidas o podemos predecirlas, la instrucción "select" nos servirá para llevar esa tarea.

La instrucción "select" en Pascal, tiene la siguiente forma:

	case MiRespuesta of
		Respuesta1:
		Código que se ejecuta por la respuesta1;
		Respuesta2:
		Código que se ejecuta por la respuesta2;
		else
		Código que se ejecuta si ninguna respuesta anterior se cumple;
	end;


Para cada sentencia posible, se pueden usar números o cadenas de texto, es conveniente utilizar solo un tipo, ya que a veces las conversiones de una cadena de texto por ejemplo: "7"; no se puede convertir directamente a 7, entonces si estamos esperando un valor numérico, siempre debemos de usar 7 en la sentencia.
Cada sentencia debe de ir seguida por ":" (dos puntos), cuando terminamos de escribir el código que se ejecutará, pondremos ";" para indicar que es el fin de esa parte.

Supongamos que queremos crear un programa que pase un número a su correspondiente día de la semana de la siguiente forma:


  • 1 -> Domingo.
  • 2 -> Lunes.
  • 3 -> Martes.
  • 4 -> Miércoles.
  • 5 -> Jueves.
  • 6 -> Viernes.
  • 7 -> Sábado.
Usando "Select", lo podemos hacer de la siguiente manera:

program Casos;
uses crt;
var MiRespuesta : integer;
begin
	writeln('ingresa un numero del 1 al 7');
	readln(MiRespuesta);
	case MiRespuesta of
		1:
		writeln('El dia 1 corresponde al domingo');
		2:
		writeln('El dia 2 corresponde al lunes');
		3:
		writeln('El dia 3 corresponde al martes');
		4:
		writeln('El dia 4 corresponde al miercoles');
		5:
		writeln('El dia 5 corresponde al jueves');
		6:
		writeln('El dia 6 corresponde al viernes');
		7:
		writeln('El dia 7 corresponde al sabado');
		else
		writeln('Dia desconocido');
	end;
	readln;
end.

Cómo veras, esta definida una respuesta para cada día de la semana, si el usuario escribe 1, el programa mostrará que ese día corresponde al domingo, en este ejemplo, se hace uso de la conversión implícita, para empezar funciona, pero no en todos los casos se puede convertir un fragmento de texto (que parezca número) a un número.
También hacemos uso de "else", esta, se debe de usar al final de todos los casos y será utilizada cuando ninguno de los casos definidos se cumpla, en el caso del programa anterior, si el usuario introduce un valor fuera del rango 1-7, se mostrará: "Día desconocido".

Y bien, por ahora es todo, en futuros post, veremos cuando es conveniente el uso de "if" y cuando el uso de "case", el ejemplo anterior puede ser hecho usando solo "if" en lugar de "Select".

Los leo luego.

Vamos a programar #38 - La conjetura de Collatz (Números maravillosos) versión C#.

Hola de nuevo a todos, el día de hoy vamos a hacer un pequeño receso a la programación en Pascal y vamos a ver un poco de C#.

La conjetura de Collatz dice que todos aquellos números son "maravillosos", siempre y cuando al aplicar las siguientes operaciones de forma recursiva, nos resulte 1.

El enunciado dice:

Sea la siguiente operación, aplicable a cualquier número entero positivo:
  • Si el número es par, se divide entre 2.
  • Si el número es impar, se multiplica por 3 y se suma 1.
El problema en si, es fácil, pero al momento de crear la aplicación, debemos de recordar que estamos limitados en el numero máximo que podemos usar. Para solucionarlo, vamos a usar el tipo "BigInteger".
En .net 4.0 se introdujo este nuevo tipo de dato, que permite utilizar enteros de longitud arbitraria, mas que suficiente para poder calcular la conjetura. Actualmente el número mas grande calculado tiene mas de 60 mil millones de cifras.

El código.

El codigo en c# que calcula la conjetura de Colltaz es el siguiente (la lista de controles no se incluye, todo el código fuente lo puedes descargar de dropbox):

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)
			{
				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();
		}
		public FrmMain()
		{
			InitializeComponent();
		}

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

		}
	}
}


El código es realmente simple, ya que solo hacemos, para empezar, un bucle "while", que se ejecutará mientras la variable "MyNumber", sea mayor que uno. Después comprobamos si el numero es par, dentro de la estructura del "BigInteger", hay una función llamada "IsEven", la cual sirve para comprobar si un número es par. La utilizamos y si el resultado es "true", hacemos la división entre dos (cómo dice el enunciado), en caso contrario multiplicamos por 3 y le sumamos uno.
Para poder agilizar un poco, todo el proceso, se hace en un "thread" diferente, con lo cual la aplicación no se bloquea.
Por ahora, es todo. Desde mi punto de vista, parece que todos los numero son maravillosos, puesto que al ser impar (que usualmente son números raros), al multiplicar y sumarle uno, en algún punto, siempre nos dará un numero par. Aunque la conjetura no se ha probado (de ahí lo de conjetura), resulta un buen ejercicio para todo programador.
En el siguiente post de C#, agregaremos funciones para obtener más datos que son relevantes para la conjetura, Además, veremos un poco mas a fondo el tipo "BigInteger" y extenderemos el uso de threads para hacer cálculos un poco más "masivos". Cómo de costumbre, el código completo lo dejo en mi dropbox para que lo descargues.

Los leo luego.